UNPKG

zeebe-node

Version:

The Node.js client library for the Zeebe Workflow Automation Engine.

276 lines (271 loc) 11.1 kB
"use strict"; var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) { if (k2 === undefined) k2 = k; Object.defineProperty(o, k2, { enumerable: true, get: function() { return m[k]; } }); }) : (function(o, m, k, k2) { if (k2 === undefined) k2 = k; o[k2] = m[k]; })); var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) { Object.defineProperty(o, "default", { enumerable: true, value: v }); }) : function(o, v) { o["default"] = v; }); var __importStar = (this && this.__importStar) || function (mod) { if (mod && mod.__esModule) return mod; var result = {}; if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k); __setModuleDefault(result, mod); return result; }; Object.defineProperty(exports, "__esModule", { value: true }); exports.BpmnParser = void 0; const fast_xml_parser_1 = require("fast-xml-parser"); const fs = require("fs"); const path = __importStar(require("path")); // Converts, for example, task_type or task-type to TaskType const getSafeName = tasktype => tasktype .split('_') .map(([f, ...r]) => [f.toUpperCase(), ...r].join('')) .join('') .split('-') .map(([f, ...r]) => [f.toUpperCase(), ...r].join('')) .join(''); const getConstantName = name => name .split('-') .join('_') .split(' ') .join('_') .toUpperCase(); const toArray = stringOrArray => Array.isArray(stringOrArray) ? stringOrArray : [stringOrArray]; class BpmnParser { /** * Read BPMN files and return an array of one or more parsed BPMN objects. * @param filenames - A single BPMN file path, or array of BPMN file paths. */ static parseBpmn(filenames) { if (typeof filenames === 'string') { filenames = [filenames]; } return filenames.map(filename => { const xmlData = fs.readFileSync(filename).toString(); if (fast_xml_parser_1.XMLValidator.validate(xmlData)) { return BpmnParser.parser.parse(xmlData); } return {}; }); } // @ TODO: examine Camunda's parse BPMN code // https://github.com/camunda/camunda-bpmn-model/tree/master/src/main/java/org/camunda/bpm/model/bpmn static getProcessId(bpmnString) { var _a, _b; const jsonObj = BpmnParser.parser.parse(bpmnString); return (_b = (_a = jsonObj === null || jsonObj === void 0 ? void 0 : jsonObj['bpmn:definitions']) === null || _a === void 0 ? void 0 : _a['bpmn:process']) === null || _b === void 0 ? void 0 : _b['@_id']; } // Produce a starter worker file from a BPMN file static async scaffold(filename) { const removeUndefined = t => !!t; const buildEnumDictionaryFromArray = (a) => a .filter(removeUndefined) .map(t => ({ [t]: getConstantName(t) })) .reduce((prev, curr) => ({ ...prev, ...curr }), {}); const bpmnObject = BpmnParser.parseBpmn(filename)[0]; const taskTypes = await BpmnParser.getTaskTypes(bpmnObject); const taskEnumDict = buildEnumDictionaryFromArray(taskTypes); const interfaces = await BpmnParser.generateConstantsForBpmnFiles(filename); const headerInterfaces = {}; // mutated in the recursive function await scanForHeadersRecursively(bpmnObject); const importStmnt = `import { ZBClient } from "zeebe-node" const zbc = new ZBClient() `; const genericWorkflowVariables = `// @TODO Update with the shape of your job variables // For better intellisense and type-safety export interface WorkflowVariables { [key: string]: any }`; const workers = taskTypes .map(t => `// Worker for tasks of type "${t}" ${headerInterfaces[t] ? headerInterfaces[t].join('|') : 'type ' + getSafeName(t) + 'CustomHeaders = never'} export const ${getSafeName(t)}Worker = zbc.createWorker< WorkflowVariables, ${getSafeName(t)}CustomHeaders, WorkflowVariables >({ taskType: TaskType.${taskEnumDict[t]}, taskHandler: job => { console.log(job) return job.complete() } }) `) .join('\n'); return `${importStmnt} ${genericWorkflowVariables} ${interfaces} ${workers}`; async function scanForHeadersRecursively(obj) { if (!(obj instanceof Object)) { return; } for (const k in obj) { if (obj.hasOwnProperty(k)) { if (k === 'bpmn:serviceTask') { const tasks = toArray(obj[k]); tasks.forEach(t => { const taskHeaders = t['bpmn:extensionElements']['zeebe:taskHeaders']; const customHeaderNames = taskHeaders ? toArray(taskHeaders['zeebe:header']).map(h => h['@_key']) : undefined; const tasktype = t['bpmn:extensionElements']['zeebe:taskDefinition']['@_type']; const headerInterfaceName = getSafeName(tasktype); if (customHeaderNames) { const headerInterfaceDfnBody = customHeaderNames .sort() .map(h => ' ' + h + ': string') .join('\n'); const headerInterfaceDfn = `interface ${headerInterfaceName}CustomHeaders { ${headerInterfaceDfnBody} }`; if (!headerInterfaces[tasktype]) { headerInterfaces[tasktype] = [ headerInterfaceDfn, ]; } else { if (headerInterfaces[tasktype].filter(d => d === headerInterfaceDfn).length === 0) { headerInterfaces[tasktype].push(`{ ${headerInterfaceDfnBody} }`); } } } }); } else { // recursive call to scan property await scanForHeadersRecursively(obj[k]); } } } } } /** * Generate TypeScript constants for task types and message names in BPMN files * @param filenames - a BPMN file path or array of BPMN file paths */ static async generateConstantsForBpmnFiles(filenames) { const removeUndefined = t => !!t; const buildEnumListFromArray = a => a .filter(removeUndefined) .map(t => ` ${getConstantName(t)} = "${t}"`) .join(',\n'); if (typeof filenames === 'string') { filenames = [filenames]; } const parsed = BpmnParser.parseBpmn(filenames); const taskTypes = await BpmnParser.getTaskTypes(parsed); const messageNames = await BpmnParser.getMessageNames(parsed); const files = filenames.map(f => path.basename(f)); const taskEnumMembers = buildEnumListFromArray(taskTypes); const messageEnumMembers = buildEnumListFromArray(messageNames); return ` // Autogenerated constants for ${files} export enum TaskType { ${taskEnumMembers} } export enum MessageName { ${messageEnumMembers} } `; } /** * Take one or more parsed BPMN objects and return an array of unique task types. * @param processes - A parsed BPMN object, or an array of parsed BPMN objects. */ static async getTaskTypes(processes) { const processArray = toArray(processes); return BpmnParser.mergeDedupeAndSort(await Promise.all(processArray.map(BpmnParser.scanBpmnObjectForTasks))); } /** * Take one or more parsed BPMN objects and return an array of unique message names. * @param processes - A parsed BPMN object, or an array of parsed BPMN objects. */ static async getMessageNames(processes) { const processArray = toArray(processes); return BpmnParser.mergeDedupeAndSort(await Promise.all(processArray.map(BpmnParser.scanBpmnObjectForMessages))); } static mergeDedupeAndSort(arr) { return [...new Set([].concat(...arr).sort())]; } /** * Return an array of task types. * @param bpmnObject - A parsed Bpmn object. */ static async scanBpmnObjectForTasks(bpmnObject) { let taskTypes = []; // mutated in the recursive function await scanRecursively(bpmnObject); return [...new Set(taskTypes.sort())]; async function scanRecursively(obj) { if (!(obj instanceof Object)) { return; // not an Object so obj[k] here is a value } for (const k in obj) { if (obj.hasOwnProperty(k)) { if (k === 'bpmn:serviceTask') { const tasks = toArray(obj[k]); taskTypes = taskTypes.concat(tasks.map(t => t['bpmn:extensionElements']['zeebe:taskDefinition']['@_type'])); } else { // recursive call to scan property await scanRecursively(obj[k]); } } } } } /** * Return an array of message names. * @param bpmnObject - A parsed Bpmn object. */ static async scanBpmnObjectForMessages(bpmnObject) { let messageNames = []; // mutated in the recursive function await scanRecursively(bpmnObject); return [...new Set(messageNames.sort())]; async function scanRecursively(obj) { if (!(obj instanceof Object)) { return; // not an Object so obj[k] here is a value } for (const k in obj) { if (obj.hasOwnProperty(k)) { if (k === 'bpmn:message') { const messages = toArray(obj[k]); messageNames = messageNames.concat(messages.map(m => m['@_name'])); } else { // recursive call to scan property await scanRecursively(obj[k]); } } } } } } exports.BpmnParser = BpmnParser; BpmnParser.parserOptions = { allowBooleanAttributes: false, attrNodeName: 'attr', attributeNamePrefix: '@_', cdataPositionChar: '\\c', cdataTagName: '__cdata', ignoreAttributes: false, ignoreNameSpace: false, localeRange: '', parseAttributeValue: false, parseNodeValue: true, parseTrueNumberOnly: false, textNodeName: '#text', trimValues: true, }; BpmnParser.parser = new fast_xml_parser_1.XMLParser(BpmnParser.parserOptions); //# sourceMappingURL=BpmnParser.js.map