umbot
Version:
Мультиплатформенный фреймворк для создания голосовых навыков и чат-ботов с единой бизнес-логикой. Встроенная поддержка ВКонтакте, Telegram, Viber, MAX, Яндекс Алисы, Маруси и Сбера SmartApp. Архитектура на адаптерах позволяет подключать любые другие платф
249 lines (248 loc) • 12.2 kB
JavaScript
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.Base = void 0;
const index_1 = require("../../../index");
const Base_1 = require("../../Base");
/**
* Базовый класс для создания адаптеров баз данных.
* При наследовании вы ОБЯЗАНЫ реализовать следующие методы:
* - _select - Получение данных
* - _insert - Добавление данных
* - _update - Обновление данных
* - _remove - Удаление данных
* - isConnected - Проверка не то, есть ли подключение к базе или нет
*
* Эти методы вызываются внутренней логикой фреймворка и определяют, как именно выполняются операции с вашей БД.
*
* Рекомендуется наследоваться от этого класса при создании собственного адаптера, так как он уже реализует общую логику: интеграцию с контекстом приложения, сбор метрик, управление соединениями.
*
* Если по техническим причинам наследование невозможно — ваш класс должен в точности реализовывать интерфейс `IDatabaseAdapter`
*
* ⚠️ Важно: в приложении может быть активен только один адаптер БД.
*/
class Base extends Base_1.BasePlugin {
/**
* Формат базы данных
*/
dbFormat;
/**
* Контекст приложения
* @protected
*/
_appContext = new index_1.AppContext(); // Для подстраховки, на случай если кто-то забудет прокинуть
/**
* Дополнительные опции для работы базы данных
*/
_dbOptions;
/**
* Конструктор для адаптера базы данных
* @param options Дополнительные опции для настройки БД.
* Тут можно передать настройки для подключения к базе, либо дополнительную информацию, необходимую для работы.
*/
constructor(options) {
super();
this.dbFormat = 'unknown';
this._dbOptions = options;
}
/**
* Метод инициализации адаптера.
* Вызывается один раз при подключении через `bot.use()`.
* В данном методе можно произвести дополнительную настройку для подключения к базе
* @param appContext Контекст приложения
*/
init(appContext) {
this._appContext = appContext;
if (appContext.database.adapter) {
appContext.database.adapter.destroy()?.catch((e) => {
appContext.logError(`Произошла ошибка при уничтожении предыдущего адаптера! Текст ошибки: ${e.message}`, {
error: e,
});
});
}
appContext.database = {
adapter: this,
databaseInfo: {},
};
}
/**
* Устанавливает подключение к базе данных.
* В случае успешного подключения возвращается true
*/
connect() {
return true;
}
/**
* Удобный метод для извлечения данных из результата запроса.
* Проверяет `res.status` и возвращает `res.data` только при успехе.
*
* Используется в моделях и контроллерах для безопасного доступа к данным.
* @param res Результат запроса. В случае успешного запроса вернутся данные, в противном случае null
*/
getValue(res) {
if (res?.status) {
return res.data;
}
return null;
}
/**
* Выполняет произвольный запрос через callback.
* Если вы планируете использовать `query()`, обязательно переопределите `_query`. По умолчанию он возвращает null и не выполняет никаких действий.
*
* @param _callback функция обработчик
*/
_query(_callback) {
return null;
}
/**
* Выполняет SELECT-запрос.
*
* Внутри себя вызывает this._select, основное отличие в том, что в данном методе пишутся метрики.
* @param selectData Дополнительная информация для запроса. Содержит информацию о таблице и структуре.
* @param where Сам запрос
* @param isOne Определяет нужно ли вернуть только 1 найденную запись, либо отдать все доступные данные.
*/
async select(selectData, where, isOne) {
if (!this._appContext?.usedMetric) {
return this._select(selectData, where, isOne);
}
const start = performance.now();
let res = this._select(selectData, where, isOne);
if ((0, index_1.isPromise)(res)) {
res = await res;
}
this._appContext?.logMetric(index_1.EMetric.DB_SELECT, performance.now() - start, {
tableName: selectData.tableName,
});
return res;
}
/**
* Выполняет INSERT-запрос.
*
* Внутри себя вызывает this._insert, основное отличие в том, что в данном методе пишутся метрики.
* @param insertData Дополнительная информация для запроса. Содержит сам запроса, а также название таблицы и прочие данные.
*/
async insert(insertData) {
if (!this._appContext?.usedMetric) {
return this._insert(insertData);
}
const start = performance.now();
let res = this._insert(insertData);
if ((0, index_1.isPromise)(res)) {
res = await res;
}
this._appContext?.logMetric(index_1.EMetric.DB_INSERT, performance.now() - start, {
tableName: insertData.tableName,
});
return res;
}
/**
* Выполняет UPDATE-запрос.
*
* Внутри себя вызывает this._update, основное отличие в том, что в данном методе пишутся метрики.
* @param updateData Дополнительная информация для запроса. Содержит сам запроса, а также название таблицы и прочие данные.
*/
async update(updateData) {
if (!this._appContext?.usedMetric) {
return this._update(updateData);
}
const start = performance.now();
let res = this._update(updateData);
if ((0, index_1.isPromise)(res)) {
res = await res;
}
this._appContext?.logMetric(index_1.EMetric.DB_UPDATE, performance.now() - start, {
tableName: updateData.tableName,
});
return res;
}
/**
* Выполняет DELETE-запрос.
*
* Внутри себя вызывает this._remove, основное отличие в том, что в данном методе пишутся метрики.
* @param removeData Дополнительная информация для запроса. Содержит сам запроса, а также название таблицы и прочие данные.
*/
async remove(removeData) {
if (!this._appContext?.usedMetric) {
return this._remove(removeData);
}
const start = performance.now();
let res = this._remove(removeData);
if ((0, index_1.isPromise)(res)) {
res = await res;
}
this._appContext?.logMetric(index_1.EMetric.DB_REMOVE, performance.now() - start, {
tableName: removeData.tableName,
});
return res;
}
/**
* Выполняет произвольный запрос через callback.
*
* Внутри себя вызывает this._query, основное отличие в том, что в данном методе пишутся метрики.
* @param callback функция обработчик
*/
query(callback) {
return this._query(callback);
}
/**
* Сохраняет запись: вставляет новую или обновляет существующую.
* Логика:
* - Если `isNew === true` → вызывается `insert`
* - Иначе → сначала делается `selectOne` по `saveData.query`,
* и если запись найдена — вызывается `update`, иначе — `insert`
* *
* @param saveData Данные для запроса. Включает как запроса, так и сами данные
* @param isNew Флаг, говорящий о том, что точно происходит добавление новой записи
*/
async save(saveData, isNew) {
if (isNew) {
saveData.data = { ...saveData.data, ...saveData.query };
return await this.insert(saveData);
}
const select = await this.selectOne(saveData, saveData.query);
if (select?.status) {
return await this.update(saveData);
}
else {
saveData.data = { ...saveData.data, ...saveData.query };
return await this.insert(saveData);
}
}
/**
* Выполняет SELECT с ограничением до одной записи.
* @param selectData Дополнительные данные для запроса
* @param where Сам запроса
*/
async selectOne(selectData, where) {
if (where) {
return this.select(selectData, where, true);
}
return null;
}
/**
* Экранирует строку для безопасного использования в запросах.
*
* ⚠️ По умолчанию просто приводит значение к строке.
* Если ваша БД требует экранирования (например, SQL), обязательно переопределите этот метод.
* @param str Экранируемый запрос
*/
escapeString(str) {
return str + '';
}
/**
* Вызывается при завершении работы приложения или замене адаптера.
* Используйте для закрытия соединений, сохранения данных и т.п.
*/
destroy() {
// TODO document why this method 'destroy' is empty
}
/**
* Вызывается при удалении модели или завершении сессии.
* Может использоваться для освобождения ресурсов, связанных с таблицей.
* @param _tableName Название таблицы, подключение к которой закрывается
*/
close(_tableName) {
// TODO document why this method 'close' is empty
}
}
exports.Base = Base;