From 1b9dfccc8d64be2b04e48490905771a77aefdd96 Mon Sep 17 00:00:00 2001 From: Nicolas PHILIPPE Date: Mon, 16 Sep 2024 10:31:06 +0200 Subject: [PATCH 01/20] fix: count TraversablePaginator (#6611) --- src/State/Pagination/TraversablePaginator.php | 4 ++++ tests/State/Pagination/TraversablePaginatorTest.php | 2 ++ 2 files changed, 6 insertions(+) diff --git a/src/State/Pagination/TraversablePaginator.php b/src/State/Pagination/TraversablePaginator.php index 8749e3b2029..87de541a9b8 100644 --- a/src/State/Pagination/TraversablePaginator.php +++ b/src/State/Pagination/TraversablePaginator.php @@ -68,6 +68,10 @@ public function count(): int return (int) ceil($this->totalItems); } + if ($this->totalItems === $this->itemsPerPage) { + return (int) ceil($this->totalItems); + } + return $this->totalItems % $this->itemsPerPage; } diff --git a/tests/State/Pagination/TraversablePaginatorTest.php b/tests/State/Pagination/TraversablePaginatorTest.php index 4f49a8f353d..10dd837e057 100644 --- a/tests/State/Pagination/TraversablePaginatorTest.php +++ b/tests/State/Pagination/TraversablePaginatorTest.php @@ -50,6 +50,8 @@ public static function initializeProvider(): array 'Empty results' => [[], 1, 2, 0, 1, 0], '0 items per page' => [[0, 1, 2, 3], 1, 0, 4, 1, 4], 'Total items less than items per page' => [[0, 1, 2], 1, 4, 3, 1, 3], + 'Only one result' => [[0], 1, 1, 1, 1, 1], + 'Same result number than total page' => [[0, 2, 3], 1, 3, 3, 1, 3], ]; } } From a3a4a990d527136f093b022782a82e1d5b04c0b5 Mon Sep 17 00:00:00 2001 From: soyuka Date: Wed, 18 Sep 2024 11:08:08 +0200 Subject: [PATCH 02/20] fix(metadata): loop on operations can be null --- .../Factory/BackedEnumResourceMetadataCollectionFactory.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Metadata/Resource/Factory/BackedEnumResourceMetadataCollectionFactory.php b/src/Metadata/Resource/Factory/BackedEnumResourceMetadataCollectionFactory.php index 9f7411b1c3b..4c47bf90d3e 100644 --- a/src/Metadata/Resource/Factory/BackedEnumResourceMetadataCollectionFactory.php +++ b/src/Metadata/Resource/Factory/BackedEnumResourceMetadataCollectionFactory.php @@ -38,7 +38,7 @@ public function create(string $resourceClass): ResourceMetadataCollection foreach ($resourceMetadataCollection as $i => $resourceMetadata) { $newOperations = []; - foreach ($resourceMetadata->getOperations() as $operationName => $operation) { + foreach ($resourceMetadata->getOperations() ?? [] as $operationName => $operation) { $newOperations[$operationName] = $operation; if (null !== $operation->getProvider()) { @@ -49,7 +49,7 @@ public function create(string $resourceClass): ResourceMetadataCollection } $newGraphQlOperations = []; - foreach ($resourceMetadata->getGraphQlOperations() as $operationName => $operation) { + foreach ($resourceMetadata->getGraphQlOperations() ?? [] as $operationName => $operation) { $newGraphQlOperations[$operationName] = $operation; if (null !== $operation->getProvider()) { From 31f7ec2f8a80c4608c0b883d2eed926da34d3b98 Mon Sep 17 00:00:00 2001 From: soyuka Date: Wed, 18 Sep 2024 19:25:23 +0200 Subject: [PATCH 03/20] docs: 3.4.0 changelog --- CHANGELOG.md | 43 ++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 42 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 7d73466151c..d30a1e2f4d6 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,47 @@ # Changelog +## v3.4.0 + +### Deprecations: + +Namespaces like `ApiPlatform/Api` or `ApiPlatform/Util` are deprecated and will be removed in 4.0. +You should now install `api-platform/symfony` instead of `api-platform/core`. + +Read the [upgrade guide](https://api-platform.com/docs/core/upgrade-guide/) for detailed steps. + +### Bug fixes + +* [56153b755](https://github.com/api-platform/core/commit/56153b755151ee11c8c17fdc3fd919d544f078ac) fix(hydra): error hydra prefix (#6599) +* [7c89f66a5](https://github.com/api-platform/core/commit/7c89f66a534cc1100c3dab0a129381d307d9d8b4) fix: replace ApiPlatform\\Exception use by ApiPlatform\Metadata\\Exception (#6597) +* [a3a4a990d](https://github.com/api-platform/core/commit/a3a4a990d527136f093b022782a82e1d5b04c0b5) fix(metadata): loop on operations can be null +* [ef0ee6427](https://github.com/api-platform/core/commit/ef0ee6427f8056bcb2617c228a7cf9ffd9d29ccd) fix(doctrine): use parameter.property for filter value (#6572) +* [17c6b586c](https://github.com/api-platform/core/commit/17c6b586c5ab49437ac11dd092efdd5f0baf569b) fix(state): log on missing provider (#6519) +* [601ccfb42](https://github.com/api-platform/core/commit/601ccfb4243803f40a7fa7179e0661da59c88b86) fix(doctrine): move event listeners to doctrine/common (#6573) +* [6499e0aa5](https://github.com/api-platform/core/commit/6499e0aa5dd61fdff7706e7940cdf8c1fc3e18ef) fix: deprecate url generator interface namespace (#6575) +* [3c5aea80f](https://github.com/api-platform/core/commit/3c5aea80fdbed20216764f6d721fe4f37cf2889d) fix(symfony): load isApiResource metadata (#6562) +* [61af0cc90](https://github.com/api-platform/core/commit/61af0cc90c1e095edb12e32ef433a742ef46637e) fix(doctrine): allow doctrine/dbal:^4 +* [e063b80af](https://github.com/api-platform/core/commit/e063b80afe012ca4a6c8999de55b59193e8ae0ae) fix: parameter context for filters (#6535) +* [e22392193](https://github.com/api-platform/core/commit/e22392193bb1fc71ece5abf393fa54b0745fc287) fix(state): security parameter with listeners (#6457) + +Various fixes for components isolation. + +### Features + +* [130fb5a8c](https://github.com/api-platform/core/commit/130fb5a8c833430e5e09624b06f296e0bcb7ceed) feat: better path sorting for openapi UIs (#6583) +* [26d700e06](https://github.com/api-platform/core/commit/26d700e06035eaf4d04ddd52f3101dae690734d8) feat(symfony): add error page (#6389) +* [48267c9b6](https://github.com/api-platform/core/commit/48267c9b6a942b1fb54f0efcfa5b2d2ac47c93bf) feat(openapi): add error resources schemes (#6332) +* [e91c783a2](https://github.com/api-platform/core/commit/e91c783a2abf51bf2bdcf1230826108632c44a0d) feat(state): "deserializer_type" context (#6429) +* [17c6b586c](https://github.com/api-platform/core/commit/17c6b586c5ab49437ac11dd092efdd5f0baf569b) feat(state): log on missing provider (#6519) +* [05e75be83](https://github.com/api-platform/core/commit/05e75be834c629e0487caaaedfe9fdf0bd5a7226) feat(doctrine): add new filter for filtering an entity using PHP backed enum, resolves #6506 (#6547) (#6560) +* [0b985ae76](https://github.com/api-platform/core/commit/0b985ae760bc4689d3f5bbacebb21b35b334d0be) feat(state): add security to parameters (#6435) +* [63ccfd58c](https://github.com/api-platform/core/commit/63ccfd58c95b5aa4aa0353eb122d96ef35187222) feat: BackedEnum resources (#6309) +* [65296eaf1](https://github.com/api-platform/core/commit/65296eaf1eb18dc725e9316e9ab49b191aae43a3) feat(openapi): allow optional request body content (#6374) +* [7399fcf7e](https://github.com/api-platform/core/commit/7399fcf7eaf28cd649d137da1fdd54f69093e275) feat(symfony): skip error handler (#6463) +* [74986cb55](https://github.com/api-platform/core/commit/74986cb552182dc645bd1fc967faa0954dd59e0a) feat: inflector as service (#6447) +* [b47edb2a4](https://github.com/api-platform/core/commit/b47edb2a499c34e79c167f963e3a626a3e9d040a) feat(serializer): context IRI in HAL or JsonApi format (#6215) +* [db1241c66](https://github.com/api-platform/core/commit/db1241c66a08d226f57d8d61e0ec519071c6afdb) feat(openapi): make open_api_override_responses act on default 404 response generation (#6551) +* [e09e73efc](https://github.com/api-platform/core/commit/e09e73efc5b4a39ab33d00c5d5422d8d9f7b5e89) feat: remove hydra prefix (#6418) + ## v3.4.0-alpha.5 ### Deprecations: @@ -32,7 +74,6 @@ Various fixes for components isolation. * [db1241c66](https://github.com/api-platform/core/commit/db1241c66a08d226f57d8d61e0ec519071c6afdb) feat(openapi): make open_api_override_responses act on default 404 response generation (#6551) * [e09e73efc](https://github.com/api-platform/core/commit/e09e73efc5b4a39ab33d00c5d5422d8d9f7b5e89) feat: remove hydra prefix (#6418) - ## v3.4.0-alpha.4 ### Deprecations: From a6a07de4bd8637a7521d97e996964d80e8c16c9c Mon Sep 17 00:00:00 2001 From: soyuka Date: Wed, 18 Sep 2024 19:30:10 +0200 Subject: [PATCH 04/20] docs: 4.0.0 changelog --- CHANGELOG.md | 39 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 39 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 69592e3f491..91455c45bec 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,44 @@ # Changelog +## v4.0.0 + +### Bug fixes + +* [7c5689626](https://github.com/api-platform/core/commit/7c568962634691892fe1057b3c982765a1c20ba2) fix(laravel): call authorize on delete but not validation (#6618) +* [d74b2b5fa](https://github.com/api-platform/core/commit/d74b2b5fa939a9f5ca11d3538e358070047a6c3d) fix: swagger ui with route identifier (#6616) +* [de6e3f546](https://github.com/api-platform/core/commit/de6e3f546a26c5ad5444d8e2448d81faec36bd73) fix(laravel): validate enum schema within filter (#6615) +* [0a461d749](https://github.com/api-platform/core/commit/0a461d749b7b4ac706f3b7b6138a13cb6e4a9d2d) fix(symfony): allow schema restriction for collection like property from choice constraint (#6520) +* [0c66a494d](https://github.com/api-platform/core/commit/0c66a494d3bda5817e59da2e43f0232e2e8fea15) fix(laravel): cache metadata, add trace on debug mode (#6555) +* [1b6e7c6cc](https://github.com/api-platform/core/commit/1b6e7c6ccf3d2c61816a7dceb35f1d5980ea0565) fix(laravel): disable GraphQL by default and fix provider +* [290944103](https://github.com/api-platform/core/commit/290944103039a4a6d64904d1a89264b800c809d5) fix(laravel): SwaggerUI title (#6527) +* [2df0860b5](https://github.com/api-platform/core/commit/2df0860b577bb1ae0882096436f3eaeb91281901) fix(laravel): Eloquent date and datetime type detection (#6529) +* [2fc74f2e6](https://github.com/api-platform/core/commit/2fc74f2e651f229e982343c1cb0c6a2c5d5eee64) fix: remove PUT from default operations (#6570) +* [3b42c9ff2](https://github.com/api-platform/core/commit/3b42c9ff235de5feac555d0283c513a6e4643953) fix: deserialization path for not denormalizable relations collected errors (#6537) +* [3c554a605](https://github.com/api-platform/core/commit/3c554a605ec9d5f36dfd852c4f93f0ce582064c9) fix(laravel): docs _format and open swagger ui (#6595) +* [3c5aea80f](https://github.com/api-platform/core/commit/3c5aea80fdbed20216764f6d721fe4f37cf2889d) fix(symfony): load isApiResource metadata (#6562) +* [4ee209eff](https://github.com/api-platform/core/commit/4ee209effb0fd11789db9f016d6e90aa3cb942a9) fix(laravel): visible and hidden fields support (#6538) +* [6e15eb95f](https://github.com/api-platform/core/commit/6e15eb95fffb2deec3d381f5d6fd87e189772270) fix(laravel): register HydraPartialCollectionViewNormalizer (#6588) +* [86365be2a](https://github.com/api-platform/core/commit/86365be2a5b8e8d0050e09d4e401bb758aa8b7a8) fix(laravel): Eloquent PropertyAccessor (#6536) +* [a1dd0b54d](https://github.com/api-platform/core/commit/a1dd0b54d137d70f2163fa03690c1f4c74a549c0) fix(laravel): entrypoint with doc formats (#6552) +* [a4a53ab48](https://github.com/api-platform/core/commit/a4a53ab4838f4d17d3677952157b44ec165e3e3a) fix(laravel): identitifer is not writable unless marked as writable (#6531) +* [a6f355358](https://github.com/api-platform/core/commit/a6f355358a88e7cf7759db0dee41e185157ddc68) fix(laravel): do not normalize exception originalTrace (#6533) +* [bd6a57c4c](https://github.com/api-platform/core/commit/bd6a57c4c0b38d6f880a5bd79031a87033f707e6) fix(laravel): snake case props (#6532) +* [c31566602](https://github.com/api-platform/core/commit/c315666022185839742b8c5ef81601d85d8c3f4b) fix(laravel): api_doc route regex +* [ebc61d59d](https://github.com/api-platform/core/commit/ebc61d59d60eda3a020593cf4cb46c5d30548e46) fix(laravel): entrypoint serialization (#6541) + +### Features + +* [06a647a80](https://github.com/api-platform/core/commit/06a647a80d4c6b7bfb3474d0685bcb445b56a5a8) feat(laravel): add CSV support (#6617) +* [a49bde1ea](https://github.com/api-platform/core/commit/a49bde1ea79ae4226b70c20f9bf967ac77e9ab89) feat(laravel): filter validations rules +* [03357fb90](https://github.com/api-platform/core/commit/03357fb90ac0003f0cec2002df01711d0fb99a1e) feat(laravel): supports more Eloquent types (#6544) +* [05e75be83](https://github.com/api-platform/core/commit/05e75be834c629e0487caaaedfe9fdf0bd5a7226) feat(doctrine): add new filter for filtering an entity using PHP backed enum, resolves #6506 (#6547) (#6560) +* [538648840](https://github.com/api-platform/core/commit/538648840dc7439b12033ed6f413f3167705da4d) feat(laravel): enable graphQl support (#6550) +* [5b9767be2](https://github.com/api-platform/core/commit/5b9767be215afdf61fccf8490d0a3a7018078ce5) feat(laravel): policy, auth and gate (#6523) +* [5e1233c57](https://github.com/api-platform/core/commit/5e1233c57a497d00a7c4bd3e3ad0cac25aeac014) feat(laravel): search filter (#6534) +* [9c461626f](https://github.com/api-platform/core/commit/9c461626f7d02f8d15487134b636f11966c19a5e) feat(laravel): provide a trait in addition to the annotation (#6543) +* [c9f18d4fb](https://github.com/api-platform/core/commit/c9f18d4fb833ea0b89ef18021cad491cf0600ef1) feat(laravel): eloquent filters (search, date, equals, or) (#6593) +* [e09e73efc](https://github.com/api-platform/core/commit/e09e73efc5b4a39ab33d00c5d5422d8d9f7b5e89) feat: remove hydra prefix (#6418) + ## v4.0.0-alpha.5 ### Bug fixes From ac771b377e71df894a1e6d0dd60aae39c31dabe6 Mon Sep 17 00:00:00 2001 From: valentindrdt Date: Wed, 18 Sep 2024 09:02:54 -0700 Subject: [PATCH 05/20] feat(laravel): automatically register policies (#6623) * feat: if a policy matches the name of a model we automatically register it, no need to do it manually anymore * fix: optimising the way we handle collection * fix: phpstan * fix: cs-fixer --- ...quentResourceCollectionMetadataFactory.php | 27 ++++ .../Security/ResourceAccessChecker.php | 8 +- src/Laravel/Tests/Policy/BookAllowPolicy.php | 60 +++++++ src/Laravel/Tests/Policy/BookDenyPolicy.php | 60 +++++++ src/Laravel/Tests/Policy/PolicyAllowTest.php | 149 ++++++++++++++++++ src/Laravel/Tests/Policy/PolicyDenyTest.php | 135 ++++++++++++++++ 6 files changed, 438 insertions(+), 1 deletion(-) create mode 100644 src/Laravel/Tests/Policy/BookAllowPolicy.php create mode 100644 src/Laravel/Tests/Policy/BookDenyPolicy.php create mode 100644 src/Laravel/Tests/Policy/PolicyAllowTest.php create mode 100644 src/Laravel/Tests/Policy/PolicyDenyTest.php diff --git a/src/Laravel/Eloquent/Metadata/Factory/Resource/EloquentResourceCollectionMetadataFactory.php b/src/Laravel/Eloquent/Metadata/Factory/Resource/EloquentResourceCollectionMetadataFactory.php index 5a7ddb822bb..c693ff55ff5 100644 --- a/src/Laravel/Eloquent/Metadata/Factory/Resource/EloquentResourceCollectionMetadataFactory.php +++ b/src/Laravel/Eloquent/Metadata/Factory/Resource/EloquentResourceCollectionMetadataFactory.php @@ -18,13 +18,29 @@ use ApiPlatform\Laravel\Eloquent\State\PersistProcessor; use ApiPlatform\Laravel\Eloquent\State\RemoveProcessor; use ApiPlatform\Metadata\CollectionOperationInterface; +use ApiPlatform\Metadata\Delete; use ApiPlatform\Metadata\DeleteOperationInterface; +use ApiPlatform\Metadata\Get; +use ApiPlatform\Metadata\GetCollection; +use ApiPlatform\Metadata\Patch; +use ApiPlatform\Metadata\Post; +use ApiPlatform\Metadata\Put; use ApiPlatform\Metadata\Resource\Factory\ResourceMetadataCollectionFactoryInterface; use ApiPlatform\Metadata\Resource\ResourceMetadataCollection; use Illuminate\Database\Eloquent\Model; +use Illuminate\Support\Facades\Gate; final class EloquentResourceCollectionMetadataFactory implements ResourceMetadataCollectionFactoryInterface { + private const POLICY_METHODS = [ + Put::class => 'update', + Post::class => 'create', + Get::class => 'view', + GetCollection::class => 'viewAny', + Delete::class => 'delete', + Patch::class => 'update', + ]; + public function __construct( private readonly ResourceMetadataCollectionFactoryInterface $decorated, ) { @@ -55,6 +71,17 @@ public function create(string $resourceClass): ResourceMetadataCollection $operation = $operation->withProvider($operation instanceof CollectionOperationInterface ? CollectionProvider::class : ItemProvider::class); } + if (!$operation->getPolicy() && ($policy = Gate::getPolicyFor($model))) { + $policyMethod = self::POLICY_METHODS[$operation::class] ?? null; + if ($operation instanceof Put && $operation->getAllowCreate()) { + $policyMethod = self::POLICY_METHODS[Post::class]; + } + + if ($policyMethod && method_exists($policy, $policyMethod)) { + $operation = $operation->withPolicy($policyMethod); + } + } + if (!$operation->getProcessor()) { $operation = $operation->withProcessor($operation instanceof DeleteOperationInterface ? RemoveProcessor::class : PersistProcessor::class); } diff --git a/src/Laravel/Security/ResourceAccessChecker.php b/src/Laravel/Security/ResourceAccessChecker.php index 1a7c4d53541..5633ea1c808 100644 --- a/src/Laravel/Security/ResourceAccessChecker.php +++ b/src/Laravel/Security/ResourceAccessChecker.php @@ -13,6 +13,7 @@ namespace ApiPlatform\Laravel\Security; +use ApiPlatform\Laravel\Eloquent\Paginator; use ApiPlatform\Metadata\ResourceAccessCheckerInterface; use Illuminate\Support\Facades\Gate; @@ -20,6 +21,11 @@ class ResourceAccessChecker implements ResourceAccessCheckerInterface { public function isGranted(string $resourceClass, string $expression, array $extraVariables = []): bool { - return Gate::allows($expression, $extraVariables['object']); + return Gate::allows( + $expression, + $extraVariables['object'] instanceof Paginator ? + $resourceClass : + $extraVariables['object'] + ); } } diff --git a/src/Laravel/Tests/Policy/BookAllowPolicy.php b/src/Laravel/Tests/Policy/BookAllowPolicy.php new file mode 100644 index 00000000000..bea0109c494 --- /dev/null +++ b/src/Laravel/Tests/Policy/BookAllowPolicy.php @@ -0,0 +1,60 @@ + + * + * 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\Laravel\Tests\Policy; + +use Illuminate\Foundation\Auth\User; +use Workbench\App\Models\Book; + +class BookAllowPolicy +{ + /** + * Determine whether the user can view any models. + */ + public function viewAny(?User $user): bool + { + return true; + } + + /** + * Determine whether the user can view the model. + */ + public function view(?User $user, Book $book): bool + { + return true; + } + + /** + * Determine whether the user can create models. + */ + public function create(?User $user): bool + { + return true; + } + + /** + * Determine whether the user can update the model. + */ + public function update(?User $user, Book $book): bool + { + return true; + } + + /** + * Determine whether the user can delete the model. + */ + public function delete(?User $user, Book $book): bool + { + return true; + } +} diff --git a/src/Laravel/Tests/Policy/BookDenyPolicy.php b/src/Laravel/Tests/Policy/BookDenyPolicy.php new file mode 100644 index 00000000000..d9f70e82930 --- /dev/null +++ b/src/Laravel/Tests/Policy/BookDenyPolicy.php @@ -0,0 +1,60 @@ + + * + * 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\Laravel\Tests\Policy; + +use Illuminate\Foundation\Auth\User; +use Workbench\App\Models\Book; + +class BookDenyPolicy +{ + /** + * Determine whether the user can view any models. + */ + public function viewAny(?User $user): bool + { + return false; + } + + /** + * Determine whether the user can view the model. + */ + public function view(?User $user, Book $book): bool + { + return false; + } + + /** + * Determine whether the user can create models. + */ + public function create(?User $user): bool + { + return false; + } + + /** + * Determine whether the user can update the model. + */ + public function update(?User $user, Book $book): bool + { + return false; + } + + /** + * Determine whether the user can delete the model. + */ + public function delete(?User $user, Book $book): bool + { + return false; + } +} diff --git a/src/Laravel/Tests/Policy/PolicyAllowTest.php b/src/Laravel/Tests/Policy/PolicyAllowTest.php new file mode 100644 index 00000000000..c5124a6d5e1 --- /dev/null +++ b/src/Laravel/Tests/Policy/PolicyAllowTest.php @@ -0,0 +1,149 @@ + + * + * 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\Laravel\Tests\Policy; + +use ApiPlatform\Laravel\Test\ApiTestAssertionsTrait; +use Illuminate\Contracts\Config\Repository; +use Illuminate\Foundation\Application; +use Illuminate\Foundation\Testing\RefreshDatabase; +use Illuminate\Support\Facades\Gate; +use Orchestra\Testbench\Concerns\WithWorkbench; +use Orchestra\Testbench\TestCase; +use Workbench\App\Models\Author; +use Workbench\App\Models\Book; + +class PolicyAllowTest extends TestCase +{ + use ApiTestAssertionsTrait; + use RefreshDatabase; + use WithWorkbench; + + /** + * @param Application $app + */ + protected function defineEnvironment($app): void + { + Gate::guessPolicyNamesUsing(function (string $modelClass) { + return Book::class === $modelClass ? + BookAllowPolicy::class : + null; + }); + + tap($app['config'], function (Repository $config): void { + $config->set('api-platform.formats', ['jsonapi' => ['application/vnd.api+json']]); + $config->set('api-platform.docs_formats', ['jsonapi' => ['application/vnd.api+json']]); + }); + } + + public function testGetCollection(): void + { + $response = $this->get('/api/books', ['accept' => ['application/vnd.api+json']]); + $response->assertStatus(200); + } + + public function testGetEmptyColelction(): void + { + $response = $this->get('/api/books?publicationDate[gt]=9999-12-31', ['accept' => ['application/vnd.api+json']]); + $response->assertStatus(200); + $response->assertJsonFragment([ + 'meta' => [ + 'totalItems' => 0, + 'itemsPerPage' => 5, + 'currentPage' => 1, + ], + ]); + } + + public function testGetBook(): void + { + $book = Book::first(); + $iri = $this->getIriFromResource($book); + $response = $this->get($iri, ['accept' => ['application/vnd.api+json']]); + $response->assertStatus(200); + } + + public function testCreateBook(): void + { + $author = Author::find(1); + $response = $this->postJson( + '/api/books', + [ + 'data' => [ + 'attributes' => [ + 'name' => 'Don Quichotte', + 'isbn' => fake()->isbn13(), + 'publicationDate' => fake()->optional()->date(), + ], + 'relationships' => [ + 'author' => [ + 'data' => [ + 'id' => $this->getIriFromResource($author), + 'type' => 'Author', + ], + ], + ], + ], + ], + [ + 'accept' => 'application/vnd.api+json', + 'content_type' => 'application/vnd.api+json', + ] + ); + + $response->assertStatus(201); + } + + public function testUpdateBook(): void + { + $book = Book::first(); + $iri = $this->getIriFromResource($book); + $response = $this->putJson( + $iri, + [ + 'data' => ['attributes' => ['name' => 'updated title']], + ], + [ + 'accept' => 'application/vnd.api+json', + 'content_type' => 'application/vnd.api+json', + ] + ); + $response->assertStatus(200); + } + + public function testPatchBook(): void + { + $book = Book::first(); + $iri = $this->getIriFromResource($book); + $response = $this->patchJson( + $iri, + [ + 'name' => 'updated title', + ], + [ + 'accept' => 'application/vnd.api+json', + 'content_type' => 'application/merge-patch+json', + ] + ); + $response->assertStatus(200); + } + + public function testDeleteBook(): void + { + $book = Book::first(); + $iri = $this->getIriFromResource($book); + $response = $this->delete($iri, headers: ['accept' => 'application/vnd.api+json']); + $response->assertStatus(204); + $this->assertNull(Book::find($book->id)); + } +} diff --git a/src/Laravel/Tests/Policy/PolicyDenyTest.php b/src/Laravel/Tests/Policy/PolicyDenyTest.php new file mode 100644 index 00000000000..60d6eaa9ed9 --- /dev/null +++ b/src/Laravel/Tests/Policy/PolicyDenyTest.php @@ -0,0 +1,135 @@ + + * + * 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\Laravel\Tests\Policy; + +use ApiPlatform\Laravel\Test\ApiTestAssertionsTrait; +use Illuminate\Contracts\Config\Repository; +use Illuminate\Foundation\Application; +use Illuminate\Foundation\Testing\RefreshDatabase; +use Illuminate\Support\Facades\Gate; +use Orchestra\Testbench\Concerns\WithWorkbench; +use Orchestra\Testbench\TestCase; +use Workbench\App\Models\Author; +use Workbench\App\Models\Book; + +class PolicyDenyTest extends TestCase +{ + use ApiTestAssertionsTrait; + use RefreshDatabase; + use WithWorkbench; + + /** + * @param Application $app + */ + protected function defineEnvironment($app): void + { + Gate::guessPolicyNamesUsing(function (string $modelClass) { + return Book::class === $modelClass ? + BookDenyPolicy::class : + null; + }); + + tap($app['config'], function (Repository $config): void { + $config->set('api-platform.formats', ['jsonapi' => ['application/vnd.api+json']]); + $config->set('api-platform.docs_formats', ['jsonapi' => ['application/vnd.api+json']]); + }); + } + + public function testGetCollection(): void + { + $response = $this->get('/api/books', ['accept' => ['application/vnd.api+json']]); + $response->assertStatus(403); + } + + public function testGetBook(): void + { + $book = Book::first(); + $iri = $this->getIriFromResource($book); + $response = $this->get($iri, ['accept' => ['application/vnd.api+json']]); + $response->assertStatus(403); + } + + public function testCreateBook(): void + { + $author = Author::find(1); + $response = $this->postJson( + '/api/books', + [ + 'data' => [ + 'attributes' => [ + 'name' => 'Don Quichotte', + 'isbn' => fake()->isbn13(), + 'publicationDate' => fake()->optional()->date(), + ], + 'relationships' => [ + 'author' => [ + 'data' => [ + 'id' => $this->getIriFromResource($author), + 'type' => 'Author', + ], + ], + ], + ], + ], + [ + 'accept' => 'application/vnd.api+json', + 'content_type' => 'application/vnd.api+json', + ] + ); + + $response->assertStatus(403); + } + + public function testUpdateBook(): void + { + $book = Book::first(); + $iri = $this->getIriFromResource($book); + $response = $this->putJson( + $iri, + [ + 'data' => ['attributes' => ['name' => 'updated title']], + ], + [ + 'accept' => 'application/vnd.api+json', + 'content_type' => 'application/vnd.api+json', + ] + ); + $response->assertStatus(403); + } + + public function testPatchBook(): void + { + $book = Book::first(); + $iri = $this->getIriFromResource($book); + $response = $this->patchJson( + $iri, + [ + 'name' => 'updated title', + ], + [ + 'accept' => 'application/vnd.api+json', + 'content_type' => 'application/merge-patch+json', + ] + ); + $response->assertStatus(403); + } + + public function testDeleteBook(): void + { + $book = Book::first(); + $iri = $this->getIriFromResource($book); + $response = $this->delete($iri, headers: ['accept' => 'application/vnd.api+json']); + $response->assertStatus(403); + } +} From 6d1e7cf4a7b3cf3263f0727b8b8642763e4dc3a7 Mon Sep 17 00:00:00 2001 From: soyuka Date: Wed, 18 Sep 2024 19:36:46 +0200 Subject: [PATCH 06/20] docs: 4.0.0 changelog --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 91455c45bec..609fe5cc24f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -28,6 +28,7 @@ ### Features +* [00787f32d](https://github.com/api-platform/core/commit/00787f32da54418de7d869cff218e22d8ae2ae1d) feat(laravel): automatically register policies (#6623) * [06a647a80](https://github.com/api-platform/core/commit/06a647a80d4c6b7bfb3474d0685bcb445b56a5a8) feat(laravel): add CSV support (#6617) * [a49bde1ea](https://github.com/api-platform/core/commit/a49bde1ea79ae4226b70c20f9bf967ac77e9ab89) feat(laravel): filter validations rules * [03357fb90](https://github.com/api-platform/core/commit/03357fb90ac0003f0cec2002df01711d0fb99a1e) feat(laravel): supports more Eloquent types (#6544) From bf1d9fd5cb25da90212a8baacaa24ed45cba6642 Mon Sep 17 00:00:00 2001 From: soyuka Date: Thu, 19 Sep 2024 15:43:26 +0200 Subject: [PATCH 07/20] fix!(state): precise format on content-location --- features/main/crud.feature | 9 ++--- features/main/crud_abstract.feature | 8 ++--- features/main/crud_uri_variables.feature | 4 +-- features/main/custom_normalized.feature | 10 +++--- .../main/custom_writable_identifier.feature | 4 +-- features/main/operation_resource.feature | 2 +- features/main/uuid.feature | 6 ++-- src/State/Processor/RespondProcessor.php | 36 +++++++++++++------ 8 files changed, 48 insertions(+), 31 deletions(-) diff --git a/features/main/crud.feature b/features/main/crud.feature index 1fc310d63bb..a167ef70e45 100644 --- a/features/main/crud.feature +++ b/features/main/crud.feature @@ -22,7 +22,7 @@ Feature: Create-Retrieve-Update-Delete Then the response status code should be 201 And the response should be in JSON And the header "Content-Type" should be equal to "application/ld+json; charset=utf-8" - And the header "Content-Location" should be equal to "/dummies/1" + And the header "Content-Location" should be equal to "/dummies/1.jsonld" And the header "Location" should be equal to "/dummies/1" And the JSON should be equal to: """ @@ -106,6 +106,7 @@ Feature: Create-Retrieve-Update-Delete Then the response status code should be 200 And the response should be in JSON And the header "Content-Type" should be equal to "application/ld+json; charset=utf-8" + And the header "Content-Location" should be equal to "/dummies.jsonld" And the JSON should be equal to: """ { @@ -513,7 +514,7 @@ Feature: Create-Retrieve-Update-Delete Then the response status code should be 200 And the response should be in JSON And the header "Content-Type" should be equal to "application/ld+json; charset=utf-8" - And the header "Content-Location" should be equal to "/dummies/1" + And the header "Content-Location" should be equal to "/dummies/1.jsonld" And the JSON should be equal to: """ { @@ -571,7 +572,7 @@ Feature: Create-Retrieve-Update-Delete Then the response status code should be 201 And the response should be in JSON And the header "Content-Type" should be equal to "application/ld+json; charset=utf-8" - And the header "Content-Location" should be equal to "/processor_entities/1" + And the header "Content-Location" should be equal to "/processor_entities/1.jsonld" And the header "Location" should be equal to "/processor_entities/1" And the JSON should be equal to: """ @@ -596,7 +597,7 @@ Feature: Create-Retrieve-Update-Delete Then the response status code should be 201 And the response should be in JSON And the header "Content-Type" should be equal to "application/ld+json; charset=utf-8" - And the header "Content-Location" should be equal to "/provider_entities/1" + And the header "Content-Location" should be equal to "/provider_entities/1.jsonld" And the header "Location" should be equal to "/provider_entities/1" And the JSON should be equal to: """ diff --git a/features/main/crud_abstract.feature b/features/main/crud_abstract.feature index 87d6ca2578f..12d99f397eb 100644 --- a/features/main/crud_abstract.feature +++ b/features/main/crud_abstract.feature @@ -16,7 +16,7 @@ Feature: Create-Retrieve-Update-Delete on abstract resource Then the response status code should be 201 And the response should be in JSON And the header "Content-Type" should be equal to "application/ld+json; charset=utf-8" - And the header "Content-Location" should be equal to "/concrete_dummies/1" + And the header "Content-Location" should be equal to "/concrete_dummies/1.jsonld" And the header "Location" should be equal to "/concrete_dummies/1" And the JSON should be equal to: """ @@ -92,7 +92,7 @@ Feature: Create-Retrieve-Update-Delete on abstract resource Then the response status code should be 200 And the response should be in JSON And the header "Content-Type" should be equal to "application/ld+json; charset=utf-8" - And the header "Content-Location" should be equal to "/concrete_dummies/1" + And the header "Content-Location" should be equal to "/concrete_dummies/1.jsonld" And the JSON should be equal to: """ { @@ -118,7 +118,7 @@ Feature: Create-Retrieve-Update-Delete on abstract resource Then the response status code should be 200 And the response should be in JSON And the header "Content-Type" should be equal to "application/ld+json; charset=utf-8" - And the header "Content-Location" should be equal to "/concrete_dummies/1" + And the header "Content-Location" should be equal to "/concrete_dummies/1.jsonld" And the JSON should be equal to: """ { @@ -150,7 +150,7 @@ Feature: Create-Retrieve-Update-Delete on abstract resource Then the response status code should be 201 And the response should be in JSON And the header "Content-Type" should be equal to "application/ld+json; charset=utf-8" - And the header "Content-Location" should be equal to "/concrete_dummies/1" + And the header "Content-Location" should be equal to "/concrete_dummies/1.jsonld" And the header "Location" should be equal to "/concrete_dummies/1" And the JSON should be equal to: """ diff --git a/features/main/crud_uri_variables.feature b/features/main/crud_uri_variables.feature index fb86d203961..68aef4a3173 100644 --- a/features/main/crud_uri_variables.feature +++ b/features/main/crud_uri_variables.feature @@ -13,7 +13,7 @@ Feature: Uri Variables Then the response status code should be 201 And the response should be in JSON And the header "Content-Type" should be equal to "application/ld+json; charset=utf-8" - And the header "Content-Location" should be equal to "/companies/1" + And the header "Content-Location" should be equal to "/companies/1.jsonld" And the header "Location" should be equal to "/companies/1" And the JSON should be equal to: """ @@ -51,7 +51,7 @@ Feature: Uri Variables Then the response status code should be 201 And the response should be in JSON And the header "Content-Type" should be equal to "application/ld+json; charset=utf-8" - And the header "Content-Location" should be equal to "/companies/1/employees/1" + And the header "Content-Location" should be equal to "/companies/1/employees/1.jsonld" And the header "Location" should be equal to "/companies/1/employees/1" And the JSON should be equal to: """ diff --git a/features/main/custom_normalized.feature b/features/main/custom_normalized.feature index 67a668fa0f0..7d1a49f3fed 100644 --- a/features/main/custom_normalized.feature +++ b/features/main/custom_normalized.feature @@ -16,7 +16,7 @@ Feature: Using custom normalized entity Then the response status code should be 201 And the response should be in JSON And the header "Content-Type" should be equal to "application/ld+json; charset=utf-8" - And the header "Content-Location" should be equal to "/custom_normalized_dummies/1" + And the header "Content-Location" should be equal to "/custom_normalized_dummies/1.jsonld" And the header "Location" should be equal to "/custom_normalized_dummies/1" And the JSON should be equal to: """ @@ -43,7 +43,7 @@ Feature: Using custom normalized entity Then the response status code should be 201 And the response should be in JSON And the header "Content-Type" should be equal to "application/json; charset=utf-8" - And the header "Content-Location" should be equal to "/related_normalized_dummies/1" + And the header "Content-Location" should be equal to "/related_normalized_dummies/1.json" And the header "Location" should be equal to "/related_normalized_dummies/1" And the JSON should be equal to: """ @@ -92,7 +92,7 @@ Feature: Using custom normalized entity Then the response status code should be 200 And the response should be in JSON And the header "Content-Type" should be equal to "application/json; charset=utf-8" - And the header "Content-Location" should be equal to "/related_normalized_dummies/1" + And the header "Content-Location" should be equal to "/related_normalized_dummies/1.json" And the JSON should be equal to: """ { @@ -158,7 +158,7 @@ Feature: Using custom normalized entity Then the response status code should be 200 And the response should be in JSON And the header "Content-Type" should be equal to "application/ld+json; charset=utf-8" - And the header "Content-Location" should be equal to "/custom_normalized_dummies/1" + And the header "Content-Location" should be equal to "/custom_normalized_dummies/1.jsonld" And the JSON should be equal to: """ { @@ -182,7 +182,7 @@ Feature: Using custom normalized entity Then the response status code should be 200 And the response should be in JSON And the header "Content-Type" should be equal to "application/ld+json; charset=utf-8" - And the header "Content-Location" should be equal to "/custom_normalized_dummies/1" + And the header "Content-Location" should be equal to "/custom_normalized_dummies/1.jsonld" And the JSON should be equal to: """ { diff --git a/features/main/custom_writable_identifier.feature b/features/main/custom_writable_identifier.feature index d2878f0afc5..097d253b9ad 100644 --- a/features/main/custom_writable_identifier.feature +++ b/features/main/custom_writable_identifier.feature @@ -16,7 +16,7 @@ Feature: Using custom writable identifier on resource Then the response status code should be 201 And the response should be in JSON And the header "Content-Type" should be equal to "application/ld+json; charset=utf-8" - And the header "Content-Location" should be equal to "/custom_writable_identifier_dummies/my_slug" + And the header "Content-Location" should be equal to "/custom_writable_identifier_dummies/my_slug.jsonld" And the header "Location" should be equal to "/custom_writable_identifier_dummies/my_slug" And the JSON should be equal to: """ @@ -81,7 +81,7 @@ Feature: Using custom writable identifier on resource Then the response status code should be 200 And the response should be in JSON And the header "Content-Type" should be equal to "application/ld+json; charset=utf-8" - And the header "Content-Location" should be equal to "/custom_writable_identifier_dummies/slug_modified" + And the header "Content-Location" should be equal to "/custom_writable_identifier_dummies/slug_modified.jsonld" And the JSON should be equal to: """ { diff --git a/features/main/operation_resource.feature b/features/main/operation_resource.feature index 070cb5be9fd..b4bd729fcaf 100644 --- a/features/main/operation_resource.feature +++ b/features/main/operation_resource.feature @@ -53,7 +53,7 @@ Feature: Resource operations Then the response status code should be 200 And the response should be in JSON And the header "Content-Type" should be equal to "application/ld+json; charset=utf-8" - And the header "Content-Location" should be equal to "/operation_resources/1" + And the header "Content-Location" should be equal to "/operation_resources/1.jsonld" And the JSON should be equal to: """ { diff --git a/features/main/uuid.feature b/features/main/uuid.feature index d0634b0bc94..a7506a15bec 100644 --- a/features/main/uuid.feature +++ b/features/main/uuid.feature @@ -16,7 +16,7 @@ Feature: Using uuid identifier on resource Then the response status code should be 201 And the response should be in JSON And the header "Content-Type" should be equal to "application/ld+json; charset=utf-8" - And the header "Content-Location" should be equal to "/uuid_identifier_dummies/41b29566-144b-11e6-a148-3e1d05defe78" + And the header "Content-Location" should be equal to "/uuid_identifier_dummies/41b29566-144b-11e6-a148-3e1d05defe78.jsonld" And the header "Location" should be equal to "/uuid_identifier_dummies/41b29566-144b-11e6-a148-3e1d05defe78" Scenario: Get a resource @@ -69,7 +69,7 @@ Feature: Using uuid identifier on resource Then the response status code should be 200 And the response should be in JSON And the header "Content-Type" should be equal to "application/ld+json; charset=utf-8" - And the header "Content-Location" should be equal to "/uuid_identifier_dummies/41b29566-144b-11e6-a148-3e1d05defe78" + And the header "Content-Location" should be equal to "/uuid_identifier_dummies/41b29566-144b-11e6-a148-3e1d05defe78.jsonld" And the JSON should be equal to: """ { @@ -90,7 +90,7 @@ Feature: Using uuid identifier on resource Then the response status code should be 201 And the response should be in JSON And the header "Content-Type" should be equal to "application/ld+json; charset=utf-8" - And the header "Content-Location" should be equal to "/custom_generated_identifiers/foo" + And the header "Content-Location" should be equal to "/custom_generated_identifiers/foo.jsonld" And the header "Location" should be equal to "/custom_generated_identifiers/foo" And the JSON should be equal to: """ diff --git a/src/State/Processor/RespondProcessor.php b/src/State/Processor/RespondProcessor.php index 15e9c030327..e5a35b4e56c 100644 --- a/src/State/Processor/RespondProcessor.php +++ b/src/State/Processor/RespondProcessor.php @@ -14,6 +14,9 @@ namespace ApiPlatform\State\Processor; use ApiPlatform\Metadata\Exception\HttpExceptionInterface; +use ApiPlatform\Metadata\Exception\InvalidArgumentException; +use ApiPlatform\Metadata\Exception\ItemNotFoundException; +use ApiPlatform\Metadata\Exception\RuntimeException; use ApiPlatform\Metadata\HttpOperation; use ApiPlatform\Metadata\IriConverterInterface; use ApiPlatform\Metadata\Operation; @@ -92,18 +95,23 @@ public function process(mixed $data, Operation $operation, array $uriVariables = $hasOutput = \is_array($outputMetadata) && \array_key_exists('class', $outputMetadata) && null !== $outputMetadata['class']; $hasData = !$hasOutput ? false : ($this->resourceClassResolver && $originalData && \is_object($originalData) && $this->resourceClassResolver->isResourceClass($this->getObjectClass($originalData))); - if ($hasData && $this->iriConverter) { + if ($hasData) { + $isAlternateResourceMetadata = $operation->getExtraProperties()['is_alternate_resource_metadata'] ?? false; + $canonicalUriTemplate = $operation->getExtraProperties()['canonical_uri_template'] ?? null; + if ( !isset($headers['Location']) && 300 <= $status && $status < 400 - && (($operation->getExtraProperties()['is_alternate_resource_metadata'] ?? false) || ($operation->getExtraProperties()['canonical_uri_template'] ?? null)) + && ($isAlternateResourceMetadata || $canonicalUriTemplate) ) { $canonicalOperation = $operation; - if ($this->operationMetadataFactory && null !== ($operation->getExtraProperties()['canonical_uri_template'] ?? null)) { - $canonicalOperation = $this->operationMetadataFactory->create($operation->getExtraProperties()['canonical_uri_template'], $context); + if ($this->operationMetadataFactory && null !== ($canonicalUriTemplate)) { + $canonicalOperation = $this->operationMetadataFactory->create($canonicalUriTemplate, $context); } - $headers['Location'] = $this->iriConverter->getIriFromResource($originalData, UrlGeneratorInterface::ABS_PATH, $canonicalOperation); + if ($this->iriConverter) { + $headers['Location'] = $this->iriConverter->getIriFromResource($originalData, UrlGeneratorInterface::ABS_PATH, $canonicalOperation); + } } elseif ('PUT' === $method && !$request->attributes->get('previous_data') && null === $status && ($operation instanceof Put && ($operation->getAllowCreate() ?? false))) { $status = 201; } @@ -111,12 +119,20 @@ public function process(mixed $data, Operation $operation, array $uriVariables = $status ??= self::METHOD_TO_CODE[$method] ?? 200; - if ($hasData && $this->iriConverter && !isset($headers['Content-Location'])) { - $iri = $this->iriConverter->getIriFromResource($originalData); - $headers['Content-Location'] = $iri; + if ($this->iriConverter && !isset($headers['Content-Location'])) { + try { + if ($hasData) { + $iri = $this->iriConverter->getIriFromResource($originalData); + } else { + $iri = $this->iriConverter->getIriFromResource($operation->getClass(), UrlGeneratorInterface::ABS_PATH, $operation); + } - if ((201 === $status || (300 <= $status && $status < 400)) && 'POST' === $method && !isset($headers['Location'])) { - $headers['Location'] = $iri; + $headers['Content-Location'] = sprintf('%s.%s', $iri, $request->getRequestFormat()); + + if ((201 === $status || (300 <= $status && $status < 400)) && 'POST' === $method && !isset($headers['Location'])) { + $headers['Location'] = $iri; + } + } catch (InvalidArgumentException|ItemNotFoundException|RuntimeException) { } } From fb39884c07d32d290737020e1175dbfed06dabb5 Mon Sep 17 00:00:00 2001 From: Antoine Bluchet Date: Thu, 19 Sep 2024 16:53:32 +0200 Subject: [PATCH 08/20] test: missing provider argument (#6629) --- tests/State/Pagination/TraversablePaginatorTest.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/State/Pagination/TraversablePaginatorTest.php b/tests/State/Pagination/TraversablePaginatorTest.php index 74399c6e781..41751233f09 100644 --- a/tests/State/Pagination/TraversablePaginatorTest.php +++ b/tests/State/Pagination/TraversablePaginatorTest.php @@ -28,7 +28,7 @@ public function testInitialize( float $totalItems, float $lastPage, int $currentItems, - bool $hasNextPage, + bool $hasNextPage = false, ): void { $traversable = new \ArrayIterator($results); @@ -47,7 +47,7 @@ public function testInitialize( public static function initializeProvider(): array { return [ - 'First of three pages of 3 items each' => [[0, 1, 2, 3, 4, 5, 6], 1, 3, 7, 3, 3], + 'First of three pages of 3 items each' => [[0, 1, 2, 3, 4, 5, 6], 1, 3, 7, 3, 3, true], 'Second of two pages of 3 items for the first page and 2 for the second' => [[0, 1, 2, 3, 4], 2, 3, 5, 2, 2], 'Empty results' => [[], 1, 2, 0, 1, 0], '0 items per page' => [[0, 1, 2, 3], 1, 0, 4, 1, 4], From 4a2271670a88c318ab38bd7eb2a1c0b93a5c0ea0 Mon Sep 17 00:00:00 2001 From: valentindrdt Date: Thu, 19 Sep 2024 07:48:28 -0700 Subject: [PATCH 09/20] feat: api-platform/json-hal component (#6621) * feat: add hal support for laravel * feat: quick review * fix: typo & cs-fixer * fix: typo in composer.json * fix: cs-fixer & phpstan * fix: forgot about hal item normalizer, therefore there's no more createbook nor updatebook test as Hal is a readonly format --- src/Hal/Serializer/ObjectNormalizer.php | 1 - src/Hal/composer.json | 62 +++++++++++++ src/Laravel/ApiPlatformProvider.php | 50 ++++++++++- src/Laravel/Tests/HalTest.php | 112 ++++++++++++++++++++++++ src/Laravel/composer.json | 1 + 5 files changed, 221 insertions(+), 5 deletions(-) create mode 100644 src/Hal/composer.json create mode 100644 src/Laravel/Tests/HalTest.php diff --git a/src/Hal/Serializer/ObjectNormalizer.php b/src/Hal/Serializer/ObjectNormalizer.php index ea3d7a895b8..f52903b877f 100644 --- a/src/Hal/Serializer/ObjectNormalizer.php +++ b/src/Hal/Serializer/ObjectNormalizer.php @@ -17,7 +17,6 @@ use Symfony\Component\Serializer\Exception\LogicException; use Symfony\Component\Serializer\Normalizer\DenormalizerInterface; use Symfony\Component\Serializer\Normalizer\NormalizerInterface; -use Symfony\Component\Serializer\Serializer; /** * Decorates the output with JSON HAL metadata when appropriate, but otherwise diff --git a/src/Hal/composer.json b/src/Hal/composer.json new file mode 100644 index 00000000000..df27693cd26 --- /dev/null +++ b/src/Hal/composer.json @@ -0,0 +1,62 @@ +{ + "name": "api-platform/json-hal", + "description": "API Hal support", + "type": "library", + "keywords": [ + "REST", + "API", + "HAL" + ], + "homepage": "https://api-platform.com", + "license": "MIT", + "authors": [ + { + "name": "Kévin Dunglas", + "email": "kevin@dunglas.fr", + "homepage": "https://dunglas.fr" + }, + { + "name": "API Platform Community", + "homepage": "https://api-platform.com/community/contributors" + } + ], + "require": { + "php": ">=8.1", + "api-platform/state": "^3.4 || ^4.0", + "api-platform/metadata": "^3.4 || ^4.0", + "api-platform/serializer": "^3.4 || ^4.0" + }, + "autoload": { + "psr-4": { + "ApiPlatform\\Hal\\": "" + }, + "exclude-from-classmap": [ + "/Tests/" + ] + }, + "config": { + "preferred-install": { + "*": "dist" + }, + "sort-packages": true, + "allow-plugins": { + "composer/package-versions-deprecated": true, + "phpstan/extension-installer": true + } + }, + "extra": { + "branch-alias": { + "dev-main": "4.0.x-dev", + "dev-3.4": "3.4.x-dev" + }, + "symfony": { + "require": "^6.4 || ^7.1" + } + }, + "scripts": { + "test": "./vendor/bin/phpunit" + }, + "require-dev": { + "phpunit/phpunit": "^11.2" + } +} diff --git a/src/Laravel/ApiPlatformProvider.php b/src/Laravel/ApiPlatformProvider.php index 9a53504e324..d11885ab54f 100644 --- a/src/Laravel/ApiPlatformProvider.php +++ b/src/Laravel/ApiPlatformProvider.php @@ -43,6 +43,10 @@ use ApiPlatform\GraphQl\Type\TypesContainerInterface; use ApiPlatform\GraphQl\Type\TypesFactory; use ApiPlatform\GraphQl\Type\TypesFactoryInterface; +use ApiPlatform\Hal\Serializer\CollectionNormalizer as HalCollectionNormalizer; +use ApiPlatform\Hal\Serializer\EntrypointNormalizer as HalEntrypointNormalizer; +use ApiPlatform\Hal\Serializer\ItemNormalizer as HalItemNormalizer; +use ApiPlatform\Hal\Serializer\ObjectNormalizer as HalObjectNormalizer; use ApiPlatform\Hydra\JsonSchema\SchemaFactory as HydraSchemaFactory; use ApiPlatform\Hydra\Serializer\CollectionFiltersNormalizer as HydraCollectionFiltersNormalizer; use ApiPlatform\Hydra\Serializer\CollectionNormalizer as HydraCollectionNormalizer; @@ -660,6 +664,43 @@ public function register(): void ); }); + $this->app->singleton(HalCollectionNormalizer::class, function (Application $app) { + /** @var ConfigRepository */ + $config = $app['config']; + + return new HalCollectionNormalizer( + $app->make(ResourceClassResolverInterface::class), + $config->get('api-platform.pagination.page_parameter_name'), + $app->make(ResourceMetadataCollectionFactoryInterface::class), + ); + }); + + $this->app->singleton(HalObjectNormalizer::class, function (Application $app) { + return new HalObjectNormalizer( + $app->make(ObjectNormalizer::class), + $app->make(IriConverterInterface::class) + ); + }); + + $this->app->singleton(HalItemNormalizer::class, function (Application $app) { + /** @var ConfigRepository */ + $config = $app['config']; + $defaultContext = $config->get('api-platform.serializer', []); + + return new HalItemNormalizer( + $app->make(PropertyNameCollectionFactoryInterface::class), + $app->make(PropertyMetadataFactoryInterface::class), + $app->make(IriConverterInterface::class), + $app->make(ResourceClassResolverInterface::class), + $app->make(PropertyAccessorInterface::class), + $app->make(NameConverterInterface::class), + $app->make(ClassMetadataFactoryInterface::class), + $defaultContext, + $app->make(ResourceMetadataCollectionFactoryInterface::class), + $app->make(ResourceAccessCheckerInterface::class), + ); + }); + $this->app->singleton(Options::class, function (Application $app) { /** @var ConfigRepository */ $config = $app['config']; @@ -922,6 +963,10 @@ public function register(): void $list = new \SplPriorityQueue(); $list->insert($app->make(HydraEntrypointNormalizer::class), -800); $list->insert($app->make(HydraPartialCollectionViewNormalizer::class), -800); + $list->insert($app->make(HalCollectionNormalizer::class), -800); + $list->insert($app->make(HalEntrypointNormalizer::class), -985); + $list->insert($app->make(HalObjectNormalizer::class), -995); + $list->insert($app->make(HalItemNormalizer::class), -890); $list->insert($app->make(JsonLdItemNormalizer::class), -890); $list->insert($app->make(JsonLdObjectNormalizer::class), -995); $list->insert($app->make(ArrayDenormalizer::class), -990); @@ -950,10 +995,6 @@ public function register(): void // TODO: unused + implement hal/jsonapi ? // $list->insert($dataUriNormalizer, -920); // $list->insert($unwrappingDenormalizer, 1000); - // $list->insert($halItemNormalizer, -890); - // $list->insert($halEntrypointNormalizer, -800); - // $list->insert($halCollectionNormalizer, -985); - // $list->insert($halObjectNormalizer, -995); // $list->insert($jsonserializableNormalizer, -900); // $list->insert($uuidDenormalizer, -895); //Todo ramsey uuid support ? @@ -964,6 +1005,7 @@ public function register(): void $app->make(JsonEncoder::class), new JsonEncoder('jsonopenapi'), new JsonEncoder('jsonapi'), + new JsonEncoder('jsonhal'), new CsvEncoder(), ]); }); diff --git a/src/Laravel/Tests/HalTest.php b/src/Laravel/Tests/HalTest.php new file mode 100644 index 00000000000..4fc11f40461 --- /dev/null +++ b/src/Laravel/Tests/HalTest.php @@ -0,0 +1,112 @@ + + * + * 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\Laravel\Tests; + +use ApiPlatform\Laravel\Test\ApiTestAssertionsTrait; +use Illuminate\Contracts\Config\Repository; +use Illuminate\Foundation\Application; +use Illuminate\Foundation\Testing\RefreshDatabase; +use Orchestra\Testbench\Concerns\WithWorkbench; +use Orchestra\Testbench\TestCase; +use Workbench\App\Models\Book; + +class HalTest extends TestCase +{ + use ApiTestAssertionsTrait; + use RefreshDatabase; + use WithWorkbench; + + /** + * @param Application $app + */ + protected function defineEnvironment($app): void + { + tap($app['config'], function (Repository $config): void { + $config->set('api-platform.formats', ['jsonhal' => ['application/hal+json']]); + $config->set('api-platform.docs_formats', ['jsonhal' => ['application/hal+json']]); + }); + } + + public function testGetEntrypoint(): void + { + $response = $this->get('/api/', ['accept' => ['application/hal+json']]); + $response->assertStatus(200); + $response->assertHeader('content-type', 'application/hal+json; charset=utf-8'); + + $this->assertJsonContains( + [ + '_links' => [ + 'self' => ['href' => '/api'], + 'book' => ['href' => '/api/books'], + 'post' => ['href' => '/api/posts'], + 'sluggable' => ['href' => '/api/sluggables'], + 'vault' => ['href' => '/api/vaults'], + 'author' => ['href' => '/api/authors'], + ], + ], + $response->json() + ); + } + + public function testGetCollection(): void + { + $response = $this->get('/api/books', ['accept' => 'application/hal+json']); + $response->assertStatus(200); + $response->assertHeader('content-type', 'application/hal+json; charset=utf-8'); + $this->assertJsonContains( + [ + '_links' => [ + 'first' => ['href' => '/api/books?page=1'], + 'self' => ['href' => '/api/books?page=1'], + 'last' => ['href' => '/api/books?page=2'], + ], + 'totalItems' => 10, + ], + $response->json() + ); + } + + public function testGetBook(): void + { + $book = Book::first(); + $iri = $this->getIriFromResource($book); + $response = $this->get($iri, ['accept' => ['application/hal+json']]); + $response->assertStatus(200); + $response->assertHeader('content-type', 'application/hal+json; charset=utf-8'); + $this->assertJsonContains( + [ + 'name' => $book->name, // @phpstan-ignore-line + 'isbn' => $book->isbn, // @phpstan-ignore-line + '_links' => [ + 'self' => [ + 'href' => $iri, + ], + 'author' => [ + 'href' => '/api/authors/1', + ], + ], + ], + $response->json() + ); + } + + public function testDeleteBook(): void + { + $book = Book::first(); + $iri = $this->getIriFromResource($book); + $response = $this->delete($iri, headers: ['accept' => 'application/hal+json']); + $response->assertStatus(204); + $this->assertNull(Book::find($book->id)); + } +} diff --git a/src/Laravel/composer.json b/src/Laravel/composer.json index 99f9cd65816..ce2378f9c0a 100644 --- a/src/Laravel/composer.json +++ b/src/Laravel/composer.json @@ -30,6 +30,7 @@ "php": ">=8.1", "api-platform/documentation": "^4.0", "api-platform/hydra": "^4.0", + "api-platform/json-hal": "^4.0", "api-platform/json-schema": "^4.0", "api-platform/jsonld": "^4.0", "api-platform/json-api": "^4.0", From eb80a1a5651b81cab13b018662a0d21e05facbfe Mon Sep 17 00:00:00 2001 From: Antoine Bluchet Date: Thu, 19 Sep 2024 17:27:30 +0200 Subject: [PATCH 10/20] fix!(state): precise format on content-location (#6627) --- features/hydra/collection.feature | 1 + src/State/Processor/RespondProcessor.php | 15 +++++++++++---- 2 files changed, 12 insertions(+), 4 deletions(-) diff --git a/features/hydra/collection.feature b/features/hydra/collection.feature index 2ba5de7f1b3..0f29bf4c711 100644 --- a/features/hydra/collection.feature +++ b/features/hydra/collection.feature @@ -77,6 +77,7 @@ Feature: Collections support When I send a "GET" request to "/dummies?page=7" Then the response status code should be 200 And the response should be in JSON + And the header "Content-Location" should be equal to "/dummies.jsonld?page=7" And the header "Content-Type" should be equal to "application/ld+json; charset=utf-8" And the JSON should be valid according to this schema: """ diff --git a/src/State/Processor/RespondProcessor.php b/src/State/Processor/RespondProcessor.php index e5a35b4e56c..2bb9b13b802 100644 --- a/src/State/Processor/RespondProcessor.php +++ b/src/State/Processor/RespondProcessor.php @@ -105,7 +105,7 @@ public function process(mixed $data, Operation $operation, array $uriVariables = && ($isAlternateResourceMetadata || $canonicalUriTemplate) ) { $canonicalOperation = $operation; - if ($this->operationMetadataFactory && null !== ($canonicalUriTemplate)) { + if ($this->operationMetadataFactory && null !== $canonicalUriTemplate) { $canonicalOperation = $this->operationMetadataFactory->create($canonicalUriTemplate, $context); } @@ -119,6 +119,7 @@ public function process(mixed $data, Operation $operation, array $uriVariables = $status ??= self::METHOD_TO_CODE[$method] ?? 200; + $requestParts = parse_url($request->getRequestUri()); if ($this->iriConverter && !isset($headers['Content-Location'])) { try { if ($hasData) { @@ -127,10 +128,16 @@ public function process(mixed $data, Operation $operation, array $uriVariables = $iri = $this->iriConverter->getIriFromResource($operation->getClass(), UrlGeneratorInterface::ABS_PATH, $operation); } - $headers['Content-Location'] = sprintf('%s.%s', $iri, $request->getRequestFormat()); + if ($iri) { + $location = \sprintf('%s.%s', $iri, $request->getRequestFormat()); + if (isset($requestParts['query'])) { + $location .= '?'.$requestParts['query']; + } - if ((201 === $status || (300 <= $status && $status < 400)) && 'POST' === $method && !isset($headers['Location'])) { - $headers['Location'] = $iri; + $headers['Content-Location'] = $location; + if ((201 === $status || (300 <= $status && $status < 400)) && 'POST' === $method && !isset($headers['Location'])) { + $headers['Location'] = $iri; + } } } catch (InvalidArgumentException|ItemNotFoundException|RuntimeException) { } From 608d2e5b51eedde4d5b7883b62bedd5684ebbf08 Mon Sep 17 00:00:00 2001 From: soyuka Date: Thu, 19 Sep 2024 17:32:43 +0200 Subject: [PATCH 11/20] test: missing condition --- src/State/Processor/RespondProcessor.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/State/Processor/RespondProcessor.php b/src/State/Processor/RespondProcessor.php index 2bb9b13b802..3d025071b5c 100644 --- a/src/State/Processor/RespondProcessor.php +++ b/src/State/Processor/RespondProcessor.php @@ -124,7 +124,7 @@ public function process(mixed $data, Operation $operation, array $uriVariables = try { if ($hasData) { $iri = $this->iriConverter->getIriFromResource($originalData); - } else { + } elseif ($operation->getClass()) { $iri = $this->iriConverter->getIriFromResource($operation->getClass(), UrlGeneratorInterface::ABS_PATH, $operation); } From 93903ff3e4249db28dadf5958b02d4c962e8e978 Mon Sep 17 00:00:00 2001 From: soyuka Date: Thu, 19 Sep 2024 17:33:27 +0200 Subject: [PATCH 12/20] cs: missing variable declaration --- src/State/Processor/RespondProcessor.php | 1 + 1 file changed, 1 insertion(+) diff --git a/src/State/Processor/RespondProcessor.php b/src/State/Processor/RespondProcessor.php index 3d025071b5c..64b07308d3f 100644 --- a/src/State/Processor/RespondProcessor.php +++ b/src/State/Processor/RespondProcessor.php @@ -122,6 +122,7 @@ public function process(mixed $data, Operation $operation, array $uriVariables = $requestParts = parse_url($request->getRequestUri()); if ($this->iriConverter && !isset($headers['Content-Location'])) { try { + $iri = null; if ($hasData) { $iri = $this->iriConverter->getIriFromResource($originalData); } elseif ($operation->getClass()) { From 67d15644956d4a5537fd4b04d0e69991bd2db98a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?K=C3=A9vin=20Dunglas?= Date: Thu, 19 Sep 2024 17:56:07 +0200 Subject: [PATCH 13/20] chore: setup star forwarding (#6630) --- composer.json | 4 ++++ docs/composer.json | 6 ++++++ src/Doctrine/Common/composer.json | 13 +++++++++---- src/Doctrine/Odm/composer.json | 9 ++++++++- src/Doctrine/Orm/composer.json | 9 ++++++++- src/Documentation/composer.json | 4 ++++ src/Elasticsearch/composer.json | 10 ++++++++-- src/GraphQl/composer.json | 4 ++++ src/HttpCache/composer.json | 10 ++++++++-- src/Hydra/composer.json | 9 +++++---- src/JsonApi/composer.json | 12 +++++------- src/JsonLd/composer.json | 10 +++++----- src/JsonSchema/composer.json | 4 ++++ src/Metadata/composer.json | 4 ++++ src/OpenApi/composer.json | 8 ++++++-- src/ParameterValidator/composer.json | 4 ++++ src/RamseyUuid/composer.json | 8 +++++++- src/Serializer/composer.json | 10 ++++++++-- src/State/composer.json | 4 ++++ src/Symfony/composer.json | 11 +++++++++-- src/Validator/composer.json | 10 ++++++++-- 21 files changed, 128 insertions(+), 35 deletions(-) diff --git a/composer.json b/composer.json index 36cae81ebf1..c5b816e8d2a 100644 --- a/composer.json +++ b/composer.json @@ -183,6 +183,10 @@ }, "pmu": { "projects": ["./src/*/composer.json", "src/Doctrine/*/composer.json"] + }, + "thanks": { + "name": "api-platform/api-platform", + "url": "https://github.com/api-platform/api-platform" } } } diff --git a/docs/composer.json b/docs/composer.json index 941dde8f54e..58f45c2fc20 100644 --- a/docs/composer.json +++ b/docs/composer.json @@ -44,5 +44,11 @@ }, "require-dev": { "phpunit/phpunit": "^10" + }, + "extra": { + "thanks": { + "name": "api-platform/api-platform", + "url": "https://github.com/api-platform/api-platform" + } } } diff --git a/src/Doctrine/Common/composer.json b/src/Doctrine/Common/composer.json index 95c604baf03..6abfecea8fc 100644 --- a/src/Doctrine/Common/composer.json +++ b/src/Doctrine/Common/composer.json @@ -3,10 +3,11 @@ "description": "Common files used by api-platform/doctrine-orm and api-platform/doctrine-odm", "type": "library", "keywords": [ - "DOCTRINE", - "ORM", - "ODM", - "COMMON" + "doctrine", + "orm", + "odm", + "REST", + "GraphQL" ], "homepage": "https://api-platform.com", "license": "MIT", @@ -66,6 +67,10 @@ }, "symfony": { "require": "^6.4 || ^7.1" + }, + "thanks": { + "name": "api-platform/api-platform", + "url": "https://github.com/api-platform/api-platform" } }, "scripts": { diff --git a/src/Doctrine/Odm/composer.json b/src/Doctrine/Odm/composer.json index 368403e10db..3caf6bc136c 100644 --- a/src/Doctrine/Odm/composer.json +++ b/src/Doctrine/Odm/composer.json @@ -5,7 +5,10 @@ "keywords": [ "Doctrine", "ODM", - "MongoDB" + "MongoDB", + "API", + "REST", + "GraphQL" ], "homepage": "https://api-platform.com", "license": "MIT", @@ -64,6 +67,10 @@ }, "symfony": { "require": "^6.4 || ^7.1" + }, + "thanks": { + "name": "api-platform/api-platform", + "url": "https://github.com/api-platform/api-platform" } }, "scripts": { diff --git a/src/Doctrine/Orm/composer.json b/src/Doctrine/Orm/composer.json index 85a0ed300b2..5fda971d2ff 100644 --- a/src/Doctrine/Orm/composer.json +++ b/src/Doctrine/Orm/composer.json @@ -4,7 +4,10 @@ "type": "library", "keywords": [ "Doctrine", - "ORM" + "ORM", + "API", + "REST", + "GraphQL" ], "homepage": "https://api-platform.com", "license": "MIT", @@ -64,6 +67,10 @@ }, "symfony": { "require": "^6.4 || ^7.1" + }, + "thanks": { + "name": "api-platform/api-platform", + "url": "https://github.com/api-platform/api-platform" } }, "scripts": { diff --git a/src/Documentation/composer.json b/src/Documentation/composer.json index fb0a7423468..f1359f6b86f 100644 --- a/src/Documentation/composer.json +++ b/src/Documentation/composer.json @@ -29,6 +29,10 @@ }, "symfony": { "require": "^6.4 || ^7.1" + }, + "thanks": { + "name": "api-platform/api-platform", + "url": "https://github.com/api-platform/api-platform" } } } diff --git a/src/Elasticsearch/composer.json b/src/Elasticsearch/composer.json index 7700551eea3..803258c7075 100644 --- a/src/Elasticsearch/composer.json +++ b/src/Elasticsearch/composer.json @@ -3,8 +3,10 @@ "description": "API Platform Elasticsearch bridge", "type": "library", "keywords": [ - "Filter", - "Elasticsearch", + "REST", + "API", + "filter", + "elasticsearch", "search" ], "homepage": "https://api-platform.com", @@ -64,6 +66,10 @@ }, "symfony": { "require": "^6.4 || ^7.1" + }, + "thanks": { + "name": "api-platform/api-platform", + "url": "https://github.com/api-platform/api-platform" } }, "scripts": { diff --git a/src/GraphQl/composer.json b/src/GraphQl/composer.json index ef82cc041e1..a4373fb154f 100644 --- a/src/GraphQl/composer.json +++ b/src/GraphQl/composer.json @@ -70,6 +70,10 @@ }, "symfony": { "require": "^6.4 || ^7.1" + }, + "thanks": { + "name": "api-platform/api-platform", + "url": "https://github.com/api-platform/api-platform" } }, "scripts": { diff --git a/src/HttpCache/composer.json b/src/HttpCache/composer.json index 59bd1492988..e0369cbfb63 100644 --- a/src/HttpCache/composer.json +++ b/src/HttpCache/composer.json @@ -3,8 +3,10 @@ "description": "API Platform HttpCache component", "type": "library", "keywords": [ - "Cache", - "Http" + "REST", + "API", + "cache", + "HTTP" ], "homepage": "https://api-platform.com", "license": "MIT", @@ -58,6 +60,10 @@ }, "symfony": { "require": "^6.4 || ^7.1" + }, + "thanks": { + "name": "api-platform/api-platform", + "url": "https://github.com/api-platform/api-platform" } }, "scripts": { diff --git a/src/Hydra/composer.json b/src/Hydra/composer.json index 4f7f5bd7aa5..1c04e79818e 100644 --- a/src/Hydra/composer.json +++ b/src/Hydra/composer.json @@ -8,10 +8,7 @@ "API", "JSON-LD", "Hydra", - "JSONAPI", - "OpenAPI", - "HAL", - "Swagger" + "JSONAPI" ], "homepage": "https://api-platform.com", "license": "MIT", @@ -65,6 +62,10 @@ }, "symfony": { "require": "^6.4 || ^7.1" + }, + "thanks": { + "name": "api-platform/api-platform", + "url": "https://github.com/api-platform/api-platform" } }, "scripts": { diff --git a/src/JsonApi/composer.json b/src/JsonApi/composer.json index 4d690461c3a..6ab91ec6571 100644 --- a/src/JsonApi/composer.json +++ b/src/JsonApi/composer.json @@ -4,14 +4,8 @@ "type": "library", "keywords": [ "REST", - "GraphQL", "API", - "JSON-LD", - "Hydra", - "JSONAPI", - "OpenAPI", - "HAL", - "Swagger" + "JSONAPI" ], "homepage": "https://api-platform.com", "license": "MIT", @@ -66,6 +60,10 @@ }, "symfony": { "require": "^6.1" + }, + "thanks": { + "name": "api-platform/api-platform", + "url": "https://github.com/api-platform/api-platform" } }, "scripts": { diff --git a/src/JsonLd/composer.json b/src/JsonLd/composer.json index 57a447c514b..53f6887fbd0 100644 --- a/src/JsonLd/composer.json +++ b/src/JsonLd/composer.json @@ -7,11 +7,7 @@ "GraphQL", "API", "JSON-LD", - "Hydra", - "JSONAPI", - "OpenAPI", - "HAL", - "Swagger" + "Hydra" ], "homepage": "https://api-platform.com", "license": "MIT", @@ -57,6 +53,10 @@ }, "symfony": { "require": "^6.4 || ^7.1" + }, + "thanks": { + "name": "api-platform/api-platform", + "url": "https://github.com/api-platform/api-platform" } }, "scripts": { diff --git a/src/JsonSchema/composer.json b/src/JsonSchema/composer.json index 02d6843a166..dab8560e16e 100644 --- a/src/JsonSchema/composer.json +++ b/src/JsonSchema/composer.json @@ -61,6 +61,10 @@ }, "symfony": { "require": "^6.4 || ^7.1" + }, + "thanks": { + "name": "api-platform/api-platform", + "url": "https://github.com/api-platform/api-platform" } }, "scripts": { diff --git a/src/Metadata/composer.json b/src/Metadata/composer.json index 79d22a4e04e..0c96e8354ed 100644 --- a/src/Metadata/composer.json +++ b/src/Metadata/composer.json @@ -78,6 +78,10 @@ }, "symfony": { "require": "^6.4 || ^7.1" + }, + "thanks": { + "name": "api-platform/api-platform", + "url": "https://github.com/api-platform/api-platform" } }, "scripts": { diff --git a/src/OpenApi/composer.json b/src/OpenApi/composer.json index c455fdfe837..4d7f5351960 100644 --- a/src/OpenApi/composer.json +++ b/src/OpenApi/composer.json @@ -7,11 +7,11 @@ "GraphQL", "API", "JSON-LD", - "Hydra", + "hydra", "JSONAPI", "OpenAPI", "HAL", - "Swagger" + "swagger" ], "homepage": "https://api-platform.com", "license": "MIT", @@ -67,6 +67,10 @@ }, "symfony": { "require": "^6.4 || ^7.1" + }, + "thanks": { + "name": "api-platform/api-platform", + "url": "https://github.com/api-platform/api-platform" } }, "scripts": { diff --git a/src/ParameterValidator/composer.json b/src/ParameterValidator/composer.json index e291254967c..e382da3fb46 100644 --- a/src/ParameterValidator/composer.json +++ b/src/ParameterValidator/composer.json @@ -63,6 +63,10 @@ }, "symfony": { "require": "^6.4 || ^7.1" + }, + "thanks": { + "name": "api-platform/api-platform", + "url": "https://github.com/api-platform/api-platform" } }, "scripts": { diff --git a/src/RamseyUuid/composer.json b/src/RamseyUuid/composer.json index 78c5bdcc0a2..c611997bd4d 100644 --- a/src/RamseyUuid/composer.json +++ b/src/RamseyUuid/composer.json @@ -4,7 +4,9 @@ "type": "library", "keywords": [ "UUID", - "API" + "API", + "REST", + "GraphQL" ], "homepage": "https://api-platform.com", "license": "MIT", @@ -53,6 +55,10 @@ }, "symfony": { "require": "^6.4 || ^7.1" + }, + "thanks": { + "name": "api-platform/api-platform", + "url": "https://github.com/api-platform/api-platform" } }, "scripts": { diff --git a/src/Serializer/composer.json b/src/Serializer/composer.json index 34ab79af792..32a98d6c451 100644 --- a/src/Serializer/composer.json +++ b/src/Serializer/composer.json @@ -3,8 +3,10 @@ "description": "API Platform core Serializer", "type": "library", "keywords": [ - "Serializer", - "API" + "serializer", + "API", + "REST", + "GraphQL" ], "homepage": "https://api-platform.com", "license": "MIT", @@ -65,6 +67,10 @@ }, "symfony": { "require": "^6.4 || ^7.1" + }, + "thanks": { + "name": "api-platform/api-platform", + "url": "https://github.com/api-platform/api-platform" } }, "scripts": { diff --git a/src/State/composer.json b/src/State/composer.json index 46b80f637db..c813028d6fb 100644 --- a/src/State/composer.json +++ b/src/State/composer.json @@ -65,6 +65,10 @@ }, "symfony": { "require": "^6.4 || ^7.1" + }, + "thanks": { + "name": "api-platform/api-platform", + "url": "https://github.com/api-platform/api-platform" } }, "suggest": { diff --git a/src/Symfony/composer.json b/src/Symfony/composer.json index 94c12cdb97f..acbf0d6be92 100644 --- a/src/Symfony/composer.json +++ b/src/Symfony/composer.json @@ -1,9 +1,12 @@ { "name": "api-platform/symfony", "description": "Symfony API Platform integration", - "type": "library", + "type": "symfony-bundle", "keywords": [ - "API" + "API", + "symfony", + "REST", + "GraphQL" ], "homepage": "https://api-platform.com", "license": "MIT", @@ -98,6 +101,10 @@ }, "symfony": { "require": "^6.4 || ^7.1" + }, + "thanks": { + "name": "api-platform/api-platform", + "url": "https://github.com/api-platform/api-platform" } }, "scripts": { diff --git a/src/Validator/composer.json b/src/Validator/composer.json index 185a6f87749..2bd1cd748d2 100644 --- a/src/Validator/composer.json +++ b/src/Validator/composer.json @@ -3,8 +3,10 @@ "description": "API Platform validator component", "type": "library", "keywords": [ - "Validator", - "API" + "validator", + "API", + "REST", + "GraphQL" ], "homepage": "https://api-platform.com", "license": "MIT", @@ -54,6 +56,10 @@ }, "symfony": { "require": "^6.4 || ^7.1" + }, + "thanks": { + "name": "api-platform/api-platform", + "url": "https://github.com/api-platform/api-platform" } }, "scripts": { From 21982bf530e1492744ff84952951dcafb04c4b76 Mon Sep 17 00:00:00 2001 From: Antoine Bluchet Date: Thu, 19 Sep 2024 17:56:20 +0200 Subject: [PATCH 14/20] chore: php version >= 8.2 (#6628) * chore: php version >= 8.2 * down to sf 7.0 * symfony extra require * missing hal constraint --- composer.json | 74 +++++++++++++++---------------- src/Doctrine/Common/composer.json | 4 +- src/Doctrine/Odm/composer.json | 20 ++++----- src/Doctrine/Orm/composer.json | 16 +++---- src/Documentation/composer.json | 3 +- src/Elasticsearch/composer.json | 12 ++--- src/GraphQl/composer.json | 14 +++--- src/Hal/composer.json | 2 +- src/HttpCache/composer.json | 10 ++--- src/Hydra/composer.json | 6 +-- src/JsonApi/composer.json | 8 ++-- src/JsonLd/composer.json | 4 +- src/JsonSchema/composer.json | 8 ++-- src/Laravel/composer.json | 5 ++- src/Metadata/composer.json | 16 +++---- src/OpenApi/composer.json | 10 ++--- src/RamseyUuid/composer.json | 6 +-- src/Serializer/composer.json | 22 ++++----- src/State/composer.json | 4 +- src/Symfony/composer.json | 20 ++++----- src/Validator/composer.json | 8 ++-- 21 files changed, 138 insertions(+), 134 deletions(-) diff --git a/composer.json b/composer.json index cc4ead3ddaf..0ed1b2d4a19 100644 --- a/composer.json +++ b/composer.json @@ -25,18 +25,18 @@ } ], "require": { - "php": ">=8.1", + "php": ">=8.2", "doctrine/inflector": "^1.0 || ^2.0", "psr/cache": "^1.0 || ^2.0 || ^3.0", "psr/container": "^1.0 || ^2.0", "symfony/deprecation-contracts": "^3.1", - "symfony/http-foundation": "^6.4 || ^7.1", - "symfony/http-kernel": "^6.4 || ^7.1", - "symfony/property-access": "^6.4 || ^7.1", - "symfony/property-info": "^6.4 || ^7.1", - "symfony/serializer": "^6.4 || ^7.1", + "symfony/http-foundation": "^6.4 || ^7.0", + "symfony/http-kernel": "^6.4 || ^7.0", + "symfony/property-access": "^6.4 || ^7.0", + "symfony/property-info": "^6.4 || ^7.0", + "symfony/serializer": "^6.4 || ^7.0", "symfony/translation-contracts": "^3.3", - "symfony/web-link": "^6.4 || ^7.1", + "symfony/web-link": "^6.4 || ^7.0", "willdurand/negotiation": "^3.1" }, "require-dev": { @@ -96,36 +96,36 @@ "soyuka/contexts": "^3.3.10", "soyuka/pmu": "^0.0.12", "soyuka/stubs-mongodb": "^1.0", - "symfony/asset": "^6.4 || ^7.1", - "symfony/browser-kit": "^6.4 || ^7.1", - "symfony/cache": "^6.4 || ^7.1", - "symfony/config": "^6.4 || ^7.1", - "symfony/console": "^6.4 || ^7.1", - "symfony/css-selector": "^6.4 || ^7.1", - "symfony/dependency-injection": "^6.4 || ^7.1", - "symfony/doctrine-bridge": "^6.4.2 || ^7.1.2", - "symfony/dom-crawler": "^6.4 || ^7.1", - "symfony/error-handler": "^6.4 || ^7.1", - "symfony/event-dispatcher": "^6.4 || ^7.1", - "symfony/expression-language": "^6.4 || ^7.1", - "symfony/finder": "^6.4 || ^7.1", - "symfony/form": "^6.4 || ^7.1", - "symfony/framework-bundle": "^6.4 || ^7.1", - "symfony/http-client": "^6.4 || ^7.1", - "symfony/intl": "^6.4 || ^7.1", + "symfony/asset": "^6.4 || ^7.0", + "symfony/browser-kit": "^6.4 || ^7.0", + "symfony/cache": "^6.4 || ^7.0", + "symfony/config": "^6.4 || ^7.0", + "symfony/console": "^6.4 || ^7.0", + "symfony/css-selector": "^6.4 || ^7.0", + "symfony/dependency-injection": "^6.4 || ^7.0", + "symfony/doctrine-bridge": "^6.4.2 || ^7.0.2", + "symfony/dom-crawler": "^6.4 || ^7.0", + "symfony/error-handler": "^6.4 || ^7.0", + "symfony/event-dispatcher": "^6.4 || ^7.0", + "symfony/expression-language": "^6.4 || ^7.0", + "symfony/finder": "^6.4 || ^7.0", + "symfony/form": "^6.4 || ^7.0", + "symfony/framework-bundle": "^6.4 || ^7.0", + "symfony/http-client": "^6.4 || ^7.0", + "symfony/intl": "^6.4 || ^7.0", "symfony/maker-bundle": "^1.24", "symfony/mercure-bundle": "*", - "symfony/messenger": "^6.4 || ^7.1", - "symfony/routing": "^6.4 || ^7.1", - "symfony/security-bundle": "^6.4 || ^7.1", - "symfony/security-core": "^6.4 || ^7.1", - "symfony/stopwatch": "^6.4 || ^7.1", - "symfony/string": "^6.4 || ^7.1", - "symfony/twig-bundle": "^6.4 || ^7.1", - "symfony/uid": "^6.4 || ^7.1", - "symfony/validator": "^6.4 || ^7.1", - "symfony/web-profiler-bundle": "^6.4 || ^7.1", - "symfony/yaml": "^6.4 || ^7.1", + "symfony/messenger": "^6.4 || ^7.0", + "symfony/routing": "^6.4 || ^7.0", + "symfony/security-bundle": "^6.4 || ^7.0", + "symfony/security-core": "^6.4 || ^7.0", + "symfony/stopwatch": "^6.4 || ^7.0", + "symfony/string": "^6.4 || ^7.0", + "symfony/twig-bundle": "^6.4 || ^7.0", + "symfony/uid": "^6.4 || ^7.0", + "symfony/validator": "^6.4 || ^7.0", + "symfony/web-profiler-bundle": "^6.4 || ^7.0", + "symfony/yaml": "^6.4 || ^7.0", "twig/twig": "^1.42.3 || ^2.12 || ^3.0", "webonyx/graphql-php": "^15.0" }, @@ -135,7 +135,7 @@ "doctrine/orm": "<2.14.0", "doctrine/mongodb-odm": "<2.4", "doctrine/persistence": "<1.3", - "symfony/framework-bundle": "6.4.6 || 7.1.6", + "symfony/framework-bundle": "6.4.6 || 7.0.6", "symfony/var-exporter": "<6.1.1", "phpunit/phpunit": "<9.5", "phpspec/prophecy": "<1.15" @@ -190,7 +190,7 @@ "dev-main": "4.0.x-dev" }, "symfony": { - "require": "^6.4 || ^7.1" + "require": "^6.4 || ^7.0" }, "pmu": { "projects": ["./src/*/composer.json", "src/Doctrine/*/composer.json"] diff --git a/src/Doctrine/Common/composer.json b/src/Doctrine/Common/composer.json index 1e9e415cd37..da471c175b5 100644 --- a/src/Doctrine/Common/composer.json +++ b/src/Doctrine/Common/composer.json @@ -22,7 +22,7 @@ } ], "require": { - "php": ">=8.1", + "php": ">=8.2", "api-platform/metadata": "^3.4 || ^4.0", "api-platform/state": "^3.4 || ^4.0", "doctrine/collections": "^2.1", @@ -63,7 +63,7 @@ "dev-3.4": "3.4.x-dev" }, "symfony": { - "require": "^6.4 || ^7.1" + "require": "^6.4 || ^7.0" } }, "scripts": { diff --git a/src/Doctrine/Odm/composer.json b/src/Doctrine/Odm/composer.json index 069c5340d8a..4b5ebb5527d 100644 --- a/src/Doctrine/Odm/composer.json +++ b/src/Doctrine/Odm/composer.json @@ -21,25 +21,25 @@ } ], "require": { - "php": ">=8.1", + "php": ">=8.2", "api-platform/doctrine-common": "^3.4 || ^4.0", "api-platform/metadata": "^3.4 || ^4.0", "api-platform/state": "^3.4 || ^4.0", - "doctrine/mongodb-odm": "^2.2", - "doctrine/mongodb-odm-bundle": "^5.0", - "symfony/property-info": "^6.4 || ^7.1" + "doctrine/mongodb-odm": "^2.6", + "doctrine/mongodb-odm-bundle": "^4.0 || ^5.0", + "symfony/property-info": "^6.4 || ^7.0" }, "require-dev": { "doctrine/doctrine-bundle": "^2.11", "phpspec/prophecy-phpunit": "^2.2", "phpunit/phpunit": "^11.2", - "symfony/cache": "^6.4 || ^7.1", - "symfony/framework-bundle": "^6.4 || ^7.1", + "symfony/cache": "^6.4 || ^7.0", + "symfony/framework-bundle": "^6.4 || ^7.0", "symfony/property-access": "^6.4 || ^7.0", "symfony/serializer": "^6.4 || ^7.0", - "symfony/uid": "^6.4 || ^7.1", - "symfony/validator": "^6.4 || ^7.1", - "symfony/yaml": "^6.4 || ^7.1" + "symfony/uid": "^6.4 || ^7.0", + "symfony/validator": "^6.4 || ^7.0", + "symfony/yaml": "^6.4 || ^7.0" }, "autoload": { "psr-4": { @@ -61,7 +61,7 @@ "dev-3.4": "3.4.x-dev" }, "symfony": { - "require": "^6.4 || ^7.1" + "require": "^6.4 || ^7.0" } }, "scripts": { diff --git a/src/Doctrine/Orm/composer.json b/src/Doctrine/Orm/composer.json index f9b962226ee..e734865f929 100644 --- a/src/Doctrine/Orm/composer.json +++ b/src/Doctrine/Orm/composer.json @@ -20,26 +20,26 @@ } ], "require": { - "php": ">=8.1", + "php": ">=8.2", "api-platform/doctrine-common": "^3.4 || ^4.0", "api-platform/metadata": "^3.4 || ^4.0", "api-platform/state": "^3.4 || ^4.0", "doctrine/doctrine-bundle": "^2.11", "doctrine/orm": "^2.17 || ^3.0", - "symfony/property-info": "^6.4 || ^7.1" + "symfony/property-info": "^6.4 || ^7.0" }, "require-dev": { "phpspec/prophecy-phpunit": "^2.2", "phpunit/phpunit": "^11.2", "ramsey/uuid": "^4.0", "ramsey/uuid-doctrine": "^2.0", - "symfony/cache": "^6.4 || ^7.1", - "symfony/framework-bundle": "^6.4 || ^7.1", + "symfony/cache": "^6.4 || ^7.0", + "symfony/framework-bundle": "^6.4 || ^7.0", "symfony/property-access": "^6.4 || ^7.0", "symfony/serializer": "^6.4 || ^7.0", - "symfony/uid": "^6.4 || ^7.1", - "symfony/validator": "^6.4 || ^7.1", - "symfony/yaml": "^6.4 || ^7.1" + "symfony/uid": "^6.4 || ^7.0", + "symfony/validator": "^6.4 || ^7.0", + "symfony/yaml": "^6.4 || ^7.0" }, "autoload": { "psr-4": { @@ -61,7 +61,7 @@ "dev-3.4": "3.4.x-dev" }, "symfony": { - "require": "^6.4 || ^7.1" + "require": "^6.4 || ^7.0" } }, "scripts": { diff --git a/src/Documentation/composer.json b/src/Documentation/composer.json index 9f07e064525..52dbe6e1332 100644 --- a/src/Documentation/composer.json +++ b/src/Documentation/composer.json @@ -20,6 +20,7 @@ } ], "require": { + "php": ">=8.2", "api-platform/metadata": "^3.4 || ^4.0" }, "extra": { @@ -28,7 +29,7 @@ "dev-3.4": "3.4.x-dev" }, "symfony": { - "require": "^6.4 || ^7.1" + "require": "^6.4 || ^7.0" } }, "require-dev": { diff --git a/src/Elasticsearch/composer.json b/src/Elasticsearch/composer.json index ea99dbb472d..170bd45544b 100644 --- a/src/Elasticsearch/composer.json +++ b/src/Elasticsearch/composer.json @@ -21,16 +21,16 @@ } ], "require": { - "php": ">=8.1", + "php": ">=8.2", "api-platform/metadata": "^3.4 || ^4.0", "api-platform/serializer": "^3.4 || ^4.0", "api-platform/state": "^3.4 || ^4.0", - "elasticsearch/elasticsearch": "^8.9", + "elasticsearch/elasticsearch": "^8.4", "symfony/cache": "^6.4 || ^7.0", "symfony/console": "^6.4 || ^7.0", - "symfony/property-access": "^6.4 || ^7.1", - "symfony/property-info": "^6.4 || ^7.1", - "symfony/serializer": "^6.4 || ^7.1", + "symfony/property-access": "^6.4 || ^7.0", + "symfony/property-info": "^6.4 || ^7.0", + "symfony/serializer": "^6.4 || ^7.0", "symfony/uid": "^6.4 || ^7.0" }, "require-dev": { @@ -62,7 +62,7 @@ "dev-3.4": "3.4.x-dev" }, "symfony": { - "require": "^6.4 || ^7.1" + "require": "^6.4 || ^7.0" } }, "scripts": { diff --git a/src/GraphQl/composer.json b/src/GraphQl/composer.json index 2abccd95161..6315c58573c 100644 --- a/src/GraphQl/composer.json +++ b/src/GraphQl/composer.json @@ -20,21 +20,21 @@ } ], "require": { - "php": ">=8.1", + "php": ">=8.2", "api-platform/metadata": "^3.4 || ^4.0", "api-platform/state": "^3.4 || ^4.0", "api-platform/serializer": "^3.4 || ^4.0", - "symfony/property-info": "^6.4 || ^7.1", - "symfony/serializer": "^6.4 || ^7.1", - "webonyx/graphql-php": "^14.0 || ^15.0", + "symfony/property-info": "^6.4 || ^7.0", + "symfony/serializer": "^6.4 || ^7.0", + "webonyx/graphql-php": "^15.0", "willdurand/negotiation": "^3.1" }, "require-dev": { "phpspec/prophecy-phpunit": "^2.2", "api-platform/validator": "^3.4 || ^4.0", - "twig/twig": "^3.7", + "twig/twig": "^1.42.3 || ^2.12 || ^3.0", "symfony/mercure-bundle": "*", - "symfony/routing": "^6.4 || ^7.1", + "symfony/routing": "^6.4 || ^7.0", "phpunit/phpunit": "^11.2" }, "autoload": { @@ -67,7 +67,7 @@ "dev-3.4": "3.4.x-dev" }, "symfony": { - "require": "^6.4 || ^7.1" + "require": "^6.4 || ^7.0" } }, "scripts": { diff --git a/src/Hal/composer.json b/src/Hal/composer.json index df27693cd26..03b4c445da1 100644 --- a/src/Hal/composer.json +++ b/src/Hal/composer.json @@ -50,7 +50,7 @@ "dev-3.4": "3.4.x-dev" }, "symfony": { - "require": "^6.4 || ^7.1" + "require": "^6.4 || ^7.0" } }, "scripts": { diff --git a/src/HttpCache/composer.json b/src/HttpCache/composer.json index 80e65df7201..4ed63d18a0c 100644 --- a/src/HttpCache/composer.json +++ b/src/HttpCache/composer.json @@ -20,16 +20,16 @@ } ], "require": { - "php": ">=8.1", + "php": ">=8.2", "api-platform/metadata": "^3.4 || ^4.0", "api-platform/state": "^3.4 || ^4.0", - "symfony/http-foundation": "^6.4 || ^7.1" + "symfony/http-foundation": "^6.4 || ^7.0" }, "require-dev": { "guzzlehttp/guzzle": "^6.0 || ^7.0", - "symfony/dependency-injection": "^6.4 || ^7.1", + "symfony/dependency-injection": "^6.4 || ^7.0", "phpspec/prophecy-phpunit": "^2.2", - "symfony/http-client": "^6.4 || ^7.1", + "symfony/http-client": "^6.4 || ^7.0", "phpunit/phpunit": "^11.2" }, "autoload": { @@ -56,7 +56,7 @@ "dev-3.4": "3.4.x-dev" }, "symfony": { - "require": "^6.4 || ^7.1" + "require": "^6.4 || ^7.0" } }, "scripts": { diff --git a/src/Hydra/composer.json b/src/Hydra/composer.json index 9e1518af2d7..2e56b027bab 100644 --- a/src/Hydra/composer.json +++ b/src/Hydra/composer.json @@ -27,14 +27,14 @@ } ], "require": { - "php": ">=8.1", + "php": ">=8.2", "api-platform/state": "^3.4 || ^4.0", "api-platform/documentation": "^3.4 || ^4.0", "api-platform/metadata": "^3.4 || ^4.0", "api-platform/jsonld": "^3.4 || ^4.0", "api-platform/json-schema": "^3.4 || ^4.0", "api-platform/serializer": "^3.4 || ^4.0", - "symfony/web-link": "^6.4 || ^7.1" + "symfony/web-link": "^6.4 || ^7.0" }, "require-dev": { "api-platform/doctrine-odm": "^3.4 || ^4.0", @@ -68,7 +68,7 @@ "dev-3.4": "3.4.x-dev" }, "symfony": { - "require": "^6.4 || ^7.1" + "require": "^6.4 || ^7.0" } }, "scripts": { diff --git a/src/JsonApi/composer.json b/src/JsonApi/composer.json index 4d690461c3a..c138c36dff6 100644 --- a/src/JsonApi/composer.json +++ b/src/JsonApi/composer.json @@ -27,14 +27,14 @@ } ], "require": { - "php": ">=8.1", + "php": ">=8.2", "api-platform/documentation": "^3.4 || ^4.0", "api-platform/json-schema": "^3.4 || ^4.0", "api-platform/metadata": "^3.4 || ^4.0", "api-platform/serializer": "^3.4 || ^4.0", "api-platform/state": "^3.4 || ^4.0", - "symfony/error-handler": "^6.4 || ^7.1", - "symfony/http-foundation": "^6.4 || ^7.1" + "symfony/error-handler": "^6.4 || ^7.0", + "symfony/http-foundation": "^6.4 || ^7.0" }, "require-dev": { "phpspec/prophecy": "^1.19", @@ -65,7 +65,7 @@ "dev-3.4": "3.4.x-dev" }, "symfony": { - "require": "^6.1" + "require": "^6.4 || ^7.0" } }, "scripts": { diff --git a/src/JsonLd/composer.json b/src/JsonLd/composer.json index 57ff30a042f..b379e55df13 100644 --- a/src/JsonLd/composer.json +++ b/src/JsonLd/composer.json @@ -27,7 +27,7 @@ } ], "require": { - "php": ">=8.1", + "php": ">=8.2", "api-platform/state": "^3.4 || ^4.0", "api-platform/metadata": "^3.4 || ^4.0", "api-platform/serializer": "^3.4 || ^4.0" @@ -56,7 +56,7 @@ "dev-3.4": "3.4.x-dev" }, "symfony": { - "require": "^6.4 || ^7.1" + "require": "^6.4 || ^7.0" } }, "scripts": { diff --git a/src/JsonSchema/composer.json b/src/JsonSchema/composer.json index 3c7704a8530..92c3d6e0085 100644 --- a/src/JsonSchema/composer.json +++ b/src/JsonSchema/composer.json @@ -24,11 +24,11 @@ } ], "require": { - "php": ">=8.1", + "php": ">=8.2", "api-platform/metadata": "^3.4 || ^4.0", "symfony/console": "^6.4 || ^7.0", - "symfony/property-info": "^6.4 || ^7.1", - "symfony/serializer": "^6.4 || ^7.1", + "symfony/property-info": "^6.4 || ^7.0", + "symfony/serializer": "^6.4 || ^7.0", "symfony/uid": "^6.4 || ^7.0" }, "require-dev": { @@ -59,7 +59,7 @@ "dev-3.4": "3.4.x-dev" }, "symfony": { - "require": "^6.4 || ^7.1" + "require": "^6.4 || ^7.0" } }, "scripts": { diff --git a/src/Laravel/composer.json b/src/Laravel/composer.json index ce2378f9c0a..863d1a6b80e 100644 --- a/src/Laravel/composer.json +++ b/src/Laravel/composer.json @@ -27,7 +27,7 @@ } ], "require": { - "php": ">=8.1", + "php": ">=8.2", "api-platform/documentation": "^4.0", "api-platform/hydra": "^4.0", "api-platform/json-hal": "^4.0", @@ -85,6 +85,9 @@ "branch-alias": { "dev-main": "4.0.x-dev", "dev-3.4": "3.4.x-dev" + }, + "symfony": { + "require": "^6.4 || ^7.1" } }, "autoload-dev": { diff --git a/src/Metadata/composer.json b/src/Metadata/composer.json index fb408810539..63b489512fa 100644 --- a/src/Metadata/composer.json +++ b/src/Metadata/composer.json @@ -27,11 +27,11 @@ } ], "require": { - "php": ">=8.1", + "php": ">=8.2", "doctrine/inflector": "^1.0 || ^2.0", "psr/cache": "^1.0 || ^2.0 || ^3.0", "psr/log": "^1.0 || ^2.0 || ^3.0", - "symfony/property-info": "^6.4 || ^7.1", + "symfony/property-info": "^6.4 || ^7.0", "symfony/string": "^6.4 || ^7.0", "symfony/type-info": "^7.1" }, @@ -39,14 +39,14 @@ "api-platform/json-schema": "^3.4 || ^4.0", "api-platform/openapi": "^3.4 || ^4.0", "api-platform/state": "^3.4 || ^4.0", - "phpspec/prophecy-phpunit": "^2.0", - "phpstan/phpdoc-parser": "^1.16", + "phpspec/prophecy-phpunit": "^2.2", + "phpstan/phpdoc-parser": "^1.13", "phpunit/phpunit": "^11.2", - "symfony/config": "^6.4 || ^7.1", - "symfony/routing": "^6.4 || ^7.1", + "symfony/config": "^6.4 || ^7.0", + "symfony/routing": "^6.4 || ^7.0", "symfony/var-dumper": "^6.4 || ^7.0", "symfony/web-link": "^6.4 || ^7.0", - "symfony/yaml": "^6.4 || ^7.1" + "symfony/yaml": "^6.4 || ^7.0" }, "suggest": { "phpstan/phpdoc-parser": "For PHP documentation support.", @@ -77,7 +77,7 @@ "dev-3.4": "3.4.x-dev" }, "symfony": { - "require": "^6.4 || ^7.1" + "require": "^6.4 || ^7.0" } }, "scripts": { diff --git a/src/OpenApi/composer.json b/src/OpenApi/composer.json index 3ca9cf67951..a8425b049a1 100644 --- a/src/OpenApi/composer.json +++ b/src/OpenApi/composer.json @@ -27,16 +27,16 @@ } ], "require": { - "php": ">=8.1", + "php": ">=8.2", "api-platform/json-schema": "^3.4 || ^4.0", "api-platform/metadata": "^3.4 || ^4.0", "api-platform/state": "^3.4 || ^4.0", "symfony/console": "^6.4 || ^7.0", - "symfony/property-access": "^6.4 || ^7.1", - "symfony/serializer": "^6.4 || ^7.1" + "symfony/property-access": "^6.4 || ^7.0", + "symfony/serializer": "^6.4 || ^7.0" }, "require-dev": { - "phpspec/prophecy-phpunit": "^2.0", + "phpspec/prophecy-phpunit": "^2.2", "phpunit/phpunit": "^11.2", "api-platform/doctrine-common": "^3.4 || ^4.0", "api-platform/doctrine-orm": "^3.4 || ^4.0", @@ -66,7 +66,7 @@ "dev-3.4": "3.4.x-dev" }, "symfony": { - "require": "^6.4 || ^7.1" + "require": "^6.4 || ^7.0" } }, "scripts": { diff --git a/src/RamseyUuid/composer.json b/src/RamseyUuid/composer.json index ba67fa76560..b319084a2d5 100644 --- a/src/RamseyUuid/composer.json +++ b/src/RamseyUuid/composer.json @@ -20,9 +20,9 @@ } ], "require": { - "php": ">=8.1", + "php": ">=8.2", "api-platform/metadata": "^3.4 || ^4.0", - "symfony/serializer": "^6.4 || ^7.1" + "symfony/serializer": "^6.4 || ^7.0" }, "require-dev": { "phpspec/prophecy-phpunit": "^2.2", @@ -51,7 +51,7 @@ "dev-3.4": "3.4.x-dev" }, "symfony": { - "require": "^6.4 || ^7.1" + "require": "^6.4 || ^7.0" } }, "scripts": { diff --git a/src/Serializer/composer.json b/src/Serializer/composer.json index f3fe2df7af9..fb2b33ea950 100644 --- a/src/Serializer/composer.json +++ b/src/Serializer/composer.json @@ -20,26 +20,26 @@ } ], "require": { - "php": ">=8.1", + "php": ">=8.2", "api-platform/metadata": "^3.4 || ^4.0", "api-platform/state": "^3.4 || ^4.0", "doctrine/collections": "^2.1", - "symfony/property-access": "^6.4 || ^7.1", - "symfony/property-info": "^6.4 || ^7.1", - "symfony/serializer": "^6.4 || ^7.1", + "symfony/property-access": "^6.4 || ^7.0", + "symfony/property-info": "^6.4 || ^7.0", + "symfony/serializer": "^6.4 || ^7.0", "symfony/validator": "^6.4 || ^7.0" }, "require-dev": { - "api-platform/openapi": "^3.2 || ^4.0", - "api-platform/json-schema": "^3.2 || ^4.0", + "api-platform/openapi": "^3.4 || ^4.0", + "api-platform/json-schema": "^3.4 || ^4.0", "phpspec/prophecy-phpunit": "^2.2", "symfony/mercure-bundle": "*", "symfony/var-dumper": "^6.4 || ^7.0", "phpunit/phpunit": "^11.2", - "symfony/yaml": "^6.4 || ^7.1", - "api-platform/doctrine-odm": "^3.2 || ^4.0", - "api-platform/doctrine-orm": "^3.2 || ^4.0", - "api-platform/doctrine-common": "^3.2 || ^4.0" + "symfony/yaml": "^6.4 || ^7.0", + "api-platform/doctrine-odm": "^3.4 || ^4.0", + "api-platform/doctrine-orm": "^3.4 || ^4.0", + "api-platform/doctrine-common": "^3.4 || ^4.0" }, "suggest": { "api-platform/doctrine-orm": "To support doctrine ORM state options." @@ -68,7 +68,7 @@ "dev-3.4": "3.4.x-dev" }, "symfony": { - "require": "^6.4 || ^7.1" + "require": "^6.4 || ^7.0" } }, "scripts": { diff --git a/src/State/composer.json b/src/State/composer.json index b6968b9b394..c2a3528948c 100644 --- a/src/State/composer.json +++ b/src/State/composer.json @@ -27,7 +27,7 @@ } ], "require": { - "php": ">=8.1", + "php": ">=8.2", "api-platform/metadata": "^3.4 || ^4.0", "psr/container": "^1.0 || ^2.0", "symfony/http-kernel": "^6.4 || ^7.0" @@ -64,7 +64,7 @@ "dev-3.4": "3.4.x-dev" }, "symfony": { - "require": "^6.4 || ^7.1" + "require": "^6.4 || ^7.0" } }, "suggest": { diff --git a/src/Symfony/composer.json b/src/Symfony/composer.json index 66063a205d2..a4329dffd29 100644 --- a/src/Symfony/composer.json +++ b/src/Symfony/composer.json @@ -28,7 +28,7 @@ } ], "require": { - "php": ">=8.1", + "php": ">=8.2", "api-platform/documentation": "^3.4 || ^4.0", "api-platform/http-cache": "^3.4 || ^4.0", "api-platform/json-schema": "^3.4 || ^4.0", @@ -39,26 +39,26 @@ "api-platform/state": "^3.4 || ^4.0", "api-platform/validator": "^3.4 || ^4.0", "api-platform/openapi": "^3.4 || ^4.0", - "symfony/property-info": "^6.4 || ^7.1", - "symfony/property-access": "^6.4 || ^7.1", - "symfony/serializer": "^6.4 || ^7.1", + "symfony/property-info": "^6.4 || ^7.0", + "symfony/property-access": "^6.4 || ^7.0", + "symfony/serializer": "^6.4 || ^7.0", "symfony/security-core": "^6.4 || ^7.0", - "willdurand/negotiation": "^3.0" + "willdurand/negotiation": "^3.1" }, "require-dev": { "phpspec/prophecy-phpunit": "^2.2", - "symfony/routing": "^6.4 || ^7.1", + "symfony/routing": "^6.4 || ^7.0", "phpunit/phpunit": "^11.2", - "symfony/validator": "^6.4 || ^7.1", + "symfony/validator": "^6.4 || ^7.0", "symfony/mercure-bundle": "*", - "webonyx/graphql-php": "^14.0 || ^15.0", + "webonyx/graphql-php": "^15.0", "api-platform/doctrine-common": "^3.4 || ^4.0", "api-platform/elasticsearch": "^3.4 || ^4.0", "api-platform/graphql": "^3.4 || ^4.0", "api-platform/doctrine-orm": "^3.4 || ^4.0", "api-platform/doctrine-odm": "^3.4 || ^4.0", "api-platform/parameter-validator": "^3.1", - "symfony/expression-language": "^6.4 || ^7.1" + "symfony/expression-language": "^6.4 || ^7.0" }, "suggest": { "api-platform/doctrine-orm": "To support Doctrine ORM.", @@ -105,7 +105,7 @@ "dev-3.4": "3.4.x-dev" }, "symfony": { - "require": "^6.4 || ^7.1" + "require": "^6.4 || ^7.0" } }, "scripts": { diff --git a/src/Validator/composer.json b/src/Validator/composer.json index 3b76f5b0a96..2f61e6f2f96 100644 --- a/src/Validator/composer.json +++ b/src/Validator/composer.json @@ -20,15 +20,15 @@ } ], "require": { - "php": ">=8.1", + "php": ">=8.2", "api-platform/metadata": "^3.4 || ^4.0", - "symfony/web-link": "^6.4 || ^7.1" + "symfony/web-link": "^6.4 || ^7.0" }, "require-dev": { "phpspec/prophecy-phpunit": "^2.2", "symfony/serializer": "^6.4 || ^7.0", "phpunit/phpunit": "^11.2", - "symfony/validator": "^6.4 || ^7.1", + "symfony/validator": "^6.4 || ^7.0", "symfony/http-kernel": "^6.4 || ^7.0" }, "autoload": { @@ -52,7 +52,7 @@ "dev-3.4": "3.4.x-dev" }, "symfony": { - "require": "^6.4 || ^7.1" + "require": "^6.4 || ^7.0" } }, "scripts": { From 1fe014b6c6811fb277d3fbb499ab6fe760aa38c4 Mon Sep 17 00:00:00 2001 From: soyuka Date: Thu, 19 Sep 2024 17:58:51 +0200 Subject: [PATCH 15/20] chore: fix thanks url --- src/Doctrine/Common/composer.json | 4 ++++ src/Doctrine/Odm/composer.json | 4 ++++ src/Doctrine/Orm/composer.json | 4 ++++ src/Documentation/composer.json | 4 ++++ src/Elasticsearch/composer.json | 4 ++++ src/GraphQl/composer.json | 4 ++++ src/Hal/composer.json | 4 ++++ src/HttpCache/composer.json | 4 ++++ src/Hydra/composer.json | 4 ++++ src/JsonApi/composer.json | 4 ++++ src/JsonLd/composer.json | 4 ++++ src/JsonSchema/composer.json | 4 ++++ src/Laravel/composer.json | 4 ++++ src/Metadata/composer.json | 4 ++++ src/OpenApi/composer.json | 4 ++++ src/RamseyUuid/composer.json | 4 ++++ src/Serializer/composer.json | 4 ++++ src/State/composer.json | 4 ++++ src/Symfony/composer.json | 4 ++++ src/Validator/composer.json | 4 ++++ 20 files changed, 80 insertions(+) diff --git a/src/Doctrine/Common/composer.json b/src/Doctrine/Common/composer.json index 72682d1f873..8ecfb1da2a5 100644 --- a/src/Doctrine/Common/composer.json +++ b/src/Doctrine/Common/composer.json @@ -65,6 +65,10 @@ }, "symfony": { "require": "^6.4 || ^7.0" + }, + "thanks": { + "name": "api-platform/api-platform", + "url": "https://github.com/api-platform/api-platform" } }, "scripts": { diff --git a/src/Doctrine/Odm/composer.json b/src/Doctrine/Odm/composer.json index dab4cef6a17..59b1988f834 100644 --- a/src/Doctrine/Odm/composer.json +++ b/src/Doctrine/Odm/composer.json @@ -65,6 +65,10 @@ }, "symfony": { "require": "^6.4 || ^7.0" + }, + "thanks": { + "name": "api-platform/api-platform", + "url": "https://github.com/api-platform/api-platform" } }, "scripts": { diff --git a/src/Doctrine/Orm/composer.json b/src/Doctrine/Orm/composer.json index c16aae77534..ac24c64e8a9 100644 --- a/src/Doctrine/Orm/composer.json +++ b/src/Doctrine/Orm/composer.json @@ -65,6 +65,10 @@ }, "symfony": { "require": "^6.4 || ^7.0" + }, + "thanks": { + "name": "api-platform/api-platform", + "url": "https://github.com/api-platform/api-platform" } }, "scripts": { diff --git a/src/Documentation/composer.json b/src/Documentation/composer.json index 52dbe6e1332..71c570c8c48 100644 --- a/src/Documentation/composer.json +++ b/src/Documentation/composer.json @@ -30,6 +30,10 @@ }, "symfony": { "require": "^6.4 || ^7.0" + }, + "thanks": { + "name": "api-platform/api-platform", + "url": "https://github.com/api-platform/api-platform" } }, "require-dev": { diff --git a/src/Elasticsearch/composer.json b/src/Elasticsearch/composer.json index 0ea30b24813..7d4961afef3 100644 --- a/src/Elasticsearch/composer.json +++ b/src/Elasticsearch/composer.json @@ -65,6 +65,10 @@ }, "symfony": { "require": "^6.4 || ^7.0" + }, + "thanks": { + "name": "api-platform/api-platform", + "url": "https://github.com/api-platform/api-platform" } }, "scripts": { diff --git a/src/GraphQl/composer.json b/src/GraphQl/composer.json index 6315c58573c..f676ddae850 100644 --- a/src/GraphQl/composer.json +++ b/src/GraphQl/composer.json @@ -68,6 +68,10 @@ }, "symfony": { "require": "^6.4 || ^7.0" + }, + "thanks": { + "name": "api-platform/api-platform", + "url": "https://github.com/api-platform/api-platform" } }, "scripts": { diff --git a/src/Hal/composer.json b/src/Hal/composer.json index 03b4c445da1..2da21682e20 100644 --- a/src/Hal/composer.json +++ b/src/Hal/composer.json @@ -51,6 +51,10 @@ }, "symfony": { "require": "^6.4 || ^7.0" + }, + "thanks": { + "name": "api-platform/api-platform", + "url": "https://github.com/api-platform/api-platform" } }, "scripts": { diff --git a/src/HttpCache/composer.json b/src/HttpCache/composer.json index a22acbdbe59..6b42bcabde8 100644 --- a/src/HttpCache/composer.json +++ b/src/HttpCache/composer.json @@ -59,6 +59,10 @@ }, "symfony": { "require": "^6.4 || ^7.0" + }, + "thanks": { + "name": "api-platform/api-platform", + "url": "https://github.com/api-platform/api-platform" } }, "scripts": { diff --git a/src/Hydra/composer.json b/src/Hydra/composer.json index b8645207c13..eece6d0e331 100644 --- a/src/Hydra/composer.json +++ b/src/Hydra/composer.json @@ -66,6 +66,10 @@ }, "symfony": { "require": "^6.4 || ^7.0" + }, + "thanks": { + "name": "api-platform/api-platform", + "url": "https://github.com/api-platform/api-platform" } }, "scripts": { diff --git a/src/JsonApi/composer.json b/src/JsonApi/composer.json index 5e7113d1cbf..60e5fd25bef 100644 --- a/src/JsonApi/composer.json +++ b/src/JsonApi/composer.json @@ -60,6 +60,10 @@ }, "symfony": { "require": "^6.4 || ^7.0" + }, + "thanks": { + "name": "api-platform/api-platform", + "url": "https://github.com/api-platform/api-platform" } }, "scripts": { diff --git a/src/JsonLd/composer.json b/src/JsonLd/composer.json index 32bd4b66c25..d6566f62a41 100644 --- a/src/JsonLd/composer.json +++ b/src/JsonLd/composer.json @@ -53,6 +53,10 @@ }, "symfony": { "require": "^6.4 || ^7.0" + }, + "thanks": { + "name": "api-platform/api-platform", + "url": "https://github.com/api-platform/api-platform" } }, "scripts": { diff --git a/src/JsonSchema/composer.json b/src/JsonSchema/composer.json index 92c3d6e0085..0695729b5eb 100644 --- a/src/JsonSchema/composer.json +++ b/src/JsonSchema/composer.json @@ -60,6 +60,10 @@ }, "symfony": { "require": "^6.4 || ^7.0" + }, + "thanks": { + "name": "api-platform/api-platform", + "url": "https://github.com/api-platform/api-platform" } }, "scripts": { diff --git a/src/Laravel/composer.json b/src/Laravel/composer.json index 863d1a6b80e..eeab8e2bd2c 100644 --- a/src/Laravel/composer.json +++ b/src/Laravel/composer.json @@ -88,6 +88,10 @@ }, "symfony": { "require": "^6.4 || ^7.1" + }, + "thanks": { + "name": "api-platform/api-platform", + "url": "https://github.com/api-platform/api-platform" } }, "autoload-dev": { diff --git a/src/Metadata/composer.json b/src/Metadata/composer.json index 63b489512fa..af7647e2020 100644 --- a/src/Metadata/composer.json +++ b/src/Metadata/composer.json @@ -78,6 +78,10 @@ }, "symfony": { "require": "^6.4 || ^7.0" + }, + "thanks": { + "name": "api-platform/api-platform", + "url": "https://github.com/api-platform/api-platform" } }, "scripts": { diff --git a/src/OpenApi/composer.json b/src/OpenApi/composer.json index 4f3856673ad..3576b855da7 100644 --- a/src/OpenApi/composer.json +++ b/src/OpenApi/composer.json @@ -67,6 +67,10 @@ }, "symfony": { "require": "^6.4 || ^7.0" + }, + "thanks": { + "name": "api-platform/api-platform", + "url": "https://github.com/api-platform/api-platform" } }, "scripts": { diff --git a/src/RamseyUuid/composer.json b/src/RamseyUuid/composer.json index 1db8918f096..5d31ceb4fdf 100644 --- a/src/RamseyUuid/composer.json +++ b/src/RamseyUuid/composer.json @@ -54,6 +54,10 @@ }, "symfony": { "require": "^6.4 || ^7.0" + }, + "thanks": { + "name": "api-platform/api-platform", + "url": "https://github.com/api-platform/api-platform" } }, "scripts": { diff --git a/src/Serializer/composer.json b/src/Serializer/composer.json index dbab982ea19..361fb2eea34 100644 --- a/src/Serializer/composer.json +++ b/src/Serializer/composer.json @@ -71,6 +71,10 @@ }, "symfony": { "require": "^6.4 || ^7.0" + }, + "thanks": { + "name": "api-platform/api-platform", + "url": "https://github.com/api-platform/api-platform" } }, "scripts": { diff --git a/src/State/composer.json b/src/State/composer.json index c2a3528948c..72664396851 100644 --- a/src/State/composer.json +++ b/src/State/composer.json @@ -65,6 +65,10 @@ }, "symfony": { "require": "^6.4 || ^7.0" + }, + "thanks": { + "name": "api-platform/api-platform", + "url": "https://github.com/api-platform/api-platform" } }, "suggest": { diff --git a/src/Symfony/composer.json b/src/Symfony/composer.json index a2216a00bf4..a251115188b 100644 --- a/src/Symfony/composer.json +++ b/src/Symfony/composer.json @@ -106,6 +106,10 @@ }, "symfony": { "require": "^6.4 || ^7.0" + }, + "thanks": { + "name": "api-platform/api-platform", + "url": "https://github.com/api-platform/api-platform" } }, "scripts": { diff --git a/src/Validator/composer.json b/src/Validator/composer.json index e372420f812..a09c071ff18 100644 --- a/src/Validator/composer.json +++ b/src/Validator/composer.json @@ -55,6 +55,10 @@ }, "symfony": { "require": "^6.4 || ^7.0" + }, + "thanks": { + "name": "api-platform/api-platform", + "url": "https://github.com/api-platform/api-platform" } }, "scripts": { From 80fc2d2303f35bccdc6789dc72a205781c3dc3d0 Mon Sep 17 00:00:00 2001 From: soyuka Date: Thu, 19 Sep 2024 18:21:53 +0200 Subject: [PATCH 16/20] doc: changelog 4.0.1 --- CHANGELOG.md | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 609fe5cc24f..564ed77d2a1 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,15 @@ # Changelog +## v4.0.1 + +### Bug fixes + +* [eb80a1a56](https://github.com/api-platform/core/commit/eb80a1a5651b81cab13b018662a0d21e05facbfe) fix(state): precise format on content-location (#6627) + +### Features + +* [4a2271670](https://github.com/api-platform/core/commit/4a2271670a88c318ab38bd7eb2a1c0b93a5c0ea0) feat: api-platform/json-hal component (#6621) + ## v4.0.0 ### Bug fixes From bf376fc848af8654a00f3d8953f9e082858f06da Mon Sep 17 00:00:00 2001 From: Antoine Bluchet Date: Fri, 20 Sep 2024 11:32:54 +0200 Subject: [PATCH 17/20] chore: move dependency to dev requirements (#6634) --- src/Doctrine/Orm/composer.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Doctrine/Orm/composer.json b/src/Doctrine/Orm/composer.json index ac24c64e8a9..9bc1aa9ecf5 100644 --- a/src/Doctrine/Orm/composer.json +++ b/src/Doctrine/Orm/composer.json @@ -27,11 +27,11 @@ "api-platform/doctrine-common": "^3.4 || ^4.0", "api-platform/metadata": "^3.4 || ^4.0", "api-platform/state": "^3.4 || ^4.0", - "doctrine/doctrine-bundle": "^2.11", "doctrine/orm": "^2.17 || ^3.0", "symfony/property-info": "^6.4 || ^7.0" }, "require-dev": { + "doctrine/doctrine-bundle": "^2.11", "phpspec/prophecy-phpunit": "^2.2", "phpunit/phpunit": "^11.2", "ramsey/uuid": "^4.0", From fcb78ea30889ae64bb1ccdf3261c577615274963 Mon Sep 17 00:00:00 2001 From: Antoine Bluchet Date: Fri, 20 Sep 2024 11:34:57 +0200 Subject: [PATCH 18/20] chore: json-hal as dev dependency (#6636) --- composer.json | 1 + 1 file changed, 1 insertion(+) diff --git a/composer.json b/composer.json index 5359316c30b..50fe150e926 100644 --- a/composer.json +++ b/composer.json @@ -49,6 +49,7 @@ "api-platform/openapi": "^3.4 || ^4.0", "api-platform/ramsey-uuid": "^3.4 || ^4.0", "api-platform/jsonld": "^3.4 || ^4.0", + "api-platform/json-hal": "^3.4 || ^4.0", "api-platform/serializer": "^3.4 || ^4.0", "api-platform/json-api": "^3.3 || ^4.0", "api-platform/http-cache": "^3.4 || ^4.0", From c47e2996e51c587c998fde88903703bd6ac9a43c Mon Sep 17 00:00:00 2001 From: soyuka Date: Fri, 20 Sep 2024 10:03:15 +0200 Subject: [PATCH 19/20] fix: default format and standard_put values --- src/Doctrine/Common/State/PersistProcessor.php | 2 +- src/JsonLd/Serializer/ItemNormalizer.php | 2 +- src/State/Provider/DeserializeProvider.php | 2 +- src/Symfony/Bundle/DependencyInjection/Configuration.php | 1 + tests/Fixtures/app/AppKernel.php | 3 --- .../Symfony/Bundle/DependencyInjection/ConfigurationTest.php | 4 +++- 6 files changed, 7 insertions(+), 7 deletions(-) diff --git a/src/Doctrine/Common/State/PersistProcessor.php b/src/Doctrine/Common/State/PersistProcessor.php index 20e93ac4399..abc21bf9e15 100644 --- a/src/Doctrine/Common/State/PersistProcessor.php +++ b/src/Doctrine/Common/State/PersistProcessor.php @@ -48,7 +48,7 @@ public function process(mixed $data, Operation $operation, array $uriVariables = // PUT: reset the existing object managed by Doctrine and merge data sent by the user in it // This custom logic is needed because EntityManager::merge() has been deprecated and UPSERT isn't supported: // https://github.com/doctrine/orm/issues/8461#issuecomment-1250233555 - if ($operation instanceof HttpOperation && 'PUT' === $operation->getMethod() && ($operation->getExtraProperties()['standard_put'] ?? false)) { + if ($operation instanceof HttpOperation && 'PUT' === $operation->getMethod() && ($operation->getExtraProperties()['standard_put'] ?? true)) { \assert(method_exists($manager, 'getReference')); $newData = $data; $identifiers = array_reverse($uriVariables); diff --git a/src/JsonLd/Serializer/ItemNormalizer.php b/src/JsonLd/Serializer/ItemNormalizer.php index 6be47cd3a27..8d6e2aefb27 100644 --- a/src/JsonLd/Serializer/ItemNormalizer.php +++ b/src/JsonLd/Serializer/ItemNormalizer.php @@ -175,7 +175,7 @@ public function denormalize(mixed $data, string $class, ?string $format = null, } catch (ItemNotFoundException $e) { $operation = $context['operation'] ?? null; - if (!('PUT' === $operation?->getMethod() && ($operation->getExtraProperties()['standard_put'] ?? false))) { + if (!('PUT' === $operation?->getMethod() && ($operation->getExtraProperties()['standard_put'] ?? true))) { throw $e; } } diff --git a/src/State/Provider/DeserializeProvider.php b/src/State/Provider/DeserializeProvider.php index 1e8a68310bb..db735e19961 100644 --- a/src/State/Provider/DeserializeProvider.php +++ b/src/State/Provider/DeserializeProvider.php @@ -83,7 +83,7 @@ public function provide(Operation $operation, array $uriVariables = [], array $c && ( 'POST' === $method || 'PATCH' === $method - || ('PUT' === $method && !($operation->getExtraProperties()['standard_put'] ?? false)) + || ('PUT' === $method && !($operation->getExtraProperties()['standard_put'] ?? true)) ) ) { $serializerContext[AbstractNormalizer::OBJECT_TO_POPULATE] = $data; diff --git a/src/Symfony/Bundle/DependencyInjection/Configuration.php b/src/Symfony/Bundle/DependencyInjection/Configuration.php index e073b7c1844..f963fa8c9db 100644 --- a/src/Symfony/Bundle/DependencyInjection/Configuration.php +++ b/src/Symfony/Bundle/DependencyInjection/Configuration.php @@ -164,6 +164,7 @@ public function getConfigTreeBuilder(): TreeBuilder $this->addExceptionToStatusSection($rootNode); $this->addFormatSection($rootNode, 'formats', [ + 'jsonld' => ['mime_types' => ['application/ld+json']] ]); $this->addFormatSection($rootNode, 'patch_formats', [ 'json' => ['mime_types' => ['application/merge-patch+json']], diff --git a/tests/Fixtures/app/AppKernel.php b/tests/Fixtures/app/AppKernel.php index c69826188b5..7d55824f4de 100644 --- a/tests/Fixtures/app/AppKernel.php +++ b/tests/Fixtures/app/AppKernel.php @@ -265,9 +265,6 @@ class_exists(NativePasswordHasher::class) ? 'password_hashers' : 'encoders' => [ 'public' => true, ], 'normalization_context' => ['skip_null_values' => false], - 'extra_properties' => [ - 'standard_put' => true, - ], 'operations' => [ Get::class, GetCollection::class, diff --git a/tests/Symfony/Bundle/DependencyInjection/ConfigurationTest.php b/tests/Symfony/Bundle/DependencyInjection/ConfigurationTest.php index c2f322e5f12..84a1ed5066c 100644 --- a/tests/Symfony/Bundle/DependencyInjection/ConfigurationTest.php +++ b/tests/Symfony/Bundle/DependencyInjection/ConfigurationTest.php @@ -75,7 +75,9 @@ private function runDefaultConfigTests(array $doctrineIntegrationsToLoad = ['orm 'description' => 'description', 'version' => '1.0.0', 'show_webby' => true, - 'formats' => [], + 'formats' => [ + 'jsonld' => ['mime_types' => ['application/ld+json']], + ], 'docs_formats' => [ 'jsonopenapi' => ['mime_types' => ['application/vnd.openapi+json']], 'yamlopenapi' => ['mime_types' => ['application/vnd.openapi+yaml']], From 93314b08de1e6f0505af9e3a3ba3d9971f1ef09c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Micha=C5=82=20Bundyra?= Date: Fri, 20 Sep 2024 11:08:11 +0100 Subject: [PATCH 20/20] fix(serializer): allow state's SerializerFilterContextBuilderInterface (#6632) * fix: Update SerializerFilterContextBuilder to accept non-deprecated SerializerContextBuilderInterface * Update src/Serializer/SerializerFilterContextBuilder.php --------- Co-authored-by: Antoine Bluchet --- src/Serializer/SerializerFilterContextBuilder.php | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/Serializer/SerializerFilterContextBuilder.php b/src/Serializer/SerializerFilterContextBuilder.php index 7c64b9947b9..9073bbe1136 100644 --- a/src/Serializer/SerializerFilterContextBuilder.php +++ b/src/Serializer/SerializerFilterContextBuilder.php @@ -17,6 +17,7 @@ use ApiPlatform\Metadata\Resource\Factory\ResourceMetadataCollectionFactoryInterface; use ApiPlatform\Metadata\Util\AttributesExtractor; use ApiPlatform\Serializer\Filter\FilterInterface; +use ApiPlatform\State\SerializerContextBuilderInterface as StateSerializerContextBuilderInterface; use Psr\Container\ContainerInterface; use Symfony\Component\HttpFoundation\Request; @@ -27,7 +28,7 @@ */ final class SerializerFilterContextBuilder implements SerializerContextBuilderInterface { - public function __construct(private readonly ResourceMetadataCollectionFactoryInterface $resourceMetadataCollectionFactory, private readonly ContainerInterface $filterLocator, private readonly SerializerContextBuilderInterface $decorated) + public function __construct(private readonly ResourceMetadataCollectionFactoryInterface $resourceMetadataCollectionFactory, private readonly ContainerInterface $filterLocator, private readonly SerializerContextBuilderInterface|StateSerializerContextBuilderInterface $decorated) { }