UNPKG

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

Version:

## Peer dependencies

125 lines (124 loc) 5.54 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.Node = exports.BaseUrlsConfig = exports.NodeEnvConfig = void 0; const tslib_1 = require("tslib"); const z = tslib_1.__importStar(require("zod")); const errors_1 = require("./errors"); const nodes_config_env_1 = require("./nodes-config-env"); exports.NodeEnvConfig = z.object({ EWX_SOLUTION_ID: z.string().optional(), EWX_SOLUTION_GROUP_ID: z.string().optional(), EWX_WORKLOGIC_ID: z.string().optional(), EWX_SQLITE_PATH: z.string().optional(), EWX_WORKER_ADDRESS: z.string().optional(), BASE_URL_CDN: z.string().optional(), }); exports.BaseUrlsConfig = z.object({ kafka_url: z.union([z.string(), z.array(z.string())]).optional(), kafka_proxy_url: z.string().optional(), indexer_url: z.string().optional(), rpc_url: z.string().optional(), }); class Node { constructor(api, nodeConfig, // this is NodeDef from node-red, but let's keep it unknown for convenience (we add custom types there) messageZod, { validateMessage = true } = {}) { this.nodeConfig = nodeConfig; this.messageZod = messageZod; // node-red will bind its functions to a node constructor, // therefore if we pass `this` to API, full instance with all node-red things will be available this.api = api.cloneAndInstantiate(this, nodeConfig); this.api.on('input', (msg, done) => { const validator = validateMessage ? this.messageZod : z.object({}).passthrough(); const parseResult = validator.safeParse(msg); if (!parseResult.success) { this.api.error(`[${this.constructor.name}] Invalid input message .payload: ${JSON.stringify(msg)}. Errors: ${parseResult.error}`); done(new errors_1.GGPError(errors_1.ErrorCode.InvalidPayload, { payload: JSON.stringify(msg), error: parseResult.error.message })); return; } this.handleMaybePromise(() => this.onInput(parseResult.data), done); }); this.api.on('close', (done) => { this.handleMaybePromise(() => (this.onDestroy ? this.onDestroy() : undefined), done); }); } /** * Returns base_urls from a special endpoint maintained by EWF that contains configuration */ async getBaseUrls() { // Default is here, because this is a new feature, so we have time to migrate // @TODO remove default at some point so its not confusing const baseUrl = this.getNodeEnvConfig().BASE_URL_CDN || nodes_config_env_1.nodesGlobalEnvConfig.BASE_URL_CDN || 'https://marketplace-cdn.energyweb.org/base_urls.json'; const response = await fetch(baseUrl, { method: 'GET' }).then(response => response.json()); return exports.BaseUrlsConfig.parse(response); } /** * Returns parsed `.__envConfig` from node's config. * node's config, is basically one entry in flow.json * Marketplace will be injecting that property to each node before node-red start * Note, that if `.__envConfig` is injected that way, even the slightest node change (through node-red GUI) removes that property * because it is unknown to node-red. */ getNodeEnvConfig() { const envConfig = this.nodeConfig.__envConfig; if (!envConfig) { return {}; } return exports.NodeEnvConfig.parse(envConfig); } /** * SendBuilder hides complexity of creating proper messages, and selecting output. * It is supposed to be used by all nodes to send messages to outputs * to ensure correct behavior. * * It uses message received on node input (passed manually), and then the payload should be only * >added< (not replaced!) to the input message. Optionally topic can be set. * * Note that if topic is not set explicitly, it will be removed (because topic is used * to describe what the node receiving it should do with the message, so it's not meant to be passed around * like message payload). */ sendBuilder(inputMessage) { const internalBuilder = (message) => ({ addPayload: (payload) => internalBuilder({ ...message, payload: { ...message.payload, ...payload, } }), setTopic: (topic) => internalBuilder({ ...message, topic, }), sendToOutput: (index) => { const messages = new Array(index + 1).fill(null); messages[index] = message; this.api.send(messages); }, getMessage: () => message, }); const { topic, ...messageWithoutTopic } = inputMessage; return internalBuilder(messageWithoutTopic); } handleMaybePromise(maybePromiseCb, done) { try { const maybePromise = maybePromiseCb(); if (maybePromise instanceof Promise) { void maybePromise.then(() => { done(); }).catch((err) => { this.api.error(`[${this.constructor.name}] Error: ${err.message} ${err.stack}`); done(err); }); } else { done(); } } catch (err) { this.api.error(`[${this.constructor.name}] Error: ${err.message} ${err.stack}`); done(err); } } } exports.Node = Node;