src/app/streams/stream-deploy/builder/builder.component.ts
TODO
changeDetection | ChangeDetectionStrategy.OnPush |
selector | app-stream-deploy-builder |
styleUrls | styles.scss |
templateUrl | builder.component.html |
Properties |
Methods |
|
Inputs |
Outputs |
constructor(streamDeployService: StreamDeployService, changeDetector: ChangeDetectorRef, notificationService: NotificationService, bsModalService: BsModalService)
|
||||||||||||||||||||
Parameters :
|
id
|
Stream ID
Type: |
isDeployed
|
Is Deployed
Default value: |
properties
|
Properties to load
Type: |
copyProperties
|
Emit for request copy $event type: EventEmitter
|
deploy
|
Emit for request deploy $event type: EventEmitter
|
exportProperties
|
Emit for request export $event type: EventEmitter
|
update
|
Emits on destroy component with the current value $event type: EventEmitter
|
Private build | ||||||||
build(streamDeployConfig: StreamDeployConfig)
|
||||||||
Build the Group Form
Parameters :
Returns :
{ formGroup: any; builderAppsProperties: {}; builderDeploymentProperties: { global: {}; apps: {};...
|
copyToClipboard |
copyToClipboard()
|
Copye to clipboard
Returns :
void
|
deployStream |
deployStream()
|
Emit a request deploy
Returns :
void
|
exportProps |
exportProps()
|
Emit a request export
Returns :
void
|
getAppProperties | ||||||||||||
getAppProperties(builderAppsProperties: literal type, appId: string)
|
||||||||||||
Load the properties of an app
Parameters :
Returns :
Array<literal type>
|
getDeploymentProperties | ||||||||||||
getDeploymentProperties(builderDeploymentProperties: literal type, appId?: string)
|
||||||||||||
Load the deployment properties of an app or global
Parameters :
Returns :
Array<literal type>
|
Private getProperties |
getProperties()
|
Return an array of properties
Returns :
Array<string>
|
isErrorPlatform | ||||||||||||
isErrorPlatform(platforms: Array
|
||||||||||||
Return true if the platform is on error
Parameters :
Returns :
boolean
|
isErrorVersion |
isErrorVersion(app: any, version: string)
|
Return true if the version is not invalid
Returns :
boolean
|
isInvalidPlatform | ||||||||||||
isInvalidPlatform(platforms: Array
|
||||||||||||
Return true if the platform is invalid (invalid value)
Parameters :
Returns :
boolean
|
isSubmittable | ||||||||
isSubmittable(builder: )
|
||||||||
Return true if the builder is valid
Parameters :
Returns :
boolean
|
ngOnDestroy |
ngOnDestroy()
|
On Destroy
Returns :
void
|
ngOnInit |
ngOnInit()
|
On Init
Returns :
void
|
openApp | ||||||||||||
openApp(builder: , app: any)
|
||||||||||||
Open Application Properties modal
Parameters :
Returns :
void
|
openDeploymentProperties | ||||||||||||
openDeploymentProperties(builder: , appId?: string)
|
||||||||||||
Open Deployment Properties modal for an app or global
Parameters :
Returns :
void
|
Private populate | ||||||||
populate(builder: )
|
||||||||
Populate values
Parameters :
Returns :
any
|
Private populateApp | ||||||||
populateApp(builder?: any)
|
||||||||
Populate values app
Parameters :
Returns :
any
|
removeError | ||||||||
removeError(value: literal type)
|
||||||||
Remove an error property
Parameters :
Returns :
void
|
tooltip | ||||||||||||
tooltip(streamDeployConfig: , control: AbstractControl)
|
||||||||||||
Generate a tooltip
Parameters :
Returns :
string
|
Private updateFormArray |
updateFormArray(builder: , array: FormArray, appKey: string, key: string, value: )
|
Returns :
void
|
builder$ |
builder$:
|
Type : Observable<any>
|
Builder observable Contains the form and the input data |
refBuilder |
refBuilder:
|
Builder Reference |
state |
state:
|
Type : any
|
States |
import {
ChangeDetectionStrategy, ChangeDetectorRef, Component, EventEmitter, Input, OnDestroy, OnInit,
Output
} from '@angular/core';
import { AbstractControl, FormArray, FormControl, FormGroup } from '@angular/forms';
import { StreamDeployService } from '../stream-deploy.service';
import { map } from 'rxjs/operators';
import { StreamDeployConfig } from '../../model/stream-deploy-config';
import { Observable } from 'rxjs';
import { StreamDeployValidator } from '../stream-deploy.validator';
import { AppPropertiesSource, StreamDeployAppPropertiesComponent } from '../app-properties/app-properties.component';
import { BsModalService } from 'ngx-bootstrap';
import { Properties } from 'spring-flo';
import { NotificationService } from '../../../shared/services/notification.service';
import {
GroupPropertiesSource, GroupPropertiesSources,
PropertiesGroupsDialogComponent
} from '../../../shared/flo/properties-groups/properties-groups-dialog.component';
/**
* TODO
*
* @author Damien Vitrac
* @author Janne Valkealahti
*/
@Component({
selector: 'app-stream-deploy-builder',
templateUrl: 'builder.component.html',
styleUrls: ['styles.scss'],
changeDetection: ChangeDetectionStrategy.OnPush
})
export class StreamDeployBuilderComponent implements OnInit, OnDestroy {
/**
* Stream ID
*/
@Input() id: string;
/**
* Emits on destroy component with the current value
*/
@Output() update = new EventEmitter();
/**
* Emit for request export
*/
@Output() exportProperties = new EventEmitter();
/**
* Emit for request deploy
*/
@Output() deploy = new EventEmitter();
/**
* Emit for request copy
*/
@Output() copyProperties = new EventEmitter();
/**
* Properties to load
*/
@Input() properties: Array<string> = [];
/**
* Is Deployed
*/
@Input() isDeployed = false;
/**
* Builder observable
* Contains the form and the input data
*/
builder$: Observable<any>;
/**
* Builder Reference
*/
refBuilder;
/**
* States
*/
state: any = {
platform: true,
deployer: true,
app: true,
specificPlatform: true
};
constructor(private streamDeployService: StreamDeployService,
private changeDetector: ChangeDetectorRef,
private notificationService: NotificationService,
private bsModalService: BsModalService) {
}
/**
* On Init
*/
ngOnInit() {
this.builder$ = this.streamDeployService
.config(this.id)
.pipe(map((streamDeployConfig) => this.build(streamDeployConfig)))
.pipe(map((builder) => this.populate(builder)))
.pipe(map((builder) => this.populateApp(builder)));
}
/**
* On Destroy
*/
ngOnDestroy() {
if (this.refBuilder) {
this.update.emit(this.getProperties());
}
}
/**
* Return an array of properties
* @returns {Array<string>}
*/
private getProperties(): Array<string> {
const result: Array<string> = [];
const isEmpty = (control: AbstractControl) => !control || (control.value === '' || control.value === null);
const deployers: FormArray = this.refBuilder.formGroup.get('deployers') as FormArray;
const appsVersion: FormGroup = this.refBuilder.formGroup.get('appsVersion') as FormGroup;
const global: FormArray = this.refBuilder.formGroup.get('global') as FormArray;
const specificPlatform: FormArray = this.refBuilder.formGroup.get('specificPlatform') as FormArray;
// Platform
if (!isEmpty(this.refBuilder.formGroup.get('platform'))) {
result.push(`spring.cloud.dataflow.skipper.platformName=${this.refBuilder.formGroup.get('platform').value}`);
}
// Deployers
this.refBuilder.streamDeployConfig.deployers.forEach((deployer, index) => {
if (!isEmpty(deployers.controls[index].get('global'))) {
result.push(`deployer.*.${deployer.id}=${deployers.controls[index].get('global').value}`);
}
this.refBuilder.streamDeployConfig.apps.forEach((app) => {
if (!isEmpty(deployers.controls[index].get(app.name))) {
result.push(`deployer.${app.name}.${deployer.id}=${deployers.controls[index].get(app.name).value}`);
}
});
});
// Dynamic Form
[specificPlatform, global].forEach((arr, index) => {
const keyStart = (!index) ? 'deployer' : 'app';
arr.controls.forEach((line: FormGroup) => {
if (!isEmpty(line.get('property'))) {
const key = line.get('property').value;
if (!isEmpty(line.get('global'))) {
result.push(`${keyStart}.*.${key}=${line.get('global').value}`);
}
this.refBuilder.streamDeployConfig.apps.forEach((app) => {
if (!isEmpty(line.get(app.name))) {
result.push(`${keyStart}.${app.name}.${key}=${line.get(app.name).value}`);
}
});
}
});
});
// Apps Version (appsVersion)
this.refBuilder.streamDeployConfig.apps.forEach((app) => {
if (!isEmpty(appsVersion.get(app.name))) {
result.push(`version.${app.name}=${appsVersion.get(app.name).value}`);
}
// App deployment props set via modal
this.getDeploymentProperties(this.refBuilder.builderDeploymentProperties, app.name).forEach((keyValue) => {
result.push(`deployer.${app.name}.${keyValue.key.replace(/spring.cloud.deployer./, '')}=${keyValue.value}`);
});
});
// Apps Properties
Object.keys(this.refBuilder.builderAppsProperties).forEach((key: string) => {
this.getAppProperties(this.refBuilder.builderAppsProperties, key).forEach((keyValue) => {
result.push(`app.${key}.${keyValue.key}=${keyValue.value}`);
});
});
// Global deployment props set via modal
this.getDeploymentProperties(this.refBuilder.builderDeploymentProperties).forEach((keyValue) => {
result.push(`deployer.*.${keyValue.key.replace(/spring.cloud.deployer./, '')}=${keyValue.value}`);
});
// Errors
this.refBuilder.errors.global.forEach((error) => {
result.push(error);
});
this.refBuilder.errors.app.forEach((error) => {
result.push(error);
});
return result;
}
private updateFormArray(builder, array: FormArray, appKey: string, key: string, value) {
let group: FormGroup;
const lines = array.controls
.filter((formGroup: FormGroup) => key === formGroup.get('property').value);
if (lines.length > 0) {
group = lines[0] as FormGroup;
} else {
group = new FormGroup({
'property': new FormControl('', [StreamDeployValidator.key]),
'global': new FormControl('')
}, { validators: StreamDeployValidator.keyRequired });
builder.streamDeployConfig.apps.forEach((app) => {
group.addControl(app.name, new FormControl(''));
});
array.push(group);
}
group.get('property').setValue(key);
group.get(appKey === '*' ? 'global' : appKey).setValue(value);
}
/**
* Populate values app
*/
private populateApp(builder?: any) {
builder = builder || this.refBuilder;
if (!builder) {
return false;
}
builder.formGroup.get('global').controls = [];
builder.errors.app = [];
const appNames: Array<string> = builder.streamDeployConfig.apps.map((app, index) => {
const appInfo = builder.streamDeployConfig.apps[index];
builder.builderAppsProperties[app.name] = (appInfo && appInfo.options)
? builder.builderAppsProperties[app.name] = appInfo.options
.map((property) => Object.assign({}, property))
: [];
return app.name;
});
const add = (array: FormArray) => {
const group = new FormGroup({
'property': new FormControl('', [StreamDeployValidator.key]),
'global': new FormControl('')
}, { validators: StreamDeployValidator.keyRequired });
builder.streamDeployConfig.apps.forEach((app) => {
group.addControl(app.name, new FormControl(''));
});
array.push(group);
};
this.properties.forEach((line: string) => {
const arr = line.split(/=(.*)/);
const key = arr[0] as string;
const value = arr[1] as string;
const appKey = key.split('.')[1];
if (StreamDeployService.app.is(key)) {
const keyReduce = StreamDeployService.app.extract(key);
if ((appKey !== '*' && appNames.indexOf(appKey) === -1) || keyReduce === '') {
// Error: app not found
builder.errors.app.push(line);
} else {
let free = true;
if (appNames.indexOf(appKey) > -1) {
const appProperties = builder.streamDeployConfig.apps[appNames.indexOf(appKey)];
if (appProperties.options && !appProperties.optionsState.isInvalid) {
const option = builder.builderAppsProperties[appKey].find(opt => {
return opt.name === keyReduce || opt.id === keyReduce;
});
if (option) {
option.value = value;
free = false;
}
}
}
if (free) {
this.updateFormArray(builder, builder.formGroup.get('global') as FormArray, appKey, keyReduce, value);
}
}
}
});
add(builder.formGroup.get('global'));
return builder;
}
/**
* Populate values
*/
private populate(builder) {
this.refBuilder = builder;
const appNames: Array<string> = builder.streamDeployConfig.apps.map(app => app.name);
const deployerKeys: Array<string> = builder.streamDeployConfig.deployers.map(deployer => deployer.name);
builder.errors.global = [];
// we need to iterate twice to get a possible platform name set
// as it's needed later and we don't control order of these properties
this.properties.some((line: string) => {
const arr = line.split(/=(.*)/);
const key = arr[0] as string;
const value = arr[1] as string;
if (StreamDeployService.platform.is(key)) {
builder.formGroup.get('platform').setValue(value);
const platform = builder.streamDeployConfig.platform.values.find(p => p.name === value);
builder.builderDeploymentProperties.global = [];
builder.streamDeployConfig.apps.forEach((app: any) => {
builder.builderDeploymentProperties.apps[app.name] = [];
});
if (platform) {
platform.options.forEach(o => {
builder.builderDeploymentProperties.global.push(Object.assign({isSemantic: true}, o));
builder.streamDeployConfig.apps.forEach((app: any) => {
builder.builderDeploymentProperties.apps[app.name].push(Object.assign({isSemantic: true}, o));
});
});
}
// got it, break from loop
return true;
}
return false;
});
this.properties.forEach((line: string) => {
const arr = line.split(/=(.*)/);
const key = arr[0] as string;
const value = arr[1] as string;
const appKey = key.split('.')[1];
if (StreamDeployService.platform.is(key)) {
// consume but don't do anything, otherwise error is added
// platform value is set in first iteration
} else if (StreamDeployService.deployer.is(key)) {
// Deployer
const keyReduce = StreamDeployService.deployer.extract(key);
if ((appKey !== '*' && appNames.indexOf(appKey) === -1) || keyReduce === '') {
// Error: app not found / * not found
builder.errors.global.push(line);
} else {
if (deployerKeys.indexOf(keyReduce) > -1) {
builder.formGroup.get('deployers')
.controls[deployerKeys.indexOf(keyReduce)]
.get(appKey === '*' ? 'global' : appKey).setValue(value);
} else {
const keymatch = 'spring.cloud.deployer.' + keyReduce;
// go through deployment properties for global and apps and
// mark match if it looks property is handled by modal, otherwise
// it belong to form array
let match = false;
if (key.indexOf('deployer.*.') > -1) {
builder.builderDeploymentProperties.global.forEach(p => {
if (keymatch === p.id) {
match = true;
p.value = value;
}
});
} else if (key.indexOf('deployer.' + appKey + '.') > -1) {
builder.builderDeploymentProperties.apps[appKey].forEach(p => {
if (keymatch === p.id) {
match = true;
p.value = value;
}
});
}
if (!match) {
this.updateFormArray(builder, builder.formGroup.get('specificPlatform') as FormArray, appKey,
keyReduce, value);
}
}
}
} else if (StreamDeployService.version.is(key)) {
// Version
if (appNames.indexOf(appKey) === -1) {
// Error: app not found
builder.errors.global.push(line);
} else {
const app = this.refBuilder.streamDeployConfig.apps.find((app_: any) => app_.name === appKey);
// Populate if it's not the default version
if (!app || app.version !== value) {
builder.formGroup.get('appsVersion').get(appKey).setValue(value);
}
}
} else if (!StreamDeployService.app.is(key)) {
// Invalid Key
builder.errors.global.push(line);
}
});
return builder;
}
/**
* Build the Group Form
*/
private build(streamDeployConfig: StreamDeployConfig) {
const formGroup: FormGroup = new FormGroup({});
const getValue = (defaultValue) => !defaultValue ? '' : defaultValue;
const builderAppsProperties = {};
const builderDeploymentProperties = {global: [], apps: {}};
// Platform
const platformControl = new FormControl(getValue(streamDeployConfig.platform.defaultValue),
(formControl: FormControl) => {
if (this.isErrorPlatform(streamDeployConfig.platform.values, formControl.value)) {
return { invalid: true };
}
if (streamDeployConfig.platform.values.length > 1 && !formControl.value) {
return { invalid: true };
}
return null;
});
const defaultPlatform = streamDeployConfig.platform.values.length === 1 ?
streamDeployConfig.platform.values[0] : null;
const populateDeployerProperties = (value) => {
builderDeploymentProperties.global = [];
streamDeployConfig.apps.forEach((app: any) => {
builderDeploymentProperties.apps[app.name] = [];
});
const platform = streamDeployConfig.platform.values.find(p => p.name === value);
if (platform) {
platform.options.forEach(o => {
builderDeploymentProperties.global.push(Object.assign({isSemantic: true}, o));
streamDeployConfig.apps.forEach((app: any) => {
builderDeploymentProperties.apps[app.name].push(Object.assign({isSemantic: true}, o));
});
});
}
};
if (defaultPlatform) {
populateDeployerProperties(defaultPlatform.name);
}
platformControl.valueChanges.subscribe((value) => {
if (!defaultPlatform) {
populateDeployerProperties(value);
}
});
formGroup.addControl('platform', platformControl);
// Deployers
const deployers = new FormArray([]);
streamDeployConfig.deployers.forEach((deployer: any) => {
const groupDeployer: FormGroup = new FormGroup({});
const validators = [];
if (deployer.type === 'java.lang.Integer') {
validators.push(StreamDeployValidator.number);
}
groupDeployer.addControl('global', new FormControl(getValue(deployer.defaultValue), validators));
streamDeployConfig.apps.forEach((app: any) => {
groupDeployer.addControl(app.name, new FormControl('', validators));
});
deployers.push(groupDeployer);
});
// Applications
const appsVersion = new FormGroup({});
streamDeployConfig.apps.forEach((app: any) => {
builderAppsProperties[app.name] = [];
const control = new FormControl(null,
(formControl: FormControl) => {
if (this.isErrorVersion(app, formControl.value)) {
return { invalid: true };
}
return null;
});
control.valueChanges.subscribe((value) => {
builderAppsProperties[app.name] = [];
if (this.isErrorVersion(app, value)) {
app.optionsState.isInvalid = true;
app.options = null;
this.changeDetector.markForCheck();
} else {
app.optionsState.isInvalid = false;
app.optionsState.isOnError = false;
app.optionsState.isLoading = true;
this.streamDeployService.appDetails(app.type, app.origin, value).subscribe((options) => {
app.options = options;
}, (error) => {
app.options = [];
app.optionsState.isOnError = true;
}, () => {
app.optionsState.isLoading = false;
this.changeDetector.markForCheck();
this.populateApp();
});
}
});
control.setValue(getValue(app.defaultValue));
appsVersion.addControl(app.name, control);
});
// Useful methods for FormArray
const add = (array: FormArray) => {
const group = new FormGroup({
'property': new FormControl('', [StreamDeployValidator.key]),
'global': new FormControl('')
}, { validators: StreamDeployValidator.keyRequired });
streamDeployConfig.apps.forEach((app) => {
group.addControl(app.name, new FormControl(''));
});
array.push(group);
};
const isEmpty = (dictionary): boolean => Object.entries(dictionary).every((a) => a[1] === '');
const clean = (val: Array<any>, array: FormArray) => {
const toRemove = val.map((a, index) =>
((index < (val.length - 1) && isEmpty(a)) ? index : null)).filter((a) => a != null);
toRemove.reverse().forEach((a) => {
array.removeAt(a);
});
if (!isEmpty(val[val.length - 1])) {
return add(array);
}
};
// Dynamic App properties
const globalControls: FormArray = new FormArray([]);
add(globalControls);
globalControls.valueChanges.subscribe((val: Array<any>) => {
clean(val, globalControls);
});
// Dynamic Platform properties
const specificPlatformControls: FormArray = new FormArray([]);
add(specificPlatformControls);
specificPlatformControls.valueChanges.subscribe((val: Array<any>) => {
clean(val, specificPlatformControls);
});
formGroup.addControl('deployers', deployers);
formGroup.addControl('appsVersion', appsVersion);
formGroup.addControl('global', globalControls);
formGroup.addControl('specificPlatform', specificPlatformControls);
return {
formGroup: formGroup,
builderAppsProperties: builderAppsProperties,
builderDeploymentProperties: builderDeploymentProperties,
streamDeployConfig: streamDeployConfig,
errors: {
global: [],
app: []
}
};
}
/**
* Return true if the version is not invalid
*
* @param {any} app
* @param {string} version
* @returns {boolean}
*/
isErrorVersion(app: any, version: string): boolean {
return version !== '' && !app.versions.find(v => v.version === version);
}
/**
* Return true if the builder is valid
* @param builder
* @returns {boolean}
*/
isSubmittable(builder): boolean {
if (!builder) {
return false;
}
return builder.formGroup.valid;
}
/**
* Return true if the platform is on error
*
* @param {Array<any>} platforms
* @param {string} platform
* @returns {boolean}
*/
isErrorPlatform(platforms: Array<any>, platform: string): boolean {
return (platform !== '' && !platforms.find(p => p.key === platform))
|| (platform === '' && platforms.length > 1);
}
/**
* Return true if the platform is invalid (invalid value)
*
* @param {Array<any>} platforms
* @param {string} platform
* @returns {boolean}
*/
isInvalidPlatform(platforms: Array<any>, platform: string): boolean {
return (platform !== '' && !platforms.find(p => p.key === platform));
}
/**
* Generate a tooltip
*
* @param streamDeployConfig
* @param control
* @returns {string}
*/
tooltip(streamDeployConfig, control: AbstractControl): string {
const arr = [];
if (control instanceof FormGroup) {
if (control.get('property')) {
if (control.get('property') && control.get('property').invalid) {
arr.push(`The field "property" is not valid.`);
}
}
if (control.get('global') && control.get('global').invalid) {
arr.push(`The field "global" is not valid.`);
}
streamDeployConfig.apps.forEach((app) => {
if (control.get(app.name).invalid) {
arr.push(`The field "${app.name}" is not valid.`);
}
});
} else {
}
return arr.join('<br />');
}
/**
* Load the deployment properties of an app or global
*
* @param {{}} builderDeploymentProperties
* @param {string} appId
* @returns {Array}
*/
getDeploymentProperties(builderDeploymentProperties: {global: any[], apps: any}, appId?: string): Array<{ key: string, value: any }> {
const deploymentProperties = appId ? builderDeploymentProperties.apps[appId] : builderDeploymentProperties.global;
if (!deploymentProperties) {
return [];
}
return deploymentProperties.map((property: Properties.Property) => {
if (property.value !== null && property.value !== undefined && property.value.toString() !== ''
&& property.value !== property.defaultValue) {
return {
key: `${property.id}`,
value: property.value
};
}
return null;
}).filter((app) => app !== null);
}
/**
* Open Deployment Properties modal for an app or global
*
* @param builder
* @param {string} appId
* @param app
*/
openDeploymentProperties(builder, appId?: string) {
const modal = this.bsModalService.show(PropertiesGroupsDialogComponent);
const options = appId ? builder.builderDeploymentProperties.apps[appId] : builder.builderDeploymentProperties.global;
modal.content.title = `Deployment properties for platform`;
// jee.foo.bar-xxx -> jee.foo
const deduceKey = (key) => {
return key.substring(0, key.lastIndexOf('.'));
};
// grouping all properties by a deduced key
const groupBy = (items, key) => items.reduce(
(result, item) => {
const groupKey = deduceKey(item[key]);
return ({
...result,
[groupKey]: [...(result[groupKey] || []), item],
});
}, {}
);
// setup groups and sort alphabetically by group titles
let groupedPropertiesSources: Array<GroupPropertiesSource> = [];
const groupedEntries: { [s: string]: Array<any>; } = groupBy(options, 'id');
Object.entries(groupedEntries).forEach(v => {
const groupedPropertiesSource = new GroupPropertiesSource(Object.assign([], v[1]
.map((property) => Object.assign({}, property))), v[0]);
groupedPropertiesSources.push(groupedPropertiesSource);
});
groupedPropertiesSources = groupedPropertiesSources.sort(((a, b) => {
return a.title === b.title ? 0 : a.title < b.title ? -1 : 1;
}));
const groupPropertiesSources = new GroupPropertiesSources(groupedPropertiesSources);
// get new props from modal
groupPropertiesSources.confirm.subscribe((properties: Array<any>) => {
if (appId) {
builder.builderDeploymentProperties.apps[appId] = properties;
} else {
builder.builderDeploymentProperties.global = properties;
}
this.changeDetector.markForCheck();
});
modal.content.setData(groupPropertiesSources);
}
/**
* Load the properties of an app
*
* @param {{}} builderAppsProperties
* @param {string} appId
* @returns {Array}
*/
getAppProperties(builderAppsProperties: {}, appId: string): Array<{ key: string, value: any }> {
const appProperties = builderAppsProperties[appId];
if (!appProperties) {
return [];
}
return appProperties.map((property: Properties.Property) => {
if (property.value !== null && property.value !== undefined && property.value.toString() !== ''
&& property.value !== property.defaultValue) {
if (property.id.startsWith(`${appId}.`)) {
return {
key: `${property.name}`,
value: property.value
};
} else {
return {
key: `${property.id}`,
value: property.value
};
}
}
return null;
}).filter((app) => app !== null);
}
/**
* Open Application Properties modal
*
* @param builder
* @param app
*/
openApp(builder, app: any) {
const version = builder.formGroup.get('appsVersion').get(app.name).value || app.version;
const options = builder.builderAppsProperties[app.name] ? builder.builderAppsProperties[app.name] : app.options;
const modal = this.bsModalService.show(StreamDeployAppPropertiesComponent);
modal.content.title = `Properties for ${app.name}`;
if (version) {
modal.content.title += ` (${version})`;
}
const appPropertiesSource = new AppPropertiesSource(Object.assign([], options
.map((property) => Object.assign({}, property))));
appPropertiesSource.confirm.subscribe((properties: Array<any>) => {
builder.builderAppsProperties[app.name] = properties;
this.changeDetector.markForCheck();
});
modal.content.setData(appPropertiesSource);
}
/**
* Remove an error property
*/
removeError(value: { type: string, index: number }) {
const errors = this.refBuilder.errors[value.type];
if (errors[value.index]) {
errors.splice(value.index, 1);
}
this.properties = this.getProperties();
}
/**
* Emit a request deploy
*/
deployStream() {
if (!this.isSubmittable(this.refBuilder)) {
this.notificationService.error('Some field(s) are invalid.');
} else {
this.deploy.emit(this.getProperties());
}
}
/**
* Emit a request export
*/
exportProps() {
this.exportProperties.emit(this.getProperties());
}
/**
* Copye to clipboard
*/
copyToClipboard() {
this.copyProperties.emit(this.getProperties());
}
}
<div *ngIf="builder$ | async as builder; else loading">
<form class="form-horizontal" novalidate (ngSubmit)="deployStream()">
<div>
<div class="tab-pane">
<app-stream-deploy-builder-errors [errors]="builder.errors" (removeError)="removeError($event)">
</app-stream-deploy-builder-errors>
<div class="builder" [formGroup]="builder.formGroup">
<div class="col-fixed col-key">
<!-- 0 -->
<div class="line-head">
</div>
<!-- 1 -->
<div class="line-toggle">
<a class="toggle" (click)="state.platform = !state.platform">
<span class="fa" [class.fa-chevron-down]="state.platform"
[class.fa-chevron-right]="!state.platform"></span>
Platform
</a>
</div>
<!-- 2 -->
<div class="line-body" *ngIf="state.platform">
<div class="status" *ngIf="builder.formGroup.get('platform').invalid">
<div tooltipPlacement="right"
[tooltip]="tooltipTemplate"
class="fa fa-warning"></div>
<ng-template #tooltipTemplate>
<div [innerHtml]="'The define platform is not valid (unknown)'"></div>
</ng-template>
</div>
<div class="form-control form-control-label">
Platform
</div>
</div>
<!-- 3 -->
<div class="line-toggle">
<a class="toggle" (click)="state.deployer = !state.deployer">
<span class="fa" [class.fa-chevron-down]="state.deployer"
[class.fa-chevron-right]="!state.deployer"></span>
Generic Deployer Properties
</a>
</div>
<!-- 4 -->
<div *ngIf="state.deployer">
<div class="line-body" *ngFor="let deployer of builder.streamDeployConfig.deployers;let i = index">
<div class="status" *ngIf="builder.formGroup.get('deployers').controls[i].invalid">
<div tooltipPlacement="right"
[tooltip]="tooltipTemplate"
class="fa fa-warning"></div>
<ng-template #tooltipTemplate>
<div
[innerHtml]="tooltip(builder.streamDeployConfig, builder.formGroup.get('deployers').controls[i])"></div>
</ng-template>
</div>
<div class="form-control form-control-label">
{{ deployer.name }}
</div>
</div>
</div>
<!-- 5 -->
<div class="line-toggle">
<div class="help">
<span class="fa fa-question-circle" tooltipPlacement="right" [tooltip]="tooltipTemplate"></span>
<ng-template #tooltipTemplate>
<div>
<div><strong>Example:</strong></div>
<div>Key: <strong>cloudfoundry.services</strong></div>
<div>Value (application <strong>jdbc</strong>): <strong>mysqlcups</strong></div>
<div>Generated property: <strong>deployer.jdbc.cloudfoundry.services=mysqlcups</strong></div>
</div>
</ng-template>
</div>
<a class="toggle" (click)="state.specificPlatform = !state.specificPlatform">
<span class="fa" [class.fa-chevron-down]="state.specificPlatform"
[class.fa-chevron-right]="!state.specificPlatform"></span>
Deployment Platform
<!--
<span *ngIf="builder.formGroup.get('platform').value">
({{ builder.formGroup.get('platform').value }})
</span>
-->
</a>
</div>
<!-- 6 -->
<div *ngIf="state.specificPlatform">
<div *ngIf="builder.builderDeploymentProperties.global.length > 0"
class="line-body">
<div class="form-control form-control-label">
Properties
</div>
</div>
</div>
<div *ngIf="state.specificPlatform"
formArrayName="specificPlatform">
<div class="line-body" *ngFor="let f of builder.formGroup.get('specificPlatform').controls;let i = index"
[formGroupName]="i"
[class.has-error]="builder.formGroup.get('specificPlatform').controls[i].get('property').invalid">
<div class="status" *ngIf="builder.formGroup.get('specificPlatform').controls[i].invalid">
<div tooltipPlacement="right"
[tooltip]="tooltipTemplate"
class="fa fa-warning"></div>
<ng-template #tooltipTemplate>
<div
[innerHtml]="tooltip(builder.streamDeployConfig, builder.formGroup.get('specificPlatform').controls[i])"></div>
</ng-template>
</div>
<input tabindex="{{ 50 + i }}" placeholder="Enter a value" class="form-control"
formControlName="property" type="text"/>
</div>
</div>
<!-- 7 -->
<div class="line-toggle">
<a class="toggle" (click)="state.app = !state.app">
<span class="fa" [class.fa-chevron-down]="state.app" [class.fa-chevron-right]="!state.app"></span>
Applications Properties
</a>
</div>
<!-- 8 -->
<div *ngIf="state.app">
<div class="line-body">
<div class="status" *ngIf="builder.formGroup.get('appsVersion').invalid">
<div tooltipPlacement="right"
[tooltip]="tooltipTemplate"
class="fa fa-warning"></div>
<ng-template #tooltipTemplate>
<div
[innerHtml]="tooltip(builder.streamDeployConfig, builder.formGroup.get('appsVersion'))"></div>
</ng-template>
</div>
<div class="form-control form-control-label">
Version
</div>
</div>
<div class="line-body">
<div class="form-control form-control-label">
Properties
</div>
</div>
<div formArrayName="global">
<div class="line-body" *ngFor="let f of builder.formGroup.get('global').controls;let i = index"
[formGroupName]="i"
[class.has-error]="builder.formGroup.get('global').controls[i].get('property').invalid">
<div class="status" *ngIf="builder.formGroup.get('global').controls[i].invalid">
<div tooltipPlacement="right"
[tooltip]="tooltipTemplate"
class="fa fa-warning"></div>
<ng-template #tooltipTemplate>
<div
[innerHtml]="tooltip(builder.streamDeployConfig, builder.formGroup.get('global').controls[i])"></div>
</ng-template>
</div>
<input tabindex="{{ 102 + i }}" placeholder="Enter a value" class="form-control"
formControlName="property" type="text"/>
</div>
</div>
</div>
</div>
<div class="col-fixed global">
<!-- 0 -->
<div class="line-head">
Global
</div>
<!-- 1 -->
<div class="line-toggle"></div>
<!-- 2 -->
<div class="line-body" *ngIf="state.platform"
[class.has-error]="isErrorPlatform(builder.streamDeployConfig.platform.values, builder.formGroup.get('platform').value)">
<select tabindex="1" formControlName="platform">
<option value="">Select a value</option>
<option *ngFor="let platform of builder.streamDeployConfig.platform.values" [value]="platform.key">
{{ platform.name }}
({{ platform.type }})
</option>
<option
*ngIf="isInvalidPlatform(builder.streamDeployConfig.platform.values, builder.formGroup.get('platform').value)"
[value]="builder.formGroup.get('platform').value">
{{ builder.formGroup.get('platform').value }} (invalid)
</option>
</select>
</div>
<!-- 3 -->
<div class="line-toggle"></div>
<!-- 4 -->
<div formArrayName="deployers" *ngIf="state.deployer">
<div [formGroupName]="i" class="line-body"
*ngFor="let deployer of builder.streamDeployConfig.deployers; let i = index"
[class.has-error]="builder.formGroup.get('deployers').controls[i].get('global').invalid">
<input tabindex="{{ 2 + i }}" placeholder="Enter a number" formControlName="global" class="form-control"
[class.form-control-number]="deployer.suffix" type="text"/>
<span class="placeholder-unit">{{ deployer.suffix }}</span>
</div>
</div>
<!-- 5 -->
<div class="line-toggle"></div>
<div class="line-body"
*ngIf="state.specificPlatform && builder.builderDeploymentProperties.global.length > 0">
<div class="form-control form-control-label">
<strong>{{ getDeploymentProperties(builder.builderDeploymentProperties).length }}</strong>
<span> /</span> {{ builder.builderDeploymentProperties.global.length }} properties
<button tabindex="{{ 101 }}" type="button" (click)="openDeploymentProperties(builder)"
class="btn btn-primary">
<span class="fa fa-pencil"></span>
</button>
</div>
</div>
<!-- 6 -->
<div *ngIf="state.specificPlatform"
formArrayName="specificPlatform">
<div class="line-body" *ngFor="let f of builder.formGroup.get('specificPlatform').controls;let i = index"
[formGroupName]="i"
[class.has-error]="builder.formGroup.get('specificPlatform').controls[i].get('global').invalid">
<input tabindex="{{ 50 + i }}" placeholder="Enter a value" class="form-control" formControlName="global"
type="text"/>
</div>
</div>
<!-- 7 -->
<div class="line-toggle"></div>
<!-- 8 -->
<div *ngIf="state.app">
<div class="line-body"></div>
<div class="line-body"></div>
<div formArrayName="global">
<div class="line-body" *ngFor="let f of builder.formGroup.get('global').controls;let i = index"
[formGroupName]="i"
[class.has-error]="builder.formGroup.get('global').controls[i].get('global').invalid">
<input tabindex="{{ 102 + i }}" placeholder="Enter a value" class="form-control"
formControlName="global" type="text"/>
</div>
</div>
</div>
</div>
<div class="scroll">
<div class="col" *ngFor="let app of builder.streamDeployConfig.apps">
<!-- 0 -->
<div class="line-head">
<span *ngIf="app.origin != app.name">
<strong>{{ app.name }}</strong>
({{ app.origin }})
</span>
<strong *ngIf="app.origin == app.name">{{ app.name }}</strong>
<br/>
<app-type [application]="app"></app-type>
</div>
<!-- 1 -->
<div class="line-toggle"></div>
<!-- 2 -->
<div class="line-body" *ngIf="state.platform"></div>
<!-- 3 -->
<div class="line-toggle"></div>
<!-- 4 -->
<div formArrayName="deployers" *ngIf="state.deployer">
<div [formGroupName]="i" class="line-body"
*ngFor="let deployer of builder.streamDeployConfig.deployers; let i = index"
[class.has-error]="builder.formGroup.get('deployers').controls[i].get(app.name).invalid">
<input tabindex="{{ 2 + i }}" placeholder="Enter a number" [formControlName]="app.name"
class="form-control"
[class.form-control-number]="deployer.suffix" type="text"/>
<span class="placeholder-unit">{{ deployer.suffix }}</span>
</div>
</div>
<!-- 5 -->
<div class="line-toggle"></div>
<!-- 6 -->
<!-- track visibility from global as those are same as per apps -->
<div class="line-body" *ngIf="state.specificPlatform && builder.builderDeploymentProperties.global.length > 0">
<div class="form-control form-control-label">
<strong>{{ getDeploymentProperties(builder.builderDeploymentProperties, app.name).length }}</strong>
<span> /</span> {{ builder.builderDeploymentProperties.apps[app.name].length }} properties
<button tabindex="{{ 101 }}" type="button" (click)="openDeploymentProperties(builder, app.name)"
class="btn btn-primary">
<span class="fa fa-pencil"></span>
</button>
</div>
</div>
<div *ngIf="state.specificPlatform"
formArrayName="specificPlatform">
<div class="line-body"
*ngFor="let f of builder.formGroup.get('specificPlatform').controls;let i = index"
[formGroupName]="i"
[class.has-error]="builder.formGroup.get('specificPlatform').controls[i].get(app.name).invalid">
<input tabindex="{{ 50 + i }}" placeholder="Enter a value" class="form-control"
[formControlName]="app.name" type="text"/>
</div>
</div>
<!-- 7 -->
<div class="line-toggle"></div>
<!-- 8 -->
<div *ngIf="state.app">
<div class="line-body" [class.has-error]="app.optionsState.isInvalid">
<div formGroupName="appsVersion">
<div class="cell">
<select tabindex="{{ 100 }}" [formControlName]="app.name">
<option value="">Default version ({{ app.version }})</option>
<option *ngFor="let version of app.versions" [value]="version.version">
{{ version.version }}
</option>
<option *ngIf="app.optionsState.isInvalid"
[value]="builder.formGroup.get('appsVersion').get(app.name).value">
{{ builder.formGroup.get('appsVersion').get(app.name).value }} (invalid)
</option>
</select>
</div>
</div>
</div>
<div class="line-body" [class.has-error]="app.optionsState.isInvalid">
<div
*ngIf="app.options && !app.optionsState.isLoading && !app.optionsState.isOnError && !app.optionsState.isInvalid"
class="form-control form-control-label">
<div *ngIf="app.options.length > 0">
<strong>{{ getAppProperties(builder.builderAppsProperties, app.name).length }}</strong>
<span> /</span> {{ app.options.length }} properties
<button tabindex="{{ 101 }}" type="button" (click)="openApp(builder, app)"
class="btn btn-primary">
<span class="fa fa-pencil"></span>
</button>
</div>
<div *ngIf="app.options.length == 0">
<strong>No properties</strong>
</div>
</div>
<div *ngIf="app.optionsState.isLoading" class="form-control form-control-label">
Loading ...
</div>
<div *ngIf="app.optionsState.isOnError" class="form-control form-control-label">
Error loading
</div>
<div *ngIf="app.optionsState.isInvalid" class="form-control form-control-label">
Invalid version
</div>
</div>
<div formArrayName="global">
<div class="line-body" *ngFor="let f of builder.formGroup.get('global').controls;let i = index"
[formGroupName]="i"
[class.has-error]="builder.formGroup.get('global').controls[i].get(app.name).invalid">
<input tabindex="{{ 102 + i }}" placeholder="Enter a value" class="form-control"
[formControlName]="app.name" type="text"/>
</div>
</div>
</div>
</div>
</div>
</div>
<app-page-actions>
<a tabindex="200" id="btn-cancel" class="btn btn-default" routerLink="/streams">Cancel</a>
<button tabindex="200" id="btn-export" type="button" class="btn btn-default" (click)="exportProps()">
Export
</button>
<button tabindex="200" id="btn-copy" type="button" class="btn btn-default" (click)="copyToClipboard()">
Copy to Clipboard
</button>
<button tabindex="200" id="btn-deploy-builder" type="submit" class="btn btn-primary">
<span *ngIf="!isDeployed">Deploy stream</span>
<span *ngIf="isDeployed">Update stream</span>
</button>
</app-page-actions>
</div>
</div>
</form>
</div>
<ng-template #loading>
<app-loader></app-loader>
</ng-template>