Skip to content

Commit b859906

Browse files
chore: revamp OpenApiFactory
1 parent ec99ae7 commit b859906

File tree

3 files changed

+110
-65
lines changed

3 files changed

+110
-65
lines changed

src/OpenApi/Factory/OpenApiFactory.php

Lines changed: 80 additions & 56 deletions
Original file line numberDiff line numberDiff line change
@@ -131,14 +131,12 @@ private function collectPaths(ApiResource $resource, ResourceMetadataCollection
131131
}
132132

133133
$openapiOperation = $operation->getOpenapi();
134-
$hasOpenapi = null !== $openapiOperation && !\is_bool($openapiOperation);
135134

136135
// Operation ignored from OpenApi
137136
if ($operation instanceof HttpOperation && false === $openapiOperation) {
138137
continue;
139138
}
140139

141-
$uriVariables = $operation->getUriVariables();
142140
$resourceClass = $operation->getClass() ?? $resource->getClass();
143141
$routeName = $operation->getRouteName() ?? $operation->getName();
144142

@@ -159,17 +157,69 @@ private function collectPaths(ApiResource $resource, ResourceMetadataCollection
159157
continue;
160158
}
161159

160+
if (!\is_object($openapiOperation)) {
161+
$openapiOperation = new Model\Operation();
162+
}
163+
164+
// Complete with defaults
165+
$openapiOperation = new Model\Operation(
166+
operationId: null !== $openapiOperation->getOperationId() ? $openapiOperation->getOperationId() : $this->normalizeOperationName($operationName),
167+
tags: null !== $openapiOperation->getTags() ? $openapiOperation->getTags() : [$operation->getShortName() ?: $resourceShortName],
168+
responses: null !== $openapiOperation->getResponses() ? $openapiOperation->getResponses() : [],
169+
summary: null !== $openapiOperation->getSummary() ? $openapiOperation->getSummary() : $this->getPathDescription($resourceShortName, $method, $operation instanceof CollectionOperationInterface),
170+
description: null !== $openapiOperation->getDescription() ? $openapiOperation->getDescription() : $this->getPathDescription($resourceShortName, $method, $operation instanceof CollectionOperationInterface),
171+
externalDocs: $openapiOperation->getExternalDocs(),
172+
parameters: null !== $openapiOperation->getParameters() ? $openapiOperation->getParameters() : [],
173+
requestBody: $openapiOperation->getRequestBody(),
174+
callbacks: $openapiOperation->getCallbacks(),
175+
deprecated: null !== $openapiOperation->getDeprecated() ? $openapiOperation->getDeprecated() : (bool) $operation->getDeprecationReason(),
176+
security: null !== $openapiOperation->getSecurity() ? $openapiOperation->getSecurity() : null,
177+
servers: null !== $openapiOperation->getServers() ? $openapiOperation->getServers() : null,
178+
extensionProperties: $openapiOperation->getExtensionProperties(),
179+
);
180+
162181
[$requestMimeTypes, $responseMimeTypes] = $this->getMimeTypes($operation);
163182

