Открыть меню
Платформа Эра. Документация
Toggle preferences menu
Открыть персональное меню
Вы не представились системе
Your IP address will be publicly visible if you make any edits.

Работа с данными платформы. API: различия между версиями

Материал из Платформа Эра. Документации
 
(не показано 7 промежуточных версий этого же участника)
Строка 22: Строка 22:


= Подписка на события класса =
= Подписка на события класса =
Для доступа к событиям классов используется коллекция объектов этих классов. Её название аналогично названию classMultiName.
В примере кода ниже, создается приватное свойство класса для хранения коллекции, затем в конструкторе эта коллекция инициализируется, и далее происходит бинд события на метод внутри класса сервиса.<syntaxhighlight lang="typescript">
import { GlobalUtils, Converter } from "./../../utils";
import { Service } from "./../../platform/core";
import { ITicket, ITickets } from "../model";
import { Tickets } from "../classes";
import { IBaseEntity, IDataUpdateParams } from "../../base/model";
class Server2Service extends Service {
    private _tickets: ITickets; //свойство класса, через которое мы будем доступаться к коллекции Tickets
   
    constructor() {
        super("creomate_tutorial.Server2Service");
       
        // onCreateCode
        this._tickets = new Tickets(this.context); //инициализируем коллекцию Tickets
        this._tickets.onAfterInsert(this.onTicketAfterInsert.bind(this)); //привязываем метод onTicketAfterInsert к событию onAfterInsert коллекции Tickets
        this.load();
    }
    onTicketAfterInsert(params_: IDataUpdateParams<ITicket>){
        this.log.info(params_.updateKind); //В этом свойстве хранится тип опецации (удаление, изменение, добавление)
        this.log.info(params_.id); //В этом свойстве хранится ID объекта, над которым была совершена операция
        this.log.info(params_.entity?.subject); //В свойстве entity хранятся данные объекта, в данном случае получаем поле subject из объекта класса
       
    }
}
export default Server2Service;
</syntaxhighlight>Данный пример кода будет выводить в лог-журнал информацию об операции, идентификаторе и теме обращения каждый раз при добавлении нового обращения.
Для каждого класса доступны следующие события для обработки в рамках сервиса:
* onAfterInsert - событие вызывается после добавления нового объекта класса
* onBeforeDelete - событие вызывается перед удалением объекта класса
* onAfterDelete - событие вызывается после удаления объекта класса
* onAfterModify - событие вызвается после модификации объекта класса
* onAfterReload - событие после перезагрузки класса из хранилища. Используется при потере актуальности кеша
* onAfterUpdate - универсальное событие после delete/modify/insert
В параметр функции, вызываемой каждым событием передается дженерик-параметр IDataUpdateParams, который может быть типизирован каким-либо интерфейсом.
Вариантом по-умолчанию можно считать IDataUpdateParams<IBaseEntity>, в таком случае в параметре невозможно будет получить значения свойств класса, вызвавшего событие. Будет доступен только его ID.
Однако, если использовать типизацию интерфейсом того класса, чьи изменения ожидаются, например - IDataUpdateParams<ITicket>, то через свойство '''entity''' моно получить доступ к свойствам экземпляра класса.
Также, в данном параметре передаются две коллекции - oldData и newData. В этих коллекциях можно получить изначальные и новые значения свойств объекта, при обработке событий изменения объекта.


= Получение объекта класса по ID =
= Получение объекта класса по ID =
Для получения объекта класса по его идентификатору, в коллекции присутствует два метода:
getByID - метод, позволяющий получить экземпляр класса по ID из кеша или из хранилища. Если экземпляр по ID не найден, вернет undefined
getbyIDStrong - метод, позволяющий получить экземпляр класса по ID из кеша или из хранилища. Если экземпляр по ID не найден, возникнет исключение
Оба метода являются асинхронными, соответственно, в отличии от предыдущего примера, метод класса, который их вызывает, также должен быть асинхронным. <syntaxhighlight lang="typescript">
import { GlobalUtils, Converter } from "./../../utils";
import { Service } from "./../../platform/core";
import { ITicket, ITickets } from "../model";
import { Tickets } from "../classes";
import { IBaseEntity, IDataUpdateParams } from "../../base/model";
class Server2Service extends Service {
    private _tickets: ITickets; //свойство класса, через которое мы будем доступаться к коллекции Tickets
   
