Skip to content

Commit 36a4d3e

Browse files
committed
Add GraphQL Url scalar
1 parent 961e2f9 commit 36a4d3e

File tree

4 files changed

+108
-10
lines changed

4 files changed

+108
-10
lines changed

app/GraphQL/Scalars/Url.php

Lines changed: 72 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,72 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace App\GraphQL\Scalars;
6+
7+
use GraphQL\Error\Error;
8+
use GraphQL\Error\InvariantViolation;
9+
use GraphQL\Language\AST\Node;
10+
use GraphQL\Language\AST\StringValueNode;
11+
use GraphQL\Language\AST\ValueNode;
12+
use GraphQL\Type\Definition\ScalarType;
13+
use Illuminate\Support\Facades\Validator;
14+
15+
final class Url extends ScalarType
16+
{
17+
protected function validate(mixed $value): bool
18+
{
19+
return Validator::make([
20+
'value' => $value,
21+
], [
22+
'value' => 'url',
23+
])->passes();
24+
}
25+
26+
/**
27+
* Serializes an internal value to include in a response.
28+
*
29+
* @throws InvariantViolation
30+
*/
31+
public function serialize(mixed $value): string
32+
{
33+
if (!$this->validate($value)) {
34+
throw new InvariantViolation("Could not serialize {$value} as URL.");
35+
}
36+
37+
return $this->parseValue($value);
38+
}
39+
40+
/**
41+
* Parses an externally provided value (query variable) to use as an input.
42+
*
43+
* @throws InvariantViolation
44+
*/
45+
public function parseValue(mixed $value): string
46+
{
47+
if (!$this->validate($value)) {
48+
throw new InvariantViolation("Could not parse {$value} as URL.");
49+
}
50+
51+
return (string) $value;
52+
}
53+
54+
/**
55+
* Parses an externally provided literal value (hardcoded in GraphQL query) to use as an input.
56+
*
57+
* Should throw an exception with a client friendly message on invalid value nodes.
58+
*
59+
* @param ValueNode&Node $valueNode
60+
* @param array<string, mixed>|null $variables
61+
*
62+
* @throws Error
63+
*/
64+
public function parseLiteral(Node $valueNode, ?array $variables = null): string
65+
{
66+
if (!($valueNode instanceof StringValueNode)) {
67+
throw new Error("Query error: Can only parse Strings, got {$valueNode->kind}.", $valueNode);
68+
}
69+
70+
return (string) $valueNode->value;
71+
}
72+
}

graphql/schema.graphql

