Skip to content

Commit

Permalink
fix(graphql): register query parameter arguments with filters (#6726)
Browse files Browse the repository at this point in the history
  • Loading branch information
soyuka authored Oct 15, 2024
1 parent 48ab538 commit 4171d5f
Show file tree
Hide file tree
Showing 3 changed files with 99 additions and 60 deletions.
129 changes: 69 additions & 60 deletions src/GraphQl/Type/FieldsBuilder.php
Original file line number Diff line number Diff line change
Expand Up @@ -287,61 +287,6 @@ public function resolveResourceArgs(array $args, Operation $operation): array
$args[$id]['type'] = $this->typeConverter->resolveType($arg['type']);
}

/*
* This is @experimental, read the comment on the parameterToObjectType function as additional information.
*/
foreach ($operation->getParameters() ?? [] as $parameter) {
$key = $parameter->getKey();

if (str_contains($key, ':property')) {
if (!($filterId = $parameter->getFilter()) || !$this->filterLocator->has($filterId)) {
continue;
}

$filter = $this->filterLocator->get($filterId);
$parsedKey = explode('[:property]', $key);
$flattenFields = [];

if ($filter instanceof FilterInterface) {
foreach ($filter->getDescription($operation->getClass()) as $name => $value) {
$values = [];
parse_str($name, $values);
if (isset($values[$parsedKey[0]])) {
$values = $values[$parsedKey[0]];
}

$name = key($values);
$flattenFields[] = ['name' => $name, 'required' => $value['required'] ?? null, 'description' => $value['description'] ?? null, 'leafs' => $values[$name], 'type' => $value['type'] ?? 'string'];
}

$args[$parsedKey[0]] = $this->parameterToObjectType($flattenFields, $parsedKey[0]);
}

if ($filter instanceof OpenApiParameterFilterInterface) {
foreach ($filter->getOpenApiParameters($parameter) as $value) {
$values = [];
parse_str($value->getName(), $values);
if (isset($values[$parsedKey[0]])) {
$values = $values[$parsedKey[0]];
}

$name = key($values);
$flattenFields[] = ['name' => $name, 'required' => $value->getRequired(), 'description' => $value->getDescription(), 'leafs' => $values[$name], 'type' => $value->getSchema()['type'] ?? 'string'];
}

$args[$parsedKey[0]] = $this->parameterToObjectType($flattenFields, $parsedKey[0].$operation->getShortName().$operation->getName());
}

continue;
}

$args[$key] = ['type' => GraphQLType::string()];

if ($parameter->getRequired()) {
$args[$key]['type'] = GraphQLType::nonNull($args[$key]['type']);
}
}

return $args;
}

