Skip to content

Commit

Permalink
Add scalar Null (#15)
Browse files Browse the repository at this point in the history
  • Loading branch information
spawnia committed Dec 6, 2021
1 parent f955521 commit 1d46168
Show file tree
Hide file tree
Showing 19 changed files with 209 additions and 48 deletions.
6 changes: 6 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,12 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0

## Unreleased

## v5.1.0

### Added

- Add scalar `Null`

## v5.0.1

### Fixed
Expand Down
10 changes: 7 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -24,21 +24,25 @@ A [RFC 5321](https://tools.ietf.org/html/rfc5321) compliant email.

### [JSON](src/JSON.php)

Arbitrary data encoded in JavaScript Object Notation. See https://www.json.org/.
Arbitrary data encoded in JavaScript Object Notation. See https://www.json.org.

### [Mixed](src/MixedScalar.php)

Loose type that allows any value. Be careful when passing in large `Int` or `Float` literals,
as they may not be parsed correctly on the server side. Use `String` literals if you are
dealing with really large numbers to be on the safe side.

### [Null](src/NullScalar.php)

Always `null`. Strictly validates value is non-null, no coercion.

### [Regex](src/Regex.php)

The `Regex` class allows you to define a custom scalar that validates that the given
value matches a regular expression.

The quickest way to define a custom scalar is the `make` factory method. Just provide
a name and a regular expression and you will receive a ready-to-use custom regex scalar.
a name and a regular expression, you will receive a ready-to-use custom regex scalar.

```php
use MLL\GraphQLScalars\Regex;
Expand Down Expand Up @@ -78,7 +82,7 @@ DESCRIPTION;
### [StringScalar](src/StringScalar.php)

The `StringScalar` encapsulates all the boilerplate associated with creating a string-based Scalar type.
It does the proper string checking for you and let's you focus on the minimal logic that is specific to your use case.
It performs basic checks and coercion, you can focus on the minimal logic that is specific to your use case.

All you have to specify is a function that checks if the given string is valid.
Use the factory method `make` to generate an instance on the fly.
Expand Down
4 changes: 2 additions & 2 deletions composer.json
Original file line number Diff line number Diff line change
Expand Up @@ -16,10 +16,10 @@
"require": {
"php": "^7.4 || ^8",
"ext-json": "*",
"egulias/email-validator": "^2 || ^3",
"egulias/email-validator": "^2.1.17 || ^3",
"spatie/regex": "^1.4",
"thecodingmachine/safe": "^1.3",
"webonyx/graphql-php": "^0.13 || ^14"
"webonyx/graphql-php": "^0.13.9 || ^14"
},
"require-dev": {
"ergebnis/composer-normalize": "^2.16",
Expand Down
4 changes: 1 addition & 3 deletions src/Email.php
Original file line number Diff line number Diff line change
@@ -1,6 +1,4 @@
<?php

declare(strict_types=1);
<?php declare(strict_types=1);

namespace MLL\GraphQLScalars;

Expand Down
6 changes: 2 additions & 4 deletions src/JSON.php
Original file line number Diff line number Diff line change
@@ -1,6 +1,4 @@
<?php

declare(strict_types=1);
<?php declare(strict_types=1);

namespace MLL\GraphQLScalars;

Expand All @@ -12,7 +10,7 @@
class JSON extends ScalarType
{
public $description /** @lang Markdown */
= 'Arbitrary data encoded in JavaScript Object Notation. See https://www.json.org/.';
= 'Arbitrary data encoded in JavaScript Object Notation. See https://www.json.org.';

public function serialize($value): string
{
Expand Down
4 changes: 1 addition & 3 deletions src/MixedScalar.php
Original file line number Diff line number Diff line change
@@ -1,6 +1,4 @@
<?php

declare(strict_types=1);
<?php declare(strict_types=1);

namespace MLL\GraphQLScalars;

Expand Down
55 changes: 55 additions & 0 deletions src/NullScalar.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
<?php declare(strict_types=1);

namespace MLL\GraphQLScalars;

use GraphQL\Error\Error;
use GraphQL\Error\InvariantViolation;
use GraphQL\Language\AST\NullValueNode;
use GraphQL\Type\Definition\ScalarType;
use GraphQL\Utils\Utils;

class NullScalar extends ScalarType
{
public $name = 'Null';

public $description /** @lang Markdown */
= 'Always `null`. Strictly validates value is non-null, no coercion.';

public function serialize($value)
{
if (null !== $value) {
throw new InvariantViolation(static::notNullMessage($value));
}

return null;
}

public function parseValue($value)
{
if (null !== $value) {
throw new Error(static::notNullMessage($value));
}

return null;
}

public function parseLiteral($valueNode, ?array $variables = null)
{
if (! $valueNode instanceof NullValueNode) {
// Intentionally without message, as all information is already present in the wrapped error
throw new Error('');
}

return null;
}

/**
* @param mixed $value any non-null value
*/
public static function notNullMessage($value): string
{
$notNull = Utils::printSafe($value);

return "Expected null, got: {$notNull}";
}
}
4 changes: 1 addition & 3 deletions src/Regex.php
Original file line number Diff line number Diff line change
@@ -1,6 +1,4 @@
<?php

declare(strict_types=1);
<?php declare(strict_types=1);

namespace MLL\GraphQLScalars;

Expand Down
4 changes: 1 addition & 3 deletions src/StringScalar.php
Original file line number Diff line number Diff line change
@@ -1,6 +1,4 @@
<?php

declare(strict_types=1);
<?php declare(strict_types=1);

namespace MLL\GraphQLScalars;

Expand Down
4 changes: 1 addition & 3 deletions src/Utils.php
Original file line number Diff line number Diff line change
@@ -1,6 +1,4 @@
<?php

declare(strict_types=1);
<?php declare(strict_types=1);

namespace MLL\GraphQLScalars;

Expand Down
4 changes: 1 addition & 3 deletions tests/EmailTest.php
Original file line number Diff line number Diff line change
@@ -1,6 +1,4 @@
<?php

declare(strict_types=1);
<?php declare(strict_types=1);

namespace MLL\GraphQLScalars\Tests;

Expand Down
4 changes: 1 addition & 3 deletions tests/JSONTest.php
Original file line number Diff line number Diff line change
@@ -1,6 +1,4 @@
<?php

declare(strict_types=1);
<?php declare(strict_types=1);

namespace MLL\GraphQLScalars\Tests;

Expand Down
4 changes: 1 addition & 3 deletions tests/MixedScalarTest.php
Original file line number Diff line number Diff line change
@@ -1,6 +1,4 @@
<?php

declare(strict_types=1);
<?php declare(strict_types=1);

namespace MLL\GraphQLScalars\Tests;

Expand Down
4 changes: 1 addition & 3 deletions tests/MyRegex.php
Original file line number Diff line number Diff line change
@@ -1,6 +1,4 @@
<?php

declare(strict_types=1);
<?php declare(strict_types=1);

namespace MLL\GraphQLScalars\Tests;

Expand Down
4 changes: 1 addition & 3 deletions tests/MyStringScalar.php
Original file line number Diff line number Diff line change
@@ -1,6 +1,4 @@
<?php

declare(strict_types=1);
<?php declare(strict_types=1);

namespace MLL\GraphQLScalars\Tests;

Expand Down
124 changes: 124 additions & 0 deletions tests/NullScalarTest.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,124 @@
<?php declare(strict_types=1);

namespace MLL\GraphQLScalars\Tests;

use GraphQL\Executor\ExecutionResult;
use GraphQL\GraphQL;
use GraphQL\Type\Definition\ObjectType;
use GraphQL\Type\Schema;
use GraphQL\Type\SchemaConfig;
use MLL\GraphQLScalars\NullScalar;
use PHPUnit\Framework\TestCase;

final class NullScalarTest extends TestCase
{
private Schema $schema;

/**
* @var mixed will be returned by field mixed
*/
private $return;

public function setUp(): void
{
parent::setUp();

$null = new NullScalar();

$schemaConfig = new SchemaConfig();
$schemaConfig->setQuery(
new ObjectType([
'name' => 'Query',
'fields' => [
'foo' => [
'type' => $null,
'resolve' => function ($root, array $args) {
return reset($args);
},
'args' => [
'bar' => $null,
],
],
'mixed' => [
'type' => $null,
'resolve' => function () {
return $this->return;
},
],
],
])
);

$this->schema = new Schema($schemaConfig);
}

public function testAllowsNullArguments(): void
{
$graphqlResult = $this->executeQueryWithLiteral('null');
self::assertNull($graphqlResult->data['foo']);

$jsonResult = $this->executeQueryWithJsonVariable('null');
self::assertNull($jsonResult->data['foo']);
}

public function testForbidsNonNullArguments(): void
{
$graphqlResult = $this->executeQueryWithLiteral('1');
// @phpstan-ignore-next-line graphql-php is wrong
self::assertNull($graphqlResult->data);
self::assertSame('Field "foo" argument "bar" requires type Null, found 1.', $graphqlResult->errors[0]->getMessage());

$jsonResult = $this->executeQueryWithJsonVariable('1');
// @phpstan-ignore-next-line graphql-php is wrong
self::assertNull($jsonResult->data);
self::assertSame('Variable "$var" got invalid value 1; Expected type Null; Expected null, got: 1', $jsonResult->errors[0]->getMessage());
}

public function testForbidsNonNullReturn(): void
{
$this->return = 1;
$graphqlResult = GraphQL::executeQuery($this->schema, /** @lang GraphQL */ '{ mixed }');
self::assertSame('Expected a value of type "Null" but received: 1', $graphqlResult->errors[0]->getMessage());
self::assertSame(['mixed' => null], $graphqlResult->data);
}

protected function executeQueryWithLiteral(string $literal): ExecutionResult
{
$query = /** @lang GraphQL */ "
{
foo(bar: {$literal})
}
";

return GraphQL::executeQuery(
$this->schema,
$query
);
}

protected function executeQueryWithJsonVariable(string $jsonLiteral): ExecutionResult
{
$query = /** @lang GraphQL */ '
query Foo($var: Null) {
foo(bar: $var)
}
';

/** @var array{var: mixed} $json */
$json = \Safe\json_decode(/** @lang JSON */ <<<JSON
{
"var": $jsonLiteral
}
JSON,
true
);

return GraphQL::executeQuery(
$this->schema,
$query,
null,
null,
$json
);
}
}
4 changes: 1 addition & 3 deletions tests/RegexTest.php
Original file line number Diff line number Diff line change
@@ -1,6 +1,4 @@
<?php

declare(strict_types=1);
<?php declare(strict_types=1);

namespace MLL\GraphQLScalars\Tests;

Expand Down
4 changes: 1 addition & 3 deletions tests/SchemaUsageTest.php
Original file line number Diff line number Diff line change
@@ -1,6 +1,4 @@
<?php

declare(strict_types=1);
<?php declare(strict_types=1);

namespace MLL\GraphQLScalars\Tests;

Expand Down
4 changes: 1 addition & 3 deletions tests/StringScalarTest.php
Original file line number Diff line number Diff line change
@@ -1,6 +1,4 @@
<?php

declare(strict_types=1);
<?php declare(strict_types=1);

namespace MLL\GraphQLScalars\Tests;

Expand Down

0 comments on commit 1d46168

Please sign in to comment.