From f3f20e380f4f6be25a7c4069fb8fc1118a38f669 Mon Sep 17 00:00:00 2001 From: Dimitri Gritsajuk Date: Thu, 5 Dec 2024 16:29:00 +0100 Subject: [PATCH 1/2] Update api-platform to v4 --- composer.json | 2 +- composer.lock | 199 +++-- config/bundles.php | 2 +- config/packages/api_platform.yaml | 22 +- config/packages/dev/api_platform.yaml | 2 - config/packages/test/api_platform.yaml | 6 +- config/services.yaml | 47 +- config/services/services.xml | 9 - src/Adhesion/Request/MembershipRequest.php | 1 - src/Api/Filter/AbstractScopeFilter.php | 4 +- src/Api/Filter/ActiveRipostesFilter.php | 2 +- src/Api/Filter/AdherentIdentityFilter.php | 2 +- src/Api/Filter/JecouteNewsZipCodeFilter.php | 2 +- src/Api/Filter/MyCreatedEventsFilter.php | 2 +- src/Api/Filter/MySubscribedEventsFilter.php | 2 +- .../OrderEventsBySubscriptionsFilter.php | 2 +- src/Api/Filter/ScopeVisibilityFilter.php | 2 +- src/Api/Filter/SurveyTypeFilter.php | 2 +- src/Api/IriConverterDecorator.php | 36 + src/Api/Listener/AdherentListener.php | 2 +- .../PostEventInscriptionEditListener.php | 2 +- src/Api/Provider/EventProvider.php | 2 +- .../CommitteeGroupsContextBuilder.php | 2 +- .../DesignationGroupsContextBuilder.php | 2 +- .../EnforceTypeValidationContextBuilder.php | 25 + .../JecouteNewsGroupsContextBuilder.php | 2 +- .../PrivatePublicContextBuilder.php | 2 +- .../RiposteGroupsContextBuilder.php | 2 +- .../Api/Jecoute/SurveyController.php | 20 +- src/Controller/Api/UserController.php | 6 +- src/Entity/Action/Action.php | 7 +- .../AbstractAdherentMessage.php | 21 +- .../ElectedRepresentative.php | 2 +- src/Entity/ElectedRepresentative/Mandate.php | 2 +- src/Entity/Event/Event.php | 19 +- src/Entity/Jecoute/JemarcheDataSurvey.php | 6 +- src/Entity/Jecoute/News.php | 2 - src/Entity/Jecoute/Region.php | 1 - src/Entity/Jecoute/Riposte.php | 10 +- src/Entity/MyTeam/MyTeam.php | 7 +- src/Entity/Pap/Campaign.php | 1 - src/Entity/Pap/CampaignHistory.php | 6 +- src/Entity/Phoning/Campaign.php | 1 - src/Entity/Phoning/CampaignHistory.php | 7 +- src/Entity/ProcurationV2/Request.php | 16 +- .../Designation/Designation.php | 10 +- src/Normalizer/AdherentNormalizer.php | 8 +- .../CommitteeCandidacyDenormalizer.php | 4 +- .../ConstraintViolationListNormalizer.php | 8 +- src/Normalizer/DesignationDenormalizer.php | 2 +- src/Normalizer/DeviceNormalizer.php | 8 +- src/Normalizer/DocumentNormalizer.php | 2 +- .../DonationValueObjectNormalizer.php | 8 +- src/Normalizer/ElectedMandateDenormalizer.php | 2 +- ...epresentativeAdherentMandateNormalizer.php | 6 +- ...ivePoliticalFunctionFromIdDenormalizer.php | 2 +- .../EntityCategoryFromSlugDenormalizer.php | 2 +- src/Normalizer/EntityFromUuidDenormalizer.php | 2 +- src/Normalizer/FormationNormalizer.php | 2 +- .../GeneralMeetingReportNormalizer.php | 2 +- src/Normalizer/ItemNormalizerDecorator.php | 844 ------------------ .../JecouteChoiceFromIdDenormalizer.php | 2 +- src/Normalizer/JecouteDeviceNormalizer.php | 2 - src/Normalizer/JecouteSurveyNormalizer.php | 40 +- ...wsletterSubscriptionSourceDenormalizer.php | 2 +- .../Pap/BuildingBlockNormalizer.php | 18 +- .../Pap/BuildingFloorNormalizer.php | 19 +- src/Normalizer/SurveyDenormalizer.php | 18 +- .../SurveyQuestionFromIdDenormalizer.php | 2 +- .../UpdateElectedMandateDenormalizer.php | 2 +- .../Listener/CheckUpdatedStatusListener.php | 4 +- .../ProcurationSlotStatusActionListener.php | 8 +- .../ProcurationStatusActionListener.php | 8 +- .../Jecoute/LocalSurveyRepository.php | 16 - src/Swagger/PaginatorSwagger.php | 48 - src/Swagger/SwaggerDecorator.php | 101 --- 76 files changed, 404 insertions(+), 1319 deletions(-) delete mode 100644 config/packages/dev/api_platform.yaml create mode 100644 src/Api/IriConverterDecorator.php create mode 100644 src/Api/Serializer/EnforceTypeValidationContextBuilder.php delete mode 100644 src/Normalizer/ItemNormalizerDecorator.php delete mode 100644 src/Swagger/PaginatorSwagger.php delete mode 100644 src/Swagger/SwaggerDecorator.php diff --git a/composer.json b/composer.json index 7fe55d7a35f..7b66f9f6b91 100644 --- a/composer.json +++ b/composer.json @@ -40,7 +40,7 @@ "ext-zip": "*", "a2lix/translation-form-bundle": "^3.2", "algolia/search-bundle": "^6.0", - "api-platform/core": "^2.7", + "api-platform/core": "^4.0", "beberlei/doctrineextensions": "^1.2", "cakephp/chronos": "^3.1", "cocur/slugify": "^4.0", diff --git a/composer.lock b/composer.lock index eb36628f071..be9fff94dba 100644 --- a/composer.lock +++ b/composer.lock @@ -4,7 +4,7 @@ "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", "This file is @generated automatically" ], - "content-hash": "60e1802e655dd850c58ecbcd96f3f222", + "content-hash": "d43395e4a5334c241a339135d33c4d49", "packages": [ { "name": "a2lix/auto-form-bundle", @@ -315,107 +315,145 @@ }, { "name": "api-platform/core", - "version": "v2.7.18", + "version": "v4.0.17", "source": { "type": "git", "url": "https://github.com/api-platform/core.git", - "reference": "6ff3e05d97602cdaa3b329112ca21f7e916a504d" + "reference": "155c01ff237a432cdd535387cc408fcf6152551f" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/api-platform/core/zipball/6ff3e05d97602cdaa3b329112ca21f7e916a504d", - "reference": "6ff3e05d97602cdaa3b329112ca21f7e916a504d", + "url": "https://api.github.com/repos/api-platform/core/zipball/155c01ff237a432cdd535387cc408fcf6152551f", + "reference": "155c01ff237a432cdd535387cc408fcf6152551f", "shasum": "" }, "require": { "doctrine/inflector": "^1.0 || ^2.0", - "php": ">=7.1", + "php": ">=8.2", "psr/cache": "^1.0 || ^2.0 || ^3.0", "psr/container": "^1.0 || ^2.0", - "symfony/deprecation-contracts": "^2.1 || ^3.0", - "symfony/http-foundation": "^4.4 || ^5.1 || ^6.0", - "symfony/http-kernel": "^4.4 || ^5.1 || ^6.0", - "symfony/property-access": "^3.4.19 || ^4.4 || ^5.1 || ^6.0", - "symfony/property-info": "^3.4 || ^4.4 || ^5.2.1 || ^6.0", - "symfony/serializer": "^4.4 || ^5.1 || ^6.0", - "symfony/web-link": "^4.4 || ^5.1 || ^6.0", - "willdurand/negotiation": "^2.0.3 || ^3.0" + "symfony/deprecation-contracts": "^3.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.0", + "willdurand/negotiation": "^3.1" }, "conflict": { - "doctrine/common": "<2.7", + "doctrine/common": "<3.2.2", "doctrine/dbal": "<2.10", - "doctrine/mongodb-odm": "<2.2", + "doctrine/mongodb-odm": "<2.4", + "doctrine/orm": "<2.14.0", "doctrine/persistence": "<1.3", - "elasticsearch/elasticsearch": ">=8.0" + "phpspec/prophecy": "<1.15", + "phpunit/phpunit": "<9.5", + "symfony/framework-bundle": "6.4.6 || 7.0.6", + "symfony/var-exporter": "<6.1.1" }, - "require-dev": { - "behat/behat": "^3.1", - "behat/mink": "^1.9@dev", - "doctrine/annotations": "^1.7", + "replace": { + "api-platform/doctrine-common": "self.version", + "api-platform/doctrine-odm": "self.version", + "api-platform/doctrine-orm": "self.version", + "api-platform/documentation": "self.version", + "api-platform/elasticsearch": "self.version", + "api-platform/graphql": "self.version", + "api-platform/http-cache": "self.version", + "api-platform/hydra": "self.version", + "api-platform/json-api": "self.version", + "api-platform/json-hal": "self.version", + "api-platform/json-schema": "self.version", + "api-platform/jsonld": "self.version", + "api-platform/laravel": "self.version", + "api-platform/metadata": "self.version", + "api-platform/openapi": "self.version", + "api-platform/parameter-validator": "self.version", + "api-platform/ramsey-uuid": "self.version", + "api-platform/serializer": "self.version", + "api-platform/state": "self.version", + "api-platform/symfony": "self.version", + "api-platform/validator": "self.version" + }, + "require-dev": { + "behat/behat": "^3.11", + "behat/mink": "^1.9", "doctrine/cache": "^1.11 || ^2.1", - "doctrine/common": "^2.11 || ^3.0", - "doctrine/data-fixtures": "^1.2.2", - "doctrine/dbal": "^2.6 || ^3.0", - "doctrine/doctrine-bundle": "^1.12 || ^2.0", - "doctrine/mongodb-odm": "^2.2", - "doctrine/mongodb-odm-bundle": "^4.0", - "doctrine/orm": "^2.6.4", - "elasticsearch/elasticsearch": "^7.11.0", + "doctrine/common": "^3.2.2", + "doctrine/dbal": "^4.0", + "doctrine/doctrine-bundle": "^2.11", + "doctrine/mongodb-odm": "^2.6", + "doctrine/mongodb-odm-bundle": "^4.0 || ^5.0", + "doctrine/orm": "^2.17 || ^3.0", + "elasticsearch/elasticsearch": "^8.4", "friends-of-behat/mink-browserkit-driver": "^1.3.1", "friends-of-behat/mink-extension": "^2.2", "friends-of-behat/symfony-extension": "^2.1", "guzzlehttp/guzzle": "^6.0 || ^7.0", + "illuminate/config": "^11.0", + "illuminate/contracts": "^11.0", + "illuminate/database": "^11.0", + "illuminate/http": "^11.0", + "illuminate/pagination": "^11.0", + "illuminate/routing": "^11.0", + "illuminate/support": "^11.0", "jangregor/phpstan-prophecy": "^1.0", - "justinrainbow/json-schema": "^5.2.1", - "phpdocumentor/reflection-docblock": "^3.0 || ^4.0 || ^5.1", - "phpdocumentor/type-resolver": "^0.3 || ^0.4 || ^1.4", - "phpspec/prophecy": "^1.10", + "justinrainbow/json-schema": "^5.2.11", + "laravel/framework": "^11.0", + "orchestra/testbench": "^9.1", + "phpspec/prophecy-phpunit": "^2.2", "phpstan/extension-installer": "^1.1", - "phpstan/phpstan": "^1.1", + "phpstan/phpdoc-parser": "^1.13|^2.0", + "phpstan/phpstan": "^1.10", "phpstan/phpstan-doctrine": "^1.0", "phpstan/phpstan-phpunit": "^1.0", "phpstan/phpstan-symfony": "^1.0", + "phpunit/phpunit": "^11.2", "psr/log": "^1.0 || ^2.0 || ^3.0", - "ramsey/uuid": "^3.7 || ^4.0", - "ramsey/uuid-doctrine": "^1.4", - "soyuka/contexts": "^3.3.6", + "ramsey/uuid": "^4.0", + "ramsey/uuid-doctrine": "^2.0", + "soyuka/contexts": "^3.3.10", + "soyuka/pmu": "^0.0.15", "soyuka/stubs-mongodb": "^1.0", - "symfony/asset": "^3.4 || ^4.4 || ^5.1 || ^6.0", - "symfony/browser-kit": "^4.4 || ^5.1 || ^6.0", - "symfony/cache": "^3.4 || ^4.4 || ^5.1 || ^6.0", - "symfony/config": "^3.4 || ^4.4 || ^5.1 || ^6.0", - "symfony/console": "^3.4 || ^4.4 || ^5.1 || ^6.0", - "symfony/css-selector": "^3.4 || ^4.4 || ^5.1 || ^6.0", - "symfony/debug": "^3.4 || ^4.4 || ^5.1 || ^6.0", - "symfony/dependency-injection": "^3.4 || ^4.4 || ^5.1 || ^6.0", - "symfony/doctrine-bridge": "^3.4 || ^4.4 || ^5.1 || ^6.0", - "symfony/dom-crawler": "^3.4 || ^4.4 || ^5.1 || ^6.0", - "symfony/event-dispatcher": "^3.4 || ^4.4 || ^5.1 || ^6.0", - "symfony/expression-language": "^3.4 || ^4.4 || ^5.1 || ^6.0", - "symfony/finder": "^3.4 || ^4.4 || ^5.1 || ^6.0", - "symfony/form": "^3.4 || ^4.4 || ^5.1 || ^6.0", - "symfony/framework-bundle": "^4.4 || ^5.1 || ^6.0", - "symfony/http-client": "^4.4 || ^5.1 || ^6.0", - "symfony/intl": "^4.4 || ^5.3 || ^6.0", + "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": "^4.4 || ^5.1 || ^6.0", - "symfony/phpunit-bridge": "^5.4 || ^6.0", - "symfony/routing": "^3.4 || ^4.4 || ^5.1 || ^6.0", - "symfony/security-bundle": "^3.4 || ^4.4 || ^5.1 || ^6.0", - "symfony/security-core": "^4.4 || ^5.1 || ^6.0", - "symfony/twig-bundle": "^3.4 || ^4.4 || ^5.1 || ^6.0", - "symfony/validator": "^3.4 || ^4.4 || ^5.1 || ^6.0", - "symfony/web-profiler-bundle": "^4.4 || ^5.1 || ^6.0", - "symfony/yaml": "^3.4 || ^4.4 || ^5.1 || ^6.0", + "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": "^14.0" + "webonyx/graphql-php": "^15.0" }, "suggest": { "doctrine/mongodb-odm-bundle": "To support MongoDB. Only versions 4.0 and later are supported.", "elasticsearch/elasticsearch": "To support Elasticsearch.", "ocramius/package-versions": "To display the API Platform's version in the debug bar.", - "phpdocumentor/reflection-docblock": "To support extracting metadata from PHPDoc.", + "phpstan/phpdoc-parser": "To support extracting metadata from PHPDoc.", "psr/cache-implementation": "To use metadata caching.", "ramsey/uuid": "To support Ramsey's UUID identifiers.", "symfony/cache": "To have metadata caching when using Symfony integration.", @@ -431,16 +469,27 @@ }, "type": "library", "extra": { - "branch-alias": { - "dev-main": "2.7.x-dev" + "pmu": { + "projects": [ + "./src/*/composer.json", + "src/Doctrine/*/composer.json" + ] + }, + "thanks": { + "url": "https://github.com/api-platform/api-platform", + "name": "api-platform/api-platform" }, "symfony": { - "require": "^3.4 || ^4.4 || ^5.1 || ^6.0" + "require": "^6.4 || ^7.1" + }, + "branch-alias": { + "dev-3.4": "3.4.x-dev", + "dev-main": "4.0.x-dev" } }, "autoload": { "files": [ - "src/deprecation.php" + "src/JsonLd/HydraContext.php" ], "psr-4": { "ApiPlatform\\": "src/" @@ -466,15 +515,17 @@ "graphql", "hal", "jsonapi", + "laravel", "openapi", "rest", - "swagger" + "swagger", + "symfony" ], "support": { "issues": "https://github.com/api-platform/core/issues", - "source": "https://github.com/api-platform/core/tree/v2.7.18" + "source": "https://github.com/api-platform/core/tree/v4.0.17" }, - "time": "2024-03-19T07:17:43+00:00" + "time": "2025-02-07T15:47:55+00:00" }, { "name": "bacon/bacon-qr-code", @@ -22899,6 +22950,6 @@ "ext-pdo": "*", "ext-zip": "*" }, - "platform-dev": [], - "plugin-api-version": "2.3.0" + "platform-dev": {}, + "plugin-api-version": "2.6.0" } diff --git a/config/bundles.php b/config/bundles.php index 832300e6661..4875ae17838 100644 --- a/config/bundles.php +++ b/config/bundles.php @@ -28,7 +28,7 @@ Knp\Bundle\TimeBundle\KnpTimeBundle::class => ['all' => true], Exercise\HTMLPurifierBundle\ExerciseHTMLPurifierBundle::class => ['all' => true], Nelmio\CorsBundle\NelmioCorsBundle::class => ['all' => true], - ApiPlatform\Core\Bridge\Symfony\Bundle\ApiPlatformBundle::class => ['all' => true], + ApiPlatform\Symfony\Bundle\ApiPlatformBundle::class => ['all' => true], Symfony\Bundle\DebugBundle\DebugBundle::class => ['dev' => true, 'test' => true], Symfony\Bundle\WebProfilerBundle\WebProfilerBundle::class => ['dev' => true, 'test' => true], DAMA\DoctrineTestBundle\DAMADoctrineTestBundle::class => ['test' => true], diff --git a/config/packages/api_platform.yaml b/config/packages/api_platform.yaml index 5bee7956149..6c02fae8cf8 100644 --- a/config/packages/api_platform.yaml +++ b/config/packages/api_platform.yaml @@ -1,14 +1,13 @@ api_platform: name_converter: serializer.name_converter.camel_case_to_snake_case + use_symfony_listeners: true resource_class_directories: - '%kernel.project_dir%/src/Entity' - enable_docs: true - enable_swagger_ui: true + enable_docs: false + enable_swagger_ui: false enable_entrypoint: false - allow_plain_identifiers: true - metadata_backward_compatibility_layer: false exception_to_status: - ApiPlatform\Core\Bridge\Symfony\Validator\Exception\ValidationException: 400 + ApiPlatform\Validator\Exception\ValidationException: !php/const Symfony\Component\HttpFoundation\Response::HTTP_BAD_REQUEST formats: json: mime_types: ['application/json'] @@ -17,9 +16,16 @@ api_platform: error_formats: jsonproblem: mime_types: ['application/problem+json'] + patch_formats: + json: ['application/json'] collection: pagination: - items_per_page: 10 - maximum_items_per_page: 30 - client_items_per_page: true items_per_page_parameter_name: page_size + defaults: + normalization_context: + skip_null_values: false + extra_properties: + standard_put: false + pagination_items_per_page: 10 + pagination_maximum_items_per_page: 30 + pagination_client_items_per_page: true diff --git a/config/packages/dev/api_platform.yaml b/config/packages/dev/api_platform.yaml deleted file mode 100644 index c3d8796fc2d..00000000000 --- a/config/packages/dev/api_platform.yaml +++ /dev/null @@ -1,2 +0,0 @@ -api_platform: - enable_entrypoint: true diff --git a/config/packages/test/api_platform.yaml b/config/packages/test/api_platform.yaml index 49e13c58a17..e0e019bff57 100644 --- a/config/packages/test/api_platform.yaml +++ b/config/packages/test/api_platform.yaml @@ -1,5 +1,3 @@ api_platform: - enable_entrypoint: false - collection: - pagination: - items_per_page: 2 + defaults: + pagination_items_per_page: 2 diff --git a/config/services.yaml b/config/services.yaml index e52492d0eb2..c788b5cda97 100644 --- a/config/services.yaml +++ b/config/services.yaml @@ -230,27 +230,11 @@ services: tags: [{ name: 'serializer.normalizer', priority: 1 }] App\Normalizer\ConstraintViolationListNormalizer: - decorates: 'serializer.normalizer.constraint_violation_list' + decorates: 'api_platform.problem.normalizer.validation_exception' + tags: [{ name: 'serializer.normalizer', priority: -800 }] arguments: - '@serializer.name_converter.metadata_aware' - App\Normalizer\ItemNormalizerDecorator: - decorates: 'api_platform.serializer.normalizer.item' - arguments: - - '@api_platform.metadata.property.name_collection_factory' - - '@api_platform.metadata.property.metadata_factory' - - '@api_platform.iri_converter' - - '@api_platform.resource_class_resolver' - - '@api_platform.property_accessor' - - '@api_platform.name_converter' - - '@serializer.mapping.class_metadata_factory' - - '@api_platform.item_data_provider' - - '%api_platform.allow_plain_identifiers%' - - [] - - !tagged_iterator 'api_platform.data_transformer' - - ~ - - '@api_platform.security.resource_access_checker' - App\Normalizer\Indexer\ThrowExceptionNormalizer: tags: [{ name: 'serializer.normalizer', priority: -801 }] # to be just after CustomNormalizer @@ -261,7 +245,7 @@ services: App\Algolia\SearchService: decorates: 'Algolia\SearchBundle\SearchService' arguments: - - '@App\Algolia\SearchService.inner' + - '@.inner' - '%kernel.debug%' Geocoder\Geocoder: '@bazinga_geocoder.geocoder' @@ -360,11 +344,15 @@ services: App\Api\Serializer\RiposteGroupsContextBuilder: decorates: 'api_platform.serializer.context_builder' - arguments: ['@App\Api\Serializer\RiposteGroupsContextBuilder.inner'] + arguments: ['@.inner'] App\Api\Serializer\JecouteNewsGroupsContextBuilder: decorates: 'api_platform.serializer.context_builder' - arguments: ['@App\Api\Serializer\JecouteNewsGroupsContextBuilder.inner'] + arguments: ['@.inner'] + + App\Api\Serializer\EnforceTypeValidationContextBuilder: + decorates: 'api_platform.serializer.context_builder' + arguments: ['@.inner'] App\Api\Serializer\PrivatePublicContextBuilder: decorates: 'api_platform.serializer.context_builder' @@ -372,11 +360,11 @@ services: App\Api\Serializer\CommitteeGroupsContextBuilder: decorates: 'api_platform.serializer.context_builder' - arguments: ['@App\Api\Serializer\CommitteeGroupsContextBuilder.inner'] + arguments: ['@.inner'] App\Api\Serializer\DesignationGroupsContextBuilder: decorates: 'api_platform.serializer.context_builder' - arguments: ['@App\Api\Serializer\DesignationGroupsContextBuilder.inner'] + arguments: ['@.inner'] Symfony\Bridge\Monolog\Processor\TokenProcessor: tags: ['monolog.processor'] @@ -385,13 +373,16 @@ services: App\Normalizer\DateTimeNormalizer: decorates: 'serializer.normalizer.datetime' - arguments: - - '@App\Normalizer\DateTimeNormalizer.inner' + arguments: ['@.inner'] + + App\Api\IriConverterDecorator: + decorates: 'api_platform.symfony.iri_converter' + arguments: ['@.inner'] App\ErrorRenderer\TwigErrorRenderer: decorates: 'twig.error_renderer.html' arguments: - $fallbackErrorRenderer: '@App\ErrorRenderer\TwigErrorRenderer.inner' + $fallbackErrorRenderer: '@.inner' $debug: !service class: 'bool' factory: Symfony\Bridge\Twig\ErrorRenderer\TwigErrorRenderer::isDebug @@ -404,7 +395,7 @@ services: App\FranceCities\CachedCitiesStorage: decorates: App\FranceCities\CitiesStorage arguments: - $decorated: '@App\FranceCities\CachedCitiesStorage.inner' + $decorated: '@.inner' $cache: !service class: Symfony\Component\Cache\Psr16Cache arguments: @@ -437,7 +428,7 @@ services: App\Messenger\AmqpTransport\AmqpTransportFactory: decorates: 'messenger.transport.amqp.factory' - arguments: ['@App\Messenger\AmqpTransport\AmqpTransportFactory.inner'] + arguments: ['@.inner'] League\Glide\Responses\SymfonyResponseFactory: ~ diff --git a/config/services/services.xml b/config/services/services.xml index 56b0fca32df..8615dd8d997 100644 --- a/config/services/services.xml +++ b/config/services/services.xml @@ -47,14 +47,5 @@ - - - - - %api_path_prefix% - - diff --git a/src/Adhesion/Request/MembershipRequest.php b/src/Adhesion/Request/MembershipRequest.php index 10976e7d689..8221811aa5e 100644 --- a/src/Adhesion/Request/MembershipRequest.php +++ b/src/Adhesion/Request/MembershipRequest.php @@ -41,7 +41,6 @@ class MembershipRequest implements DonationRequestInterface #[Assert\NotBlank] public ?string $nationality = null; - #[Assert\Type('bool')] public ?bool $exclusiveMembership = null; #[Assert\AtLeastOneOf([ diff --git a/src/Api/Filter/AbstractScopeFilter.php b/src/Api/Filter/AbstractScopeFilter.php index fc8f02c5a11..259ce30215a 100644 --- a/src/Api/Filter/AbstractScopeFilter.php +++ b/src/Api/Filter/AbstractScopeFilter.php @@ -28,7 +28,7 @@ protected function filterProperty( string $resourceClass, ?Operation $operation = null, array $context = [], - ) { + ): void { if (self::PROPERTY_NAME !== $property || !$this->isValidOperation($operation)) { return; } @@ -87,7 +87,7 @@ public function setSecurity(Security $security): void protected function getAllowedOperationNames(string $resourceClass): array { - return ['{uuid}_get', '{uuid}.{_format}_get', '_get_collection']; + return ['{uuid}_get', '{uuid}{._format}_get', '_get_collection']; } private function isValidOperation(?Operation $operation): bool diff --git a/src/Api/Filter/ActiveRipostesFilter.php b/src/Api/Filter/ActiveRipostesFilter.php index 72d6905f55e..ba2e5bf5a5d 100644 --- a/src/Api/Filter/ActiveRipostesFilter.php +++ b/src/Api/Filter/ActiveRipostesFilter.php @@ -20,7 +20,7 @@ protected function filterProperty( string $resourceClass, ?Operation $operation = null, array $context = [], - ) { + ): void { if (Riposte::class !== $resourceClass || self::PROPERTY_NAME !== $property) { return; } diff --git a/src/Api/Filter/AdherentIdentityFilter.php b/src/Api/Filter/AdherentIdentityFilter.php index fec996a2ea1..8576e17a10c 100644 --- a/src/Api/Filter/AdherentIdentityFilter.php +++ b/src/Api/Filter/AdherentIdentityFilter.php @@ -22,7 +22,7 @@ protected function filterProperty( string $resourceClass, ?Operation $operation = null, array $context = [], - ) { + ): void { if ( !\in_array($resourceClass, [PhoningCampaignHistory::class, PapCampaignHistory::class]) || !\in_array($property, self::PROPERTY_NAMES, true) diff --git a/src/Api/Filter/JecouteNewsZipCodeFilter.php b/src/Api/Filter/JecouteNewsZipCodeFilter.php index b470a2d8058..2b2a01c8989 100644 --- a/src/Api/Filter/JecouteNewsZipCodeFilter.php +++ b/src/Api/Filter/JecouteNewsZipCodeFilter.php @@ -22,7 +22,7 @@ protected function filterProperty( string $resourceClass, ?Operation $operation = null, array $context = [], - ) { + ): void { if (News::class !== $resourceClass || 'zipCode' !== $property || empty($value) diff --git a/src/Api/Filter/MyCreatedEventsFilter.php b/src/Api/Filter/MyCreatedEventsFilter.php index da24f4cfed8..75bf5582615 100644 --- a/src/Api/Filter/MyCreatedEventsFilter.php +++ b/src/Api/Filter/MyCreatedEventsFilter.php @@ -26,7 +26,7 @@ protected function filterProperty( string $resourceClass, ?Operation $operation = null, array $context = [], - ) { + ): void { if ( Event::class !== $resourceClass || self::PROPERTY_NAME !== $property diff --git a/src/Api/Filter/MySubscribedEventsFilter.php b/src/Api/Filter/MySubscribedEventsFilter.php index 790d55221fd..2566763be9e 100644 --- a/src/Api/Filter/MySubscribedEventsFilter.php +++ b/src/Api/Filter/MySubscribedEventsFilter.php @@ -27,7 +27,7 @@ protected function filterProperty( string $resourceClass, ?Operation $operation = null, array $context = [], - ) { + ): void { if ( !is_a($resourceClass, Event::class, true) || self::PROPERTY_NAME !== $property diff --git a/src/Api/Filter/OrderEventsBySubscriptionsFilter.php b/src/Api/Filter/OrderEventsBySubscriptionsFilter.php index 82a47031a1e..1d0d7711979 100644 --- a/src/Api/Filter/OrderEventsBySubscriptionsFilter.php +++ b/src/Api/Filter/OrderEventsBySubscriptionsFilter.php @@ -22,7 +22,7 @@ protected function filterProperty( string $resourceClass, ?Operation $operation = null, array $context = [], - ) { + ): void { if ( !is_a($resourceClass, Event::class, true) || self::PROPERTY_NAME !== $property diff --git a/src/Api/Filter/ScopeVisibilityFilter.php b/src/Api/Filter/ScopeVisibilityFilter.php index e9988d6f9b4..b542166082a 100644 --- a/src/Api/Filter/ScopeVisibilityFilter.php +++ b/src/Api/Filter/ScopeVisibilityFilter.php @@ -96,6 +96,6 @@ protected function getAllowedOperationNames(string $resourceClass): array return ['_api_/v3/jecoute/news/{uuid}_get', '_api_/v3/jecoute/news_get_collection']; } - return ['{uuid}_get', '{uuid}.{_format}_get', '_get_collection']; + return ['{uuid}_get', '{uuid}{._format}_get', '_get_collection']; } } diff --git a/src/Api/Filter/SurveyTypeFilter.php b/src/Api/Filter/SurveyTypeFilter.php index 2fe80a0c354..20b08c1e5b9 100644 --- a/src/Api/Filter/SurveyTypeFilter.php +++ b/src/Api/Filter/SurveyTypeFilter.php @@ -21,7 +21,7 @@ protected function filterProperty( string $resourceClass, ?Operation $operation = null, array $context = [], - ) { + ): void { if ( Survey::class !== $resourceClass || self::PROPERTY_NAME !== $property diff --git a/src/Api/IriConverterDecorator.php b/src/Api/IriConverterDecorator.php new file mode 100644 index 00000000000..00fa74e6e87 --- /dev/null +++ b/src/Api/IriConverterDecorator.php @@ -0,0 +1,36 @@ +decorated->getIriFromResource(resource: $context['resource_class'], context: ['uri_variables' => ['uuid' => $iri]]); + } elseif (is_a($context['resource_class'], BaseEventCategory::class, true)) { + $iri = $this->decorated->getIriFromResource(resource: $context['resource_class'], context: ['uri_variables' => ['slug' => $iri]]); + } + + return $this->decorated->getResourceFromIri($iri, $context, $operation); + } + + public function getIriFromResource( + object|string $resource, + int $referenceType = UrlGeneratorInterface::ABS_PATH, + ?Operation $operation = null, + array $context = [], + ): ?string { + return $this->decorated->getIriFromResource($resource, $referenceType, $operation, $context); + } +} diff --git a/src/Api/Listener/AdherentListener.php b/src/Api/Listener/AdherentListener.php index a18fbe7a090..a58f2098a96 100644 --- a/src/Api/Listener/AdherentListener.php +++ b/src/Api/Listener/AdherentListener.php @@ -34,7 +34,7 @@ public function onPostWrite(ViewEvent $viewEvent): void if ( !\in_array($request->getMethod(), [Request::METHOD_POST, Request::METHOD_PUT]) - || '_api_/adherents/{uuid}/elect_put' !== $request->attributes->get('_api_operation_name') + || '_api_/v3/adherents/{uuid}/elect_put' !== $request->attributes->get('_api_operation_name') ) { return; } diff --git a/src/Api/Listener/PostEventInscriptionEditListener.php b/src/Api/Listener/PostEventInscriptionEditListener.php index 481ebd53c0f..b5595067a95 100644 --- a/src/Api/Listener/PostEventInscriptionEditListener.php +++ b/src/Api/Listener/PostEventInscriptionEditListener.php @@ -26,7 +26,7 @@ public function onEventInscriptionChange(RequestEvent $requestEvent): void { $request = $requestEvent->getRequest(); - if ('_api_/event_inscriptions/{uuid}.{_format}_put' !== $request->attributes->get('_api_operation_name')) { + if ('_api_/event_inscriptions/{uuid}{._format}_put' !== $request->attributes->get('_api_operation_name')) { return; } diff --git a/src/Api/Provider/EventProvider.php b/src/Api/Provider/EventProvider.php index 72d281476fd..3e0c9dcf4c1 100644 --- a/src/Api/Provider/EventProvider.php +++ b/src/Api/Provider/EventProvider.php @@ -13,7 +13,7 @@ public function __construct(private readonly EventRepository $eventRepository) { } - public function provide(Operation $operation, array $uriVariables = [], array $context = []) + public function provide(Operation $operation, array $uriVariables = [], array $context = []): object|array|null { $identifier = (string) $context['uri_variables']['uuid'] ?? null; diff --git a/src/Api/Serializer/CommitteeGroupsContextBuilder.php b/src/Api/Serializer/CommitteeGroupsContextBuilder.php index 5ed17e01d57..6fe7ca95a14 100644 --- a/src/Api/Serializer/CommitteeGroupsContextBuilder.php +++ b/src/Api/Serializer/CommitteeGroupsContextBuilder.php @@ -2,7 +2,7 @@ namespace App\Api\Serializer; -use ApiPlatform\Serializer\SerializerContextBuilderInterface; +use ApiPlatform\State\SerializerContextBuilderInterface; use App\Entity\Committee; use App\Entity\CommitteeElection; use App\VotingPlatform\Designation\DesignationStatusEnum; diff --git a/src/Api/Serializer/DesignationGroupsContextBuilder.php b/src/Api/Serializer/DesignationGroupsContextBuilder.php index ea9c5dd0780..06d025995da 100644 --- a/src/Api/Serializer/DesignationGroupsContextBuilder.php +++ b/src/Api/Serializer/DesignationGroupsContextBuilder.php @@ -2,7 +2,7 @@ namespace App\Api\Serializer; -use ApiPlatform\Serializer\SerializerContextBuilderInterface; +use ApiPlatform\State\SerializerContextBuilderInterface; use App\Entity\VotingPlatform\Designation\Designation; use Symfony\Component\HttpFoundation\Request; diff --git a/src/Api/Serializer/EnforceTypeValidationContextBuilder.php b/src/Api/Serializer/EnforceTypeValidationContextBuilder.php new file mode 100644 index 00000000000..6f48b29b29d --- /dev/null +++ b/src/Api/Serializer/EnforceTypeValidationContextBuilder.php @@ -0,0 +1,25 @@ +decorated->createFromRequest($request, $normalization, $extractedAttributes); + $context[AbstractObjectNormalizer::DISABLE_TYPE_ENFORCEMENT] = true; + + return $context; + } +} diff --git a/src/Api/Serializer/JecouteNewsGroupsContextBuilder.php b/src/Api/Serializer/JecouteNewsGroupsContextBuilder.php index c7b3d03c515..1b01682dc17 100644 --- a/src/Api/Serializer/JecouteNewsGroupsContextBuilder.php +++ b/src/Api/Serializer/JecouteNewsGroupsContextBuilder.php @@ -2,7 +2,7 @@ namespace App\Api\Serializer; -use ApiPlatform\Serializer\SerializerContextBuilderInterface; +use ApiPlatform\State\SerializerContextBuilderInterface; use App\Entity\Jecoute\News; use App\Scope\AuthorizationChecker; use App\Scope\FeatureEnum; diff --git a/src/Api/Serializer/PrivatePublicContextBuilder.php b/src/Api/Serializer/PrivatePublicContextBuilder.php index 9c68cb9282d..2ed90df8b8e 100644 --- a/src/Api/Serializer/PrivatePublicContextBuilder.php +++ b/src/Api/Serializer/PrivatePublicContextBuilder.php @@ -2,7 +2,7 @@ namespace App\Api\Serializer; -use ApiPlatform\Serializer\SerializerContextBuilderInterface; +use ApiPlatform\State\SerializerContextBuilderInterface; use Symfony\Component\HttpFoundation\Request; use Symfony\Component\Security\Core\Authorization\AuthorizationCheckerInterface; diff --git a/src/Api/Serializer/RiposteGroupsContextBuilder.php b/src/Api/Serializer/RiposteGroupsContextBuilder.php index 9e16a3d5551..c6be7ae9d09 100644 --- a/src/Api/Serializer/RiposteGroupsContextBuilder.php +++ b/src/Api/Serializer/RiposteGroupsContextBuilder.php @@ -2,7 +2,7 @@ namespace App\Api\Serializer; -use ApiPlatform\Serializer\SerializerContextBuilderInterface; +use ApiPlatform\State\SerializerContextBuilderInterface; use App\Entity\Jecoute\Riposte; use Symfony\Component\HttpFoundation\Request; use Symfony\Component\Security\Core\Authorization\AuthorizationCheckerInterface; diff --git a/src/Controller/Api/Jecoute/SurveyController.php b/src/Controller/Api/Jecoute/SurveyController.php index 4a494ff5b5f..3e002bc5e4c 100644 --- a/src/Controller/Api/Jecoute/SurveyController.php +++ b/src/Controller/Api/Jecoute/SurveyController.php @@ -18,7 +18,6 @@ use Symfony\Component\HttpFoundation\Response; use Symfony\Component\Routing\Attribute\Route; use Symfony\Component\Security\Http\Attribute\IsGranted; -use Symfony\Component\Serializer\SerializerInterface; #[IsGranted(new Expression("(is_granted('ROLE_USER') or is_granted('ROLE_OAUTH_DEVICE')) and (is_granted('ROLE_OAUTH_SCOPE_JECOUTE_SURVEYS') or is_granted('ROLE_OAUTH_SCOPE_JEMARCHE_APP'))"))] #[Route(path: '/jecoute/survey')] @@ -30,7 +29,6 @@ public function surveyListAction( LocalSurveyRepository $localSurveyRepository, NationalSurveyRepository $nationalSurveyRepository, ZoneRepository $zoneRepository, - SerializerInterface $serializer, ): Response { $postalCode = null; /** @var Adherent|DeviceApiUser $user */ @@ -54,18 +52,12 @@ public function surveyListAction( $localSurveys = $localSurveyRepository->findAllByZones($zones); - return new JsonResponse( - $serializer->serialize( - array_merge( - $localSurveys, - $nationalSurveyRepository->findAllPublished() - ), - 'json', - ['groups' => ['survey_list']] + return $this->json( + array_merge( + $localSurveys, + $nationalSurveyRepository->findAllPublished() ), - JsonResponse::HTTP_OK, - [], - true + context: ['groups' => ['survey_list']] ); } @@ -115,7 +107,7 @@ public function surveyReplyAction( return new JsonResponse(['status' => 'ok'], JsonResponse::HTTP_CREATED); } - private function getFormErrors(FormInterface $form) + private function getFormErrors(FormInterface $form): array { $errors = []; diff --git a/src/Controller/Api/UserController.php b/src/Controller/Api/UserController.php index 9a71b5a558f..3ff5aea1787 100644 --- a/src/Controller/Api/UserController.php +++ b/src/Controller/Api/UserController.php @@ -2,7 +2,6 @@ namespace App\Controller\Api; -use ApiPlatform\Problem\Serializer\ConstraintViolationListNormalizer; use App\AdherentProfile\Password; use App\Entity\Adherent; use App\Entity\AdherentResetPasswordToken; @@ -86,10 +85,7 @@ public function createPassword( $errors = $validator->validate($password); if (0 !== $errors->count()) { - return JsonResponse::fromJsonString( - $serializer->serialize($errors, ConstraintViolationListNormalizer::FORMAT), - Response::HTTP_BAD_REQUEST - ); + return $this->json($errors, Response::HTTP_BAD_REQUEST); } try { diff --git a/src/Entity/Action/Action.php b/src/Entity/Action/Action.php index c3ae8dd55f8..d5b33431e10 100644 --- a/src/Entity/Action/Action.php +++ b/src/Entity/Action/Action.php @@ -55,16 +55,17 @@ uriTemplate: '/v3/actions/{uuid}', security: "object.getAuthor() == user or user.hasDelegatedFromUser(object.getAuthor(), 'actions')" ), - new Put( + new HttpOperation( + method: 'PUT', uriTemplate: '/v3/actions/{uuid}/cancel', - defaults: ['_api_receive' => false], + deserialize: false, requirements: ['uuid' => '%pattern_uuid%'], controller: CancelActionController::class ), new HttpOperation( method: 'POST|DELETE', uriTemplate: '/v3/actions/{uuid}/register', - defaults: ['_api_receive' => false], + deserialize: false, requirements: ['uuid' => '%pattern_uuid%'], controller: RegisterController::class ), diff --git a/src/Entity/AdherentMessage/AbstractAdherentMessage.php b/src/Entity/AdherentMessage/AbstractAdherentMessage.php index aafb70bb505..10bcb8d1fea 100644 --- a/src/Entity/AdherentMessage/AbstractAdherentMessage.php +++ b/src/Entity/AdherentMessage/AbstractAdherentMessage.php @@ -9,6 +9,7 @@ use ApiPlatform\Metadata\Delete; use ApiPlatform\Metadata\Get; use ApiPlatform\Metadata\GetCollection; +use ApiPlatform\Metadata\HttpOperation; use ApiPlatform\Metadata\Post; use ApiPlatform\Metadata\Put; use App\AdherentMessage\AdherentMessageDataObject; @@ -64,27 +65,31 @@ normalizationContext: ['groups' => ['message_read']], security: "is_granted('REQUEST_SCOPE_GRANTED', 'messages') and (object.getAuthor() == user or user.hasDelegatedFromUser(object.getAuthor(), 'messages'))" ), - new Post( + new HttpOperation( + method: 'POST', uriTemplate: '/v3/adherent_messages/{uuid}/send', - defaults: ['_api_receive' => false], + deserialize: false, requirements: ['uuid' => '%pattern_uuid%'], controller: SendAdherentMessageController::class ), - new Post( + new HttpOperation( + method: 'POST', uriTemplate: '/v3/adherent_messages/{uuid}/send-test', - defaults: ['_api_receive' => false], + deserialize: false, requirements: ['uuid' => '%pattern_uuid%'], controller: SendTestAdherentMessageController::class ), - new Put( + new HttpOperation( + method: 'PUT', uriTemplate: '/v3/adherent_messages/{uuid}/filter', - defaults: ['_api_receive' => false], + deserialize: false, requirements: ['uuid' => '%pattern_uuid%'], controller: UpdateAdherentMessageFilterController::class ), - new Post( + new HttpOperation( + method: 'POST', uriTemplate: '/v3/adherent_messages/{uuid}/duplicate', - defaults: ['_api_receive' => false], + deserialize: false, requirements: ['uuid' => '%pattern_uuid%'], controller: DuplicateMessageController::class ), diff --git a/src/Entity/ElectedRepresentative/ElectedRepresentative.php b/src/Entity/ElectedRepresentative/ElectedRepresentative.php index 0ee54232d3b..ad868cb885b 100644 --- a/src/Entity/ElectedRepresentative/ElectedRepresentative.php +++ b/src/Entity/ElectedRepresentative/ElectedRepresentative.php @@ -131,7 +131,7 @@ class ElectedRepresentative implements EntityAdherentBlameableInterface, EntityA */ #[Groups(['elected_representative_write', 'elected_representative_read'])] #[ORM\Column(type: 'boolean', options: ['default' => false])] - private $hasFollowedTraining = false; + public bool $hasFollowedTraining = false; /** * Mailchimp unsubscribed date diff --git a/src/Entity/ElectedRepresentative/Mandate.php b/src/Entity/ElectedRepresentative/Mandate.php index 3299e4ae323..4d7ca429307 100644 --- a/src/Entity/ElectedRepresentative/Mandate.php +++ b/src/Entity/ElectedRepresentative/Mandate.php @@ -63,7 +63,7 @@ class Mandate */ #[Groups(['elected_mandate_write', 'elected_mandate_read', 'elected_representative_read'])] #[ORM\Column(type: 'boolean', options: ['default' => false])] - private $isElected; + public $isElected; /** * @var Zone|null diff --git a/src/Entity/Event/Event.php b/src/Entity/Event/Event.php index 91f27200e4f..17f9789975f 100644 --- a/src/Entity/Event/Event.php +++ b/src/Entity/Event/Event.php @@ -94,22 +94,22 @@ new Delete( uriTemplate: '/v3/events/{uuid}', requirements: ['uuid' => '%pattern_uuid%'], - openapiContext: ['parameters' => [['name' => 'uuid', 'in' => 'path', 'type' => 'uuid', 'description' => 'The UUID of the Event.', 'example' => 'de7982c4-3729-4f9d-9587-376df25354c3']]], security: "is_granted('REQUEST_SCOPE_GRANTED', 'events') and is_granted('CAN_MANAGE_EVENT', object) and is_granted('CAN_DELETE_EVENT', object)" ), new HttpOperation( method: 'POST|DELETE', uriTemplate: '/v3/events/{uuid}/subscribe', - defaults: ['_api_receive' => false], requirements: ['uuid' => '%pattern_uuid%'], controller: SubscribeAsAdherentController::class, - security: 'is_granted(\'ROLE_USER\')' + security: 'is_granted(\'ROLE_USER\')', + deserialize: false ), - new Post( + new HttpOperation( + method: 'POST', uriTemplate: '/events/{uuid}/subscribe', - defaults: ['_api_receive' => false], requirements: ['uuid' => '%pattern_uuid%'], - controller: SubscribeAsAnonymousController::class + controller: SubscribeAsAnonymousController::class, + deserialize: false ), new HttpOperation( method: 'POST|DELETE', @@ -119,11 +119,12 @@ security: "is_granted('REQUEST_SCOPE_GRANTED', 'events') and is_granted('CAN_MANAGE_EVENT', request.attributes.get('data'))", deserialize: false ), - new Put( + new HttpOperation( + method: 'PUT', uriTemplate: '/v3/events/{uuid}/cancel', - defaults: ['_api_receive' => false], requirements: ['uuid' => '%pattern_uuid%'], - controller: CancelEventController::class + controller: CancelEventController::class, + deserialize: false ), new GetCollection( uriTemplate: '/v3/events', diff --git a/src/Entity/Jecoute/JemarcheDataSurvey.php b/src/Entity/Jecoute/JemarcheDataSurvey.php index 1810e68a268..3004f9b551a 100644 --- a/src/Entity/Jecoute/JemarcheDataSurvey.php +++ b/src/Entity/Jecoute/JemarcheDataSurvey.php @@ -4,6 +4,7 @@ use ApiPlatform\Metadata\ApiResource; use ApiPlatform\Metadata\GetCollection; +use ApiPlatform\Metadata\HttpOperation; use ApiPlatform\Metadata\Post; use App\Controller\Api\Jecoute\JemarcheDataSurveyKpiController; use App\Controller\Api\Jecoute\JemarcheDataSurveyReplyController; @@ -22,9 +23,10 @@ #[ApiResource( operations: [ - new Post( + new HttpOperation( + method: 'POST', uriTemplate: '/v3/jemarche_data_surveys/{uuid}/reply', - defaults: ['_api_receive' => false], + deserialize: false, requirements: ['uuid' => '%pattern_uuid%'], controller: JemarcheDataSurveyReplyController::class, normalizationContext: ['groups' => ['data_survey_read']], diff --git a/src/Entity/Jecoute/News.php b/src/Entity/Jecoute/News.php index d6ad396b623..aa044ac0e95 100644 --- a/src/Entity/Jecoute/News.php +++ b/src/Entity/Jecoute/News.php @@ -49,7 +49,6 @@ new Get( uriTemplate: '/jecoute/news/{uuid}', requirements: ['uuid' => '%pattern_uuid%'], - openapiContext: ['summary' => 'Retrieves a News resource by UUID.', 'description' => 'Retrieves a News resource by UUID.', 'parameters' => [['name' => 'uuid', 'in' => 'path', 'type' => 'string', 'description' => 'The UUID of the News resource.', 'example' => '28']]], security: 'is_granted(\'ROLE_OAUTH_SCOPE_JEMARCHE_APP\')' ), new Get( @@ -66,7 +65,6 @@ ), new GetCollection( uriTemplate: '/jecoute/news', - openapiContext: ['parameters' => [['name' => 'uuid', 'in' => 'query', 'type' => 'string', 'description' => 'Filter News by exact uuid.', 'example' => 'a046adbe-9c7b-56a9-a676-6151a6785dda'], ['name' => 'title', 'in' => 'query', 'type' => 'string', 'description' => 'Filter News by partial title.', 'example' => 'Rassem']]], security: 'is_granted(\'ROLE_OAUTH_SCOPE_JEMARCHE_APP\')' ), new GetCollection( diff --git a/src/Entity/Jecoute/Region.php b/src/Entity/Jecoute/Region.php index c1055d5b184..73f9141bb73 100644 --- a/src/Entity/Jecoute/Region.php +++ b/src/Entity/Jecoute/Region.php @@ -111,7 +111,6 @@ class Region /** * @var bool */ - #[Assert\Type('bool')] #[ORM\Column(type: 'boolean', options: ['default' => true])] private $enabled; diff --git a/src/Entity/Jecoute/Riposte.php b/src/Entity/Jecoute/Riposte.php index 2752e0231da..0e7f3607052 100644 --- a/src/Entity/Jecoute/Riposte.php +++ b/src/Entity/Jecoute/Riposte.php @@ -6,6 +6,7 @@ use ApiPlatform\Metadata\Delete; use ApiPlatform\Metadata\Get; use ApiPlatform\Metadata\GetCollection; +use ApiPlatform\Metadata\HttpOperation; use ApiPlatform\Metadata\Post; use ApiPlatform\Metadata\Put; use App\Controller\Api\Jecoute\IncrementRiposteStatsCounterController; @@ -39,12 +40,13 @@ uriTemplate: '/v3/ripostes/{uuid}', requirements: ['uuid' => '%pattern_uuid%'] ), - new Put( + new HttpOperation( + method: 'PUT', uriTemplate: '/v3/ripostes/{uuid}/action/{action}', - defaults: ['_api_receive' => false], requirements: ['uuid' => '%pattern_uuid%'], controller: IncrementRiposteStatsCounterController::class, - security: "is_granted('REQUEST_SCOPE_GRANTED', 'ripostes') or (is_granted('ROLE_USER') and is_granted('ROLE_OAUTH_SCOPE_JEMARCHE_APP'))" + security: "is_granted('REQUEST_SCOPE_GRANTED', 'ripostes') or (is_granted('ROLE_USER') and is_granted('ROLE_OAUTH_SCOPE_JEMARCHE_APP'))", + deserialize: false, ), new GetCollection( uriTemplate: '/v3/ripostes', @@ -103,7 +105,6 @@ class Riposte implements AuthorInterface, IndexableEntityInterface /** * @var bool */ - #[Assert\Type('bool')] #[Groups(['riposte_list_read', 'riposte_read', 'riposte_write'])] #[ORM\Column(type: 'boolean', options: ['default' => true])] private $withNotification; @@ -111,7 +112,6 @@ class Riposte implements AuthorInterface, IndexableEntityInterface /** * @var bool */ - #[Assert\Type('bool')] #[Groups(['riposte_list_read', 'riposte_read', 'riposte_write'])] #[ORM\Column(type: 'boolean', options: ['default' => true])] private $enabled; diff --git a/src/Entity/MyTeam/MyTeam.php b/src/Entity/MyTeam/MyTeam.php index 12ec7115f1c..4f8445d5f6e 100644 --- a/src/Entity/MyTeam/MyTeam.php +++ b/src/Entity/MyTeam/MyTeam.php @@ -4,7 +4,7 @@ use ApiPlatform\Metadata\ApiResource; use ApiPlatform\Metadata\GetCollection; -use ApiPlatform\Metadata\Post; +use ApiPlatform\Metadata\HttpOperation; use App\Api\Filter\MyTeamScopeFilter; use App\Controller\Api\MyTeam\InitializeMyTeamController; use App\Entity\Adherent; @@ -26,9 +26,10 @@ uriTemplate: '/v3/my_teams', normalizationContext: ['groups' => ['my_team_read_list']] ), - new Post( + new HttpOperation( + method: 'POST', uriTemplate: '/v3/my_teams', - defaults: ['_api_receive' => false], + deserialize: false, controller: InitializeMyTeamController::class ), ], diff --git a/src/Entity/Pap/Campaign.php b/src/Entity/Pap/Campaign.php index af1994d9d2c..6230d7a7cb9 100644 --- a/src/Entity/Pap/Campaign.php +++ b/src/Entity/Pap/Campaign.php @@ -60,7 +60,6 @@ ), new Get( uriTemplate: '/v3/pap_campaigns/{uuid}/questioners', - defaults: ['_api_receive' => false], requirements: ['uuid' => '%pattern_uuid%'], controller: GetPapCampaignQuestionersStatsController::class, security: "is_granted('REQUEST_SCOPE_GRANTED', ['pap_v2', 'pap'])" diff --git a/src/Entity/Pap/CampaignHistory.php b/src/Entity/Pap/CampaignHistory.php index bbe9e96396e..12994b10ef9 100644 --- a/src/Entity/Pap/CampaignHistory.php +++ b/src/Entity/Pap/CampaignHistory.php @@ -8,6 +8,7 @@ use ApiPlatform\Metadata\ApiFilter; use ApiPlatform\Metadata\ApiResource; use ApiPlatform\Metadata\GetCollection; +use ApiPlatform\Metadata\HttpOperation; use ApiPlatform\Metadata\Post; use ApiPlatform\Metadata\Put; use App\Api\Filter\AdherentIdentityFilter; @@ -42,9 +43,10 @@ requirements: ['uuid' => '%pattern_uuid%'], security: 'is_granted(\'ROLE_OAUTH_SCOPE_JEMARCHE_APP\') and object.getQuestioner() == user' ), - new Post( + new HttpOperation( + method: 'POST', uriTemplate: '/v3/pap_campaign_histories/{uuid}/reply', - defaults: ['_api_receive' => false], + deserialize: false, requirements: ['uuid' => '%pattern_uuid%'], controller: CampaignHistoryReplyController::class, normalizationContext: ['groups' => ['data_survey_read']] diff --git a/src/Entity/Phoning/Campaign.php b/src/Entity/Phoning/Campaign.php index 45016b4d83b..70624244c15 100644 --- a/src/Entity/Phoning/Campaign.php +++ b/src/Entity/Phoning/Campaign.php @@ -63,7 +63,6 @@ ), new Get( uriTemplate: '/v3/phoning_campaigns/{uuid}/callers', - defaults: ['_api_receive' => false], requirements: ['uuid' => '%pattern_uuid%'], controller: GetPhoningCampaignCallersStatsController::class, security: "is_granted('REQUEST_SCOPE_GRANTED', 'phoning_campaign')" diff --git a/src/Entity/Phoning/CampaignHistory.php b/src/Entity/Phoning/CampaignHistory.php index 80da23a8c06..66ea0a03f12 100644 --- a/src/Entity/Phoning/CampaignHistory.php +++ b/src/Entity/Phoning/CampaignHistory.php @@ -7,7 +7,7 @@ use ApiPlatform\Metadata\ApiFilter; use ApiPlatform\Metadata\ApiResource; use ApiPlatform\Metadata\GetCollection; -use ApiPlatform\Metadata\Post; +use ApiPlatform\Metadata\HttpOperation; use ApiPlatform\Metadata\Put; use App\Api\Filter\AdherentIdentityFilter; use App\Controller\Api\Phoning\CampaignHistoryReplyController; @@ -37,9 +37,10 @@ requirements: ['uuid' => '%pattern_uuid%'], security: 'is_granted(\'IS_CAMPAIGN_HISTORY_CALLER\', object)' ), - new Post( + new HttpOperation( + method: 'POST', uriTemplate: '/v3/phoning_campaign_histories/{uuid}/reply', - defaults: ['_api_receive' => false], + deserialize: false, requirements: ['uuid' => '%pattern_uuid%'], controller: CampaignHistoryReplyController::class, normalizationContext: ['groups' => ['data_survey_read']] diff --git a/src/Entity/ProcurationV2/Request.php b/src/Entity/ProcurationV2/Request.php index 62a161b1047..581d77fefc0 100644 --- a/src/Entity/ProcurationV2/Request.php +++ b/src/Entity/ProcurationV2/Request.php @@ -8,8 +8,8 @@ use ApiPlatform\Metadata\ApiResource; use ApiPlatform\Metadata\Get; use ApiPlatform\Metadata\GetCollection; +use ApiPlatform\Metadata\HttpOperation; use ApiPlatform\Metadata\Patch; -use ApiPlatform\Metadata\Post; use App\Api\Filter\OrTextSearchFilter; use App\Api\Filter\ProcurationZoneFilter; use App\Controller\Api\Procuration\GetMatchedProxiesController; @@ -42,17 +42,19 @@ requirements: ['uuid' => '%pattern_uuid%'], normalizationContext: ['groups' => ['procuration_request_read'], 'enable_tag_translator' => true] ), - new Post( + new HttpOperation( + method: 'POST', uriTemplate: '/requests/{uuid}/match', - defaults: ['_api_receive' => false], requirements: ['uuid' => '%pattern_uuid%'], - controller: MatchRequestWithProxyController::class + controller: MatchRequestWithProxyController::class, + deserialize: false ), - new Post( + new HttpOperation( + method: 'POST', uriTemplate: '/requests/{uuid}/unmatch', - defaults: ['_api_receive' => false], requirements: ['uuid' => '%pattern_uuid%'], - controller: UnmatchRequestAndProxyController::class + controller: UnmatchRequestAndProxyController::class, + deserialize: false ), new Patch( uriTemplate: '/requests/{uuid}', diff --git a/src/Entity/VotingPlatform/Designation/Designation.php b/src/Entity/VotingPlatform/Designation/Designation.php index 9d47a53f46b..21e3d494565 100644 --- a/src/Entity/VotingPlatform/Designation/Designation.php +++ b/src/Entity/VotingPlatform/Designation/Designation.php @@ -7,6 +7,7 @@ use ApiPlatform\Metadata\ApiResource; use ApiPlatform\Metadata\Get; use ApiPlatform\Metadata\GetCollection; +use ApiPlatform\Metadata\HttpOperation; use ApiPlatform\Metadata\Post; use ApiPlatform\Metadata\Put; use App\Api\Filter\InZoneOfScopeFilter; @@ -54,12 +55,13 @@ security: "is_granted('REQUEST_SCOPE_GRANTED', 'designation')", validationContext: ['groups' => UpdateDesignationGroupGenerator::class] ), - new Put( + new HttpOperation( + method: 'PUT', uriTemplate: '/designations/{uuid}/cancel', - defaults: ['_api_receive' => false], requirements: ['uuid' => '%pattern_uuid%'], controller: CancelElectionController::class, - security: "is_granted('REQUEST_SCOPE_GRANTED', 'designation')" + security: "is_granted('REQUEST_SCOPE_GRANTED', 'designation')", + deserialize: false ), new GetCollection(normalizationContext: ['groups' => ['designation_list']]), new Post(validationContext: ['groups' => ['api_designation_write']]), @@ -273,7 +275,7 @@ class Designation implements EntityAdministratorBlameableInterface, EntityAdhere #[Groups(['designation_read', 'designation_list'])] #[ORM\Column(type: 'boolean', options: ['default' => false])] - private bool $isCanceled = false; + public bool $isCanceled = false; #[ORM\Column(nullable: true)] public ?string $alertTitle = null; diff --git a/src/Normalizer/AdherentNormalizer.php b/src/Normalizer/AdherentNormalizer.php index afc6256927a..20b69f2630f 100644 --- a/src/Normalizer/AdherentNormalizer.php +++ b/src/Normalizer/AdherentNormalizer.php @@ -37,8 +37,6 @@ public function __construct( 'last_name' => 'lastName', ]; - protected const ALREADY_CALLED = 'ADHERENT_NORMALIZER_ALREADY_CALLED'; - /** * @param Adherent $object * @@ -46,9 +44,7 @@ public function __construct( */ public function normalize($object, $format = null, array $context = []): array|string|int|float|bool|\ArrayObject|null { - $context[static::ALREADY_CALLED] = true; - - $data = $this->normalizer->normalize($object, $format, $context); + $data = $this->normalizer->normalize($object, $format, $context + [__CLASS__ => true]); $groups = $context['groups'] ?? []; if (\in_array('legacy', $groups)) { @@ -117,7 +113,7 @@ public function getSupportedTypes(?string $format): array public function supportsNormalization($data, $format = null, array $context = []): bool { - return !isset($context[static::ALREADY_CALLED]) && $data instanceof Adherent; + return !isset($context[__CLASS__]) && $data instanceof Adherent; } protected function addBackwardCompatibilityFields(array $data): array diff --git a/src/Normalizer/CommitteeCandidacyDenormalizer.php b/src/Normalizer/CommitteeCandidacyDenormalizer.php index 06ce2b6f49e..69e921ac8de 100644 --- a/src/Normalizer/CommitteeCandidacyDenormalizer.php +++ b/src/Normalizer/CommitteeCandidacyDenormalizer.php @@ -2,7 +2,7 @@ namespace App\Normalizer; -use ApiPlatform\Exception\ItemNotFoundException; +use ApiPlatform\Metadata\Exception\ItemNotFoundException; use App\Entity\CommitteeCandidacy; use App\Repository\CommitteeCandidaciesGroupRepository; use App\Repository\CommitteeMembershipRepository; @@ -50,6 +50,6 @@ public function getSupportedTypes(?string $format): array public function supportsDenormalization($data, string $type, ?string $format = null, array $context = []): bool { - return CommitteeCandidacy::class === $type && '_api_/committee_candidacies_post' === $context['operation_name']; + return CommitteeCandidacy::class === $type && '_api_/v3/committee_candidacies_post' === $context['operation_name']; } } diff --git a/src/Normalizer/ConstraintViolationListNormalizer.php b/src/Normalizer/ConstraintViolationListNormalizer.php index d0eca02fe06..813764a6dd7 100644 --- a/src/Normalizer/ConstraintViolationListNormalizer.php +++ b/src/Normalizer/ConstraintViolationListNormalizer.php @@ -2,6 +2,7 @@ namespace App\Normalizer; +use ApiPlatform\Validator\Exception\ConstraintViolationListAwareExceptionInterface; use Symfony\Component\Serializer\NameConverter\NameConverterInterface; use Symfony\Component\Serializer\Normalizer\NormalizerInterface; use Symfony\Component\Validator\ConstraintViolationListInterface; @@ -14,6 +15,10 @@ public function __construct(private readonly NameConverterInterface $nameConvert public function normalize($object, ?string $format = null, array $context = []): array|string|int|float|bool|\ArrayObject|null { + if ($object instanceof ConstraintViolationListAwareExceptionInterface) { + $object = $object->getConstraintViolationList(); + } + $violations = []; foreach ($object as $violation) { @@ -38,11 +43,12 @@ public function getSupportedTypes(?string $format): array return [ '*' => null, ConstraintViolationListInterface::class => true, + ConstraintViolationListAwareExceptionInterface::class => true, ]; } public function supportsNormalization($data, ?string $format = null, array $context = []): bool { - return $data instanceof ConstraintViolationListInterface; + return $data instanceof ConstraintViolationListInterface || $data instanceof ConstraintViolationListAwareExceptionInterface; } } diff --git a/src/Normalizer/DesignationDenormalizer.php b/src/Normalizer/DesignationDenormalizer.php index 815c7cf46a1..402348bb682 100644 --- a/src/Normalizer/DesignationDenormalizer.php +++ b/src/Normalizer/DesignationDenormalizer.php @@ -59,6 +59,6 @@ public function supportsDenormalization($data, string $type, ?string $format = n { return !isset($context[__CLASS__]) && is_a($type, Designation::class, true) - && \in_array($context['operation_name'] ?? null, ['_api_/designations.{_format}_post', '_api_/designations/{uuid}_put'], true); + && \in_array($context['operation_name'] ?? null, ['_api_/v3/designations{._format}_post', '_api_/v3/designations/{uuid}_put'], true); } } diff --git a/src/Normalizer/DeviceNormalizer.php b/src/Normalizer/DeviceNormalizer.php index 43a2a5e8747..f7351e3ef2d 100644 --- a/src/Normalizer/DeviceNormalizer.php +++ b/src/Normalizer/DeviceNormalizer.php @@ -11,8 +11,6 @@ class DeviceNormalizer implements NormalizerInterface, NormalizerAwareInterface { use NormalizerAwareTrait; - protected const ALREADY_CALLED = 'DEVICE_NORMALIZER_ALREADY_CALLED'; - /** * @param Device $object * @@ -20,9 +18,7 @@ class DeviceNormalizer implements NormalizerInterface, NormalizerAwareInterface */ public function normalize($object, $format = null, array $context = []): array|string|int|float|bool|\ArrayObject|null { - $context[static::ALREADY_CALLED] = true; - - return $this->normalizer->normalize($object, $format, $context); + return $this->normalizer->normalize($object, $format, $context + [__CLASS__ => true]); } public function getSupportedTypes(?string $format): array @@ -35,6 +31,6 @@ public function getSupportedTypes(?string $format): array public function supportsNormalization($data, $format = null, array $context = []): bool { - return !isset($context[static::ALREADY_CALLED]) && $data instanceof Device; + return !isset($context[__CLASS__]) && $data instanceof Device; } } diff --git a/src/Normalizer/DocumentNormalizer.php b/src/Normalizer/DocumentNormalizer.php index e3e339d5dc2..19113989ee3 100644 --- a/src/Normalizer/DocumentNormalizer.php +++ b/src/Normalizer/DocumentNormalizer.php @@ -54,7 +54,7 @@ public function supportsNormalization($data, $format = null, array $context = [] private function getUrl(Document $document): string { return $this->urlGenerator->generate( - '_api_/documents/{uuid}/file_get', + '_api_/v3/documents/{uuid}/file_get', ['uuid' => $document->getUuid()], UrlGeneratorInterface::ABSOLUTE_URL ); diff --git a/src/Normalizer/DonationValueObjectNormalizer.php b/src/Normalizer/DonationValueObjectNormalizer.php index 8a6a16915fb..7867a6c6075 100644 --- a/src/Normalizer/DonationValueObjectNormalizer.php +++ b/src/Normalizer/DonationValueObjectNormalizer.php @@ -16,8 +16,6 @@ public function __construct(private readonly TranslatorInterface $translator) { } - protected const ALREADY_CALLED = 'DONATION_VALUE_OBJECT_NORMALIZER_ALREADY_CALLED'; - /** * @param DonationValueObject $object * @@ -25,9 +23,7 @@ public function __construct(private readonly TranslatorInterface $translator) */ public function normalize($object, $format = null, array $context = []): array|string|int|float|bool|\ArrayObject|null { - $context[static::ALREADY_CALLED] = true; - - $data = $this->normalizer->normalize($object, $format, $context); + $data = $this->normalizer->normalize($object, $format, $context + [__CLASS__ => true]); $groups = $context['groups'] ?? []; if (\in_array('donation_read', $groups)) { @@ -47,7 +43,7 @@ public function getSupportedTypes(?string $format): array public function supportsNormalization($data, $format = null, array $context = []): bool { - return !isset($context[static::ALREADY_CALLED]) && $data instanceof DonationValueObject; + return !isset($context[__CLASS__]) && $data instanceof DonationValueObject; } private function translateDonationType(string $donationType): string diff --git a/src/Normalizer/ElectedMandateDenormalizer.php b/src/Normalizer/ElectedMandateDenormalizer.php index 2d91fe0d81b..8c896ccd5b2 100644 --- a/src/Normalizer/ElectedMandateDenormalizer.php +++ b/src/Normalizer/ElectedMandateDenormalizer.php @@ -54,6 +54,6 @@ public function supportsDenormalization($data, string $type, ?string $format = n { return !isset($context[__CLASS__]) && is_a($type, Mandate::class, true) - && '_api_/elected_mandates_post' === ($context['operation_name'] ?? null); + && '_api_/v3/elected_mandates_post' === ($context['operation_name'] ?? null); } } diff --git a/src/Normalizer/ElectedRepresentativeAdherentMandateNormalizer.php b/src/Normalizer/ElectedRepresentativeAdherentMandateNormalizer.php index 00a035686bf..4f238c5b128 100644 --- a/src/Normalizer/ElectedRepresentativeAdherentMandateNormalizer.php +++ b/src/Normalizer/ElectedRepresentativeAdherentMandateNormalizer.php @@ -25,9 +25,7 @@ public function __construct(private readonly TranslatorInterface $translator) */ public function normalize($object, $format = null, array $context = []): array|string|int|float|bool|\ArrayObject|null { - $context[static::ALREADY_CALLED] = true; - - $data = $this->normalizer->normalize($object, $format, $context); + $data = $this->normalizer->normalize($object, $format, $context + [__CLASS__ => true]); $groups = $context['groups'] ?? []; if (\in_array('adherent_elect_read', $groups)) { @@ -47,7 +45,7 @@ public function getSupportedTypes(?string $format): array public function supportsNormalization($data, $format = null, array $context = []): bool { - return !isset($context[static::ALREADY_CALLED]) && $data instanceof ElectedRepresentativeAdherentMandate; + return !isset($context[__CLASS__]) && $data instanceof ElectedRepresentativeAdherentMandate; } private function translateMandateType(string $mandateType): string diff --git a/src/Normalizer/ElectedRepresentativePoliticalFunctionFromIdDenormalizer.php b/src/Normalizer/ElectedRepresentativePoliticalFunctionFromIdDenormalizer.php index f29214ae14e..ffd359706cf 100644 --- a/src/Normalizer/ElectedRepresentativePoliticalFunctionFromIdDenormalizer.php +++ b/src/Normalizer/ElectedRepresentativePoliticalFunctionFromIdDenormalizer.php @@ -2,7 +2,7 @@ namespace App\Normalizer; -use ApiPlatform\Exception\ItemNotFoundException; +use ApiPlatform\Metadata\Exception\ItemNotFoundException; use App\Entity\ElectedRepresentative\PoliticalFunction; use App\Repository\ElectedRepresentative\PoliticalFunctionRepository; use Symfony\Component\Serializer\Normalizer\DenormalizerInterface; diff --git a/src/Normalizer/EntityCategoryFromSlugDenormalizer.php b/src/Normalizer/EntityCategoryFromSlugDenormalizer.php index ee31e3e6dd4..9468922df69 100644 --- a/src/Normalizer/EntityCategoryFromSlugDenormalizer.php +++ b/src/Normalizer/EntityCategoryFromSlugDenormalizer.php @@ -2,7 +2,7 @@ namespace App\Normalizer; -use ApiPlatform\Exception\ItemNotFoundException; +use ApiPlatform\Metadata\Exception\ItemNotFoundException; use App\Entity\Event\EventCategory; use App\Repository\EventCategoryRepository; use Symfony\Component\Serializer\Normalizer\DenormalizerInterface; diff --git a/src/Normalizer/EntityFromUuidDenormalizer.php b/src/Normalizer/EntityFromUuidDenormalizer.php index 07f4a751b7b..3baa8e5b6ae 100644 --- a/src/Normalizer/EntityFromUuidDenormalizer.php +++ b/src/Normalizer/EntityFromUuidDenormalizer.php @@ -2,7 +2,7 @@ namespace App\Normalizer; -use ApiPlatform\Exception\ItemNotFoundException; +use ApiPlatform\Metadata\Exception\ItemNotFoundException; use Doctrine\ORM\EntityManagerInterface; use Ramsey\Uuid\Uuid; use Symfony\Component\Serializer\Normalizer\DenormalizerInterface; diff --git a/src/Normalizer/FormationNormalizer.php b/src/Normalizer/FormationNormalizer.php index c5db38558b0..91498141a6a 100644 --- a/src/Normalizer/FormationNormalizer.php +++ b/src/Normalizer/FormationNormalizer.php @@ -59,7 +59,7 @@ private function getUrl(Formation $formation): string $parameters['scope'] = $scope->getCode(); } - return $this->urlGenerator->generate('_api_/formations/{uuid}/file_get', $parameters, UrlGeneratorInterface::ABSOLUTE_URL); + return $this->urlGenerator->generate('_api_/v3/formations/{uuid}/file_get', $parameters, UrlGeneratorInterface::ABSOLUTE_URL); } private function getCurrentScope(): ?ScopeGeneratorInterface diff --git a/src/Normalizer/GeneralMeetingReportNormalizer.php b/src/Normalizer/GeneralMeetingReportNormalizer.php index 952b039dded..f635ea93bd8 100644 --- a/src/Normalizer/GeneralMeetingReportNormalizer.php +++ b/src/Normalizer/GeneralMeetingReportNormalizer.php @@ -59,7 +59,7 @@ private function getUrl(GeneralMeetingReport $generalMeetingReport): string $parameters['scope'] = $scope->getCode(); } - return $this->urlGenerator->generate('_api_/general_meeting_reports/{uuid}/file_get', $parameters, UrlGeneratorInterface::ABSOLUTE_URL); + return $this->urlGenerator->generate('_api_/v3/general_meeting_reports/{uuid}/file_get', $parameters, UrlGeneratorInterface::ABSOLUTE_URL); } private function getCurrentScope(): ?ScopeGeneratorInterface diff --git a/src/Normalizer/ItemNormalizerDecorator.php b/src/Normalizer/ItemNormalizerDecorator.php deleted file mode 100644 index d703234951f..00000000000 --- a/src/Normalizer/ItemNormalizerDecorator.php +++ /dev/null @@ -1,844 +0,0 @@ -iriConverter->getIriFromResource($object); - }; - } - - if (!interface_exists(AdvancedNameConverterInterface::class) && method_exists($this, 'setCircularReferenceHandler')) { - $this->setCircularReferenceHandler($defaultContext['circular_reference_handler']); - } - - parent::__construct($classMetadataFactory, $nameConverter, null, null, \Closure::fromCallable([$this, 'getObjectClass']), $defaultContext); - - $this->propertyNameCollectionFactory = $propertyNameCollectionFactory; - $this->propertyMetadataFactory = $propertyMetadataFactory; - $this->iriConverter = $iriConverter; - $this->resourceClassResolver = $resourceClassResolver; - $this->propertyAccessor = $propertyAccessor ?: PropertyAccess::createPropertyAccessor(); - $this->itemDataProvider = $itemDataProvider; - $this->allowPlainIdentifiers = $allowPlainIdentifiers; - $this->dataTransformers = $dataTransformers; - $this->resourceMetadataFactory = $resourceMetadataFactory; - $this->resourceAccessChecker = $resourceAccessChecker; - } - - public function getSupportedTypes(?string $format): array - { - return [ - '*' => false, - ]; - } - - public function supportsNormalization($data, $format = null, array $context = []): bool - { - if (!\is_object($data) || is_iterable($data)) { - return false; - } - - $class = $this->getObjectClass($data); - if (($context['output']['class'] ?? null) === $class) { - return true; - } - - return $this->resourceClassResolver->isResourceClass($class); - } - - public function hasCacheableSupportsMethod(): bool - { - return false; - } - - public function normalize($object, $format = null, array $context = []): array|string|int|float|bool|\ArrayObject|null - { - $resourceClass = $this->getObjectClass($object); - if (!($isTransformed = isset($context[AbstractItemNormalizer::IS_TRANSFORMED_TO_SAME_CLASS])) && $outputClass = $this->getOutputClass($resourceClass, $context)) { - if (!$this->serializer instanceof NormalizerInterface) { - throw new LogicException('Cannot normalize the output because the injected serializer is not a normalizer'); - } - - // Data transformers are deprecated, this is removed from 3.0 - if ($dataTransformer = $this->getDataTransformer($object, $outputClass, $context)) { - $transformed = $dataTransformer->transform($object, $outputClass, $context); - - if ($object === $transformed) { - $context[AbstractItemNormalizer::IS_TRANSFORMED_TO_SAME_CLASS] = true; - } else { - $context['api_normalize'] = true; - $context['api_resource'] = $object; - unset($context['output'], $context['resource_class']); - } - - return $this->serializer->normalize($transformed, $format, $context); - } - - unset($context['output'], $context['operation_name']); - if ($this->resourceMetadataFactory instanceof ResourceMetadataCollectionFactoryInterface && !isset($context['operation'])) { - $context['operation'] = $this->resourceMetadataFactory->create($context['resource_class'])->getOperation(); - } - $context['resource_class'] = $outputClass; - $context['api_sub_level'] = true; - $context[self::ALLOW_EXTRA_ATTRIBUTES] = false; - - return $this->serializer->normalize($object, $format, $context); - } - - if ($isTransformed) { - unset($context[AbstractItemNormalizer::IS_TRANSFORMED_TO_SAME_CLASS]); - } - - $iri = null; - if ($this->resourceClassResolver->isResourceClass($resourceClass)) { - $context = $this->initContext($resourceClass, $context); - } - - $context['iri'] = true; - $context['api_normalize'] = true; - - $emptyResourceAsIri = $context['api_empty_resource_as_iri'] ?? false; - unset($context['api_empty_resource_as_iri']); - - if (isset($context['resources'])) { - $context['resources'][$iri] = $iri; - } - - $data = parent::normalize($object, $format, $context); - - if ($emptyResourceAsIri && \is_array($data) && 0 === \count($data)) { - return $iri; - } - - return $data; - } - - public function supportsDenormalization($data, $type, $format = null, array $context = []): bool - { - if (!isset($context['resource_class'])) { - return false; - } - - if (($context['input']['class'] ?? null) === $type) { - return true; - } - - return $this->localCache[$type] ?? $this->localCache[$type] = $this->resourceClassResolver->isResourceClass($type); - } - - public function denormalize($data, $class, $format = null, array $context = []): mixed - { - // Avoid issues with proxies if we populated the object - if (isset($data['id']) && !isset($context[self::OBJECT_TO_POPULATE])) { - if (isset($context['api_allow_update']) && true !== $context['api_allow_update']) { - throw new NotNormalizableValueException('Update is not allowed for this operation.'); - } - - if (isset($context['resource_class'])) { - $this->updateObjectToPopulate($data, $context); - } - } - - $resourceClass = $class; - - if (null !== $inputClass = $this->getInputClass($resourceClass, $context)) { - if (null !== $dataTransformer = $this->getDataTransformer($data, $resourceClass, $context)) { - $dataTransformerContext = $context; - - unset($context['input']); - unset($context['resource_class']); - - if (!$this->serializer instanceof DenormalizerInterface) { - throw new LogicException('Cannot denormalize the input because the injected serializer is not a denormalizer'); - } - - if ($dataTransformer instanceof DataTransformerInitializerInterface) { - $context[AbstractNormalizer::OBJECT_TO_POPULATE] = $dataTransformer->initialize($inputClass, $context); - $context[AbstractObjectNormalizer::DEEP_OBJECT_TO_POPULATE] = true; - } - - try { - $denormalizedInput = $this->serializer->denormalize($data, $inputClass, $format, $context); - } catch (NotNormalizableValueException $e) { - throw new UnexpectedValueException('The input data is misformatted.', $e->getCode(), $e); - } - - if (!\is_object($denormalizedInput)) { - throw new UnexpectedValueException('Expected denormalized input to be an object.'); - } - - return $dataTransformer->transform($denormalizedInput, $resourceClass, $dataTransformerContext); - } - - unset($context['input']); - unset($context['operation']); - unset($context['operation_name']); - $context['resource_class'] = $inputClass; - - if (!$this->serializer instanceof DenormalizerInterface) { - throw new LogicException('Cannot denormalize the input because the injected serializer is not a denormalizer'); - } - - try { - return $this->serializer->denormalize($data, $inputClass, $format, $context); - } catch (NotNormalizableValueException $e) { - throw new UnexpectedValueException('The input data is misformatted.', $e->getCode(), $e); - } - } - - if (null === $objectToPopulate = $this->extractObjectToPopulate($class, $context, static::OBJECT_TO_POPULATE)) { - $normalizedData = \is_scalar($data) ? [$data] : $this->prepareForDenormalization($data); - $class = $this->getClassDiscriminatorResolvedClass($normalizedData, $class); - } - - $context['api_denormalize'] = true; - - if ($this->resourceClassResolver->isResourceClass($class)) { - $newResourceClass = $this->resourceClassResolver->getResourceClass($objectToPopulate, $class); - if (!(new \ReflectionClass($newResourceClass))->isAbstract()) { - $context['resource_class'] = $resourceClass = $newResourceClass; - } - } - - $supportsPlainIdentifiers = $this->supportsPlainIdentifiers(); - - if (\is_string($data)) { - try { - return $this->iriConverter->getResourceFromIri($data, $context + ['fetch_data' => true]); - } catch (ItemNotFoundException $e) { - if (!$supportsPlainIdentifiers) { - throw new UnexpectedValueException($e->getMessage(), $e->getCode(), $e); - } - } catch (InvalidArgumentException $e) { - if (!$supportsPlainIdentifiers) { - throw new UnexpectedValueException(\sprintf('Invalid IRI "%s".', $data), $e->getCode(), $e); - } - } - } - - if (!\is_array($data)) { - if (!$supportsPlainIdentifiers) { - throw new UnexpectedValueException(\sprintf('Expected IRI or document for resource "%s", "%s" given.', $resourceClass, \gettype($data))); - } - - $item = $this->itemDataProvider->getItem($resourceClass, $data, null, $context + ['fetch_data' => true]); - if (null === $item) { - throw new ItemNotFoundException(\sprintf('Item not found for resource "%s" with id "%s".', $resourceClass, $data)); - } - - return $item; - } - - $previousObject = null !== $objectToPopulate ? clone $objectToPopulate : null; - $object = parent::denormalize($data, $resourceClass, $format, $context); - - if (!$this->resourceClassResolver->isResourceClass($context['resource_class'])) { - return $object; - } - - // Revert attributes that aren't allowed to be changed after a post-denormalize check - foreach (array_keys($data) as $attribute) { - if (!$this->canAccessAttributePostDenormalize($object, $previousObject, $attribute, $context)) { - if (null !== $previousObject) { - $this->setValue($object, $attribute, $this->propertyAccessor->getValue($previousObject, $attribute)); - } else { - $propertyMetadata = $this->propertyMetadataFactory->create($resourceClass, $attribute, $this->getFactoryOptions($context)); - $this->setValue($object, $attribute, $propertyMetadata->getDefault()); - } - } - } - - return $object; - } - - protected function getClassDiscriminatorResolvedClass(array &$data, string $class): string - { - if (null === $this->classDiscriminatorResolver || (null === $mapping = $this->classDiscriminatorResolver->getMappingForClass($class))) { - return $class; - } - - if (!isset($data[$mapping->getTypeProperty()])) { - throw new RuntimeException(\sprintf('Type property "%s" not found for the abstract object "%s"', $mapping->getTypeProperty(), $class)); - } - - $type = $data[$mapping->getTypeProperty()]; - if (null === ($mappedClass = $mapping->getClassForType($type))) { - throw new RuntimeException(\sprintf('The type "%s" has no mapped class for the abstract object "%s"', $type, $class)); - } - - return $mappedClass; - } - - protected function extractAttributes($object, $format = null, array $context = []) - { - return []; - } - - protected function isAllowedAttribute($classOrObject, $attribute, $format = null, array $context = []) - { - if (!parent::isAllowedAttribute($classOrObject, $attribute, $format, $context)) { - return false; - } - - return $this->canAccessAttribute(\is_object($classOrObject) ? $classOrObject : null, $attribute, $context); - } - - protected function canAccessAttribute($object, string $attribute, array $context = []): bool - { - if (!$this->resourceClassResolver->isResourceClass($context['resource_class'])) { - return true; - } - - $options = $this->getFactoryOptions($context); - /** @var PropertyMetadata|ApiProperty */ - $propertyMetadata = $this->propertyMetadataFactory->create($context['resource_class'], $attribute, $options); - $security = $propertyMetadata instanceof PropertyMetadata ? $propertyMetadata->getAttribute('security') : $propertyMetadata->getSecurity(); - if ($this->resourceAccessChecker && $security) { - return $this->resourceAccessChecker->isGranted($context['resource_class'], $security, [ - 'object' => $object, - ]); - } - - return true; - } - - protected function canAccessAttributePostDenormalize( - $object, - $previousObject, - string $attribute, - array $context = [], - ): bool { - $options = $this->getFactoryOptions($context); - /** @var PropertyMetadata|ApiProperty */ - $propertyMetadata = $this->propertyMetadataFactory->create($context['resource_class'], $attribute, $options); - $security = $propertyMetadata instanceof PropertyMetadata ? $propertyMetadata->getAttribute('security_post_denormalize') : $propertyMetadata->getSecurityPostDenormalize(); - if ($this->resourceAccessChecker && $security) { - return $this->resourceAccessChecker->isGranted($context['resource_class'], $security, [ - 'object' => $object, - 'previous_object' => $previousObject, - ]); - } - - return true; - } - - protected function setAttributeValue($object, $attribute, $value, $format = null, array $context = []) - { - $this->setValue($object, $attribute, $this->createAttributeValue($attribute, $value, $format, $context)); - } - - protected function validateType(string $attribute, Type $type, $value, ?string $format = null) - { - $builtinType = $type->getBuiltinType(); - if (Type::BUILTIN_TYPE_FLOAT === $builtinType && null !== $format && str_contains($format, 'json')) { - $isValid = \is_float($value) || \is_int($value); - } else { - $isValid = \call_user_func('is_'.$builtinType, $value); - } - - if (!$isValid) { - throw new UnexpectedValueException(\sprintf('The type of the "%s" attribute must be "%s", "%s" given.', $attribute, $builtinType, \gettype($value))); - } - } - - protected function denormalizeCollection( - string $attribute, - $propertyMetadata, - Type $type, - string $className, - $value, - ?string $format, - array $context, - ): array { - if (!\is_array($value)) { - throw new InvalidArgumentException(\sprintf('The type of the "%s" attribute must be "array", "%s" given.', $attribute, \gettype($value))); - } - - $collectionKeyType = method_exists(Type::class, 'getCollectionKeyTypes') ? ($type->getCollectionKeyTypes()[0] ?? null) : $type->getCollectionKeyType(); - $collectionKeyBuiltinType = null === $collectionKeyType ? null : $collectionKeyType->getBuiltinType(); - - $values = []; - foreach ($value as $index => $obj) { - if (null !== $collectionKeyBuiltinType && !\call_user_func('is_'.$collectionKeyBuiltinType, $index)) { - throw new InvalidArgumentException(\sprintf('The type of the key "%s" must be "%s", "%s" given.', $index, $collectionKeyBuiltinType, \gettype($index))); - } - - $values[$index] = $this->denormalizeRelation($attribute, $propertyMetadata, $className, $obj, $format, $this->createChildContext($context, $attribute, $format)); - } - - return $values; - } - - protected function denormalizeRelation( - string $attributeName, - $propertyMetadata, - string $className, - $value, - ?string $format, - array $context, - ) { - $supportsPlainIdentifiers = $this->supportsPlainIdentifiers(); - - if (\is_string($value)) { - try { - return $this->iriConverter->getResourceFromIri($value, $context + ['fetch_data' => true]); - } catch (ItemNotFoundException $e) { - if (!$supportsPlainIdentifiers) { - throw new UnexpectedValueException($e->getMessage(), $e->getCode(), $e); - } - } catch (InvalidArgumentException $e) { - if (!$supportsPlainIdentifiers) { - throw new UnexpectedValueException(\sprintf('Invalid IRI "%s".', $value), $e->getCode(), $e); - } - } - } - - if ($propertyMetadata->isWritableLink()) { - $context['api_allow_update'] = true; - - if (!$this->serializer instanceof DenormalizerInterface) { - throw new LogicException(\sprintf('The injected serializer must be an instance of "%s".', DenormalizerInterface::class)); - } - - try { - $item = $this->serializer->denormalize($value, $className, $format, $context); - if (!\is_object($item) && null !== $item) { - throw new \UnexpectedValueException('Expected item to be an object or null.'); - } - - return $item; - } catch (InvalidValueException $e) { - if (!$supportsPlainIdentifiers) { - throw $e; - } - } - } - - if (!\is_array($value)) { - if (!$supportsPlainIdentifiers) { - throw new UnexpectedValueException(\sprintf('Expected IRI or nested document for attribute "%s", "%s" given.', $attributeName, \gettype($value))); - } - - $item = $this->itemDataProvider->getItem($className, $value, null, $context + ['fetch_data' => true]); - if (null === $item) { - throw new ItemNotFoundException(\sprintf('Item not found for resource "%s" with id "%s".', $className, $value)); - } - - return $item; - } - - throw new UnexpectedValueException(\sprintf('Nested documents for attribute "%s" are not allowed. Use IRIs instead.', $attributeName)); - } - - protected function getFactoryOptions(array $context): array - { - $options = []; - - if (isset($context[self::GROUPS])) { - /* @see https://github.com/symfony/symfony/blob/v4.2.6/src/Symfony/Component/PropertyInfo/Extractor/SerializerExtractor.php */ - $options['serializer_groups'] = (array) $context[self::GROUPS]; - } - - if (isset($context['resource_class']) && $this->resourceClassResolver->isResourceClass($context['resource_class']) && $this->resourceMetadataFactory instanceof ResourceMetadataCollectionFactoryInterface) { - $resourceClass = $this->resourceClassResolver->getResourceClass(null, $context['resource_class']); // fix for abstract classes and interfaces - // This is a hot spot, we should avoid calling this here but in many cases we can't - $operation = $context['operation'] ?? $this->resourceMetadataFactory->create($resourceClass)->getOperation($context['operation_name'] ?? null); - $options['normalization_groups'] = $operation->getNormalizationContext()['groups'] ?? null; - $options['denormalization_groups'] = $operation->getDenormalizationContext()['groups'] ?? null; - } - - if (isset($context['operation_name'])) { - $options['operation_name'] = $context['operation_name']; - } - - if (isset($context['collection_operation_name'])) { - $options['collection_operation_name'] = $context['collection_operation_name']; - } - - if (isset($context['item_operation_name'])) { - $options['item_operation_name'] = $context['item_operation_name']; - } - - return $options; - } - - protected function getAttributeValue($object, $attribute, $format = null, array $context = []) - { - $context['api_attribute'] = $attribute; - /** @var ApiProperty|PropertyMetadata */ - $propertyMetadata = $this->propertyMetadataFactory->create($context['resource_class'], $attribute, $this->getFactoryOptions($context)); - - try { - $attributeValue = $this->propertyAccessor->getValue($object, $attribute); - } catch (NoSuchPropertyException $e) { - // BC to be removed in 3.0 - if ($propertyMetadata instanceof PropertyMetadata && !$propertyMetadata->hasChildInherited()) { - throw $e; - } - - if ($propertyMetadata instanceof ApiProperty) { - throw $e; - } - - $attributeValue = null; - } - - if ($context['api_denormalize'] ?? false) { - return $attributeValue; - } - - $type = $propertyMetadata instanceof PropertyMetadata ? $propertyMetadata->getType() : ($propertyMetadata->getBuiltinTypes()[0] ?? null); - - if ( - $type - && $type->isCollection() - && ($collectionValueType = method_exists(Type::class, 'getCollectionValueTypes') ? ($type->getCollectionValueTypes()[0] ?? null) : $type->getCollectionValueType()) - && ($className = $collectionValueType->getClassName()) - && $this->resourceClassResolver->isResourceClass($className) - ) { - if (!is_iterable($attributeValue)) { - throw new UnexpectedValueException('Unexpected non-iterable value for to-many relation.'); - } - - $resourceClass = $this->resourceClassResolver->getResourceClass($attributeValue, $className); - $childContext = $this->createChildContext($context, $attribute, $format); - $childContext['resource_class'] = $resourceClass; - if ($this->resourceMetadataFactory instanceof ResourceMetadataCollectionFactoryInterface) { - $childContext['operation'] = $this->resourceMetadataFactory->create($resourceClass)->getOperation(); - } - unset($childContext['iri'], $childContext['uri_variables']); - - return $this->normalizeCollectionOfRelations($propertyMetadata, $attributeValue, $resourceClass, $format, $childContext); - } - - if ( - $type - && ($className = $type->getClassName()) - && $this->resourceClassResolver->isResourceClass($className) - ) { - if (!\is_object($attributeValue) && null !== $attributeValue) { - throw new UnexpectedValueException('Unexpected non-object value for to-one relation.'); - } - - $resourceClass = $this->resourceClassResolver->getResourceClass($attributeValue, $className); - $childContext = $this->createChildContext($context, $attribute, $format); - $childContext['resource_class'] = $resourceClass; - if ($this->resourceMetadataFactory instanceof ResourceMetadataCollectionFactoryInterface) { - $childContext['operation'] = $this->resourceMetadataFactory->create($resourceClass)->getOperation(); - } - unset($childContext['iri'], $childContext['uri_variables']); - - return $this->normalizeRelation($propertyMetadata, $attributeValue, $resourceClass, $format, $childContext); - } - - if (!$this->serializer instanceof NormalizerInterface) { - throw new LogicException(\sprintf('The injected serializer must be an instance of "%s".', NormalizerInterface::class)); - } - - unset($context['resource_class']); - - if ($type && $type->getClassName()) { - $childContext = $this->createChildContext($context, $attribute, $format); - unset($childContext['iri'], $childContext['uri_variables']); - - if ($propertyMetadata instanceof PropertyMetadata) { - $childContext['output']['iri'] = $propertyMetadata->getIri() ?? false; - } else { - $childContext['output']['gen_id'] = $propertyMetadata->getGenId() ?? false; - } - - return $this->serializer->normalize($attributeValue, $format, $childContext); - } - - return $this->serializer->normalize($attributeValue, $format, $context); - } - - protected function normalizeCollectionOfRelations( - $propertyMetadata, - $attributeValue, - string $resourceClass, - ?string $format, - array $context, - ): array { - $value = []; - foreach ($attributeValue as $index => $obj) { - if (!\is_object($obj) && null !== $obj) { - throw new UnexpectedValueException('Unexpected non-object element in to-many relation.'); - } - - $value[$index] = $this->normalizeRelation($propertyMetadata, $obj, $resourceClass, $format, $context); - } - - return $value; - } - - protected function normalizeRelation( - $propertyMetadata, - $relatedObject, - string $resourceClass, - ?string $format, - array $context, - ) { - if (null === $relatedObject || !empty($context['attributes']) || $propertyMetadata->isReadableLink()) { - if (!$this->serializer instanceof NormalizerInterface) { - throw new LogicException(\sprintf('The injected serializer must be an instance of "%s".', NormalizerInterface::class)); - } - - $normalizedRelatedObject = $this->serializer->normalize($relatedObject, $format, $context); - if (!\is_string($normalizedRelatedObject) && !\is_array($normalizedRelatedObject) && !$normalizedRelatedObject instanceof \ArrayObject && null !== $normalizedRelatedObject) { - throw new UnexpectedValueException('Expected normalized relation to be an IRI, array, \ArrayObject or null'); - } - - return $normalizedRelatedObject; - } - - $iri = $this->iriConverter->getIriFromResource($relatedObject); - - if (isset($context['resources'])) { - $context['resources'][$iri] = $iri; - } - - $push = $propertyMetadata instanceof PropertyMetadata ? $propertyMetadata->getAttribute('push', false) : ($propertyMetadata->getPush() ?? false); - if (isset($context['resources_to_push']) && $push) { - $context['resources_to_push'][$iri] = $iri; - } - - return $iri; - } - - protected function getDataTransformer($data, string $to, array $context = []): ?DataTransformerInterface - { - foreach ($this->dataTransformers as $dataTransformer) { - if ($dataTransformer->supportsTransformation($data, $to, $context)) { - return $dataTransformer; - } - } - - return null; - } - - private function createAttributeValue($attribute, $value, $format = null, array $context = []) - { - if (!$this->resourceClassResolver->isResourceClass($context['resource_class'])) { - return $value; - } - - /** @var ApiProperty|PropertyMetadata */ - $propertyMetadata = $this->propertyMetadataFactory->create($context['resource_class'], $attribute, $this->getFactoryOptions($context)); - $type = $propertyMetadata instanceof PropertyMetadata ? $propertyMetadata->getType() : ($propertyMetadata->getBuiltinTypes()[0] ?? null); - - if (null === $type) { - // No type provided, blindly return the value - return $value; - } - - if (null === $value && $type->isNullable()) { - return $value; - } - - $collectionValueType = method_exists(Type::class, 'getCollectionValueTypes') ? ($type->getCollectionValueTypes()[0] ?? null) : $type->getCollectionValueType(); - - /* From @see AbstractObjectNormalizer::validateAndDenormalize() */ - // Fix a collection that contains the only one element - // This is special to xml format only - if ('xml' === $format && null !== $collectionValueType && (!\is_array($value) || !\is_int(key($value)))) { - $value = [$value]; - } - - if ( - $type->isCollection() - && null !== $collectionValueType - && null !== ($className = $collectionValueType->getClassName()) - && $this->resourceClassResolver->isResourceClass($className) - ) { - $resourceClass = $this->resourceClassResolver->getResourceClass(null, $className); - $context['resource_class'] = $resourceClass; - - return $this->denormalizeCollection($attribute, $propertyMetadata, $type, $resourceClass, $value, $format, $context); - } - - if ( - null !== ($className = $type->getClassName()) - && $this->resourceClassResolver->isResourceClass($className) - ) { - $resourceClass = $this->resourceClassResolver->getResourceClass(null, $className); - $childContext = $this->createChildContext($context, $attribute, $format); - $childContext['resource_class'] = $resourceClass; - if ($this->resourceMetadataFactory instanceof ResourceMetadataCollectionFactoryInterface) { - $childContext['operation'] = $this->resourceMetadataFactory->create($resourceClass)->getOperation(); - } - - return $this->denormalizeRelation($attribute, $propertyMetadata, $resourceClass, $value, $format, $childContext); - } - - if ( - $type->isCollection() - && null !== $collectionValueType - && null !== ($className = $collectionValueType->getClassName()) - ) { - if (!$this->serializer instanceof DenormalizerInterface) { - throw new LogicException(\sprintf('The injected serializer must be an instance of "%s".', DenormalizerInterface::class)); - } - - unset($context['resource_class']); - - return $this->serializer->denormalize($value, $className.'[]', $format, $context); - } - - if (null !== $className = $type->getClassName()) { - if (!$this->serializer instanceof DenormalizerInterface) { - throw new LogicException(\sprintf('The injected serializer must be an instance of "%s".', DenormalizerInterface::class)); - } - - unset($context['resource_class']); - - return $this->serializer->denormalize($value, $className, $format, $context); - } - - /* From @see AbstractObjectNormalizer::validateAndDenormalize() */ - // In XML and CSV all basic datatypes are represented as strings, it is e.g. not possible to determine, - // if a value is meant to be a string, float, int or a boolean value from the serialized representation. - // That's why we have to transform the values, if one of these non-string basic datatypes is expected. - if (\is_string($value) && (XmlEncoder::FORMAT === $format || CsvEncoder::FORMAT === $format)) { - if ('' === $value && $type->isNullable() && \in_array($type->getBuiltinType(), [Type::BUILTIN_TYPE_BOOL, Type::BUILTIN_TYPE_INT, Type::BUILTIN_TYPE_FLOAT], true)) { - return null; - } - - switch ($type->getBuiltinType()) { - case Type::BUILTIN_TYPE_BOOL: - // according to https://www.w3.org/TR/xmlschema-2/#boolean, valid representations are "false", "true", "0" and "1" - if ('false' === $value || '0' === $value) { - $value = false; - } elseif ('true' === $value || '1' === $value) { - $value = true; - } else { - throw new NotNormalizableValueException(\sprintf('The type of the "%s" attribute for class "%s" must be bool ("%s" given).', $attribute, $className, $value)); - } - break; - case Type::BUILTIN_TYPE_INT: - if (ctype_digit($value) || ('-' === $value[0] && ctype_digit(substr($value, 1)))) { - $value = (int) $value; - } else { - throw new NotNormalizableValueException(\sprintf('The type of the "%s" attribute for class "%s" must be int ("%s" given).', $attribute, $className, $value)); - } - break; - case Type::BUILTIN_TYPE_FLOAT: - if (is_numeric($value)) { - return (float) $value; - } - - switch ($value) { - case 'NaN': - return \NAN; - case 'INF': - return \INF; - case '-INF': - return -\INF; - default: - throw new NotNormalizableValueException(\sprintf('The type of the "%s" attribute for class "%s" must be float ("%s" given).', $attribute, $className, $value)); - } - } - } - - if ($context[static::DISABLE_TYPE_ENFORCEMENT] ?? false) { - return $value; - } - - $this->validateType($attribute, $type, $value, $format); - - return $value; - } - - private function setValue($object, string $attributeName, $value) - { - try { - $this->propertyAccessor->setValue($object, $attributeName, $value); - } catch (NoSuchPropertyException $exception) { - } - } - - private function updateObjectToPopulate(array $data, array &$context): void - { - try { - $context[self::OBJECT_TO_POPULATE] = $this->iriConverter->getResourceFromIri((string) $data['id'], $context + ['fetch_data' => true]); - } catch (InvalidArgumentException $e) { - $iri = $this->iriConverter->getIriFromResource($context['resource_class'], UrlGeneratorInterface::ABS_PATH, null, ['uri_variables' => ['id' => $data['id']]]); - $context[self::OBJECT_TO_POPULATE] = $this->iriConverter->getResourceFromIri($iri, ['fetch_data' => true]); - } - } - - private function supportsPlainIdentifiers(): bool - { - return $this->allowPlainIdentifiers && null !== $this->itemDataProvider; - } -} diff --git a/src/Normalizer/JecouteChoiceFromIdDenormalizer.php b/src/Normalizer/JecouteChoiceFromIdDenormalizer.php index d43a4841da1..f2bd8b7ccf3 100644 --- a/src/Normalizer/JecouteChoiceFromIdDenormalizer.php +++ b/src/Normalizer/JecouteChoiceFromIdDenormalizer.php @@ -2,7 +2,7 @@ namespace App\Normalizer; -use ApiPlatform\Exception\ItemNotFoundException; +use ApiPlatform\Metadata\Exception\ItemNotFoundException; use App\Entity\Jecoute\Choice; use App\Repository\Jecoute\ChoiceRepository; use Symfony\Component\Serializer\Normalizer\DenormalizerInterface; diff --git a/src/Normalizer/JecouteDeviceNormalizer.php b/src/Normalizer/JecouteDeviceNormalizer.php index dd2df2c889d..a55813c9306 100644 --- a/src/Normalizer/JecouteDeviceNormalizer.php +++ b/src/Normalizer/JecouteDeviceNormalizer.php @@ -9,8 +9,6 @@ class JecouteDeviceNormalizer extends DeviceNormalizer { use NormalizerAwareTrait; - protected const ALREADY_CALLED = 'JECOUTE_DEVICE_NORMALIZER_ALREADY_CALLED'; - public function __construct(private readonly JemarcheDataSurveyRepository $dataSurveyRepository) { } diff --git a/src/Normalizer/JecouteSurveyNormalizer.php b/src/Normalizer/JecouteSurveyNormalizer.php index 602b3e0bb01..05c815038c7 100644 --- a/src/Normalizer/JecouteSurveyNormalizer.php +++ b/src/Normalizer/JecouteSurveyNormalizer.php @@ -2,6 +2,8 @@ namespace App\Normalizer; +use App\Entity\Jecoute\LocalSurvey; +use App\Entity\Jecoute\NationalSurvey; use App\Entity\Jecoute\Survey; use App\Entity\Jecoute\SurveyQuestion; use Symfony\Component\Serializer\Normalizer\NormalizerAwareInterface; @@ -12,8 +14,6 @@ class JecouteSurveyNormalizer implements NormalizerInterface, NormalizerAwareInt { use NormalizerAwareTrait; - protected const ALREADY_CALLED = 'JECOUTE_SURVEY_NORMALIZER_ALREADY_CALLED'; - /** * @param Survey $object * @@ -21,22 +21,26 @@ class JecouteSurveyNormalizer implements NormalizerInterface, NormalizerAwareInt */ public function normalize($object, $format = null, array $context = []): array|string|int|float|bool|\ArrayObject|null { - $context[static::ALREADY_CALLED] = true; + $data = $this->normalizer->normalize($object, $format, $context + [__CLASS__ => true]); - $data = $this->normalizer->normalize($object, $format, $context); + if ($object instanceof LocalSurvey) { + $data['zone'] = $this->normalizer->normalize($object->getZone(), $format, $context); + } - $data['questions'] = array_map(function (SurveyQuestion $surveyQuestion) use ($format, $context) { - $question = $surveyQuestion->getQuestion(); - $choices = $this->normalizer->normalize($question->getChoicesOrdered(), $format, $context); - $choices = array_values($choices); + if (!\in_array('survey_list_dc', $context['groups'])) { + $data['questions'] = array_map(function (SurveyQuestion $surveyQuestion) use ($format, $context) { + $question = $surveyQuestion->getQuestion(); + $choices = $this->normalizer->normalize($question->getChoicesOrdered(), $format, $context); + $choices = array_values($choices); - return [ - 'id' => $surveyQuestion->getId(), - 'type' => $question->getType(), - 'content' => $question->getContent(), - 'choices' => $choices, - ]; - }, $object->getQuestions()->toArray()); + return [ + 'id' => $surveyQuestion->getId(), + 'type' => $question->getType(), + 'content' => $question->getContent(), + 'choices' => $choices, + ]; + }, $object->getQuestions()->toArray()); + } return $data; } @@ -46,13 +50,15 @@ public function getSupportedTypes(?string $format): array return [ '*' => null, Survey::class => false, + LocalSurvey::class => false, + NationalSurvey::class => false, ]; } public function supportsNormalization($data, $format = null, array $context = []): bool { - return !isset($context[static::ALREADY_CALLED]) + return !isset($context[__CLASS__]) && $data instanceof Survey - && 0 !== \count(array_intersect(['pap_campaign_survey_read', 'survey_list', 'survey_read_dc'], $context['groups'] ?? [])); + && 0 !== \count(array_intersect(['pap_campaign_survey_read', 'survey_list', 'survey_read_dc', 'survey_list_dc'], $context['groups'] ?? [])); } } diff --git a/src/Normalizer/LegislativeNewsletterSubscriptionSourceDenormalizer.php b/src/Normalizer/LegislativeNewsletterSubscriptionSourceDenormalizer.php index 83bf1b944d5..acff1689f5e 100644 --- a/src/Normalizer/LegislativeNewsletterSubscriptionSourceDenormalizer.php +++ b/src/Normalizer/LegislativeNewsletterSubscriptionSourceDenormalizer.php @@ -2,7 +2,7 @@ namespace App\Normalizer; -use ApiPlatform\Exception\ItemNotFoundException; +use ApiPlatform\Metadata\Exception\ItemNotFoundException; use App\Entity\Geo\Zone; use App\Entity\LegislativeNewsletterSubscription; use App\Repository\Geo\ZoneRepository; diff --git a/src/Normalizer/Pap/BuildingBlockNormalizer.php b/src/Normalizer/Pap/BuildingBlockNormalizer.php index c6f025f82bf..320b3b8cf32 100644 --- a/src/Normalizer/Pap/BuildingBlockNormalizer.php +++ b/src/Normalizer/Pap/BuildingBlockNormalizer.php @@ -13,15 +13,10 @@ class BuildingBlockNormalizer implements NormalizerInterface, NormalizerAwareInt { use NormalizerAwareTrait; - protected const ALREADY_CALLED = 'BUILDING_BLOCK_NORMALIZER_ALREADY_CALLED'; - - private RequestStack $requestStack; - private CampaignRepository $campaignRepository; - - public function __construct(RequestStack $requestStack, CampaignRepository $campaignRepository) - { - $this->requestStack = $requestStack; - $this->campaignRepository = $campaignRepository; + public function __construct( + private readonly RequestStack $requestStack, + private readonly CampaignRepository $campaignRepository, + ) { } /** @@ -29,7 +24,6 @@ public function __construct(RequestStack $requestStack, CampaignRepository $camp */ public function normalize($object, $format = null, array $context = []): array|string|int|float|bool|\ArrayObject|null { - $context[static::ALREADY_CALLED] = true; $campaign = null; if ($campaignUuid = $this->requestStack->getMainRequest()->query->get('campaign_uuid')) { @@ -38,7 +32,7 @@ public function normalize($object, $format = null, array $context = []): array|s } } - $data = $this->normalizer->normalize($object, $format, $context); + $data = $this->normalizer->normalize($object, $format, $context + [__CLASS__ => true]); if ($campaign && $campaignUuid && $stats = $object->findStatisticsForCampaign($campaign)) { $data['campaign_statistics'] = [ @@ -61,6 +55,6 @@ public function getSupportedTypes(?string $format): array public function supportsNormalization($data, $format = null, array $context = []): bool { - return $data instanceof BuildingBlock && !isset($context[static::ALREADY_CALLED]); + return !isset($context[__CLASS__]) && $data instanceof BuildingBlock; } } diff --git a/src/Normalizer/Pap/BuildingFloorNormalizer.php b/src/Normalizer/Pap/BuildingFloorNormalizer.php index bb0b99bf8af..6021bf1c95f 100644 --- a/src/Normalizer/Pap/BuildingFloorNormalizer.php +++ b/src/Normalizer/Pap/BuildingFloorNormalizer.php @@ -14,15 +14,10 @@ class BuildingFloorNormalizer implements NormalizerInterface, NormalizerAwareInt { use NormalizerAwareTrait; - protected const ALREADY_CALLED = 'FLOOR_NORMALIZER_ALREADY_CALLED'; - - private RequestStack $requestStack; - private CampaignRepository $campaignRepository; - - public function __construct(RequestStack $requestStack, CampaignRepository $campaignRepository) - { - $this->requestStack = $requestStack; - $this->campaignRepository = $campaignRepository; + public function __construct( + private readonly RequestStack $requestStack, + private readonly CampaignRepository $campaignRepository, + ) { } /** @@ -30,9 +25,7 @@ public function __construct(RequestStack $requestStack, CampaignRepository $camp */ public function normalize($object, $format = null, array $context = []): array|string|int|float|bool|\ArrayObject|null { - $context[static::ALREADY_CALLED] = true; - - $data = $this->normalizer->normalize($object, $format, $context); + $data = $this->normalizer->normalize($object, $format, $context + [__CLASS__ => true]); $campaign = null; $campaignUuid = $this->requestStack->getMainRequest()->query->get('campaign_uuid'); @@ -68,6 +61,6 @@ public function getSupportedTypes(?string $format): array public function supportsNormalization($data, $format = null, array $context = []): bool { - return $data instanceof Floor && !isset($context[static::ALREADY_CALLED]); + return !isset($context[__CLASS__]) && $data instanceof Floor; } } diff --git a/src/Normalizer/SurveyDenormalizer.php b/src/Normalizer/SurveyDenormalizer.php index 490d4f7b039..55cbec18d4c 100644 --- a/src/Normalizer/SurveyDenormalizer.php +++ b/src/Normalizer/SurveyDenormalizer.php @@ -7,6 +7,8 @@ use App\Entity\Jecoute\NationalSurvey; use App\Entity\Jecoute\Survey; use App\Jecoute\SurveyTypeEnum; +use App\Repository\Geo\ZoneRepository; +use Ramsey\Uuid\Uuid; use Symfony\Component\Serializer\Exception\UnexpectedValueException; use Symfony\Component\Serializer\Normalizer\AbstractNormalizer; use Symfony\Component\Serializer\Normalizer\DenormalizerAwareInterface; @@ -17,6 +19,10 @@ class SurveyDenormalizer implements DenormalizerInterface, DenormalizerAwareInte { use DenormalizerAwareTrait; + public function __construct(private readonly ZoneRepository $zoneRepository) + { + } + public function denormalize($data, $type, $format = null, array $context = []): mixed { if (!empty($context[AbstractNormalizer::OBJECT_TO_POPULATE])) { @@ -40,7 +46,15 @@ public function denormalize($data, $type, $format = null, array $context = []): $operation = $context['operation']; $context['operation'] = $operation->withClass($surveyClass); - return $this->denormalizer->denormalize($data, $surveyClass, $format, $context + [__CLASS__ => true]); + $survey = $this->denormalizer->denormalize($data, $surveyClass, $format, $context + [__CLASS__ => true]); + + if ($survey instanceof LocalSurvey && !empty($data['zone']) && Uuid::isValid($data['zone'])) { + if ($zone = $this->zoneRepository->findOneByUuid($data['zone'])) { + $survey->setZone($zone); + } + } + + return $survey; } public function getSupportedTypes(?string $format): array @@ -48,6 +62,8 @@ public function getSupportedTypes(?string $format): array return [ '*' => null, Survey::class => false, + LocalSurvey::class => false, + NationalSurvey::class => false, ]; } diff --git a/src/Normalizer/SurveyQuestionFromIdDenormalizer.php b/src/Normalizer/SurveyQuestionFromIdDenormalizer.php index 26379205fdf..f221d181e75 100644 --- a/src/Normalizer/SurveyQuestionFromIdDenormalizer.php +++ b/src/Normalizer/SurveyQuestionFromIdDenormalizer.php @@ -2,7 +2,7 @@ namespace App\Normalizer; -use ApiPlatform\Exception\ItemNotFoundException; +use ApiPlatform\Metadata\Exception\ItemNotFoundException; use App\Entity\Jecoute\SurveyQuestion; use App\Repository\Jecoute\SurveyQuestionRepository; use Symfony\Component\Serializer\Normalizer\DenormalizerInterface; diff --git a/src/Normalizer/UpdateElectedMandateDenormalizer.php b/src/Normalizer/UpdateElectedMandateDenormalizer.php index 9aca6eb2b14..32812f11316 100644 --- a/src/Normalizer/UpdateElectedMandateDenormalizer.php +++ b/src/Normalizer/UpdateElectedMandateDenormalizer.php @@ -66,7 +66,7 @@ public function supportsDenormalization($data, string $type, ?string $format = n { return !isset($context[__CLASS__]) && is_a($type, Mandate::class, true) - && '_api_/elected_mandates/{uuid}_put' === ($context['operation_name'] ?? null); + && '_api_/v3/elected_mandates/{uuid}_put' === ($context['operation_name'] ?? null); } private function handleChanges( diff --git a/src/Procuration/Listener/CheckUpdatedStatusListener.php b/src/Procuration/Listener/CheckUpdatedStatusListener.php index 92534ed520a..7a8d9476418 100644 --- a/src/Procuration/Listener/CheckUpdatedStatusListener.php +++ b/src/Procuration/Listener/CheckUpdatedStatusListener.php @@ -36,8 +36,8 @@ public function preDeserialize(RequestEvent $event): void $operationName = $request->attributes->get('_api_operation_name'); if (!\in_array($operationName, [ - '_api_/requests/{uuid}_patch', - '_api_/proxies/{uuid}_patch', + '_api_/v3/procuration/requests/{uuid}_patch', + '_api_/v3/procuration/proxies/{uuid}_patch', ], true)) { return; } diff --git a/src/Procuration/Listener/ProcurationSlotStatusActionListener.php b/src/Procuration/Listener/ProcurationSlotStatusActionListener.php index 8c60a32ffd3..bd4f9487a18 100644 --- a/src/Procuration/Listener/ProcurationSlotStatusActionListener.php +++ b/src/Procuration/Listener/ProcurationSlotStatusActionListener.php @@ -34,8 +34,8 @@ public function preDeserialize(RequestEvent $event): void $operationName = $request->attributes->get('_api_operation_name'); if (!\in_array($operationName, [ - '_api_/request_slots/{uuid}_put', - '_api_/proxy_slots/{uuid}_put', + '_api_/v3/procuration/request_slots/{uuid}_put', + '_api_/v3/procuration/proxy_slots/{uuid}_put', ], true)) { return; } @@ -55,8 +55,8 @@ public function preWrite(ViewEvent $event): void $operationName = $request->attributes->get('_api_operation_name'); if (!\in_array($operationName, [ - '_api_/request_slots/{uuid}_put', - '_api_/proxy_slots/{uuid}_put', + '_api_/v3/procuration/request_slots/{uuid}_put', + '_api_/v3/procuration/proxy_slots/{uuid}_put', ], true)) { return; } diff --git a/src/Procuration/Listener/ProcurationStatusActionListener.php b/src/Procuration/Listener/ProcurationStatusActionListener.php index 7ab5be85df4..a529fb5fba6 100644 --- a/src/Procuration/Listener/ProcurationStatusActionListener.php +++ b/src/Procuration/Listener/ProcurationStatusActionListener.php @@ -37,8 +37,8 @@ public function preDeserialize(RequestEvent $event): void $operationName = $request->attributes->get('_api_operation_name'); if (!\in_array($operationName, [ - '_api_/requests/{uuid}_patch', - '_api_/proxies/{uuid}_patch', + '_api_/v3/procuration/requests/{uuid}_patch', + '_api_/v3/procuration/proxies/{uuid}_patch', ], true)) { return; } @@ -66,8 +66,8 @@ public function preWrite(ViewEvent $event): void $operationName = $request->attributes->get('_api_operation_name'); if (!\in_array($operationName, [ - '_api_/requests/{uuid}_patch', - '_api_/proxies/{uuid}_patch', + '_api_/v3/procuration/requests/{uuid}_patch', + '_api_/v3/procuration/proxies/{uuid}_patch', ], true)) { return; } diff --git a/src/Repository/Jecoute/LocalSurveyRepository.php b/src/Repository/Jecoute/LocalSurveyRepository.php index be5cc9eccb3..393e7f39cf0 100644 --- a/src/Repository/Jecoute/LocalSurveyRepository.php +++ b/src/Repository/Jecoute/LocalSurveyRepository.php @@ -2,7 +2,6 @@ namespace App\Repository\Jecoute; -use App\Entity\Adherent; use App\Entity\Jecoute\DataSurvey; use App\Entity\Jecoute\LocalSurvey; use App\Entity\Jecoute\SurveyQuestion; @@ -92,19 +91,4 @@ public function createSurveysByZonesQueryBuilder(array $zones): QueryBuilder ->orderBy('survey.id') ; } - - public function findAllByAuthor(Adherent $adherent): array - { - return $this - ->createQueryBuilder('survey') - ->leftJoin('survey.zone', 'zone') - ->addSelect('zone') - ->addSelect(\sprintf('(SELECT COUNT(q.id) FROM %s AS q WHERE q.survey = survey) AS questions_count', SurveyQuestion::class)) - ->addSelect(\sprintf('(SELECT COUNT(r.id) FROM %s AS r WHERE r.survey = survey) AS responses_count', DataSurvey::class)) - ->where('survey.createdByAdherent = :author') - ->setParameter('author', $adherent) - ->getQuery() - ->getResult() - ; - } } diff --git a/src/Swagger/PaginatorSwagger.php b/src/Swagger/PaginatorSwagger.php deleted file mode 100644 index 1a0df5dff57..00000000000 --- a/src/Swagger/PaginatorSwagger.php +++ /dev/null @@ -1,48 +0,0 @@ - 'object', - 'properties' => [ - 'total_items' => [ - 'type' => 'integer', - ], - 'items_per_page' => [ - 'type' => 'integer', - ], - 'count' => [ - 'type' => 'integer', - ], - 'current_page' => [ - 'type' => 'integer', - ], - 'last_page' => [ - 'type' => 'integer', - ], - ], - ]; - - public static function getPaginatedResponseFor(string $definition): array - { - $model = substr($definition, 0, strpos($definition, '-')); - - return [ - 'description' => "Paginated $model collection response", - 'schema' => [ - 'type' => 'object', - 'properties' => [ - 'metadatas' => self::DEFINITION, - 'items' => [ - 'type' => 'array', - 'items' => [ - '$ref' => "#/definitions/$definition", - ], - ], - ], - ], - ]; - } -} diff --git a/src/Swagger/SwaggerDecorator.php b/src/Swagger/SwaggerDecorator.php deleted file mode 100644 index 20e68342667..00000000000 --- a/src/Swagger/SwaggerDecorator.php +++ /dev/null @@ -1,101 +0,0 @@ -decorated = $decorated; - $this->apiPathPrefix = $apiPathPrefix; - $this->resourceMetadataFactory = $resourceMetadataFactory; - $this->operationPathResolver = $operationPathResolver; - } - - public function normalize($object, $format = null, array $context = []) - { - $docs = $this->decorated->normalize($object, $format, $context); - - foreach ($object->getResourceNameCollection() as $resourceClass) { - $resourceMetadata = $this->resourceMetadataFactory->create($resourceClass); - - if ($resourceMetadata->getCollectionOperationAttribute('get', 'pagination_enabled', true, true)) { - $context = $resourceMetadata->getCollectionOperationAttribute('get', 'normalization_context', null, true); - - if (isset($context['groups']) && ($groups = $context['groups']) && \is_array($groups)) { - $group = $groups[0]; - } - - $docs = $this->overridePaginatedResponseFormat($docs, $resourceMetadata, $group ?? null); - } - } - - $docs['info']['title'] = self::TITLE; - - return $docs; - } - - public function supportsNormalization($data, $format = null): bool - { - return $this->decorated->supportsNormalization($data, $format); - } - - private function overridePaginatedResponseFormat( - array $docs, - ResourceMetadata $resourceMetadata, - ?string $group, - ): array { - $path = \sprintf('%s%s', $this->apiPathPrefix, $this->getPath($resourceMetadata->getShortName(), ['get'], 'collection')); - - if ($group) { - $definition = \sprintf('%s-%s', $resourceMetadata->getShortName(), $group); - } else { - $definition = $resourceMetadata->getShortName(); - } - - $docs['paths'][$path]['get']['responses'][200] = PaginatorSwagger::getPaginatedResponseFor($definition); - - return $docs; - } - - /** - * Gets the path for an operation. - * - * If the path ends with the optional _format parameter, it is removed - * as optional path parameters are not yet supported. - * - * @see https://github.com/OAI/OpenAPI-Specification/issues/93 - */ - private function getPath(string $resourceShortName, array $operation, string $operationType): string - { - $path = $this->operationPathResolver->resolveOperationPath($resourceShortName, $operation, $operationType); - if (str_ends_with($path, '.{_format}')) { - $path = substr($path, 0, -10); - } - - return $path; - } - - public function getSupportedTypes(?string $format): array - { - return [ - '*' => false, - ]; - } -} From c106db202686183b7dc17037baeb919850e6d0c0 Mon Sep 17 00:00:00 2001 From: Dimitri Gritsajuk Date: Fri, 31 Jan 2025 15:55:14 +0100 Subject: [PATCH 2/2] Fix tests --- features/api/adherents.feature | 26 ++++-- features/api/audiences.feature | 16 +--- features/api/change_email.feature | 6 +- features/api/committees.feature | 14 ++- features/api/department_sites.feature | 22 ++--- features/api/designations.feature | 19 ++-- features/api/events.feature | 16 +--- features/api/jecoute_news.feature | 27 ++---- features/api/jecoute_ripostes.feature | 23 ++--- features/api/membership_avecvous.feature | 108 ++++++++--------------- features/api/my_teams.feature | 25 ++---- features/api/pap_address.feature | 6 +- features/api/pap_campaigns.feature | 48 +++------- features/api/phoning_campaigns.feature | 32 +++---- features/api/profile.feature | 64 ++++---------- features/api/report.feature | 12 +-- features/api/surveys.feature | 15 +--- features/api/surveys_replys.feature | 8 +- features/api/teams.feature | 30 +++---- features/api/user.feature | 21 ++--- 20 files changed, 173 insertions(+), 365 deletions(-) diff --git a/features/api/adherents.feature b/features/api/adherents.feature index 68a4525273f..cbe14a42c46 100644 --- a/features/api/adherents.feature +++ b/features/api/adherents.feature @@ -33,12 +33,10 @@ Feature: And the JSON should be equal to: """ { - "type": "https://tools.ietf.org/html/rfc2616#section-10", - "title": "An error occurred", - "detail": "nickname: Cette valeur est déjà utilisée.", + "message": "Validation Failed", + "status": "error", "violations": [ { - "code": "@uuid@", "propertyPath": "nickname", "message": "Cette valeur est déjà utilisée." } @@ -59,12 +57,10 @@ Feature: And the JSON should be equal to: """ { - "type": "https://tools.ietf.org/html/rfc2616#section-10", - "title": "An error occurred", - "detail": "nickname: Vous devez saisir au maximum 25 caractères.", + "message": "Validation Failed", + "status": "error", "violations": [ { - "code": "@uuid@", "propertyPath": "nickname", "message": "Vous devez saisir au maximum 25 caractères." } @@ -82,7 +78,19 @@ Feature: """ Then the response status code should be 400 And the response should be in JSON - And the JSON node "detail" should be equal to "nickname: La syntaxe est incorrecte, le pseudo ne peut contenir que des chiffres, lettres, et les caractères _ et -" + And the JSON should be equal to: + """ + { + "message": "Validation Failed", + "status": "error", + "violations": [ + { + "propertyPath": "nickname", + "message": "La syntaxe est incorrecte, le pseudo ne peut contenir que des chiffres, lettres, et les caractères _ et -" + } + ] + } + """ Scenario: As a logged-in user I can set my nickname but not use it Given I am logged as "jacques.picard@en-marche.fr" diff --git a/features/api/audiences.feature b/features/api/audiences.feature index 864578475a3..2b30728619d 100644 --- a/features/api/audiences.feature +++ b/features/api/audiences.feature @@ -49,12 +49,10 @@ Feature: And the JSON should be equal to: """ { - "type": "https://tools.ietf.org/html/rfc2616#section-10", - "title": "An error occurred", - "detail": "name: Cette valeur ne doit pas \u00eatre vide.", + "message": "Validation Failed", + "status": "error", "violations": [ { - "code": "@uuid@", "propertyPath": "name", "message": "Cette valeur ne doit pas être vide." } @@ -79,32 +77,26 @@ Feature: And the JSON should be equal to: """ { - "type": "https://tools.ietf.org/html/rfc2616#section-10", - "title": "An error occurred", - "detail": "zone: La zone choisie ne fait pas partie des zones gérées.\nname: Cette valeur ne doit pas être vide.\nfirst_name: Vous devez saisir au maximum 50 caractères.\nlast_name: Vous devez saisir au maximum 50 caractères.\ngender: Ce sexe n'est pas valide.", + "message": "Validation Failed", + "status": "error", "violations": [ { - "code": null, "propertyPath": "zone", "message": "La zone choisie ne fait pas partie des zones gérées." }, { - "code": "@uuid@", "propertyPath": "name", "message": "Cette valeur ne doit pas être vide." }, { - "code": "@uuid@", "propertyPath": "first_name", "message": "Vous devez saisir au maximum 50 caractères." }, { - "code": "@uuid@", "propertyPath": "last_name", "message": "Vous devez saisir au maximum 50 caractères." }, { - "code": "@uuid@", "propertyPath": "gender", "message": "Ce sexe n'est pas valide." } diff --git a/features/api/change_email.feature b/features/api/change_email.feature index 9c5e908e7e9..79a23103b50 100644 --- a/features/api/change_email.feature +++ b/features/api/change_email.feature @@ -44,12 +44,10 @@ Feature: And the JSON should be equal to: """ { - "detail": "email_address: Ceci n'est pas une adresse email valide.", - "title": "An error occurred", - "type": "@string@.isUrl()", + "message": "Validation Failed", + "status": "error", "violations": [ { - "code": "@uuid@", "message": "Ceci n'est pas une adresse email valide.", "propertyPath": "email_address" } diff --git a/features/api/committees.feature b/features/api/committees.feature index 2fd023a7ac9..0e900702681 100644 --- a/features/api/committees.feature +++ b/features/api/committees.feature @@ -371,14 +371,12 @@ Feature: And the JSON should be equal to: """ { - "type": "https://tools.ietf.org/html/rfc2616#section-10", - "title": "An error occurred", - "detail": "zones: Le type de la zone est invalide", + "message": "Validation Failed", + "status": "error", "violations": [ { "propertyPath": "zones", - "message": "Le type de la zone est invalide", - "code": null + "message": "Le type de la zone est invalide" } ] } @@ -480,7 +478,7 @@ Feature: """ Then the response status code should be 400 And the response should be in JSON - And the JSON node "detail" should be equal to "Vous ne pouvez pas créer de liste sur une élection en cours" + And the JSON node "violations[0].message" should be equal to "Vous ne pouvez pas créer de liste sur une élection en cours" Examples: | user | scope | @@ -625,7 +623,7 @@ Feature: """ Then the response status code should be 400 And the response should be in JSON - And the JSON node "detail" should be equal to "Vous ne pouvez pas créer de candidature sur une élection en cours" + And the JSON node "violations[0].message" should be equal to "Vous ne pouvez pas créer de candidature sur une élection en cours" Examples: | user | scope | @@ -655,7 +653,7 @@ Feature: """ Then the response status code should be 400 And the response should be in JSON - And the JSON node "detail" should be equal to "committee_membership: L'adhérent ne fait pas partie de votre zone de couverture." + And the JSON node "violations[0].message" should be equal to "L'adhérent ne fait pas partie de votre zone de couverture." Examples: | user | scope | diff --git a/features/api/department_sites.feature b/features/api/department_sites.feature index c336104c36f..068a4738f96 100644 --- a/features/api/department_sites.feature +++ b/features/api/department_sites.feature @@ -52,14 +52,12 @@ Feature: And the JSON should be equal to: """ { - "type": "https://tools.ietf.org/html/rfc2616#section-10", - "title": "An error occurred", - "detail": "zone: Cette zone ne fait pas partie des zones que vous gérez.", + "message": "Validation Failed", + "status": "error", "violations": [ { "propertyPath": "zone", - "message": "Cette zone ne fait pas partie des zones que vous gérez.", - "code": null + "message": "Cette zone ne fait pas partie des zones que vous gérez." } ] } @@ -124,24 +122,20 @@ Feature: And the JSON should be equal to: """ { - "type": "https://tools.ietf.org/html/rfc2616#section-10", - "title": "An error occurred", - "detail": "zone: Le type de la zone est invalide.\nzone: Cette valeur ne doit pas être vide.\ncontent: Cette valeur ne doit pas être vide.", + "message": "Validation Failed", + "status": "error", "violations": [ { "propertyPath": "zone", - "message": "Le type de la zone est invalide.", - "code": "@uuid@" + "message": "Le type de la zone est invalide." }, { "propertyPath": "zone", - "message": "Cette valeur ne doit pas être vide.", - "code": "@uuid@" + "message": "Cette valeur ne doit pas être vide." }, { "propertyPath": "content", - "message": "Cette valeur ne doit pas être vide.", - "code": "@uuid@" + "message": "Cette valeur ne doit pas être vide." } ] } diff --git a/features/api/designations.feature b/features/api/designations.feature index 8c24e0f7e5d..1c5cf0c4de3 100644 --- a/features/api/designations.feature +++ b/features/api/designations.feature @@ -16,29 +16,24 @@ Feature: And the JSON should be equal to: """ { - "type": "https://tools.ietf.org/html/rfc2616#section-10", - "title": "An error occurred", - "detail": "custom_title: Cette valeur ne doit pas être vide.\ntype: Cette valeur ne doit pas être vide.\nvote_end_date: La date de clôture doit être postérieur à la date de début\ndescription: Cette valeur ne doit pas être vide.", + "message": "Validation Failed", + "status": "error", "violations": [ { "propertyPath": "custom_title", - "message": "Cette valeur ne doit pas être vide.", - "code": "@uuid@" + "message": "Cette valeur ne doit pas être vide." }, { "propertyPath": "type", - "message": "Cette valeur ne doit pas être vide.", - "code": "@uuid@" + "message": "Cette valeur ne doit pas être vide." }, { "propertyPath": "vote_end_date", - "message": "La date de clôture doit être postérieur à la date de début", - "code": "@uuid@" + "message": "La date de clôture doit être postérieur à la date de début" }, { "propertyPath": "description", - "message": "Cette valeur ne doit pas être vide.", - "code": "@uuid@" + "message": "Cette valeur ne doit pas être vide." } ] } @@ -63,7 +58,7 @@ Feature: """ Then the response status code should be 400 And the response should be in JSON - And the JSON node "detail" should be equal to "election_entity_identifier: Un identifiant est requis pour ce champs." + And the JSON node "violations[0].message" should be equal to "Un identifiant est requis pour ce champs." Examples: | user | scope | diff --git a/features/api/events.feature b/features/api/events.feature index 095cb914679..9b9407f16a6 100644 --- a/features/api/events.feature +++ b/features/api/events.feature @@ -1153,32 +1153,26 @@ Feature: And the JSON should be equal to: """ { - "detail": "category: Catégorie est requise.\nfinish_at: La date de fin de votre événement ne peut pas dépasser le 4 janv. 2018, 10:10.\nname: Cette valeur ne doit pas être vide.\ncanonical_name: Cette valeur ne doit pas être vide.\ndescription: Cette valeur ne doit pas être vide.", - "title": "An error occurred", - "type": "https://tools.ietf.org/html/rfc2616#section-10", + "message": "Validation Failed", + "status": "error", "violations": [ { - "code": null, "message": "Catégorie est requise.", "propertyPath": "category" }, { - "code": null, "message": "La date de fin de votre événement ne peut pas dépasser le 4 janv. 2018, 10:10.", "propertyPath": "finish_at" }, { - "code": "@uuid@", "message": "Cette valeur ne doit pas être vide.", "propertyPath": "name" }, { - "code": "@uuid@", "message": "Cette valeur ne doit pas être vide.", "propertyPath": "canonical_name" }, { - "code": "@uuid@", "message": "Cette valeur ne doit pas être vide.", "propertyPath": "description" } @@ -1214,12 +1208,10 @@ Feature: And the JSON should be equal to: """ { - "detail": "finish_at: La date de fin de l'événement doit être postérieure à la date de début.", - "title": "An error occurred", - "type": "https://tools.ietf.org/html/rfc2616#section-10", + "message": "Validation Failed", + "status": "error", "violations": [ { - "code": "@uuid@", "message": "La date de fin de l'événement doit être postérieure à la date de début.", "propertyPath": "finish_at" } diff --git a/features/api/jecoute_news.feature b/features/api/jecoute_news.feature index 816e4177b2d..da8b091b479 100644 --- a/features/api/jecoute_news.feature +++ b/features/api/jecoute_news.feature @@ -749,17 +749,14 @@ Feature: And the JSON should be equal to: """ { - "type": "https://tools.ietf.org/html/rfc2616#section-10", - "title": "An error occurred", - "detail": "text: Le texte est obligatoire.\ntitle: Cette valeur ne doit pas être vide.", + "message": "Validation Failed", + "status": "error", "violations": [ { - "code": null, "propertyPath": "text", "message": "Le texte est obligatoire." }, { - "code": "@uuid@", "propertyPath": "title", "message": "Cette valeur ne doit pas être vide." } @@ -782,22 +779,18 @@ Feature: And the JSON should be equal to: """ { - "type": "https://tools.ietf.org/html/rfc2616#section-10", - "title": "An error occurred", - "detail": "text: Le texte ne doit pas contenir plus de 1000 caractères.\ntitle: Vous devez saisir au maximum 120 caractères.\nexternal_link: Cette valeur n'est pas une URL valide.", + "message": "Validation Failed", + "status": "error", "violations": [ { - "code": null, "propertyPath": "text", "message": "Le texte ne doit pas contenir plus de 1000 caractères." }, { - "code": "@uuid@", "propertyPath": "title", "message": "Vous devez saisir au maximum 120 caractères." }, { - "code": "@uuid@", "propertyPath": "external_link", "message": "Cette valeur n'est pas une URL valide." } @@ -1293,12 +1286,10 @@ Feature: And the JSON should be equal to: """ { - "type": "https://tools.ietf.org/html/rfc2616#section-10", - "title": "An error occurred", - "detail": "zone: Veuillez spécifier une zone.", + "message": "Validation Failed", + "status": "error", "violations": [ { - "code": null, "propertyPath": "zone", "message": "Veuillez spécifier une zone." } @@ -1324,12 +1315,10 @@ Feature: And the JSON should be equal to: """ { - "type": "https://tools.ietf.org/html/rfc2616#section-10", - "title": "An error occurred", - "detail": "zone: La zone spécifiée n'est pas gérée par votre rôle.", + "message": "Validation Failed", + "status": "error", "violations": [ { - "code": null, "propertyPath": "zone", "message": "La zone spécifiée n'est pas gérée par votre rôle." } diff --git a/features/api/jecoute_ripostes.feature b/features/api/jecoute_ripostes.feature index 46da3c2193d..ad16c56116e 100644 --- a/features/api/jecoute_ripostes.feature +++ b/features/api/jecoute_ripostes.feature @@ -257,22 +257,18 @@ Feature: And the JSON should be equal to: """ { - "type": "https://tools.ietf.org/html/rfc2616#section-10", - "title": "An error occurred", - "detail": "title: Cette valeur ne doit pas être vide.\nbody: Cette valeur ne doit pas être vide.\nsource_url: Cette valeur ne doit pas être vide.", + "message": "Validation Failed", + "status": "error", "violations": [ { - "code": "@uuid@", "propertyPath": "title", "message": "Cette valeur ne doit pas être vide." }, { - "code": "@uuid@", "propertyPath": "body", "message": "Cette valeur ne doit pas être vide." }, { - "code": "@uuid@", "propertyPath": "source_url", "message": "Cette valeur ne doit pas être vide." } @@ -286,7 +282,7 @@ Feature: """ { "title": "Une nouvelle riposte d'aujourd'hui - Une nouvelle riposte d'aujourd'hui - Une nouvelle riposte d'aujourd'hui - Une nouvelle riposte d'aujourd'hui - Une nouvelle riposte d'aujourd'hui - Une nouvelle riposte d'aujourd'hui - Une nouvelle riposte d'aujourd'hui", - "with_notification": "true" + "with_notification": true } """ Then the response status code should be 400 @@ -294,29 +290,20 @@ Feature: And the JSON should be equal to: """ { - "type": "https://tools.ietf.org/html/rfc2616#section-10", - "title": "An error occurred", - "detail": "title: Vous devez saisir au maximum 255 caractères.\nbody: Cette valeur ne doit pas être vide.\nsource_url: Cette valeur ne doit pas être vide.\nwith_notification: Cette valeur doit être de type bool.", + "message": "Validation Failed", + "status": "error", "violations": [ { - "code": "@uuid@", "propertyPath": "title", "message": "Vous devez saisir au maximum 255 caractères." }, { - "code": "@uuid@", "propertyPath": "body", "message": "Cette valeur ne doit pas être vide." }, { - "code": "@uuid@", "propertyPath": "source_url", "message": "Cette valeur ne doit pas être vide." - }, - { - "code": "@uuid@", - "propertyPath": "with_notification", - "message": "Cette valeur doit être de type bool." } ] } diff --git a/features/api/membership_avecvous.feature b/features/api/membership_avecvous.feature index a858a204328..c75979e2532 100644 --- a/features/api/membership_avecvous.feature +++ b/features/api/membership_avecvous.feature @@ -207,12 +207,10 @@ Feature: And the JSON should be equal to: """ { - "type": "https://tools.ietf.org/html/rfc2616#section-10", - "title": "An error occurred", - "detail": "recaptcha: Merci de confirmer le captcha avant de continuer.", + "message": "Validation Failed", + "status": "error", "violations": [ { - "code": null, "propertyPath": "recaptcha", "message": "Merci de confirmer le captcha avant de continuer." } @@ -237,12 +235,10 @@ Feature: And the JSON should be equal to: """ { - "type": "https://tools.ietf.org/html/rfc2616#section-10", - "title": "An error occurred", - "detail": "recaptcha: Le captcha soumis est invalide.", + "message": "Validation Failed", + "status": "error", "violations": [ { - "code": null, "propertyPath": "recaptcha", "message": "Le captcha soumis est invalide." } @@ -267,12 +263,10 @@ Feature: And the JSON should be equal to: """ { - "type": "https://tools.ietf.org/html/rfc2616#section-10", - "title": "An error occurred", - "detail": "cgu_accepted: Veuillez accepter les CGU.", + "message": "Validation Failed", + "status": "error", "violations": [ { - "code": "@uuid@", "propertyPath": "cgu_accepted", "message": "Veuillez accepter les CGU." } @@ -297,12 +291,10 @@ Feature: And the JSON should be equal to: """ { - "type": "https://tools.ietf.org/html/rfc2616#section-10", - "title": "An error occurred", - "detail": "email_address: Ceci n'est pas une adresse email valide.", + "message": "Validation Failed", + "status": "error", "violations": [ { - "code": "@uuid@", "propertyPath": "email_address", "message": "Ceci n'est pas une adresse email valide." } @@ -327,12 +319,10 @@ Feature: And the JSON should be equal to: """ { - "type": "https://tools.ietf.org/html/rfc2616#section-10", - "title": "An error occurred", - "detail": "email_address: Cette valeur est déjà utilisée.", + "message": "Validation Failed", + "status": "error", "violations": [ { - "code": "@uuid@", "propertyPath": "email_address", "message": "Cette valeur est déjà utilisée." } @@ -357,12 +347,10 @@ Feature: And the JSON should be equal to: """ { - "type": "https://tools.ietf.org/html/rfc2616#section-10", - "title": "An error occurred", - "detail": "first_name: Cette valeur ne doit pas être vide.", + "message": "Validation Failed", + "status": "error", "violations": [ { - "code": "@uuid@", "propertyPath": "first_name", "message": "Cette valeur ne doit pas être vide." } @@ -387,12 +375,10 @@ Feature: And the JSON should be equal to: """ { - "type": "https://tools.ietf.org/html/rfc2616#section-10", - "title": "An error occurred", - "detail": "first_name: Votre prénom doit comporter au moins 2 caractères.", + "message": "Validation Failed", + "status": "error", "violations": [ { - "code": "@uuid@", "propertyPath": "first_name", "message": "Votre prénom doit comporter au moins 2 caractères." } @@ -417,12 +403,10 @@ Feature: And the JSON should be equal to: """ { - "type": "https://tools.ietf.org/html/rfc2616#section-10", - "title": "An error occurred", - "detail": "first_name: Votre prénom ne peut pas dépasser 50 caractères.", + "message": "Validation Failed", + "status": "error", "violations": [ { - "code": "@uuid@", "propertyPath": "first_name", "message": "Votre prénom ne peut pas dépasser 50 caractères." } @@ -447,12 +431,10 @@ Feature: And the JSON should be equal to: """ { - "type": "https://tools.ietf.org/html/rfc2616#section-10", - "title": "An error occurred", - "detail": "source: Cette valeur ne doit pas être vide.", + "message": "Validation Failed", + "status": "error", "violations": [ { - "code": "@uuid@", "propertyPath": "source", "message": "Cette valeur ne doit pas être vide." } @@ -477,12 +459,10 @@ Feature: And the JSON should be equal to: """ { - "type": "https://tools.ietf.org/html/rfc2616#section-10", - "title": "An error occurred", - "detail": "source: Cette valeur n'est pas valide.", + "message": "Validation Failed", + "status": "error", "violations": [ { - "code": "@uuid@", "propertyPath": "source", "message": "Cette valeur n'est pas valide." } @@ -502,12 +482,10 @@ Feature: And the JSON should be equal to: """ { - "type": "https://tools.ietf.org/html/rfc2616#section-10", - "title": "An error occurred", - "detail": "interests: Une ou plusieurs des valeurs soumises sont invalides.", + "message": "Validation Failed", + "status": "error", "violations": [ { - "code": "@uuid@", "propertyPath": "interests", "message": "Une ou plusieurs des valeurs soumises sont invalides." } @@ -527,12 +505,10 @@ Feature: And the JSON should be equal to: """ { - "type": "https://tools.ietf.org/html/rfc2616#section-10", - "title": "An error occurred", - "detail": "cgu_accepted: Veuillez accepter les CGU.", + "message": "Validation Failed", + "status": "error", "violations": [ { - "code": "@uuid@", "propertyPath": "cgu_accepted", "message": "Veuillez accepter les CGU." } @@ -552,12 +528,10 @@ Feature: And the JSON should be equal to: """ { - "type": "https://tools.ietf.org/html/rfc2616#section-10", - "title": "An error occurred", - "detail": "phone: Cette valeur n'est pas un numéro de téléphone valide.", + "message": "Validation Failed", + "status": "error", "violations": [ { - "code": "@uuid@", "propertyPath": "phone", "message": "Cette valeur n'est pas un numéro de téléphone valide." } @@ -577,12 +551,10 @@ Feature: And the JSON should be equal to: """ { - "type": "https://tools.ietf.org/html/rfc2616#section-10", - "title": "An error occurred", - "detail": "birthdate: Cette valeur doit être comprise entre @string@ et @string@.", + "message": "Validation Failed", + "status": "error", "violations": [ { - "code": "@uuid@", "propertyPath": "birthdate", "message": "Cette valeur doit être comprise entre @string@ et @string@." } @@ -604,12 +576,10 @@ Feature: And the JSON should be equal to: """ { - "type": "https://tools.ietf.org/html/rfc2616#section-10", - "title": "An error occurred", - "detail": "post_address.address: Vous devez saisir au maximum @string@ caractères.", + "message": "Validation Failed", + "status": "error", "violations": [ { - "code": "@uuid@", "propertyPath": "post_address.address", "message": "Vous devez saisir au maximum @string@ caractères." } @@ -631,12 +601,10 @@ Feature: And the JSON should be equal to: """ { - "type": "https://tools.ietf.org/html/rfc2616#section-10", - "title": "An error occurred", - "detail": "post_address.postal_code: Vous devez saisir au maximum @string@ caractères.", + "message": "Validation Failed", + "status": "error", "violations": [ { - "code": "@uuid@", "propertyPath": "post_address.postal_code", "message": "Vous devez saisir au maximum @string@ caractères." } @@ -658,12 +626,10 @@ Feature: And the JSON should be equal to: """ { - "type": "https://tools.ietf.org/html/rfc2616#section-10", - "title": "An error occurred", - "detail": "post_address.city_name: Vous devez saisir au maximum @string@ caractères.", + "message": "Validation Failed", + "status": "error", "violations": [ { - "code": "@uuid@", "propertyPath": "post_address.city_name", "message": "Vous devez saisir au maximum @string@ caractères." } @@ -685,12 +651,10 @@ Feature: And the JSON should be equal to: """ { - "type": "https://tools.ietf.org/html/rfc2616#section-10", - "title": "An error occurred", - "detail": "post_address.country: Ce pays n'est pas valide.", + "message": "Validation Failed", + "status": "error", "violations": [ { - "code": "@uuid@", "propertyPath": "post_address.country", "message": "Ce pays n'est pas valide." } diff --git a/features/api/my_teams.feature b/features/api/my_teams.feature index 57cd97866b9..dd45452d277 100644 --- a/features/api/my_teams.feature +++ b/features/api/my_teams.feature @@ -77,12 +77,10 @@ Feature: And the JSON should be equal to: """ { - "type": "https://tools.ietf.org/html/rfc2616#section-10", - "title": "An error occurred", - "detail": "role: Ce poste n'est pas valide.", + "message": "Validation Failed", + "status": "error", "violations": [ { - "code": "@uuid@", "propertyPath": "role", "message": "Ce poste n'est pas valide." } @@ -104,17 +102,14 @@ Feature: And the JSON should be equal to: """ { - "type": "https://tools.ietf.org/html/rfc2616#section-10", - "title": "An error occurred", - "detail": "adherent: Veuillez sélectionner un militant.\nscope_features: Vous pouvez déléguer que les accès que vous possédez.", + "message": "Validation Failed", + "status": "error", "violations": [ { - "code": "@uuid@", "propertyPath": "adherent", "message": "Veuillez sélectionner un militant." }, { - "code": null, "propertyPath": "scope_features", "message": "Vous pouvez déléguer que les accès que vous possédez." } @@ -137,12 +132,10 @@ Feature: And the JSON should be equal to: """ { - "type": "https://tools.ietf.org/html/rfc2616#section-10", - "title": "An error occurred", - "detail": "adherent: Le militant fait déjà partie de cette équipe.", + "message": "Validation Failed", + "status": "error", "violations": [ { - "code": "@uuid@", "propertyPath": "adherent", "message": "Le militant fait déjà partie de cette équipe." } @@ -165,12 +158,10 @@ Feature: And the JSON should be equal to: """ { - "type": "https://tools.ietf.org/html/rfc2616#section-10", - "title": "An error occurred", - "detail": "adherent: Vous ne pouvez pas ajouter votre compte ou le compte qui vous a délégué l'accès", + "message": "Validation Failed", + "status": "error", "violations": [ { - "code": null, "propertyPath": "adherent", "message": "Vous ne pouvez pas ajouter votre compte ou le compte qui vous a délégué l'accès" } diff --git a/features/api/pap_address.feature b/features/api/pap_address.feature index 392a69e30de..ce853105279 100644 --- a/features/api/pap_address.feature +++ b/features/api/pap_address.feature @@ -717,12 +717,10 @@ Feature: And the JSON should be equal to: """ { - "type": "https://tools.ietf.org/html/rfc2616#section-10", - "title": "An error occurred", - "detail": "type: Le type n'est pas valide.", + "message": "Validation Failed", + "status": "error", "violations": [ { - "code": "@uuid@", "propertyPath": "type", "message": "Le type n'est pas valide." } diff --git a/features/api/pap_campaigns.feature b/features/api/pap_campaigns.feature index 3e4d242d34d..c59889e6c41 100644 --- a/features/api/pap_campaigns.feature +++ b/features/api/pap_campaigns.feature @@ -832,12 +832,10 @@ Feature: And the JSON should be equal to: """ { - "type": "https://tools.ietf.org/html/rfc2616#section-10", - "title": "An error occurred", - "detail": "vote_places: Un ou plusieurs bureaux de vote ne sont pas dans la zone gérée", + "message": "Validation Failed", + "status": "error", "violations": [ { - "code": null, "propertyPath": "vote_places", "message": "Un ou plusieurs bureaux de vote ne sont pas dans la zone gérée" } @@ -956,17 +954,14 @@ Feature: And the JSON should be equal to: """ { - "detail": "survey: La campagne a déjà commencé, vous ne pouvez pas changer le questionnaire.\nvote_places: La campagne a déjà commencé, vous ne pouvez pas retirer les bureaux de vote déjà utilisés.", - "title": "An error occurred", - "type": "https://tools.ietf.org/html/rfc2616#section-10", + "message": "Validation Failed", + "status": "error", "violations": [ { - "code": null, "message": "La campagne a déjà commencé, vous ne pouvez pas changer le questionnaire.", "propertyPath": "survey" }, { - "code": null, "message": "La campagne a déjà commencé, vous ne pouvez pas retirer les bureaux de vote déjà utilisés.", "propertyPath": "vote_places" } @@ -1508,17 +1503,14 @@ Feature: And the JSON should be equal to: """ { - "type": "https://tools.ietf.org/html/rfc2616#section-10", - "title": "An error occurred", - "detail": "building: Cette valeur ne doit pas être nulle.\nstatus: Le statut n'est pas valide.", + "message": "Validation Failed", + "status": "error", "violations": [ { - "code": "@uuid@", "propertyPath": "building", "message": "Cette valeur ne doit pas être nulle." }, { - "code": "@uuid@", "propertyPath": "status", "message": "Le statut n'est pas valide." } @@ -1594,37 +1586,30 @@ Feature: And the JSON should be equal to: """ { - "type": "https://tools.ietf.org/html/rfc2616#section-10", - "title": "An error occurred", - "detail": "status: Le statut n'est pas valide.\nemail_address: Cette valeur n'est pas une adresse email valide.\nemail_address: L'adresse email est trop longue, 255 caractères maximum.\ngender: Ce sexe n'est pas valide.\nage_range: Cette valeur doit être l'un des choix proposés.\nprofession: Cette valeur doit être l'un des choix proposés.", + "message": "Validation Failed", + "status": "error", "violations": [ { - "code": "@uuid@", "propertyPath": "status", "message": "Le statut n'est pas valide." }, { - "code": "@uuid@", "propertyPath": "email_address", "message": "Cette valeur n'est pas une adresse email valide." }, { - "code": "@uuid@", "propertyPath": "email_address", "message": "L'adresse email est trop longue, 255 caractères maximum." }, { - "code": "@uuid@", "propertyPath": "gender", "message": "Ce sexe n'est pas valide." }, { - "code": "@uuid@", "propertyPath": "age_range", "message": "Cette valeur doit être l'un des choix proposés." }, { - "code": "@uuid@", "propertyPath": "profession", "message": "Cette valeur doit être l'un des choix proposés." } @@ -1672,27 +1657,22 @@ Feature: And the JSON should be equal to: """ { - "type": "https://tools.ietf.org/html/rfc2616#section-10", - "title": "An error occurred", - "detail": "title: Cette valeur ne doit pas être vide.\ngoal: Cette valeur ne doit pas être vide.\nfinish_at: La date de fin doit être postérieure à la date de début.\nsurvey: Cette valeur ne doit pas être vide.", + "message": "Validation Failed", + "status": "error", "violations": [ { - "code": "@uuid@", "propertyPath": "title", "message": "Cette valeur ne doit pas être vide." }, { - "code": "@uuid@", "propertyPath": "goal", "message": "Cette valeur ne doit pas être vide." }, { - "code": "@uuid@", "propertyPath": "finish_at", "message": "La date de fin doit être postérieure à la date de début." }, { - "code": "@uuid@", "propertyPath": "survey", "message": "Cette valeur ne doit pas être vide." } @@ -1717,22 +1697,18 @@ Feature: And the JSON should be equal to: """ { - "type": "https://tools.ietf.org/html/rfc2616#section-10", - "title": "An error occurred", - "detail": "goal: Cette valeur doit être supérieure à \"0\".\nbegin_at: La date de début doit être dans le futur.\nfinish_at: La date de fin doit être postérieure à la date de début.", + "message": "Validation Failed", + "status": "error", "violations": [ { - "code": "@uuid@", "propertyPath": "goal", "message": "Cette valeur doit être supérieure à \"0\"." }, { - "code": "@uuid@", "propertyPath": "begin_at", "message": "La date de début doit être dans le futur." }, { - "code": "@uuid@", "propertyPath": "finish_at", "message": "La date de fin doit être postérieure à la date de début." } diff --git a/features/api/phoning_campaigns.feature b/features/api/phoning_campaigns.feature index e065b0062ce..67244ca84bc 100644 --- a/features/api/phoning_campaigns.feature +++ b/features/api/phoning_campaigns.feature @@ -270,12 +270,10 @@ Feature: And the JSON should be equal to: """ { - "detail": "zone: Un rôle national ne peut pas définir de zone.", - "title": "An error occurred", - "type": "https://tools.ietf.org/html/rfc2616#section-10", + "message": "Validation Failed", + "status": "error", "violations": [ { - "code": null, "message": "Un rôle national ne peut pas définir de zone.", "propertyPath": "zone" } @@ -646,12 +644,10 @@ Feature: And the JSON should be equal to: """ { - "type": "https://tools.ietf.org/html/rfc2616#section-10", - "title": "An error occurred", - "detail": "zone: La zone spécifiée n'est pas gérée par votre rôle.", + "message": "Validation Failed", + "status": "error", "violations": [ { - "code": null, "propertyPath": "zone", "message": "La zone spécifiée n'est pas gérée par votre rôle." } @@ -709,12 +705,10 @@ Feature: And the JSON should be equal to: """ { - "type": "https://tools.ietf.org/html/rfc2616#section-10", - "title": "An error occurred", - "detail": "zone: Veuillez spécifier une zone.", + "message": "Validation Failed", + "status": "error", "violations": [ { - "code": null, "propertyPath": "zone", "message": "Veuillez spécifier une zone." } @@ -1139,12 +1133,10 @@ Feature: And the JSON should be equal to: """ { - "type": "https://tools.ietf.org/html/rfc2616#section-10", - "title": "An error occurred", - "detail": "status: Le statut n'est pas valide.", + "message": "Validation Failed", + "status": "error", "violations": [ { - "code": "@uuid@", "propertyPath": "status", "message": "Le statut n'est pas valide." } @@ -1635,22 +1627,18 @@ Feature: And the JSON should be equal to: """ { - "type": "https://tools.ietf.org/html/rfc2616#section-10", - "title": "An error occurred", - "detail": "title: Cette valeur ne doit pas être vide.\ngoal: Cette valeur ne doit pas être vide.\nsurvey: Cette valeur ne doit pas être vide.", + "message": "Validation Failed", + "status": "error", "violations": [ { - "code": "@uuid@", "propertyPath": "title", "message": "Cette valeur ne doit pas être vide." }, { - "code": "@uuid@", "propertyPath": "goal", "message": "Cette valeur ne doit pas être vide." }, { - "code": "@uuid@", "propertyPath": "survey", "message": "Cette valeur ne doit pas être vide." } diff --git a/features/api/profile.feature b/features/api/profile.feature index 8cbc566a390..bcdd312fe50 100644 --- a/features/api/profile.feature +++ b/features/api/profile.feature @@ -210,12 +210,10 @@ Feature: And the JSON should be equal to: """ { - "detail": "@string@", - "title": "An error occurred", - "type": "@string@", + "message": "Validation Failed", + "status": "error", "violations": [ { - "code": "@uuid@", "propertyPath": "gender", "message": "Ce sexe n'est pas valide." } @@ -274,12 +272,10 @@ Feature: And the JSON should be equal to: """ { - "detail": "@string@", - "title": "An error occurred", - "type": "@string@", + "message": "Validation Failed", + "status": "error", "violations": [ { - "code": "@uuid@", "propertyPath": "first_name", "message": "Votre prénom doit comporter au moins 2 caractères." } @@ -298,17 +294,14 @@ Feature: And the JSON should be equal to: """ { - "detail": "@string@", - "title": "An error occurred", - "type": "@string@", + "message": "Validation Failed", + "status": "error", "violations": [ { - "code": "@uuid@", "propertyPath": "first_name", "message": "Votre prénom ne peut pas dépasser 50 caractères." }, { - "code": "@uuid@", "propertyPath": "last_name", "message": "Votre nom ne peut pas dépasser 50 caractères." } @@ -327,27 +320,22 @@ Feature: And the JSON should be equal to: """ { - "detail": "@string@", - "title": "An error occurred", - "type": "@string@", + "message": "Validation Failed", + "status": "error", "violations": [ { - "code": "@uuid@", "propertyPath": "first_name", "message": "Votre prénom doit comporter au moins 2 caractères." }, { - "code": "@uuid@", "propertyPath": "first_name", "message": "Cette valeur ne doit pas être vide." }, { - "code": "@uuid@", "propertyPath": "last_name", "message": "Votre nom doit comporter au moins 1 caractères." }, { - "code": "@uuid@", "propertyPath": "last_name", "message": "Cette valeur ne doit pas être vide." } @@ -387,7 +375,6 @@ Feature: { "violations": [ { - "code": null, "propertyPath": "interests", "message": "Valeur d'intérêt n'est pas valide" } @@ -459,22 +446,18 @@ Feature: And the JSON should be equal to: """ { - "detail": "@string@", - "title": "An error occurred", - "type": "@string@", + "message": "Validation Failed", + "status": "error", "violations": [ { - "code": "@uuid@", "propertyPath": "position", "message": "Le statut d'activité n'est pas valide." }, { - "code": "@uuid@", "propertyPath": "job", "message": "Le métier n'est pas valide." }, { - "code": "@uuid@", "propertyPath": "activity_area", "message": "Le secteur d'activité n'est pas valide." } @@ -514,12 +497,10 @@ Feature: And the JSON should be equal to: """ { - "detail": "@string@", - "title": "An error occurred", - "type": "@string@", + "message": "Validation Failed", + "status": "error", "violations": [ { - "code": "@uuid@", "propertyPath": "nationality", "message": "Cette nationalité n'est pas valide." } @@ -576,12 +557,10 @@ Feature: And the JSON should be equal to: """ { - "detail": "@string@", - "title": "An error occurred", - "type": "@string@", + "message": "Validation Failed", + "status": "error", "violations": [ { - "code": "@uuid@", "propertyPath": "phone", "message": "Cette valeur n'est pas un numéro de téléphone valide." } @@ -626,27 +605,22 @@ Feature: And the JSON should be equal to: """ { - "detail": "@string@", - "title": "An error occurred", - "type": "@string@", + "message": "Validation Failed", + "status": "error", "violations": [ { - "code": "@uuid@", "propertyPath": "facebook_page_url", "message": "Cette URL ne semble pas être une URL Facebook valide." }, { - "code": "@uuid@", "propertyPath": "twitter_page_url", "message": "Cette URL ne semble pas être une URL Twitter valide." }, { - "code": "@uuid@", "propertyPath": "linkedin_page_url", "message": "Cette URL ne semble pas être une URL LinkedIn valide." }, { - "code": "@uuid@", "propertyPath": "telegram_page_url", "message": "Cette URL ne semble pas être une URL Telegram valide." } @@ -688,12 +662,10 @@ Feature: And the JSON should be equal to: """ { - "detail": "@string@", - "title": "An error occurred", - "type": "@string@", + "message": "Validation Failed", + "status": "error", "violations": [ { - "code": "@uuid@", "propertyPath": "email_address", "message": "Ceci n'est pas une adresse email valide." } diff --git a/features/api/report.feature b/features/api/report.feature index e0ff76ce2b3..0524b63b42f 100644 --- a/features/api/report.feature +++ b/features/api/report.feature @@ -60,12 +60,10 @@ Feature: And the JSON should be equal to: """ { - "type": "@string@.isUrl()", - "title": "An error occurred", - "detail": "comment: Vous devez cocher la case \"Autre\" afin de renseigner un commentaire.", + "message": "Validation Failed", + "status": "error", "violations": [ { - "code": null, "propertyPath": "comment", "message": "Vous devez cocher la case \"Autre\" afin de renseigner un commentaire." } @@ -86,12 +84,10 @@ Feature: And the JSON should be equal to: """ { - "type": "@string@.isUrl()", - "title": "An error occurred", - "detail": "reasons: Afin de valider votre signalement, veuillez sélectionner au moins une raison.", + "message": "Validation Failed", + "status": "error", "violations": [ { - "code": "@uuid@", "propertyPath": "reasons", "message": "Afin de valider votre signalement, veuillez sélectionner au moins une raison." } diff --git a/features/api/surveys.feature b/features/api/surveys.feature index 376044205ff..30daf894d11 100644 --- a/features/api/surveys.feature +++ b/features/api/surveys.feature @@ -31,7 +31,6 @@ Feature: "code": "77", "name": "Seine-et-Marne" }, - "city":null, "questions": [ { "id": @integer@, @@ -220,7 +219,6 @@ Feature: "code": "77", "name": "Seine-et-Marne" }, - "city":null, "questions": [ { "id": @integer@, @@ -1112,7 +1110,6 @@ Feature: """ { "type": "local", - "city": null, "zone": { "uuid": "e3efe5c5-906e-11eb-a875-0242ac150002", "code": "77", @@ -1232,7 +1229,7 @@ Feature: } """ Then the response status code should be 400 - And the JSON node "detail" should be equal to "Vous ne pouvez pas créer ou modifier un questionnaire de type local avec le scope national." + And the JSON node "violations[0].message" should be equal to "Vous ne pouvez pas créer ou modifier un questionnaire de type local avec le scope national." Scenario: As a user with national scope I cannot create a local survey Given I am logged with "referent@en-marche-dev.fr" via OAuth client "JeMengage Web" @@ -1287,7 +1284,7 @@ Feature: } """ Then the response status code should be 400 - And the JSON node "detail" should be equal to "Vous ne pouvez pas créer ou modifier un questionnaire de type national avec le scope president_departmental_assembly." + And the JSON node "violations[0].message" should be equal to "Vous ne pouvez pas créer ou modifier un questionnaire de type national avec le scope president_departmental_assembly." Scenario: As a user with national role I can create a national survey Given I am logged with "deputy@en-marche-dev.fr" via OAuth client "JeMengage Web" @@ -1461,7 +1458,6 @@ Feature: """ { "type": "local", - "city": null, "zone": { "uuid": "e3efe6fd-906e-11eb-a875-0242ac150002", "code": "92", @@ -1608,7 +1604,6 @@ Feature: """ { "type": "local", - "city": null, "zone": { "uuid": "e3efe5c5-906e-11eb-a875-0242ac150002", "code": "77", @@ -1945,12 +1940,10 @@ Feature: And the JSON should be equal to: """ { - "type": "https://tools.ietf.org/html/rfc2616#section-10", - "title": "An error occurred", - "detail": "questions: Le questionnaire doit contenir au moins une question.", + "message": "Validation Failed", + "status": "error", "violations": [ { - "code": "@uuid@", "propertyPath": "questions", "message": "Le questionnaire doit contenir au moins une question." } diff --git a/features/api/surveys_replys.feature b/features/api/surveys_replys.feature index dddf0589569..31432684356 100644 --- a/features/api/surveys_replys.feature +++ b/features/api/surveys_replys.feature @@ -146,22 +146,18 @@ Feature: And the JSON should be equal to: """ { - "type": "https://tools.ietf.org/html/rfc2616#section-10", - "title": "An error occurred", - "detail": "profession: Cette valeur doit être l'un des choix proposés.\nage_range: Cette valeur doit être l'un des choix proposés.\ngender: Cette valeur doit être l'un des choix proposés.", + "message": "Validation Failed", + "status": "error", "violations": [ { - "code": "@uuid@", "propertyPath": "profession", "message": "Cette valeur doit être l'un des choix proposés." }, { - "code": "@uuid@", "propertyPath": "age_range", "message": "Cette valeur doit être l'un des choix proposés." }, { - "code": "@uuid@", "propertyPath": "gender", "message": "Cette valeur doit être l'un des choix proposés." } diff --git a/features/api/teams.feature b/features/api/teams.feature index 01d05fc7c52..a00261623c3 100644 --- a/features/api/teams.feature +++ b/features/api/teams.feature @@ -240,12 +240,10 @@ Feature: And the JSON should be equal to: """ { - "type": "https://tools.ietf.org/html/rfc2616#section-10", - "title": "An error occurred", - "detail": "zone: Un rôle national ne peut pas définir de zone.", + "message": "Validation Failed", + "status": "error", "violations": [ { - "code": null, "propertyPath": "zone", "message": "Un rôle national ne peut pas définir de zone." } @@ -430,12 +428,10 @@ Feature: And the JSON should be equal to: """ { - "type": "https://tools.ietf.org/html/rfc2616#section-10", - "title": "An error occurred", - "detail": "zone: La zone spécifiée n'est pas gérée par votre rôle.", + "message": "Validation Failed", + "status": "error", "violations": [ { - "code": null, "propertyPath": "zone", "message": "La zone spécifiée n'est pas gérée par votre rôle." } @@ -466,12 +462,10 @@ Feature: And the JSON should be equal to: """ { - "type": "https://tools.ietf.org/html/rfc2616#section-10", - "title": "An error occurred", - "detail": "zone: Veuillez spécifier une zone.", + "message": "Validation Failed", + "status": "error", "violations": [ { - "code": null, "propertyPath": "zone", "message": "Veuillez spécifier une zone." } @@ -535,12 +529,10 @@ Feature: And the JSON should be equal to: """ { - "type": "https://tools.ietf.org/html/rfc2616#section-10", - "title": "An error occurred", - "detail": "name: Une équipe porte déjà le même nom.", + "message": "Validation Failed", + "status": "error", "violations": [ { - "code": "@uuid@", "propertyPath": "name", "message": "Une équipe porte déjà le même nom." } @@ -570,12 +562,10 @@ Feature: And the JSON should be equal to: """ { - "type": "https://tools.ietf.org/html/rfc2616#section-10", - "title": "An error occurred", - "detail": "name: Une équipe porte déjà le même nom.", + "message": "Validation Failed", + "status": "error", "violations": [ { - "code": "@uuid@", "propertyPath": "name", "message": "Une équipe porte déjà le même nom." } diff --git a/features/api/user.feature b/features/api/user.feature index 1e14d652f88..9d540745b2b 100644 --- a/features/api/user.feature +++ b/features/api/user.feature @@ -126,11 +126,7 @@ Feature: { "uuid": "@string@", "device_uuid": "dd4SOCS-4UlCtO-gZiQGDA", - "postal_code": null, - "surveys": { - "total": 0, - "last_month": 0 - } + "postal_code": null } """ @@ -162,12 +158,10 @@ Feature: And the JSON should be equal to: """ { - "type": "https://tools.ietf.org/html/rfc2616#section-10", - "title": "An error occurred", - "detail": "password: Le mot de passe ne doit pas être vide.", + "message": "Validation Failed", + "status": "error", "violations": [ { - "code": "@uuid@", "propertyPath": "password", "message": "Le mot de passe ne doit pas être vide." } @@ -182,17 +176,14 @@ Feature: "password": "test" } """ - Then the response status code should be 400 - And the response should be in JSON + Then the response should be in JSON And the JSON should be equal to: """ { - "type": "https://tools.ietf.org/html/rfc2616#section-10", - "title": "An error occurred", - "detail": "password: Votre mot de passe doit comporter au moins 8 caractères.", + "message": "Validation Failed", + "status": "error", "violations": [ { - "code": "@uuid@", "propertyPath": "password", "message": "Votre mot de passe doit comporter au moins 8 caractères." }