Enhanced response with Complex State in API 35/129335/6
authormpriyank <priyank.maheshwari@est.tech>
Fri, 20 May 2022 14:25:15 +0000 (15:25 +0100)
committermpriyank <priyank.maheshwari@est.tech>
Tue, 24 May 2022 13:40:40 +0000 (14:40 +0100)
- Introduced RestOutputCmHandleState in API specs of
  retrieveCmHandleDetailsById
- Mapper to map CompositeState to RestOutputCmHandleState
- Enhanced existing test cases and introduced new one to test the
  mapping result

Issue-ID: CPS-1047
Change-Id: I34fa198287e5d920bc0cea312ee4e368f3be2b90
Signed-off-by: mpriyank <priyank.maheshwari@est.tech>
cps-ncmp-rest/docs/openapi/components.yaml
cps-ncmp-rest/src/main/java/org/onap/cps/ncmp/rest/controller/NetworkCmProxyController.java
cps-ncmp-rest/src/main/java/org/onap/cps/ncmp/rest/mapper/RestOutputCmHandleStateMapper.java [new file with mode: 0644]
cps-ncmp-rest/src/test/groovy/org/onap/cps/ncmp/rest/controller/NetworkCmProxyControllerSpec.groovy
cps-ncmp-rest/src/test/groovy/org/onap/cps/ncmp/rest/exceptions/NetworkCmProxyRestExceptionHandlerSpec.groovy
cps-ncmp-rest/src/test/groovy/org/onap/cps/ncmp/rest/mapper/RestOutputCmHandleStateMapperTest.groovy [new file with mode: 0644]
cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/yangmodels/YangModelCmHandle.java
cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/models/NcmpServiceCmHandle.java
cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/api/inventory/CompositeStateSpec.groovy
cps-ncmp-service/src/test/resources/expectedStateModel.json

index 32d25e3..5fe47e4 100644 (file)
@@ -209,6 +209,8 @@ components:
           example: my-cm-handle1
         publicCmHandleProperties:
           $ref: '#/components/schemas/CmHandlePublicProperties'
+        state:
+          $ref: '#/components/schemas/RestOutputCmHandleState'
     CmHandlePublicProperties:
       type: array
       items:
@@ -216,6 +218,50 @@ components:
         additionalProperties:
           type: string
           example: Book Type
+    RestOutputCmHandleState:
+      type: object
+      properties:
+        cmHandleState:
+          type: string
+          example: ADVISED
+        lockReason:
+          $ref: '#/components/schemas/lock-reason'
+        lastUpdateTime:
+          type: string
+          example: 2022-12-31T20:30:40.000+0000
+        dataSyncEnabled:
+          type: boolean
+          example: false
+        dataSyncState:
+          $ref: '#/components/schemas/dataStores'
+
+    lock-reason:
+      type: object
+      properties:
+        reason:
+          type: string
+          example: LOCKED_OTHER
+        details:
+          type: string
+          example: locked due to module sync
+
+    dataStores:
+      type: object
+      properties:
+        operational:
+          $ref: '#/components/schemas/sync-state'
+        running:
+          $ref: '#/components/schemas/sync-state'
+
+    sync-state:
+      type: object
+      properties:
+        state:
+          type: string
+          example: NONE_REQUESTED
+        lastSyncTime:
+          type: string
+          example: 2022-12-31T20:30:40.000+0000
 
     RestOutputCmHandlePublicProperties:
       type: object
index ca7e258..cedc946 100755 (executable)
@@ -46,6 +46,7 @@ import org.onap.cps.ncmp.api.impl.exception.InvalidTopicException;
 import org.onap.cps.ncmp.api.models.CmHandleQueryApiParameters;
 import org.onap.cps.ncmp.api.models.NcmpServiceCmHandle;
 import org.onap.cps.ncmp.rest.api.NetworkCmProxyApi;
+import org.onap.cps.ncmp.rest.mapper.RestOutputCmHandleStateMapper;
 import org.onap.cps.ncmp.rest.model.CmHandleProperties;
 import org.onap.cps.ncmp.rest.model.CmHandleProperty;
 import org.onap.cps.ncmp.rest.model.CmHandlePublicProperties;
