zeebe-node
Version:
The Node.js client library for the Zeebe Workflow Automation Engine.
276 lines (271 loc) • 11.1 kB
JavaScript
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
;