Commit 59ce8828 authored by Alexander Philipp Nowosad's avatar Alexander Philipp Nowosad
Browse files

Merge branch 'develop'

parents a28dfe8f 71e1b7aa
Pipeline #139481 passed with stages
in 10 minutes and 48 seconds
......@@ -74,12 +74,7 @@
}
]
],
"relationshipTypes": [
"requires",
"excludes",
"supports",
"hurts"
],
"relationshipTypes": ["requires", "excludes", "supports", "hurts"],
"_id": "1625144329606"
},
{
......@@ -123,9 +118,7 @@
{
"module": "Canvas Module",
"method": "editCanvas",
"outputMappings": [
[]
]
"outputMappings": [[]]
}
],
"_id": "1635786343509"
......@@ -239,9 +232,7 @@
"module": "Canvas Module",
"method": "editCanvas",
"predefinedInput": null,
"outputMappings": [
[]
]
"outputMappings": [[]]
}
],
"_id": "1635787346356"
......
......@@ -77,7 +77,7 @@ describe('Enaction', () => {
cy.contains('.btn', 'Execute next step').click();
cy.get('#name').clear();
cy.get('#name').type('TestInstance');
cy.contains('.btn', 'Create Feature Model Instance').click();
cy.contains('.btn', 'Create Canvas Artifact').click();
cy.contains('.btn', 'Execute next step').click();
cy.contains('app-canvas-building-block', 'Products & Services')
.find('.fa-plus-square')
......@@ -117,7 +117,12 @@ describe('Enaction', () => {
'have.text',
'Select Method Building Block to add to TODO'
);
cy.get(':nth-child(2) > app-development-method-selection-form > .d-flex > .btn').click();
cy.get('.modal-title').should('have.text', ' Configure Building Block Complete ');
cy.get(
':nth-child(2) > app-development-method-selection-form > .d-flex > .btn'
).click();
cy.get('.modal-title').should(
'have.text',
' Configure Building Block Complete '
);
});
});
import Chainable = Cypress.Chainable;
const expertModels = (): Chainable => {
return cy.getPane('List of Expert Business Knowledge Models');
return cy.getPane('Canvas Building Blocks');
};
const modelInfo = (): Chainable => {
return cy.getPane('Model Info');
return cy.getPane('Info');
};
const authorInfo = (): Chainable => {
......@@ -16,7 +16,7 @@ describe('Expert Models', () => {
it('import expert model', () => {
cy.fixture('expertModel').then((model) => {
cy.visit('http://localhost:4200/expertModels/');
cy.contains('div.bg-white', 'Import Expert Model').within(() => {
cy.contains('div.bg-white', 'Import Canvas Building Block').within(() => {
cy.get('input[type=file]').then((input: JQuery<HTMLInputElement>) => {
const file = new File([JSON.stringify(model)], 'Test.json', {
type: 'application/json',
......@@ -26,13 +26,10 @@ describe('Expert Models', () => {
input.get()[0].files = dataTransfer.files;
input.get()[0].dispatchEvent(new Event('change'));
});
cy.contains('.btn', 'Import Expert Model').click();
cy.contains('.btn', 'Import Canvas Building Block').click();
});
});
cy.contains(
'div.bg-white',
'List of Expert Business Knowledge Models'
).within(() => {
expertModels().within(() => {
cy.get('li').should('contain.text', 'Mobile Todo-Apps');
cy.contains('.btn', 'View').click();
});
......@@ -61,7 +58,7 @@ describe('Expert Models', () => {
cy.get('#name').type('Test Model');
cy.get('#description').clear();
cy.get('#description').type('Test Description');
cy.contains('.btn', 'Add Expert Business Knowledge Model').click();
cy.contains('.btn', 'Add Canvas Building Block').click();
expertModels().within(() => {
cy.get('.text-gray-dark').should('have.text', 'Test Model');
cy.contains('.btn', 'View').click();
......@@ -73,13 +70,13 @@ describe('Expert Models', () => {
cy.contains('tr', 'Name').find('td').should('have.text', 'Test Author');
});
cy.contains('.nav-link', '2. Edit').click();
cy.contains('.btn', 'Edit Expert Model').click();
cy.contains('.btn', 'Edit Canvas Building Block').click();
cy.get('#name').clear();
cy.get('#name').type('Test Feature A');
cy.get('#description').clear();
cy.get('#description').type('My Feature Description');
cy.contains('.btn', 'Add Feature').click();
cy.getPane('Model List').within(() => {
cy.getPane('Feature tree').within(() => {
cy.contains('li', 'Key Partners').within(() => {
cy.contains('div', 'Test Feature A')
.find('small')
......@@ -92,7 +89,7 @@ describe('Expert Models', () => {
cy.get('#type').select('Yes');
cy.contains('.btn', 'Update Feature').click();
});
cy.getPane('Model List').within(() => {
cy.getPane('Feature tree').within(() => {
cy.contains('li', 'Key Activities').within(() => {
cy.contains('li', 'Test Feature A').within(() => {
cy.get('div > :first-child > i').should(
......
......@@ -30,7 +30,7 @@ Cypress.Commands.add(
);
Cypress.Commands.add('getPane', (heading) => {
return cy.contains('div.bg-white', heading);
return cy.contains('div.bg-white.rounded', heading);
});
Cypress.Commands.add('getModal', () => {
......
......@@ -9,12 +9,14 @@ import { DatabaseRootEntry } from '../database/database-entry';
export interface CanvasDefinitionEntry extends DatabaseRootEntry {
name: string;
description: string;
rows: CanvasDefinitionCellEntry[][];
relationshipTypes: string[];
}
interface CanvasDefinitionJsonSchema {
name: string;
description: string;
rows: CanvasDefinitionCellEntry[][];
relationshipTypes: string[];
}
......@@ -23,6 +25,7 @@ export class CanvasDefinition extends DatabaseModel {
static readonly typeName = 'CanvasDefinition';
name: string;
description: string;
rows: CanvasDefinitionCell[][] = [];
......@@ -78,6 +81,7 @@ export class CanvasDefinition extends DatabaseModel {
return {
...super.toDb(),
name: this.name,
description: this.description,
rows: this.rows.map((row) => row.map((cell) => cell.toDb())),
relationshipTypes: this.relationshipTypes,
};
......@@ -86,6 +90,7 @@ export class CanvasDefinition extends DatabaseModel {
toJSON(): CanvasDefinitionJsonSchema {
return {
name: this.name,
description: this.description,
rows: this.rows.map((row) => row.map((cell) => cell.toDb())),
relationshipTypes: this.relationshipTypes,
};
......
......@@ -30,6 +30,14 @@ export class CanvasMetaModelApiService implements MetaModelApi {
}
}
async getName(model: ArtifactDataReference): Promise<string | undefined> {
const companyModel = await this.companyModelService.get(model.id);
if (companyModel.instances.length > 0) {
return companyModel.instances[0].name;
}
return undefined;
}
async copy(model: ArtifactDataReference): Promise<ArtifactDataReference> {
const companyModel = await this.companyModelService.get(model.id);
companyModel.resetDatabaseState();
......
<ng-template #createModal let-d="dismiss">
<div class="modal-header">
<h4 class="modal-title" id="modal-basic-title">Create Company Model</h4>
<h4 class="modal-title" id="modal-basic-title">Create Composed Model</h4>
<button type="button" class="close" aria-label="Close" (click)="d()">
<span aria-hidden="true">&times;</span>
</button>
......@@ -18,7 +18,7 @@
class="btn btn-sm btn-dark btn-block"
[disabled]="!createForm.valid"
>
Create Company Model
Create Composed Model
</button>
</div>
</div>
......@@ -30,7 +30,7 @@
<ng-container [formGroup]="formGroup">
<div class="form-group form-row">
<label for="companyModelSelector" class="col-sm-6 col-form-label"
>Company Model</label
>Composed Model</label
>
<div class="col-sm-6 input-group">
<select
......@@ -66,7 +66,7 @@
class="btn btn-primary ml-auto"
(click)="openCreateCompanyModelModal()"
>
Create new Company Model
Create new Composed Model
</button>
</div>
</ng-container>
<div class="nav-scroller bg-white shadow-sm">
<nav class="nav nav-underline" *ngIf="processApiService.runningProcess">
<a
class="nav-link"
[routerLink]="[
'/',
'runningprocess',
'runningprocessview',
processApiService.runningProcess._id
]"
>
{{ processApiService.runningProcess.process.name }} &ndash;
{{ processApiService.runningProcess.name }}
</a>
<a
*ngIf="processApiService.runningMethod"
class="nav-link"
[routerLink]="[
'/',
'runningprocess',
'runningprocessview',
processApiService.runningProcess._id,
'method',
processApiService.runningMethod.executionId
]"
>
{{ processApiService.runningMethod.methodName }}
</a>
<a
class="nav-link active"
[routerLink]="[]"
[queryParams]="processApiService.queryParams"
>
Create Canvas
</a>
</nav>
</div>
<app-api-navigation apiName="Create Canvas"></app-api-navigation>
<app-step-errors></app-step-errors>
<main
*ngIf="
processApiService.runningMethod != null && processApiService.isCorrectStep()
"
*ngIf="runningMethod != null && isCorrectStep()"
role="main"
class="container"
>
<div class="my-3 p-3 bg-white rounded shadow-sm">
<h6 class="border-bottom border-gray pb-2 mb-0">Create Instance</h6>
<h6 class="border-bottom border-gray pb-2 mb-0">Create Artifact</h6>
<div class="text-muted pt-3">
<form [formGroup]="form" (ngSubmit)="submit()">
<app-feature-model-instance-subform></app-feature-model-instance-subform>
......@@ -57,11 +19,16 @@
class="btn btn-sm btn-dark btn-block"
[disabled]="!form.valid"
>
Create Feature Model Instance
Create Canvas Artifact
</button>
</div>
</div>
</form>
</div>
</div>
<div class="my-3 p-3 bg-white rounded shadow-sm">
<h6 class="border-bottom border-gray pb-2">Canvas Artifact Preview</h6>
<app-feature-model [featureModel]="companyModel"></app-feature-model>
</div>
</main>
......@@ -5,6 +5,8 @@ import { Instance } from '../../../canvas-meta-model/instance';
import { CompanyModelService } from '../../../canvas-meta-model/company-model.service';
import { CanvasResolveService } from '../../canvas-resolve.service';
import { ProcessApiService } from '../process-api.service';
import { RunningMethod } from '../../../development-process-registry/running-process/running-method';
import { CompanyModel } from '../../../canvas-meta-model/company-model';
@Component({
selector: 'app-create-canvas',
......@@ -13,25 +15,27 @@ import { ProcessApiService } from '../process-api.service';
providers: [ProcessApiService],
})
export class CreateCanvasComponent implements OnInit {
companyModel: CompanyModel;
form: FormGroup;
constructor(
private canvasResolveService: CanvasResolveService,
private companyModelService: CompanyModelService,
private featureModelInstanceFormService: FeatureModelInstanceFormService,
public processApiService: ProcessApiService
private processApiService: ProcessApiService
) {}
ngOnInit(): void {
this.form = this.featureModelInstanceFormService.createForm();
this.processApiService.loaded.subscribe(() => this.loadCompanyModel());
}
async submit(): Promise<void> {
if (this.processApiService.stepInfo) {
const runningMethod = this.processApiService.runningMethod;
const companyModel = await this.companyModelService.get(
runningMethod.decision.stepDecisions[runningMethod.currentStepNumber]
.companyModelId
this.runningMethod.decision.stepDecisions[
this.runningMethod.currentStepNumber
].companyModelId
);
const instance: Partial<Instance> =
this.featureModelInstanceFormService.get(this.form.value);
......@@ -46,4 +50,20 @@ export class CreateCanvasComponent implements OnInit {
);
}
}
private async loadCompanyModel(): Promise<void> {
this.companyModel = await this.companyModelService.get(
this.runningMethod.decision.stepDecisions[
this.runningMethod.currentStepNumber
].companyModelId
);
}
get runningMethod(): RunningMethod {
return this.processApiService.runningMethod;
}
isCorrectStep(): boolean {
return this.processApiService.isCorrectStep();
}
}
import { Injectable, OnDestroy } from '@angular/core';
import { ActivatedRoute } from '@angular/router';
import { Subscription } from 'rxjs';
import { Observable, Subject, Subscription } from 'rxjs';
import { RunningProcess } from '../../development-process-registry/running-process/running-process';
import { RunningMethod } from '../../development-process-registry/running-process/running-method';
import { StepInfo } from '../../development-process-registry/module-api/step-info';
......@@ -8,6 +8,12 @@ import { RunningProcessService } from '../../development-process-registry/runnin
@Injectable()
export class ProcessApiService implements OnDestroy {
private _loadedObservable: Observable<void>;
private _loaded: Subject<void>;
get loaded(): Observable<void> {
return this._loadedObservable;
}
runningProcess: RunningProcess = null;
runningMethod: RunningMethod = null;
errorLoading = false;
......@@ -24,7 +30,9 @@ export class ProcessApiService implements OnDestroy {
this.init();
}
private init() {
private init(): void {
this._loaded = new Subject<void>();
this._loadedObservable = this._loaded.asObservable();
this.querySubscription = this.route.queryParamMap.subscribe((params) => {
this.stepInfo = {
step: params.has('step') ? +params.get('step') : undefined,
......@@ -45,9 +53,10 @@ export class ProcessApiService implements OnDestroy {
});
}
ngOnDestroy() {
ngOnDestroy(): void {
this.checkUnsubscribeChangesFeed();
this.querySubscription.unsubscribe();
this._loaded.complete();
}
isInitialized(): boolean {
......@@ -58,7 +67,11 @@ export class ProcessApiService implements OnDestroy {
return this.runningMethod.currentStepNumber === this.stepInfo.step;
}
get queryParams() {
get queryParams(): {
step: number;
runningProcessId: string;
executionId: string;
} {
return {
step: this.stepInfo.step != null ? this.stepInfo.step : undefined,
runningProcessId: this.runningProcess
......@@ -69,7 +82,7 @@ export class ProcessApiService implements OnDestroy {
};
}
private async loadProcessInfo() {
private async loadProcessInfo(): Promise<void> {
try {
this.runningProcess = await this.runningProcessService.get(
this.stepInfo.runningProcessId
......@@ -87,10 +100,12 @@ export class ProcessApiService implements OnDestroy {
if (this.runningMethod == null) {
this.errorLoading = true;
this.runningMethod = null;
} else {
this._loaded.next();
}
}
private checkUnsubscribeChangesFeed() {
private checkUnsubscribeChangesFeed(): void {
if (this.changesFeed) {
this.changesFeed.unsubscribe();
this.changesFeed = null;
......
<ng-container [formGroup]="formGroup">
<div class="form-group form-row">
<label for="definitionSelector" class="col-sm-6 col-form-label"
>Definition</label
>Canvas Model</label
>
<div class="col-sm-6">
<select
......
......@@ -11,6 +11,17 @@
/>
</div>
</div>
<div class="form-group form-row">
<label for="description" class="col-sm-4 col-form-label">Description</label>
<div class="col-sm-8">
<textarea
formControlName="description"
class="form-control"
id="description"
rows="3"
></textarea>
</div>
</div>
<div class="form-group row">
<div class="col-sm-12">
<button
......@@ -20,7 +31,7 @@
[class.btn-primary]="changed"
[disabled]="!form.valid"
>
Update Canvas Definition<span *ngIf="changed"> (unsaved changes)</span>
Update Canvas Model<span *ngIf="changed"> (unsaved changes)</span>
</button>
</div>
</div>
......
......@@ -8,7 +8,12 @@ import {
Output,
SimpleChanges,
} from '@angular/core';
import { FormBuilder, FormGroup, Validators } from '@angular/forms';
import {
FormBuilder,
FormControl,
FormGroup,
Validators,
} from '@angular/forms';
import { Subscription } from 'rxjs';
import { debounceTime, tap } from 'rxjs/operators';
import { CanvasDefinition } from '../../../canvas-meta-model/canvas-definition';
......@@ -27,6 +32,7 @@ export class CanvasDefinitionFormComponent
form: FormGroup = this.fb.group({
name: ['', Validators.required],
description: [''],
});
changed = false;
......@@ -34,7 +40,7 @@ export class CanvasDefinitionFormComponent
constructor(private fb: FormBuilder) {}
ngOnInit() {
ngOnInit(): void {
this.changeSubscription = this.form.valueChanges
.pipe(
debounceTime(300),
......@@ -49,7 +55,7 @@ export class CanvasDefinitionFormComponent
.subscribe();
}
ngOnChanges(changes: SimpleChanges) {
ngOnChanges(changes: SimpleChanges): void {
if (changes.canvasDefinition) {
const newCanvasDefinition = changes.canvasDefinition.currentValue;
const oldCanvasDefinition = changes.canvasDefinition.previousValue;
......@@ -61,38 +67,44 @@ export class CanvasDefinitionFormComponent
}
}
ngOnDestroy() {
ngOnDestroy(): void {
if (this.changeSubscription) {
this.changeSubscription.unsubscribe();
}
}
emitSubmitForm() {
emitSubmitForm(): void {
this.submitForm.emit(this.form);
}
private loadForm(canvasDefinition: CanvasDefinition) {
private loadForm(canvasDefinition: CanvasDefinition): void {
if (canvasDefinition != null) {
this.form.setValue({ name: canvasDefinition.name });
this.form.patchValue({
name: canvasDefinition.name,
description: canvasDefinition.description,
});
} else {
this.form.setValue({ name: '' });
this.form.patchValue({ name: '', description: '' });
}
}
private equalCanvasDefinitions(
canvasDefinitionA: CanvasDefinition,
canvasDefinitionB: CanvasDefinition
) {
): boolean {
if (canvasDefinitionA == null && canvasDefinitionB == null) {
return true;
}
if (canvasDefinitionA == null || canvasDefinitionB == null) {
return false;
}
return canvasDefinitionA.name === canvasDefinitionB.name;
return (
canvasDefinitionA.name === canvasDefinitionB.name &&
canvasDefinitionA.description === canvasDefinitionB.description
);
}
get nameControl() {
return this.form.get('name');
get nameControl(): FormControl {
return this.form.get('name') as FormControl;
}
}
<table class="table table-bordered" style="table-layout: fixed">
<tbody>
<tr *ngFor="let row of canvasDefinitionCells">
<ng-container *ngFor="let cell of row">
<td
*ngIf="!cell.isSpacer"
[colSpan]="cell.colspan"
[rowSpan]="cell.rowspan"
>
<b>{{ cell.name }}</b>
</td>