function ResourceTestBase::doTestRelationshipMutation

Same name in other branches
  1. 8.9.x core/modules/jsonapi/tests/src/Functional/ResourceTestBase.php \Drupal\Tests\jsonapi\Functional\ResourceTestBase::doTestRelationshipMutation()
  2. 10 core/modules/jsonapi/tests/src/Functional/ResourceTestBase.php \Drupal\Tests\jsonapi\Functional\ResourceTestBase::doTestRelationshipMutation()
  3. 11.x core/modules/jsonapi/tests/src/Functional/ResourceTestBase.php \Drupal\Tests\jsonapi\Functional\ResourceTestBase::doTestRelationshipMutation()

Performs one round of relationship POST, PATCH and DELETE route testing.

Parameters

array $request_options: Request options to apply.

See also

\GuzzleHttp\ClientInterface::request()

::testRelationships

2 calls to ResourceTestBase::doTestRelationshipMutation()
MediaTest::doTestRelationshipMutation in core/modules/jsonapi/tests/src/Functional/MediaTest.php
@todo Remove this in https://www.drupal.org/node/2824851.
ResourceTestBase::testRelationships in core/modules/jsonapi/tests/src/Functional/ResourceTestBase.php
Tests CRUD of individual resource relationship data.
1 method overrides ResourceTestBase::doTestRelationshipMutation()
MediaTest::doTestRelationshipMutation in core/modules/jsonapi/tests/src/Functional/MediaTest.php
@todo Remove this in https://www.drupal.org/node/2824851.

File

core/modules/jsonapi/tests/src/Functional/ResourceTestBase.php, line 1441

Class

ResourceTestBase
Subclass this for every JSON:API resource type.

Namespace

Drupal\Tests\jsonapi\Functional

Code

