1 | <?php |
||||
2 | |||||
3 | namespace SilverStripe\RestfulServer\Tests; |
||||
4 | |||||
5 | use SilverStripe\RestfulServer\RestfulServer; |
||||
6 | use SilverStripe\RestfulServer\Tests\Stubs\RestfulServerTestComment; |
||||
7 | use SilverStripe\RestfulServer\Tests\Stubs\RestfulServerTestExceptionThrown; |
||||
8 | use SilverStripe\RestfulServer\Tests\Stubs\RestfulServerTestSecretThing; |
||||
9 | use SilverStripe\RestfulServer\Tests\Stubs\RestfulServerTestPage; |
||||
10 | use SilverStripe\RestfulServer\Tests\Stubs\RestfulServerTestAuthor; |
||||
11 | use SilverStripe\RestfulServer\Tests\Stubs\RestfulServerTestAuthorRating; |
||||
12 | use SilverStripe\Control\Director; |
||||
13 | use SilverStripe\Core\Convert; |
||||
14 | use SilverStripe\Control\Controller; |
||||
15 | use SilverStripe\RestfulServer\Tests\Stubs\RestfulServerTestValidationFailure; |
||||
16 | use SilverStripe\Security\Member; |
||||
17 | use SilverStripe\Security\Security; |
||||
18 | use SilverStripe\ORM\DataObject; |
||||
19 | use SilverStripe\Dev\SapphireTest; |
||||
20 | use SilverStripe\RestfulServer\DataFormatter\JSONDataFormatter; |
||||
21 | use Page; |
||||
0 ignored issues
–
show
|
|||||
22 | use SilverStripe\Core\Config\Config; |
||||
23 | |||||
24 | /** |
||||
25 | * |
||||
26 | * @todo Test Relation getters |
||||
27 | * @todo Test filter and limit through GET params |
||||
28 | * @todo Test DELETE verb |
||||
29 | * |
||||
30 | */ |
||||
31 | class RestfulServerTest extends SapphireTest |
||||
32 | { |
||||
33 | protected static $fixture_file = 'RestfulServerTest.yml'; |
||||
34 | |||||
35 | protected $baseURI = 'http://www.fakesite.test'; |
||||
36 | |||||
37 | protected static $extra_dataobjects = [ |
||||
38 | RestfulServerTestComment::class, |
||||
39 | RestfulServerTestSecretThing::class, |
||||
40 | RestfulServerTestPage::class, |
||||
41 | RestfulServerTestAuthor::class, |
||||
42 | RestfulServerTestAuthorRating::class, |
||||
43 | RestfulServerTestValidationFailure::class, |
||||
44 | RestfulServerTestExceptionThrown::class, |
||||
45 | ]; |
||||
46 | |||||
47 | protected function urlSafeClassname($classname) |
||||
48 | { |
||||
49 | return str_replace('\\', '-', $classname); |
||||
50 | } |
||||
51 | |||||
52 | protected function setUp() |
||||
53 | { |
||||
54 | parent::setUp(); |
||||
55 | Director::config()->set('alternate_base_url', $this->baseURI); |
||||
56 | $this->logOut(); |
||||
57 | } |
||||
58 | |||||
59 | public function testApiAccess() |
||||
60 | { |
||||
61 | $comment1 = $this->objFromFixture(RestfulServerTestComment::class, 'comment1'); |
||||
62 | $page1 = $this->objFromFixture(RestfulServerTestPage::class, 'page1'); |
||||
63 | |||||
64 | // normal GET should succeed with $api_access enabled |
||||
65 | $urlSafeClassname = $this->urlSafeClassname(RestfulServerTestComment::class); |
||||
66 | $url = "{$this->baseURI}/api/v1/$urlSafeClassname/" . $comment1->ID; |
||||
67 | |||||
68 | $response = Director::test($url, null, null, 'GET'); |
||||
69 | $this->assertEquals(200, $response->getStatusCode()); |
||||
70 | |||||
71 | $_SERVER['PHP_AUTH_USER'] = '[email protected]'; |
||||
72 | $_SERVER['PHP_AUTH_PW'] = 'user'; |
||||
73 | |||||
74 | // even with logged in user a GET with $api_access disabled should fail |
||||
75 | $urlSafeClassname = $this->urlSafeClassname(RestfulServerTestPage::class); |
||||
76 | $url = "{$this->baseURI}/api/v1/$urlSafeClassname/" . $page1->ID; |
||||
77 | $response = Director::test($url, null, null, 'GET'); |
||||
78 | $this->assertEquals(401, $response->getStatusCode()); |
||||
79 | |||||
80 | unset($_SERVER['PHP_AUTH_USER']); |
||||
81 | unset($_SERVER['PHP_AUTH_PW']); |
||||
82 | } |
||||
83 | |||||
84 | public function testApiAccessBoolean() |
||||
85 | { |
||||
86 | $comment1 = $this->objFromFixture(RestfulServerTestComment::class, 'comment1'); |
||||
87 | |||||
88 | $urlSafeClassname = $this->urlSafeClassname(RestfulServerTestComment::class); |
||||
89 | $url = "{$this->baseURI}/api/v1/$urlSafeClassname/" . $comment1->ID; |
||||
90 | $response = Director::test($url, null, null, 'GET'); |
||||
91 | $this->assertContains('<ID>', $response->getBody()); |
||||
92 | $this->assertContains('<Name>', $response->getBody()); |
||||
93 | $this->assertContains('<Comment>', $response->getBody()); |
||||
94 | $this->assertContains('<Page', $response->getBody()); |
||||
95 | $this->assertContains('<Author', $response->getBody()); |
||||
96 | } |
||||
97 | |||||
98 | public function testAuthenticatedGET() |
||||
99 | { |
||||
100 | $thing1 = $this->objFromFixture(RestfulServerTestSecretThing::class, 'thing1'); |
||||
101 | $comment1 = $this->objFromFixture(RestfulServerTestComment::class, 'comment1'); |
||||
102 | |||||
103 | // @todo create additional mock object with authenticated VIEW permissions |
||||
104 | $urlSafeClassname = $this->urlSafeClassname(RestfulServerTestSecretThing::class); |
||||
105 | $url = "{$this->baseURI}/api/v1/$urlSafeClassname/" . $thing1->ID; |
||||
106 | $response = Director::test($url, null, null, 'GET'); |
||||
107 | $this->assertEquals(401, $response->getStatusCode()); |
||||
108 | |||||
109 | $_SERVER['PHP_AUTH_USER'] = '[email protected]'; |
||||
110 | $_SERVER['PHP_AUTH_PW'] = 'user'; |
||||
111 | |||||
112 | $urlSafeClassname = $this->urlSafeClassname(RestfulServerTestComment::class); |
||||
113 | $url = "{$this->baseURI}/api/v1/$urlSafeClassname/" . $comment1->ID; |
||||
114 | $response = Director::test($url, null, null, 'GET'); |
||||
115 | $this->assertEquals(200, $response->getStatusCode()); |
||||
116 | |||||
117 | unset($_SERVER['PHP_AUTH_USER']); |
||||
118 | unset($_SERVER['PHP_AUTH_PW']); |
||||
119 | } |
||||
120 | |||||
121 | public function testGETWithFieldAlias() |
||||
122 | { |
||||
123 | Config::inst()->set(RestfulServerTestAuthorRating::class, 'api_field_mapping', ['rate' => 'Rating']); |
||||
0 ignored issues
–
show
The method
set() does not exist on SilverStripe\Config\Coll...nfigCollectionInterface . It seems like you code against a sub-type of SilverStripe\Config\Coll...nfigCollectionInterface such as SilverStripe\Config\Coll...nfigCollectionInterface .
(
Ignorable by Annotation
)
If this is a false-positive, you can also ignore this issue in your code via the
![]() |
|||||
124 | $rating1 = $this->objFromFixture(RestfulServerTestAuthorRating::class, 'rating1'); |
||||
125 | |||||
126 | $urlSafeClassname = $this->urlSafeClassname(RestfulServerTestAuthorRating::class); |
||||
127 | $url = "{$this->baseURI}/api/v1/$urlSafeClassname/" . $rating1->ID; |
||||
128 | $response = Director::test($url, null, null, 'GET'); |
||||
129 | $responseArr = Convert::xml2array($response->getBody()); |
||||
130 | $this->assertEquals(3, $responseArr['rate']); |
||||
131 | } |
||||
132 | |||||
133 | public function testAuthenticatedPUT() |
||||
134 | { |
||||
135 | $comment1 = $this->objFromFixture(RestfulServerTestComment::class, 'comment1'); |
||||
136 | |||||
137 | $urlSafeClassname = $this->urlSafeClassname(RestfulServerTestComment::class); |
||||
138 | $url = "{$this->baseURI}/api/v1/$urlSafeClassname/" . $comment1->ID; |
||||
139 | $data = array('Comment' => 'created'); |
||||
140 | |||||
141 | $response = Director::test($url, $data, null, 'PUT'); |
||||
142 | $this->assertEquals(401, $response->getStatusCode()); // Permission failure |
||||
143 | |||||
144 | $_SERVER['PHP_AUTH_USER'] = '[email protected]'; |
||||
145 | $_SERVER['PHP_AUTH_PW'] = 'editor'; |
||||
146 | $response = Director::test($url, $data, null, 'PUT'); |
||||
147 | $this->assertEquals(202, $response->getStatusCode()); // Accepted |
||||
148 | |||||
149 | unset($_SERVER['PHP_AUTH_USER']); |
||||
150 | unset($_SERVER['PHP_AUTH_PW']); |
||||
151 | } |
||||
152 | |||||
153 | public function testGETRelationshipsXML() |
||||
154 | { |
||||
155 | $author1 = $this->objFromFixture(RestfulServerTestAuthor::class, 'author1'); |
||||
156 | $rating1 = $this->objFromFixture(RestfulServerTestAuthorRating::class, 'rating1'); |
||||
157 | $rating2 = $this->objFromFixture(RestfulServerTestAuthorRating::class, 'rating2'); |
||||
158 | |||||
159 | // @todo should be set up by fixtures, doesn't work for some reason... |
||||
160 | $author1->Ratings()->add($rating1); |
||||
161 | $author1->Ratings()->add($rating2); |
||||
162 | |||||
163 | $urlSafeClassname = $this->urlSafeClassname(RestfulServerTestAuthor::class); |
||||
164 | $url = "{$this->baseURI}/api/v1/$urlSafeClassname/" . $author1->ID; |
||||
165 | $response = Director::test($url, null, null, 'GET'); |
||||
166 | $this->assertEquals(200, $response->getStatusCode()); |
||||
167 | |||||
168 | $responseArr = Convert::xml2array($response->getBody()); |
||||
169 | $xmlTagSafeClassName = $this->urlSafeClassname(RestfulServerTestAuthorRating::class); |
||||
170 | $ratingsArr = $responseArr['Ratings'][$xmlTagSafeClassName]; |
||||
171 | $this->assertEquals(2, count($ratingsArr)); |
||||
172 | $ratingIDs = array( |
||||
173 | (int)$ratingsArr[0]['@attributes']['id'], |
||||
174 | (int)$ratingsArr[1]['@attributes']['id'] |
||||
175 | ); |
||||
176 | $this->assertContains($rating1->ID, $ratingIDs); |
||||
177 | $this->assertContains($rating2->ID, $ratingIDs); |
||||
178 | } |
||||
179 | |||||
180 | public function testGETRelationshipsWithAlias() |
||||
181 | { |
||||
182 | // Alias do not currently work with Relationships |
||||
183 | Config::inst()->set(RestfulServerTestAuthor::class, 'api_field_mapping', ['stars' => 'Ratings']); |
||||
184 | $author1 = $this->objFromFixture(RestfulServerTestAuthor::class, 'author1'); |
||||
185 | $rating1 = $this->objFromFixture(RestfulServerTestAuthorRating::class, 'rating1'); |
||||
186 | |||||
187 | // @todo should be set up by fixtures, doesn't work for some reason... |
||||
188 | $author1->Ratings()->add($rating1); |
||||
189 | |||||
190 | $urlSafeClassname = $this->urlSafeClassname(RestfulServerTestAuthor::class); |
||||
191 | $url = "{$this->baseURI}/api/v1/$urlSafeClassname/" . $author1->ID . '?add_fields=stars'; |
||||
192 | $response = Director::test($url, null, null, 'GET'); |
||||
193 | $this->assertEquals(200, $response->getStatusCode()); |
||||
194 | |||||
195 | $responseArr = Convert::xml2array($response->getBody()); |
||||
196 | $xmlTagSafeClassName = $this->urlSafeClassname(RestfulServerTestAuthorRating::class); |
||||
0 ignored issues
–
show
|
|||||
197 | |||||
198 | $this->assertTrue(array_key_exists('Ratings', $responseArr)); |
||||
199 | $this->assertFalse(array_key_exists('stars', $responseArr)); |
||||
200 | } |
||||
201 | |||||
202 | public function testGETManyManyRelationshipsXML() |
||||
203 | { |
||||
204 | // author4 has related authors author2 and author3 |
||||
205 | $author2 = $this->objFromFixture(RestfulServerTestAuthor::class, 'author2'); |
||||
206 | $author3 = $this->objFromFixture(RestfulServerTestAuthor::class, 'author3'); |
||||
207 | $author4 = $this->objFromFixture(RestfulServerTestAuthor::class, 'author4'); |
||||
208 | |||||
209 | $urlSafeClassname = $this->urlSafeClassname(RestfulServerTestAuthor::class); |
||||
210 | $url = "{$this->baseURI}/api/v1/$urlSafeClassname/" . $author4->ID . '/RelatedAuthors'; |
||||
211 | $response = Director::test($url, null, null, 'GET'); |
||||
212 | $this->assertEquals(200, $response->getStatusCode()); |
||||
213 | $arr = Convert::xml2array($response->getBody()); |
||||
214 | $xmlSafeClassName = $this->urlSafeClassname(RestfulServerTestAuthor::class); |
||||
215 | $authorsArr = $arr[$xmlSafeClassName]; |
||||
216 | |||||
217 | $this->assertEquals(2, count($authorsArr)); |
||||
218 | $ratingIDs = array( |
||||
219 | (int)$authorsArr[0]['ID'], |
||||
220 | (int)$authorsArr[1]['ID'] |
||||
221 | ); |
||||
222 | $this->assertContains($author2->ID, $ratingIDs); |
||||
223 | $this->assertContains($author3->ID, $ratingIDs); |
||||
224 | } |
||||
225 | |||||
226 | public function testPUTWithFormEncoded() |
||||
227 | { |
||||
228 | $comment1 = $this->objFromFixture(RestfulServerTestComment::class, 'comment1'); |
||||
229 | |||||
230 | $_SERVER['PHP_AUTH_USER'] = '[email protected]'; |
||||
231 | $_SERVER['PHP_AUTH_PW'] = 'editor'; |
||||
232 | |||||
233 | $urlSafeClassname = $this->urlSafeClassname(RestfulServerTestComment::class); |
||||
234 | $url = "{$this->baseURI}/api/v1/$urlSafeClassname/" . $comment1->ID; |
||||
235 | $body = 'Name=Updated Comment&Comment=updated'; |
||||
236 | $headers = array( |
||||
237 | 'Content-Type' => 'application/x-www-form-urlencoded' |
||||
238 | ); |
||||
239 | $response = Director::test($url, null, null, 'PUT', $body, $headers); |
||||
240 | $this->assertEquals(202, $response->getStatusCode()); // Accepted |
||||
241 | // Assumption: XML is default output |
||||
242 | $responseArr = Convert::xml2array($response->getBody()); |
||||
243 | $this->assertEquals($comment1->ID, $responseArr['ID']); |
||||
244 | $this->assertEquals('updated', $responseArr['Comment']); |
||||
245 | $this->assertEquals('Updated Comment', $responseArr['Name']); |
||||
246 | |||||
247 | unset($_SERVER['PHP_AUTH_USER']); |
||||
248 | unset($_SERVER['PHP_AUTH_PW']); |
||||
249 | } |
||||
250 | |||||
251 | public function testPOSTWithFormEncoded() |
||||
252 | { |
||||
253 | $comment1 = $this->objFromFixture(RestfulServerTestComment::class, 'comment1'); |
||||
254 | |||||
255 | $_SERVER['PHP_AUTH_USER'] = '[email protected]'; |
||||
256 | $_SERVER['PHP_AUTH_PW'] = 'editor'; |
||||
257 | |||||
258 | $urlSafeClassname = $this->urlSafeClassname(RestfulServerTestComment::class); |
||||
259 | $url = "{$this->baseURI}/api/v1/$urlSafeClassname"; |
||||
260 | $body = 'Name=New Comment&Comment=created'; |
||||
261 | $headers = array( |
||||
262 | 'Content-Type' => 'application/x-www-form-urlencoded' |
||||
263 | ); |
||||
264 | $response = Director::test($url, null, null, 'POST', $body, $headers); |
||||
265 | $this->assertEquals(201, $response->getStatusCode()); // Created |
||||
266 | // Assumption: XML is default output |
||||
267 | $responseArr = Convert::xml2array($response->getBody()); |
||||
268 | $this->assertTrue($responseArr['ID'] > 0); |
||||
269 | $this->assertNotEquals($responseArr['ID'], $comment1->ID); |
||||
270 | $this->assertEquals('created', $responseArr['Comment']); |
||||
271 | $this->assertEquals('New Comment', $responseArr['Name']); |
||||
272 | $this->assertEquals( |
||||
273 | Controller::join_links($url, $responseArr['ID'] . '.xml'), |
||||
274 | $response->getHeader('Location') |
||||
275 | ); |
||||
276 | |||||
277 | unset($_SERVER['PHP_AUTH_USER']); |
||||
278 | unset($_SERVER['PHP_AUTH_PW']); |
||||
279 | } |
||||
280 | |||||
281 | public function testPostWithoutBodyReturnsNoContent() |
||||
282 | { |
||||
283 | $_SERVER['PHP_AUTH_USER'] = '[email protected]'; |
||||
284 | $_SERVER['PHP_AUTH_PW'] = 'editor'; |
||||
285 | |||||
286 | $url = "{$this->baseURI}/api/v1/" . RestfulServerTestComment::class; |
||||
287 | $response = Director::test($url, null, null, 'POST'); |
||||
288 | |||||
289 | $this->assertEquals('No Content', $response->getBody()); |
||||
290 | |||||
291 | unset($_SERVER['PHP_AUTH_USER'], $_SERVER['PHP_AUTH_PW']); |
||||
292 | } |
||||
293 | |||||
294 | public function testPUTwithJSON() |
||||
295 | { |
||||
296 | $comment1 = $this->objFromFixture(RestfulServerTestComment::class, 'comment1'); |
||||
297 | |||||
298 | $_SERVER['PHP_AUTH_USER'] = '[email protected]'; |
||||
299 | $_SERVER['PHP_AUTH_PW'] = 'editor'; |
||||
300 | |||||
301 | // by acceptance mimetype |
||||
302 | $urlSafeClassname = $this->urlSafeClassname(RestfulServerTestComment::class); |
||||
303 | $url = "{$this->baseURI}/api/v1/$urlSafeClassname/" . $comment1->ID; |
||||
304 | $body = '{"Comment":"updated"}'; |
||||
305 | $response = Director::test($url, null, null, 'PUT', $body, array( |
||||
306 | 'Content-Type'=>'application/json', |
||||
307 | 'Accept' => 'application/json' |
||||
308 | )); |
||||
309 | $this->assertEquals(202, $response->getStatusCode()); // Accepted |
||||
310 | $obj = json_decode($response->getBody()); |
||||
311 | $this->assertEquals($comment1->ID, $obj->ID); |
||||
312 | $this->assertEquals('updated', $obj->Comment); |
||||
313 | |||||
314 | // by extension |
||||
315 | $urlSafeClassname = $this->urlSafeClassname(RestfulServerTestComment::class); |
||||
316 | $url = "{$this->baseURI}/api/v1/$urlSafeClassname/{$comment1->ID}.json"; |
||||
317 | $body = '{"Comment":"updated"}'; |
||||
318 | $response = Director::test($url, null, null, 'PUT', $body); |
||||
319 | $this->assertEquals(202, $response->getStatusCode()); // Accepted |
||||
320 | $this->assertEquals($url, $response->getHeader('Location')); |
||||
321 | $obj = json_decode($response->getBody()); |
||||
322 | $this->assertEquals($comment1->ID, $obj->ID); |
||||
323 | $this->assertEquals('updated', $obj->Comment); |
||||
324 | |||||
325 | unset($_SERVER['PHP_AUTH_USER']); |
||||
326 | unset($_SERVER['PHP_AUTH_PW']); |
||||
327 | } |
||||
328 | |||||
329 | public function testPUTwithXML() |
||||
330 | { |
||||
331 | $comment1 = $this->objFromFixture(RestfulServerTestComment::class, 'comment1'); |
||||
332 | |||||
333 | $_SERVER['PHP_AUTH_USER'] = '[email protected]'; |
||||
334 | $_SERVER['PHP_AUTH_PW'] = 'editor'; |
||||
335 | |||||
336 | // by mimetype |
||||
337 | $urlSafeClassname = $this->urlSafeClassname(RestfulServerTestComment::class); |
||||
338 | $url = "{$this->baseURI}/api/v1/$urlSafeClassname/" . $comment1->ID; |
||||
339 | $body = '<RestfulServerTestComment><Comment>updated</Comment></RestfulServerTestComment>'; |
||||
340 | $response = Director::test($url, null, null, 'PUT', $body, array('Content-Type'=>'text/xml')); |
||||
341 | $this->assertEquals(202, $response->getStatusCode()); // Accepted |
||||
342 | $obj = Convert::xml2array($response->getBody()); |
||||
343 | $this->assertEquals($comment1->ID, $obj['ID']); |
||||
344 | $this->assertEquals('updated', $obj['Comment']); |
||||
345 | |||||
346 | // by extension |
||||
347 | $urlSafeClassname = $this->urlSafeClassname(RestfulServerTestComment::class); |
||||
348 | $url = "{$this->baseURI}/api/v1/$urlSafeClassname/{$comment1->ID}.xml"; |
||||
349 | $body = '<RestfulServerTestComment><Comment>updated</Comment></RestfulServerTestComment>'; |
||||
350 | $response = Director::test($url, null, null, 'PUT', $body); |
||||
351 | $this->assertEquals(202, $response->getStatusCode()); // Accepted |
||||
352 | $this->assertEquals($url, $response->getHeader('Location')); |
||||
353 | $obj = Convert::xml2array($response->getBody()); |
||||
354 | $this->assertEquals($comment1->ID, $obj['ID']); |
||||
355 | $this->assertEquals('updated', $obj['Comment']); |
||||
356 | |||||
357 | unset($_SERVER['PHP_AUTH_USER']); |
||||
358 | unset($_SERVER['PHP_AUTH_PW']); |
||||
359 | } |
||||
360 | |||||
361 | public function testHTTPAcceptAndContentType() |
||||
362 | { |
||||
363 | $comment1 = $this->objFromFixture(RestfulServerTestComment::class, 'comment1'); |
||||
364 | |||||
365 | $urlSafeClassname = $this->urlSafeClassname(RestfulServerTestComment::class); |
||||
366 | $url = "{$this->baseURI}/api/v1/$urlSafeClassname/" . $comment1->ID; |
||||
367 | |||||
368 | $headers = array('Accept' => 'application/json'); |
||||
369 | $response = Director::test($url, null, null, 'GET', null, $headers); |
||||
370 | $this->assertEquals(200, $response->getStatusCode()); // Success |
||||
371 | $obj = json_decode($response->getBody()); |
||||
372 | $this->assertEquals($comment1->ID, $obj->ID); |
||||
373 | $this->assertEquals('application/json', $response->getHeader('Content-Type')); |
||||
374 | } |
||||
375 | |||||
376 | public function testNotFound() |
||||
377 | { |
||||
378 | $_SERVER['PHP_AUTH_USER'] = '[email protected]'; |
||||
379 | $_SERVER['PHP_AUTH_PW'] = 'user'; |
||||
380 | |||||
381 | $urlSafeClassname = $this->urlSafeClassname(RestfulServerTestComment::class); |
||||
382 | $url = "{$this->baseURI}/api/v1/$urlSafeClassname/99"; |
||||
383 | $response = Director::test($url, null, null, 'GET'); |
||||
384 | $this->assertEquals(404, $response->getStatusCode()); |
||||
385 | |||||
386 | unset($_SERVER['PHP_AUTH_USER']); |
||||
387 | unset($_SERVER['PHP_AUTH_PW']); |
||||
388 | } |
||||
389 | |||||
390 | public function testMethodNotAllowed() |
||||
391 | { |
||||
392 | $comment1 = $this->objFromFixture(RestfulServerTestComment::class, 'comment1'); |
||||
393 | |||||
394 | $urlSafeClassname = $this->urlSafeClassname(RestfulServerTestComment::class); |
||||
395 | $url = "{$this->baseURI}/api/v1/$urlSafeClassname/" . $comment1->ID; |
||||
396 | $response = Director::test($url, null, null, 'UNKNOWNHTTPMETHOD'); |
||||
397 | $this->assertEquals(405, $response->getStatusCode()); |
||||
398 | } |
||||
399 | |||||
400 | public function testConflictOnExistingResourceWhenUsingPost() |
||||
401 | { |
||||
402 | $rating1 = $this->objFromFixture(RestfulServerTestAuthorRating::class, 'rating1'); |
||||
403 | |||||
404 | $urlSafeClassname = $this->urlSafeClassname(RestfulServerTestAuthorRating::class); |
||||
405 | $url = "{$this->baseURI}/api/v1/$urlSafeClassname/" . $rating1->ID; |
||||
406 | $response = Director::test($url, null, null, 'POST'); |
||||
407 | $this->assertEquals(409, $response->getStatusCode()); |
||||
408 | } |
||||
409 | |||||
410 | public function testUnsupportedMediaType() |
||||
411 | { |
||||
412 | $_SERVER['PHP_AUTH_USER'] = '[email protected]'; |
||||
413 | $_SERVER['PHP_AUTH_PW'] = 'user'; |
||||
414 | |||||
415 | $urlSafeClassname = $this->urlSafeClassname(RestfulServerTestComment::class); |
||||
416 | $url = "{$this->baseURI}/api/v1/$urlSafeClassname"; |
||||
417 | $data = "Comment||\/||updated"; // weird format |
||||
418 | $headers = array('Content-Type' => 'text/weirdformat'); |
||||
419 | $response = Director::test($url, null, null, 'POST', $data, $headers); |
||||
420 | $this->assertEquals(415, $response->getStatusCode()); |
||||
421 | |||||
422 | unset($_SERVER['PHP_AUTH_USER']); |
||||
423 | unset($_SERVER['PHP_AUTH_PW']); |
||||
424 | } |
||||
425 | |||||
426 | public function testXMLValueFormatting() |
||||
427 | { |
||||
428 | $rating1 = $this->objFromFixture(RestfulServerTestAuthorRating::class, 'rating1'); |
||||
429 | |||||
430 | $urlSafeClassname = $this->urlSafeClassname(RestfulServerTestAuthorRating::class); |
||||
431 | $url = "{$this->baseURI}/api/v1/$urlSafeClassname/" . $rating1->ID; |
||||
432 | $response = Director::test($url, null, null, 'GET'); |
||||
433 | $this->assertContains('<ID>' . $rating1->ID . '</ID>', $response->getBody()); |
||||
434 | $this->assertContains('<Rating>' . $rating1->Rating . '</Rating>', $response->getBody()); |
||||
435 | } |
||||
436 | |||||
437 | public function testXMLValueFormattingWithFieldAlias() |
||||
438 | { |
||||
439 | Config::inst()->set(RestfulServerTestAuthorRating::class, 'api_field_mapping', ['rate' => 'Rating']); |
||||
440 | $rating1 = $this->objFromFixture(RestfulServerTestAuthorRating::class, 'rating1'); |
||||
441 | |||||
442 | $urlSafeClassname = $this->urlSafeClassname(RestfulServerTestAuthorRating::class); |
||||
443 | $url = "{$this->baseURI}/api/v1/$urlSafeClassname/" . $rating1->ID; |
||||
444 | $response = Director::test($url, null, null, 'GET'); |
||||
445 | $this->assertContains('<rate>' . $rating1->Rating . '</rate>', $response->getBody()); |
||||
446 | } |
||||
447 | |||||
448 | public function testApiAccessFieldRestrictions() |
||||
449 | { |
||||
450 | $author1 = $this->objFromFixture(RestfulServerTestAuthor::class, 'author1'); |
||||
451 | $rating1 = $this->objFromFixture(RestfulServerTestAuthorRating::class, 'rating1'); |
||||
452 | |||||
453 | $urlSafeClassname = $this->urlSafeClassname(RestfulServerTestAuthorRating::class); |
||||
454 | $url = "{$this->baseURI}/api/v1/$urlSafeClassname/" . $rating1->ID; |
||||
455 | $response = Director::test($url, null, null, 'GET'); |
||||
456 | $this->assertContains('<ID>', $response->getBody()); |
||||
457 | $this->assertContains('<Rating>', $response->getBody()); |
||||
458 | $this->assertContains('<Author', $response->getBody()); |
||||
459 | $this->assertNotContains('<SecretField>', $response->getBody()); |
||||
460 | $this->assertNotContains('<SecretRelation>', $response->getBody()); |
||||
461 | |||||
462 | $urlSafeClassname = $this->urlSafeClassname(RestfulServerTestAuthorRating::class); |
||||
463 | $url = "{$this->baseURI}/api/v1/$urlSafeClassname/" . $rating1->ID . '?add_fields=SecretField,SecretRelation'; |
||||
464 | $response = Director::test($url, null, null, 'GET'); |
||||
465 | $this->assertNotContains( |
||||
466 | '<SecretField>', |
||||
467 | $response->getBody(), |
||||
468 | '"add_fields" URL parameter filters out disallowed fields from $api_access' |
||||
469 | ); |
||||
470 | $this->assertNotContains( |
||||
471 | '<SecretRelation>', |
||||
472 | $response->getBody(), |
||||
473 | '"add_fields" URL parameter filters out disallowed relations from $api_access' |
||||
474 | ); |
||||
475 | |||||
476 | $urlSafeClassname = $this->urlSafeClassname(RestfulServerTestAuthorRating::class); |
||||
477 | $url = "{$this->baseURI}/api/v1/$urlSafeClassname/" . $rating1->ID . '?fields=SecretField,SecretRelation'; |
||||
478 | $response = Director::test($url, null, null, 'GET'); |
||||
479 | $this->assertNotContains( |
||||
480 | '<SecretField>', |
||||
481 | $response->getBody(), |
||||
482 | '"fields" URL parameter filters out disallowed fields from $api_access' |
||||
483 | ); |
||||
484 | $this->assertNotContains( |
||||
485 | '<SecretRelation>', |
||||
486 | $response->getBody(), |
||||
487 | '"fields" URL parameter filters out disallowed relations from $api_access' |
||||
488 | ); |
||||
489 | |||||
490 | $urlSafeClassname = $this->urlSafeClassname(RestfulServerTestAuthor::class); |
||||
491 | $url = "{$this->baseURI}/api/v1/$urlSafeClassname/" . $author1->ID . '/Ratings'; |
||||
492 | $response = Director::test($url, null, null, 'GET'); |
||||
493 | $this->assertContains( |
||||
494 | '<Rating>', |
||||
495 | $response->getBody(), |
||||
496 | 'Relation viewer shows fields allowed through $api_access' |
||||
497 | ); |
||||
498 | $this->assertNotContains( |
||||
499 | '<SecretField>', |
||||
500 | $response->getBody(), |
||||
501 | 'Relation viewer on has-many filters out disallowed fields from $api_access' |
||||
502 | ); |
||||
503 | } |
||||
504 | |||||
505 | public function testApiAccessRelationRestrictionsInline() |
||||
506 | { |
||||
507 | $author1 = $this->objFromFixture(RestfulServerTestAuthor::class, 'author1'); |
||||
508 | |||||
509 | $urlSafeClassname = $this->urlSafeClassname(RestfulServerTestAuthor::class); |
||||
510 | $url = "{$this->baseURI}/api/v1/$urlSafeClassname/" . $author1->ID; |
||||
511 | $response = Director::test($url, null, null, 'GET'); |
||||
512 | $this->assertNotContains('<RelatedPages', $response->getBody(), 'Restricts many-many with api_access=false'); |
||||
513 | $this->assertNotContains('<PublishedPages', $response->getBody(), 'Restricts has-many with api_access=false'); |
||||
514 | } |
||||
515 | |||||
516 | public function testApiAccessRelationRestrictionsOnEndpoint() |
||||
517 | { |
||||
518 | $author1 = $this->objFromFixture(RestfulServerTestAuthor::class, 'author1'); |
||||
519 | |||||
520 | $urlSafeClassname = $this->urlSafeClassname(RestfulServerTestAuthor::class); |
||||
521 | $url = "{$this->baseURI}/api/v1/$urlSafeClassname/" . $author1->ID . "/ProfilePage"; |
||||
522 | $response = Director::test($url, null, null, 'GET'); |
||||
523 | $this->assertEquals(404, $response->getStatusCode(), 'Restricts has-one with api_access=false'); |
||||
524 | |||||
525 | $urlSafeClassname = $this->urlSafeClassname(RestfulServerTestAuthor::class); |
||||
526 | $url = "{$this->baseURI}/api/v1/$urlSafeClassname/" . $author1->ID . "/RelatedPages"; |
||||
527 | $response = Director::test($url, null, null, 'GET'); |
||||
528 | $this->assertEquals(404, $response->getStatusCode(), 'Restricts many-many with api_access=false'); |
||||
529 | |||||
530 | $urlSafeClassname = $this->urlSafeClassname(RestfulServerTestAuthor::class); |
||||
531 | $url = "{$this->baseURI}/api/v1/$urlSafeClassname/" . $author1->ID . "/PublishedPages"; |
||||
532 | $response = Director::test($url, null, null, 'GET'); |
||||
533 | $this->assertEquals(404, $response->getStatusCode(), 'Restricts has-many with api_access=false'); |
||||
534 | } |
||||
535 | |||||
536 | public function testApiAccessWithPUT() |
||||
537 | { |
||||
538 | $rating1 = $this->objFromFixture(RestfulServerTestAuthorRating::class, 'rating1'); |
||||
539 | |||||
540 | $urlSafeClassname = $this->urlSafeClassname(RestfulServerTestAuthorRating::class); |
||||
541 | $url = "{$this->baseURI}/api/v1/$urlSafeClassname/" . $rating1->ID; |
||||
542 | $data = array( |
||||
543 | 'Rating' => '42', |
||||
544 | 'WriteProtectedField' => 'haxx0red' |
||||
545 | ); |
||||
546 | $response = Director::test($url, $data, null, 'PUT'); |
||||
547 | // Assumption: XML is default output |
||||
548 | $responseArr = Convert::xml2array($response->getBody()); |
||||
549 | $this->assertEquals(42, $responseArr['Rating']); |
||||
550 | $this->assertNotEquals('haxx0red', $responseArr['WriteProtectedField']); |
||||
551 | } |
||||
552 | |||||
553 | public function testFieldAliasWithPUT() |
||||
554 | { |
||||
555 | Config::inst()->set(RestfulServerTestAuthorRating::class, 'api_field_mapping', ['rate' => 'Rating']); |
||||
556 | $rating1 = $this->objFromFixture(RestfulServerTestAuthorRating::class, 'rating1'); |
||||
557 | $urlSafeClassname = $this->urlSafeClassname(RestfulServerTestAuthorRating::class); |
||||
558 | $url = "{$this->baseURI}/api/v1/$urlSafeClassname/" . $rating1->ID; |
||||
559 | // Test input with original fieldname |
||||
560 | $data = array( |
||||
561 | 'Rating' => '42', |
||||
562 | ); |
||||
563 | $response = Director::test($url, $data, null, 'PUT'); |
||||
564 | // Assumption: XML is default output |
||||
565 | $responseArr = Convert::xml2array($response->getBody()); |
||||
566 | // should output with aliased name |
||||
567 | $this->assertEquals(42, $responseArr['rate']); |
||||
568 | } |
||||
569 | |||||
570 | public function testJSONDataFormatter() |
||||
571 | { |
||||
572 | $formatter = new JSONDataFormatter(); |
||||
573 | $editor = $this->objFromFixture(Member::class, 'editor'); |
||||
574 | $user = $this->objFromFixture(Member::class, 'user'); |
||||
575 | |||||
576 | // The DataFormatter performs canView calls |
||||
577 | // these are `Member`s so we need to be ADMIN types |
||||
578 | $this->logInWithPermission('ADMIN'); |
||||
579 | |||||
580 | $this->assertEquals( |
||||
581 | '{"FirstName":"Editor","Email":"[email protected]"}', |
||||
582 | $formatter->convertDataObject($editor, ["FirstName", "Email"]), |
||||
583 | "Correct JSON formatting with field subset" |
||||
584 | ); |
||||
585 | |||||
586 | $set = Member::get() |
||||
587 | ->filter('ID', [$editor->ID, $user->ID]) |
||||
588 | ->sort('"Email" ASC'); // for sorting for postgres |
||||
589 | $this->assertEquals( |
||||
590 | '{"totalSize":null,"items":[{"FirstName":"Editor","Email":"[email protected]"},' . |
||||
591 | '{"FirstName":"User","Email":"[email protected]"}]}', |
||||
592 | $formatter->convertDataObjectSet($set, ["FirstName", "Email"]), |
||||
593 | "Correct JSON formatting on a dataobjectset with field filter" |
||||
594 | ); |
||||
595 | } |
||||
596 | |||||
597 | public function testJSONDataFormatterWithFieldAlias() |
||||
598 | { |
||||
599 | Config::inst()->set(Member::class, 'api_field_mapping', ['MyName' => 'FirstName']); |
||||
600 | $formatter = new JSONDataFormatter(); |
||||
601 | $editor = $this->objFromFixture(Member::class, 'editor'); |
||||
602 | $user = $this->objFromFixture(Member::class, 'user'); |
||||
603 | |||||
604 | // The DataFormatter performs canView calls |
||||
605 | // these are `Member`s so we need to be ADMIN types |
||||
606 | $this->logInWithPermission('ADMIN'); |
||||
607 | |||||
608 | $set = Member::get() |
||||
609 | ->filter('ID', [$editor->ID, $user->ID]) |
||||
610 | ->sort('"Email" ASC'); // for sorting for postgres |
||||
611 | |||||
612 | $this->assertEquals( |
||||
613 | '{"totalSize":null,"items":[{"MyName":"Editor","Email":"[email protected]"},' . |
||||
614 | '{"MyName":"User","Email":"[email protected]"}]}', |
||||
615 | $formatter->convertDataObjectSet($set, ["FirstName", "Email"]), |
||||
616 | "Correct JSON formatting with field alias" |
||||
617 | ); |
||||
618 | } |
||||
619 | |||||
620 | public function testGetWithSortDescending() |
||||
621 | { |
||||
622 | $urlSafeClassname = $this->urlSafeClassname(RestfulServerTestAuthor::class); |
||||
623 | $url = "{$this->baseURI}/api/v1/{$urlSafeClassname}?sort=FirstName&dir=DESC&fields=FirstName"; |
||||
624 | |||||
625 | $response = Director::test($url); |
||||
626 | $results = Convert::xml2array($response->getBody()); |
||||
627 | |||||
628 | $this->assertSame('Author 4', $results[$urlSafeClassname][0]['FirstName']); |
||||
629 | $this->assertSame('Author 3', $results[$urlSafeClassname][1]['FirstName']); |
||||
630 | $this->assertSame('Author 2', $results[$urlSafeClassname][2]['FirstName']); |
||||
631 | $this->assertSame('Author 1', $results[$urlSafeClassname][3]['FirstName']); |
||||
632 | } |
||||
633 | |||||
634 | public function testGetWithSortAscending() |
||||
635 | { |
||||
636 | $urlSafeClassname = $this->urlSafeClassname(RestfulServerTestAuthor::class); |
||||
637 | $url = "{$this->baseURI}/api/v1/{$urlSafeClassname}?sort=FirstName&dir=ASC&fields=FirstName"; |
||||
638 | |||||
639 | $response = Director::test($url); |
||||
640 | $results = Convert::xml2array($response->getBody()); |
||||
641 | |||||
642 | $this->assertSame('Author 1', $results[$urlSafeClassname][0]['FirstName']); |
||||
643 | $this->assertSame('Author 2', $results[$urlSafeClassname][1]['FirstName']); |
||||
644 | $this->assertSame('Author 3', $results[$urlSafeClassname][2]['FirstName']); |
||||
645 | $this->assertSame('Author 4', $results[$urlSafeClassname][3]['FirstName']); |
||||
646 | } |
||||
647 | |||||
648 | public function testGetSortsByIdWhenInvalidSortColumnIsProvided() |
||||
649 | { |
||||
650 | $urlSafeClassname = $this->urlSafeClassname(RestfulServerTestAuthor::class); |
||||
651 | $url = "{$this->baseURI}/api/v1/{$urlSafeClassname}?sort=Surname&dir=DESC&fields=FirstName"; |
||||
652 | |||||
653 | $response = Director::test($url); |
||||
654 | |||||
655 | $results = Convert::xml2array($response->getBody()); |
||||
656 | |||||
657 | $this->assertSame('Author 1', $results[$urlSafeClassname][0]['FirstName']); |
||||
658 | $this->assertSame('Author 2', $results[$urlSafeClassname][1]['FirstName']); |
||||
659 | $this->assertSame('Author 3', $results[$urlSafeClassname][2]['FirstName']); |
||||
660 | $this->assertSame('Author 4', $results[$urlSafeClassname][3]['FirstName']); |
||||
661 | } |
||||
662 | |||||
663 | public function testApiAccessWithPOST() |
||||
664 | { |
||||
665 | $urlSafeClassname = $this->urlSafeClassname(RestfulServerTestAuthorRating::class); |
||||
666 | $url = "{$this->baseURI}/api/v1/$urlSafeClassname/"; |
||||
667 | $data = [ |
||||
668 | 'Rating' => '42', |
||||
669 | 'WriteProtectedField' => 'haxx0red' |
||||
670 | ]; |
||||
671 | $response = Director::test($url, $data, null, 'POST'); |
||||
672 | // Assumption: XML is default output |
||||
673 | $responseArr = Convert::xml2array($response->getBody()); |
||||
674 | $this->assertEquals(42, $responseArr['Rating']); |
||||
675 | $this->assertNotEquals('haxx0red', $responseArr['WriteProtectedField']); |
||||
676 | } |
||||
677 | |||||
678 | public function testFieldAliasWithPOST() |
||||
679 | { |
||||
680 | Config::inst()->set(RestfulServerTestAuthorRating::class, 'api_field_mapping', ['rate' => 'Rating']); |
||||
681 | $urlSafeClassname = $this->urlSafeClassname(RestfulServerTestAuthorRating::class); |
||||
682 | $url = "{$this->baseURI}/api/v1/$urlSafeClassname/"; |
||||
683 | $data = [ |
||||
684 | 'rate' => '42', |
||||
685 | ]; |
||||
686 | $response = Director::test($url, $data, null, 'POST'); |
||||
687 | $responseArr = Convert::xml2array($response->getBody()); |
||||
688 | $this->assertEquals(42, $responseArr['rate']); |
||||
689 | } |
||||
690 | |||||
691 | public function testCanViewRespectedInList() |
||||
692 | { |
||||
693 | // Default content type |
||||
694 | $urlSafeClassname = $this->urlSafeClassname(RestfulServerTestSecretThing::class); |
||||
695 | $url = "{$this->baseURI}/api/v1/$urlSafeClassname/"; |
||||
696 | $response = Director::test($url, null, null, 'GET'); |
||||
697 | $this->assertEquals(200, $response->getStatusCode()); |
||||
698 | $this->assertNotContains('Unspeakable', $response->getBody()); |
||||
699 | |||||
700 | // JSON content type |
||||
701 | $url = "{$this->baseURI}/api/v1/$urlSafeClassname.json"; |
||||
702 | $response = Director::test($url, null, null, 'GET'); |
||||
703 | $this->assertEquals(200, $response->getStatusCode()); |
||||
704 | $this->assertNotContains('Unspeakable', $response->getBody()); |
||||
705 | $responseArray = json_decode($response->getBody(), true); |
||||
706 | $this->assertSame(0, $responseArray['totalSize']); |
||||
707 | |||||
708 | // With authentication |
||||
709 | $_SERVER['PHP_AUTH_USER'] = '[email protected]'; |
||||
710 | $_SERVER['PHP_AUTH_PW'] = 'editor'; |
||||
711 | $urlSafeClassname = $this->urlSafeClassname(RestfulServerTestSecretThing::class); |
||||
712 | $url = "{$this->baseURI}/api/v1/$urlSafeClassname/"; |
||||
713 | $response = Director::test($url, null, null, 'GET'); |
||||
714 | $this->assertEquals(200, $response->getStatusCode()); |
||||
715 | $this->assertContains('Unspeakable', $response->getBody()); |
||||
716 | // Assumption: default formatter is XML |
||||
717 | $responseArray = Convert::xml2array($response->getBody()); |
||||
718 | $this->assertEquals(1, $responseArray['@attributes']['totalSize']); |
||||
719 | unset($_SERVER['PHP_AUTH_USER']); |
||||
720 | unset($_SERVER['PHP_AUTH_PW']); |
||||
721 | } |
||||
722 | |||||
723 | public function testValidationErrorWithPOST() |
||||
724 | { |
||||
725 | $urlSafeClassname = $this->urlSafeClassname(RestfulServerTestValidationFailure::class); |
||||
726 | $url = "{$this->baseURI}/api/v1/$urlSafeClassname/"; |
||||
727 | $data = [ |
||||
728 | 'Content' => 'Test', |
||||
729 | ]; |
||||
730 | $response = Director::test($url, $data, null, 'POST'); |
||||
731 | // Assumption: XML is default output |
||||
732 | $responseArr = Convert::xml2array($response->getBody()); |
||||
733 | $this->assertEquals('SilverStripe\\ORM\\ValidationException', $responseArr['type']); |
||||
734 | } |
||||
735 | |||||
736 | public function testExceptionThrownWithPOST() |
||||
737 | { |
||||
738 | $urlSafeClassname = $this->urlSafeClassname(RestfulServerTestExceptionThrown::class); |
||||
739 | $url = "{$this->baseURI}/api/v1/$urlSafeClassname/"; |
||||
740 | $data = [ |
||||
741 | 'Content' => 'Test', |
||||
742 | ]; |
||||
743 | $response = Director::test($url, $data, null, 'POST'); |
||||
744 | // Assumption: XML is default output |
||||
745 | $responseArr = Convert::xml2array($response->getBody()); |
||||
746 | $this->assertEquals(\Exception::class, $responseArr['type']); |
||||
747 | } |
||||
748 | |||||
749 | public function testParseClassName() |
||||
750 | { |
||||
751 | $manyMany = RestfulServerTestAuthor::config()->get('many_many'); |
||||
752 | |||||
753 | // simple syntax (many many standard) |
||||
754 | $className = RestfulServer::parseRelationClass($manyMany['RelatedPages']); |
||||
755 | $this->assertEquals(RestfulServerTestPage::class, $className); |
||||
756 | |||||
757 | // array syntax (many many through) |
||||
758 | $className = RestfulServer::parseRelationClass($manyMany['SortedPages']); |
||||
759 | $this->assertEquals(RestfulServerTestPage::class, $className); |
||||
760 | } |
||||
761 | } |
||||
762 |
The issue could also be caused by a filter entry in the build configuration. If the path has been excluded in your configuration, e.g.
excluded_paths: ["lib/*"]
, you can move it to the dependency path list as follows:For further information see https://scrutinizer-ci.com/docs/tools/php/php-scrutinizer/#list-dependency-paths