3 * ============LICENSE_START=======================================================
4 * Copyright (C) 2022 Nordix Foundation.
5 * ================================================================================
6 * Licensed under the Apache License, Version 2.0 (the "License");
7 * you may not use this file except in compliance with the License.
8 * You may obtain a copy of the License at
10 * http://www.apache.org/licenses/LICENSE-2.0
12 * Unless required by applicable law or agreed to in writing, software
13 * distributed under the License is distributed on an "AS IS" BASIS,
14 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15 * See the License for the specific language governing permissions and
16 * limitations under the License.
18 * SPDX-License-Identifier: Apache-2.0
19 * ============LICENSE_END=========================================================
22 import {Component, EventEmitter, Input, OnDestroy, OnInit, Output} from '@angular/core';
23 import {PropertyBEModel} from "../../../../../models/properties-inputs/property-be-model";
24 import {AbstractControl, FormControl, FormGroup, ValidationErrors, Validators} from "@angular/forms";
25 import {PROPERTY_DATA} from "../../../../../utils/constants";
26 import {DataTypeService} from "../../../../services/data-type.service";
27 import {DataTypeModel} from "../../../../../models/data-types";
28 import {Subscription} from "rxjs";
29 import {ToscaTypeHelper} from "../../../../../utils/tosca-type-helper";
30 import {SchemaProperty, SchemaPropertyGroupModel} from "../../../../../models/schema-property";
33 selector: 'app-add-property',
34 templateUrl: './add-property.component.html',
35 styleUrls: ['./add-property.component.less']
37 export class AddPropertyComponent implements OnInit, OnDestroy {
39 @Input() property: PropertyBEModel;
40 @Input() readOnly: boolean = true;
41 @Input() model: string;
43 @Output() onValidityChange: EventEmitter<PropertyValidationEvent> = new EventEmitter<PropertyValidationEvent>();
45 private validConstraints: boolean = true;
46 private valueChangesSub: Subscription;
47 private descriptionForm: FormControl = new FormControl(undefined);
48 private requiredForm: FormControl = new FormControl(false, Validators.required);
49 nameForm: FormControl = new FormControl(undefined, [Validators.required]);
50 typeForm: FormControl = new FormControl(undefined, Validators.required);
51 schemaForm: FormControl = new FormControl(undefined, (control: AbstractControl): ValidationErrors | null => {
52 if (this.typeNeedsSchema() && !control.value) {
53 return {required: true};
58 hasDefaultValueForm: FormControl = new FormControl(false, Validators.required);
59 defaultValueForm: FormControl = new FormControl(undefined);
60 constraintsForm: FormControl = new FormControl(undefined);
61 formGroup: FormGroup = new FormGroup({
62 'name': this.nameForm,
63 'description': this.descriptionForm,
64 'type': this.typeForm,
65 'required': this.requiredForm,
66 'schema': this.schemaForm,
67 'defaultValue': this.defaultValueForm,
68 'hasDefaultValue': this.hasDefaultValueForm,
69 'constraints': this.constraintsForm,
72 isLoading: boolean = false;
73 showSchema: boolean = false;
75 dataTypeMap: Map<string, DataTypeModel>;
76 dataType: DataTypeModel;
77 schemaTypeList: string[];
79 constructor(private dataTypeService: DataTypeService) {
83 this.isLoading = true;
84 this.initTypeAndSchemaDropdown().then(() => this.updateDataType());
86 this.valueChangesSub = this.formGroup.valueChanges.subscribe(() => {
87 this.emitValidityChange();
92 if (this.valueChangesSub) {
93 this.valueChangesSub.unsubscribe();
97 onSchemaChange(): void {
98 this.resetDefaultValue();
101 onTypeChange(): void {
102 this.schemaForm.setValue(null);
103 this.showSchema = this.typeNeedsSchema();
104 this.updateDataType();
105 this.resetDefaultValue();
107 this.property.constraints = [];
111 private updateDataType(): void {
112 this.dataType = this.dataTypeMap.get(this.typeForm.value);
115 private initForm(): void {
116 if (!this.property) {
120 this.nameForm.setValue(this.property.name);
121 this.descriptionForm.setValue(this.property.description);
122 this.typeForm.setValue(this.property.type);
123 this.showSchema = this.typeNeedsSchema();
124 this.requiredForm.setValue(this.property.required);
125 this.schemaForm.setValue(this.property.schemaType);
126 this.constraintsForm.setValue(this.property.constraints);
127 this.initDefaultValueForm();
130 private initDefaultValueForm() {
131 if (this.property.defaultValue == undefined) {
135 if (!this.isTypeSimple() && typeof this.property.defaultValue === 'string') {
136 defaultValue = JSON.parse(this.property.defaultValue);
138 defaultValue = this.property.defaultValue;
140 this.defaultValueForm.setValue(defaultValue);
141 this.hasDefaultValueForm.setValue(true);
144 private typeNeedsSchema() {
145 return PROPERTY_DATA.SCHEMA_TYPES.indexOf(this.typeForm.value) > -1;
148 private initTypeAndSchemaDropdown(): Promise<Map<string, DataTypeModel>> {
149 const primitiveTypes: string[] = Array.from(PROPERTY_DATA.TYPES).sort((a, b) => a.localeCompare(b));
150 const promise = this.dataTypeService.findAllDataTypesByModel(this.model);
151 promise.then((dataTypeMap: Map<string, DataTypeModel>) => {
152 this.dataTypeMap = dataTypeMap;
153 const nonPrimitiveTypes: string[] = Array.from(dataTypeMap.keys()).filter(type => {
154 return primitiveTypes.indexOf(type) === -1;
156 nonPrimitiveTypes.sort((a, b) => a.localeCompare(b));
157 this.typeList = [...primitiveTypes, ...nonPrimitiveTypes];
158 this.schemaTypeList = Array.from(this.typeList);
159 this.isLoading = false;
164 private emitValidityChange(): void {
165 const isValid: boolean = this.formGroup.valid;
166 this.onValidityChange.emit({
167 isValid: isValid && this.validConstraints,
168 property: isValid ? this.buildPropertyFromForm() : undefined
172 private buildPropertyFromForm(): PropertyBEModel {
173 const property = new PropertyBEModel();
174 property.name = this.nameForm.value;
175 property.type = this.typeForm.value;
176 property.constraints = this.constraintsForm.value;
177 if (this.schemaForm.value) {
178 property.schemaType = this.schemaForm.value;
180 property.description = this.descriptionForm.value;
181 if (this.hasDefaultValueForm.value === true) {
182 property.defaultValue = this.defaultValueForm.value;
187 public isTypeSimple(): boolean {
188 return ToscaTypeHelper.isTypeSimple(this.typeForm.value);
191 public isTypeList(): boolean {
192 return ToscaTypeHelper.isTypeList(this.typeForm.value);
195 public isTypeMap(): boolean {
196 return ToscaTypeHelper.isTypeMap(this.typeForm.value);
199 public isTypeComplex(): boolean {
200 return ToscaTypeHelper.isTypeComplex(this.typeForm.value);
203 private isTypeRange() {
204 return ToscaTypeHelper.isTypeRange(this.typeForm.value);
207 onPropertyValueChange($event: any): void {
208 this.defaultValueForm.setValue($event.value);
211 showDefaultValue(): boolean {
213 return this.defaultValueForm.value != undefined && this.dataTypeMap && this.typeForm.valid && this.schemaForm.valid;
215 return this.dataTypeMap && this.typeForm.valid && this.schemaForm.valid;
218 getDataType(type: string): DataTypeModel {
219 return this.dataTypeMap.get(type);
222 private resetDefaultValue(): void {
223 this.defaultValueForm.reset();
224 if (this.isTypeComplex() || this.isTypeMap()) {
225 this.defaultValueForm.setValue({});
226 } else if (this.isTypeList() || this.isTypeRange()) {
227 this.defaultValueForm.setValue([]);
231 buildSchemaGroupProperty(): SchemaPropertyGroupModel {
232 const schemaProperty = new SchemaProperty();
233 schemaProperty.type = this.schemaForm.value
234 return new SchemaPropertyGroupModel(schemaProperty);
237 onConstraintChange = (constraints: any): void => {
239 if (!this.property.constraints) {
240 this.property.constraints = [];
242 this.property.constraints = constraints.constraints;
245 this.constraintsForm.setValue(constraints.constraints);
247 this.validConstraints = constraints.valid;
248 this.onValidityChange.emit({
249 isValid: constraints.valid,
250 property: constraints.valid ? this.buildPropertyFromForm() : undefined
255 export class PropertyValidationEvent {
257 property: PropertyBEModel;