Passed
Push — master ( eaef7a...5f7861 )
by Robbie
03:03 queued 01:21
created

RestfulServerTest::testExceptionThrownWithPOST()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 11
Code Lines 7

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 1
eloc 7
nc 1
nop 0
dl 0
loc 11
rs 9.4285
c 0
b 0
f 0
1
<?php
2
3
namespace SilverStripe\RestfulServer\Tests;
4
5
use SilverStripe\RestfulServer\Tests\Stubs\RestfulServerTestComment;
6
use SilverStripe\RestfulServer\Tests\Stubs\RestfulServerTestExceptionThrown;
7
use SilverStripe\RestfulServer\Tests\Stubs\RestfulServerTestSecretThing;
8
use SilverStripe\RestfulServer\Tests\Stubs\RestfulServerTestPage;
9
use SilverStripe\RestfulServer\Tests\Stubs\RestfulServerTestAuthor;
10
use SilverStripe\RestfulServer\Tests\Stubs\RestfulServerTestAuthorRating;
11
use SilverStripe\Control\Director;
12
use SilverStripe\Core\Convert;
13
use SilverStripe\Control\Controller;
14
use SilverStripe\RestfulServer\Tests\Stubs\RestfulServerTestValidationFailure;
15
use SilverStripe\Security\Member;
16
use SilverStripe\Security\Security;
17
use SilverStripe\ORM\DataObject;
18
use SilverStripe\Dev\SapphireTest;
19
use SilverStripe\RestfulServer\DataFormatter\JSONDataFormatter;
20
use Page;
0 ignored issues
show
Bug introduced by
The type Page was not found. Maybe you did not declare it correctly or list all dependencies?

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:

filter:
    dependency_paths: ["lib/*"]

For further information see https://scrutinizer-ci.com/docs/tools/php/php-scrutinizer/#list-dependency-paths

Loading history...
21
use SilverStripe\Core\Config\Config;
22
23
/**
24
 *
25
 * @todo Test Relation getters
26
 * @todo Test filter and limit through GET params
27
 * @todo Test DELETE verb
28
 *
29
 */
