Merge "Replacing ModelMapper with MapStruct"
authorToine Siebelink <toine.siebelink@est.tech>
Mon, 14 Mar 2022 09:47:27 +0000 (09:47 +0000)
committerGerrit Code Review <gerrit@onap.org>
Mon, 14 Mar 2022 09:47:27 +0000 (09:47 +0000)
1  2 
cps-ncmp-rest/docs/openapi/components.yaml
cps-ncmp-rest/docs/openapi/ncmp.yml
cps-ncmp-rest/src/main/java/org/onap/cps/ncmp/rest/controller/NetworkCmProxyController.java
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

@@@ -53,23 -53,23 +53,23 @@@ components
              $ref: '#/components/schemas/RestInputCmHandle'
          updatedCmHandles:
            type: array
 -          example:
 -            cmHandle: my-cm-handle
 -            cmHandleProperties:
 -              add-my-property: add-property
 -              update-my-property: updated-property
 -              delete-my-property: '~'
 -            publicCmHandleProperties:
 -              add-my-property: add-property
 -              update-my-property: updated-property
 -              delete-my-property: '~'
            items:
              $ref: '#/components/schemas/RestInputCmHandle'
 +            example:
 +              cmHandle: my-cm-handle
 +              cmHandleProperties:
 +                add-my-property: add-property
 +                update-my-property: updated-property
 +                delete-my-property: '~'
 +              publicCmHandleProperties:
 +                add-my-property: add-property
 +                update-my-property: updated-property
 +                delete-my-property: '~'
          removedCmHandles:
            type: array
            items:
              type: string
 -            example: [my-cm-handle1, my-cm-handle2, my-cm-handle3]
 +          example: [my-cm-handle1, my-cm-handle2, my-cm-handle3]
  
      RestInputCmHandle:
        required:
            type: string
            example: my-cm-handle-id
  
-     ModuleReference:
+     RestModuleReference:
        type: object
        title: Module reference details
        properties:
          sample 3:
            value:
              options: (depth=2,fields=book/authors)
 +    topicParamInQuery:
 +      name: topic
 +      in: query
 +      description: topic parameter in query.
 +      required: false
 +      schema:
 +        type: string
 +      allowReserved: true
 +      examples:
 +        sample 1:
 +          value:
 +            topic: my-topic-name
      contentParamInHeader:
        name: Content-Type
        in: header
@@@ -29,7 -29,6 +29,7 @@@ getResourceDataForPassthroughOperationa
        - $ref: 'components.yaml#/components/parameters/resourceIdentifierInQuery'
        - $ref: 'components.yaml#/components/parameters/acceptParamInHeader'
        - $ref: 'components.yaml#/components/parameters/optionsParamInQuery'
 +      - $ref: 'components.yaml#/components/parameters/topicParamInQuery'
      responses:
        200:
          description: OK
@@@ -61,7 -60,6 +61,7 @@@ resourceDataForPassthroughRunning
        - $ref: 'components.yaml#/components/parameters/resourceIdentifierInQuery'
        - $ref: 'components.yaml#/components/parameters/acceptParamInHeader'
        - $ref: 'components.yaml#/components/parameters/optionsParamInQuery'
 +      - $ref: 'components.yaml#/components/parameters/topicParamInQuery'
      responses:
        200:
          description: OK
@@@ -226,7 -224,7 +226,7 @@@ fetchModuleReferencesByCmHandle
              schema:
                type: array
                items:
-                 $ref: 'components.yaml#/components/schemas/ModuleReference'
+                 $ref: 'components.yaml#/components/schemas/RestModuleReference'
        400:
          $ref: 'components.yaml#/components/responses/BadRequest'
        401:
@@@ -37,7 -37,6 +37,6 @@@ 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;
  import org.onap.cps.ncmp.api.models.NcmpServiceCmHandle;
  import org.onap.cps.ncmp.rest.api.NetworkCmProxyApi;
@@@ -49,7 -48,7 +48,7 @@@ import org.onap.cps.ncmp.rest.model.Con
  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.ncmp.rest.model.RestModuleReference;
  import org.onap.cps.ncmp.rest.model.RestOutputCmHandle;
  import org.onap.cps.utils.JsonObjectMapper;
  import org.springframework.http.HttpStatus;
@@@ -65,9 -64,9 +64,9 @@@ public class NetworkCmProxyController i
  
      private static final String NO_BODY = null;
  
-     private final ModelMapper modelMapper;
      private final NetworkCmProxyDataService networkCmProxyDataService;
      private final JsonObjectMapper jsonObjectMapper;
