UNPKG

@energyweb/node-red-contrib-green-proof-worker

Version:

## Peer dependencies

153 lines (152 loc) 6 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.SourceHttpApiHttpAck = void 0; const tslib_1 = require("tslib"); const promises_1 = require("timers/promises"); const z = tslib_1.__importStar(require("zod")); const errors_1 = require("../errors"); const node_1 = require("../node"); const nodes_config_env_1 = require("../nodes-config-env"); const InputMessage = z.union([ z.object({ topic: z.literal('finished-processing'), payload: z.object({ httpApiMessageId: z.string(), }) }), z.object({ topic: z.literal('force-reset') }) ]); const Config = z.object({ namespace: z.string(), host: z.string(), workerId: z.string(), }); const HttpMessage = z.object({ id: z.string(), content: z.any(), }); const SourceHttpApiHttpAck = (api) => class SourceHttpApi extends node_1.Node { constructor(config) { super(api, config, InputMessage); /** Used to orchestrate consumer retry */ this.isDestroying = false; this.nextMessageWaitTime = 10000; this.pendingProcessing = null; const envConfig = this.getNodeEnvConfig(); this.config = (async () => { return Config.parse({ host: config.host || nodes_config_env_1.nodesGlobalEnvConfig.SOURCE_HTTP_API_HOST || await this.getBaseUrls().then(c => c.kafka_proxy_url), namespace: envConfig.EWX_SOLUTION_ID || nodes_config_env_1.nodesGlobalEnvConfig.VOTING_SERVICE_CONFIG_SOLUTION_NAMESPACE, workerId: envConfig.EWX_WORKER_ADDRESS || nodes_config_env_1.nodesGlobalEnvConfig.VOTING_SERVICE_CONFIG_SOLUTION_NAMESPACE }); })(); void this.getMessageLoop(); } async getMessageLoop() { this.api.log('HTTP API Source started'); while (!this.isDestroying) { try { const result = await this.getMessage(); if (result) { const message = HttpMessage.parse(result); this.api.log(`Processing message id: ${message.id}`); this.setStatus('pending'); this .sendBuilder({}) .addPayload({ ...message.content, httpApiMessageId: message.id, }) .sendToOutput(0); await new Promise((resolve, reject) => { this.pendingProcessing = { messageId: message.id, resolve, reject, }; }); await this.ackMessage(message.id); this.api.log(`Message processed: ${message.id}`); } else { this.api.log('No message available. Waiting before next retry'); this.setStatus('waiting'); await (0, promises_1.setTimeout)(this.nextMessageWaitTime); } } catch (e) { this.api.error(e.message); this.setStatus('error'); await (0, promises_1.setTimeout)(this.nextMessageWaitTime); } } } setStatus(status) { switch (status) { case 'waiting': const waitingTime = (this.nextMessageWaitTime / 1000).toFixed(2); this.api.status({ fill: 'green', shape: 'ring', text: `Waiting ${waitingTime} seconds` }); break; case 'error': const waitingTimeError = (this.nextMessageWaitTime / 1000).toFixed(2); this.api.status({ fill: 'red', shape: 'ring', text: `Encountered error. Waiting ${waitingTimeError} seconds` }); break; case 'pending': this.api.status({ fill: 'yellow', shape: 'dot', text: `Waiting for message (offset ${this.pendingProcessing?.messageId}) to be processed` }); break; } } /** * Input is responsible for resolving promise that http api is waiting for */ onInput(message) { if (!this.pendingProcessing) { return; } if (message.topic === 'force-reset') { this.pendingProcessing.reject(new Error('HTTP API: Force resetted')); this.pendingProcessing = null; return; } if (this.pendingProcessing.messageId !== message.payload.httpApiMessageId) { throw new errors_1.GGPError(errors_1.ErrorCode.SourceKafkaUnexpectedMessage, { expected: this.pendingProcessing.messageId, received: message.payload.httpApiMessageId }); } this.pendingProcessing.resolve(); this.pendingProcessing = null; } async getMessage() { const url = await this.buildGetMessageUrl(); const result = await fetch(url, { method: 'GET' }).then(r => r.json()); return result; } async buildGetMessageUrl() { const config = await this.config; const url = new URL('/api/message/next', config.host); url.searchParams.set('namespace', config.namespace); url.searchParams.set('workerId', config.workerId); return url; } async ackMessage(messageId) { const config = await this.config; const url = new URL('/api/message/ack', config.host); await fetch(url, { method: 'POST', body: JSON.stringify({ messageId, workerId: config.workerId }), headers: { 'Content-Type': 'application/json' } }); } onDestroy() { this.isDestroying = true; if (this.pendingProcessing) { this.pendingProcessing.reject(new Error('HTTP API: Destroying node')); } } }; exports.SourceHttpApiHttpAck = SourceHttpApiHttpAck;