welcome back to dyb-tech
This commit is contained in:
@@ -0,0 +1,96 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Nelmio\ApiDocBundle\Processor;
|
||||
|
||||
use Nelmio\ApiDocBundle\OpenApiPhp\Util;
|
||||
use Nelmio\ApiDocBundle\RouteDescriber\RouteArgumentDescriber\SymfonyMapQueryStringDescriber;
|
||||
use OpenApi\Analysis;
|
||||
use OpenApi\Annotations as OA;
|
||||
use OpenApi\Generator;
|
||||
use OpenApi\Processors\ProcessorInterface;
|
||||
use Symfony\Component\HttpKernel\ControllerMetadata\ArgumentMetadata;
|
||||
|
||||
/**
|
||||
* A processor that adds query parameters to operations that have a MapQueryString attribute.
|
||||
* A processor is used to ensure that a Model has been created.
|
||||
*
|
||||
* @see SymfonyMapQueryStringDescriber
|
||||
*/
|
||||
final class MapQueryStringProcessor implements ProcessorInterface
|
||||
{
|
||||
public function __invoke(Analysis $analysis)
|
||||
{
|
||||
/** @var OA\Operation[] $operations */
|
||||
$operations = $analysis->getAnnotationsOfType(OA\Operation::class);
|
||||
|
||||
foreach ($operations as $operation) {
|
||||
if (!isset($operation->_context->{SymfonyMapQueryStringDescriber::CONTEXT_KEY})) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$mapQueryStringContexts = $operation->_context->{SymfonyMapQueryStringDescriber::CONTEXT_KEY};
|
||||
if (!is_array($mapQueryStringContexts)) {
|
||||
throw new \LogicException(sprintf('MapQueryString contexts not found for operation "%s"', $operation->operationId));
|
||||
}
|
||||
|
||||
foreach ($mapQueryStringContexts as $mapQueryStringContext) {
|
||||
$this->addQueryParameters($analysis, $operation, $mapQueryStringContext);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private function addQueryParameters(Analysis $analysis, OA\Operation $operation, array $mapQueryStringContext): void
|
||||
{
|
||||
$argumentMetaData = $mapQueryStringContext[SymfonyMapQueryStringDescriber::CONTEXT_ARGUMENT_METADATA];
|
||||
if (!$argumentMetaData instanceof ArgumentMetadata) {
|
||||
throw new \LogicException(sprintf('MapQueryString ArgumentMetaData not found for operation "%s"', $operation->operationId));
|
||||
}
|
||||
|
||||
$modelRef = $mapQueryStringContext[SymfonyMapQueryStringDescriber::CONTEXT_MODEL_REF];
|
||||
if (!isset($modelRef)) {
|
||||
throw new \LogicException(sprintf('MapQueryString Model reference not found for operation "%s"', $operation->operationId));
|
||||
}
|
||||
|
||||
$nativeModelName = str_replace(OA\Components::SCHEMA_REF, '', $modelRef);
|
||||
|
||||
$schemaModel = Util::getSchema($analysis->openapi, $nativeModelName);
|
||||
|
||||
// There are no properties to map to query parameters
|
||||
if (Generator::UNDEFINED === $schemaModel->properties) {
|
||||
return;
|
||||
}
|
||||
|
||||
$isModelOptional = $argumentMetaData->hasDefaultValue() || $argumentMetaData->isNullable();
|
||||
|
||||
foreach ($schemaModel->properties as $property) {
|
||||
$name = 'array' === $property->type
|
||||
? $property->property.'[]'
|
||||
: $property->property;
|
||||
|
||||
$operationParameter = Util::getOperationParameter($operation, $name, 'query');
|
||||
|
||||
// Remove incompatible properties
|
||||
$propertyVars = get_object_vars($property);
|
||||
unset($propertyVars['property']);
|
||||
|
||||
$schema = new OA\Schema($propertyVars);
|
||||
|
||||
Util::modifyAnnotationValue($operationParameter, 'schema', $schema);
|
||||
Util::modifyAnnotationValue($operationParameter, 'name', $property->property);
|
||||
Util::modifyAnnotationValue($operationParameter, 'description', $schema->description);
|
||||
Util::modifyAnnotationValue($operationParameter, 'required', $schema->required);
|
||||
Util::modifyAnnotationValue($operationParameter, 'deprecated', $schema->deprecated);
|
||||
Util::modifyAnnotationValue($operationParameter, 'example', $schema->example);
|
||||
|
||||
if ($isModelOptional) {
|
||||
Util::modifyAnnotationValue($operationParameter, 'required', false);
|
||||
} elseif (is_array($schemaModel->required) && in_array($property->property, $schemaModel->required, true)) {
|
||||
Util::modifyAnnotationValue($operationParameter, 'required', true);
|
||||
} else {
|
||||
Util::modifyAnnotationValue($operationParameter, 'required', false);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,105 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Nelmio\ApiDocBundle\Processor;
|
||||
|
||||
use Nelmio\ApiDocBundle\OpenApiPhp\Util;
|
||||
use Nelmio\ApiDocBundle\RouteDescriber\RouteArgumentDescriber\SymfonyMapRequestPayloadDescriber;
|
||||
use OpenApi\Analysis;
|
||||
use OpenApi\Annotations as OA;
|
||||
use OpenApi\Generator;
|
||||
use OpenApi\Processors\ProcessorInterface;
|
||||
use Symfony\Component\HttpKernel\Attribute\MapRequestPayload;
|
||||
use Symfony\Component\HttpKernel\ControllerMetadata\ArgumentMetadata;
|
||||
|
||||
/**
|
||||
* A processor that adds query parameters to operations that have a MapRequestPayload attribute.
|
||||
* A processor is used to ensure that a Model has been created.
|
||||
*
|
||||
* @see SymfonyMapRequestPayloadDescriber
|
||||
*/
|
||||
final class MapRequestPayloadProcessor implements ProcessorInterface
|
||||
{
|
||||
public function __invoke(Analysis $analysis)
|
||||
{
|
||||
/** @var OA\Operation[] $operations */
|
||||
$operations = $analysis->getAnnotationsOfType(OA\Operation::class);
|
||||
|
||||
foreach ($operations as $operation) {
|
||||
if (!isset($operation->_context->{SymfonyMapRequestPayloadDescriber::CONTEXT_ARGUMENT_METADATA})) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$argumentMetaData = $operation->_context->{SymfonyMapRequestPayloadDescriber::CONTEXT_ARGUMENT_METADATA};
|
||||
if (!$argumentMetaData instanceof ArgumentMetadata) {
|
||||
throw new \LogicException(sprintf('MapRequestPayload ArgumentMetaData not found for operation "%s"', $operation->operationId));
|
||||
}
|
||||
|
||||
/** @var MapRequestPayload $attribute */
|
||||
if (!$attribute = $argumentMetaData->getAttributes(MapRequestPayload::class, ArgumentMetadata::IS_INSTANCEOF)[0] ?? null) {
|
||||
throw new \LogicException(sprintf('Operation "%s" does not contain attribute of "%s', $operation->operationId, MapRequestPayload::class));
|
||||
}
|
||||
|
||||
$modelRef = $operation->_context->{SymfonyMapRequestPayloadDescriber::CONTEXT_MODEL_REF};
|
||||
if (!isset($modelRef)) {
|
||||
throw new \LogicException(sprintf('MapRequestPayload Model reference not found for operation "%s"', $operation->operationId));
|
||||
}
|
||||
|
||||
/** @var OA\RequestBody $requestBody */
|
||||
$requestBody = Util::getChild($operation, OA\RequestBody::class);
|
||||
Util::modifyAnnotationValue($requestBody, 'required', !($argumentMetaData->hasDefaultValue() || $argumentMetaData->isNullable()));
|
||||
|
||||
$formats = $attribute->acceptFormat;
|
||||
if (!is_array($formats)) {
|
||||
$formats = [$attribute->acceptFormat ?? 'json'];
|
||||
}
|
||||
|
||||
foreach ($formats as $format) {
|
||||
if (!Generator::isDefault($requestBody->content)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$contentSchema = $this->getContentSchemaForType($requestBody, $format);
|
||||
|
||||
Util::modifyAnnotationValue($contentSchema, 'ref', $modelRef);
|
||||
|
||||
if ($argumentMetaData->isNullable()) {
|
||||
$contentSchema->nullable = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private function getContentSchemaForType(OA\RequestBody $requestBody, string $type): OA\Schema
|
||||
{
|
||||
Util::modifyAnnotationValue($requestBody, 'content', []);
|
||||
switch ($type) {
|
||||
case 'json':
|
||||
$contentType = 'application/json';
|
||||
|
||||
break;
|
||||
case 'xml':
|
||||
$contentType = 'application/xml';
|
||||
|
||||
break;
|
||||
default:
|
||||
throw new \InvalidArgumentException('Unsupported media type');
|
||||
}
|
||||
|
||||
if (!isset($requestBody->content[$contentType])) {
|
||||
$weakContext = Util::createWeakContext($requestBody->_context);
|
||||
$requestBody->content[$contentType] = new OA\MediaType(
|
||||
[
|
||||
'mediaType' => $contentType,
|
||||
'_context' => $weakContext,
|
||||
]
|
||||
);
|
||||
}
|
||||
|
||||
return Util::getChild(
|
||||
$requestBody->content[$contentType],
|
||||
OA\Schema::class
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,41 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Nelmio\ApiDocBundle\Processor;
|
||||
|
||||
use OpenApi\Analysis;
|
||||
use OpenApi\Annotations as OA;
|
||||
use OpenApi\Generator;
|
||||
use OpenApi\Processors\ProcessorInterface;
|
||||
|
||||
/**
|
||||
* Processor to clean up the generated OpenAPI documentation for nullable properties.
|
||||
*/
|
||||
final class NullablePropertyProcessor implements ProcessorInterface
|
||||
{
|
||||
public function __invoke(Analysis $analysis): void
|
||||
{
|
||||
if (Generator::isDefault($analysis->openapi->components) || Generator::isDefault($analysis->openapi->components->schemas)) {
|
||||
return;
|
||||
}
|
||||
|
||||
/** @var OA\Schema[] $schemas */
|
||||
$schemas = $analysis->openapi->components->schemas;
|
||||
|
||||
foreach ($schemas as $schema) {
|
||||
if (Generator::UNDEFINED === $schema->properties) {
|
||||
continue;
|
||||
}
|
||||
|
||||
foreach ($schema->properties as $property) {
|
||||
if (Generator::UNDEFINED !== $property->nullable) {
|
||||
if (!$property->nullable) {
|
||||
// if already false mark it as undefined (so it does not show up as `nullable: false`)
|
||||
$property->nullable = Generator::UNDEFINED;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user