mirror of
https://github.com/silverstripe/silverstripe-restfulserver
synced 2024-10-22 14:05:58 +02:00
Merge 80b17460c4
into 3c1055e2f0
This commit is contained in:
commit
8dcc0c4ffb
@ -28,7 +28,11 @@
|
|||||||
"SilverStripe\\RestfulServer\\Tests\\": "tests"
|
"SilverStripe\\RestfulServer\\Tests\\": "tests"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"extra": [],
|
"extra": {
|
||||||
|
"branch-alias": {
|
||||||
|
"dev-master": "2.x-dev"
|
||||||
|
}
|
||||||
|
},
|
||||||
"prefer-stable": true,
|
"prefer-stable": true,
|
||||||
"minimum-stability": "dev"
|
"minimum-stability": "dev"
|
||||||
}
|
}
|
||||||
|
@ -2,12 +2,14 @@
|
|||||||
|
|
||||||
namespace SilverStripe\RestfulServer\DataFormatter;
|
namespace SilverStripe\RestfulServer\DataFormatter;
|
||||||
|
|
||||||
|
use SilverStripe\RestfulServer\RestfulServer;
|
||||||
use SilverStripe\View\ArrayData;
|
use SilverStripe\View\ArrayData;
|
||||||
use SilverStripe\Core\Convert;
|
use SilverStripe\Core\Convert;
|
||||||
use SilverStripe\RestfulServer\DataFormatter;
|
use SilverStripe\RestfulServer\DataFormatter;
|
||||||
use SilverStripe\ORM\DataObjectInterface;
|
use SilverStripe\ORM\DataObjectInterface;
|
||||||
use SilverStripe\Control\Director;
|
use SilverStripe\Control\Director;
|
||||||
use SilverStripe\ORM\SS_List;
|
use SilverStripe\ORM\SS_List;
|
||||||
|
use SilverStripe\ORM\FieldType;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Formats a DataObject's member fields into a JSON string
|
* Formats a DataObject's member fields into a JSON string
|
||||||
@ -88,7 +90,7 @@ class JSONDataFormatter extends DataFormatter
|
|||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
$fieldValue = $obj->obj($fieldName)->forTemplate();
|
$fieldValue = self::cast($obj->obj($fieldName));
|
||||||
$mappedFieldName = $this->getFieldAlias($className, $fieldName);
|
$mappedFieldName = $this->getFieldAlias($className, $fieldName);
|
||||||
$serobj->$mappedFieldName = $fieldValue;
|
$serobj->$mappedFieldName = $fieldValue;
|
||||||
}
|
}
|
||||||
@ -119,11 +121,13 @@ class JSONDataFormatter extends DataFormatter
|
|||||||
$serobj->$relName = ArrayData::array_to_object(array(
|
$serobj->$relName = ArrayData::array_to_object(array(
|
||||||
"className" => $relClass,
|
"className" => $relClass,
|
||||||
"href" => "$href.json",
|
"href" => "$href.json",
|
||||||
"id" => $obj->$fieldName
|
"id" => self::cast($obj->obj($fieldName))
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
|
|
||||||
foreach ($obj->hasMany() + $obj->manyMany() as $relName => $relClass) {
|
foreach ($obj->hasMany() + $obj->manyMany() as $relName => $relClass) {
|
||||||
|
$relClass = RestfulServer::parseRelationClass($relClass);
|
||||||
|
|
||||||
//remove dot notation from relation names
|
//remove dot notation from relation names
|
||||||
$parts = explode('.', $relClass);
|
$parts = explode('.', $relClass);
|
||||||
$relClass = array_shift($parts);
|
$relClass = array_shift($parts);
|
||||||
@ -193,4 +197,19 @@ class JSONDataFormatter extends DataFormatter
|
|||||||
{
|
{
|
||||||
return Convert::json2array($strData);
|
return Convert::json2array($strData);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static function cast(FieldType\DBField $dbfield)
|
||||||
|
{
|
||||||
|
switch (true) {
|
||||||
|
case $dbfield instanceof FieldType\DBInt:
|
||||||
|
return (int)$dbfield->RAW();
|
||||||
|
case $dbfield instanceof FieldType\DBFloat:
|
||||||
|
return (float)$dbfield->RAW();
|
||||||
|
case $dbfield instanceof FieldType\DBBoolean:
|
||||||
|
return (bool)$dbfield->RAW();
|
||||||
|
case is_null($dbfield->RAW()):
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
return $dbfield->RAW();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -10,6 +10,7 @@ use SilverStripe\ORM\DataObject;
|
|||||||
use SilverStripe\ORM\DataObjectInterface;
|
use SilverStripe\ORM\DataObjectInterface;
|
||||||
use SilverStripe\Control\Director;
|
use SilverStripe\Control\Director;
|
||||||
use SilverStripe\ORM\SS_List;
|
use SilverStripe\ORM\SS_List;
|
||||||
|
use SilverStripe\RestfulServer\RestfulServer;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Formats a DataObject's member fields into an XML string
|
* Formats a DataObject's member fields into an XML string
|
||||||
@ -196,6 +197,8 @@ class XMLDataFormatter extends DataFormatter
|
|||||||
}
|
}
|
||||||
|
|
||||||
foreach ($obj->manyMany() as $relName => $relClass) {
|
foreach ($obj->manyMany() as $relName => $relClass) {
|
||||||
|
$relClass = RestfulServer::parseRelationClass($relClass);
|
||||||
|
|
||||||
//remove dot notation from relation names
|
//remove dot notation from relation names
|
||||||
$parts = explode('.', $relClass);
|
$parts = explode('.', $relClass);
|
||||||
$relClass = array_shift($parts);
|
$relClass = array_shift($parts);
|
||||||
|
@ -15,6 +15,7 @@ use SilverStripe\ORM\ValidationResult;
|
|||||||
use SilverStripe\Security\Member;
|
use SilverStripe\Security\Member;
|
||||||
use SilverStripe\Security\Security;
|
use SilverStripe\Security\Security;
|
||||||
use SilverStripe\CMS\Model\SiteTree;
|
use SilverStripe\CMS\Model\SiteTree;
|
||||||
|
use SilverStripe\Core\Injector\Injector;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Generic RESTful server, which handles webservice access to arbitrary DataObjects.
|
* Generic RESTful server, which handles webservice access to arbitrary DataObjects.
|
||||||
@ -41,6 +42,10 @@ use SilverStripe\CMS\Model\SiteTree;
|
|||||||
*/
|
*/
|
||||||
class RestfulServer extends Controller
|
class RestfulServer extends Controller
|
||||||
{
|
{
|
||||||
|
/**
|
||||||
|
* @config
|
||||||
|
* @var array
|
||||||
|
*/
|
||||||
private static $url_handlers = array(
|
private static $url_handlers = array(
|
||||||
'$ClassName!/$ID/$Relation' => 'handleAction',
|
'$ClassName!/$ID/$Relation' => 'handleAction',
|
||||||
'' => 'notFound'
|
'' => 'notFound'
|
||||||
@ -62,10 +67,24 @@ class RestfulServer extends Controller
|
|||||||
* If no extension is given in the request, resolve to this extension
|
* If no extension is given in the request, resolve to this extension
|
||||||
* (and subsequently the {@link self::$default_mimetype}.
|
* (and subsequently the {@link self::$default_mimetype}.
|
||||||
*
|
*
|
||||||
|
* @config
|
||||||
* @var string
|
* @var string
|
||||||
*/
|
*/
|
||||||
private static $default_extension = "xml";
|
private static $default_extension = "xml";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Whether or not to send an additional "Location" header for POST requests
|
||||||
|
* to satisfy HTTP 1.1: https://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html
|
||||||
|
*
|
||||||
|
* Note: With this enabled (the default), no POST request for resource creation
|
||||||
|
* will return an HTTP 201. Because of the addition of the "Location" header,
|
||||||
|
* all responses become a straight HTTP 200.
|
||||||
|
*
|
||||||
|
* @config
|
||||||
|
* @var boolean
|
||||||
|
*/
|
||||||
|
private static $location_header_on_create = true;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* If no extension is given, resolve the request to this mimetype.
|
* If no extension is given, resolve the request to this mimetype.
|
||||||
*
|
*
|
||||||
@ -122,6 +141,32 @@ class RestfulServer extends Controller
|
|||||||
return str_replace('-', '\\', $className);
|
return str_replace('-', '\\', $className);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Parse many many relation class (works with through array syntax)
|
||||||
|
*
|
||||||
|
* @param string|array $class
|
||||||
|
* @return string|array
|
||||||
|
*/
|
||||||
|
public static function parseRelationClass($class)
|
||||||
|
{
|
||||||
|
// detect many many through syntax
|
||||||
|
if (is_array($class)
|
||||||
|
&& array_key_exists('through', $class)
|
||||||
|
&& array_key_exists('to', $class)
|
||||||
|
) {
|
||||||
|
$toRelation = $class['to'];
|
||||||
|
|
||||||
|
$hasOne = Config::inst()->get($class['through'], 'has_one');
|
||||||
|
if (empty($hasOne) || !is_array($hasOne) || !array_key_exists($toRelation, $hasOne)) {
|
||||||
|
return $class;
|
||||||
|
}
|
||||||
|
|
||||||
|
return $hasOne[$toRelation];
|
||||||
|
}
|
||||||
|
|
||||||
|
return $class;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* This handler acts as the switchboard for the controller.
|
* This handler acts as the switchboard for the controller.
|
||||||
* Since no $Action url-param is set, all requests are sent here.
|
* Since no $Action url-param is set, all requests are sent here.
|
||||||
@ -154,6 +199,7 @@ class RestfulServer extends Controller
|
|||||||
// authenticate through HTTP BasicAuth
|
// authenticate through HTTP BasicAuth
|
||||||
$this->member = $this->authenticate();
|
$this->member = $this->authenticate();
|
||||||
|
|
||||||
|
try {
|
||||||
// handle different HTTP verbs
|
// handle different HTTP verbs
|
||||||
if ($this->request->isGET() || $this->request->isHEAD()) {
|
if ($this->request->isGET() || $this->request->isHEAD()) {
|
||||||
return $this->getHandler($className, $id, $relation);
|
return $this->getHandler($className, $id, $relation);
|
||||||
@ -170,6 +216,9 @@ class RestfulServer extends Controller
|
|||||||
if ($this->request->isDELETE()) {
|
if ($this->request->isDELETE()) {
|
||||||
return $this->deleteHandler($className, $id, $relation);
|
return $this->deleteHandler($className, $id, $relation);
|
||||||
}
|
}
|
||||||
|
} catch (\Exception $e) {
|
||||||
|
return $this->exceptionThrown($this->getRequestDataFormatter($className), $e);
|
||||||
|
}
|
||||||
|
|
||||||
// if no HTTP verb matches, return error
|
// if no HTTP verb matches, return error
|
||||||
return $this->methodNotAllowed();
|
return $this->methodNotAllowed();
|
||||||
@ -459,7 +508,7 @@ class RestfulServer extends Controller
|
|||||||
return $obj;
|
return $obj;
|
||||||
}
|
}
|
||||||
|
|
||||||
$this->getResponse()->setStatusCode(200); // Success
|
$this->getResponse()->setStatusCode(202); // Accepted
|
||||||
$this->getResponse()->addHeader('Content-Type', $responseFormatter->getOutputContentType());
|
$this->getResponse()->addHeader('Content-Type', $responseFormatter->getOutputContentType());
|
||||||
|
|
||||||
// Append the default extension for the output format to the Location header
|
// Append the default extension for the output format to the Location header
|
||||||
@ -523,7 +572,8 @@ class RestfulServer extends Controller
|
|||||||
if (!singleton($className)->canCreate($this->getMember())) {
|
if (!singleton($className)->canCreate($this->getMember())) {
|
||||||
return $this->permissionFailure();
|
return $this->permissionFailure();
|
||||||
}
|
}
|
||||||
$obj = new $className();
|
|
||||||
|
$obj = Injector::inst()->create($className);
|
||||||
|
|
||||||
$reqFormatter = $this->getRequestDataFormatter($className);
|
$reqFormatter = $this->getRequestDataFormatter($className);
|
||||||
if (!$reqFormatter) {
|
if (!$reqFormatter) {
|
||||||
@ -554,10 +604,15 @@ class RestfulServer extends Controller
|
|||||||
$type = ".{$types[0]}";
|
$type = ".{$types[0]}";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Deviate slightly from the spec: Helps datamodel API access restrict
|
||||||
|
// to consulting just canCreate(), not canView() as a result of the additional
|
||||||
|
// "Location" header.
|
||||||
|
if ($this->config()->get('location_header_on_create')) {
|
||||||
$urlSafeClassName = $this->sanitiseClassName(get_class($obj));
|
$urlSafeClassName = $this->sanitiseClassName(get_class($obj));
|
||||||
$apiBase = $this->config()->api_base;
|
$apiBase = $this->config()->api_base;
|
||||||
$objHref = Director::absoluteURL($apiBase . "$urlSafeClassName/$obj->ID" . $type);
|
$objHref = Director::absoluteURL($apiBase . "$urlSafeClassName/$obj->ID" . $type);
|
||||||
$this->getResponse()->addHeader('Location', $objHref);
|
$this->getResponse()->addHeader('Location', $objHref);
|
||||||
|
}
|
||||||
|
|
||||||
return $responseFormatter->convertDataObject($obj);
|
return $responseFormatter->convertDataObject($obj);
|
||||||
}
|
}
|
||||||
@ -681,7 +736,11 @@ class RestfulServer extends Controller
|
|||||||
$this->getResponse()->setStatusCode(401);
|
$this->getResponse()->setStatusCode(401);
|
||||||
$this->getResponse()->addHeader('WWW-Authenticate', 'Basic realm="API Access"');
|
$this->getResponse()->addHeader('WWW-Authenticate', 'Basic realm="API Access"');
|
||||||
$this->getResponse()->addHeader('Content-Type', 'text/plain');
|
$this->getResponse()->addHeader('Content-Type', 'text/plain');
|
||||||
return "You don't have access to this item through the API.";
|
|
||||||
|
$reponse = "You don't have access to this item through the API.";
|
||||||
|
$this->extend(__FUNCTION__, $reponse);
|
||||||
|
|
||||||
|
return $reponse;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -692,7 +751,11 @@ class RestfulServer extends Controller
|
|||||||
// return a 404
|
// return a 404
|
||||||
$this->getResponse()->setStatusCode(404);
|
$this->getResponse()->setStatusCode(404);
|
||||||
$this->getResponse()->addHeader('Content-Type', 'text/plain');
|
$this->getResponse()->addHeader('Content-Type', 'text/plain');
|
||||||
return "That object wasn't found";
|
|
||||||
|
$reponse = "That object wasn't found";
|
||||||
|
$this->extend(__FUNCTION__, $reponse);
|
||||||
|
|
||||||
|
return $reponse;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -702,7 +765,11 @@ class RestfulServer extends Controller
|
|||||||
{
|
{
|
||||||
$this->getResponse()->setStatusCode(405);
|
$this->getResponse()->setStatusCode(405);
|
||||||
$this->getResponse()->addHeader('Content-Type', 'text/plain');
|
$this->getResponse()->addHeader('Content-Type', 'text/plain');
|
||||||
return "Method Not Allowed";
|
|
||||||
|
$reponse = "Method Not Allowed";
|
||||||
|
$this->extend(__FUNCTION__, $reponse);
|
||||||
|
|
||||||
|
return $reponse;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -712,7 +779,11 @@ class RestfulServer extends Controller
|
|||||||
{
|
{
|
||||||
$this->response->setStatusCode(415); // Unsupported Media Type
|
$this->response->setStatusCode(415); // Unsupported Media Type
|
||||||
$this->getResponse()->addHeader('Content-Type', 'text/plain');
|
$this->getResponse()->addHeader('Content-Type', 'text/plain');
|
||||||
return "Unsupported Media Type";
|
|
||||||
|
$reponse = "Unsupported Media Type";
|
||||||
|
$this->extend(__FUNCTION__, $reponse);
|
||||||
|
|
||||||
|
return $reponse;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -729,6 +800,28 @@ class RestfulServer extends Controller
|
|||||||
'messages' => $result->getMessages(),
|
'messages' => $result->getMessages(),
|
||||||
];
|
];
|
||||||
|
|
||||||
|
$this->extend(__FUNCTION__, $response, $result);
|
||||||
|
|
||||||
|
return $responseFormatter->convertArray($response);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param DataFormatter $responseFormatter
|
||||||
|
* @param \Exception $e
|
||||||
|
* @return string
|
||||||
|
*/
|
||||||
|
protected function exceptionThrown(DataFormatter $responseFormatter, \Exception $e)
|
||||||
|
{
|
||||||
|
$this->getResponse()->setStatusCode(500);
|
||||||
|
$this->getResponse()->addHeader('Content-Type', $responseFormatter->getOutputContentType());
|
||||||
|
|
||||||
|
$response = [
|
||||||
|
'type' => get_class($e),
|
||||||
|
'message' => $e->getMessage(),
|
||||||
|
];
|
||||||
|
|
||||||
|
$this->extend(__FUNCTION__, $response, $e);
|
||||||
|
|
||||||
return $responseFormatter->convertArray($response);
|
return $responseFormatter->convertArray($response);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -760,6 +853,8 @@ class RestfulServer extends Controller
|
|||||||
$relations = (array)$obj->hasOne() + (array)$obj->hasMany() + (array)$obj->manyMany();
|
$relations = (array)$obj->hasOne() + (array)$obj->hasMany() + (array)$obj->manyMany();
|
||||||
if ($relations) {
|
if ($relations) {
|
||||||
foreach ($relations as $relName => $relClass) {
|
foreach ($relations as $relName => $relClass) {
|
||||||
|
$relClass = static::parseRelationClass($relClass);
|
||||||
|
|
||||||
//remove dot notation from relation names
|
//remove dot notation from relation names
|
||||||
$parts = explode('.', $relClass);
|
$parts = explode('.', $relClass);
|
||||||
$relClass = array_shift($parts);
|
$relClass = array_shift($parts);
|
||||||
|
46
tests/unit/JSONDataFormatterTest.php
Normal file
46
tests/unit/JSONDataFormatterTest.php
Normal file
@ -0,0 +1,46 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace SilverStripe\RestfulServer\Tests;
|
||||||
|
|
||||||
|
use SilverStripe\RestfulServer\RestfulServer;
|
||||||
|
use SilverStripe\RestfulServer\Tests\Stubs\JSONDataFormatterTypeTestObject;
|
||||||
|
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
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
class JSONDataFormatterTest extends SapphireTest
|
||||||
|
{
|
||||||
|
protected static $fixture_file = 'JSONDataFormatterTest.yml';
|
||||||
|
|
||||||
|
protected static $extra_dataobjects = [
|
||||||
|
JSONDataFormatterTypeTestObject::class,
|
||||||
|
];
|
||||||
|
|
||||||
|
public function testJSONTypes()
|
||||||
|
{
|
||||||
|
$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');
|
||||||
|
$this->assertContains('"Active":true', $json, 'Boolean casted to boolean');
|
||||||
|
$this->assertContains('"Sort":17', $json, 'Integer casted to integer');
|
||||||
|
$this->assertContains('"Average":1.2345', $json, 'Float casted to float');
|
||||||
|
$this->assertContains('"ParentID":0', $json, 'Empty FK is 0');
|
||||||
|
|
||||||
|
$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');
|
||||||
|
}
|
||||||
|
}
|
20
tests/unit/JSONDataFormatterTest.yml
Normal file
20
tests/unit/JSONDataFormatterTest.yml
Normal file
@ -0,0 +1,20 @@
|
|||||||
|
SilverStripe\RestfulServer\Tests\Stubs\JSONDataFormatterTypeTestObject:
|
||||||
|
parent:
|
||||||
|
Name: Parent
|
||||||
|
Active: true
|
||||||
|
Sort: 17
|
||||||
|
Average: 1.2345
|
||||||
|
child1:
|
||||||
|
Name: Child 1
|
||||||
|
Active: 1
|
||||||
|
Sort: 4
|
||||||
|
Average: 6.78
|
||||||
|
Parent: =>SilverStripe\RestfulServer\Tests\Stubs\JSONDataFormatterTypeTestObject.parent
|
||||||
|
child2:
|
||||||
|
Name: Child 2
|
||||||
|
Active: false
|
||||||
|
Sort: 9
|
||||||
|
Average: 1
|
||||||
|
Parent: =>SilverStripe\RestfulServer\Tests\Stubs\JSONDataFormatterTypeTestObject.parent
|
||||||
|
child3:
|
||||||
|
Parent: =>SilverStripe\RestfulServer\Tests\Stubs\JSONDataFormatterTypeTestObject.parent
|
@ -2,7 +2,9 @@
|
|||||||
|
|
||||||
namespace SilverStripe\RestfulServer\Tests;
|
namespace SilverStripe\RestfulServer\Tests;
|
||||||
|
|
||||||
|
use SilverStripe\RestfulServer\RestfulServer;
|
||||||
use SilverStripe\RestfulServer\Tests\Stubs\RestfulServerTestComment;
|
use SilverStripe\RestfulServer\Tests\Stubs\RestfulServerTestComment;
|
||||||
|
use SilverStripe\RestfulServer\Tests\Stubs\RestfulServerTestExceptionThrown;
|
||||||
use SilverStripe\RestfulServer\Tests\Stubs\RestfulServerTestSecretThing;
|
use SilverStripe\RestfulServer\Tests\Stubs\RestfulServerTestSecretThing;
|
||||||
use SilverStripe\RestfulServer\Tests\Stubs\RestfulServerTestPage;
|
use SilverStripe\RestfulServer\Tests\Stubs\RestfulServerTestPage;
|
||||||
use SilverStripe\RestfulServer\Tests\Stubs\RestfulServerTestAuthor;
|
use SilverStripe\RestfulServer\Tests\Stubs\RestfulServerTestAuthor;
|
||||||
@ -38,6 +40,8 @@ class RestfulServerTest extends SapphireTest
|
|||||||
RestfulServerTestPage::class,
|
RestfulServerTestPage::class,
|
||||||
RestfulServerTestAuthor::class,
|
RestfulServerTestAuthor::class,
|
||||||
RestfulServerTestAuthorRating::class,
|
RestfulServerTestAuthorRating::class,
|
||||||
|
RestfulServerTestValidationFailure::class,
|
||||||
|
RestfulServerTestExceptionThrown::class,
|
||||||
];
|
];
|
||||||
|
|
||||||
protected function urlSafeClassname($classname)
|
protected function urlSafeClassname($classname)
|
||||||
@ -140,7 +144,7 @@ class RestfulServerTest extends SapphireTest
|
|||||||
$_SERVER['PHP_AUTH_USER'] = 'editor@test.com';
|
$_SERVER['PHP_AUTH_USER'] = 'editor@test.com';
|
||||||
$_SERVER['PHP_AUTH_PW'] = 'editor';
|
$_SERVER['PHP_AUTH_PW'] = 'editor';
|
||||||
$response = Director::test($url, $data, null, 'PUT');
|
$response = Director::test($url, $data, null, 'PUT');
|
||||||
$this->assertEquals(200, $response->getStatusCode()); // Success
|
$this->assertEquals(202, $response->getStatusCode()); // Accepted
|
||||||
|
|
||||||
unset($_SERVER['PHP_AUTH_USER']);
|
unset($_SERVER['PHP_AUTH_USER']);
|
||||||
unset($_SERVER['PHP_AUTH_PW']);
|
unset($_SERVER['PHP_AUTH_PW']);
|
||||||
@ -233,7 +237,7 @@ class RestfulServerTest extends SapphireTest
|
|||||||
'Content-Type' => 'application/x-www-form-urlencoded'
|
'Content-Type' => 'application/x-www-form-urlencoded'
|
||||||
);
|
);
|
||||||
$response = Director::test($url, null, null, 'PUT', $body, $headers);
|
$response = Director::test($url, null, null, 'PUT', $body, $headers);
|
||||||
$this->assertEquals(200, $response->getStatusCode()); // Success
|
$this->assertEquals(202, $response->getStatusCode()); // Accepted
|
||||||
// Assumption: XML is default output
|
// Assumption: XML is default output
|
||||||
$responseArr = Convert::xml2array($response->getBody());
|
$responseArr = Convert::xml2array($response->getBody());
|
||||||
$this->assertEquals($comment1->ID, $responseArr['ID']);
|
$this->assertEquals($comment1->ID, $responseArr['ID']);
|
||||||
@ -302,7 +306,7 @@ class RestfulServerTest extends SapphireTest
|
|||||||
'Content-Type'=>'application/json',
|
'Content-Type'=>'application/json',
|
||||||
'Accept' => 'application/json'
|
'Accept' => 'application/json'
|
||||||
));
|
));
|
||||||
$this->assertEquals(200, $response->getStatusCode()); // Updated
|
$this->assertEquals(202, $response->getStatusCode()); // Accepted
|
||||||
$obj = Convert::json2obj($response->getBody());
|
$obj = Convert::json2obj($response->getBody());
|
||||||
$this->assertEquals($comment1->ID, $obj->ID);
|
$this->assertEquals($comment1->ID, $obj->ID);
|
||||||
$this->assertEquals('updated', $obj->Comment);
|
$this->assertEquals('updated', $obj->Comment);
|
||||||
@ -312,7 +316,7 @@ class RestfulServerTest extends SapphireTest
|
|||||||
$url = "{$this->baseURI}/api/v1/$urlSafeClassname/{$comment1->ID}.json";
|
$url = "{$this->baseURI}/api/v1/$urlSafeClassname/{$comment1->ID}.json";
|
||||||
$body = '{"Comment":"updated"}';
|
$body = '{"Comment":"updated"}';
|
||||||
$response = Director::test($url, null, null, 'PUT', $body);
|
$response = Director::test($url, null, null, 'PUT', $body);
|
||||||
$this->assertEquals(200, $response->getStatusCode()); // Updated
|
$this->assertEquals(202, $response->getStatusCode()); // Accepted
|
||||||
$this->assertEquals($url, $response->getHeader('Location'));
|
$this->assertEquals($url, $response->getHeader('Location'));
|
||||||
$obj = Convert::json2obj($response->getBody());
|
$obj = Convert::json2obj($response->getBody());
|
||||||
$this->assertEquals($comment1->ID, $obj->ID);
|
$this->assertEquals($comment1->ID, $obj->ID);
|
||||||
@ -334,7 +338,7 @@ class RestfulServerTest extends SapphireTest
|
|||||||
$url = "{$this->baseURI}/api/v1/$urlSafeClassname/" . $comment1->ID;
|
$url = "{$this->baseURI}/api/v1/$urlSafeClassname/" . $comment1->ID;
|
||||||
$body = '<RestfulServerTestComment><Comment>updated</Comment></RestfulServerTestComment>';
|
$body = '<RestfulServerTestComment><Comment>updated</Comment></RestfulServerTestComment>';
|
||||||
$response = Director::test($url, null, null, 'PUT', $body, array('Content-Type'=>'text/xml'));
|
$response = Director::test($url, null, null, 'PUT', $body, array('Content-Type'=>'text/xml'));
|
||||||
$this->assertEquals(200, $response->getStatusCode()); // Updated
|
$this->assertEquals(202, $response->getStatusCode()); // Accepted
|
||||||
$obj = Convert::xml2array($response->getBody());
|
$obj = Convert::xml2array($response->getBody());
|
||||||
$this->assertEquals($comment1->ID, $obj['ID']);
|
$this->assertEquals($comment1->ID, $obj['ID']);
|
||||||
$this->assertEquals('updated', $obj['Comment']);
|
$this->assertEquals('updated', $obj['Comment']);
|
||||||
@ -344,7 +348,7 @@ class RestfulServerTest extends SapphireTest
|
|||||||
$url = "{$this->baseURI}/api/v1/$urlSafeClassname/{$comment1->ID}.xml";
|
$url = "{$this->baseURI}/api/v1/$urlSafeClassname/{$comment1->ID}.xml";
|
||||||
$body = '<RestfulServerTestComment><Comment>updated</Comment></RestfulServerTestComment>';
|
$body = '<RestfulServerTestComment><Comment>updated</Comment></RestfulServerTestComment>';
|
||||||
$response = Director::test($url, null, null, 'PUT', $body);
|
$response = Director::test($url, null, null, 'PUT', $body);
|
||||||
$this->assertEquals(200, $response->getStatusCode()); // Updated
|
$this->assertEquals(202, $response->getStatusCode()); // Accepted
|
||||||
$this->assertEquals($url, $response->getHeader('Location'));
|
$this->assertEquals($url, $response->getHeader('Location'));
|
||||||
$obj = Convert::xml2array($response->getBody());
|
$obj = Convert::xml2array($response->getBody());
|
||||||
$this->assertEquals($comment1->ID, $obj['ID']);
|
$this->assertEquals($comment1->ID, $obj['ID']);
|
||||||
@ -685,4 +689,30 @@ class RestfulServerTest extends SapphireTest
|
|||||||
$responseArr = Convert::xml2array($response->getBody());
|
$responseArr = Convert::xml2array($response->getBody());
|
||||||
$this->assertEquals('SilverStripe\\ORM\\ValidationException', $responseArr['type']);
|
$this->assertEquals('SilverStripe\\ORM\\ValidationException', $responseArr['type']);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function testExceptionThrownWithPOST()
|
||||||
|
{
|
||||||
|
$urlSafeClassname = $this->urlSafeClassname(RestfulServerTestExceptionThrown::class);
|
||||||
|
$url = "{$this->baseURI}/api/v1/$urlSafeClassname/";
|
||||||
|
$data = [
|
||||||
|
'Content' => 'Test',
|
||||||
|
];
|
||||||
|
$response = Director::test($url, $data, null, 'POST');
|
||||||
|
// Assumption: XML is default output
|
||||||
|
$responseArr = Convert::xml2array($response->getBody());
|
||||||
|
$this->assertEquals(\Exception::class, $responseArr['type']);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testParseClassName()
|
||||||
|
{
|
||||||
|
$manyMany = RestfulServerTestAuthor::config()->get('many_many');
|
||||||
|
|
||||||
|
// simple syntax (many many standard)
|
||||||
|
$className = RestfulServer::parseRelationClass($manyMany['RelatedPages']);
|
||||||
|
$this->assertEquals(RestfulServerTestPage::class, $className);
|
||||||
|
|
||||||
|
// array syntax (many many through)
|
||||||
|
$className = RestfulServer::parseRelationClass($manyMany['SortedPages']);
|
||||||
|
$this->assertEquals(RestfulServerTestPage::class, $className);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
29
tests/unit/Stubs/AuthorSortedPageRelation.php
Normal file
29
tests/unit/Stubs/AuthorSortedPageRelation.php
Normal file
@ -0,0 +1,29 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace SilverStripe\RestfulServer\Tests\Stubs;
|
||||||
|
|
||||||
|
use SilverStripe\Dev\TestOnly;
|
||||||
|
use SilverStripe\ORM\DataObject;
|
||||||
|
|
||||||
|
class AuthorSortedPageRelation extends DataObject implements TestOnly
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* @var string
|
||||||
|
*/
|
||||||
|
private static $table_name = 'AuthorSortedPageRelation';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @var array
|
||||||
|
*/
|
||||||
|
private static $has_one = [
|
||||||
|
'Parent' => RestfulServerTestAuthor::class,
|
||||||
|
'SortedPage' => RestfulServerTestPage::class,
|
||||||
|
];
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @var array
|
||||||
|
*/
|
||||||
|
private static $db = [
|
||||||
|
'Sort' => 'Int',
|
||||||
|
];
|
||||||
|
}
|
32
tests/unit/Stubs/JSONDataFormatterTypeTestObject.php
Normal file
32
tests/unit/Stubs/JSONDataFormatterTypeTestObject.php
Normal file
@ -0,0 +1,32 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace SilverStripe\RestfulServer\Tests\Stubs;
|
||||||
|
|
||||||
|
use SilverStripe\Dev\TestOnly;
|
||||||
|
use SilverStripe\ORM\DataObject;
|
||||||
|
|
||||||
|
class JSONDataFormatterTypeTestObject extends DataObject implements TestOnly
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* @var string
|
||||||
|
*/
|
||||||
|
private static $table_name = 'JSONDataFormatterTypeTestObject';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @var array
|
||||||
|
*/
|
||||||
|
private static $db = [
|
||||||
|
'Name' => 'Varchar',
|
||||||
|
'Active' => 'Boolean',
|
||||||
|
'Sort' => 'Int',
|
||||||
|
'Average' => 'Float',
|
||||||
|
];
|
||||||
|
|
||||||
|
private static $has_one = [
|
||||||
|
'Parent' => JSONDataFormatterTypeTestObject::class,
|
||||||
|
];
|
||||||
|
|
||||||
|
private static $has_many = [
|
||||||
|
'Children' => JSONDataFormatterTypeTestObject::class,
|
||||||
|
];
|
||||||
|
}
|
@ -18,11 +18,17 @@ class RestfulServerTestAuthor extends DataObject implements TestOnly
|
|||||||
private static $many_many = array(
|
private static $many_many = array(
|
||||||
'RelatedPages' => RestfulServerTestPage::class,
|
'RelatedPages' => RestfulServerTestPage::class,
|
||||||
'RelatedAuthors' => RestfulServerTestAuthor::class,
|
'RelatedAuthors' => RestfulServerTestAuthor::class,
|
||||||
|
'SortedPages' => [
|
||||||
|
'through' => AuthorSortedPageRelation::class,
|
||||||
|
'from' => 'Parent',
|
||||||
|
'to' => 'SortedPage',
|
||||||
|
],
|
||||||
);
|
);
|
||||||
|
|
||||||
private static $has_many = array(
|
private static $has_many = array(
|
||||||
'PublishedPages' => RestfulServerTestPage::class,
|
'PublishedPages' => RestfulServerTestPage::class,
|
||||||
'Ratings' => RestfulServerTestAuthorRating::class,
|
'Ratings' => RestfulServerTestAuthorRating::class,
|
||||||
|
'SortedPagesRelation' => AuthorSortedPageRelation::class . '.Parent',
|
||||||
);
|
);
|
||||||
|
|
||||||
public function canView($member = null)
|
public function canView($member = null)
|
||||||
|
52
tests/unit/Stubs/RestfulServerTestExceptionThrown.php
Normal file
52
tests/unit/Stubs/RestfulServerTestExceptionThrown.php
Normal file
@ -0,0 +1,52 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace SilverStripe\RestfulServer\Tests\Stubs;
|
||||||
|
|
||||||
|
use SilverStripe\Dev\TestOnly;
|
||||||
|
use SilverStripe\ORM\DataObject;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Class RestfulServerTestExceptionThrown
|
||||||
|
* @package SilverStripe\RestfulServer\Tests\Stubs
|
||||||
|
*
|
||||||
|
* @property string Content
|
||||||
|
* @property string Title
|
||||||
|
*/
|
||||||
|
class RestfulServerTestExceptionThrown extends DataObject implements TestOnly
|
||||||
|
{
|
||||||
|
private static $api_access = true;
|
||||||
|
|
||||||
|
private static $table_name = 'RestfulServerTestExceptionThrown';
|
||||||
|
|
||||||
|
private static $db = array(
|
||||||
|
'Content' => 'Text',
|
||||||
|
'Title' => 'Text',
|
||||||
|
);
|
||||||
|
|
||||||
|
public function onBeforeWrite()
|
||||||
|
{
|
||||||
|
parent::onBeforeWrite();
|
||||||
|
|
||||||
|
throw new \Exception('This is an exception test');
|
||||||
|
}
|
||||||
|
|
||||||
|
public function canView($member = null)
|
||||||
|
{
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function canEdit($member = null)
|
||||||
|
{
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function canDelete($member = null)
|
||||||
|
{
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function canCreate($member = null, $context = array())
|
||||||
|
{
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user