Skip to content
This repository was archived by the owner on May 14, 2025. It is now read-only.

Commit a357621

Browse files
BoykoAlexoodamien
authored andcommitted
Generalized properties dialog
1 parent 8f19ede commit a357621

13 files changed

+234
-149
lines changed

ui/package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -41,7 +41,7 @@
4141
"web-animations-js": "2.3.1",
4242
"stompjs": "2.3.3",
4343
"jshint": "2.9.5",
44-
"spring-flo": "git://github.com/spring-projects/spring-flo.git#c39d28dba288095fc56350ac11f46c89878f1215",
44+
"spring-flo": "git://github.com/spring-projects/spring-flo.git#303920857362413abf3795790e9ce4d8d2067539",
4545
"ng-busy": "1.4.4"
4646
},
4747
"devDependencies": {

ui/src/app/shared/flo/properties/properties-dialog.component.html

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ <h4 class="modal-title pull-left">{{ title }}</h4>
66
</div>
77
<div class="modal-body app-properties">
88
<properties-group *ngIf="propertiesGroupModel" [propertiesGroupModel]="propertiesGroupModel"
9-
[form]="propertiesFormGroup" [ngBusy]="propertiesGroupModel.loadedSubject.subscribe()"></properties-group>
9+
[form]="propertiesFormGroup" [ngBusy]="busy"></properties-group>
1010
</div>
1111
<div class="modal-footer">
1212
<button type="button" class="btn btn-default" (click)="handleCancel()">Close</button>

ui/src/app/shared/flo/properties/properties-dialog.component.ts

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,8 @@ import { BsModalRef } from 'ngx-bootstrap';
33
import { FormGroup } from '@angular/forms';
44
import { Subscription } from 'rxjs/Subscription';
55
import { PropertiesGroupModel } from '../support/properties-group-model';
6-
import { dia } from 'jointjs';
6+
import {Properties} from 'spring-flo';
7+
import PropertiesSource = Properties.PropertiesSource;
78

89
/**
910
* Component for displaying application properties and capturing their values.
@@ -51,9 +52,10 @@ export class PropertiesDialogComponent implements OnInit {
5152
ngOnInit() {
5253
}
5354

54-
setData(c: dia.Cell, graph: dia.Graph) {
55-
this.propertiesGroupModel = new PropertiesGroupModel(c);
55+
setData(propertiesSource: PropertiesSource) {
56+
this.propertiesGroupModel = new PropertiesGroupModel(propertiesSource);
5657
this.propertiesGroupModel.load();
58+
this.busy = this.propertiesGroupModel.loadedSubject.subscribe();
5759
}
5860

5961
}
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
import {Properties} from 'spring-flo';
2+
3+
/**
4+
* App property consumable by properties dialog
5+
*
6+
* @author Alex Boyko
7+
*/
8+
export interface AppUiProperty extends Properties.Property {
9+
attr: string;
10+
isSemantic: boolean;
11+
code?: CodeProperty;
12+
}
13+
14+
export interface CodeProperty {
15+
langPropertyName: string;
16+
language: string;
17+
}
Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
import { Flo, Properties } from 'spring-flo';
2+
import {AppUiProperty} from './app-ui-property';
3+
4+
/**
5+
* Able to provide AppUiProperty array from the graph node to be consumed by properties dialog
6+
* as well apply changes from the properties changed in the dialog to the graph node
7+
*
8+
* @author Alex Boyko
9+
*/
10+
export class GraphNodePropertiesSource extends Properties.DefaultCellPropertiesSource {
11+
12+
getProperties(): Promise<Array<AppUiProperty>> {
13+
return super.getProperties().then(semanticProperties => {
14+
const notationalProperties = this.createNotationalProperties();
15+
return semanticProperties ? notationalProperties.concat(<AppUiProperty[]>semanticProperties) : notationalProperties;
16+
});
17+
}
18+
19+
protected determineAttributeName(metadata: Flo.PropertyMetadata): string {
20+
const nameAttr = `props/${metadata.name}`;
21+
const idAttr = `props/${metadata.id}`;
22+
const valueFromName = this.cell.attr(nameAttr);
23+
const valueFromId = this.cell.attr(idAttr);
24+
if ((valueFromName === undefined || valueFromName === null) && !(valueFromId === undefined || valueFromId === null)) {
25+
return idAttr;
26+
} else {
27+
return nameAttr;
28+
}
29+
}
30+
31+
protected createProperty(metadata: Flo.PropertyMetadata): AppUiProperty {
32+
return {
33+
id: metadata.id,
34+
name: metadata.name,
35+
type: metadata.type,
36+
defaultValue: metadata.defaultValue,
37+
attr: this.determineAttributeName(metadata),
38+
value: this.cell.attr(this.determineAttributeName(metadata)),
39+
description: metadata.description,
40+
valueOptions: metadata.options,
41+
isSemantic: true,
42+
code: metadata.code
43+
};
44+
}
45+
46+
protected createNotationalProperties(): Array<AppUiProperty> {
47+
return [];
48+
}
49+
50+
}
Lines changed: 14 additions & 52 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
1-
import { Flo, Properties } from 'spring-flo';
1+
import { Properties } from 'spring-flo';
22
import { Validators } from '@angular/forms';
3-
import { dia } from 'jointjs';
43
import { Utils } from './utils';
4+
import {AppUiProperty} from './app-ui-property';
55

66
/**
77
* Utility class for working with Properties.
@@ -11,15 +11,11 @@ import { Utils } from './utils';
1111
*/
1212
export class PropertiesGroupModel extends Properties.PropertiesGroupModel {
1313

14-
constructor(cell: dia.Cell) {
15-
super(cell);
16-
}
17-
18-
protected createControlModel(property: Properties.Property): Properties.ControlModel<any> {
14+
protected createControlModel(property: AppUiProperty): Properties.ControlModel<any> {
1915
let inputType = Properties.InputType.TEXT;
2016
let validation: Properties.Validation;
21-
if (property.metadata) {
22-
switch (property.metadata.type) {
17+
if (property.isSemantic) {
18+
switch (property.type) {
2319
case 'java.lang.Long':
2420
case 'java.lang.Integer':
2521
inputType = Properties.InputType.NUMBER;
@@ -31,72 +27,38 @@ export class PropertiesGroupModel extends Properties.PropertiesGroupModel {
3127
case 'java.lang.Boolean':
3228
return new Properties.CheckBoxControlModel(property);
3329
default:
34-
if (property.metadata.code) {
35-
if (property.metadata.code.langPropertyName) {
30+
if (property.code) {
31+
if (property.code.langPropertyName) {
3632
return new Properties.CodeControlModelWithDynamicLanguageProperty(property,
37-
property.metadata.code.langPropertyName, this, Utils.encodeTextToDSL, Utils.decodeTextFromDSL);
33+
property.code.langPropertyName, this, Utils.encodeTextToDSL, Utils.decodeTextFromDSL);
3834
} else {
39-
return new Properties.GenericCodeControlModel(property, property.metadata.code.language,
35+
return new Properties.GenericCodeControlModel(property, property.code.language,
4036
Utils.encodeTextToDSL, Utils.decodeTextFromDSL);
4137
}
42-
} else if (Array.isArray(property.metadata.options)) {
38+
} else if (Array.isArray(property.valueOptions)) {
4339
return new Properties.SelectControlModel(property,
44-
Properties.InputType.SELECT, (<Array<string>> property.metadata.options).filter(o => o.length > 0).map(o => {
40+
Properties.InputType.SELECT, (<Array<string>> property.valueOptions).filter(o => o.length > 0).map(o => {
4541
return {
4642
name: o.charAt(0).toUpperCase() + o.substr(1).toLowerCase(),
4743
value: o === property.defaultValue ? undefined : o
4844
};
4945
}));
50-
} else if (property.metadata.name === 'password') {
46+
} else if (property.name === 'password') {
5147
inputType = Properties.InputType.PASSWORD;
52-
} else if (property.metadata.name === 'e-mail' || property.metadata.name === 'email') {
48+
} else if (property.name === 'e-mail' || property.name === 'email') {
5349
inputType = Properties.InputType.EMAIL;
5450
validation = {
5551
validator: Validators.email,
5652
errorData: [
5753
{id: 'email', message: 'Invalid E-Mail value!'}
5854
]
5955
};
60-
} else if (property.metadata.type && property.metadata.type.lastIndexOf('[]') === property.metadata.type.length - 2) {
56+
} else if (property.type && property.type.lastIndexOf('[]') === property.type.length - 2) {
6157
return new Properties.GenericListControlModel(property);
6258
}
6359
}
6460
}
6561
return new Properties.GenericControlModel(property, inputType, validation);
6662
}
6763

68-
protected createProperties(): Promise<Array<Properties.Property>> {
69-
return super.createProperties().then(semanticProperties => {
70-
const notationalProperties = this.createNotationalProperties();
71-
return semanticProperties ? notationalProperties.concat(semanticProperties) : notationalProperties;
72-
});
73-
}
74-
75-
protected determineAttributeName(metadata: Flo.PropertyMetadata): string {
76-
const nameAttr = `props/${metadata.name}`;
77-
const idAttr = `props/${metadata.id}`;
78-
const valueFromName = this.cell.attr(nameAttr);
79-
const valueFromId = this.cell.attr(idAttr);
80-
if ((valueFromName === undefined || valueFromName === null) && !(valueFromId === undefined || valueFromId === null)) {
81-
return idAttr;
82-
} else {
83-
return nameAttr;
84-
}
85-
}
86-
87-
protected createProperty(metadata: Flo.PropertyMetadata): Properties.Property {
88-
return {
89-
id: metadata.id,
90-
name: metadata.name,
91-
defaultValue: metadata.defaultValue,
92-
attr: this.determineAttributeName(metadata),
93-
value: this.cell.attr(this.determineAttributeName(metadata)),
94-
description: metadata.description,
95-
metadata: metadata
96-
};
97-
}
98-
99-
protected createNotationalProperties(): Array<Properties.Property> {
100-
return [];
101-
}
10264
}

ui/src/app/streams/flo/editor.service.ts

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@ import { dia } from 'jointjs';
2222
import { StreamPropertiesDialogComponent } from './properties/stream-properties-dialog.component';
2323
import { Utils } from './support/utils';
2424
import * as _joint from 'jointjs';
25+
import {StreamGraphPropertiesSource, StreamHead} from "./properties/stream-properties-source";
2526
const joint: any = _joint;
2627

2728
const NODE_DROPPING = false;
@@ -69,7 +70,20 @@ export class EditorService implements Flo.Editor {
6970
if (element.attr('metadata/version')) {
7071
modalRef.content.title += ` (${element.attr('metadata/version')})`;
7172
}
72-
modalRef.content.setData(element, flo.getGraph());
73+
// const streamHeads: dia.Cell[] = flo.getGraph().getElements().filter(e => Utils.canBeHeadOfStream(flo.getGraph(), e));
74+
// const streamNames = streamHeads
75+
// .filter(e => e.attr('stream-name') && e !== c)
76+
// .map(e => e.attr('stream-name'));
77+
78+
const graph = flo.getGraph();
79+
const streamHeads: dia.Cell[] = graph.getElements().filter(e => Utils.canBeHeadOfStream(graph, e));
80+
81+
const streamHead: StreamHead = streamHeads.indexOf(element) >= 0 ? {
82+
presentStreamNames: streamHeads
83+
.filter(e => e.attr('stream-name') && e !== element)
84+
.map(e => e.attr('stream-name'))
85+
} : undefined;
86+
modalRef.content.setData(new StreamGraphPropertiesSource(element, streamHead));
7387
}, pt);
7488
}
7589
}

ui/src/app/streams/flo/properties/stream-properties-dialog.component.ts

Lines changed: 13 additions & 53 deletions
Original file line numberDiff line numberDiff line change
@@ -2,14 +2,13 @@
22

33
import { Component, ViewEncapsulation } from '@angular/core';
44
import { BsModalRef } from 'ngx-bootstrap';
5-
import { Flo, Properties } from 'spring-flo';
5+
import { Properties } from 'spring-flo';
66
import { Validators } from '@angular/forms';
7-
import { dia } from 'jointjs';
87
import { StreamsService } from '../../streams.service';
9-
import { Utils } from '../support/utils';
10-
import { ApplicationType } from '../../../shared/model/application-type';
118
import { PropertiesDialogComponent } from '../../../shared/flo/properties/properties-dialog.component';
129
import { PropertiesGroupModel } from '../../../shared/flo/support/properties-group-model';
10+
import {AppUiProperty} from '../../../shared/flo/support/app-ui-property';
11+
import {StreamAppPropertiesSource} from './stream-properties-source';
1312

1413
// Workaround to load jshint to have linting working for JS snippet inside the props dialog
1514
import { JSHINT } from 'jshint';
@@ -23,20 +22,18 @@ if (!(<any>window).JSHINT) {
2322
* @author Alex Boyko
2423
* @author Andy Clement
2524
*/
26-
class StreamPropertiesGroupModel extends PropertiesGroupModel {
25+
export class StreamPropertiesGroupModel extends PropertiesGroupModel {
2726

28-
constructor(cell: dia.Cell,
29-
private streamHeads: Array<dia.Cell>,
30-
private isStreamHead: boolean,
27+
constructor(propertiesSource: StreamAppPropertiesSource,
3128
private streamService: StreamsService
3229
) {
33-
super(cell);
30+
super(propertiesSource);
3431
}
3532

36-
protected createControlModel(property: Properties.Property): Properties.ControlModel<any> {
33+
protected createControlModel(property: AppUiProperty): Properties.ControlModel<any> {
3734
const inputType = Properties.InputType.TEXT;
3835
let validation: Properties.Validation;
39-
if (property.metadata) {
36+
if (property.isSemantic) {
4037
return super.createControlModel(property);
4138
} else {
4239
// Notational properties
@@ -51,9 +48,7 @@ class StreamPropertiesGroupModel extends PropertiesGroupModel {
5148
validation = {
5249
validator: [
5350
Validators.pattern(/^[\w_]+[\w_-]*$/),
54-
Properties.Validators.noneOf(this.streamHeads
55-
.filter(e => e.attr('stream-name') && e !== this.cell)
56-
.map(e => e.attr('stream-name')))
51+
Properties.Validators.noneOf((<StreamAppPropertiesSource>this.propertiesSource).getStreamHead().presentStreamNames)
5752
],
5853
asyncValidator: Properties.Validators.uniqueResource((value) => this.streamService.getDefinition(value), 500),
5954
errorData: [
@@ -67,41 +62,6 @@ class StreamPropertiesGroupModel extends PropertiesGroupModel {
6762
return new Properties.GenericControlModel(property, inputType, validation);
6863
}
6964

70-
protected createNotationalProperties(): Array<Properties.Property> {
71-
const notationalProperties = [];
72-
if (typeof ApplicationType[this.cell.attr('metadata/group')] === 'number') {
73-
notationalProperties.push({
74-
id: 'label',
75-
name: 'label',
76-
defaultValue: this.cell.attr('metadata/name'),
77-
attr: 'node-name',
78-
value: this.cell.attr('node-name'),
79-
description: 'Label of the app',
80-
metadata: null
81-
});
82-
}
83-
if (this.isStreamHead) {
84-
notationalProperties.push({
85-
id: 'stream-name',
86-
name: 'stream name',
87-
value: this.cell.attr('stream-name'),
88-
defaultValue: '',
89-
description: 'The name of the stream started by this app',
90-
attr: 'stream-name',
91-
metadata: null
92-
});
93-
}
94-
return notationalProperties;
95-
}
96-
97-
protected determineAttributeName(metadata: Flo.PropertyMetadata): string {
98-
if (this.cell.attr('metadata/group') === 'other') {
99-
// For something in the other group (like tap) use the id not the name of the property
100-
return `props/${metadata.id}`;
101-
}
102-
return super.determineAttributeName(metadata);
103-
}
104-
10565
}
10666

10767
/**
@@ -126,10 +86,10 @@ export class StreamPropertiesDialogComponent extends PropertiesDialogComponent {
12686
super(bsModalRef);
12787
}
12888

129-
setData(c: dia.Cell, graph: dia.Graph) {
130-
const streamHeads = graph.getElements().filter(e => Utils.canBeHeadOfStream(graph, e));
131-
this.propertiesGroupModel = new StreamPropertiesGroupModel(c, streamHeads,
132-
(<Array<dia.Cell>>streamHeads).indexOf(c) >= 0, this.streamService);
89+
setData(propertiesSource: StreamAppPropertiesSource) {
90+
this.propertiesGroupModel = new StreamPropertiesGroupModel(
91+
propertiesSource,
92+
this.streamService);
13393
this.propertiesGroupModel.load();
13494
}
13595

0 commit comments

Comments
 (0)