2 * ============LICENSE_START=======================================================
4 * ================================================================================
5 * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
6 * ================================================================================
7 * Licensed under the Apache License, Version 2.0 (the "License");
8 * you may not use this file except in compliance with the License.
9 * You may obtain a copy of the License at
11 * http://www.apache.org/licenses/LICENSE-2.0
13 * Unless required by applicable law or agreed to in writing, software
14 * distributed under the License is distributed on an "AS IS" BASIS,
15 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16 * See the License for the specific language governing permissions and
17 * limitations under the License.
18 * ============LICENSE_END=========================================================
22 import * as _ from "lodash";
23 import { PROPERTY_TYPES, ValidationUtils, PROPERTY_VALUE_CONSTRAINTS, FormState, PROPERTY_DATA } from "app/utils";
24 import { DataTypesService } from "app/services";
25 import { PropertyModel, DataTypesMap, Component, GroupInstance, PolicyInstance, PropertyBEModel, ComponentMetadata } from "app/models";
26 import { ComponentInstance } from "../../../../models/componentsInstances/componentInstance";
27 import { ComponentInstanceServiceNg2 } from "app/ng2/services/component-instance-services/component-instance.service";
28 import { SdcUiCommon, SdcUiServices, SdcUiComponents } from "onap-ui-angular";
29 import { CompositionService } from "app/ng2/pages/composition/composition.service";
30 import { WorkspaceService } from "app/ng2/pages/workspace/workspace.service";
31 import { Observable } from "rxjs";
32 import { TopologyTemplateService } from "app/ng2/services/component-services/topology-template.service";
34 export interface IEditPropertyModel {
35 property:PropertyModel;
37 simpleTypes:Array<string>;
40 interface IPropertyFormViewModelScope extends ng.IScope {
42 editForm:ng.IFormController;
43 footerButtons:Array<any>;
47 validationPattern:RegExp;
48 propertyNameValidationPattern:RegExp;
49 commentValidationPattern:RegExp;
50 editPropertyModel:IEditPropertyModel;
51 modalInstanceProperty:ng.ui.bootstrap.IModalServiceInstance;
52 currentPropertyIndex:number;
53 isLastProperty:boolean;
55 nonPrimitiveTypes:Array<string>;
56 dataTypes:DataTypesMap;
57 isTypeDataType:boolean;
59 isPropertyValueOwner:boolean;
60 isVnfConfiguration:boolean;
62 modelNameFilter:string;
64 validateJson(json:string):boolean;
65 save(doNotCloseModal?:boolean):void;
66 getValidationPattern(type:string):RegExp;
67 validateIntRange(value:string):boolean;
70 onSchemaTypeChange():void;
71 onTypeChange(resetSchema:boolean):void;
73 delete(property:PropertyModel):void;
76 isSimpleType(typeName:string):boolean;
77 getDefaultValue():any;
80 export class PropertyFormViewModel {
84 'Sdc.Services.DataTypesService',
88 'PropertyNameValidationPattern',
89 'CommentValidationPattern',
96 'isPropertyValueOwner',
99 'ComponentInstanceServiceNg2',
100 'TopologyTemplateService',
101 'CompositionService',
105 private formState:FormState;
107 constructor(private $scope:IPropertyFormViewModelScope,
108 private DataTypesService:DataTypesService,
109 private $uibModalInstance:ng.ui.bootstrap.IModalServiceInstance,
110 private property:PropertyModel,
111 private ValidationPattern:RegExp,
112 private PropertyNameValidationPattern:RegExp,
113 private CommentValidationPattern:RegExp,
114 private ValidationUtils:ValidationUtils,
115 // private component:Component,
116 private $filter:ng.IFilterService,
117 private modalService:SdcUiServices.ModalService,
118 private filteredProperties:Array<PropertyModel>,
119 private $timeout:ng.ITimeoutService,
120 private isPropertyValueOwner:boolean,
121 private propertyOwnerType:string,
122 private propertyOwnerId:string,
123 private ComponentInstanceServiceNg2: ComponentInstanceServiceNg2,
124 private topologyTemplateService: TopologyTemplateService,
125 private compositionService: CompositionService,
126 private workspaceService: WorkspaceService) {
128 this.formState = angular.isDefined(property.name) ? FormState.UPDATE : FormState.CREATE;
132 private initResource = ():void => {
133 this.$scope.editPropertyModel.property = new PropertyModel(this.property);
134 this.$scope.editPropertyModel.property.type = this.property.type ? this.property.type : null;
135 this.$scope.editPropertyModel.property.value = this.$scope.editPropertyModel.property.value || this.$scope.editPropertyModel.property.defaultValue;
136 this.$scope.constraints = this.property.constraints && this.property.constraints[0] ? this.property.constraints[0]["validValues"] : null;
140 //init property add-ons labels that show up at the left side of the input.
141 private initAddOnLabels = () => {
142 if (this.$scope.editPropertyModel.property.name == 'network_role' && this.$scope.isService) {
143 //the server sends back the normalized name. Remove it (to prevent interference with validation) and set the addon label to the component name directly.
144 //Note: this cant be done in properties.ts because we dont have access to the component
145 if (this.$scope.editPropertyModel.property.value) {
146 let splitProp = this.$scope.editPropertyModel.property.value.split(new RegExp(this.workspaceService.metadata.normalizedName + '.', "gi"));
147 this.$scope.editPropertyModel.property.value = splitProp.pop();
149 this.$scope.editPropertyModel.property.addOn = this.workspaceService.metadata.name;
153 private initForNotSimpleType = ():void => {
154 let property = this.$scope.editPropertyModel.property;
155 this.$scope.isTypeDataType = this.DataTypesService.isDataTypeForPropertyType(this.$scope.editPropertyModel.property);
156 if (property.type && this.$scope.editPropertyModel.simpleTypes.indexOf(property.type) == -1) {
157 if (!(property.value || property.defaultValue)) {
158 switch (property.type) {
159 case PROPERTY_TYPES.MAP:
160 this.$scope.myValue = {'': null};
162 case PROPERTY_TYPES.LIST:
163 this.$scope.myValue = [];
166 this.$scope.myValue = {};
169 this.$scope.myValue = JSON.parse(property.value || property.defaultValue);
174 private setMaxLength = ():void => {
175 switch (this.$scope.editPropertyModel.property.type) {
176 case PROPERTY_TYPES.MAP:
177 case PROPERTY_TYPES.LIST:
178 this.$scope.maxLength = this.$scope.editPropertyModel.property.schema.property.type == PROPERTY_TYPES.JSON ?
179 PROPERTY_VALUE_CONSTRAINTS.JSON_MAX_LENGTH :
180 PROPERTY_VALUE_CONSTRAINTS.MAX_LENGTH;
182 case PROPERTY_TYPES.JSON:
183 this.$scope.maxLength = PROPERTY_VALUE_CONSTRAINTS.JSON_MAX_LENGTH;
186 this.$scope.maxLength =PROPERTY_VALUE_CONSTRAINTS.MAX_LENGTH;
191 private initScope = ():void => {
194 this.$scope.isLoading = true;
195 this.$scope.forms = {};
196 this.$scope.validationPattern = this.ValidationPattern;
197 this.$scope.propertyNameValidationPattern = this.PropertyNameValidationPattern;
198 this.$scope.commentValidationPattern = this.CommentValidationPattern;
199 this.$scope.isNew = (this.formState === FormState.CREATE);
200 this.$scope.isService = this.workspaceService.metadata.isService();
201 this.$scope.modalInstanceProperty = this.$uibModalInstance;
202 this.$scope.currentPropertyIndex = _.findIndex(this.filteredProperties, i=> i.name == this.property.name);
203 this.$scope.isLastProperty = this.$scope.currentPropertyIndex == (this.filteredProperties.length - 1);
204 this.$scope.editPropertyModel = {
205 property : new PropertyModel(this.property),
206 types : PROPERTY_DATA.TYPES,
207 simpleTypes : PROPERTY_DATA.SIMPLE_TYPES}; //All simple types
208 this.$scope.isPropertyValueOwner = this.isPropertyValueOwner;
209 this.$scope.propertyOwnerType = this.propertyOwnerType;
210 this.$scope.modelNameFilter = this.workspaceService.metadata.model;
211 //check if property of VnfConfiguration
212 this.$scope.isVnfConfiguration = false;
213 if(this.propertyOwnerType == "component" && angular.isArray(this.compositionService.componentInstances)) {
214 var componentPropertyOwner:ComponentInstance = this.compositionService.componentInstances.find((ci:ComponentInstance) => {
215 return ci.uniqueId === this.property.resourceInstanceUniqueId;
217 if (componentPropertyOwner && componentPropertyOwner.componentName === 'vnfConfiguration') {
218 this.$scope.isVnfConfiguration = true;
222 this.initForNotSimpleType();
224 this.$scope.validateJson = (json:string):boolean => {
228 return this.ValidationUtils.validateJson(json);
231 this.DataTypesService.fetchDataTypesByModel(this.workspaceService.metadata.model).then(response => {
232 this.$scope.dataTypes = response.data as DataTypesMap;
233 this.$scope.nonPrimitiveTypes = _.filter(Object.keys(this.$scope.dataTypes), (type:string)=> {
234 return this.$scope.editPropertyModel.types.indexOf(type) == -1;
237 this.$scope.isLoading = false;
241 this.$scope.save = (doNotCloseModal?:boolean):void => {
242 let property:PropertyModel = this.$scope.editPropertyModel.property;
243 this.$scope.editPropertyModel.property.description = this.ValidationUtils.stripAndSanitize(this.$scope.editPropertyModel.property.description);
244 //if read only - or no changes made - just closes the modal
245 //need to check for property.value changes manually to detect if map properties deleted
246 if ((this.$scope.editPropertyModel.property.readonly && !this.$scope.isPropertyValueOwner)
247 || (!this.$scope.forms.editForm.$dirty && angular.equals(JSON.stringify(this.$scope.myValue), this.$scope.editPropertyModel.property.value))) {
248 this.$uibModalInstance.close();
252 this.$scope.isLoading = true;
254 let onPropertyFaild = (response):void => {
255 console.info('onFaild', response);
256 this.$scope.isLoading = false;
259 let onPropertySuccess = (propertyFromBE:PropertyModel):void => {
260 this.$scope.isLoading = false;
261 this.filteredProperties[this.$scope.currentPropertyIndex] = propertyFromBE;
262 if (!doNotCloseModal) {
263 this.$uibModalInstance.close(propertyFromBE);
265 this.$scope.forms.editForm.$setPristine();
266 this.$scope.editPropertyModel.property = new PropertyModel();
270 //Not clean, but doing this as a temporary fix until we update the property right panel modals
271 if (this.propertyOwnerType === "group"){
272 this.ComponentInstanceServiceNg2.updateComponentGroupInstanceProperties(this.workspaceService.metadata.componentType, this.workspaceService.metadata.uniqueId, this.propertyOwnerId, [property])
273 .subscribe((propertiesFromBE) => { onPropertySuccess(<PropertyModel>propertiesFromBE[0])}, error => onPropertyFaild(error));
274 } else if (this.propertyOwnerType === "policy"){
275 if (!this.$scope.editPropertyModel.property.simpleType &&
276 !this.$scope.isSimpleType(this.$scope.editPropertyModel.property.type) &&
277 !_.isNil(this.$scope.myValue)) {
278 property.value = JSON.stringify(this.$scope.myValue);
280 this.ComponentInstanceServiceNg2.updateComponentPolicyInstanceProperties(this.workspaceService.metadata.componentType, this.workspaceService.metadata.uniqueId, this.propertyOwnerId, [property])
281 .subscribe((propertiesFromBE) => { onPropertySuccess(<PropertyModel>propertiesFromBE[0])}, error => onPropertyFaild(error));
283 //in case we have uniqueId we call update method
284 if (this.$scope.isPropertyValueOwner) {
285 if (!this.$scope.editPropertyModel.property.simpleType && !this.$scope.isSimpleType(property.type)) {
286 let myValueString:string = JSON.stringify(this.$scope.myValue);
287 property.value = myValueString;
289 this.updateInstanceProperties(property.resourceInstanceUniqueId, [property]).subscribe((propertiesFromBE) => onPropertySuccess(propertiesFromBE[0]),
290 error => onPropertyFaild(error));
292 if (!this.$scope.editPropertyModel.property.simpleType && !this.$scope.isSimpleType(property.type)) {
293 let myValueString:string = JSON.stringify(this.$scope.myValue);
294 property.defaultValue = myValueString;
296 this.$scope.editPropertyModel.property.defaultValue = this.$scope.editPropertyModel.property.value;
298 this.addOrUpdateProperty(property).subscribe(onPropertySuccess, error => onPropertyFaild(error));
303 this.$scope.getPrev = ():void=> {
304 this.property = this.filteredProperties[--this.$scope.currentPropertyIndex];
306 this.initForNotSimpleType();
307 this.$scope.isLastProperty = false;
310 this.$scope.getNext = ():void=> {
311 this.property = this.filteredProperties[++this.$scope.currentPropertyIndex];
313 this.initForNotSimpleType();
314 this.$scope.isLastProperty = this.$scope.currentPropertyIndex == (this.filteredProperties.length - 1);
317 this.$scope.isSimpleType = (typeName:string):boolean=> {
318 return typeName && this.$scope.editPropertyModel.simpleTypes.indexOf(typeName) != -1;
321 this.$scope.showSchema = ():boolean => {
322 return [PROPERTY_TYPES.LIST, PROPERTY_TYPES.MAP].indexOf(this.$scope.editPropertyModel.property.type) > -1;
325 this.$scope.getValidationPattern = (type:string):RegExp => {
326 return this.ValidationUtils.getValidationPattern(type);
329 this.$scope.validateIntRange = (value:string):boolean => {
330 return !value || this.ValidationUtils.validateIntRange(value);
333 this.$scope.close = ():void => {
334 this.$uibModalInstance.close();
337 // put default value when instance value is empty
338 this.$scope.onValueChange = ():void => {
339 if (!this.$scope.editPropertyModel.property.value) {
340 if (this.$scope.isPropertyValueOwner) {
341 this.$scope.editPropertyModel.property.value = this.$scope.editPropertyModel.property.defaultValue;
346 // Add the done button at the footer.
347 this.$scope.footerButtons = [
348 {'name': 'Save', 'css': 'blue', 'callback': this.$scope.save},
349 {'name': 'Cancel', 'css': 'grey', 'callback': this.$scope.close}
352 this.$scope.$watch("forms.editForm.$invalid", (newVal, oldVal) => {
353 this.$scope.footerButtons[0].disabled = this.$scope.forms.editForm.$invalid;
356 this.$scope.getDefaultValue = ():any => {
357 return this.$scope.isPropertyValueOwner ? this.$scope.editPropertyModel.property.defaultValue : null;
360 this.$scope.onTypeChange = ():void => {
361 this.$scope.editPropertyModel.property.value = '';
362 this.$scope.editPropertyModel.property.defaultValue = '';
364 this.initForNotSimpleType();
367 this.$scope.onSchemaTypeChange = ():void => {
368 if (this.$scope.editPropertyModel.property.type == PROPERTY_TYPES.MAP) {
369 this.$scope.myValue = {'': null};
370 } else if (this.$scope.editPropertyModel.property.type == PROPERTY_TYPES.LIST) {
371 this.$scope.myValue = [];
376 this.$scope.delete = (property:PropertyModel):void => {
377 let onOk: Function = ():void => {
378 this.deleteProperty(property.uniqueId).subscribe(
382 let title:string = this.$filter('translate')("PROPERTY_VIEW_DELETE_MODAL_TITLE");
383 let message:string = this.$filter('translate')("PROPERTY_VIEW_DELETE_MODAL_TEXT", "{'name': '" + property.name + "'}");
384 const okButton = {testId: "OK", text: "OK", type: SdcUiCommon.ButtonType.info, callback: onOk, closeModal: true} as SdcUiComponents.ModalButtonComponent;
385 this.modalService.openInfoModal(title, message, 'delete-modal', [okButton]);
389 private updateInstanceProperties = (componentInstanceId:string, properties:PropertyModel[]):Observable<PropertyModel[]> => {
391 return this.ComponentInstanceServiceNg2.updateInstanceProperties(this.workspaceService.metadata.componentType, this.workspaceService.metadata.uniqueId, componentInstanceId, properties)
392 .map(newProperties => {
393 newProperties.forEach((newProperty) => {
394 if (!_.isNil(newProperty.path)) {
395 if (newProperty.path[0] === newProperty.resourceInstanceUniqueId) newProperty.path.shift();
396 // find exist instance property in parent component for update the new value ( find bu uniqueId & path)
397 let existProperty: PropertyModel = <PropertyModel>_.find(this.compositionService.componentInstancesProperties[newProperty.resourceInstanceUniqueId], {
398 uniqueId: newProperty.uniqueId,
399 path: newProperty.path
401 let index = this.compositionService.componentInstancesProperties[newProperty.resourceInstanceUniqueId].indexOf(existProperty);
402 this.compositionService.componentInstancesProperties[newProperty.resourceInstanceUniqueId][index] = newProperty;
405 return newProperties;
409 private addOrUpdateProperty = (property: PropertyModel): Observable<PropertyModel> => {
410 if (!property.uniqueId) {
411 let onSuccess = (newProperty: PropertyModel): PropertyModel => {
412 this.filteredProperties.push(newProperty);
415 return this.topologyTemplateService.addProperty(this.workspaceService.metadata.componentType, this.workspaceService.metadata.uniqueId, property)
418 let onSuccess = (newProperty: PropertyModel): PropertyModel => {
419 // find exist instance property in parent component for update the new value ( find bu uniqueId )
420 let existProperty: PropertyModel = <PropertyModel>_.find(this.filteredProperties, {uniqueId: newProperty.uniqueId});
421 let propertyIndex = this.filteredProperties.indexOf(existProperty);
422 this.filteredProperties[propertyIndex] = newProperty;
425 return this.topologyTemplateService.updateProperty(this.workspaceService.metadata.componentType, this.workspaceService.metadata.uniqueId, property).map(onSuccess);
429 public deleteProperty = (propertyId:string):Observable<void> => {
430 let onSuccess = ():void => {
431 console.log("Property deleted");
432 delete _.remove(this.filteredProperties, {uniqueId: propertyId})[0];
434 let onFailed = ():void => {
435 console.log("Failed to delete property");
437 return this.topologyTemplateService.deleteProperty(this.workspaceService.metadata.componentType, this.workspaceService.metadata.uniqueId, propertyId).map(onSuccess, onFailed);