welcome back to dyb-tech

This commit is contained in:
Daniel Guzman
2024-05-18 02:28:01 +02:00
parent 9513cdba09
commit 9f30bc98c7
6149 changed files with 668407 additions and 0 deletions
@@ -0,0 +1,47 @@
<?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\Describer;
use ApiPlatform\Core\Swagger\Serializer\DocumentationNormalizer;
use ApiPlatform\Documentation\DocumentationInterface;
use ApiPlatform\OpenApi\OpenApi;
use Symfony\Component\Serializer\Normalizer\NormalizerInterface;
final class ApiPlatformDescriber extends ExternalDocDescriber
{
public function __construct(object $documentation, NormalizerInterface $normalizer)
{
if (!$documentation instanceof DocumentationInterface && !$documentation instanceof OpenApi) {
throw new \InvalidArgumentException(sprintf('Argument 1 passed to %s() must be an instance of %s or %s. The documentation provided is an instance of %s.', __METHOD__, Documentation::class, OpenApi::class, get_class($documentation)));
}
if (!$normalizer->supportsNormalization($documentation, 'json')) {
throw new \InvalidArgumentException(sprintf('Argument 2 passed to %s() must implement %s and support normalization of %s. The normalizer provided is an instance of %s.', __METHOD__, NormalizerInterface::class, Documentation::class, get_class($normalizer)));
}
parent::__construct(function () use ($documentation, $normalizer) {
$documentation = (array) $normalizer->normalize(
$documentation,
null,
class_exists(DocumentationNormalizer::class) ? [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']);
unset($documentation['servers']);
return $documentation;
});
}
}
@@ -0,0 +1,53 @@
<?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\Describer;
use Nelmio\ApiDocBundle\OpenApiPhp\Util;
use OpenApi\Annotations as OA;
use OpenApi\Generator;
/**
* Makes the swagger documentation valid even if there are missing fields.
*
* @author Ener-Getick <egetick@gmail.com>
*/
final class DefaultDescriber implements DescriberInterface
{
public function describe(OA\OpenApi $api)
{
// Info
/** @var OA\Info $info */
$info = Util::getChild($api, OA\Info::class);
if (Generator::UNDEFINED === $info->title) {
$info->title = '';
}
if (Generator::UNDEFINED === $info->version) {
$info->version = '0.0.0';
}
// Paths
if (Generator::UNDEFINED === $api->paths) {
$api->paths = [];
}
foreach ($api->paths as $path) {
foreach (Util::OPERATIONS as $method) {
/** @var OA\Operation $operation */
$operation = $path->{$method};
if (Generator::UNDEFINED !== $operation && null !== $operation && (Generator::UNDEFINED === $operation->responses || empty($operation->responses))) {
/** @var OA\Response $response */
$response = Util::getIndexedCollectionItem($operation, OA\Response::class, 'default');
$response->description = '';
}
}
}
}
}
@@ -0,0 +1,19 @@
<?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\Describer;
use OpenApi\Annotations\OpenApi;
interface DescriberInterface
{
public function describe(OpenApi $api);
}
@@ -0,0 +1,49 @@
<?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\Describer;
use Nelmio\ApiDocBundle\OpenApiPhp\Util;
use OpenApi\Annotations as OA;
class ExternalDocDescriber implements DescriberInterface
{
private $externalDoc;
private $overwrite;
/**
* @param array|callable $externalDoc
*/
public function __construct($externalDoc, bool $overwrite = false)
{
$this->externalDoc = $externalDoc;
$this->overwrite = $overwrite;
}
public function describe(OA\OpenApi $api)
{
$externalDoc = $this->getExternalDoc();
if (!empty($externalDoc)) {
Util::merge($api, $externalDoc, $this->overwrite);
}
}
private function getExternalDoc()
{
if (is_callable($this->externalDoc)) {
return call_user_func($this->externalDoc);
}
return $this->externalDoc;
}
}
@@ -0,0 +1,19 @@
<?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\Describer;
use Nelmio\ApiDocBundle\Model\ModelRegistry;
interface ModelRegistryAwareInterface
{
public function setModelRegistry(ModelRegistry $modelRegistry);
}
@@ -0,0 +1,27 @@
<?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\Describer;
use Nelmio\ApiDocBundle\Model\ModelRegistry;
trait ModelRegistryAwareTrait
{
/**
* @var ModelRegistry
*/
private $modelRegistry;
public function setModelRegistry(ModelRegistry $modelRegistry)
{
$this->modelRegistry = $modelRegistry;
}
}
@@ -0,0 +1,232 @@
<?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\Describer;
use Doctrine\Common\Annotations\Reader;
use Nelmio\ApiDocBundle\Annotation\Operation;
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;
use Symfony\Component\Routing\Route;
use Symfony\Component\Routing\RouteCollection;
// Help opcache.preload discover Swagger\Annotations\Swagger
class_exists(OA\OpenApi::class);
final class OpenApiPhpDescriber
{
use SetsContextTrait;
private $routeCollection;
private $controllerReflector;
/**
* @var Reader|null
*/
private $annotationReader;
private $logger;
private $overwrite;
public function __construct(RouteCollection $routeCollection, ControllerReflector $controllerReflector, ?Reader $annotationReader, LoggerInterface $logger, bool $overwrite = false)
{
$this->routeCollection = $routeCollection;
$this->controllerReflector = $controllerReflector;
$this->annotationReader = $annotationReader;
$this->logger = $logger;
$this->overwrite = $overwrite;
}
public function describe(OA\OpenApi $api)
{
$classAnnotations = [];
/** @var \ReflectionMethod $method */
foreach ($this->getMethodsToParse() as $method => [$path, $httpMethods, $routeName]) {
$declaringClass = $method->getDeclaringClass();
$path = Util::getPath($api, $path);
$context = Util::createContext(['nested' => $path], $path->_context);
$context->namespace = $declaringClass->getNamespaceName();
$context->class = $declaringClass->getShortName();
$context->method = $method->name;
$context->filename = $method->getFileName();
$this->setContext($context);
if (!array_key_exists($declaringClass->getName(), $classAnnotations)) {
$classAnnotations = [];
if (null !== $this->annotationReader) {
$classAnnotations = $this->annotationReader->getClassAnnotations($declaringClass);
}
$classAnnotations = array_filter($classAnnotations, function ($v) {
return $v instanceof OA\AbstractAnnotation;
});
$classAnnotations = array_merge($classAnnotations, $this->getAttributesAsAnnotation($declaringClass, $context));
$classAnnotations[$declaringClass->getName()] = $classAnnotations;
}
$annotations = [];
if (null !== $this->annotationReader) {
$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;
}
$implicitAnnotations = [];
$mergeProperties = new \stdClass();
foreach (array_merge($annotations, $classAnnotations[$declaringClass->getName()]) as $annotation) {
if ($annotation instanceof Operation) {
foreach ($httpMethods as $httpMethod) {
$operation = Util::getOperation($path, $httpMethod);
$operation->mergeProperties($annotation);
}
continue;
}
if ($annotation instanceof OA\Operation) {
if (!in_array($annotation->method, $httpMethods, true)) {
continue;
}
if (Generator::UNDEFINED !== $annotation->path && $path->path !== $annotation->path) {
continue;
}
$operation = Util::getOperation($path, $annotation->method);
$operation->mergeProperties($annotation);
continue;
}
if ($annotation instanceof Security) {
$annotation->validate();
if (null === $annotation->name) {
$mergeProperties->security = [];
continue;
}
$mergeProperties->security[] = [$annotation->name => $annotation->scopes];
continue;
}
if ($annotation instanceof OA\Tag) {
$annotation->validate();
$mergeProperties->tags[] = $annotation->name;
continue;
}
if (
!$annotation instanceof OA\Response &&
!$annotation instanceof OA\RequestBody &&
!$annotation instanceof OA\Parameter &&
!$annotation instanceof OA\ExternalDocumentation
) {
throw new \LogicException(sprintf('Using the annotation "%s" as a root annotation in "%s::%s()" is not allowed.', get_class($annotation), $method->getDeclaringClass()->name, $method->name));
}
$implicitAnnotations[] = $annotation;
}
if (empty($implicitAnnotations) && empty(get_object_vars($mergeProperties))) {
continue;
}
foreach ($httpMethods as $httpMethod) {
$operation = Util::getOperation($path, $httpMethod);
$operation->merge($implicitAnnotations);
$operation->mergeProperties($mergeProperties);
if (Generator::UNDEFINED === $operation->operationId) {
$operation->operationId = $httpMethod.'_'.$routeName;
}
}
}
// Reset the Generator after the parsing
$this->setContext(null);
}
private function getMethodsToParse(): \Generator
{
foreach ($this->routeCollection->all() as $routeName => $route) {
if (!$route->hasDefault('_controller')) {
continue;
}
$controller = $route->getDefault('_controller');
$reflectedMethod = $this->controllerReflector->getReflectionMethod($controller);
if (null === $reflectedMethod) {
continue;
}
$path = $this->normalizePath($route->getPath());
$supportedHttpMethods = $this->getSupportedHttpMethods($route);
if (empty($supportedHttpMethods)) {
$this->logger->warning('None of the HTTP methods specified for path {path} are supported by swagger-ui, skipping this path', [
'path' => $path,
]);
continue;
}
yield $reflectedMethod => [$path, $supportedHttpMethods, $routeName];
}
}
private function getSupportedHttpMethods(Route $route): array
{
$allMethods = Util::OPERATIONS;
$methods = array_map('strtolower', $route->getMethods());
return array_intersect($methods ?: $allMethods, $allMethods);
}
private function normalizePath(string $path): string
{
if ('.{_format}' === substr($path, -10)) {
$path = substr($path, 0, -10);
}
return $path;
}
/**
* @param \ReflectionClass|\ReflectionMethod $reflection
*
* @return OA\AbstractAnnotation[]
*/
private function getAttributesAsAnnotation($reflection, \OpenApi\Context $context): array
{
$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;
}
}
@@ -0,0 +1,64 @@
<?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\Describer;
use Nelmio\ApiDocBundle\RouteDescriber\RouteDescriberInterface;
use Nelmio\ApiDocBundle\Util\ControllerReflector;
use OpenApi\Annotations as OA;
use Symfony\Component\Routing\RouteCollection;
final class RouteDescriber implements DescriberInterface, ModelRegistryAwareInterface
{
use ModelRegistryAwareTrait;
private $routeCollection;
private $controllerReflector;
private $routeDescribers;
/**
* @param RouteDescriberInterface[]|iterable $routeDescribers
*/
public function __construct(RouteCollection $routeCollection, ControllerReflector $controllerReflector, $routeDescribers)
{
$this->routeCollection = $routeCollection;
$this->controllerReflector = $controllerReflector;
$this->routeDescribers = $routeDescribers;
}
public function describe(OA\OpenApi $api)
{
if (0 === count($this->routeDescribers)) {
return;
}
foreach ($this->routeCollection->all() as $route) {
if (!$route->hasDefault('_controller')) {
continue;
}
// if able to resolve the controller
$controller = $route->getDefault('_controller');
if ($method = $this->controllerReflector->getReflectionMethod($controller)) {
// Extract as many information as possible about this route
foreach ($this->routeDescribers as $describer) {
if ($describer instanceof ModelRegistryAwareInterface) {
$describer->setModelRegistry($this->modelRegistry);
}
$describer->describe($api, $route, $method);
}
}
}
}
}