Commit 2ec19af7 authored by Guillaume Perréal's avatar Guillaume Perréal
Browse files

Fonctionnement de base réstauré.

No related merge requests found
Showing with 190 additions and 137 deletions
+190 -137
......@@ -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": {
......
File suppressed by a .gitattributes entry or the file's encoding is unsupported.
<?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
{
}
<?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;
}
}
<?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
{
}
<?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
{
}
......@@ -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);
}
}
<?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;
}
......@@ -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);
}
}
......@@ -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];
}
}
......@@ -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);
}
}
......@@ -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);
}
......
Supports Markdown
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment