Commit d1bc8b76 authored by Alexander Philipp Nowosad's avatar Alexander Philipp Nowosad
Browse files

Add warnings about missing artifacts

parent 5875c3b5
......@@ -190,6 +190,37 @@
</ul>
</ng-template>
</ngb-panel>
<ngb-panel title="Missing Artifacts">
<ng-template ngbPanelContent>
<div *ngIf="missingArtifacts.length === 0" class="alert alert-success mb-0">
There are no warnings about missing artifacts
</div>
<ul *ngIf="missingArtifacts.length > 0">
<li *ngFor="let warning of missingArtifacts">
{{warning.elementName}} <a [routerLink]="[]"
(click)="selectElement(warning.elementId)">{{warning.name}}</a>
is missing the following Artifacts
<ul>
<li *ngFor="let artifact of warning.artifacts">{{artifact.name}}</li>
</ul>
</li>
</ul>
</ng-template>
</ngb-panel>
<ngb-panel title="Unreachable">
<ng-template ngbPanelContent>
<div *ngIf="unreachableActivities.length === 0" class="alert alert-success mb-0">
There are no warnings about unreachable methods or activities
</div>
<ul *ngIf="unreachableActivities.length > 0">
<li *ngFor="let warning of unreachableActivities">
{{warning.elementName}} <a [routerLink]="[]"
(click)="selectElement(warning.elementId)">{{warning.name}}</a>
can not be reached.
</li>
</ul>
</ng-template>
</ngb-panel>
</ngb-accordion>
</div>
</div>
......
......@@ -21,6 +21,7 @@ import { DevelopmentMethodService } from '../../development-process-registry/dev
import { ProcessPattern } from '../shared/process-pattern';
import { DiagramComponentInterface } from '../shared/diagram-component-interface';
import { SituationalFactor } from '../../development-process-registry/situational-factor';
import { Artifact } from '../../development-process-registry/artifact';
@Component({
selector: 'app-bm-process-diagram',
......@@ -43,6 +44,9 @@ export class BmProcessDiagramComponent implements DiagramComponentInterface, OnI
isGeneratingWarnings = false;
private wantsToGenerateWarnings = false;
missingArtifacts: { elementId: string, elementName: string, name: string, artifacts: Artifact[] }[] = [];
unreachableActivities: { elementId: string, elementName: string, name: string }[] = [];
modalElement;
modalDevelopmentMethod: DevelopmentMethod;
modalProcessPattern: ProcessPattern;
......@@ -103,6 +107,7 @@ export class BmProcessDiagramComponent implements DiagramComponentInterface, OnI
this.bpmnService.resizeView(this.modeler);
}
this.checkWarnings();
this.checkArtifacts();
}).catch(
error => console.log('LoadBmProcess: ' + error)
);
......@@ -368,4 +373,19 @@ export class BmProcessDiagramComponent implements DiagramComponentInterface, OnI
});
return distance;
}
checkArtifacts() {
const missingMap = this.bpmnService.checkArtifacts(this.modeler, this.bmProcess.decisions);
const decisions = this.bmProcess.decisions;
const missing = Object.entries(missingMap).map(([key, value]) => {
return {
artifacts: value,
elementId: key,
elementName: key in decisions ? 'Method' : 'Activity',
name: key in decisions ? decisions[key].method.name : this.modeler.get('elementRegistry').get(key).businessObject.name,
};
});
this.missingArtifacts = missing.filter((element) => element.artifacts);
this.unreachableActivities = missing.filter((element) => !element.artifacts);
}
}
......@@ -11,6 +11,8 @@ import { pointInRect } from 'diagram-js/lib/util/Geometry';
import { DevelopmentMethod } from '../../development-process-registry/development-method';
import { ProcessPattern } from './process-pattern';
import { Type } from '../../development-process-registry/type';
import { Artifact } from '../../development-process-registry/artifact';
import { Decision } from '../../development-process-registry/decision';
@Injectable({
providedIn: 'root'
......@@ -252,6 +254,17 @@ export class BpmnService {
.map((element) => element.businessObject.get('processPatternId'));
}
/**
* Check whether there are artifacts that are not created earlier
*
* @param modeler the modeler of the current model
* @param decisions the decisions of the bm process
*/
checkArtifacts(modeler: BpmnModeler, decisions: { [elementId: string]: Decision }): { [elementId: string]: Artifact[] } {
const operation = new BpmnCheckArtifacts(modeler, decisions);
return operation.checkArtifacts();
}
}
class BpmnOperation {
......@@ -748,6 +761,187 @@ class BpmnAttachOperation extends BpmnAppendOperation {
}
class BpmnCheckArtifacts {
private readonly elementRegistry;
private readonly decisions: { [elementId: string]: Decision };
private visitedNodes = new Set();
private artifacts: { [elementId: string]: Artifact[] } = {};
private currentNodes = [];
constructor(modeler: BpmnModeler, decisions: { [elementId: string]: Decision }) {
this.elementRegistry = modeler.get('elementRegistry');
this.decisions = decisions;
}
checkArtifacts(): { [elementId: string]: Artifact[] | null } {
const missingMap = {};
const startingElement = this.elementRegistry.find((element) => is(element, 'bpmn:StartEvent') && is(element.parent, 'bpmn:Process'));
this.currentNodes.push(startingElement);
while (this.currentNodes.length > 0) {
const currentNode = this.currentNodes[0];
this.currentNodes.splice(0, 1);
if (this.visitedNodes.has(currentNode)) {
continue;
}
let incomingArtifacts;
if (is(currentNode, 'bpmn:ParallelGateway')) {
incomingArtifacts = this.getIncomingArtifactsUnion(currentNode);
if (incomingArtifacts === null) {
continue;
}
this.visitedNodes.add(currentNode);
} else if (is(currentNode, 'bpmn:ExclusiveGateway')) {
incomingArtifacts = this.getIncomingArtifactsIntersect(currentNode);
if (incomingArtifacts === null) {
incomingArtifacts = this.getIncomingArtifacts(currentNode);
} else {
this.visitedNodes.add(currentNode);
}
} else {
incomingArtifacts = this.getIncomingArtifactsUnion(currentNode);
if (incomingArtifacts === null) {
incomingArtifacts = this.getIncomingArtifacts(currentNode);
} else {
this.visitedNodes.add(currentNode);
}
}
const incomingArtifactIds = incomingArtifacts.map((artifact) => artifact._id);
const neededArtifacts = this.getNeededArtifacts(currentNode);
const missing = neededArtifacts.filter((artifact) => !incomingArtifactIds.includes(artifact._id));
if (missing.length > 0 || currentNode.id in missingMap) {
missingMap[currentNode.id] = missing;
}
this.artifacts[currentNode.id] = [
...incomingArtifacts,
...this.getCreatedArtifacts(currentNode),
];
this.currentNodes.push(...this.getTargets(currentNode).filter((node) => !this.visitedNodes.has(node)));
}
this.elementRegistry.getAll()
.filter((element) => is(element, 'bpmn:FlowNode') && !this.visitedNodes.has(element) && !is(element, 'bpmn:SubProcess'))
.forEach((element) => missingMap[element.id] = null);
return missingMap;
}
getNeededArtifacts(element): Artifact[] {
return this.getDecisionArtifacts(element, true);
}
getCreatedArtifacts(element): Artifact[] {
return this.getDecisionArtifacts(element, false);
}
getDecisionArtifacts(element, input = false): Artifact[] {
if (element.id in this.decisions) {
const artifacts: Artifact[] = [];
const decision = this.decisions[element.id];
const decisionArtifacts = input ? decision.inputArtifacts : decision.outputArtifacts;
if (decisionArtifacts.selectedGroup === null || decisionArtifacts.selectedGroup === undefined) {
return [];
}
const method = decision.method;
const group = input
? method.inputArtifacts[decisionArtifacts.selectedGroup]
: method.outputArtifacts[decisionArtifacts.selectedGroup];
group.forEach((selection, index) => {
if (selection.element) {
artifacts.push(selection.element);
} else {
artifacts.push(...decisionArtifacts.elements[index].filter((e) => e));
}
});
return artifacts;
}
return [];
}
getIncomingArtifactsUnion(element): Artifact[] {
const artifacts: Artifact[] = [];
const sources = this.getSources(element);
if (sources.some((source) => !(source.id in this.artifacts))) {
return null;
}
sources.forEach((source) => artifacts.push(...this.getArtifacts(source)));
return artifacts;
}
getIncomingArtifactsIntersect(element): Artifact[] {
const artifacts: Artifact[][] = [];
const sources = this.getSources(element);
if (sources.length === 0) {
return [];
}
if (sources.some((source) => !(source.id in this.artifacts))) {
return null;
}
sources.forEach((source) => artifacts.push(this.getArtifacts(source)));
const others: string[][] = artifacts.map((group) => group.map((artifact) => artifact._id));
others.slice(0, 1);
let resultingArtifacts: Artifact[] = artifacts[0];
others.forEach((group) => resultingArtifacts = resultingArtifacts.filter((artifact) => group.includes(artifact._id)));
return resultingArtifacts;
}
getIncomingArtifacts(element): Artifact[] {
const artifacts: Artifact[] = [];
const sources = this.getSources(element);
sources.forEach((source) => artifacts.push(...this.getArtifacts(source)));
return artifacts;
}
getArtifacts(element): Artifact[] {
if (element.id in this.artifacts) {
return this.artifacts[element.id];
}
return [];
}
getSources(element): any[] {
let node;
let incomingFlows;
for (
node = element, incomingFlows = node.incoming;
incomingFlows.length === 0;
node = node.parent, incomingFlows = node.incoming
) {
if (is(node.parent, 'bpmn:Process')) {
return [];
}
}
return incomingFlows.map((flow) => flow.source).map((source) => {
if (is(source, 'bpmn:SubProcess')) {
return source.children.find((e) => is(e, 'bpmn:EndEvent'));
} else {
return source;
}
});
}
getTargets(element): any[] {
let node;
let outgoingFlows;
for (
node = element, outgoingFlows = node.outgoing;
outgoingFlows.length === 0;
node = node.parent, outgoingFlows = node.outgoing
) {
if (is(node.parent, 'bpmn:Process')) {
return [];
}
}
return outgoingFlows.map((flow) => flow.target).map((target) => {
if (is(target, 'bpmn:SubProcess')) {
return target.children.find((e) => is(e, 'bpmn:StartEvent'));
} else {
return target;
}
});
}
}
function mapTypes(moddle, element: { list: string, element: Type }) {
return moddle.create('bmdl:Type', {
list: element.list,
......
Supports Markdown
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment