From bb23883e5bf090c44d6e163b9907fece5d692871 Mon Sep 17 00:00:00 2001 From: maxpozdeev Date: Mon, 5 Sep 2022 17:13:46 +0300 Subject: [PATCH] implement ApiResponse class and change http api internals --- src/api.php | 83 ++++++++++++++++++---------- src/includes/api/AuthController.php | 14 ++--- src/includes/api/ListsController.php | 63 ++++++++++----------- src/includes/api/TagsController.php | 21 ++++--- src/includes/api/TasksController.php | 78 +++++++++++++------------- 5 files changed, 145 insertions(+), 114 deletions(-) diff --git a/src/api.php b/src/api.php index 3dc07c6..f0430d7 100644 --- a/src/api.php +++ b/src/api.php @@ -21,8 +21,6 @@ require_once(MTTINC. 'api/TasksController.php'); require_once(MTTINC. 'api/TagsController.php'); require_once(MTTINC. 'api/AuthController.php'); -$req = new ApiRequest(); - $endpoints = array( '/lists' => [ 'GET' => [ ListsController::class , 'get' ], @@ -57,20 +55,21 @@ $endpoints = array( ], ); +$req = new ApiRequest(); +$response = new ApiResponse(); $executed = false; $data = null; + foreach ($endpoints as $search => $methods) { $m = array(); if (preg_match("#^$search$#", $req->path, $m)) { $classDescr = $methods[$req->method] ?? null; // check if http method is supported for path if ( is_null($classDescr) ) { - http_response_code(500); - die ("Unknown method for resource"); + $response->htmlContent("Unknown method for resource", 500)->exit(); } if ( !is_array($classDescr) || count($classDescr) != 2) { - http_response_code(500); - die ("Incorrect method definition"); + $response->htmlContent("Incorrect method definition", 500)->exit(); } // check if class method exists $class = $classDescr[0]; @@ -82,36 +81,27 @@ foreach ($endpoints as $search => $methods) { if (method_exists($class, $classMethod)) { // test for static with ReflectionMethod? if ($req->method != 'GET' && $req->contentType == 'application/json') { if ($req->decodeJsonBody() === false) { - http_response_code(500); - die ("Failed to parse JSON body"); + $response->htmlContent("Failed to parse JSON body", 500)->exit(); } } - $instance = new $class($req); - $data = $instance->$classMethod($param); + $instance = new $class($req, $response); + $instance->$classMethod($param); $executed = true; break; } else { - http_response_code(405); if (MTT_DEBUG) { - die ("Class method $class:$classMethod() not found"); + $response->htmlContent("Class method $class:$classMethod() not found", 405)->exit(); } - die ("Class method not found"); + $response->htmlContent("Class method not found", 405)->exit(); } } } -if ($executed) { - if (is_null($data)) { - http_response_code(404); - } - jsonExit($data); +if (!$executed) { + $response->htmlContent("Unknown command", 404); } -else { - http_response_code(404); - die ("Unknown command"); -} - +$response->exit(); @@ -195,6 +185,7 @@ function haveWriteAccess(?int $listId = null) : bool return true; } + class ApiRequest { public $path; @@ -214,14 +205,46 @@ class ApiRequest } } -abstract class ApiController +class ApiResponse { - /** - * @var ApiRequest - */ - protected $req; + public $data = null; + public $contentType = 'application/json'; + public $code = null; - function __construct(ApiRequest $req) { - $this->req = $req; + function htmlContent(string $content, int $code = 200): ApiResponse + { + $this->contentType = 'text/html'; + $this->data = $content; + $this->code = $code; + return $this; + } + + function exit() + { + if (is_null($this->data) && is_null($this->code)) { + http_response_code(404); + } + if (!is_null($this->code)) { + http_response_code($this->code); + } + if ($this->contentType == 'text/html') { + print $this->data; + exit(); + } + jsonExit($this->data); + } +} + +abstract class ApiController +{ + /** @var ApiRequest */ + protected $req; + + /** @var ApiResponse */ + protected $response; + + function __construct(ApiRequest $req, ApiResponse $response) { + $this->req = $req; + $this->response = $response; } } diff --git a/src/includes/api/AuthController.php b/src/includes/api/AuthController.php index 57d59bc..74a3bee 100644 --- a/src/includes/api/AuthController.php +++ b/src/includes/api/AuthController.php @@ -11,14 +11,14 @@ class AuthController extends ApiController { function postAction($action) { switch ($action) { - case 'login': return $this->login(); break; - case 'logout': return $this->logout(); break; - case 'session': return $this->createSession(); break; - default: return ['total' => 0]; // error 400 ? + case 'login': $this->response->data = $this->login(); break; + case 'logout': $this->response->data = $this->logout(); break; + case 'session': $this->response->data = $this->createSession(); break; + default: $this->response->data = ['total' => 0]; // error 400 ? } } - private function login() + private function login(): ?array { check_token(); $t = array('logged' => 0); @@ -35,7 +35,7 @@ class AuthController extends ApiController { return $t; } - private function logout() + private function logout(): ?array { check_token(); updateSessionLogged(false); @@ -45,7 +45,7 @@ class AuthController extends ApiController { return $t; } - private function createSession() + private function createSession(): ?array { $t = array(); if (!need_auth()) { diff --git a/src/includes/api/ListsController.php b/src/includes/api/ListsController.php index b76cd99..54cc2a5 100644 --- a/src/includes/api/ListsController.php +++ b/src/includes/api/ListsController.php @@ -10,7 +10,7 @@ class ListsController extends ApiController { /** * Get all lists - * @return array + * @return void * @throws Exception */ function get() @@ -34,14 +34,14 @@ class ListsController extends ApiController { $t['total']++; $t['list'][] = $this->prepareList($r, $haveWriteAccess); } - return $t; + $this->response->data = $t; } /** * Create new list * Code 201 on success - * @return array + * @return void * @throws Exception */ function post() @@ -65,12 +65,12 @@ class ListsController extends ApiController { $oo = $this->prepareList($r, true); MTTNotificationCenter::postNotification(MTTNotification::didCreateList, $oo); $t['list'][] = $oo; - return $t; + $this->response->data = $t; } /** * Actions with all lists - * @return array + * @return void * @throws Exception */ function put() @@ -78,8 +78,8 @@ class ListsController extends ApiController { checkWriteAccess(); $action = $this->req->jsonBody['action'] ?? ''; switch ($action) { - case 'order': return $this->changeListOrder(); break; - default: return ['total' => 0]; // error 400 ? + case 'order': $this->response->data = $this->changeListOrder(); break; + default: $this->response->data = ['total' => 0]; // error 400 ? } } @@ -89,7 +89,7 @@ class ListsController extends ApiController { /** * Get single list by Id * @param mixed $id - * @return null|array + * @return void * @throws Exception */ function getId($id) @@ -98,16 +98,17 @@ class ListsController extends ApiController { $db = DBConnection::instance(); $r = $db->sqa( "SELECT * FROM {$db->prefix}lists WHERE id=?", array($id) ); if (!$r) { - return null; + $this->response->data = null; + return; } $t = $this->prepareList($r, haveWriteAccess()); - return $t; + $this->response->data = $t; } /** * Delete list by Id * @param mixed $id - * @return array + * @return void * @throws Exception */ function deleteId($id) @@ -125,7 +126,7 @@ class ListsController extends ApiController { $db->ex("DELETE FROM {$db->prefix}todolist WHERE list_id=$id"); } $db->ex("COMMIT"); - return $t; + $this->response->data = $t; } @@ -133,7 +134,7 @@ class ListsController extends ApiController { * Edit some properties of List * Actions: rename * @param mixed $id - * @return array + * @return void * @throws Exception */ function putId($id) @@ -143,21 +144,21 @@ class ListsController extends ApiController { $action = $this->req->jsonBody['action'] ?? ''; switch ($action) { - case 'rename': return $this->renameList($id); break; - case 'sort': return $this->sortList($id); break; - case 'publish': return $this->publishList($id); break; - case 'enableFeedKey': return $this->enableFeedKey($id); break; - case 'showNotes': return $this->showNotes($id); break; - case 'hide': return $this->hideList($id); break; - case 'clearCompleted': return $this->clearCompleted($id); break; - default: return ['total' => 0]; + case 'rename': $this->response->data = $this->renameList($id); break; + case 'sort': $this->response->data = $this->sortList($id); break; + case 'publish': $this->response->data = $this->publishList($id); break; + case 'enableFeedKey': $this->response->data = $this->enableFeedKey($id); break; + case 'showNotes': $this->response->data = $this->showNotes($id); break; + case 'hide': $this->response->data = $this->hideList($id); break; + case 'clearCompleted': $this->response->data = $this->clearCompleted($id); break; + default: $this->response->data = ['total' => 0]; } } /* Private Functions */ - private function prepareAllTasksList() + private function prepareAllTasksList(): array { //default values $hidden = 1; @@ -181,7 +182,7 @@ class ListsController extends ApiController { ); } - private function prepareList($row, bool $haveWriteAccess) + private function prepareList($row, bool $haveWriteAccess): array { $taskview = (int)$row['taskview']; $feedKey = ''; @@ -206,7 +207,7 @@ class ListsController extends ApiController { ); } - private function renameList(int $id) + private function renameList(int $id): ?array { $db = DBConnection::instance(); $t = array(); @@ -223,7 +224,7 @@ class ListsController extends ApiController { return $t; } - private function sortList(int $listId) + private function sortList(int $listId): ?array { $sort = (int)($this->req->jsonBody['sort'] ?? 0); self::setListSortingById($listId, $sort); @@ -259,7 +260,7 @@ class ListsController extends ApiController { } } - private function publishList(int $listId) + private function publishList(int $listId): ?array { $db = DBConnection::instance(); $publish = (int)($this->req->jsonBody['publish'] ?? 0); @@ -267,7 +268,7 @@ class ListsController extends ApiController { return ['total'=>1]; } - private function enableFeedKey(int $listId) + private function enableFeedKey(int $listId): ?array { $db = DBConnection::instance(); $flag = (int)($this->req->jsonBody['enable'] ?? 0); @@ -294,7 +295,7 @@ class ListsController extends ApiController { ]; } - private function showNotes(int $listId) + private function showNotes(int $listId): ?array { $db = DBConnection::instance(); $flag = (int)($this->req->jsonBody['shownotes'] ?? 0); @@ -303,7 +304,7 @@ class ListsController extends ApiController { return ['total'=>1]; } - private function hideList(int $listId) + private function hideList(int $listId): ?array { $db = DBConnection::instance(); $flag = (int)($this->req->jsonBody['hide'] ?? 0); @@ -319,7 +320,7 @@ class ListsController extends ApiController { return ['total'=>1]; } - private function clearCompleted(int $listId) + private function clearCompleted(int $listId): ?array { $db = DBConnection::instance(); $t = array(); @@ -332,7 +333,7 @@ class ListsController extends ApiController { return $t; } - private function changeListOrder() + private function changeListOrder(): ?array { $t = array(); $t['total'] = 0; diff --git a/src/includes/api/TagsController.php b/src/includes/api/TagsController.php index 493d921..cfe1790 100644 --- a/src/includes/api/TagsController.php +++ b/src/includes/api/TagsController.php @@ -10,7 +10,7 @@ class TagsController extends ApiController { /** * Get tag cloud - * @return array + * @return void * @throws Exception */ function getCloud($listId) @@ -35,7 +35,8 @@ class TagsController extends ApiController { $t['total'] = 0; $count = sizeof($at); if (!$count) { - return $t; + $this->response->data = $t; + return; } $qmax = max($ac); @@ -52,9 +53,13 @@ class TagsController extends ApiController { ); } $t['total'] = $count; - return $t; + $this->response->data = $t; } + /** + * @return void + * @throws Exception + */ function getSuggestions($listId) { $listId = (int)_get('list'); @@ -68,15 +73,15 @@ class TagsController extends ApiController { while ($r = $q->fetchRow()) { $t[] = $r[0]; } - return $t; + $this->response->data = $t; } - private function tagWeight(int $qmin, int $q, float $step) + private function tagWeight(int $qmin, int $q, float $step): float { - if ($step == 0) return 1; + if ($step == 0) return 1.0; $v = ceil(($q - $qmin)/$step); - if ($v == 0) return 0; - else return $v-1; + if ($v == 0) return 0.0; + else return $v - 1.0; } diff --git a/src/includes/api/TasksController.php b/src/includes/api/TasksController.php index 0702529..8ba4024 100644 --- a/src/includes/api/TasksController.php +++ b/src/includes/api/TasksController.php @@ -13,7 +13,7 @@ class TasksController extends ApiController { /** * Get tasks. * Filters are set with query parameters. - * @return array + * @return void * @throws Exception */ function get() @@ -104,13 +104,13 @@ class TasksController extends ApiController { if (_get('saveSort') == 1 && haveWriteAccess($listId)) { ListsController::setListSortingById($listId, $sort); } - return $t; + $this->response->data = $t; } /** * Create new task * action: simple or full - * @return mixed + * @return void * @throws Exception */ function post() @@ -119,14 +119,14 @@ class TasksController extends ApiController { checkWriteAccess($listId); $action = $this->req->jsonBody['action'] ?? ''; if ($action == 'full') { - return $this->fullNewTaskInList($listId); + $this->response->data = $this->fullNewTaskInList($listId); } - return $this->newTaskInList($listId); + $this->response->data = $this->newTaskInList($listId); } /** * Actions with multiple tasks - * @return array + * @return void * @throws Exception */ function put() @@ -134,8 +134,8 @@ class TasksController extends ApiController { checkWriteAccess(); $action = $this->req->jsonBody['action'] ?? ''; switch ($action) { - case 'order': return $this->changeTaskOrder(); break; - default: return ['total' => 0]; // error 400 ? + case 'order': $this->response->data = $this->changeTaskOrder(); break; + default: $this->response->data = ['total' => 0]; // error 400 ? } } @@ -143,7 +143,7 @@ class TasksController extends ApiController { /** * Delete task by Id * @param mixed $id - * @return array + * @return void * @throws Exception */ function deleteId($id) @@ -160,13 +160,13 @@ class TasksController extends ApiController { $t = array(); $t['total'] = $deleted; $t['list'][] = array('id' => $id); - return $t; + $this->response->data = $t; } /** * Edit some properties of Task * @param mixed $id - * @return array + * @return void * @throws Exception */ function putId($id) @@ -176,19 +176,19 @@ class TasksController extends ApiController { $action = $this->req->jsonBody['action'] ?? ''; switch ($action) { - case 'edit': return $this->editTask($id); break; - case 'complete': return $this->completeTask($id); break; - case 'note': return $this->editNote($id); break; - case 'move': return $this->moveTask($id); break; - case 'priority': return $this->priorityTask($id); break; - default: return ['total' => 0]; + case 'edit': $this->response->data = $this->editTask($id); break; + case 'complete': $this->response->data = $this->completeTask($id); break; + case 'note': $this->response->data = $this->editNote($id); break; + case 'move': $this->response->data = $this->moveTask($id); break; + case 'priority': $this->response->data = $this->priorityTask($id); break; + default: $this->response->data = ['total' => 0]; } } /** * Parse task input string to components for representing in edit/add form - * @return array + * @return void * @throws Exception */ function postTitleParse() @@ -205,12 +205,12 @@ class TasksController extends ApiController { $t['prio'] = $a['prio']; $t['tags'] = $a['tags']; } - return $t; + $this->response->data = $t; } /* Private Functions */ - private function newTaskInList(int $listId) + private function newTaskInList(int $listId): ?array { $db = DBConnection::instance(); $t = array(); @@ -257,7 +257,7 @@ class TasksController extends ApiController { return $t; } - private function fullNewTaskInList(int $listId) + private function fullNewTaskInList(int $listId): ?array { $db = DBConnection::instance(); $title = trim($this->req->jsonBody['title'] ?? ''); @@ -297,7 +297,7 @@ class TasksController extends ApiController { return $t; } - private function editTask(int $id) + private function editTask(int $id): ?array { $db = DBConnection::instance(); $title = trim($this->req->jsonBody['title'] ?? ''); @@ -332,7 +332,7 @@ class TasksController extends ApiController { return $t; } - private function moveTask(int $id) + private function moveTask(int $id): ?array { $db = DBConnection::instance(); $fromId = (int)($this->req->jsonBody['from'] ?? 0); @@ -345,7 +345,7 @@ class TasksController extends ApiController { return $t; } - private function doMoveTask(int $id, int $listId) + private function doMoveTask(int $id, int $listId): bool { $db = DBConnection::instance(); @@ -366,7 +366,7 @@ class TasksController extends ApiController { return true; } - private function completeTask(int $id) + private function completeTask(int $id): ?array { $db = DBConnection::instance(); $compl = (int)($this->req->jsonBody['compl'] ?? 0); @@ -384,7 +384,7 @@ class TasksController extends ApiController { return $t; } - private function editNote(int $id) + private function editNote(int $id): ?array { $db = DBConnection::instance(); $note = $this->req->jsonBody['note'] ?? ''; @@ -396,7 +396,7 @@ class TasksController extends ApiController { return $t; } - private function priorityTask(int $id) + private function priorityTask(int $id): ?array { $db = DBConnection::instance(); $prio = (int)($this->req->jsonBody['prio'] ?? 0); @@ -410,7 +410,7 @@ class TasksController extends ApiController { } - private function changeTaskOrder() + private function changeTaskOrder(): ?array { $db = DBConnection::instance(); $order = $this->req->jsonBody['order'] ?? null; @@ -436,7 +436,7 @@ class TasksController extends ApiController { return $t; } - private function getUserListsSimple() + private function getUserListsSimple(): array { $db = DBConnection::instance(); $a = array(); @@ -447,7 +447,7 @@ class TasksController extends ApiController { return $a; } - private function prepareTaskRow(array $r) + private function prepareTaskRow(array $r): array { $lang = Lang::instance(); $dueA = $this->prepareDuedate($r['duedate']); @@ -486,7 +486,7 @@ class TasksController extends ApiController { } - private function parseDuedate($s) + private function parseDuedate($s): ?string { $df2 = Config::get('dateformat2'); if (max((int)strpos($df2,'n'), (int)strpos($df2,'m')) > max((int)strpos($df2,'d'), (int)strpos($df2,'j'))) $formatDayFirst = true; @@ -535,7 +535,7 @@ class TasksController extends ApiController { return "$y-$m-$d"; } - private function prepareDuedate($duedate) + private function prepareDuedate($duedate): array { $lang = Lang::instance(); @@ -568,7 +568,9 @@ class TasksController extends ApiController { private function date2int($d) : int { - if (!$d) return 33330000; + if (!$d) { + return 33330000; + } $ad = explode('-', $d); $s = $ad[0]; if (strlen($ad[1]) < 2) $s .= "0$ad[1]"; else $s .= $ad[1]; @@ -576,7 +578,7 @@ class TasksController extends ApiController { return (int)$s; } - private function daysInMonth(int $m, int $y = 0) + private function daysInMonth(int $m, int $y = 0): int { if ($y == 0) $y = (int)date('Y'); $a = array(1=>31,(($y-2000)%4?28:29),31,30,31,30,31,31,30,31,30,31); @@ -591,7 +593,7 @@ class TasksController extends ApiController { return $id ? $id : 0; } - private function getOrCreateTag($name) + private function getOrCreateTag($name): array { $db = DBConnection::instance(); $tagId = $db->sq("SELECT id FROM {$db->prefix}tags WHERE name=?", array($name)); @@ -605,10 +607,10 @@ class TasksController extends ApiController { ); } - private function prepareTags(string $tagsStr) + private function prepareTags(string $tagsStr): ?array { $tags = explode(',', $tagsStr); - if (!$tags) return 0; + if (!$tags) return null; $aTags = array('tags'=>array(), 'ids'=>array()); foreach ($tags as $tag) @@ -637,7 +639,7 @@ class TasksController extends ApiController { } } - private function parseSmartSyntax($title) + private function parseSmartSyntax($title): array { $a = [ 'prio' => 0,