Expand Down Expand Up @@ -463,12 +408,15 @@ private function getResourceFieldConfiguration(?string $property, ?string $field

$args = [];

if (!$input && !$rootOperation instanceof Mutation && !$rootOperation instanceof Subscription && !$isStandardGraphqlType && $isCollectionType) {
if (!$this->isEnumClass($resourceClass) && $this->pagination->isGraphQlEnabled($resourceOperation)) {
$args = $this->getGraphQlPaginationArgs($resourceOperation);
}
if (!$input && !$rootOperation instanceof Mutation && !$rootOperation instanceof Subscription && !$isStandardGraphqlType) {
if ($isCollectionType) {
if (!$this->isEnumClass($resourceClass) && $this->pagination->isGraphQlEnabled($resourceOperation)) {
$args = $this->getGraphQlPaginationArgs($resourceOperation);
}

$args = $this->getFilterArgs($args, $resourceClass, $rootResource, $resourceOperation, $rootOperation, $property, $depth);
$args = $this->getFilterArgs($args, $resourceClass, $rootResource, $resourceOperation, $rootOperation, $property, $depth);
$args = $this->getParameterArgs($rootOperation, $args);
}
}

if ($isStandardGraphqlType || $input) {
Expand All @@ -491,6 +439,67 @@ private function getResourceFieldConfiguration(?string $property, ?string $field
return null;
}

/*
* This function is @experimental, read the comment on the parameterToObjectType function for additional information.
* @experimental
*/
private function getParameterArgs(Operation $operation, array $args = []): array
{
foreach ($operation->getParameters() ?? [] as $parameter) {
$key = $parameter->getKey();

if (!str_contains($key, ':property')) {
$args[$key] = ['type' => GraphQLType::string()];

if ($parameter->getRequired()) {
$args[$key]['type'] = GraphQLType::nonNull($args[$key]['type']);
}

continue;
}

if (!($filterId = $parameter->getFilter()) || !$this->filterLocator->has($filterId)) {
continue;
}

$filter = $this->filterLocator->get($filterId);
$parsedKey = explode('[:property]', $key);
$flattenFields = [];

if ($filter instanceof FilterInterface) {
foreach ($filter->getDescription($operation->getClass()) as $name => $value) {
$values = [];
parse_str($name, $values);
if (isset($values[$parsedKey[0]])) {
$values = $values[$parsedKey[0]];
}

$name = key($values);
$flattenFields[] = ['name' => $name, 'required' => $value['required'] ?? null, 'description' => $value['description'] ?? null, 'leafs' => $values[$name], 'type' => $value['type'] ?? 'string'];
}

$args[$parsedKey[0]] = $this->parameterToObjectType($flattenFields, $parsedKey[0]);
}

if ($filter instanceof OpenApiParameterFilterInterface) {
foreach ($filter->getOpenApiParameters($parameter) as $value) {
$values = [];
parse_str($value->getName(), $values);
if (isset($values[$parsedKey[0]])) {
$values = $values[$parsedKey[0]];
}

$name = key($values);
$flattenFields[] = ['name' => $name, 'required' => $value->getRequired(), 'description' => $value->getDescription(), 'leafs' => $values[$name], 'type' => $value->getSchema()['type'] ?? 'string'];
}

$args[$parsedKey[0]] = $this->parameterToObjectType($flattenFields, $parsedKey[0].$operation->getShortName().$operation->getName());
}
}

return $args;
}

private function getGraphQlPaginationArgs(Operation $queryOperation): array
{
$paginationType = $this->pagination->getGraphQlPaginationType($queryOperation);
Expand Down
19 changes: 19 additions & 0 deletions src/Laravel/Tests/GraphQlTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -47,4 +47,23 @@ public function testGetBooks(): void
$this->assertArrayHasKey('data', $data);
$this->assertArrayNotHasKey('errors', $data);
}

public function testGetBooksWithPaginationAndOrder(): void
{
BookFactory::new()->has(AuthorFactory::new())->count(10)->create();
$response = $this->postJson('/api/graphql', ['query' => '{
books(first: 3, order: {name: "desc"}) {
edges {
node {
id, name, publicationDate, author { id, name }
}
}
}
}'], ['accept' => ['application/json']]);
$response->assertStatus(200);
$data = $response->json();
$this->assertArrayHasKey('data', $data);
$this->assertCount(3, $data['data']['books']['edges']);
$this->assertArrayNotHasKey('errors', $data);
}
}
11 changes: 11 additions & 0 deletions src/Laravel/workbench/app/Models/Book.php
Original file line number Diff line number Diff line change
Expand Up @@ -15,13 +15,16 @@

use ApiPlatform\Laravel\Eloquent\Filter\DateFilter;
use ApiPlatform\Laravel\Eloquent\Filter\EqualsFilter;
use ApiPlatform\Laravel\Eloquent\Filter\OrderFilter;
use ApiPlatform\Laravel\Eloquent\Filter\OrFilter;
use ApiPlatform\Laravel\Eloquent\Filter\PartialSearchFilter;
use ApiPlatform\Laravel\Eloquent\Filter\RangeFilter;
use ApiPlatform\Metadata\ApiResource;
use ApiPlatform\Metadata\Delete;
use ApiPlatform\Metadata\Get;
use ApiPlatform\Metadata\GetCollection;
use ApiPlatform\Metadata\GraphQl\Query;
use ApiPlatform\Metadata\GraphQl\QueryCollection;
use ApiPlatform\Metadata\Patch;
use ApiPlatform\Metadata\Post;
use ApiPlatform\Metadata\Put;
Expand All @@ -44,6 +47,14 @@
new Post(),
new Delete(),
new GetCollection(),
],
graphQlOperations: [
new Query(),
new QueryCollection(
parameters: [
new QueryParameter(key: 'order[:property]', filter: OrderFilter::class),
],
),
]
)]
#[QueryParameter(key: 'isbn', filter: PartialSearchFilter::class, constraints: 'min:2')]
Expand Down

0 comments on commit 4171d5f

Please sign in to comment.