Enable selection of base type of service

Signed-off-by: MichaelMorris <michael.morris@est.tech>
Issue-ID: SDC-3506
Change-Id: Iaba39955fac9056cb0d0f1eccd223c05dfb9c5b4
Signed-off-by: Vasyl Razinkov <vasyl.razinkov@est.tech>
diff --git a/catalog-ui/src/app/models/base-types.ts b/catalog-ui/src/app/models/base-types.ts
new file mode 100644
index 0000000..ac5f842
--- /dev/null
+++ b/catalog-ui/src/app/models/base-types.ts
@@ -0,0 +1,28 @@
+/*
+* ============LICENSE_START=======================================================
+*  Copyright (C) 2021 Nordix Foundation. All rights reserved.
+*  ================================================================================
+*  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=========================================================
+*/
+
+
+interface ListBaseTypesResponse {
+  baseTypes: BaseTypeResponse[];
+}
+
+interface BaseTypeResponse {
+	toscaResourceName:string;
+    versions:string[];
+}
diff --git a/catalog-ui/src/app/models/components/component.ts b/catalog-ui/src/app/models/components/component.ts
index f787142..be92f76 100644
--- a/catalog-ui/src/app/models/components/component.ts
+++ b/catalog-ui/src/app/models/components/component.ts
@@ -144,6 +144,8 @@
     public vspArchived: boolean;
     public componentMetadata: ComponentMetadata;
     public categorySpecificMetadata: Metadata = new Metadata();
+    public derivedFromGenericType: string;
+    public derivedFromGenericVersion: string;
 
     constructor(componentService:IComponentService, protected $q:ng.IQService, component?:Component) {
         if (component) {
@@ -205,6 +207,9 @@
                 this.copyCategoryMetadata(component);
                 this.copySubcategoryMetadata(component);
             }
+
+            this.derivedFromGenericType = component.derivedFromGenericType;
+            this.derivedFromGenericVersion = component.derivedFromGenericVersion;
         }
 
         //custom properties
diff --git a/catalog-ui/src/app/modules/service-module.ts b/catalog-ui/src/app/modules/service-module.ts
index e2f4f16..38cd272 100644
--- a/catalog-ui/src/app/modules/service-module.ts
+++ b/catalog-ui/src/app/modules/service-module.ts
@@ -43,6 +43,7 @@
 import { HomeService } from '../ng2/services/home.service';
 import { ModalService } from '../ng2/services/modal.service';
 import { OnboardingService } from '../ng2/services/onboarding.service';
+import { ElementService } from '../ng2/services/element.service';
 import { PluginsService } from '../ng2/services/plugins.service';
 import { PoliciesService as PoliciesServiceNg2 } from '../ng2/services/policies.service';
 import { SharingService } from '../ng2/services/sharing.service';
@@ -126,4 +127,5 @@
 serviceModule.factory('ReqAndCapabilitiesService', downgradeInjectable(ReqAndCapabilitiesService));
 serviceModule.factory('NodesFactory', downgradeInjectable(NodesFactory));
 serviceModule.service('OnboardingService', downgradeInjectable(OnboardingService));
+serviceModule.service('ElementService', downgradeInjectable(ElementService));
 serviceModule.service('ImportVSPService', downgradeInjectable(ImportVSPService));
diff --git a/catalog-ui/src/app/ng2/app.module.ts b/catalog-ui/src/app/ng2/app.module.ts
index e8dde94..55dd969 100644
--- a/catalog-ui/src/app/ng2/app.module.ts
+++ b/catalog-ui/src/app/ng2/app.module.ts
@@ -98,6 +98,7 @@
 import {CapabilitiesFilterPropertiesEditorComponentModule} from "./pages/composition/capabilities-filter-properties-editor/capabilities-filter-properties-editor.module";
 import {InterfaceOperationHandlerModule} from "./pages/composition/interface-operatons/operation-creator/interface-operation-handler.module";
 import {AttributesOutputsModule} from "./pages/attributes-outputs/attributes-outputs.module";
+import { ElementService } from "./services/element.service";
 
 
 declare const __ENV__: string;
@@ -195,6 +196,7 @@
     ModalService,
     ImportVSPService,
     OnboardingService,
+    ElementService,
     ServiceServiceNg2,
     AutomatedUpgradeService,
     WorkflowServiceNg2,
