Merge "Add output filename param to compresToFilePart"
authorDan Timoney <dtimoney@att.com>
Mon, 26 Aug 2019 19:21:34 +0000 (19:21 +0000)
committerGerrit Code Review <gerrit@onap.org>
Mon, 26 Aug 2019 19:21:34 +0000 (19:21 +0000)
35 files changed:
cds-ui/client/src/app/feature-modules/blueprint/modify-template/editor/editor.component.ts
cds-ui/client/src/app/feature-modules/blueprint/modify-template/editor/editor.service.ts
cds-ui/client/src/app/feature-modules/blueprint/select-template/metadata/metadata.component.ts
cds-ui/client/src/app/feature-modules/blueprint/select-template/search-template/search-from-database/search-from-database.component.html
cds-ui/client/src/app/feature-modules/blueprint/select-template/search-template/search-from-database/search-from-database.component.ts
cds-ui/client/src/app/feature-modules/blueprint/select-template/search-template/search-template.module.ts
cds-ui/client/src/app/feature-modules/blueprint/select-template/select-template.module.ts
cds-ui/client/src/app/feature-modules/blueprint/select-template/select-template.service.ts
cds-ui/server/src/datasources/rest.datasource.json [deleted file]
cds-ui/server/src/datasources/rest.datasource.ts [deleted file]
cds-ui/server/src/services/rest.service.ts [deleted file]
components/model-catalog/blueprint-model/uat-blueprints/pnf_config/Tests/uat.yaml
docs/datadictionary/complexResponse.rst
docs/datadictionary/create_netbox_ip_address.rst
docs/datadictionary/dbsystemcode.rst
docs/datadictionary/dt-netbox-ip.rst
docs/datadictionary/index.rst
docs/datadictionary/resourcedefinitioncodesnip.rst
docs/datadictionary/restauth.rst
docs/microservices/bluePrintsProcessorMS.rst
ms/blueprintsprocessor/application/src/test/kotlin/org/onap/ccsdk/cds/blueprintsprocessor/BlueprintsAcceptanceTest.kt
ms/blueprintsprocessor/application/src/test/kotlin/org/onap/ccsdk/cds/blueprintsprocessor/MoreMatchers.kt [new file with mode: 0644]
ms/blueprintsprocessor/application/src/test/kotlin/org/onap/ccsdk/cds/blueprintsprocessor/UatDefinition.kt
ms/blueprintsprocessor/modules/commons/processor-core/src/main/kotlin/org/onap/ccsdk/cds/blueprintsprocessor/core/ApiDataExtensions.kt [new file with mode: 0644]
ms/blueprintsprocessor/modules/services/execution-service/src/main/kotlin/org/onap/ccsdk/cds/blueprintsprocessor/services/execution/AbstractComponentFunction.kt
ms/blueprintsprocessor/modules/services/workflow-service/src/main/kotlin/org/onap/ccsdk/cds/blueprintsprocessor/services/workflow/ImperativeWorkflowExecutionService.kt
ms/blueprintsprocessor/modules/services/workflow-service/src/main/kotlin/org/onap/ccsdk/cds/blueprintsprocessor/services/workflow/NodeTemplateExecutionService.kt
ms/blueprintsprocessor/modules/services/workflow-service/src/test/kotlin/org/onap/ccsdk/cds/blueprintsprocessor/services/workflow/DGWorkflowExecutionServiceTest.kt
ms/controllerblueprints/modules/blueprint-core/src/main/kotlin/org/onap/ccsdk/cds/controllerblueprints/core/BluePrintConstants.kt
ms/controllerblueprints/modules/blueprint-core/src/main/kotlin/org/onap/ccsdk/cds/controllerblueprints/core/CustomFunctions.kt
ms/controllerblueprints/modules/blueprint-core/src/main/kotlin/org/onap/ccsdk/cds/controllerblueprints/core/data/BluePrintGraph.kt
ms/controllerblueprints/modules/blueprint-core/src/main/kotlin/org/onap/ccsdk/cds/controllerblueprints/core/service/BluePrintContext.kt
ms/controllerblueprints/modules/blueprint-core/src/main/kotlin/org/onap/ccsdk/cds/controllerblueprints/core/service/BluePrintWorkflowService.kt
ms/controllerblueprints/modules/blueprint-core/src/test/kotlin/org/onap/ccsdk/cds/controllerblueprints/core/service/BluePrintWorkflowServiceTest.kt
ms/controllerblueprints/modules/service/src/main/kotlin/org/onap/ccsdk/cds/controllerblueprints/service/controller/ControllerBlueprintExceptionHandler.kt [moved from ms/controllerblueprints/modules/service/src/main/kotlin/org/onap/ccsdk/cds/controllerblueprints/service/controller/ControllerBlueprintExeptionHandler.kt with 78% similarity]