+     private final NcmpRestInputMapper ncmpRestInputMapper;
  
      /**
       * Get resource data from operational datastore.
       * @param resourceIdentifier resource identifier
       * @param acceptParamInHeader accept header parameter
       * @param optionsParamInQuery options query parameter
 +     * @param topicParamInQuery topic query parameter
       * @return {@code ResponseEntity} response from dmi plugin
       */
      @Override
      public ResponseEntity<Object> getResourceDataOperationalForCmHandle(final String cmHandle,
                                                                          final @NotNull @Valid String resourceIdentifier,
                                                                          final String acceptParamInHeader,
 -                                                                        final @Valid String optionsParamInQuery) {
 +                                                                        final @Valid String optionsParamInQuery,
 +                                                                        final @Valid String topicParamInQuery) {
          final Object responseObject = networkCmProxyDataService.getResourceDataOperationalForCmHandle(cmHandle,
                  resourceIdentifier,
                  acceptParamInHeader,
 -                optionsParamInQuery);
 +                optionsParamInQuery,
 +                topicParamInQuery);
          return ResponseEntity.ok(responseObject);
      }
  
       * @param resourceIdentifier resource identifier
       * @param acceptParamInHeader accept header parameter
       * @param optionsParamInQuery options query parameter
 +     * @param topicParamInQuery topic query parameter
       * @return {@code ResponseEntity} response from dmi plugin
       */
      @Override
      public ResponseEntity<Object> getResourceDataRunningForCmHandle(final String cmHandle,
                                                                      final @NotNull @Valid String resourceIdentifier,
                                                                      final String acceptParamInHeader,
 -                                                                    final @Valid String optionsParamInQuery) {
 +                                                                    final @Valid String optionsParamInQuery,
 +                                                                    final @Valid String topicParamInQuery) {
          final Object responseObject = networkCmProxyDataService.getResourceDataPassThroughRunningForCmHandle(cmHandle,
                  resourceIdentifier,
                  acceptParamInHeader,
 -                optionsParamInQuery);
 +                optionsParamInQuery,
 +                topicParamInQuery);
          return ResponseEntity.ok(responseObject);
      }
  
       * @param cmHandle the cm handle
       * @return module references for cm handle. Namespace will be always blank because restConf does not include this.
       */
