mirror of
https://github.com/sartography/cr-connect-bpmn.git
synced 2025-02-03 20:34:07 +00:00
Fixes all the modeler stuff that broke with the latest version changes.
This commit is contained in:
parent
1b447a9d88
commit
53badb187a
@ -1,7 +1,12 @@
|
||||
export interface BpmnError {
|
||||
warnings: BpmnWarning[];
|
||||
}
|
||||
|
||||
export interface BpmnWarning {
|
||||
message?: string;
|
||||
element?: any;
|
||||
property?: string;
|
||||
value?: string;
|
||||
context?: any;
|
||||
error?: Error;
|
||||
}
|
||||
|
@ -13,6 +13,6 @@ export const bpmnModelerConfig: ModelerConfig = {
|
||||
codeModule,
|
||||
],
|
||||
moddleExtensions: {
|
||||
camunda: bpmnModdleDescriptor.default
|
||||
camunda: bpmnModdleDescriptor
|
||||
}
|
||||
};
|
||||
|
@ -1,12 +1,18 @@
|
||||
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 { ComponentFixture, fakeAsync, TestBed, tick, waitForAsync } from '@angular/core/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,
|
||||
DMN_DIAGRAM_DEFAULT,
|
||||
FileType,
|
||||
MockEnvironment,
|
||||
} from 'sartography-workflow-lib';
|
||||
import {
|
||||
BPMN_DIAGRAM,
|
||||
BPMN_DIAGRAM_WITH_WARNINGS,
|
||||
@ -20,6 +26,16 @@ describe('DiagramComponent', () => {
|
||||
let fixture: ComponentFixture<DiagramComponent>;
|
||||
let component: DebugNode['componentInstance'];
|
||||
const mockRouter = {navigate: jasmine.createSpy('navigate')};
|
||||
const testSaveAs = async (fileType: FileType, xml: string, callMethod: string, filename: string) => {
|
||||
const saveDiagramSpy = spyOn(component, 'saveDiagram').and.callThrough();
|
||||
const _fileSaveAsSpy = spyOn((component as any), '_fileSaveAs').and.stub();
|
||||
component.fileName = filename + '.' + fileType;
|
||||
component.diagramType = fileType;
|
||||
component.xml = xml;
|
||||
await component[callMethod]();
|
||||
await expect(saveDiagramSpy).toHaveBeenCalled();
|
||||
await expect(_fileSaveAsSpy).toHaveBeenCalledWith(jasmine.any(Blob), jasmine.stringMatching(filename));
|
||||
};
|
||||
|
||||
beforeEach(waitForAsync(() => {
|
||||
TestBed.configureTestingModule({
|
||||
@ -87,13 +103,14 @@ describe('DiagramComponent', () => {
|
||||
const diagramURL = 'some-url';
|
||||
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');
|
||||
expect(result.type).toEqual('error');
|
||||
expect(result.error).toBeDefined();
|
||||
expect(result.error.warnings).toBeDefined();
|
||||
expect(result.error.warnings.length).toEqual(1);
|
||||
expect(result.error.warnings[0].message).toContain('unparsable content <process> detected');
|
||||
done();
|
||||
},
|
||||
error => {
|
||||
console.log('importDone > subscribe > error', error);
|
||||
});
|
||||
component.loadUrl(diagramURL);
|
||||
|
||||
@ -131,16 +148,20 @@ describe('DiagramComponent', () => {
|
||||
});
|
||||
});
|
||||
|
||||
it('should save diagram as SVG', () => {
|
||||
const fileSaverSpy = spyOn(FileSaver, 'saveAs').and.stub();
|
||||
component.saveSVG();
|
||||
expect(fileSaverSpy).toHaveBeenCalled();
|
||||
it('should save BPMN diagram as SVG', async () => {
|
||||
await testSaveAs(FileType.BPMN, BPMN_DIAGRAM, 'saveSVG', 'some_bpmn_name');
|
||||
});
|
||||
|
||||
it('should save diagram as XML', () => {
|
||||
const fileSaverSpy = spyOn(FileSaver, 'saveAs').and.stub();
|
||||
component.saveXML();
|
||||
expect(fileSaverSpy).toHaveBeenCalled();
|
||||
it('should save BPMN diagram as XML', async () => {
|
||||
await testSaveAs(FileType.BPMN, BPMN_DIAGRAM, 'saveXML', 'some_bpmn_name');
|
||||
});
|
||||
|
||||
it('should save DMN diagram as SVG', async () => {
|
||||
await testSaveAs(FileType.DMN, DMN_DIAGRAM, 'saveSVG', 'some_dmn_name');
|
||||
});
|
||||
|
||||
it('should save DMN diagram as XML', async () => {
|
||||
await testSaveAs(FileType.DMN, DMN_DIAGRAM, 'saveXML', 'some_dmn_name');
|
||||
});
|
||||
|
||||
it('should insert date into filename', () => {
|
||||
@ -188,7 +209,6 @@ describe('DiagramComponent', () => {
|
||||
done();
|
||||
},
|
||||
error => {
|
||||
console.log('importDone > subscribe > error', error);
|
||||
});
|
||||
});
|
||||
|
||||
@ -196,16 +216,16 @@ describe('DiagramComponent', () => {
|
||||
component.openDiagram('INVALID DMN XML', FileType.DMN);
|
||||
component.importDone.subscribe(
|
||||
result => {
|
||||
expect(result.type).toEqual('success');
|
||||
expect(result.type).toEqual('error');
|
||||
expect(result.error.message).toContain('unparsable content INVALID DMN XML detected');
|
||||
done();
|
||||
},
|
||||
error => {
|
||||
console.log('importDone > subscribe > error', error);
|
||||
console.error('importDone > subscribe > error', error);
|
||||
});
|
||||
});
|
||||
|
||||
it('should edit diagram', () => {
|
||||
it('should edit BPMN diagram', () => {
|
||||
const initializeModelerSpy = spyOn(component, 'initializeModeler').and.stub();
|
||||
const onChangeSpy = spyOn(component, 'onChange').and.stub();
|
||||
const importXMLSpy = spyOn(component.modeler, 'importXML').and.callThrough();
|
||||
@ -219,6 +239,23 @@ describe('DiagramComponent', () => {
|
||||
expect(onChangeSpy).toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('should edit DMN diagram', () => {
|
||||
const initializeModelerSpy = spyOn(component, 'initializeModeler').and.stub();
|
||||
const onChangeSpy = spyOn(component, 'onChange').and.stub();
|
||||
const importXMLSpy = spyOn(component.modeler, 'importXML').and.callThrough();
|
||||
spyOn(component, 'getRandomString').and.returnValue('REPLACE_ME');
|
||||
|
||||
component.diagramType = FileType.DMN;
|
||||
component.openDiagram(DMN_DIAGRAM_DEFAULT, FileType.DMN);
|
||||
expect(initializeModelerSpy).toHaveBeenCalledWith(FileType.DMN);
|
||||
expect(importXMLSpy).toHaveBeenCalledWith(DMN_DIAGRAM_DEFAULT, jasmine.any(Function));
|
||||
initializeModelerSpy.calls.reset();
|
||||
|
||||
component.writeValue(DMN_DIAGRAM);
|
||||
expect(initializeModelerSpy).toHaveBeenCalledWith(undefined);
|
||||
expect(onChangeSpy).toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('should register onChange function', () => {
|
||||
const fn = (s: string) => s.toLowerCase().trim();
|
||||
const input = ' TRIMMED AND LOWERCASED ';
|
||||
|
@ -24,11 +24,12 @@ import {
|
||||
FileType,
|
||||
} from 'sartography-workflow-lib';
|
||||
import { v4 as uuidv4 } from 'uuid';
|
||||
import { BpmnWarning } from '../_interfaces/bpmn-warning';
|
||||
import { BpmnError, 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';
|
||||
import isEqual from 'lodash.isequal';
|
||||
|
||||
@Component({
|
||||
selector: 'app-diagram',
|
||||
@ -144,61 +145,89 @@ export class DiagramComponent implements ControlValueAccessor, AfterViewInit, On
|
||||
|
||||
return this.zone.run(() => {
|
||||
const isDMN = diagramType === FileType.DMN;
|
||||
|
||||
if (!xml) {
|
||||
const defaultXml = isDMN ? DMN_DIAGRAM_DEFAULT : BPMN_DIAGRAM_DEFAULT;
|
||||
xml = defaultXml.replace(/REPLACE_ME/gi, () => this.getRandomString(7));
|
||||
const randomString = this.getRandomString(7);
|
||||
xml = defaultXml.replace(/REPLACE_ME/gi, () => randomString);
|
||||
}
|
||||
|
||||
// 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));
|
||||
this.modeler.importXML(xml, (e, w) => this.onImport(e, w || e && e.warnings));
|
||||
} else {
|
||||
// BPMN Modeler returns a Promise
|
||||
this.modeler.importXML(xml).then(
|
||||
(e, w) => this.onImport(e, w),
|
||||
error => this.onImport(error, [error.warnings]),
|
||||
(e, w) => this.onImport(e, w || e && e.warnings),
|
||||
e => this.onImport(e, e && e.warnings),
|
||||
);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
saveSVG() {
|
||||
this.saveDiagram();
|
||||
this.modeler.saveSVG((err, svg) => {
|
||||
const blob = new Blob([svg], {type: 'image/svg+xml'});
|
||||
fileSaver.saveAs(blob, `${this.diagramType.toString().toUpperCase()} Diagram - ${new Date().toISOString()}.svg`);
|
||||
});
|
||||
async saveSVG() {
|
||||
await this.saveDiagram();
|
||||
const {svg} = await this.modeler.saveSVG();
|
||||
const blob = new Blob([svg], {type: 'image/svg+xml'});
|
||||
this._fileSaveAs(blob, this.insertDateIntoFileName());
|
||||
}
|
||||
|
||||
saveDiagram() {
|
||||
async saveDiagram() {
|
||||
if (this.modeler && this.modeler.saveSVG) {
|
||||
this.modeler.saveSVG((svgErr, svg) => {
|
||||
this.svg = svg;
|
||||
this.modeler.saveXML({format: true}, (xmlErr, xml) => {
|
||||
this.xml = xml;
|
||||
this.writeValue(xml);
|
||||
});
|
||||
});
|
||||
} else {
|
||||
this.modeler.saveXML({format: true}, (xmlErr, xml) => {
|
||||
this.xml = xml;
|
||||
this.writeValue(xml);
|
||||
});
|
||||
const {svg} = await this.modeler.saveSVG();
|
||||
this.svg = svg;
|
||||
}
|
||||
|
||||
const {xml} = await this.modeler.saveXML({format: true});
|
||||
this.xml = xml;
|
||||
this.writeValue(xml);
|
||||
}
|
||||
|
||||
saveXML() {
|
||||
this.saveDiagram();
|
||||
this.modeler.saveXML({format: true}, (err, xml) => {
|
||||
const blob = new Blob([xml], {type: 'text/xml'});
|
||||
fileSaver.saveAs(blob, this.insertDateIntoFileName());
|
||||
});
|
||||
async saveXML() {
|
||||
await this.saveDiagram();
|
||||
const {xml} = await this.modeler.saveXML({format: true})
|
||||
const blob = new Blob([xml], {type: 'text/xml'});
|
||||
this._fileSaveAs(blob, this.insertDateIntoFileName());
|
||||
}
|
||||
|
||||
onImport(err?: HttpErrorResponse, warnings?: BpmnWarning[]) {
|
||||
if (err && warnings && warnings.length > 0) {
|
||||
this._handleErrors(err);
|
||||
onImport(err?: Error | BpmnError, warnings?: BpmnWarning[]) {
|
||||
warnings = warnings || [];
|
||||
|
||||
// SUCCESS (no errors or warnings)
|
||||
// -----------------------------------------
|
||||
// err = {warnings: []}
|
||||
// warnings = []
|
||||
// -----------------------------------------
|
||||
// err = undefined
|
||||
// warnings = []
|
||||
// -----------------------------------------
|
||||
|
||||
// ERROR
|
||||
// -----------------------------------------
|
||||
// err = {warnings: [{message: '...', error: {stack '...', message: '...'}}]}
|
||||
// warnings = [{message: '...', error: {stack '...', message: '...'}}]
|
||||
// -----------------------------------------
|
||||
|
||||
// SUCCESS WITH WARNINGS
|
||||
// -----------------------------------------
|
||||
// err = {warnings: [{message: '...', error: {}}]
|
||||
// warnings = [{message: '...', error: {}}]
|
||||
// -----------------------------------------
|
||||
// err = undefined
|
||||
// warnings = [{message: '...', error: {}}]
|
||||
// -----------------------------------------
|
||||
const isSuccess = (!err || isEqual(err, {warnings: []})) && isEqual(warnings, []);
|
||||
const isError = !isSuccess && err && (
|
||||
err instanceof Error ||
|
||||
err.warnings && err.warnings.some(w => w.error && !isEqual(w.error, {}))
|
||||
);
|
||||
|
||||
if (isSuccess && !isError) {
|
||||
this._handleSuccess();
|
||||
} else if (isError) {
|
||||
const errors = err || warnings.filter(w => !!w.error);
|
||||
this._handleErrors(errors);
|
||||
} else {
|
||||
this._handleWarnings(warnings);
|
||||
}
|
||||
@ -208,10 +237,20 @@ export class DiagramComponent implements ControlValueAccessor, AfterViewInit, On
|
||||
* Load diagram from URL and emit completion event
|
||||
*/
|
||||
loadUrl(url: string) {
|
||||
this.api.getStringFromUrl(url).subscribe(xml => {
|
||||
const diagramType = getDiagramTypeFromXml(xml);
|
||||
this.openDiagram(xml, diagramType);
|
||||
}, error => this._handleErrors(error));
|
||||
this.api.getStringFromUrl(url).subscribe(
|
||||
xml => {
|
||||
const diagramType = getDiagramTypeFromXml(xml);
|
||||
this.openDiagram(xml, diagramType);
|
||||
},
|
||||
error => this._handleErrors(error)
|
||||
);
|
||||
}
|
||||
|
||||
private _handleSuccess() {
|
||||
this.importDone.emit({
|
||||
type: 'success',
|
||||
warnings: [],
|
||||
});
|
||||
}
|
||||
|
||||
private _handleWarnings(warnings: BpmnWarning[]) {
|
||||
@ -338,4 +377,9 @@ export class DiagramComponent implements ControlValueAccessor, AfterViewInit, On
|
||||
return `${this.fileName}_${dateString}.${this.diagramType}`;
|
||||
}
|
||||
}
|
||||
|
||||
private _fileSaveAs(blob: Blob, filename: string) {
|
||||
fileSaver.saveAs(blob, filename);
|
||||
};
|
||||
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user