30
class RestfulServerTest extends SapphireTest
31
{
32
    protected static $fixture_file = 'RestfulServerTest.yml';
33
34
    protected $baseURI = 'http://www.fakesite.test';
35
36
    protected static $extra_dataobjects = [
37
        RestfulServerTestComment::class,
38
        RestfulServerTestSecretThing::class,
39
        RestfulServerTestPage::class,
40
        RestfulServerTestAuthor::class,
41
        RestfulServerTestAuthorRating::class,
42
        RestfulServerTestValidationFailure::class,
43
        RestfulServerTestExceptionThrown::class,
44
    ];
45
46
    protected function urlSafeClassname($classname)
47
    {
48
        return str_replace('\\', '-', $classname);
49
    }
50
51
    protected function setUp()
52
    {
53
        parent::setUp();
54
        Director::config()->set('alternate_base_url', $this->baseURI);
55
        Security::setCurrentUser(null);
56
    }
57
58
    public function testApiAccess()
59
    {
60
        $comment1 = $this->objFromFixture(RestfulServerTestComment::class, 'comment1');
61
        $page1 = $this->objFromFixture(RestfulServerTestPage::class, 'page1');
62
63
        // normal GET should succeed with $api_access enabled
64
        $urlSafeClassname = $this->urlSafeClassname(RestfulServerTestComment::class);
65
        $url = "{$this->baseURI}/api/v1/$urlSafeClassname/" . $comment1->ID;
66
67
        $response = Director::test($url, null, null, 'GET');
68
        $this->assertEquals(200, $response->getStatusCode());
69
70
        $_SERVER['PHP_AUTH_USER'] = '[email protected]';
71
        $_SERVER['PHP_AUTH_PW'] = 'user';
72
73
        // even with logged in user a GET with $api_access disabled should fail
74
        $urlSafeClassname = $this->urlSafeClassname(RestfulServerTestPage::class);
75
        $url = "{$this->baseURI}/api/v1/$urlSafeClassname/" . $page1->ID;
76
        $response = Director::test($url, null, null, 'GET');
77
        $this->assertEquals(401, $response->getStatusCode());
78
79
        unset($_SERVER['PHP_AUTH_USER']);
80
        unset($_SERVER['PHP_AUTH_PW']);
81
    }
82
83
    public function testApiAccessBoolean()
84
    {
85
        $comment1 = $this->objFromFixture(RestfulServerTestComment::class, 'comment1');
86
87
        $urlSafeClassname = $this->urlSafeClassname(RestfulServerTestComment::class);
88
        $url = "{$this->baseURI}/api/v1/$urlSafeClassname/" . $comment1->ID;
89
        $response = Director::test($url, null, null, 'GET');
90
        $this->assertContains('<ID>', $response->getBody());
91
        $this->assertContains('<Name>', $response->getBody());
92
        $this->assertContains('<Comment>', $response->getBody());
93
        $this->assertContains('<Page', $response->getBody());
94
        $this->assertContains('<Author', $response->getBody());
95
    }
96
97
    public function testAuthenticatedGET()
98
    {
99
        $thing1 = $this->objFromFixture(RestfulServerTestSecretThing::class, 'thing1');
100
        $comment1 = $this->objFromFixture(RestfulServerTestComment::class, 'comment1');
101
102
        // @todo create additional mock object with authenticated VIEW permissions
103
        $urlSafeClassname = $this->urlSafeClassname(RestfulServerTestSecretThing::class);
104
        $url = "{$this->baseURI}/api/v1/$urlSafeClassname/" . $thing1->ID;
105
        $response = Director::test($url, null, null, 'GET');
106
        $this->assertEquals(401, $response->getStatusCode());
107
108
        $_SERVER['PHP_AUTH_USER'] = '[email protected]';
109
        $_SERVER['PHP_AUTH_PW'] = 'user';
110
111
        $urlSafeClassname = $this->urlSafeClassname(RestfulServerTestComment::class);
112
        $url = "{$this->baseURI}/api/v1/$urlSafeClassname/" . $comment1->ID;
113
        $response = Director::test($url, null, null, 'GET');
114
        $this->assertEquals(200, $response->getStatusCode());
115
116
        unset($_SERVER['PHP_AUTH_USER']);
117
        unset($_SERVER['PHP_AUTH_PW']);
118
    }
119
120
    public function testGETWithFieldAlias()
121
    {
122
        Config::inst()->set(RestfulServerTestAuthorRating::class, 'api_field_mapping', ['rate' => 'Rating']);
0 ignored issues
show
Bug introduced by
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 ignore-call  annotation

122
        Config::inst()->/** @scrutinizer ignore-call */ set(RestfulServerTestAuthorRating::class, 'api_field_mapping', ['rate' => 'Rating']);
Loading history...
123
        $rating1 = $this->objFromFixture(RestfulServerTestAuthorRating::class, 'rating1');
124
125
        $urlSafeClassname = $this->urlSafeClassname(RestfulServerTestAuthorRating::class);
126
        $url = "{$this->baseURI}/api/v1/$urlSafeClassname/" . $rating1->ID;
127
        $response = Director::test($url, null, null, 'GET');
128
        $responseArr = Convert::xml2array($response->getBody());
129
        $this->assertEquals(3, $responseArr['rate']);
130
    }
131
132
    public function testAuthenticatedPUT()
133
    {
134
        $comment1 = $this->objFromFixture(RestfulServerTestComment::class, 'comment1');
135
136
        $urlSafeClassname = $this->urlSafeClassname(RestfulServerTestComment::class);
137
        $url = "{$this->baseURI}/api/v1/$urlSafeClassname/" . $comment1->ID;
138
        $data = array('Comment' => 'created');
139
140
        $response = Director::test($url, $data, null, 'PUT');
141
        $this->assertEquals(401, $response->getStatusCode()); // Permission failure
142
143
        $_SERVER['PHP_AUTH_USER'] = '[email protected]';
144
        $_SERVER['PHP_AUTH_PW'] = 'editor';
145
        $response = Director::test($url, $data, null, 'PUT');
146
        $this->assertEquals(200, $response->getStatusCode()); // Success
147
148
        unset($_SERVER['PHP_AUTH_USER']);
149
        unset($_SERVER['PHP_AUTH_PW']);
150
    }
151
152
    public function testGETRelationshipsXML()
153
    {
154
        $author1 = $this->objFromFixture(RestfulServerTestAuthor::class, 'author1');
155
        $rating1 = $this->objFromFixture(RestfulServerTestAuthorRating::class, 'rating1');
156
        $rating2 = $this->objFromFixture(RestfulServerTestAuthorRating::class, 'rating2');
157
158
        // @todo should be set up by fixtures, doesn't work for some reason...
159
        $author1->Ratings()->add($rating1);
160
        $author1->Ratings()->add($rating2);
161
162
        $urlSafeClassname = $this->urlSafeClassname(RestfulServerTestAuthor::class);
163
        $url = "{$this->baseURI}/api/v1/$urlSafeClassname/" . $author1->ID;
164
        $response = Director::test($url, null, null, 'GET');
165
        $this->assertEquals(200, $response->getStatusCode());
166
167
        $responseArr = Convert::xml2array($response->getBody());
168
        $xmlTagSafeClassName = $this->urlSafeClassname(RestfulServerTestAuthorRating::class);
169
        $ratingsArr = $responseArr['Ratings'][$xmlTagSafeClassName];
170
        $this->assertEquals(2, count($ratingsArr));
171
        $ratingIDs = array(
172
            (int)$ratingsArr[0]['@attributes']['id'],
173
            (int)$ratingsArr[1]['@attributes']['id']
174
        );
175
        $this->assertContains($rating1->ID, $ratingIDs);
176
        $this->assertContains($rating2->ID, $ratingIDs);
177
    }
178
179
    public function testGETRelationshipsWithAlias()
180
    {
181
        // Alias do not currently work with Relationships
182
        Config::inst()->set(RestfulServerTestAuthor::class, 'api_field_mapping', ['stars' => 'Ratings']);
183
        $author1 = $this->objFromFixture(RestfulServerTestAuthor::class, 'author1');
184
        $rating1 = $this->objFromFixture(RestfulServerTestAuthorRating::class, 'rating1');
185
186
        // @todo should be set up by fixtures, doesn't work for some reason...
187
        $author1->Ratings()->add($rating1);
188
189
        $urlSafeClassname = $this->urlSafeClassname(RestfulServerTestAuthor::class);
190
        $url = "{$this->baseURI}/api/v1/$urlSafeClassname/" . $author1->ID . '?add_fields=stars';
191
        $response = Director::test($url, null, null, 'GET');
192
        $this->assertEquals(200, $response->getStatusCode());
193
194
        $responseArr = Convert::xml2array($response->getBody());
195
        $xmlTagSafeClassName = $this->urlSafeClassname(RestfulServerTestAuthorRating::class);
0 ignored issues
show
Unused Code introduced by
The assignment to $xmlTagSafeClassName is dead and can be removed.
Loading history...
196
197
        $this->assertTrue(array_key_exists('Ratings', $responseArr));
198
        $this->assertFalse(array_key_exists('stars', $responseArr));
199
    }
200
201
    public function testGETManyManyRelationshipsXML()
202
    {
203
        // author4 has related authors author2 and author3
204
        $author2 = $this->objFromFixture(RestfulServerTestAuthor::class, 'author2');
205
        $author3 = $this->objFromFixture(RestfulServerTestAuthor::class, 'author3');
206
        $author4 = $this->objFromFixture(RestfulServerTestAuthor::class, 'author4');
207
208
        $urlSafeClassname = $this->urlSafeClassname(RestfulServerTestAuthor::class);
209
        $url = "{$this->baseURI}/api/v1/$urlSafeClassname/" . $author4->ID . '/RelatedAuthors';
210
        $response = Director::test($url, null, null, 'GET');
211
        $this->assertEquals(200, $response->getStatusCode());
212
        $arr = Convert::xml2array($response->getBody());
213
        $xmlSafeClassName = $this->urlSafeClassname(RestfulServerTestAuthor::class);
214
        $authorsArr = $arr[$xmlSafeClassName];
215
216
        $this->assertEquals(2, count($authorsArr));
217
        $ratingIDs = array(
218
            (int)$authorsArr[0]['ID'],
219
            (int)$authorsArr[1]['ID']
220
        );
221
        $this->assertContains($author2->ID, $ratingIDs);
222
        $this->assertContains($author3->ID, $ratingIDs);
223
    }
224
225
    public function testPUTWithFormEncoded()
226
    {
227
        $comment1 = $this->objFromFixture(RestfulServerTestComment::class, 'comment1');
228
229
        $_SERVER['PHP_AUTH_USER'] = '[email protected]';
230
        $_SERVER['PHP_AUTH_PW'] = 'editor';
231
232
        $urlSafeClassname = $this->urlSafeClassname(RestfulServerTestComment::class);
233
        $url = "{$this->baseURI}/api/v1/$urlSafeClassname/" . $comment1->ID;
234
        $body = 'Name=Updated Comment&Comment=updated';
235
        $headers = array(
236
            'Content-Type' => 'application/x-www-form-urlencoded'
237
        );
238
        $response = Director::test($url, null, null, 'PUT', $body, $headers);
239
        $this->assertEquals(200, $response->getStatusCode()); // Success
240
        // Assumption: XML is default output
241
        $responseArr = Convert::xml2array($response->getBody());
242
        $this->assertEquals($comment1->ID, $responseArr['ID']);
243
        $this->assertEquals('updated', $responseArr['Comment']);
244
        $this->assertEquals('Updated Comment', $responseArr['Name']);
245
246
        unset($_SERVER['PHP_AUTH_USER']);
247
        unset($_SERVER['PHP_AUTH_PW']);
248
    }
249
250
    public function testPOSTWithFormEncoded()
251
    {
252
        $comment1 = $this->objFromFixture(RestfulServerTestComment::class, 'comment1');
253
254
        $_SERVER['PHP_AUTH_USER'] = '[email protected]';
255
        $_SERVER['PHP_AUTH_PW'] = 'editor';
256
257
        $urlSafeClassname = $this->urlSafeClassname(RestfulServerTestComment::class);
258
        $url = "{$this->baseURI}/api/v1/$urlSafeClassname";
259
        $body = 'Name=New Comment&Comment=created';
260
        $headers = array(
261
            'Content-Type' => 'application/x-www-form-urlencoded'
262
        );
263
        $response = Director::test($url, null, null, 'POST', $body, $headers);
264
        $this->assertEquals(201, $response->getStatusCode()); // Created
265
        // Assumption: XML is default output
266
        $responseArr = Convert::xml2array($response->getBody());
267
        $this->assertTrue($responseArr['ID'] > 0);
268
        $this->assertNotEquals($responseArr['ID'], $comment1->ID);
269
        $this->assertEquals('created', $responseArr['Comment']);
270
        $this->assertEquals('New Comment', $responseArr['Name']);
271
        $this->assertEquals(
272
            Controller::join_links($url, $responseArr['ID'] . '.xml'),
273
            $response->getHeader('Location')
274
        );
275
276
        unset($_SERVER['PHP_AUTH_USER']);
277
        unset($_SERVER['PHP_AUTH_PW']);
278
    }
279
280
    public function testPostWithoutBodyReturnsNoContent()
281
    {
282
        $_SERVER['PHP_AUTH_USER'] = '[email protected]';
283
        $_SERVER['PHP_AUTH_PW'] = 'editor';
284
285
        $url = "{$this->baseURI}/api/v1/" . RestfulServerTestComment::class;
286
        $response = Director::test($url, null, null, 'POST');
287
288
        $this->assertEquals('No Content', $response->getBody());
289
290
        unset($_SERVER['PHP_AUTH_USER'], $_SERVER['PHP_AUTH_PW']);
291
    }
292
293
    public function testPUTwithJSON()
294
    {
295
        $comment1 = $this->objFromFixture(RestfulServerTestComment::class, 'comment1');
296
297
        $_SERVER['PHP_AUTH_USER'] = '[email protected]';
298
        $_SERVER['PHP_AUTH_PW'] = 'editor';
299
300
        // by acceptance mimetype
301
        $urlSafeClassname = $this->urlSafeClassname(RestfulServerTestComment::class);
302
        $url = "{$this->baseURI}/api/v1/$urlSafeClassname/" . $comment1->ID;
303
        $body = '{"Comment":"updated"}';
304
        $response = Director::test($url, null, null, 'PUT', $body, array(
305
            'Content-Type'=>'application/json',
306
            'Accept' => 'application/json'
307
        ));
308
        $this->assertEquals(200, $response->getStatusCode()); // Updated
309
        $obj = Convert::json2obj($response->getBody());
310
        $this->assertEquals($comment1->ID, $obj->ID);
311
        $this->assertEquals('updated', $obj->Comment);
312
313
        // by extension
314
        $urlSafeClassname = $this->urlSafeClassname(RestfulServerTestComment::class);
315
        $url = "{$this->baseURI}/api/v1/$urlSafeClassname/{$comment1->ID}.json";
316
        $body = '{"Comment":"updated"}';
317
        $response = Director::test($url, null, null, 'PUT', $body);
318
        $this->assertEquals(200, $response->getStatusCode()); // Updated
319
        $this->assertEquals($url, $response->getHeader('Location'));
320
        $obj = Convert::json2obj($response->getBody());
321
        $this->assertEquals($comment1->ID, $obj->ID);
322
        $this->assertEquals('updated', $obj->Comment);
323
324
        unset($_SERVER['PHP_AUTH_USER']);
325
        unset($_SERVER['PHP_AUTH_PW']);
326
    }
327
328
    public function testPUTwithXML()
329
    {
330
        $comment1 = $this->objFromFixture(RestfulServerTestComment::class, 'comment1');
331
332
        $_SERVER['PHP_AUTH_USER'] = '[email protected]';
333
        $_SERVER['PHP_AUTH_PW'] = 'editor';
334
335
        // by mimetype
336
        $urlSafeClassname = $this->urlSafeClassname(RestfulServerTestComment::class);
337
        $url = "{$this->baseURI}/api/v1/$urlSafeClassname/" . $comment1->ID;
338
        $body = '<RestfulServerTestComment><Comment>updated</Comment></RestfulServerTestComment>';
339
        $response = Director::test($url, null, null, 'PUT', $body, array('Content-Type'=>'text/xml'));
340
        $this->assertEquals(200, $response->getStatusCode()); // Updated
341
        $obj = Convert::xml2array($response->getBody());
342
        $this->assertEquals($comment1->ID, $obj['ID']);
343
        $this->assertEquals('updated', $obj['Comment']);
344
345
        // by extension
346
        $urlSafeClassname = $this->urlSafeClassname(RestfulServerTestComment::class);
347
        $url = "{$this->baseURI}/api/v1/$urlSafeClassname/{$comment1->ID}.xml";
348
        $body = '<RestfulServerTestComment><Comment>updated</Comment></RestfulServerTestComment>';
349
        $response = Director::test($url, null, null, 'PUT', $body);
350
        $this->assertEquals(200, $response->getStatusCode()); // Updated
351
        $this->assertEquals($url, $response->getHeader('Location'));
352
        $obj = Convert::xml2array($response->getBody());
353
        $this->assertEquals($comment1->ID, $obj['ID']);
354
        $this->assertEquals('updated', $obj['Comment']);
355
356
        unset($_SERVER['PHP_AUTH_USER']);
357
        unset($_SERVER['PHP_AUTH_PW']);
358
    }
359
360
    public function testHTTPAcceptAndContentType()
361
    {
362
        $comment1 = $this->objFromFixture(RestfulServerTestComment::class, 'comment1');
363
364
        $urlSafeClassname = $this->urlSafeClassname(RestfulServerTestComment::class);
365
        $url = "{$this->baseURI}/api/v1/$urlSafeClassname/" . $comment1->ID;
366
367
        $headers = array('Accept' => 'application/json');
368
        $response = Director::test($url, null, null, 'GET', null, $headers);
369
        $this->assertEquals(200, $response->getStatusCode()); // Success
370
        $obj = Convert::json2obj($response->getBody());
371
        $this->assertEquals($comment1->ID, $obj->ID);
372
        $this->assertEquals('application/json', $response->getHeader('Content-Type'));
373
    }
374
375
    public function testNotFound()
376
    {
377
        $_SERVER['PHP_AUTH_USER'] = '[email protected]';
378
        $_SERVER['PHP_AUTH_PW'] = 'user';
379
380
        $urlSafeClassname = $this->urlSafeClassname(RestfulServerTestComment::class);
381
        $url = "{$this->baseURI}/api/v1/$urlSafeClassname/99";
382
        $response = Director::test($url, null, null, 'GET');
383
        $this->assertEquals(404, $response->getStatusCode());
384
385
        unset($_SERVER['PHP_AUTH_USER']);
386
        unset($_SERVER['PHP_AUTH_PW']);
387
    }
388
389
    public function testMethodNotAllowed()
390
    {
391
        $comment1 = $this->objFromFixture(RestfulServerTestComment::class, 'comment1');
392
393
        $urlSafeClassname = $this->urlSafeClassname(RestfulServerTestComment::class);
394
        $url = "{$this->baseURI}/api/v1/$urlSafeClassname/" . $comment1->ID;
395
        $response = Director::test($url, null, null, 'UNKNOWNHTTPMETHOD');
396
        $this->assertEquals(405, $response->getStatusCode());
397
    }
398
399
    public function testConflictOnExistingResourceWhenUsingPost()
400
    {
401
        $rating1 = $this->objFromFixture(RestfulServerTestAuthorRating::class, 'rating1');
402
403
        $urlSafeClassname = $this->urlSafeClassname(RestfulServerTestAuthorRating::class);
404
        $url = "{$this->baseURI}/api/v1/$urlSafeClassname/" . $rating1->ID;
405
        $response = Director::test($url, null, null, 'POST');
406
        $this->assertEquals(409, $response->getStatusCode());
407
    }
408
409
    public function testUnsupportedMediaType()
410
    {
411
        $_SERVER['PHP_AUTH_USER'] = '[email protected]';
412
        $_SERVER['PHP_AUTH_PW'] = 'user';
413
414
        $urlSafeClassname = $this->urlSafeClassname(RestfulServerTestComment::class);
415
        $url = "{$this->baseURI}/api/v1/$urlSafeClassname";
416
        $data = "Comment||\/||updated"; // weird format
417
        $headers = array('Content-Type' => 'text/weirdformat');
418
        $response = Director::test($url, null, null, 'POST', $data, $headers);
419
        $this->assertEquals(415, $response->getStatusCode());
420
421
        unset($_SERVER['PHP_AUTH_USER']);
422
        unset($_SERVER['PHP_AUTH_PW']);
423
    }
424
425
    public function testXMLValueFormatting()
426
    {
427
        $rating1 = $this->objFromFixture(RestfulServerTestAuthorRating::class, 'rating1');
428
429
        $urlSafeClassname = $this->urlSafeClassname(RestfulServerTestAuthorRating::class);
430
        $url = "{$this->baseURI}/api/v1/$urlSafeClassname/" . $rating1->ID;
431
        $response = Director::test($url, null, null, 'GET');
432
        $this->assertContains('<ID>' . $rating1->ID . '</ID>', $response->getBody());
433
        $this->assertContains('<Rating>' . $rating1->Rating . '</Rating>', $response->getBody());
434
    }
435
436
    public function testXMLValueFormattingWithFieldAlias()
437
    {
438
        Config::inst()->set(RestfulServerTestAuthorRating::class, 'api_field_mapping', ['rate' => 'Rating']);
439
        $rating1 = $this->objFromFixture(RestfulServerTestAuthorRating::class, 'rating1');
440
441
        $urlSafeClassname = $this->urlSafeClassname(RestfulServerTestAuthorRating::class);
442
        $url = "{$this->baseURI}/api/v1/$urlSafeClassname/" . $rating1->ID;
443
        $response = Director::test($url, null, null, 'GET');
444
        $this->assertContains('<rate>' . $rating1->Rating . '</rate>', $response->getBody());
445
    }
446
447
    public function testApiAccessFieldRestrictions()
448
    {
449
        $author1 = $this->objFromFixture(RestfulServerTestAuthor::class, 'author1');
450
        $rating1 = $this->objFromFixture(RestfulServerTestAuthorRating::class, 'rating1');
451
452
        $urlSafeClassname = $this->urlSafeClassname(RestfulServerTestAuthorRating::class);
453
        $url = "{$this->baseURI}/api/v1/$urlSafeClassname/" . $rating1->ID;
454
        $response = Director::test($url, null, null, 'GET');
455
        $this->assertContains('<ID>', $response->getBody());
456
        $this->assertContains('<Rating>', $response->getBody());
457
        $this->assertContains('<Author', $response->getBody());
458
        $this->assertNotContains('<SecretField>', $response->getBody());
459
        $this->assertNotContains('<SecretRelation>', $response->getBody());
460
461
        $urlSafeClassname = $this->urlSafeClassname(RestfulServerTestAuthorRating::class);
462
        $url = "{$this->baseURI}/api/v1/$urlSafeClassname/" . $rating1->ID . '?add_fields=SecretField,SecretRelation';
463
        $response = Director::test($url, null, null, 'GET');
464
        $this->assertNotContains(
465
            '<SecretField>',
466
            $response->getBody(),
467
            '"add_fields" URL parameter filters out disallowed fields from $api_access'
468
        );
469
        $this->assertNotContains(
470
            '<SecretRelation>',
471
            $response->getBody(),
472
            '"add_fields" URL parameter filters out disallowed relations from $api_access'
473
        );
474
475
        $urlSafeClassname = $this->urlSafeClassname(RestfulServerTestAuthorRating::class);
476
        $url = "{$this->baseURI}/api/v1/$urlSafeClassname/" . $rating1->ID . '?fields=SecretField,SecretRelation';
477
        $response = Director::test($url, null, null, 'GET');
478
        $this->assertNotContains(
479
            '<SecretField>',
480
            $response->getBody(),
481
            '"fields" URL parameter filters out disallowed fields from $api_access'
482
        );
483
        $this->assertNotContains(
484
            '<SecretRelation>',
485
            $response->getBody(),
486
            '"fields" URL parameter filters out disallowed relations from $api_access'
487
        );
488
489
        $urlSafeClassname = $this->urlSafeClassname(RestfulServerTestAuthor::class);
490
        $url = "{$this->baseURI}/api/v1/$urlSafeClassname/" . $author1->ID . '/Ratings';
491
        $response = Director::test($url, null, null, 'GET');
492
        $this->assertContains(
493
            '<Rating>',
494
            $response->getBody(),
495
            'Relation viewer shows fields allowed through $api_access'
496
        );
497
        $this->assertNotContains(
498
            '<SecretField>',
499
            $response->getBody(),
500
            'Relation viewer on has-many filters out disallowed fields from $api_access'
501
        );
502
    }
503
504
    public function testApiAccessRelationRestrictionsInline()
505
    {
506
        $author1 = $this->objFromFixture(RestfulServerTestAuthor::class, 'author1');
507
508
        $urlSafeClassname = $this->urlSafeClassname(RestfulServerTestAuthor::class);
509
        $url = "{$this->baseURI}/api/v1/$urlSafeClassname/" . $author1->ID;
510
        $response = Director::test($url, null, null, 'GET');
511
        $this->assertNotContains('<RelatedPages', $response->getBody(), 'Restricts many-many with api_access=false');
512
        $this->assertNotContains('<PublishedPages', $response->getBody(), 'Restricts has-many with api_access=false');
513
    }
514
515
    public function testApiAccessRelationRestrictionsOnEndpoint()
516
    {
517
        $author1 = $this->objFromFixture(RestfulServerTestAuthor::class, 'author1');
518
519
        $urlSafeClassname = $this->urlSafeClassname(RestfulServerTestAuthor::class);
520
        $url = "{$this->baseURI}/api/v1/$urlSafeClassname/" . $author1->ID . "/ProfilePage";
521
        $response = Director::test($url, null, null, 'GET');
522
        $this->assertEquals(404, $response->getStatusCode(), 'Restricts has-one with api_access=false');
523
524
        $urlSafeClassname = $this->urlSafeClassname(RestfulServerTestAuthor::class);
525
        $url = "{$this->baseURI}/api/v1/$urlSafeClassname/" . $author1->ID . "/RelatedPages";
526
        $response = Director::test($url, null, null, 'GET');
527
        $this->assertEquals(404, $response->getStatusCode(), 'Restricts many-many with api_access=false');
528
529
        $urlSafeClassname = $this->urlSafeClassname(RestfulServerTestAuthor::class);
530
        $url = "{$this->baseURI}/api/v1/$urlSafeClassname/" . $author1->ID . "/PublishedPages";
531
        $response = Director::test($url, null, null, 'GET');
532
        $this->assertEquals(404, $response->getStatusCode(), 'Restricts has-many with api_access=false');
533
    }
534
535
    public function testApiAccessWithPUT()
536
    {
537
        $rating1 = $this->objFromFixture(RestfulServerTestAuthorRating::class, 'rating1');
538
539
        $urlSafeClassname = $this->urlSafeClassname(RestfulServerTestAuthorRating::class);
540
        $url = "{$this->baseURI}/api/v1/$urlSafeClassname/" . $rating1->ID;
541
        $data = array(
542
            'Rating' => '42',
543
            'WriteProtectedField' => 'haxx0red'
544
        );
545
        $response = Director::test($url, $data, null, 'PUT');
546
        // Assumption: XML is default output
547
        $responseArr = Convert::xml2array($response->getBody());
548
        $this->assertEquals(42, $responseArr['Rating']);
549
        $this->assertNotEquals('haxx0red', $responseArr['WriteProtectedField']);
550
    }
551
552
    public function testFieldAliasWithPUT()
553
    {
554
        Config::inst()->set(RestfulServerTestAuthorRating::class, 'api_field_mapping', ['rate' => 'Rating']);
555
        $rating1 = $this->objFromFixture(RestfulServerTestAuthorRating::class, 'rating1');
556
        $urlSafeClassname = $this->urlSafeClassname(RestfulServerTestAuthorRating::class);
557
        $url = "{$this->baseURI}/api/v1/$urlSafeClassname/" . $rating1->ID;
558
        // Test input with original fieldname
559
        $data = array(
560
            'Rating' => '42',
561
        );
562
        $response = Director::test($url, $data, null, 'PUT');
563
        // Assumption: XML is default output
564
        $responseArr = Convert::xml2array($response->getBody());
565
        // should output with aliased name
566
        $this->assertEquals(42, $responseArr['rate']);
567
    }
568
569
    public function testJSONDataFormatter()
570
    {
571
        $formatter = new JSONDataFormatter();
572
        $editor = $this->objFromFixture(Member::class, 'editor');
573
        $user = $this->objFromFixture(Member::class, 'user');
574
575
        // The DataFormatter performs canView calls
576
        // these are `Member`s so we need to be ADMIN types
577
        $this->logInWithPermission('ADMIN');
578
579
        $this->assertEquals(
580
            '{"FirstName":"Editor","Email":"[email protected]"}',
581
            $formatter->convertDataObject($editor, ["FirstName", "Email"]),
582
            "Correct JSON formatting with field subset"
583
        );
584
585
        $set = Member::get()
586
            ->filter('ID', [$editor->ID, $user->ID])
587
            ->sort('"Email" ASC'); // for sorting for postgres
588
        $this->assertEquals(
589
            '{"totalSize":null,"items":[{"FirstName":"Editor","Email":"[email protected]"},' .
590
                '{"FirstName":"User","Email":"[email protected]"}]}',
591
            $formatter->convertDataObjectSet($set, ["FirstName", "Email"]),
592
            "Correct JSON formatting on a dataobjectset with field filter"
593
        );
594
    }
595
596
    public function testJSONDataFormatterWithFieldAlias()
597
    {
598
        Config::inst()->set(Member::class, 'api_field_mapping', ['MyName' => 'FirstName']);
599
        $formatter = new JSONDataFormatter();
600
        $editor = $this->objFromFixture(Member::class, 'editor');
601
        $user = $this->objFromFixture(Member::class, 'user');
602
603
        // The DataFormatter performs canView calls
604
        // these are `Member`s so we need to be ADMIN types
605
        $this->logInWithPermission('ADMIN');
606
607
        $set = Member::get()
608
            ->filter('ID', [$editor->ID, $user->ID])
609
            ->sort('"Email" ASC'); // for sorting for postgres
610
611
        $this->assertEquals(
612
            '{"totalSize":null,"items":[{"MyName":"Editor","Email":"[email protected]"},' .
613
                '{"MyName":"User","Email":"[email protected]"}]}',
614
            $formatter->convertDataObjectSet($set, ["FirstName", "Email"]),
615
            "Correct JSON formatting with field alias"
616
        );
617
    }
618
619
    public function testApiAccessWithPOST()
620
    {
621
        $urlSafeClassname = $this->urlSafeClassname(RestfulServerTestAuthorRating::class);
622
        $url = "{$this->baseURI}/api/v1/$urlSafeClassname/";
623
        $data = [
624
            'Rating' => '42',
625
            'WriteProtectedField' => 'haxx0red'
626
        ];
627
        $response = Director::test($url, $data, null, 'POST');
628
        // Assumption: XML is default output
629
        $responseArr = Convert::xml2array($response->getBody());
630
        $this->assertEquals(42, $responseArr['Rating']);
631
        $this->assertNotEquals('haxx0red', $responseArr['WriteProtectedField']);
632
    }
633
634
    public function testFieldAliasWithPOST()
635
    {
636
        Config::inst()->set(RestfulServerTestAuthorRating::class, 'api_field_mapping', ['rate' => 'Rating']);
637
        $urlSafeClassname = $this->urlSafeClassname(RestfulServerTestAuthorRating::class);
638
        $url = "{$this->baseURI}/api/v1/$urlSafeClassname/";
639
        $data = [
640
            'rate' => '42',
641
        ];
642
        $response = Director::test($url, $data, null, 'POST');
643
        $responseArr = Convert::xml2array($response->getBody());
644
        $this->assertEquals(42, $responseArr['rate']);
645
    }
646
647
    public function testCanViewRespectedInList()
648
    {
649
        // Default content type
650
        $urlSafeClassname = $this->urlSafeClassname(RestfulServerTestSecretThing::class);
651
        $url = "{$this->baseURI}/api/v1/$urlSafeClassname/";
652
        $response = Director::test($url, null, null, 'GET');
653
        $this->assertEquals(200, $response->getStatusCode());
654
        $this->assertNotContains('Unspeakable', $response->getBody());
655
656
        // JSON content type
657
        $url = "{$this->baseURI}/api/v1/$urlSafeClassname.json";
658
        $response = Director::test($url, null, null, 'GET');
659
        $this->assertEquals(200, $response->getStatusCode());
660
        $this->assertNotContains('Unspeakable', $response->getBody());
661
        $responseArray = Convert::json2array($response->getBody());
662
        $this->assertSame(0, $responseArray['totalSize']);
663
664
        // With authentication
665
        $_SERVER['PHP_AUTH_USER'] = '[email protected]';
666
        $_SERVER['PHP_AUTH_PW'] = 'editor';
667
        $urlSafeClassname = $this->urlSafeClassname(RestfulServerTestSecretThing::class);
668
        $url = "{$this->baseURI}/api/v1/$urlSafeClassname/";
669
        $response = Director::test($url, null, null, 'GET');
670
        $this->assertEquals(200, $response->getStatusCode());
671
        $this->assertContains('Unspeakable', $response->getBody());
672
        // Assumption: default formatter is XML
673
        $responseArray = Convert::xml2array($response->getBody());
674
        $this->assertEquals(1, $responseArray['@attributes']['totalSize']);
675
        unset($_SERVER['PHP_AUTH_USER']);
676
        unset($_SERVER['PHP_AUTH_PW']);
677
    }
678
679
    public function testValidationErrorWithPOST()
680
    {
681
        $urlSafeClassname = $this->urlSafeClassname(RestfulServerTestValidationFailure::class);
682
        $url = "{$this->baseURI}/api/v1/$urlSafeClassname/";
683
        $data = [
684
            'Content' => 'Test',
685
        ];
686
        $response = Director::test($url, $data, null, 'POST');
687
        // Assumption: XML is default output
688
        $responseArr = Convert::xml2array($response->getBody());
689
        $this->assertEquals('SilverStripe\\ORM\\ValidationException', $responseArr['type']);
690
    }
691
692
    public function testExceptionThrownWithPOST()
693
    {
694
        $urlSafeClassname = $this->urlSafeClassname(RestfulServerTestExceptionThrown::class);
695
        $url = "{$this->baseURI}/api/v1/$urlSafeClassname/";
696
        $data = [
697
            'Content' => 'Test',
698
        ];
699
        $response = Director::test($url, $data, null, 'POST');
700
        // Assumption: XML is default output
701
        $responseArr = Convert::xml2array($response->getBody());
702
        $this->assertEquals(\Exception::class, $responseArr['type']);
703
    }
704
}
705