Child pages
  • Туториал по написанию API для модуля

Versions Compared

Key

  • This line was added.
  • This line was removed.
  • Formatting was changed.

...

Code Block
languageperl
titleсловарь ошибок Portal::Errors
# Пэкэдж должен ОБЯЗАТЕЛЬНО иметь название *модуль*::Errors
# Это позволяет библиотеке словаря ошибок его же найти

package Portal::Errors;

=head1 NAME

  Portal::Errors
  # Крайне рекомендуем указывать в pod какой префикс ошибок у модуля.
  # Это просто поможет в поддержке, понимать какой префикс у какого модуля.
  IDS: 144*

=cut

use strict;
use warnings FATAL => 'all';

#**********************************************************
=head2 errors() - errors list

=cut
#**********************************************************
# Создаём функцию словаря ошибок с анонимным хэшэм.
# В будущем он будет заполнен парами *errno* => *errstr* 
sub errors {
  return {
    # Семизначный код ошибки, с приставкой 144 (модуль Portal) => ключ в словаре ошибки
    # ПримерПримеры:
    # 1440001 => 'ERR_PORTAL_NO_SENDER',
    1440002 => 'ERR_PORTAL_NO_ARTICLE',
  };
}

1;

Валидатор

Документация по валидатору
Позволяет проверять полученный реквест от клиента по схеме.
Избегает вызова пути с неправильными параметрами, что повышает устойчивость системы и возможных ошибок.
Также позволяет чётко и подробно расписывать какие именно параметры отсутствуют или неправильные в целом.

Создаём корневой файл Validations.pm с таким примерным содержанием:

И не забудьте заполнить lng_english.pl

Code Block
languageperl
titleсхемы валидатора Portal::Validations
# Называем пэкэдж *модуль*::Validations
package Portal::Validations;

use strict;
use warnings FATAL => 'all';

# Вызываем специальный модуль экспортера, для удобного экспортирования констант с описанием в IDE
use Exporter;
use parent# Заполняем ключи ошибок, можем даже вставлять переменные
$lang{ERR_PORTAL_NO_SENDER} = 'No sender with id %ID%';
$lang{ERR_PORTAL_NO_ARTICLE} = 'No article with id %ID%';


Валидатор

Документация по валидатору
Позволяет проверять полученный реквест от клиента по схеме.
Избегает вызова пути с неправильными параметрами, что повышает устойчивость системы и возможных ошибок.
Также позволяет чётко и подробно расписывать какие именно параметры отсутствуют или неправильные в целом.

Создаём корневой файл Validations.pm с таким примерным содержанием:

Code Block
languageperl
titleсхемы валидатора Portal::Validations
# Называем пэкэдж *модуль*::Validations
package Portal::Validations;

use strict;
use warnings FATAL => 'all';

# Вызываем специальный модуль экспортера, для удобного экспортирования констант с описанием в IDE
use Exporter;
use parent 'Exporter';

# Записываем константы валидации в экспорт
our @EXPORT = qw(
  POST_PORTAL_ARTICLES
);

# Записываем константы валидации в экспорт
our @EXPORT_OK = qw(
  POST_PORTAL_ARTICLES
);

use constant {
  # Называем константу *МЕТОД*_ПУТЬ_
  # А подробнее про валидатор можно узнать ссылкой выше.
  POST_PORTAL_ARTICLES   => {
    TITLE             => {
      required   => 1,
      type       => 'string',
      min_length => 5,
      max_length => 255
    },
    DATE              => {
      required => 1,
      type     => 'date'
    },
    PORTAL_MENU_ID    => {
      required => 1,
      type     => 'string'
    },
    SHORT_DESCRIPTION => {
      type       => 'string',
      max_length => 600
    },
    CONTENT           => {
      type => 'string',
    }
  },
};

1;

...

Code Block
languageperl
#**********************************************************
=head2 delete_portal_articles_id($path_params, $query_params)

  Endpoint DELETE /portal/articles/:id/

=cut
#**********************************************************
sub delete_portal_articles_id {
  my $self = shift;
  my ($path_params, $query_params) = @_;

  my $list = $Portal->portal_articles_list({ ID => $path_params->{id}, COLS_NAME => 1 });

  if (!($list && scalar(@$list))) {
    # Не забудьте написать этот код ошибки в словаре!
    return $Errors->throw_error(1440002, { lang_vars => { ID => $path_params->{id} }});
  }

  my $result = $Portal->portal_article_del({ ID => $path_params->{id} });
  if (!$Portal->{errno}) {
    $Attachments->delete_attachment($path_params->{id});
  }

  return $result;
}

...

Code Block
languageperl
 
#**********************************************************
=head2 get_user_portal_news($path_params, $query_params)

  Endpoint GET /user/portal/news

