Chris@18
|
1 <?php
|
Chris@18
|
2
|
Chris@18
|
3 namespace Drupal\Tests\jsonapi\Functional;
|
Chris@18
|
4
|
Chris@18
|
5 use Drupal\comment\Entity\Comment;
|
Chris@18
|
6 use Drupal\comment\Plugin\Field\FieldType\CommentItemInterface;
|
Chris@18
|
7 use Drupal\comment\Tests\CommentTestTrait;
|
Chris@18
|
8 use Drupal\Component\Serialization\Json;
|
Chris@18
|
9 use Drupal\Core\Field\FieldStorageDefinitionInterface;
|
Chris@18
|
10 use Drupal\Core\Url;
|
Chris@18
|
11 use Drupal\datetime\Plugin\Field\FieldType\DateTimeItem;
|
Chris@18
|
12 use Drupal\entity_test\Entity\EntityTestMapField;
|
Chris@18
|
13 use Drupal\field\Entity\FieldConfig;
|
Chris@18
|
14 use Drupal\field\Entity\FieldStorageConfig;
|
Chris@18
|
15 use Drupal\node\Entity\Node;
|
Chris@18
|
16 use Drupal\node\Entity\NodeType;
|
Chris@18
|
17 use Drupal\shortcut\Entity\Shortcut;
|
Chris@18
|
18 use Drupal\taxonomy\Entity\Term;
|
Chris@18
|
19 use Drupal\taxonomy\Entity\Vocabulary;
|
Chris@18
|
20 use Drupal\user\Entity\Role;
|
Chris@18
|
21 use Drupal\user\Entity\User;
|
Chris@18
|
22 use Drupal\user\RoleInterface;
|
Chris@18
|
23 use GuzzleHttp\RequestOptions;
|
Chris@18
|
24
|
Chris@18
|
25 /**
|
Chris@18
|
26 * JSON:API regression tests.
|
Chris@18
|
27 *
|
Chris@18
|
28 * @group jsonapi
|
Chris@18
|
29 * @group legacy
|
Chris@18
|
30 *
|
Chris@18
|
31 * @internal
|
Chris@18
|
32 */
|
Chris@18
|
33 class JsonApiRegressionTest extends JsonApiFunctionalTestBase {
|
Chris@18
|
34
|
Chris@18
|
35 use CommentTestTrait;
|
Chris@18
|
36
|
Chris@18
|
37 /**
|
Chris@18
|
38 * {@inheritdoc}
|
Chris@18
|
39 */
|
Chris@18
|
40 public static $modules = [
|
Chris@18
|
41 'basic_auth',
|
Chris@18
|
42 ];
|
Chris@18
|
43
|
Chris@18
|
44 /**
|
Chris@18
|
45 * Ensure filtering on relationships works with bundle-specific target types.
|
Chris@18
|
46 *
|
Chris@18
|
47 * @see https://www.drupal.org/project/jsonapi/issues/2953207
|
Chris@18
|
48 */
|
Chris@18
|
49 public function testBundleSpecificTargetEntityTypeFromIssue2953207() {
|
Chris@18
|
50 // Set up data model.
|
Chris@18
|
51 $this->assertTrue($this->container->get('module_installer')->install(['comment'], TRUE), 'Installed modules.');
|
Chris@18
|
52 $this->addDefaultCommentField('taxonomy_term', 'tags', 'comment', CommentItemInterface::OPEN, 'tcomment');
|
Chris@18
|
53 $this->rebuildAll();
|
Chris@18
|
54
|
Chris@18
|
55 // Create data.
|
Chris@18
|
56 Term::create([
|
Chris@18
|
57 'name' => 'foobar',
|
Chris@18
|
58 'vid' => 'tags',
|
Chris@18
|
59 ])->save();
|
Chris@18
|
60 Comment::create([
|
Chris@18
|
61 'subject' => 'Llama',
|
Chris@18
|
62 'entity_id' => 1,
|
Chris@18
|
63 'entity_type' => 'taxonomy_term',
|
Chris@18
|
64 'field_name' => 'comment',
|
Chris@18
|
65 ])->save();
|
Chris@18
|
66
|
Chris@18
|
67 // Test.
|
Chris@18
|
68 $user = $this->drupalCreateUser([
|
Chris@18
|
69 'access comments',
|
Chris@18
|
70 ]);
|
Chris@18
|
71 $response = $this->request('GET', Url::fromUri('internal:/jsonapi/comment/tcomment?include=entity_id&filter[entity_id.name]=foobar'), [
|
Chris@18
|
72 RequestOptions::AUTH => [
|
Chris@18
|
73 $user->getUsername(),
|
Chris@18
|
74 $user->pass_raw,
|
Chris@18
|
75 ],
|
Chris@18
|
76 ]);
|
Chris@18
|
77 $this->assertSame(200, $response->getStatusCode());
|
Chris@18
|
78 }
|
Chris@18
|
79
|
Chris@18
|
80 /**
|
Chris@18
|
81 * Ensure deep nested include works on multi target entity type field.
|
Chris@18
|
82 *
|
Chris@18
|
83 * @see https://www.drupal.org/project/jsonapi/issues/2973681
|
Chris@18
|
84 */
|
Chris@18
|
85 public function testDeepNestedIncludeMultiTargetEntityTypeFieldFromIssue2973681() {
|
Chris@18
|
86 // Set up data model.
|
Chris@18
|
87 $this->assertTrue($this->container->get('module_installer')->install(['comment'], TRUE), 'Installed modules.');
|
Chris@18
|
88 $this->addDefaultCommentField('node', 'article');
|
Chris@18
|
89 $this->addDefaultCommentField('taxonomy_term', 'tags', 'comment', CommentItemInterface::OPEN, 'tcomment');
|
Chris@18
|
90 $this->drupalCreateContentType(['type' => 'page']);
|
Chris@18
|
91 $this->createEntityReferenceField(
|
Chris@18
|
92 'node',
|
Chris@18
|
93 'page',
|
Chris@18
|
94 'field_comment',
|
Chris@18
|
95 NULL,
|
Chris@18
|
96 'comment',
|
Chris@18
|
97 'default',
|
Chris@18
|
98 [
|
Chris@18
|
99 'target_bundles' => [
|
Chris@18
|
100 'comment' => 'comment',
|
Chris@18
|
101 'tcomment' => 'tcomment',
|
Chris@18
|
102 ],
|
Chris@18
|
103 ],
|
Chris@18
|
104 FieldStorageDefinitionInterface::CARDINALITY_UNLIMITED
|
Chris@18
|
105 );
|
Chris@18
|
106 $this->rebuildAll();
|
Chris@18
|
107
|
Chris@18
|
108 // Create data.
|
Chris@18
|
109 $node = Node::create([
|
Chris@18
|
110 'title' => 'test article',
|
Chris@18
|
111 'type' => 'article',
|
Chris@18
|
112 ]);
|
Chris@18
|
113 $node->save();
|
Chris@18
|
114 $comment = Comment::create([
|
Chris@18
|
115 'subject' => 'Llama',
|
Chris@18
|
116 'entity_id' => 1,
|
Chris@18
|
117 'entity_type' => 'node',
|
Chris@18
|
118 'field_name' => 'comment',
|
Chris@18
|
119 ]);
|
Chris@18
|
120 $comment->save();
|
Chris@18
|
121 $page = Node::create([
|
Chris@18
|
122 'title' => 'test node',
|
Chris@18
|
123 'type' => 'page',
|
Chris@18
|
124 'field_comment' => [
|
Chris@18
|
125 'entity' => $comment,
|
Chris@18
|
126 ],
|
Chris@18
|
127 ]);
|
Chris@18
|
128 $page->save();
|
Chris@18
|
129
|
Chris@18
|
130 // Test.
|
Chris@18
|
131 $user = $this->drupalCreateUser([
|
Chris@18
|
132 'access content',
|
Chris@18
|
133 'access comments',
|
Chris@18
|
134 ]);
|
Chris@18
|
135 $response = $this->request('GET', Url::fromUri('internal:/jsonapi/node/page?include=field_comment,field_comment.entity_id,field_comment.entity_id.uid'), [
|
Chris@18
|
136 RequestOptions::AUTH => [
|
Chris@18
|
137 $user->getUsername(),
|
Chris@18
|
138 $user->pass_raw,
|
Chris@18
|
139 ],
|
Chris@18
|
140 ]);
|
Chris@18
|
141 $this->assertSame(200, $response->getStatusCode());
|
Chris@18
|
142 }
|
Chris@18
|
143
|
Chris@18
|
144 /**
|
Chris@18
|
145 * Ensure POST and PATCH works for bundle-less relationship routes.
|
Chris@18
|
146 *
|
Chris@18
|
147 * @see https://www.drupal.org/project/jsonapi/issues/2976371
|
Chris@18
|
148 */
|
Chris@18
|
149 public function testBundlelessRelationshipMutationFromIssue2973681() {
|
Chris@18
|
150 $this->config('jsonapi.settings')->set('read_only', FALSE)->save(TRUE);
|
Chris@18
|
151
|
Chris@18
|
152 // Set up data model.
|
Chris@18
|
153 $this->drupalCreateContentType(['type' => 'page']);
|
Chris@18
|
154 $this->createEntityReferenceField(
|
Chris@18
|
155 'node',
|
Chris@18
|
156 'page',
|
Chris@18
|
157 'field_test',
|
Chris@18
|
158 NULL,
|
Chris@18
|
159 'user',
|
Chris@18
|
160 'default',
|
Chris@18
|
161 [
|
Chris@18
|
162 'target_bundles' => NULL,
|
Chris@18
|
163 ],
|
Chris@18
|
164 FieldStorageDefinitionInterface::CARDINALITY_UNLIMITED
|
Chris@18
|
165 );
|
Chris@18
|
166 $this->rebuildAll();
|
Chris@18
|
167
|
Chris@18
|
168 // Create data.
|
Chris@18
|
169 $node = Node::create([
|
Chris@18
|
170 'title' => 'test article',
|
Chris@18
|
171 'type' => 'page',
|
Chris@18
|
172 ]);
|
Chris@18
|
173 $node->save();
|
Chris@18
|
174 $target = $this->createUser();
|
Chris@18
|
175
|
Chris@18
|
176 // Test.
|
Chris@18
|
177 $user = $this->drupalCreateUser(['bypass node access']);
|
Chris@18
|
178 $url = Url::fromRoute('jsonapi.node--page.field_test.relationship.post', ['entity' => $node->uuid()]);
|
Chris@18
|
179 $request_options = [
|
Chris@18
|
180 RequestOptions::HEADERS => [
|
Chris@18
|
181 'Content-Type' => 'application/vnd.api+json',
|
Chris@18
|
182 'Accept' => 'application/vnd.api+json',
|
Chris@18
|
183 ],
|
Chris@18
|
184 RequestOptions::AUTH => [$user->getUsername(), $user->pass_raw],
|
Chris@18
|
185 RequestOptions::JSON => [
|
Chris@18
|
186 'data' => [
|
Chris@18
|
187 ['type' => 'user--user', 'id' => $target->uuid()],
|
Chris@18
|
188 ],
|
Chris@18
|
189 ],
|
Chris@18
|
190 ];
|
Chris@18
|
191 $response = $this->request('POST', $url, $request_options);
|
Chris@18
|
192 $this->assertSame(204, $response->getStatusCode(), (string) $response->getBody());
|
Chris@18
|
193 }
|
Chris@18
|
194
|
Chris@18
|
195 /**
|
Chris@18
|
196 * Ensures GETting terms works when multiple vocabularies exist.
|
Chris@18
|
197 *
|
Chris@18
|
198 * @see https://www.drupal.org/project/jsonapi/issues/2977879
|
Chris@18
|
199 */
|
Chris@18
|
200 public function testGetTermWhenMultipleVocabulariesExistFromIssue2977879() {
|
Chris@18
|
201 // Set up data model.
|
Chris@18
|
202 $this->assertTrue($this->container->get('module_installer')->install(['taxonomy'], TRUE), 'Installed modules.');
|
Chris@18
|
203 Vocabulary::create([
|
Chris@18
|
204 'name' => 'one',
|
Chris@18
|
205 'vid' => 'one',
|
Chris@18
|
206 ])->save();
|
Chris@18
|
207 Vocabulary::create([
|
Chris@18
|
208 'name' => 'two',
|
Chris@18
|
209 'vid' => 'two',
|
Chris@18
|
210 ])->save();
|
Chris@18
|
211 $this->rebuildAll();
|
Chris@18
|
212
|
Chris@18
|
213 // Create data.
|
Chris@18
|
214 Term::create(['vid' => 'one'])
|
Chris@18
|
215 ->setName('Test')
|
Chris@18
|
216 ->save();
|
Chris@18
|
217
|
Chris@18
|
218 // Test.
|
Chris@18
|
219 $user = $this->drupalCreateUser([
|
Chris@18
|
220 'access content',
|
Chris@18
|
221 ]);
|
Chris@18
|
222 $response = $this->request('GET', Url::fromUri('internal:/jsonapi/taxonomy_term/one'), [
|
Chris@18
|
223 RequestOptions::AUTH => [
|
Chris@18
|
224 $user->getUsername(),
|
Chris@18
|
225 $user->pass_raw,
|
Chris@18
|
226 ],
|
Chris@18
|
227 ]);
|
Chris@18
|
228 $this->assertSame(200, $response->getStatusCode());
|
Chris@18
|
229 }
|
Chris@18
|
230
|
Chris@18
|
231 /**
|
Chris@18
|
232 * Cannot PATCH an entity with dangling references in an ER field.
|
Chris@18
|
233 *
|
Chris@18
|
234 * @see https://www.drupal.org/project/jsonapi/issues/2968972
|
Chris@18
|
235 */
|
Chris@18
|
236 public function testDanglingReferencesInAnEntityReferenceFieldFromIssue2968972() {
|
Chris@18
|
237 $this->config('jsonapi.settings')->set('read_only', FALSE)->save(TRUE);
|
Chris@18
|
238
|
Chris@18
|
239 // Set up data model.
|
Chris@18
|
240 $this->drupalCreateContentType(['type' => 'journal_issue']);
|
Chris@18
|
241 $this->drupalCreateContentType(['type' => 'journal_article']);
|
Chris@18
|
242 $this->createEntityReferenceField(
|
Chris@18
|
243 'node',
|
Chris@18
|
244 'journal_article',
|
Chris@18
|
245 'field_issue',
|
Chris@18
|
246 NULL,
|
Chris@18
|
247 'node',
|
Chris@18
|
248 'default',
|
Chris@18
|
249 [
|
Chris@18
|
250 'target_bundles' => [
|
Chris@18
|
251 'journal_issue' => 'journal_issue',
|
Chris@18
|
252 ],
|
Chris@18
|
253 ],
|
Chris@18
|
254 FieldStorageDefinitionInterface::CARDINALITY_UNLIMITED
|
Chris@18
|
255 );
|
Chris@18
|
256 $this->rebuildAll();
|
Chris@18
|
257
|
Chris@18
|
258 // Create data.
|
Chris@18
|
259 $issue_node = Node::create([
|
Chris@18
|
260 'title' => 'Test Journal Issue',
|
Chris@18
|
261 'type' => 'journal_issue',
|
Chris@18
|
262 ]);
|
Chris@18
|
263 $issue_node->save();
|
Chris@18
|
264
|
Chris@18
|
265 $user = $this->drupalCreateUser([
|
Chris@18
|
266 'access content',
|
Chris@18
|
267 'edit own journal_article content',
|
Chris@18
|
268 ]);
|
Chris@18
|
269 $article_node = Node::create([
|
Chris@18
|
270 'title' => 'Test Journal Article',
|
Chris@18
|
271 'type' => 'journal_article',
|
Chris@18
|
272 'field_issue' => [
|
Chris@18
|
273 'target_id' => $issue_node->id(),
|
Chris@18
|
274 ],
|
Chris@18
|
275 ]);
|
Chris@18
|
276 $article_node->setOwner($user);
|
Chris@18
|
277 $article_node->save();
|
Chris@18
|
278
|
Chris@18
|
279 // Test.
|
Chris@18
|
280 $url = Url::fromUri(sprintf('internal:/jsonapi/node/journal_article/%s', $article_node->uuid()));
|
Chris@18
|
281 $request_options = [
|
Chris@18
|
282 RequestOptions::HEADERS => [
|
Chris@18
|
283 'Content-Type' => 'application/vnd.api+json',
|
Chris@18
|
284 'Accept' => 'application/vnd.api+json',
|
Chris@18
|
285 ],
|
Chris@18
|
286 RequestOptions::AUTH => [$user->getUsername(), $user->pass_raw],
|
Chris@18
|
287 RequestOptions::JSON => [
|
Chris@18
|
288 'data' => [
|
Chris@18
|
289 'type' => 'node--journal_article',
|
Chris@18
|
290 'id' => $article_node->uuid(),
|
Chris@18
|
291 'attributes' => [
|
Chris@18
|
292 'title' => 'My New Article Title',
|
Chris@18
|
293 ],
|
Chris@18
|
294 ],
|
Chris@18
|
295 ],
|
Chris@18
|
296 ];
|
Chris@18
|
297 $issue_node->delete();
|
Chris@18
|
298 $response = $this->request('PATCH', $url, $request_options);
|
Chris@18
|
299 $this->assertSame(200, $response->getStatusCode(), (string) $response->getBody());
|
Chris@18
|
300 }
|
Chris@18
|
301
|
Chris@18
|
302 /**
|
Chris@18
|
303 * Ensures GETting node collection + hook_node_grants() implementations works.
|
Chris@18
|
304 *
|
Chris@18
|
305 * @see https://www.drupal.org/project/jsonapi/issues/2984964
|
Chris@18
|
306 */
|
Chris@18
|
307 public function testGetNodeCollectionWithHookNodeGrantsImplementationsFromIssue2984964() {
|
Chris@18
|
308 // Set up data model.
|
Chris@18
|
309 $this->assertTrue($this->container->get('module_installer')->install(['node_access_test'], TRUE), 'Installed modules.');
|
Chris@18
|
310 node_access_rebuild();
|
Chris@18
|
311 $this->rebuildAll();
|
Chris@18
|
312
|
Chris@18
|
313 // Create data.
|
Chris@18
|
314 Node::create([
|
Chris@18
|
315 'title' => 'test article',
|
Chris@18
|
316 'type' => 'article',
|
Chris@18
|
317 ])->save();
|
Chris@18
|
318
|
Chris@18
|
319 // Test.
|
Chris@18
|
320 $user = $this->drupalCreateUser([
|
Chris@18
|
321 'access content',
|
Chris@18
|
322 ]);
|
Chris@18
|
323 $response = $this->request('GET', Url::fromUri('internal:/jsonapi/node/article'), [
|
Chris@18
|
324 RequestOptions::AUTH => [
|
Chris@18
|
325 $user->getUsername(),
|
Chris@18
|
326 $user->pass_raw,
|
Chris@18
|
327 ],
|
Chris@18
|
328 ]);
|
Chris@18
|
329 $this->assertSame(200, $response->getStatusCode());
|
Chris@18
|
330 $this->assertTrue(in_array('user.node_grants:view', explode(' ', $response->getHeader('X-Drupal-Cache-Contexts')[0]), TRUE));
|
Chris@18
|
331 }
|
Chris@18
|
332
|
Chris@18
|
333 /**
|
Chris@18
|
334 * Cannot GET an entity with dangling references in an ER field.
|
Chris@18
|
335 *
|
Chris@18
|
336 * @see https://www.drupal.org/project/jsonapi/issues/2984647
|
Chris@18
|
337 */
|
Chris@18
|
338 public function testDanglingReferencesInAnEntityReferenceFieldFromIssue2984647() {
|
Chris@18
|
339 // Set up data model.
|
Chris@18
|
340 $this->drupalCreateContentType(['type' => 'journal_issue']);
|
Chris@18
|
341 $this->drupalCreateContentType(['type' => 'journal_conference']);
|
Chris@18
|
342 $this->drupalCreateContentType(['type' => 'journal_article']);
|
Chris@18
|
343 $this->createEntityReferenceField(
|
Chris@18
|
344 'node',
|
Chris@18
|
345 'journal_article',
|
Chris@18
|
346 'field_issue',
|
Chris@18
|
347 NULL,
|
Chris@18
|
348 'node',
|
Chris@18
|
349 'default',
|
Chris@18
|
350 [
|
Chris@18
|
351 'target_bundles' => [
|
Chris@18
|
352 'journal_issue' => 'journal_issue',
|
Chris@18
|
353 ],
|
Chris@18
|
354 ],
|
Chris@18
|
355 FieldStorageDefinitionInterface::CARDINALITY_UNLIMITED
|
Chris@18
|
356 );
|
Chris@18
|
357 $this->createEntityReferenceField(
|
Chris@18
|
358 'node',
|
Chris@18
|
359 'journal_article',
|
Chris@18
|
360 'field_mentioned_in',
|
Chris@18
|
361 NULL,
|
Chris@18
|
362 'node',
|
Chris@18
|
363 'default',
|
Chris@18
|
364 [
|
Chris@18
|
365 'target_bundles' => [
|
Chris@18
|
366 'journal_issue' => 'journal_issue',
|
Chris@18
|
367 'journal_conference' => 'journal_conference',
|
Chris@18
|
368 ],
|
Chris@18
|
369 ],
|
Chris@18
|
370 FieldStorageDefinitionInterface::CARDINALITY_UNLIMITED
|
Chris@18
|
371 );
|
Chris@18
|
372 $this->rebuildAll();
|
Chris@18
|
373
|
Chris@18
|
374 // Create data.
|
Chris@18
|
375 $issue_node = Node::create([
|
Chris@18
|
376 'title' => 'Test Journal Issue',
|
Chris@18
|
377 'type' => 'journal_issue',
|
Chris@18
|
378 ]);
|
Chris@18
|
379 $issue_node->save();
|
Chris@18
|
380 $conference_node = Node::create([
|
Chris@18
|
381 'title' => 'First Journal Conference!',
|
Chris@18
|
382 'type' => 'journal_conference',
|
Chris@18
|
383 ]);
|
Chris@18
|
384 $conference_node->save();
|
Chris@18
|
385
|
Chris@18
|
386 $user = $this->drupalCreateUser([
|
Chris@18
|
387 'access content',
|
Chris@18
|
388 'edit own journal_article content',
|
Chris@18
|
389 ]);
|
Chris@18
|
390 $article_node = Node::create([
|
Chris@18
|
391 'title' => 'Test Journal Article',
|
Chris@18
|
392 'type' => 'journal_article',
|
Chris@18
|
393 'field_issue' => [
|
Chris@18
|
394 ['target_id' => $issue_node->id()],
|
Chris@18
|
395 ],
|
Chris@18
|
396 'field_mentioned_in' => [
|
Chris@18
|
397 ['target_id' => $issue_node->id()],
|
Chris@18
|
398 ['target_id' => $conference_node->id()],
|
Chris@18
|
399 ],
|
Chris@18
|
400 ]);
|
Chris@18
|
401 $article_node->setOwner($user);
|
Chris@18
|
402 $article_node->save();
|
Chris@18
|
403
|
Chris@18
|
404 // Test.
|
Chris@18
|
405 $url = Url::fromUri(sprintf('internal:/jsonapi/node/journal_article/%s', $article_node->uuid()));
|
Chris@18
|
406 $request_options = [
|
Chris@18
|
407 RequestOptions::HEADERS => [
|
Chris@18
|
408 'Content-Type' => 'application/vnd.api+json',
|
Chris@18
|
409 'Accept' => 'application/vnd.api+json',
|
Chris@18
|
410 ],
|
Chris@18
|
411 RequestOptions::AUTH => [$user->getUsername(), $user->pass_raw],
|
Chris@18
|
412 ];
|
Chris@18
|
413 $issue_node->delete();
|
Chris@18
|
414 $response = $this->request('GET', $url, $request_options);
|
Chris@18
|
415 $this->assertSame(200, $response->getStatusCode());
|
Chris@18
|
416
|
Chris@18
|
417 // Entity reference field allowing a single bundle: dangling reference's
|
Chris@18
|
418 // resource type is deduced.
|
Chris@18
|
419 $this->assertSame([
|
Chris@18
|
420 [
|
Chris@18
|
421 'type' => 'node--journal_issue',
|
Chris@18
|
422 'id' => 'missing',
|
Chris@18
|
423 'meta' => [
|
Chris@18
|
424 'links' => [
|
Chris@18
|
425 'help' => [
|
Chris@18
|
426 'href' => 'https://www.drupal.org/docs/8/modules/json-api/core-concepts#missing',
|
Chris@18
|
427 'meta' => [
|
Chris@18
|
428 'about' => "Usage and meaning of the 'missing' resource identifier.",
|
Chris@18
|
429 ],
|
Chris@18
|
430 ],
|
Chris@18
|
431 ],
|
Chris@18
|
432 ],
|
Chris@18
|
433 ],
|
Chris@18
|
434 ], Json::decode((string) $response->getBody())['data']['relationships']['field_issue']['data']);
|
Chris@18
|
435
|
Chris@18
|
436 // Entity reference field allowing multiple bundles: dangling reference's
|
Chris@18
|
437 // resource type is NOT deduced.
|
Chris@18
|
438 $this->assertSame([
|
Chris@18
|
439 [
|
Chris@18
|
440 'type' => 'unknown',
|
Chris@18
|
441 'id' => 'missing',
|
Chris@18
|
442 'meta' => [
|
Chris@18
|
443 'links' => [
|
Chris@18
|
444 'help' => [
|
Chris@18
|
445 'href' => 'https://www.drupal.org/docs/8/modules/json-api/core-concepts#missing',
|
Chris@18
|
446 'meta' => [
|
Chris@18
|
447 'about' => "Usage and meaning of the 'missing' resource identifier.",
|
Chris@18
|
448 ],
|
Chris@18
|
449 ],
|
Chris@18
|
450 ],
|
Chris@18
|
451 ],
|
Chris@18
|
452 ],
|
Chris@18
|
453 [
|
Chris@18
|
454 'type' => 'node--journal_conference',
|
Chris@18
|
455 'id' => $conference_node->uuid(),
|
Chris@18
|
456 ],
|
Chris@18
|
457 ], Json::decode((string) $response->getBody())['data']['relationships']['field_mentioned_in']['data']);
|
Chris@18
|
458 }
|
Chris@18
|
459
|
Chris@18
|
460 /**
|
Chris@18
|
461 * Ensures that JSON:API routes are caches are dynamically rebuilt.
|
Chris@18
|
462 *
|
Chris@18
|
463 * Adding a new relationship field should cause new routes to be immediately
|
Chris@18
|
464 * regenerated. The site builder should not need to manually rebuild caches.
|
Chris@18
|
465 *
|
Chris@18
|
466 * @see https://www.drupal.org/project/jsonapi/issues/2984886
|
Chris@18
|
467 */
|
Chris@18
|
468 public function testThatRoutesAreRebuiltAfterDataModelChangesFromIssue2984886() {
|
Chris@18
|
469 $user = $this->drupalCreateUser(['access content']);
|
Chris@18
|
470 $request_options = [
|
Chris@18
|
471 RequestOptions::AUTH => [
|
Chris@18
|
472 $user->getUsername(),
|
Chris@18
|
473 $user->pass_raw,
|
Chris@18
|
474 ],
|
Chris@18
|
475 ];
|
Chris@18
|
476
|
Chris@18
|
477 $response = $this->request('GET', Url::fromUri('internal:/jsonapi/node/dog'), $request_options);
|
Chris@18
|
478 $this->assertSame(404, $response->getStatusCode());
|
Chris@18
|
479
|
Chris@18
|
480 $node_type_dog = NodeType::create(['type' => 'dog']);
|
Chris@18
|
481 $node_type_dog->save();
|
Chris@18
|
482 NodeType::create(['type' => 'cat'])->save();
|
Chris@18
|
483 \Drupal::service('router.builder')->rebuildIfNeeded();
|
Chris@18
|
484
|
Chris@18
|
485 $response = $this->request('GET', Url::fromUri('internal:/jsonapi/node/dog'), $request_options);
|
Chris@18
|
486 $this->assertSame(200, $response->getStatusCode());
|
Chris@18
|
487
|
Chris@18
|
488 $this->createEntityReferenceField('node', 'dog', 'field_test', NULL, 'node');
|
Chris@18
|
489 \Drupal::service('router.builder')->rebuildIfNeeded();
|
Chris@18
|
490
|
Chris@18
|
491 $dog = Node::create(['type' => 'dog', 'title' => 'Rosie P. Mosie']);
|
Chris@18
|
492 $dog->save();
|
Chris@18
|
493
|
Chris@18
|
494 $response = $this->request('GET', Url::fromUri('internal:/jsonapi/node/dog/' . $dog->uuid() . '/field_test'), $request_options);
|
Chris@18
|
495 $this->assertSame(200, $response->getStatusCode());
|
Chris@18
|
496
|
Chris@18
|
497 $this->createEntityReferenceField('node', 'cat', 'field_test', NULL, 'node');
|
Chris@18
|
498 \Drupal::service('router.builder')->rebuildIfNeeded();
|
Chris@18
|
499
|
Chris@18
|
500 $cat = Node::create(['type' => 'cat', 'title' => 'E. Napoleon']);
|
Chris@18
|
501 $cat->save();
|
Chris@18
|
502
|
Chris@18
|
503 $response = $this->request('GET', Url::fromUri('internal:/jsonapi/node/cat/' . $cat->uuid() . '/field_test'), $request_options);
|
Chris@18
|
504 $this->assertSame(200, $response->getStatusCode());
|
Chris@18
|
505
|
Chris@18
|
506 FieldConfig::loadByName('node', 'cat', 'field_test')->delete();
|
Chris@18
|
507 \Drupal::service('router.builder')->rebuildIfNeeded();
|
Chris@18
|
508
|
Chris@18
|
509 $response = $this->request('GET', Url::fromUri('internal:/jsonapi/node/cat/' . $cat->uuid() . '/field_test'), $request_options);
|
Chris@18
|
510 $this->assertSame(404, $response->getStatusCode());
|
Chris@18
|
511
|
Chris@18
|
512 $node_type_dog->delete();
|
Chris@18
|
513 \Drupal::service('router.builder')->rebuildIfNeeded();
|
Chris@18
|
514
|
Chris@18
|
515 $response = $this->request('GET', Url::fromUri('internal:/jsonapi/node/dog'), $request_options);
|
Chris@18
|
516 $this->assertSame(404, $response->getStatusCode());
|
Chris@18
|
517 }
|
Chris@18
|
518
|
Chris@18
|
519 /**
|
Chris@18
|
520 * Ensures denormalizing relationships with aliased field names works.
|
Chris@18
|
521 *
|
Chris@18
|
522 * @see https://www.drupal.org/project/jsonapi/issues/3007113
|
Chris@18
|
523 * @see https://www.drupal.org/project/jsonapi_extras/issues/3004582#comment-12817261
|
Chris@18
|
524 */
|
Chris@18
|
525 public function testDenormalizeAliasedRelationshipFromIssue2953207() {
|
Chris@18
|
526 $this->config('jsonapi.settings')->set('read_only', FALSE)->save(TRUE);
|
Chris@18
|
527
|
Chris@18
|
528 // Since the JSON:API module does not have an explicit mechanism to set up
|
Chris@18
|
529 // field aliases, create a strange data model so that automatic aliasing
|
Chris@18
|
530 // allows us to test aliased relationships.
|
Chris@18
|
531 // @see \Drupal\jsonapi\ResourceType\ResourceTypeRepository::getFieldMapping()
|
Chris@18
|
532 $internal_relationship_field_name = 'type';
|
Chris@18
|
533 $public_relationship_field_name = 'taxonomy_term_' . $internal_relationship_field_name;
|
Chris@18
|
534
|
Chris@18
|
535 // Set up data model.
|
Chris@18
|
536 $this->createEntityReferenceField(
|
Chris@18
|
537 'taxonomy_term',
|
Chris@18
|
538 'tags',
|
Chris@18
|
539 $internal_relationship_field_name,
|
Chris@18
|
540 NULL,
|
Chris@18
|
541 'user'
|
Chris@18
|
542 );
|
Chris@18
|
543 $this->rebuildAll();
|
Chris@18
|
544
|
Chris@18
|
545 // Create data.
|
Chris@18
|
546 Term::create([
|
Chris@18
|
547 'name' => 'foobar',
|
Chris@18
|
548 'vid' => 'tags',
|
Chris@18
|
549 'type' => ['target_id' => 1],
|
Chris@18
|
550 ])->save();
|
Chris@18
|
551
|
Chris@18
|
552 // Test.
|
Chris@18
|
553 $user = $this->drupalCreateUser([
|
Chris@18
|
554 'edit terms in tags',
|
Chris@18
|
555 ]);
|
Chris@18
|
556 $body = [
|
Chris@18
|
557 'data' => [
|
Chris@18
|
558 'type' => 'user--user',
|
Chris@18
|
559 'id' => User::load(0)->uuid(),
|
Chris@18
|
560 ],
|
Chris@18
|
561 ];
|
Chris@18
|
562
|
Chris@18
|
563 // Test.
|
Chris@18
|
564 $response = $this->request('PATCH', Url::fromUri(sprintf('internal:/jsonapi/taxonomy_term/tags/%s/relationships/%s', Term::load(1)->uuid(), $public_relationship_field_name)), [
|
Chris@18
|
565 RequestOptions::AUTH => [
|
Chris@18
|
566 $user->getUsername(),
|
Chris@18
|
567 $user->pass_raw,
|
Chris@18
|
568 ],
|
Chris@18
|
569 RequestOptions::HEADERS => [
|
Chris@18
|
570 'Content-Type' => 'application/vnd.api+json',
|
Chris@18
|
571 ],
|
Chris@18
|
572 RequestOptions::BODY => Json::encode($body),
|
Chris@18
|
573 ]);
|
Chris@18
|
574 $this->assertSame(204, $response->getStatusCode());
|
Chris@18
|
575 }
|
Chris@18
|
576
|
Chris@18
|
577 /**
|
Chris@18
|
578 * Ensures that Drupal's page cache is effective.
|
Chris@18
|
579 *
|
Chris@18
|
580 * @see https://www.drupal.org/project/jsonapi/issues/3009596
|
Chris@18
|
581 */
|
Chris@18
|
582 public function testPageCacheFromIssue3009596() {
|
Chris@18
|
583 $anonymous_role = Role::load(RoleInterface::ANONYMOUS_ID);
|
Chris@18
|
584 $anonymous_role->grantPermission('access content');
|
Chris@18
|
585 $anonymous_role->trustData()->save();
|
Chris@18
|
586
|
Chris@18
|
587 NodeType::create(['type' => 'emu_fact'])->save();
|
Chris@18
|
588 \Drupal::service('router.builder')->rebuildIfNeeded();
|
Chris@18
|
589
|
Chris@18
|
590 $node = Node::create([
|
Chris@18
|
591 'type' => 'emu_fact',
|
Chris@18
|
592 'title' => "Emus don't say moo!",
|
Chris@18
|
593 ]);
|
Chris@18
|
594 $node->save();
|
Chris@18
|
595
|
Chris@18
|
596 $request_options = [
|
Chris@18
|
597 RequestOptions::HEADERS => ['Accept' => 'application/vnd.api+json'],
|
Chris@18
|
598 ];
|
Chris@18
|
599 $node_url = Url::fromUri('internal:/jsonapi/node/emu_fact/' . $node->uuid());
|
Chris@18
|
600
|
Chris@18
|
601 // The first request should be a cache MISS.
|
Chris@18
|
602 $response = $this->request('GET', $node_url, $request_options);
|
Chris@18
|
603 $this->assertSame(200, $response->getStatusCode());
|
Chris@18
|
604 $this->assertSame('MISS', $response->getHeader('X-Drupal-Cache')[0]);
|
Chris@18
|
605
|
Chris@18
|
606 // The second request should be a cache HIT.
|
Chris@18
|
607 $response = $this->request('GET', $node_url, $request_options);
|
Chris@18
|
608 $this->assertSame(200, $response->getStatusCode());
|
Chris@18
|
609 $this->assertSame('HIT', $response->getHeader('X-Drupal-Cache')[0]);
|
Chris@18
|
610 }
|
Chris@18
|
611
|
Chris@18
|
612 /**
|
Chris@18
|
613 * Ensures that filtering by a sequential internal ID named 'id' is possible.
|
Chris@18
|
614 *
|
Chris@18
|
615 * @see https://www.drupal.org/project/jsonapi/issues/3015759
|
Chris@18
|
616 */
|
Chris@18
|
617 public function testFilterByIdFromIssue3015759() {
|
Chris@18
|
618 // Set up data model.
|
Chris@18
|
619 $this->assertTrue($this->container->get('module_installer')->install(['shortcut'], TRUE), 'Installed modules.');
|
Chris@18
|
620 $this->rebuildAll();
|
Chris@18
|
621
|
Chris@18
|
622 // Create data.
|
Chris@18
|
623 $shortcut = Shortcut::create([
|
Chris@18
|
624 'shortcut_set' => 'default',
|
Chris@18
|
625 'title' => $this->randomMachineName(),
|
Chris@18
|
626 'weight' => -20,
|
Chris@18
|
627 'link' => [
|
Chris@18
|
628 'uri' => 'internal:/user/logout',
|
Chris@18
|
629 ],
|
Chris@18
|
630 ]);
|
Chris@18
|
631 $shortcut->save();
|
Chris@18
|
632
|
Chris@18
|
633 // Test.
|
Chris@18
|
634 $user = $this->drupalCreateUser([
|
Chris@18
|
635 'access shortcuts',
|
Chris@18
|
636 'customize shortcut links',
|
Chris@18
|
637 ]);
|
Chris@18
|
638 $response = $this->request('GET', Url::fromUri('internal:/jsonapi/shortcut/default?filter[drupal_internal__id]=' . $shortcut->id()), [
|
Chris@18
|
639 RequestOptions::AUTH => [
|
Chris@18
|
640 $user->getUsername(),
|
Chris@18
|
641 $user->pass_raw,
|
Chris@18
|
642 ],
|
Chris@18
|
643 ]);
|
Chris@18
|
644 $this->assertSame(200, $response->getStatusCode());
|
Chris@18
|
645 $doc = Json::decode((string) $response->getBody());
|
Chris@18
|
646 $this->assertNotEmpty($doc['data']);
|
Chris@18
|
647 $this->assertSame($doc['data'][0]['id'], $shortcut->uuid());
|
Chris@18
|
648 $this->assertSame($doc['data'][0]['attributes']['drupal_internal__id'], (int) $shortcut->id());
|
Chris@18
|
649 $this->assertSame($doc['data'][0]['attributes']['title'], $shortcut->label());
|
Chris@18
|
650 }
|
Chris@18
|
651
|
Chris@18
|
652 /**
|
Chris@18
|
653 * Ensures datetime fields are normalized using the correct timezone.
|
Chris@18
|
654 *
|
Chris@18
|
655 * @see https://www.drupal.org/project/jsonapi/issues/2999438
|
Chris@18
|
656 */
|
Chris@18
|
657 public function testPatchingDateTimeNormalizedWrongTimeZoneIssue3021194() {
|
Chris@18
|
658 // Set up data model.
|
Chris@18
|
659 $this->assertTrue($this->container->get('module_installer')->install(['datetime'], TRUE), 'Installed modules.');
|
Chris@18
|
660 $this->drupalCreateContentType(['type' => 'page']);
|
Chris@18
|
661 $this->rebuildAll();
|
Chris@18
|
662 FieldStorageConfig::create([
|
Chris@18
|
663 'field_name' => 'when',
|
Chris@18
|
664 'type' => 'datetime',
|
Chris@18
|
665 'entity_type' => 'node',
|
Chris@18
|
666 'settings' => ['datetime_type' => DateTimeItem::DATETIME_TYPE_DATETIME],
|
Chris@18
|
667 ])
|
Chris@18
|
668 ->save();
|
Chris@18
|
669 FieldConfig::create([
|
Chris@18
|
670 'field_name' => 'when',
|
Chris@18
|
671 'entity_type' => 'node',
|
Chris@18
|
672 'bundle' => 'page',
|
Chris@18
|
673 ])
|
Chris@18
|
674 ->save();
|
Chris@18
|
675
|
Chris@18
|
676 // Create data.
|
Chris@18
|
677 $page = Node::create([
|
Chris@18
|
678 'title' => 'Stegosaurus',
|
Chris@18
|
679 'type' => 'page',
|
Chris@18
|
680 'when' => [
|
Chris@18
|
681 'value' => '2018-09-16T12:00:00',
|
Chris@18
|
682 ],
|
Chris@18
|
683 ]);
|
Chris@18
|
684 $page->save();
|
Chris@18
|
685
|
Chris@18
|
686 // Test.
|
Chris@18
|
687 $user = $this->drupalCreateUser([
|
Chris@18
|
688 'access content',
|
Chris@18
|
689 ]);
|
Chris@18
|
690 $response = $this->request('GET', Url::fromUri('internal:/jsonapi/node/page/' . $page->uuid()), [
|
Chris@18
|
691 RequestOptions::AUTH => [
|
Chris@18
|
692 $user->getUsername(),
|
Chris@18
|
693 $user->pass_raw,
|
Chris@18
|
694 ],
|
Chris@18
|
695 ]);
|
Chris@18
|
696 $this->assertSame(200, $response->getStatusCode());
|
Chris@18
|
697 $doc = Json::decode((string) $response->getBody());
|
Chris@18
|
698 $this->assertSame('2018-09-16T22:00:00+10:00', $doc['data']['attributes']['when']);
|
Chris@18
|
699 }
|
Chris@18
|
700
|
Chris@18
|
701 /**
|
Chris@18
|
702 * Ensures PATCHing datetime (both date-only & date+time) fields is possible.
|
Chris@18
|
703 *
|
Chris@18
|
704 * @see https://www.drupal.org/project/jsonapi/issues/3021194
|
Chris@18
|
705 */
|
Chris@18
|
706 public function testPatchingDateTimeFieldsFromIssue3021194() {
|
Chris@18
|
707 $this->config('jsonapi.settings')->set('read_only', FALSE)->save(TRUE);
|
Chris@18
|
708
|
Chris@18
|
709 // Set up data model.
|
Chris@18
|
710 $this->assertTrue($this->container->get('module_installer')->install(['datetime'], TRUE), 'Installed modules.');
|
Chris@18
|
711 $this->drupalCreateContentType(['type' => 'page']);
|
Chris@18
|
712 $this->rebuildAll();
|
Chris@18
|
713 FieldStorageConfig::create([
|
Chris@18
|
714 'field_name' => 'when',
|
Chris@18
|
715 'type' => 'datetime',
|
Chris@18
|
716 'entity_type' => 'node',
|
Chris@18
|
717 'settings' => ['datetime_type' => DateTimeItem::DATETIME_TYPE_DATE],
|
Chris@18
|
718 ])
|
Chris@18
|
719 ->save();
|
Chris@18
|
720 FieldConfig::create([
|
Chris@18
|
721 'field_name' => 'when',
|
Chris@18
|
722 'entity_type' => 'node',
|
Chris@18
|
723 'bundle' => 'page',
|
Chris@18
|
724 ])
|
Chris@18
|
725 ->save();
|
Chris@18
|
726 FieldStorageConfig::create([
|
Chris@18
|
727 'field_name' => 'when_exactly',
|
Chris@18
|
728 'type' => 'datetime',
|
Chris@18
|
729 'entity_type' => 'node',
|
Chris@18
|
730 'settings' => ['datetime_type' => DateTimeItem::DATETIME_TYPE_DATETIME],
|
Chris@18
|
731 ])
|
Chris@18
|
732 ->save();
|
Chris@18
|
733 FieldConfig::create([
|
Chris@18
|
734 'field_name' => 'when_exactly',
|
Chris@18
|
735 'entity_type' => 'node',
|
Chris@18
|
736 'bundle' => 'page',
|
Chris@18
|
737 ])
|
Chris@18
|
738 ->save();
|
Chris@18
|
739
|
Chris@18
|
740 // Create data.
|
Chris@18
|
741 $page = Node::create([
|
Chris@18
|
742 'title' => 'Stegosaurus',
|
Chris@18
|
743 'type' => 'page',
|
Chris@18
|
744 'when' => [
|
Chris@18
|
745 'value' => '2018-12-19',
|
Chris@18
|
746 ],
|
Chris@18
|
747 'when_exactly' => [
|
Chris@18
|
748 'value' => '2018-12-19T17:00:00',
|
Chris@18
|
749 ],
|
Chris@18
|
750 ]);
|
Chris@18
|
751 $page->save();
|
Chris@18
|
752
|
Chris@18
|
753 // Test.
|
Chris@18
|
754 $user = $this->drupalCreateUser([
|
Chris@18
|
755 'access content',
|
Chris@18
|
756 'edit any page content',
|
Chris@18
|
757 ]);
|
Chris@18
|
758 $request_options = [
|
Chris@18
|
759 RequestOptions::AUTH => [
|
Chris@18
|
760 $user->getUsername(),
|
Chris@18
|
761 $user->pass_raw,
|
Chris@18
|
762 ],
|
Chris@18
|
763 RequestOptions::HEADERS => [
|
Chris@18
|
764 'Content-Type' => 'application/vnd.api+json',
|
Chris@18
|
765 'Accept' => 'application/vnd.api+json',
|
Chris@18
|
766 ],
|
Chris@18
|
767 ];
|
Chris@18
|
768 $node_url = Url::fromUri('internal:/jsonapi/node/page/' . $page->uuid());
|
Chris@18
|
769 $response = $this->request('GET', $node_url, $request_options);
|
Chris@18
|
770 $this->assertSame(200, $response->getStatusCode());
|
Chris@18
|
771 $doc = Json::decode((string) $response->getBody());
|
Chris@18
|
772 $this->assertSame('2018-12-19', $doc['data']['attributes']['when']);
|
Chris@18
|
773 $this->assertSame('2018-12-20T04:00:00+11:00', $doc['data']['attributes']['when_exactly']);
|
Chris@18
|
774 $doc['data']['attributes']['when'] = '2018-12-20';
|
Chris@18
|
775 $doc['data']['attributes']['when_exactly'] = '2018-12-19T19:00:00+01:00';
|
Chris@18
|
776 $request_options = $request_options + [RequestOptions::JSON => $doc];
|
Chris@18
|
777 $response = $this->request('PATCH', $node_url, $request_options);
|
Chris@18
|
778 $this->assertSame(200, $response->getStatusCode());
|
Chris@18
|
779 $doc = Json::decode((string) $response->getBody());
|
Chris@18
|
780 $this->assertSame('2018-12-20', $doc['data']['attributes']['when']);
|
Chris@18
|
781 $this->assertSame('2018-12-20T05:00:00+11:00', $doc['data']['attributes']['when_exactly']);
|
Chris@18
|
782 }
|
Chris@18
|
783
|
Chris@18
|
784 /**
|
Chris@18
|
785 * Ensure includes are respected even when POSTing.
|
Chris@18
|
786 *
|
Chris@18
|
787 * @see https://www.drupal.org/project/jsonapi/issues/3026030
|
Chris@18
|
788 */
|
Chris@18
|
789 public function testPostToIncludeUrlDoesNotReturnIncludeFromIssue3026030() {
|
Chris@18
|
790 $this->config('jsonapi.settings')->set('read_only', FALSE)->save(TRUE);
|
Chris@18
|
791
|
Chris@18
|
792 // Set up data model.
|
Chris@18
|
793 $this->drupalCreateContentType(['type' => 'page']);
|
Chris@18
|
794 $this->rebuildAll();
|
Chris@18
|
795
|
Chris@18
|
796 // Test.
|
Chris@18
|
797 $user = $this->drupalCreateUser(['bypass node access']);
|
Chris@18
|
798 $url = Url::fromUri('internal:/jsonapi/node/page?include=uid');
|
Chris@18
|
799 $request_options = [
|
Chris@18
|
800 RequestOptions::HEADERS => [
|
Chris@18
|
801 'Content-Type' => 'application/vnd.api+json',
|
Chris@18
|
802 'Accept' => 'application/vnd.api+json',
|
Chris@18
|
803 ],
|
Chris@18
|
804 RequestOptions::AUTH => [$user->getUsername(), $user->pass_raw],
|
Chris@18
|
805 RequestOptions::JSON => [
|
Chris@18
|
806 'data' => [
|
Chris@18
|
807 'type' => 'node--page',
|
Chris@18
|
808 'attributes' => [
|
Chris@18
|
809 'title' => 'test',
|
Chris@18
|
810 ],
|
Chris@18
|
811 ],
|
Chris@18
|
812 ],
|
Chris@18
|
813 ];
|
Chris@18
|
814 $response = $this->request('POST', $url, $request_options);
|
Chris@18
|
815 $this->assertSame(201, $response->getStatusCode());
|
Chris@18
|
816 $doc = Json::decode((string) $response->getBody());
|
Chris@18
|
817 $this->assertArrayHasKey('included', $doc);
|
Chris@18
|
818 $this->assertSame($user->label(), $doc['included'][0]['attributes']['name']);
|
Chris@18
|
819 }
|
Chris@18
|
820
|
Chris@18
|
821 /**
|
Chris@18
|
822 * Ensure includes are respected even when PATCHing.
|
Chris@18
|
823 *
|
Chris@18
|
824 * @see https://www.drupal.org/project/jsonapi/issues/3026030
|
Chris@18
|
825 */
|
Chris@18
|
826 public function testPatchToIncludeUrlDoesNotReturnIncludeFromIssue3026030() {
|
Chris@18
|
827 $this->config('jsonapi.settings')->set('read_only', FALSE)->save(TRUE);
|
Chris@18
|
828
|
Chris@18
|
829 // Set up data model.
|
Chris@18
|
830 $this->drupalCreateContentType(['type' => 'page']);
|
Chris@18
|
831 $this->rebuildAll();
|
Chris@18
|
832
|
Chris@18
|
833 // Create data.
|
Chris@18
|
834 $user = $this->drupalCreateUser(['bypass node access']);
|
Chris@18
|
835 $page = Node::create([
|
Chris@18
|
836 'title' => 'original',
|
Chris@18
|
837 'type' => 'page',
|
Chris@18
|
838 'uid' => $user->id(),
|
Chris@18
|
839 ]);
|
Chris@18
|
840 $page->save();
|
Chris@18
|
841
|
Chris@18
|
842 // Test.
|
Chris@18
|
843 $url = Url::fromUri(sprintf('internal:/jsonapi/node/page/%s/?include=uid', $page->uuid()));
|
Chris@18
|
844 $request_options = [
|
Chris@18
|
845 RequestOptions::HEADERS => [
|
Chris@18
|
846 'Content-Type' => 'application/vnd.api+json',
|
Chris@18
|
847 'Accept' => 'application/vnd.api+json',
|
Chris@18
|
848 ],
|
Chris@18
|
849 RequestOptions::AUTH => [$user->getUsername(), $user->pass_raw],
|
Chris@18
|
850 RequestOptions::JSON => [
|
Chris@18
|
851 'data' => [
|
Chris@18
|
852 'type' => 'node--page',
|
Chris@18
|
853 'id' => $page->uuid(),
|
Chris@18
|
854 'attributes' => [
|
Chris@18
|
855 'title' => 'modified',
|
Chris@18
|
856 ],
|
Chris@18
|
857 ],
|
Chris@18
|
858 ],
|
Chris@18
|
859 ];
|
Chris@18
|
860 $response = $this->request('PATCH', $url, $request_options);
|
Chris@18
|
861 $this->assertSame(200, $response->getStatusCode());
|
Chris@18
|
862 $doc = Json::decode((string) $response->getBody());
|
Chris@18
|
863 $this->assertArrayHasKey('included', $doc);
|
Chris@18
|
864 $this->assertSame($user->label(), $doc['included'][0]['attributes']['name']);
|
Chris@18
|
865 }
|
Chris@18
|
866
|
Chris@18
|
867 /**
|
Chris@18
|
868 * Ensure `@FieldType=map` fields are normalized correctly.
|
Chris@18
|
869 *
|
Chris@18
|
870 * @see https://www.drupal.org/project/jsonapi/issues/3040590
|
Chris@18
|
871 */
|
Chris@18
|
872 public function testMapFieldTypeNormalizationFromIssue3040590() {
|
Chris@18
|
873 $this->assertTrue($this->container->get('module_installer')->install(['entity_test'], TRUE), 'Installed modules.');
|
Chris@18
|
874
|
Chris@18
|
875 // Create data.
|
Chris@18
|
876 $entity = EntityTestMapField::create([
|
Chris@18
|
877 'data' => [
|
Chris@18
|
878 'foo' => 'bar',
|
Chris@18
|
879 'baz' => 'qux',
|
Chris@18
|
880 ],
|
Chris@18
|
881 ]);
|
Chris@18
|
882 $entity->save();
|
Chris@18
|
883 $user = $this->drupalCreateUser([
|
Chris@18
|
884 'administer entity_test content',
|
Chris@18
|
885 ]);
|
Chris@18
|
886
|
Chris@18
|
887 // Test.
|
Chris@18
|
888 $url = Url::fromUri(sprintf('internal:/jsonapi/entity_test_map_field/entity_test_map_field', $entity->uuid()));
|
Chris@18
|
889 $request_options = [
|
Chris@18
|
890 RequestOptions::AUTH => [$user->getUsername(), $user->pass_raw],
|
Chris@18
|
891 ];
|
Chris@18
|
892 $response = $this->request('GET', $url, $request_options);
|
Chris@18
|
893 $this->assertSame(200, $response->getStatusCode());
|
Chris@18
|
894 $data = Json::decode((string) $response->getBody());
|
Chris@18
|
895 $this->assertSame([
|
Chris@18
|
896 'foo' => 'bar',
|
Chris@18
|
897 'baz' => 'qux',
|
Chris@18
|
898 ], $data['data'][0]['attributes']['data']);
|
Chris@18
|
899 $entity->set('data', [
|
Chris@18
|
900 'foo' => 'bar',
|
Chris@18
|
901 ])->save();
|
Chris@18
|
902 $response = $this->request('GET', $url, $request_options);
|
Chris@18
|
903 $this->assertSame(200, $response->getStatusCode());
|
Chris@18
|
904 $data = Json::decode((string) $response->getBody());
|
Chris@18
|
905 $this->assertSame(['foo' => 'bar'], $data['data'][0]['attributes']['data']);
|
Chris@18
|
906 }
|
Chris@18
|
907
|
Chris@18
|
908 }
|