Added API documentation prototype
This commit is contained in:
parent
03a6809510
commit
634d0061d4
12 changed files with 205 additions and 24 deletions
5
public_html/media/css/static-api.css
Normal file
5
public_html/media/css/static-api.css
Normal file
|
@ -0,0 +1,5 @@
|
||||||
|
pre {
|
||||||
|
background: ghostwhite;
|
||||||
|
padding: 0.5em;
|
||||||
|
border-left: 0.2em solid silver;
|
||||||
|
}
|
|
@ -1,6 +1,11 @@
|
||||||
<?php
|
<?php
|
||||||
final class Api
|
final class Api
|
||||||
{
|
{
|
||||||
|
public static function getUrl()
|
||||||
|
{
|
||||||
|
return \Chibi\Router::linkTo(['ApiController', 'runAction']);
|
||||||
|
}
|
||||||
|
|
||||||
public static function run(IJob $job, $jobArgs)
|
public static function run(IJob $job, $jobArgs)
|
||||||
{
|
{
|
||||||
$user = Auth::getCurrentUser();
|
$user = Auth::getCurrentUser();
|
||||||
|
|
|
@ -59,4 +59,14 @@ class JobArgs
|
||||||
{
|
{
|
||||||
return JobArgsOptional::factory(func_get_args());
|
return JobArgsOptional::factory(func_get_args());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static function getInternalArguments()
|
||||||
|
{
|
||||||
|
return
|
||||||
|
[
|
||||||
|
self::ARG_POST_ENTITY,
|
||||||
|
self::ARG_USER_ENTITY,
|
||||||
|
self::ARG_COMMENT_ENTITY
|
||||||
|
];
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -7,6 +7,7 @@ abstract class AbstractJob implements IJob
|
||||||
|
|
||||||
protected $arguments = [];
|
protected $arguments = [];
|
||||||
protected $context = self::CONTEXT_NORMAL;
|
protected $context = self::CONTEXT_NORMAL;
|
||||||
|
protected $subJobs;
|
||||||
|
|
||||||
public function prepare()
|
public function prepare()
|
||||||
{
|
{
|
||||||
|
@ -27,6 +28,16 @@ abstract class AbstractJob implements IJob
|
||||||
return $name;
|
return $name;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function addSubJob(IJob $subJob)
|
||||||
|
{
|
||||||
|
$this->subJobs []= $subJob;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getSubJobs()
|
||||||
|
{
|
||||||
|
return $this->subJobs;
|
||||||
|
}
|
||||||
|
|
||||||
public function getRequiredPrivileges()
|
public function getRequiredPrivileges()
|
||||||
{
|
{
|
||||||
return false;
|
return false;
|
||||||
|
|
|
@ -1,6 +1,11 @@
|
||||||
<?php
|
<?php
|
||||||
class AddPostJob extends AbstractJob
|
class AddPostJob extends AbstractJob
|
||||||
{
|
{
|
||||||
|
public function __construct()
|
||||||
|
{
|
||||||
|
$this->addSubJob(new EditPostJob());
|
||||||
|
}
|
||||||
|
|
||||||
public function execute()
|
public function execute()
|
||||||
{
|
{
|
||||||
$post = PostModel::spawn();
|
$post = PostModel::spawn();
|
||||||
|
@ -20,7 +25,7 @@ class AddPostJob extends AbstractJob
|
||||||
Logger::bufferChanges();
|
Logger::bufferChanges();
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
$job = new EditPostJob();
|
$job = $this->getSubJobs()[0];
|
||||||
$job->setContext(AbstractJob::CONTEXT_BATCH_ADD);
|
$job->setContext(AbstractJob::CONTEXT_BATCH_ADD);
|
||||||
Api::run($job, $arguments);
|
Api::run($job, $arguments);
|
||||||
}
|
}
|
||||||
|
|
|
@ -6,6 +6,12 @@ class EditPostJob extends AbstractJob
|
||||||
public function __construct()
|
public function __construct()
|
||||||
{
|
{
|
||||||
$this->postRetriever = new PostRetriever($this);
|
$this->postRetriever = new PostRetriever($this);
|
||||||
|
$this->addSubJob(new EditPostSafetyJob());
|
||||||
|
$this->addSubJob(new EditPostTagsJob());
|
||||||
|
$this->addSubJob(new EditPostSourceJob());
|
||||||
|
$this->addSubJob(new EditPostRelationsJob());
|
||||||
|
$this->addSubJob(new EditPostContentJob());
|
||||||
|
$this->addSubJob(new EditPostThumbJob());
|
||||||
}
|
}
|
||||||
|
|
||||||
public function execute()
|
public function execute()
|
||||||
|
@ -14,17 +20,7 @@ class EditPostJob extends AbstractJob
|
||||||
|
|
||||||
Logger::bufferChanges();
|
Logger::bufferChanges();
|
||||||
|
|
||||||
$subJobs =
|
foreach ($this->getSubJobs() as $subJob)
|
||||||
[
|
|
||||||
new EditPostSafetyJob(),
|
|
||||||
new EditPostTagsJob(),
|
|
||||||
new EditPostSourceJob(),
|
|
||||||
new EditPostRelationsJob(),
|
|
||||||
new EditPostContentJob(),
|
|
||||||
new EditPostThumbJob(),
|
|
||||||
];
|
|
||||||
|
|
||||||
foreach ($subJobs as $subJob)
|
|
||||||
{
|
{
|
||||||
$subJob->setContext($this->getContext() == self::CONTEXT_BATCH_ADD
|
$subJob->setContext($this->getContext() == self::CONTEXT_BATCH_ADD
|
||||||
? self::CONTEXT_BATCH_ADD
|
? self::CONTEXT_BATCH_ADD
|
||||||
|
|
|
@ -1,6 +1,11 @@
|
||||||
<?php
|
<?php
|
||||||
class AddUserJob extends AbstractJob
|
class AddUserJob extends AbstractJob
|
||||||
{
|
{
|
||||||
|
public function __construct()
|
||||||
|
{
|
||||||
|
$this->addSubJob(new EditUserJob());
|
||||||
|
}
|
||||||
|
|
||||||
public function execute()
|
public function execute()
|
||||||
{
|
{
|
||||||
$firstUser = UserModel::getCount() == 0;
|
$firstUser = UserModel::getCount() == 0;
|
||||||
|
@ -25,7 +30,7 @@ class AddUserJob extends AbstractJob
|
||||||
Logger::bufferChanges();
|
Logger::bufferChanges();
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
$job = new EditUserJob();
|
$job = $this->getSubJobs()[0];
|
||||||
$job->setContext(self::CONTEXT_BATCH_ADD);
|
$job->setContext(self::CONTEXT_BATCH_ADD);
|
||||||
Api::run($job, $arguments);
|
Api::run($job, $arguments);
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,24 +2,20 @@
|
||||||
class EditUserJob extends AbstractJob
|
class EditUserJob extends AbstractJob
|
||||||
{
|
{
|
||||||
protected $userRetriever;
|
protected $userRetriever;
|
||||||
protected $subJobs;
|
|
||||||
|
|
||||||
public function __construct()
|
public function __construct()
|
||||||
{
|
{
|
||||||
$this->userRetriever = new UserRetriever($this);
|
$this->userRetriever = new UserRetriever($this);
|
||||||
$this->subJobs =
|
$this->addSubJob(new EditUserAccessRankJob());
|
||||||
[
|
$this->addSubJob(new EditUserNameJob());
|
||||||
new EditUserAccessRankJob(),
|
$this->addSubJob(new EditUserPasswordJob());
|
||||||
new EditUserNameJob(),
|
$this->addSubJob(new EditUserEmailJob());
|
||||||
new EditUserPasswordJob(),
|
|
||||||
new EditUserEmailJob(),
|
|
||||||
];
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public function canEditAnything($user)
|
public function canEditAnything($user)
|
||||||
{
|
{
|
||||||
$this->privileges = [];
|
$this->privileges = [];
|
||||||
foreach ($this->subJobs as $subJob)
|
foreach ($this->getSubJobs() as $subJob)
|
||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
|
@ -40,7 +36,7 @@ class EditUserJob extends AbstractJob
|
||||||
|
|
||||||
Logger::bufferChanges();
|
Logger::bufferChanges();
|
||||||
|
|
||||||
foreach ($this->subJobs as $subJob)
|
foreach ($this->getSubJobs() as $subJob)
|
||||||
{
|
{
|
||||||
$subJob->setContext($this->getContext() == self::CONTEXT_BATCH_ADD
|
$subJob->setContext($this->getContext() == self::CONTEXT_BATCH_ADD
|
||||||
? self::CONTEXT_BATCH_ADD
|
? self::CONTEXT_BATCH_ADD
|
||||||
|
|
|
@ -38,6 +38,11 @@ class StaticPagesController extends AbstractController
|
||||||
$this->renderView('static-help');
|
$this->renderView('static-help');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function apiDocsView()
|
||||||
|
{
|
||||||
|
$this->renderView('static-api');
|
||||||
|
}
|
||||||
|
|
||||||
public function fatalErrorView($code = null)
|
public function fatalErrorView($code = null)
|
||||||
{
|
{
|
||||||
throw new SimpleException('Error ' . $code . ' while retrieving ' . $_SERVER['REQUEST_URI']);
|
throw new SimpleException('Error ' . $code . ' while retrieving ' . $_SERVER['REQUEST_URI']);
|
||||||
|
|
|
@ -42,9 +42,10 @@ $this->assets->addScript('core.js');
|
||||||
<div class="left">
|
<div class="left">
|
||||||
<span>
|
<span>
|
||||||
<a href="<?= Core::getConfig()->misc->githubLink ?>">
|
<a href="<?= Core::getConfig()->misc->githubLink ?>">
|
||||||
szurubooru <?= PropertyModel::get(PropertyModel::EngineVersion) ?>
|
<?= PropertyModel::get(PropertyModel::EngineVersion) ?>
|
||||||
</a>
|
</a>
|
||||||
</span>
|
</span>
|
||||||
|
<span><a href="<?= \Chibi\Router::linkTo(['StaticPagesController', 'apiDocsView']) ?>">API</a></span>
|
||||||
<?php if (Access::check(new Privilege(Privilege::ListLogs))): ?>
|
<?php if (Access::check(new Privilege(Privilege::ListLogs))): ?>
|
||||||
<span><a href="<?= \Chibi\Router::linkTo(['LogController', 'listView']) ?>">Logs</a></span>
|
<span><a href="<?= \Chibi\Router::linkTo(['LogController', 'listView']) ?>">Logs</a></span>
|
||||||
<?php endif ?>
|
<?php endif ?>
|
||||||
|
|
141
src/Views/static/static-api.phtml
Normal file
141
src/Views/static/static-api.phtml
Normal file
|
@ -0,0 +1,141 @@
|
||||||
|
<?php
|
||||||
|
$this->assets->addStylesheet('static-api.css');
|
||||||
|
?>
|
||||||
|
|
||||||
|
<h2>API documentation</h2>
|
||||||
|
|
||||||
|
<p>All interaction with API proceeds through just one URL: <code><?= Api::getUrl() ?></code>.</p>
|
||||||
|
|
||||||
|
<p>One request to API means executing one so-called „job”. Most of things that can be done through web
|
||||||
|
interface, can be also done through the API.</p>
|
||||||
|
|
||||||
|
<hr/>
|
||||||
|
|
||||||
|
<h2>Request construction</h2>
|
||||||
|
|
||||||
|
<p>To specify job to be executed, send <code>name</code> parameter. Optional job arguments can be specified with
|
||||||
|
<code>args</code> parameter. Example using <code>curl</code>:</p>
|
||||||
|
|
||||||
|
<pre><code>curl \
|
||||||
|
--data 'name=get-post' \
|
||||||
|
'&args[post-id]=5408' \
|
||||||
|
<?= Api::getUrl() ?></code></pre>
|
||||||
|
|
||||||
|
<h3>Authentication</h3>
|
||||||
|
|
||||||
|
<p>In order to make authenticated request, you need to supply request with your credentials. This can be done by
|
||||||
|
passing <code>auth[user]</code> and <code>auth[name]</code> parameters:</p>
|
||||||
|
|
||||||
|
<pre><code>curl \
|
||||||
|
--data 'auth[user]=example' \
|
||||||
|
'&auth[pass]=secret' \
|
||||||
|
'&name=get-post' \
|
||||||
|
'&args[post-id]=5408' \
|
||||||
|
<?= Api::getUrl() ?></code></pre>
|
||||||
|
|
||||||
|
<h3>Sending files</h3>
|
||||||
|
|
||||||
|
<p>In order to send files to API, you have to send <code>multipart/form-data</code> request. With <code>curl</code>
|
||||||
|
this can be done using <code>-F</code> option to encode each parameter and prepending <code>@</code> character to
|
||||||
|
chosen file name:</p>
|
||||||
|
|
||||||
|
<pre><code>curl \
|
||||||
|
-F 'auth[user]=example' \
|
||||||
|
-F 'auth[pass]=secret' \
|
||||||
|
-F 'name=add-post' \
|
||||||
|
-F 'args[new-tag-names][0]=test' \
|
||||||
|
-F 'args[new-tag-names][1]=test2' \
|
||||||
|
-F 'args[new-tag-names][2]=test3' \
|
||||||
|
-F 'args[new-post-content]=@./custom.png' \
|
||||||
|
<?= Api::getUrl() ?></code></pre>
|
||||||
|
|
||||||
|
<h3>Output</h3>
|
||||||
|
|
||||||
|
<p>Output is always represented in JSON array. Files are stored in JSON as well using gzip compression and base64
|
||||||
|
encoding.</p>
|
||||||
|
|
||||||
|
<h3>Handling errors</h3>
|
||||||
|
|
||||||
|
<p>When errors occur all errors are logged to <code>message</code> field and changes done with request is rolled
|
||||||
|
back.</p>
|
||||||
|
|
||||||
|
<hr/>
|
||||||
|
|
||||||
|
<h2>Privilege table</h2>
|
||||||
|
|
||||||
|
<p>Each job checks for some privileges depending on context it is run with.</p>
|
||||||
|
|
||||||
|
<table>
|
||||||
|
<thead>
|
||||||
|
<tr>
|
||||||
|
<th>Privilege</th>
|
||||||
|
<th>Minimum access rank</th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody>
|
||||||
|
<?php foreach (Core::getConfig()->privileges as $privilege => $minAccessRank): ?>
|
||||||
|
<tr>
|
||||||
|
<td><?= $privilege ?></td>
|
||||||
|
<td><?= $minAccessRank ?></td>
|
||||||
|
</tr>
|
||||||
|
<?php endforeach ?>
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
|
||||||
|
<hr/>
|
||||||
|
|
||||||
|
<h2>Full list of available jobs</h2>
|
||||||
|
|
||||||
|
<?php
|
||||||
|
$jobClassNames = Api::getAllJobClassNames();
|
||||||
|
natcasesort($jobClassNames);
|
||||||
|
|
||||||
|
foreach ($jobClassNames as $className)
|
||||||
|
{
|
||||||
|
$job = new $className;
|
||||||
|
?>
|
||||||
|
|
||||||
|
<h3 id="job-<?= $job->getName() ?>">
|
||||||
|
<a href="#job-<?= $job->getName() ?>"><?= $job->getName() ?></a>
|
||||||
|
</h3>
|
||||||
|
|
||||||
|
<?php
|
||||||
|
$showArgs = function($args) use (&$showArgs)
|
||||||
|
{
|
||||||
|
if ($args instanceof JobArgsConjunction)
|
||||||
|
{
|
||||||
|
return '(' . implode(' AND ', array_filter(array_map(function($arg) use ($showArgs)
|
||||||
|
{
|
||||||
|
return $showArgs($arg);
|
||||||
|
}, $args->args))) . ')';
|
||||||
|
}
|
||||||
|
elseif ($args instanceof JobArgsAlternative)
|
||||||
|
{
|
||||||
|
return '(' . implode(' OR ', array_filter(array_map(function($arg) use ($showArgs)
|
||||||
|
{
|
||||||
|
return $showArgs($arg);
|
||||||
|
}, $args->args))) . ')';
|
||||||
|
}
|
||||||
|
elseif ($args instanceof JobArgsOptional)
|
||||||
|
return $showArgs($args->args[0]) . ' (optional)';
|
||||||
|
elseif (in_array($args, JobArgs::getInternalArguments()))
|
||||||
|
return null;
|
||||||
|
else
|
||||||
|
return $args;
|
||||||
|
};
|
||||||
|
?>
|
||||||
|
|
||||||
|
<p>Required arguments: <?= $showArgs($job->getRequiredArguments()) ?></p>
|
||||||
|
<p>Requires e-mail confirmation: <?= $job->isConfirmedEmailRequired() ? 'yes' : 'no' ?></p>
|
||||||
|
<p>Requires authentication: <?= $job->isAuthenticationRequired() ? 'yes' : 'no' ?></p>
|
||||||
|
<?php if (!empty($job->getSubJobs())): ?>
|
||||||
|
<p>Sub jobs: <?= implode(', ', array_map(function($job)
|
||||||
|
{
|
||||||
|
return '<a href="#job-' . $job->getName() . '">' . $job->getName() . '</a>';
|
||||||
|
}, $job->getSubJobs())); ?></p>
|
||||||
|
<?php endif ?>
|
||||||
|
|
||||||
|
<hr/>
|
||||||
|
<?php
|
||||||
|
}
|
||||||
|
?>
|
|
@ -3,6 +3,7 @@
|
||||||
|
|
||||||
\Chibi\Router::register(['StaticPagesController', 'mainPageView'], 'GET', '');
|
\Chibi\Router::register(['StaticPagesController', 'mainPageView'], 'GET', '');
|
||||||
\Chibi\Router::register(['StaticPagesController', 'mainPageView'], 'GET', '/index');
|
\Chibi\Router::register(['StaticPagesController', 'mainPageView'], 'GET', '/index');
|
||||||
|
\Chibi\Router::register(['StaticPagesController', 'apiDocsView'], 'GET', '/api-docs');
|
||||||
\Chibi\Router::register(['StaticPagesController', 'helpView'], 'GET', '/help');
|
\Chibi\Router::register(['StaticPagesController', 'helpView'], 'GET', '/help');
|
||||||
\Chibi\Router::register(['StaticPagesController', 'helpView'], 'GET', '/help/{tab}');
|
\Chibi\Router::register(['StaticPagesController', 'helpView'], 'GET', '/help/{tab}');
|
||||||
\Chibi\Router::register(['StaticPagesController', 'fatalErrorView'], 'POST', '/fatal-error/{code}');
|
\Chibi\Router::register(['StaticPagesController', 'fatalErrorView'], 'POST', '/fatal-error/{code}');
|
||||||
|
|
Loading…
Reference in a new issue