ЦИТК-901 (Добавление поддержки протоколов MQTT и KAFKA)

This commit is contained in:
Dollerino 2024-09-19 15:12:44 +03:00
parent a09167055d
commit 59893f0458
12 changed files with 1724 additions and 137 deletions

View File

@ -71,6 +71,34 @@ let inComing = {
nPoolIncrement: 0 nPoolIncrement: 0
}; };
//Параметры подключения к Kafka
let kafkaConnection = {
//ID клиента-отправителя
sClientIdSender: "Parus",
//ID клиента-получателя
sClientIdRecipient: "Parus",
//Время ожидания успешного подключения (мс)
nConnectionTimeout: 5000,
//Необходимость попытки переподключения при потере соединения
bRestartOnFailure: false,
//Время максимального ожидания между попытками переподключения (мс)
nMaxRetryTime: 20000,
//Время ожидания между попытками переподключения (мс)
nInitialRetryTime: 10000
};
//Параметры подключения по MQTT протоколу
let mqttConnection = {
//ID клиента-отправителя
sClientIdSender: "Parus",
//ID клиента-получателя (если равен sClientIdSender, то отправленные сообщения будут игнорироваться)
sClientIdRecipient: "ParusRecipient",
//Время ожидания успешного подключения (мс)
nConnectTimeout: 5000,
//Время ожидания между попытками переподключения (мс)
nReconnectPeriod: 10000
};
//Параметры отправки E-Mail уведомлений //Параметры отправки E-Mail уведомлений
let mail = { let mail = {
//Адреc сервера SMTP //Адреc сервера SMTP
@ -98,5 +126,7 @@ module.exports = {
dbConnect, dbConnect,
outGoing, outGoing,
inComing, inComing,
kafkaConnection,
mqttConnection,
mail mail
}; };

View File

@ -71,6 +71,34 @@ let inComing = {
nPoolIncrement: 0 nPoolIncrement: 0
}; };
//Параметры подключения к Kafka
let kafkaConnection = {
//ID клиента-отправителя
sClientIdSender: "Parus",
//ID клиента-получателя
sClientIdRecipient: "Parus",
//Время ожидания успешного подключения (мс)
nConnectionTimeout: 5000,
//Необходимость попытки переподключения при потере соединения
bRestartOnFailure: false,
//Время максимального ожидания между попытками переподключения (мс)
nMaxRetryTime: 20000,
//Время ожидания между попытками переподключения (мс)
nInitialRetryTime: 10000
};
//Параметры подключения по MQTT протоколу
let mqttConnection = {
//ID клиента-отправителя
sClientIdSender: "Parus",
//ID клиента-получателя (если равен sClientIdSender, то отправленные сообщения будут игнорироваться)
sClientIdRecipient: "ParusRecipient",
//Время ожидания успешного подключения (мс)
nConnectTimeout: 5000,
//Время ожидания между попытками переподключения (мс)
nReconnectPeriod: 10000
};
//Параметры отправки E-Mail уведомлений //Параметры отправки E-Mail уведомлений
let mail = { let mail = {
//Адреc сервера SMTP //Адреc сервера SMTP
@ -98,5 +126,7 @@ module.exports = {
dbConnect, dbConnect,
outGoing, outGoing,
inComing, inComing,
kafkaConnection,
mqttConnection,
mail mail
}; };

View File

@ -196,9 +196,7 @@ class ParusAppServer {
//Если настройки верны - будем стартовать //Если настройки верны - будем стартовать
if (!sCheckResult) { if (!sCheckResult) {
//Протоколируем версию и релиз //Протоколируем версию и релиз
await this.logger.info( await this.logger.info(`Версия сервера приложений: ${prms.config.common.sVersion}, релиз: ${prms.config.common.sRelease}`);
`Версия сервера приложений: ${prms.config.common.sVersion}, релиз: ${prms.config.common.sRelease}`
);
//Создаём подключение к БД //Создаём подключение к БД
this.dbConn = new db.DBConnector({ this.dbConn = new db.DBConnector({
connectSettings: { connectSettings: {
@ -219,7 +217,9 @@ class ParusAppServer {
dbConn: this.dbConn, dbConn: this.dbConn,
logger: this.logger, logger: this.logger,
notifier: this.notifier, notifier: this.notifier,
sProxy: prms.config.outGoing.sProxy sProxy: prms.config.outGoing.sProxy,
kafkaConnectionPrms: prms.config.kafkaConnection,
mqttConnectionPrms: prms.config.mqttConnection
}); });
//Создаём обработчик очереди входящих //Создаём обработчик очереди входящих
this.inQ = new iq.InQueue({ this.inQ = new iq.InQueue({
@ -227,7 +227,9 @@ class ParusAppServer {
inComing: prms.config.inComing, inComing: prms.config.inComing,
dbConn: this.dbConn, dbConn: this.dbConn,
logger: this.logger, logger: this.logger,
notifier: this.notifier notifier: this.notifier,
kafkaConnectionPrms: prms.config.kafkaConnection,
mqttConnectionPrms: prms.config.mqttConnection
}); });
//Создаём контроллер доступности удалённых сервисов //Создаём контроллер доступности удалённых сервисов
this.srvAvlCtrl = new sac.ServiceAvailableController({ this.srvAvlCtrl = new sac.ServiceAvailableController({

View File

@ -13,7 +13,16 @@ const express = require("express"); //WEB-сервер Express
const cors = require("cors"); //Управление заголовками безопасности для WEB-сервера Express const cors = require("cors"); //Управление заголовками безопасности для WEB-сервера Express
const bodyParser = require("body-parser"); //Модуль для Express (разбор тела входящего запроса) const bodyParser = require("body-parser"); //Модуль для Express (разбор тела входящего запроса)
const { ServerError } = require("./server_errors"); //Типовая ошибка const { ServerError } = require("./server_errors"); //Типовая ошибка
const { makeErrorText, validateObject, buildURL, getAppSrvFunction, buildOptionsXML, parseOptionsXML, deepMerge } = require("./utils"); //Вспомогательные функции const {
makeErrorText,
validateObject,
buildURL,
getAppSrvFunction,
buildOptionsXML,
parseOptionsXML,
deepMerge,
getKafkaBroker
} = require("./utils"); //Вспомогательные функции
const { NINC_EXEC_CNT_YES, NIS_ORIGINAL_NO, NIS_ORIGINAL_YES } = require("../models/prms_db_connector"); //Схемы валидации параметров функций модуля взаимодействия с БД const { NINC_EXEC_CNT_YES, NIS_ORIGINAL_NO, NIS_ORIGINAL_YES } = require("../models/prms_db_connector"); //Схемы валидации параметров функций модуля взаимодействия с БД
const objInQueueSchema = require("../models/obj_in_queue"); //Схема валидации сообщений обмена с бработчиком очереди входящих сообщений const objInQueueSchema = require("../models/obj_in_queue"); //Схема валидации сообщений обмена с бработчиком очереди входящих сообщений
const objServiceSchema = require("../models/obj_service"); //Схемы валидации сервиса const objServiceSchema = require("../models/obj_service"); //Схемы валидации сервиса
@ -28,6 +37,8 @@ const {
SERR_DB_SERVER, SERR_DB_SERVER,
SERR_UNAUTH SERR_UNAUTH
} = require("./constants"); //Общесистемные константы } = require("./constants"); //Общесистемные константы
const { subscribeMQTT } = require("./mqtt_connector"); //Модуль для работы с MQTT
const { subscribeKafka } = require("./kafka_connector"); //Модуль для работы с Kafka
//-------------------------- //--------------------------
// Глобальные идентификаторы // Глобальные идентификаторы
@ -71,6 +82,13 @@ class InQueue extends EventEmitter {
this.webApp.options("*", cors()); this.webApp.options("*", cors());
//WEB-сервер //WEB-сервер
this.srv = null; this.srv = null;
//Параметры подключения к Kafka
this.kafkaConnectionPrms = _.cloneDeep(prms.kafkaConnectionPrms);
//Параметры подключения к MQTT
this.mqttConnectionPrms = _.cloneDeep(prms.mqttConnectionPrms);
//Внешние подключения
this.kafkaConnections = [];
this.mqttConnections = [];
} else { } else {
throw new ServerError(SERR_OBJECT_BAD_INTERFACE, sCheckResult); throw new ServerError(SERR_OBJECT_BAD_INTERFACE, sCheckResult);
} }
@ -360,6 +378,326 @@ class InQueue extends EventEmitter {
throw new ServerError(SERR_OBJECT_BAD_INTERFACE, sCheckResult); throw new ServerError(SERR_OBJECT_BAD_INTERFACE, sCheckResult);
} }
} }
//Обработка сообщения kafka
async processKafkaMessage({ message, service, fn }) {
//Буфер для сообщения очереди
let q = null;
try {
//Тело сообщения и ответ на него
let blMsg = null;
let blResp = null;
//Параметры сообщения и ответа на него
let options = {};
let optionsResp = {};
//Флаг прекращения обработки сообщения
let bStopPropagation = false;
//Получим тело сообщения
blMsg = message.value ? message.value : null;
//Определимся с параметрами сообщения полученными от внешней системы
options = {
method: fn.sFnPrmsType,
headers: _.cloneDeep(message.headers)
};
//Кладём сообщение в очередь
q = await this.dbConn.putQueue({
nServiceFnId: fn.nId,
sOptions: buildOptionsXML({ options }),
blMsg
});
//Скажем что пришло новое входящее сообщение
await this.logger.info(
`Новое входящее Kafka-сообщение для функции ${fn.sCode} (${buildURL({
sSrvRoot: service.sSrvRoot,
sFnURL: fn.sFnURL
})})`,
{ nQueueId: q.nId }
);
//Вызываем обработчик со стороны БД (если он есть)
if (fn.sPrcResp) {
//Фиксируем начало исполнения сервером БД - в статусе сообщения
q = await this.dbConn.setQueueState({
nQueueId: q.nId,
nExecState: objQueueSchema.NQUEUE_EXEC_STATE_DB
});
//Вызов обработчика БД
let prcRes = await this.dbConn.execQueueDBPrc({ nQueueId: q.nId });
//Если результат - ошибка пробрасываем её
if (prcRes.sResult == objQueueSchema.SPRC_RESP_RESULT_ERR) throw new ServerError(SERR_DB_SERVER, prcRes.sMsg);
//Если результат - ошибка аутентификации, то и её пробрасываем, но с правильным кодом
if (prcRes.sResult == objQueueSchema.SPRC_RESP_RESULT_UNAUTH) throw new ServerError(SERR_UNAUTH, prcRes.sMsg || "Нет аутентификации");
//Выставим статус сообщению очереди - исполнено обработчиком БД
q = await this.dbConn.setQueueState({
nQueueId: q.nId,
nExecState: objQueueSchema.NQUEUE_EXEC_STATE_DB_OK
});
//Считаем ответ полученный от системы
let qData = await this.dbConn.getQueueResp({ nQueueId: q.nId });
blResp = qData.blResp;
}
//Выполняем обработчик "После" (если он есть)
if (bStopPropagation === false && fn.sAppSrvAfter) {
//Выставим статус сообщению очереди - исполняется сервером приложений
q = await this.dbConn.setQueueState({
nQueueId: q.nId,
nExecState: objQueueSchema.NQUEUE_EXEC_STATE_APP
});
//Выполняем
const fnAfter = getAppSrvFunction(fn.sAppSrvAfter);
let resAfter = null;
try {
let resAfterPrms = { res: { body: blMsg }, service: service, function: fn };
resAfterPrms.queue = _.cloneDeep(q);
resAfterPrms.queue.blMsg = blMsg;
resAfterPrms.queue.blResp = blResp;
resAfterPrms.options = _.cloneDeep(options);
resAfterPrms.optionsResp = _.cloneDeep(optionsResp);
resAfterPrms.dbConn = this.dbConn;
resAfterPrms.notifier = this.notifier;
resAfter = await fnAfter(resAfterPrms);
} catch (e) {
throw new ServerError(SERR_APP_SERVER_AFTER, e.message);
}
//Проверяем структуру ответа функции предобработки
if (resAfter) {
let sCheckResult = validateObject(
resAfter,
objInQueueSchema.InQueueProcessorFnAfter,
"Результат функции постобработки входящего сообщения"
);
//Если структура ответа в норме
if (!sCheckResult) {
//Выставим статус сообщению очереди - исполнено сервером приложений
q = await this.dbConn.setQueueState({
nQueueId: q.nId,
nExecState: objQueueSchema.NQUEUE_EXEC_STATE_APP_OK
});
//Фиксируем результат исполнения "После" - ответ системы
if (!_.isUndefined(resAfter.blResp)) {
blResp = resAfter.blResp;
q = await this.dbConn.setQueueResp({
nQueueId: q.nId,
blResp,
nIsOriginal: NIS_ORIGINAL_NO
});
}
//Фиксируем результат исполнения "После" - параметры ответа на запрос
if (!_.isUndefined(resAfter.optionsResp)) {
optionsResp = deepMerge(optionsResp, resAfter.optionsResp);
let sOptionsResp = buildOptionsXML({ options: optionsResp });
q = await this.dbConn.setQueueOptionsResp({ nQueueId: q.nId, sOptionsResp });
}
//Если пришел флаг ошибочной аутентификации и он положительный - то это ошибка, дальше ничего не делаем
if (!_.isUndefined(resAfter.bUnAuth)) if (resAfter.bUnAuth === true) throw new ServerError(SERR_UNAUTH, "Нет аутентификации");
} else {
//Или расскажем об ошибке
throw new ServerError(SERR_OBJECT_BAD_INTERFACE, sCheckResult);
}
}
}
//Фиксируем успех обработки - в протоколе работы сервиса
await this.logger.info(`Входящее сообщение ${q.nId} успешно отработано`, { nQueueId: q.nId });
//Фиксируем успех обработки - в статусе сообщения
q = await this.dbConn.setQueueState({
nQueueId: q.nId,
nIncExecCnt: NINC_EXEC_CNT_YES,
nExecState: objQueueSchema.NQUEUE_EXEC_STATE_OK
});
} catch (e) {
//Тема и текст уведомления об ошибке
let sSubject = `Ошибка обработки входящего сообщения сервером приложений для функции "${fn.sCode}" сервиса "${service.sCode}"`;
let sMessage = makeErrorText(e);
//Если сообщение очереди успели создать
if (q) {
//Фиксируем ошибку обработки сервером приложений - в статусе сообщения
q = await this.dbConn.setQueueState({
nQueueId: q.nId,
sExecMsg: sMessage,
nIncExecCnt: NINC_EXEC_CNT_YES,
nExecState: objQueueSchema.NQUEUE_EXEC_STATE_ERR
});
//Фиксируем ошибку обработки сервером приложений - в протоколе работы сервиса
await this.logger.error(`Ошибка обработки входящего сообщения ${q.nId} сервером приложений: ${sMessage}`, { nQueueId: q.nId });
//Добавим чуть больше информации в тему сообщения
sSubject = `Ошибка обработки входящего сообщения ${q.nId} сервером приложений для функции "${fn.sCode}" сервиса "${service.sCode}"`;
} else {
//Ограничимся общей ошибкой
await this.logger.error(sMessage, {
nServiceId: service.nId,
nServiceFnId: fn.nId
});
}
//Если для функции-обработчика указан признак необходимости оповещения об ошибках
if (fn.nErrNtfSign == objServiceFnSchema.NERR_NTF_SIGN_YES) {
//Отправим уведомление об ошибке отработки в почту
await this.notifier.addMessage({
sTo: fn.sErrNtfMail,
sSubject,
sMessage
});
}
}
}
//Обработка сообщения
async processMQTTMessage({ message, service, fn }) {
//Буфер для сообщения очереди
let q = null;
try {
//Тело сообщения и ответ на него
let blMsg = null;
let blResp = null;
//Параметры сообщения и ответа на него
let options = {};
let optionsResp = {};
//Получим тело сообщения
blMsg = message ? message : null;
//Определимся с параметрами сообщения полученными от внешней системы
options = {
method: fn.sFnPrmsType
};
console.log("Кладём в очередь");
//Кладём сообщение в очередь
q = await this.dbConn.putQueue({
nServiceFnId: fn.nId,
sOptions: buildOptionsXML({ options }),
blMsg
});
console.log("Продолжаем");
//Скажем что пришло новое входящее сообщение
await this.logger.info(
`Новое входящее MQTT-сообщение для функции ${fn.sCode} (${buildURL({
sSrvRoot: service.sSrvRoot,
sFnURL: fn.sFnURL
})})`,
{ nQueueId: q.nId }
);
//Вызываем обработчик со стороны БД (если он есть)
if (fn.sPrcResp) {
//Фиксируем начало исполнения сервером БД - в статусе сообщения
q = await this.dbConn.setQueueState({
nQueueId: q.nId,
nExecState: objQueueSchema.NQUEUE_EXEC_STATE_DB
});
//Вызов обработчика БД
let prcRes = await this.dbConn.execQueueDBPrc({ nQueueId: q.nId });
//Если результат - ошибка пробрасываем её
if (prcRes.sResult == objQueueSchema.SPRC_RESP_RESULT_ERR) throw new ServerError(SERR_DB_SERVER, prcRes.sMsg);
//Если результат - ошибка аутентификации, то и её пробрасываем, но с правильным кодом
if (prcRes.sResult == objQueueSchema.SPRC_RESP_RESULT_UNAUTH) throw new ServerError(SERR_UNAUTH, prcRes.sMsg || "Нет аутентификации");
//Выставим статус сообщению очереди - исполнено обработчиком БД
q = await this.dbConn.setQueueState({
nQueueId: q.nId,
nExecState: objQueueSchema.NQUEUE_EXEC_STATE_DB_OK
});
//Считаем ответ полученный от системы
let qData = await this.dbConn.getQueueResp({ nQueueId: q.nId });
blResp = qData.blResp;
}
//Выполняем обработчик "После" (если он есть)
if (fn.sAppSrvAfter) {
//Выставим статус сообщению очереди - исполняется сервером приложений
q = await this.dbConn.setQueueState({
nQueueId: q.nId,
nExecState: objQueueSchema.NQUEUE_EXEC_STATE_APP
});
//Выполняем
const fnAfter = getAppSrvFunction(fn.sAppSrvAfter);
let resAfter = null;
try {
let resAfterPrms = { res: { body: message }, service: service, function: fn };
resAfterPrms.queue = _.cloneDeep(q);
resAfterPrms.queue.blMsg = blMsg;
resAfterPrms.queue.blResp = blResp;
resAfterPrms.options = _.cloneDeep(options);
resAfterPrms.optionsResp = _.cloneDeep(optionsResp);
resAfterPrms.dbConn = this.dbConn;
resAfterPrms.notifier = this.notifier;
resAfter = await fnAfter(resAfterPrms);
} catch (e) {
throw new ServerError(SERR_APP_SERVER_AFTER, e.message);
}
//Проверяем структуру ответа функции предобработки
if (resAfter) {
let sCheckResult = validateObject(
resAfter,
objInQueueSchema.InQueueProcessorFnAfter,
"Результат функции постобработки входящего сообщения"
);
//Если структура ответа в норме
if (!sCheckResult) {
//Выставим статус сообщению очереди - исполнено сервером приложений
q = await this.dbConn.setQueueState({
nQueueId: q.nId,
nExecState: objQueueSchema.NQUEUE_EXEC_STATE_APP_OK
});
//Фиксируем результат исполнения "После" - ответ системы
if (!_.isUndefined(resAfter.blResp)) {
blResp = resAfter.blResp;
q = await this.dbConn.setQueueResp({
nQueueId: q.nId,
blResp,
nIsOriginal: NIS_ORIGINAL_NO
});
}
//Фиксируем результат исполнения "После" - параметры ответа на запрос
if (!_.isUndefined(resAfter.optionsResp)) {
optionsResp = deepMerge(optionsResp, resAfter.optionsResp);
let sOptionsResp = buildOptionsXML({ options: optionsResp });
q = await this.dbConn.setQueueOptionsResp({ nQueueId: q.nId, sOptionsResp });
}
//Если пришел флаг ошибочной аутентификации и он положительный - то это ошибка, дальше ничего не делаем
if (!_.isUndefined(resAfter.bUnAuth)) if (resAfter.bUnAuth === true) throw new ServerError(SERR_UNAUTH, "Нет аутентификации");
} else {
//Или расскажем об ошибке
throw new ServerError(SERR_OBJECT_BAD_INTERFACE, sCheckResult);
}
}
}
//Фиксируем успех обработки - в протоколе работы сервиса
await this.logger.info(`Входящее сообщение ${q.nId} успешно отработано`, { nQueueId: q.nId });
//Фиксируем успех обработки - в статусе сообщения
q = await this.dbConn.setQueueState({
nQueueId: q.nId,
nIncExecCnt: NINC_EXEC_CNT_YES,
nExecState: objQueueSchema.NQUEUE_EXEC_STATE_OK
});
} catch (e) {
//Тема и текст уведомления об ошибке
let sSubject = `Ошибка обработки входящего сообщения сервером приложений для функции "${fn.sCode}" сервиса "${service.sCode}"`;
let sMessage = makeErrorText(e);
//Если сообщение очереди успели создать
if (q) {
//Фиксируем ошибку обработки сервером приложений - в статусе сообщения
q = await this.dbConn.setQueueState({
nQueueId: q.nId,
sExecMsg: sMessage,
nIncExecCnt: NINC_EXEC_CNT_YES,
nExecState: objQueueSchema.NQUEUE_EXEC_STATE_ERR
});
//Фиксируем ошибку обработки сервером приложений - в протоколе работы сервиса
await this.logger.error(`Ошибка обработки входящего сообщения ${q.nId} сервером приложений: ${sMessage}`, { nQueueId: q.nId });
//Добавим чуть больше информации в тему сообщения
sSubject = `Ошибка обработки входящего сообщения ${q.nId} сервером приложений для функции "${fn.sCode}" сервиса "${service.sCode}"`;
} else {
//Ограничимся общей ошибкой
await this.logger.error(sMessage, {
nServiceId: service.nId,
nServiceFnId: fn.nId
});
}
//Если для функции-обработчика указан признак необходимости оповещения об ошибках
if (fn.nErrNtfSign == objServiceFnSchema.NERR_NTF_SIGN_YES) {
//Отправим уведомление об ошибке отработки в почту
await this.notifier.addMessage({
sTo: fn.sErrNtfMail,
sSubject,
sMessage
});
}
}
}
//Запуск обработки очереди входящих сообщений //Запуск обработки очереди входящих сообщений
startProcessing(prms) { startProcessing(prms) {
//Проверяем структуру переданного объекта для старта //Проверяем структуру переданного объекта для старта
@ -428,6 +766,58 @@ class InQueue extends EventEmitter {
} }
); );
}); });
//Считываем прием сообщений по Kafka
let kafkaSrvs = _.filter(this.services, srv => {
return srv.nSrvType === objServiceSchema.NSRV_TYPE_RECIVE && srv.sSrvRoot.startsWith("kafka://");
});
//Если есть сервисы с приемом сообщений по Kafka
if (kafkaSrvs.length !== 0) {
//Обходим данные сервисы
_.forEach(kafkaSrvs, async srvs => {
//Если у сервиса обмена есть функции
if (srvs.functions.length !== 0) {
//Подключаемся и подписываемся на соответствующий брокер
let connectionKafka = await subscribeKafka({
connectionPrms: this.kafkaConnectionPrms,
service: srvs,
processKafkaMessage: prms => this.processKafkaMessage(prms),
logger: this.logger
});
//Если подключение было создано
if (connectionKafka) {
//Добавляем в общий список подключений kafka
this.kafkaConnections.push(connectionKafka);
}
}
});
}
//Считываем прием сообщений по MQTT
let mqttSrvs = _.filter(this.services, srv => {
return (
srv.nSrvType === objServiceSchema.NSRV_TYPE_RECIVE && (srv.sSrvRoot.startsWith("mqtt://") || srv.sSrvRoot.startsWith("mqtts://"))
);
});
//Если есть сервисы с приемом сообщений по MQTT
if (mqttSrvs.length !== 0) {
//Обходим данные сервисы
_.forEach(mqttSrvs, async srvs => {
//Если у сервиса обмена есть функции
if (srvs.functions.length !== 0) {
//Подключаемся и подписываемся на соответствующий брокер
let connectionMQTT = await subscribeMQTT({
connectionPrms: this.mqttConnectionPrms,
service: srvs,
processMQTTMessage: prms => this.processMQTTMessage(prms),
logger: this.logger
});
//Если подключение было создано
if (connectionMQTT) {
//Добавляем в общий список подключений kafka
this.mqttConnections.push(connectionMQTT);
}
}
});
}
//Запросы на адреса, не входящие в состав объявленных сервисов - 404 NOT FOUND //Запросы на адреса, не входящие в состав объявленных сервисов - 404 NOT FOUND
this.webApp.use("*", (req, res) => { this.webApp.use("*", (req, res) => {
res.status(404).send( res.status(404).send(
@ -453,10 +843,39 @@ class InQueue extends EventEmitter {
throw new ServerError(SERR_OBJECT_BAD_INTERFACE, sCheckResult); throw new ServerError(SERR_OBJECT_BAD_INTERFACE, sCheckResult);
} }
} }
//Закрытие подключений, если они есть
stopConnections() {
//Если у нас есть соединения с MQTT
if (this.mqttConnections.length !== 0) {
//Закрываем их
_.forEach(this.mqttConnections, async connection => {
try {
await connection.end();
} catch (e) {
await this.logger.error(`Ошибка завершения MQTT подключения: ${makeErrorText(e)}`);
}
});
}
//Если у нас есть соединения с Kafka
if (this.kafkaConnections.length !== 0) {
//Закрываем их
_.forEach(this.kafkaConnections, async connection => {
try {
await connection.disconnect();
} catch (e) {
await this.logger.error(`Ошибка завершения Kafka подключения: ${makeErrorText(e)}`);
}
});
}
}
//Остановка обработки очереди исходящих сообщений //Остановка обработки очереди исходящих сообщений
stopProcessing() { stopProcessing() {
//Выставляем флаг неработы //Выставляем флаг неработы
this.bWorking = false; this.bWorking = false;
//Закрываем подключения, если они есть
this.stopConnections();
//Останавливаем WEB-сервер (если создавался) //Останавливаем WEB-сервер (если создавался)
if (this.srv) { if (this.srv) {
this.srv.close(() => { this.srv.close(() => {

118
core/kafka_connector.js Normal file
View File

@ -0,0 +1,118 @@
/*
Сервис интеграции ПП Парус 8 с WEB API
Модуль ядра: обработчик kafka сообщений
*/
//----------------------
// Подключение библиотек
//----------------------
const _ = require("lodash"); //Работа с массивами и коллекциями
const { makeErrorText, getKafkaBroker, getKafkaAuth } = require("./utils"); //Вспомогательные функции
const { Kafka, logLevel } = require("kafkajs"); //Работа с Kafka
//------------
// Тело модуля
//------------
//Отправка сообщения Kafka
const publishKafka = async ({ connectionPrms, url, auth, topic, message }) => {
//Иницализируем подключение к Kafka
let kafka = new Kafka({
clientId: connectionPrms.sClientIdSender,
brokers: [url],
connectionTimeout: connectionPrms.nConnectionTimeout,
logLevel: logLevel.NOTHING,
...auth
});
//Инициализируем продюсера
let producer = kafka.producer();
//Подключаемся к Kafka
await producer.connect();
//Отправляем сообщение
let res = await producer.send({ topic: topic, messages: [{ value: message }] });
//Отключаемся
await producer.disconnect();
//Возвращаем ответ
return res;
};
//Получение MQTT сообщений
const subscribeKafka = async ({ connectionPrms, service, processKafkaMessage, logger }) => {
try {
//Признак необходимости вывода сообщения о потере соединения
let bLogLostConnection = true;
//Получаем брокера по URL сервиса
let sBroker = getKafkaBroker(service.sSrvRoot);
//Иницализируем подключение к Kafka
let client = new Kafka({
clientId: connectionPrms.sClientIdRecipient,
brokers: [sBroker],
connectionTimeout: connectionPrms.nConnectionTimeout,
...getKafkaAuth(service.sSrvUser, service.sSrvPass),
logLevel: logLevel.NOTHING,
retry: {
retries: 0,
maxRetryTime: connectionPrms.nMaxRetryTime,
initialRetryTime: connectionPrms.nInitialRetryTime,
restartOnFailure: error => {
return new Promise(resolve => {
//Если требуется вывести ошибку
if (bLogLostConnection) {
//Выводим ошибку
logger.error(`Соединение с Kafka потеряно (${sBroker}): ${makeErrorText(error)}`);
//Сбрасываем признак необходимости вывода ошибки
bLogLostConnection = false;
}
resolve(connectionPrms.bRestartOnFailure);
});
}
}
});
//Инициализируем получателя
let consumer = client.consumer({ groupId: "ParusWebApi" });
//Устанавливаем прослушивание
await consumer.connect();
consumer.subscribe({ topics: _.map(service.functions, "sFnURL") });
//Запускаем прослушивание необходимых топиков
consumer.run({
eachMessage: async ({ topic, message }) => {
try {
//Вызываем обработчик
processKafkaMessage({
message,
service,
fn: _.find(service.functions, { sFnURL: topic })
});
} catch (e) {
await logger.error(`Ошибка обработки исходящего сообщения Kafka: ${makeErrorText(e)}`);
}
}
});
//Отслеживаем соединение
consumer.on(consumer.events.CONNECT, () => {
//Если сообщение о потере соединения уже выводилось
if (!bLogLostConnection) {
//Сообщим о восстановлении соединения
logger.info(`Соединение с Kafka восстановлено (${sBroker})`);
//Устанавливаем признак сообщения о потере соединения
bLogLostConnection = true;
}
});
//Возвращаем соединение
return consumer;
} catch (e) {
await logger.error(`Ошибка запуска обработчика очереди исходящих сообщений Kafka: ${makeErrorText(e)}`);
}
};
//-----------------
// Интерфейс модуля
//-----------------
exports.publishKafka = publishKafka;
exports.subscribeKafka = subscribeKafka;

89
core/mqtt_connector.js Normal file
View File

@ -0,0 +1,89 @@
/*
Сервис интеграции ПП Парус 8 с WEB API
Модуль ядра: обработчик mqtt сообщений
*/
//----------------------
// Подключение библиотек
//----------------------
const _ = require("lodash"); //Работа с массивами и коллекциями
const { makeErrorText } = require("./utils"); //Вспомогательные функции
const mqtt = require("mqtt"); //Работа с MQTT
//------------
// Тело модуля
//------------
//Отправка MQTT сообщения
const publishMQTT = async ({ connectionPrms, url, auth, topic, message }) => {
//Инициализируем подключение
const client = await mqtt.connectAsync(url, {
clientId: connectionPrms.sClientIdSender,
clean: true,
connectTimeout: connectionPrms.nConnectTimeout,
username: auth.user,
password: auth.pass,
reconnectPeriod: connectionPrms.nReconnectPeriod
});
//Отправляем сообщение
await client.publishAsync(topic, message);
//Закрываем подключение
await client.endAsync();
//Возвращаем сообщение, которое было отправлено
return { statusCode: 200 };
};
//Получение MQTT сообщений
const subscribeMQTT = async ({ connectionPrms, service, processMQTTMessage, logger }) => {
try {
//Инициализируем строку подключения
let sBroker = service.sSrvRoot;
//Инициализируем подключение
const client = await mqtt.connectAsync(sBroker, {
clientId: connectionPrms.sClientIdRecipient,
clean: true,
connectTimeout: connectionPrms.nConnectTimeout,
username: service.sSrvUser,
password: service.sSrvPass,
reconnectPeriod: connectionPrms.nReconnectPeriod
});
//Обходим функции сервиса
_.forEach(
_.filter(service.functions, fn => !fn.sFnURL.startsWith("@")),
fn => {
client.subscribe(fn.sFnURL);
}
);
//Прослушиваем сообщения
client.on("message", (topic, message) => {
console.log("message");
//Обрабатываем сообщение
processMQTTMessage({ message, service, fn: _.find(service.functions, { sFnURL: topic }) });
});
//Прослушиваем отключение от сервера
client.on("offline", () => {
//Выводим ошибку
logger.error(`Соединение с MQTT потеряно (${sBroker})`);
});
//Прослушиваем восстановление соединения
client.on("reconnect", () => {
//Сообщим о восстановлении соединения
logger.info(`Соединение с MQTT восстановлено (${sBroker})`);
});
//Возвращаем подключение
return client;
} catch (e) {
logger.error(`Ошибка запуска обработчика очереди исходящих сообщений MQTT: ${makeErrorText(e)}`);
}
};
//-----------------
// Интерфейс модуля
//-----------------
exports.publishMQTT = publishMQTT;
exports.subscribeMQTT = subscribeMQTT;

View File

@ -70,6 +70,10 @@ class OutQueue extends EventEmitter {
this.inProgress = []; this.inProgress = [];
//Привяжем методы к указателю на себя для использования в обработчиках событий //Привяжем методы к указателю на себя для использования в обработчиках событий
this.outDetectingLoop = this.outDetectingLoop.bind(this); this.outDetectingLoop = this.outDetectingLoop.bind(this);
//Параметры подключения к Kafka
this.kafkaConnectionPrms = _.cloneDeep(prms.kafkaConnectionPrms);
//Параметры подключения к MQTT
this.mqttConnectionPrms = _.cloneDeep(prms.mqttConnectionPrms);
} else { } else {
throw new ServerError(SERR_OBJECT_BAD_INTERFACE, sCheckResult); throw new ServerError(SERR_OBJECT_BAD_INTERFACE, sCheckResult);
} }
@ -141,11 +145,7 @@ class OutQueue extends EventEmitter {
//Старт обработчика //Старт обработчика
startQueueProcessor(prms) { startQueueProcessor(prms) {
//Проверяем структуру переданного объекта для старта обработчика //Проверяем структуру переданного объекта для старта обработчика
let sCheckResult = validateObject( let sCheckResult = validateObject(prms, prmsOutQueueSchema.startQueueProcessor, "Параметры функции запуска обработчика сообщения очереди");
prms,
prmsOutQueueSchema.startQueueProcessor,
"Параметры функции запуска обработчика сообщения очереди"
);
//Если структура объекта в норме //Если структура объекта в норме
if (!sCheckResult) { if (!sCheckResult) {
//Добавляем идентификатор позиции очереди в список обрабатываемых //Добавляем идентификатор позиции очереди в список обрабатываемых
@ -163,7 +163,9 @@ class OutQueue extends EventEmitter {
function: _.find(_.find(this.services, { nId: prms.queue.nServiceId }).functions, { function: _.find(_.find(this.services, { nId: prms.queue.nServiceId }).functions, {
nId: prms.queue.nServiceFnId nId: prms.queue.nServiceFnId
}), }),
sProxy: this.sProxy sProxy: this.sProxy,
kafkaConnectionPrms: this.kafkaConnectionPrms,
mqttConnectionPrms: this.mqttConnectionPrms
}); });
//Уменьшаем количество доступных обработчиков //Уменьшаем количество доступных обработчиков
this.nWorkersLeft--; this.nWorkersLeft--;
@ -174,11 +176,7 @@ class OutQueue extends EventEmitter {
//Останов обработчика //Останов обработчика
stopQueueProcessor(prms) { stopQueueProcessor(prms) {
//Проверяем структуру переданного объекта для останова обработчика //Проверяем структуру переданного объекта для останова обработчика
let sCheckResult = validateObject( let sCheckResult = validateObject(prms, prmsOutQueueSchema.stopQueueProcessor, "Параметры функции останова обработчика сообщения очереди");
prms,
prmsOutQueueSchema.stopQueueProcessor,
"Параметры функции останова обработчика сообщения очереди"
);
//Если структура объекта в норме //Если структура объекта в норме
if (!sCheckResult) { if (!sCheckResult) {
//Удаляем идентификатор позиции очереди из списка обрабатываемых //Удаляем идентификатор позиции очереди из списка обрабатываемых
@ -219,19 +217,13 @@ class OutQueue extends EventEmitter {
throw new ServerError(SERR_OBJECT_BAD_INTERFACE, sCheckResult); throw new ServerError(SERR_OBJECT_BAD_INTERFACE, sCheckResult);
} }
} catch (e) { } catch (e) {
await this.logger.error( await this.logger.error(`При отправке уведомления об ошибке обработки исходящего сообщения: ${makeErrorText(e)}`);
`При отправке уведомления об ошибке обработки исходящего сообщения: ${makeErrorText(e)}`
);
} }
} }
//Запуск обработки очередного сообщения //Запуск обработки очередного сообщения
processMessage(prms) { processMessage(prms) {
//Проверяем структуру переданного объекта //Проверяем структуру переданного объекта
let sCheckResult = validateObject( let sCheckResult = validateObject(prms, prmsOutQueueSchema.processMessage, "Параметры функции запуска обработки очередного сообщения");
prms,
prmsOutQueueSchema.processMessage,
"Параметры функции запуска обработки очередного сообщения"
);
//Если структура объекта в норме //Если структура объекта в норме
if (!sCheckResult) { if (!sCheckResult) {
//Проверим, что есть доступные обработчики //Проверим, что есть доступные обработчики
@ -276,10 +268,9 @@ class OutQueue extends EventEmitter {
}); });
} catch (e) { } catch (e) {
//Отразим в протоколе ошибку постановки задачи на аутентификацию сервиса //Отразим в протоколе ошибку постановки задачи на аутентификацию сервиса
await self.logger.error( await self.logger.error(`Ошибка постановки задачи на аутентификацию сервиса: ${makeErrorText(e)}`, {
`Ошибка постановки задачи на аутентификацию сервиса: ${makeErrorText(e)}`, nQueueId: prms.queue.nId
{ nQueueId: prms.queue.nId } });
);
} }
} }
} }
@ -298,25 +289,21 @@ class OutQueue extends EventEmitter {
sExecMsg: sError, sExecMsg: sError,
nIncExecCnt: nQueueOldExecCnt == prms.queue.nExecCnt ? NINC_EXEC_CNT_YES : NINC_EXEC_CNT_NO, nIncExecCnt: nQueueOldExecCnt == prms.queue.nExecCnt ? NINC_EXEC_CNT_YES : NINC_EXEC_CNT_NO,
nExecState: nExecState:
(nQueueOldExecCnt == prms.queue.nExecCnt (nQueueOldExecCnt == prms.queue.nExecCnt ? prms.queue.nExecCnt + 1 : prms.queue.nExecCnt) < prms.queue.nRetryAttempts
? prms.queue.nExecCnt + 1
: prms.queue.nExecCnt) < prms.queue.nRetryAttempts
? prms.queue.nExecState ? prms.queue.nExecState
: objQueueSchema.NQUEUE_EXEC_STATE_ERR : objQueueSchema.NQUEUE_EXEC_STATE_ERR
}); });
} }
//Если исполнение завершилось полностью и с ошибкой - расскажем об этом //Если исполнение завершилось полностью и с ошибкой - расскажем об этом
if (prms.queue.nExecState == objQueueSchema.NQUEUE_EXEC_STATE_ERR) if (prms.queue.nExecState == objQueueSchema.NQUEUE_EXEC_STATE_ERR) await this.notifyMessageProcessError(prms);
await this.notifyMessageProcessError(prms);
//Останавливаем обработчик и инкрементируем флаг их доступного количества //Останавливаем обработчик и инкрементируем флаг их доступного количества
try { try {
this.stopQueueProcessor({ nQueueId: prms.queue.nId, proc }); this.stopQueueProcessor({ nQueueId: prms.queue.nId, proc });
} catch (e) { } catch (e) {
//Отразим в протоколе ошибку останова //Отразим в протоколе ошибку останова
await self.logger.error( await self.logger.error(`Ошибка останова обработчика исходящего сообщения: ${makeErrorText(e)}`, {
`Ошибка останова обработчика исходящего сообщения: ${makeErrorText(e)}`, nQueueId: prms.queue.nId
{ nQueueId: prms.queue.nId } });
);
} }
}); });
//Перехват ошибок обработчика //Перехват ошибок обработчика
@ -333,23 +320,20 @@ class OutQueue extends EventEmitter {
sExecMsg: makeErrorText(e), sExecMsg: makeErrorText(e),
nIncExecCnt: nQueueOldExecCnt == prms.queue.nExecCnt ? NINC_EXEC_CNT_YES : NINC_EXEC_CNT_NO, nIncExecCnt: nQueueOldExecCnt == prms.queue.nExecCnt ? NINC_EXEC_CNT_YES : NINC_EXEC_CNT_NO,
nExecState: nExecState:
(nQueueOldExecCnt == prms.queue.nExecCnt ? prms.queue.nExecCnt + 1 : prms.queue.nExecCnt) < (nQueueOldExecCnt == prms.queue.nExecCnt ? prms.queue.nExecCnt + 1 : prms.queue.nExecCnt) < prms.queue.nRetryAttempts
prms.queue.nRetryAttempts
? prms.queue.nExecState ? prms.queue.nExecState
: objQueueSchema.NQUEUE_EXEC_STATE_ERR : objQueueSchema.NQUEUE_EXEC_STATE_ERR
}); });
//Если исполнение завершилось полностью и с ошибкой - расскажем об этом //Если исполнение завершилось полностью и с ошибкой - расскажем об этом
if (prms.queue.nExecState == objQueueSchema.NQUEUE_EXEC_STATE_ERR) if (prms.queue.nExecState == objQueueSchema.NQUEUE_EXEC_STATE_ERR) await this.notifyMessageProcessError(prms);
await this.notifyMessageProcessError(prms);
//Останавливаем обработчик и инкрементируем флаг их доступного количества //Останавливаем обработчик и инкрементируем флаг их доступного количества
try { try {
this.stopQueueProcessor({ nQueueId: prms.queue.nId, proc }); this.stopQueueProcessor({ nQueueId: prms.queue.nId, proc });
} catch (e) { } catch (e) {
//Отразим в протоколе ошибку останова //Отразим в протоколе ошибку останова
await self.logger.error( await self.logger.error(`Ошибка останова обработчика исходящего сообщения: ${makeErrorText(e)}`, {
`Ошибка останова обработчика исходящего сообщения: ${makeErrorText(e)}`, nQueueId: prms.queue.nId
{ nQueueId: prms.queue.nId } });
);
} }
}); });
//Перехват останова обработчика //Перехват останова обработчика
@ -392,16 +376,12 @@ class OutQueue extends EventEmitter {
nQueueId: outMsg.nId, nQueueId: outMsg.nId,
sExecMsg: makeErrorText(e), sExecMsg: makeErrorText(e),
nIncExecCnt: NINC_EXEC_CNT_YES, nIncExecCnt: NINC_EXEC_CNT_YES,
nExecState: nExecState: outMsg.nExecCnt + 1 < outMsg.nRetryAttempts ? outMsg.nExecState : objQueueSchema.NQUEUE_EXEC_STATE_ERR
outMsg.nExecCnt + 1 < outMsg.nRetryAttempts
? outMsg.nExecState
: objQueueSchema.NQUEUE_EXEC_STATE_ERR
}); });
//Фиксируем ошибку обработки сервером приложений - запись в протокол работы сервера приложений //Фиксируем ошибку обработки сервером приложений - запись в протокол работы сервера приложений
await this.logger.error(makeErrorText(e), { nQueueId: outMsg.nId }); await this.logger.error(makeErrorText(e), { nQueueId: outMsg.nId });
//Если исполнение завершилось полностью и с ошибкой - расскажем об этом //Если исполнение завершилось полностью и с ошибкой - расскажем об этом
if (queue.nExecState == objQueueSchema.NQUEUE_EXEC_STATE_ERR) if (queue.nExecState == objQueueSchema.NQUEUE_EXEC_STATE_ERR) await this.notifyMessageProcessError({ queue });
await this.notifyMessageProcessError({ queue });
} }
} }
} }

View File

@ -12,7 +12,18 @@ const _ = require("lodash"); //Работа с массивами и объек
const rqp = require("request-promise"); //Работа с HTTP/HTTPS запросами const rqp = require("request-promise"); //Работа с HTTP/HTTPS запросами
const lg = require("./logger"); //Протоколирование работы const lg = require("./logger"); //Протоколирование работы
const db = require("./db_connector"); //Взаимодействие с БД const db = require("./db_connector"); //Взаимодействие с БД
const { makeErrorText, validateObject, getAppSrvFunction, buildURL, parseOptionsXML, buildOptionsXML, deepMerge } = require("./utils"); //Вспомогательные функции const {
makeErrorText,
validateObject,
getAppSrvFunction,
buildURL,
parseOptionsXML,
buildOptionsXML,
deepMerge,
getKafkaBroker,
getKafkaAuth,
getURLProtocol
} = require("./utils"); //Вспомогательные функции
const { ServerError } = require("./server_errors"); //Типовая ошибка const { ServerError } = require("./server_errors"); //Типовая ошибка
const objOutQueueProcessorSchema = require("../models/obj_out_queue_processor"); //Схема валидации сообщений обмена с бработчиком очереди исходящих сообщений const objOutQueueProcessorSchema = require("../models/obj_out_queue_processor"); //Схема валидации сообщений обмена с бработчиком очереди исходящих сообщений
const prmsOutQueueProcessorSchema = require("../models/prms_out_queue_processor"); //Схема валидации параметров функций модуля const prmsOutQueueProcessorSchema = require("../models/prms_out_queue_processor"); //Схема валидации параметров функций модуля
@ -28,6 +39,8 @@ const {
SERR_UNAUTH SERR_UNAUTH
} = require("./constants"); //Глобальные константы } = require("./constants"); //Глобальные константы
const { NINC_EXEC_CNT_YES, NINC_EXEC_CNT_NO, NIS_ORIGINAL_NO, NIS_ORIGINAL_YES } = require("../models/prms_db_connector"); //Схемы валидации параметров функций модуля взаимодействия с БД const { NINC_EXEC_CNT_YES, NINC_EXEC_CNT_NO, NIS_ORIGINAL_NO, NIS_ORIGINAL_YES } = require("../models/prms_db_connector"); //Схемы валидации параметров функций модуля взаимодействия с БД
const { publishMQTT } = require("./mqtt_connector"); //Работа с MQTT/MQTTS запросами
const { publishKafka } = require("./kafka_connector"); //Работа с Kafka запросами
//-------------------------- //--------------------------
// Глобальные идентификаторы // Глобальные идентификаторы
@ -129,11 +142,41 @@ const appProcess = async prms => {
let optionsResp = {}; let optionsResp = {};
//Флаг прекращения обработки сообщения //Флаг прекращения обработки сообщения
let bStopPropagation = false; let bStopPropagation = false;
//Флаг выполнения обработчика "До"
let bExecuteBefore = true;
//Флаг выполнения обработчика "После"
let bExecuteAfter = true;
//Считываем протокол работы
let sProtocol = getURLProtocol(prms.service.sSrvRoot);
//Исходя из протокола собираем параметры
switch (true) {
//Kafka
case sProtocol === objServiceFnSchema.SPROTOCOL_KAFKA:
options.url = getKafkaBroker(prms.service.sSrvRoot);
options.body = prms.queue.blMsg;
options.topic = prms.function.sFnURL;
options.auth = getKafkaAuth(prms.service.sSrvUser, prms.service.sSrvPass);
//Указываем, что выполнение обработчика "После" невозможно
bExecuteAfter = false;
break;
//mqtt и mqtts
case [objServiceFnSchema.SPROTOCOL_MQTT, objServiceFnSchema.SPROTOCOL_MQTTS].includes(sProtocol):
options.url = prms.service.sSrvRoot;
options.body = prms.queue.blMsg;
options.topic = prms.function.sFnURL;
options.auth = { user: prms.service.sSrvUser, pass: prms.service.sSrvPass };
//Указываем, что выполнение обработчика "После" невозможно
bExecuteAfter = false;
break;
//Другие
default:
//Определимся с URL и телом сообщения в зависимости от способа передачи параметров (для POST, PATCH и PUT - данные в теле, для остальных - в URI) //Определимся с URL и телом сообщения в зависимости от способа передачи параметров (для POST, PATCH и PUT - данные в теле, для остальных - в URI)
if ( if (
[objServiceFnSchema.NFN_PRMS_TYPE_POST, objServiceFnSchema.NFN_PRMS_TYPE_PATCH, objServiceFnSchema.NFN_PRMS_TYPE_PUT].includes( [
prms.function.nFnPrmsType objServiceFnSchema.NFN_PRMS_TYPE_POST,
) objServiceFnSchema.NFN_PRMS_TYPE_PATCH,
objServiceFnSchema.NFN_PRMS_TYPE_PUT
].includes(prms.function.nFnPrmsType)
) { ) {
options.url = buildURL({ sSrvRoot: prms.service.sSrvRoot, sFnURL: prms.function.sFnURL }); options.url = buildURL({ sSrvRoot: prms.service.sSrvRoot, sFnURL: prms.function.sFnURL });
options.body = prms.queue.blMsg; options.body = prms.queue.blMsg;
@ -145,6 +188,8 @@ const appProcess = async prms => {
sQuery: prms.queue.blMsg === null ? "" : prms.queue.blMsg.toString() sQuery: prms.queue.blMsg === null ? "" : prms.queue.blMsg.toString()
}); });
} }
break;
}
// Если у сервиса указан прокси, либо у приложения установлен глобальный прокси // Если у сервиса указан прокси, либо у приложения установлен глобальный прокси
if (prms.service.sProxyURL || prms.sProxy) { if (prms.service.sProxyURL || prms.sProxy) {
// Добавляем прокси с приоритетом сервиса // Добавляем прокси с приоритетом сервиса
@ -167,7 +212,7 @@ const appProcess = async prms => {
} }
} }
//Выполняем обработчик "До" (если он есть) //Выполняем обработчик "До" (если он есть)
if (prms.function.sAppSrvBefore) { if (prms.function.sAppSrvBefore && bExecuteBefore) {
const fnBefore = getAppSrvFunction(prms.function.sAppSrvBefore); const fnBefore = getAppSrvFunction(prms.function.sAppSrvBefore);
let resBefore = null; let resBefore = null;
try { try {
@ -199,7 +244,12 @@ const appProcess = async prms => {
objServiceFnSchema.NFN_PRMS_TYPE_POST, objServiceFnSchema.NFN_PRMS_TYPE_POST,
objServiceFnSchema.NFN_PRMS_TYPE_PATCH, objServiceFnSchema.NFN_PRMS_TYPE_PATCH,
objServiceFnSchema.NFN_PRMS_TYPE_PUT objServiceFnSchema.NFN_PRMS_TYPE_PUT
].includes(prms.function.nFnPrmsType) ].includes(prms.function.nFnPrmsType) ||
[
objServiceFnSchema.SPROTOCOL_KAFKA,
objServiceFnSchema.SPROTOCOL_MQTT,
objServiceFnSchema.SPROTOCOL_MQTTS
].includes(sProtocol)
) { ) {
options.body = prms.queue.blMsg; options.body = prms.queue.blMsg;
} else { } else {
@ -263,7 +313,34 @@ const appProcess = async prms => {
} }
//Ждем ответ от удалённого сервера //Ждем ответ от удалённого сервера
options.resolveWithFullResponse = true; options.resolveWithFullResponse = true;
let serverResp = await rqp(options); let serverResp = null;
//Выполняем отправку исходя из протокола
switch (true) {
//Kafka
case sProtocol === objServiceFnSchema.SPROTOCOL_KAFKA:
serverResp = await publishKafka({
connectionPrms: prms.kafkaConnectionPrms,
url: options.url,
auth: options.auth,
topic: options.topic,
message: options.body
});
break;
//mqtt и mqtts
case [objServiceFnSchema.SPROTOCOL_MQTT, objServiceFnSchema.SPROTOCOL_MQTTS].includes(sProtocol):
serverResp = await publishMQTT({
connectionPrms: prms.mqttConnectionPrms,
url: options.url,
auth: options.auth,
topic: options.topic,
message: options.body
});
break;
//Другие
default:
serverResp = await rqp(options);
break;
}
//Сохраняем полученный ответ //Сохраняем полученный ответ
prms.queue.blResp = Buffer.from(serverResp.body || ""); prms.queue.blResp = Buffer.from(serverResp.body || "");
await dbConn.setQueueResp({ await dbConn.setQueueResp({
@ -293,7 +370,7 @@ const appProcess = async prms => {
throw new ServerError(SERR_WEB_SERVER, sError); throw new ServerError(SERR_WEB_SERVER, sError);
} }
//Выполняем обработчик "После" (если он есть) //Выполняем обработчик "После" (если он есть)
if (prms.function.sAppSrvAfter) { if (prms.function.sAppSrvAfter && bExecuteAfter) {
const fnAfter = getAppSrvFunction(prms.function.sAppSrvAfter); const fnAfter = getAppSrvFunction(prms.function.sAppSrvAfter);
let resAfter = null; let resAfter = null;
try { try {
@ -515,7 +592,9 @@ const processTask = async prms => {
queue: q, queue: q,
service: prms.task.service, service: prms.task.service,
function: prms.task.function, function: prms.task.function,
sProxy: prms.task.sProxy sProxy: prms.task.sProxy,
kafkaConnectionPrms: prms.task.kafkaConnectionPrms,
mqttConnectionPrms: prms.task.mqttConnectionPrms
}); });
//Если результат обработки ошибка - пробрасываем её дальше //Если результат обработки ошибка - пробрасываем её дальше
if (res instanceof ServerError) { if (res instanceof ServerError) {

View File

@ -45,9 +45,7 @@ const validateObject = (obj, schema, sObjName) => {
let a = errors.map(e => { let a = errors.map(e => {
return e.message; return e.message;
}); });
sRes = `Объект${sObjName ? ` "${sObjName}" ` : " "}имеет некорректный формат: ${_.uniq(a).join( sRes = `Объект${sObjName ? ` "${sObjName}" ` : " "}имеет некорректный формат: ${_.uniq(a).join("; ")}`;
"; "
)}`;
} }
} else { } else {
//Валидатор вернул не то, что мы ожидали //Валидатор вернул не то, что мы ожидали
@ -142,8 +140,7 @@ const getAppSrvFunction = sAppSrv => {
//Объявим формат (для сообщений об ошибках) //Объявим формат (для сообщений об ошибках)
const sFormat = "(ожидаемый формат: <МОДУЛЬ>/<ФУНКЦИЯ>)"; const sFormat = "(ожидаемый формат: <МОДУЛЬ>/<ФУНКЦИЯ>)";
//Проверим, что есть что разбирать //Проверим, что есть что разбирать
if (!sAppSrv) if (!sAppSrv) throw new ServerError(SERR_MODULES_NO_MODULE_SPECIFIED, `Не указаны модуль и функция обработчика ${sFormat}`);
throw new ServerError(SERR_MODULES_NO_MODULE_SPECIFIED, `Не указаны модуль и функция обработчика ${sFormat}`);
//Разбираем //Разбираем
try { try {
//Разбираем на модуль и функцию //Разбираем на модуль и функцию
@ -175,11 +172,7 @@ const getAppSrvFunction = sAppSrv => {
const sendMail = prms => { const sendMail = prms => {
return new Promise((resolve, reject) => { return new Promise((resolve, reject) => {
//Проверяем структуру переданного объекта для отправки E-Mail уведомления //Проверяем структуру переданного объекта для отправки E-Mail уведомления
let sCheckResult = validateObject( let sCheckResult = validateObject(prms, prmsUtilsSchema.sendMail, "Параметры функции отправки E-Mail уведомления");
prms,
prmsUtilsSchema.sendMail,
"Параметры функции отправки E-Mail уведомления"
);
//Если структура объекта в норме //Если структура объекта в норме
if (!sCheckResult) { if (!sCheckResult) {
//Формируем параметры для подключения к SMTP //Формируем параметры для подключения к SMTP
@ -196,7 +189,7 @@ const sendMail = prms => {
transpOptions.auth = { transpOptions.auth = {
user: prms.mail.sUser, user: prms.mail.sUser,
pass: prms.mail.sPass pass: prms.mail.sPass
} };
} }
//Настраиваем подключение к SMTP-серверу //Настраиваем подключение к SMTP-серверу
let transporter = nodemailer.createTransport(transpOptions); let transporter = nodemailer.createTransport(transpOptions);
@ -215,12 +208,7 @@ const sendMail = prms => {
reject(new ServerError(SERR_MAIL_FAILED, `${error.code}: ${error}`)); reject(new ServerError(SERR_MAIL_FAILED, `${error.code}: ${error}`));
} else { } else {
if (info.rejected && Array.isArray(info.rejected) && info.rejected.length > 0) { if (info.rejected && Array.isArray(info.rejected) && info.rejected.length > 0) {
reject( reject(new ServerError(SERR_MAIL_FAILED, `Сообщение не доствлено адресатам: ${info.rejected.join(", ")}`));
new ServerError(
SERR_MAIL_FAILED,
`Сообщение не доствлено адресатам: ${info.rejected.join(", ")}`
)
);
} else { } else {
resolve(info); resolve(info);
} }
@ -239,7 +227,7 @@ const buildURL = prms => {
//Если структура объекта в норме //Если структура объекта в норме
if (!sCheckResult) { if (!sCheckResult) {
//Формируем URL с учетом лишних "/" //Формируем URL с учетом лишних "/"
return `${prms.sSrvRoot.replace(/\/+$/, '')}/${prms.sFnURL.replace(/^\/+/, '')}${prms.sQuery ? `?${prms.sQuery}` : ""}`; return `${prms.sSrvRoot.replace(/\/+$/, "")}/${prms.sFnURL.replace(/^\/+/, "")}${prms.sQuery ? `?${prms.sQuery}` : ""}`;
} else { } else {
throw new ServerError(SERR_OBJECT_BAD_INTERFACE, sCheckResult); throw new ServerError(SERR_OBJECT_BAD_INTERFACE, sCheckResult);
} }
@ -283,11 +271,7 @@ const parseXML = prms => {
//Разбор параметров сообщения/ответа (XML > JSON) //Разбор параметров сообщения/ответа (XML > JSON)
const parseOptionsXML = async prms => { const parseOptionsXML = async prms => {
//Проверяем структуру переданных параметров //Проверяем структуру переданных параметров
let sCheckResult = validateObject( let sCheckResult = validateObject(prms, prmsUtilsSchema.parseOptionsXML, "Параметры функции разбора XML параметров сообщения/ответа");
prms,
prmsUtilsSchema.parseOptionsXML,
"Параметры функции разбора XML параметров сообщения/ответа"
);
//Если структура объекта в норме //Если структура объекта в норме
if (!sCheckResult) { if (!sCheckResult) {
try { try {
@ -311,11 +295,7 @@ const parseOptionsXML = async prms => {
//Сборка параметров сообщения/ответа (JSON > XML) //Сборка параметров сообщения/ответа (JSON > XML)
const buildOptionsXML = prms => { const buildOptionsXML = prms => {
//Проверяем структуру переданных параметров //Проверяем структуру переданных параметров
let sCheckResult = validateObject( let sCheckResult = validateObject(prms, prmsUtilsSchema.buildOptionsXML, "Параметры функции сборки XML параметров сообщения/ответа");
prms,
prmsUtilsSchema.buildOptionsXML,
"Параметры функции сборки XML параметров сообщения/ответа"
);
//Если структура объекта в норме //Если структура объекта в норме
if (!sCheckResult) { if (!sCheckResult) {
try { try {
@ -346,6 +326,23 @@ const deepMerge = (...args) => {
return res; return res;
}; };
//Получение брокера Kafka по адресу сервиса обмена
const getKafkaBroker = sURL => {
//Убираем лишние символы
return sURL.slice(8);
};
//Получение авторизации для Kafka
const getKafkaAuth = (sUser, sPass) => {
return sUser ? { ssl: true, sasl: { mechanism: "plain", username: sUser, password: sPass } } : null;
};
//Получение протокола адреса
const getURLProtocol = sURL => {
//Считываем протокол адреса
return new URL(sURL).protocol.slice(0, -1);
};
//----------------- //-----------------
// Интерфейс модуля // Интерфейс модуля
//----------------- //-----------------
@ -364,3 +361,6 @@ exports.parseOptionsXML = parseOptionsXML;
exports.buildOptionsXML = buildOptionsXML; exports.buildOptionsXML = buildOptionsXML;
exports.getNowString = getNowString; exports.getNowString = getNowString;
exports.deepMerge = deepMerge; exports.deepMerge = deepMerge;
exports.getKafkaBroker = getKafkaBroker;
exports.getKafkaAuth = getKafkaAuth;
exports.getURLProtocol = getURLProtocol;

View File

@ -70,6 +70,13 @@ const NERR_NTF_SIGN_YES = 1; //Оповещать об ошибке исполн
const SERR_NTF_SIGN_NO = "ERR_NTF_SIGN_NO"; //Не оповещать об ошибке исполнения (строковый код) const SERR_NTF_SIGN_NO = "ERR_NTF_SIGN_NO"; //Не оповещать об ошибке исполнения (строковый код)
const SERR_NTF_SIGN_YES = "ERR_NTF_SIGN_YES"; //Оповещать об ошибке исполнения (строковый код) const SERR_NTF_SIGN_YES = "ERR_NTF_SIGN_YES"; //Оповещать об ошибке исполнения (строковый код)
//Протоколы работы сервиса
const SPROTOCOL_HTTP = "http"; //Протокол HTTP
const SPROTOCOL_HTTPS = "https"; //Протокол HTTPS
const SPROTOCOL_MQTT = "mqtt"; //Протокол MQTT
const SPROTOCOL_MQTTS = "mqtts"; //Протокол MQTTS
const SPROTOCOL_KAFKA = "kafka"; //Протокол для работы с KAFKA
//------------- //-------------
// Тело модуля // Тело модуля
//------------- //-------------
@ -139,6 +146,11 @@ exports.NERR_NTF_SIGN_NO = NERR_NTF_SIGN_NO;
exports.NERR_NTF_SIGN_YES = NERR_NTF_SIGN_YES; exports.NERR_NTF_SIGN_YES = NERR_NTF_SIGN_YES;
exports.SERR_NTF_SIGN_NO = SERR_NTF_SIGN_NO; exports.SERR_NTF_SIGN_NO = SERR_NTF_SIGN_NO;
exports.SERR_NTF_SIGN_YES = SERR_NTF_SIGN_YES; exports.SERR_NTF_SIGN_YES = SERR_NTF_SIGN_YES;
exports.SPROTOCOL_HTTP = SPROTOCOL_HTTP;
exports.SPROTOCOL_HTTPS = SPROTOCOL_HTTPS;
exports.SPROTOCOL_MQTT = SPROTOCOL_MQTT;
exports.SPROTOCOL_MQTTS = SPROTOCOL_MQTTS;
exports.SPROTOCOL_KAFKA = SPROTOCOL_KAFKA;
//Схема валидации функции сервиса //Схема валидации функции сервиса
exports.ServiceFunction = new Schema({ exports.ServiceFunction = new Schema({
@ -156,8 +168,7 @@ exports.ServiceFunction = new Schema({
type: Number, type: Number,
required: true, required: true,
message: { message: {
type: path => type: path => `Идентификатор родительского сервиса функции (${path}) имеет некорректный тип данных (ожидалось - Number)`,
`Идентификатор родительского сервиса функции (${path}) имеет некорректный тип данных (ожидалось - Number)`,
required: path => `Не указан идентификатор родительского сервиса функции (${path})` required: path => `Не указан идентификатор родительского сервиса функции (${path})`
} }
}, },
@ -187,8 +198,7 @@ exports.ServiceFunction = new Schema({
enum: [SFN_TYPE_DATA, SFN_TYPE_LOGIN, SFN_TYPE_LOGOUT], enum: [SFN_TYPE_DATA, SFN_TYPE_LOGIN, SFN_TYPE_LOGOUT],
required: true, required: true,
message: { message: {
type: path => type: path => `Строковый код типа функции сервиса (${path}) имеет некорректный тип данных (ожидалось - String)`,
`Строковый код типа функции сервиса (${path}) имеет некорректный тип данных (ожидалось - String)`,
enum: path => `Значение строкового кода типа функции сервиса (${path}) не поддерживается`, enum: path => `Значение строкового кода типа функции сервиса (${path}) не поддерживается`,
required: path => `Не указан строковый код типа функции сервиса (${path})` required: path => `Не указан строковый код типа функции сервиса (${path})`
} }
@ -218,8 +228,7 @@ exports.ServiceFunction = new Schema({
], ],
required: true, required: true,
message: { message: {
type: path => type: path => `Способ передачи параметров функции сервиса (${path}) имеет некорректный тип данных (ожидалось - Number)`,
`Способ передачи параметров функции сервиса (${path}) имеет некорректный тип данных (ожидалось - Number)`,
enum: path => `Значение способа передачи параметров функции сервиса (${path}) не поддерживается`, enum: path => `Значение способа передачи параметров функции сервиса (${path}) не поддерживается`,
required: path => `Не указан способ передачи параметров функции сервиса (${path})` required: path => `Не указан способ передачи параметров функции сервиса (${path})`
} }
@ -240,10 +249,8 @@ exports.ServiceFunction = new Schema({
], ],
required: true, required: true,
message: { message: {
type: path => type: path => `Строковый код способа передачи параметров функции сервиса (${path}) имеет некорректный тип данных (ожидалось - String)`,
`Строковый код способа передачи параметров функции сервиса (${path}) имеет некорректный тип данных (ожидалось - String)`, enum: path => `Значение строкового кода способа передачи параметров функции сервиса (${path}) не поддерживается`,
enum: path =>
`Значение строкового кода способа передачи параметров функции сервиса (${path}) не поддерживается`,
required: path => `Не указан строковый код способа передачи параметров функции сервиса (${path})` required: path => `Не указан строковый код способа передачи параметров функции сервиса (${path})`
} }
}, },
@ -261,8 +268,7 @@ exports.ServiceFunction = new Schema({
], ],
required: true, required: true,
message: { message: {
type: path => type: path => `График повторной отправки запроса функции сервиса (${path}) имеет некорректный тип данных (ожидалось - Number)`,
`График повторной отправки запроса функции сервиса (${path}) имеет некорректный тип данных (ожидалось - Number)`,
enum: path => `Значение графика повторной отправки запроса функции сервиса (${path}) не поддерживается`, enum: path => `Значение графика повторной отправки запроса функции сервиса (${path}) не поддерживается`,
required: path => `Не указан график повторной отправки запроса функции сервиса (${path})` required: path => `Не указан график повторной отправки запроса функции сервиса (${path})`
} }
@ -283,8 +289,7 @@ exports.ServiceFunction = new Schema({
message: { message: {
type: path => type: path =>
`Строковый код графика повторной отправки запроса функции сервиса (${path}) имеет некорректный тип данных (ожидалось - String)`, `Строковый код графика повторной отправки запроса функции сервиса (${path}) имеет некорректный тип данных (ожидалось - String)`,
enum: path => enum: path => `Значение строкового кода графика повторной отправки запроса функции сервиса (${path}) не поддерживается`,
`Значение строкового кода графика повторной отправки запроса функции сервиса (${path}) не поддерживается`,
required: path => `Не указан строковый код графика повторной отправки запроса функции сервиса (${path})` required: path => `Не указан строковый код графика повторной отправки запроса функции сервиса (${path})`
} }
}, },
@ -295,8 +300,7 @@ exports.ServiceFunction = new Schema({
message: { message: {
type: path => type: path =>
`Идентификатор типового сообщения обмена, обрабатываемого функцией сервиса (${path}) имеет некорректный тип данных (ожидалось - Number)`, `Идентификатор типового сообщения обмена, обрабатываемого функцией сервиса (${path}) имеет некорректный тип данных (ожидалось - Number)`,
required: path => required: path => `Не указан идентификатор типового сообщения обмена, обрабатываемого функцией сервиса (${path})`
`Не указан идентификатор типового сообщения обмена, обрабатываемого функцией сервиса (${path})`
} }
}, },
//Код типового сообщения обмена, обрабатываемого функцией сервиса //Код типового сообщения обмена, обрабатываемого функцией сервиса
@ -314,8 +318,7 @@ exports.ServiceFunction = new Schema({
type: String, type: String,
required: false, required: false,
message: { message: {
type: path => type: path => `Обработчик сообщения со стороны БД для функции сервиса (${path}) имеет некорректный тип данных (ожидалось - String)`,
`Обработчик сообщения со стороны БД для функции сервиса (${path}) имеет некорректный тип данных (ожидалось - String)`,
required: path => `Не указан обработчик сообщения со стороны БД для функции сервиса (${path})` required: path => `Не указан обработчик сообщения со стороны БД для функции сервиса (${path})`
} }
}, },
@ -327,8 +330,7 @@ exports.ServiceFunction = new Schema({
message: { message: {
type: path => type: path =>
`Обработчик сообщения 'до' на строне сервера приложений для функции сервиса (${path}) имеет некорректный тип данных (ожидалось - String)`, `Обработчик сообщения 'до' на строне сервера приложений для функции сервиса (${path}) имеет некорректный тип данных (ожидалось - String)`,
required: path => required: path => `Не указан обработчик сообщения 'до' на строне сервера приложений для функции сервиса (${path})`,
`Не указан обработчик сообщения 'до' на строне сервера приложений для функции сервиса (${path})`,
validateAppSrvFn: path => validateAppSrvFn: path =>
`Обработчик сообщения 'до' на строне сервера приложений для функции сервиса (${path}) имеет некорректный формат, ожидалось: <МОДУЛЬ>.js/<ФУНКЦИЯ>` `Обработчик сообщения 'до' на строне сервера приложений для функции сервиса (${path}) имеет некорректный формат, ожидалось: <МОДУЛЬ>.js/<ФУНКЦИЯ>`
} }
@ -341,8 +343,7 @@ exports.ServiceFunction = new Schema({
message: { message: {
type: path => type: path =>
`Обработчик сообщения 'после' на строне сервера приложений для функции сервиса (${path}) имеет некорректный тип данных (ожидалось - String)`, `Обработчик сообщения 'после' на строне сервера приложений для функции сервиса (${path}) имеет некорректный тип данных (ожидалось - String)`,
required: path => required: path => `Не указан обработчик сообщения 'после' на строне сервера приложений для функции сервиса (${path})`,
`Не указан обработчик сообщения 'после' на строне сервера приложений для функции сервиса (${path})`,
validateAppSrvFn: path => validateAppSrvFn: path =>
`Обработчик сообщения 'после' на строне сервера приложений для функции сервиса (${path}) имеет некорректный формат, ожидалось: <МОДУЛЬ>.js/<ФУНКЦИЯ>` `Обработчик сообщения 'после' на строне сервера приложений для функции сервиса (${path}) имеет некорректный формат, ожидалось: <МОДУЛЬ>.js/<ФУНКЦИЯ>`
} }
@ -355,10 +356,8 @@ exports.ServiceFunction = new Schema({
message: { message: {
type: path => type: path =>
`Признак необходимости аутентификации для исполнения функции сервсиа обмена (${path}) имеет некорректный тип данных (ожидалось - Number)`, `Признак необходимости аутентификации для исполнения функции сервсиа обмена (${path}) имеет некорректный тип данных (ожидалось - Number)`,
enum: path => enum: path => `Значение признака необходимости аутентификации для исполнения функции сервсиа обмена (${path}) не поддерживается`,
`Значение признака необходимости аутентификации для исполнения функции сервсиа обмена (${path}) не поддерживается`, required: path => `Не указан признак необходимости аутентификации для исполнения функции сервсиа обмена (${path})`
required: path =>
`Не указан признак необходимости аутентификации для исполнения функции сервсиа обмена (${path})`
} }
}, },
//Признак необходимости аутентификации для исполнения функции сервсиа обмена (строковый код) //Признак необходимости аутентификации для исполнения функции сервсиа обмена (строковый код)
@ -371,8 +370,7 @@ exports.ServiceFunction = new Schema({
`Строковый код признака необходимости аутентификации для исполнения функции сервсиа обмена (${path}) имеет некорректный тип данных (ожидалось - String)`, `Строковый код признака необходимости аутентификации для исполнения функции сервсиа обмена (${path}) имеет некорректный тип данных (ожидалось - String)`,
enum: path => enum: path =>
`Значение строкового кода признака необходимости аутентификации для исполнения функции сервсиа обмена (${path}) не поддерживается`, `Значение строкового кода признака необходимости аутентификации для исполнения функции сервсиа обмена (${path}) не поддерживается`,
required: path => required: path => `Не указан строковый код признака необходимости аутентификации для исполнения функции сервсиа обмена (${path})`
`Не указан строковый код признака необходимости аутентификации для исполнения функции сервсиа обмена (${path})`
} }
}, },
//Признак оповещения об ошибке исполнения сообщения очереди для функции обработки //Признак оповещения об ошибке исполнения сообщения очереди для функции обработки
@ -383,10 +381,8 @@ exports.ServiceFunction = new Schema({
message: { message: {
type: path => type: path =>
`Признак оповещения об ошибке исполнения сообщения очереди для функции обработки (${path}) имеет некорректный тип данных (ожидалось - Number)`, `Признак оповещения об ошибке исполнения сообщения очереди для функции обработки (${path}) имеет некорректный тип данных (ожидалось - Number)`,
enum: path => enum: path => `Значение признака оповещения об ошибке исполнения сообщения очереди для функции обработки (${path}) не поддерживается`,
`Значение признака оповещения об ошибке исполнения сообщения очереди для функции обработки (${path}) не поддерживается`, required: path => `Не указан признак оповещения об ошибке исполнения сообщения очереди для функции обработки (${path})`
required: path =>
`Не указан признак оповещения об ошибке исполнения сообщения очереди для функции обработки (${path})`
} }
}, },
//Признак оповещения об ошибке исполнения сообщения очереди для функции обработки (строковый код) //Признак оповещения об ошибке исполнения сообщения очереди для функции обработки (строковый код)
@ -399,8 +395,7 @@ exports.ServiceFunction = new Schema({
`Строковый код признака оповещения об ошибке исполнения сообщения очереди для функции обработки (${path}) имеет некорректный тип данных (ожидалось - String)`, `Строковый код признака оповещения об ошибке исполнения сообщения очереди для функции обработки (${path}) имеет некорректный тип данных (ожидалось - String)`,
enum: path => enum: path =>
`Значение строкового кода признака оповещения об ошибке исполнения сообщения очереди для функции обработки (${path}) не поддерживается`, `Значение строкового кода признака оповещения об ошибке исполнения сообщения очереди для функции обработки (${path}) не поддерживается`,
required: path => required: path => `Не указан строковый код признака оповещения об ошибке исполнения сообщения очереди для функции обработки (${path})`
`Не указан строковый код признака оповещения об ошибке исполнения сообщения очереди для функции обработки (${path})`
} }
}, },
//Список адресов E-Mail для оповещения об ошибке исполнения сообщения очереди для функции обработки //Список адресов E-Mail для оповещения об ошибке исполнения сообщения очереди для функции обработки
@ -411,8 +406,7 @@ exports.ServiceFunction = new Schema({
message: { message: {
type: path => type: path =>
`Список адресов E-Mail для оповещения об ошибке исполнения сообщения очереди для функции обработки (${path}) имеет некорректный тип данных (ожидалось - String)`, `Список адресов E-Mail для оповещения об ошибке исполнения сообщения очереди для функции обработки (${path}) имеет некорректный тип данных (ожидалось - String)`,
required: path => required: path => `Не указан список адресов E-Mail для оповещения об ошибке исполнения сообщения очереди для функции обработки (${path})`,
`Не указан список адресов E-Mail для оповещения об ошибке исполнения сообщения очереди для функции обработки (${path})`,
validateErrNtfMail: path => validateErrNtfMail: path =>
`Неверный формат списка адресов E-Mail для оповещения об ошибке исполнения сообщения очереди для функции обработки (${path}), для указания нескольких адресов следует использовать запятую в качестве разделителя (без пробелов)` `Неверный формат списка адресов E-Mail для оповещения об ошибке исполнения сообщения очереди для функции обработки (${path}), для указания нескольких адресов следует использовать запятую в качестве разделителя (без пробелов)`
} }

844
package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@ -21,8 +21,10 @@
"body-parser": "^1.19.0", "body-parser": "^1.19.0",
"cors": "^2.8.5", "cors": "^2.8.5",
"express": "^4.17.1", "express": "^4.17.1",
"kafkajs": "^2.2.4",
"lodash": "^4.17.19", "lodash": "^4.17.19",
"module-alias": "^2.2.2", "module-alias": "^2.2.2",
"mqtt": "^5.10.1",
"nodemailer": "^6.4.11", "nodemailer": "^6.4.11",
"oracledb": "^4.2.0", "oracledb": "^4.2.0",
"request": "^2.88.2", "request": "^2.88.2",