index 7b2eaeb..cfd83f7 100644 (file)
@@ -331,18 +331,7 @@ export class EditorComponent implements OnInit {
   download() {
     console.log(this.artifactName);
     // status = this.editorService.downloadCBA("/download-blueprint/" + this.artifactName + "/" + this.artifactVersion);
-    status = this.editorService.downloadCBA("/"+this.artifactName + "/" + this.artifactVersion);
-    window.alert(status);
-    // .subscribe(response => {
-    //   console.log(response);
-    //   var blob = new Blob([response], { type: 'application/zip' });
-    //   const fileName = 'CBA';
-    //   saveAs(blob, fileName);
-    // },
-    //   error => {
-    //     console.log(error);
-    //   }
-    // );
+     this.editorService.downloadCBA("/"+this.artifactName + "/" + this.artifactVersion);
   }
   
   setEditorMode() {
index 025fc95..f1d1d14 100644 (file)
@@ -1,7 +1,7 @@
 /*
 ============LICENSE_START==========================================
 ===================================================================
-Copyright (C) 2018 IBM Intellectual Property. All rights reserved.
+Copyright (C) 2018-19 IBM Intellectual Property. All rights reserved.
 ===================================================================
 
 Unless otherwise specified, all software contained herein is licensed
@@ -26,10 +26,12 @@ import { Observable, observable } from 'rxjs';
 import { ApiService } from '../../../../common/core/services/api.service';
 import { saveAs } from 'file-saver';
 import { BlueprintURLs } from '../../../../common/constants/app-constants';
+import { NotificationHandlerService } from 'src/app/common/core/services/notification-handler.service';
 
 @Injectable()
 export class EditorService {
-    constructor(private _http: HttpClient, private api: ApiService) {
+    constructor(private _http: HttpClient, private api: ApiService,
+        private alertService: NotificationHandlerService,) {
     }
 
     enrich(body: FormData): Observable<any> {
@@ -40,7 +42,7 @@ export class EditorService {
             .subscribe(response => {
                 let blob = new Blob([response], { 'type': "application/octet-stream" });
                 saveAs(blob, "CBA.zip");
-                window.alert('Blueprint download successfull' );
+                this.alertService.success('Blueprint downloaded successfully' );
             });
         return "Download Success";
 
index 174bdf1..f495170 100644 (file)
@@ -48,7 +48,7 @@ export class MetadataComponent implements OnInit {
   blueprintName: string;
   uploadedFileName: string;
   entryDefinition: string;
-
+  
   constructor(private formBuilder: FormBuilder, private store: Store<IAppState>,
     private loader: LoaderService, private dataService: SelectTemplateService) {
     this.bpState = this.store.select('blueprint');
@@ -60,12 +60,17 @@ export class MetadataComponent implements OnInit {
       template_version: ['', Validators.required],
       template_tags: ['', Validators.required]
     });
+
   }
 
   ngOnInit() {
-    this.dataService.getCbaOption().subscribe(
-      res => {console.log("data from service " + res);}
+    this.dataService.currentMessage.subscribe(
+      res => {
+        let options = res;
+        console.log(options + " data from service ngoninit" + res);
+      }
     );
+    
     this.bpState.subscribe(
       blueprintdata => {
         var blueprintState: IBlueprintState = { blueprint: blueprintdata.blueprint, isLoadSuccess: blueprintdata.isLoadSuccess, isSaveSuccess: blueprintdata.isSaveSuccess, isUpdateSuccess: blueprintdata.isUpdateSuccess };
@@ -76,8 +81,6 @@ export class MetadataComponent implements OnInit {
         this.uploadedFileName = blueprintdata.uploadedFileName;
         this.entryDefinition = blueprintdata.entryDefinition;
 
-
-
         var blueprintState: IBlueprintState = { blueprint: blueprintdata.blueprint, isLoadSuccess: blueprintdata.isLoadSuccess, isSaveSuccess: blueprintdata.isSaveSuccess, isUpdateSuccess: blueprintdata.isUpdateSuccess };
         this.metadata = blueprintState.blueprint.metadata;
         this.blueprint = blueprintState.blueprint;
@@ -99,11 +102,7 @@ export class MetadataComponent implements OnInit {
         });
       })
   }
-ngAfterInit(){
-  this.dataService.getCbaOption().subscribe(
-    res => {console.log("data from service after init" + res);}
-  );
-}
+  
   UploadMetadata() {
     this.loader.showLoader();
     this.metadata = Object.assign({}, this.CBAMetadataForm.value);
index 6482710..9cab6c4 100644 (file)
@@ -41,9 +41,9 @@ limitations under the License.
                     <br>{{option.blueprintModel.updatedBy}}
                 </mat-card-content>
                 <mat-card-actions class="flexBox">
-                    <button matStepperNext mat-menu-item (click)="editCBA(option.blueprintModel.artifactName,option.blueprintModel.artifactVersion,edit)">Edit</button>
-                    <button matStepperNext mat-menu-item (click)="editCBA(option.blueprintModel.artifactName,option.blueprintModel.artifactVersion,clone)">Clone</button>
-                    <button matStepperNext mat-menu-item (click)="editCBA(option.blueprintModel.artifactName,option.blueprintModel.artifactVersion,info)">Info</button>
+                    <button matStepperNext mat-menu-item (click)="editCBA(option.blueprintModel.artifactName,option.blueprintModel.artifactVersion,1)">Edit</button>
+                    <button matStepperNext mat-menu-item (click)="editCBA(option.blueprintModel.artifactName,option.blueprintModel.artifactVersion,2)">Clone</button>
+                    <button matStepperNext mat-menu-item (click)="editCBA(option.blueprintModel.artifactName,option.blueprintModel.artifactVersion,3)">Info</button>
                 </mat-card-actions>
             </mat-card>
         </div>
index 588854f..47771a7 100644 (file)
@@ -33,7 +33,7 @@ import { IBlueprint } from '../../../../../common/core/store/models/blueprint.mo
 import { IBlueprintState } from '../../../../../common/core/store/models/blueprintState.model';
 import { IAppState } from '../../../../../common/core/store/state/app.state';
 import { SetBlueprintState } from '../../../../../common/core/store/actions/blueprint.action';
-
+import { SelectTemplateService } from '../../select-template.service';
 @Component({
   selector: 'app-search-from-database',
   templateUrl: './search-from-database.component.html',
@@ -61,8 +61,8 @@ export class SearchFromDatabaseComponent implements OnInit {
 
   searchText: string = '';
   constructor(private _formBuilder: FormBuilder,
-    private searchService: SearchTemplateService, private alertService: NotificationHandlerService, 
-    private loader: LoaderService, private store: Store<IAppState>) { }
+    private searchService: SearchTemplateService, private alertService: NotificationHandlerService,
+    private loader: LoaderService, private store: Store<IAppState>, private cbEditOption: SelectTemplateService) { }
 
   ngOnInit() {
     this.myControl = this._formBuilder.group({
@@ -85,7 +85,8 @@ export class SearchFromDatabaseComponent implements OnInit {
       })
   }
 
-  editCBA(artifactName: string,artifactVersion:string, option: string) {
+  editCBA(artifactName: string, artifactVersion: string, option: string) {
+    this.cbEditOption.setCbaOption(option);
     this.zipFile.generateAsync({ type: "blob" })
       .then(blob => {
         const formData = new FormData();
index 9bafaeb..a4e30a4 100644 (file)
@@ -26,7 +26,7 @@ import { SearchTemplateComponent } from './search-template.component';
 import { ReactiveFormsModule } from '@angular/forms';
 import { AppMaterialModule } from 'src/app/common/modules/app-material.module';
 import { SharedModule} from 'src/app/common/shared/shared.module';
-import { SelectTemplateService } from 'src/app/feature-modules/blueprint/select-template/select-template.service';
+// import { SelectTemplateService } from 'src/app/feature-modules/blueprint/select-template/select-template.service';
   
 @NgModule({
   declarations: [
@@ -42,6 +42,6 @@ import { SelectTemplateService } from 'src/app/feature-modules/blueprint/select-
     SearchTemplateComponent,
     SearchFromDatabaseComponent
     ],
-  providers:[ SelectTemplateService]
+  // providers:[ SelectTemplateService]
 })
 export class SearchTemplateModule { }
index f66b78c..085da72 100644 (file)
@@ -27,15 +27,15 @@ import { MetadataComponent } from './metadata/metadata.component';
 import { SelectTemplateComponent } from './select-template.component';
 import { SelectTemplateRoutingModule } from './select-template-routing.module';
 import { AppMaterialModule } from 'src/app/common/modules/app-material.module';
-import { SearchTemplateModule} from './search-template/search-template.module';
-
+import { SearchTemplateModule } from './search-template/search-template.module';
+import { SelectTemplateService } from './select-template.service';
 @NgModule({
   declarations: [
     TemplateOptionsComponent,
-     MetadataComponent,
-     SelectTemplateComponent
+    MetadataComponent,
+    SelectTemplateComponent
   ],
-   exports: [
+  exports: [
     TemplateOptionsComponent,
     SearchTemplateComponent,
     MetadataComponent,
@@ -47,6 +47,9 @@ import { SearchTemplateModule} from './search-template/search-template.module';
     ReactiveFormsModule,
     AppMaterialModule,
     SearchTemplateModule
+  ],
+  providers: [
+    SelectTemplateService
   ]
 })
 export class SelectTemplateModule { }
index fa18cbd..d6bcfb3 100644 (file)
@@ -20,21 +20,29 @@ limitations under the License.
 */
 
 import { Injectable } from '@angular/core';
-import { Observable, of } from 'rxjs';
+import { BehaviorSubject } from 'rxjs';
 
 @Injectable({
   providedIn: 'root'
 })
 export class SelectTemplateService {
   cbaOption: string;
+  private messageSource = new BehaviorSubject('default message');
+  currentMessage = this.messageSource.asObservable();
 
   constructor() { }
 
   setCbaOption(option: string) {
-    this.cbaOption = option;
-  }
-
-  getCbaOption(): Observable<string> {
-    return of(this.cbaOption);
+    this.messageSource.next(option);
   }
+  // setCbaOption(option: string) {
+  //   this.cbaOption = option;
+  //   console.log("CBA option set to"+this.cbaOption+":"+option);
+  // }
+
+  // getCbaOption(): Observable<any> {
+  //   console.log("CBA option is "+this.cbaOption);
+  //   // return of(this.cbaOption);
+  //   return this.cbaOption.asObservable(); 
+  // }
 }
diff --git a/cds-ui/server/src/datasources/rest.datasource.json b/cds-ui/server/src/datasources/rest.datasource.json
deleted file mode 100644 (file)
index 86f33fb..0000000
+++ /dev/null
@@ -1,39 +0,0 @@
-{
-   "name": "restConfig",
-   "connector": "rest",
-   "options": {
-      "headers": {
-          "accept": "application/json",
-          "content-type": "application/json"
-      }
-   },
-   "operations": [
-       {
-      "template": {
-         "method": "GET",
-         "url": ""
-      },
-      "functions": {
-         "getEnricheddata": [""]
-      }
-   },
-   {
-      "template": {
-         "method": "POST",
-         "url": ""
-      },
-      "functions": {
-         "saveBlueprint": [""]
-      }
-   },
-   {
-      "template": {
-         "method": "POST",
-         "url": ""
-      },
-      "functions": {
-         "test": [""]
-      }
-   }
-]
-}
\ No newline at end of file
diff --git a/cds-ui/server/src/datasources/rest.datasource.ts b/cds-ui/server/src/datasources/rest.datasource.ts
deleted file mode 100644 (file)
index 1b5a44d..0000000
+++ /dev/null
@@ -1,32 +0,0 @@
-/*
-============LICENSE_START==========================================
-===================================================================
-Copyright (C) 2018-19 IBM Intellectual Property. All rights reserved.
-===================================================================
-
-Unless otherwise specified, all software contained herein is licensed
-under the Apache License, Version 2.0 (the License);
-you may not use this software 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 { juggler } from '@loopback/service-proxy';
-import * as config from './rest.datasource.json';
-
-
-export class RestDataSource extends juggler.DataSource {
-   static dataSourceName = 'rest';
-   constructor(dsConfig: object = config) {
-      super(dsConfig);
-   }
-}
diff --git a/cds-ui/server/src/services/rest.service.ts b/cds-ui/server/src/services/rest.service.ts
deleted file mode 100644 (file)
index b94ea4d..0000000
+++ /dev/null
@@ -1,46 +0,0 @@
-/*
-============LICENSE_START==========================================
-===================================================================
-Copyright (C) 2018-19 IBM Intellectual Property. All rights reserved.
-===================================================================
-
-Unless otherwise specified, all software contained herein is licensed
-under the Apache License, Version 2.0 (the License);
-you may not use this software 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 {getService, juggler} from '@loopback/service-proxy';
-import {inject, Provider} from '@loopback/core';
-import {RestDataSource} from '../datasources/rest.datasource';
-
-
-export interface RestResponseData {
-   userId: number;
-   id: number;
-   title: string;
-   completed: boolean;
-}
-
-export interface RestService {
-   getrestdata(id?: number): Promise<RestResponseData>;
-}
-export class RestProvider implements Provider<RestService> {
-   constructor(
-      @inject('datasources.rest')
-      protected dataSource: juggler.DataSource = new RestDataSource(),
-   ) {}
-   
-   value(): Promise<RestService> {
-      return getService(this.dataSource);
-   }
-}
\ No newline at end of file
index 789659e..3a5903c 100644 (file)
@@ -96,7 +96,8 @@ external-services:
       - request:
           method: PUT
           path: &configUri [ restconf/config, &nodeIdentifier [network-topology:network-topology/topology/topology-netconf/node, *pnfId]]
-          content-type: application/json
+          headers:
+            Content-Type: application/json
           body:
             node:
               - node-id: *pnfId
@@ -124,7 +125,8 @@ external-services:
       - request:
           method: PATCH
           path: [*configUri, *configletResourcePath]
-          content-type: application/yang.patch+json
+          headers:
+            Content-Type: application/yang.patch+json
           body: *assignPatch
       - request:
           method: DELETE
index aa39ff2..d32d13d 100644 (file)
@@ -5,16 +5,17 @@
 complex Response code
 =====================
 
-{
-    "id": 4,
-    "address": "192.168.10.2/32",
-    "vrf": null,
-    "tenant": null,
-    "status": 1,
-    "role": null,
-    "interface": null,
-    "description": "",
-    "nat_inside": null,
-    "created": "2018-08-30",
-    "last_updated": "2018-08-30T14:59:05.277820Z"
-}
+.. code-block:: json
+   :linenos:
+
+   "id": 4,
+   "address": "192.168.10.2/32",
+   "vrf": null,
+   "tenant": null,
+   "status": 1,
+   "role": null,
+   "interface": null,
+   "description": "",
+   "nat_inside": null,
+   "created": "2018-08-30",
+   "last_updated": "2018-08-30T14:59:05.277820Z"
\ No newline at end of file
index 5d9a9be..f17ddb4 100644 (file)
@@ -5,32 +5,35 @@
 create_netbox_ip_address code
 =============================
 
-{
-    "tags" : "oam-local-ipv4-address",
-    "name" : "create_netbox_ip",
-    "property" : {
-      "description" : "netbox ip",
-      "type" : "dt-netbox-ip"
-    },
-    "updated-by" : "adetalhouet",
-    "sources" : {
-      "primary-config-data" : {
-        "type" : "source-rest",
-        "properties" : {
-          "type" : "JSON",
-          "verb" : "POST",
-          "endpoint-selector" : "ipam-1",
-          "url-path" : "/api/ipam/prefixes/$prefixId/available-ips/",
-          "path" : "",
-          "input-key-mapping" : {
-            "prefixId" : "prefix-id"
-          },
-          "output-key-mapping" : {
-            "address" : "address",
-            "id" : "id"
-          },
-          "key-dependencies" : [ "prefix-id" ]
-        }
-      }
-    }
-  }
\ No newline at end of file
+.. code-block:: json
+   :linenos:
+   
+   {
+       "tags" : "oam-local-ipv4-address",
+       "name" : "create_netbox_ip",
+       "property" : {
+         "description" : "netbox ip",
+         "type" : "dt-netbox-ip"
+       },
+       "updated-by" : "adetalhouet",
+       "sources" : {
+         "primary-config-data" : {
+           "type" : "source-rest",
+           "properties" : {
+             "type" : "JSON",
+             "verb" : "POST",
+             "endpoint-selector" : "ipam-1",
+             "url-path" : "/api/ipam/prefixes/$prefixId/available-ips/",
+             "path" : "",
+             "input-key-mapping" : {
+               "prefixId" : "prefix-id"
+             },
+             "output-key-mapping" : {
+               "address" : "address",
+               "id" : "id"
+             },
+             "key-dependencies" : [ "prefix-id" ]
+           }
+         }
+       }
+   }
\ No newline at end of file
index 5051d1e..d6cda5b 100644 (file)
@@ -4,12 +4,14 @@
 
 Dbsystemcode
 ============
-
-"dsl_definitions": {
-  "dynamic-db-source": {
+.. code-block:: json
+   :linenos:
+   
+   "dsl_definitions": {
+   "dynamic-db-source": {
     "type": "maria-db",
     "url": "jdbc:mysql://localhost:3306/sdnctl",
-    "username": "sdnctl",
-    "password": "sdnctl"
-  }
-}
\ No newline at end of file
+    "username": <username>,
+    "password": <password>
+   }
+   }
\ No newline at end of file
index 9410580..6dc3c84 100644 (file)
@@ -5,18 +5,21 @@
 dt-netbox-ip code
 =================
 
-{
-  "version": "1.0.0",
-  "description": "This is Netbox IP Data Type",
-  "properties": {
-    "address": {
-      "required": true,
-      "type": "string"
-    },
-    "id": {
-      "required": true,
-      "type": "integer"
-    }
-  },
-  "derived_from": "tosca.datatypes.Root"
-}
+.. code-block:: none
+   :linenos:
+   
+   {
+     "version": "1.0.0",
+     "description": "This is Netbox IP Data Type",
+     "properties": {
+       "address": {
+         "required": true,
+         "type": "string"
+       },
+       "id": {
+         "required": true,
+         "type": "integer"
+       }
+     },
+     "derived_from": "tosca.datatypes.Root"
+   }
index 3ac8587..4039cca 100644 (file)
@@ -40,7 +40,7 @@ Here is how input-key-mapping, output-key-mapping and key-dependencies can be us
 .. toctree::
    :maxdepth: 1
    
-       resourcedefinitioncodesnip 
+   resourcedefinitioncodesnip 
 
 
 Resource source:
index a917676..6504a07 100644 (file)
@@ -5,44 +5,45 @@
 Source Capability Code
 ======================
 
-{
-  "description": "This is Component Resource Source Node Type",
-  "version": "1.0.0",
-  "properties": {
-    "script-type": {
-      "required": true,
-      "type": "string",
-      "default": "kotlin",
-      "constraints": [
-        {
-          "valid_values": [
-            "kotlin",
-            "jython"
-          ]
-        }
-      ]
-    },
-    "script-class-reference": {
-      "description": "Capability reference name for internal and kotlin, for jython script file path",
-      "required": true,
-      "type": "string"
-    },
-    "instance-dependencies": {
-      "required": false,
-      "description": "Instance dependency Names to Inject to Kotlin / Jython Script.",
-      "type": "list",
-      "entry_schema": {
-        "type": "string"
-      }
-    },
-    "key-dependencies": {
-      "description": "Resource Resolution dependency dictionary names.",
-      "required": true,
-      "type": "list",
-      "entry_schema": {
-        "type": "string"
-      }
-    }
-  },
-  "derived_from": "tosca.nodes.ResourceSource"
-}
+.. code-block:: json
+   :linenos:
+   
+   "description": "This is Component Resource Source Node Type",
+   "version": "1.0.0",
+   "properties": {
+     "script-type": {
+       "required": true,
+       "type": "string",
+       "default": "kotlin",
+       "constraints": [
+         {
+           "valid_values": [
+             "kotlin",
+             "jython"
+           ]
+         }
+       ]
+     },
+     "script-class-reference": {
+       "description": "Capability reference name for internal and kotlin, for jython script file path",
+       "required": true,
+       "type": "string"
+     },
+     "instance-dependencies": {
+       "required": false,
+       "description": "Instance dependency Names to Inject to Kotlin / Jython Script.",
+       "type": "list",
+       "entry_schema": {
+         "type": "string"
+       }
+     },
+     "key-dependencies": {
+       "description": "Resource Resolution dependency dictionary names.",
+       "required": true,
+       "type": "list",
+       "entry_schema": {
+         "type": "string"
+       }
+     }
+   },
+   "derived_from": "tosca.nodes.ResourceSource"
\ No newline at end of file
index 40c1793..9f62869 100644 (file)
@@ -7,36 +7,48 @@ Resource Rest Authentication
 ----------------------------
 
 token-auth:
-
-"dsl_definitions": {
-  "dynamic-rest-source": {
-    "type" : "token-auth",
-    "url" : "http://localhost:32778",
-    "token" : "Token 0123456789abcdef0123456789abcdef01234567"
-  }
-}
-
+~~~~~~~~~~~
+
+.. code-block:: json
+   :linenos:
+   
+   "dsl_definitions": {
+     "dynamic-rest-source": {
+       "type" : "token-auth",
+       "url" : "http://localhost:32778",
+       "token" : <token>
+     }
+   }
+   
 basic-auth:
-
-"dsl_definitions": {
-  "dynamic-rest-source": {
-    "type" : "basic-auth",
-    "url" : "http://localhost:32778",
-    "username" : "bob",
-    "password": "marley"
- }
-}
-
+~~~~~~~~~~~
+
+.. code-block:: json
+   :linenos:
+      
+   "dsl_definitions": {
+     "dynamic-rest-source": {
+       "type" : "basic-auth",
+       "url" : "http://localhost:32778",
+       "username" : <username>,
+       "password": <password>
+    }
+   }
+   
 ssl-basic-auth:
-
-"dsl_definitions": {
-  "dynamic-rest-source": {
-    "type" : "ssl-basic-auth",
-    "url" : "http://localhost:32778",
-    "keyStoreInstance": "JKS or PKCS12",
-    "sslTrust": "trusture",
-    "sslTrustPassword": "trustore password",
-    "sslKey": "keystore",
-    "sslKeyPassword: "keystore password"
- }
-}
\ No newline at end of file
+~~~~~~~~~~~~~~~
+
+.. code-block:: json
+   :linenos:
+      
+   "dsl_definitions": {
+     "dynamic-rest-source": {
+       "type" : "ssl-basic-auth",
+       "url" : "http://localhost:32778",
+       "keyStoreInstance": "JKS or PKCS12",
+       "sslTrust": "trusture",
+       "sslTrustPassword": <password>,
+       "sslKey": "keystore",
+       "sslKeyPassword: <password>
+    }
+   }
\ No newline at end of file
index bd0b699..292f99e 100644 (file)
@@ -84,4 +84,4 @@ Testing the environment:
 
 Point your browser to http://localhost:8000/api/v1/execution-service/ping (please note that the port is 8000, not 8080)
 
-To authenticate, use ccsdkapps / ccsdkapps as login / password.
\ No newline at end of file
+To authenticate, use login user id and password.
\ No newline at end of file
index ad4173c..dfa0a85 100644 (file)
@@ -35,6 +35,7 @@ import org.junit.ClassRule
 import org.junit.Rule
 import org.junit.runner.RunWith
 import org.junit.runners.Parameterized
+import org.mockito.Answers
 import org.onap.ccsdk.cds.blueprintsprocessor.rest.RestLibConstants
 import org.onap.ccsdk.cds.blueprintsprocessor.rest.service.BluePrintRestLibPropertyService
 import org.onap.ccsdk.cds.blueprintsprocessor.rest.service.BlueprintWebClientService
@@ -105,7 +106,7 @@ class BlueprintsAcceptanceTest(private val blueprintName: String, private val fi
     @JvmField
     val springMethodRule = SpringMethodRule()
 
-    @MockBean(name = RestLibConstants.SERVICE_BLUEPRINT_REST_LIB_PROPERTY)
+    @MockBean(name = RestLibConstants.SERVICE_BLUEPRINT_REST_LIB_PROPERTY, answer = Answers.RETURNS_SMART_NULLS)
     lateinit var restClientFactory: BluePrintRestLibPropertyService
 
     @Autowired
@@ -130,10 +131,10 @@ class BlueprintsAcceptanceTest(private val blueprintName: String, private val fi
 
         uploadBlueprint(blueprintName)
 
-        // Configure mocked external services
-        val expectationPerClient = uat.externalServices.associateBy(
+        // Configure mocked external services and save their expected requests for further validation
+        val requestsPerClient = uat.externalServices.associateBy(
                 { service -> createRestClientMock(service.selector, service.expectations) },
-                { service -> service.expectations }
+                { service -> service.expectations.map { it.request } }
         )
 
         // Run processes
@@ -143,14 +144,14 @@ class BlueprintsAcceptanceTest(private val blueprintName: String, private val fi
                     JsonNormalizer.getNormalizer(mapper, process.responseNormalizerSpec))
         }
 
-        // Validate request payloads to external services
-        for ((mockClient, expectations) in expectationPerClient) {
-            expectations.forEach { expectation ->
+        // Validate requests to external services
+        for ((mockClient, requests) in requestsPerClient) {
+            requests.forEach { request ->
                 verify(mockClient, atLeastOnce()).exchangeResource(
-                        eq(expectation.request.method),
-                        eq(expectation.request.path),
-                        argThat { assertJsonEqual(expectation.request.body, this) },
-                        expectation.request.requestHeadersMatcher())
+                        eq(request.method),
+                        eq(request.path),
+                        argThat { assertJsonEqual(request.body, this) },
+                        argThat(RequiredMapEntriesMatcher(request.headers)))
             }
             // Don't mind the invocations to the overloaded exchangeResource(String, String, String)
             verify(mockClient, atLeast(0)).exchangeResource(any(), any(), any())
@@ -160,7 +161,8 @@ class BlueprintsAcceptanceTest(private val blueprintName: String, private val fi
 
     private fun createRestClientMock(selector: String, restExpectations: List<ExpectationDefinition>)
             : BlueprintWebClientService {
-        val restClient = mock<BlueprintWebClientService>(verboseLogging = true)
+        val restClient = mock<BlueprintWebClientService>(verboseLogging = true,
+                defaultAnswer = Answers.RETURNS_SMART_NULLS)
 
         // Delegates to overloaded exchangeResource(String, String, String, Map<String, String>)
         whenever(restClient.exchangeResource(any(), any(), any()))
diff --git a/ms/blueprintsprocessor/application/src/test/kotlin/org/onap/ccsdk/cds/blueprintsprocessor/MoreMatchers.kt b/ms/blueprintsprocessor/application/src/test/kotlin/org/onap/ccsdk/cds/blueprintsprocessor/MoreMatchers.kt
new file mode 100644 (file)
index 0000000..71e07ab
--- /dev/null
@@ -0,0 +1,34 @@
+/*-
+ * ============LICENSE_START=======================================================
+ *  Copyright (C) 2019 Nordix Foundation.
+ * ================================================================================
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ * ============LICENSE_END=========================================================
+ */
+package org.onap.ccsdk.cds.blueprintsprocessor
+
+import com.google.common.collect.Maps
+import org.mockito.ArgumentMatcher
+
+class RequiredMapEntriesMatcher<K, V>(private val requiredEntries: Map<K, V>) : ArgumentMatcher<Map<K, V>> {
+    override fun matches(argument: Map<K, V>?): Boolean {
+        val missingEntries = Maps.difference(requiredEntries, argument).entriesOnlyOnLeft()
+        return missingEntries.isEmpty()
+    }
+
+    override fun toString(): String {
+        return requiredEntries.toString()
+    }
+}
index ce20611..abb1dfc 100644 (file)
@@ -24,8 +24,6 @@ import com.fasterxml.jackson.databind.JsonNode
 import com.fasterxml.jackson.databind.ObjectMapper
 import com.fasterxml.jackson.databind.annotation.JsonDeserialize
 import com.fasterxml.jackson.databind.node.MissingNode
-import com.nhaarman.mockitokotlin2.any
-import com.nhaarman.mockitokotlin2.eq
 import org.yaml.snakeyaml.Yaml
 import java.nio.file.Path
 
@@ -35,13 +33,8 @@ data class ProcessDefinition(val name: String, val request: JsonNode, val expect
 data class RequestDefinition(val method: String,
                              @JsonDeserialize(using = PathDeserializer::class)
                              val path: String,
-                             @JsonAlias("content-type")
-                             val contentType: String? = null,
-                             val body: JsonNode = MissingNode.getInstance()) {
-    fun requestHeadersMatcher(): Map<String, String> {
-        return if (contentType != null) eq(mapOf("Content-Type" to contentType)) else any()
-    }
-}
+                             val headers: Map<String, String> = emptyMap(),
+                             val body: JsonNode = MissingNode.getInstance())
 
 data class ResponseDefinition(val status: Int = 200, val body: JsonNode = MissingNode.getInstance()) {
     companion object {
diff --git a/ms/blueprintsprocessor/modules/commons/processor-core/src/main/kotlin/org/onap/ccsdk/cds/blueprintsprocessor/core/ApiDataExtensions.kt b/ms/blueprintsprocessor/modules/commons/processor-core/src/main/kotlin/org/onap/ccsdk/cds/blueprintsprocessor/core/ApiDataExtensions.kt
new file mode 100644 (file)
index 0000000..47b55b0
--- /dev/null
@@ -0,0 +1,28 @@
+/*
+ *  Copyright Â© 2019 IBM.
+ *
+ *  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.
+ */
+
+package org.onap.ccsdk.cds.blueprintsprocessor.core
+
+import org.onap.ccsdk.cds.blueprintsprocessor.core.api.data.ExecutionServiceInput
+import org.onap.ccsdk.cds.controllerblueprints.core.asType
+import kotlin.reflect.KClass
+
+
+fun <T : Any> ExecutionServiceInput.payloadAsType(clazzType: KClass<T>): T {
+    val actionName = this.actionIdentifiers.actionName
+    val requestJsonNode = this.payload.get("$actionName-request")
+    return requestJsonNode.asType(clazzType.java)
+}
\ No newline at end of file
index 408bb58..8759338 100644 (file)
@@ -19,6 +19,7 @@ package org.onap.ccsdk.cds.blueprintsprocessor.services.execution
 
 
 import com.fasterxml.jackson.databind.JsonNode
+import kotlinx.coroutines.withTimeout
 import org.onap.ccsdk.cds.blueprintsprocessor.core.api.data.ExecutionServiceInput
 import org.onap.ccsdk.cds.blueprintsprocessor.core.api.data.ExecutionServiceOutput
 import org.onap.ccsdk.cds.blueprintsprocessor.core.api.data.Status
@@ -47,6 +48,7 @@ abstract class AbstractComponentFunction : BlueprintFunctionNode<ExecutionServic
     lateinit var interfaceName: String
     lateinit var operationName: String
     lateinit var nodeTemplateName: String
+    var timeout: Int = 180
     var operationInputs: MutableMap<String, JsonNode> = hashMapOf()
 
     override fun getName(): String {
@@ -87,6 +89,9 @@ abstract class AbstractComponentFunction : BlueprintFunctionNode<ExecutionServic
 
         this.operationInputs.putAll(operationResolvedProperties)
 
+        val timeout = this.operationInputs.getOptionalAsInt(BluePrintConstants.PROPERTY_CURRENT_TIMEOUT)
+        timeout?.let { this.timeout = timeout }
+
         return executionRequest
     }
 
@@ -118,7 +123,9 @@ abstract class AbstractComponentFunction : BlueprintFunctionNode<ExecutionServic
     override suspend fun applyNB(executionServiceInput: ExecutionServiceInput): ExecutionServiceOutput {
         try {
             prepareRequestNB(executionServiceInput)
-            processNB(executionServiceInput)
+            withTimeout((timeout * 1000).toLong()) {
+                processNB(executionServiceInput)
+            }
         } catch (runtimeException: RuntimeException) {
             log.error("failed in ${getName()} : ${runtimeException.message}", runtimeException)
             recoverNB(runtimeException, executionServiceInput)
index 2a14be2..6bee17f 100644 (file)
@@ -44,10 +44,8 @@ class ImperativeWorkflowExecutionService(
 
         val graph = bluePrintContext.workflowByName(workflowName).asGraph()
 
-        val deferredOutput = CompletableDeferred<ExecutionServiceOutput>()
-        imperativeBluePrintWorkflowService.executeWorkflow(graph, bluePrintRuntimeService,
-                executionServiceInput, deferredOutput)
-        return deferredOutput.await()
+        return imperativeBluePrintWorkflowService.executeWorkflow(graph, bluePrintRuntimeService,
+                executionServiceInput)
     }
 }
 
@@ -60,35 +58,41 @@ open class ImperativeBluePrintWorkflowService(private val nodeTemplateExecutionS
     lateinit var bluePrintRuntimeService: BluePrintRuntimeService<*>
     lateinit var executionServiceInput: ExecutionServiceInput
     lateinit var workflowName: String
-    lateinit var deferredExecutionServiceOutput: CompletableDeferred<ExecutionServiceOutput>
 
     override suspend fun executeWorkflow(graph: Graph, bluePrintRuntimeService: BluePrintRuntimeService<*>,
-                                         input: ExecutionServiceInput,
-                                         output: CompletableDeferred<ExecutionServiceOutput>) {
+                                         input: ExecutionServiceInput): ExecutionServiceOutput {
         this.graph = graph
         this.bluePrintRuntimeService = bluePrintRuntimeService
         this.executionServiceInput = input
         this.workflowName = this.executionServiceInput.actionIdentifiers.actionName
-        this.deferredExecutionServiceOutput = output
         this.workflowId = bluePrintRuntimeService.id()
+        val output = CompletableDeferred<ExecutionServiceOutput>()
         val startMessage = WorkflowExecuteMessage(input, output)
-        workflowActor().send(startMessage)
+        val workflowActor = workflowActor()
+        if (!workflowActor.isClosedForSend) {
+            workflowActor.send(startMessage)
+        } else {
+            throw BluePrintProcessorException("workflow($workflowActor) actor is closed")
+        }
+        return output.await()
     }
 
     override suspend fun initializeWorkflow(input: ExecutionServiceInput): EdgeLabel {
         return EdgeLabel.SUCCESS
     }
 
-    override suspend fun prepareWorkflowOutput(exception: BluePrintProcessorException?): ExecutionServiceOutput {
-        val wfStatus = if (exception != null) {
-            val status = Status()
-            status.message = BluePrintConstants.STATUS_FAILURE
-            status.errorMessage = exception.message
-            status
-        } else {
-            val status = Status()
-            status.message = BluePrintConstants.STATUS_SUCCESS
-            status
+    override suspend fun prepareWorkflowOutput(): ExecutionServiceOutput {
+        val wfStatus = Status().apply {
+            if (exceptions.isNotEmpty()) {
+                exceptions.forEach {
+                    val errorMessage = it.message ?: ""
+                    bluePrintRuntimeService.getBluePrintError().addError(errorMessage)
+                    log.error("workflow($workflowId) exception :", it)
+                }
+                message = BluePrintConstants.STATUS_FAILURE
+            } else {
+                message = BluePrintConstants.STATUS_SUCCESS
+            }
         }
         return ExecutionServiceOutput().apply {
             commonHeader = executionServiceInput.commonHeader
index 89732e3..b64177a 100644 (file)
@@ -22,7 +22,7 @@ import org.onap.ccsdk.cds.blueprintsprocessor.core.api.data.ExecutionServiceOutp
 import org.onap.ccsdk.cds.blueprintsprocessor.core.api.data.StepData
 import org.onap.ccsdk.cds.blueprintsprocessor.services.execution.AbstractComponentFunction
 import org.onap.ccsdk.cds.controllerblueprints.core.BluePrintConstants
-import org.onap.ccsdk.cds.controllerblueprints.core.putJsonElement
+import org.onap.ccsdk.cds.controllerblueprints.core.asJsonPrimitive
 import org.onap.ccsdk.cds.controllerblueprints.core.service.BluePrintDependencyService
 import org.onap.ccsdk.cds.controllerblueprints.core.service.BluePrintRuntimeService
 import org.slf4j.LoggerFactory
@@ -37,15 +37,22 @@ open class NodeTemplateExecutionService {
                                     executionServiceInput: ExecutionServiceInput): ExecutionServiceOutput {
         // Get the Blueprint Context
         val blueprintContext = bluePrintRuntimeService.bluePrintContext()
+
+        val nodeTemplate = blueprintContext.nodeTemplateByName(nodeTemplateName)
         // Get the Component Name, NodeTemplate type is mapped to Component Name
-        val componentName = blueprintContext.nodeTemplateByName(nodeTemplateName).type
+        val componentName = nodeTemplate.type
 
         val interfaceName = blueprintContext.nodeTemplateFirstInterfaceName(nodeTemplateName)
 
         val operationName = blueprintContext.nodeTemplateFirstInterfaceFirstOperationName(nodeTemplateName)
 
+        val nodeTemplateImplementation = blueprintContext
+                .nodeTemplateOperationImplementation(nodeTemplateName, interfaceName, operationName)
+
+        val timeout: Int = nodeTemplateImplementation?.timeout ?: 180
+
         log.info("executing node template($nodeTemplateName) component($componentName) " +
-                "interface($interfaceName) operation($operationName)")
+                "interface($interfaceName) operation($operationName) with timeout($timeout) sec.")
 
         // Get the Component Instance
         val plugin = BluePrintDependencyService.instance<AbstractComponentFunction>(componentName)
@@ -62,9 +69,10 @@ open class NodeTemplateExecutionService {
 
         // Populate Step Meta Data
         val stepInputs: MutableMap<String, JsonNode> = hashMapOf()
-        stepInputs.putJsonElement(BluePrintConstants.PROPERTY_CURRENT_NODE_TEMPLATE, nodeTemplateName)
-        stepInputs.putJsonElement(BluePrintConstants.PROPERTY_CURRENT_INTERFACE, interfaceName)
-        stepInputs.putJsonElement(BluePrintConstants.PROPERTY_CURRENT_OPERATION, operationName)
+        stepInputs[BluePrintConstants.PROPERTY_CURRENT_NODE_TEMPLATE] = nodeTemplateName.asJsonPrimitive()
+        stepInputs[BluePrintConstants.PROPERTY_CURRENT_INTERFACE] = interfaceName.asJsonPrimitive()
+        stepInputs[BluePrintConstants.PROPERTY_CURRENT_OPERATION] = operationName.asJsonPrimitive()
+        stepInputs[BluePrintConstants.PROPERTY_CURRENT_TIMEOUT] = timeout.asJsonPrimitive()
         val stepInputData = StepData().apply {
             name = nodeTemplateName
             properties = stepInputs
index ac2d7d6..6e7e3cb 100644 (file)
 package org.onap.ccsdk.cds.blueprintsprocessor.services.workflow
 
 import kotlinx.coroutines.runBlocking
+import org.junit.Before
 import org.junit.Test
 import org.junit.runner.RunWith
 import org.onap.ccsdk.cds.blueprintsprocessor.core.api.data.ExecutionServiceInput
 import org.onap.ccsdk.cds.blueprintsprocessor.services.workflow.executor.ComponentExecuteNodeExecutor
 import org.onap.ccsdk.cds.controllerblueprints.core.BluePrintConstants
+import org.onap.ccsdk.cds.controllerblueprints.core.service.BluePrintDependencyService
 import org.onap.ccsdk.cds.controllerblueprints.core.utils.BluePrintMetadataUtils
 import org.onap.ccsdk.cds.controllerblueprints.core.utils.JacksonReactorUtils
 import org.springframework.beans.factory.annotation.Autowired
+import org.springframework.context.ApplicationContext
+import org.springframework.test.annotation.DirtiesContext
 import org.springframework.test.context.ContextConfiguration
 import org.springframework.test.context.junit4.SpringRunner
 import kotlin.test.assertEquals
@@ -35,10 +39,18 @@ import kotlin.test.assertNotNull
 @ContextConfiguration(classes = [WorkflowServiceConfiguration::class, ComponentExecuteNodeExecutor::class])
 class DGWorkflowExecutionServiceTest {
 
+    @Autowired
+    lateinit var applicationContext: ApplicationContext
+
     @Autowired
     lateinit var dgWorkflowExecutionService: DGWorkflowExecutionService
 
 
+    @Before
+    fun init() {
+        BluePrintDependencyService.inject(applicationContext)
+    }
+
     @Test
     fun testExecuteDirectedGraph() {
         runBlocking {
index 064c196..ba5815b 100644 (file)
@@ -180,6 +180,7 @@ object BluePrintConstants {
     const val PROPERTY_CURRENT_NODE_TEMPLATE = "current-node-template"
     const val PROPERTY_CURRENT_INTERFACE = "current-interface"
     const val PROPERTY_CURRENT_OPERATION = "current-operation"
+    const val PROPERTY_CURRENT_TIMEOUT = "current-timeout"
     const val PROPERTY_CURRENT_IMPLEMENTATION = "current-implementation"
     const val PROPERTY_EXECUTION_REQUEST = "execution-request"
 
index 93ba15e..08bc6c3 100644 (file)
@@ -242,6 +242,22 @@ fun Map<String, JsonNode>.getAsDouble(key: String): Double {
     return this[key]?.asDouble() ?: throw BluePrintException("couldn't find value for key($key)")
 }
 
+fun Map<String, JsonNode>.getOptionalAsString(key: String): String? {
+    return if (this.containsKey(key)) this[key]!!.asText() else null
+}
+
+fun Map<String, JsonNode>.getOptionalAsBoolean(key: String): Boolean? {
+    return if (this.containsKey(key)) this[key]!!.asBoolean() else null
+}
+
+fun Map<String, JsonNode>.getOptionalAsInt(key: String): Int? {
+    return if (this.containsKey(key)) this[key]!!.asInt() else null
+}
+
+fun Map<String, JsonNode>.getOptionalAsDouble(key: String): Double? {
+    return if (this.containsKey(key)) this[key]!!.asDouble() else null
+}
+
 // Checks
 
 inline fun checkEquals(value1: String?, value2: String?, lazyMessage: () -> Any): Boolean {
index 066516f..b368c01 100644 (file)
@@ -216,6 +216,11 @@ class BluePrintContext(val serviceTemplate: ServiceTemplate) {
                 ?: throw BluePrintException("could't get NodeTemplate($nodeTemplateName)'s first InterfaceAssignment's first OperationAssignment name")
     }
 
+    fun nodeTemplateOperationImplementation(nodeTemplateName: String, interfaceName: String, operationName: String)
+            : Implementation? {
+        return nodeTemplateInterfaceOperation(nodeTemplateName, interfaceName, operationName).implementation
+    }
+
     fun nodeTemplateInterfaceOperationInputs(nodeTemplateName: String, interfaceName: String, operationName: String): MutableMap<String, JsonNode>? {
         return nodeTemplateInterfaceOperation(nodeTemplateName, interfaceName, operationName).inputs
     }
index 9051502..5cec3c9 100644 (file)
@@ -30,13 +30,12 @@ import kotlin.coroutines.CoroutineContext
 interface BluePrintWorkFlowService<In, Out> {
 
     /** Executes imperative workflow graph [graph] for the bluePrintRuntimeService [bluePrintRuntimeService]
-     * and workflow input [input], response will be retrieve from output [output]*/
-    suspend fun executeWorkflow(graph: Graph, bluePrintRuntimeService: BluePrintRuntimeService<*>,
-                                input: In, output: CompletableDeferred<Out>)
+     * and workflow input [input]*/
+    suspend fun executeWorkflow(graph: Graph, bluePrintRuntimeService: BluePrintRuntimeService<*>, input: In): Out
 
     suspend fun initializeWorkflow(input: In): EdgeLabel
 
-    suspend fun prepareWorkflowOutput(exception: BluePrintProcessorException?): Out
+    suspend fun prepareWorkflowOutput(): Out
 
     /** Prepare the message for the Node */
     suspend fun prepareNodeExecutionMessage(node: Graph.Node): NodeExecuteMessage<In, Out>
@@ -91,6 +90,8 @@ abstract class AbstractBluePrintWorkFlowService<In, Out> : CoroutineScope, BlueP
 
     lateinit var workflowId: String
 
+    var exceptions: MutableList<Exception> = arrayListOf()
+
     final override val coroutineContext: CoroutineContext
         get() = job + CoroutineName("Wf")
 
@@ -100,7 +101,7 @@ abstract class AbstractBluePrintWorkFlowService<In, Out> : CoroutineScope, BlueP
         throw CancellationException("Workflow($workflowId) cancelled as requested")
     }
 
-    fun workflowActor() = actor<WorkflowMessage<In, Out>>(coroutineContext, Channel.UNLIMITED) {
+    suspend fun workflowActor() = actor<WorkflowMessage<In, Out>>(coroutineContext, Channel.UNLIMITED) {
         /** Process the workflow execution message */
         suspend fun executeMessageActor(workflowExecuteMessage: WorkflowExecuteMessage<In, Out>) {
 
@@ -119,13 +120,11 @@ abstract class AbstractBluePrintWorkFlowService<In, Out> : CoroutineScope, BlueP
             // Wait for workflow completion or Error
             nodeActor.invokeOnClose { exception ->
                 launch {
-                    log.info("End Node Completed, processing completion message")
-                    val bluePrintProcessorException: BluePrintProcessorException? =
-                            if (exception != null) BluePrintProcessorException(exception) else null
-
-                    val workflowOutput = prepareWorkflowOutput(bluePrintProcessorException)
+                    if (exception != null) exceptions.add(BluePrintProcessorException(exception))
+                    log.info("workflow($workflowId) nodes completed with (${exceptions.size})exceptions")
+                    val workflowOutput = prepareWorkflowOutput()
                     workflowExecuteMessage.output.complete(workflowOutput)
-                    channel.close(exception)
+                    channel.close()
                 }
             }
         }
@@ -135,7 +134,11 @@ abstract class AbstractBluePrintWorkFlowService<In, Out> : CoroutineScope, BlueP
             when (message) {
                 is WorkflowExecuteMessage<In, Out> -> {
                     launch {
-                        executeMessageActor(message)
+                        try {
+                            executeMessageActor(message)
+                        } catch (e: Exception) {
+                            exceptions.add(e)
+                        }
                     }
                 }
                 is WorkflowRestartMessage<In, Out> -> {
@@ -153,7 +156,7 @@ abstract class AbstractBluePrintWorkFlowService<In, Out> : CoroutineScope, BlueP
     }
 
 
-    private fun nodeActor() = actor<NodeMessage<In, Out>>(coroutineContext, Channel.UNLIMITED) {
+    private suspend fun nodeActor() = actor<NodeMessage<In, Out>>(coroutineContext, Channel.UNLIMITED) {
 
         /** Send message to process from one state to other state */
         fun sendNodeMessage(nodeMessage: NodeMessage<In, Out>) = launch {
@@ -164,7 +167,6 @@ abstract class AbstractBluePrintWorkFlowService<In, Out> : CoroutineScope, BlueP
         fun processNextNodes(node: Graph.Node, nodeState: EdgeLabel) {
             // Process only Next Success Node
             val stateEdges = graph.outgoingEdges(node.id, arrayListOf(nodeState))
-            log.debug("Next Edges :$stateEdges")
             if (stateEdges.isNotEmpty()) {
                 stateEdges.forEach { stateEdge ->
                     // Prepare next node ready message and Send NodeReadyMessage
@@ -213,7 +215,7 @@ abstract class AbstractBluePrintWorkFlowService<In, Out> : CoroutineScope, BlueP
                     }
                     triggerToExecuteOrSkip(newMessage)
                 } else {
-                    log.info("node(${node.id}) waiting for not completed edges($notCompletedEdges)")
+                    log.info("node(${node.id}) is waiting for incoming edges($notCompletedEdges)")
                 }
             } else {
                 triggerToExecuteOrSkip(message)
@@ -233,15 +235,19 @@ abstract class AbstractBluePrintWorkFlowService<In, Out> : CoroutineScope, BlueP
             }
             // Update Node Completed
             node.status = NodeStatus.EXECUTED
-            log.info("Execute Node($node) -> Executed State($nodeState)")
+            log.info("Execute node(${node.id}) -> executed state($nodeState)")
+            // Check if the Node status edge is there, If not close processing
+            val edgePresent = graph.outgoingEdges(node.id, nodeState).isNotEmpty()
 
             // If End Node, Send End Message
             if (graph.isEndNode(node)) {
                 // Close the current channel
                 channel.close()
+            } else if (!edgePresent) {
+                throw BluePrintProcessorException("node(${node.id}) outgoing edge($nodeState) is missing.")
             } else {
                 val skippingEdges = graph.outgoingEdgesNotInLabels(node.id, arrayListOf(nodeState))
-                log.debug("Skipping node($node) outgoing Edges($skippingEdges)")
+                log.debug("Skipping node($node)'s outgoing edges($skippingEdges)")
                 // Process Skip Edges
                 skippingEdges.forEach { skippingEdge ->
                     // Prepare next node ready message and Send NodeReadyMessage
@@ -266,7 +272,7 @@ abstract class AbstractBluePrintWorkFlowService<In, Out> : CoroutineScope, BlueP
                 log.debug("$$$$$ Skipping workflow($workflowId) node($node) $$$$$")
                 // Call the Extension Function
                 val nodeState = skipNode(node, message.nodeInput, message.nodeOutput)
-                log.info("Skip Node($node) -> Executed State($nodeState)")
+                log.info("Skip node(${node.id}) -> executed state($nodeState)")
                 // Mark the Current node as Skipped
                 node.status = NodeStatus.SKIPPED
                 // Look for next possible skip nodes
@@ -283,7 +289,7 @@ abstract class AbstractBluePrintWorkFlowService<In, Out> : CoroutineScope, BlueP
 
         fun cancelNodeWorker(messageWorkflow: WorkflowCancelMessage<In, Out>) = launch {
             channel.close()
-            throw CancellationException("Workflow($workflowId) actor cancelled as requested ...")
+            throw CancellationException("Workflow($workflowId) actor cancelled as requested.")
         }
 
         /** Process each actor message received based on type **/
@@ -294,7 +300,8 @@ abstract class AbstractBluePrintWorkFlowService<In, Out> : CoroutineScope, BlueP
                     try {
                         readyNodeWorker(nodeMessage)
                     } catch (e: Exception) {
-                        channel.close(e)
+                        exceptions.add(e)
+                        channel.close()
                     }
                 }
                 is NodeExecuteMessage<In, Out> -> {
@@ -302,7 +309,9 @@ abstract class AbstractBluePrintWorkFlowService<In, Out> : CoroutineScope, BlueP
                         try {
                             executeNodeWorker(nodeMessage)
                         } catch (e: Exception) {
-                            channel.close(e)
+                            nodeMessage.node.status = NodeStatus.TERMINATED
+                            exceptions.add(e)
+                            channel.close()
                         }
                     }
                 }
@@ -311,7 +320,9 @@ abstract class AbstractBluePrintWorkFlowService<In, Out> : CoroutineScope, BlueP
                         try {
                             skipNodeWorker(nodeMessage)
                         } catch (e: Exception) {
-                            channel.close(e)
+                            nodeMessage.node.status = NodeStatus.TERMINATED
+                            exceptions.add(e)
+                            channel.close()
                         }
                     }
                 }
@@ -320,20 +331,12 @@ abstract class AbstractBluePrintWorkFlowService<In, Out> : CoroutineScope, BlueP
                         try {
                             restartNodeWorker(nodeMessage)
                         } catch (e: Exception) {
-                            channel.close(e)
+                            exceptions.add(e)
+                            channel.close()
                         }
                     }
                 }
             }
         }
     }
-
-    override suspend fun executeWorkflow(graph: Graph, bluePrintRuntimeService: BluePrintRuntimeService<*>,
-                                         input: In, output: CompletableDeferred<Out>) {
-        log.info("Executing Graph : $graph")
-        this.graph = graph
-        this.workflowId = bluePrintRuntimeService.id()
-        val startMessage = WorkflowExecuteMessage(input, output)
-        workflowActor().send(startMessage)
-    }
 }
\ No newline at end of file
index b8d8cea..4d97f8b 100644 (file)
@@ -18,13 +18,13 @@ package org.onap.ccsdk.cds.controllerblueprints.core.service
 
 import io.mockk.every
 import io.mockk.mockk
-import kotlinx.coroutines.CompletableDeferred
-import kotlinx.coroutines.runBlocking
+import kotlinx.coroutines.*
 import org.junit.Test
 import org.onap.ccsdk.cds.controllerblueprints.core.BluePrintException
 import org.onap.ccsdk.cds.controllerblueprints.core.BluePrintProcessorException
 import org.onap.ccsdk.cds.controllerblueprints.core.data.EdgeLabel
 import org.onap.ccsdk.cds.controllerblueprints.core.data.Graph
+import org.onap.ccsdk.cds.controllerblueprints.core.logger
 import org.onap.ccsdk.cds.controllerblueprints.core.toGraph
 import kotlin.test.assertNotNull
 
@@ -36,10 +36,66 @@ class BluePrintWorkflowServiceTest {
                     .toGraph()
             val simpleWorkflow = TestBluePrintWorkFlowService()
             simpleWorkflow.simulatedState = prepareSimulation(arrayListOf("A", "B", "C", "D", "E"), null)
-            val deferredOutput = CompletableDeferred<String>()
             val input = "123456"
-            simpleWorkflow.executeWorkflow(graph, mockBluePrintRuntimeService(), input, deferredOutput)
-            val response = deferredOutput.await()
+            val response = simpleWorkflow.executeWorkflow(graph, mockBluePrintRuntimeService(), input)
+            assertNotNull(response, "failed to get response")
+        }
+    }
+
+    @Test
+    fun testMultipleFlows() {
+        runBlocking {
+            coroutineScope {
+                val wfs = listOf("12345", "12346").map {
+                    async {
+                        val graph = "[START>A/SUCCESS, A>B/SUCCESS, B>C/SUCCESS, C>D/SUCCESS, D>END/SUCCESS]"
+                                .toGraph()
+                        val simpleWorkflow = TestBluePrintWorkFlowService()
+                        simpleWorkflow.simulatedState = prepareSimulation(arrayListOf("A", "B", "C", "D"), null)
+                        val response = simpleWorkflow.executeWorkflow(graph, mockBluePrintRuntimeService(it), it)
+                        assertNotNull(response, "failed to get response")
+                    }
+                }
+                wfs.awaitAll()
+            }
+        }
+    }
+
+    @Test
+    fun testMissingEdgeForBFailureState() {
+        runBlocking {
+            val graph = "[START>A/SUCCESS, A>B/SUCCESS, B>C/SUCCESS, C>D/SUCCESS, D>END/SUCCESS]"
+                    .toGraph()
+            val simpleWorkflow = TestBluePrintWorkFlowService()
+            simpleWorkflow.simulatedState = prepareSimulation(arrayListOf("A", "C", "D", "E"), arrayListOf("B"))
+            val input = "123456"
+            val response = simpleWorkflow.executeWorkflow(graph, mockBluePrintRuntimeService(), input)
+            assertNotNull(response, "failed to get response")
+        }
+    }
+
+    @Test
+    fun testBExceptionFlow() {
+        runBlocking {
+            val graph = "[START>A/SUCCESS, A>B/SUCCESS, B>C/SUCCESS, C>D/SUCCESS, D>END/SUCCESS]"
+                    .toGraph()
+            val simpleWorkflow = TestBluePrintWorkFlowService()
+            simpleWorkflow.simulatedState = prepareSimulation(arrayListOf("A", "C", "D", "E"), null)
+            val input = "123456"
+            val response = simpleWorkflow.executeWorkflow(graph, mockBluePrintRuntimeService(), input)
+            assertNotNull(response, "failed to get response")
+        }
+    }
+
+    @Test
+    fun testTimeoutExceptionFlow() {
+        runBlocking {
+            val graph = "[START>A/SUCCESS, A>TO/SUCCESS, TO>C/SUCCESS, C>D/SUCCESS, D>END/SUCCESS]"
+                    .toGraph()
+            val simpleWorkflow = TestBluePrintWorkFlowService()
+            simpleWorkflow.simulatedState = prepareSimulation(arrayListOf("A", "TO", "C", "D", "E"), null)
+            val input = "123456"
+            val response = simpleWorkflow.executeWorkflow(graph, mockBluePrintRuntimeService(), input)
             assertNotNull(response, "failed to get response")
         }
     }
@@ -51,10 +107,8 @@ class BluePrintWorkflowServiceTest {
                     .toGraph()
             val simpleWorkflow = TestBluePrintWorkFlowService()
             simpleWorkflow.simulatedState = prepareSimulation(arrayListOf("A", "B", "C", "D", "E"), null)
-            val deferredOutput = CompletableDeferred<String>()
             val input = "123456"
-            simpleWorkflow.executeWorkflow(graph, mockBluePrintRuntimeService(), input, deferredOutput)
-            val response = deferredOutput.await()
+            val response = simpleWorkflow.executeWorkflow(graph, mockBluePrintRuntimeService(), input)
             assertNotNull(response, "failed to get response")
         }
     }
@@ -68,10 +122,8 @@ class BluePrintWorkflowServiceTest {
             val failurePathWorkflow = TestBluePrintWorkFlowService()
             failurePathWorkflow.simulatedState = prepareSimulation(arrayListOf("B", "C", "D", "E"),
                     arrayListOf("A"))
-            val failurePathWorkflowDeferredOutput = CompletableDeferred<String>()
             val failurePathWorkflowInput = "123456"
-            failurePathWorkflow.executeWorkflow(failurePatGraph, mockBluePrintRuntimeService(), failurePathWorkflowInput, failurePathWorkflowDeferredOutput)
-            val failurePathResponse = failurePathWorkflowDeferredOutput.await()
+            val failurePathResponse = failurePathWorkflow.executeWorkflow(failurePatGraph, mockBluePrintRuntimeService(), failurePathWorkflowInput)
             assertNotNull(failurePathResponse, "failed to get response")
         }
     }
@@ -83,10 +135,8 @@ class BluePrintWorkflowServiceTest {
                     .toGraph()
             val simpleWorkflow = TestBluePrintWorkFlowService()
             simpleWorkflow.simulatedState = prepareSimulation(arrayListOf("A", "B", "C", "D", "E"), null)
-            val deferredOutput = CompletableDeferred<String>()
             val input = "123456"
-            simpleWorkflow.executeWorkflow(graph, mockBluePrintRuntimeService(), input, deferredOutput)
-            val response = deferredOutput.await()
+            val response = simpleWorkflow.executeWorkflow(graph, mockBluePrintRuntimeService(), input)
             assertNotNull(response, "failed to get response")
         }
     }
@@ -98,17 +148,19 @@ class BluePrintWorkflowServiceTest {
                     .toGraph()
             val simpleWorkflow = TestBluePrintWorkFlowService()
             simpleWorkflow.simulatedState = prepareSimulation(arrayListOf("A", "B", "C", "D"), null)
-            val deferredOutput = CompletableDeferred<String>()
             val input = "123456"
-            simpleWorkflow.executeWorkflow(graph, mockBluePrintRuntimeService(), input, deferredOutput)
-            val response = deferredOutput.await()
+            val response = simpleWorkflow.executeWorkflow(graph, mockBluePrintRuntimeService(), input)
             assertNotNull(response, "failed to get response")
         }
     }
 
     private fun mockBluePrintRuntimeService(): BluePrintRuntimeService<*> {
+        return mockBluePrintRuntimeService("123456")
+    }
+
+    private fun mockBluePrintRuntimeService(id: String): BluePrintRuntimeService<*> {
         val bluePrintRuntimeService = mockk<BluePrintRuntimeService<*>>()
-        every { bluePrintRuntimeService.id() } returns "123456"
+        every { bluePrintRuntimeService.id() } returns id
         return bluePrintRuntimeService
     }
 
@@ -126,6 +178,7 @@ class BluePrintWorkflowServiceTest {
 
 class TestBluePrintWorkFlowService
     : AbstractBluePrintWorkFlowService<String, String>() {
+    val log = logger(TestBluePrintWorkFlowService::class)
 
     lateinit var simulatedState: MutableMap<String, EdgeLabel>
 
@@ -133,6 +186,21 @@ class TestBluePrintWorkFlowService
         return EdgeLabel.SUCCESS
     }
 
+    override suspend fun executeWorkflow(graph: Graph, bluePrintRuntimeService: BluePrintRuntimeService<*>, input: String): String {
+        log.info("Executing Graph : $graph")
+        this.graph = graph
+        this.workflowId = bluePrintRuntimeService.id()
+        val output = CompletableDeferred<String>()
+        val startMessage = WorkflowExecuteMessage(input, output)
+        val workflowActor = workflowActor()
+        if (!workflowActor.isClosedForSend) {
+            workflowActor().send(startMessage)
+        } else {
+            throw BluePrintProcessorException("workflow actor is closed for send $workflowActor")
+        }
+        return startMessage.output.await()
+    }
+
     override suspend fun prepareNodeExecutionMessage(node: Graph.Node)
             : NodeExecuteMessage<String, String> {
         return NodeExecuteMessage(node, "$node Input", "")
@@ -140,23 +208,26 @@ class TestBluePrintWorkFlowService
 
     override suspend fun executeNode(node: Graph.Node, nodeInput: String,
                                      nodeOutput: String): EdgeLabel {
-//        val random = (1..10).random() * 1000
-//        println("will reply in $random ms")
+//        val random = (1..10).random() * 100
+//        log.info("workflow($workflowId) node(${node.id}) will reply in $random ms")
 //        kotlinx.coroutines.delay(random.toLong())
-        val status = simulatedState[node.id] ?: throw BluePrintException("failed to get status for the node($node)")
-        return status
+//        //Simulation for timeout
+        if (node.id == "TO") {
+            withTimeout(1) {
+                kotlinx.coroutines.delay(2)
+            }
+        }
+        return simulatedState[node.id] ?: throw BluePrintException("failed to get status for the node($node)")
     }
 
     override suspend fun prepareNodeSkipMessage(node: Graph.Node): NodeSkipMessage<String, String> {
         val nodeOutput = ""
-        val nodeSkipMessage = NodeSkipMessage(node, "$node Skip Input", nodeOutput)
-        return nodeSkipMessage
+        return NodeSkipMessage(node, "$node Skip Input", nodeOutput)
     }
 
     override suspend fun skipNode(node: Graph.Node, nodeInput: String,
                                   nodeOutput: String): EdgeLabel {
-        val status = simulatedState[node.id] ?: throw BluePrintException("failed to get status for the node($node)")
-        return status
+        return simulatedState[node.id] ?: throw BluePrintException("failed to get status for the node($node)")
     }
 
     override suspend fun cancelNode(node: Graph.Node, nodeInput: String,
@@ -169,7 +240,12 @@ class TestBluePrintWorkFlowService
         TODO("not implemented") //To change body of created functions use File | Settings | File Templates.
     }
 
-    override suspend fun prepareWorkflowOutput(exception: BluePrintProcessorException?): String {
+    override suspend fun prepareWorkflowOutput(): String {
+        if (exceptions.isNotEmpty()) {
+            exceptions.forEach {
+                log.error("workflow($workflowId) exceptions :", it)
+            }
+        }
         return "Final Response"
     }
 }
\ No newline at end of file
@@ -20,6 +20,7 @@ import org.springframework.web.bind.annotation.RestControllerAdvice
 import org.onap.ccsdk.cds.controllerblueprints.core.BluePrintException
 import org.onap.ccsdk.cds.controllerblueprints.core.data.ErrorCode
 import org.onap.ccsdk.cds.controllerblueprints.service.common.ErrorMessage
+import org.slf4j.LoggerFactory
 import org.springframework.http.HttpStatus
 import org.springframework.http.ResponseEntity
 import org.springframework.web.bind.annotation.ExceptionHandler
@@ -32,19 +33,26 @@ import org.springframework.web.bind.annotation.ExceptionHandler
  * @version 1.0
  */
 @RestControllerAdvice("org.onap.ccsdk.cds.controllerblueprints")
-open class ControllerBlueprintExeptionHandler {
+open class ControllerBlueprintExceptionHandler {
+
+    companion object ControllerBlueprintExeptionHandler {
+        val LOG = LoggerFactory.getLogger(ControllerBlueprintExceptionHandler::class.java)
+    }
 
     @ExceptionHandler
-    fun ControllerBlueprintException(e: BluePrintException): ResponseEntity<ErrorMessage> {
+    fun ControllerBlueprintExceptionHandler(e: BluePrintException): ResponseEntity<ErrorMessage> {
         var errorCode = ErrorCode.valueOf(e.code)
         val errorMessage = ErrorMessage(errorCode?.message(e.message!!), errorCode?.value, "ControllerBluePrint_Error_Message")
+        LOG.error("Error: $errorCode ${e.message}")
         return ResponseEntity(errorMessage, HttpStatus.resolve(errorCode!!.httpCode))
     }
 
     @ExceptionHandler
-    fun ControllerBlueprintException(e: Exception): ResponseEntity<ErrorMessage> {
+    fun ControllerBlueprintExceptionHandler(e: Exception): ResponseEntity<ErrorMessage> {
         var errorCode = ErrorCode.GENERIC_FAILURE
         val errorMessage = ErrorMessage(errorCode?.message(e.message!!), errorCode?.value, "ControllerBluePrint_Error_Message")
+        LOG.error("Error: $errorCode ${e.message}")
         return ResponseEntity(errorMessage, HttpStatus.resolve(errorCode!!.httpCode))
     }
-}
\ No newline at end of file
+}
+