Skip to content

Commit

Permalink
Merge branch 'master' into feat-cache-validation-rules
Browse files Browse the repository at this point in the history
# Conflicts:
#	CHANGELOG.md
  • Loading branch information
k0ka committed Sep 19, 2024
2 parents b7ee225 + 82d0272 commit c8eb73d
Show file tree
Hide file tree
Showing 10 changed files with 225 additions and 33 deletions.
18 changes: 18 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,24 @@ You can find and compare releases at the [GitHub release page](https://github.co

- Cache query validation results https://github.com/nuwave/lighthouse/pull/2603

## v6.44.2

### Fixed

- Apply `@convertEmptyStringsToNull` to input fields when used upon fields https://github.com/nuwave/lighthouse/issues/2610

## v6.44.1

### Fixed

- Ensure `deprecationReason` is set on arguments and input fields https://github.com/nuwave/lighthouse/pull/2609

## v6.44.0

### Added

- Allow `@deprecated` directive on arguments and input fields https://github.com/nuwave/lighthouse/pull/2607

## v6.43.1

### Changed
Expand Down
18 changes: 10 additions & 8 deletions docs/6/api-reference/directives.md
Original file line number Diff line number Diff line change
Expand Up @@ -896,7 +896,10 @@ final class ComplexityAnalyzer
```graphql
"""
Replaces `""` with `null`.
Replaces incoming empty strings `""` with `null`.

When used upon fields, empty strings for non-nullable inputs will pass unchanged.
Only explicitly placing this on non-nullable inputs will force the conversion.
"""
directive @convertEmptyStringsToNull on ARGUMENT_DEFINITION | INPUT_FIELD_DEFINITION | FIELD_DEFINITION
```
Expand Down Expand Up @@ -1165,17 +1168,16 @@ Marks an element of a GraphQL schema as no longer supported.
"""
directive @deprecated(
"""
Explains why this element was deprecated, usually also including a
suggestion for how to access supported similar data.
Formatted in [Markdown](https://commonmark.org).
Explains why this element was deprecated.
It is also beneficial to suggest what to use instead.
Formatted in Markdown, as specified by [CommonMark](https://commonmark.org).
"""
reason: String = "No longer supported"
) on FIELD_DEFINITION | ENUM_VALUE
) on FIELD_DEFINITION | ARGUMENT_DEFINITION | INPUT_FIELD_DEFINITION | ENUM_VALUE
```
You can mark fields as deprecated by adding the [@deprecated](#deprecated) directive.
It is recommended to provide a `reason` for the deprecation, as well as a suggestion on
how to move forward.
You can indicate schema elements are no longer supported by adding the [@deprecated](#deprecated) directive.
It is recommended to provide a `reason` for the deprecation, as well as suggest a replacement.
```graphql
type Query {
Expand Down
18 changes: 10 additions & 8 deletions docs/master/api-reference/directives.md
Original file line number Diff line number Diff line change
Expand Up @@ -896,7 +896,10 @@ final class ComplexityAnalyzer
```graphql
"""
Replaces `""` with `null`.
Replaces incoming empty strings `""` with `null`.

When used upon fields, empty strings for non-nullable inputs will pass unchanged.
Only explicitly placing this on non-nullable inputs will force the conversion.
"""
directive @convertEmptyStringsToNull on ARGUMENT_DEFINITION | INPUT_FIELD_DEFINITION | FIELD_DEFINITION
```
Expand Down Expand Up @@ -1165,17 +1168,16 @@ Marks an element of a GraphQL schema as no longer supported.
"""
directive @deprecated(
"""
Explains why this element was deprecated, usually also including a
suggestion for how to access supported similar data.
Formatted in [Markdown](https://commonmark.org).
Explains why this element was deprecated.
It is also beneficial to suggest what to use instead.
Formatted in Markdown, as specified by [CommonMark](https://commonmark.org).
"""
reason: String = "No longer supported"
) on FIELD_DEFINITION | ENUM_VALUE
) on FIELD_DEFINITION | ARGUMENT_DEFINITION | INPUT_FIELD_DEFINITION | ENUM_VALUE
```
You can mark fields as deprecated by adding the [@deprecated](#deprecated) directive.
It is recommended to provide a `reason` for the deprecation, as well as a suggestion on
how to move forward.
You can indicate schema elements are no longer supported by adding the [@deprecated](#deprecated) directive.
It is recommended to provide a `reason` for the deprecation, as well as suggest a replacement.
```graphql
type Query {
Expand Down
2 changes: 1 addition & 1 deletion phpstan.neon
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,7 @@ parameters:
- '#Call to an undefined method Illuminate\\Database\\Eloquent\\Builder<Illuminate\\Database\\Eloquent\\Model>|Illuminate\\Database\\Eloquent\\Relations\\Relation<Illuminate\\Database\\Eloquent\\Model>|Illuminate\\Database\\Query\\Builder::(orderBy|where|whereIn|whereNotIn|whereBetween|whereJsonContains|whereNotBetween)\(\)\.#'

# Laravel 11 added generics that are handled differently, so we just omit them
- '#generic class Illuminate\\Database\\Eloquent\\Builder but does not specify its types#'
- '#generic class (Illuminate\\Database\\Eloquent\\Builder|Laravel\\Scout\\Builder)( but)? does not specify its types#'

# This test cheats and uses reflection to make assertions
- path: tests/Unit/Schema/Directives/BaseDirectiveTest.php
Expand Down
2 changes: 1 addition & 1 deletion src/Schema/AST/ASTHelper.php
Original file line number Diff line number Diff line change
Expand Up @@ -327,7 +327,7 @@ public static function qualifiedArgType(
}

/** Given a collection of directives, returns the string value for the deprecation reason. */
public static function deprecationReason(EnumValueDefinitionNode|FieldDefinitionNode $node): ?string
public static function deprecationReason(EnumValueDefinitionNode|FieldDefinitionNode|InputValueDefinitionNode $node): ?string
{
$deprecated = Values::getDirectiveValues(
DirectiveDefinition::deprecatedDirective(),
Expand Down
24 changes: 13 additions & 11 deletions src/Schema/Directives/ConvertEmptyStringsToNullDirective.php
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,10 @@ public static function definition(): string
{
return /** @lang GraphQL */ <<<'GRAPHQL'
"""
Replaces `""` with `null`.
Replaces incoming empty strings `""` with `null`.
When used upon fields, empty strings for non-nullable inputs will pass unchanged.
Only explicitly placing this on non-nullable inputs will force the conversion.
"""
directive @convertEmptyStringsToNull on ARGUMENT_DEFINITION | INPUT_FIELD_DEFINITION | FIELD_DEFINITION
GRAPHQL;
Expand All @@ -41,12 +44,13 @@ protected function transformArgumentSet(ArgumentSet $argumentSet): ArgumentSet
{
foreach ($argumentSet->arguments as $argument) {
$namedType = $argument->namedType();
if (
$namedType !== null
$argumentValue = $argument->value;

$isNullableStringType = $namedType !== null
&& $namedType->name === ScalarType::STRING
&& ! $namedType->nonNull
) {
$argument->value = $this->sanitize($argument->value);
&& ! $namedType->nonNull;
if ($isNullableStringType || $argumentValue instanceof ArgumentSet) {
$argument->value = $this->sanitize($argumentValue);
}
}

Expand All @@ -60,10 +64,8 @@ protected function transformArgumentSet(ArgumentSet $argumentSet): ArgumentSet
*/
protected function transformLeaf(mixed $value): mixed
{
if ($value === '') {
return null;
}

return $value;
return $value === ''
? null
: $value;
}
}
8 changes: 4 additions & 4 deletions src/Schema/Directives/DeprecatedDirective.php
Original file line number Diff line number Diff line change
Expand Up @@ -14,12 +14,12 @@ public static function definition(): string
"""
directive @deprecated(
"""
Explains why this element was deprecated, usually also including a
suggestion for how to access supported similar data. Formatted
in [Markdown](https://daringfireball.net/projects/markdown).
Explains why this element was deprecated.
It is also beneficial to suggest what to use instead.
Formatted in Markdown, as specified by [CommonMark](https://commonmark.org).
"""
reason: String = "No longer supported"
) on FIELD_DEFINITION | ENUM_VALUE
) on FIELD_DEFINITION | ARGUMENT_DEFINITION | INPUT_FIELD_DEFINITION | ENUM_VALUE
GRAPHQL;
}
}
1 change: 1 addition & 0 deletions src/Schema/Factories/ArgumentFactory.php
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,7 @@ public function convert(InputValueDefinitionNode $definitionNode): array
'name' => $definitionNode->name->value,
'description' => $definitionNode->description?->value,
'type' => $type,
'deprecationReason' => ASTHelper::deprecationReason($definitionNode),
'astNode' => $definitionNode,
];

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

namespace Tests\Integration\Schema\Directives;

use Nuwave\Lighthouse\Schema\Directives\ConvertEmptyStringsToNullDirective;
use Tests\TestCase;

final class ConvertEmptyStringsToNullDirectiveTest extends TestCase
Expand Down Expand Up @@ -159,4 +160,154 @@ public function testConvertsNonNullableArgumentsWhenUsedOnArgument(): void
],
]);
}

public function testConvertsEmptyStringToNullWithFieldDirective(): void
{
$this->schema = /** @lang GraphQL */ '
type Query {
foo(bar: String): FooResponse
@convertEmptyStringsToNull
@field(resolver: "Tests\\\Utils\\\Mutations\\\ReturnReceivedInput")
}
type FooResponse {
bar: String
}
';

$this->graphQL(/** @lang GraphQL */ '
{
foo(bar: "") {
bar
}
}
')->assertExactJson([
'data' => [
'foo' => [
'bar' => null,
],
],
]);
}

public function testConvertsEmptyStringToNullWithGlobalFieldMiddleware(): void
{
config(['lighthouse.field_middleware' => [
ConvertEmptyStringsToNullDirective::class,
]]);

$this->schema = /** @lang GraphQL */ '
type Query {
foo(bar: String): FooResponse
@field(resolver: "Tests\\\Utils\\\Mutations\\\ReturnReceivedInput")
}
type FooResponse {
bar: String
}
';

$this->graphQL(/** @lang GraphQL */ '
{
foo(bar: "") {
bar
}
}
')->assertExactJson([
'data' => [
'foo' => [
'bar' => null,
],
],
]);
}

public function testConvertsEmptyStringToNullWithFieldDirectiveAndInputType(): void
{
$this->schema = /** @lang GraphQL */ '
type Query {
foo(input: FooInput): FooInputResponse
@convertEmptyStringsToNull
@field(resolver: "Tests\\\Utils\\\Mutations\\\ReturnReceivedInput")
}
input FooInput {
bar: String
}
type FooInputResponse {
input: FooResponse
}
type FooResponse {
bar: String
}
';

$this->graphQL(/** @lang GraphQL */ '
{
foo(input: {
bar: ""
}) {
input {
bar
}
}
}
')->assertExactJson([
'data' => [
'foo' => [
'input' => [
'bar' => null,
],
],
],
]);
}

public function testConvertsEmptyStringToNullWithGlobalFieldMiddlewareAndInputType(): void
{
config(['lighthouse.field_middleware' => [
ConvertEmptyStringsToNullDirective::class,
]]);

$this->schema = /** @lang GraphQL */ '
type Query {
foo(input: FooInput): FooInputResponse
@field(resolver: "Tests\\\Utils\\\Mutations\\\ReturnReceivedInput")
}
input FooInput {
bar: String
}
type FooInputResponse {
input: FooResponse
}
type FooResponse {
bar: String
}
';

$this->graphQL(/** @lang GraphQL */ '
{
foo(input: {
bar: ""
}) {
input {
bar
}
}
}
')->assertExactJson([
'data' => [
'foo' => [
'input' => [
'bar' => null,
],
],
],
]);
}
}
16 changes: 16 additions & 0 deletions tests/Utils/Mutations/ReturnReceivedInput.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
<?php declare(strict_types=1);

namespace Tests\Utils\Mutations;

final class ReturnReceivedInput
{
/**
* @param array<string, mixed> $args
*
* @return array<string, mixed>
*/
public function __invoke(mixed $root, array $args): array
{
return $args;
}
}

0 comments on commit c8eb73d

Please sign in to comment.