diff --git a/src/Hydra/Serializer/DocumentationNormalizer.php b/src/Hydra/Serializer/DocumentationNormalizer.php
index 2903eb5557..08049825f7 100644
--- a/src/Hydra/Serializer/DocumentationNormalizer.php
+++ b/src/Hydra/Serializer/DocumentationNormalizer.php
@@ -54,6 +54,7 @@ public function __construct(
private readonly UrlGeneratorInterface $urlGenerator,
private readonly ?NameConverterInterface $nameConverter = null,
private readonly ?array $defaultContext = [],
+ private readonly ?bool $entrypointEnabled = true,
) {
}
@@ -443,19 +444,21 @@ private function isSingleRelation(ApiProperty $propertyMetadata): bool
*/
private function getClasses(array $entrypointProperties, array $classes, string $hydraPrefix = ContextBuilder::HYDRA_PREFIX): array
{
- $classes[] = [
- '@id' => '#Entrypoint',
- '@type' => $hydraPrefix.'Class',
- $hydraPrefix.'title' => 'Entrypoint',
- $hydraPrefix.'supportedProperty' => $entrypointProperties,
- $hydraPrefix.'supportedOperation' => [
- '@type' => $hydraPrefix.'Operation',
- $hydraPrefix.'method' => 'GET',
- $hydraPrefix.'title' => 'index',
- $hydraPrefix.'description' => 'The API Entrypoint.',
- $hydraPrefix.'returns' => 'Entrypoint',
- ],
- ];
+ if ($this->entrypointEnabled) {
+ $classes[] = [
+ '@id' => '#Entrypoint',
+ '@type' => $hydraPrefix.'Class',
+ $hydraPrefix.'title' => 'Entrypoint',
+ $hydraPrefix.'supportedProperty' => $entrypointProperties,
+ $hydraPrefix.'supportedOperation' => [
+ '@type' => $hydraPrefix.'Operation',
+ $hydraPrefix.'method' => 'GET',
+ $hydraPrefix.'title' => 'index',
+ $hydraPrefix.'description' => 'The API Entrypoint.',
+ $hydraPrefix.'returns' => 'Entrypoint',
+ ],
+ ];
+ }
$classes[] = [
'@id' => '#ConstraintViolationList',
@@ -560,7 +563,9 @@ private function computeDoc(Documentation $object, array $classes, string $hydra
$doc[$hydraPrefix.'description'] = $object->getDescription();
}
- $doc[$hydraPrefix.'entrypoint'] = $this->urlGenerator->generate('api_entrypoint');
+ if ($this->entrypointEnabled) {
+ $doc[$hydraPrefix.'entrypoint'] = $this->urlGenerator->generate('api_entrypoint');
+ }
$doc[$hydraPrefix.'supportedClass'] = $classes;
return $doc;
diff --git a/src/Hydra/Tests/Serializer/DocumentationNormalizerTest.php b/src/Hydra/Tests/Serializer/DocumentationNormalizerTest.php
index 142acf2d32..56bff521b2 100644
--- a/src/Hydra/Tests/Serializer/DocumentationNormalizerTest.php
+++ b/src/Hydra/Tests/Serializer/DocumentationNormalizerTest.php
@@ -824,4 +824,106 @@ public function testNormalizeWithoutPrefix(): void
$this->assertEquals($expected, $documentationNormalizer->normalize($documentation, null, [ContextBuilder::HYDRA_CONTEXT_HAS_PREFIX => false]));
}
+
+ public function testNormalizeNoEntrypointAndHideHydraOperation(): void
+ {
+ $title = 'Test Api';
+ $desc = 'test ApiGerard';
+ $version = '0.0.0';
+ $documentation = new Documentation(new ResourceNameCollection(['dummy' => 'dummy']), $title, $desc, $version);
+
+ $resourceMetadataFactoryProphecy = $this->prophesize(ResourceMetadataCollectionFactoryInterface::class);
+ $resourceMetadataFactoryProphecy->create('dummy')->willReturn(new ResourceMetadataCollection('dummy', [
+ (new ApiResource())->withHideHydraOperation(true)->withOperations(new Operations([
+ 'get' => new Get(),
+ ])),
+ ]));
+ $propertyNameCollectionFactoryProphecy = $this->prophesize(PropertyNameCollectionFactoryInterface::class);
+ $propertyMetadataFactoryProphecy = $this->prophesize(PropertyMetadataFactoryInterface::class);
+ $resourceClassResolverProphecy = $this->prophesize(ResourceClassResolverInterface::class);
+ $urlGenerator = $this->prophesize(UrlGeneratorInterface::class);
+ $urlGenerator->generate('api_doc', ['_format' => 'jsonld'], Argument::any())->willReturn('/doc');
+
+ $documentationNormalizer = new DocumentationNormalizer(
+ $resourceMetadataFactoryProphecy->reveal(),
+ $propertyNameCollectionFactoryProphecy->reveal(),
+ $propertyMetadataFactoryProphecy->reveal(),
+ $resourceClassResolverProphecy->reveal(),
+ $urlGenerator->reveal(),
+ new CustomConverter(),
+ [],
+ false,
+ );
+
+ $expected = [
+ '@context' => [
+ HYDRA_CONTEXT,
+ [
+ '@vocab' => '/doc#',
+ 'hydra' => 'http://www.w3.org/ns/hydra/core#',
+ 'rdf' => 'http://www.w3.org/1999/02/22-rdf-syntax-ns#',
+ 'rdfs' => 'http://www.w3.org/2000/01/rdf-schema#',
+ 'xmls' => 'http://www.w3.org/2001/XMLSchema#',
+ 'owl' => 'http://www.w3.org/2002/07/owl#',
+ 'schema' => 'https://schema.org/',
+ 'domain' => [
+ '@id' => 'rdfs:domain',
+ '@type' => '@id',
+ ],
+ 'range' => [
+ '@id' => 'rdfs:range',
+ '@type' => '@id',
+ ],
+ 'subClassOf' => [
+ '@id' => 'rdfs:subClassOf',
+ '@type' => '@id',
+ ],
+ ],
+ ],
+ '@id' => '/doc',
+ '@type' => 'ApiDocumentation',
+ 'title' => 'Test Api',
+ 'description' => 'test ApiGerard',
+ 'supportedClass' => [
+ [
+ '@id' => '#ConstraintViolationList',
+ '@type' => 'Class',
+ 'title' => 'ConstraintViolationList',
+ 'description' => 'A constraint violation List.',
+ 'supportedProperty' => [
+ [
+ '@type' => 'SupportedProperty',
+ 'property' => [
+ '@id' => '#ConstraintViolationList/propertyPath',
+ '@type' => 'rdf:Property',
+ 'rdfs:label' => 'propertyPath',
+ 'domain' => '#ConstraintViolationList',
+ 'range' => 'xmls:string',
+ ],
+ 'title' => 'propertyPath',
+ 'description' => 'The property path of the violation',
+ 'readable' => true,
+ 'writeable' => false,
+ ],
+ [
+ '@type' => 'SupportedProperty',
+ 'property' => [
+ '@id' => '#ConstraintViolationList/message',
+ '@type' => 'rdf:Property',
+ 'rdfs:label' => 'message',
+ 'domain' => '#ConstraintViolationList',
+ 'range' => 'xmls:string',
+ ],
+ 'title' => 'message',
+ 'description' => 'The message associated with the violation',
+ 'readable' => true,
+ 'writeable' => false,
+ ],
+ ],
+ ],
+ ],
+ ];
+
+ $this->assertEquals($expected, $documentationNormalizer->normalize($documentation, null, [ContextBuilder::HYDRA_CONTEXT_HAS_PREFIX => false]));
+ }
}
diff --git a/src/Laravel/ApiPlatformProvider.php b/src/Laravel/ApiPlatformProvider.php
index f9a42c9047..475e51242b 100644
--- a/src/Laravel/ApiPlatformProvider.php
+++ b/src/Laravel/ApiPlatformProvider.php
@@ -731,6 +731,7 @@ public function register(): void
$this->app->singleton(HydraDocumentationNormalizer::class, function (Application $app) {
$config = $app['config'];
$defaultContext = $config->get('api-platform.serializer', []);
+ $entrypointEnabled = $config->get('api-platform.enable_entrypoint', true);
return new HydraDocumentationNormalizer(
$app->make(ResourceMetadataCollectionFactoryInterface::class),
@@ -739,7 +740,8 @@ public function register(): void
$app->make(ResourceClassResolverInterface::class),
$app->make(UrlGeneratorInterface::class),
$app->make(NameConverterInterface::class),
- $defaultContext
+ $defaultContext,
+ $entrypointEnabled
);
});
diff --git a/src/Symfony/Bundle/DependencyInjection/ApiPlatformExtension.php b/src/Symfony/Bundle/DependencyInjection/ApiPlatformExtension.php
index 4e273866bd..4bcf3e5a5e 100644
--- a/src/Symfony/Bundle/DependencyInjection/ApiPlatformExtension.php
+++ b/src/Symfony/Bundle/DependencyInjection/ApiPlatformExtension.php
@@ -112,6 +112,14 @@ public function load(array $configs, ContainerBuilder $container): void
$patchFormats = $this->getFormats($config['patch_formats']);
$errorFormats = $this->getFormats($config['error_formats']);
$docsFormats = $this->getFormats($config['docs_formats']);
+ if (!$config['enable_docs']) {
+ // JSON-LD documentation format is mandatory, even if documentation is disabled.
+ $docsFormats = isset($formats['jsonld']) ? ['jsonld' => ['application/ld+json']] : [];
+ // If documentation is disabled, the Hydra documentation for all the resources is hidden by default.
+ if (!isset($config['defaults']['hideHydraOperation']) && !isset($config['defaults']['hide_hydra_operation'])) {
+ $config['defaults']['hideHydraOperation'] = true;
+ }
+ }
$jsonSchemaFormats = $config['jsonschema_formats'];
if (!$jsonSchemaFormats) {
@@ -538,11 +546,6 @@ private function registerJsonLdHydraConfiguration(ContainerBuilder $container, a
if (!$container->has('api_platform.json_schema.schema_factory')) {
$container->removeDefinition('api_platform.hydra.json_schema.schema_factory');
}
-
- if (!$config['enable_docs']) {
- $container->removeDefinition('api_platform.hydra.listener.response.add_link_header');
- $container->removeDefinition('api_platform.hydra.processor.link');
- }
}
private function registerJsonHalConfiguration(array $formats, XmlFileLoader $loader): void
diff --git a/src/Symfony/Bundle/Resources/config/hydra.xml b/src/Symfony/Bundle/Resources/config/hydra.xml
index 021b885d3b..6fb156141a 100644
--- a/src/Symfony/Bundle/Resources/config/hydra.xml
+++ b/src/Symfony/Bundle/Resources/config/hydra.xml
@@ -17,6 +17,7 @@
%api_platform.serializer.default_context%
+ %api_platform.enable_entrypoint%
diff --git a/src/Symfony/Routing/ApiLoader.php b/src/Symfony/Routing/ApiLoader.php
index f71206cbe5..6da29ed775 100644
--- a/src/Symfony/Routing/ApiLoader.php
+++ b/src/Symfony/Routing/ApiLoader.php
@@ -36,7 +36,7 @@ final class ApiLoader extends Loader
private readonly XmlFileLoader $fileLoader;
- public function __construct(KernelInterface $kernel, private readonly ResourceNameCollectionFactoryInterface $resourceNameCollectionFactory, private readonly ResourceMetadataCollectionFactoryInterface $resourceMetadataFactory, private readonly ContainerInterface $container, private readonly array $formats, private readonly array $resourceClassDirectories = [], private readonly bool $graphqlEnabled = false, private readonly bool $entrypointEnabled = true, private readonly bool $docsEnabled = true, private readonly bool $graphiQlEnabled = false, private readonly bool $graphQlPlaygroundEnabled = false)
+ public function __construct(KernelInterface $kernel, private readonly ResourceNameCollectionFactoryInterface $resourceNameCollectionFactory, private readonly ResourceMetadataCollectionFactoryInterface $resourceMetadataFactory, private readonly ContainerInterface $container, private readonly array $formats, private readonly array $resourceClassDirectories = [], private readonly bool $graphqlEnabled = false, private readonly bool $entrypointEnabled = true, readonly bool $docsEnabled = true, private readonly bool $graphiQlEnabled = false, private readonly bool $graphQlPlaygroundEnabled = false)
{
/** @var string[]|string $paths */
$paths = $kernel->locateResource('@ApiPlatformBundle/Resources/config/routing');
@@ -124,6 +124,7 @@ public function supports(mixed $resource, ?string $type = null): bool
*/
private function loadExternalFiles(RouteCollection $routeCollection): void
{
+ $routeCollection->addCollection($this->fileLoader->load('docs.xml'));
$routeCollection->addCollection($this->fileLoader->load('genid.xml'));
$routeCollection->addCollection($this->fileLoader->load('errors.xml'));
@@ -131,10 +132,6 @@ private function loadExternalFiles(RouteCollection $routeCollection): void
$routeCollection->addCollection($this->fileLoader->load('api.xml'));
}
- if ($this->docsEnabled) {
- $routeCollection->addCollection($this->fileLoader->load('docs.xml'));
- }
-
if ($this->graphqlEnabled) {
$graphqlCollection = $this->fileLoader->load('graphql/graphql.xml');
$graphqlCollection->addDefaults(['_graphql' => true]);