-     public ResponseEntity<List<ModuleReference>> getModuleReferencesByCmHandle(final String cmHandle) {
-         final List<ModuleReference> moduleReferences =
+     public ResponseEntity<List<RestModuleReference>> getModuleReferencesByCmHandle(final String cmHandle) {
+         final List<RestModuleReference> restModuleReferences =
              networkCmProxyDataService.getYangResourcesModuleReferences(cmHandle).stream()
-             .map(moduleReference -> modelMapper.map(moduleReference, ModuleReference.class))
+             .map(ncmpRestInputMapper::toRestModuleReference)
                  .collect(Collectors.toList());
-         return new ResponseEntity<>(moduleReferences, HttpStatus.OK);
+         return new ResponseEntity<>(restModuleReferences, HttpStatus.OK);
      }
  
      private Collection<String> processConditions(final List<ConditionProperties> conditionProperties) {
@@@ -22,9 -22,8 +22,9 @@@
  
  package org.onap.cps.ncmp.rest.controller
  
+ import org.mapstruct.factory.Mappers
  import org.onap.cps.ncmp.api.models.NcmpServiceCmHandle
 +import spock.lang.Shared
  
  import static org.onap.cps.ncmp.api.impl.operations.DmiRequestBody.OperationEnum.PATCH
  import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.delete
@@@ -37,7 -36,6 +37,6 @@@ import static org.onap.cps.ncmp.api.imp
  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
@@@ -61,20 -59,17 +60,20 @@@ class NetworkCmProxyControllerSpec exte
      NetworkCmProxyDataService mockNetworkCmProxyDataService = Mock()
  
      @SpringBean
-     ModelMapper modelMapper = new ModelMapper()
+     JsonObjectMapper jsonObjectMapper = new JsonObjectMapper(new ObjectMapper())
  
      @SpringBean
-     JsonObjectMapper jsonObjectMapper = new JsonObjectMapper(new ObjectMapper())
+     NcmpRestInputMapper ncmpRestInputMapper = Mappers.getMapper(NcmpRestInputMapper)
  
      @Value('${rest.api.ncmp-base-path}/v1')
      def ncmpBasePathV1
  
      def requestBody = '{"some-key":"some-value"}'
  
 -    def 'Get Resource Data from pass-through operational.' () {
 +    @Shared
 +    def NO_TOPIC = null
 +
 +    def 'Get Resource Data from pass-through operational.'() {
          given: 'resource data url'
              def getUrl = "$ncmpBasePathV1/ch/testCmHandle/data/ds/ncmp-datastore:passthrough-operational" +
                      "?resourceIdentifier=parent/child&options=(a=1,b=2)"
              1 * mockNetworkCmProxyDataService.getResourceDataOperationalForCmHandle('testCmHandle',
                      'parent/child',
                      'application/json',
 -                    '(a=1,b=2)')
 +                    '(a=1,b=2)',
 +                    NO_TOPIC)
 +        and: 'response status is Ok'
 +            response.status == HttpStatus.OK.value()
 +    }
 +
 +    def 'Get Resource Data from pass-through operational with #scenario.'() {
 +        given: 'resource data url'
 +            def getUrl = "$ncmpBasePathV1/ch/testCmHandle/data/ds/ncmp-datastore:passthrough-operational" +
 +                    "?resourceIdentifier=parent/child&options=(a=1,b=2)${topicQueryParam}"
 +        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: 'the NCMP data service is called with operational data for cm handle'
 +            1 * mockNetworkCmProxyDataService.getResourceDataOperationalForCmHandle('testCmHandle',
 +                    'parent/child',
 +                    'application/json',
 +                    '(a=1,b=2)',
 +                    expectedTopicName)
          and: 'response status is Ok'
              response.status == HttpStatus.OK.value()
 +        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="              || ""
      }
  
 -    def 'Get Resource Data from pass-through running with #scenario value in resource identifier param.' () {
 +    def 'Get Resource Data from pass-through running with #scenario value in resource identifier param.'() {
          given: 'resource data url'
              def getUrl = "$ncmpBasePathV1/ch/testCmHandle/data/ds/ncmp-datastore:passthrough-running" +
                      "?resourceIdentifier=" + resourceIdentifier + "&options=(a=1,b=2)"
              mockNetworkCmProxyDataService.getResourceDataPassThroughRunningForCmHandle('testCmHandle',
                      resourceIdentifier,
                      'application/json',
 -                    '(a=1,b=2)') >> '{valid-json}'
 +                    '(a=1,b=2)',
 +                    NO_TOPIC) >> '{valid-json}'
          when: 'get data resource request is performed'
              def response = mvc.perform(
                      get(getUrl)
  
  package org.onap.cps.ncmp.rest.exceptions
  
+ import com.fasterxml.jackson.databind.ObjectMapper
  import groovy.json.JsonSlurper
- import org.modelmapper.ModelMapper
+ import org.mapstruct.factory.Mappers
  import org.onap.cps.TestUtils
  import org.onap.cps.ncmp.api.NetworkCmProxyDataService
  import org.onap.cps.ncmp.api.impl.exception.DmiRequestException
  import org.onap.cps.ncmp.api.impl.exception.ServerNcmpException
- import org.onap.cps.ncmp.rest.controller.RestInputMapper
+ import org.onap.cps.ncmp.api.models.NcmpServiceCmHandle
+ import org.onap.cps.ncmp.rest.controller.NcmpRestInputMapper
  import org.onap.cps.spi.exceptions.CpsException
  import org.onap.cps.spi.exceptions.DataNodeNotFoundException
  import org.onap.cps.spi.exceptions.DataValidationException
@@@ -45,7 -47,6 +47,7 @@@ import static org.onap.cps.ncmp.rest.ex
  import static org.onap.cps.ncmp.rest.exceptions.NetworkCmProxyRestExceptionHandlerSpec.ApiType.NCMPINVENTORY
  import static org.springframework.http.HttpStatus.BAD_REQUEST
  import static org.springframework.http.HttpStatus.INTERNAL_SERVER_ERROR
 +import static org.springframework.http.HttpStatus.NOT_FOUND
  import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get
  import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.post
  
@@@ -58,14 -59,11 +60,11 @@@ class NetworkCmProxyRestExceptionHandle
      @SpringBean
      NetworkCmProxyDataService mockNetworkCmProxyDataService = Mock()
  
-     @SpringBean
-     ModelMapper modelMapper = Stub()
      @SpringBean
      JsonObjectMapper jsonObjectMapper = Stub()
  
      @SpringBean
-     RestInputMapper restInputMapper = Mock()
+     NcmpRestInputMapper ncmpRestInputMapper = Mappers.getMapper(NcmpRestInputMapper)
  
      @Value('${rest.api.ncmp-base-path}')
      def basePathNcmp
      def dataNodeBaseEndpointNcmpInventory
  
      @Shared
 -    def errorMessage = 'some error message'
 +    def sampleErrorMessage = 'some error message'
      @Shared
 -    def errorDetails = 'some error details'
 -    @Shared
 -    def dataNodeNotFoundErrorMessage = 'DataNode not found'
 +    def sampleErrorDetails = 'some error details'
  
      def setup() {
          dataNodeBaseEndpointNcmp = "$basePathNcmp/v1"
          dataNodeBaseEndpointNcmpInventory = "$basePathNcmpInventory/v1"
      }
  
 -    def 'Get request with generic #scenario exception returns correct HTTP Status.'() {
 +    def 'Get request with generic #scenario exception returns correct HTTP Status with #scenario'() {
          when: 'an exception is thrown by the service'
              setupTestException(exception, NCMP)
              def response = performTestRequest(NCMP)
          then: 'an HTTP response is returned with correct message and details'
              assertTestResponse(response, expectedErrorCode, expectedErrorMessage, expectedErrorDetails)
          where:
 -            scenario              | exception                                                                 || expectedErrorDetails | expectedErrorMessage          | expectedErrorCode
 -            'CPS'                 | new CpsException(errorMessage, errorDetails)                              || errorDetails         |  errorMessage                 | INTERNAL_SERVER_ERROR
 -            'NCMP-server'         | new ServerNcmpException(errorMessage, errorDetails)                       || null                 |  errorMessage                 | INTERNAL_SERVER_ERROR
 -            'NCMP-client'         | new DmiRequestException(errorMessage, errorDetails)                       || null                 |  errorMessage                 | BAD_REQUEST
 -            'DataNode Validation' | new DataNodeNotFoundException(dataNodeNotFoundErrorMessage, errorDetails) || null                 |  dataNodeNotFoundErrorMessage | BAD_REQUEST
 -            'other'               | new IllegalStateException(errorMessage)                                   || null                 |  errorMessage                 | INTERNAL_SERVER_ERROR
 +            scenario              | exception                                                        || expectedErrorDetails | expectedErrorMessage | expectedErrorCode
 +            'CPS'                 | new CpsException(sampleErrorMessage, sampleErrorDetails)         || sampleErrorDetails   | sampleErrorMessage   | INTERNAL_SERVER_ERROR
 +            'NCMP-server'         | new ServerNcmpException(sampleErrorMessage, sampleErrorDetails)  || null                 | sampleErrorMessage   | INTERNAL_SERVER_ERROR
 +            'NCMP-client'         | new DmiRequestException(sampleErrorMessage, sampleErrorDetails)  || null                 | sampleErrorMessage   | BAD_REQUEST
 +            'DataNode Validation' | new DataNodeNotFoundException('myDataspaceName', 'myAnchorName') || null                 | 'DataNode not found' | NOT_FOUND
 +            'other'               | new IllegalStateException(sampleErrorMessage)                    || null                 | sampleErrorMessage   | INTERNAL_SERVER_ERROR
 +            'Data Node Not Found' | new DataNodeNotFoundException('myDataspaceName', 'myAnchorName') || 'DataNode not found' | 'DataNode not found' | NOT_FOUND
      }
  
      def 'Post request with exception returns correct HTTP Status.'() {
          given: 'the service throws data validation exception'
 -            def exception = new DataValidationException(errorMessage, errorDetails)
 +            def exception = new DataValidationException(sampleErrorMessage, sampleErrorDetails)
              setupTestException(exception, NCMPINVENTORY)
          when: 'the HTTP request is made'
              def response = performTestRequest(NCMPINVENTORY)
          then: 'an HTTP response is returned with correct message and details'
 -            assertTestResponse(response, BAD_REQUEST, errorMessage, errorDetails)
 +            assertTestResponse(response, BAD_REQUEST, sampleErrorMessage, sampleErrorDetails)
      }
  
      def setupTestException(exception, apiType) {
      static void assertTestResponse(response, expectedStatus , expectedErrorMessage , expectedErrorDetails) {
          assert response.status == expectedStatus.value()
          def content = new JsonSlurper().parseText(response.contentAsString)
 -        assert content['status'] == expectedStatus.toString()
 -        assert content['message'] == expectedErrorMessage
 -        assert expectedErrorDetails == null || content['details'] == expectedErrorDetails
 +        assert content['status'].toString().contains(expectedStatus.toString())
 +        assert content['message'].toString().contains(expectedErrorMessage)
 +        assert expectedErrorDetails == null || content['details'].toString().contains(expectedErrorDetails)
      }
  
      enum ApiType {