-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Add Time (of the day) GraphQL scalar type #21
- Loading branch information
Showing
3 changed files
with
145 additions
and
0 deletions.
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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,62 @@ | ||
<?php | ||
|
||
declare(strict_types=1); | ||
|
||
namespace Ecodev\Felix\Api\Scalar; | ||
|
||
use Cake\Chronos\ChronosTime; | ||
use GraphQL\Error\Error; | ||
use GraphQL\Language\AST\Node; | ||
use GraphQL\Language\AST\StringValueNode; | ||
use GraphQL\Type\Definition\ScalarType; | ||
use GraphQL\Utils\Utils; | ||
use UnexpectedValueException; | ||
|
||
final class TimeType extends ScalarType | ||
{ | ||
public ?string $description = 'A time of the day (local time, no timezone).'; | ||
|
||
/** | ||
* Serializes an internal value to include in a response. | ||
*/ | ||
public function serialize(mixed $value): mixed | ||
{ | ||
if ($value instanceof ChronosTime) { | ||
return $value->format('H:i:s.u'); | ||
} | ||
|
||
return $value; | ||
} | ||
|
||
/** | ||
* Parses an externally provided value (query variable) to use as an input. | ||
*/ | ||
public function parseValue(mixed $value): ?ChronosTime | ||
{ | ||
if (!is_string($value)) { | ||
throw new UnexpectedValueException('Cannot represent value as Chronos time: ' . Utils::printSafe($value)); | ||
} | ||
|
||
if ($value === '') { | ||
return null; | ||
} | ||
|
||
$time = new ChronosTime($value); | ||
|
||
return $time; | ||
} | ||
|
||
/** | ||
* Parses an externally provided literal value to use as an input (e.g. in Query AST). | ||
*/ | ||
public function parseLiteral(Node $valueNode, ?array $variables = null): ?ChronosTime | ||
{ | ||
// Note: throwing GraphQL\Error\Error vs \UnexpectedValueException to benefit from GraphQL | ||
// error location in query: | ||
if (!($valueNode instanceof StringValueNode)) { | ||
throw new Error('Query error: Can only parse strings got: ' . $valueNode->kind, $valueNode); | ||
} | ||
|
||
return $this->parseValue($valueNode->value); | ||
} | ||
} |
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,26 @@ | ||
<?php | ||
|
||
declare(strict_types=1); | ||
|
||
namespace Ecodev\Felix\DBAL\Types; | ||
|
||
use Cake\Chronos\ChronosTime; | ||
use DateTimeInterface; | ||
use Doctrine\DBAL\Platforms\AbstractPlatform; | ||
|
||
final class TimeType extends \Doctrine\DBAL\Types\TimeType | ||
{ | ||
/** | ||
* @param null|ChronosTime|DateTimeInterface|string $value | ||
*/ | ||
public function convertToPHPValue(mixed $value, AbstractPlatform $platform): ?ChronosTime | ||
{ | ||
if ($value === null || $value instanceof ChronosTime) { | ||
return $value; | ||
} | ||
|
||
$val = new ChronosTime($value); | ||
|
||
return $val; | ||
} | ||
} |
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,57 @@ | ||
<?php | ||
|
||
declare(strict_types=1); | ||
|
||
namespace EcodevTests\Felix\Api\Scalar; | ||
|
||
use Cake\Chronos\ChronosTime; | ||
use Ecodev\Felix\Api\Scalar\TimeType; | ||
use GraphQL\Language\AST\IntValueNode; | ||
use GraphQL\Language\AST\StringValueNode; | ||
use PHPUnit\Framework\TestCase; | ||
|
||
final class TimeTypeTest extends TestCase | ||
{ | ||
public function testSerialize(): void | ||
{ | ||
$type = new TimeType(); | ||
$time = new ChronosTime('14:30:25'); | ||
$actual = $type->serialize($time); | ||
self::assertSame('14:30:25.000000', $actual); | ||
|
||
// Test serialize with microseconds | ||
$time = new ChronosTime('23:59:59.1254'); | ||
$actual = $type->serialize($time); | ||
self::assertSame('23:59:59.001254', $actual); | ||
} | ||
|
||
/** | ||
* @dataProvider providerValues | ||
*/ | ||
public function testParseLiteral(string $input, ?string $expected): void | ||
{ | ||
$type = new TimeType(); | ||
$ast = new StringValueNode(['value' => $input]); | ||
|
||
$actual = $type->parseLiteral($ast); | ||
self::assertInstanceOf(ChronosTime::class, $actual); | ||
self::assertSame($expected, $actual->format('H:i:s.u')); | ||
} | ||
|
||
public function testParseLiteralAsInt(): void | ||
{ | ||
$type = new TimeType(); | ||
$ast = new IntValueNode(['value' => '123']); | ||
|
||
$this->expectExceptionMessage('Query error: Can only parse strings got: IntValue'); | ||
$type->parseLiteral($ast); | ||
} | ||
|
||
public static function providerValues(): array | ||
{ | ||
return [ | ||
'normal timr' => ['14:30:25', '14:30:25.000000'], | ||
'time with milliseconds' => ['23:45:13.300', '23:45:13.000300'], | ||
]; | ||
} | ||
} |