@@ -79,6 +80,7 @@ public class NetworkCmProxyController implements NetworkCmProxyApi {
     private final NetworkCmProxyDataService networkCmProxyDataService;
     private final JsonObjectMapper jsonObjectMapper;
     private final NcmpRestInputMapper ncmpRestInputMapper;
+    private final RestOutputCmHandleStateMapper restOutputCmHandleStateMapper;
 
     /**
      * Get resource data from operational datastore.
@@ -312,6 +314,8 @@ public class NetworkCmProxyController implements NetworkCmProxyApi {
         restOutputCmHandle.setCmHandle(ncmpServiceCmHandle.getCmHandleId());
         cmHandlePublicProperties.add(ncmpServiceCmHandle.getPublicProperties());
         restOutputCmHandle.setPublicCmHandleProperties(cmHandlePublicProperties);
+        restOutputCmHandle.setState(restOutputCmHandleStateMapper.toRestOutputCmHandleState(
+                ncmpServiceCmHandle.getCompositeState()));
         return restOutputCmHandle;
     }
 
diff --git a/cps-ncmp-rest/src/main/java/org/onap/cps/ncmp/rest/mapper/RestOutputCmHandleStateMapper.java b/cps-ncmp-rest/src/main/java/org/onap/cps/ncmp/rest/mapper/RestOutputCmHandleStateMapper.java
new file mode 100644 (file)
index 0000000..89015cc
--- /dev/null
@@ -0,0 +1,79 @@
+/*
+ * ============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.ncmp.rest.mapper;
+
+import org.mapstruct.Mapper;
+import org.mapstruct.Mapping;
+import org.mapstruct.Named;
+import org.onap.cps.ncmp.api.inventory.CmHandleState;
+import org.onap.cps.ncmp.api.inventory.CompositeState;
+import org.onap.cps.ncmp.rest.model.DataStores;
+import org.onap.cps.ncmp.rest.model.RestOutputCmHandleState;
+import org.onap.cps.ncmp.rest.model.SyncState;
+
+@Mapper(componentModel = "spring")
+public interface RestOutputCmHandleStateMapper {
+
+    @Mapping(target = "dataSyncState", source = "dataStores", qualifiedByName = "dataStoreToDataSyncState")
+    @Mapping(target = "cmHandleState", source = "cmhandleState", qualifiedByName = "cmHandleStateEnumToString")
+    RestOutputCmHandleState toRestOutputCmHandleState(CompositeState compositeState);
+
+    /**
+     * Convert from CompositeState datastore to RestOutput Datastores.
+     *
+     * @param compositeStateDataStore Composite State data stores
+     * @return DataStores
+     */
+    @Named("dataStoreToDataSyncState")
+    static DataStores toDataStores(CompositeState.DataStores compositeStateDataStore) {
+
+        final DataStores dataStores = new DataStores();
+
+        if (compositeStateDataStore.getRunningDataStore() != null) {
+            final SyncState runningSyncState = new SyncState();
+            runningSyncState.setState(compositeStateDataStore.getRunningDataStore().getSyncState());
+            runningSyncState.setLastSyncTime(compositeStateDataStore.getRunningDataStore().getLastSyncTime());
+            dataStores.setRunning(runningSyncState);
+        }
+
+        if (compositeStateDataStore.getOperationalDataStore() != null) {
+            final SyncState operationalSyncState = new SyncState();
+            operationalSyncState.setState(compositeStateDataStore.getOperationalDataStore().getSyncState());
+            operationalSyncState.setLastSyncTime(compositeStateDataStore.getOperationalDataStore().getLastSyncTime());
+            dataStores.setOperational(operationalSyncState);
+        }
+
+
+        return dataStores;
+
+    }
+
+    /**
+     * Converts cmHandleState enum value to equivalent string.
+     * @param cmHandleState cm handle state enum
+     * @return cm handle state as string
+     */
+    @Named("cmHandleStateEnumToString")
+    static String toCmHandleState(final CmHandleState cmHandleState) {
+        return cmHandleState.name();
+    }
+
+}
index ba49321..6cf1506 100644 (file)
 package org.onap.cps.ncmp.rest.controller
 
 import org.mapstruct.factory.Mappers