    constructor() {
        super("creomate_tutorial.Server2Service");
       
        // onCreateCode
        this._tickets = new Tickets(this.context); //инициализируем коллекцию Tickets
     
        this.load();
    }
    async myServiceMethod(ticketId: string){
        const ticket = await this._tickets.getByID(ticketId);
        try{
            const ticketStrong = await this._tickets.getByIDStrong(ticketId);
        }catch(e){
            this.log.exception("myServiceMethod: get ticket strong", e);
        }
       
    }
}
export default Server2Service;
</syntaxhighlight>


= Поиск объекта класса =
= Поиск объекта класса =
Метод коллекции find позволяет осуществлять поиск по коллекции с помощью предиката.
В данном примере, по коллекции _tickets осуществляется поиск такого ticket, у котрого subject не заполнен.<syntaxhighlight lang="typescript">
import { GlobalUtils, Converter } from "./../../utils";
import { Service } from "./../../platform/core";
import { ITicket, ITickets } from "../model";
import { Tickets } from "../classes";
import { IBaseEntity, IDataUpdateParams } from "../../base/model";
class Server2Service extends Service {
    private _tickets: ITickets; //свойство класса, через которое мы будем доступаться к коллекции Tickets
   
    constructor() {
        super("creomate_tutorial.Server2Service");
       
        // onCreateCode
        this._tickets = new Tickets(this.context); //инициализируем коллекцию Tickets
     
        this.load();
    }
    myFindTicketMethod(ticketId: string){
        const ticket = this._tickets.find(item_ => item_.subject === "");
   
       
    }
}
export default Server2Service;
</syntaxhighlight>


= Создание объекта класса =
= Создание объекта класса =
Для создания нового объекта используется метод коллекции addNew.
Данный метод является асинхронным и создает новый объект класса, либо генерирует исключение.
<syntaxhighlight lang="typescript">
import { GlobalUtils, Converter } from "./../../utils";
import { Service } from "./../../platform/core";
import { ITicket, ITickets } from "../model";
import { Tickets } from "../classes";
import { IBaseEntity, IDataUpdateParams } from "../../base/model";
class Server2Service extends Service {
    private _tickets: ITickets; //свойство класса, через которое мы будем доступаться к коллекции Tickets
   
    constructor() {
        super("creomate_tutorial.Server2Service");
       
        // onCreateCode
        this._tickets = new Tickets(this.context); //инициализируем коллекцию Tickets
     
        this.load();
    }
    async createTicketMethod(subject:string, description:string){
        const newTicket = await this._tickets.addNew(ticket_=>{
            ticket_.subject = subject,
            ticket_.description = description
        })
       
    }
}
export default Server2Service;
</syntaxhighlight>


= Модификация объекта класса =
= Модификация объекта класса =
Для изменения объекта класса, необходимо каким-либо образом получить его экземпляр. Примеры возможных вариантов получения:
* Получение entity с помощью подписки на событие
* Получение entity по ID
* Получение entity с помощью поиска find
* Получение entity как параметра метода из другого метода
После получения entity, можно напрямую изменять его свойства. Для убоства конвертации данных, представлен класс Converter, содержащий основные методы конвертации данных для их представления пользователю.
После внесения изменений в свойства класса, необходимо применить их с помощью асинхронного метода applyUpdates().
<syntaxhighlight lang="typescript">
import { GlobalUtils, Converter } from "./../../utils";
import { Service } from "./../../platform/core";
import { ITicket, ITickets } from "../model";
import { Tickets } from "../classes";
import { IBaseEntity, IDataUpdateParams } from "../../base/model";
class Server2Service extends Service {
    private _tickets: ITickets; //свойство класса, через которое мы будем доступаться к коллекции Tickets
   
    constructor() {
        super("creomate_tutorial.Server2Service");
       
        // onCreateCode
        this._tickets = new Tickets(this.context); //инициализируем коллекцию Tickets
     
        this.load();
    }
    async modifyTicketDescription(id: string){
        try{
            const ticket = await this._tickets.getByIDStrong(id);
            ticket.description = ticket.description + "\n" + Converter.dateTimeToFullDateDisplay(GlobalUtils.nowDateTime());
            await ticket.applyUpdates();
        }catch(e){
            this.log.exception("modifyTicketDescription: get ticket by ID", e);
        }
       
    }
}
export default Server2Service;
</syntaxhighlight>


