mirror of
https://github.com/silverstripe/silverstripe-restfulserver
synced 2024-10-22 14:05:58 +02:00
Compare commits
No commits in common. "810f87cc4951d5a0172493ef0761e6278accc997" and "9f39cb180aed7fd481d38dca9aceb9e5b784a300" have entirely different histories.
810f87cc49
...
9f39cb180a
@ -32,6 +32,8 @@ abstract class DataFormatter
|
|||||||
* ($has_one, $has_many, $many_many).
|
* ($has_one, $has_many, $many_many).
|
||||||
* Set to "0" to disable relation output.
|
* Set to "0" to disable relation output.
|
||||||
*
|
*
|
||||||
|
* @todo Support more than one nesting level
|
||||||
|
*
|
||||||
* @var int
|
* @var int
|
||||||
*/
|
*/
|
||||||
public $relationDepth = 1;
|
public $relationDepth = 1;
|
||||||
@ -288,6 +290,9 @@ abstract class DataFormatter
|
|||||||
* Returns all fields on the object which should be shown
|
* Returns all fields on the object which should be shown
|
||||||
* in the output. Can be customised through {@link self::setCustomFields()}.
|
* in the output. Can be customised through {@link self::setCustomFields()}.
|
||||||
*
|
*
|
||||||
|
* @todo Allow for custom getters on the processed object (currently filtered through inheritedDatabaseFields)
|
||||||
|
* @todo Field level permission checks
|
||||||
|
*
|
||||||
* @param DataObject $obj
|
* @param DataObject $obj
|
||||||
* @return array
|
* @return array
|
||||||
*/
|
*/
|
||||||
@ -298,6 +303,7 @@ abstract class DataFormatter
|
|||||||
// if custom fields are specified, only select these
|
// if custom fields are specified, only select these
|
||||||
if (is_array($this->customFields)) {
|
if (is_array($this->customFields)) {
|
||||||
foreach ($this->customFields as $fieldName) {
|
foreach ($this->customFields as $fieldName) {
|
||||||
|
// @todo Possible security risk by making methods accessible - implement field-level security
|
||||||
if (($obj->hasField($fieldName) && !is_object($obj->getField($fieldName)))
|
if (($obj->hasField($fieldName) && !is_object($obj->getField($fieldName)))
|
||||||
|| $obj->hasMethod("get{$fieldName}")
|
|| $obj->hasMethod("get{$fieldName}")
|
||||||
) {
|
) {
|
||||||
@ -312,6 +318,7 @@ abstract class DataFormatter
|
|||||||
|
|
||||||
if (is_array($this->customAddFields)) {
|
if (is_array($this->customAddFields)) {
|
||||||
foreach ($this->customAddFields as $fieldName) {
|
foreach ($this->customAddFields as $fieldName) {
|
||||||
|
// @todo Possible security risk by making methods accessible - implement field-level security
|
||||||
if ($obj->hasField($fieldName) || $obj->hasMethod("get{$fieldName}")) {
|
if ($obj->hasField($fieldName) || $obj->hasMethod("get{$fieldName}")) {
|
||||||
$dbFields[$fieldName] = $fieldName;
|
$dbFields[$fieldName] = $fieldName;
|
||||||
}
|
}
|
||||||
|
@ -12,6 +12,7 @@ namespace SilverStripe\RestfulServer\DataFormatter;
|
|||||||
* curl -X PUT -d "Name=This is an updated record" http://host/api/v1/(DataObject)/1
|
* curl -X PUT -d "Name=This is an updated record" http://host/api/v1/(DataObject)/1
|
||||||
* </code>
|
* </code>
|
||||||
*
|
*
|
||||||
|
* @todo Format response form encoded as well - currently uses XMLDataFormatter
|
||||||
*
|
*
|
||||||
* @author Cam Spiers <camspiers at gmail dot com>
|
* @author Cam Spiers <camspiers at gmail dot com>
|
||||||
*/
|
*/
|
||||||
@ -36,5 +37,7 @@ class FormEncodedDataFormatter extends XMLDataFormatter
|
|||||||
$postArray = array();
|
$postArray = array();
|
||||||
parse_str($strData ?? '', $postArray);
|
parse_str($strData ?? '', $postArray);
|
||||||
return $postArray;
|
return $postArray;
|
||||||
|
//TODO: It would be nice to implement this function in Convert.php
|
||||||
|
//return Convert::querystr2array($strData);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -18,6 +18,7 @@ class JSONDataFormatter extends DataFormatter
|
|||||||
{
|
{
|
||||||
/**
|
/**
|
||||||
* @config
|
* @config
|
||||||
|
* @todo pass this from the API to the data formatter somehow
|
||||||
*/
|
*/
|
||||||
private static $api_base = "api/v1/";
|
private static $api_base = "api/v1/";
|
||||||
|
|
||||||
|
@ -22,6 +22,7 @@ class XMLDataFormatter extends DataFormatter
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* @config
|
* @config
|
||||||
|
* @todo pass this from the API to the data formatter somehow
|
||||||
*/
|
*/
|
||||||
private static $api_base = "api/v1/";
|
private static $api_base = "api/v1/";
|
||||||
|
|
||||||
|
@ -22,6 +22,23 @@ use SilverStripe\Security\Security;
|
|||||||
* Relies on serialization/deserialization into different formats provided
|
* Relies on serialization/deserialization into different formats provided
|
||||||
* by the DataFormatter APIs in core.
|
* by the DataFormatter APIs in core.
|
||||||
*
|
*
|
||||||
|
* @todo Implement PUT/POST/DELETE for relations
|
||||||
|
* @todo Access-Control for relations (you might be allowed to view Members and Groups,
|
||||||
|
* but not their relation with each other)
|
||||||
|
* @todo Make SearchContext specification customizeable for each class
|
||||||
|
* @todo Allow for range-searches (e.g. on Created column)
|
||||||
|
* @todo Filter relation listings by $api_access and canView() permissions
|
||||||
|
* @todo Exclude relations when "fields" are specified through URL (they should be explicitly
|
||||||
|
* requested in this case)
|
||||||
|
* @todo Custom filters per DataObject subclass, e.g. to disallow showing unpublished pages in
|
||||||
|
* SiteTree/Versioned/Hierarchy
|
||||||
|
* @todo URL parameter namespacing for search-fields, limit, fields, add_fields
|
||||||
|
* (might all be valid dataobject properties)
|
||||||
|
* e.g. you wouldn't be able to search for a "limit" property on your subclass as
|
||||||
|
* its overlayed with the search logic
|
||||||
|
* @todo i18n integration (e.g. Page/1.xml?lang=de_DE)
|
||||||
|
* @todo Access to extendable methods/relations like SiteTree/1/Versions or SiteTree/1/Version/22
|
||||||
|
* @todo Respect $api_access array notation in search contexts
|
||||||
*/
|
*/
|
||||||
class RestfulServer extends Controller
|
class RestfulServer extends Controller
|
||||||
{
|
{
|
||||||
@ -102,6 +119,7 @@ class RestfulServer extends Controller
|
|||||||
{
|
{
|
||||||
/* This sets up SiteTree the same as when viewing a page through the frontend. Versioned defaults
|
/* This sets up SiteTree the same as when viewing a page through the frontend. Versioned defaults
|
||||||
* to Stage, and then when viewing the front-end Versioned::choose_site_stage changes it to Live.
|
* to Stage, and then when viewing the front-end Versioned::choose_site_stage changes it to Live.
|
||||||
|
* TODO: In 3.2 we should make the default Live, then change to Stage in the admin area (with a nicer API)
|
||||||
*/
|
*/
|
||||||
if (class_exists(SiteTree::class)) {
|
if (class_exists(SiteTree::class)) {
|
||||||
singleton(SiteTree::class)->extend('modelascontrollerInit', $this);
|
singleton(SiteTree::class)->extend('modelascontrollerInit', $this);
|
||||||
@ -244,6 +262,8 @@ class RestfulServer extends Controller
|
|||||||
* - static $api_access must be set. This enables the API on a class by class basis
|
* - static $api_access must be set. This enables the API on a class by class basis
|
||||||
* - $obj->canView() must return true. This lets you implement record-level security
|
* - $obj->canView() must return true. This lets you implement record-level security
|
||||||
*
|
*
|
||||||
|
* @todo Access checking
|
||||||
|
*
|
||||||
* @param string $className
|
* @param string $className
|
||||||
* @param int $id
|
* @param int $id
|
||||||
* @param string $relation
|
* @param string $relation
|
||||||
@ -299,6 +319,7 @@ class RestfulServer extends Controller
|
|||||||
return $this->notFound();
|
return $this->notFound();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// TODO Avoid creating data formatter again for relation class (see above)
|
||||||
$responseFormatter = $this->getResponseDataFormatter($obj->dataClass());
|
$responseFormatter = $this->getResponseDataFormatter($obj->dataClass());
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
@ -341,6 +362,8 @@ class RestfulServer extends Controller
|
|||||||
* an existing query object (mostly a component query from {@link DataObject})
|
* an existing query object (mostly a component query from {@link DataObject})
|
||||||
* with search clauses.
|
* with search clauses.
|
||||||
*
|
*
|
||||||
|
* @todo Allow specifying of different searchcontext getters on model-by-model basis
|
||||||
|
*
|
||||||
* @param string $className
|
* @param string $className
|
||||||
* @param array $params
|
* @param array $params
|
||||||
* @return SS_List
|
* @return SS_List
|
||||||
@ -533,6 +556,9 @@ class RestfulServer extends Controller
|
|||||||
/**
|
/**
|
||||||
* Handler for object append / method call.
|
* Handler for object append / method call.
|
||||||
*
|
*
|
||||||
|
* @todo Posting to an existing URL (without a relation)
|
||||||
|
* current resolves in creatig a new element,
|
||||||
|
* rather than a "Conflict" message.
|
||||||
*/
|
*/
|
||||||
protected function postHandler($className, $id, $relation)
|
protected function postHandler($className, $id, $relation)
|
||||||
{
|
{
|
||||||
@ -652,6 +678,7 @@ class RestfulServer extends Controller
|
|||||||
$data[$newkey] = $value;
|
$data[$newkey] = $value;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// @todo Disallow editing of certain keys in database
|
||||||
$data = array_diff_key($data ?? [], ['ID', 'Created']);
|
$data = array_diff_key($data ?? [], ['ID', 'Created']);
|
||||||
|
|
||||||
$apiAccess = singleton($className)->config()->api_access;
|
$apiAccess = singleton($className)->config()->api_access;
|
||||||
@ -839,6 +866,7 @@ class RestfulServer extends Controller
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Return only relations which have $api_access enabled.
|
* Return only relations which have $api_access enabled.
|
||||||
|
* @todo Respect field level permissions once they are available in core
|
||||||
*
|
*
|
||||||
* @param string $class
|
* @param string $class
|
||||||
* @param Member $member
|
* @param Member $member
|
||||||
|
@ -7,6 +7,13 @@ use SilverStripe\RestfulServer\Tests\Stubs\JSONDataFormatterTypeTestObject;
|
|||||||
use SilverStripe\Dev\SapphireTest;
|
use SilverStripe\Dev\SapphireTest;
|
||||||
use SilverStripe\RestfulServer\DataFormatter\JSONDataFormatter;
|
use SilverStripe\RestfulServer\DataFormatter\JSONDataFormatter;
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @todo Test Relation getters
|
||||||
|
* @todo Test filter and limit through GET params
|
||||||
|
* @todo Test DELETE verb
|
||||||
|
*
|
||||||
|
*/
|
||||||
class JSONDataFormatterTest extends SapphireTest
|
class JSONDataFormatterTest extends SapphireTest
|
||||||
{
|
{
|
||||||
protected static $fixture_file = 'JSONDataFormatterTest.yml';
|
protected static $fixture_file = 'JSONDataFormatterTest.yml';
|
||||||
|
@ -22,6 +22,13 @@ use Page;
|
|||||||
use SilverStripe\Core\Config\Config;
|
use SilverStripe\Core\Config\Config;
|
||||||
use SilverStripe\RestfulServer\DataFormatter\XMLDataFormatter;
|
use SilverStripe\RestfulServer\DataFormatter\XMLDataFormatter;
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @todo Test Relation getters
|
||||||
|
* @todo Test filter and limit through GET params
|
||||||
|
* @todo Test DELETE verb
|
||||||
|
*
|
||||||
|
*/
|
||||||
class RestfulServerTest extends SapphireTest
|
class RestfulServerTest extends SapphireTest
|
||||||
{
|
{
|
||||||
protected static $fixture_file = 'RestfulServerTest.yml';
|
protected static $fixture_file = 'RestfulServerTest.yml';
|
||||||
@ -94,6 +101,7 @@ class RestfulServerTest extends SapphireTest
|
|||||||
$thing1 = $this->objFromFixture(RestfulServerTestSecretThing::class, 'thing1');
|
$thing1 = $this->objFromFixture(RestfulServerTestSecretThing::class, 'thing1');
|
||||||
$comment1 = $this->objFromFixture(RestfulServerTestComment::class, 'comment1');
|
$comment1 = $this->objFromFixture(RestfulServerTestComment::class, 'comment1');
|
||||||
|
|
||||||
|
// @todo create additional mock object with authenticated VIEW permissions
|
||||||
$urlSafeClassname = $this->urlSafeClassname(RestfulServerTestSecretThing::class);
|
$urlSafeClassname = $this->urlSafeClassname(RestfulServerTestSecretThing::class);
|
||||||
$url = "{$this->baseURI}/api/v1/$urlSafeClassname/" . $thing1->ID;
|
$url = "{$this->baseURI}/api/v1/$urlSafeClassname/" . $thing1->ID;
|
||||||
$response = Director::test($url, null, null, 'GET');
|
$response = Director::test($url, null, null, 'GET');
|
||||||
@ -150,6 +158,7 @@ class RestfulServerTest extends SapphireTest
|
|||||||
$rating1 = $this->objFromFixture(RestfulServerTestAuthorRating::class, 'rating1');
|
$rating1 = $this->objFromFixture(RestfulServerTestAuthorRating::class, 'rating1');
|
||||||
$rating2 = $this->objFromFixture(RestfulServerTestAuthorRating::class, 'rating2');
|
$rating2 = $this->objFromFixture(RestfulServerTestAuthorRating::class, 'rating2');
|
||||||
|
|
||||||
|
// @todo should be set up by fixtures, doesn't work for some reason...
|
||||||
$author1->Ratings()->add($rating1);
|
$author1->Ratings()->add($rating1);
|
||||||
$author1->Ratings()->add($rating2);
|
$author1->Ratings()->add($rating2);
|
||||||
|
|
||||||
@ -178,6 +187,7 @@ class RestfulServerTest extends SapphireTest
|
|||||||
$author1 = $this->objFromFixture(RestfulServerTestAuthor::class, 'author1');
|
$author1 = $this->objFromFixture(RestfulServerTestAuthor::class, 'author1');
|
||||||
$rating1 = $this->objFromFixture(RestfulServerTestAuthorRating::class, 'rating1');
|
$rating1 = $this->objFromFixture(RestfulServerTestAuthorRating::class, 'rating1');
|
||||||
|
|
||||||
|
// @todo should be set up by fixtures, doesn't work for some reason...
|
||||||
$author1->Ratings()->add($rating1);
|
$author1->Ratings()->add($rating1);
|
||||||
|
|
||||||
$urlSafeClassname = $this->urlSafeClassname(RestfulServerTestAuthor::class);
|
$urlSafeClassname = $this->urlSafeClassname(RestfulServerTestAuthor::class);
|
||||||
|
Loading…
Reference in New Issue
Block a user