diff --git a/.editorconfig b/.editorconfig
new file mode 100644
index 0000000..e94d260
--- /dev/null
+++ b/.editorconfig
@@ -0,0 +1,8 @@
+[*.html]
+indent_size = 2
+indent_style = space
+
+[*.php]
+indent_size = 2
+tab_width = 2
+indent_style = space
diff --git a/src/PHPDraft/Model/Elements/BasicStructureElement.php b/src/PHPDraft/Model/Elements/BasicStructureElement.php
index 8dc886f..ac7952d 100644
--- a/src/PHPDraft/Model/Elements/BasicStructureElement.php
+++ b/src/PHPDraft/Model/Elements/BasicStructureElement.php
@@ -45,9 +45,9 @@ abstract class BasicStructureElement implements StructureElement
/**
* Object status (required|optional).
*
- * @var string|null
+ * @var string[]
*/
- public ?string $status = '';
+ public array $status = [];
/**
* Parent structure.
*
@@ -130,14 +130,13 @@ protected function parse_common(object $object, array &$dependencies): void
$this->is_variable = $object->attributes->variable->content ?? false;
- $this->status = null;
if (isset($object->attributes->typeAttributes->content)) {
$data = array_map(function ($item) {
return $item->content;
}, $object->attributes->typeAttributes->content);
- $this->status = join(', ', $data);
+ $this->status = $data;
} elseif (isset($object->attributes->typeAttributes)) {
- $this->status = join(', ', $object->attributes->typeAttributes);
+ $this->status = $object->attributes->typeAttributes;
}
if (!in_array($this->type, self::DEFAULTS, true) && $this->type !== null) {
diff --git a/src/PHPDraft/Model/Elements/ObjectStructureElement.php b/src/PHPDraft/Model/Elements/ObjectStructureElement.php
index abc14ab..cb540f7 100644
--- a/src/PHPDraft/Model/Elements/ObjectStructureElement.php
+++ b/src/PHPDraft/Model/Elements/ObjectStructureElement.php
@@ -214,6 +214,7 @@ protected function construct_string_return(string $value): string
$desc = MarkdownExtra::defaultTransform($this->description);
}
- return "
{$this->key->value}{$variable} | {$type} | {$this->status} | {$desc} | {$value} |
";
+ $status_string = join(', ', $this->status);
+ return "{$this->key->value}{$variable} | {$type} | {$status_string} | {$desc} | {$value} |
";
}
}
diff --git a/src/PHPDraft/Model/Elements/Tests/ArrayStructureElementTest.php b/src/PHPDraft/Model/Elements/Tests/ArrayStructureElementTest.php
index 8ca65b5..0153b44 100644
--- a/src/PHPDraft/Model/Elements/Tests/ArrayStructureElementTest.php
+++ b/src/PHPDraft/Model/Elements/Tests/ArrayStructureElementTest.php
@@ -65,7 +65,7 @@ public static function parseObjectProvider(): array
$val2->value = 'Objective-C';
$val2->type = 'string';
$base1->value = [$val1, $val2];
- $base1->status = null;
+ $base1->status = [];
$base1->element = 'array';
$base1->type = null;
$base1->is_variable = false;
@@ -82,7 +82,7 @@ public static function parseObjectProvider(): array
$val2->value = 'another item';
$val2->type = 'string';
$base2->value = [$val1, $val2];
- $base2->status = null;
+ $base2->status = [];
$base2->element = 'array';
$base2->type = 'Some simple array';
$base2->is_variable = false;
@@ -101,7 +101,7 @@ public static function parseObjectProvider(): array
$val2->value = null;
$val2->type = 'array';
$base3->value = [$val1, $val2];
- $base3->status = 'optional';
+ $base3->status = ['optional'];
$base3->element = 'member';
$base3->type = 'array';
$base3->is_variable = false;
diff --git a/src/PHPDraft/Model/Elements/Tests/BasicStructureElementTest.php b/src/PHPDraft/Model/Elements/Tests/BasicStructureElementTest.php
index ad9b622..4fd7806 100644
--- a/src/PHPDraft/Model/Elements/Tests/BasicStructureElementTest.php
+++ b/src/PHPDraft/Model/Elements/Tests/BasicStructureElementTest.php
@@ -150,7 +150,7 @@ public static function parseValueProvider(): array
$obj2 = clone $obj;
$obj2->attributes->typeAttributes = [1, 2];
- $answer->status = '1, 2';
+ $answer->status = [1, 2];
$return[] = [$obj2, $answer];
diff --git a/src/PHPDraft/Model/Elements/Tests/EnumStructureElementTest.php b/src/PHPDraft/Model/Elements/Tests/EnumStructureElementTest.php
index dcb9680..537a4b0 100644
--- a/src/PHPDraft/Model/Elements/Tests/EnumStructureElementTest.php
+++ b/src/PHPDraft/Model/Elements/Tests/EnumStructureElementTest.php
@@ -151,7 +151,7 @@ public static function parseObjectProvider(): array
$base1 = new EnumStructureElement();
$base1->key = null;
$base1->value = [ $value1, $value2 ];
- $base1->status = null;
+ $base1->status = [];
$base1->element = 'enum';
$base1->type = 'Some simple enum';
$base1->is_variable = false;
@@ -164,7 +164,7 @@ public static function parseObjectProvider(): array
$base2->key->type = 'string';
$base2->key->value = 'car_id_list';
$base2->value = 'world';
- $base2->status = null;
+ $base2->status = [];
$base2->element = 'enum';
$base2->type = 'string';
$base2->description = null;
@@ -177,7 +177,7 @@ public static function parseObjectProvider(): array
$base3->key->type = 'number';
$base3->key->value = '5';
$base3->value = '5';
- $base3->status = 'optional';
+ $base3->status = ['optional'];
$base3->element = 'member';
$base3->type = 'number';
$base3->description = "List of car identifiers to retrieve";
diff --git a/src/PHPDraft/Model/Elements/Tests/ObjectStructureElementTest.php b/src/PHPDraft/Model/Elements/Tests/ObjectStructureElementTest.php
index 03821d8..193072c 100644
--- a/src/PHPDraft/Model/Elements/Tests/ObjectStructureElementTest.php
+++ b/src/PHPDraft/Model/Elements/Tests/ObjectStructureElementTest.php
@@ -82,7 +82,7 @@ public static function parseObjectProvider(): array
$base1->key->type = 'string';
$base1->key->value = 'name';
$base1->value = 'P10';
- $base1->status = 'optional';
+ $base1->status = ['optional'];
$base1->element = 'member';
$base1->type = 'string';
$base1->is_variable = false;
@@ -95,7 +95,7 @@ public static function parseObjectProvider(): array
$base2->key->type = 'string';
$base2->key->value = 'Auth2';
$base2->value = 'something';
- $base2->status = 'required';
+ $base2->status = ['required'];
$base2->element = 'member';
$base2->type = 'string';
$base2->is_variable = false;
diff --git a/src/PHPDraft/Model/Elements/Tests/RequestBodyElementTest.php b/src/PHPDraft/Model/Elements/Tests/RequestBodyElementTest.php
index e62e4ad..334fe1d 100644
--- a/src/PHPDraft/Model/Elements/Tests/RequestBodyElementTest.php
+++ b/src/PHPDraft/Model/Elements/Tests/RequestBodyElementTest.php
@@ -142,7 +142,7 @@ public static function parseObjectProvider(): array
$base1->key->value = 'name';
$base1->key->description = null;
$base1->value = 'P10';
- $base1->status = 'optional';
+ $base1->status = ['optional'];
$base1->element = 'member';
$base1->type = 'string';
$base1->is_variable = false;
@@ -156,7 +156,7 @@ public static function parseObjectProvider(): array
$base2->key->value = 'Auth2';
$base2->key->description = null;
$base2->value = 'something';
- $base2->status = 'required';
+ $base2->status = ['required'];
$base2->element = 'member';
$base2->type = 'string';
$base2->is_variable = false;
diff --git a/src/PHPDraft/Model/HierarchyElement.php b/src/PHPDraft/Model/HierarchyElement.php
index 0445363..a2d2790 100644
--- a/src/PHPDraft/Model/HierarchyElement.php
+++ b/src/PHPDraft/Model/HierarchyElement.php
@@ -27,9 +27,9 @@ abstract class HierarchyElement
/**
* Description of the element.
*
- * @var string
+ * @var string|null
*/
- public string $description;
+ public ?string $description = NULL;
/**
* Child elements.
diff --git a/src/PHPDraft/Model/Tests/ObjectElementTest.php b/src/PHPDraft/Model/Tests/ObjectElementTest.php
index 817a172..bea4b24 100644
--- a/src/PHPDraft/Model/Tests/ObjectElementTest.php
+++ b/src/PHPDraft/Model/Tests/ObjectElementTest.php
@@ -83,7 +83,7 @@ public function testValueSetup(): void
*/
public function testStatusSetup(): void
{
- $this->assertSame('', $this->class->status);
+ $this->assertSame([], $this->class->status);
}
/**
diff --git a/src/PHPDraft/Out/OpenAPI/OpenApiRenderer.php b/src/PHPDraft/Out/OpenAPI/OpenApiRenderer.php
index 920687c..36ac768 100644
--- a/src/PHPDraft/Out/OpenAPI/OpenApiRenderer.php
+++ b/src/PHPDraft/Out/OpenAPI/OpenApiRenderer.php
@@ -2,10 +2,14 @@
namespace PHPDraft\Out\OpenAPI;
+use PHPDraft\Model\Elements\BasicStructureElement;
+use PHPDraft\Model\Elements\ElementStructureElement;
+use PHPDraft\Model\Elements\StructureElement;
use PHPDraft\Model\HTTPRequest;
use PHPDraft\Model\HTTPResponse;
+use PHPDraft\Model\Resource;
+use PHPDraft\Model\Transition;
use PHPDraft\Out\BaseTemplateRenderer;
-use stdClass;
class OpenApiRenderer extends BaseTemplateRenderer {
@@ -18,7 +22,7 @@ public function init(object $json): self
public function write(string $filename): void
{
- $output = json_encode($this->toOpenApiObject(), JSON_PRETTY_PRINT);
+ $output = json_encode($this->toOpenApiObject(), JSON_PRETTY_PRINT | JSON_UNESCAPED_SLASHES);
file_put_contents($filename, $output);
}
@@ -92,21 +96,43 @@ private function getServers(): array {
private function getPaths(): object {
$return = [];
foreach ($this->categories as $category) {
+ /** @var Resource $resource */
foreach ($category->children ?? [] as $resource) {
+ /** @var Transition $transition */
foreach ($resource->children ?? [] as $transition) {
$transition_return = [];
- $transition_return['parameters'] = [];
+ $parameters = [];
if ($transition->url_variables !== []) {
- $transition_return['parameters'] = $this->toParameters($transition->url_variables, $transition->href);
+ $parameters += $this->toParameters($transition->url_variables, $transition->href);
}
+ if ($transition->data_variables !== NULL)
+ {
+ $parameters += $this->toParameters([$transition->data_variables], $transition->href);
+ }
+ $transition_return['parameters'] = $parameters;
- foreach ($transition->requests as $request) {
+ /** @var HTTPRequest $request */
+ foreach ($transition->requests as $request)
+ {
$request_return = [
'operationId' => $request->get_id(),
'responses' => $this->toResponses($transition->responses),
+ 'summary' => $request->title ?? $transition->title,
+ 'description' => $request->description ?? $transition->description,
];
+
+ $parameters = [];
+ if ($request->struct !== NULL) {
+ if (is_array($request->struct))
+ {
+ $parameters += $this->toParameters($request->struct, $transition->href);
+ } else {
+ $parameters += $this->toParameters([$request->struct], $transition->href);
+ }
+ }
+ $request_return['tags'] = [$category->title];
if (isset($transition_return['parameters']) && $transition_return['parameters'] !== []) {
- $request_return['parameters'] = $transition_return['parameters'];
+ $request_return['parameters'] = array_merge($transition_return['parameters'], $parameters);
}
if ($request->body !== NULL) {
$request_return['requestBody'] = $this->toBody($request);
@@ -141,14 +167,22 @@ private function toParameters(array $objects, string $href): array {
$return = [];
foreach ($objects as $variable) {
+ if ($variable->key === NULL)
+ {
+ continue;
+ }
+
$return_tmp = [
'name' => $variable->key->value,
'in' => str_contains($href, '{' . $variable->key->value . '}') ? 'path' : 'query',
'required' => $variable->status === 'required',
- 'schema' => [
- 'type' => $variable->type,
- ],
+ 'schema' => [],
];
+ if ($this->isRef($variable->type)) {
+ $return_tmp['schema']['$ref'] = '#/components/schemas/' . $variable->type;
+ } else {
+ $return_tmp['schema']['type'] = $variable->type;
+ }
if (isset($variable->value))
{
@@ -165,47 +199,9 @@ private function toParameters(array $objects, string $href): array {
return $return;
}
- /**
- * Convert a HTTP Request into an OpenAPI body
- *
- * @param HTTPRequest $request Request to convert
- *
- * @return array> OpenAPI style body
- */
- private function toBody(HTTPRequest $request): array
+ private function isRef(string $type): bool
{
- $return = [];
-
- if (!is_array($request->struct)) {
- $return['description'] = $request->struct->description;
- }
-
- $content_type = $request->headers['Content-Type'] ?? 'text/plain';
- if (isset($request->struct) && $request->struct !== [])
- {
- $return['content'] = [
- $content_type => [
- 'schema' => [
- 'type' => $request->struct->element,
- 'properties' => array_map(fn($value) => [$value->key->value => ['type' => $value->type]], $request->struct->value),
- ],
- ],
- ];
- } else {
- $return['content'] = [
- $content_type => [
- 'schema' => [
- 'type' => 'string',
- ],
- ],
- ];
- }
-
- if ($request->body !== NULL && $request->body !== []) {
- $return['content'][$content_type]['example'] = $request->body[0];
- }
-
- return $return;
+ return !in_array($type, ["array", "boolean", "integer", "null", "number", "object", "string"], TRUE);
}
/**
@@ -227,17 +223,21 @@ private function toResponses(array $responses): array
'schema' => [
'type' => 'string',
'example' => $value,
- ]
+ ],
];
}
$content = [];
foreach ($response->content as $key => $contents) {
$content[$key] = [
- "schema"=> [
- "type"=> "string",
- "example"=> $contents
- ]
+ 'schema' => [
+ 'type' => "string",
+ ],
+ 'examples' => [
+ 'base' => [
+ 'value' => $contents,
+ ],
+ ],
];
}
foreach ($response->structure as $structure) {
@@ -248,10 +248,12 @@ private function toResponses(array $responses): array
"properties"=> [
$structure->key->value => [
"type" => $structure->type,
- 'example' => $structure->value,
- ]
- ]
- ]
+ ],
+ ],
+ 'example' => [
+ $structure->key->value => $structure->value,
+ ],
+ ],
];
}
$return[$response->statuscode] = [
@@ -264,6 +266,53 @@ private function toResponses(array $responses): array
return $return;
}
+ /**
+ * Convert a HTTP Request into an OpenAPI body
+ *
+ * @param HTTPRequest $request Request to convert
+ *
+ * @return array> OpenAPI style body
+ */
+ private function toBody(HTTPRequest $request): array
+ {
+ $return = [];
+
+ if (!is_array($request->struct) && $request->struct->description !== NULL) {
+ $return['description'] = $request->struct->description;
+ }
+
+ $content_type = $request->headers['Content-Type'] ?? 'text/plain';
+ if (isset($request->struct) && $request->struct !== [])
+ {
+ $properties = [];
+ foreach ($request->struct->value as $value) {
+ $properties[$value->key->value] = ['type' => $value->type];
+ }
+ $return['content'] = [
+ $content_type => [
+ 'schema' => [
+ 'type' => $request->struct->element,
+ 'properties' => $properties,
+ ],
+ ],
+ ];
+ } else {
+ $return['content'] = [
+ $content_type => [
+ 'schema' => [
+ 'type' => 'string',
+ ],
+ ],
+ ];
+ }
+
+ if ($request->body !== NULL && $request->body !== []) {
+ $return['content'][$content_type]['examples']['base']['value'] = $request->body[0];
+ }
+
+ return $return;
+ }
+
/**
* Get webhook information for the API.
* @return object
@@ -274,7 +323,130 @@ private function getWebhooks(): object { return (object) []; }
* Get component information for the API.
* @return object
*/
- private function getComponents(): object { return (object) []; }
+ private function getComponents(): object {
+ $return = [];
+ foreach ($this->base_structures as $structure)
+ {
+ $object = $this->getComponent($structure);
+
+ if ($structure->ref !== NULL) {
+ $return[$structure->type] = [
+ 'allOf' => [
+ ['$ref' => "#/components/schemas/$structure->ref"],
+ $object,
+ ],
+ ];
+ } else {
+ $return[$structure->type] = $object;
+ }
+ }
+ return (object) ['schemas' => $return ];
+ }
+
+ /**
+ * Get a component
+ *
+ * @param BasicStructureElement $structure
+ *
+ * @return array
+ */
+ private function getComponent(BasicStructureElement $structure): array
+ {
+ $required = [];
+ $properties = [];
+ if ($structure->value !== NULL)
+ {
+ /** @var BasicStructureElement $value */
+ foreach ($structure->value as $value)
+ {
+ $propery_data = $this->getSchemaProperty($value);
+ if ($propery_data === NULL) { continue; }
+ if (in_array('required', $value->status, TRUE)) { $required[] = $value->key->value;}
+
+ $properties[$value->key->value] = $propery_data;
+ }
+ }
+
+ $object = [
+ 'type' => $structure->element,
+ ];
+ switch ($structure->element) {
+ case 'enum':
+ case 'array':
+ $object['items'] = $properties;
+ break;
+ case 'object':
+ $object['properties'] = $properties;
+ $object['required'] = $required;
+ break;
+ default:
+ break;
+ }
+
+ if ($structure->description !== NULL) {
+ $object['description'] = $structure->description;
+ }
+
+ return $object;
+ }
+
+ /**
+ * Get property in a schema
+ *
+ * @param BasicStructureElement|ElementStructureElement $value Data to convert
+ *
+ * @return array|null
+ */
+ private function getSchemaProperty(BasicStructureElement|ElementStructureElement $value): ?array
+ {
+ //TODO: Check this case
+ if ($value instanceof ElementStructureElement || $value->key === NULL)
+ {
+ return NULL;
+ }
+
+ $propery_data = [];
+ if ($value->description !== NULL) {
+ $propery_data['description'] = $value->description;
+ }
+
+ if ($this->isRef($value->type) && $value->type !== 'enum')
+ {
+ $propery_data['$ref'] = '#/components/schemas/' . $value->type;
+ return $propery_data;
+ }
+
+ if ($value->type === 'enum') {
+ $propery_data['type'] = in_array('nullable', $value->status, TRUE) ? [ $value->type, 'null' ] : $value->type;
+ $options = [];
+ foreach ($value->value->value as $option) {
+ if ($option instanceof ElementStructureElement) {
+ $options[] = ['const' => $option->value, 'title' => $option->value];
+ }
+ }
+ $propery_data['oneOf'] = $options;
+
+ return $propery_data;
+ }
+
+ if ($value->type === 'array') {
+ $propery_data['type'] = array_unique(array_map(fn($item) => $item->type,$value->value->value));
+ $propery_data['example'] = array_merge(array_filter(array_map(fn($item) => $item->value,$value->value->value)));
+
+ return $propery_data;
+ }
+
+ if ($value->type === 'object') {
+ $propery_data['type'] = $value->type;
+ $propery_data['properties'] = $this->getComponent($value->value)['properties'];
+
+ return $propery_data;
+ }
+
+ $propery_data['type'] = in_array('nullable', $value->status, TRUE) ? [ $value->type, 'null' ] : $value->type;
+
+ return $propery_data;
+ }
/**
* Get security information for the API
@@ -282,12 +454,26 @@ private function getComponents(): object { return (object) []; }
*/
private function getSecurity(): array { return []; }
+// private function getDocs(): object { return (object) []; }
+
/**
* Get tags for the API
- * @return string[]
+ * @return array>
*/
- private function getTags(): array { return []; }
+ private function getTags(): array {
+ $return = [];
+ foreach ($this->categories as $category) {
+ $data = [
+ 'name' => $category->title,
+ ];
+ if ($category->description !== NULL) {
+ $data['description'] = $category->description;
+ }
-// private function getDocs(): object { return (object) []; }
+ $return[] = $data;
+ }
+
+ return $return;
+ }
}
\ No newline at end of file
diff --git a/src/PHPDraft/Out/OpenAPI/Tests/OpenApiRendererTest.php b/src/PHPDraft/Out/OpenAPI/Tests/OpenApiRendererTest.php
index ddcaef9..774b043 100644
--- a/src/PHPDraft/Out/OpenAPI/Tests/OpenApiRendererTest.php
+++ b/src/PHPDraft/Out/OpenAPI/Tests/OpenApiRendererTest.php
@@ -56,7 +56,7 @@ public function testGetComponents(): void
$method = $this->get_reflection_method('getComponents');
$result = $method->invokeArgs($this->class, []);
- $this->assertEquals((object)[],$result);
+ $this->assertEquals((object)['schemas' => []],$result);
}
public function testGetDocs(): void
diff --git a/tests/statics/openapi/empty.json b/tests/statics/openapi/empty.json
index b31cd31..14497bf 100644
--- a/tests/statics/openapi/empty.json
+++ b/tests/statics/openapi/empty.json
@@ -17,7 +17,9 @@
],
"paths": {},
"webhooks": {},
- "components": {},
+ "components": {
+ "schemas": []
+ },
"security": [],
"tags": []
}
\ No newline at end of file