diff --git a/src/Writing/OpenApiSpecGenerators/BaseGenerator.php b/src/Writing/OpenApiSpecGenerators/BaseGenerator.php index 0674e5c1..aaf54346 100644 --- a/src/Writing/OpenApiSpecGenerators/BaseGenerator.php +++ b/src/Writing/OpenApiSpecGenerators/BaseGenerator.php @@ -230,7 +230,7 @@ public function generateSchemaForResponseValue(mixed $value, OutputEndpointData $path ); if ($required) { - $schema['required'] = $required; + $schema['items']['required'] = $required; } } } @@ -239,7 +239,7 @@ public function generateSchemaForResponseValue(mixed $value, OutputEndpointData } /** - * Given an enpoint and a set of object keys at a path, return the properties that are specified as required. + * Given an endpoint and a set of object keys at a path, return the properties that are specified as required. */ public function filterRequiredResponseFields(OutputEndpointData $endpoint, array $properties, string $path = ''): array { @@ -485,19 +485,27 @@ protected function generateResponseContentSpec(?string $responseContent, OutputE // Non-empty array if (is_object($decoded[0])) { - // If the first item is an object, we assume it's an array of objects' + // If the first item is an object, we assume it's an array of objects $properties = collect($decoded[0])->mapWithKeys(function ($value, $key) use ($endpoint) { return [$key => $this->generateSchemaForResponseValue($value, $endpoint, $key)]; })->toArray(); + $requiredFields = $this->filterRequiredResponseFields($endpoint, array_keys($properties)); + + $items = [ + 'type' => $this->convertScribeOrPHPTypeToOpenAPIType(gettype($decoded[0])), + 'properties' => $this->objectIfEmpty($properties), + ]; + + if ($requiredFields) { + $items['required'] = $requiredFields; + } + return [ $contentType => [ 'schema' => [ 'type' => 'array', - 'items' => [ - 'type' => $this->convertScribeOrPHPTypeToOpenAPIType(gettype($decoded[0])), - 'properties' => $this->objectIfEmpty($properties), - ], + 'items' => $items, 'example' => $decoded, ], ], diff --git a/tests/Unit/OpenAPISpecWriterTest.php b/tests/Unit/OpenAPISpecWriterTest.php index e7952d31..49246592 100644 --- a/tests/Unit/OpenAPISpecWriterTest.php +++ b/tests/Unit/OpenAPISpecWriterTest.php @@ -884,6 +884,60 @@ public function adds_responses_correctly_as_array_of_objects() ], $results['paths']['/path1']['get']['responses']); } + /** @test */ + public function adds_required_fields_on_bare_array_of_objects() + { + $endpointData = $this->createMockEndpointData([ + 'httpMethods' => ['GET'], + 'uri' => '/path1', + 'responses' => [ + [ + 'status' => 200, + 'description' => 'Successfully.', + 'content' => '[{"id": 1, "name": "John"}, {"id": 2, "name": "Jane"}]', + ], + ], + 'responseFields' => [ + 'id' => [ + 'name' => 'id', + 'type' => 'integer', + 'description' => 'The ID', + 'required' => true, + ], + 'name' => [ + 'name' => 'name', + 'type' => 'string', + 'description' => 'The name', + 'required' => true, + ], + ], + ]); + + $groups = [$this->createGroup([$endpointData])]; + $results = $this->generate($groups); + + $this->assertArraySubset([ + '200' => [ + 'description' => 'Successfully.', + 'content' => [ + 'application/json' => [ + 'schema' => [ + 'type' => 'array', + 'items' => [ + 'type' => 'object', + 'properties' => [ + 'id' => ['type' => 'integer'], + 'name' => ['type' => 'string'], + ], + 'required' => ['id', 'name'], + ], + ], + ], + ], + ], + ], $results['paths']['/path1']['get']['responses']); + } + /** @test */ public function adds_response_content_type_correctly() { @@ -1175,11 +1229,11 @@ public function adds_required_fields_on_array_of_objects() 'description' => 'Is primary resource', ], ], - ], - 'required' => [ - 'name', - 'uuid', - 'primary', + 'required' => [ + 'name', + 'uuid', + 'primary', + ], ], ], ], @@ -1483,11 +1537,11 @@ public function adds_enum_values_to_response_properties() 'description' => 'Is primary resource', ], ], - ], - 'required' => [ - 'name', - 'uuid', - 'primary', + 'required' => [ + 'name', + 'uuid', + 'primary', + ], ], ], ],