= Удаление объекта класса =
= Удаление объекта класса =


Для удаления объекта класса, его экземпляр необходимо также получить любым из способов.
Для удаления объекта класса используется асинхронный метод delete.
Использование метода может вызвать исключение, например если какие-либо проверки прерывают операцию удаления объекта.<syntaxhighlight lang="typescript">
import { GlobalUtils, Converter } from "./../../utils";
import { Service } from "./../../platform/core";
import { ITicket, ITickets } from "../model";
import { Tickets } from "../classes";
import { IBaseEntity, IDataUpdateParams } from "../../base/model";
class Server2Service extends Service {
    private _tickets: ITickets; //свойство класса, через которое мы будем доступаться к коллекции Tickets
   
    constructor() {
        super("creomate_tutorial.Server2Service");
       
        // onCreateCode
        this._tickets = new Tickets(this.context); //инициализируем коллекцию Tickets
     
        this.load();
    }
    async deleteTicketById(id: string){
        try{
            const ticket = await this._tickets.getByIDStrong(id);
            try{
                await ticket.delete();
            }catch(e){
                this.log.exception("deleteTicketById", e);
            }
        }catch(e){
            this.log.exception("deleteTicketById: get ticket by ID", e);
        }
       
    }
}
export default Server2Service;
</syntaxhighlight>
= Вызов сервисов платформы и работа с внутренним API =
Работу с сервисами платформы можно условно разделить на две категории:
* Работа с сервисами продуктового слоя - вызов сервисов, реализованных в сторонних пакетах
* Работа с платформенными сервисами - вызов базовых сервисов платформы
Полную документацию по работе с платформенными сервисами можно найти на [https://vendor.era-platform.ru/docs/era/latest/api/index.html портале Vendor].
Работа с сервисами продуктового слоя сводится к изучению их исходного кода (он выгружается в разделе Services). Техническая же сторона работы с вызовами описана в статье [[Разработка сервисов]]. 


  Следующая статья курса: [[Работа с внешними системами. Возможности интеграции]]
  Следующая статья курса: [[Работа с внешними системами. Возможности интеграции]]

Текущая версия от 13:20, 13 марта 2025

Предыдущая статья курса: Получение информации о пользователе

Общая информация

В рамках разработки сервисов, одной из ключевых задач является работа с данными платформы.

Основной способ работы с данными классов в хранилище - это использование классов-обёрток. Такие классы автоматически генерируются платформой на основании настроек классов в пакетах. Эти классы выгружаются вместе с исходными кодами сервисов и могут использоваться в коде сервисов для быстрого и удобного взаимодействия с данными.

С помощью классов-обёрток возможны следующие действия с данными:

  • Коллекция (classMultiName)
    • Подписка на события объекта класса (Insert, Update, Delete)
    • Получение объекта класса по ID
    • Поиск объекта класса
  • Объект класса (className)
    • Создание
    • Модификация
    • Удаление
Приложение Объектная модель

Рассмотрим каждое возможное действие более детально и с примерами.

Так же, для большего удоства и ориентирования по базовым классам платформы и других пакетов, удобно использовать приложение Объектная модель, которое позволяет быстро посмотреть список полей классов во всех пакетах, а также значения перечислений.

Если при работе в IDE, например Visual Studio Code, autocompletion не предлагает для выбора классы-обертки из каких либо пакетов, рекомендуется открыть в IDE index.ts файл этого пакета. В этом случае, IDE дозагрузит необходимые данные классов и они начнут появляться.

Подписка на события класса

Для доступа к событиям классов используется коллекция объектов этих классов. Её название аналогично названию classMultiName.

В примере кода ниже, создается приватное свойство класса для хранения коллекции, затем в конструкторе эта коллекция инициализируется, и далее происходит бинд события на метод внутри класса сервиса.

import { GlobalUtils, Converter } from "./../../utils";
import { Service } from "./../../platform/core";
import { ITicket, ITickets } from "../model";
import { Tickets } from "../classes";
import { IBaseEntity, IDataUpdateParams } from "../../base/model";

class Server2Service extends Service {
    private _tickets: ITickets; //свойство класса, через которое мы будем доступаться к коллекции Tickets
    
    constructor() {
        super("creomate_tutorial.Server2Service");
        
        // onCreateCode
        this._tickets = new Tickets(this.context); //инициализируем коллекцию Tickets
        this._tickets.onAfterInsert(this.onTicketAfterInsert.bind(this)); //привязываем метод onTicketAfterInsert к событию onAfterInsert коллекции Tickets
        this.load();
    } 
    onTicketAfterInsert(params_: IDataUpdateParams<ITicket>){
        this.log.info(params_.updateKind); //В этом свойстве хранится тип опецации (удаление, изменение, добавление)
        this.log.info(params_.id); //В этом свойстве хранится ID объекта, над которым была совершена операция
        this.log.info(params_.entity?.subject); //В свойстве entity хранятся данные объекта, в данном случае получаем поле subject из объекта класса

        
    }
}
export default Server2Service;

Данный пример кода будет выводить в лог-журнал информацию об операции, идентификаторе и теме обращения каждый раз при добавлении нового обращения.

Для каждого класса доступны следующие события для обработки в рамках сервиса:

  • onAfterInsert - событие вызывается после добавления нового объекта класса
  • onBeforeDelete - событие вызывается перед удалением объекта класса
  • onAfterDelete - событие вызывается после удаления объекта класса
  • onAfterModify - событие вызвается после модификации объекта класса
  • onAfterReload - событие после перезагрузки класса из хранилища. Используется при потере актуальности кеша
  • onAfterUpdate - универсальное событие после delete/modify/insert

В параметр функции, вызываемой каждым событием передается дженерик-параметр IDataUpdateParams, который может быть типизирован каким-либо интерфейсом.

Вариантом по-умолчанию можно считать IDataUpdateParams<IBaseEntity>, в таком случае в параметре невозможно будет получить значения свойств класса, вызвавшего событие. Будет доступен только его ID.

Однако, если использовать типизацию интерфейсом того класса, чьи изменения ожидаются, например - IDataUpdateParams<ITicket>, то через свойство entity моно получить доступ к свойствам экземпляра класса.

Также, в данном параметре передаются две коллекции - oldData и newData. В этих коллекциях можно получить изначальные и новые значения свойств объекта, при обработке событий изменения объекта.


Получение объекта класса по ID

Для получения объекта класса по его идентификатору, в коллекции присутствует два метода:

getByID - метод, позволяющий получить экземпляр класса по ID из кеша или из хранилища. Если экземпляр по ID не найден, вернет undefined

getbyIDStrong - метод, позволяющий получить экземпляр класса по ID из кеша или из хранилища. Если экземпляр по ID не найден, возникнет исключение

Оба метода являются асинхронными, соответственно, в отличии от предыдущего примера, метод класса, который их вызывает, также должен быть асинхронным.

import { GlobalUtils, Converter } from "./../../utils";
import { Service } from "./../../platform/core";
import { ITicket, ITickets } from "../model";
import { Tickets } from "../classes";
import { IBaseEntity, IDataUpdateParams } from "../../base/model";

class Server2Service extends Service {
    private _tickets: ITickets; //свойство класса, через которое мы будем доступаться к коллекции Tickets
    
    constructor() {
        super("creomate_tutorial.Server2Service");
        
        // onCreateCode
        this._tickets = new Tickets(this.context); //инициализируем коллекцию Tickets
       
        this.load();
    } 
    async myServiceMethod(ticketId: string){

        const ticket = await this._tickets.getByID(ticketId);
        try{
            const ticketStrong = await this._tickets.getByIDStrong(ticketId);
        }catch(e){

            this.log.exception("myServiceMethod: get ticket strong", e);

        }
        
    }
}
export default Server2Service;

Поиск объекта класса

Метод коллекции find позволяет осуществлять поиск по коллекции с помощью предиката.

В данном примере, по коллекции _tickets осуществляется поиск такого ticket, у котрого subject не заполнен.

import { GlobalUtils, Converter } from "./../../utils";
import { Service } from "./../../platform/core";
import { ITicket, ITickets } from "../model";
import { Tickets } from "../classes";
import { IBaseEntity, IDataUpdateParams } from "../../base/model";

class Server2Service extends Service {
    private _tickets: ITickets; //свойство класса, через которое мы будем доступаться к коллекции Tickets
    
    constructor() {
        super("creomate_tutorial.Server2Service");
        
        // onCreateCode
        this._tickets = new Tickets(this.context); //инициализируем коллекцию Tickets
       
        this.load();
    } 
    myFindTicketMethod(ticketId: string){

        const ticket = this._tickets.find(item_ => item_.subject === "");
    
        
    }
}
export default Server2Service;

Создание объекта класса

Для создания нового объекта используется метод коллекции addNew.

Данный метод является асинхронным и создает новый объект класса, либо генерирует исключение.

import { GlobalUtils, Converter } from "./../../utils";
import { Service } from "./../../platform/core";
import { ITicket, ITickets } from "../model";
import { Tickets } from "../classes";
import { IBaseEntity, IDataUpdateParams } from "../../base/model";

class Server2Service extends Service {
    private _tickets: ITickets; //свойство класса, через которое мы будем доступаться к коллекции Tickets
    
    constructor() {
        super("creomate_tutorial.Server2Service");
        
        // onCreateCode
        this._tickets = new Tickets(this.context); //инициализируем коллекцию Tickets
       
        this.load();
    } 
    async createTicketMethod(subject:string, description:string){

        const newTicket = await this._tickets.addNew(ticket_=>{
            ticket_.subject = subject,
            ticket_.description = description
        })
        
    }
}
export default Server2Service;

Модификация объекта класса

Для изменения объекта класса, необходимо каким-либо образом получить его экземпляр. Примеры возможных вариантов получения:

  • Получение entity с помощью подписки на событие
  • Получение entity по ID
  • Получение entity с помощью поиска find
  • Получение entity как параметра метода из другого метода

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

После внесения изменений в свойства класса, необходимо применить их с помощью асинхронного метода applyUpdates().

import { GlobalUtils, Converter } from "./../../utils";
import { Service } from "./../../platform/core";
import { ITicket, ITickets } from "../model";
import { Tickets } from "../classes";
import { IBaseEntity, IDataUpdateParams } from "../../base/model";

class Server2Service extends Service {
    private _tickets: ITickets; //свойство класса, через которое мы будем доступаться к коллекции Tickets
    
    constructor() {
        super("creomate_tutorial.Server2Service");
        
        // onCreateCode
        this._tickets = new Tickets(this.context); //инициализируем коллекцию Tickets
       
        this.load();
    } 
    async modifyTicketDescription(id: string){
        try{
            const ticket = await this._tickets.getByIDStrong(id);
            ticket.description = ticket.description + "\n" + Converter.dateTimeToFullDateDisplay(GlobalUtils.nowDateTime());
            await ticket.applyUpdates();
        }catch(e){

            this.log.exception("modifyTicketDescription: get ticket by ID", e);

        }
        
    }
}
export default Server2Service;

Удаление объекта класса

Для удаления объекта класса, его экземпляр необходимо также получить любым из способов.

Для удаления объекта класса используется асинхронный метод delete.

Использование метода может вызвать исключение, например если какие-либо проверки прерывают операцию удаления объекта.

import { GlobalUtils, Converter } from "./../../utils";
import { Service } from "./../../platform/core";
import { ITicket, ITickets } from "../model";
import { Tickets } from "../classes";
import { IBaseEntity, IDataUpdateParams } from "../../base/model";

class Server2Service extends Service {
    private _tickets: ITickets; //свойство класса, через которое мы будем доступаться к коллекции Tickets
    
    constructor() {
        super("creomate_tutorial.Server2Service");
        
        // onCreateCode
        this._tickets = new Tickets(this.context); //инициализируем коллекцию Tickets
       
        this.load();
    } 
    async deleteTicketById(id: string){
        try{
            const ticket = await this._tickets.getByIDStrong(id);
            try{
                await ticket.delete();
            }catch(e){

                this.log.exception("deleteTicketById", e);

            }
        }catch(e){

            this.log.exception("deleteTicketById: get ticket by ID", e);

        }
        
    }
}
export default Server2Service;

Вызов сервисов платформы и работа с внутренним API

Работу с сервисами платформы можно условно разделить на две категории:

  • Работа с сервисами продуктового слоя - вызов сервисов, реализованных в сторонних пакетах
  • Работа с платформенными сервисами - вызов базовых сервисов платформы

Полную документацию по работе с платформенными сервисами можно найти на портале Vendor.

Работа с сервисами продуктового слоя сводится к изучению их исходного кода (он выгружается в разделе Services). Техническая же сторона работы с вызовами описана в статье Разработка сервисов.

Следующая статья курса: Работа с внешними системами. Возможности интеграции
Предыдущая статья курса: Получение информации о пользователе