Lines changed: 11 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,8 @@ scalar NonNegativeSeconds @scalar(class: "App\\GraphQL\\Scalars\\NonNegativeSeco
1111
"A non-negative integer number of milliseconds."
1212
scalar NonNegativeIntegerMilliseconds @scalar(class: "App\\GraphQL\\Scalars\\NonNegativeIntegerMilliseconds")
1313

14+
scalar Url @scalar(class: "App\\GraphQL\\Scalars\\Url")
15+
1416

1517
"Indicates what fields are available at the top level of a query operation."
1618
type Query {
@@ -144,7 +146,10 @@ type Project {
144146
description: String!
145147

146148
"Homepage for this project."
147-
homeurl: String!
149+
homeurl: Url @deprecated(reason: "Use 'homeUrl' instead.")
150+
151+
"Homepage for this project."
152+
homeUrl: Url @rename(attribute: "homeurl")
148153

149154
"Visibility."
150155
visibility: ProjectVisibility! @rename(attribute: "public") @filterable
@@ -201,13 +206,16 @@ enum ProjectVisibility {
201206

202207
input CreateProjectInput {
203208
"Unique name."
204-
name: String!
209+
name: String! @rules(apply: ["App\\Rules\\ProjectVisibilityRule"])
205210

206211
"Description."
207212
description: String!
208213

209214
"Project homepage"
210-
homeurl: String!
215+
homeurl: Url @deprecated(reason: "Use 'homeUrl' instead.") @rules(apply: ["prohibits:homeUrl"])
216+
217+
"Project homepage"
218+
homeUrl: Url @rename(attribute: "homeurl") @rules(apply: ["prohibits:homeurl"])
211219

212220
"Visibility."
213221
visibility: ProjectVisibility! @rename(attribute: "public") @rules(attribute: "public", apply: ["App\\Rules\\ProjectVisibilityRule"])

phpstan-baseline.neon

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -342,6 +342,24 @@ parameters:
342342
count: 2
343343
path: app/GraphQL/Scalars/NonNegativeSeconds.php
344344

345+
-
346+
rawMessage: Cannot cast mixed to string.
347+
identifier: cast.string
348+
count: 1
349+
path: app/GraphQL/Scalars/Url.php
350+
351+
-
352+
rawMessage: Casting to string something that's already string.
353+
identifier: cast.useless
354+
count: 1
355+
path: app/GraphQL/Scalars/Url.php
356+
357+
-
358+
rawMessage: 'Part $value (mixed) of encapsed string cannot be cast to string.'
359+
identifier: encapsedStringPart.nonString
360+
count: 2
361+
path: app/GraphQL/Scalars/Url.php
362+
345363
-
346364
rawMessage: Class App\Http\Controllers\AbstractBuildController has an uninitialized property $build. Give it default value or assign it in the constructor.
347365
identifier: property.uninitialized

tests/Feature/GraphQL/ProjectTypeTest.php

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -351,7 +351,7 @@ public function testCreateProjectNoUser(): void
351351
'input' => [
352352
'name' => $name,
353353
'description' => 'test',
354-
'homeurl' => 'https://cdash.org',
354+
'homeUrl' => 'https://cdash.org',
355355
'visibility' => 'PUBLIC',
356356
'authenticateSubmissions' => false,
357357
],
@@ -377,7 +377,7 @@ public function testCreateProjectUnauthorizedUser(): void
377377
'input' => [
378378
'name' => $name,
379379
'description' => 'test',
380-
'homeurl' => 'https://cdash.org',
380+
'homeUrl' => 'https://cdash.org',
381381
'visibility' => 'PUBLIC',
382382
'authenticateSubmissions' => false,
383383
],
@@ -405,7 +405,7 @@ public function testCreateProjectUserCreateProjectNoUser(): void
405405
'input' => [
406406
'name' => $name,
407407
'description' => 'test',
408-
'homeurl' => 'https://cdash.org',
408+
'homeUrl' => 'https://cdash.org',
409409
'visibility' => 'PUBLIC',
410410
'authenticateSubmissions' => false,
411411
],
@@ -433,7 +433,7 @@ public function testCreateProjectUserCreateProject(): void
433433
'input' => [
434434
'name' => $name,
435435
'description' => 'test',
436-
'homeurl' => 'https://cdash.org',
436+
'homeUrl' => 'https://cdash.org',
437437
'visibility' => 'PUBLIC',
438438
'authenticateSubmissions' => false,
439439
],
@@ -467,7 +467,7 @@ public function testCreateProjectAdmin(): void
467467
'input' => [
468468
'name' => $name,
469469
'description' => 'test',
470-
'homeurl' => 'https://cdash.org',
470+
'homeUrl' => 'https://cdash.org',
471471
'visibility' => 'PUBLIC',
472472
'authenticateSubmissions' => false,
473473
],
@@ -952,7 +952,7 @@ public function testCreateProjectMaxVisibility(string $user, string $visibility,
952952
'input' => [
953953
'name' => $name,
954954
'description' => 'test',
955-
'homeurl' => 'https://cdash.org',
955+
'homeUrl' => 'https://cdash.org',
956956
'visibility' => $visibility,
957957
'authenticateSubmissions' => false,
958958
],
@@ -1023,7 +1023,7 @@ public function testRequireAuthenticatedSubmissions(
10231023
'input' => [
10241024
'name' => $name,
10251025
'description' => 'test',
1026-
'homeurl' => 'https://cdash.org',
1026+
'homeUrl' => 'https://cdash.org',
10271027
'visibility' => 'PUBLIC',
10281028
'authenticateSubmissions' => $use_authenticated_submits,
10291029
],

0 commit comments

Comments
 (0)