From ddf11ad4f8ab5c7b6fefd0e0f2e61abf2d73d7c1 Mon Sep 17 00:00:00 2001 From: Perreal Guillaume <guillaume.perreal@irstea.fr> Date: Thu, 23 May 2019 15:34:09 +0200 Subject: [PATCH] =?UTF-8?q?Mise=20=C3=A0=20jour=20de=20l'ensemble=20pour?= =?UTF-8?q?=20produire=20du=20json-schema=20de=20fa=C3=A7on=20coh=C3=A9ren?= =?UTF-8?q?te.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- phpstan.neon | 6 + .../Bundle/Controller/AbstractController.php | 15 +- .../Bundle/Controller/OperationController.php | 40 ++++-- .../Bundle/Controller/ResourceController.php | 14 +- .../IrsteaApiMetadataExtension.php | 2 +- .../Bundle/Resources/config/routing.xml | 4 +- .../Bundle/Resources/config/services.xml | 14 +- .../URI/OperationMetadataURIGenerator.php | 22 +-- .../URI/ResourceMetadataURIGenerator.php | 14 +- .../Serializer/ObjectMetadataNormalizer.php | 15 +- .../Serializer/TypeMetadataNormalizer.php | 5 +- src/Exception/InvalidArgumentException.php | 28 ++++ .../Operation/NullOperationFactory.php | 8 +- .../Operation/OperationFactoryInterface.php | 17 ++- .../Operation/ResourceOperationFactory.php | 67 +++++---- .../Property/ResourcePropertyFactory.php | 2 - src/Factory/Type/CollectionTypeFactory.php | 3 +- src/Factory/Type/ObjectTypeFactory.php | 1 - src/Factory/Type/ResourceTypeFactory.php | 11 +- src/Model/Identity/OperationIdentity.php | 130 ++++++++++++++++++ .../Identity/OperationIdentityInterface.php | 47 +++++++ src/Model/Identity/ResourceIdentity.php | 103 ++++++++++++++ .../Identity/ResourceIdentityInterface.php | 37 +++++ src/Model/MapMetadata.php | 1 - src/Model/OperationMetadata.php | 58 +++----- src/Model/ResourceMetadata.php | 39 +++--- src/Service/ResourceMap.php | 51 +++++-- src/Service/ResourceMapInterface.php | 19 ++- src/URI/CompositeURIGenerator.php | 4 +- src/URI/URIGeneratorInterface.php | 4 +- tests/Fixtures/Entity/Address.php | 16 --- tests/FunctionalTest.php | 4 +- tests/Service/ResourceMapTest.php | 27 ++-- tests/TestRemoteRefProvider.php | 4 +- 34 files changed, 608 insertions(+), 224 deletions(-) create mode 100644 src/Exception/InvalidArgumentException.php create mode 100644 src/Model/Identity/OperationIdentity.php create mode 100644 src/Model/Identity/OperationIdentityInterface.php create mode 100644 src/Model/Identity/ResourceIdentity.php create mode 100644 src/Model/Identity/ResourceIdentityInterface.php diff --git a/phpstan.neon b/phpstan.neon index d25b5cc..0e7d34b 100644 --- a/phpstan.neon +++ b/phpstan.neon @@ -2,4 +2,10 @@ includes: - vendor/irstea/phpstan-config/strict.neon - vendor/irstea/phpstan-config/phpunit.neon +parameters: + level: 7 + + paths: + - src + - tests diff --git a/src/Bridge/Symfony/Bundle/Controller/AbstractController.php b/src/Bridge/Symfony/Bundle/Controller/AbstractController.php index 99dc02f..60b6d43 100644 --- a/src/Bridge/Symfony/Bundle/Controller/AbstractController.php +++ b/src/Bridge/Symfony/Bundle/Controller/AbstractController.php @@ -22,6 +22,7 @@ namespace Irstea\ApiMetadata\Bridge\Symfony\Bundle\Controller; use Irstea\ApiMetadata\Factory\Type\TypeFactoryInterface; use Irstea\ApiMetadata\Service\ResourceMapInterface; +use Irstea\ApiMetadata\URI\URIGeneratorInterface; use Symfony\Component\Serializer\SerializerInterface; /** @@ -42,20 +43,26 @@ abstract class AbstractController /** @var SerializerInterface */ protected $serializer; + /** @var URIGeneratorInterface */ + protected $uriGenerator; + /** * ResourceListAction constructor. * - * @param ResourceMapInterface $resourceMap - * @param TypeFactoryInterface $typeFactory - * @param SerializerInterface $serializer + * @param ResourceMapInterface $resourceMap + * @param TypeFactoryInterface $typeFactory + * @param SerializerInterface $serializer + * @param URIGeneratorInterface $uriGenerator */ public function __construct( ResourceMapInterface $resourceMap, TypeFactoryInterface $typeFactory, - SerializerInterface $serializer + SerializerInterface $serializer, + URIGeneratorInterface $uriGenerator ) { $this->resourceMap = $resourceMap; $this->typeFactory = $typeFactory; $this->serializer = $serializer; + $this->uriGenerator = $uriGenerator; } } diff --git a/src/Bridge/Symfony/Bundle/Controller/OperationController.php b/src/Bridge/Symfony/Bundle/Controller/OperationController.php index d6d2f5a..9606431 100644 --- a/src/Bridge/Symfony/Bundle/Controller/OperationController.php +++ b/src/Bridge/Symfony/Bundle/Controller/OperationController.php @@ -20,9 +20,14 @@ namespace Irstea\ApiMetadata\Bridge\Symfony\Bundle\Controller; +use Irstea\ApiMetadata\Factory\Context; use Irstea\ApiMetadata\Factory\Operation\OperationFactoryInterface; use Irstea\ApiMetadata\Factory\Type\TypeFactoryInterface; +use Irstea\ApiMetadata\Model\Identity\OperationIdentity; use Irstea\ApiMetadata\Service\ResourceMapInterface; +use Irstea\ApiMetadata\URI\URIGeneratorInterface; +use Symfony\Component\HttpFoundation\JsonResponse; +use Symfony\Component\HttpFoundation\Request; use Symfony\Component\HttpFoundation\Response; use Symfony\Component\Serializer\SerializerInterface; @@ -42,15 +47,17 @@ class OperationController extends AbstractController * @param ResourceMapInterface $resourceMap * @param TypeFactoryInterface $typeFactory * @param SerializerInterface $serializer + * @param URIGeneratorInterface $uriGenerator * @param OperationFactoryInterface $operationFactory */ public function __construct( ResourceMapInterface $resourceMap, TypeFactoryInterface $typeFactory, SerializerInterface $serializer, + URIGeneratorInterface $uriGenerator, OperationFactoryInterface $operationFactory ) { - parent::__construct($resourceMap, $typeFactory, $serializer); + parent::__construct($resourceMap, $typeFactory, $serializer, $uriGenerator); $this->operationFactory = $operationFactory; } @@ -63,10 +70,21 @@ class OperationController extends AbstractController */ public function item(string $shortName, string $type, string $name): Response { - [$className, $ctx] = $this->setupContext($shortName); - $operation = $this->operationFactory->create($className, $type, $name, $ctx); + $resourceId = $this->resourceMap->getByShortName($shortName); - return $this->createResponse($operation, $ctx); + $operationId = OperationIdentity::fromValues( + $resourceId->getClass(), + $resourceId->getShortName(), + $type, + $name + ); + + $ctx = new Context($this->typeFactory); + $metadata = $this->operationFactory->createOperation($operationId, $ctx); + + $json = $this->serializer->serialize($metadata, 'json', ['root' => $metadata]); + + return JsonResponse::fromJsonString($json); } /** @@ -74,16 +92,18 @@ class OperationController extends AbstractController * * @return Response */ - public function collection(string $shortName): Response + public function collection(Request $request, string $shortName): Response { - [$className, $ctx] = $this->setupContext($shortName); - + $resourceId = $this->resourceMap->getByShortName($shortName); $operations = []; - foreach ($this->operationFactory->enumerate($className, $ctx) as $key => $operation) { - $operations[$key] = $operation; + foreach ($this->operationFactory->enumerateOperations($resourceId) as $operationId) { + $operations[] = ['$ref' => $this->uriGenerator->generateURI($operationId)]; } - return $this->createResponse($operations, $ctx); + return JsonResponse::create([ + '$id' => $request->getUri(), + 'operations' => $operations, + ]); } } diff --git a/src/Bridge/Symfony/Bundle/Controller/ResourceController.php b/src/Bridge/Symfony/Bundle/Controller/ResourceController.php index b4dd24c..a6c808a 100644 --- a/src/Bridge/Symfony/Bundle/Controller/ResourceController.php +++ b/src/Bridge/Symfony/Bundle/Controller/ResourceController.php @@ -20,9 +20,9 @@ namespace Irstea\ApiMetadata\Bridge\Symfony\Bundle\Controller; -use Irstea\ApiMetadata\Exception\ResourceNotFoundException; use Irstea\ApiMetadata\Factory\Context; use Irstea\ApiMetadata\Helper\PropertyInfoType; +use Irstea\ApiMetadata\Model\Identity\ResourceIdentityInterface; use Irstea\ApiMetadata\Model\Reference; use Symfony\Component\HttpFoundation\JsonResponse; use Symfony\Component\HttpFoundation\Request; @@ -40,14 +40,11 @@ class ResourceController extends AbstractController */ public function item(string $shortName): Response { - $className = $this->resourceMap->getClassName($shortName); - if ($className === null) { - throw new ResourceNotFoundException($shortName); - } + $resourceId = $this->resourceMap->getByShortName($shortName); $ctx = new Context($this->typeFactory); - $type = PropertyInfoType::create($className); + $type = PropertyInfoType::create($resourceId->getClass()); $metadata = $ctx->createType($type, $ctx); if ($metadata instanceof Reference) { @@ -67,8 +64,9 @@ class ResourceController extends AbstractController public function collection(Request $request): Response { $resources = []; - foreach ($this->resourceMap as $shortName) { - $resources[$shortName] = ['$ref' => $shortName]; + /** @var ResourceIdentityInterface $resourceId */ + foreach ($this->resourceMap as $resourceId) { + $resources[$resourceId->getShortName()] = ['$ref' => $this->uriGenerator->generateURI($resourceId)]; } return JsonResponse::create([ diff --git a/src/Bridge/Symfony/Bundle/DependencyInjection/IrsteaApiMetadataExtension.php b/src/Bridge/Symfony/Bundle/DependencyInjection/IrsteaApiMetadataExtension.php index c6f6f77..5414724 100644 --- a/src/Bridge/Symfony/Bundle/DependencyInjection/IrsteaApiMetadataExtension.php +++ b/src/Bridge/Symfony/Bundle/DependencyInjection/IrsteaApiMetadataExtension.php @@ -33,7 +33,7 @@ class IrsteaApiMetadataExtension extends Extension /** * {@inheritdoc} */ - public function load(array $configs, ContainerBuilder $container) + public function load(array $configs, ContainerBuilder $container): void { $loader = new XmlFileLoader($container, new FileLocator(__DIR__ . '/../Resources/config')); $loader->load('services.xml'); diff --git a/src/Bridge/Symfony/Bundle/Resources/config/routing.xml b/src/Bridge/Symfony/Bundle/Resources/config/routing.xml index 550fbf5..923c6e9 100644 --- a/src/Bridge/Symfony/Bundle/Resources/config/routing.xml +++ b/src/Bridge/Symfony/Bundle/Resources/config/routing.xml @@ -7,7 +7,7 @@ > <route id="api_metadata_operation_get" path="/metadata/{shortName}/operations/{type}_{name}.{_format}" methods="GET"> - <default key="_controller">api_metadata.operation.resource::item</default> + <default key="_controller">api_metadata.controller.operation::item</default> <default key="_format">json</default> <requirement key="type">(item|collection)</requirement> @@ -15,7 +15,7 @@ </route> <route id="api_metadata_operation_list" path="/metadata/{shortName}/operations.{_format}" methods="GET"> - <default key="_controller">api_metadata.operation.resource::collection</default> + <default key="_controller">api_metadata.controller.operation::collection</default> <default key="_format">json</default> </route> diff --git a/src/Bridge/Symfony/Bundle/Resources/config/services.xml b/src/Bridge/Symfony/Bundle/Resources/config/services.xml index 126bc7d..3154a83 100644 --- a/src/Bridge/Symfony/Bundle/Resources/config/services.xml +++ b/src/Bridge/Symfony/Bundle/Resources/config/services.xml @@ -17,6 +17,7 @@ <argument type="service" id="irstea_api_metadata.service.resource_map"/> <argument type="service" id="irstea_api_metadata.type_factory"/> <argument type="service" id="serializer"/> + <argument type="service" id="irstea_api_metadata.uri_generator"/> </service> <service @@ -27,6 +28,7 @@ <argument type="service" id="irstea_api_metadata.service.resource_map"/> <argument type="service" id="irstea_api_metadata.type_factory"/> <argument type="service" id="serializer"/> + <argument type="service" id="irstea_api_metadata.uri_generator"/> <argument type="service" id="irstea_api_metadata.operation_factory"/> </service> @@ -179,21 +181,21 @@ </service> <service - id="irstea_api_metadata.uri_generator.resource" - class="Irstea\ApiMetadata\Bridge\Symfony\Bundle\URI\ResourceMetadataURIGenerator" + id="irstea_api_metadata.uri_generator.operation" + class="Irstea\ApiMetadata\Bridge\Symfony\Bundle\URI\OperationMetadataURIGenerator" > <argument type="service" id="router"/> - <tag name="irstea_api_metadata.uri_generator" priority="30"></tag> + <tag name="irstea_api_metadata.uri_generator" priorty="30"></tag> </service> <service - id="irstea_api_metadata.uri_generator.operation" - class="Irstea\ApiMetadata\Bridge\Symfony\Bundle\URI\OperationMetadataURIGenerator" + id="irstea_api_metadata.uri_generator.resource" + class="Irstea\ApiMetadata\Bridge\Symfony\Bundle\URI\ResourceMetadataURIGenerator" > <argument type="service" id="router"/> - <tag name="irstea_api_metadata.uri_generator" priorty="20"></tag> + <tag name="irstea_api_metadata.uri_generator" priority="20"></tag> </service> <service diff --git a/src/Bridge/Symfony/Bundle/URI/OperationMetadataURIGenerator.php b/src/Bridge/Symfony/Bundle/URI/OperationMetadataURIGenerator.php index 3402f1f..e6ce839 100644 --- a/src/Bridge/Symfony/Bundle/URI/OperationMetadataURIGenerator.php +++ b/src/Bridge/Symfony/Bundle/URI/OperationMetadataURIGenerator.php @@ -21,7 +21,7 @@ namespace Irstea\ApiMetadata\Bridge\Symfony\Bundle\URI; use Assert\Assertion; -use Irstea\ApiMetadata\Model\OperationMetadata; +use Irstea\ApiMetadata\Model\Identity\OperationIdentityInterface; use Irstea\ApiMetadata\URI\URIGeneratorInterface; use Symfony\Component\Routing\Generator\UrlGeneratorInterface; @@ -44,27 +44,27 @@ class OperationMetadataURIGenerator implements URIGeneratorInterface /** * {@inheritdoc} */ - public function generateURI($resource): string + public function generateURI($operation): string { - Assertion::isInstanceOf($resource, OperationMetadata::class); - /* @var OperationMetadata $resource */ + Assertion::isInstanceOf($operation, OperationIdentityInterface::class); + /* @var OperationIdentityInterface $operation */ return $this->urlGenerator->generate( 'api_metadata_operation_get', [ - 'shortName' => $resource->getResource(), - 'type' => $resource->getType(), - 'name' => $resource->getName(), - ], + 'shortName' => $operation->getShortName(), + 'type' => $operation->getType(), + 'name' => $operation->getName(), + ], UrlGeneratorInterface::ABSOLUTE_URL - ) . '#'; + ) . '#'; } /** * {@inheritdoc} */ - public function supports($resource): bool + public function supports($operation): bool { - return $resource instanceof OperationMetadata; + return $operation instanceof OperationIdentityInterface; } } diff --git a/src/Bridge/Symfony/Bundle/URI/ResourceMetadataURIGenerator.php b/src/Bridge/Symfony/Bundle/URI/ResourceMetadataURIGenerator.php index 878e6e6..b42af0f 100644 --- a/src/Bridge/Symfony/Bundle/URI/ResourceMetadataURIGenerator.php +++ b/src/Bridge/Symfony/Bundle/URI/ResourceMetadataURIGenerator.php @@ -21,7 +21,7 @@ namespace Irstea\ApiMetadata\Bridge\Symfony\Bundle\URI; use Assert\Assertion; -use Irstea\ApiMetadata\Model\ResourceMetadata; +use Irstea\ApiMetadata\Model\Identity\ResourceIdentityInterface; use Irstea\ApiMetadata\URI\URIGeneratorInterface; use Symfony\Component\Routing\Generator\UrlGeneratorInterface; @@ -46,16 +46,14 @@ class ResourceMetadataURIGenerator implements URIGeneratorInterface */ public function generateURI($resource): string { - Assertion::isInstanceOf($resource, ResourceMetadata::class); - /* @var ResourceMetadata $resource */ + Assertion::isInstanceOf($resource, ResourceIdentityInterface::class); + /* @var ResourceIdentityInterface $resource */ return $this->urlGenerator->generate( 'api_metadata_resource_get', - [ - 'shortName' => $resource->getShortName(), - ], + ['shortName' => $resource->getShortName()], UrlGeneratorInterface::ABSOLUTE_URL - ) . '#'; + ) . '#'; } /** @@ -63,6 +61,6 @@ class ResourceMetadataURIGenerator implements URIGeneratorInterface */ public function supports($resource): bool { - return $resource instanceof ResourceMetadata; + return $resource instanceof ResourceIdentityInterface; } } diff --git a/src/Bridge/Symfony/Serializer/ObjectMetadataNormalizer.php b/src/Bridge/Symfony/Serializer/ObjectMetadataNormalizer.php index f8cbb36..f2f331c 100644 --- a/src/Bridge/Symfony/Serializer/ObjectMetadataNormalizer.php +++ b/src/Bridge/Symfony/Serializer/ObjectMetadataNormalizer.php @@ -110,9 +110,7 @@ class ObjectMetadataNormalizer implements NormalizerAwareInterface, NormalizerIn $operations = []; foreach ($root->getOperations() as $operation) { - $operations[$operation->getKey()] = [ - '$ref' => $this->generateURI($operation, $context, true), - ]; + $operations[] = ['$ref' => $this->generateURI($operation, $context, true)]; } return array_merge( @@ -122,14 +120,14 @@ class ObjectMetadataNormalizer implements NormalizerAwareInterface, NormalizerIn ], $data, [ - 'operations' => (object) $operations, + 'operations' => $operations, 'definitions' => (object) ($defs->getArrayCopy()), ] ); } /** - * @param $resource + * @param mixed $resource * @param array $context * @param bool $relative * @@ -151,7 +149,7 @@ class ObjectMetadataNormalizer implements NormalizerAwareInterface, NormalizerIn } /** - * @param ObjectMetadata $root + * @param ObjectMetadata $object * @param string|null $format * @param array $context * @@ -191,6 +189,9 @@ class ObjectMetadataNormalizer implements NormalizerAwareInterface, NormalizerIn { $attrs = $object->getTypeAttributes(); - return ['type' => $object->getBaseType()] + $this->normalizer->normalize($attrs, $format, $context); + $normalizedAttrs = $this->normalizer->normalize($attrs, $format, $context); + Assertion::isArray($normalizedAttrs); + + return ['type' => $object->getBaseType()] + $normalizedAttrs; } } diff --git a/src/Bridge/Symfony/Serializer/TypeMetadataNormalizer.php b/src/Bridge/Symfony/Serializer/TypeMetadataNormalizer.php index 64e8ff9..260282c 100644 --- a/src/Bridge/Symfony/Serializer/TypeMetadataNormalizer.php +++ b/src/Bridge/Symfony/Serializer/TypeMetadataNormalizer.php @@ -50,6 +50,9 @@ class TypeMetadataNormalizer implements NormalizerAwareInterface, NormalizerInte /** @var TypeMetadata $object */ $attrs = $object->getTypeAttributes(); - return ['type' => $object->getBaseType()] + $this->normalizer->normalize($attrs, $format, $context); + $normalizedAttrs = $this->normalizer->normalize($attrs, $format, $context); + Assertion::isArray($normalizedAttrs); + + return ['type' => $object->getBaseType()] + $normalizedAttrs; } } diff --git a/src/Exception/InvalidArgumentException.php b/src/Exception/InvalidArgumentException.php new file mode 100644 index 0000000..d75fa45 --- /dev/null +++ b/src/Exception/InvalidArgumentException.php @@ -0,0 +1,28 @@ +<?php declare(strict_types=1); +/* + * This file is part of "irstea/api-metadata". + * + * Copyright (C) 2019 IRSTEA + * + * This program is free software: you can redistribute it and/or modify it under + * the terms of the GNU Lesser General Public License as published by the Free + * Software Foundation, either version 3 of the License, or (at your option) any + * later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT ANY + * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A + * PARTICULAR PURPOSE. See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License and the GNU + * Lesser General Public License along with this program. If not, see + * <https://www.gnu.org/licenses/>. + */ + +namespace Irstea\ApiMetadata\Exception; + +/** + * Class InvalidArgumentException. + */ +class InvalidArgumentException extends \InvalidArgumentException implements ApiMetadataException +{ +} diff --git a/src/Factory/Operation/NullOperationFactory.php b/src/Factory/Operation/NullOperationFactory.php index 0a6a9eb..b1b77e8 100644 --- a/src/Factory/Operation/NullOperationFactory.php +++ b/src/Factory/Operation/NullOperationFactory.php @@ -22,6 +22,8 @@ namespace Irstea\ApiMetadata\Factory\Operation; use Irstea\ApiMetadata\Exception\OperationNotFoundException; use Irstea\ApiMetadata\Factory\ContextInterface; +use Irstea\ApiMetadata\Model\Identity\OperationIdentityInterface; +use Irstea\ApiMetadata\Model\Identity\ResourceIdentityInterface; use Irstea\ApiMetadata\Model\OperationMetadata; /** @@ -32,15 +34,15 @@ class NullOperationFactory implements OperationFactoryInterface /** * {@inheritdoc} */ - public function create(string $class, string $type, string $name, ContextInterface $context): OperationMetadata + public function createOperation(OperationIdentityInterface $operationId, ContextInterface $context): OperationMetadata { - throw new OperationNotFoundException("No operation metadata for $class/$name ($type)"); + throw new OperationNotFoundException('No operation metadata for ' . $operationId->getClass()); } /** * {@inheritdoc} */ - public function enumerate(string $class, ContextInterface $context): array + public function enumerateOperations(ResourceIdentityInterface $resourceId): array { return []; } diff --git a/src/Factory/Operation/OperationFactoryInterface.php b/src/Factory/Operation/OperationFactoryInterface.php index ce2ea7d..7357e8c 100644 --- a/src/Factory/Operation/OperationFactoryInterface.php +++ b/src/Factory/Operation/OperationFactoryInterface.php @@ -21,6 +21,8 @@ namespace Irstea\ApiMetadata\Factory\Operation; use Irstea\ApiMetadata\Factory\ContextInterface; +use Irstea\ApiMetadata\Model\Identity\OperationIdentityInterface; +use Irstea\ApiMetadata\Model\Identity\ResourceIdentityInterface; use Irstea\ApiMetadata\Model\OperationMetadata; /** @@ -29,20 +31,17 @@ use Irstea\ApiMetadata\Model\OperationMetadata; interface OperationFactoryInterface { /** - * @param string $class - * @param string $type - * @param string $name - * @param ContextInterface $context + * @param OperationIdentityInterface $operationId + * @param ContextInterface $context * * @return OperationMetadata */ - public function create(string $class, string $type, string $name, ContextInterface $context): OperationMetadata; + public function createOperation(OperationIdentityInterface $operationId, ContextInterface $context): OperationMetadata; /** - * @param string $class - * @param ContextInterface $context + * @param ResourceIdentityInterface $resourceId * - * @return OperationMetadata[] + * @return OperationIdentityInterface[] */ - public function enumerate(string $class, ContextInterface $context): array; + public function enumerateOperations(ResourceIdentityInterface $resourceId): array; } diff --git a/src/Factory/Operation/ResourceOperationFactory.php b/src/Factory/Operation/ResourceOperationFactory.php index 91382a2..77d91fe 100644 --- a/src/Factory/Operation/ResourceOperationFactory.php +++ b/src/Factory/Operation/ResourceOperationFactory.php @@ -25,15 +25,20 @@ use ApiPlatform\Core\Api\OperationType; use ApiPlatform\Core\Metadata\Resource\Factory\ResourceMetadataFactoryInterface; use ApiPlatform\Core\Metadata\Resource\ResourceMetadata; use ApiPlatform\Core\PathResolver\OperationPathResolverInterface; +use Irstea\ApiMetadata\Exception\InvalidArgumentException; use Irstea\ApiMetadata\Exception\OperationNotFoundException; use Irstea\ApiMetadata\Factory\ContextInterface; -use Irstea\ApiMetadata\Factory\Type\TypeFactoryInterface; use Irstea\ApiMetadata\Helper\PropertyInfoType; +use Irstea\ApiMetadata\Model\Identity\OperationIdentity; +use Irstea\ApiMetadata\Model\Identity\OperationIdentityInterface; +use Irstea\ApiMetadata\Model\Identity\ResourceIdentityInterface; use Irstea\ApiMetadata\Model\OperationMetadata; use Irstea\ApiMetadata\Model\TypeMetadata; /** * Class ResourceOperationFactory. + * + * @SuppressWarnings(PHPMD.CouplingBetweenObjects) @TODO Refactorer la classe */ class ResourceOperationFactory implements OperationFactoryInterface { @@ -55,7 +60,6 @@ class ResourceOperationFactory implements OperationFactoryInterface * @param ResourceMetadataFactoryInterface $metadataFactory * @param OperationMethodResolverInterface $methodResolver * @param OperationPathResolverInterface $pathResolver - * @param TypeFactoryInterface $typeFactory * @param OperationFactoryInterface $next */ public function __construct( @@ -73,70 +77,75 @@ class ResourceOperationFactory implements OperationFactoryInterface /** * {@inheritdoc} */ - public function create(string $class, string $type, string $name, ContextInterface $context): OperationMetadata + public function createOperation(OperationIdentityInterface $operationId, ContextInterface $context): OperationMetadata { - $resource = $this->metadataFactory->create($class); + $class = $operationId->getClass(); + $type = $operationId->getType(); + + $resourceMetadata = $this->metadataFactory->create($class); - switch ($type) { + switch ($operationId->getType()) { case OperationType::ITEM: - $ops = $resource->getItemOperations(); + $ops = $resourceMetadata->getItemOperations(); break; case OperationType::COLLECTION: - $ops = $resource->getCollectionOperations(); + $ops = $resourceMetadata->getCollectionOperations(); break; default: - throw new \InvalidArgumentException("unhandled operationt type: $type"); + throw new \InvalidArgumentException("unhandled operation type: $type"); } foreach ((array) $ops as $opName => $attributes) { - if ($opName === $name) { - return $this->extractMetadata($class, $resource, $type, $name, $attributes, $context); + if ($opName === $operationId->getName()) { + return $this->extractMetadata($operationId, $resourceMetadata, $attributes, $context); } } - throw new OperationNotFoundException("unknown ${type} operation: ${name}"); + throw new OperationNotFoundException("unknown ${type} operation: " . $operationId->getName()); } /** * {@inheritdoc} */ - public function enumerate(string $class, ContextInterface $context): array + public function enumerateOperations(ResourceIdentityInterface $resourceId): array { - $operations = $this->next->enumerate($class, $context); + $class = $resourceId->getClass(); + + $operations = $this->next->enumerateOperations($resourceId); $resource = $this->metadataFactory->create($class); foreach ((array) $resource->getItemOperations() as $name => $attributes) { - $operation = $this->extractMetadata($class, $resource, OperationType::ITEM, $name, $attributes, $context); - $operations[$operation->getKey()] = $operation; + $operations[] = OperationIdentity::fromValues($resourceId->getClass(), $resourceId->getShortName(), OperationType::ITEM, $name); } foreach ((array) $resource->getCollectionOperations() as $name => $attributes) { - $operation = $this->extractMetadata($class, $resource, OperationType::COLLECTION, $name, $attributes, $context); - $operations[$operation->getKey()] = $operation; + $operations[] = OperationIdentity::fromValues($resourceId->getClass(), $resourceId->getShortName(), OperationType::COLLECTION, $name); } return $operations; } /** - * @param string $class - * @param ResourceMetadata $resource - * @param string $type - * @param string $name - * @param array $attributes - * @param ContextInterface $context + * @param OperationIdentityInterface $operationId + * @param ResourceMetadata $resource + * @param array $attributes + * @param ContextInterface $context * * @return OperationMetadata */ - private function extractMetadata(string $class, ResourceMetadata $resource, string $type, string $name, array $attributes, ContextInterface $context): OperationMetadata + private function extractMetadata(OperationIdentityInterface $operationId, ResourceMetadata $resource, array $attributes, ContextInterface $context): OperationMetadata { - $path = $this->pathResolver->resolveOperationPath($resource->getShortName() ?: $class, $attributes, $type); + $class = $operationId->getClass(); + $type = $operationId->getType(); + $name = $operationId->getName(); + + $path = $this->pathResolver->resolveOperationPath($resource->getShortName() ?: $class, $attributes, $operationId->getType()); $path = str_replace('.{_format}', '', $path); - switch ($type) { + switch ($operationId->getType()) { case OperationType::ITEM: $method = $this->methodResolver->getItemOperationMethod($class, $name); $input = $resource->getItemOperationAttribute($name, 'input', $class, true); @@ -154,9 +163,7 @@ class ResourceOperationFactory implements OperationFactoryInterface } return new OperationMetadata( - $resource->getShortName(), - $type, - $name, + $operationId, $path, $method, $method !== 'GET' && $method !== 'HEAD' ? $this->findInOuts($input, $context) : null, @@ -187,6 +194,6 @@ class ResourceOperationFactory implements OperationFactoryInterface return $context->createType(PropertyInfoType::create($definition), $context); } - throw new \InvalidArgumentException('unknown input/output argument'); + throw new InvalidArgumentException('unknown input/output argument'); } } diff --git a/src/Factory/Property/ResourcePropertyFactory.php b/src/Factory/Property/ResourcePropertyFactory.php index ce76d76..4f77f10 100644 --- a/src/Factory/Property/ResourcePropertyFactory.php +++ b/src/Factory/Property/ResourcePropertyFactory.php @@ -25,7 +25,6 @@ use ApiPlatform\Core\Exception\ResourceClassNotFoundException; use ApiPlatform\Core\Metadata\Property\Factory\PropertyMetadataFactoryInterface; use ApiPlatform\Core\Metadata\Property\Factory\PropertyNameCollectionFactoryInterface; use Irstea\ApiMetadata\Factory\ContextInterface; -use Irstea\ApiMetadata\Factory\Type\TypeFactoryInterface; use Irstea\ApiMetadata\Helper\PropertyInfoType; use Irstea\ApiMetadata\Model\PropertyMetadata; @@ -48,7 +47,6 @@ class ResourcePropertyFactory implements PropertyFactoryInterface * * @param PropertyNameCollectionFactoryInterface $namesFactory * @param PropertyMetadataFactoryInterface $metadataFactory - * @param TypeFactoryInterface $typeFactory * @param PropertyFactoryInterface|null $next */ public function __construct( diff --git a/src/Factory/Type/CollectionTypeFactory.php b/src/Factory/Type/CollectionTypeFactory.php index 26a3e91..8bb60ff 100644 --- a/src/Factory/Type/CollectionTypeFactory.php +++ b/src/Factory/Type/CollectionTypeFactory.php @@ -20,6 +20,7 @@ namespace Irstea\ApiMetadata\Factory\Type; +use Irstea\ApiMetadata\Exception\InvalidArgumentException; use Irstea\ApiMetadata\Factory\ContextInterface; use Irstea\ApiMetadata\Helper\PropertyInfoType; use Irstea\ApiMetadata\Model\ArrayMetadata; @@ -56,6 +57,6 @@ class CollectionTypeFactory extends TypeFactoryDecorator ); } - throw new \InvalidArgumentException("unsupported key type: ${keyType}"); + throw new InvalidArgumentException('unsupported key type'); } } diff --git a/src/Factory/Type/ObjectTypeFactory.php b/src/Factory/Type/ObjectTypeFactory.php index fdceaf1..db57978 100644 --- a/src/Factory/Type/ObjectTypeFactory.php +++ b/src/Factory/Type/ObjectTypeFactory.php @@ -39,7 +39,6 @@ class ObjectTypeFactory extends AbstractClassTypeFactory /** * ObjectTypeFactory constructor. * - * @param TypeFactoryInterface $typeFactory * @param PropertyFactoryInterface $propertyFactory * @param TypeFactoryInterface $next */ diff --git a/src/Factory/Type/ResourceTypeFactory.php b/src/Factory/Type/ResourceTypeFactory.php index e6e5295..7896483 100644 --- a/src/Factory/Type/ResourceTypeFactory.php +++ b/src/Factory/Type/ResourceTypeFactory.php @@ -27,6 +27,7 @@ use Irstea\ApiMetadata\Factory\ContextInterface; use Irstea\ApiMetadata\Factory\Operation\OperationFactoryInterface; use Irstea\ApiMetadata\Factory\Property\PropertyFactoryInterface; use Irstea\ApiMetadata\Factory\ReferenceFactoryInterface; +use Irstea\ApiMetadata\Model\Identity\ResourceIdentity; use Irstea\ApiMetadata\Model\ResourceMetadata; use Irstea\ApiMetadata\Model\TypeMetadata; @@ -103,16 +104,16 @@ class ResourceTypeFactory extends AbstractClassTypeFactory private function doCreateClass(string $className, ContextInterface $context): TypeMetadata { $resource = $this->metadataFactory->create($className); - $shortName = $resource->getShortName(); - Assertion::string($shortName); + Assertion::notNull($shortName); + + $resourceId = ResourceIdentity::fromValues($className, $shortName); return new ResourceMetadata( - $className, - $shortName, + $resourceId, null, //$parentMeta, $this->propertyFactory->enumerate($className, $context), - $this->operationFactory->enumerate($className, $context) + $this->operationFactory->enumerateOperations($resourceId) ); } } diff --git a/src/Model/Identity/OperationIdentity.php b/src/Model/Identity/OperationIdentity.php new file mode 100644 index 0000000..524303f --- /dev/null +++ b/src/Model/Identity/OperationIdentity.php @@ -0,0 +1,130 @@ +<?php declare(strict_types=1); +/* + * This file is part of "irstea/api-metadata". + * + * Copyright (C) 2019 IRSTEA + * + * This program is free software: you can redistribute it and/or modify it under + * the terms of the GNU Lesser General Public License as published by the Free + * Software Foundation, either version 3 of the License, or (at your option) any + * later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT ANY + * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A + * PARTICULAR PURPOSE. See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License and the GNU + * Lesser General Public License along with this program. If not, see + * <https://www.gnu.org/licenses/>. + */ + +namespace Irstea\ApiMetadata\Model\Identity; + +use ApiPlatform\Core\Api\OperationType; +use Assert\Assertion; + +/** + * Class OperationID. + */ +final class OperationIdentity implements OperationIdentityInterface +{ + /** @var ResourceIdentityInterface */ + private $resourceId; + + /** @var string */ + private $type; + + /** @var string */ + private $name; + + /** + * OperationIdentifiers constructor. + * + * @param ResourceIdentityInterface $resourceId + * @param string $type + * @param string $name + */ + public function __construct(ResourceIdentityInterface $resourceId, string $type, string $name) + { + Assertion::choice($type, OperationType::TYPES); + + $this->resourceId = $resourceId; + $this->type = $type; + $this->name = $name; + } + + /** + * Get shortName. + * + * @return string + */ + public function getClass(): string + { + return $this->resourceId->getClass(); + } + + /** + * Get shortName. + * + * @return string + */ + public function getShortName(): string + { + return $this->resourceId->getShortName(); + } + + /** + * Get type. + * + * @return string + */ + public function getType(): string + { + return $this->type; + } + + /** + * Get name. + * + * @return string + */ + public function getName(): string + { + return $this->name; + } + + /** + * @return string + */ + public function __toString(): string + { + return sprintf('%s::%s_%s', $this->resourceId->getShortName(), $this->name, $this->type); + } + + /** + * @param string $class + * @param string $shortName + * @param string $type + * @param string $name + * + * @return self + */ + public static function fromValues(string $class, string $shortName, string $type, string $name): self + { + return new self(ResourceIdentity::fromValues($class, $shortName), $type, $name); + } + + /** + * @param OperationIdentityInterface $operation + * + * @return self + */ + public static function fromOperation(OperationIdentityInterface $operation): self + { + if ($operation instanceof self) { + return $operation; + } + + return new self(ResourceIdentity::fromValues($operation->getClass(), $operation->getShortName()), $operation->getType(), $operation->getName()); + } +} diff --git a/src/Model/Identity/OperationIdentityInterface.php b/src/Model/Identity/OperationIdentityInterface.php new file mode 100644 index 0000000..4b7dec7 --- /dev/null +++ b/src/Model/Identity/OperationIdentityInterface.php @@ -0,0 +1,47 @@ +<?php declare(strict_types=1); +/* + * This file is part of "irstea/api-metadata". + * + * Copyright (C) 2019 IRSTEA + * + * This program is free software: you can redistribute it and/or modify it under + * the terms of the GNU Lesser General Public License as published by the Free + * Software Foundation, either version 3 of the License, or (at your option) any + * later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT ANY + * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A + * PARTICULAR PURPOSE. See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License and the GNU + * Lesser General Public License along with this program. If not, see + * <https://www.gnu.org/licenses/>. + */ + +namespace Irstea\ApiMetadata\Model\Identity; + +/** + * Class OperationIdentityInterface. + */ +interface OperationIdentityInterface +{ + /** + * @return string + */ + public function getShortName(): string; + + /** + * @return string + */ + public function getClass(): string; + + /** + * @return string + */ + public function getType(): string; + + /** + * @return string + */ + public function getName(): string; +} diff --git a/src/Model/Identity/ResourceIdentity.php b/src/Model/Identity/ResourceIdentity.php new file mode 100644 index 0000000..0915ca8 --- /dev/null +++ b/src/Model/Identity/ResourceIdentity.php @@ -0,0 +1,103 @@ +<?php declare(strict_types=1); +/* + * This file is part of "irstea/api-metadata". + * + * Copyright (C) 2019 IRSTEA + * + * This program is free software: you can redistribute it and/or modify it under + * the terms of the GNU Lesser General Public License as published by the Free + * Software Foundation, either version 3 of the License, or (at your option) any + * later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT ANY + * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A + * PARTICULAR PURPOSE. See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License and the GNU + * Lesser General Public License along with this program. If not, see + * <https://www.gnu.org/licenses/>. + */ + +namespace Irstea\ApiMetadata\Model\Identity; + +use Assert\Assertion; + +/** + * Class ResourceIdentity. + */ +final class ResourceIdentity implements ResourceIdentityInterface +{ + /** + * @var string + */ + private $shortName; + /** + * @var string + */ + private $class; + + /** + * ResourceIdentifiers constructor. + * + * @param string $class + * @param string $shortName + */ + private function __construct(string $class, string $shortName) + { + Assertion::classExists($class); + + $this->shortName = $shortName; + $this->class = $class; + } + + /** + * {@inheritdoc} + */ + public function getClass(): string + { + return $this->class; + } + + /** + * Get shortName. + * + * @return string + */ + public function getShortName(): string + { + return $this->shortName; + } + + /** + * @return string + */ + public function __toString(): string + { + return $this->shortName; + } + + /** + * @param string $class + * @param string $shortName + * + * @return self + */ + public static function fromValues(string $class, string $shortName): self + { + return new self($class, $shortName); + } + + /** + * @param ResourceIdentityInterface $resource + * + * @return self + */ + public static function fromResource(ResourceIdentityInterface $resource): self + { + if ($resource instanceof self) { + return $resource; + } + + return new self($resource->getClass(), $resource->getShortName()); + } +} diff --git a/src/Model/Identity/ResourceIdentityInterface.php b/src/Model/Identity/ResourceIdentityInterface.php new file mode 100644 index 0000000..4aa749c --- /dev/null +++ b/src/Model/Identity/ResourceIdentityInterface.php @@ -0,0 +1,37 @@ +<?php declare(strict_types=1); +/* + * This file is part of "irstea/api-metadata". + * + * Copyright (C) 2019 IRSTEA + * + * This program is free software: you can redistribute it and/or modify it under + * the terms of the GNU Lesser General Public License as published by the Free + * Software Foundation, either version 3 of the License, or (at your option) any + * later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT ANY + * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A + * PARTICULAR PURPOSE. See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License and the GNU + * Lesser General Public License along with this program. If not, see + * <https://www.gnu.org/licenses/>. + */ + +namespace Irstea\ApiMetadata\Model\Identity; + +/** + * Interface ResourceIdentityInterface. + */ +interface ResourceIdentityInterface +{ + /** + * @return string + */ + public function getClass(): string; + + /** + * @return string + */ + public function getShortName(): string; +} diff --git a/src/Model/MapMetadata.php b/src/Model/MapMetadata.php index 2aea3f1..83da68c 100644 --- a/src/Model/MapMetadata.php +++ b/src/Model/MapMetadata.php @@ -31,7 +31,6 @@ final class MapMetadata implements TypeMetadata /** * MapMetadata constructor. * - * @param TypeMetadata $key * @param TypeMetadata $valueType */ public function __construct(TypeMetadata $valueType) diff --git a/src/Model/OperationMetadata.php b/src/Model/OperationMetadata.php index e060c54..b299333 100644 --- a/src/Model/OperationMetadata.php +++ b/src/Model/OperationMetadata.php @@ -20,19 +20,15 @@ namespace Irstea\ApiMetadata\Model; +use Irstea\ApiMetadata\Model\Identity\OperationIdentityInterface; + /** * Class OperationMetadata. */ -final class OperationMetadata +final class OperationMetadata implements OperationIdentityInterface { - /** @var string */ - private $resource; - - /** @var string */ - private $type; - - /** @var string */ - private $name; + /** @var OperationIdentityInterface */ + private $id; /** @var string */ private $path; @@ -49,19 +45,15 @@ final class OperationMetadata /** * OperationMetadata constructor. * - * @param string $resource - * @param string $type - * @param string $name - * @param string $path - * @param string $method - * @param TypeMetadata|null $input - * @param TypeMetadata|null $output + * @param OperationIdentityInterface $id + * @param string $path + * @param string $method + * @param TypeMetadata|null $input + * @param TypeMetadata|null $output */ - public function __construct(string $resource, string $type, string $name, string $path, string $method, ?TypeMetadata $input, ?TypeMetadata $output) + public function __construct(OperationIdentityInterface $id, string $path, string $method, ?TypeMetadata $input, ?TypeMetadata $output) { - $this->resource = $resource; - $this->type = $type; - $this->name = $name; + $this->id = $id; $this->path = $path; $this->method = $method; $this->input = $input; @@ -69,41 +61,35 @@ final class OperationMetadata } /** - * @return string + * {@inheritdoc} */ - public function getKey(): string + public function getShortName(): string { - return $this->type . '_' . $this->name; + return $this->id->getShortName(); } /** - * Get resource. - * - * @return string + * {@inheritdoc} */ - public function getResource(): string + public function getClass(): string { - return $this->resource; + return $this->id->getClass(); } /** - * Get type. - * - * @return string + * {@inheritdoc} */ public function getType(): string { - return $this->type; + return $this->id->getType(); } /** - * Get name. - * - * @return string + * {@inheritdoc} */ public function getName(): string { - return $this->name; + return $this->id->getName(); } /** diff --git a/src/Model/ResourceMetadata.php b/src/Model/ResourceMetadata.php index bccb23a..28f5285 100644 --- a/src/Model/ResourceMetadata.php +++ b/src/Model/ResourceMetadata.php @@ -21,53 +21,50 @@ namespace Irstea\ApiMetadata\Model; use Assert\Assertion; +use Irstea\ApiMetadata\Model\Identity\OperationIdentityInterface; +use Irstea\ApiMetadata\Model\Identity\ResourceIdentityInterface; /** * Class ResourceMetadata. */ -final class ResourceMetadata extends ObjectMetadata +final class ResourceMetadata extends ObjectMetadata implements ResourceIdentityInterface { - /** - * @var string - */ - private $shortName; + /** @var ResourceIdentityInterface */ + private $id; - /** @var array<OperationMetadata> */ + /** @var array<OperationIdentityInterface> */ private $operations = []; /** * ResourceMetadata constructor. * - * @param string $class - * @param string $shortName - * @param ObjectMetadata|null $parent - * @param array $properties - * @param OperationMetadata[] $operations + * @param ResourceIdentityInterface $id + * @param ObjectMetadata|null $parent + * @param array $properties + * @param OperationIdentityInterface[] $operations */ - public function __construct(string $class, string $shortName, ?ObjectMetadata $parent, array $properties, array $operations) + public function __construct(ResourceIdentityInterface $id, ?ObjectMetadata $parent, array $properties, array $operations) { - Assertion::allIsInstanceOf($operations, OperationMetadata::class); + Assertion::allIsInstanceOf($operations, OperationIdentityInterface::class); - parent::__construct($class, $parent, $properties); + parent::__construct($id->getClass(), $parent, $properties); - $this->shortName = $shortName; + $this->id = $id; $this->operations = $operations; } /** - * Get shortName. - * - * @return string + * {@inheritdoc} */ public function getShortName(): string { - return $this->shortName; + return $this->id->getShortName(); } /** * Get operations. * - * @return OperationMetadata[] + * @return OperationIdentityInterface[] */ public function getOperations(): array { @@ -79,6 +76,6 @@ final class ResourceMetadata extends ObjectMetadata */ public function getTypeAttributes(): array { - return ['shortName' => $this->shortName] + parent::getTypeAttributes(); + return ['shortName' => $this->getShortName()] + parent::getTypeAttributes(); } } diff --git a/src/Service/ResourceMap.php b/src/Service/ResourceMap.php index 3de4539..54cba62 100644 --- a/src/Service/ResourceMap.php +++ b/src/Service/ResourceMap.php @@ -20,18 +20,25 @@ namespace Irstea\ApiMetadata\Service; +use ApiPlatform\Core\Exception\ResourceClassNotFoundException; use ApiPlatform\Core\Metadata\Resource\Factory\ResourceMetadataFactoryInterface; use ApiPlatform\Core\Metadata\Resource\Factory\ResourceNameCollectionFactoryInterface; +use ArrayIterator; +use Assert\Assertion; +use Irstea\ApiMetadata\Exception\ResourceNotFoundException; +use Irstea\ApiMetadata\Model\Identity\ResourceIdentity; +use Irstea\ApiMetadata\Model\Identity\ResourceIdentityInterface; +use Iterator; /** * Class ResourceMap. */ class ResourceMap implements ResourceMapInterface { - /** @var array<string,string>|null */ + /** @var array<string, ResourceIdentityInterface>|null */ private $resourceMap = null; - /** @var array<string,string>|null */ + /** @var array<string, ResourceIdentityInterface>|null */ private $reverseMap = null; /** @var ResourceMetadataFactoryInterface */ @@ -55,13 +62,17 @@ class ResourceMap implements ResourceMapInterface } /** - * {@inheritdoc} + * @return Iterator<string, ResourceIdentityInterface> */ - public function getIterator(): \Iterator + public function getIterator(): Iterator { $this->init(); + Assertion::notNull($this->resourceMap); + + /** @var Iterator<string, ResourceIdentityInterface> $iter */ + $iter = new ArrayIterator($this->resourceMap); - return new \ArrayIterator($this->resourceMap); + return $iter; } /** @@ -77,37 +88,47 @@ class ResourceMap implements ResourceMapInterface /** * {@inheritdoc} */ - public function getShortName(string $className): ?string + public function isResourceName(string $shortName): bool { $this->init(); - return $this->resourceMap[$className] ?? null; + return isset($this->reverseMap[$shortName]); } /** * {@inheritdoc} */ - public function isResourceName(string $shortName): bool + public function getByClassName(string $className): ResourceIdentityInterface { $this->init(); - return isset($this->reverseMap[$shortName]); + $resourceId = $this->resourceMap[$className] ?? null; + if ($resourceId === null) { + throw new ResourceNotFoundException("No ressource found for class $className"); + } + + return $resourceId; } /** * {@inheritdoc} */ - public function getClassName(string $shortName): ?string + public function getByShortName(string $shortName): ResourceIdentityInterface { $this->init(); - return $this->reverseMap[$shortName] ?? null; + $resourceId = $this->reverseMap[$shortName] ?? null; + if ($resourceId === null) { + throw new ResourceNotFoundException("No ressource found for name $shortName"); + } + + return $resourceId; } /** * Initialise les maps de correspondance shortName <=> className. * - * @throws \ApiPlatform\Core\Exception\ResourceClassNotFoundException + * @throws ResourceClassNotFoundException */ private function init(): void { @@ -119,9 +140,11 @@ class ResourceMap implements ResourceMapInterface foreach ($this->nameFactory->create() as $className) { $metadata = $this->metadataFactory->create($className); + $shortName = $metadata->getShortName(); - $this->resourceMap[$className] = $shortName; - $this->reverseMap[$shortName] = $className; + Assertion::notNull($shortName); + + $this->resourceMap[$className] = $this->reverseMap[$shortName] = ResourceIdentity::fromValues($className, $shortName); } } } diff --git a/src/Service/ResourceMapInterface.php b/src/Service/ResourceMapInterface.php index 2fdaeb5..ed4267d 100644 --- a/src/Service/ResourceMapInterface.php +++ b/src/Service/ResourceMapInterface.php @@ -20,6 +20,9 @@ namespace Irstea\ApiMetadata\Service; +use Irstea\ApiMetadata\Exception\ResourceNotFoundException; +use Irstea\ApiMetadata\Model\Identity\ResourceIdentityInterface; + /** * Interface ResourceMapInterface. */ @@ -40,16 +43,20 @@ interface ResourceMapInterface extends \IteratorAggregate public function isResourceName(string $shortName): bool; /** - * @param string $shortName + * @param string $className + * + * @throws ResourceNotFoundException * - * @return string|null + * @return ResourceIdentityInterface */ - public function getClassName(string $shortName): ?string; + public function getByClassName(string $className): ResourceIdentityInterface; /** - * @param string $className + * @param string $shortName + * + * @throws ResourceNotFoundException * - * @return string|null + * @return ResourceIdentityInterface */ - public function getShortName(string $className): ?string; + public function getByShortName(string $shortName): ResourceIdentityInterface; } diff --git a/src/URI/CompositeURIGenerator.php b/src/URI/CompositeURIGenerator.php index 998ec5e..54ec214 100644 --- a/src/URI/CompositeURIGenerator.php +++ b/src/URI/CompositeURIGenerator.php @@ -28,14 +28,14 @@ use Assert\Assertion; class CompositeURIGenerator implements URIGeneratorInterface { /** - * @var URIGeneratorInterface[] + * @var iterable<URIGeneratorInterface> */ private $generators; /** * CompositeURIGenerator constructor. * - * @param URIGeneratorInterface[] $generators + * @param iterable<URIGeneratorInterface> $generators */ public function __construct(iterable $generators) { diff --git a/src/URI/URIGeneratorInterface.php b/src/URI/URIGeneratorInterface.php index f67de02..3764c0c 100644 --- a/src/URI/URIGeneratorInterface.php +++ b/src/URI/URIGeneratorInterface.php @@ -26,14 +26,14 @@ namespace Irstea\ApiMetadata\URI; interface URIGeneratorInterface { /** - * @param $resource + * @param mixed $resource * * @return string */ public function generateURI($resource): string; /** - * @param $resource + * @param mixed $resource * * @return bool */ diff --git a/tests/Fixtures/Entity/Address.php b/tests/Fixtures/Entity/Address.php index fc462fb..f70e893 100644 --- a/tests/Fixtures/Entity/Address.php +++ b/tests/Fixtures/Entity/Address.php @@ -138,20 +138,4 @@ class Address { $this->country = $country; } - - /** - * @param mixed $other - * - * @return bool - * - * @API\ApiProperty(required=false) - */ - public function isEqualTo($other): bool - { - return $other instanceof self - && $other->street === $this->street - && $other->postalCode === $this->postalCode - && $other->city === $this->city - && $other->country === $this->country; - } } diff --git a/tests/FunctionalTest.php b/tests/FunctionalTest.php index d126c70..7684ac3 100644 --- a/tests/FunctionalTest.php +++ b/tests/FunctionalTest.php @@ -109,8 +109,8 @@ class FunctionalTest extends WebTestCase [ '$id' => 'http://localhost/metadata', 'resources' => [ - 'User' => ['$ref' => 'User'], - 'Person' => ['$ref' => 'Person'], + 'User' => ['$ref' => 'http://localhost/metadata/User#'], + 'Person' => ['$ref' => 'http://localhost/metadata/Person#'], ], ], $collection diff --git a/tests/Service/ResourceMapTest.php b/tests/Service/ResourceMapTest.php index 2668f31..4776e84 100644 --- a/tests/Service/ResourceMapTest.php +++ b/tests/Service/ResourceMapTest.php @@ -34,6 +34,7 @@ use Prophecy\Prophecy\ObjectProphecy; * Class ResourceMapTest. * * @covers \Irstea\ApiMetadata\Service\ResourceMap + * @covers \Irstea\ApiMetadata\Model\Identity\ResourceIdentity */ class ResourceMapTest extends TestCase { @@ -46,11 +47,11 @@ class ResourceMapTest extends TestCase protected function setUp() { $this->resources = [ - '\\App\\Foo' => new ResourceMetadata('Foo'), - '\\App\\Bar' => new ResourceMetadata('Bar'), + \stdClass::class => new ResourceMetadata('stdClass'), + ResourceMap::class => new ResourceMetadata('ResourceMap'), ]; - /** @var ResourceNameCollectionFactoryInterface|ObjectProphecy $nameFactoryProph */ + /** @var ObjectProphecy<ResourceNameCollectionFactoryInterface> $nameFactoryProph */ $nameFactoryProph = $this->prophesize(ResourceNameCollectionFactoryInterface::class); /** @var MethodProphecy $proph */ @@ -59,7 +60,7 @@ class ResourceMapTest extends TestCase new ResourceNameCollection(array_keys($this->resources)) ); - /** @var ResourceMetadataFactoryInterface|ObjectProphecy $metadataFactoryProph */ + /** @var ObjectProphecy<ResourceMetadataFactoryInterface> $metadataFactoryProph */ $metadataFactoryProph = $this->prophesize(ResourceMetadataFactoryInterface::class); /** @var string $stringArg */ @@ -80,25 +81,27 @@ class ResourceMapTest extends TestCase $this->map = new ResourceMap($nameFactory, $metadataFactory); } - public function testGetClassName() + public function testGetByShortName() { - self::assertEquals('\\App\\Foo', $this->map->getClassName('Foo')); + $resourceId = $this->map->getByShortName('stdClass'); + self::assertEquals(\stdClass::class, $resourceId->getClass()); } - public function testGetShortName() + public function testGetByClassName() { - self::assertEquals('Foo', $this->map->getShortName('\\App\\Foo')); + $resourceId = $this->map->getByClassName(\stdClass::class); + self::assertEquals(\stdClass::class, $resourceId->getClass()); } public function testIsResourceClass() { - self::assertTrue($this->map->isResourceClass('\\App\\Foo')); - self::assertFalse($this->map->isResourceClass(\stdClass::class)); + self::assertTrue($this->map->isResourceClass(ResourceMap::class)); + self::assertFalse($this->map->isResourceClass(__CLASS__)); } public function testIsResourceName() { - self::assertTrue($this->map->isResourceName('Foo')); - self::assertFalse($this->map->isResourceName('Baz')); + self::assertTrue($this->map->isResourceName('ResourceMap')); + self::assertFalse($this->map->isResourceName('ResourceMapTest')); } } diff --git a/tests/TestRemoteRefProvider.php b/tests/TestRemoteRefProvider.php index 4d353f0..90a3b6b 100644 --- a/tests/TestRemoteRefProvider.php +++ b/tests/TestRemoteRefProvider.php @@ -29,9 +29,7 @@ use Symfony\Component\HttpFoundation\Response; */ class TestRemoteRefProvider implements RemoteRefProvider { - /** - * @var - */ + /** @var Client */ private $client; /** -- GitLab