1

I am working on Camunda modeler to add a diagram type based on the open source project. I would like to add a brand-new diagram type beyond the default diagrams like BPMN DMN and CMMN. I can call it Activity Diagram. Adding a new diagram means Camunda engine should be able to recognize and show it on the UI. For example, I should be able to run the following code without any issue in the base project.

const element = event.element;
  if (is(element, 'bpmn:ManualTask')) {
    // Code to open and render the new Activity Diagram
    const activityDiagramXML = `<activityDiagram xmlns="http://example.com/schema/activityDiagram">
      <metrics id="metrics1"/>
      <operation id="operation1"/>
    </activityDiagram>`;
    
    BPMNJS.importXML(activityDiagramXML, (err) => {
      if (!err) {
        console.log('Activity Diagram rendered');
      } else {
        console.error('Failed to render Activity Diagram', err);
      }
    });
  }

Unfortunately, the default importXML function only support default xml tags like bpmn:Definitions. Here is the piece of code of the function.

BaseViewer.prototype.importXML = async function importXML(xml, bpmnDiagram) {

    const self = this;
  
    function ParseCompleteEvent(data) {
      return self.get('eventBus').createEvent(data);
    }
  
    let aggregatedWarnings = [];
    try {
  
      // hook in pre-parse listeners +
      // allow xml manipulation
  
      /**
       * A `import.parse.start` event.
       *
       * @event BaseViewer#ImportParseStartEvent
       * @type {ImportParseStartEvent}
       */
      xml = this._emit('import.parse.start', { xml: xml }) || xml;
  
      let parseResult;
      try {
        parseResult = await this._moddle.fromXML(xml, 'bpmn:Definitions');
      } catch (error) {
        this._emit('import.parse.complete', {
          error
        });
  
        throw error;
      }
  
      let definitions = parseResult.rootElement;
      const references = parseResult.references;
      const parseWarnings = parseResult.warnings;
      const elementsById = parseResult.elementsById;
  
      aggregatedWarnings = aggregatedWarnings.concat(parseWarnings);
  
      // hook in post parse listeners +
      // allow definitions manipulation
  
      /**
       * A `import.parse.complete` event.
       *
       * @event BaseViewer#ImportParseCompleteEvent
       * @type {ImportParseCompleteEvent}
       */
      definitions = this._emit('import.parse.complete', ParseCompleteEvent({
        error: null,
        definitions: definitions,
        elementsById: elementsById,
        references: references,
        warnings: aggregatedWarnings
      })) || definitions;
  
      const importResult = await this.importDefinitions(definitions, bpmnDiagram);
  
      aggregatedWarnings = aggregatedWarnings.concat(importResult.warnings);
  
      /**
       * A `import.parse.complete` event.
       *
       * @event BaseViewer#ImportDoneEvent
       * @type {ImportDoneEvent}
       */
      this._emit('import.done', { error: null, warnings: aggregatedWarnings });
  
      return { warnings: aggregatedWarnings };
    } catch (err) {
      let error = err;
      aggregatedWarnings = aggregatedWarnings.concat(error.warnings || []);
      addWarningsToError(error, aggregatedWarnings);
  
      error = checkValidationError(error);
  
      this._emit('import.done', { error, warnings: error.warnings });
  
      throw error;
    }
};

How can I add a new diagram type to Camunda modeler and make the use of xml tags like <activityDiagram> and <operation> possible?

I wrote 2 files, namely, activityDiagramModdle.js and ActivityDiagramRendered.js to support the new diagram type and registered them properly to make Camunda model recognize them. But when I try to render the new diagram type, it causes the following error: failed to parse document as <bpmn:Definitions>. Any help or suggestion would be welcome.

Here are my files. activityDiagramModdle.js

export default {
  name: 'ActivityDiagram',
  prefix: 'ad',
  uri: 'http://example.com/schema/activityDiagram',
  xml: {
    tagAlias: 'lowerCase'
  },
  types: [
    {
      name: 'ActivityDiagram',
      superClass: ['Element'],
      properties: [
        {
          name: 'metrics',
          isMany: true,
          type: 'ad:Metrics'
        },
        {
          name: 'operation',
          isMany: true,
          type: 'ad:Operation'
        }
      ]
    },
    {
      name: 'Metrics',
      superClass: ['Element'],
      properties: [
        {
          name: 'id',
          isAttr: true,
          type: 'String'
        }
      ]
    },
    {
      name: 'Operation',
      superClass: ['Element'],
      properties: [
        {
          name: 'id',
          isAttr: true,
          type: 'String'
        }
      ]
    }
  ]
};

ActivityDiagramRendered.js

import BaseRenderer from 'diagram-js/lib/draw/BaseRenderer';

export default class ActivityDiagramRenderer extends BaseRenderer {
  constructor(eventBus, bpmnRenderer) {
    super(eventBus, 2000);

    this.bpmnRenderer = bpmnRenderer;
  }

  canRender(element) {
    return /^ad:/.test(element.type);
  }

  drawShape(parentNode, element) {
    const shape = this.bpmnRenderer.drawShape(parentNode, element);

    if (element.type === 'ad:Metrics') {
      shape.attr({
        fill: 'green'
      });
    } else if (element.type === 'ad:Operation') {
      shape.attr({
        fill: 'blue'
      });
    }

    return shape;
  }
}

ActivityDiagramRenderer.$inject = ['eventBus', 'bpmnRenderer'];

0

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.