164-
$operationId = $hasOpenapi && $openapiOperation->getOperationId() ? $openapiOperation->getOperationId() : $this->normalizeOperationName($operationName);
165183
// TODO Remove in 4.0
166-
if ($operation->getOpenapiContext()['operationId'] ?? false) {
184+
foreach (['operationId', 'tags', 'summary', 'description', 'security', 'servers'] as $key) {
185+
if (null !== ($operation->getOpenapiContext()[$key] ?? null)) {
186+
trigger_deprecation(
187+
'api-platform/core',
188+
'3.1',
189+
'The "openapiContext" option is deprecated, use "openapi" instead.'
190+
);
191+
$openapiOperation = $openapiOperation->{'with'.ucfirst($key)}($operation->getOpenapiContext()[$key]);
192+
}
193+
}
194+
195+
// TODO Remove in 4.0
196+
if (null !== ($operation->getOpenapiContext()['externalDocs'] ?? null)) {
197+
trigger_deprecation(
198+
'api-platform/core',
199+
'3.1',
200+
'The "openapiContext" option is deprecated, use "openapi" instead.'
201+
);
202+
$openapiOperation = $openapiOperation->withExternalDocs(new ExternalDocumentation($operation->getOpenapiContext()['externalDocs']['description'] ?? null, $operation->getOpenapiContext()['externalDocs']['url']));
203+
}
204+
205+
// TODO Remove in 4.0
206+
if (null !== ($operation->getOpenapiContext()['callbacks'] ?? null)) {
167207
trigger_deprecation(
168208
'api-platform/core',
169209
'3.1',
170210
'The "openapiContext" option is deprecated, use "openapi" instead.'
171211
);
172-
$operationId = $operation->getOpenapiContext()['operationId'];
212+
$openapiOperation = $openapiOperation->withCallbacks(new \ArrayObject($operation->getOpenapiContext()['callbacks']));
213+
}
214+
215+
// TODO Remove in 4.0
216+
if (null !== ($operation->getOpenapiContext()['deprecated'] ?? null)) {
217+
trigger_deprecation(
218+
'api-platform/core',
219+
'3.1',
220+
'The "openapiContext" option is deprecated, use "openapi" instead.'
221+
);
222+
$openapiOperation = $openapiOperation->withDeprecated((bool) $operation->getOpenapiContext()['deprecated']);
173223
}
174224

175225
if ($path) {
@@ -190,9 +240,6 @@ private function collectPaths(ApiResource $resource, ResourceMetadataCollection
190240
$this->appendSchemaDefinitions($schemas, $operationOutputSchema->getDefinitions());
191241
}
192242

193-
$parameters = $hasOpenapi ? $openapiOperation->getParameters() : [];
194-
$responses = [];
195-
196243
// TODO Remove in 4.0
197244
if ($operation->getOpenapiContext()['parameters'] ?? false) {
198245
trigger_deprecation(
@@ -204,29 +251,30 @@ private function collectPaths(ApiResource $resource, ResourceMetadataCollection
204251
foreach ($operation->getOpenapiContext()['parameters'] as $parameter) {
205252
$parameters[] = new Parameter($parameter['name'], $parameter['in'], $parameter['description'] ?? '', $parameter['required'] ?? false, $parameter['deprecated'] ?? false, $parameter['allowEmptyValue'] ?? false, $parameter['schema'] ?? [], $parameter['style'] ?? null, $parameter['explode'] ?? false, $parameter['allowReserved '] ?? false, $parameter['example'] ?? null, isset($parameter['examples']) ? new \ArrayObject($parameter['examples']) : null, isset($parameter['content']) ? new \ArrayObject($parameter['content']) : null);
206253
}
254+
$openapiOperation = $openapiOperation->withParameters($parameters);
207255
}
208256

209257
// Set up parameters
210-
foreach ($uriVariables ?? [] as $parameterName => $uriVariable) {
258+
foreach ($operation->getUriVariables() ?? [] as $parameterName => $uriVariable) {
211259
if ($uriVariable->getExpandedValue() ?? false) {
212260
continue;
213261
}
214262

215263
$parameter = new Parameter($parameterName, 'path', (new \ReflectionClass($uriVariable->getFromClass()))->getShortName().' identifier', true, false, false, ['type' => 'string']);
216-
if ($this->hasParameter($parameter, $parameters)) {
264+
if ($this->hasParameter($openapiOperation, $parameter)) {
217265
continue;
218266
}
219267

220-
$parameters[] = $parameter;
268+
$openapiOperation = $openapiOperation->withParameter($parameter);
221269
}
222270

223271
if ($operation instanceof CollectionOperationInterface && HttpOperation::METHOD_POST !== $method) {
224272
foreach (array_merge($this->getPaginationParameters($operation), $this->getFiltersParameters($operation)) as $parameter) {
225-
if ($this->hasParameter($parameter, $parameters)) {
273+
if ($this->hasParameter($openapiOperation, $parameter)) {
226274
continue;
227275
}
228276

229-
$parameters[] = $parameter;
277+
$openapiOperation = $openapiOperation->withParameter($parameter);
230278
}
231279
}
232280

@@ -235,66 +283,59 @@ private function collectPaths(ApiResource $resource, ResourceMetadataCollection
235283
case HttpOperation::METHOD_GET:
236284
$successStatus = (string) $operation->getStatus() ?: 200;
237285
$responseContent = $this->buildContent($responseMimeTypes, $operationOutputSchemas);
238-
$responses[$successStatus] = new Response(sprintf('%s %s', $resourceShortName, $operation instanceof CollectionOperationInterface ? 'collection' : 'resource'), $responseContent);
286+
$openapiOperation = $openapiOperation->withResponse($successStatus, new Response(sprintf('%s %s', $resourceShortName, $operation instanceof CollectionOperationInterface ? 'collection' : 'resource'), $responseContent));
239287
break;
240288
case HttpOperation::METHOD_POST:
241289
$responseLinks = $this->getLinks($resourceMetadataCollection, $operation);
242290
$responseContent = $this->buildContent($responseMimeTypes, $operationOutputSchemas);
243291
$successStatus = (string) $operation->getStatus() ?: 201;
244-
$responses[$successStatus] = new Response(sprintf('%s resource created', $resourceShortName), $responseContent, null, $responseLinks);
245-
$responses['400'] = new Response('Invalid input');
246-
$responses['422'] = new Response('Unprocessable entity');
292+
$openapiOperation = $openapiOperation->withResponse($successStatus, new Response(sprintf('%s resource created', $resourceShortName), $responseContent, null, $responseLinks));
293+
$openapiOperation = $openapiOperation->withResponse(400, new Response('Invalid input'));
294+
$openapiOperation = $openapiOperation->withResponse(422, new Response('Unprocessable entity'));
247295
break;
248296
case HttpOperation::METHOD_PATCH:
249297
case HttpOperation::METHOD_PUT:
250298
$responseLinks = $this->getLinks($resourceMetadataCollection, $operation);
251299
$successStatus = (string) $operation->getStatus() ?: 200;
252300
$responseContent = $this->buildContent($responseMimeTypes, $operationOutputSchemas);
253-
$responses[$successStatus] = new Response(sprintf('%s resource updated', $resourceShortName), $responseContent, null, $responseLinks);
254-
$responses['400'] = new Response('Invalid input');
255-
$responses['422'] = new Response('Unprocessable entity');
301+
$openapiOperation = $openapiOperation->withResponse($successStatus, new Response(sprintf('%s resource updated', $resourceShortName), $responseContent, null, $responseLinks));
302+
$openapiOperation = $openapiOperation->withResponse(400, new Response('Invalid input'));
303+
$openapiOperation = $openapiOperation->withResponse(422, new Response('Unprocessable entity'));
256304
break;
257305
case HttpOperation::METHOD_DELETE:
258306
$successStatus = (string) $operation->getStatus() ?: 204;
259-
$responses[$successStatus] = new Response(sprintf('%s resource deleted', $resourceShortName));
307+
$openapiOperation = $openapiOperation->withResponse($successStatus, new Response(sprintf('%s resource deleted', $resourceShortName)));
260308
break;
261309
}
262310

263311
if (!$operation instanceof CollectionOperationInterface && HttpOperation::METHOD_POST !== $operation->getMethod()) {
264-
$responses['404'] = new Response('Resource not found');
312+
$openapiOperation = $openapiOperation->withResponse(404, new Response('Resource not found'));
265313
}
266314

267-
if (!$responses) {
268-
$responses['default'] = new Response('Unexpected error');
315+
if (!$openapiOperation->getResponses()) {
316+
$openapiOperation = $openapiOperation->withResponse('default', new Response('Unexpected error'));
269317
}
270318

271-
if ($hasOpenapi && ($contextResponses = $openapiOperation->getResponses() ?: false)) {
272-
foreach ($contextResponses as $statusCode => $contextResponse) {
273-
$responses[$statusCode] = $contextResponse;
274-
}
275-
} elseif ($contextResponses = $operation->getOpenapiContext()['responses'] ?? false) {
319+
if ($contextResponses = $operation->getOpenapiContext()['responses'] ?? false) {
276320
// TODO Remove this "elseif" in 4.0
277321
trigger_deprecation(
278322
'api-platform/core',
279323
'3.1',
280324
'The "openapiContext" option is deprecated, use "openapi" instead.'
281325
);
282326
foreach ($contextResponses as $statusCode => $contextResponse) {
283-
$responses[$statusCode] = new Response($contextResponse['description'] ?? '', isset($contextResponse['content']) ? new \ArrayObject($contextResponse['content']) : null, isset($contextResponse['headers']) ? new \ArrayObject($contextResponse['headers']) : null, isset($contextResponse['links']) ? new \ArrayObject($contextResponse['links']) : null);
327+
$openapiOperation = $openapiOperation->withResponse($statusCode, new Response($contextResponse['description'] ?? '', isset($contextResponse['content']) ? new \ArrayObject($contextResponse['content']) : null, isset($contextResponse['headers']) ? new \ArrayObject($contextResponse['headers']) : null, isset($contextResponse['links']) ? new \ArrayObject($contextResponse['links']) : null));
284328
}
285329
}
286330

287-
$requestBody = null;
288-
if ($hasOpenapi && ($contextRequestBody = $openapiOperation->getRequestBody() ?: false)) {
289-
$requestBody = $contextRequestBody;
290-
} elseif ($contextRequestBody = $operation->getOpenapiContext()['requestBody'] ?? false) {
331+
if ($contextRequestBody = $operation->getOpenapiContext()['requestBody'] ?? false) {
291332
// TODO Remove this "elseif" in 4.0
292333
trigger_deprecation(
293334
'api-platform/core',
294335
'3.1',
295336
'The "openapiContext" option is deprecated, use "openapi" instead.'
296337
);
297-
$requestBody = new RequestBody($contextRequestBody['description'] ?? '', new \ArrayObject($contextRequestBody['content']), $contextRequestBody['required'] ?? false);
338+
$openapiOperation = $openapiOperation->withRequestBody(new RequestBody($contextRequestBody['description'] ?? '', new \ArrayObject($contextRequestBody['content']), $contextRequestBody['required'] ?? false));
298339
} elseif (\in_array($method, [HttpOperation::METHOD_PATCH, HttpOperation::METHOD_PUT, HttpOperation::METHOD_POST], true)) {
299340
$operationInputSchemas = [];
300341
foreach ($requestMimeTypes as $operationFormat) {
@@ -303,16 +344,9 @@ private function collectPaths(ApiResource $resource, ResourceMetadataCollection
303344
$this->appendSchemaDefinitions($schemas, $operationInputSchema->getDefinitions());
304345
}
305346

306-
$requestBody = new RequestBody(sprintf('The %s %s resource', HttpOperation::METHOD_POST === $method ? 'new' : 'updated', $resourceShortName), $this->buildContent($requestMimeTypes, $operationInputSchemas), true);
347+
$openapiOperation = $openapiOperation->withRequestBody(new RequestBody(sprintf('The %s %s resource', HttpOperation::METHOD_POST === $method ? 'new' : 'updated', $resourceShortName), $this->buildContent($requestMimeTypes, $operationInputSchemas), true));
307348
}
308349

309-
$openapiOperation = ($hasOpenapi ? $openapiOperation : new Model\Operation())
310-
// Defaults
311-
->withTags($hasOpenapi && $openapiOperation->getTags() ? $openapiOperation->getTags() : [$operation->getShortName() ?: $resourceShortName])
312-
->withSummary($hasOpenapi && $openapiOperation->getSummary() ? $openapiOperation->getSummary() : $this->getPathDescription($resourceShortName, $method, $operation instanceof CollectionOperationInterface))
313-
->withDescription($hasOpenapi && $openapiOperation->getDescription() ? $openapiOperation->getDescription() : $this->getPathDescription($resourceShortName, $method, $operation instanceof CollectionOperationInterface))
314-
->withDeprecated($hasOpenapi && $openapiOperation->getDeprecated() ? $openapiOperation->getDeprecated() : (bool) $operation->getDeprecationReason());
315-
316350
// TODO Remove in 4.0
317351
if (null !== $operation->getOpenapiContext() && \count($operation->getOpenapiContext())) {
318352
trigger_deprecation(
@@ -338,14 +372,7 @@ private function collectPaths(ApiResource $resource, ResourceMetadataCollection
338372
}
339373
}
340374

341-
$pathItem = $pathItem->{'with'.ucfirst($method)}($openapiOperation
342-
->withOperationId($operationId)
343-
->withResponses($responses)
344-
->withParameters($parameters)
345-
->withRequestBody($requestBody)
346-
);
347-
348-
$paths->addPath($path, $pathItem);
375+
$paths->addPath($path, $pathItem->{'with'.ucfirst($method)}($openapiOperation));
349376
}
350377
}
351378

@@ -620,12 +647,9 @@ private function appendSchemaDefinitions(\ArrayObject $schemas, \ArrayObject $de
620647
}
621648
}
622649

623-
/**
624-
* @param Model\Parameter[] $parameters
625-
*/
626-
private function hasParameter(Parameter $parameter, array $parameters): bool
650+
private function hasParameter(Model\Operation $operation, Parameter $parameter): bool
627651
{
628-
foreach ($parameters as $existingParameter) {
652+
foreach ($operation->getParameters() as $existingParameter) {
629653
if ($existingParameter->getName() === $parameter->getName() && $existingParameter->getIn() === $parameter->getIn()) {
630654
return true;
631655
}

0 commit comments

Comments
 (0)