remove the support of duglas

This commit is contained in:
Ilyas Salikhov 2024-10-01 16:39:44 +03:00
parent eba661dcb6
commit 4b6137f618
11 changed files with 6 additions and 535 deletions

View File

@ -1,37 +0,0 @@
<?php
/*
* This file is part of the NelmioApiDocBundle.
*
* (c) Nelmio <hello@nelm.io>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Nelmio\ApiDocBundle\DependencyInjection;
use Symfony\Component\DependencyInjection\Compiler\CompilerPassInterface;
use Symfony\Component\DependencyInjection\ContainerBuilder;
use Symfony\Component\DependencyInjection\Reference;
/**
* AnnotationsProvider compiler pass.
*
* @author Kévin Dunglas <dunglas@gmail.com>
*/
class AnnotationsProviderCompilerPass implements CompilerPassInterface
{
public function process(ContainerBuilder $container): void
{
$annotationsProviders = [];
foreach ($container->findTaggedServiceIds('nelmio_api_doc.extractor.annotations_provider') as $id => $attributes) {
$annotationsProviders[] = new Reference($id);
}
$container
->getDefinition('nelmio_api_doc.extractor.api_doc_extractor')
->replaceArgument(4, $annotationsProviders)
;
}
}

View File

@ -1,189 +0,0 @@
<?php
/*
* This file is part of the NelmioApiDocBundle.
*
* (c) Nelmio <hello@nelm.io>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Nelmio\ApiDocBundle\Extractor\AnnotationsProvider;
use Dunglas\ApiBundle\Api\Operation\OperationInterface;
use Dunglas\ApiBundle\Api\ResourceCollectionInterface;
use Dunglas\ApiBundle\Api\ResourceInterface;
use Dunglas\ApiBundle\Hydra\ApiDocumentationBuilderInterface;
use Dunglas\ApiBundle\Mapping\ClassMetadataFactoryInterface;
use Nelmio\ApiDocBundle\Annotation\ApiDoc;
use Nelmio\ApiDocBundle\Extractor\AnnotationsProviderInterface;
use Nelmio\ApiDocBundle\Parser\DunglasApiParser;
use Symfony\Component\HttpFoundation\Request;
/**
* Creates ApiDoc annotations for DunglasApiBundle.
*
* @author Kévin Dunglas <dunglas@gmail.com>
*/
class DunglasApiProvider implements AnnotationsProviderInterface
{
/**
* @var ResourceCollectionInterface
*/
private $resourceCollection;
/**
* @var ApiDocumentationBuilderInterface
*/
private $apiDocumentationBuilder;
/**
* @var ClassMetadataFactoryInterface
*/
private $classMetadataFactory;
public function __construct(
ResourceCollectionInterface $resourceCollection,
ApiDocumentationBuilderInterface $apiDocumentationBuilder,
ClassMetadataFactoryInterface $classMetadataFactory,
) {
$this->resourceCollection = $resourceCollection;
$this->apiDocumentationBuilder = $apiDocumentationBuilder;
$this->classMetadataFactory = $classMetadataFactory;
}
public function getAnnotations()
{
$annotations = [];
$hydraDoc = $this->apiDocumentationBuilder->getApiDocumentation();
$entrypointHydraDoc = $this->getResourceHydraDoc($hydraDoc, '#Entrypoint');
/*
* @var ResourceInterface
*/
foreach ($this->resourceCollection as $resource) {
$classMetadata = $this->classMetadataFactory->getMetadataFor($resource->getEntityClass());
$prefixedShortName = ($iri = $classMetadata->getIri()) ? $iri : '#' . $resource->getShortName();
$resourceHydraDoc = $this->getResourceHydraDoc($hydraDoc, $prefixedShortName);
if ($hydraDoc) {
foreach ($resource->getCollectionOperations() as $operation) {
$annotations[] = $this->getApiDoc(true, $resource, $operation, $resourceHydraDoc, $entrypointHydraDoc);
}
foreach ($resource->getItemOperations() as $operation) {
$annotations[] = $this->getApiDoc(false, $resource, $operation, $resourceHydraDoc);
}
}
}
return $annotations;
}
/**
* Builds ApiDoc annotation from DunglasApiBundle data.
*
* @param bool $collection
*
* @return ApiDoc
*/
private function getApiDoc(
$collection,
ResourceInterface $resource,
OperationInterface $operation,
array $resourceHydraDoc,
array $entrypointHydraDoc = [],
) {
$method = $operation->getRoute()->getMethods()[0];
if ($collection) {
$operationHydraDoc = $this->getCollectionOperationHydraDoc($resource->getShortName(), $method, $entrypointHydraDoc);
} else {
$operationHydraDoc = $this->getOperationHydraDoc($operation->getRoute()->getMethods()[0], $resourceHydraDoc);
}
$route = $operation->getRoute();
$data = [
'resource' => $route->getPath(),
'description' => $operationHydraDoc['hydra:title'],
'resourceDescription' => $resourceHydraDoc['hydra:title'],
'section' => $resourceHydraDoc['hydra:title'],
];
$entityClass = $resource->getEntityClass();
if (isset($operationHydraDoc['expects']) && 'owl:Nothing' !== $operationHydraDoc['expects']) {
$data['input'] = sprintf('%s:%s', DunglasApiParser::IN_PREFIX, $entityClass);
}
if (isset($operationHydraDoc['returns']) && 'owl:Nothing' !== $operationHydraDoc['returns']) {
$data['output'] = sprintf('%s:%s', DunglasApiParser::OUT_PREFIX, $entityClass);
}
if (Request::METHOD_GET === $method && $collection) {
$data['filters'] = [];
foreach ($resource->getFilters() as $filter) {
foreach ($filter->getDescription($resource) as $name => $definition) {
$data['filters'][] = ['name' => $name] + $definition;
}
}
}
$apiDoc = new ApiDoc($data);
$apiDoc->setRoute($route);
return $apiDoc;
}
/**
* Gets Hydra documentation for the given resource.
*
* @param string $prefixedShortName
*
* @return array|null
*/
private function getResourceHydraDoc(array $hydraApiDoc, $prefixedShortName)
{
foreach ($hydraApiDoc['hydra:supportedClass'] as $supportedClass) {
if ($supportedClass['@id'] === $prefixedShortName) {
return $supportedClass;
}
}
}
/**
* Gets the Hydra documentation of a given operation.
*
* @param string $method
*
* @return array|null
*/
private function getOperationHydraDoc($method, array $hydraDoc)
{
foreach ($hydraDoc['hydra:supportedOperation'] as $supportedOperation) {
if ($supportedOperation['hydra:method'] === $method) {
return $supportedOperation;
}
}
}
/**
* Gets the Hydra documentation for the collection operation.
*
* @param string $shortName
* @param string $method
*
* @return array|null
*/
private function getCollectionOperationHydraDoc($shortName, $method, array $hydraEntrypointDoc)
{
$propertyName = '#Entrypoint/' . lcfirst($shortName);
foreach ($hydraEntrypointDoc['hydra:supportedProperty'] as $supportedProperty) {
$hydraProperty = $supportedProperty['hydra:property'];
if ($hydraProperty['@id'] === $propertyName) {
return $this->getOperationHydraDoc($method, $hydraProperty);
}
}
}
}

