diff --git a/composer.json b/composer.json index 02949a3..aa4ac4a 100644 --- a/composer.json +++ b/composer.json @@ -25,7 +25,8 @@ "fguillot/picofeed": ">=0.1.31", "hosteurope/password-generator": "^1.0", "docopt/docopt": "^1.0", - "jkingweb/druuid": "^3.0" + "jkingweb/druuid": "^3.0", + "zendframework/zend-diactoros": "^1.6" }, "require-dev": { "bamarni/composer-bin-plugin": "*" diff --git a/composer.lock b/composer.lock index 8a76cbd..3c1262a 100644 --- a/composer.lock +++ b/composer.lock @@ -4,7 +4,7 @@ "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#composer-lock-the-lock-file", "This file is @generated automatically" ], - "content-hash": "8a3c7ff23f125a5fa3dac2e6a7244a90", + "content-hash": "7d381fa958169b7079c1d3c5b911f3bd", "packages": [ { "name": "docopt/docopt", @@ -190,6 +190,108 @@ ], "time": "2017-02-09T14:17:01+00:00" }, + { + "name": "psr/http-message", + "version": "1.0.1", + "source": { + "type": "git", + "url": "https://github.com/php-fig/http-message.git", + "reference": "f6561bf28d520154e4b0ec72be95418abe6d9363" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/php-fig/http-message/zipball/f6561bf28d520154e4b0ec72be95418abe6d9363", + "reference": "f6561bf28d520154e4b0ec72be95418abe6d9363", + "shasum": "" + }, + "require": { + "php": ">=5.3.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.0.x-dev" + } + }, + "autoload": { + "psr-4": { + "Psr\\Http\\Message\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "PHP-FIG", + "homepage": "http://www.php-fig.org/" + } + ], + "description": "Common interface for HTTP messages", + "homepage": "https://github.com/php-fig/http-message", + "keywords": [ + "http", + "http-message", + "psr", + "psr-7", + "request", + "response" + ], + "time": "2016-08-06T14:39:51+00:00" + }, + { + "name": "zendframework/zend-diactoros", + "version": "1.6.1", + "source": { + "type": "git", + "url": "https://github.com/zendframework/zend-diactoros.git", + "reference": "c8664b92a6d5bc229e48b0923486c097e45a7877" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/zendframework/zend-diactoros/zipball/c8664b92a6d5bc229e48b0923486c097e45a7877", + "reference": "c8664b92a6d5bc229e48b0923486c097e45a7877", + "shasum": "" + }, + "require": { + "php": "^5.6 || ^7.0", + "psr/http-message": "^1.0" + }, + "provide": { + "psr/http-message-implementation": "1.0" + }, + "require-dev": { + "ext-dom": "*", + "ext-libxml": "*", + "phpunit/phpunit": "^5.7.16 || ^6.0.8", + "zendframework/zend-coding-standard": "~1.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.6-dev", + "dev-develop": "1.7-dev" + } + }, + "autoload": { + "psr-4": { + "Zend\\Diactoros\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-2-Clause" + ], + "description": "PSR HTTP Message implementations", + "homepage": "https://github.com/zendframework/zend-diactoros", + "keywords": [ + "http", + "psr", + "psr-7" + ], + "time": "2017-10-12T15:24:51+00:00" + }, { "name": "zendframework/zendxml", "version": "1.0.2", diff --git a/lib/REST.php b/lib/REST.php index d79c003..ea8c87d 100644 --- a/lib/REST.php +++ b/lib/REST.php @@ -48,7 +48,7 @@ class REST { public function __construct() { } - public function dispatch(REST\Request $req = null): REST\Response { + public function dispatch(REST\Request $req = null): \Psr\Http\Message\ResponseInterface { if ($req===null) { $req = new REST\Request(); } diff --git a/lib/REST/AbstractHandler.php b/lib/REST/AbstractHandler.php index d471525..273e61a 100644 --- a/lib/REST/AbstractHandler.php +++ b/lib/REST/AbstractHandler.php @@ -11,7 +11,7 @@ use JKingWeb\Arsse\Misc\ValueInfo; abstract class AbstractHandler implements Handler { abstract public function __construct(); - abstract public function dispatch(Request $req): Response; + abstract public function dispatch(Request $req): \Psr\Http\Message\ResponseInterface; protected function fieldMapNames(array $data, array $map): array { $out = []; diff --git a/lib/REST/Handler.php b/lib/REST/Handler.php index 05ddaff..4d4904a 100644 --- a/lib/REST/Handler.php +++ b/lib/REST/Handler.php @@ -8,5 +8,5 @@ namespace JKingWeb\Arsse\REST; interface Handler { public function __construct(); - public function dispatch(Request $req): Response; + public function dispatch(Request $req): \Psr\Http\Message\ResponseInterface; } diff --git a/lib/REST/NextCloudNews/V1_2.php b/lib/REST/NextCloudNews/V1_2.php index 814ee57..bf39551 100644 --- a/lib/REST/NextCloudNews/V1_2.php +++ b/lib/REST/NextCloudNews/V1_2.php @@ -15,7 +15,9 @@ use JKingWeb\Arsse\Misc\ValueInfo; use JKingWeb\Arsse\AbstractException; use JKingWeb\Arsse\Db\ExceptionInput; use JKingWeb\Arsse\Feed\Exception as FeedException; -use JKingWeb\Arsse\REST\Response; +use \Psr\Http\Message\ResponseInterface; +use Zend\Diactoros\Response\JsonResponse as Response; +use Zend\Diactoros\Response\EmptyResponse; class V1_2 extends \JKingWeb\Arsse\REST\AbstractHandler { const REALM = "NextCloud News API v1-2"; @@ -72,10 +74,10 @@ class V1_2 extends \JKingWeb\Arsse\REST\AbstractHandler { public function __construct() { } - public function dispatch(\JKingWeb\Arsse\REST\Request $req): Response { + public function dispatch(\JKingWeb\Arsse\REST\Request $req): ResponseInterface { // try to authenticate if (!Arsse::$user->authHTTP()) { - return new Response(401, "", "", ['WWW-Authenticate: Basic realm="'.self::REALM.'"']); + return new EmptyResponse(401, ['WWW-Authenticate' => 'Basic realm="'.self::REALM.'"']); } // handle HTTP OPTIONS requests if ($req->method=="OPTIONS") { @@ -85,12 +87,12 @@ class V1_2 extends \JKingWeb\Arsse\REST\AbstractHandler { if ($req->body) { // if the entity body is not JSON according to content type, return "415 Unsupported Media Type" if (!preg_match("<^application/json\b|^$>", $req->type)) { - return new Response(415, "", "", ['Accept: application/json']); + return new EmptyResponse(415, ['Accept' => "application/json"]); } $data = @json_decode($req->body, true); if (json_last_error() != \JSON_ERROR_NONE) { // if the body could not be parsed as JSON, return "400 Bad Request" - return new Response(400); + return new EmptyResponse(400); } } else { $data = []; @@ -101,12 +103,12 @@ class V1_2 extends \JKingWeb\Arsse\REST\AbstractHandler { try { $func = $this->chooseCall($req->paths, $req->method); } catch (Exception404 $e) { - return new Response(404); + return new EmptyResponse(404); } catch (Exception405 $e) { - return new Response(405, "", "", ["Allow: ".$e->getMessage()]); + return new EmptyResponse(405, ['Allow' => $e->getMessage()]); } if (!method_exists($this, $func)) { - return new Response(501); // @codeCoverageIgnore + return new EmptyResponse(501); // @codeCoverageIgnore } // dispatch try { @@ -114,10 +116,10 @@ class V1_2 extends \JKingWeb\Arsse\REST\AbstractHandler { // @codeCoverageIgnoreStart } catch (Exception $e) { // if there was a REST exception return 400 - return new Response(400); + return new EmptyResponse(400); } catch (AbstractException $e) { // if there was any other Arsse exception return 500 - return new Response(500); + return new EmptyResponse(500); } // @codeCoverageIgnoreEnd } @@ -242,7 +244,7 @@ class V1_2 extends \JKingWeb\Arsse\REST\AbstractHandler { return $article; } - protected function handleHTTPOptions(array $url): Response { + protected function handleHTTPOptions(array $url): ResponseInterface { // normalize the URL path $url = $this->normalizePath($url); if (isset($this->paths[$url])) { @@ -252,81 +254,81 @@ class V1_2 extends \JKingWeb\Arsse\REST\AbstractHandler { if (in_array("GET", $allowed)) { array_unshift($allowed, "HEAD"); } - return new Response(204, "", "", [ - "Allow: ".implode(",", $allowed), - "Accept: application/json", + return new EmptyResponse(204, [ + 'Allow' => implode(",", $allowed), + 'Accept' => "application/json", ]); } else { // if the path is not supported, return 404 - return new Response(404); + return new EmptyResponse(404); } } // list folders - protected function folderList(array $url, array $data): Response { + protected function folderList(array $url, array $data): ResponseInterface { $folders = []; foreach (Arsse::$db->folderList(Arsse::$user->id, null, false) as $folder) { $folders[] = $this->folderTranslate($folder); } - return new Response(200, ['folders' => $folders]); + return new Response(['folders' => $folders]); } // create a folder - protected function folderAdd(array $url, array $data): Response { + protected function folderAdd(array $url, array $data): ResponseInterface { try { $folder = Arsse::$db->folderAdd(Arsse::$user->id, ['name' => $data['name']]); } catch (ExceptionInput $e) { switch ($e->getCode()) { // folder already exists - case 10236: return new Response(409); + case 10236: return new EmptyResponse(409); // folder name not acceptable case 10231: - case 10232: return new Response(422); + case 10232: return new EmptyResponse(422); // other errors related to input - default: return new Response(400); // @codeCoverageIgnore + default: return new EmptyResponse(400); // @codeCoverageIgnore } } $folder = $this->folderTranslate(Arsse::$db->folderPropertiesGet(Arsse::$user->id, $folder)); - return new Response(200, ['folders' => [$folder]]); + return new Response(['folders' => [$folder]]); } // delete a folder - protected function folderRemove(array $url, array $data): Response { + protected function folderRemove(array $url, array $data): ResponseInterface { // perform the deletion try { Arsse::$db->folderRemove(Arsse::$user->id, (int) $url[1]); } catch (ExceptionInput $e) { // folder does not exist - return new Response(404); + return new EmptyResponse(404); } - return new Response(204); + return new EmptyResponse(204); } // rename a folder (also supports moving nesting folders, but this is not a feature of the API) - protected function folderRename(array $url, array $data): Response { + protected function folderRename(array $url, array $data): ResponseInterface { try { Arsse::$db->folderPropertiesSet(Arsse::$user->id, (int) $url[1], ['name' => $data['name']]); } catch (ExceptionInput $e) { switch ($e->getCode()) { // folder does not exist - case 10239: return new Response(404); + case 10239: return new EmptyResponse(404); // folder already exists - case 10236: return new Response(409); + case 10236: return new EmptyResponse(409); // folder name not acceptable case 10231: - case 10232: return new Response(422); + case 10232: return new EmptyResponse(422); // other errors related to input - default: return new Response(400); // @codeCoverageIgnore + default: return new EmptyResponse(400); // @codeCoverageIgnore } } - return new Response(204); + return new EmptyResponse(204); } // mark all articles associated with a folder as read - protected function folderMarkRead(array $url, array $data): Response { + protected function folderMarkRead(array $url, array $data): ResponseInterface { if (!ValueInfo::id($data['newestItemId'])) { // if the item ID is invalid (i.e. not a positive integer), this is an error - return new Response(422); + return new EmptyResponse(422); } // build the context $c = new Context; @@ -337,16 +339,16 @@ class V1_2 extends \JKingWeb\Arsse\REST\AbstractHandler { Arsse::$db->articleMark(Arsse::$user->id, ['read' => true], $c); } catch (ExceptionInput $e) { // folder does not exist - return new Response(404); + return new EmptyResponse(404); } - return new Response(204); + return new EmptyResponse(204); } // return list of feeds which should be refreshed - protected function feedListStale(array $url, array $data): Response { + protected function feedListStale(array $url, array $data): ResponseInterface { // function requires admin rights per spec if (Arsse::$user->rightsGet(Arsse::$user->id)==User::RIGHTS_NONE) { - return new Response(403); + return new EmptyResponse(403); } // list stale feeds which should be checked for updates $feeds = Arsse::$db->feedListStale(); @@ -355,42 +357,42 @@ class V1_2 extends \JKingWeb\Arsse\REST\AbstractHandler { // since in our implementation feeds don't belong the users, the 'userId' field will always be an empty string $out[] = ['id' => $feed, 'userId' => ""]; } - return new Response(200, ['feeds' => $out]); + return new Response(['feeds' => $out]); } // refresh a feed - protected function feedUpdate(array $url, array $data): Response { + protected function feedUpdate(array $url, array $data): ResponseInterface { // function requires admin rights per spec if (Arsse::$user->rightsGet(Arsse::$user->id)==User::RIGHTS_NONE) { - return new Response(403); + return new EmptyResponse(403); } try { Arsse::$db->feedUpdate($data['feedId']); } catch (ExceptionInput $e) { switch ($e->getCode()) { case 10239: // feed does not exist - return new Response(404); + return new EmptyResponse(404); case 10237: // feed ID invalid - return new Response(422); + return new EmptyResponse(422); default: // other errors related to input - return new Response(400); // @codeCoverageIgnore + return new EmptyResponse(400); // @codeCoverageIgnore } } - return new Response(204); + return new EmptyResponse(204); } // add a new feed - protected function subscriptionAdd(array $url, array $data): Response { + protected function subscriptionAdd(array $url, array $data): ResponseInterface { // try to add the feed $tr = Arsse::$db->begin(); try { $id = Arsse::$db->subscriptionAdd(Arsse::$user->id, (string) $data['url']); } catch (ExceptionInput $e) { // feed already exists - return new Response(409); + return new EmptyResponse(409); } catch (FeedException $e) { // feed could not be retrieved - return new Response(422); + return new EmptyResponse(422); } // if a folder was specified, move the feed to the correct folder; silently ignore errors if ($data['folderId']) { @@ -408,11 +410,11 @@ class V1_2 extends \JKingWeb\Arsse\REST\AbstractHandler { if ($newest) { $out['newestItemId'] = $newest; } - return new Response(200, $out); + return new Response($out); } // return list of feeds for the logged-in user - protected function subscriptionList(array $url, array $data): Response { + protected function subscriptionList(array $url, array $data): ResponseInterface { $subs = Arsse::$db->subscriptionList(Arsse::$user->id); $out = []; foreach ($subs as $sub) { @@ -424,43 +426,43 @@ class V1_2 extends \JKingWeb\Arsse\REST\AbstractHandler { if ($newest) { $out['newestItemId'] = $newest; } - return new Response(200, $out); + return new Response($out); } // delete a feed - protected function subscriptionRemove(array $url, array $data): Response { + protected function subscriptionRemove(array $url, array $data): ResponseInterface { try { Arsse::$db->subscriptionRemove(Arsse::$user->id, (int) $url[1]); } catch (ExceptionInput $e) { // feed does not exist - return new Response(404); + return new EmptyResponse(404); } - return new Response(204); + return new EmptyResponse(204); } // rename a feed - protected function subscriptionRename(array $url, array $data): Response { + protected function subscriptionRename(array $url, array $data): ResponseInterface { try { Arsse::$db->subscriptionPropertiesSet(Arsse::$user->id, (int) $url[1], ['title' => (string) $data['feedTitle']]); } catch (ExceptionInput $e) { switch ($e->getCode()) { // subscription does not exist - case 10239: return new Response(404); + case 10239: return new EmptyResponse(404); // name is invalid case 10231: - case 10232: return new Response(422); + case 10232: return new EmptyResponse(422); // other errors related to input - default: return new Response(400); // @codeCoverageIgnore + default: return new EmptyResponse(400); // @codeCoverageIgnore } } - return new Response(204); + return new EmptyResponse(204); } // move a feed to a folder - protected function subscriptionMove(array $url, array $data): Response { + protected function subscriptionMove(array $url, array $data): ResponseInterface { // if no folder is specified this is an error if (!isset($data['folderId'])) { - return new Response(422); + return new EmptyResponse(422); } // perform the move try { @@ -468,22 +470,22 @@ class V1_2 extends \JKingWeb\Arsse\REST\AbstractHandler { } catch (ExceptionInput $e) { switch ($e->getCode()) { case 10239: // subscription does not exist - return new Response(404); + return new EmptyResponse(404); case 10235: // folder does not exist case 10237: // folder ID is invalid - return new Response(422); + return new EmptyResponse(422); default: // other errors related to input - return new Response(400); // @codeCoverageIgnore + return new EmptyResponse(400); // @codeCoverageIgnore } } - return new Response(204); + return new EmptyResponse(204); } // mark all articles associated with a subscription as read - protected function subscriptionMarkRead(array $url, array $data): Response { + protected function subscriptionMarkRead(array $url, array $data): ResponseInterface { if (!ValueInfo::id($data['newestItemId'])) { // if the item ID is invalid (i.e. not a positive integer), this is an error - return new Response(422); + return new EmptyResponse(422); } // build the context $c = new Context; @@ -494,13 +496,13 @@ class V1_2 extends \JKingWeb\Arsse\REST\AbstractHandler { Arsse::$db->articleMark(Arsse::$user->id, ['read' => true], $c); } catch (ExceptionInput $e) { // subscription does not exist - return new Response(404); + return new EmptyResponse(404); } - return new Response(204); + return new EmptyResponse(204); } // list articles and their properties - protected function articleList(array $url, array $data): Response { + protected function articleList(array $url, array $data): ResponseInterface { // set the context options supplied by the client $c = new Context; // set the batch size @@ -553,32 +555,32 @@ class V1_2 extends \JKingWeb\Arsse\REST\AbstractHandler { $items = Arsse::$db->articleList(Arsse::$user->id, $c, Database::LIST_TYPICAL); } catch (ExceptionInput $e) { // ID of subscription or folder is not valid - return new Response(422); + return new EmptyResponse(422); } $out = []; foreach ($items as $item) { $out[] = $this->articleTranslate($item); } $out = ['items' => $out]; - return new Response(200, $out); + return new Response($out); } // mark all articles as read - protected function articleMarkReadAll(array $url, array $data): Response { + protected function articleMarkReadAll(array $url, array $data): ResponseInterface { if (!ValueInfo::id($data['newestItemId'])) { // if the item ID is invalid (i.e. not a positive integer), this is an error - return new Response(422); + return new EmptyResponse(422); } // build the context $c = new Context; $c->latestEdition((int) $data['newestItemId']); // perform the operation Arsse::$db->articleMark(Arsse::$user->id, ['read' => true], $c); - return new Response(204); + return new EmptyResponse(204); } // mark a single article as read - protected function articleMarkRead(array $url, array $data): Response { + protected function articleMarkRead(array $url, array $data): ResponseInterface { // initialize the matching context $c = new Context; $c->edition((int) $url[1]); @@ -588,13 +590,13 @@ class V1_2 extends \JKingWeb\Arsse\REST\AbstractHandler { Arsse::$db->articleMark(Arsse::$user->id, ['read' => $set], $c); } catch (ExceptionInput $e) { // ID is not valid - return new Response(404); + return new EmptyResponse(404); } - return new Response(204); + return new EmptyResponse(204); } // mark a single article as read - protected function articleMarkStarred(array $url, array $data): Response { + protected function articleMarkStarred(array $url, array $data): ResponseInterface { // initialize the matching context $c = new Context; $c->article((int) $url[2]); @@ -604,13 +606,13 @@ class V1_2 extends \JKingWeb\Arsse\REST\AbstractHandler { Arsse::$db->articleMark(Arsse::$user->id, ['starred' => $set], $c); } catch (ExceptionInput $e) { // ID is not valid - return new Response(404); + return new EmptyResponse(404); } - return new Response(204); + return new EmptyResponse(204); } // mark an array of articles as read - protected function articleMarkReadMulti(array $url, array $data): Response { + protected function articleMarkReadMulti(array $url, array $data): ResponseInterface { // determine whether to mark read or unread $set = ($url[1]=="read"); // initialize the matching context @@ -620,11 +622,11 @@ class V1_2 extends \JKingWeb\Arsse\REST\AbstractHandler { Arsse::$db->articleMark(Arsse::$user->id, ['read' => $set], $c); } catch (ExceptionInput $e) { } - return new Response(204); + return new EmptyResponse(204); } // mark an array of articles as starred - protected function articleMarkStarredMulti(array $url, array $data): Response { + protected function articleMarkStarredMulti(array $url, array $data): ResponseInterface { // determine whether to mark starred or unstarred $set = ($url[1]=="star"); // initialize the matching context @@ -634,10 +636,10 @@ class V1_2 extends \JKingWeb\Arsse\REST\AbstractHandler { Arsse::$db->articleMark(Arsse::$user->id, ['starred' => $set], $c); } catch (ExceptionInput $e) { } - return new Response(204); + return new EmptyResponse(204); } - protected function userStatus(array $url, array $data): Response { + protected function userStatus(array $url, array $data): ResponseInterface { $data = Arsse::$user->propertiesGet(Arsse::$user->id, true); // construct the avatar structure, if an image is available if (isset($data['avatar'])) { @@ -655,37 +657,37 @@ class V1_2 extends \JKingWeb\Arsse\REST\AbstractHandler { 'lastLoginTimestamp' => time(), 'avatar' => $avatar, ]; - return new Response(200, $out); + return new Response($out); } - protected function cleanupBefore(array $url, array $data): Response { + protected function cleanupBefore(array $url, array $data): ResponseInterface { // function requires admin rights per spec if (Arsse::$user->rightsGet(Arsse::$user->id)==User::RIGHTS_NONE) { - return new Response(403); + return new EmptyResponse(403); } Service::cleanupPre(); - return new Response(204); + return new EmptyResponse(204); } - protected function cleanupAfter(array $url, array $data): Response { + protected function cleanupAfter(array $url, array $data): ResponseInterface { // function requires admin rights per spec if (Arsse::$user->rightsGet(Arsse::$user->id)==User::RIGHTS_NONE) { - return new Response(403); + return new EmptyResponse(403); } Service::cleanupPost(); - return new Response(204); + return new EmptyResponse(204); } // return the server version - protected function serverVersion(array $url, array $data): Response { - return new Response(200, [ + protected function serverVersion(array $url, array $data): ResponseInterface { + return new Response([ 'version' => self::VERSION, 'arsse_version' => Arsse::VERSION, ]); } - protected function serverStatus(array $url, array $data): Response { - return new Response(200, [ + protected function serverStatus(array $url, array $data): ResponseInterface { + return new Response([ 'version' => self::VERSION, 'arsse_version' => Arsse::VERSION, 'warnings' => [ diff --git a/lib/REST/NextCloudNews/Versions.php b/lib/REST/NextCloudNews/Versions.php index b51773f..1f97e77 100644 --- a/lib/REST/NextCloudNews/Versions.php +++ b/lib/REST/NextCloudNews/Versions.php @@ -6,22 +6,23 @@ declare(strict_types=1); namespace JKingWeb\Arsse\REST\NextCloudNews; -use JKingWeb\Arsse\REST\Response; +use Zend\Diactoros\Response\JsonResponse as Response; +use Zend\Diactoros\Response\EmptyResponse; class Versions implements \JKingWeb\Arsse\REST\Handler { public function __construct() { } - public function dispatch(\JKingWeb\Arsse\REST\Request $req): Response { + public function dispatch(\JKingWeb\Arsse\REST\Request $req): \Psr\Http\Message\ResponseInterface { if (!preg_match("<^/?$>", $req->path)) { // if the request path is an empty string or just a slash, the client is probably trying a version we don't support - return new Response(404); + return new EmptyResponse(404); } elseif ($req->method=="OPTIONS") { // if the request method is OPTIONS, respond accordingly - return new Response(204, "", "", ["Allow: HEAD,GET"]); + return new EmptyResponse(204, ['Allow' => "HEAD,GET"]); } elseif ($req->method != "GET") { // if a method other than GET was used, this is an error - return new Response(405, "", "", ["Allow: HEAD,GET"]); + return new EmptyResponse(405, ['Allow' => "HEAD,GET"]); } else { // otherwise return the supported versions $out = [ @@ -29,7 +30,7 @@ class Versions implements \JKingWeb\Arsse\REST\Handler { 'v1-2', ] ]; - return new Response(200, $out); + return new Response($out); } } } diff --git a/lib/REST/TinyTinyRSS/API.php b/lib/REST/TinyTinyRSS/API.php index 7435c80..2464954 100644 --- a/lib/REST/TinyTinyRSS/API.php +++ b/lib/REST/TinyTinyRSS/API.php @@ -19,7 +19,8 @@ use JKingWeb\Arsse\ExceptionType; use JKingWeb\Arsse\Db\ExceptionInput; use JKingWeb\Arsse\Db\ResultEmpty; use JKingWeb\Arsse\Feed\Exception as FeedException; -use JKingWeb\Arsse\REST\Response; +use Zend\Diactoros\Response\JsonResponse as Response; +use Zend\Diactoros\Response\EmptyResponse; class API extends \JKingWeb\Arsse\REST\AbstractHandler { const LEVEL = 14; // emulated API level @@ -88,23 +89,23 @@ class API extends \JKingWeb\Arsse\REST\AbstractHandler { public function __construct() { } - public function dispatch(\JKingWeb\Arsse\REST\Request $req): Response { + public function dispatch(\JKingWeb\Arsse\REST\Request $req): \Psr\Http\Message\ResponseInterface { if (!preg_match("<^(?:/(?:index\.php)?)?$>", $req->path)) { // reject paths other than the index - return new Response(404); + return new EmptyResponse(404); } if ($req->method=="OPTIONS") { // respond to OPTIONS rquests; the response is a fib, as we technically accept any type or method - return new Response(204, "", "", [ - "Allow: POST", - "Accept: application/json, text/json", + return new EmptyResponse(204, [ + 'Allow' => "POST", + 'Accept' => "application/json, text/json", ]); } if ($req->body) { // only JSON entities are allowed, but Content-Type is ignored, as is request method $data = @json_decode($req->body, true); if (json_last_error() != \JSON_ERROR_NONE || !is_array($data)) { - return new Response(200, self::FATAL_ERR); + return new Response(self::FATAL_ERR); } try { // normalize input @@ -123,23 +124,23 @@ class API extends \JKingWeb\Arsse\REST\AbstractHandler { // TT-RSS operations are case-insensitive by dint of PHP method names being case-insensitive; this will only trigger if the method really doesn't exist throw new Exception("UNKNOWN_METHOD", ['method' => $data['op']]); } - return new Response(200, [ + return new Response([ 'seq' => $data['seq'], 'status' => 0, 'content' => $this->$method($data), ]); } catch (Exception $e) { - return new Response(200, [ + return new Response([ 'seq' => $data['seq'], 'status' => 1, 'content' => $e->getData(), ]); } catch (AbstractException $e) { - return new Response(500); + return new EmptyResponse(500); } } else { // absence of a request body indicates an error - return new Response(200, self::FATAL_ERR); + return new Response(self::FATAL_ERR); } } diff --git a/lib/REST/TinyTinyRSS/Icon.php b/lib/REST/TinyTinyRSS/Icon.php index aabb2c2..3641f17 100644 --- a/lib/REST/TinyTinyRSS/Icon.php +++ b/lib/REST/TinyTinyRSS/Icon.php @@ -7,16 +7,16 @@ declare(strict_types=1); namespace JKingWeb\Arsse\REST\TinyTinyRSS; use JKingWeb\Arsse\Arsse; -use JKingWeb\Arsse\REST\Response; +use Zend\Diactoros\Response\EmptyResponse as Response; class Icon extends \JKingWeb\Arsse\REST\AbstractHandler { public function __construct() { } - public function dispatch(\JKingWeb\Arsse\REST\Request $req): Response { + public function dispatch(\JKingWeb\Arsse\REST\Request $req): \Psr\Http\Message\ResponseInterface { if ($req->method != "GET") { // only GET requests are allowed - return new Response(405, "", "", ["Allow: GET"]); + return new Response(405, ['Allow' => "GET"]); } elseif (!preg_match("<^(\d+)\.ico$>", $req->url, $match) || !((int) $match[1])) { return new Response(404); } @@ -26,7 +26,7 @@ class Icon extends \JKingWeb\Arsse\REST\AbstractHandler { if (($pos = strpos($url, "\r")) !== false || ($pos = strpos($url, "\n")) !== false) { $url = substr($url, 0, $pos); } - return new Response(301, "", "", ["Location: $url"]); + return new Response(301, ['Location' => $url]); } else { return new Response(404); } diff --git a/tests/cases/REST/NextCloudNews/TestV1_2.php b/tests/cases/REST/NextCloudNews/TestV1_2.php index 17c4679..d16e101 100644 --- a/tests/cases/REST/NextCloudNews/TestV1_2.php +++ b/tests/cases/REST/NextCloudNews/TestV1_2.php @@ -12,13 +12,14 @@ use JKingWeb\Arsse\User; use JKingWeb\Arsse\Database; use JKingWeb\Arsse\Service; use JKingWeb\Arsse\REST\Request; -use JKingWeb\Arsse\REST\Response; use JKingWeb\Arsse\Test\Result; use JKingWeb\Arsse\Misc\Date; use JKingWeb\Arsse\Misc\Context; use JKingWeb\Arsse\Db\ExceptionInput; use JKingWeb\Arsse\Db\Transaction; use JKingWeb\Arsse\REST\NextCloudNews\V1_2; +use Zend\Diactoros\Response\JsonResponse as Response; +use Zend\Diactoros\Response\EmptyResponse; use Phake; /** @covers \JKingWeb\Arsse\REST\NextCloudNews\V1_2 */ @@ -317,14 +318,9 @@ class TestV1_2 extends \JKingWeb\Arsse\Test\AbstractTest { $this->clearData(); } - protected function assertResponse(Response $exp, Response $act, string $text = null) { - $this->assertEquals($exp, $act, $text); - $this->assertSame($exp->payload, $act->payload, $text); - } - public function testSendAuthenticationChallenge() { Phake::when(Arsse::$user)->authHTTP->thenReturn(false); - $exp = new Response(401, "", "", ['WWW-Authenticate: Basic realm="'.V1_2::REALM.'"']); + $exp = new EmptyResponse(401, ['WWW-Authenticate' => 'Basic realm="'.V1_2::REALM.'"']); $this->assertResponse($exp, $this->h->dispatch(new Request("GET", "/"))); } @@ -361,12 +357,12 @@ class TestV1_2 extends \JKingWeb\Arsse\Test\AbstractTest { ], ]; foreach ($errs[404] as $req) { - $exp = new Response(404); + $exp = new EmptyResponse(404); list($method, $path) = $req; $this->assertResponse($exp, $this->h->dispatch(new Request($method, $path)), "$method call to $path did not return 404."); } foreach ($errs[405] as $allow => $cases) { - $exp = new Response(405, "", "", ['Allow: '.$allow]); + $exp = new EmptyResponse(405, ['Allow' => $allow]); foreach ($cases as $req) { list($method, $path) = $req; $this->assertResponse($exp, $this->h->dispatch(new Request($method, $path)), "$method call to $path did not return 405."); @@ -375,29 +371,29 @@ class TestV1_2 extends \JKingWeb\Arsse\Test\AbstractTest { } public function testRespondToInvalidInputTypes() { - $exp = new Response(415, "", "", ['Accept: application/json']); + $exp = new EmptyResponse(415, ['Accept' => "application/json"]); $this->assertResponse($exp, $this->h->dispatch(new Request("PUT", "/folders/1", '', 'application/xml'))); - $exp = new Response(400); + $exp = new EmptyResponse(400); $this->assertResponse($exp, $this->h->dispatch(new Request("PUT", "/folders/1", '', 'application/json'))); } public function testRespondToOptionsRequests() { - $exp = new Response(204, "", "", [ - "Allow: HEAD,GET,POST", - "Accept: application/json", + $exp = new EmptyResponse(204, [ + 'Allow' => "HEAD,GET,POST", + 'Accept' => "application/json", ]); $this->assertResponse($exp, $this->h->dispatch(new Request("OPTIONS", "/feeds"))); - $exp = new Response(204, "", "", [ - "Allow: DELETE", - "Accept: application/json", + $exp = new EmptyResponse(204, [ + 'Allow' => "DELETE", + 'Accept' => "application/json", ]); $this->assertResponse($exp, $this->h->dispatch(new Request("OPTIONS", "/feeds/2112"))); - $exp = new Response(204, "", "", [ - "Allow: HEAD,GET", - "Accept: application/json", + $exp = new EmptyResponse(204, [ + 'Allow' => "HEAD,GET", + 'Accept' => "application/json", ]); $this->assertResponse($exp, $this->h->dispatch(new Request("OPTIONS", "/user"))); - $exp = new Response(404); + $exp = new EmptyResponse(404); $this->assertResponse($exp, $this->h->dispatch(new Request("OPTIONS", "/invalid/path"))); } @@ -411,9 +407,9 @@ class TestV1_2 extends \JKingWeb\Arsse\Test\AbstractTest { ['id' => 12, 'name' => "Hardware"], ]; Phake::when(Arsse::$db)->folderList(Arsse::$user->id, null, false)->thenReturn(new Result([]))->thenReturn(new Result($list)); - $exp = new Response(200, ['folders' => []]); + $exp = new Response(['folders' => []]); $this->assertResponse($exp, $this->h->dispatch(new Request("GET", "/folders"))); - $exp = new Response(200, ['folders' => $out]); + $exp = new Response(['folders' => $out]); $this->assertResponse($exp, $this->h->dispatch(new Request("GET", "/folders"))); } @@ -441,33 +437,33 @@ class TestV1_2 extends \JKingWeb\Arsse\Test\AbstractTest { Phake::when(Arsse::$db)->folderAdd(Arsse::$user->id, ['name' => ""])->thenThrow(new ExceptionInput("missing")); Phake::when(Arsse::$db)->folderAdd(Arsse::$user->id, ['name' => " "])->thenThrow(new ExceptionInput("whitespace")); // correctly add two folders, using different means - $exp = new Response(200, ['folders' => [$out[0]]]); + $exp = new Response(['folders' => [$out[0]]]); $this->assertResponse($exp, $this->h->dispatch(new Request("POST", "/folders", json_encode($in[0]), 'application/json'))); - $exp = new Response(200, ['folders' => [$out[1]]]); + $exp = new Response(['folders' => [$out[1]]]); $this->assertResponse($exp, $this->h->dispatch(new Request("POST", "/folders?name=Hardware"))); Phake::verify(Arsse::$db)->folderAdd(Arsse::$user->id, $in[0]); Phake::verify(Arsse::$db)->folderAdd(Arsse::$user->id, $in[1]); Phake::verify(Arsse::$db)->folderPropertiesGet(Arsse::$user->id, 1); Phake::verify(Arsse::$db)->folderPropertiesGet(Arsse::$user->id, 2); // test bad folder names - $exp = new Response(422); + $exp = new EmptyResponse(422); $this->assertResponse($exp, $this->h->dispatch(new Request("POST", "/folders"))); $this->assertResponse($exp, $this->h->dispatch(new Request("POST", "/folders", '{"name":""}', 'application/json'))); $this->assertResponse($exp, $this->h->dispatch(new Request("POST", "/folders", '{"name":" "}', 'application/json'))); $this->assertResponse($exp, $this->h->dispatch(new Request("POST", "/folders", '{"name":{}}', 'application/json'))); // try adding the same two folders again - $exp = new Response(409); + $exp = new EmptyResponse(409); $this->assertResponse($exp, $this->h->dispatch(new Request("POST", "/folders?name=Software"))); - $exp = new Response(409); + $exp = new EmptyResponse(409); $this->assertResponse($exp, $this->h->dispatch(new Request("POST", "/folders", json_encode($in[1]), 'application/json'))); } public function testRemoveAFolder() { Phake::when(Arsse::$db)->folderRemove(Arsse::$user->id, 1)->thenReturn(true)->thenThrow(new ExceptionInput("subjectMissing")); - $exp = new Response(204); + $exp = new EmptyResponse(204); $this->assertResponse($exp, $this->h->dispatch(new Request("DELETE", "/folders/1"))); // fail on the second invocation because it no longer exists - $exp = new Response(404); + $exp = new EmptyResponse(404); $this->assertResponse($exp, $this->h->dispatch(new Request("DELETE", "/folders/1"))); Phake::verify(Arsse::$db, Phake::times(2))->folderRemove(Arsse::$user->id, 1); } @@ -486,22 +482,22 @@ class TestV1_2 extends \JKingWeb\Arsse\Test\AbstractTest { Phake::when(Arsse::$db)->folderPropertiesSet(Arsse::$user->id, 1, $in[3])->thenThrow(new ExceptionInput("whitespace")); Phake::when(Arsse::$db)->folderPropertiesSet(Arsse::$user->id, 1, $in[4])->thenReturn(true); // this should be stopped by the handler before the request gets to the database Phake::when(Arsse::$db)->folderPropertiesSet(Arsse::$user->id, 3, $this->anything())->thenThrow(new ExceptionInput("subjectMissing")); // folder ID 3 does not exist - $exp = new Response(204); + $exp = new EmptyResponse(204); $this->assertResponse($exp, $this->h->dispatch(new Request("PUT", "/folders/1", json_encode($in[0]), 'application/json'))); - $exp = new Response(409); + $exp = new EmptyResponse(409); $this->assertResponse($exp, $this->h->dispatch(new Request("PUT", "/folders/2", json_encode($in[1]), 'application/json'))); - $exp = new Response(422); + $exp = new EmptyResponse(422); $this->assertResponse($exp, $this->h->dispatch(new Request("PUT", "/folders/1", json_encode($in[2]), 'application/json'))); - $exp = new Response(422); + $exp = new EmptyResponse(422); $this->assertResponse($exp, $this->h->dispatch(new Request("PUT", "/folders/1", json_encode($in[3]), 'application/json'))); - $exp = new Response(422); + $exp = new EmptyResponse(422); $this->assertResponse($exp, $this->h->dispatch(new Request("PUT", "/folders/1", json_encode($in[4]), 'application/json'))); - $exp = new Response(404); + $exp = new EmptyResponse(404); $this->assertResponse($exp, $this->h->dispatch(new Request("PUT", "/folders/3", json_encode($in[0]), 'application/json'))); } public function testRetrieveServerVersion() { - $exp = new Response(200, [ + $exp = new Response([ 'version' => V1_2::VERSION, 'arsse_version' => Arsse::VERSION, ]); @@ -521,9 +517,9 @@ class TestV1_2 extends \JKingWeb\Arsse\Test\AbstractTest { Phake::when(Arsse::$db)->subscriptionList(Arsse::$user->id)->thenReturn(new Result([]))->thenReturn(new Result($this->feeds['db'])); Phake::when(Arsse::$db)->articleStarred(Arsse::$user->id)->thenReturn(['total' => 0])->thenReturn(['total' => 5]); Phake::when(Arsse::$db)->editionLatest(Arsse::$user->id)->thenReturn(0)->thenReturn(4758915); - $exp = new Response(200, $exp1); + $exp = new Response($exp1); $this->assertResponse($exp, $this->h->dispatch(new Request("GET", "/feeds"))); - $exp = new Response(200, $exp2); + $exp = new Response($exp2); $this->assertResponse($exp, $this->h->dispatch(new Request("GET", "/feeds"))); } @@ -556,31 +552,31 @@ class TestV1_2 extends \JKingWeb\Arsse\Test\AbstractTest { // set up a mock for a bad feed which succeeds the second time Phake::when(Arsse::$db)->subscriptionAdd(Arsse::$user->id, "http://example.net/news.atom")->thenThrow(new \JKingWeb\Arsse\Feed\Exception("http://example.net/news.atom", new \PicoFeed\Client\InvalidUrlException()))->thenReturn(47); // add the subscriptions - $exp = new Response(200, $out[0]); + $exp = new Response($out[0]); $this->assertResponse($exp, $this->h->dispatch(new Request("POST", "/feeds", json_encode($in[0]), 'application/json'))); - $exp = new Response(200, $out[1]); + $exp = new Response($out[1]); $this->assertResponse($exp, $this->h->dispatch(new Request("POST", "/feeds", json_encode($in[1]), 'application/json'))); // try to add them a second time - $exp = new Response(409); + $exp = new EmptyResponse(409); $this->assertResponse($exp, $this->h->dispatch(new Request("POST", "/feeds", json_encode($in[0]), 'application/json'))); $this->assertResponse($exp, $this->h->dispatch(new Request("POST", "/feeds", json_encode($in[1]), 'application/json'))); // try to add a bad feed - $exp = new Response(422); + $exp = new EmptyResponse(422); $this->assertResponse($exp, $this->h->dispatch(new Request("POST", "/feeds", json_encode($in[2]), 'application/json'))); // try again (this will succeed), with an invalid folder ID - $exp = new Response(200, $out[2]); + $exp = new Response($out[2]); $this->assertResponse($exp, $this->h->dispatch(new Request("POST", "/feeds", json_encode($in[3]), 'application/json'))); // try to add no feed - $exp = new Response(422); + $exp = new EmptyResponse(422); $this->assertResponse($exp, $this->h->dispatch(new Request("POST", "/feeds", json_encode($in[4]), 'application/json'))); } public function testRemoveASubscription() { Phake::when(Arsse::$db)->subscriptionRemove(Arsse::$user->id, 1)->thenReturn(true)->thenThrow(new ExceptionInput("subjectMissing")); - $exp = new Response(204); + $exp = new EmptyResponse(204); $this->assertResponse($exp, $this->h->dispatch(new Request("DELETE", "/feeds/1"))); // fail on the second invocation because it no longer exists - $exp = new Response(404); + $exp = new EmptyResponse(404); $this->assertResponse($exp, $this->h->dispatch(new Request("DELETE", "/feeds/1"))); Phake::verify(Arsse::$db, Phake::times(2))->subscriptionRemove(Arsse::$user->id, 1); } @@ -599,17 +595,17 @@ class TestV1_2 extends \JKingWeb\Arsse\Test\AbstractTest { Phake::when(Arsse::$db)->subscriptionPropertiesSet(Arsse::$user->id, 1, ['folder' => 2112])->thenThrow(new ExceptionInput("idMissing")); // folder does not exist Phake::when(Arsse::$db)->subscriptionPropertiesSet(Arsse::$user->id, 1, ['folder' => -1])->thenThrow(new ExceptionInput("typeViolation")); // folder is invalid Phake::when(Arsse::$db)->subscriptionPropertiesSet(Arsse::$user->id, 42, $this->anything())->thenThrow(new ExceptionInput("subjectMissing")); // subscription does not exist - $exp = new Response(204); + $exp = new EmptyResponse(204); $this->assertResponse($exp, $this->h->dispatch(new Request("PUT", "/feeds/1/move", json_encode($in[0]), 'application/json'))); - $exp = new Response(204); + $exp = new EmptyResponse(204); $this->assertResponse($exp, $this->h->dispatch(new Request("PUT", "/feeds/1/move", json_encode($in[1]), 'application/json'))); - $exp = new Response(422); + $exp = new EmptyResponse(422); $this->assertResponse($exp, $this->h->dispatch(new Request("PUT", "/feeds/1/move", json_encode($in[2]), 'application/json'))); - $exp = new Response(404); + $exp = new EmptyResponse(404); $this->assertResponse($exp, $this->h->dispatch(new Request("PUT", "/feeds/42/move", json_encode($in[3]), 'application/json'))); - $exp = new Response(422); + $exp = new EmptyResponse(422); $this->assertResponse($exp, $this->h->dispatch(new Request("PUT", "/feeds/1/move", json_encode($in[4]), 'application/json'))); - $exp = new Response(422); + $exp = new EmptyResponse(422); $this->assertResponse($exp, $this->h->dispatch(new Request("PUT", "/feeds/1/move", json_encode($in[5]), 'application/json'))); } @@ -629,17 +625,17 @@ class TestV1_2 extends \JKingWeb\Arsse\Test\AbstractTest { Phake::when(Arsse::$db)->subscriptionPropertiesSet(Arsse::$user->id, 1, $this->identicalTo(['title' => ""]))->thenThrow(new ExceptionInput("missing")); Phake::when(Arsse::$db)->subscriptionPropertiesSet(Arsse::$user->id, 1, $this->identicalTo(['title' => false]))->thenThrow(new ExceptionInput("missing")); Phake::when(Arsse::$db)->subscriptionPropertiesSet(Arsse::$user->id, 42, $this->anything())->thenThrow(new ExceptionInput("subjectMissing")); - $exp = new Response(422); + $exp = new EmptyResponse(422); $this->assertResponse($exp, $this->h->dispatch(new Request("PUT", "/feeds/1/rename", json_encode($in[0]), 'application/json'))); - $exp = new Response(204); + $exp = new EmptyResponse(204); $this->assertResponse($exp, $this->h->dispatch(new Request("PUT", "/feeds/1/rename", json_encode($in[1]), 'application/json'))); - $exp = new Response(422); + $exp = new EmptyResponse(422); $this->assertResponse($exp, $this->h->dispatch(new Request("PUT", "/feeds/1/rename", json_encode($in[2]), 'application/json'))); - $exp = new Response(422); + $exp = new EmptyResponse(422); $this->assertResponse($exp, $this->h->dispatch(new Request("PUT", "/feeds/1/rename", json_encode($in[3]), 'application/json'))); - $exp = new Response(404); + $exp = new EmptyResponse(404); $this->assertResponse($exp, $this->h->dispatch(new Request("PUT", "/feeds/42/rename", json_encode($in[4]), 'application/json'))); - $exp = new Response(422); + $exp = new EmptyResponse(422); $this->assertResponse($exp, $this->h->dispatch(new Request("PUT", "/feeds/1/rename", json_encode($in[6]), 'application/json'))); } @@ -655,11 +651,11 @@ class TestV1_2 extends \JKingWeb\Arsse\Test\AbstractTest { ], ]; Phake::when(Arsse::$db)->feedListStale->thenReturn(array_column($out, "id")); - $exp = new Response(200, ['feeds' => $out]); + $exp = new Response(['feeds' => $out]); $this->assertResponse($exp, $this->h->dispatch(new Request("GET", "/feeds/all"))); // retrieving the list when not an admin fails Phake::when(Arsse::$user)->rightsGet->thenReturn(0); - $exp = new Response(403); + $exp = new EmptyResponse(403); $this->assertResponse($exp, $this->h->dispatch(new Request("GET", "/feeds/all"))); } @@ -674,17 +670,17 @@ class TestV1_2 extends \JKingWeb\Arsse\Test\AbstractTest { Phake::when(Arsse::$db)->feedUpdate(42)->thenReturn(true); Phake::when(Arsse::$db)->feedUpdate(2112)->thenThrow(new ExceptionInput("subjectMissing")); Phake::when(Arsse::$db)->feedUpdate($this->lessThan(1))->thenThrow(new ExceptionInput("typeViolation")); - $exp = new Response(204); + $exp = new EmptyResponse(204); $this->assertResponse($exp, $this->h->dispatch(new Request("GET", "/feeds/update", json_encode($in[0]), 'application/json'))); - $exp = new Response(404); + $exp = new EmptyResponse(404); $this->assertResponse($exp, $this->h->dispatch(new Request("GET", "/feeds/update", json_encode($in[1]), 'application/json'))); - $exp = new Response(422); + $exp = new EmptyResponse(422); $this->assertResponse($exp, $this->h->dispatch(new Request("GET", "/feeds/update", json_encode($in[2]), 'application/json'))); $this->assertResponse($exp, $this->h->dispatch(new Request("GET", "/feeds/update", json_encode($in[3]), 'application/json'))); $this->assertResponse($exp, $this->h->dispatch(new Request("GET", "/feeds/update", json_encode($in[4]), 'application/json'))); // updating a feed when not an admin fails Phake::when(Arsse::$user)->rightsGet->thenReturn(0); - $exp = new Response(403); + $exp = new EmptyResponse(403); $this->assertResponse($exp, $this->h->dispatch(new Request("GET", "/feeds/update", json_encode($in[0]), 'application/json'))); } @@ -710,12 +706,12 @@ class TestV1_2 extends \JKingWeb\Arsse\Test\AbstractTest { Phake::when(Arsse::$db)->articleList(Arsse::$user->id, (new Context)->reverse(true)->folder(2112), Database::LIST_TYPICAL)->thenThrow(new ExceptionInput("idMissing")); Phake::when(Arsse::$db)->articleList(Arsse::$user->id, (new Context)->reverse(true)->subscription(-1), Database::LIST_TYPICAL)->thenThrow(new ExceptionInput("typeViolation")); Phake::when(Arsse::$db)->articleList(Arsse::$user->id, (new Context)->reverse(true)->folder(-1), Database::LIST_TYPICAL)->thenThrow(new ExceptionInput("typeViolation")); - $exp = new Response(200, ['items' => $this->articles['rest']]); + $exp = new Response(['items' => $this->articles['rest']]); // check the contents of the response $this->assertResponse($exp, $this->h->dispatch(new Request("GET", "/items"))); // first instance of base context $this->assertResponse($exp, $this->h->dispatch(new Request("GET", "/items/updated"))); // second instance of base context // check error conditions - $exp = new Response(422); + $exp = new EmptyResponse(422); $this->assertResponse($exp, $this->h->dispatch(new Request("GET", "/items", json_encode($in[0]), 'application/json'))); $this->assertResponse($exp, $this->h->dispatch(new Request("GET", "/items", json_encode($in[1]), 'application/json'))); $this->assertResponse($exp, $this->h->dispatch(new Request("GET", "/items", json_encode($in[2]), 'application/json'))); @@ -748,13 +744,13 @@ class TestV1_2 extends \JKingWeb\Arsse\Test\AbstractTest { $in = json_encode(['newestItemId' => 2112]); Phake::when(Arsse::$db)->articleMark(Arsse::$user->id, $read, (new Context)->folder(1)->latestEdition(2112))->thenReturn(42); Phake::when(Arsse::$db)->articleMark(Arsse::$user->id, $read, (new Context)->folder(42)->latestEdition(2112))->thenThrow(new ExceptionInput("idMissing")); // folder doesn't exist - $exp = new Response(204); + $exp = new EmptyResponse(204); $this->assertResponse($exp, $this->h->dispatch(new Request("PUT", "/folders/1/read", $in, 'application/json'))); $this->assertResponse($exp, $this->h->dispatch(new Request("PUT", "/folders/1/read?newestItemId=2112"))); - $exp = new Response(422); + $exp = new EmptyResponse(422); $this->assertResponse($exp, $this->h->dispatch(new Request("PUT", "/folders/1/read"))); $this->assertResponse($exp, $this->h->dispatch(new Request("PUT", "/folders/1/read?newestItemId=ook"))); - $exp = new Response(404); + $exp = new EmptyResponse(404); $this->assertResponse($exp, $this->h->dispatch(new Request("PUT", "/folders/42/read", $in, 'application/json'))); } @@ -763,13 +759,13 @@ class TestV1_2 extends \JKingWeb\Arsse\Test\AbstractTest { $in = json_encode(['newestItemId' => 2112]); Phake::when(Arsse::$db)->articleMark(Arsse::$user->id, $read, (new Context)->subscription(1)->latestEdition(2112))->thenReturn(42); Phake::when(Arsse::$db)->articleMark(Arsse::$user->id, $read, (new Context)->subscription(42)->latestEdition(2112))->thenThrow(new ExceptionInput("idMissing")); // subscription doesn't exist - $exp = new Response(204); + $exp = new EmptyResponse(204); $this->assertResponse($exp, $this->h->dispatch(new Request("PUT", "/feeds/1/read", $in, 'application/json'))); $this->assertResponse($exp, $this->h->dispatch(new Request("PUT", "/feeds/1/read?newestItemId=2112"))); - $exp = new Response(422); + $exp = new EmptyResponse(422); $this->assertResponse($exp, $this->h->dispatch(new Request("PUT", "/feeds/1/read"))); $this->assertResponse($exp, $this->h->dispatch(new Request("PUT", "/feeds/1/read?newestItemId=ook"))); - $exp = new Response(404); + $exp = new EmptyResponse(404); $this->assertResponse($exp, $this->h->dispatch(new Request("PUT", "/feeds/42/read", $in, 'application/json'))); } @@ -777,10 +773,10 @@ class TestV1_2 extends \JKingWeb\Arsse\Test\AbstractTest { $read = ['read' => true]; $in = json_encode(['newestItemId' => 2112]); Phake::when(Arsse::$db)->articleMark(Arsse::$user->id, $read, (new Context)->latestEdition(2112))->thenReturn(42); - $exp = new Response(204); + $exp = new EmptyResponse(204); $this->assertResponse($exp, $this->h->dispatch(new Request("PUT", "/items/read", $in, 'application/json'))); $this->assertResponse($exp, $this->h->dispatch(new Request("PUT", "/items/read?newestItemId=2112"))); - $exp = new Response(422); + $exp = new EmptyResponse(422); $this->assertResponse($exp, $this->h->dispatch(new Request("PUT", "/items/read"))); $this->assertResponse($exp, $this->h->dispatch(new Request("PUT", "/items/read?newestItemId=ook"))); } @@ -798,12 +794,12 @@ class TestV1_2 extends \JKingWeb\Arsse\Test\AbstractTest { Phake::when(Arsse::$db)->articleMark(Arsse::$user->id, $star, (new Context)->article(2112))->thenThrow(new ExceptionInput("subjectMissing")); // article doesn't exist doesn't exist Phake::when(Arsse::$db)->articleMark(Arsse::$user->id, $unstar, (new Context)->article(4))->thenReturn(42); Phake::when(Arsse::$db)->articleMark(Arsse::$user->id, $unstar, (new Context)->article(1337))->thenThrow(new ExceptionInput("subjectMissing")); // article doesn't exist doesn't exist - $exp = new Response(204); + $exp = new EmptyResponse(204); $this->assertResponse($exp, $this->h->dispatch(new Request("PUT", "/items/1/read"))); $this->assertResponse($exp, $this->h->dispatch(new Request("PUT", "/items/2/unread"))); $this->assertResponse($exp, $this->h->dispatch(new Request("PUT", "/items/1/3/star"))); $this->assertResponse($exp, $this->h->dispatch(new Request("PUT", "/items/4400/4/unstar"))); - $exp = new Response(404); + $exp = new EmptyResponse(404); $this->assertResponse($exp, $this->h->dispatch(new Request("PUT", "/items/42/read"))); $this->assertResponse($exp, $this->h->dispatch(new Request("PUT", "/items/47/unread"))); $this->assertResponse($exp, $this->h->dispatch(new Request("PUT", "/items/1/2112/star"))); @@ -829,7 +825,7 @@ class TestV1_2 extends \JKingWeb\Arsse\Test\AbstractTest { Phake::when(Arsse::$db)->articleMark(Arsse::$user->id, $this->anything(), $this->anything())->thenReturn(42); Phake::when(Arsse::$db)->articleMark(Arsse::$user->id, $this->anything(), (new Context)->editions([]))->thenThrow(new ExceptionInput("tooShort")); // data model function requires one valid integer for multiples Phake::when(Arsse::$db)->articleMark(Arsse::$user->id, $this->anything(), (new Context)->articles([]))->thenThrow(new ExceptionInput("tooShort")); // data model function requires one valid integer for multiples - $exp = new Response(204); + $exp = new EmptyResponse(204); $this->assertResponse($exp, $this->h->dispatch(new Request("PUT", "/items/read/multiple"))); $this->assertResponse($exp, $this->h->dispatch(new Request("PUT", "/items/unread/multiple"))); $this->assertResponse($exp, $this->h->dispatch(new Request("PUT", "/items/star/multiple"))); @@ -882,29 +878,29 @@ class TestV1_2 extends \JKingWeb\Arsse\Test\AbstractTest { ]; $arr2['warnings']['improperlyConfiguredCron'] = true; $arr2['warnings']['incorrectDbCharset'] = true; - $exp = new Response(200, $arr1); + $exp = new Response($arr1); $this->assertResponse($exp, $this->h->dispatch(new Request("GET", "/status"))); } public function testCleanUpBeforeUpdate() { Phake::when(Arsse::$db)->feedCleanup()->thenReturn(true); - $exp = new Response(204); + $exp = new EmptyResponse(204); $this->assertResponse($exp, $this->h->dispatch(new Request("GET", "/cleanup/before-update"))); Phake::verify(Arsse::$db)->feedCleanup(); // performing a cleanup when not an admin fails Phake::when(Arsse::$user)->rightsGet->thenReturn(0); - $exp = new Response(403); + $exp = new EmptyResponse(403); $this->assertResponse($exp, $this->h->dispatch(new Request("GET", "/cleanup/before-update"))); } public function testCleanUpAfterUpdate() { Phake::when(Arsse::$db)->articleCleanup()->thenReturn(true); - $exp = new Response(204); + $exp = new EmptyResponse(204); $this->assertResponse($exp, $this->h->dispatch(new Request("GET", "/cleanup/after-update"))); Phake::verify(Arsse::$db)->articleCleanup(); // performing a cleanup when not an admin fails Phake::when(Arsse::$user)->rightsGet->thenReturn(0); - $exp = new Response(403); + $exp = new EmptyResponse(403); $this->assertResponse($exp, $this->h->dispatch(new Request("GET", "/cleanup/after-update"))); } } diff --git a/tests/cases/REST/NextCloudNews/TestVersions.php b/tests/cases/REST/NextCloudNews/TestVersions.php index 3081d57..6904d3e 100644 --- a/tests/cases/REST/NextCloudNews/TestVersions.php +++ b/tests/cases/REST/NextCloudNews/TestVersions.php @@ -8,7 +8,8 @@ namespace JKingWeb\Arsse\TestCase\REST\NextCloudNews; use JKingWeb\Arsse\REST\NextCloudNews\Versions; use JKingWeb\Arsse\REST\Request; -use JKingWeb\Arsse\REST\Response; +use Zend\Diactoros\Response\JsonResponse as Response; +use Zend\Diactoros\Response\EmptyResponse; /** @covers \JKingWeb\Arsse\REST\NextCloudNews\Versions */ class TestVersions extends \JKingWeb\Arsse\Test\AbstractTest { @@ -17,43 +18,43 @@ class TestVersions extends \JKingWeb\Arsse\Test\AbstractTest { } public function testFetchVersionList() { - $exp = new Response(200, ['apiLevels' => ['v1-2']]); + $exp = new Response(['apiLevels' => ['v1-2']]); $h = new Versions; $req = new Request("GET", "/"); $res = $h->dispatch($req); - $this->assertEquals($exp, $res); + $this->assertResponse($exp, $res); $req = new Request("GET", ""); $res = $h->dispatch($req); - $this->assertEquals($exp, $res); + $this->assertResponse($exp, $res); $req = new Request("GET", "/?id=1827"); $res = $h->dispatch($req); - $this->assertEquals($exp, $res); + $this->assertResponse($exp, $res); } public function testRespondToOptionsRequest() { - $exp = new Response(204, "", "", ["Allow: HEAD,GET"]); + $exp = new EmptyResponse(204, ['Allow' => "HEAD,GET"]); $h = new Versions; $req = new Request("OPTIONS", "/"); $res = $h->dispatch($req); - $this->assertEquals($exp, $res); + $this->assertResponse($exp, $res); } public function testUseIncorrectMethod() { - $exp = new Response(405, "", "", ["Allow: HEAD,GET"]); + $exp = new EmptyResponse(405, ['Allow' => "HEAD,GET"]); $h = new Versions; $req = new Request("POST", "/"); $res = $h->dispatch($req); - $this->assertEquals($exp, $res); + $this->assertResponse($exp, $res); } public function testUseIncorrectPath() { - $exp = new Response(404); + $exp = new EmptyResponse(404); $h = new Versions; $req = new Request("GET", "/ook"); $res = $h->dispatch($req); - $this->assertEquals($exp, $res); + $this->assertResponse($exp, $res); $req = new Request("OPTIONS", "/ook"); $res = $h->dispatch($req); - $this->assertEquals($exp, $res); + $this->assertResponse($exp, $res); } } diff --git a/tests/cases/REST/TinyTinyRSS/TestAPI.php b/tests/cases/REST/TinyTinyRSS/TestAPI.php index 6844e9a..d2f1ff3 100644 --- a/tests/cases/REST/TinyTinyRSS/TestAPI.php +++ b/tests/cases/REST/TinyTinyRSS/TestAPI.php @@ -12,13 +12,15 @@ use JKingWeb\Arsse\User; use JKingWeb\Arsse\Database; use JKingWeb\Arsse\Service; use JKingWeb\Arsse\REST\Request; -use JKingWeb\Arsse\REST\Response; use JKingWeb\Arsse\Test\Result; use JKingWeb\Arsse\Misc\Date; use JKingWeb\Arsse\Misc\Context; use JKingWeb\Arsse\Db\ExceptionInput; use JKingWeb\Arsse\Db\Transaction; use JKingWeb\Arsse\REST\TinyTinyRSS\API; +use Psr\Http\Message\ResponseInterface; +use Zend\Diactoros\Response\JsonResponse as Response; +use Zend\Diactoros\Response\EmptyResponse; use Phake; /** @covers \JKingWeb\Arsse\REST\TinyTinyRSS\API @@ -122,12 +124,12 @@ class TestAPI extends \JKingWeb\Arsse\Test\AbstractTest { LONG_STRING; - protected function req($data) : Response { + protected function req($data): ResponseInterface { return $this->h->dispatch(new Request("POST", "", json_encode($data))); } protected function respGood($content = null, $seq = 0): Response { - return new Response(200, [ + return new Response([ 'seq' => $seq, 'status' => 0, 'content' => $content, @@ -136,18 +138,13 @@ LONG_STRING; protected function respErr(string $msg, $content = [], $seq = 0): Response { $err = ['error' => $msg]; - return new Response(200, [ + return new Response([ 'seq' => $seq, 'status' => 1, 'content' => array_merge($err, $content, $err), ]); } - protected function assertResponse(Response $exp, Response $act, string $text = null) { - $this->assertEquals($exp, $act, $text); - $this->assertSame($exp->payload, $act->payload, $text); - } - public function setUp() { $this->clearData(); Arsse::$conf = new Conf(); @@ -178,14 +175,14 @@ LONG_STRING; $this->assertResponse($exp, $this->h->dispatch(new Request("POST", "", ""))); $this->assertResponse($exp, $this->h->dispatch(new Request("POST", "/", ""))); $this->assertResponse($exp, $this->h->dispatch(new Request("POST", "/index.php", ""))); - $exp = new Response(404); + $exp = new EmptyResponse(404); $this->assertResponse($exp, $this->h->dispatch(new Request("POST", "/bad/path", ""))); } public function testHandleOptionsRequest() { - $exp = new Response(204, "", "", [ - "Allow: POST", - "Accept: application/json, text/json", + $exp = new EmptyResponse(204, [ + 'Allow' => "POST", + 'Accept' => "application/json, text/json", ]); $this->assertResponse($exp, $this->h->dispatch(new Request("OPTIONS", ""))); } @@ -226,7 +223,7 @@ LONG_STRING; 'user' => Arsse::$user->id, 'password' => "secret", ]; - $exp = new Response(500); + $exp = new EmptyResponse(500); $this->assertResponse($exp, $this->req($data)); } @@ -1630,10 +1627,10 @@ LONG_STRING; $this->assertResponse($this->outputHeadlines(1), $test); // test 'show_content' $test = $this->req($in[1]); - $this->assertArrayHasKey("content", $test->payload['content'][0]); - $this->assertArrayHasKey("content", $test->payload['content'][1]); + $this->assertArrayHasKey("content", $test->getPayload()['content'][0]); + $this->assertArrayHasKey("content", $test->getPayload()['content'][1]); foreach ($this->generateHeadlines(1) as $key => $row) { - $this->assertSame($row['content'], $test->payload['content'][$key]['content']); + $this->assertSame($row['content'], $test->getPayload()['content'][$key]['content']); } // test 'include_attachments' $test = $this->req($in[2]); @@ -1649,25 +1646,23 @@ LONG_STRING; 'post_id' => "2112", ], ]; - $this->assertArrayHasKey("attachments", $test->payload['content'][0]); - $this->assertArrayHasKey("attachments", $test->payload['content'][1]); - $this->assertSame([], $test->payload['content'][0]['attachments']); - $this->assertSame($exp, $test->payload['content'][1]['attachments']); + $this->assertArrayHasKey("attachments", $test->getPayload()['content'][0]); + $this->assertArrayHasKey("attachments", $test->getPayload()['content'][1]); + $this->assertSame([], $test->getPayload()['content'][0]['attachments']); + $this->assertSame($exp, $test->getPayload()['content'][1]['attachments']); // test 'include_header' $test = $this->req($in[3]); - $exp = $this->outputHeadlines(1); - $exp->payload['content'] = [ + $exp = $this->respGood([ ['id' => -4, 'is_cat' => false, 'first_id' => 1], - $exp->payload['content'], - ]; + $this->outputHeadlines(1)->getPayload()['content'], + ]); $this->assertResponse($exp, $test); // test 'include_header' with a category $test = $this->req($in[4]); - $exp = $this->outputHeadlines(1); - $exp->payload['content'] = [ + $exp = $this->respGood([ ['id' => -3, 'is_cat' => true, 'first_id' => 1], - $exp->payload['content'], - ]; + $this->outputHeadlines(1)->getPayload()['content'], + ]); $this->assertResponse($exp, $test); // test 'include_header' with an empty result $test = $this->req($in[5]); @@ -1686,37 +1681,34 @@ LONG_STRING; $this->assertResponse($exp, $test); // test 'include_header' with ascending order $test = $this->req($in[7]); - $exp = $this->outputHeadlines(1); - $exp->payload['content'] = [ + $exp = $this->respGood([ ['id' => -4, 'is_cat' => false, 'first_id' => 0], - $exp->payload['content'], - ]; + $this->outputHeadlines(1)->getPayload()['content'], + ]); $this->assertResponse($exp, $test); // test 'include_header' with skip Phake::when(Arsse::$db)->articleList($this->anything(), (new Context)->reverse(true)->limit(1)->subscription(42), Database::LIST_MINIMAL)->thenReturn($this->generateHeadlines(1867)); $test = $this->req($in[8]); - $exp = $this->outputHeadlines(1); - $exp->payload['content'] = [ + $exp = $this->respGood([ ['id' => 42, 'is_cat' => false, 'first_id' => 1867], - $exp->payload['content'], - ]; + $this->outputHeadlines(1)->getPayload()['content'], + ]); $this->assertResponse($exp, $test); // test 'include_header' with skip and ascending order $test = $this->req($in[9]); - $exp = $this->outputHeadlines(1); - $exp->payload['content'] = [ + $exp = $this->respGood([ ['id' => 42, 'is_cat' => false, 'first_id' => 0], - $exp->payload['content'], - ]; + $this->outputHeadlines(1)->getPayload()['content'], + ]); $this->assertResponse($exp, $test); // test 'show_excerpt' $exp1 = "“This & that, you know‽”"; $exp2 = "Pour vous faire mieux connaitre d’ou\u{300} vient l’erreur de ceux qui bla\u{302}ment la volupte\u{301}, et qui louent en…"; $test = $this->req($in[10]); - $this->assertArrayHasKey("excerpt", $test->payload['content'][0]); - $this->assertArrayHasKey("excerpt", $test->payload['content'][1]); - $this->assertSame($exp1, $test->payload['content'][0]['excerpt']); - $this->assertSame($exp2, $test->payload['content'][1]['excerpt']); + $this->assertArrayHasKey("excerpt", $test->getPayload()['content'][0]); + $this->assertArrayHasKey("excerpt", $test->getPayload()['content'][1]); + $this->assertSame($exp1, $test->getPayload()['content'][0]['excerpt']); + $this->assertSame($exp2, $test->getPayload()['content'][1]['excerpt']); } protected function generateHeadlines(int $id): Result { diff --git a/tests/cases/REST/TinyTinyRSS/TestIcon.php b/tests/cases/REST/TinyTinyRSS/TestIcon.php index 64f3358..c5c67bc 100644 --- a/tests/cases/REST/TinyTinyRSS/TestIcon.php +++ b/tests/cases/REST/TinyTinyRSS/TestIcon.php @@ -12,7 +12,7 @@ use JKingWeb\Arsse\User; use JKingWeb\Arsse\Database; use JKingWeb\Arsse\REST\TinyTinyRSS\Icon; use JKingWeb\Arsse\REST\Request; -use JKingWeb\Arsse\REST\Response; +use Zend\Diactoros\Response\EmptyResponse as Response; use Phake; /** @covers \JKingWeb\Arsse\REST\TinyTinyRSS\Icon */ @@ -38,20 +38,20 @@ class TestIcon extends \JKingWeb\Arsse\Test\AbstractTest { Phake::when(Arsse::$db)->subscriptionFavicon(2112)->thenReturn("http://example.net/logo.png"); Phake::when(Arsse::$db)->subscriptionFavicon(1337)->thenReturn("http://example.org/icon.gif\r\nLocation: http://bad.example.com/"); // these requests should succeed - $exp = new Response(301, "", "", ["Location: http://example.com/favicon.ico"]); - $this->assertEquals($exp, $this->h->dispatch(new Request("GET", "42.ico"))); - $exp = new Response(301, "", "", ["Location: http://example.net/logo.png"]); - $this->assertEquals($exp, $this->h->dispatch(new Request("GET", "2112.ico"))); - $exp = new Response(301, "", "", ["Location: http://example.org/icon.gif"]); - $this->assertEquals($exp, $this->h->dispatch(new Request("GET", "1337.ico"))); + $exp = new Response(301, ['Location' => "http://example.com/favicon.ico"]); + $this->assertResponse($exp, $this->h->dispatch(new Request("GET", "42.ico"))); + $exp = new Response(301, ['Location' => "http://example.net/logo.png"]); + $this->assertResponse($exp, $this->h->dispatch(new Request("GET", "2112.ico"))); + $exp = new Response(301, ['Location' => "http://example.org/icon.gif"]); + $this->assertResponse($exp, $this->h->dispatch(new Request("GET", "1337.ico"))); // these requests should fail $exp = new Response(404); - $this->assertEquals($exp, $this->h->dispatch(new Request("GET", "ook.ico"))); - $this->assertEquals($exp, $this->h->dispatch(new Request("GET", "ook"))); - $this->assertEquals($exp, $this->h->dispatch(new Request("GET", "47.ico"))); - $this->assertEquals($exp, $this->h->dispatch(new Request("GET", "2112.png"))); + $this->assertResponse($exp, $this->h->dispatch(new Request("GET", "ook.ico"))); + $this->assertResponse($exp, $this->h->dispatch(new Request("GET", "ook"))); + $this->assertResponse($exp, $this->h->dispatch(new Request("GET", "47.ico"))); + $this->assertResponse($exp, $this->h->dispatch(new Request("GET", "2112.png"))); // only GET is allowed - $exp = new Response(405, "", "", ["Allow: GET"]); - $this->assertEquals($exp, $this->h->dispatch(new Request("PUT", "2112.ico"))); + $exp = new Response(405, ['Allow' => "GET"]); + $this->assertResponse($exp, $this->h->dispatch(new Request("PUT", "2112.ico"))); } } diff --git a/tests/lib/AbstractTest.php b/tests/lib/AbstractTest.php index 85bb0eb..762e991 100644 --- a/tests/lib/AbstractTest.php +++ b/tests/lib/AbstractTest.php @@ -9,6 +9,9 @@ namespace JKingWeb\Arsse\Test; use JKingWeb\Arsse\Exception; use JKingWeb\Arsse\Arsse; use JKingWeb\Arsse\Misc\Date; +use Psr\Http\Message\ResponseInterface; +use Zend\Diactoros\Response\JsonResponse; +use Zend\Diactoros\Response\EmptyResponse; /** @coversNothing */ abstract class AbstractTest extends \PHPUnit\Framework\TestCase { @@ -29,6 +32,16 @@ abstract class AbstractTest extends \PHPUnit\Framework\TestCase { } } + protected function assertResponse(ResponseInterface $exp, ResponseInterface $act, string $text = null) { + $this->assertEquals($exp->getHeaders(), $act->getHeaders(), $text); + $this->assertEquals($exp->getStatusCode(), $act->getStatusCode(), $text); + $this->assertInstanceOf(get_class($exp), $act); + if ($exp instanceof JsonResponse) { + $this->assertEquals($exp->getPayload(), $act->getPayload(), $text); + $this->assertSame($exp->getPayload(), $act->getPayload(), $text); + } + } + public function approximateTime($exp, $act) { if (is_null($act)) { return null; diff --git a/vendor-bin/phpunit/composer.lock b/vendor-bin/phpunit/composer.lock index bb02864..26b133e 100644 --- a/vendor-bin/phpunit/composer.lock +++ b/vendor-bin/phpunit/composer.lock @@ -777,16 +777,16 @@ }, { "name": "phpunit/phpunit", - "version": "6.5.4", + "version": "6.5.5", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/phpunit.git", - "reference": "1b2f933d5775f9237369deaa2d2bfbf9d652be4c" + "reference": "83d27937a310f2984fd575686138597147bdc7df" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/1b2f933d5775f9237369deaa2d2bfbf9d652be4c", - "reference": "1b2f933d5775f9237369deaa2d2bfbf9d652be4c", + "url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/83d27937a310f2984fd575686138597147bdc7df", + "reference": "83d27937a310f2984fd575686138597147bdc7df", "shasum": "" }, "require": { @@ -857,7 +857,7 @@ "testing", "xunit" ], - "time": "2017-12-10T08:06:19+00:00" + "time": "2017-12-17T06:31:19+00:00" }, { "name": "phpunit/phpunit-mock-objects", @@ -965,16 +965,16 @@ }, { "name": "sebastian/comparator", - "version": "2.1.0", + "version": "2.1.1", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/comparator.git", - "reference": "1174d9018191e93cb9d719edec01257fc05f8158" + "reference": "b11c729f95109b56a0fe9650c6a63a0fcd8c439f" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/comparator/zipball/1174d9018191e93cb9d719edec01257fc05f8158", - "reference": "1174d9018191e93cb9d719edec01257fc05f8158", + "url": "https://api.github.com/repos/sebastianbergmann/comparator/zipball/b11c729f95109b56a0fe9650c6a63a0fcd8c439f", + "reference": "b11c729f95109b56a0fe9650c6a63a0fcd8c439f", "shasum": "" }, "require": { @@ -1025,7 +1025,7 @@ "compare", "equality" ], - "time": "2017-11-03T07:16:52+00:00" + "time": "2017-12-22T14:50:35+00:00" }, { "name": "sebastian/diff", diff --git a/vendor-bin/robo/composer.lock b/vendor-bin/robo/composer.lock index ee2d3df..3f95a03 100644 --- a/vendor-bin/robo/composer.lock +++ b/vendor-bin/robo/composer.lock @@ -59,28 +59,33 @@ }, { "name": "consolidation/config", - "version": "1.0.7", + "version": "1.0.9", "source": { "type": "git", "url": "https://github.com/consolidation/config.git", - "reference": "b59a3b9ea750c21397f26a68fd2e04d9580af42e" + "reference": "34ca8d7c1ee60a7b591b10617114cf1210a2e92c" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/consolidation/config/zipball/b59a3b9ea750c21397f26a68fd2e04d9580af42e", - "reference": "b59a3b9ea750c21397f26a68fd2e04d9580af42e", + "url": "https://api.github.com/repos/consolidation/config/zipball/34ca8d7c1ee60a7b591b10617114cf1210a2e92c", + "reference": "34ca8d7c1ee60a7b591b10617114cf1210a2e92c", "shasum": "" }, "require": { "dflydev/dot-access-data": "^1.1.0", - "grasmash/yaml-expander": "^1.1", + "grasmash/expander": "^1", "php": ">=5.4.0" }, "require-dev": { + "greg-1-anderson/composer-test-scenarios": "^1", "phpunit/phpunit": "^4", "satooshi/php-coveralls": "^1.0", "squizlabs/php_codesniffer": "2.*", - "symfony/console": "^2.5|^3" + "symfony/console": "^2.5|^3|^4", + "symfony/yaml": "^2.8.11|^3|^4" + }, + "suggest": { + "symfony/yaml": "Required to use Consolidation\\Config\\Loader\\YamlConfigLoader" }, "type": "library", "extra": { @@ -104,7 +109,7 @@ } ], "description": "Provide configuration services for a commandline tool.", - "time": "2017-10-25T05:50:10+00:00" + "time": "2017-12-22T17:28:19+00:00" }, { "name": "consolidation/log", @@ -205,16 +210,16 @@ }, { "name": "consolidation/robo", - "version": "1.2.0", + "version": "1.2.1", "source": { "type": "git", "url": "https://github.com/consolidation/Robo.git", - "reference": "c46c13de3eca55e6b3635f363688ce85e845adf0" + "reference": "b6296f1cf1088f1a11b0b819f9e42ef6f00b79a9" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/consolidation/Robo/zipball/c46c13de3eca55e6b3635f363688ce85e845adf0", - "reference": "c46c13de3eca55e6b3635f363688ce85e845adf0", + "url": "https://api.github.com/repos/consolidation/Robo/zipball/b6296f1cf1088f1a11b0b819f9e42ef6f00b79a9", + "reference": "b6296f1cf1088f1a11b0b819f9e42ef6f00b79a9", "shasum": "" }, "require": { @@ -278,7 +283,7 @@ } ], "description": "Modern task runner", - "time": "2017-12-13T02:10:49+00:00" + "time": "2017-12-29T06:48:35+00:00" }, { "name": "container-interop/container-interop", @@ -370,6 +375,53 @@ ], "time": "2017-01-20T21:14:22+00:00" }, + { + "name": "grasmash/expander", + "version": "1.0.0", + "source": { + "type": "git", + "url": "https://github.com/grasmash/expander.git", + "reference": "95d6037344a4be1dd5f8e0b0b2571a28c397578f" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/grasmash/expander/zipball/95d6037344a4be1dd5f8e0b0b2571a28c397578f", + "reference": "95d6037344a4be1dd5f8e0b0b2571a28c397578f", + "shasum": "" + }, + "require": { + "dflydev/dot-access-data": "^1.1.0", + "php": ">=5.4" + }, + "require-dev": { + "greg-1-anderson/composer-test-scenarios": "^1", + "phpunit/phpunit": "^4|^5.5.4", + "satooshi/php-coveralls": "^1.0.2|dev-master", + "squizlabs/php_codesniffer": "^2.7" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.x-dev" + } + }, + "autoload": { + "psr-4": { + "Grasmash\\Expander\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Matthew Grasmick" + } + ], + "description": "Expands internal property references in PHP arrays file.", + "time": "2017-12-21T22:14:55+00:00" + }, { "name": "grasmash/yaml-expander", "version": "1.4.0",