From 2ec19af77e010235ff459a491d04e7f3f33be5f6 Mon Sep 17 00:00:00 2001 From: Perreal Guillaume <guillaume.perreal@irstea.fr> Date: Tue, 20 Aug 2019 13:46:13 +0200 Subject: [PATCH] =?UTF-8?q?Fonctionnement=20de=20base=20r=C3=A9staur=C3=A9?= =?UTF-8?q?.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- composer.json | 1 + composer.lock | Bin 222354 -> 225135 bytes src/php/Exceptions/EmptyModelException.php | 20 ++++++ src/php/Exceptions/InvalidModelException.php | 44 ++++++++++++ .../Exceptions/MissingIdentifierException.php | 20 ++++++ .../TooManyIdentifiersException.php | 20 ++++++ src/php/ModelGenerator.php | 63 +++++++++--------- src/php/Models/Types/Deferred.php | 38 ----------- .../Types/Factory/DeferrableTypeFactory.php | 8 ++- src/php/Models/Types/Factory/TypeCache.php | 27 ++++++-- src/php/Models/Types/Reference.php | 54 +++++++-------- src/php/SerializationMapper.php | 32 +-------- 12 files changed, 190 insertions(+), 137 deletions(-) create mode 100644 src/php/Exceptions/EmptyModelException.php create mode 100644 src/php/Exceptions/InvalidModelException.php create mode 100644 src/php/Exceptions/MissingIdentifierException.php create mode 100644 src/php/Exceptions/TooManyIdentifiersException.php delete mode 100644 src/php/Models/Types/Deferred.php diff --git a/composer.json b/composer.json index c767394..fcbde08 100644 --- a/composer.json +++ b/composer.json @@ -52,6 +52,7 @@ "ramsey/uuid": "^3.8", "ramsey/uuid-doctrine": "^1.5", "symfony/framework-bundle": "^4.3", + "symfony/var-dumper": "^4.3", "symfony/yaml": "^4.3" }, "scripts": { diff --git a/composer.lock b/composer.lock index 75227eb0e125a54c689cf7e965eb08d9054b24d6..2bd2df8c4617d8e30fdf121a1713f94e248acc31 100644 GIT binary patch delta 736 zcma))&ubGw6vx@!#)u&_S|a$Xj*-%c3HvMAY<fzJq(Ko|ZAB0&`(rj4+@0C(Zfetm zP^x(HU<a9_;!QlLAm&;`3ZA6rfapJ<|3Ps#){~bW9`ktfeeZqVo7w)F{rx@reu@KH zO*3U(b8J;NE1D_Ex~gbOMKTnp>OiNurE*VIE-`+nazdv~Z(3>gcwTruVfQ_sHuh(W zup-NnS+6U$Vwq5`>vqjnO{G$?rCQyRt*Tzr<k+W+XlriG2+Bz&48#rOn}+L(hs<ZI z7rDahVa3gB+)4W5{yVz!ImJJc+1@=K(A>;4YZQbQn|{J&X#Q@C?Y!hCQph5BjeU5- zonq20VT!&mXQ*gr=#AIs*sm&=V{aQm`@vXMm;;3+0+Iw6L;w?9US4XKbr41Y!rc%= z1pEyYNdUru=MX#)ec}!r<htc?4S+DPj1U629vwt117i{a6M}vS?MrkupC6-@5RTk~ zo?(wX1Oe<Kf&n%>n3Rc7d~Og7LA+DCx1S#m(2NP=Iw0yna2p0O>D74SE#i3wwn1!S z%$8i52#fz7q#TZwAhZJHvs{|jDIOg=@xO#MJ^mSMT;Z0f_vL&d$~t{)MKM<>CKwk+ zml+VbBK`I;lfGk&n-3SS3y`O;c8^LPw0Z_cp$D881FrinNmiF|B~$o`ztC9#F&HvT x7t)`*D=8bA{Vv^1=Y{0g$%PV==7l3{vLrOwnMonbK8^U(+biMlct_Yg`v*rs_M-p* delta 78 zcmaEVk9X2Z-VMTx3YMuRMyW}OW~RpG7KY|VX_l!eMn(n(mc}L)$p!|diOt50?Z%8C g%(UH@k=es-x;#HK=k#s2nWQGg6>onR#M~$Y0F`PN5&!@I diff --git a/src/php/Exceptions/EmptyModelException.php b/src/php/Exceptions/EmptyModelException.php new file mode 100644 index 0000000..5d9ba0f --- /dev/null +++ b/src/php/Exceptions/EmptyModelException.php @@ -0,0 +1,20 @@ +<?php declare(strict_types=1); +/** + * Copyright (C) 2019 IRSTEA + * All rights reserved. + * + * @copyright 2019 IRSTEA + * @author guillaume.perreal + */ + + +namespace Irstea\NgModelGeneratorBundle\Exceptions; + + +/** + * Class EmptyModelException + */ +final class EmptyModelException extends InvalidModelException +{ + +} diff --git a/src/php/Exceptions/InvalidModelException.php b/src/php/Exceptions/InvalidModelException.php new file mode 100644 index 0000000..c00379a --- /dev/null +++ b/src/php/Exceptions/InvalidModelException.php @@ -0,0 +1,44 @@ +<?php declare(strict_types=1); +/** + * Copyright (C) 2019 IRSTEA + * All rights reserved. + * + * @copyright 2019 IRSTEA + * @author guillaume.perreal + */ + + +namespace Irstea\NgModelGeneratorBundle\Exceptions; + + +use Throwable; + +/** + * Class InvalidModelException + */ +class InvalidModelException extends DomainException +{ + /** + * @var string + */ + private $className; + + /** + * {@inheritDoc} + */ + public function __construct(string $className, $message = '', Throwable $previous = null) + { + parent::__construct($message, 0, $previous); + $this->className = $className; + } + + /** + * Get className. + * + * @return string + */ + public function getClassName(): string + { + return $this->className; + } +} diff --git a/src/php/Exceptions/MissingIdentifierException.php b/src/php/Exceptions/MissingIdentifierException.php new file mode 100644 index 0000000..be89b73 --- /dev/null +++ b/src/php/Exceptions/MissingIdentifierException.php @@ -0,0 +1,20 @@ +<?php declare(strict_types=1); +/** + * Copyright (C) 2019 IRSTEA + * All rights reserved. + * + * @copyright 2019 IRSTEA + * @author guillaume.perreal + */ + + +namespace Irstea\NgModelGeneratorBundle\Exceptions; + + +/** + * Class MissingIdentifierException + */ +final class MissingIdentifierException extends InvalidModelException +{ + +} diff --git a/src/php/Exceptions/TooManyIdentifiersException.php b/src/php/Exceptions/TooManyIdentifiersException.php new file mode 100644 index 0000000..7932050 --- /dev/null +++ b/src/php/Exceptions/TooManyIdentifiersException.php @@ -0,0 +1,20 @@ +<?php declare(strict_types=1); +/** + * Copyright (C) 2019 IRSTEA + * All rights reserved. + * + * @copyright 2019 IRSTEA + * @author guillaume.perreal + */ + + +namespace Irstea\NgModelGeneratorBundle\Exceptions; + + +/** + * Class TooManyIdentifiersException + */ +final class TooManyIdentifiersException extends InvalidModelException +{ + +} diff --git a/src/php/ModelGenerator.php b/src/php/ModelGenerator.php index 63ac7ce..a4c4c05 100644 --- a/src/php/ModelGenerator.php +++ b/src/php/ModelGenerator.php @@ -23,7 +23,10 @@ namespace Irstea\NgModelGeneratorBundle; use ApiPlatform\Core\Documentation\Documentation; use Irstea\NgModelGeneratorBundle\Exceptions\DomainException; +use Irstea\NgModelGeneratorBundle\Exceptions\EmptyModelException; use Irstea\NgModelGeneratorBundle\Exceptions\Exception; +use Irstea\NgModelGeneratorBundle\Exceptions\MissingIdentifierException; +use Irstea\NgModelGeneratorBundle\Exceptions\TooManyIdentifiersException; use Irstea\NgModelGeneratorBundle\Iterators\IteratorBuilder; use Irstea\NgModelGeneratorBundle\Metadata\MetadataFactoryInterface; use Irstea\NgModelGeneratorBundle\Metadata\ResourceMetadata; @@ -34,8 +37,10 @@ use Irstea\NgModelGeneratorBundle\Models\Types\Factory\TypeFactoryInterface; use Irstea\NgModelGeneratorBundle\Models\Types\Factory\TypeHelper; use Irstea\NgModelGeneratorBundle\Models\Types\Objects\Property; use Irstea\NgModelGeneratorBundle\Models\Types\Objects\Repository; +use Irstea\NgModelGeneratorBundle\Models\Types\Resources\Representation; use Irstea\NgModelGeneratorBundle\Models\Types\Type; use Irstea\NgModelGeneratorBundle\Writers\MultiFileWriter; +use PHPStan\Type\ResourceType; use Twig\Environment; /** @@ -202,11 +207,9 @@ final class ModelGenerator /* @var ResourceMetadata $resourceMeta */ try { $repo = $this->buildRepositories($resourceMeta); - if ($repo) { - $repositories[$repo->getName()] = $repo; - } + $repositories[$repo->getName()] = $repo; } catch (Exception $ex) { - printf("Error while mapping %s: %s\n%s\n", $class, $ex->getMessage(), $ex->getTraceAsString()); + printf("%s while mapping %s: %s\n%s\n", get_class($ex), $class, $ex->getMessage(), $ex->getTraceAsString()); } } @@ -218,48 +221,46 @@ final class ModelGenerator /** * @param ResourceMetadata $resourceMeta * - * @return Repository|null + * @return Repository */ - private function buildRepositories(ResourceMetadata $resourceMeta): ?Repository + private function buildRepositories(ResourceMetadata $resourceMeta): Repository { $defaultNormalization = $resourceMeta->getDefaultNormalization(); $defaultContext = $this->contextFactory->create($defaultNormalization, true); - /** - * @var Type - * @var Property $identifier - * @var Property[] $properties - */ - [$defaultRepr, $identifier, $properties] = $defaultContext->createType(TypeHelper::fromClassName($resourceMeta->getFullName())); + $defaultTypeRef = $defaultContext->createType(TypeHelper::fromClassName($resourceMeta->getFullName())); - if (!$properties) { - printf( - "No property found for %s (groups: [%s])\n", - $resourceMeta->getBaseName(), - implode(', ', $defaultNormalization->getGroups()) - ); + /** @var Representation $defaultType */ + $defaultType = $defaultTypeRef->findType(Representation::class); + + $properties = $defaultType->getProperties(); - return null; + if (!$properties) { + throw new EmptyModelException($resourceMeta->getFullName(), "no properties"); } - if (!$identifier) { - printf( - "No identifier found for %s (groups: [%s])\n", - $resourceMeta->getBaseName(), - implode(', ', $defaultNormalization->getGroups()) - ); + $identifiers = \array_filter( + $properties, + function(Property $property): bool { + return $property->getName(){0} !== '@' && $property->isIdentifier(); + } + ); + + if (!$identifiers) { + throw new MissingIdentifierException($resourceMeta->getFullName()); + } - return null; + if (count($identifiers) > 1) { + throw new TooManyIdentifiersException($resourceMeta->getFullName()); } + $identifier = array_shift($identifiers); + $operations = []; $pathParser = new PathParser($properties); $opsMeta = $resourceMeta->getOperations(); - if (!$opsMeta) { - return null; - } if (isset($opsMeta['getitem'])) { $get = $opsMeta['getitem']; @@ -274,9 +275,9 @@ final class ModelGenerator } if (!$operations) { - return null; + throw new EmptyModelException($resourceMeta->getFullName(), "no operations"); } - return new Repository($resourceMeta, $defaultRepr, $identifier, $iri, $operations); + return new Repository($resourceMeta, $defaultType, $identifier, $iri, $operations); } } diff --git a/src/php/Models/Types/Deferred.php b/src/php/Models/Types/Deferred.php deleted file mode 100644 index dd4e722..0000000 --- a/src/php/Models/Types/Deferred.php +++ /dev/null @@ -1,38 +0,0 @@ -<?php declare(strict_types=1); -/* - * This file is part of "irstea/ng-model-generator-bundle". - * - * "irstea/ng-model-generator-bundle" generates Typescript interfaces for Angular using api-platform metadata. - * Copyright (C) 2018-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\NgModelGeneratorBundle\Models\Types; - -/** - * Interface Deferred. - */ -interface Deferred extends Type -{ - /** - * @param callable $callback - */ - public function resolveWith(callable $callback): Type; - - /** - * @param Type $result - */ - public function resolve(Type $result): Type; -} diff --git a/src/php/Models/Types/Factory/DeferrableTypeFactory.php b/src/php/Models/Types/Factory/DeferrableTypeFactory.php index bd47ecc..c12428e 100644 --- a/src/php/Models/Types/Factory/DeferrableTypeFactory.php +++ b/src/php/Models/Types/Factory/DeferrableTypeFactory.php @@ -35,8 +35,12 @@ final class DeferrableTypeFactory extends AbstractTypeFactoryDecorator */ public function createType(PropertyType $type, ContextInterface $context): Type { - $type = new Reference(TypeHelper::getTypeKey($type)); + $key = TypeHelper::getTypeKey($type); - return $type; + $deferred = function () use ($type, $context) : Type { + return parent::createType($type, $context); + }; + + return new Reference($deferred, $key); } } diff --git a/src/php/Models/Types/Factory/TypeCache.php b/src/php/Models/Types/Factory/TypeCache.php index 6455d4b..e6db859 100644 --- a/src/php/Models/Types/Factory/TypeCache.php +++ b/src/php/Models/Types/Factory/TypeCache.php @@ -21,6 +21,7 @@ namespace Irstea\NgModelGeneratorBundle\Models\Types\Factory; +use Assert\Assertion; use Irstea\NgModelGeneratorBundle\Models\Types\Type; use Symfony\Component\PropertyInfo\Type as PropertyType; @@ -30,16 +31,27 @@ use Symfony\Component\PropertyInfo\Type as PropertyType; final class TypeCache extends AbstractTypeFactoryDecorator { /** - * @var array + * @var Type[] */ - private $cache = []; + private $typeCache = []; + + /** + * @var bool[] + */ + private $supportCache = []; /** * {@inheritdoc} */ public function supportsType(PropertyType $type, ContextInterface $context): bool { - return \array_key_exists(TypeHelper::getTypeKey($type), $this->cache) || parent::supportsType($type, $context); + $key = TypeHelper::getTypeKey($type); + + if (!\array_key_exists($key, $this->supportCache)) { + $this->supportCache[$key] = parent::supportsType($type, $context); + } + + return $this->supportCache[$key]; } /** @@ -47,11 +59,14 @@ final class TypeCache extends AbstractTypeFactoryDecorator */ public function createType(PropertyType $type, ContextInterface $context): Type { + Assertion::true($this->supportsType($type, $context)); + $key = TypeHelper::getTypeKey($type); - if (\array_key_exists($key, $this->cache)) { - return $this->cache[$key]; + + if (!\array_key_exists($key, $this->typeCache)) { + $this->typeCache[$key] = parent::createType($type, $context); } - return $this->cache[$key] = parent::createType($type, $context); + return $this->typeCache[$key]; } } diff --git a/src/php/Models/Types/Reference.php b/src/php/Models/Types/Reference.php index 61f41ad..ed9ff8a 100644 --- a/src/php/Models/Types/Reference.php +++ b/src/php/Models/Types/Reference.php @@ -21,10 +21,12 @@ namespace Irstea\NgModelGeneratorBundle\Models\Types; +use ApiPlatform\Core\Tests\Fixtures\TestBundle\Entity\CircularReference; + /** * Class Ref. */ -class Reference extends AbstractType implements Deferred +class Reference extends AbstractType { private const UNRESOLVED = 0; private const RESOLVING = 1; @@ -39,15 +41,20 @@ class Reference extends AbstractType implements Deferred /** @var int */ private $state = self::UNRESOLVED; + /** @var callable */ + private $deferred; + /** * Reference constructor. * + * @param callable $deferred * @param string $name */ - public function __construct(string $name = 'anonymous reference') + public function __construct(callable $deferred, string $name = 'anonymous reference') { $this->name = $name; $this->target = Unresolved::get(); + $this->deferred = $deferred; } /** @@ -57,34 +64,17 @@ class Reference extends AbstractType implements Deferred */ public function getTarget(): Type { - return $this->target; - } - - /** - * {@inheritdoc} - */ - public function resolveWith(callable $callback): Type - { - switch ($this->state) { - case self::UNRESOLVED: - $this->state = self::RESOLVING; - - return $this->resolve($callback()); - case self::RESOLVING: - return $this; - case self::RESOLVED: - return $this->target; + if ($this->state === self::RESOLVING) { + throw new \RuntimeException("circular reference"); } - } - /** - * {@inheritdoc} - */ - public function resolve(Type $result): Type - { - if ($this->state !== self::RESOLVED) { + if ($this->state === self::UNRESOLVED) { + $this->state = self::RESOLVING; + + $deferred = $this->deferred; + $this->target = $deferred(); + $this->state = self::RESOLVED; - $this->target = $result; } return $this->target; @@ -95,7 +85,11 @@ class Reference extends AbstractType implements Deferred */ private function dereference(): Type { - return $this->target instanceof Reference ? $this->target->dereference() : $this->target; + $target = $this->getTarget(); + if ($target instanceof self && $target !== $this) { + return $target->dereference(); + } + return $target; } /** @@ -127,7 +121,7 @@ class Reference extends AbstractType implements Deferred */ public function getIterator() { - yield $this->target; + yield $this->getTarget(); } /** @@ -151,6 +145,6 @@ class Reference extends AbstractType implements Deferred */ public function findType(string $typeClass): ?Type { - return parent::findType($typeClass) ?: $this->target->findType($typeClass); + return parent::findType($typeClass) ?: $this->getTarget()->findType($typeClass); } } diff --git a/src/php/SerializationMapper.php b/src/php/SerializationMapper.php index 67a92a3..7598966 100644 --- a/src/php/SerializationMapper.php +++ b/src/php/SerializationMapper.php @@ -85,7 +85,7 @@ final class SerializationMapper implements TypeFactoryInterface */ public function createType(PropertyType $type, ContextInterface $context): Type { - Assertion::true($this->createType($type, $context)); + Assertion::true($this->supportsType($type, $context)); $class = PHPClass::get($type->getClassName()); $repr = $this->serialization->getRepresentationOf($class); @@ -93,31 +93,6 @@ final class SerializationMapper implements TypeFactoryInterface return $this->mapRepresentation($repr, $context); } - /** - * @param ContextInterface $context - * - * @return array - */ - public function getResourceData(ContextInterface $context): array - { - $resource = $this->serialization->getRoot(); - $resourceName = $resource->getFullName(); - $classInfo = $this->getClassInfo($resource); - - $properties = array_map([$this, 'mapProperty'], $classInfo->getVirtualProperties()); - - $identifiers = array_filter( - $properties, - function (Property $property) { - return $property->isIdentifier(); - } - ); - - $identifier = $identifiers ? array_shift($identifiers) : null; - - return [$context->createType(TypeHelper::fromClassName($resourceName)), $identifier, $properties]; - } - /** * @param RepresentationMetadata $repr * @param ContextInterface $context @@ -149,10 +124,7 @@ final class SerializationMapper implements TypeFactoryInterface case 0: throw new DomainException(sprintf('Union with no children: %s', $repr)); case 1: - $ref = new Reference($types[0]->getUsage()); - $ref->resolve($types[0]); - - return $ref; + return $types[0]; default: return new Alias($repr->getName(), Union::create($types), $desc); } -- GitLab