diff --git a/Describer/ParameterRefMergeDescriber.php b/Describer/ParameterRefMergeDescriber.php new file mode 100644 index 0000000..bebf8d3 --- /dev/null +++ b/Describer/ParameterRefMergeDescriber.php @@ -0,0 +1,79 @@ +getPaths() as $path) { + /** @var Operation $operation */ + foreach ($path->getOperations() as $operation) { + $this->checkOperation($api, $operation); + } + } + } + + /** + * This method removes parameters that also have a ref version as they will be duplicated otherwise. + */ + private function checkOperation(Swagger $api, Operation $operation) + { + $parametersToRemove = []; + + /** @var Parameter[] $globalParams */ + $globalParams = $api->getParameters(); + /** @var Parameters|Parameter[] $currentParams */ + $currentParams = $operation->getParameters(); + + foreach ($currentParams as $parameter) { + $ref = $parameter->getRef(); + + if (null === $ref) { + // we only concern ourselves with '$ref' parameters + continue; + } + + $ref = \mb_substr($ref, 13); // trim the '#/parameters/' part of ref + if (!isset($globalParams[$ref])) { + // this really shouldn't happen, if it does there will be other failures elsewhere, so just ignore here + continue; + } + + $refParameter = $globalParams[$ref]; + // param ids are in form {name}/{in} + $refParameterId = \sprintf('%s/%s', $refParameter->getName(), $refParameter->getIn()); + if ($currentParams->has($refParameterId)) { + // if we got here it means a duplicate parameter is directly defined, schedule it for removal + $parametersToRemove[] = $currentParams->get($refParameterId); + } + } + + foreach ($parametersToRemove as $parameterToRemove) { + $currentParams->remove($parameterToRemove); + } + } +} diff --git a/Resources/config/services.xml b/Resources/config/services.xml index 978930e..c6ad025 100644 --- a/Resources/config/services.xml +++ b/Resources/config/services.xml @@ -32,6 +32,10 @@ + + + + diff --git a/Tests/Describer/AbstractDescriberTest.php b/Tests/Describer/AbstractDescriberTest.php index b1cca2d..f73dcb3 100644 --- a/Tests/Describer/AbstractDescriberTest.php +++ b/Tests/Describer/AbstractDescriberTest.php @@ -12,10 +12,12 @@ namespace Nelmio\ApiDocBundle\Tests\Describer; use EXSyst\Component\Swagger\Swagger; +use Nelmio\ApiDocBundle\Describer\DescriberInterface; use PHPUnit\Framework\TestCase; abstract class AbstractDescriberTest extends TestCase { + /** @var DescriberInterface */ protected $describer; protected function getSwaggerDoc(): Swagger diff --git a/Tests/Describer/ParameterRefMergeDescriberTest.php b/Tests/Describer/ParameterRefMergeDescriberTest.php new file mode 100644 index 0000000..bd2b666 --- /dev/null +++ b/Tests/Describer/ParameterRefMergeDescriberTest.php @@ -0,0 +1,64 @@ + '2.0', + 'info' => ['title' => 'Ref Test'], + 'paths' => [ + '/api/{version}/product' => [ + 'get' => [ + 'parameters' => [ + [ + '$ref' => '#/parameters/versionParam', + ], + [ + 'name' => 'version', + 'in' => 'path', + 'required' => true, + 'type' => 'string', + 'pattern' => 'v\\d+', + ], + ], + ], + ], + ], + 'parameters' => [ + 'versionParam' => [ + 'name' => 'version', + 'in' => 'path', + 'required' => true, + 'type' => 'string', + ], + ], + ]; + $api = new Swagger($apiDef); + $this->describer->describe($api); + + $describedData = $api->toArray(); + // only one parameter should remain as they were duplicates + $this->assertCount(1, $describedData['paths']['/api/{version}/product']['get']['parameters']); + } + + protected function setUp() + { + $this->describer = new ParameterRefMergeDescriber(); + } +}