aboutsummaryrefslogtreecommitdiffstats
path: root/catalog-ui/src/app/ng2/components/forms
diff options
context:
space:
mode:
authorys9693 <ys9693@att.com>2020-01-19 13:50:02 +0200
committerOfir Sonsino <ofir.sonsino@intl.att.com>2020-01-22 12:33:31 +0000
commit16a9fce0e104a38371a9e5a567ec611ae3fc7f33 (patch)
tree03a2aff3060ddb5bc26a90115805a04becbaffc9 /catalog-ui/src/app/ng2/components/forms
parentaa83a2da4f911c3ac89318b8e9e8403b072942e1 (diff)
Catalog alignment
Issue-ID: SDC-2724 Signed-off-by: ys9693 <ys9693@att.com> Change-Id: I52b4aacb58cbd432ca0e1ff7ff1f7dd52099c6fe
Diffstat (limited to 'catalog-ui/src/app/ng2/components/forms')
-rw-r--r--catalog-ui/src/app/ng2/components/forms/artifacts-form/__snapshots__/artifact-form.component.spec.ts.snap40
-rw-r--r--catalog-ui/src/app/ng2/components/forms/artifacts-form/artifact-form.component.html55
-rw-r--r--catalog-ui/src/app/ng2/components/forms/artifacts-form/artifact-form.component.less23
-rw-r--r--catalog-ui/src/app/ng2/components/forms/artifacts-form/artifact-form.component.spec.ts242
-rw-r--r--catalog-ui/src/app/ng2/components/forms/artifacts-form/artifact-form.component.ts132
-rw-r--r--catalog-ui/src/app/ng2/components/forms/artifacts-form/artifact-form.module.ts21
-rw-r--r--catalog-ui/src/app/ng2/components/forms/artifacts-form/artifacts.service.ts175
-rw-r--r--catalog-ui/src/app/ng2/components/forms/env-params/__snapshots__/env-params.component.spec.ts.snap42
-rw-r--r--catalog-ui/src/app/ng2/components/forms/env-params/env-params.component.html71
-rw-r--r--catalog-ui/src/app/ng2/components/forms/env-params/env-params.component.less21
-rw-r--r--catalog-ui/src/app/ng2/components/forms/env-params/env-params.component.spec.ts98
-rw-r--r--catalog-ui/src/app/ng2/components/forms/env-params/env-params.component.ts91
-rw-r--r--catalog-ui/src/app/ng2/components/forms/env-params/env-params.module.ts28
13 files changed, 1039 insertions, 0 deletions
diff --git a/catalog-ui/src/app/ng2/components/forms/artifacts-form/__snapshots__/artifact-form.component.spec.ts.snap b/catalog-ui/src/app/ng2/components/forms/artifacts-form/__snapshots__/artifact-form.component.spec.ts.snap
new file mode 100644
index 0000000000..8cd085e248
--- /dev/null
+++ b/catalog-ui/src/app/ng2/components/forms/artifacts-form/__snapshots__/artifact-form.component.spec.ts.snap
@@ -0,0 +1,40 @@
+// Jest Snapshot v1, https://goo.gl/fbAQLP
+
+exports[`artifact form component should match current snapshot of artifact form component 1`] = `
+<artifact-form
+ artifactTypesOptions={[Function Array]}
+ cacheService={[Function Object]}
+ initArtifactTypes={[Function Function]}
+ onDescriptionChange={[Function Function]}
+ onLabelChange={[Function Function]}
+ onTypeChange={[Function Function]}
+ onUploadFile={[Function Function]}
+ onValidationChange={[Function Subject]}
+ verifyTypeAndFileWereFilled={[Function Function]}
+>
+ <form
+ class="artifact-form"
+ name="artifactForm"
+ novalidate=""
+ >
+ <onap-file-upload />
+ <div
+ class="artifact-form-container"
+ >
+
+ <div
+ class="right-form-container"
+ >
+ <sdc-textarea
+ label="Description"
+ testid="description"
+ />
+ <sdc-validation>
+ <sdc-required-validator />
+ <sdc-regex-validator />
+ </sdc-validation>
+ </div>
+ </div>
+ </form>
+</artifact-form>
+`;
diff --git a/catalog-ui/src/app/ng2/components/forms/artifacts-form/artifact-form.component.html b/catalog-ui/src/app/ng2/components/forms/artifacts-form/artifact-form.component.html
new file mode 100644
index 0000000000..c84d6de512
--- /dev/null
+++ b/catalog-ui/src/app/ng2/components/forms/artifacts-form/artifact-form.component.html
@@ -0,0 +1,55 @@
+<form class="artifact-form" novalidate name="artifactForm">
+
+ <onap-file-upload [disabled]="isViewOnly || showTypeFields && !artifact.artifactType" [convertToBase64]="true" [(value)]="artifact.artifactName" (fileUpload)="onUploadFile($event)" [placeHolder]="'Select File'" [label]="'Upload File'" [testId]="'fileUploadElement'" [required]="true">
+
+ </onap-file-upload>
+ <div class="artifact-form-container">
+ <div class="left-form-container" *ngIf="showTypeFields">
+ <sdc-input #artifactLabel
+ required="true"
+ [(value)]="artifact.artifactLabel"
+ [maxLength]="25"
+ [label]="'Artifact Label'"
+ [disabled]="isViewOnly || artifact && artifact.uniqueId"
+ [testId]="'artifactLabel'"
+ (keyup)="verifyTypeAndFileWereFilled()">
+ </sdc-input>
+ <sdc-validation [validateElement]="artifactLabel" (validityChanged)="onLabelChange($event)">
+ <sdc-required-validator [message]="'ADD_ARTIFACT_ERROR_LABEL_REQUIRED' | translate"></sdc-required-validator>
+ <sdc-regex-validator [message]="'VALIDATION_ERROR_SPECIAL_CHARS_NOT_ALLOWED' | translate"
+ [pattern]="validationPatterns['label']"></sdc-regex-validator>
+ </sdc-validation>
+
+ <sdc-dropdown #artifactType [disabled]="isViewOnly || artifact && artifact.uniqueId" label="Type" [required]="true"
+ [selectedOption]="selectedFileType" placeHolder="Please choose type" (changed)="onTypeChange($event)"
+ [options]="artifactTypesOptions" [testId]="'artifacttype'"></sdc-dropdown>
+ <sdc-validation [validateElement]="artifactType">
+ <sdc-required-validator
+ [message]="'ADD_ARTIFACT_ERROR_TYPE_REQUIRED' | translate"></sdc-required-validator>
+ </sdc-validation>
+ </div>
+
+ <div class="right-form-container">
+ <sdc-textarea #artifactDescription
+ [(value)]="artifact.description"
+ [required]="true"
+ testId="description"
+ [maxLength]="256"
+ label="Description"
+ [disabled]="isViewOnly"
+ (keyup)="verifyTypeAndFileWereFilled()">
+ </sdc-textarea>
+ <sdc-validation [validateElement]="artifactDescription" (validityChanged)="onDescriptionChange($event)">
+ <sdc-required-validator
+ [message]="'ADD_ARTIFACT_ERROR_DESCRIPTION_REQUIRED' | translate:{'field': 'Message' }"></sdc-required-validator>
+ <sdc-regex-validator [message]="'VALIDATION_ERROR_SPECIAL_CHARS_NOT_ALLOWED' | translate"
+ [pattern]="validationPatterns['comment']"></sdc-regex-validator>
+ </sdc-validation>
+ </div>
+ </div>
+</form>
+
+<div *ngIf="artifact && artifact.esId">
+ <div>UUID: {{artifact.artifactUUID}}</div>
+ <div>Version: {{artifact.artifactVersion}}</div>
+</div> \ No newline at end of file
diff --git a/catalog-ui/src/app/ng2/components/forms/artifacts-form/artifact-form.component.less b/catalog-ui/src/app/ng2/components/forms/artifacts-form/artifact-form.component.less
new file mode 100644
index 0000000000..3b04122085
--- /dev/null
+++ b/catalog-ui/src/app/ng2/components/forms/artifacts-form/artifact-form.component.less
@@ -0,0 +1,23 @@
+.artifact-form {
+ display: flex;
+ justify-content: space-between;
+ flex-direction: column;
+
+ .artifact-form-container {
+ margin-top: 10px;
+ display: flex;
+ flex-direction: row;
+ .left-form-container {
+ flex: 1;
+ padding-right: 10px;
+ }
+
+ .right-form-container {
+ flex: 1;
+
+ /deep/.sdc-textarea .sdc-textarea__textarea{
+ min-height: 110px;
+ }
+ }
+ }
+} \ No newline at end of file
diff --git a/catalog-ui/src/app/ng2/components/forms/artifacts-form/artifact-form.component.spec.ts b/catalog-ui/src/app/ng2/components/forms/artifacts-form/artifact-form.component.spec.ts
new file mode 100644
index 0000000000..fc69509ee9
--- /dev/null
+++ b/catalog-ui/src/app/ng2/components/forms/artifacts-form/artifact-form.component.spec.ts
@@ -0,0 +1,242 @@
+import {ArtifactFormComponent} from "./artifact-form.component";
+import {async, ComponentFixture} from "@angular/core/testing";
+import {ConfigureFn, configureTests} from "../../../../../jest/test-config.helper";
+import {NO_ERRORS_SCHEMA} from "@angular/core";
+import {TranslateModule} from "../../../shared/translator/translate.module";
+import {CacheService} from "../../../services/cache.service";
+import {TranslateService} from "../../../shared/translator/translate.service";
+import {ArtifactModel} from "../../../../models/artifacts";
+import {IDropDownOption} from "onap-ui-angular/dist/form-elements/dropdown/dropdown-models";
+import {Observable, Subject} from "rxjs";
+import {getValue} from "@ngxs/store";
+
+
+describe('artifact form component', () => {
+
+ let fixture: ComponentFixture<ArtifactFormComponent>;
+ let cacheServiceMock: Partial<CacheService>;
+ let onValidationChangeMock: Partial<Subject<boolean>>;
+
+ let artifactModel = new ArtifactModel();
+
+
+ beforeEach(
+ async(() => {
+
+ onValidationChangeMock = {
+ next: jest.fn()
+ }
+
+ cacheServiceMock = {
+ contains: jest.fn(),
+ remove: jest.fn(),
+ set: jest.fn(),
+ get: jest.fn()
+ }
+
+ const configure: ConfigureFn = testBed => {
+ testBed.configureTestingModule({
+ declarations: [ArtifactFormComponent],
+ imports: [TranslateModule],
+ schemas: [NO_ERRORS_SCHEMA],
+ providers: [
+ {provide: CacheService, useValue: cacheServiceMock},
+ {provide: TranslateService, useValue: {}}
+ ],
+ });
+ };
+
+ configureTests(configure).then(testBed => {
+ fixture = testBed.createComponent(ArtifactFormComponent);
+ });
+ })
+ );
+
+
+ it('should verify initArtifactTypes for DEPLOYMENT and ArtifactType = HEAT_ENV', () =>{
+
+ cacheServiceMock.get.mockImplementation(() =>{
+ return {
+ artifacts: {
+ deployment:{
+ resourceInstanceDeploymentArtifacts: [{name: "Dummy Value Returned from ui api"}],
+ }
+ }
+ }
+ });
+
+ fixture.componentInstance.artifactType = 'DEPLOYMENT';
+ fixture.componentInstance.artifact = artifactModel;
+ fixture.componentInstance.artifact.artifactType = 'HEAT_ENV';
+
+ fixture.componentInstance.initArtifactTypes();
+
+ expect(fixture.componentInstance.selectedFileType).toEqual(undefined);
+
+ });
+
+ it('should verify initArtifactTypes for DEPLOYMENT and ComponentType = RESOURCE', () =>{
+
+ cacheServiceMock.get.mockImplementation(() =>{
+ return {
+ artifacts: {
+ deployment:{
+ resourceDeploymentArtifacts: [{name: "Dummy Value Returned from ui api"}],
+ }
+ }
+ }
+ });
+
+ fixture.componentInstance.artifactType = 'DEPLOYMENT';
+ fixture.componentInstance.artifact = artifactModel;
+ fixture.componentInstance.componentType = 'RESOURCE';
+
+ fixture.componentInstance.initArtifactTypes();
+
+ expect(fixture.componentInstance.selectedFileType).toEqual(undefined);
+
+ });
+
+ it('should verify initArtifactTypes for DEPLOYMENT and NOT ComponentType = RESOURCE OR NOT ArtifactType = HEAT_ENV', () =>{
+
+ cacheServiceMock.get.mockImplementation(() =>{
+ return {
+ artifacts: {
+ deployment:{
+ serviceDeploymentArtifacts: [{name: "Dummy Value Returned from ui api"}],
+ }
+ }
+ }
+ });
+
+ fixture.componentInstance.artifactType = 'DEPLOYMENT';
+ fixture.componentInstance.artifact = artifactModel;
+ // fixture.componentInstance.componentType = 'RESOURCE';
+
+ fixture.componentInstance.initArtifactTypes();
+
+ expect(fixture.componentInstance.selectedFileType).toEqual(undefined);
+
+ });
+
+ it('should verify initArtifactTypes for INFORMATION', () =>{
+
+ cacheServiceMock.get.mockImplementation(() =>{
+ return {
+ artifacts: {
+ other: [{name: "Val1"}, {name: "ExpectedValToBeSelected"}, {name: "Val3"}]
+ }
+ }
+ });
+
+ fixture.componentInstance.artifactType = 'INFORMATIONAL';
+ fixture.componentInstance.artifact = artifactModel;
+ fixture.componentInstance.artifact.artifactType = 'ExpectedValToBeSelected';
+
+ fixture.componentInstance.initArtifactTypes();
+
+ expect(fixture.componentInstance.selectedFileType).toEqual({"label": "ExpectedValToBeSelected", "value": "ExpectedValToBeSelected"});
+
+ });
+
+
+ it('should match current snapshot of artifact form component', () => {
+ expect(fixture).toMatchSnapshot();
+ });
+
+ it('should verify onUploadFile -> file gets file name', () => {
+ let file = {
+ filename:'dummyFileName'
+ }
+
+ fixture.componentInstance.verifyTypeAndFileWereFilled = jest.fn();
+ fixture.componentInstance.artifact = artifactModel;
+ fixture.componentInstance.onUploadFile(file);
+
+ expect(fixture.componentInstance.artifact.artifactName).toBe('dummyFileName');
+
+ const spy1 = jest.spyOn(fixture.componentInstance,'verifyTypeAndFileWereFilled');
+ expect(spy1).toHaveBeenCalled();
+ });
+
+ it('should verify onUploadFile -> file is null', () => {
+ let file = null;
+ fixture.componentInstance.artifact = artifactModel;
+ fixture.componentInstance.onUploadFile(file);
+
+ expect(fixture.componentInstance.artifact.artifactName).toBe(null);
+ });
+
+ it('should verify onTypeChange -> verifyTypeAndFileWereFilled is being called', () => {
+ let selectedFileType:IDropDownOption;
+ selectedFileType = {"label": "dummyLabel", "value": "dummyValue"};
+ fixture.componentInstance.verifyTypeAndFileWereFilled = jest.fn();
+
+ fixture.componentInstance.artifact = artifactModel;
+ fixture.componentInstance.onTypeChange(selectedFileType);
+
+ const spy1 = jest.spyOn(fixture.componentInstance,'verifyTypeAndFileWereFilled');
+ expect(spy1).toHaveBeenCalled();
+ });
+
+ it('should verify onDescriptionChange -> verifyTypeAndFileWereFilled is being called', () => {
+ fixture.componentInstance.verifyTypeAndFileWereFilled = jest.fn();
+ fixture.componentInstance.onValidationChange.next = jest.fn(() => true);
+
+ fixture.componentInstance.artifact = artifactModel;
+ fixture.componentInstance.onDescriptionChange();
+
+ const spy1 = jest.spyOn(fixture.componentInstance,'verifyTypeAndFileWereFilled');
+ expect(spy1).toHaveBeenCalled();
+ });
+
+ it('should verify onLabelChange -> verifyTypeAndFileWereFilled is being called', () => {
+ fixture.componentInstance.verifyTypeAndFileWereFilled = jest.fn();
+ fixture.componentInstance.onValidationChange.next = jest.fn(() => true);
+
+ fixture.componentInstance.artifact = artifactModel;
+ fixture.componentInstance.onLabelChange(true);
+
+ const spy1 = jest.spyOn(fixture.componentInstance,'verifyTypeAndFileWereFilled');
+ expect(spy1).toHaveBeenCalled();
+ });
+
+ it('should verify verifyTypeAndFileWereFilled -> verify branch this.artifact.artifactType !== \'DEPLOYMENT\' ==>> onValidationChange.next(false)', () => {
+ fixture.componentInstance.artifact = artifactModel;
+ fixture.componentInstance.artifact.artifactType = 'NOT_DEPLOYMENT';
+ fixture.componentInstance.artifact.mandatory = true;
+ fixture.componentInstance.descriptionIsValid = false;
+
+ let onValidationChangeResult;
+
+ fixture.componentInstance.onValidationChange.subscribe((data) => {
+ onValidationChangeResult = data;
+ // console.log("Subscriber got data >>>>> "+ data);
+ });
+
+ fixture.componentInstance.verifyTypeAndFileWereFilled();
+
+ expect(onValidationChangeResult).toBe(false);
+ });
+
+ it('should verify verifyTypeAndFileWereFilled -> verify branch this.artifact.artifactType !== \'DEPLOYMENT\' ==>> onValidationChange.next(true)', () => {
+ fixture.componentInstance.artifact = artifactModel;
+ fixture.componentInstance.artifact.artifactType = 'NOT_DEPLOYMENT';
+ fixture.componentInstance.artifact.mandatory = true;
+ fixture.componentInstance.artifact.artifactName = 'Something';
+ fixture.componentInstance.descriptionIsValid = true;
+
+ let onValidationChangeResult;
+
+ fixture.componentInstance.onValidationChange.subscribe((data) => {
+ onValidationChangeResult = data;
+ // console.log("Subscriber got data >>>>> "+ data);
+ });
+
+ fixture.componentInstance.verifyTypeAndFileWereFilled();
+
+ expect(onValidationChangeResult).toBe(true);
+ });
+
+
+}); \ No newline at end of file
diff --git a/catalog-ui/src/app/ng2/components/forms/artifacts-form/artifact-form.component.ts b/catalog-ui/src/app/ng2/components/forms/artifacts-form/artifact-form.component.ts
new file mode 100644
index 0000000000..905d1a25ad
--- /dev/null
+++ b/catalog-ui/src/app/ng2/components/forms/artifacts-form/artifact-form.component.ts
@@ -0,0 +1,132 @@
+/**
+ * Created by rc2122 on 5/31/2018.
+ */
+import { Component, Input } from '@angular/core';
+import * as _ from 'lodash';
+import { IDropDownOption } from 'onap-ui-angular/dist/form-elements/dropdown/dropdown-models';
+import { Subject } from 'rxjs/Subject';
+import { ArtifactModel } from '../../../../models';
+import { ArtifactType, ComponentType } from '../../../../utils';
+import { Dictionary } from '../../../../utils/dictionary/dictionary';
+import { CacheService } from '../../../services/cache.service';
+
+@Component({
+ selector: 'artifact-form',
+ templateUrl: './artifact-form.component.html',
+ styleUrls: ['./artifact-form.component.less']
+})
+export class ArtifactFormComponent {
+
+ @Input() artifact: ArtifactModel;
+ @Input() artifactType: ArtifactType;
+ @Input() componentType: string;
+ @Input() instanceId: string;
+ @Input() isViewOnly: boolean;
+
+ public artifactTypesOptions: IDropDownOption[] = [];
+ public validationPatterns: Dictionary<string, RegExp>;
+ public selectedFileType: IDropDownOption;
+ public showTypeFields: boolean;
+ private onValidationChange: Subject<boolean> = new Subject();
+ private descriptionIsValid: boolean;
+ private labelIsValid: boolean;
+
+ constructor(private cacheService: CacheService) {
+ }
+
+ ngOnInit(): void {
+ this.validationPatterns = this.cacheService.get('validation').validationPatterns;
+ this.initArtifactTypes();
+ this.artifact.artifactGroupType = this.artifact.artifactGroupType || this.artifactType.toString();
+ this.showTypeFields = (this.artifact.artifactGroupType === 'DEPLOYMENT' || !this.artifact.mandatory) && this.artifact.artifactGroupType !== 'SERVICE_API';
+ }
+
+ public onTypeChange = (selectedFileType: IDropDownOption) => {
+ this.artifact.artifactType = selectedFileType.value;
+ this.verifyTypeAndFileWereFilled();
+ }
+
+ public onUploadFile = (file) => {
+ if (file) {
+ this.artifact.artifactName = file.filename;
+ this.artifact.payloadData = file.base64;
+ console.log('FILE UPLOADED', file);
+ } else {
+ this.artifact.artifactName = null;
+ }
+ this.verifyTypeAndFileWereFilled();
+ }
+
+ private initArtifactTypes = (): void => {
+ const artifactTypes: any = this.cacheService.get('UIConfiguration');
+ let validExtensions: string[];
+ let artifactTypesList: string[];
+
+ switch (this.artifactType) {
+ case ArtifactType.DEPLOYMENT:
+ if (this.artifact.artifactType === ArtifactType.HEAT_ENV || this.instanceId) {
+ validExtensions = artifactTypes.artifacts.deployment.resourceInstanceDeploymentArtifacts;
+ } else if (this.componentType === ComponentType.RESOURCE) {
+ validExtensions = artifactTypes.artifacts.deployment.resourceDeploymentArtifacts;
+ } else {
+ validExtensions = artifactTypes.artifacts.deployment.serviceDeploymentArtifacts;
+ }
+ if (validExtensions) {
+ artifactTypesList = Object.keys(validExtensions);
+ }
+ break;
+ case ArtifactType.INFORMATION:
+ artifactTypesList = artifactTypes.artifacts.other.map((element: any) => {
+ return element.name;
+ });
+ _.remove(artifactTypesList, (item: string) => {
+ return _.has(ArtifactType.THIRD_PARTY_RESERVED_TYPES, item) ||
+ _.has(ArtifactType.TOSCA, item);
+ });
+ break;
+ }
+
+ _.forEach(artifactTypesList, (artifactType: string) => {
+ this.artifactTypesOptions.push({ label: artifactType, value: artifactType });
+ });
+
+ this.selectedFileType = _.find(this.artifactTypesOptions, (artifactType) => {
+ return artifactType.value === this.artifact.artifactType;
+ });
+
+ }
+
+ // Verify that the Type and the Name (file) are filled in the Modal
+ // For Description and Label - I used this.descriptionIsValid:boolean & this.labelIsValid:boolean as part of the sdc-validation Element
+ private verifyTypeAndFileWereFilled = () => {
+ if (this.artifact.artifactType === 'DEPLOYMENT' || !this.artifact.mandatory && this.artifact.artifactGroupType !== 'SERVICE_API') {
+ // In case of all fields are required:
+ // File, Description, Type and Label
+ if (this.artifact.artifactType && this.artifact.artifactName && this.descriptionIsValid && this.labelIsValid) {
+ this.onValidationChange.next(true);
+ } else {
+ this.onValidationChange.next(false);
+ }
+ } else {
+ // In case of like Information Artifact
+ // Only file and description are required
+ if (this.descriptionIsValid && this.artifact.artifactName) {
+ this.onValidationChange.next(true);
+ } else {
+ this.onValidationChange.next(false);
+ }
+ }
+ }
+
+ // sdc-validation for Description
+ private onDescriptionChange = (isValid: boolean): void => {
+ this.descriptionIsValid = isValid;
+ this.onValidationChange.next(isValid) && this.verifyTypeAndFileWereFilled();
+ }
+
+ // sdc-validation for Label
+ private onLabelChange = (isValid: boolean): void => {
+ this.labelIsValid = isValid;
+ this.onValidationChange.next(isValid) && this.verifyTypeAndFileWereFilled();
+ }
+}
diff --git a/catalog-ui/src/app/ng2/components/forms/artifacts-form/artifact-form.module.ts b/catalog-ui/src/app/ng2/components/forms/artifacts-form/artifact-form.module.ts
new file mode 100644
index 0000000000..dba801212e
--- /dev/null
+++ b/catalog-ui/src/app/ng2/components/forms/artifacts-form/artifact-form.module.ts
@@ -0,0 +1,21 @@
+/**
+ * Created by rc2122 on 5/24/2018.
+ */
+import { NgModule } from "@angular/core";
+import { TranslateModule } from "app/ng2/shared/translator/translate.module";
+import { SdcUiComponentsModule } from "onap-ui-angular";
+import { CommonModule } from '@angular/common';
+import {ArtifactFormComponent} from "./artifact-form.component";
+
+@NgModule({
+ declarations: [ArtifactFormComponent],
+ imports: [TranslateModule,
+ SdcUiComponentsModule,
+ CommonModule],
+ exports: [ArtifactFormComponent],
+ entryComponents: [ArtifactFormComponent]
+})
+
+
+export class ArtifactFormModule {
+} \ No newline at end of file
diff --git a/catalog-ui/src/app/ng2/components/forms/artifacts-form/artifacts.service.ts b/catalog-ui/src/app/ng2/components/forms/artifacts-form/artifacts.service.ts
new file mode 100644
index 0000000000..f9400e9d18
--- /dev/null
+++ b/catalog-ui/src/app/ng2/components/forms/artifacts-form/artifacts.service.ts
@@ -0,0 +1,175 @@
+import { Injectable } from '@angular/core';
+import { Store } from '@ngxs/store';
+import { SdcUiCommon, SdcUiComponents, SdcUiServices } from 'onap-ui-angular';
+import { ArtifactModel } from '../../../../models';
+import { ArtifactGroupType, ArtifactType } from '../../../../utils/constants';
+import { TopologyTemplateService } from '../../../services/component-services/topology-template.service';
+import { TranslateService } from '../../../shared/translator/translate.service';
+import { CreateOrUpdateArtifactAction, DeleteArtifactAction } from '../../../store/actions/artifacts.action';
+import { EnvParamsComponent } from '../env-params/env-params.component';
+import { ArtifactFormComponent } from './artifact-form.component';
+
+import {
+ CreateInstanceArtifactAction,
+ DeleteInstanceArtifactAction,
+ UpdateInstanceArtifactAction
+} from '../../../store/actions/instance-artifacts.actions';
+
+@Injectable()
+export class ArtifactsService {
+
+ constructor(private serviceLoader: SdcUiServices.LoaderService,
+ private modalService: SdcUiServices.ModalService,
+ private topologyTemplateService: TopologyTemplateService,
+ private translateService: TranslateService,
+ private store: Store) {
+ }
+
+ public dispatchArtifactAction = (componentId: string, componentType: string, artifact: ArtifactModel, artifactType: ArtifactGroupType, instanceId: string) => {
+ const artifactObj = {
+ componentType,
+ componentId,
+ instanceId,
+ artifact
+ };
+
+ // Create or update instance artifact
+ if (instanceId) {
+ if (!artifact.uniqueId) {
+ // create instance artifact
+ return this.store.dispatch(new CreateInstanceArtifactAction(artifactObj));
+ } else {
+ // update instance artifact
+ return this.store.dispatch(new UpdateInstanceArtifactAction(artifactObj));
+ }
+ } else {
+ // Create or update artifact
+ return this.store.dispatch(new CreateOrUpdateArtifactAction(artifactObj));
+ }
+ }
+
+ public openArtifactModal = (componentId: string, componentType: string, artifact: ArtifactModel, artifactType: ArtifactGroupType, isViewOnly?: boolean, instanceId?: string) => {
+
+ let modalInstance;
+
+ const onOkPressed = () => {
+ const updatedArtifact = modalInstance.innerModalContent.instance.artifact;
+ this.serviceLoader.activate();
+ this.dispatchArtifactAction(componentId, componentType, updatedArtifact, artifactType, instanceId)
+ .subscribe().add(() => this.serviceLoader.deactivate());
+ };
+
+ const addOrUpdateArtifactModalConfig = {
+ title: (artifact && artifact.uniqueId) ? 'Update Artifact' : 'Create Artifact',
+ size: 'md',
+ type: SdcUiCommon.ModalType.custom,
+ testId: 'upgradeVspModal',
+ buttons: [
+ {
+ id: 'done',
+ text: 'DONE',
+ disabled: isViewOnly,
+ size: 'Add Another',
+ closeModal: true,
+ callback: onOkPressed
+ },
+ {text: 'CANCEL', size: 'sm', closeModal: true, type: 'secondary'}
+ ] as SdcUiCommon.IModalButtonComponent[]
+ } as SdcUiCommon.IModalConfig;
+
+ modalInstance = this.modalService.openCustomModal(addOrUpdateArtifactModalConfig, ArtifactFormComponent, {
+ artifact: new ArtifactModel(artifact),
+ artifactType,
+ instanceId,
+ componentType,
+ isViewOnly
+ });
+
+ if (!isViewOnly) {
+ modalInstance.innerModalContent.instance.onValidationChange.subscribe((isValid) => {
+ modalInstance.getButtonById('done').disabled = !isValid;
+ });
+ }
+ }
+
+ public openViewEnvParams(componentType: string, componentId: string, artifact: ArtifactModel, instanceId?: string) {
+ const envParamsModal = {
+ title: artifact.artifactDisplayName,
+ size: 'xl',
+ type: SdcUiCommon.ModalType.custom,
+ testId: 'viewEnvParams',
+ isDisabled: false,
+ } as SdcUiCommon.IModalConfig;
+
+ this.modalService.openCustomModal(envParamsModal, EnvParamsComponent, {
+ isInstanceSelected: !!instanceId, // equals to instanceId ? true : false
+ artifact: new ArtifactModel(artifact),
+ isViewOnly: true
+ });
+ }
+
+ public openUpdateEnvParams(componentType: string, componentId: string, artifact: ArtifactModel, instanceId?: string) {
+ let modalInstance;
+ const onOkPressed = () => {
+ const updatedArtifact = modalInstance.innerModalContent.instance.artifact;
+ this.serviceLoader.activate();
+ this.dispatchArtifactAction(componentId, componentType, updatedArtifact, ArtifactType.DEPLOYMENT, instanceId)
+ .subscribe().add(() => this.serviceLoader.deactivate());
+ };
+
+ const envParamsModal = {
+ title: artifact.artifactDisplayName,
+ size: 'xl',
+ type: SdcUiCommon.ModalType.custom,
+ testId: 'envParams',
+ isDisabled: false,
+ buttons: [
+ {
+ id: 'save',
+ text: 'Save',
+ spinner_position: 'left',
+ size: 'sm',
+ callback: onOkPressed,
+ closeModal: true
+ },
+ {text: 'Cancel', size: 'sm', closeModal: true, type: 'secondary'}
+ ] as SdcUiCommon.IModalButtonComponent[]
+ } as SdcUiCommon.IModalConfig;
+
+ modalInstance = this.modalService.openCustomModal(envParamsModal, EnvParamsComponent, {
+ isInstanceSelected: !!instanceId, // equals to instanceId ? true : false
+ artifact: new ArtifactModel(artifact)
+ });
+
+ modalInstance.innerModalContent.instance.onValidationChange.subscribe((isValid) => {
+ modalInstance.getButtonById('save').disabled = !isValid;
+ });
+ }
+
+ public deleteArtifact = (componentType: string, componentId: string, artifact: ArtifactModel, instanceId?: string) => {
+
+ const artifactObject = {
+ componentType,
+ componentId,
+ artifact,
+ instanceId
+ };
+
+ const onOkPressed: Function = () => {
+ this.serviceLoader.activate();
+ this.store.dispatch((instanceId) ? new DeleteInstanceArtifactAction(artifactObject) : new DeleteArtifactAction(artifactObject))
+ .subscribe().add(() => this.serviceLoader.deactivate());
+ };
+
+ const title = this.translateService.translate('ARTIFACT_VIEW_DELETE_MODAL_TITLE');
+ const text = this.translateService.translate('ARTIFACT_VIEW_DELETE_MODAL_TEXT', {name: artifact.artifactDisplayName});
+ const okButton = {
+ testId: 'OK',
+ text: 'OK',
+ type: SdcUiCommon.ButtonType.warning,
+ callback: onOkPressed,
+ closeModal: true
+ } as SdcUiComponents.ModalButtonComponent;
+ this.modalService.openWarningModal(title, text, 'delete-information-artifact-modal', [okButton]);
+ }
+}
diff --git a/catalog-ui/src/app/ng2/components/forms/env-params/__snapshots__/env-params.component.spec.ts.snap b/catalog-ui/src/app/ng2/components/forms/env-params/__snapshots__/env-params.component.spec.ts.snap
new file mode 100644
index 0000000000..aa567bbef2
--- /dev/null
+++ b/catalog-ui/src/app/ng2/components/forms/env-params/__snapshots__/env-params.component.spec.ts.snap
@@ -0,0 +1,42 @@
+// Jest Snapshot v1, https://goo.gl/fbAQLP
+
+exports[`environment parameters component should match current snapshot of env-params component 1`] = `
+<env-params
+ cacheService={[Function Object]}
+ clearCurrentValue={[Function Function]}
+ copiedWorkingArtifactHeatParameters={[Function Array]}
+ defaultDeploymentTimeout={[Function Number]}
+ displayRegexValid={[Function String]}
+ maxDeploymentTimeout={[Function Number]}
+ minDeploymentTimeout={[Function Number]}
+ onValidationChange={[Function Subject]}
+ onValidityChange={[Function Function]}
+ openPopOver={[Function Function]}
+ popoverService={[Function Object]}
+ textArea="undefined"
+>
+ <div
+ class="filter-bar"
+ >
+ <sdc-filter-bar />
+ </div><ngx-datatable
+ class="material ngx-datatable"
+ >
+ <div
+ visibilityobserver=""
+ >
+
+ <datatable-body
+ class="datatable-body"
+ >
+ <datatable-selection>
+
+
+
+ </datatable-selection>
+ </datatable-body>
+
+ </div>
+ </ngx-datatable>
+</env-params>
+`;
diff --git a/catalog-ui/src/app/ng2/components/forms/env-params/env-params.component.html b/catalog-ui/src/app/ng2/components/forms/env-params/env-params.component.html
new file mode 100644
index 0000000000..f55aff5132
--- /dev/null
+++ b/catalog-ui/src/app/ng2/components/forms/env-params/env-params.component.html
@@ -0,0 +1,71 @@
+<div class="filter-bar">
+ <sdc-filter-bar
+ [placeHolder]="'Search...'"
+ [testId]="'search-env-param-name'"
+ (keyup)="updateFilter($event)">
+ </sdc-filter-bar>
+</div>
+
+<ngx-datatable
+ class='material'
+ [rows]='artifact.heatParameters'
+ [columnMode]="'flex'"
+ [headerHeight]="40"
+ [rowHeight]="'auto'"
+ [scrollbarV]="false">
+
+ <ngx-datatable-column name="Parameter" [flexGrow]="2">
+ <ng-template let-row="row" ngx-datatable-cell-template prop="name">
+ {{row.name}}
+ <span *ngIf="row.description.length > 0" class="info">
+ <svg-icon [name]="'comment'" (click)="openPopOver('',row.description,{x:$event.pageX , y:$event.pageY },'bottom')"></svg-icon>
+ </span>
+ </ng-template>
+ </ngx-datatable-column>
+
+ <ngx-datatable-column name="DefaultValue"[flexGrow]="1">
+ <ng-template let-row="row" let-value="value" ngx-datatable-cell-template>
+ {{row.defaultValue}}
+ </ng-template>
+ </ngx-datatable-column>
+
+ <ngx-datatable-column name="CurrentValue" [flexGrow]="2">
+ <ng-template let-row="row" let-value="value" ngx-datatable-cell-template>
+ <sdc-input class="sdc-input-wrapper" #numberValidator
+ [placeHolder]="'Enter text'"
+ [isViewMode]="isViewOnly"
+ [size]="'medium'"
+ [(value)]=row.currentValue
+ [isIconClickable]="true"
+ (onRighIconClicked)="clearCurrentValue(row.name)"
+ [righIconName]="'trash-o'"
+ [testId] = "'value-field-of-' + row.name">
+ </sdc-input>
+
+ <sdc-validation [validateElement]="numberValidator" (validityChanged)="onValidityChange($event)" [disabled]="false" [testId]="_testId">
+ <sdc-regex-validator *ngIf="displayRegexValid && row.type == 'number' && row.currentValue !== null" [message]="'Value should be of type number.'" [pattern]="displayRegexValid" [disabled]="false"></sdc-regex-validator>
+ </sdc-validation>
+ </ng-template>
+ </ngx-datatable-column>
+
+</ngx-datatable>
+
+<div *ngIf="isInstanceSelected" class="artifactTimeout">
+
+ <sdc-number-input
+ label="Deployment Timeout ({{minDeploymentTimeout}}-{{maxDeploymentTimeout}} minutes)"
+ [required]="true"
+ [disabled]="false"
+ name="artifactTimeout"
+ testId="deploymentTimeout"
+ value="{{artifact.timeout}}"
+ [maxValue]="maxDeploymentTimeout"
+ [minValue]="minDeploymentTimeout"
+ (valueChange)="timeoutChanged($event)"
+ [isViewMode]="isViewOnly"
+ [step]="1"
+ >
+
+ </sdc-number-input>
+
+</div> \ No newline at end of file
diff --git a/catalog-ui/src/app/ng2/components/forms/env-params/env-params.component.less b/catalog-ui/src/app/ng2/components/forms/env-params/env-params.component.less
new file mode 100644
index 0000000000..48b4cba184
--- /dev/null
+++ b/catalog-ui/src/app/ng2/components/forms/env-params/env-params.component.less
@@ -0,0 +1,21 @@
+.filter-bar {
+ padding-bottom: 25px;
+}
+
+:host ::ng-deep {
+
+ .ngx-datatable {
+ //border: 1px solid red;
+ .datatable-body-cell {
+ .info {
+ float: right;
+ }
+ }
+ }
+}
+
+.artifactTimeout{
+ padding-top: 25px;
+ justify-content: start;
+ width: 230px;
+}
diff --git a/catalog-ui/src/app/ng2/components/forms/env-params/env-params.component.spec.ts b/catalog-ui/src/app/ng2/components/forms/env-params/env-params.component.spec.ts
new file mode 100644
index 0000000000..f6b0eb4a21
--- /dev/null
+++ b/catalog-ui/src/app/ng2/components/forms/env-params/env-params.component.spec.ts
@@ -0,0 +1,98 @@
+import {async, ComponentFixture} from "@angular/core/testing";
+import {EnvParamsComponent} from "./env-params.component";
+import {SdcUiServices, SdcUiCommon} from "onap-ui-angular";
+import {ConfigureFn, configureTests} from "../../../../../jest/test-config.helper";
+import {NgxDatatableModule} from "@swimlane/ngx-datatable";
+import {NO_ERRORS_SCHEMA} from "@angular/core";
+import {ArtifactModel} from "../../../../models/artifacts";
+import { CacheService } from '../../../services/cache.service';
+
+describe('environment parameters component', () => {
+
+ let fixture: ComponentFixture<EnvParamsComponent>;
+ let popoverServiceMock: Partial<SdcUiServices.PopoverService>;
+ let regexPatterns: any;
+
+ let artifactModel = new ArtifactModel();
+
+ let mockHeatParameters = [
+ {currentValue: "1", defaultValue: null, description: "Description 1", empty:false, name: "Param1", ownerId: null, type: "string", uniqueId: null, envDisplayName:null, version: null, filterTerm:null},
+ {currentValue: "2", defaultValue: null, description: "Description 2", empty:false, name: "Param2", ownerId: null, type: "string", uniqueId: null, envDisplayName:null, version: null, filterTerm:null},
+ {currentValue: "3", defaultValue: null, description: "Description 3", empty:false, name: "Param3", ownerId: null, type: "string", uniqueId: null, envDisplayName:null, version: null, filterTerm:null}
+ ];
+
+ let keyboardEvent = new KeyboardEvent("keyup");
+
+ beforeEach(
+ async(() => {
+
+ popoverServiceMock = {
+ createPopOver : jest.fn()
+ }
+
+ const configure: ConfigureFn = testBed => {
+ testBed.configureTestingModule({
+ declarations: [EnvParamsComponent],
+ imports: [NgxDatatableModule],
+ schemas: [NO_ERRORS_SCHEMA],
+ providers: [
+ { provide: SdcUiServices.PopoverService, useValue: popoverServiceMock },
+ { provide: CacheService, useValue: { get: jest.fn() } }
+ ],
+ });
+ };
+
+ configureTests(configure).then(testBed => {
+ fixture = testBed.createComponent(EnvParamsComponent);
+ });
+ })
+ );
+
+ it('should match current snapshot of env-params component', () => {
+ expect(fixture).toMatchSnapshot();
+ });
+
+ it('should clear CurrentValue for a given name in the heat parameter', () => {
+ fixture.componentInstance.artifact = artifactModel;
+ fixture.componentInstance.artifact.heatParameters = mockHeatParameters;
+ expect(fixture.componentInstance.artifact.heatParameters.length).toBe(3);
+ expect(fixture.componentInstance.artifact.heatParameters[0].currentValue).toBe("1");
+ fixture.componentInstance.clearCurrentValue("Param1");
+ expect(fixture.componentInstance.artifact.heatParameters[0].currentValue).toBe("");
+ });
+
+ it("should update filter heatParameters so there won''t be any value to be displayed", () => {
+
+ fixture.componentInstance.artifact = artifactModel;
+ fixture.componentInstance.artifact.heatParameters = mockHeatParameters;
+ fixture.componentInstance.ngOnInit();
+
+ let event = {
+ target : {
+ value: 'paramNotExist'
+ }
+ }
+
+ expect(fixture.componentInstance.artifact.heatParameters.length).toBe(3);
+ fixture.componentInstance.updateFilter(event);
+ expect(fixture.componentInstance.artifact.heatParameters.length).toBe(0);
+ });
+
+ it("should update filter heatParameters so there will be only 1 value to be displayed", () => {
+
+ fixture.componentInstance.artifact = artifactModel;
+ fixture.componentInstance.artifact.heatParameters = mockHeatParameters;
+ fixture.componentInstance.ngOnInit();
+
+ let event = {
+ target : {
+ value: 'param1'
+ }
+ }
+
+ expect(fixture.componentInstance.artifact.heatParameters.length).toBe(3);
+ fixture.componentInstance.updateFilter(event);
+ expect(fixture.componentInstance.artifact.heatParameters.length).toBe(1);
+ });
+
+}); \ No newline at end of file
diff --git a/catalog-ui/src/app/ng2/components/forms/env-params/env-params.component.ts b/catalog-ui/src/app/ng2/components/forms/env-params/env-params.component.ts
new file mode 100644
index 0000000000..58d266a01d
--- /dev/null
+++ b/catalog-ui/src/app/ng2/components/forms/env-params/env-params.component.ts
@@ -0,0 +1,91 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. 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.
+ * ============LICENSE_END=========================================================
+ */
+
+import { Component, ElementRef, Input, OnInit, ViewChild } from '@angular/core';
+import { SdcUiCommon, SdcUiServices } from 'onap-ui-angular';
+import { Subject } from 'rxjs/Rx';
+import { ArtifactModel } from '../../../../models/artifacts';
+import { CacheService } from '../../../services/cache.service';
+
+export interface IPoint {
+ x: number;
+ y: number;
+}
+
+@Component({
+ selector: 'env-params',
+ templateUrl: './env-params.component.html',
+ styleUrls: ['../../../../../assets/styles/table-style.less', './env-params.component.less']
+})
+export class EnvParamsComponent implements OnInit {
+
+ @Input() public artifact: ArtifactModel;
+ @Input() public isInstanceSelected: boolean;
+ @Input() public isViewOnly: boolean;
+
+ @ViewChild('textArea') textArea: ElementRef;
+ private copiedWorkingArtifactHeatParameters = [];
+ private onValidationChange: Subject<boolean> = new Subject();
+ private displayRegexValid = SdcUiCommon.RegexPatterns.numberOrEmpty;
+
+ // Deployment timeout in minutes
+ private maxDeploymentTimeout: number = 120;
+ private minDeploymentTimeout: number = 1;
+ private defaultDeploymentTimeout: number = 60;
+
+ constructor(private cacheService: CacheService, private popoverService: SdcUiServices.PopoverService) {
+ const configuration = cacheService.get('UIConfiguration');
+ if (configuration && configuration.heatDeploymentTimeout) {
+ this.maxDeploymentTimeout = configuration.heatDeploymentTimeout.maxMinutes;
+ this.minDeploymentTimeout = configuration.heatDeploymentTimeout.minMinutes;
+ this.defaultDeploymentTimeout = configuration.heatDeploymentTimeout.defaultMinutes;
+ }
+ }
+
+ ngOnInit(): void {
+ this.copiedWorkingArtifactHeatParameters = [...this.artifact.heatParameters];
+ }
+
+ public clearCurrentValue = (name: string) => {
+ this.artifact.heatParameters.filter((param) => param.name === name)[0].currentValue = '';
+ }
+
+ public timeoutChanged(timeout) {
+ this.artifact.timeout = timeout;
+ }
+
+ updateFilter(event) {
+ const val = event.target.value.toLowerCase();
+ // filter our data
+ const temp = this.copiedWorkingArtifactHeatParameters.filter((param) => {
+ return !val || param.name ? param.name.toLowerCase().indexOf(val) !== -1 : -1 || param.currentValue ? param.currentValue.toLowerCase().indexOf(val) !== -1 : -1;
+ });
+ // update the rows
+ this.artifact.heatParameters = temp;
+ }
+
+ private openPopOver = (title: string, content: string, positionOnPage: IPoint, location: string) => {
+ this.popoverService.createPopOver(title, content, positionOnPage, location);
+ }
+
+ private onValidityChange = (isValid: boolean): void => {
+ this.onValidationChange.next(isValid);
+ }
+}
diff --git a/catalog-ui/src/app/ng2/components/forms/env-params/env-params.module.ts b/catalog-ui/src/app/ng2/components/forms/env-params/env-params.module.ts
new file mode 100644
index 0000000000..85797bd45f
--- /dev/null
+++ b/catalog-ui/src/app/ng2/components/forms/env-params/env-params.module.ts
@@ -0,0 +1,28 @@
+import { NgModule } from "@angular/core";
+import { EnvParamsComponent } from "./env-params.component";
+import { NgxDatatableModule } from "@swimlane/ngx-datatable";
+import { SdcUiComponentsModule, SdcUiServices } from "onap-ui-angular";
+
+
+@NgModule({
+ declarations: [
+ EnvParamsComponent
+ ],
+ imports: [
+ NgxDatatableModule,
+ SdcUiComponentsModule
+ ],
+ exports: [
+ EnvParamsComponent
+ ],
+ entryComponents: [ //need to add anything that will be dynamically created
+ EnvParamsComponent
+ ],
+ providers: [
+ SdcUiServices.ModalService
+ ]
+})
+
+export class EnvParamsModule {
+
+}