aboutsummaryrefslogtreecommitdiffstats
path: root/catalog-ui/src/app/models/properties-inputs
diff options
context:
space:
mode:
Diffstat (limited to 'catalog-ui/src/app/models/properties-inputs')
-rw-r--r--catalog-ui/src/app/models/properties-inputs/derived-fe-property.ts40
-rw-r--r--catalog-ui/src/app/models/properties-inputs/input-be-model.ts3
-rw-r--r--catalog-ui/src/app/models/properties-inputs/input-fe-model.ts39
-rw-r--r--catalog-ui/src/app/models/properties-inputs/property-fe-model.ts205
4 files changed, 269 insertions, 18 deletions
diff --git a/catalog-ui/src/app/models/properties-inputs/derived-fe-property.ts b/catalog-ui/src/app/models/properties-inputs/derived-fe-property.ts
index 8ed5c734c0..33e83ce00a 100644
--- a/catalog-ui/src/app/models/properties-inputs/derived-fe-property.ts
+++ b/catalog-ui/src/app/models/properties-inputs/derived-fe-property.ts
@@ -18,14 +18,18 @@
* ============LICENSE_END=========================================================
*/
+import * as _ from "lodash";
import { SchemaPropertyGroupModel, SchemaProperty } from '../aschema-property';
-import { DerivedPropertyType, PropertyBEModel } from '../../models';
+import { DerivedPropertyType, PropertyBEModel, PropertyFEModel } from '../../models';
import { PROPERTY_TYPES } from 'app/utils';
import { UUID } from "angular2-uuid";
export class DerivedFEProperty extends PropertyBEModel {
- valueObj: any;
+ valueObj: any;
+ valueObjIsValid: boolean;
+ valueObjOrig: any;
+ valueObjIsChanged: boolean;
parentName: string;
propertiesName: string; //"network_assignments#ipv4_subnet#use_ipv4 = parentPath + name
derivedDataType: DerivedPropertyType;
@@ -36,6 +40,7 @@ export class DerivedFEProperty extends PropertyBEModel {
isChildOfListOrMap: boolean;
canBeDeclared: boolean;
mapKey: string;
+ mapKeyError: string;
constructor(property: PropertyBEModel, parentName?: string, createChildOfListOrMap?: boolean, key?:string, value?:any) {
if (!createChildOfListOrMap) { //creating a standard derived prop
@@ -54,17 +59,44 @@ export class DerivedFEProperty extends PropertyBEModel {
if (property.type == PROPERTY_TYPES.LIST) {
this.mapKey = property.schema.property.type.split('.').pop();
+ this.mapKeyError = null;
this.type = property.schema.property.type;
} else { //map
- this.mapKey = key || "";
+ if (key) {
+ this.mapKey = key;
+ this.mapKeyError = null;
+ } else {
+ this.mapKey = '';
+ this.mapKeyError = 'Key cannot be empty.';
+ }
this.type = property.type;
}
this.valueObj = (this.type == PROPERTY_TYPES.JSON && typeof value == 'object') ? JSON.stringify(value) : value;
this.schema = new SchemaPropertyGroupModel(new SchemaProperty(property.schema.property));
+ this.updateValueObjOrig();
}
+ this.valueObjIsValid = true;
this.derivedDataType = this.getDerivedPropertyType();
}
-
+
+ public getActualMapKey() {
+ return (this.mapKeyError) ? this.name : this.mapKey;
+ }
+
+ public updateValueObj(valueObj:any, isValid:boolean) {
+ this.valueObj = PropertyFEModel.cleanValueObj(valueObj);
+ this.valueObjIsValid = isValid;
+ this.valueObjIsChanged = this.hasValueObjChanged();
+ }
+
+ public updateValueObjOrig() {
+ this.valueObjOrig = _.cloneDeep(this.valueObj);
+ this.valueObjIsChanged = false;
+ }
+
+ public hasValueObjChanged() {
+ return !_.isEqual(this.valueObj, this.valueObjOrig);
+ }
}
export class DerivedFEPropertyMap {
[parentPath: string]: Array<DerivedFEProperty>;
diff --git a/catalog-ui/src/app/models/properties-inputs/input-be-model.ts b/catalog-ui/src/app/models/properties-inputs/input-be-model.ts
index f6548a352c..942d775c6e 100644
--- a/catalog-ui/src/app/models/properties-inputs/input-be-model.ts
+++ b/catalog-ui/src/app/models/properties-inputs/input-be-model.ts
@@ -41,9 +41,6 @@ export class InputBEModel extends PropertyBEModel {
this.inputPath = input.inputPath;
}
- public toJSON = (): any => {
- };
-
}
export interface ComponentInstanceModel extends InputBEModel {
diff --git a/catalog-ui/src/app/models/properties-inputs/input-fe-model.ts b/catalog-ui/src/app/models/properties-inputs/input-fe-model.ts
index 630374aac3..c349f41ea2 100644
--- a/catalog-ui/src/app/models/properties-inputs/input-fe-model.ts
+++ b/catalog-ui/src/app/models/properties-inputs/input-fe-model.ts
@@ -18,15 +18,22 @@
* ============LICENSE_END=========================================================
*/
+import * as _ from "lodash";
import { SchemaPropertyGroupModel, SchemaProperty } from "../aschema-property";
-import { PropertyBEModel } from "../../models";
+import {PropertyFEModel} from "../../models";
import {PROPERTY_DATA} from "../../utils/constants";
import {InputBEModel} from "./input-be-model";
+import {DerivedPropertyType} from "./property-be-model";
export class InputFEModel extends InputBEModel {
isSimpleType: boolean;
relatedPropertyValue: any;
relatedPropertyName: string;
+ defaultValueObj:any;
+ defaultValueObjIsValid:boolean;
+ defaultValueObjOrig:any;
+ defaultValueObjIsChanged:boolean;
+ derivedDataType: DerivedPropertyType;
constructor(input?: InputBEModel) {
super(input);
@@ -37,7 +44,37 @@ export class InputFEModel extends InputBEModel {
this.relatedPropertyValue = relatedProperty.value;
this.relatedPropertyName = relatedProperty.name;
}
+ this.derivedDataType = this.getDerivedPropertyType();
+ this.resetDefaultValueObjValidation();
+ this.updateDefaultValueObjOrig();
}
}
+ public updateDefaultValueObj(defaultValueObj:any, isValid:boolean) {
+ this.defaultValueObj = PropertyFEModel.cleanValueObj(defaultValueObj);
+ this.defaultValueObjIsValid = isValid;
+ this.defaultValueObjIsChanged = this.hasDefaultValueChanged();
+ }
+
+ public updateDefaultValueObjOrig() {
+ this.defaultValueObjOrig = _.cloneDeep(this.defaultValueObj);
+ this.defaultValueObjIsChanged = false;
+ }
+
+ public getJSONDefaultValue(): string {
+ return PropertyFEModel.stringifyValueObj(this.defaultValueObj, this.schema.property.type, this.derivedDataType);
+ }
+
+ public getDefaultValueObj(): any {
+ return PropertyFEModel.parseValueObj(this.defaultValue, this.type, this.derivedDataType);
+ }
+
+ public resetDefaultValueObjValidation() {
+ this.defaultValueObjIsValid = true;
+ }
+
+ hasDefaultValueChanged(): boolean {
+ return !_.isEqual(this.defaultValueObj, this.defaultValueObjOrig);
+ }
+
} \ No newline at end of file
diff --git a/catalog-ui/src/app/models/properties-inputs/property-fe-model.ts b/catalog-ui/src/app/models/properties-inputs/property-fe-model.ts
index 6faa6ada84..a0c087bdc2 100644
--- a/catalog-ui/src/app/models/properties-inputs/property-fe-model.ts
+++ b/catalog-ui/src/app/models/properties-inputs/property-fe-model.ts
@@ -18,6 +18,7 @@
* ============LICENSE_END=========================================================
*/
+import * as _ from "lodash";
import {SchemaPropertyGroupModel, SchemaProperty} from '../aschema-property';
import { PROPERTY_DATA, PROPERTY_TYPES } from 'app/utils';
import { FilterPropertiesAssignmentData, PropertyBEModel, DerivedPropertyType, DerivedFEPropertyMap, DerivedFEProperty } from 'app/models';
@@ -34,6 +35,10 @@ export class PropertyFEModel extends PropertyBEModel {
propertiesName: string;
uniqueId: string;
valueObj: any; //this is the only value we relate to in the html templates
+ valueObjValidation: any;
+ valueObjIsValid: boolean;
+ valueObjOrig: any; //this is valueObj representation as saved in server
+ valueObjIsChanged: boolean;
derivedDataType: DerivedPropertyType;
constructor(property: PropertyBEModel){
@@ -44,19 +49,50 @@ export class PropertyFEModel extends PropertyBEModel {
this.derivedDataType = this.getDerivedPropertyType();
this.flattenedChildren = [];
this.propertiesName = this.name;
+ this.valueObj = null;
+ this.updateValueObjOrig();
+ this.resetValueObjValidation();
}
- public getJSONValue = (): string => {
- //If type is JSON, need to try parsing it before we stringify it so that it appears property in TOSCA - change per Bracha due to AMDOCS
- //TODO: handle this.derivedDataType == DerivedPropertyType.MAP
- if (this.derivedDataType == DerivedPropertyType.LIST && this.schema.property.type == PROPERTY_TYPES.JSON) {
- try {
- return JSON.stringify(this.valueObj.map(item => (typeof item == 'string')? JSON.parse(item) : item));
- } catch (e){}
+ public updateValueObj(valueObj:any, isValid:boolean) {
+ this.valueObj = PropertyFEModel.cleanValueObj(valueObj);
+ this.valueObjValidation = this.valueObjIsValid = isValid;
+ this.valueObjIsChanged = this.hasValueObjChanged();
+ }
+
+ public updateValueObjOrig() {
+ this.valueObjOrig = _.cloneDeep(this.valueObj);
+ this.valueObjIsChanged = false;
+ }
+
+ public calculateValueObjIsValid(valueObjValidation?: any) {
+ valueObjValidation = (valueObjValidation !== undefined) ? valueObjValidation : this.valueObjValidation;
+ if (valueObjValidation instanceof Array) {
+ return valueObjValidation.every((v) => this.calculateValueObjIsValid(v));
+ } else if (valueObjValidation instanceof Object) {
+ return Object.keys(valueObjValidation).every((k) => this.calculateValueObjIsValid(valueObjValidation[k]));
}
+ return Boolean(valueObjValidation);
+ }
- return (this.derivedDataType == DerivedPropertyType.SIMPLE) ? this.valueObj : JSON.stringify(this.valueObj);
+ public resetValueObjValidation() {
+ if (this.derivedDataType === DerivedPropertyType.SIMPLE) {
+ this.valueObjValidation = null;
+ } else if (this.derivedDataType === DerivedPropertyType.LIST) {
+ this.valueObjValidation = [];
+ } else {
+ this.valueObjValidation = {};
+ }
+ this.valueObjIsValid = true;
+ }
+
+ public getJSONValue = (): string => {
+ return PropertyFEModel.stringifyValueObj(this.valueObj, this.schema.property.type, this.derivedDataType);
+ }
+
+ public getValueObj = (): any => {
+ return PropertyFEModel.parseValueObj(this.value, this.type, this.derivedDataType, this.defaultValue);
}
public setNonDeclared = (childPath?: string): void => {
@@ -106,12 +142,94 @@ export class PropertyFEModel extends PropertyBEModel {
public childPropUpdated = (childProp: DerivedFEProperty): void => {
let parentNames = this.getParentNamesArray(childProp.propertiesName, []);
if (parentNames.length) {
- _.set(this.valueObj, parentNames.join('.'), childProp.valueObj);
+ const childPropName = parentNames.join('.');
+ // unset value only if is null and valid, and not in a list
+ if (childProp.valueObj === null && childProp.valueObjIsValid) {
+ const parentChildProp = this.flattenedChildren.find((ch) => ch.propertiesName === childProp.parentName) || this;
+ if (parentChildProp.derivedDataType !== DerivedPropertyType.LIST) {
+ _.unset(this.valueObj, childPropName);
+ this.valueObj = PropertyFEModel.cleanValueObj(this.valueObj);
+ } else {
+ _.set(this.valueObj, childPropName, null);
+ }
+ } else {
+ _.set(this.valueObj, childPropName, childProp.valueObj);
+ }
+ if (childProp.valueObjIsChanged) {
+ _.set(this.valueObjValidation, childPropName, childProp.valueObjIsValid);
+ this.valueObjIsValid = childProp.valueObjIsValid && this.calculateValueObjIsValid();
+ this.valueObjIsChanged = true;
+ } else {
+ _.unset(this.valueObjValidation, childPropName);
+ this.valueObjIsValid = this.calculateValueObjIsValid();
+ this.valueObjIsChanged = this.hasValueObjChanged();
+ }
+ }
+ };
+
+ childPropMapKeyUpdated = (childProp: DerivedFEProperty, newMapKey: string, forceValidate: boolean = false) => {
+ if (!childProp.isChildOfListOrMap || childProp.derivedDataType !== DerivedPropertyType.MAP) {
+ return;
+ }
+
+ const childParentNames = this.getParentNamesArray(childProp.parentName);
+ const oldActualMapKey = childProp.getActualMapKey();
+
+ childProp.mapKey = newMapKey;
+ if (childProp.mapKey === null) { // null -> remove map key
+ childProp.mapKeyError = null;
+ } else if (!childProp.mapKey) {
+ childProp.mapKeyError = 'Key cannot be empty.';
+ } else if (this.flattenedChildren
+ .filter((fch) => fch !== childProp && fch.parentName === childProp.parentName) // filter sibling child props
+ .map((fch) => fch.mapKey)
+ .indexOf(childProp.mapKey) !== -1) {
+ childProp.mapKeyError = 'This key already exists.';
+ } else {
+ childProp.mapKeyError = null;
+ }
+ const newActualMapKey = childProp.getActualMapKey();
+ const newMapKeyIsValid = !childProp.mapKeyError;
+
+ // if mapKey was changed, then replace the old key with the new one
+ if (newActualMapKey !== oldActualMapKey) {
+ const oldChildPropNames = childParentNames.concat([oldActualMapKey]);
+ const newChildPropNames = (newActualMapKey) ? childParentNames.concat([newActualMapKey]) : null;
+
+ // add map key to valueObj and valueObjValidation
+ if (newChildPropNames) {
+ const newChildVal = _.get(this.valueObj, oldChildPropNames);
+ if (newChildVal !== undefined) {
+ _.set(this.valueObj, newChildPropNames, newChildVal);
+ _.set(this.valueObjValidation, newChildPropNames, _.get(this.valueObjValidation, oldChildPropNames, childProp.valueObjIsValid));
+ }
+ }
+
+ // remove map key from valueObj and valueObjValidation
+ _.unset(this.valueObj, oldChildPropNames);
+ _.unset(this.valueObjValidation, oldChildPropNames);
+
+ // force validate after map key change
+ forceValidate = true;
+ }
+
+ if (forceValidate) {
+ // add custom entry for map key validation:
+ const childMapKeyNames = childParentNames.concat(`%%KEY:${childProp.name}%%`);
+ if (newActualMapKey) {
+ _.set(this.valueObjValidation, childMapKeyNames, newMapKeyIsValid);
+ } else {
+ _.unset(this.valueObjValidation, childMapKeyNames);
+ }
+
+ this.valueObjIsValid = newMapKeyIsValid && this.calculateValueObjIsValid();
+ this.valueObjIsChanged = this.hasValueObjChanged();
}
};
/* Returns array of individual parents for given prop path, with list/map UUIDs replaced with index/mapkey */
public getParentNamesArray = (parentPropName: string, parentNames?: Array<string>): Array<string> => {
+ parentNames = parentNames || [];
if (parentPropName.indexOf("#") == -1) { return parentNames; } //finished recursing parents. return
let parentProp: DerivedFEProperty = this.flattenedChildren.find(prop => prop.propertiesName === parentPropName);
@@ -119,7 +237,7 @@ export class PropertyFEModel extends PropertyBEModel {
if (parentProp.isChildOfListOrMap) {
if (parentProp.derivedDataType == DerivedPropertyType.MAP) {
- nameToInsert = parentProp.mapKey;
+ nameToInsert = parentProp.getActualMapKey();
} else { //LIST
let siblingProps = this.flattenedChildren.filter(prop => prop.parentName == parentProp.parentName).map(prop => prop.propertiesName);
nameToInsert = siblingProps.indexOf(parentProp.propertiesName).toString();
@@ -130,5 +248,72 @@ export class PropertyFEModel extends PropertyBEModel {
return this.getParentNamesArray(parentProp.parentName, parentNames); //continue recursing
}
+ public hasValueObjChanged() {
+ return !_.isEqual(this.valueObj, this.valueObjOrig);
+ }
+ static stringifyValueObj(valueObj: any, propertyType: PROPERTY_TYPES, propertyDerivedType: DerivedPropertyType): string {
+ // if valueObj is null, return null
+ if (valueObj === null || valueObj === undefined) {
+ return null;
+ }
+
+ //If type is JSON, need to try parsing it before we stringify it so that it appears property in TOSCA - change per Bracha due to AMDOCS
+ //TODO: handle this.derivedDataType == DerivedPropertyType.MAP
+ if (propertyDerivedType == DerivedPropertyType.LIST && propertyType == PROPERTY_TYPES.JSON) {
+ try {
+ return JSON.stringify(valueObj.map(item => (typeof item == 'string') ? JSON.parse(item) : item));
+ } catch (e){}
+ }
+
+ // if type is anything but string, then stringify valueObj
+ if ((typeof valueObj) !== 'string') {
+ return JSON.stringify(valueObj);
+ }
+
+ // return string value as is
+ return valueObj;
+ }
+
+ static parseValueObj(value: string, propertyType: PROPERTY_TYPES, propertyDerivedType: DerivedPropertyType, defaultValue?: string): any {
+ let valueObj;
+ if (propertyDerivedType === DerivedPropertyType.SIMPLE) {
+ valueObj = value || defaultValue || null; // use null for empty value object
+ if (valueObj &&
+ propertyType !== PROPERTY_TYPES.STRING &&
+ propertyType !== PROPERTY_TYPES.JSON &&
+ PROPERTY_DATA.SCALAR_TYPES.indexOf(<string>propertyType) == -1) {
+ valueObj = JSON.parse(value); // the value object contains the real value ans not the value as string
+ }
+ } else if (propertyDerivedType == DerivedPropertyType.LIST) {
+ valueObj = _.merge([], JSON.parse(defaultValue || '[]'), JSON.parse(value || '[]')); // value object should be merged value and default value. Value takes higher precedence. Set value object to empty obj if undefined.
+ } else {
+ valueObj = _.merge({}, JSON.parse(defaultValue || '{}'), JSON.parse(value || '{}')); // value object should be merged value and default value. Value takes higher precedence. Set value object to empty obj if undefined.
+ }
+ return valueObj;
+ };
+
+ static cleanValueObj(valueObj: any, unsetEmpty?: boolean): any {
+ // By default - unsetEmpty undefined - will make valueObj cleaned (no null or empty objects, but array will keep null or empty objects).
+ if (valueObj === undefined || valueObj === null || valueObj === '') {
+ return null;
+ }
+ if (valueObj instanceof Array) {
+ const cleanArr = valueObj.map((v) => PropertyFEModel.cleanValueObj(v)).filter((v) => v !== null);
+ valueObj.splice(0, valueObj.length, ...cleanArr)
+ } else if (valueObj instanceof Object) {
+ Object.keys(valueObj).forEach((k) => {
+ // clean each item in the valueObj (by default, unset empty objects)
+ valueObj[k] = PropertyFEModel.cleanValueObj(valueObj[k], unsetEmpty !== undefined ? unsetEmpty : true);
+ if (valueObj[k] === null) {
+ delete valueObj[k];
+ }
+ });
+ // if unsetEmpty flag is true and valueObj is empty
+ if (unsetEmpty && !Object.keys(valueObj).length) {
+ return null;
+ }
+ }
+ return valueObj;
+ }
}