From 2619eb87c9ec5d8abd89c55df045a36146f2ac37 Mon Sep 17 00:00:00 2001 From: Aaron Louie Date: Wed, 11 Aug 2021 16:34:09 -0400 Subject: [PATCH] WIP: Fixes some of the things with BPMN & DMN importing that broke with the new versions. --- .../open-file-dialog.component.ts | 3 +- src/app/_util/diagram-type.ts | 5 + src/app/diagram/diagram.component.spec.ts | 82 +++++++------ src/app/diagram/diagram.component.ts | 21 +++- src/app/modeler/modeler.component.ts | 2 +- src/testing/mocks/diagram.mocks.ts | 109 +++++++++++++++++- 6 files changed, 176 insertions(+), 46 deletions(-) create mode 100644 src/app/_util/diagram-type.ts diff --git a/src/app/_dialogs/open-file-dialog/open-file-dialog.component.ts b/src/app/_dialogs/open-file-dialog/open-file-dialog.component.ts index cdfae9a..56c74c2 100644 --- a/src/app/_dialogs/open-file-dialog/open-file-dialog.component.ts +++ b/src/app/_dialogs/open-file-dialog/open-file-dialog.component.ts @@ -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', diff --git a/src/app/_util/diagram-type.ts b/src/app/_util/diagram-type.ts new file mode 100644 index 0000000..ff6e071 --- /dev/null +++ b/src/app/_util/diagram-type.ts @@ -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); +}; diff --git a/src/app/diagram/diagram.component.spec.ts b/src/app/diagram/diagram.component.spec.ts index f8dd7c5..30f538a 100644 --- a/src/app/diagram/diagram.component.spec.ts +++ b/src/app/diagram/diagram.component.spec.ts @@ -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,12 +85,16 @@ describe('DiagramComponent', () => { it('should expose BPMN import warnings', (done) => { 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 detected'); - done(); - }); + component.importDone.subscribe( + result => { + expect(result.type).toEqual('success'); + expect(result.warnings.length).toEqual(1); + expect(result.warnings[0].message).toContain('unparsable content detected'); + done(); + }, + error => { + console.log('importDone > subscribe > error', error); + }); component.loadUrl(diagramURL); const request = httpMock.expectOne({url: diagramURL, method: 'GET'}); @@ -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 => { - expect(result.type).toEqual('error'); - expect(result.error.message).toContain('unparsable content INVALID BPMN XML detected'); - done(); - }); + 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'); - expect(result.error.message).toContain('unparsable content INVALID DMN XML detected'); - done(); - }); + 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); diff --git a/src/app/diagram/diagram.component.ts b/src/app/diagram/diagram.component.ts index c72f99f..f792977 100644 --- a/src/app/diagram/diagram.component.ts +++ b/src/app/diagram/diagram.component.ts @@ -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 - this.modeler.importXML(xml, (e, w) => this.onImport(e, w)); + 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 || [] }); } diff --git a/src/app/modeler/modeler.component.ts b/src/app/modeler/modeler.component.ts index bda46ee..dc9ae4d 100644 --- a/src/app/modeler/modeler.component.ts +++ b/src/app/modeler/modeler.component.ts @@ -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', diff --git a/src/testing/mocks/diagram.mocks.ts b/src/testing/mocks/diagram.mocks.ts index 8f38c59..67a97e2 100644 --- a/src/testing/mocks/diagram.mocks.ts +++ b/src/testing/mocks/diagram.mocks.ts @@ -46,8 +46,111 @@ export const BPMN_DIAGRAM_WITH_WARNINGS = ` `; - export const DMN_DIAGRAM_EMPTY = ` + + + + + + + + + + + + + + + + + + + + +`; + +export const DMN_DIAGRAM = ` + + + + + + + + + + + + + 0 + + + "GREAT Dog! I love you." + + + + + 1 + + + "Oh, Ginger." + + + + + 2 + + + "Sheesh, you silly dog." + + + + + > 2 + + + "!@#$!@#$" + + + + + + + + + + + + +`; + +export const DMN_DIAGRAM_WITH_WARNINGS = ` + + + + + + + + + + + +`; + +export const DMN_V1_1_DIAGRAM_EMPTY = ` `; -export const DMN_DIAGRAM_WITH_WARNINGS = ` +export const DMN_V1_1_DIAGRAM_WITH_WARNINGS = `