Replacing ModelMapper with MapStruct

- Removed Model Mapper from pom files
- Replaced ModelMapper with MapStruct
- Added Tests for MapStruct
- Changed mapstruct annotations for individual variables to be null safe rather than all variables
- Excluded generated code from code coverage
- Set ModuleReferences input to required for SchemaSet so that ModuleReferences list set to empty list rather than null

Issue-ID: CPS-127
Signed-off-by: lukegleeson <luke.gleeson@est.tech>
Change-Id: I43f874aea79f58dda5526c6fdead27d8474d90af
diff --git a/cps-rest/docs/openapi/components.yml b/cps-rest/docs/openapi/components.yml
index ae0326d..269e724 100644
--- a/cps-rest/docs/openapi/components.yml
+++ b/cps-rest/docs/openapi/components.yml
@@ -73,6 +73,8 @@
     SchemaSetDetails:
       type: object
       title: Schema set details by dataspace and schemasetName
+      required:
+        - "moduleReferences"
       properties:
         dataspaceName:
           type: string
diff --git a/cps-rest/pom.xml b/cps-rest/pom.xml
index 20870c3..5a21957 100755
--- a/cps-rest/pom.xml
+++ b/cps-rest/pom.xml
@@ -2,6 +2,7 @@
 <!--
   ============LICENSE_START=======================================================
   Copyright (c) 2020 Linux Foundation.
+  Modifications Copyright (C) 2020-2022 Nordix Foundation.
   Modifications Copyright (C) 2021 Bell Canada.
   ================================================================================
   Licensed under the Apache License, Version 2.0 (the "License");
@@ -78,8 +79,12 @@
             <artifactId>commons-lang3</artifactId>
         </dependency>
         <dependency>
-            <groupId>org.modelmapper</groupId>
-            <artifactId>modelmapper</artifactId>
+            <groupId>org.mapstruct</groupId>
+            <artifactId>mapstruct</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.mapstruct</groupId>
+            <artifactId>mapstruct-processor</artifactId>
         </dependency>
         <!-- T E S T   D E P E N D E N C I E S -->
         <dependency>
diff --git a/cps-rest/src/main/java/org/onap/cps/config/CpsConfig.java b/cps-rest/src/main/java/org/onap/cps/config/CpsConfig.java
deleted file mode 100755
index 4f4501a..0000000
--- a/cps-rest/src/main/java/org/onap/cps/config/CpsConfig.java
+++ /dev/null
@@ -1,41 +0,0 @@
-/*

- * ============LICENSE_START=======================================================

- * Copyright (C) 2020 Nordix Foundation.

- * Modifications Copyright (C) 2021 Pantheon.tech

- * Modifications Copyright (C) 2021 Bell Canada.

- * ================================================================================

- * 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.config;

-

-import org.modelmapper.ModelMapper;

-import org.springframework.context.annotation.Bean;

-import org.springframework.context.annotation.Configuration;

-import org.springframework.retry.annotation.EnableRetry;

-

-@Configuration

-@EnableRetry

-public class CpsConfig {

-

-    /**

-     * ModelMapper configuration.

-     */

-    @Bean

-    public ModelMapper modelMapper() {

-        return new ModelMapper();

-    }

-}

