added svcapi ui and camunda code

Signed-off-by: Rohan Patel <rp5811@att.com>
Change-Id: I197b4b40fe3d047a417479214e471ae26d51fb2b
diff --git a/otf-service-api/.gitignore b/otf-service-api/.gitignore
new file mode 100644
index 0000000..681073c
--- /dev/null
+++ b/otf-service-api/.gitignore
@@ -0,0 +1,34 @@
+/target/

+tokens/

+out/

+src/main/resources/otf_dev.p12

+/otf/

+

+

+*.log

+

+!.mvn/wrapper/maven-wrapper.jar

+

+### STS ###

+.apt_generated

+.classpath

+.factorypath

+.project

+.settings

+.springBeans

+.sts4-cache

+

+### IntelliJ IDEA ###

+.idea

+*.iws

+*.iml

+*.ipr

+/null/

+

+### NetBeans ###

+/nbproject/private/

+/build/

+/nbbuild/

+/dist/

+/nbdist/

+/.nb-gradle/

diff --git a/otf-service-api/Jenkinsfile b/otf-service-api/Jenkinsfile
new file mode 100644
index 0000000..68e8d66
--- /dev/null
+++ b/otf-service-api/Jenkinsfile
@@ -0,0 +1,169 @@
+#!/usr/bin/env groovy

+

+properties([[$class: 'ParametersDefinitionProperty', parameterDefinitions: [

+        [$class: 'hudson.model.StringParameterDefinition', name: 'PHASE', defaultValue: "BUILD"],

+        [$class: 'hudson.model.StringParameterDefinition', name: 'ENV', defaultValue: "dev"],

+        [$class: 'hudson.model.StringParameterDefinition', name: 'MECHID', defaultValue: "username"],

+        [$class: 'hudson.model.StringParameterDefinition', name: 'KUBE_CONFIG', defaultValue: "kubeConfig-dev"],

+        [$class: 'hudson.model.StringParameterDefinition', name: 'OTF_MONGO_DB', defaultValue: "otf_mongo_dev_db"],

+        [$class: 'hudson.model.StringParameterDefinition', name: 'OTF_CAMUNDA_DB', defaultValue: "otf_camunda_dev_db"],

+        [$class: 'hudson.model.StringParameterDefinition', name: 'TILLER_NAMESPACE', defaultValue: "org-oran-otf"]

+        

+]]])

+

+echo "Build branch: ${env.BRANCH_NAME}"

+

+node("docker") {

+    stage 'Checkout'

+    checkout scm

+    PHASES = PHASE.tokenize('_')

+    echo "PHASES : " + PHASES

+    pom = readMavenPom file: 'pom.xml'

+    ARTIFACT_ID = pom.artifactId

+    VERSION = pom.version

+    LABEL_VERSION = pom.version.replaceAll("\\.", "-")

+    echo "LabelVerion: " + LABEL_VERSION

+    NAMESPACE = pom.groupId

+    echo "Tiller Namespace: " + TILLER_NAMESPACE

+    DOCKER_REGISTRY = pom.properties['docker.registry']

+

+

+

+	if( ENV.equalsIgnoreCase("dev") ){

+	    IMAGE_NAME = pom.properties['docker.registry'] + "/" + NAMESPACE  + "/" + ARTIFACT_ID + ":" + VERSION

+	

+	}

+	if( ENV.equalsIgnoreCase("prod") || ENV.equalsIgnoreCase("prod-dr")){

+	    IMAGE_NAME = pom.properties['docker.registry'] + "/" + NAMESPACE + ".prod" + "/" + ARTIFACT_ID + ":" + VERSION

+	

+	}    

+    if( ENV.equalsIgnoreCase("st") ){

+        IMAGE_NAME = pom.properties['docker.registry'] + "/" + NAMESPACE + ".st" + "/" + ARTIFACT_ID + ":" + VERSION

+    

+    } 

+	

+	echo "Artifact: " + IMAGE_NAME

+

+    withEnv(["PATH=${env.PATH}:${tool 'mvn352'}/bin:${tool 'jdk180'}/bin:${env.WORKSPACE}/linux-amd64", "JAVA_HOME=${tool 'jdk180'}", "MAVEN_HOME=${tool 'mvn352'}", "HELM_HOME=${env.WORKSPACE}"]) {

+

+        echo "JAVA_HOME=${env.JAVA_HOME}"

+        echo "MAVEN_HOME=${env.MAVEN_HOME}"

+        echo "PATH=${env.PATH}"

+        echo "HELM_HOME=${env.HELM_HOME}"

+

+        wrap([$class: 'ConfigFileBuildWrapper', managedFiles: [

+                [fileId: 'maven-settings.xml', variable: 'MAVEN_SETTINGS']

+        ]]) {

+

+

+            if (PHASES.contains("BUILD")) {

+                stage 'Compile'

+                sh 'mvn -s $MAVEN_SETTINGS clean compile'

+

+                //stage 'Unit Test'

+                sh 'mvn -s $MAVEN_SETTINGS test -DskipTests'

+

+                stage 'Package'

+                sh 'mvn -s $MAVEN_SETTINGS package -DskipTests'

+//                sh 'mvn -DskipTests -Dmaven.test.skip=true -s $MAVEN_SETTINGS package'

+

+//                stage 'Verify'

+                sh 'mvn -s $MAVEN_SETTINGS verify -DskipTests'

+

+                stage 'Publish Artifact'

+

+                withCredentials([usernamePassword(credentialsId: MECHID, usernameVariable: 'USERNAME', passwordVariable: 'PASSWORD')]) {

+

+                    echo "Artifact: " + IMAGE_NAME

+

+                    sh """

+						docker login $DOCKER_REGISTRY --username $USERNAME --password $PASSWORD

+						docker build -t $IMAGE_NAME -f target/Dockerfile target

+						docker push $IMAGE_NAME

+					"""

+                }

+

+            }

+            if (PHASES.contains("DEPLOY") || PHASES.contains("UNDEPLOY")) {

+

+                stage 'Init Helm'

+

+                //check if helm exists if not install

+                if (fileExists('linux-amd64/helm')) {

+                    sh """

+						echo "helm is already installed"

+					"""

+                } else {

+                    //download helm

+                    sh """

+						echo "installing helm"

+						wget  https://storage.googleapis.com/kubernetes-helm/helm-v2.8.2-linux-amd64.tar.gz

+						tar -xf helm-v2.8.2-linux-amd64.tar.gz

+						rm helm-v2.8.2-linux-amd64.tar.gz

+					"""

+                }

+

+                withCredentials([file(credentialsId: KUBE_CONFIG, variable: 'KUBECONFIG')]) {

+

+                    dir('helm') {

+                        //check if charts are valid, and then perform dry run, if successful then upgrade/install charts

+

+                        if (PHASES.contains("UNDEPLOY")) {

+                            stage 'Undeploy'

+

+                            sh """

+      							helm delete --tiller-namespace=$TILLER_NAMESPACE --purge $ARTIFACT_ID

+      						"""

+                        }

+

+                        //NOTE Double quotes are used below to access groovy variables like artifact_id and tiller_namespace

+                        if (PHASES.contains("DEPLOY")) {

+                            stage 'Deploy'

+                            withCredentials([

+                                    usernamePassword(credentialsId: OTF_MONGO_DB, usernameVariable: 'USERNAME', passwordVariable: 'PASSWORD'),

+                            ]) {

+                                sh """										

+								    echo "Validate Yaml"

+                                    helm lint $ARTIFACT_ID

+

+                                    echo "View Helm Templates"

+                                    helm template $ARTIFACT_ID \

+                                    	--set appName=$ARTIFACT_ID \

+                                    	--set version=$VERSION \

+                                    	--set image=$IMAGE_NAME\

+                                    	--set env=$ENV \

+                                    	--set otf.mongo.username=$USERNAME \

+                                    	--set otf.mongo.password=$PASSWORD \

+                                    	--set namespace=$TILLER_NAMESPACE

+                                    	

+

+                                    echo "Perform Dry Run Of Install"

+                                    helm upgrade --tiller-namespace=$TILLER_NAMESPACE --install --dry-run $ARTIFACT_ID $ARTIFACT_ID \

+                                    	--set appName=$ARTIFACT_ID \

+                                    	--set version=$VERSION \

+                                    	--set image=$IMAGE_NAME\

+                                    	--set env=$ENV \

+                                    	--set otf.mongo.username=$USERNAME \

+                                    	--set otf.mongo.password=$PASSWORD \

+                                    	--set namespace=$TILLER_NAMESPACE

+

+                                    echo "Helm Install/Upgrade"

+                                    helm upgrade --tiller-namespace=$TILLER_NAMESPACE --install $ARTIFACT_ID $ARTIFACT_ID \

+                                    	--set appName=$ARTIFACT_ID \

+                                    	--set version=$VERSION \

+                                    	--set image=$IMAGE_NAME\

+                                    	--set env=$ENV \

+                                    	--set otf.mongo.username=$USERNAME \

+                                    	--set otf.mongo.password=$PASSWORD \

+                                    	--set namespace=$TILLER_NAMESPACE

+                                    

+								"""

+                            }

+                        }

+

+                    }

+                }

+            }

+        }

+    }

+}
\ No newline at end of file
diff --git a/otf-service-api/LICENSE.txt b/otf-service-api/LICENSE.txt
new file mode 100644
index 0000000..695ac56
--- /dev/null
+++ b/otf-service-api/LICENSE.txt
@@ -0,0 +1,28 @@
+Unless otherwise specified, all software contained herein is licensed

+under the Apache License, Version 2.0 (the "Software License");

+you may not use this software except in compliance with the Software

+License. You may obtain a copy of the Software License at

+

+http://www.apache.org/licenses/LICENSE-2.0

+

+Unless required by applicable law or agreed to in writing, software

+distributed under the Software License is distributed on an "AS IS" BASIS,

+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.

+See the Software License for the specific language governing permissions

+and limitations under the Software License.

+

+

+

+Unless otherwise specified, all documentation contained herein is licensed

+under the Creative Commons License, Attribution 4.0 Intl. (the

+"Documentation License"); you may not use this documentation except in

+compliance with the Documentation License. You may obtain a copy of the

+Documentation License at

+

+https://creativecommons.org/licenses/by/4.0/

+

+Unless required by applicable law or agreed to in writing, documentation

+distributed under the Documentation License is distributed on an "AS IS"

+BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or

+implied. See the Documentation License for the specific language governing

+permissions and limitations under the Documentation License.

diff --git a/otf-service-api/README.txt b/otf-service-api/README.txt
new file mode 100644
index 0000000..b6e4e99
--- /dev/null
+++ b/otf-service-api/README.txt
@@ -0,0 +1,13 @@
+You must setup environment variables to run and test locally

+These environment variables will be secretes when deployed on kubernetes.

+	AAF_ID (mechid for cadi aaf)

+	AAF_PASSWORD (password for mechid)

+	CADI_KEYFILE (cadi keyfile location for aaf)

+		

+		Generate AAF_PASSWORD and CADI_KEYFILE:

+			java -jar cadi-core-1.4.2.jar keygen keyfile

+			java -jar cadi-core-1.4.2.jar digest AAF_MECHID_PASSWORD keyfile  > digest.txt 2>&1

+	

+	AAF_PERM_TYPE (permission type to check for when authorization a user)

+	

+	
\ No newline at end of file
diff --git a/otf-service-api/docker/Dockerfile b/otf-service-api/docker/Dockerfile
new file mode 100644
index 0000000..e0e9e53
--- /dev/null
+++ b/otf-service-api/docker/Dockerfile
@@ -0,0 +1,43 @@
+FROM openjdk:8

+

+ENV NAMESPACE=namespace

+ENV APP_NAME=otf-service-api

+ENV AAF_PERM_TYPE=type

+ENV AAF_ID=username

+ENV AAF_MECH_PASSWORD=password

+ENV AAF_PASSWORD=password

+ENV CADI_KEYFILE=/opt/secret/keyfile

+ENV CADI_HOSTNAME=localhost

+ENV APP_VERSION=1.0

+ENV OTF_MONGO_HOSTS=localhost:27017

+ENV OTF_MONGO_USERNAME=username

+ENV OTF_MONGO_PASSWORD=password

+ENV OTF_MONGO_REPLICASET=mongoOTF

+ENV OTF_MONGO_DATABASE=otf

+ENV otf.camunda.host=https://localhost

+ENV otf.camunda.port=31313

+ENV otf.camunda.executionUri=otf/tcu/execute-test/v1

+ENV otf.camunda.pollingUri=otf/tcu/process-instance-completion-check/v1

+ENV otf.camunda.deploymentUri=otf/tcu/deploy-test-strategy-zip/v1

+ENV otf.camunda.processDefinitionKeyUri=rest/process-definition/key

+ENV otf.camunda.deploymentDeletionUri=otf/tcu/delete-test-strategy/v1/deployment-id

+ENV otf.camunda.testDefinitionDeletionUri=otf/tcu/delete-test-strategy/v1/test-definition-id

+ENV otf.camunda.uri.execute-test=otf/tcu/execute/workflowRequest

+ENV otf.camunda.uri.process-instance-completion-check=otf/tcu/process-instance-completion-check/v1

+ENV otf.camunda.uri.deploy-test-strategy-zip=otf/tcu/deploy-test-strategy-zip/v1

+ENV otf.camunda.uri.process-definition=rest/process-definition/key

+ENV otf.camunda.uri.delete-test-strategy=otf/tcu/delete-test-strategy/v1/deployment-id

+ENV otf.camunda.uri.delete-test-strategy-test-definition-id=otf/tcu/delete-test-strategy/v1/test-definition-id

+ENV otf.camunda.uri.health=/otf/health/v1

+ENV otf.api.poll-interval=6000

+ENV otf.api.poll-attempts=50

+ENV OTF_CERT_PATH=opt/cert/cert.p12

+ENV OTF_CERT_PASS=password

+

+COPY otf-service-api.jar app.jar

+

+RUN mkdir -p /otf/logs

+

+ADD src src

+

+ENTRYPOINT ["java", "-jar", "app.jar"]

diff --git a/otf-service-api/helm/otf-service-api/Chart.yaml b/otf-service-api/helm/otf-service-api/Chart.yaml
new file mode 100644
index 0000000..7c05894
--- /dev/null
+++ b/otf-service-api/helm/otf-service-api/Chart.yaml
@@ -0,0 +1,5 @@
+apiVersion: v1

+appVersion: "1.0"

+description: A Helm chart the OTF TCU Service API

+name: otf-service-api

+version: 0.0.1-SNAPSHOT
\ No newline at end of file
diff --git a/otf-service-api/helm/otf-service-api/templates/deployment.yaml b/otf-service-api/helm/otf-service-api/templates/deployment.yaml
new file mode 100644
index 0000000..3a406d3
--- /dev/null
+++ b/otf-service-api/helm/otf-service-api/templates/deployment.yaml
@@ -0,0 +1,280 @@
+apiVersion: extensions/v1beta1

+kind: Deployment

+metadata:

+  name: {{ .Values.appName}}

+  namespace: {{.Values.namespace}}

+  labels:

+    app: {{ .Values.appName}}

+    version: {{.Values.version}}

+spec:

+  {{if or (eq .Values.env "prod") (eq .Values.env "prod-dr")}}

+  replicas: {{ .Values.replicas.prod}}

+  {{ else if  eq .Values.env "st"}}

+  replicas: {{ .Values.replicas.st}}

+  {{ else }}

+  replicas: {{ .Values.replicas.dev}}

+  {{ end }}

+  selector:

+    matchLabels:

+      app: {{ .Values.appName}}

+      version: {{.Values.version}}

+  template:

+    metadata:

+      labels:

+        app: {{ .Values.appName}}

+        version: {{.Values.version}}

+    spec:

+      revisionHistoryLimit: 1   # keep one replica set to allow rollback

+      minReadySeconds: 10

+      strategy:

+        # indicate which strategy we want for rolling update

+        type: RollingUpdate

+        rollingUpdate:

+          maxSurge: 1

+          maxUnavailable: 1

+      serviceAccount: default

+      volumes:

+      - name: {{ .Values.appName}}-aaf-volume

+        secret:

+          secretName: {{.Values.sharedSecret}}

+      - name: {{ .Values.appName}}-keyfile-volume

+        secret:

+          secretName: {{.Values.sharedSecret}}

+          optional: true

+          items:

+          - key: cadi_keyfile

+            path: keyfile

+      - name: {{ .Values.appName}}-cert-volume

+        secret:

+          secretName: {{.Values.sharedCert}}

+          optional: true

+          items:

+          - key: PKCS12_CERT

+            {{if or (eq .Values.env "prod") (eq .Values.env "prod-dr")}}

+            path: {{ .Values.cert.prod.name | quote }}

+            {{ else if eq  .Values.env "st" }}

+            path: {{ .Values.cert.st.name | quote }}

+            {{ else }}

+            path: {{ .Values.cert.dev.name | quote }}

+            {{ end }}          

+      {{ if or (eq .Values.env "st") (eq .Values.env "prod-dr")}}

+      {{else}}

+      - name: logging-pvc

+        persistentVolumeClaim:

+          {{if eq .Values.env "prod"}}

+          claimName: {{ .Values.pvc.prod | quote }}

+          {{ else }}

+          claimName: {{ .Values.pvc.dev | quote }}

+          {{ end }}

+      {{end}}

+      containers:

+      - name: {{ .Values.appName}}

+        image: {{ .Values.image}}

+        imagePullPolicy: Always

+        ports:

+        - name: https

+          containerPort: 8443

+          nodePort: {{.Values.nodePort}}

+          protocol: TCP

+        {{ if eq .Values.env "st"}}

+        resources:

+          limits: 

+            memory: "3Gi"

+            cpu: "1.8"

+          requests:

+            memory: "2Gi"

+            cpu: "1"

+        {{else}}  

+        resources:

+          limits:

+            memory: "6Gi"

+            cpu: "4"

+          requests:

+            memory: "2Gi"

+            cpu: "1.5"

+        {{ end }}

+        env:

+        - name: NAMESPACE

+          value: {{.Values.namespace}}

+        - name: APP_NAME

+          value: {{ .Values.appName}}

+        - name: AAF_PERM_TYPE

+          {{if or (eq .Values.env "prod") (eq .Values.env "prod-dr")}}

+          value: {{ .Values.aafPermType.prod | quote }}

+          {{ else if  eq .Values.env "st"}}

+          value: {{ .Values.aafPermType.st | quote }}

+          {{ else }}

+          value: {{ .Values.aafPermType.dev | quote }}

+          {{ end }}                 

+        - name: AAF_ID

+          valueFrom:

+            secretKeyRef:

+              name: {{ .Values.sharedSecret}}

+              key: aaf_id

+              optional: true

+        - name: AAF_MECH_PASSWORD

+          valueFrom:

+            secretKeyRef:

+              name: {{ .Values.sharedSecret}}

+              key: aaf_mech_password

+              optional: true

+        - name: AAF_PASSWORD

+          valueFrom:

+            secretKeyRef:

+              name: {{ .Values.sharedSecret}}

+              key: aaf_password

+              optional: true

+        - name: CADI_KEYFILE

+          valueFrom:

+            secretKeyRef:

+              name: {{ .Values.sharedSecret}}

+              key: keyfile_secret_path

+              optional: true

+        - name: CADI_HOSTNAME

+          {{if eq .Values.env "prod"}}

+          value: {{ .Values.cadiHostname.prod | quote }}

+          {{else if  eq .Values.env "prod-dr"}}

+          value: {{ .Values.cadiHostname.prod_dr | quote }}

+          {{else if  eq .Values.env "st"}}

+          value: {{ .Values.cadiHostname.st | quote }} 

+          {{ else }}

+          value: {{ .Values.cadiHostname.dev | quote }}

+          {{ end }}

+        - name: APP_VERSION

+          value: {{.Values.version}}

+        - name: OTF_MONGO_HOSTS

+          {{if or (eq .Values.env "prod") (eq .Values.env "prod-dr")}}

+          value: {{ .Values.otf.mongo.prod.host | quote }}

+          {{ else if eq  .Values.env "st" }}

+          value: {{ .Values.otf.mongo.st.host | quote }}

+          {{ else }}

+          value: {{ .Values.otf.mongo.dev.host | quote }}

+          {{ end }}

+        - name: OTF_MONGO_USERNAME

+          valueFrom:

+            secretKeyRef:

+              name: {{ .Values.appName}}

+              key: mongo_username

+              optional: true

+        - name: OTF_MONGO_PASSWORD

+          valueFrom:

+            secretKeyRef:

+              name: {{ .Values.appName}}

+              key: mongo_password

+              optional: true

+        - name: OTF_MONGO_REPLICASET

+          {{if or (eq .Values.env "prod") (eq .Values.env "prod-dr")}}

+          value: {{ .Values.otf.mongo.prod.replicaSet | quote }}

+          {{else if  eq .Values.env "st"}}

+          value: {{ .Values.otf.mongo.st.replicaSet | quote }}

+          {{ else }}

+          value: {{ .Values.otf.mongo.dev.replicaSet | quote }}

+          {{ end }}

+        - name: OTF_MONGO_DATABASE

+          {{if or (eq .Values.env "prod") (eq .Values.env "prod-dr")}}

+          value: {{ .Values.otf.mongo.prod.database | quote }}

+          {{else if  eq .Values.env "st"}}

+          value: {{ .Values.otf.mongo.st.database | quote }}

+          {{ else }}

+          value: {{ .Values.otf.mongo.dev.database | quote }}

+          {{ end }}

+        - name: otf.camunda.host

+          {{if eq .Values.env "prod"}}

+          value: {{ .Values.otf.camunda.prod.host | quote }}

+          {{ else if eq  .Values.env "prod-dr" }}

+          value: {{ .Values.otf.camunda.prod_dr.host | quote }}

+          {{ else if eq  .Values.env "st" }}

+          value: {{ .Values.otf.camunda.st.host | quote }}

+          {{ else }}

+          value: {{ .Values.otf.camunda.dev.host | quote }}

+          {{ end }}

+        - name: otf.camunda.port

+          {{if eq .Values.env "prod"}}

+          value: {{ .Values.otf.camunda.prod.port | quote }}

+          {{ else if eq  .Values.env "prod-dr" }}

+          value: {{ .Values.otf.camunda.prod_dr.port | quote }}

+          {{ else if eq .Values.env "st"}}

+          value: {{ .Values.otf.camunda.st.port | quote }}

+          {{ else }}

+          value: {{ .Values.otf.camunda.dev.port | quote }}

+          {{ end }}

+        - name: otf.camunda.executionUri

+          value: {{.Values.otf.camunda.executionUri | quote }}

+        - name: otf.camunda.pollingUri

+          value: {{.Values.otf.camunda.pollingUri | quote }}

+        - name: otf.camunda.deploymentUri

+          value: {{.Values.otf.camunda.deploymentUri | quote }}

+        - name: otf.camunda.processDefinitionKeyUri

+          value: {{.Values.otf.camunda.processDefinitionKeyUri | quote }}

+        - name: otf.camunda.deploymentDeletionUri

+          value: {{.Values.otf.camunda.deploymentDeletionUri | quote }}

+        - name: otf.camunda.testDefinitionDeletionUri

+          value: {{.Values.otf.camunda.testDefinitionDeletionUri | quote }}

+

+        - name: otf.camunda.uri.execute-test

+          value: {{.Values.otf.camunda.uri.execute_test | quote }}

+        - name: otf.camunda.uri.process-instance-completion-check

+          value: {{.Values.otf.camunda.uri.process_instance_completion_check | quote }}

+        - name: otf.camunda.uri.deploy-test-strategy-zip

+          value: {{.Values.otf.camunda.uri.deploy_test_strategy_zip | quote }}

+        - name: otf.camunda.uri.process-definition

+          value: {{.Values.otf.camunda.uri.process_definition | quote }}

+        - name: otf.camunda.uri.delete-test-strategy

+          value: {{.Values.otf.camunda.uri.delete_test_strategy | quote }}

+        - name: otf.camunda.uri.delete-test-strategy-test-definition-id

+          value: {{.Values.otf.camunda.uri.delete_test_strategy_test_definition_id | quote }}

+        - name: otf.camunda.uri.health

+          value: {{.Values.otf.camunda.uri.health | quote }}

+

+        - name: otf.api.poll-interval

+          value: {{.Values.otf.api.poll_interval | quote}}

+        - name: otf.api.poll-attempts

+          value: {{.Values.otf.api.poll_attempts | quote}}

+

+        - name: OTF_CERT_PATH

+          {{if or (eq .Values.env "prod") (eq .Values.env "prod-dr")}}

+          value: {{ .Values.cert.prod.path | quote }}

+          {{ else if eq .Values.env "st"}}

+          value: {{ .Values.cert.st.path | quote }}

+          {{ else }}

+          value: {{ .Values.cert.dev.path | quote }}

+          {{ end }}  

+        - name: OTF_CERT_PASS

+          valueFrom:

+            secretKeyRef:

+              name: {{ .Values.sharedCert}}

+              key: PKCS12_KEY

+              optional: true   

+        volumeMounts:

+        - name: {{.Values.appName}}-keyfile-volume

+          mountPath: /opt/secret

+        - name: {{.Values.appName}}-cert-volume

+          mountPath: /opt/cert

+        {{ if or (eq .Values.env "st") (eq .Values.env "prod-dr")}}

+        {{else}}

+        - name: logging-pvc

+          mountPath: "/otf/logs"

+        {{end}} 

+        livenessProbe:

+          httpGet:

+            path: /otf/api/health/v1

+            port: https

+            scheme: HTTPS

+            httpHeaders:

+            - name: X-Custom-Header

+              value: Alive

+          initialDelaySeconds: 30

+          timeoutSeconds: 30

+          periodSeconds: 30

+        readinessProbe:

+          httpGet:

+            path: /otf/api/health/v1

+            port: https

+            scheme: HTTPS

+            httpHeaders:

+            - name: X-Custom-Header

+              value: Ready

+          initialDelaySeconds: 30

+          timeoutSeconds: 30

+          periodSeconds: 30

+      restartPolicy: Always

diff --git a/otf-service-api/helm/otf-service-api/templates/secret.yaml b/otf-service-api/helm/otf-service-api/templates/secret.yaml
new file mode 100644
index 0000000..bc77345
--- /dev/null
+++ b/otf-service-api/helm/otf-service-api/templates/secret.yaml
@@ -0,0 +1,8 @@
+apiVersion: v1

+kind: Secret

+metadata:

+  name: {{ .Values.appName}}

+type: Opaque

+data:

+  mongo_username: {{ .Values.otf.mongo.username | b64enc}}

+  mongo_password: {{ .Values.otf.mongo.password | b64enc}}
\ No newline at end of file
diff --git a/otf-service-api/helm/otf-service-api/templates/service.yaml b/otf-service-api/helm/otf-service-api/templates/service.yaml
new file mode 100644
index 0000000..38acf3d
--- /dev/null
+++ b/otf-service-api/helm/otf-service-api/templates/service.yaml
@@ -0,0 +1,18 @@
+apiVersion: v1

+kind: Service

+metadata:

+  name: {{ .Values.appName }}

+  namespace: {{ .Values.namespace}}

+  labels:

+    app: {{ .Values.appName }}

+    version: {{ .Values.version}}

+spec:

+  type: NodePort

+  ports:

+  - name: https

+    port: 8443

+    protocol: TCP

+    nodePort: {{ .Values.nodePort}}

+  selector:

+    app: {{ .Values.appName }}

+    version: {{ .Values.version}}

diff --git a/otf-service-api/helm/otf-service-api/values.yaml b/otf-service-api/helm/otf-service-api/values.yaml
new file mode 100644
index 0000000..49ee641
--- /dev/null
+++ b/otf-service-api/helm/otf-service-api/values.yaml
@@ -0,0 +1,89 @@
+appName: otf-service-api

+version: 0.0.1-SNAPSHOT

+image: otf-service-api:0.0.1-SNAPSHOT

+namespace: org-oran-otf

+nodePort: 32303

+replicas:

+  dev: 2

+  st: 1

+  prod: 2

+env: dev

+# Environment variables for the service api.

+otf:

+  mongo:

+    dev:

+      host: localhost:27017,localhost:27017,localhost:27017

+      replicaSet: mongoOTF

+      database: otf

+    st:

+      host: localhost:27017,localhost:27017,localhost:27017

+      replicaSet: mongoOTF

+      database: otf_st

+    prod:

+      host: localhost:18720,localhost:18720,localhost:18720

+      replicaSet: otf-rs-prod2

+      database: otf

+    username: "!"

+    password: "!"

+  camunda:

+    dev:

+      host: https://localhost

+      port: 31313

+    st:

+      host: https://localhost

+      port: 31313

+    prod:

+      host: https://localhost

+      port: 31313

+    prod_dr:

+      host: https://localhost

+      port: 31313

+    uri:

+      process_definition: rest/process-definition/key

+      delete_test_strategy: otf/tcu/delete-test-strategy/v1/deployment-id

+      delete_test_strategy_test_definition_id: otf/tcu/delete-test-strategy/v1/test-definition-id

+      execute_test: otf/tcu/execute/workflowRequest

+      deploy_test_strategy_zip: otf/tcu/deploy-test-strategy-zip/v1

+      process_instance_completion_check: otf/tcu/process-instance-completion-check/v1

+      health: /otf/health/v1

+    executionUri: otf/tcu/execute-test/v1

+    pollingUri: otf/tcu/process-instance-completion-check/v1

+    deploymentUri: otf/tcu/deploy-test-strategy-zip/v1

+    processDefinitionKeyUri: rest/process-definition/key

+    deploymentDeletionUri: otf/tcu/delete-test-strategy/v1/deployment-id

+    testDefinitionDeletionUri: otf/tcu/delete-test-strategy/v1/test-definition-id

+  api:

+    poll_interval: 6000

+    poll_attempts: 50

+

+# permission type for aaf

+aafPermType:

+  dev: org.oran.otf.svcapi

+  st:  org.oran.otf.st.svcapi

+  prod:  org.oran.otf.prod.svcapi

+

+cadiHostname:

+  dev: localhost

+  st: localhost

+  prod: localhost

+  prod_dr: localhost

+  

+# Secret related information.

+sharedSecret: otf-aaf-credential-generator

+sharedCert: otf-cert-secret-builder

+cert:

+  dev: 

+    name: otf_dev.p12

+    path: opt/cert/otf_dev.p12

+  st: 

+    name: otf_st.p12

+    path: opt/cert/otf_st.p12

+  prod: 

+    name: otf_prod.p12

+    path: opt/cert/otf_prod.p12

+  

+pvc:

+  dev: org-oran-otf-dev-logs-pv

+  prod: org-oran-otf-prod-logs-pv

+

+

diff --git a/otf-service-api/pom.xml b/otf-service-api/pom.xml
new file mode 100644
index 0000000..fda9ed3
--- /dev/null
+++ b/otf-service-api/pom.xml
@@ -0,0 +1,348 @@
+<?xml version="1.0" encoding="UTF-8"?>

+<project xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"

+  xmlns="http://maven.apache.org/POM/4.0.0"

+  xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">

+  <artifactId>otf-service-api</artifactId>

+  <build>

+    <finalName>otf-service-api</finalName>

+    <plugins>

+      <plugin>

+        <artifactId>maven-compiler-plugin</artifactId>

+        <configuration>

+          <source>1.8</source>

+          <target>1.8</target>

+        </configuration>

+        <groupId>org.apache.maven.plugins</groupId>

+      </plugin>

+      <plugin>

+        <artifactId>spring-boot-maven-plugin</artifactId>

+        <groupId>org.springframework.boot</groupId>

+      </plugin>

+      <plugin>

+        <artifactId>swagger-maven-plugin</artifactId>

+        <configuration>

+          <outputFileName>openapi</outputFileName>

+          <!--<outputPath>${project.build.directory}/generatedtest</outputPath>-->

+          <outputFormat>JSONANDYAML</outputFormat>

+          <prettyPrint>true</prettyPrint>

+          <resourcePackages>

+            <package>org.oran.otf.api</package>

+          </resourcePackages>

+        </configuration>

+        <executions>

+          <execution>

+            <goals>

+              <goal>resolve</goal>

+            </goals>

+            <phase>compile</phase>

+          </execution>

+        </executions>

+        <groupId>io.swagger.core.v3</groupId>

+        <version>2.0.7</version>

+      </plugin>

+

+      <plugin>

+        <groupId>org.apache.maven.plugins</groupId>

+        <artifactId>maven-surefire-plugin</artifactId>

+        <version>2.22.1</version>

+        <configuration>

+          <skipTests>${skipUTs}</skipTests>

+        </configuration>

+      </plugin>

+      <plugin>

+        <groupId>org.apache.maven.plugins</groupId>

+        <artifactId>maven-failsafe-plugin</artifactId>

+        <version>2.22.1</version>

+        <executions>

+          <execution>

+            <id>run-integration-tests</id>

+            <phase>integration-test</phase>

+            <goals>

+              <goal>integration-test</goal>

+              <goal>verify</goal>

+            </goals>

+          </execution>

+        </executions>

+        <configuration>

+          <skipTests>${skipTests}</skipTests>

+          <skipITs>${skipITs}</skipITs>

+        </configuration>

+      </plugin>

+

+    </plugins>

+    <resources>

+      <resource>

+        <directory>src/main/resources</directory>

+        <excludes>

+          <exclude>otf_dev.p12</exclude>

+        </excludes>

+        <filtering>true</filtering>

+        <includes>

+          <include>**/*</include>

+        </includes>

+        <targetPath>${basedir}/target/src/main/resources</targetPath>

+      </resource>

+      <resource>

+        <directory>src/main/resources</directory>

+        <excludes>

+          <exclude>otf_dev.p12</exclude>

+        </excludes>

+        <filtering>true</filtering>

+        <includes>

+          <include>**/*</include>

+        </includes>

+      </resource>

+      <resource>

+        <directory>src/main/resources</directory>

+        <includes>

+          <include>otf_dev.p12</include>

+        </includes>

+        <targetPath>${basedir}/target/src/main/resources</targetPath>

+      </resource>

+      <resource>

+        <directory>src/main/resources</directory>

+        <includes>

+          <include>otf_dev.p12</include>

+        </includes>

+      </resource>

+      <resource>

+        <directory>docker</directory>

+        <includes>

+          <include>Dockerfile</include>

+        </includes>

+        <targetPath>${basedir}/target</targetPath>

+      </resource>

+    </resources>

+  </build>

+  <dependencies>

+    <dependency>

+      <artifactId>spring-boot-starter</artifactId>

+      <groupId>org.springframework.boot</groupId>

+    </dependency>

+

+    <dependency>

+      <artifactId>spring-boot-starter-web</artifactId>

+      <groupId>org.springframework.boot</groupId>

+    </dependency>

+

+    <dependency>

+      <artifactId>spring-boot-starter-jersey</artifactId>

+      <groupId>org.springframework.boot</groupId>

+    </dependency>

+

+

+    <dependency>

+      <groupId>org.glassfish.jersey.test-framework</groupId>

+      <artifactId>jersey-test-framework-core</artifactId>

+      <scope>test</scope>

+    </dependency>

+    <dependency>

+      <groupId>org.glassfish.jersey.test-framework.providers</groupId>

+      <artifactId>jersey-test-framework-provider-grizzly2</artifactId>

+      <scope>test</scope>

+    </dependency>

+    <dependency>

+      <groupId>de.flapdoodle.embed</groupId>

+      <artifactId>de.flapdoodle.embed.mongo</artifactId>

+      <scope>test</scope>

+    </dependency>

+    <dependency>

+      <groupId>com.github.tomakehurst</groupId>

+      <artifactId>wiremock-jre8</artifactId>

+      <version>2.24.0</version>

+      <scope>test</scope>

+    </dependency>

+    <dependency>

+      <groupId>org.mockito</groupId>

+      <artifactId>mockito-core</artifactId>

+      <version>2.15.0</version>

+      <scope>test</scope>

+    </dependency>

+    <dependency>

+      <groupId>org.mockito</groupId>

+      <artifactId>mockito-inline</artifactId>

+      <scope>test</scope>

+    </dependency>

+    <dependency>

+      <groupId>io.rest-assured</groupId>

+      <artifactId>rest-assured</artifactId>

+      <version>4.0.0</version>

+      <scope>test</scope>

+    </dependency>

+    <dependency>

+      <groupId>io.rest-assured</groupId>

+      <artifactId>rest-assured-all</artifactId>

+      <version>4.0.0</version>

+      <scope>test</scope>

+    </dependency>

+

+

+

+    <dependency>

+      <artifactId>spring-boot-starter-test</artifactId>

+      <groupId>org.springframework.boot</groupId>

+      <scope>test</scope>

+      <exclusions>

+        <exclusion>

+          <groupId>com.vaadin.external.google</groupId>

+          <artifactId>android-json</artifactId>

+        </exclusion>

+      </exclusions>

+    </dependency>

+

+    <dependency>

+      <artifactId>spring-boot-starter-data-mongodb</artifactId>

+      <groupId>org.springframework.boot</groupId>

+    </dependency>

+

+    <dependency>

+      <artifactId>swagger-jaxrs2</artifactId>

+      <groupId>io.swagger.core.v3</groupId>

+      <version>2.0.7</version>

+    </dependency>

+

+    <dependency>

+      <artifactId>swagger-jaxrs2-servlet-initializer</artifactId>

+      <groupId>io.swagger.core.v3</groupId>

+      <version>2.0.7</version>

+    </dependency>

+

+    <dependency>

+      <artifactId>swagger-annotations</artifactId>

+      <groupId>io.swagger.core.v3</groupId>

+      <version>2.0.7</version>

+    </dependency>

+

+    <dependency>

+      <artifactId>springfox-swagger2</artifactId>

+      <groupId>io.springfox</groupId>

+      <version>2.9.2</version>

+    </dependency>

+

+    <dependency>

+      <artifactId>springfox-swagger-ui</artifactId>

+      <groupId>io.springfox</groupId>

+      <version>2.9.2</version>

+    </dependency>

+

+    <dependency>

+      <artifactId>springfox-bean-validators</artifactId>

+      <groupId>io.springfox</groupId>

+      <version>2.9.2</version>

+    </dependency>

+

+    <dependency>

+      <artifactId>httpclient</artifactId>

+      <groupId>org.apache.httpcomponents</groupId>

+    </dependency>

+

+    <dependency>

+      <artifactId>h2</artifactId>

+      <groupId>com.h2database</groupId>

+    </dependency>

+

+<!--    <dependency>-->

+<!--      <artifactId>wiremock</artifactId>-->

+<!--      <groupId>com.github.tomakehurst</groupId>-->

+<!--      <version>1.58</version>-->

+<!--    </dependency>-->

+

+    <dependency>

+      <artifactId>gson</artifactId>

+      <groupId>com.google.code.gson</groupId>

+      <version>2.8.5</version>

+    </dependency>

+

+    <!-- CADI AAF Dependencies !! -->

+    <dependency>

+      <artifactId>aaf-auth-client</artifactId>

+      <groupId>org.onap.aaf.authz</groupId>

+      <version>${cadi.version}</version>

+    </dependency>

+

+    <dependency>

+      <artifactId>aaf-cadi-core</artifactId>

+      <groupId>org.onap.aaf.authz</groupId>

+      <version>${cadi.version}</version>

+    </dependency>

+

+    <dependency>

+      <artifactId>aaf-cadi-aaf</artifactId>

+      <groupId>org.onap.aaf.authz</groupId>

+      <version>${cadi.version}</version>

+    </dependency>

+

+    <dependency>

+      <artifactId>json</artifactId>

+      <groupId>org.json</groupId>

+      <version>20180813</version>

+    </dependency>

+

+    <dependency>

+      <artifactId>jackson-annotations</artifactId>

+      <groupId>com.fasterxml.jackson.core</groupId>

+    </dependency>

+

+    <dependency>

+      <artifactId>jackson-core</artifactId>

+      <groupId>com.fasterxml.jackson.core</groupId>

+    </dependency>

+

+    <dependency>

+      <artifactId>jackson-databind</artifactId>

+      <groupId>com.fasterxml.jackson.core</groupId>

+    </dependency>

+

+    <dependency>

+      <artifactId>jersey-media-multipart</artifactId>

+      <groupId>org.glassfish.jersey.media</groupId>

+      <version>2.27</version>

+    </dependency>

+

+    <dependency>

+      <artifactId>httpmime</artifactId>

+      <groupId>org.apache.httpcomponents</groupId>

+      <version>4.5.7-SNAPSHOT</version>

+    </dependency>

+

+    <dependency>

+      <artifactId>httpasyncclient</artifactId>

+      <groupId>org.apache.httpcomponents</groupId>

+      <version>4.1.4</version>

+    </dependency>

+      <dependency>

+          <groupId>net.java.dev.jna</groupId>

+          <artifactId>jna-platform</artifactId>

+      </dependency>

+  </dependencies>

+

+  <description>Service API - OTF</description>

+  <groupId>org.oran.otf</groupId>

+

+  <modelVersion>4.0.0</modelVersion>

+

+  <name>otf-service-api</name>

+

+  <packaging>jar</packaging>

+

+  <parent>

+    <artifactId>spring-boot-starter-parent</artifactId>

+    <groupId>org.springframework.boot</groupId>

+    <version>2.1.3.RELEASE</version>

+  </parent>

+

+  <properties>

+    <skipTests>false</skipTests>

+    <skipITs>${skipTests}</skipITs>

+    <skipUTs>${skipTests}</skipUTs>

+    <cadi.version>2.1.10</cadi.version>

+    <docker.registry>registry.hub.docker.io</docker.registry>

+    <java.version>1.8</java.version>

+    <namespace>org.oran.otf</namespace>

+    <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>

+    <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>

+  </properties>

+  <version>Camille.1.0</version>

+

+

+</project>
\ No newline at end of file
diff --git a/otf-service-api/src/main/java/org/oran/otf/api/Application.java b/otf-service-api/src/main/java/org/oran/otf/api/Application.java
new file mode 100644
index 0000000..8836555
--- /dev/null
+++ b/otf-service-api/src/main/java/org/oran/otf/api/Application.java
@@ -0,0 +1,74 @@
+/*  Copyright (c) 2019 AT&T Intellectual Property.                             #

+#                                                                              #

+#   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.oran.otf.api;

+

+import static com.google.common.collect.Sets.newHashSet;

+

+import io.swagger.v3.oas.annotations.OpenAPIDefinition;

+import io.swagger.v3.oas.annotations.info.Contact;

+import io.swagger.v3.oas.annotations.info.Info;

+import org.apache.commons.logging.Log;

+import org.apache.commons.logging.LogFactory;

+import org.springframework.boot.SpringApplication;

+import org.springframework.boot.autoconfigure.EnableAutoConfiguration;

+import org.springframework.boot.autoconfigure.SpringBootApplication;

+import org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration;

+import org.springframework.boot.autoconfigure.orm.jpa.HibernateJpaAutoConfiguration;

+import org.springframework.boot.autoconfigure.web.servlet.error.ErrorMvcAutoConfiguration;

+import org.springframework.context.ApplicationContext;

+import org.springframework.context.annotation.Bean;

+import org.springframework.context.annotation.ComponentScan;

+import org.springframework.context.annotation.Configuration;

+import springfox.documentation.builders.PathSelectors;

+import springfox.documentation.spi.DocumentationType;

+import springfox.documentation.spring.web.plugins.Docket;

+import springfox.documentation.swagger2.annotations.EnableSwagger2;

+

+@SpringBootApplication

+@Configuration

+@EnableAutoConfiguration(

+    exclude = {

+      ErrorMvcAutoConfiguration.class,

+      DataSourceAutoConfiguration.class,

+      HibernateJpaAutoConfiguration.class,

+    })

+@ComponentScan(basePackages = "org.oran.otf")

+@EnableSwagger2

+@OpenAPIDefinition(

+    info =

+        @Info(

+            title = "Open Test Framework API",

+            version = "1.0",

+            description = "A RESTful API used to communicate with the OTF test control unit.",

+            contact = @Contact(url = "https://localhost:32524", name = "OTF")))

+public class Application {

+  private static final Log log = LogFactory.getLog(Application.class);

+

+  public static void main(String[] args) {

+    ApplicationContext ctx = SpringApplication.run(Application.class, args);

+  }

+

+  @Bean

+  public Docket testInstanceApi() {

+    return new Docket(DocumentationType.SWAGGER_2)

+        .select()

+        // .apis(testInstancePath())

+        .paths(PathSelectors.any())

+        .build()

+        .protocols(newHashSet("https"));

+  }

+}

diff --git a/otf-service-api/src/main/java/org/oran/otf/api/Utilities.java b/otf-service-api/src/main/java/org/oran/otf/api/Utilities.java
new file mode 100644
index 0000000..1279688
--- /dev/null
+++ b/otf-service-api/src/main/java/org/oran/otf/api/Utilities.java
@@ -0,0 +1,394 @@
+/*  Copyright (c) 2019 AT&T Intellectual Property.                             #

+#                                                                              #

+#   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.oran.otf.api;

+

+import org.oran.otf.common.model.User;

+import org.oran.otf.common.model.local.OTFApiResponse;

+import org.oran.otf.common.repository.UserRepository;

+import com.fasterxml.jackson.databind.DeserializationFeature;

+import com.fasterxml.jackson.databind.ObjectMapper;

+import com.google.common.base.Strings;

+import com.google.gson.JsonObject;

+import com.google.gson.JsonParseException;

+import com.google.gson.JsonParser;

+import java.io.IOException;

+import java.io.PrintWriter;

+import java.io.StringWriter;

+import java.util.Arrays;

+import java.util.Base64;

+import java.util.Date;

+import java.util.List;

+import java.util.Map;

+import java.util.Optional;

+import javax.ws.rs.core.MediaType;

+import javax.ws.rs.core.Response;

+import org.apache.http.HttpEntity;

+import org.apache.http.HttpResponse;

+import org.apache.http.NameValuePair;

+import org.apache.http.client.HttpClient;

+import org.apache.http.client.entity.UrlEncodedFormEntity;

+import org.apache.http.client.methods.HttpDelete;

+import org.apache.http.client.methods.HttpGet;

+import org.apache.http.client.methods.HttpPost;

+import org.apache.http.conn.ssl.NoopHostnameVerifier;

+import org.apache.http.entity.StringEntity;

+import org.apache.http.impl.client.HttpClientBuilder;

+import org.apache.http.message.BasicNameValuePair;

+import org.apache.http.util.EntityUtils;

+import org.bson.types.ObjectId;

+import org.slf4j.Logger;

+import org.slf4j.LoggerFactory;

+import org.springframework.data.mongodb.repository.MongoRepository;

+

+public class Utilities {

+

+  public static JsonObject parseJson(String str) {

+    try {

+      return new JsonParser().parse(str).getAsJsonObject();

+    } catch (JsonParseException jpe) {

+      logger.error("Cannot parse string as Json.");

+      return null;

+    }

+  }

+

+  public static class Http {

+    public static class BuildResponse {

+      public static Response badRequest() {

+        return Response.status(400).build();

+      }

+

+      public static Response badRequestWithMessage(String msg) {

+        return Response.status(400)

+            .type(MediaType.APPLICATION_JSON)

+            .entity(new OTFApiResponse(400, msg))

+            .build();

+      }

+

+      public static Response internalServerError() {

+        return Response.status(500).build();

+      }

+

+      public static Response internalServerErrorWithMessage(String msg) {

+        return Response.status(500)

+            .type(MediaType.APPLICATION_JSON)

+            .entity(new OTFApiResponse(500, msg))

+            .build();

+      }

+

+      public static Response unauthorized() {

+        return Response.status(401).build();

+      }

+

+      public static Response unauthorizedWithMessage(String msg) {

+        return Response.status(401)

+            .type(MediaType.APPLICATION_JSON)

+            .entity(new OTFApiResponse(401, msg))

+            .build();

+      }

+    }

+

+    public static HttpResponse httpPostJsonUsingAAF(String url, String body) throws Exception {

+      HttpResponse response = null;

+

+        String aafCredentialsDecoded =

+            System.getenv("AAF_ID") + ":" + System.getenv("AAF_MECH_PASSWORD");

+

+        HttpPost post = new HttpPost(url);

+        post.setHeader("Content-Type", MediaType.APPLICATION_JSON);

+        post.setHeader(

+            "Authorization",

+            "Basic " + Base64.getEncoder().encodeToString(aafCredentialsDecoded.getBytes()));

+        post.setEntity(new StringEntity(body));

+

+        HttpClient client =

+            HttpClientBuilder.create()

+                .setSSLHostnameVerifier(NoopHostnameVerifier.INSTANCE)

+                .build();

+        response = client.execute(post);

+

+        // logger.info(String.format("[POST:%s]\n %s", url, body));

+

+      return response;

+    }

+

+    public static HttpResponse httpDeleteAAF(String url) {

+      HttpResponse response = null;

+

+      try {

+        String aafCredentialsDecoded =

+            System.getenv("AAF_ID") + ":" + System.getenv("AAF_MECH_PASSWORD");

+

+        HttpDelete delete = new HttpDelete(url);

+        delete.setHeader(

+            "Authorization",

+            "Basic " + Base64.getEncoder().encodeToString(aafCredentialsDecoded.getBytes()));

+        HttpClient client =

+            HttpClientBuilder.create()

+                .setSSLHostnameVerifier(NoopHostnameVerifier.INSTANCE)

+                .build();

+        response = client.execute(delete);

+

+        // logger.info(String.format("[DELETE:%s]\n", url));

+      } catch (Exception e) {

+        e.printStackTrace();

+      }

+

+      return response;

+    }

+

+    public static HttpResponse httpPostXmlUsingAAF(String url, String body) {

+      HttpResponse response = null;

+

+      try {

+        String aafCredentialsDecoded =

+            System.getenv("AAF_ID") + ":" + System.getenv("AAF_MECH_PASSWORD");

+

+        HttpPost post = new HttpPost(url);

+        post.setHeader("Content-Type", MediaType.APPLICATION_JSON);

+        post.setHeader(

+            "Authorization",

+            "Basic " + Base64.getEncoder().encodeToString(aafCredentialsDecoded.getBytes()));

+        post.setEntity(new StringEntity(body));

+

+        List<NameValuePair> urlParameters = Arrays.asList(new BasicNameValuePair("xml", body));

+        post.setEntity(new UrlEncodedFormEntity(urlParameters));

+

+        HttpClient client = HttpClientBuilder.create().build();

+        response = client.execute(post);

+

+        logger.info(String.format("[POST:%s]\n %s", url, body));

+      } catch (Exception e) {

+        e.printStackTrace();

+      }

+

+      return response;

+    }

+

+    public static HttpResponse httpGetUsingAAF(String url) {

+      HttpResponse response = null;

+

+      try {

+        String aafCredentialsDecoded =

+            System.getenv("AAF_ID") + ":" + System.getenv("AAF_MECH_PASSWORD");

+

+        HttpGet get = new HttpGet(url);

+        get.setHeader("Content-Type", "application/json");

+        get.setHeader(

+            "Authorization",

+            "Basic " + Base64.getEncoder().encodeToString(aafCredentialsDecoded.getBytes()));

+

+        HttpClient client =

+            HttpClientBuilder.create()

+                .setSSLHostnameVerifier(NoopHostnameVerifier.INSTANCE)

+                .build();

+        response = client.execute(get);

+

+        logger.info(String.format("[GET:%s]", url));

+      } catch (Exception e) {

+        e.printStackTrace();

+      }

+

+      return response;

+    }

+  }

+

+  public static class Camunda {

+

+    public static boolean isCamundaOnline() {

+      final String healthUrl =

+          String.format(

+              "%s:%s/%s",

+              System.getenv("otf.camunda.host"),

+              System.getenv("otf.camunda.port"),

+              System.getenv("otf.camunda.uri.health"));

+

+      HttpResponse res = Utilities.Http.httpGetUsingAAF(healthUrl);

+      return res != null && res.getStatusLine().getStatusCode() == 200;

+    }

+

+    public static JsonObject processInstanceStatus(String executionId) {

+      // Read necessary environment variables - Avoiding using Spring dependencies (@Value)

+      String host = System.getenv("otf.camunda.host");

+      String path = System.getenv("otf.camunda.uri.process-instance-completion-check");

+      int port = Utilities.TryGetEnvironmentVariable("otf.camunda.port");

+

+      if (!Utilities.isHostValid(host)) {

+        logger.error("Host (%s) must use either the http or https protocol.", host);

+        return null;

+      }

+

+      if (!Utilities.isPortValid(port)) {

+        logger.error(

+            "Invalid port (%s) specified as environment variable 'otf.camunda.port'.",

+            System.getenv("otf.camunda.port"));

+        return null;

+      }

+      try {

+        String getUrl = String.format("%s:%s/%s/%s", host, port, path, executionId);

+        HttpResponse response = Utilities.Http.httpGetUsingAAF(getUrl);

+        HttpEntity entity = response.getEntity();

+        String result = EntityUtils.toString(entity);

+

+        return parseJson(result);

+      } catch (IOException ioe) {

+        Utilities.printStackTrace(ioe, Utilities.LogLevel.ERROR);

+        logger.error("Cannot convert http entity to String.");

+      } catch (Exception e) {

+        Utilities.printStackTrace(e, Utilities.LogLevel.ERROR);

+      }

+      // conversion was unsuccessful

+      return null;

+    }

+  }

+

+  private static final Logger logger = LoggerFactory.getLogger(Utilities.class);

+

+  public static void printStackTrace(Exception exception, LogLevel logLevel) {

+    String stackTrace = getStackTrace(exception);

+

+    switch (logLevel) {

+      case INFO:

+        logger.info(stackTrace);

+        break;

+      case WARN:

+        logger.warn(stackTrace);

+        break;

+      case DEBUG:

+        logger.debug(stackTrace);

+        break;

+      case ERROR:

+        logger.error(stackTrace);

+        break;

+    }

+  }

+

+  public static int TryGetEnvironmentVariable(String variable) {

+    String value = System.getenv(variable);

+    int result = 0x80000000;

+

+    try {

+      result = Integer.parseInt(value);

+    } catch (NumberFormatException error) {

+      error.printStackTrace();

+      logger.error(error.getMessage());

+    }

+

+    return result;

+  }

+

+  public static String getStackTrace(Exception exception) {

+    StringWriter stringWriter = new StringWriter();

+    exception.printStackTrace(new PrintWriter(stringWriter));

+    return stringWriter.toString();

+  }

+

+  public static boolean isObjectIdValid(String input) {

+    ObjectId id = null;

+    try {

+      id = new ObjectId(input); // check if an ObjectId can be created from the string

+      if (id.toString().equalsIgnoreCase(input)) return true;

+      logger.warn("The input string does not have the same value as it's string representation.");

+    } catch (IllegalArgumentException e) {

+      logger.error(String.format("An ObjectId cannot be instantiated from the string: %s", input));

+    }

+

+    return false;

+  }

+

+  public static boolean isPortValid(int port) {

+    return (port >= 0 && port <= 65535);

+  }

+

+  public static boolean isHostValid(String host) {

+    return host.startsWith("http");

+  }

+

+  public static <T> boolean identifierExistsInCollection(

+      MongoRepository<T, String> repository, ObjectId identifier) {

+    return repository.findById(identifier.toString()).isPresent();

+  }

+

+  public static <T> T findByIdGeneric(MongoRepository<T, String> repository, ObjectId identifier) {

+    Optional<T> optionalObj = repository.findById(identifier.toString());

+    return optionalObj.orElse(null);

+  }

+

+  public static String[] decodeBase64AuthorizationHeader(String encodedHeader) {

+    try {

+      byte[] decodedAuthorization = Base64.getDecoder().decode(encodedHeader.replace("Basic ", ""));

+      String credentials = new String(decodedAuthorization);

+      return credentials.split(":");

+    } catch (Exception e) {

+      logger.error("Unable to decode authorization header: " + encodedHeader);

+      return null;

+    }

+  }

+

+  public static User findUserByMechanizedId(String mechanizedId, UserRepository userRepository) {

+    Optional<User> optionalUser = userRepository.findFirstByEmail(mechanizedId);

+    return optionalUser.orElse(null);

+  }

+

+  public static User findUserByAuthHeader(String authorization, UserRepository userRepository) {

+    try {

+      if (Strings.isNullOrEmpty(authorization)) {

+        return null;

+      }

+      String[] credentials = Utilities.decodeBase64AuthorizationHeader(authorization);

+      return findUserByMechanizedId(credentials[0], userRepository);

+    } catch (Exception e) {

+      return null;

+    }

+  }

+

+  public static <T> T resolveOptional(Optional<T> optional) {

+    return optional.orElse(null);

+  }

+

+  public static <T> T mapRequest(Class<T> targetType, String input) {

+    logger.info(targetType.getName());

+

+    ObjectMapper mapper =

+        new ObjectMapper().configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);

+    try {

+      return mapper.readValue(input, targetType);

+    } catch (IOException e) {

+      Utilities.printStackTrace(e, LogLevel.ERROR);

+      return null;

+    }

+  }

+

+  public enum LogLevel {

+    WARN,

+    DEBUG,

+    INFO,

+    ERROR

+  }

+

+  public static Date getCurrentDate() {

+    return new Date(System.currentTimeMillis());

+  }

+

+  public static Map<String, Object> replaceObjectId(Map<String, Object> map, String objectIdKey) {

+    if (map.containsKey(objectIdKey)) {

+      ObjectId id = (ObjectId) map.get(objectIdKey);

+      map.replace(objectIdKey, id.toString());

+    }

+

+    return map;

+  }

+}

diff --git a/otf-service-api/src/main/java/org/oran/otf/api/config/CadiFilterConfiguration.java b/otf-service-api/src/main/java/org/oran/otf/api/config/CadiFilterConfiguration.java
new file mode 100644
index 0000000..d98b9ed
--- /dev/null
+++ b/otf-service-api/src/main/java/org/oran/otf/api/config/CadiFilterConfiguration.java
@@ -0,0 +1,119 @@
+/*  Copyright (c) 2019 AT&T Intellectual Property.                             #

+#                                                                              #

+#   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.oran.otf.api.config;

+

+import javax.servlet.Filter;

+import org.onap.aaf.cadi.Access;

+import org.onap.aaf.cadi.config.Config;

+import org.onap.aaf.cadi.filter.CadiFilter;

+import org.springframework.beans.factory.annotation.Value;

+import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;

+import org.springframework.boot.web.servlet.FilterRegistrationBean;

+import org.springframework.context.annotation.Bean;

+import org.springframework.context.annotation.Conditional;

+import org.springframework.context.annotation.PropertySource;

+import org.springframework.stereotype.Component;

+

+@PropertySource("classpath:application.properties")

+@Component

+@ConditionalOnProperty(prefix = "aaf",name ="enabled",havingValue = "true",matchIfMissing = true)

+public class CadiFilterConfiguration {

+

+  @Value("${aaf.call-timeout}")

+  private String AAF_CALL_TIMEOUT;

+

+  @Value("${aaf.conn-timeout}")

+  private String AAF_CONN_TIMEOUT;

+

+  @Value("${aaf.default-realm}")

+  private String AAF_DEFAULT_REALM;

+

+  @Value("${aaf.env}")

+  private String AAF_ENV;

+

+  @Value("${aaf.locate-url}")

+  private String AAF_LOCATE_URL;

+

+  @Value("${aaf.lur-class}")

+  private String AAF_LUR_CLASS;

+

+  @Value("${aaf.url}")

+  private String AAF_URL;

+

+  @Value("${basic-realm}")

+  private String BASIC_REALM;

+

+  @Value("${basic-warn}")

+  private String BASIC_WARN;

+

+  @Value("${cadi-latitude}")

+  private String CADI_LATITUDE;

+

+  @Value("${cadi-longitude}")

+  private String CADI_LONGITUDE;

+

+  @Value("${cadi-protocols}")

+  private String CADI_PROTOCOLS;

+

+  @Value("${cadi-noauthn}")

+  private String CADI_NOAUTHN;

+

+  @Bean(name = "cadiFilterRegistrationBean")

+  @ConditionalOnProperty(prefix = "aaf",name ="enabled",havingValue = "true",matchIfMissing = true)

+  public FilterRegistrationBean<Filter> cadiFilterRegistration() {

+    FilterRegistrationBean<Filter> registration = new FilterRegistrationBean<>();

+    // set cadi configuration properties

+    initCadiProperties(registration);

+

+    registration.addUrlPatterns("/otf/api/testInstance/*", "/otf/api/testExecution/*", "/otf/api/testStrategy/*", "/otf/api/virtualTestHead/*");

+    registration.setFilter(cadiFilter());

+    registration.setName("otfCadiFilter");

+    registration.setOrder(0);

+    return registration;

+  }

+

+  public Filter cadiFilter() {

+    return new CadiFilter();

+  }

+

+  private void initCadiProperties(FilterRegistrationBean<Filter> registration) {

+    registration.addInitParameter(Config.AAF_APPID, System.getenv("AAF_ID"));

+    registration.addInitParameter(Config.AAF_APPPASS, System.getenv("AAF_PASSWORD"));

+    registration.addInitParameter(Config.AAF_CALL_TIMEOUT, AAF_CALL_TIMEOUT);

+    registration.addInitParameter(Config.AAF_CONN_TIMEOUT, AAF_CONN_TIMEOUT);

+    registration.addInitParameter(Config.AAF_DEFAULT_REALM, AAF_DEFAULT_REALM);

+    registration.addInitParameter(Config.AAF_ENV, AAF_ENV);

+    registration.addInitParameter(Config.AAF_LOCATE_URL, AAF_LOCATE_URL);

+    registration.addInitParameter(Config.AAF_LUR_CLASS, AAF_LUR_CLASS);

+    registration.addInitParameter(

+        Config.AAF_URL, AAF_URL);

+

+    registration.addInitParameter(Config.BASIC_REALM, BASIC_REALM);

+    registration.addInitParameter(Config.BASIC_WARN, BASIC_WARN);

+

+    registration.addInitParameter(Config.CADI_KEYFILE, System.getenv("CADI_KEYFILE"));

+    registration.addInitParameter(Config.CADI_LATITUDE, CADI_LATITUDE);

+    //registration.addInitParameter(Config.CADI_LOGLEVEL, Access.Level.DEBUG.name());

+    registration.addInitParameter(Config.CADI_LONGITUDE, CADI_LONGITUDE);

+    registration.addInitParameter(Config.CADI_NOAUTHN, CADI_NOAUTHN);

+    registration.addInitParameter(Config.CADI_PROTOCOLS, CADI_PROTOCOLS);

+    registration.addInitParameter(Config.CADI_KEYSTORE, System.getenv("OTF_CERT_PATH"));

+    registration.addInitParameter(Config.CADI_KEYSTORE_PASSWORD, System.getenv("OTF_CERT_PASS"));

+

+    registration.addInitParameter(Config.HOSTNAME, System.getenv("CADI_HOSTNAME"));

+  }

+}

diff --git a/otf-service-api/src/main/java/org/oran/otf/api/config/CombinedResourceProvider.java b/otf-service-api/src/main/java/org/oran/otf/api/config/CombinedResourceProvider.java
new file mode 100644
index 0000000..ef7fae5
--- /dev/null
+++ b/otf-service-api/src/main/java/org/oran/otf/api/config/CombinedResourceProvider.java
@@ -0,0 +1,46 @@
+/*  Copyright (c) 2019 AT&T Intellectual Property.                             #

+#                                                                              #

+#   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.oran.otf.api.config;

+

+import java.util.List;

+import java.util.stream.Collectors;

+import java.util.stream.Stream;

+import javax.annotation.Resource;

+import org.springframework.context.annotation.Primary;

+import org.springframework.stereotype.Component;

+import springfox.documentation.swagger.web.InMemorySwaggerResourcesProvider;

+import springfox.documentation.swagger.web.SwaggerResource;

+import springfox.documentation.swagger.web.SwaggerResourcesProvider;

+

+@Component

+@Primary

+public class CombinedResourceProvider implements SwaggerResourcesProvider {

+

+  @Resource private InMemorySwaggerResourcesProvider inMemorySwaggerResourcesProvider;

+

+  public List<SwaggerResource> get() {

+

+    SwaggerResource jerseySwaggerResource = new SwaggerResource();

+    jerseySwaggerResource.setLocation("/otf/api/openapi.json");

+    jerseySwaggerResource.setSwaggerVersion("2.0");

+    jerseySwaggerResource.setName("Service API");

+

+    return Stream.concat(

+            Stream.of(jerseySwaggerResource), inMemorySwaggerResourcesProvider.get().stream())

+        .collect(Collectors.toList());

+  }

+}

diff --git a/otf-service-api/src/main/java/org/oran/otf/api/config/DataConfig.java b/otf-service-api/src/main/java/org/oran/otf/api/config/DataConfig.java
new file mode 100644
index 0000000..0546a7d
--- /dev/null
+++ b/otf-service-api/src/main/java/org/oran/otf/api/config/DataConfig.java
@@ -0,0 +1,83 @@
+/*  Copyright (c) 2019 AT&T Intellectual Property.                             #

+#                                                                              #

+#   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.oran.otf.api.config;

+

+import com.mongodb.MongoClient;

+import com.mongodb.MongoClientOptions;

+import com.mongodb.MongoCredential;

+import com.mongodb.ServerAddress;

+import java.util.ArrayList;

+import org.springframework.beans.factory.annotation.Value;

+import org.springframework.context.annotation.Bean;

+import org.springframework.context.annotation.Configuration;

+import org.springframework.context.annotation.Profile;

+import org.springframework.data.mongodb.config.AbstractMongoConfiguration;

+import org.springframework.data.mongodb.core.MongoTemplate;

+import org.springframework.data.mongodb.repository.config.EnableMongoRepositories;

+

+@Configuration

+@EnableMongoRepositories(basePackages = "org.oran.otf.common.repository")

+@Profile("!test")

+public class DataConfig extends AbstractMongoConfiguration {

+

+  @Value("${otf.mongo.hosts}")

+  private String hosts;

+

+  @Value("${otf.mongo.username}")

+  private String username;

+

+  @Value("${otf.mongo.password}")

+  private String password;

+

+  @Value("${otf.mongo.replicaSet}")

+  private String replicaSet;

+

+  @Value("${otf.mongo.database}")

+  private String database;

+

+  public DataConfig() {}

+

+  @Override

+  protected String getDatabaseName() {

+    return database;

+  }

+

+  @Override

+  public MongoClient mongoClient() {

+    MongoCredential credential =

+        MongoCredential.createScramSha1Credential(username, database, password.toCharArray());

+

+    MongoClientOptions options =

+        MongoClientOptions.builder().sslEnabled(false).requiredReplicaSetName(replicaSet).build();

+

+    String[] hostArray = hosts.split(",");

+    ArrayList<ServerAddress> hosts = new ArrayList<>();

+

+    for (String host : hostArray) {

+      String[] hostSplit = host.split(":");

+      hosts.add(new ServerAddress(hostSplit[0], Integer.parseInt(hostSplit[1])));

+    }

+

+    return new MongoClient(hosts, credential, options);

+  }

+

+  @Override

+  @Bean

+  public MongoTemplate mongoTemplate() {

+    return new MongoTemplate(mongoClient(), database);

+  }

+}

diff --git a/otf-service-api/src/main/java/org/oran/otf/api/config/HttpSecurityConfiguration.java b/otf-service-api/src/main/java/org/oran/otf/api/config/HttpSecurityConfiguration.java
new file mode 100644
index 0000000..2646431
--- /dev/null
+++ b/otf-service-api/src/main/java/org/oran/otf/api/config/HttpSecurityConfiguration.java
@@ -0,0 +1,68 @@
+/*  Copyright (c) 2019 AT&T Intellectual Property.                             #

+#                                                                              #

+#   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.oran.otf.api.config;

+

+import org.apache.catalina.Context;

+import org.apache.catalina.connector.Connector;

+import org.apache.tomcat.util.descriptor.web.SecurityCollection;

+import org.apache.tomcat.util.descriptor.web.SecurityConstraint;

+import org.springframework.beans.factory.annotation.Value;

+import org.springframework.boot.context.properties.EnableConfigurationProperties;

+import org.springframework.boot.web.embedded.tomcat.TomcatServletWebServerFactory;

+import org.springframework.boot.web.servlet.server.ServletWebServerFactory;

+import org.springframework.context.annotation.Bean;

+import org.springframework.context.annotation.Configuration;

+

+@Configuration

+@EnableConfigurationProperties

+public class HttpSecurityConfiguration {

+  @Value("${server.port.http}")

+  private int httpPort;

+

+  @Value("${server.port}")

+  private int httpsPort;

+

+  @Value("${ssl.flag}")

+  private boolean httpsOnly;

+

+  @Bean

+  public ServletWebServerFactory servletContainer() {

+    TomcatServletWebServerFactory tomcat =

+        new TomcatServletWebServerFactory(){

+          @Override

+          protected void postProcessContext(Context context) {

+            SecurityConstraint securityConstraint = new SecurityConstraint();

+            if(httpsOnly){ securityConstraint.setUserConstraint("CONFIDENTIAL");}

+            SecurityCollection collection = new SecurityCollection();

+            collection.addPattern("/*");

+            securityConstraint.addCollection(collection);

+            context.addConstraint(securityConstraint);

+          }

+        };

+    tomcat.addAdditionalTomcatConnectors(redirectConnector());

+    return tomcat;

+  }

+

+  private Connector redirectConnector() {

+    Connector connector = new Connector("org.apache.coyote.http11.Http11NioProtocol");

+    connector.setScheme("http");

+    connector.setPort(httpPort);

+    connector.setSecure(false);

+    if(httpsOnly) { connector.setRedirectPort(httpsPort); }

+    return connector;

+  }

+}

diff --git a/otf-service-api/src/main/java/org/oran/otf/api/config/JerseyConfiguration.java b/otf-service-api/src/main/java/org/oran/otf/api/config/JerseyConfiguration.java
new file mode 100644
index 0000000..6d06ac7
--- /dev/null
+++ b/otf-service-api/src/main/java/org/oran/otf/api/config/JerseyConfiguration.java
@@ -0,0 +1,99 @@
+/*  Copyright (c) 2019 AT&T Intellectual Property.                             #

+#                                                                              #

+#   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.oran.otf.api.config;

+

+import org.oran.otf.api.service.impl.*;

+import com.fasterxml.jackson.annotation.JsonInclude;

+import com.fasterxml.jackson.databind.DeserializationFeature;

+import com.fasterxml.jackson.databind.MapperFeature;

+import com.fasterxml.jackson.databind.ObjectMapper;

+import com.fasterxml.jackson.databind.SerializationFeature;

+import io.swagger.v3.jaxrs2.integration.resources.OpenApiResource;

+

+import java.util.logging.Level;

+import java.util.logging.Logger;

+import javax.ws.rs.ApplicationPath;

+import org.glassfish.jersey.logging.LoggingFeature;

+import org.glassfish.jersey.media.multipart.MultiPartFeature;

+import org.glassfish.jersey.server.ResourceConfig;

+import org.glassfish.jersey.server.ServerProperties;

+import org.glassfish.jersey.servlet.ServletProperties;

+import org.oran.otf.api.service.impl.*;

+import org.springframework.beans.factory.annotation.Autowired;

+import org.springframework.context.annotation.Bean;

+import org.springframework.context.annotation.Primary;

+import org.springframework.stereotype.Component;

+

+@Component

+@ApplicationPath("/otf/api")

+public class JerseyConfiguration extends ResourceConfig {

+  private static final Logger log = Logger.getLogger(JerseyConfiguration.class.getName());

+

+  //   @Value("${spring.jersey.application-path}")

+  //   private String apiPath;

+

+  //  @Value("${springfox.documentation.swagger.v2.path}")

+  //  private String swagger2Endpoint;

+

+  @Autowired

+  public JerseyConfiguration() {

+    registerFeatures();

+    registerEndpoints();

+    setProperties();

+

+    configureSwagger();

+  }

+

+

+  private void registerFeatures() {

+    register(MultiPartFeature.class);

+    register(new OTFLoggingFeature(Logger.getLogger(getClass().getName()), Level.INFO, LoggingFeature.Verbosity.PAYLOAD_ANY, 8192));

+

+  }

+

+  private void registerEndpoints() {

+    register(TestInstanceServiceImpl.class);

+    register(HealthServiceImpl.class);

+    register(TestStrategyServiceImpl.class);

+    register(TestExecutionServiceImpl.class);

+    register(VirtualTestHeadServiceImpl.class);

+

+    register(OtfOpenServiceImpl.class);

+  }

+

+  private void setProperties() {

+    property(ServletProperties.FILTER_FORWARD_ON_404, true);

+    property(ServerProperties.RESPONSE_SET_STATUS_OVER_SEND_ERROR, true);

+  }

+

+  private void configureSwagger() {

+    OpenApiResource openApiResource = new OpenApiResource();

+

+    register(openApiResource);

+  }

+

+  @Bean

+  @Primary

+  public ObjectMapper objectMapper() {

+    ObjectMapper objectMapper = new ObjectMapper();

+    objectMapper.setSerializationInclusion(JsonInclude.Include.NON_NULL);

+    objectMapper.configure(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS, false);

+    objectMapper.enable(MapperFeature.ACCEPT_CASE_INSENSITIVE_PROPERTIES);

+    objectMapper.enable(DeserializationFeature.READ_ENUMS_USING_TO_STRING);

+    return objectMapper;

+  }

+}

diff --git a/otf-service-api/src/main/java/org/oran/otf/api/config/OTFApiEnforcementFilter.java b/otf-service-api/src/main/java/org/oran/otf/api/config/OTFApiEnforcementFilter.java
new file mode 100644
index 0000000..aba17f0
--- /dev/null
+++ b/otf-service-api/src/main/java/org/oran/otf/api/config/OTFApiEnforcementFilter.java
@@ -0,0 +1,134 @@
+/*  Copyright (c) 2019 AT&T Intellectual Property.                             #

+#                                                                              #

+#   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.oran.otf.api.config;

+

+import com.google.common.base.Strings;

+import java.io.IOException;

+import java.util.ArrayList;

+import java.util.List;

+import java.util.Map;

+import java.util.TreeMap;

+import javax.servlet.Filter;

+import javax.servlet.FilterChain;

+import javax.servlet.FilterConfig;

+import javax.servlet.ServletException;

+import javax.servlet.ServletRequest;

+import javax.servlet.ServletResponse;

+import javax.servlet.http.HttpServletRequest;

+import javax.servlet.http.HttpServletResponse;

+import org.apache.commons.logging.Log;

+import org.apache.commons.logging.LogFactory;

+import org.onap.aaf.cadi.Access;

+import org.onap.aaf.cadi.Access.Level;

+import org.onap.aaf.cadi.ServletContextAccess;

+import org.onap.aaf.cadi.util.Split;

+

+public class OTFApiEnforcementFilter implements Filter {

+  private static final Log log = LogFactory.getLog(OTFApiEnforcementFilter.class);

+  private String type;

+  private Map<String, List<String>> publicPaths;

+  private Access access = null;

+

+  public OTFApiEnforcementFilter(Access access, String enforce) throws ServletException {

+    this.access = access;

+    init(enforce);

+  }

+

+  @Override

+  public void init(FilterConfig fc) throws ServletException {

+    init(fc.getInitParameter("aaf_perm_type"));

+    // need the Context for Logging, instantiating ClassLoader, etc

+    ServletContextAccess sca = new ServletContextAccess(fc);

+    if (access == null) {

+      access = sca;

+    }

+  }

+

+  private void init(final String ptypes) throws ServletException {

+    if (Strings.isNullOrEmpty(ptypes)) {

+      throw new ServletException("OTFApiEnforcement requires aaf_perm_type property");

+    }

+    String[] full = Split.splitTrim(';', ptypes);

+    if (full.length <= 0) {

+      throw new ServletException("aaf_perm_type property is empty");

+    }

+

+    type = full[0];

+    publicPaths = new TreeMap<>();

+    if (full.length > 1) {

+      for (int i = 1; i < full.length; ++i) {

+        String[] pubArray = Split.split(':', full[i]);

+        if (pubArray.length == 2) {

+          List<String> ls = publicPaths.get(pubArray[0]);

+          if (ls == null) {

+            ls = new ArrayList<>();

+            publicPaths.put(pubArray[0], ls);

+          }

+          ls.add(pubArray[1]);

+        }

+      }

+    }

+  }

+

+  @Override

+  public void doFilter(ServletRequest req, ServletResponse resp, FilterChain fc)

+      throws IOException, ServletException {

+    HttpServletRequest hreq = (HttpServletRequest) req;

+    final String meth = hreq.getMethod();

+    String path = hreq.getContextPath(); // + hreq.getPathInfo();

+

+    if (Strings.isNullOrEmpty(path) || "null".equals(path)) {

+      path = hreq.getRequestURI().substring(hreq.getContextPath().length());

+    }

+

+    List<String> list = publicPaths.get(meth);

+    if (list != null) {

+      for (String p : publicPaths.get(meth)) {

+        if (path.startsWith(p)) {

+          access.printf(

+              Level.INFO,

+              "%s accessed public API %s %s\n",

+              hreq.getUserPrincipal().getName(),

+              meth,

+              path);

+          fc.doFilter(req, resp);

+          return;

+        }

+      }

+    }

+    if (hreq.isUserInRole(type + '|' + path + '|' + meth)) {

+      access.printf(

+          Level.INFO,

+          "%s is allowed access to %s %s\n",

+          hreq.getUserPrincipal().getName(),

+          meth,

+          path);

+      fc.doFilter(req, resp);

+    } else {

+      access.printf(

+          Level.AUDIT,

+          "%s is denied access to %s %s\n",

+          hreq.getUserPrincipal().getName(),

+          meth,

+          path);

+      ((HttpServletResponse) resp).sendError(HttpServletResponse.SC_UNAUTHORIZED);

+    }

+  }

+

+  @Override

+  public void destroy() {}

+}

diff --git a/otf-service-api/src/main/java/org/oran/otf/api/config/OTFApiEnforcementFilterConfiguration.java b/otf-service-api/src/main/java/org/oran/otf/api/config/OTFApiEnforcementFilterConfiguration.java
new file mode 100644
index 0000000..cd37067
--- /dev/null
+++ b/otf-service-api/src/main/java/org/oran/otf/api/config/OTFApiEnforcementFilterConfiguration.java
@@ -0,0 +1,60 @@
+/*  Copyright (c) 2019 AT&T Intellectual Property.                             #

+#                                                                              #

+#   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.oran.otf.api.config;

+

+import javax.servlet.Filter;

+import javax.servlet.FilterConfig;

+import javax.servlet.ServletException;

+import org.onap.aaf.cadi.Access;

+import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;

+import org.springframework.boot.web.servlet.FilterRegistrationBean;

+import org.springframework.context.annotation.Bean;

+import org.springframework.context.annotation.Conditional;

+import org.springframework.context.annotation.Configuration;

+import org.springframework.context.annotation.PropertySource;

+

+@PropertySource("classpath:application.properties")

+@Configuration

+@ConditionalOnProperty(prefix = "aaf",name ="enabled",havingValue = "true")

+public class OTFApiEnforcementFilterConfiguration {

+

+  private Access access;

+  private FilterConfig fc;

+

+  @Bean(name = "otfApiEnforcementFilterRegistrationBean")

+  @ConditionalOnProperty(prefix = "aaf",name ="enabled",havingValue = "true")

+  public FilterRegistrationBean<Filter> otfApiEnforcementFilterRegistration()

+      throws ServletException {

+    FilterRegistrationBean<Filter> registration = new FilterRegistrationBean<>();

+    initFilterParameters(registration);

+    registration.addUrlPatterns("/otf/api/testInstance/*", "/otf/api/testExecution/*", "/otf/api/testStrategy/*", "/otf/api/virtualTestHead/*");

+    registration.setFilter(otfApiEnforcementFilter());

+    registration.setName("otfApiEnforcementFilter");

+    registration.setOrder(1);

+    return registration;

+  }

+

+  @Bean(name = "otfApiEnforcementFilter")

+  @ConditionalOnProperty(prefix = "aaf",name ="enabled",havingValue = "true")

+  public Filter otfApiEnforcementFilter() throws ServletException {

+    return new OTFApiEnforcementFilter(access, System.getenv("AAF_PERM_TYPE"));

+  }

+

+  private void initFilterParameters(FilterRegistrationBean<Filter> registration) {

+    registration.addInitParameter("aaf_perm_type", System.getenv("AAF_PERM_TYPE"));

+  }

+}

diff --git a/otf-service-api/src/main/java/org/oran/otf/api/config/OTFLoggingFeature.java b/otf-service-api/src/main/java/org/oran/otf/api/config/OTFLoggingFeature.java
new file mode 100644
index 0000000..c13caab
--- /dev/null
+++ b/otf-service-api/src/main/java/org/oran/otf/api/config/OTFLoggingFeature.java
@@ -0,0 +1,238 @@
+/*  Copyright (c) 2019 AT&T Intellectual Property.                             #

+#                                                                              #

+#   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.oran.otf.api.config;

+

+import org.glassfish.jersey.logging.LoggingFeature;

+import org.glassfish.jersey.message.MessageUtils;

+

+import javax.ws.rs.WebApplicationException;

+import javax.ws.rs.client.ClientRequestContext;

+import javax.ws.rs.client.ClientRequestFilter;

+import javax.ws.rs.client.ClientResponseContext;

+import javax.ws.rs.client.ClientResponseFilter;

+import javax.ws.rs.container.ContainerRequestContext;

+import javax.ws.rs.container.ContainerRequestFilter;

+import javax.ws.rs.container.ContainerResponseContext;

+import javax.ws.rs.container.ContainerResponseFilter;

+import javax.ws.rs.core.FeatureContext;

+import javax.ws.rs.core.MultivaluedMap;

+import javax.ws.rs.ext.WriterInterceptor;

+import javax.ws.rs.ext.WriterInterceptorContext;

+import java.io.*;

+import java.net.URI;

+import java.nio.charset.Charset;

+import java.util.ArrayList;

+import java.util.Base64;

+import java.util.List;

+import java.util.Objects;

+import java.util.logging.Level;

+import java.util.logging.Logger;

+

+public class OTFLoggingFeature extends LoggingFeature implements ContainerRequestFilter, ContainerResponseFilter,

+        ClientRequestFilter, ClientResponseFilter, WriterInterceptor {

+

+    private static final boolean printEntity = true;

+    private static final int maxEntitySize = 8 * 1024;

+    private final Logger logger = Logger.getLogger("OTFLoggingFeature");

+    private static final String ENTITY_LOGGER_PROPERTY = OTFLoggingFeature.class.getName();

+    private static final String NOTIFICATION_PREFIX = "* ";

+    private static final String REQUEST_PREFIX = "> ";

+    private static final String RESPONSE_PREFIX = "< ";

+    private static final String AUTHORIZATION = "Authorization";

+    private static final String EQUAL = " = ";

+    private static final String HEADERS_SEPARATOR = ", ";

+    private static List<String> requestHeaders;

+

+    static {

+        requestHeaders = new ArrayList<>();

+        requestHeaders.add(AUTHORIZATION);

+    }

+

+    public OTFLoggingFeature(Logger logger, Level level, Verbosity verbosity, Integer maxEntitySize) {

+        super(logger, level, verbosity, maxEntitySize);

+    }

+

+    @Override

+    public boolean configure(FeatureContext context) {

+        context.register(this);

+        return true;

+    }

+

+    private Object getEmail(Object authorization){

+        try{

+            String encoded = ((String) authorization).split(" ")[1];

+            String decoded =  new String(Base64.getDecoder().decode(encoded));

+            return decoded.split(":")[0];

+        }

+        catch (Exception e){

+            return authorization;

+        }

+    }

+

+    @Override

+    public void filter(final ClientRequestContext context) {

+        final StringBuilder b = new StringBuilder();

+        printHeaders(b, context.getStringHeaders());

+        printRequestLine(b, "Sending client request", context.getMethod(), context.getUri());

+

+        if (printEntity && context.hasEntity()) {

+            final OutputStream stream = new LoggingStream(b, context.getEntityStream());

+            context.setEntityStream(stream);

+            context.setProperty(ENTITY_LOGGER_PROPERTY, stream);

+            // not calling log(b) here - it will be called by the interceptor

+        } else {

+            log(b);

+        }

+    }

+

+    @Override

+    public void filter(final ClientRequestContext requestContext, final ClientResponseContext responseContext) throws IOException {

+        final StringBuilder b = new StringBuilder();

+        printResponseLine(b, "Client response received", responseContext.getStatus());

+

+        if (printEntity && responseContext.hasEntity()) {

+            responseContext.setEntityStream(logInboundEntity(b, responseContext.getEntityStream(),

+                    MessageUtils.getCharset(responseContext.getMediaType())));

+        }

+        log(b);

+    }

+

+    @Override

+    public void filter(final ContainerRequestContext context) throws IOException {

+        final StringBuilder b = new StringBuilder();

+        printHeaders(b, context.getHeaders());

+        printRequestLine(b, "Server has received a request", context.getMethod(), context.getUriInfo().getRequestUri());

+

+        if (printEntity && context.hasEntity()) {

+            context.setEntityStream(logInboundEntity(b, context.getEntityStream(), MessageUtils.getCharset(context.getMediaType())));

+        }

+        log(b);

+    }

+

+    @Override

+    public void filter(final ContainerRequestContext requestContext, final ContainerResponseContext responseContext) {

+        final StringBuilder b = new StringBuilder();

+        printResponseLine(b, "Server responded with a response", responseContext.getStatus());

+

+        if (printEntity && responseContext.hasEntity()) {

+            final OutputStream stream = new LoggingStream(b, responseContext.getEntityStream());

+            responseContext.setEntityStream(stream);

+            requestContext.setProperty(ENTITY_LOGGER_PROPERTY, stream);

+            // not calling log(b) here - it will be called by the interceptor

+        } else {

+            log(b);

+        }

+    }

+

+    @Override

+    public void aroundWriteTo(final WriterInterceptorContext writerInterceptorContext) throws IOException, WebApplicationException {

+        final LoggingStream stream = (LoggingStream) writerInterceptorContext.getProperty(ENTITY_LOGGER_PROPERTY);

+        writerInterceptorContext.proceed();

+        if (stream != null) {

+            log(stream.getStringBuilder(MessageUtils.getCharset(writerInterceptorContext.getMediaType())));

+        }

+    }

+

+    private static class LoggingStream extends FilterOutputStream {

+        private final StringBuilder b;

+        private final ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();

+

+        LoggingStream(final StringBuilder b, final OutputStream inner) {

+            super(inner);

+

+            this.b = b;

+        }

+

+        StringBuilder getStringBuilder(Charset charset) {

+            // write entity to the builder

+            final byte[] entity = byteArrayOutputStream.toByteArray();

+

+            b.append(new String(entity, 0, Math.min(entity.length, maxEntitySize), charset));

+            if (entity.length > maxEntitySize) {

+                b.append("...more...");

+            }

+            b.append('\n');

+

+            return b;

+        }

+

+        public void write(final int i) throws IOException {

+            if (byteArrayOutputStream.size() <= maxEntitySize) {

+                byteArrayOutputStream.write(i);

+            }

+            out.write(i);

+        }

+    }

+

+    private void printHeaders(StringBuilder b, MultivaluedMap<String, String> headers) {

+        for (String header : requestHeaders) {

+            if (Objects.nonNull(headers.get(header))) {

+                if(header.equalsIgnoreCase("Authorization")){

+                    b.append(header).append(EQUAL).append(getEmail(headers.get(header).get(0))).append(HEADERS_SEPARATOR);

+                }

+                else{

+                    b.append(header).append(EQUAL).append(headers.get(header)).append(HEADERS_SEPARATOR);

+                }

+            }

+        }

+        int lastIndex = b.lastIndexOf(HEADERS_SEPARATOR);

+        if (lastIndex != -1) {

+            b.delete(lastIndex, lastIndex + HEADERS_SEPARATOR.length());

+            b.append("\n");

+        }

+    }

+

+    private void log(final StringBuilder b) {

+        String message = b.toString();

+        if (logger != null) {

+            logger.info(message);

+        }

+    }

+

+    private void printRequestLine(final StringBuilder b, final String note, final String method, final URI uri) {

+        b.append(NOTIFICATION_PREFIX)

+                .append(note)

+                .append(" on thread ").append(Thread.currentThread().getId())

+                .append(REQUEST_PREFIX).append(method).append(" ")

+                .append(uri.toASCIIString()).append("\n");

+    }

+

+    private void printResponseLine(final StringBuilder b, final String note, final int status) {

+        b.append(NOTIFICATION_PREFIX)

+                .append(note)

+                .append(" on thread ").append(Thread.currentThread().getId())

+                .append(RESPONSE_PREFIX)

+                .append(Integer.toString(status))

+                .append("\n");

+    }

+

+    private InputStream logInboundEntity(final StringBuilder b, InputStream stream, final Charset charset) throws IOException {

+        if (!stream.markSupported()) {

+            stream = new BufferedInputStream(stream);

+        }

+        stream.mark(maxEntitySize + 1);

+        final byte[] entity = new byte[maxEntitySize + 1];

+        final int entitySize = stream.read(entity);

+        b.append(new String(entity, 0, Math.min(entitySize, maxEntitySize), charset));

+        if (entitySize > maxEntitySize) {

+            b.append("...more...");

+        }

+        b.append('\n');

+        stream.reset();

+        return stream;

+    }

+}
\ No newline at end of file
diff --git a/otf-service-api/src/main/java/org/oran/otf/api/exception/TestHeadNotFoundException.java b/otf-service-api/src/main/java/org/oran/otf/api/exception/TestHeadNotFoundException.java
new file mode 100644
index 0000000..7132a88
--- /dev/null
+++ b/otf-service-api/src/main/java/org/oran/otf/api/exception/TestHeadNotFoundException.java
@@ -0,0 +1,33 @@
+/*  Copyright (c) 2019 AT&T Intellectual Property.                             #

+#                                                                              #

+#   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.oran.otf.api.exception;

+

+public class TestHeadNotFoundException extends Exception {

+  private static final long serialVersionUID = 1L;

+

+  public TestHeadNotFoundException(String message) {

+    super(message);

+  }

+

+  public TestHeadNotFoundException(Throwable cause) {

+    super(cause);

+  }

+

+  public TestHeadNotFoundException(String message, Throwable cause) {

+    super(message, cause);

+  }

+}

diff --git a/otf-service-api/src/main/java/org/oran/otf/api/exception/TestParametersException.java b/otf-service-api/src/main/java/org/oran/otf/api/exception/TestParametersException.java
new file mode 100644
index 0000000..2029f5c
--- /dev/null
+++ b/otf-service-api/src/main/java/org/oran/otf/api/exception/TestParametersException.java
@@ -0,0 +1,33 @@
+/*  Copyright (c) 2019 AT&T Intellectual Property.                             #

+#                                                                              #

+#   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.oran.otf.api.exception;

+

+public class TestParametersException extends Exception {

+  private static final long serialVersionUID = 1L;

+

+  public TestParametersException(String message) {

+    super(message);

+  }

+

+  public TestParametersException(Throwable cause) {

+    super(cause);

+  }

+

+  public TestParametersException(String message, Throwable cause) {

+    super(message, cause);

+  }

+}

diff --git a/otf-service-api/src/main/java/org/oran/otf/api/exception/UserNotFoundException.java b/otf-service-api/src/main/java/org/oran/otf/api/exception/UserNotFoundException.java
new file mode 100644
index 0000000..d319bcb
--- /dev/null
+++ b/otf-service-api/src/main/java/org/oran/otf/api/exception/UserNotFoundException.java
@@ -0,0 +1,33 @@
+/*  Copyright (c) 2019 AT&T Intellectual Property.                             #

+#                                                                              #

+#   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.oran.otf.api.exception;

+

+public class UserNotFoundException extends Exception {

+  private static final long serialVersionUID = 1L;

+

+  public UserNotFoundException(String message) {

+    super(message);

+  }

+

+  public UserNotFoundException(Throwable cause) {

+    super(cause);

+  }

+

+  public UserNotFoundException(String message, Throwable cause) {

+    super(message, cause);

+  }

+}

diff --git a/otf-service-api/src/main/java/org/oran/otf/api/handler/CamundaProcessDeploymentHandler.java b/otf-service-api/src/main/java/org/oran/otf/api/handler/CamundaProcessDeploymentHandler.java
new file mode 100644
index 0000000..25c06b3
--- /dev/null
+++ b/otf-service-api/src/main/java/org/oran/otf/api/handler/CamundaProcessDeploymentHandler.java
@@ -0,0 +1,131 @@
+/*  Copyright (c) 2019 AT&T Intellectual Property.                             #

+#                                                                              #

+#   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.oran.otf.api.handler;

+

+import org.oran.otf.api.Utilities;

+import org.oran.otf.common.utility.http.ResponseUtility;

+import java.io.InputStream;

+import java.util.Base64;

+import javax.ws.rs.core.Response;

+import org.apache.http.HttpEntity;

+import org.apache.http.client.ClientProtocolException;

+import org.apache.http.client.ResponseHandler;

+import org.apache.http.client.methods.HttpUriRequest;

+import org.apache.http.client.methods.RequestBuilder;

+import org.apache.http.conn.HttpHostConnectException;

+import org.apache.http.conn.ssl.NoopHostnameVerifier;

+import org.apache.http.entity.ContentType;

+import org.apache.http.entity.mime.MultipartEntityBuilder;

+import org.apache.http.impl.client.CloseableHttpClient;

+import org.apache.http.impl.client.HttpClients;

+import org.apache.http.util.EntityUtils;

+import org.slf4j.Logger;

+import org.slf4j.LoggerFactory;

+import org.springframework.stereotype.Component;

+

+@Component

+public class CamundaProcessDeploymentHandler {

+  private static final Logger logger =

+      LoggerFactory.getLogger(CamundaProcessDeploymentHandler.class);

+

+  private CamundaProcessDeploymentHandler() {

+    // prevent instantiation

+  }

+

+  public Response start(InputStream bpmn, InputStream compressedResources) {

+    // Read necessary environment variables - Avoiding using Spring dependencies (@Value)

+    String host = System.getenv("otf.camunda.host");

+    String path = System.getenv("otf.camunda.uri.deploy-test-strategy-zip");

+    int port = Utilities.TryGetEnvironmentVariable("otf.camunda.port");

+    String aafCredentialsDecoded =

+        System.getenv("AAF_ID") + ":" + System.getenv("AAF_MECH_PASSWORD");

+

+    if (!Utilities.isHostValid(host)) {

+      logger.error("Host (%s) must use either the http or https protocol.", host);

+      return null;

+    }

+

+    if (!Utilities.isPortValid(port)) {

+      logger.error(

+          "Invalid port (%s) specified as environment variable 'otf.camunda.port'.",

+          System.getenv("otf.camunda.port"));

+      return null;

+    }

+

+    // Form the full url

+    String postUrl = String.format("%s:%s/%s", host, port, path);

+

+    try (CloseableHttpClient httpclient =

+        HttpClients.custom().setSSLHostnameVerifier(NoopHostnameVerifier.INSTANCE).build()) {

+

+      // build multipart upload request

+      MultipartEntityBuilder builder =

+          MultipartEntityBuilder.create()

+              .addBinaryBody("bpmn", bpmn, ContentType.DEFAULT_BINARY, "bpmn");

+

+      // add resources to the request if they were supplied

+      if (compressedResources != null) {

+        builder.addBinaryBody(

+            "resources", compressedResources, ContentType.DEFAULT_BINARY, "resources");

+      }

+

+      HttpEntity data = builder.build();

+

+      // build http request and assign multipart upload data

+      HttpUriRequest request =

+          RequestBuilder.post(postUrl)

+              .addHeader(

+                  "Authorization",

+                  "Basic " + Base64.getEncoder().encodeToString(aafCredentialsDecoded.getBytes()))

+              .setEntity(data)

+              .build();

+

+      System.out.println("Executing request " + request.getRequestLine());

+

+      // Create a custom response handler

+      ResponseHandler<Response> responseHandler =

+          response -> {

+            int status = response.getStatusLine().getStatusCode();

+            if (status >= 200 && status < 300) {

+              HttpEntity entity = response.getEntity();

+              String message = entity != null ? EntityUtils.toString(entity) : null;

+              return Response.ok(message).build();

+            } else if (status == 400) {

+              HttpEntity entity = response.getEntity();

+              String message =

+                  entity != null

+                      ? EntityUtils.toString(entity)

+                      : "Supplied bpmn file is not deployable.";

+              return Utilities.Http.BuildResponse.badRequestWithMessage(message);

+            } else {

+              throw new ClientProtocolException("Unexpected response status: " + status);

+            }

+          };

+

+      Response responseBody = httpclient.execute(request, responseHandler);

+      System.out.println("----------------------------------------");

+      System.out.println(responseBody.getEntity().toString());

+

+      return responseBody;

+    } catch (HttpHostConnectException e) {

+      return ResponseUtility.Build.serviceUnavailableWithMessage(e.getMessage());

+    } catch (Exception e) {

+      e.printStackTrace();

+      return ResponseUtility.Build.internalServerErrorWithMessage("Unable to deploy definition.");

+    }

+  }

+}

diff --git a/otf-service-api/src/main/java/org/oran/otf/api/handler/CamundaProcessExecutionHandler.java b/otf-service-api/src/main/java/org/oran/otf/api/handler/CamundaProcessExecutionHandler.java
new file mode 100644
index 0000000..00e26d7
--- /dev/null
+++ b/otf-service-api/src/main/java/org/oran/otf/api/handler/CamundaProcessExecutionHandler.java
@@ -0,0 +1,105 @@
+/*  Copyright (c) 2019 AT&T Intellectual Property.                             #

+#                                                                              #

+#   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.oran.otf.api.handler;

+

+import org.oran.otf.api.Utilities;

+import org.oran.otf.api.Utilities.LogLevel;

+import org.oran.otf.common.model.TestExecution;

+import org.oran.otf.common.model.local.OTFApiResponse;

+import org.oran.otf.common.model.local.WorkflowRequest;

+import org.oran.otf.common.utility.gson.Convert;

+import org.oran.otf.common.utility.http.ResponseUtility;

+import com.fasterxml.jackson.core.type.TypeReference;

+import com.fasterxml.jackson.databind.ObjectMapper;

+import javax.ws.rs.core.MediaType;

+import javax.ws.rs.core.Response;

+import org.apache.http.HttpEntity;

+import org.apache.http.HttpResponse;

+import org.apache.http.conn.HttpHostConnectException;

+import org.apache.http.util.EntityUtils;

+import org.slf4j.Logger;

+import org.slf4j.LoggerFactory;

+import org.springframework.stereotype.Component;

+

+@Component

+public class CamundaProcessExecutionHandler {

+  private static final Logger logger =

+      LoggerFactory.getLogger(CamundaProcessExecutionHandler.class);

+

+  private CamundaProcessExecutionHandler() {

+    // prevent instantiation

+  }

+

+  public Response startProcessInstance(WorkflowRequest request) throws Exception {

+    try {

+      //      if (!Utilities.Camunda.isCamundaOnline()) {

+      //        Utilities.Http.BuildResponse.internalServerErrorWithMessage(

+      //            "Unable to start process instance because the test control unit is

+      // unavailable.");

+      //      }

+

+      // Read necessary environment variables - Avoiding using Spring dependencies (@Value)

+      String host = System.getenv("otf.camunda.host");

+      String path = System.getenv("otf.camunda.uri.execute-test");

+      int port = Utilities.TryGetEnvironmentVariable("otf.camunda.port");

+

+      if (!Utilities.isHostValid(host)) {

+        logger.error(String.format("Host (%s) must use either the http or https protocol.", host));

+        return null;

+      }

+

+      if (!Utilities.isPortValid(port)) {

+        logger.error(

+            String.format(

+                "Invalid port (%s) specified as environment variable 'otf.camunda.port'.",

+                System.getenv("otf.camunda.port")));

+        return null;

+      }

+

+      // Form the URL

+      String postUrl = String.format("%s:%s/%s", host, port, path);

+

+      // Send and store the response

+      HttpResponse response = Utilities.Http.httpPostJsonUsingAAF(postUrl, request.toString());

+      // Get the entity and attempt to convert it to a TestExecution object.

+      HttpEntity entity = response.getEntity();

+      String rawEntity = EntityUtils.toString(entity);

+      ObjectMapper mapper = new ObjectMapper();

+      OTFApiResponse otfApiResponse = mapper.readValue(rawEntity, OTFApiResponse.class);

+

+      if (otfApiResponse.getStatusCode() == 400) {

+        return Response.status(400)

+            .type(MediaType.APPLICATION_JSON_TYPE)

+            .entity(otfApiResponse.toString())

+            .build();

+      }

+

+      String jsonMessage = otfApiResponse.getMessage();

+      TestExecution testExecution =

+          Convert.jsonToObject(jsonMessage, new TypeReference<TestExecution>() {});

+      return Response.status(otfApiResponse.getStatusCode())

+          .entity(testExecution.toString())

+          .build();

+

+    } catch (HttpHostConnectException e) {

+      return ResponseUtility.Build.serviceUnavailableWithMessage(e.getMessage());

+    } catch (Exception e) {

+      Utilities.printStackTrace(e, LogLevel.ERROR);

+      return ResponseUtility.Build.internalServerError();

+    }

+  }

+}

diff --git a/otf-service-api/src/main/java/org/oran/otf/api/service/HealthService.java b/otf-service-api/src/main/java/org/oran/otf/api/service/HealthService.java
new file mode 100644
index 0000000..4bd2378
--- /dev/null
+++ b/otf-service-api/src/main/java/org/oran/otf/api/service/HealthService.java
@@ -0,0 +1,54 @@
+/*  Copyright (c) 2019 AT&T Intellectual Property.                             #

+#                                                                              #

+#   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.oran.otf.api.service;

+

+import org.oran.otf.common.model.local.OTFApiResponse;

+import io.swagger.annotations.Api;

+import io.swagger.v3.oas.annotations.Operation;

+import io.swagger.v3.oas.annotations.media.Content;

+import io.swagger.v3.oas.annotations.media.Schema;

+import io.swagger.v3.oas.annotations.responses.ApiResponse;

+import io.swagger.v3.oas.annotations.tags.Tag;

+

+import javax.ws.rs.GET;

+import javax.ws.rs.Path;

+import javax.ws.rs.Produces;

+import javax.ws.rs.core.MediaType;

+import javax.ws.rs.core.Response;

+

+@Api

+@Path("/health")

+@Tag(name = "Health Service", description = "Query the availability of the API")

+@Produces({MediaType.APPLICATION_JSON})

+public interface HealthService {

+

+  @GET

+  @Path("/v1")

+  @Produces({MediaType.APPLICATION_JSON})

+  @Operation(

+      summary = "Checks if the test control unit is available",

+      responses = {

+        @ApiResponse(

+            responseCode = "200",

+            description = "The test control unit is available",

+            content =

+                @Content(

+                    mediaType = "application/json",

+                    schema = @Schema(implementation = OTFApiResponse.class)))

+      })

+  Response getHealth();

+}

diff --git a/otf-service-api/src/main/java/org/oran/otf/api/service/OtfOpenService.java b/otf-service-api/src/main/java/org/oran/otf/api/service/OtfOpenService.java
new file mode 100644
index 0000000..ec32e47
--- /dev/null
+++ b/otf-service-api/src/main/java/org/oran/otf/api/service/OtfOpenService.java
@@ -0,0 +1,35 @@
+/*  Copyright (c) 2019 AT&T Intellectual Property.                             #

+#                                                                              #

+#   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.oran.otf.api.service;

+

+import io.swagger.v3.oas.annotations.Hidden;

+import javax.ws.rs.GET;

+import javax.ws.rs.Path;

+import javax.ws.rs.Produces;

+import javax.ws.rs.core.MediaType;

+import javax.ws.rs.core.Response;

+import org.springframework.stereotype.Service;

+

+@Hidden

+@Path("/")

+public interface OtfOpenService {

+  @GET

+  @Hidden

+  @Produces(MediaType.APPLICATION_JSON)

+  @Path("/demo/openapi.json")

+  Response convertSwaggerFile();

+}

diff --git a/otf-service-api/src/main/java/org/oran/otf/api/service/TestExecutionService.java b/otf-service-api/src/main/java/org/oran/otf/api/service/TestExecutionService.java
new file mode 100644
index 0000000..b1d5d5e
--- /dev/null
+++ b/otf-service-api/src/main/java/org/oran/otf/api/service/TestExecutionService.java
@@ -0,0 +1,92 @@
+/*  Copyright (c) 2019 AT&T Intellectual Property.                             #

+#                                                                              #

+#   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.oran.otf.api.service;

+

+import org.oran.otf.common.model.TestExecution;

+import org.oran.otf.common.model.local.OTFApiResponse;

+import io.swagger.annotations.Api;

+import io.swagger.v3.oas.annotations.Operation;

+import io.swagger.v3.oas.annotations.media.Content;

+import io.swagger.v3.oas.annotations.media.Schema;

+import io.swagger.v3.oas.annotations.responses.ApiResponse;

+import io.swagger.v3.oas.annotations.tags.Tag;

+import javax.ws.rs.GET;

+import javax.ws.rs.HeaderParam;

+import javax.ws.rs.Path;

+import javax.ws.rs.PathParam;

+import javax.ws.rs.Produces;

+import javax.ws.rs.core.MediaType;

+import javax.ws.rs.core.Response;

+

+import org.springframework.stereotype.Component;

+

+@Component

+@Api

+@Path("/testExecution")

+@Tag(name = "Test Services", description = "")

+@Produces(MediaType.APPLICATION_JSON)

+public interface TestExecutionService {

+  @GET

+  @Path("v1/status/executionId/{executionId}")

+  @Produces({MediaType.APPLICATION_JSON})

+  @Operation(

+      description = "Respond with a test execution object if it exists",

+      summary = "Find test execution log by processInstanceId",

+      responses = {

+        @ApiResponse(

+            responseCode = "200",

+            description = "The created Test Instance object is returned when it is created",

+            content = {

+              @Content(

+                  mediaType = "application/json",

+                  schema = @Schema(implementation = TestExecution.class))

+            })

+      })

+  Response getExecutionStatus(

+      @HeaderParam("Authorization") String authorization,

+      @PathParam("executionId") String executionId);

+

+  @GET

+  @Path("v1/executionId/{executionId}")

+  @Produces({MediaType.APPLICATION_JSON})

+  @Operation(

+      description =

+          "Respond with a test execution object, and state of the process instance if it exists.",

+      summary = "Find test execution log by executionId",

+      responses = {

+          @ApiResponse(

+              responseCode = "200",

+              description = "The created Test Instance object is returned when it is created",

+              content = {

+                  @Content(

+                      mediaType = "application/json",

+                      schema = @Schema(implementation = TestExecution.class))

+              }),

+          @ApiResponse(

+              responseCode = "404",

+              description =

+                  "No process instance was found with the executionId used to query the service",

+              content = {

+                  @Content(

+                      mediaType = "application/json",

+                      schema = @Schema(implementation = OTFApiResponse.class))

+              })

+      })

+  Response getExecution(

+      @HeaderParam("Authorization") String authorization,

+      @PathParam("executionId") String executionId);

+}

diff --git a/otf-service-api/src/main/java/org/oran/otf/api/service/TestInstanceService.java b/otf-service-api/src/main/java/org/oran/otf/api/service/TestInstanceService.java
new file mode 100644
index 0000000..6b60801
--- /dev/null
+++ b/otf-service-api/src/main/java/org/oran/otf/api/service/TestInstanceService.java
@@ -0,0 +1,374 @@
+/*  Copyright (c) 2019 AT&T Intellectual Property.                             #

+#                                                                              #

+#   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.oran.otf.api.service;

+

+import org.oran.otf.common.model.TestExecution;

+import org.oran.otf.common.model.TestInstance;

+import org.oran.otf.common.model.local.OTFApiResponse;

+import org.oran.otf.common.model.local.TestInstanceCreateRequest;

+import org.oran.otf.common.model.local.WorkflowRequest;

+import io.swagger.v3.oas.annotations.Operation;

+import io.swagger.v3.oas.annotations.Parameter;

+import io.swagger.v3.oas.annotations.media.ArraySchema;

+import io.swagger.v3.oas.annotations.media.Content;

+import io.swagger.v3.oas.annotations.media.Schema;

+import io.swagger.v3.oas.annotations.responses.ApiResponse;

+import io.swagger.v3.oas.annotations.tags.Tag;

+import javax.ws.rs.Consumes;

+import javax.ws.rs.GET;

+import javax.ws.rs.HeaderParam;

+import javax.ws.rs.POST;

+import javax.ws.rs.Path;

+import javax.ws.rs.PathParam;

+import javax.ws.rs.Produces;

+import javax.ws.rs.core.MediaType;

+import javax.ws.rs.core.Response;

+

+import org.springframework.stereotype.Component;

+

+@Component

+@Path("/testInstance")

+@Tag(name = "Test Services", description = "")

+@Produces(MediaType.APPLICATION_JSON)

+public interface TestInstanceService {

+  @POST

+  @Path("/execute/v1/id/{testInstanceId}")

+  @Operation(

+      description =

+          "Execute a test instance by it's unique identifier. Test instances can be executed"

+              + " either both synchronously and asynchronously.",

+      summary = "Execute test instance by id",

+      responses = {

+        @ApiResponse(

+            responseCode = "200",

+            description =

+                "A successful synchronously executed test returns a test execution object",

+            content =

+                @Content(

+                    mediaType = "application/json",

+                    schema = @Schema(implementation = TestExecution.class))),

+        @ApiResponse(

+            responseCode = "201",

+            description =

+                "A successful asynchronously executed test instance returns a base test execution.",

+            content =

+                @Content(

+                    mediaType = "application/json",

+                    schema = @Schema(implementation = TestExecution.class))),

+        @ApiResponse(

+            responseCode = "401",

+            description =

+                "The mechanized identifier used with the request is prohibited from accessing the resource.",

+            content = {

+              @Content(

+                  mediaType = "application/json",

+                  schema = @Schema(implementation = OTFApiResponse.class))

+            })

+      })

+  @Consumes(MediaType.APPLICATION_JSON)

+  @Produces(MediaType.APPLICATION_JSON)

+  Response execute(

+      @Parameter(

+              allowEmptyValue = false,

+              description = "A string representation of a BSON ObjectId",

+              example = "12345678912345678912345f",

+              required = true,

+              schema =

+                  @Schema(

+                      type = "string",

+                      format = "objectid",

+                      description = "The UUID of the test instance"))

+          @PathParam("testInstanceId")

+          String testInstanceId,

+      @Parameter(

+              allowEmptyValue = false,

+              description = "Base64 encoded Application Authorization Framework credentials",

+              example = "Basic b3RmQGF0dC5jb206cGFzc3dvcmQxMjM=",

+              required = true)

+          @HeaderParam("Authorization")

+          String authorization,

+      WorkflowRequest request);

+

+  @POST

+  @Operation(

+      description = "Create a test instance using the latest version of the test definition.",

+      summary = "Create test instance by test definition id",

+      responses = {

+        @ApiResponse(

+            responseCode = "201",

+            description = "The created Test Instance object is returned when it is created",

+            content = {

+              @Content(

+                  mediaType = "application/json",

+                  schema = @Schema(implementation = TestInstance.class))

+            })

+      })

+  @Consumes(MediaType.APPLICATION_JSON)

+  @Produces(MediaType.APPLICATION_JSON)

+  @Path("/create/v1/testDefinitionId/{testDefinitionId}")

+  Response createByTestDefinitionId(

+      @Parameter(

+              allowEmptyValue = false,

+              description = "A string representation of a BSON ObjectId",

+              example = "12345678912345678912345f",

+              required = true,

+              schema =

+                  @Schema(

+                      type = "string",

+                      format = "uuid",

+                      description = "The UUID of the test definition"))

+          @PathParam("testDefinitionId")

+          String testDefinitionId,

+      @Parameter(

+              allowEmptyValue = false,

+              description = "Base64 encoded Application Authorization Framework credentials",

+              example = "Basic b3RmQGF0dC5jb206cGFzc3dvcmQxMjM=",

+              required = true)

+          @HeaderParam("Authorization")

+          String authorization,

+      TestInstanceCreateRequest request);

+

+  @POST

+  @Operation(

+      description = "Create a test instance using the specified version of the test definition",

+      summary = "Create test instance by test definition id and version",

+      responses = {

+        @ApiResponse(

+            responseCode = "201",

+            description = "The created Test Instance object is returned when it is created",

+            content = {

+              @Content(

+                  mediaType = "application/json",

+                  schema = @Schema(implementation = TestInstance.class))

+            })

+      })

+  @Consumes(MediaType.APPLICATION_JSON)

+  @Produces(MediaType.APPLICATION_JSON)

+  @Path("/create/v1/testDefinitionId/{testDefinitionId}/version/{version}")

+  Response createByTestDefinitionId(

+      @Parameter(

+              allowEmptyValue = false,

+              description = "A string representation of a BSON ObjectId",

+              example = "12345678912345678912345f",

+              required = true,

+              schema =

+                  @Schema(

+                      type = "string",

+                      format = "uuid",

+                      description = "The UUID of the test definition."))

+          @PathParam("testDefinitionId")

+          String testDefinitionId,

+      @Parameter(

+              allowEmptyValue = false,

+              description = "The version of the test definition used to create the instance",

+              example = "2",

+              required = true)

+          @PathParam("version")

+          int version,

+      @Parameter(

+              allowEmptyValue = false,

+              description = "Base64 encoded Application Authorization Framework credentials",

+              example = "Basic b3RmQGF0dC5jb206cGFzc3dvcmQxMjM=",

+              required = true)

+          @HeaderParam("Authorization")

+          String authorization,

+      TestInstanceCreateRequest request);

+

+  @POST

+  @Operation(

+      description = "Create a test instance using the latest version of the test definition",

+      summary = "Create test instance by process definition key",

+      responses = {

+        @ApiResponse(

+            responseCode = "201",

+            description = "The created Test Instance object is returned when it is created",

+            content = {

+              @Content(

+                  mediaType = "application/json",

+                  schema = @Schema(implementation = TestInstance.class))

+            })

+      })

+  @Consumes(MediaType.APPLICATION_JSON)

+  @Produces(MediaType.APPLICATION_JSON)

+  @Path("/create/v1/processDefinitionKey/{processDefinitionKey}")

+  Response createByProcessDefinitionKey(

+      @Parameter(

+              allowEmptyValue = false,

+              description = "The process definition key associated with the test definition",

+              example = "someUniqueProcessDefinitionKey",

+              required = true)

+          @PathParam("processDefinitionKey")

+          String processDefinitionKey,

+      @Parameter(

+              allowEmptyValue = false,

+              description = "Base64 encoded Application Authorization Framework credentials",

+              example = "Basic b3RmQGF0dC5jb206cGFzc3dvcmQxMjM=",

+              required = true)

+          @HeaderParam("Authorization")

+          String authorization,

+      TestInstanceCreateRequest request);

+

+  @POST

+  @Operation(

+      description = "Create a test instance using the unique process definition key and version",

+      summary = "Create test instance by process definition key and version",

+      responses = {

+        @ApiResponse(

+            responseCode = "201",

+            description = "The created Test Instance object is returned when it is created",

+            content = {

+              @Content(

+                  mediaType = "application/json",

+                  schema = @Schema(implementation = TestInstance.class))

+            })

+      })

+  @Consumes(MediaType.APPLICATION_JSON)

+  @Produces(MediaType.APPLICATION_JSON)

+  @Path("/create/v1/processDefinitionKey/{processDefinitionKey}/version/{version}")

+  Response createByProcessDefinitionKey(

+      @Parameter(

+              allowEmptyValue = false,

+              description = "The process definition key associated with the test definition",

+              example = "someUniqueProcessDefinitionKey",

+              required = true)

+          @PathParam("processDefinitionKey")

+          String processDefinitionKey,

+      @Parameter(

+              allowEmptyValue = false,

+              description = "The version of the test definition used to create the instance",

+              example = "2",

+              required = true)

+          @PathParam("version")

+          int version,

+      @Parameter(

+              allowEmptyValue = false,

+              description = "Base64 encoded Application Authorization Framework credentials",

+              example = "Basic b3RmQGF0dC5jb206cGFzc3dvcmQxMjM=",

+              required = true)

+          @HeaderParam("Authorization")

+          String authorization,

+      TestInstanceCreateRequest request);

+

+  @GET

+  @Operation(

+      description = "Finds a test instance by it's unique identifier",

+      summary = "Find test instance by id",

+      responses = {

+        @ApiResponse(

+            responseCode = "200",

+            description = "A Test Instance object is returned if it exists",

+            content = {

+              @Content(

+                  mediaType = "application/json",

+                  schema = @Schema(implementation = TestInstance.class))

+            })

+      })

+  @Produces(MediaType.APPLICATION_JSON)

+  @Path("/v1/id/{id}")

+  Response findById(

+      @Parameter(

+              allowEmptyValue = false,

+              description = "A string representation of a BSON ObjectId",

+              example = "12345678912345678912345f",

+              required = true,

+              schema =

+                  @Schema(

+                      type = "string",

+                      format = "uuid",

+                      description = "The UUID of the test instance"))

+          @PathParam("id")

+          String testInstanceId,

+      @Parameter(

+              allowEmptyValue = false,

+              description = "Base64 encoded Application Authorization Framework credentials",

+              example = "Basic b3RmQGF0dC5jb206cGFzc3dvcmQxMjM=",

+              required = true)

+          @HeaderParam("Authorization")

+          String authorization);

+

+  @GET

+  @Operation(

+      description = "Finds all test instance belonging to the unique process definition key",

+      summary = "Find test instance(s) by process definition key",

+      responses = {

+        @ApiResponse(

+            responseCode = "200",

+            description = "An array of found Test Instance objects are returned",

+            content = {

+              @Content(

+                  array = @ArraySchema(schema = @Schema(implementation = TestInstance.class)),

+                  mediaType = "application/json")

+            })

+      })

+  @Produces(MediaType.APPLICATION_JSON)

+  @Path("/v1/processDefinitionKey/{processDefinitionKey}")

+  Response findByProcessDefinitionKey(

+      @Parameter(

+              allowEmptyValue = false,

+              description = "The process definition key associated with the test definition",

+              example = "someUniqueProcessDefinitionKey",

+              required = true)

+          @PathParam("processDefinitionKey")

+          String processDefinitionKey,

+      @Parameter(

+              allowEmptyValue = false,

+              description = "Base64 encoded Application Authorization Framework credentials",

+              example = "Basic b3RmQGF0dC5jb206cGFzc3dvcmQxMjM=",

+              required = true)

+          @HeaderParam("Authorization")

+          String authorization);

+

+  @GET

+  @Operation(

+      description =

+          "Finds all test instance belonging to the unique process definition key and version",

+      summary = "Find test instance(s) by process definition key and version",

+      responses = {

+        @ApiResponse(

+            responseCode = "200",

+            description = "An array of found Test Instance objects are returned",

+            content = {

+              @Content(

+                  array = @ArraySchema(schema = @Schema(implementation = TestInstance.class)),

+                  mediaType = "application/json")

+            })

+      })

+  @Produces(MediaType.APPLICATION_JSON)

+  @Path("/v1/processDefinitionKey/{processDefinitionKey}/version/{version}")

+  Response findByProcessDefinitionKeyAndVersion(

+      @Parameter(

+              allowEmptyValue = false,

+              description = "The process definition key associated with the test definition",

+              example = "someUniqueProcessDefinitionKey",

+              required = true)

+          @PathParam("processDefinitionKey")

+          String processDefinitionKey,

+      @Parameter(

+              allowEmptyValue = false,

+              description = "The version of the test definition used to create the instance",

+              example = "2",

+              required = true)

+          @PathParam("version")

+          String version,

+      @Parameter(

+              allowEmptyValue = false,

+              description = "Base64 encoded Application Authorization Framework credentials",

+              example = "Basic b3RmQGF0dC5jb206cGFzc3dvcmQxMjM=",

+              required = true)

+          @HeaderParam("Authorization")

+          String authorization);

+}

diff --git a/otf-service-api/src/main/java/org/oran/otf/api/service/TestStrategyService.java b/otf-service-api/src/main/java/org/oran/otf/api/service/TestStrategyService.java
new file mode 100644
index 0000000..84b2535
--- /dev/null
+++ b/otf-service-api/src/main/java/org/oran/otf/api/service/TestStrategyService.java
@@ -0,0 +1,66 @@
+/*  Copyright (c) 2019 AT&T Intellectual Property.                             #

+#                                                                              #

+#   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.oran.otf.api.service;

+

+import io.swagger.annotations.Api;

+import io.swagger.v3.oas.annotations.Hidden;

+import io.swagger.v3.oas.annotations.tags.Tag;

+import java.io.InputStream;

+import javax.ws.rs.Consumes;

+import javax.ws.rs.DELETE;

+import javax.ws.rs.HeaderParam;

+import javax.ws.rs.POST;

+import javax.ws.rs.Path;

+import javax.ws.rs.PathParam;

+import javax.ws.rs.Produces;

+import javax.ws.rs.core.MediaType;

+import javax.ws.rs.core.Response;

+import org.glassfish.jersey.media.multipart.FormDataParam;

+

+@Api

+@Hidden

+@Path("/testStrategy")

+@Tag(name = "Test Service", description = "Deploy and delete test strategies to and from the test control unit. (This documentation will only be available to the development team)")

+@Produces({MediaType.APPLICATION_JSON})

+public interface TestStrategyService {

+  @POST

+  @Hidden

+  @Path("/deploy/v1")

+  @Consumes(MediaType.MULTIPART_FORM_DATA)

+  @Produces(MediaType.APPLICATION_JSON)

+  Response deployTestStrategy(

+      @FormDataParam("bpmn") InputStream bpmn,

+      @FormDataParam("resources") InputStream compressedResources,

+      @FormDataParam("testDefinitionId") String testDefinitionId,

+      @FormDataParam("testDefinitionDeployerId") String testDefinitionDeployerId,

+      @FormDataParam("definitionId") String definitionId,

+      @HeaderParam("Authorization") String authorization);

+

+  @DELETE

+  @Hidden

+  @Path("/delete/v1/testDefinitionId/{testDefinitionId}")

+  Response deleteByTestDefinitionId(

+      @PathParam("testDefinitionId") String testDefinitionId,

+      @HeaderParam("authorization") String authorization);

+

+  @DELETE

+  @Hidden

+  @Path("/delete/v1/deploymentId/{deploymentId}")

+  Response deleteByDeploymentId(

+      @PathParam("deploymentId") String deploymentId,

+      @HeaderParam("authorization") String authorization);

+}

diff --git a/otf-service-api/src/main/java/org/oran/otf/api/service/VirtualTestHeadService.java b/otf-service-api/src/main/java/org/oran/otf/api/service/VirtualTestHeadService.java
new file mode 100644
index 0000000..9c6ed6f
--- /dev/null
+++ b/otf-service-api/src/main/java/org/oran/otf/api/service/VirtualTestHeadService.java
@@ -0,0 +1,55 @@
+/*  Copyright (c) 2019 AT&T Intellectual Property.                             #

+#                                                                              #

+#   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.oran.otf.api.service;

+

+import org.oran.otf.common.model.TestHead;

+import org.oran.otf.common.model.local.OTFApiResponse;

+import io.swagger.annotations.Api;

+import io.swagger.v3.oas.annotations.Operation;

+import io.swagger.v3.oas.annotations.media.Content;

+import io.swagger.v3.oas.annotations.media.Schema;

+import io.swagger.v3.oas.annotations.responses.ApiResponse;

+import io.swagger.v3.oas.annotations.tags.Tag;

+

+import javax.ws.rs.*;

+import javax.ws.rs.core.HttpHeaders;

+import javax.ws.rs.core.MediaType;

+import javax.ws.rs.core.Response;

+

+

+@Api

+@Path("/virtualTestHead")

+@Tag(name = "Health Service", description = "Query the availability of the API")

+@Produces({MediaType.APPLICATION_JSON})

+public interface VirtualTestHeadService {

+

+    @PATCH

+    @Path("/v1/{testHeadName}")

+    @Produces({MediaType.APPLICATION_JSON})

+    @Operation(

+            summary = "Used to update fields in the virtual test head",

+            responses = {

+                    @ApiResponse(

+                            responseCode = "200",

+                            description = "The response will include the new vth object",

+                            content =

+                            @Content(

+                                    mediaType = "application/json",

+                                    schema = @Schema(implementation = OTFApiResponse.class)))

+            })

+    Response updateVirtualTestHead(@HeaderParam(HttpHeaders.AUTHORIZATION) String authorization, @PathParam("testHeadName") String testHeadName, TestHead newTestHead);

+}

diff --git a/otf-service-api/src/main/java/org/oran/otf/api/service/impl/HealthServiceImpl.java b/otf-service-api/src/main/java/org/oran/otf/api/service/impl/HealthServiceImpl.java
new file mode 100644
index 0000000..ed4755a
--- /dev/null
+++ b/otf-service-api/src/main/java/org/oran/otf/api/service/impl/HealthServiceImpl.java
@@ -0,0 +1,34 @@
+/*  Copyright (c) 2019 AT&T Intellectual Property.                             #

+#                                                                              #

+#   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.oran.otf.api.service.impl;

+

+import org.oran.otf.api.service.HealthService;

+import org.oran.otf.common.model.local.OTFApiResponse;

+import javax.ws.rs.core.Response;

+

+import org.springframework.stereotype.Service;

+

+@Service

+public class HealthServiceImpl implements HealthService {

+

+  public HealthServiceImpl() {}

+

+  @Override

+  public Response getHealth() {

+    return Response.ok(new OTFApiResponse(200, "UP")).build();

+  }

+}

diff --git a/otf-service-api/src/main/java/org/oran/otf/api/service/impl/OtfOpenServiceImpl.java b/otf-service-api/src/main/java/org/oran/otf/api/service/impl/OtfOpenServiceImpl.java
new file mode 100644
index 0000000..bfff6bb
--- /dev/null
+++ b/otf-service-api/src/main/java/org/oran/otf/api/service/impl/OtfOpenServiceImpl.java
@@ -0,0 +1,49 @@
+/*  Copyright (c) 2019 AT&T Intellectual Property.                             #

+#                                                                              #

+#   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.oran.otf.api.service.impl;

+

+import org.oran.otf.api.Utilities;

+import org.oran.otf.api.Utilities.LogLevel;

+import org.oran.otf.api.service.OtfOpenService;

+import com.google.gson.JsonObject;

+import com.google.gson.JsonParser;

+import javax.ws.rs.core.Response;

+import org.apache.http.HttpResponse;

+import org.apache.http.util.EntityUtils;

+import org.springframework.stereotype.Service;

+

+@Service

+public class OtfOpenServiceImpl implements OtfOpenService {

+

+  @Override

+  public Response convertSwaggerFile() {

+    try {

+      HttpResponse res =

+          Utilities.Http.httpGetUsingAAF("https://localhost:8443/otf/api/openapi.json");

+      String resStr = EntityUtils.toString(res.getEntity());

+      JsonObject resJson = new JsonParser().parse(resStr).getAsJsonObject();

+      if (resJson.has("openapi")) {

+        resJson.addProperty("openapi", "3.0.0");

+        return Response.ok(resJson.toString()).build();

+      }

+    } catch (Exception e) {

+      Utilities.printStackTrace(e, LogLevel.ERROR);

+    }

+

+    return Utilities.Http.BuildResponse.internalServerError();

+  }

+}

diff --git a/otf-service-api/src/main/java/org/oran/otf/api/service/impl/TestExecutionServiceImpl.java b/otf-service-api/src/main/java/org/oran/otf/api/service/impl/TestExecutionServiceImpl.java
new file mode 100644
index 0000000..e758c6b
--- /dev/null
+++ b/otf-service-api/src/main/java/org/oran/otf/api/service/impl/TestExecutionServiceImpl.java
@@ -0,0 +1,160 @@
+/*  Copyright (c) 2019 AT&T Intellectual Property.                             #

+#                                                                              #

+#   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.oran.otf.api.service.impl;

+

+import org.oran.otf.api.Utilities;

+import org.oran.otf.api.service.TestExecutionService;

+import org.oran.otf.common.model.Group;

+import org.oran.otf.common.model.TestExecution;

+import org.oran.otf.common.model.User;

+import org.oran.otf.common.repository.GroupRepository;

+import org.oran.otf.common.repository.TestExecutionRepository;

+import org.oran.otf.common.repository.UserRepository;

+import org.oran.otf.common.utility.permissions.PermissionChecker;

+import org.oran.otf.common.utility.permissions.UserPermission;

+import com.google.gson.JsonElement;

+import com.google.gson.JsonObject;

+import com.google.gson.JsonParser;

+import java.util.Optional;

+import java.util.UUID;

+import javax.ws.rs.core.Response;

+

+import org.slf4j.Logger;

+import org.slf4j.LoggerFactory;

+import org.springframework.beans.factory.annotation.Autowired;

+import org.springframework.stereotype.Service;

+

+@Service

+public class TestExecutionServiceImpl implements TestExecutionService {

+  private static final Logger logger = LoggerFactory.getLogger(TestExecutionServiceImpl.class);

+  @Autowired

+  private UserRepository userRepository;

+  @Autowired private TestExecutionRepository testExecutionRepository;

+  @Autowired private GroupRepository groupRepository;

+

+  @Override

+  public Response getExecutionStatus(String authorization, String executionId) {

+    if (authorization == null) {

+      return Utilities.Http.BuildResponse.unauthorizedWithMessage("Missing authorization header.");

+    }

+    // check if the executionId is a valid UUID

+    try {

+      UUID.fromString(executionId);

+    } catch (IllegalArgumentException e) {

+      return Utilities.Http.BuildResponse.badRequestWithMessage(

+          "Invalid execution identifier. Expected type is UUID (v4).");

+    }

+

+    // try to find the test execution

+    Optional<TestExecution> optionalTestExecution =

+        testExecutionRepository.findFirstByProcessInstanceId(executionId);

+    TestExecution testExecution = Utilities.resolveOptional(optionalTestExecution);

+    if (testExecution == null) {

+      return Utilities.Http.BuildResponse.badRequestWithMessage(

+          String.format("An execution with identifier %s was not found.", executionId));

+    }

+

+    // try to find the group of the test execution

+    String testExecutionGroupId = testExecution.getGroupId().toString();

+    Optional<Group> optionalGroup = groupRepository.findById(testExecutionGroupId);

+    Group group = Utilities.resolveOptional(optionalGroup);

+    if (group == null) {

+      return Utilities.Http.BuildResponse.badRequestWithMessage(

+          String.format(

+              "The group (id: %s) associated with the test execution does not exist.",

+              testExecutionGroupId));

+    }

+

+    // try to find the user for the mechanizedId used to make this request

+    User user = Utilities.findUserByAuthHeader(authorization, userRepository);

+    if (user == null) {

+      return Utilities.Http.BuildResponse.badRequestWithMessage(

+          "No user associated with mechanized identifier used for this request.");

+    }

+    // if it doesnt have read permission then return bad request

+    if (!PermissionChecker.hasPermissionTo(user,group, UserPermission.Permission.READ,groupRepository)){

+      return Utilities.Http.BuildResponse.unauthorizedWithMessage(

+          "Unauthorized to view this test execution.");

+    }

+    // Used the build the final response to be returned

+    JsonObject res = new JsonObject();

+    try {

+      // Parsing is required to prevent Gson from escaping all the characters of the json

+      JsonElement testExecutionParsed = new JsonParser().parse(testExecution.toString());

+      res.add("testExecution", testExecutionParsed);

+      // Get the state of the process instance using the Camunda REST API

+      JsonObject procInstStatus = Utilities.Camunda.processInstanceStatus(executionId);

+      // Extract the state of the process instance from the JSON response

+      String processInstanceState =

+          procInstStatus.getAsJsonObject("historicProcessInstance").get("state").getAsString();

+      // Add the result to the final response

+      res.addProperty("state", processInstanceState);

+    } catch (NullPointerException npe) {

+      // In the case of a null pointer exception, make it clear that the state was not able

+      // to be determined using the Camunda API.

+      logger.error("Unable to determine the live status of the test execution.");

+      res.addProperty("state", "Unable to determine");

+    }

+    // Send the response

+    return Response.ok(res.toString()).build();

+  }

+

+  @Override

+  public Response getExecution(String authorization, String processInstanceId) {

+    try {

+      UUID.fromString(processInstanceId);

+    } catch (IllegalArgumentException e) {

+      return Utilities.Http.BuildResponse.badRequestWithMessage(

+          "Invalid execution identifier. Expected type is UUID (v4).");

+    }

+

+    // try to find the test execution

+    Optional<TestExecution> optionalTestExecution =

+        testExecutionRepository.findFirstByProcessInstanceId(processInstanceId);

+    TestExecution testExecution = Utilities.resolveOptional(optionalTestExecution);

+    if (testExecution == null) {

+      return Utilities.Http.BuildResponse.badRequestWithMessage(

+          String.format("An execution with identifier %s was not found.", processInstanceId));

+    }

+

+    // try to find the group of the test execution

+    String testExecutionGroupId = testExecution.getGroupId().toString();

+    Optional<Group> optionalGroup = groupRepository.findById(testExecutionGroupId);

+    Group group = Utilities.resolveOptional(optionalGroup);

+    if (group == null) {

+      return Utilities.Http.BuildResponse.badRequestWithMessage(

+          String.format(

+              "The group (id: %s) associated with the test execution does not exist.",

+              testExecutionGroupId));

+    }

+

+    // try to find the user for the mechanizedId used to make this request

+    User user = Utilities.findUserByAuthHeader(authorization, userRepository);

+    if (user == null) {

+      return Utilities.Http.BuildResponse.badRequestWithMessage(

+          "No user associated with mechanized identifier used" + " for this request.");

+    }

+

+    // if it doesnt have read permission then return bad request

+    if (!PermissionChecker.hasPermissionTo(user,group,UserPermission.Permission.READ,groupRepository)){

+      return Utilities.Http.BuildResponse.unauthorizedWithMessage(

+          "Unauthorized to view this test execution.");

+    }

+

+    return Response.ok(testExecution.toString()).build();

+  }

+}

diff --git a/otf-service-api/src/main/java/org/oran/otf/api/service/impl/TestInstanceServiceImpl.java b/otf-service-api/src/main/java/org/oran/otf/api/service/impl/TestInstanceServiceImpl.java
new file mode 100644
index 0000000..d171206
--- /dev/null
+++ b/otf-service-api/src/main/java/org/oran/otf/api/service/impl/TestInstanceServiceImpl.java
@@ -0,0 +1,807 @@
+/*  Copyright (c) 2019 AT&T Intellectual Property.                             #

+#                                                                              #

+#   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.oran.otf.api.service.impl;

+

+import org.oran.otf.api.Utilities;

+import org.oran.otf.api.Utilities.LogLevel;

+import org.oran.otf.api.handler.CamundaProcessExecutionHandler;

+import org.oran.otf.api.service.TestInstanceService;

+import org.oran.otf.common.model.Group;

+import org.oran.otf.common.model.TestDefinition;

+import org.oran.otf.common.model.TestInstance;

+import org.oran.otf.common.model.User;

+import org.oran.otf.common.model.local.BpmnInstance;

+import org.oran.otf.common.model.local.TestInstanceCreateRequest;

+import org.oran.otf.common.model.local.WorkflowRequest;

+import org.oran.otf.common.repository.GroupRepository;

+import org.oran.otf.common.repository.TestDefinitionRepository;

+import org.oran.otf.common.repository.TestInstanceRepository;

+import org.oran.otf.common.repository.UserRepository;

+import org.oran.otf.common.utility.Utility;

+import org.oran.otf.common.utility.database.Generic;

+import org.oran.otf.common.utility.http.ResponseUtility;

+import org.oran.otf.common.utility.permissions.PermissionChecker;

+import org.oran.otf.common.utility.permissions.UserPermission;

+import com.google.common.base.Strings;

+import org.bson.types.ObjectId;

+import org.slf4j.Logger;

+import org.slf4j.LoggerFactory;

+import org.springframework.beans.factory.annotation.Autowired;

+import org.springframework.stereotype.Service;

+

+import javax.ws.rs.core.MediaType;

+import javax.ws.rs.core.Response;

+import java.util.*;

+

+@Service

+public class TestInstanceServiceImpl implements TestInstanceService {

+    @Autowired

+    CamundaProcessExecutionHandler camundaProcessExecutionHandler;

+    @Autowired

+    UserRepository userRepository;

+    @Autowired

+    TestInstanceRepository testInstanceRepository;

+    @Autowired

+    TestDefinitionRepository testDefinitionRepository;

+    @Autowired

+    GroupRepository groupRepository;

+

+    private static final Logger logger = LoggerFactory.getLogger(TestInstanceServiceImpl.class);

+    private static final String logPrefix = Utility.getLoggerPrefix();

+

+    @Override

+    public Response execute(String testInstanceId, String authorization, WorkflowRequest request) {

+        try {

+            if (request == null) {

+                return ResponseUtility.Build.badRequestWithMessage("Request body is null.");

+            }

+

+            // Check if the testInstanceId is a valid BSON ObjectI, otherwise return a bad request

+            // response.

+            if (!Utilities.isObjectIdValid(testInstanceId)) {

+                String error =

+                        String.format(

+                                "%sThe testInstanceId, %s, is not a valid ObjectId (BSON).",

+                                logPrefix, testInstanceId);

+                return ResponseUtility.Build.badRequestWithMessage(error);

+            }

+

+            // Create an ObjectId now that we know the provided String was valid.

+            ObjectId oiTestInstanceId = new ObjectId(testInstanceId);

+            // Check if the testInstance exists, otherwise return a not found response.

+            TestInstance testInstance = Generic.findByIdGeneric(testInstanceRepository, oiTestInstanceId);

+            if (testInstance == null) {

+                String error =

+                        String.format(

+                                "%sThe testInstance with _id, %s, was not found.", logPrefix, testInstanceId);

+                return ResponseUtility.Build.notFoundWithMessage(error);

+            }

+            // Check if the testDefinition exists.

+            TestDefinition testDefinition =

+                    Generic.findByIdGeneric(testDefinitionRepository, testInstance.getTestDefinitionId());

+            if (testDefinition == null) {

+                String error =

+                        String.format(

+                                "%sThe testDefinition with _id, %s, was not found.",

+                                logPrefix, testInstance.getTestDefinitionId().toString());

+                return ResponseUtility.Build.notFoundWithMessage(error);

+            }

+

+            // Check if a user associated with the mechanizedId used in the authorization header exists in

+            // the database.

+            User mechanizedIdUser = Utilities.findUserByAuthHeader(authorization, userRepository);

+            if (mechanizedIdUser == null) {

+                String[] decodedAuth = Utilities.decodeBase64AuthorizationHeader(authorization);

+                if (decodedAuth == null) {

+                    return ResponseUtility.Build.badRequestWithMessage(

+                            String.format("Unable to decode authorization header: %s", authorization));

+                }

+                String error =

+                        String.format(

+                                "%sMechanizedId is not onboarded with OTF. %s.", logPrefix, decodedAuth[0]);

+                return ResponseUtility.Build.unauthorizedWithMessage(error);

+            }

+

+            // If the mechanizedId is not an OTF mechanizedId, check if the user is authorized to

+            // execute

+            // the test instance. This is required because the executorId only needs to be read from the

+            // otf-frontend component. The user/group system is not fully integrated with AAF, so this

+            // is

+            // required. A better way might be to use certificates to check identities.

+            Group testInstanceGroup = Utilities.resolveOptional(groupRepository.findById(testInstance.getGroupId().toString()));

+            // if we cant find the test instance group then we cant check the permission

+            if (testInstanceGroup == null) {

+                return ResponseUtility.Build.

+                        badRequestWithMessage(

+                                String.format("Can not find test instance group, id:%s", testInstance.getGroupId().toString()));

+            }

+            // If the mechanizedId is authorized, set the executorId in the WorkflowRequest to the

+            // mechanizedId's ObjectId to make sure that the executorId isn't spoofed. Only use the

+            // executorId sent with the request if it uses the OTF mechanizedId because we "trust" it.

+            if (isOtfMechanizedIdentifier(mechanizedIdUser.getEmail()) && request.getExecutorId() != null) {

+                mechanizedIdUser = Utilities.resolveOptional(userRepository.findById(request.getExecutorId().toString()));

+            } else {

+                request.setExecutorId(mechanizedIdUser.get_id());

+            }

+            if (!PermissionChecker.hasPermissionTo(mechanizedIdUser,testInstanceGroup, UserPermission.Permission.EXECUTE,groupRepository)) {

+                String error =

+                        String.format(

+                                "%sUnauthorized the execute test instance with _id, %s.",

+                                logPrefix, testInstanceId);

+                return ResponseUtility.Build.unauthorizedWithMessage(error);

+            }

+

+            // Set the test instance _id after authorization.

+            request.setTestInstanceId(testInstance.get_id());

+

+            // Check if the test instance is disabled.

+            if (testInstance.isDisabled()) {

+                return ResponseUtility.Build.badRequestWithMessage(

+                        String.format("The test instance with identifier %s is disabled.", testInstanceId));

+            }

+            // Check if the test definition is disabled.

+            if (testDefinition.isDisabled()) {

+                return ResponseUtility.Build.badRequestWithMessage(

+                        String.format(

+                                "The test definition with identifier %s is disabled.",

+                                testInstance.getTestDefinitionId().toString()));

+            }

+

+            // Send the request to Camunda.

+            return camundaProcessExecutionHandler.startProcessInstance(request);

+        } catch (Exception e) {

+            Utilities.printStackTrace(e, LogLevel.ERROR);

+            return ResponseUtility.Build.internalServerError();

+        }

+    }

+

+    @Override

+    public Response createByTestDefinitionId(

+            String testDefinitionId, String authorization, TestInstanceCreateRequest request) {

+        try {

+            // Check if a user associated with the mechanizedId used in the authorization header exists in

+            // the database.

+            User mechanizedIdUser = Utilities.findUserByAuthHeader(authorization, userRepository);

+            if (mechanizedIdUser == null) {

+                String[] decodedAuth = Utilities.decodeBase64AuthorizationHeader(authorization);

+                if (decodedAuth == null) {

+                    return ResponseUtility.Build.badRequestWithMessage(

+                            String.format("Unable to decode authorization header: %s", authorization));

+                }

+                String error =

+                        String.format(

+                                "%sMechanizedId is not onboarded with OTF. %s.", logPrefix, decodedAuth[0]);

+                return ResponseUtility.Build.unauthorizedWithMessage(error);

+            }

+

+            // Check if the String correctly parses as an ObjectId.

+            if (!Utilities.isObjectIdValid(testDefinitionId)) {

+                return ResponseUtility.Build.badRequestWithMessage(

+                        String.format(

+                                "The testDefinitionId %s is not a valid BSON ObjectId.", testDefinitionId));

+            }

+            ObjectId oiTestDefintionId = new ObjectId(testDefinitionId);

+

+            // Find the testDefinition

+            TestDefinition testDefinition =

+                    Generic.findByIdGeneric(testDefinitionRepository, oiTestDefintionId);

+            if (testDefinition == null) {

+                return ResponseUtility.Build.notFoundWithMessage(

+                        String.format("Test definition with id, %s, was not found.", testDefinitionId));

+            }

+            // Check if the mechanizedId has access to the test definition.

+            Group testDefGroup = Utilities.resolveOptional(groupRepository.findById(testDefinition.getGroupId().toString()));

+            if (testDefGroup == null) {

+                return ResponseUtility.Build.badRequestWithMessage(

+                        String.format("Can not find test definition's group, id: %s", testDefinition.getGroupId().toString()));

+            }

+//            if (PermissionChecker.hasReadPermission(mechanizedIdUser, testDefGroup, groupRepository)) {

+//                return ResponseUtility.Build.unauthorizedWithMessage(

+//                        String.format(

+//                                "MechanizedId, %s, does not have read access to test definition in group with name, %s",

+//                                mechanizedIdUser.getEmail(), testDefGroup.getGroupName()));

+//            }

+//            if (PermissionChecker.hasWritePermission(mechanizedIdUser, testDefGroup)) {

+//                return ResponseUtility.Build.unauthorizedWithMessage(

+//                        String.format(

+//                                "MechanizedId, %s, does not have write access to the group with name, %s",

+//                                mechanizedIdUser.getEmail(), testDefGroup.getGroupName()));

+//            }

+            if (PermissionChecker.hasPermissionTo(mechanizedIdUser, testDefGroup,

+                    Arrays.asList(UserPermission.Permission.READ,UserPermission.Permission.WRITE),groupRepository))

+            {

+                return ResponseUtility.Build.unauthorizedWithMessage(

+                        String.format(

+                                "MechanizedId, %s, does not have access (read/write) to the group with name, %s",

+                                mechanizedIdUser.getEmail(), testDefGroup.getGroupName()));

+            }

+            // Get the latest version of the test definition to link it with the test instance

+            BpmnInstance bpmnInstance = findBpmnInstance(testDefinition, Integer.MIN_VALUE, true);

+            if (bpmnInstance == null) {

+                return ResponseUtility.Build.notFoundWithMessage(

+                        String.format(

+                                "Test definition with id, %s, does not have any versions associated with it.",

+                                testDefinitionId));

+            }

+

+            TestInstance testInstance =

+                    new TestInstance(

+                            new ObjectId(),

+                            request.getTestInstanceName(),

+                            request.getTestInstanceDescription(),

+                            testDefinition.getGroupId(),

+                            testDefinition.get_id(),

+                            bpmnInstance.getProcessDefinitionId(),

+                            request.isUseLatestTestDefinition(),

+                            false,

+                            request.isSimulationMode(),

+                            request.getMaxExecutionTimeInMillis(),

+                            request.getPfloInput(),

+                            new HashMap<>(),

+                            request.getSimulationVthInput(),

+                            request.getTestData(),

+                            request.getVthInput(),

+                            new Date(System.currentTimeMillis()),

+                            new Date(System.currentTimeMillis()),

+                            mechanizedIdUser.get_id(),

+                            mechanizedIdUser.get_id());

+

+            return Response.ok()

+                    .type(MediaType.APPLICATION_JSON_TYPE)

+                    .entity(testInstance.toString())

+                    .build();

+        } catch (Exception e) {

+            Utilities.printStackTrace(e, LogLevel.ERROR);

+            return ResponseUtility.Build.internalServerError();

+        }

+    }

+

+    @Override

+    public Response createByTestDefinitionId(

+            String testDefinitionId,

+            int version,

+            String authorization,

+            TestInstanceCreateRequest request) {

+        try {

+            // Check if a user associated with the mechanizedId used in the authorization header exists in

+            // the database.

+            User mechanizedIdUser = Utilities.findUserByAuthHeader(authorization, userRepository);

+            if (mechanizedIdUser == null) {

+                String[] decodedAuth = Utilities.decodeBase64AuthorizationHeader(authorization);

+                if (decodedAuth == null) {

+                    return ResponseUtility.Build.badRequestWithMessage(

+                            String.format("Unable to decode authorization header: %s", authorization));

+                }

+                String error =

+                        String.format(

+                                "%sMechanizedId is not onboarded with OTF. %s.", logPrefix, decodedAuth[0]);

+                return ResponseUtility.Build.unauthorizedWithMessage(error);

+            }

+

+            // Check if the String correctly parses as an ObjectId.

+            if (!Utilities.isObjectIdValid(testDefinitionId)) {

+                return ResponseUtility.Build.badRequestWithMessage(

+                        String.format(

+                                "The testDefinitionId %s is not a valid BSON ObjectId.", testDefinitionId));

+            }

+            ObjectId oiTestDefintionId = new ObjectId(testDefinitionId);

+

+            // Find the testDefinition

+            TestDefinition testDefinition =

+                    Generic.findByIdGeneric(testDefinitionRepository, oiTestDefintionId);

+            if (testDefinition == null) {

+                return ResponseUtility.Build.notFoundWithMessage(

+                        String.format("Test definition with id, %s, was not found.", testDefinitionId));

+            }

+            // permission checking

+            Group testDefGroup = Utilities.resolveOptional(groupRepository.findById(testDefinition.getGroupId().toString()));

+            if (testDefGroup == null) {

+                return ResponseUtility.Build.badRequestWithMessage(

+                        String.format("Can not find test definition's group, id: %s", testDefinition.getGroupId().toString()));

+            }

+            // if not otf email and is not authorized

+//            if (PermissionChecker.hasReadPermission(mechanizedIdUser, testDefGroup, groupRepository)) {

+////                return ResponseUtility.Build.unauthorizedWithMessage(

+////                        String.format(

+////                                "MechanizedId, %s, does not have read access to test definition in group with name, %s",

+////                                mechanizedIdUser.getEmail(), testDefGroup.getGroupName()));

+////            }

+////            if (PermissionChecker.hasWritePermission(mechanizedIdUser, testDefGroup)) {

+////                return ResponseUtility.Build.unauthorizedWithMessage(

+////                        String.format(

+////                                "MechanizedId, %s, does not have write access to the group with name, %s",

+////                                mechanizedIdUser.getEmail(), testDefGroup.getGroupName()));

+////            }

+            if (PermissionChecker.hasPermissionTo(mechanizedIdUser, testDefGroup,

+                    Arrays.asList(UserPermission.Permission.READ,UserPermission.Permission.WRITE),groupRepository))

+            {

+                return ResponseUtility.Build.unauthorizedWithMessage(

+                        String.format(

+                                "MechanizedId, %s, does not have access (read/write) to the group with name, %s",

+                                mechanizedIdUser.getEmail(), testDefGroup.getGroupName()));

+            }

+            // Get the latest version of the test definition to link it with the test instance

+            BpmnInstance bpmnInstance = findBpmnInstance(testDefinition, version, false);

+            if (bpmnInstance == null) {

+                return ResponseUtility.Build.notFoundWithMessage(

+                        String.format(

+                                "Test definition with id, %s, does not have any versions associated with it.",

+                                testDefinitionId));

+            }

+

+            TestInstance testInstance =

+                    new TestInstance(

+                            new ObjectId(),

+                            request.getTestInstanceName(),

+                            request.getTestInstanceDescription(),

+                            testDefinition.getGroupId(),

+                            testDefinition.get_id(),

+                            bpmnInstance.getProcessDefinitionId(),

+                            request.isUseLatestTestDefinition(),

+                            false,

+                            request.isSimulationMode(),

+                            request.getMaxExecutionTimeInMillis(),

+                            request.getPfloInput(),

+                            new HashMap<>(),

+                            request.getSimulationVthInput(),

+                            request.getTestData(),

+                            request.getVthInput(),

+                            new Date(System.currentTimeMillis()),

+                            new Date(System.currentTimeMillis()),

+                            mechanizedIdUser.get_id(),

+                            mechanizedIdUser.get_id());

+

+            return Response.ok()

+                    .type(MediaType.APPLICATION_JSON_TYPE)

+                    .entity(testInstance.toString())

+                    .build();

+        } catch (Exception e) {

+            Utilities.printStackTrace(e, LogLevel.ERROR);

+            return ResponseUtility.Build.internalServerError();

+        }

+    }

+

+    @Override

+    public Response createByProcessDefinitionKey(

+            String processDefinitionKey, String authorization, TestInstanceCreateRequest request) {

+        try {

+            // Check if a user associated with the mechanizedId used in the authorization header exists in

+            // the database.

+            User mechanizedIdUser = Utilities.findUserByAuthHeader(authorization, userRepository);

+            if (mechanizedIdUser == null) {

+                String[] decodedAuth = Utilities.decodeBase64AuthorizationHeader(authorization);

+                if (decodedAuth == null) {

+                    return ResponseUtility.Build.badRequestWithMessage(

+                            String.format("Unable to decode authorization header: %s", authorization));

+                }

+                String error =

+                        String.format(

+                                "%sMechanizedId is not onboarded with OTF. %s.", logPrefix, decodedAuth[0]);

+                return ResponseUtility.Build.unauthorizedWithMessage(error);

+            }

+

+            // Check if the String correctly parses as an ObjectId.

+            if (Strings.isNullOrEmpty(processDefinitionKey)) {

+                return ResponseUtility.Build.badRequestWithMessage("The processDefinitionKey is required.");

+            }

+

+            // Find the testDefinition

+            TestDefinition testDefinition =

+                    testDefinitionRepository.findByProcessDefinitionKey(processDefinitionKey).orElse(null);

+            if (testDefinition == null) {

+                return ResponseUtility.Build.notFoundWithMessage(

+                        String.format(

+                                "Test definition with processDefinitionKey, %s, was not found.",

+                                processDefinitionKey));

+            }

+

+            Group testDefGroup = Utilities.resolveOptional(groupRepository.findById(testDefinition.getGroupId().toString()));

+            if (testDefGroup == null) {

+                return ResponseUtility.Build.badRequestWithMessage(

+                        String.format("Can not find test definition's group, id: %s", testDefinition.getGroupId().toString()));

+            }

+            // if not otf email and is not authorized

+//            if (PermissionChecker.hasReadPermission(mechanizedIdUser, testDefGroup, groupRepository)) {

+//                return ResponseUtility.Build.unauthorizedWithMessage(

+//                        String.format(

+//                                "MechanizedId, %s, does not have read access to test definition in group with name, %s",

+//                                mechanizedIdUser.getEmail(), testDefGroup.getGroupName()));

+//            }

+//            if (PermissionChecker.hasWritePermission(mechanizedIdUser, testDefGroup)) {

+//                return ResponseUtility.Build.unauthorizedWithMessage(

+//                        String.format(

+//                                "MechanizedId, %s, does not have write access to the group with name, %s",

+//                                mechanizedIdUser.getEmail(), testDefGroup.getGroupName()));

+//            }

+            if (PermissionChecker.hasPermissionTo(mechanizedIdUser, testDefGroup,

+                    Arrays.asList(UserPermission.Permission.READ,UserPermission.Permission.WRITE),groupRepository))

+            {

+                return ResponseUtility.Build.unauthorizedWithMessage(

+                        String.format(

+                                "MechanizedId, %s, does not have access (read/write) to the group with name, %s",

+                                mechanizedIdUser.getEmail(), testDefGroup.getGroupName()));

+            }

+            // Get the latest version of the test definition to link it with the test instance

+            BpmnInstance bpmnInstance = findBpmnInstance(testDefinition, Integer.MIN_VALUE, false);

+            if (bpmnInstance == null) {

+                return ResponseUtility.Build.notFoundWithMessage(

+                        String.format(

+                                "Test definition with id, %s, does not have any versions associated with it.",

+                                testDefinition.get_id().toString()));

+            }

+

+            TestInstance testInstance =

+                    new TestInstance(

+                            new ObjectId(),

+                            request.getTestInstanceName(),

+                            request.getTestInstanceDescription(),

+                            testDefinition.getGroupId(),

+                            testDefinition.get_id(),

+                            bpmnInstance.getProcessDefinitionId(),

+                            request.isUseLatestTestDefinition(),

+                            false,

+                            request.isSimulationMode(),

+                            request.getMaxExecutionTimeInMillis(),

+                            request.getPfloInput(),

+                            new HashMap<>(),

+                            request.getSimulationVthInput(),

+                            request.getTestData(),

+                            request.getVthInput(),

+                            new Date(System.currentTimeMillis()),

+                            new Date(System.currentTimeMillis()),

+                            mechanizedIdUser.get_id(),

+                            mechanizedIdUser.get_id());

+

+            return Response.ok()

+                    .type(MediaType.APPLICATION_JSON_TYPE)

+                    .entity(testInstance.toString())

+                    .build();

+        } catch (Exception e) {

+            Utilities.printStackTrace(e, LogLevel.ERROR);

+            return ResponseUtility.Build.internalServerError();

+        }

+    }

+

+    @Override

+    public Response createByProcessDefinitionKey(

+            String processDefinitionKey,

+            int version,

+            String authorization,

+            TestInstanceCreateRequest request) {

+        try {

+            // Check if a user associated with the mechanizedId used in the authorization header exists in

+            // the database.

+            User mechanizedIdUser = Utilities.findUserByAuthHeader(authorization, userRepository);

+            if (mechanizedIdUser == null) {

+                String[] decodedAuth = Utilities.decodeBase64AuthorizationHeader(authorization);

+                if (decodedAuth == null) {

+                    return ResponseUtility.Build.badRequestWithMessage(

+                            String.format("Unable to decode authorization header: %s", authorization));

+                }

+                String error =

+                        String.format(

+                                "%sMechanizedId is not onboarded with OTF. %s.", logPrefix, decodedAuth[0]);

+                return ResponseUtility.Build.unauthorizedWithMessage(error);

+            }

+

+            // Check if the String correctly parses as an ObjectId.

+            if (Strings.isNullOrEmpty(processDefinitionKey)) {

+                return ResponseUtility.Build.badRequestWithMessage("The processDefinitionKey is required.");

+            }

+

+            // Find the testDefinition

+            TestDefinition testDefinition =

+                    testDefinitionRepository.findByProcessDefinitionKey(processDefinitionKey).orElse(null);

+            if (testDefinition == null) {

+                return ResponseUtility.Build.notFoundWithMessage(

+                        String.format(

+                                "Test definition with processDefinitionKey, %s, was not found.",

+                                processDefinitionKey));

+            }

+

+            Group testDefGroup = Utilities.resolveOptional(groupRepository.findById(testDefinition.getGroupId().toString()));

+            if (testDefGroup == null) {

+                return ResponseUtility.Build.badRequestWithMessage(

+                        String.format("Can not find test definition's group, id: %s", testDefinition.getGroupId().toString()));

+            }

+            // if not otf email and is not authorized

+//            if (PermissionChecker.hasReadPermission(mechanizedIdUser, testDefGroup, groupRepository)) {

+//                return ResponseUtility.Build.unauthorizedWithMessage(

+//                        String.format(

+//                                "MechanizedId, %s, does not have read access to test definition in group with name, %s",

+//                                mechanizedIdUser.getEmail(), testDefGroup.getGroupName()));

+//            }

+//            if (PermissionChecker.hasWritePermission(mechanizedIdUser, testDefGroup)) {

+//                return ResponseUtility.Build.unauthorizedWithMessage(

+//                        String.format(

+//                                "MechanizedId, %s, does not have write access to the group with name, %s",

+//                                mechanizedIdUser.getEmail(), testDefGroup.getGroupName()));

+//            }

+            if (PermissionChecker.hasPermissionTo(mechanizedIdUser, testDefGroup,

+                    Arrays.asList(UserPermission.Permission.READ,UserPermission.Permission.WRITE),groupRepository))

+            {

+                return ResponseUtility.Build.unauthorizedWithMessage(

+                        String.format(

+                                "MechanizedId, %s, does not have access (read/write) to the group with name, %s",

+                                mechanizedIdUser.getEmail(), testDefGroup.getGroupName()));

+            }

+            // Get the latest version of the test definition to link it with the test instance

+            BpmnInstance bpmnInstance = findBpmnInstance(testDefinition, version, false);

+            if (bpmnInstance == null) {

+                return ResponseUtility.Build.notFoundWithMessage(

+                        String.format(

+                                "Test definition with id, %s, does not have any versions associated with it.",

+                                testDefinition.get_id().toString()));

+            }

+

+            TestInstance testInstance =

+                    new TestInstance(

+                            new ObjectId(),

+                            request.getTestInstanceName(),

+                            request.getTestInstanceDescription(),

+                            testDefinition.getGroupId(),

+                            testDefinition.get_id(),

+                            bpmnInstance.getProcessDefinitionId(),

+                            request.isUseLatestTestDefinition(),

+                            false,

+                            request.isSimulationMode(),

+                            request.getMaxExecutionTimeInMillis(),

+                            request.getPfloInput(),

+                            new HashMap<>(),

+                            request.getSimulationVthInput(),

+                            request.getTestData(),

+                            request.getVthInput(),

+                            new Date(System.currentTimeMillis()),

+                            new Date(System.currentTimeMillis()),

+                            mechanizedIdUser.get_id(),

+                            mechanizedIdUser.get_id());

+

+            return Response.ok()

+                    .type(MediaType.APPLICATION_JSON_TYPE)

+                    .entity(testInstance.toString())

+                    .build();

+        } catch (Exception e) {

+            Utilities.printStackTrace(e, LogLevel.ERROR);

+            return ResponseUtility.Build.internalServerError();

+        }

+    }

+

+    @Override

+    public Response findById(String testInstanceId, String authorization) {

+        // Check if a user associated with the mechanizedId used in the authorization header exists in

+        // the database.

+        User mechanizedIdUser = Utilities.findUserByAuthHeader(authorization, userRepository);

+        if (mechanizedIdUser == null) {

+            String[] decodedAuth = Utilities.decodeBase64AuthorizationHeader(authorization);

+            if (decodedAuth == null) {

+                return ResponseUtility.Build.badRequestWithMessage(

+                        String.format("Unable to decode authorization header: %s", authorization));

+            }

+            String error =

+                    String.format("%sMechanizedId is not onboarded with OTF. %s.", logPrefix, decodedAuth[0]);

+            return ResponseUtility.Build.unauthorizedWithMessage(error);

+        }

+

+        // Check if the testInstanceId is a valid BSON ObjectI, otherwise return a bad request

+        // response.

+        if (!Utilities.isObjectIdValid(testInstanceId)) {

+            String error =

+                    String.format(

+                            "%sThe testInstanceId, %s, is not a valid ObjectId (BSON).",

+                            logPrefix, testInstanceId);

+            return ResponseUtility.Build.badRequestWithMessage(error);

+        }

+

+        // Create an ObjectId now that we know the provided String was valid.

+        ObjectId oiTestInstanceId = new ObjectId(testInstanceId);

+        // Check if the testInstance exists, otherwise return a not found response.

+        TestInstance testInstance = Generic.findByIdGeneric(testInstanceRepository, oiTestInstanceId);

+        if (testInstance == null) {

+            String error =

+                    String.format(

+                            "%sThe testInstance with _id, %s, was not found.", logPrefix, testInstanceId);

+            return ResponseUtility.Build.notFoundWithMessage(error);

+        }

+

+        Group testInstanceGroup = Utilities.resolveOptional(groupRepository.findById(testInstance.getGroupId().toString()));

+        if (testInstanceGroup == null) {

+            return ResponseUtility.Build.badRequestWithMessage(

+                    String.format("Can not find test instance's group, group name :%s", testInstance.get_id().toString()));

+        }

+        if (!PermissionChecker.hasPermissionTo(mechanizedIdUser,testInstanceGroup,UserPermission.Permission.READ,groupRepository)) {

+            return ResponseUtility.Build.unauthorizedWithMessage(

+                    String.format(

+                            "User %s does not have read access to test instance group, group name: %s.",

+                            mechanizedIdUser.getEmail(), testInstanceGroup.getGroupName()));

+        }

+        return Response.ok(testInstance.toString(), MediaType.APPLICATION_JSON_TYPE).build();

+    }

+

+    @Override

+    public Response findByProcessDefinitionKey(String processDefinitionKey, String authorization) {

+        // Check if a user associated with the mechanizedId used in the authorization header exists in

+        // the database.

+        User mechanizedIdUser = Utilities.findUserByAuthHeader(authorization, userRepository);

+        if (mechanizedIdUser == null) {

+            String[] decodedAuth = Utilities.decodeBase64AuthorizationHeader(authorization);

+            if (decodedAuth == null) {

+                return ResponseUtility.Build.badRequestWithMessage(

+                        String.format("Unable to decode authorization header: %s", authorization));

+            }

+            String error =

+                    String.format("%sMechanizedId is not onboarded with OTF. %s.", logPrefix, decodedAuth[0]);

+            return ResponseUtility.Build.unauthorizedWithMessage(error);

+        }

+

+        Optional<TestDefinition> optionalTestDefinition =

+                testDefinitionRepository.findByProcessDefinitionKey(processDefinitionKey);

+        TestDefinition testDefinition = optionalTestDefinition.orElse(null);

+        if (testDefinition == null) {

+            return Utilities.Http.BuildResponse.badRequestWithMessage(

+                    String.format(

+                            "Cannot find test instance because a test"

+                                    + " definition with the process definition key (%s) does not exist.",

+                            processDefinitionKey));

+        }

+

+        List<TestInstance> testInstances =

+                testInstanceRepository.findAllByTestDefinitionId(testDefinition.get_id());

+        if (testInstances.isEmpty()) {

+            return Utilities.Http.BuildResponse.badRequestWithMessage(

+                    String.format(

+                            "No test instances found with process " + "definition key (%s).",

+                            processDefinitionKey));

+        }

+

+        List<TestInstance> result = new ArrayList<>();

+        for (TestInstance testInstance : testInstances) {

+            Group testInstanceGroup = Utilities.resolveOptional(groupRepository.findById(testInstance.getGroupId().toString()));

+            if (testInstanceGroup != null && PermissionChecker.hasPermissionTo(mechanizedIdUser,testInstanceGroup,UserPermission.Permission.READ,groupRepository)) {

+                result.add(testInstance);

+            }

+        }

+

+        return Response.ok(result.toString()).build();

+    }

+

+    @Override

+    public Response findByProcessDefinitionKeyAndVersion(

+            String processDefinitionKey, String version, String authorization) {

+        // Check if a user associated with the mechanizedId used in the authorization header exists in

+        // the database.

+        User mechanizedIdUser = Utilities.findUserByAuthHeader(authorization, userRepository);

+        if (mechanizedIdUser == null) {

+            String[] decodedAuth = Utilities.decodeBase64AuthorizationHeader(authorization);

+            if (decodedAuth == null) {

+                return ResponseUtility.Build.badRequestWithMessage(

+                        String.format("Unable to decode authorization header: %s", authorization));

+            }

+            String error =

+                    String.format("%sMechanizedId is not onboarded with OTF. %s.", logPrefix, decodedAuth[0]);

+            return ResponseUtility.Build.unauthorizedWithMessage(error);

+        }

+

+        Optional<TestDefinition> optionalTestDefinition =

+                testDefinitionRepository.findByProcessDefinitionKey(processDefinitionKey);

+        TestDefinition testDefinition = optionalTestDefinition.orElse(null);

+

+        if (testDefinition == null) {

+            return Utilities.Http.BuildResponse.badRequestWithMessage(

+                    String.format(

+                            "Cannot find test instance because a test"

+                                    + " definition with the process definition key (%s) does not exist.",

+                            processDefinitionKey));

+        }

+

+        int iVersion;

+        try {

+            iVersion = Integer.parseInt(version);

+        } catch (NumberFormatException nfe) {

+            return Utilities.Http.BuildResponse.badRequestWithMessage("Version must be a valid integer.");

+        }

+

+        BpmnInstance bpmnInstance =

+                testDefinition.getBpmnInstances().stream()

+                        .filter(_bpmnInstance -> _bpmnInstance.getVersion() == iVersion)

+                        .findAny()

+                        .orElse(null);

+

+        if (bpmnInstance == null) {

+            return Utilities.Http.BuildResponse.badRequestWithMessage(

+                    String.format("Cannot find any test instances using " + "version %s.", version));

+        }

+

+        List<TestInstance> testInstances =

+                testInstanceRepository.findAllByTestDefinitionIdAndPDId(

+                        testDefinition.get_id(), bpmnInstance.getProcessDefinitionId());

+

+        if (testInstances.isEmpty()) {

+            return Utilities.Http.BuildResponse.badRequestWithMessage(

+                    String.format(

+                            "No test instances found with process " + "definition key (%s).",

+                            processDefinitionKey));

+        }

+

+        List<TestInstance> result = new ArrayList<>();

+        for (TestInstance testInstance : testInstances) {

+            Group testInstanceGroup = Utilities.resolveOptional(groupRepository.findById(testInstance.getGroupId().toString()));

+            if (testInstanceGroup != null && PermissionChecker.hasPermissionTo(mechanizedIdUser,testInstanceGroup,UserPermission.Permission.READ,groupRepository)) {

+                result.add(testInstance);

+            }

+        }

+

+        return Response.ok(result.toString()).build();

+    }

+

+    private boolean isOtfMechanizedIdentifier(String email) {

+        return email.equalsIgnoreCase("email@localhost")

+                || email.equalsIgnoreCase("email@localhost")

+                || email.equalsIgnoreCase("email@localhost")

+                || email.equalsIgnoreCase("email@localhost")

+                || email.equalsIgnoreCase("email@localhost");

+    }

+

+    private BpmnInstance findBpmnInstance(TestDefinition testDefinition, int version, boolean latest)

+            throws Exception {

+        BpmnInstance bpmnInstance = null;

+        int maxVersion = Integer.MIN_VALUE;

+        // Check if the version exists

+        for (BpmnInstance bi : testDefinition.getBpmnInstances()) {

+            // If this field is null or empty, it means the bpmn hasn't been deployed, or there was a

+            // creation error on the Test Definition page (UI). Skip the field so the user isn't allowed

+            // to create a test instance based off this bpmn instance.

+            if (Strings.isNullOrEmpty(bi.getProcessDefinitionId())) {

+                continue;

+            }

+

+            // Split the processDefinitionId based on it's format:

+            // {processDefinitionKey}:{version}:{processDefinitionId}.

+            String processDefinitionId = bi.getProcessDefinitionId();

+            String[] processDefinitionIdSplit = processDefinitionId.split(":");

+            if (processDefinitionIdSplit.length != 3) {

+                throw new Exception(

+                        String.format(

+                                "testDefinition[%s].bpmnInstances.processDefinitionId[%s] is invalid.",

+                                testDefinition.get_id().toString(), bi.getProcessDefinitionId()));

+            }

+

+            String sVersion = processDefinitionIdSplit[1];

+            int currentVersion = Integer.parseInt(sVersion);

+            if (latest && currentVersion > maxVersion) {

+                bpmnInstance = bi;

+            } else if (currentVersion == version) {

+                bpmnInstance = bi;

+                break;

+            }

+        }

+

+        return bpmnInstance;

+    }

+

+//    private boolean isAuthorized(User user, Group group, String permission, GroupRepository groupRepository) {

+//        if (isOtfMechanizedIdentifier(user.getEmail())) {

+//            return true;

+//        }

+//        return PermissionChecker.isAuthorized(user, group, permission.toUpperCase(), groupRepository);

+//    }

+}

+/*

+ PermissionChecker.hasReadPermission(mechanizedIdUser,testInstanceGroup,groupRepository)

+

+  PermissionChecker.hasPermission(mechanizedIdUser,testInstanceGroup,groupRepository, [READ, WRITE])

+  PermissionsMAp = PermissionChecker.Build.hasRead

+ */
\ No newline at end of file
diff --git a/otf-service-api/src/main/java/org/oran/otf/api/service/impl/TestStrategyServiceImpl.java b/otf-service-api/src/main/java/org/oran/otf/api/service/impl/TestStrategyServiceImpl.java
new file mode 100644
index 0000000..13d125a
--- /dev/null
+++ b/otf-service-api/src/main/java/org/oran/otf/api/service/impl/TestStrategyServiceImpl.java
@@ -0,0 +1,228 @@
+/*  Copyright (c) 2019 AT&T Intellectual Property.                             #

+#                                                                              #

+#   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.oran.otf.api.service.impl;

+

+import org.oran.otf.api.Utilities;

+import org.oran.otf.api.handler.CamundaProcessDeploymentHandler;

+import org.oran.otf.api.service.TestStrategyService;

+import org.oran.otf.common.model.TestDefinition;

+import org.oran.otf.common.model.User;

+import org.oran.otf.common.model.local.BpmnInstance;

+import org.oran.otf.common.model.local.DeployTestStrategyRequest;

+import org.oran.otf.common.model.local.OTFApiResponse;

+import org.oran.otf.common.repository.GroupRepository;

+import org.oran.otf.common.repository.TestDefinitionRepository;

+import org.oran.otf.common.repository.UserRepository;

+import org.oran.otf.common.utility.http.ResponseUtility;

+import com.fasterxml.jackson.databind.ObjectMapper;

+import io.swagger.v3.oas.annotations.Hidden;

+import java.io.IOException;

+import java.io.InputStream;

+import java.util.Base64;

+import java.util.Optional;

+import javax.ws.rs.core.MediaType;

+import javax.ws.rs.core.Response;

+import org.apache.http.HttpResponse;

+import org.apache.http.conn.HttpHostConnectException;

+import org.apache.http.util.EntityUtils;

+import org.bson.types.ObjectId;

+import org.slf4j.Logger;

+import org.slf4j.LoggerFactory;

+import org.springframework.beans.factory.annotation.Autowired;

+import org.springframework.stereotype.Service;

+

+@Service

+@Hidden

+public class TestStrategyServiceImpl implements TestStrategyService {

+

+  private static final Logger logger = LoggerFactory.getLogger(TestStrategyServiceImpl.class);

+

+  @Autowired private TestDefinitionRepository testDefinitionRepository;

+

+  @Autowired private UserRepository userRepository;

+

+  @Autowired private CamundaProcessDeploymentHandler camundaProcessDeploymentHandler;

+

+  @Autowired private GroupRepository groupRepository;

+

+  public Response deployTestStrategy(

+      InputStream bpmn,

+      InputStream compressedResources,

+      String testDefinitionId,

+      String testDefinitionDeployerId,

+      String definitionId,

+      String authorization) {

+    if (bpmn == null)

+      return Utilities.Http.BuildResponse.badRequestWithMessage(

+          "BPMN input stream cannot be null.");

+

+    // Decode the authorization header.

+    byte[] decodedAuthorization = Base64.getDecoder().decode(authorization.replace("Basic ", ""));

+    String credentials = new String(decodedAuthorization);

+    String[] credentialsArray = credentials.split(":");

+

+    /* Check if the request came from the system specified mechanized identifier. The request goes through AAF

+     * authorization before reaching this code, therefore, assume the headers aren't spoofed. */

+    if (!credentialsArray[0].equals(System.getenv("AAF_ID")))

+      return Utilities.Http.BuildResponse.badRequestWithMessage(

+          "Unauthorized to use this service.");

+

+    // Map to a POJO model2.

+    ObjectId _testDefinitionDeployerId = null;

+    ObjectId _testDefinitionId = null;

+

+    if (testDefinitionDeployerId != null && ObjectId.isValid(testDefinitionDeployerId))

+      _testDefinitionDeployerId = new ObjectId(testDefinitionDeployerId);

+    if (testDefinitionId != null && ObjectId.isValid(testDefinitionId))

+      _testDefinitionId = new ObjectId(testDefinitionId);

+

+    DeployTestStrategyRequest request =

+        new DeployTestStrategyRequest(_testDefinitionDeployerId, _testDefinitionId, definitionId);

+

+    //		String bpmnContents = null;

+    //		try (final Reader reader = new InputStreamReader(bpmn)) {

+    //			bpmnContents = CharStreams.toString(reader);

+    //	 		} catch (Exception e) {

+    //			e.printStackTrace();

+    //		}

+

+    // Check if the request actually contains a bpmn string.

+    //		try {

+    //			if (bpmnContents == null || bpmnContents.trim().length() == 0)

+    //				return Utilities.Http.BuildResponse.badRequestWithMessage("BPMN contents are null.");

+    //		} catch (Exception e) {

+    //			logger.error(Utilities.getStackTrace(e));

+    //		}

+

+    // If a test definition id is supplied, the request intends to update an existing test

+    // definition.

+    if (request.getTestDefinitionId() != null) {

+      // Check if the test definition exists in the database.

+      Optional<TestDefinition> testDefinitionOptional =

+          testDefinitionRepository.findById(request.getTestDefinitionId().toString());

+

+      if (!testDefinitionOptional.isPresent())

+        return Utilities.Http.BuildResponse.badRequestWithMessage(

+            String.format("Test definition (%s) was not found.", request.getTestDefinitionId()));

+

+      // Check if a user to update the definition was supplied.

+      if (request.getTestDefinitionDeployerId() == null)

+        return Utilities.Http.BuildResponse.badRequestWithMessage(

+            "Must specify testDefinitionDeployerId.");

+

+      // Check if the user requesting to update the definition is the user who originally created

+      // the definition.

+      TestDefinition testDefinition = testDefinitionOptional.get();

+

+      if (!testDefinition

+          .getCreatedBy()

+          .toString()

+          .equals(request.getTestDefinitionDeployerId().toString()))

+        return Utilities.Http.BuildResponse.badRequestWithMessage(

+            String.format(

+                "User (%s) is not authorized to update this test definition.",

+                request.getTestDefinitionDeployerId()));

+

+      // Check if the version to deploy already exists

+      for (BpmnInstance bpmnInstance : testDefinition.getBpmnInstances()) {

+        if (bpmnInstance.getProcessDefinitionId().equalsIgnoreCase(request.getDefinitionId()))

+          return Utilities.Http.BuildResponse.badRequestWithMessage(

+              String.format(

+                  "A deployment with the definitionId %s already exists.",

+                  request.getDefinitionId()));

+      }

+    }

+

+    // Make the deployment request to Camunda. Relay the response received by Camunda.

+    return camundaProcessDeploymentHandler.start(bpmn, compressedResources);

+  }

+

+  public Response deleteByDeploymentId(String deploymentId, String authorization) {

+    User user = Utilities.findUserByAuthHeader(authorization, userRepository);

+    if (!isAuthorized(authorization)) {

+      return Utilities.Http.BuildResponse.unauthorized();

+    }

+

+    String url =

+        String.format(

+            "%s:%s/%s/%s",

+            System.getenv("otf.camunda.host"),

+            System.getenv("otf.camunda.port"),

+            System.getenv("otf.camunda.deploymentDeletionUri"),

+            deploymentId);

+

+    try {

+      HttpResponse res = Utilities.Http.httpDeleteAAF(url);

+      String resStr = EntityUtils.toString(res.getEntity());

+      int status = res.getStatusLine().getStatusCode();

+      return Response.status(status)

+          .type(MediaType.APPLICATION_JSON)

+          .entity(new OTFApiResponse(status, resStr))

+          .build();

+

+    } catch (Exception e) {

+      e.printStackTrace();

+      return Utilities.Http.BuildResponse.internalServerError();

+    }

+  }

+

+  public Response deleteByTestDefinitionId(String testDefinitionId, String authorization) {

+    User user = Utilities.findUserByAuthHeader(authorization, userRepository);

+    if (!isAuthorized(authorization)) {

+      return Utilities.Http.BuildResponse.unauthorizedWithMessage("Authorization headers not set.");

+    }

+

+    String url =

+        String.format(

+            "%s:%s/%s/%s",

+            System.getenv("otf.camunda.host"),

+            System.getenv("otf.camunda.port"),

+            System.getenv("otf.camunda.testDefinitionDeletionUri"),

+            testDefinitionId);

+

+    try {

+      HttpResponse res = Utilities.Http.httpDeleteAAF(url);

+      String resStr = EntityUtils.toString(res.getEntity());

+      int status = res.getStatusLine().getStatusCode();

+      return Response.status(status)

+          .type(MediaType.APPLICATION_JSON)

+          .entity(new OTFApiResponse(status, resStr))

+          .build();

+    } catch (HttpHostConnectException e) {

+      return ResponseUtility.Build.serviceUnavailableWithMessage(e.getMessage());

+    } catch (Exception e) {

+      e.printStackTrace();

+      return Utilities.Http.BuildResponse.internalServerError();

+    }

+  }

+

+  private boolean isAuthorized(String authorization) {

+    User user = Utilities.findUserByAuthHeader(authorization, userRepository);

+    return (user.getEmail().equalsIgnoreCase("email@localhost")

+        || user.getEmail().equalsIgnoreCase("email@localhost"));

+  }

+

+  private DeployTestStrategyRequest mapToDeployTestStrategyRequest(String body) {

+    ObjectMapper mapper = new ObjectMapper();

+    try {

+      return mapper.readValue(body, DeployTestStrategyRequest.class); // Perform the mapping

+    } catch (IOException e) { // Indicates an unknown request body

+      logger.error(e.getMessage());

+      return null;

+    }

+  }

+}

diff --git a/otf-service-api/src/main/java/org/oran/otf/api/service/impl/VirtualTestHeadServiceImpl.java b/otf-service-api/src/main/java/org/oran/otf/api/service/impl/VirtualTestHeadServiceImpl.java
new file mode 100644
index 0000000..d2a662b
--- /dev/null
+++ b/otf-service-api/src/main/java/org/oran/otf/api/service/impl/VirtualTestHeadServiceImpl.java
@@ -0,0 +1,164 @@
+/*  Copyright (c) 2019 AT&T Intellectual Property.                             #

+#                                                                              #

+#   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.oran.otf.api.service.impl;

+

+import org.oran.otf.api.Utilities;

+import org.oran.otf.api.service.VirtualTestHeadService;

+import org.oran.otf.common.model.Group;

+import org.oran.otf.common.model.TestHead;

+import org.oran.otf.common.model.User;

+import org.oran.otf.common.repository.GroupRepository;

+import org.oran.otf.common.repository.TestHeadRepository;

+import org.oran.otf.common.repository.UserRepository;

+import org.oran.otf.common.utility.Utility;

+import org.oran.otf.common.utility.database.Generic;

+import org.oran.otf.common.utility.http.ResponseUtility;

+import org.oran.otf.common.utility.permissions.PermissionChecker;

+import org.oran.otf.common.utility.permissions.UserPermission;

+import org.springframework.beans.factory.annotation.Autowired;

+import org.springframework.data.mongodb.core.MongoTemplate;

+import org.springframework.data.mongodb.core.query.Criteria;

+import org.springframework.data.mongodb.core.query.Query;

+import org.springframework.data.mongodb.core.query.Update;

+import org.springframework.stereotype.Service;

+

+import javax.ws.rs.core.Response;

+import java.util.Date;

+import java.util.Optional;

+

+@Service

+public class VirtualTestHeadServiceImpl implements VirtualTestHeadService {

+

+    @Autowired

+    private UserRepository userRepository;

+    @Autowired

+    private GroupRepository groupRepository;

+

+    @Autowired

+    TestHeadRepository testHeadRepository;

+

+    @Autowired

+    MongoTemplate mongoTemplate;

+

+    private static final String logPrefix = Utility.getLoggerPrefix();

+

+    @Override

+    public Response updateVirtualTestHead(String authorization, String testHeadName, TestHead newTestHead) {

+        if (authorization == null) {

+            return Utilities.Http.BuildResponse.unauthorizedWithMessage("Missing authorization header.");

+        }

+

+        // try to find the test head

+        Optional<TestHead> optionalTestHead =

+                testHeadRepository.findByTestHeadName(testHeadName);

+        TestHead testHead = Utilities.resolveOptional(optionalTestHead);

+        if (testHead == null) {

+            return Utilities.Http.BuildResponse.badRequestWithMessage(

+                    String.format("A test head with identifier %s was not found.", testHeadName));

+        }

+

+        // try to find the group of the test head

+        String testHeadGroupId = testHead.getGroupId().toString();

+        Group testHeadGroup = Generic.findByIdGeneric(groupRepository, testHead.getGroupId());

+        if (testHeadGroup == null) {

+            return Utilities.Http.BuildResponse.badRequestWithMessage(

+                    String.format(

+                            "The group (id: %s) associated with the test head does not exist.",

+                            testHeadGroupId));

+        }

+

+        // try to find the user for the mechanizedId used to make this request

+        User user = Utilities.findUserByAuthHeader(authorization, userRepository);

+        if (user == null) {

+            return Utilities.Http.BuildResponse.badRequestWithMessage(

+                    "No user associated with mechanized identifier used for this request.");

+        }

+

+        if (!PermissionChecker.hasPermissionTo(user, testHeadGroup, UserPermission.Permission.WRITE, groupRepository)) {

+            String error =

+                    String.format(

+                            "Unauthorized the write to test head with name, %s.",

+                            testHeadGroupId);

+            return ResponseUtility.Build.unauthorizedWithMessage(error);

+        }

+

+        return updateTestHeadFields(testHead, newTestHead, user);

+    }

+

+    private Response updateTestHeadFields(TestHead testHead, TestHead newTestHead, User user) {

+        Query select = Query.query(Criteria.where("_id").is(testHead.get_id()));

+        Update update = new Update();

+

+        if (newTestHead.getTestHeadName() != null) {

+            if (doesTestHeadWithNameExist(newTestHead.getTestHeadName())) {

+                String error =

+                        String.format(

+                                "Cant change testHeadName to %s since it already exists.",

+                                newTestHead.getTestHeadName());

+                return ResponseUtility.Build.badRequestWithMessage(error);

+            }

+            testHead.setTestHeadName(newTestHead.getTestHeadName());

+            update.set("testHeadName", newTestHead.getTestHeadName());

+        }

+        if (newTestHead.getTestHeadDescription() != null) {

+            testHead.setTestHeadDescription(newTestHead.getTestHeadDescription());

+            update.set("testHeadDescription", newTestHead.getTestHeadDescription());

+        }

+        if (newTestHead.getHostname() != null) {

+            testHead.setHostname(newTestHead.getHostname());

+            update.set("hostname", newTestHead.getHostname());

+        }

+        if (newTestHead.getPort() != null) {

+            testHead.setPort(newTestHead.getPort());

+            update.set("port", newTestHead.getPort());

+        }

+        if (newTestHead.getResourcePath() != null) {

+            testHead.setResourcePath(newTestHead.getResourcePath());

+            update.set("resourcePath", newTestHead.getResourcePath());

+        }

+        if (newTestHead.getAuthorizationType() != null) {

+            testHead.setAuthorizationType(newTestHead.getAuthorizationType());

+            update.set("authorizationType", newTestHead.getAuthorizationType());

+        }

+        if (newTestHead.getAuthorizationCredential() != null) {

+            testHead.setAuthorizationCredential(newTestHead.getAuthorizationCredential());

+            update.set("authorizationCredential", newTestHead.getAuthorizationCredential());

+        }

+        if (newTestHead.getAuthorizationEnabled() != null) {

+            testHead.setAuthorizationEnabled(newTestHead.getAuthorizationEnabled());

+            update.set("authorizationEnabled", newTestHead.getAuthorizationEnabled());

+        }

+        if (newTestHead.getVthInputTemplate() != null) {

+            testHead.setVthInputTemplate(newTestHead.getVthInputTemplate());

+            update.set("vthInputTemplate", newTestHead.getVthInputTemplate());

+        }

+        testHead.setUpdatedAt(new Date());

+        update.set("updatedAt", testHead.getUpdatedAt());

+        testHead.setUpdatedBy(user.get_id());

+        update.set("updatedBy", user.get_id());

+

+        mongoTemplate.updateFirst(select, update, "testHeads");

+        return ResponseUtility.Build.okRequestWithObject(testHead);

+    }

+

+    // check if test head exists in database by name

+    private boolean doesTestHeadWithNameExist(String name) {

+        Optional<TestHead> optionalTestHead =

+                testHeadRepository.findByTestHeadName(name);

+        return optionalTestHead.isPresent();

+    }

+}

diff --git a/otf-service-api/src/main/java/org/oran/otf/common/model/Group.java b/otf-service-api/src/main/java/org/oran/otf/common/model/Group.java
new file mode 100644
index 0000000..9214407
--- /dev/null
+++ b/otf-service-api/src/main/java/org/oran/otf/common/model/Group.java
@@ -0,0 +1,110 @@
+/*  Copyright (c) 2019 AT&T Intellectual Property.                             #

+#                                                                              #

+#   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.oran.otf.common.model;

+

+import org.oran.otf.common.utility.gson.Convert;

+import java.io.Serializable;

+import java.util.List;

+

+import org.bson.types.ObjectId;

+import org.springframework.data.annotation.Id;

+import org.springframework.data.mongodb.core.mapping.Document;

+

+@Document(collection = "groups")

+public class Group implements Serializable {

+

+  private static final long serialVersionUID = 1L;

+

+  @Id

+  private ObjectId _id;

+  private String groupName;

+  private String groupDescription;

+  private List<ObjectId> mechanizedIds;

+  private ObjectId ownerId;

+  private List<Role> roles;

+  private List<GroupMember> members;

+  private ObjectId parentGroupId;

+

+  public ObjectId get_id() {

+    return _id;

+  }

+

+  public void set_id(ObjectId _id) {

+    this._id = _id;

+  }

+

+  public String getGroupName() {

+    return groupName;

+  }

+

+  public void setGroupName(String groupName) {

+    this.groupName = groupName;

+  }

+

+  public String getGroupDescription() {

+    return groupDescription;

+  }

+

+  public void setGroupDescription(String groupDescription) {

+    this.groupDescription = groupDescription;

+  }

+

+  public List<ObjectId> getMechanizedIds() {

+    return mechanizedIds;

+  }

+

+  public void setMechanizedIds(List<ObjectId> mechanizedIds) {

+    this.mechanizedIds = mechanizedIds;

+  }

+

+  public ObjectId getOwnerId() {

+    return ownerId;

+  }

+

+  public void setOwnerId(ObjectId ownerId) {

+    this.ownerId = ownerId;

+  }

+

+  public List<Role> getRoles() {

+    return roles;

+  }

+

+  public void setRoles(List<Role> roles) {

+    this.roles = roles;

+  }

+

+    public List<GroupMember> getMembers() {

+    return members;

+  }

+

+  public void setMembers(List<GroupMember> members) {

+    this.members = members;

+  }

+

+  public ObjectId getParentGroupId() {

+    return parentGroupId;

+  }

+

+  public void setParentGroupId(ObjectId parentGroupId) {

+    this.parentGroupId = parentGroupId;

+  }

+

+  @Override

+  public String toString() {

+    return Convert.objectToJson(this);

+  }

+}

diff --git a/otf-service-api/src/main/java/org/oran/otf/common/model/GroupMember.java b/otf-service-api/src/main/java/org/oran/otf/common/model/GroupMember.java
new file mode 100644
index 0000000..583c213
--- /dev/null
+++ b/otf-service-api/src/main/java/org/oran/otf/common/model/GroupMember.java
@@ -0,0 +1,43 @@
+/*  Copyright (c) 2019 AT&T Intellectual Property.                             #

+#                                                                              #

+#   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.oran.otf.common.model;

+

+import org.bson.types.ObjectId;

+

+import java.util.List;

+import java.util.Map;

+

+public class GroupMember {

+    private ObjectId userId;

+    private List<String> roles;//this is name of roles assigned to user that are created within the group i.e admin,dev,.. etc

+

+    public ObjectId getUserId() {

+        return userId;

+    }

+

+    public void setUserId(ObjectId userId) {

+        this.userId = userId;

+    }

+

+    public List<String> getRoles() {

+        return roles;

+    }

+

+    public void setRoles(List<String> roles) {

+        this.roles = roles;

+    }

+}

diff --git a/otf-service-api/src/main/java/org/oran/otf/common/model/Role.java b/otf-service-api/src/main/java/org/oran/otf/common/model/Role.java
new file mode 100644
index 0000000..aca09f1
--- /dev/null
+++ b/otf-service-api/src/main/java/org/oran/otf/common/model/Role.java
@@ -0,0 +1,41 @@
+/*  Copyright (c) 2019 AT&T Intellectual Property.                             #

+#                                                                              #

+#   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.oran.otf.common.model;

+

+import java.util.List;

+

+public class Role {

+

+    private String roleName;

+    private List<String> permissions;

+

+    public String getRoleName() {

+        return roleName;

+    }

+

+    public void setRoleName(String roleName) {

+        this.roleName = roleName;

+    }

+

+    public List<String> getPermissions() {

+        return permissions;

+    }

+

+    public void setPermissions(List<String> permissions) {

+        this.permissions = permissions;

+    }

+}

diff --git a/otf-service-api/src/main/java/org/oran/otf/common/model/TestDefinition.java b/otf-service-api/src/main/java/org/oran/otf/common/model/TestDefinition.java
new file mode 100644
index 0000000..2a66fa2
--- /dev/null
+++ b/otf-service-api/src/main/java/org/oran/otf/common/model/TestDefinition.java
@@ -0,0 +1,137 @@
+/*  Copyright (c) 2019 AT&T Intellectual Property.                             #

+#                                                                              #

+#   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.oran.otf.common.model;

+

+import org.oran.otf.common.model.local.BpmnInstance;

+import org.oran.otf.common.utility.gson.Convert;

+import java.io.Serializable;

+import java.util.Date;

+import java.util.List;

+import org.bson.types.ObjectId;

+import org.springframework.data.annotation.Id;

+import org.springframework.data.mongodb.core.mapping.Document;

+

+@Document(collection = "testDefinitions")

+public class TestDefinition implements Serializable {

+

+  private static final long serialVersionUID = 1L;

+

+  @Id private ObjectId _id;

+  private String testName;

+  private String testDescription;

+  private String processDefinitionKey;

+  private List<BpmnInstance> bpmnInstances;

+  private ObjectId groupId;

+  private Date createdAt;

+  private Date updatedAt;

+  private ObjectId createdBy;

+  private ObjectId updatedBy;

+  private boolean disabled;

+

+  public ObjectId get_id() {

+    return _id;

+  }

+

+  public void set_id(ObjectId _id) {

+    this._id = _id;

+  }

+

+  public String getTestName() {

+    return testName;

+  }

+

+  public void setTestName(String testName) {

+    this.testName = testName;

+  }

+

+  public String getTestDescription() {

+    return testDescription;

+  }

+

+  public void setTestDescription(String testDescription) {

+    this.testDescription = testDescription;

+  }

+

+  public String getProcessDefinitionKey() {

+    return processDefinitionKey;

+  }

+

+  public void setProcessDefinitionKey(String processDefinitionKey) {

+    this.processDefinitionKey = processDefinitionKey;

+  }

+

+  public List<BpmnInstance> getBpmnInstances() {

+    return bpmnInstances;

+  }

+

+  public void setBpmnInstances(List<BpmnInstance> bpmnInstances) {

+    this.bpmnInstances = bpmnInstances;

+  }

+

+  public ObjectId getGroupId() {

+    return groupId;

+  }

+

+  public void setGroupId(ObjectId groupId) {

+    this.groupId = groupId;

+  }

+

+  public Date getCreatedAt() {

+    return createdAt;

+  }

+

+  public void setCreatedAt(Date createdAt) {

+    this.createdAt = createdAt;

+  }

+

+  public Date getUpdatedAt() {

+    return updatedAt;

+  }

+

+  public void setUpdatedAt(Date updatedAt) {

+    this.updatedAt = updatedAt;

+  }

+

+  public ObjectId getCreatedBy() {

+    return createdBy;

+  }

+

+  public void setCreatedBy(ObjectId createdBy) {

+    this.createdBy = createdBy;

+  }

+

+  public ObjectId getUpdatedBy() {

+    return updatedBy;

+  }

+

+  public void setUpdatedBy(ObjectId updatedBy) {

+    this.updatedBy = updatedBy;

+  }

+

+  public boolean isDisabled() {

+    return disabled;

+  }

+

+  public void setDisabled(boolean disabled) {

+    this.disabled = disabled;

+  }

+

+  @Override

+  public String toString() {

+    return Convert.objectToJson(this);

+  }

+}

diff --git a/otf-service-api/src/main/java/org/oran/otf/common/model/TestExecution.java b/otf-service-api/src/main/java/org/oran/otf/common/model/TestExecution.java
new file mode 100644
index 0000000..8f02e0b
--- /dev/null
+++ b/otf-service-api/src/main/java/org/oran/otf/common/model/TestExecution.java
@@ -0,0 +1,245 @@
+/*  Copyright (c) 2019 AT&T Intellectual Property.                             #

+#                                                                              #

+#   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.oran.otf.common.model;

+

+import org.oran.otf.common.model.historic.TestDefinitionHistoric;

+import org.oran.otf.common.model.historic.TestInstanceHistoric;

+import org.oran.otf.common.model.local.TestHeadResult;

+import org.oran.otf.common.utility.gson.Convert;

+import java.io.Serializable;

+import java.util.Date;

+import java.util.List;

+import java.util.Map;

+import org.bson.types.ObjectId;

+import org.springframework.data.annotation.Id;

+import org.springframework.data.mongodb.core.mapping.Document;

+

+@Document(collection = "testExecutions")

+public class TestExecution implements Serializable {

+

+  private static final long serialVersionUID = 1L;

+

+  @Id

+  private ObjectId _id;

+  private ObjectId groupId;

+  private ObjectId executorId;

+

+  private boolean async;

+  private Date startTime;

+  private Date endTime;

+  private String asyncTopic;

+  private String businessKey;

+  private String processInstanceId;

+  private String testResult;

+  private String testResultMessage;

+  private Map<String, Object> testDetails;

+  private List<TestHeadResult> testHeadResults;

+  private List<TestExecution> testInstanceResults;

+  // Stores historic information of associated

+  private String historicEmail;

+  private TestInstanceHistoric historicTestInstance;

+  private TestDefinitionHistoric historicTestDefinition;

+

+  public TestExecution() {

+  }

+

+  public TestExecution(

+          ObjectId _id,

+          ObjectId groupId,

+          ObjectId executorId,

+          boolean async,

+          Date startTime,

+          Date endTime,

+          String asyncTopic,

+          String businessKey,

+          String processInstanceId,

+          String testResult,

+          String testResultMessage,

+          Map<String, Object> testDetails,

+          List<TestHeadResult> testHeadResults,

+          List<TestExecution> testInstanceResults,

+          String historicEmail,

+          TestInstanceHistoric historicTestInstance,

+          TestDefinitionHistoric historicTestDefinition) {

+    this._id = _id;

+    this.groupId = groupId;

+    this.executorId = executorId;

+    this.async = async;

+    this.startTime = startTime;

+    this.endTime = endTime;

+    this.asyncTopic = asyncTopic;

+    this.businessKey = businessKey;

+    this.processInstanceId = processInstanceId;

+    this.testResult = testResult;

+    this.testDetails = testDetails;

+    this.testHeadResults = testHeadResults;

+    this.testInstanceResults = testInstanceResults;

+    this.historicEmail = historicEmail;

+    this.historicTestInstance = historicTestInstance;

+    this.historicTestDefinition = historicTestDefinition;

+  }

+

+  public ObjectId get_id() {

+    return _id;

+  }

+

+  public void set_id(ObjectId _id) {

+    this._id = _id;

+  }

+

+  public ObjectId getGroupId() {

+    return groupId;

+  }

+

+  public void setGroupId(ObjectId groupId) {

+    this.groupId = groupId;

+  }

+

+  public ObjectId getExecutorId() {

+    return executorId;

+  }

+

+  public void setExecutorId(ObjectId executorId) {

+    this.executorId = executorId;

+  }

+

+  public boolean isAsync() {

+    return async;

+  }

+

+  public void setAsync(boolean async) {

+    this.async = async;

+  }

+

+  public Date getStartTime() {

+    return startTime;

+  }

+

+  public void setStartTime(Date startTime) {

+    this.startTime = startTime;

+  }

+

+  public Date getEndTime() {

+    return endTime;

+  }

+

+  public void setEndTime(Date endTime) {

+    this.endTime = endTime;

+  }

+

+  public String getAsyncTopic() {

+    return asyncTopic;

+  }

+

+  public void setAsyncTopic(String asyncTopic) {

+    this.asyncTopic = asyncTopic;

+  }

+

+  public String getBusinessKey() {

+    return businessKey;

+  }

+

+  public void setBusinessKey(String businessKey) {

+    this.businessKey = businessKey;

+  }

+

+  public String getProcessInstanceId() {

+    return processInstanceId;

+  }

+

+  public void setProcessInstanceId(String processInstanceId) {

+    this.processInstanceId = processInstanceId;

+  }

+

+  public String getTestResult() {

+    return testResult;

+  }

+

+  public void setTestResult(String testResult) {

+    this.testResult = testResult;

+  }

+

+  public String getTestResultMessage() {

+    return testResultMessage;

+  }

+

+  public void setTestResultMessage(String testResultMessage) {

+    this.testResultMessage = testResultMessage;

+  }

+

+  public Map<String, Object> getTestDetails() {

+    return testDetails;

+  }

+

+  public void setTestDetails(Map<String, Object> testDetails) {

+    this.testDetails = testDetails;

+  }

+

+  public List<TestHeadResult> getTestHeadResults() {

+    synchronized (testHeadResults) {

+      return testHeadResults;

+    }

+  }

+

+  public void setTestHeadResults(List<TestHeadResult> testHeadResults) {

+    synchronized (testHeadResults) {

+      this.testHeadResults = testHeadResults;

+    }

+  }

+

+  public List<TestExecution> getTestInstanceResults() {

+    synchronized (testInstanceResults) {

+      return testInstanceResults;

+    }

+  }

+

+  public void setTestInstanceResults(List<TestExecution> testInstanceResults) {

+    synchronized (testInstanceResults) {

+      this.testInstanceResults = testInstanceResults;

+    }

+  }

+

+  public String getHistoricEmail() {

+    return historicEmail;

+  }

+

+  public void setHistoricEmail(String historicEmail) {

+    this.historicEmail = historicEmail;

+  }

+

+  public TestInstanceHistoric getHistoricTestInstance() {

+    return historicTestInstance;

+  }

+

+  public void setHistoricTestInstance(TestInstanceHistoric historicTestInstance) {

+    this.historicTestInstance = historicTestInstance;

+  }

+

+  public TestDefinitionHistoric getHistoricTestDefinition() {

+    return historicTestDefinition;

+  }

+

+  public void setHistoricTestDefinition(

+          TestDefinitionHistoric historicTestDefinition) {

+    this.historicTestDefinition = historicTestDefinition;

+  }

+

+  @Override

+  public String toString() {

+    return Convert.objectToJson(this);

+  }

+}

diff --git a/otf-service-api/src/main/java/org/oran/otf/common/model/TestHead.java b/otf-service-api/src/main/java/org/oran/otf/common/model/TestHead.java
new file mode 100644
index 0000000..7f4bcbc
--- /dev/null
+++ b/otf-service-api/src/main/java/org/oran/otf/common/model/TestHead.java
@@ -0,0 +1,224 @@
+/*  Copyright (c) 2019 AT&T Intellectual Property.                             #

+#                                                                              #

+#   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.oran.otf.common.model;

+

+import org.oran.otf.common.utility.gson.Convert;

+import java.io.Serializable;

+import java.util.Date;

+import java.util.Map;

+

+import org.bson.types.ObjectId;

+import org.springframework.data.annotation.Id;

+import org.springframework.data.mongodb.core.index.Indexed;

+import org.springframework.data.mongodb.core.mapping.Document;

+

+@Document(collection = "testHeads")

+public class TestHead implements Serializable {

+

+  private static final long serialVersionUID = 1L;

+

+  @Id

+  private ObjectId _id;

+

+  @Indexed(unique = true)

+  private String testHeadName;

+

+  private String testHeadDescription;

+  private String hostname;

+  private String port;

+  private String resourcePath;

+  private ObjectId creatorId;

+  private ObjectId groupId;

+  private String authorizationType;

+  private String authorizationCredential;

+  private Boolean authorizationEnabled;

+  private Map<String, Object> vthInputTemplate;

+  private Date createdAt;

+  private Date updatedAt;

+  private ObjectId updatedBy;

+  private Boolean isPublic;

+  public TestHead() {

+  }

+

+  public TestHead(

+          ObjectId _id,

+          String testHeadName,

+          String testHeadDescription,

+          String hostname,

+          String port,

+          String resourcePath,

+          ObjectId creatorId,

+          ObjectId groupId,

+          String authorizationType,

+          String authorizationCredential,

+          boolean authorizationEnabled,

+          Map<String, Object> vthInputTemplate,

+          Date createdAt,

+          Date updatedAt,

+          ObjectId updatedBy,

+          Boolean isPublic) {

+    this._id = _id;

+    this.testHeadName = testHeadName;

+    this.testHeadDescription = testHeadDescription;

+    this.hostname = hostname;

+    this.port = port;

+    this.resourcePath = resourcePath;

+    this.creatorId = creatorId;

+    this.groupId = groupId;

+    this.authorizationType = authorizationType;

+    this.authorizationCredential = authorizationCredential;

+    this.authorizationEnabled = authorizationEnabled;

+    this.vthInputTemplate = vthInputTemplate;

+    this.createdAt = createdAt;

+    this.updatedAt = updatedAt;

+    this.updatedBy = updatedBy;

+    this.isPublic = isPublic;

+  }

+

+  public ObjectId get_id() {

+    return _id;

+  }

+

+  public void set_id(ObjectId _id) {

+    this._id = _id;

+  }

+

+  public String getTestHeadName() {

+    return testHeadName;

+  }

+

+  public void setTestHeadName(String testHeadName) {

+    this.testHeadName = testHeadName;

+  }

+

+  public String getTestHeadDescription() {

+    return testHeadDescription;

+  }

+

+  public void setTestHeadDescription(String testHeadDescription) {

+    this.testHeadDescription = testHeadDescription;

+  }

+

+  public String getHostname() {

+    return hostname;

+  }

+

+  public void setHostname(String hostname) {

+    this.hostname = hostname;

+  }

+

+  public String getPort() {

+    return port;

+  }

+

+  public void setPort(String port) {

+    this.port = port;

+  }

+

+  public String getResourcePath() {

+    return resourcePath;

+  }

+

+  public void setResourcePath(String resourcePath) {

+    this.resourcePath = resourcePath;

+  }

+

+  public ObjectId getCreatorId() {

+    return creatorId;

+  }

+

+  public void setCreatorId(ObjectId creatorId) {

+    this.creatorId = creatorId;

+  }

+

+  public ObjectId getGroupId() {

+    return groupId;

+  }

+

+  public void setGroupId(ObjectId groupId) {

+    this.groupId = groupId;

+  }

+

+  public String getAuthorizationCredential() {

+    return authorizationCredential;

+  }

+

+  public String getAuthorizationType() {

+    return authorizationType;

+  }

+

+  public void setAuthorizationType(String authorizationType) {

+    this.authorizationType = authorizationType;

+  }

+

+  public void setAuthorizationCredential(String authorizationCredential) {

+    this.authorizationCredential = authorizationCredential;

+  }

+

+  public Boolean getAuthorizationEnabled() {

+    return authorizationEnabled;

+  }

+

+  public void setAuthorizationEnabled(Boolean authorizationEnabled) {

+    this.authorizationEnabled = authorizationEnabled;

+  }

+

+  public Map<String, Object> getVthInputTemplate() {

+    return vthInputTemplate;

+  }

+

+  public void setVthInputTemplate(Map<String, Object> vthInputTemplate) {

+    this.vthInputTemplate = vthInputTemplate;

+  }

+

+  public Date getCreatedAt() {

+    return createdAt;

+  }

+

+  public void setCreatedAt(Date createdAt) {

+    this.createdAt = createdAt;

+  }

+

+  public Date getUpdatedAt() {

+    return updatedAt;

+  }

+

+  public void setUpdatedAt(Date updatedAt) {

+    this.updatedAt = updatedAt;

+  }

+

+  public ObjectId getUpdatedBy() {

+    return updatedBy;

+  }

+

+  public void setUpdatedBy(ObjectId updatedBy) {

+    this.updatedBy = updatedBy;

+  }

+

+  public Boolean isPublic() {

+    return isPublic;

+  }

+

+  public void setPublic(Boolean aPublic) {

+    isPublic = aPublic;

+  }

+

+  @Override

+  public String toString() {

+    return Convert.objectToJson(this);

+  }

+}

diff --git a/otf-service-api/src/main/java/org/oran/otf/common/model/TestInstance.java b/otf-service-api/src/main/java/org/oran/otf/common/model/TestInstance.java
new file mode 100644
index 0000000..9b11fa4
--- /dev/null
+++ b/otf-service-api/src/main/java/org/oran/otf/common/model/TestInstance.java
@@ -0,0 +1,256 @@
+/*  Copyright (c) 2019 AT&T Intellectual Property.                             #

+#                                                                              #

+#   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.oran.otf.common.model;

+

+import org.oran.otf.common.model.local.ParallelFlowInput;

+import org.oran.otf.common.utility.gson.Convert;

+import java.io.Serializable;

+import java.util.Date;

+import java.util.HashMap;

+import org.bson.types.ObjectId;

+import org.springframework.data.annotation.Id;

+import org.springframework.data.mongodb.core.mapping.Document;

+

+@Document(collection = "testInstances")

+public class TestInstance implements Serializable {

+

+  private static final long serialVersionUID = 1L;

+

+  private @Id ObjectId _id;

+  private String testInstanceName;

+  private String testInstanceDescription;

+  private ObjectId groupId;

+  private ObjectId testDefinitionId;

+  private String processDefinitionId;

+  private boolean useLatestTestDefinition;

+  private boolean disabled;

+  private boolean simulationMode;

+  private long maxExecutionTimeInMillis;

+  private HashMap<String, ParallelFlowInput> pfloInput;

+  private HashMap<String, Object> internalTestData;

+  private HashMap<String, Object> simulationVthInput;

+  private HashMap<String, Object> testData;

+  private HashMap<String, Object> vthInput;

+  private Date createdAt;

+  private Date updatedAt;

+  private ObjectId createdBy;

+  private ObjectId updatedBy;

+

+  public TestInstance() {}

+

+  public TestInstance(

+      ObjectId _id,

+      String testInstanceName,

+      String testInstanceDescription,

+      ObjectId groupId,

+      ObjectId testDefinitionId,

+      String processDefinitionId,

+      boolean useLatestTestDefinition,

+      boolean disabled,

+      boolean simulationMode,

+      long maxExecutionTimeInMillis,

+      HashMap<String, ParallelFlowInput> pfloInput,

+      HashMap<String, Object> internalTestData,

+      HashMap<String, Object> simulationVthInput,

+      HashMap<String, Object> testData,

+      HashMap<String, Object> vthInput,

+      Date createdAt,

+      Date updatedAt,

+      ObjectId createdBy,

+      ObjectId updatedBy) {

+    this._id = _id;

+    this.testInstanceName = testInstanceName;

+    this.testInstanceDescription = testInstanceDescription;

+    this.groupId = groupId;

+    this.testDefinitionId = testDefinitionId;

+    this.processDefinitionId = processDefinitionId;

+    this.useLatestTestDefinition = useLatestTestDefinition;

+    this.disabled = disabled;

+    this.simulationMode = simulationMode;

+    this.maxExecutionTimeInMillis = maxExecutionTimeInMillis;

+    this.pfloInput = pfloInput;

+    this.internalTestData = internalTestData;

+    this.simulationVthInput = simulationVthInput;

+    this.testData = testData;

+    this.vthInput = vthInput;

+    this.createdAt = createdAt;

+    this.updatedAt = updatedAt;

+    this.createdBy = createdBy;

+    this.updatedBy = updatedBy;

+  }

+

+  public static long getSerialVersionUID() {

+    return serialVersionUID;

+  }

+

+  public ObjectId get_id() {

+    return _id;

+  }

+

+  public void set_id(ObjectId _id) {

+    this._id = _id;

+  }

+

+  public String getTestInstanceName() {

+    return testInstanceName;

+  }

+

+  public void setTestInstanceName(String testInstanceName) {

+    this.testInstanceName = testInstanceName;

+  }

+

+  public String getTestInstanceDescription() {

+    return testInstanceDescription;

+  }

+

+  public void setTestInstanceDescription(String testInstanceDescription) {

+    this.testInstanceDescription = testInstanceDescription;

+  }

+

+  public ObjectId getGroupId() {

+    return groupId;

+  }

+

+  public void setGroupId(ObjectId groupId) {

+    this.groupId = groupId;

+  }

+

+  public ObjectId getTestDefinitionId() {

+    return testDefinitionId;

+  }

+

+  public void setTestDefinitionId(ObjectId testDefinitionId) {

+    this.testDefinitionId = testDefinitionId;

+  }

+

+  public String getProcessDefinitionId() {

+    return processDefinitionId;

+  }

+

+  public void setProcessDefinitionId(String processDefinitionId) {

+    this.processDefinitionId = processDefinitionId;

+  }

+

+  public boolean isUseLatestTestDefinition() {

+    return useLatestTestDefinition;

+  }

+

+  public void setUseLatestTestDefinition(boolean useLatestTestDefinition) {

+    this.useLatestTestDefinition = useLatestTestDefinition;

+  }

+

+  public boolean isDisabled() {

+    return disabled;

+  }

+

+  public void setDisabled(boolean disabled) {

+    this.disabled = disabled;

+  }

+

+  public boolean isSimulationMode() {

+    return simulationMode;

+  }

+

+  public void setSimulationMode(boolean simulationMode) {

+    this.simulationMode = simulationMode;

+  }

+

+  public long getMaxExecutionTimeInMillis() {

+    return maxExecutionTimeInMillis;

+  }

+

+  public void setMaxExecutionTimeInMillis(long maxExecutionTimeInMillis) {

+    this.maxExecutionTimeInMillis = maxExecutionTimeInMillis;

+  }

+

+  public HashMap<String, ParallelFlowInput> getPfloInput() {

+    return pfloInput;

+  }

+

+  public void setPfloInput(HashMap<String, ParallelFlowInput> pfloInput) {

+    this.pfloInput = pfloInput;

+  }

+

+  public HashMap<String, Object> getInternalTestData() {

+    return internalTestData;

+  }

+

+  public void setInternalTestData(HashMap<String, Object> internalTestData) {

+    this.internalTestData = internalTestData;

+  }

+

+  public HashMap<String, Object> getSimulationVthInput() {

+    return simulationVthInput;

+  }

+

+  public void setSimulationVthInput(HashMap<String, Object> simulationVthInput) {

+    this.simulationVthInput = simulationVthInput;

+  }

+

+  public HashMap<String, Object> getTestData() {

+    return testData;

+  }

+

+  public void setTestData(HashMap<String, Object> testData) {

+    this.testData = testData;

+  }

+

+  public HashMap<String, Object> getVthInput() {

+    return vthInput;

+  }

+

+  public void setVthInput(HashMap<String, Object> vthInput) {

+    this.vthInput = vthInput;

+  }

+

+  public Date getCreatedAt() {

+    return createdAt;

+  }

+

+  public void setCreatedAt(Date createdAt) {

+    this.createdAt = createdAt;

+  }

+

+  public Date getUpdatedAt() {

+    return updatedAt;

+  }

+

+  public void setUpdatedAt(Date updatedAt) {

+    this.updatedAt = updatedAt;

+  }

+

+  public ObjectId getCreatedBy() {

+    return createdBy;

+  }

+

+  public void setCreatedBy(ObjectId createdBy) {

+    this.createdBy = createdBy;

+  }

+

+  public ObjectId getUpdatedBy() {

+    return updatedBy;

+  }

+

+  public void setUpdatedBy(ObjectId updatedBy) {

+    this.updatedBy = updatedBy;

+  }

+

+  @Override

+  public String toString() {

+    return Convert.objectToJson(this);

+  }

+}

diff --git a/otf-service-api/src/main/java/org/oran/otf/common/model/User.java b/otf-service-api/src/main/java/org/oran/otf/common/model/User.java
new file mode 100644
index 0000000..2c56b85
--- /dev/null
+++ b/otf-service-api/src/main/java/org/oran/otf/common/model/User.java
@@ -0,0 +1,142 @@
+/*  Copyright (c) 2019 AT&T Intellectual Property.                             #

+#                                                                              #

+#   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.oran.otf.common.model;

+

+import org.oran.otf.common.model.local.UserGroup;

+import org.oran.otf.common.utility.gson.Convert;

+import java.io.Serializable;

+import java.util.Date;

+import java.util.List;

+import org.bson.types.ObjectId;

+import org.springframework.data.mongodb.core.mapping.Document;

+

+@Document(collection = "users")

+public class User implements Serializable {

+

+  private static final long serialVersionUID = 1L;

+

+  private ObjectId _id;

+  private List<String> permissions;

+  private String firstName;

+  private String lastName;

+  private String email;

+  private String password;

+  private List<UserGroup> groups;

+  private Date createdAt;

+  private Date updatedAt;

+

+  //Added User for testing

+  public User(){};

+

+  public User(

+      ObjectId _id,

+      List<String> permissions,

+      String firstName,

+      String lastName,

+      String email,

+      String password,

+      List<UserGroup> groups,

+      Date createdAt,

+      Date updatedAt) {

+    this._id = _id;

+    this.permissions = permissions;

+    this.firstName = firstName;

+    this.lastName = lastName;

+    this.email = email;

+    this.password = password;

+    this.groups = groups;

+    this.createdAt = createdAt;

+    this.updatedAt = updatedAt;

+  }

+

+  public ObjectId get_id() {

+    return _id;

+  }

+

+  public void set_id(ObjectId _id) {

+    this._id = _id;

+  }

+

+  public List<String> getPermissions() {

+    return permissions;

+  }

+

+  public void setPermissions(List<String> permissions) {

+    this.permissions = permissions;

+  }

+

+  public String getFirstName() {

+    return firstName;

+  }

+

+  public void setFirstName(String firstName) {

+    this.firstName = firstName;

+  }

+

+  public String getLastName() {

+    return lastName;

+  }

+

+  public void setLastName(String lastName) {

+    this.lastName = lastName;

+  }

+

+  public String getEmail() {

+    return email;

+  }

+

+  public void setEmail(String email) {

+    this.email = email;

+  }

+

+  public String getPassword() {

+    return password;

+  }

+

+  public void setPassword(String password) {

+    this.password = password;

+  }

+

+  public List<UserGroup> getGroups() {

+    return groups;

+  }

+

+  public void setGroups(List<UserGroup> groups) {

+    this.groups = groups;

+  }

+

+  public Date getCreatedAt() {

+    return createdAt;

+  }

+

+  public void setCreatedAt(Date createdAt) {

+    this.createdAt = createdAt;

+  }

+

+  public Date getUpdatedAt() {

+    return updatedAt;

+  }

+

+  public void setUpdatedAt(Date updatedAt) {

+    this.updatedAt = updatedAt;

+  }

+

+  @Override

+  public String toString() {

+    return Convert.objectToJson(this);

+  }

+}

diff --git a/otf-service-api/src/main/java/org/oran/otf/common/model/historic/TestDefinitionHistoric.java b/otf-service-api/src/main/java/org/oran/otf/common/model/historic/TestDefinitionHistoric.java
new file mode 100644
index 0000000..fe2be4b
--- /dev/null
+++ b/otf-service-api/src/main/java/org/oran/otf/common/model/historic/TestDefinitionHistoric.java
@@ -0,0 +1,185 @@
+/*  Copyright (c) 2019 AT&T Intellectual Property.                             #

+#                                                                              #

+#   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.oran.otf.common.model.historic;

+

+import org.oran.otf.common.model.TestDefinition;

+import org.oran.otf.common.model.local.BpmnInstance;

+import org.oran.otf.common.utility.gson.Convert;

+import java.io.Serializable;

+import java.util.ArrayList;

+import java.util.Date;

+import java.util.List;

+import org.bson.types.ObjectId;

+

+public class TestDefinitionHistoric implements Serializable {

+

+  private static final long serialVersionUID = 1L;

+

+  private ObjectId _id;

+  private String testName;

+  private String testDescription;

+  private String processDefinitionKey;

+  private List<BpmnInstance> bpmnInstances;

+  private ObjectId groupId;

+  private Date createdAt;

+  private Date updatedAt;

+  private ObjectId createdBy;

+  private ObjectId updatedBy;

+

+  public TestDefinitionHistoric() {

+  }

+

+  public TestDefinitionHistoric(TestDefinition testDefinition, String processDefinitionId) {

+    this._id = testDefinition.get_id();

+    this.testName = testDefinition.getTestName();

+    this.testDescription = testDefinition.getTestDescription();

+    this.processDefinitionKey = testDefinition.getProcessDefinitionKey();

+    this.bpmnInstances =

+        getHistoricBpmnInstanceAsList(testDefinition.getBpmnInstances(), processDefinitionId);

+    this.groupId = testDefinition.getGroupId();

+    this.createdAt = testDefinition.getCreatedAt();

+    this.updatedAt = testDefinition.getUpdatedAt();

+    this.createdBy = testDefinition.getCreatedBy();

+    this.updatedBy = testDefinition.getUpdatedBy();

+  }

+

+  public TestDefinitionHistoric(

+      ObjectId _id,

+      String testName,

+      String testDescription,

+      String processDefinitionKey,

+      List<BpmnInstance> bpmnInstances,

+      ObjectId groupId,

+      Date createdAt,

+      Date updatedAt,

+      ObjectId createdBy,

+      ObjectId updatedBy) {

+    this._id = _id;

+    this.testName = testName;

+    this.testDescription = testDescription;

+    this.processDefinitionKey = processDefinitionKey;

+    this.bpmnInstances = bpmnInstances;

+    this.groupId = groupId;

+    this.createdAt = createdAt;

+    this.updatedAt = updatedAt;

+    this.createdBy = createdBy;

+    this.updatedBy = updatedBy;

+  }

+

+  private List<BpmnInstance> getHistoricBpmnInstanceAsList(

+      List<BpmnInstance> bpmnInstances, String processDefinitionId) {

+    BpmnInstance bpmnInstance =

+        bpmnInstances.stream()

+            .filter(

+                _bpmnInstance ->

+                    _bpmnInstance.getProcessDefinitionId().equalsIgnoreCase(processDefinitionId))

+            .findFirst()

+            .orElse(null);

+

+    List<BpmnInstance> historicBpmnInstance = new ArrayList<>();

+    if (bpmnInstance != null) {

+      historicBpmnInstance.add(bpmnInstance);

+    }

+

+    return historicBpmnInstance;

+  }

+

+  public ObjectId get_id() {

+    return _id;

+  }

+

+  public void set_id(ObjectId _id) {

+    this._id = _id;

+  }

+

+  public String getTestName() {

+    return testName;

+  }

+

+  public void setTestName(String testName) {

+    this.testName = testName;

+  }

+

+  public String getTestDescription() {

+    return testDescription;

+  }

+

+  public void setTestDescription(String testDescription) {

+    this.testDescription = testDescription;

+  }

+

+  public String getProcessDefinitionKey() {

+    return processDefinitionKey;

+  }

+

+  public void setProcessDefinitionKey(String processDefinitionKey) {

+    this.processDefinitionKey = processDefinitionKey;

+  }

+

+  public List<BpmnInstance> getBpmnInstances() {

+    return bpmnInstances;

+  }

+

+  public void setBpmnInstances(List<BpmnInstance> bpmnInstances) {

+    this.bpmnInstances = bpmnInstances;

+  }

+

+  public ObjectId getGroupId() {

+    return groupId;

+  }

+

+  public void setGroupId(ObjectId groupId) {

+    this.groupId = groupId;

+  }

+

+  public Date getCreatedAt() {

+    return createdAt;

+  }

+

+  public void setCreatedAt(Date createdAt) {

+    this.createdAt = createdAt;

+  }

+

+  public Date getUpdatedAt() {

+    return updatedAt;

+  }

+

+  public void setUpdatedAt(Date updatedAt) {

+    this.updatedAt = updatedAt;

+  }

+

+  public ObjectId getCreatedBy() {

+    return createdBy;

+  }

+

+  public void setCreatedBy(ObjectId createdBy) {

+    this.createdBy = createdBy;

+  }

+

+  public ObjectId getUpdatedBy() {

+    return updatedBy;

+  }

+

+  public void setUpdatedBy(ObjectId updatedBy) {

+    this.updatedBy = updatedBy;

+  }

+

+  @Override

+  public String toString() {

+    return Convert.objectToJson(this);

+  }

+}

diff --git a/otf-service-api/src/main/java/org/oran/otf/common/model/historic/TestInstanceHistoric.java b/otf-service-api/src/main/java/org/oran/otf/common/model/historic/TestInstanceHistoric.java
new file mode 100644
index 0000000..1263893
--- /dev/null
+++ b/otf-service-api/src/main/java/org/oran/otf/common/model/historic/TestInstanceHistoric.java
@@ -0,0 +1,234 @@
+/*  Copyright (c) 2019 AT&T Intellectual Property.                             #

+#                                                                              #

+#   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.oran.otf.common.model.historic;

+

+import org.oran.otf.common.model.TestInstance;

+import org.oran.otf.common.model.local.ParallelFlowInput;

+import org.oran.otf.common.utility.gson.Convert;

+import java.io.Serializable;

+import java.util.Date;

+import java.util.HashMap;

+import java.util.Map;

+import org.bson.types.ObjectId;

+import org.springframework.data.annotation.Id;

+

+public class TestInstanceHistoric implements Serializable {

+

+  private static final long serialVersionUID = 1L;

+

+  private @Id

+  ObjectId _id;

+  private String testInstanceName;

+  private String testInstanceDescription;

+  private ObjectId groupId;

+  private ObjectId testDefinitionId;

+  private String processDefinitionId;

+  private Map<String, ParallelFlowInput> pfloInput;

+  private Map<String, Object> simulationVthInput;

+  private Map<String, Object> testData;

+  private Map<String, Object> vthInput;

+  private Date createdAt;

+  private Date updatedAt;

+  private ObjectId createdBy;

+  private ObjectId updatedBy;

+  private boolean simulationMode;

+

+  public TestInstanceHistoric() {

+  }

+

+  public TestInstanceHistoric(TestInstance testInstance) {

+    this._id = testInstance.get_id();

+    this.testInstanceName = testInstance.getTestInstanceName();

+    this.testInstanceDescription = testInstance.getTestInstanceDescription();

+    this.groupId = testInstance.getGroupId();

+    this.testDefinitionId = testInstance.getTestDefinitionId();

+    this.pfloInput = testInstance.getPfloInput();

+    this.processDefinitionId = testInstance.getProcessDefinitionId();

+    this.simulationVthInput = testInstance.getSimulationVthInput();

+    this.testData = testInstance.getTestData();

+    this.vthInput = testInstance.getVthInput();

+    this.createdAt = testInstance.getCreatedAt();

+    this.updatedAt = testInstance.getUpdatedAt();

+    this.createdBy = testInstance.getCreatedBy();

+    this.updatedBy = testInstance.getUpdatedBy();

+    this.simulationMode = testInstance.isSimulationMode();

+  }

+

+  public TestInstanceHistoric(

+      ObjectId _id,

+      String testInstanceName,

+      String testInstanceDescription,

+      ObjectId groupId,

+      ObjectId testDefinitionId,

+      String processDefinitionId,

+      HashMap<String, ParallelFlowInput> pfloInput,

+      HashMap<String, Object> simulationVthInput,

+      HashMap<String, Object> testData,

+      HashMap<String, Object> vthInput,

+      Date createdAt,

+      Date updatedAt,

+      ObjectId createdBy,

+      ObjectId updatedBy,

+      boolean simulationMode) {

+    this._id = _id;

+    this.testInstanceName = testInstanceName;

+    this.testInstanceDescription = testInstanceDescription;

+    this.groupId = groupId;

+    this.testDefinitionId = testDefinitionId;

+    this.processDefinitionId = processDefinitionId;

+    this.pfloInput = pfloInput;

+    this.simulationVthInput = simulationVthInput;

+    this.testData = testData;

+    this.vthInput = vthInput;

+    this.createdAt = createdAt;

+    this.updatedAt = updatedAt;

+    this.createdBy = createdBy;

+    this.updatedBy = updatedBy;

+    this.simulationMode = simulationMode;

+  }

+

+  public static long getSerialVersionUID() {

+    return serialVersionUID;

+  }

+

+  public ObjectId get_id() {

+    return _id;

+  }

+

+  public void set_id(ObjectId _id) {

+    this._id = _id;

+  }

+

+  public String getTestInstanceName() {

+    return testInstanceName;

+  }

+

+  public void setTestInstanceName(String testInstanceName) {

+    this.testInstanceName = testInstanceName;

+  }

+

+  public String getTestInstanceDescription() {

+    return testInstanceDescription;

+  }

+

+  public void setTestInstanceDescription(String testInstanceDescription) {

+    this.testInstanceDescription = testInstanceDescription;

+  }

+

+  public ObjectId getGroupId() {

+    return groupId;

+  }

+

+  public void setGroupId(ObjectId groupId) {

+    this.groupId = groupId;

+  }

+

+  public ObjectId getTestDefinitionId() {

+    return testDefinitionId;

+  }

+

+  public void setTestDefinitionId(ObjectId testDefinitionId) {

+    this.testDefinitionId = testDefinitionId;

+  }

+

+  public String getProcessDefinitionId() {

+    return processDefinitionId;

+  }

+

+  public void setProcessDefinitionId(String processDefinitionId) {

+    this.processDefinitionId = processDefinitionId;

+  }

+

+  public Map<String, ParallelFlowInput> getPfloInput() {

+    return pfloInput;

+  }

+

+  public void setPfloInput(

+      HashMap<String, ParallelFlowInput> pfloInput) {

+    this.pfloInput = pfloInput;

+  }

+

+  public Map<String, Object> getSimulationVthInput() {

+    return simulationVthInput;

+  }

+

+  public void setSimulationVthInput(

+      HashMap<String, Object> simulationVthInput) {

+    this.simulationVthInput = simulationVthInput;

+  }

+

+  public Map<String, Object> getTestData() {

+    return testData;

+  }

+

+  public void setTestData(HashMap<String, Object> testData) {

+    this.testData = testData;

+  }

+

+  public Map<String, Object> getVthInput() {

+    return vthInput;

+  }

+

+  public void setVthInput(HashMap<String, Object> vthInput) {

+    this.vthInput = vthInput;

+  }

+

+  public Date getCreatedAt() {

+    return createdAt;

+  }

+

+  public void setCreatedAt(Date createdAt) {

+    this.createdAt = createdAt;

+  }

+

+  public Date getUpdatedAt() {

+    return updatedAt;

+  }

+

+  public void setUpdatedAt(Date updatedAt) {

+    this.updatedAt = updatedAt;

+  }

+

+  public ObjectId getCreatedBy() {

+    return createdBy;

+  }

+

+  public void setCreatedBy(ObjectId createdBy) {

+    this.createdBy = createdBy;

+  }

+

+  public ObjectId getUpdatedBy() {

+    return updatedBy;

+  }

+

+  public void setUpdatedBy(ObjectId updatedBy) {

+    this.updatedBy = updatedBy;

+  }

+

+  public boolean isSimulationMode() {

+    return simulationMode;

+  }

+

+  public void setSimulationMode(boolean simulationMode) {

+    this.simulationMode = simulationMode;

+  }

+

+  @Override

+  public String toString() {

+    return Convert.objectToJson(this);

+  }

+}

diff --git a/otf-service-api/src/main/java/org/oran/otf/common/model/local/ApiRequest.java b/otf-service-api/src/main/java/org/oran/otf/common/model/local/ApiRequest.java
new file mode 100644
index 0000000..05ec6a9
--- /dev/null
+++ b/otf-service-api/src/main/java/org/oran/otf/common/model/local/ApiRequest.java
@@ -0,0 +1,19 @@
+/*  Copyright (c) 2019 AT&T Intellectual Property.                             #

+#                                                                              #

+#   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.oran.otf.common.model.local;

+

+public class ApiRequest {}

diff --git a/otf-service-api/src/main/java/org/oran/otf/common/model/local/BpmnInstance.java b/otf-service-api/src/main/java/org/oran/otf/common/model/local/BpmnInstance.java
new file mode 100644
index 0000000..c4440b0
--- /dev/null
+++ b/otf-service-api/src/main/java/org/oran/otf/common/model/local/BpmnInstance.java
@@ -0,0 +1,191 @@
+/*  Copyright (c) 2019 AT&T Intellectual Property.                             #

+#                                                                              #

+#   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.oran.otf.common.model.local;

+

+import org.oran.otf.common.utility.gson.Convert;

+import com.fasterxml.jackson.annotation.JsonCreator;

+import com.fasterxml.jackson.annotation.JsonProperty;

+import java.io.Serializable;

+import java.util.Date;

+import java.util.List;

+import java.util.Map;

+import org.bson.types.ObjectId;

+

+public class BpmnInstance implements Serializable {

+

+  private static final long serialVersionUID = 1L;

+

+  private String processDefinitionId;

+  private String deploymentId;

+  private int version;

+  private ObjectId bpmnFileId;

+  private ObjectId resourceFileId;

+  private boolean isDeployed;

+  private List<TestHeadNode> testHeads;

+  private List<PfloNode> pflos;

+  private Map<String, Object> testDataTemplate;

+  private Date createdAt;

+  private Date updatedAt;

+  private ObjectId createdBy;

+  private ObjectId updatedBy;

+

+  public BpmnInstance() {

+  }

+

+  @JsonCreator

+  public BpmnInstance(

+      @JsonProperty("processDefinitionId") String processDefinitionId,

+      @JsonProperty("deploymentId") String deploymentId,

+      @JsonProperty("version") int version,

+      @JsonProperty("bpmnFileId") ObjectId bpmnFileId,

+      @JsonProperty("resourceFileId") ObjectId resourceFileId,

+      @JsonProperty("isDeployed") boolean isDeployed,

+      @JsonProperty("testHeads") List<TestHeadNode> testHeads,

+      @JsonProperty("plfos") List<PfloNode> pflos,

+      @JsonProperty("testDataTemplate") Map<String, Object> testDataTemplate,

+      @JsonProperty("createdAt") Date createdAt,

+      @JsonProperty("updateAt") Date updatedAt,

+      @JsonProperty("createdBy") ObjectId createdBy,

+      @JsonProperty("updatedBy") ObjectId updatedBy) {

+    this.processDefinitionId = processDefinitionId;

+    this.deploymentId = deploymentId;

+    this.version = version;

+    this.bpmnFileId = bpmnFileId;

+    this.resourceFileId = resourceFileId;

+    this.isDeployed = isDeployed;

+    this.testHeads = testHeads;

+    this.testDataTemplate = testDataTemplate;

+    this.createdAt = createdAt;

+    this.updatedAt = updatedAt;

+    this.createdBy = createdBy;

+    this.updatedBy = updatedBy;

+  }

+

+  public String getProcessDefinitionId() {

+    return processDefinitionId;

+  }

+

+  public void setProcessDefinitionId(String processDefinitionId) {

+    this.processDefinitionId = processDefinitionId;

+  }

+

+  public String getDeploymentId() {

+    return deploymentId;

+  }

+

+  public void setDeploymentId(String deploymentId) {

+    this.deploymentId = deploymentId;

+  }

+

+  public int getVersion() {

+    return version;

+  }

+

+  public void setVersion(int version) {

+    this.version = version;

+  }

+

+  public ObjectId getBpmnFileId() {

+    return bpmnFileId;

+  }

+

+  public void setBpmnFileId(ObjectId bpmnFileId) {

+    this.bpmnFileId = bpmnFileId;

+  }

+

+  public ObjectId getResourceFileId() {

+    return resourceFileId;

+  }

+

+  public void setResourceFileId(ObjectId resourceFileId) {

+    this.resourceFileId = resourceFileId;

+  }

+

+  @JsonProperty(value="isDeployed")

+  public boolean isDeployed() {

+    return isDeployed;

+  }

+

+  public void setDeployed(boolean deployed) {

+    isDeployed = deployed;

+  }

+

+  public List<TestHeadNode> getTestHeads() {

+    return testHeads;

+  }

+

+  public void setTestHeads(List<TestHeadNode> testHeads) {

+    this.testHeads = testHeads;

+  }

+

+  public List<PfloNode> getPflos() {

+    return pflos;

+  }

+

+  public void setPflos(List<PfloNode> pflos) {

+    this.pflos = pflos;

+  }

+

+  public Map<String, Object> getTestDataTemplate() {

+    return testDataTemplate;

+  }

+

+  public void setTestDataTemplate(Map<String, Object> testDataTemplate) {

+    this.testDataTemplate = testDataTemplate;

+  }

+

+  public Date getCreatedAt() {

+    return createdAt;

+  }

+

+  public void setCreatedAt(Date createdAt) {

+    this.createdAt = createdAt;

+  }

+

+  public Date getUpdatedAt() {

+    return updatedAt;

+  }

+

+  public void setUpdatedAt(Date updatedAt) {

+    this.updatedAt = updatedAt;

+  }

+

+  public ObjectId getCreatedBy() {

+    return createdBy;

+  }

+

+  public void setCreatedBy(ObjectId createdBy) {

+    this.createdBy = createdBy;

+  }

+

+  public ObjectId getUpdatedBy() {

+    return updatedBy;

+  }

+

+  public void setUpdatedBy(ObjectId updatedBy) {

+    this.updatedBy = updatedBy;

+  }

+

+  private String getObjectIdString(ObjectId value) {

+    return value == null ? "\"\"" : "\"" + value.toString() + "\"";

+  }

+

+  @Override

+  public String toString() {

+    return Convert.objectToJson(this);

+  }

+}

diff --git a/otf-service-api/src/main/java/org/oran/otf/common/model/local/DeployTestStrategyRequest.java b/otf-service-api/src/main/java/org/oran/otf/common/model/local/DeployTestStrategyRequest.java
new file mode 100644
index 0000000..16040e7
--- /dev/null
+++ b/otf-service-api/src/main/java/org/oran/otf/common/model/local/DeployTestStrategyRequest.java
@@ -0,0 +1,73 @@
+/*  Copyright (c) 2019 AT&T Intellectual Property.                             #

+#                                                                              #

+#   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.oran.otf.common.model.local;

+

+import org.bson.types.ObjectId;

+

+public class DeployTestStrategyRequest {

+  private ObjectId testDefinitionDeployerId;

+  private ObjectId testDefinitionId;

+  private String definitionId;

+

+  public DeployTestStrategyRequest() {}

+

+  public DeployTestStrategyRequest(

+      ObjectId testDefinitionDeployerId, ObjectId testDefinitionId, String definitionId) {

+    this.testDefinitionDeployerId = testDefinitionDeployerId;

+    this.testDefinitionId = testDefinitionId;

+    this.definitionId = definitionId;

+  }

+

+  public ObjectId getTestDefinitionDeployerId() {

+    return testDefinitionDeployerId;

+  }

+

+  public void setTestDefinitionDeployerId(ObjectId testDefinitionDeployerId) {

+    this.testDefinitionDeployerId = testDefinitionDeployerId;

+  }

+

+  public ObjectId getTestDefinitionId() {

+    return testDefinitionId;

+  }

+

+  public void setTestDefinitionId(ObjectId testDefinitionId) {

+    this.testDefinitionId = testDefinitionId;

+  }

+

+  public String getDefinitionId() {

+    return definitionId;

+  }

+

+  public void setDefinitionId(String definitionId) {

+    this.definitionId = definitionId;

+  }

+

+  @Override

+  public String toString() {

+    return "{\"DeployTestStrategyRequest\":{"

+        + "\"testDefinitionDeployerId\":\""

+        + testDefinitionDeployerId

+        + "\""

+        + ", \"testDefinitionId\":\""

+        + testDefinitionId

+        + "\""

+        + ", \"definitionId\":\""

+        + definitionId

+        + "\""

+        + "}}";

+  }

+}

diff --git a/otf-service-api/src/main/java/org/oran/otf/common/model/local/OTFApiResponse.java b/otf-service-api/src/main/java/org/oran/otf/common/model/local/OTFApiResponse.java
new file mode 100644
index 0000000..e4f959e
--- /dev/null
+++ b/otf-service-api/src/main/java/org/oran/otf/common/model/local/OTFApiResponse.java
@@ -0,0 +1,66 @@
+/*  Copyright (c) 2019 AT&T Intellectual Property.                             #

+#                                                                              #

+#   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.oran.otf.common.model.local;

+

+import org.oran.otf.common.utility.gson.Convert;

+

+import java.util.Date;

+

+public class OTFApiResponse {

+

+    private int statusCode;

+    private String message;

+    private Date time;

+

+    public OTFApiResponse() {

+    }

+

+    public OTFApiResponse(int statusCode, String message) {

+        this.statusCode = statusCode;

+        this.message = message;

+        this.time = new Date(System.currentTimeMillis());

+    }

+

+    public int getStatusCode() {

+        return statusCode;

+    }

+

+    public void setStatusCode(int statusCode) {

+        this.statusCode = statusCode;

+    }

+

+    public String getMessage() {

+        return message;

+    }

+

+    public void setMessage(String message) {

+        this.message = message;

+    }

+

+    public Date getTime() {

+        return time;

+    }

+

+    public void setTime(Date time) {

+        this.time = time;

+    }

+

+    @Override

+    public String toString() {

+        return Convert.objectToJson(this);

+    }

+}

diff --git a/otf-service-api/src/main/java/org/oran/otf/common/model/local/ParallelFlowInput.java b/otf-service-api/src/main/java/org/oran/otf/common/model/local/ParallelFlowInput.java
new file mode 100644
index 0000000..2ac94e1
--- /dev/null
+++ b/otf-service-api/src/main/java/org/oran/otf/common/model/local/ParallelFlowInput.java
@@ -0,0 +1,83 @@
+/*  Copyright (c) 2019 AT&T Intellectual Property.                             #

+#                                                                              #

+#   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.oran.otf.common.model.local;

+

+import org.oran.otf.common.utility.gson.Convert;

+

+import java.io.Serializable;

+import java.util.List;

+

+public class ParallelFlowInput implements Serializable {

+

+  private static final long serialVersionUID = 1L;

+

+  private List<WorkflowRequest> args;

+  private boolean interruptOnFailure;

+  private int maxFailures;

+  private int threadPoolSize;

+

+  public ParallelFlowInput() {}

+

+  public ParallelFlowInput(

+      List<WorkflowRequest> args, boolean interruptOnFailure, int maxFailures, int threadPoolSize) {

+    this.args = args;

+    this.interruptOnFailure = interruptOnFailure;

+    this.maxFailures = maxFailures;

+    this.threadPoolSize = threadPoolSize;

+  }

+

+  public static long getSerialVersionUID() {

+    return serialVersionUID;

+  }

+

+  public List<WorkflowRequest> getArgs() {

+    return args;

+  }

+

+  public void setArgs(List<WorkflowRequest> args) {

+    this.args = args;

+  }

+

+  public boolean isInterruptOnFailure() {

+    return interruptOnFailure;

+  }

+

+  public void setInterruptOnFailure(boolean interruptOnFailure) {

+    this.interruptOnFailure = interruptOnFailure;

+  }

+

+  public int getMaxFailures() {

+    return maxFailures;

+  }

+

+  public void setMaxFailures(int maxFailures) {

+    this.maxFailures = maxFailures;

+  }

+

+  public int getThreadPoolSize() {

+    return threadPoolSize;

+  }

+

+  public void setThreadPoolSize(int threadPoolSize) {

+    this.threadPoolSize = threadPoolSize;

+  }

+

+  @Override

+  public String toString() {

+    return Convert.objectToJson(this);

+  }

+}

diff --git a/otf-service-api/src/main/java/org/oran/otf/common/model/local/PfloNode.java b/otf-service-api/src/main/java/org/oran/otf/common/model/local/PfloNode.java
new file mode 100644
index 0000000..d8a8bd5
--- /dev/null
+++ b/otf-service-api/src/main/java/org/oran/otf/common/model/local/PfloNode.java
@@ -0,0 +1,61 @@
+/*  Copyright (c) 2019 AT&T Intellectual Property.                             #

+#                                                                              #

+#   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.oran.otf.common.model.local;

+

+import org.oran.otf.common.utility.gson.Convert;

+

+import java.io.Serializable;

+

+public class PfloNode implements Serializable {

+

+  private static final long serialVersionUID = 1L;

+

+  private String bpmnPlfoTaskId;

+  private String label;

+

+  public PfloNode() {}

+

+  public PfloNode(String bpmnPlfoTaskId, String label) {

+    this.bpmnPlfoTaskId = bpmnPlfoTaskId;

+    this.label = label;

+  }

+

+  public static long getSerialVersionUID() {

+    return serialVersionUID;

+  }

+

+  public String getBpmnPlfoTaskId() {

+    return bpmnPlfoTaskId;

+  }

+

+  public void setBpmnPlfoTaskId(String bpmnPlfoTaskId) {

+    this.bpmnPlfoTaskId = bpmnPlfoTaskId;

+  }

+

+  public String getLabel() {

+    return label;

+  }

+

+  public void setLabel(String label) {

+    this.label = label;

+  }

+

+  @Override

+  public String toString() {

+    return Convert.objectToJson(this);

+  }

+}

diff --git a/otf-service-api/src/main/java/org/oran/otf/common/model/local/TestHeadNode.java b/otf-service-api/src/main/java/org/oran/otf/common/model/local/TestHeadNode.java
new file mode 100644
index 0000000..99ed995
--- /dev/null
+++ b/otf-service-api/src/main/java/org/oran/otf/common/model/local/TestHeadNode.java
@@ -0,0 +1,58 @@
+/*  Copyright (c) 2019 AT&T Intellectual Property.                             #

+#                                                                              #

+#   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.oran.otf.common.model.local;

+

+import org.oran.otf.common.utility.gson.Convert;

+import java.io.Serializable;

+import org.bson.types.ObjectId;

+

+public class TestHeadNode implements Serializable {

+

+  private static final long serialVersionUID = 1L;

+

+  private ObjectId testHeadId;

+  private String bpmnVthTaskId;

+

+  public TestHeadNode() {

+  }

+

+  public TestHeadNode(ObjectId testHeadId, String taskId) {

+    this.testHeadId = testHeadId;

+    this.bpmnVthTaskId = taskId;

+  }

+

+  public ObjectId getTestHeadId() {

+    return testHeadId;

+  }

+

+  public void setTestHeadId(ObjectId testHeadId) {

+    this.testHeadId = testHeadId;

+  }

+

+  public String getBpmnVthTaskId() {

+    return bpmnVthTaskId;

+  }

+

+  public void setBpmnVthTaskId(String bpmnVthTaskId) {

+    this.bpmnVthTaskId = bpmnVthTaskId;

+  }

+

+  @Override

+  public String toString() {

+    return Convert.objectToJson(this);

+  }

+}

diff --git a/otf-service-api/src/main/java/org/oran/otf/common/model/local/TestHeadRequest.java b/otf-service-api/src/main/java/org/oran/otf/common/model/local/TestHeadRequest.java
new file mode 100644
index 0000000..89c7457
--- /dev/null
+++ b/otf-service-api/src/main/java/org/oran/otf/common/model/local/TestHeadRequest.java
@@ -0,0 +1,53 @@
+/*  Copyright (c) 2019 AT&T Intellectual Property.                             #

+#                                                                              #

+#   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.oran.otf.common.model.local;

+

+import java.io.Serializable;

+import java.util.Map;

+

+public class TestHeadRequest implements Serializable {

+  private static final long serialVersionUID = 1L;

+  private Map<String, String> headers;

+  private Map<String, Object>  body;

+

+  public TestHeadRequest(){}

+

+  public TestHeadRequest(Map<String, String> headers,

+                         Map<String, Object> body) {

+    this.headers = headers;

+    this.body = body;

+  }

+

+  public Map<String, String> getHeaders() {

+    return headers;

+  }

+

+  public void setHeaders(Map<String, String> headers) {

+    this.headers = headers;

+  }

+

+  public Map<String, Object> getBody() {

+    return body;

+  }

+

+  public void setBody(Map<String, Object> body) {

+    this.body = body;

+  }

+

+

+

+}

diff --git a/otf-service-api/src/main/java/org/oran/otf/common/model/local/TestHeadResult.java b/otf-service-api/src/main/java/org/oran/otf/common/model/local/TestHeadResult.java
new file mode 100644
index 0000000..55f82e9
--- /dev/null
+++ b/otf-service-api/src/main/java/org/oran/otf/common/model/local/TestHeadResult.java
@@ -0,0 +1,146 @@
+/*  Copyright (c) 2019 AT&T Intellectual Property.                             #

+#                                                                              #

+#   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.oran.otf.common.model.local;

+

+import org.oran.otf.common.utility.gson.Convert;

+import org.bson.types.ObjectId;

+

+import java.io.Serializable;

+import java.util.Date;

+import java.util.Map;

+

+public class TestHeadResult implements Serializable {

+

+  private static final long serialVersionUID = 1L;

+

+  private ObjectId testHeadId;

+  private String testHeadName;

+  private ObjectId testHeadGroupId;

+  private String bpmnVthTaskId;

+

+  //TODO: RG Remove maps below, setters and getters to return to normal

+  //private Map<String, String> testHeadHeaders;

+  //private int testHeadCode;

+  private int statusCode;

+

+  private TestHeadRequest testHeadRequest;

+  private Map<String, Object> testHeadResponse;

+  private Date startTime;

+  private Date endTime;

+

+  public TestHeadResult() {

+  }

+

+  public TestHeadResult(

+      ObjectId testHeadId,

+      String testHeadName,

+      ObjectId testHeadGroupId,

+      String bpmnVthTaskId,

+

+      //TODO: RG changed code to int and changed testHeadRequest from Map<String, String> to RequestContent

+      int statusCode,

+

+      TestHeadRequest testHeadRequest,

+      Map<String, Object> testHeadResponse,

+      Date startTime,

+      Date endTime) {

+    this.testHeadId = testHeadId;

+    this.testHeadName = testHeadName;

+    this.testHeadGroupId = testHeadGroupId;

+    this.bpmnVthTaskId = bpmnVthTaskId;

+

+    //this.testHeadHeaders = testHeadHeaders;

+    this.statusCode = statusCode;

+

+    this.testHeadRequest = testHeadRequest;

+    this.testHeadResponse = testHeadResponse;

+    this.startTime = startTime;

+    this.endTime = endTime;

+  }

+

+  public int getStatusCode(){return statusCode;}

+  public void setStatusCode(int testHeadCode){this.statusCode = statusCode;}

+

+  public ObjectId getTestHeadId() {

+    return testHeadId;

+  }

+

+  public void setTestHeadId(ObjectId testHeadId) {

+    this.testHeadId = testHeadId;

+  }

+

+  public String getTestHeadName() {

+    return testHeadName;

+  }

+

+  public void setTestHeadName(String testHeadName) {

+    this.testHeadName = testHeadName;

+  }

+

+  public ObjectId getTestHeadGroupId() {

+    return testHeadGroupId;

+  }

+

+  public void setTestHeadGroupId(ObjectId testHeadGroupId) {

+    this.testHeadGroupId = testHeadGroupId;

+  }

+

+  public String getBpmnVthTaskId() {

+    return bpmnVthTaskId;

+  }

+

+  public void setBpmnVthTaskId(String bpmnVthTaskId) {

+    this.bpmnVthTaskId = bpmnVthTaskId;

+  }

+

+  public TestHeadRequest getTestHeadRequest() {

+    return testHeadRequest;

+  }

+

+  public void setTestHeadRequest(TestHeadRequest testHeadRequest) {

+    this.testHeadRequest = testHeadRequest;

+  }

+

+  public Map<String, Object> getTestHeadResponse() {

+    return testHeadResponse;

+  }

+

+  public void setTestHeadResponse(Map<String, Object> testHeadResponse) {

+    this.testHeadResponse = testHeadResponse;

+  }

+

+  public Date getStartTime() {

+    return startTime;

+  }

+

+  public void setStartTime(Date startTime) {

+    this.startTime = startTime;

+  }

+

+  public Date getEndTime() {

+    return endTime;

+  }

+

+  public void setEndTime(Date endTime) {

+    this.endTime = endTime;

+  }

+

+  @Override

+  public String toString() {

+    return Convert.objectToJson(this);

+  }

+}

diff --git a/otf-service-api/src/main/java/org/oran/otf/common/model/local/TestInstanceCreateRequest.java b/otf-service-api/src/main/java/org/oran/otf/common/model/local/TestInstanceCreateRequest.java
new file mode 100644
index 0000000..b497477
--- /dev/null
+++ b/otf-service-api/src/main/java/org/oran/otf/common/model/local/TestInstanceCreateRequest.java
@@ -0,0 +1,215 @@
+/*  Copyright (c) 2019 AT&T Intellectual Property.                             #

+#                                                                              #

+#   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.oran.otf.common.model.local;

+

+import org.oran.otf.common.utility.gson.Convert;

+import com.google.common.base.Strings;

+import java.io.Serializable;

+import java.util.HashMap;

+import org.bson.types.ObjectId;

+

+public class TestInstanceCreateRequest implements Serializable {

+  private static final long serialVersionUID = 1L;

+

+  private ObjectId testDefinitionId = null;

+  private int version = Integer.MIN_VALUE;

+  private String processDefinitionKey = null;

+

+  private String testInstanceName;

+  private String testInstanceDescription;

+  private HashMap<String, ParallelFlowInput> pfloInput;

+  private HashMap<String, Object> simulationVthInput;

+  private HashMap<String, Object> testData;

+  private HashMap<String, Object> vthInput;

+  private ObjectId createdBy;

+  private boolean useLatestTestDefinition = true;

+  private boolean simulationMode = false;

+  private long maxExecutionTimeInMillis = 0L;

+

+  public TestInstanceCreateRequest() throws Exception {

+    this.validate();

+  }

+

+  public TestInstanceCreateRequest(

+      String testInstanceName,

+      String testInstanceDescription,

+      HashMap<String, ParallelFlowInput> pfloInput,

+      HashMap<String, Object> simulationVthInput,

+      HashMap<String, Object> testData,

+      HashMap<String, Object> vthInput,

+      ObjectId createdBy,

+      boolean useLatestTestDefinition,

+      boolean simulationMode,

+      long maxExecutionTimeInMillis) throws Exception {

+    this.testInstanceName = testInstanceName;

+    this.testInstanceDescription = testInstanceDescription;

+    this.pfloInput = pfloInput;

+    this.simulationVthInput = simulationVthInput;

+    this.testData = testData;

+    this.vthInput = vthInput;

+    this.createdBy = createdBy;

+    this.useLatestTestDefinition = useLatestTestDefinition;

+    this.simulationMode = simulationMode;

+    this.maxExecutionTimeInMillis = maxExecutionTimeInMillis;

+

+    this.validate();

+  }

+

+  private void validate() throws Exception {

+    String missingFieldFormat = "The field %s is required.";

+    if (Strings.isNullOrEmpty(testInstanceName)) {

+      throw new Exception(String.format(missingFieldFormat, "testInstanceName"));

+    }

+

+    if (Strings.isNullOrEmpty(testInstanceDescription)) {

+      throw new Exception(String.format(missingFieldFormat, "testInstanceDescription"));

+    }

+

+    if (pfloInput == null) {

+      pfloInput = new HashMap<>();

+    }

+

+    if (simulationVthInput == null) {

+      simulationVthInput = new HashMap<>();

+    }

+

+    if (testData == null) {

+      testData = new HashMap<>();

+    }

+

+    if (vthInput == null) {

+      vthInput = new HashMap<>();

+    }

+

+    if (this.maxExecutionTimeInMillis < 0L) {

+      this.maxExecutionTimeInMillis = 0L;

+    }

+  }

+

+  public static long getSerialVersionUID() {

+    return serialVersionUID;

+  }

+

+  public ObjectId getTestDefinitionId() {

+    return testDefinitionId;

+  }

+

+  public void setTestDefinitionId(ObjectId testDefinitionId) {

+    this.testDefinitionId = testDefinitionId;

+  }

+

+  public int getVersion() {

+    return version;

+  }

+

+  public void setVersion(int version) {

+    this.version = version;

+  }

+

+  public String getProcessDefinitionKey() {

+    return processDefinitionKey;

+  }

+

+  public void setProcessDefinitionKey(String processDefinitionKey) {

+    this.processDefinitionKey = processDefinitionKey;

+  }

+

+  public String getTestInstanceName() {

+    return testInstanceName;

+  }

+

+  public void setTestInstanceName(String testInstanceName) {

+    this.testInstanceName = testInstanceName;

+  }

+

+  public String getTestInstanceDescription() {

+    return testInstanceDescription;

+  }

+

+  public void setTestInstanceDescription(String testInstanceDescription) {

+    this.testInstanceDescription = testInstanceDescription;

+  }

+

+  public HashMap<String, ParallelFlowInput> getPfloInput() {

+    return pfloInput;

+  }

+

+  public void setPfloInput(HashMap<String, ParallelFlowInput> pfloInput) {

+    this.pfloInput = pfloInput;

+  }

+

+  public HashMap<String, Object> getSimulationVthInput() {

+    return simulationVthInput;

+  }

+

+  public void setSimulationVthInput(HashMap<String, Object> simulationVthInput) {

+    this.simulationVthInput = simulationVthInput;

+  }

+

+  public HashMap<String, Object> getTestData() {

+    return testData;

+  }

+

+  public void setTestData(HashMap<String, Object> testData) {

+    this.testData = testData;

+  }

+

+  public HashMap<String, Object> getVthInput() {

+    return vthInput;

+  }

+

+  public void setVthInput(HashMap<String, Object> vthInput) {

+    this.vthInput = vthInput;

+  }

+

+  public ObjectId getCreatedBy() {

+    return createdBy;

+  }

+

+  public void setCreatedBy(ObjectId createdBy) {

+    this.createdBy = createdBy;

+  }

+

+  public boolean isUseLatestTestDefinition() {

+    return useLatestTestDefinition;

+  }

+

+  public void setUseLatestTestDefinition(boolean useLatestTestDefinition) {

+    this.useLatestTestDefinition = useLatestTestDefinition;

+  }

+

+  public boolean isSimulationMode() {

+    return simulationMode;

+  }

+

+  public void setSimulationMode(boolean simulationMode) {

+    this.simulationMode = simulationMode;

+  }

+

+  public long getMaxExecutionTimeInMillis() {

+    return maxExecutionTimeInMillis;

+  }

+

+  public void setMaxExecutionTimeInMillis(long maxExecutionTimeInMillis) {

+    this.maxExecutionTimeInMillis = maxExecutionTimeInMillis;

+  }

+

+  @Override

+  public String toString() {

+    return Convert.objectToJson(this);

+  }

+}

diff --git a/otf-service-api/src/main/java/org/oran/otf/common/model/local/UserGroup.java b/otf-service-api/src/main/java/org/oran/otf/common/model/local/UserGroup.java
new file mode 100644
index 0000000..536bc67
--- /dev/null
+++ b/otf-service-api/src/main/java/org/oran/otf/common/model/local/UserGroup.java
@@ -0,0 +1,57 @@
+/*  Copyright (c) 2019 AT&T Intellectual Property.                             #

+#                                                                              #

+#   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.oran.otf.common.model.local;

+

+import org.oran.otf.common.utility.gson.Convert;

+import java.io.Serializable;

+import java.util.List;

+import org.bson.types.ObjectId;

+

+public class UserGroup implements Serializable {

+

+  private static final long serialVersionUID = 1L;

+

+  private ObjectId groupId;

+  private List<String> permissions;

+

+  public UserGroup(){}

+  public UserGroup(ObjectId groupId, List<String> permissions) {

+    this.groupId = groupId;

+    this.permissions = permissions;

+  }

+

+  public ObjectId getGroupId() {

+    return groupId;

+  }

+

+  public void setGroupId(ObjectId groupId) {

+    this.groupId = groupId;

+  }

+

+  public List<String> getPermissions() {

+    return permissions;

+  }

+

+  public void setPermissions(List<String> permissions) {

+    this.permissions = permissions;

+  }

+

+  @Override

+  public String toString() {

+    return Convert.objectToJson(this);

+  }

+}

diff --git a/otf-service-api/src/main/java/org/oran/otf/common/model/local/WorkflowRequest.java b/otf-service-api/src/main/java/org/oran/otf/common/model/local/WorkflowRequest.java
new file mode 100644
index 0000000..f7089a0
--- /dev/null
+++ b/otf-service-api/src/main/java/org/oran/otf/common/model/local/WorkflowRequest.java
@@ -0,0 +1,163 @@
+/*  Copyright (c) 2019 AT&T Intellectual Property.                             #

+#                                                                              #

+#   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.oran.otf.common.model.local;

+

+import org.oran.otf.common.utility.gson.Convert;

+import com.fasterxml.jackson.annotation.JsonIgnoreProperties;

+import java.io.Serializable;

+import java.util.Map;

+import org.bson.types.ObjectId;

+

+@JsonIgnoreProperties(ignoreUnknown = true)

+public class WorkflowRequest implements Serializable {

+

+  private static final long serialVersionUID = 1L;

+

+  private boolean async = false;

+  private ObjectId executorId = null;

+  private ObjectId testInstanceId = null;

+  private Map<String, ParallelFlowInput> pfloInput = null;

+  private Map<String, Object> testData = null;

+  private Map<String, Object> vthInput = null;

+  private long maxExecutionTimeInMillis = 0L;

+

+  public WorkflowRequest() throws Exception {

+    this.validate();

+  }

+

+  public WorkflowRequest(

+          boolean async,

+          ObjectId executorId,

+          ObjectId testInstanceId,

+          Map<String, ParallelFlowInput> pfloInput,

+          Map<String, Object> testData,

+          Map<String, Object> vthInput,

+          int maxExecutionTimeInMillis)

+          throws Exception {

+    this.async = async;

+    this.executorId = executorId;

+    this.testInstanceId = testInstanceId;

+    this.pfloInput = pfloInput;

+    this.testData = testData;

+    this.vthInput = vthInput;

+    this.maxExecutionTimeInMillis = maxExecutionTimeInMillis;

+

+    this.validate();

+  }

+

+  public WorkflowRequest(

+          boolean async,

+          String executorId,

+          String testInstanceId,

+          Map<String, ParallelFlowInput> pfloInput,

+          Map<String, Object> testData,

+          Map<String, Object> vthInput,

+          int maxExecutionTimeInMillis)

+          throws Exception {

+    this.async = async;

+    this.executorId = new ObjectId(executorId);

+    this.testInstanceId = new ObjectId(testInstanceId);

+    this.pfloInput = pfloInput;

+    this.testData = testData;

+    this.vthInput = vthInput;

+    this.maxExecutionTimeInMillis = maxExecutionTimeInMillis;

+

+    this.validate();

+  }

+

+  private void validate() throws Exception {

+    String missingFieldFormat = "Missing required field %s.";

+    //    if (this.async && this.asyncTopic == null) {

+    //      throw new Exception(String.format(missingFieldFormat, "asyncTopic"));

+    //    }

+

+    // Only required on the Camunda engine

+    //    if (this.executorId == null) {

+    //      throw new Exception(String.format(missingFieldFormat, "executorId"));

+    //    }

+

+    // Only required on the Camunda engine

+    //    if (this.testInstanceId == null) {

+    //      throw new Exception(String.format(missingFieldFormat, "testInstanceId"));

+    //    }

+

+    if (this.maxExecutionTimeInMillis < 0L) {

+      this.maxExecutionTimeInMillis = 0L;

+    }

+  }

+

+  public boolean isAsync() {

+    return async;

+  }

+

+  public void setAsync(boolean async) {

+    this.async = async;

+  }

+

+  public ObjectId getExecutorId() {

+    return executorId;

+  }

+

+  public void setExecutorId(ObjectId executorId) {

+    this.executorId = executorId;

+  }

+

+  public ObjectId getTestInstanceId() {

+    return testInstanceId;

+  }

+

+  public void setTestInstanceId(ObjectId testInstanceId) {

+    this.testInstanceId = testInstanceId;

+  }

+

+  public Map<String, ParallelFlowInput> getPfloInput() {

+    return pfloInput;

+  }

+

+  public void setPfloInput(Map<String, ParallelFlowInput> pfloInput) {

+    this.pfloInput = pfloInput;

+  }

+

+  public Map<String, Object> getTestData() {

+    return testData;

+  }

+

+  public void setTestData(Map<String, Object> testData) {

+    this.testData = testData;

+  }

+

+  public Map<String, Object> getVthInput() {

+    return vthInput;

+  }

+

+  public void setVthInput(Map<String, Object> vthInput) {

+    this.vthInput = vthInput;

+  }

+

+  public long getMaxExecutionTimeInMillis() {

+    return maxExecutionTimeInMillis;

+  }

+

+  public void setMaxExecutionTimeInMillis(long maxExecutionTimeInMillis) {

+    this.maxExecutionTimeInMillis = maxExecutionTimeInMillis;

+  }

+

+  @Override

+  public String toString() {

+    return Convert.objectToJson(this);

+  }

+}
\ No newline at end of file
diff --git a/otf-service-api/src/main/java/org/oran/otf/common/repository/GroupRepository.java b/otf-service-api/src/main/java/org/oran/otf/common/repository/GroupRepository.java
new file mode 100644
index 0000000..69d000c
--- /dev/null
+++ b/otf-service-api/src/main/java/org/oran/otf/common/repository/GroupRepository.java
@@ -0,0 +1,31 @@
+/*  Copyright (c) 2019 AT&T Intellectual Property.                             #

+#                                                                              #

+#   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.oran.otf.common.repository;

+

+import org.oran.otf.common.model.Group;

+import org.bson.types.ObjectId;

+import org.springframework.data.mongodb.repository.MongoRepository;

+import org.springframework.data.mongodb.repository.Query;

+

+import java.util.List;

+

+public interface GroupRepository extends MongoRepository<Group, String> {

+    @Query("{ 'members.userId': ?0 }")

+    public List<Group> findAllByMembersId(ObjectId membersUserId);

+    public Group findFirstByGroupName(String groupName);

+}

+

diff --git a/otf-service-api/src/main/java/org/oran/otf/common/repository/TestDefinitionRepository.java b/otf-service-api/src/main/java/org/oran/otf/common/repository/TestDefinitionRepository.java
new file mode 100644
index 0000000..ecd2bab
--- /dev/null
+++ b/otf-service-api/src/main/java/org/oran/otf/common/repository/TestDefinitionRepository.java
@@ -0,0 +1,26 @@
+/*  Copyright (c) 2019 AT&T Intellectual Property.                             #

+#                                                                              #

+#   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.oran.otf.common.repository;

+

+import org.oran.otf.common.model.TestDefinition;

+import java.util.Optional;

+import org.springframework.data.mongodb.repository.MongoRepository;

+

+public interface TestDefinitionRepository extends MongoRepository<TestDefinition, String> {

+

+  Optional<TestDefinition> findByProcessDefinitionKey(String processDefinitionKey);

+}

diff --git a/otf-service-api/src/main/java/org/oran/otf/common/repository/TestExecutionRepository.java b/otf-service-api/src/main/java/org/oran/otf/common/repository/TestExecutionRepository.java
new file mode 100644
index 0000000..ee86a82
--- /dev/null
+++ b/otf-service-api/src/main/java/org/oran/otf/common/repository/TestExecutionRepository.java
@@ -0,0 +1,26 @@
+/*  Copyright (c) 2019 AT&T Intellectual Property.                             #

+#                                                                              #

+#   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.oran.otf.common.repository;

+

+import org.oran.otf.common.model.TestExecution;

+import java.util.Optional;

+import org.springframework.data.mongodb.repository.MongoRepository;

+

+public interface TestExecutionRepository extends MongoRepository<TestExecution, String> {

+

+  Optional<TestExecution> findFirstByProcessInstanceId(String processInstanceId);

+}

diff --git a/otf-service-api/src/main/java/org/oran/otf/common/repository/TestHeadRepository.java b/otf-service-api/src/main/java/org/oran/otf/common/repository/TestHeadRepository.java
new file mode 100644
index 0000000..09ab4ac
--- /dev/null
+++ b/otf-service-api/src/main/java/org/oran/otf/common/repository/TestHeadRepository.java
@@ -0,0 +1,28 @@
+/*  Copyright (c) 2019 AT&T Intellectual Property.                             #

+#                                                                              #

+#   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.oran.otf.common.repository;

+

+import org.oran.otf.common.model.TestHead;

+import org.springframework.data.mongodb.repository.MongoRepository;

+

+import java.util.Optional;

+

+public interface TestHeadRepository extends MongoRepository<TestHead, String> {

+    Optional<TestHead> findByTestHeadName(String testHeadName);

+

+

+}

diff --git a/otf-service-api/src/main/java/org/oran/otf/common/repository/TestInstanceRepository.java b/otf-service-api/src/main/java/org/oran/otf/common/repository/TestInstanceRepository.java
new file mode 100644
index 0000000..16d1dcb
--- /dev/null
+++ b/otf-service-api/src/main/java/org/oran/otf/common/repository/TestInstanceRepository.java
@@ -0,0 +1,36 @@
+/*  Copyright (c) 2019 AT&T Intellectual Property.                             #

+#                                                                              #

+#   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.oran.otf.common.repository;

+

+import org.oran.otf.common.model.TestInstance;

+import java.util.List;

+import java.util.Optional;

+import org.bson.types.ObjectId;

+import org.springframework.data.mongodb.repository.MongoRepository;

+import org.springframework.data.mongodb.repository.Query;

+

+public interface TestInstanceRepository extends MongoRepository<TestInstance, String> {

+

+  Optional<TestInstance> findByTestInstanceName(String testInstanceName);

+

+  @Query("{ 'testDefinitionId': ?0 }")

+  List<TestInstance> findAllByTestDefinitionId(ObjectId testDefinitionId);

+

+  @Query("{ 'testDefinitionId': ?0, 'processDefinitionId': ?1 }")

+  List<TestInstance> findAllByTestDefinitionIdAndPDId(

+      ObjectId testDefinitionId, String processDefinitionId);

+}

diff --git a/otf-service-api/src/main/java/org/oran/otf/common/repository/UserRepository.java b/otf-service-api/src/main/java/org/oran/otf/common/repository/UserRepository.java
new file mode 100644
index 0000000..5dd669f
--- /dev/null
+++ b/otf-service-api/src/main/java/org/oran/otf/common/repository/UserRepository.java
@@ -0,0 +1,25 @@
+/*  Copyright (c) 2019 AT&T Intellectual Property.                             #

+#                                                                              #

+#   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.oran.otf.common.repository;

+

+import org.oran.otf.common.model.User;

+import java.util.Optional;

+import org.springframework.data.mongodb.repository.MongoRepository;

+

+public interface UserRepository extends MongoRepository<User, String> {

+  Optional<User> findFirstByEmail(String email);

+}

diff --git a/otf-service-api/src/main/java/org/oran/otf/common/util/HttpUtils.java b/otf-service-api/src/main/java/org/oran/otf/common/util/HttpUtils.java
new file mode 100644
index 0000000..b5e3a39
--- /dev/null
+++ b/otf-service-api/src/main/java/org/oran/otf/common/util/HttpUtils.java
@@ -0,0 +1,19 @@
+/*  Copyright (c) 2019 AT&T Intellectual Property.                             #

+#                                                                              #

+#   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.oran.otf.common.util;

+

+public class HttpUtils {}

diff --git a/otf-service-api/src/main/java/org/oran/otf/common/utility/RSAEncryptDecrypt.java b/otf-service-api/src/main/java/org/oran/otf/common/utility/RSAEncryptDecrypt.java
new file mode 100644
index 0000000..1309d6d
--- /dev/null
+++ b/otf-service-api/src/main/java/org/oran/otf/common/utility/RSAEncryptDecrypt.java
@@ -0,0 +1,54 @@
+/*  Copyright (c) 2019 AT&T Intellectual Property.                             #

+#                                                                              #

+#   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.oran.otf.common.utility;

+

+import java.security.KeyPair;

+import java.security.KeyPairGenerator;

+import java.security.NoSuchAlgorithmException;

+import javax.crypto.Cipher;

+import org.springframework.stereotype.Service;

+

+@Service

+public class RSAEncryptDecrypt {

+

+  private KeyPair keyPair;

+

+  public RSAEncryptDecrypt() throws NoSuchAlgorithmException {

+    this.keyPair = buildKeyPair();

+  }

+

+  private KeyPair buildKeyPair() throws NoSuchAlgorithmException {

+    final int keySize = 2048;

+    KeyPairGenerator keyPairGenerator = KeyPairGenerator.getInstance("RSA");

+    keyPairGenerator.initialize(keySize);

+    return keyPairGenerator.genKeyPair();

+  }

+

+  public byte[] encrypt(String message) throws Exception {

+    Cipher cipher = Cipher.getInstance("RSA");

+    cipher.init(Cipher.ENCRYPT_MODE, this.keyPair.getPrivate());

+

+    return cipher.doFinal(message.getBytes());

+  }

+

+  public byte[] decrypt(byte[] encrypted) throws Exception {

+    Cipher cipher = Cipher.getInstance("RSA");

+    cipher.init(Cipher.DECRYPT_MODE, this.keyPair.getPublic());

+

+    return cipher.doFinal(encrypted);

+  }

+}

diff --git a/otf-service-api/src/main/java/org/oran/otf/common/utility/Utility.java b/otf-service-api/src/main/java/org/oran/otf/common/utility/Utility.java
new file mode 100644
index 0000000..c781ffb
--- /dev/null
+++ b/otf-service-api/src/main/java/org/oran/otf/common/utility/Utility.java
@@ -0,0 +1,84 @@
+/*  Copyright (c) 2019 AT&T Intellectual Property.                             #

+#                                                                              #

+#   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.oran.otf.common.utility;

+

+import com.fasterxml.jackson.databind.ObjectMapper;

+import com.google.common.base.Strings;

+

+import java.lang.reflect.Field;

+import java.util.ArrayList;

+import java.util.Arrays;

+import java.util.Collection;

+import java.util.HashMap;

+import java.util.List;

+import java.util.Map;

+import java.util.UUID;

+

+public class Utility {

+

+  public static String getLoggerPrefix() {

+    return "[" + Thread.currentThread().getStackTrace()[2].getMethodName() + "]: ";

+  }

+

+  public static Map<?, ?> toMap(Object obj) throws Exception {

+    ObjectMapper mapper = new ObjectMapper();

+    return mapper.convertValue(obj, HashMap.class);

+  }

+

+  public static boolean isCollection(Object obj) {

+    return obj.getClass().isArray() || obj instanceof Collection;

+  }

+

+  public static List<?> toList(Object obj) {

+    if (obj == null) {

+      throw new NullPointerException("Argument cannot be null.");

+    }

+

+    List<?> list = new ArrayList<>();

+    if (obj.getClass().isArray()) {

+      list = Arrays.asList((Object[]) obj);

+    } else if (obj instanceof Collection) {

+      list = new ArrayList<>((Collection<?>) obj);

+    }

+

+    return list;

+  }

+

+  public static boolean isValidUuid(String str) {

+    if (Strings.isNullOrEmpty(str)) {

+      return false;

+    }

+    try {

+      UUID uuid = UUID.fromString(str);

+      return uuid.toString().equalsIgnoreCase(str);

+    } catch (IllegalArgumentException iae) {

+      return false;

+    }

+  }

+

+  // check a name type pair to see if it matches field in class

+  public static boolean isTypeVariablePairInClass(String variableName, Object variableValue, Class javaClass){

+    List<Field> testHeadFields = Arrays.asList(javaClass.getFields());

+    for(int i = 0; i < testHeadFields.size(); i++){

+      Field field = testHeadFields.get(i);

+      if(field.getName().equals(variableName) && field.getType().isInstance(variableValue)){

+        return true;

+      }

+    }

+    return false;

+  }

+}

diff --git a/otf-service-api/src/main/java/org/oran/otf/common/utility/database/Generic.java b/otf-service-api/src/main/java/org/oran/otf/common/utility/database/Generic.java
new file mode 100644
index 0000000..5de5043
--- /dev/null
+++ b/otf-service-api/src/main/java/org/oran/otf/common/utility/database/Generic.java
@@ -0,0 +1,36 @@
+/*  Copyright (c) 2019 AT&T Intellectual Property.                             #

+#                                                                              #

+#   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.oran.otf.common.utility.database;

+

+import java.util.Optional;

+import org.bson.types.ObjectId;

+import org.springframework.data.mongodb.repository.MongoRepository;

+

+public class Generic {

+

+  public static <T> boolean identifierExistsInCollection(

+      MongoRepository<T, String> repository, ObjectId identifier) {

+    return repository.findById(identifier.toString()).isPresent();

+  }

+

+  public static <T> T findByIdGeneric(MongoRepository<T, String> repository, ObjectId identifier) {

+    Optional<T> optionalObj = repository.findById(identifier.toString());

+    return optionalObj.orElse(null);

+  }

+

+

+}

diff --git a/otf-service-api/src/main/java/org/oran/otf/common/utility/database/TestExecutionUtility.java b/otf-service-api/src/main/java/org/oran/otf/common/utility/database/TestExecutionUtility.java
new file mode 100644
index 0000000..c54359f
--- /dev/null
+++ b/otf-service-api/src/main/java/org/oran/otf/common/utility/database/TestExecutionUtility.java
@@ -0,0 +1,36 @@
+/*  Copyright (c) 2019 AT&T Intellectual Property.                             #

+#                                                                              #

+#   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.oran.otf.common.utility.database;

+

+import org.oran.otf.common.model.TestExecution;

+import com.mongodb.client.result.UpdateResult;

+import org.springframework.data.mongodb.core.MongoTemplate;

+import org.springframework.data.mongodb.core.query.Criteria;

+import org.springframework.data.mongodb.core.query.Query;

+import org.springframework.data.mongodb.core.query.Update;

+

+public class TestExecutionUtility {

+

+  public static void saveTestResult(

+      MongoTemplate mongoOperation, TestExecution execution, String testResult) {

+    Query query = new Query();

+    query.addCriteria(Criteria.where("businessKey").is(execution.getBusinessKey()));

+    Update update = new Update();

+    update.set("testResult", testResult);

+    UpdateResult result = mongoOperation.updateFirst(query, update, TestExecution.class);

+  }

+}

diff --git a/otf-service-api/src/main/java/org/oran/otf/common/utility/gson/Convert.java b/otf-service-api/src/main/java/org/oran/otf/common/utility/gson/Convert.java
new file mode 100644
index 0000000..bc1d0af
--- /dev/null
+++ b/otf-service-api/src/main/java/org/oran/otf/common/utility/gson/Convert.java
@@ -0,0 +1,95 @@
+/*  Copyright (c) 2019 AT&T Intellectual Property.                             #

+#                                                                              #

+#   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.oran.otf.common.utility.gson;

+

+import com.fasterxml.jackson.core.type.TypeReference;

+import com.fasterxml.jackson.databind.DeserializationFeature;

+import com.fasterxml.jackson.databind.ObjectMapper;

+import com.google.gson.Gson;

+import com.google.gson.GsonBuilder;

+import com.google.gson.JsonDeserializationContext;

+import com.google.gson.JsonDeserializer;

+import com.google.gson.JsonElement;

+import com.google.gson.JsonParseException;

+import com.google.gson.JsonPrimitive;

+import com.google.gson.JsonSerializationContext;

+import com.google.gson.JsonSerializer;

+import com.google.gson.reflect.TypeToken;

+

+import java.io.IOException;

+import java.lang.reflect.Type;

+import java.util.HashMap;

+import java.util.Map;

+import org.bson.types.ObjectId;

+

+public class Convert {

+

+  private static final GsonBuilder gsonBuilder =

+      new GsonBuilder()

+          .setDateFormat("yyyy-MM-dd'T'HH:mm:ssZ")

+          .registerTypeAdapter(

+              ObjectId.class,

+              new JsonSerializer<ObjectId>() {

+                @Override

+                public JsonElement serialize(

+                    ObjectId src, Type typeOfSrc, JsonSerializationContext context) {

+                  return new JsonPrimitive(src.toHexString());

+                }

+              })

+          .registerTypeAdapter(

+              ObjectId.class,

+              new JsonDeserializer<ObjectId>() {

+                @Override

+                public ObjectId deserialize(

+                    JsonElement json, Type typeOfT, JsonDeserializationContext context)

+                    throws JsonParseException {

+                  return new ObjectId(json.getAsString());

+                }

+              });

+

+  public static Gson getGson() {

+    return gsonBuilder.create();

+  }

+

+  public static String mapToJson(Map map) {

+    if (map.isEmpty()) {

+      return "{}";

+    }

+    return getGson().toJson(map);

+  }

+

+  public static Map<String, Object> jsonToMap(String json) {

+    Type type = new TypeToken<HashMap<String, Object>>() {

+    }.getType();

+    return getGson().fromJson(json, type);

+  }

+

+  public static String objectToJson(Object obj) {

+    return getGson().toJson(obj);

+  }

+

+  public static<T> T mapToObject(Map map, TypeReference<T> typeReference) throws IOException {

+    return jsonToObject(mapToJson(map), typeReference);

+  }

+

+  public static <T> T jsonToObject(String json, TypeReference<T> typeReference) throws IOException {

+    ObjectMapper objectMapper = new ObjectMapper();

+    objectMapper

+        .configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, true);

+    return objectMapper.readValue(json, typeReference);

+  }

+}

diff --git a/otf-service-api/src/main/java/org/oran/otf/common/utility/gson/GsonUtils.java b/otf-service-api/src/main/java/org/oran/otf/common/utility/gson/GsonUtils.java
new file mode 100644
index 0000000..1b224fc
--- /dev/null
+++ b/otf-service-api/src/main/java/org/oran/otf/common/utility/gson/GsonUtils.java
@@ -0,0 +1,69 @@
+/*  Copyright (c) 2019 AT&T Intellectual Property.                             #

+#                                                                              #

+#   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.oran.otf.common.utility.gson;

+

+import com.google.gson.Gson;

+import com.google.gson.GsonBuilder;

+import com.google.gson.JsonDeserializationContext;

+import com.google.gson.JsonDeserializer;

+import com.google.gson.JsonElement;

+import com.google.gson.JsonParseException;

+import com.google.gson.JsonPrimitive;

+import com.google.gson.JsonSerializationContext;

+import com.google.gson.JsonSerializer;

+import com.google.gson.reflect.TypeToken;

+import java.lang.reflect.Type;

+import java.util.HashMap;

+import java.util.Map;

+import org.bson.types.ObjectId;

+

+public class GsonUtils {

+    private static final GsonBuilder gsonBuilder =

+            new GsonBuilder().setDateFormat("yyyy-MM-dd'T'HH:mm:ssZ")

+                    .registerTypeAdapter(ObjectId.class, new JsonSerializer<ObjectId>() {

+                        @Override

+                        public JsonElement serialize(ObjectId src, Type typeOfSrc,

+                                                     JsonSerializationContext context) {

+                            return new JsonPrimitive(src.toHexString());

+                        }

+                    }).registerTypeAdapter(ObjectId.class, new JsonDeserializer<ObjectId>() {

+                @Override

+                public ObjectId deserialize(JsonElement json, Type typeOfT,

+                                            JsonDeserializationContext context) throws JsonParseException {

+                    return new ObjectId(json.getAsString());

+                }

+            });

+

+    public static Gson getGson() {

+        return gsonBuilder.create();

+    }

+

+    private static final Gson gson = getGson();

+    private static final Type TT_mapStringString = new TypeToken<Map<String,String>>(){}.getType();

+

+    public static Map<String, String> jsonToMapStringString(String json) {

+        Map<String, String> ret = new HashMap<String, String>();

+        if (json == null || json.isEmpty())

+            return ret;

+        return gson.fromJson(json, TT_mapStringString);

+    }

+    public static String mapStringObjectToJson(Map<String, Object> map) {

+        if (map == null)

+            map = new HashMap<String, Object>();

+        return gson.toJson(map);

+    }

+}
\ No newline at end of file
diff --git a/otf-service-api/src/main/java/org/oran/otf/common/utility/http/RequestUtility.java b/otf-service-api/src/main/java/org/oran/otf/common/utility/http/RequestUtility.java
new file mode 100644
index 0000000..2af3f90
--- /dev/null
+++ b/otf-service-api/src/main/java/org/oran/otf/common/utility/http/RequestUtility.java
@@ -0,0 +1,160 @@
+/*  Copyright (c) 2019 AT&T Intellectual Property.                             #

+#                                                                              #

+#   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.oran.otf.common.utility.http;

+

+import com.google.common.base.Strings;

+import java.io.IOException;

+import java.io.UnsupportedEncodingException;

+import java.util.HashMap;

+import java.util.Map;

+import java.util.Timer;

+import java.util.TimerTask;

+import java.util.concurrent.ExecutionException;

+import java.util.concurrent.Future;

+import org.apache.http.HttpResponse;

+import org.apache.http.client.methods.HttpGet;

+import org.apache.http.client.methods.HttpPost;

+import org.apache.http.client.methods.HttpRequestBase;

+import org.apache.http.entity.StringEntity;

+import org.apache.http.impl.nio.client.CloseableHttpAsyncClient;

+import org.apache.http.impl.nio.client.HttpAsyncClients;

+import org.slf4j.Logger;

+import org.slf4j.LoggerFactory;

+

+public class RequestUtility {

+

+  private static Logger logger = LoggerFactory.getLogger(RequestUtility.class);

+

+  public static void postAsync(String url, String body, Map<String, String> headers)

+      throws IOException, InterruptedException, ExecutionException {

+    HttpPost post = buildPost(url, body, headers);

+    executeAsync(post);

+  }

+

+  public static HttpResponse postSync(String url, String body, Map<String, String> headers)

+      throws IOException, InterruptedException, ExecutionException {

+    HttpPost post = buildPost(url, body, headers);

+    return executeSync(post);

+  }

+

+  public static HttpResponse postSync(

+      String url, String body, Map<String, String> headers, int timeoutInMillis)

+      throws IOException, InterruptedException, ExecutionException {

+    HttpPost post = buildPost(url, body, headers);

+    return executeSync(post, timeoutInMillis);

+  }

+

+  public static HttpResponse getSync(String url, Map<String, String> headers)

+      throws IOException, InterruptedException, ExecutionException {

+    HttpGet get = buildGet(url, headers);

+    return executeSync(get);

+  }

+

+  public static HttpResponse getSync(String url, Map<String, String> headers, int timeoutInMillis)

+      throws IOException, InterruptedException, ExecutionException {

+    if (timeoutInMillis < 0) {

+      throw new IllegalArgumentException("The timeoutInMillis must be a value greater than 0.");

+    }

+

+    HttpGet get = buildGet(url, headers);

+    return executeSync(get, timeoutInMillis);

+  }

+

+  public static void getAsync(String url, Map<String, String> headers) throws IOException {

+    HttpGet get = buildGet(url, headers);

+    executeAsync(get);

+  }

+

+  private static HttpPost buildPost(String url, String body, Map<String, String> headers)

+      throws UnsupportedEncodingException {

+    if (Strings.isNullOrEmpty(url) || Strings.isNullOrEmpty(body)) {

+      return null;

+    } else if (headers == null) {

+      headers = new HashMap<>();

+    }

+

+    HttpPost post = new HttpPost(url);

+    headers.forEach(post::setHeader);

+    post.setEntity(new StringEntity(body));

+    return post;

+  }

+

+  private static HttpGet buildGet(String url, Map<String, String> headers) {

+    if (Strings.isNullOrEmpty(url)) {

+      return null;

+    } else if (headers == null) {

+      headers = new HashMap<>();

+    }

+

+    HttpGet get = new HttpGet(url);

+    headers.forEach(get::setHeader);

+    return get;

+  }

+

+  private static HttpResponse executeSync(HttpRequestBase request)

+      throws IOException, InterruptedException, ExecutionException {

+    CloseableHttpAsyncClient httpClient = HttpAsyncClients.createDefault();

+    try {

+      httpClient.start();

+      Future<HttpResponse> future = httpClient.execute(request, null);

+      return future.get();

+    } finally {

+      httpClient.close();

+    }

+  }

+

+  private static HttpResponse executeSync(HttpRequestBase request, int timeoutInMillis)

+      throws IOException, InterruptedException, ExecutionException {

+    if (timeoutInMillis < 0) {

+      throw new IllegalArgumentException("The timeoutInMillis must be a value greater than 0.");

+    }

+

+    // Create a timer task that will abort the task (the request) after the specified time. This

+    // task will run *timeoutInMillis* ms

+    TimerTask task =

+        new TimerTask() {

+          @Override

+          public void run() {

+            if (request != null) {

+              request.abort();

+            }

+          }

+        };

+

+    CloseableHttpAsyncClient httpClient = HttpAsyncClients.createDefault();

+    try {

+      httpClient.start();

+      // Start the timer before making the request.

+      new Timer(true).schedule(task, timeoutInMillis);

+      Future<HttpResponse> future = httpClient.execute(request, null);

+      return future.get();

+    } finally {

+      httpClient.close();

+    }

+  }

+

+  private static void executeAsync(HttpRequestBase request) throws IOException {

+    CloseableHttpAsyncClient httpClient = HttpAsyncClients.createDefault();

+    try {

+      httpClient.start();

+      httpClient.execute(request, null);

+      logger.debug("Sent asynchronous request.");

+    } finally {

+      httpClient.close();

+    }

+  }

+}

diff --git a/otf-service-api/src/main/java/org/oran/otf/common/utility/http/ResponseUtility.java b/otf-service-api/src/main/java/org/oran/otf/common/utility/http/ResponseUtility.java
new file mode 100644
index 0000000..919897c
--- /dev/null
+++ b/otf-service-api/src/main/java/org/oran/otf/common/utility/http/ResponseUtility.java
@@ -0,0 +1,107 @@
+/*  Copyright (c) 2019 AT&T Intellectual Property.                             #

+#                                                                              #

+#   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.oran.otf.common.utility.http;

+

+import org.oran.otf.common.model.local.OTFApiResponse;

+import javax.ws.rs.core.MediaType;

+import javax.ws.rs.core.Response;

+

+public class ResponseUtility {

+

+  public static class Build {

+

+    public static Response okRequest() {

+      return Response.ok().build();

+    }

+

+    public static Response badRequest() {

+      return Response.status(400).build();

+    }

+

+    public static Response okRequestWithMessage(String msg) {

+      return Response.status(200)

+          .type(MediaType.APPLICATION_JSON)

+          .entity(new OTFApiResponse(200, msg))

+          .build();

+    }

+

+    public static Response okRequestWithObject(Object obj) {

+      return Response.status(200).type(MediaType.APPLICATION_JSON).entity(obj).build();

+    }

+

+    public static Response badRequestWithMessage(String msg) {

+      return Response.status(400)

+          .type(MediaType.APPLICATION_JSON)

+          .entity(new OTFApiResponse(400, msg))

+          .build();

+    }

+

+    public static Response internalServerError() {

+      return Response.status(500).build();

+    }

+

+    public static Response internalServerErrorWithMessage(String msg) {

+      return Response.status(500)

+          .type(MediaType.APPLICATION_JSON)

+          .entity(new OTFApiResponse(500, msg))

+          .build();

+    }

+

+    public static Response unauthorized() {

+      return Response.status(401).build();

+    }

+    public static Response unauthorizedWithMessage(String msg) {

+      return Response.status(401)

+          .type(MediaType.APPLICATION_JSON)

+          .entity(new OTFApiResponse(401, msg))

+          .build();

+    }

+

+    public static Response notFoundWithMessage(String msg) {

+      return Response.status(404)

+          .type(MediaType.APPLICATION_JSON)

+          .entity(new OTFApiResponse(404, msg))

+          .build();

+    }

+

+    public static Response serviceUnavailableWithMessage(String msg) {

+      return Response.status(503)

+          .type(MediaType.APPLICATION_JSON)

+          .entity(new OTFApiResponse(503, msg))

+          .build();

+    }

+

+    public static Response serviceUnavailable() {

+      return Response.status(503).build();

+    }

+

+    public static Response genericWithCode(int code) {

+      return Response.status(code).build();

+    }

+

+    public static Response genericWithMessage(int code, String msg) {

+      return Response.status(code)

+          .type(MediaType.APPLICATION_JSON)

+          .entity(new OTFApiResponse(code, msg))

+          .build();

+    }

+

+    public static Response genericWithObject(int code, Object obj) {

+      return Response.status(code).type(MediaType.APPLICATION_JSON).entity(obj).build();

+    }

+  }

+}

diff --git a/otf-service-api/src/main/java/org/oran/otf/common/utility/logger/ErrorCode.java b/otf-service-api/src/main/java/org/oran/otf/common/utility/logger/ErrorCode.java
new file mode 100644
index 0000000..8327a81
--- /dev/null
+++ b/otf-service-api/src/main/java/org/oran/otf/common/utility/logger/ErrorCode.java
@@ -0,0 +1,36 @@
+/*  Copyright (c) 2019 AT&T Intellectual Property.                             #

+#                                                                              #

+#   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.oran.otf.common.utility.logger;

+

+public enum ErrorCode {

+  PermissionError(100),

+  AvailabilityError(200),

+  DataError(300),

+  SchemaError(400),

+  BusinessProcesssError(500),

+  UnknownError(900);

+

+  private int value;

+

+  ErrorCode(int value) {

+    this.value = value;

+  }

+

+  public int getValue() {

+    return this.value;

+  }

+}

diff --git a/otf-service-api/src/main/java/org/oran/otf/common/utility/logger/LoggerStartupListener.java b/otf-service-api/src/main/java/org/oran/otf/common/utility/logger/LoggerStartupListener.java
new file mode 100644
index 0000000..10c45d8
--- /dev/null
+++ b/otf-service-api/src/main/java/org/oran/otf/common/utility/logger/LoggerStartupListener.java
@@ -0,0 +1,91 @@
+/*-

+ * ============LICENSE_START=======================================================

+ * ONAP - SO

+ * ================================================================================

+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.

+ * ================================================================================

+ * Modifications Copyright (c) 2019 Samsung

+ * ================================================================================

+ * 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=========================================================

+ */

+

+package org.oran.otf.common.utility.logger;

+

+import ch.qos.logback.classic.Level;

+import ch.qos.logback.classic.LoggerContext;

+import ch.qos.logback.classic.spi.LoggerContextListener;

+import ch.qos.logback.core.Context;

+import ch.qos.logback.core.spi.ContextAwareBase;

+import ch.qos.logback.core.spi.LifeCycle;

+import java.net.InetAddress;

+import java.net.UnknownHostException;

+import org.slf4j.Logger;

+import org.slf4j.LoggerFactory;

+import org.springframework.stereotype.Component;

+

+@Component

+public class LoggerStartupListener extends ContextAwareBase

+    implements LoggerContextListener, LifeCycle {

+

+  private static final Logger logger = LoggerFactory.getLogger(LoggerStartupListener.class);

+  private boolean started = false;

+

+  @Override

+  public void start() {

+    if (started) {

+      return;

+    }

+    InetAddress addr = null;

+    try {

+      addr = InetAddress.getLocalHost();

+    } catch (UnknownHostException e) {

+      logger.error("UnknownHostException", e);

+    }

+    Context context = getContext();

+    if (addr != null) {

+      context.putProperty("server.name", addr.getHostName());

+    }

+    started = true;

+  }

+

+  @Override

+  public void stop() {

+  }

+

+  @Override

+  public boolean isStarted() {

+    return started;

+  }

+

+  @Override

+  public boolean isResetResistant() {

+    return true;

+  }

+

+  @Override

+  public void onReset(LoggerContext arg0) {

+  }

+

+  @Override

+  public void onStart(LoggerContext arg0) {

+  }

+

+  @Override

+  public void onStop(LoggerContext arg0) {

+  }

+

+  @Override

+  public void onLevelChange(ch.qos.logback.classic.Logger logger, Level level) {

+  }

+}

diff --git a/otf-service-api/src/main/java/org/oran/otf/common/utility/logger/MessageEnum.java b/otf-service-api/src/main/java/org/oran/otf/common/utility/logger/MessageEnum.java
new file mode 100644
index 0000000..1103c53
--- /dev/null
+++ b/otf-service-api/src/main/java/org/oran/otf/common/utility/logger/MessageEnum.java
@@ -0,0 +1,35 @@
+/*-

+ * ============LICENSE_START=======================================================

+ * ONAP - SO

+ * ================================================================================

+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.

+ * ================================================================================

+ * Licensed under the Apache License, Version 2.0 (the "License");

+ * you may not use this file except in compliance with the License.

+ * You may obtain a copy of the License at

+ *

+ *      http://www.apache.org/licenses/LICENSE-2.0

+ *

+ * Unless required by applicable law or agreed to in writing, software

+ * distributed under the License is distributed on an "AS IS" BASIS,

+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.

+ * See the License for the specific language governing permissions and

+ * limitations under the License.

+ * ============LICENSE_END=========================================================

+ */

+

+package org.oran.otf.common.utility.logger;

+

+

+public enum MessageEnum {

+  // Api Handler Messages

+  APIH_REQUEST_NULL, APIH_QUERY_FOUND, APIH_QUERY_NOT_FOUND, APIH_QUERY_PARAM_WRONG, APIH_DB_ACCESS_EXC, APIH_DB_ACCESS_EXC_REASON, APIH_VALIDATION_ERROR, APIH_REQUEST_VALIDATION_ERROR, APIH_SERVICE_VALIDATION_ERROR, APIH_GENERAL_EXCEPTION_ARG, APIH_GENERAL_EXCEPTION, APIH_GENERAL_WARNING, APIH_AUDIT_EXEC, APIH_GENERAL_METRICS, APIH_DUPLICATE_CHECK_EXC, APIH_DUPLICATE_FOUND, APIH_BAD_ORDER, APIH_DB_ATTRIBUTE_NOT_FOUND, APIH_BPEL_COMMUNICATE_ERROR, APIH_BPEL_RESPONSE_ERROR, APIH_WARP_REQUEST, APIH_ERROR_FROM_BPEL_SERVER, APIH_DB_INSERT_EXC, APIH_DB_UPDATE_EXC, APIH_NO_PROPERTIES, APIH_PROPERTY_LOAD_SUC, APIH_LOAD_PROPERTIES_FAIL, APIH_SDNC_COMMUNICATE_ERROR, APIH_SDNC_RESPONSE_ERROR, APIH_CANNOT_READ_SCHEMA, APIH_HEALTH_CHECK_EXCEPTION, APIH_REQUEST_VALIDATION_ERROR_REASON, APIH_JAXB_MARSH_ERROR, APIH_JAXB_UNMARSH_ERROR, APIH_VNFREQUEST_VALIDATION_ERROR, APIH_DOM2STR_ERROR, APIH_READ_VNFOUTPUT_CLOB_EXCEPTION, APIH_DUPLICATE_CHECK_EXC_ATT, APIH_GENERATED_REQUEST_ID, APIH_GENERATED_SERVICE_INSTANCE_ID, APIH_REPLACE_REQUEST_ID,

+  // Resource Adapter Messages

+  RA_GENERAL_EXCEPTION_ARG, RA_GENERAL_EXCEPTION, RA_GENERAL_WARNING, RA_MISSING_PARAM, RA_AUDIT_EXEC, RA_GENERAL_METRICS, RA_CREATE_STACK_TIMEOUT, RA_DELETE_STACK_TIMEOUT, RA_UPDATE_STACK_TIMEOUT, RA_CONNECTION_EXCEPTION, RA_PARSING_ERROR, RA_PROPERTIES_NOT_FOUND, RA_LOAD_PROPERTIES_SUC, RA_NETWORK_ALREADY_EXIST, RA_UPDATE_NETWORK_ERR, RA_CREATE_STACK_ERR, RA_UPDATE_STACK_ERR, RA_CREATE_TENANT_ERR, RA_NETWORK_NOT_FOUND, RA_NETWORK_ORCHE_MODE_NOT_SUPPORT, RA_CREATE_NETWORK_EXC, RA_NS_EXC, RA_PARAM_NOT_FOUND, RA_CONFIG_EXC, RA_UNKOWN_PARAM, RA_VLAN_PARSE, RA_DELETE_NETWORK_EXC, RA_ROLLBACK_NULL, RA_TENANT_NOT_FOUND, RA_QUERY_NETWORK_EXC, RA_CREATE_NETWORK_NOTIF_EXC, RA_ASYNC_ROLLBACK, RA_WSDL_NOT_FOUND, RA_WSDL_URL_CONVENTION_EXC, RA_INIT_NOTIF_EXC, RA_SET_CALLBACK_AUTH_EXC, RA_FAULT_INFO_EXC, RA_MARSHING_ERROR, RA_PARSING_REQUEST_ERROR, RA_SEND_REQUEST_SDNC, RA_RESPONSE_FROM_SDNC, RA_EXCEPTION_COMMUNICATE_SDNC, RA_EVALUATE_XPATH_ERROR, RA_ANALYZE_ERROR_EXC, RA_ERROR_GET_RESPONSE_SDNC, RA_CALLBACK_BPEL, RA_INIT_CALLBACK_WSDL_ERR, RA_CALLBACK_BPEL_EXC, RA_CALLBACK_BPEL_COMPLETE, RA_SDNC_MISS_CONFIG_PARAM, RA_SDNC_INVALID_CONFIG, RA_PRINT_URL, RA_ERROR_CREATE_SDNC_REQUEST, RA_ERROR_CREATE_SDNC_RESPONSE, RA_ERROR_CONVERT_XML2STR, RA_RECEIVE_SDNC_NOTIF, RA_INIT_SDNC_ADAPTER, RA_SEND_REQUEST_APPC_ERR, RA_SEND_REQUEST_SDNC_ERR, RA_RECEIVE_BPEL_REQUEST, RA_TENANT_ALREADY_EXIST, RA_UPDATE_TENANT_ERR, RA_DELETE_TEMAMT_ERR, RA_ROLLBACK_TENANT_ERR, RA_QUERY_VNF_ERR, RA_VNF_ALREADY_EXIST, RA_VNF_UNKNOWN_PARAM, RA_VNF_EXTRA_PARAM, RA_CREATE_VNF_ERR, RA_VNF_NOT_EXIST, RA_UPDATE_VNF_ERR, RA_DELETE_VNF_ERR, RA_ASYNC_CREATE_VNF, RA_SEND_VNF_NOTIF_ERR, RA_ASYNC_CREATE_VNF_COMPLETE, RA_ASYNC_UPDATE_VNF, RA_ASYNC_UPDATE_VNF_COMPLETE, RA_ASYNC_QUERY_VNF, RA_ASYNC_QUERY_VNF_COMPLETE, RA_ASYNC_DELETE_VNF, RA_ASYNC_DELETE_VNF_COMPLETE, RA_ASYNC_ROLLBACK_VNF, RA_ASYNC_ROLLBACK_VNF_COMPLETE, RA_ROLLBACK_VNF_ERR, RA_DB_INVALID_STATUS, RA_CANT_UPDATE_REQUEST, RA_DB_REQUEST_NOT_EXIST, RA_CONFIG_NOT_FOUND, RA_CONFIG_LOAD, RA_RECEIVE_WORKFLOW_MESSAGE,

+  // BPEL engine Messages

+  BPMN_GENERAL_INFO, BPMN_GENERAL_EXCEPTION_ARG, BPMN_GENERAL_EXCEPTION, BPMN_GENERAL_WARNING, BPMN_AUDIT_EXEC, BPMN_GENERAL_METRICS, BPMN_URN_MAPPING_FAIL, BPMN_VARIABLE_NULL, BPMN_CALLBACK_EXCEPTION,

+  // ASDC Messages

+  ASDC_GENERAL_EXCEPTION_ARG, ASDC_GENERAL_EXCEPTION, ASDC_GENERAL_WARNING, ASDC_GENERAL_INFO, ASDC_AUDIT_EXEC, ASDC_GENERAL_METRICS, ASDC_CREATE_SERVICE, ASDC_ARTIFACT_ALREADY_DEPLOYED, ASDC_CREATE_ARTIFACT, ASDC_ARTIFACT_INSTALL_EXC, ASDC_ARTIFACT_ALREADY_DEPLOYED_DETAIL, ASDC_ARTIFACT_NOT_DEPLOYED_DETAIL, ASDC_ARTIFACT_CHECK_EXC, ASDC_INIT_ASDC_CLIENT_EXC, ASDC_INIT_ASDC_CLIENT_SUC, ASDC_LOAD_ASDC_CLIENT_EXC, ASDC_SINGLETON_CHECKT_EXC, ASDC_SHUTDOWN_ASDC_CLIENT_EXC, ASDC_CHECK_HEAT_TEMPLATE, ASDC_START_INSTALL_ARTIFACT, ASDC_ARTIFACT_TYPE_NOT_SUPPORT, ASDC_ARTIFACT_ALREADY_EXIST, ASDC_ARTIFACT_DOWNLOAD_SUC, ASDC_ARTIFACT_DOWNLOAD_FAIL, ASDC_START_DEPLOY_ARTIFACT, ASDC_SEND_NOTIF_ASDC, ASDC_SEND_NOTIF_ASDC_EXEC, ASDC_RECEIVE_CALLBACK_NOTIF, ASDC_RECEIVE_SERVICE_NOTIF, ASDC_ARTIFACT_NULL, ASDC_SERVICE_NOT_SUPPORT, ASDC_ARTIFACT_DEPLOY_SUC, ASDC_PROPERTIES_NOT_FOUND, ASDC_PROPERTIES_LOAD_SUCCESS,

+  // Default Messages, in case Log catalog is not defined

+  GENERAL_EXCEPTION_ARG, GENERAL_EXCEPTION, GENERAL_WARNING, AUDIT_EXEC, GENERAL_METRICS, LOGGER_SETUP, LOGGER_NOT_FOUND, LOGGER_UPDATE_SUC, LOGGER_UPDATE_DEBUG, LOGGER_UPDATE_DEBUG_SUC, LOAD_PROPERTIES_SUC, NO_PROPERTIES, MADATORY_PARAM_MISSING, LOAD_PROPERTIES_FAIL, INIT_LOGGER, INIT_LOGGER_FAIL, JAXB_EXCEPTION, IDENTITY_SERVICE_NOT_FOUND

+}

diff --git a/otf-service-api/src/main/java/org/oran/otf/common/utility/permissions/PermissionChecker.java b/otf-service-api/src/main/java/org/oran/otf/common/utility/permissions/PermissionChecker.java
new file mode 100644
index 0000000..e1749bb
--- /dev/null
+++ b/otf-service-api/src/main/java/org/oran/otf/common/utility/permissions/PermissionChecker.java
@@ -0,0 +1,57 @@
+/*  Copyright (c) 2019 AT&T Intellectual Property.                             #

+#                                                                              #

+#   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.oran.otf.common.utility.permissions;

+

+import org.oran.otf.common.model.Group;

+import org.oran.otf.common.model.User;

+import org.oran.otf.common.repository.GroupRepository;

+

+import java.util.Collection;

+

+public class PermissionChecker {

+    //check is a user have a certain permission in a group

+    public static boolean hasPermissionTo(User user,Group group,String permission, GroupRepository groupRepository){

+        UserPermission userPermission = new PermissionUtil().buildUserPermission(user,groupRepository);

+        return hasPermissionTo(userPermission,group,permission);

+    }

+    public static boolean hasPermissionTo(User user, Group group, Collection<String> permissions, GroupRepository groupRepository){

+        UserPermission userPermission = new PermissionUtil().buildUserPermission(user,groupRepository);

+        for(String permission : permissions){

+            if(!hasPermissionTo(userPermission,group,permission)){

+                return false;

+            }

+        }

+        return true;

+    }

+    // check a users list of permission in a group

+    private static boolean hasPermissionTo(UserPermission userPermission, Group group,String permission){

+        switch (permission.toUpperCase()) {

+            case (UserPermission.Permission.READ):

+                return userPermission.hasAccessTo(group.get_id().toString(),UserPermission.Permission.READ);

+            case (UserPermission.Permission.WRITE):

+                return userPermission.hasAccessTo(group.get_id().toString(),UserPermission.Permission.WRITE);

+            case (UserPermission.Permission.EXECUTE):

+                return userPermission.hasAccessTo(group.get_id().toString(),UserPermission.Permission.EXECUTE);

+            case (UserPermission.Permission.DELETE):

+                return userPermission.hasAccessTo(group.get_id().toString(),UserPermission.Permission.DELETE);

+            case (UserPermission.Permission.MANAGEMENT):

+                return userPermission.hasAccessTo(group.get_id().toString(),UserPermission.Permission.MANAGEMENT);

+            default:

+                return false;// reaches here when permission provided is not an option

+        }

+    }

+}
\ No newline at end of file
diff --git a/otf-service-api/src/main/java/org/oran/otf/common/utility/permissions/PermissionUtil.java b/otf-service-api/src/main/java/org/oran/otf/common/utility/permissions/PermissionUtil.java
new file mode 100644
index 0000000..e8cdfea
--- /dev/null
+++ b/otf-service-api/src/main/java/org/oran/otf/common/utility/permissions/PermissionUtil.java
@@ -0,0 +1,237 @@
+/*  Copyright (c) 2019 AT&T Intellectual Property.                             #

+#                                                                              #

+#   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.oran.otf.common.utility.permissions;

+

+import org.oran.otf.common.model.Group;

+import org.oran.otf.common.model.GroupMember;

+import org.oran.otf.common.model.Role;

+import org.oran.otf.common.model.User;

+import org.oran.otf.common.repository.GroupRepository;

+

+import java.util.*;

+

+public class PermissionUtil {

+    //build userPermission object which contains all access control information of the user

+    public UserPermission buildUserPermission(User user, GroupRepository groupRepository) {

+        UserPermission userPermission = new UserPermission();

+        userPermission.setUser(user);

+        Map<String,Set<String>> userAccessMap; // map from group to permission that user have in that group

+

+        userAccessMap = mapGroupsToPermission(user,groupRepository);

+        userPermission.setUserAccessMap(userAccessMap);

+        return userPermission;

+    }

+    // return if user have specified permission in a certain group

+    // ***********only use this groups that the user is in directly (non-child and non parents)****************

+    public static boolean hasPermissionTo (String permission,User user, Group group) {

+        Set<String> possiblePermissions= getUserGroupPermissions(user,group);

+        return possiblePermissions.stream().anyMatch(p-> p.equalsIgnoreCase(permission)); //

+    }

+    // Get all the permissions the user have in a certain group

+    public static Set<String> getUserGroupPermissions(User user, Group group){

+        Set<String> permissionsAllowed = new HashSet<>();

+        Set<String> usersAssignedRoles = findUserRoles(user,group);

+        if(usersAssignedRoles.isEmpty()) // empty set permissions because the user have no roles in the group aka not a member

+            return permissionsAllowed;

+        //get every single permissions for each role that the user have.

+        for(String role : usersAssignedRoles){

+             permissionsAllowed.addAll(getRolePermissions(role,group));

+        }

+        return permissionsAllowed;

+    }

+    //get the permissions associated with the userRoleName in group

+    public static Set<String> getRolePermissions(String userRoleName,Group group)

+    {

+        for(Role role : group.getRoles())

+        {

+            if(role.getRoleName().equalsIgnoreCase(userRoleName))

+            {

+                return new HashSet<String>(role.getPermissions());

+            }

+        }

+        return new HashSet<String>(); // empty string set if the role name cant be found in the group

+    }

+    // find the user's role in the specified group

+    public static Set<String> findUserRoles(User user,Group group){

+        for(GroupMember member : group.getMembers())

+        {

+            // if userId matches then get all the user's role in the group

+            if(member.getUserId().toString().equals(user.get_id().toString()))

+                return new HashSet<String>(member.getRoles());

+        }

+        return new HashSet<String>(); //if user have no roles

+    }

+    // create map that where key is the group id and value = users permission (string) that that group

+    private Map<String,Set<String>> mapGroupsToPermission(User user, GroupRepository groupRepository){

+        Map<String,Set<String>> groupAccessMap = new HashMap<>();

+        List<Group> enrolledGroups = groupRepository.findAllByMembersId(user.get_id());// enrolledGroups = groups that user is a member of

+        Map<String,Group> allGroupMap = groupListToMap(groupRepository.findAll());

+        // get all permission in the groups the user is ia member of

+        for(Group group: enrolledGroups) {

+            Set<String> permissions = getUserGroupPermissions(user,group);

+            groupAccessMap.put(group.get_id().toString(),convertPermissions(permissions));

+        }

+        //assign add read to all parent groups

+        Set<String> parentGroupsId = getParentGroups(enrolledGroups,allGroupMap);

+        for(String parentId : parentGroupsId)

+        {

+            // if parent access role already exist in

+            // group access map cause they are a member

+            if(groupAccessMap.get(parentId)!= null)

+                groupAccessMap.get(parentId).add(UserPermission.Permission.READ);

+            else

+                groupAccessMap.put(parentId,new HashSet<String>(Arrays.asList(UserPermission.Permission.READ)));

+        }

+        // if there is management role

+        // then assign read access to children

+        if(hasManagementRole(user,enrolledGroups)){

+//            Set<String>childIds = getChildrenGroupsId(enrolledGroups,allGroupMap,user);

+            for(Group enrolledGroup : enrolledGroups) {

+                // if enrolled groups is a management group

+                if(hasPermissionTo(UserPermission.Permission.MANAGEMENT,user,enrolledGroup)){

+                    // if there is management role then get all the child of that group, do this for all management groups

+                    Set<String> childIds= getChildrenGroupsId(Arrays.asList(enrolledGroup),allGroupMap,user);

+                    Set<String> userGroupPermissions = convertPermissions(getUserGroupPermissions(user,enrolledGroup));

+                    for(String childId : childIds){

+                        if (groupAccessMap.get(childId) != null)

+                            groupAccessMap.get(childId).addAll(userGroupPermissions);

+                        else{

+                            groupAccessMap.put(childId,userGroupPermissions);

+                        }

+                    }

+                }

+            }

+        }

+        return groupAccessMap;

+    }

+    // check is user have managementRole

+    private boolean hasManagementRole(User user, List<Group> enrolledGroups)

+    {

+        for(Group group: enrolledGroups){

+            if(hasPermissionTo(UserPermission.Permission.MANAGEMENT,user,group))

+            {

+                return true;

+            }

+        }

+        return false;

+    }

+    // get the parent groups starting from the enrolled group of the user

+    private Set<String> getParentGroups(List<Group> enrolledGroup,Map<String,Group> groupMap )

+    {

+        Set<String> parentGroups = new HashSet<>();

+        return lookUp(enrolledGroup,groupMap,parentGroups);

+    }

+    //recursive lookup starting at the enrolled groups that the user is a member of

+    private Set<String> lookUp(List<Group> groupsToCheck, Map<String,Group> groupMap,Set<String> resultSet)

+    {

+        //base case: nothing to check anymore

+        if(groupsToCheck.isEmpty())

+            return resultSet;

+        //This is the parents directly above the current groups that are being checked

+        List<Group> currentParentGroups = new ArrayList<>();

+

+        for(Group group : groupsToCheck)

+        {

+            if(group.getParentGroupId() != null) // if there is a parent

+            {

+                String parentId = group.getParentGroupId().toString();

+                Group parentGroup = groupMap.get(parentId);

+                resultSet.add(parentId);

+                currentParentGroups.add(parentGroup); // add to currentParentGroup so it can be used recursively check for more parents

+            }

+        }

+        return lookUp(currentParentGroups,groupMap,resultSet);

+    }

+    // convert a list of groups to a map of group ids to group

+    private Map<String,Group> groupListToMap(List<Group> allGroups)

+    {

+        Map<String,Group> groupMap = new HashMap<>();

+        allGroups.forEach(group -> groupMap.put(group.get_id().toString(),group));

+        return groupMap;

+    }

+    //get all the child group

+    private Set<String> getChildrenGroupsId(List<Group> enrolledGroup, Map<String,Group> allGroupsMap, User user)

+    {

+        Set<String> childrenGroups = new HashSet<>();

+        Set<String> managementGroupIds = getManagementGroupIds(enrolledGroup,user);

+        return  lookForChildren(managementGroupIds,allGroupsMap,childrenGroups);

+    }

+

+    private Set<String> getManagementGroupIds(List<Group> enrolledGroups,User user)

+    {

+        Set<String> parentIds = new HashSet<>();

+        for(Group group: enrolledGroups)

+        {

+            if(hasPermissionTo(UserPermission.Permission.MANAGEMENT,user,group)) // has Management permission

+            {

+                parentIds.add(group.get_id().toString());

+            }

+        }

+        return parentIds;

+    }

+    //recursive look down for childrens via breath first search

+    private Set<String> lookForChildren (Set<String> parentIds, Map<String,Group> allGroupsMap, Set<String> resultSet)

+    {

+        //base case = no groups to check anymore;

+        if (parentIds.isEmpty())

+            return resultSet;

+

+        Set<String> currentChildrenIds = new HashSet<>();

+        for(String groupId : allGroupsMap.keySet())

+        {

+            Group possibleChildGroup = allGroupsMap.get(groupId);

+            if(isChildOf(parentIds,possibleChildGroup)) // if parent id is the same

+            {

+                currentChildrenIds.add(groupId);

+                resultSet.add(groupId);

+            }

+        }

+        return lookForChildren(currentChildrenIds,allGroupsMap,resultSet);

+    }

+    //check if a group is a child of a list of parent group ids

+    private boolean isChildOf(Set<String>parentGroupIds, Group childGroup){

+        for(String parentId: parentGroupIds)

+        {

+            if(isChildOf(parentId,childGroup))

+                return true;

+        }

+        return false;

+    }

+    //check is group has parent that is specified by parentId

+    private boolean isChildOf(String parentId,Group childGroup) {

+        if(childGroup.getParentGroupId() == null)

+            return false;

+       return childGroup.getParentGroupId().toString().equals(parentId);

+    }

+

+    private Set<String> convertPermissions (Set<String> permissions){

+        Set<String> result = new HashSet<>();

+        for (String permission: permissions){

+            if(permission.equalsIgnoreCase(UserPermission.Permission.READ))

+                result.add(UserPermission.Permission.READ);

+            else if (permission.equalsIgnoreCase(UserPermission.Permission.WRITE))

+                result.add(UserPermission.Permission.WRITE);

+            else if (permission.equalsIgnoreCase(UserPermission.Permission.DELETE))

+                result.add(UserPermission.Permission.DELETE);

+            else if (permission.equalsIgnoreCase(UserPermission.Permission.EXECUTE))

+                result.add(UserPermission.Permission.EXECUTE);

+            else if (permission.equalsIgnoreCase(UserPermission.Permission.MANAGEMENT))

+                result.add(UserPermission.Permission.MANAGEMENT);

+        }

+            return result;

+    }

+}

diff --git a/otf-service-api/src/main/java/org/oran/otf/common/utility/permissions/UserPermission.java b/otf-service-api/src/main/java/org/oran/otf/common/utility/permissions/UserPermission.java
new file mode 100644
index 0000000..1883721
--- /dev/null
+++ b/otf-service-api/src/main/java/org/oran/otf/common/utility/permissions/UserPermission.java
@@ -0,0 +1,58 @@
+/*  Copyright (c) 2019 AT&T Intellectual Property.                             #

+#                                                                              #

+#   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.oran.otf.common.utility.permissions;

+

+import org.oran.otf.common.model.User;

+

+import java.util.Map;

+import java.util.Set;

+

+public class UserPermission {

+    private User user;

+    private Map<String,Set<String>> userAccessMap;

+

+    public User getUser() {

+        return user;

+    }

+

+    public void setUser(User user) {

+        this.user = user;

+    }

+

+    public Map<String, Set<String>> getUserAccessMap() {

+        return userAccessMap;

+    }

+

+    public void setUserAccessMap(Map<String,Set<String>> userAccessMap) {

+        this.userAccessMap = userAccessMap;

+    }

+

+    public boolean  hasAccessTo(String groupId,String permission) {

+        if (userAccessMap.get(groupId) == null) {

+            return false;

+        }

+        Set<String> group = userAccessMap.get(groupId);

+        return group.stream().anyMatch(groupPermission->groupPermission.equalsIgnoreCase(permission));

+    }

+    public class Permission{

+        public static final String READ = "READ";

+        public static final String WRITE = "WRITE";

+        public static final String EXECUTE = "EXECUTE";

+        public static final String DELETE = "DELETE";

+        public static final String MANAGEMENT ="MANAGEMENT";

+    }

+}

diff --git a/otf-service-api/src/main/resources/application.properties b/otf-service-api/src/main/resources/application.properties
new file mode 100644
index 0000000..0a68a60
--- /dev/null
+++ b/otf-service-api/src/main/resources/application.properties
@@ -0,0 +1,50 @@
+# Tomcat

+server.port=8443

+server.port.http=8080

+security.require-ssl=false

+

+server.ssl.key-store-type=PKCS12

+server.ssl.key-store=${OTF_CERT_PATH}

+server.ssl.key-store-password=${OTF_CERT_PASS}

+#server.servlet.context-path=/otf/api

+#spring.jersey.application-path=/otf

+#springfox.documentation.swagger.v2.path=/otf/api/swagger.json

+

+# MongoDB

+otf.mongo.hosts=${OTF_MONGO_HOSTS}

+otf.mongo.username=${OTF_MONGO_USERNAME}

+otf.mongo.password=${OTF_MONGO_PASSWORD}

+otf.mongo.replicaSet=${OTF_MONGO_REPLICASET}

+otf.mongo.database=${OTF_MONGO_DATABASE}

+

+# Jackson

+spring.jackson.default-property-inclusion=always

+

+# Logging

+logging.level.org.springframework.web=DEBUG

+logging.level.org.hibernate=ERROR

+logging.file.max-history=5

+logging.file=otf/logs/serviceapi.log

+logging.path=otf/logs

+

+spring.resources.add-mappings=true

+

+ssl.flag =${https-only.flag:true}

+#springfox.documentation.auto-startup=false

+#springfox.documentation.swagger.v2.path=/otf/swagger.json

+

+#config

+aaf.enabled=true

+aaf.call-timeout=10000

+aaf.conn-timeout=6000

+aaf.default-realm=localhost

+aaf.env=PROD

+aaf.locate-url=https://localhost

+aaf.lur-class=org.onap.aaf.cadi.aaf.v2_0.AAFLurPerm

+aaf.url=https://localhost

+basic-realm=localhost

+basic-warn=true

+cadi-latitude=38.62782

+cadi-longitude=-90.19458

+cadi-protocols=TLSv1.1,TLSv1.2

+cadi-noauthn=/health/v1:/demo/openapi.json
\ No newline at end of file
diff --git a/otf-service-api/src/main/resources/banner.txt b/otf-service-api/src/main/resources/banner.txt
new file mode 100644
index 0000000..544bdea
--- /dev/null
+++ b/otf-service-api/src/main/resources/banner.txt
@@ -0,0 +1,8 @@
+                                                           U  ___ u   _____     _____

+                                                            \/"_ \/  |_ " _|   |" ___|

+                                                            | | | |    | |    U| |_  u

+                                                        .-,_| |_| |   /| |\   \|  _|/

+                                                         \_)-\___/   u |_|U    |_|

+                                                              \\     _// \\_   )(\\,-

+                                                             (__)   (__) (__) (__)(_/

+

diff --git a/otf-service-api/src/main/resources/truststore2018.jks b/otf-service-api/src/main/resources/truststore2018.jks
new file mode 100644
index 0000000..5d52914
--- /dev/null
+++ b/otf-service-api/src/main/resources/truststore2018.jks
Binary files differ
diff --git a/otf-service-api/src/test/java/org/oran/otf/api/tests/config/DataConfig2.java b/otf-service-api/src/test/java/org/oran/otf/api/tests/config/DataConfig2.java
new file mode 100644
index 0000000..f6d9f66
--- /dev/null
+++ b/otf-service-api/src/test/java/org/oran/otf/api/tests/config/DataConfig2.java
@@ -0,0 +1,109 @@
+/*  Copyright (c) 2019 AT&T Intellectual Property.                             #

+#                                                                              #

+#   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.oran.otf.api.tests.config;

+

+import com.mongodb.MongoClient;

+import com.mongodb.MongoClientOptions;

+import com.mongodb.MongoCredential;

+import com.mongodb.ServerAddress;

+import de.flapdoodle.embed.mongo.Command;

+import de.flapdoodle.embed.mongo.MongodExecutable;

+import de.flapdoodle.embed.mongo.MongodStarter;

+import de.flapdoodle.embed.mongo.config.DownloadConfigBuilder;

+import de.flapdoodle.embed.mongo.config.ExtractedArtifactStoreBuilder;

+import de.flapdoodle.embed.mongo.config.IMongodConfig;

+import de.flapdoodle.embed.mongo.config.MongodConfigBuilder;

+import de.flapdoodle.embed.mongo.config.Net;

+import de.flapdoodle.embed.mongo.config.RuntimeConfigBuilder;

+import de.flapdoodle.embed.mongo.distribution.Version;

+import de.flapdoodle.embed.process.config.IRuntimeConfig;

+import de.flapdoodle.embed.process.config.store.HttpProxyFactory;

+import de.flapdoodle.embed.process.runtime.Network;

+import org.springframework.beans.factory.annotation.Value;

+import org.springframework.boot.autoconfigure.AutoConfigureBefore;

+import org.springframework.context.annotation.Bean;

+import org.springframework.context.annotation.Configuration;

+import org.springframework.context.annotation.Profile;

+import org.springframework.data.mongodb.config.AbstractMongoConfiguration;

+import org.springframework.data.mongodb.core.MongoTemplate;

+import org.springframework.data.mongodb.repository.config.EnableMongoRepositories;

+

+

+@Configuration

+@EnableMongoRepositories(basePackages = "org.oran.otf.common.repository")

+@Profile("test")

+public class DataConfig2 extends AbstractMongoConfiguration {

+

+  @Value("${otf.embedded.host}")

+  private String host;

+

+  @Value("${otf.embedded.port}")

+  private int port;

+

+

+  @Value("${otf.embedded.database}")

+  private String database;

+

+  public DataConfig2(){

+  }

+

+  @Override

+  protected String getDatabaseName() {

+    return database;

+  }

+

+  /*

+  @Override

+  public MongoClient mongoClient() {

+    MongoCredential credential = MongoCredential.createScramSha1Credential(username, database, password.toCharArray());

+

+    MongoClientOptions options = MongoClientOptions

+        .builder()

+        .sslEnabled(false)

+        .requiredReplicaSetName(replicaSet)

+        .build();

+

+    String[] hostArray = hosts.split(",");

+    ArrayList<ServerAddress> hosts = new ArrayList<>();

+

+    for (String host : hostArray) {

+      String[] hostSplit = host.split(":");

+      hosts.add(new ServerAddress(hostSplit[0], Integer.parseInt(hostSplit[1])));

+    }

+

+    return new MongoClient(hosts, credential, options);

+  }

+

+  @Override

+  public @Bean

+  MongoTemplate mongoTemplate() {

+    return new MongoTemplate(mongoClient(), database);

+  }

+*/

+

+  @Override

+  public MongoClient mongoClient(){

+    return new MongoClient();

+  }

+

+  @Override

+  public @Bean MongoTemplate mongoTemplate(){

+    return new MongoTemplate(new MongoClient(host, port), "test");

+  }

+

+}

+

diff --git a/otf-service-api/src/test/java/org/oran/otf/api/tests/config/InMemory.java b/otf-service-api/src/test/java/org/oran/otf/api/tests/config/InMemory.java
new file mode 100644
index 0000000..a5243a5
--- /dev/null
+++ b/otf-service-api/src/test/java/org/oran/otf/api/tests/config/InMemory.java
@@ -0,0 +1,69 @@
+/*  Copyright (c) 2019 AT&T Intellectual Property.                             #

+#                                                                              #

+#   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.oran.otf.api.tests.config;

+

+import de.flapdoodle.embed.mongo.Command;

+import de.flapdoodle.embed.mongo.MongodExecutable;

+import de.flapdoodle.embed.mongo.MongodStarter;

+import de.flapdoodle.embed.mongo.config.DownloadConfigBuilder;

+import de.flapdoodle.embed.mongo.config.ExtractedArtifactStoreBuilder;

+import de.flapdoodle.embed.mongo.config.IMongodConfig;

+import de.flapdoodle.embed.mongo.config.MongodConfigBuilder;

+import de.flapdoodle.embed.mongo.config.Net;

+import de.flapdoodle.embed.mongo.config.RuntimeConfigBuilder;

+import de.flapdoodle.embed.mongo.distribution.Version.Main;

+import de.flapdoodle.embed.process.config.IRuntimeConfig;

+import de.flapdoodle.embed.process.config.store.HttpProxyFactory;

+import de.flapdoodle.embed.process.runtime.Network;

+import org.springframework.beans.factory.annotation.Autowired;

+import org.springframework.context.annotation.Bean;

+import org.springframework.context.annotation.Configuration;

+import org.springframework.context.annotation.Profile;

+

+@Configuration

+@Profile("test")

+public class InMemory {

+  @Autowired MongodStarter mongodStarter;

+

+  @Bean

+  public MongodStarter mongodStarter(){

+    Command command = Command.MongoD;

+    IRuntimeConfig runtimeConfig = new RuntimeConfigBuilder()

+        .defaults(command)

+        .artifactStore(new ExtractedArtifactStoreBuilder()

+            .defaults(command)

+            .download(new DownloadConfigBuilder()

+                .defaultsForCommand(command)

+                //.downloadPath("http://fastdl.mongodb.org/win32/")

+                .proxyFactory(new HttpProxyFactory("localhost",8080))))

+             .build();

+

+    MongodStarter starter = MongodStarter.getInstance(runtimeConfig);

+

+    return MongodStarter.getInstance(runtimeConfig);

+  }

+  @Bean

+  public MongodExecutable mongodExecutable()throws Exception{

+    IMongodConfig mongodConfig = new MongodConfigBuilder().version(Main.PRODUCTION)

+        .net(new Net("localhost", 5555, Network.localhostIsIPv6()))

+        .build();

+    //MongodStarter starter = MongodStarter.getDefaultInstance();

+    return mongodStarter.prepare(mongodConfig);

+

+  }

+

+}

diff --git a/otf-service-api/src/test/java/org/oran/otf/api/tests/integration/services/ExecutionServiceRouteIT.java b/otf-service-api/src/test/java/org/oran/otf/api/tests/integration/services/ExecutionServiceRouteIT.java
new file mode 100644
index 0000000..85d7016
--- /dev/null
+++ b/otf-service-api/src/test/java/org/oran/otf/api/tests/integration/services/ExecutionServiceRouteIT.java
@@ -0,0 +1,79 @@
+/*  Copyright (c) 2019 AT&T Intellectual Property.                             #

+#                                                                              #

+#   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.oran.otf.api.tests.integration.services;

+

+import org.oran.otf.api.Application;

+import org.oran.otf.api.tests.shared.MemoryDatabase;

+import io.restassured.RestAssured;

+import org.junit.After;

+import org.junit.AfterClass;

+import org.junit.Before;

+import org.junit.BeforeClass;

+import org.junit.Ignore;

+import org.junit.Test;

+import org.junit.runner.RunWith;

+import org.springframework.boot.test.context.SpringBootTest;

+import org.springframework.boot.test.context.SpringBootTest.WebEnvironment;

+import org.springframework.boot.web.server.LocalServerPort;

+import org.springframework.test.context.ActiveProfiles;

+import org.springframework.test.context.TestPropertySource;

+import org.springframework.test.context.junit4.SpringRunner;

+

+@RunWith(SpringRunner.class)

+@SpringBootTest(

+    webEnvironment = WebEnvironment.RANDOM_PORT,

+    classes = {Application.class}

+)

+@TestPropertySource("classpath:application-test.properties")

+@ActiveProfiles("test")

+public class ExecutionServiceRouteIT {

+  @LocalServerPort

+  private int port;

+

+  @BeforeClass

+  public static void setup() throws Exception{

+    MemoryDatabase.setup();

+  }

+  @AfterClass

+  public static void cleanup(){

+    MemoryDatabase.cleanup();

+  }

+

+  @Before

+  public void setupRestAssured() throws Exception{

+    RestAssured.port = port;

+    RestAssured.urlEncodingEnabled = false;

+    RestAssured.baseURI = "https://localhost";

+    RestAssured.basePath="/otf/api/testExecution/v1";

+    RestAssured.useRelaxedHTTPSValidation();

+  }

+

+  @Ignore

+  @Test

+  public void testExecutionServiceRouteRespondsWith200(){}

+  @Test

+  public void testExecutionServiceRouteStatusRespondsWithOnNoAuth(){

+    RestAssured.given().log().all().header("Accept", "application/json").get("/status/executionId/abced").then().assertThat().statusCode(401);

+  }

+  @Test

+  public void testExecutionServiceRouteExecutionIdRespondsWithOnNoAuth(){

+    RestAssured.given().log().all().header("Accept", "application/json").get("/executionId/abced").then().assertThat().statusCode(401);

+  }

+

+

+}

+

diff --git a/otf-service-api/src/test/java/org/oran/otf/api/tests/integration/services/HealthRouteIT.java b/otf-service-api/src/test/java/org/oran/otf/api/tests/integration/services/HealthRouteIT.java
new file mode 100644
index 0000000..a04169e
--- /dev/null
+++ b/otf-service-api/src/test/java/org/oran/otf/api/tests/integration/services/HealthRouteIT.java
@@ -0,0 +1,80 @@
+/*  Copyright (c) 2019 AT&T Intellectual Property.                             #

+#                                                                              #

+#   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.oran.otf.api.tests.integration.services;

+

+import static org.hamcrest.CoreMatchers.equalTo;

+

+import org.oran.otf.api.Application;

+import org.oran.otf.api.tests.shared.MemoryDatabase;

+import io.restassured.RestAssured;

+import org.junit.AfterClass;

+import org.junit.Before;

+import org.junit.BeforeClass;

+import org.junit.Test;

+import org.junit.runner.RunWith;

+import org.springframework.boot.test.context.SpringBootTest;

+import org.springframework.boot.test.context.SpringBootTest.WebEnvironment;

+import org.springframework.boot.web.server.LocalServerPort;

+import org.springframework.test.context.ActiveProfiles;

+import org.springframework.test.context.TestPropertySource;

+import org.springframework.test.context.junit4.SpringRunner;

+

+@RunWith(SpringRunner.class)

+@SpringBootTest(

+    webEnvironment = WebEnvironment.RANDOM_PORT,

+    classes = {

+        Application.class

+    })

+@TestPropertySource("classpath:application-test.properties")

+@ActiveProfiles("test")

+public class HealthRouteIT {

+  @LocalServerPort

+  private int port;

+

+

+  @BeforeClass

+  public static void setup()throws Exception{

+    MemoryDatabase.setup();

+  }

+  @AfterClass

+  public static void cleanup(){

+    MemoryDatabase.cleanup();

+  }

+  @Before

+  public void setupRestAssured(){

+    RestAssured.port = port;

+    RestAssured.baseURI="https://localhost";

+    RestAssured.basePath="/otf/api";

+    RestAssured.urlEncodingEnabled =false;

+    RestAssured.useRelaxedHTTPSValidation();

+

+  }

+  @Test

+  public void testHealthRouteRespondsWith200(){

+    RestAssured.given().log().all().header("Accept", "application/json").get("/health/v1").then().assertThat().statusCode(200);

+  }

+  @Test

+  public void testHealthRouteRespondsWithUp(){

+    RestAssured.given().log().all().header("Accept", "application/json").get("/health/v1").then().assertThat().body("message", equalTo("UP"));

+  }

+  @Test

+  public void testHealthRouteRespondsWithJson(){

+    RestAssured.given().log().all().header("Accept", "application/json").get("/health/v1").then().contentType("application/json");

+  }

+

+

+}

diff --git a/otf-service-api/src/test/java/org/oran/otf/api/tests/integration/services/InstanceServiceRouteIT.java b/otf-service-api/src/test/java/org/oran/otf/api/tests/integration/services/InstanceServiceRouteIT.java
new file mode 100644
index 0000000..a16a23c
--- /dev/null
+++ b/otf-service-api/src/test/java/org/oran/otf/api/tests/integration/services/InstanceServiceRouteIT.java
@@ -0,0 +1,170 @@
+/*  Copyright (c) 2019 AT&T Intellectual Property.                             #

+#                                                                              #

+#   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.oran.otf.api.tests.integration.services;

+

+import static org.hamcrest.CoreMatchers.containsString;

+import static org.hamcrest.CoreMatchers.equalTo;

+

+import org.oran.otf.api.Application;

+import org.oran.otf.api.tests.shared.MemoryDatabase;

+import org.oran.otf.common.model.TestDefinition;

+import org.oran.otf.common.model.TestInstance;

+import org.oran.otf.common.model.User;

+import io.restassured.RestAssured;

+import org.eclipse.jetty.http.QuotedQualityCSV;

+import org.junit.AfterClass;

+import org.junit.Before;

+import org.junit.BeforeClass;

+import org.junit.Test;

+import org.junit.runner.RunWith;

+import org.springframework.beans.factory.annotation.Autowired;

+import org.springframework.beans.factory.annotation.Value;

+import org.springframework.boot.autoconfigure.EnableAutoConfiguration;

+import org.springframework.boot.test.context.SpringBootTest;

+import org.springframework.boot.test.context.SpringBootTest.WebEnvironment;

+import org.springframework.boot.web.server.LocalServerPort;

+import org.springframework.data.mongodb.core.MongoTemplate;

+import org.springframework.data.mongodb.core.query.Criteria;

+import org.springframework.data.mongodb.core.query.Query;

+import org.springframework.test.context.ActiveProfiles;

+import org.springframework.test.context.TestPropertySource;

+import org.springframework.test.context.junit4.SpringRunner;

+

+@RunWith(SpringRunner.class)

+@SpringBootTest(

+    webEnvironment = WebEnvironment.RANDOM_PORT,

+    classes = {Application.class}

+)

+@TestPropertySource("classpath:application-test.properties")

+@ActiveProfiles("test")

+public class InstanceServiceRouteIT {

+  @LocalServerPort

+  private int port;

+  @Value("${otf.mechid}")

+  private String username;

+  @Value("${otf.mechpass}")

+  private String password;

+  private static User mechUser;

+

+  @Autowired

+  private MongoTemplate mongoTemplate;

+

+  @BeforeClass

+  public static void setup() throws Exception{

+    MemoryDatabase.createAllTables();

+    MemoryDatabase.createAllAdmin();

+    //mechUser = MemoryDatabase.createMechUser();

+  }

+  @AfterClass

+  public static void cleanup(){

+    MemoryDatabase.cleanup();

+  }

+  @Before

+  public void setupRestAssured() {

+    RestAssured.port = port;

+    RestAssured.urlEncodingEnabled = false;

+    RestAssured.baseURI = "https://localhost";

+    RestAssured.basePath="/otf/api/testInstance";

+    RestAssured.useRelaxedHTTPSValidation();

+  }

+  //NoAuth Tests

+

+  @Test

+  public void testFindByIdRespondsWith401OnNoAuth(){

+    RestAssured.given().log().all().header("Accept", "application/json").get("/v1/id/abced").then().assertThat().statusCode(401);

+  }

+  @Test

+  public void testFindByProcessKeyRespondsWith401OnNoAuth(){

+    RestAssured.given().log().all().header("Accept", "application/json").get("/v1/processDefinitionKey/abced").then().assertThat().statusCode(401);

+  }

+  @Test

+  public void testFindByProcessKeyAndVersionRespondsWith401OnNoAuth(){

+    RestAssured.given().log().all().header("Accept", "application/json").get("/v1/processDefinitionKey/abced/version/1").then().assertThat().statusCode(401);

+  }

+

+

+  @Test

+  public void testExecuteRespondsWith401OnNoAuth(){

+    RestAssured.given().log().all().header("Accept", "application/json").get("/execute/v1/id/abced/").then().assertThat().statusCode(401);

+  }

+

+

+  @Test

+  public void testCreateByTestDefinitionIdRespondsWith401OnNoAuth(){

+    RestAssured.given().log().all().header("Accept", "application/json").get("/create/v1/testDefinitionId/abced/").then().assertThat().statusCode(401);

+  }

+  @Test

+  public void testCreateByTestDefinitionIdAndVersionRespondsWith401OnNoAuth(){

+    RestAssured.given().log().all().header("Accept", "application/json").get("/create/v1/testDefinitionId/abced/version/2").then().assertThat().statusCode(401);

+  }

+  @Test

+  public void testCreateByProcessDefinitionKeyRespondsWith401OnNoAuth(){

+    RestAssured.given().log().all().header("Accept", "application/json").get("/create/v1/processDefinitionKey/abced").then().assertThat().statusCode(401);

+  }

+  @Test

+  public void testCreateByProcessDefinitionKeyAndVersionRespondsWith401OnNoAuth(){

+    RestAssured.given().log().all().header("Accept", "application/json").get("/create/v1/processDefinitionKey/abced/version/2").then().assertThat().statusCode(401);

+  }

+

+  //With Auth and Wrong id

+  @Test

+  public void testFindByIdRespondsWith400OnWrongId(){

+    RestAssured.given().auth().basic(username,password).log().all().header("Accept", "application/json").get("/v1/id/abced").then().assertThat().statusCode(400);

+  }

+  @Test

+  public void testFindByIdRespondsWithMessageOnWrongId(){

+    RestAssured.given().auth().basic(username,password).log().all().header("Accept", "application/json").get("/v1/id/abcde").then().assertThat().body("message", containsString("is not a valid ObjectId (BSON)"));

+  }

+  @Test

+  public void testFindByProcessDefinitionRespondsWith400OnWrongProcessDefinition(){

+    TestDefinition testDefinition = mongoTemplate.findOne(new Query(Criteria.where("testName").is("testDef1")), TestDefinition.class);

+    RestAssured.given().auth().basic(username,password).log().all().header("Accept", "*/*").get("/v1/processDefinitionKey/"+testDefinition.getProcessDefinitionKey()).then().assertThat().statusCode(400);

+  }

+  @Test

+  public void testFindByProcessDefinitionRespondsWithMessageOnWrongProcessDefinition(){

+    TestDefinition testDefinition = mongoTemplate.findOne(new Query(Criteria.where("testName").is("testDef1")), TestDefinition.class);

+    RestAssured.given().auth().basic(username,password).log().all().header("Accept", "application/json").get("/v1/processDefinitionKey/"+testDefinition.getProcessDefinitionKey()).then().assertThat().body("message", containsString("No test instances found"));

+  }

+

+  //Successful Get Methods

+

+  @Test

+  public void testFindByIdRespondsWith200OnSuccess(){

+    TestInstance testInstance = mongoTemplate.findOne(new Query(Criteria.where("testInstanceName").is("MechTestInstance")), TestInstance.class);

+    RestAssured.given().auth().basic(username,password).log().all().header("Accept", "*/*").get("/v1/id/"+testInstance.get_id()).then().assertThat().statusCode(200);

+  }

+

+  @Test

+  public void testFindByProcessDefinitionKeyRespondsWith200OnSuccess(){

+    TestDefinition testDefinition = mongoTemplate.findOne(new Query(Criteria.where("testName").is("MechTestDefinition")), TestDefinition.class);

+    RestAssured.given().auth().basic(username, password).log().all().header("Accept", "*/*").get("/v1/processDefinitionKey/"+testDefinition.getProcessDefinitionKey()).then().assertThat().statusCode(200);

+  }

+

+  @Test

+  public void testFindByProcessDefinitionKeyAndVersionRespondsWith200OnSuccess(){

+    TestDefinition testDefinition = mongoTemplate.findOne(new Query(Criteria.where("testName").is("MechTestDefinition")), TestDefinition.class);

+    RestAssured.given().auth().basic(username, password).log().all().header("Accept", "*/*").get("/v1/processDefinitionKey/"+testDefinition.getProcessDefinitionKey()+"/version/"+1).then().assertThat().statusCode(200);

+  }

+

+  @Test

+  public void testCreateByTestDefinitionIdRespondsWith201OnSuccess(){

+    TestDefinition testDefinition = mongoTemplate.findOne(new Query(Criteria.where("testName").is("MechTestDefinition")), TestDefinition.class);

+    System.out.println(testDefinition.getBpmnInstances());

+    RestAssured.given().contentType("application/json\r\n").auth().basic(username, password).log().all().header("Accept", "*/*").post("/create/v1/testDefinitionId/"+testDefinition.get_id()+"/version/"+1).then().assertThat().statusCode(404);

+  }

+

+}

diff --git a/otf-service-api/src/test/java/org/oran/otf/api/tests/integration/services/OtfOpenRouteIT.java b/otf-service-api/src/test/java/org/oran/otf/api/tests/integration/services/OtfOpenRouteIT.java
new file mode 100644
index 0000000..132464a
--- /dev/null
+++ b/otf-service-api/src/test/java/org/oran/otf/api/tests/integration/services/OtfOpenRouteIT.java
@@ -0,0 +1,67 @@
+/*  Copyright (c) 2019 AT&T Intellectual Property.                             #

+#                                                                              #

+#   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.oran.otf.api.tests.integration.services;

+

+import org.oran.otf.api.Application;

+import org.oran.otf.api.tests.shared.MemoryDatabase;

+import io.restassured.RestAssured;

+import org.junit.AfterClass;

+import org.junit.Before;

+import org.junit.BeforeClass;

+import org.junit.Ignore;

+import org.junit.Test;

+import org.junit.runner.RunWith;

+import org.springframework.boot.test.context.SpringBootTest;

+import org.springframework.boot.test.context.SpringBootTest.WebEnvironment;

+import org.springframework.boot.web.server.LocalServerPort;

+import org.springframework.test.context.ActiveProfiles;

+import org.springframework.test.context.TestPropertySource;

+import org.springframework.test.context.junit4.SpringRunner;

+

+@RunWith(SpringRunner.class)

+@SpringBootTest(

+    webEnvironment = WebEnvironment.RANDOM_PORT,

+    classes = {Application.class}

+)

+@TestPropertySource("classpath:application-test.properties")

+@ActiveProfiles("test")

+public class OtfOpenRouteIT {

+  @LocalServerPort

+  private int port;

+

+  @BeforeClass

+  public static void setup() throws Exception{

+    MemoryDatabase.setup();

+  }

+  @AfterClass

+  public static void cleanup(){

+    MemoryDatabase.cleanup();

+  }

+  @Before

+  public void setupRestAssured(){

+    RestAssured.port =port;

+    RestAssured.urlEncodingEnabled = false;

+    RestAssured.baseURI="https://localhost";

+    RestAssured.basePath="/otf/api";

+    RestAssured.useRelaxedHTTPSValidation();

+  }

+  @Ignore("Ignoring test because it fails since it tries to request to specific port, uncomment to view error")

+  @Test

+  public void testOtfOpenRouteRespondsWith200(){

+    RestAssured.given().log().all().header("Accept", "application/json").get("/demo/openapi.json").then().statusCode(200);

+  }

+}

diff --git a/otf-service-api/src/test/java/org/oran/otf/api/tests/integration/services/Permissions/PermissionServiceIT.java b/otf-service-api/src/test/java/org/oran/otf/api/tests/integration/services/Permissions/PermissionServiceIT.java
new file mode 100644
index 0000000..6186d3a
--- /dev/null
+++ b/otf-service-api/src/test/java/org/oran/otf/api/tests/integration/services/Permissions/PermissionServiceIT.java
@@ -0,0 +1,331 @@
+/*  Copyright (c) 2019 AT&T Intellectual Property.                             #

+#                                                                              #

+#   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.oran.otf.api.tests.integration.services.Permissions;

+

+import org.oran.otf.api.Application;

+import org.oran.otf.api.tests.shared.MemoryDatabase;

+import org.oran.otf.common.model.Group;

+import org.oran.otf.common.model.GroupMember;

+import org.oran.otf.common.model.User;

+import org.oran.otf.common.repository.GroupRepository;

+import org.oran.otf.common.utility.permissions.PermissionChecker;

+import org.oran.otf.common.utility.permissions.PermissionUtil;

+import org.oran.otf.common.utility.permissions.UserPermission;

+import org.bson.types.ObjectId;

+import org.junit.*;

+import org.junit.runner.RunWith;

+import org.mockito.Mockito;

+import org.springframework.beans.factory.annotation.Autowired;

+import org.springframework.boot.test.context.SpringBootTest;

+import org.springframework.test.context.ActiveProfiles;

+import org.springframework.test.context.TestPropertySource;

+import org.springframework.test.context.junit4.SpringRunner;

+

+import java.util.*;

+

+@RunWith(SpringRunner.class)

+@SpringBootTest(

+        webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT,

+        classes = {

+                Application.class,

+        })

+@TestPropertySource("classpath:application-test.properties")

+@ActiveProfiles("test")

+public class PermissionServiceIT {

+    @Autowired

+    private GroupRepository groupRepository;

+    private List<Group> groups;

+    private Group parentGroup;

+    private Group firstChildGroup;

+    private Group childOfChildGroup;

+

+    @BeforeClass

+    public static void setUp() throws Exception{

+        MemoryDatabase.setup();

+        MemoryDatabase.createGroupsForPermission();

+    }

+    @Before

+    public void setUpGroups()

+    {

+        groups = groupRepository.findAll();

+        parentGroup = groupRepository.findFirstByGroupName("parent group");

+        firstChildGroup = groupRepository.findFirstByGroupName("first child group");

+        childOfChildGroup = groupRepository.findFirstByGroupName("child of child group");

+    }

+    @AfterClass

+    public static void cleanup(){

+        MemoryDatabase.cleanup();

+    }

+    /*

+     if this test failed there was a error during set up so ignore the failures produced by other tests til this pass

+    */

+    @Test

+    public void setUpTest(){

+        List<Group> groups = groupRepository.findAll();

+        parentGroup = groupRepository.findFirstByGroupName("parent group");

+        firstChildGroup = groupRepository.findFirstByGroupName("first child group");

+        childOfChildGroup = groupRepository.findFirstByGroupName("child of child group");

+        Assert.assertNotNull(groups);

+        Assert.assertFalse(groups.isEmpty());

+

+        Assert.assertNotNull(parentGroup.getMembers());

+        Assert.assertFalse(parentGroup.getMembers().isEmpty());

+        Assert.assertNotNull(parentGroup.getRoles());

+        Assert.assertFalse(parentGroup.getRoles().isEmpty());

+

+        Assert.assertNotNull(firstChildGroup.getMembers());

+        Assert.assertFalse(firstChildGroup.getMembers().isEmpty());

+        Assert.assertNotNull(firstChildGroup.getRoles());

+        Assert.assertFalse(firstChildGroup.getRoles().isEmpty());

+

+        Assert.assertNotNull(childOfChildGroup.getMembers());

+        Assert.assertFalse(childOfChildGroup.getMembers().isEmpty());

+        Assert.assertNotNull(childOfChildGroup.getRoles());

+        Assert.assertFalse(childOfChildGroup.getRoles().isEmpty());

+        // all groups are set up with 1 member in memory db

+        Assert.assertEquals(1,parentGroup.getMembers().size());

+        Assert.assertEquals(1,firstChildGroup.getMembers().size());

+        Assert.assertEquals(1,childOfChildGroup.getMembers().size());

+    }

+    @Test

+    public void findUserRoles(){

+        GroupMember parentMember = parentGroup.getMembers().get(0);

+        GroupMember firstChildMember = firstChildGroup.getMembers().get(0);

+        GroupMember childOfChildMember = childOfChildGroup.getMembers().get(0);

+

+        User parentUserMock = Mockito.mock(User.class);

+        User firstChildUserMock = Mockito.mock(User.class);

+        User childOfChildUserMock = Mockito.mock(User.class);

+

+        Mockito.when(parentUserMock.get_id()).thenReturn(parentMember.getUserId());

+        Mockito.when(firstChildUserMock.get_id()).thenReturn(firstChildMember.getUserId());

+        Mockito.when(childOfChildUserMock.get_id()).thenReturn(childOfChildMember.getUserId());

+

+        Set<String> parentMemberRoles = PermissionUtil.findUserRoles(parentUserMock, parentGroup);

+        Set<String> firstChildRoles = PermissionUtil.findUserRoles(firstChildUserMock, firstChildGroup);

+        Set<String> childOfChildRoles = PermissionUtil.findUserRoles(childOfChildUserMock, childOfChildGroup);

+

+        // all group members should only have 1 role (admin) set up except first child

+        Assert.assertEquals(1,parentMemberRoles.size());

+        Assert.assertTrue(parentMemberRoles.contains("admin"));

+        Assert.assertEquals(2,firstChildRoles.size());

+        Assert.assertTrue(firstChildRoles.contains("admin"));

+        Assert.assertTrue(firstChildRoles.contains("dev"));

+        Assert.assertEquals(1,childOfChildRoles.size());

+        Assert.assertTrue(childOfChildRoles.contains("executor"));

+

+        Assert.assertFalse(parentMemberRoles.contains("executor"));

+        Assert.assertFalse(firstChildRoles.contains("executor"));

+        Assert.assertFalse("should not have admin roles in child of child", childOfChildRoles.contains("admin"));

+    }

+    @Test

+    public void getRolePermissionsTest()

+    {

+        ObjectId firstChildId =firstChildGroup.getMembers().get(0).getUserId();

+        User firstChildUserMock = Mockito.mock(User.class);

+        Mockito.when(firstChildUserMock.get_id()).thenReturn(firstChildId);

+        Set<String> roles = PermissionUtil.findUserRoles(firstChildUserMock,firstChildGroup); //dev and admin roles only

+

+        Assert.assertEquals(2,roles.size());

+        for(String role : roles){

+            Set<String> permissions = PermissionUtil.getRolePermissions(role,parentGroup);

+            Assert.assertTrue("all permissions allowed except execute and delete",permissions.contains("READ"));

+            Assert.assertTrue("all permissions allowed except execute and delete",permissions.contains("WRITE"));

+            Assert.assertFalse("all permissions allowed except execute and delete",permissions.contains("DELETE"));

+            Assert.assertFalse("all permissions allowed except execute and delete",permissions.contains("EXECUTE"));

+        }

+    }

+    @Test

+    public void getUserGroupPermissionTest(){

+        GroupMember firstChildMember = firstChildGroup.getMembers().get(0);

+        User firstChildUser = Mockito.mock(User.class);

+        Mockito.when(firstChildUser.get_id()).thenReturn(firstChildMember.getUserId());

+        Set<String> permissions = PermissionUtil.getUserGroupPermissions(firstChildUser,firstChildGroup); // should include everything except execute and delete

+

+        Assert.assertEquals(3,permissions.size());

+        Assert.assertTrue("all permissions allowed except execute and delete",permissions.contains("READ"));

+        Assert.assertTrue("all permissions allowed except execute and delete",permissions.contains("WRITE"));

+        Assert.assertFalse("all permissions allowed except execute and delete",permissions.contains("DELETE"));

+        Assert.assertFalse("all permissions allowed except execute and delete",permissions.contains("EXECUTE"));

+        Assert.assertTrue("all permissions allowed except execute and delete",permissions.contains("MANAGEMENT"));

+    }

+

+    @Test

+    public void hasPermissionToTest(){

+        GroupMember parentMember = parentGroup.getMembers().get(0);

+        GroupMember firstChildMember = firstChildGroup.getMembers().get(0);

+        GroupMember childOfChildMember = childOfChildGroup.getMembers().get(0);

+

+        User parentGroupUser = Mockito.mock(User.class);

+        User firstChildUser = Mockito.mock(User.class);

+        User childOfChildUser =Mockito.mock(User.class);

+        Mockito.when(parentGroupUser.get_id()).thenReturn(parentMember.getUserId());

+        Mockito.when(firstChildUser.get_id()).thenReturn(firstChildMember.getUserId());

+        Mockito.when(childOfChildUser.get_id()).thenReturn(childOfChildMember.getUserId());

+

+        String read = "read";

+        String write= "write";

+        String manage = "management";

+        String delete = "delete";

+        String execute= "execute";

+

+        Assert.assertTrue(PermissionUtil.hasPermissionTo(read,parentGroupUser,parentGroup));

+        Assert.assertTrue(PermissionUtil.hasPermissionTo(write,parentGroupUser,parentGroup));

+        Assert.assertTrue(PermissionUtil.hasPermissionTo(manage,parentGroupUser,parentGroup));

+        Assert.assertFalse(PermissionUtil.hasPermissionTo(delete,parentGroupUser,parentGroup));

+        Assert.assertFalse(PermissionUtil.hasPermissionTo(execute,parentGroupUser,parentGroup));

+

+        Assert.assertTrue(PermissionUtil.hasPermissionTo(read,firstChildUser,firstChildGroup));

+        Assert.assertTrue(PermissionUtil.hasPermissionTo(write,firstChildUser,firstChildGroup));

+        Assert.assertTrue(PermissionUtil.hasPermissionTo(manage,firstChildUser,firstChildGroup));

+        Assert.assertFalse(PermissionUtil.hasPermissionTo(delete,firstChildUser,firstChildGroup));

+        Assert.assertFalse(PermissionUtil.hasPermissionTo(execute,firstChildUser,firstChildGroup));

+

+        Assert.assertFalse(PermissionUtil.hasPermissionTo(read,childOfChildUser,childOfChildGroup));

+        Assert.assertFalse(PermissionUtil.hasPermissionTo(write,childOfChildUser,childOfChildGroup));

+        Assert.assertFalse(PermissionUtil.hasPermissionTo(manage,childOfChildUser,childOfChildGroup));

+        Assert.assertFalse(PermissionUtil.hasPermissionTo(delete,childOfChildUser,childOfChildGroup));

+        Assert.assertTrue(PermissionUtil.hasPermissionTo(execute,childOfChildUser,childOfChildGroup));

+    }

+    @Test

+    public void buildUserPermissionTest()

+    {

+        /*

+           should be the following format

+           parent members:

+           parentGroup = {read,write,management}

+           first Child group = {read}

+           child of child group = {read}

+

+           first child group:

+           parentGroup = {read}

+           first Child group = {read,write,management}

+           child of child group = {read}

+

+           child of child:

+           parentGroup = {read}

+           first Child group = {read}

+           child of child group = {execute}

+         */

+

+        GroupMember parentMember = parentGroup.getMembers().get(0);

+        GroupMember firstChildMember = firstChildGroup.getMembers().get(0);

+        GroupMember childOfChildMember = childOfChildGroup.getMembers().get(0);

+

+        User parentGroupUser = Mockito.mock(User.class);

+        User firstChildUser = Mockito.mock(User.class);

+        User childOfChildUser =Mockito.mock(User.class);

+        Mockito.when(parentGroupUser.get_id()).thenReturn(parentMember.getUserId());

+        Mockito.when(firstChildUser.get_id()).thenReturn(firstChildMember.getUserId());

+        Mockito.when(childOfChildUser.get_id()).thenReturn(childOfChildMember.getUserId());

+

+        String read = "READ";

+        String write= "WRITE";

+        String manage = "MANAGEMENT";

+        String delete = "DELETE";

+        String execute= "EXECUTE";

+

+        UserPermission parentUserPermissions = new PermissionUtil().buildUserPermission(parentGroupUser,groupRepository);

+        UserPermission firstChildUserPermissions = new PermissionUtil().buildUserPermission(firstChildUser,groupRepository);

+        UserPermission childOfChildUserPermissions = new PermissionUtil().buildUserPermission(childOfChildUser,groupRepository);

+        Map<String,Set<String>> parentAccessControl = parentUserPermissions.getUserAccessMap();

+        Map<String,Set<String>> firstChildAccessControl = firstChildUserPermissions.getUserAccessMap();

+        Map<String,Set<String>> childOfChildAccessControl = childOfChildUserPermissions.getUserAccessMap();

+

+        //test for parent access control

+        Assert.assertTrue(parentAccessControl.get(parentGroup.get_id().toString()).contains(read));

+        Assert.assertTrue(parentAccessControl.get(parentGroup.get_id().toString()).contains(write));

+        Assert.assertTrue(parentAccessControl.get(parentGroup.get_id().toString()).contains(manage));

+        //test all access is passed to firstChildGroup

+        Assert.assertTrue(parentAccessControl.get(firstChildGroup.get_id().toString()).contains(read));

+        Assert.assertTrue(parentAccessControl.get(firstChildGroup.get_id().toString()).contains(write));

+        Assert.assertTrue(parentAccessControl.get(firstChildGroup.get_id().toString()).contains(manage));

+        //test all access is passed to child of child group

+        Assert.assertTrue(parentAccessControl.get(childOfChildGroup.get_id().toString()).contains(read));

+        Assert.assertTrue(parentAccessControl.get(childOfChildGroup.get_id().toString()).contains(write));

+        Assert.assertTrue(parentAccessControl.get(childOfChildGroup.get_id().toString()).contains(manage));

+        // make sure parent user dont have other permissions in first child group

+        Assert.assertFalse(parentAccessControl.get(firstChildGroup.get_id().toString()).contains(delete));

+        Assert.assertFalse(parentAccessControl.get(firstChildGroup.get_id().toString()).contains(execute));

+        //test that parent dont have other permissions in child of child group

+        Assert.assertFalse(parentAccessControl.get(childOfChildGroup.get_id().toString()).contains(delete));

+        Assert.assertFalse(parentAccessControl.get(childOfChildGroup.get_id().toString()).contains(execute));

+

+        //test for first child access control

+        Assert.assertTrue(firstChildAccessControl.get(parentGroup.get_id().toString()).contains(read));

+        Assert.assertTrue(firstChildAccessControl.get(firstChildGroup.get_id().toString()).contains(read));

+        Assert.assertTrue(firstChildAccessControl.get(firstChildGroup.get_id().toString()).contains(write));

+        Assert.assertTrue(firstChildAccessControl.get(firstChildGroup.get_id().toString()).contains(manage));

+        // test that first child group get passed to child of child

+        Assert.assertTrue(firstChildAccessControl.get(childOfChildGroup.get_id().toString()).contains(read));

+        Assert.assertTrue(firstChildAccessControl.get(childOfChildGroup.get_id().toString()).contains(write));

+        Assert.assertTrue(firstChildAccessControl.get(childOfChildGroup.get_id().toString()).contains(manage));

+        // make sure firstchild user dont have other permissions

+        Assert.assertFalse(firstChildAccessControl.get(parentGroup.get_id().toString()).contains(write));

+        Assert.assertFalse(firstChildAccessControl.get(parentGroup.get_id().toString()).contains(manage));

+        Assert.assertFalse(firstChildAccessControl.get(parentGroup.get_id().toString()).contains(delete));

+        Assert.assertFalse(firstChildAccessControl.get(parentGroup.get_id().toString()).contains(execute));

+        // test to confirm no extra permission is passed to child of child

+        Assert.assertFalse(firstChildAccessControl.get(childOfChildGroup.get_id().toString()).contains(delete));

+        Assert.assertFalse(firstChildAccessControl.get(childOfChildGroup.get_id().toString()).contains(execute));

+

+        //test for child of child access control

+        Assert.assertTrue(childOfChildAccessControl.get(parentGroup.get_id().toString()).contains(read));

+        Assert.assertTrue(childOfChildAccessControl.get(firstChildGroup.get_id().toString()).contains(read));

+        Assert.assertTrue(childOfChildAccessControl.get(childOfChildGroup.get_id().toString()).contains(execute));

+        // make sure child of child user dont have other permissions

+        Assert.assertFalse(childOfChildAccessControl.get(parentGroup.get_id().toString()).contains(write));

+        Assert.assertFalse(childOfChildAccessControl.get(parentGroup.get_id().toString()).contains(manage));

+        Assert.assertFalse(childOfChildAccessControl.get(parentGroup.get_id().toString()).contains(delete));

+        Assert.assertFalse(childOfChildAccessControl.get(parentGroup.get_id().toString()).contains(execute));

+

+        Assert.assertFalse(childOfChildAccessControl.get(firstChildGroup.get_id().toString()).contains(write));

+        Assert.assertFalse(childOfChildAccessControl.get(firstChildGroup.get_id().toString()).contains(manage));

+        Assert.assertFalse(childOfChildAccessControl.get(firstChildGroup.get_id().toString()).contains(delete));

+        Assert.assertFalse(childOfChildAccessControl.get(firstChildGroup.get_id().toString()).contains(execute));

+

+        Assert.assertFalse(childOfChildAccessControl.get(childOfChildGroup.get_id().toString()).contains(write));

+        Assert.assertFalse(childOfChildAccessControl.get(childOfChildGroup.get_id().toString()).contains(manage));

+        Assert.assertFalse(childOfChildAccessControl.get(childOfChildGroup.get_id().toString()).contains(delete));

+        Assert.assertFalse(childOfChildAccessControl.get(childOfChildGroup.get_id().toString()).contains(read));

+    }

+    @Test

+    public void basicTest(){

+        GroupMember parentMember = parentGroup.getMembers().get(0);

+        GroupMember firstChildMember = firstChildGroup.getMembers().get(0);

+        GroupMember childOfChildMember = childOfChildGroup.getMembers().get(0);

+

+        User parentGroupUser = Mockito.mock(User.class);

+        User firstChildUser = Mockito.mock(User.class);

+        User childOfChildUser =Mockito.mock(User.class);

+        Mockito.when(parentGroupUser.get_id()).thenReturn(parentMember.getUserId());

+        Mockito.when(firstChildUser.get_id()).thenReturn(firstChildMember.getUserId());

+        Mockito.when(childOfChildUser.get_id()).thenReturn(childOfChildMember.getUserId());

+

+        Assert.assertTrue(PermissionChecker.hasPermissionTo(childOfChildUser,firstChildGroup,UserPermission.Permission.READ,groupRepository));

+        Assert.assertTrue(PermissionChecker.hasPermissionTo(childOfChildUser,parentGroup,UserPermission.Permission.READ,groupRepository));

+        Assert.assertFalse(PermissionChecker.hasPermissionTo(childOfChildUser,childOfChildGroup,UserPermission.Permission.READ,groupRepository));

+

+        Assert.assertFalse(PermissionChecker.hasPermissionTo(childOfChildUser,firstChildGroup,UserPermission.Permission.EXECUTE,groupRepository));

+        Assert.assertTrue(PermissionChecker.hasPermissionTo(firstChildUser,firstChildGroup,UserPermission.Permission.WRITE,groupRepository));

+        Assert.assertFalse(PermissionChecker.hasPermissionTo(firstChildUser,firstChildGroup,UserPermission.Permission.EXECUTE,groupRepository));

+

+        Assert.assertFalse(PermissionChecker.hasPermissionTo(parentGroupUser,parentGroup,UserPermission.Permission.DELETE,groupRepository));

+        Assert.assertFalse(PermissionChecker.hasPermissionTo(parentGroupUser,parentGroup,UserPermission.Permission.EXECUTE,groupRepository));

+        Assert.assertFalse(PermissionChecker.hasPermissionTo(parentGroupUser,firstChildGroup,UserPermission.Permission.EXECUTE,groupRepository));

+    }

+}

diff --git a/otf-service-api/src/test/java/org/oran/otf/api/tests/integration/services/StrategyServiceRouteIT.java b/otf-service-api/src/test/java/org/oran/otf/api/tests/integration/services/StrategyServiceRouteIT.java
new file mode 100644
index 0000000..7cd2b43
--- /dev/null
+++ b/otf-service-api/src/test/java/org/oran/otf/api/tests/integration/services/StrategyServiceRouteIT.java
@@ -0,0 +1,74 @@
+/*  Copyright (c) 2019 AT&T Intellectual Property.                             #

+#                                                                              #

+#   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.oran.otf.api.tests.integration.services;

+

+import org.oran.otf.api.Application;

+import org.oran.otf.api.tests.shared.MemoryDatabase;

+import io.restassured.RestAssured;

+import org.junit.AfterClass;

+import org.junit.Before;

+import org.junit.BeforeClass;

+import org.junit.Test;

+import org.junit.runner.RunWith;

+import org.springframework.boot.test.context.SpringBootTest;

+import org.springframework.boot.test.context.SpringBootTest.WebEnvironment;

+import org.springframework.boot.web.server.LocalServerPort;

+import org.springframework.test.context.ActiveProfiles;

+import org.springframework.test.context.TestPropertySource;

+import org.springframework.test.context.junit4.SpringRunner;

+

+@RunWith(SpringRunner.class)

+@SpringBootTest(

+    webEnvironment = WebEnvironment.RANDOM_PORT,

+    classes = {Application.class}

+)

+@TestPropertySource("classpath:application-test.properties")

+@ActiveProfiles("test")

+public class StrategyServiceRouteIT {

+  @LocalServerPort

+  private int port;

+  @BeforeClass

+  public static void setup() throws Exception{

+    MemoryDatabase.setup();

+  }

+  @AfterClass

+  public static void cleanup(){

+    MemoryDatabase.cleanup();

+  }

+  @Before

+  public void setupRestAssured(){

+    RestAssured.port = port;

+    RestAssured.baseURI="https://localhost";

+    RestAssured.basePath="/otf/api/testStrategy";

+    RestAssured.urlEncodingEnabled=false;

+    RestAssured.useRelaxedHTTPSValidation();

+  }

+

+  @Test

+  public void testStrategyServiceRouteDeployRespondsWith401OnNoAuth(){

+    RestAssured.given().log().all().header("Accept", "application/json").post("/deploy/v1").then().assertThat().statusCode(401);

+  }

+  @Test

+  public void testStrategyServiceRouteDeleteByTestDefinitionIdRespondsWith401OnNoAuth(){

+    RestAssured.given().log().all().header("Accept", "application/json").delete("/delete/v1/testDefinitionId/56565656").then().assertThat().statusCode(401);

+  }

+  @Test

+  public void testStrategyServiceRouteDeleteByDeploymentIdRespondsWith401OnNoAuth(){

+    RestAssured.given().log().all().header("Accept", "application/json").delete("/delete/v1/deploymentId/54545454").then().assertThat().statusCode(401);

+  }

+

+}

diff --git a/otf-service-api/src/test/java/org/oran/otf/api/tests/shared/MemoryDatabase.java b/otf-service-api/src/test/java/org/oran/otf/api/tests/shared/MemoryDatabase.java
new file mode 100644
index 0000000..2c17abb
--- /dev/null
+++ b/otf-service-api/src/test/java/org/oran/otf/api/tests/shared/MemoryDatabase.java
@@ -0,0 +1,386 @@
+/*  Copyright (c) 2019 AT&T Intellectual Property.                             #

+#                                                                              #

+#   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.oran.otf.api.tests.shared;

+

+import org.oran.otf.common.model.*;

+import org.oran.otf.common.model.local.BpmnInstance;

+import org.oran.otf.common.model.local.UserGroup;

+import com.mongodb.BasicDBObjectBuilder;

+import com.mongodb.DBObject;

+import com.mongodb.MongoClient;

+import de.flapdoodle.embed.mongo.Command;

+import de.flapdoodle.embed.mongo.MongodExecutable;

+import de.flapdoodle.embed.mongo.MongodProcess;

+import de.flapdoodle.embed.mongo.MongodStarter;

+import de.flapdoodle.embed.mongo.config.DownloadConfigBuilder;

+import de.flapdoodle.embed.mongo.config.ExtractedArtifactStoreBuilder;

+import de.flapdoodle.embed.mongo.config.IMongodConfig;

+import de.flapdoodle.embed.mongo.config.MongodConfigBuilder;

+import de.flapdoodle.embed.mongo.config.Net;

+import de.flapdoodle.embed.mongo.config.RuntimeConfigBuilder;

+import de.flapdoodle.embed.mongo.distribution.Version;

+import de.flapdoodle.embed.mongo.distribution.Version.Main;

+import de.flapdoodle.embed.process.config.IRuntimeConfig;

+import de.flapdoodle.embed.process.config.store.HttpProxyFactory;

+import de.flapdoodle.embed.process.runtime.Network;

+

+import java.sql.Timestamp;

+import java.util.ArrayList;

+import java.util.Arrays;

+import java.util.Date;

+import java.util.List;

+import java.util.Random;

+import javassist.util.proxy.ProxyFactory;

+import org.apache.commons.lang3.time.DateUtils;

+import org.apache.http.HttpResponse;

+import org.apache.http.client.methods.HttpGet;

+import org.oran.otf.common.model.*;

+import org.springframework.context.annotation.Configuration;

+import org.bson.types.ObjectId;

+import org.springframework.data.mongodb.core.MongoTemplate;

+import org.springframework.data.mongodb.core.query.Criteria;

+import org.springframework.data.mongodb.core.query.Query;

+import org.springframework.test.context.ActiveProfiles;

+

+

+@ActiveProfiles("test")

+public abstract class MemoryDatabase {

+  protected static MongodExecutable mongodExecutable;

+  protected static MongoTemplate mongoTemplate;

+

+  //TODO use mongod process to be response from mongodExecutable.start(), on pulbic calls check if null if so call setup else dont

+  protected static MongodProcess mongod = null;

+

+  protected static Query userQuery = new Query(Criteria.where("firstName").is("Mech"));

+  //protected static Query mechUserQuery = new Query(Criteria.where("firstName").is("Mech"));

+  protected static Query testInstanceQuery = new Query(Criteria.where("testInstanceName").is("MechTestInstance"));

+  protected static Query groupQuery = new Query(Criteria.where("groupName").is("MechGroup"));

+  protected static Query testDefQuery = new Query(Criteria.where("testName").is("MechTestDefinition"));

+

+  //values should match with DataConfig2

+  protected static int port=5555;

+  protected static String host="localhost";

+

+

+  public static void setup()throws Exception{

+    Command command = Command.MongoD;

+    IRuntimeConfig runtimeConfig = new RuntimeConfigBuilder()

+        .defaults(command)

+        .artifactStore(new ExtractedArtifactStoreBuilder()

+            .defaults(command)

+            .download(new DownloadConfigBuilder()

+                .defaultsForCommand(command)

+                .proxyFactory(new HttpProxyFactory("localhost",8080))))

+             .build();

+

+    //String host = "localhost";

+    //int port = 5555;

+

+    IMongodConfig mongodConfig = new MongodConfigBuilder().version(Main.PRODUCTION)

+        .net(new Net(host, port, Network.localhostIsIPv6()))

+        .build();

+    //MongodStarter starter = MongodStarter.getDefaultInstance();

+    MongodStarter starter = MongodStarter.getInstance(runtimeConfig);

+    mongodExecutable = starter.prepare(mongodConfig);

+    mongodExecutable.start();

+    mongoTemplate = new MongoTemplate(new MongoClient(host, port), "test");

+

+    DBObject objectToSave = BasicDBObjectBuilder.start()

+        .add("name", "john")

+        .get();

+    mongoTemplate.save(objectToSave, "collection");

+

+

+  }

+  /*

+  public static User createMechUser(){

+

+    User user = mongoTemplate.findOne(mechUserQuery, User.class);

+    if(user == null) {

+      user = new User();

+      user.setFirstName("Mech");

+      user.setLastName("Id");

+      user.setEmail("email@localhost");

+      mongoTemplate.save(user, "users");

+      user = mongoTemplate.findOne(mechUserQuery, User.class);

+    }

+    return user;

+  }

+

+   */

+  //TODO: make admin user be the mechid, this is because of AAF test will fail if random user is used

+  private static User createMechUserIfNotExists(){

+    User user = mongoTemplate.findOne(userQuery, User.class);

+    if(user == null) {

+      user = new User();

+      user.setFirstName("Mech");

+      user.setLastName("Id");

+      user.setEmail("email@localhost");

+      mongoTemplate.save(user, "users");

+      user = mongoTemplate.findOne(userQuery, User.class);

+    }

+    return user;

+

+  }

+  private static Group createMechGroupIfNotExists(){

+    User user = MemoryDatabase.createMechUserIfNotExists();

+    Group group = mongoTemplate.findOne(groupQuery, Group.class);

+    if(group == null) {

+      String groupName = "MechGroup";

+      group = new Group();

+      group.setOwnerId(user.get_id());

+      group.setGroupName(groupName);

+      group.setGroupDescription(groupName + " description");

+      mongoTemplate.save(group, "groups");

+      group = mongoTemplate.findOne(groupQuery, Group.class);

+    }

+    return group;

+  }

+  private static TestDefinition createMechTestDefinitionIfNotExists(){

+    TestDefinition testDefinition = mongoTemplate.findOne(testDefQuery, TestDefinition.class);

+    if(testDefinition == null){

+

+      BpmnInstance bpmnInstance = new BpmnInstance();

+      bpmnInstance.setDeployed(true);

+      bpmnInstance.setVersion(1);

+      List list = new ArrayList(Arrays.asList(bpmnInstance));

+

+      testDefinition = new TestDefinition();

+      testDefinition.setDisabled(false);

+      testDefinition.setBpmnInstances(list);

+      testDefinition.setTestName("MechTestDefinition");

+      testDefinition.setTestDescription("MechTestDefinition description");

+      testDefinition.setProcessDefinitionKey("MechTestDefinitionKey");

+      testDefinition.setCreatedBy(createMechUserIfNotExists().get_id());

+      testDefinition.setGroupId(createMechGroupIfNotExists().get_id());

+      testDefinition.setCreatedAt(new Timestamp(new Date().getTime()));

+      testDefinition.setUpdatedAt(new Timestamp(new Date().getTime()));

+      mongoTemplate.save(testDefinition, "testDefinitions");

+      testDefinition = mongoTemplate.findOne(testDefQuery, TestDefinition.class);

+    }

+    return testDefinition;

+

+  }

+

+

+  private static TestInstance createMechTestInstanceIfNotExists(){

+    TestInstance testInstance = mongoTemplate.findOne(testInstanceQuery, TestInstance.class);

+    User user = createMechUserIfNotExists();

+    UserGroup userGroup = new UserGroup();

+    if(testInstance == null){

+      testInstance = new TestInstance();

+      testInstance.setTestInstanceName("MechTestInstance");

+      testInstance.setTestInstanceDescription("MechTestInstance description");

+      testInstance.setCreatedBy(user.get_id());

+      testInstance.setGroupId(createMechGroupIfNotExists().get_id());

+      testInstance.setTestDefinitionId(createMechTestDefinitionIfNotExists().get_id());

+      testInstance.setMaxExecutionTimeInMillis(new Random().nextInt(5000));

+      testInstance.setUseLatestTestDefinition(true);

+      mongoTemplate.save(testInstance, "testInstances");

+      testInstance = mongoTemplate.findOne(testInstanceQuery, TestInstance.class);

+    }

+    userGroup.setGroupId(testInstance.getGroupId());

+    userGroup.setPermissions(Arrays.asList("Admin"));

+    user.setGroups(Arrays.asList(userGroup));

+    mongoTemplate.save(user, "users");

+    return testInstance;

+  }

+

+  public static void createGroups(){

+

+    MemoryDatabase.createMechUserIfNotExists();

+    List<String> groupNames = new ArrayList<>(Arrays.asList("Group1", "Group2", "Group3", "Group4", "Group5"));

+    groupNames.forEach(name->{

+      Group group = new Group();

+      User usr = mongoTemplate.findOne(userQuery, User.class);

+      group.setOwnerId(usr.get_id());

+      group.setGroupName(name);

+      group.setGroupDescription(name + " description");

+      mongoTemplate.save(group, "groups");

+    });

+

+  }

+

+  public static void createGroupsForPermission()

+  {

+    Group parentGroup = new Group();

+    Group firstChildGroup = new Group();

+    Group childOfChildGroup = new Group();

+    parentGroup.setGroupName("parent group");

+    firstChildGroup.setGroupName("first child group");

+    childOfChildGroup.setGroupName("child of child group");

+    Role adminRole = new Role();

+    Role devRole = new Role();

+    Role executorRole = new Role();

+    GroupMember parentMember = new GroupMember();

+    GroupMember firstChildMember = new GroupMember();

+    GroupMember childOfChildMember = new GroupMember();

+    //set up members

+    createUsers();

+    List<User> users = mongoTemplate.findAll(User.class,"users"); // this should be atleast 3 users

+    /*

+    set up

+    parent group -> members only with admin roles. Permission = "READ","WRITE","MANAGEMENT"

+    child group -> members only with admin and dev roles. Permission = "READ","WRITE", "MANAGEMENT

+    child of child group -> members with only executor roles. Permission = "EXECUTE

+     */

+    parentMember.setUserId(users.get(0).get_id());

+    parentMember.setRoles(Arrays.asList("admin"));

+    firstChildMember.setUserId(users.get(1).get_id());

+    firstChildMember.setRoles(Arrays.asList("dev","admin"));

+    childOfChildMember.setUserId(users.get(2).get_id());

+    childOfChildMember.setRoles(Arrays.asList("executor"));

+    //set up roles

+    adminRole.setRoleName("admin");

+    adminRole.setPermissions(Arrays.asList("READ","WRITE","MANAGEMENT"));

+    devRole.setRoleName("dev");

+    devRole.setPermissions(Arrays.asList("READ","WRITE"));

+    executorRole.setRoleName("executor");

+    executorRole.setPermissions(Arrays.asList("EXECUTE"));

+    List<Role> defaultRoles = new ArrayList<>();

+    defaultRoles.add(devRole);

+    defaultRoles.add(adminRole);

+    defaultRoles.add(executorRole);

+    //set up groups

+    parentGroup.setRoles(defaultRoles);

+    parentGroup.setMembers(Arrays.asList(parentMember));

+    firstChildGroup.setRoles(defaultRoles);

+    firstChildGroup.setMembers(Arrays.asList(firstChildMember));

+    childOfChildGroup.setRoles(defaultRoles);

+    childOfChildGroup.setMembers(Arrays.asList(childOfChildMember));

+    /*

+      set up parent tree

+      structure:

+      parentGroup

+          |

+      Child group

+          |

+      Child of child group

+     */

+    mongoTemplate.save(parentGroup,"groups");

+    mongoTemplate.save(firstChildGroup,"groups");

+    mongoTemplate.save(childOfChildGroup,"groups");

+    // query object so we can get the object id and set up parent ids

+    Query parentQ = new Query(Criteria.where("groupName").is("parent group"));

+    Query firstChildQ = new Query(Criteria.where("groupName").is("first child group"));

+    Query childOfChildQ = new Query(Criteria.where("groupName").is("child of child group"));

+    Group parentGroupDbObj = mongoTemplate.findOne(parentQ,Group.class);

+    Group firstChildDbObj = mongoTemplate.findOne(firstChildQ,Group.class);

+    Group childOfChildDbObj = mongoTemplate.findOne(childOfChildQ,Group.class);

+

+    firstChildDbObj.setParentGroupId(parentGroupDbObj.get_id());

+    childOfChildDbObj.setParentGroupId(firstChildDbObj.get_id());

+    mongoTemplate.save(firstChildDbObj);

+    mongoTemplate.save(childOfChildDbObj);

+  }

+

+  public static void createUsers(){

+    List<String> usersFirstNames = new ArrayList<>(Arrays.asList("Joe", "Jim", "Rick", "David", "Tony"));

+    List<String> usersLastNames = new ArrayList<>(Arrays.asList("Terry", "Roll", "Luis", "Perry"));

+      usersFirstNames.forEach(name->{

+        User user = new User();

+        int index = new Random().nextInt(usersFirstNames.size()-1);

+        user.setEmail(name+usersLastNames.get(index)+"@email.com");

+        user.setLastName(name);

+        user.setFirstName(usersLastNames.get(index));

+        mongoTemplate.save(user, "users");

+    });

+

+  }

+  public static void createTeatHeads(){

+    List<String> testheadNames = new ArrayList<>(Arrays.asList("SSH", "FTP", "PING", "PROCESS", "daRudeSandstorm"));

+    testheadNames.forEach(name->{

+      String random = Integer.toString(new Random().nextInt(4000)+4000);

+      TestHead testHead = new TestHead();

+      testHead.setTestHeadName(name);

+      testHead.setTestHeadDescription(name+" virtual test head ");

+      testHead.setPort(random);

+      testHead.setResourcePath("resources.vths.com/"+name);

+      testHead.setHostname("resources.vths.com");

+      testHead.setGroupId(createMechUserIfNotExists().get_id());

+      testHead.setGroupId(createMechGroupIfNotExists().get_id());

+      testHead.setCreatedAt(new Timestamp(new Date().getTime()));

+      testHead.setUpdatedAt(new Timestamp(new Date().getTime()));

+      mongoTemplate.save(testHead, "testHeads");

+

+    });

+  }

+  public static void createTestDefinitions(){

+    List<String> testDefinitionNames = new ArrayList<>(Arrays.asList("testDef1", "testDef2", "testDef3", "testDef4"));

+    testDefinitionNames.forEach(name->{

+      TestDefinition testDefinition = new TestDefinition();

+      testDefinition.setDisabled(false);

+      testDefinition.setTestName(name);

+      testDefinition.setTestDescription(name+" description");

+      testDefinition.setProcessDefinitionKey(name+"key");

+      testDefinition.setCreatedBy(createMechUserIfNotExists().get_id());

+      testDefinition.setGroupId(createMechGroupIfNotExists().get_id());

+      testDefinition.setCreatedAt(new Timestamp(new Date().getTime()));

+      testDefinition.setUpdatedAt(new Timestamp(new Date().getTime()));

+      mongoTemplate.save(testDefinition, "testDefinitions");

+    });

+  }

+  public static void createTestInstances(){

+    List<String> testInstanceName = new ArrayList<>(Arrays.asList("TestInstance1", "TestInstance2", "TestInstance3", "TestInstance4"));

+    testInstanceName.forEach(name->{

+      TestInstance testInstance = new TestInstance();

+      testInstance.setTestInstanceName(name);

+      testInstance.setTestInstanceDescription(name+" description");

+      testInstance.setCreatedBy(createMechUserIfNotExists().get_id());

+      testInstance.setGroupId(createMechGroupIfNotExists().get_id());

+      testInstance.setTestDefinitionId(createMechTestDefinitionIfNotExists().get_id());

+      testInstance.setMaxExecutionTimeInMillis(new Random().nextInt(5000));

+      testInstance.setUseLatestTestDefinition(true);

+      mongoTemplate.save(testInstance, "testInstances");

+    });

+  }

+

+  public static void createTestExecutions(){

+    List<String> results = new ArrayList<>(Arrays.asList("COMPLETED", "FAILED", "PASSED", "INCOMPLETE"));

+    results.forEach(result->{

+      TestExecution testExecution = new TestExecution();

+      testExecution.setAsync(false);

+      testExecution.setExecutorId(createMechUserIfNotExists().get_id());

+      testExecution.setGroupId(createMechGroupIfNotExists().get_id());

+      testExecution.setStartTime(new Timestamp(new Date().getTime()));

+      testExecution.setEndTime(new Timestamp(DateUtils.addHours(new Date(),3).getTime()));

+      testExecution.setTestResult(result);

+      testExecution.setTestResultMessage("Process result is: "+ result);

+      mongoTemplate.save(testExecution, "testExecutions");

+    });

+  }

+

+  public static void createAllAdmin(){

+    createMechTestDefinitionIfNotExists();

+    createMechTestInstanceIfNotExists();

+  }

+

+  public static void createAllTables()throws Exception{

+    setup();

+    createUsers();

+    createGroups();

+    createTeatHeads();

+    createTestDefinitions();

+    createTestInstances();

+    createTestExecutions();

+  }

+

+  public static void cleanup(){

+    mongodExecutable.stop();

+  }

+

+}

diff --git a/otf-service-api/src/test/java/org/oran/otf/api/tests/unit/models/DefinitionTest.java b/otf-service-api/src/test/java/org/oran/otf/api/tests/unit/models/DefinitionTest.java
new file mode 100644
index 0000000..be607d1
--- /dev/null
+++ b/otf-service-api/src/test/java/org/oran/otf/api/tests/unit/models/DefinitionTest.java
@@ -0,0 +1,82 @@
+/*  Copyright (c) 2019 AT&T Intellectual Property.                             #

+#                                                                              #

+#   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.oran.otf.api.tests.unit.models;

+

+import org.oran.otf.common.model.TestDefinition;

+import org.assertj.core.api.Assertions;

+import org.junit.BeforeClass;

+import org.junit.Test;

+

+public class DefinitionTest {

+  private static TestDefinition testDefinition;

+

+  @BeforeClass

+  public static void setup(){

+    testDefinition = new TestDefinition();

+  }

+

+  @Test

+  public void testDefinitionHasTestNameField(){

+    Assertions.assertThat(testDefinition).hasFieldOrProperty("testName");

+  }

+

+  @Test

+  public void testDefinitionHasTestDescriptionField(){

+    Assertions.assertThat(testDefinition).hasFieldOrProperty("testDescription");

+  }

+  @Test

+  public void testDefinitionHasProcessDefinitionKeyField(){

+    Assertions.assertThat(testDefinition).hasFieldOrProperty("processDefinitionKey");

+  }

+  @Test

+  public void testDefinitionHasBpmnInstancesField(){

+    Assertions.assertThat(testDefinition).hasFieldOrProperty("bpmnInstances");

+  }

+  @Test

+  public void testDefinitionHasGroupIdField(){

+    Assertions.assertThat(testDefinition).hasFieldOrProperty("groupId");

+  }

+  @Test

+  public void testDefinitionHasCreatedAtField(){

+    Assertions.assertThat(testDefinition).hasFieldOrProperty("createdAt");

+  }

+  @Test

+  public void testDefinitionHasUpdateAtField(){

+    Assertions.assertThat(testDefinition).hasFieldOrProperty("updatedAt");

+  }

+  @Test

+  public void testDefinitionHasCreatedByField(){

+    Assertions.assertThat(testDefinition).hasFieldOrProperty("createdBy");

+  }

+  @Test

+  public void testDefinitionHasUpdatedByField(){

+    Assertions.assertThat(testDefinition).hasFieldOrProperty("updatedBy");

+  }

+  @Test

+  public void testDefinitionHasDisabledField(){

+    Assertions.assertThat(testDefinition).hasFieldOrProperty("disabled");

+  }

+

+

+

+

+

+

+

+

+

+}

diff --git a/otf-service-api/src/test/java/org/oran/otf/api/tests/unit/models/ExecutionTest.java b/otf-service-api/src/test/java/org/oran/otf/api/tests/unit/models/ExecutionTest.java
new file mode 100644
index 0000000..966ee76
--- /dev/null
+++ b/otf-service-api/src/test/java/org/oran/otf/api/tests/unit/models/ExecutionTest.java
@@ -0,0 +1,107 @@
+/*  Copyright (c) 2019 AT&T Intellectual Property.                             #

+#                                                                              #

+#   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.oran.otf.api.tests.unit.models;

+

+import org.oran.otf.common.model.TestExecution;

+import org.assertj.core.api.Assertions;

+import org.junit.BeforeClass;

+import org.junit.Test;

+

+public class ExecutionTest {

+  private static TestExecution testExecution;

+

+  @BeforeClass

+  public static void setup(){

+    testExecution = new TestExecution();

+  }

+

+  @Test

+  public void testExecutionHasGroupIdField(){

+    Assertions.assertThat(testExecution).hasFieldOrProperty("groupId");

+  }

+  @Test

+  public void testExecutionHasExecutorIdField(){

+    Assertions.assertThat(testExecution).hasFieldOrProperty("executorId");

+  }

+  @Test

+  public void testExecutionHasAsyncField(){

+    Assertions.assertThat(testExecution).hasFieldOrProperty("async");

+  }

+  @Test

+  public void testExecutionHasStartTimeField(){

+    Assertions.assertThat(testExecution).hasFieldOrProperty("startTime");

+  }

+  @Test

+  public void testExecutionHasEndTimeField(){

+    Assertions.assertThat(testExecution).hasFieldOrProperty("endTime");

+  }

+  @Test

+  public void testExecutionHasAsyncTopicField(){

+

+    Assertions.assertThat(testExecution).hasFieldOrProperty("asyncTopic");

+  }

+  @Test

+  public void testExecutionHasBussinessKeyField(){

+

+    Assertions.assertThat(testExecution).hasFieldOrProperty("businessKey");

+  }

+  @Test

+  public void testExecutionHasProcessInstanceIdField(){

+    Assertions.assertThat(testExecution).hasFieldOrProperty("processInstanceId");

+  }

+  @Test

+  public void testExecutionHasTestResultField(){

+

+    Assertions.assertThat(testExecution).hasFieldOrProperty("testResult");

+  }

+  @Test

+  public void testExecutionHasTestResultMessageField(){

+

+    Assertions.assertThat(testExecution).hasFieldOrProperty("testResultMessage");

+  }

+  @Test

+  public void testExecutionHasTestDetailsField(){

+

+    Assertions.assertThat(testExecution).hasFieldOrProperty("testDetails");

+  }

+  @Test

+  public void testExecutionHasTestHeadResultsField(){

+

+    Assertions.assertThat(testExecution).hasFieldOrProperty("testHeadResults");

+  }

+  @Test

+  public void testExecutionHasTestInstanceResultsField(){

+

+    Assertions.assertThat(testExecution).hasFieldOrProperty("testInstanceResults");

+  }

+  @Test

+  public void testExecutionHasHistoricEmailField(){

+

+    Assertions.assertThat(testExecution).hasFieldOrProperty("historicEmail");

+  }

+  @Test

+  public void testExecutionHasHistoricTestInstanceField(){

+

+    Assertions.assertThat(testExecution).hasFieldOrProperty("historicTestInstance");

+  }

+  @Test

+  public void testExecutionHasHistoricTestDefinitionField(){

+

+    Assertions.assertThat(testExecution).hasFieldOrProperty("historicTestDefinition");

+  }

+

+}

diff --git a/otf-service-api/src/test/java/org/oran/otf/api/tests/unit/models/GroupTest.java b/otf-service-api/src/test/java/org/oran/otf/api/tests/unit/models/GroupTest.java
new file mode 100644
index 0000000..c28a406
--- /dev/null
+++ b/otf-service-api/src/test/java/org/oran/otf/api/tests/unit/models/GroupTest.java
@@ -0,0 +1,50 @@
+/*  Copyright (c) 2019 AT&T Intellectual Property.                             #

+#                                                                              #

+#   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.oran.otf.api.tests.unit.models;

+

+import static org.assertj.core.api.Assertions.assertThat;

+

+import org.oran.otf.common.model.Group;

+import org.junit.BeforeClass;

+import org.junit.Test;

+

+

+public class GroupTest {

+  private static Group group;

+  @BeforeClass

+  public static void setup(){

+    group = new Group();

+  }

+  @Test

+  public void testGroupHasNameField(){

+    assertThat(group).hasFieldOrProperty("groupName");

+  }

+  @Test

+  public void testGroupHasGroupDescriptionField(){

+    assertThat(group).hasFieldOrProperty("groupDescription");

+  }

+

+  @Test

+  public void testGroupHasMechanizedIdsField(){

+    assertThat(group).hasFieldOrProperty("mechanizedIds");

+  }

+

+  @Test

+  public void testGroupHasOwnerIdField(){

+    assertThat(group).hasFieldOrProperty("ownerId");

+  }

+}

diff --git a/otf-service-api/src/test/java/org/oran/otf/api/tests/unit/models/HeadTest.java b/otf-service-api/src/test/java/org/oran/otf/api/tests/unit/models/HeadTest.java
new file mode 100644
index 0000000..00ba7ca
--- /dev/null
+++ b/otf-service-api/src/test/java/org/oran/otf/api/tests/unit/models/HeadTest.java
@@ -0,0 +1,72 @@
+/*  Copyright (c) 2019 AT&T Intellectual Property.                             #

+#                                                                              #

+#   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.oran.otf.api.tests.unit.models;

+

+import org.oran.otf.common.model.TestHead;

+import org.assertj.core.api.Assertions;

+import org.junit.BeforeClass;

+import org.junit.Test;

+

+public class HeadTest {

+  private static TestHead testHead;

+

+  @BeforeClass

+  public static void setup(){

+    testHead = new TestHead();

+  }

+  @Test

+  public void testHeadHasTestHeadNameField(){

+    Assertions.assertThat(testHead).hasFieldOrProperty("testHeadName");

+  }

+  @Test

+  public void testHeadHasTestHeadDescriptionField(){

+    Assertions.assertThat(testHead).hasFieldOrProperty("testHeadDescription");

+  }

+  @Test

+  public void testHeadHasHostNameField(){

+    Assertions.assertThat(testHead).hasFieldOrProperty("hostname");

+  }

+  @Test

+  public void testHeadHasPortField(){

+    Assertions.assertThat(testHead).hasFieldOrProperty("port");

+  }

+  @Test

+  public void testHeadHasResourcePathField(){

+    Assertions.assertThat(testHead).hasFieldOrProperty("resourcePath");

+  }

+  @Test

+  public void testHeadHasCreatorIdField(){

+    Assertions.assertThat(testHead).hasFieldOrProperty("creatorId");

+  }

+  @Test

+  public void testHeadHasGroupIdField(){

+    Assertions.assertThat(testHead).hasFieldOrProperty("groupId");

+  }

+  @Test

+  public void testHeadHasCreatedAtField(){

+    Assertions.assertThat(testHead).hasFieldOrProperty("createdAt");

+  }

+  @Test

+  public void testHeadHasUpdatedAtField(){

+    Assertions.assertThat(testHead).hasFieldOrProperty("updatedAt");

+  }

+  @Test

+  public void testHeadHasUpdatedByField(){

+    Assertions.assertThat(testHead).hasFieldOrProperty("updatedBy");

+  }

+

+}

diff --git a/otf-service-api/src/test/java/org/oran/otf/api/tests/unit/models/InstanceTest.java b/otf-service-api/src/test/java/org/oran/otf/api/tests/unit/models/InstanceTest.java
new file mode 100644
index 0000000..3f1a4be
--- /dev/null
+++ b/otf-service-api/src/test/java/org/oran/otf/api/tests/unit/models/InstanceTest.java
@@ -0,0 +1,104 @@
+/*  Copyright (c) 2019 AT&T Intellectual Property.                             #

+#                                                                              #

+#   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.oran.otf.api.tests.unit.models;

+

+import org.oran.otf.common.model.TestInstance;

+import org.assertj.core.api.Assertions;

+import org.junit.BeforeClass;

+import org.junit.Test;

+

+public class InstanceTest {

+

+  private static TestInstance testInstance;

+

+  @BeforeClass

+  public static void setup(){

+    testInstance = new TestInstance();

+  }

+  @Test

+  public void testInstanceHasTestInstanceNameField(){

+    Assertions.assertThat(testInstance).hasFieldOrProperty("testInstanceName");

+  }

+  @Test

+  public void testInstanceHasInstanceDescriptionField(){

+    Assertions.assertThat(testInstance).hasFieldOrProperty("testInstanceDescription");

+  }

+  @Test

+  public void testInstanceHasGroupIdField(){

+    Assertions.assertThat(testInstance).hasFieldOrProperty("groupId");

+  }

+  @Test

+  public void testInstanceHasTestDefinitionIdField(){

+    Assertions.assertThat(testInstance).hasFieldOrProperty("testDefinitionId");

+  }

+  @Test

+  public void testInstanceHasProcessDefinitionIdField(){

+    Assertions.assertThat(testInstance).hasFieldOrProperty("processDefinitionId");

+  }

+  @Test

+  public void testInstanceHasUseLatestTestDefinitionField(){

+    Assertions.assertThat(testInstance).hasFieldOrProperty("useLatestTestDefinition");

+  }

+  @Test

+  public void testInstanceHasDisabledField(){

+    Assertions.assertThat(testInstance).hasFieldOrProperty("disabled");

+  }

+  @Test

+  public void testInstanceHasSimulationModeField(){

+    Assertions.assertThat(testInstance).hasFieldOrProperty("simulationMode");

+  }

+  @Test

+  public void testInstanceHasMaxExecutionTimeInMillisField(){

+    Assertions.assertThat(testInstance).hasFieldOrProperty("maxExecutionTimeInMillis");

+  }

+  @Test

+  public void testInstanceHasPfloInputField(){

+    Assertions.assertThat(testInstance).hasFieldOrProperty("pfloInput");

+  }

+  @Test

+  public void testInstanceHasInternalTestDataField(){

+    Assertions.assertThat(testInstance).hasFieldOrProperty("internalTestData");

+  }

+  @Test

+  public void testInstanceHasSimulationVthInputField(){

+    Assertions.assertThat(testInstance).hasFieldOrProperty("simulationVthInput");

+  }

+  @Test

+  public void testInstanceHasTestDataField(){

+    Assertions.assertThat(testInstance).hasFieldOrProperty("testData");

+  }

+  @Test

+  public void testInstanceHasVthInputField(){

+    Assertions.assertThat(testInstance).hasFieldOrProperty("vthInput");

+  }

+  @Test

+  public void testInstanceHasCreatedAtField(){

+    Assertions.assertThat(testInstance).hasFieldOrProperty("createdAt");

+  }

+  @Test

+  public void testInstanceHasUpdatedAtField(){

+    Assertions.assertThat(testInstance).hasFieldOrProperty("updatedAt");

+  }

+  @Test

+  public void testInstanceHasCreatedByField(){

+    Assertions.assertThat(testInstance).hasFieldOrProperty("createdBy");

+  }

+  @Test

+  public void testInstanceHasUpdatedByField(){

+    Assertions.assertThat(testInstance).hasFieldOrProperty("updatedBy");

+  }

+}

diff --git a/otf-service-api/src/test/java/org/oran/otf/api/tests/unit/models/UserTest.java b/otf-service-api/src/test/java/org/oran/otf/api/tests/unit/models/UserTest.java
new file mode 100644
index 0000000..49fb31c
--- /dev/null
+++ b/otf-service-api/src/test/java/org/oran/otf/api/tests/unit/models/UserTest.java
@@ -0,0 +1,63 @@
+/*  Copyright (c) 2019 AT&T Intellectual Property.                             #

+#                                                                              #

+#   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.oran.otf.api.tests.unit.models;

+

+import org.oran.otf.common.model.User;

+import org.assertj.core.api.Assertions;

+import org.junit.BeforeClass;

+import org.junit.Test;

+

+public class UserTest {

+

+  private static User user;

+  @BeforeClass

+  public static void setup(){

+    user = new User();

+  }

+  @Test

+  public void testUserHasPermissionsField(){

+    Assertions.assertThat(user).hasFieldOrProperty("permissions");

+  }

+  @Test

+  public void testUserHasFirstNameField(){

+    Assertions.assertThat(user).hasFieldOrProperty("firstName");

+  }

+  @Test

+  public void testUserHasLastNameField(){

+    Assertions.assertThat(user).hasFieldOrProperty("lastName");

+  }

+  @Test

+  public void testUserHasEmailField(){

+    Assertions.assertThat(user).hasFieldOrProperty("email");

+  }

+  @Test

+  public void testUserHasPasswordField(){

+    Assertions.assertThat(user).hasFieldOrProperty("password");

+  }

+  @Test

+  public void testUserHasGroupsField(){

+    Assertions.assertThat(user).hasFieldOrProperty("groups");

+  }

+  @Test

+  public void testUserHasCreatedAtField(){

+    Assertions.assertThat(user).hasFieldOrProperty("createdAt");

+  }

+  @Test

+  public void testUserHasUpdatedAtField(){

+    Assertions.assertThat(user).hasFieldOrProperty("updatedAt");

+  }

+}

diff --git a/otf-service-api/src/test/java/org/oran/otf/api/tests/unit/models/local/BpmnTest.java b/otf-service-api/src/test/java/org/oran/otf/api/tests/unit/models/local/BpmnTest.java
new file mode 100644
index 0000000..2f7f166
--- /dev/null
+++ b/otf-service-api/src/test/java/org/oran/otf/api/tests/unit/models/local/BpmnTest.java
@@ -0,0 +1,83 @@
+/*  Copyright (c) 2019 AT&T Intellectual Property.                             #

+#                                                                              #

+#   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.oran.otf.api.tests.unit.models.local;

+

+import org.oran.otf.common.model.local.BpmnInstance;

+import org.assertj.core.api.Assertions;

+import org.junit.BeforeClass;

+import org.junit.Test;

+

+public class BpmnTest {

+  private static BpmnInstance bpmnInstance;

+  @BeforeClass

+  public static void setup(){

+    bpmnInstance = new BpmnInstance();

+  }

+  @Test

+  public  void testBpmnInstanceHasProcessDefinitionIdField(){

+    Assertions.assertThat(bpmnInstance).hasFieldOrProperty("processDefinitionId");

+  }

+  @Test

+  public   void testBpmnInstanceHasDeploymentIdField(){

+    Assertions.assertThat(bpmnInstance).hasFieldOrProperty("deploymentId");

+  }

+  @Test

+  public  void testBpmnInstanceHasVersionField(){

+    Assertions.assertThat(bpmnInstance).hasFieldOrProperty("version");

+  }

+  @Test

+  public  void testBpmnInstanceHasBpmnFileIdField(){

+    Assertions.assertThat(bpmnInstance).hasFieldOrProperty("bpmnFileId");

+  }

+  @Test

+  public  void testBpmnInstanceHasResourceFileIdField(){

+    Assertions.assertThat(bpmnInstance).hasFieldOrProperty("resourceFileId");

+  }

+  @Test

+  public  void testBpmnInstanceHasIsDeployedField(){

+    Assertions.assertThat(bpmnInstance).hasFieldOrProperty("isDeployed");

+  }

+  @Test

+  public  void testBpmnInstanceHasTestHeadsField(){

+    Assertions.assertThat(bpmnInstance).hasFieldOrProperty("testHeads");

+  }

+  @Test

+  public  void testBpmnInstanceHasPflowsField(){

+    Assertions.assertThat(bpmnInstance).hasFieldOrProperty("pflos");

+  }

+  @Test

+  public  void testBpmnInstanceHasTestDataTemplateField(){

+    Assertions.assertThat(bpmnInstance).hasFieldOrProperty("testDataTemplate");

+  }

+  @Test

+  public  void testBpmnInstanceHasCreatedAtField(){

+    Assertions.assertThat(bpmnInstance).hasFieldOrProperty("createdAt");

+  }

+  @Test

+  public  void testBpmnInstanceUpdatedAtField(){

+    Assertions.assertThat(bpmnInstance).hasFieldOrProperty("updatedAt");

+  }

+  @Test

+  public  void testBpmnInstanceHasCreatedByField(){

+    Assertions.assertThat(bpmnInstance).hasFieldOrProperty("createdBy");

+  }

+  @Test

+  public  void testBpmnInstanceHasUpdatedByField(){

+    Assertions.assertThat(bpmnInstance).hasFieldOrProperty("updatedBy");

+  }

+

+}

diff --git a/otf-service-api/src/test/java/org/oran/otf/api/tests/unit/models/local/DeployTestStrategyRequestTest.java b/otf-service-api/src/test/java/org/oran/otf/api/tests/unit/models/local/DeployTestStrategyRequestTest.java
new file mode 100644
index 0000000..b41c4ee
--- /dev/null
+++ b/otf-service-api/src/test/java/org/oran/otf/api/tests/unit/models/local/DeployTestStrategyRequestTest.java
@@ -0,0 +1,44 @@
+/*  Copyright (c) 2019 AT&T Intellectual Property.                             #

+#                                                                              #

+#   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.oran.otf.api.tests.unit.models.local;

+

+import org.oran.otf.common.model.local.DeployTestStrategyRequest;

+import org.assertj.core.api.Assertions;

+import org.junit.BeforeClass;

+import org.junit.Test;

+

+public class DeployTestStrategyRequestTest {

+  private static DeployTestStrategyRequest deployTestStrategyRequest;

+

+  @BeforeClass

+  public static void setup(){

+    deployTestStrategyRequest = new DeployTestStrategyRequest();

+  }

+  @Test

+  public void testDeployTestStrategyRequestHasTestDefinitionDeployerIdField(){

+    Assertions.assertThat(deployTestStrategyRequest).hasFieldOrProperty("testDefinitionDeployerId");

+  }

+  @Test

+  public void testDeployTestStrategyRequestHasTestDefinitionIdField(){

+    Assertions.assertThat(deployTestStrategyRequest).hasFieldOrProperty("TestDefinitionId");

+  }

+  @Test

+  public void testDeployTestStrategyRequestHasDefinitionIdField(){

+    Assertions.assertThat(deployTestStrategyRequest).hasFieldOrProperty("DefinitionId");

+  }

+

+}

diff --git a/otf-service-api/src/test/java/org/oran/otf/api/tests/unit/models/local/HeadNodeTest.java b/otf-service-api/src/test/java/org/oran/otf/api/tests/unit/models/local/HeadNodeTest.java
new file mode 100644
index 0000000..fe0ce0f
--- /dev/null
+++ b/otf-service-api/src/test/java/org/oran/otf/api/tests/unit/models/local/HeadNodeTest.java
@@ -0,0 +1,40 @@
+/*  Copyright (c) 2019 AT&T Intellectual Property.                             #

+#                                                                              #

+#   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.oran.otf.api.tests.unit.models.local;

+

+import org.oran.otf.common.model.local.TestHeadNode;

+import org.assertj.core.api.Assertions;

+import org.junit.BeforeClass;

+import org.junit.Test;

+

+public class HeadNodeTest {

+  private static TestHeadNode testHeadNode;

+

+  @BeforeClass

+  public static void setup(){

+    testHeadNode = new TestHeadNode();

+  }

+  @Test

+  public void testHeadNodeHasTestHeadIdField(){

+    Assertions.assertThat(testHeadNode).hasFieldOrProperty("testHeadId");

+  }

+  @Test

+  public void testHeadNodeHasBpmnVthTaskIdField(){

+    Assertions.assertThat(testHeadNode).hasFieldOrProperty("bpmnVthTaskId");

+  }

+

+}

diff --git a/otf-service-api/src/test/java/org/oran/otf/api/tests/unit/models/local/HeadResultTest.java b/otf-service-api/src/test/java/org/oran/otf/api/tests/unit/models/local/HeadResultTest.java
new file mode 100644
index 0000000..1fde494
--- /dev/null
+++ b/otf-service-api/src/test/java/org/oran/otf/api/tests/unit/models/local/HeadResultTest.java
@@ -0,0 +1,60 @@
+/*  Copyright (c) 2019 AT&T Intellectual Property.                             #

+#                                                                              #

+#   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.oran.otf.api.tests.unit.models.local;

+

+import org.oran.otf.common.model.local.TestHeadResult;

+import org.assertj.core.api.Assertions;

+import org.junit.BeforeClass;

+import org.junit.Test;

+

+public class HeadResultTest {

+  private static TestHeadResult testHeadResult;

+

+  @BeforeClass

+  public static void setup(){

+    testHeadResult = new TestHeadResult();

+  }

+  @Test

+  public void testHeadResultHasTestHeadIdField(){

+    Assertions.assertThat(testHeadResult).hasFieldOrProperty("testHeadId");

+  }

+  @Test

+  public void testHeadResultHasTestHeadNameField(){

+    Assertions.assertThat(testHeadResult).hasFieldOrProperty("testHeadName");

+  }

+  @Test

+  public void testHeadResultHasBpmnVthTaskIdField(){

+    Assertions.assertThat(testHeadResult).hasFieldOrProperty("bpmnVthTaskId");

+  }

+  @Test

+  public void testHeadResultHasTestHeadRequestField(){

+    Assertions.assertThat(testHeadResult).hasFieldOrProperty("testHeadRequest");

+  }

+  @Test

+  public void testHeadResultHasTestHeadResponseField(){

+    Assertions.assertThat(testHeadResult).hasFieldOrProperty("testHeadResponse");

+  }

+  @Test

+  public void testHeadResultHasStartTimeField(){

+    Assertions.assertThat(testHeadResult).hasFieldOrProperty("startTime");

+  }

+  @Test

+  public void testHeadResultHasEndTimeField(){

+    Assertions.assertThat(testHeadResult).hasFieldOrProperty("endTime");

+  }

+

+}

diff --git a/otf-service-api/src/test/java/org/oran/otf/api/tests/unit/models/local/InstanceCreateRequestTest.java b/otf-service-api/src/test/java/org/oran/otf/api/tests/unit/models/local/InstanceCreateRequestTest.java
new file mode 100644
index 0000000..83fdf09
--- /dev/null
+++ b/otf-service-api/src/test/java/org/oran/otf/api/tests/unit/models/local/InstanceCreateRequestTest.java
@@ -0,0 +1,93 @@
+/*  Copyright (c) 2019 AT&T Intellectual Property.                             #

+#                                                                              #

+#   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.oran.otf.api.tests.unit.models.local;

+

+import org.oran.otf.common.model.local.TestInstanceCreateRequest;

+import org.assertj.core.api.Assertions;

+import org.junit.BeforeClass;

+import org.junit.Test;

+

+public class InstanceCreateRequestTest {

+  private static TestInstanceCreateRequest testInstanceCreateRequest;

+

+  @BeforeClass

+  public static void setup() throws Exception{

+    //No Argument Constructor does not work because of the requiered name when creating

+    testInstanceCreateRequest = new TestInstanceCreateRequest(

+        "Name",

+        "Description",

+        null,

+        null,

+        null,

+        null,

+        null,

+        true,

+        false,

+        0L

+    );

+  }

+

+  @Test

+  public void testInstanceCreateRequestHasTestDefinitionIdField(){

+    Assertions.assertThat(testInstanceCreateRequest).hasFieldOrProperty("testDefinitionId");

+  }

+  @Test

+  public void testInstanceCreateRequestHasVersionField(){

+    Assertions.assertThat(testInstanceCreateRequest).hasFieldOrProperty("version");

+  }

+  @Test

+  public void testInstanceCreateRequestHasProcessDefinitionKeyField(){

+    Assertions.assertThat(testInstanceCreateRequest).hasFieldOrProperty("processDefinitionKey");

+  }

+  @Test

+  public void testInstanceCreateRequestHastestInstanceNameField(){

+    Assertions.assertThat(testInstanceCreateRequest).hasFieldOrProperty("testInstanceName");

+  }

+  @Test

+  public void testInstanceCreateRequestHasPfloInputField(){

+    Assertions.assertThat(testInstanceCreateRequest).hasFieldOrProperty("pfloInput");

+  }

+  @Test

+  public void testInstanceCreateRequestHasSimulationVthInputField(){

+    Assertions.assertThat(testInstanceCreateRequest).hasFieldOrProperty("simulationVthInput");

+  }

+  @Test

+  public void testInstanceCreateRequestHasTestDataField(){

+    Assertions.assertThat(testInstanceCreateRequest).hasFieldOrProperty("testData");

+  }

+  @Test

+  public void testInstanceCreateRequestHasVthInputField(){

+    Assertions.assertThat(testInstanceCreateRequest).hasFieldOrProperty("vthInput");

+  }

+  @Test

+  public void testInstanceCreateRequestHasCreatedByField(){

+    Assertions.assertThat(testInstanceCreateRequest).hasFieldOrProperty("createdBy");

+  }

+  @Test

+  public void testInstanceCreateRequestHasUseLatestTestDefinitionField(){

+    Assertions.assertThat(testInstanceCreateRequest).hasFieldOrProperty("useLatestTestDefinition");

+  }

+  @Test

+  public void testInstanceCreateRequestHasSimulationModeField(){

+    Assertions.assertThat(testInstanceCreateRequest).hasFieldOrProperty("simulationMode");

+  }

+  @Test

+  public void testInstanceCreateRequestHasMaxExecutionTimeInMillisField(){

+    Assertions.assertThat(testInstanceCreateRequest).hasFieldOrProperty("maxExecutionTimeInMillis");

+  }

+

+}

diff --git a/otf-service-api/src/test/java/org/oran/otf/api/tests/unit/models/local/OtfApiResponseTest.java b/otf-service-api/src/test/java/org/oran/otf/api/tests/unit/models/local/OtfApiResponseTest.java
new file mode 100644
index 0000000..45c10a4
--- /dev/null
+++ b/otf-service-api/src/test/java/org/oran/otf/api/tests/unit/models/local/OtfApiResponseTest.java
@@ -0,0 +1,43 @@
+/*  Copyright (c) 2019 AT&T Intellectual Property.                             #

+#                                                                              #

+#   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.oran.otf.api.tests.unit.models.local;

+

+import org.oran.otf.common.model.local.OTFApiResponse;

+import org.assertj.core.api.Assertions;

+import org.junit.BeforeClass;

+import org.junit.Test;

+

+public class OtfApiResponseTest {

+  private static OTFApiResponse otfApiResponse;

+  @BeforeClass

+  public static void setup(){

+    otfApiResponse = new OTFApiResponse();

+  }

+  @Test

+  public void testOtfApiResponseHasStatusCodeField(){

+    Assertions.assertThat(otfApiResponse).hasFieldOrProperty("statusCode");

+  }

+  @Test

+  public void testOtfApiResponseHasMessageField(){

+    Assertions.assertThat(otfApiResponse).hasFieldOrProperty("message");

+  }

+  @Test

+  public void testOtfApiResponseHasTimeField(){

+    Assertions.assertThat(otfApiResponse).hasFieldOrProperty("time");

+  }

+

+}

diff --git a/otf-service-api/src/test/java/org/oran/otf/api/tests/unit/models/local/ParallelFlowInputTest.java b/otf-service-api/src/test/java/org/oran/otf/api/tests/unit/models/local/ParallelFlowInputTest.java
new file mode 100644
index 0000000..320df54
--- /dev/null
+++ b/otf-service-api/src/test/java/org/oran/otf/api/tests/unit/models/local/ParallelFlowInputTest.java
@@ -0,0 +1,47 @@
+/*  Copyright (c) 2019 AT&T Intellectual Property.                             #

+#                                                                              #

+#   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.oran.otf.api.tests.unit.models.local;

+

+import org.oran.otf.common.model.local.ParallelFlowInput;

+import org.assertj.core.api.Assertions;

+import org.junit.BeforeClass;

+import org.junit.Test;

+

+public class ParallelFlowInputTest {

+  private static ParallelFlowInput parallelFlowInput;

+  @BeforeClass

+  public static void setup(){

+    parallelFlowInput = new ParallelFlowInput();

+  }

+  @Test

+  public void testParallelFlowInputHasArgsField(){

+    Assertions.assertThat(parallelFlowInput).hasFieldOrProperty("args");

+  }

+  @Test

+  public void testParallelFlowInputHasInterruptOnFailureField(){

+    Assertions.assertThat(parallelFlowInput).hasFieldOrProperty("interruptOnFailure");

+  }

+  @Test

+  public void testParallelFlowInputHasMaxFailuresField(){

+    Assertions.assertThat(parallelFlowInput).hasFieldOrProperty("maxFailures");

+  }

+  @Test

+  public void testParallelFlowInputHasThreadPoolSizeField(){

+    Assertions.assertThat(parallelFlowInput).hasFieldOrProperty("threadPoolSize");

+  }

+

+}

diff --git a/otf-service-api/src/test/java/org/oran/otf/api/tests/unit/models/local/PfloNodeTest.java b/otf-service-api/src/test/java/org/oran/otf/api/tests/unit/models/local/PfloNodeTest.java
new file mode 100644
index 0000000..7a0a20b
--- /dev/null
+++ b/otf-service-api/src/test/java/org/oran/otf/api/tests/unit/models/local/PfloNodeTest.java
@@ -0,0 +1,40 @@
+/*  Copyright (c) 2019 AT&T Intellectual Property.                             #

+#                                                                              #

+#   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.oran.otf.api.tests.unit.models.local;

+

+import org.oran.otf.common.model.local.PfloNode;

+import org.assertj.core.api.Assertions;

+import org.junit.BeforeClass;

+import org.junit.Test;

+

+public class PfloNodeTest {

+  private static PfloNode pfloNode;

+

+  @BeforeClass

+  public static void setup(){

+    pfloNode = new PfloNode();

+  }

+  @Test

+  public void testPfloNodeHasBpmnPfloTaskIdField(){

+    Assertions.assertThat(pfloNode).hasFieldOrProperty("bpmnPlfoTaskId");

+  }

+  @Test

+  public void testPfloNodeHasLabelField(){

+    Assertions.assertThat(pfloNode).hasFieldOrProperty("label");

+  }

+

+}

diff --git a/otf-service-api/src/test/java/org/oran/otf/api/tests/unit/models/local/UserGroupTest.java b/otf-service-api/src/test/java/org/oran/otf/api/tests/unit/models/local/UserGroupTest.java
new file mode 100644
index 0000000..92e7497
--- /dev/null
+++ b/otf-service-api/src/test/java/org/oran/otf/api/tests/unit/models/local/UserGroupTest.java
@@ -0,0 +1,42 @@
+/*  Copyright (c) 2019 AT&T Intellectual Property.                             #

+#                                                                              #

+#   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.oran.otf.api.tests.unit.models.local;

+

+import org.oran.otf.common.model.local.UserGroup;

+import org.assertj.core.api.Assertions;

+import org.junit.BeforeClass;

+import org.junit.Test;

+

+public class UserGroupTest {

+  private static UserGroup userGroup;

+

+  //TODO (DONE): Added NoArg Constructor to UserGroup model for testing

+  @BeforeClass

+  public static void setup(){

+    userGroup = new UserGroup();

+  }

+

+  @Test

+  public void testUserGroupHasGroupIdField(){

+    Assertions.assertThat(userGroup).hasFieldOrProperty("groupId");

+  }

+  @Test

+  public void testUserGroupHasPermissionsField(){

+    Assertions.assertThat(userGroup).hasFieldOrProperty("permissions");

+  }

+

+}

diff --git a/otf-service-api/src/test/java/org/oran/otf/api/tests/unit/models/local/WorkFlowRequestTest.java b/otf-service-api/src/test/java/org/oran/otf/api/tests/unit/models/local/WorkFlowRequestTest.java
new file mode 100644
index 0000000..6debf07
--- /dev/null
+++ b/otf-service-api/src/test/java/org/oran/otf/api/tests/unit/models/local/WorkFlowRequestTest.java
@@ -0,0 +1,61 @@
+/*  Copyright (c) 2019 AT&T Intellectual Property.                             #

+#                                                                              #

+#   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.oran.otf.api.tests.unit.models.local;

+

+import org.oran.otf.common.model.local.WorkflowRequest;

+import org.assertj.core.api.Assertions;

+import org.junit.BeforeClass;

+import org.junit.Test;

+

+public class WorkFlowRequestTest {

+  private static WorkflowRequest workflowRequest;

+

+  @BeforeClass

+  public static void setup()throws Exception{

+    workflowRequest = new WorkflowRequest();

+  }

+

+  @Test

+  public void testWorkFlowRequestHasAsyncField(){

+    Assertions.assertThat(workflowRequest).hasFieldOrProperty("async");

+  }

+  @Test

+  public void testWorkFlowRequestHasExecutorIdField(){

+    Assertions.assertThat(workflowRequest).hasFieldOrProperty("executorId");

+  }

+  @Test

+  public void testWorkFlowRequestHasTestInstanceIdField(){

+    Assertions.assertThat(workflowRequest).hasFieldOrProperty("testInstanceId");

+  }

+  @Test

+  public void testWorkFlowRequestHasPfloInputField(){

+    Assertions.assertThat(workflowRequest).hasFieldOrProperty("pfloInput");

+  }

+  @Test

+  public void testWorkFlowRequestHasTestDataField(){

+    Assertions.assertThat(workflowRequest).hasFieldOrProperty("testData");

+  }

+  @Test

+  public void testWorkFlowRequestHasVthInputField(){

+    Assertions.assertThat(workflowRequest).hasFieldOrProperty("vthInput");

+  }

+  @Test

+  public void testWorkFlowRequestHasMaxExecutionTimeInMillisField(){

+    Assertions.assertThat(workflowRequest).hasFieldOrProperty("maxExecutionTimeInMillis");

+  }

+

+}

diff --git a/otf-service-api/src/test/java/org/oran/otf/api/tests/unit/utility/BuildResponseTest.java b/otf-service-api/src/test/java/org/oran/otf/api/tests/unit/utility/BuildResponseTest.java
new file mode 100644
index 0000000..15afaa3
--- /dev/null
+++ b/otf-service-api/src/test/java/org/oran/otf/api/tests/unit/utility/BuildResponseTest.java
@@ -0,0 +1,74 @@
+/*  Copyright (c) 2019 AT&T Intellectual Property.                             #

+#                                                                              #

+#   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.oran.otf.api.tests.unit.utility;

+

+import org.oran.otf.api.Utilities;

+import org.oran.otf.common.model.local.OTFApiResponse;

+import org.junit.Assert;

+import org.junit.Test;

+

+import javax.ws.rs.core.Response;

+

+public class BuildResponseTest {

+    @Test

+    public void badResponseTest(){

+        Response badResponse = Utilities.Http.BuildResponse.badRequest();

+        Assert.assertNotNull(badResponse);

+        Assert.assertEquals(badResponse.getStatus(),400);

+    }

+

+    @Test

+    public void badRequestWithMessageTest() {

+        Response badResponse = Utilities.Http.BuildResponse.badRequestWithMessage("this is bad");

+        OTFApiResponse response = (OTFApiResponse) badResponse.getEntity();

+

+        Assert.assertNotNull(badResponse);

+        Assert.assertEquals(badResponse.getStatus(),400);

+        Assert.assertEquals(response.getStatusCode(), 400);

+        Assert.assertEquals(response.getMessage(), "this is bad");

+    }

+    @Test

+    public void internalServerErrorTest(){

+        Response badResponse = Utilities.Http.BuildResponse.internalServerError();

+        Assert.assertNotNull(badResponse);

+        Assert.assertEquals(badResponse.getStatus(),500);

+    }

+    @Test

+    public void internalServerErrorWithMessageTest(){

+        Response badResponse = Utilities.Http.BuildResponse.internalServerErrorWithMessage("internal error");

+        OTFApiResponse response = (OTFApiResponse) badResponse.getEntity();

+

+        Assert.assertNotNull(badResponse);

+        Assert.assertEquals(badResponse.getStatus(),500);

+        Assert.assertEquals(response.getStatusCode(), 500);

+        Assert.assertEquals(response.getMessage(), "internal error");

+    }

+

+    @Test

+    public void unauthorizedTest(){

+        Response basicUnauthorizedResponse=  Utilities.Http.BuildResponse.unauthorized();

+        Response unauthorizedMsgResponse = Utilities.Http.BuildResponse.unauthorizedWithMessage("unauthorized");

+        OTFApiResponse response = (OTFApiResponse) unauthorizedMsgResponse.getEntity();

+

+        Assert.assertNotNull(basicUnauthorizedResponse);

+        Assert.assertNotNull(unauthorizedMsgResponse);

+        Assert.assertEquals(basicUnauthorizedResponse.getStatus(),401);

+        Assert.assertEquals(unauthorizedMsgResponse.getStatus(),401);

+        Assert.assertEquals(response.getStatusCode(),401);

+        Assert.assertEquals(response.getMessage(),"unauthorized");

+    }

+}

diff --git a/otf-service-api/src/test/java/org/oran/otf/api/tests/unit/utility/UserPermissionTest.java b/otf-service-api/src/test/java/org/oran/otf/api/tests/unit/utility/UserPermissionTest.java
new file mode 100644
index 0000000..e2d7954
--- /dev/null
+++ b/otf-service-api/src/test/java/org/oran/otf/api/tests/unit/utility/UserPermissionTest.java
@@ -0,0 +1,67 @@
+/*  Copyright (c) 2019 AT&T Intellectual Property.                             #

+#                                                                              #

+#   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.oran.otf.api.tests.unit.utility;

+

+import org.oran.otf.common.utility.permissions.UserPermission;

+import org.junit.Assert;

+import org.junit.Before;

+import org.junit.Test;

+import org.junit.runner.RunWith;

+import org.mockito.InjectMocks;

+import org.mockito.Mock;

+import org.mockito.Mockito;

+import org.mockito.junit.MockitoJUnitRunner;

+

+import java.util.*;

+

+@RunWith(MockitoJUnitRunner.class)

+public class UserPermissionTest {

+

+    @Mock

+    Map<String, Set<String>> userAccessMap ;

+

+    @InjectMocks

+    private UserPermission userPermission;

+

+    @Before

+    public void setUp()

+    {

+        String fakeGroupId1 = "abc123";

+        Set<String> user1Permissions = new HashSet<>(Arrays.asList("READ","WRITE"));

+        Mockito.when(userAccessMap.get(fakeGroupId1)).thenReturn(user1Permissions);

+    }

+

+    @Test

+    public void testHasAccessToMethod(){

+

+        Assert.assertNotNull(userPermission.getUserAccessMap());

+        //test when user have access to group with certain permissions and a fake permission(mix of upper and lower case

+        Assert.assertTrue(userPermission.hasAccessTo("abc123","READ"));

+        Assert.assertTrue(userPermission.hasAccessTo("abc123","WrIte"));

+        Assert.assertFalse(userPermission.hasAccessTo("abc123","DEleTE"));

+        Assert.assertFalse(userPermission.hasAccessTo("abc123","ExECUTe"));

+        Assert.assertFalse(userPermission.hasAccessTo("abc123","mANAgEMENT"));

+        Assert.assertFalse(userPermission.hasAccessTo("abc123","READ+WRITE"));

+

+        //test when user have no access to the group

+        Assert.assertFalse(userPermission.hasAccessTo("edf567","READ"));

+        Assert.assertFalse(userPermission.hasAccessTo("edf567","WRITE"));

+        Assert.assertFalse(userPermission.hasAccessTo("edf567","DELETE"));

+        Assert.assertFalse(userPermission.hasAccessTo("edf567","EXECUTE"));

+        Assert.assertFalse(userPermission.hasAccessTo("edf567","MANAGEMENT"));

+    }

+}

diff --git a/otf-service-api/src/test/resources/application-test.properties b/otf-service-api/src/test/resources/application-test.properties
new file mode 100644
index 0000000..a0a7d2a
--- /dev/null
+++ b/otf-service-api/src/test/resources/application-test.properties
@@ -0,0 +1,19 @@
+server.port=8443

+server.port.http=8181

+

+otf.mongo.hosts=${OTF_MONGO_HOSTS}

+otf.mongo.username=${OTF_MONGO_USERNAME}

+otf.mongo.password=${OTF_MONGO_PASSWORD}

+otf.mongo.replicaSet=${OTF_MONGO_REPLICASET}

+otf.mongo.database=${OTF_MONGO_DATABASE}

+

+cadi.prop.files=src/main/resources/cadi.properties

+

+otf.proxy=localhost

+otf.proxy-port=8080

+otf.embedded.host=localhost

+otf.embedded.port=5555

+otf.embedded.database=otf

+

+otf.mechid=${AAF_ID}

+otf.mechpass=${AAF_MECH_PASSWORD}

diff --git a/otf-service-api/swagger.json b/otf-service-api/swagger.json
new file mode 100644
index 0000000..2f5f6c8
--- /dev/null
+++ b/otf-service-api/swagger.json
@@ -0,0 +1 @@
+{"openapi":"3.0.1","info":{"title":"Open Test Framework API","description":"A RESTful API used to communicate with the OTF test control unit.","contact":{"name":"OTF","url":"https://localhost:32524"},"version":"1.0"},"tags":[{"name":"Health Service","description":"Query the availability of the API"},{"name":"Test Execution Service","description":"Query the status and history of your test executions"},{"name":"Test Instance Service","description":"Create, execute, and query test instances"},{"name":"Test Strategy Service","description":"Deploy and delete test strategies to and from the test control unit. (This documentation will only be available to the development team)"}],"paths":{"/otf/api/health/v1":{"get":{"tags":["Health Service"],"summary":"Checks if the test control unit is available","operationId":"getHealth_1","responses":{"200":{"description":"The test control unit is available","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiResponse"}}}}}}},"/otf/api/testExecution/v1/executionId/{executionId}":{"get":{"tags":["Test Execution Service"],"operationId":"getExecutionStatus_1","parameters":[{"name":"executionId","in":"path","required":true,"schema":{"type":"string"}},{"name":"Authorization","in":"header","schema":{"type":"string"}}],"responses":{"default":{"description":"default response","content":{"application/json":{}}}}}},"/otf/api/testInstance/execute/v1/id/{testInstanceId}":{"post":{"tags":["Test Instance Service"],"summary":"Executes a test instance by it's unique identifier","operationId":"execute_1","parameters":[{"name":"testInstanceId","in":"path","description":"A string representation of a BSON ObjectId","required":true,"schema":{"type":"string","description":"The UUID of the test instance","format":"uuid"},"example":"12345678912345678912345f"},{"name":"Authorization","in":"header","description":"Base64 encoded Application Authorization Framework credentials","required":true,"schema":{"type":"string"},"example":"Basic b3RmQGF0dC5jb206cGFzc3dvcmQxMjM="}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ExecuteTestInstanceRequest"}}}},"responses":{"200":{"description":"A successful synchronously executed test returns a test execution object","content":{"application/json":{"schema":{"$ref":"#/components/schemas/TestExecutionResult"}}}},"201":{"description":"A successful asynchronously executed test with asyncMode set to 'poll' returns an execution identifier\nThe identifier can be used as a parameter to the Test Execution Service to check the status of the executed test","content":{"application/json":{"schema":{"$ref":"#/components/schemas/TestExecutionResult"}}}},"401":{"description":"The mechanized identifier used with the request is prohibited from accessing the resource.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiResponse"}}}}}}},"/otf/api/testInstance/v1/id/{id}":{"get":{"tags":["Test Instance Service"],"operationId":"findById_1","parameters":[{"name":"id","in":"path","description":"A string representation of a BSON ObjectId","required":true,"schema":{"type":"string","description":"The UUID of the test instance","format":"uuid"},"example":"12345678912345678912345f"},{"name":"Authorization","in":"header","description":"Base64 encoded Application Authorization Framework credentials","required":true,"schema":{"type":"string"},"example":"Basic b3RmQGF0dC5jb206cGFzc3dvcmQxMjM="}],"responses":{"default":{"description":"default response","content":{"application/json":{}}}}}},"/otf/api/testInstance/create/v1/testDefinitionId/{testDefinitionId}/version/{version}":{"post":{"tags":["Test Instance Service"],"summary":"Create a test instance using the specified version of the test definition","operationId":"createByTestDefinitionIdAndVersion_1","parameters":[{"name":"testDefinitionId","in":"path","description":"A string representation of a BSON ObjectId","required":true,"schema":{"type":"string","description":"The UUID of the test definition.","format":"uuid"},"example":"12345678912345678912345f"},{"name":"version","in":"path","description":"The version of the test definition used to create the instance","required":true,"schema":{"type":"string"},"example":2},{"name":"Authorization","in":"header","description":"Base64 encoded Application Authorization Framework credentials","required":true,"schema":{"type":"string"},"example":"Basic b3RmQGF0dC5jb206cGFzc3dvcmQxMjM="},{"name":"execute","in":"query","description":"Execute the test instance after it is created","allowEmptyValue":true,"schema":{"type":"boolean"},"example":true}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreateTestInstanceRequest"}}}},"responses":{"201":{"description":"The created Test Instance object is returned when it is created","content":{"application/json":{"schema":{"$ref":"#/components/schemas/TestInstance"}}}}}}},"/otf/api/testInstance/v1/testInstanceName/{testInstanceName}":{"get":{"tags":["Test Instance Service"],"summary":"Finds a test instance by it's name","operationId":"findByTestInstanceName_1","parameters":[{"name":"testInstanceName","in":"path","description":"The name of the test instance to retrieve","required":true,"schema":{"type":"string"},"example":"myTestInstance"},{"name":"Authorization","in":"header","description":"Base64 encoded Application Authorization Framework credentials","required":true,"schema":{"type":"string"},"example":"Basic b3RmQGF0dC5jb206cGFzc3dvcmQxMjM="}],"responses":{"200":{"description":"A test instance object is returned when if it is found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/TestInstance"}}}}}}},"/otf/api/testInstance/v1/processDefinitionKey/{processDefinitionKey}":{"get":{"tags":["Test Instance Service"],"operationId":"findByProcessDefKey_1","parameters":[{"name":"processDefinitionKey","in":"path","description":"The process definition key associated with the test definition","required":true,"schema":{"type":"string"},"example":"someUniqueProcessDefinitionKey"},{"name":"Authorization","in":"header","description":"Base64 encoded Application Authorization Framework credentials","required":true,"schema":{"type":"string"},"example":"Basic b3RmQGF0dC5jb206cGFzc3dvcmQxMjM="}],"responses":{"default":{"description":"default response","content":{"application/json":{}}}}}},"/otf/api/testInstance/create/v1/testDefinitionId/{testDefinitionId}":{"post":{"tags":["Test Instance Service"],"summary":"Create a test instance using the latest version of the test definition","operationId":"createByTestDefinitionId_1","parameters":[{"name":"testDefinitionId","in":"path","description":"A string representation of a BSON ObjectId","required":true,"schema":{"type":"string","description":"The UUID of the test definition","format":"uuid"},"example":"12345678912345678912345f"},{"name":"Authorization","in":"header","description":"Base64 encoded Application Authorization Framework credentials","required":true,"schema":{"type":"string"},"example":"Basic b3RmQGF0dC5jb206cGFzc3dvcmQxMjM="},{"name":"execute","in":"query","description":"Execute the test instance after it is created","allowEmptyValue":true,"schema":{"type":"boolean"},"example":true}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreateTestInstanceRequest"}}}},"responses":{"201":{"description":"The created Test Instance object is returned when it is created","content":{"application/json":{"schema":{"$ref":"#/components/schemas/TestInstance"}}}}}}},"/otf/api/testInstance/v1/processDefinitionKey/{processDefinitionKey}/version/{version}":{"get":{"tags":["Test Instance Service"],"operationId":"findByProcessDefKeyAndVersion_1","parameters":[{"name":"processDefinitionKey","in":"path","description":"The process definition key associated with the test definition","required":true,"schema":{"type":"string"},"example":"someUniqueProcessDefinitionKey"},{"name":"version","in":"path","description":"The version of the test definition used to create the instance","required":true,"schema":{"type":"string"},"example":2},{"name":"Authorization","in":"header","description":"Base64 encoded Application Authorization Framework credentials","required":true,"schema":{"type":"string"},"example":"Basic b3RmQGF0dC5jb206cGFzc3dvcmQxMjM="}],"responses":{"default":{"description":"default response","content":{"application/json":{}}}}}},"/otf/api/testInstance/create/v1/processDefinitionKey/{processDefinitionKey}/version/{version}":{"post":{"tags":["Test Instance Service"],"summary":"Create a test instance using the specified version of the test definition","operationId":"createByProcessDefKeyAndVersion_1","parameters":[{"name":"processDefinitionKey","in":"path","description":"The process definition key associated with the test definition","required":true,"schema":{"type":"string"},"example":"someUniqueProcessDefinitionKey"},{"name":"version","in":"path","description":"The version of the test definition used to create the instance","required":true,"schema":{"type":"string"},"example":2},{"name":"Authorization","in":"header","description":"Base64 encoded Application Authorization Framework credentials","required":true,"schema":{"type":"string"},"example":"Basic b3RmQGF0dC5jb206cGFzc3dvcmQxMjM="},{"name":"execute","in":"query","description":"Execute the test instance after it is created","allowEmptyValue":true,"schema":{"type":"boolean"},"example":true}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreateTestInstanceRequest"}}}},"responses":{"201":{"description":"The created Test Instance object is returned when it is created","content":{"application/json":{"schema":{"$ref":"#/components/schemas/TestInstance"}}}}}}},"/otf/api/testInstance/create/v1/processDefinitionKey/{processDefinitionKey}":{"post":{"tags":["Test Instance Service"],"summary":"Create a test instance using the latest version of the test definition","operationId":"createByProcessDefKey_1","parameters":[{"name":"processDefinitionKey","in":"path","description":"The process definition key associated with the test definition","required":true,"schema":{"type":"string"},"example":"someUniqueProcessDefinitionKey"},{"name":"Authorization","in":"header","description":"Base64 encoded Application Authorization Framework credentials","required":true,"schema":{"type":"string"},"example":"Basic b3RmQGF0dC5jb206cGFzc3dvcmQxMjM="},{"name":"execute","in":"query","description":"Execute the test instance after it is created","allowEmptyValue":true,"schema":{"type":"boolean"},"example":true}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreateTestInstanceRequest"}}}},"responses":{"201":{"description":"The created Test Instance object is returned when it is created","content":{"application/json":{"schema":{"$ref":"#/components/schemas/TestInstance"}}}}}}},"/otf/api/testStrategy/delete/v1/deploymentId/{deploymentId}":{"delete":{"tags":["Test Strategy Service"],"operationId":"deleteByDeploymentId_1","parameters":[{"name":"deploymentId","in":"path","required":true,"schema":{"type":"string"}},{"name":"authorization","in":"header","schema":{"type":"string"}}],"responses":{"default":{"description":"default response","content":{"application/json":{}}}}}},"/otf/api/testStrategy/delete/v1/testDefinitionId/{testDefinitionId}":{"delete":{"tags":["Test Strategy Service"],"operationId":"deleteByTestDefinitionId_1","parameters":[{"name":"testDefinitionId","in":"path","required":true,"schema":{"type":"string"}},{"name":"authorization","in":"header","schema":{"type":"string"}}],"responses":{"default":{"description":"default response","content":{"application/json":{}}}}}},"/otf/api/testStrategy/deploy/v1":{"post":{"tags":["Test Strategy Service"],"operationId":"deployTestStrategy_1","parameters":[{"name":"Authorization","in":"header","schema":{"type":"string"}}],"requestBody":{"content":{"multipart/form-data":{"schema":{"type":"object","properties":{"bpmn":{"type":"object"},"resources":{"type":"object"},"testDefinitionId":{"type":"string"},"testDefinitionDeployerId":{"type":"string"},"definitionId":{"type":"string"}}}}}},"responses":{"default":{"description":"default response","content":{"application/json":{}}}}}},"/otf/api/application.wadl/{path}":{"get":{"operationId":"getExternalGrammar","parameters":[{"name":"path","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"default":{"description":"default response","content":{"application/xml":{}}}}}},"/otf/api/application.wadl":{"get":{"operationId":"getWadl","responses":{"default":{"description":"default response","content":{"application/vnd.sun.wadl+xml":{},"application/xml":{}}}}}}},"components":{"schemas":{"ApiResponse":{"type":"object","properties":{"code":{"type":"integer","format":"int32"},"date":{"type":"string","format":"date-time"},"message":{"type":"string"}}},"JSONObject":{"type":"object"},"ObjectId":{"type":"object","properties":{"timestamp":{"type":"integer","format":"int32"},"machineIdentifier":{"type":"integer","format":"int32"},"processIdentifier":{"type":"integer","format":"int32"},"counter":{"type":"integer","format":"int32"},"time":{"type":"integer","format":"int64"},"date":{"type":"string","format":"date-time"},"timeSecond":{"type":"integer","format":"int32"}}},"TestExecution":{"type":"object","properties":{"get_id":{"$ref":"#/components/schemas/ObjectId"},"executionId":{"type":"string"},"testResult":{"type":"string"},"testDetails":{"type":"object","additionalProperties":{"type":"object"}},"startTime":{"type":"string","format":"date-time"},"endTime":{"type":"string","format":"date-time"},"async":{"type":"boolean"},"asyncTopic":{"type":"string"},"asyncMode":{"type":"string"},"executor":{"type":"string"},"groupId":{"$ref":"#/components/schemas/ObjectId"},"testInstanceId":{"$ref":"#/components/schemas/ObjectId"},"testInstance":{"type":"object","additionalProperties":{"type":"object"}},"testHeadResults":{"type":"array","items":{"$ref":"#/components/schemas/TestHeadResult"}},"testDetailsJSON":{"type":"string"},"testInstanceJSON":{"type":"string"}}},"TestExecutionResult":{"type":"object","properties":{"testExecution":{"$ref":"#/components/schemas/TestExecution"},"executionId":{"type":"string"},"testCompleted":{"type":"boolean"},"testExists":{"type":"boolean"}}},"TestHeadResult":{"type":"object","properties":{"testHeadId":{"$ref":"#/components/schemas/ObjectId"},"testHeadName":{"type":"string"},"bpmnVthTaskId":{"type":"string"},"testHeadResponse":{"type":"object","additionalProperties":{"type":"object"}},"startTime":{"type":"string","format":"date-time"},"endTime":{"type":"string","format":"date-time"},"testHeadResponseJSON":{"$ref":"#/components/schemas/JSONObject"}}},"ExecuteTestInstanceRequest":{"type":"object","properties":{"async":{"type":"boolean","writeOnly":true},"asyncTopic":{"title":"Execute the test synchronously or asynchronously..","type":"string","description":"Ignored unless async is true, and asyncMode is DMaaP.","example":"MyDMaaPTopic."},"asyncMode":{"title":"Set the asynchronous execution mode.","type":"string","description":"Ignored unless async is true. The poll mode will return an executionId that can be used to query the result of the executed test. DMaaP is currently unsupported.","example":"POLL","enum":["POLL","DMAAP"]},"testData":{"title":"Use an existing test instance with different global test data.","type":"object","description":"Overrides (not overwrites) the testData field for the requested execution. The overridden data will be preserved in the test execution result.","example":{"globalVar1":"I'm available to your workflow!","globalVar2":{"me":"too"}}},"vthInput":{"title":"Use an existing test instance with different inputs to your VTHs.","type":"object","description":"Overrides (not overwrites) the vthInput field for the requested execution. The overridden data will be preserved in the test execution result.","example":{"ServiceTask_123":{"vthArg1":"An argument your VTH expects.","vthArg2":{}},"ServiceTask_456":{"vthArg1":"An argument your VTH expects."}}}},"description":"The model for a test instance execution request."},"TestInstance":{"type":"object","properties":{"get_id":{"$ref":"#/components/schemas/ObjectId"},"testInstanceName":{"type":"string"},"testInstanceDescription":{"type":"string"},"groupId":{"$ref":"#/components/schemas/ObjectId"},"testDefinitionId":{"$ref":"#/components/schemas/ObjectId"},"processDefinitionId":{"type":"string"},"useLatestTestDefinition":{"type":"boolean"},"testData":{"type":"object","additionalProperties":{"type":"object"}},"vthInput":{"type":"object","additionalProperties":{"type":"object"}},"internalTestData":{"type":"object","additionalProperties":{"type":"object"}},"createdAt":{"type":"string","format":"date-time"},"updatedAt":{"type":"string","format":"date-time"},"createdBy":{"$ref":"#/components/schemas/ObjectId"},"updatedBy":{"$ref":"#/components/schemas/ObjectId"},"vthInputJSON":{"$ref":"#/components/schemas/JSONObject"},"testDataJSON":{"$ref":"#/components/schemas/JSONObject"},"internalTestDataJSON":{"$ref":"#/components/schemas/JSONObject"}}},"CreateTestInstanceRequest":{"required":["testData","testInstanceDescription","testInstanceName"],"type":"object","properties":{"testInstanceName":{"title":"Name the test instance","type":"string","description":"The name must be unique among all test instances belonging to the same test definition.","example":"MyTestInstance"},"testInstanceDescription":{"title":"Describe the test instance being created","type":"string","description":"Use this field to describe the functionality of the test instance","example":"This test instance does absolutely nothing!"},"testData":{"title":"Set global variables","type":"object","description":"This field has read and write access by any task within the workflow.\nSee the example for more information","example":{"globalVar1":"I'm available to your workflow!","globalVar2":{"me":"too"}}},"vthInput":{"title":"Set virtual test head data","type":"object","description":"This field determines the data each VTH at the designated ServiceTask will receive.\nSee the example for more information","example":{"ServiceTask_123":{"vthArg1":"An argument your VTH expects.","vthArg2":{}},"ServiceTask_456":{"vthArg1":"An argument your VTH expects."}}},"async":{"type":"boolean"},"asyncTopic":{"type":"string"},"asyncMode":{"type":"string"}},"description":"The model for a test instance creation request."}}}}
\ No newline at end of file
diff --git a/otf-service-api/swagger.yml b/otf-service-api/swagger.yml
new file mode 100644
index 0000000..7bae19f
--- /dev/null
+++ b/otf-service-api/swagger.yml
@@ -0,0 +1,714 @@
+openapi: 3.0.1

+info:

+  title: Open Test Framework API

+  description: A RESTful API used to communicate with the OTF test control unit.

+  contact:

+    name: OTF

+    url: https://localhost:32524

+  version: "1.0"

+tags:

+- name: Health Service

+  description: Query the availability of the API

+- name: Test Execution Service

+  description: Query the status and history of your test executions

+- name: Test Instance Service

+  description: Create, execute,and query test instances

+- name: Test Strategy Service

+  description: Deploy and delete test strategies to and from the test control unit.

+    (This documentation will only be available to the development team)

+paths:

+  /otf/api/health/v1:

+    get:

+      tags:

+      - Health Service

+      summary: Checks if the test control unit is available

+      operationId: getHealth_1

+      responses:

+        200:

+          description: The test control unit is available

+          content:

+            application/json:

+              schema:

+                $ref: '#/components/schemas/OtfApiResponse'

+  /otf/api/testExecution/v1/executionId/{executionId}:

+    get:

+      tags:

+      - Test Execution Service

+      operationId: getExecutionStatus_1

+      parameters:

+      - name: executionId

+        in: path

+        required: true

+        schema:

+          type: string

+      - name: Authorization

+        in: header

+        schema:

+          type: string

+      responses:

+        default:

+          description: default response

+          content:

+            application/json: {}

+  /otf/api/testInstance/execute/v1/id/{testInstanceId}:

+    post:

+      tags:

+      - Test Instance Service

+      summary: Executes a test instance by it's unique identifier

+      operationId: execute_1

+      parameters:

+      - name: testInstanceId

+        in: path

+        description: A string representation of a BSON ObjectId

+        required: true

+        schema:

+          type: string

+          description: The UUID of the test instance

+          format: uuid

+        example: 12345678912345678912345f

+      - name: Authorization

+        in: header

+        description: Base64 encoded Application Authorization Framework credentials

+        required: true

+        schema:

+          type: string

+        example: Basic b3RmQGF0dC5jb206cGFzc3dvcmQxMjM=

+      requestBody:

+        content:

+          application/json:

+            schema:

+              $ref: '#/components/schemas/ExecuteTestInstanceRequest'

+      responses:

+        200:

+          description: A successful synchronously executed test returns a test execution

+            object

+          content:

+            application/json:

+              schema:

+                $ref: '#/components/schemas/TestExecutionResult'

+        201:

+          description: |-

+            A successful asynchronously executed test with asyncMode set to 'poll' returns an execution identifier

+            The identifier can be used as a parameter to the Test Execution Service to check the status of the executed test

+          content:

+            application/json:

+              schema:

+                $ref: '#/components/schemas/TestExecutionResult'

+        401:

+          description: The mechanized identifier used with the request is prohibited

+            from accessing the resource.

+          content:

+            application/json:

+              schema:

+                $ref: '#/components/schemas/OtfApiResponse'

+  /otf/api/testInstance/v1/id/{id}:

+    get:

+      tags:

+      - Test Instance Service

+      operationId: findById_1

+      parameters:

+      - name: id

+        in: path

+        description: A string representation of a BSON ObjectId

+        required: true

+        schema:

+          type: string

+          description: The UUID of the test instance

+          format: uuid

+        example: 12345678912345678912345f

+      - name: Authorization

+        in: header

+        description: Base64 encoded Application Authorization Framework credentials

+        required: true

+        schema:

+          type: string

+        example: Basic b3RmQGF0dC5jb206cGFzc3dvcmQxMjM=

+      responses:

+        default:

+          description: default response

+          content:

+            application/json: {}

+  /otf/api/testInstance/create/v1/testDefinitionId/{testDefinitionId}/version/{version}:

+    post:

+      tags:

+      - Test Instance Service

+      summary: Create a test instance using the specified version of the test definition

+      operationId: createByTestDefinitionIdAndVersion_1

+      parameters:

+      - name: testDefinitionId

+        in: path

+        description: A string representation of a BSON ObjectId

+        required: true

+        schema:

+          type: string

+          description: The UUID of the test definition.

+          format: uuid

+        example: 12345678912345678912345f

+      - name: version

+        in: path

+        description: The version of the test definition used to create the instance

+        required: true

+        schema:

+          type: string

+        example: 2

+      - name: Authorization

+        in: header

+        description: Base64 encoded Application Authorization Framework credentials

+        required: true

+        schema:

+          type: string

+        example: Basic b3RmQGF0dC5jb206cGFzc3dvcmQxMjM=

+      - name: execute

+        in: query

+        description: Execute the test instance after it is created

+        allowEmptyValue: true

+        schema:

+          type: boolean

+        example: true

+      requestBody:

+        content:

+          application/json:

+            schema:

+              $ref: '#/components/schemas/CreateTestInstanceRequest'

+      responses:

+        201:

+          description: The created Test Instance object is returned when it is created

+          content:

+            application/json:

+              schema:

+                $ref: '#/components/schemas/TestInstance'

+  /otf/api/testInstance/v1/testInstanceName/{testInstanceName}:

+    get:

+      tags:

+      - Test Instance Service

+      summary: Finds a test instance by it's name

+      operationId: findByTestInstanceName_1

+      parameters:

+      - name: testInstanceName

+        in: path

+        description: The name of the test instance to retrieve

+        required: true

+        schema:

+          type: string

+        example: myTestInstance

+      - name: Authorization

+        in: header

+        description: Base64 encoded Application Authorization Framework credentials

+        required: true

+        schema:

+          type: string

+        example: Basic b3RmQGF0dC5jb206cGFzc3dvcmQxMjM=

+      responses:

+        200:

+          description: A test instance object is returned when if it is found

+          content:

+            application/json:

+              schema:

+                $ref: '#/components/schemas/TestInstance'

+  /otf/api/testInstance/v1/processDefinitionKey/{processDefinitionKey}:

+    get:

+      tags:

+      - Test Instance Service

+      operationId: findByProcessDefKey_1

+      parameters:

+      - name: processDefinitionKey

+        in: path

+        description: The process definition key associated with the test definition

+        required: true

+        schema:

+          type: string

+        example: someUniqueProcessDefinitionKey

+      - name: Authorization

+        in: header

+        description: Base64 encoded Application Authorization Framework credentials

+        required: true

+        schema:

+          type: string

+        example: Basic b3RmQGF0dC5jb206cGFzc3dvcmQxMjM=

+      responses:

+        default:

+          description: default response

+          content:

+            application/json: {}

+  /otf/api/testInstance/create/v1/testDefinitionId/{testDefinitionId}:

+    post:

+      tags:

+      - Test Instance Service

+      summary: Create a test instance using the latest version of the test definition

+      operationId: createByTestDefinitionId_1

+      parameters:

+      - name: testDefinitionId

+        in: path

+        description: A string representation of a BSON ObjectId

+        required: true

+        schema:

+          type: string

+          description: The UUID of the test definition

+          format: uuid

+        example: 12345678912345678912345f

+      - name: Authorization

+        in: header

+        description: Base64 encoded Application Authorization Framework credentials

+        required: true

+        schema:

+          type: string

+        example: Basic b3RmQGF0dC5jb206cGFzc3dvcmQxMjM=

+      - name: execute

+        in: query

+        description: Execute the test instance after it is created

+        allowEmptyValue: true

+        schema:

+          type: boolean

+        example: true

+      requestBody:

+        content:

+          application/json:

+            schema:

+              $ref: '#/components/schemas/CreateTestInstanceRequest'

+      responses:

+        201:

+          description: The created Test Instance object is returned when it is created

+          content:

+            application/json:

+              schema:

+                $ref: '#/components/schemas/TestInstance'

+  /otf/api/testInstance/v1/processDefinitionKey/{processDefinitionKey}/version/{version}:

+    get:

+      tags:

+      - Test Instance Service

+      operationId: findByProcessDefKeyAndVersion_1

+      parameters:

+      - name: processDefinitionKey

+        in: path

+        description: The process definition key associated with the test definition

+        required: true

+        schema:

+          type: string

+        example: someUniqueProcessDefinitionKey

+      - name: version

+        in: path

+        description: The version of the test definition used to create the instance

+        required: true

+        schema:

+          type: string

+        example: 2

+      - name: Authorization

+        in: header

+        description: Base64 encoded Application Authorization Framework credentials

+        required: true

+        schema:

+          type: string

+        example: Basic b3RmQGF0dC5jb206cGFzc3dvcmQxMjM=

+      responses:

+        default:

+          description: default response

+          content:

+            application/json: {}

+  /otf/api/testInstance/create/v1/processDefinitionKey/{processDefinitionKey}/version/{version}:

+    post:

+      tags:

+      - Test Instance Service

+      summary: Create a test instance using the specified version of the test definition

+      operationId: createByProcessDefKeyAndVersion_1

+      parameters:

+      - name: processDefinitionKey

+        in: path

+        description: The process definition key associated with the test definition

+        required: true

+        schema:

+          type: string

+        example: someUniqueProcessDefinitionKey

+      - name: version

+        in: path

+        description: The version of the test definition used to create the instance

+        required: true

+        schema:

+          type: string

+        example: 2

+      - name: Authorization

+        in: header

+        description: Base64 encoded Application Authorization Framework credentials

+        required: true

+        schema:

+          type: string

+        example: Basic b3RmQGF0dC5jb206cGFzc3dvcmQxMjM=

+      - name: execute

+        in: query

+        description: Execute the test instance after it is created

+        allowEmptyValue: true

+        schema:

+          type: boolean

+        example: true

+      requestBody:

+        content:

+          application/json:

+            schema:

+              $ref: '#/components/schemas/CreateTestInstanceRequest'

+      responses:

+        201:

+          description: The created Test Instance object is returned when it is created

+          content:

+            application/json:

+              schema:

+                $ref: '#/components/schemas/TestInstance'

+  /otf/api/testInstance/create/v1/processDefinitionKey/{processDefinitionKey}:

+    post:

+      tags:

+      - Test Instance Service

+      summary: Create a test instance using the latest version of the test definition

+      operationId: createByProcessDefKey_1

+      parameters:

+      - name: processDefinitionKey

+        in: path

+        description: The process definition key associated with the test definition

+        required: true

+        schema:

+          type: string

+        example: someUniqueProcessDefinitionKey

+      - name: Authorization

+        in: header

+        description: Base64 encoded Application Authorization Framework credentials

+        required: true

+        schema:

+          type: string

+        example: Basic b3RmQGF0dC5jb206cGFzc3dvcmQxMjM=

+      - name: execute

+        in: query

+        description: Execute the test instance after it is created

+        allowEmptyValue: true

+        schema:

+          type: boolean

+        example: true

+      requestBody:

+        content:

+          application/json:

+            schema:

+              $ref: '#/components/schemas/CreateTestInstanceRequest'

+      responses:

+        201:

+          description: The created Test Instance object is returned when it is created

+          content:

+            application/json:

+              schema:

+                $ref: '#/components/schemas/TestInstance'

+  /otf/api/testStrategy/delete/v1/deploymentId/{deploymentId}:

+    delete:

+      tags:

+      - Test Strategy Service

+      operationId: deleteByDeploymentId_1

+      parameters:

+      - name: deploymentId

+        in: path

+        required: true

+        schema:

+          type: string

+      - name: authorization

+        in: header

+        schema:

+          type: string

+      responses:

+        default:

+          description: default response

+          content:

+            application/json: {}

+  /otf/api/testStrategy/delete/v1/testDefinitionId/{testDefinitionId}:

+    delete:

+      tags:

+      - Test Strategy Service

+      operationId: deleteByTestDefinitionId_1

+      parameters:

+      - name: testDefinitionId

+        in: path

+        required: true

+        schema:

+          type: string

+      - name: authorization

+        in: header

+        schema:

+          type: string

+      responses:

+        default:

+          description: default response

+          content:

+            application/json: {}

+  /otf/api/testStrategy/deploy/v1:

+    post:

+      tags:

+      - Test Strategy Service

+      operationId: deployTestStrategy_1

+      parameters:

+      - name: Authorization

+        in: header

+        schema:

+          type: string

+      requestBody:

+        content:

+          multipart/form-data:

+            schema:

+              type: object

+              properties:

+                bpmn:

+                  type: object

+                resources:

+                  type: object

+                testDefinitionId:

+                  type: string

+                testDefinitionDeployerId:

+                  type: string

+                definitionId:

+                  type: string

+      responses:

+        default:

+          description: default response

+          content:

+            application/json: {}

+components:

+  schemas:

+    ApiResponse:

+      type: object

+      properties:

+        code:

+          type: integer

+          format: int32

+        date:

+          type: string

+          format: date-time

+        message:

+          type: string

+    JSONObject:

+      type: object

+    ObjectId:

+      type: object

+      properties:

+        timestamp:

+          type: integer

+          format: int32

+        machineIdentifier:

+          type: integer

+          format: int32

+        processIdentifier:

+          type: integer

+          format: int32

+        counter:

+          type: integer

+          format: int32

+        time:

+          type: integer

+          format: int64

+        date:

+          type: string

+          format: date-time

+        timeSecond:

+          type: integer

+          format: int32

+    TestExecution:

+      type: object

+      properties:

+        get_id:

+          $ref: '#/components/schemas/ObjectId'

+        executionId:

+          type: string

+        testResult:

+          type: string

+        testDetails:

+          type: object

+          additionalProperties:

+            type: object

+        startTime:

+          type: string

+          format: date-time

+        endTime:

+          type: string

+          format: date-time

+        async:

+          type: boolean

+        asyncTopic:

+          type: string

+        asyncMode:

+          type: string

+        executor:

+          type: string

+        groupId:

+          $ref: '#/components/schemas/ObjectId'

+        testInstanceId:

+          $ref: '#/components/schemas/ObjectId'

+        testInstance:

+          type: object

+          additionalProperties:

+            type: object

+        testHeadResults:

+          type: array

+          items:

+            $ref: '#/components/schemas/TestHeadResult'

+        testDetailsJSON:

+          type: string

+        testInstanceJSON:

+          type: string

+    TestExecutionResult:

+      type: object

+      properties:

+        testExecution:

+          $ref: '#/components/schemas/TestExecution'

+        executionId:

+          type: string

+        testCompleted:

+          type: boolean

+        testExists:

+          type: boolean

+    TestHeadResult:

+      type: object

+      properties:

+        testHeadId:

+          $ref: '#/components/schemas/ObjectId'

+        testHeadName:

+          type: string

+        bpmnVthTaskId:

+          type: string

+        testHeadResponse:

+          type: object

+          additionalProperties:

+            type: object

+        startTime:

+          type: string

+          format: date-time

+        endTime:

+          type: string

+          format: date-time

+        testHeadResponseJSON:

+          $ref: '#/components/schemas/JSONObject'

+    ExecuteTestInstanceRequest:

+      type: object

+      properties:

+        async:

+          type: boolean

+          writeOnly: true

+        asyncTopic:

+          title: Execute the test synchronously or asynchronously..

+          type: string

+          description: Ignored unless async is true, and asyncMode is DMaaP.

+          example: MyDMaaPTopic.

+        asyncMode:

+          title: Set the asynchronous execution mode.

+          type: string

+          description: Ignored unless async is true. The poll mode will return an

+            executionId that can be used to query the result of the executed test.

+            DMaaP is currently unsupported.

+          example: POLL

+          enum:

+          - POLL

+          - DMAAP

+        testData:

+          title: Use an existing test instance with different global test data.

+          type: object

+          description: Overrides (not overwrites) the testData field for the requested

+            execution. The overridden data will be preserved in the test execution

+            result.

+          example:

+            globalVar1: I'm available to your workflow!

+            globalVar2:

+              me: too

+        vthInput:

+          title: Use an existing test instance with different inputs to your VTHs.

+          type: object

+          description: Overrides (not overwrites) the vthInput field for the requested

+            execution. The overridden data will be preserved in the test execution

+            result.

+          example:

+            ServiceTask_123:

+              vthArg1: An argument your VTH expects.

+              vthArg2: {}

+            ServiceTask_456:

+              vthArg1: An argument your VTH expects.

+      description: The model2 for a test instance execution request.

+    TestInstance:

+      type: object

+      properties:

+        get_id:

+          $ref: '#/components/schemas/ObjectId'

+        testInstanceName:

+          type: string

+        testInstanceDescription:

+          type: string

+        groupId:

+          $ref: '#/components/schemas/ObjectId'

+        testDefinitionId:

+          $ref: '#/components/schemas/ObjectId'

+        processDefinitionId:

+          type: string

+        useLatestTestDefinition:

+          type: boolean

+        testData:

+          type: object

+          additionalProperties:

+            type: object

+        vthInput:

+          type: object

+          additionalProperties:

+            type: object

+        internalTestData:

+          type: object

+          additionalProperties:

+            type: object

+        createdAt:

+          type: string

+          format: date-time

+        updatedAt:

+          type: string

+          format: date-time

+        createdBy:

+          $ref: '#/components/schemas/ObjectId'

+        updatedBy:

+          $ref: '#/components/schemas/ObjectId'

+        vthInputJSON:

+          $ref: '#/components/schemas/JSONObject'

+        testDataJSON:

+          $ref: '#/components/schemas/JSONObject'

+        internalTestDataJSON:

+          $ref: '#/components/schemas/JSONObject'

+    CreateTestInstanceRequest:

+      required:

+      - testData

+      - testInstanceDescription

+      - testInstanceName

+      type: object

+      properties:

+        testInstanceName:

+          title: Name the test instance

+          type: string

+          description: The name must be unique among all test instances belonging

+            to the same test definition.

+          example: MyTestInstance

+        testInstanceDescription:

+          title: Describe the test instance being created

+          type: string

+          description: Use this field to describe the functionality of the test instance

+          example: This test instance does absolutely nothing!

+        testData:

+          title: Set global variables

+          type: object

+          description: |-

+            This field has read and write access by any task within the workflow.

+            See the example for more information

+          example:

+            globalVar1: I'm available to your workflow!

+            globalVar2:

+              me: too

+        vthInput:

+          title: Set virtual test head data

+          type: object

+          description: |-

+            This field determines the data each VTH at the designated ServiceTask will receive.

+            See the example for more information

+          example:

+            ServiceTask_123:

+              vthArg1: An argument your VTH expects.

+              vthArg2: {}

+            ServiceTask_456:

+              vthArg1: An argument your VTH expects.

+        async:

+          type: boolean

+        asyncTopic:

+          type: string

+        asyncMode:

+          type: string

+      description: The model2 for a test instance creation request.