Chris@18
|
1 <?php
|
Chris@18
|
2
|
Chris@18
|
3 namespace Drupal\Tests\jsonapi\Functional;
|
Chris@18
|
4
|
Chris@18
|
5 use Drupal\Component\Serialization\Json;
|
Chris@18
|
6 use Drupal\Component\Utility\NestedArray;
|
Chris@18
|
7 use Drupal\Core\Url;
|
Chris@18
|
8 use Drupal\file\Entity\File;
|
Chris@18
|
9 use Drupal\Tests\jsonapi\Traits\CommonCollectionFilterAccessTestPatternsTrait;
|
Chris@18
|
10 use Drupal\Tests\rest\Functional\BcTimestampNormalizerUnixTestTrait;
|
Chris@18
|
11 use Drupal\user\Entity\User;
|
Chris@18
|
12 use GuzzleHttp\RequestOptions;
|
Chris@18
|
13
|
Chris@18
|
14 /**
|
Chris@18
|
15 * JSON:API integration test for the "File" content entity type.
|
Chris@18
|
16 *
|
Chris@18
|
17 * @group jsonapi
|
Chris@18
|
18 */
|
Chris@18
|
19 class FileTest extends ResourceTestBase {
|
Chris@18
|
20
|
Chris@18
|
21 use BcTimestampNormalizerUnixTestTrait;
|
Chris@18
|
22 use CommonCollectionFilterAccessTestPatternsTrait;
|
Chris@18
|
23
|
Chris@18
|
24 /**
|
Chris@18
|
25 * {@inheritdoc}
|
Chris@18
|
26 */
|
Chris@18
|
27 public static $modules = ['file', 'user'];
|
Chris@18
|
28
|
Chris@18
|
29 /**
|
Chris@18
|
30 * {@inheritdoc}
|
Chris@18
|
31 */
|
Chris@18
|
32 protected static $entityTypeId = 'file';
|
Chris@18
|
33
|
Chris@18
|
34 /**
|
Chris@18
|
35 * {@inheritdoc}
|
Chris@18
|
36 */
|
Chris@18
|
37 protected static $resourceTypeName = 'file--file';
|
Chris@18
|
38
|
Chris@18
|
39 /**
|
Chris@18
|
40 * {@inheritdoc}
|
Chris@18
|
41 *
|
Chris@18
|
42 * @var \Drupal\file\FileInterface
|
Chris@18
|
43 */
|
Chris@18
|
44 protected $entity;
|
Chris@18
|
45
|
Chris@18
|
46 /**
|
Chris@18
|
47 * {@inheritdoc}
|
Chris@18
|
48 */
|
Chris@18
|
49 protected static $patchProtectedFieldNames = [
|
Chris@18
|
50 'uri' => NULL,
|
Chris@18
|
51 'filemime' => NULL,
|
Chris@18
|
52 'filesize' => NULL,
|
Chris@18
|
53 'status' => NULL,
|
Chris@18
|
54 'changed' => NULL,
|
Chris@18
|
55 ];
|
Chris@18
|
56
|
Chris@18
|
57 /**
|
Chris@18
|
58 * The file author.
|
Chris@18
|
59 *
|
Chris@18
|
60 * @var \Drupal\user\UserInterface
|
Chris@18
|
61 */
|
Chris@18
|
62 protected $author;
|
Chris@18
|
63
|
Chris@18
|
64 /**
|
Chris@18
|
65 * {@inheritdoc}
|
Chris@18
|
66 */
|
Chris@18
|
67 protected function setUpAuthorization($method) {
|
Chris@18
|
68 switch ($method) {
|
Chris@18
|
69 case 'GET':
|
Chris@18
|
70 $this->grantPermissionsToTestedRole(['access content']);
|
Chris@18
|
71 break;
|
Chris@18
|
72
|
Chris@18
|
73 case 'PATCH':
|
Chris@18
|
74 case 'DELETE':
|
Chris@18
|
75 // \Drupal\file\FileAccessControlHandler::checkAccess() grants 'update'
|
Chris@18
|
76 // and 'delete' access only to the user that owns the file. So there is
|
Chris@18
|
77 // no permission to grant: instead, the file owner must be changed from
|
Chris@18
|
78 // its default (user 1) to the current user.
|
Chris@18
|
79 $this->makeCurrentUserFileOwner();
|
Chris@18
|
80 break;
|
Chris@18
|
81 }
|
Chris@18
|
82 }
|
Chris@18
|
83
|
Chris@18
|
84 /**
|
Chris@18
|
85 * Makes the current user the file owner.
|
Chris@18
|
86 */
|
Chris@18
|
87 protected function makeCurrentUserFileOwner() {
|
Chris@18
|
88 $account = User::load(2);
|
Chris@18
|
89 $this->entity->setOwnerId($account->id());
|
Chris@18
|
90 $this->entity->setOwner($account);
|
Chris@18
|
91 $this->entity->save();
|
Chris@18
|
92 }
|
Chris@18
|
93
|
Chris@18
|
94 /**
|
Chris@18
|
95 * {@inheritdoc}
|
Chris@18
|
96 */
|
Chris@18
|
97 protected function createEntity() {
|
Chris@18
|
98 $this->author = User::load(1);
|
Chris@18
|
99
|
Chris@18
|
100 $file = File::create();
|
Chris@18
|
101 $file->setOwnerId($this->author->id());
|
Chris@18
|
102 $file->setFilename('drupal.txt');
|
Chris@18
|
103 $file->setMimeType('text/plain');
|
Chris@18
|
104 $file->setFileUri('public://drupal.txt');
|
Chris@18
|
105 $file->set('status', FILE_STATUS_PERMANENT);
|
Chris@18
|
106 $file->save();
|
Chris@18
|
107
|
Chris@18
|
108 file_put_contents($file->getFileUri(), 'Drupal');
|
Chris@18
|
109
|
Chris@18
|
110 return $file;
|
Chris@18
|
111 }
|
Chris@18
|
112
|
Chris@18
|
113 /**
|
Chris@18
|
114 * {@inheritdoc}
|
Chris@18
|
115 */
|
Chris@18
|
116 protected function createAnotherEntity($key) {
|
Chris@18
|
117 /* @var \Drupal\file\FileInterface $duplicate */
|
Chris@18
|
118 $duplicate = parent::createAnotherEntity($key);
|
Chris@18
|
119 $duplicate->setFileUri("public://$key.txt");
|
Chris@18
|
120 $duplicate->save();
|
Chris@18
|
121 return $duplicate;
|
Chris@18
|
122 }
|
Chris@18
|
123
|
Chris@18
|
124 /**
|
Chris@18
|
125 * {@inheritdoc}
|
Chris@18
|
126 */
|
Chris@18
|
127 protected function getExpectedDocument() {
|
Chris@18
|
128 $self_url = Url::fromUri('base:/jsonapi/file/file/' . $this->entity->uuid())->setAbsolute()->toString(TRUE)->getGeneratedUrl();
|
Chris@18
|
129 return [
|
Chris@18
|
130 'jsonapi' => [
|
Chris@18
|
131 'meta' => [
|
Chris@18
|
132 'links' => [
|
Chris@18
|
133 'self' => ['href' => 'http://jsonapi.org/format/1.0/'],
|
Chris@18
|
134 ],
|
Chris@18
|
135 ],
|
Chris@18
|
136 'version' => '1.0',
|
Chris@18
|
137 ],
|
Chris@18
|
138 'links' => [
|
Chris@18
|
139 'self' => ['href' => $self_url],
|
Chris@18
|
140 ],
|
Chris@18
|
141 'data' => [
|
Chris@18
|
142 'id' => $this->entity->uuid(),
|
Chris@18
|
143 'type' => 'file--file',
|
Chris@18
|
144 'links' => [
|
Chris@18
|
145 'self' => ['href' => $self_url],
|
Chris@18
|
146 ],
|
Chris@18
|
147 'attributes' => [
|
Chris@18
|
148 'created' => (new \DateTime())->setTimestamp($this->entity->getCreatedTime())->setTimezone(new \DateTimeZone('UTC'))->format(\DateTime::RFC3339),
|
Chris@18
|
149 'changed' => (new \DateTime())->setTimestamp($this->entity->getChangedTime())->setTimezone(new \DateTimeZone('UTC'))->format(\DateTime::RFC3339),
|
Chris@18
|
150 'filemime' => 'text/plain',
|
Chris@18
|
151 'filename' => 'drupal.txt',
|
Chris@18
|
152 'filesize' => (int) $this->entity->getSize(),
|
Chris@18
|
153 'langcode' => 'en',
|
Chris@18
|
154 'status' => TRUE,
|
Chris@18
|
155 'uri' => [
|
Chris@18
|
156 'url' => base_path() . $this->siteDirectory . '/files/drupal.txt',
|
Chris@18
|
157 'value' => 'public://drupal.txt',
|
Chris@18
|
158 ],
|
Chris@18
|
159 'drupal_internal__fid' => 1,
|
Chris@18
|
160 ],
|
Chris@18
|
161 'relationships' => [
|
Chris@18
|
162 'uid' => [
|
Chris@18
|
163 'data' => [
|
Chris@18
|
164 'id' => $this->author->uuid(),
|
Chris@18
|
165 'type' => 'user--user',
|
Chris@18
|
166 ],
|
Chris@18
|
167 'links' => [
|
Chris@18
|
168 'related' => ['href' => $self_url . '/uid'],
|
Chris@18
|
169 'self' => ['href' => $self_url . '/relationships/uid'],
|
Chris@18
|
170 ],
|
Chris@18
|
171 ],
|
Chris@18
|
172 ],
|
Chris@18
|
173 ],
|
Chris@18
|
174 ];
|
Chris@18
|
175 }
|
Chris@18
|
176
|
Chris@18
|
177 /**
|
Chris@18
|
178 * {@inheritdoc}
|
Chris@18
|
179 */
|
Chris@18
|
180 protected function getPostDocument() {
|
Chris@18
|
181 return [
|
Chris@18
|
182 'data' => [
|
Chris@18
|
183 'type' => 'file--file',
|
Chris@18
|
184 'attributes' => [
|
Chris@18
|
185 'filename' => 'drupal.txt',
|
Chris@18
|
186 ],
|
Chris@18
|
187 ],
|
Chris@18
|
188 ];
|
Chris@18
|
189 }
|
Chris@18
|
190
|
Chris@18
|
191 /**
|
Chris@18
|
192 * {@inheritdoc}
|
Chris@18
|
193 */
|
Chris@18
|
194 public function testPostIndividual() {
|
Chris@18
|
195 // @todo https://www.drupal.org/node/1927648
|
Chris@18
|
196 $this->markTestSkipped();
|
Chris@18
|
197 }
|
Chris@18
|
198
|
Chris@18
|
199 /**
|
Chris@18
|
200 * {@inheritdoc}
|
Chris@18
|
201 */
|
Chris@18
|
202 protected function getExpectedUnauthorizedAccessMessage($method) {
|
Chris@18
|
203 if ($method === 'GET') {
|
Chris@18
|
204 return "The 'access content' permission is required.";
|
Chris@18
|
205 }
|
Chris@18
|
206 if ($method === 'PATCH' || $method === 'DELETE') {
|
Chris@18
|
207 return "Only the file owner can update or delete the file entity.";
|
Chris@18
|
208 }
|
Chris@18
|
209 return parent::getExpectedUnauthorizedAccessMessage($method);
|
Chris@18
|
210 }
|
Chris@18
|
211
|
Chris@18
|
212 /**
|
Chris@18
|
213 * {@inheritdoc}
|
Chris@18
|
214 */
|
Chris@18
|
215 public function testCollectionFilterAccess() {
|
Chris@18
|
216 $label_field_name = 'filename';
|
Chris@18
|
217 // Verify the expected behavior in the common case: when the file is public.
|
Chris@18
|
218 $this->doTestCollectionFilterAccessBasedOnPermissions($label_field_name, 'access content');
|
Chris@18
|
219
|
Chris@18
|
220 $collection_url = Url::fromRoute('jsonapi.entity_test--bar.collection');
|
Chris@18
|
221 $collection_filter_url = $collection_url->setOption('query', ["filter[spotlight.$label_field_name]" => $this->entity->label()]);
|
Chris@18
|
222 $request_options = [];
|
Chris@18
|
223 $request_options[RequestOptions::HEADERS]['Accept'] = 'application/vnd.api+json';
|
Chris@18
|
224 $request_options = NestedArray::mergeDeep($request_options, $this->getAuthenticationRequestOptions());
|
Chris@18
|
225
|
Chris@18
|
226 // 1 result because the current user is the file owner, even though the file
|
Chris@18
|
227 // is private.
|
Chris@18
|
228 $this->entity->setFileUri('private://drupal.txt');
|
Chris@18
|
229 $this->entity->setOwner($this->account);
|
Chris@18
|
230 $this->entity->save();
|
Chris@18
|
231 $response = $this->request('GET', $collection_filter_url, $request_options);
|
Chris@18
|
232 $doc = Json::decode((string) $response->getBody());
|
Chris@18
|
233 $this->assertCount(1, $doc['data']);
|
Chris@18
|
234
|
Chris@18
|
235 // 0 results because the current user is no longer the file owner and the
|
Chris@18
|
236 // file is private.
|
Chris@18
|
237 $this->entity->setOwner(User::load(0));
|
Chris@18
|
238 $this->entity->save();
|
Chris@18
|
239 $response = $this->request('GET', $collection_filter_url, $request_options);
|
Chris@18
|
240 $doc = Json::decode((string) $response->getBody());
|
Chris@18
|
241 $this->assertCount(0, $doc['data']);
|
Chris@18
|
242 }
|
Chris@18
|
243
|
Chris@18
|
244 }
|