diff options
27 files changed, 386 insertions, 222 deletions
diff --git a/cps-ncmp-rest/pom.xml b/cps-ncmp-rest/pom.xml index 93ee680daf..26b83bef87 100644 --- a/cps-ncmp-rest/pom.xml +++ b/cps-ncmp-rest/pom.xml @@ -1,7 +1,7 @@ <?xml version="1.0" encoding="UTF-8"?> <!-- ============LICENSE_START======================================================= - Copyright (C) 2021 Nordix Foundation + Copyright (C) 2021-2022 Nordix Foundation Modifications Copyright (C) 2021 Bell Canada ================================================================================ Licensed under the Apache License, Version 2.0 (the "License"); @@ -61,10 +61,6 @@ <groupId>io.swagger.core.v3</groupId> <artifactId>swagger-annotations</artifactId> </dependency> - <dependency> - <groupId>org.modelmapper</groupId> - <artifactId>modelmapper</artifactId> - </dependency> <!-- T E S T D E P E N D E N C I E S --> <dependency> <groupId>org.codehaus.groovy</groupId> diff --git a/cps-ncmp-rest/src/main/java/org/onap/cps/ncmp/rest/controller/NetworkCmProxyController.java b/cps-ncmp-rest/src/main/java/org/onap/cps/ncmp/rest/controller/NetworkCmProxyController.java index f2f43c9897..419f6e9268 100755 --- a/cps-ncmp-rest/src/main/java/org/onap/cps/ncmp/rest/controller/NetworkCmProxyController.java +++ b/cps-ncmp-rest/src/main/java/org/onap/cps/ncmp/rest/controller/NetworkCmProxyController.java @@ -27,8 +27,6 @@ import static org.onap.cps.ncmp.api.impl.operations.DmiRequestBody.OperationEnum import static org.onap.cps.ncmp.api.impl.operations.DmiRequestBody.OperationEnum.PATCH; import static org.onap.cps.ncmp.api.impl.operations.DmiRequestBody.OperationEnum.UPDATE; -import com.google.gson.Gson; -import com.google.gson.GsonBuilder; import java.util.ArrayList; import java.util.Collection; import java.util.Collections; @@ -36,6 +34,7 @@ import java.util.List; import java.util.stream.Collectors; import javax.validation.Valid; import javax.validation.constraints.NotNull; +import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; import org.modelmapper.ModelMapper; import org.onap.cps.ncmp.api.NetworkCmProxyDataService; @@ -48,6 +47,7 @@ import org.onap.cps.ncmp.rest.model.Conditions; import org.onap.cps.ncmp.rest.model.ModuleNameAsJsonObject; import org.onap.cps.ncmp.rest.model.ModuleNamesAsJsonArray; import org.onap.cps.ncmp.rest.model.ModuleReference; +import org.onap.cps.utils.JsonObjectMapper; import org.springframework.http.HttpStatus; import org.springframework.http.ResponseEntity; import org.springframework.web.bind.annotation.RequestMapping; @@ -56,21 +56,14 @@ import org.springframework.web.bind.annotation.RestController; @Slf4j @RestController @RequestMapping("${rest.api.ncmp-base-path}") +@RequiredArgsConstructor public class NetworkCmProxyController implements NetworkCmProxyApi { - private static final Gson GSON = new GsonBuilder().create(); private static final String NO_BODY = null; - private final ModelMapper modelMapper = new ModelMapper(); + private final ModelMapper modelMapper; private final NetworkCmProxyDataService networkCmProxyDataService; - - /** - * Constructor Injection for Dependencies. - * @param networkCmProxyDataService Data Service Interface - */ - public NetworkCmProxyController(final NetworkCmProxyDataService networkCmProxyDataService) { - this.networkCmProxyDataService = networkCmProxyDataService; - } + private final JsonObjectMapper jsonObjectMapper; /** * Get resource data from operational datastore. @@ -119,7 +112,7 @@ public class NetworkCmProxyController implements NetworkCmProxyApi { final String cmHandle, final Object requestBody, final String contentType) { final Object responseObject = networkCmProxyDataService.writeResourceDataPassThroughRunningForCmHandle(cmHandle, - resourceIdentifier, PATCH, GSON.toJson(requestBody), contentType); + resourceIdentifier, PATCH, jsonObjectMapper.asJsonString(requestBody), contentType); return ResponseEntity.ok(responseObject); } @@ -136,7 +129,7 @@ public class NetworkCmProxyController implements NetworkCmProxyApi { public ResponseEntity<Void> createResourceDataRunningForCmHandle(final String resourceIdentifier, final String cmHandle, final Object requestBody, final String contentType) { networkCmProxyDataService.writeResourceDataPassThroughRunningForCmHandle(cmHandle, - resourceIdentifier, CREATE, GSON.toJson(requestBody), contentType); + resourceIdentifier, CREATE, jsonObjectMapper.asJsonString(requestBody), contentType); return new ResponseEntity<>(HttpStatus.CREATED); } @@ -155,7 +148,7 @@ public class NetworkCmProxyController implements NetworkCmProxyApi { final Object requestBody, final String contentType) { networkCmProxyDataService.writeResourceDataPassThroughRunningForCmHandle(cmHandle, - resourceIdentifier, UPDATE, GSON.toJson(requestBody), contentType); + resourceIdentifier, UPDATE, jsonObjectMapper.asJsonString(requestBody), contentType); return new ResponseEntity<>(HttpStatus.OK); } diff --git a/cps-ncmp-rest/src/test/groovy/org/onap/cps/ncmp/rest/controller/NetworkCmProxyControllerSpec.groovy b/cps-ncmp-rest/src/test/groovy/org/onap/cps/ncmp/rest/controller/NetworkCmProxyControllerSpec.groovy index b5dc2eabbb..0c8b2227d0 100644 --- a/cps-ncmp-rest/src/test/groovy/org/onap/cps/ncmp/rest/controller/NetworkCmProxyControllerSpec.groovy +++ b/cps-ncmp-rest/src/test/groovy/org/onap/cps/ncmp/rest/controller/NetworkCmProxyControllerSpec.groovy @@ -22,9 +22,6 @@ package org.onap.cps.ncmp.rest.controller -import org.onap.cps.TestUtils -import org.onap.cps.spi.model.ModuleReference - import static org.onap.cps.ncmp.api.impl.operations.DmiRequestBody.OperationEnum.PATCH import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.delete import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get @@ -35,6 +32,11 @@ import static org.onap.cps.ncmp.api.impl.operations.DmiRequestBody.OperationEnum import static org.onap.cps.ncmp.api.impl.operations.DmiRequestBody.OperationEnum.UPDATE import static org.onap.cps.ncmp.api.impl.operations.DmiRequestBody.OperationEnum.DELETE +import com.fasterxml.jackson.databind.ObjectMapper +import org.modelmapper.ModelMapper +import org.onap.cps.TestUtils +import org.onap.cps.spi.model.ModuleReference +import org.onap.cps.utils.JsonObjectMapper import org.onap.cps.ncmp.api.NetworkCmProxyDataService import org.spockframework.spring.SpringBean import org.springframework.beans.factory.annotation.Autowired @@ -54,10 +56,16 @@ class NetworkCmProxyControllerSpec extends Specification { @SpringBean NetworkCmProxyDataService mockNetworkCmProxyDataService = Mock() + @SpringBean + ModelMapper modelMapper = new ModelMapper() + + @SpringBean + JsonObjectMapper jsonObjectMapper = new JsonObjectMapper(new ObjectMapper()) + @Value('${rest.api.ncmp-base-path}/v1') def ncmpBasePathV1 - def jsonString = '{"some-key":"some-value"}' + def requestBody = '{"some-key":"some-value"}' def 'Get Resource Data from pass-through operational.' () { given: 'resource data url' @@ -115,11 +123,11 @@ class NetworkCmProxyControllerSpec extends Specification { def response = mvc.perform( put(updateUrl) .contentType(MediaType.APPLICATION_JSON_VALUE) - .accept(MediaType.APPLICATION_JSON_VALUE).content(jsonString) + .accept(MediaType.APPLICATION_JSON_VALUE).content(requestBody) ).andReturn().response then: 'ncmp service method to update resource is called' 1 * mockNetworkCmProxyDataService.writeResourceDataPassThroughRunningForCmHandle('testCmHandle', - 'parent/child', UPDATE, jsonString, 'application/json;charset=UTF-8') + 'parent/child', UPDATE, requestBody, 'application/json;charset=UTF-8') and: 'the response status is OK' response.status == HttpStatus.OK.value() } @@ -192,11 +200,11 @@ class NetworkCmProxyControllerSpec extends Specification { def response = mvc.perform( patch(url) .contentType(MediaType.APPLICATION_JSON) - .accept(MediaType.APPLICATION_JSON).content(jsonString) + .accept(MediaType.APPLICATION_JSON).content(requestBody) ).andReturn().response then: 'ncmp service method to update resource is called' 1 * mockNetworkCmProxyDataService.writeResourceDataPassThroughRunningForCmHandle('testCmHandle', - 'parent/child', PATCH, jsonString, 'application/json;charset=UTF-8') + 'parent/child', PATCH, requestBody, 'application/json;charset=UTF-8') and: 'the response status is OK' response.status == HttpStatus.OK.value() } diff --git a/cps-ncmp-rest/src/test/groovy/org/onap/cps/ncmp/rest/exceptions/NetworkCmProxyRestExceptionHandlerSpec.groovy b/cps-ncmp-rest/src/test/groovy/org/onap/cps/ncmp/rest/exceptions/NetworkCmProxyRestExceptionHandlerSpec.groovy index f36a706937..7b3cd89145 100644 --- a/cps-ncmp-rest/src/test/groovy/org/onap/cps/ncmp/rest/exceptions/NetworkCmProxyRestExceptionHandlerSpec.groovy +++ b/cps-ncmp-rest/src/test/groovy/org/onap/cps/ncmp/rest/exceptions/NetworkCmProxyRestExceptionHandlerSpec.groovy @@ -21,9 +21,11 @@ package org.onap.cps.ncmp.rest.exceptions import groovy.json.JsonSlurper +import org.modelmapper.ModelMapper import org.onap.cps.ncmp.api.NetworkCmProxyDataService import org.onap.cps.ncmp.api.impl.exception.NcmpException import org.onap.cps.spi.exceptions.CpsException +import org.onap.cps.utils.JsonObjectMapper import org.spockframework.spring.SpringBean import org.springframework.beans.factory.annotation.Autowired import org.springframework.beans.factory.annotation.Value @@ -44,6 +46,12 @@ class NetworkCmProxyRestExceptionHandlerSpec extends Specification { @SpringBean NetworkCmProxyDataService mockNetworkCmProxyDataService = Mock() + @SpringBean + ModelMapper modelMapper = Stub() + + @SpringBean + JsonObjectMapper jsonObjectMapper = Stub() + @Value('${rest.api.ncmp-base-path}') def basePath diff --git a/cps-ncmp-service/pom.xml b/cps-ncmp-service/pom.xml index b70279303e..871bd14c58 100644 --- a/cps-ncmp-service/pom.xml +++ b/cps-ncmp-service/pom.xml @@ -1,7 +1,7 @@ <?xml version="1.0" encoding="UTF-8"?> <!-- ============LICENSE_START======================================================= - Copyright (C) 2021 Nordix Foundation + Copyright (C) 2021-2022 Nordix Foundation Modifications Copyright (C) 2021 Pantheon.tech ================================================================================ Licensed under the Apache License, Version 2.0 (the "License"); @@ -66,5 +66,9 @@ <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-validation</artifactId> </dependency> + <dependency> + <groupId>org.modelmapper</groupId> + <artifactId>modelmapper</artifactId> + </dependency> </dependencies> </project> diff --git a/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/NetworkCmProxyDataServiceImpl.java b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/NetworkCmProxyDataServiceImpl.java index 240d6b5ca9..82145efc3d 100755 --- a/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/NetworkCmProxyDataServiceImpl.java +++ b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/NetworkCmProxyDataServiceImpl.java @@ -27,13 +27,13 @@ import static org.onap.cps.ncmp.api.impl.operations.DmiRequestBody.OperationEnum import static org.onap.cps.spi.CascadeDeleteAllowed.CASCADE_DELETE_ALLOWED; import com.fasterxml.jackson.core.JsonProcessingException; -import com.fasterxml.jackson.databind.ObjectMapper; import java.time.OffsetDateTime; import java.util.ArrayList; import java.util.Collection; import java.util.HashMap; import java.util.List; import java.util.Map; +import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; import org.onap.cps.api.CpsAdminService; import org.onap.cps.api.CpsDataService; @@ -50,11 +50,13 @@ import org.onap.cps.ncmp.api.models.PersistenceCmHandlesList; import org.onap.cps.spi.exceptions.DataNodeNotFoundException; import org.onap.cps.spi.exceptions.DataValidationException; import org.onap.cps.spi.model.ModuleReference; +import org.onap.cps.utils.JsonObjectMapper; import org.springframework.http.ResponseEntity; import org.springframework.stereotype.Service; @Slf4j @Service +@RequiredArgsConstructor public class NetworkCmProxyDataServiceImpl implements NetworkCmProxyDataService { private static final String NFP_OPERATIONAL_DATASTORE_DATASPACE_NAME = "NFP-Operational"; @@ -65,37 +67,17 @@ public class NetworkCmProxyDataServiceImpl implements NetworkCmProxyDataService private static final OffsetDateTime NO_TIMESTAMP = null; - private CpsDataService cpsDataService; + private final CpsDataService cpsDataService; - private ObjectMapper objectMapper; + private final JsonObjectMapper jsonObjectMapper; - private DmiDataOperations dmiDataOperations; + private final DmiDataOperations dmiDataOperations; - private DmiModelOperations dmiModelOperations; + private final DmiModelOperations dmiModelOperations; - private CpsModuleService cpsModuleService; + private final CpsModuleService cpsModuleService; - private CpsAdminService cpsAdminService; - - /** - * Constructor Injection for Dependencies. - * @param dmiDataOperations DMI operation - * @param cpsDataService Data Service Interface - * @param objectMapper Object Mapper - */ - public NetworkCmProxyDataServiceImpl(final DmiDataOperations dmiDataOperations, - final DmiModelOperations dmiModelOperations, - final CpsModuleService cpsModuleService, - final CpsDataService cpsDataService, - final CpsAdminService cpsAdminService, - final ObjectMapper objectMapper) { - this.dmiDataOperations = dmiDataOperations; - this.dmiModelOperations = dmiModelOperations; - this.cpsModuleService = cpsModuleService; - this.cpsDataService = cpsDataService; - this.cpsAdminService = cpsAdminService; - this.objectMapper = objectMapper; - } + private final CpsAdminService cpsAdminService; @Override public void updateDmiRegistrationAndSyncModule(final DmiPluginRegistration dmiPluginRegistration) { @@ -198,7 +180,7 @@ public class NetworkCmProxyDataServiceImpl implements NetworkCmProxyDataService throws JsonProcessingException { final PersistenceCmHandlesList updatedPersistenceCmHandlesList = getUpdatedPersistenceCmHandlesList(dmiPluginRegistration, dmiPluginRegistration.getUpdatedCmHandles()); - final String cmHandlesAsJson = objectMapper.writeValueAsString(updatedPersistenceCmHandlesList); + final String cmHandlesAsJson = jsonObjectMapper.asJsonString(updatedPersistenceCmHandlesList); cpsDataService.updateNodeLeavesAndExistingDescendantLeaves(NCMP_DATASPACE_NAME, NCMP_DMI_REGISTRY_ANCHOR, "/dmi-registry", cmHandlesAsJson, NO_TIMESTAMP); } @@ -223,7 +205,7 @@ public class NetworkCmProxyDataServiceImpl implements NetworkCmProxyDataService private void registerAndSyncNewCmHandles(final PersistenceCmHandlesList persistenceCmHandlesList) throws JsonProcessingException { - final String cmHandleJsonData = objectMapper.writeValueAsString(persistenceCmHandlesList); + final String cmHandleJsonData = jsonObjectMapper.asJsonString(persistenceCmHandlesList); cpsDataService.saveListElements(NCMP_DATASPACE_NAME, NCMP_DMI_REGISTRY_ANCHOR, "/dmi-registry", cmHandleJsonData, NO_TIMESTAMP); diff --git a/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/operations/DmiDataOperations.java b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/operations/DmiDataOperations.java index 095f677ef8..3bfb424a2e 100644 --- a/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/operations/DmiDataOperations.java +++ b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/operations/DmiDataOperations.java @@ -1,6 +1,6 @@ /* * ============LICENSE_START======================================================= - * Copyright (C) 2021 Nordix Foundation + * Copyright (C) 2021-2022 Nordix Foundation * ================================================================================ * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -25,10 +25,10 @@ import static org.onap.cps.ncmp.api.impl.operations.DmiRequestBody.OperationEnum import static org.onap.cps.ncmp.api.impl.operations.DmiRequestBody.OperationEnum.READ; import static org.onap.cps.ncmp.api.impl.operations.RequiredDmiService.DATA; -import com.fasterxml.jackson.databind.ObjectMapper; import org.onap.cps.ncmp.api.impl.client.DmiRestClient; import org.onap.cps.ncmp.api.impl.config.NcmpConfiguration; import org.onap.cps.ncmp.api.models.PersistenceCmHandle; +import org.onap.cps.utils.JsonObjectMapper; import org.springframework.http.HttpHeaders; import org.springframework.http.ResponseEntity; import org.springframework.stereotype.Component; @@ -45,10 +45,10 @@ public class DmiDataOperations extends DmiOperations { * @param dmiRestClient {@code DmiRestClient} */ public DmiDataOperations(final PersistenceCmHandleRetriever cmHandlePropertiesRetriever, - final ObjectMapper objectMapper, + final JsonObjectMapper jsonObjectMapper, final NcmpConfiguration.DmiProperties dmiProperties, final DmiRestClient dmiRestClient) { - super(cmHandlePropertiesRetriever, objectMapper, dmiProperties, dmiRestClient); + super(cmHandlePropertiesRetriever, jsonObjectMapper, dmiProperties, dmiRestClient); } /** @@ -73,7 +73,7 @@ public class DmiDataOperations extends DmiOperations { .operation(READ) .build(); dmiRequestBody.asCmHandleProperties(persistenceCmHandle.getAdditionalProperties()); - final String jsonBody = getDmiRequestBodyAsString(dmiRequestBody); + final String jsonBody = jsonObjectMapper.asJsonString(dmiRequestBody); final var dmiResourceDataUrl = getDmiDatastoreUrlWithOptions( persistenceCmHandle.resolveDmiServiceName(DATA), cmHandle, resourceId, @@ -106,7 +106,7 @@ public class DmiDataOperations extends DmiOperations { .dataType(dataType) .build(); dmiRequestBody.asCmHandleProperties(persistenceCmHandle.getAdditionalProperties()); - final String jsonBody = getDmiRequestBodyAsString(dmiRequestBody); + final String jsonBody = jsonObjectMapper.asJsonString(dmiRequestBody); final String dmiUrl = getResourceInDataStoreUrl(persistenceCmHandle.resolveDmiServiceName(DATA), cmHandle, resourceId, PASSTHROUGH_RUNNING); diff --git a/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/operations/DmiModelOperations.java b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/operations/DmiModelOperations.java index 20a3309d01..1ce6dcfcd5 100644 --- a/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/operations/DmiModelOperations.java +++ b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/operations/DmiModelOperations.java @@ -1,6 +1,6 @@ /* * ============LICENSE_START======================================================= - * Copyright (C) 2021 Nordix Foundation + * Copyright (C) 2021-2022 Nordix Foundation * ================================================================================ * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -22,7 +22,6 @@ package org.onap.cps.ncmp.api.impl.operations; import static org.onap.cps.ncmp.api.impl.operations.RequiredDmiService.MODEL; -import com.fasterxml.jackson.databind.ObjectMapper; import com.google.gson.JsonArray; import com.google.gson.JsonObject; import java.util.ArrayList; @@ -34,10 +33,14 @@ import org.onap.cps.ncmp.api.impl.config.NcmpConfiguration; import org.onap.cps.ncmp.api.models.PersistenceCmHandle; import org.onap.cps.ncmp.api.models.YangResource; import org.onap.cps.spi.model.ModuleReference; +import org.onap.cps.utils.JsonObjectMapper; import org.springframework.http.HttpHeaders; import org.springframework.http.ResponseEntity; import org.springframework.stereotype.Component; +/** + * Operations class for DMI Model. + */ @Component public class DmiModelOperations extends DmiOperations { @@ -47,10 +50,10 @@ public class DmiModelOperations extends DmiOperations { * @param dmiRestClient {@code DmiRestClient} */ public DmiModelOperations(final PersistenceCmHandleRetriever cmHandlePropertiesRetriever, - final ObjectMapper objectMapper, + final JsonObjectMapper jsonObjectMapper, final NcmpConfiguration.DmiProperties dmiProperties, final DmiRestClient dmiRestClient) { - super(cmHandlePropertiesRetriever, objectMapper, dmiProperties, dmiRestClient); + super(cmHandlePropertiesRetriever, jsonObjectMapper, dmiProperties, dmiRestClient); } /** @@ -65,7 +68,7 @@ public class DmiModelOperations extends DmiOperations { dmiRequestBody.asCmHandleProperties(persistenceCmHandle.getAdditionalProperties()); final ResponseEntity<Object> dmiFetchModulesResponseEntity = getResourceFromDmiWithJsonData( persistenceCmHandle.resolveDmiServiceName(MODEL), - getDmiRequestBodyAsString(dmiRequestBody), persistenceCmHandle.getId(), "modules"); + jsonObjectMapper.asJsonString(dmiRequestBody), persistenceCmHandle.getId(), "modules"); return toModuleReferences((Map) dmiFetchModulesResponseEntity.getBody()); } @@ -144,7 +147,7 @@ public class DmiModelOperations extends DmiOperations { if (moduleReferencesAsList != null) { moduleReferencesAsList.forEach(moduleReferenceAsMap -> { final ModuleReference moduleReference = - objectMapper.convertValue(moduleReferenceAsMap, ModuleReference.class); + jsonObjectMapper.convertToValueType(moduleReferenceAsMap, ModuleReference.class); moduleReferences.add(moduleReference); }); } @@ -159,7 +162,7 @@ public class DmiModelOperations extends DmiOperations { if (yangResourcesAsList != null) { yangResourcesAsList.forEach(yangResourceAsMap -> { final YangResource yangResource = - objectMapper.convertValue(yangResourceAsMap, YangResource.class); + jsonObjectMapper.convertToValueType(yangResourceAsMap, YangResource.class); yangResourcesModuleNameToContentMap.put(yangResource.getModuleName(), yangResource.getYangSource()); }); diff --git a/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/operations/DmiOperations.java b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/operations/DmiOperations.java index 509728061e..2f7376e87c 100644 --- a/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/operations/DmiOperations.java +++ b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/operations/DmiOperations.java @@ -1,6 +1,6 @@ /* * ============LICENSE_START======================================================= - * Copyright (C) 2021 Nordix Foundation + * Copyright (C) 2021-2022 Nordix Foundation * ================================================================================ * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -20,17 +20,19 @@ package org.onap.cps.ncmp.api.impl.operations; -import com.fasterxml.jackson.core.JsonProcessingException; -import com.fasterxml.jackson.databind.ObjectMapper; import com.google.common.base.Strings; import lombok.Getter; +import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; import org.onap.cps.ncmp.api.impl.client.DmiRestClient; import org.onap.cps.ncmp.api.impl.config.NcmpConfiguration; -import org.onap.cps.ncmp.api.impl.exception.NcmpException; +import org.onap.cps.utils.JsonObjectMapper; import org.springframework.http.HttpHeaders; +import org.springframework.stereotype.Service; @Slf4j +@RequiredArgsConstructor +@Service public class DmiOperations { @Getter @@ -44,28 +46,13 @@ public class DmiOperations { } } - protected ObjectMapper objectMapper; - protected PersistenceCmHandleRetriever cmHandlePropertiesRetriever; - protected DmiRestClient dmiRestClient; - protected NcmpConfiguration.DmiProperties dmiProperties; + protected final PersistenceCmHandleRetriever cmHandlePropertiesRetriever; + protected final JsonObjectMapper jsonObjectMapper; + protected final NcmpConfiguration.DmiProperties dmiProperties; + protected final DmiRestClient dmiRestClient; static final String URL_SEPARATOR = "/"; - /** - * Constructor for {@code DmiOperations}. This method also manipulates url properties. - * - * @param dmiRestClient {@code DmiRestClient} - */ - public DmiOperations(final PersistenceCmHandleRetriever cmHandlePropertiesRetriever, - final ObjectMapper objectMapper, - final NcmpConfiguration.DmiProperties dmiProperties, - final DmiRestClient dmiRestClient) { - this.cmHandlePropertiesRetriever = cmHandlePropertiesRetriever; - this.objectMapper = objectMapper; - this.dmiRestClient = dmiRestClient; - this.dmiProperties = dmiProperties; - } - String getCmHandleUrl(final String dmiServiceName, final String cmHandle) { return dmiServiceName + dmiProperties.getDmiBasePath() @@ -95,20 +82,4 @@ public class DmiOperations { return httpHeaders; } - /** - * Convert DmiRequestBody to JSON. - * - * @param dmiRequestBody the dmi request body - * @return DmiRequestBody as JSON - */ - String getDmiRequestBodyAsString(final DmiRequestBody dmiRequestBody) { - try { - return objectMapper.writeValueAsString(dmiRequestBody); - } catch (final JsonProcessingException e) { - log.error("Parsing error occurred while converting Object to JSON."); - throw new NcmpException("Parsing error occurred while converting given object to JSON.", - e.getMessage()); - } - } - } diff --git a/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/api/impl/NetworkCmProxyDataServiceImplModelSyncSpec.groovy b/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/api/impl/NetworkCmProxyDataServiceImplModelSyncSpec.groovy index b7f059a7ce..464757158f 100644 --- a/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/api/impl/NetworkCmProxyDataServiceImplModelSyncSpec.groovy +++ b/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/api/impl/NetworkCmProxyDataServiceImplModelSyncSpec.groovy @@ -1,6 +1,6 @@ /* * ============LICENSE_START======================================================= - * Copyright (C) 2021 Nordix Foundation + * Copyright (C) 2021-2022 Nordix Foundation * ================================================================================ * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -20,22 +20,25 @@ package org.onap.cps.ncmp.api.impl -import com.fasterxml.jackson.databind.ObjectMapper import org.onap.cps.api.CpsAdminService import org.onap.cps.api.CpsModuleService +import org.onap.cps.ncmp.api.impl.operations.DmiDataOperations import org.onap.cps.ncmp.api.impl.operations.DmiModelOperations import org.onap.cps.ncmp.api.models.PersistenceCmHandle import org.onap.cps.spi.model.ModuleReference +import org.onap.cps.utils.JsonObjectMapper import spock.lang.Specification class NetworkCmProxyDataServiceImplModelSyncSpec extends Specification { + def mockJsonObjectMapper = Mock(JsonObjectMapper) def mockCpsModuleService = Mock(CpsModuleService) def mockCpsAdminService = Mock(CpsAdminService) def mockDmiModelOperations = Mock(DmiModelOperations) + def mockDmiDataOperations = Mock(DmiDataOperations) - def objectUnderTest = new NetworkCmProxyDataServiceImpl(null, mockDmiModelOperations, - mockCpsModuleService, null, mockCpsAdminService, new ObjectMapper()) + def objectUnderTest = new NetworkCmProxyDataServiceImpl(null, mockJsonObjectMapper, mockDmiDataOperations, mockDmiModelOperations, + mockCpsModuleService, mockCpsAdminService) def expectedDataspaceName = 'NFP-Operational' diff --git a/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/api/impl/NetworkCmProxyDataServiceImplRegistrationSpec.groovy b/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/api/impl/NetworkCmProxyDataServiceImplRegistrationSpec.groovy index 90fcbfc612..b954b41ef2 100644 --- a/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/api/impl/NetworkCmProxyDataServiceImplRegistrationSpec.groovy +++ b/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/api/impl/NetworkCmProxyDataServiceImplRegistrationSpec.groovy @@ -1,6 +1,6 @@ /* * ============LICENSE_START======================================================= - * Copyright (C) 2021 Nordix Foundation + * Copyright (C) 2021-2022 Nordix Foundation * ================================================================================ * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -22,13 +22,17 @@ package org.onap.cps.ncmp.api.impl import com.fasterxml.jackson.core.JsonProcessingException import com.fasterxml.jackson.databind.ObjectMapper +import org.onap.cps.api.CpsAdminService import org.onap.cps.api.CpsDataService import org.onap.cps.api.CpsModuleService import org.onap.cps.ncmp.api.impl.exception.NcmpException +import org.onap.cps.ncmp.api.impl.operations.DmiDataOperations +import org.onap.cps.ncmp.api.impl.operations.DmiModelOperations import org.onap.cps.ncmp.api.models.CmHandle import org.onap.cps.ncmp.api.models.DmiPluginRegistration import org.onap.cps.spi.exceptions.DataNodeNotFoundException import org.onap.cps.spi.exceptions.DataValidationException +import org.onap.cps.utils.JsonObjectMapper import spock.lang.Shared import spock.lang.Specification @@ -44,7 +48,10 @@ class NetworkCmProxyDataServiceImplRegistrationSpec extends Specification { def mockCpsDataService = Mock(CpsDataService) def mockCpsModuleService = Mock(CpsModuleService) - def spyObjectMapper = Spy(ObjectMapper) + def spiedJsonObjectMapper = Spy(new JsonObjectMapper(new ObjectMapper())) + def mockCpsAdminService = Mock(CpsAdminService) + def mockDmiModelOperations = Mock(DmiModelOperations) + def mockDmiDataOperations = Mock(DmiDataOperations) def noTimestamp = null @@ -102,7 +109,7 @@ class NetworkCmProxyDataServiceImplRegistrationSpec extends Specification { dmiPluginRegistration.createdCmHandles = createdCmHandles dmiPluginRegistration.updatedCmHandles = updatedCmHandles and: 'an json processing exception occurs' - spyObjectMapper.writeValueAsString(_) >> { throw (new JsonProcessingException('')) } + spiedJsonObjectMapper.asJsonString(_) >> { throw (new JsonProcessingException('')) } when: 'registration is updated and modules are synced' objectUnderTest.updateDmiRegistrationAndSyncModule(dmiPluginRegistration) then: 'a data validation exception is thrown' @@ -182,8 +189,8 @@ class NetworkCmProxyDataServiceImplRegistrationSpec extends Specification { } def getObjectUnderTestWithModelSyncDisabled() { - def objectUnderTest = Spy(new NetworkCmProxyDataServiceImpl(null, null, mockCpsModuleService, - mockCpsDataService, null, spyObjectMapper)) + def objectUnderTest = Spy(new NetworkCmProxyDataServiceImpl(mockCpsDataService, spiedJsonObjectMapper, mockDmiDataOperations, mockDmiModelOperations, + mockCpsModuleService, mockCpsAdminService)) objectUnderTest.syncModulesAndCreateAnchor(*_) >> null return objectUnderTest } diff --git a/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/api/impl/NetworkCmProxyDataServiceImplSpec.groovy b/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/api/impl/NetworkCmProxyDataServiceImplSpec.groovy index 5753d7b675..51af0f55e1 100644 --- a/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/api/impl/NetworkCmProxyDataServiceImplSpec.groovy +++ b/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/api/impl/NetworkCmProxyDataServiceImplSpec.groovy @@ -28,6 +28,8 @@ import static org.onap.cps.ncmp.api.impl.operations.DmiRequestBody.OperationEnum import static org.onap.cps.ncmp.api.impl.operations.DmiRequestBody.OperationEnum.READ import static org.onap.cps.ncmp.api.impl.operations.DmiRequestBody.OperationEnum.UPDATE +import org.onap.cps.ncmp.api.impl.operations.DmiModelOperations +import org.onap.cps.utils.JsonObjectMapper import com.fasterxml.jackson.core.JsonProcessingException import com.fasterxml.jackson.databind.ObjectMapper import org.onap.cps.api.CpsAdminService @@ -46,11 +48,12 @@ class NetworkCmProxyDataServiceImplSpec extends Specification { def mockCpsDataService = Mock(CpsDataService) def mockCpsModuleService = Mock(CpsModuleService) def mockCpsAdminService = Mock(CpsAdminService) - def spyObjectMapper = Spy(ObjectMapper) + def spiedJsonObjectMapper = Spy(new JsonObjectMapper(new ObjectMapper())) + def mockDmiModelOperations = Mock(DmiModelOperations) def mockDmiDataOperations = Mock(DmiDataOperations) - def objectUnderTest = new NetworkCmProxyDataServiceImpl(mockDmiDataOperations, null, - mockCpsModuleService, mockCpsDataService, mockCpsAdminService, spyObjectMapper) + def objectUnderTest = new NetworkCmProxyDataServiceImpl(mockCpsDataService, spiedJsonObjectMapper, mockDmiDataOperations, mockDmiModelOperations, + mockCpsModuleService, mockCpsAdminService) def cmHandleXPath = "/dmi-registry/cm-handles[@id='testCmHandle']" @@ -126,9 +129,7 @@ class NetworkCmProxyDataServiceImplSpec extends Specification { mockCpsDataService.getDataNode('NCMP-Admin', 'ncmp-dmi-registry', cmHandleXPath, FetchDescendantsOption.INCLUDE_ALL_DESCENDANTS) >> dataNode and: 'objectMapper not able to parse object' - def mockObjectMapper = Mock(ObjectMapper) - objectUnderTest.objectMapper = mockObjectMapper - mockObjectMapper.writeValueAsString(_) >> { throw new JsonProcessingException('testException') } + spiedJsonObjectMapper.asJsonString(_) >> { throw new JsonProcessingException('testException') } and: 'dmi returns NOK response' mockDmiDataOperations.getResourceDataFromDmi(*_) >> new ResponseEntity<>('NOK-json', HttpStatus.NOT_FOUND) diff --git a/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/api/impl/operations/DmiDataOperationsSpec.groovy b/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/api/impl/operations/DmiDataOperationsSpec.groovy index b9704b1ea0..54ae0aa6fe 100644 --- a/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/api/impl/operations/DmiDataOperationsSpec.groovy +++ b/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/api/impl/operations/DmiDataOperationsSpec.groovy @@ -1,6 +1,6 @@ /* * ============LICENSE_START======================================================= - * Copyright (C) 2021 Nordix Foundation + * Copyright (C) 2021-2022 Nordix Foundation * ================================================================================ * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -20,8 +20,10 @@ package org.onap.cps.ncmp.api.impl.operations -import org.onap.cps.ncmp.api.impl.client.DmiRestClient +import com.fasterxml.jackson.databind.ObjectMapper import org.onap.cps.ncmp.api.impl.config.NcmpConfiguration +import org.onap.cps.utils.JsonObjectMapper +import org.spockframework.spring.SpringBean import org.springframework.beans.factory.annotation.Autowired import org.springframework.boot.test.context.SpringBootTest import org.springframework.http.ResponseEntity @@ -37,6 +39,9 @@ import org.springframework.http.HttpStatus @ContextConfiguration(classes = [NcmpConfiguration.DmiProperties, DmiDataOperations]) class DmiDataOperationsSpec extends DmiOperationsBaseSpec { + @SpringBean + JsonObjectMapper jsonObjectMapper = new JsonObjectMapper(new ObjectMapper()) + @Autowired DmiDataOperations objectUnderTest diff --git a/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/api/impl/operations/DmiModelOperationsSpec.groovy b/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/api/impl/operations/DmiModelOperationsSpec.groovy index 335bc062cc..2efd6c2b3a 100644 --- a/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/api/impl/operations/DmiModelOperationsSpec.groovy +++ b/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/api/impl/operations/DmiModelOperationsSpec.groovy @@ -1,6 +1,6 @@ /* * ============LICENSE_START======================================================= - * Copyright (C) 2021 Nordix Foundation + * Copyright (C) 2021-2022 Nordix Foundation * ================================================================================ * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -21,9 +21,11 @@ package org.onap.cps.ncmp.api.impl.operations import com.fasterxml.jackson.core.JsonProcessingException +import com.fasterxml.jackson.databind.ObjectMapper import org.onap.cps.ncmp.api.impl.config.NcmpConfiguration -import org.onap.cps.ncmp.api.impl.exception.NcmpException import org.onap.cps.spi.model.ModuleReference +import org.onap.cps.utils.JsonObjectMapper +import org.spockframework.spring.SpringBean import org.springframework.beans.factory.annotation.Autowired import org.springframework.boot.test.context.SpringBootTest import org.springframework.http.HttpStatus @@ -41,6 +43,9 @@ class DmiModelOperationsSpec extends DmiOperationsBaseSpec { @Autowired DmiModelOperations objectUnderTest + @SpringBean + JsonObjectMapper spiedJsonObjectMapper = Spy(new JsonObjectMapper(new ObjectMapper())) + def 'Retrieving module references.'() { given: 'a persistence cm handle' mockPersistenceCmHandleRetrieval([]) @@ -158,11 +163,11 @@ class DmiModelOperationsSpec extends DmiOperationsBaseSpec { given: 'a persistence cm handle' mockPersistenceCmHandleRetrieval([]) and: 'a Json processing exception occurs' - spyObjectMapper.writeValueAsString(_) >> {throw (new JsonProcessingException(''))} + spiedJsonObjectMapper.asJsonString(_) >> {throw (new JsonProcessingException('parsing error'))} when: 'a dmi operation is executed' objectUnderTest.getModuleReferences(persistenceCmHandle) then: 'an ncmp exception is thrown' - def exceptionThrown = thrown(NcmpException) + def exceptionThrown = thrown(JsonProcessingException) and: 'the message indicates a parsing error' exceptionThrown.message.toLowerCase().contains('parsing error') } diff --git a/cps-rest/src/main/java/org/onap/cps/rest/controller/DataRestController.java b/cps-rest/src/main/java/org/onap/cps/rest/controller/DataRestController.java index a55b1ba7a1..fc2818b6f6 100755 --- a/cps-rest/src/main/java/org/onap/cps/rest/controller/DataRestController.java +++ b/cps-rest/src/main/java/org/onap/cps/rest/controller/DataRestController.java @@ -2,7 +2,7 @@ * ============LICENSE_START======================================================= * Copyright (C) 2020-2021 Bell Canada. * Modifications Copyright (C) 2021 Pantheon.tech - * Modifications Copyright (C) 2021 Nordix Foundation + * Modifications Copyright (C) 2021-2022 Nordix Foundation * ================================================================================ * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -22,7 +22,6 @@ package org.onap.cps.rest.controller; -import com.google.gson.Gson; import java.time.OffsetDateTime; import java.time.format.DateTimeFormatter; import javax.validation.ValidationException; @@ -32,6 +31,7 @@ import org.onap.cps.api.CpsDataService; import org.onap.cps.rest.api.CpsDataApi; import org.onap.cps.spi.FetchDescendantsOption; import org.onap.cps.utils.DataMapUtils; +import org.onap.cps.utils.JsonObjectMapper; import org.springframework.http.HttpStatus; import org.springframework.http.ResponseEntity; import org.springframework.web.bind.annotation.RequestMapping; @@ -47,17 +47,18 @@ public class DataRestController implements CpsDataApi { private static final DateTimeFormatter ISO_TIMESTAMP_FORMATTER = DateTimeFormatter.ofPattern(ISO_TIMESTAMP_FORMAT); private final CpsDataService cpsDataService; - private final Gson gson; + private final JsonObjectMapper jsonObjectMapper; @Override public ResponseEntity<String> createNode(final String dataspaceName, final String anchorName, final Object jsonData, final String parentNodeXpath, final String observedTimestamp) { + final String jsonDataAsString = jsonObjectMapper.asJsonString(jsonData); if (isRootXpath(parentNodeXpath)) { - cpsDataService.saveData(dataspaceName, anchorName, gson.toJson(jsonData), - toOffsetDateTime(observedTimestamp)); + cpsDataService.saveData(dataspaceName, anchorName, jsonDataAsString, + toOffsetDateTime(observedTimestamp)); } else { - cpsDataService.saveData(dataspaceName, anchorName, parentNodeXpath, gson.toJson(jsonData), - toOffsetDateTime(observedTimestamp)); + cpsDataService.saveData(dataspaceName, anchorName, parentNodeXpath, + jsonDataAsString, toOffsetDateTime(observedTimestamp)); } return new ResponseEntity<>(HttpStatus.CREATED); } @@ -73,8 +74,8 @@ public class DataRestController implements CpsDataApi { @Override public ResponseEntity<String> addListElements(final String parentNodeXpath, final String dataspaceName, final String anchorName, final Object jsonData, final String observedTimestamp) { - cpsDataService.saveListElements(dataspaceName, anchorName, parentNodeXpath, gson.toJson(jsonData), - toOffsetDateTime(observedTimestamp)); + cpsDataService.saveListElements(dataspaceName, anchorName, parentNodeXpath, + jsonObjectMapper.asJsonString(jsonData), toOffsetDateTime(observedTimestamp)); return new ResponseEntity<>(HttpStatus.CREATED); } @@ -91,8 +92,8 @@ public class DataRestController implements CpsDataApi { @Override public ResponseEntity<Object> updateNodeLeaves(final String dataspaceName, final String anchorName, final Object jsonData, final String parentNodeXpath, final String observedTimestamp) { - cpsDataService.updateNodeLeaves(dataspaceName, anchorName, parentNodeXpath, gson.toJson(jsonData), - toOffsetDateTime(observedTimestamp)); + cpsDataService.updateNodeLeaves(dataspaceName, anchorName, parentNodeXpath, + jsonObjectMapper.asJsonString(jsonData), toOffsetDateTime(observedTimestamp)); return new ResponseEntity<>(HttpStatus.OK); } @@ -100,8 +101,8 @@ public class DataRestController implements CpsDataApi { public ResponseEntity<Object> replaceNode(final String dataspaceName, final String anchorName, final Object jsonData, final String parentNodeXpath, final String observedTimestamp) { cpsDataService - .replaceNodeTree(dataspaceName, anchorName, parentNodeXpath, gson.toJson(jsonData), - toOffsetDateTime(observedTimestamp)); + .replaceNodeTree(dataspaceName, anchorName, parentNodeXpath, + jsonObjectMapper.asJsonString(jsonData), toOffsetDateTime(observedTimestamp)); return new ResponseEntity<>(HttpStatus.OK); } @@ -109,8 +110,8 @@ public class DataRestController implements CpsDataApi { public ResponseEntity<Object> replaceListContent(final String parentNodeXpath, final String dataspaceName, final String anchorName, final Object jsonData, final String observedTimestamp) { - cpsDataService.replaceListContent(dataspaceName, anchorName, parentNodeXpath, gson.toJson(jsonData), - toOffsetDateTime(observedTimestamp)); + cpsDataService.replaceListContent(dataspaceName, anchorName, parentNodeXpath, + jsonObjectMapper.asJsonString(jsonData), toOffsetDateTime(observedTimestamp)); return new ResponseEntity<>(HttpStatus.OK); } diff --git a/cps-rest/src/main/java/org/onap/cps/rest/controller/QueryRestController.java b/cps-rest/src/main/java/org/onap/cps/rest/controller/QueryRestController.java index 8aa65a0057..eb422dc69e 100644 --- a/cps-rest/src/main/java/org/onap/cps/rest/controller/QueryRestController.java +++ b/cps-rest/src/main/java/org/onap/cps/rest/controller/QueryRestController.java @@ -1,6 +1,6 @@ /* * ============LICENSE_START======================================================= - * Copyright (C) 2021 Nordix Foundation + * Copyright (C) 2021-2022 Nordix Foundation * ================================================================================ * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -20,18 +20,18 @@ package org.onap.cps.rest.controller; -import com.google.gson.Gson; import java.util.ArrayList; import java.util.Collection; import java.util.List; import java.util.Map; import javax.validation.Valid; +import lombok.RequiredArgsConstructor; import org.onap.cps.api.CpsQueryService; import org.onap.cps.rest.api.CpsQueryApi; import org.onap.cps.spi.FetchDescendantsOption; import org.onap.cps.spi.model.DataNode; import org.onap.cps.utils.DataMapUtils; -import org.springframework.beans.factory.annotation.Autowired; +import org.onap.cps.utils.JsonObjectMapper; import org.springframework.http.HttpStatus; import org.springframework.http.ResponseEntity; import org.springframework.web.bind.annotation.RequestMapping; @@ -39,10 +39,11 @@ import org.springframework.web.bind.annotation.RestController; @RestController @RequestMapping("${rest.api.cps-base-path}") +@RequiredArgsConstructor public class QueryRestController implements CpsQueryApi { - @Autowired - private CpsQueryService cpsQueryService; + private final CpsQueryService cpsQueryService; + private final JsonObjectMapper jsonObjectMapper; @Override public ResponseEntity<Object> getNodesByDataspaceAndAnchorAndCpsPath(final String dataspaceName, @@ -55,6 +56,6 @@ public class QueryRestController implements CpsQueryApi { for (final DataNode dataNode : dataNodes) { dataNodeList.add(DataMapUtils.toDataMap(dataNode)); } - return new ResponseEntity<>(new Gson().toJson(dataNodeList), HttpStatus.OK); + return new ResponseEntity<>(jsonObjectMapper.asJsonString(dataNodeList), HttpStatus.OK); } } diff --git a/cps-rest/src/test/groovy/org/onap/cps/rest/controller/DataRestControllerSpec.groovy b/cps-rest/src/test/groovy/org/onap/cps/rest/controller/DataRestControllerSpec.groovy index fbb5636566..4d75848cf7 100755 --- a/cps-rest/src/test/groovy/org/onap/cps/rest/controller/DataRestControllerSpec.groovy +++ b/cps-rest/src/test/groovy/org/onap/cps/rest/controller/DataRestControllerSpec.groovy @@ -1,6 +1,6 @@ /* * ============LICENSE_START======================================================= - * Copyright (C) 2021 Nordix Foundation + * Copyright (C) 2021-2022 Nordix Foundation * Modifications Copyright (C) 2021 Pantheon.tech * Modifications Copyright (C) 2021 Bell Canada. * ================================================================================ @@ -22,10 +22,12 @@ package org.onap.cps.rest.controller +import com.fasterxml.jackson.databind.ObjectMapper import org.onap.cps.api.CpsDataService import org.onap.cps.spi.model.DataNode import org.onap.cps.spi.model.DataNodeBuilder import org.onap.cps.utils.DateTimeUtility +import org.onap.cps.utils.JsonObjectMapper import org.spockframework.spring.SpringBean import org.springframework.beans.factory.annotation.Autowired import org.springframework.beans.factory.annotation.Value @@ -50,6 +52,9 @@ class DataRestControllerSpec extends Specification { @SpringBean CpsDataService mockCpsDataService = Mock() + @SpringBean + JsonObjectMapper jsonObjectMapper = new JsonObjectMapper(new ObjectMapper()) + @Autowired MockMvc mvc diff --git a/cps-rest/src/test/groovy/org/onap/cps/rest/controller/QueryRestControllerSpec.groovy b/cps-rest/src/test/groovy/org/onap/cps/rest/controller/QueryRestControllerSpec.groovy index 550dec972e..1e42a0060a 100644 --- a/cps-rest/src/test/groovy/org/onap/cps/rest/controller/QueryRestControllerSpec.groovy +++ b/cps-rest/src/test/groovy/org/onap/cps/rest/controller/QueryRestControllerSpec.groovy @@ -1,6 +1,6 @@ /* * ============LICENSE_START======================================================= - * Copyright (C) 2021 Nordix Foundation + * Copyright (C) 2021-2022 Nordix Foundation * Modifications Copyright (C) 2021 Bell Canada. * Modifications Copyright (C) 2021 Pantheon.tech * ================================================================================ @@ -22,17 +22,12 @@ package org.onap.cps.rest.controller -import org.onap.cps.spi.model.DataNode - import static org.onap.cps.spi.FetchDescendantsOption.INCLUDE_ALL_DESCENDANTS import static org.onap.cps.spi.FetchDescendantsOption.OMIT_DESCENDANTS import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get -import com.google.gson.Gson -import org.modelmapper.ModelMapper -import org.onap.cps.api.CpsAdminService -import org.onap.cps.api.CpsDataService -import org.onap.cps.api.CpsModuleService +import com.fasterxml.jackson.databind.ObjectMapper +import org.onap.cps.utils.JsonObjectMapper import org.onap.cps.api.CpsQueryService import org.onap.cps.spi.model.DataNodeBuilder import org.spockframework.spring.SpringBean @@ -49,6 +44,9 @@ class QueryRestControllerSpec extends Specification { @SpringBean CpsQueryService mockCpsQueryService = Mock() + @SpringBean + JsonObjectMapper jsonObjectMapper = new JsonObjectMapper(new ObjectMapper()) + @Autowired MockMvc mvc diff --git a/cps-rest/src/test/groovy/org/onap/cps/rest/exceptions/CpsRestExceptionHandlerSpec.groovy b/cps-rest/src/test/groovy/org/onap/cps/rest/exceptions/CpsRestExceptionHandlerSpec.groovy index 658dcaa70e..a2eaa525e0 100644 --- a/cps-rest/src/test/groovy/org/onap/cps/rest/exceptions/CpsRestExceptionHandlerSpec.groovy +++ b/cps-rest/src/test/groovy/org/onap/cps/rest/exceptions/CpsRestExceptionHandlerSpec.groovy @@ -1,7 +1,7 @@ /* * ============LICENSE_START======================================================= * Copyright (C) 2020 Pantheon.tech - * Modifications Copyright (C) 2021 Nordix Foundation + * Modifications Copyright (C) 2021-2022 Nordix Foundation * Modifications Copyright (C) 2021 Bell Canada. * ================================================================================ * Licensed under the Apache License, Version 2.0 (the "License"); @@ -38,6 +38,7 @@ import org.onap.cps.spi.exceptions.ModelValidationException import org.onap.cps.spi.exceptions.NotFoundInDataspaceException import org.onap.cps.spi.exceptions.SchemaSetInUseException import org.onap.cps.spi.exceptions.DataspaceInUseException +import org.onap.cps.utils.JsonObjectMapper import org.spockframework.spring.SpringBean import org.springframework.beans.factory.annotation.Autowired import org.springframework.beans.factory.annotation.Value @@ -58,19 +59,22 @@ import static org.springframework.test.web.servlet.request.MockMvcRequestBuilder class CpsRestExceptionHandlerSpec extends Specification { @SpringBean - CpsAdminService mockCpsAdminService = Mock() + CpsAdminService mockCpsAdminService = Stub() @SpringBean - CpsModuleService mockCpsModuleService = Mock() + CpsModuleService mockCpsModuleService = Stub() @SpringBean - CpsDataService mockCpsDataService = Mock() + CpsDataService mockCpsDataService = Stub() @SpringBean - CpsQueryService mockCpsQueryService = Mock() + CpsQueryService mockCpsQueryService = Stub() @SpringBean - ModelMapper modelMapper = Mock() + ModelMapper modelMapper = Stub() + + @SpringBean + JsonObjectMapper jsonObjectMapper = Stub() @Autowired MockMvc mvc diff --git a/cps-ri/src/main/java/org/onap/cps/spi/impl/CpsDataPersistenceServiceImpl.java b/cps-ri/src/main/java/org/onap/cps/spi/impl/CpsDataPersistenceServiceImpl.java index c616c8face..c6e28aba91 100644 --- a/cps-ri/src/main/java/org/onap/cps/spi/impl/CpsDataPersistenceServiceImpl.java +++ b/cps-ri/src/main/java/org/onap/cps/spi/impl/CpsDataPersistenceServiceImpl.java @@ -1,6 +1,6 @@ /* * ============LICENSE_START======================================================= - * Copyright (C) 2021 Nordix Foundation + * Copyright (C) 2021-2022 Nordix Foundation * Modifications Copyright (C) 2021 Pantheon.tech * Modifications Copyright (C) 2020-2021 Bell Canada. * ================================================================================ @@ -24,12 +24,8 @@ package org.onap.cps.spi.impl; import static org.onap.cps.spi.FetchDescendantsOption.INCLUDE_ALL_DESCENDANTS; -import com.fasterxml.jackson.core.JsonProcessingException; -import com.fasterxml.jackson.databind.ObjectMapper; import com.google.common.collect.ImmutableSet; import com.google.common.collect.ImmutableSet.Builder; -import com.google.gson.Gson; -import com.google.gson.GsonBuilder; import java.util.Collection; import java.util.Collections; import java.util.HashMap; @@ -41,6 +37,7 @@ import java.util.regex.Matcher; import java.util.regex.Pattern; import java.util.stream.Collectors; import javax.transaction.Transactional; +import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; import org.hibernate.StaleStateException; import org.onap.cps.cpspath.parser.CpsPathQuery; @@ -53,43 +50,29 @@ import org.onap.cps.spi.exceptions.AlreadyDefinedException; import org.onap.cps.spi.exceptions.ConcurrencyException; import org.onap.cps.spi.exceptions.CpsPathException; import org.onap.cps.spi.exceptions.DataNodeNotFoundException; -import org.onap.cps.spi.exceptions.DataValidationException; import org.onap.cps.spi.model.DataNode; import org.onap.cps.spi.model.DataNodeBuilder; import org.onap.cps.spi.repository.AnchorRepository; import org.onap.cps.spi.repository.DataspaceRepository; import org.onap.cps.spi.repository.FragmentRepository; +import org.onap.cps.utils.JsonObjectMapper; import org.springframework.dao.DataIntegrityViolationException; import org.springframework.stereotype.Service; @Service @Slf4j +@RequiredArgsConstructor public class CpsDataPersistenceServiceImpl implements CpsDataPersistenceService { - private DataspaceRepository dataspaceRepository; + private final DataspaceRepository dataspaceRepository; - private AnchorRepository anchorRepository; + private final AnchorRepository anchorRepository; - private FragmentRepository fragmentRepository; + private final FragmentRepository fragmentRepository; - private final ObjectMapper objectMapper; + private final JsonObjectMapper jsonObjectMapper; - /** - * Constructor. - * - * @param dataspaceRepository dataspaceRepository - * @param anchorRepository anchorRepository - * @param fragmentRepository fragmentRepository - */ - public CpsDataPersistenceServiceImpl(final DataspaceRepository dataspaceRepository, - final AnchorRepository anchorRepository, final FragmentRepository fragmentRepository) { - this.dataspaceRepository = dataspaceRepository; - this.anchorRepository = anchorRepository; - this.fragmentRepository = fragmentRepository; - objectMapper = new ObjectMapper(); - } - private static final Gson GSON = new GsonBuilder().create(); private static final String REG_EX_FOR_OPTIONAL_LIST_INDEX = "(\\[@[\\s\\S]+?]){0,1})"; private static final Pattern REG_EX_PATTERN_FOR_LIST_ELEMENT_KEY_PREDICATE = Pattern.compile("\\[(\\@([^\\/]{0,9999}))\\]$"); @@ -152,7 +135,7 @@ public class CpsDataPersistenceServiceImpl implements CpsDataPersistenceService * @param dataNodeToBeConverted dataNode * @return a Fragment built from current DataNode */ - private static FragmentEntity convertToFragmentWithAllDescendants(final DataspaceEntity dataspaceEntity, + private FragmentEntity convertToFragmentWithAllDescendants(final DataspaceEntity dataspaceEntity, final AnchorEntity anchorEntity, final DataNode dataNodeToBeConverted) { final FragmentEntity parentFragment = toFragmentEntity(dataspaceEntity, anchorEntity, dataNodeToBeConverted); final Builder<FragmentEntity> childFragmentsImmutableSetBuilder = ImmutableSet.builder(); @@ -177,13 +160,13 @@ public class CpsDataPersistenceServiceImpl implements CpsDataPersistenceService } } - private static FragmentEntity toFragmentEntity(final DataspaceEntity dataspaceEntity, + private FragmentEntity toFragmentEntity(final DataspaceEntity dataspaceEntity, final AnchorEntity anchorEntity, final DataNode dataNode) { return FragmentEntity.builder() .dataspace(dataspaceEntity) .anchor(anchorEntity) .xpath(dataNode.getXpath()) - .attributes(GSON.toJson(dataNode.getLeaves())) + .attributes(jsonObjectMapper.asJsonString(dataNode.getLeaves())) .build(); } @@ -249,14 +232,7 @@ public class CpsDataPersistenceServiceImpl implements CpsDataPersistenceService final List<DataNode> childDataNodes = getChildDataNodes(fragmentEntity, fetchDescendantsOption); Map<String, Object> leaves = new HashMap<>(); if (fragmentEntity.getAttributes() != null) { - try { - leaves = objectMapper.readValue(fragmentEntity.getAttributes(), Map.class); - } catch (final JsonProcessingException jsonProcessingException) { - final String message = "Parsing error occurred while processing fragmentEntity attributes."; - log.error(message); - throw new DataValidationException(message, - jsonProcessingException.getMessage(), jsonProcessingException); - } + leaves = jsonObjectMapper.convertJsonString(fragmentEntity.getAttributes(), Map.class); } return new DataNodeBuilder() .withXpath(fragmentEntity.getXpath()) @@ -278,7 +254,7 @@ public class CpsDataPersistenceServiceImpl implements CpsDataPersistenceService public void updateDataLeaves(final String dataspaceName, final String anchorName, final String xpath, final Map<String, Object> leaves) { final FragmentEntity fragmentEntity = getFragmentByXpath(dataspaceName, anchorName, xpath); - fragmentEntity.setAttributes(GSON.toJson(leaves)); + fragmentEntity.setAttributes(jsonObjectMapper.asJsonString(leaves)); fragmentRepository.save(fragmentEntity); } @@ -296,10 +272,10 @@ public class CpsDataPersistenceServiceImpl implements CpsDataPersistenceService } } - private static void replaceDataNodeTree(final FragmentEntity existingFragmentEntity, + private void replaceDataNodeTree(final FragmentEntity existingFragmentEntity, final DataNode newDataNode) { - existingFragmentEntity.setAttributes(GSON.toJson(newDataNode.getLeaves())); + existingFragmentEntity.setAttributes(jsonObjectMapper.asJsonString(newDataNode.getLeaves())); final Map<String, FragmentEntity> existingChildrenByXpath = existingFragmentEntity.getChildFragments() .stream().collect(Collectors.toMap(FragmentEntity::getXpath, childFragmentEntity -> childFragmentEntity)); @@ -412,7 +388,7 @@ public class CpsDataPersistenceServiceImpl implements CpsDataPersistenceService return firstChildNodeXpath.substring(0, firstChildNodeXpath.lastIndexOf("[") + 1); } - private static FragmentEntity getFragmentForReplacement(final FragmentEntity parentEntity, + private FragmentEntity getFragmentForReplacement(final FragmentEntity parentEntity, final DataNode newListElement, final FragmentEntity existingListElementEntity) { if (existingListElementEntity == null) { @@ -433,10 +409,11 @@ public class CpsDataPersistenceServiceImpl implements CpsDataPersistenceService return !existingListElementsByXpath.containsKey(replacementDataNode.getXpath()); } - private static void copyAttributesFromNewListElement(final FragmentEntity existingListElementEntity, + private void copyAttributesFromNewListElement(final FragmentEntity existingListElementEntity, final DataNode newListElement) { final FragmentEntity replacementFragmentEntity = - FragmentEntity.builder().attributes(GSON.toJson(newListElement.getLeaves())).build(); + FragmentEntity.builder().attributes(jsonObjectMapper.asJsonString( + newListElement.getLeaves())).build(); existingListElementEntity.setAttributes(replacementFragmentEntity.getAttributes()); } diff --git a/cps-ri/src/main/java/org/onap/cps/spi/repository/FragmentRepositoryCpsPathQueryImpl.java b/cps-ri/src/main/java/org/onap/cps/spi/repository/FragmentRepositoryCpsPathQueryImpl.java index fddedcad27..37202498fb 100644 --- a/cps-ri/src/main/java/org/onap/cps/spi/repository/FragmentRepositoryCpsPathQueryImpl.java +++ b/cps-ri/src/main/java/org/onap/cps/spi/repository/FragmentRepositoryCpsPathQueryImpl.java @@ -1,6 +1,6 @@ /*- * ============LICENSE_START======================================================= - * Copyright (C) 2021 Nordix Foundation. + * Copyright (C) 2021-2022 Nordix Foundation. * ================================================================================ * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -20,18 +20,19 @@ package org.onap.cps.spi.repository; -import com.google.gson.Gson; -import com.google.gson.GsonBuilder; import java.util.HashMap; import java.util.List; import java.util.Map; import javax.persistence.EntityManager; import javax.persistence.PersistenceContext; import javax.persistence.Query; +import lombok.RequiredArgsConstructor; import org.onap.cps.cpspath.parser.CpsPathPrefixType; import org.onap.cps.cpspath.parser.CpsPathQuery; import org.onap.cps.spi.entities.FragmentEntity; +import org.onap.cps.utils.JsonObjectMapper; +@RequiredArgsConstructor public class FragmentRepositoryCpsPathQueryImpl implements FragmentRepositoryCpsPathQuery { public static final String SIMILAR_TO_ABSOLUTE_PATH_PREFIX = "%/"; @@ -39,8 +40,7 @@ public class FragmentRepositoryCpsPathQueryImpl implements FragmentRepositoryCps @PersistenceContext private EntityManager entityManager; - - private static final Gson GSON = new GsonBuilder().create(); + private final JsonObjectMapper jsonObjectMapper; @Override public List<FragmentEntity> findByAnchorAndCpsPath(final int anchorId, final CpsPathQuery cpsPathQuery) { @@ -52,7 +52,8 @@ public class FragmentRepositoryCpsPathQueryImpl implements FragmentRepositoryCps queryParameters.put("xpathRegex", xpathRegex); if (cpsPathQuery.hasLeafConditions()) { sqlStringBuilder.append(" AND attributes @> :leafDataAsJson\\:\\:jsonb"); - queryParameters.put("leafDataAsJson", GSON.toJson(cpsPathQuery.getLeavesData())); + queryParameters.put("leafDataAsJson", jsonObjectMapper.asJsonString( + cpsPathQuery.getLeavesData())); } addTextFunctionCondition(cpsPathQuery, sqlStringBuilder, queryParameters); diff --git a/cps-ri/src/test/groovy/org/onap/cps/spi/impl/CpsDataPersistenceServiceIntegrationSpec.groovy b/cps-ri/src/test/groovy/org/onap/cps/spi/impl/CpsDataPersistenceServiceIntegrationSpec.groovy index 85e1155cff..69e6aa81c4 100755 --- a/cps-ri/src/test/groovy/org/onap/cps/spi/impl/CpsDataPersistenceServiceIntegrationSpec.groovy +++ b/cps-ri/src/test/groovy/org/onap/cps/spi/impl/CpsDataPersistenceServiceIntegrationSpec.groovy @@ -1,6 +1,6 @@ /* * ============LICENSE_START======================================================= - * Copyright (C) 2021 Nordix Foundation + * Copyright (C) 2021-2022 Nordix Foundation * Modifications Copyright (C) 2021 Pantheon.tech * Modifications Copyright (C) 2021 Bell Canada. * ================================================================================ @@ -21,9 +21,8 @@ */ package org.onap.cps.spi.impl +import com.fasterxml.jackson.databind.ObjectMapper import com.google.common.collect.ImmutableSet -import com.google.gson.Gson -import com.google.gson.GsonBuilder import org.onap.cps.spi.CpsDataPersistenceService import org.onap.cps.spi.entities.FragmentEntity import org.onap.cps.spi.exceptions.AlreadyDefinedException @@ -32,6 +31,7 @@ import org.onap.cps.spi.exceptions.DataNodeNotFoundException import org.onap.cps.spi.exceptions.DataspaceNotFoundException import org.onap.cps.spi.model.DataNode import org.onap.cps.spi.model.DataNodeBuilder +import org.onap.cps.utils.JsonObjectMapper import org.springframework.beans.factory.annotation.Autowired import org.springframework.test.context.jdbc.Sql @@ -46,7 +46,7 @@ class CpsDataPersistenceServiceIntegrationSpec extends CpsPersistenceSpecBase { @Autowired CpsDataPersistenceService objectUnderTest - static final Gson GSON = new GsonBuilder().create() + static final JsonObjectMapper jsonObjectMapper = new JsonObjectMapper(new ObjectMapper()) static final String SET_DATA = '/data/fragment.sql' static final long ID_DATA_NODE_WITH_DESCENDANTS = 4001 @@ -549,7 +549,7 @@ class CpsDataPersistenceServiceIntegrationSpec extends CpsPersistenceSpecBase { } static Map<String, Object> getLeavesMap(FragmentEntity fragmentEntity) { - return GSON.fromJson(fragmentEntity.getAttributes(), Map<String, Object>.class) + return jsonObjectMapper.convertJsonString(fragmentEntity.getAttributes(), Map<String, Object>.class) } def static assertLeavesMaps(actualLeavesMap, expectedLeavesMap) { diff --git a/cps-ri/src/test/groovy/org/onap/cps/spi/impl/CpsDataPersistenceServiceSpec.groovy b/cps-ri/src/test/groovy/org/onap/cps/spi/impl/CpsDataPersistenceServiceSpec.groovy index 162a56682a..7166008ad3 100644 --- a/cps-ri/src/test/groovy/org/onap/cps/spi/impl/CpsDataPersistenceServiceSpec.groovy +++ b/cps-ri/src/test/groovy/org/onap/cps/spi/impl/CpsDataPersistenceServiceSpec.groovy @@ -18,6 +18,7 @@ package org.onap.cps.spi.impl +import com.fasterxml.jackson.databind.ObjectMapper import org.hibernate.StaleStateException import org.onap.cps.spi.FetchDescendantsOption import org.onap.cps.spi.entities.FragmentEntity @@ -27,6 +28,7 @@ import org.onap.cps.spi.model.DataNodeBuilder import org.onap.cps.spi.repository.AnchorRepository import org.onap.cps.spi.repository.DataspaceRepository import org.onap.cps.spi.repository.FragmentRepository +import org.onap.cps.utils.JsonObjectMapper import spock.lang.Specification @@ -35,9 +37,10 @@ class CpsDataPersistenceServiceSpec extends Specification { def mockDataspaceRepository = Mock(DataspaceRepository) def mockAnchorRepository = Mock(AnchorRepository) def mockFragmentRepository = Mock(FragmentRepository) + def jsonObjectMapper = new JsonObjectMapper(new ObjectMapper()) def objectUnderTest = new CpsDataPersistenceServiceImpl( - mockDataspaceRepository, mockAnchorRepository, mockFragmentRepository) + mockDataspaceRepository, mockAnchorRepository, mockFragmentRepository, jsonObjectMapper) def 'Handling of StaleStateException (caused by concurrent updates) during data node tree update.'() { diff --git a/cps-ri/src/test/groovy/org/onap/cps/spi/impl/CpsPersistenceSpecBase.groovy b/cps-ri/src/test/groovy/org/onap/cps/spi/impl/CpsPersistenceSpecBase.groovy index 8ec5c90e9c..8e13be2ae1 100644 --- a/cps-ri/src/test/groovy/org/onap/cps/spi/impl/CpsPersistenceSpecBase.groovy +++ b/cps-ri/src/test/groovy/org/onap/cps/spi/impl/CpsPersistenceSpecBase.groovy @@ -1,6 +1,6 @@ /* * ============LICENSE_START======================================================= - * Copyright (C) 2021 Nordix Foundation + * Copyright (C) 2021-2022 Nordix Foundation * Modifications Copyright (C) 2021 Pantheon.tech * Modifications Copyright (C) 2021 Bell Canada. * ================================================================================ @@ -22,11 +22,14 @@ package org.onap.cps.spi.impl +import com.fasterxml.jackson.databind.ObjectMapper import org.onap.cps.DatabaseTestContainer import org.onap.cps.spi.repository.AnchorRepository import org.onap.cps.spi.repository.DataspaceRepository import org.onap.cps.spi.repository.FragmentRepository import org.onap.cps.spi.repository.YangResourceRepository +import org.onap.cps.utils.JsonObjectMapper +import org.spockframework.spring.SpringBean import org.springframework.beans.factory.annotation.Autowired import org.springframework.boot.test.context.SpringBootTest import org.testcontainers.spock.Testcontainers @@ -52,6 +55,9 @@ class CpsPersistenceSpecBase extends Specification { @Autowired FragmentRepository fragmentRepository + @SpringBean + JsonObjectMapper jsonObjectMapper = new JsonObjectMapper(new ObjectMapper()) + static final String CLEAR_DATA = '/data/clear-all.sql' static final String DATASPACE_NAME = 'DATASPACE-001' diff --git a/cps-service/pom.xml b/cps-service/pom.xml index b875bbb894..bfc3931bc8 100644 --- a/cps-service/pom.xml +++ b/cps-service/pom.xml @@ -114,6 +114,11 @@ <scope>test</scope>
</dependency>
<dependency>
+ <groupId>org.codehaus.groovy</groupId>
+ <artifactId>groovy-json</artifactId>
+ <scope>test</scope>
+ </dependency>
+ <dependency>
<groupId>org.spockframework</groupId>
<artifactId>spock-core</artifactId>
<scope>test</scope>
diff --git a/cps-service/src/main/java/org/onap/cps/utils/JsonObjectMapper.java b/cps-service/src/main/java/org/onap/cps/utils/JsonObjectMapper.java new file mode 100644 index 0000000000..2459b51af4 --- /dev/null +++ b/cps-service/src/main/java/org/onap/cps/utils/JsonObjectMapper.java @@ -0,0 +1,90 @@ +/* + * ============LICENSE_START======================================================= + * Copyright (C) 2022 Nordix Foundation + * ================================================================================ + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * SPDX-License-Identifier: Apache-2.0 + * ============LICENSE_END========================================================= + */ + +package org.onap.cps.utils; + +import com.fasterxml.jackson.core.JsonProcessingException; +import com.fasterxml.jackson.databind.ObjectMapper; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.onap.cps.spi.exceptions.DataValidationException; +import org.springframework.stereotype.Component; + +@Slf4j +@RequiredArgsConstructor +@Component +public class JsonObjectMapper { + + private final ObjectMapper objectMapper; + + /** + * Serializing generic java object to JSON using Jackson. + * + * @param object any java object value + * @return the generated JSON as a string. + */ + public String asJsonString(final Object object) { + try { + return objectMapper.writeValueAsString(object); + } catch (final JsonProcessingException e) { + log.error("Parsing error occurred while converting Object to JSON string."); + throw new DataValidationException("Parsing error occurred while converting given object to JSON string.", + e.getMessage()); + } + } + + /** + * Constructing JavaType out of given type (typically java.lang.Class). + * Allow efficient value conversions for structurally compatible json objects, + * according to standard Jackson configuration. + * + * @param jsonObject structurally compatible json object + * @param valueType compatible Object class type + * @param <T> type parameter + * @return a class object of specific class type 'T' + */ + public <T> T convertToValueType(final Object jsonObject, final Class<T> valueType) { + try { + return objectMapper.convertValue(jsonObject, valueType); + } catch (final IllegalArgumentException e) { + log.error("Found structurally incompatible object while converting into value type."); + throw new DataValidationException("Found structurally incompatible object while converting " + + "into value type.", e.getMessage()); + } + } + + /** + * Deserialize JSON content from given JSON content String. + * + * @param jsonContent JSON content + * @param valueType compatible Object class type + * @param <T> type parameter + * @return a class object of specific class type 'T' + */ + public <T> T convertJsonString(final String jsonContent, final Class<T> valueType) { + try { + return objectMapper.readValue(jsonContent, valueType); + } catch (final JsonProcessingException e) { + log.error("Parsing error occurred while converting JSON content to specific class type."); + throw new DataValidationException("Parsing error occurred while converting " + + "JSON content to specific class type.", e.getMessage()); + } + } +} diff --git a/cps-service/src/test/groovy/org/onap/cps/utils/JsonObjectMapperSpec.groovy b/cps-service/src/test/groovy/org/onap/cps/utils/JsonObjectMapperSpec.groovy new file mode 100644 index 0000000000..f9b8febd43 --- /dev/null +++ b/cps-service/src/test/groovy/org/onap/cps/utils/JsonObjectMapperSpec.groovy @@ -0,0 +1,87 @@ +/* + * ============LICENSE_START======================================================= + * Copyright (C) 2022 Nordix Foundation + * ================================================================================ + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * SPDX-License-Identifier: Apache-2.0 + * ============LICENSE_END========================================================= + */ + +package org.onap.cps.utils + + +import com.fasterxml.jackson.databind.ObjectMapper +import groovy.json.JsonSlurper +import org.onap.cps.TestUtils +import org.onap.cps.spi.exceptions.DataValidationException +import spock.lang.Specification + +class JsonObjectMapperSpec extends Specification { + + def spiedObjectMapper = Spy(ObjectMapper) + def jsonObjectMapper = new JsonObjectMapper(spiedObjectMapper) + + def 'Map a structured object to json String.'() { + given: 'an object model' + def object = spiedObjectMapper.readValue(TestUtils.getResourceFileContent('bookstore.json'), Object) + when: 'the object is mapped to string' + def content = jsonObjectMapper.asJsonString(object); + then: 'the result is a valid json string (can be parsed)' + def contentMap = new JsonSlurper().parseText(content) + and: 'the parsed content is as expected' + assert contentMap.'test:bookstore'.'bookstore-name' == 'Chapters' + } + + def 'Map a structurally compatible object to class object of specific class type T.'() { + given: 'a map object model' + def contentMap = new JsonSlurper().parseText(TestUtils.getResourceFileContent('bookstore.json')) + when: 'converted into a Map' + def result = jsonObjectMapper.convertToValueType(contentMap, Map); + then: 'the result is a mapped into class of type Map' + assert result instanceof Map + and: 'the map contains the expected key' + assert result.containsKey('test:bookstore') + assert result.'test:bookstore'.categories[0].name == 'SciFi' + + } + + def 'Mapping an unstructured json string to class object of specific class type T.'() { + given: 'Unstructured json string' + def content = '{ "nest": { "birds": "bird"] } }' + when: 'mapping json string to given class type' + def contentMap = jsonObjectMapper.convertJsonString(content, Map); + then: 'an exception is thrown' + thrown(DataValidationException) + } + + def 'Map an incompatible object to class object of specific class type T.'() { + given: 'a map object model' + def contentMap = new JsonSlurper().parseText(TestUtils.getResourceFileContent('bookstore.json')) + and: 'Object mapper throws an exception' + spiedObjectMapper.convertValue(*_) >> { throw new IllegalArgumentException() } + when: 'converted into specific class type' + jsonObjectMapper.convertToValueType(contentMap, Object); + then: 'an exception is thrown' + thrown(DataValidationException) + } + + def 'Map a unstructured object to json String.'() { + given: 'Unstructured object' + def object = new Object() + when: 'the object is mapped to string' + jsonObjectMapper.asJsonString(object); + then: 'an exception is thrown' + thrown(DataValidationException) + } +} |