+import org.onap.cps.ncmp.api.inventory.CmHandleState
+import org.onap.cps.ncmp.api.inventory.CompositeState
 import org.onap.cps.ncmp.api.models.NcmpServiceCmHandle
+import org.onap.cps.ncmp.rest.mapper.RestOutputCmHandleStateMapper
 import spock.lang.Shared
 
+import java.time.OffsetDateTime
+import java.time.ZoneOffset
+import java.time.format.DateTimeFormatter
+
 import static org.onap.cps.ncmp.api.impl.operations.DmiRequestBody.OperationEnum.PATCH
+import static org.onap.cps.ncmp.api.inventory.CompositeState.DataStores
+import static org.onap.cps.ncmp.api.inventory.CompositeState.LockReason
+import static org.onap.cps.ncmp.api.inventory.CompositeState.Operational
+import static org.onap.cps.ncmp.api.inventory.CompositeState.Running
 import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.delete
 import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get
 import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.patch
@@ -69,6 +80,12 @@ class NetworkCmProxyControllerSpec extends Specification {
     @SpringBean
     NcmpRestInputMapper ncmpRestInputMapper = Mappers.getMapper(NcmpRestInputMapper)
 
+    @SpringBean
+    RestOutputCmHandleStateMapper restOutputCmHandleStateMapper = Mappers.getMapper(RestOutputCmHandleStateMapper)
+
+    def formattedDateAndTime = DateTimeFormatter.ofPattern("yyyy-MM-dd'T'HH:mm:ss.SSSZ")
+        .format(OffsetDateTime.of(2022, 12, 31, 20, 30, 40, 1, ZoneOffset.UTC))
+
     @Value('${rest.api.ncmp-base-path}/v1')
     def ncmpBasePathV1
 
@@ -225,24 +242,32 @@ class NetworkCmProxyControllerSpec extends Specification {
             response.contentAsString == '{"cmHandles":[{"cmHandleId":"some-cmhandle-id1"},{"cmHandleId":"some-cmhandle-id2"}]}'
     }
 
-    def 'Get Cm Handle details by Cm Handle id.' () {
+    def 'Get Cm Handle details by Cm Handle id.'() {
         given: 'an endpoint and a cm handle'
             def cmHandleDetailsEndpoint = "$ncmpBasePathV1/ch/some-cm-handle"
         and: 'an existing ncmp service cm handle'
             def cmHandleId = 'some-cm-handle'
             def dmiProperties = [ prop:'some DMI property' ]
             def publicProperties = [ "public prop":'some public property' ]
-            def ncmpServiceCmHandle = new NcmpServiceCmHandle(cmHandleId: cmHandleId, dmiProperties: dmiProperties, publicProperties: publicProperties)
+            def compositeState = new CompositeState(cmhandleState: CmHandleState.ADVISED,
+                lockReason: LockReason.builder().reason('LOCKED_OTHER').details("lock-misbehaving-details").build(),
+                lastUpdateTime: formattedDateAndTime.toString(),
+                dataSyncEnabled: false,
+                dataStores: dataStores())
+            def ncmpServiceCmHandle = new NcmpServiceCmHandle(cmHandleId: cmHandleId, dmiProperties: dmiProperties, publicProperties: publicProperties, compositeState: compositeState)
         and: 'the service method is invoked with the cm handle id'
             1 * mockNetworkCmProxyDataService.getNcmpServiceCmHandle('some-cm-handle') >> ncmpServiceCmHandle
         when: 'the cm handle details api is invoked'
             def response = mvc.perform(get(cmHandleDetailsEndpoint)).andReturn().response
         then: 'the correct response is returned'
             response.status == HttpStatus.OK.value()
-        and: 'the response returns public properties and the correct properties'
+        and: 'the response returns public properties and the correct cm handle states'
             response.contentAsString.contains('publicCmHandleProperties')
-            response.contentAsString.contains('public prop')
-            response.contentAsString.contains('some public property')
+            response.contentAsString.contains('LOCKED_OTHER')
+            response.contentAsString.contains('lock-misbehaving-details')
+            response.contentAsString.contains('ADVISED')
+            response.contentAsString.contains('NONE_REQUESTED')
+            response.contentAsString.contains('2022-12-31T20:30:40.000+0000')
         and: 'the content does not contain dmi properties'
             !response.contentAsString.contains("some DMI property")
     }
@@ -348,5 +373,16 @@ class NetworkCmProxyControllerSpec extends Specification {
             ':passthrough-running'     | 'passthrough-running'
     }
 
+    def dataStores() {
+        DataStores.builder()
+            .operationalDataStore(Operational.builder()
+                .syncState('NONE_REQUESTED')
+                .lastSyncTime(formattedDateAndTime.toString()).build())
+            .runningDataStore(Running.builder()
+                .syncState('NONE_REQUESTED')
+                .lastSyncTime(formattedDateAndTime.toString()).build())
+            .build()
+    }
+
 }
 
index 751fdcd..1258e6e 100644 (file)
@@ -29,6 +29,7 @@ import org.onap.cps.ncmp.api.impl.exception.DmiRequestException
 import org.onap.cps.ncmp.api.impl.exception.HttpClientRequestException
 import org.onap.cps.ncmp.api.impl.exception.ServerNcmpException
 import org.onap.cps.ncmp.rest.controller.NcmpRestInputMapper
+import org.onap.cps.ncmp.rest.mapper.RestOutputCmHandleStateMapper
 import org.onap.cps.spi.exceptions.CpsException
 import org.onap.cps.spi.exceptions.DataNodeNotFoundException
 import org.onap.cps.spi.exceptions.DataValidationException
@@ -66,6 +67,9 @@ class NetworkCmProxyRestExceptionHandlerSpec extends Specification {
     @SpringBean
     NcmpRestInputMapper ncmpRestInputMapper = Mappers.getMapper(NcmpRestInputMapper)
 
+    @SpringBean
+    RestOutputCmHandleStateMapper restOutputCmHandleStateMapper = Mappers.getMapper(RestOutputCmHandleStateMapper)
+
     @Value('${rest.api.ncmp-base-path}')
     def basePathNcmp
 
diff --git a/cps-ncmp-rest/src/test/groovy/org/onap/cps/ncmp/rest/mapper/RestOutputCmHandleStateMapperTest.groovy b/cps-ncmp-rest/src/test/groovy/org/onap/cps/ncmp/rest/mapper/RestOutputCmHandleStateMapperTest.groovy
new file mode 100644 (file)
index 0000000..4560ae4
--- /dev/null
@@ -0,0 +1,75 @@
+/*
+ * ============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.ncmp.rest.mapper
+
+import org.mapstruct.factory.Mappers
+import org.onap.cps.ncmp.api.inventory.CmHandleState
+import org.onap.cps.ncmp.api.inventory.CompositeState
+import org.onap.cps.ncmp.rest.model.RestOutputCmHandleState
+import spock.lang.Specification
+
+import java.time.OffsetDateTime
+import java.time.ZoneOffset
+import java.time.format.DateTimeFormatter
+
+import static org.onap.cps.ncmp.api.inventory.CompositeState.DataStores
+import static org.onap.cps.ncmp.api.inventory.CompositeState.LockReason
+import static org.onap.cps.ncmp.api.inventory.CompositeState.Operational
+
+class RestOutputCmHandleStateMapperTest extends Specification {
+
+    def formattedDateAndTime = DateTimeFormatter.ofPattern("yyyy-MM-dd'T'HH:mm:ss.SSSZ")
+        .format(OffsetDateTime.of(2022, 12, 31, 20, 30, 40, 1, ZoneOffset.UTC))
+    def objectUnderTest = Mappers.getMapper(RestOutputCmHandleStateMapper)
+
+    def 'Composite State to Rest Output CmHandleState'() {
+        given: 'a composite state model'
+            def compositeState = new CompositeState(cmhandleState: CmHandleState.ADVISED,
+                lockReason: LockReason.builder().reason('LOCKED_OTHER').details('locked-other-details').build(),
+                lastUpdateTime: formattedDateAndTime.toString(),
+                dataSyncEnabled: false,
+                dataStores: dataStores())
+        when: 'mapper is called'
+            def result = objectUnderTest.toRestOutputCmHandleState(compositeState)
+        then: 'result is of the correct type'
+            assert result.class == RestOutputCmHandleState.class
+        and: 'mapped result should have correct values'
+            assert !result.dataSyncEnabled
+            assert result.lastUpdateTime == formattedDateAndTime
+            assert result.lockReason.reason == 'LOCKED_OTHER'
+            assert result.lockReason.details == 'locked-other-details'
+            assert result.cmHandleState == CmHandleState.ADVISED.name()
+            assert result.dataSyncState.operational != null
+            assert result.dataSyncState.running != null
+    }
+
+    def dataStores() {
+
+        return DataStores.builder()
+            .operationalDataStore(Operational.builder()
+                .syncState('NONE_REQUESTED')
+                .lastSyncTime(formattedDateAndTime.toString()).build())
+            .runningDataStore(CompositeState.Running.builder()
+                .syncState('NONE_REQUESTED')
+                .lastSyncTime(formattedDateAndTime.toString()).build())
+            .build()
+    }
+}
index d4c64ea..65e03f1 100644 (file)
@@ -90,6 +90,7 @@ public class YangModelCmHandle {
         yangModelCmHandle.setDmiProperties(asYangModelCmHandleProperties(ncmpServiceCmHandle.getDmiProperties()));
         yangModelCmHandle.setPublicProperties(asYangModelCmHandleProperties(
                 ncmpServiceCmHandle.getPublicProperties()));
+        yangModelCmHandle.setCompositeState(ncmpServiceCmHandle.getCompositeState());
         return yangModelCmHandle;
     }
 
index 6811b59..963b484 100644 (file)
@@ -27,6 +27,7 @@ import java.util.Map;
 import lombok.Getter;
 import lombok.NoArgsConstructor;
 import lombok.Setter;
+import org.onap.cps.ncmp.api.inventory.CompositeState;
 import org.springframework.validation.annotation.Validated;
 
 /**
@@ -47,4 +48,7 @@ public class NcmpServiceCmHandle {
     @JsonSetter(nulls = Nulls.AS_EMPTY)
     private Map<String, String> publicProperties = Collections.emptyMap();
 
+    @JsonSetter(nulls = Nulls.AS_EMPTY)
+    private CompositeState compositeState;
+
 }
index 4f4cccf..59c9951 100644 (file)
@@ -36,7 +36,8 @@ import static org.springframework.util.StringUtils.trimAllWhitespace
 
 class CompositeStateSpec extends Specification {
 
-    def formattedDateAndTime = DateTimeFormatter.ofPattern("yyyy-MM-dd'T'HH:mm:ss.SSSZ").format(OffsetDateTime.of(2022, 1, 1, 1, 1, 1, 1, ZoneOffset.MIN))
+    def formattedDateAndTime = DateTimeFormatter.ofPattern("yyyy-MM-dd'T'HH:mm:ss.SSSZ")
+        .format(OffsetDateTime.of(2022, 12, 31, 20, 30, 40, 1, ZoneOffset.UTC))
     def objectMapper = new ObjectMapper()
 
     def "Composite State Specification"() {
index a416194..f68d725 100644 (file)
@@ -4,16 +4,16 @@
     "reason" : "lock-reason",
     "details" : "lock-misbehaving-details"
   },
-  "last-update-time" : "2022-01-01T01:01:01.000-1800",
+  "last-update-time" : "2022-12-31T20:30:40.000+0000",
   "data-sync-enabled" : false,
   "datastores" : {
     "operational" : {
       "sync-state" : "NONE_REQUESTED",
-      "last-sync-time" : "2022-01-01T01:01:01.000-1800"
+      "last-sync-time" : "2022-12-31T20:30:40.000+0000"
     },
     "running" : {
       "sync-state" : "NONE_REQUESTED",
-      "last-sync-time" : "2022-01-01T01:01:01.000-1800"
+      "last-sync-time" : "2022-12-31T20:30:40.000+0000"
     }
   }
 }
\ No newline at end of file