This commit is contained in:
ant1234 2020-01-24 07:51:33 +13:00 committed by GitHub
commit f11d470abd
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
4 changed files with 125 additions and 14 deletions

View File

@ -8,8 +8,8 @@
## Overview
This class gives your application a RESTful API. All you have to do is set the `api_access` configuration option to `true`
on the appropriate DataObjects. You will need to ensure that all of your data manipulation and security is defined in
your model layer (ie, the DataObject classes) and not in your Controllers. This is the recommended design for SilverStripe
on the appropriate `DataObject`. You will need to ensure that all of your data manipulation and security is defined in
your model layer (ie, the `DataObject` classes) and not in your Controllers. This is the recommended design for SilverStripe
applications.
## Requirements
@ -20,7 +20,7 @@ For a SilverStripe 3.x compatible version of this module, please see the [1.0 br
## Configuration
Example DataObject with simple API access, giving full access to all object properties and relations,
Example `DataObject` with simple API access, giving full access to all object properties and relations,
unless explicitly controlled through model permissions.
```php
@ -39,7 +39,7 @@ class Article extends DataObject {
}
```
Example DataObject with advanced API access, limiting viewing and editing to Title attribute only:
Example `DataObject` with advanced API access, limiting viewing and editing to the "Title" attribute only:
```php
namespace Vendor\Project;
@ -60,7 +60,7 @@ class Article extends DataObject {
}
```
Example DataObject field mapping, allows aliasing fields so that public requests and responses display different field names:
Example `DataObject` field mapping, allows aliasing fields so that public requests and responses display different field names:
```php
namespace Vendor\Project;
@ -83,7 +83,66 @@ class Article extends DataObject {
];
}
```
Given a dataobject with values:
Example `DataObject` `HasMany` and `ManyMany` field-display handling. Only available on `JSONDataFormatter`. Declaring a `getApiFields` method in your `DataObject` (or an `Extension` subclass) allows additional fields to be shown on those relations, in addition to "id", "className" and "href":
```php
namespace Vendor\Project;
use SilverStripe\ORM\DataObject;
class Article extends DataObject {
private static $db = [
'Title'=>'Text',
'Published'=>'Boolean'
];
private static $api_access = true;
/**
* @param array $baseFields
* @return array
*/
public function getApiFields($baseFields)
{
return [
'Title' => $this->Title,
];
}
}
```
Example `DataObject` `HasMany` and `ManyMany` field-display handling. Only available on `JSONDataFormatter`. Declaring a `getApiFields` method in your `DataObject` (or an `Extension` subclass) allows existing fields that the formatter returns (like "id", "className" and "href"), to be overloaded:
```php
namespace Vendor\Project;
use SilverStripe\ORM\DataObject;
class Article extends DataObject {
private static $db = [
'Title'=>'Text',
'Published'=>'Boolean'
];
private static $api_access = true;
/**
* @param array $baseFields
* @return array
*/
public function getApiFields($baseFields)
{
return [
'href' => $this->myHrefOverrideMethod($baseFields['href']),
];
}
}
```
Given a `DataObject` with values:
```yml
ID: 12
Title: Title Value

View File

@ -10,6 +10,8 @@ use SilverStripe\ORM\DataObjectInterface;
use SilverStripe\Control\Director;
use SilverStripe\ORM\SS_List;
use SilverStripe\ORM\FieldType;
use SilverStripe\Core\ClassInfo;
use SilverStripe\ORM\DataObject;
/**
* Formats a DataObject's member fields into a JSON string
@ -118,10 +120,16 @@ class JSONDataFormatter extends DataFormatter
? $this->sanitiseClassName($relClass) . '/' . $obj->$fieldName
: $this->sanitiseClassName($className) . "/$id/$relName";
$href = Director::absoluteURL($rel);
$serobj->$relName = ArrayData::array_to_object(array(
$component = $obj->getField($relName);
$baseFields = [
"className" => $relClass,
"href" => "$href.json",
"id" => self::cast($obj->obj($fieldName))
"id" => self::cast($obj->obj($fieldName)),
];
$serobj->$relName = ArrayData::array_to_object(array_replace(
$baseFields,
ClassInfo::hasMethod($component, 'getApiFields') ? (array) $component->getApiFields($baseFields) : []
));
}
@ -152,10 +160,14 @@ class JSONDataFormatter extends DataFormatter
}
$rel = $this->config()->api_base . $this->sanitiseClassName($relClass) . "/$item->ID";
$href = Director::absoluteURL($rel);
$innerParts[] = ArrayData::array_to_object(array(
$baseFields = [
"className" => $relClass,
"href" => "$href.json",
"id" => $item->ID
];
$innerParts[] = ArrayData::array_to_object(array_replace(
$baseFields,
ClassInfo::hasMethod($item, 'getApiFields') ? (array) $item->getApiFields($baseFields) : []
));
}
$serobj->$relName = $innerParts;

View File

@ -3,16 +3,15 @@
namespace SilverStripe\RestfulServer\Tests;
use SilverStripe\RestfulServer\RestfulServer;
use SilverStripe\ORM\DataObject;
use SilverStripe\RestfulServer\Tests\Stubs\JSONDataFormatterTypeTestObject;
use SilverStripe\Core\Config\Config;
use SilverStripe\Dev\SapphireTest;
use SilverStripe\RestfulServer\DataFormatter\JSONDataFormatter;
/**
*
* @todo Test Relation getters
* @todo Test filter and limit through GET params
* @todo Test DELETE verb
*
* Tests improvements made to JsonTypes,
* calls method which appends more fields
*/
class JSONDataFormatterTest extends SapphireTest
{
@ -22,11 +21,20 @@ class JSONDataFormatterTest extends SapphireTest
JSONDataFormatterTypeTestObject::class,
];
protected $usesDatabase = true;
public function testJSONTypes()
{
// Needed as private static $api_access = true; doesn't seem to work on the stub file
Config::inst()->update(JSONDataFormatterTypeTestObject::class, 'api_access', true);
// Grab test object
$formatter = new JSONDataFormatter();
$parent = $this->objFromFixture(JSONDataFormatterTypeTestObject::class, 'parent');
$json = $formatter->convertDataObject($parent);
$this->assertRegexp('/"ID":\d+/', $json, 'PK casted to integer');
$this->assertRegexp('/"Created":"\d{4}-\d{2}-\d{2} \d{2}:\d{2}:\d{2}"/', $json, 'Datetime casted to string');
$this->assertContains('"Name":"Parent"', $json, 'String casted to string');
@ -37,10 +45,35 @@ class JSONDataFormatterTest extends SapphireTest
$child3 = $this->objFromFixture(JSONDataFormatterTypeTestObject::class, 'child3');
$json = $formatter->convertDataObject($child3);
$this->assertContains('"Name":null', $json, 'Empty string is null');
$this->assertContains('"Active":false', $json, 'Empty boolean is false');
$this->assertContains('"Sort":0', $json, 'Empty integer is 0');
$this->assertContains('"Average":0', $json, 'Empty float is 0');
$this->assertRegexp('/"ParentID":\d+/', $json, 'FK casted to integer');
$original = $this->objFromFixture(JSONDataFormatterTypeTestObject::class, 'original');
$json = json_decode($formatter->convertDataObject($original));
// Returns valid array and isn't null
$this->assertNotEmpty($json, 'Array is empty');
// Test that original fields still exist, ie id, href, and className
$standard_id = $json->Children[0]->id;
$standard_className = $json->Children[0]->className;
$standard_href = $json->Children[0]->href;
$this->assertEquals(8, $standard_id, "Standard id field not equal");
$this->assertEquals('SilverStripe\RestfulServer\Tests\Stubs\JSONDataFormatterTypeTestObject', $standard_className, "Standard className does not equal");
$this->assertEquals('http://localhost/api/v1/SilverStripe-RestfulServer-Tests-Stubs-JSONDataFormatterTypeTestObject/8.json', $standard_href, "Standard href field not equal");
// Test method improvements, more fields rather than just id, href, className
$this->assertEquals(9, $json->ID, "ID not equal");
$this->assertEquals("SilverStripe\\RestfulServer\\Tests\\Stubs\\JSONDataFormatterTypeTestObject", $json->ClassName, "Class not equal");
$this->assertEquals("Test Object", $json->Name, "Name not equal");
$this->assertEquals(false, $json->Active, "Active not equal to false");
$this->assertEquals(0, $json->Sort, "Sort not equal to 0");
$this->assertEquals(0, $json->Average, "Average not equal to 0");
$this->assertEquals(0, $json->ParentID, "ParentID not equal to 0");
}
}

View File

@ -1,4 +1,11 @@
SilverStripe\RestfulServer\Tests\Stubs\JSONDataFormatterTypeTestObject:
foo:
ID: 8
Name: Test Object 1
original:
ID: 9
Name: Test Object
Children: =>SilverStripe\RestfulServer\Tests\Stubs\JSONDataFormatterTypeTestObject.foo
parent:
Name: Parent
Active: true