View File

@ -1,27 +0,0 @@
<?php
/*
* This file is part of the NelmioApiDocBundle.
*
* (c) Nelmio <hello@nelm.io>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Nelmio\ApiDocBundle\Extractor;
/**
* Interface for annotations providers.
*
* @author Kévin Dunglas <dunglas@gmail.com>
*/
interface AnnotationsProviderInterface
{
/**
* Returns an array ApiDoc annotations.
*
* @return \Nelmio\ApiDocBundle\Annotation\ApiDoc[]
*/
public function getAnnotations();
}

View File

@ -30,16 +30,14 @@ class ApiDocExtractor
protected array $parsers = []; protected array $parsers = [];
/** /**
* @param HandlerInterface[] $handlers * @param HandlerInterface[] $handlers
* @param AnnotationsProviderInterface[] $annotationsProviders * @param string[] $excludeSections
* @param string[] $excludeSections
*/ */
public function __construct( public function __construct(
protected RouterInterface $router, protected RouterInterface $router,
protected Reader $reader, protected Reader $reader,
protected DocCommentExtractor $commentExtractor, protected DocCommentExtractor $commentExtractor,
protected array $handlers, protected array $handlers,
protected array $annotationsProviders,
protected array $excludeSections, protected array $excludeSections,
) { ) {
} }
@ -129,13 +127,6 @@ class ApiDocExtractor
} }
} }
foreach ($this->annotationsProviders as $annotationProvider) {
foreach ($annotationProvider->getAnnotations() as $annotation) {
$route = $annotation->getRoute();
$array[] = ['annotation' => $this->extractData($annotation, $route, $this->getReflectionMethod($route->getDefault('_controller')))];
}
}
rsort($resources); rsort($resources);
foreach ($array as $index => $element) { foreach ($array as $index => $element) {
$hasResource = false; $hasResource = false;

View File

@ -26,22 +26,20 @@ use Symfony\Component\Routing\RouterInterface;
class CachingApiDocExtractor extends ApiDocExtractor class CachingApiDocExtractor extends ApiDocExtractor
{ {
/** /**
* @param HandlerInterface[] $handlers * @param HandlerInterface[] $handlers
* @param AnnotationsProviderInterface[] $annotationsProviders * @param string[] $excludeSections
* @param string[] $excludeSections * @param bool|false $debug
* @param bool|false $debug
*/ */
public function __construct( public function __construct(
RouterInterface $router, RouterInterface $router,
Reader $reader, Reader $reader,
DocCommentExtractor $commentExtractor, DocCommentExtractor $commentExtractor,
array $handlers, array $handlers,
array $annotationsProviders,
array $excludeSections, array $excludeSections,
private string $cacheFile, private string $cacheFile,
private bool $debug = false, private bool $debug = false,
) { ) {
parent::__construct($router, $reader, $commentExtractor, $handlers, $annotationsProviders, $excludeSections); parent::__construct($router, $reader, $commentExtractor, $handlers, $excludeSections);
} }
/** /**

View File

@ -2,7 +2,6 @@
namespace Nelmio\ApiDocBundle; namespace Nelmio\ApiDocBundle;
use Nelmio\ApiDocBundle\DependencyInjection\AnnotationsProviderCompilerPass;
use Nelmio\ApiDocBundle\DependencyInjection\ExtractorHandlerCompilerPass; use Nelmio\ApiDocBundle\DependencyInjection\ExtractorHandlerCompilerPass;
use Nelmio\ApiDocBundle\DependencyInjection\FormInfoParserCompilerPass; use Nelmio\ApiDocBundle\DependencyInjection\FormInfoParserCompilerPass;
use Nelmio\ApiDocBundle\DependencyInjection\LoadExtractorParsersPass; use Nelmio\ApiDocBundle\DependencyInjection\LoadExtractorParsersPass;
@ -20,7 +19,6 @@ class NelmioApiDocBundle extends Bundle
$container->addCompilerPass(new LoadExtractorParsersPass()); $container->addCompilerPass(new LoadExtractorParsersPass());
$container->addCompilerPass(new RegisterExtractorParsersPass()); $container->addCompilerPass(new RegisterExtractorParsersPass());
$container->addCompilerPass(new ExtractorHandlerCompilerPass()); $container->addCompilerPass(new ExtractorHandlerCompilerPass());
$container->addCompilerPass(new AnnotationsProviderCompilerPass());
$container->addCompilerPass(new SwaggerConfigCompilerPass()); $container->addCompilerPass(new SwaggerConfigCompilerPass());
$container->addCompilerPass(new FormInfoParserCompilerPass()); $container->addCompilerPass(new FormInfoParserCompilerPass());
} }

View File

@ -1,187 +0,0 @@
<?php
/*
* This file is part of the NelmioApiDocBundle.
*
* (c) Nelmio <hello@nelm.io>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Nelmio\ApiDocBundle\Parser;
use Dunglas\ApiBundle\Api\ResourceCollectionInterface;
use Dunglas\ApiBundle\Api\ResourceInterface;
use Dunglas\ApiBundle\Mapping\AttributeMetadataInterface;
use Dunglas\ApiBundle\Mapping\ClassMetadataFactoryInterface;
use Nelmio\ApiDocBundle\DataTypes;
use PropertyInfo\Type;
/**
* Use DunglasApiBundle to extract input and output information.
*
* @author Kévin Dunglas <dunglas@gmail.com>
*/
class DunglasApiParser implements ParserInterface
{
public const IN_PREFIX = 'dunglas_api_in';
public const OUT_PREFIX = 'dunglas_api_out';
public const IRI = 'IRI';
private static $typeMap = [
'int' => DataTypes::INTEGER,
'bool' => DataTypes::BOOLEAN,
'string' => DataTypes::STRING,
'float' => DataTypes::FLOAT,
];
/**
* @var ResourceCollectionInterface
*/
private $resourceCollection;
/**
* @var ClassMetadataFactory
*/
private $classMetadataFactory;
public function __construct(
ResourceCollectionInterface $resourceCollection,
ClassMetadataFactoryInterface $classMetadataFactory,
) {
$this->resourceCollection = $resourceCollection;
$this->classMetadataFactory = $classMetadataFactory;
}
public function supports(array $item)
{
$data = explode(':', $item['class'], 2);
if (isset($data[1])) {
return null !== $this->resourceCollection->getResourceForEntity($data[1]);
}
return false;
}
public function parse(array $item)
{
[$io, $entityClass] = explode(':', $item['class'], 2);
$resource = $this->resourceCollection->getResourceForEntity($entityClass);
return $this->parseClass($resource, $entityClass, $io);
}
/**
* Parses a class.
*
* @param string $entityClass
* @param string $io
* @param string[] $visited
*
* @return array
*/
private function parseClass(ResourceInterface $resource, $entityClass, $io, array $visited = [])
{
$visited[] = $entityClass;
$classMetadata = $this->classMetadataFactory->getMetadataFor(
$entityClass,
$resource->getNormalizationGroups(),
$resource->getDenormalizationGroups(),
$resource->getValidationGroups()
);
$data = [];
foreach ($classMetadata->getAttributes() as $attributeMetadata) {
if (
(!$attributeMetadata->isIdentifier() && $attributeMetadata->isReadable() && self::OUT_PREFIX === $io)
|| ($attributeMetadata->isWritable() && self::IN_PREFIX === $io)
) {
$data[$attributeMetadata->getName()] = $this->parseAttribute($resource, $attributeMetadata, $io, null, $visited);
}
}
return $data;
}
/**
* Parses an attribute.
*
* @param string $io
* @param string[] $visited
*
* @return array
*/
private function parseAttribute(ResourceInterface $resource, AttributeMetadataInterface $attributeMetadata, $io, ?Type $type = null, array $visited = [])
{
$data = [
'dataType' => null,
'required' => $attributeMetadata->isRequired(),
'description' => $attributeMetadata->getDescription(),
'readonly' => !$attributeMetadata->isWritable(),
];
if (null == $type) {
if (!isset($attributeMetadata->getTypes()[0])) {
// Default to string
$data['dataType'] = DataTypes::STRING;
return $data;
}
// Use the first type found as primary
$type = $attributeMetadata->getTypes()[0];
}
if ($type->isCollection()) {
$data['actualType'] = DataTypes::COLLECTION;
if ($collectionType = $type->getCollectionType()) {
$subAttribute = $this->parseAttribute($resource, $attributeMetadata, $io, $collectionType, $visited);
if (self::IRI === $subAttribute['dataType']) {
$data['dataType'] = 'array of IRIs';
$data['subType'] = DataTypes::STRING;
return $data;
}
$data['subType'] = $subAttribute['subType'];
$data['children'] = $subAttribute['children'];
}
return $data;
}
$phpType = $type->getType();
if ('object' === $phpType) {
$class = $type->getClass();
if ('DateTime' === $class) {
$data['dataType'] = DataTypes::DATETIME;
$data['format'] = sprintf('{DateTime %s}', \DateTime::ATOM);
return $data;
}
if (
(self::OUT_PREFIX === $io && $attributeMetadata->isNormalizationLink())
|| (self::IN_PREFIX === $io && $attributeMetadata->isDenormalizationLink())
) {
$data['dataType'] = self::IRI;
$data['actualType'] = DataTypes::STRING;
return $data;
}
$data['actualType'] = DataTypes::MODEL;
$data['subType'] = $class;
$data['children'] = in_array($class, $visited) ? [] : $this->parseClass($resource, $class, $io, $visited);
return $data;
}
$data['dataType'] = isset(self::$typeMap[$type->getType()]) ? self::$typeMap[$type->getType()] : DataTypes::STRING;
return $data;
}
}

View File

@ -1,23 +0,0 @@
<?xml version="1.0" ?>
<container xmlns="http://symfony.com/schema/dic/services"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://symfony.com/schema/dic/services http://symfony.com/schema/dic/services/services-1.0.xsd">
<services>
<service id="nelmio_api_doc.annotations_provider.dunglas_api_annotation_provider" class="Nelmio\ApiDocBundle\Extractor\AnnotationsProvider\DunglasApiProvider">
<argument type="service" id="api.resource_collection" />
<argument type="service" id="api.hydra.documentation_builder" />
<argument type="service" id="api.mapping.class_metadata_factory" />
<tag name="nelmio_api_doc.extractor.annotations_provider" />
</service>
<service id="nelmio_api_doc.parser.dunglas_api_parser" class="Nelmio\ApiDocBundle\Parser\DunglasApiParser">
<argument type="service" id="api.resource_collection" />
<argument type="service" id="api.mapping.class_metadata_factory" />
<tag name="nelmio_api_doc.extractor.parser" />
</service>
</services>
</container>

View File

@ -30,7 +30,6 @@
<argument type="service" id="annotation_reader" /> <argument type="service" id="annotation_reader" />
<argument type="service" id="nelmio_api_doc.doc_comment_extractor" /> <argument type="service" id="nelmio_api_doc.doc_comment_extractor" />
<argument type="collection" /> <argument type="collection" />
<argument type="collection" />
<argument>%nelmio_api_doc.exclude_sections%</argument> <argument>%nelmio_api_doc.exclude_sections%</argument>
</service> </service>

View File

@ -1,51 +0,0 @@
<?php
/*
* This file is part of the NelmioApiDocBundle.
*
* (c) Nelmio <hello@nelm.io>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace NelmioApiDocBundle\Tests\Parser;
use Nelmio\ApiDocBundle\DataTypes;
use Nelmio\ApiDocBundle\Parser\DunglasApiParser;
use Nelmio\ApiDocBundle\Tests\WebTestCase;
/**
* @author Kévin Dunglas <dunglas@gmail.com>
*/
class DunglasApiParserTest extends WebTestCase
{
protected function setUp(): void
{
if (!class_exists('Dunglas\ApiBundle\DunglasApiBundle')) {
$this->markTestSkipped(
'DunglasApiBundle is not available.'
);
}
}
public function testParser(): void
{
$container = $this->getContainer();
$parser = $container->get('nelmio_api_doc.parser.dunglas_api_parser');
$item = ['class' => DunglasApiParser::OUT_PREFIX . ':Nelmio\ApiDocBundle\Tests\Fixtures\Model\Popo'];
$expected = [
'foo' => [
'required' => false,
'description' => '',
'readonly' => false,
'dataType' => DataTypes::STRING,
],
];
$this->assertTrue($parser->supports($item));
$this->assertEquals($expected, $parser->parse($item));
}
}

View File

@ -46,7 +46,6 @@
"symfony/form": "For using form definitions as input.", "symfony/form": "For using form definitions as input.",
"symfony/validator": "For making use of validator information in the doc.", "symfony/validator": "For making use of validator information in the doc.",
"friendsofsymfony/rest-bundle": "For making use of REST information in the doc.", "friendsofsymfony/rest-bundle": "For making use of REST information in the doc.",
"dunglas/api-bundle": "For making use of resources definitions of DunglasApiBundle.",
"jms/serializer": "For making use of serializer information in the doc." "jms/serializer": "For making use of serializer information in the doc."
}, },
"autoload": { "autoload": {