implement ApiResponse class and change http api internals

This commit is contained in:
maxpozdeev 2022-09-05 17:13:46 +03:00
parent f31c173db4
commit bb23883e5b
5 changed files with 145 additions and 114 deletions

View file

@ -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;
}
}

View file

@ -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()) {

View file

@ -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;

View file

@ -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;
}

View file

@ -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,