UI support for service update via tosca template import 30/135830/3
authorJvD_Ericsson <jeff.van.dam@est.tech>
Fri, 25 Aug 2023 12:46:04 +0000 (13:46 +0100)
committerVasyl Razinkov <vasyl.razinkov@est.tech>
Wed, 30 Aug 2023 10:37:05 +0000 (10:37 +0000)
Issue-ID: SDC-4604
Signed-off-by: JvD_Ericsson <jeff.van.dam@est.tech>
Change-Id: I68f7640a254d0636cb967c15d51522b8c8a7abfc

catalog-ui/src/app/ng2/components/ui/ui-elements.module.ts
catalog-ui/src/app/ng2/components/ui/upload-artifact/upload-artifact.component.ts [new file with mode: 0644]
catalog-ui/src/app/ng2/pages/workspace/tosca-artifacts/__snapshots__/tosca-artifact-page.spec.ts.snap
catalog-ui/src/app/ng2/pages/workspace/tosca-artifacts/tosca-artifact-page.component.html
catalog-ui/src/app/ng2/pages/workspace/tosca-artifacts/tosca-artifact-page.component.ts
catalog-ui/src/app/ng2/pages/workspace/tosca-artifacts/tosca-artifact-page.spec.ts
catalog-ui/src/app/ng2/services/component-services/topology-template.service.ts

index 8e0a66c..3209b0f 100644 (file)
@@ -42,6 +42,7 @@ import {SdcTileModule} from "./tile/sdc-tile.module";
 import {PerfectScrollbarDirective} from "./perfect-scroll-bar/perfect-scrollbar.directive";
 import {FileOpenerComponent} from "./file-opener/file-opener.component";
 import {DownloadArtifactComponent} from "app/ng2/components/ui/download-artifact/download-artifact.component";
+import {UploadArtifactComponent} from "app/ng2/components/ui/upload-artifact/upload-artifact.component";
 import {SdcElementIconComponent} from "./sdc-element-icon/sdc-element-icon.component";
 import {PanelWrapperComponent} from "./panel-wrapper/panel-wrapper.component";
 import { ModalImportTypeComponent } from './modal-import-type/modal-import-type.component';
@@ -57,6 +58,7 @@ import { ModalImportTypeComponent } from './modal-import-type/modal-import-type.
         FileOpenerComponent,
         SdcElementIconComponent,
         DownloadArtifactComponent,
+        UploadArtifactComponent,
         PanelWrapperComponent,
         ModalImportTypeComponent
     ],
@@ -99,6 +101,7 @@ import { ModalImportTypeComponent } from './modal-import-type/modal-import-type.
         SdcElementIconComponent,
         FileOpenerComponent,
         DownloadArtifactComponent,
+        UploadArtifactComponent,
         PanelWrapperComponent,
         ModalImportTypeComponent
     ],
