added svcapi ui and camunda code

Signed-off-by: Rohan Patel <rp5811@att.com>
Change-Id: I197b4b40fe3d047a417479214e471ae26d51fb2b
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