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