Skip to content

Commit

Permalink
Merge 4.0
Browse files Browse the repository at this point in the history
  • Loading branch information
soyuka committed Oct 22, 2024
2 parents fcd9a07 + 5db40ae commit 1ac0c3d
Show file tree
Hide file tree
Showing 76 changed files with 2,180 additions and 135 deletions.
1 change: 1 addition & 0 deletions .php-cs-fixer.dist.php
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
->in(__DIR__)
->exclude([
'src/Core/Bridge/Symfony/Maker/Resources/skeleton',
'src/Laravel/Console/Maker/Resources/skeleton',
'src/Laravel/config',
'tests/Fixtures/app/var',
'docs/guides',
Expand Down
44 changes: 41 additions & 3 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,27 @@
# Changelog

## v4.0.5

### Bug fixes

* [4171d5f9c](https://github.com/api-platform/core/commit/4171d5f9cd41731b857c53a186270ba0626baedf) fix(graphql): register query parameter arguments with filters (#6726)
* [48ab53816](https://github.com/api-platform/core/commit/48ab53816c55e6116aa64ac81f522f4b7b9bb9f6) fix(laravel): make command writes to app instead of src (#6723)

### Features

## v4.0.4

### Bug fixes

* [2e8287dad](https://github.com/api-platform/core/commit/2e8287dad0c0315dd6527279a6359c0a22f40d93) fix(laravel): allow serializer attributes through ApiProperty (#6680)
* [439c188ea](https://github.com/api-platform/core/commit/439c188ea1685676d5e705a49a4b835f35a84d72) fix(laravel): match integer type (#6715)
* [4ad7a50aa](https://github.com/api-platform/core/commit/4ad7a50aaabf0d85e2eb5bb3a6d4ef8d5b7b39a7) fix(laravel): openapi Options binding (#6714)
* [ec6e64512](https://github.com/api-platform/core/commit/ec6e6451299a50fcab397e86fafe6db132ce7519) fix(laravel): skip resource path when not available (#6697)

### Features

* [5aa799321](https://github.com/api-platform/core/commit/5aa7993219a6fb55f11476a031963a542b2d3586) feat(laravel): command to generate state providers/processors (#6708)

## v4.0.3

### Bug fixes
Expand Down Expand Up @@ -130,6 +152,25 @@ Notes:

* [0d5f35683](https://github.com/api-platform/core/commit/0d5f356839eb6aa9f536044abe4affa736553e76) feat(laravel): laravel component (#5882)

## v3.4.4

### Bug fixes

* [550347867](https://github.com/api-platform/core/commit/550347867f30611b673d8df99f65186d013919dd) fix(graphql): register query parameter arguments with filters (#6727)
* [99262dce7](https://github.com/api-platform/core/commit/99262dce739800bd841c95e026848b587ba25801) fix(jsonschema): handle @id when genId is false (#6716)
* [ad5efa535](https://github.com/api-platform/core/commit/ad5efa535a4dcbaad64ecff89514eaa6e07f5b7c) fix: multiple parameter provider #6673 (#6732)
* [d34cd7be8](https://github.com/api-platform/core/commit/d34cd7be8e7a12fd08a8b10270a614c06c10aa89) fix: use stateOptions when retrieving a Parameter filter (#6728)
* [e7fb04fab](https://github.com/api-platform/core/commit/e7fb04fab05bc077e2dbeb0fa0fc2c1d28c96105) fix(symfony): fetch api-platform/symfony version debug bar (#6722)
* [e96623ebf](https://github.com/api-platform/core/commit/e96623ebfd8691ba943bdb56a4d91e160497a311) fix(jsonld): prefix error @type with hydra: (#6721)

## v3.4.3

### Bug fixes

* [3ca599158](https://github.com/api-platform/core/commit/3ca599158139d56fbd6ee66f2de3e586120d735c) fix(hydra): hydra_prefix on errors (#6704)
* [f7f605dc8](https://github.com/api-platform/core/commit/f7f605dc8b798b975d2286c970c9091436d7f890) fix: check that api-platform/ramsey-uuid is installed before registering related services (#6696)
* [fbb53e5e3](https://github.com/api-platform/core/commit/fbb53e5e35ca0ec3de26ddc7de7ea4d1dda5c20b) fix(symfony): metadata aware name converter has 0 arguments by default (#6711)

## v3.4.2

### Bug fixes
Expand All @@ -142,9 +183,6 @@ Notes:
* [afe7d47d7](https://github.com/api-platform/core/commit/afe7d47d7b7ba6c8591bfb60137a65d1fa1fe38f) fix(metadata): passing class as parameter in XML ApiResource's definition (#6659)
* [b93ee467c](https://github.com/api-platform/core/commit/b93ee467c69253e0cfe60e75b48a5c7aa683474a) fix(metadata): overwriting XML ApiResource definition by YAML ApiResource definition (#6660)

> [!WARNING]
> Hydra prefix on errors is breaking, read `title` not `hydra:title`. The `hydra_prefix` flag doesn't apply to errors as it provided redundant information (both `hydra:title` and `title` were available)
## v3.4.1

### Bug fixes
Expand Down
11 changes: 9 additions & 2 deletions docs/guides/error-resource.php
Original file line number Diff line number Diff line change
Expand Up @@ -18,12 +18,12 @@ class MyDomainException extends \Exception implements ProblemExceptionInterface
{
public function getType(): string
{
return 'teapot';
return '/errors/418';
}

public function getTitle(): ?string
{
return null;
return 'Teapot error';
}

public function getStatus(): ?int
Expand All @@ -40,6 +40,8 @@ public function getInstance(): ?string
{
return null;
}

public string $myCustomField = 'I usually prefer coffee.';
}

use ApiPlatform\Metadata\ApiResource;
Expand Down Expand Up @@ -83,7 +85,12 @@ public function testBookDoesNotExists(): void
// you can override this by looking at the [Error Provider guide](/docs/guides/error-provider).
$this->assertResponseStatusCodeSame(418);
$this->assertJsonContains([
'@id' => '/my_domain_exceptions',
'@type' => 'MyDomainException',
'type' => '/errors/418',
'title' => 'Teapot error',
'detail' => 'I am teapot',
'myCustomField' => 'I usually prefer coffee.'
]);
}
}
Expand Down
4 changes: 4 additions & 0 deletions features/hydra/error.feature
Original file line number Diff line number Diff line change
Expand Up @@ -17,8 +17,10 @@ Feature: Error handling
And the header "Link" should contain '<http://www.w3.org/ns/hydra/error>; rel="http://www.w3.org/ns/json-ld#error"'
And the JSON node "type" should exist
And the JSON node "title" should be equal to "An error occurred"
And the JSON node "hydra:title" should be equal to "An error occurred"
And the JSON node "detail" should exist
And the JSON node "description" should exist
And the JSON node "hydra:description" should exist
And the JSON node "trace" should exist
And the JSON node "status" should exist
And the JSON node "@context" should exist
Expand Down Expand Up @@ -48,6 +50,8 @@ Feature: Error handling
],
"detail": "name: This value should not be blank.",
"title": "An error occurred",
"hydra:title": "An error occurred",
"hydra:description": "name: This value should not be blank.",
"description": "name: This value should not be blank.",
"type": "/validation_errors/c1051bb4-d103-4f74-8988-acbcafc7fdc3"
}
Expand Down
2 changes: 1 addition & 1 deletion features/main/relation.feature
Original file line number Diff line number Diff line change
Expand Up @@ -493,7 +493,7 @@ Feature: Relations support
"properties": {
"@type": {
"type": "string",
"pattern": "^Error$"
"pattern": "^hydra:Error$"
},
"title": {
"type": "string",
Expand Down
4 changes: 2 additions & 2 deletions features/mongodb/filters.feature
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ Feature: Filters on collections
And the response should be in JSON
And the header "Content-Type" should be equal to "application/problem+json; charset=utf-8"
And the JSON node "@context" should be equal to "/contexts/Error"
And the JSON node "@type" should be equal to "Error"
And the JSON node "@type" should be equal to "hydra:Error"
And the JSON node "title" should be equal to "An error occurred"
And the JSON node "description" should be equal to "Cannot use reference 'badFourthLevel' in class 'ThirdLevel' for lookup or graphLookup: dbRef references are not supported."
And the JSON node "trace" should exist
Expand All @@ -23,7 +23,7 @@ Feature: Filters on collections
And the response should be in JSON
And the header "Content-Type" should be equal to "application/problem+json; charset=utf-8"
And the JSON node "@context" should be equal to "/contexts/Error"
And the JSON node "@type" should be equal to "Error"
And the JSON node "@type" should be equal to "hydra:Error"
And the JSON node "title" should be equal to "An error occurred"
And the JSON node "description" should be equal to "Cannot use reference 'badThirdLevel' in class 'FourthLevel' for lookup or graphLookup: dbRef references are not supported."
And the JSON node "trace" should exist
10 changes: 5 additions & 5 deletions features/security/strong_typing.feature
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,7 @@ Feature: Handle properly invalid data submitted to the API
And the response should be in JSON
And the header "Content-Type" should be equal to "application/problem+json; charset=utf-8"
And the JSON node "@context" should be equal to "/contexts/Error"
And the JSON node "@type" should be equal to "Error"
And the JSON node "@type" should be equal to "hydra:Error"
And the JSON node "title" should be equal to "An error occurred"
And the JSON node "description" should be equal to 'The type of the "name" attribute must be "string", "NULL" given.'

Expand All @@ -71,7 +71,7 @@ Feature: Handle properly invalid data submitted to the API
And the response should be in JSON
And the header "Content-Type" should be equal to "application/problem+json; charset=utf-8"
And the JSON node "@context" should be equal to "/contexts/Error"
And the JSON node "@type" should be equal to "Error"
And the JSON node "@type" should be equal to "hydra:Error"
And the JSON node "title" should be equal to "An error occurred"
And the JSON node "description" should be equal to 'Invalid IRI "1".'
And the JSON node "trace" should exist
Expand Down Expand Up @@ -102,7 +102,7 @@ Feature: Handle properly invalid data submitted to the API
And the response should be in JSON
And the header "Content-Type" should be equal to "application/problem+json; charset=utf-8"
And the JSON node "@context" should be equal to "/contexts/Error"
And the JSON node "@type" should be equal to "Error"
And the JSON node "@type" should be equal to "hydra:Error"
And the JSON node "title" should be equal to "An error occurred"
And the JSON node "description" should be equal to 'The type of the "relatedDummies" attribute must be "array", "string" given.'
And the JSON node "trace" should exist
Expand All @@ -120,7 +120,7 @@ Feature: Handle properly invalid data submitted to the API
And the response should be in JSON
And the header "Content-Type" should be equal to "application/problem+json; charset=utf-8"
And the JSON node "@context" should be equal to "/contexts/Error"
And the JSON node "@type" should be equal to "Error"
And the JSON node "@type" should be equal to "hydra:Error"
And the JSON node "title" should be equal to "An error occurred"
And the JSON node "description" should be equal to 'The type of the key "a" must be "int", "string" given.'

Expand All @@ -136,7 +136,7 @@ Feature: Handle properly invalid data submitted to the API
And the response should be in JSON
And the header "Content-Type" should be equal to "application/problem+json; charset=utf-8"
And the JSON node "@context" should be equal to "/contexts/Error"
And the JSON node "@type" should be equal to "Error"
And the JSON node "@type" should be equal to "hydra:Error"
And the JSON node "title" should be equal to "An error occurred"
And the JSON node "description" should be equal to 'The type of the "name" attribute must be "string", "integer" given.'

Expand Down
2 changes: 1 addition & 1 deletion features/serializer/vo_relations.feature
Original file line number Diff line number Diff line change
Expand Up @@ -148,7 +148,7 @@ Feature: Value object as ApiResource
"properties": {
"@type": {
"type": "string",
"pattern": "^Error$"
"pattern": "^hydra:Error$"
},
"title": {
"type": "string",
Expand Down
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
8 changes: 6 additions & 2 deletions src/GraphQl/Type/TypeConverter.php
Original file line number Diff line number Diff line change
Expand Up @@ -168,10 +168,14 @@ private function getResourceType(Type $type, bool $input, Operation $rootOperati
try {
$operation = $resourceMetadataCollection->getOperation($operationName);
} catch (OperationNotFoundException) {
$operation = $resourceMetadataCollection->getOperation($isCollection ? 'collection_query' : 'item_query');
try {
$operation = $resourceMetadataCollection->getOperation($isCollection ? 'collection_query' : 'item_query');
} catch (OperationNotFoundException) {
throw new OperationNotFoundException(\sprintf('A GraphQl operation named "%s" should exist on the type "%s" as we reference this type in another query.', $isCollection ? 'collection_query' : 'item_query', $resourceClass));
}
}
if (!$operation instanceof Operation) {
throw new OperationNotFoundException();
throw new OperationNotFoundException(\sprintf('A GraphQl operation named "%s" should exist on the type "%s" as we reference this type in another query.', $operationName, $resourceClass));
}

return $this->typeBuilder->getResourceObjectType($resourceMetadataCollection, $operation, $propertyMetadata, [
Expand Down
3 changes: 1 addition & 2 deletions src/JsonLd/ContextBuilder.php
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,6 @@
namespace ApiPlatform\JsonLd;

use ApiPlatform\JsonLd\Serializer\HydraPrefixTrait;
use ApiPlatform\Metadata\Error;
use ApiPlatform\Metadata\Get;
use ApiPlatform\Metadata\HttpOperation;
use ApiPlatform\Metadata\IriConverterInterface;
Expand Down Expand Up @@ -185,7 +184,7 @@ private function getResourceContextWithShortname(string $resourceClass, int $ref
}
}

if (false === ($this->defaultContext[self::HYDRA_CONTEXT_HAS_PREFIX] ?? true) || $operation instanceof Error) {
if (false === ($this->defaultContext[self::HYDRA_CONTEXT_HAS_PREFIX] ?? true)) {
return ['http://www.w3.org/ns/hydra/context.jsonld', $context];
}

Expand Down
Loading

0 comments on commit 1ac0c3d

Please sign in to comment.