Skip to content

Commit b37150e

Browse files
committed
Fully implement Regex scalar and tests
1 parent f4aac2d commit b37150e

File tree

5 files changed

+280
-55
lines changed

5 files changed

+280
-55
lines changed

README.md

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,3 +17,38 @@ A collection of custom scalar types for usage with https://github.com/webonyx/gr
1717

1818
You can use the provided Scalars just like any other type in your schema definition.
1919
Check [SchemaUsageTest](tests/SchemaUsageTest.php) for an example.
20+
21+
### The Regex Scalar
22+
23+
The `Regex` class allows you to define a custom scalar that validates that the given
24+
value matches a regular expression.
25+
26+
The quickest way to define a custom scalar is the `make` factory method. Just provide
27+
a name and a regular expression and you will receive a ready-to-use custom regex scalar.
28+
29+
```php
30+
<?php
31+
32+
use MLL\GraphQLScalars\Regex;
33+
34+
$hexValue = Regex::make('HexValue', '/^#?([a-f0-9]{6}|[a-f0-9]{3})$/');
35+
36+
$hexValue instanceof \GraphQL\Type\Definition\ScalarType; // true
37+
```
38+
39+
You may also define your regex scalar as a class.
40+
41+
```php
42+
<?php
43+
44+
use MLL\GraphQLScalars\Regex;
45+
46+
// The name is implicitly set through the class name here
47+
class HexValue extends Regex
48+
{
49+
protected function regex() : string
50+
{
51+
return '/^#?([a-f0-9]{6}|[a-f0-9]{3})$/';
52+
}
53+
}
54+
```

composer.json

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,17 @@
11
{
22
"name": "mll-lab/graphql-php-scalars",
33
"description": "A collection of custom scalar types for usage with https://github.com/webonyx/graphql-php",
4-
"license": "MIT",
4+
"type": "library",
55
"keywords": ["graphql", "php"],
6+
"license": "MIT",
67
"authors": [
78
{
89
"name": "Benedikt Franke",
910
"email": "[email protected]"
1011
}
1112
],
1213
"require": {
14+
"php": ">=7.0",
1315
"webonyx/graphql-php": "^0.12.6",
1416
"spatie/regex": "^1.3"
1517
},
@@ -24,6 +26,11 @@
2426
"MLL\\GraphQLScalars\\": "src"
2527
}
2628
},
29+
"autoload-dev": {
30+
"psr-4": {
31+
"Tests\\": "tests/"
32+
}
33+
},
2734
"scripts": {
2835
"test": "phpunit"
2936
}

src/Regex.php

Lines changed: 76 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -4,26 +4,66 @@
44

55
namespace MLL\GraphQLScalars;
66

7+
use GraphQL\Error\Error;
78
use GraphQL\Error\InvariantViolation;
89
use GraphQL\Language\AST\Node;
10+
use GraphQL\Language\AST\StringValueNode;
911
use GraphQL\Type\Definition\ScalarType;
12+
use GraphQL\Utils\Utils;
1013
use Spatie\Regex\Regex as RegexValidator;
1114

1215
abstract class Regex extends ScalarType
1316
{
17+
/**
18+
* Return the Regex that the values are validated against.
19+
*
20+
* @return string
21+
*/
22+
abstract protected function regex(): string;
23+
24+
/**
25+
* This factory method allows you to create a Regex scalar in a one-liner.
26+
*
27+
* @param string $name
28+
* @param string $regex
29+
*
30+
* @return Regex
31+
*/
32+
public static function make(string $name, string $regex): Regex
33+
{
34+
$regexClass = new class() extends Regex {
35+
/**
36+
* Return the Regex that the values are validated against.
37+
*
38+
* Must be a valid
39+
*
40+
* @return string
41+
*/
42+
protected function regex(): string
43+
{
44+
return $this->regex;
45+
}
46+
};
47+
48+
$regexClass->name = $name;
49+
$regexClass->regex = $regex;
50+
51+
return $regexClass;
52+
}
53+
1454
/**
1555
* Serializes an internal value to include in a response.
1656
*
17-
* @param string $value
57+
* @param mixed $value
1858
*
1959
* @return string
2060
*/
2161
public function serialize($value): string
2262
{
2363
if (!canBeString($value)) {
24-
$valueClass = get_class($value);
64+
$safeValue = Utils::printSafe($value);
2565

26-
throw new InvariantViolation("The given value of class $valueClass can not be serialized.");
66+
throw new InvariantViolation("The given value {$safeValue} can not be serialized.");
2767
}
2868

2969
$stringValue = strval($value);
@@ -34,21 +74,34 @@ public function serialize($value): string
3474

3575
return $stringValue;
3676
}
37-
77+
3878
/**
3979
* Parses an externally provided value (query variable) to use as an input.
4080
*
4181
* @param mixed $value
4282
*
83+
* @throws Error
84+
*
4385
* @return mixed
4486
*/
45-
public function parseValue($value)
87+
public function parseValue($value): string
4688
{
47-
// TODO implement validation
89+
if (!canBeString($value)) {
90+
$safeValue = Utils::printSafe($value);
91+
92+
throw new Error("The given value {$safeValue} can not be serialized.");
93+
}
94+
95+
$stringValue = strval($value);
96+
97+
if(!$this->matchesRegex($stringValue)){
98+
$safeValue = Utils::printSafeJson($stringValue);
99+
throw new Error("The given value {$safeValue} did not match the regex {$this->regex()}");
100+
}
48101

49102
return $value;
50103
}
51-
104+
52105
/**
53106
* Parses an externally provided literal value (hardcoded in GraphQL query) to use as an input.
54107
*
@@ -60,13 +113,24 @@ public function parseValue($value)
60113
* @param Node $valueNode
61114
* @param array $variables
62115
*
63-
* @return mixed
116+
* @throws Error
117+
*
118+
* @return string
64119
*/
65-
public function parseLiteral($valueNode, array $variables = null)
120+
public function parseLiteral($valueNode, array $variables = null): string
66121
{
67-
// TODO implement validation
68-
69-
return $valueNode->value;
122+
if (!$valueNode instanceof StringValueNode) {
123+
throw new Error("Query error: Can only parse strings got: {$valueNode->kind}", [$valueNode]);
124+
}
125+
126+
$value = $valueNode->value;
127+
128+
if(!$this->matchesRegex($value)){
129+
$safeValue = Utils::printSafeJson($value);
130+
throw new Error("The given value {$safeValue} did not match the regex {$this->regex()}", [$valueNode]);
131+
}
132+
133+
return $value;
70134
}
71135

72136
/**
@@ -81,13 +145,4 @@ protected function matchesRegex(string $value): bool
81145
$value
82146
)->hasMatch();
83147
}
84-
85-
/**
86-
* Return the Regex that the values are validated against.
87-
*
88-
* Must be a valid
89-
*
90-
* @return string
91-
*/
92-
abstract protected function regex(): string;
93148
}

tests/Foo.php

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
<?php
2+
3+
namespace Tests;
4+
5+
use MLL\GraphQLScalars\Regex;
6+
7+
class Foo extends Regex
8+
{
9+
/**
10+
* Return the Regex that the values are validated against.
11+
*
12+
* Must be a valid
13+
*
14+
* @return string
15+
*/
16+
protected function regex(): string
17+
{
18+
return '/foo/';
19+
}
20+
}

0 commit comments

Comments
 (0)