Support addition of scalar type 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() set propertyConstraints(propertyConstraints: any[]) {
31     this.constraints = new Array();
32     if(propertyConstraints) {
33       propertyConstraints.forEach((constraint: any) => {
34         this.constraints.push(this.getConstraintFromPropertyBEModel(constraint));
35       });
36     }
37   }
38   @Input() set propertyType(propertyType: string) {
39     if (!this._propertyType || propertyType == this._propertyType) {
40       this._propertyType = propertyType;
41       return;
42     }
43     this.constraints = new Array();
44     this._propertyType = propertyType;
45     this.emitOnConstraintChange();
46   }
47   @Input() isViewOnly: boolean = false;
48   @Output() onConstraintChange: EventEmitter<any> = new EventEmitter<any>();
49
50   constraints: Constraint[] = new Array();
51   constraintTypes: string[];
52   ConstraintTypesMapping = ConstraintTypesMapping;
53   valid: boolean = true;
54   _propertyType: string;
55
56   ngOnInit() {
57     this.constraintTypes = Object.keys(ConstraintTypes).map(key => ConstraintTypes[key]);
58   }
59
60   private getConstraintFromPropertyBEModel(constraint: any):Constraint {
61     let constraintType: ConstraintTypes;
62     let constraintValue: any;
63     if (!constraint) {
64       constraintType = ConstraintTypes.null;
65       constraintValue = "";
66     } else if(constraint.hasOwnProperty(ConstraintTypes.valid_values)){
67       constraintType = ConstraintTypes.valid_values;
68       constraintValue = constraint.validValues;
69     } else if(constraint.hasOwnProperty(ConstraintTypes.equal)) {
70       constraintType = ConstraintTypes.equal;
71       constraintValue = constraint.equal;
72     } else if(constraint.hasOwnProperty(ConstraintTypes.greater_than)) {
73       constraintType = ConstraintTypes.greater_than;
74       constraintValue = constraint.greaterThan;
75     } else if(constraint.hasOwnProperty(ConstraintTypes.greater_or_equal)) {
76       constraintType = ConstraintTypes.greater_or_equal;
77       constraintValue = constraint.greaterOrEqual;
78     } else if(constraint.hasOwnProperty(ConstraintTypes.less_than)) {
79       constraintType = ConstraintTypes.less_than;
80       constraintValue = constraint.lessThan;
81     } else if(constraint.hasOwnProperty(ConstraintTypes.less_or_equal)) {
82       constraintType = ConstraintTypes.less_or_equal;
83       constraintValue = constraint.lessOrEqual;
84     } else if(constraint.hasOwnProperty(ConstraintTypes.in_range)) {
85       constraintType = ConstraintTypes.in_range;
86       constraintValue = new Array(constraint.inRange[0], constraint.inRange[1]);
87     } else if(constraint.rangeMaxValue || constraint.rangeMinValue) {
88       constraintType = ConstraintTypes.in_range;
89       constraintValue = new Array(constraint.rangeMinValue, constraint.rangeMaxValue);
90     } else if(constraint.hasOwnProperty(ConstraintTypes.length)) {
91       constraintType = ConstraintTypes.length;
92       constraintValue = constraint.length;
93     } else if(constraint.hasOwnProperty(ConstraintTypes.min_length)) {
94       constraintType = ConstraintTypes.min_length;
95       constraintValue = constraint.minLength;
96     } else if(constraint.hasOwnProperty(ConstraintTypes.max_length)) {
97       constraintType = ConstraintTypes.max_length;
98       constraintValue = constraint.maxLength;
99     } else if(constraint.hasOwnProperty(ConstraintTypes.pattern)) {
100       constraintType = ConstraintTypes.pattern;
101       constraintValue = constraint.pattern;
102     }
103     return {
104       type:constraintType,
105       value:constraintValue
106     }
107   }
108
109   private getConstraintsFormat(): any[] {
110     let constraintArray = new Array();
111     this.constraints.forEach((constraint: Constraint) => {
112       constraintArray.push(this.getConstraintFormat(constraint))
113     });
114     return constraintArray;
115   }
116
117   private getConstraintFormat(constraint: Constraint): any {
118     switch (constraint.type) {
119       case ConstraintTypes.equal:
120         return {
121           [ConstraintTypes.equal]: constraint.value
122         }
123       case ConstraintTypes.less_or_equal:
124         return {
125           [ConstraintTypes.less_or_equal]: constraint.value
126         }
127       case ConstraintTypes.less_than:
128         return {
129           [ConstraintTypes.less_than]: constraint.value
130         }
131       case ConstraintTypes.greater_or_equal:
132         return {
133           [ConstraintTypes.greater_or_equal]: constraint.value
134         }
135       case ConstraintTypes.greater_than:
136         return {
137           [ConstraintTypes.greater_than]: constraint.value
138         }
139       case ConstraintTypes.in_range:
140         return {
141           [ConstraintTypes.in_range]: constraint.value
142         }
143       case ConstraintTypes.length:
144         return {
145           [ConstraintTypes.length]: constraint.value
146         }
147       case ConstraintTypes.max_length:
148         return {
149           [ConstraintTypes.max_length]: constraint.value
150         }
151       case ConstraintTypes.min_length:
152         return {
153           [ConstraintTypes.min_length]: constraint.value
154         }
155       case ConstraintTypes.pattern:
156         return {
157           [ConstraintTypes.pattern]: constraint.value
158         }
159       case ConstraintTypes.valid_values:
160         return {
161           [ConstraintTypes.valid_values]: constraint.value
162         }
163       default:
164         return;
165     }
166   }
167
168   private validateConstraints(): void {
169     this.valid = this.constraints.every((constraint: Constraint) => {
170       if (Array.isArray(constraint.value)) {
171         return !(constraint.value.length == 0 || this.doesArrayContaintEmptyValues(constraint.value));
172       }
173       return constraint.value && constraint.type != ConstraintTypes.null
174     });
175   }
176
177   private doesArrayContaintEmptyValues(arr) {
178     for(const element of arr) {
179       if(element === "") return true;
180     }
181     return false;
182   }
183
184   private emitOnConstraintChange(): void {
185     this.validateConstraints();
186     const newConstraints = this.getConstraintsFormat();
187     this.onConstraintChange.emit({
188       constraints: newConstraints,
189       valid: this.valid
190     });
191   }
192
193   removeFromList(constraintIndex: number, valueIndex: number){
194     this.constraints[constraintIndex].value.splice(valueIndex, 1);
195     this.emitOnConstraintChange()
196   }
197
198   addToList(constraintIndex: number){
199     if (!this.constraints[constraintIndex].value) {
200       this.constraints[constraintIndex].value = new Array();
201     }
202     this.constraints[constraintIndex].value.push("");
203     this.emitOnConstraintChange()
204   }
205
206   onChangeConstraintType(constraintIndex: number, newType: ConstraintTypes) {
207     this.constraints[constraintIndex].type = newType;
208     if ((newType == ConstraintTypes.in_range || newType == ConstraintTypes.valid_values) && !Array.isArray(this.constraints[constraintIndex].value)) {
209       this.constraints[constraintIndex].value = new Array()
210     }
211     this.emitOnConstraintChange();
212   }
213
214   onChangeConstraintValue(constraintIndex: number, newValue: any) {
215     this.constraints[constraintIndex].value = newValue;
216     this.emitOnConstraintChange();
217   }
218
219   onChangeConstrainValueIndex(constraintIndex: number, newValue: any, valueIndex: number) {
220     if(!this.constraints[constraintIndex].value) {
221       this.constraints[constraintIndex].value = new Array();
222     }
223     this.constraints[constraintIndex].value[valueIndex] = newValue;
224     this.emitOnConstraintChange();
225   }
226
227   removeConstraint(constraintIndex: number) {
228     this.constraints.splice(constraintIndex, 1);
229     this.emitOnConstraintChange();
230 }
231
232   addConstraint() {
233     let newConstraint: Constraint = {
234       type: ConstraintTypes.null,
235       value: ""
236     }
237     this.constraints.push(newConstraint);
238     this.emitOnConstraintChange();
239   }
240
241   getInRangeValue(constraintIndex: number, valueIndex: number): string {
242     if(!this.constraints[constraintIndex].value || !this.constraints[constraintIndex].value[valueIndex]) {
243       return "";
244     }
245     return this.constraints[constraintIndex].value[valueIndex];
246   }
247
248   disableConstraint(optionConstraintType: ConstraintTypes): boolean {
249     const invalid = this.notAllowedConstraint(optionConstraintType);
250     return invalid ? invalid : this.getConstraintTypeIfPresent(optionConstraintType) ? true : false;
251   }
252
253   notAllowedConstraint(optionConstraintType: ConstraintTypes): boolean {
254     switch (optionConstraintType) {
255       case ConstraintTypes.less_or_equal:
256       case ConstraintTypes.less_than:
257       case ConstraintTypes.greater_or_equal:
258       case ConstraintTypes.greater_than:
259       case ConstraintTypes.in_range:
260         if (this.isComparable(this._propertyType)){
261           return false;
262         }
263         break;
264       case ConstraintTypes.length:
265       case ConstraintTypes.max_length:
266       case ConstraintTypes.min_length:
267         if (this._propertyType == PROPERTY_TYPES.STRING || this._propertyType == PROPERTY_TYPES.MAP || this._propertyType == PROPERTY_TYPES.LIST){
268           return false;
269         }
270         break;
271       case ConstraintTypes.pattern:
272         if (this._propertyType == PROPERTY_TYPES.STRING){
273           return false;
274         }
275         break;
276       case ConstraintTypes.valid_values:
277       case ConstraintTypes.equal:
278         return false;
279     }
280     return true;
281   }
282
283   getConstraintTypeIfPresent(constraintType: ConstraintTypes): Constraint {
284     return this.constraints.find((constraint) => {
285       return constraint.type == constraintType ? true : false;
286     })
287   }
288
289   trackByFn(index) {
290     return index;
291   }
292
293   isComparable(propType: string): boolean {
294     if (PROPERTY_DATA.COMPARABLE_TYPES.indexOf(propType) >= 0) {
295       return true;
296     }
297     return false;
298   }
299
300 }
301
302 export enum ConstraintTypes {
303   null = "",
304   equal= "equal",
305   greater_than = "greaterThan",
306   greater_or_equal = "greaterOrEqual",
307   less_than = "lessThan",
308   less_or_equal = "lessOrEqual",
309   in_range = "inRange",
310   valid_values = "validValues",
311   length = "length",
312   min_length = "minLength",
313   max_length = "maxLength",
314   pattern = "pattern"
315 }
316
317 export const ConstraintTypesMapping = {
318   [ConstraintTypes.equal]: "equal",
319   [ConstraintTypes.greater_than]: "greater_than",
320   [ConstraintTypes.greater_or_equal]: "greater_or_equal",
321   [ConstraintTypes.less_than]: "less_than",
322   [ConstraintTypes.less_or_equal]: "less_or_equal",
323   [ConstraintTypes.in_range]: "in_range",
324   [ConstraintTypes.valid_values]: "valid_values",
325   [ConstraintTypes.length]: "length",
326   [ConstraintTypes.min_length]: "min_length",
327   [ConstraintTypes.max_length]: "max_length",
328   [ConstraintTypes.pattern]: "pattern"
329 };
330
331 export interface Constraint {
332   type:ConstraintTypes,
333   value:any
334 }