diff --git a/src/ReferenceResolver/LocalReferenceResolver.php b/src/ReferenceResolver/LocalReferenceResolver.php index 9fbe311..e4e4666 100644 --- a/src/ReferenceResolver/LocalReferenceResolver.php +++ b/src/ReferenceResolver/LocalReferenceResolver.php @@ -40,7 +40,7 @@ public function resolve(Reference $reference): Document foreach ($tryKeys as $tryKey) { if ($this->rootDocument->has($tryKey)) { - /** @var array{"$id"?: string, id?: string, ...}|string $resolvedDocument */ + /** @var array{"$id"?: string, id?: string, ...}|string $resolvedDocument */ $resolvedDocument = $this->rootDocument->get($tryKey); if (!is_array($resolvedDocument)) { throw DocumentParseError::invalidContent($tryKey); diff --git a/src/Service/InlineService.php b/src/Service/InlineService.php index 95adcb3..5d8acbf 100644 --- a/src/Service/InlineService.php +++ b/src/Service/InlineService.php @@ -58,7 +58,7 @@ public function inline(string $schemaFile): Document $this->components = []; $this->allOfPaths = []; - $document = $this->processDocument($schemaDocument); + $document = $this->processDocument($schemaDocument, $schemaDocument); $allOfPaths = array_unique($this->allOfPaths); if ($allOfPaths) { $merger = new AllOfMerger($document); @@ -98,6 +98,7 @@ private function getRefName(Reference $reference, SchemaType $type): string private function processDocument( Document&WritableDocument $document, + Document $fileRoot, ?Reference $rootReference = null, ?string $parentPath = null, ): Document&WritableDocument { @@ -107,7 +108,7 @@ private function processDocument( $type = $this->resolveSchemaType($documentPaths); $refName = $this->getRefName($reference, $type); - $nextParentPath = $parentPath; + $nextParentPath = ($parentPath ?? '') . $documentPaths[0]; if ( isset($this->components[$type->name][$refName]) && $this->components[$type->name][$refName] @@ -134,7 +135,7 @@ private function processDocument( } if ($reference->isInternal()) { - $subDocument = $document->get($reference->getPath()); + $subDocument = $fileRoot->get($reference->getPath()); if (!is_array($subDocument)) { throw new \BadMethodCallException('Failed to resolve internal model properly'); } @@ -142,16 +143,18 @@ private function processDocument( $refName, $subDocument, // @phpstan-ignore argument.type ); + $nextFileRoot = $fileRoot; } else { $referenceDocument = BasicDocument::fromDocument($this->referenceResolver->resolve($reference)); - $nextParentPath .= $documentPaths[0]; + $nextFileRoot = $referenceDocument; } $schemaId = $referenceDocument->get()['$id'] ?? $refName; // reserve schema to avoid infinite recursion $this->components[$type->name][$refName] = $schemaId; $this->components[$type->name][$refName] = $this->processDocument( $referenceDocument, + $nextFileRoot, $reference, $nextParentPath, )->get(); @@ -198,7 +201,7 @@ private function processDocumentPaths( elseif ($processAllOf && str_contains($path, '/allOf/')) { $stripped = substr($path, 0, (int)strpos($path, '/allOf/')); $this->modelAllOfPaths[$parentName][] = $stripped; - $this->allOfPaths[] = $parentPath . $stripped; + $this->allOfPaths[] = ($parentPath ?? '') . $stripped; } $document->set($path, $schema); } diff --git a/tests/inlined-schemas/inline-allOf-internal-nested/schema.dist.json b/tests/inlined-schemas/inline-allOf-internal-nested/schema.dist.json new file mode 100644 index 0000000..51e19c3 --- /dev/null +++ b/tests/inlined-schemas/inline-allOf-internal-nested/schema.dist.json @@ -0,0 +1,98 @@ +{ + "openapi": "3.0.0", + "paths": { + "/test": { + "get": { + "responses": { + "200": { + "description": "OK", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "sub": { + "properties": { + "subIntermediate": { + "allOf": [ + { + "type": "object", + "properties": { + "id": { + "type": "string" + } + }, + "$id": "Base" + }, + { + "type": "object", + "properties": { + "intermediate": { + "type": "boolean" + } + } + } + ] + } + }, + "$id": "Intermediate" + } + }, + "$id": "NestedAllOf" + } + } + } + } + } + } + } + }, + "components": { + "schemas": { + "Base": { + "type": "object", + "properties": { + "id": { + "type": "string" + } + } + }, + "Intermediate": { + "properties": { + "subIntermediate": { + "type": "object", + "properties": { + "id": { + "type": "string" + }, + "intermediate": { + "type": "boolean" + } + } + } + } + }, + "NestedAllOf": { + "type": "object", + "properties": { + "sub": { + "properties": { + "subIntermediate": { + "type": "object", + "properties": { + "id": { + "type": "string" + }, + "intermediate": { + "type": "boolean" + } + } + } + }, + "$id": "Intermediate" + } + } + } + } + } +} \ No newline at end of file diff --git a/tests/inlined-schemas/inline-allOf-internal-nested/schema.json b/tests/inlined-schemas/inline-allOf-internal-nested/schema.json new file mode 100644 index 0000000..8799732 --- /dev/null +++ b/tests/inlined-schemas/inline-allOf-internal-nested/schema.json @@ -0,0 +1,54 @@ +{ + "openapi": "3.0.0", + "paths": { + "/test": { + "get": { + "responses": { + "200": { + "description": "OK", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/NestedAllOf" + } + } + } + } + } + } + } + }, + "components": { + "schemas": { + "Base": { + "type": "object", + "properties": { + "id": { "type": "string" } + } + }, + "Intermediate": { + "properties": { + "subIntermediate": { + "allOf": [ + { "$ref": "#/components/schemas/Base" }, + { + "type": "object", + "properties": { + "intermediate": { "type": "boolean" } + } + } + ] + } + } + }, + "NestedAllOf": { + "type": "object", + "properties": { + "sub": { + "$ref": "#/components/schemas/Intermediate" + } + } + } + } + } +} diff --git a/tests/inlined-schemas/inline-nested-internal-refs/schema.dist.json b/tests/inlined-schemas/inline-nested-internal-refs/schema.dist.json new file mode 100644 index 0000000..55b3510 --- /dev/null +++ b/tests/inlined-schemas/inline-nested-internal-refs/schema.dist.json @@ -0,0 +1,155 @@ +{ + "openapi": "3.0.0", + "info": { + "title": "parking api", + "version": "1.0", + "contact": { + "name": "Stefna ehf", + "url": "https://stefna.is", + "email": "no-support@stefna.is" + } + }, + "paths": { + "/customer": { + "post": { + "summary": "Create new customer", + "operationId": "createCustomer", + "responses": { + "201": { + "description": "OK" + } + }, + "requestBody": { + "description": "Create customer payload", + "required": true, + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "customer": { + "title": "Customer", + "type": "object", + "required": [ + "id" + ], + "properties": { + "id": { + "type": "string", + "format": "uuid" + }, + "name": { + "type": "string", + "nullable": true, + "example": "Albert Albertsson" + }, + "options": { + "type": "object", + "properties": { + "billingSsn": { + "type": "string" + } + }, + "$id": "CustomerOption" + } + }, + "$id": "Customer" + } + }, + "required": [ + "customer" + ] + } + } + }, + "$id": "CreateCustomerPayload" + } + } + } + }, + "components": { + "schemas": { + "Customer": { + "title": "Customer", + "type": "object", + "required": [ + "id" + ], + "properties": { + "id": { + "type": "string", + "format": "uuid" + }, + "name": { + "type": "string", + "nullable": true, + "example": "Albert Albertsson" + }, + "options": { + "type": "object", + "properties": { + "billingSsn": { + "type": "string" + } + }, + "$id": "CustomerOption" + } + } + }, + "CustomerOption": { + "type": "object", + "properties": { + "billingSsn": { + "type": "string" + } + } + } + }, + "requestBodies": { + "CreateCustomerPayload": { + "description": "Create customer payload", + "required": true, + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "customer": { + "title": "Customer", + "type": "object", + "required": [ + "id" + ], + "properties": { + "id": { + "type": "string", + "format": "uuid" + }, + "name": { + "type": "string", + "nullable": true, + "example": "Albert Albertsson" + }, + "options": { + "type": "object", + "properties": { + "billingSsn": { + "type": "string" + } + }, + "$id": "CustomerOption" + } + }, + "$id": "Customer" + } + }, + "required": [ + "customer" + ] + } + } + } + } + } + } +} diff --git a/tests/inlined-schemas/inline-nested-internal-refs/schema.json b/tests/inlined-schemas/inline-nested-internal-refs/schema.json new file mode 100644 index 0000000..83a6684 --- /dev/null +++ b/tests/inlined-schemas/inline-nested-internal-refs/schema.json @@ -0,0 +1,82 @@ +{ + "openapi": "3.0.0", + "info": { + "title": "parking api", + "version": "1.0", + "contact": { + "name": "Stefna ehf", + "url": "https://stefna.is", + "email": "no-support@stefna.is" + } + }, + "paths": { + "/customer": { + "post": { + "summary": "Create new customer", + "operationId": "createCustomer", + "responses": { + "201": { + "description": "OK" + } + }, + "requestBody": { + "$ref": "#/components/requestBodies/CreateCustomerPayload" + } + } + } + }, + "components": { + "schemas": { + "Customer": { + "title": "Customer", + "type": "object", + "required": [ + "id" + ], + "properties": { + "id": { + "type": "string", + "format": "uuid" + }, + "name": { + "type": "string", + "nullable": true, + "example": "Albert Albertsson" + }, + "options": { + "$ref": "#/components/schemas/CustomerOption" + } + } + }, + "CustomerOption": { + "type": "object", + "properties": { + "billingSsn": { + "type": "string" + } + } + } + }, + "requestBodies": { + "CreateCustomerPayload": { + "description": "Create customer payload", + "required": true, + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "customer": { + "$ref": "#/components/schemas/Customer" + } + }, + "required": [ + "customer" + ] + } + } + } + } + } + } +}