diff --git a/cps-rest/src/main/java/org/onap/cps/rest/controller/AdminRestController.java b/cps-rest/src/main/java/org/onap/cps/rest/controller/AdminRestController.java
index 52e64a9..2707d9f 100755
--- a/cps-rest/src/main/java/org/onap/cps/rest/controller/AdminRestController.java
+++ b/cps-rest/src/main/java/org/onap/cps/rest/controller/AdminRestController.java
@@ -1,6 +1,6 @@
 /*
  *  ============LICENSE_START=======================================================
- *  Copyright (C) 2020 Nordix Foundation
+ *  Copyright (C) 2020-2022 Nordix Foundation
  *  Modifications Copyright (C) 2020-2021 Bell Canada.
  *  Modifications Copyright (C) 2021 Pantheon.tech
  *  ================================================================================
@@ -30,7 +30,7 @@
 import java.util.stream.Collectors;
 import javax.validation.Valid;
 import javax.validation.constraints.NotNull;
-import org.modelmapper.ModelMapper;
+import lombok.RequiredArgsConstructor;
 import org.onap.cps.api.CpsAdminService;
 import org.onap.cps.api.CpsModuleService;
 import org.onap.cps.rest.api.CpsAdminApi;
@@ -38,7 +38,6 @@
 import org.onap.cps.rest.model.SchemaSetDetails;
 import org.onap.cps.spi.model.Anchor;
 import org.onap.cps.spi.model.SchemaSet;
-import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.http.HttpStatus;
 import org.springframework.http.ResponseEntity;
 import org.springframework.web.bind.annotation.RequestMapping;
@@ -47,16 +46,12 @@
 
 @RestController
 @RequestMapping("${rest.api.cps-base-path}")
+@RequiredArgsConstructor
 public class AdminRestController implements CpsAdminApi {
 
-    @Autowired
-    private CpsAdminService cpsAdminService;
-
-    @Autowired
-    private CpsModuleService cpsModuleService;
-
-    @Autowired
-    private ModelMapper modelMapper;
+    private final CpsAdminService cpsAdminService;
+    private final CpsModuleService cpsModuleService;
+    private final CpsRestInputMapper cpsRestInputMapper;
 
     /**
      * Create a dataspace.
@@ -107,7 +102,7 @@
     @Override
     public ResponseEntity<SchemaSetDetails> getSchemaSet(final String dataspaceName, final String schemaSetName) {
         final var schemaSet = cpsModuleService.getSchemaSet(dataspaceName, schemaSetName);
-        final var schemaSetDetails = modelMapper.map(schemaSet, SchemaSetDetails.class);
+        final var schemaSetDetails = cpsRestInputMapper.toSchemaSetDetails(schemaSet);
         return new ResponseEntity<>(schemaSetDetails, HttpStatus.OK);
     }
 
@@ -162,7 +157,7 @@
     @Override
     public ResponseEntity<AnchorDetails> getAnchor(final String dataspaceName, final String anchorName) {
         final var anchor = cpsAdminService.getAnchor(dataspaceName, anchorName);
-        final var anchorDetails = modelMapper.map(anchor, AnchorDetails.class);
+        final var anchorDetails = cpsRestInputMapper.toAnchorDetails(anchor);
         return new ResponseEntity<>(anchorDetails, HttpStatus.OK);
     }
 
@@ -175,8 +170,8 @@
     @Override
     public ResponseEntity<List<AnchorDetails>> getAnchors(final String dataspaceName) {
         final Collection<Anchor> anchors = cpsAdminService.getAnchors(dataspaceName);
-        final List<AnchorDetails> anchorDetails = anchors.stream().map(anchor ->
-            modelMapper.map(anchor, AnchorDetails.class)).collect(Collectors.toList());
+        final List<AnchorDetails> anchorDetails = anchors.stream().map(cpsRestInputMapper::toAnchorDetails)
+            .collect(Collectors.toList());
         return new ResponseEntity<>(anchorDetails, HttpStatus.OK);
     }
 }
diff --git a/cps-rest/src/main/java/org/onap/cps/rest/controller/CpsRestInputMapper.java b/cps-rest/src/main/java/org/onap/cps/rest/controller/CpsRestInputMapper.java
new file mode 100644
index 0000000..d0a4a10
--- /dev/null
+++ b/cps-rest/src/main/java/org/onap/cps/rest/controller/CpsRestInputMapper.java
@@ -0,0 +1,42 @@
+/*
+ *  ============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.rest.controller;
+
+import org.mapstruct.Mapper;
+import org.mapstruct.Mapping;
+import org.mapstruct.NullValueCheckStrategy;
+import org.mapstruct.NullValuePropertyMappingStrategy;
+import org.onap.cps.rest.model.AnchorDetails;
+import org.onap.cps.rest.model.SchemaSetDetails;
+import org.onap.cps.spi.model.Anchor;
+import org.onap.cps.spi.model.SchemaSet;
+
+@Mapper(componentModel = "spring")
+public interface CpsRestInputMapper {
+
+    @Mapping(source = "moduleReferences", target = "moduleReferences",
+        nullValueCheckStrategy = NullValueCheckStrategy.ALWAYS,
+        nullValuePropertyMappingStrategy = NullValuePropertyMappingStrategy.SET_TO_DEFAULT)
+    SchemaSetDetails toSchemaSetDetails(final SchemaSet schemaSet);
+
+    AnchorDetails toAnchorDetails(final Anchor anchor);
+
+}
diff --git a/cps-rest/src/test/groovy/org/onap/cps/config/CpsConfigSpec.groovy b/cps-rest/src/test/groovy/org/onap/cps/config/CpsConfigSpec.groovy
deleted file mode 100644
index fc96f04..0000000
--- a/cps-rest/src/test/groovy/org/onap/cps/config/CpsConfigSpec.groovy
+++ /dev/null
@@ -1,34 +0,0 @@
-/*
- *  ============LICENSE_START=======================================================
- *  Copyright (C) 2021 Nordix Foundation
- *  Modifications Copyright (C) 2021 Bell Canada.
- *  ================================================================================
- *  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.config
-
-import org.modelmapper.ModelMapper
-import spock.lang.Specification
-
-class CpsConfigSpec extends Specification {
-    def objectUnderTest = new CpsConfig()
-
-    def 'CPS configuration has a Model Mapper'() {
-        expect: 'the CPS configuration has a Model Mapper'
-            objectUnderTest.modelMapper() instanceof ModelMapper
-    }
-}
diff --git a/cps-rest/src/test/groovy/org/onap/cps/rest/controller/AdminRestControllerSpec.groovy b/cps-rest/src/test/groovy/org/onap/cps/rest/controller/AdminRestControllerSpec.groovy
index e8cfcfb..58a5ebf 100755
--- a/cps-rest/src/test/groovy/org/onap/cps/rest/controller/AdminRestControllerSpec.groovy
+++ b/cps-rest/src/test/groovy/org/onap/cps/rest/controller/AdminRestControllerSpec.groovy
@@ -2,7 +2,7 @@
  *  ============LICENSE_START=======================================================
  *  Copyright (C) 2020-2021 Pantheon.tech
  *  Modifications Copyright (C) 2020-2021 Bell Canada.
- *  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,17 +22,16 @@
 
 package org.onap.cps.rest.controller
 
+import org.mapstruct.factory.Mappers
+
 import static org.onap.cps.spi.CascadeDeleteAllowed.CASCADE_DELETE_PROHIBITED
 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.multipart
 import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.post
 
-import org.modelmapper.ModelMapper
 import org.onap.cps.api.CpsAdminService
-import org.onap.cps.api.CpsDataService
 import org.onap.cps.api.CpsModuleService
-import org.onap.cps.api.CpsQueryService
 import org.onap.cps.spi.exceptions.AlreadyDefinedException
 import org.onap.cps.spi.exceptions.SchemaSetInUseException
 import org.onap.cps.spi.model.Anchor
@@ -59,7 +58,7 @@
     CpsAdminService mockCpsAdminService = Mock()
 
     @SpringBean
-    ModelMapper modelMapper = Spy()
+    CpsRestInputMapper cpsRestInputMapper = Mappers.getMapper(CpsRestInputMapper)
 
     @Autowired
     MockMvc mvc
@@ -68,10 +67,9 @@
     def basePath
 
     def dataspaceName = 'my_dataspace'
-    def anchor = new Anchor(name: 'my_anchor')
-    def anchorList = [anchor]
     def anchorName = 'my_anchor'
     def schemaSetName = 'my_schema_set'
+    def anchor = new Anchor(name: anchorName, dataspaceName: dataspaceName, schemaSetName: schemaSetName)
 
     def 'Create new dataspace.'() {
         given: 'an endpoint'
@@ -274,7 +272,7 @@
 
     def 'Get existing anchor.'() {
         given: 'service method returns a list of anchors'
-            mockCpsAdminService.getAnchors(dataspaceName) >> anchorList
+            mockCpsAdminService.getAnchors(dataspaceName) >> [anchor]
         and: 'an endpoint'
             def anchorEndpoint = "$basePath/v1/dataspaces/$dataspaceName/anchors"
         when: 'get all anchors API is invoked'
diff --git a/cps-rest/src/test/groovy/org/onap/cps/rest/controller/CpsRestInputMapperSpec.groovy b/cps-rest/src/test/groovy/org/onap/cps/rest/controller/CpsRestInputMapperSpec.groovy
new file mode 100644
index 0000000..9ff1a9f
--- /dev/null
+++ b/cps-rest/src/test/groovy/org/onap/cps/rest/controller/CpsRestInputMapperSpec.groovy
@@ -0,0 +1,69 @@
+/*
+ *  ============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.rest.controller
+
+import org.mapstruct.factory.Mappers
+import org.onap.cps.rest.model.AnchorDetails
+import org.onap.cps.rest.model.SchemaSetDetails
+import org.onap.cps.spi.model.Anchor
+import org.onap.cps.rest.model.ModuleReferences
+import org.onap.cps.spi.model.ModuleReference
+import org.onap.cps.spi.model.SchemaSet
+import spock.lang.Specification
+
+class CpsRestInputMapperSpec extends Specification {
+
+    def objectUnderTest = Mappers.getMapper(CpsRestInputMapper.class)
+
+    def 'Convert a SchemaSet to a SchemaSetDetails a ModuleReference'() {
+        given: 'a ModuleReference'
+            def moduleReference = new ModuleReference()
+        and: 'a SchemaSet containing the ModuleReference'
+            def schemaSet = new SchemaSet(name: 'some-schema-set', dataspaceName: 'some-dataspace',
+                moduleReferences: [moduleReference])
+        when: 'to schemaSetDetails is called'
+            def result = objectUnderTest.toSchemaSetDetails(schemaSet)
+        then: 'the result returns a SchemaSetDetails'
+            result.class == SchemaSetDetails.class
+        and: 'the results ModuleReferences are of type ModuleReference'
+            result.moduleReferences[0].class == ModuleReferences.class
+    }
+
+    def 'Convert a schemaSet to a SchemaSetDetails without an ModuleReference'() {
+        given: 'a SchemaSet'
+            def schemaSet = new SchemaSet()
+        when: 'to schemaSetDetails is called'
+            def result = objectUnderTest.toSchemaSetDetails(schemaSet)
+        then: 'the result returns a SchemaSetDetails'
+            result.class == SchemaSetDetails.class
+        and: 'the ModuleReferences of SchemaSetDetails is an empty collection'
+            result.moduleReferences.size() == 0
+    }
+
+    def 'Convert an Anchor to an AnchorDetails'() {
+        given: 'an Anchor'
+            def anchor = new Anchor()
+        when: 'to anchorDetails is called'
+            def result = objectUnderTest.toAnchorDetails(anchor)
+        then: 'the result returns an AnchorDetails'
+            result.class == AnchorDetails.class
+    }
+}
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 a2eaa52..2aa4ddd 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
@@ -22,12 +22,14 @@
 
 package org.onap.cps.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.api.CpsAdminService
 import org.onap.cps.api.CpsDataService
 import org.onap.cps.api.CpsModuleService
 import org.onap.cps.api.CpsQueryService
+import org.onap.cps.rest.controller.CpsRestInputMapper
 import org.onap.cps.spi.exceptions.AlreadyDefinedException
 import org.onap.cps.spi.exceptions.CpsException
 import org.onap.cps.spi.exceptions.CpsPathException
@@ -71,10 +73,10 @@
     CpsQueryService mockCpsQueryService = Stub()
 
     @SpringBean
-    ModelMapper modelMapper = Stub()
+    JsonObjectMapper jsonObjectMapper = new JsonObjectMapper(new ObjectMapper())
 
     @SpringBean
-    JsonObjectMapper jsonObjectMapper = Stub()
+    CpsRestInputMapper cpsRestInputMapper = Stub()
 
     @Autowired
     MockMvc mvc