protected function doTestRelationshipMutation(array $request_options) {
    
    /** @var \Drupal\Core\Entity\FieldableEntityInterface $resource */
    $resource = $this->createAnotherEntity('dupe');
    $resource->set('field_jsonapi_test_entity_ref', NULL);
    $violations = $resource->validate();
    assert($violations->count() === 0, (string) $violations);
    $resource->save();
    $target_resource = $this->createUser();
    $violations = $target_resource->validate();
    assert($violations->count() === 0, (string) $violations);
    $target_resource->save();
    $target_identifier = static::toResourceIdentifier($target_resource);
    $resource_identifier = static::toResourceIdentifier($resource);
    $relationship_field_name = 'field_jsonapi_test_entity_ref';
    
    /** @var \Drupal\Core\Access\AccessResultReasonInterface $update_access */
    $update_access = static::entityAccess($resource, 'update', $this->account)
        ->andIf(static::entityFieldAccess($resource, $relationship_field_name, 'edit', $this->account));
    $url = Url::fromRoute(sprintf("jsonapi.{$resource_identifier['type']}.{$relationship_field_name}.relationship.patch"), [
        'entity' => $resource->uuid(),
    ]);
    // Test POST: missing content-type.
    $response = $this->request('POST', $url, $request_options);
    $this->assertSame(415, $response->getStatusCode());
    // Set the JSON:API media type header for all subsequent requests.
    $request_options[RequestOptions::HEADERS]['Content-Type'] = 'application/vnd.api+json';
    if ($update_access->isAllowed()) {
        // Test POST: empty body.
        $response = $this->request('POST', $url, $request_options);
        $this->assertResourceErrorResponse(400, 'Empty request body.', $url, $response, FALSE);
        // Test PATCH: empty body.
        $response = $this->request('PATCH', $url, $request_options);
        $this->assertResourceErrorResponse(400, 'Empty request body.', $url, $response, FALSE);
        // Test POST: empty data.
        $request_options[RequestOptions::BODY] = Json::encode([
            'data' => [],
        ]);
        $response = $this->request('POST', $url, $request_options);
        $this->assertResourceResponse(204, NULL, $response);
        // Test PATCH: empty data.
        $request_options[RequestOptions::BODY] = Json::encode([
            'data' => [],
        ]);
        $response = $this->request('PATCH', $url, $request_options);
        $this->assertResourceResponse(204, NULL, $response);
        // Test POST: data as resource identifier, not array of identifiers.
        $request_options[RequestOptions::BODY] = Json::encode([
            'data' => $target_identifier,
        ]);
        $response = $this->request('POST', $url, $request_options);
        $this->assertResourceErrorResponse(400, 'Invalid body payload for the relationship.', $url, $response, FALSE);
        // Test PATCH: data as resource identifier, not array of identifiers.
        $request_options[RequestOptions::BODY] = Json::encode([
            'data' => $target_identifier,
        ]);
        $response = $this->request('PATCH', $url, $request_options);
        $this->assertResourceErrorResponse(400, 'Invalid body payload for the relationship.', $url, $response, FALSE);
        // Test POST: missing the 'type' field.
        $request_options[RequestOptions::BODY] = Json::encode([
            'data' => array_intersect_key($target_identifier, [
                'id' => 'id',
            ]),
        ]);
        $response = $this->request('POST', $url, $request_options);
        $this->assertResourceErrorResponse(400, 'Invalid body payload for the relationship.', $url, $response, FALSE);
        // Test PATCH: missing the 'type' field.
        $request_options[RequestOptions::BODY] = Json::encode([
            'data' => array_intersect_key($target_identifier, [
                'id' => 'id',
            ]),
        ]);
        $response = $this->request('PATCH', $url, $request_options);
        $this->assertResourceErrorResponse(400, 'Invalid body payload for the relationship.', $url, $response, FALSE);
        // If the base resource type is the same as that of the target's (as it
        // will be for `user--user`), then the validity error will not be
        // triggered, needlessly failing this assertion.
        if (static::$resourceTypeName !== $target_identifier['type']) {
            // Test POST: invalid target.
            $request_options[RequestOptions::BODY] = Json::encode([
                'data' => [
                    $resource_identifier,
                ],
            ]);
            $response = $this->request('POST', $url, $request_options);
            $this->assertResourceErrorResponse(400, sprintf('The provided type (%s) does not match the destination resource types (%s).', $resource_identifier['type'], $target_identifier['type']), $url, $response, FALSE);
            // Test PATCH: invalid target.
            $request_options[RequestOptions::BODY] = Json::encode([
                'data' => [
                    $resource_identifier,
                ],
            ]);
            $response = $this->request('POST', $url, $request_options);
            $this->assertResourceErrorResponse(400, sprintf('The provided type (%s) does not match the destination resource types (%s).', $resource_identifier['type'], $target_identifier['type']), $url, $response, FALSE);
        }
        // Test POST: duplicate targets, no arity.
        $request_options[RequestOptions::BODY] = Json::encode([
            'data' => [
                $target_identifier,
                $target_identifier,
            ],
        ]);
        $response = $this->request('POST', $url, $request_options);
        $this->assertResourceErrorResponse(400, 'Duplicate relationships are not permitted. Use `meta.arity` to distinguish resource identifiers with matching `type` and `id` values.', $url, $response, FALSE);
        // Test PATCH: duplicate targets, no arity.
        $request_options[RequestOptions::BODY] = Json::encode([
            'data' => [
                $target_identifier,
                $target_identifier,
            ],
        ]);
        $response = $this->request('PATCH', $url, $request_options);
        $this->assertResourceErrorResponse(400, 'Duplicate relationships are not permitted. Use `meta.arity` to distinguish resource identifiers with matching `type` and `id` values.', $url, $response, FALSE);
        // Test POST: success.
        $request_options[RequestOptions::BODY] = Json::encode([
            'data' => [
                $target_identifier,
            ],
        ]);
        $response = $this->request('POST', $url, $request_options);
        $this->assertResourceResponse(204, NULL, $response);
        // Test POST: success, relationship already exists, no arity.
        $response = $this->request('POST', $url, $request_options);
        $this->assertResourceResponse(204, NULL, $response);
        // Test POST: success, relationship already exists, new arity.
        $request_options[RequestOptions::BODY] = Json::encode([
            'data' => [
                $target_identifier + [
                    'meta' => [
                        'arity' => 1,
                    ],
                ],
            ],
        ]);
        $response = $this->request('POST', $url, $request_options);
        $resource->set($relationship_field_name, [
            $target_resource,
            $target_resource,
        ]);
        $expected_document = $this->getExpectedGetRelationshipDocument($relationship_field_name, $resource);
        $expected_document['data'][0] += [
            'meta' => [
                'arity' => 0,
            ],
        ];
        $expected_document['data'][1] += [
            'meta' => [
                'arity' => 1,
            ],
        ];
        $this->assertResourceResponse(200, $expected_document, $response);
        // Test PATCH: success, new value is the same as given value.
        $request_options[RequestOptions::BODY] = Json::encode([
            'data' => [
                $target_identifier + [
                    'meta' => [
                        'arity' => 0,
                    ],
                ],
                $target_identifier + [
                    'meta' => [
                        'arity' => 1,
                    ],
                ],
            ],
        ]);
        $response = $this->request('PATCH', $url, $request_options);
        $this->assertResourceResponse(204, NULL, $response);
        // Test POST: success, relationship already exists, new arity.
        $request_options[RequestOptions::BODY] = Json::encode([
            'data' => [
                $target_identifier + [
                    'meta' => [
                        'arity' => 2,
                    ],
                ],
            ],
        ]);
        $response = $this->request('POST', $url, $request_options);
        $resource->set($relationship_field_name, [
            $target_resource,
            $target_resource,
            $target_resource,
        ]);
        $expected_document = $this->getExpectedGetRelationshipDocument($relationship_field_name, $resource);
        $expected_document['data'][0] += [
            'meta' => [
                'arity' => 0,
            ],
        ];
        $expected_document['data'][1] += [
            'meta' => [
                'arity' => 1,
            ],
        ];
        $expected_document['data'][2] += [
            'meta' => [
                'arity' => 2,
            ],
        ];
        // 200 with response body because the request did not include the
        // existing relationship resource identifier object.
        $this->assertResourceResponse(200, $expected_document, $response);
        // Test POST: success.
        $request_options[RequestOptions::BODY] = Json::encode([
            'data' => [
                $target_identifier + [
                    'meta' => [
                        'arity' => 0,
                    ],
                ],
                $target_identifier + [
                    'meta' => [
                        'arity' => 1,
                    ],
                ],
            ],
        ]);
        $response = $this->request('POST', $url, $request_options);
        // 200 with response body because the request did not include the
        // resource identifier with arity 2.
        $this->assertResourceResponse(200, $expected_document, $response);
        // Test PATCH: success.
        $request_options[RequestOptions::BODY] = Json::encode([
            'data' => [
                $target_identifier + [
                    'meta' => [
                        'arity' => 0,
                    ],
                ],
                $target_identifier + [
                    'meta' => [
                        'arity' => 1,
                    ],
                ],
                $target_identifier + [
                    'meta' => [
                        'arity' => 2,
                    ],
                ],
            ],
        ]);
        $response = $this->request('PATCH', $url, $request_options);
        // 204 no content. PATCH data matches existing data.
        $this->assertResourceResponse(204, NULL, $response);
        // Test DELETE: three existing relationships, two removed.
        $request_options[RequestOptions::BODY] = Json::encode([
            'data' => [
                $target_identifier + [
                    'meta' => [
                        'arity' => 0,
                    ],
                ],
                $target_identifier + [
                    'meta' => [
                        'arity' => 2,
                    ],
                ],
            ],
        ]);
        $response = $this->request('DELETE', $url, $request_options);
        $this->assertResourceResponse(204, NULL, $response);
        // Subsequent GET should return only one resource identifier, with no
        // arity.
        $resource->set($relationship_field_name, [
            $target_resource,
        ]);
        $expected_document = $this->getExpectedGetRelationshipDocument($relationship_field_name, $resource);
        $response = $this->request('GET', $url, $request_options);
        $this->assertSameDocument($expected_document, Json::decode((string) $response->getBody()));
        // Test DELETE: one existing relationship, removed.
        $request_options[RequestOptions::BODY] = Json::encode([
            'data' => [
                $target_identifier,
            ],
        ]);
        $response = $this->request('DELETE', $url, $request_options);
        $resource->set($relationship_field_name, []);
        $this->assertResourceResponse(204, NULL, $response);
        $expected_document = $this->getExpectedGetRelationshipDocument($relationship_field_name, $resource);
        $response = $this->request('GET', $url, $request_options);
        $this->assertSameDocument($expected_document, Json::decode((string) $response->getBody()));
        // Test DELETE: no existing relationships, no op, success.
        $request_options[RequestOptions::BODY] = Json::encode([
            'data' => [
                $target_identifier,
            ],
        ]);
        $response = $this->request('DELETE', $url, $request_options);
        $this->assertResourceResponse(204, NULL, $response);
        $expected_document = $this->getExpectedGetRelationshipDocument($relationship_field_name, $resource);
        $response = $this->request('GET', $url, $request_options);
        $this->assertSameDocument($expected_document, Json::decode((string) $response->getBody()));
        // Test PATCH: success, new value is different than existing value.
        $request_options[RequestOptions::BODY] = Json::encode([
            'data' => [
                $target_identifier + [
                    'meta' => [
                        'arity' => 2,
                    ],
                ],
                $target_identifier + [
                    'meta' => [
                        'arity' => 3,
                    ],
                ],
            ],
        ]);
        $response = $this->request('PATCH', $url, $request_options);
        $resource->set($relationship_field_name, [
            $target_resource,
            $target_resource,
        ]);
        $expected_document = $this->getExpectedGetRelationshipDocument($relationship_field_name, $resource);
        $expected_document['data'][0] += [
            'meta' => [
                'arity' => 0,
            ],
        ];
        $expected_document['data'][1] += [
            'meta' => [
                'arity' => 1,
            ],
        ];
        // 200 with response body because arity values are computed; that means
        // that the PATCH arity values 2 + 3 will become 0 + 1 if there are not
        // already resource identifiers with those arity values.
        $this->assertResourceResponse(200, $expected_document, $response);
        // Test DELETE: two existing relationships, both removed because no arity
        // was specified.
        $request_options[RequestOptions::BODY] = Json::encode([
            'data' => [
                $target_identifier,
            ],
        ]);
        $response = $this->request('DELETE', $url, $request_options);
        $resource->set($relationship_field_name, []);
        $this->assertResourceResponse(204, NULL, $response);
        $resource->set($relationship_field_name, []);
        $expected_document = $this->getExpectedGetRelationshipDocument($relationship_field_name, $resource);
        $response = $this->request('GET', $url, $request_options);
        $this->assertSameDocument($expected_document, Json::decode((string) $response->getBody()));
    }
    else {
        $request_options[RequestOptions::BODY] = Json::encode([
            'data' => [
                $target_identifier,
            ],
        ]);
        $response = $this->request('POST', $url, $request_options);
        $message = 'The current user is not allowed to edit this relationship.';
        $message .= ($reason = $update_access->getReason()) ? ' ' . $reason : '';
        $this->assertResourceErrorResponse(403, $message, $url, $response, FALSE);
        $response = $this->request('PATCH', $url, $request_options);
        $this->assertResourceErrorResponse(403, $message, $url, $response, FALSE);
        $response = $this->request('DELETE', $url, $request_options);
        $this->assertResourceErrorResponse(403, $message, $url, $response, FALSE);
    }
    // Remove the test entities that were created.
    $resource->delete();
    $target_resource->delete();
}

Buggy or inaccurate documentation? Please file an issue. Need support? Need help programming? Connect with the Drupal community.