diff --git a/jsonapi.services.yml b/jsonapi.services.yml index 6a0e7a2..318fd8e 100644 --- a/jsonapi.services.yml +++ b/jsonapi.services.yml @@ -132,7 +132,7 @@ services: # Event subscribers. jsonapi.resource_response.subscriber: class: Drupal\jsonapi\EventSubscriber\ResourceResponseSubscriber - arguments: ['@serializer', '@renderer', '@logger.channel.jsonapi'] + arguments: ['@serializer', '@renderer', '@logger.channel.jsonapi', '@module_handler'] calls: - [setValidator, []] - [setSchemaFactory, ['@?schemata.schema_factory']] # This is only injected when the service is available. diff --git a/src/EventSubscriber/ResourceResponseSubscriber.php b/src/EventSubscriber/ResourceResponseSubscriber.php index 46087af..3200009 100644 --- a/src/EventSubscriber/ResourceResponseSubscriber.php +++ b/src/EventSubscriber/ResourceResponseSubscriber.php @@ -5,6 +5,7 @@ namespace Drupal\jsonapi\EventSubscriber; use Drupal\Component\Serialization\Json; use Drupal\Core\Cache\CacheableResponse; use Drupal\Core\Cache\CacheableResponseInterface; +use Drupal\Core\Extension\ModuleHandlerInterface; use Drupal\Core\Render\RenderContext; use Drupal\Core\Render\RendererInterface; use Drupal\jsonapi\ResourceResponse; @@ -78,6 +79,13 @@ class ResourceResponseSubscriber implements EventSubscriberInterface { */ protected $schemaFactory; + /** + * The module handler. + * + * @var \Drupal\Core\Extension\ModuleHandlerInterface + */ + protected $moduleHandler; + /** * Constructs a ResourceResponseSubscriber object. * @@ -87,11 +95,14 @@ class ResourceResponseSubscriber implements EventSubscriberInterface { * The renderer. * @param \Psr\Log\LoggerInterface $logger * The JSON API logger channel. + * @param \Drupal\Core\Extension\ModuleHandlerInterface $module_handler + * The module handler. */ - public function __construct(SerializerInterface $serializer, RendererInterface $renderer, LoggerInterface $logger) { + public function __construct(SerializerInterface $serializer, RendererInterface $renderer, LoggerInterface $logger, ModuleHandlerInterface $module_handler) { $this->serializer = $serializer; $this->renderer = $renderer; $this->logger = $logger; + $this->moduleHandler = $module_handler; } /** @@ -255,8 +266,13 @@ class ResourceResponseSubscriber implements EventSubscriberInterface { return TRUE; } - $schema_path = dirname(dirname(__DIR__)) . '/schema.json'; - $generic_jsonapi_schema = (object) ['$ref' => 'file://' . $schema_path]; + $schema_ref = sprintf( + 'file://%s/%s/%s', + DRUPAL_ROOT, + $this->moduleHandler->getModule('jsonapi')->getPath(), + 'schema.json' + ); + $generic_jsonapi_schema = (object) ['$ref' => $schema_ref]; $is_valid = $this->validateSchema($generic_jsonapi_schema, $response_data); if (!$is_valid) { return FALSE; @@ -287,10 +303,13 @@ class ResourceResponseSubscriber implements EventSubscriberInterface { $output_format = 'schema_json'; $described_format = 'api_json'; - $generic_jsonapi_schema = $this->schemaFactory->create($entity_type_id, $bundle); + $schema_object = $this->schemaFactory->create($entity_type_id, $bundle); $format = $output_format . ':' . $described_format; - $output = $this->serializer->serialize($generic_jsonapi_schema, $format); + $output = $this->serializer->serialize($schema_object, $format); $specific_schema = Json::decode($output); + if (!$specific_schema) { + return $is_valid; + } // We need to individually validate each collection resource object. $is_collection = strpos($route_name, '.collection') !== FALSE; diff --git a/tests/src/Unit/EventSubscriber/ResourceResponseSubscriberTest.php b/tests/src/Unit/EventSubscriber/ResourceResponseSubscriberTest.php index 06da13c..7fd8dd3 100644 --- a/tests/src/Unit/EventSubscriber/ResourceResponseSubscriberTest.php +++ b/tests/src/Unit/EventSubscriber/ResourceResponseSubscriberTest.php @@ -2,6 +2,8 @@ namespace Drupal\Tests\jsonapi\Unit\EventSubscriber; +use Drupal\Core\Extension\Extension; +use Drupal\Core\Extension\ModuleHandlerInterface; use Drupal\Core\Render\RendererInterface; use Drupal\jsonapi\EventSubscriber\ResourceResponseSubscriber; use Drupal\rest\ResourceResponse; @@ -15,6 +17,8 @@ use Symfony\Component\HttpFoundation\Request; use Symfony\Component\Routing\Route; use Symfony\Component\Serializer\Serializer; +define('DRUPAL_ROOT', dirname(dirname(dirname(dirname(dirname(__DIR__)))))); + /** * @coversDefaultClass \Drupal\jsonapi\EventSubscriber\ResourceResponseSubscriber * @group jsonapi @@ -37,10 +41,17 @@ class ResourceResponseSubscriberTest extends UnitTestCase { $this->fail('The JSON Schema validator is missing. You can install it with `composer require justinrainbow/json-schema`.'); } + $module_handler = $this->prophesize(ModuleHandlerInterface::class); + $module = $this->prophesize(Extension::class); + $module->getPath()->willReturn('jsonapi'); + $module_handler->getModule('jsonapi')->willReturn( + $module->reveal() + ); $subscriber = new ResourceResponseSubscriber( new Serializer([], [new JsonSchemaEncoder()]), $this->prophesize(RendererInterface::class)->reveal(), - $this->prophesize(LoggerInterface::class)->reveal() + $this->prophesize(LoggerInterface::class)->reveal(), + $module_handler->reveal() ); $subscriber->setValidator(); $this->subscriber = $subscriber;