=cut
#**********************************************************
sub get_user_portal_news {
  my $self = shift;
  my ($path_params, $query_params) = @_;

  # Не обязательно писать всю логику прямо внутри эндпоинта, как в примере с ADMIN API
  # Вы можете делить логику в бизнес-функции, для сокращения использования.
  # Но для простоты понимания, с самого начала лучше писать всё в эндпоинтах
  
  return $self->_portal_menu({
    # Если пользователь авторизован - в $path_params->{uid} будет UID пользователя.
    # Если нет - поле будет пустое.
    UID       => $path_params->{uid} || '',
    DOMAIN_ID => $query_params->{DOMAIN_ID},
    PORTAL_MENU_ID => $query_params->{PORTAL_MENU_ID},
    MAIN_PAGE => $query_params->{MAIN_PAGE},
    LIST      => 1
  });
} 


Практики

Поиск, сортировка, пагинация

Для правильной работы сортировки мы рекомендуем делать это не вручну, а с помощью search_former которые находятся внутри любого современного модуля.
Он выглядит приблизительно так, с точки зрения пути GET /portal/menus

Code Block
languageperl
#**********************************************************
=head2 get_portal_menus($path_params, $query_params)

  Endpoint GET /portal/menus

=cut
#**********************************************************
sub get_portal_menus {
  my $self = shift;
  my ($path_params, $query_params) = @_;

  # Проверяем, определяем стандартные параметры для вызова
  my %PARAMS = (
    COLS_NAME => 1,
    PAGE_ROWS => $query_params->{PAGE_ROWS} ? $query_params->{PAGE_ROWS} : 25,
    SORT      => $query_params->{SORT} ? $query_params->{SORT} : 1,
    PG        => $query_params->{PG} ? $query_params->{PG} : 0,
    DESC      => $query_params->{DESC},
  );

  foreach my $param (keys %{$query_params}) {
    $query_params->{$param} = ($query_params->{$param} || "$query_params->{$param}" eq '0')
      ? $query_params->{$param}
      : '_SHOW';
  }

  my $list = $Portal->portal_menu_list({
    ID => '_SHOW',
    NAME => '_SHOW',
    URL => '_SHOW',
    DATE => '_SHOW',
    STATUS => '_SHOW',
    # С помощью внутренней деструктуризации присваиваем
    %$query_params,
    # Оверрайдим параметры
    %PARAMS,
  });

  return {
    list  => $list,
    total => $Portal->{TOTAL}
  };
}

И смотрим что происходит под капотом у portal_menu_list

Code Block
languageperl
#**********************************************************
=head2 function portal_menu_list() - get menu section list

  Arguments:
    $attr

  Returns:
    \@list -
  Examples:
    my $list = $Portal->portal_menu_list({COLS_NAME=>1});

=cut
#**********************************************************
sub portal_menu_list {
  my $self = shift;
  my ($attr) = @_;

  # Преопределяем параметры, если их нет
  my $SORT = ($attr->{SORT}) ? $attr->{SORT} : 1;
  my $DESC = ($attr->{DESC}) ? $attr->{DESC} : '';
  my $PG = $attr->{PG} ? $attr->{PG} : 0;
  my $PAGE_ROWS = $attr->{PAGE_ROWS} ? $attr->{PAGE_ROWS} : 25;

  # Эта функция на основе пришедших параметров и паттерном формирует $WHERE clause.
  my $WHERE = $self->search_former($attr, [
    [ 'ID',       'INT',   'pm.id',                   1 ],
    [ 'NAME',     'STR',   'pm.name',                 1 ],
    [ 'URL',      'STR',   'pm.url',                  1 ],
    [ 'DATE',     'STR',   'DATE(pm.date) as date',   1 ],
    [ 'STATUS',   'INT',   'pm.status',               1 ],
  ], { WHERE => 1 });

  $self->query(
    "SELECT $self->{SEARCH_FIELDS} pm.id
      FROM portal_menu pm
      $WHERE
      ORDER BY $SORT $DESC LIMIT $PG, $PAGE_ROWS;;",
    undef, $attr
  );

  my $list = $self->{list} || [];

  # Берём общий count
  $self->query("SELECT COUNT(*) AS total FROM portal_menu pm
    $WHERE;",
    undef,
    { INFO => 1 }
  );

  return $list || [];
}

И вот как раз вы можете в $query_params при вызове делать условный /portal/menu/?url=call*&name=test, и оно соответственно создаст выражения для поиска.
Полностью автоматически!

А насчёт сортирования, есть специальные параметры:

SORT - параметр для сортировки
DESC - по возрастанию или убыванию

А пагинации и лимита:
PG - какая страница
PAGE_ROWS - лимит с таблицы

Это позволяет формировать единые экспрессии по всей системе, без ручного определения.