Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

add empty check for properties #712

Merged
merged 2 commits into from
Aug 19, 2023
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
47 changes: 24 additions & 23 deletions src/Writing/OpenAPISpecWriter.php
Original file line number Diff line number Diff line change
Expand Up @@ -22,17 +22,9 @@ class OpenAPISpecWriter

private DocumentationConfig $config;

/**
* Object to represent empty values, since empty arrays get serialised as objects.
* Can't use a constant because of initialisation expression.
*
*/
public \stdClass $EMPTY;

public function __construct(DocumentationConfig $config = null)
{
$this->config = $config ?: new DocumentationConfig(config('scribe', []));
$this->EMPTY = new \stdClass();
}

/**
Expand Down Expand Up @@ -241,6 +233,10 @@ protected function generateEndpointRequestBodySpec(OutputEndpointData $endpoint)
$schema['properties'][$name] = $fieldData;
}

// We remove 'properties' if the request body is an array, so we need to check if it's still there
if (array_key_exists('properties', $schema)) {
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

'properties' gets erased when an array is encountered, so we need to check if it's still there

shalvah marked this conversation as resolved.
Show resolved Hide resolved
$schema['properties'] = $this->objectIfEmpty($schema['properties']);
}
$body['required'] = $hasRequiredParameter;

if ($hasFileParameter) {
Expand All @@ -257,7 +253,7 @@ protected function generateEndpointRequestBodySpec(OutputEndpointData $endpoint)
}

// return object rather than empty array, so can get properly serialised as object
return count($body) > 0 ? $body : $this->EMPTY;
return $this->objectIfEmpty($body);
}

protected function generateEndpointResponsesSpec(OutputEndpointData $endpoint)
Expand All @@ -282,7 +278,7 @@ protected function generateEndpointResponsesSpec(OutputEndpointData $endpoint)
}

// return object rather than empty array, so can get properly serialised as object
return count($responses) > 0 ? $responses : $this->EMPTY;
return $this->objectIfEmpty($responses);
}

protected function getResponseDescription(Response $response): string
Expand Down Expand Up @@ -386,16 +382,12 @@ protected function generateResponseContentSpec(?string $responseContent, OutputE
return [$key => $this->generateSchemaForValue($value, $endpoint, $key)];
})->toArray();

if (!count($properties)) {
$properties = $this->EMPTY;
}

return [
'application/json' => [
'schema' => [
'type' => 'object',
'example' => $decoded,
'properties' => $properties,
'properties' => $this->objectIfEmpty($properties),
],
],
];
Expand Down Expand Up @@ -513,9 +505,9 @@ public function generateFieldData($field): array
'type' => 'object',
'description' => $field->description ?: '',
'example' => $field->example,
'properties' => collect($field->__fields)->mapWithKeys(function ($subfield, $subfieldName) {
'properties' => $this->objectIfEmpty(collect($field->__fields)->mapWithKeys(function ($subfield, $subfieldName) {
return [$subfieldName => $this->generateFieldData($subfield)];
})->all(),
})->all()),
];
} else {
return [
Expand All @@ -534,6 +526,15 @@ protected function operationId(OutputEndpointData $endpoint): string
return Str::lower($endpoint->httpMethods[0]) . join('', array_map(fn ($part) => ucfirst($part), $parts));
}

/**
* Given an array, return an object if the array is empty. To be used with fields that are
* required by OpenAPI spec to be objects, since empty arrays get serialised as [].
*/
protected function objectIfEmpty(array $field): array | \stdClass
{
return count($field) > 0 ? $field : new \stdClass();
}

/**
* Given a value, generate the schema for it. The schema consists of: {type:, example:, properties: (if value is an object)},
* and possibly a description for each property.
Expand All @@ -543,17 +544,17 @@ public function generateSchemaForValue(mixed $value, OutputEndpointData $endpoin
{
if ($value instanceof \stdClass) {
$value = (array) $value;
$schema = [
'type' => 'object',
'properties' => [],
];
$properties = [];
// Recurse into the object
foreach($value as $subField => $subValue){
$subFieldPath = sprintf('%s.%s', $path, $subField);
$schema['properties'][$subField] = $this->generateSchemaForValue($subValue, $endpoint, $subFieldPath);
$properties[$subField] = $this->generateSchemaForValue($subValue, $endpoint, $subFieldPath);
}

return $schema;
return [
'type' => 'object',
'properties' => $this->objectIfEmpty($properties),
];
}

$schema = [
Expand Down
Loading