Issue #3070204 by gabesullice, ravi.shankar, Wim Leers, mglaman, catch, larowlan, jibran: Refactor the JSON:API FieldResolver to use a resource type instead of an entity type ID and bundle ID pair
parent
ade7b950a1
commit
12b47612f2
|
|
@ -251,10 +251,8 @@ class FieldResolver {
|
||||||
* 'uid.field_first_name' -> 'uid.entity.field_first_name'.
|
* 'uid.field_first_name' -> 'uid.entity.field_first_name'.
|
||||||
* 'author.firstName' -> 'field_author.entity.field_first_name'
|
* 'author.firstName' -> 'field_author.entity.field_first_name'
|
||||||
*
|
*
|
||||||
* @param string $entity_type_id
|
* @param \Drupal\jsonapi\ResourceType\ResourceType $resource_type
|
||||||
* The type of the entity for which to resolve the field name.
|
* The JSON:API resource type from which to resolve the field name.
|
||||||
* @param string $bundle
|
|
||||||
* The bundle of the entity for which to resolve the field name.
|
|
||||||
* @param string $external_field_name
|
* @param string $external_field_name
|
||||||
* The public field name to map to a Drupal field name.
|
* The public field name to map to a Drupal field name.
|
||||||
*
|
*
|
||||||
|
|
@ -263,9 +261,21 @@ class FieldResolver {
|
||||||
*
|
*
|
||||||
* @throws \Drupal\Core\Http\Exception\CacheableBadRequestHttpException
|
* @throws \Drupal\Core\Http\Exception\CacheableBadRequestHttpException
|
||||||
*/
|
*/
|
||||||
public function resolveInternalEntityQueryPath($entity_type_id, $bundle, $external_field_name) {
|
public function resolveInternalEntityQueryPath($resource_type, $external_field_name) {
|
||||||
|
$function_args = func_get_args();
|
||||||
|
// @todo Remove this conditional block in drupal:9.0.0 and add a type hint
|
||||||
|
// to the first argument of this method.
|
||||||
|
// @see https://www.drupal.org/project/drupal/issues/3078045
|
||||||
|
if (count($function_args) === 3) {
|
||||||
|
@trigger_error('Passing the entity type ID and bundle to ' . __METHOD__ . ' is deprecated in drupal:8.8.0 and will throw a fatal error in drupal:9.0.0. Pass a JSON:API resource type instead. See https://www.drupal.org/node/3078036', E_USER_DEPRECATED);
|
||||||
|
list($entity_type_id, $bundle, $external_field_name) = $function_args;
|
||||||
|
$resource_type = $this->resourceTypeRepository->get($entity_type_id, $bundle);
|
||||||
|
}
|
||||||
|
elseif (!$resource_type instanceof ResourceType) {
|
||||||
|
throw new \InvalidArgumentException("The first argument to " . __METHOD__ . " should be an instance of \Drupal\jsonapi\ResourceType\ResourceType, " . gettype($resource_type) . " given.");
|
||||||
|
}
|
||||||
|
|
||||||
$cacheability = (new CacheableMetadata())->addCacheContexts(['url.query_args:filter', 'url.query_args:sort']);
|
$cacheability = (new CacheableMetadata())->addCacheContexts(['url.query_args:filter', 'url.query_args:sort']);
|
||||||
$resource_type = $this->resourceTypeRepository->get($entity_type_id, $bundle);
|
|
||||||
if (empty($external_field_name)) {
|
if (empty($external_field_name)) {
|
||||||
throw new CacheableBadRequestHttpException($cacheability, 'No field name was provided for the filter.');
|
throw new CacheableBadRequestHttpException($cacheability, 'No field name was provided for the filter.');
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -877,7 +877,7 @@ class EntityResource {
|
||||||
// Apply any sorts to the entity query.
|
// Apply any sorts to the entity query.
|
||||||
if (isset($params[Sort::KEY_NAME]) && $sort = $params[Sort::KEY_NAME]) {
|
if (isset($params[Sort::KEY_NAME]) && $sort = $params[Sort::KEY_NAME]) {
|
||||||
foreach ($sort->fields() as $field) {
|
foreach ($sort->fields() as $field) {
|
||||||
$path = $this->fieldResolver->resolveInternalEntityQueryPath($resource_type->getEntityTypeId(), $resource_type->getBundle(), $field[Sort::PATH_KEY]);
|
$path = $this->fieldResolver->resolveInternalEntityQueryPath($resource_type, $field[Sort::PATH_KEY]);
|
||||||
$direction = isset($field[Sort::DIRECTION_KEY]) ? $field[Sort::DIRECTION_KEY] : 'ASC';
|
$direction = isset($field[Sort::DIRECTION_KEY]) ? $field[Sort::DIRECTION_KEY] : 'ASC';
|
||||||
$langcode = isset($field[Sort::LANGUAGE_KEY]) ? $field[Sort::LANGUAGE_KEY] : NULL;
|
$langcode = isset($field[Sort::LANGUAGE_KEY]) ? $field[Sort::LANGUAGE_KEY] : NULL;
|
||||||
$query->sort($path, $direction, $langcode);
|
$query->sort($path, $direction, $langcode);
|
||||||
|
|
|
||||||
|
|
@ -157,7 +157,7 @@ class Filter {
|
||||||
foreach ($expanded as &$filter_item) {
|
foreach ($expanded as &$filter_item) {
|
||||||
if (isset($filter_item[static::CONDITION_KEY][EntityCondition::PATH_KEY])) {
|
if (isset($filter_item[static::CONDITION_KEY][EntityCondition::PATH_KEY])) {
|
||||||
$unresolved = $filter_item[static::CONDITION_KEY][EntityCondition::PATH_KEY];
|
$unresolved = $filter_item[static::CONDITION_KEY][EntityCondition::PATH_KEY];
|
||||||
$filter_item[static::CONDITION_KEY][EntityCondition::PATH_KEY] = $field_resolver->resolveInternalEntityQueryPath($resource_type->getEntityTypeId(), $resource_type->getBundle(), $unresolved);
|
$filter_item[static::CONDITION_KEY][EntityCondition::PATH_KEY] = $field_resolver->resolveInternalEntityQueryPath($resource_type, $unresolved);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return new static(static::buildEntityConditionGroup($expanded));
|
return new static(static::buildEntityConditionGroup($expanded));
|
||||||
|
|
|
||||||
|
|
@ -168,7 +168,8 @@ class FieldResolverTest extends JsonapiKernelTestBase {
|
||||||
* @dataProvider resolveInternalEntityQueryPathProvider
|
* @dataProvider resolveInternalEntityQueryPathProvider
|
||||||
*/
|
*/
|
||||||
public function testResolveInternalEntityQueryPath($expect, $external_path, $entity_type_id = 'entity_test_with_bundle', $bundle = 'bundle1') {
|
public function testResolveInternalEntityQueryPath($expect, $external_path, $entity_type_id = 'entity_test_with_bundle', $bundle = 'bundle1') {
|
||||||
$this->assertEquals($expect, $this->sut->resolveInternalEntityQueryPath($entity_type_id, $bundle, $external_path));
|
$resource_type = $this->resourceTypeRepository->get($entity_type_id, $bundle);
|
||||||
|
$this->assertEquals($expect, $this->sut->resolveInternalEntityQueryPath($resource_type, $external_path));
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
@ -252,7 +253,8 @@ class FieldResolverTest extends JsonapiKernelTestBase {
|
||||||
if (!empty($expected_message)) {
|
if (!empty($expected_message)) {
|
||||||
$this->expectExceptionMessage($expected_message);
|
$this->expectExceptionMessage($expected_message);
|
||||||
}
|
}
|
||||||
$this->sut->resolveInternalEntityQueryPath($entity_type, $bundle, $external_path);
|
$resource_type = $this->resourceTypeRepository->get($entity_type, $bundle);
|
||||||
|
$this->sut->resolveInternalEntityQueryPath($resource_type, $external_path);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
||||||
|
|
@ -46,6 +46,13 @@ class FilterTest extends JsonapiKernelTestBase {
|
||||||
*/
|
*/
|
||||||
protected $nodeStorage;
|
protected $nodeStorage;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The JSON:API resource type repository.
|
||||||
|
*
|
||||||
|
* @var \Drupal\jsonapi\ResourceType\ResourceTypeRepositoryInterface
|
||||||
|
*/
|
||||||
|
protected $resourceTypeRepository;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* {@inheritdoc}
|
* {@inheritdoc}
|
||||||
*/
|
*/
|
||||||
|
|
@ -68,6 +75,7 @@ class FilterTest extends JsonapiKernelTestBase {
|
||||||
|
|
||||||
$this->nodeStorage = $this->container->get('entity_type.manager')->getStorage('node');
|
$this->nodeStorage = $this->container->get('entity_type.manager')->getStorage('node');
|
||||||
$this->fieldResolver = $this->container->get('jsonapi.field_resolver');
|
$this->fieldResolver = $this->container->get('jsonapi.field_resolver');
|
||||||
|
$this->resourceTypeRepository = $this->container->get('jsonapi.resource_type.repository');
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
@ -76,7 +84,7 @@ class FilterTest extends JsonapiKernelTestBase {
|
||||||
public function testInvalidFilterPathDueToMissingPropertyName() {
|
public function testInvalidFilterPathDueToMissingPropertyName() {
|
||||||
$this->expectException(CacheableBadRequestHttpException::class);
|
$this->expectException(CacheableBadRequestHttpException::class);
|
||||||
$this->expectExceptionMessage('Invalid nested filtering. The field `colors`, given in the path `colors` is incomplete, it must end with one of the following specifiers: `value`, `format`, `processed`.');
|
$this->expectExceptionMessage('Invalid nested filtering. The field `colors`, given in the path `colors` is incomplete, it must end with one of the following specifiers: `value`, `format`, `processed`.');
|
||||||
$resource_type = new ResourceType('node', 'painting', NULL);
|
$resource_type = $this->resourceTypeRepository->get('node', 'painting');
|
||||||
Filter::createFromQueryParameter(['colors' => ''], $resource_type, $this->fieldResolver);
|
Filter::createFromQueryParameter(['colors' => ''], $resource_type, $this->fieldResolver);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -86,7 +94,7 @@ class FilterTest extends JsonapiKernelTestBase {
|
||||||
public function testInvalidFilterPathDueToMissingPropertyNameReferenceFieldWithMetaProperties() {
|
public function testInvalidFilterPathDueToMissingPropertyNameReferenceFieldWithMetaProperties() {
|
||||||
$this->expectException(CacheableBadRequestHttpException::class);
|
$this->expectException(CacheableBadRequestHttpException::class);
|
||||||
$this->expectExceptionMessage('Invalid nested filtering. The field `photo`, given in the path `photo` is incomplete, it must end with one of the following specifiers: `id`, `meta.alt`, `meta.title`, `meta.width`, `meta.height`.');
|
$this->expectExceptionMessage('Invalid nested filtering. The field `photo`, given in the path `photo` is incomplete, it must end with one of the following specifiers: `id`, `meta.alt`, `meta.title`, `meta.width`, `meta.height`.');
|
||||||
$resource_type = new ResourceType('node', 'painting', NULL);
|
$resource_type = $this->resourceTypeRepository->get('node', 'painting');
|
||||||
Filter::createFromQueryParameter(['photo' => ''], $resource_type, $this->fieldResolver);
|
Filter::createFromQueryParameter(['photo' => ''], $resource_type, $this->fieldResolver);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -96,7 +104,7 @@ class FilterTest extends JsonapiKernelTestBase {
|
||||||
public function testInvalidFilterPathDueMissingMetaPrefixReferenceFieldWithMetaProperties() {
|
public function testInvalidFilterPathDueMissingMetaPrefixReferenceFieldWithMetaProperties() {
|
||||||
$this->expectException(CacheableBadRequestHttpException::class);
|
$this->expectException(CacheableBadRequestHttpException::class);
|
||||||
$this->expectExceptionMessage('Invalid nested filtering. The property `alt`, given in the path `photo.alt` belongs to the meta object of a relationship and must be preceded by `meta`.');
|
$this->expectExceptionMessage('Invalid nested filtering. The property `alt`, given in the path `photo.alt` belongs to the meta object of a relationship and must be preceded by `meta`.');
|
||||||
$resource_type = new ResourceType('node', 'painting', NULL);
|
$resource_type = $this->resourceTypeRepository->get('node', 'painting');
|
||||||
Filter::createFromQueryParameter(['photo.alt' => ''], $resource_type, $this->fieldResolver);
|
Filter::createFromQueryParameter(['photo.alt' => ''], $resource_type, $this->fieldResolver);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -106,7 +114,7 @@ class FilterTest extends JsonapiKernelTestBase {
|
||||||
public function testInvalidFilterPathDueToMissingPropertyNameReferenceFieldWithoutMetaProperties() {
|
public function testInvalidFilterPathDueToMissingPropertyNameReferenceFieldWithoutMetaProperties() {
|
||||||
$this->expectException(CacheableBadRequestHttpException::class);
|
$this->expectException(CacheableBadRequestHttpException::class);
|
||||||
$this->expectExceptionMessage('Invalid nested filtering. The field `uid`, given in the path `uid` is incomplete, it must end with one of the following specifiers: `id`.');
|
$this->expectExceptionMessage('Invalid nested filtering. The field `uid`, given in the path `uid` is incomplete, it must end with one of the following specifiers: `id`.');
|
||||||
$resource_type = new ResourceType('node', 'painting', NULL);
|
$resource_type = $this->resourceTypeRepository->get('node', 'painting');
|
||||||
Filter::createFromQueryParameter(['uid' => ''], $resource_type, $this->fieldResolver);
|
Filter::createFromQueryParameter(['uid' => ''], $resource_type, $this->fieldResolver);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -116,7 +124,7 @@ class FilterTest extends JsonapiKernelTestBase {
|
||||||
public function testInvalidFilterPathDueToNonexistentProperty() {
|
public function testInvalidFilterPathDueToNonexistentProperty() {
|
||||||
$this->expectException(CacheableBadRequestHttpException::class);
|
$this->expectException(CacheableBadRequestHttpException::class);
|
||||||
$this->expectExceptionMessage('Invalid nested filtering. The property `foobar`, given in the path `colors.foobar`, does not exist. Must be one of the following property names: `value`, `format`, `processed`.');
|
$this->expectExceptionMessage('Invalid nested filtering. The property `foobar`, given in the path `colors.foobar`, does not exist. Must be one of the following property names: `value`, `format`, `processed`.');
|
||||||
$resource_type = new ResourceType('node', 'painting', NULL);
|
$resource_type = $this->resourceTypeRepository->get('node', 'painting');
|
||||||
Filter::createFromQueryParameter(['colors.foobar' => ''], $resource_type, $this->fieldResolver);
|
Filter::createFromQueryParameter(['colors.foobar' => ''], $resource_type, $this->fieldResolver);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -126,7 +134,7 @@ class FilterTest extends JsonapiKernelTestBase {
|
||||||
public function testInvalidFilterPathDueToElidedSoleProperty() {
|
public function testInvalidFilterPathDueToElidedSoleProperty() {
|
||||||
$this->expectException(CacheableBadRequestHttpException::class);
|
$this->expectException(CacheableBadRequestHttpException::class);
|
||||||
$this->expectExceptionMessage('Invalid nested filtering. The property `value`, given in the path `promote.value`, does not exist. Filter by `promote`, not `promote.value` (the JSON:API module elides property names from single-property fields).');
|
$this->expectExceptionMessage('Invalid nested filtering. The property `value`, given in the path `promote.value`, does not exist. Filter by `promote`, not `promote.value` (the JSON:API module elides property names from single-property fields).');
|
||||||
$resource_type = new ResourceType('node', 'painting', NULL);
|
$resource_type = $this->resourceTypeRepository->get('node', 'painting');
|
||||||
Filter::createFromQueryParameter(['promote.value' => ''], $resource_type, $this->fieldResolver);
|
Filter::createFromQueryParameter(['promote.value' => ''], $resource_type, $this->fieldResolver);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -155,7 +163,7 @@ class FilterTest extends JsonapiKernelTestBase {
|
||||||
return (string) $p->getValue($entity_query);
|
return (string) $p->getValue($entity_query);
|
||||||
};
|
};
|
||||||
|
|
||||||
$resource_type = new ResourceType('node', 'painting', NULL);
|
$resource_type = $this->resourceTypeRepository->get('node', 'painting');
|
||||||
foreach ($data as $case) {
|
foreach ($data as $case) {
|
||||||
$parameter = $case[0];
|
$parameter = $case[0];
|
||||||
$expected_query = $case[1];
|
$expected_query = $case[1];
|
||||||
|
|
@ -405,7 +413,7 @@ class FilterTest extends JsonapiKernelTestBase {
|
||||||
*/
|
*/
|
||||||
protected function getFieldResolverMock(ResourceType $resource_type) {
|
protected function getFieldResolverMock(ResourceType $resource_type) {
|
||||||
$field_resolver = $this->prophesize(FieldResolver::class);
|
$field_resolver = $this->prophesize(FieldResolver::class);
|
||||||
$field_resolver->resolveInternalEntityQueryPath($resource_type->getEntityTypeId(), $resource_type->getBundle(), Argument::any())->willReturnArgument(2);
|
$field_resolver->resolveInternalEntityQueryPath($resource_type, Argument::any())->willReturnArgument(1);
|
||||||
return $field_resolver->reveal();
|
return $field_resolver->reveal();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue