UNPKG

@fabric-es/fabric-cqrs

Version:

Hyperledger Fabric middleware for event sourcing and cqrs pattern

196 lines 11 kB
"use strict"; var __importDefault = (this && this.__importDefault) || function (mod) { return (mod && mod.__esModule) ? mod : { "default": mod }; }; Object.defineProperty(exports, "__esModule", { value: true }); exports.createQueryHandler = void 0; const util_1 = __importDefault(require("util")); const debug_1 = __importDefault(require("debug")); const store_1 = require("../store"); const projection_1 = require("../store/projection"); const query_1 = require("../store/query"); const reconcile_1 = require("../store/reconcile"); const utils_1 = require("../utils"); const constants_1 = require("./constants"); const createQueryHandler = (options) => { const debug = debug_1.default('queryHandler:createQueryHandler'); const { entityNames, gateway, queryDatabase, channelName, wallet, connectionProfile, reducers, pubSub, } = options; const logger = utils_1.getLogger({ name: '[query-handler] createQueryHandler.js' }); options.queryDatabase = queryDatabase; options.logger = logger; const store = store_1.getStore(options); let contractListener; let contract; let network; const commandOption = { logger, wallet, store, connectionProfile, channelName }; const queryOption = { logger, store }; let isReconciled = false; return { clearNotification: async ({ creator, entityName, id, commitId }) => utils_1.dispatcher((payload) => query_1.action.clearNotification(payload), { store, logger, name: 'query:clearNotification', slice: 'query', SuccessAction: query_1.action.CLEAR_NOTI_SUCCESS, ErrorAction: query_1.action.CLEAR_NOTI_ERROR, })({ creator, entityName, id, commitId }), clearNotifications: async ({ creator, entityName, id }) => utils_1.dispatcher((payload) => query_1.action.clearNotifications(payload), { store, logger, name: 'query:clearNotifications', slice: 'query', SuccessAction: query_1.action.CLEAR_NOTI_SUCCESS, ErrorAction: query_1.action.CLEAR_NOTI_ERROR, })({ creator, entityName, id }), create: (entityName) => { if (!entityNames.includes(entityName)) throw new Error(constants_1.INVALID_ARG); return utils_1.commandCreate(entityName, false, commandOption); }, command_deleteByEntityId: (entityName) => { if (!entityNames.includes(entityName)) throw new Error(constants_1.INVALID_ARG); return utils_1.commandDeleteByEntityId(entityName, false, commandOption); }, command_getByEntityName: (entityName) => { if (!entityNames.includes(entityName)) throw new Error(constants_1.INVALID_ARG); return utils_1.commandGetByEntityName(entityName, false, commandOption); }, getById: (entityName) => utils_1.queryGetById(entityName, reducers[entityName], false, commandOption), getByEntityName: (entityName) => utils_1.queryGetEntityByEntityName(entityName, reducers[entityName], queryOption), getCommitById: (entityName) => utils_1.queryGetCommitByEntityId(entityName, queryOption), query_deleteCommitByEntityId: (entityName) => utils_1.queryDeleteCommitByEntityId(entityName, queryOption), query_deleteCommitByEntityName: (entityName) => utils_1.queryDeleteCommitByEntityName(entityName, queryOption), query_deleteEntityByEntityName: (entityName) => () => utils_1.dispatcher((payload) => query_1.action.deleteEntityByEntityName(payload), { store, logger, slice: 'query', name: 'query:deleteEntityByEntityName', SuccessAction: query_1.action.DELETE_ENTITY_SUCCESS, ErrorAction: query_1.action.DELETE_ENTITY_ERROR, })({ entityName }), fullTextSearchCommit: async ({ query, param, cursor, pagesize }) => utils_1.queryFullTextSearch({ store, logger, query, param, cursor, pagesize }), fullTextSearchEntity: async ({ query, param, cursor, pagesize, entityName }) => utils_1.queryFullTextSearch({ store, logger, query, param, cursor, pagesize, entityName }), getNotification: async ({ creator, entityName, id, commitId }) => utils_1.dispatcher((payload) => query_1.action.getNotifications(payload), { store, logger, name: 'query:getNotification', slice: 'query', SuccessAction: query_1.action.GET_NOTI_SUCCESS, ErrorAction: query_1.action.GET_NOTI_ERROR, })({ creator, entityName, id, commitId }), getNotifications: async ({ creator, entityName, id }) => utils_1.dispatcher((payload) => query_1.action.getNotifications(payload), { store, logger, name: 'query:getNotifications', slice: 'query', SuccessAction: query_1.action.GET_NOTI_SUCCESS, ErrorAction: query_1.action.GET_NOTI_ERROR, })({ creator, entityName, id }), disconnect: () => gateway.disconnect(), reconcile: (payload) => (() => utils_1.dispatcher(({ tx_id, args }) => reconcile_1.action.reconcile({ tx_id, args, store, channelName, connectionProfile, wallet, }), { name: 'reconcile', store, slice: 'reconcile', SuccessAction: reconcile_1.action.RECONCILE_SUCCESS, ErrorAction: reconcile_1.action.RECONCILE_ERROR, logger, }))()(payload).then((result) => { isReconciled = true; return result; }), reconciled: () => (isReconciled = true), subscribeHub: async (entityNames) => { logger.info('♨️ subscribe channel event hub'); try { network = await gateway.getNetwork(channelName); } catch (err) { logger.error(util_1.default.format('❌ failed to subscribeHub. cannot getNetwork from gateway', err)); return Promise.reject(new Error('failed to subscribeHub. cannot getNetwork from gateway')); } contract = network.getContract('eventstore'); try { contractListener = await network.getContract('eventstore').addContractListener(async ({ payload, eventName, getTransactionEvent }) => { var _a; logger.info(`💢 event arrives - tx_id: ${getTransactionEvent().transactionId}`); if (!isReconciled) { logger.info(`pending reconciliation, skipping 'merge_one_entity'...`); return; } let commit; if (eventName !== 'createCommit') { logger.warn(`receive unexpected contract event: ${eventName}`); return; } try { commit = JSON.parse(payload.toString('utf8')); } catch (e) { logger.error(util_1.default.format('fail to parse contract event, %j', e)); return; } if (utils_1.isCommit(commit)) { logger.info(`💢 event arrives - commitId: ${commit.id} found`); if (!entityNames.includes(commit === null || commit === void 0 ? void 0 : commit.entityName)) { logger.info(util_1.default.format('receive commit of unsubscribed entityName, %s:%s', commit.entityName, commit.id)); return; } const mergeEntityResult = await utils_1.dispatcher((payload) => projection_1.action.mergeEntity(payload), { name: 'merge_one_entity', store, slice: 'projection', SuccessAction: projection_1.action.MERGE_ENTITY_SUCCESS, ErrorAction: projection_1.action.MERGE_ENTITY_ERROR, logger, })({ commit }); if (mergeEntityResult.status === 'OK') { debug('subscribeHub:mergeEntityResult, %O', mergeEntityResult); logger.info(util_1.default.format('mergeComit: %j', (mergeEntityResult === null || mergeEntityResult === void 0 ? void 0 : mergeEntityResult.data) || 'no data written')); } else logger.error(util_1.default.format('%s ❌ fail to mergeEntity, %j', constants_1.FATAL, mergeEntityResult)); if (pubSub && mergeEntityResult.status === 'OK') { const events = []; commit.events.forEach((event) => events.push(event === null || event === void 0 ? void 0 : event.type)); await pubSub .publish('COMMIT_ARRIVED', { entityAdded: { commit, events, key: (_a = mergeEntityResult === null || mergeEntityResult === void 0 ? void 0 : mergeEntityResult.data[0]) === null || _a === void 0 ? void 0 : _a.key, }, }) .catch((e) => { logger.error(util_1.default.format('fail to publish commit, %j, %j ', commit, e)); }); } else if (pubSub && mergeEntityResult.status !== 'OK') { await pubSub.publish('SYSTEM_EVENT', { systemEvent: Object.assign(Object.assign({}, mergeEntityResult), { error: JSON.stringify(mergeEntityResult === null || mergeEntityResult === void 0 ? void 0 : mergeEntityResult.error), event: 'FAIL_TO_MERGE_ENTITY_TO_QDB', timestamp: Date.now() }), }); } } else logger.error(util_1.default.format('receive commit of unknown type, %j', commit)); }, { type: 'full' }); } catch (err) { logger.error(util_1.default.format('❌ failed to subscribeHub. cannot addContractlistner', err)); return Promise.reject(new Error('failed to subscribeHub. cannot addContractlistner')); } return true; }, unsubscribeHub: () => contract.removeContractListener(contractListener), }; }; exports.createQueryHandler = createQueryHandler; //# sourceMappingURL=createQueryHandler.js.map