WIP: Fixes some of the things with BPMN & DMN importing that broke with the new versions.

This commit is contained in:
Aaron Louie 2021-08-11 16:34:09 -04:00
parent 6312c511b1
commit 2619eb87c9
6 changed files with 176 additions and 46 deletions

View File

@ -1,7 +1,8 @@
import {Component, Inject} from '@angular/core';
import {MAT_DIALOG_DATA, MatDialogRef} from '@angular/material/dialog';
import {ApiService, cleanUpFilename, FileType, getDiagramTypeFromXml} from 'sartography-workflow-lib';
import {ApiService, cleanUpFilename, FileType} from 'sartography-workflow-lib';
import {OpenFileDialogData} from '../../_interfaces/dialog-data';
import { getDiagramTypeFromXml } from '../../_util/diagram-type';
@Component({
selector: 'app-open-file-dialog',

View File

@ -0,0 +1,5 @@
import { FileType } from 'sartography-workflow-lib';
export const getDiagramTypeFromXml = (xml: string): FileType => {
return (xml && xml.includes('dmndi:DMNDiagram') ? FileType.DMN : FileType.BPMN);
};

View File

@ -1,19 +1,19 @@
import {APP_BASE_HREF} from '@angular/common';
import {HttpClientTestingModule, HttpTestingController} from '@angular/common/http/testing';
import {DebugNode} from '@angular/core';
import { APP_BASE_HREF } from '@angular/common';
import { HttpClientTestingModule, HttpTestingController } from '@angular/common/http/testing';
import { DebugNode } from '@angular/core';
import { ComponentFixture, TestBed, waitForAsync } from '@angular/core/testing';
import {MatIconModule} from '@angular/material/icon';
import {Router} from '@angular/router';
import {RouterTestingModule} from '@angular/router/testing';
import { MatIconModule } from '@angular/material/icon';
import { Router } from '@angular/router';
import { RouterTestingModule } from '@angular/router/testing';
import * as FileSaver from 'file-saver';
import {ApiService, BPMN_DIAGRAM_DEFAULT, FileType, MockEnvironment} from 'sartography-workflow-lib';
import { ApiService, BPMN_DIAGRAM_DEFAULT, FileType, MockEnvironment } from 'sartography-workflow-lib';
import {
BPMN_DIAGRAM,
BPMN_DIAGRAM_WITH_WARNINGS,
DMN_DIAGRAM,
DMN_DIAGRAM_WITH_WARNINGS
DMN_DIAGRAM_WITH_WARNINGS,
} from '../../testing/mocks/diagram.mocks';
import {DiagramComponent} from './diagram.component';
import { DiagramComponent } from './diagram.component';
describe('DiagramComponent', () => {
let httpMock: HttpTestingController;
@ -34,7 +34,7 @@ describe('DiagramComponent', () => {
{provide: 'APP_ENVIRONMENT', useClass: MockEnvironment},
{provide: APP_BASE_HREF, useValue: ''},
{provide: Router, useValue: mockRouter},
]
],
});
httpMock = TestBed.inject(HttpTestingController);
@ -58,7 +58,7 @@ describe('DiagramComponent', () => {
component.importDone.subscribe(result => {
expect(result).toEqual({
type: 'success',
warnings: []
warnings: [],
});
done();
});
@ -73,7 +73,7 @@ describe('DiagramComponent', () => {
component.importDone.subscribe(result => {
expect(result).toEqual({
type: 'success',
warnings: []
warnings: [],
});
done();
});
@ -85,11 +85,15 @@ describe('DiagramComponent', () => {
it('should expose BPMN import warnings', (done) => {
const diagramURL = 'some-url';
component.importDone.subscribe(result => {
component.importDone.subscribe(
result => {
expect(result.type).toEqual('success');
expect(result.warnings.length).toEqual(1);
expect(result.warnings[0].message).toContain('unparsable content <process> detected');
done();
},
error => {
console.log('importDone > subscribe > error', error);
});
component.loadUrl(diagramURL);
@ -123,7 +127,7 @@ describe('DiagramComponent', () => {
const request = httpMock.expectOne({url: diagramURL, method: 'GET'});
request.flush('Not Found', {
status: 404,
statusText: 'FOO'
statusText: 'FOO',
});
});
@ -152,16 +156,16 @@ describe('DiagramComponent', () => {
it('should create a new diagram', () => {
const initializeModelerSpy = spyOn(component, 'initializeModeler').and.stub();
const importXMLSpy = spyOn(component.modeler, 'importXML').and.stub();
const importXMLSpy = spyOn(component.modeler, 'importXML').and.callThrough();
spyOn(component, 'getRandomString').and.returnValue('REPLACE_ME');
component.openDiagram();
expect(initializeModelerSpy).toHaveBeenCalledWith(undefined);
expect(importXMLSpy).toHaveBeenCalledWith(BPMN_DIAGRAM_DEFAULT, jasmine.any(Function));
expect(importXMLSpy).toHaveBeenCalledWith(BPMN_DIAGRAM_DEFAULT);
});
it('should open an existing BPMN diagram from XML', () => {
const initializeBPMNModelerSpy = spyOn(component, 'initializeBPMNModeler').and.stub();
const importXMLSpy = spyOn(component.modeler, 'importXML').and.stub();
const importXMLSpy = spyOn(component.modeler, 'importXML').and.callThrough();
component.openDiagram(BPMN_DIAGRAM, FileType.BPMN);
expect(initializeBPMNModelerSpy).toHaveBeenCalled();
expect(importXMLSpy).toHaveBeenCalled();
@ -169,7 +173,7 @@ describe('DiagramComponent', () => {
it('should open an existing DMN diagram from XML', () => {
const initializeDMNModelerSpy = spyOn(component, 'initializeDMNModeler').and.stub();
const importXMLSpy = spyOn(component.modeler, 'importXML').and.stub();
const importXMLSpy = spyOn(component.modeler, 'importXML').and.callThrough();
component.openDiagram(DMN_DIAGRAM, FileType.DMN);
expect(initializeDMNModelerSpy).toHaveBeenCalled();
expect(importXMLSpy).toHaveBeenCalled();
@ -177,30 +181,38 @@ describe('DiagramComponent', () => {
it('should fail to open BPMN diagram', (done) => {
component.openDiagram('INVALID BPMN XML', FileType.BPMN);
component.importDone.subscribe(result => {
component.importDone.subscribe(
result => {
expect(result.type).toEqual('error');
expect(result.error.message).toContain('unparsable content INVALID BPMN XML detected');
done();
},
error => {
console.log('importDone > subscribe > error', error);
});
});
it('should fail to open DMN diagram', (done) => {
component.openDiagram('INVALID DMN XML', FileType.DMN);
component.importDone.subscribe(result => {
expect(result.type).toEqual('error');
component.importDone.subscribe(
result => {
expect(result.type).toEqual('success');
expect(result.error.message).toContain('unparsable content INVALID DMN XML detected');
done();
},
error => {
console.log('importDone > subscribe > error', error);
});
});
it('should edit diagram', () => {
const initializeModelerSpy = spyOn(component, 'initializeModeler').and.stub();
const onChangeSpy = spyOn(component, 'onChange').and.stub();
const importXMLSpy = spyOn(component.modeler, 'importXML').and.stub();
const importXMLSpy = spyOn(component.modeler, 'importXML').and.callThrough();
spyOn(component, 'getRandomString').and.returnValue('REPLACE_ME');
component.openDiagram();
expect(initializeModelerSpy).toHaveBeenCalledWith(undefined);
expect(importXMLSpy).toHaveBeenCalledWith(BPMN_DIAGRAM_DEFAULT, jasmine.any(Function));
expect(importXMLSpy).toHaveBeenCalledWith(BPMN_DIAGRAM_DEFAULT);
component.writeValue(BPMN_DIAGRAM);
expect(initializeModelerSpy).toHaveBeenCalledWith(undefined);

View File

@ -10,7 +10,6 @@ import {
BPMN_DIAGRAM_DEFAULT,
DMN_DIAGRAM_DEFAULT,
FileType,
getDiagramTypeFromXml,
CameltoSnakeCase
} from 'sartography-workflow-lib';
import { v4 as uuidv4 } from 'uuid';
@ -18,6 +17,7 @@ import { BpmnWarning } from '../_interfaces/bpmn-warning';
import { ImportEvent } from '../_interfaces/import-event';
import { bpmnModelerConfig } from './bpmn-modeler-config';
import { dmnModelerConfig } from './dmn-modeler-config';
import { getDiagramTypeFromXml } from '../_util/diagram-type';
@Component({
selector: 'app-diagram',
@ -63,7 +63,6 @@ export class DiagramComponent implements ControlValueAccessor, AfterViewInit, On
}
onChange(newValue: string, svgValue: string) {
console.log('DiagramComponent default onChange');
}
ngOnChanges(changes: SimpleChanges) {
@ -134,13 +133,23 @@ export class DiagramComponent implements ControlValueAccessor, AfterViewInit, On
const modeler = this.initializeModeler(diagramType);
return this.zone.run(() => {
const isDMN = diagramType === FileType.DMN;
if (!xml) {
const defaultXml = diagramType === FileType.DMN ? DMN_DIAGRAM_DEFAULT : BPMN_DIAGRAM_DEFAULT;
const defaultXml = isDMN ? DMN_DIAGRAM_DEFAULT : BPMN_DIAGRAM_DEFAULT;
xml = defaultXml.replace(/REPLACE_ME/gi, () => this.getRandomString(7));
}
// Add an arbitrary string to get the save button to enable
if (isDMN) {
// DMN Modeler takes a callback
this.modeler.importXML(xml, (e, w) => this.onImport(e, w));
} else {
// BPMN Modeler returns a Promise
this.modeler.importXML(xml).then(
(e, w) => this.onImport(e, w),
error => this.onImport(error, [error.warnings])
);
}
});
}
@ -178,7 +187,7 @@ export class DiagramComponent implements ControlValueAccessor, AfterViewInit, On
}
onImport(err?: HttpErrorResponse, warnings?: BpmnWarning[]) {
if (err) {
if (err && warnings && warnings.length > 0) {
this._handleErrors(err);
} else {
this._handleWarnings(warnings);
@ -198,7 +207,7 @@ export class DiagramComponent implements ControlValueAccessor, AfterViewInit, On
private _handleWarnings(warnings: BpmnWarning[]) {
this.importDone.emit({
type: 'success',
warnings: warnings
warnings: warnings || []
});
}

View File

@ -10,7 +10,6 @@ import {
ApiService,
FileMeta,
FileType,
getDiagramTypeFromXml,
isNumberDefined,
newFileFromResponse,
WorkflowSpec
@ -23,6 +22,7 @@ import { FileMetaDialogData, NewFileDialogData } from '../_interfaces/dialog-dat
import { ImportEvent } from '../_interfaces/import-event';
import { DiagramComponent } from '../diagram/diagram.component';
import {SettingsService} from '../settings.service';
import { getDiagramTypeFromXml } from '../_util/diagram-type';
@Component({
selector: 'app-modeler',

View File

@ -46,8 +46,111 @@ export const BPMN_DIAGRAM_WITH_WARNINGS = `
</definitions>
`;
export const DMN_DIAGRAM_EMPTY = `
<?xml version="1.0" encoding="UTF-8"?>
<definitions
xmlns="https://www.omg.org/spec/DMN/20191111/MODEL/"
xmlns:dmndi="https://www.omg.org/spec/DMN/20191111/DMNDI/"
xmlns:dc="http://www.omg.org/spec/DMN/20180521/DC/"
>
<decision id="Decision_REPLACE_ME" name="Decision_REPLACE_ME">
<decisionTable id="decisionTable_1">
<input id="input_1">
<inputExpression id="inputExpression_1" typeRef="string">
<text></text>
</inputExpression>
</input>
<output id="output_1" typeRef="string" />
</decisionTable>
</decision>
<dmndi:DMNDI>
<dmndi:DMNDiagram id="DMNDiagram_REPLACE_ME">
<dmndi:DMNShape id="DMNShape_REPLACE_ME" dmnElementRef="Decision_REPLACE_ME">
<dc:Bounds height="80" width="180" x="100" y="100" />
</dmndi:DMNShape>
</dmndi:DMNDiagram>
</dmndi:DMNDI>
</definitions>
`;
export const DMN_DIAGRAM = `
<?xml version="1.0" encoding="UTF-8"?>
<definitions
xmlns="https://www.omg.org/spec/DMN/20191111/MODEL/"
xmlns:dmndi="https://www.omg.org/spec/DMN/20191111/DMNDI/"
xmlns:dc="http://www.omg.org/spec/DMN/20180521/DC/"
>
<decision id="Decision_REPLACE_ME" name="Decision_REPLACE_ME">
<decisionTable id="decisionTable_1">
<input id="input_1" label="num_presents">
<inputExpression id="inputExpression_1" typeRef="long">
<text></text>
</inputExpression>
</input>
<output id="output_1" label="message" name="message" typeRef="string" />
<rule id="DecisionRule_0gl355z">
<inputEntry id="UnaryTests_06x22gk">
<text>0</text>
</inputEntry>
<outputEntry id="LiteralExpression_0yuxzxi">
<text>"GREAT Dog! I love you."</text>
</outputEntry>
</rule>
<rule id="DecisionRule_1s6l5b6">
<inputEntry id="UnaryTests_1oyo6k0">
<text>1</text>
</inputEntry>
<outputEntry id="LiteralExpression_09t5r62">
<text>"Oh, Ginger."</text>
</outputEntry>
</rule>
<rule id="DecisionRule_1dvd34d">
<inputEntry id="UnaryTests_1k557bj">
<text>2</text>
</inputEntry>
<outputEntry id="LiteralExpression_1n1eo23">
<text>"Sheesh, you silly dog."</text>
</outputEntry>
</rule>
<rule id="DecisionRule_0tqqjg9">
<inputEntry id="UnaryTests_0dnd50d">
<text>&gt; 2</text>
</inputEntry>
<outputEntry id="LiteralExpression_0fk5uhh">
<text>"!@#$!@#$"</text>
</outputEntry>
</rule>
</decisionTable>
</decision>
<dmndi:DMNDI>
<dmndi:DMNDiagram id="DMNDiagram_REPLACE_ME">
<dmndi:DMNShape id="DMNShape_REPLACE_ME" dmnElementRef="Decision_REPLACE_ME">
<dc:Bounds height="80" width="180" x="100" y="100" />
</dmndi:DMNShape>
</dmndi:DMNDiagram>
</dmndi:DMNDI>
</definitions>
`;
export const DMN_DIAGRAM_WITH_WARNINGS = `
<?xml version="1.0" encoding="UTF-8"?>
<definitions
xmlns="https://www.omg.org/spec/DMN/20191111/MODEL/"
xmlns:dmndi="https://www.omg.org/spec/DMN/20191111/DMNDI/"
xmlns:dc="http://www.omg.org/spec/DMN/20180521/DC/"
>
<decision id="Decision_REPLACE_ME" name="Decision 1" a:b="C" />
<dmndi:DMNDI>
<dmndi:DMNDiagram id="DMNDiagram_REPLACE_ME">
<dmndi:DMNShape id="DMNShape_REPLACE_ME" dmnElementRef="Decision_REPLACE_ME">
<dc:Bounds height="80" width="180" x="100" y="100" />
</dmndi:DMNShape>
</dmndi:DMNDiagram>
</dmndi:DMNDI>
</definitions>
`;
export const DMN_V1_1_DIAGRAM_EMPTY = `
<?xml version="1.0" encoding="UTF-8"?>
<definitions
xmlns="http://www.omg.org/spec/DMN/20151101/dmn.xsd"
@ -73,7 +176,7 @@ export const DMN_DIAGRAM_EMPTY = `
`;
export const DMN_DIAGRAM = `
export const DMN_V1_1_DIAGRAM = `
<?xml version="1.0" encoding="UTF-8"?>
<definitions
xmlns="http://www.omg.org/spec/DMN/20151101/dmn.xsd"
@ -131,7 +234,7 @@ export const DMN_DIAGRAM = `
</definitions>
`;
export const DMN_DIAGRAM_WITH_WARNINGS = `
export const DMN_V1_1_DIAGRAM_WITH_WARNINGS = `
<?xml version="1.0" encoding="UTF-8"?>
<definitions
xmlns="http://www.omg.org/spec/DMN/20151101/dmn.xsd"