fix(import): forgive invalid flowElements
Instead of failing hard when we parse invalid flow elements (i.e. TextAnnotation) we log a warning that may be handled by the diagram import. Related to #74
This commit is contained in:
parent
55d9215e62
commit
8ad29d034d
|
@ -1,6 +1,6 @@
|
|||
var _ = require('lodash');
|
||||
|
||||
function BpmnTraverser(handler) {
|
||||
function BpmnTreeWalker(handler) {
|
||||
|
||||
var elementDiMap = {};
|
||||
var elementGfxMap = {};
|
||||
|
@ -26,7 +26,7 @@ function BpmnTraverser(handler) {
|
|||
|
||||
// avoid multiple rendering of elements
|
||||
if (gfx) {
|
||||
return gfx;
|
||||
throw new Error('already rendered <' + element.id + '>');
|
||||
}
|
||||
|
||||
// call handler
|
||||
|
@ -133,8 +133,8 @@ function BpmnTraverser(handler) {
|
|||
|
||||
// walk through all processes that have not yet been drawn and draw them
|
||||
// (in case they contain lanes with DI information)
|
||||
var processes = _.forEach(rootElements, function(e) {
|
||||
return e.$type === 'bpmn:Process' && e.laneSets && handledProcesses.indexOf(e) !== -1;
|
||||
var processes = _.filter(rootElements, function(e) {
|
||||
return e.$type === 'bpmn:Process' && e.laneSets && handledProcesses.indexOf(e) === -1;
|
||||
});
|
||||
|
||||
processes.forEach(contextual(handleProcess));
|
||||
|
@ -272,7 +272,9 @@ function BpmnTraverser(handler) {
|
|||
if (is(e, 'bpmn:DataObjectReference')) {
|
||||
handleDataElement(e, context);
|
||||
} else {
|
||||
throw new Error('unrecognized element <' + e.$type + '> in context ' + (context ? context.id : null));
|
||||
logError(
|
||||
'unrecognized flowElement <' + e.$type + '> in context ' + (context ? context.id : null),
|
||||
{ element: e, context: context });
|
||||
}
|
||||
});
|
||||
|
||||
|
@ -326,4 +328,4 @@ function BpmnTraverser(handler) {
|
|||
};
|
||||
}
|
||||
|
||||
module.exports = BpmnTraverser;
|
||||
module.exports = BpmnTreeWalker;
|
|
@ -110,6 +110,7 @@ function importBpmnDiagram(diagram, definitions, done) {
|
|||
data.label = label;
|
||||
}
|
||||
|
||||
var warnings = [];
|
||||
|
||||
var visitor = {
|
||||
|
||||
|
@ -161,16 +162,23 @@ function importBpmnDiagram(diagram, definitions, done) {
|
|||
},
|
||||
|
||||
error: function(message, context) {
|
||||
console.warn('[import]', message, context);
|
||||
warnings.push({ message: message, context: context });
|
||||
}
|
||||
};
|
||||
|
||||
var walker = new BpmnTreeWalker(visitor);
|
||||
walker.handleDefinitions(definitions);
|
||||
|
||||
commandStack.clear();
|
||||
try {
|
||||
// import
|
||||
walker.handleDefinitions(definitions);
|
||||
|
||||
done();
|
||||
// clear command stack
|
||||
commandStack.clear();
|
||||
|
||||
done(null, warnings);
|
||||
} catch (e) {
|
||||
done(e);
|
||||
}
|
||||
}
|
||||
|
||||
module.exports.importBpmnDiagram = Util.failSafeAsync(importBpmnDiagram);
|
||||
module.exports.importBpmnDiagram = importBpmnDiagram;
|
|
@ -40,7 +40,7 @@
|
|||
<bpmndi:BPMNShape id="_BPMNShape_StartEvent_3" bpmnElement="StartEvent_1">
|
||||
<dc:Bounds height="36.0" width="36.0" x="324.0" y="448.0"/>
|
||||
<bpmndi:BPMNLabel>
|
||||
<dc:Bounds height="0.0" width="0.0" x="342.0" y="489.0"/>
|
||||
<dc:Bounds height="1.0" width="0.0" x="342.0" y="489.0"/>
|
||||
</bpmndi:BPMNLabel>
|
||||
</bpmndi:BPMNShape>
|
||||
<bpmndi:BPMNShape id="_BPMNShape_Task_3" bpmnElement="Task_1">
|
||||
|
|
|
@ -0,0 +1,36 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<bpmn2:definitions xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:bpmn2="http://www.omg.org/spec/BPMN/20100524/MODEL" xmlns:bpmndi="http://www.omg.org/spec/BPMN/20100524/DI" xmlns:dc="http://www.omg.org/spec/DD/20100524/DC" xsi:schemaLocation="http://www.omg.org/spec/BPMN/20100524/MODEL BPMN20.xsd" id="_u8q6UPrKEeOYcLGs4Ul9qQ" targetNamespace="http://activiti.org/bpmn">
|
||||
<bpmn2:collaboration id="_Collaboration_2">
|
||||
<bpmn2:participant id="_Participant_2" name="Pool" processRef="Process_1"/>
|
||||
</bpmn2:collaboration>
|
||||
<bpmn2:process id="Process_1" isExecutable="false">
|
||||
<bpmn2:laneSet id="LaneSet_1" name="Lane Set 1">
|
||||
<bpmn2:lane id="Lane_1" name="Lane 1">
|
||||
<bpmn2:flowNodeRef>Task_1</bpmn2:flowNodeRef>
|
||||
<bpmn2:flowNodeRef>TextAnnotation_1</bpmn2:flowNodeRef>
|
||||
</bpmn2:lane>
|
||||
</bpmn2:laneSet>
|
||||
<bpmn2:task id="Task_1"/>
|
||||
<bpmn2:textAnnotation id="TextAnnotation_1">
|
||||
<bpmn2:text><![CDATA[FOOO
|
||||
BAR
|
||||
YOO!]]></bpmn2:text>
|
||||
</bpmn2:textAnnotation>
|
||||
</bpmn2:process>
|
||||
<bpmndi:BPMNDiagram id="BPMNDiagram_1">
|
||||
<bpmndi:BPMNPlane id="BPMNPlane_1" bpmnElement="_Collaboration_2">
|
||||
<bpmndi:BPMNShape id="_BPMNShape_Participant_2" bpmnElement="_Participant_2" isHorizontal="true">
|
||||
<dc:Bounds height="215.0" width="540.0" x="409.0" y="161.0"/>
|
||||
</bpmndi:BPMNShape>
|
||||
<bpmndi:BPMNShape id="_BPMNShape_Lane_2" bpmnElement="Lane_1" isHorizontal="true">
|
||||
<dc:Bounds height="215.0" width="510.0" x="439.0" y="161.0"/>
|
||||
</bpmndi:BPMNShape>
|
||||
<bpmndi:BPMNShape id="_BPMNShape_Task_2" bpmnElement="Task_1">
|
||||
<dc:Bounds height="80.0" width="100.0" x="490.0" y="195.0"/>
|
||||
</bpmndi:BPMNShape>
|
||||
<bpmndi:BPMNShape id="_BPMNShape_TextAnnotation_2" bpmnElement="TextAnnotation_1">
|
||||
<dc:Bounds height="106.0" width="85.0" x="744.0" y="195.0"/>
|
||||
</bpmndi:BPMNShape>
|
||||
</bpmndi:BPMNPlane>
|
||||
</bpmndi:BPMNDiagram>
|
||||
</bpmn2:definitions>
|
|
@ -39,29 +39,40 @@ describe('import - importer', function() {
|
|||
});
|
||||
}
|
||||
|
||||
var diagram;
|
||||
|
||||
it('should fire <bpmn.element.add> during import', function(done) {
|
||||
beforeEach(function() {
|
||||
diagram = createDiagram();
|
||||
});
|
||||
|
||||
// given
|
||||
var xml = fs.readFileSync('test/fixtures/bpmn/simple.bpmn', 'utf8');
|
||||
|
||||
var diagram = createDiagram();
|
||||
|
||||
var events = [];
|
||||
|
||||
// log events
|
||||
diagram.get('eventBus').on('bpmn.element.add', function(e) {
|
||||
events.push({ type: 'add', semantic: e.semantic.id, di: e.di.id, diagramElement: e.diagramElement.id });
|
||||
});
|
||||
|
||||
// when
|
||||
function runImport(diagram, xml, done) {
|
||||
BpmnModel.fromXML(xml, function(err, definitions) {
|
||||
if (err) {
|
||||
return done(err);
|
||||
}
|
||||
|
||||
Importer.importBpmnDiagram(diagram, definitions, done);
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
describe('event emitter', function() {
|
||||
|
||||
it('should fire <bpmn.element.add> during import', function(done) {
|
||||
|
||||
// given
|
||||
var xml = fs.readFileSync('test/fixtures/bpmn/simple.bpmn', 'utf8');
|
||||
|
||||
var events = [];
|
||||
|
||||
// log events
|
||||
diagram.get('eventBus').on('bpmn.element.add', function(e) {
|
||||
events.push({ type: 'add', semantic: e.semantic.id, di: e.di.id, diagramElement: e.diagramElement.id });
|
||||
});
|
||||
|
||||
// when
|
||||
Importer.importBpmnDiagram(diagram, definitions, function(err) {
|
||||
runImport(diagram, xml, function(err, warnings) {
|
||||
|
||||
// then
|
||||
expect(events).toEqual([
|
||||
|
@ -74,31 +85,112 @@ describe('import - importer', function() {
|
|||
done(err);
|
||||
});
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
|
||||
it('should clear commandStack after import', function(done) {
|
||||
describe('basics', function() {
|
||||
|
||||
// given
|
||||
var xml = fs.readFileSync('test/fixtures/bpmn/simple.bpmn', 'utf8');
|
||||
it('should import process', function(done) {
|
||||
|
||||
var diagram = createDiagram();
|
||||
// given
|
||||
var xml = fs.readFileSync('test/fixtures/bpmn/simple.bpmn', 'utf8');
|
||||
|
||||
var commandStack = diagram.get('commandStack');
|
||||
var events = [];
|
||||
|
||||
// when
|
||||
BpmnModel.fromXML(xml, function(err, definitions) {
|
||||
if (err) {
|
||||
return done(err);
|
||||
}
|
||||
// log events
|
||||
diagram.get('eventBus').on('bpmn.element.add', function(e) {
|
||||
events.push({ type: 'add', semantic: e.semantic.id, di: e.di.id, diagramElement: e.diagramElement.id });
|
||||
});
|
||||
|
||||
// when
|
||||
Importer.importBpmnDiagram(diagram, definitions, function(err) {
|
||||
runImport(diagram, xml, function(err, warnings) {
|
||||
|
||||
// then
|
||||
expect(events).toEqual([
|
||||
{ type: 'add', semantic: 'SubProcess_1', di: '_BPMNShape_SubProcess_2', diagramElement: 'SubProcess_1' },
|
||||
{ type: 'add', semantic: 'StartEvent_1', di: '_BPMNShape_StartEvent_2', diagramElement: 'StartEvent_1' },
|
||||
{ type: 'add', semantic: 'Task_1', di: '_BPMNShape_Task_2', diagramElement: 'Task_1' },
|
||||
{ type: 'add', semantic: 'SequenceFlow_1', di: 'BPMNEdge_SequenceFlow_1', diagramElement: 'SequenceFlow_1' }
|
||||
]);
|
||||
|
||||
done(err);
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
it('should import collaboration', function(done) {
|
||||
|
||||
// given
|
||||
var xml = fs.readFileSync('test/fixtures/bpmn/collaboration.bpmn', 'utf8');
|
||||
|
||||
var events = [];
|
||||
|
||||
// log events
|
||||
diagram.get('eventBus').on('bpmn.element.add', function(e) {
|
||||
events.push({ type: 'add', semantic: e.semantic.id, di: e.di.id, diagramElement: e.diagramElement.id });
|
||||
});
|
||||
|
||||
// when
|
||||
runImport(diagram, xml, function(err, warnings) {
|
||||
|
||||
// then
|
||||
expect(events).toEqual([
|
||||
{ type: 'add', semantic: 'Participant_2', di: '_BPMNShape_Participant_2', diagramElement: 'Participant_2' },
|
||||
{ type: 'add', semantic: 'Lane_1', di: '_BPMNShape_Lane_2', diagramElement: 'Lane_1' },
|
||||
{ type: 'add', semantic: 'Lane_2', di: '_BPMNShape_Lane_3', diagramElement: 'Lane_2' },
|
||||
{ type: 'add', semantic: 'Lane_3', di: '_BPMNShape_Lane_4', diagramElement: 'Lane_3' },
|
||||
{ type: 'add', semantic: 'Task_1', di: '_BPMNShape_Task_3', diagramElement: 'Task_1' },
|
||||
{ type: 'add', semantic: 'Participant_1', di: '_BPMNShape_Participant_3', diagramElement: 'Participant_1' },
|
||||
{ type: 'add', semantic: 'StartEvent_1', di: '_BPMNShape_StartEvent_3', diagramElement: 'StartEvent_1' }
|
||||
]);
|
||||
|
||||
done(err);
|
||||
});
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
describe('command stack integration', function() {
|
||||
|
||||
it('should clear stack after import', function(done) {
|
||||
|
||||
// given
|
||||
var xml = fs.readFileSync('test/fixtures/bpmn/simple.bpmn', 'utf8');
|
||||
|
||||
var commandStack = diagram.get('commandStack');
|
||||
|
||||
// when
|
||||
runImport(diagram, xml, function(err, warnings) {
|
||||
|
||||
// then
|
||||
expect(commandStack.getStack()).toEqual([]);
|
||||
|
||||
done(err);
|
||||
});
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
|
||||
describe('forgiveness', function() {
|
||||
|
||||
it('should import invalid flowElement', function(done) {
|
||||
|
||||
// given
|
||||
var xml = fs.readFileSync('test/fixtures/bpmn/error/invalid-flow-element.bpmn', 'utf8');
|
||||
|
||||
var commandStack = diagram.get('commandStack');
|
||||
|
||||
// when
|
||||
runImport(diagram, xml, function(err, warnings) {
|
||||
|
||||
expect(warnings.length).toBe(1);
|
||||
|
||||
done(err);
|
||||
});
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
});
|
Loading…
Reference in New Issue