├── Bundle ├── Resources │ ├── public │ │ ├── web.png │ │ ├── webby.png │ │ ├── fonts │ │ │ └── open-sans │ │ │ │ └── files │ │ │ │ ├── open-sans-math-400-normal.woff │ │ │ │ ├── open-sans-math-700-normal.woff │ │ │ │ ├── open-sans-greek-400-normal.woff │ │ │ │ ├── open-sans-greek-400-normal.woff2 │ │ │ │ ├── open-sans-greek-700-normal.woff │ │ │ │ ├── open-sans-greek-700-normal.woff2 │ │ │ │ ├── open-sans-hebrew-400-normal.woff │ │ │ │ ├── open-sans-hebrew-700-normal.woff │ │ │ │ ├── open-sans-latin-400-normal.woff │ │ │ │ ├── open-sans-latin-400-normal.woff2 │ │ │ │ ├── open-sans-latin-700-normal.woff │ │ │ │ ├── open-sans-latin-700-normal.woff2 │ │ │ │ ├── open-sans-math-400-normal.woff2 │ │ │ │ ├── open-sans-math-700-normal.woff2 │ │ │ │ ├── open-sans-cyrillic-400-normal.woff │ │ │ │ ├── open-sans-cyrillic-400-normal.woff2 │ │ │ │ ├── open-sans-cyrillic-700-normal.woff │ │ │ │ ├── open-sans-cyrillic-700-normal.woff2 │ │ │ │ ├── open-sans-greek-ext-400-normal.woff │ │ │ │ ├── open-sans-greek-ext-700-normal.woff │ │ │ │ ├── open-sans-hebrew-400-normal.woff2 │ │ │ │ ├── open-sans-hebrew-700-normal.woff2 │ │ │ │ ├── open-sans-latin-ext-400-normal.woff │ │ │ │ ├── open-sans-latin-ext-700-normal.woff │ │ │ │ ├── open-sans-symbols-400-normal.woff │ │ │ │ ├── open-sans-symbols-400-normal.woff2 │ │ │ │ ├── open-sans-symbols-700-normal.woff │ │ │ │ ├── open-sans-symbols-700-normal.woff2 │ │ │ │ ├── open-sans-greek-ext-400-normal.woff2 │ │ │ │ ├── open-sans-greek-ext-700-normal.woff2 │ │ │ │ ├── open-sans-latin-ext-400-normal.woff2 │ │ │ │ ├── open-sans-latin-ext-700-normal.woff2 │ │ │ │ ├── open-sans-vietnamese-400-normal.woff │ │ │ │ ├── open-sans-vietnamese-400-normal.woff2 │ │ │ │ ├── open-sans-vietnamese-700-normal.woff │ │ │ │ ├── open-sans-vietnamese-700-normal.woff2 │ │ │ │ ├── open-sans-cyrillic-ext-400-normal.woff │ │ │ │ ├── open-sans-cyrillic-ext-400-normal.woff2 │ │ │ │ ├── open-sans-cyrillic-ext-700-normal.woff │ │ │ │ └── open-sans-cyrillic-ext-700-normal.woff2 │ │ ├── init-redoc-ui.js │ │ ├── init-common-ui.js │ │ └── init-graphiql.js │ ├── config │ │ ├── routing │ │ │ ├── graphql │ │ │ │ ├── graphql.php │ │ │ │ └── graphiql.php │ │ │ ├── errors.php │ │ │ ├── genid.php │ │ │ ├── docs.php │ │ │ ├── api.php │ │ │ └── jsonld.php │ │ ├── filter.php │ │ ├── metadata │ │ │ ├── php.php │ │ │ ├── mutator.php │ │ │ ├── php_doc.php │ │ │ ├── links.php │ │ │ ├── xml.php │ │ │ ├── operation.php │ │ │ └── property_name.php │ │ ├── openapi │ │ │ └── yaml.php │ │ ├── graphql_mercure.php │ │ ├── state │ │ │ ├── hydra.php │ │ │ ├── jsonapi.php │ │ │ ├── swagger_ui.php │ │ │ ├── mercure.php │ │ │ ├── parameter_provider.php │ │ │ ├── http_cache_purger.php │ │ │ ├── security_validator.php │ │ │ ├── jsonld.php │ │ │ ├── processor.php │ │ │ ├── security.php │ │ │ └── object_mapper.php │ │ ├── argument_resolver.php │ │ ├── symfony │ │ │ ├── swagger_ui.php │ │ │ ├── uid.php │ │ │ ├── jsonld.php │ │ │ ├── symfony.php │ │ │ └── controller.php │ │ ├── ramsey_uuid.php │ │ ├── link_security.php │ │ ├── data_collector.php │ │ ├── problem.php │ │ ├── http_cache.php │ │ ├── debug.php │ │ ├── maker.php │ │ ├── messenger.php │ │ ├── validator │ │ │ ├── state.php │ │ │ ├── events.php │ │ │ └── validator.php │ │ ├── doctrine_orm_http_cache_purger.php │ │ ├── graphql │ │ │ ├── validator.php │ │ │ └── security.php │ │ ├── json_streamer │ │ │ ├── json.php │ │ │ └── hydra.php │ │ ├── http_cache_purger.php │ │ ├── security.php │ │ ├── doctrine_orm_mercure_publisher.php │ │ ├── doctrine_odm_mercure_publisher.php │ │ ├── swagger_ui.php │ │ └── json_schema.php │ └── views │ │ ├── Graphiql │ │ └── index.html.twig │ │ └── DataCollector │ │ └── api-platform-icon.svg ├── Test │ ├── ClientTrait.php │ └── Constraint │ │ ├── ArraySubset.php │ │ └── ArraySubsetTrait.php ├── ArgumentResolver │ └── CompatibleValueResolverInterface.php ├── DependencyInjection │ └── Compiler │ │ ├── SerializerMappingLoaderPass.php │ │ ├── AuthenticatorManagerPass.php │ │ ├── AttributeResourcePass.php │ │ ├── GraphQlResolverPass.php │ │ ├── TestClientPass.php │ │ ├── FilterPass.php │ │ ├── GraphQlTypePass.php │ │ ├── DataProviderPass.php │ │ ├── TestMercureHubPass.php │ │ ├── MetadataAwareNameConverterPass.php │ │ ├── MutatorPass.php │ │ └── ElasticsearchClientPass.php ├── DataCollector │ └── DataCollected.php ├── CacheWarmer │ └── CachePoolClearerCacheWarmer.php ├── SwaggerUi │ └── SwaggerUiContext.php └── Command │ └── GraphQlExportCommand.php ├── Maker ├── Resources │ ├── help │ │ ├── MakeStateProvider.txt │ │ ├── MakeStateProcessor.txt │ │ └── MakeFilter.txt │ └── skeleton │ │ ├── StateProcessor.php.tpl │ │ ├── StateProvider.php.tpl │ │ ├── OdmFilter.php.tpl │ │ └── OrmFilter.php.tpl └── Enum │ └── SupportedFilterTypes.php ├── README.md ├── Messenger ├── RemoveStamp.php ├── ContextStamp.php ├── DispatchTrait.php ├── Processor.php └── Metadata │ └── MessengerResourceMetadataCollectionFactory.php ├── Security ├── ObjectVariableCheckerInterface.php ├── Exception │ └── AccessDeniedException.php └── Core │ └── Authorization │ └── ExpressionLanguageProvider.php ├── Action ├── NotFoundAction.php ├── PlaceholderAction.php ├── NotExposedAction.php └── EntrypointAction.php ├── Validator ├── ValidationGroupsGeneratorInterface.php ├── Metadata │ └── Property │ │ └── Restriction │ │ ├── PropertySchemaUniqueRestriction.php │ │ ├── PropertySchemaRegexRestriction.php │ │ ├── PropertySchemaCountRestriction.php │ │ ├── PropertySchemaRestrictionMetadataInterface.php │ │ ├── PropertySchemaOneOfRestriction.php │ │ ├── PropertySchemaGreaterThanOrEqualRestriction.php │ │ ├── PropertySchemaLessThanRestriction.php │ │ ├── PropertySchemaLessThanOrEqualRestriction.php │ │ ├── PropertySchemaGreaterThanRestriction.php │ │ ├── PropertySchemaLengthRestriction.php │ │ ├── PropertySchemaFormat.php │ │ └── PropertySchemaRangeRestriction.php ├── State │ ├── ErrorProvider.php │ └── ValidateProvider.php ├── Validator.php └── Serializer │ └── ValidationExceptionNormalizer.php ├── phpunit.baseline.xml ├── EventListener ├── EventPriorities.php ├── JsonApi │ ├── TransformPaginationParametersListener.php │ ├── TransformFilteringParametersListener.php │ └── TransformSortingParametersListener.php ├── ExceptionListener.php ├── ValidateListener.php ├── RespondListener.php ├── AddFormatListener.php ├── JsonStreamerDeserializeListener.php ├── JsonStreamerSerializeListener.php └── SerializeListener.php ├── LICENSE ├── UriVariableTransformer ├── UlidUriVariableTransformer.php └── UuidUriVariableTransformer.php ├── State └── MercureLinkProcessor.php ├── GraphQl └── Resolver │ └── Factory │ └── DataCollectorResolverFactory.php └── Routing └── SkolemIriConverter.php /Bundle/Resources/public/web.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/api-platform/symfony/HEAD/Bundle/Resources/public/web.png -------------------------------------------------------------------------------- /Bundle/Resources/public/webby.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/api-platform/symfony/HEAD/Bundle/Resources/public/webby.png -------------------------------------------------------------------------------- /Bundle/Resources/public/fonts/open-sans/files/open-sans-math-400-normal.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/api-platform/symfony/HEAD/Bundle/Resources/public/fonts/open-sans/files/open-sans-math-400-normal.woff -------------------------------------------------------------------------------- /Bundle/Resources/public/fonts/open-sans/files/open-sans-math-700-normal.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/api-platform/symfony/HEAD/Bundle/Resources/public/fonts/open-sans/files/open-sans-math-700-normal.woff -------------------------------------------------------------------------------- /Bundle/Resources/public/fonts/open-sans/files/open-sans-greek-400-normal.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/api-platform/symfony/HEAD/Bundle/Resources/public/fonts/open-sans/files/open-sans-greek-400-normal.woff -------------------------------------------------------------------------------- /Bundle/Resources/public/fonts/open-sans/files/open-sans-greek-400-normal.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/api-platform/symfony/HEAD/Bundle/Resources/public/fonts/open-sans/files/open-sans-greek-400-normal.woff2 -------------------------------------------------------------------------------- /Bundle/Resources/public/fonts/open-sans/files/open-sans-greek-700-normal.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/api-platform/symfony/HEAD/Bundle/Resources/public/fonts/open-sans/files/open-sans-greek-700-normal.woff -------------------------------------------------------------------------------- /Bundle/Resources/public/fonts/open-sans/files/open-sans-greek-700-normal.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/api-platform/symfony/HEAD/Bundle/Resources/public/fonts/open-sans/files/open-sans-greek-700-normal.woff2 -------------------------------------------------------------------------------- /Bundle/Resources/public/fonts/open-sans/files/open-sans-hebrew-400-normal.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/api-platform/symfony/HEAD/Bundle/Resources/public/fonts/open-sans/files/open-sans-hebrew-400-normal.woff -------------------------------------------------------------------------------- /Bundle/Resources/public/fonts/open-sans/files/open-sans-hebrew-700-normal.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/api-platform/symfony/HEAD/Bundle/Resources/public/fonts/open-sans/files/open-sans-hebrew-700-normal.woff -------------------------------------------------------------------------------- /Bundle/Resources/public/fonts/open-sans/files/open-sans-latin-400-normal.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/api-platform/symfony/HEAD/Bundle/Resources/public/fonts/open-sans/files/open-sans-latin-400-normal.woff -------------------------------------------------------------------------------- /Bundle/Resources/public/fonts/open-sans/files/open-sans-latin-400-normal.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/api-platform/symfony/HEAD/Bundle/Resources/public/fonts/open-sans/files/open-sans-latin-400-normal.woff2 -------------------------------------------------------------------------------- /Bundle/Resources/public/fonts/open-sans/files/open-sans-latin-700-normal.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/api-platform/symfony/HEAD/Bundle/Resources/public/fonts/open-sans/files/open-sans-latin-700-normal.woff -------------------------------------------------------------------------------- /Bundle/Resources/public/fonts/open-sans/files/open-sans-latin-700-normal.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/api-platform/symfony/HEAD/Bundle/Resources/public/fonts/open-sans/files/open-sans-latin-700-normal.woff2 -------------------------------------------------------------------------------- /Bundle/Resources/public/fonts/open-sans/files/open-sans-math-400-normal.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/api-platform/symfony/HEAD/Bundle/Resources/public/fonts/open-sans/files/open-sans-math-400-normal.woff2 -------------------------------------------------------------------------------- /Bundle/Resources/public/fonts/open-sans/files/open-sans-math-700-normal.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/api-platform/symfony/HEAD/Bundle/Resources/public/fonts/open-sans/files/open-sans-math-700-normal.woff2 -------------------------------------------------------------------------------- /Bundle/Resources/public/fonts/open-sans/files/open-sans-cyrillic-400-normal.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/api-platform/symfony/HEAD/Bundle/Resources/public/fonts/open-sans/files/open-sans-cyrillic-400-normal.woff -------------------------------------------------------------------------------- /Bundle/Resources/public/fonts/open-sans/files/open-sans-cyrillic-400-normal.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/api-platform/symfony/HEAD/Bundle/Resources/public/fonts/open-sans/files/open-sans-cyrillic-400-normal.woff2 -------------------------------------------------------------------------------- /Bundle/Resources/public/fonts/open-sans/files/open-sans-cyrillic-700-normal.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/api-platform/symfony/HEAD/Bundle/Resources/public/fonts/open-sans/files/open-sans-cyrillic-700-normal.woff -------------------------------------------------------------------------------- /Bundle/Resources/public/fonts/open-sans/files/open-sans-cyrillic-700-normal.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/api-platform/symfony/HEAD/Bundle/Resources/public/fonts/open-sans/files/open-sans-cyrillic-700-normal.woff2 -------------------------------------------------------------------------------- /Bundle/Resources/public/fonts/open-sans/files/open-sans-greek-ext-400-normal.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/api-platform/symfony/HEAD/Bundle/Resources/public/fonts/open-sans/files/open-sans-greek-ext-400-normal.woff -------------------------------------------------------------------------------- /Bundle/Resources/public/fonts/open-sans/files/open-sans-greek-ext-700-normal.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/api-platform/symfony/HEAD/Bundle/Resources/public/fonts/open-sans/files/open-sans-greek-ext-700-normal.woff -------------------------------------------------------------------------------- /Bundle/Resources/public/fonts/open-sans/files/open-sans-hebrew-400-normal.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/api-platform/symfony/HEAD/Bundle/Resources/public/fonts/open-sans/files/open-sans-hebrew-400-normal.woff2 -------------------------------------------------------------------------------- /Bundle/Resources/public/fonts/open-sans/files/open-sans-hebrew-700-normal.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/api-platform/symfony/HEAD/Bundle/Resources/public/fonts/open-sans/files/open-sans-hebrew-700-normal.woff2 -------------------------------------------------------------------------------- /Bundle/Resources/public/fonts/open-sans/files/open-sans-latin-ext-400-normal.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/api-platform/symfony/HEAD/Bundle/Resources/public/fonts/open-sans/files/open-sans-latin-ext-400-normal.woff -------------------------------------------------------------------------------- /Bundle/Resources/public/fonts/open-sans/files/open-sans-latin-ext-700-normal.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/api-platform/symfony/HEAD/Bundle/Resources/public/fonts/open-sans/files/open-sans-latin-ext-700-normal.woff -------------------------------------------------------------------------------- /Bundle/Resources/public/fonts/open-sans/files/open-sans-symbols-400-normal.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/api-platform/symfony/HEAD/Bundle/Resources/public/fonts/open-sans/files/open-sans-symbols-400-normal.woff -------------------------------------------------------------------------------- /Bundle/Resources/public/fonts/open-sans/files/open-sans-symbols-400-normal.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/api-platform/symfony/HEAD/Bundle/Resources/public/fonts/open-sans/files/open-sans-symbols-400-normal.woff2 -------------------------------------------------------------------------------- /Bundle/Resources/public/fonts/open-sans/files/open-sans-symbols-700-normal.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/api-platform/symfony/HEAD/Bundle/Resources/public/fonts/open-sans/files/open-sans-symbols-700-normal.woff -------------------------------------------------------------------------------- /Bundle/Resources/public/fonts/open-sans/files/open-sans-symbols-700-normal.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/api-platform/symfony/HEAD/Bundle/Resources/public/fonts/open-sans/files/open-sans-symbols-700-normal.woff2 -------------------------------------------------------------------------------- /Bundle/Resources/public/fonts/open-sans/files/open-sans-greek-ext-400-normal.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/api-platform/symfony/HEAD/Bundle/Resources/public/fonts/open-sans/files/open-sans-greek-ext-400-normal.woff2 -------------------------------------------------------------------------------- /Bundle/Resources/public/fonts/open-sans/files/open-sans-greek-ext-700-normal.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/api-platform/symfony/HEAD/Bundle/Resources/public/fonts/open-sans/files/open-sans-greek-ext-700-normal.woff2 -------------------------------------------------------------------------------- /Bundle/Resources/public/fonts/open-sans/files/open-sans-latin-ext-400-normal.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/api-platform/symfony/HEAD/Bundle/Resources/public/fonts/open-sans/files/open-sans-latin-ext-400-normal.woff2 -------------------------------------------------------------------------------- /Bundle/Resources/public/fonts/open-sans/files/open-sans-latin-ext-700-normal.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/api-platform/symfony/HEAD/Bundle/Resources/public/fonts/open-sans/files/open-sans-latin-ext-700-normal.woff2 -------------------------------------------------------------------------------- /Bundle/Resources/public/fonts/open-sans/files/open-sans-vietnamese-400-normal.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/api-platform/symfony/HEAD/Bundle/Resources/public/fonts/open-sans/files/open-sans-vietnamese-400-normal.woff -------------------------------------------------------------------------------- /Bundle/Resources/public/fonts/open-sans/files/open-sans-vietnamese-400-normal.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/api-platform/symfony/HEAD/Bundle/Resources/public/fonts/open-sans/files/open-sans-vietnamese-400-normal.woff2 -------------------------------------------------------------------------------- /Bundle/Resources/public/fonts/open-sans/files/open-sans-vietnamese-700-normal.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/api-platform/symfony/HEAD/Bundle/Resources/public/fonts/open-sans/files/open-sans-vietnamese-700-normal.woff -------------------------------------------------------------------------------- /Bundle/Resources/public/fonts/open-sans/files/open-sans-vietnamese-700-normal.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/api-platform/symfony/HEAD/Bundle/Resources/public/fonts/open-sans/files/open-sans-vietnamese-700-normal.woff2 -------------------------------------------------------------------------------- /Bundle/Resources/public/fonts/open-sans/files/open-sans-cyrillic-ext-400-normal.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/api-platform/symfony/HEAD/Bundle/Resources/public/fonts/open-sans/files/open-sans-cyrillic-ext-400-normal.woff -------------------------------------------------------------------------------- /Bundle/Resources/public/fonts/open-sans/files/open-sans-cyrillic-ext-400-normal.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/api-platform/symfony/HEAD/Bundle/Resources/public/fonts/open-sans/files/open-sans-cyrillic-ext-400-normal.woff2 -------------------------------------------------------------------------------- /Bundle/Resources/public/fonts/open-sans/files/open-sans-cyrillic-ext-700-normal.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/api-platform/symfony/HEAD/Bundle/Resources/public/fonts/open-sans/files/open-sans-cyrillic-ext-700-normal.woff -------------------------------------------------------------------------------- /Bundle/Resources/public/fonts/open-sans/files/open-sans-cyrillic-ext-700-normal.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/api-platform/symfony/HEAD/Bundle/Resources/public/fonts/open-sans/files/open-sans-cyrillic-ext-700-normal.woff2 -------------------------------------------------------------------------------- /Bundle/Resources/public/init-redoc-ui.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | window.onload = () => { 4 | const data = JSON.parse(document.getElementById('swagger-data').innerText); 5 | 6 | Redoc.init(data.spec, {}, document.getElementById('swagger-ui')); 7 | }; 8 | -------------------------------------------------------------------------------- /Maker/Resources/help/MakeStateProvider.txt: -------------------------------------------------------------------------------- 1 | The %command.name% command generates a new API Platform state provider class. 2 | 3 | php %command.full_name% AwesomeStateProvider 4 | 5 | If the argument is missing, the command will ask for the class name interactively. 6 | -------------------------------------------------------------------------------- /Maker/Resources/help/MakeStateProcessor.txt: -------------------------------------------------------------------------------- 1 | The %command.name% command generates a new API Platform state processor class. 2 | 3 | php %command.full_name% AwesomeStateProcessor 4 | 5 | If the argument is missing, the command will ask for the class name interactively. 6 | -------------------------------------------------------------------------------- /Bundle/Resources/public/init-common-ui.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | const graphiQlLink = document.querySelector('.graphiql-link'); 4 | if (graphiQlLink) { 5 | graphiQlLink.addEventListener('click', e => { 6 | if (!e.target.hasAttribute('href')) { 7 | alert('GraphQL support is not enabled, see https://api-platform.com/docs/core/graphql/'); 8 | } 9 | }); 10 | } 11 | -------------------------------------------------------------------------------- /Maker/Resources/help/MakeFilter.txt: -------------------------------------------------------------------------------- 1 | The %command.name% command generates a new API Platform filter class for Doctrine ORM or ODM (MongoDB). 2 | 3 | php %command.full_name% type name 4 | 5 | Important: 6 | - If you omit the argument, the command will prompt you to choose the filter type interactively. 7 | - If you omit the argument, the command will ask you to enter the class name interactively. 8 | 9 | Elasticsearch isn't supported yet. 10 | -------------------------------------------------------------------------------- /Maker/Resources/skeleton/StateProcessor.php.tpl: -------------------------------------------------------------------------------- 1 | 3 | 4 | namespace ; 5 | 6 | use ApiPlatform\Metadata\Operation; 7 | use ApiPlatform\State\ProcessorInterface; 8 | 9 | class implements ProcessorInterface 10 | { 11 | public function process(mixed $data, Operation $operation, array $uriVariables = [], array $context = []): void 12 | { 13 | // Handle the state 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /Maker/Enum/SupportedFilterTypes.php: -------------------------------------------------------------------------------- 1 | 7 | * 8 | * For the full copyright and license information, please view the LICENSE 9 | * file that was distributed with this source code. 10 | */ 11 | 12 | declare(strict_types=1); 13 | 14 | namespace ApiPlatform\Symfony\Maker\Enum; 15 | 16 | enum SupportedFilterTypes: string 17 | { 18 | case ORM = 'orm'; 19 | case ODM = 'odm'; 20 | } 21 | -------------------------------------------------------------------------------- /Maker/Resources/skeleton/StateProvider.php.tpl: -------------------------------------------------------------------------------- 1 | 3 | 4 | namespace ; 5 | 6 | use ApiPlatform\Metadata\Operation; 7 | use ApiPlatform\State\ProviderInterface; 8 | 9 | class implements ProviderInterface 10 | { 11 | public function provide(Operation $operation, array $uriVariables = [], array $context = []): object|array|null 12 | { 13 | // Retrieve the state from somewhere 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # API Platform for Symfony 2 | 3 | Integration of [Symfony](https://symfony.com) with the [API Platform](https://api-platform.com) framework. 4 | 5 | [Documentation](https://api-platform.com/docs/symfony/) 6 | 7 | > [!CAUTION] 8 | > 9 | > This is a read-only sub split of `api-platform/core`, please 10 | > [report issues](https://github.com/api-platform/core/issues) and 11 | > [send Pull Requests](https://github.com/api-platform/core/pulls) 12 | > in the [core API Platform repository](https://github.com/api-platform/core). 13 | -------------------------------------------------------------------------------- /Bundle/Resources/config/routing/graphql/graphql.php: -------------------------------------------------------------------------------- 1 | 7 | * 8 | * For the full copyright and license information, please view the LICENSE 9 | * file that was distributed with this source code. 10 | */ 11 | 12 | declare(strict_types=1); 13 | 14 | namespace Symfony\Component\Routing\Loader\Configurator; 15 | 16 | return function (RoutingConfigurator $routes) { 17 | $routes->add('api_graphql_entrypoint', '/graphql') 18 | ->controller('api_platform.graphql.action.entrypoint'); 19 | }; 20 | -------------------------------------------------------------------------------- /Bundle/Resources/config/routing/errors.php: -------------------------------------------------------------------------------- 1 | 7 | * 8 | * For the full copyright and license information, please view the LICENSE 9 | * file that was distributed with this source code. 10 | */ 11 | 12 | declare(strict_types=1); 13 | 14 | namespace Symfony\Component\Routing\Loader\Configurator; 15 | 16 | return function (RoutingConfigurator $routes) { 17 | $routes->add('api_validation_errors', '/validation_errors/{id}') 18 | ->controller('api_platform.action.not_exposed') 19 | ->methods(['GET', 'HEAD']); 20 | }; 21 | -------------------------------------------------------------------------------- /Messenger/RemoveStamp.php: -------------------------------------------------------------------------------- 1 | 7 | * 8 | * For the full copyright and license information, please view the LICENSE 9 | * file that was distributed with this source code. 10 | */ 11 | 12 | declare(strict_types=1); 13 | 14 | namespace ApiPlatform\Symfony\Messenger; 15 | 16 | use Symfony\Component\Messenger\Stamp\StampInterface; 17 | 18 | /** 19 | * Hints that the resource in the envelope must be removed. 20 | * 21 | * @author Kévin Dunglas 22 | */ 23 | final class RemoveStamp implements StampInterface 24 | { 25 | } 26 | -------------------------------------------------------------------------------- /Bundle/Resources/config/routing/graphql/graphiql.php: -------------------------------------------------------------------------------- 1 | 7 | * 8 | * For the full copyright and license information, please view the LICENSE 9 | * file that was distributed with this source code. 10 | */ 11 | 12 | declare(strict_types=1); 13 | 14 | namespace Symfony\Component\Routing\Loader\Configurator; 15 | 16 | return function (RoutingConfigurator $routes) { 17 | $routes->add('api_graphql_graphiql', '/graphql/graphiql') 18 | ->controller('api_platform.graphql.action.graphiql') 19 | ->methods(['GET', 'HEAD']); 20 | }; 21 | -------------------------------------------------------------------------------- /Bundle/Test/ClientTrait.php: -------------------------------------------------------------------------------- 1 | 7 | * 8 | * For the full copyright and license information, please view the LICENSE 9 | * file that was distributed with this source code. 10 | */ 11 | 12 | declare(strict_types=1); 13 | 14 | namespace ApiPlatform\Symfony\Bundle\Test; 15 | 16 | trait ClientTrait 17 | { 18 | public function withOptions(array $options): static 19 | { 20 | $clone = clone $this; 21 | $clone->defaultOptions = self::mergeDefaultOptions($options, $this->defaultOptions); 22 | 23 | return $clone; 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /Bundle/Resources/config/filter.php: -------------------------------------------------------------------------------- 1 | 7 | * 8 | * For the full copyright and license information, please view the LICENSE 9 | * file that was distributed with this source code. 10 | */ 11 | 12 | declare(strict_types=1); 13 | 14 | namespace Symfony\Component\DependencyInjection\Loader\Configurator; 15 | 16 | return function (ContainerConfigurator $container) { 17 | $services = $container->services(); 18 | 19 | $services->set('api_platform.filter_locator', 'Symfony\Component\DependencyInjection\ServiceLocator') 20 | ->tag('container.service_locator'); 21 | }; 22 | -------------------------------------------------------------------------------- /Security/ObjectVariableCheckerInterface.php: -------------------------------------------------------------------------------- 1 | 7 | * 8 | * For the full copyright and license information, please view the LICENSE 9 | * file that was distributed with this source code. 10 | */ 11 | 12 | declare(strict_types=1); 13 | 14 | namespace ApiPlatform\Symfony\Security; 15 | 16 | interface ObjectVariableCheckerInterface 17 | { 18 | /** 19 | * @param string $expression a Expression Language string 20 | * @param array $variables 21 | */ 22 | public function usesObjectVariable(string $expression, array $variables = []): bool; 23 | } 24 | -------------------------------------------------------------------------------- /Bundle/Resources/config/routing/genid.php: -------------------------------------------------------------------------------- 1 | 7 | * 8 | * For the full copyright and license information, please view the LICENSE 9 | * file that was distributed with this source code. 10 | */ 11 | 12 | declare(strict_types=1); 13 | 14 | namespace Symfony\Component\Routing\Loader\Configurator; 15 | 16 | return function (RoutingConfigurator $routes) { 17 | $routes->add('api_genid', '/.well-known/genid/{id}') 18 | ->controller('api_platform.action.not_exposed') 19 | ->methods(['GET', 'HEAD']) 20 | ->defaults([ 21 | '_api_respond' => true, 22 | ]); 23 | }; 24 | -------------------------------------------------------------------------------- /Action/NotFoundAction.php: -------------------------------------------------------------------------------- 1 | 7 | * 8 | * For the full copyright and license information, please view the LICENSE 9 | * file that was distributed with this source code. 10 | */ 11 | 12 | declare(strict_types=1); 13 | 14 | namespace ApiPlatform\Symfony\Action; 15 | 16 | use Symfony\Component\HttpKernel\Exception\NotFoundHttpException; 17 | 18 | /** 19 | * An action which always returns HTTP 404 Not Found. Useful for disabling an operation. 20 | */ 21 | final class NotFoundAction 22 | { 23 | public function __invoke(): void 24 | { 25 | throw new NotFoundHttpException(); 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /Bundle/Resources/config/routing/docs.php: -------------------------------------------------------------------------------- 1 | 7 | * 8 | * For the full copyright and license information, please view the LICENSE 9 | * file that was distributed with this source code. 10 | */ 11 | 12 | declare(strict_types=1); 13 | 14 | namespace Symfony\Component\Routing\Loader\Configurator; 15 | 16 | return function (RoutingConfigurator $routes) { 17 | $routes->add('api_doc', '/docs.{_format}') 18 | ->controller('api_platform.action.documentation') 19 | ->methods(['GET', 'HEAD']) 20 | ->defaults([ 21 | '_format' => null, 22 | '_api_respond' => true, 23 | ]); 24 | }; 25 | -------------------------------------------------------------------------------- /Action/PlaceholderAction.php: -------------------------------------------------------------------------------- 1 | 7 | * 8 | * For the full copyright and license information, please view the LICENSE 9 | * file that was distributed with this source code. 10 | */ 11 | 12 | declare(strict_types=1); 13 | 14 | namespace ApiPlatform\Symfony\Action; 15 | 16 | /** 17 | * Placeholder returning the data passed in parameter. 18 | * 19 | * @author Kévin Dunglas 20 | */ 21 | final class PlaceholderAction 22 | { 23 | /** 24 | * @param object $data 25 | * 26 | * @return object 27 | */ 28 | public function __invoke($data) 29 | { 30 | return $data; 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /Bundle/Resources/config/metadata/php.php: -------------------------------------------------------------------------------- 1 | 7 | * 8 | * For the full copyright and license information, please view the LICENSE 9 | * file that was distributed with this source code. 10 | */ 11 | 12 | declare(strict_types=1); 13 | 14 | namespace Symfony\Component\DependencyInjection\Loader\Configurator; 15 | 16 | return function (ContainerConfigurator $container) { 17 | $services = $container->services(); 18 | 19 | $services->set('api_platform.metadata.resource_extractor.php_file', 'ApiPlatform\Metadata\Extractor\PhpFileResourceExtractor') 20 | ->args([ 21 | [], 22 | service('service_container'), 23 | ]); 24 | }; 25 | -------------------------------------------------------------------------------- /Validator/ValidationGroupsGeneratorInterface.php: -------------------------------------------------------------------------------- 1 | 7 | * 8 | * For the full copyright and license information, please view the LICENSE 9 | * file that was distributed with this source code. 10 | */ 11 | 12 | declare(strict_types=1); 13 | 14 | namespace ApiPlatform\Symfony\Validator; 15 | 16 | use Symfony\Component\Validator\Constraints\GroupSequence; 17 | 18 | /** 19 | * Generates validation groups for an object. 20 | * 21 | * @author Tomas Norkūnas 22 | */ 23 | interface ValidationGroupsGeneratorInterface 24 | { 25 | /** 26 | * @return GroupSequence|string[] 27 | */ 28 | public function __invoke(object $object): array|GroupSequence; 29 | } 30 | -------------------------------------------------------------------------------- /Bundle/Resources/config/openapi/yaml.php: -------------------------------------------------------------------------------- 1 | 7 | * 8 | * For the full copyright and license information, please view the LICENSE 9 | * file that was distributed with this source code. 10 | */ 11 | 12 | declare(strict_types=1); 13 | 14 | namespace Symfony\Component\DependencyInjection\Loader\Configurator; 15 | 16 | return function (ContainerConfigurator $container) { 17 | $services = $container->services(); 18 | 19 | $services->set('api_platform.yamlopenapi.encoder', 'ApiPlatform\Serializer\YamlEncoder') 20 | ->args([ 21 | 'yamlopenapi', 22 | service('serializer.encoder.yaml')->nullOnInvalid(), 23 | ]) 24 | ->tag('serializer.encoder'); 25 | }; 26 | -------------------------------------------------------------------------------- /Bundle/Resources/config/metadata/mutator.php: -------------------------------------------------------------------------------- 1 | 7 | * 8 | * For the full copyright and license information, please view the LICENSE 9 | * file that was distributed with this source code. 10 | */ 11 | 12 | declare(strict_types=1); 13 | 14 | namespace Symfony\Component\DependencyInjection\Loader\Configurator; 15 | 16 | return function (ContainerConfigurator $container) { 17 | $services = $container->services(); 18 | 19 | $services->set('api_platform.metadata.mutator_collection.resource', 'ApiPlatform\Metadata\Mutator\ResourceMutatorCollection'); 20 | 21 | $services->set('api_platform.metadata.mutator_collection.operation', 'ApiPlatform\Metadata\Mutator\OperationMutatorCollection'); 22 | }; 23 | -------------------------------------------------------------------------------- /Bundle/Resources/config/graphql_mercure.php: -------------------------------------------------------------------------------- 1 | 7 | * 8 | * For the full copyright and license information, please view the LICENSE 9 | * file that was distributed with this source code. 10 | */ 11 | 12 | declare(strict_types=1); 13 | 14 | namespace Symfony\Component\DependencyInjection\Loader\Configurator; 15 | 16 | return function (ContainerConfigurator $container) { 17 | $services = $container->services(); 18 | 19 | $services->set('api_platform.graphql.subscription.mercure_iri_generator', 'ApiPlatform\GraphQl\Subscription\MercureSubscriptionIriGenerator') 20 | ->args([ 21 | service('router.request_context'), 22 | service('Symfony\Component\Mercure\HubRegistry'), 23 | ]); 24 | }; 25 | -------------------------------------------------------------------------------- /Bundle/Resources/config/routing/api.php: -------------------------------------------------------------------------------- 1 | 7 | * 8 | * For the full copyright and license information, please view the LICENSE 9 | * file that was distributed with this source code. 10 | */ 11 | 12 | declare(strict_types=1); 13 | 14 | namespace Symfony\Component\Routing\Loader\Configurator; 15 | 16 | return function (RoutingConfigurator $routes) { 17 | $routes->add('api_entrypoint', '/{index}.{_format}') 18 | ->controller('api_platform.action.entrypoint') 19 | ->methods(['GET', 'HEAD']) 20 | ->defaults([ 21 | '_format' => null, 22 | '_api_respond' => true, 23 | 'index' => 'index', 24 | ]) 25 | ->requirements([ 26 | 'index' => 'index', 27 | ]); 28 | }; 29 | -------------------------------------------------------------------------------- /Bundle/Resources/config/state/hydra.php: -------------------------------------------------------------------------------- 1 | 7 | * 8 | * For the full copyright and license information, please view the LICENSE 9 | * file that was distributed with this source code. 10 | */ 11 | 12 | declare(strict_types=1); 13 | 14 | namespace Symfony\Component\DependencyInjection\Loader\Configurator; 15 | 16 | return function (ContainerConfigurator $container) { 17 | $services = $container->services(); 18 | 19 | $services->set('api_platform.hydra.processor.link', 'ApiPlatform\Hydra\State\HydraLinkProcessor') 20 | ->decorate('api_platform.state_processor.respond', null, 410) 21 | ->args([ 22 | service('api_platform.hydra.processor.link.inner'), 23 | service('api_platform.router'), 24 | ]); 25 | }; 26 | -------------------------------------------------------------------------------- /Bundle/Resources/config/state/jsonapi.php: -------------------------------------------------------------------------------- 1 | 7 | * 8 | * For the full copyright and license information, please view the LICENSE 9 | * file that was distributed with this source code. 10 | */ 11 | 12 | declare(strict_types=1); 13 | 14 | namespace Symfony\Component\DependencyInjection\Loader\Configurator; 15 | 16 | return function (ContainerConfigurator $container) { 17 | $services = $container->services(); 18 | 19 | $services->set('api_platform.jsonapi.state_provider', 'ApiPlatform\JsonApi\State\JsonApiProvider') 20 | ->decorate('api_platform.state_provider.read', null, 0) 21 | ->args([ 22 | service('api_platform.jsonapi.state_provider.inner'), 23 | '%api_platform.collection.order_parameter_name%', 24 | ]); 25 | }; 26 | -------------------------------------------------------------------------------- /Bundle/Test/Constraint/ArraySubset.php: -------------------------------------------------------------------------------- 1 | 7 | * 8 | * For the full copyright and license information, please view the LICENSE 9 | * file that was distributed with this source code. 10 | */ 11 | 12 | declare(strict_types=1); 13 | 14 | namespace ApiPlatform\Symfony\Bundle\Test\Constraint; 15 | 16 | use PHPUnit\Framework\Constraint\Constraint; 17 | 18 | /** 19 | * Is used for phpunit >= 9. 20 | * 21 | * @internal 22 | */ 23 | final class ArraySubset extends Constraint 24 | { 25 | use ArraySubsetTrait; 26 | 27 | /** 28 | * {@inheritdoc} 29 | */ 30 | public function evaluate($other, string $description = '', bool $returnResult = false): ?bool 31 | { 32 | return $this->_evaluate($other, $description, $returnResult); 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /Bundle/Resources/config/state/swagger_ui.php: -------------------------------------------------------------------------------- 1 | 7 | * 8 | * For the full copyright and license information, please view the LICENSE 9 | * file that was distributed with this source code. 10 | */ 11 | 12 | declare(strict_types=1); 13 | 14 | namespace Symfony\Component\DependencyInjection\Loader\Configurator; 15 | 16 | return function (ContainerConfigurator $container) { 17 | $services = $container->services(); 18 | 19 | $services->set('api_platform.swagger_ui.provider', 'ApiPlatform\Symfony\Bundle\SwaggerUi\SwaggerUiProvider') 20 | ->decorate('api_platform.state_provider.read', null, 0) 21 | ->args([ 22 | service('api_platform.swagger_ui.provider.inner'), 23 | service('api_platform.openapi.factory'), 24 | ]); 25 | }; 26 | -------------------------------------------------------------------------------- /Bundle/Resources/config/routing/jsonld.php: -------------------------------------------------------------------------------- 1 | 7 | * 8 | * For the full copyright and license information, please view the LICENSE 9 | * file that was distributed with this source code. 10 | */ 11 | 12 | declare(strict_types=1); 13 | 14 | namespace Symfony\Component\Routing\Loader\Configurator; 15 | 16 | return function (RoutingConfigurator $routes) { 17 | $routes->add('api_jsonld_context', '/contexts/{shortName}.{_format}') 18 | ->controller('api_platform.jsonld.action.context') 19 | ->methods(['GET', 'HEAD']) 20 | ->defaults([ 21 | '_format' => 'jsonld', 22 | '_api_respond' => true, 23 | ]) 24 | ->requirements([ 25 | 'shortName' => '[^.]+', 26 | '_format' => 'jsonld', 27 | ]); 28 | }; 29 | -------------------------------------------------------------------------------- /Bundle/Resources/config/metadata/php_doc.php: -------------------------------------------------------------------------------- 1 | 7 | * 8 | * For the full copyright and license information, please view the LICENSE 9 | * file that was distributed with this source code. 10 | */ 11 | 12 | declare(strict_types=1); 13 | 14 | namespace Symfony\Component\DependencyInjection\Loader\Configurator; 15 | 16 | return function (ContainerConfigurator $container) { 17 | $services = $container->services(); 18 | 19 | $services->set('api_platform.metadata.resource.metadata_collection_factory.php_doc', 'ApiPlatform\Metadata\Resource\Factory\PhpDocResourceMetadataCollectionFactory') 20 | ->decorate('api_platform.metadata.resource.metadata_collection_factory', null, 200) 21 | ->args([service('api_platform.metadata.resource.metadata_collection_factory.php_doc.inner')]); 22 | }; 23 | -------------------------------------------------------------------------------- /Bundle/Resources/config/argument_resolver.php: -------------------------------------------------------------------------------- 1 | 7 | * 8 | * For the full copyright and license information, please view the LICENSE 9 | * file that was distributed with this source code. 10 | */ 11 | 12 | declare(strict_types=1); 13 | 14 | namespace Symfony\Component\DependencyInjection\Loader\Configurator; 15 | 16 | return function (ContainerConfigurator $container) { 17 | $services = $container->services(); 18 | 19 | $services->set('api_platform.argument_resolver.payload', 'ApiPlatform\Symfony\Bundle\ArgumentResolver\PayloadArgumentResolver') 20 | ->args([ 21 | service('api_platform.metadata.resource.metadata_collection_factory'), 22 | service('api_platform.serializer.context_builder'), 23 | ]) 24 | ->tag('controller.argument_value_resolver'); 25 | }; 26 | -------------------------------------------------------------------------------- /Bundle/Resources/config/metadata/links.php: -------------------------------------------------------------------------------- 1 | 7 | * 8 | * For the full copyright and license information, please view the LICENSE 9 | * file that was distributed with this source code. 10 | */ 11 | 12 | declare(strict_types=1); 13 | 14 | namespace Symfony\Component\DependencyInjection\Loader\Configurator; 15 | 16 | return function (ContainerConfigurator $container) { 17 | $services = $container->services(); 18 | 19 | $services->set('api_platform.metadata.resource.link_factory', 'ApiPlatform\Metadata\Resource\Factory\LinkFactory') 20 | ->args([ 21 | service('api_platform.metadata.property.name_collection_factory'), 22 | service('api_platform.metadata.property.metadata_factory'), 23 | service('api_platform.resource_class_resolver'), 24 | ]); 25 | }; 26 | -------------------------------------------------------------------------------- /Bundle/ArgumentResolver/CompatibleValueResolverInterface.php: -------------------------------------------------------------------------------- 1 | 7 | * 8 | * For the full copyright and license information, please view the LICENSE 9 | * file that was distributed with this source code. 10 | */ 11 | 12 | declare(strict_types=1); 13 | 14 | namespace ApiPlatform\Symfony\Bundle\ArgumentResolver; 15 | 16 | use Symfony\Component\HttpKernel\Controller\ArgumentValueResolverInterface; 17 | use Symfony\Component\HttpKernel\Controller\ValueResolverInterface; 18 | 19 | if (interface_exists(ValueResolverInterface::class)) { 20 | /** @internal */ 21 | interface CompatibleValueResolverInterface extends ValueResolverInterface 22 | { 23 | } 24 | } else { 25 | /** @internal */ 26 | interface CompatibleValueResolverInterface extends ArgumentValueResolverInterface 27 | { 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /Bundle/Resources/config/symfony/swagger_ui.php: -------------------------------------------------------------------------------- 1 | 7 | * 8 | * For the full copyright and license information, please view the LICENSE 9 | * file that was distributed with this source code. 10 | */ 11 | 12 | declare(strict_types=1); 13 | 14 | namespace Symfony\Component\DependencyInjection\Loader\Configurator; 15 | 16 | return function (ContainerConfigurator $container) { 17 | $services = $container->services(); 18 | 19 | $services->set('api_platform.swagger_ui.documentation.provider', 'ApiPlatform\Symfony\Bundle\SwaggerUi\SwaggerUiProvider') 20 | ->decorate('api_platform.state_provider.documentation.read', null, 0) 21 | ->args([ 22 | service('api_platform.swagger_ui.documentation.provider.inner'), 23 | service('api_platform.openapi.factory'), 24 | ]); 25 | }; 26 | -------------------------------------------------------------------------------- /Bundle/Resources/config/state/mercure.php: -------------------------------------------------------------------------------- 1 | 7 | * 8 | * For the full copyright and license information, please view the LICENSE 9 | * file that was distributed with this source code. 10 | */ 11 | 12 | declare(strict_types=1); 13 | 14 | namespace Symfony\Component\DependencyInjection\Loader\Configurator; 15 | 16 | return function (ContainerConfigurator $container) { 17 | $services = $container->services(); 18 | 19 | $services->set('api_platform.mercure.processor.add_link_header', 'ApiPlatform\Symfony\State\MercureLinkProcessor') 20 | ->decorate('api_platform.state_processor.respond', null, 400) 21 | ->args([ 22 | service('api_platform.mercure.processor.add_link_header.inner'), 23 | service('Symfony\Component\Mercure\Discovery')->ignoreOnInvalid(), 24 | ]); 25 | }; 26 | -------------------------------------------------------------------------------- /Bundle/Resources/config/ramsey_uuid.php: -------------------------------------------------------------------------------- 1 | 7 | * 8 | * For the full copyright and license information, please view the LICENSE 9 | * file that was distributed with this source code. 10 | */ 11 | 12 | declare(strict_types=1); 13 | 14 | namespace Symfony\Component\DependencyInjection\Loader\Configurator; 15 | 16 | return function (ContainerConfigurator $container) { 17 | $services = $container->services(); 18 | 19 | $services->set('api_platform.serializer.uuid_denormalizer', 'ApiPlatform\RamseyUuid\Serializer\UuidDenormalizer') 20 | ->tag('serializer.normalizer'); 21 | 22 | $services->set('api_platform.ramsey_uuid.uri_variables.transformer.uuid', 'ApiPlatform\RamseyUuid\UriVariableTransformer\UuidUriVariableTransformer') 23 | ->tag('api_platform.uri_variables.transformer', ['priority' => -100]); 24 | }; 25 | -------------------------------------------------------------------------------- /Security/Exception/AccessDeniedException.php: -------------------------------------------------------------------------------- 1 | 7 | * 8 | * For the full copyright and license information, please view the LICENSE 9 | * file that was distributed with this source code. 10 | */ 11 | 12 | declare(strict_types=1); 13 | 14 | namespace ApiPlatform\Symfony\Security\Exception; 15 | 16 | use ApiPlatform\Metadata\Exception\HttpExceptionInterface; 17 | use Symfony\Component\Security\Core\Exception\AccessDeniedException as ExceptionAccessDeniedException; 18 | 19 | /** 20 | * TODO: deprecate in favor of Metadata. 21 | */ 22 | final class AccessDeniedException extends ExceptionAccessDeniedException implements HttpExceptionInterface 23 | { 24 | public function getStatusCode(): int 25 | { 26 | return 403; 27 | } 28 | 29 | public function getHeaders(): array 30 | { 31 | return []; 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /Bundle/Resources/config/symfony/uid.php: -------------------------------------------------------------------------------- 1 | 7 | * 8 | * For the full copyright and license information, please view the LICENSE 9 | * file that was distributed with this source code. 10 | */ 11 | 12 | declare(strict_types=1); 13 | 14 | namespace Symfony\Component\DependencyInjection\Loader\Configurator; 15 | 16 | return function (ContainerConfigurator $container) { 17 | $services = $container->services(); 18 | 19 | $services->set('api_platform.symfony.uri_variables.transformer.ulid', 'ApiPlatform\Symfony\UriVariableTransformer\UlidUriVariableTransformer') 20 | ->tag('api_platform.uri_variables.transformer'); 21 | 22 | $services->set('api_platform.symfony.uri_variables.transformer.uuid', 'ApiPlatform\Symfony\UriVariableTransformer\UuidUriVariableTransformer') 23 | ->tag('api_platform.uri_variables.transformer'); 24 | }; 25 | -------------------------------------------------------------------------------- /phpunit.baseline.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | -------------------------------------------------------------------------------- /Bundle/Resources/config/state/parameter_provider.php: -------------------------------------------------------------------------------- 1 | 7 | * 8 | * For the full copyright and license information, please view the LICENSE 9 | * file that was distributed with this source code. 10 | */ 11 | 12 | declare(strict_types=1); 13 | 14 | namespace Symfony\Component\DependencyInjection\Loader\Configurator; 15 | 16 | return function (ContainerConfigurator $container) { 17 | $services = $container->services(); 18 | 19 | $services->set('api_platform.state_provider.parameter.iri_converter', 'ApiPlatform\State\ParameterProvider\IriConverterParameterProvider') 20 | ->args([ 21 | service('api_platform.iri_converter'), 22 | service('logger')->ignoreOnInvalid(), 23 | ]) 24 | ->tag('api_platform.parameter_provider', ['key' => 'ApiPlatform\State\ParameterProvider\IriConverterParameterProvider', 'priority' => -895]); 25 | }; 26 | -------------------------------------------------------------------------------- /Bundle/Resources/config/state/http_cache_purger.php: -------------------------------------------------------------------------------- 1 | 7 | * 8 | * For the full copyright and license information, please view the LICENSE 9 | * file that was distributed with this source code. 10 | */ 11 | 12 | declare(strict_types=1); 13 | 14 | namespace Symfony\Component\DependencyInjection\Loader\Configurator; 15 | 16 | return function (ContainerConfigurator $container) { 17 | $services = $container->services(); 18 | 19 | $services->set('api_platform.http_cache_purger.processor.add_tags', 'ApiPlatform\HttpCache\State\AddTagsProcessor') 20 | ->decorate('api_platform.state_processor.respond', null, 0) 21 | ->args([ 22 | service('api_platform.http_cache_purger.processor.add_tags.inner'), 23 | service('api_platform.iri_converter'), 24 | service('api_platform.http_cache.purger')->nullOnInvalid(), 25 | ]); 26 | }; 27 | -------------------------------------------------------------------------------- /Bundle/Resources/config/state/security_validator.php: -------------------------------------------------------------------------------- 1 | 7 | * 8 | * For the full copyright and license information, please view the LICENSE 9 | * file that was distributed with this source code. 10 | */ 11 | 12 | declare(strict_types=1); 13 | 14 | namespace Symfony\Component\DependencyInjection\Loader\Configurator; 15 | 16 | return function (ContainerConfigurator $container) { 17 | $services = $container->services(); 18 | 19 | $services->set('api_platform.state_provider.access_checker.post_validate', 'ApiPlatform\Symfony\Security\State\AccessCheckerProvider') 20 | ->decorate('api_platform.state_provider.validate', null, 0) 21 | ->args([ 22 | service('api_platform.state_provider.access_checker.post_validate.inner'), 23 | service('api_platform.security.resource_access_checker'), 24 | 'post_validate', 25 | ]); 26 | }; 27 | -------------------------------------------------------------------------------- /Bundle/Resources/config/link_security.php: -------------------------------------------------------------------------------- 1 | 7 | * 8 | * For the full copyright and license information, please view the LICENSE 9 | * file that was distributed with this source code. 10 | */ 11 | 12 | declare(strict_types=1); 13 | 14 | namespace Symfony\Component\DependencyInjection\Loader\Configurator; 15 | 16 | return function (ContainerConfigurator $container) { 17 | $services = $container->services(); 18 | 19 | $services->set('api_platform.state_provider.read_link', 'ApiPlatform\State\ParameterProvider\ReadLinkParameterProvider') 20 | ->args([ 21 | service('api_platform.state_provider.locator'), 22 | service('api_platform.metadata.resource.metadata_collection_factory'), 23 | ]) 24 | ->tag('api_platform.parameter_provider', ['key' => 'ApiPlatform\State\ParameterProvider\ReadLinkParameterProvider', 'priority' => -890]); 25 | }; 26 | -------------------------------------------------------------------------------- /Bundle/Resources/config/metadata/xml.php: -------------------------------------------------------------------------------- 1 | 7 | * 8 | * For the full copyright and license information, please view the LICENSE 9 | * file that was distributed with this source code. 10 | */ 11 | 12 | declare(strict_types=1); 13 | 14 | namespace Symfony\Component\DependencyInjection\Loader\Configurator; 15 | 16 | return function (ContainerConfigurator $container) { 17 | $services = $container->services(); 18 | 19 | $services->set('api_platform.metadata.resource_extractor.xml', 'ApiPlatform\Metadata\Extractor\XmlResourceExtractor') 20 | ->args([ 21 | [], 22 | service('service_container'), 23 | ]); 24 | 25 | $services->set('api_platform.metadata.property_extractor.xml', 'ApiPlatform\Metadata\Extractor\XmlPropertyExtractor') 26 | ->args([ 27 | [], 28 | service('service_container'), 29 | ]); 30 | }; 31 | -------------------------------------------------------------------------------- /Bundle/Resources/config/data_collector.php: -------------------------------------------------------------------------------- 1 | 7 | * 8 | * For the full copyright and license information, please view the LICENSE 9 | * file that was distributed with this source code. 10 | */ 11 | 12 | declare(strict_types=1); 13 | 14 | namespace Symfony\Component\DependencyInjection\Loader\Configurator; 15 | 16 | return function (ContainerConfigurator $container) { 17 | $services = $container->services(); 18 | 19 | $services->set('api_platform.data_collector.request', 'ApiPlatform\Symfony\Bundle\DataCollector\RequestDataCollector') 20 | ->args([ 21 | service('api_platform.metadata.resource.metadata_collection_factory'), 22 | service('api_platform.filter_locator'), 23 | ]) 24 | ->tag('data_collector', ['template' => '@ApiPlatform/DataCollector/request.html.twig', 'id' => 'api_platform.data_collector.request', 'priority' => 334]); 25 | }; 26 | -------------------------------------------------------------------------------- /Maker/Resources/skeleton/OdmFilter.php.tpl: -------------------------------------------------------------------------------- 1 | 3 | 4 | namespace ; 5 | 6 | use ApiPlatform\Doctrine\Odm\Filter\FilterInterface; 7 | use ApiPlatform\Metadata\BackwardCompatibleFilterDescriptionTrait; 8 | use ApiPlatform\Metadata\Operation; 9 | use Doctrine\ODM\MongoDB\Aggregation\Builder; 10 | 11 | class implements FilterInterface 12 | { 13 | use BackwardCompatibleFilterDescriptionTrait; // Here for backward compatibility, keep it until 5.0. 14 | 15 | public function apply(Builder $aggregationBuilder, string $resourceClass, ?Operation $operation = null, array &$context = []): void 16 | { 17 | // Retrieve the parameter and it's value 18 | // $parameter = $context['parameter']; 19 | // $value = $parameter->getValue(); 20 | 21 | // Retrieve the property 22 | // $property = $parameter->getProperty(); 23 | 24 | // TODO: make your awesome query using the $aggregationBuilder 25 | // $aggregationBuilder-> 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /Bundle/Resources/config/problem.php: -------------------------------------------------------------------------------- 1 | 7 | * 8 | * For the full copyright and license information, please view the LICENSE 9 | * file that was distributed with this source code. 10 | */ 11 | 12 | declare(strict_types=1); 13 | 14 | namespace Symfony\Component\DependencyInjection\Loader\Configurator; 15 | 16 | return function (ContainerConfigurator $container) { 17 | $services = $container->services(); 18 | 19 | $services->set('api_platform.problem.encoder', 'ApiPlatform\Serializer\JsonEncoder') 20 | ->args(['jsonproblem']) 21 | ->tag('serializer.encoder'); 22 | 23 | $services->set('api_platform.problem.normalizer.validation_exception', 'ApiPlatform\Symfony\Validator\Serializer\ValidationExceptionNormalizer') 24 | ->args([ 25 | service('api_platform.serializer.normalizer.item'), 26 | service('api_platform.name_converter')->ignoreOnInvalid(), 27 | ]) 28 | ->tag('serializer.normalizer', ['priority' => -800]); 29 | }; 30 | -------------------------------------------------------------------------------- /EventListener/EventPriorities.php: -------------------------------------------------------------------------------- 1 | 7 | * 8 | * For the full copyright and license information, please view the LICENSE 9 | * file that was distributed with this source code. 10 | */ 11 | 12 | declare(strict_types=1); 13 | 14 | namespace ApiPlatform\Symfony\EventListener; 15 | 16 | /** 17 | * Constants for common priorities. 18 | * 19 | * @author Kévin Dunglas 20 | */ 21 | final class EventPriorities 22 | { 23 | // kernel.request 24 | public const PRE_READ = 5; 25 | public const POST_READ = 3; 26 | public const PRE_DESERIALIZE = 3; 27 | public const POST_DESERIALIZE = 1; 28 | // kernel.view 29 | public const PRE_VALIDATE = 65; 30 | public const POST_VALIDATE = 63; 31 | public const PRE_WRITE = 33; 32 | public const POST_WRITE = 31; 33 | public const PRE_SERIALIZE = 17; 34 | public const POST_SERIALIZE = 15; 35 | public const PRE_RESPOND = 9; 36 | // kernel.response 37 | public const POST_RESPOND = 0; 38 | } 39 | -------------------------------------------------------------------------------- /Bundle/Resources/config/http_cache.php: -------------------------------------------------------------------------------- 1 | 7 | * 8 | * For the full copyright and license information, please view the LICENSE 9 | * file that was distributed with this source code. 10 | */ 11 | 12 | declare(strict_types=1); 13 | 14 | namespace Symfony\Component\DependencyInjection\Loader\Configurator; 15 | 16 | return function (ContainerConfigurator $container) { 17 | $services = $container->services(); 18 | 19 | $services->set('api_platform.http_cache.processor.add_headers', 'ApiPlatform\HttpCache\State\AddHeadersProcessor') 20 | ->decorate('api_platform.state_processor.respond', null, 0) 21 | ->args([ 22 | service('api_platform.http_cache.processor.add_headers.inner'), 23 | '%api_platform.http_cache.etag%', 24 | '%api_platform.http_cache.max_age%', 25 | '%api_platform.http_cache.shared_max_age%', 26 | '%api_platform.http_cache.vary%', 27 | '%api_platform.http_cache.public%', 28 | ]); 29 | }; 30 | -------------------------------------------------------------------------------- /Action/NotExposedAction.php: -------------------------------------------------------------------------------- 1 | 7 | * 8 | * For the full copyright and license information, please view the LICENSE 9 | * file that was distributed with this source code. 10 | */ 11 | 12 | declare(strict_types=1); 13 | 14 | namespace ApiPlatform\Symfony\Action; 15 | 16 | use ApiPlatform\Metadata\Exception\NotExposedHttpException; 17 | use Symfony\Component\HttpFoundation\Request; 18 | 19 | /** 20 | * An action which always returns HTTP 404 Not Found with an explanation for why the operation is not exposed. 21 | */ 22 | final class NotExposedAction 23 | { 24 | public function __invoke(Request $request): never 25 | { 26 | $message = 'This route does not aim to be called.'; 27 | if ('api_genid' === $request->attributes->get('_route')) { 28 | $message = 'This route is not exposed on purpose. It generates an IRI for a collection resource without identifier nor item operation.'; 29 | } 30 | 31 | throw new NotExposedHttpException($message); 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /Bundle/DependencyInjection/Compiler/SerializerMappingLoaderPass.php: -------------------------------------------------------------------------------- 1 | 7 | * 8 | * For the full copyright and license information, please view the LICENSE 9 | * file that was distributed with this source code. 10 | */ 11 | 12 | declare(strict_types=1); 13 | 14 | namespace ApiPlatform\Symfony\Bundle\DependencyInjection\Compiler; 15 | 16 | use Symfony\Component\DependencyInjection\Compiler\CompilerPassInterface; 17 | use Symfony\Component\DependencyInjection\ContainerBuilder; 18 | 19 | final class SerializerMappingLoaderPass implements CompilerPassInterface 20 | { 21 | public function process(ContainerBuilder $container): void 22 | { 23 | $chainLoader = $container->getDefinition('serializer.mapping.chain_loader'); 24 | $loaders = $chainLoader->getArgument(0); 25 | $loaders[] = $container->getDefinition('api_platform.serializer.property_metadata_loader'); 26 | $container->getDefinition('serializer.mapping.cache_warmer')->replaceArgument(0, $loaders); 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /Bundle/Resources/config/debug.php: -------------------------------------------------------------------------------- 1 | 7 | * 8 | * For the full copyright and license information, please view the LICENSE 9 | * file that was distributed with this source code. 10 | */ 11 | 12 | declare(strict_types=1); 13 | 14 | namespace Symfony\Component\DependencyInjection\Loader\Configurator; 15 | 16 | return function (ContainerConfigurator $container) { 17 | $services = $container->services(); 18 | 19 | $services->set('debug.var_dumper.cloner', 'Symfony\Component\VarDumper\Cloner\VarCloner'); 20 | 21 | $services->set('debug.var_dumper.cli_dumper', 'Symfony\Component\VarDumper\Dumper\CliDumper'); 22 | 23 | $services->set('debug.api_platform.debug_resource.command', 'ApiPlatform\Symfony\Bundle\Command\DebugResourceCommand') 24 | ->args([ 25 | service('api_platform.metadata.resource.metadata_collection_factory'), 26 | service('debug.var_dumper.cloner'), 27 | service('debug.var_dumper.cli_dumper'), 28 | ]) 29 | ->tag('console.command'); 30 | }; 31 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT license 2 | 3 | Copyright (c) 2015-present Kévin Dunglas 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is furnished 10 | to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 21 | THE SOFTWARE. 22 | -------------------------------------------------------------------------------- /Bundle/Resources/config/state/jsonld.php: -------------------------------------------------------------------------------- 1 | 7 | * 8 | * For the full copyright and license information, please view the LICENSE 9 | * file that was distributed with this source code. 10 | */ 11 | 12 | declare(strict_types=1); 13 | 14 | namespace Symfony\Component\DependencyInjection\Loader\Configurator; 15 | 16 | return function (ContainerConfigurator $container) { 17 | $services = $container->services(); 18 | 19 | $services->set('api_platform.jsonld.action.context', 'ApiPlatform\JsonLd\Action\ContextAction') 20 | ->public() 21 | ->args([ 22 | service('api_platform.jsonld.context_builder'), 23 | service('api_platform.metadata.resource.name_collection_factory'), 24 | service('api_platform.metadata.resource.metadata_collection_factory'), 25 | service('api_platform.state_provider.main')->nullOnInvalid(), 26 | service('api_platform.state_processor.main')->nullOnInvalid(), 27 | service('api_platform.serializer')->nullOnInvalid(), 28 | ]); 29 | }; 30 | -------------------------------------------------------------------------------- /Bundle/DependencyInjection/Compiler/AuthenticatorManagerPass.php: -------------------------------------------------------------------------------- 1 | 7 | * 8 | * For the full copyright and license information, please view the LICENSE 9 | * file that was distributed with this source code. 10 | */ 11 | 12 | declare(strict_types=1); 13 | 14 | namespace ApiPlatform\Symfony\Bundle\DependencyInjection\Compiler; 15 | 16 | use Symfony\Component\DependencyInjection\Compiler\CompilerPassInterface; 17 | use Symfony\Component\DependencyInjection\ContainerBuilder; 18 | 19 | /** 20 | * Checks if the new authenticator manager exists. 21 | * 22 | * @internal 23 | * 24 | * @author Alan Poulain 25 | */ 26 | final class AuthenticatorManagerPass implements CompilerPassInterface 27 | { 28 | /** 29 | * {@inheritdoc} 30 | */ 31 | public function process(ContainerBuilder $container): void 32 | { 33 | if ($container->has('security.authenticator.manager')) { 34 | $container->getDefinition('api_platform.security.resource_access_checker')->setArgument(5, false); 35 | } 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /Bundle/Resources/config/maker.php: -------------------------------------------------------------------------------- 1 | 7 | * 8 | * For the full copyright and license information, please view the LICENSE 9 | * file that was distributed with this source code. 10 | */ 11 | 12 | declare(strict_types=1); 13 | 14 | namespace Symfony\Component\DependencyInjection\Loader\Configurator; 15 | 16 | return function (ContainerConfigurator $container) { 17 | $services = $container->services(); 18 | 19 | $services->set('api_platform.maker.command.state_processor', 'ApiPlatform\Symfony\Maker\MakeStateProcessor') 20 | ->args([param('api_platform.maker.namespace_prefix')]) 21 | ->tag('maker.command'); 22 | 23 | $services->set('api_platform.maker.command.state_provider', 'ApiPlatform\Symfony\Maker\MakeStateProvider') 24 | ->args([param('api_platform.maker.namespace_prefix')]) 25 | ->tag('maker.command'); 26 | 27 | $services->set('api_platform.maker.command.filter', 'ApiPlatform\Symfony\Maker\MakeFilter') 28 | ->args([param('api_platform.maker.namespace_prefix')]) 29 | ->tag('maker.command'); 30 | }; 31 | -------------------------------------------------------------------------------- /Bundle/Resources/config/symfony/jsonld.php: -------------------------------------------------------------------------------- 1 | 7 | * 8 | * For the full copyright and license information, please view the LICENSE 9 | * file that was distributed with this source code. 10 | */ 11 | 12 | declare(strict_types=1); 13 | 14 | namespace Symfony\Component\DependencyInjection\Loader\Configurator; 15 | 16 | return function (ContainerConfigurator $container) { 17 | $services = $container->services(); 18 | 19 | $services->set('api_platform.jsonld.action.context', 'ApiPlatform\JsonLd\Action\ContextAction') 20 | ->public() 21 | ->args([ 22 | service('api_platform.jsonld.context_builder'), 23 | service('api_platform.metadata.resource.name_collection_factory'), 24 | service('api_platform.metadata.resource.metadata_collection_factory'), 25 | service('api_platform.state_provider.documentation')->nullOnInvalid(), 26 | service('api_platform.state_processor.documentation')->nullOnInvalid(), 27 | service('api_platform.serializer')->nullOnInvalid(), 28 | ]); 29 | }; 30 | -------------------------------------------------------------------------------- /Bundle/DataCollector/DataCollected.php: -------------------------------------------------------------------------------- 1 | 7 | * 8 | * For the full copyright and license information, please view the LICENSE 9 | * file that was distributed with this source code. 10 | */ 11 | 12 | declare(strict_types=1); 13 | 14 | namespace ApiPlatform\Symfony\Bundle\DataCollector; 15 | 16 | use Symfony\Component\VarDumper\Cloner\Data; 17 | 18 | final class DataCollected 19 | { 20 | public function __construct(private readonly string $resourceClass, private readonly Data $resourceMetadataCollection, private readonly array $filters, private readonly array $counters) 21 | { 22 | } 23 | 24 | public function getResourceClass(): string 25 | { 26 | return $this->resourceClass; 27 | } 28 | 29 | public function getResourceMetadataCollection(): Data 30 | { 31 | return $this->resourceMetadataCollection; 32 | } 33 | 34 | public function getFilters(): array 35 | { 36 | return $this->filters; 37 | } 38 | 39 | public function getCounters(): array 40 | { 41 | return $this->counters; 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /Validator/Metadata/Property/Restriction/PropertySchemaUniqueRestriction.php: -------------------------------------------------------------------------------- 1 | 7 | * 8 | * For the full copyright and license information, please view the LICENSE 9 | * file that was distributed with this source code. 10 | */ 11 | 12 | declare(strict_types=1); 13 | 14 | namespace ApiPlatform\Symfony\Validator\Metadata\Property\Restriction; 15 | 16 | use ApiPlatform\Metadata\ApiProperty; 17 | use Symfony\Component\Validator\Constraint; 18 | use Symfony\Component\Validator\Constraints\Unique; 19 | 20 | /** 21 | * @author Tomas Norkūnas 22 | */ 23 | final class PropertySchemaUniqueRestriction implements PropertySchemaRestrictionMetadataInterface 24 | { 25 | /** 26 | * {@inheritdoc} 27 | */ 28 | public function create(Constraint $constraint, ApiProperty $propertyMetadata): array 29 | { 30 | return ['uniqueItems' => true]; 31 | } 32 | 33 | /** 34 | * {@inheritdoc} 35 | */ 36 | public function supports(Constraint $constraint, ApiProperty $propertyMetadata): bool 37 | { 38 | return $constraint instanceof Unique; 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /Security/Core/Authorization/ExpressionLanguageProvider.php: -------------------------------------------------------------------------------- 1 | 7 | * 8 | * For the full copyright and license information, please view the LICENSE 9 | * file that was distributed with this source code. 10 | */ 11 | 12 | declare(strict_types=1); 13 | 14 | namespace ApiPlatform\Symfony\Security\Core\Authorization; 15 | 16 | use Symfony\Component\ExpressionLanguage\ExpressionFunction; 17 | use Symfony\Component\ExpressionLanguage\ExpressionFunctionProviderInterface; 18 | 19 | /** 20 | * Registers API Platform's Expression Language functions. 21 | * 22 | * @author Yanick Witschi 23 | */ 24 | final class ExpressionLanguageProvider implements ExpressionFunctionProviderInterface 25 | { 26 | public function getFunctions(): array 27 | { 28 | return [ 29 | new ExpressionFunction('is_granted', static fn ($attributes, $object = 'null'): string => \sprintf('$auth_checker->isGranted(%s, %s)', $attributes, $object), static fn (array $variables, $attributes, $object = null) => $variables['auth_checker']->isGranted($attributes, $object)), 30 | ]; 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /Bundle/Resources/config/messenger.php: -------------------------------------------------------------------------------- 1 | 7 | * 8 | * For the full copyright and license information, please view the LICENSE 9 | * file that was distributed with this source code. 10 | */ 11 | 12 | declare(strict_types=1); 13 | 14 | namespace Symfony\Component\DependencyInjection\Loader\Configurator; 15 | 16 | return function (ContainerConfigurator $container) { 17 | $services = $container->services(); 18 | 19 | $services->alias('api_platform.message_bus', 'messenger.default_bus'); 20 | 21 | $services->set('api_platform.messenger.metadata.resource.metadata_collection_factory', 'ApiPlatform\Symfony\Messenger\Metadata\MessengerResourceMetadataCollectionFactory') 22 | ->decorate('api_platform.metadata.resource.metadata_collection_factory', null, 50) 23 | ->args([service('api_platform.messenger.metadata.resource.metadata_collection_factory.inner')]); 24 | 25 | $services->set('ApiPlatform\Symfony\Messenger\Processor', 'ApiPlatform\Symfony\Messenger\Processor') 26 | ->args([service('api_platform.message_bus')]) 27 | ->tag('api_platform.state_processor', ['priority' => -900]); 28 | }; 29 | -------------------------------------------------------------------------------- /Bundle/Resources/config/validator/state.php: -------------------------------------------------------------------------------- 1 | 7 | * 8 | * For the full copyright and license information, please view the LICENSE 9 | * file that was distributed with this source code. 10 | */ 11 | 12 | declare(strict_types=1); 13 | 14 | namespace Symfony\Component\DependencyInjection\Loader\Configurator; 15 | 16 | return function (ContainerConfigurator $container) { 17 | $services = $container->services(); 18 | 19 | $services->set('api_platform.state_provider.validate', 'ApiPlatform\Symfony\Validator\State\ValidateProvider') 20 | ->decorate('api_platform.state_provider.main', null, 200) 21 | ->args([ 22 | service('api_platform.state_provider.validate.inner'), 23 | service('api_platform.validator'), 24 | ]); 25 | 26 | $services->set('api_platform.state_provider.parameter_validator', 'ApiPlatform\Symfony\Validator\State\ParameterValidatorProvider') 27 | ->public() 28 | ->decorate('api_platform.state_provider.main', null, 191) 29 | ->args([ 30 | service('validator'), 31 | service('api_platform.state_provider.parameter_validator.inner'), 32 | ]); 33 | }; 34 | -------------------------------------------------------------------------------- /Messenger/ContextStamp.php: -------------------------------------------------------------------------------- 1 | 7 | * 8 | * For the full copyright and license information, please view the LICENSE 9 | * file that was distributed with this source code. 10 | */ 11 | 12 | declare(strict_types=1); 13 | 14 | namespace ApiPlatform\Symfony\Messenger; 15 | 16 | use Symfony\Component\HttpFoundation\Request; 17 | use Symfony\Component\Messenger\Stamp\StampInterface; 18 | 19 | /** 20 | * An envelope stamp with context which related to a message. 21 | * 22 | * @author Sergii Pavlenko 23 | */ 24 | final class ContextStamp implements StampInterface 25 | { 26 | private readonly array $context; 27 | 28 | public function __construct(array $context = []) 29 | { 30 | /* Symfony does not guarantee that the Request object is serializable */ 31 | if (($request = ($context['request'] ?? null)) && $request instanceof Request) { 32 | unset($context['request']); 33 | } 34 | 35 | $this->context = $context; 36 | } 37 | 38 | /** 39 | * Get the context related to a message. 40 | */ 41 | public function getContext(): array 42 | { 43 | return $this->context; 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /Bundle/Resources/config/doctrine_orm_http_cache_purger.php: -------------------------------------------------------------------------------- 1 | 7 | * 8 | * For the full copyright and license information, please view the LICENSE 9 | * file that was distributed with this source code. 10 | */ 11 | 12 | declare(strict_types=1); 13 | 14 | namespace Symfony\Component\DependencyInjection\Loader\Configurator; 15 | 16 | return function (ContainerConfigurator $container) { 17 | $services = $container->services(); 18 | 19 | $services->set('api_platform.doctrine.listener.http_cache.purge', 'ApiPlatform\Symfony\Doctrine\EventListener\PurgeHttpCacheListener') 20 | ->args([ 21 | service('api_platform.http_cache.purger'), 22 | service('api_platform.iri_converter'), 23 | service('api_platform.resource_class_resolver'), 24 | service('api_platform.property_accessor'), 25 | service('api_platform.object_mapper')->nullOnInvalid(), 26 | service('api_platform.object_mapper.metadata_factory')->nullOnInvalid(), 27 | ]) 28 | ->tag('doctrine.event_listener', ['event' => 'preUpdate']) 29 | ->tag('doctrine.event_listener', ['event' => 'onFlush']) 30 | ->tag('doctrine.event_listener', ['event' => 'postFlush']); 31 | }; 32 | -------------------------------------------------------------------------------- /Bundle/Resources/config/graphql/validator.php: -------------------------------------------------------------------------------- 1 | 7 | * 8 | * For the full copyright and license information, please view the LICENSE 9 | * file that was distributed with this source code. 10 | */ 11 | 12 | declare(strict_types=1); 13 | 14 | namespace Symfony\Component\DependencyInjection\Loader\Configurator; 15 | 16 | return function (ContainerConfigurator $container) { 17 | $services = $container->services(); 18 | 19 | $services->set('api_platform.graphql.state_provider.validate', 'ApiPlatform\Symfony\Validator\State\ValidateProvider') 20 | ->decorate('api_platform.graphql.state_provider', null, 200) 21 | ->args([ 22 | service('api_platform.graphql.state_provider.validate.inner'), 23 | service('api_platform.validator'), 24 | ]); 25 | 26 | $services->set('api_platform.graphql.state_provider.validate_after_resolver', 'ApiPlatform\Symfony\Validator\State\ValidateProvider') 27 | ->decorate('api_platform.graphql.state_provider', null, 180) 28 | ->args([ 29 | service('api_platform.graphql.state_provider.validate_after_resolver.inner'), 30 | service('api_platform.validator'), 31 | 'canValidateAfterResolver', 32 | ]); 33 | }; 34 | -------------------------------------------------------------------------------- /Maker/Resources/skeleton/OrmFilter.php.tpl: -------------------------------------------------------------------------------- 1 | 3 | 4 | namespace ; 5 | 6 | use ApiPlatform\Doctrine\Orm\Filter\FilterInterface; 7 | use ApiPlatform\Doctrine\Orm\Util\QueryNameGeneratorInterface; 8 | use ApiPlatform\Metadata\BackwardCompatibleFilterDescriptionTrait; 9 | use ApiPlatform\Metadata\Operation; 10 | use Doctrine\ORM\QueryBuilder; 11 | 12 | class implements FilterInterface 13 | { 14 | use BackwardCompatibleFilterDescriptionTrait; // Here for backward compatibility, keep it until 5.0. 15 | 16 | public function apply(QueryBuilder $queryBuilder, QueryNameGeneratorInterface $queryNameGenerator, string $resourceClass, ?Operation $operation = null, array $context = []): void 17 | { 18 | // Retrieve the parameter and it's value 19 | // $parameter = $context['parameter']; 20 | // $value = $parameter->getValue(); 21 | 22 | // Retrieve the property 23 | // $property = $parameter->getProperty(); 24 | 25 | // Retrieve alias and parameter name 26 | // $alias = $queryBuilder->getRootAliases()[0]; 27 | // $parameterName = $queryNameGenerator->generateParameterName($property); 28 | 29 | // TODO: make your awesome query using the $queryBuilder 30 | // $queryBuilder-> 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /UriVariableTransformer/UlidUriVariableTransformer.php: -------------------------------------------------------------------------------- 1 | 7 | * 8 | * For the full copyright and license information, please view the LICENSE 9 | * file that was distributed with this source code. 10 | */ 11 | 12 | declare(strict_types=1); 13 | 14 | namespace ApiPlatform\Symfony\UriVariableTransformer; 15 | 16 | use ApiPlatform\Metadata\Exception\InvalidUriVariableException; 17 | use ApiPlatform\Metadata\UriVariableTransformerInterface; 18 | use Symfony\Component\Uid\Ulid; 19 | 20 | /** 21 | * Transforms an ULID string to an instance of Symfony\Component\Uid\Ulid. 22 | */ 23 | final class UlidUriVariableTransformer implements UriVariableTransformerInterface 24 | { 25 | /** 26 | * {@inheritdoc} 27 | */ 28 | public function transform(mixed $value, array $types, array $context = []): Ulid 29 | { 30 | try { 31 | return Ulid::fromString($value); 32 | } catch (\InvalidArgumentException $e) { 33 | throw new InvalidUriVariableException($e->getMessage(), $e->getCode(), $e); 34 | } 35 | } 36 | 37 | /** 38 | * {@inheritdoc} 39 | */ 40 | public function supportsTransformation(mixed $value, array $types, array $context = []): bool 41 | { 42 | return \is_string($value) && is_a($types[0], Ulid::class, true); 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /UriVariableTransformer/UuidUriVariableTransformer.php: -------------------------------------------------------------------------------- 1 | 7 | * 8 | * For the full copyright and license information, please view the LICENSE 9 | * file that was distributed with this source code. 10 | */ 11 | 12 | declare(strict_types=1); 13 | 14 | namespace ApiPlatform\Symfony\UriVariableTransformer; 15 | 16 | use ApiPlatform\Metadata\Exception\InvalidUriVariableException; 17 | use ApiPlatform\Metadata\UriVariableTransformerInterface; 18 | use Symfony\Component\Uid\Uuid; 19 | 20 | /** 21 | * Transforms an UUID string to an instance of Symfony\Component\Uid\Uuid. 22 | */ 23 | final class UuidUriVariableTransformer implements UriVariableTransformerInterface 24 | { 25 | /** 26 | * {@inheritdoc} 27 | */ 28 | public function transform(mixed $value, array $types, array $context = []): Uuid 29 | { 30 | try { 31 | return Uuid::fromString($value); 32 | } catch (\InvalidArgumentException $e) { 33 | throw new InvalidUriVariableException($e->getMessage(), $e->getCode(), $e); 34 | } 35 | } 36 | 37 | /** 38 | * {@inheritdoc} 39 | */ 40 | public function supportsTransformation(mixed $value, array $types, array $context = []): bool 41 | { 42 | return \is_string($value) && is_a($types[0], Uuid::class, true); 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /Bundle/Resources/views/Graphiql/index.html.twig: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | {% block head_metas %} 5 | 6 | {% endblock %} 7 | 8 | {% block title %} 9 | {% if title %}{{ title }} - {% endif %}API Platform 10 | {% endblock %} 11 | 12 | {% block head_stylesheets %} 13 | 14 | 15 | {% endblock %} 16 | 17 | {% block head_javascript %} 18 | {# json_encode(65) is for JSON_UNESCAPED_SLASHES|JSON_HEX_TAG to avoid JS XSS #} 19 | 20 | {% endblock %} 21 | 22 | 23 | 24 |
Loading...
25 | 26 | {% block body_javascript %} 27 | 28 | 29 | 30 | 31 | {% endblock %} 32 | 33 | 34 | 35 | -------------------------------------------------------------------------------- /Validator/Metadata/Property/Restriction/PropertySchemaRegexRestriction.php: -------------------------------------------------------------------------------- 1 | 7 | * 8 | * For the full copyright and license information, please view the LICENSE 9 | * file that was distributed with this source code. 10 | */ 11 | 12 | declare(strict_types=1); 13 | 14 | namespace ApiPlatform\Symfony\Validator\Metadata\Property\Restriction; 15 | 16 | use ApiPlatform\Metadata\ApiProperty; 17 | use Symfony\Component\Validator\Constraint; 18 | use Symfony\Component\Validator\Constraints\Regex; 19 | 20 | /** 21 | * Class PropertySchemaRegexRestriction. 22 | * 23 | * @author Andrii Penchuk penja7@gmail.com 24 | */ 25 | class PropertySchemaRegexRestriction implements PropertySchemaRestrictionMetadataInterface 26 | { 27 | /** 28 | * {@inheritdoc} 29 | * 30 | * @param Regex $constraint 31 | */ 32 | public function create(Constraint $constraint, ApiProperty $propertyMetadata): array 33 | { 34 | if (null !== ($htmlPattern = $constraint->getHtmlPattern())) { 35 | return ['pattern' => '^('.$htmlPattern.')$']; 36 | } 37 | 38 | return []; 39 | } 40 | 41 | /** 42 | * {@inheritdoc} 43 | */ 44 | public function supports(Constraint $constraint, ApiProperty $propertyMetadata): bool 45 | { 46 | return $constraint instanceof Regex; 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /Bundle/Resources/config/json_streamer/json.php: -------------------------------------------------------------------------------- 1 | 7 | * 8 | * For the full copyright and license information, please view the LICENSE 9 | * file that was distributed with this source code. 10 | */ 11 | 12 | declare(strict_types=1); 13 | 14 | namespace Symfony\Component\DependencyInjection\Loader\Configurator; 15 | 16 | return function (ContainerConfigurator $container) { 17 | $services = $container->services(); 18 | 19 | $services->set('api_platform.state_processor.json_streamer', 'ApiPlatform\Serializer\State\JsonStreamerProcessor') 20 | ->decorate('api_platform.state_processor.main', null, 190) 21 | ->args([ 22 | service('api_platform.state_processor.json_streamer.inner'), 23 | service('json_streamer.stream_writer'), 24 | service('api_platform.iri_converter'), 25 | service('api_platform.resource_class_resolver'), 26 | service('api_platform.metadata.operation.metadata_factory'), 27 | ]); 28 | 29 | $services->set('api_platform.state_provider.json_streamer', 'ApiPlatform\Serializer\State\JsonStreamerProvider') 30 | ->decorate('api_platform.state_provider.main', null, 310) 31 | ->args([ 32 | service('api_platform.state_provider.json_streamer.inner'), 33 | service('json_streamer.stream_reader'), 34 | ]); 35 | }; 36 | -------------------------------------------------------------------------------- /Bundle/Resources/config/http_cache_purger.php: -------------------------------------------------------------------------------- 1 | 7 | * 8 | * For the full copyright and license information, please view the LICENSE 9 | * file that was distributed with this source code. 10 | */ 11 | 12 | declare(strict_types=1); 13 | 14 | namespace Symfony\Component\DependencyInjection\Loader\Configurator; 15 | 16 | return function (ContainerConfigurator $container) { 17 | $services = $container->services(); 18 | 19 | $services->alias('api_platform.http_cache.purger.varnish', 'api_platform.http_cache.purger.varnish.ban'); 20 | 21 | $services->set('api_platform.http_cache.purger.varnish.ban', 'ApiPlatform\HttpCache\VarnishPurger') 22 | ->args([tagged_iterator('api_platform.http_cache.http_client')]); 23 | 24 | $services->set('api_platform.http_cache.purger.varnish.xkey', 'ApiPlatform\HttpCache\VarnishXKeyPurger') 25 | ->args([ 26 | tagged_iterator('api_platform.http_cache.http_client'), 27 | '%api_platform.http_cache.invalidation.max_header_length%', 28 | '%api_platform.http_cache.invalidation.xkey.glue%', 29 | ]); 30 | 31 | $services->set('api_platform.http_cache.purger.souin', 'ApiPlatform\HttpCache\SouinPurger') 32 | ->args([ 33 | tagged_iterator('api_platform.http_cache.http_client'), 34 | '%api_platform.http_cache.invalidation.max_header_length%', 35 | ]); 36 | }; 37 | -------------------------------------------------------------------------------- /State/MercureLinkProcessor.php: -------------------------------------------------------------------------------- 1 | 7 | * 8 | * For the full copyright and license information, please view the LICENSE 9 | * file that was distributed with this source code. 10 | */ 11 | 12 | declare(strict_types=1); 13 | 14 | namespace ApiPlatform\Symfony\State; 15 | 16 | use ApiPlatform\Metadata\Operation; 17 | use ApiPlatform\State\ProcessorInterface; 18 | use Symfony\Component\Mercure\Discovery; 19 | 20 | /** 21 | * @template T1 22 | * @template T2 23 | * 24 | * @implements ProcessorInterface 25 | */ 26 | final class MercureLinkProcessor implements ProcessorInterface 27 | { 28 | /** 29 | * @param ProcessorInterface $decorated 30 | */ 31 | public function __construct(private readonly ProcessorInterface $decorated, private readonly Discovery $discovery) 32 | { 33 | } 34 | 35 | public function process(mixed $data, Operation $operation, array $uriVariables = [], array $context = []): mixed 36 | { 37 | if (!($request = $context['request'] ?? null) || !$mercure = $operation->getMercure()) { 38 | return $this->decorated->process($data, $operation, $uriVariables, $context); 39 | } 40 | 41 | $hub = \is_array($mercure) ? ($mercure['hub'] ?? null) : null; 42 | $this->discovery->addLink($request, $hub); 43 | 44 | return $this->decorated->process($data, $operation, $uriVariables, $context); 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /Bundle/DependencyInjection/Compiler/AttributeResourcePass.php: -------------------------------------------------------------------------------- 1 | 7 | * 8 | * For the full copyright and license information, please view the LICENSE 9 | * file that was distributed with this source code. 10 | */ 11 | 12 | declare(strict_types=1); 13 | 14 | namespace ApiPlatform\Symfony\Bundle\DependencyInjection\Compiler; 15 | 16 | use ApiPlatform\Metadata\ApiResource; 17 | use Symfony\Component\DependencyInjection\Compiler\CompilerPassInterface; 18 | use Symfony\Component\DependencyInjection\ContainerBuilder; 19 | 20 | /** 21 | * Registers resource classes from {@see ApiResource} attribute. 22 | * 23 | * @internal 24 | * 25 | * @author Jérôme Tamarelle 26 | */ 27 | final class AttributeResourcePass implements CompilerPassInterface 28 | { 29 | /** 30 | * {@inheritdoc} 31 | */ 32 | public function process(ContainerBuilder $container): void 33 | { 34 | $classes = $container->getParameter('api_platform.class_name_resources'); 35 | 36 | // findTaggedServiceIds cannot be used, as the services are excluded 37 | foreach ($container->getDefinitions() as $definition) { 38 | if ($definition->hasTag('api_platform.resource')) { 39 | $classes[] = $definition->getClass(); 40 | } 41 | } 42 | 43 | $container->setParameter('api_platform.class_name_resources', array_unique($classes)); 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /Bundle/DependencyInjection/Compiler/GraphQlResolverPass.php: -------------------------------------------------------------------------------- 1 | 7 | * 8 | * For the full copyright and license information, please view the LICENSE 9 | * file that was distributed with this source code. 10 | */ 11 | 12 | declare(strict_types=1); 13 | 14 | namespace ApiPlatform\Symfony\Bundle\DependencyInjection\Compiler; 15 | 16 | use Symfony\Component\DependencyInjection\Compiler\CompilerPassInterface; 17 | use Symfony\Component\DependencyInjection\ContainerBuilder; 18 | use Symfony\Component\DependencyInjection\Reference; 19 | 20 | /** 21 | * Injects GraphQL resolvers. 22 | * 23 | * @internal 24 | * 25 | * @author Lukas Lücke 26 | */ 27 | final class GraphQlResolverPass implements CompilerPassInterface 28 | { 29 | /** 30 | * {@inheritdoc} 31 | */ 32 | public function process(ContainerBuilder $container): void 33 | { 34 | if (!$container->getParameter('api_platform.graphql.enabled')) { 35 | return; 36 | } 37 | 38 | $resolvers = []; 39 | foreach ($container->findTaggedServiceIds('api_platform.graphql.resolver', true) as $serviceId => $tags) { 40 | foreach ($tags as $tag) { 41 | $resolvers[$tag['id'] ?? $serviceId] = new Reference($serviceId); 42 | } 43 | } 44 | 45 | $container->getDefinition('api_platform.graphql.resolver_locator')->addArgument($resolvers); 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /Bundle/DependencyInjection/Compiler/TestClientPass.php: -------------------------------------------------------------------------------- 1 | 7 | * 8 | * For the full copyright and license information, please view the LICENSE 9 | * file that was distributed with this source code. 10 | */ 11 | 12 | declare(strict_types=1); 13 | 14 | namespace ApiPlatform\Symfony\Bundle\DependencyInjection\Compiler; 15 | 16 | use ApiPlatform\Symfony\Bundle\Test\Client; 17 | use Symfony\Component\BrowserKit\AbstractBrowser; 18 | use Symfony\Component\DependencyInjection\Compiler\CompilerPassInterface; 19 | use Symfony\Component\DependencyInjection\ContainerBuilder; 20 | use Symfony\Component\DependencyInjection\Definition; 21 | use Symfony\Component\DependencyInjection\Reference; 22 | use Symfony\Component\HttpClient\HttpClientTrait; 23 | 24 | final class TestClientPass implements CompilerPassInterface 25 | { 26 | public function process(ContainerBuilder $container): void 27 | { 28 | if ( 29 | !class_exists(AbstractBrowser::class) 30 | || !trait_exists(HttpClientTrait::class) 31 | || !$container->hasParameter('test.client.parameters') 32 | ) { 33 | return; 34 | } 35 | 36 | $container->setDefinition( 37 | 'test.api_platform.client', 38 | (new Definition(Client::class, [new Reference('test.client')])) 39 | ->setShared(false) 40 | ->setPublic(true) 41 | ); 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /Bundle/Resources/config/security.php: -------------------------------------------------------------------------------- 1 | 7 | * 8 | * For the full copyright and license information, please view the LICENSE 9 | * file that was distributed with this source code. 10 | */ 11 | 12 | declare(strict_types=1); 13 | 14 | namespace Symfony\Component\DependencyInjection\Loader\Configurator; 15 | 16 | return function (ContainerConfigurator $container) { 17 | $services = $container->services(); 18 | 19 | $services->alias('api_platform.security.expression_language', 'security.expression_language'); 20 | 21 | $services->set('api_platform.security.resource_access_checker', 'ApiPlatform\Symfony\Security\ResourceAccessChecker') 22 | ->args([ 23 | service('api_platform.security.expression_language')->nullOnInvalid(), 24 | service('security.authentication.trust_resolver')->nullOnInvalid(), 25 | service('security.role_hierarchy')->nullOnInvalid(), 26 | service('security.token_storage')->nullOnInvalid(), 27 | service('security.authorization_checker')->nullOnInvalid(), 28 | ]); 29 | 30 | $services->alias('ApiPlatform\Metadata\ResourceAccessCheckerInterface', 'api_platform.security.resource_access_checker'); 31 | 32 | $services->set('api_platform.security.expression_language_provider', 'ApiPlatform\Symfony\Security\Core\Authorization\ExpressionLanguageProvider') 33 | ->tag('security.expression_language_provider'); 34 | }; 35 | -------------------------------------------------------------------------------- /Validator/Metadata/Property/Restriction/PropertySchemaCountRestriction.php: -------------------------------------------------------------------------------- 1 | 7 | * 8 | * For the full copyright and license information, please view the LICENSE 9 | * file that was distributed with this source code. 10 | */ 11 | 12 | declare(strict_types=1); 13 | 14 | namespace ApiPlatform\Symfony\Validator\Metadata\Property\Restriction; 15 | 16 | use ApiPlatform\Metadata\ApiProperty; 17 | use Symfony\Component\Validator\Constraint; 18 | use Symfony\Component\Validator\Constraints\Count; 19 | 20 | /** 21 | * @author Tomas Norkūnas 22 | */ 23 | class PropertySchemaCountRestriction implements PropertySchemaRestrictionMetadataInterface 24 | { 25 | /** 26 | * {@inheritdoc} 27 | * 28 | * @param Count $constraint 29 | */ 30 | public function create(Constraint $constraint, ApiProperty $propertyMetadata): array 31 | { 32 | $restriction = []; 33 | 34 | if (null !== $constraint->min) { 35 | $restriction['minItems'] = $constraint->min; 36 | } 37 | 38 | if (null !== $constraint->max) { 39 | $restriction['maxItems'] = $constraint->max; 40 | } 41 | 42 | return $restriction; 43 | } 44 | 45 | /** 46 | * {@inheritdoc} 47 | */ 48 | public function supports(Constraint $constraint, ApiProperty $propertyMetadata): bool 49 | { 50 | return $constraint instanceof Count; 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /EventListener/JsonApi/TransformPaginationParametersListener.php: -------------------------------------------------------------------------------- 1 | 7 | * 8 | * For the full copyright and license information, please view the LICENSE 9 | * file that was distributed with this source code. 10 | */ 11 | 12 | declare(strict_types=1); 13 | 14 | namespace ApiPlatform\Symfony\EventListener\JsonApi; 15 | 16 | use Symfony\Component\HttpKernel\Event\RequestEvent; 17 | 18 | /** 19 | * @see http://jsonapi.org/format/#fetching-pagination 20 | * @see https://api-platform.com/docs/core/pagination 21 | * 22 | * @author Héctor Hurtarte 23 | * @author Baptiste Meyer 24 | */ 25 | final class TransformPaginationParametersListener 26 | { 27 | public function onKernelRequest(RequestEvent $event): void 28 | { 29 | $request = $event->getRequest(); 30 | if (($operation = $request->attributes->get('_api_operation')) && 'api_platform.symfony.main_controller' === $operation->getController()) { 31 | return; 32 | } 33 | 34 | $pageParameter = $request->query->all()['page'] ?? null; 35 | 36 | if ( 37 | !\is_array($pageParameter) 38 | || 'jsonapi' !== $request->getRequestFormat() 39 | ) { 40 | return; 41 | } 42 | 43 | $filters = $request->attributes->get('_api_filters', []); 44 | $request->attributes->set('_api_filters', array_merge($pageParameter, $filters)); 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /Bundle/CacheWarmer/CachePoolClearerCacheWarmer.php: -------------------------------------------------------------------------------- 1 | 7 | * 8 | * For the full copyright and license information, please view the LICENSE 9 | * file that was distributed with this source code. 10 | */ 11 | 12 | declare(strict_types=1); 13 | 14 | namespace ApiPlatform\Symfony\Bundle\CacheWarmer; 15 | 16 | use Symfony\Component\HttpKernel\CacheClearer\Psr6CacheClearer; 17 | use Symfony\Component\HttpKernel\CacheWarmer\CacheWarmerInterface; 18 | 19 | /** 20 | * Clears the cache pools when warming up the cache. 21 | * 22 | * Do not use in production! 23 | * 24 | * @internal 25 | */ 26 | final class CachePoolClearerCacheWarmer implements CacheWarmerInterface 27 | { 28 | public function __construct(private readonly Psr6CacheClearer $poolClearer, private readonly array $pools = []) 29 | { 30 | } 31 | 32 | /** 33 | * {@inheritdoc} 34 | * 35 | * @return string[] 36 | */ 37 | public function warmUp(string $cacheDir, ?string $buildDir = null): array 38 | { 39 | foreach ($this->pools as $pool) { 40 | if ($this->poolClearer->hasPool($pool)) { 41 | $this->poolClearer->clearPool($pool); 42 | } 43 | } 44 | 45 | return []; 46 | } 47 | 48 | /** 49 | * {@inheritdoc} 50 | */ 51 | public function isOptional(): bool 52 | { 53 | // optional cache warmers are not run when handling the request 54 | return false; 55 | } 56 | } 57 | -------------------------------------------------------------------------------- /Bundle/DependencyInjection/Compiler/FilterPass.php: -------------------------------------------------------------------------------- 1 | 7 | * 8 | * For the full copyright and license information, please view the LICENSE 9 | * file that was distributed with this source code. 10 | */ 11 | 12 | declare(strict_types=1); 13 | 14 | namespace ApiPlatform\Symfony\Bundle\DependencyInjection\Compiler; 15 | 16 | use ApiPlatform\Metadata\Exception\RuntimeException; 17 | use Symfony\Component\DependencyInjection\Compiler\CompilerPassInterface; 18 | use Symfony\Component\DependencyInjection\ContainerBuilder; 19 | use Symfony\Component\DependencyInjection\Reference; 20 | 21 | /** 22 | * Injects filters. 23 | * 24 | * @internal 25 | * 26 | * @author Kévin Dunglas 27 | */ 28 | final class FilterPass implements CompilerPassInterface 29 | { 30 | /** 31 | * {@inheritdoc} 32 | * 33 | * @throws RuntimeException 34 | */ 35 | public function process(ContainerBuilder $container): void 36 | { 37 | $filters = []; 38 | foreach ($container->findTaggedServiceIds('api_platform.filter', true) as $serviceId => $tags) { 39 | foreach ($tags as $tag) { 40 | if (!isset($tag['id'])) { 41 | $tag['id'] = $serviceId; 42 | } 43 | 44 | $filters[$tag['id']] = new Reference($serviceId); 45 | } 46 | } 47 | 48 | $container->getDefinition('api_platform.filter_locator')->addArgument($filters); 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /Bundle/Resources/config/symfony/symfony.php: -------------------------------------------------------------------------------- 1 | 7 | * 8 | * For the full copyright and license information, please view the LICENSE 9 | * file that was distributed with this source code. 10 | */ 11 | 12 | declare(strict_types=1); 13 | 14 | namespace Symfony\Component\DependencyInjection\Loader\Configurator; 15 | 16 | return function (ContainerConfigurator $container) { 17 | $services = $container->services(); 18 | 19 | $services->set('api_platform.listener.exception', 'ApiPlatform\Symfony\EventListener\ExceptionListener') 20 | ->args([ 21 | service('api_platform.error_listener')->nullOnInvalid(), 22 | '%api_platform.handle_symfony_errors%', 23 | ]) 24 | ->tag('kernel.event_listener', ['event' => 'kernel.exception', 'method' => 'onKernelException', 'priority' => -96]) 25 | ->tag('monolog.logger', ['channel' => 'request']); 26 | 27 | $services->set('api_platform.cache_warmer.cache_pool_clearer', 'ApiPlatform\Symfony\Bundle\CacheWarmer\CachePoolClearerCacheWarmer') 28 | ->args([ 29 | service('cache.system_clearer'), 30 | ['api_platform.cache.metadata.property', 'api_platform.cache.metadata.resource', 'api_platform.cache.metadata.resource_collection', 'api_platform.cache.route_name_resolver', 'api_platform.cache.identifiers_extractor', 'api_platform.elasticsearch.cache.metadata.document'], 31 | ]) 32 | ->tag('kernel.cache_warmer', ['priority' => 64]); 33 | }; 34 | -------------------------------------------------------------------------------- /Bundle/Resources/config/validator/events.php: -------------------------------------------------------------------------------- 1 | 7 | * 8 | * For the full copyright and license information, please view the LICENSE 9 | * file that was distributed with this source code. 10 | */ 11 | 12 | declare(strict_types=1); 13 | 14 | namespace Symfony\Component\DependencyInjection\Loader\Configurator; 15 | 16 | return function (ContainerConfigurator $container) { 17 | $services = $container->services(); 18 | 19 | $services->set('api_platform.state_provider.validate', 'ApiPlatform\Symfony\Validator\State\ValidateProvider') 20 | ->args([ 21 | null, 22 | service('api_platform.validator'), 23 | ]); 24 | 25 | $services->set('api_platform.listener.view.validate', 'ApiPlatform\Symfony\EventListener\ValidateListener') 26 | ->args([ 27 | service('api_platform.state_provider.validate'), 28 | service('api_platform.metadata.resource.metadata_collection_factory'), 29 | ]) 30 | ->tag('kernel.event_listener', ['event' => 'kernel.view', 'method' => 'onKernelView', 'priority' => 64]); 31 | 32 | $services->set('api_platform.state_provider.parameter_validator', 'ApiPlatform\Symfony\Validator\State\ParameterValidatorProvider') 33 | ->public() 34 | ->decorate('api_platform.state_provider.read', null, 110) 35 | ->args([ 36 | service('validator'), 37 | service('api_platform.state_provider.parameter_validator.inner'), 38 | ]); 39 | }; 40 | -------------------------------------------------------------------------------- /Messenger/DispatchTrait.php: -------------------------------------------------------------------------------- 1 | 7 | * 8 | * For the full copyright and license information, please view the LICENSE 9 | * file that was distributed with this source code. 10 | */ 11 | 12 | declare(strict_types=1); 13 | 14 | namespace ApiPlatform\Symfony\Messenger; 15 | 16 | use Symfony\Component\Messenger\Envelope; 17 | use Symfony\Component\Messenger\Exception\HandlerFailedException; 18 | use Symfony\Component\Messenger\MessageBusInterface; 19 | 20 | /** 21 | * @internal 22 | */ 23 | trait DispatchTrait 24 | { 25 | private ?MessageBusInterface $messageBus; 26 | 27 | /** 28 | * @param object|Envelope $message 29 | */ 30 | private function dispatch(object $message): Envelope 31 | { 32 | if (!$this->messageBus instanceof MessageBusInterface) { 33 | throw new \InvalidArgumentException('The message bus is not set.'); 34 | } 35 | 36 | if (!class_exists(HandlerFailedException::class)) { 37 | return $this->messageBus->dispatch($message); 38 | } 39 | 40 | try { 41 | return $this->messageBus->dispatch($message); 42 | } catch (HandlerFailedException $e) { 43 | // unwrap the exception thrown in handler for Symfony Messenger >= 4.3 44 | while ($e instanceof HandlerFailedException) { 45 | /** @var \Throwable $e */ 46 | $e = $e->getPrevious(); 47 | } 48 | 49 | throw $e; 50 | } 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /Validator/State/ErrorProvider.php: -------------------------------------------------------------------------------- 1 | 7 | * 8 | * For the full copyright and license information, please view the LICENSE 9 | * file that was distributed with this source code. 10 | */ 11 | 12 | declare(strict_types=1); 13 | 14 | namespace ApiPlatform\Symfony\Validator\State; 15 | 16 | use ApiPlatform\Metadata\HttpOperation; 17 | use ApiPlatform\Metadata\Operation; 18 | use ApiPlatform\State\ProviderInterface; 19 | use ApiPlatform\Validator\Exception\ConstraintViolationListAwareExceptionInterface; 20 | use Symfony\Component\Validator\ConstraintViolationListInterface; 21 | 22 | /** 23 | * @internal 24 | */ 25 | final class ErrorProvider implements ProviderInterface 26 | { 27 | public function __construct() 28 | { 29 | } 30 | 31 | public function provide(Operation $operation, array $uriVariables = [], array $context = []): ConstraintViolationListInterface|\Throwable 32 | { 33 | if (!($request = $context['request'] ?? null) || !$operation instanceof HttpOperation) { 34 | throw new \RuntimeException('Not an HTTP request'); 35 | } 36 | 37 | $exception = $request->attributes->get('exception'); 38 | $exception->setStatus($operation->getStatus()); 39 | 40 | if ('jsonapi' === $request->getRequestFormat() && $exception instanceof ConstraintViolationListAwareExceptionInterface) { 41 | return $exception->getConstraintViolationList(); 42 | } 43 | 44 | return $exception; 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /Bundle/Resources/config/doctrine_orm_mercure_publisher.php: -------------------------------------------------------------------------------- 1 | 7 | * 8 | * For the full copyright and license information, please view the LICENSE 9 | * file that was distributed with this source code. 10 | */ 11 | 12 | declare(strict_types=1); 13 | 14 | namespace Symfony\Component\DependencyInjection\Loader\Configurator; 15 | 16 | return function (ContainerConfigurator $container) { 17 | $services = $container->services(); 18 | 19 | $services->set('api_platform.doctrine.orm.listener.mercure.publish', 'ApiPlatform\Symfony\Doctrine\EventListener\PublishMercureUpdatesListener') 20 | ->args([ 21 | service('api_platform.resource_class_resolver'), 22 | service('api_platform.iri_converter'), 23 | service('api_platform.metadata.resource.metadata_collection_factory'), 24 | service('api_platform.serializer'), 25 | '%api_platform.formats%', 26 | service('messenger.default_bus')->ignoreOnInvalid(), 27 | service('Symfony\Component\Mercure\HubRegistry'), 28 | service('api_platform.graphql.subscription.subscription_manager')->ignoreOnInvalid(), 29 | service('api_platform.graphql.subscription.mercure_iri_generator')->ignoreOnInvalid(), 30 | null, 31 | '%api_platform.mercure.include_type%', 32 | ]) 33 | ->tag('doctrine.event_listener', ['event' => 'onFlush']) 34 | ->tag('doctrine.event_listener', ['event' => 'postFlush']); 35 | }; 36 | -------------------------------------------------------------------------------- /EventListener/JsonApi/TransformFilteringParametersListener.php: -------------------------------------------------------------------------------- 1 | 7 | * 8 | * For the full copyright and license information, please view the LICENSE 9 | * file that was distributed with this source code. 10 | */ 11 | 12 | declare(strict_types=1); 13 | 14 | namespace ApiPlatform\Symfony\EventListener\JsonApi; 15 | 16 | use Symfony\Component\HttpKernel\Event\RequestEvent; 17 | 18 | /** 19 | * @see http://jsonapi.org/format/#fetching-filtering 20 | * @see http://jsonapi.org/recommendations/#filtering 21 | * 22 | * @author Héctor Hurtarte 23 | * @author Baptiste Meyer 24 | */ 25 | final class TransformFilteringParametersListener 26 | { 27 | public function onKernelRequest(RequestEvent $event): void 28 | { 29 | $request = $event->getRequest(); 30 | if (($operation = $request->attributes->get('_api_operation')) && 'api_platform.symfony.main_controller' === $operation->getController()) { 31 | return; 32 | } 33 | 34 | $filterParameter = $request->query->all()['filter'] ?? null; 35 | 36 | if ( 37 | !$filterParameter 38 | || !\is_array($filterParameter) 39 | || 'jsonapi' !== $request->getRequestFormat() 40 | ) { 41 | return; 42 | } 43 | 44 | $filters = $request->attributes->get('_api_filters', []); 45 | $request->attributes->set('_api_filters', array_merge($filterParameter, $filters)); 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /Bundle/DependencyInjection/Compiler/GraphQlTypePass.php: -------------------------------------------------------------------------------- 1 | 7 | * 8 | * For the full copyright and license information, please view the LICENSE 9 | * file that was distributed with this source code. 10 | */ 11 | 12 | declare(strict_types=1); 13 | 14 | namespace ApiPlatform\Symfony\Bundle\DependencyInjection\Compiler; 15 | 16 | use Symfony\Component\DependencyInjection\Compiler\CompilerPassInterface; 17 | use Symfony\Component\DependencyInjection\ContainerBuilder; 18 | use Symfony\Component\DependencyInjection\Reference; 19 | 20 | /** 21 | * Injects GraphQL types. 22 | * 23 | * @internal 24 | * 25 | * @author Alan Poulain 26 | */ 27 | final class GraphQlTypePass implements CompilerPassInterface 28 | { 29 | /** 30 | * {@inheritdoc} 31 | */ 32 | public function process(ContainerBuilder $container): void 33 | { 34 | if (!$container->getParameter('api_platform.graphql.enabled')) { 35 | return; 36 | } 37 | 38 | $types = []; 39 | foreach ($container->findTaggedServiceIds('api_platform.graphql.type', true) as $serviceId => $tags) { 40 | foreach ($tags as $tag) { 41 | $types[$tag['id'] ?? $serviceId] = new Reference($serviceId); 42 | } 43 | } 44 | 45 | $container->getDefinition('api_platform.graphql.type_locator')->addArgument($types); 46 | $container->getDefinition('api_platform.graphql.types_factory')->addArgument(array_keys($types)); 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /Bundle/Resources/config/metadata/operation.php: -------------------------------------------------------------------------------- 1 | 7 | * 8 | * For the full copyright and license information, please view the LICENSE 9 | * file that was distributed with this source code. 10 | */ 11 | 12 | declare(strict_types=1); 13 | 14 | namespace Symfony\Component\DependencyInjection\Loader\Configurator; 15 | 16 | return function (ContainerConfigurator $container) { 17 | $services = $container->services(); 18 | 19 | $services->set('api_platform.metadata.operation.metadata_factory', 'ApiPlatform\Metadata\Operation\Factory\OperationMetadataFactory') 20 | ->args([ 21 | service('api_platform.metadata.resource.name_collection_factory'), 22 | service('api_platform.metadata.resource.metadata_collection_factory'), 23 | ]); 24 | 25 | $services->alias('ApiPlatform\Metadata\Operation\Factory\OperationMetadataFactoryInterface', 'api_platform.metadata.operation.metadata_factory'); 26 | 27 | $services->set('api_platform.metadata.operation.metadata_factory.cached', 'ApiPlatform\Metadata\Operation\Factory\CacheOperationMetadataFactory') 28 | ->decorate('api_platform.metadata.operation.metadata_factory', null, -10) 29 | ->args([ 30 | service('api_platform.cache.metadata.operation'), 31 | service('api_platform.metadata.operation.metadata_factory.cached.inner'), 32 | ]); 33 | 34 | $services->set('api_platform.cache.metadata.operation') 35 | ->parent('cache.system') 36 | ->tag('cache.pool'); 37 | }; 38 | -------------------------------------------------------------------------------- /Bundle/DependencyInjection/Compiler/DataProviderPass.php: -------------------------------------------------------------------------------- 1 | 7 | * 8 | * For the full copyright and license information, please view the LICENSE 9 | * file that was distributed with this source code. 10 | */ 11 | 12 | declare(strict_types=1); 13 | 14 | namespace ApiPlatform\Symfony\Bundle\DependencyInjection\Compiler; 15 | 16 | use ApiPlatform\State\SerializerAwareProviderInterface; 17 | use Symfony\Component\DependencyInjection\Compiler\CompilerPassInterface; 18 | use Symfony\Component\DependencyInjection\ContainerBuilder; 19 | use Symfony\Component\DependencyInjection\Reference; 20 | 21 | /** 22 | * Registers data providers. 23 | * 24 | * @internal since 4.2 25 | * 26 | * @author Kévin Dunglas 27 | * @author Vincent Chalamon 28 | * 29 | * TODO: remove in 5.x 30 | */ 31 | final class DataProviderPass implements CompilerPassInterface 32 | { 33 | /** 34 | * {@inheritdoc} 35 | */ 36 | public function process(ContainerBuilder $container): void 37 | { 38 | $services = $container->findTaggedServiceIds('api_platform.state_provider', true); 39 | 40 | foreach ($services as $id => $tags) { 41 | $definition = $container->getDefinition((string) $id); 42 | if (is_a($definition->getClass(), SerializerAwareProviderInterface::class, true)) { 43 | $definition->addMethodCall('setSerializerLocator', [new Reference('api_platform.serializer_locator')]); 44 | } 45 | } 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /Bundle/Resources/config/doctrine_odm_mercure_publisher.php: -------------------------------------------------------------------------------- 1 | 7 | * 8 | * For the full copyright and license information, please view the LICENSE 9 | * file that was distributed with this source code. 10 | */ 11 | 12 | declare(strict_types=1); 13 | 14 | namespace Symfony\Component\DependencyInjection\Loader\Configurator; 15 | 16 | return function (ContainerConfigurator $container) { 17 | $services = $container->services(); 18 | 19 | $services->set('api_platform.doctrine_mongodb.odm.listener.mercure.publish', 'ApiPlatform\Symfony\Doctrine\EventListener\PublishMercureUpdatesListener') 20 | ->args([ 21 | service('api_platform.resource_class_resolver'), 22 | service('api_platform.symfony.iri_converter'), 23 | service('api_platform.metadata.resource.metadata_collection_factory'), 24 | service('api_platform.serializer'), 25 | '%api_platform.formats%', 26 | service('messenger.default_bus')->ignoreOnInvalid(), 27 | service('Symfony\Component\Mercure\HubRegistry'), 28 | service('api_platform.graphql.subscription.subscription_manager')->ignoreOnInvalid(), 29 | service('api_platform.graphql.subscription.mercure_iri_generator')->ignoreOnInvalid(), 30 | null, 31 | '%api_platform.mercure.include_type%', 32 | ]) 33 | ->tag('doctrine_mongodb.odm.event_listener', ['event' => 'onFlush']) 34 | ->tag('doctrine_mongodb.odm.event_listener', ['event' => 'postFlush']); 35 | }; 36 | -------------------------------------------------------------------------------- /Bundle/Resources/config/validator/validator.php: -------------------------------------------------------------------------------- 1 | 7 | * 8 | * For the full copyright and license information, please view the LICENSE 9 | * file that was distributed with this source code. 10 | */ 11 | 12 | declare(strict_types=1); 13 | 14 | namespace Symfony\Component\DependencyInjection\Loader\Configurator; 15 | 16 | return function (ContainerConfigurator $container) { 17 | $services = $container->services(); 18 | 19 | $services->set('api_platform.validator', 'ApiPlatform\Symfony\Validator\Validator') 20 | ->args([ 21 | service('validator'), 22 | tagged_locator('api_platform.validation_groups_generator'), 23 | ]); 24 | 25 | $services->alias('ApiPlatform\Validator\ValidatorInterface', 'api_platform.validator'); 26 | 27 | $services->set('api_platform.validator.state.error_provider', 'ApiPlatform\Symfony\Validator\State\ErrorProvider') 28 | ->tag('api_platform.state_provider', ['key' => 'api_platform.validator.state.error_provider']); 29 | 30 | $services->set('api_platform.validator.metadata.resource.metadata_collection_factory.parameter', 'ApiPlatform\Validator\Metadata\Resource\Factory\ParameterValidationResourceMetadataCollectionFactory') 31 | ->decorate('api_platform.metadata.resource.metadata_collection_factory', null, 1000) 32 | ->args([ 33 | service('api_platform.validator.metadata.resource.metadata_collection_factory.parameter.inner'), 34 | service('api_platform.filter_locator'), 35 | ]); 36 | }; 37 | -------------------------------------------------------------------------------- /Bundle/DependencyInjection/Compiler/TestMercureHubPass.php: -------------------------------------------------------------------------------- 1 | 7 | * 8 | * For the full copyright and license information, please view the LICENSE 9 | * file that was distributed with this source code. 10 | */ 11 | 12 | declare(strict_types=1); 13 | 14 | namespace ApiPlatform\Symfony\Bundle\DependencyInjection\Compiler; 15 | 16 | use Symfony\Component\DependencyInjection\Compiler\CompilerPassInterface; 17 | use Symfony\Component\DependencyInjection\ContainerBuilder; 18 | use Symfony\Component\DependencyInjection\Reference; 19 | use Symfony\Component\Mercure\Debug\TraceableHub; 20 | 21 | /** 22 | * Decorate each Mercure Hub with TraceableHub for test purpose. 23 | * Prevents enabling debug mode on tests for Mercure assertions. 24 | */ 25 | final class TestMercureHubPass implements CompilerPassInterface 26 | { 27 | public function process(ContainerBuilder $container): void 28 | { 29 | // Only enable this if class exists and "framework.test" is enabled 30 | if (!class_exists(TraceableHub::class) || !$container->has('test.service_container')) { 31 | return; 32 | } 33 | 34 | foreach ($container->findTaggedServiceIds('mercure.hub', true) as $serviceId => $attributes) { 35 | $container->register("test.api_platform.mercure.hub.$serviceId", TraceableHub::class) 36 | ->setDecoratedService($serviceId) 37 | ->addArgument(new Reference("test.api_platform.mercure.hub.$serviceId.inner")) 38 | ->addArgument(new Reference('debug.stopwatch')); 39 | } 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /Validator/State/ValidateProvider.php: -------------------------------------------------------------------------------- 1 | 7 | * 8 | * For the full copyright and license information, please view the LICENSE 9 | * file that was distributed with this source code. 10 | */ 11 | 12 | declare(strict_types=1); 13 | 14 | namespace ApiPlatform\Symfony\Validator\State; 15 | 16 | use ApiPlatform\Metadata\Operation; 17 | use ApiPlatform\State\ProviderInterface; 18 | use ApiPlatform\Validator\ValidatorInterface; 19 | use Symfony\Component\HttpFoundation\Response; 20 | 21 | /** 22 | * Validates data on an HTTP or GraphQl operation. 23 | */ 24 | final class ValidateProvider implements ProviderInterface 25 | { 26 | public function __construct(private readonly ?ProviderInterface $decorated, private readonly ValidatorInterface $validator, private readonly string $canValidateAccessor = 'canValidate') 27 | { 28 | } 29 | 30 | public function provide(Operation $operation, array $uriVariables = [], array $context = []): object|array|null 31 | { 32 | $body = $this->decorated?->provide($operation, $uriVariables, $context) ?? ($context['request'] ?? null)?->attributes->get('data'); 33 | 34 | if ($body instanceof Response || !$body) { 35 | return $body; 36 | } 37 | 38 | if (method_exists($operation, $this->canValidateAccessor) && !($operation->{$this->canValidateAccessor}() ?? ('canValidate' === $this->canValidateAccessor))) { 39 | return $body; 40 | } 41 | 42 | $this->validator->validate($body, $operation->getValidationContext() ?? []); 43 | 44 | return $body; 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /Bundle/SwaggerUi/SwaggerUiContext.php: -------------------------------------------------------------------------------- 1 | 7 | * 8 | * For the full copyright and license information, please view the LICENSE 9 | * file that was distributed with this source code. 10 | */ 11 | 12 | declare(strict_types=1); 13 | 14 | namespace ApiPlatform\Symfony\Bundle\SwaggerUi; 15 | 16 | final class SwaggerUiContext 17 | { 18 | /** 19 | * @param string|null $assetPackage 20 | */ 21 | public function __construct(private readonly bool $swaggerUiEnabled = false, private readonly bool $showWebby = true, private readonly bool $reDocEnabled = false, private readonly bool $graphQlEnabled = false, private readonly bool $graphiQlEnabled = false, private $assetPackage = null, private readonly array $extraConfiguration = []) 22 | { 23 | } 24 | 25 | public function isSwaggerUiEnabled(): bool 26 | { 27 | return $this->swaggerUiEnabled; 28 | } 29 | 30 | public function isWebbyShown(): bool 31 | { 32 | return $this->showWebby; 33 | } 34 | 35 | public function isRedocEnabled(): bool 36 | { 37 | return $this->reDocEnabled; 38 | } 39 | 40 | public function isGraphQlEnabled(): bool 41 | { 42 | return $this->graphQlEnabled; 43 | } 44 | 45 | public function isGraphiQlEnabled(): bool 46 | { 47 | return $this->graphiQlEnabled; 48 | } 49 | 50 | public function getAssetPackage(): ?string 51 | { 52 | return $this->assetPackage; 53 | } 54 | 55 | public function getExtraConfiguration(): array 56 | { 57 | return $this->extraConfiguration; 58 | } 59 | } 60 | -------------------------------------------------------------------------------- /Bundle/Resources/config/json_streamer/hydra.php: -------------------------------------------------------------------------------- 1 | 7 | * 8 | * For the full copyright and license information, please view the LICENSE 9 | * file that was distributed with this source code. 10 | */ 11 | 12 | declare(strict_types=1); 13 | 14 | namespace Symfony\Component\DependencyInjection\Loader\Configurator; 15 | 16 | return function (ContainerConfigurator $container) { 17 | $services = $container->services(); 18 | 19 | $services->set('api_platform.jsonld.state_processor.json_streamer', 'ApiPlatform\Hydra\State\JsonStreamerProcessor') 20 | ->decorate('api_platform.state_processor.main', null, 190) 21 | ->args([ 22 | service('api_platform.jsonld.state_processor.json_streamer.inner'), 23 | service('api_platform.jsonld.json_streamer.stream_writer'), 24 | service('api_platform.iri_converter'), 25 | service('api_platform.resource_class_resolver'), 26 | service('api_platform.metadata.operation.metadata_factory'), 27 | '%api_platform.collection.pagination.page_parameter_name%', 28 | '%api_platform.collection.pagination.enabled_parameter_name%', 29 | '%api_platform.url_generation_strategy%', 30 | ]); 31 | 32 | $services->set('api_platform.jsonld.state_provider.json_streamer', 'ApiPlatform\Hydra\State\JsonStreamerProvider') 33 | ->decorate('api_platform.state_provider.main', null, 310) 34 | ->args([ 35 | service('api_platform.jsonld.state_provider.json_streamer.inner'), 36 | service('api_platform.jsonld.json_streamer.stream_reader'), 37 | ]); 38 | }; 39 | -------------------------------------------------------------------------------- /Bundle/Resources/config/swagger_ui.php: -------------------------------------------------------------------------------- 1 | 7 | * 8 | * For the full copyright and license information, please view the LICENSE 9 | * file that was distributed with this source code. 10 | */ 11 | 12 | declare(strict_types=1); 13 | 14 | namespace Symfony\Component\DependencyInjection\Loader\Configurator; 15 | 16 | return function (ContainerConfigurator $container) { 17 | $services = $container->services(); 18 | 19 | $services->set('api_platform.swagger_ui.context', 'ApiPlatform\Symfony\Bundle\SwaggerUi\SwaggerUiContext') 20 | ->args([ 21 | '%api_platform.enable_swagger_ui%', 22 | '%api_platform.show_webby%', 23 | '%api_platform.enable_re_doc%', 24 | '%api_platform.graphql.enabled%', 25 | '%api_platform.graphql.graphiql.enabled%', 26 | '%api_platform.asset_package%', 27 | '%api_platform.swagger_ui.extra_configuration%', 28 | ]); 29 | 30 | $services->set('api_platform.swagger_ui.processor', 'ApiPlatform\Symfony\Bundle\SwaggerUi\SwaggerUiProcessor') 31 | ->args([ 32 | service('twig')->nullOnInvalid(), 33 | service('router'), 34 | service('api_platform.serializer'), 35 | service('api_platform.openapi.options'), 36 | service('api_platform.swagger_ui.context'), 37 | '%api_platform.docs_formats%', 38 | '%api_platform.oauth.clientId%', 39 | '%api_platform.oauth.clientSecret%', 40 | '%api_platform.oauth.pkce%', 41 | ]) 42 | ->tag('api_platform.state_processor', ['priority' => -100, 'key' => 'api_platform.swagger_ui.processor']); 43 | }; 44 | -------------------------------------------------------------------------------- /GraphQl/Resolver/Factory/DataCollectorResolverFactory.php: -------------------------------------------------------------------------------- 1 | 7 | * 8 | * For the full copyright and license information, please view the LICENSE 9 | * file that was distributed with this source code. 10 | */ 11 | 12 | declare(strict_types=1); 13 | 14 | namespace ApiPlatform\Symfony\GraphQl\Resolver\Factory; 15 | 16 | use ApiPlatform\GraphQl\Resolver\Factory\ResolverFactoryInterface; 17 | use ApiPlatform\Metadata\GraphQl\Operation; 18 | use ApiPlatform\Metadata\Property\Factory\PropertyMetadataFactoryInterface; 19 | use GraphQL\Type\Definition\ResolveInfo; 20 | use Symfony\Component\HttpFoundation\RequestStack; 21 | 22 | final class DataCollectorResolverFactory implements ResolverFactoryInterface 23 | { 24 | public function __construct(private readonly ResolverFactoryInterface $resolverFactory, private readonly ?RequestStack $requestStack) 25 | { 26 | } 27 | 28 | public function __invoke(?string $resourceClass = null, ?string $rootClass = null, ?Operation $operation = null, ?PropertyMetadataFactoryInterface $propertyMetadataFactory = null): callable 29 | { 30 | return function (?array $source, array $args, $context, ResolveInfo $info) use ($resourceClass, $rootClass, $operation) { 31 | if ($this->requestStack && null !== $request = $this->requestStack->getCurrentRequest()) { 32 | $request->attributes->set( 33 | '_graphql_args', 34 | [$resourceClass => $args] + $request->attributes->get('_graphql_args', []) 35 | ); 36 | } 37 | 38 | return ($this->resolverFactory)($resourceClass, $rootClass, $operation)($source, $args, $context, $info); 39 | }; 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /EventListener/ExceptionListener.php: -------------------------------------------------------------------------------- 1 | 7 | * 8 | * For the full copyright and license information, please view the LICENSE 9 | * file that was distributed with this source code. 10 | */ 11 | 12 | declare(strict_types=1); 13 | 14 | namespace ApiPlatform\Symfony\EventListener; 15 | 16 | use ApiPlatform\Metadata\Error; 17 | use ApiPlatform\State\Util\RequestAttributesExtractor; 18 | use Symfony\Component\HttpKernel\Event\ExceptionEvent; 19 | use Symfony\Component\HttpKernel\EventListener\ErrorListener; 20 | 21 | /** 22 | * Handles requests errors. 23 | * 24 | * @author Samuel ROZE 25 | * @author Kévin Dunglas 26 | */ 27 | final class ExceptionListener 28 | { 29 | public function __construct(private readonly ErrorListener $errorListener, public bool $handleSymfonyErrors = false) 30 | { 31 | } 32 | 33 | public function onKernelException(ExceptionEvent $event): void 34 | { 35 | $request = $event->getRequest(); 36 | 37 | // Normalize exceptions only for routes managed by API Platform 38 | if ( 39 | false === $this->handleSymfonyErrors 40 | && !((RequestAttributesExtractor::extractAttributes($request)['respond'] ?? $request->attributes->getBoolean('_api_respond', false)) || $request->attributes->getBoolean('_graphql', false)) 41 | ) { 42 | return; 43 | } 44 | 45 | // Don't loop on errors leave it to Symfony as we could not handle this properly 46 | if (($operation = $request->attributes->get('_api_operation')) && $operation instanceof Error) { 47 | return; 48 | } 49 | 50 | $this->errorListener->onKernelException($event); 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /Bundle/DependencyInjection/Compiler/MetadataAwareNameConverterPass.php: -------------------------------------------------------------------------------- 1 | 7 | * 8 | * For the full copyright and license information, please view the LICENSE 9 | * file that was distributed with this source code. 10 | */ 11 | 12 | declare(strict_types=1); 13 | 14 | namespace ApiPlatform\Symfony\Bundle\DependencyInjection\Compiler; 15 | 16 | use ApiPlatform\Metadata\Exception\RuntimeException; 17 | use Symfony\Component\DependencyInjection\Compiler\CompilerPassInterface; 18 | use Symfony\Component\DependencyInjection\ContainerBuilder; 19 | use Symfony\Component\DependencyInjection\Exception\InvalidArgumentException; 20 | use Symfony\Component\DependencyInjection\Reference; 21 | 22 | /** 23 | * Injects the metadata aware name converter if available. 24 | * 25 | * @internal 26 | * 27 | * @author Antoine Bluchet 28 | */ 29 | final class MetadataAwareNameConverterPass implements CompilerPassInterface 30 | { 31 | /** 32 | * {@inheritdoc} 33 | * 34 | * @throws RuntimeException 35 | * @throws InvalidArgumentException 36 | */ 37 | public function process(ContainerBuilder $container): void 38 | { 39 | if (!$container->hasDefinition('serializer.name_converter.metadata_aware')) { 40 | return; 41 | } 42 | 43 | if ($container->hasAlias('api_platform.name_converter')) { 44 | $nameConverter = (string) $container->getAlias('api_platform.name_converter'); 45 | 46 | $container->setParameter('.serializer.name_converter', $nameConverter); 47 | $container->getDefinition('serializer.name_converter.metadata_aware')->setArgument(1, new Reference($nameConverter)); 48 | } 49 | 50 | $container->setAlias('api_platform.name_converter', 'serializer.name_converter.metadata_aware'); 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /Validator/Metadata/Property/Restriction/PropertySchemaRestrictionMetadataInterface.php: -------------------------------------------------------------------------------- 1 | 7 | * 8 | * For the full copyright and license information, please view the LICENSE 9 | * file that was distributed with this source code. 10 | */ 11 | 12 | declare(strict_types=1); 13 | 14 | namespace ApiPlatform\Symfony\Validator\Metadata\Property\Restriction; 15 | 16 | use ApiPlatform\Metadata\ApiProperty; 17 | use Symfony\Component\Validator\Constraint; 18 | 19 | /** 20 | * Interface PropertySchemaRestrictionsInterface. 21 | * This interface is autoconfigured with the `api_platform.metadata.property_schema_restriction` tag. 22 | * It is used to generate a Resource schema using property restrictions based on the Symfony’s built-in validator. For example, the [Regex](https://symfony.com/doc/current/reference/constraints/Regex.html) constraint uses a [pattern](https://swagger.io/docs/specification/data-models/data-types/#pattern) type within the JSON schema. 23 | * 24 | * @author Andrii Penchuk penja7@gmail.com 25 | */ 26 | interface PropertySchemaRestrictionMetadataInterface 27 | { 28 | /** 29 | * Creates json schema restrictions based on the validation constraints. 30 | * 31 | * @param Constraint $constraint The validation constraint 32 | * @param ApiProperty $propertyMetadata The property metadata 33 | * 34 | * @return array The array of restrictions 35 | */ 36 | public function create(Constraint $constraint, ApiProperty $propertyMetadata): array; 37 | 38 | /** 39 | * Is the constraint supported by the schema restriction? 40 | * 41 | * @param Constraint $constraint The validation constraint 42 | * @param ApiProperty $propertyMetadata The property metadata 43 | */ 44 | public function supports(Constraint $constraint, ApiProperty $propertyMetadata): bool; 45 | } 46 | -------------------------------------------------------------------------------- /Messenger/Processor.php: -------------------------------------------------------------------------------- 1 | 7 | * 8 | * For the full copyright and license information, please view the LICENSE 9 | * file that was distributed with this source code. 10 | */ 11 | 12 | declare(strict_types=1); 13 | 14 | namespace ApiPlatform\Symfony\Messenger; 15 | 16 | use ApiPlatform\Metadata\DeleteOperationInterface; 17 | use ApiPlatform\Metadata\Operation; 18 | use ApiPlatform\Metadata\Util\ClassInfoTrait; 19 | use ApiPlatform\State\ProcessorInterface; 20 | use Symfony\Component\Messenger\Envelope; 21 | use Symfony\Component\Messenger\MessageBusInterface; 22 | use Symfony\Component\Messenger\Stamp\HandledStamp; 23 | 24 | final class Processor implements ProcessorInterface 25 | { 26 | use ClassInfoTrait; 27 | use DispatchTrait; 28 | 29 | public function __construct(MessageBusInterface $messageBus) 30 | { 31 | $this->messageBus = $messageBus; 32 | } 33 | 34 | private function persist(mixed $data, array $context = []): mixed 35 | { 36 | $envelope = $this->dispatch( 37 | (new Envelope($data)) 38 | ->with(new ContextStamp($context)) 39 | ); 40 | 41 | $handledStamp = $envelope->last(HandledStamp::class); 42 | if (!$handledStamp instanceof HandledStamp) { 43 | return $data; 44 | } 45 | 46 | return $handledStamp->getResult(); 47 | } 48 | 49 | private function remove(mixed $data): void 50 | { 51 | $this->dispatch( 52 | (new Envelope($data)) 53 | ->with(new RemoveStamp()) 54 | ); 55 | } 56 | 57 | public function process(mixed $data, Operation $operation, array $uriVariables = [], array $context = []): mixed 58 | { 59 | if ($operation instanceof DeleteOperationInterface) { 60 | $this->remove($data); 61 | 62 | return $data; 63 | } 64 | 65 | return $this->persist($data, $context); 66 | } 67 | } 68 | -------------------------------------------------------------------------------- /Bundle/Resources/config/state/processor.php: -------------------------------------------------------------------------------- 1 | 7 | * 8 | * For the full copyright and license information, please view the LICENSE 9 | * file that was distributed with this source code. 10 | */ 11 | 12 | declare(strict_types=1); 13 | 14 | namespace Symfony\Component\DependencyInjection\Loader\Configurator; 15 | 16 | return function (ContainerConfigurator $container) { 17 | $services = $container->services(); 18 | 19 | $services->alias('api_platform.state_processor.main', 'api_platform.state_processor.respond'); 20 | 21 | $services->set('api_platform.state_processor.serialize', 'ApiPlatform\State\Processor\SerializeProcessor') 22 | ->decorate('api_platform.state_processor.main', null, 200) 23 | ->args([ 24 | service('api_platform.state_processor.serialize.inner'), 25 | service('api_platform.serializer'), 26 | service('api_platform.serializer.context_builder'), 27 | ]); 28 | 29 | $services->set('api_platform.state_processor.write', 'ApiPlatform\State\Processor\WriteProcessor') 30 | ->decorate('api_platform.state_processor.main', null, 100) 31 | ->args([ 32 | service('api_platform.state_processor.write.inner'), 33 | service('api_platform.state_processor.locator'), 34 | ]); 35 | 36 | $services->set('api_platform.state_processor.respond', 'ApiPlatform\State\Processor\RespondProcessor') 37 | ->args([ 38 | service('api_platform.iri_converter'), 39 | service('api_platform.resource_class_resolver'), 40 | service('api_platform.metadata.operation.metadata_factory'), 41 | ]); 42 | 43 | $services->set('api_platform.state_processor.add_link_header', 'ApiPlatform\State\Processor\AddLinkHeaderProcessor') 44 | ->decorate('api_platform.state_processor.respond', null, 0) 45 | ->args([service('api_platform.state_processor.add_link_header.inner')]); 46 | }; 47 | -------------------------------------------------------------------------------- /EventListener/ValidateListener.php: -------------------------------------------------------------------------------- 1 | 7 | * 8 | * For the full copyright and license information, please view the LICENSE 9 | * file that was distributed with this source code. 10 | */ 11 | 12 | declare(strict_types=1); 13 | 14 | namespace ApiPlatform\Symfony\EventListener; 15 | 16 | use ApiPlatform\Metadata\Resource\Factory\ResourceMetadataCollectionFactoryInterface; 17 | use ApiPlatform\State\ProviderInterface; 18 | use ApiPlatform\State\Util\OperationRequestInitiatorTrait; 19 | use ApiPlatform\Validator\Exception\ValidationException; 20 | use Symfony\Component\HttpKernel\Event\ViewEvent; 21 | 22 | /** 23 | * Validates data. 24 | * 25 | * @author Kévin Dunglas 26 | */ 27 | final class ValidateListener 28 | { 29 | use OperationRequestInitiatorTrait; 30 | 31 | /** 32 | * @param ProviderInterface $provider 33 | */ 34 | public function __construct(private readonly ProviderInterface $provider, ?ResourceMetadataCollectionFactoryInterface $resourceMetadataCollectionFactory = null) 35 | { 36 | $this->resourceMetadataCollectionFactory = $resourceMetadataCollectionFactory; 37 | } 38 | 39 | /** 40 | * Validates data returned by the controller if applicable. 41 | * 42 | * @throws ValidationException 43 | */ 44 | public function onKernelView(ViewEvent $event): void 45 | { 46 | $request = $event->getRequest(); 47 | $operation = $this->initializeOperation($request); 48 | 49 | if (!$operation) { 50 | return; 51 | } 52 | 53 | if (null === $operation->canValidate()) { 54 | $operation = $operation->withValidate(!$request->isMethodSafe() && !$request->isMethod('DELETE')); 55 | } 56 | 57 | $this->provider->provide($operation, $request->attributes->get('_api_uri_variables') ?? [], [ 58 | 'request' => $request, 59 | 'uri_variables' => $request->attributes->get('_api_uri_variables') ?? [], 60 | 'resource_class' => $operation->getClass(), 61 | ]); 62 | } 63 | } 64 | -------------------------------------------------------------------------------- /Validator/Metadata/Property/Restriction/PropertySchemaOneOfRestriction.php: -------------------------------------------------------------------------------- 1 | 7 | * 8 | * For the full copyright and license information, please view the LICENSE 9 | * file that was distributed with this source code. 10 | */ 11 | 12 | declare(strict_types=1); 13 | 14 | namespace ApiPlatform\Symfony\Validator\Metadata\Property\Restriction; 15 | 16 | use ApiPlatform\Metadata\ApiProperty; 17 | use Symfony\Component\Validator\Constraint; 18 | use Symfony\Component\Validator\Constraints\AtLeastOneOf; 19 | 20 | /** 21 | * @author Alan Poulain 22 | */ 23 | final class PropertySchemaOneOfRestriction implements PropertySchemaRestrictionMetadataInterface 24 | { 25 | /** 26 | * @param iterable $restrictionsMetadata 27 | */ 28 | public function __construct(private readonly iterable $restrictionsMetadata = []) 29 | { 30 | } 31 | 32 | /** 33 | * {@inheritdoc} 34 | * 35 | * @param AtLeastOneOf $constraint 36 | */ 37 | public function create(Constraint $constraint, ApiProperty $propertyMetadata): array 38 | { 39 | $oneOfConstraints = $constraint->constraints; 40 | $oneOfRestrictions = []; 41 | 42 | foreach ($oneOfConstraints as $oneOfConstraint) { 43 | foreach ($this->restrictionsMetadata as $restrictionMetadata) { 44 | if ($restrictionMetadata->supports($oneOfConstraint, $propertyMetadata) && !empty($oneOfRestriction = $restrictionMetadata->create($oneOfConstraint, $propertyMetadata))) { 45 | $oneOfRestrictions[] = $oneOfRestriction; 46 | } 47 | } 48 | } 49 | 50 | if (!empty($oneOfRestrictions)) { 51 | return ['oneOf' => $oneOfRestrictions]; 52 | } 53 | 54 | return []; 55 | } 56 | 57 | /** 58 | * {@inheritdoc} 59 | */ 60 | public function supports(Constraint $constraint, ApiProperty $propertyMetadata): bool 61 | { 62 | return $constraint instanceof AtLeastOneOf; 63 | } 64 | } 65 | -------------------------------------------------------------------------------- /Validator/Validator.php: -------------------------------------------------------------------------------- 1 | 7 | * 8 | * For the full copyright and license information, please view the LICENSE 9 | * file that was distributed with this source code. 10 | */ 11 | 12 | declare(strict_types=1); 13 | 14 | namespace ApiPlatform\Symfony\Validator; 15 | 16 | use ApiPlatform\Validator\Exception\ValidationException; 17 | use ApiPlatform\Validator\ValidatorInterface; 18 | use Psr\Container\ContainerInterface; 19 | use Symfony\Component\Validator\Constraints\GroupSequence; 20 | use Symfony\Component\Validator\Validator\ValidatorInterface as SymfonyValidatorInterface; 21 | 22 | /** 23 | * Validates an item using the Symfony validator component. 24 | * 25 | * @author Kévin Dunglas 26 | */ 27 | final class Validator implements ValidatorInterface 28 | { 29 | public function __construct(private readonly SymfonyValidatorInterface $validator, private readonly ?ContainerInterface $container = null) 30 | { 31 | } 32 | 33 | /** 34 | * {@inheritdoc} 35 | */ 36 | public function validate(object $data, array $context = []): void 37 | { 38 | if (null !== $validationGroups = $context['groups'] ?? null) { 39 | if ( 40 | $this->container 41 | && \is_string($validationGroups) 42 | && $this->container->has($validationGroups) 43 | && ($service = $this->container->get($validationGroups)) 44 | && \is_callable($service) 45 | ) { 46 | $validationGroups = $service($data); 47 | } elseif (\is_callable($validationGroups)) { 48 | $validationGroups = $validationGroups($data); 49 | } 50 | 51 | if (!$validationGroups instanceof GroupSequence) { 52 | $validationGroups = (array) $validationGroups; 53 | } 54 | } 55 | 56 | $violations = $this->validator->validate($data, null, $validationGroups); 57 | if (0 !== \count($violations)) { 58 | throw new ValidationException($violations); 59 | } 60 | } 61 | } 62 | -------------------------------------------------------------------------------- /Bundle/Resources/config/state/security.php: -------------------------------------------------------------------------------- 1 | 7 | * 8 | * For the full copyright and license information, please view the LICENSE 9 | * file that was distributed with this source code. 10 | */ 11 | 12 | declare(strict_types=1); 13 | 14 | namespace Symfony\Component\DependencyInjection\Loader\Configurator; 15 | 16 | use ApiPlatform\State\Provider\SecurityParameterProvider; 17 | use ApiPlatform\Symfony\Security\State\AccessCheckerProvider; 18 | 19 | return function (ContainerConfigurator $container) { 20 | $services = $container->services(); 21 | 22 | $services->set('api_platform.state_provider.access_checker', AccessCheckerProvider::class) 23 | ->decorate('api_platform.state_provider.read', null, 0) 24 | ->args([ 25 | service('api_platform.state_provider.access_checker.inner'), 26 | service('api_platform.security.resource_access_checker'), 27 | ]); 28 | 29 | $services->set('api_platform.state_provider.access_checker.post_deserialize', AccessCheckerProvider::class) 30 | ->decorate('api_platform.state_provider.deserialize', null, 0) 31 | ->args([ 32 | service('api_platform.state_provider.access_checker.post_deserialize.inner'), 33 | service('api_platform.security.resource_access_checker'), 34 | 'post_denormalize', 35 | ]); 36 | 37 | $services->set('api_platform.state_provider.security_parameter', SecurityParameterProvider::class) 38 | ->decorate('api_platform.state_provider.access_checker', null, 0) 39 | ->args([ 40 | service('api_platform.state_provider.security_parameter.inner'), 41 | service('api_platform.security.resource_access_checker'), 42 | ]); 43 | 44 | $services->set('api_platform.state_provider.access_checker.pre_read', AccessCheckerProvider::class) 45 | ->decorate('api_platform.state_provider.read', null, 10) 46 | ->args([ 47 | service('api_platform.state_provider.access_checker.pre_read.inner'), 48 | service('api_platform.security.resource_access_checker'), 49 | 'pre_read', 50 | ]); 51 | }; 52 | -------------------------------------------------------------------------------- /Validator/Serializer/ValidationExceptionNormalizer.php: -------------------------------------------------------------------------------- 1 | 7 | * 8 | * For the full copyright and license information, please view the LICENSE 9 | * file that was distributed with this source code. 10 | */ 11 | 12 | declare(strict_types=1); 13 | 14 | namespace ApiPlatform\Symfony\Validator\Serializer; 15 | 16 | use ApiPlatform\Validator\Exception\ValidationException; 17 | use Symfony\Component\Serializer\NameConverter\NameConverterInterface; 18 | use Symfony\Component\Serializer\Normalizer\NormalizerInterface; 19 | 20 | class ValidationExceptionNormalizer implements NormalizerInterface 21 | { 22 | public function __construct(private readonly NormalizerInterface $decorated, private readonly ?NameConverterInterface $nameConverter) 23 | { 24 | } 25 | 26 | public function normalize(mixed $object, ?string $format = null, array $context = []): array|string|int|float|bool|\ArrayObject|null 27 | { 28 | $messages = []; 29 | foreach ($object->getConstraintViolationList() as $violation) { 30 | $class = \is_object($root = $violation->getRoot()) ? $root::class : null; 31 | 32 | if ($this->nameConverter) { 33 | $propertyPath = $this->nameConverter->normalize($violation->getPropertyPath(), $class, $format); 34 | } else { 35 | $propertyPath = $violation->getPropertyPath(); 36 | } 37 | 38 | $messages[] = ($propertyPath ? "{$propertyPath}: " : '').$violation->getMessage(); 39 | } 40 | 41 | $str = implode("\n", $messages); 42 | $object->setDetail($str); 43 | 44 | return $this->decorated->normalize($object, $format, $context); 45 | } 46 | 47 | public function supportsNormalization(mixed $data, ?string $format = null, array $context = []): bool 48 | { 49 | return $data instanceof ValidationException && $this->decorated->supportsNormalization($data, $format, $context); 50 | } 51 | 52 | /** 53 | * @param string|null $format 54 | */ 55 | public function getSupportedTypes($format): array 56 | { 57 | return [ValidationException::class => false]; 58 | } 59 | } 60 | -------------------------------------------------------------------------------- /Bundle/Resources/config/symfony/controller.php: -------------------------------------------------------------------------------- 1 | 7 | * 8 | * For the full copyright and license information, please view the LICENSE 9 | * file that was distributed with this source code. 10 | */ 11 | 12 | declare(strict_types=1); 13 | 14 | namespace Symfony\Component\DependencyInjection\Loader\Configurator; 15 | 16 | return function (ContainerConfigurator $container) { 17 | $services = $container->services(); 18 | 19 | $services->set('api_platform.symfony.main_controller', 'ApiPlatform\Symfony\Controller\MainController') 20 | ->public() 21 | ->args([ 22 | service('api_platform.metadata.resource.metadata_collection_factory'), 23 | service('api_platform.state_provider.main'), 24 | service('api_platform.state_processor.main'), 25 | service('api_platform.uri_variables.converter')->ignoreOnInvalid(), 26 | service('logger')->ignoreOnInvalid(), 27 | ]); 28 | 29 | $services->set('api_platform.action.entrypoint', 'ApiPlatform\Symfony\Action\EntrypointAction') 30 | ->public() 31 | ->args([ 32 | service('api_platform.metadata.resource.name_collection_factory'), 33 | service('api_platform.state_provider.main'), 34 | service('api_platform.state_processor.main'), 35 | '%api_platform.docs_formats%', 36 | ]); 37 | 38 | $services->set('api_platform.action.documentation', 'ApiPlatform\Symfony\Action\DocumentationAction') 39 | ->public() 40 | ->args([ 41 | service('api_platform.metadata.resource.name_collection_factory'), 42 | '%api_platform.title%', 43 | '%api_platform.description%', 44 | '%api_platform.version%', 45 | service('api_platform.openapi.factory')->nullOnInvalid(), 46 | service('api_platform.state_provider.main'), 47 | service('api_platform.state_processor.main'), 48 | service('api_platform.negotiator')->nullOnInvalid(), 49 | '%api_platform.docs_formats%', 50 | '%api_platform.enable_swagger_ui%', 51 | '%api_platform.enable_docs%', 52 | ]); 53 | }; 54 | -------------------------------------------------------------------------------- /EventListener/JsonApi/TransformSortingParametersListener.php: -------------------------------------------------------------------------------- 1 | 7 | * 8 | * For the full copyright and license information, please view the LICENSE 9 | * file that was distributed with this source code. 10 | */ 11 | 12 | declare(strict_types=1); 13 | 14 | namespace ApiPlatform\Symfony\EventListener\JsonApi; 15 | 16 | use Symfony\Component\HttpKernel\Event\RequestEvent; 17 | 18 | /** 19 | * @see http://jsonapi.org/format/#fetching-sorting 20 | * @see https://api-platform.com/docs/core/filters#order-filter 21 | * 22 | * @author Héctor Hurtarte 23 | * @author Baptiste Meyer 24 | */ 25 | final class TransformSortingParametersListener 26 | { 27 | public function __construct(private readonly string $orderParameterName = 'order') 28 | { 29 | } 30 | 31 | public function onKernelRequest(RequestEvent $event): void 32 | { 33 | $request = $event->getRequest(); 34 | if (($operation = $request->attributes->get('_api_operation')) && 'api_platform.symfony.main_controller' === $operation->getController()) { 35 | return; 36 | } 37 | 38 | $orderParameter = $request->query->all()['sort'] ?? null; 39 | 40 | if ( 41 | null === $orderParameter 42 | || \is_array($orderParameter) 43 | || 'jsonapi' !== $request->getRequestFormat() 44 | ) { 45 | return; 46 | } 47 | 48 | $orderParametersArray = explode(',', (string) $orderParameter); 49 | $transformedOrderParametersArray = []; 50 | 51 | foreach ($orderParametersArray as $orderParameter) { 52 | $sorting = 'asc'; 53 | 54 | if ('-' === ($orderParameter[0] ?? null)) { 55 | $sorting = 'desc'; 56 | $orderParameter = substr($orderParameter, 1); 57 | } 58 | 59 | $transformedOrderParametersArray[$orderParameter] = $sorting; 60 | } 61 | 62 | $filters = $request->attributes->get('_api_filters', []); 63 | $filters[$this->orderParameterName] = $transformedOrderParametersArray; 64 | $request->attributes->set('_api_filters', $filters); 65 | } 66 | } 67 | -------------------------------------------------------------------------------- /EventListener/RespondListener.php: -------------------------------------------------------------------------------- 1 | 7 | * 8 | * For the full copyright and license information, please view the LICENSE 9 | * file that was distributed with this source code. 10 | */ 11 | 12 | declare(strict_types=1); 13 | 14 | namespace ApiPlatform\Symfony\EventListener; 15 | 16 | use ApiPlatform\Metadata\Resource\Factory\ResourceMetadataCollectionFactoryInterface; 17 | use ApiPlatform\State\ProcessorInterface; 18 | use ApiPlatform\State\Util\OperationRequestInitiatorTrait; 19 | use ApiPlatform\State\Util\RequestAttributesExtractor; 20 | use Symfony\Component\HttpFoundation\Response; 21 | use Symfony\Component\HttpKernel\Event\ViewEvent; 22 | 23 | /** 24 | * Builds the response object. 25 | * 26 | * @author Kévin Dunglas 27 | */ 28 | final class RespondListener 29 | { 30 | use OperationRequestInitiatorTrait; 31 | 32 | /** 33 | * @param ProcessorInterface $processor 34 | */ 35 | public function __construct(private readonly ProcessorInterface $processor, ?ResourceMetadataCollectionFactoryInterface $resourceMetadataFactory = null) 36 | { 37 | $this->resourceMetadataCollectionFactory = $resourceMetadataFactory; 38 | } 39 | 40 | /** 41 | * Creates a Response to send to the client according to the requested format. 42 | */ 43 | public function onKernelView(ViewEvent $event): void 44 | { 45 | $request = $event->getRequest(); 46 | $operation = $this->initializeOperation($request); 47 | 48 | $attributes = RequestAttributesExtractor::extractAttributes($request); 49 | if (!($attributes['respond'] ?? $request->attributes->getBoolean('_api_respond')) || !$operation) { 50 | return; 51 | } 52 | 53 | $uriVariables = $request->attributes->get('_api_uri_variables') ?? []; 54 | $response = $this->processor->process($event->getControllerResult(), $operation, $uriVariables, [ 55 | 'request' => $request, 56 | 'uri_variables' => $uriVariables, 57 | 'resource_class' => $operation->getClass(), 58 | 'original_data' => $request->attributes->get('original_data'), 59 | ]); 60 | 61 | $event->setResponse($response); 62 | } 63 | } 64 | -------------------------------------------------------------------------------- /Bundle/Resources/config/graphql/security.php: -------------------------------------------------------------------------------- 1 | 7 | * 8 | * For the full copyright and license information, please view the LICENSE 9 | * file that was distributed with this source code. 10 | */ 11 | 12 | declare(strict_types=1); 13 | 14 | namespace Symfony\Component\DependencyInjection\Loader\Configurator; 15 | 16 | return function (ContainerConfigurator $container) { 17 | $services = $container->services(); 18 | 19 | $services->set('api_platform.graphql.state_provider.access_checker', 'ApiPlatform\Symfony\Security\State\AccessCheckerProvider') 20 | ->decorate('api_platform.graphql.state_provider.read', null, 0) 21 | ->args([ 22 | service('api_platform.graphql.state_provider.access_checker.inner'), 23 | service('api_platform.security.resource_access_checker'), 24 | ]); 25 | 26 | $services->set('api_platform.graphql.state_provider.access_checker.post_deserialize', 'ApiPlatform\Symfony\Security\State\AccessCheckerProvider') 27 | ->decorate('api_platform.graphql.state_provider.denormalizer', null, 0) 28 | ->args([ 29 | service('api_platform.graphql.state_provider.access_checker.post_deserialize.inner'), 30 | service('api_platform.security.resource_access_checker'), 31 | 'post_denormalize', 32 | ]); 33 | 34 | $services->set('api_platform.graphql.state_provider.access_checker.post_validate', 'ApiPlatform\Symfony\Security\State\AccessCheckerProvider') 35 | ->decorate('api_platform.graphql.state_provider.validate', null, 0) 36 | ->args([ 37 | service('api_platform.graphql.state_provider.access_checker.post_validate.inner'), 38 | service('api_platform.security.resource_access_checker'), 39 | 'post_validate', 40 | ]); 41 | 42 | $services->set('api_platform.graphql.state_provider.access_checker.after_resolver', 'ApiPlatform\Symfony\Security\State\AccessCheckerProvider') 43 | ->decorate('api_platform.graphql.state_provider', null, 170) 44 | ->args([ 45 | service('api_platform.graphql.state_provider.access_checker.after_resolver.inner'), 46 | service('api_platform.security.resource_access_checker'), 47 | 'after_resolver', 48 | ]); 49 | }; 50 | -------------------------------------------------------------------------------- /Bundle/Resources/config/metadata/property_name.php: -------------------------------------------------------------------------------- 1 | 7 | * 8 | * For the full copyright and license information, please view the LICENSE 9 | * file that was distributed with this source code. 10 | */ 11 | 12 | declare(strict_types=1); 13 | 14 | namespace Symfony\Component\DependencyInjection\Loader\Configurator; 15 | 16 | return function (ContainerConfigurator $container) { 17 | $services = $container->services(); 18 | 19 | $services->alias('api_platform.metadata.property.name_collection_factory', 'api_platform.metadata.property.name_collection_factory.property_info'); 20 | 21 | $services->alias('ApiPlatform\Metadata\Property\Factory\PropertyNameCollectionFactoryInterface', 'api_platform.metadata.property.name_collection_factory'); 22 | 23 | $services->set('api_platform.metadata.property.name_collection_factory.property_info', 'ApiPlatform\Metadata\Property\Factory\PropertyInfoPropertyNameCollectionFactory') 24 | ->args([service('api_platform.property_info')]); 25 | 26 | $services->set('api_platform.metadata.property.name_collection_factory.cached', 'ApiPlatform\Metadata\Property\Factory\CachedPropertyNameCollectionFactory') 27 | ->decorate('api_platform.metadata.property.name_collection_factory', null, -10) 28 | ->args([ 29 | service('api_platform.cache.metadata.property'), 30 | service('api_platform.metadata.property.name_collection_factory.cached.inner'), 31 | ]); 32 | 33 | $services->set('api_platform.metadata.property.name_collection_factory.xml', 'ApiPlatform\Metadata\Property\Factory\ExtractorPropertyNameCollectionFactory') 34 | ->decorate('api_platform.metadata.property.name_collection_factory', null, 0) 35 | ->args([ 36 | service('api_platform.metadata.property_extractor.xml'), 37 | service('api_platform.metadata.property.name_collection_factory.xml.inner'), 38 | ]); 39 | 40 | $services->set('api_platform.metadata.property.name_collection_factory.concerns', 'ApiPlatform\Metadata\Property\Factory\ConcernsPropertyNameCollectionMetadataFactory') 41 | ->decorate('api_platform.metadata.property.name_collection_factory', null, 0) 42 | ->args([service('api_platform.metadata.property.name_collection_factory.concerns.inner')]); 43 | }; 44 | -------------------------------------------------------------------------------- /Bundle/Resources/views/DataCollector/api-platform-icon.svg: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /Validator/Metadata/Property/Restriction/PropertySchemaGreaterThanOrEqualRestriction.php: -------------------------------------------------------------------------------- 1 | 7 | * 8 | * For the full copyright and license information, please view the LICENSE 9 | * file that was distributed with this source code. 10 | */ 11 | 12 | declare(strict_types=1); 13 | 14 | namespace ApiPlatform\Symfony\Validator\Metadata\Property\Restriction; 15 | 16 | use ApiPlatform\Metadata\ApiProperty; 17 | use Symfony\Component\PropertyInfo\PropertyInfoExtractor; 18 | use Symfony\Component\PropertyInfo\Type as LegacyType; 19 | use Symfony\Component\TypeInfo\Type; 20 | use Symfony\Component\TypeInfo\TypeIdentifier; 21 | use Symfony\Component\Validator\Constraint; 22 | use Symfony\Component\Validator\Constraints\GreaterThanOrEqual; 23 | 24 | /** 25 | * @author Tomas Norkūnas 26 | */ 27 | final class PropertySchemaGreaterThanOrEqualRestriction implements PropertySchemaRestrictionMetadataInterface 28 | { 29 | /** 30 | * {@inheritdoc} 31 | * 32 | * @param GreaterThanOrEqual $constraint 33 | */ 34 | public function create(Constraint $constraint, ApiProperty $propertyMetadata): array 35 | { 36 | return [ 37 | 'minimum' => $constraint->value, 38 | ]; 39 | } 40 | 41 | public function supports(Constraint $constraint, ApiProperty $propertyMetadata): bool 42 | { 43 | if (!$constraint instanceof GreaterThanOrEqual || !is_numeric($constraint->value)) { 44 | return false; 45 | } 46 | 47 | if (method_exists(PropertyInfoExtractor::class, 'getType')) { 48 | $type = $propertyMetadata->getExtraProperties()['nested_schema'] ?? false 49 | ? Type::int() 50 | : $propertyMetadata->getNativeType(); 51 | 52 | return $type->isIdentifiedBy(TypeIdentifier::INT, TypeIdentifier::FLOAT); 53 | } 54 | 55 | $types = array_map(fn (LegacyType $type) => $type->getBuiltinType(), $propertyMetadata->getBuiltinTypes() ?? []); 56 | if ($propertyMetadata->getExtraProperties()['nested_schema'] ?? false) { 57 | $types = [LegacyType::BUILTIN_TYPE_INT]; 58 | } 59 | 60 | return \count($types) > 0 && \count(array_intersect($types, [LegacyType::BUILTIN_TYPE_INT, LegacyType::BUILTIN_TYPE_FLOAT])) > 0; 61 | } 62 | } 63 | -------------------------------------------------------------------------------- /Bundle/DependencyInjection/Compiler/MutatorPass.php: -------------------------------------------------------------------------------- 1 | 7 | * 8 | * For the full copyright and license information, please view the LICENSE 9 | * file that was distributed with this source code. 10 | */ 11 | 12 | declare(strict_types=1); 13 | 14 | namespace ApiPlatform\Symfony\Bundle\DependencyInjection\Compiler; 15 | 16 | use Symfony\Component\DependencyInjection\Compiler\CompilerPassInterface; 17 | use Symfony\Component\DependencyInjection\ContainerBuilder; 18 | use Symfony\Component\DependencyInjection\Reference; 19 | 20 | final class MutatorPass implements CompilerPassInterface 21 | { 22 | public function process(ContainerBuilder $container): void 23 | { 24 | $this->processResourceMutators($container); 25 | $this->processOperationMutators($container); 26 | } 27 | 28 | public function processResourceMutators(ContainerBuilder $container): void 29 | { 30 | if (!$container->hasDefinition('api_platform.metadata.mutator_collection.resource')) { 31 | return; 32 | } 33 | 34 | $definition = $container->getDefinition('api_platform.metadata.mutator_collection.resource'); 35 | 36 | $mutators = $container->findTaggedServiceIds('api_platform.resource_mutator'); 37 | 38 | foreach ($mutators as $id => $tags) { 39 | foreach ($tags as $tag) { 40 | $definition->addMethodCall('add', [ 41 | $tag['resourceClass'], 42 | new Reference($id), 43 | ]); 44 | } 45 | } 46 | } 47 | 48 | private function processOperationMutators(ContainerBuilder $container): void 49 | { 50 | if (!$container->hasDefinition('api_platform.metadata.mutator_collection.operation')) { 51 | return; 52 | } 53 | 54 | $definition = $container->getDefinition('api_platform.metadata.mutator_collection.operation'); 55 | 56 | $mutators = $container->findTaggedServiceIds('api_platform.operation_mutator'); 57 | 58 | foreach ($mutators as $id => $tags) { 59 | foreach ($tags as $tag) { 60 | $definition->addMethodCall('add', [ 61 | $tag['operationName'], 62 | new Reference($id), 63 | ]); 64 | } 65 | } 66 | } 67 | } 68 | -------------------------------------------------------------------------------- /Bundle/Resources/config/state/object_mapper.php: -------------------------------------------------------------------------------- 1 | 7 | * 8 | * For the full copyright and license information, please view the LICENSE 9 | * file that was distributed with this source code. 10 | */ 11 | 12 | declare(strict_types=1); 13 | 14 | namespace Symfony\Component\DependencyInjection\Loader\Configurator; 15 | 16 | return function (ContainerConfigurator $container) { 17 | $services = $container->services(); 18 | 19 | $services->set('api_platform.object_mapper.metadata_factory', 'Symfony\Component\ObjectMapper\Metadata\ReflectionObjectMapperMetadataFactory'); 20 | 21 | $services->alias('api_platform.object_mapper', 'object_mapper'); 22 | 23 | $services->set('api_platform.object_mapper.relation', 'ApiPlatform\State\ObjectMapper\ObjectMapper') 24 | ->decorate('api_platform.object_mapper', null, -255) 25 | ->args([service('api_platform.object_mapper.relation.inner')]); 26 | 27 | $services->set('api_platform.state_provider.object_mapper', 'ApiPlatform\State\Provider\ObjectMapperProvider') 28 | ->decorate('api_platform.state_provider.locator', null, 0) 29 | ->args([ 30 | service('api_platform.object_mapper')->nullOnInvalid(), 31 | service('api_platform.state_provider.object_mapper.inner'), 32 | service('api_platform.object_mapper.metadata_factory'), 33 | ]); 34 | 35 | $services->set('api_platform.state_processor.object_mapper', 'ApiPlatform\State\Processor\ObjectMapperProcessor') 36 | ->decorate('api_platform.state_processor.locator', null, 0) 37 | ->args([ 38 | service('api_platform.object_mapper')->nullOnInvalid(), 39 | service('api_platform.state_processor.object_mapper.inner'), 40 | service('api_platform.object_mapper.metadata_factory'), 41 | ]); 42 | 43 | $services->set('api_platform.metadata.resource.metadata_collection_factory.object_mapper', 'ApiPlatform\Metadata\Resource\Factory\ObjectMapperMetadataCollectionFactory') 44 | ->decorate('api_platform.metadata.resource.metadata_collection_factory', null, 100) 45 | ->args([ 46 | service('api_platform.metadata.resource.metadata_collection_factory.object_mapper.inner'), 47 | service('api_platform.object_mapper.metadata_factory'), 48 | ]); 49 | }; 50 | -------------------------------------------------------------------------------- /Validator/Metadata/Property/Restriction/PropertySchemaLessThanRestriction.php: -------------------------------------------------------------------------------- 1 | 7 | * 8 | * For the full copyright and license information, please view the LICENSE 9 | * file that was distributed with this source code. 10 | */ 11 | 12 | declare(strict_types=1); 13 | 14 | namespace ApiPlatform\Symfony\Validator\Metadata\Property\Restriction; 15 | 16 | use ApiPlatform\Metadata\ApiProperty; 17 | use Symfony\Component\PropertyInfo\PropertyInfoExtractor; 18 | use Symfony\Component\PropertyInfo\Type as LegacyType; 19 | use Symfony\Component\TypeInfo\Type; 20 | use Symfony\Component\TypeInfo\TypeIdentifier; 21 | use Symfony\Component\Validator\Constraint; 22 | use Symfony\Component\Validator\Constraints\LessThan; 23 | 24 | /** 25 | * @author Tomas Norkūnas 26 | */ 27 | final class PropertySchemaLessThanRestriction implements PropertySchemaRestrictionMetadataInterface 28 | { 29 | /** 30 | * {@inheritdoc} 31 | * 32 | * @param LessThan $constraint 33 | */ 34 | public function create(Constraint $constraint, ApiProperty $propertyMetadata): array 35 | { 36 | return [ 37 | 'maximum' => $constraint->value, 38 | 'exclusiveMaximum' => $constraint->value, 39 | ]; 40 | } 41 | 42 | public function supports(Constraint $constraint, ApiProperty $propertyMetadata): bool 43 | { 44 | if (!$constraint instanceof LessThan || !is_numeric($constraint->value)) { 45 | return false; 46 | } 47 | 48 | if (method_exists(PropertyInfoExtractor::class, 'getType')) { 49 | $type = $propertyMetadata->getExtraProperties()['nested_schema'] ?? false 50 | ? Type::int() 51 | : $propertyMetadata->getNativeType(); 52 | 53 | return $type->isIdentifiedBy(TypeIdentifier::INT, TypeIdentifier::FLOAT); 54 | } 55 | 56 | $types = array_map(fn (LegacyType $type) => $type->getBuiltinType(), $propertyMetadata->getBuiltinTypes() ?? []); 57 | if ($propertyMetadata->getExtraProperties()['nested_schema'] ?? false) { 58 | $types = [LegacyType::BUILTIN_TYPE_INT]; 59 | } 60 | 61 | return \count($types) > 0 && \count(array_intersect($types, [LegacyType::BUILTIN_TYPE_INT, LegacyType::BUILTIN_TYPE_FLOAT])) > 0; 62 | } 63 | } 64 | -------------------------------------------------------------------------------- /EventListener/AddFormatListener.php: -------------------------------------------------------------------------------- 1 | 7 | * 8 | * For the full copyright and license information, please view the LICENSE 9 | * file that was distributed with this source code. 10 | */ 11 | 12 | declare(strict_types=1); 13 | 14 | namespace ApiPlatform\Symfony\EventListener; 15 | 16 | use ApiPlatform\Metadata\Resource\Factory\ResourceMetadataCollectionFactoryInterface; 17 | use ApiPlatform\State\ProviderInterface; 18 | use ApiPlatform\State\Util\OperationRequestInitiatorTrait; 19 | use ApiPlatform\State\Util\RequestAttributesExtractor; 20 | use Symfony\Component\HttpKernel\Event\RequestEvent; 21 | use Symfony\Component\HttpKernel\Exception\NotAcceptableHttpException; 22 | use Symfony\Component\HttpKernel\Exception\NotFoundHttpException; 23 | 24 | /** 25 | * Chooses the format to use according to the Accept header and supported formats. 26 | * 27 | * @author Kévin Dunglas 28 | */ 29 | final class AddFormatListener 30 | { 31 | use OperationRequestInitiatorTrait; 32 | 33 | /** 34 | * @param ProviderInterface $provider 35 | */ 36 | public function __construct( 37 | private ProviderInterface $provider, 38 | ?ResourceMetadataCollectionFactoryInterface $resourceMetadataCollectionFactory = null, 39 | ) { 40 | $this->resourceMetadataCollectionFactory = $resourceMetadataCollectionFactory; 41 | } 42 | 43 | /** 44 | * Sets the applicable format to the HttpFoundation Request. 45 | * 46 | * @throws NotFoundHttpException 47 | * @throws NotAcceptableHttpException 48 | */ 49 | public function onKernelRequest(RequestEvent $event): void 50 | { 51 | $request = $event->getRequest(); 52 | $operation = $this->initializeOperation($request); 53 | $attributes = RequestAttributesExtractor::extractAttributes($request); 54 | if (!($attributes['respond'] ?? $request->attributes->getBoolean('_api_respond')) || !$operation) { 55 | return; 56 | } 57 | 58 | $this->provider->provide($operation, $request->attributes->get('_api_uri_variables') ?? [], [ 59 | 'request' => $request, 60 | 'uri_variables' => $request->attributes->get('_api_uri_variables') ?? [], 61 | 'resource_class' => $operation->getClass(), 62 | ]); 63 | } 64 | } 65 | -------------------------------------------------------------------------------- /Validator/Metadata/Property/Restriction/PropertySchemaLessThanOrEqualRestriction.php: -------------------------------------------------------------------------------- 1 | 7 | * 8 | * For the full copyright and license information, please view the LICENSE 9 | * file that was distributed with this source code. 10 | */ 11 | 12 | declare(strict_types=1); 13 | 14 | namespace ApiPlatform\Symfony\Validator\Metadata\Property\Restriction; 15 | 16 | use ApiPlatform\Metadata\ApiProperty; 17 | use Symfony\Component\PropertyInfo\PropertyInfoExtractor; 18 | use Symfony\Component\PropertyInfo\Type as LegacyType; 19 | use Symfony\Component\TypeInfo\Type; 20 | use Symfony\Component\TypeInfo\TypeIdentifier; 21 | use Symfony\Component\Validator\Constraint; 22 | use Symfony\Component\Validator\Constraints\LessThanOrEqual; 23 | 24 | /** 25 | * @author Tomas Norkūnas 26 | */ 27 | final class PropertySchemaLessThanOrEqualRestriction implements PropertySchemaRestrictionMetadataInterface 28 | { 29 | /** 30 | * {@inheritdoc} 31 | * 32 | * @param LessThanOrEqual $constraint 33 | */ 34 | public function create(Constraint $constraint, ApiProperty $propertyMetadata): array 35 | { 36 | return [ 37 | 'maximum' => $constraint->value, 38 | ]; 39 | } 40 | 41 | /** 42 | * {@inheritdoc} 43 | */ 44 | public function supports(Constraint $constraint, ApiProperty $propertyMetadata): bool 45 | { 46 | if (!$constraint instanceof LessThanOrEqual || !is_numeric($constraint->value)) { 47 | return false; 48 | } 49 | 50 | if (method_exists(PropertyInfoExtractor::class, 'getType')) { 51 | $type = $propertyMetadata->getExtraProperties()['nested_schema'] ?? false 52 | ? Type::int() 53 | : $propertyMetadata->getNativeType(); 54 | 55 | return $type->isIdentifiedBy(TypeIdentifier::INT, TypeIdentifier::FLOAT); 56 | } 57 | 58 | $types = array_map(fn (LegacyType $type) => $type->getBuiltinType(), $propertyMetadata->getBuiltinTypes() ?? []); 59 | if ($propertyMetadata->getExtraProperties()['nested_schema'] ?? false) { 60 | $types = [LegacyType::BUILTIN_TYPE_INT]; 61 | } 62 | 63 | return \count($types) > 0 && \count(array_intersect($types, [LegacyType::BUILTIN_TYPE_INT, LegacyType::BUILTIN_TYPE_FLOAT])) > 0; 64 | } 65 | } 66 | -------------------------------------------------------------------------------- /EventListener/JsonStreamerDeserializeListener.php: -------------------------------------------------------------------------------- 1 | 7 | * 8 | * For the full copyright and license information, please view the LICENSE 9 | * file that was distributed with this source code. 10 | */ 11 | 12 | declare(strict_types=1); 13 | 14 | namespace ApiPlatform\Symfony\EventListener; 15 | 16 | use ApiPlatform\Metadata\Resource\Factory\ResourceMetadataCollectionFactoryInterface; 17 | use ApiPlatform\State\ProviderInterface; 18 | use ApiPlatform\State\Util\OperationRequestInitiatorTrait; 19 | use ApiPlatform\State\Util\RequestAttributesExtractor; 20 | use Symfony\Component\HttpKernel\Event\RequestEvent; 21 | 22 | /** 23 | * Deserializes the data sent in the requested format using JSON Streamer. 24 | * 25 | * @author Kévin Dunglas 26 | */ 27 | final class JsonStreamerDeserializeListener 28 | { 29 | use OperationRequestInitiatorTrait; 30 | 31 | /** 32 | * @param ProviderInterface $jsonStreamerProvider 33 | */ 34 | public function __construct( 35 | private ProviderInterface $jsonStreamerProvider, 36 | private readonly string $format, 37 | ?ResourceMetadataCollectionFactoryInterface $resourceMetadataFactory = null, 38 | ) { 39 | $this->resourceMetadataCollectionFactory = $resourceMetadataFactory; 40 | } 41 | 42 | /** 43 | * Deserializes the data sent in the requested format. 44 | */ 45 | public function onKernelRequest(RequestEvent $event): void 46 | { 47 | $request = $event->getRequest(); 48 | $operation = $this->initializeOperation($request); 49 | 50 | if ( 51 | !($attributes = RequestAttributesExtractor::extractAttributes($request)) 52 | || !$attributes['receive'] 53 | || !$operation 54 | || !$operation->getJsonStream() 55 | || $this->format !== $request->attributes->get('input_format') 56 | ) { 57 | return; 58 | } 59 | 60 | $data = $this->jsonStreamerProvider->provide($operation, $request->attributes->get('_api_uri_variables') ?? [], [ 61 | 'request' => $request, 62 | 'uri_variables' => $request->attributes->get('_api_uri_variables') ?? [], 63 | 'resource_class' => $operation->getClass(), 64 | ]); 65 | 66 | $request->attributes->set('data', $data); 67 | } 68 | } 69 | -------------------------------------------------------------------------------- /Validator/Metadata/Property/Restriction/PropertySchemaGreaterThanRestriction.php: -------------------------------------------------------------------------------- 1 | 7 | * 8 | * For the full copyright and license information, please view the LICENSE 9 | * file that was distributed with this source code. 10 | */ 11 | 12 | declare(strict_types=1); 13 | 14 | namespace ApiPlatform\Symfony\Validator\Metadata\Property\Restriction; 15 | 16 | use ApiPlatform\Metadata\ApiProperty; 17 | use Symfony\Component\PropertyInfo\PropertyInfoExtractor; 18 | use Symfony\Component\PropertyInfo\Type as LegacyType; 19 | use Symfony\Component\TypeInfo\Type; 20 | use Symfony\Component\TypeInfo\TypeIdentifier; 21 | use Symfony\Component\Validator\Constraint; 22 | use Symfony\Component\Validator\Constraints\GreaterThan; 23 | 24 | /** 25 | * @author Tomas Norkūnas 26 | */ 27 | final class PropertySchemaGreaterThanRestriction implements PropertySchemaRestrictionMetadataInterface 28 | { 29 | /** 30 | * {@inheritdoc} 31 | * 32 | * @param GreaterThan $constraint 33 | */ 34 | public function create(Constraint $constraint, ApiProperty $propertyMetadata): array 35 | { 36 | return [ 37 | 'minimum' => $constraint->value, 38 | 'exclusiveMinimum' => $constraint->value, 39 | ]; 40 | } 41 | 42 | /** 43 | * {@inheritdoc} 44 | */ 45 | public function supports(Constraint $constraint, ApiProperty $propertyMetadata): bool 46 | { 47 | if (!$constraint instanceof GreaterThan || !is_numeric($constraint->value)) { 48 | return false; 49 | } 50 | 51 | if (method_exists(PropertyInfoExtractor::class, 'getType')) { 52 | $type = $propertyMetadata->getExtraProperties()['nested_schema'] ?? false 53 | ? Type::int() 54 | : $propertyMetadata->getNativeType(); 55 | 56 | return $type->isIdentifiedBy(TypeIdentifier::INT, TypeIdentifier::FLOAT); 57 | } 58 | 59 | $types = array_map(fn (LegacyType $type) => $type->getBuiltinType(), $propertyMetadata->getBuiltinTypes() ?? []); 60 | if ($propertyMetadata->getExtraProperties()['nested_schema'] ?? false) { 61 | $types = [LegacyType::BUILTIN_TYPE_INT]; 62 | } 63 | 64 | return \count($types) && array_intersect($types, [LegacyType::BUILTIN_TYPE_INT, LegacyType::BUILTIN_TYPE_FLOAT]); 65 | } 66 | } 67 | -------------------------------------------------------------------------------- /Validator/Metadata/Property/Restriction/PropertySchemaLengthRestriction.php: -------------------------------------------------------------------------------- 1 | 7 | * 8 | * For the full copyright and license information, please view the LICENSE 9 | * file that was distributed with this source code. 10 | */ 11 | 12 | declare(strict_types=1); 13 | 14 | namespace ApiPlatform\Symfony\Validator\Metadata\Property\Restriction; 15 | 16 | use ApiPlatform\Metadata\ApiProperty; 17 | use Symfony\Component\PropertyInfo\PropertyInfoExtractor; 18 | use Symfony\Component\PropertyInfo\Type as LegacyType; 19 | use Symfony\Component\TypeInfo\Type; 20 | use Symfony\Component\TypeInfo\TypeIdentifier; 21 | use Symfony\Component\Validator\Constraint; 22 | use Symfony\Component\Validator\Constraints\Length; 23 | 24 | /** 25 | * Class PropertySchemaLengthRestrictions. 26 | * 27 | * @author Andrii Penchuk penja7@gmail.com 28 | */ 29 | class PropertySchemaLengthRestriction implements PropertySchemaRestrictionMetadataInterface 30 | { 31 | /** 32 | * {@inheritdoc} 33 | * 34 | * @param Length $constraint 35 | */ 36 | public function create(Constraint $constraint, ApiProperty $propertyMetadata): array 37 | { 38 | $restriction = []; 39 | 40 | if (isset($constraint->min)) { 41 | $restriction['minLength'] = (int) $constraint->min; 42 | } 43 | 44 | if (isset($constraint->max)) { 45 | $restriction['maxLength'] = (int) $constraint->max; 46 | } 47 | 48 | return $restriction; 49 | } 50 | 51 | /** 52 | * {@inheritdoc} 53 | */ 54 | public function supports(Constraint $constraint, ApiProperty $propertyMetadata): bool 55 | { 56 | if (method_exists(PropertyInfoExtractor::class, 'getType')) { 57 | $type = $propertyMetadata->getExtraProperties()['nested_schema'] ?? false ? Type::string() : $propertyMetadata->getNativeType(); 58 | 59 | return $constraint instanceof Length && $type?->isIdentifiedBy(TypeIdentifier::STRING); 60 | } 61 | 62 | $types = array_map(fn (LegacyType $type) => $type->getBuiltinType(), $propertyMetadata->getBuiltinTypes() ?? []); 63 | if ($propertyMetadata->getExtraProperties()['nested_schema'] ?? false) { 64 | $types = [LegacyType::BUILTIN_TYPE_STRING]; 65 | } 66 | 67 | return $constraint instanceof Length && \count($types) && \in_array(LegacyType::BUILTIN_TYPE_STRING, $types, true); 68 | } 69 | } 70 | -------------------------------------------------------------------------------- /EventListener/JsonStreamerSerializeListener.php: -------------------------------------------------------------------------------- 1 | 7 | * 8 | * For the full copyright and license information, please view the LICENSE 9 | * file that was distributed with this source code. 10 | */ 11 | 12 | declare(strict_types=1); 13 | 14 | namespace ApiPlatform\Symfony\EventListener; 15 | 16 | use ApiPlatform\Metadata\Resource\Factory\ResourceMetadataCollectionFactoryInterface; 17 | use ApiPlatform\State\ProcessorInterface; 18 | use ApiPlatform\State\Util\OperationRequestInitiatorTrait; 19 | use ApiPlatform\State\Util\RequestAttributesExtractor; 20 | use Symfony\Component\HttpFoundation\Response; 21 | use Symfony\Component\HttpKernel\Event\ViewEvent; 22 | 23 | /** 24 | * Serializes data using JSON Streamer. 25 | * 26 | * @author Kévin Dunglas 27 | */ 28 | final class JsonStreamerSerializeListener 29 | { 30 | use OperationRequestInitiatorTrait; 31 | 32 | /** 33 | * @param ProcessorInterface $jsonStreamerProcessor 34 | */ 35 | public function __construct(private readonly ProcessorInterface $jsonStreamerProcessor, private readonly string $format, ?ResourceMetadataCollectionFactoryInterface $resourceMetadataFactory = null) 36 | { 37 | $this->resourceMetadataCollectionFactory = $resourceMetadataFactory; 38 | } 39 | 40 | /** 41 | * Creates a Response to send to the client according to the requested format. 42 | */ 43 | public function onKernelView(ViewEvent $event): void 44 | { 45 | $request = $event->getRequest(); 46 | $operation = $this->initializeOperation($request); 47 | 48 | $attributes = RequestAttributesExtractor::extractAttributes($request); 49 | if (!($attributes['respond'] ?? $request->attributes->getBoolean('_api_respond')) || !$operation || !$operation->getJsonStream() || $this->format !== $request->getRequestFormat()) { 50 | return; 51 | } 52 | 53 | $uriVariables = $request->attributes->get('_api_uri_variables') ?? []; 54 | $response = $this->jsonStreamerProcessor->process($event->getControllerResult(), $operation, $uriVariables, [ 55 | 'request' => $request, 56 | 'uri_variables' => $uriVariables, 57 | 'resource_class' => $operation->getClass(), 58 | 'original_data' => $request->attributes->get('original_data'), 59 | ]); 60 | 61 | $event->setResponse($response); 62 | } 63 | } 64 | -------------------------------------------------------------------------------- /Action/EntrypointAction.php: -------------------------------------------------------------------------------- 1 | 7 | * 8 | * For the full copyright and license information, please view the LICENSE 9 | * file that was distributed with this source code. 10 | */ 11 | 12 | declare(strict_types=1); 13 | 14 | namespace ApiPlatform\Symfony\Action; 15 | 16 | use ApiPlatform\Documentation\Entrypoint; 17 | use ApiPlatform\Metadata\Get; 18 | use ApiPlatform\Metadata\Resource\Factory\ResourceNameCollectionFactoryInterface; 19 | use ApiPlatform\Metadata\Resource\ResourceNameCollection; 20 | use ApiPlatform\OpenApi\Serializer\LegacyOpenApiNormalizer; 21 | use ApiPlatform\State\ProcessorInterface; 22 | use ApiPlatform\State\ProviderInterface; 23 | use Symfony\Component\HttpFoundation\Request; 24 | 25 | /** 26 | * Generates the API entrypoint. 27 | * 28 | * @author Kévin Dunglas 29 | */ 30 | final class EntrypointAction 31 | { 32 | private static ResourceNameCollection $resourceNameCollection; 33 | 34 | public function __construct( 35 | private readonly ResourceNameCollectionFactoryInterface $resourceNameCollectionFactory, 36 | private readonly ProviderInterface $provider, 37 | private readonly ProcessorInterface $processor, 38 | private readonly array $documentationFormats = [], 39 | ) { 40 | } 41 | 42 | public function __invoke(Request $request): mixed 43 | { 44 | static::$resourceNameCollection = $this->resourceNameCollectionFactory->create(); 45 | $context = [ 46 | 'request' => $request, 47 | 'spec_version' => (string) $request->query->get(LegacyOpenApiNormalizer::SPEC_VERSION), 48 | ]; 49 | $request->attributes->set('_api_platform_disable_listeners', true); 50 | $operation = new Get( 51 | outputFormats: $this->documentationFormats, 52 | read: true, 53 | serialize: true, 54 | class: Entrypoint::class, 55 | provider: [self::class, 'provide'] 56 | ); 57 | $request->attributes->set('_api_operation', $operation); 58 | $body = $this->provider->provide($operation, [], $context); 59 | $operation = $request->attributes->get('_api_operation'); 60 | 61 | return $this->processor->process($body, $operation, [], $context); 62 | } 63 | 64 | public static function provide(): Entrypoint 65 | { 66 | return new Entrypoint(static::$resourceNameCollection); 67 | } 68 | } 69 | -------------------------------------------------------------------------------- /Bundle/Resources/config/json_schema.php: -------------------------------------------------------------------------------- 1 | 7 | * 8 | * For the full copyright and license information, please view the LICENSE 9 | * file that was distributed with this source code. 10 | */ 11 | 12 | declare(strict_types=1); 13 | 14 | namespace Symfony\Component\DependencyInjection\Loader\Configurator; 15 | 16 | return function (ContainerConfigurator $container) { 17 | $services = $container->services(); 18 | 19 | $services->set('api_platform.json_schema.schema_factory', 'ApiPlatform\JsonSchema\SchemaFactory') 20 | ->args([ 21 | service('api_platform.metadata.resource.metadata_collection_factory'), 22 | service('api_platform.metadata.property.name_collection_factory'), 23 | service('api_platform.metadata.property.metadata_factory'), 24 | service('api_platform.name_converter')->ignoreOnInvalid(), 25 | service('api_platform.resource_class_resolver'), 26 | [], 27 | service('api_platform.json_schema.definition_name_factory')->ignoreOnInvalid(), 28 | ]); 29 | 30 | $services->alias('ApiPlatform\JsonSchema\SchemaFactoryInterface', 'api_platform.json_schema.schema_factory'); 31 | 32 | $services->set('api_platform.json_schema.json_schema_generate_command', 'ApiPlatform\JsonSchema\Command\JsonSchemaGenerateCommand') 33 | ->args([ 34 | service('api_platform.json_schema.schema_factory'), 35 | '%api_platform.formats%', 36 | ]) 37 | ->tag('console.command'); 38 | 39 | $services->set('api_platform.json_schema.metadata.property.metadata_factory.schema', 'ApiPlatform\JsonSchema\Metadata\Property\Factory\SchemaPropertyMetadataFactory') 40 | ->decorate('api_platform.metadata.property.metadata_factory', null, 10) 41 | ->args([ 42 | service('api_platform.resource_class_resolver'), 43 | service('api_platform.json_schema.metadata.property.metadata_factory.schema.inner'), 44 | ]); 45 | 46 | $services->set('api_platform.json_schema.backward_compatible_schema_factory', 'ApiPlatform\JsonSchema\BackwardCompatibleSchemaFactory') 47 | ->decorate('api_platform.json_schema.schema_factory', null, -2) 48 | ->args([service('api_platform.json_schema.backward_compatible_schema_factory.inner')]); 49 | 50 | $services->set('api_platform.json_schema.definition_name_factory', 'ApiPlatform\JsonSchema\DefinitionNameFactory'); 51 | }; 52 | -------------------------------------------------------------------------------- /Validator/Metadata/Property/Restriction/PropertySchemaFormat.php: -------------------------------------------------------------------------------- 1 | 7 | * 8 | * For the full copyright and license information, please view the LICENSE 9 | * file that was distributed with this source code. 10 | */ 11 | 12 | declare(strict_types=1); 13 | 14 | namespace ApiPlatform\Symfony\Validator\Metadata\Property\Restriction; 15 | 16 | use ApiPlatform\Metadata\ApiProperty; 17 | use Symfony\Component\Validator\Constraint; 18 | use Symfony\Component\Validator\Constraints\Email; 19 | use Symfony\Component\Validator\Constraints\Hostname; 20 | use Symfony\Component\Validator\Constraints\Ip; 21 | use Symfony\Component\Validator\Constraints\Ulid; 22 | use Symfony\Component\Validator\Constraints\Url; 23 | use Symfony\Component\Validator\Constraints\Uuid; 24 | 25 | /** 26 | * Class PropertySchemaFormat. 27 | * 28 | * @author Andrii Penchuk penja7@gmail.com 29 | */ 30 | class PropertySchemaFormat implements PropertySchemaRestrictionMetadataInterface 31 | { 32 | /** 33 | * {@inheritdoc} 34 | */ 35 | public function create(Constraint $constraint, ApiProperty $propertyMetadata): array 36 | { 37 | if ($constraint instanceof Email) { 38 | return ['format' => 'email']; 39 | } 40 | 41 | if ($constraint instanceof Url) { 42 | return ['format' => 'uri']; 43 | } 44 | 45 | if ($constraint instanceof Hostname) { 46 | return ['format' => 'hostname']; 47 | } 48 | 49 | if ($constraint instanceof Uuid) { 50 | return ['format' => 'uuid']; 51 | } 52 | 53 | if ($constraint instanceof Ulid) { 54 | return ['format' => 'ulid']; 55 | } 56 | 57 | if ($constraint instanceof Ip) { 58 | if ($constraint->version === $constraint::V4) { 59 | return ['format' => 'ipv4']; 60 | } 61 | 62 | return ['format' => 'ipv6']; 63 | } 64 | 65 | return []; 66 | } 67 | 68 | /** 69 | * {@inheritdoc} 70 | */ 71 | public function supports(Constraint $constraint, ApiProperty $propertyMetadata): bool 72 | { 73 | $schema = $propertyMetadata->getSchema(); 74 | 75 | return empty($schema['format']) && ($constraint instanceof Email || $constraint instanceof Url || $constraint instanceof Hostname || $constraint instanceof Uuid || $constraint instanceof Ulid || $constraint instanceof Ip); 76 | } 77 | } 78 | -------------------------------------------------------------------------------- /EventListener/SerializeListener.php: -------------------------------------------------------------------------------- 1 | 7 | * 8 | * For the full copyright and license information, please view the LICENSE 9 | * file that was distributed with this source code. 10 | */ 11 | 12 | declare(strict_types=1); 13 | 14 | namespace ApiPlatform\Symfony\EventListener; 15 | 16 | use ApiPlatform\Metadata\Error; 17 | use ApiPlatform\Metadata\Resource\Factory\ResourceMetadataCollectionFactoryInterface; 18 | use ApiPlatform\State\ProcessorInterface; 19 | use ApiPlatform\State\Util\OperationRequestInitiatorTrait; 20 | use ApiPlatform\State\Util\RequestAttributesExtractor; 21 | use Symfony\Component\HttpKernel\Event\ViewEvent; 22 | 23 | /** 24 | * Serializes data. 25 | * 26 | * @author Kévin Dunglas 27 | */ 28 | final class SerializeListener 29 | { 30 | use OperationRequestInitiatorTrait; 31 | 32 | /** 33 | * @param ProcessorInterface $processor 34 | */ 35 | public function __construct(private readonly ProcessorInterface $processor, ?ResourceMetadataCollectionFactoryInterface $resourceMetadataFactory = null) 36 | { 37 | $this->resourceMetadataCollectionFactory = $resourceMetadataFactory; 38 | } 39 | 40 | /** 41 | * Serializes the data to the requested format. 42 | */ 43 | public function onKernelView(ViewEvent $event): void 44 | { 45 | $controllerResult = $event->getControllerResult(); 46 | $request = $event->getRequest(); 47 | $operation = $this->initializeOperation($request); 48 | 49 | $attributes = RequestAttributesExtractor::extractAttributes($request); 50 | 51 | if (!($attributes['respond'] ?? $request->attributes->getBoolean('_api_respond', false)) || !$operation) { 52 | return; 53 | } 54 | 55 | if (null === $operation->canSerialize()) { 56 | $operation = $operation->withSerialize(true); 57 | } 58 | 59 | if ($operation instanceof Error) { 60 | // we don't want the FlattenException 61 | $controllerResult = $request->attributes->get('data') ?? $controllerResult; 62 | } 63 | 64 | $uriVariables = $request->attributes->get('_api_uri_variables') ?? []; 65 | $serialized = $this->processor->process($controllerResult, $operation, $uriVariables, [ 66 | 'request' => $request, 67 | 'uri_variables' => $uriVariables, 68 | 'resource_class' => $operation->getClass(), 69 | ]); 70 | 71 | $event->setControllerResult($serialized); 72 | } 73 | } 74 | -------------------------------------------------------------------------------- /Routing/SkolemIriConverter.php: -------------------------------------------------------------------------------- 1 | 7 | * 8 | * For the full copyright and license information, please view the LICENSE 9 | * file that was distributed with this source code. 10 | */ 11 | 12 | declare(strict_types=1); 13 | 14 | namespace ApiPlatform\Symfony\Routing; 15 | 16 | use ApiPlatform\Metadata\Exception\ItemNotFoundException; 17 | use ApiPlatform\Metadata\IriConverterInterface; 18 | use ApiPlatform\Metadata\Operation; 19 | use ApiPlatform\Metadata\UrlGeneratorInterface; 20 | use Symfony\Component\Routing\RouterInterface; 21 | 22 | /** 23 | * {@inheritdoc} 24 | * 25 | * @author Antoine Bluchet 26 | */ 27 | final class SkolemIriConverter implements IriConverterInterface 28 | { 29 | public static string $skolemUriTemplate = '/.well-known/genid/{id}'; 30 | 31 | private \SplObjectStorage $objectHashMap; 32 | private array $classHashMap = []; 33 | private RouterInterface $router; 34 | 35 | public function __construct(RouterInterface $router) 36 | { 37 | $this->router = $router; 38 | $this->objectHashMap = new \SplObjectStorage(); 39 | } 40 | 41 | /** 42 | * {@inheritdoc} 43 | */ 44 | public function getResourceFromIri(string $iri, array $context = [], ?Operation $operation = null): object 45 | { 46 | throw new ItemNotFoundException(\sprintf('Item not found for "%s".', $iri)); 47 | } 48 | 49 | /** 50 | * {@inheritdoc} 51 | */ 52 | public function getIriFromResource(object|string $resource, int $referenceType = UrlGeneratorInterface::ABS_PATH, ?Operation $operation = null, array $context = []): string 53 | { 54 | $referenceType = $operation ? ($operation->getUrlGenerationStrategy() ?? $referenceType) : $referenceType; 55 | if (($isObject = \is_object($resource)) && $this->objectHashMap->offsetExists($resource)) { 56 | return $this->router->generate('api_genid', ['id' => $this->objectHashMap[$resource]], $referenceType); 57 | } 58 | 59 | if (\is_string($resource) && isset($this->classHashMap[$resource])) { 60 | return $this->router->generate('api_genid', ['id' => $this->classHashMap[$resource]], $referenceType); 61 | } 62 | 63 | $id = bin2hex(random_bytes(10)); 64 | 65 | if ($isObject) { 66 | $this->objectHashMap[$resource] = $id; 67 | } else { 68 | $this->classHashMap[$resource] = $id; 69 | } 70 | 71 | return $this->router->generate('api_genid', ['id' => $id], $referenceType); 72 | } 73 | } 74 | -------------------------------------------------------------------------------- /Validator/Metadata/Property/Restriction/PropertySchemaRangeRestriction.php: -------------------------------------------------------------------------------- 1 | 7 | * 8 | * For the full copyright and license information, please view the LICENSE 9 | * file that was distributed with this source code. 10 | */ 11 | 12 | declare(strict_types=1); 13 | 14 | namespace ApiPlatform\Symfony\Validator\Metadata\Property\Restriction; 15 | 16 | use ApiPlatform\Metadata\ApiProperty; 17 | use Symfony\Component\PropertyInfo\PropertyInfoExtractor; 18 | use Symfony\Component\PropertyInfo\Type as LegacyType; 19 | use Symfony\Component\TypeInfo\Type; 20 | use Symfony\Component\TypeInfo\TypeIdentifier; 21 | use Symfony\Component\Validator\Constraint; 22 | use Symfony\Component\Validator\Constraints\Range; 23 | 24 | /** 25 | * @author Tomas Norkūnas 26 | */ 27 | final class PropertySchemaRangeRestriction implements PropertySchemaRestrictionMetadataInterface 28 | { 29 | /** 30 | * {@inheritdoc} 31 | * 32 | * @param Range $constraint 33 | */ 34 | public function create(Constraint $constraint, ApiProperty $propertyMetadata): array 35 | { 36 | $restriction = []; 37 | 38 | if (isset($constraint->min) && is_numeric($constraint->min)) { 39 | $restriction['minimum'] = $constraint->min; 40 | } 41 | 42 | if (isset($constraint->max) && is_numeric($constraint->max)) { 43 | $restriction['maximum'] = $constraint->max; 44 | } 45 | 46 | return $restriction; 47 | } 48 | 49 | /** 50 | * {@inheritdoc} 51 | */ 52 | public function supports(Constraint $constraint, ApiProperty $propertyMetadata): bool 53 | { 54 | if (!$constraint instanceof Range) { 55 | return false; 56 | } 57 | 58 | if (method_exists(PropertyInfoExtractor::class, 'getType')) { 59 | $type = $propertyMetadata->getExtraProperties()['nested_schema'] ?? false 60 | ? Type::int() 61 | : $propertyMetadata->getNativeType(); 62 | 63 | return $type->isIdentifiedBy(TypeIdentifier::INT, TypeIdentifier::FLOAT); 64 | } 65 | 66 | $types = array_map(fn (LegacyType $type) => $type->getBuiltinType(), $propertyMetadata->getBuiltinTypes() ?? []); 67 | if ($propertyMetadata->getExtraProperties()['nested_schema'] ?? false) { 68 | $types = [LegacyType::BUILTIN_TYPE_INT]; 69 | } 70 | 71 | return \count($types) > 0 && \count(array_intersect($types, [LegacyType::BUILTIN_TYPE_INT, LegacyType::BUILTIN_TYPE_FLOAT])) > 0; 72 | } 73 | } 74 | -------------------------------------------------------------------------------- /Messenger/Metadata/MessengerResourceMetadataCollectionFactory.php: -------------------------------------------------------------------------------- 1 | 7 | * 8 | * For the full copyright and license information, please view the LICENSE 9 | * file that was distributed with this source code. 10 | */ 11 | 12 | declare(strict_types=1); 13 | 14 | namespace ApiPlatform\Symfony\Messenger\Metadata; 15 | 16 | use ApiPlatform\Metadata\Resource\Factory\ResourceMetadataCollectionFactoryInterface; 17 | use ApiPlatform\Metadata\Resource\ResourceMetadataCollection; 18 | use ApiPlatform\Symfony\Messenger\Processor; 19 | 20 | final class MessengerResourceMetadataCollectionFactory implements ResourceMetadataCollectionFactoryInterface 21 | { 22 | public function __construct(private readonly ResourceMetadataCollectionFactoryInterface $decorated) 23 | { 24 | } 25 | 26 | /** 27 | * {@inheritdoc} 28 | */ 29 | public function create(string $resourceClass): ResourceMetadataCollection 30 | { 31 | $resourceMetadataCollection = $this->decorated->create($resourceClass); 32 | 33 | foreach ($resourceMetadataCollection as $i => $resourceMetadata) { 34 | $operations = $resourceMetadata->getOperations(); 35 | 36 | if ($operations) { 37 | foreach ($resourceMetadata->getOperations() as $operationName => $operation) { 38 | if (null !== $operation->getProcessor() || false === ($operation->getMessenger() ?? false)) { 39 | continue; 40 | } 41 | 42 | $operations->add($operationName, $operation->withProcessor(Processor::class)); 43 | } 44 | 45 | $resourceMetadata = $resourceMetadata->withOperations($operations); 46 | } 47 | 48 | $graphQlOperations = $resourceMetadata->getGraphQlOperations(); 49 | 50 | if ($graphQlOperations) { 51 | foreach ($graphQlOperations as $operationName => $graphQlOperation) { 52 | if (null !== $graphQlOperation->getProcessor() || false === ($graphQlOperation->getMessenger() ?? false)) { 53 | continue; 54 | } 55 | 56 | $graphQlOperations[$operationName] = $graphQlOperation->withProcessor(Processor::class); 57 | } 58 | 59 | $resourceMetadata = $resourceMetadata->withGraphQlOperations($graphQlOperations); 60 | } 61 | 62 | $resourceMetadataCollection[$i] = $resourceMetadata; 63 | } 64 | 65 | return $resourceMetadataCollection; 66 | } 67 | } 68 | -------------------------------------------------------------------------------- /Bundle/DependencyInjection/Compiler/ElasticsearchClientPass.php: -------------------------------------------------------------------------------- 1 | 7 | * 8 | * For the full copyright and license information, please view the LICENSE 9 | * file that was distributed with this source code. 10 | */ 11 | 12 | declare(strict_types=1); 13 | 14 | namespace ApiPlatform\Symfony\Bundle\DependencyInjection\Compiler; 15 | 16 | use Symfony\Component\DependencyInjection\Compiler\CompilerPassInterface; 17 | use Symfony\Component\DependencyInjection\ContainerBuilder; 18 | use Symfony\Component\DependencyInjection\Reference; 19 | 20 | /** 21 | * Creates the Elasticsearch client. 22 | * 23 | * @author Baptiste Meyer 24 | */ 25 | final class ElasticsearchClientPass implements CompilerPassInterface 26 | { 27 | /** 28 | * {@inheritdoc} 29 | */ 30 | public function process(ContainerBuilder $container): void 31 | { 32 | if (!$container->getParameter('api_platform.elasticsearch.enabled')) { 33 | return; 34 | } 35 | 36 | $clientConfiguration = []; 37 | 38 | if ($hosts = $container->getParameter('api_platform.elasticsearch.hosts')) { 39 | $clientConfiguration['hosts'] = $hosts; 40 | } 41 | 42 | if (class_exists(\Elasticsearch\ClientBuilder::class)) { 43 | // ES v7 44 | $builderName = \Elasticsearch\ClientBuilder::class; 45 | } else { 46 | // ES v8 and up 47 | $builderName = \Elastic\Elasticsearch\ClientBuilder::class; 48 | } 49 | 50 | if ($container->has('logger')) { 51 | $clientConfiguration['logger'] = new Reference('logger'); 52 | 53 | // @phpstan-ignore-next-line 54 | if (\Elasticsearch\ClientBuilder::class === $builderName) { 55 | $clientConfiguration['tracer'] = new Reference('logger'); 56 | } 57 | } 58 | 59 | if ($sslCaBundle = $container->getParameter('api_platform.elasticsearch.ssl_ca_bundle')) { 60 | $clientConfiguration['CABundle'] = $sslCaBundle; 61 | } 62 | 63 | if (false === $container->getParameter('api_platform.elasticsearch.ssl_verification')) { 64 | $clientConfiguration['SSLVerification'] = false; 65 | } 66 | 67 | $clientDefinition = $container->getDefinition('api_platform.elasticsearch.client'); 68 | 69 | if (!$clientConfiguration) { 70 | $clientDefinition->setFactory([$builderName, 'build']); 71 | } else { 72 | $clientDefinition->setFactory([$builderName, 'fromConfig']); 73 | $clientDefinition->setArguments([$clientConfiguration]); 74 | } 75 | } 76 | } 77 | -------------------------------------------------------------------------------- /Bundle/Resources/public/init-graphiql.js: -------------------------------------------------------------------------------- 1 | var initParameters = {}; 2 | var entrypoint = null; 3 | 4 | function onEditQuery(newQuery) { 5 | initParameters.query = newQuery; 6 | updateURL(); 7 | } 8 | 9 | function onEditVariables(newVariables) { 10 | initParameters.variables = newVariables; 11 | updateURL(); 12 | } 13 | 14 | function onEditOperationName(newOperationName) { 15 | initParameters.operationName = newOperationName; 16 | updateURL(); 17 | } 18 | 19 | function updateURL() { 20 | var newSearch = '?' + Object.keys(initParameters).filter(function (key) { 21 | return Boolean(initParameters[key]); 22 | }).map(function (key) { 23 | return encodeURIComponent(key) + '=' + encodeURIComponent(initParameters[key]); 24 | }).join('&'); 25 | history.replaceState(null, null, newSearch); 26 | } 27 | 28 | function graphQLFetcher(graphQLParams, {headers}) { 29 | return fetch(entrypoint, { 30 | method: 'post', 31 | headers: { 32 | 'Accept': 'application/json', 33 | 'Content-Type': 'application/json', 34 | ...headers 35 | }, 36 | body: JSON.stringify(graphQLParams), 37 | credentials: 'include' 38 | }).then(function (response) { 39 | return response.text(); 40 | }).then(function (responseBody) { 41 | try { 42 | return JSON.parse(responseBody); 43 | } catch (error) { 44 | return responseBody; 45 | } 46 | }); 47 | } 48 | 49 | window.onload = function() { 50 | var data = JSON.parse(document.getElementById('graphiql-data').innerText); 51 | entrypoint = data.entrypoint; 52 | 53 | var search = window.location.search; 54 | search.substr(1).split('&').forEach(function (entry) { 55 | var eq = entry.indexOf('='); 56 | if (eq >= 0) { 57 | initParameters[decodeURIComponent(entry.slice(0, eq))] = decodeURIComponent(entry.slice(eq + 1)); 58 | } 59 | }); 60 | 61 | if (initParameters.variables) { 62 | try { 63 | initParameters.variables = JSON.stringify(JSON.parse(initParameters.variables), null, 2); 64 | } catch (e) { 65 | // Do nothing, we want to display the invalid JSON as a string, rather than present an error. 66 | } 67 | } 68 | 69 | ReactDOM.render( 70 | React.createElement(GraphiQL, { 71 | fetcher: graphQLFetcher, 72 | query: initParameters.query, 73 | variables: initParameters.variables, 74 | operationName: initParameters.operationName, 75 | onEditQuery: onEditQuery, 76 | onEditVariables: onEditVariables, 77 | onEditOperationName: onEditOperationName 78 | }), 79 | document.getElementById('graphiql') 80 | ); 81 | } 82 | -------------------------------------------------------------------------------- /Bundle/Command/GraphQlExportCommand.php: -------------------------------------------------------------------------------- 1 | 7 | * 8 | * For the full copyright and license information, please view the LICENSE 9 | * file that was distributed with this source code. 10 | */ 11 | 12 | declare(strict_types=1); 13 | 14 | namespace ApiPlatform\Symfony\Bundle\Command; 15 | 16 | use ApiPlatform\GraphQl\Type\SchemaBuilderInterface; 17 | use GraphQL\Utils\SchemaPrinter; 18 | use Symfony\Component\Console\Command\Command; 19 | use Symfony\Component\Console\Input\InputInterface; 20 | use Symfony\Component\Console\Input\InputOption; 21 | use Symfony\Component\Console\Output\OutputInterface; 22 | use Symfony\Component\Console\Style\SymfonyStyle; 23 | 24 | /** 25 | * Export the GraphQL schema in Schema Definition Language (SDL). 26 | * 27 | * @author Alan Poulain 28 | */ 29 | class GraphQlExportCommand extends Command 30 | { 31 | public function __construct(private readonly SchemaBuilderInterface $schemaBuilder) 32 | { 33 | parent::__construct(); 34 | } 35 | 36 | /** 37 | * {@inheritdoc} 38 | */ 39 | protected function configure(): void 40 | { 41 | $this 42 | ->setDescription('Export the GraphQL schema in Schema Definition Language (SDL)') 43 | ->addOption('comment-descriptions', null, InputOption::VALUE_NONE, 'Use preceding comments as the description (deprecated: graphql-php < 15)') 44 | ->addOption('sort-types', null, InputOption::VALUE_NONE, 'Order types alphabetically') 45 | ->addOption('output', 'o', InputOption::VALUE_REQUIRED, 'Write output to file'); 46 | } 47 | 48 | /** 49 | * {@inheritdoc} 50 | */ 51 | protected function execute(InputInterface $input, OutputInterface $output): int 52 | { 53 | $io = new SymfonyStyle($input, $output); 54 | 55 | $options = []; 56 | 57 | // Removed in graphql-php 15 58 | if ($input->getOption('comment-descriptions')) { 59 | $options['commentDescriptions'] = true; 60 | } 61 | if ($input->getOption('sort-types')) { 62 | $options['sortTypes'] = true; 63 | } 64 | 65 | $schemaExport = SchemaPrinter::doPrint($this->schemaBuilder->getSchema(), $options); 66 | 67 | $filename = $input->getOption('output'); 68 | if (\is_string($filename)) { 69 | file_put_contents($filename, $schemaExport); 70 | $io->success(\sprintf('Data written to %s.', $filename)); 71 | } else { 72 | $output->writeln($schemaExport); 73 | } 74 | 75 | return \defined(Command::class.'::SUCCESS') ? Command::SUCCESS : 0; 76 | } 77 | 78 | public static function getDefaultName(): string 79 | { 80 | return 'api:graphql:export'; 81 | } 82 | } 83 | -------------------------------------------------------------------------------- /Bundle/Test/Constraint/ArraySubsetTrait.php: -------------------------------------------------------------------------------- 1 | 7 | * 8 | * For the full copyright and license information, please view the LICENSE 9 | * file that was distributed with this source code. 10 | */ 11 | 12 | declare(strict_types=1); 13 | 14 | namespace ApiPlatform\Symfony\Bundle\Test\Constraint; 15 | 16 | use SebastianBergmann\Comparator\ComparisonFailure; 17 | use SebastianBergmann\Exporter\Exporter; 18 | 19 | /** 20 | * Constraint that asserts that the array it is evaluated for has a specified subset. 21 | * 22 | * Uses array_replace_recursive() to check if a key value subset is part of the 23 | * subject array. 24 | * 25 | * Imported from dms/phpunit-arraysubset-asserts, because the original constraint has been deprecated. 26 | * 27 | * @copyright Sebastian Bergmann 28 | * @copyright Rafael Dohms 29 | * 30 | * @see https://github.com/sebastianbergmann/phpunit/issues/3494 31 | */ 32 | trait ArraySubsetTrait 33 | { 34 | public function __construct(private iterable $subset, private readonly bool $strict = false) 35 | { 36 | } 37 | 38 | private function _evaluate($other, string $description = '', bool $returnResult = false): ?bool 39 | { 40 | // type cast $other & $this->subset as an array to allow 41 | // support in standard array functions. 42 | $other = $this->toArray($other); 43 | $this->subset = $this->toArray($this->subset); 44 | $patched = array_replace_recursive($other, $this->subset); 45 | if ($this->strict) { 46 | $result = $other === $patched; 47 | } else { 48 | $result = $other == $patched; 49 | } 50 | if ($returnResult) { 51 | return $result; 52 | } 53 | if ($result) { 54 | return null; 55 | } 56 | 57 | $f = new ComparisonFailure( 58 | $patched, 59 | $other, 60 | var_export($patched, true), 61 | var_export($other, true) 62 | ); 63 | $this->fail($other, $description, $f); 64 | } 65 | 66 | /** 67 | * {@inheritdoc} 68 | */ 69 | public function toString(): string 70 | { 71 | return 'has the subset '.(new Exporter())->export($this->subset); 72 | } 73 | 74 | /** 75 | * {@inheritdoc} 76 | */ 77 | protected function failureDescription($other): string 78 | { 79 | return 'an array '.$this->toString(); 80 | } 81 | 82 | private function toArray(iterable $other): array 83 | { 84 | if (\is_array($other)) { 85 | return $other; 86 | } 87 | if ($other instanceof \ArrayObject) { 88 | return $other->getArrayCopy(); 89 | } 90 | 91 | return iterator_to_array($other); 92 | } 93 | } 94 | --------------------------------------------------------------------------------