diff --git a/src/Metadata/Resource/Factory/XmlResourceMetadataFactory.php b/src/Metadata/Resource/Factory/XmlResourceMetadataFactory.php index 168030015c7..f779f3a390c 100644 --- a/src/Metadata/Resource/Factory/XmlResourceMetadataFactory.php +++ b/src/Metadata/Resource/Factory/XmlResourceMetadataFactory.php @@ -61,22 +61,18 @@ public function create(string $resourceClass) : ResourceMetadata $metadata = null; foreach ($this->paths as $path) { - $this->xmlParser->loadXML(file_get_contents($path)); + $resources = $this->getResourcesDom($path); $internalErrors = libxml_use_internal_errors(true); - if (false === @$this->xmlParser->schemaValidate(self::RESOURCE_SCHEMA)) { + if (false === @$resources->schemaValidate(self::RESOURCE_SCHEMA)) { throw new \InvalidArgumentException(sprintf('XML Schema loaded from path %s is not valid! Errors: %s', realpath($path), implode("\n", $this->getXmlErrors($internalErrors)))); } libxml_clear_errors(); libxml_use_internal_errors($internalErrors); - $xpath = new \DOMXpath($this->xmlParser); - - $resources = $xpath->query('/resources/resource'); - - foreach ($resources as $resource) { + foreach ($resources->getElementsByTagName('resource') as $resource) { $class = $resource->getAttribute('class'); if ($resourceClass !== $class) { @@ -93,6 +89,8 @@ public function create(string $resourceClass) : ResourceMetadata return $this->handleNotFound($parentResourceMetadata, $resourceClass); } + $xpath = new \DOMXpath($resources); + $metadata = [ 'shortName' => $metadata->getAttribute('shortName') ?: null, 'description' => $metadata->getAttribute('description') ?: null, @@ -126,6 +124,31 @@ public function create(string $resourceClass) : ResourceMetadata return $resourceMetadata; } + /** + * Creates a DOMDocument based on `resource` tags of a file-loaded xml document. + * + * @param string $path the xml file path + * + * @return \DOMDocument + */ + private function getResourcesDom(string $path) : \DOMDocument + { + $doc = new \DOMDocument('1.0', 'utf-8'); + $root = $doc->createElement('resources'); + $doc->appendChild($root); + + $this->xmlParser->loadXML(file_get_contents($path)); + + $xpath = new \DOMXpath($this->xmlParser); + $resources = $xpath->query('//resource'); + + foreach ($resources as $resource) { + $root->appendChild($doc->importNode($resource, true)); + } + + return $doc; + } + /** * Get operations from xml. * diff --git a/src/Metadata/Resource/Factory/XmlResourceNameCollectionFactory.php b/src/Metadata/Resource/Factory/XmlResourceNameCollectionFactory.php index d22e4f90dba..096ad7c57df 100644 --- a/src/Metadata/Resource/Factory/XmlResourceNameCollectionFactory.php +++ b/src/Metadata/Resource/Factory/XmlResourceNameCollectionFactory.php @@ -51,22 +51,18 @@ public function create() : ResourceNameCollection } foreach ($this->paths as $path) { - $this->xmlParser->loadXML(file_get_contents($path)); + $resources = $this->getResourcesDom($path); $internalErrors = libxml_use_internal_errors(true); - if (false === @$this->xmlParser->schemaValidate(self::RESOURCE_SCHEMA)) { + if (false === @$resources->schemaValidate(self::RESOURCE_SCHEMA)) { throw new \InvalidArgumentException(sprintf('XML Schema loaded from path %s is not valid! Errors: %s', realpath($path), implode("\n", $this->getXmlErrors($internalErrors)))); } libxml_clear_errors(); libxml_use_internal_errors($internalErrors); - $xpath = new \DOMXpath($this->xmlParser); - - $resources = $xpath->query('/resources/resource'); - - foreach ($resources as $resource) { + foreach ($resources->getElementsByTagName('resource') as $resource) { $classes[$resource->getAttribute('class')] = true; } } @@ -74,6 +70,31 @@ public function create() : ResourceNameCollection return new ResourceNameCollection(array_keys($classes)); } + /** + * Creates a DOMDocument based on `resource` tags of a file-loaded xml document. + * + * @param string $path the xml file path + * + * @return \DOMDocument + */ + private function getResourcesDom(string $path) : \DOMDocument + { + $doc = new \DOMDocument('1.0', 'utf-8'); + $root = $doc->createElement('resources'); + $doc->appendChild($root); + + $this->xmlParser->loadXML(file_get_contents($path)); + + $xpath = new \DOMXpath($this->xmlParser); + $resources = $xpath->query('//resource'); + + foreach ($resources as $resource) { + $root->appendChild($doc->importNode($resource, true)); + } + + return $doc; + } + /** * Returns the XML errors of the internal XML parser. * diff --git a/src/Metadata/Resource/Factory/YamlResourceMetadataFactory.php b/src/Metadata/Resource/Factory/YamlResourceMetadataFactory.php index 5578a3c62c0..b52c968de07 100644 --- a/src/Metadata/Resource/Factory/YamlResourceMetadataFactory.php +++ b/src/Metadata/Resource/Factory/YamlResourceMetadataFactory.php @@ -60,7 +60,9 @@ public function create(string $resourceClass) : ResourceMetadata $metadata = null; foreach ($this->paths as $path) { - $resources = $this->yamlParser->parse(file_get_contents($path))['resources']; + $resources = $this->yamlParser->parse(file_get_contents($path)); + + $resources = $resources['resources'] ?? $resources; foreach ($resources as $resource) { if (!isset($resource['class'])) { diff --git a/src/Metadata/Resource/Factory/YamlResourceNameCollectionFactory.php b/src/Metadata/Resource/Factory/YamlResourceNameCollectionFactory.php index 4d64112a6bb..958590721a2 100644 --- a/src/Metadata/Resource/Factory/YamlResourceNameCollectionFactory.php +++ b/src/Metadata/Resource/Factory/YamlResourceNameCollectionFactory.php @@ -50,7 +50,9 @@ public function create() : ResourceNameCollection } foreach ($this->paths as $path) { - $resources = $this->yamlParser->parse(file_get_contents($path))['resources']; + $resources = $this->yamlParser->parse(file_get_contents($path)); + + $resources = $resources['resources'] ?? $resources; foreach ($resources as $resource) { if (!isset($resource['class'])) { diff --git a/tests/Fixtures/FileConfigurations/single_resource.xml b/tests/Fixtures/FileConfigurations/single_resource.xml new file mode 100644 index 00000000000..7c098728f8f --- /dev/null +++ b/tests/Fixtures/FileConfigurations/single_resource.xml @@ -0,0 +1,26 @@ + + + + + + + + + + + + + default + + + + + default + + + + hydra:Operation + File config Dummy + + + diff --git a/tests/Fixtures/FileConfigurations/single_resource.yml b/tests/Fixtures/FileConfigurations/single_resource.yml new file mode 100644 index 00000000000..cfeb78e9910 --- /dev/null +++ b/tests/Fixtures/FileConfigurations/single_resource.yml @@ -0,0 +1,22 @@ +configdummy: + shortName: 'thedummyshortname' + description: 'Dummy resource' + class: 'ApiPlatform\Core\Tests\Fixtures\TestBundle\Entity\FileConfigDummy' + itemOperations: + my_op_name: + method: 'GET' + my_other_op_name: + method: 'POST' + collectionOperations: + my_collection_op: + method: 'POST' + path: 'the/collection/path' + attributes: + normalization_context: + groups: ['default'] + denormalization_context: + groups: ['default'] + hydra_context: + '@type': 'hydra:Operation' + '@hydra:title': 'File config Dummy' + iri: 'someirischema' diff --git a/tests/Fixtures/TestBundle/Resources/config/api_resources/my_resource.yml b/tests/Fixtures/TestBundle/Resources/config/api_resources/my_resource.yml index 7b773d7faa7..555ed937374 100644 --- a/tests/Fixtures/TestBundle/Resources/config/api_resources/my_resource.yml +++ b/tests/Fixtures/TestBundle/Resources/config/api_resources/my_resource.yml @@ -1,5 +1,4 @@ -resources: - single_file_config: - shortName: 'single_file_config' - description: 'File configured resource' - class: 'ApiPlatform\Core\Tests\Fixtures\TestBundle\Entity\SingleFileConfigDummy' +single_file_config: + shortName: 'single_file_config' + description: 'File configured resource' + class: 'ApiPlatform\Core\Tests\Fixtures\TestBundle\Entity\SingleFileConfigDummy' diff --git a/tests/Metadata/Resource/Factory/FileConfigurationMetadataTest.php b/tests/Metadata/Resource/Factory/FileConfigurationMetadataTest.php index a7ab2a1ef24..fc82e55d06a 100644 --- a/tests/Metadata/Resource/Factory/FileConfigurationMetadataTest.php +++ b/tests/Metadata/Resource/Factory/FileConfigurationMetadataTest.php @@ -192,4 +192,52 @@ public function testYamlOptionalResourceMetadata($expectedResourceMetadata) $this->assertInstanceOf(ResourceMetadata::class, $resourceMetadata); $this->assertEquals($expectedResourceMetadata, $resourceMetadata); } + + public function testXmlSingleResourceName() + { + $configPath = __DIR__.'/../../../Fixtures/FileConfigurations/single_resource.xml'; + $xmlResourceNameCollectionFactory = new XmlResourceNameCollectionFactory([$configPath]); + + $this->assertEquals($xmlResourceNameCollectionFactory->create(), new ResourceNameCollection([ + FileConfigDummy::class, + ])); + } + + /** + * @dataProvider resourceMetadataProvider + */ + public function testXmlSingleResourceMetadata($expectedResourceMetadata) + { + $configPath = __DIR__.'/../../../Fixtures/FileConfigurations/single_resource.xml'; + + $resourceMetadataFactory = new XmlResourceMetadataFactory([$configPath]); + $resourceMetadata = $resourceMetadataFactory->create(FileConfigDummy::class); + + $this->assertInstanceOf(ResourceMetadata::class, $resourceMetadata); + $this->assertEquals($expectedResourceMetadata, $resourceMetadata); + } + + public function testYamlSingleResourceName() + { + $configPath = __DIR__.'/../../../Fixtures/FileConfigurations/single_resource.yml'; + $yamlResourceNameCollectionFactory = new YamlResourceNameCollectionFactory([$configPath]); + + $this->assertEquals($yamlResourceNameCollectionFactory->create(), new ResourceNameCollection([ + FileConfigDummy::class, + ])); + } + + /** + * @dataProvider resourceMetadataProvider + */ + public function testYamlSingleResourceMetadata(ResourceMetadata $expectedResourceMetadata) + { + $configPath = __DIR__.'/../../../Fixtures/FileConfigurations/single_resource.yml'; + + $resourceMetadataFactory = new YamlResourceMetadataFactory([$configPath]); + $resourceMetadata = $resourceMetadataFactory->create(FileConfigDummy::class); + + $this->assertInstanceOf(ResourceMetadata::class, $resourceMetadata); + $this->assertEquals($expectedResourceMetadata, $resourceMetadata); + } }