Merge pull request 'Add Adminstration API to support accounts management' (#94) from admin/api-accounts into master
continuous-integration/drone/push Build is passing
Details
continuous-integration/drone/push Build is passing
Details
Reviewed-on: #94pull/96/head
commit
a3e38bded1
@ -0,0 +1,45 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace IQBall\Api;
|
||||||
|
|
||||||
|
use IQBall\Core\Control;
|
||||||
|
use IQBall\Core\ControlSchemaErrorResponseFactory;
|
||||||
|
use IQBall\Core\Http\HttpCodes;
|
||||||
|
use IQBall\Core\Http\HttpRequest;
|
||||||
|
use IQBall\Core\Http\HttpResponse;
|
||||||
|
use IQBall\Core\Http\JsonHttpResponse;
|
||||||
|
use IQBall\Core\Validation\Validator;
|
||||||
|
|
||||||
|
class APIControl {
|
||||||
|
private static function errorFactory(): ControlSchemaErrorResponseFactory {
|
||||||
|
return new class () implements ControlSchemaErrorResponseFactory {
|
||||||
|
public function apply(array $failures): HttpResponse {
|
||||||
|
return new JsonHttpResponse($failures, HttpCodes::BAD_REQUEST);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Runs given callback, if the request's payload json validates the given schema.
|
||||||
|
* @param array<string, Validator[]> $schema an array of `fieldName => DefaultValidators` which represents the request object schema
|
||||||
|
* @param callable(HttpRequest): HttpResponse $run the callback to run if the request is valid according to the given schema.
|
||||||
|
* The callback must accept an HttpRequest, and return an HttpResponse object.
|
||||||
|
* @return HttpResponse
|
||||||
|
*/
|
||||||
|
public static function runChecked(array $schema, callable $run): HttpResponse {
|
||||||
|
return Control::runChecked($schema, $run, self::errorFactory());
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Runs given callback, if the given request data array validates the given schema.
|
||||||
|
* @param array<string, mixed> $data the request's data array.
|
||||||
|
* @param array<string, Validator[]> $schema an array of `fieldName => DefaultValidators` which represents the request object schema
|
||||||
|
* @param callable(HttpRequest): HttpResponse $run the callback to run if the request is valid according to the given schema.
|
||||||
|
* The callback must accept an HttpRequest, and return an HttpResponse object.
|
||||||
|
* @return HttpResponse
|
||||||
|
*/
|
||||||
|
public static function runCheckedFrom(array $data, array $schema, callable $run): HttpResponse {
|
||||||
|
return Control::runCheckedFrom($data, $schema, $run, self::errorFactory());
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,108 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace IQBall\Api\Controller;
|
||||||
|
|
||||||
|
use IQBall\Api\APIControl;
|
||||||
|
use IQBall\App\Control;
|
||||||
|
use IQBall\Core\Data\Account;
|
||||||
|
use IQBall\Core\Gateway\AccountGateway;
|
||||||
|
use IQBall\Core\Http\HttpCodes;
|
||||||
|
use IQBall\Core\Http\HttpRequest;
|
||||||
|
use IQBall\Core\Http\HttpResponse;
|
||||||
|
use IQBall\Core\Http\JsonHttpResponse;
|
||||||
|
use IQBall\Core\Model\AuthModel;
|
||||||
|
use IQBall\Core\Validation\DefaultValidators;
|
||||||
|
use IQBall\Core\Validation\ValidationFail;
|
||||||
|
|
||||||
|
class APIAccountsController {
|
||||||
|
private AccountGateway $accounts;
|
||||||
|
private AuthModel $authModel;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param AuthModel $model
|
||||||
|
* @param AccountGateway $accounts
|
||||||
|
*/
|
||||||
|
public function __construct(AuthModel $model, AccountGateway $accounts) {
|
||||||
|
$this->accounts = $accounts;
|
||||||
|
$this->authModel = $model;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param array<string, mixed> $request
|
||||||
|
* @return HttpResponse
|
||||||
|
*/
|
||||||
|
public function listUsers(array $request): HttpResponse {
|
||||||
|
return APIControl::runCheckedFrom($request, [
|
||||||
|
'start' => [DefaultValidators::isUnsignedInteger()],
|
||||||
|
'n' => [DefaultValidators::isIntInRange(0, 250)],
|
||||||
|
'search' => [DefaultValidators::lenBetween(0, 256)],
|
||||||
|
], function (HttpRequest $req) {
|
||||||
|
$accounts = $this->accounts->searchAccounts(intval($req['start']), intval($req['n']), $req["search"]);
|
||||||
|
$users = array_map(fn(Account $acc) => $acc->getUser(), $accounts);
|
||||||
|
return new JsonHttpResponse([
|
||||||
|
"users" => $users,
|
||||||
|
"totalCount" => $this->accounts->totalCount(),
|
||||||
|
]);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param int $userId
|
||||||
|
* @return HttpResponse given user information.
|
||||||
|
*/
|
||||||
|
public function getUser(int $userId): HttpResponse {
|
||||||
|
$acc = $this->accounts->getAccount($userId);
|
||||||
|
|
||||||
|
if ($acc == null) {
|
||||||
|
return new JsonHttpResponse([ValidationFail::notFound("User not found")], HttpCodes::NOT_FOUND);
|
||||||
|
}
|
||||||
|
|
||||||
|
return new JsonHttpResponse($acc->getUser());
|
||||||
|
}
|
||||||
|
|
||||||
|
public function addUser(): HttpResponse {
|
||||||
|
return APIControl::runChecked([
|
||||||
|
"username" => [DefaultValidators::name()],
|
||||||
|
"email" => [DefaultValidators::email()],
|
||||||
|
"password" => [DefaultValidators::password()],
|
||||||
|
"isAdmin" => [DefaultValidators::bool()],
|
||||||
|
], function (HttpRequest $req) {
|
||||||
|
$model = new AuthModel($this->accounts);
|
||||||
|
|
||||||
|
$account = $model->register($req["username"], $req["password"], $req["email"]);
|
||||||
|
if ($account == null) {
|
||||||
|
return new JsonHttpResponse([new ValidationFail("already exists", "An account with provided email ")], HttpCodes::FORBIDDEN);
|
||||||
|
}
|
||||||
|
|
||||||
|
return new JsonHttpResponse([
|
||||||
|
"id" => $account->getUser()->getId(),
|
||||||
|
]);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
public function removeUsers(): HttpResponse {
|
||||||
|
return APIControl::runChecked([
|
||||||
|
"identifiers" => [DefaultValidators::array(), DefaultValidators::forall(DefaultValidators::isUnsignedInteger())],
|
||||||
|
], function (HttpRequest $req) {
|
||||||
|
$this->accounts->removeAccounts($req["identifiers"]);
|
||||||
|
return HttpResponse::fromCode(HttpCodes::OK);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
public function updateUser(int $id): HttpResponse {
|
||||||
|
return APIControl::runChecked([
|
||||||
|
"email" => [DefaultValidators::email()],
|
||||||
|
"username" => [DefaultValidators::name()],
|
||||||
|
"isAdmin" => [DefaultValidators::bool()],
|
||||||
|
], function (HttpRequest $req) use ($id) {
|
||||||
|
$mailAccount = $this->accounts->getAccount($id);
|
||||||
|
if ($mailAccount->getUser()->getId() != $id) {
|
||||||
|
return new JsonHttpResponse([new ValidationFail("email exists", "The provided mail address already exists for another account.")], HttpCodes::FORBIDDEN);
|
||||||
|
}
|
||||||
|
|
||||||
|
$this->authModel->update($id, $req["email"], $req["username"], $req["isAdmin"]);
|
||||||
|
return HttpResponse::fromCode(HttpCodes::OK);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,45 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace IQBall\Api\Controller;
|
||||||
|
|
||||||
|
use IQBall\Core\Http\HttpResponse;
|
||||||
|
use IQBall\Core\Http\JsonHttpResponse;
|
||||||
|
|
||||||
|
class APIServerController {
|
||||||
|
private string $basePath;
|
||||||
|
private \PDO $pdo;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param string $basePath
|
||||||
|
* @param \PDO $pdo
|
||||||
|
*/
|
||||||
|
public function __construct(string $basePath, \PDO $pdo) {
|
||||||
|
$this->basePath = $basePath;
|
||||||
|
$this->pdo = $pdo;
|
||||||
|
}
|
||||||
|
|
||||||
|
private function countLines(string $table): int {
|
||||||
|
$stmnt = $this->pdo->prepare("SELECT count(*) FROM $table");
|
||||||
|
$stmnt->execute();
|
||||||
|
$res = $stmnt->fetch(\PDO::FETCH_BOTH);
|
||||||
|
return $res[0];
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return HttpResponse some (useless) information about the server
|
||||||
|
*/
|
||||||
|
public function getServerInfo(): HttpResponse {
|
||||||
|
|
||||||
|
return new JsonHttpResponse([
|
||||||
|
'base_path' => $this->basePath,
|
||||||
|
'date' => (int) gettimeofday(true) * 1000,
|
||||||
|
'database' => [
|
||||||
|
'accounts' => $this->countLines("Account") . " line(s)",
|
||||||
|
'tactics' => $this->countLines("Tactic") . " line(s)",
|
||||||
|
'teams' => $this->countLines("Team") . " line(s)",
|
||||||
|
],
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,44 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace IQBall\App;
|
||||||
|
|
||||||
|
use IQBall\Core\Control;
|
||||||
|
use IQBall\Core\ControlSchemaErrorResponseFactory;
|
||||||
|
use IQBall\Core\Http\HttpCodes;
|
||||||
|
use IQBall\Core\Http\HttpRequest;
|
||||||
|
use IQBall\Core\Http\HttpResponse;
|
||||||
|
use IQBall\Core\Validation\Validator;
|
||||||
|
|
||||||
|
class AppControl {
|
||||||
|
private static function errorFactory(): ControlSchemaErrorResponseFactory {
|
||||||
|
return new class () implements ControlSchemaErrorResponseFactory {
|
||||||
|
public function apply(array $failures): HttpResponse {
|
||||||
|
return ViewHttpResponse::twig("error.html.twig", ['failures' => $failures], HttpCodes::BAD_REQUEST);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Runs given callback, if the request's payload json validates the given schema.
|
||||||
|
* @param array<string, Validator[]> $schema an array of `fieldName => DefaultValidators` which represents the request object schema
|
||||||
|
* @param callable(HttpRequest): HttpResponse $run the callback to run if the request is valid according to the given schema.
|
||||||
|
* The callback must accept an HttpRequest, and return an HttpResponse object.
|
||||||
|
* @return HttpResponse
|
||||||
|
*/
|
||||||
|
public static function runChecked(array $schema, callable $run): HttpResponse {
|
||||||
|
return Control::runChecked($schema, $run, self::errorFactory());
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Runs given callback, if the given request data array validates the given schema.
|
||||||
|
* @param array<string, mixed> $data the request's data array.
|
||||||
|
* @param array<string, Validator[]> $schema an array of `fieldName => DefaultValidators` which represents the request object schema
|
||||||
|
* @param callable(HttpRequest): HttpResponse $run the callback to run if the request is valid according to the given schema.
|
||||||
|
* The callback must accept an HttpRequest, and return an HttpResponse object.
|
||||||
|
* @return HttpResponse
|
||||||
|
*/
|
||||||
|
public static function runCheckedFrom(array $data, array $schema, callable $run): HttpResponse {
|
||||||
|
return Control::runCheckedFrom($data, $schema, $run, self::errorFactory());
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,14 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace IQBall\Core;
|
||||||
|
|
||||||
|
use IQBall\Core\Http\HttpResponse;
|
||||||
|
use IQBall\Core\Validation\ValidationFail;
|
||||||
|
|
||||||
|
interface ControlSchemaErrorResponseFactory {
|
||||||
|
/**
|
||||||
|
* @param ValidationFail[] $failures
|
||||||
|
* @return HttpResponse
|
||||||
|
*/
|
||||||
|
public function apply(array $failures): HttpResponse;
|
||||||
|
}
|
Loading…
Reference in new issue