Skip to content

Commit

Permalink
Add Time (of the day) GraphQL scalar type #21
Browse files Browse the repository at this point in the history
  • Loading branch information
stissot authored and PowerKiKi committed Nov 14, 2023
1 parent 68037bc commit 9aff229
Show file tree
Hide file tree
Showing 3 changed files with 145 additions and 0 deletions.
62 changes: 62 additions & 0 deletions src/Api/Scalar/TimeType.php
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);
}
}
26 changes: 26 additions & 0 deletions src/DBAL/Types/TimeType.php
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;
}
}
57 changes: 57 additions & 0 deletions tests/Api/Scalar/TimeTypeTest.php
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'],
];
}
}

0 comments on commit 9aff229

Please sign in to comment.