diff --git a/catalog-ui/src/app/ng2/components/ui/upload-artifact/upload-artifact.component.ts b/catalog-ui/src/app/ng2/components/ui/upload-artifact/upload-artifact.component.ts
new file mode 100644 (file)
index 0000000..ece47e7
--- /dev/null
@@ -0,0 +1,92 @@
+/*
+ * ============LICENSE_START=======================================================
+ *  Copyright (C) 2023 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, Output } from "@angular/core";
+import { ArtifactModel } from "app/models";
+import { TranslateService } from '../../../shared/translator/translate.service';
+import { SdcUiServices } from 'onap-ui-angular';
+
+@Component({
+    selector: 'upload-artifact',
+    template: `
+       <svg-icon
+        [mode]="'primary2'"
+        [disabled]="disabled"
+        [clickable]="!disabled"
+        [name]="iconType"
+        [testId]="testId" mode="info"
+        (click)="fileUpload.click()"
+        clickable="true"
+        size="medium"
+    ></svg-icon>
+    <input
+        type="file"
+        style="display: none;"
+        [disabled]="disabled"
+        (change)="onFileSelect($event)"
+        [accept]="extensionsWithDot"
+        #fileUpload>
+`
+})
+export class UploadArtifactComponent {
+
+    @Input() extensions: string;
+    @Input() artifact: ArtifactModel;
+    @Input() isInstance: boolean;
+    @Input() uploadIconClass: string;
+    @Input() componentType: string;
+    @Input() componentId: string;
+    @Input() testId: string;
+    @Input() disabled: boolean;
+    @Output("onFileUpload") onFileUpload: EventEmitter<any> = new EventEmitter<any>();
+
+    public extensionsWithDot: string;
+    public iconType:string = "upload-o";
+
+    constructor(
+        private modalService: SdcUiServices.ModalService,
+        private translateService: TranslateService) {
+
+    }
+
+    ngOnInit () {
+        this.extensionsWithDot = this.getExtensionsWithDot(this.extensions);
+    }
+
+    public getExtensionsWithDot(extensions:string):string {
+        extensions = extensions || '';
+        return extensions.split(',')
+            .map(ext => '.' + ext.toString())
+            .join(',');
+    }
+
+    public onFileSelect(event) {
+        const file = event.target.files[0];
+        if (file && file.name) {
+            const fileExtension: string = file.name.split('.').pop();
+            if (this.extensionsWithDot.includes(fileExtension.toLowerCase())) {
+                this.onFileUpload.emit(file);
+            } else {
+                const title: string = this.translateService.translate('NEW_SERVICE_RESOURCE_ERROR_VALID_TOSCA_EXTENSIONS_TITLE');
+                const message: string = this.translateService.translate('NEW_SERVICE_RESOURCE_ERROR_VALID_TOSCA_EXTENSIONS', {extensions: this.extensionsWithDot});
+                this.modalService.openWarningModal(title, message, 'error-invalid-tosca-ext');
+            }
+        }
+    }
+}
index 14146d5..695e16d 100644 (file)
@@ -2,7 +2,9 @@
 
 exports[`tosca artifacts page should match current snapshot of tosca artifact pages component 1`] = `
 <tosca-artifact-page
-  serviceLoader={[Function Object]}
+  Notification="undefined"
+  componentService={[Function Object]}
+  isLoading="false"
   store={[Function Store]}
   table={[Function DatatableComponent]}
   workspaceService={[Function Object]}
@@ -10,6 +12,7 @@ exports[`tosca artifacts page should match current snapshot of tosca artifact pa
   <div
     class="tosca-artifact-page"
   >
+    <loader />
     <ngx-datatable
       class="ngx-datatable"
       columnmode="flex"
index fece92e..fe55064 100644 (file)
@@ -1,4 +1,5 @@
 <div class="tosca-artifact-page">
+    <loader [display]="isLoading" [relative]="true"></loader>
     <ngx-datatable
             columnMode="flex"
             [headerHeight]="40"
@@ -27,7 +28,7 @@
                 </div>
             </ng-template>
         </ngx-datatable-column>
-        <ngx-datatable-column [resizeable]="false"name="Type" [flexGrow]="3">
+        <ngx-datatable-column [resizeable]="false" name="Type" [flexGrow]="3">
             <ng-template ngx-datatable-cell-template let-row="row">
                 {{row.artifactType}}
             </ng-template>
                 {{ row.artifactVersion }}
             </ng-template>
         </ngx-datatable-column>
-        <ngx-datatable-column [resizeable]="false"[flexGrow]="1">
+        <ngx-datatable-column *ngIf="isService() && isCheckedOut()" [resizeable]="false" name="Upload" [flexGrow]="1">
+            <ng-template ngx-datatable-cell-template let-row="row">
+                <div class="download-artifact-button">
+                    <upload-artifact
+                        [artifact]="row"
+                        [componentId]="componentId"
+                        [componentType]="componentType"
+                        [disabled]="!isCheckedOut()"
+                        [extensions]="getExtension(row.artifactType)"
+                        (onFileUpload)="onFileUpload($event, row.artifactType)"
+                        testId="upload_{{row.artifactDisplayName}}"
+                    ></upload-artifact>
+                </div>
+            </ng-template>
+        </ngx-datatable-column>
+        <ngx-datatable-column [resizeable]="false" name="Download" [flexGrow]="1">
             <ng-template ngx-datatable-cell-template let-row="row">
                 <div class="download-artifact-button">
                     <download-artifact [artifact]="row" [componentId]="componentId"
index ef4112c..e6d820e 100644 (file)
@@ -1,14 +1,15 @@
-import {Component, OnInit, ViewChild} from "@angular/core";
-import {WorkspaceService} from "../workspace.service";
-import {SdcUiServices} from "onap-ui-angular";
-import {ArtifactModel} from "../../../../models";
-import {Select, Store} from "@ngxs/store";
-import {WorkspaceState} from "../../../store/states/workspace.state";
-import {ArtifactGroupType} from "../../../../utils";
-import {GetArtifactsByTypeAction} from "../../../store/actions/artifacts.action";
-import {Observable} from "rxjs/index";
-import {ArtifactsState} from "../../../store/states/artifacts.state";
-import {map} from "rxjs/operators";
+import { Component, Inject, OnInit, ViewChild } from "@angular/core";
+import { WorkspaceService } from "../workspace.service";
+import { ArtifactModel } from "../../../../models";
+import { Select, Store } from "@ngxs/store";
+import { WorkspaceState } from "../../../store/states/workspace.state";
+import { ArtifactGroupType } from "../../../../utils";
+import { GetArtifactsByTypeAction } from "../../../store/actions/artifacts.action";
+import { Observable } from "rxjs/index";
+import { ArtifactsState } from "../../../store/states/artifacts.state";
+import { map } from "rxjs/operators";
+import { ArtifactType, ComponentState, ComponentType } from "app/utils/constants"
+import { TopologyTemplateService } from "app/ng2/services/component-services/topology-template.service";
 
 @Component({
     selector: 'tosca-artifact-page',
@@ -23,8 +24,13 @@ export class ToscaArtifactPageComponent implements OnInit {
     public toscaArtifacts$: Observable<ArtifactModel[]>;
     public componentId: string;
     public componentType:string;
+    public isLoading: boolean = false;
 
-    constructor(private serviceLoader: SdcUiServices.LoaderService, private workspaceService: WorkspaceService, private store: Store) {
+    constructor(
+        private workspaceService: WorkspaceService,
+        private store: Store,
+        @Inject("Notification") private Notification: any,
+        private componentService: TopologyTemplateService) {
     }
 
 
@@ -41,4 +47,47 @@ export class ToscaArtifactPageComponent implements OnInit {
             this.table.rowDetail.toggleExpandRow(event.row);
         }
     }
+
+    getExtension(artifactType: string) {
+        switch (artifactType) {
+            case (ArtifactType.TOSCA.TOSCA_CSAR):
+                return "csar";
+            case (ArtifactType.TOSCA.TOSCA_TEMPLATE):
+                return "yaml,yml";
+        }
+    }
+
+    isService() {
+        return ComponentType.SERVICE === this.componentType;
+    }
+
+    isCheckedOut() {
+        return this.workspaceService.metadata.lifecycleState === ComponentState.NOT_CERTIFIED_CHECKOUT;
+    }
+
+    onFileUpload(file, artifactType) {
+        if (file && file.name) {
+            this.isLoading = true;
+            switch (artifactType) {
+                case (ArtifactType.TOSCA.TOSCA_CSAR):
+                    this.Notification.error({
+                        message: "Feature not implemented yet",
+                        title: "Error"
+                    });
+                    this.isLoading = false;
+                    break;
+                case (ArtifactType.TOSCA.TOSCA_TEMPLATE):
+                    this.componentService.putServiceToscaTemplate(this.componentId, this.componentType, file).subscribe((response)=> {
+                        this.Notification.success({
+                            message: "Service " + response.name + " has been updated",
+                            title: "Success"
+                        });
+                        this.isLoading = false;
+                    }, () => {
+                        this.isLoading = false;
+                    });
+                    break;
+            }
+        }
+    }
 }
\ No newline at end of file
index af3558e..6b59f97 100644 (file)
@@ -12,14 +12,15 @@ import 'rxjs/add/observable/of';
 import {NgxsModule, Store} from "@ngxs/store";
 import {ArtifactsState} from "../../../store/states/artifacts.state";
 import {toscaArtifactMock} from "../../../../../jest/mocks/artifacts-mock";
+import { ComponentServiceNg2 } from "app/ng2/services/component-services/component.service";
 
 describe('tosca artifacts page', () => {
 
     let fixture: ComponentFixture<ToscaArtifactPageComponent>;
     let topologyTemplateServiceMock: Partial<TopologyTemplateService>;
     let workspaceServiceMock: Partial<WorkspaceService>;
-    let loaderServiceMock: Partial<SdcUiServices.LoaderService>;
     let store: Store;
+    let notificationMock: Partial<any>;
 
 
     beforeEach(
@@ -30,10 +31,6 @@ describe('tosca artifacts page', () => {
             };
             workspaceServiceMock = {metadata: <ComponentMetadata>{uniqueId: 'service_unique_id', componentType: 'SERVICE'}}
 
-            loaderServiceMock = {
-                activate : jest.fn(),
-                deactivate: jest.fn()
-            }
             const configure: ConfigureFn = testBed => {
                 testBed.configureTestingModule({
                     declarations: [ToscaArtifactPageComponent],
@@ -42,7 +39,8 @@ describe('tosca artifacts page', () => {
                     providers: [
                         {provide: WorkspaceService, useValue: workspaceServiceMock},
                         {provide: TopologyTemplateService, useValue: topologyTemplateServiceMock},
-                        {provide: SdcUiServices.LoaderService, useValue: loaderServiceMock }
+                        {provide: ComponentServiceNg2, useValue: {}},
+                        {provide: "Notification", useValue: notificationMock }
                     ],
                 });
             };
index 812f157..0386a15 100644 (file)
@@ -76,6 +76,10 @@ export class TopologyTemplateService {
         this.baseUrl = sdcConfig.api.root + sdcConfig.api.component_api_root;
     }
 
+    putServiceToscaTemplate(componentId: string, componentType: string, file) {
+        return this.http.put<any>(this.baseUrl + this.getServerTypeUrl(componentType) + componentId + '/toscaTemplate', file)
+    }
+
     getFullComponent(componentType: string, uniqueId: string): Observable<Component> {
         return this.http.get<Component>(this.baseUrl + this.getServerTypeUrl(componentType) + uniqueId);
     }