Skip to content

Commit

Permalink
Add a new abstract scalar IntRange
Browse files Browse the repository at this point in the history
  • Loading branch information
spawnia authored Sep 5, 2024
1 parent 019584c commit c2a38af
Show file tree
Hide file tree
Showing 8 changed files with 176 additions and 28 deletions.
6 changes: 6 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,12 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0

## Unreleased

## v6.3.0

### Added

- Add a new abstract scalar `IntRange`

## v6.2.0

### Added
Expand Down
51 changes: 35 additions & 16 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

A collection of custom scalar types for usage with https://github.com/webonyx/graphql-php

[![Continuous Integration](https://github.com/mll-lab/graphql-php-scalars/workflows/Continuous%20Integration/badge.svg)](https://github.com/mll-lab/graphql-php-scalars/actions)
[![Validate](https://github.com/mll-lab/graphql-php-scalars/workflows/Validate/badge.svg)](https://github.com/mll-lab/graphql-php-scalars/actions)
[![codecov](https://codecov.io/gh/mll-lab/graphql-php-scalars/branch/master/graph/badge.svg)](https://codecov.io/gh/mll-lab/graphql-php-scalars)

[![GitHub license](https://img.shields.io/github/license/mll-lab/graphql-php-scalars.svg)](https://github.com/mll-lab/graphql-php-scalars/blob/master/LICENSE)
Expand All @@ -11,7 +11,9 @@ A collection of custom scalar types for usage with https://github.com/webonyx/gr

## Installation

composer require mll-lab/graphql-php-scalars
```sh
composer require mll-lab/graphql-php-scalars
```

## Usage

Expand Down Expand Up @@ -43,6 +45,27 @@ A datetime string with format `Y-m-d\TH:i:s.uP`, e.g. `2020-04-20T16:20:04+04:00

A [RFC 5321](https://tools.ietf.org/html/rfc5321) compliant email.

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

Allows defining numeric scalars where the values must lie between a defined minimum and maximum.

```php
use MLL\GraphQLScalars\IntRange;

final class UpToADozen extends IntRange
{
protected static function min(): int
{
return 1;
}

protected static function max(): int
{
return 12;
}
}
```

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

Arbitrary data encoded in JavaScript Object Notation. See https://www.json.org.
Expand Down Expand Up @@ -131,13 +154,11 @@ use MLL\GraphQLScalars\Regex;
// The name is implicitly set through the class name here
class HexValue extends Regex
{
/**
* The description that is used for schema introspection.
*/
public ?string $description = <<<'DESCRIPTION'
A hexadecimal color is specified with: `#RRGGBB`, where `RR` (red), `GG` (green) and `BB` (blue)
are hexadecimal integers between `00` and `FF` specifying the intensity of the color.
DESCRIPTION;
/** The description that is used for schema introspection. */
public ?string $description = /** @lang Markdown */<<<'MARKDOWN'
A hexadecimal color is specified with: `#RRGGBB`, where `RR` (red), `GG` (green) and `BB` (blue)
are hexadecimal integers between `00` and `FF` specifying the intensity of the color.
MARKDOWN;

public static function regex(): string
{
Expand All @@ -160,13 +181,11 @@ use MLL\GraphQLScalars\StringScalar;
$coolName = StringScalar::make(
'CoolName',
'A name that is most definitely cool.',
static function (string $name): bool {
return in_array($name, [
'Vladar',
'Benedikt',
'Christopher',
]);
}
static fn (string $name): bool => in_array($name, [
'Vladar',
'Benedikt',
'Christopher',
]),
);
```

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

namespace MLL\GraphQLScalars;

use GraphQL\Error\Error;
use GraphQL\Language\AST\IntValueNode;
use GraphQL\Language\AST\Node;
use GraphQL\Language\Printer;
use GraphQL\Type\Definition\ScalarType;
use GraphQL\Utils\Utils;

abstract class IntRange extends ScalarType
{
/** The minimum allowed value. */
abstract protected static function min(): int;

/** The maximum allowed value. */
abstract protected static function max(): int;

public function serialize($value)
{
if (is_int($value) && $this->isValueInExpectedRange($value)) {
return $value;
}

$notInRange = Utils::printSafe($value);
throw new \InvalidArgumentException("Value not in range {$this->rangeDescription()}: {$notInRange}.");
}

public function parseValue($value)
{
if (is_int($value) && $this->isValueInExpectedRange($value)) {
return $value;
}

$notInRange = Utils::printSafe($value);
throw new Error("Value not in range {$this->rangeDescription()}: {$notInRange}.");
}

public function parseLiteral(Node $valueNode, ?array $variables = null)
{
if ($valueNode instanceof IntValueNode) {
$value = (int) $valueNode->value;
if ($this->isValueInExpectedRange($value)) {
return $value;
}
}

$notInRange = Printer::doPrint($valueNode);
throw new Error("Value not in range {$this->rangeDescription()}: {$notInRange}.", $valueNode);
}

private function isValueInExpectedRange(int $value): bool
{
return $value <= static::max() && $value >= static::min();
}

private function rangeDescription(): string
{
$min = static::min();
$max = static::max();

return "{$min}-{$max}";
}
}
48 changes: 48 additions & 0 deletions tests/IntRangeTest.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
<?php declare(strict_types=1);

namespace MLL\GraphQLScalars\Tests;

use GraphQL\Error\Error;
use PHPUnit\Framework\TestCase;

final class IntRangeTest extends TestCase
{
public function testSerializeThrowsIfNotAnInt(): void
{
$this->expectException(\InvalidArgumentException::class);
$this->expectExceptionMessage('Value not in range 1-12: "12".');

(new UpToADozen())->serialize('12');
}

public function testSerializeThrowsIfInvalid(): void
{
$this->expectException(\InvalidArgumentException::class);
$this->expectExceptionMessage('Value not in range 1-12: 13.');

(new UpToADozen())->serialize(13);
}

public function testSerializePassesWhenValid(): void
{
$serializedResult = (new UpToADozen())->serialize(12);

self::assertSame(12, $serializedResult);
}

public function testParseValueThrowsIfInvalid(): void
{
$this->expectException(Error::class);
$this->expectExceptionMessage('Value not in range 1-12: 13.');

(new UpToADozen())->parseValue(13);
}

public function testParseValuePassesIfValid(): void
{
self::assertSame(
12,
(new UpToADozen())->parseValue(12)
);
}
}
4 changes: 1 addition & 3 deletions tests/MixedScalarTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -27,9 +27,7 @@ public function setUp(): void
'fields' => [
'foo' => [
'type' => $mixed,
'resolve' => function ($root, array $args) {
return reset($args);
},
'resolve' => fn ($root, array $args) => reset($args),
'args' => [
'bar' => $mixed,
],
Expand Down
8 changes: 2 additions & 6 deletions tests/NullScalarTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -30,18 +30,14 @@ public function setUp(): void
'fields' => [
'foo' => [
'type' => $null,
'resolve' => function ($root, array $args) {
return reset($args);
},
'resolve' => fn ($root, array $args) => reset($args),
'args' => [
'bar' => $null,
],
],
'mixed' => [
'type' => $null,
'resolve' => function () {
return $this->return;
},
'resolve' => fn () => $this->return,
],
],
])
Expand Down
4 changes: 1 addition & 3 deletions tests/StringScalarTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -46,9 +46,7 @@ protected function isValid(string $stringValue): bool
StringScalar::make(
'MyStringScalar',
'Bar',
function (string $string) {
return $string === 'foo';
}
fn (string $string) => $string === 'foo'
),
];
}
Expand Down
18 changes: 18 additions & 0 deletions tests/UpToADozen.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
<?php declare(strict_types=1);

namespace MLL\GraphQLScalars\Tests;

use MLL\GraphQLScalars\IntRange;

final class UpToADozen extends IntRange
{
protected static function min(): int
{
return 1;
}

protected static function max(): int
{
return 12;
}
}

0 comments on commit c2a38af

Please sign in to comment.