From f9d0232f0e369e3ee537dd5ca766d29922ed3c99 Mon Sep 17 00:00:00 2001 From: shmax Date: Mon, 19 Aug 2024 19:20:35 -0700 Subject: [PATCH 1/4] ? --- examples/01-basic-scalar-validation/README.md | 74 ++++---- examples/02-custom-error-codes/README.md | 86 +++++----- examples/03-input-object-validation/README.md | 162 +++++++++--------- .../04-list-of-scalar-validation/README.md | 112 ++++++------ phpcs.xml.dist | 36 ++-- 5 files changed, 235 insertions(+), 235 deletions(-) diff --git a/examples/01-basic-scalar-validation/README.md b/examples/01-basic-scalar-validation/README.md index 8cb96e7..294a23b 100644 --- a/examples/01-basic-scalar-validation/README.md +++ b/examples/01-basic-scalar-validation/README.md @@ -1,38 +1,38 @@ -# Simple scalar validation -The simplest possible type of user input validation. Mutation expects an `authorId` arg, and will respond with a nicely formatted error code and message if an author by that id doesn't exist. - -### Run locally -``` -php -S localhost:8000 ./index.php -``` - -### Install ChromeiQL plug-in for Chrome -1. Install from [here](https://chrome.google.com/webstore/detail/chromeiql/fkkiamalmpiidkljmicmjfbieiclmeij?hl=en) -2. Enter "http://localhost:8000" in the text field at the top and press the "Set Endpoint" button -3. Be sure to inspect the "Docs" flyout to get familiar with the dynamically-generated types - -### Try mutation with valid input -``` -mutation { - deleteAuthor(id: 1) { - valid - result - } -} -``` - -### Try mutation with invalid input -``` -mutation { - deleteAuthor(id: 3) { - valid - result - suberrors { - id { - code - msg - } - } - } -} +# Simple scalar validation +The simplest possible type of user input validation. Mutation expects an `authorId` arg, and will respond with a nicely formatted error code and message if an author by that id doesn't exist. + +### Run locally +``` +php -S localhost:8000 ./index.php +``` + +### Install ChromeiQL plug-in for Chrome +1. Install from [here](https://chrome.google.com/webstore/detail/chromeiql/fkkiamalmpiidkljmicmjfbieiclmeij?hl=en) +2. Enter "http://localhost:8000" in the text field at the top and press the "Set Endpoint" button +3. Be sure to inspect the "Docs" flyout to get familiar with the dynamically-generated types + +### Try mutation with valid input +``` +mutation { + deleteAuthor(id: 1) { + valid + result + } +} +``` + +### Try mutation with invalid input +``` +mutation { + deleteAuthor(id: 3) { + valid + result + suberrors { + id { + code + msg + } + } + } +} ``` \ No newline at end of file diff --git a/examples/02-custom-error-codes/README.md b/examples/02-custom-error-codes/README.md index c9198c0..1051014 100644 --- a/examples/02-custom-error-codes/README.md +++ b/examples/02-custom-error-codes/README.md @@ -1,44 +1,44 @@ -# Simple scalar validation -If you supply an `errorCodes` property on your `fields` or `args` definitions, then a custom, unique error code type will be created. - -### Run locally -``` -php -S localhost:8000 ./index.php -``` - -### Install ChromeiQL plug-in for Chrome -1. Install from [here](https://chrome.google.com/webstore/detail/chromeiql/fkkiamalmpiidkljmicmjfbieiclmeij?hl=en) -2. Enter "http://localhost:8000" in the text field at the top and press the "Set Endpoint" button -3. Be sure to inspect the "Docs" flyout to get familiar with the dynamically-generated types - -### Try mutation with valid input -``` -mutation { - deleteAuthor(id: 1) { - valid - result - suberrors { - id { - code - msg - } - } - } -} -``` - -### Try mutation with invalid input -``` -mutation { - deleteAuthor(id: 3) { - valid - result - suberrors { - id { - code - msg - } - } - } -} +# Simple scalar validation +If you supply an `errorCodes` property on your `fields` or `args` definitions, then a custom, unique error code type will be created. + +### Run locally +``` +php -S localhost:8000 ./index.php +``` + +### Install ChromeiQL plug-in for Chrome +1. Install from [here](https://chrome.google.com/webstore/detail/chromeiql/fkkiamalmpiidkljmicmjfbieiclmeij?hl=en) +2. Enter "http://localhost:8000" in the text field at the top and press the "Set Endpoint" button +3. Be sure to inspect the "Docs" flyout to get familiar with the dynamically-generated types + +### Try mutation with valid input +``` +mutation { + deleteAuthor(id: 1) { + valid + result + suberrors { + id { + code + msg + } + } + } +} +``` + +### Try mutation with invalid input +``` +mutation { + deleteAuthor(id: 3) { + valid + result + suberrors { + id { + code + msg + } + } + } +} ``` \ No newline at end of file diff --git a/examples/03-input-object-validation/README.md b/examples/03-input-object-validation/README.md index 71a0b33..d71aa02 100644 --- a/examples/03-input-object-validation/README.md +++ b/examples/03-input-object-validation/README.md @@ -1,82 +1,82 @@ -# Validation of InputObjects (or Objects) - -You can add `validate` properties to the fields of nested Objects or InputObjects (and those fields can themselves be of complex types with their own fields, and so on). This library will sniff them out and recursively build up a result type with a similarly nested structure. - - -### Run locally -``` -php -S localhost:8000 ./index.php -``` - -### Install ChromeiQL plug-in for Chrome -1. Install from [here](https://chrome.google.com/webstore/detail/chromeiql/fkkiamalmpiidkljmicmjfbieiclmeij?hl=en) -2. Enter "http://localhost:8000" in the text field at the top and press the "Set Endpoint" button -3. Be sure to inspect the "Docs" flyout to get familiar with the dynamically-generated types - -### Try mutation with valid input -``` -mutation { - updateAuthor( - authorId: 1 - attributes: { - name: "Stephen King", - age: 47 - } - ) { - result { - id - name - } - suberrors { - attributes { - code - msg - suberrors { - age { - code - msg - } - name { - code - msg - } - } - } - } - } -} -``` - -### Try mutation with invalid input -``` -mutation { - updateAuthor( - authorId: 1 - attributes: { - name: "Edward John Moreton Drax Plunkett, 18th Baron of Dunsany", - age: -3 - } - ) { - result { - id - name - } - suberrors { - attributes { - code - msg - suberrors { - age { - code - msg - } - name { - code - msg - } - } - } - } - } -} +# Validation of InputObjects (or Objects) + +You can add `validate` properties to the fields of nested Objects or InputObjects (and those fields can themselves be of complex types with their own fields, and so on). This library will sniff them out and recursively build up a result type with a similarly nested structure. + + +### Run locally +``` +php -S localhost:8000 ./index.php +``` + +### Install ChromeiQL plug-in for Chrome +1. Install from [here](https://chrome.google.com/webstore/detail/chromeiql/fkkiamalmpiidkljmicmjfbieiclmeij?hl=en) +2. Enter "http://localhost:8000" in the text field at the top and press the "Set Endpoint" button +3. Be sure to inspect the "Docs" flyout to get familiar with the dynamically-generated types + +### Try mutation with valid input +``` +mutation { + updateAuthor( + authorId: 1 + attributes: { + name: "Stephen King", + age: 47 + } + ) { + result { + id + name + } + suberrors { + attributes { + code + msg + suberrors { + age { + code + msg + } + name { + code + msg + } + } + } + } + } +} +``` + +### Try mutation with invalid input +``` +mutation { + updateAuthor( + authorId: 1 + attributes: { + name: "Edward John Moreton Drax Plunkett, 18th Baron of Dunsany", + age: -3 + } + ) { + result { + id + name + } + suberrors { + attributes { + code + msg + suberrors { + age { + code + msg + } + name { + code + msg + } + } + } + } + } +} ``` \ No newline at end of file diff --git a/examples/04-list-of-scalar-validation/README.md b/examples/04-list-of-scalar-validation/README.md index 095b02b..839e9f3 100644 --- a/examples/04-list-of-scalar-validation/README.md +++ b/examples/04-list-of-scalar-validation/README.md @@ -1,57 +1,57 @@ -# Validation of InputObjects (or Objects) - -You can add validate lists of things. You can specify a `validate` callback on the `ListOf` field itself, and also specify a `validateItem` callback to be applied to each item in the list. Any errors returned on the list items will each have an `index` property so you will know exactly which items were invalid. - - -### Run locally -``` -php -S localhost:8000 ./index.php -``` - -### Install ChromeiQL plug-in for Chrome -1. Install from [here](https://chrome.google.com/webstore/detail/chromeiql/fkkiamalmpiidkljmicmjfbieiclmeij?hl=en) -2. Enter "http://localhost:8000" in the text field at the top and press the "Set Endpoint" button -3. Be sure to inspect the "Docs" flyout to get familiar with the dynamically-generated types - - -### Try mutation with valid input -``` -mutation { - savePhoneNumbers( - phoneNumbers: [ - "123-3456", - "867-5309" - ] - ) { - valid - suberrors { - phoneNumbers { - code - msg - path - } - } - } -} -``` - -### Try mutation with invalid input -``` -mutation { - savePhoneNumbers( - phoneNumbers: [ - "123-3456", - "xxx-3456" - ] - ) { - valid - suberrors { - phoneNumbers { - code - msg - path - } - } - } -} +# Validation of InputObjects (or Objects) + +You can add validate lists of things. You can specify a `validate` callback on the `ListOf` field itself, and also specify a `validateItem` callback to be applied to each item in the list. Any errors returned on the list items will each have an `index` property so you will know exactly which items were invalid. + + +### Run locally +``` +php -S localhost:8000 ./index.php +``` + +### Install ChromeiQL plug-in for Chrome +1. Install from [here](https://chrome.google.com/webstore/detail/chromeiql/fkkiamalmpiidkljmicmjfbieiclmeij?hl=en) +2. Enter "http://localhost:8000" in the text field at the top and press the "Set Endpoint" button +3. Be sure to inspect the "Docs" flyout to get familiar with the dynamically-generated types + + +### Try mutation with valid input +``` +mutation { + savePhoneNumbers( + phoneNumbers: [ + "123-3456", + "867-5309" + ] + ) { + valid + suberrors { + phoneNumbers { + code + msg + path + } + } + } +} +``` + +### Try mutation with invalid input +``` +mutation { + savePhoneNumbers( + phoneNumbers: [ + "123-3456", + "xxx-3456" + ] + ) { + valid + suberrors { + phoneNumbers { + code + msg + path + } + } + } +} ``` \ No newline at end of file diff --git a/phpcs.xml.dist b/phpcs.xml.dist index 8f3ff5a..0e2f7af 100644 --- a/phpcs.xml.dist +++ b/phpcs.xml.dist @@ -1,18 +1,18 @@ - - - - - - - - - - - - src - tests - - - - - + + + + + + + + + + + + src + tests + + + + + From 5c9943ea954b54b6f0a85723f888d0d9b6fdf95c Mon Sep 17 00:00:00 2001 From: shmax Date: Mon, 19 Aug 2024 19:21:47 -0700 Subject: [PATCH 2/4] remove partial, and make requirable optionall a callback --- .../Definition/ValidatedFieldDefinition.php | 9 ++++---- ...ation.php => RequiredFieldsValidation.php} | 23 ++++++++++++++++--- 2 files changed, 25 insertions(+), 7 deletions(-) rename tests/Type/ValidatedFieldDefinition/{NonPartialValidation.php => RequiredFieldsValidation.php} (92%) diff --git a/src/Type/Definition/ValidatedFieldDefinition.php b/src/Type/Definition/ValidatedFieldDefinition.php index e0c61ae..546747b 100644 --- a/src/Type/Definition/ValidatedFieldDefinition.php +++ b/src/Type/Definition/ValidatedFieldDefinition.php @@ -14,8 +14,7 @@ * typeSetter?: callable, * name?: string, * validName?: string, - * partial?: bool, - * required?: bool|array, + * required?: bool|array|callable(): bool, * resultName?: string, * args: array, * resolve?: FieldResolver|null, @@ -208,7 +207,6 @@ protected function _validateInputObject(mixed $arg, mixed $value, array &$res, b protected function _validateInputObjectFields(InputObjectType $type, array $objectConfig, mixed $value, array &$res, bool $isParentList = false): void { $createSubErrors = UserErrorsType::needSuberrors($objectConfig, $isParentList); - $isPartial = $objectConfig['partial'] ?? true; $fields = $type->getFields(); foreach ($fields as $key => $field) { @@ -216,6 +214,9 @@ protected function _validateInputObjectFields(InputObjectType $type, array $obje $isKeyPresent = array_key_exists($key, $value); $isRequired = $config['required'] ?? false; + if(is_callable($isRequired)) { + $isRequired = $isRequired(); + } if($isRequired && !isset($value[$key])) { if ($isRequired === true) { $error = ['error' => [1, "$key is required"]]; @@ -224,7 +225,7 @@ protected function _validateInputObjectFields(InputObjectType $type, array $obje $error = ['error' => $isRequired]; } } - else if (!$isPartial || $isKeyPresent) { + else if ($isKeyPresent) { $error = $this->_validate($config, $value[$key] ?? null); } diff --git a/tests/Type/ValidatedFieldDefinition/NonPartialValidation.php b/tests/Type/ValidatedFieldDefinition/RequiredFieldsValidation.php similarity index 92% rename from tests/Type/ValidatedFieldDefinition/NonPartialValidation.php rename to tests/Type/ValidatedFieldDefinition/RequiredFieldsValidation.php index 3caf18e..e8b5f64 100644 --- a/tests/Type/ValidatedFieldDefinition/NonPartialValidation.php +++ b/tests/Type/ValidatedFieldDefinition/RequiredFieldsValidation.php @@ -8,7 +8,7 @@ use GraphQL\Type\Definition\Type; use GraphQL\Type\Definition\ValidatedFieldDefinition; -final class NonPartialValidation extends FieldDefinition +final class RequiredFieldsValidation extends FieldDefinition { /** @var mixed[] */ protected $data = [ @@ -27,11 +27,9 @@ public function testInputObjectValidationOnFieldFail(): void 'type' => Type::boolean(), 'args' => [ 'bookAttributes' => [ - 'partial' => false, 'type' => function () { // lazy load return new InputObjectType([ 'name' => 'BookAttributes', - 'partial' => false, 'fields' => [ 'title' => [ 'type' => Type::string(), @@ -68,7 +66,18 @@ public function testInputObjectValidationOnFieldFail(): void return 0; }, ], + 'naz' => [ + 'type' => Type::string(), + 'description' => 'Provide a naz', + 'required' => static fn () => true, + 'validate' => function (string $naz) { + if (strlen($naz) < 10) { + return [1, 'naz must be more than 10 characters!']; + } + return 0; + } + ], 'author' => [ 'type' => Type::id(), 'description' => 'Provide a valid author id', @@ -115,6 +124,10 @@ public function testInputObjectValidationOnFieldFail(): void code msg } + naz { + code + msg + } } } result @@ -147,6 +160,10 @@ public function testInputObjectValidationOnFieldFail(): void 'code' => 1, 'msg' => 'We have no record of that author', ], + 'naz' => [ + 'code' => 1, + 'msg' => 'naz is required', + ], ], ], 'result' => null, From 371c95aa81c80baf0b0fc0739e7531eb26e506ce Mon Sep 17 00:00:00 2001 From: shmax Date: Mon, 19 Aug 2024 19:27:42 -0700 Subject: [PATCH 3/4] fix stan --- src/Type/Definition/ValidatedFieldDefinition.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Type/Definition/ValidatedFieldDefinition.php b/src/Type/Definition/ValidatedFieldDefinition.php index 546747b..de6a712 100644 --- a/src/Type/Definition/ValidatedFieldDefinition.php +++ b/src/Type/Definition/ValidatedFieldDefinition.php @@ -14,7 +14,7 @@ * typeSetter?: callable, * name?: string, * validName?: string, - * required?: bool|array|callable(): bool, + * required?: bool|array|callable(): bool|array, * resultName?: string, * args: array, * resolve?: FieldResolver|null, From bd144b734a2015cc74d381a21361912bb901c07e Mon Sep 17 00:00:00 2001 From: shmax Date: Mon, 19 Aug 2024 19:31:08 -0700 Subject: [PATCH 4/4] update docs --- README.md | 11 ++++++++++- src/Type/Definition/ValidatedFieldDefinition.php | 2 +- 2 files changed, 11 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 5e59768..d703b52 100644 --- a/README.md +++ b/README.md @@ -118,7 +118,7 @@ If the value is valid, return `0`, otherwise `1`. ``` ### The `required` property -You can mark any field as `required`, and if the value is not provided, then an automatic validation will happen for you (thus removing the need for you to weaken your validation callback with `null` types). You can set it to `true`, or you can provide an error array similar to the one returned by your validate callback: +You can mark any field as `required`, and if the value is not provided, then an automatic validation will happen for you (thus removing the need for you to weaken your validation callback with `null` types). You can set it to `true`, or you can provide an error array similar to the one returned by your validate callback. You can also set it to a callable that returns the same bool or error array. ```php //... @@ -142,6 +142,15 @@ You can mark any field as `required`, and if the value is not provided, then an } return 1; } + ], + 'naz' => [ + 'required' => static fn() => !Moderator::loggedIn(), + 'validate' => function(string $naz) { + if(Naz::find($naz)) { + return 0; + } + return 1; + } ] ] ]) diff --git a/src/Type/Definition/ValidatedFieldDefinition.php b/src/Type/Definition/ValidatedFieldDefinition.php index de6a712..2d59a5a 100644 --- a/src/Type/Definition/ValidatedFieldDefinition.php +++ b/src/Type/Definition/ValidatedFieldDefinition.php @@ -14,7 +14,7 @@ * typeSetter?: callable, * name?: string, * validName?: string, - * required?: bool|array|callable(): bool|array, + * required?: bool|array|callable(): bool|array, * resultName?: string, * args: array, * resolve?: FieldResolver|null,