You are here

protected function ResourceResponseValidator::validateResponse in JSON:API 8

Same name and namespace in other branches
  1. 8.2 src/EventSubscriber/ResourceResponseValidator.php \Drupal\jsonapi\EventSubscriber\ResourceResponseValidator::validateResponse()

Validates a response against the JSON API specification.

Parameters

\Symfony\Component\HttpFoundation\Response $response: The response to validate.

\Symfony\Component\HttpFoundation\Request $request: The request containing info about what to validate.

Return value

bool FALSE if the response failed validation, otherwise TRUE.

1 call to ResourceResponseValidator::validateResponse()
ResourceResponseValidator::doValidateResponse in src/EventSubscriber/ResourceResponseValidator.php
Wraps validation in an assert to prevent execution in production.

File

src/EventSubscriber/ResourceResponseValidator.php, line 162

Class

ResourceResponseValidator
Response subscriber that validates a JSON API response.

Namespace

Drupal\jsonapi\EventSubscriber

Code

protected function validateResponse(Response $response, Request $request) {

  // If the validator isn't set, then the validation library is not installed.
  if (!$this->validator) {
    return TRUE;
  }

  // Do not use Json::decode here since it coerces the response into an
  // associative array, which creates validation errors.
  $response_data = json_decode($response
    ->getContent());
  if (empty($response_data)) {
    return TRUE;
  }
  $schema_ref = sprintf('file://%s/schema.json', implode('/', [
    $this->appRoot,
    $this->moduleHandler
      ->getModule('jsonapi')
      ->getPath(),
  ]));
  $generic_jsonapi_schema = (object) [
    '$ref' => $schema_ref,
  ];
  $is_valid = $this
    ->validateSchema($generic_jsonapi_schema, $response_data);
  if (!$is_valid) {
    return FALSE;
  }

  // This will be set if the schemata module is present.
  if (!$this->schemaFactory) {

    // Fall back the valid generic result since schemata is absent.
    return TRUE;
  }

  // Get the schema for the current resource. For that we will need to
  // introspect the request to find the entity type and bundle matched by the
  // router.
  $resource_type = $request
    ->get(Routes::RESOURCE_TYPE_KEY);
  $route_name = $request->attributes
    ->get(RouteObjectInterface::ROUTE_NAME);

  // We shouldn't validate related/relationships.
  $is_related = strpos($route_name, '.related') !== FALSE;
  $is_relationship = strpos($route_name, '.relationship') !== FALSE;
  if ($is_related || $is_relationship) {

    // Fall back the valid generic result since schemata is absent.
    return TRUE;
  }
  $entity_type_id = $resource_type
    ->getEntityTypeId();
  $bundle = $resource_type
    ->getBundle();
  $output_format = 'schema_json';
  $described_format = 'api_json';
  $schema_object = $this->schemaFactory
    ->create($entity_type_id, $bundle);
  $format = $output_format . ':' . $described_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;

  // Iterate over each resource object and check the schema.
  return array_reduce($is_collection ? $response_data->data : [
    $response_data->data,
  ], function ($valid, $resource_object) use ($specific_schema) {

    // Validating the schema first ensures that every object is processed.
    return $this
      ->validateSchema($specific_schema, $resource_object) && $valid;
  }, TRUE);
}