From 67a34d8cf6d9b20f9cb7b86d884f531ee7c7202b Mon Sep 17 00:00:00 2001 From: turegjorup Date: Sun, 8 Nov 2026 21:36:45 +0100 Subject: [PATCH 1/3] chore: update API Platform to 4.4 API Platform 4.4 exports OpenAPI 3.2.0 and no longer adds charset to the application/ld+json content type (api-platform/core#8226). Update the content-type assertions in the tests. Vocabulary.tags has no element type, so the spec now has no item type for it. --- CHANGELOG.md | 2 + composer.json | 2 +- composer.lock | 94 +++++++++---------- config/reference.php | 10 +- public/spec.yaml | 7 +- .../ContentNegotiationContractTest.php | 2 +- .../Trait/GetEntitiesTestTrait.php | 2 +- tests/ApiPlatform/Trait/GetItemTestTrait.php | 2 +- 8 files changed, 62 insertions(+), 59 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index ab21e58..f003c8c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -8,6 +8,8 @@ See [keep a changelog] for information about writing changes to this log. ## [Unreleased] +- [PR-XX](https://github.com/itk-dev/event-database-api/pull/XX) + Update API Platform to 4.4 (OpenAPI 3.2.0, no `charset` in the `application/ld+json` content type) - [PR-75](https://github.com/itk-dev/event-database-api/pull/75) Update Rector (2.8) and PHPStan (2.3) and apply the new Rector rules - [PR-74](https://github.com/itk-dev/event-database-api/pull/74) diff --git a/composer.json b/composer.json index ade9b18..390b0d0 100644 --- a/composer.json +++ b/composer.json @@ -7,7 +7,7 @@ "php": ">=8.5", "ext-ctype": "*", "ext-iconv": "*", - "api-platform/core": "~4.3.0", + "api-platform/core": "~4.4.0", "elasticsearch/elasticsearch": "^8.19", "nelmio/cors-bundle": "^2.4", "phpdocumentor/reflection-docblock": "^5.6", diff --git a/composer.lock b/composer.lock index f9b290f..f60651e 100644 --- a/composer.lock +++ b/composer.lock @@ -4,20 +4,20 @@ "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", "This file is @generated automatically" ], - "content-hash": "153b29e08b44baf747ef0df3ef75331a", + "content-hash": "0b8b0e658b7dce17f6848b7f79e34c48", "packages": [ { "name": "api-platform/core", - "version": "v4.3.21", + "version": "v4.4.6", "source": { "type": "git", "url": "https://github.com/api-platform/core.git", - "reference": "f5ae49cc473548a800739f5f0ec6f3370a83d88d" + "reference": "fcaa1cd28551de1a84ae1437b16472486ebf57f9" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/api-platform/core/zipball/f5ae49cc473548a800739f5f0ec6f3370a83d88d", - "reference": "f5ae49cc473548a800739f5f0ec6f3370a83d88d", + "url": "https://api.github.com/repos/api-platform/core/zipball/fcaa1cd28551de1a84ae1437b16472486ebf57f9", + "reference": "fcaa1cd28551de1a84ae1437b16472486ebf57f9", "shasum": "" }, "require": { @@ -26,17 +26,17 @@ "php": ">=8.2", "psr/cache": "^1.0 || ^2.0 || ^3.0", "psr/container": "^1.0 || ^2.0", - "symfony/asset": "^6.4 || ^7.0 || ^8.0", + "symfony/asset": "^7.4 || ^8.0", "symfony/deprecation-contracts": "^3.1", - "symfony/http-foundation": "^6.4.14 || ^7.0 || ^8.0", - "symfony/http-kernel": "^6.4.13 || ^7.0 || ^8.0", - "symfony/property-access": "^6.4 || ^7.0 || ^8.0", - "symfony/property-info": "^6.4 || ^7.1 || ^8.0", - "symfony/serializer": "^6.4.37 || ^7.4.9 || ^8.0.9", + "symfony/http-foundation": "^7.4 || ^8.0", + "symfony/http-kernel": "^7.4 || ^8.0", + "symfony/property-access": "^7.4 || ^8.0", + "symfony/property-info": "^7.4 || ^8.0", + "symfony/serializer": "^7.4.9 || ^8.0.9", "symfony/translation-contracts": "^3.3", "symfony/type-info": "^7.4 || ^8.0", - "symfony/validator": "^6.4.11 || ^7.1 || ^8.0", - "symfony/web-link": "^6.4 || ^7.1 || ^8.0", + "symfony/validator": "^7.4 || ^8.0", + "symfony/web-link": "^7.4 || ^8.0", "willdurand/negotiation": "^3.1" }, "conflict": { @@ -47,7 +47,7 @@ "doctrine/persistence": "<1.3", "phpspec/prophecy": "<1.15", "phpunit/phpunit": "<9.5", - "symfony/framework-bundle": "6.4.6 || 7.0.6", + "symfony/framework-bundle": "<7.4", "symfony/object-mapper": "<7.3.4", "symfony/var-exporter": "<6.1.1" }, @@ -78,8 +78,8 @@ "require-dev": { "doctrine/common": "^3.2.2", "doctrine/dbal": "^4.0", - "doctrine/doctrine-bundle": "^2.11 || ^3.1", - "doctrine/orm": "^2.17 || ^3.0", + "doctrine/doctrine-bundle": "^2.11.1 || ^3.1", + "doctrine/orm": "^2.17 || ^3.3", "elasticsearch/elasticsearch": "^7.17 || ^8.4 || ^9.0", "friendsofphp/php-cs-fixer": "^3.93", "guzzlehttp/guzzle": "^6.0 || ^7.0", @@ -108,38 +108,38 @@ "ramsey/uuid-doctrine": "^2.0", "soyuka/pmu": "^0.2.0", "soyuka/stubs-mongodb": "^1.0", - "symfony/browser-kit": "^6.4 || ^7.0 || ^8.0", - "symfony/cache": "^6.4 || ^7.0 || ^8.0", - "symfony/config": "^6.4 || ^7.0 || ^8.0", - "symfony/console": "^6.4 || ^7.0 || ^8.0", - "symfony/css-selector": "^6.4 || ^7.0 || ^8.0", - "symfony/dependency-injection": "^6.4 || ^7.0 || ^8.0", - "symfony/doctrine-bridge": "^6.4.2 || ^7.1 || ^8.0", - "symfony/dom-crawler": "^6.4 || ^7.0 || ^8.0", - "symfony/error-handler": "^6.4 || ^7.0 || ^8.0", - "symfony/event-dispatcher": "^6.4 || ^7.0 || ^8.0", - "symfony/expression-language": "^6.4 || ^7.0 || ^8.0", - "symfony/finder": "^6.4 || ^7.0 || ^8.0", - "symfony/form": "^6.4 || ^7.0 || ^8.0", - "symfony/framework-bundle": "^6.4 || ^7.0 || ^8.0", - "symfony/http-client": "^6.4 || ^7.0 || ^8.0", - "symfony/intl": "^6.4 || ^7.0 || ^8.0", + "symfony/browser-kit": "^7.4 || ^8.0", + "symfony/cache": "^7.4 || ^8.0", + "symfony/config": "^7.4 || ^8.0", + "symfony/console": "^7.4 || ^8.0", + "symfony/css-selector": "^7.4 || ^8.0", + "symfony/dependency-injection": "^7.4 || ^8.0", + "symfony/doctrine-bridge": "^7.4 || ^8.0", + "symfony/dom-crawler": "^7.4 || ^8.0", + "symfony/error-handler": "^7.4 || ^8.0", + "symfony/event-dispatcher": "^7.4 || ^8.0", + "symfony/expression-language": "^7.4 || ^8.0", + "symfony/finder": "^7.4 || ^8.0", + "symfony/form": "^7.4 || ^8.0", + "symfony/framework-bundle": "^7.4 || ^8.0", + "symfony/http-client": "^7.4 || ^8.0", + "symfony/intl": "^7.4 || ^8.0", "symfony/json-streamer": "^7.4 || ^8.0", "symfony/maker-bundle": "^1.24", "symfony/mcp-bundle": "^0.13", - "symfony/mercure-bundle": "^0.4.3|^0.5", - "symfony/messenger": "^6.4 || ^7.0 || ^8.0", + "symfony/mercure-bundle": "^0.5", + "symfony/messenger": "^7.4 || ^8.0", "symfony/object-mapper": "^7.4 || ^8.0", - "symfony/routing": "^6.4 || ^7.0 || ^8.0", - "symfony/security-bundle": "^6.4 || ^7.0 || ^8.0", - "symfony/security-core": "^6.4 || ^7.0 || ^8.0", - "symfony/stopwatch": "^6.4 || ^7.0 || ^8.0", - "symfony/string": "^6.4 || ^7.0 || ^8.0", - "symfony/twig-bundle": "^6.4 || ^7.0 || ^8.0", - "symfony/uid": "^6.4 || ^7.0 || ^8.0", + "symfony/routing": "^7.4 || ^8.0", + "symfony/security-bundle": "^7.4 || ^8.0", + "symfony/security-core": "^7.4 || ^8.0", + "symfony/stopwatch": "^7.4 || ^8.0", + "symfony/string": "^7.4 || ^8.0", + "symfony/twig-bundle": "^7.4 || ^8.0", + "symfony/uid": "^7.4 || ^8.0", "symfony/var-exporter": "^7.4 || ^8.0", - "symfony/web-profiler-bundle": "^6.4 || ^7.0 || ^8.0", - "symfony/yaml": "^6.4 || ^7.0 || ^8.0", + "symfony/web-profiler-bundle": "^7.4 || ^8.0", + "symfony/yaml": "^7.4 || ^8.0", "twig/twig": "^1.42.3 || ^2.12 || ^3.0", "webonyx/graphql-php": "^15.0" }, @@ -175,7 +175,7 @@ "name": "api-platform/api-platform" }, "symfony": { - "require": "^6.4 || ^7.1 || ^8.0" + "require": "^7.4 || ^8.0" }, "branch-alias": { "dev-3.4": "3.4.x-dev", @@ -220,10 +220,10 @@ ], "support": { "issues": "https://github.com/api-platform/core/issues", - "source": "https://github.com/api-platform/core/tree/v4.3.21" + "source": "https://github.com/api-platform/core/tree/v4.4.6" }, - "time": "2026-09-29T19:59:04+00:00", - "published-time": "2026-09-29T20:02:59+00:00" + "time": "2026-10-23T07:49:00+00:00", + "published-time": "2026-10-23T07:53:11+00:00" }, { "name": "composer/semver", diff --git a/config/reference.php b/config/reference.php index b8f6629..8fe72f5 100644 --- a/config/reference.php +++ b/config/reference.php @@ -1070,10 +1070,10 @@ * inflector?: scalar|Param|null, // Specify an inflector to use. // Default: "api_platform.metadata.inflector" * validator?: array{ * serialize_payload_fields?: mixed, // Set to null to serialize all payload fields when a validation error is thrown, or set the fields you want to include explicitly. // Default: [] - * query_parameter_validation?: bool|Param, // Deprecated: Will be removed in API Platform 5.0. // Default: true + * query_parameter_validation?: bool|Param, // Deprecated: The "query_parameter_validation" configuration is deprecated and will be removed in API Platform 5.0. // Default: true * }, * jsonapi?: array{ - * use_iri_as_id?: bool|Param, // Set to false to use entity identifiers instead of IRIs as the "id" field in JSON:API responses. // Default: true + * use_iri_as_id?: bool|Param|null, // Set to false to use entity identifiers instead of IRIs as the "id" field in JSON:API responses. Defaults to true; this default will change to false in API Platform 5.0. // Default: null * allow_client_generated_id?: bool|Param, // Allow client-generated IDs on JSON:API POST per https://jsonapi.org/format/#crud-creating-client-ids. Off by default to prevent id spoofing on public endpoints. // Default: false * }, * eager_loading?: bool|array{ @@ -1090,9 +1090,10 @@ * enable_scalar?: bool|Param, // Enable Scalar API Reference // Default: true * enable_entrypoint?: bool|Param, // Enable the entrypoint // Default: true * enable_docs?: bool|Param, // Enable the docs // Default: true + * enable_head_request_optimization?: bool|Param, // Skip response body construction on HEAD requests so collections are not iterated. Disable to process HEAD identically to GET. // Default: true * enable_profiler?: bool|Param, // Enable the data collector and the WebProfilerBundle integration. // Default: true * enable_phpdoc_parser?: bool|Param, // Enable resource metadata collector using PHPStan PhpDocParser. // Default: true - * enable_link_security?: bool|Param, // Deprecated: This option is always enabled and will be removed in API Platform 5.0. // Enable security for Links (sub resources). // Default: true + * enable_link_security?: bool|Param, // Deprecated: The "enable_link_security" configuration is deprecated, this option is always enabled and will be removed in API Platform 5.0. // Enable security for Links (sub resources). // Default: true * collection?: array{ * exists_parameter_name?: scalar|Param|null, // The name of the query parameter to filter on nullable field values. // Default: "exists" * order?: scalar|Param|null, // The default order of results. // Default: "ASC" @@ -1155,6 +1156,7 @@ * }, * swagger?: array{ * persist_authorization?: bool|Param, // Persist the SwaggerUI Authorization in the localStorage. // Default: false + * with_credentials?: bool|Param, // Send credentials (cookies, authorization headers) on Swagger UI cross-origin requests (e.g. when running behind Cloudflare Access). // Default: false * versions?: list, * api_keys?: arrayrequestWithAccept('application/ld+json'); $this->assertSame(Response::HTTP_OK, $response->getStatusCode()); - $this->assertResponseHeaderSame('content-type', 'application/ld+json; charset=utf-8'); + $this->assertResponseHeaderSame('content-type', 'application/ld+json'); } public function testPlainJsonIsNotAcceptable(): void diff --git a/tests/ApiPlatform/Trait/GetEntitiesTestTrait.php b/tests/ApiPlatform/Trait/GetEntitiesTestTrait.php index eeb1880..1cc4153 100644 --- a/tests/ApiPlatform/Trait/GetEntitiesTestTrait.php +++ b/tests/ApiPlatform/Trait/GetEntitiesTestTrait.php @@ -17,7 +17,7 @@ public function testGetEntities(): void $response = $this->get([]); $this->assertResponseStatusCodeSame(Response::HTTP_OK); - $this->assertResponseHeaderSame('content-type', 'application/ld+json; charset=utf-8'); + $this->assertResponseHeaderSame('content-type', 'application/ld+json'); $this->assertJson($response->getContent()); $data = $response->toArray(); diff --git a/tests/ApiPlatform/Trait/GetItemTestTrait.php b/tests/ApiPlatform/Trait/GetItemTestTrait.php index 6102c5e..24bbfd4 100644 --- a/tests/ApiPlatform/Trait/GetItemTestTrait.php +++ b/tests/ApiPlatform/Trait/GetItemTestTrait.php @@ -18,7 +18,7 @@ public function testGetItem(): void $response = $this->get([], $path); $this->assertResponseStatusCodeSame(Response::HTTP_OK); - $this->assertResponseHeaderSame('content-type', 'application/ld+json; charset=utf-8'); + $this->assertResponseHeaderSame('content-type', 'application/ld+json'); $this->assertJson($response->getContent()); $this->assertMatchesResourceItemJsonSchema(static::$resourceClass); } From cf89cfa402628b175714d3f16681b330e897420d Mon Sep 17 00:00:00 2001 From: turegjorup Date: Sun, 8 Nov 2026 21:37:12 +0100 Subject: [PATCH 2/3] docs: set PR number in CHANGELOG entry --- CHANGELOG.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index f003c8c..1c6d6b7 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -8,7 +8,7 @@ See [keep a changelog] for information about writing changes to this log. ## [Unreleased] -- [PR-XX](https://github.com/itk-dev/event-database-api/pull/XX) +- [PR-76](https://github.com/itk-dev/event-database-api/pull/76) Update API Platform to 4.4 (OpenAPI 3.2.0, no `charset` in the `application/ld+json` content type) - [PR-75](https://github.com/itk-dev/event-database-api/pull/75) Update Rector (2.8) and PHPStan (2.3) and apply the new Rector rules From 0780aa35a88d58b9603d9f1452a1d3fde709d9fe Mon Sep 17 00:00:00 2001 From: turegjorup Date: Sun, 8 Nov 2026 21:39:49 +0100 Subject: [PATCH 3/3] fix: declare Vocabulary tags as list of strings API Platform 4.4 does not guess an item type for untyped arrays, so the spec had 'items: {}' and oasdiff reported a breaking change. The tags are keyword strings in Elasticsearch. --- public/spec.yaml | 3 ++- src/Api/Dto/Vocabulary.php | 3 +++ 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/public/spec.yaml b/public/spec.yaml index e497132..64c098b 100644 --- a/public/spec.yaml +++ b/public/spec.yaml @@ -1799,7 +1799,8 @@ components: type: string tags: type: array - items: { } + items: + type: string responses: { } parameters: { } examples: { } diff --git a/src/Api/Dto/Vocabulary.php b/src/Api/Dto/Vocabulary.php index 8f88f82..7bbc618 100644 --- a/src/Api/Dto/Vocabulary.php +++ b/src/Api/Dto/Vocabulary.php @@ -54,6 +54,9 @@ )] readonly class Vocabulary { + /** + * @param list $tags + */ public function __construct( public string $name, #[ApiProperty(