diff options
57 files changed, 1815 insertions, 204 deletions
@@ -5,3 +5,46 @@ ## Reference https://github.com/pinterest/ktlint + +## Getting started + +![Basic_Architecture](./docs/media/CDS.png) + +## Basic Requirements +Before you can build this project, you must install and configure the following dependencies on your machine: + + 1- Java 8 SDK + + 2- Node and npm installed + + 3- Angular CLI & Angular v8 + + 4- MySQL Database + + 5- maven + +## Running in Development Mode + +* In the main project folder run `mvn clean install` command to install all libraries and dependencies for maven and npm. + + 1- Back-end MS + + * create `sdnctl` table in MySQL + * (Optional) You could run the project in a specific profile(dev, local) by adding `-Dspring.profiles.active=profile-name` + +2- Loop-back server: that allow your request to reach the backend from cds-ui + * move to `./cds-ui/server/` folder and run the command `npm start` + + 3- cds-ui + + * move to `./cds-ui/designer-client/` folder and run the command `npm start`, This command will make sure all the files follow the linting rules and then connect to the loopback server. + +## Using an IDE + +**Front-end** you could use `vscode` or `webstorm`. + +**Back-end** `Intellij` with kotlin plugin or any other editor + + +--- +For more information check the `docs` folder.
\ No newline at end of file diff --git a/cds-ui/designer-client/angular.json b/cds-ui/designer-client/angular.json index 256c35c1e..e503c1535 100644 --- a/cds-ui/designer-client/angular.json +++ b/cds-ui/designer-client/angular.json @@ -25,6 +25,7 @@ ], "styles": [ "src/styles.css", + "node_modules/ngx-toastr/toastr.css", "./node_modules/datatables.net-dt/css/jquery.dataTables.css", "./node_modules/bootstrap/dist/css/bootstrap.min.css", "./node_modules/@angular/material/prebuilt-themes/purple-green.css", @@ -61,12 +62,10 @@ }, "configurations": { "production": { - "fileReplacements": [ - { - "replace": "src/environments/environment.ts", - "with": "src/environments/environment.prod.ts" - } - ], + "fileReplacements": [{ + "replace": "src/environments/environment.ts", + "with": "src/environments/environment.prod.ts" + }], "optimization": true, "outputHashing": "all", "sourceMap": false, @@ -76,8 +75,7 @@ "extractLicenses": true, "vendorChunk": false, "buildOptimizer": true, - "budgets": [ - { + "budgets": [{ "type": "initial", "maximumWarning": "2mb", "maximumError": "5mb" diff --git a/cds-ui/designer-client/package-lock.json b/cds-ui/designer-client/package-lock.json index 46997c4ab..89d9c064c 100644 --- a/cds-ui/designer-client/package-lock.json +++ b/cds-ui/designer-client/package-lock.json @@ -8101,6 +8101,14 @@ "tslib": "^1.9.0" } }, + "ngx-toastr": { + "version": "11.3.3", + "resolved": "https://registry.npmjs.org/ngx-toastr/-/ngx-toastr-11.3.3.tgz", + "integrity": "sha512-DbLFkSZHsVPuuIIrsY1ziEhdkFUQ0V1yG1N0+1nKXGI5QBVesEDxLUVtntjzxJcWw/uUV+bKApo//tGHHORabQ==", + "requires": { + "tslib": "^1.9.0" + } + }, "nice-try": { "version": "1.0.5", "resolved": "https://registry.npmjs.org/nice-try/-/nice-try-1.0.5.tgz", diff --git a/cds-ui/designer-client/package.json b/cds-ui/designer-client/package.json index ec968ba7a..1e5128111 100644 --- a/cds-ui/designer-client/package.json +++ b/cds-ui/designer-client/package.json @@ -43,6 +43,7 @@ "ng2-ace-editor": "^0.3.9", "ngx-bootstrap": "^5.6.1", "ngx-file-drop": "^8.0.8", + "ngx-toastr": "^11.3.3", "rxjs": "~6.4.0", "stream": "0.0.2", "tslib": "^1.10.0", diff --git a/cds-ui/designer-client/src/app/app.module.ts b/cds-ui/designer-client/src/app/app.module.ts index da7ddfbd0..fd07d34c0 100644 --- a/cds-ui/designer-client/src/app/app.module.ts +++ b/cds-ui/designer-client/src/app/app.module.ts @@ -34,6 +34,7 @@ import {SidebarModule} from 'ng-sidebar'; import {SharedModulesModule} from './modules/shared-modules/shared-modules.module'; import {NgxFileDropModule} from 'ngx-file-drop'; import {ResourceDictionaryModule} from './modules/feature-modules/resource-dictionary/resource-dictionary.module'; +import { ToastrModule } from 'ngx-toastr'; @NgModule({ @@ -53,6 +54,7 @@ import {ResourceDictionaryModule} from './modules/feature-modules/resource-dicti NgxFileDropModule, ResourceDictionaryModule, SidebarModule, + ToastrModule.forRoot() // ToastrModule added ], providers: [ApiService], diff --git a/cds-ui/designer-client/src/app/common/core/pipes/search.pipe.ts b/cds-ui/designer-client/src/app/common/core/pipes/search.pipe.ts new file mode 100644 index 000000000..104fe0090 --- /dev/null +++ b/cds-ui/designer-client/src/app/common/core/pipes/search.pipe.ts @@ -0,0 +1,44 @@ +/* +* ============LICENSE_START======================================================= +* ONAP : CDS +* ================================================================================ +* Copyright (C) 2020 TechMahindra +*================================================================================= +* 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 { Pipe, PipeTransform } from '@angular/core'; + +@Pipe({ + name: 'search' +}) + +export class SearchPipe implements PipeTransform { + transform(items: any[], searchText: string): any[] { + if (!items) { + return []; + } + if (!searchText) { + return items; + } + searchText = searchText.toLowerCase(); + return items.filter( it => { + if (it.name) { + return it.name.toLowerCase().includes(searchText); + } else { + return items; + } + }); + } + +} diff --git a/cds-ui/designer-client/src/app/modules/feature-modules/packages/package-creation/imports-tab/imports-tab.component.html b/cds-ui/designer-client/src/app/modules/feature-modules/packages/package-creation/imports-tab/imports-tab.component.html index ac02c50e2..d487de3d1 100644 --- a/cds-ui/designer-client/src/app/modules/feature-modules/packages/package-creation/imports-tab/imports-tab.component.html +++ b/cds-ui/designer-client/src/app/modules/feature-modules/packages/package-creation/imports-tab/imports-tab.component.html @@ -36,38 +36,39 @@ <div class="card"> <div class="card-header" [id]="'head-'+mapIndex"> <h5 class="mb-0 d-flex justify-content-between"> - <button class="btn btn-link " data-toggle="collapse" - aria-expanded="false" - (click)="changeDivShow(mapIndex)" > + <button class="btn btn-link " data-toggle="collapse" aria-expanded="false" + (click)="changeDivShow(mapIndex)"> <i class="icon-file-code"></i> {{file.key}} </button> - - <a data-toggle="modal" data-target="#exampleModal" class="accordion-delete"><i - class="icon-delete-sm"></i></a> - - <!-- <a class="accordion-delete" (click)="removeFile(mapIndex)"><i class="icon-delete-sm"></i></a> --> - <!-- Delete Modal --> - <div class="modal fade" id="exampleModal" tabindex="-1" role="dialog" - aria-labelledby="exampleModalLabel" aria-hidden="true"> - <div class="modal-dialog" role="document"> - <div class="modal-content"> - <div class="modal-header"> - <h5 class="modal-title" id="exampleModalLabel">Delete File</h5> - <button type="button" class="close" data-dismiss="modal" aria-label="Close"> - <span aria-hidden="true">×</span> - </button> - </div> - <div class="modal-body"> - <p>Are you sure you want to delete file <span>artifact_types.json</span>?</p> - </div> - <div class="modal-footer"> - <button type="button" class="btn btn-secondary" - data-dismiss="modal">Cancel</button> - <button type="button" class="btn btn-primary">Delete</button> + + <a data-toggle="modal" (click)="initDelete(file)" data-target="#exampleModal" + class="accordion-delete"><i class="icon-delete-sm"></i></a> + + <!-- <a class="accordion-delete" (click)="removeFile(mapIndex)"><i class="icon-delete-sm"></i></a> --> + <!-- Delete Modal --> + <div class="modal fade" id="exampleModal" tabindex="-1" role="dialog" + aria-labelledby="exampleModalLabel" aria-hidden="true"> + <div class="modal-dialog" role="document"> + <div class="modal-content"> + <div class="modal-header"> + <h5 class="modal-title" id="exampleModalLabel">Delete File</h5> + <button type="button" class="close" data-dismiss="modal" aria-label="Close"> + <span aria-hidden="true">×</span> + </button> + </div> + <div class="modal-body"> + <p>Are you sure you want to delete file + <span>{{fileToDelete?.key?.split('/')[1]}}</span>?</p> + </div> + <div class="modal-footer"> + <button type="button" class="btn btn-secondary" + data-dismiss="modal">Cancel</button> + <button type="button" data-dismiss="modal" (click)="removeFile()" + class="btn btn-primary">Delete</button> + </div> </div> </div> </div> - </div> </h5> </div> <div [id]="'id-'+mapIndex" class="collapse" [attr.aria-labelledby]="'head-'+mapIndex" @@ -153,7 +154,8 @@ <button type="button" class="btn btn-sm btn-secondary" data-dismiss="modal" (click)="resetTheUploadedFiles()">Cancel </button> - <button type="button" class="btn btn-sm btn-primary" data-dismiss="modal" (click)="setFilesToStore()"> + <button type="button" class="btn btn-sm btn-primary" [disabled]="uploadedFiles?.length<=0" + data-dismiss="modal" (click)="setFilesToStore()"> Import </button> </div> diff --git a/cds-ui/designer-client/src/app/modules/feature-modules/packages/package-creation/imports-tab/imports-tab.component.ts b/cds-ui/designer-client/src/app/modules/feature-modules/packages/package-creation/imports-tab/imports-tab.component.ts index 35c0918de..9858fd580 100644 --- a/cds-ui/designer-client/src/app/modules/feature-modules/packages/package-creation/imports-tab/imports-tab.component.ts +++ b/cds-ui/designer-client/src/app/modules/feature-modules/packages/package-creation/imports-tab/imports-tab.component.ts @@ -1,7 +1,7 @@ -import {Component, OnInit} from '@angular/core'; -import {FileSystemFileEntry, NgxFileDropEntry} from 'ngx-file-drop'; -import {PackageCreationStore} from '../package-creation.store'; -import {PackageCreationUtils} from '../package-creation.utils'; +import { Component, OnInit } from '@angular/core'; +import { FileSystemFileEntry, NgxFileDropEntry } from 'ngx-file-drop'; +import { PackageCreationStore } from '../package-creation.store'; +import { PackageCreationUtils } from '../package-creation.utils'; @Component({ @@ -14,7 +14,7 @@ export class ImportsTabComponent implements OnInit { public definitionFiles: Map<string, string> = new Map<string, string>(); public uploadedFiles: FileSystemFileEntry[] = []; private fileNames: Set<string> = new Set(); - + fileToDelete: any = {}; public files: NgxFileDropEntry[] = []; constructor(private packageCreationStore: PackageCreationStore, private packageCreationUtils: PackageCreationUtils) { @@ -40,11 +40,21 @@ export class ImportsTabComponent implements OnInit { } } } - - removeFile(fileIndex: number) { - const filename = 'Definitions/' + this.uploadedFiles[fileIndex].name; + initDelete(file) { + console.log(file); + this.fileToDelete = file; + } + removeFile() { + const filename = this.fileToDelete.key; this.packageCreationStore.removeFileFromDefinition(filename); - this.uploadedFiles.splice(fileIndex, 1); + + for (let i = 0; i < this.uploadedFiles.length; i++) { + console.log(this.uploadedFiles[i]); + if (this.uploadedFiles[i].name === filename) { + this.uploadedFiles.splice(i, 1); + break; + } + } } public fileOver(event) { diff --git a/cds-ui/designer-client/src/app/modules/feature-modules/packages/package-creation/package-creation.store.ts b/cds-ui/designer-client/src/app/modules/feature-modules/packages/package-creation/package-creation.store.ts index 0808223cd..8302697fe 100644 --- a/cds-ui/designer-client/src/app/modules/feature-modules/packages/package-creation/package-creation.store.ts +++ b/cds-ui/designer-client/src/app/modules/feature-modules/packages/package-creation/package-creation.store.ts @@ -84,6 +84,10 @@ export class PackageCreationStore extends Store<CBAPackage> { this.state.scripts.files.delete(name); } + fileExist(key: string) { + return this.state.templates.files.has(key); + } + removeFileFromDefinition(filename) { this.state.definitions.imports.delete(filename); } diff --git a/cds-ui/designer-client/src/app/modules/feature-modules/packages/package-creation/scripts-tab/scripts-tab.component.html b/cds-ui/designer-client/src/app/modules/feature-modules/packages/package-creation/scripts-tab/scripts-tab.component.html index 79c444061..159f7aec4 100644 --- a/cds-ui/designer-client/src/app/modules/feature-modules/packages/package-creation/scripts-tab/scripts-tab.component.html +++ b/cds-ui/designer-client/src/app/modules/feature-modules/packages/package-creation/scripts-tab/scripts-tab.component.html @@ -20,12 +20,12 @@ <div class="card"> <div [id]="'head-script-'+mapIndex" class="card-header"> <h5 class="mb-0 d-flex justify-content-between"> - <button (click)="changeDivShow(mapIndex)" - aria-expanded="false" class="btn btn-link collapsed" data-toggle="collapse"> + <button (click)="changeDivShow(mapIndex)" aria-expanded="false" class="btn btn-link collapsed" + data-toggle="collapse"> <i class="icon-file-code"></i> {{file.key}} </button> - <a data-toggle="modal" data-target="#exampleModal" class="accordion-delete"><i - class="icon-delete-sm"></i></a> + <a data-toggle="modal" (click)="initDelete(file)" data-target="#exampleModal" + class="accordion-delete"><i class="icon-delete-sm"></i></a> <!-- <a (click)="removeFile(file.key,mapIndex)" data-toggle="modal" data-target="#exampleModal" class="accordion-delete"><i class="icon-delete-sm"></i></a> --> <!-- Delete Modal --> @@ -40,12 +40,14 @@ </button> </div> <div class="modal-body"> - <p>Are you sure you want to delete script file <span>ConfigDeploy.py</span>?</p> + <p>Are you sure you want to delete script file + <span>{{fileToDelete?.key?.split('/')[2]}}</span>?</p> </div> <div class="modal-footer"> <button type="button" class="btn btn-secondary" data-dismiss="modal">Cancel</button> - <button type="button" class="btn btn-primary">Delete</button> + <button type="button" (click)="removeFile(fileToDelete?.key,0)" + data-dismiss="modal" class="btn btn-primary">Delete</button> </div> </div> </div> @@ -114,10 +116,11 @@ <button (click)="resetTheUploadedFiles()" class="btn btn-sm btn-secondary" data-dismiss="modal" type="button">Cancel </button> - <button (click)="setFilesToStore()" class="btn btn-sm btn-primary" data-dismiss="modal" type="button"> + <button (click)="setFilesToStore()" [disabled]="uploadedFiles?.length<=0" class="btn btn-sm btn-primary" + data-dismiss="modal" type="button"> Import </button> </div> </div> </div> -</div> +</div>
\ No newline at end of file diff --git a/cds-ui/designer-client/src/app/modules/feature-modules/packages/package-creation/scripts-tab/scripts-tab.component.ts b/cds-ui/designer-client/src/app/modules/feature-modules/packages/package-creation/scripts-tab/scripts-tab.component.ts index efe28e9a4..a85ccf146 100644 --- a/cds-ui/designer-client/src/app/modules/feature-modules/packages/package-creation/scripts-tab/scripts-tab.component.ts +++ b/cds-ui/designer-client/src/app/modules/feature-modules/packages/package-creation/scripts-tab/scripts-tab.component.ts @@ -3,6 +3,7 @@ import { FileSystemFileEntry, NgxFileDropEntry } from 'ngx-file-drop'; import { PackageCreationStore } from '../package-creation.store'; import 'ace-builds/src-noconflict/ace'; import 'ace-builds/webpack-resolver'; +declare var $: any; @Component({ selector: 'app-scripts-tab', @@ -15,6 +16,7 @@ export class ScriptsTabComponent implements OnInit { public uploadedFiles: FileSystemFileEntry[] = []; public files: NgxFileDropEntry[] = []; private fileNames: Set<string> = new Set(); + fileToDelete: any = {}; constructor( private packageCreationStore: PackageCreationStore, @@ -42,7 +44,9 @@ export class ScriptsTabComponent implements OnInit { } } } - + initDelete(file) { + this.fileToDelete = file; + } removeFile(filePath: string, FileIndex: number) { const filename = filePath.split('/')[2] || ''; // const filename = 'Scripts/' + this.getFileType(this.uploadedFiles[fileIndex].name) + '/' + this.uploadedFiles[fileIndex].name; diff --git a/cds-ui/designer-client/src/app/modules/feature-modules/packages/package-creation/template-mapping/templ-mapp-creation/templ-mapp-creation.component.css b/cds-ui/designer-client/src/app/modules/feature-modules/packages/package-creation/template-mapping/templ-mapp-creation/templ-mapp-creation.component.css index e69de29bb..7273b9c27 100644 --- a/cds-ui/designer-client/src/app/modules/feature-modules/packages/package-creation/template-mapping/templ-mapp-creation/templ-mapp-creation.component.css +++ b/cds-ui/designer-client/src/app/modules/feature-modules/packages/package-creation/template-mapping/templ-mapp-creation/templ-mapp-creation.component.css @@ -0,0 +1,4 @@ +.accordian-title { + color: #a09e9e; + font-size: .9em; +}
\ No newline at end of file diff --git a/cds-ui/designer-client/src/app/modules/feature-modules/packages/package-creation/template-mapping/templ-mapp-creation/templ-mapp-creation.component.html b/cds-ui/designer-client/src/app/modules/feature-modules/packages/package-creation/template-mapping/templ-mapp-creation/templ-mapp-creation.component.html index 16c3101f2..e6149c883 100644 --- a/cds-ui/designer-client/src/app/modules/feature-modules/packages/package-creation/template-mapping/templ-mapp-creation/templ-mapp-creation.component.html +++ b/cds-ui/designer-client/src/app/modules/feature-modules/packages/package-creation/template-mapping/templ-mapp-creation/templ-mapp-creation.component.html @@ -12,13 +12,13 @@ </div> <div class="template-mapping-accordion"> - <div id="accordion"> + <div class="accordion" id="accordion"> <div class="card"> <div class="card-header" id="headingOne"> <h5 class="mb-0 d-flex justify-content-between"> <button class="btn btn-link" data-toggle="collapse" data-target="#collapseOne" aria-expanded="true" aria-controls="collapseOne"> - 1. Create Template + 1. Template <span class="accordian-title">{{currentTemplate?.fileName?.split('/')[1]}}</span> </button> </h5> @@ -70,7 +70,8 @@ <h5 class="mb-0"> <button class="btn btn-link collapsed" data-toggle="collapse" data-target="#collapseTwo" aria-expanded="false" aria-controls="collapseTwo"> - 2. Manage Mapping + 2. Manage Mapping <span + class="accordian-title">{{currentMapping?.fileName?.split('/')[1]}}</span> </button> </h5> </div> @@ -119,8 +120,10 @@ <tbody> <tr *ngFor="let dict of resourceDictionaryRes"> <td> - <img *ngIf="dict.definition?.property?.required" src="/assets/img/icon-required-yes.svg"> - <img *ngIf="!dict.definition?.property?.required" src="/assets/img/icon-required-no.svg"> + <img *ngIf="dict.definition?.property?.required" + src="/assets/img/icon-required-yes.svg"> + <img *ngIf="!dict.definition?.property?.required" + src="/assets/img/icon-required-no.svg"> </td> <td>{{ dict.name }}</td> <td>{{ dict.name }}</td> @@ -166,8 +169,10 @@ <tbody> <tr *ngFor="let dict of mappingRes"> <td> - <img *ngIf="dict.definition?.property?.required" src="/assets/img/icon-required-yes.svg"> - <img *ngIf="!dict.definition?.property?.required" src="/assets/img/icon-required-no.svg"> + <img *ngIf="dict.definition?.property?.required" + src="/assets/img/icon-required-yes.svg"> + <img *ngIf="!dict.definition?.property?.required" + src="/assets/img/icon-required-no.svg"> </td> <td>{{ dict['name'] }}</td> <td>{{ dict['name'] }}</td> @@ -194,7 +199,7 @@ </div> <div class="template-mapping-action"> - <button class="btn btn-outline-secondary">Cancel</button> + <button (click)="cancel()" class="btn btn-outline-secondary">Cancel</button> <button (click)="saveToStore()" class="btn btn-primary">Submit</button> </div> </div> @@ -212,8 +217,8 @@ </button> </div> <div class="modal-body"> - <ngx-file-drop [multiple]="false" [accept]="allowedExt" dropZoneLabel="Drop files here" (onFileDrop)="dropped($event)" - (onFileOver)="fileOver($event)" (onFileLeave)="fileLeave($event)"> + <ngx-file-drop [multiple]="false" [accept]="allowedExt" dropZoneLabel="Drop files here" + (onFileDrop)="dropped($event)" (onFileOver)="fileOver($event)" (onFileLeave)="fileLeave($event)"> <ng-template ngx-file-drop-content-tmp let-openFileSelector="openFileSelector"> <div class="folder-upload"> <img src="assets/img/folder-upload.svg" /> diff --git a/cds-ui/designer-client/src/app/modules/feature-modules/packages/package-creation/template-mapping/templ-mapp-creation/templ-mapp-creation.component.ts b/cds-ui/designer-client/src/app/modules/feature-modules/packages/package-creation/template-mapping/templ-mapp-creation/templ-mapp-creation.component.ts index 628d963ce..7d4705d69 100644 --- a/cds-ui/designer-client/src/app/modules/feature-modules/packages/package-creation/template-mapping/templ-mapp-creation/templ-mapp-creation.component.ts +++ b/cds-ui/designer-client/src/app/modules/feature-modules/packages/package-creation/template-mapping/templ-mapp-creation/templ-mapp-creation.component.ts @@ -7,7 +7,8 @@ import { ResourceDictionary } from '../../mapping-models/ResourceDictionary.mode import { DataTableDirective } from 'angular-datatables'; import { Mapping, MappingAdapter } from '../../mapping-models/mappingAdapter.model'; import { PackageCreationUtils } from '../../package-creation.utils'; -import { JsonConvert } from 'json2typescript'; +import { JsonConvert, Any } from 'json2typescript'; +import { ToastrService } from 'ngx-toastr'; @Component({ selector: 'app-templ-mapp-creation', @@ -40,13 +41,16 @@ export class TemplMappCreationComponent implements OnInit, OnDestroy { dependancies = new Map<string, Array<string>>(); dependanciesSource = new Map<string, string>(); mappingRes = []; + currentTemplate: any; + currentMapping: any; constructor( private packageCreationStore: PackageCreationStore, private templateStore: TemplateStore, - private packageCreationUtils: PackageCreationUtils + private packageCreationUtils: PackageCreationUtils, + private toastr: ToastrService ) { } @@ -56,13 +60,18 @@ export class TemplMappCreationComponent implements OnInit, OnDestroy { console.log(templateInfo); this.templateInfo = templateInfo; this.fileName = templateInfo.fileName.split('/')[1]; + if (this.fileName) { + this.fileName = this.fileName.split('-')[0]; + } if (templateInfo.type === 'mapping') { this.mappingRes = templateInfo.mapping; + this.currentMapping = templateInfo; this.resourceDictionaryRes = []; this.resTableDtTrigger.next(); } else { this.templateFileContent = templateInfo.fileContent; + this.currentTemplate = templateInfo; } }); @@ -224,28 +233,44 @@ export class TemplMappCreationComponent implements OnInit, OnDestroy { } return map.key; } + cancel() { + this.fileName = ''; + this.templateFileContent = ''; + this.resourceDictionaryRes = []; + this.mappingRes = []; + this.currentMapping = {}; + this.currentTemplate = {}; + } saveToStore() { - console.log(this.dependancies); - console.log(this.dependanciesSource); if (this.fileName) { - // Save Mapping to Store - if (this.resourceDictionaryRes && this.resourceDictionaryRes.length > 0) { - const mapArray = this.convertDictionaryToMap(this.resourceDictionaryRes); - this.packageCreationStore.addMapping('Templates/' + this.fileName + '-mapping.json', - this.packageCreationUtils.transformToJson(this.jsonConvert.serialize(mapArray))); - this.resourceDictionaryRes = []; - } - // Save Template to store - if (this.templateFileContent) { - this.packageCreationStore.addTemplate('Templates/' + this.fileName + '-template' + this.getFileExtension(), - this.templateFileContent); - this.templateFileContent = ''; + // check file duplication + if (!(this.packageCreationStore.fileExist('Templates/' + this.fileName + '-mapping.json') + || this.packageCreationStore.fileExist('Templates/' + this.fileName + '-template' + this.getFileExtension()))) { + // Save Mapping to Store + if (this.resourceDictionaryRes && this.resourceDictionaryRes.length > 0) { + const mapArray = this.convertDictionaryToMap(this.resourceDictionaryRes); + this.packageCreationStore.addMapping('Templates/' + this.fileName + '-mapping.json', + this.packageCreationUtils.transformToJson(this.jsonConvert.serialize(mapArray))); + this.resourceDictionaryRes = []; + } + // Save Template to store + if (this.templateFileContent) { + this.packageCreationStore.addTemplate('Templates/' + this.fileName + '-template' + this.getFileExtension(), + this.templateFileContent); + this.templateFileContent = ''; + } + this.fileName = ''; + this.toastr.success('File is created', 'success'); + } else { + console.log('this file already exist'); + this.toastr.error('File name already exist', 'Error'); } } else { - + this.toastr.error('Add the file name', 'Error'); } } + selectSource(dict, e) { const source = e.target.value; let keyDepend = null; diff --git a/cds-ui/designer-client/src/app/modules/feature-modules/packages/package-creation/template-mapping/templ-mapp-listing/templ-mapp-listing.component.html b/cds-ui/designer-client/src/app/modules/feature-modules/packages/package-creation/template-mapping/templ-mapp-listing/templ-mapp-listing.component.html index f5e683f28..a47963a72 100644 --- a/cds-ui/designer-client/src/app/modules/feature-modules/packages/package-creation/template-mapping/templ-mapp-listing/templ-mapp-listing.component.html +++ b/cds-ui/designer-client/src/app/modules/feature-modules/packages/package-creation/template-mapping/templ-mapp-listing/templ-mapp-listing.component.html @@ -4,7 +4,7 @@ <div class="template-mapping-accordion"> - <div id="accordion"> + <div class="accordion" id="listAccordion"> <div class="card"> <div class="card-header" id="headingThree"> <h5 class="mb-0 d-flex justify-content-between"> @@ -16,12 +16,12 @@ </h5> </div> - <div id="collapseThree" class="collapse show" aria-labelledby="headingThree" data-parent="#accordion"> + <div id="collapseThree" class="collapse show" aria-labelledby="headingThree" data-parent="#listAccordion"> <div class="card-body max-height-list"> <div class="row"> <!-- <div class="col-4" style="color:white" *ngFor="let file of templates.files | keyvalue; let mapIndex = index">--> <div class="col-4" *ngFor="let file of getKeys(templateAndMappingMap)"> - <a (click)="setSourceCodeEditor(file)" class="template-mapping-list active">{{file}} + <a (click)="setSourceCodeEditor(file)" class="template-mapping-list" [ngClass]="{'active':currentFile == file}">{{file}} <span *ngIf="getValue(file).isMapping">Mapping</span> <span *ngIf="getValue(file).isTemplate">Template</span> </a> diff --git a/cds-ui/designer-client/src/app/modules/feature-modules/packages/package-creation/template-mapping/templ-mapp-listing/templ-mapp-listing.component.ts b/cds-ui/designer-client/src/app/modules/feature-modules/packages/package-creation/template-mapping/templ-mapp-listing/templ-mapp-listing.component.ts index 372fbca03..448899019 100644 --- a/cds-ui/designer-client/src/app/modules/feature-modules/packages/package-creation/template-mapping/templ-mapp-listing/templ-mapp-listing.component.ts +++ b/cds-ui/designer-client/src/app/modules/feature-modules/packages/package-creation/template-mapping/templ-mapp-listing/templ-mapp-listing.component.ts @@ -17,6 +17,7 @@ export class TemplMappListingComponent implements OnInit { private templates: Template; private mapping: Mapping; isCreate = true; + currentFile: string; constructor( private packageCreationStore: PackageCreationStore, @@ -72,6 +73,7 @@ export class TemplMappListingComponent implements OnInit { } setSourceCodeEditor(key: string) { + this.currentFile = key; const templateKey = 'Templates/' + key + '-template.vtl'; this.packageCreationStore.state$.subscribe(cba => { console.log('cba ------'); diff --git a/cds-ui/designer-client/src/app/modules/feature-modules/resource-dictionary/model/definition.model.ts b/cds-ui/designer-client/src/app/modules/feature-modules/resource-dictionary/model/definition.model.ts index 96d188a54..7911c028c 100644 --- a/cds-ui/designer-client/src/app/modules/feature-modules/resource-dictionary/model/definition.model.ts +++ b/cds-ui/designer-client/src/app/modules/feature-modules/resource-dictionary/model/definition.model.ts @@ -21,10 +21,6 @@ import { Sources } from './sources.model'; export class Definition { - tag: string; - name: string; - property: string; - updatedBy: string; sources: Sources[]; } diff --git a/cds-ui/designer-client/src/app/modules/feature-modules/resource-dictionary/model/metaData.model.ts b/cds-ui/designer-client/src/app/modules/feature-modules/resource-dictionary/model/metaData.model.ts index e4b9be75f..609b066e2 100644 --- a/cds-ui/designer-client/src/app/modules/feature-modules/resource-dictionary/model/metaData.model.ts +++ b/cds-ui/designer-client/src/app/modules/feature-modules/resource-dictionary/model/metaData.model.ts @@ -26,4 +26,7 @@ export class MetaData { public entrySchema: string; public updatedBy: string; public createdDate: string; + public libraryInstance: string; + public required: string; + public derivedFrom: string; } diff --git a/cds-ui/designer-client/src/app/modules/feature-modules/resource-dictionary/model/resource-dictionary.model.ts b/cds-ui/designer-client/src/app/modules/feature-modules/resource-dictionary/model/resource-dictionary.model.ts new file mode 100644 index 000000000..85686551e --- /dev/null +++ b/cds-ui/designer-client/src/app/modules/feature-modules/resource-dictionary/model/resource-dictionary.model.ts @@ -0,0 +1,26 @@ +/* +* ============LICENSE_START======================================================= +* ONAP : CDS +* ================================================================================ +* Copyright (C) 2020 TechMahindra +*================================================================================= +* 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 { MetaData } from './metaData.model'; +import { Definition } from './definition.model'; + +export class ResourceDictionary { + public metaData: MetaData; + public definition: Definition; +} diff --git a/cds-ui/designer-client/src/app/modules/feature-modules/resource-dictionary/model/sources.model.ts b/cds-ui/designer-client/src/app/modules/feature-modules/resource-dictionary/model/sources.model.ts index 4074e5138..35d3f77c3 100644 --- a/cds-ui/designer-client/src/app/modules/feature-modules/resource-dictionary/model/sources.model.ts +++ b/cds-ui/designer-client/src/app/modules/feature-modules/resource-dictionary/model/sources.model.ts @@ -17,8 +17,6 @@ * limitations under the License. * ============LICENSE_END========================================================= */ - export class Sources { sources: []; - } diff --git a/cds-ui/designer-client/src/app/modules/feature-modules/resource-dictionary/resource-dictionary-creation/dictionary-creation.service.spec.ts b/cds-ui/designer-client/src/app/modules/feature-modules/resource-dictionary/resource-dictionary-creation/dictionary-creation.service.spec.ts new file mode 100644 index 000000000..896c7caf0 --- /dev/null +++ b/cds-ui/designer-client/src/app/modules/feature-modules/resource-dictionary/resource-dictionary-creation/dictionary-creation.service.spec.ts @@ -0,0 +1,31 @@ +/* +* ============LICENSE_START======================================================= +* ONAP : CDS +* ================================================================================ +* Copyright (C) 2020 TechMahindra +*================================================================================= +* 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 { TestBed } from '@angular/core/testing'; + +import { DictionaryCreationService } from './dictionary-creation.service'; + +describe('DictionaryCreationService', () => { + beforeEach(() => TestBed.configureTestingModule({})); + + it('should be created', () => { + const service: DictionaryCreationService = TestBed.get(DictionaryCreationService); + expect(service).toBeTruthy(); + }); +}); diff --git a/cds-ui/designer-client/src/app/modules/feature-modules/resource-dictionary/resource-dictionary-creation/dictionary-creation.service.ts b/cds-ui/designer-client/src/app/modules/feature-modules/resource-dictionary/resource-dictionary-creation/dictionary-creation.service.ts new file mode 100644 index 000000000..df90e6cd1 --- /dev/null +++ b/cds-ui/designer-client/src/app/modules/feature-modules/resource-dictionary/resource-dictionary-creation/dictionary-creation.service.ts @@ -0,0 +1,37 @@ +/* +* ============LICENSE_START======================================================= +* ONAP : CDS +* ================================================================================ +* Copyright (C) 2020 TechMahindra +*================================================================================= +* 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 { Injectable } from '@angular/core'; +import { ResourceDictionaryURLs } from 'src/app/common/constants/app-constants'; +import { Observable } from 'rxjs'; +import { Sources } from '../model/sources.model'; +import { ApiService } from 'src/app/common/core/services/api.service'; + +@Injectable({ + providedIn: 'root' +}) +export class DictionaryCreationService { + + constructor(private api: ApiService) { } + + getSources() { + return this.api.get(ResourceDictionaryURLs.getSources); +} + +} diff --git a/cds-ui/designer-client/src/app/modules/feature-modules/resource-dictionary/resource-dictionary-creation/dictionary-creation.store.ts b/cds-ui/designer-client/src/app/modules/feature-modules/resource-dictionary/resource-dictionary-creation/dictionary-creation.store.ts new file mode 100644 index 000000000..20cec7448 --- /dev/null +++ b/cds-ui/designer-client/src/app/modules/feature-modules/resource-dictionary/resource-dictionary-creation/dictionary-creation.store.ts @@ -0,0 +1,53 @@ +/* +* ============LICENSE_START======================================================= +* ONAP : CDS +* ================================================================================ +* Copyright (C) 2020 TechMahindra +*================================================================================= +* 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 { Injectable } from '@angular/core'; +import { Store } from 'src/app/common/core/stores/Store'; +import { ResourceDictionary } from '../model/resource-dictionary.model'; +import { DictionaryCreationService } from './dictionary-creation.service'; +import { MetaData } from '../model/metaData.model'; +import { Sources } from '../model/sources.model'; +import { SourcesStore } from './sources-template/sources.store'; + +@Injectable({ + providedIn: 'root' +}) +export class DictionaryCreationStore extends Store<ResourceDictionary> { + constructor(private dictionaryCreationService: DictionaryCreationService, private sourcesStore: SourcesStore) { + super(new ResourceDictionary()); + } + + changeMetaData(metaDataObject: MetaData) { + console.log(metaDataObject); + this.setState({ + ...this.state, + metaData: metaDataObject + }); + } + + getSources() { + this.sourcesStore.state$.subscribe(data => { + console.log(data); + }); + } + + SaveResourceDictionary(resourceDictionary: ResourceDictionary) { + console.log(this.setState); + } +} diff --git a/cds-ui/designer-client/src/app/modules/feature-modules/resource-dictionary/resource-dictionary-creation/dictionary-editor/dictionary-editor.component.css b/cds-ui/designer-client/src/app/modules/feature-modules/resource-dictionary/resource-dictionary-creation/dictionary-editor/dictionary-editor.component.css new file mode 100644 index 000000000..f263c0086 --- /dev/null +++ b/cds-ui/designer-client/src/app/modules/feature-modules/resource-dictionary/resource-dictionary-creation/dictionary-editor/dictionary-editor.component.css @@ -0,0 +1,19 @@ +/* +* ============LICENSE_START======================================================= +* ONAP : CDS +* ================================================================================ +* Copyright (C) 2020 TechMahindra +*================================================================================= +* 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========================================================= +*/
\ No newline at end of file diff --git a/cds-ui/designer-client/src/app/modules/feature-modules/resource-dictionary/resource-dictionary-creation/dictionary-editor/dictionary-editor.component.html b/cds-ui/designer-client/src/app/modules/feature-modules/resource-dictionary/resource-dictionary-creation/dictionary-editor/dictionary-editor.component.html new file mode 100644 index 000000000..93d7df7d1 --- /dev/null +++ b/cds-ui/designer-client/src/app/modules/feature-modules/resource-dictionary/resource-dictionary-creation/dictionary-editor/dictionary-editor.component.html @@ -0,0 +1,22 @@ +<!--/* +* ============LICENSE_START======================================================= +* ONAP : CDS +* ================================================================================ +* Copyright (C) 2020 TechMahindra +*================================================================================= +* 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========================================================= +*/--> +<ace-editor [(text)]="text" [mode]="'javascript'" [autoUpdateContent]="true" +[durationBeforeCallback]="1000" (textChanged)="textChanged($event)" [theme]="'tomorrow_night_bright'" #editor style="height:500px;"> +</ace-editor> diff --git a/cds-ui/designer-client/src/app/modules/feature-modules/resource-dictionary/resource-dictionary-creation/dictionary-editor/dictionary-editor.component.spec.ts b/cds-ui/designer-client/src/app/modules/feature-modules/resource-dictionary/resource-dictionary-creation/dictionary-editor/dictionary-editor.component.spec.ts new file mode 100644 index 000000000..09dcdbd37 --- /dev/null +++ b/cds-ui/designer-client/src/app/modules/feature-modules/resource-dictionary/resource-dictionary-creation/dictionary-editor/dictionary-editor.component.spec.ts @@ -0,0 +1,44 @@ +/* +* ============LICENSE_START======================================================= +* ONAP : CDS +* ================================================================================ +* Copyright (C) 2020 TechMahindra +*================================================================================= +* 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 { async, ComponentFixture, TestBed } from '@angular/core/testing'; + +import { DictionaryEditorComponent } from './dictionary-editor.component'; + +describe('DictionaryEditorComponent', () => { + let component: DictionaryEditorComponent; + let fixture: ComponentFixture<DictionaryEditorComponent>; + + beforeEach(async(() => { + TestBed.configureTestingModule({ + declarations: [ DictionaryEditorComponent ] + }) + .compileComponents(); + })); + + beforeEach(() => { + fixture = TestBed.createComponent(DictionaryEditorComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + }); + + it('should create', () => { + expect(component).toBeTruthy(); + }); +}); diff --git a/cds-ui/designer-client/src/app/modules/feature-modules/resource-dictionary/resource-dictionary-creation/dictionary-editor/dictionary-editor.component.ts b/cds-ui/designer-client/src/app/modules/feature-modules/resource-dictionary/resource-dictionary-creation/dictionary-editor/dictionary-editor.component.ts new file mode 100644 index 000000000..92b27c24c --- /dev/null +++ b/cds-ui/designer-client/src/app/modules/feature-modules/resource-dictionary/resource-dictionary-creation/dictionary-editor/dictionary-editor.component.ts @@ -0,0 +1,38 @@ +/* +* ============LICENSE_START======================================================= +* ONAP : CDS +* ================================================================================ +* Copyright (C) 2020 TechMahindra +*================================================================================= +* 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, OnInit } from '@angular/core'; + +@Component({ + selector: 'app-dictionary-editor', + templateUrl: './dictionary-editor.component.html', + styleUrls: ['./dictionary-editor.component.css'] +}) +export class DictionaryEditorComponent implements OnInit { + constructor() { + } + + ngOnInit() { + } + + textChanged(event) { + console.log(event); + } +} + diff --git a/cds-ui/designer-client/src/app/modules/feature-modules/resource-dictionary/resource-dictionary-creation/dictionary-metadata/dictionary-metadata.component.css b/cds-ui/designer-client/src/app/modules/feature-modules/resource-dictionary/resource-dictionary-creation/dictionary-metadata/dictionary-metadata.component.css new file mode 100644 index 000000000..f263c0086 --- /dev/null +++ b/cds-ui/designer-client/src/app/modules/feature-modules/resource-dictionary/resource-dictionary-creation/dictionary-metadata/dictionary-metadata.component.css @@ -0,0 +1,19 @@ +/* +* ============LICENSE_START======================================================= +* ONAP : CDS +* ================================================================================ +* Copyright (C) 2020 TechMahindra +*================================================================================= +* 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========================================================= +*/
\ No newline at end of file diff --git a/cds-ui/designer-client/src/app/modules/feature-modules/resource-dictionary/resource-dictionary-creation/dictionary-metadata/dictionary-metadata.component.html b/cds-ui/designer-client/src/app/modules/feature-modules/resource-dictionary/resource-dictionary-creation/dictionary-metadata/dictionary-metadata.component.html new file mode 100644 index 000000000..bea6081c0 --- /dev/null +++ b/cds-ui/designer-client/src/app/modules/feature-modules/resource-dictionary/resource-dictionary-creation/dictionary-metadata/dictionary-metadata.component.html @@ -0,0 +1,91 @@ +<!--/* +* ============LICENSE_START======================================================= +* ONAP : CDS +* ================================================================================ +* Copyright (C) 2020 TechMahindra +*================================================================================= +* 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========================================================= +*/--> +<div class="card creat-card col-11"> + <div class="single-line-model"> + <label class="label-name">Name</label> + <div class="label-input"> + <input type="input" [(ngModel)]="metaDataTab.name" + placeholder="Topology name.vLB.CDS"> + </div> + <!-- <div class="model-note-container error-message"> + Package name already exists with this version. Please enter a different name or enter different version + number. + </div> --> + </div> + + <!-- <div class="single-line-model"> + <label class="label-name">Version <span>*</span></label> + <div class="label-input"> + <input type="input" [readOnly]="!packageNameAndVersionEnables" [(ngModel)]="metaDataTab.version" + (input)="validatePackageNameAndVersion()" placeholder="Example: 1.0.0"> + </div> + <div class="model-note-container error-message">{{errorMessage}}</div> + </div> --> + <div class="single-line-model"> + <label class="label-name">Entry Schema</label> + <div class="label-input"> + <input type="input" [(ngModel)]="metaDataTab.entrySchema" placeholder="Entry Schema"> + </div> + </div> + <div class="single-line-model"> + <label class="label-name">Data Type</label> + <div class="label-input"> + <input type="input" [(ngModel)]="metaDataTab.dataType" placeholder="Data Type"> + </div> + </div> + <div class="single-line-model"> + <label class="label-name">Description</label> + <div class="label-input"> + <input type="input" [(ngModel)]="metaDataTab.description" placeholder="Descripe the package"> + </div> + </div> + <div class="single-line-model"> + <label class="label-name">Required</label> + <div class="label-input"> + <input type="input" [(ngModel)]="metaDataTab.updatedBy" placeholder="required"> + </div> + </div> + <div class="single-line-model"> + <label class="label-name">Library Instance</label> + <div class="label-input"> + <input type="input" [(ngModel)]="metaDataTab.libraryInstance" placeholder="Library Instance"> + </div> + </div> + <div class="single-line-model"> + <label class="label-name">Derived From</label> + <div class="label-input"> + <input type="input" [(ngModel)]="metaDataTab.derivedFrom" placeholder="Derived From"> + </div> + </div> + + <div class="single-line-model"> + <label class="label-name">Tags</label> + <div class="label-input"> + <input type="input" (keyup.enter)="addTag($event)" [(ngModel)]="metaDataTab.tags" + placeholder="Ex., vDNS-CDS"> + + </div> + <div class="model-note-container tag-notes">Seprate tags with comma or space</div> + <div class="model-note-container tages-container"> + <span *ngFor="let tag of tags" class="single-tage">{{tag}} <i (click)="removeTag(tag)" + class="fa fa-times-circle"></i></span> + </div> + </div> +</div> diff --git a/cds-ui/designer-client/src/app/modules/feature-modules/resource-dictionary/resource-dictionary-creation/dictionary-metadata/dictionary-metadata.component.spec.ts b/cds-ui/designer-client/src/app/modules/feature-modules/resource-dictionary/resource-dictionary-creation/dictionary-metadata/dictionary-metadata.component.spec.ts new file mode 100644 index 000000000..ce9335ff9 --- /dev/null +++ b/cds-ui/designer-client/src/app/modules/feature-modules/resource-dictionary/resource-dictionary-creation/dictionary-metadata/dictionary-metadata.component.spec.ts @@ -0,0 +1,44 @@ +/* +* ============LICENSE_START======================================================= +* ONAP : CDS +* ================================================================================ +* Copyright (C) 2020 TechMahindra +*================================================================================= +* 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 { async, ComponentFixture, TestBed } from '@angular/core/testing'; + +import { DictionaryMetadataComponent } from './dictionary-metadata.component'; + +describe('DictionaryMetadataComponent', () => { + let component: DictionaryMetadataComponent; + let fixture: ComponentFixture<DictionaryMetadataComponent>; + + beforeEach(async(() => { + TestBed.configureTestingModule({ + declarations: [ DictionaryMetadataComponent ] + }) + .compileComponents(); + })); + + beforeEach(() => { + fixture = TestBed.createComponent(DictionaryMetadataComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + }); + + it('should create', () => { + expect(component).toBeTruthy(); + }); +}); diff --git a/cds-ui/designer-client/src/app/modules/feature-modules/resource-dictionary/resource-dictionary-creation/dictionary-metadata/dictionary-metadata.component.ts b/cds-ui/designer-client/src/app/modules/feature-modules/resource-dictionary/resource-dictionary-creation/dictionary-metadata/dictionary-metadata.component.ts new file mode 100644 index 000000000..d5c4a109b --- /dev/null +++ b/cds-ui/designer-client/src/app/modules/feature-modules/resource-dictionary/resource-dictionary-creation/dictionary-metadata/dictionary-metadata.component.ts @@ -0,0 +1,78 @@ +/* +* ============LICENSE_START======================================================= +* ONAP : CDS +* ================================================================================ +* Copyright (C) 2020 TechMahindra +*================================================================================= +* 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, OnInit } from '@angular/core'; +import { ActivatedRoute } from '@angular/router'; +import { DictionaryModel } from '../../model/dictionary.model'; +import { DictionaryCreationService } from '../dictionary-creation.service'; +import { DictionaryCreationStore } from '../dictionary-creation.store'; +import { MetaData } from '../../model/metaData.model'; + +@Component({ + selector: 'app-dictionary-metadata', + templateUrl: './dictionary-metadata.component.html', + styleUrls: ['./dictionary-metadata.component.css'] +}) +export class DictionaryMetadataComponent implements OnInit { + packageNameAndVersionEnables = true; + counter = 0; + tags = new Set<string>(); + private metaDataTab: MetaData = new MetaData(); + private errorMessage: string; + + constructor( + private route: ActivatedRoute, + private dictionaryCreationService: DictionaryCreationService, + private dictionaryCreationStore: DictionaryCreationStore + ) {} + + ngOnInit() { + this.dictionaryCreationStore.state$.subscribe(element => { + if (element && element.metaData) { + this.metaDataTab.name = element.metaData.name; + this.metaDataTab.description = element.metaData.description; + this.metaDataTab.dataType = element.metaData.dataType; + this.metaDataTab.tags = element.metaData.tags; + this.metaDataTab.entrySchema = element.metaData.entrySchema; + this.metaDataTab.required = element.metaData.required; + this.metaDataTab.libraryInstance = element.metaData.libraryInstance; + this.metaDataTab.derivedFrom = element.metaData.derivedFrom; + console.log(element); + } + }); + console.log(this.metaDataTab.name); + } + + removeTag(value) { + this.tags.delete(value); + } + + addTag(event) { + const value = event.target.value; + console.log(value); + if (value && value.trim().length > 0) { + event.target.value = ''; + this.tags.add(value); + } + } + + saveMetaDataToStore() { + this.dictionaryCreationStore.changeMetaData(this.metaDataTab); + } +} diff --git a/cds-ui/designer-client/src/app/modules/feature-modules/resource-dictionary/resource-dictionary-creation/resource-dictionary-creation.component.css b/cds-ui/designer-client/src/app/modules/feature-modules/resource-dictionary/resource-dictionary-creation/resource-dictionary-creation.component.css new file mode 100644 index 000000000..f02bd233d --- /dev/null +++ b/cds-ui/designer-client/src/app/modules/feature-modules/resource-dictionary/resource-dictionary-creation/resource-dictionary-creation.component.css @@ -0,0 +1,37 @@ +/* +* ============LICENSE_START======================================================= +* ONAP : CDS +* ================================================================================ +* Copyright (C) 2020 TechMahindra +*================================================================================= +* 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========================================================= +*/ +.edit-button{ + color:white; + background:#1B3E6F; + margin-right: 60px; + border: none; + width: 80px; + height: 20px; + margin-top: 25px; + font-size: 10px; + padding-left:5px; + padding-top: 3px; + } + .ed{ + justify-content: space-between; + } + .single-line-model{ + margin:20px; + }
\ No newline at end of file diff --git a/cds-ui/designer-client/src/app/modules/feature-modules/resource-dictionary/resource-dictionary-creation/resource-dictionary-creation.component.html b/cds-ui/designer-client/src/app/modules/feature-modules/resource-dictionary/resource-dictionary-creation/resource-dictionary-creation.component.html new file mode 100644 index 000000000..bf183216d --- /dev/null +++ b/cds-ui/designer-client/src/app/modules/feature-modules/resource-dictionary/resource-dictionary-creation/resource-dictionary-creation.component.html @@ -0,0 +1,122 @@ +<!--/* +* ============LICENSE_START======================================================= +* ONAP : CDS +* ================================================================================ +* Copyright (C) 2020 TechMahindra +*================================================================================= +* 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========================================================= +*/--> +<app-header></app-header> + +<div class="new-wrapper"> + <div class="container-fluid main-container"> + <header class="page-title"> + <div class="row"> + <h2 class="col m-0 pb-0"> + <ul class="breadcrumb-header"> + <li><a routerLink="/resource-dictionary">Resource Dictionary</a></li> + <i class="fa fa-angle-right ml-2 mr-2"></i> + <li>Dictionary Name</li> + </ul> + </h2> + </div> + </header> + <div class="container-fluid body-container"> + <div class="container card creat-card col-11"> + <div class="single-line-model customKeyTitle"> + <h5 class="label-name w-100 "> + Dictionary Name + </h5> + <!-- <label class="label-name"></label> --> + <span>Last modified {{createDate}} by me</span> + </div> + </div> + </div> + <div class="container-fluid body-container"> + <div class="container"> + <div class="creat-action-container"> + <a class="action-button" (click)="saveDictionaryToStore()"> + <i class="icon-save-sm" aria-hidden="true"></i> + <span>Save</span> + </a> + <a href="#" class="action-button" (click)="goBackToDashBorad()"> + <i class="icon-discard-sm" aria-hidden="true"></i> + <span>Discard Changes</span> + </a> + <a href="#" class="action-button"> + <i class="icon-clone-sm" aria-hidden="true"></i> + <span>Clone</span> + </a> + <a href="#" class="action-button delete"> + <i class="icon-delete-sm" aria-hidden="true"></i> + <span>Delete</span> + </a> + </div> + <nav class="row"> + <!--Nav Tabs--> + <div class="col"> + <div class="nav nav-tabs ed" id="nav-tab" role="tablist"> + <a class="nav-item nav-link active" id="nav-metadata-tab" data-toggle="tab" + href="#nav-metadata" role="tab" aria-controls="nav-metadata" aria-selected="false" + autofocus #nameit (focusout)="test()">METADATA</a> + <a class="edit-button" id="nav-editor-tab" data-toggle="tab" href="#nav-editor" + role="tab" aria-controls="nav-editor" aria-selected="false">EDITOR MODE</a> + + </div> + </div> + </nav> + <div> + <div class="row mt-4"> + <div class="col"> + <div class="tab-content" id="nav-tabContent"> + <div class="tab-pane fade show active" id="nav-metadata" role="tabpanel" + aria-labelledby="nav-metadata-tab"> + <app-dictionary-metadata></app-dictionary-metadata> + <nav class="row"> + <!--Nav Tabs--> + <div class="col"> + <div class="nav nav-tabs " id="nav-tab" role="tablist"> + <a class="nav-item nav-link active" id="nav-source-tab" data-toggle="tab" + href="#nav-source" role="tab" aria-controls="nav-source" aria-selected="false" + >SOURCES</a> + </div> + </div> + </nav> + <div class="row mt-4"> + <div class="col"> + <div class="tab-content" id="nav-tabContent"> + <div class="tab-pane fade show active" id="nav-source" role="tabpanel" + aria-labelledby="nav-source-tab"> + <app-sources-template></app-sources-template> + </div> + </div> + </div> + </div> + </div> + <div class="tab-pane fade col-11" id="nav-editor" role="tabpanel" + aria-labelledby="nav-editor-tab"> + <div class="card creat-card"> + <div class="editor-container"> + <app-dictionary-editor></app-dictionary-editor> + </div></div> + </div> + </div> + </div> + </div> + + + </div> + </div> + </div> +</div>
\ No newline at end of file diff --git a/cds-ui/designer-client/src/app/modules/feature-modules/resource-dictionary/resource-dictionary-creation/resource-dictionary-creation.component.spec.ts b/cds-ui/designer-client/src/app/modules/feature-modules/resource-dictionary/resource-dictionary-creation/resource-dictionary-creation.component.spec.ts new file mode 100644 index 000000000..b1e6b6d24 --- /dev/null +++ b/cds-ui/designer-client/src/app/modules/feature-modules/resource-dictionary/resource-dictionary-creation/resource-dictionary-creation.component.spec.ts @@ -0,0 +1,44 @@ +/* +* ============LICENSE_START======================================================= +* ONAP : CDS +* ================================================================================ +* Copyright (C) 2020 TechMahindra +*================================================================================= +* 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 { async, ComponentFixture, TestBed } from '@angular/core/testing'; + +import { ResourceDictionaryCreationComponent } from './resource-dictionary-creation.component'; + +describe('ResourceDictionaryCreationComponent', () => { + let component: ResourceDictionaryCreationComponent; + let fixture: ComponentFixture<ResourceDictionaryCreationComponent>; + + beforeEach(async(() => { + TestBed.configureTestingModule({ + declarations: [ ResourceDictionaryCreationComponent ] + }) + .compileComponents(); + })); + + beforeEach(() => { + fixture = TestBed.createComponent(ResourceDictionaryCreationComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + }); + + it('should create', () => { + expect(component).toBeTruthy(); + }); +}); diff --git a/cds-ui/designer-client/src/app/modules/feature-modules/resource-dictionary/resource-dictionary-creation/resource-dictionary-creation.component.ts b/cds-ui/designer-client/src/app/modules/feature-modules/resource-dictionary/resource-dictionary-creation/resource-dictionary-creation.component.ts new file mode 100644 index 000000000..1a3484bae --- /dev/null +++ b/cds-ui/designer-client/src/app/modules/feature-modules/resource-dictionary/resource-dictionary-creation/resource-dictionary-creation.component.ts @@ -0,0 +1,76 @@ +/* +* ============LICENSE_START======================================================= +* ONAP : CDS +* ================================================================================ +* Copyright (C) 2020 TechMahindra +*================================================================================= +* 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, OnInit, ViewChild, ElementRef } from '@angular/core'; +import { Router } from '@angular/router'; +import { DictionaryCreationStore } from './dictionary-creation.store'; +import { DictionaryModel } from '../model/dictionary.model'; +import { Definition } from '../model/definition.model'; +import { DictionaryMetadataComponent } from './dictionary-metadata/dictionary-metadata.component'; +import { SourcesTemplateComponent } from './sources-template/sources-template.component'; + +@Component({ + selector: 'app-resource-dictionary-creation', + templateUrl: './resource-dictionary-creation.component.html', + styleUrls: ['./resource-dictionary-creation.component.css'] +}) +export class ResourceDictionaryCreationComponent implements OnInit { + + constructor(private router: Router, private dictionaryCreationStore: DictionaryCreationStore) { + } + + modes: object[] = [ + {name: 'Designer Mode', style: 'mode-icon icon-designer-mode'}, + {name: 'Scripting Mode', style: 'mode-icon icon-scripting-mode'} + ]; + + private metaDataTab: DictionaryModel = new DictionaryModel(); + private definition: Definition = new Definition(); + + @ViewChild(DictionaryMetadataComponent, {static: false}) + private metadataTabComponent: DictionaryMetadataComponent; + + @ViewChild(SourcesTemplateComponent, {static: false}) + private sourcesTemplateComponent: SourcesTemplateComponent; + + @ViewChild('nameit', {static: true}) + private elementRef: ElementRef; + + ngOnInit() { + this.elementRef.nativeElement.focus(); + // this.elementRef2.nativeElement.focus(); + } + + saveDictionaryToStore() { + this.dictionaryCreationStore.getSources(); + this.dictionaryCreationStore.state$.subscribe(dd => { + console.log(dd); + }); + } + + test() { + this.metadataTabComponent.saveMetaDataToStore(); + this.sourcesTemplateComponent.saveSorcesDataToStore(); + } + + goBackToDashBorad() { + this.router.navigate(['/resource-dictionary']); + } + +} diff --git a/cds-ui/designer-client/src/app/modules/feature-modules/resource-dictionary/resource-dictionary-creation/sources-template/sources-template.component.css b/cds-ui/designer-client/src/app/modules/feature-modules/resource-dictionary/resource-dictionary-creation/sources-template/sources-template.component.css new file mode 100644 index 000000000..7799d915f --- /dev/null +++ b/cds-ui/designer-client/src/app/modules/feature-modules/resource-dictionary/resource-dictionary-creation/sources-template/sources-template.component.css @@ -0,0 +1,208 @@ +/* +* ============LICENSE_START======================================================= +* ONAP : CDS +* ================================================================================ +* Copyright (C) 2020 TechMahindra +*================================================================================= +* 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========================================================= +*/ +.source{ + left: 20px; + width: 72%; +} +.source1{ + width: 25%; + background-color:#F4F9FE; +} +h5{ + padding-top: 10px; + padding-left: 10px; + background-color:white; + height: 40px; + width: 100%; +} +.mat-form-field + .mat-form-field { + margin-left: 8px; +} +mat-expansion-panel-header{ + background-color:#E0E8F2; +} +mat-panel-title{ + color:#1B3E6F; +} +mat-expansion-panel{ + border-radius: 0px; + border-left: none; +} +.example-list .card{ + margin-bottom: 10px !important; +} +.expansion-panel{ + border: none; + background: white; + border-radius: 0px; + overflow: hidden; + display: block; + width: 100%; + color:#1B3E6F; + } +.example-container { + width: 230px; + max-width: 100%; + margin: 10px 10px 15px 0; + display: inline-block; + vertical-align: top; + border-radius: 2px; + height: 260px; + background-color:#F4F9FE; + } + .example-container2 { + width: 630px; + max-width: 100%; + margin: 10px 10px 5px 0; + display: inline-block; + vertical-align: top; + border-radius: 2px; + height: 300px; + background-color:#F4F9FE; + } + + .example-list { + min-height: 12px; + border-radius: 0px; + overflow: hidden; + display: block; + margin: 5px; + overflow-y:scroll; + overflow-x:hidden; + margin-left: 15px; + width: 195px; + margin-top: 15px; + } + + .example-list1 { + min-height: 12px; + border-radius: 0px; + overflow: hidden; + display: block; + margin: 5px; + overflow-y:hidden; + overflow-x:hidden; + } + + .checkbox{ + margin-left: 11%; + } + .example-box { + padding: 2px 1px; + display: flex; + flex-direction: row; + align-items: center; + justify-content: left; + box-sizing: border-box; + cursor: move; + font-size: 14px; + + } + .example-box1 { + padding: 1px 1px; + display: flex; + flex-direction: row; + align-items: center; + justify-content: left; + box-sizing: border-box; + cursor: move; + font-size: 14px; + border-radius: 0px !important; + } + + .cdk-drag-preview { + box-sizing: border-box; + border-radius: 4px; + box-shadow: 0 5px 5px -3px rgba(0, 0, 0, 0.2), + 0 8px 10px 1px rgba(0, 0, 0, 0.14), + 0 3px 14px 2px rgba(0, 0, 0, 0.12); + } + + .cdk-drag-placeholder { + opacity: 0; + } + + .cdk-drag-animating { + transition: transform 250ms cubic-bezier(0, 0, 0.2, 1); + } + + .example-box:last-child { + border: none; + } + + .example-list.cdk-drop-list-dragging .example-box:not(.cdk-drag-placeholder) { + transition: transform 250ms cubic-bezier(0, 0, 0.2, 1); + } + +.searchText{ + width: 180px; + /* border-top: solid 2px #F4F9FE; */ + border: 0px; + color: #1B3E6F; + font-size: 13px; + margin-top: 2px; +} +.searchBox{ + position: relative; + top: 0%; + right: 0%; + height: 35px; + border-top: solid 2px #F4F9FE; + width: 100%; + margin-left:0px; + background-color:white; +} + +.searchButton1{ + float: left; + padding-left: 0 !important; + height: 30px; + width:30px; + background: url(src/assets/img/icon-search.svg) center center no-repeat; + border: 0 !important; + margin-right:0px; +} +.action-button1{ + margin-left:50px; + padding: 1px 6px; + box-shadow: none; + color:white; + font-size: 14px; + height: 25px; + background-color:#007bff; + border-radius:16px; + border:solid 0.5px #ededed; +} +.footer{ + margin: 1px 0px; +} +.delete{ + color: red; + font-size: 14px; + margin: 2px; +} +.footer input{ + margin: 6px 0px 1px 5px; +} +.select-button{ + color: #007bff; + font-size: 14px; + margin: 2px; +}
\ No newline at end of file diff --git a/cds-ui/designer-client/src/app/modules/feature-modules/resource-dictionary/resource-dictionary-creation/sources-template/sources-template.component.html b/cds-ui/designer-client/src/app/modules/feature-modules/resource-dictionary/resource-dictionary-creation/sources-template/sources-template.component.html new file mode 100644 index 000000000..b274ce95d --- /dev/null +++ b/cds-ui/designer-client/src/app/modules/feature-modules/resource-dictionary/resource-dictionary-creation/sources-template/sources-template.component.html @@ -0,0 +1,85 @@ +<!--/* +* ============LICENSE_START======================================================= +* ONAP : CDS +* ================================================================================ +* Copyright (C) 2020 TechMahindra +*================================================================================= +* 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========================================================= +*/--> +<div class="col-11"> + <div class= "row "> + <div class="card creat-card source1"> + <h5 class="label-name"> Sources Options</h5> + <div class="searchBox row"><i class="searchButton1 col-1" aria-hidden="true"></i> + <input class="searchText col-8" [(ngModel)]="searchText" type="input" placeholder="filter sources"> + </div> + + <div class="example-container"> + <div + cdkDropList + #todoList="cdkDropList" + [cdkDropListData]="option" + [cdkDropListConnectedTo]="[doneList]" + class="example-list" + (cdkDropListDropped)="drop($event)"> + <div class="example-box card creat-card" *ngFor="let item of option| search :searchText" cdkDrag> + <input type="checkbox" class="checkbox" [(ngModel)]="checked" (change)="onChange(item, $event.target.checked)"> + {{item.name}} + </div> + + </div> + + </div> + <div class="footer row"> + <a class="select-button col-sm-05">Select all</a> + <button class="action-button1 col-sm-04" >Add to list</button> + </div> + </div> + + <div class="card creat-card source"> + <h5 class="label-name">Sources List</h5> + <div class="example-container2 card creat-card"> + <div + cdkDropList + #doneList="cdkDropList" + [cdkDropListData]="sourcesOptions" + [cdkDropListConnectedTo]="[todoList]" + class="example-list1" + (cdkDropListDropped)="drop($event)"> + + <div class="example-box1" *ngFor="let item of sourcesOptions" cdkDrag> + <input type="checkbox" value="item.name"> + <mat-expansion-panel class="expansion-panel"> + <mat-expansion-panel-header [collapsedHeight]="'23px'" [expandedHeight]="'23px'"> + <mat-panel-title> + {{item.name}} + </mat-panel-title> + </mat-expansion-panel-header> + <br> + <ace-editor [(text)]=item.value [mode]="'javascript'" [autoUpdateContent]="true" + [durationBeforeCallback]="1000" (textChanged)="textChanged($event,item)" [theme]="'tomorrow_night_bright'" #editor style="height:300px;"> + </ace-editor> + </mat-expansion-panel> + </div> + + </div> + + </div> + <div> + <a type="submit" class="delete">Delete</a> + </div> + </div> +</div> +</div> + diff --git a/cds-ui/designer-client/src/app/modules/feature-modules/resource-dictionary/resource-dictionary-creation/sources-template/sources-template.component.spec.ts b/cds-ui/designer-client/src/app/modules/feature-modules/resource-dictionary/resource-dictionary-creation/sources-template/sources-template.component.spec.ts new file mode 100644 index 000000000..e9bd4ff43 --- /dev/null +++ b/cds-ui/designer-client/src/app/modules/feature-modules/resource-dictionary/resource-dictionary-creation/sources-template/sources-template.component.spec.ts @@ -0,0 +1,44 @@ +/* +* ============LICENSE_START======================================================= +* ONAP : CDS +* ================================================================================ +* Copyright (C) 2020 TechMahindra +*================================================================================= +* 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 { async, ComponentFixture, TestBed } from '@angular/core/testing'; + +import { SourcesTemplateComponent } from './sources-template.component'; + +describe('SourcesTemplateComponent', () => { + let component: SourcesTemplateComponent; + let fixture: ComponentFixture<SourcesTemplateComponent>; + + beforeEach(async(() => { + TestBed.configureTestingModule({ + declarations: [ SourcesTemplateComponent ] + }) + .compileComponents(); + })); + + beforeEach(() => { + fixture = TestBed.createComponent(SourcesTemplateComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + }); + + it('should create', () => { + expect(component).toBeTruthy(); + }); +}); diff --git a/cds-ui/designer-client/src/app/modules/feature-modules/resource-dictionary/resource-dictionary-creation/sources-template/sources-template.component.ts b/cds-ui/designer-client/src/app/modules/feature-modules/resource-dictionary/resource-dictionary-creation/sources-template/sources-template.component.ts new file mode 100644 index 000000000..4a4f215cd --- /dev/null +++ b/cds-ui/designer-client/src/app/modules/feature-modules/resource-dictionary/resource-dictionary-creation/sources-template/sources-template.component.ts @@ -0,0 +1,109 @@ +/* +* ============LICENSE_START======================================================= +* ONAP : CDS +* ================================================================================ +* Copyright (C) 2020 TechMahindra +*================================================================================= +* 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, OnInit} from '@angular/core'; +import { CdkDragDrop, moveItemInArray, transferArrayItem } from '@angular/cdk/drag-drop'; +import { SourcesStore } from './sources.store'; + +@Component({ + selector: 'app-sources-template', + templateUrl: './sources-template.component.html', + styleUrls: ['./sources-template.component.css'] +}) +export class SourcesTemplateComponent implements OnInit { + private searchQuery = ''; + lang = 'json'; + sources = []; + option = []; + sourcesOptions = []; + textValue: any; + selectItem: boolean; + ddSource = []; + checked: boolean; + selectedArray = []; + constructor(private sourcesStore: SourcesStore) { + this.sourcesStore.state$.subscribe(sources => { + this.sources = sources.sources; + for (const key in this.sources) { + if (key) { + const sourceObj = { name: key, value: JSON.stringify(this.sources[key] )}; + this.option.push(sourceObj); + } + } + }); + } + + ngOnInit() { + this.sourcesStore.getAllSources(); + } + + saveSorcesDataToStore() { + this.sourcesStore.saveSources(this.ddSource); + } + + drop(event: CdkDragDrop<string[]>) { + this.ddSource = []; + if (event.previousContainer === event.container) { + moveItemInArray(event.container.data, event.previousIndex, event.currentIndex); + } else { + transferArrayItem(event.previousContainer.data, + event.container.data, + event.previousIndex, + event.currentIndex); + } + + for (const key2 in this.sources) { + if (key2) { + const originalSources = this.sourcesOptions; + for (const key of originalSources) { + if (key.name === key2) { + const obj = `{${key.name}: ${key.value}}`; + this.ddSource.push(obj); + } + } + } + } + } + + searchDictionary(event: any) { + this.searchQuery = event.target.value; + this.searchQuery = this.searchQuery.trim(); + console.log(this.searchQuery); + // this.dictionaryStore.search(this.searchQuery); + } + + onChange(item: string, isChecked: boolean) { + if (isChecked) { + this.selectedArray.push(item); + } + } + + textChanged(event, item) { + const editedData = JSON.parse(event); + const originalSources = this.sources; + for (const key in originalSources) { + if (key === item.name) { + this.sources[key] = editedData; + } + } + this.option = []; + this.sourcesStore.changeSources(this.sources); + } + +} diff --git a/cds-ui/designer-client/src/app/modules/feature-modules/resource-dictionary/resource-dictionary-creation/sources-template/sources.store.ts b/cds-ui/designer-client/src/app/modules/feature-modules/resource-dictionary/resource-dictionary-creation/sources-template/sources.store.ts new file mode 100644 index 000000000..7da8f03d3 --- /dev/null +++ b/cds-ui/designer-client/src/app/modules/feature-modules/resource-dictionary/resource-dictionary-creation/sources-template/sources.store.ts @@ -0,0 +1,60 @@ +/* +* ============LICENSE_START======================================================= +* ONAP : CDS +* ================================================================================ +* Copyright (C) 2020 TechMahindra +*================================================================================= +* 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 { Sources } from '../../model/sources.model'; +import { Store } from 'src/app/common/core/stores/Store'; +import { Injectable } from '@angular/core'; +import { DictionaryCreationService } from '../dictionary-creation.service'; +import { Definition } from '../../model/definition.model'; + +@Injectable({ + providedIn: 'root' +}) +export class SourcesStore extends Store<Sources> { + constructor(private dictionaryCreationService: DictionaryCreationService) { + super(new Sources()); + } + + public getAllSources() { + console.log('getting all sources...'); + this.getSources(); + } + + protected getSources() { + this.dictionaryCreationService.getSources() + .subscribe((sou) => { + console.log(sou); + this.setState({ + ...this.state, + sources: sou + }); + }); + } + + public changeSources(sou) { + this.setState({ + ...this.state, + sources: sou + }); + } + + public saveSources(sources) { + console.log(sources); + } +} diff --git a/cds-ui/designer-client/src/app/modules/feature-modules/resource-dictionary/resource-dictionary-dashboard/dictionary-list/dictionary-list.component.html b/cds-ui/designer-client/src/app/modules/feature-modules/resource-dictionary/resource-dictionary-dashboard/dictionary-list/dictionary-list.component.html index 1cedeeb09..b6fbf5939 100644 --- a/cds-ui/designer-client/src/app/modules/feature-modules/resource-dictionary/resource-dictionary-dashboard/dictionary-list/dictionary-list.component.html +++ b/cds-ui/designer-client/src/app/modules/feature-modules/resource-dictionary/resource-dictionary-dashboard/dictionary-list/dictionary-list.component.html @@ -28,7 +28,7 @@ <div class="card-footer row"> <div class="col text-center"> <a routerLink="/resource-dictionary/createDictionary" role="button" aria-pressed="true" - class="btn-create-package float"><i class="icon-create-white" aria-hidden="true"></i>Create Package + class="btn-create-package float"><i class="icon-create-white" aria-hidden="true"></i>Create Dictionary </a> <br/> <a href="#" role="button" aria-pressed="true" class="btn-import-package float mb-3"><i class="icon-import-blue" aria-hidden="true"></i>Import Package diff --git a/cds-ui/designer-client/src/app/modules/feature-modules/resource-dictionary/resource-dictionary-routing.module.ts b/cds-ui/designer-client/src/app/modules/feature-modules/resource-dictionary/resource-dictionary-routing.module.ts index eb29c4c87..04b66c7bd 100644 --- a/cds-ui/designer-client/src/app/modules/feature-modules/resource-dictionary/resource-dictionary-routing.module.ts +++ b/cds-ui/designer-client/src/app/modules/feature-modules/resource-dictionary/resource-dictionary-routing.module.ts @@ -17,15 +17,19 @@ * limitations under the License. * ============LICENSE_END========================================================= */ - import { NgModule } from '@angular/core'; import { Routes, RouterModule } from '@angular/router'; import { ResourceDictionaryDashboardComponent } from './resource-dictionary-dashboard/resource-dictionary-dashboard.component'; +import { ResourceDictionaryCreationComponent } from './resource-dictionary-creation/resource-dictionary-creation.component'; const routes: Routes = [ { path: '', component: ResourceDictionaryDashboardComponent + }, + { + path: 'createDictionary', + component: ResourceDictionaryCreationComponent } ]; diff --git a/cds-ui/designer-client/src/app/modules/feature-modules/resource-dictionary/resource-dictionary.module.ts b/cds-ui/designer-client/src/app/modules/feature-modules/resource-dictionary/resource-dictionary.module.ts index 5bd6710f8..3f0f89b0d 100644 --- a/cds-ui/designer-client/src/app/modules/feature-modules/resource-dictionary/resource-dictionary.module.ts +++ b/cds-ui/designer-client/src/app/modules/feature-modules/resource-dictionary/resource-dictionary.module.ts @@ -36,6 +36,13 @@ import { SortDictionaryComponent } from './resource-dictionary-dashboard/sort-di import { DictionaryPaginationComponent } from './resource-dictionary-dashboard/dictionary-pagination/dictionary-pagination.component'; import { SharedModulesModule } from '../../shared-modules/shared-modules.module'; import { DictionaryListComponent } from './resource-dictionary-dashboard/dictionary-list/dictionary-list.component'; +import { SearchPipe } from 'src/app/common/core/pipes/search.pipe'; +import { ResourceDictionaryCreationComponent } from './resource-dictionary-creation/resource-dictionary-creation.component'; +import { DictionaryMetadataComponent } from './resource-dictionary-creation/dictionary-metadata/dictionary-metadata.component'; +import { SourcesTemplateComponent } from './resource-dictionary-creation/sources-template/sources-template.component'; +import { DragDropModule } from '@angular/cdk/drag-drop'; +import { MatExpansionModule } from '@angular/material'; +import { DictionaryEditorComponent } from './resource-dictionary-creation/dictionary-editor/dictionary-editor.component'; @NgModule({ declarations: [ @@ -46,6 +53,11 @@ import { DictionaryListComponent } from './resource-dictionary-dashboard/diction SortDictionaryComponent, DictionaryPaginationComponent, DictionaryListComponent, + ResourceDictionaryCreationComponent, + DictionaryMetadataComponent, + SourcesTemplateComponent, + SearchPipe, + DictionaryEditorComponent, ], imports: [ CommonModule, @@ -57,6 +69,8 @@ import { DictionaryListComponent } from './resource-dictionary-dashboard/diction NgxFileDropModule, AceEditorModule, DataTablesModule, + DragDropModule, + MatExpansionModule, ] }) export class ResourceDictionaryModule { } diff --git a/docs/media/CDS.png b/docs/media/CDS.png Binary files differnew file mode 100644 index 000000000..65f4115f8 --- /dev/null +++ b/docs/media/CDS.png diff --git a/ms/blueprintsprocessor/application/src/main/resources/application-dev.properties b/ms/blueprintsprocessor/application/src/main/resources/application-dev.properties index bf5e23bc9..fb32d9afc 100755 --- a/ms/blueprintsprocessor/application/src/main/resources/application-dev.properties +++ b/ms/blueprintsprocessor/application/src/main/resources/application-dev.properties @@ -69,9 +69,9 @@ blueprintsprocessor.grpcclient.py-executor.trustCertCollection=src/main/resource # Blueprint Processor File Execution and Handling Properties ### use absolute paths if testing inside docker -### blueprintsprocessor.blueprintDeployPath=/opt/app/onap/blueprints/deploy -### blueprintsprocessor.blueprintArchivePath=/opt/app/onap/blueprints/archive -### blueprintsprocessor.blueprintWorkingPath=/opt/app/onap/blueprints/working +#blueprintsprocessor.blueprintDeployPath=/opt/app/onap/blueprints/deploy +#blueprintsprocessor.blueprintArchivePath=/opt/app/onap/blueprints/archive +#blueprintsprocessor.blueprintWorkingPath=/opt/app/onap/blueprints/working # db @@ -120,10 +120,12 @@ blueprintsprocessor.netconfExecutor.enabled=true blueprintsprocessor.restConfExecutor.enabled=true blueprintsprocessor.cliExecutor.enabled=true ### If enabling remote python executor, set this value to true -### blueprintsprocessor.remoteScriptCommand.enabled=true +#blueprintsprocessor.remoteScriptCommand.enabled=true blueprintsprocessor.remoteScriptCommand.enabled=false +blueprintsprocessor.remote-script-command.response.log.enabled=false # Kafka-message-lib Configurations +## Request consumer blueprintsprocessor.messageconsumer.self-service-api.kafkaEnable=false blueprintsprocessor.messageconsumer.self-service-api.type=kafka-basic-auth blueprintsprocessor.messageconsumer.self-service-api.bootstrapServers=127.0.0.1:9092 @@ -141,6 +143,12 @@ blueprintsprocessor.messageconsumer.self-service-api.pollMillSec=1000 #blueprintsprocessor.messageconsumer.self-service-api.scramUsername=test-user #blueprintsprocessor.messageconsumer.self-service-api.scramPassword=testUserPassword +## Response producer +blueprintsprocessor.messageproducer.self-service-api.type=kafka-basic-auth +blueprintsprocessor.messageproducer.self-service-api.bootstrapServers=127.0.0.1:9092 +blueprintsprocessor.messageproducer.self-service-api.clientId=producer-id +blueprintsprocessor.messageproducer.self-service-api.topic=producer.t + # Kafka audit service Configurations ## Audit request blueprintsprocessor.messageproducer.self-service-api.audit.kafkaEnable=false diff --git a/ms/blueprintsprocessor/functions/python-executor/src/main/kotlin/org/onap/ccsdk/cds/blueprintsprocessor/functions/python/executor/ComponentRemotePythonExecutor.kt b/ms/blueprintsprocessor/functions/python-executor/src/main/kotlin/org/onap/ccsdk/cds/blueprintsprocessor/functions/python/executor/ComponentRemotePythonExecutor.kt index d66e8b374..d4c8841a8 100644 --- a/ms/blueprintsprocessor/functions/python-executor/src/main/kotlin/org/onap/ccsdk/cds/blueprintsprocessor/functions/python/executor/ComponentRemotePythonExecutor.kt +++ b/ms/blueprintsprocessor/functions/python-executor/src/main/kotlin/org/onap/ccsdk/cds/blueprintsprocessor/functions/python/executor/ComponentRemotePythonExecutor.kt @@ -21,6 +21,7 @@ import kotlinx.coroutines.GlobalScope import kotlinx.coroutines.TimeoutCancellationException import kotlinx.coroutines.async import kotlinx.coroutines.withTimeout +import org.onap.ccsdk.cds.blueprintsprocessor.core.BluePrintPropertiesService import org.onap.ccsdk.cds.blueprintsprocessor.core.api.data.ExecutionServiceInput import org.onap.ccsdk.cds.blueprintsprocessor.core.api.data.PrepareRemoteEnvInput import org.onap.ccsdk.cds.blueprintsprocessor.core.api.data.RemoteIdentifier @@ -47,11 +48,15 @@ import org.springframework.stereotype.Component @ConditionalOnBean(name = [ExecutionServiceConstant.SERVICE_GRPC_REMOTE_SCRIPT_EXECUTION]) @Component("component-remote-python-executor") @Scope(value = ConfigurableBeanFactory.SCOPE_PROTOTYPE) -open class ComponentRemotePythonExecutor(private val remoteScriptExecutionService: RemoteScriptExecutionService) : AbstractComponentFunction() { +open class ComponentRemotePythonExecutor( + private val remoteScriptExecutionService: RemoteScriptExecutionService, + private var bluePrintPropertiesService: BluePrintPropertiesService +) : AbstractComponentFunction() { private val log = LoggerFactory.getLogger(ComponentRemotePythonExecutor::class.java)!! companion object { + const val SELECTOR_CMD_EXEC = "blueprintsprocessor.remote-script-command" const val INPUT_ENDPOINT_SELECTOR = "endpoint-selector" const val INPUT_DYNAMIC_PROPERTIES = "dynamic-properties" const val INPUT_ARGUMENT_PROPERTIES = "argument-properties" @@ -62,6 +67,8 @@ open class ComponentRemotePythonExecutor(private val remoteScriptExecutionServic const val INPUT_ENV_PREPARE_TIMEOUT = "env-prepare-timeout" const val INPUT_EXECUTE_TIMEOUT = "execution-timeout" + const val STEP_PREPARE_ENV = "prepare-env" + const val STEP_EXEC_CMD = "execute-command" const val ATTRIBUTE_EXEC_CMD_STATUS = "status" const val ATTRIBUTE_PREPARE_ENV_LOG = "prepare-environment-logs" const val ATTRIBUTE_EXEC_CMD_LOG = "execute-command-logs" @@ -74,6 +81,8 @@ open class ComponentRemotePythonExecutor(private val remoteScriptExecutionServic log.debug("Processing : $operationInputs") + val isLogResponseEnabled = bluePrintPropertiesService.propertyBeanType("$SELECTOR_CMD_EXEC.response.log.enabled", Boolean::class.java) + val bluePrintContext = bluePrintRuntimeService.bluePrintContext() val blueprintName = bluePrintContext.name() val blueprintVersion = bluePrintContext.version() @@ -142,15 +151,25 @@ open class ComponentRemotePythonExecutor(private val remoteScriptExecutionServic ) val prepareEnvOutput = remoteScriptExecutionService.prepareEnv(prepareEnvInput) log.info("$ATTRIBUTE_PREPARE_ENV_LOG - ${prepareEnvOutput.response}") - val logs = prepareEnvOutput.response + val logs = JacksonUtils.jsonNodeFromObject(prepareEnvOutput.response) val logsEnv = logs.toString().asJsonPrimitive() setAttribute(ATTRIBUTE_PREPARE_ENV_LOG, logsEnv) if (prepareEnvOutput.status != StatusType.SUCCESS) { - setAttribute(ATTRIBUTE_EXEC_CMD_LOG, "N/A".asJsonPrimitive()) - setNodeOutputErrors(prepareEnvOutput.status.name, logsEnv) + val errorMessage = prepareEnvOutput.payload + setNodeOutputErrors(prepareEnvOutput.status.name, + STEP_PREPARE_ENV, + logs, + errorMessage, + isLogResponseEnabled + ) } else { - setNodeOutputProperties(prepareEnvOutput.status.name.asJsonPrimitive(), logsEnv, "".asJsonPrimitive()) + setNodeOutputProperties(prepareEnvOutput.status.name.asJsonPrimitive(), + STEP_PREPARE_ENV, + logsEnv, + "".asJsonPrimitive(), + isLogResponseEnabled + ) } } else { // set env preparation log to empty... @@ -159,13 +178,13 @@ open class ComponentRemotePythonExecutor(private val remoteScriptExecutionServic } catch (grpcEx: io.grpc.StatusRuntimeException) { val grpcErrMsg = "Command failed during env. preparation... timeout($envPrepTimeout) requestId ($processId)." setAttribute(ATTRIBUTE_PREPARE_ENV_LOG, grpcErrMsg.asJsonPrimitive()) - setNodeOutputErrors(status = grpcErrMsg, message = "${grpcEx.status}".asJsonPrimitive()) + setNodeOutputErrors(status = grpcErrMsg, step = STEP_PREPARE_ENV, error = "${grpcEx.status}".asJsonPrimitive(), logging = isLogResponseEnabled) log.error(grpcErrMsg, grpcEx) addError(grpcErrMsg) } catch (e: Exception) { val timeoutErrMsg = "Command executor failed during env. preparation.. timeout($envPrepTimeout) requestId ($processId)." setAttribute(ATTRIBUTE_PREPARE_ENV_LOG, e.message.asJsonPrimitive()) - setNodeOutputErrors(status = timeoutErrMsg, message = "${e.message}".asJsonPrimitive()) + setNodeOutputErrors(status = timeoutErrMsg, step = STEP_PREPARE_ENV, error = "${e.message}".asJsonPrimitive(), logging = isLogResponseEnabled) log.error("Failed to process on remote executor requestId ($processId)", e) addError(timeoutErrMsg) } @@ -195,18 +214,37 @@ open class ComponentRemotePythonExecutor(private val remoteScriptExecutionServic } val logs = JacksonUtils.jsonNodeFromObject(remoteExecutionOutput.response) if (remoteExecutionOutput.status != StatusType.SUCCESS) { - setNodeOutputErrors(remoteExecutionOutput.status.name, logs, remoteExecutionOutput.payload) + setNodeOutputErrors(remoteExecutionOutput.status.name, + STEP_EXEC_CMD, + logs, + remoteExecutionOutput.payload, + isLogResponseEnabled + ) } else { - setNodeOutputProperties(remoteExecutionOutput.status.name.asJsonPrimitive(), logs, - remoteExecutionOutput.payload) + setNodeOutputProperties(remoteExecutionOutput.status.name.asJsonPrimitive(), + STEP_EXEC_CMD, + logs, + remoteExecutionOutput.payload, + isLogResponseEnabled + ) } } catch (timeoutEx: TimeoutCancellationException) { val timeoutErrMsg = "Command executor timed out executing after $executionTimeout seconds requestId ($processId)" - setNodeOutputErrors(status = timeoutErrMsg, message = "".asJsonPrimitive()) + setNodeOutputErrors(status = timeoutErrMsg, + step = STEP_EXEC_CMD, + logs = "".asJsonPrimitive(), + error = "".asJsonPrimitive(), + logging = isLogResponseEnabled + ) log.error(timeoutErrMsg, timeoutEx) } catch (grpcEx: io.grpc.StatusRuntimeException) { val timeoutErrMsg = "Command executor timed out executing after $executionTimeout seconds requestId ($processId)" - setNodeOutputErrors(status = timeoutErrMsg, message = "".asJsonPrimitive()) + setNodeOutputErrors(status = timeoutErrMsg, + step = STEP_EXEC_CMD, + logs = "".asJsonPrimitive(), + error = "".asJsonPrimitive(), + logging = isLogResponseEnabled + ) log.error("Command executor time out during GRPC call", grpcEx) } catch (e: Exception) { log.error("Failed to process on remote executor requestId ($processId)", e) @@ -234,25 +272,38 @@ open class ComponentRemotePythonExecutor(private val remoteScriptExecutionServic /** * Utility function to set the output properties of the executor node */ - private fun setNodeOutputProperties(status: JsonNode, message: JsonNode, artifacts: JsonNode) { + private fun setNodeOutputProperties(status: JsonNode, step: String, message: JsonNode, artifacts: JsonNode, logging: Boolean = true) { setAttribute(ATTRIBUTE_EXEC_CMD_STATUS, status) - log.info("Executor status : $status") setAttribute(ATTRIBUTE_RESPONSE_DATA, artifacts) - log.info("Executor artifacts: $artifacts") setAttribute(ATTRIBUTE_EXEC_CMD_LOG, message) - log.info("Executor message : $message") + + if (logging) { + log.info("Executor status : $step : $status") + log.info("Executor artifacts: $step : $artifacts") + log.info("Executor message : $step : $message") + } } /** * Utility function to set the output properties and errors of the executor node, in cas of errors */ - private fun setNodeOutputErrors(status: String, message: JsonNode, artifacts: JsonNode = "".asJsonPrimitive()) { + private fun setNodeOutputErrors( + status: String, + step: String, + logs: JsonNode = "N/A".asJsonPrimitive(), + error: JsonNode, + logging: Boolean = true + ) { setAttribute(ATTRIBUTE_EXEC_CMD_STATUS, status.asJsonPrimitive()) - log.info("Executor status : $status") - setAttribute(ATTRIBUTE_EXEC_CMD_LOG, message) - log.info("Executor message : $message") - setAttribute(ATTRIBUTE_RESPONSE_DATA, artifacts) - log.info("Executor artifacts: $artifacts") - addError(status, ATTRIBUTE_EXEC_CMD_LOG, message.toString()) + setAttribute(ATTRIBUTE_EXEC_CMD_LOG, logs) + setAttribute(ATTRIBUTE_RESPONSE_DATA, "N/A".asJsonPrimitive()) + + if (logging) { + log.info("Executor status : $step : $status") + log.info("Executor message : $step : $error") + log.info("Executor logs : $step : $logs") + } + + addError(status, step, error.toString()) } } diff --git a/ms/blueprintsprocessor/functions/python-executor/src/test/kotlin/org/onap/ccsdk/cds/blueprintsprocessor/functions/python/executor/ComponentRemotePythonExecutorTest.kt b/ms/blueprintsprocessor/functions/python-executor/src/test/kotlin/org/onap/ccsdk/cds/blueprintsprocessor/functions/python/executor/ComponentRemotePythonExecutorTest.kt index 5e57b9eb7..d4edf4bb2 100644 --- a/ms/blueprintsprocessor/functions/python-executor/src/test/kotlin/org/onap/ccsdk/cds/blueprintsprocessor/functions/python/executor/ComponentRemotePythonExecutorTest.kt +++ b/ms/blueprintsprocessor/functions/python-executor/src/test/kotlin/org/onap/ccsdk/cds/blueprintsprocessor/functions/python/executor/ComponentRemotePythonExecutorTest.kt @@ -22,6 +22,7 @@ import io.mockk.every import io.mockk.mockk import kotlinx.coroutines.runBlocking import org.junit.Test +import org.onap.ccsdk.cds.blueprintsprocessor.core.BluePrintPropertiesService import org.onap.ccsdk.cds.blueprintsprocessor.core.api.data.ExecutionServiceInput import org.onap.ccsdk.cds.blueprintsprocessor.core.api.data.PrepareRemoteEnvInput import org.onap.ccsdk.cds.blueprintsprocessor.core.api.data.RemoteScriptExecutionInput @@ -47,7 +48,10 @@ class ComponentRemotePythonExecutorTest { runBlocking { val remoteScriptExecutionService = MockRemoteScriptExecutionService() - val componentRemotePythonExecutor = ComponentRemotePythonExecutor(remoteScriptExecutionService) + val componentRemotePythonExecutor = ComponentRemotePythonExecutor( + remoteScriptExecutionService, + mockk<BluePrintPropertiesService>() + ) val executionServiceInput = JacksonUtils.readValueFromClassPathFile( @@ -88,7 +92,10 @@ class ComponentRemotePythonExecutorTest { fun testComponentRemotePythonExecutorProcessNB() { runBlocking { val remoteScriptExecutionService = MockRemoteScriptExecutionService() - val componentRemotePythonExecutor = ComponentRemotePythonExecutor(remoteScriptExecutionService) + val componentRemotePythonExecutor = ComponentRemotePythonExecutor( + remoteScriptExecutionService, + mockk<BluePrintPropertiesService>() + ) val bluePrintRuntime = mockk<DefaultBluePrintRuntimeService>("123456-1000") every { bluePrintRuntime.getBluePrintError() } answers { BluePrintError() } // successful case. diff --git a/ms/blueprintsprocessor/modules/commons/message-lib/src/main/kotlin/org/onap/ccsdk/cds/blueprintsprocessor/message/BluePrintMessageLibData.kt b/ms/blueprintsprocessor/modules/commons/message-lib/src/main/kotlin/org/onap/ccsdk/cds/blueprintsprocessor/message/BluePrintMessageLibData.kt index ac35fbf2c..b07d64388 100644 --- a/ms/blueprintsprocessor/modules/commons/message-lib/src/main/kotlin/org/onap/ccsdk/cds/blueprintsprocessor/message/BluePrintMessageLibData.kt +++ b/ms/blueprintsprocessor/modules/commons/message-lib/src/main/kotlin/org/onap/ccsdk/cds/blueprintsprocessor/message/BluePrintMessageLibData.kt @@ -78,7 +78,7 @@ open class KafkaSslAuthMessageProducerProperties : KafkaBasicAuthMessageProducer var keystore: String? = null var keystorePassword: String? = null var keystoreType: String = SslConfigs.DEFAULT_SSL_KEYSTORE_TYPE - var sslEndpointIdentificationAlgorithm: String = "" + var sslEndpointIdentificationAlgorithm: String = SslConfigs.DEFAULT_SSL_ENDPOINT_IDENTIFICATION_ALGORITHM override fun getConfig(): HashMap<String, Any> { val configProps = super.getConfig() @@ -142,7 +142,7 @@ open class KafkaStreamsSslAuthConsumerProperties : KafkaStreamsBasicAuthConsumer var keystore: String? = null var keystorePassword: String? = null var keystoreType: String = SslConfigs.DEFAULT_SSL_KEYSTORE_TYPE - var sslEndpointIdentificationAlgorithm: String = "" + var sslEndpointIdentificationAlgorithm: String = SslConfigs.DEFAULT_SSL_ENDPOINT_IDENTIFICATION_ALGORITHM override fun getConfig(): HashMap<String, Any> { val configProps = super.getConfig() @@ -218,7 +218,7 @@ open class KafkaSslAuthMessageConsumerProperties : KafkaBasicAuthMessageConsumer var keystore: String? = null var keystorePassword: String? = null var keystoreType: String = SslConfigs.DEFAULT_SSL_KEYSTORE_TYPE - var sslEndpointIdentificationAlgorithm: String = "" + var sslEndpointIdentificationAlgorithm: String = SslConfigs.DEFAULT_SSL_ENDPOINT_IDENTIFICATION_ALGORITHM override fun getConfig(): HashMap<String, Any> { val configProps = super.getConfig() diff --git a/ms/blueprintsprocessor/modules/commons/message-lib/src/main/kotlin/org/onap/ccsdk/cds/blueprintsprocessor/message/service/KafkaMessageProducerService.kt b/ms/blueprintsprocessor/modules/commons/message-lib/src/main/kotlin/org/onap/ccsdk/cds/blueprintsprocessor/message/service/KafkaMessageProducerService.kt index 931f052ed..e4991d2d8 100644 --- a/ms/blueprintsprocessor/modules/commons/message-lib/src/main/kotlin/org/onap/ccsdk/cds/blueprintsprocessor/message/service/KafkaMessageProducerService.kt +++ b/ms/blueprintsprocessor/modules/commons/message-lib/src/main/kotlin/org/onap/ccsdk/cds/blueprintsprocessor/message/service/KafkaMessageProducerService.kt @@ -17,12 +17,16 @@ package org.onap.ccsdk.cds.blueprintsprocessor.message.service +import com.fasterxml.jackson.databind.node.ObjectNode import org.apache.commons.lang.builder.ToStringBuilder import org.apache.kafka.clients.producer.Callback import org.apache.kafka.clients.producer.KafkaProducer import org.apache.kafka.clients.producer.ProducerRecord import org.apache.kafka.common.header.internals.RecordHeader +import org.onap.ccsdk.cds.blueprintsprocessor.core.api.data.ExecutionServiceOutput +import org.onap.ccsdk.cds.blueprintsprocessor.core.api.data.Status import org.onap.ccsdk.cds.blueprintsprocessor.message.MessageProducerProperties +import org.onap.ccsdk.cds.controllerblueprints.core.asJsonPrimitive import org.onap.ccsdk.cds.controllerblueprints.core.asJsonString import org.onap.ccsdk.cds.controllerblueprints.core.defaultToUUID import org.slf4j.LoggerFactory @@ -39,6 +43,10 @@ class KafkaMessageProducerService( private val messageLoggerService = MessageLoggerService() + companion object { + const val MAX_ERR_MSG_LEN = 128 + } + override suspend fun sendMessageNB(message: Any): Boolean { checkNotNull(messageProducerProperties.topic) { "default topic is not configured" } return sendMessageNB(messageProducerProperties.topic!!, message) @@ -54,9 +62,14 @@ class KafkaMessageProducerService( message: Any, headers: MutableMap<String, String>? ): Boolean { - val byteArrayMessage = when (message) { - is String -> message.toByteArray(Charset.defaultCharset()) - else -> message.asJsonString().toByteArray(Charset.defaultCharset()) + var clonedMessage = message + if (clonedMessage is ExecutionServiceOutput) { + clonedMessage = truncateResponse(clonedMessage) + } + + val byteArrayMessage = when (clonedMessage) { + is String -> clonedMessage.toByteArray(Charset.defaultCharset()) + else -> clonedMessage.asJsonString().toByteArray(Charset.defaultCharset()) } val record = ProducerRecord<String, ByteArray>(topic, defaultToUUID(), byteArrayMessage) @@ -85,4 +98,37 @@ class KafkaMessageProducerService( return kafkaProducer!! } + + /** + * Truncation of BP responses + */ + private fun truncateResponse(executionServiceOutput: ExecutionServiceOutput): ExecutionServiceOutput { + /** Truncation of error messages */ + var truncErrMsg = executionServiceOutput.status.errorMessage + if (truncErrMsg != null && truncErrMsg.length > MAX_ERR_MSG_LEN) { + truncErrMsg = "${truncErrMsg.substring(0,MAX_ERR_MSG_LEN)}" + + " [...]. Check Blueprint Processor logs for more information." + } + /** Truncation for Command Executor responses */ + var truncPayload = executionServiceOutput.payload.deepCopy() + val workflowName = executionServiceOutput.actionIdentifiers.actionName + if (truncPayload.path("$workflowName-response").has("execute-command-logs")) { + var cmdExecLogNode = truncPayload.path("$workflowName-response") as ObjectNode + cmdExecLogNode.replace("execute-command-logs", "Check Command Executor logs for more information.".asJsonPrimitive()) + } + return ExecutionServiceOutput().apply { + correlationUUID = executionServiceOutput.correlationUUID + commonHeader = executionServiceOutput.commonHeader + actionIdentifiers = executionServiceOutput.actionIdentifiers + status = Status().apply { + code = executionServiceOutput.status.code + eventType = executionServiceOutput.status.eventType + timestamp = executionServiceOutput.status.timestamp + errorMessage = truncErrMsg + message = executionServiceOutput.status.message + } + payload = truncPayload + stepData = executionServiceOutput.stepData + } + } } diff --git a/ms/blueprintsprocessor/modules/commons/message-lib/src/test/kotlin/org/onap/ccsdk/cds/blueprintsprocessor/message/service/BlueprintMessageConsumerServiceTest.kt b/ms/blueprintsprocessor/modules/commons/message-lib/src/test/kotlin/org/onap/ccsdk/cds/blueprintsprocessor/message/service/BlueprintMessageConsumerServiceTest.kt index ac08dc7b7..fdf6e48e7 100644 --- a/ms/blueprintsprocessor/modules/commons/message-lib/src/test/kotlin/org/onap/ccsdk/cds/blueprintsprocessor/message/service/BlueprintMessageConsumerServiceTest.kt +++ b/ms/blueprintsprocessor/modules/commons/message-lib/src/test/kotlin/org/onap/ccsdk/cds/blueprintsprocessor/message/service/BlueprintMessageConsumerServiceTest.kt @@ -214,7 +214,7 @@ open class BlueprintMessageConsumerServiceTest { SslConfigs.SSL_KEYSTORE_LOCATION_CONFIG to "/path/to/keystore.jks", SslConfigs.SSL_KEYSTORE_TYPE_CONFIG to "JKS", SslConfigs.SSL_KEYSTORE_PASSWORD_CONFIG to "secretpassword", - SslConfigs.SSL_ENDPOINT_IDENTIFICATION_ALGORITHM_CONFIG to "", + SslConfigs.SSL_ENDPOINT_IDENTIFICATION_ALGORITHM_CONFIG to SslConfigs.DEFAULT_SSL_ENDPOINT_IDENTIFICATION_ALGORITHM, SaslConfigs.SASL_MECHANISM to "SCRAM-SHA-512", SaslConfigs.SASL_JAAS_CONFIG to "${ScramLoginModule::class.java.canonicalName} required " + "username=\"sample-user\" " + diff --git a/ms/blueprintsprocessor/modules/commons/message-lib/src/test/kotlin/org/onap/ccsdk/cds/blueprintsprocessor/message/service/BlueprintMessageProducerServiceTest.kt b/ms/blueprintsprocessor/modules/commons/message-lib/src/test/kotlin/org/onap/ccsdk/cds/blueprintsprocessor/message/service/BlueprintMessageProducerServiceTest.kt index 72a47ed56..da7394998 100644 --- a/ms/blueprintsprocessor/modules/commons/message-lib/src/test/kotlin/org/onap/ccsdk/cds/blueprintsprocessor/message/service/BlueprintMessageProducerServiceTest.kt +++ b/ms/blueprintsprocessor/modules/commons/message-lib/src/test/kotlin/org/onap/ccsdk/cds/blueprintsprocessor/message/service/BlueprintMessageProducerServiceTest.kt @@ -109,7 +109,7 @@ open class BlueprintMessageProducerServiceTest { SslConfigs.SSL_KEYSTORE_LOCATION_CONFIG to "/path/to/keystore.jks", SslConfigs.SSL_KEYSTORE_TYPE_CONFIG to "JKS", SslConfigs.SSL_KEYSTORE_PASSWORD_CONFIG to "secretpassword", - SslConfigs.SSL_ENDPOINT_IDENTIFICATION_ALGORITHM_CONFIG to "", + SslConfigs.SSL_ENDPOINT_IDENTIFICATION_ALGORITHM_CONFIG to SslConfigs.DEFAULT_SSL_ENDPOINT_IDENTIFICATION_ALGORITHM, SaslConfigs.SASL_MECHANISM to "SCRAM-SHA-512", SaslConfigs.SASL_JAAS_CONFIG to "${ScramLoginModule::class.java.canonicalName} required " + "username=\"sample-user\" " + diff --git a/ms/blueprintsprocessor/modules/inbounds/selfservice-api/src/main/kotlin/org/onap/ccsdk/cds/blueprintsprocessor/selfservice/api/BluePrintProcessingKafkaConsumer.kt b/ms/blueprintsprocessor/modules/inbounds/selfservice-api/src/main/kotlin/org/onap/ccsdk/cds/blueprintsprocessor/selfservice/api/BluePrintProcessingKafkaConsumer.kt index 49f2a50d5..a95af8123 100644 --- a/ms/blueprintsprocessor/modules/inbounds/selfservice-api/src/main/kotlin/org/onap/ccsdk/cds/blueprintsprocessor/selfservice/api/BluePrintProcessingKafkaConsumer.kt +++ b/ms/blueprintsprocessor/modules/inbounds/selfservice-api/src/main/kotlin/org/onap/ccsdk/cds/blueprintsprocessor/selfservice/api/BluePrintProcessingKafkaConsumer.kt @@ -51,13 +51,15 @@ open class BluePrintProcessingKafkaConsumer( companion object { const val CONSUMER_SELECTOR = "self-service-api" + const val PRODUCER_SELECTOR = "self-service-api" } @EventListener(ApplicationReadyEvent::class) fun setupMessageListener() = runBlocking { try { log.info( - "Setting up message consumer($CONSUMER_SELECTOR)" + "Setting up message consumer($CONSUMER_SELECTOR)" + + "message producer($PRODUCER_SELECTOR)..." ) /** Get the Message Consumer Service **/ @@ -72,6 +74,18 @@ open class BluePrintProcessingKafkaConsumer( throw BluePrintProcessorException("failed to create consumer service ${e.message}") } + /** Get the Message Producer Service **/ + val blueprintMessageProducerService = try { + bluePrintMessageLibPropertyService + .blueprintMessageProducerService(PRODUCER_SELECTOR) + } catch (e: BluePrintProcessorException) { + val errorMsg = "Failed creating Kafka producer message service." + throw e.updateErrorMessage(SelfServiceApiDomains.SELF_SERVICE_API, errorMsg, + "Wrong Kafka selector provided or internal error in Kafka service.") + } catch (e: Exception) { + throw BluePrintProcessorException("failed to create producer service ${e.message}") + } + launch { /** Subscribe to the consumer topics */ val additionalConfig: MutableMap<String, Any> = hashMapOf() @@ -82,7 +96,8 @@ open class BluePrintProcessingKafkaConsumer( ph.register() log.trace("Consumed Message : $message") val executionServiceInput = message.jsonAsType<ExecutionServiceInput>() - executionServiceHandler.doProcess(executionServiceInput) + val executionServiceOutput = executionServiceHandler.doProcess(executionServiceInput) + blueprintMessageProducerService.sendMessage(executionServiceOutput) } catch (e: Exception) { log.error("failed in processing the consumed message : $message", e) } finally { @@ -93,7 +108,8 @@ open class BluePrintProcessingKafkaConsumer( } } catch (e: Exception) { log.error( - "failed to start message consumer($CONSUMER_SELECTOR) ", e + "failed to start message consumer($CONSUMER_SELECTOR) " + + "message producer($PRODUCER_SELECTOR) ", e ) } } @@ -102,7 +118,8 @@ open class BluePrintProcessingKafkaConsumer( fun shutdownMessageListener() = runBlocking { try { log.info( - "Shutting down message consumer($CONSUMER_SELECTOR)" + "Shutting down message consumer($CONSUMER_SELECTOR)" + + "message producer($PRODUCER_SELECTOR)..." ) blueprintMessageConsumerService.shutDown() ph.arriveAndAwaitAdvance() diff --git a/ms/blueprintsprocessor/modules/inbounds/selfservice-api/src/main/kotlin/org/onap/ccsdk/cds/blueprintsprocessor/selfservice/api/KafkaPublishAuditService.kt b/ms/blueprintsprocessor/modules/inbounds/selfservice-api/src/main/kotlin/org/onap/ccsdk/cds/blueprintsprocessor/selfservice/api/KafkaPublishAuditService.kt index 129e7a54d..1c5d47c27 100644 --- a/ms/blueprintsprocessor/modules/inbounds/selfservice-api/src/main/kotlin/org/onap/ccsdk/cds/blueprintsprocessor/selfservice/api/KafkaPublishAuditService.kt +++ b/ms/blueprintsprocessor/modules/inbounds/selfservice-api/src/main/kotlin/org/onap/ccsdk/cds/blueprintsprocessor/selfservice/api/KafkaPublishAuditService.kt @@ -24,6 +24,7 @@ import org.onap.ccsdk.cds.blueprintsprocessor.functions.resource.resolution.Reso import org.onap.ccsdk.cds.blueprintsprocessor.message.service.BluePrintMessageLibPropertyService import org.onap.ccsdk.cds.blueprintsprocessor.message.service.BlueprintMessageProducerService import org.onap.ccsdk.cds.controllerblueprints.core.BluePrintProcessorException +import org.onap.ccsdk.cds.controllerblueprints.core.asJsonPrimitive import org.onap.ccsdk.cds.controllerblueprints.core.common.ApplicationConstants import org.onap.ccsdk.cds.controllerblueprints.core.interfaces.BluePrintCatalogService import org.onap.ccsdk.cds.controllerblueprints.core.utils.BluePrintMetadataUtils @@ -48,12 +49,9 @@ class KafkaPublishAuditService( private val bluePrintMessageLibPropertyService: BluePrintMessageLibPropertyService, private val blueprintsProcessorCatalogService: BluePrintCatalogService ) : PublishAuditService { - private var inputInstance: BlueprintMessageProducerService? = null private var outputInstance: BlueprintMessageProducerService? = null - private lateinit var correlationUUID: String - private val log = LoggerFactory.getLogger(KafkaPublishAuditService::class.toString()) companion object { @@ -127,7 +125,8 @@ class KafkaPublishAuditService( correlationUUID = executionServiceInput.correlationUUID commonHeader = executionServiceInput.commonHeader actionIdentifiers = executionServiceInput.actionIdentifiers - payload = executionServiceInput.payload + payload = executionServiceInput.payload.deepCopy() + stepData = executionServiceInput.stepData } val blueprintName = clonedExecutionServiceInput.actionIdentifiers.blueprintName @@ -173,8 +172,7 @@ class KafkaPublishAuditService( sensitiveParameters.forEach { sensitiveParameter -> if (workflowProperties.has(sensitiveParameter)) { - workflowProperties.remove(sensitiveParameter) - workflowProperties.put(sensitiveParameter, ApplicationConstants.LOG_REDACTED) + workflowProperties.replace(sensitiveParameter, ApplicationConstants.LOG_REDACTED.asJsonPrimitive()) } } } diff --git a/ms/command-executor/src/main/docker/Dockerfile b/ms/command-executor/src/main/docker/Dockerfile index e91265b56..e84d5531b 100644 --- a/ms/command-executor/src/main/docker/Dockerfile +++ b/ms/command-executor/src/main/docker/Dockerfile @@ -3,7 +3,7 @@ FROM python:3.6-slim ENV GRPC_PYTHON_VERSION 1.20.0 RUN python -m pip install --upgrade pip RUN pip install grpcio==${GRPC_PYTHON_VERSION} grpcio-tools==${GRPC_PYTHON_VERSION} -RUN pip install virtualenv==16.7.9 +RUN pip install virtualenv==16.7.9 pympler==0.8 RUN groupadd -r onap && useradd -r -g onap onap diff --git a/ms/command-executor/src/main/python/command_executor_handler.py b/ms/command-executor/src/main/python/command_executor_handler.py index 1e6f03b81..0c476b23e 100644 --- a/ms/command-executor/src/main/python/command_executor_handler.py +++ b/ms/command-executor/src/main/python/command_executor_handler.py @@ -43,43 +43,48 @@ class CommandExecutorHandler(): def is_installed(self): return os.path.exists(self.installed) - def prepare_env(self, request, results): + def prepare_env(self, request): + results_log = [] if not self.is_installed(): create_venv_status = self.create_venv() - if not create_venv_status["cds_is_successful"]: - err_msg = "ERROR: failed to prepare environment for request {} due to error in creating virtual Python env. Original error {}".format(self.blueprint_id, create_venv_status["err_msg"]) + if not create_venv_status[utils.CDS_IS_SUCCESSFUL_KEY]: + err_msg = "ERROR: failed to prepare environment for request {} due to error in creating virtual Python env. Original error {}".format(self.blueprint_id, create_venv_status[utils.ERR_MSG_KEY]) self.logger.error(err_msg) - return utils.build_ret_data(False, err_msg) + return utils.build_ret_data(False, error=err_msg) activate_venv_status = self.activate_venv() - if not activate_venv_status["cds_is_successful"]: - err_msg = "ERROR: failed to prepare environment for request {} due Python venv_activation. Original error {}".format(self.blueprint_id, activate_venv_status["err_msg"]) + if not activate_venv_status[utils.CDS_IS_SUCCESSFUL_KEY]: + err_msg = "ERROR: failed to prepare environment for request {} due Python venv_activation. Original error {}".format(self.blueprint_id, activate_venv_status[utils.ERR_MSG_KEY]) self.logger.error(err_msg) - return utils.build_ret_data(False, err_msg) + return utils.build_ret_data(False, error=err_msg) try: with open(self.installed, "w+") as f: - if not self.install_packages(request, CommandExecutor_pb2.pip, f, results): - return utils.build_ret_data(False, "ERROR: failed to prepare environment for request {} during pip package install.".format(self.blueprint_id)) + if not self.install_packages(request, CommandExecutor_pb2.pip, f, results_log): + err_msg = "ERROR: failed to prepare environment for request {} during pip package install.".format(self.blueprint_id) + return utils.build_ret_data(False, results_log=results_log, error=err_msg) f.write("\r\n") # TODO: is \r needed? - results.append("\n") - if not self.install_packages(request, CommandExecutor_pb2.ansible_galaxy, f, results): - return utils.build_ret_data(False, "ERROR: failed to prepare environment for request {} during Ansible install.".format(self.blueprint_id)) + results_log.append("\n") + if not self.install_packages(request, CommandExecutor_pb2.ansible_galaxy, f, results_log): + err_msg = "ERROR: failed to prepare environment for request {} during Ansible install.".format(self.blueprint_id) + return utils.build_ret_data(False, results_log=results_log, error=err_msg) except Exception as ex: err_msg = "ERROR: failed to prepare environment for request {} during installing packages. Exception: {}".format(self.blueprint_id, ex) self.logger.error(err_msg) - return utils.build_ret_data(False, err_msg) + return utils.build_ret_data(False, error=err_msg) else: try: with open(self.installed, "r") as f: - results.append(f.read()) + results_log.append(f.read()) except Exception as ex: - return utils.build_ret_data(False, "ERROR: failed to prepare environment during reading 'installed' file {}. Exception: {}".format(self.installed, ex)) + err_msg="ERROR: failed to prepare environment during reading 'installed' file {}. Exception: {}".format(self.installed, ex) + return utils.build_ret_data(False, error=err_msg) # deactivate_venv(blueprint_id) - return utils.build_ret_data(True, "") + return utils.build_ret_data(True, results_log=results_log) - def execute_command(self, request, results): - payload_result = {} + def execute_command(self, request): + results_log = [] + result = {} # workaround for when packages are not specified, we may not want to go through the install step # can just call create_venv from here. if not self.is_installed(): @@ -87,16 +92,14 @@ class CommandExecutorHandler(): try: if not self.is_installed(): create_venv_status = self.create_venv - if not create_venv_status["cds_is_successful"]: - err_msg = "{} - Failed to execute command during venv creation. Original error: {}".format(self.blueprint_id, create_venv_status["err_msg"]) - results.append(err_msg) - return utils.build_ret_data(False, err_msg) + if not create_venv_status[utils.CDS_IS_SUCCESSFUL_KEY]: + err_msg = "{} - Failed to execute command during venv creation. Original error: {}".format(self.blueprint_id, create_venv_status[utils.ERR_MSG_KEY]) + return utils.build_ret_data(False, error=err_msg) activate_response = self.activate_venv() - if not activate_response["cds_is_successful"]: - orig_error = activate_response["err_msg"] + if not activate_response[utils.CDS_IS_SUCCESSFUL_KEY]: + orig_error = activate_response[utils.ERR_MSG_KEY] err_msg = "{} - Failed to execute command during environment activation. Original error: {}".format(self.blueprint_id, orig_error) - results.append(err_msg) #TODO: get rid of results and just rely on the return data struct. - return utils.build_ret_data(False, err_msg) + return utils.build_ret_data(False, error=err_msg) cmd = "cd " + self.venv_home @@ -131,26 +134,25 @@ class CommandExecutorHandler(): payload = '\n'.join(payload_section) msg = email.parser.Parser().parsestr(payload) for part in msg.get_payload(): - payload_result = json.loads(part.get_payload()) + result = json.loads(part.get_payload()) if output and not is_payload_section: self.logger.info(output.strip()) - results.append(output.strip()) + results_log.append(output.strip()) else: payload_section.append(output.strip()) rc = newProcess.poll() except Exception as e: err_msg = "{} - Failed to execute command. Error: {}".format(self.blueprint_id, e) - self.logger.info(err_msg) - results.append(e) - payload_result.update(utils.build_ret_data(False, err_msg)) - return payload_result + return utils.build_ret_data(False, results=result, results_log=results_log, error=err_msg) # deactivate_venv(blueprint_id) #Since return code is only used to check if it's zero (success), we can just return success flag instead. self.logger.debug("python return_code : {}".format(rc)) - is_execution_successful = rc == 0 - payload_result.update(utils.build_ret_data(is_execution_successful, "")) - return payload_result + if rc == 0: + return utils.build_ret_data(True, results=result, results_log=results_log) + else: + err_msg = "{} - Something wrong happened during command execution. See execute command logs for more information.".format(self.blueprint_id) + return utils.build_ret_data(False, results=result, results_log=results_log, error=err_msg) def install_packages(self, request, type, f, results): success = self.install_python_packages('UTILITY', results) diff --git a/ms/command-executor/src/main/python/command_executor_server.py b/ms/command-executor/src/main/python/command_executor_server.py index 3435e2272..207097605 100644 --- a/ms/command-executor/src/main/python/command_executor_server.py +++ b/ms/command-executor/src/main/python/command_executor_server.py @@ -22,9 +22,6 @@ import proto.CommandExecutor_pb2_grpc as CommandExecutor_pb2_grpc from command_executor_handler import CommandExecutorHandler import utils -_ONE_DAY_IN_SECONDS = 60 * 60 * 24 - - class CommandExecutorServer(CommandExecutor_pb2_grpc.CommandExecutorServiceServicer): def __init__(self): @@ -35,14 +32,14 @@ class CommandExecutorServer(CommandExecutor_pb2_grpc.CommandExecutorServiceServi self.logger.info("{} - Received prepareEnv request".format(blueprint_id)) self.logger.info(request) - results = [] handler = CommandExecutorHandler(request) - prepare_env_response = handler.prepare_env(request, results) - if not prepare_env_response["cds_is_successful"]: - self.logger.info("{} - Failed to prepare python environment. {}".format(blueprint_id, results)) - return utils.build_grpc_response(request, results, {}, False) - self.logger.info("{} - Package installation logs {}".format(blueprint_id, results)) - return utils.build_grpc_response(request, results, {}, True) + prepare_env_response = handler.prepare_env(request) + if prepare_env_response[utils.CDS_IS_SUCCESSFUL_KEY]: + self.logger.info("{} - Package installation logs {}".format(blueprint_id, prepare_env_response[utils.RESULTS_LOG_KEY])) + else: + self.logger.info("{} - Failed to prepare python environment. {}".format(blueprint_id, prepare_env_response[utils.ERR_MSG_KEY])) + self.logger.info("Prepare Env Response returned : %s" % prepare_env_response) + return utils.build_grpc_response(request.requestId, prepare_env_response) def executeCommand(self, request, context): blueprint_id = utils.get_blueprint_id(request) @@ -53,13 +50,15 @@ class CommandExecutorServer(CommandExecutor_pb2_grpc.CommandExecutorServiceServi log_results = [] payload_result = {} handler = CommandExecutorHandler(request) - payload_result = handler.execute_command(request, log_results) - if not payload_result["cds_is_successful"]: - self.logger.info("{} - Failed to executeCommand. {}".format(blueprint_id, log_results)) - else: + exec_cmd_response = handler.execute_command(request) + if exec_cmd_response[utils.CDS_IS_SUCCESSFUL_KEY]: self.logger.info("{} - Execution finished successfully.".format(blueprint_id)) + self.logger.info("{} - Log Results {}: ".format(blueprint_id, exec_cmd_response[utils.RESULTS_LOG_KEY])) + self.logger.info("{} - Results : {}".format(blueprint_id, exec_cmd_response[utils.RESULTS_KEY])) + else: + self.logger.info("{} - Failed to executeCommand. {}".format(blueprint_id, exec_cmd_response[utils.ERR_MSG_KEY])) - ret = utils.build_grpc_response(request, log_results, payload_result, payload_result["cds_is_successful"]) - self.logger.info("Payload returned %s" % payload_result) + ret = utils.build_grpc_response(request.requestId, exec_cmd_response) + self.logger.info("Response returned : {}".format(exec_cmd_response)) return ret
\ No newline at end of file diff --git a/ms/command-executor/src/main/python/utils.py b/ms/command-executor/src/main/python/utils.py index 574be51db..b98241629 100644 --- a/ms/command-executor/src/main/python/utils.py +++ b/ms/command-executor/src/main/python/utils.py @@ -17,7 +17,13 @@ from google.protobuf.timestamp_pb2 import Timestamp import proto.CommandExecutor_pb2 as CommandExecutor_pb2 import json +from pympler import asizeof +CDS_IS_SUCCESSFUL_KEY = "cds_is_successful" +ERR_MSG_KEY = "err_msg" +RESULTS_KEY = "results" +RESULTS_LOG_KEY = "results_log" +TRUNC_MSG_LEN = 3 * 1024 * 1024 def get_blueprint_id(request): blueprint_name = request.identifiers.blueprintName @@ -25,27 +31,47 @@ def get_blueprint_id(request): return blueprint_name + '/' + blueprint_version # Create a response for grpc. Fills in the timestamp as well as removes cds_is_successful element -def build_grpc_response(request, log_results, payload_return, is_success=False): - if is_success: +def build_grpc_response(request_id, response): + if response[CDS_IS_SUCCESSFUL_KEY]: status = CommandExecutor_pb2.SUCCESS + payload = json.dumps(response[RESULTS_KEY]) else: status = CommandExecutor_pb2.FAILURE + # truncate error message if too heavy + if asizeof.asizeof(response[ERR_MSG_KEY]) > TRUNC_MSG_LEN: + response[ERR_MSG_KEY] = "{} [...]. Check command executor logs for more information.".format(response[ERR_MSG_KEY][:TRUNC_MSG_LEN]) + payload = json.dumps(response[ERR_MSG_KEY]) + + # truncate cmd-exec logs if too heavy + response[RESULTS_LOG_KEY] = truncate_cmd_exec_logs(response[RESULTS_LOG_KEY]) timestamp = Timestamp() timestamp.GetCurrentTime() - if "cds_is_successful" in payload_return: - payload_return.pop('cds_is_successful') - payload_str = json.dumps(payload_return) - return CommandExecutor_pb2.ExecutionOutput(requestId=request.requestId, - response=log_results, + return CommandExecutor_pb2.ExecutionOutput(requestId=request_id, + response=response[RESULTS_LOG_KEY], status=status, - payload=payload_str, + payload=payload, timestamp=timestamp) -# build a return data structure which may contain an error message -def build_ret_data(cds_is_successful, err_msg): - ret_data = {"cds_is_successful": cds_is_successful } - if err_msg != "": - ret_data["err_msg"] = err_msg +# build a ret data structure +def build_ret_data(cds_is_successful, results={}, results_log=[], error=None): + ret_data = { + CDS_IS_SUCCESSFUL_KEY: cds_is_successful, + RESULTS_KEY: results, + RESULTS_LOG_KEY: results_log + } + if error: + ret_data[ERR_MSG_KEY] = error return ret_data + +def truncate_cmd_exec_logs(logs): + truncated_logs = [] + truncated_logs_memsize = 0 + for log in logs: + truncated_logs_memsize += asizeof.asizeof(log) + if truncated_logs_memsize > TRUNC_MSG_LEN: + truncated_logs.append("Execution logs exceeds the maximum size allowed. Check command executor logs to view the execute-command-logs.") + break + truncated_logs.append(log) + return truncated_logs
\ No newline at end of file |