-
Notifications
You must be signed in to change notification settings - Fork 12
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
4 changed files
with
168 additions
and
1 deletion.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,95 @@ | ||
<?php | ||
|
||
declare(strict_types=1); | ||
|
||
namespace MLL\GraphQLScalars; | ||
|
||
use Exception; | ||
use GraphQL\Error\Error; | ||
use GraphQL\Language\AST\Node; | ||
use GraphQL\Type\Definition\ScalarType; | ||
use GraphQL\Utils\Utils; | ||
use Safe\Exceptions\JsonException; | ||
|
||
class JSON extends ScalarType | ||
{ | ||
/** | ||
* The description that is used for schema introspection. | ||
* | ||
* @var string | ||
*/ | ||
public $description = 'Arbitrary data encoded in JavaScript Object Notation. See https://www.json.org/.'; | ||
|
||
/** | ||
* Serializes an internal value to include in a response. | ||
* | ||
* @param mixed $value | ||
* | ||
* @return string | ||
*/ | ||
public function serialize($value): string | ||
{ | ||
return \Safe\json_encode($value); | ||
} | ||
|
||
/** | ||
* Parses an externally provided value (query variable) to use as an input | ||
* | ||
* In the case of an invalid value this method must throw an Exception | ||
* | ||
* @param mixed $value | ||
* | ||
* @return mixed | ||
* | ||
* @throws Error | ||
*/ | ||
public function parseValue($value) | ||
{ | ||
return $this->decodeJSON($value); | ||
} | ||
|
||
/** | ||
* Parses an externally provided literal value (hardcoded in GraphQL query) to use as an input | ||
* | ||
* In the case of an invalid node or value this method must throw an Exception | ||
* | ||
* @param Node $valueNode | ||
* @param mixed[]|null $variables | ||
* | ||
* @return mixed | ||
* | ||
* @throws Exception | ||
*/ | ||
public function parseLiteral($valueNode, ?array $variables = null) | ||
{ | ||
if(!property_exists($valueNode, 'value')){ | ||
throw new Error( | ||
'Can only parse literals that contain a value, got ' . Utils::printSafeJson($valueNode) | ||
); | ||
} | ||
|
||
return $this->decodeJSON($valueNode->value); | ||
} | ||
|
||
/** | ||
* Try to decode a user-given value into JSON. | ||
* | ||
* @param mixed $value | ||
* | ||
* @return mixed | ||
* | ||
* @throws Error | ||
*/ | ||
protected function decodeJSON($value) | ||
{ | ||
try { | ||
$parsed = \Safe\json_decode($value); | ||
} catch (JsonException $jsonException) { | ||
throw new Error( | ||
$jsonException->getMessage() | ||
); | ||
} | ||
|
||
return $parsed; | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,67 @@ | ||
<?php | ||
|
||
declare(strict_types=1); | ||
|
||
namespace Tests; | ||
|
||
use GraphQL\Error\Error; | ||
use GraphQL\Language\AST\StringValueNode; | ||
use MLL\GraphQLScalars\JSON; | ||
use PHPUnit\Framework\TestCase; | ||
use Safe\Exceptions\JsonException; | ||
|
||
class JSONTest extends TestCase | ||
{ | ||
const INVALID_UTF8_SEQUENCE = "\xB1\x31"; | ||
|
||
public function testSerializeThrowsIfNonEncodableValueIsGiven(): void | ||
{ | ||
$this->expectException(JsonException::class); | ||
|
||
(new JSON())->serialize(self::INVALID_UTF8_SEQUENCE); | ||
} | ||
|
||
public function testSerializeThrowsIfJSONIsInvalid(): void | ||
{ | ||
$this->expectException(JsonException::class); | ||
|
||
(new JSON())->serialize(self::INVALID_UTF8_SEQUENCE); | ||
} | ||
|
||
public function testSerializePassesWhenJSONIsValid(): void | ||
{ | ||
$serializedResult = (new JSON())->serialize(1); | ||
|
||
$this->assertSame('1', $serializedResult); | ||
} | ||
|
||
public function testParseValueThrowsIfJSONIsInvalid(): void | ||
{ | ||
$this->expectException(Error::class); | ||
|
||
(new JSON())->parseValue('foo'); | ||
} | ||
|
||
public function testParseValuePassesIfJSONIsValid(): void | ||
{ | ||
$this->assertSame( | ||
[1, 2], | ||
(new JSON())->parseValue('[1, 2]') | ||
); | ||
} | ||
|
||
public function testParseLiteralThrowsIfNotValidJSON(): void | ||
{ | ||
$this->expectException(Error::class); | ||
|
||
(new JSON())->parseLiteral(new StringValueNode(['value' => 'foo'])); | ||
} | ||
|
||
public function testParseLiteralPassesIfJSONIsValid(): void | ||
{ | ||
$this->assertSame( | ||
'bar', | ||
(new JSON())->parseLiteral(new StringValueNode(['value' => '{"foo": "bar"}']))->foo | ||
); | ||
} | ||
} |