From ea01d09861289ff162dff318713d1c24eba89259 Mon Sep 17 00:00:00 2001 From: sourabh_sourabh Date: Mon, 28 Mar 2022 13:21:55 +0100 Subject: Async: NCMP Rest impl. including Request ID generation - Restructured code and moved some of them at controller and service layer. - Unit is fixed and organized to it's belonging classes. Issue-ID: CPS-828 Signed-off-by: sourabh_sourabh Change-Id: I0919218e35b1d11cb579d707f376b76de80409da --- .../rest/controller/NetworkCmProxyController.java | 61 ++++++++++++++++++++-- .../controller/NetworkCmProxyControllerSpec.groovy | 61 +++++++++++++++++----- 2 files changed, 104 insertions(+), 18 deletions(-) (limited to 'cps-ncmp-rest/src') 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 de6c3c4f3..19b919333 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 @@ -31,13 +31,17 @@ import static org.onap.cps.ncmp.api.impl.operations.DmiRequestBody.OperationEnum import java.util.ArrayList; import java.util.Collection; import java.util.Collections; +import java.util.HashMap; import java.util.List; +import java.util.Map; +import java.util.UUID; import java.util.stream.Collectors; import javax.validation.Valid; import javax.validation.constraints.NotNull; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; import org.onap.cps.ncmp.api.NetworkCmProxyDataService; +import org.onap.cps.ncmp.api.impl.exception.InvalidTopicException; import org.onap.cps.ncmp.api.models.NcmpServiceCmHandle; import org.onap.cps.ncmp.rest.api.NetworkCmProxyApi; import org.onap.cps.ncmp.rest.model.CmHandleProperties; @@ -50,6 +54,7 @@ import org.onap.cps.ncmp.rest.model.ModuleNameAsJsonObject; import org.onap.cps.ncmp.rest.model.ModuleNamesAsJsonArray; import org.onap.cps.ncmp.rest.model.RestModuleReference; import org.onap.cps.ncmp.rest.model.RestOutputCmHandle; +import org.onap.cps.utils.CpsValidator; import org.onap.cps.utils.JsonObjectMapper; import org.springframework.http.HttpStatus; import org.springframework.http.ResponseEntity; @@ -63,6 +68,9 @@ import org.springframework.web.bind.annotation.RestController; public class NetworkCmProxyController implements NetworkCmProxyApi { private static final String NO_BODY = null; + private static final String NO_REQUEST_ID = null; + private static final String NO_TOPIC = null; + public static final String ASYNC_REQUEST_ID = "requestId"; private final NetworkCmProxyDataService networkCmProxyDataService; private final JsonObjectMapper jsonObjectMapper; @@ -82,11 +90,19 @@ public class NetworkCmProxyController implements NetworkCmProxyApi { final @NotNull @Valid String resourceIdentifier, final @Valid String optionsParamInQuery, final @Valid String topicParamInQuery) { + final ResponseEntity> asyncResponse = populateAsyncResponse(topicParamInQuery); + final Map asyncResponseData = asyncResponse.getBody(); + final Object responseObject = networkCmProxyDataService.getResourceDataOperationalForCmHandle(cmHandle, resourceIdentifier, optionsParamInQuery, - topicParamInQuery); - return ResponseEntity.ok(responseObject); + asyncResponseData == null ? NO_TOPIC : topicParamInQuery, + asyncResponseData == null ? NO_REQUEST_ID : asyncResponseData.get(ASYNC_REQUEST_ID).toString()); + + if (asyncResponseData == null) { + return ResponseEntity.ok(responseObject); + } + return ResponseEntity.ok(asyncResponse); } /** @@ -103,11 +119,19 @@ public class NetworkCmProxyController implements NetworkCmProxyApi { final @NotNull @Valid String resourceIdentifier, final @Valid String optionsParamInQuery, final @Valid String topicParamInQuery) { + final ResponseEntity> asyncResponse = populateAsyncResponse(topicParamInQuery); + final Map asyncResponseData = asyncResponse.getBody(); + final Object responseObject = networkCmProxyDataService.getResourceDataPassThroughRunningForCmHandle(cmHandle, resourceIdentifier, optionsParamInQuery, - topicParamInQuery); - return ResponseEntity.ok(responseObject); + asyncResponseData == null ? NO_TOPIC : topicParamInQuery, + asyncResponseData == null ? NO_REQUEST_ID : asyncResponseData.get(ASYNC_REQUEST_ID).toString()); + + if (asyncResponseData == null) { + return ResponseEntity.ok(responseObject); + } + return ResponseEntity.ok(asyncResponse); } @Override @@ -257,4 +281,33 @@ public class NetworkCmProxyController implements NetworkCmProxyApi { restOutputCmHandle.setPublicCmHandleProperties(cmHandlePublicProperties); return restOutputCmHandle; } + + private ResponseEntity> populateAsyncResponse(final String topicParamInQuery) { + final boolean processAsynchronously = hasTopicParameter(topicParamInQuery); + final Map responseData; + if (processAsynchronously) { + responseData = getAsyncResponseData(); + } else { + responseData = null; + } + return ResponseEntity.ok().body(responseData); + } + + private static boolean hasTopicParameter(final String topicName) { + if (topicName == null) { + return false; + } + if (CpsValidator.validateTopicName(topicName)) { + return true; + } + throw new InvalidTopicException("Topic name " + topicName + " is invalid", "invalid topic"); + } + + private Map getAsyncResponseData() { + final Map asyncResponseData = new HashMap<>(1); + final String resourceDataRequestId = UUID.randomUUID().toString(); + asyncResponseData.put(ASYNC_REQUEST_ID, resourceDataRequestId); + return asyncResponseData; + } + } 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 88633450b..d79d9622e 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 @@ -72,6 +72,7 @@ class NetworkCmProxyControllerSpec extends Specification { @Shared def NO_TOPIC = null + def NO_REQUEST_ID = null def 'Get Resource Data from pass-through operational.'() { given: 'resource data url' @@ -86,14 +87,15 @@ class NetworkCmProxyControllerSpec extends Specification { 1 * mockNetworkCmProxyDataService.getResourceDataOperationalForCmHandle('testCmHandle', 'parent/child', '(a=1,b=2)', - NO_TOPIC) + NO_TOPIC, + NO_REQUEST_ID) and: 'response status is Ok' response.status == HttpStatus.OK.value() } - def 'Get Resource Data from pass-through operational with #scenario.'() { + def 'Get Resource Data from #datastoreInUrl with #scenario.'() { given: 'resource data url' - def getUrl = "$ncmpBasePathV1/ch/testCmHandle/data/ds/ncmp-datastore:passthrough-operational" + + def getUrl = "$ncmpBasePathV1/ch/testCmHandle/data/ds/ncmp-datastore:${datastoreInUrl}" + "?resourceIdentifier=parent/child&options=(a=1,b=2)${topicQueryParam}" when: 'get data resource request is performed' def response = mvc.perform( @@ -101,19 +103,30 @@ class NetworkCmProxyControllerSpec extends Specification { .contentType(MediaType.APPLICATION_JSON) ).andReturn().response then: 'the NCMP data service is called with operational data for cm handle' - 1 * mockNetworkCmProxyDataService.getResourceDataOperationalForCmHandle('testCmHandle', + expectedNumberOfMethodExecutions + * mockNetworkCmProxyDataService."${expectedMethodName}"('testCmHandle', 'parent/child', '(a=1,b=2)', - expectedTopicName) - and: 'response status is Ok' - response.status == HttpStatus.OK.value() + expectedTopicName, + _) + then: 'response status is expected' + response.status == expectedHttpStatus where: 'the following parameters are used' - scenario | topicQueryParam || expectedTopicName - 'Url with valid topic' | "&topic=my-topic-name" || "my-topic-name" - 'No topic in url' | '' || NO_TOPIC - 'Null topic in url' | "&topic=null" || "null" - 'Empty topic in url' | "&topic=\"\"" || "\"\"" - 'Missing topic in url' | "&topic=" || "" + scenario | datastoreInUrl | topicQueryParam || expectedTopicName | expectedMethodName | expectedNumberOfMethodExecutions | expectedHttpStatus + 'url with valid topic' | 'passthrough-operational' | '&topic=my-topic-name' || 'my-topic-name' | 'getResourceDataOperationalForCmHandle' | 1 | HttpStatus.OK.value() + 'no topic in url' | 'passthrough-operational' | '' || NO_TOPIC | 'getResourceDataOperationalForCmHandle' | 1 | HttpStatus.OK.value() + 'null topic in url' | 'passthrough-operational' | '&topic=null' || 'null' | 'getResourceDataOperationalForCmHandle' | 1 | HttpStatus.OK.value() + 'empty topic in url' | 'passthrough-operational' | '&topic=\"\"' || null | 'getResourceDataOperationalForCmHandle' | 0 | HttpStatus.BAD_REQUEST.value() + 'missing topic in url' | 'passthrough-operational' | '&topic=' || null | 'getResourceDataOperationalForCmHandle' | 0 | HttpStatus.BAD_REQUEST.value() + 'blank topic value in url' | 'passthrough-operational' | '&topic=\" \"' || null | 'getResourceDataOperationalForCmHandle' | 0 | HttpStatus.BAD_REQUEST.value() + 'invalid non-empty topic value in url' | 'passthrough-operational' | '&topic=1_5_*_#' || null | 'getResourceDataOperationalForCmHandle' | 0 | HttpStatus.BAD_REQUEST.value() + 'url with valid topic' | 'passthrough-running' | '&topic=my-topic-name' || 'my-topic-name' | 'getResourceDataPassThroughRunningForCmHandle' | 1 | HttpStatus.OK.value() + 'no topic in url' | 'passthrough-running' | '' || NO_TOPIC | 'getResourceDataPassThroughRunningForCmHandle' | 1 | HttpStatus.OK.value() + 'null topic in url' | 'passthrough-running' | '&topic=null' || 'null' | 'getResourceDataPassThroughRunningForCmHandle' | 1 | HttpStatus.OK.value() + 'empty topic in url' | 'passthrough-running' | '&topic=\"\"' || null | 'getResourceDataPassThroughRunningForCmHandle' | 0 | HttpStatus.BAD_REQUEST.value() + 'missing topic in url' | 'passthrough-running' | '&topic=' || null | 'getResourceDataPassThroughRunningForCmHandle' | 0 | HttpStatus.BAD_REQUEST.value() + 'blank topic value in url' | 'passthrough-running' | '&topic=\" \"' || null | 'getResourceDataPassThroughRunningForCmHandle' | 0 | HttpStatus.BAD_REQUEST.value() + 'invalid non-empty topic value in url' | 'passthrough-running' | '&topic=1_5_*_#' || null | 'getResourceDataPassThroughRunningForCmHandle' | 0 | HttpStatus.BAD_REQUEST.value() } def 'Get Resource Data from pass-through running with #scenario value in resource identifier param.'() { @@ -124,7 +137,8 @@ class NetworkCmProxyControllerSpec extends Specification { mockNetworkCmProxyDataService.getResourceDataPassThroughRunningForCmHandle('testCmHandle', resourceIdentifier, '(a=1,b=2)', - NO_TOPIC) >> '{valid-json}' + NO_TOPIC, + NO_REQUEST_ID) >> '{valid-json}' when: 'get data resource request is performed' def response = mvc.perform( get(getUrl) @@ -271,5 +285,24 @@ class NetworkCmProxyControllerSpec extends Specification { and: 'the response is No Content' response.status == HttpStatus.NO_CONTENT.value() } + + def 'Get resource data from DMI with valid topic i.e. async request for #scenario'() { + given: 'resource data url' + def getUrl = "$ncmpBasePathV1/ch/testCmHandle/data/ds/ncmp-datastore:${datastoreInUrl}" + + "?resourceIdentifier=parent/child&options=(a=1,b=2)&topic=my-topic-name" + when: 'get data resource request is performed' + def response = mvc.perform( + get(getUrl) + .contentType(MediaType.APPLICATION_JSON) + .accept(MediaType.APPLICATION_JSON_VALUE) + ).andReturn().response + then: 'async request id is generated' + assert response.contentAsString.contains("requestId") + where: 'the following parameters are used' + scenario | datastoreInUrl + ':passthrough-operational' | 'passthrough-operational' + ':passthrough-running' | 'passthrough-running' + } + } -- cgit 1.2.3-korg