...
- articles_list - GET /portal/articles
- articles_info - GET /portal/articles/:id/
- article_add - POST /portal/articles
- articles_update - PUT /portal/articles/:id/
- article_delete - DELETE /portal/articles/:id/
И так далее.Чётких правил по названию папки нет, но рекомендуем использовать конструкцию subpath_action
Ядро
За пример взят микромодуль Companies.
Расположение - t/Api
Создаём:
...
- companies - GET /companies
- company - GET /companies/:id/
- company_add - POST /companies
- companies_change - PUT /companies/:id/
- company_delete - DELETE /companies/:id/
Написание Api.t
будет дополняться
- - POST /companies
- companies_change - PUT /companies/:id/
- company_delete - DELETE /companies/:id/
Чётких правил по названию папки нет, но рекомендуем использовать конструкцию subpath_action
Написание Api.t
На данный момент этот раздел документации не завершён.
Спецификация Api.t ещё полностью не определена.
Следите за обновлениями.
Code Block | ||||||
---|---|---|---|---|---|---|
| ||||||
=head1 NAME
Portal API test
=cut
use strict;
use warnings;
use lib '../';
use Test::More;
use FindBin '$Bin';
use FindBin qw($RealBin);
use JSON;
# Подключаем конфиг
require $Bin . '/../../../../libexec/config.pl';
# Добавляем пути компилятора
BEGIN {
our $libpath = '../../../../';
my $sql_type = 'mysql';
unshift(@INC, $libpath . "Abills/$sql_type/");
unshift(@INC, $libpath);
unshift(@INC, $libpath . 'lib/');
unshift(@INC, $libpath . 'libexec/');
unshift(@INC, $libpath . 'Abills/');
unshift(@INC, $libpath . 'Abills/modules/');
}
use Abills::Defs;
use Abills::Api::Tests::Init qw(test_runner folder_list help);
use Abills::Base qw(parse_arguments);
use Admins;
use Users;
use Portal;
our (
%conf
);
# Обязательно подключение к базе
my $db = Abills::SQL->connect(
$conf{dbtype}, $conf{dbhost}, $conf{dbname}, $conf{dbuser}, $conf{dbpasswd},
{
CHARSET => ($conf{dbcharset}) ? $conf{dbcharset} : undef,
dbdebug => $conf{dbdebug}
}
);
my $admin = Admins->new($db, \%conf);
my $Users = Users->new($db, $admin, \%conf);
my $Portal = Portal->new($db, $admin, \%conf);
# Выбираем последнего пользователя к нашим кредам
my $user = $Users->list({
LOGIN => $conf{API_TEST_USER_LOGIN} || 'test',
COLS_NAME => 1,
COLS_UPPER => 1
})->[0];
# Выбираем с базы articles
my $articles = $Portal->portal_articles_list({
COLS_NAME => 1
});
# Выбираем с базы menus
my $menus = $Portal->portal_menu_list({
ID => '_SHOW',
COLS_NAME => 1
});
# Выбираем с базы newsletters
my $newsletters = $Portal->portal_newsletter_list({
COLS_NAME => 1
});
# Выбираем с базы attachments
my $attachments = $Portal->attachment_list({
ID => '_SHOW',
COLS_NAME => 1
});
# Парсим аргументы с stdin
my $ARGS = parse_arguments(\@ARGV);
# ADMIN API key
my $apiKey = $ARGS->{KEY} || $ARGV[$#ARGV] || q{};
# Получаем список request, schema.json
my @test_list = folder_list($ARGS, $RealBin);
my $debug = $ARGS->{DEBUG} || 0;
# Определяем ли это help, и принтим его в случае
if (($ARGV[0] && lc($ARGV[0]) eq 'help') || defined($ARGS->{help}) || defined($ARGS->{HELP})) {
help();
exit 0;
}
# Берём динамические айдишки для мока.
# Тоесть, база перед этим должна быть заполненная данными.
my $article_id = $articles->[-1]->{id} || 0;
my $newsletter_id = $newsletters->[-1]->{id} || 0;
my $attachment_id = $attachments->[-1]->{id} || 0;
my $menu_id = $menus->[-1]->{id} || 0;
# Проходимся по списку, и заменяем path params на реальные данные.
foreach my $test (@test_list) {
if ($test->{path} =~ /portal\/articles\/:id/g) {
$test->{path} =~ s/:id/$article_id/g;
}
elsif ($test->{path} =~ /portal\/newsletter\/:id/g) {
$test->{path} =~ s/:id/$newsletter_id/g;
}
elsif ($test->{path} =~ /portal\/menus\/:id/g) {
$test->{path} =~ s/:id/$menu_id/g;
}
elsif ($test->{path} =~ /portal\/attachment\/:id/g) {
$test->{path} =~ s/:id/$attachment_id/g;
}
elsif ($test->{path} =~ /user\/portal\/news\/:id/g) {
$test->{path} =~ s/:id/$article_id/
}
}
# Обязательно: включаем тесты по списку
test_runner({
apiKey => $apiKey,
debug => $debug,
args => $ARGS
}, \@test_list);
# Завершаем тест с накопленными результатами
done_testing();
1;
|
Создание реквеста и json-schema
...
Code Block | ||||||
---|---|---|---|---|---|---|
| ||||||
{ "method": "GET", "name": "ADMIN_PORTAL_ARTICLES_LIST", "path": "portal/articles/" } |
...
Code Block | ||||||
---|---|---|---|---|---|---|
| ||||||
{ "$schema": "http://json-schema.org/draft-04/schema#", "type": "object", "properties": { "list": { "type": "array", "items": { "type": "object", "properties": { "id": { "type": "integer" }, "archive": { "type": "integer" }, "addressFlat": { "type": "string" }, "buildId": { "type": "number" }, "content": { "type": "string" }, "date": { "type": "string" }, "deeplink": { "type": "integer" }, "districtId": { "type": "string" }, "domainId": { "type": "integer" }, "endDate": { "type": "string" }, "etimestamp": { "type": "integer" }, "gid": { "type": "integer" }, "importance": { "type": "integer" }, "name": { "type": "string" }, "onMainPage": { "type": "integer" }, "permalink": { "type": "string" }, "picture": { "type": "string" }, "portalMenuId": { "type": "integer" }, "shortDescription": { "type": "string" }, "stName": { "type": "string" }, "status": { "type": "integer" }, "streetId": { "type": "integer" }, "tagName": { "type": "string" }, "tags": { "type": "integer" }, "title": { "type": "string" }, "url": { "type": "string" }, "utimestamp": { "type": "integer" } }, "required": [ "id", "archive", "addressFlat", "buildId", "content", "date", "deeplink", "districtId", "domainId", "endDate", "etimestamp", "gid", "importance", "name", "onMainPage", "permalink", "picture", "portalMenuId", "shortDescription", "stName", "status", "streetId", "tagName", "tags", "title", "url", "utimestamp" ] } }, "total": { "type": "number" } }, "required": [ "list", "total" ] } |
...
Code Block | ||||||
---|---|---|---|---|---|---|
| ||||||
{ "body": { "addressFlat": "4", "archive": 1, "buildId": 8, "content": "<p>Новая Open Source версия биллинга!</p>\n\n\n\n<p>Полный список новинок, исправлений и улучшений биллинга к новому релизу!</p>", "date": "2023-01-27", "districtId": 8, "domainId": 0, "endDate": "", "gid": 0, "importance": 0, "name": "Releases", "onMainPage": 0, "permalink": "releases-abills-095-blackout", "picture": "https://demo.abills.net.ua:9443/images/attach/portal/13863233.jpg", "portalMenuId": 8, "shortDescription": "Встречайте новый релиз 2023", "stName": "", "status": 1, "streetId": 0, "tagName": "", "tags": 0, "title": "Релиз ABillS 0.95 Blackout" }, "method": "PUT", "name": "ADMIN_PORTAL_ARTICLE_UPDATE", "path": "portal/articles/:id/" } |
...
Code Block | ||||||
---|---|---|---|---|---|---|
| ||||||
{ "$schema": "http://json-schema.org/draft-04/schema#", "type": "object", "properties": { "affected": { "type": "integer" }, "total": { "type": "integer" } }, "required": ["affected", "total"] } |
...
Code Block | ||||||
---|---|---|---|---|---|---|
| ||||||
{ "method": "DELETE", "name": "ADMIN_PORTAL_ARTICLE_DELETE", "path": "portal/articles/:id/" } |
...
Code Block | ||||||
---|---|---|---|---|---|---|
| ||||||
{ "$schema": "http://json-schema.org/draft-04/schema#", "type": "object", "properties": { "result": { "type": "string" } }, "required": [ "result" ] } |
Запуск теста
Для запуска теста на ваш модуль USER API - запускайте созданный вами Api.t, будет взяты данные автоматически с конфига.
Практики
...
Для запуска теста на ADMIN API - запускайте с параметром KEY=*ваш API_KEY*
Практики
- Чтобы не писать request.json руками - используйте ChatGPT - это сэкономит вам время.
Отправляйте ему OpenAPI вашего пути, и он сгенерирует нужный вам schema.json.
Это сэкономит вам время, и позволит избежать человеческого фактора ошибки.