diff --git a/catalog-ui/src/app/ng2/services/element.service.ts b/catalog-ui/src/app/ng2/services/element.service.ts
new file mode 100644
index 0000000..97efbcc
--- /dev/null
+++ b/catalog-ui/src/app/ng2/services/element.service.ts
@@ -0,0 +1,42 @@
+/*
+* ============LICENSE_START=======================================================
+*  Copyright (C) 2021 Nordix Foundation. All rights reserved.
+*  ================================================================================
+*  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 { HttpClient } from '@angular/common/http';
+import { Inject, Injectable } from '@angular/core';
+import { Response } from '@angular/http';
+import { Observable } from 'rxjs/Observable';
+import { ISdcConfig, SdcConfigToken } from '../config/sdc-config.config';
+import {map} from "rxjs/operators";
+
+@Injectable()
+export class ElementService {
+
+    protected baseUrl;
+
+    constructor(protected http: HttpClient, @Inject(SdcConfigToken) sdcConfig: ISdcConfig) {
+        this.baseUrl = sdcConfig.api.root;
+    }
+
+    getCategoryBasetypes(categoryName:string):Observable<BaseTypeResponse[]> {
+        return this.http.get<ListBaseTypesResponse>(this.baseUrl + "/v1/category/services/" + categoryName + "/baseTypes")
+            .pipe(map(response => response.baseTypes));
+    }
+
+}
+
diff --git a/catalog-ui/src/app/ng2/services/responses/component-generic-response.ts b/catalog-ui/src/app/ng2/services/responses/component-generic-response.ts
index 09ace56..784a3d0 100644
--- a/catalog-ui/src/app/ng2/services/responses/component-generic-response.ts
+++ b/catalog-ui/src/app/ng2/services/responses/component-generic-response.ts
@@ -59,6 +59,8 @@
     public derivedList:Array<any>;
     public nodeFilterforNode: Array<any>;
     public substitutionFilterForTopologyTemplate: Array<any>;
+    public derivedFromGenericType;
+    public derivedFromGenericVersion;
 
     deserialize (response): ComponentGenericResponse {
 
@@ -130,6 +132,12 @@
         if(response.substitutionFilterForTopologyTemplate) {
             this.substitutionFilterForTopologyTemplate = response.substitutionFilterForTopologyTemplate;
         }
+        if(response.derivedFromGenericType) {
+            this.derivedFromGenericType = response.derivedFromGenericType;
+        }
+        if(response.derivedFromGenericVersion) {
+            this.derivedFromGenericVersion = response.derivedFromGenericVersion;
+        }
         return this;
     }
 }
diff --git a/catalog-ui/src/app/services-ng2.ts b/catalog-ui/src/app/services-ng2.ts
index e2811ba..119f2e3 100644
--- a/catalog-ui/src/app/services-ng2.ts
+++ b/catalog-ui/src/app/services-ng2.ts
@@ -16,6 +16,7 @@
 export * from './ng2/services/dynamic-component.service';
 export * from './ng2/services/event-bus.service';
 export * from './ng2/services/groups.service';
+export * from './ng2/services/element.service';
 
 export * from './ng2/services/component-services/component.service';
 export * from './ng2/services/component-services/component.service.factory';
diff --git a/catalog-ui/src/app/utils/component-factory.ts b/catalog-ui/src/app/utils/component-factory.ts
index fd82c27..667c6bc 100644
--- a/catalog-ui/src/app/utils/component-factory.ts
+++ b/catalog-ui/src/app/utils/component-factory.ts
@@ -197,6 +197,8 @@
         component.setUniqueId(componentId);
         this.ComponentServiceNg2.getComponentMetadata(component.uniqueId, component.componentType).subscribe((response:ComponentGenericResponse) => {
             component.setComponentMetadata(response.metadata);
+            component.derivedFromGenericType = response.derivedFromGenericType;
+            component.derivedFromGenericVersion = response.derivedFromGenericVersion;
             deferred.resolve(component);
         });
         return deferred.promise;
diff --git a/catalog-ui/src/app/view-models/workspace/tabs/general/general-view-model.ts b/catalog-ui/src/app/view-models/workspace/tabs/general/general-view-model.ts
index 1065404..2876544 100644
--- a/catalog-ui/src/app/view-models/workspace/tabs/general/general-view-model.ts
+++ b/catalog-ui/src/app/view-models/workspace/tabs/general/general-view-model.ts
@@ -23,7 +23,7 @@
 import {ModalsHandler, ValidationUtils, EVENTS, CHANGE_COMPONENT_CSAR_VERSION_FLAG, ComponentType, DEFAULT_ICON,
     ResourceType, ComponentState, instantiationType, ComponentFactory} from "app/utils";
 import { EventListenerService, ProgressService} from "app/services";
-import {CacheService, OnboardingService, ImportVSPService} from "app/services-ng2";
+import {CacheService, OnboardingService, ImportVSPService, ElementService} from "app/services-ng2";
 import {IAppConfigurtaion, IValidate, IMainCategory, Resource, ISubCategory,Service, ICsarComponent, Component, IMetadataKey} from "app/models";
 import {IWorkspaceViewModelScope} from "app/view-models/workspace/workspace-view-model";
 import {Dictionary} from "lodash";
@@ -79,10 +79,12 @@
     convertCategoryStringToOneArray(category:string, subcategory:string):Array<IMainCategory>;
     onCategoryChange():void;
     onEcompGeneratedNamingChange():void;
+    onBaseTypeChange():void;
     openOnBoardingModal():void;
     initCategoreis():void;
     initEnvironmentContext():void;
     initInstantiationTypes():void;
+    initBaseTypes():void;
     onInstantiationTypeChange():void;
     updateIcon():void;
     possibleToUpdateIcon():boolean;
@@ -114,6 +116,7 @@
         'OnboardingService',
         'ComponentFactory',
         'ImportVSPService',
+        'ElementService',
         '$stateParams'
     ];
 
@@ -139,6 +142,7 @@
                 private onBoardingService: OnboardingService,
                 private ComponentFactory:ComponentFactory,
                 private importVSPService: ImportVSPService,
+                private elementService: ElementService,
                 private $stateParams: any) {
 
         this.initScopeValidation();
@@ -225,6 +229,7 @@
         this.$scope.componentCategories = new componentCategories();
         this.$scope.componentCategories.selectedCategory = this.$scope.component.selectedCategory;
 
+
         // Init UIModel
         this.$scope.component.tags = _.without(this.$scope.component.tags, this.$scope.component.name);
 
@@ -257,6 +262,7 @@
             }
             // Init Instantiation types
             this.$scope.initInstantiationTypes();
+            this.$scope.initBaseTypes();
         }
 
         if (this.cacheService.get(PREVIOUS_CSAR_COMPONENT)) { //keep the old component in the cache until checkout, so we dont need to pass it around
@@ -434,6 +440,20 @@
             }
         };
 
+        this.$scope.initBaseTypes = ():void => {
+            if (this.$scope.componentType === ComponentType.SERVICE && this.$scope.component && this.$scope.component.categories) {
+                 this.elementService.getCategoryBasetypes(this.$scope.component.categories[0].name).subscribe((data: BaseTypeResponse[]) => {
+	                 this.$scope.baseTypes = []
+                     this.$scope.baseTypeVersions = []
+                     data.forEach(baseType => {
+	                     this.$scope.baseTypes.push(baseType.toscaResourceName)
+                         if (baseType.toscaResourceName === this.$scope.component.derivedFromGenericType){
+	                         baseType.versions.reverse().forEach(version => this.$scope.baseTypeVersions.push(version));
+                     }});
+                 })
+            }
+        };
+
         this.$scope.initEnvironmentContext = ():void => {
             if (this.$scope.componentType === ComponentType.SERVICE) {
                 this.$scope.environmentContextObj = this.cacheService.get('UIConfiguration').environmentContext;
@@ -645,6 +665,25 @@
                    }
                 }
             }
+            if (this.$scope.componentType === ComponentType.SERVICE && this.$scope.component.categories[0]) {
+	            this.elementService.getCategoryBasetypes(this.$scope.component.categories[0].name).subscribe((data: BaseTypeResponse[]) => {
+		
+                    if(this.$scope.isCreateMode()){
+                        this.$scope.baseTypes = []
+                        this.$scope.baseTypeVersions = []
+                        data.forEach(baseType => this.$scope.baseTypes.push(baseType.toscaResourceName));
+                        data[0].versions.reverse().forEach(version => this.$scope.baseTypeVersions.push(version));
+                        this.$scope.component.derivedFromGenericType = data[0].toscaResourceName;
+                        this.$scope.component.derivedFromGenericVersion = data[0].versions[0];
+                    } else {
+                        var isValidForBaseType:boolean = false;
+                        data.forEach(baseType => {if (!this.$scope.component.derivedFromGenericType || baseType.toscaResourceName === this.$scope.component.derivedFromGenericType){
+                            isValidForBaseType = true;
+                        };});
+                        this.$scope.editForm['category'].$setValidity('validForBaseType', isValidForBaseType);
+                    }
+                });
+            }   
         };
 
         this.$scope.onEcompGeneratedNamingChange = (): void => {
@@ -653,6 +692,18 @@
             }
         };
 
+        this.$scope.onBaseTypeChange = (): void => {
+            this.elementService.getCategoryBasetypes(this.$scope.component.categories[0].name).subscribe((data: BaseTypeResponse[]) => {
+                     this.$scope.baseTypeVersions = []
+                     data.forEach(baseType => {
+	                     if(baseType.toscaResourceName === this.$scope.component.derivedFromGenericType) {
+		                     baseType.versions.reverse().forEach(version => this.$scope.baseTypeVersions.push(version));
+                             this.$scope.component.derivedFromGenericVersion = baseType.versions[0];
+	                     };
+                     });
+             })
+        };
+
         this.$scope.onVendorNameChange = (oldVendorName: string): void => {
             if (this.$scope.component.icon === oldVendorName) {
                 this.$scope.component.icon = DEFAULT_ICON;
diff --git a/catalog-ui/src/app/view-models/workspace/tabs/general/general-view.html b/catalog-ui/src/app/view-models/workspace/tabs/general/general-view.html
index 40300c8..d598e17 100644
--- a/catalog-ui/src/app/view-models/workspace/tabs/general/general-view.html
+++ b/catalog-ui/src/app/view-models/workspace/tabs/general/general-view.html
@@ -109,6 +109,7 @@
 
                                 <div class="input-error" data-ng-show="validateField(editForm.category)">
                                     <span ng-show="editForm.category.$error.required" translate="NEW_SERVICE_RESOURCE_ERROR_CATEGORY_REQUIRED"></span>
+                                    <span ng-show="editForm.category.$error.validForBaseType" translate="NEW_SERVICE_RESOURCE_ERROR_CATEGORY_NOT_VALID"></span>
                                 </div>
                             </div>
                             <!--------------------- CATEGORIES -------------------->
@@ -574,6 +575,40 @@
 
                     <!--------------------- Instantiation Type -------------------->
 
+                    <!--------------------- Base Type -------------------->
+
+                    <div class="w-sdc-form-columns-wrapper">
+                        <div class="w-sdc-form-column">
+		                    <div class="i-sdc-form-item" data-ng-if="component.isService()">
+		                        <label class="i-sdc-form-label">Base Type</label>
+		                        <select class="i-sdc-form-select"
+		                                name="baseType"
+		                                data-ng-class="{'view-mode': isViewMode()}"
+		                                data-ng-disabled="component.isCsarComponent() || !isCreateMode()"
+		                                data-ng-model="component.derivedFromGenericType"
+		                                data-ng-change="onBaseTypeChange()"
+		                                data-tests-id="selectBaseType">
+		                            <option ng-repeat="type in baseTypes">{{type}}</option>
+		                        </select>
+		                    </div>
+                		</div>
+                		<div class="w-sdc-form-column">
+		                    <div class="i-sdc-form-item" data-ng-if="component.isService()">
+		                        <label class="i-sdc-form-label">Base Type Version</label>
+		                        <select class="i-sdc-form-select"
+		                                name="baseTypeVersion"
+		                                data-ng-class="{'view-mode': isViewMode()}"
+		                                data-ng-disabled="component.isCsarComponent() || !isCreateMode()"
+		                                data-ng-model="component.derivedFromGenericVersion"
+		                                data-tests-id="selectBaseTypeVersion">
+		                            <option ng-repeat="version in baseTypeVersions">{{version}}</option>
+		                        </select>
+		                    </div>
+                		</div>
+                	</div>
+
+                    <!--------------------- Instantiation Type -------------------->
+
                     <div class="meta-data" data-ng-if="component.creationDate">
                         <div>
                             <b>Created:</b>
diff --git a/catalog-ui/src/assets/languages/en_US.json b/catalog-ui/src/assets/languages/en_US.json
index 7cc6d55..df869c3 100644
--- a/catalog-ui/src/assets/languages/en_US.json
+++ b/catalog-ui/src/assets/languages/en_US.json
@@ -217,6 +217,7 @@
     "NEW_SERVICE_RESOURCE_ERROR_NAME_EXISTS": "Name already exists.",
     "NEW_SERVICE_RESOURCE_ERROR_SPECIAL_CHARS": "Special characters not allowed.",
     "NEW_SERVICE_RESOURCE_ERROR_CATEGORY_REQUIRED": "category is required.",
+    "NEW_SERVICE_RESOURCE_ERROR_CATEGORY_NOT_VALID": "Category not valid for base type.",
     "NEW_SERVICE_RESOURCE_ERROR_CONTACT_REQUIRED": "Contact is required.",
     "NEW_SERVICE_RESOURCE_ERROR_CONTACT_NOT_VALID": "Contact is not valid.",
     "NEW_SERVICE_RESOURCE_ERROR_SERVICE_DESCRIPTION_REQUIRED": "Service description is required.",