aboutsummaryrefslogtreecommitdiffstats
path: root/src/main
diff options
context:
space:
mode:
authorMatthieuGeerebaert <matthieu.geerebaert@orange.com>2018-03-28 13:36:26 +0200
committerMatthieuGeerebaert <matthieu.geerebaert@orange.com>2018-04-03 23:35:04 +0200
commit99bf586a6eb9799c4f33e43976d741f2807ea287 (patch)
tree817406c30c99f366c000afc3fe0963b6b32499e7 /src/main
parent65fd3da0ea090f3c1fc82cea0c49547dbf362fcf (diff)
Add serviceCatalog rest services
- Add get and find serviceSpecification operations - Add API exceptions management - Consumes SDC apis - Add tests - Fix pom.xml conflict Change-Id: Id103d83dc8a981885100daabe868cdf18a922975 Issue-ID: EXTAPI-39 Signed-off-by: MatthieuGeerebaert <matthieu.geerebaert@orange.com>
Diffstat (limited to 'src/main')
-rw-r--r--src/main/java/org/onap/nbi/OnapComponentsUrlPaths.java29
-rw-r--r--src/main/java/org/onap/nbi/apis/RestConfiguration.java18
-rw-r--r--src/main/java/org/onap/nbi/apis/servicecatalog/SdcClient.java145
-rw-r--r--src/main/java/org/onap/nbi/apis/servicecatalog/ServiceSpecificationResource.java46
-rw-r--r--src/main/java/org/onap/nbi/apis/servicecatalog/ServiceSpecificationService.java51
-rw-r--r--src/main/java/org/onap/nbi/apis/servicecatalog/ToscaInfosProcessor.java249
-rw-r--r--src/main/java/org/onap/nbi/apis/servicecatalog/jolt/FindServiceSpecJsonTransformer.java36
-rw-r--r--src/main/java/org/onap/nbi/apis/servicecatalog/jolt/GetServiceSpecJsonTransformer.java37
-rw-r--r--src/main/java/org/onap/nbi/exceptions/ApiError.java67
-rw-r--r--src/main/java/org/onap/nbi/exceptions/ApiException.java33
-rw-r--r--src/main/java/org/onap/nbi/exceptions/ApiExceptionHandler.java46
-rw-r--r--src/main/java/org/onap/nbi/exceptions/BackendErrorHandler.java24
-rw-r--r--src/main/java/org/onap/nbi/exceptions/BackendFunctionalException.java22
-rw-r--r--src/main/java/org/onap/nbi/exceptions/TechnicalException.java26
-rw-r--r--src/main/java/org/onap/nbi/exceptions/ValidationException.java32
-rw-r--r--src/main/resources/application-localhost.properties7
-rw-r--r--src/main/resources/application.properties3
-rw-r--r--src/main/resources/jolt/findServiceCatalog.json28
-rw-r--r--src/main/resources/jolt/getServiceCatalog.json77
19 files changed, 975 insertions, 1 deletions
diff --git a/src/main/java/org/onap/nbi/OnapComponentsUrlPaths.java b/src/main/java/org/onap/nbi/OnapComponentsUrlPaths.java
new file mode 100644
index 0000000..5ee6528
--- /dev/null
+++ b/src/main/java/org/onap/nbi/OnapComponentsUrlPaths.java
@@ -0,0 +1,29 @@
+package org.onap.nbi;
+
+/**
+ * Contains ONAP SDC and AAI urlPaths
+ *
+ */
+public final class OnapComponentsUrlPaths {
+
+ private OnapComponentsUrlPaths() {}
+
+ // SDC
+ public static final String SDC_ROOT_URL = "/sdc/v1/catalog/services/";
+ public static final String SDC_GET_PATH = "/metadata";
+ public static final String SDC_TOSCA_PATH = "/toscaModel";
+
+ // AAI
+ public static final String AAI_GET_TENANTS_PATH =
+ "/aai/v11/cloud-infrastructure/cloud-regions/cloud-region/$cloudOwner/$lcpCloudRegionId/tenants";
+ public static final String AAI_GET_CUSTOMER_PATH = "/aai/v11/business/customers/customer/";
+ public static final String AAI_GET_SERVICES_FOR_CUSTOMER_PATH =
+ "/aai/v11/business/customers/customer/$customerId/service-subscriptions";
+ public static final String AAI_PUT_SERVICE_FOR_CUSTOMER_PATH =
+ "/aai/v11/business/customers/customer/$customerId/service-subscriptions/service-subscription/";
+ public static final String AAI_GET_SERVICE_FOR_CUSTOMER_PATH =
+ "/aai/v11/business/customers/customer/$customerId/service-subscriptions/service-subscription/$serviceSpecName/service-instances/service-instance/$serviceId";
+ public static final String AAI_GET_SERVICE_INSTANCES_PATH =
+ "/aai/v11/business/customers/customer/$customerId/service-subscriptions/service-subscription/$serviceSpecName/service-instances/";
+
+}
diff --git a/src/main/java/org/onap/nbi/apis/RestConfiguration.java b/src/main/java/org/onap/nbi/apis/RestConfiguration.java
new file mode 100644
index 0000000..8152d6f
--- /dev/null
+++ b/src/main/java/org/onap/nbi/apis/RestConfiguration.java
@@ -0,0 +1,18 @@
+package org.onap.nbi.apis;
+
+import org.onap.nbi.exceptions.BackendErrorHandler;
+import org.springframework.boot.web.client.RestTemplateBuilder;
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+import org.springframework.web.client.RestTemplate;
+
+@Configuration
+public class RestConfiguration {
+
+ @Bean
+ public RestTemplate restTemplate(RestTemplateBuilder builder) {
+ RestTemplate restTemplate = builder.build();
+ restTemplate.setErrorHandler(new BackendErrorHandler());
+ return restTemplate;
+ }
+}
diff --git a/src/main/java/org/onap/nbi/apis/servicecatalog/SdcClient.java b/src/main/java/org/onap/nbi/apis/servicecatalog/SdcClient.java
new file mode 100644
index 0000000..abc8bda
--- /dev/null
+++ b/src/main/java/org/onap/nbi/apis/servicecatalog/SdcClient.java
@@ -0,0 +1,145 @@
+package org.onap.nbi.apis.servicecatalog;
+
+import java.io.File;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.util.LinkedHashMap;
+import java.util.List;
+import java.util.Map;
+import org.apache.commons.io.IOUtils;
+import org.onap.nbi.OnapComponentsUrlPaths;
+import org.onap.nbi.exceptions.BackendFunctionalException;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.beans.factory.annotation.Value;
+import org.springframework.http.HttpEntity;
+import org.springframework.http.HttpHeaders;
+import org.springframework.http.HttpMethod;
+import org.springframework.http.HttpStatus;
+import org.springframework.http.ResponseEntity;
+import org.springframework.stereotype.Service;
+import org.springframework.util.MultiValueMap;
+import org.springframework.web.client.RestTemplate;
+import org.springframework.web.util.UriComponentsBuilder;
+
+/**
+ * @author user
+ *
+ */
+@Service
+public class SdcClient {
+
+ public static final String HTTP_CALL_SDC_ON = "HTTP call SDC on ";
+ @Autowired
+ private RestTemplate restTemplate;
+
+ @Value("${sdc.host}")
+ private String sdcHost;
+
+ @Value("${sdc.header.ecompInstanceId}")
+ private String ecompInstanceId;
+
+ @Value("${sdc.header.authorization}")
+ private String sdcHeaderAuthorization;
+
+ private static final String HEADER_ECOMP_INSTANCE_ID = "x-ecomp-instanceid";
+ private static final String HEADER_AUTHORIZATION = "Authorization";
+
+ private static final Logger LOGGER = LoggerFactory.getLogger(SdcClient.class);
+
+
+ public LinkedHashMap callGet(String id) {
+ StringBuilder callURL = new StringBuilder().append(sdcHost).append(OnapComponentsUrlPaths.SDC_ROOT_URL).append(id)
+ .append(OnapComponentsUrlPaths.SDC_GET_PATH);
+
+ ResponseEntity<Object> response = callSdc(callURL.toString());
+ return (LinkedHashMap) response.getBody();
+
+ }
+
+ public List<LinkedHashMap> callFind(MultiValueMap<String, String> parametersMap) {
+
+ UriComponentsBuilder callURL = UriComponentsBuilder.fromHttpUrl(sdcHost + OnapComponentsUrlPaths.SDC_ROOT_URL);
+ if (parametersMap != null) {
+ Map<String, String> stringStringMap = parametersMap.toSingleValueMap();
+ for (String key : stringStringMap.keySet()) {
+ if (!key.equals("fields")) {
+ callURL.queryParam(key, stringStringMap.get(key));
+ }
+ }
+ }
+
+ ResponseEntity<Object> response = callSdc(callURL.build().encode().toUri().toString());
+ return (List<LinkedHashMap>) response.getBody();
+
+ }
+
+
+ public File callGetWithAttachment(String toscaModelUrl) {
+ StringBuilder callURL = new StringBuilder().append(sdcHost).append(toscaModelUrl);
+
+ String fileName = System.currentTimeMillis() + "tosca.csar";
+ ResponseEntity<byte[]> response = callSdcWithAttachment(callURL.toString());
+ File toscaFile = new File(fileName);
+ try {
+ FileOutputStream toscaFileStream = new FileOutputStream(toscaFile);
+ if (response != null) {
+ IOUtils.write(response.getBody(), toscaFileStream);
+ }
+ toscaFileStream.close();
+ } catch (IOException e) {
+ LOGGER.error("cannot get TOSCA File for url " + toscaModelUrl);
+ }
+ return toscaFile;
+
+ }
+
+ private HttpEntity<String> buildRequestHeader() {
+ HttpHeaders httpHeaders = new HttpHeaders();
+ httpHeaders.add(HEADER_ECOMP_INSTANCE_ID, ecompInstanceId);
+ httpHeaders.add(HEADER_AUTHORIZATION, sdcHeaderAuthorization);
+ HttpEntity<String> entity = new HttpEntity<>("parameters", httpHeaders);
+
+ return entity;
+ }
+
+
+ private ResponseEntity<Object> callSdc(String callURL) {
+ ResponseEntity<Object> response =
+ restTemplate.exchange(callURL, HttpMethod.GET, buildRequestHeader(), Object.class);
+ LOGGER.debug("response body : " + response.getBody().toString());
+ LOGGER.info("response status : " + response.getStatusCodeValue());
+ loggDebugIfResponseKo(callURL, response);
+ return response;
+ }
+
+ private void loggDebugIfResponseKo(String callURL, ResponseEntity<Object> response) {
+ if (!response.getStatusCode().equals(HttpStatus.OK)) {
+ LOGGER.warn(HTTP_CALL_SDC_ON + callURL + " returns " + response.getStatusCodeValue() + ", "
+ + response.getBody().toString());
+ }
+ }
+
+ private ResponseEntity<byte[]> callSdcWithAttachment(String callURL) {
+ try {
+ ResponseEntity<byte[]> response =
+ restTemplate.exchange(callURL.toString(), HttpMethod.GET, buildRequestHeader(), byte[].class);
+ LOGGER.info("response status : " + response.getStatusCodeValue());
+ if (!response.getStatusCode().equals(HttpStatus.OK)) {
+ LOGGER.warn(HTTP_CALL_SDC_ON + callURL.toString() + " returns " + response.getStatusCodeValue() + ", "
+ + response.getBody().toString());
+ }
+ return response;
+
+ } catch (BackendFunctionalException e) {
+ LOGGER.error(HTTP_CALL_SDC_ON + callURL.toString() + " error " + e);
+
+ return null;
+ }
+ }
+
+
+}
+
+
diff --git a/src/main/java/org/onap/nbi/apis/servicecatalog/ServiceSpecificationResource.java b/src/main/java/org/onap/nbi/apis/servicecatalog/ServiceSpecificationResource.java
new file mode 100644
index 0000000..512b088
--- /dev/null
+++ b/src/main/java/org/onap/nbi/apis/servicecatalog/ServiceSpecificationResource.java
@@ -0,0 +1,46 @@
+package org.onap.nbi.apis.servicecatalog;
+
+import java.util.LinkedHashMap;
+import java.util.List;
+import org.onap.nbi.commons.JsonRepresentation;
+import org.onap.nbi.commons.Resource;
+import org.onap.nbi.commons.ResourceManagement;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.http.MediaType;
+import org.springframework.http.ResponseEntity;
+import org.springframework.util.MultiValueMap;
+import org.springframework.web.bind.annotation.GetMapping;
+import org.springframework.web.bind.annotation.PathVariable;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.RequestParam;
+import org.springframework.web.bind.annotation.RestController;
+
+@RestController
+@RequestMapping("/serviceSpecification")
+public class ServiceSpecificationResource extends ResourceManagement<Resource> {
+
+
+ @Autowired
+ ServiceSpecificationService serviceSpecificationService;
+
+ @GetMapping(value = "/{serviceSpecId}", produces = MediaType.APPLICATION_JSON_VALUE)
+ public ResponseEntity<Object> getServiceSpecification(@PathVariable String serviceSpecId,
+ @RequestParam MultiValueMap<String, String> params) {
+ LinkedHashMap response = serviceSpecificationService.get(serviceSpecId);
+ JsonRepresentation filter = new JsonRepresentation(params);
+ if (response.get("serviceSpecCharacteristic") != null) {
+ return this.getResponse(response, filter);
+ } else {
+ return this.getPartialResponse(response, filter);
+
+ }
+ }
+
+ @GetMapping(value = "", produces = MediaType.APPLICATION_JSON_VALUE)
+ public ResponseEntity<Object> findServiceSpecification(@RequestParam MultiValueMap<String, String> params) {
+ List<LinkedHashMap> response = serviceSpecificationService.find(params);
+ JsonRepresentation filter = new JsonRepresentation(params);
+ return this.findResponse(response, filter, null);
+ }
+
+}
diff --git a/src/main/java/org/onap/nbi/apis/servicecatalog/ServiceSpecificationService.java b/src/main/java/org/onap/nbi/apis/servicecatalog/ServiceSpecificationService.java
new file mode 100644
index 0000000..3b56819
--- /dev/null
+++ b/src/main/java/org/onap/nbi/apis/servicecatalog/ServiceSpecificationService.java
@@ -0,0 +1,51 @@
+package org.onap.nbi.apis.servicecatalog;
+
+import java.util.LinkedHashMap;
+import java.util.List;
+import org.onap.nbi.apis.servicecatalog.jolt.FindServiceSpecJsonTransformer;
+import org.onap.nbi.apis.servicecatalog.jolt.GetServiceSpecJsonTransformer;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Service;
+import org.springframework.util.MultiValueMap;
+
+@Service
+public class ServiceSpecificationService {
+
+ @Autowired
+ SdcClient sdcClient;
+
+ @Autowired
+ GetServiceSpecJsonTransformer getServiceSpecJsonTransformer;
+
+ @Autowired
+ FindServiceSpecJsonTransformer findServiceSpecJsonTransformer;
+
+ @Autowired
+ ToscaInfosProcessor toscaInfosProcessor;
+
+ private static final Logger LOGGER = LoggerFactory.getLogger(ServiceSpecificationService.class);
+
+
+ public LinkedHashMap get(String serviceSpecId) {
+ LinkedHashMap sdcResponse = sdcClient.callGet(serviceSpecId);
+ LinkedHashMap serviceCatalogResponse = (LinkedHashMap) getServiceSpecJsonTransformer.transform(sdcResponse);
+ LinkedHashMap toscaInfosTopologyTemplate = toscaInfosProcessor.getToscaInfos(serviceCatalogResponse);
+ if (toscaInfosTopologyTemplate != null) {
+ LOGGER.debug("tosca file found, retrieving informations");
+ toscaInfosProcessor.buildResponseWithToscaInfos(toscaInfosTopologyTemplate, serviceCatalogResponse);
+ } else {
+ LOGGER.debug("no tosca file found, partial response");
+ }
+ return serviceCatalogResponse;
+ }
+
+
+ public List<LinkedHashMap> find(MultiValueMap<String, String> parametersMap) {
+ List<LinkedHashMap> sdcResponse = sdcClient.callFind(parametersMap);
+ List<LinkedHashMap> serviceCatalogResponse =
+ (List<LinkedHashMap>) findServiceSpecJsonTransformer.transform(sdcResponse);
+ return serviceCatalogResponse;
+ }
+}
diff --git a/src/main/java/org/onap/nbi/apis/servicecatalog/ToscaInfosProcessor.java b/src/main/java/org/onap/nbi/apis/servicecatalog/ToscaInfosProcessor.java
new file mode 100644
index 0000000..4bfbd7b
--- /dev/null
+++ b/src/main/java/org/onap/nbi/apis/servicecatalog/ToscaInfosProcessor.java
@@ -0,0 +1,249 @@
+package org.onap.nbi.apis.servicecatalog;
+
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.sql.Timestamp;
+import java.util.ArrayList;
+import java.util.LinkedHashMap;
+import java.util.List;
+import java.util.zip.ZipEntry;
+import java.util.zip.ZipInputStream;
+import org.apache.commons.collections.CollectionUtils;
+import org.apache.commons.io.FileUtils;
+import org.onap.nbi.exceptions.TechnicalException;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Service;
+import com.fasterxml.jackson.databind.ObjectMapper;
+import com.fasterxml.jackson.dataformat.yaml.YAMLFactory;
+
+@Service
+public class ToscaInfosProcessor {
+
+ @Autowired
+ SdcClient sdcClient;
+
+ final ObjectMapper mapper = new ObjectMapper(new YAMLFactory()); // jackson databind
+
+ private static final Logger LOGGER = LoggerFactory.getLogger(ToscaInfosProcessor.class);
+
+ public void buildResponseWithToscaInfos(LinkedHashMap toscaInfosTopologyTemplate,
+ LinkedHashMap serviceCatalogResponse) {
+ if (toscaInfosTopologyTemplate.get("inputs") != null) {
+ ArrayList serviceSpecCharacteristic = new ArrayList();
+ LinkedHashMap toscaInfos = (LinkedHashMap) toscaInfosTopologyTemplate.get("inputs");
+ for (Object key : toscaInfos.keySet()) {
+ String keyString = (String) key;
+ LinkedHashMap inputParameter = (LinkedHashMap) toscaInfos.get(key);
+ LinkedHashMap mapParameter = new LinkedHashMap();
+ String parameterType = (String) inputParameter.get("type");
+ mapParameter.put("name", keyString);
+ mapParameter.put("description", inputParameter.get("description"));
+ mapParameter.put("valueType", parameterType);
+ mapParameter.put("@type", "ONAPserviceCharacteristic");
+ mapParameter.put("required", inputParameter.get("required"));
+ mapParameter.put("status", inputParameter.get("status"));
+ List<LinkedHashMap> serviceSpecCharacteristicValues =
+ buildServiceSpecCharacteristicsValues(inputParameter, parameterType);
+ mapParameter.put("serviceSpecCharacteristicValue", serviceSpecCharacteristicValues);
+ serviceSpecCharacteristic.add(mapParameter);
+ }
+
+ serviceCatalogResponse.put("serviceSpecCharacteristic", serviceSpecCharacteristic);
+ }
+ LinkedHashMap node_templates = (LinkedHashMap) toscaInfosTopologyTemplate.get("node_templates");
+
+ List<LinkedHashMap> resourceSpecifications =
+ (List<LinkedHashMap>) serviceCatalogResponse.get("resourceSpecification");
+ for (LinkedHashMap resourceSpecification : resourceSpecifications) {
+ String id = (String) resourceSpecification.get("id");
+ LOGGER.debug("get tosca infos for service id: " + id);
+ LinkedHashMap toscaInfosFromResourceId = getToscaInfosFromResourceUUID(node_templates, id);
+ resourceSpecification.put("modelCustomizationId", toscaInfosFromResourceId.get("customizationUUID"));
+ resourceSpecification.put("modelCustomizationName", toscaInfosFromResourceId.get("name"));
+
+ }
+ }
+
+ private List<LinkedHashMap> buildServiceSpecCharacteristicsValues(LinkedHashMap parameter, String parameterType) {
+ List<LinkedHashMap> serviceSpecCharacteristicValues = new ArrayList<>();
+ if (!"map".equalsIgnoreCase(parameterType) && !"list".equalsIgnoreCase(parameterType)) {
+ LOGGER.debug("get tosca infos for serviceSpecCharacteristicValues of type map or string : " + parameter);
+ Object aDefault = parameter.get("default");
+ if (parameter.get("entry_schema") != null) {
+ ArrayList entry_schema = (ArrayList) parameter.get("entry_schema");
+ if (CollectionUtils.isNotEmpty(entry_schema)) {
+ buildCharacteristicValuesFormShema(parameterType, serviceSpecCharacteristicValues, aDefault,
+ entry_schema);
+ }
+ }
+ }
+ return serviceSpecCharacteristicValues;
+ }
+
+ private void buildCharacteristicValuesFormShema(String parameterType,
+ List<LinkedHashMap> serviceSpecCharacteristicValues, Object aDefault, ArrayList entry_schema) {
+ LinkedHashMap constraints = (LinkedHashMap) entry_schema.get(0);
+ if (constraints != null) {
+ ArrayList constraintsList = (ArrayList) constraints.get("constraints");
+ if (CollectionUtils.isNotEmpty(constraintsList)) {
+ LinkedHashMap valuesMap = (LinkedHashMap) constraintsList.get(0);
+ if (valuesMap != null) {
+ List<Object> values = (List<Object>) valuesMap.get("valid_values");
+ for (Object value : values) {
+ String stringValue = value.toString();
+ LinkedHashMap serviceSpecCharacteristicValue = new LinkedHashMap();
+ serviceSpecCharacteristicValue.put("isDefault",
+ aDefault != null && aDefault.toString().equals(stringValue));
+ serviceSpecCharacteristicValue.put("value", stringValue);
+ serviceSpecCharacteristicValue.put("valueType", parameterType);
+ serviceSpecCharacteristicValues.add(serviceSpecCharacteristicValue);
+ }
+ }
+ }
+ }
+ }
+
+
+ private LinkedHashMap getToscaInfosFromResourceUUID(LinkedHashMap node_templates, String name) {
+ for (Object nodeTemplateObject : node_templates.values()) {
+ LinkedHashMap nodeTemplate = (LinkedHashMap) nodeTemplateObject;
+ LinkedHashMap metadata = (LinkedHashMap) nodeTemplate.get("metadata");
+ String metadataUUID = (String) metadata.get("UUID");
+ String metadataType = (String) metadata.get("type");
+ if ("VF".equalsIgnoreCase(metadataType) && name.equalsIgnoreCase(metadataUUID)) {
+ return metadata;
+ }
+ }
+ return null;
+ }
+
+
+ public LinkedHashMap getToscaInfos(LinkedHashMap sdcResponse) {
+ String toscaModelUrl = (String) sdcResponse.get("toscaModelURL");
+ String serviceId = (String) sdcResponse.get("uuid");
+ File toscaFile = sdcClient.callGetWithAttachment(toscaModelUrl);
+ Timestamp timestamp = new Timestamp(System.currentTimeMillis());
+ String tempFolderName = serviceId + timestamp;
+ File folderTemp = null;
+ LinkedHashMap topology_template = null;
+ try {
+ unZipArchive(toscaFile.getName(), tempFolderName);
+ folderTemp = new File(tempFolderName);
+ LOGGER.debug("temp folder for tosca files : " + folderTemp.getName());
+
+ LinkedHashMap toscaMetaFileHashMap = parseToscaFile(tempFolderName + "/TOSCA-Metadata/TOSCA.meta");
+ if (toscaMetaFileHashMap.get("Entry-Definitions") == null) {
+ throw new NullPointerException("no Entry-Definitions node in TOSCA.meta");
+ }
+ String toscaFilePath = (String) toscaMetaFileHashMap.get("Entry-Definitions");
+ LinkedHashMap toscaFileHashMap = parseToscaFile(tempFolderName + "/" + toscaFilePath);
+
+ if (toscaFileHashMap.get("topology_template") == null) {
+ throw new NullPointerException("no topology_template node in tosca file");
+ }
+ topology_template = (LinkedHashMap) toscaFileHashMap.get("topology_template");
+
+ } catch (NullPointerException e) {
+ LOGGER.error("unable to parse tosca file for id : " + serviceId + ", " + e.getMessage());
+ return null;
+ } finally {
+ try {
+ LOGGER.debug("deleting temp folder for tosca files : " + folderTemp.getName());
+ FileUtils.deleteDirectory(folderTemp);
+ LOGGER.debug("deleting tosca archive : " + toscaFile.getName());
+ FileUtils.forceDelete(toscaFile);
+ return topology_template;
+ } catch (IOException e) {
+ LOGGER.error("unable to delete temp directory tosca file for id : " + serviceId);
+ return null;
+
+ }
+ }
+ }
+
+
+ private LinkedHashMap parseToscaFile(String fileName) {
+
+ File toscaFile = new File(fileName);
+ if (toscaFile == null) {
+ throw new TechnicalException("unable to find file : " + fileName);
+ }
+ try {
+ return (LinkedHashMap) mapper.readValue(toscaFile, Object.class);
+ } catch (IOException e) {
+ LOGGER.error("unable to parse tosca file : " + fileName);
+ LOGGER.error(e.getMessage());
+ throw new TechnicalException("Unable to parse tosca file : " + fileName);
+
+ } catch (NullPointerException e) {
+ LOGGER.error("unable to find tosca file : " + fileName);
+ LOGGER.error(e.getMessage());
+ throw new TechnicalException("unable to find tosca file : " + fileName);
+ }
+ }
+
+
+ /**
+ * Unzip it
+ *
+ * @param zipFile input zip file
+ * @param outputFolder zip file output folder
+ */
+ private void unZipArchive(String zipFile, String outputFolder) {
+
+ byte[] buffer = new byte[1024];
+
+ try {
+
+ // create output directory is not exists
+ File folder = new File(outputFolder);
+ if (!folder.exists()) {
+ folder.mkdir();
+ }
+
+ // get the zip file content
+ try (ZipInputStream zis = new ZipInputStream(new FileInputStream(zipFile))) {
+ // get the zipped file list entry
+ ZipEntry ze = zis.getNextEntry();
+
+ while (ze != null) {
+
+ String fileName = ze.getName();
+ File newFile = new File(outputFolder + File.separator + fileName);
+
+ LOGGER.debug("File to unzip : " + newFile.getAbsoluteFile());
+
+ // create all non exists folders
+ // else you will hit FileNotFoundException for compressed folder
+ new File(newFile.getParent()).mkdirs();
+
+ try (FileOutputStream fos = new FileOutputStream(newFile)) {
+
+ int len;
+ while ((len = zis.read(buffer)) > 0) {
+ fos.write(buffer, 0, len);
+ }
+
+ fos.close();
+ }
+ ze = zis.getNextEntry();
+ }
+
+ zis.closeEntry();
+ zis.close();
+ }
+
+ LOGGER.debug("Done");
+
+ } catch (IOException ex) {
+ LOGGER.error("Error while unzipping ToscaModel archive from ONAP : " + ex.getMessage());
+ throw new TechnicalException("Error while unzipping ToscaModel archive from ONAP");
+ }
+ }
+
+
+}
diff --git a/src/main/java/org/onap/nbi/apis/servicecatalog/jolt/FindServiceSpecJsonTransformer.java b/src/main/java/org/onap/nbi/apis/servicecatalog/jolt/FindServiceSpecJsonTransformer.java
new file mode 100644
index 0000000..b32f9fc
--- /dev/null
+++ b/src/main/java/org/onap/nbi/apis/servicecatalog/jolt/FindServiceSpecJsonTransformer.java
@@ -0,0 +1,36 @@
+package org.onap.nbi.apis.servicecatalog.jolt;
+
+import com.bazaarvoice.jolt.Chainr;
+import com.bazaarvoice.jolt.JsonUtils;
+import com.bazaarvoice.jolt.exception.JoltException;
+import org.onap.nbi.exceptions.TechnicalException;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.stereotype.Service;
+
+import java.util.List;
+
+@Service
+public class FindServiceSpecJsonTransformer {
+
+ private Chainr chainr;
+
+ private static final Logger LOGGER = LoggerFactory.getLogger(FindServiceSpecJsonTransformer.class);
+
+ public FindServiceSpecJsonTransformer() {
+ List<Object> specs = JsonUtils.classpathToList("/jolt/findServiceCatalog.json");
+ this.chainr = Chainr.fromSpec(specs);
+ }
+
+ public Object transform(Object serviceSpec) {
+ Object output = null;
+ try {
+ output = chainr.transform(serviceSpec);
+ } catch (JoltException joE) {
+ LOGGER.error("Unable to transform SDC response with JOLT Transformer : " + joE.getMessage());
+ throw new TechnicalException("Error while parsing ONAP response");
+ }
+ return output;
+ }
+
+}
diff --git a/src/main/java/org/onap/nbi/apis/servicecatalog/jolt/GetServiceSpecJsonTransformer.java b/src/main/java/org/onap/nbi/apis/servicecatalog/jolt/GetServiceSpecJsonTransformer.java
new file mode 100644
index 0000000..784ed6f
--- /dev/null
+++ b/src/main/java/org/onap/nbi/apis/servicecatalog/jolt/GetServiceSpecJsonTransformer.java
@@ -0,0 +1,37 @@
+package org.onap.nbi.apis.servicecatalog.jolt;
+
+import com.bazaarvoice.jolt.Chainr;
+import com.bazaarvoice.jolt.JsonUtils;
+import com.bazaarvoice.jolt.exception.JoltException;
+import org.onap.nbi.exceptions.TechnicalException;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.stereotype.Service;
+
+import java.util.List;
+
+@Service
+public class GetServiceSpecJsonTransformer {
+
+ private Chainr chainr;
+
+ private static final Logger LOGGER = LoggerFactory.getLogger(GetServiceSpecJsonTransformer.class);
+
+
+ public GetServiceSpecJsonTransformer() {
+ List<Object> specs = JsonUtils.classpathToList("/jolt/getServiceCatalog.json");
+ this.chainr = Chainr.fromSpec(specs);
+ }
+
+ public Object transform(Object serviceSpec) {
+ Object output = null;
+ try {
+ output = chainr.transform(serviceSpec);
+ } catch (JoltException joE) {
+ LOGGER.error("Unable to transform SDC response with JOLT Transformer : " + joE.getMessage());
+ throw new TechnicalException("Error while parsing ONAP response");
+ }
+ return output;
+ }
+
+}
diff --git a/src/main/java/org/onap/nbi/exceptions/ApiError.java b/src/main/java/org/onap/nbi/exceptions/ApiError.java
new file mode 100644
index 0000000..5e030f8
--- /dev/null
+++ b/src/main/java/org/onap/nbi/exceptions/ApiError.java
@@ -0,0 +1,67 @@
+package org.onap.nbi.exceptions;
+
+import javax.xml.bind.annotation.XmlAccessType;
+import javax.xml.bind.annotation.XmlAccessorType;
+import javax.xml.bind.annotation.XmlElement;
+import javax.xml.bind.annotation.XmlType;
+
+@XmlAccessorType(XmlAccessType.FIELD)
+@XmlType(name = "Error", propOrder = {
+ "code",
+ "message",
+ "description",
+ "infoURL"
+})
+public class ApiError {
+ @XmlElement(required = true)
+ protected String code;
+ @XmlElement(required = true)
+ protected String message;
+ @XmlElement(required = true)
+ private String description;
+ @XmlElement(required = true)
+ protected String infoURL;
+
+ public ApiError() {
+ }
+
+ public ApiError(String code, String message, String description, String infoURL) {
+ this.code = code;
+ this.message = message;
+ this.description = description;
+ this.infoURL = infoURL;
+ }
+
+ public String getCode() {
+ return code;
+ }
+
+ public void setCode(String code) {
+ this.code = code;
+ }
+
+ public String getMessage() {
+ return message;
+ }
+
+ public void setMessage(String message) {
+ this.message = message;
+ }
+
+ public String getDescription() {
+ return description;
+ }
+
+ public void setDescription(String description) {
+ this.description = description;
+ }
+
+ public String getInfoURL() {
+ return infoURL;
+ }
+
+ public void setInfoURL(String infoURL) {
+ this.infoURL = infoURL;
+ }
+}
+
diff --git a/src/main/java/org/onap/nbi/exceptions/ApiException.java b/src/main/java/org/onap/nbi/exceptions/ApiException.java
new file mode 100644
index 0000000..d07d9db
--- /dev/null
+++ b/src/main/java/org/onap/nbi/exceptions/ApiException.java
@@ -0,0 +1,33 @@
+package org.onap.nbi.exceptions;
+
+import java.io.Serializable;
+
+public class ApiException extends RuntimeException implements Serializable {
+
+ protected final String localisationClass;
+ protected final String localisationMethod;
+
+ public ApiException() {
+ super();
+ localisationClass = "";
+ localisationMethod = "";
+ }
+
+ public ApiException(String message) {
+ super(message);
+ localisationClass = "";
+ localisationMethod = "";
+ }
+
+ public ApiException(String message, Throwable cause) {
+ super(message, cause);
+ localisationClass = "";
+ localisationMethod = "";
+ }
+
+ public ApiException(Throwable cause) {
+ super(cause);
+ localisationClass = "";
+ localisationMethod = "";
+ }
+}
diff --git a/src/main/java/org/onap/nbi/exceptions/ApiExceptionHandler.java b/src/main/java/org/onap/nbi/exceptions/ApiExceptionHandler.java
new file mode 100644
index 0000000..1f109cb
--- /dev/null
+++ b/src/main/java/org/onap/nbi/exceptions/ApiExceptionHandler.java
@@ -0,0 +1,46 @@
+package org.onap.nbi.exceptions;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.http.HttpStatus;
+import org.springframework.http.ResponseEntity;
+import org.springframework.web.bind.annotation.ControllerAdvice;
+import org.springframework.web.bind.annotation.ExceptionHandler;
+import org.springframework.web.bind.annotation.ResponseBody;
+import org.springframework.web.client.RestClientException;
+
+@ControllerAdvice
+public class ApiExceptionHandler {
+
+ private static final Logger LOGGER = LoggerFactory.getLogger(ApiExceptionHandler.class);
+
+
+ @ExceptionHandler(BackendFunctionalException.class)
+ @ResponseBody
+ public ResponseEntity<ApiError> backendExceptionHandler(final BackendFunctionalException exception) {
+ ApiError apiError = new ApiError(String.valueOf(exception.getHttpStatus().value()), exception.getMessage(), "", "");
+ return new ResponseEntity<ApiError>(apiError, exception.getHttpStatus());
+ }
+
+ @ExceptionHandler(TechnicalException.class)
+ @ResponseBody
+ public ResponseEntity<ApiError> technicalExceptionHandler(final TechnicalException exception) {
+ ApiError apiError = new ApiError(String.valueOf(exception.getHttpStatus().value()), exception.getMessage(), "", "");
+ return new ResponseEntity<ApiError>(apiError, exception.getHttpStatus());
+ }
+
+ @ExceptionHandler(RestClientException.class)
+ @ResponseBody
+ public ResponseEntity<ApiError> RestClientExceptionHandler(final RestClientException exception) {
+ ApiError apiError = new ApiError("500", HttpStatus.INTERNAL_SERVER_ERROR.getReasonPhrase(), "Unable to " +
+ "reach ONAP services", "");
+ return new ResponseEntity<ApiError>(apiError, HttpStatus.INTERNAL_SERVER_ERROR);
+ }
+
+ @ExceptionHandler(ValidationException.class)
+ @ResponseBody
+ public ResponseEntity<ApiError> ValidationExceptionHandler(final ValidationException exception) {
+ ApiError apiError = new ApiError("400", HttpStatus.BAD_REQUEST.getReasonPhrase(), exception.getMessages(), "");
+ return new ResponseEntity<ApiError>(apiError, HttpStatus.INTERNAL_SERVER_ERROR);
+ }
+}
diff --git a/src/main/java/org/onap/nbi/exceptions/BackendErrorHandler.java b/src/main/java/org/onap/nbi/exceptions/BackendErrorHandler.java
new file mode 100644
index 0000000..34c4ac5
--- /dev/null
+++ b/src/main/java/org/onap/nbi/exceptions/BackendErrorHandler.java
@@ -0,0 +1,24 @@
+package org.onap.nbi.exceptions;
+
+import org.springframework.http.client.ClientHttpResponse;
+import org.springframework.web.client.DefaultResponseErrorHandler;
+import org.springframework.web.client.ResponseErrorHandler;
+
+import java.io.IOException;
+
+public class BackendErrorHandler implements ResponseErrorHandler {
+
+ private ResponseErrorHandler errorHandler = new DefaultResponseErrorHandler();
+
+ @Override
+ public boolean hasError(ClientHttpResponse response) throws IOException {
+ return errorHandler.hasError(response);
+ }
+
+ @Override
+ public void handleError(ClientHttpResponse response) throws IOException {
+ if (response.getStatusCode() != null) {
+ throw new BackendFunctionalException(response.getStatusCode(), response.getStatusText());
+ }
+ }
+}
diff --git a/src/main/java/org/onap/nbi/exceptions/BackendFunctionalException.java b/src/main/java/org/onap/nbi/exceptions/BackendFunctionalException.java
new file mode 100644
index 0000000..176186f
--- /dev/null
+++ b/src/main/java/org/onap/nbi/exceptions/BackendFunctionalException.java
@@ -0,0 +1,22 @@
+package org.onap.nbi.exceptions;
+
+import org.springframework.http.HttpStatus;
+
+public class BackendFunctionalException extends ApiException {
+
+ private HttpStatus httpStatus;
+
+ public BackendFunctionalException(HttpStatus httpStatus, String message) {
+ super(message);
+ this.httpStatus = httpStatus;
+ }
+
+ public BackendFunctionalException() {
+ super();
+ }
+
+ public HttpStatus getHttpStatus() {
+ return httpStatus;
+ }
+
+} \ No newline at end of file
diff --git a/src/main/java/org/onap/nbi/exceptions/TechnicalException.java b/src/main/java/org/onap/nbi/exceptions/TechnicalException.java
new file mode 100644
index 0000000..a2abe3e
--- /dev/null
+++ b/src/main/java/org/onap/nbi/exceptions/TechnicalException.java
@@ -0,0 +1,26 @@
+package org.onap.nbi.exceptions;
+
+import org.springframework.http.HttpStatus;
+
+public class TechnicalException extends ApiException {
+
+ private HttpStatus httpStatus;
+
+ public TechnicalException(String message) {
+ super(message);
+ this.httpStatus = HttpStatus.INTERNAL_SERVER_ERROR;
+ }
+
+ public TechnicalException() {
+ super();
+ }
+
+ public HttpStatus getHttpStatus() {
+ return httpStatus;
+ }
+
+ public void setHttpStatus(HttpStatus httpStatus) {
+ this.httpStatus = httpStatus;
+ }
+
+} \ No newline at end of file
diff --git a/src/main/java/org/onap/nbi/exceptions/ValidationException.java b/src/main/java/org/onap/nbi/exceptions/ValidationException.java
new file mode 100644
index 0000000..316e8f2
--- /dev/null
+++ b/src/main/java/org/onap/nbi/exceptions/ValidationException.java
@@ -0,0 +1,32 @@
+package org.onap.nbi.exceptions;
+
+import org.springframework.validation.FieldError;
+import org.springframework.validation.ObjectError;
+
+import java.util.List;
+
+public class ValidationException extends ApiException {
+
+ private String messages;
+
+ public ValidationException(List<ObjectError> listErrors) {
+ super();
+ StringBuilder sb = new StringBuilder();
+ for (ObjectError error : listErrors) {
+ if (error instanceof FieldError) {
+ sb.append(((FieldError) error).getField())
+ .append(" ")
+ .append(((FieldError) error).getDefaultMessage())
+ .append(". ");
+ } else {
+ sb.append(" ").append(error.getDefaultMessage()).append(". ");
+ }
+ }
+ messages = sb.toString();
+
+ }
+
+ public String getMessages() {
+ return messages;
+ }
+}
diff --git a/src/main/resources/application-localhost.properties b/src/main/resources/application-localhost.properties
new file mode 100644
index 0000000..58b13f6
--- /dev/null
+++ b/src/main/resources/application-localhost.properties
@@ -0,0 +1,7 @@
+# LOGGING
+logging.level.org.onap.nbi=DEBUG
+
+# SDC
+sdc.host=http://127.0.0.1:8090
+sdc.header.ecompInstanceId=Rene
+sdc.header.authorization=Basic YWFpOktwOGJKNFNYc3pNMFdYbGhhazNlSGxjc2UyZ0F3ODR2YW9HR21KdlV5MlU=
diff --git a/src/main/resources/application.properties b/src/main/resources/application.properties
index 1d8ff27..10e0d7d 100644
--- a/src/main/resources/application.properties
+++ b/src/main/resources/application.properties
@@ -3,4 +3,5 @@ server.contextPath=/nbi/api/v1
server.port = 8080
# LOGGING
-logging.level.org.onap.nbi=WARN
+logging.level.=WARN
+
diff --git a/src/main/resources/jolt/findServiceCatalog.json b/src/main/resources/jolt/findServiceCatalog.json
new file mode 100644
index 0000000..ffe5fe3
--- /dev/null
+++ b/src/main/resources/jolt/findServiceCatalog.json
@@ -0,0 +1,28 @@
+[
+ {
+ "operation": "shift",
+ "spec": {
+ "*": {
+ "uuid": "[&1].id",
+ "description": "[&1].description",
+ "name": "[&1].name",
+ "invariantUUID": "[&1].invariantUUID",
+ "category": "[&1].category",
+ "distributionStatus": "[&1].distributionStatus",
+ "version": "[&1].version",
+ "lifecycleState": "[&1].lifecycleStatus",
+ "lastUpdaterUserId": "[&1].relatedParty.id"
+ }
+ }
+ },
+ {
+ "operation": "default",
+ "spec": {
+ "*": {
+ "relatedParty": {
+ "role": "lastUpdater"
+ }
+ }
+ }
+ }
+] \ No newline at end of file
diff --git a/src/main/resources/jolt/getServiceCatalog.json b/src/main/resources/jolt/getServiceCatalog.json
new file mode 100644
index 0000000..cac223e
--- /dev/null
+++ b/src/main/resources/jolt/getServiceCatalog.json
@@ -0,0 +1,77 @@
+[
+ {
+ "operation": "shift",
+ "spec": {
+ "uuid": "id",
+ "name": "name",
+ "description": "description",
+ "invariantUUID": "invariantUUID",
+ "toscaModelURL": "toscaModelURL",
+ "toscaResourceName": "toscaResourceName",
+ "category ": "category",
+ "subcategory": "subcategory",
+ "distributionStatus": "distributionStatus",
+ "version": "version",
+ "lifecycleState":"lifecycleStatus" ,
+ "artifacts" : {
+ "*": {
+ "artifactUUID": "attachment[&1].id",
+ "artifactName": "attachment[&1].name",
+ "artifactDescription": "attachment[&1].description",
+ "artifactLabel": "attachment[&1].artifactLabel",
+ "artifactGroupType": "attachment[&1].artifactGroupType",
+ "artifactTimeout": "attachment[&1].artifactTimeout",
+ "artifactChecksum": "attachment[&1].artifactChecksum",
+ "artifactVersion": "attachment[&1].artifactVersion",
+ "generatedFromUUID": "attachment[&1].generatedFromUUID",
+ "artifactURL": "attachment[&1].url",
+ "artifactType": "attachment[&1].mimeType"
+ }
+
+ },
+ "lastUpdaterUserId" : "relatedParty.id",
+ "lastUpdaterFullName" : "relatedParty.name",
+ "resources" : {
+ "*": {
+ "resourceUUID": "resourceSpecification[&1].id",
+ "resourceVersion": "resourceSpecification[&1].version",
+ "resourceName": "resourceSpecification[&1].name",
+ "resourceInstanceName": "resourceSpecification[&1].instanceName",
+ "resourceInvariantUUID": "resourceSpecification[&1].resourceInvariantUUID",
+ "resourceType": "resourceSpecification[&1].resourceType"
+ }
+
+ }
+
+
+
+ }
+ },
+ {
+ "operation": "modify-overwrite-beta",
+ "spec": {
+ "href": "=concat('serviceSpecification/',@(1,id))"
+ }
+ },
+ {
+ "operation": "default",
+ "spec": {
+ "@type": "ONAPservice",
+ "attachment[]" : {
+ "*": {
+ "@type": "ONAPartifact"
+ }
+
+ },
+ "relatedParty" : {
+ "role": "lastUpdater"
+ },
+ "resourceSpecification[]" : {
+ "*": {
+ "@type": "ONAPresource"
+ }
+
+ }
+ }
+ }
+] \ No newline at end of file