diff --git a/CHANGELOG.md b/CHANGELOG.md index 2139802871c..b80117950f3 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,31 @@ # Changelog +## v4.1.0-alpha.1 + +### Bug fixes + +* [67fbe51c5](https://github.com/api-platform/core/commit/67fbe51c570abe1ece6651ae6a037662e9012881) fix: reintroduce the `show_webby` parameter in Laravel config (#6741) + +### Features + +* [00787f32d](https://github.com/api-platform/core/commit/00787f32da54418de7d869cff218e22d8ae2ae1d) feat(laravel): automatically register policies (#6623) +* [12c42096b](https://github.com/api-platform/core/commit/12c42096bb0006d6ebae60ae5d90e9b356f9a335) feat(metadata): ability to hide an hydra class/operation (#6871) +* [57f15cf4f](https://github.com/api-platform/core/commit/57f15cf4f38278315c5f31d3949416c9455ba0d0) feat(state): strict query parameters (#6399) +* [bd0e92936](https://github.com/api-platform/core/commit/bd0e92936f82d3cd4563cd45ebf1f73fd1db9f01) feat(openapi): HTTP Authentication Support for Swagger UI (#6665) +* [be98f4e01](https://github.com/api-platform/core/commit/be98f4e01a52d8341ef9b65ed2f4e3b46ab31165) feat(graphql): allow to configure max query depth and max query complexity (#6880) +* [c78ed0b78](https://github.com/api-platform/core/commit/c78ed0b78baf5d2e1b7444a9882ba039c70a3887) feat(laravel): boolean filter (#6806) +* [d0a442786](https://github.com/api-platform/core/commit/d0a44278630d201b91cbba0774a09f4eeaac88f7) feat(doctrine): enhance getLinksHandler with method validation and typo suggestions (#6874) +* [f67f6f1ac](https://github.com/api-platform/core/commit/f67f6f1acb6476182c18a3503f2a8bc80ae89a0b) feat(doctrine): doctrine filters like laravel eloquent filters (#6775) + +## v4.0.14 + +### Bug fixes + +* [97cdb6b3f](https://github.com/api-platform/core/commit/97cdb6b3f43471789e096c9dc3a0c3c7b6d4e43c) fix(state): remove ProcessorInterface laravel specific type +* [b12a0d005](https://github.com/api-platform/core/commit/b12a0d005fda58a162b82a3574e6ee877838a55b) fix(graphql): register types for parameter args (#6895) + +### Features + ## v4.0.13 ### Bug fixes @@ -248,6 +274,18 @@ Notes: * [0d5f35683](https://github.com/api-platform/core/commit/0d5f356839eb6aa9f536044abe4affa736553e76) feat(laravel): laravel component (#5882) +## v3.4.14 + +### Bug fixes + +* [0cf752bce](https://github.com/api-platform/core/commit/0cf752bcec692718b2503250e655d05aea670316) fix(metadata): make the schema attribute to fallback to null for parameters in YamlResourceExtractor (#6896) +* [2b3c55db2](https://github.com/api-platform/core/commit/2b3c55db2a9ecc52f62c441fa8a5696233a30b87) fix(symfony): remove unsolvable deprecation (#6899) see also (#6655) +* [9493b9b6e](https://github.com/api-platform/core/commit/9493b9b6ec0264ab5b700c861ad1b97455b4f88d) fix(symfony): revert json schema bc break (#6903) +* [b82f9ac76](https://github.com/api-platform/core/commit/b82f9ac76ce89dd3910849c73da42317ee1339ed) fix(openapi): not forbidden response on openAPI doc (#6886) + +I mispublished a v3.4.13 on some repositories to fix them all I bumped 3.4.10 to 3.4.14 +More details at #6888. + ## v3.4.10 ### Bug fixes diff --git a/src/GraphQl/Type/FieldsBuilder.php b/src/GraphQl/Type/FieldsBuilder.php index 09529299815..6c419595182 100644 --- a/src/GraphQl/Type/FieldsBuilder.php +++ b/src/GraphQl/Type/FieldsBuilder.php @@ -415,7 +415,15 @@ private function getResourceFieldConfiguration(?string $property, ?string $field } $args = $this->getFilterArgs($args, $resourceClass, $rootResource, $resourceOperation, $rootOperation, $property, $depth); - $args = $this->getParameterArgs($rootOperation, $args); + + // Also register parameter args in the types container + // Note: This is a workaround, for more information read the comment on the parameterToObjectType function. + foreach ($this->getParameterArgs($rootOperation) as $key => $arg) { + if ($arg instanceof InputObjectType || (\is_array($arg) && isset($arg['name']))) { + $this->typesContainer->set(\is_array($arg) ? $arg['name'] : $arg->name(), $arg); + } + $args[$key] = $arg; + } } } diff --git a/src/Laravel/Eloquent/Filter/DateFilter.php b/src/Laravel/Eloquent/Filter/DateFilter.php index 67665dce87f..29903f50d44 100644 --- a/src/Laravel/Eloquent/Filter/DateFilter.php +++ b/src/Laravel/Eloquent/Filter/DateFilter.php @@ -84,7 +84,10 @@ public function getSchema(Parameter $parameter): array return ['type' => 'date']; } - public function getOpenApiParameters(Parameter $parameter): OpenApiParameter|array|null + /** + * @return OpenApiParameter[] + */ + public function getOpenApiParameters(Parameter $parameter): array { $in = $parameter instanceof QueryParameter ? 'query' : 'header'; $key = $parameter->getKey(); diff --git a/src/Laravel/Eloquent/Filter/OrFilter.php b/src/Laravel/Eloquent/Filter/OrFilter.php index 6c9e7f833b2..609608cb929 100644 --- a/src/Laravel/Eloquent/Filter/OrFilter.php +++ b/src/Laravel/Eloquent/Filter/OrFilter.php @@ -49,7 +49,7 @@ public function getSchema(Parameter $parameter): array return ['type' => 'array', 'items' => $schema]; } - public function getOpenApiParameters(Parameter $parameter): OpenApiParameter|array|null + public function getOpenApiParameters(Parameter $parameter): OpenApiParameter { return new OpenApiParameter(name: $parameter->getKey().'[]', in: 'query', style: 'deepObject', explode: true); } diff --git a/src/Laravel/Eloquent/Filter/OrderFilter.php b/src/Laravel/Eloquent/Filter/OrderFilter.php index 90315f3d083..233356d48bb 100644 --- a/src/Laravel/Eloquent/Filter/OrderFilter.php +++ b/src/Laravel/Eloquent/Filter/OrderFilter.php @@ -54,7 +54,10 @@ public function getSchema(Parameter $parameter): array return ['type' => 'string', 'enum' => ['asc', 'desc']]; } - public function getOpenApiParameters(Parameter $parameter): OpenApiParameter|array|null + /** + * @return OpenApiParameter[]|null + */ + public function getOpenApiParameters(Parameter $parameter): ?array { if (str_contains($parameter->getKey(), ':property')) { $parameters = []; diff --git a/src/Laravel/Eloquent/Filter/RangeFilter.php b/src/Laravel/Eloquent/Filter/RangeFilter.php index 82f7e4457ec..bb86f7dc78f 100644 --- a/src/Laravel/Eloquent/Filter/RangeFilter.php +++ b/src/Laravel/Eloquent/Filter/RangeFilter.php @@ -52,7 +52,10 @@ public function getSchema(Parameter $parameter): array return ['type' => 'number']; } - public function getOpenApiParameters(Parameter $parameter): OpenApiParameter|array|null + /** + * @return OpenApiParameter[] + */ + public function getOpenApiParameters(Parameter $parameter): array { $in = $parameter instanceof QueryParameter ? 'query' : 'header'; $key = $parameter->getKey(); diff --git a/src/Laravel/Eloquent/Metadata/Factory/Property/EloquentPropertyMetadataFactory.php b/src/Laravel/Eloquent/Metadata/Factory/Property/EloquentPropertyMetadataFactory.php index 6385a83568c..d4210674e69 100644 --- a/src/Laravel/Eloquent/Metadata/Factory/Property/EloquentPropertyMetadataFactory.php +++ b/src/Laravel/Eloquent/Metadata/Factory/Property/EloquentPropertyMetadataFactory.php @@ -88,7 +88,7 @@ public function create(string $resourceClass, string $property, array $options = return $propertyMetadata ->withBuiltinTypes([$type]) - ->withWritable($propertyMetadata->isWritable() ?? true) + ->withWritable($propertyMetadata->isWritable() ?? true === $p['fillable']) ->withReadable($propertyMetadata->isReadable() ?? false === $p['hidden']); } diff --git a/src/Laravel/Eloquent/Paginator.php b/src/Laravel/Eloquent/Paginator.php index 77784ce6c70..feabdf2eaac 100644 --- a/src/Laravel/Eloquent/Paginator.php +++ b/src/Laravel/Eloquent/Paginator.php @@ -34,7 +34,7 @@ public function __construct( public function count(): int { - return $this->paginator->count(); + return $this->paginator->count(); // @phpstan-ignore-line } public function getLastPage(): float diff --git a/src/Laravel/Eloquent/PartialPaginator.php b/src/Laravel/Eloquent/PartialPaginator.php index e8dd57aae8f..e193d9cfc64 100644 --- a/src/Laravel/Eloquent/PartialPaginator.php +++ b/src/Laravel/Eloquent/PartialPaginator.php @@ -33,7 +33,7 @@ public function __construct( public function count(): int { - return $this->paginator->count(); + return $this->paginator->count(); // @phpstan-ignore-line } public function getCurrentPage(): float diff --git a/src/Laravel/Routing/IriConverter.php b/src/Laravel/Routing/IriConverter.php index cc9ab93a49a..ad1e6f1b78b 100644 --- a/src/Laravel/Routing/IriConverter.php +++ b/src/Laravel/Routing/IriConverter.php @@ -159,7 +159,7 @@ private function generateRoute(object|string $resource, int $referenceType = Url if (\is_object($resource)) { try { $identifiers = $this->identifiersExtractor->getIdentifiersFromItem($resource, $identifiersExtractorOperation, $context); - } catch (InvalidArgumentException|RuntimeException $e) { + } catch (RuntimeException $e) { // We can try using context uri variables if any if (!$identifiers) { throw new InvalidArgumentException(\sprintf('Unable to generate an IRI for the item of type "%s"', $operation->getClass()), $e->getCode(), $e); diff --git a/src/Laravel/Routing/SkolemIriConverter.php b/src/Laravel/Routing/SkolemIriConverter.php index 6aa7f16f6c2..fcb67798536 100644 --- a/src/Laravel/Routing/SkolemIriConverter.php +++ b/src/Laravel/Routing/SkolemIriConverter.php @@ -53,7 +53,7 @@ public function getResourceFromIri(string $iri, array $context = [], ?Operation /** * {@inheritdoc} */ - public function getIriFromResource(object|string $resource, int $referenceType = UrlGeneratorInterface::ABS_PATH, ?Operation $operation = null, array $context = []): ?string + public function getIriFromResource(object|string $resource, int $referenceType = UrlGeneratorInterface::ABS_PATH, ?Operation $operation = null, array $context = []): string { $referenceType = $operation ? ($operation->getUrlGenerationStrategy() ?? $referenceType) : $referenceType; if (($isObject = \is_object($resource)) && $this->objectHashMap->contains($resource)) { diff --git a/src/Laravel/Tests/Console/Maker/MakeStateProcessorCommandTest.php b/src/Laravel/Tests/Console/Maker/MakeStateProcessorCommandTest.php index c51b5de1fc9..fcb05239fce 100644 --- a/src/Laravel/Tests/Console/Maker/MakeStateProcessorCommandTest.php +++ b/src/Laravel/Tests/Console/Maker/MakeStateProcessorCommandTest.php @@ -30,7 +30,7 @@ class MakeStateProcessorCommandTest extends TestCase /** @var string */ private const CHOSEN_CLASS_NAME = 'Choose a class name for your state processor (e.g. AwesomeStateProcessor)'; - private ?Filesystem $filesystem; + private Filesystem $filesystem; private PathResolver $pathResolver; private AppServiceFileGenerator $appServiceFileGenerator; diff --git a/src/Laravel/Tests/Console/Maker/MakeStateProviderCommandTest.php b/src/Laravel/Tests/Console/Maker/MakeStateProviderCommandTest.php index c259c79fa39..f7d8c8e8e5b 100644 --- a/src/Laravel/Tests/Console/Maker/MakeStateProviderCommandTest.php +++ b/src/Laravel/Tests/Console/Maker/MakeStateProviderCommandTest.php @@ -30,7 +30,7 @@ class MakeStateProviderCommandTest extends TestCase /** @var string */ private const STATE_PROVIDER_CLASS_NAME = 'Choose a class name for your state provider (e.g. AwesomeStateProvider)'; - private ?Filesystem $filesystem; + private Filesystem $filesystem; private PathResolver $pathResolver; private AppServiceFileGenerator $appServiceFileGenerator; diff --git a/src/Laravel/Tests/GraphQlTest.php b/src/Laravel/Tests/GraphQlTest.php index 571573cc6c5..e296bfb1ffa 100644 --- a/src/Laravel/Tests/GraphQlTest.php +++ b/src/Laravel/Tests/GraphQlTest.php @@ -79,20 +79,37 @@ public function testGetBooksWithSimplePagination(): void 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']]); + // Create books in reverse alphabetical order to test the 'asc' order + BookFactory::new() + ->count(10) + ->sequence(fn ($sequence) => ['name' => \chr(122 - $sequence->index)]) // ASCII codes starting from 'z' + ->has(AuthorFactory::new()) + ->create(); + + $response = $this->postJson('/api/graphql', [ + 'query' => ' + query getBooks($first: Int!, $order: orderBookcollection_query!) { + books(first: $first, order: $order) { + edges { + node { + id, name, publicationDate, author { id, name } + } + } + } + } + ', + 'variables' => [ + 'first' => 3, + 'order' => ['name' => 'asc'], + ], + ], ['accept' => ['application/json']]); $response->assertStatus(200); $data = $response->json(); $this->assertArrayHasKey('data', $data); $this->assertCount(3, $data['data']['books']['edges']); + $this->assertEquals('q', $data['data']['books']['edges'][0]['node']['name']); + $this->assertEquals('r', $data['data']['books']['edges'][1]['node']['name']); + $this->assertEquals('s', $data['data']['books']['edges'][2]['node']['name']); $this->assertArrayNotHasKey('errors', $data); } diff --git a/src/Laravel/composer.json b/src/Laravel/composer.json index 65c1ef6be14..ae78d721792 100644 --- a/src/Laravel/composer.json +++ b/src/Laravel/composer.json @@ -49,12 +49,12 @@ "illuminate/container": "^11.0", "symfony/web-link": "^6.4 || ^7.1", "willdurand/negotiation": "^3.1", - "phpstan/phpdoc-parser": "^1.29", + "phpstan/phpdoc-parser": "^1.29 || ^2.0", "phpdocumentor/reflection-docblock": "^5.1" }, "require-dev": { "doctrine/dbal": "^4.0", - "larastan/larastan": "^2.0", + "larastan/larastan": "^2.0 || ^3.0", "orchestra/testbench": "^9.1", "phpunit/phpunit": "^11.2", "api-platform/graphql": "^4.0", diff --git a/src/Laravel/workbench/app/Models/Book.php b/src/Laravel/workbench/app/Models/Book.php index 5bf77ecd21c..65f8050743b 100644 --- a/src/Laravel/workbench/app/Models/Book.php +++ b/src/Laravel/workbench/app/Models/Book.php @@ -88,8 +88,8 @@ class Book extends Model use HasFactory; use HasUlids; - protected $visible = ['name', 'author', 'isbn', 'publication_date', 'published', 'is_available']; - protected $fillable = ['name', 'is_available']; + protected $visible = ['name', 'author', 'isbn', 'publication_date', 'is_available', 'published']; + protected $fillable = ['name', 'publication_date', 'isbn', 'is_available', 'published']; protected $casts = [ 'is_available' => 'boolean', ]; diff --git a/src/Laravel/workbench/app/Models/Vault.php b/src/Laravel/workbench/app/Models/Vault.php index 2e22a52d6c2..7949c84a63c 100644 --- a/src/Laravel/workbench/app/Models/Vault.php +++ b/src/Laravel/workbench/app/Models/Vault.php @@ -42,6 +42,10 @@ class Vault extends Model { use HasFactory; + protected $fillable = [ + 'secret', + ]; + public static function provide(): self { $v = new self(); diff --git a/src/Metadata/Extractor/YamlResourceExtractor.php b/src/Metadata/Extractor/YamlResourceExtractor.php index 45fc6a29db0..402e1f3a0cd 100644 --- a/src/Metadata/Extractor/YamlResourceExtractor.php +++ b/src/Metadata/Extractor/YamlResourceExtractor.php @@ -472,7 +472,7 @@ private function buildParameters(array $resource): ?array $parameters[$key] = new $cl( key: $key, required: $this->phpize($parameter, 'required', 'bool'), - schema: $parameter['schema'], + schema: $parameter['schema'] ?? null, openApi: ($parameter['openapi'] ?? null) ? new Parameter( name: $parameter['openapi']['name'], in: $parameter['in'] ?? 'query', diff --git a/src/Metadata/Tests/Extractor/Adapter/XmlResourceAdapter.php b/src/Metadata/Tests/Extractor/Adapter/XmlResourceAdapter.php index 749d047e8d8..7b6d94584f2 100644 --- a/src/Metadata/Tests/Extractor/Adapter/XmlResourceAdapter.php +++ b/src/Metadata/Tests/Extractor/Adapter/XmlResourceAdapter.php @@ -535,8 +535,14 @@ private function buildParameters(\SimpleXMLElement $resource, ?array $values = n $childNode = $node->addChild('parameter'); $childNode->addAttribute('in', 'query'); $childNode->addAttribute('key', $key); - $childNode->addAttribute('required', $this->parse($value['required'])); - $this->buildValues($childNode->addChild('schema'), $value['schema']); + + if (\array_key_exists('required', $value)) { + $childNode->addAttribute('required', $this->parse($value['required'])); + } + + if (\array_key_exists('schema', $value)) { + $this->buildValues($childNode->addChild('schema'), $value['schema']); + } } } diff --git a/src/Metadata/Tests/Extractor/Adapter/resources.xml b/src/Metadata/Tests/Extractor/Adapter/resources.xml index 627958a2d4e..f8be268f17a 100644 --- a/src/Metadata/Tests/Extractor/Adapter/resources.xml +++ b/src/Metadata/Tests/Extractor/Adapter/resources.xml @@ -1,3 +1,3 @@ -someirischemaanotheririschemaCommentapplication/vnd.openxmlformats-officedocument.spreadsheetml.sheetapplication/merge-patch+json+ldapplication/merge-patch+json+ld_foo\d+bazhttps
60120AuthorizationAccept-LanguageAcceptcomment:read_collectioncomment:writebazbazbarcomment.another_custom_filteruserIdLorem ipsum dolor sit ametDolor sit ametbarstringapplication/vnd.ms-excelapplication/merge-patch+jsonapplication/merge-patch+jsonpouet\d+barhttphttps60120AuthorizationAccept-Languagecomment:readcomment:writecomment:custombazbazbarcomment.custom_filterfoobarcustombazcustomquxcomment:read_collectioncomment:writebarcomment.another_custom_filteruserIdLorem ipsum dolor sit ametDolor sit ametbar/v1/v1Lorem ipsum dolor sit ametDolor sit amet/v1Lorem ipsum dolor sit ametDolor sit amet/v1Lorem ipsum dolor sit ametDolor sit ametLorem ipsum dolor sit ametDolor sit amet +someirischemaanotheririschemaCommentapplication/vnd.openxmlformats-officedocument.spreadsheetml.sheetapplication/merge-patch+json+ldapplication/merge-patch+json+ld_foo\d+bazhttps
60120AuthorizationAccept-LanguageAcceptcomment:read_collectioncomment:writebazbazbarcomment.another_custom_filteruserIdLorem ipsum dolor sit ametDolor sit ametbarstringapplication/vnd.ms-excelapplication/merge-patch+jsonapplication/merge-patch+jsonpouet\d+barhttphttps60120AuthorizationAccept-Languagecomment:readcomment:writecomment:custombazbazbarcomment.custom_filterfoobarcustombazcustomquxcomment:read_collectioncomment:writebarcomment.another_custom_filteruserIdLorem ipsum dolor sit ametDolor sit ametbar/v1/v1Lorem ipsum dolor sit ametDolor sit amet/v1Lorem ipsum dolor sit ametDolor sit amet/v1Lorem ipsum dolor sit ametDolor sit ametLorem ipsum dolor sit ametDolor sit amet diff --git a/src/Metadata/Tests/Extractor/Adapter/resources.yaml b/src/Metadata/Tests/Extractor/Adapter/resources.yaml index 6e9f10d9404..945c95072b6 100644 --- a/src/Metadata/Tests/Extractor/Adapter/resources.yaml +++ b/src/Metadata/Tests/Extractor/Adapter/resources.yaml @@ -103,6 +103,7 @@ resources: Symfony\Component\Serializer\Exception\ExceptionInterface: 404 queryParameterValidationEnabled: false strictQueryParameterValidation: false + hideHydraOperation: false read: true deserialize: false validate: false @@ -138,6 +139,9 @@ resources: - rel: 'http://www.w3.org/ns/json-ld#error' href: 'http://www.w3.org/ns/hydra/error' + parameters: + date: + key: date formats: json: null jsonld: null @@ -341,6 +345,7 @@ resources: middleware: null parameters: null strictQueryParameterValidation: false + hideHydraOperation: false extraProperties: custom_property: 'Lorem ipsum dolor sit amet' another_custom_property: diff --git a/src/Metadata/Tests/Extractor/ResourceMetadataCompatibilityTest.php b/src/Metadata/Tests/Extractor/ResourceMetadataCompatibilityTest.php index d5105c32123..07eae3d2670 100644 --- a/src/Metadata/Tests/Extractor/ResourceMetadataCompatibilityTest.php +++ b/src/Metadata/Tests/Extractor/ResourceMetadataCompatibilityTest.php @@ -437,6 +437,9 @@ final class ResourceMetadataCompatibilityTest extends TestCase 'links' => [ ['rel' => 'http://www.w3.org/ns/json-ld#error', 'href' => 'http://www.w3.org/ns/hydra/error'], ], + 'parameters' => [ + 'date' => ['key' => 'date'], + ], ], ], ], @@ -755,7 +758,7 @@ private function withParameters(array $values): ?array $parameters = []; foreach ($values as $k => $value) { - $parameters[$k] = new QueryParameter(key: $value['key'], required: $value['required'], schema: $value['schema']); + $parameters[$k] = new QueryParameter(key: $value['key'] ?? $k, required: $value['required'] ?? null, schema: $value['schema'] ?? null); } return $parameters; diff --git a/src/OpenApi/Factory/OpenApiFactory.php b/src/OpenApi/Factory/OpenApiFactory.php index 30fe6cb29bb..caeb5be9731 100644 --- a/src/OpenApi/Factory/OpenApiFactory.php +++ b/src/OpenApi/Factory/OpenApiFactory.php @@ -360,6 +360,10 @@ private function collectPaths(ApiResource $resource, ResourceMetadataCollection } } + if (true === $overrideResponses && !isset($existingResponses[403]) && $operation->getSecurity()) { + $openapiOperation = $openapiOperation->withResponse(403, new Response('Forbidden')); + } + if (true === $overrideResponses && !$operation instanceof CollectionOperationInterface && 'POST' !== $operation->getMethod()) { if (!isset($existingResponses[404])) { $openapiOperation = $openapiOperation->withResponse(404, new Response('Resource not found')); diff --git a/src/OpenApi/Tests/Factory/OpenApiFactoryTest.php b/src/OpenApi/Tests/Factory/OpenApiFactoryTest.php index 648ca63ad8c..45dfdef32bd 100644 --- a/src/OpenApi/Tests/Factory/OpenApiFactoryTest.php +++ b/src/OpenApi/Tests/Factory/OpenApiFactoryTest.php @@ -58,6 +58,7 @@ use ApiPlatform\OpenApi\Tests\Fixtures\Dummy; use ApiPlatform\OpenApi\Tests\Fixtures\DummyErrorResource; use ApiPlatform\OpenApi\Tests\Fixtures\DummyFilter; +use ApiPlatform\OpenApi\Tests\Fixtures\Issue6872\Diamond; use ApiPlatform\OpenApi\Tests\Fixtures\OutputDto; use ApiPlatform\State\Pagination\PaginationOptions; use ApiPlatform\Tests\Fixtures\TestBundle\ApiResource\WithParameter; @@ -82,6 +83,7 @@ public function testInvoke(): void $baseOperation = (new HttpOperation())->withTypes(['http://schema.example.com/Dummy'])->withInputFormats(self::OPERATION_FORMATS['input_formats'])->withOutputFormats(self::OPERATION_FORMATS['output_formats'])->withClass(Dummy::class)->withOutput([ 'class' => OutputDto::class, ])->withPaginationClientItemsPerPage(true)->withShortName('Dummy')->withDescription('This is a dummy'); + $dummyResourceWebhook = (new ApiResource())->withOperations(new Operations([ 'dummy webhook' => (new Get())->withUriTemplate('/dummy/{id}')->withShortName('short')->withOpenapi(new Webhook('first webhook')), 'an other dummy webhook' => (new Post())->withUriTemplate('/dummies')->withShortName('short something')->withOpenapi(new Webhook('happy webhook', new Model\PathItem(post: new Operation( @@ -269,13 +271,23 @@ public function testInvoke(): void ]))->withOperation($baseOperation), ])); + $diamondResource = (new ApiResource()) + ->withOperations(new Operations([ + 'getDiamondCollection' => (new GetCollection(uriTemplate: '/diamonds')) + ->withSecurity("is_granted('ROLE_USER')") + ->withOperation($baseOperation), + 'putDiamond' => (new Put(uriTemplate: '/diamond/{id}')) + ->withOperation($baseOperation), + ])); + $resourceNameCollectionFactoryProphecy = $this->prophesize(ResourceNameCollectionFactoryInterface::class); - $resourceNameCollectionFactoryProphecy->create()->shouldBeCalled()->willReturn(new ResourceNameCollection([Dummy::class, WithParameter::class])); + $resourceNameCollectionFactoryProphecy->create()->shouldBeCalled()->willReturn(new ResourceNameCollection([Dummy::class, WithParameter::class, Diamond::class])); $resourceCollectionMetadataFactoryProphecy = $this->prophesize(ResourceMetadataCollectionFactoryInterface::class); $resourceCollectionMetadataFactoryProphecy->create(Dummy::class)->shouldBeCalled()->willReturn(new ResourceMetadataCollection(Dummy::class, [$dummyResource, $dummyResourceWebhook])); $resourceCollectionMetadataFactoryProphecy->create(DummyErrorResource::class)->shouldBeCalled()->willReturn(new ResourceMetadataCollection(DummyErrorResource::class, [new ApiResource(operations: [new ErrorOperation(name: 'err', description: 'nice one!')])])); $resourceCollectionMetadataFactoryProphecy->create(WithParameter::class)->shouldBeCalled()->willReturn(new ResourceMetadataCollection(WithParameter::class, [$parameterResource])); + $resourceCollectionMetadataFactoryProphecy->create(Diamond::class)->shouldBeCalled()->willReturn(new ResourceMetadataCollection(Diamond::class, [$diamondResource])); $propertyNameCollectionFactoryProphecy = $this->prophesize(PropertyNameCollectionFactoryInterface::class); $propertyNameCollectionFactoryProphecy->create(Dummy::class, Argument::any())->shouldBeCalled()->willReturn(new PropertyNameCollection(['id', 'name', 'description', 'dummyDate', 'enum'])); @@ -1174,5 +1186,20 @@ public function testInvoke(): void ], deprecated: false ), $paths->getPath('/erroredDummies')->getGet()); + + $diamondsGetPath = $paths->getPath('/diamonds'); + $diamondGetOperation = $diamondsGetPath->getGet(); + $diamondGetResponses = $diamondGetOperation->getResponses(); + + $this->assertNotNull($diamondGetOperation); + $this->assertArrayHasKey('403', $diamondGetResponses); + $this->assertSame('Forbidden', $diamondGetResponses['403']->getDescription()); + + $diamondsPutPath = $paths->getPath('/diamond/{id}'); + $diamondPutOperation = $diamondsPutPath->getPut(); + $diamondPutResponses = $diamondPutOperation->getResponses(); + + $this->assertNotNull($diamondPutOperation); + $this->assertArrayNotHasKey('403', $diamondPutResponses); } } diff --git a/src/OpenApi/Tests/Fixtures/Issue6872/Diamond.php b/src/OpenApi/Tests/Fixtures/Issue6872/Diamond.php new file mode 100644 index 00000000000..e2cf520f62c --- /dev/null +++ b/src/OpenApi/Tests/Fixtures/Issue6872/Diamond.php @@ -0,0 +1,19 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +declare(strict_types=1); + +namespace ApiPlatform\OpenApi\Tests\Fixtures\Issue6872; + +class Diamond +{ + public float $weight; +} diff --git a/src/ParameterValidator/.github/workflows/close_pr.yml b/src/ParameterValidator/.github/workflows/close_pr.yml deleted file mode 100644 index 72a8ab4325e..00000000000 --- a/src/ParameterValidator/.github/workflows/close_pr.yml +++ /dev/null @@ -1,13 +0,0 @@ -name: Close Pull Request - -on: - pull_request_target: - types: [opened] - -jobs: - run: - runs-on: ubuntu-latest - steps: - - uses: superbrothers/close-pull-request@v3 - with: - comment: "Thank you for your pull request. However, you have submitted this PR on a read-only sub split of `api-platform/core`. Please submit your PR on the https://github.com/api-platform/core repository.

Thanks!" diff --git a/src/State/ProcessorInterface.php b/src/State/ProcessorInterface.php index 07bb906798a..5d676e16ae5 100644 --- a/src/State/ProcessorInterface.php +++ b/src/State/ProcessorInterface.php @@ -29,9 +29,9 @@ interface ProcessorInterface /** * Handles the state. * - * @param T1 $data - * @param array $uriVariables - * @param array&array{request?: Request|\Illuminate\Http\Request, previous_data?: mixed, resource_class?: string|null, original_data?: mixed} $context + * @param T1 $data + * @param array $uriVariables + * @param array&array{request?: Request, previous_data?: mixed, resource_class?: string|null, original_data?: mixed} $context * * @return T2 */