Description
API Platform version(s) affected: 3.2
Description
I am working on a PR to introduce a JSON:API schema factory and hit a couple of related bugs that feel better suited to a separate issue/PR combination.
I have working "fixes" laid out below, but would like them reviewed & sanity checked before I create a PR for this.
First problem was that ApiPlatform\Hydra\JsonSchema\SchemaFactory#addDistinctFormat()
checks for ApiPlatform\JsonSchema\SchemaFactory
, where as ApiPlatform\Hal\JsonSchema\SchemaFactory
simply does a method_exists
call.
No priority
is set on any of the service decorators, and Hydra's, by chance, is currently the one with the base service .inner
. However, my new service took first place and it became impossible to build both schema formats at the same time as the last one "won out". Changing Hydra's method behaviour to be a method_exists
call fixes this.
The second is just as subtle. The base ApiPlatform\JsonSchema\SchemaFactory
calls the injected decorator service to enable subschema generation (see #6055). When the Hydra service is the one decorating the base this works fine, but the introduction of another service causes that one to be called instead, meaning Hydra misses out (see #5950)
How to reproduce
final class SchemaFactory implements SchemaFactoryInterface
{
public function __construct(private readonly SchemaFactoryInterface $schemaFactory)
{
$this->addDistinctFormat('jsonapi');
if ($this->schemaFactory instanceof SchemaFactoryAwareInterface) {
$this->schemaFactory->setSchemaFactory($this);
}
}
public function buildSchema(string $className, string $format = 'jsonapi', string $type = Schema::TYPE_OUTPUT, ?Operation $operation = null, ?Schema $schema = null, ?array $serializerContext = null, bool $forceCollection = false): Schema
{
$schema = $this->schemaFactory->buildSchema($className, $format, $type, $operation, $schema, $serializerContext, $forceCollection);
if ('jsonapi' !== $format) {
return $schema;
}
// Do schema updates here …
return $schema;
}
public function addDistinctFormat(string $format): void
{
// The Hydra schema factory does:
// if ($this->schemaFactory instanceof SchemaFactoryAwareInterface) {
//
// The HAL one does as below:
if (method_exists($this->schemaFactory, 'addDistinctFormat')) {
$this->schemaFactory->addDistinctFormat($format);
}
}
}
Obligatory service definition 😇
api_platform.jsonapi.json_schema.schema_factory:
class: 'ApiPlatform\JsonApi\JsonSchema\SchemaFactory'
decorates: 'api_platform.json_schema.schema_factory'
arguments:
- '@api_platform.jsonapi.json_schema.schema_factory.inner'
And API Platform config:
api_platform:
formats:
json: [ 'application/json' ]
jsonld: [ 'application/ld+json' ]
jsonhal: [ 'application/hal+json' ]
jsonapi: [ 'application/vnd.api+json' ]
Possible Solution
- Change Hydra
SchemaFactory#addDistinctFormat()
to use amethod_exists
call - Have all
SchemaFactory
s implementSchemaFactoryAwareInterface
😞
Obviously neither of those are particularly elegant, hence this discussion. Help and guidance most welcome!