From e9c38ac12f86130300df60b046585a1aaeb8c5d9 Mon Sep 17 00:00:00 2001 From: Jens Giessmann Date: Wed, 30 Jan 2019 14:33:26 +0100 Subject: [PATCH] added Json schema validation for RequestParams, enhance RequestParamActionTrait --- composer.json | 3 +- models/Tree.php | 31 ++++++++++++++++++ traits/RequestParamActionTrait.php | 50 +++++++++++++++++------------- 3 files changed, 62 insertions(+), 22 deletions(-) diff --git a/composer.json b/composer.json index 6d5ef50..87b64e7 100644 --- a/composer.json +++ b/composer.json @@ -32,7 +32,8 @@ "bedezign/yii2-audit": "^1.1", "mikehaertl/php-shellcommand": "^1.2.4", "pheme/yii2-settings": "^0.5.0", - "2amigos/yii2-translateable-behavior": "^1.0.4" + "2amigos/yii2-translateable-behavior": "^1.0.4", + "justinrainbow/json-schema": "^5.2.0", }, "require-dev": { "codeception/codeception": "^2.2", diff --git a/models/Tree.php b/models/Tree.php index 1304470..a0feb06 100755 --- a/models/Tree.php +++ b/models/Tree.php @@ -20,6 +20,7 @@ use yii\helpers\Inflector; use yii\helpers\Json; use yii\helpers\Url; +use JsonSchema\Validator; /** * Class Tree @@ -54,6 +55,36 @@ */ class Tree extends BaseTree { + + /** + * @inheritdoc + */ + public function rules() + { + return ArrayHelper::merge( + parent::rules(), + [ + [ + self::ATTR_REQUEST_PARAMS, + function ($attribute, $params) { + + $validator = new Validator(); + + $obj = Json::decode($this->requestParamsSchema, false); + $data = Json::decode($this->{$attribute}, false); + $validator->check($data, $obj); + if ($validator->getErrors()) { + foreach ($validator->getErrors() as $error) { + $this->addError($error['property'], "{$error['property']}: {$error['message']}"); + } + } + + }, + ], + ] + ); + } + /** * @inheritdoc */ diff --git a/traits/RequestParamActionTrait.php b/traits/RequestParamActionTrait.php index c345495..2c54038 100644 --- a/traits/RequestParamActionTrait.php +++ b/traits/RequestParamActionTrait.php @@ -26,7 +26,8 @@ * * By default it will generate a text field per action parameter. * - * For customization you can create a public method for each individual action parameter by adding a method which name have to follow this schema: + * For customization you can create a public method for each individual action parameter by adding a method which name + * have to follow this schema: * * `camelizedActionId` + ActionParam + `ParameterName` * @@ -35,34 +36,38 @@ * * Example: detailActionParamProductId * - * This method must return a array (key-value pairs), where the keys should refer to the actual value and the value will be the label + * This method must return a array (key-value pairs), where the keys should refer to the actual value and the value will + * be the label * * Example: * * return ArrayHelper::map(Product::find()->all(),'id','name'); * * - * Hint: + * Hints: * - * If the method as described above returns false, then this property will be ignored. + * - If the method as described above returns false, then this property will be ignored. * - * If the method as described above returns true, then this property will be displayed. This functionality can be used to maniulate e.g. title or description (see class propertie `$allowedProperties`) + * - If the method as described above returns true, then this property will be displayed. This functionality can be used + * to manipulate e.g. title or description (see class property `$allowedProperties`) * - * You can use php doc block to add option to properties: + * - You can use php doc block to add options to properties: * - * Example: - * - * /** - * * @editor title My Title - * *\/ - * public function detailActionParamProductName() { - * return true; - * } - * - * This will generate a input with defined title for an *existing* parameter + * Example: * + * /** + * * @editor title My Title + * *\/ + * public function detailActionParamProductName() { + * return true; + * } * + * This will generate a input with defined title for an *existing* parameter * + * - If property is NOT optional, it will be set as required in json schema. + * However, since this only implies that the property must be set in the data, but not that a value must also be set, + * a validation rule should be defined using notations (see above). For properties of type 'string' a minLength: 1 + * option is set as fallback. * * @package dmstr\modules\pages\traits * @author Elias Luhr @@ -111,13 +116,10 @@ private function generateJson($parameters, $actionId) $properties = []; $requiredFields = []; - $debug = []; foreach ($parameters as $parameter) { // get name $parameterName = $parameter->name; - $debug[] = $parameterName; - // nameActionParamId $methodName = $actionId . 'ActionParam' . ucfirst($parameterName); // use data from method if it exist. @@ -137,9 +139,9 @@ private function generateJson($parameters, $actionId) $additionalData = []; if ($docs !== false) { // matches e.g. - // @descrition My custom description + // @editor description My custom description // in php doc blocks - preg_match_all('/@editor (\$[a-z]+) (.*)\n/', $docs, $matches); + preg_match_all('/@editor[\s]+([a-zA-Z-_]+)[\s]+(.*)\n/', $docs, $matches); if (isset($matches[1], $matches[2]) && \count($matches[1]) === \count($matches[2])) { $matchIndex = 0; foreach ($matches[1] as $propertyName) { @@ -164,6 +166,11 @@ private function generateJson($parameters, $actionId) // add to required if not is optional if (!$parameter->isOptional()) { $requiredFields[] = $parameterName; + // TODO: how to check other types? + if (($additionalData['type'] === 'string') && (!isset($additionalData['minLength']))) { + $extraProperties[] = '"minLength": 1'; + } + } foreach ($additionalData as $name => $value) { @@ -204,6 +211,7 @@ private function generateJson($parameters, $actionId) if (!$parameter->isOptional()) { $requiredFields[] = $parameterName; + $extraProperties[] = '"minLength": 1'; } // generate default if nothing else is defined