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