aboutsummaryrefslogtreecommitdiffstats
path: root/catalog-ui
diff options
context:
space:
mode:
authorandre.schmid <andre.schmid@est.tech>2022-10-12 18:14:23 +0100
committerVasyl Razinkov <vasyl.razinkov@est.tech>2022-11-25 11:00:53 +0000
commit5e71c18416adc5c136ea9053a6bbac819da18c60 (patch)
tree51984434750fc8d7f80d25550196b7939b581553 /catalog-ui
parent7ae4305a259d32520a5120a3e23710cbd2c9187c (diff)
Implement create data type property
Allows to add a new data type property and visualize the properties details. Change-Id: Ib7bcd4b0bd8213dbe8ee8a3762a0636e22dc67eb Issue-ID: SDC-4258 Signed-off-by: André Schmid <andre.schmid@est.tech>
Diffstat (limited to 'catalog-ui')
-rw-r--r--catalog-ui/src/app/ng2/pages/composition/interface-operatons/operation-creator/input-list/input-list-item/input-list-item.component.html18
-rw-r--r--catalog-ui/src/app/ng2/pages/composition/interface-operatons/operation-creator/input-list/input-list-item/input-list-item.component.ts80
-rw-r--r--catalog-ui/src/app/ng2/pages/composition/interface-operatons/operation-creator/interface-operation-handler.module.ts51
-rw-r--r--catalog-ui/src/app/ng2/pages/type-workspace/type-workspace-properties/add-property/add-property.component.html66
-rw-r--r--catalog-ui/src/app/ng2/pages/type-workspace/type-workspace-properties/add-property/add-property.component.less104
-rw-r--r--catalog-ui/src/app/ng2/pages/type-workspace/type-workspace-properties/add-property/add-property.component.spec.ts88
-rw-r--r--catalog-ui/src/app/ng2/pages/type-workspace/type-workspace-properties/add-property/add-property.component.ts233
-rw-r--r--catalog-ui/src/app/ng2/pages/type-workspace/type-workspace-properties/type-workspace-properties.component.html37
-rw-r--r--catalog-ui/src/app/ng2/pages/type-workspace/type-workspace-properties/type-workspace-properties.component.less10
-rw-r--r--catalog-ui/src/app/ng2/pages/type-workspace/type-workspace-properties/type-workspace-properties.component.spec.ts5
-rw-r--r--catalog-ui/src/app/ng2/pages/type-workspace/type-workspace-properties/type-workspace-properties.component.ts238
-rw-r--r--catalog-ui/src/app/ng2/pages/type-workspace/type-workspace.component.html2
-rw-r--r--catalog-ui/src/app/ng2/pages/type-workspace/type-workspace.module.ts14
-rw-r--r--catalog-ui/src/app/ng2/services/data-type.service.ts5
-rw-r--r--catalog-ui/src/app/utils/constants.ts1
-rw-r--r--catalog-ui/src/app/utils/tosca-type-helper.ts78
-rw-r--r--catalog-ui/src/assets/languages/en_US.json16
17 files changed, 868 insertions, 178 deletions
diff --git a/catalog-ui/src/app/ng2/pages/composition/interface-operatons/operation-creator/input-list/input-list-item/input-list-item.component.html b/catalog-ui/src/app/ng2/pages/composition/interface-operatons/operation-creator/input-list/input-list-item/input-list-item.component.html
index 1e0804eb04..6a5bd5a007 100644
--- a/catalog-ui/src/app/ng2/pages/composition/interface-operatons/operation-creator/input-list/input-list-item/input-list-item.component.html
+++ b/catalog-ui/src/app/ng2/pages/composition/interface-operatons/operation-creator/input-list/input-list-item/input-list-item.component.html
@@ -67,12 +67,16 @@
<ul *ngIf="isExpanded">
<li class="input-value">
<ng-container *ngIf="isViewOnly">
- {{valueObjRef}}<em class="empty-value" *ngIf="valueObjRef !== false && !valueObjRef">empty</em>
+ {{valueObjRef}}<em class="empty-value" *ngIf="valueObjRef !== false && !valueObjRef">{{'GENERAL_LABEL_EMPTY' | translate | lowercase}}</em>
</ng-container>
- <input *ngIf="!isViewOnly" [type]="getSimpleValueInputType()" name="value"
- [(ngModel)]="valueObjRef"
- (ngModelChange)="onValueChange($event)"
- />
+ <input *ngIf="!isViewOnly && isTypeLiteral(type.name)" type="text" name="value"
+ [(ngModel)]="valueObjRef" (ngModelChange)="onValueChange($event)"/>
+ <input *ngIf="!isViewOnly && isTypeNumber(type.name)" type="number"
+ [(ngModel)]="valueObjRef" (ngModelChange)="onValueChange($event)"/>
+ <select *ngIf="!isViewOnly && isTypeBoolean(type.name)" [(ngModel)]="valueObjRef" (ngModelChange)="onValueChange($event)">
+ <option [value]="true">{{'GENERAL_LABEL_TRUE' | translate}}</option>
+ <option [value]="false">{{'GENERAL_LABEL_FALSE' | translate}}</option>
+ </select>
</li>
</ul>
</ng-container>
@@ -123,7 +127,7 @@
[type]="getDataType(schema.property.type)"
[dataTypeMap]="dataTypeMap"
[valueObjRef]="valueObjRef[i]"
- [schema]="schema"
+ [schema]="buildSchemaGroupProperty()"
[nestingLevel]="nestingLevel + 1"
[listIndex]="i"
[isListChild]="true"
@@ -155,7 +159,7 @@
[type]="getDataType(schema.property.type)"
[dataTypeMap]="dataTypeMap"
[valueObjRef]="valueObjRef[key]"
- [schema]="schema"
+ [schema]="buildSchemaGroupProperty()"
[isMapChild]="true"
[nestingLevel]="nestingLevel + 1"
[isViewOnly]="isViewOnly"
diff --git a/catalog-ui/src/app/ng2/pages/composition/interface-operatons/operation-creator/input-list/input-list-item/input-list-item.component.ts b/catalog-ui/src/app/ng2/pages/composition/interface-operatons/operation-creator/input-list/input-list-item/input-list-item.component.ts
index 88ff8deec6..145aad687c 100644
--- a/catalog-ui/src/app/ng2/pages/composition/interface-operatons/operation-creator/input-list/input-list-item/input-list-item.component.ts
+++ b/catalog-ui/src/app/ng2/pages/composition/interface-operatons/operation-creator/input-list/input-list-item/input-list-item.component.ts
@@ -21,12 +21,13 @@
import {Component, EventEmitter, Input, OnInit, Output} from '@angular/core';
import {DataTypeModel} from '../../../../../../../models/data-types';
-import {SchemaPropertyGroupModel} from '../../../../../../../models/schema-property';
-import {DerivedPropertyType, PropertyBEModel} from '../../../../../../../models/properties-inputs/property-be-model';
+import {SchemaProperty, SchemaPropertyGroupModel} from '../../../../../../../models/schema-property';
+import {PropertyBEModel} from '../../../../../../../models/properties-inputs/property-be-model';
import {PROPERTY_DATA, PROPERTY_TYPES} from '../../../../../../../utils/constants';
import {ToscaFunction} from '../../../../../../../models/tosca-function';
-import {ToscaFunctionValidationEvent} from "../../../../../../../ng2/pages/properties-assignment/tosca-function/tosca-function.component";
+import {ToscaFunctionValidationEvent} from "../../../../../properties-assignment/tosca-function/tosca-function.component";
import {InstanceFeDetails} from "../../../../../../../models/instance-fe-details";
+import {ToscaTypeHelper} from "app/utils/tosca-type-helper";
@Component({
selector: 'app-input-list-item',
@@ -41,6 +42,7 @@ export class InputListItemComponent implements OnInit {
@Input() type: DataTypeModel;
@Input() schema: SchemaPropertyGroupModel;
@Input() nestingLevel: number;
+ @Input() isExpanded: boolean = false;
@Input() isListChild: boolean = false;
@Input() isMapChild: boolean = false;
@Input() showToscaFunctionOption: boolean = false;
@@ -53,12 +55,11 @@ export class InputListItemComponent implements OnInit {
@Output('onDelete') onDeleteEvent: EventEmitter<string> = new EventEmitter<string>();
@Output('onChildListItemDelete') onChildListItemDeleteEvent: EventEmitter<number> = new EventEmitter<number>();
- isExpanded: boolean = false;
mapEntryName: string;
isToscaFunction: boolean = false;
property: PropertyBEModel;
- ngOnInit() {
+ ngOnInit(): void {
if (!this.nestingLevel) {
this.nestingLevel = 0;
}
@@ -93,26 +94,20 @@ export class InputListItemComponent implements OnInit {
}
}
- getType(typeName: string): DerivedPropertyType {
- if (PROPERTY_DATA.SIMPLE_TYPES.indexOf(typeName) > -1) {
- return DerivedPropertyType.SIMPLE;
- } else if (typeName === PROPERTY_TYPES.LIST) {
- return DerivedPropertyType.LIST;
- } else if (typeName === PROPERTY_TYPES.MAP) {
- return DerivedPropertyType.MAP;
- } else if (typeName === PROPERTY_TYPES.RANGE) {
- return DerivedPropertyType.RANGE;
- } else {
- return DerivedPropertyType.COMPLEX;
- }
+ isTypeSimple(typeName: string): boolean {
+ return ToscaTypeHelper.isTypeSimple(typeName) || this.isTypeDerivedFromSimple(typeName) && (this.isTypeWithoutProperties(typeName));
+ }
+
+ isTypeRange(typeName: string): boolean {
+ return ToscaTypeHelper.isTypeRange(typeName);
}
isTypeWithoutProperties(typeName: string): boolean {
- if (this.dataTypeMap.get(typeName) === undefined) {
- return true;
- }
- return this.dataTypeMap.get(typeName).properties === undefined ||
- this.dataTypeMap.get(typeName).properties.length == 0;
+ if (this.dataTypeMap.get(typeName) === undefined) {
+ return true;
+ }
+ return this.dataTypeMap.get(typeName).properties === undefined ||
+ this.dataTypeMap.get(typeName).properties.length == 0;
}
isTypeDerivedFromSimple(typeName: string): boolean {
@@ -134,27 +129,28 @@ export class InputListItemComponent implements OnInit {
return true;
}
- isTypeSimple(typeName: string): boolean {
- if (this.getType(typeName) == DerivedPropertyType.SIMPLE) {
- return true;
- }
- return this.isTypeDerivedFromSimple(typeName) && (this.isTypeWithoutProperties(typeName));
+ isTypeList(typeName: string): boolean {
+ return ToscaTypeHelper.isTypeList(typeName);
}
- isTypeRange(typeName: string): boolean {
- return this.getType(typeName) == DerivedPropertyType.RANGE;
+ isTypeMap(typeName: string): boolean {
+ return ToscaTypeHelper.isTypeMap(typeName);
}
- isTypeList(typeName: string): boolean {
- return this.getType(typeName) == DerivedPropertyType.LIST;
+ isTypeComplex(typeName: string): boolean {
+ return ToscaTypeHelper.isTypeComplex(typeName);
}
- isTypeMap(typeName: string): boolean {
- return this.getType(typeName) == DerivedPropertyType.MAP;
+ isTypeNumber(type: string): boolean {
+ return ToscaTypeHelper.isTypeNumber(type);
}
- isTypeComplex(typeName: string): boolean {
- return !this.isTypeSimple(typeName) && !this.isTypeList(typeName) && !this.isTypeMap(typeName) && !this.isTypeRange(typeName);
+ isTypeBoolean(type: string): boolean {
+ return ToscaTypeHelper.isTypeBoolean(type);
+ }
+
+ isTypeLiteral(type: string): boolean {
+ return ToscaTypeHelper.isTypeLiteral(type);
}
expandAndCollapse() {
@@ -180,7 +176,7 @@ export class InputListItemComponent implements OnInit {
}
onValueChange(value: any): void {
- if (this.isNumber(this.type.name)) {
+ if (this.isTypeNumber(this.type.name)) {
this.emitValueChangeEvent(this.parseNumber(value));
return;
}
@@ -297,14 +293,20 @@ export class InputListItemComponent implements OnInit {
}
getSimpleValueInputType() {
- if (this.isNumber(this.type.name)){
+ if (this.isTypeNumber(this.type.name)){
return 'number';
}
return 'text';
}
- isNumber(type: string): boolean {
- return type === PROPERTY_TYPES.INTEGER || type === PROPERTY_TYPES.FLOAT;
+ buildSchemaGroupProperty(): SchemaPropertyGroupModel {
+ const schemaProperty = new SchemaProperty();
+ if (this.schema.property.type === PROPERTY_TYPES.MAP || this.schema.property.type === PROPERTY_TYPES.LIST) {
+ schemaProperty.type = PROPERTY_TYPES.STRING;
+ } else {
+ schemaProperty.type = this.schema.property.type
+ }
+ return new SchemaPropertyGroupModel(schemaProperty);
}
private parseBoolean(value: any) {
diff --git a/catalog-ui/src/app/ng2/pages/composition/interface-operatons/operation-creator/interface-operation-handler.module.ts b/catalog-ui/src/app/ng2/pages/composition/interface-operatons/operation-creator/interface-operation-handler.module.ts
index c829ff9e5a..b4fc9d1bdb 100644
--- a/catalog-ui/src/app/ng2/pages/composition/interface-operatons/operation-creator/interface-operation-handler.module.ts
+++ b/catalog-ui/src/app/ng2/pages/composition/interface-operatons/operation-creator/interface-operation-handler.module.ts
@@ -36,31 +36,32 @@ import {PropertyTableModule} from "app/ng2/components/logic/properties-table/pro
import {ToscaFunctionModule} from '../../../properties-assignment/tosca-function/tosca-function.module';
@NgModule({
- declarations: [
- InterfaceOperationHandlerComponent,
- PropertyParamRowComponent,
- AddInputComponent,
- InputListComponent,
- InputListItemComponent
- ],
- imports: [
- CommonModule,
- SdcUiComponentsModule,
- FormsModule,
- FormElementsModule,
- TranslateModule,
- UiElementsModule,
- PropertyTableModule,
- ReactiveFormsModule,
- ToscaFunctionModule
- ],
- exports: [
- PropertyParamRowComponent
- ],
- entryComponents: [
- InterfaceOperationHandlerComponent
- ],
- providers: []
+ declarations: [
+ InterfaceOperationHandlerComponent,
+ PropertyParamRowComponent,
+ AddInputComponent,
+ InputListComponent,
+ InputListItemComponent
+ ],
+ imports: [
+ CommonModule,
+ SdcUiComponentsModule,
+ FormsModule,
+ FormElementsModule,
+ TranslateModule,
+ UiElementsModule,
+ PropertyTableModule,
+ ReactiveFormsModule,
+ ToscaFunctionModule
+ ],
+ exports: [
+ PropertyParamRowComponent,
+ InputListItemComponent
+ ],
+ entryComponents: [
+ InterfaceOperationHandlerComponent
+ ],
+ providers: []
})
export class InterfaceOperationHandlerModule {
diff --git a/catalog-ui/src/app/ng2/pages/type-workspace/type-workspace-properties/add-property/add-property.component.html b/catalog-ui/src/app/ng2/pages/type-workspace/type-workspace-properties/add-property/add-property.component.html
new file mode 100644
index 0000000000..af72e6d6d6
--- /dev/null
+++ b/catalog-ui/src/app/ng2/pages/type-workspace/type-workspace-properties/add-property/add-property.component.html
@@ -0,0 +1,66 @@
+<div class="add-property-container">
+ <loader [display]="isLoading" [size]="'large'" [relative]="true" [loaderDelay]="500"></loader>
+ <form class="w-sdc-form" [formGroup]="formGroup">
+
+ <div class="side-by-side">
+ <div class="i-sdc-form-item">
+ <label class="i-sdc-form-label required">{{'PROPERTY_NAME_LABEL' | translate}}</label>
+ <input class="i-sdc-form-input"
+ type="text"
+ data-tests-id="property-name"
+ formControlName="name"
+ [readOnly]="readOnly"/>
+ </div>
+ <div class="i-sdc-form-item">
+ <label class="i-sdc-form-label required">{{'PROPERTY_TYPE_LABEL' | translate}}</label>
+ <select formControlName="type" (change)="onTypeChange()" [attr.disabled]="readOnly ? readOnly : null">
+ <option [ngValue]="null">{{'GENERAL_LABEL_SELECT' | translate}}</option>
+ <option *ngFor="let type of typeList"
+ [ngValue]="type">{{type}}</option>
+ </select>
+ </div>
+ <div class="i-sdc-form-item">
+ <input type="checkbox" formControlName="required" [attr.disabled]="readOnly ? readOnly : null"/> {{'PROPERTY_REQUIRED_LABEL' | translate}}
+ </div>
+ <div class="i-sdc-form-item propertySchemaType" *ngIf="showSchema">
+ <label class="i-sdc-form-label required">{{'PROPERTY_SCHEMA_LABEL' | translate}}</label>
+ <select formControlName="schema" (change)="onSchemaChange()" [attr.disabled]="readOnly ? readOnly : null">
+ <option [ngValue]="null">{{'GENERAL_LABEL_SELECT' | translate}}</option>
+ <option *ngFor="let type of schemaTypeList"
+ [ngValue]="type">{{type}}</option>
+ </select>
+ </div>
+ </div>
+
+ <div class="i-sdc-form-item">
+ <label class="i-sdc-form-label">{{'PROPERTY_DESCRIPTION_LABEL' | translate}}</label>
+ <textarea class="i-sdc-form-textarea"
+ formControlName="description"
+ data-tests-id="property-description"
+ [readOnly]="readOnly">
+ </textarea>
+ </div>
+ <div class="default-value-container i-sdc-form-item" *ngIf="showDefaultValue()">
+ <label class="i-sdc-form-label">{{'PROPERTY_DEFAULT_VALUE_LABEL' | translate}}</label>
+ <ng-container *ngIf="!readOnly">
+ <input type="checkbox" formControlName="hasDefaultValue" [attr.disabled]="readOnly ? readOnly : null"/> {{'PROPERTY_SET_DEFAULT_VALUE_MSG' | translate}}
+ </ng-container>
+ <ng-container *ngIf="hasDefaultValueForm.value">
+ <ul>
+ <app-input-list-item
+ [name]="nameForm.value"
+ [type]="getDataType(typeForm.value)"
+ [dataTypeMap]="dataTypeMap"
+ [valueObjRef]="defaultValueForm.value"
+ [schema]="buildSchemaGroupProperty()"
+ [nestingLevel]="0"
+ [isViewOnly]="readOnly"
+ [allowDeletion]="false"
+ [isExpanded]="true"
+ (onValueChange)="onPropertyValueChange($event)">
+ </app-input-list-item>
+ </ul>
+ </ng-container>
+ </div>
+ </form>
+</div>
diff --git a/catalog-ui/src/app/ng2/pages/type-workspace/type-workspace-properties/add-property/add-property.component.less b/catalog-ui/src/app/ng2/pages/type-workspace/type-workspace-properties/add-property/add-property.component.less
new file mode 100644
index 0000000000..2c3300237c
--- /dev/null
+++ b/catalog-ui/src/app/ng2/pages/type-workspace/type-workspace-properties/add-property/add-property.component.less
@@ -0,0 +1,104 @@
+/*
+ * -
+ * ============LICENSE_START=======================================================
+ * Copyright (C) 2022 Nordix Foundation.
+ * ================================================================================
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ * ============LICENSE_END=========================================================
+ */
+
+.add-property-container {
+ max-width: 100%;
+}
+
+.default-value-container {
+ overflow: scroll;
+ max-height: 300px;
+ max-width: 100%;
+
+ ul {
+ margin: 0 0 0 20px;
+ list-style: none;
+ line-height: 2em;
+ }
+
+ &::-webkit-scrollbar-track {
+ border: 0;
+ }
+}
+
+li {
+ position: relative;
+
+ &:before {
+ position: absolute;
+ left: -15px;
+ top: 0;
+ content: '';
+ display: block;
+ border-left: 1px solid #ddd;
+ height: 1em;
+ border-bottom: 1px solid #ddd;
+ width: 10px;
+ }
+
+ &:after {
+ position: absolute;
+ left: -15px;
+ bottom: -7px;
+ content: '';
+ display: block;
+ border-left: 1px solid #ddd;
+ height: 100%;
+ }
+
+ &.root {
+ margin: 0 0 0 -20px;
+
+ &:before {
+ display: none;
+ }
+
+ &:after {
+ display: none;
+ }
+ }
+
+ &:last-child {
+ &:after {
+ display: none
+ }
+ }
+}
+
+.input-label {
+ margin: 0;
+ font-weight: bold;
+}
+
+.input-value {
+ display: flex;
+ flex-flow: row nowrap;
+ gap: 7px;
+
+ input {
+ min-width: 150px;
+ max-width: 250px;
+ }
+}
+
+.empty-value {
+ color: #aaaaaa;
+} \ No newline at end of file
diff --git a/catalog-ui/src/app/ng2/pages/type-workspace/type-workspace-properties/add-property/add-property.component.spec.ts b/catalog-ui/src/app/ng2/pages/type-workspace/type-workspace-properties/add-property/add-property.component.spec.ts
new file mode 100644
index 0000000000..7e2c312792
--- /dev/null
+++ b/catalog-ui/src/app/ng2/pages/type-workspace/type-workspace-properties/add-property/add-property.component.spec.ts
@@ -0,0 +1,88 @@
+/*
+ * -
+ * ============LICENSE_START=======================================================
+ * Copyright (C) 2022 Nordix Foundation.
+ * ================================================================================
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ * ============LICENSE_END=========================================================
+ */
+
+import {async, ComponentFixture, TestBed} from '@angular/core/testing';
+
+import {AddPropertyComponent} from './add-property.component';
+import {FormsModule, ReactiveFormsModule} from "@angular/forms";
+import {TranslateModule} from "../../../../shared/translator/translate.module";
+import {UiElementsModule} from "../../../../components/ui/ui-elements.module";
+import {Component, Input} from "@angular/core";
+import {DataTypeModel} from "../../../../../models/data-types";
+import {SchemaPropertyGroupModel} from "../../../../../models/schema-property";
+import {DataTypeService} from "../../../../services/data-type.service";
+import {TranslateService} from "../../../../shared/translator/translate.service";
+
+@Component({selector: 'app-input-list-item', template: ''})
+class InputListItemStubComponent {
+ @Input() valueObjRef: any;
+ @Input() name: string;
+ @Input() dataTypeMap: Map<string, DataTypeModel>;
+ @Input() type: DataTypeModel;
+ @Input() schema: SchemaPropertyGroupModel;
+ @Input() nestingLevel: number;
+ @Input() isExpanded: boolean = false;
+ @Input() isListChild: boolean = false;
+ @Input() isMapChild: boolean = false;
+ @Input() listIndex: number;
+ @Input() isViewOnly: boolean;
+ @Input() allowDeletion: boolean = false;
+}
+
+describe('AddPropertyComponent', () => {
+ let dataTypeServiceMock: Partial<DataTypeService> = {
+ findAllDataTypesByModel: jest.fn(args => {
+ return Promise.resolve(new Map());
+ })
+ };
+ let translateServiceMock: Partial<TranslateService> = {
+ translate: jest.fn((str: string) => {})
+ };
+ let component: AddPropertyComponent;
+ let fixture: ComponentFixture<AddPropertyComponent>;
+
+ beforeEach(async(() => {
+ TestBed.configureTestingModule({
+ declarations: [AddPropertyComponent, InputListItemStubComponent],
+ imports: [
+ FormsModule,
+ ReactiveFormsModule,
+ TranslateModule,
+ UiElementsModule
+ ],
+ providers: [
+ {provide: DataTypeService, useValue: dataTypeServiceMock},
+ {provide: TranslateService, useValue: translateServiceMock}
+ ]
+ })
+ .compileComponents();
+ }));
+
+ beforeEach(() => {
+ fixture = TestBed.createComponent(AddPropertyComponent);
+ component = fixture.componentInstance;
+ fixture.detectChanges();
+ });
+
+ it('should create', () => {
+ expect(component).toBeTruthy();
+ });
+});
diff --git a/catalog-ui/src/app/ng2/pages/type-workspace/type-workspace-properties/add-property/add-property.component.ts b/catalog-ui/src/app/ng2/pages/type-workspace/type-workspace-properties/add-property/add-property.component.ts
new file mode 100644
index 0000000000..dc1a0329ed
--- /dev/null
+++ b/catalog-ui/src/app/ng2/pages/type-workspace/type-workspace-properties/add-property/add-property.component.ts
@@ -0,0 +1,233 @@
+/*
+ * -
+ * ============LICENSE_START=======================================================
+ * Copyright (C) 2022 Nordix Foundation.
+ * ================================================================================
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ * ============LICENSE_END=========================================================
+ */
+
+import {Component, EventEmitter, Input, OnDestroy, OnInit, Output} from '@angular/core';
+import {PropertyBEModel} from "../../../../../models/properties-inputs/property-be-model";
+import {AbstractControl, FormControl, FormGroup, ValidationErrors, Validators} from "@angular/forms";
+import {PROPERTY_DATA} from "../../../../../utils/constants";
+import {DataTypeService} from "../../../../services/data-type.service";
+import {DataTypeModel} from "../../../../../models/data-types";
+import {Subscription} from "rxjs";
+import {ToscaTypeHelper} from "../../../../../utils/tosca-type-helper";
+import {SchemaProperty, SchemaPropertyGroupModel} from "../../../../../models/schema-property";
+
+@Component({
+ selector: 'app-add-property',
+ templateUrl: './add-property.component.html',
+ styleUrls: ['./add-property.component.less']
+})
+export class AddPropertyComponent implements OnInit, OnDestroy {
+
+ @Input() property: PropertyBEModel;
+ @Input() readOnly: boolean = true;
+ @Input() model: string;
+
+ @Output() onValidityChange: EventEmitter<PropertyValidationEvent> = new EventEmitter<PropertyValidationEvent>();
+
+ private valueChangesSub: Subscription;
+ private descriptionForm: FormControl = new FormControl(undefined);
+ private requiredForm: FormControl = new FormControl(false, Validators.required);
+ nameForm: FormControl = new FormControl(undefined, [Validators.required]);
+ typeForm: FormControl = new FormControl(undefined, Validators.required);
+ schemaForm: FormControl = new FormControl(undefined, (control: AbstractControl): ValidationErrors | null => {
+ if (this.typeNeedsSchema() && !control.value) {
+ return {required: true};
+ }
+ return null;
+ });
+ hasDefaultValueForm: FormControl = new FormControl(false, Validators.required);
+ defaultValueForm: FormControl = new FormControl(undefined);
+ formGroup: FormGroup = new FormGroup({
+ 'name': this.nameForm,
+ 'description': this.descriptionForm,
+ 'type': this.typeForm,
+ 'required': this.requiredForm,
+ 'schema': this.schemaForm,
+ 'defaultValue': this.defaultValueForm,
+ 'hasDefaultValue': this.hasDefaultValueForm,
+ });
+
+ isLoading: boolean = false;
+ showSchema: boolean = false;
+ typeList: string[];
+ dataTypeMap: Map<string, DataTypeModel>;
+ dataType: DataTypeModel;
+ schemaTypeList: string[];
+
+ constructor(private dataTypeService: DataTypeService) {
+ }
+
+ ngOnInit(): void {
+ this.isLoading = true;
+ this.initTypeAndSchemaDropdown().then(() => this.updateDataType());
+ this.initForm();
+ this.valueChangesSub = this.formGroup.valueChanges.subscribe(() => {
+ this.emitValidityChange();
+ });
+ }
+
+ ngOnDestroy(): void {
+ if (this.valueChangesSub) {
+ this.valueChangesSub.unsubscribe();
+ }
+ }
+
+ onSchemaChange(): void {
+ this.resetDefaultValue();
+ }
+
+ onTypeChange(): void {
+ this.schemaForm.setValue(null);
+ this.showSchema = this.typeNeedsSchema();
+ this.updateDataType();
+ this.resetDefaultValue();
+ }
+
+ private updateDataType(): void {
+ this.dataType = this.dataTypeMap.get(this.typeForm.value);
+ }
+
+ private initForm(): void {
+ if (!this.property) {
+ return;
+ }
+
+ this.nameForm.setValue(this.property.name);
+ this.descriptionForm.setValue(this.property.description);
+ this.typeForm.setValue(this.property.type);
+ this.showSchema = this.typeNeedsSchema();
+ this.requiredForm.setValue(this.property.required);
+ this.schemaForm.setValue(this.property.schemaType);
+ this.initDefaultValueForm();
+ }
+
+ private initDefaultValueForm() {
+ if (this.property.defaultValue == undefined) {
+ return;
+ }
+ let defaultValue;
+ if (!this.isTypeSimple() && typeof this.property.defaultValue === 'string') {
+ defaultValue = JSON.parse(this.property.defaultValue);
+ } else {
+ defaultValue = this.property.defaultValue;
+ }
+ this.defaultValueForm.setValue(defaultValue);
+ this.hasDefaultValueForm.setValue(true);
+ }
+
+ private typeNeedsSchema() {
+ return PROPERTY_DATA.SCHEMA_TYPES.indexOf(this.typeForm.value) > -1;
+ }
+
+ private initTypeAndSchemaDropdown(): Promise<Map<string, DataTypeModel>> {
+ const primitiveTypes: string[] = Array.from(PROPERTY_DATA.TYPES).sort((a, b) => a.localeCompare(b));
+ const promise = this.dataTypeService.findAllDataTypesByModel(this.model);
+ promise.then((dataTypeMap: Map<string, DataTypeModel>) => {
+ this.dataTypeMap = dataTypeMap;
+ const nonPrimitiveTypes: string[] = Array.from(dataTypeMap.keys()).filter(type => {
+ return primitiveTypes.indexOf(type) === -1;
+ });
+ nonPrimitiveTypes.sort((a, b) => a.localeCompare(b));
+ this.typeList = [...primitiveTypes, ...nonPrimitiveTypes];
+ this.schemaTypeList = Array.from(this.typeList);
+ this.isLoading = false;
+ });
+ return promise;
+ }
+
+ private emitValidityChange(): void {
+ const isValid: boolean = this.formGroup.valid;
+ this.onValidityChange.emit({
+ isValid: isValid,
+ property: isValid ? this.buildPropertyFromForm() : undefined
+ });
+ }
+
+ private buildPropertyFromForm(): PropertyBEModel {
+ const property = new PropertyBEModel();
+ property.name = this.nameForm.value;
+ property.type = this.typeForm.value;
+ if (this.schemaForm.value) {
+ property.schemaType = this.schemaForm.value;
+ }
+ property.description = this.descriptionForm.value;
+ if (this.hasDefaultValueForm.value === true) {
+ property.defaultValue = this.defaultValueForm.value;
+ }
+ return property;
+ }
+
+ public isTypeSimple(): boolean {
+ return ToscaTypeHelper.isTypeSimple(this.typeForm.value);
+ }
+
+ public isTypeList(): boolean {
+ return ToscaTypeHelper.isTypeList(this.typeForm.value);
+ }
+
+ public isTypeMap(): boolean {
+ return ToscaTypeHelper.isTypeMap(this.typeForm.value);
+ }
+
+ public isTypeComplex(): boolean {
+ return ToscaTypeHelper.isTypeComplex(this.typeForm.value);
+ }
+
+ private isTypeRange() {
+ return ToscaTypeHelper.isTypeRange(this.typeForm.value);
+ }
+
+ onPropertyValueChange($event: any): void {
+ this.defaultValueForm.setValue($event.value);
+ }
+
+ showDefaultValue(): boolean {
+ if (this.readOnly) {
+ return this.defaultValueForm.value != undefined && this.dataTypeMap && this.typeForm.valid && this.schemaForm.valid;
+ }
+ return this.dataTypeMap && this.typeForm.valid && this.schemaForm.valid;
+ }
+
+ getDataType(type: string): DataTypeModel {
+ return this.dataTypeMap.get(type);
+ }
+
+ private resetDefaultValue(): void {
+ this.defaultValueForm.reset();
+ if (this.isTypeComplex() || this.isTypeMap()) {
+ this.defaultValueForm.setValue({});
+ } else if (this.isTypeList() || this.isTypeRange()) {
+ this.defaultValueForm.setValue([]);
+ }
+ }
+
+ buildSchemaGroupProperty(): SchemaPropertyGroupModel {
+ const schemaProperty = new SchemaProperty();
+ schemaProperty.type = this.schemaForm.value
+ return new SchemaPropertyGroupModel(schemaProperty);
+ }
+
+}
+
+export class PropertyValidationEvent {
+ isValid: boolean;
+ property: PropertyBEModel;
+} \ No newline at end of file
diff --git a/catalog-ui/src/app/ng2/pages/type-workspace/type-workspace-properties/type-workspace-properties.component.html b/catalog-ui/src/app/ng2/pages/type-workspace/type-workspace-properties/type-workspace-properties.component.html
index 61c319eb6f..ec67a02a1b 100644
--- a/catalog-ui/src/app/ng2/pages/type-workspace/type-workspace-properties/type-workspace-properties.component.html
+++ b/catalog-ui/src/app/ng2/pages/type-workspace/type-workspace-properties/type-workspace-properties.component.html
@@ -25,35 +25,36 @@
<input id="search-by-name" type="search" [placeholder]="'SEARCH_LABEL' | translate" [ngModel]="tableFilterTerm" (ngModelChange)="this.tableSearchTermUpdate.next($event)"/>
<span class="sprite magnification-glass search-button"></span>
</div>
+ <div class="add-btn" data-tests-id="add-link" *ngIf="!isViewOnly" (click)="onClickAddProperty()">Add Property</div>
<div class="table-container-flex">
<div class="table" [ngClass]="{'view-mode': isViewOnly}">
<div class="head flex-container">
<div class="table-header head-row hand flex-item" *ngFor="let header of tableHeadersList" (click)="onUpdateSort(header.property)">{{header.title}}
<span *ngIf="tableSortBy === header.property" class="table-header-sort-arrow" [ngClass]="{'down': tableColumnReverse, 'up': !tableColumnReverse}"></span>
</div>
- <div class="table-no-text-header head-row flex-item" *ngIf="!isViewOnly"><span class="delete-col-header"></span></div>
</div>
<div class="body">
- <div *ngIf="filteredProperties.length === 0" class="no-row-text">
- {{'PROPERTY_LIST_EMPTY_MESSAGE' | translate}}
+ <div *ngIf="filteredProperties.length === 0" class="no-row-text">
+ {{'PROPERTY_LIST_EMPTY_MESSAGE' | translate}}
+ </div>
+ <div *ngFor="let property of filteredProperties" [attr.data-tests-id]="'property-row-' + property.name" class="flex-container data-row" (click)="onRowClick(property)">
+ <div class="table-col-general flex-item text" [title]="property.name">
+ <a [attr.data-tests-id]="'property-name-' + property.name" [ngClass]="{'disabled': isViewOnly}">{{property.name}}</a>
</div>
- <div *ngFor="let property of filteredProperties" [attr.data-tests-id]="'property-row-' + property.name" class="flex-container data-row">
- <div class="table-col-general flex-item text" [title]="property.name">
- <a [attr.data-tests-id]="'property-name-' + property.name" [ngClass]="{'disabled': isViewOnly}">{{property.name}}</a>
- </div>
-
- <div class="table-col-general flex-item text" [title]="property.type">
- <span [attr.data-tests-id]="'property-type-' + property.name">{{property.type}}</span>
- </div>
- <div class="table-col-general flex-item text" [title]="property.getSchemaType() || ''">
- <span [attr.data-tests-id]="'property-schema-' + property.name">{{property.getSchemaType() || ''}}</span>
- </div>
- <div class="table-col-general flex-item text" [title]="property.description">
- <span [attr.data-tests-id]="'property-description-' + property.name" [title]="property.description">{{property.description}}</span>
- </div>
- <div class="table-btn-col flex-item" *ngIf="!isViewOnly"></div>
+ <div class="table-col-general flex-item text" [title]="property.type">
+ <span [attr.data-tests-id]="'property-type-' + property.name">{{property.type}}</span>
+ </div>
+ <div class="table-col-general flex-item text" [title]="property.schemaType || ''">
+ <span [attr.data-tests-id]="'property-schema-' + property.name">{{property.schemaType || ''}}</span>
</div>
+ <div class="table-col-general flex-item text" [title]="property.required ? 'Yes' : 'No'">
+ <span [attr.data-tests-id]="'property-required-' + property.name">{{property.required ? 'Yes' : 'No'}}</span>
+ </div>
+ <div class="table-col-general flex-item text" [title]="property.description || ''">
+ <span [attr.data-tests-id]="'property-description-' + property.name" [title]="property.description">{{property.description}}</span>
+ </div>
+ </div>
</div>
</div>
diff --git a/catalog-ui/src/app/ng2/pages/type-workspace/type-workspace-properties/type-workspace-properties.component.less b/catalog-ui/src/app/ng2/pages/type-workspace/type-workspace-properties/type-workspace-properties.component.less
index 9c101e8ce3..fd43bd94a1 100644
--- a/catalog-ui/src/app/ng2/pages/type-workspace/type-workspace-properties/type-workspace-properties.component.less
+++ b/catalog-ui/src/app/ng2/pages/type-workspace/type-workspace-properties/type-workspace-properties.component.less
@@ -29,7 +29,7 @@
#left-top-bar {
float: left;
- width: 186px;
+ width: 200px;
::-webkit-input-placeholder {
font-style: italic;
@@ -56,7 +56,7 @@
-webkit-border-radius: 2px;
-moz-border-radius: 2px;
border-radius: 2px;
- width: 270px;
+ width: 290px;
height: 32px;
line-height: 32px;
border: 1px solid @main_color_o;
@@ -127,11 +127,15 @@
}
.flex-item:nth-child(4) {
+ flex-grow: 3;
+ }
+
+ .flex-item:nth-child(5) {
flex-grow: 20;
white-space: normal;
}
- .flex-item:nth-child(5) {
+ .flex-item:nth-child(6) {
flex-grow: 3;
padding-top: 10px;
}
diff --git a/catalog-ui/src/app/ng2/pages/type-workspace/type-workspace-properties/type-workspace-properties.component.spec.ts b/catalog-ui/src/app/ng2/pages/type-workspace/type-workspace-properties/type-workspace-properties.component.spec.ts
index 6be572d16a..e6e9c12d14 100644
--- a/catalog-ui/src/app/ng2/pages/type-workspace/type-workspace-properties/type-workspace-properties.component.spec.ts
+++ b/catalog-ui/src/app/ng2/pages/type-workspace/type-workspace-properties/type-workspace-properties.component.spec.ts
@@ -30,9 +30,11 @@ import {Observable} from "rxjs/Observable";
import {DataTypeModel} from "../../../../models/data-types";
import {Component, ViewChild} from "@angular/core";
import {PropertyBEModel} from "../../../../models/properties-inputs/property-be-model";
+import {ModalService} from "../../../services/modal.service";
describe('TypeWorkspacePropertiesComponent', () => {
const messages = require("../../../../../assets/languages/en_US.json");
+ let modalService: Partial<ModalService> = {};
let testHostComponent: TestHostComponent;
let testHostFixture: ComponentFixture<TestHostComponent>;
let dataTypeServiceMock: Partial<DataTypeService> = {
@@ -62,7 +64,8 @@ describe('TypeWorkspacePropertiesComponent', () => {
],
providers: [
{provide: DataTypeService, useValue: dataTypeServiceMock},
- {provide: TranslateService, useValue: translateServiceMock}
+ {provide: TranslateService, useValue: translateServiceMock},
+ {provide: ModalService, useValue: modalService}
]
})
.compileComponents();
diff --git a/catalog-ui/src/app/ng2/pages/type-workspace/type-workspace-properties/type-workspace-properties.component.ts b/catalog-ui/src/app/ng2/pages/type-workspace/type-workspace-properties/type-workspace-properties.component.ts
index 931fd3d97c..90bc89ae08 100644
--- a/catalog-ui/src/app/ng2/pages/type-workspace/type-workspace-properties/type-workspace-properties.component.ts
+++ b/catalog-ui/src/app/ng2/pages/type-workspace/type-workspace-properties/type-workspace-properties.component.ts
@@ -23,93 +23,173 @@ import {Component, Input, OnInit} from '@angular/core';
import {DataTypeModel} from "../../../../models/data-types";
import {DataTypeService} from "../../../services/data-type.service";
import {PropertyBEModel} from "../../../../models/properties-inputs/property-be-model";
-import { Subject } from "rxjs";
+import {Subject} from "rxjs";
import {debounceTime, distinctUntilChanged} from "rxjs/operators";
+import {ModalService} from "../../../services/modal.service";
+import {ModalModel} from "../../../../models/modal";
+import {ButtonModel} from "../../../../models/button";
+import {TranslateService} from "../../../shared/translator/translate.service";
+import {AddPropertyComponent, PropertyValidationEvent} from "./add-property/add-property.component";
@Component({
- selector: 'app-type-workspace-properties',
- templateUrl: './type-workspace-properties.component.html',
- styleUrls: ['./type-workspace-properties.component.less']
+ selector: 'app-type-workspace-properties',
+ templateUrl: './type-workspace-properties.component.html',
+ styleUrls: ['./type-workspace-properties.component.less']
})
export class TypeWorkspacePropertiesComponent implements OnInit {
- @Input() isViewOnly = true;
- @Input() dataType: DataTypeModel = new DataTypeModel();
-
- properties: Array<PropertyBEModel> = [];
- filteredProperties: Array<PropertyBEModel> = [];
- tableHeadersList: Array<TableHeader> = [];
- tableSortBy: string = 'name';
- tableColumnReverse: boolean = false;
- tableFilterTerm: string = undefined;
- tableSearchTermUpdate = new Subject<string>();
-
- constructor(private dataTypeService: DataTypeService) { }
-
- ngOnInit(): void {
- this.initTable();
- this.initProperties();
- this.tableSearchTermUpdate.pipe(
- debounceTime(400),
- distinctUntilChanged())
- .subscribe(searchTerm => {
- this.filter(searchTerm);
- });
- }
-
- private initTable(): void {
- this.tableHeadersList = [
- {title: 'Name', property: 'name'},
- {title: 'Type', property: 'type'},
- {title: 'Schema', property: 'schema.property.type'},
- {title: 'Description', property: 'description'},
- ];
-
- this.tableSortBy = this.tableHeadersList[0].property;
- }
-
- private initProperties(): void {
- this.dataTypeService.findAllProperties(this.dataType.uniqueId).subscribe(properties => {
- this.properties = properties.map(value => new PropertyBEModel(value));
- this.filteredProperties = Array.from(this.properties);
- this.sort();
- });
- }
-
- onUpdateSort(property: string): void {
- if (this.tableSortBy === property) {
- this.tableColumnReverse = !this.tableColumnReverse;
- } else {
- this.tableColumnReverse = false;
- this.tableSortBy = property;
+ @Input() isViewOnly = true;
+ @Input() dataType: DataTypeModel = new DataTypeModel();
+
+ properties: Array<PropertyBEModel> = [];
+ filteredProperties: Array<PropertyBEModel> = [];
+ tableHeadersList: Array<TableHeader> = [];
+ tableSortBy: string = 'name';
+ tableColumnReverse: boolean = false;
+ tableFilterTerm: string = undefined;
+ tableSearchTermUpdate = new Subject<string>();
+
+ constructor(private dataTypeService: DataTypeService, private modalService: ModalService, private translateService: TranslateService) {
+ }
+
+ ngOnInit(): void {
+ this.initTable();
+ this.initProperties();
+ this.tableSearchTermUpdate.pipe(
+ debounceTime(400),
+ distinctUntilChanged())
+ .subscribe(searchTerm => {
+ this.filter(searchTerm);
+ });
+ }
+
+ private initTable(): void {
+ this.tableHeadersList = [
+ {title: 'Name', property: 'name'},
+ {title: 'Type', property: 'type'},
+ {title: 'Schema', property: 'schema.property.type'},
+ {title: 'Required', property: 'required'},
+ {title: 'Description', property: 'description'},
+ ];
+
+ this.tableSortBy = this.tableHeadersList[0].property;
}
- this.sort();
- }
-
- private sort(): void {
- const field = this.tableSortBy;
- this.filteredProperties = this.filteredProperties.sort((property1, property2) => {
- const result = property1[field] > property2[field] ? 1 : property1[field] < property2[field] ? -1 : 0;
- return this.tableColumnReverse ? result * -1 : result;
- });
- }
-
- private filter(searchTerm: string): void {
- if (searchTerm) {
- searchTerm = searchTerm.toLowerCase();
- this.filteredProperties = this.properties.filter(property =>
- property.name.toLowerCase().includes(searchTerm)
- || property.type.toLowerCase().includes(searchTerm)
- || (property.getSchemaType() && property.getSchemaType().toLowerCase().includes(searchTerm))
- || (property.description && property.description.toLowerCase().includes(searchTerm))
- );
- } else {
- this.filteredProperties = Array.from(this.properties);
+
+ private initProperties(): void {
+ this.dataTypeService.findAllProperties(this.dataType.uniqueId).subscribe(properties => {
+ this.properties = properties.map(value => {
+ const property = new PropertyBEModel(value);
+ if (property.defaultValue) {
+ property.defaultValue = JSON.parse(property.defaultValue);
+ }
+
+ return property;
+ });
+ this.filteredProperties = Array.from(this.properties);
+ this.sort();
+ });
+ }
+
+ onUpdateSort(property: string): void {
+ if (this.tableSortBy === property) {
+ this.tableColumnReverse = !this.tableColumnReverse;
+ } else {
+ this.tableColumnReverse = false;
+ this.tableSortBy = property;
+ }
+ this.sort();
+ }
+
+ private sort(): void {
+ const field = this.tableSortBy;
+ this.filteredProperties.sort((property1, property2) => {
+ let result = 0;
+ if (property1[field] > property2[field]) {
+ result = 1;
+ } else if (property1[field] < property2[field]) {
+ result = -1;
+ }
+ return this.tableColumnReverse ? result * -1 : result;
+ });
+ }
+
+ private filter(searchTerm?: string): void {
+ if (searchTerm) {
+ searchTerm = searchTerm.toLowerCase();
+ this.filteredProperties = this.properties.filter(property =>
+ property.name.toLowerCase().includes(searchTerm)
+ || property.type.toLowerCase().includes(searchTerm)
+ || (property.getSchemaType() && property.getSchemaType().toLowerCase().includes(searchTerm))
+ || (property.description && property.description.toLowerCase().includes(searchTerm))
+ );
+ } else {
+ this.filteredProperties = Array.from(this.properties);
+ }
+ this.sort();
+ }
+
+ private addProperty(property: PropertyBEModel) {
+ this.properties.push(property);
+ this.filter();
+ }
+
+ onClickAddProperty() {
+ this.openAddPropertyModal();
+ }
+
+ private openAddPropertyModal(property?: PropertyBEModel, readOnly: boolean = false) {
+ const modalTitle = this.translateService.translate('PROPERTY_ADD_MODAL_TITLE');
+ const modalButtons = [];
+ let disableSaveButtonFlag = true;
+ let propertyFromModal: PropertyBEModel = undefined;
+ const modal = this.modalService.createCustomModal(new ModalModel(
+ 'md',
+ modalTitle,
+ null,
+ modalButtons,
+ null
+ ));
+ if (readOnly) {
+ modalButtons.push(new ButtonModel(this.translateService.translate('MODAL_CLOSE'), 'outline grey', () => {
+ this.modalService.closeCurrentModal();
+ }));
+ } else {
+ modalButtons.push(new ButtonModel(this.translateService.translate('MODAL_SAVE'), 'blue',
+ () => {
+ disableSaveButtonFlag = true;
+ this.dataTypeService.createProperty(this.dataType.uniqueId, propertyFromModal).subscribe(property => {
+ this.addProperty(new PropertyBEModel(property));
+ });
+ this.modalService.closeCurrentModal();
+ },
+ (): boolean => {
+ return disableSaveButtonFlag
+ }
+ ));
+
+ modalButtons.push(new ButtonModel(this.translateService.translate('MODAL_CANCEL'), 'outline grey', () => {
+ this.modalService.closeCurrentModal();
+ }));
+ }
+
+ this.modalService.addDynamicContentToModalAndBindInputs(modal, AddPropertyComponent, {
+ 'readOnly': readOnly,
+ 'property': property
+ });
+ modal.instance.dynamicContent.instance.onValidityChange.subscribe((validationEvent: PropertyValidationEvent) => {
+ disableSaveButtonFlag = !validationEvent.isValid;
+ if (validationEvent.isValid) {
+ propertyFromModal = validationEvent.property;
+ }
+ });
+ modal.instance.open();
+ }
+
+ onRowClick(property: PropertyBEModel) {
+ this.openAddPropertyModal(property, true);
}
- this.sort();
- }
}
interface TableHeader {
- title: string;
- property: string;
+ title: string;
+ property: string;
} \ No newline at end of file
diff --git a/catalog-ui/src/app/ng2/pages/type-workspace/type-workspace.component.html b/catalog-ui/src/app/ng2/pages/type-workspace/type-workspace.component.html
index 193c94e6ad..4d29e8673a 100644
--- a/catalog-ui/src/app/ng2/pages/type-workspace/type-workspace.component.html
+++ b/catalog-ui/src/app/ng2/pages/type-workspace/type-workspace.component.html
@@ -44,7 +44,7 @@
</div>
<div class="w-sdc-main-container-body-content" *ngIf="dataType">
<app-type-workspace-general *ngIf="currentMenu.state === 'general'" [dataType]="dataType"></app-type-workspace-general>
- <app-type-workspace-properties *ngIf="currentMenu.state === 'properties'" [dataType]="dataType"></app-type-workspace-properties>
+ <app-type-workspace-properties *ngIf="currentMenu.state === 'properties'" [dataType]="dataType" [isViewOnly]="false"></app-type-workspace-properties>
</div>
</div>
diff --git a/catalog-ui/src/app/ng2/pages/type-workspace/type-workspace.module.ts b/catalog-ui/src/app/ng2/pages/type-workspace/type-workspace.module.ts
index 021f84af09..5b2d3bf030 100644
--- a/catalog-ui/src/app/ng2/pages/type-workspace/type-workspace.module.ts
+++ b/catalog-ui/src/app/ng2/pages/type-workspace/type-workspace.module.ts
@@ -31,7 +31,10 @@ import {UpgradeModule} from "@angular/upgrade/static";
import {FormsModule, ReactiveFormsModule} from "@angular/forms";
import {TranslateModule} from "../../shared/translator/translate.module";
import {DataTypeService} from "../../services/data-type.service";
-import { TypeWorkspacePropertiesComponent } from './type-workspace-properties/type-workspace-properties.component';
+import {TypeWorkspacePropertiesComponent} from './type-workspace-properties/type-workspace-properties.component';
+import {ModalService} from "../../services/modal.service";
+import {AddPropertyComponent} from './type-workspace-properties/add-property/add-property.component';
+import {InterfaceOperationHandlerModule} from "../composition/interface-operatons/operation-creator/interface-operation-handler.module";
@NgModule({
imports: [
@@ -42,19 +45,22 @@ import { TypeWorkspacePropertiesComponent } from './type-workspace-properties/ty
ReactiveFormsModule,
TranslateModule,
FormsModule,
+ InterfaceOperationHandlerModule,
],
declarations: [
TypeWorkspaceComponent,
WorkspaceMenuComponent,
TypeWorkspaceGeneralComponent,
- TypeWorkspacePropertiesComponent
+ TypeWorkspacePropertiesComponent,
+ AddPropertyComponent,
],
providers: [
CacheService,
WorkspaceMenuComponent,
- DataTypeService
+ DataTypeService,
+ ModalService
],
- entryComponents: [TypeWorkspaceComponent],
+ entryComponents: [TypeWorkspaceComponent, AddPropertyComponent],
exports: [TypeWorkspaceComponent]
})
export class TypeWorkspaceModule {
diff --git a/catalog-ui/src/app/ng2/services/data-type.service.ts b/catalog-ui/src/app/ng2/services/data-type.service.ts
index 74d48d35de..298ba90b31 100644
--- a/catalog-ui/src/app/ng2/services/data-type.service.ts
+++ b/catalog-ui/src/app/ng2/services/data-type.service.ts
@@ -83,6 +83,11 @@ export class DataTypeService {
return this.httpClient.get<Array<PropertyBEModel>>(url);
}
+ public createProperty(id: string, property: PropertyBEModel): Observable<PropertyBEModel> {
+ const url = `${this.dataTypeUrl}/${id}/properties`
+ return this.httpClient.post<PropertyBEModel>(url, property);
+ }
+
public getConstraintsByParentTypeAndUniqueID(rootPropertyType, propertyName){
// const property = this.dataTypes[rootPropertyType].properties.filter(property =>
// property.name == propertyName);
diff --git a/catalog-ui/src/app/utils/constants.ts b/catalog-ui/src/app/utils/constants.ts
index 9d11f54db9..c8fb1966b4 100644
--- a/catalog-ui/src/app/utils/constants.ts
+++ b/catalog-ui/src/app/utils/constants.ts
@@ -156,6 +156,7 @@ export class PROPERTY_DATA {
public static TYPES = [PROPERTY_TYPES.STRING, PROPERTY_TYPES.INTEGER, PROPERTY_TYPES.TIMESTAMP, PROPERTY_TYPES.FLOAT, PROPERTY_TYPES.BOOLEAN, PROPERTY_TYPES.JSON, PROPERTY_TYPES.SCALAR, PROPERTY_TYPES.SCALAR_FREQUENCY, PROPERTY_TYPES.SCALAR_SIZE, PROPERTY_TYPES.SCALAR_TIME, PROPERTY_TYPES.LIST, PROPERTY_TYPES.MAP, PROPERTY_TYPES.RANGE];
public static SIMPLE_TYPES = [PROPERTY_TYPES.STRING, PROPERTY_TYPES.INTEGER, PROPERTY_TYPES.TIMESTAMP, PROPERTY_TYPES.FLOAT, PROPERTY_TYPES.BOOLEAN, PROPERTY_TYPES.JSON, PROPERTY_TYPES.SCALAR, PROPERTY_TYPES.SCALAR_FREQUENCY, PROPERTY_TYPES.SCALAR_SIZE, PROPERTY_TYPES.SCALAR_TIME];
public static SIMPLE_TYPES_COMPARABLE = [PROPERTY_TYPES.STRING, PROPERTY_TYPES.INTEGER, PROPERTY_TYPES.FLOAT];
+ public static SCHEMA_TYPES = [PROPERTY_TYPES.LIST, PROPERTY_TYPES.MAP];
public static SCALAR_TYPES = [PROPERTY_TYPES.SCALAR, PROPERTY_TYPES.SCALAR_FREQUENCY, PROPERTY_TYPES.SCALAR_SIZE, PROPERTY_TYPES.SCALAR_TIME];
public static ROOT_DATA_TYPE = "tosca.datatypes.Root";
public static OPENECOMP_ROOT = "org.openecomp.datatypes.Root";
diff --git a/catalog-ui/src/app/utils/tosca-type-helper.ts b/catalog-ui/src/app/utils/tosca-type-helper.ts
new file mode 100644
index 0000000000..7faa90a7fe
--- /dev/null
+++ b/catalog-ui/src/app/utils/tosca-type-helper.ts
@@ -0,0 +1,78 @@
+/*
+ * -
+ * ============LICENSE_START=======================================================
+ * Copyright (C) 2022 Nordix Foundation.
+ * ================================================================================
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ * ============LICENSE_END=========================================================
+ */
+
+import {DerivedPropertyType} from "../models/properties-inputs/property-be-model";
+import {PROPERTY_DATA, PROPERTY_TYPES} from "./constants";
+
+export class ToscaTypeHelper {
+
+ private ToscaTypeHelper() {
+ //not designed to be instantiated
+ }
+
+ public static getType(typeName: string): DerivedPropertyType {
+ if (PROPERTY_DATA.SIMPLE_TYPES.indexOf(typeName) > -1) {
+ return DerivedPropertyType.SIMPLE;
+ } else if (typeName === PROPERTY_TYPES.LIST) {
+ return DerivedPropertyType.LIST;
+ } else if (typeName === PROPERTY_TYPES.MAP) {
+ return DerivedPropertyType.MAP;
+ } else if (typeName === PROPERTY_TYPES.RANGE) {
+ return DerivedPropertyType.RANGE;
+ } else {
+ return DerivedPropertyType.COMPLEX;
+ }
+ }
+
+ public static isTypeSimple(typeName: string): boolean {
+ return this.getType(typeName) == DerivedPropertyType.SIMPLE;
+ }
+
+ public static isTypeList(typeName: string): boolean {
+ return this.getType(typeName) == DerivedPropertyType.LIST;
+ }
+
+ public static isTypeMap(typeName: string): boolean {
+ return this.getType(typeName) == DerivedPropertyType.MAP;
+ }
+
+ public static isTypeComplex(typeName: string): boolean {
+ return !this.isTypeSimple(typeName) && !this.isTypeList(typeName) && !this.isTypeMap(typeName);
+ }
+
+ public static isTypeNumber(typeName: string): boolean {
+ return typeName === PROPERTY_TYPES.INTEGER || typeName === PROPERTY_TYPES.FLOAT;
+ }
+
+ public static isTypeBoolean(typeName: string): boolean {
+ return typeName === PROPERTY_TYPES.BOOLEAN;
+ }
+
+ public static isTypeLiteral(typeName: string): boolean {
+ return !this.isTypeNumber(typeName) && !this.isTypeBoolean(typeName) && !this.isTypeList(typeName) && !this.isTypeMap(typeName)
+ && !this.isTypeComplex(typeName) && !this.isTypeRange(typeName);
+ }
+
+ public static isTypeRange(typeName: string): boolean {
+ return typeName === PROPERTY_TYPES.RANGE;
+ }
+
+}
diff --git a/catalog-ui/src/assets/languages/en_US.json b/catalog-ui/src/assets/languages/en_US.json
index fc5445a6c3..5dec922980 100644
--- a/catalog-ui/src/assets/languages/en_US.json
+++ b/catalog-ui/src/assets/languages/en_US.json
@@ -41,6 +41,10 @@
"GENERAL_LABEL_SYSTEM_NAME": "System Name:",
"GENERAL_LABEL_SOURCE_SERVICE_NAME": "Source Service Name:",
"GENERAL_LABEL_RESOURCE_CUSTOMIZATION_UUID": "Resource Customization UUID:",
+ "GENERAL_LABEL_EMPTY": "Empty",
+ "GENERAL_LABEL_TRUE": "True",
+ "GENERAL_LABEL_FALSE": "False",
+ "GENERAL_LABEL_SELECT": "Select",
"SEARCH_LABEL": "Search",
"=========== GENERAL_TAB ===========": "",
"GENERAL_TAB_LABEL_RESOURCE_MODEL_NUMBER": "Vendor Model Number",
@@ -506,6 +510,7 @@
"MODAL_SAVE": "Save",
"MODAL_CREATE": "Create",
"MODAL_CANCEL": "Cancel",
+ "MODAL_CLOSE": "Close",
"MODAL_DELETE": "Delete",
"=========== POLICY AND GROUPS ===========": "",
"ADD_TARGETS": "Add Targets",
@@ -582,5 +587,14 @@
"IMPORT_FAILURE_MESSAGE_TEXT": "Import Failure - error reading CSAR",
"=========== PROPERTIES ===========": "",
"PROPERTY_LIST_EMPTY_MESSAGE": "There are no properties to display",
- "PROPERTY_SHOWING_LABEL": "Showing Properties"
+ "PROPERTY_SHOWING_LABEL": "Showing Properties",
+ "PROPERTY_ADD_MODAL_TITLE": "Add Property",
+ "PROPERTY_VIEW_MODAL_TITLE": "View Property",
+ "PROPERTY_DESCRIPTION_LABEL": "Description",
+ "PROPERTY_DEFAULT_VALUE_LABEL": "Default Value",
+ "PROPERTY_SCHEMA_LABEL": "Schema",
+ "PROPERTY_REQUIRED_LABEL": "Required",
+ "PROPERTY_TYPE_LABEL": "Type",
+ "PROPERTY_SET_DEFAULT_VALUE_MSG": "Set Default Value?",
+ "PROPERTY_NAME_LABEL": "Name"
}