Enable UI component to display property constraints
[sdc.git] / catalog-ui / src / app / ng2 / pages / properties-assignment / constraints / constraints.component.ts
1 /*
2  * ============LICENSE_START=======================================================
3  *  Copyright (C) 2022 Nordix Foundation
4  *  ================================================================================
5  *  Licensed under the Apache License, Version 2.0 (the "License");
6  *  you may not use this file except in compliance with the License.
7  *  You may obtain a copy of the License at
8  *
9  *        http://www.apache.org/licenses/LICENSE-2.0
10  *  Unless required by applicable law or agreed to in writing, software
11  *  distributed under the License is distributed on an "AS IS" BASIS,
12  *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  *  See the License for the specific language governing permissions and
14  *  limitations under the License.
15  *
16  *  SPDX-License-Identifier: Apache-2.0
17  *  ============LICENSE_END=========================================================
18  */
19
20 import { Component, EventEmitter, Input, OnInit, Output } from '@angular/core';
21 import { PROPERTY_DATA, PROPERTY_TYPES } from "app/utils/constants"
22
23 @Component({
24   selector: 'app-constraints',
25   templateUrl: './constraints.component.html',
26   styleUrls: ['./constraints.component.less']
27 })
28 export class ConstraintsComponent implements OnInit {
29
30   @Input() propertyConstraints: any[];
31   @Input() propertyType: string;
32   @Input() isViewOnly: boolean = false;
33   @Output() onConstraintChange: EventEmitter<any> = new EventEmitter<any>();
34
35   constraints: Constraint[] = new Array();
36   constraintTypes: string[];
37   ConstraintTypesMapping = ConstraintTypesMapping;
38   valid: boolean = true;
39
40   ngOnInit() {
41     this.constraintTypes = Object.keys(ConstraintTypes).map(key => ConstraintTypes[key]);
42   }
43
44   ngOnChanges(changes): void {
45     if (changes.propertyType) {
46       if (!this.propertyType || changes.propertyType.currentValue == this.propertyType) {
47         this.propertyType = changes.propertyType.currentValue;
48       } else {
49         this.constraints = new Array();
50         this.propertyType = changes.propertyType;
51         this.emitOnConstraintChange();
52       }
53     }
54     this.constraints = new Array();
55     if (changes.propertyConstraints && changes.propertyConstraints.currentValue) {
56       changes.propertyConstraints.currentValue.forEach((constraint: any) => {
57         this.constraints.push(this.getConstraintFromPropertyBEModel(constraint));
58       });
59     }
60   }
61
62   private getConstraintFromPropertyBEModel(constraint: any):Constraint {
63     let constraintType: ConstraintTypes;
64     let constraintValue: any;
65     if (!constraint) {
66       constraintType = ConstraintTypes.null;
67       constraintValue = "";
68     } else if(constraint.hasOwnProperty(ConstraintTypes.valid_values)){
69       constraintType = ConstraintTypes.valid_values;
70       constraintValue = constraint.validValues;
71     } else if(constraint.hasOwnProperty(ConstraintTypes.equal)) {
72       constraintType = ConstraintTypes.equal;
73       constraintValue = constraint.equal;
74     } else if(constraint.hasOwnProperty(ConstraintTypes.greater_than)) {
75       constraintType = ConstraintTypes.greater_than;
76       constraintValue = constraint.greaterThan;
77     } else if(constraint.hasOwnProperty(ConstraintTypes.greater_or_equal)) {
78       constraintType = ConstraintTypes.greater_or_equal;
79       constraintValue = constraint.greaterOrEqual;
80     } else if(constraint.hasOwnProperty(ConstraintTypes.less_than)) {
81       constraintType = ConstraintTypes.less_than;
82       constraintValue = constraint.lessThan;
83     } else if(constraint.hasOwnProperty(ConstraintTypes.less_or_equal)) {
84       constraintType = ConstraintTypes.less_or_equal;
85       constraintValue = constraint.lessOrEqual;
86     } else if(constraint.hasOwnProperty(ConstraintTypes.in_range)) {
87       constraintType = ConstraintTypes.in_range;
88       constraintValue = new Array(constraint.inRange[0], constraint.inRange[1]);
89     } else if(constraint.rangeMaxValue || constraint.rangeMinValue) {
90       constraintType = ConstraintTypes.in_range;
91       constraintValue = new Array(constraint.rangeMinValue, constraint.rangeMaxValue);
92     } else if(constraint.hasOwnProperty(ConstraintTypes.length)) {
93       constraintType = ConstraintTypes.length;
94       constraintValue = constraint.length;
95     } else if(constraint.hasOwnProperty(ConstraintTypes.min_length)) {
96       constraintType = ConstraintTypes.min_length;
97       constraintValue = constraint.minLength;
98     } else if(constraint.hasOwnProperty(ConstraintTypes.max_length)) {
99       constraintType = ConstraintTypes.max_length;
100       constraintValue = constraint.maxLength;
101     } else if(constraint.hasOwnProperty(ConstraintTypes.pattern)) {
102       constraintType = ConstraintTypes.pattern;
103       constraintValue = constraint.pattern;
104     }
105     return {
106       type:constraintType,
107       value:constraintValue
108     }
109   }
110
111   private getConstraintsFormat(): any[] {
112     let constraintArray = new Array();
113     this.constraints.forEach((constraint: Constraint) => {
114       constraintArray.push(this.getConstraintFormat(constraint))
115     });
116     return constraintArray;
117   }
118
119   private getConstraintFormat(constraint: Constraint): any {
120     switch (constraint.type) {
121       case ConstraintTypes.equal:
122         return {
123           [ConstraintTypes.equal]: constraint.value
124         }
125       case ConstraintTypes.less_or_equal:
126         return {
127           [ConstraintTypes.less_or_equal]: constraint.value
128         }
129       case ConstraintTypes.less_than:
130         return {
131           [ConstraintTypes.less_than]: constraint.value
132         }
133       case ConstraintTypes.greater_or_equal:
134         return {
135           [ConstraintTypes.greater_or_equal]: constraint.value
136         }
137       case ConstraintTypes.greater_than:
138         return {
139           [ConstraintTypes.greater_than]: constraint.value
140         }
141       case ConstraintTypes.in_range:
142         return {
143           [ConstraintTypes.in_range]: constraint.value
144         }
145       case ConstraintTypes.length:
146         return {
147           [ConstraintTypes.length]: constraint.value
148         }
149       case ConstraintTypes.max_length:
150         return {
151           [ConstraintTypes.max_length]: constraint.value
152         }
153       case ConstraintTypes.min_length:
154         return {
155           [ConstraintTypes.min_length]: constraint.value
156         }
157       case ConstraintTypes.pattern:
158         return {
159           [ConstraintTypes.pattern]: constraint.value
160         }
161       case ConstraintTypes.valid_values:
162         return {
163           [ConstraintTypes.valid_values]: constraint.value
164         }
165       default:
166         return;
167     }
168   }
169
170   private validateConstraints(): void {
171     this.valid = this.constraints.every((constraint: Constraint) => {
172       if (Array.isArray(constraint.value)) {
173         return !(constraint.value.length == 0 || this.doesArrayContaintEmptyValues(constraint.value));
174       }
175       if (constraint.type == ConstraintTypes.pattern) {
176         try {
177           new RegExp(constraint.value);
178           this.valid = true;
179         } catch(e) {
180           this.valid = false;
181         }
182       }
183       return constraint.value && constraint.type != ConstraintTypes.null
184     });
185   }
186
187   private doesArrayContaintEmptyValues(arr) {
188     for(const element of arr) {
189       if(element === "") return true;
190     }
191     return false;
192   }
193
194   private emitOnConstraintChange(): void {
195     this.validateConstraints();
196     const newConstraints = this.getConstraintsFormat();
197     this.onConstraintChange.emit({
198       constraints: newConstraints,
199       valid: this.valid
200     });
201   }
202
203   removeFromList(constraintIndex: number, valueIndex: number){
204     this.constraints[constraintIndex].value.splice(valueIndex, 1);
205     this.emitOnConstraintChange()
206   }
207
208   addToList(constraintIndex: number){
209     if (!this.constraints[constraintIndex].value) {
210       this.constraints[constraintIndex].value = new Array();
211     }
212     this.constraints[constraintIndex].value.push("");
213     this.emitOnConstraintChange()
214   }
215
216   onChangeConstraintType(constraintIndex: number, newType: ConstraintTypes) {
217     this.constraints[constraintIndex].type = newType;
218     if ((newType == ConstraintTypes.in_range || newType == ConstraintTypes.valid_values) && !Array.isArray(this.constraints[constraintIndex].value)) {
219       this.constraints[constraintIndex].value = new Array()
220     }
221     this.emitOnConstraintChange();
222   }
223
224   onChangeConstraintValue(constraintIndex: number, newValue: any) {
225     this.constraints[constraintIndex].value = newValue;
226     this.emitOnConstraintChange();
227   }
228
229   onChangeConstrainValueIndex(constraintIndex: number, newValue: any, valueIndex: number) {
230     if(!this.constraints[constraintIndex].value) {
231       this.constraints[constraintIndex].value = new Array();
232     }
233     this.constraints[constraintIndex].value[valueIndex] = newValue;
234     this.emitOnConstraintChange();
235   }
236
237   removeConstraint(constraintIndex: number) {
238     this.constraints.splice(constraintIndex, 1);
239     this.emitOnConstraintChange();
240 }
241
242   addConstraint() {
243     let newConstraint: Constraint = {
244       type: ConstraintTypes.null,
245       value: ""
246     }
247     this.constraints.push(newConstraint);
248     this.emitOnConstraintChange();
249   }
250
251   getInRangeValue(constraintIndex: number, valueIndex: number): string {
252     if(!this.constraints[constraintIndex].value || !this.constraints[constraintIndex].value[valueIndex]) {
253       return "";
254     }
255     return this.constraints[constraintIndex].value[valueIndex];
256   }
257
258   disableConstraint(optionConstraintType: ConstraintTypes): boolean {
259     const invalid = this.notAllowedConstraint(optionConstraintType);
260     return invalid ? invalid : this.getConstraintTypeIfPresent(optionConstraintType) ? true : false;
261   }
262
263   notAllowedConstraint(optionConstraintType: ConstraintTypes): boolean {
264     switch (optionConstraintType) {
265       case ConstraintTypes.less_or_equal:
266       case ConstraintTypes.less_than:
267       case ConstraintTypes.greater_or_equal:
268       case ConstraintTypes.greater_than:
269       case ConstraintTypes.in_range:
270         if (this.isComparable(this.propertyType)){
271           return false;
272         }
273         break;
274       case ConstraintTypes.length:
275       case ConstraintTypes.max_length:
276       case ConstraintTypes.min_length:
277         if (this.propertyType == PROPERTY_TYPES.STRING || this.propertyType == PROPERTY_TYPES.MAP || this.propertyType == PROPERTY_TYPES.LIST){
278           return false;
279         }
280         break;
281       case ConstraintTypes.pattern:
282         if (this.propertyType == PROPERTY_TYPES.STRING){
283           return false;
284         }
285         break;
286       case ConstraintTypes.valid_values:
287       case ConstraintTypes.equal:
288         return false;
289     }
290     return true;
291   }
292
293   getConstraintTypeIfPresent(constraintType: ConstraintTypes): Constraint {
294     return this.constraints.find((constraint) => {
295       return constraint.type == constraintType ? true : false;
296     })
297   }
298
299   trackByFn(index) {
300     return index;
301   }
302
303   isComparable(propType: string): boolean {
304     if (PROPERTY_DATA.COMPARABLE_TYPES.indexOf(propType) >= 0) {
305       return true;
306     }
307     return false;
308   }
309
310 }
311
312 export enum ConstraintTypes {
313   null = "",
314   equal= "equal",
315   greater_than = "greaterThan",
316   greater_or_equal = "greaterOrEqual",
317   less_than = "lessThan",
318   less_or_equal = "lessOrEqual",
319   in_range = "inRange",
320   valid_values = "validValues",
321   length = "length",
322   min_length = "minLength",
323   max_length = "maxLength",
324   pattern = "pattern"
325 }
326
327 export const ConstraintTypesMapping = {
328   [ConstraintTypes.equal]: "equal",
329   [ConstraintTypes.greater_than]: "greater_than",
330   [ConstraintTypes.greater_or_equal]: "greater_or_equal",
331   [ConstraintTypes.less_than]: "less_than",
332   [ConstraintTypes.less_or_equal]: "less_or_equal",
333   [ConstraintTypes.in_range]: "in_range",
334   [ConstraintTypes.valid_values]: "valid_values",
335   [ConstraintTypes.length]: "length",
336   [ConstraintTypes.min_length]: "min_length",
337   [ConstraintTypes.max_length]: "max_length",
338   [ConstraintTypes.pattern]: "pattern"
339 };
340
341 export interface Constraint {
342   type:ConstraintTypes,
343   value:any
344 }