UNPKG

umbot

Version:

Мультиплатформенный фреймворк для создания голосовых навыков и чат-ботов с единой бизнес-логикой. Встроенная поддержка ВКонтакте, Telegram, Viber, MAX, Яндекс Алисы, Маруси и Сбера SmartApp. Архитектура на адаптерах позволяет подключать любые другие платф

249 lines (248 loc) 12.2 kB
"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;