Completed
Push — master ( c30b72...4581fb )
by Robbie
10s
created

RestfulServerTest::testGETWithFieldAlias()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 10
Code Lines 7

Duplication

Lines 0
Ratio 0 %

Importance

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

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