diff options
author | MatthieuGeerebaert <matthieu.geerebaert@orange.com> | 2018-03-28 13:36:26 +0200 |
---|---|---|
committer | MatthieuGeerebaert <matthieu.geerebaert@orange.com> | 2018-04-03 23:35:04 +0200 |
commit | 99bf586a6eb9799c4f33e43976d741f2807ea287 (patch) | |
tree | 817406c30c99f366c000afc3fe0963b6b32499e7 /src/main/java/org | |
parent | 65fd3da0ea090f3c1fc82cea0c49547dbf362fcf (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/java/org')
15 files changed, 861 insertions, 0 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; + } +} |