diff --git a/src/Writing/OpenAPISpecWriter.php b/src/Writing/OpenAPISpecWriter.php index 509c01f0..2c4f7973 100644 --- a/src/Writing/OpenAPISpecWriter.php +++ b/src/Writing/OpenAPISpecWriter.php @@ -269,7 +269,32 @@ protected function generateEndpointResponsesSpec(OutputEndpointData $endpoint) $responses[204] = [ 'description' => $this->getResponseDescription($response), ]; + } elseif (isset($responses[$response->status])) { + // If we already have a response for this status code and content type, + // we change to a `oneOf` which includes all the responses + $content = $this->generateResponseContentSpec($response->content, $endpoint); + $contentType = array_keys($content)[0]; + if (isset($responses[$response->status]['content'][$contentType])) { + // If we've already created the oneOf object, add this response + if (isset($responses[$response->status]['content'][$contentType]['schema']['oneOf'])) { + $responses[$response->status]['content'][$contentType]['schema']['oneOf'][] = $content[$contentType]; + } else { + // Create the oneOf object + $existingResponseExample = array_replace([ + 'description' => $responses[$response->status]['description'], + ], $responses[$response->status]['content'][$contentType]['schema']); + $newResponseExample = array_replace([ + 'description' => $this->getResponseDescription($response), + ], $content[$contentType]['schema']); + + $responses[$response->status]['description'] = ''; + $responses[$response->status]['content'][$contentType]['schema'] = [ + 'oneOf' => [$existingResponseExample, $newResponseExample] + ]; + } + } } else { + // Store as the response for this status $responses[$response->status] = [ 'description' => $this->getResponseDescription($response), 'content' => $this->generateResponseContentSpec($response->content, $endpoint), diff --git a/tests/Unit/OpenAPISpecWriterTest.php b/tests/Unit/OpenAPISpecWriterTest.php index 6d2c94a5..648e3529 100644 --- a/tests/Unit/OpenAPISpecWriterTest.php +++ b/tests/Unit/OpenAPISpecWriterTest.php @@ -445,7 +445,7 @@ public function adds_responses_correctly_as_responses_on_operation_object() 'type' => 'string', 'description' => 'Parameter description, ha!', ], - 'sub level 0.sub level 1 key 3.sub level 2 key 1'=> [ + 'sub level 0.sub level 1 key 3.sub level 2 key 1' => [ 'description' => 'This is description of nested object', ] ], @@ -557,6 +557,85 @@ public function adds_responses_correctly_as_responses_on_operation_object() ], $results['paths']['/path2']['put']['responses']); } + /** @test */ + public function adds_multiple_responses_correctly_using_oneOf() + { + $endpointData1 = $this->createMockEndpointData([ + 'httpMethods' => ['POST'], + 'uri' => '/path1', + 'responses' => [ + [ + 'status' => 201, + 'description' => 'This one', + 'content' => '{"this": "one"}', + ], + [ + 'status' => 201, + 'description' => 'No, that one.', + 'content' => '{"that": "one"}', + ], + [ + 'status' => 200, + 'description' => 'A separate one', + 'content' => '{"the other": "one"}', + ], + ], + ]); + $groups = [$this->createGroup([$endpointData1])]; + + $results = $this->generate($groups); + + $this->assertArraySubset([ + '200' => [ + 'description' => 'A separate one', + 'content' => [ + 'application/json' => [ + 'schema' => [ + 'type' => 'object', + 'properties' => [ + 'the other' => [ + 'example' => "one", + 'type' => 'string', + ], + ], + ], + ], + ], + ], + '201' => [ + 'description' => '', + 'content' => [ + 'application/json' => [ + 'schema' => [ + 'oneOf' => [ + [ + 'type' => 'object', + 'description' => 'This one', + 'properties' => [ + 'this' => [ + 'example' => "one", + 'type' => 'string', + ], + ], + ], + [ + 'type' => 'object', + 'description' => 'No, that one.', + 'properties' => [ + 'that' => [ + 'example' => "one", + 'type' => 'string', + ], + ], + ], + ], + ], + ], + ], + ], + ], $results['paths']['/path1']['post']['responses']); + } + protected function createMockEndpointData(array $custom = []): OutputEndpointData { $faker = Factory::create();