diff options
author | Bernhard Posselt <Raydiation@users.noreply.github.com> | 2015-08-05 17:28:08 +0200 |
---|---|---|
committer | Bernhard Posselt <Raydiation@users.noreply.github.com> | 2015-08-05 17:28:08 +0200 |
commit | 7cb0934fa27a512b2e409361fe283d9c15560327 (patch) | |
tree | 1e2f9fcb7433e53e8c072aab6f3d0c81713c01f7 | |
parent | 314fc11e1beb37e683cfa874e74ae3103632d803 (diff) | |
parent | 6c46430cdb6edf411142d0a73fb888391ea86fd7 (diff) | |
download | nextcloud-server-7cb0934fa27a512b2e409361fe283d9c15560327.tar.gz nextcloud-server-7cb0934fa27a512b2e409361fe283d9c15560327.zip |
Merge pull request #18035 from owncloud/ocs-2.0
Adding ocs/v2.php with status code mapper
-rw-r--r-- | lib/private/api.php | 113 | ||||
-rw-r--r-- | lib/private/ocs.php | 145 | ||||
-rw-r--r-- | lib/private/ocs/exception.php | 34 | ||||
-rw-r--r-- | lib/public/appframework/http/ocsresponse.php | 28 | ||||
-rw-r--r-- | lib/public/appframework/ocscontroller.php | 11 | ||||
-rw-r--r-- | ocs/v1.php | 2 | ||||
-rw-r--r-- | ocs/v2.php | 22 | ||||
-rw-r--r-- | tests/lib/appframework/controller/OCSControllerTest.php | 12 | ||||
-rw-r--r-- | tests/lib/appframework/http/OCSResponseTest.php | 5 | ||||
-rw-r--r-- | tests/ocs/response.php | 42 |
10 files changed, 211 insertions, 203 deletions
diff --git a/lib/private/api.php b/lib/private/api.php index 8e483b7efe9..86d7558526b 100644 --- a/lib/private/api.php +++ b/lib/private/api.php @@ -1,4 +1,7 @@ <?php +use OCP\API; +use OCP\AppFramework\Http; + /** * @author Bart Visscher <bartv@thisnet.nl> * @author Bernhard Posselt <dev@bernhard-posselt.com> @@ -82,7 +85,7 @@ class OC_API { * @param array $requirements */ public static function register($method, $url, $action, $app, - $authLevel = \OCP\API::USER_AUTH, + $authLevel = API::USER_AUTH, $defaults = array(), $requirements = array()) { $name = strtolower($method).$url; @@ -123,7 +126,7 @@ class OC_API { if(!self::isAuthorised($action)) { $responses[] = array( 'app' => $action['app'], - 'response' => new OC_OCS_Result(null, \OCP\API::RESPOND_UNAUTHORISED, 'Unauthorised'), + 'response' => new OC_OCS_Result(null, API::RESPOND_UNAUTHORISED, 'Unauthorised'), 'shipped' => OC_App::isShipped($action['app']), ); continue; @@ -131,7 +134,7 @@ class OC_API { if(!is_callable($action['action'])) { $responses[] = array( 'app' => $action['app'], - 'response' => new OC_OCS_Result(null, \OCP\API::RESPOND_NOT_FOUND, 'Api method not found'), + 'response' => new OC_OCS_Result(null, API::RESPOND_NOT_FOUND, 'Api method not found'), 'shipped' => OC_App::isShipped($action['app']), ); continue; @@ -252,15 +255,15 @@ class OC_API { private static function isAuthorised($action) { $level = $action['authlevel']; switch($level) { - case \OCP\API::GUEST_AUTH: + case API::GUEST_AUTH: // Anyone can access return true; break; - case \OCP\API::USER_AUTH: + case API::USER_AUTH: // User required return self::loginUser(); break; - case \OCP\API::SUBADMIN_AUTH: + case API::SUBADMIN_AUTH: // Check for subadmin $user = self::loginUser(); if(!$user) { @@ -275,7 +278,7 @@ class OC_API { } } break; - case \OCP\API::ADMIN_AUTH: + case API::ADMIN_AUTH: // Check for admin $user = self::loginUser(); if(!$user) { @@ -342,28 +345,21 @@ class OC_API { */ public static function respond($result, $format='xml') { // Send 401 headers if unauthorised - if($result->getStatusCode() === \OCP\API::RESPOND_UNAUTHORISED) { + if($result->getStatusCode() === API::RESPOND_UNAUTHORISED) { header('WWW-Authenticate: Basic realm="Authorisation Required"'); header('HTTP/1.0 401 Unauthorized'); } - $response = array( - 'ocs' => array( - 'meta' => $result->getMeta(), - 'data' => $result->getData(), - ), - ); - if ($format == 'json') { - OC_JSON::encodedPrint($response); - } else if ($format == 'xml') { - header('Content-type: text/xml; charset=UTF-8'); - $writer = new XMLWriter(); - $writer->openMemory(); - $writer->setIndent( true ); - $writer->startDocument(); - self::toXML($response, $writer); - $writer->endDocument(); - echo $writer->outputMemory(true); + + if (self::isV2()) { + $statusCode = self::mapStatusCodes($result->getStatusCode()); + if (!is_null($statusCode)) { + OC_Response::setStatus($statusCode); + } } + + self::setContentType($format); + $body = self::renderResult($result, $format); + echo $body; } /** @@ -400,8 +396,8 @@ class OC_API { /** * Based on the requested format the response content type is set */ - public static function setContentType() { - $format = self::requestedFormat(); + public static function setContentType($format = null) { + $format = is_null($format) ? self::requestedFormat() : $format; if ($format === 'xml') { header('Content-type: text/xml; charset=UTF-8'); return; @@ -415,5 +411,68 @@ class OC_API { header('Content-Type: application/octet-stream; charset=utf-8'); } + /** + * @return boolean + */ + private static function isV2() { + $request = \OC::$server->getRequest(); + $script = $request->getScriptName(); + return $script === '/ocs/v2.php'; + } + + /** + * @param integer $sc + * @return int + */ + public static function mapStatusCodes($sc) { + switch ($sc) { + case API::RESPOND_NOT_FOUND: + return Http::STATUS_NOT_FOUND; + case API::RESPOND_SERVER_ERROR: + return Http::STATUS_INTERNAL_SERVER_ERROR; + case API::RESPOND_UNKNOWN_ERROR: + return Http::STATUS_INTERNAL_SERVER_ERROR; + case API::RESPOND_UNAUTHORISED: + // already handled for v1 + return null; + case 100: + return Http::STATUS_OK; + } + // any 2xx, 4xx and 5xx will be used as is + if ($sc >= 200 && $sc < 600) { + return $sc; + } + + // any error codes > 100 are treated as client errors + if ($sc > 100 && $sc < 200) { + return Http::STATUS_BAD_REQUEST; + } + return Http::STATUS_OK; + } + + /** + * @param OC_OCS_Result $result + * @param string $format + * @return string + */ + public static function renderResult($result, $format) { + $response = array( + 'ocs' => array( + 'meta' => $result->getMeta(), + 'data' => $result->getData(), + ), + ); + if ($format == 'json') { + return OC_JSON::encode($response); + } + + $writer = new XMLWriter(); + $writer->openMemory(); + $writer->setIndent(true); + $writer->startDocument(); + self::toXML($response, $writer); + $writer->endDocument(); + return $writer->outputMemory(true); + } } diff --git a/lib/private/ocs.php b/lib/private/ocs.php index 6d166f8adb0..bb1aabf8f18 100644 --- a/lib/private/ocs.php +++ b/lib/private/ocs.php @@ -28,6 +28,7 @@ * along with this program. If not, see <http://www.gnu.org/licenses/> * */ +use OCP\API; /** * Class to handle open collaboration services API requests @@ -64,8 +65,7 @@ class OC_OCS { } } if ($data === false) { - echo self::generateXml('', 'fail', 400, 'Bad request. Please provide a valid '.$key); - exit(); + throw new \OC\OCS\Exception(new OC_OCS_Result(null, 400, 'Bad request. Please provide a valid '.$key)); } else { // NOTE: Is the raw type necessary? It might be a little risky without sanitization if ($type == 'raw') return $data; @@ -78,23 +78,12 @@ class OC_OCS { } public static function notFound() { - if($_SERVER['REQUEST_METHOD'] == 'GET') { - $method='get'; - }elseif($_SERVER['REQUEST_METHOD'] == 'PUT') { - $method='put'; - }elseif($_SERVER['REQUEST_METHOD'] == 'POST') { - $method='post'; - }else{ - echo('internal server error: method not supported'); - exit(); - } - - $format = self::readData($method, 'format', 'text', ''); + $format = OC_API::requestedFormat(); $txt='Invalid query, please check the syntax. API specifications are here:' .' http://www.freedesktop.org/wiki/Specifications/open-collaboration-services. DEBUG OUTPUT:'."\n"; $txt.=OC_OCS::getDebugOutput(); - echo(OC_OCS::generateXml($format, 'failed', 999, $txt)); + OC_API::respond(new OC_OCS_Result(null, API::RESPOND_UNKNOWN_ERROR, $txt), $format); } /** @@ -110,130 +99,4 @@ class OC_OCS { if(isset($_POST)) foreach($_POST as $key=>$value) $txt.='post parameter: '.$key.'->'.$value."\n"; return($txt); } - - - /** - * generates the xml or json response for the API call from an multidimenional data array. - * @param string $format - * @param string $status - * @param string $statuscode - * @param string $message - * @param array $data - * @param string $tag - * @param string $tagattribute - * @param int $dimension - * @param int|string $itemscount - * @param int|string $itemsperpage - * @return string xml/json - */ - public static function generateXml($format, $status, $statuscode, - $message, $data=array(), $tag='', $tagattribute='', $dimension=-1, $itemscount='', $itemsperpage='') { - if($format=='json') { - $json=array(); - $json['status']=$status; - $json['statuscode']=$statuscode; - $json['message']=$message; - $json['totalitems']=$itemscount; - $json['itemsperpage']=$itemsperpage; - $json['data']=$data; - return(json_encode($json)); - }else{ - $txt=''; - $writer = xmlwriter_open_memory(); - xmlwriter_set_indent( $writer, 2 ); - xmlwriter_start_document($writer ); - xmlwriter_start_element($writer, 'ocs'); - xmlwriter_start_element($writer, 'meta'); - xmlwriter_write_element($writer, 'status', $status); - xmlwriter_write_element($writer, 'statuscode', $statuscode); - xmlwriter_write_element($writer, 'message', $message); - if($itemscount<>'') xmlwriter_write_element($writer, 'totalitems', $itemscount); - if(!empty($itemsperpage)) xmlwriter_write_element($writer, 'itemsperpage', $itemsperpage); - xmlwriter_end_element($writer); - if($dimension=='0') { - // 0 dimensions - xmlwriter_write_element($writer, 'data', $data); - - }elseif($dimension=='1') { - xmlwriter_start_element($writer, 'data'); - foreach($data as $key=>$entry) { - xmlwriter_write_element($writer, $key, $entry); - } - xmlwriter_end_element($writer); - - }elseif($dimension=='2') { - xmlwriter_start_element($writer, 'data'); - foreach($data as $entry) { - xmlwriter_start_element($writer, $tag); - if(!empty($tagattribute)) { - xmlwriter_write_attribute($writer, 'details', $tagattribute); - } - foreach($entry as $key=>$value) { - if(is_array($value)) { - foreach($value as $k=>$v) { - xmlwriter_write_element($writer, $k, $v); - } - } else { - xmlwriter_write_element($writer, $key, $value); - } - } - xmlwriter_end_element($writer); - } - xmlwriter_end_element($writer); - - }elseif($dimension=='3') { - xmlwriter_start_element($writer, 'data'); - foreach($data as $entrykey=>$entry) { - xmlwriter_start_element($writer, $tag); - if(!empty($tagattribute)) { - xmlwriter_write_attribute($writer, 'details', $tagattribute); - } - foreach($entry as $key=>$value) { - if(is_array($value)) { - xmlwriter_start_element($writer, $entrykey); - foreach($value as $k=>$v) { - xmlwriter_write_element($writer, $k, $v); - } - xmlwriter_end_element($writer); - } else { - xmlwriter_write_element($writer, $key, $value); - } - } - xmlwriter_end_element($writer); - } - xmlwriter_end_element($writer); - }elseif($dimension=='dynamic') { - xmlwriter_start_element($writer, 'data'); - OC_OCS::toxml($writer, $data, 'comment'); - xmlwriter_end_element($writer); - } - - xmlwriter_end_element($writer); - - xmlwriter_end_document( $writer ); - $txt.=xmlwriter_output_memory( $writer ); - unset($writer); - return($txt); - } - } - - /** - * @param resource $writer - * @param array $data - * @param string $node - */ - public static function toXml($writer, $data, $node) { - foreach($data as $key => $value) { - if (is_numeric($key)) { - $key = $node; - } - if (is_array($value)) { - xmlwriter_start_element($writer, $key); - OC_OCS::toxml($writer, $value, $node); - xmlwriter_end_element($writer); - }else{ - xmlwriter_write_element($writer, $key, $value); - } - } - } } diff --git a/lib/private/ocs/exception.php b/lib/private/ocs/exception.php new file mode 100644 index 00000000000..93bee773771 --- /dev/null +++ b/lib/private/ocs/exception.php @@ -0,0 +1,34 @@ +<?php +/** + * @author Thomas Müller <thomas.mueller@tmit.eu> + * + * @copyright Copyright (c) 2015, ownCloud, Inc. + * @license AGPL-3.0 + * + * This code is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License, version 3, + * as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License, version 3, + * along with this program. If not, see <http://www.gnu.org/licenses/> + * + */ + +namespace OC\OCS; + +class Exception extends \Exception { + + public function __construct(\OC_OCS_Result $result) { + $this->result = $result; + } + + public function getResult() { + return $this->result; + } + +} diff --git a/lib/public/appframework/http/ocsresponse.php b/lib/public/appframework/http/ocsresponse.php index 52d3c2fa665..2e788a52bb9 100644 --- a/lib/public/appframework/http/ocsresponse.php +++ b/lib/public/appframework/http/ocsresponse.php @@ -41,38 +41,26 @@ class OCSResponse extends Response { private $format; private $statuscode; private $message; - private $tag; - private $tagattribute; - private $dimension; private $itemscount; private $itemsperpage; /** * generates the xml or json response for the API call from an multidimenional data array. * @param string $format - * @param string $status - * @param string $statuscode + * @param int $statuscode * @param string $message * @param array $data - * @param string $tag - * @param string $tagattribute - * @param int $dimension * @param int|string $itemscount * @param int|string $itemsperpage * @since 8.1.0 */ - public function __construct($format, $status, $statuscode, $message, - $data=[], $tag='', $tagattribute='', - $dimension=-1, $itemscount='', + public function __construct($format, $statuscode, $message, + $data=[], $itemscount='', $itemsperpage='') { $this->format = $format; - $this->setStatus($status); $this->statuscode = $statuscode; $this->message = $message; $this->data = $data; - $this->tag = $tag; - $this->tagattribute = $tagattribute; - $this->dimension = $dimension; $this->itemscount = $itemscount; $this->itemsperpage = $itemsperpage; @@ -93,11 +81,11 @@ class OCSResponse extends Response { * @since 8.1.0 */ public function render() { - return OC_OCS::generateXml( - $this->format, $this->getStatus(), $this->statuscode, $this->message, - $this->data, $this->tag, $this->tagattribute, $this->dimension, - $this->itemscount, $this->itemsperpage - ); + $r = new \OC_OCS_Result($this->data, $this->statuscode, $this->message); + $r->setTotalItems($this->itemscount); + $r->setItemsPerPage($this->itemsperpage); + + return \OC_API::renderResult($r, $this->format); } diff --git a/lib/public/appframework/ocscontroller.php b/lib/public/appframework/ocscontroller.php index 602731fe761..55ba518020a 100644 --- a/lib/public/appframework/ocscontroller.php +++ b/lib/public/appframework/ocscontroller.php @@ -42,7 +42,7 @@ abstract class OCSController extends ApiController { * constructor of the controller * @param string $appName the name of the app * @param IRequest $request an instance of the request - * @param string $corsMethods comma seperated string of HTTP verbs which + * @param string $corsMethods comma separated string of HTTP verbs which * should be allowed for websites or webapps when calling your API, defaults to * 'PUT, POST, GET, DELETE, PATCH' * @param string $corsAllowedHeaders comma seperated string of HTTP headers @@ -80,13 +80,9 @@ abstract class OCSController extends ApiController { } $params = [ - 'status' => 'OK', 'statuscode' => 100, 'message' => 'OK', 'data' => [], - 'tag' => '', - 'tagattribute' => '', - 'dimension' => 'dynamic', 'itemscount' => '', 'itemsperpage' => '' ]; @@ -96,9 +92,8 @@ abstract class OCSController extends ApiController { } return new OCSResponse( - $format, $params['status'], $params['statuscode'], - $params['message'], $params['data'], $params['tag'], - $params['tagattribute'], $params['dimension'], + $format, $params['statuscode'], + $params['message'], $params['data'], $params['itemscount'], $params['itemsperpage'] ); } diff --git a/ocs/v1.php b/ocs/v1.php index 2829cf08c57..c5c18d20b8a 100644 --- a/ocs/v1.php +++ b/ocs/v1.php @@ -56,5 +56,7 @@ try { } catch (MethodNotAllowedException $e) { OC_API::setContentType(); OC_Response::setStatus(405); +} catch (\OC\OCS\Exception $ex) { + OC_API::respond($ex->getResult(), OC_API::requestedFormat()); } diff --git a/ocs/v2.php b/ocs/v2.php new file mode 100644 index 00000000000..b2e3b259727 --- /dev/null +++ b/ocs/v2.php @@ -0,0 +1,22 @@ +<?php +/** + * @author Thomas Müller <thomas.mueller@tmit.eu> + * + * @copyright Copyright (c) 2015, ownCloud, Inc. + * @license AGPL-3.0 + * + * This code is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License, version 3, + * as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License, version 3, + * along with this program. If not, see <http://www.gnu.org/licenses/> + * + */ + +require_once 'v1.php'; diff --git a/tests/lib/appframework/controller/OCSControllerTest.php b/tests/lib/appframework/controller/OCSControllerTest.php index 11a9d45eb92..92b092cf0e9 100644 --- a/tests/lib/appframework/controller/OCSControllerTest.php +++ b/tests/lib/appframework/controller/OCSControllerTest.php @@ -69,9 +69,11 @@ class OCSControllerTest extends \Test\TestCase { $expected = "<?xml version=\"1.0\"?>\n" . "<ocs>\n" . " <meta>\n" . - " <status>OK</status>\n" . + " <status>failure</status>\n" . " <statuscode>400</statuscode>\n" . " <message>OK</message>\n" . + " <totalitems></totalitems>\n" . + " <itemsperpage></itemsperpage>\n" . " </meta>\n" . " <data>\n" . " <test>hi</test>\n" . @@ -99,9 +101,11 @@ class OCSControllerTest extends \Test\TestCase { $expected = "<?xml version=\"1.0\"?>\n" . "<ocs>\n" . " <meta>\n" . - " <status>OK</status>\n" . + " <status>failure</status>\n" . " <statuscode>400</statuscode>\n" . " <message>OK</message>\n" . + " <totalitems></totalitems>\n" . + " <itemsperpage></itemsperpage>\n" . " </meta>\n" . " <data>\n" . " <test>hi</test>\n" . @@ -126,8 +130,8 @@ class OCSControllerTest extends \Test\TestCase { $this->getMock('\OCP\Security\ISecureRandom'), $this->getMock('\OCP\IConfig') )); - $expected = '{"status":"OK","statuscode":400,"message":"OK",' . - '"totalitems":"","itemsperpage":"","data":{"test":"hi"}}'; + $expected = '{"ocs":{"meta":{"status":"failure","statuscode":400,"message":"OK",' . + '"totalitems":"","itemsperpage":""},"data":{"test":"hi"}}}'; $params = [ 'data' => [ 'test' => 'hi' diff --git a/tests/lib/appframework/http/OCSResponseTest.php b/tests/lib/appframework/http/OCSResponseTest.php index 111dc7ad0a3..1ca3e330bad 100644 --- a/tests/lib/appframework/http/OCSResponseTest.php +++ b/tests/lib/appframework/http/OCSResponseTest.php @@ -47,14 +47,13 @@ class OCSResponseTest extends \Test\TestCase { public function testRender() { $response = new OCSResponse( - 'xml', 'status', 2, 'message', ['test' => 'hi'], 'tag', 'abc', - 'dynamic', 3, 4 + 'xml', 2, 'message', ['test' => 'hi'], 3, 4 ); $out = $response->render(); $expected = "<?xml version=\"1.0\"?>\n" . "<ocs>\n" . " <meta>\n" . - " <status>status</status>\n" . + " <status>failure</status>\n" . " <statuscode>2</statuscode>\n" . " <message>message</message>\n" . " <totalitems>3</totalitems>\n" . diff --git a/tests/ocs/response.php b/tests/ocs/response.php new file mode 100644 index 00000000000..0d5883fa4cc --- /dev/null +++ b/tests/ocs/response.php @@ -0,0 +1,42 @@ +<?php +use OCP\AppFramework\Http; + +/** + * @author Thomas Müller <thomas.mueller@tmit.eu> + * + * @copyright Copyright (c) 2015, ownCloud, Inc. + * @license AGPL-3.0 + * + * This code is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License, version 3, + * as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License, version 3, + * along with this program. If not, see <http://www.gnu.org/licenses/> + * + */ + +class OcsResponseTest extends \Test\TestCase { + + /** + * @dataProvider providesStatusCodes + */ + public function testStatusCodeMapper($expected, $sc) { + $result = OC_API::mapStatusCodes($sc); + $this->assertEquals($expected, $result); + } + + public function providesStatusCodes() { + return [ + [Http::STATUS_OK, 100], + [Http::STATUS_BAD_REQUEST, 104], + [Http::STATUS_OK, 1000], + [201, 201], + ]; + } +} |