mirror of
https://github.com/retailcrm/NelmioApiDocBundle.git
synced 2025-03-11 01:56:08 +03:00
Merge remote-tracking branch 'origin/master' into constraint_groups
This commit is contained in:
commit
fca94057d2
11
.github/workflows/continuous-integration.yml
vendored
11
.github/workflows/continuous-integration.yml
vendored
@ -34,6 +34,9 @@ jobs:
|
||||
symfony-require: "5.4.*"
|
||||
- php-version: 8.1
|
||||
symfony-require: "5.4.*"
|
||||
- php-version: 8.1
|
||||
symfony-require: "6.0.*"
|
||||
api-platform: "early"
|
||||
|
||||
steps:
|
||||
- name: "Checkout"
|
||||
@ -58,6 +61,14 @@ jobs:
|
||||
path: ${{ steps.composercache.outputs.dir }}
|
||||
key: ${{ runner.os }}-composer-${{ hashFiles('**/composer.json') }}
|
||||
restore-keys: ${{ runner.os }}-composer-
|
||||
|
||||
- name: "Use an early version of Api-platform with symfony 6 support"
|
||||
if: ${{ matrix.api-platform == 'early' }}
|
||||
env:
|
||||
SYMFONY_REQUIRE: "${{ matrix.symfony-require }}"
|
||||
run: |
|
||||
composer config repositories.api-platform git https://github.com/PierreRebeilleau/core.git
|
||||
composer require api-platform/core:dev-test-compatibility --no-update --dev
|
||||
|
||||
- name: "Install dependencies with composer"
|
||||
env:
|
||||
|
1
.gitignore
vendored
1
.gitignore
vendored
@ -9,3 +9,4 @@
|
||||
/.phpunit.result.cache
|
||||
/Tests/Functional/cache
|
||||
/Tests/Functional/logs
|
||||
.idea
|
||||
|
@ -14,6 +14,7 @@ namespace Nelmio\ApiDocBundle\Annotation;
|
||||
/**
|
||||
* @Annotation
|
||||
*/
|
||||
#[\Attribute(\Attribute::TARGET_METHOD)]
|
||||
final class Areas
|
||||
{
|
||||
/** @var string[] */
|
||||
@ -22,6 +23,10 @@ final class Areas
|
||||
public function __construct(array $properties)
|
||||
{
|
||||
if (!array_key_exists('value', $properties) || !is_array($properties['value'])) {
|
||||
$properties['value'] = array_values($properties);
|
||||
}
|
||||
|
||||
if ([] === $properties['value']) {
|
||||
throw new \InvalidArgumentException('An array of areas was expected');
|
||||
}
|
||||
|
||||
|
@ -13,10 +13,12 @@ namespace Nelmio\ApiDocBundle\Annotation;
|
||||
|
||||
use OpenApi\Annotations\AbstractAnnotation;
|
||||
use OpenApi\Annotations\Parameter;
|
||||
use OpenApi\Generator;
|
||||
|
||||
/**
|
||||
* @Annotation
|
||||
*/
|
||||
#[\Attribute(\Attribute::TARGET_METHOD)]
|
||||
final class Model extends AbstractAnnotation
|
||||
{
|
||||
/** {@inheritdoc} */
|
||||
@ -46,4 +48,22 @@ final class Model extends AbstractAnnotation
|
||||
* @var mixed[]
|
||||
*/
|
||||
public $options;
|
||||
|
||||
/**
|
||||
* @param mixed[] $properties
|
||||
* @param string[] $groups
|
||||
* @param mixed[] $options
|
||||
*/
|
||||
public function __construct(
|
||||
array $properties = [],
|
||||
string $type = Generator::UNDEFINED,
|
||||
array $groups = null,
|
||||
array $options = null
|
||||
) {
|
||||
parent::__construct($properties + [
|
||||
'type' => $type,
|
||||
'groups' => $groups,
|
||||
'options' => $options,
|
||||
]);
|
||||
}
|
||||
}
|
||||
|
@ -16,6 +16,7 @@ use OpenApi\Annotations\Operation as BaseOperation;
|
||||
/**
|
||||
* @Annotation
|
||||
*/
|
||||
#[\Attribute(\Attribute::TARGET_METHOD)]
|
||||
class Operation extends BaseOperation
|
||||
{
|
||||
}
|
||||
|
@ -16,6 +16,7 @@ use OpenApi\Annotations\AbstractAnnotation;
|
||||
/**
|
||||
* @Annotation
|
||||
*/
|
||||
#[\Attribute(\Attribute::TARGET_CLASS | \Attribute::TARGET_METHOD | \Attribute::IS_REPEATABLE)]
|
||||
class Security extends AbstractAnnotation
|
||||
{
|
||||
/** {@inheritdoc} */
|
||||
@ -35,4 +36,15 @@ class Security extends AbstractAnnotation
|
||||
* @var string[]
|
||||
*/
|
||||
public $scopes = [];
|
||||
|
||||
public function __construct(
|
||||
array $properties = [],
|
||||
string $name = null,
|
||||
array $scopes = []
|
||||
) {
|
||||
parent::__construct($properties + [
|
||||
'name' => $name,
|
||||
'scopes' => $scopes,
|
||||
]);
|
||||
}
|
||||
}
|
||||
|
@ -15,7 +15,6 @@ use Nelmio\ApiDocBundle\Describer\DescriberInterface;
|
||||
use Nelmio\ApiDocBundle\Describer\ModelRegistryAwareInterface;
|
||||
use Nelmio\ApiDocBundle\Model\ModelRegistry;
|
||||
use Nelmio\ApiDocBundle\ModelDescriber\ModelDescriberInterface;
|
||||
use Nelmio\ApiDocBundle\OpenApiPhp\DefaultOperationId;
|
||||
use Nelmio\ApiDocBundle\OpenApiPhp\ModelRegister;
|
||||
use Nelmio\ApiDocBundle\OpenApiPhp\Util;
|
||||
use OpenApi\Analysis;
|
||||
@ -84,6 +83,14 @@ final class ApiDocGenerator
|
||||
}
|
||||
}
|
||||
|
||||
$generator = new Generator();
|
||||
// Remove OperationId processor as we use a lot of generated annotations which do not have enough information in their context
|
||||
// to generate these ids properly.
|
||||
// @see https://github.com/zircote/swagger-php/issues/1153
|
||||
$generator->setProcessors(array_filter($generator->getProcessors(), function ($processor) {
|
||||
return !$processor instanceof \OpenApi\Processors\OperationId;
|
||||
}));
|
||||
|
||||
$this->openApi = new OpenApi([]);
|
||||
$modelRegistry = new ModelRegistry($this->modelDescribers, $this->openApi, $this->alternativeNames);
|
||||
if (null !== $this->logger) {
|
||||
@ -97,7 +104,12 @@ final class ApiDocGenerator
|
||||
$describer->describe($this->openApi);
|
||||
}
|
||||
|
||||
$context = Util::createContext();
|
||||
$context = Util::createContext(
|
||||
// BC for for zircote/swagger-php < 4.2
|
||||
method_exists($generator, 'getVersion')
|
||||
? ['version' => $generator->getVersion()]
|
||||
: []
|
||||
);
|
||||
$analysis = new Analysis([], $context);
|
||||
$analysis->addAnnotation($this->openApi, $context);
|
||||
|
||||
@ -108,10 +120,7 @@ final class ApiDocGenerator
|
||||
// Calculate the associated schemas
|
||||
$modelRegistry->registerSchemas();
|
||||
|
||||
$defaultOperationIdProcessor = new DefaultOperationId();
|
||||
$defaultOperationIdProcessor($analysis);
|
||||
|
||||
$analysis->process((new Generator())->getProcessors());
|
||||
$analysis->process($generator->getProcessors());
|
||||
$analysis->validate();
|
||||
|
||||
if (isset($item)) {
|
||||
|
@ -30,6 +30,9 @@ final class ApiPlatformDescriber extends ExternalDocDescriber
|
||||
[DocumentationNormalizer::SPEC_VERSION => 3]
|
||||
);
|
||||
|
||||
// TODO: remove this
|
||||
// Temporary fix: zircote/swagger-php does no longer support 3.0.x with x > 0
|
||||
unset($documentation['openapi']);
|
||||
unset($documentation['basePath']);
|
||||
|
||||
return $documentation;
|
||||
|
@ -17,6 +17,7 @@ use Nelmio\ApiDocBundle\Annotation\Security;
|
||||
use Nelmio\ApiDocBundle\OpenApiPhp\Util;
|
||||
use Nelmio\ApiDocBundle\Util\ControllerReflector;
|
||||
use Nelmio\ApiDocBundle\Util\SetsContextTrait;
|
||||
use OpenApi\Analysers\AttributeAnnotationFactory;
|
||||
use OpenApi\Annotations as OA;
|
||||
use OpenApi\Generator;
|
||||
use Psr\Log\LoggerInterface;
|
||||
@ -67,12 +68,14 @@ final class OpenApiPhpDescriber
|
||||
$classAnnotations = array_filter($this->annotationReader->getClassAnnotations($declaringClass), function ($v) {
|
||||
return $v instanceof OA\AbstractAnnotation;
|
||||
});
|
||||
$classAnnotations = array_merge($classAnnotations, $this->getAttributesAsAnnotation($declaringClass, $context));
|
||||
$classAnnotations[$declaringClass->getName()] = $classAnnotations;
|
||||
}
|
||||
|
||||
$annotations = array_filter($this->annotationReader->getMethodAnnotations($method), function ($v) {
|
||||
return $v instanceof OA\AbstractAnnotation;
|
||||
});
|
||||
$annotations = array_merge($annotations, $this->getAttributesAsAnnotation($method, $context));
|
||||
|
||||
if (0 === count($annotations) && 0 === count($classAnnotations[$declaringClass->getName()])) {
|
||||
continue;
|
||||
@ -107,6 +110,13 @@ final class OpenApiPhpDescriber
|
||||
|
||||
if ($annotation instanceof Security) {
|
||||
$annotation->validate();
|
||||
|
||||
if (null === $annotation->name) {
|
||||
$mergeProperties->security = [];
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
$mergeProperties->security[] = [$annotation->name => $annotation->scopes];
|
||||
|
||||
continue;
|
||||
@ -190,4 +200,24 @@ final class OpenApiPhpDescriber
|
||||
|
||||
return $path;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param \ReflectionClass|\ReflectionMethod $reflection
|
||||
*
|
||||
* @return OA\AbstractAnnotation[]
|
||||
*/
|
||||
private function getAttributesAsAnnotation($reflection, \OpenApi\Context $context): array
|
||||
{
|
||||
// BC zircote/swagger-php < 4.0
|
||||
if (!class_exists(AttributeAnnotationFactory::class)) {
|
||||
return [];
|
||||
}
|
||||
|
||||
$attributesFactory = new AttributeAnnotationFactory();
|
||||
$attributes = $attributesFactory->build($reflection, $context);
|
||||
// The attributes factory removes the context after executing so we need to set it back...
|
||||
$this->setContext($context);
|
||||
|
||||
return $attributes;
|
||||
}
|
||||
}
|
||||
|
@ -39,8 +39,8 @@ class OpenApiAnnotationsReader
|
||||
|
||||
public function updateSchema(\ReflectionClass $reflectionClass, OA\Schema $schema): void
|
||||
{
|
||||
/** @var OA\Schema $oaSchema */
|
||||
if (!$oaSchema = $this->annotationsReader->getClassAnnotation($reflectionClass, OA\Schema::class)) {
|
||||
/** @var OA\Schema|null $oaSchema */
|
||||
if (!$oaSchema = $this->getAnnotation($reflectionClass, OA\Schema::class)) {
|
||||
return;
|
||||
}
|
||||
|
||||
@ -56,10 +56,8 @@ class OpenApiAnnotationsReader
|
||||
|
||||
public function getPropertyName($reflection, string $default): string
|
||||
{
|
||||
/** @var OA\Property $oaProperty */
|
||||
if ($reflection instanceof \ReflectionProperty && !$oaProperty = $this->annotationsReader->getPropertyAnnotation($reflection, OA\Property::class)) {
|
||||
return $default;
|
||||
} elseif ($reflection instanceof \ReflectionMethod && !$oaProperty = $this->annotationsReader->getMethodAnnotation($reflection, OA\Property::class)) {
|
||||
/** @var OA\Property|null $oaProperty */
|
||||
if (!$oaProperty = $this->getAnnotation($reflection, OA\Property::class)) {
|
||||
return $default;
|
||||
}
|
||||
|
||||
@ -78,10 +76,8 @@ class OpenApiAnnotationsReader
|
||||
'filename' => $declaringClass->getFileName(),
|
||||
]));
|
||||
|
||||
/** @var OA\Property $oaProperty */
|
||||
if ($reflection instanceof \ReflectionProperty && !$oaProperty = $this->annotationsReader->getPropertyAnnotation($reflection, OA\Property::class)) {
|
||||
return;
|
||||
} elseif ($reflection instanceof \ReflectionMethod && !$oaProperty = $this->annotationsReader->getMethodAnnotation($reflection, OA\Property::class)) {
|
||||
/** @var OA\Property|null $oaProperty */
|
||||
if (!$oaProperty = $this->getAnnotation($reflection, OA\Property::class)) {
|
||||
return;
|
||||
}
|
||||
$this->setContext(null);
|
||||
@ -95,4 +91,28 @@ class OpenApiAnnotationsReader
|
||||
|
||||
$property->mergeProperties($oaProperty);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param \ReflectionClass|\ReflectionProperty|\ReflectionMethod $reflection
|
||||
*
|
||||
* @return mixed
|
||||
*/
|
||||
private function getAnnotation($reflection, string $className)
|
||||
{
|
||||
if (\PHP_VERSION_ID >= 80100) {
|
||||
if (null !== $attribute = $reflection->getAttributes($className, \ReflectionAttribute::IS_INSTANCEOF)[0] ?? null) {
|
||||
return $attribute->newInstance();
|
||||
}
|
||||
}
|
||||
|
||||
if ($reflection instanceof \ReflectionClass) {
|
||||
return $this->annotationsReader->getClassAnnotation($reflection, $className);
|
||||
} elseif ($reflection instanceof \ReflectionProperty) {
|
||||
return $this->annotationsReader->getPropertyAnnotation($reflection, $className);
|
||||
} elseif ($reflection instanceof \ReflectionMethod) {
|
||||
return $this->annotationsReader->getMethodAnnotation($reflection, $className);
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
@ -81,7 +81,7 @@ class ObjectModelDescriber implements ModelDescriberInterface, ModelRegistryAwar
|
||||
);
|
||||
$annotationsReader->updateDefinition($reflClass, $schema);
|
||||
|
||||
$discriminatorMap = $this->doctrineReader->getClassAnnotation($reflClass, DiscriminatorMap::class);
|
||||
$discriminatorMap = $this->getAnnotation($reflClass, DiscriminatorMap::class);
|
||||
if ($discriminatorMap && Generator::UNDEFINED === $schema->discriminator) {
|
||||
$this->applyOpenApiDiscriminator(
|
||||
$model,
|
||||
@ -192,6 +192,23 @@ class ObjectModelDescriber implements ModelDescriberInterface, ModelRegistryAwar
|
||||
throw new \Exception(sprintf('Type "%s" is not supported in %s::$%s. You may use the `@OA\Property(type="")` annotation to specify it manually.', $types[0]->getBuiltinType(), $model->getType()->getClassName(), $propertyName));
|
||||
}
|
||||
|
||||
/**
|
||||
* @return mixed
|
||||
*/
|
||||
private function getAnnotation(\ReflectionClass $reflection, string $className)
|
||||
{
|
||||
if (false === class_exists($className)) {
|
||||
return null;
|
||||
}
|
||||
if (\PHP_VERSION_ID >= 80000) {
|
||||
if (null !== $attribute = $reflection->getAttributes($className, \ReflectionAttribute::IS_INSTANCEOF)[0] ?? null) {
|
||||
return $attribute->newInstance();
|
||||
}
|
||||
}
|
||||
|
||||
return $this->doctrineReader->getClassAnnotation($reflection, $className);
|
||||
}
|
||||
|
||||
public function supports(Model $model): bool
|
||||
{
|
||||
return Type::BUILTIN_TYPE_OBJECT === $model->getType()->getBuiltinType() && class_exists($model->getType()->getClassName());
|
||||
|
@ -1,36 +0,0 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the NelmioApiDocBundle package.
|
||||
*
|
||||
* (c) Nelmio
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Nelmio\ApiDocBundle\OpenApiPhp;
|
||||
|
||||
use OpenApi\Analysis;
|
||||
use OpenApi\Annotations as OA;
|
||||
use OpenApi\Generator;
|
||||
|
||||
/**
|
||||
* Disable the OperationId processor from zircote/swagger-php as it breaks our documentation by setting non-unique operation ids.
|
||||
* See https://github.com/zircote/swagger-php/pull/483#issuecomment-360739260 for the solution used here.
|
||||
*
|
||||
* @internal
|
||||
*/
|
||||
final class DefaultOperationId
|
||||
{
|
||||
public function __invoke(Analysis $analysis)
|
||||
{
|
||||
$allOperations = $analysis->getAnnotationsOfType(OA\Operation::class);
|
||||
|
||||
foreach ($allOperations as $operation) {
|
||||
if (Generator::UNDEFINED === $operation->operationId) {
|
||||
$operation->operationId = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -316,8 +316,6 @@ final class Util
|
||||
*/
|
||||
public static function createContext(array $properties = [], Context $parent = null): Context
|
||||
{
|
||||
$properties['comment'] = ''; // TODO: remove this when https://github.com/zircote/swagger-php/commit/708a25208797ca05ebeae572bbccad8b13de14d8 is released
|
||||
|
||||
return new Context($properties, $parent);
|
||||
}
|
||||
|
||||
|
@ -341,6 +341,7 @@ If you need more complex features, take a look at:
|
||||
customization
|
||||
commands
|
||||
faq
|
||||
security
|
||||
|
||||
.. _`Symfony PropertyInfo component`: https://symfony.com/doc/current/components/property_info.html
|
||||
.. _`willdurand/Hateoas`: https://github.com/willdurand/Hateoas
|
||||
|
43
Resources/doc/security.rst
Normal file
43
Resources/doc/security.rst
Normal file
@ -0,0 +1,43 @@
|
||||
Security
|
||||
========
|
||||
|
||||
A default security policy can be added in ``nelmio_api_doc.documentation.security``
|
||||
|
||||
.. code-block:: yaml
|
||||
|
||||
nelmio_api_doc:
|
||||
documentation:
|
||||
components:
|
||||
securitySchemes:
|
||||
Bearer:
|
||||
type: http
|
||||
scheme: bearer
|
||||
ApiKeyAuth:
|
||||
type: apiKey
|
||||
in: header
|
||||
name: X-API-Key
|
||||
security:
|
||||
Bearer: []
|
||||
|
||||
This will add the Bearer security policy to all registered paths.
|
||||
|
||||
Overriding Specific Paths
|
||||
-------------------------
|
||||
|
||||
The security policy can be overriden for a path using the ``@Security`` annotation.
|
||||
|
||||
.. code-block:: php
|
||||
|
||||
/**
|
||||
* @Security(name="ApiKeyAuth")
|
||||
*/
|
||||
|
||||
Notice at the bottom of the docblock is a ``@Security`` annotation with a name of `ApiKeyAuth`. This will override the global security policy to only accept the ``ApiKeyAuth`` policy for this path.
|
||||
|
||||
You can also completely remove security from a path by providing ``@Security`` with a name of ``null``.
|
||||
|
||||
.. code-block:: php
|
||||
|
||||
/**
|
||||
* @Security(name=null)
|
||||
*/
|
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
@ -44,6 +44,8 @@ final class FosRestDescriber implements RouteDescriberInterface
|
||||
$annotations = array_filter($annotations, static function ($value) {
|
||||
return $value instanceof RequestParam || $value instanceof QueryParam;
|
||||
});
|
||||
$annotations = array_merge($annotations, $this->getAttributesAsAnnotation($reflectionMethod, RequestParam::class));
|
||||
$annotations = array_merge($annotations, $this->getAttributesAsAnnotation($reflectionMethod, QueryParam::class));
|
||||
|
||||
foreach ($this->getOperations($api, $route) as $operation) {
|
||||
foreach ($annotations as $annotation) {
|
||||
@ -185,4 +187,21 @@ final class FosRestDescriber implements RouteDescriberInterface
|
||||
$schema->format = $format;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @return OA\AbstractAnnotation[]
|
||||
*/
|
||||
private function getAttributesAsAnnotation(\ReflectionMethod $reflection, string $className): array
|
||||
{
|
||||
$annotations = [];
|
||||
if (\PHP_VERSION_ID < 80100) {
|
||||
return $annotations;
|
||||
}
|
||||
|
||||
foreach ($reflection->getAttributes($className, \ReflectionAttribute::IS_INSTANCEOF) as $attribute) {
|
||||
$annotations[] = $attribute->newInstance();
|
||||
}
|
||||
|
||||
return $annotations;
|
||||
}
|
||||
}
|
||||
|
@ -130,13 +130,23 @@ final class FilteredRouteCollectionBuilder
|
||||
}
|
||||
|
||||
/** @var Areas|null $areas */
|
||||
$areas = $this->annotationReader->getMethodAnnotation(
|
||||
$reflectionMethod,
|
||||
Areas::class
|
||||
);
|
||||
$areas = $this->getAttributesAsAnnotation($reflectionMethod, Areas::class)[0] ?? null;
|
||||
|
||||
if (null === $areas) {
|
||||
$areas = $this->annotationReader->getClassAnnotation($reflectionMethod->getDeclaringClass(), Areas::class);
|
||||
/** @var Areas|null $areas */
|
||||
$areas = $this->getAttributesAsAnnotation($reflectionMethod->getDeclaringClass(), Areas::class)[0] ?? null;
|
||||
|
||||
if (null === $areas) {
|
||||
/** @var Areas|null $areas */
|
||||
$areas = $this->annotationReader->getMethodAnnotation(
|
||||
$reflectionMethod,
|
||||
Areas::class
|
||||
);
|
||||
|
||||
if (null === $areas) {
|
||||
$areas = $this->annotationReader->getClassAnnotation($reflectionMethod->getDeclaringClass(), Areas::class);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return (null !== $areas) ? $areas->has($this->area) : false;
|
||||
@ -168,4 +178,23 @@ final class FilteredRouteCollectionBuilder
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param \ReflectionClass|\ReflectionMethod $reflection
|
||||
*
|
||||
* @return Areas[]
|
||||
*/
|
||||
private function getAttributesAsAnnotation($reflection, string $className): array
|
||||
{
|
||||
$annotations = [];
|
||||
if (\PHP_VERSION_ID < 80100) {
|
||||
return $annotations;
|
||||
}
|
||||
|
||||
foreach ($reflection->getAttributes($className, \ReflectionAttribute::IS_INSTANCEOF) as $attribute) {
|
||||
$annotations[] = $attribute->newInstance();
|
||||
}
|
||||
|
||||
return $annotations;
|
||||
}
|
||||
}
|
||||
|
@ -11,257 +11,20 @@
|
||||
|
||||
namespace Nelmio\ApiDocBundle\Tests\Functional\Controller;
|
||||
|
||||
use Nelmio\ApiDocBundle\Annotation\Areas;
|
||||
use Nelmio\ApiDocBundle\Annotation\Model;
|
||||
use Nelmio\ApiDocBundle\Annotation\Operation;
|
||||
use Nelmio\ApiDocBundle\Annotation\Security;
|
||||
use Nelmio\ApiDocBundle\Tests\Functional\Entity\Article;
|
||||
use Nelmio\ApiDocBundle\Tests\Functional\Entity\CompoundEntity;
|
||||
use Nelmio\ApiDocBundle\Tests\Functional\Entity\SymfonyConstraints;
|
||||
use Nelmio\ApiDocBundle\Tests\Functional\Entity\SymfonyConstraintsWithValidationGroups;
|
||||
use Nelmio\ApiDocBundle\Tests\Functional\Entity\SymfonyDiscriminator;
|
||||
use Nelmio\ApiDocBundle\Tests\Functional\Entity\User;
|
||||
use Nelmio\ApiDocBundle\Tests\Functional\Form\DummyType;
|
||||
use Nelmio\ApiDocBundle\Tests\Functional\Form\UserType;
|
||||
use OpenApi\Annotations as OA;
|
||||
use Symfony\Component\Routing\Annotation\Route;
|
||||
|
||||
/**
|
||||
* @Route("/api", name="api_", host="api.example.com")
|
||||
*/
|
||||
class ApiController
|
||||
{
|
||||
if (\PHP_VERSION_ID >= 80100) {
|
||||
/**
|
||||
* @OA\Get(
|
||||
* @OA\Response(
|
||||
* response="200",
|
||||
* description="Success",
|
||||
* @Model(type=Article::class, groups={"light"}))
|
||||
* )
|
||||
* )
|
||||
* @OA\Parameter(ref="#/components/parameters/test")
|
||||
* @Route("/article/{id}", methods={"GET"})
|
||||
* @OA\Parameter(name="Accept-Version", in="header", @OA\Schema(type="string"))
|
||||
* @OA\Parameter(name="Application-Name", in="header", @OA\Schema(type="string"))
|
||||
* @Route("/api", name="api_", host="api.example.com")
|
||||
*/
|
||||
public function fetchArticleAction()
|
||||
class ApiController extends ApiController81
|
||||
{
|
||||
}
|
||||
|
||||
} else {
|
||||
/**
|
||||
* The method LINK is not supported by OpenAPI so the method will be ignored.
|
||||
*
|
||||
* @Route("/swagger", methods={"GET", "LINK"})
|
||||
* @Route("/swagger2", methods={"GET"})
|
||||
* @Operation(
|
||||
* @OA\Response(response="201", description="An example resource")
|
||||
* )
|
||||
* @OA\Get(
|
||||
* path="/api/swagger2",
|
||||
* @OA\Parameter(name="Accept-Version", in="header", @OA\Schema(type="string"))
|
||||
* )
|
||||
* @OA\Post(
|
||||
* path="/api/swagger2",
|
||||
* @OA\Response(response="203", description="but 203 is not actually allowed (wrong method)")
|
||||
* )
|
||||
* @Route("/api", name="api_", host="api.example.com")
|
||||
*/
|
||||
public function swaggerAction()
|
||||
{
|
||||
}
|
||||
|
||||
/**
|
||||
* @Route("/swagger/implicit", methods={"GET", "POST"})
|
||||
* @OA\Response(
|
||||
* response="201",
|
||||
* description="Operation automatically detected",
|
||||
* @Model(type=User::class)
|
||||
* ),
|
||||
* @OA\RequestBody(
|
||||
* description="This is a request body",
|
||||
* @OA\JsonContent(
|
||||
* type="array",
|
||||
* @OA\Items(ref=@Model(type=User::class))
|
||||
* )
|
||||
* )
|
||||
* @OA\Tag(name="implicit")
|
||||
*/
|
||||
public function implicitSwaggerAction()
|
||||
{
|
||||
}
|
||||
|
||||
/**
|
||||
* @Route("/test/users/{user}", methods={"POST"}, schemes={"https"}, requirements={"user"="/foo/"})
|
||||
* @OA\Response(
|
||||
* response="201",
|
||||
* description="Operation automatically detected",
|
||||
* @Model(type=User::class)
|
||||
* ),
|
||||
* @OA\RequestBody(
|
||||
* description="This is a request body",
|
||||
* @Model(type=UserType::class, options={"bar": "baz"}))
|
||||
* )
|
||||
*/
|
||||
public function submitUserTypeAction()
|
||||
{
|
||||
}
|
||||
|
||||
/**
|
||||
* @Route("/test/{user}", methods={"GET"}, schemes={"https"}, requirements={"user"="/foo/"})
|
||||
* @OA\Response(response=200, description="sucessful")
|
||||
*/
|
||||
public function userAction()
|
||||
{
|
||||
}
|
||||
|
||||
/**
|
||||
* This action is deprecated.
|
||||
*
|
||||
* Please do not use this action.
|
||||
*
|
||||
* @Route("/deprecated", methods={"GET"})
|
||||
*
|
||||
* @deprecated
|
||||
*/
|
||||
public function deprecatedAction()
|
||||
{
|
||||
}
|
||||
|
||||
/**
|
||||
* This action is not documented. It is excluded by the config.
|
||||
*
|
||||
* @Route("/admin", methods={"GET"})
|
||||
*/
|
||||
public function adminAction()
|
||||
{
|
||||
}
|
||||
|
||||
/**
|
||||
* @OA\Get(
|
||||
* path="/filtered",
|
||||
* @OA\Response(response="201", description="")
|
||||
* )
|
||||
*/
|
||||
public function filteredAction()
|
||||
{
|
||||
}
|
||||
|
||||
/**
|
||||
* @Route("/form", methods={"POST"})
|
||||
* @OA\RequestBody(
|
||||
* description="Request content",
|
||||
* @Model(type=DummyType::class))
|
||||
* )
|
||||
* @OA\Response(response="201", description="")
|
||||
*/
|
||||
public function formAction()
|
||||
{
|
||||
}
|
||||
|
||||
/**
|
||||
* @Route("/security")
|
||||
* @OA\Response(response="201", description="")
|
||||
* @Security(name="api_key")
|
||||
* @Security(name="basic")
|
||||
* @Security(name="oauth2", scopes={"scope_1"})
|
||||
*/
|
||||
public function securityAction()
|
||||
{
|
||||
}
|
||||
|
||||
/**
|
||||
* @Route("/swagger/symfonyConstraints", methods={"GET"})
|
||||
* @OA\Response(
|
||||
* response="201",
|
||||
* description="Used for symfony constraints test",
|
||||
* @Model(type=SymfonyConstraints::class)
|
||||
* )
|
||||
*/
|
||||
public function symfonyConstraintsAction()
|
||||
{
|
||||
}
|
||||
|
||||
/**
|
||||
* @Route("/swagger/symfonyConstraintsWithValidationGroups", methods={"GET"})
|
||||
* @OA\Response(
|
||||
* response="201",
|
||||
* description="Used for symfony constraints with validation groups test",
|
||||
* @Model(type=SymfonyConstraintsWithValidationGroups::class, groups={"test"})
|
||||
* )
|
||||
*/
|
||||
public function symfonyConstraintsWithGroupsAction()
|
||||
{
|
||||
}
|
||||
|
||||
/**
|
||||
* @OA\Response(
|
||||
* response="200",
|
||||
* description="Success",
|
||||
* ref="#/components/schemas/Test"
|
||||
* ),
|
||||
* @OA\Response(
|
||||
* response="201",
|
||||
* ref="#/components/responses/201"
|
||||
* )
|
||||
* @Route("/configReference", methods={"GET"})
|
||||
*/
|
||||
public function configReferenceAction()
|
||||
{
|
||||
}
|
||||
|
||||
/**
|
||||
* @Route("/multi-annotations", methods={"GET", "POST"})
|
||||
* @OA\Get(description="This is the get operation")
|
||||
* @OA\Post(description="This is post")
|
||||
*
|
||||
* @OA\Response(response=200, description="Worked well!", @Model(type=DummyType::class))
|
||||
*/
|
||||
public function operationsWithOtherAnnotations()
|
||||
{
|
||||
}
|
||||
|
||||
/**
|
||||
* @Route("/areas/new", methods={"GET", "POST"})
|
||||
*
|
||||
* @Areas({"area", "area2"})
|
||||
*/
|
||||
public function newAreaAction()
|
||||
{
|
||||
}
|
||||
|
||||
/**
|
||||
* @Route("/compound", methods={"GET", "POST"})
|
||||
*
|
||||
* @OA\Response(response=200, description="Worked well!", @Model(type=CompoundEntity::class))
|
||||
*/
|
||||
public function compoundEntityAction()
|
||||
{
|
||||
}
|
||||
|
||||
/**
|
||||
* @Route("/discriminator-mapping", methods={"GET", "POST"})
|
||||
*
|
||||
* @OA\Response(response=200, description="Worked well!", @Model(type=SymfonyDiscriminator::class))
|
||||
*/
|
||||
public function discriminatorMappingAction()
|
||||
{
|
||||
}
|
||||
|
||||
/**
|
||||
* @Route("/named_route-operation-id", name="named_route_operation_id", methods={"GET", "POST"})
|
||||
*
|
||||
* @OA\Response(response=200, description="success")
|
||||
*/
|
||||
public function namedRouteOperationIdAction()
|
||||
{
|
||||
}
|
||||
|
||||
/**
|
||||
* @Route("/custom-operation-id", methods={"GET", "POST"})
|
||||
*
|
||||
* @Operation(operationId="custom-operation-id")
|
||||
* @OA\Response(response=200, description="success")
|
||||
*/
|
||||
public function customOperationIdAction()
|
||||
class ApiController extends ApiController80
|
||||
{
|
||||
}
|
||||
}
|
||||
|
275
Tests/Functional/Controller/ApiController80.php
Normal file
275
Tests/Functional/Controller/ApiController80.php
Normal file
@ -0,0 +1,275 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the NelmioApiDocBundle package.
|
||||
*
|
||||
* (c) Nelmio
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Nelmio\ApiDocBundle\Tests\Functional\Controller;
|
||||
|
||||
use Nelmio\ApiDocBundle\Annotation\Areas;
|
||||
use Nelmio\ApiDocBundle\Annotation\Model;
|
||||
use Nelmio\ApiDocBundle\Annotation\Operation;
|
||||
use Nelmio\ApiDocBundle\Annotation\Security;
|
||||
use Nelmio\ApiDocBundle\Tests\Functional\Entity\Article;
|
||||
use Nelmio\ApiDocBundle\Tests\Functional\Entity\CompoundEntity;
|
||||
use Nelmio\ApiDocBundle\Tests\Functional\Entity\SymfonyConstraints;
|
||||
use Nelmio\ApiDocBundle\Tests\Functional\Entity\SymfonyConstraintsWithValidationGroups;
|
||||
use Nelmio\ApiDocBundle\Tests\Functional\Entity\SymfonyDiscriminator;
|
||||
use Nelmio\ApiDocBundle\Tests\Functional\Entity\User;
|
||||
use Nelmio\ApiDocBundle\Tests\Functional\Form\DummyType;
|
||||
use Nelmio\ApiDocBundle\Tests\Functional\Form\UserType;
|
||||
use OpenApi\Annotations as OA;
|
||||
use Symfony\Component\Routing\Annotation\Route;
|
||||
|
||||
class ApiController80
|
||||
{
|
||||
/**
|
||||
* @OA\Get(
|
||||
* @OA\Response(
|
||||
* response="200",
|
||||
* description="Success",
|
||||
* @Model(type=Article::class, groups={"light"}))
|
||||
* )
|
||||
* )
|
||||
* @OA\Parameter(ref="#/components/parameters/test")
|
||||
* @Route("/article/{id}", methods={"GET"})
|
||||
* @OA\Parameter(name="Accept-Version", in="header", @OA\Schema(type="string"))
|
||||
* @OA\Parameter(name="Application-Name", in="header", @OA\Schema(type="string"))
|
||||
*/
|
||||
public function fetchArticleAction()
|
||||
{
|
||||
}
|
||||
|
||||
/**
|
||||
* The method LINK is not supported by OpenAPI so the method will be ignored.
|
||||
*
|
||||
* @Route("/swagger", methods={"GET", "LINK"})
|
||||
* @Route("/swagger2", methods={"GET"})
|
||||
* @Operation(
|
||||
* @OA\Response(response="201", description="An example resource")
|
||||
* )
|
||||
* @OA\Get(
|
||||
* path="/api/swagger2",
|
||||
* @OA\Parameter(name="Accept-Version", in="header", @OA\Schema(type="string"))
|
||||
* )
|
||||
* @OA\Post(
|
||||
* path="/api/swagger2",
|
||||
* @OA\Response(response="203", description="but 203 is not actually allowed (wrong method)")
|
||||
* )
|
||||
*/
|
||||
public function swaggerAction()
|
||||
{
|
||||
}
|
||||
|
||||
/**
|
||||
* @Route("/swagger/implicit", methods={"GET", "POST"})
|
||||
* @OA\Response(
|
||||
* response="201",
|
||||
* description="Operation automatically detected",
|
||||
* @Model(type=User::class)
|
||||
* ),
|
||||
* @OA\RequestBody(
|
||||
* description="This is a request body",
|
||||
* @OA\JsonContent(
|
||||
* type="array",
|
||||
* @OA\Items(ref=@Model(type=User::class))
|
||||
* )
|
||||
* )
|
||||
* @OA\Tag(name="implicit")
|
||||
*/
|
||||
public function implicitSwaggerAction()
|
||||
{
|
||||
}
|
||||
|
||||
/**
|
||||
* @Route("/test/users/{user}", methods={"POST"}, schemes={"https"}, requirements={"user"="/foo/"})
|
||||
* @OA\Response(
|
||||
* response="201",
|
||||
* description="Operation automatically detected",
|
||||
* @Model(type=User::class)
|
||||
* ),
|
||||
* @OA\RequestBody(
|
||||
* description="This is a request body",
|
||||
* @Model(type=UserType::class, options={"bar": "baz"}))
|
||||
* )
|
||||
*/
|
||||
public function submitUserTypeAction()
|
||||
{
|
||||
}
|
||||
|
||||
/**
|
||||
* @Route("/test/{user}", methods={"GET"}, schemes={"https"}, requirements={"user"="/foo/"})
|
||||
* @OA\Response(response=200, description="sucessful")
|
||||
*/
|
||||
public function userAction()
|
||||
{
|
||||
}
|
||||
|
||||
/**
|
||||
* This action is deprecated.
|
||||
*
|
||||
* Please do not use this action.
|
||||
*
|
||||
* @Route("/deprecated", methods={"GET"})
|
||||
*
|
||||
* @deprecated
|
||||
*/
|
||||
public function deprecatedAction()
|
||||
{
|
||||
}
|
||||
|
||||
/**
|
||||
* This action is not documented. It is excluded by the config.
|
||||
*
|
||||
* @Route("/admin", methods={"GET"})
|
||||
*/
|
||||
public function adminAction()
|
||||
{
|
||||
}
|
||||
|
||||
/**
|
||||
* @OA\Get(
|
||||
* path="/filtered",
|
||||
* @OA\Response(response="201", description="")
|
||||
* )
|
||||
*/
|
||||
public function filteredAction()
|
||||
{
|
||||
}
|
||||
|
||||
/**
|
||||
* @Route("/form", methods={"POST"})
|
||||
* @OA\RequestBody(
|
||||
* description="Request content",
|
||||
* @Model(type=DummyType::class))
|
||||
* )
|
||||
* @OA\Response(response="201", description="")
|
||||
*/
|
||||
public function formAction()
|
||||
{
|
||||
}
|
||||
|
||||
/**
|
||||
* @Route("/security")
|
||||
* @OA\Response(response="201", description="")
|
||||
* @Security(name="api_key")
|
||||
* @Security(name="basic")
|
||||
* @Security(name="oauth2", scopes={"scope_1"})
|
||||
*/
|
||||
public function securityAction()
|
||||
{
|
||||
}
|
||||
|
||||
/**
|
||||
* @Route("/securityOverride")
|
||||
* @OA\Response(response="201", description="")
|
||||
* @Security(name="api_key")
|
||||
* @Security(name=null)
|
||||
*/
|
||||
public function securityActionOverride()
|
||||
{
|
||||
}
|
||||
|
||||
/**
|
||||
* @Route("/swagger/symfonyConstraints", methods={"GET"})
|
||||
* @OA\Response(
|
||||
* response="201",
|
||||
* description="Used for symfony constraints test",
|
||||
* @Model(type=SymfonyConstraints::class)
|
||||
* )
|
||||
*/
|
||||
public function symfonyConstraintsAction()
|
||||
{
|
||||
}
|
||||
|
||||
/**
|
||||
* @OA\Response(
|
||||
* response="200",
|
||||
* description="Success",
|
||||
* ref="#/components/schemas/Test"
|
||||
* ),
|
||||
* @OA\Response(
|
||||
* response="201",
|
||||
* ref="#/components/responses/201"
|
||||
* )
|
||||
* @Route("/configReference", methods={"GET"})
|
||||
*/
|
||||
public function configReferenceAction()
|
||||
{
|
||||
}
|
||||
|
||||
/**
|
||||
* @Route("/multi-annotations", methods={"GET", "POST"})
|
||||
* @OA\Get(description="This is the get operation")
|
||||
* @OA\Post(description="This is post")
|
||||
*
|
||||
* @OA\Response(response=200, description="Worked well!", @Model(type=DummyType::class))
|
||||
*/
|
||||
public function operationsWithOtherAnnotations()
|
||||
{
|
||||
}
|
||||
|
||||
/**
|
||||
* @Route("/areas/new", methods={"GET", "POST"})
|
||||
*
|
||||
* @Areas({"area", "area2"})
|
||||
*/
|
||||
public function newAreaAction()
|
||||
{
|
||||
}
|
||||
|
||||
/**
|
||||
* @Route("/compound", methods={"GET", "POST"})
|
||||
*
|
||||
* @OA\Response(response=200, description="Worked well!", @Model(type=CompoundEntity::class))
|
||||
*/
|
||||
public function compoundEntityAction()
|
||||
{
|
||||
}
|
||||
|
||||
/**
|
||||
* @Route("/discriminator-mapping", methods={"GET", "POST"})
|
||||
*
|
||||
* @OA\Response(response=200, description="Worked well!", @Model(type=SymfonyDiscriminator::class))
|
||||
*/
|
||||
public function discriminatorMappingAction()
|
||||
{
|
||||
}
|
||||
|
||||
/**
|
||||
* @Route("/named_route-operation-id", name="named_route_operation_id", methods={"GET", "POST"})
|
||||
*
|
||||
* @OA\Response(response=200, description="success")
|
||||
*/
|
||||
public function namedRouteOperationIdAction()
|
||||
{
|
||||
}
|
||||
|
||||
/**
|
||||
* @Route("/custom-operation-id", methods={"GET", "POST"})
|
||||
*
|
||||
* @OA\Get(operationId="get-custom-operation-id")
|
||||
* @OA\Post(operationId="post-custom-operation-id")
|
||||
* @OA\Response(response=200, description="success")
|
||||
*/
|
||||
public function customOperationIdAction()
|
||||
{
|
||||
}
|
||||
|
||||
/**
|
||||
* @Route("/swagger/symfonyConstraintsWithValidationGroups", methods={"GET"})
|
||||
* @OA\Response(
|
||||
* response="201",
|
||||
* description="Used for symfony constraints with validation groups test",
|
||||
* @Model(type=SymfonyConstraintsWithValidationGroups::class, groups={"test"})
|
||||
* )
|
||||
*/
|
||||
public function symfonyConstraintsWithGroupsAction()
|
||||
{
|
||||
}
|
||||
}
|
68
Tests/Functional/Controller/ApiController81.php
Normal file
68
Tests/Functional/Controller/ApiController81.php
Normal file
@ -0,0 +1,68 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the NelmioApiDocBundle package.
|
||||
*
|
||||
* (c) Nelmio
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Nelmio\ApiDocBundle\Tests\Functional\Controller;
|
||||
|
||||
use Nelmio\ApiDocBundle\Annotation\Areas;
|
||||
use Nelmio\ApiDocBundle\Annotation\Model;
|
||||
use Nelmio\ApiDocBundle\Annotation\Security;
|
||||
use Nelmio\ApiDocBundle\Tests\Functional\Entity\Article;
|
||||
use OpenApi\Attributes as OA;
|
||||
use Symfony\Component\Routing\Annotation\Route;
|
||||
|
||||
class ApiController81 extends ApiController80
|
||||
{
|
||||
#[OA\Get(responses: [
|
||||
new OA\Response(
|
||||
response: '200',
|
||||
description: 'Success',
|
||||
attachables: [
|
||||
new Model(type: Article::class, groups: ['light']),
|
||||
],
|
||||
),
|
||||
])]
|
||||
#[OA\Parameter(ref: '#/components/parameters/test')]
|
||||
#[Route('/article_attributes/{id}', methods: ['GET'])]
|
||||
#[OA\Parameter(name: 'Accept-Version', in: 'header', attachables: [new OA\Schema(type: 'string')])]
|
||||
public function fetchArticleActionWithAttributes()
|
||||
{
|
||||
}
|
||||
|
||||
#[Areas(['area', 'area2'])]
|
||||
#[Route('/areas_attributes/new', methods: ['GET', 'POST'])]
|
||||
public function newAreaActionAttributes()
|
||||
{
|
||||
}
|
||||
|
||||
#[Route('/security_attributes')]
|
||||
#[OA\Response(response: '201', description: '')]
|
||||
#[Security(name: 'api_key')]
|
||||
#[Security(name: 'basic')]
|
||||
#[Security(name: 'oauth2', scopes: ['scope_1'])]
|
||||
public function securityActionAttributes()
|
||||
{
|
||||
}
|
||||
|
||||
#[Route('/security_override_attributes')]
|
||||
#[OA\Response(response: '201', description: '')]
|
||||
#[Security(name: 'api_key')]
|
||||
#[Security(name: null)]
|
||||
public function securityOverrideActionAttributes()
|
||||
{
|
||||
}
|
||||
|
||||
#[Route('/inline_path_parameters')]
|
||||
#[OA\Response(response: '200', description: '')]
|
||||
public function inlinePathParameters(
|
||||
#[OA\PathParameter] string $product_id
|
||||
) {
|
||||
}
|
||||
}
|
@ -11,30 +11,20 @@
|
||||
|
||||
namespace Nelmio\ApiDocBundle\Tests\Functional\Controller;
|
||||
|
||||
use FOS\RestBundle\Controller\Annotations\QueryParam;
|
||||
use FOS\RestBundle\Controller\Annotations\RequestParam;
|
||||
use Symfony\Component\Routing\Annotation\Route;
|
||||
use Symfony\Component\Validator\Constraints\DateTime;
|
||||
use Symfony\Component\Validator\Constraints\IsTrue;
|
||||
use Symfony\Component\Validator\Constraints\Regex;
|
||||
|
||||
/**
|
||||
* @Route("/api", host="api.example.com")
|
||||
*/
|
||||
class FOSRestController
|
||||
{
|
||||
if (\PHP_VERSION_ID >= 80100) {
|
||||
/**
|
||||
* @Route("/fosrest.{_format}", methods={"POST"})
|
||||
* @QueryParam(name="foo", requirements=@Regex("/^\d+$/"))
|
||||
* @QueryParam(name="mapped", map=true)
|
||||
* @RequestParam(name="Barraa", key="bar", requirements="\d+")
|
||||
* @RequestParam(name="baz", requirements=@IsTrue)
|
||||
* @RequestParam(name="datetime", requirements=@DateTime("Y-m-d\TH:i:sP"))
|
||||
* @RequestParam(name="datetimeAlt", requirements=@DateTime("c"))
|
||||
* @RequestParam(name="datetimeNoFormat", requirements=@DateTime())
|
||||
* @RequestParam(name="date", requirements=@DateTime("Y-m-d"))
|
||||
* @Route("/api", host="api.example.com")
|
||||
*/
|
||||
public function fosrestAction()
|
||||
class FOSRestController extends FOSRestController81
|
||||
{
|
||||
}
|
||||
} else {
|
||||
/**
|
||||
* @Route("/api", host="api.example.com")
|
||||
*/
|
||||
class FOSRestController extends FOSRestController80
|
||||
{
|
||||
}
|
||||
}
|
||||
|
37
Tests/Functional/Controller/FOSRestController80.php
Normal file
37
Tests/Functional/Controller/FOSRestController80.php
Normal file
@ -0,0 +1,37 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the NelmioApiDocBundle package.
|
||||
*
|
||||
* (c) Nelmio
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Nelmio\ApiDocBundle\Tests\Functional\Controller;
|
||||
|
||||
use FOS\RestBundle\Controller\Annotations\QueryParam;
|
||||
use FOS\RestBundle\Controller\Annotations\RequestParam;
|
||||
use Symfony\Component\Routing\Annotation\Route;
|
||||
use Symfony\Component\Validator\Constraints\DateTime;
|
||||
use Symfony\Component\Validator\Constraints\IsTrue;
|
||||
use Symfony\Component\Validator\Constraints\Regex;
|
||||
|
||||
class FOSRestController80
|
||||
{
|
||||
/**
|
||||
* @Route("/fosrest.{_format}", methods={"POST"})
|
||||
* @QueryParam(name="foo", requirements=@Regex("/^\d+$/"))
|
||||
* @QueryParam(name="mapped", map=true)
|
||||
* @RequestParam(name="Barraa", key="bar", requirements="\d+")
|
||||
* @RequestParam(name="baz", requirements=@IsTrue)
|
||||
* @RequestParam(name="datetime", requirements=@DateTime("Y-m-d\TH:i:sP"))
|
||||
* @RequestParam(name="datetimeAlt", requirements=@DateTime("c"))
|
||||
* @RequestParam(name="datetimeNoFormat", requirements=@DateTime())
|
||||
* @RequestParam(name="date", requirements=@DateTime("Y-m-d"))
|
||||
*/
|
||||
public function fosrestAction()
|
||||
{
|
||||
}
|
||||
}
|
35
Tests/Functional/Controller/FOSRestController81.php
Normal file
35
Tests/Functional/Controller/FOSRestController81.php
Normal file
@ -0,0 +1,35 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the NelmioApiDocBundle package.
|
||||
*
|
||||
* (c) Nelmio
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Nelmio\ApiDocBundle\Tests\Functional\Controller;
|
||||
|
||||
use FOS\RestBundle\Controller\Annotations\QueryParam;
|
||||
use FOS\RestBundle\Controller\Annotations\RequestParam;
|
||||
use Symfony\Component\Routing\Annotation\Route;
|
||||
use Symfony\Component\Validator\Constraints\DateTime;
|
||||
use Symfony\Component\Validator\Constraints\IsTrue;
|
||||
use Symfony\Component\Validator\Constraints\Regex;
|
||||
|
||||
class FOSRestController81 extends FOSRestController80
|
||||
{
|
||||
#[Route('/fosrest_attributes.{_format}', methods: ['POST'])]
|
||||
#[QueryParam(name: 'foo', requirements: new Regex('/^\d+$/'))]
|
||||
#[QueryParam(name: 'mapped', map: true)]
|
||||
#[RequestParam(name: 'Barraa', key: 'bar', requirements: '\d+')]
|
||||
#[RequestParam(name: 'baz', requirements: new IsTrue())]
|
||||
#[RequestParam(name: 'datetime', requirements: new DateTime('Y-m-d\TH:i:sP'))]
|
||||
#[RequestParam(name: 'datetimeAlt', requirements: new DateTime('c'))]
|
||||
#[RequestParam(name: 'datetimeNoFormat', requirements: new DateTime())]
|
||||
#[RequestParam(name: 'date', requirements: new DateTime('Y-m-d'))]
|
||||
public function fosrestAttributesAction()
|
||||
{
|
||||
}
|
||||
}
|
@ -23,9 +23,12 @@ class FOSRestTest extends WebTestCase
|
||||
static::createClient([], ['HTTP_HOST' => 'api.example.com']);
|
||||
}
|
||||
|
||||
public function testFOSRestAction()
|
||||
/**
|
||||
* @dataProvider provideRoute
|
||||
*/
|
||||
public function testFOSRestAction(string $route)
|
||||
{
|
||||
$operation = $this->getOperation('/api/fosrest', 'post');
|
||||
$operation = $this->getOperation($route, 'post');
|
||||
|
||||
$this->assertHasParameter('foo', 'query', $operation);
|
||||
$this->assertInstanceOf(OA\RequestBody::class, $operation->requestBody);
|
||||
@ -66,4 +69,13 @@ class FOSRestTest extends WebTestCase
|
||||
// The _format path attribute should be removed
|
||||
$this->assertNotHasParameter('_format', 'path', $operation);
|
||||
}
|
||||
|
||||
public function provideRoute(): iterable
|
||||
{
|
||||
yield 'Annotations' => ['/api/fosrest'];
|
||||
|
||||
if (\PHP_VERSION_ID >= 80100) {
|
||||
yield 'Attributes' => ['/api/fosrest_attributes'];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -40,9 +40,12 @@ class FunctionalTest extends WebTestCase
|
||||
$this->assertNotHasPath('/api/admin', $api);
|
||||
}
|
||||
|
||||
public function testFetchArticleAction()
|
||||
/**
|
||||
* @dataProvider provideArticleRoute
|
||||
*/
|
||||
public function testFetchArticleAction(string $articleRoute)
|
||||
{
|
||||
$operation = $this->getOperation('/api/article/{id}', 'get');
|
||||
$operation = $this->getOperation($articleRoute, 'get');
|
||||
|
||||
$this->assertHasResponse('200', $operation);
|
||||
$response = $this->getOperationResponse($operation, '200');
|
||||
@ -56,6 +59,15 @@ class FunctionalTest extends WebTestCase
|
||||
$this->assertNotHasProperty('author', Util::getProperty($articleModel, 'author'));
|
||||
}
|
||||
|
||||
public function provideArticleRoute(): iterable
|
||||
{
|
||||
yield 'Annotations' => ['/api/article/{id}'];
|
||||
|
||||
if (\PHP_VERSION_ID >= 80100) {
|
||||
yield 'Attributes' => ['/api/article_attributes/{id}'];
|
||||
}
|
||||
}
|
||||
|
||||
public function testFilteredAction()
|
||||
{
|
||||
$openApi = $this->getOpenApiDefinition();
|
||||
@ -333,9 +345,12 @@ class FunctionalTest extends WebTestCase
|
||||
], json_decode($this->getModel('DummyType')->toJson(), true));
|
||||
}
|
||||
|
||||
public function testSecurityAction()
|
||||
/**
|
||||
* @dataProvider provideSecurityRoute
|
||||
*/
|
||||
public function testSecurityAction(string $route)
|
||||
{
|
||||
$operation = $this->getOperation('/api/security', 'get');
|
||||
$operation = $this->getOperation($route, 'get');
|
||||
|
||||
$expected = [
|
||||
['api_key' => []],
|
||||
@ -345,6 +360,46 @@ class FunctionalTest extends WebTestCase
|
||||
$this->assertEquals($expected, $operation->security);
|
||||
}
|
||||
|
||||
public function provideSecurityRoute(): iterable
|
||||
{
|
||||
yield 'Annotations' => ['/api/security'];
|
||||
|
||||
if (\PHP_VERSION_ID >= 80100) {
|
||||
yield 'Attributes' => ['/api/security_attributes'];
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @dataProvider provideSecurityOverrideRoute
|
||||
*/
|
||||
public function testSecurityOverrideAction(string $route)
|
||||
{
|
||||
$operation = $this->getOperation($route, 'get');
|
||||
$this->assertEquals([], $operation->security);
|
||||
}
|
||||
|
||||
public function provideSecurityOverrideRoute(): iterable
|
||||
{
|
||||
yield 'Annotations' => ['/api/securityOverride'];
|
||||
|
||||
if (\PHP_VERSION_ID >= 80100) {
|
||||
yield 'Attributes' => ['/api/security_override_attributes'];
|
||||
}
|
||||
}
|
||||
|
||||
public function testInlinePHP81Parameters()
|
||||
{
|
||||
if (\PHP_VERSION_ID < 80100) {
|
||||
$this->markTestSkipped('Attributes require PHP 8.1');
|
||||
}
|
||||
|
||||
$operation = $this->getOperation('/api/inline_path_parameters', 'get');
|
||||
$this->assertCount(1, $operation->parameters);
|
||||
$this->assertInstanceOf(OA\PathParameter::class, $operation->parameters[0]);
|
||||
$this->assertSame($operation->parameters[0]->name, 'product_id');
|
||||
$this->assertSame($operation->parameters[0]->schema->type, 'string');
|
||||
}
|
||||
|
||||
public function testClassSecurityAction()
|
||||
{
|
||||
$operation = $this->getOperation('/api/security/class', 'get');
|
||||
@ -522,10 +577,10 @@ class FunctionalTest extends WebTestCase
|
||||
public function testCustomOperationId()
|
||||
{
|
||||
$operation = $this->getOperation('/api/custom-operation-id', 'get');
|
||||
$this->assertEquals('custom-operation-id', $operation->operationId);
|
||||
$this->assertEquals('get-custom-operation-id', $operation->operationId);
|
||||
|
||||
$operation = $this->getOperation('/api/custom-operation-id', 'post');
|
||||
$this->assertEquals('custom-operation-id', $operation->operationId);
|
||||
$this->assertEquals('post-custom-operation-id', $operation->operationId);
|
||||
}
|
||||
|
||||
/**
|
||||
|
45
Tests/Functional/Resources/routes.yaml
Normal file
45
Tests/Functional/Resources/routes.yaml
Normal file
@ -0,0 +1,45 @@
|
||||
# Resources
|
||||
test:
|
||||
resource: ../Controller/TestController.php
|
||||
type: annotation
|
||||
|
||||
api:
|
||||
resource: ../Controller/ApiController.php
|
||||
type: annotation
|
||||
|
||||
class_api:
|
||||
resource: ../Controller/ClassApiController.php
|
||||
type: annotation
|
||||
|
||||
undocumented:
|
||||
resource: ../Controller/UndocumentedController.php
|
||||
type: annotation
|
||||
|
||||
invokable:
|
||||
resource: ../Controller/InvokableController.php
|
||||
type: annotation
|
||||
|
||||
fos_rest:
|
||||
resource: ../Controller/FOSRestController.php
|
||||
type: annotation
|
||||
|
||||
|
||||
api_platform:
|
||||
resource: .
|
||||
prefix: /api
|
||||
type: api_platform
|
||||
|
||||
# Controllers
|
||||
doc_area:
|
||||
path: /docs/{area}
|
||||
controller: nelmio_api_doc.controller.swagger_ui
|
||||
defaults:
|
||||
area: default
|
||||
|
||||
doc_json:
|
||||
path: /docs.json
|
||||
controller: nelmio_api_doc.controller.swagger_json
|
||||
|
||||
doc_yaml:
|
||||
path: /docs.yaml
|
||||
controller: nelmio_api_doc.controller.swagger_yaml
|
@ -31,7 +31,7 @@ use Symfony\Component\Config\Loader\LoaderInterface;
|
||||
use Symfony\Component\DependencyInjection\ContainerBuilder;
|
||||
use Symfony\Component\DependencyInjection\Definition;
|
||||
use Symfony\Component\HttpKernel\Kernel;
|
||||
use Symfony\Component\Routing\RouteCollectionBuilder;
|
||||
use Symfony\Component\Routing\Loader\Configurator\RoutingConfigurator;
|
||||
use Symfony\Component\Serializer\Annotation\SerializedName;
|
||||
|
||||
class TestKernel extends Kernel
|
||||
@ -81,39 +81,42 @@ class TestKernel extends Kernel
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function configureRoutes(RouteCollectionBuilder $routes)
|
||||
protected function configureRoutes($routes)
|
||||
{
|
||||
$routes->import(__DIR__.'/Controller/TestController.php', '/', 'annotation');
|
||||
$routes->import(__DIR__.'/Controller/ApiController.php', '/', 'annotation');
|
||||
$routes->import(__DIR__.'/Controller/ClassApiController.php', '/', 'annotation');
|
||||
$routes->import(__DIR__.'/Controller/UndocumentedController.php', '/', 'annotation');
|
||||
$routes->import(__DIR__.'/Controller/InvokableController.php', '/', 'annotation');
|
||||
$routes->import('', '/api', 'api_platform');
|
||||
$routes->add('/docs/{area}', 'nelmio_api_doc.controller.swagger_ui')->setDefault('area', 'default');
|
||||
$routes->add('/docs.json', 'nelmio_api_doc.controller.swagger_json');
|
||||
$routes->add('/docs.yaml', 'nelmio_api_doc.controller.swagger_yaml');
|
||||
$routes->import(__DIR__.'/Controller/FOSRestController.php', '/', 'annotation');
|
||||
$this->import($routes, __DIR__.'/Resources/routes.yaml', '/', 'yaml');
|
||||
|
||||
if (class_exists(SerializedName::class)) {
|
||||
$routes->import(__DIR__.'/Controller/SerializedNameController.php', '/', 'annotation');
|
||||
$this->import($routes, __DIR__.'/Controller/SerializedNameController.php', '/', 'annotation');
|
||||
}
|
||||
|
||||
if ($this->flags & self::USE_JMS) {
|
||||
$routes->import(__DIR__.'/Controller/JMSController.php', '/', 'annotation');
|
||||
$this->import($routes, __DIR__.'/Controller/JMSController.php', '/', 'annotation');
|
||||
}
|
||||
|
||||
if ($this->flags & self::USE_BAZINGA) {
|
||||
$routes->import(__DIR__.'/Controller/BazingaController.php', '/', 'annotation');
|
||||
$this->import($routes, __DIR__.'/Controller/BazingaController.php', '/', 'annotation');
|
||||
|
||||
try {
|
||||
new \ReflectionMethod(Embedded::class, 'getType');
|
||||
$routes->import(__DIR__.'/Controller/BazingaTypedController.php', '/', 'annotation');
|
||||
$this->import($routes, __DIR__.'/Controller/BazingaTypedController.php', '/', 'annotation');
|
||||
} catch (\ReflectionException $e) {
|
||||
}
|
||||
}
|
||||
|
||||
if ($this->flags & self::ERROR_ARRAY_ITEMS) {
|
||||
$routes->import(__DIR__.'/Controller/ArrayItemsErrorController.php', '/', 'annotation');
|
||||
$this->import($routes, __DIR__.'/Controller/ArrayItemsErrorController.php', '/', 'annotation');
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* BC for sf < 5.1.
|
||||
*/
|
||||
private function import($routes, $resource, $prefix, $type)
|
||||
{
|
||||
if ($routes instanceof RoutingConfigurator) {
|
||||
$routes->withPath($prefix)->import($resource, $type);
|
||||
} else {
|
||||
$routes->import($resource, $prefix, $type);
|
||||
}
|
||||
}
|
||||
|
||||
@ -186,6 +189,20 @@ class TestKernel extends Kernel
|
||||
'info' => [
|
||||
'title' => 'My Default App',
|
||||
],
|
||||
'paths' => [
|
||||
// Ensures we can define routes in Yaml without defining OperationIds
|
||||
// See https://github.com/zircote/swagger-php/issues/1153
|
||||
'/api/test-from-yaml' => ['get' => [
|
||||
'responses' => [
|
||||
200 => ['description' => 'success'],
|
||||
],
|
||||
]],
|
||||
'/api/test-from-yaml2' => ['get' => [
|
||||
'responses' => [
|
||||
200 => ['description' => 'success'],
|
||||
],
|
||||
]],
|
||||
],
|
||||
'components' => [
|
||||
'schemas' => [
|
||||
'Test' => [
|
||||
|
@ -14,6 +14,7 @@ namespace Nelmio\ApiDocBundle\Tests\Functional;
|
||||
use OpenApi\Annotations as OA;
|
||||
use OpenApi\Generator;
|
||||
use Symfony\Bundle\FrameworkBundle\Test\WebTestCase as BaseWebTestCase;
|
||||
use Symfony\Component\DependencyInjection\ContainerInterface;
|
||||
use Symfony\Component\HttpKernel\KernelInterface;
|
||||
|
||||
class WebTestCase extends BaseWebTestCase
|
||||
@ -168,4 +169,16 @@ class WebTestCase extends BaseWebTestCase
|
||||
sprintf('Failed asserting that property "%s" does not exist.', $property)
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* BC symfony < 5.3.
|
||||
*/
|
||||
protected static function getContainer(): ContainerInterface
|
||||
{
|
||||
if (method_exists(parent::class, 'getContainer')) {
|
||||
return parent::getContainer();
|
||||
}
|
||||
|
||||
return static::$container;
|
||||
}
|
||||
}
|
||||
|
68
Tests/ModelDescriber/Annotations/AnnotationReaderTest.php
Normal file
68
Tests/ModelDescriber/Annotations/AnnotationReaderTest.php
Normal file
@ -0,0 +1,68 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the NelmioApiDocBundle package.
|
||||
*
|
||||
* (c) Nelmio
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Nelmio\ApiDocBundle\Tests\ModelDescriber\Annotations;
|
||||
|
||||
use Doctrine\Common\Annotations\AnnotationReader;
|
||||
use Nelmio\ApiDocBundle\Model\ModelRegistry;
|
||||
use Nelmio\ApiDocBundle\ModelDescriber\Annotations\OpenApiAnnotationsReader;
|
||||
use OpenApi\Annotations as OA;
|
||||
use OpenApi\Attributes as OAattr;
|
||||
use OpenApi\Generator;
|
||||
use PHPUnit\Framework\TestCase;
|
||||
|
||||
class AnnotationReaderTest extends TestCase
|
||||
{
|
||||
/**
|
||||
* @param object $entity
|
||||
* @dataProvider provideProperty
|
||||
*/
|
||||
public function testProperty($entity)
|
||||
{
|
||||
$schema = new OA\Schema([]);
|
||||
$schema->merge([new OA\Property(['property' => 'property1'])]);
|
||||
$schema->merge([new OA\Property(['property' => 'property2'])]);
|
||||
|
||||
$registry = new ModelRegistry([], new OA\OpenApi([]), []);
|
||||
$symfonyConstraintAnnotationReader = new OpenApiAnnotationsReader(new AnnotationReader(), $registry, ['json']);
|
||||
$symfonyConstraintAnnotationReader->updateProperty(new \ReflectionProperty($entity, 'property1'), $schema->properties[0]);
|
||||
$symfonyConstraintAnnotationReader->updateProperty(new \ReflectionProperty($entity, 'property2'), $schema->properties[1]);
|
||||
|
||||
$this->assertEquals($schema->properties[0]->example, 1);
|
||||
$this->assertEquals($schema->properties[0]->description, Generator::UNDEFINED);
|
||||
|
||||
$this->assertEquals($schema->properties[1]->example, 'some example');
|
||||
$this->assertEquals($schema->properties[1]->description, 'some description');
|
||||
}
|
||||
|
||||
public function provideProperty(): iterable
|
||||
{
|
||||
yield 'Annotations' => [new class() {
|
||||
/**
|
||||
* @OA\Property(example=1)
|
||||
*/
|
||||
private $property1;
|
||||
/**
|
||||
* @OA\Property(example="some example", description="some description")
|
||||
*/
|
||||
private $property2;
|
||||
}];
|
||||
|
||||
if (\PHP_VERSION_ID >= 80100) {
|
||||
yield 'Attributes' => [new class() {
|
||||
#[OAattr\Property(example: 1)]
|
||||
private $property1;
|
||||
#[OAattr\Property(example: 'some example', description: 'some description')]
|
||||
private $property2;
|
||||
}];
|
||||
}
|
||||
}
|
||||
}
|
@ -469,7 +469,7 @@ class SymfonyConstraintAnnotationReaderTest extends TestCase
|
||||
);
|
||||
|
||||
$this->assertSame(['property1'], $schema->required, 'should have read constraint in default group');
|
||||
$this->assertSame(OA\UNDEFINED, $schema->properties[0]->minimum, 'should not have read constraint in other group');
|
||||
$this->assertSame(Generator::UNDEFINED, $schema->properties[0]->minimum, 'should not have read constraint in other group');
|
||||
}
|
||||
|
||||
/**
|
||||
@ -492,7 +492,7 @@ class SymfonyConstraintAnnotationReaderTest extends TestCase
|
||||
['other']
|
||||
);
|
||||
|
||||
$this->assertSame(OA\UNDEFINED, $schema->required, 'should not have read constraint in default group');
|
||||
$this->assertSame(Generator::UNDEFINED, $schema->required, 'should not have read constraint in default group');
|
||||
$this->assertSame(1, $schema->properties[0]->minimum, 'should have read constraint in other group');
|
||||
}
|
||||
|
||||
|
@ -23,7 +23,7 @@ class GetNelmioAssetTest extends WebTestCase
|
||||
{
|
||||
static::bootKernel();
|
||||
/** @var GetNelmioAsset $getNelmioAsset */
|
||||
$getNelmioAsset = static::$container->get('nelmio_api_doc.render_docs.html.asset');
|
||||
$getNelmioAsset = static::getContainer()->get('nelmio_api_doc.render_docs.html.asset');
|
||||
/** @var TwigFunction */
|
||||
$twigFunction = $getNelmioAsset->getFunctions()[0];
|
||||
self::assertSame($expectedContent, $twigFunction->getCallable()->__invoke($mode, $asset));
|
||||
|
@ -157,9 +157,9 @@ class FilteredRouteCollectionBuilderTest extends TestCase
|
||||
$this->assertCount(1, $filteredRoutes);
|
||||
}
|
||||
|
||||
public function getMatchingRoutes(): array
|
||||
public function getMatchingRoutes(): iterable
|
||||
{
|
||||
return [
|
||||
yield from [
|
||||
['r1', new Route('/api/bar/action1')],
|
||||
['r2', new Route('/api/foo/action1'), ['path_patterns' => ['^/api', 'i/fo', 'n1$']]],
|
||||
['r3', new Route('/api/foo/action2'), ['path_patterns' => ['^/api/foo/action2$']]],
|
||||
@ -167,6 +167,10 @@ class FilteredRouteCollectionBuilderTest extends TestCase
|
||||
['r9', new Route('/api/bar/action1', [], [], [], 'api.example.com'), ['path_patterns' => ['^/api/'], 'host_patterns' => ['^api\.ex']]],
|
||||
['r10', new Route('/api/areas/new'), ['path_patterns' => ['^/api']]],
|
||||
];
|
||||
|
||||
if (\PHP_VERSION_ID < 80000) {
|
||||
yield ['r10', new Route('/api/areas_attributes/new'), ['path_patterns' => ['^/api']]];
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@ -201,9 +205,9 @@ class FilteredRouteCollectionBuilderTest extends TestCase
|
||||
$this->assertCount(1, $filteredRoutes);
|
||||
}
|
||||
|
||||
public function getMatchingRoutesWithAnnotation(): array
|
||||
public function getMatchingRoutesWithAnnotation(): iterable
|
||||
{
|
||||
return [
|
||||
yield from [
|
||||
'with annotation only' => [
|
||||
'r10',
|
||||
new Route('/api/areas/new', ['_controller' => 'ApiController::newAreaAction']),
|
||||
@ -215,6 +219,21 @@ class FilteredRouteCollectionBuilderTest extends TestCase
|
||||
['path_patterns' => ['^/api'], 'with_annotation' => true],
|
||||
],
|
||||
];
|
||||
|
||||
if (\PHP_VERSION_ID < 80000) {
|
||||
yield from [
|
||||
'with attribute only' => [
|
||||
'r10',
|
||||
new Route('/api/areas_attributes/new', ['_controller' => 'ApiController::newAreaActionAttributes']),
|
||||
['with_annotation' => true],
|
||||
],
|
||||
'with attribute and path patterns' => [
|
||||
'r10',
|
||||
new Route('/api/areas_attributes/new', ['_controller' => 'ApiController::newAreaActionAttributes']),
|
||||
['path_patterns' => ['^/api'], 'with_annotation' => true],
|
||||
],
|
||||
];
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -58,7 +58,7 @@ class UtilTest extends TestCase
|
||||
{
|
||||
$context = Util::createContext([], $this->rootContext);
|
||||
|
||||
$this->assertSame($this->rootContext, $context->getRootContext());
|
||||
$this->assertContextIsConnectedToRootContext($context);
|
||||
}
|
||||
|
||||
public function testCreateContextWithProperties()
|
||||
@ -818,7 +818,20 @@ class UtilTest extends TestCase
|
||||
|
||||
public function assertIsConnectedToRootContext(OA\AbstractAnnotation $annotation)
|
||||
{
|
||||
$this->assertSame($this->rootContext, $annotation->_context->getRootContext());
|
||||
$this->assertContextIsConnectedToRootContext($annotation->_context);
|
||||
}
|
||||
|
||||
public function assertContextIsConnectedToRootContext(Context $context)
|
||||
{
|
||||
$getRootContext = \Closure::bind(function (Context $context) use (&$getRootContext) {
|
||||
if (null !== $context->_parent) {
|
||||
return $getRootContext($context->_parent);
|
||||
}
|
||||
|
||||
return $context;
|
||||
}, null, Context::class);
|
||||
|
||||
$this->assertSame($this->rootContext, $getRootContext($context));
|
||||
}
|
||||
|
||||
private function getSetupPropertiesWithoutClass(array $setup)
|
||||
|
@ -13,14 +13,14 @@ The Upgrade to Swagger 3.0
|
||||
The biggest part of the upgrade will most likely be the upgrade of the library `zircote/swagger-php` to `3.0` which introduces new annotations in order to support OpenAPI 3.0 but also changes
|
||||
their namespace from ``Swagger`` to ``OpenApi``.
|
||||
|
||||
They created a dedicated page to help you upgrade : https://zircote.github.io/swagger-php/Migrating-to-v3.html.
|
||||
They created a dedicated page to help you upgrade : https://github.com/zircote/swagger-php/blob/3.x/docs/Migrating-to-v3.md.
|
||||
|
||||
Here are some additional advices that are more likely to apply to NelmioApiDocBundle users:
|
||||
|
||||
- Upgrade all your ``use Swagger\Annotations as SWG`` statements to ``use OpenApi\Annotations as OA;`` (to simplify the upgrade you may also stick to the ``SWG`` aliasing).
|
||||
In case you changed ``SWG`` to ``OA``, upgrade all your annotations from ``@SWG\...`` to ``@OA\...``.
|
||||
|
||||
- Update your config in case you used inlined swagger docummentation (the field ``nelmio_api_doc.documentation``). [A tool](https://openapi-converter.herokuapp.com/) is available to help you convert it.
|
||||
- Update your config in case you used inlined swagger documentation (the field ``nelmio_api_doc.documentation``). [A tool](https://openapi-converter.herokuapp.com/) is available to help you convert it.
|
||||
|
||||
- In case you used ``@OA\Response(..., @OA\Schema(...))``, you should explicit your media type by using the annotation ``@OA\JsonContent`` or ``@OA\XmlContent`` instead of ``@OA\Schema``:
|
||||
``@OA\Response(..., @OA\JsonContent(...))`` or ``@OA\Response(..., @OA\XmlContent(...))``.
|
||||
|
@ -15,7 +15,7 @@ trait SetsContextTrait
|
||||
// zircote/swagger-php ^3.2
|
||||
\OpenApi\Analyser::$context = $context;
|
||||
} else {
|
||||
/// zircote/swagger-php ^4.0
|
||||
// zircote/swagger-php ^4.0
|
||||
\OpenApi\Generator::$context = $context;
|
||||
}
|
||||
}
|
||||
|
@ -21,37 +21,37 @@
|
||||
"psr/cache": "^1.0|^2.0|^3.0",
|
||||
"psr/container": "^1.0|^2.0",
|
||||
"psr/log": "^1.0|^2.0|^3.0",
|
||||
"symfony/config": "^4.4|^5.0",
|
||||
"symfony/console": "^4.4|^5.0",
|
||||
"symfony/dependency-injection": "^4.4|^5.0",
|
||||
"symfony/framework-bundle": "^4.4|^5.0",
|
||||
"symfony/http-foundation": "^4.4|^5.0",
|
||||
"symfony/http-kernel": "^4.4|^5.0",
|
||||
"symfony/options-resolver": "^4.4|^5.0",
|
||||
"symfony/property-info": "^4.4|^5.0",
|
||||
"symfony/routing": "^4.4|^5.0",
|
||||
"symfony/config": "^4.4|^5.0|^6.0",
|
||||
"symfony/console": "^4.4|^5.0|^6.0",
|
||||
"symfony/dependency-injection": "^4.4|^5.0|^6.0",
|
||||
"symfony/framework-bundle": "^4.4|^5.0|^6.0",
|
||||
"symfony/http-foundation": "^4.4|^5.0|^6.0",
|
||||
"symfony/http-kernel": "^4.4|^5.0|^6.0",
|
||||
"symfony/options-resolver": "^4.4|^5.0|^6.0",
|
||||
"symfony/property-info": "^4.4|^5.0|^6.0",
|
||||
"symfony/routing": "^4.4|^5.0|^6.0",
|
||||
"zircote/swagger-php": "^3.2|^4.0",
|
||||
"phpdocumentor/reflection-docblock": "^3.1|^4.4|^5.0"
|
||||
"phpdocumentor/reflection-docblock": "^3.1|^4.0|^5.0"
|
||||
},
|
||||
"require-dev": {
|
||||
"sensio/framework-extra-bundle": "^4.4|^5.0|^6.0",
|
||||
"symfony/asset": "^4.4|^5.0",
|
||||
"symfony/dom-crawler": "^4.4|^5.0",
|
||||
"symfony/browser-kit": "^4.4|^5.0",
|
||||
"symfony/cache": "^4.4|^5.0",
|
||||
"symfony/form": "^4.4|^5.0",
|
||||
"sensio/framework-extra-bundle": "^4.4|^5.2|^6.0",
|
||||
"symfony/asset": "^4.4|^5.2|^6.0",
|
||||
"symfony/dom-crawler": "^4.4|^5.2|^6.0",
|
||||
"symfony/browser-kit": "^4.4|^5.2|^6.0",
|
||||
"symfony/cache": "^4.4|^5.2|^6.0",
|
||||
"symfony/form": "^4.4|^5.2|^6.0",
|
||||
"symfony/phpunit-bridge": "^5.2",
|
||||
"symfony/property-access": "^4.4|^5.0",
|
||||
"symfony/serializer": "^4.4|^5.0",
|
||||
"symfony/stopwatch": "^4.4|^5.0",
|
||||
"symfony/templating": "^4.4|^5.0",
|
||||
"symfony/twig-bundle": "^4.4|^5.0",
|
||||
"symfony/validator": "^4.4|^5.0",
|
||||
"symfony/property-access": "^4.4|^5.2|^6.0",
|
||||
"symfony/serializer": "^4.4|^5.2|^6.0",
|
||||
"symfony/stopwatch": "^4.4|^5.2|^6.0",
|
||||
"symfony/templating": "^4.4|^5.2|^6.0",
|
||||
"symfony/twig-bundle": "^4.4|^5.2|^6.0",
|
||||
"symfony/validator": "^4.4|^5.2|^6.0",
|
||||
|
||||
"api-platform/core": "^2.4",
|
||||
"friendsofsymfony/rest-bundle": "^2.8|^3.0",
|
||||
"willdurand/hateoas-bundle": "^1.0|^2.0",
|
||||
"jms/serializer-bundle": "^2.3|^3.0",
|
||||
"jms/serializer-bundle": "^2.3|^3.0|^4.0",
|
||||
"jms/serializer": "^1.14|^3.0",
|
||||
"composer/package-versions-deprecated": "1.11.99.1"
|
||||
},
|
||||
|
Loading…
x
Reference in New Issue
Block a user