Introduce GET operation in ProvMnS 44/142244/6
authorseanbeirne <sean.beirne@est.tech>
Tue, 5 Aug 2025 09:53:44 +0000 (10:53 +0100)
committerseanbeirne <sean.beirne@est.tech>
Wed, 15 Oct 2025 10:51:11 +0000 (11:51 +0100)
 - Moved code gen for ProvMns from ncmp-rest to ncmp-service package
 - Created new util class for provmns operations
 - Fixed legacy naming of methods

Issue-ID: CPS-2703
Change-Id: I665a227e10c019d4dbc5d585cd65f5094de0d825
Signed-off-by: seanbeirne <sean.beirne@est.tech>
23 files changed:
cps-ncmp-rest/pom.xml
cps-ncmp-rest/src/main/java/org/onap/cps/ncmp/rest/controller/ProvMnS.java
cps-ncmp-rest/src/main/java/org/onap/cps/ncmp/rest/controller/ProvMnsController.java
cps-ncmp-rest/src/main/java/org/onap/cps/ncmp/rest/util/ProvMnSParametersMapper.java [new file with mode: 0644]
cps-ncmp-rest/src/main/java/org/onap/cps/ncmp/rest/util/ProvMnsRequestParameters.java
cps-ncmp-rest/src/test/groovy/org/onap/cps/ncmp/rest/controller/NetworkCmProxyRestExceptionHandlerSpec.groovy
cps-ncmp-rest/src/test/groovy/org/onap/cps/ncmp/rest/controller/ProvMnsControllerSpec.groovy
cps-ncmp-rest/src/test/groovy/org/onap/cps/ncmp/rest/util/ProvMnSParametersMapperSpec.groovy [new file with mode: 0644]
cps-ncmp-service/.openapi-generator-ignore-provmns [moved from cps-ncmp-rest/.openapi-generator-ignore-provmns with 62% similarity]
cps-ncmp-service/pom.xml
cps-ncmp-service/src/main/java/org/onap/cps/ncmp/impl/data/DmiDataOperations.java
cps-ncmp-service/src/main/java/org/onap/cps/ncmp/impl/datajobs/DmiSubJobRequestHandler.java
cps-ncmp-service/src/main/java/org/onap/cps/ncmp/impl/dmi/DmiRestClient.java
cps-ncmp-service/src/main/java/org/onap/cps/ncmp/impl/inventory/sync/DmiModelOperations.java
cps-ncmp-service/src/main/java/org/onap/cps/ncmp/impl/provmns/model/ClassNameIdGetDataNodeSelectorParameter.java [moved from cps-ncmp-rest/src/main/java/org/onap/cps/ncmp/rest/provmns/model/ClassNameIdGetDataNodeSelectorParameter.java with 96% similarity]
cps-ncmp-service/src/main/java/org/onap/cps/ncmp/impl/provmns/model/Resource.java [moved from cps-ncmp-rest/src/main/java/org/onap/cps/ncmp/rest/provmns/model/Resource.java with 93% similarity]
cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/impl/data/DmiDataOperationsSpec.groovy
cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/impl/datajobs/DmiSubJobRequestHandlerSpec.groovy
cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/impl/dmi/DmiRestClientSpec.groovy
cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/impl/inventory/sync/DmiModelOperationsSpec.groovy
cps-parent/pom.xml
integration-test/src/test/groovy/org/onap/cps/integration/base/DmiDispatcher.groovy
integration-test/src/test/groovy/org/onap/cps/integration/functional/ncmp/provmns/ProvMnSRestApiSpec.groovy

index b5a3032..f3afd1d 100644 (file)
                             </configOptions>
                         </configuration>
                     </execution>
-                    <execution>
-                        <id>ncmp-code-gen-provmns</id>
-                        <goals>
-                            <goal>generate</goal>
-                        </goals>
-                        <configuration>
-                            <inputSpec>https://forge.3gpp.org/rep/all/5G_APIs/-/raw/REL-18/TS28532_ProvMnS.yaml</inputSpec>
-                            <invokerPackage>org.onap.cps.ncmp.rest.provmns.controller</invokerPackage>
-                            <modelPackage>org.onap.cps.ncmp.rest.provmns.model</modelPackage>
-                            <apiPackage>org.onap.cps.ncmp.rest.provmns.api</apiPackage>
-                            <generatorName>spring</generatorName>
-                            <generateSupportingFiles>false</generateSupportingFiles>
-                            <configOptions>
-                                <sourceFolder>src/gen/java</sourceFolder>
-                                <dateLibrary>java11</dateLibrary>
-                                <interfaceOnly>true</interfaceOnly>
-                                <useSpringBoot3>true</useSpringBoot3>
-                                <useTags>true</useTags>
-                                <openApiNullable>false</openApiNullable>
-                                <skipDefaultInterface>true</skipDefaultInterface>
-                            </configOptions>
-                            <importMappings>
-                                <importMapping>Resource=org.onap.cps.ncmp.rest.provmns.model.Resource</importMapping>
-                                <importMapping>ClassNameIdGetDataNodeSelectorParameter=org.onap.cps.ncmp.rest.provmns.model.ClassNameIdGetDataNodeSelectorParameter</importMapping>
-                            </importMappings>
-                            <ignoreFileOverride>${project.basedir}/.openapi-generator-ignore-provmns</ignoreFileOverride>
-                        </configuration>
-                    </execution>
                     <execution>
                         <id>ncmp-inventory-openapi-yaml-gen</id>
                         <goals>
index 77109b5..f553b96 100644 (file)
@@ -30,13 +30,13 @@ import io.swagger.v3.oas.annotations.tags.Tag;
 import jakarta.servlet.http.HttpServletRequest;
 import jakarta.validation.Valid;
 import java.util.List;
+import org.onap.cps.ncmp.impl.provmns.model.ClassNameIdGetDataNodeSelectorParameter;
+import org.onap.cps.ncmp.impl.provmns.model.ClassNameIdPatchDefaultResponse;
+import org.onap.cps.ncmp.impl.provmns.model.ErrorResponseDefault;
+import org.onap.cps.ncmp.impl.provmns.model.ErrorResponseGet;
+import org.onap.cps.ncmp.impl.provmns.model.Resource;
+import org.onap.cps.ncmp.impl.provmns.model.Scope;
 import org.onap.cps.ncmp.rest.model.ErrorMessage;
-import org.onap.cps.ncmp.rest.provmns.model.ClassNameIdGetDataNodeSelectorParameter;
-import org.onap.cps.ncmp.rest.provmns.model.ClassNameIdPatchDefaultResponse;
-import org.onap.cps.ncmp.rest.provmns.model.ErrorResponseDefault;
-import org.onap.cps.ncmp.rest.provmns.model.ErrorResponseGet;
-import org.onap.cps.ncmp.rest.provmns.model.Resource;
-import org.onap.cps.ncmp.rest.provmns.model.Scope;
 import org.springframework.http.ResponseEntity;
 import org.springframework.web.bind.annotation.DeleteMapping;
 import org.springframework.web.bind.annotation.GetMapping;
@@ -48,39 +48,6 @@ import org.springframework.web.bind.annotation.RequestParam;
 @Tag(name = "ProvMnS", description = "Provisioning Management Service")
 public interface ProvMnS {
 
-    /**
-     * DELETE /{URI-LDN-first-part}/{className}={id} : Deletes one resource
-     * With HTTP DELETE one resource is deleted. The resources to be deleted is identified with the target URI.
-     *
-     * @param httpServletRequest (required)
-     * @return Success case "200 OK". This status code is returned, when the resource has been successfully deleted.
-     *         The response body is empty. (status code 200)
-     *         or Error case. (status code 200)
-     */
-    @Operation(
-        operationId = "deleteMoi",
-        summary = "Deletes one resource",
-        description = "With HTTP DELETE one resource is deleted. "
-            + "The resources to be deleted is identified with the target URI.",
-        responses = {
-            @ApiResponse(responseCode = "200",
-                description = "Success case (\"200 OK\"). This status code is returned, "
-                    + "when the resource has been successfully deleted. The response body is empty."),
-            @ApiResponse(responseCode = "422", description = "Invalid Path Exception", content = {
-                @Content(mediaType = "application/json", schema = @Schema(implementation = ErrorMessage.class))
-            }),
-            @ApiResponse(responseCode = "default", description = "Error case.", content = {
-                @Content(mediaType = "application/json", schema = @Schema(implementation = ErrorResponseDefault.class))
-            })
-        }
-    )
-    @DeleteMapping(
-        value = "v1/**",
-        produces = { "application/json" }
-    )
-    ResponseEntity<Void> deleteMoi(HttpServletRequest httpServletRequest);
-
-
     /**
      * GET /{URI-LDN-first-part}/{className}={id} : Reads one or multiple resources
      * With HTTP GET resources are read. The resources to be retrieved are identified with the target URI.
@@ -283,4 +250,36 @@ public interface ProvMnS {
         @Valid @RequestBody Resource resource
     );
 
+    /**
+     * DELETE /{URI-LDN-first-part}/{className}={id} : Deletes one resource
+     * With HTTP DELETE one resource is deleted. The resources to be deleted is identified with the target URI.
+     *
+     * @param httpServletRequest (required)
+     * @return Success case "200 OK". This status code is returned, when the resource has been successfully deleted.
+     *         The response body is empty. (status code 200)
+     *         or Error case. (status code 200)
+     */
+    @Operation(
+        operationId = "deleteMoi",
+        summary = "Deletes one resource",
+        description = "With HTTP DELETE one resource is deleted. "
+            + "The resources to be deleted is identified with the target URI.",
+        responses = {
+            @ApiResponse(responseCode = "200",
+                description = "Success case (\"200 OK\"). This status code is returned, "
+                    + "when the resource has been successfully deleted. The response body is empty."),
+            @ApiResponse(responseCode = "422", description = "Invalid Path Exception", content = {
+                @Content(mediaType = "application/json", schema = @Schema(implementation = ErrorMessage.class))
+            }),
+            @ApiResponse(responseCode = "default", description = "Error case.", content = {
+                @Content(mediaType = "application/json", schema = @Schema(implementation = ErrorResponseDefault.class))
+            })
+        }
+    )
+    @DeleteMapping(
+        value = "v1/**",
+        produces = { "application/json" }
+    )
+    ResponseEntity<Void> deleteMoi(HttpServletRequest httpServletRequest);
+
 }
\ No newline at end of file
index d7193f0..4891613 100644 (file)
@@ -24,91 +24,69 @@ package org.onap.cps.ncmp.rest.controller;
 import jakarta.servlet.http.HttpServletRequest;
 import java.util.List;
 import lombok.RequiredArgsConstructor;
-import lombok.extern.slf4j.Slf4j;
-import org.onap.cps.ncmp.rest.provmns.model.ClassNameIdGetDataNodeSelectorParameter;
-import org.onap.cps.ncmp.rest.provmns.model.Resource;
-import org.onap.cps.ncmp.rest.provmns.model.Scope;
+import org.onap.cps.ncmp.api.data.models.OperationType;
+import org.onap.cps.ncmp.impl.dmi.DmiRestClient;
+import org.onap.cps.ncmp.impl.inventory.InventoryPersistence;
+import org.onap.cps.ncmp.impl.inventory.models.YangModelCmHandle;
+import org.onap.cps.ncmp.impl.models.RequiredDmiService;
+import org.onap.cps.ncmp.impl.provmns.model.ClassNameIdGetDataNodeSelectorParameter;
+import org.onap.cps.ncmp.impl.provmns.model.Resource;
+import org.onap.cps.ncmp.impl.provmns.model.Scope;
+import org.onap.cps.ncmp.impl.utils.AlternateIdMatcher;
+import org.onap.cps.ncmp.impl.utils.http.UrlTemplateParameters;
+import org.onap.cps.ncmp.rest.util.ProvMnSParametersMapper;
 import org.onap.cps.ncmp.rest.util.ProvMnsRequestParameters;
 import org.springframework.http.HttpStatus;
 import org.springframework.http.ResponseEntity;
 import org.springframework.web.bind.annotation.RequestMapping;
 import org.springframework.web.bind.annotation.RestController;
 
-@Slf4j
 @RestController
 @RequestMapping("${rest.api.provmns-base-path}")
 @RequiredArgsConstructor
 public class ProvMnsController implements ProvMnS {
 
-    /**
-     * Replaces a complete single resource or creates it if it does not exist.
-     *
-     * @param httpServletRequest      URI request including path
-     * @param resource                Resource representation of the resource to be created or replaced
-     * @return {@code ResponseEntity} The representation of the updated resource is returned in the response
-     *                                message body.
-     */
-    @Override
-    public ResponseEntity<Resource> putMoi(final HttpServletRequest httpServletRequest, final Resource resource) {
-        final ProvMnsRequestParameters provMnsRequestParameters =
-            ProvMnsRequestParameters.toProvMnsRequestParameters(httpServletRequest);
-        return new ResponseEntity<>(HttpStatus.NOT_IMPLEMENTED);
-    }
+    private final AlternateIdMatcher alternateIdMatcher;
+    private final DmiRestClient dmiRestClient;
+    private final InventoryPersistence inventoryPersistence;
+    private final ProvMnSParametersMapper provMnsParametersMapper;
 
-    /**
-     * Reads one or multiple resources.
-     *
-     * @param httpServletRequest      URI request including path
-     * @param scope                   Extends the set of targeted resources beyond the base
-     *                                resource identified with the authority and path component of
-     *                                the URI.
-     * @param filter                  Reduces the targeted set of resources by applying a filter to
-     *                                the scoped set of resource representations. Only resources
-     *                                representations for which the filter construct evaluates to
-     *                                "true" are targeted.
-     * @param attributes              Attributes of the scoped resources to be returned. The
-     *                                value is a comma-separated list of attribute names.
-     * @param fields                  Attribute fields of the scoped resources to be returned. The
-     *                                value is a comma-separated list of JSON pointers to the
-     *                                attribute fields.
-     * @param dataNodeSelector        dataNodeSelector object
-     * @return {@code ResponseEntity} The resources identified in the request for retrieval are returned
-     *                                in the response message body.
-     */
     @Override
     public ResponseEntity<Resource> getMoi(final HttpServletRequest httpServletRequest, final Scope scope,
                                                    final String filter, final List<String> attributes,
                                                    final List<String> fields,
                                                    final ClassNameIdGetDataNodeSelectorParameter dataNodeSelector) {
         final ProvMnsRequestParameters requestParameters =
-            ProvMnsRequestParameters.toProvMnsRequestParameters(httpServletRequest);
-        return new ResponseEntity<>(HttpStatus.NOT_IMPLEMENTED);
+            ProvMnsRequestParameters.extractProvMnsRequestParameters(httpServletRequest);
+        final YangModelCmHandle yangModelCmHandle = inventoryPersistence.getYangModelCmHandle(
+            alternateIdMatcher.getCmHandleId(requestParameters.getAlternateId()));
+        provMnsParametersMapper.checkDataProducerIdentifier(yangModelCmHandle);
+        final UrlTemplateParameters urlTemplateParameters = provMnsParametersMapper.getUrlTemplateParameters(scope,
+                                                                                     filter, attributes,
+                                                                                     fields, dataNodeSelector,
+                                                                                     yangModelCmHandle);
+        return dmiRestClient.synchronousGetOperation(
+            RequiredDmiService.DATA, urlTemplateParameters, OperationType.READ);
     }
 
-    /**
-     * Patches (Create, Update or Delete) one or multiple resources.
-     *
-     * @param httpServletRequest      URI request including path
-     * @param resource                Resource representation of the resource to be created or replaced
-     * @return {@code ResponseEntity} The updated resource representations are returned in the response message body.
-     */
     @Override
     public ResponseEntity<Resource> patchMoi(final HttpServletRequest httpServletRequest, final Resource resource) {
         final ProvMnsRequestParameters requestParameters =
-            ProvMnsRequestParameters.toProvMnsRequestParameters(httpServletRequest);
+            ProvMnsRequestParameters.extractProvMnsRequestParameters(httpServletRequest);
+        return new ResponseEntity<>(HttpStatus.NOT_IMPLEMENTED);
+    }
+
+    @Override
+    public ResponseEntity<Resource> putMoi(final HttpServletRequest httpServletRequest, final Resource resource) {
+        final ProvMnsRequestParameters provMnsRequestParameters =
+            ProvMnsRequestParameters.extractProvMnsRequestParameters(httpServletRequest);
         return new ResponseEntity<>(HttpStatus.NOT_IMPLEMENTED);
     }
 
-    /**
-     * Delete one or multiple resources.
-     *
-     * @param httpServletRequest      URI request including path
-     * @return {@code ResponseEntity} The response body is empty, HTTP status returned.
-     */
     @Override
     public ResponseEntity<Void> deleteMoi(final HttpServletRequest httpServletRequest) {
         final ProvMnsRequestParameters requestParameters =
-            ProvMnsRequestParameters.toProvMnsRequestParameters(httpServletRequest);
+            ProvMnsRequestParameters.extractProvMnsRequestParameters(httpServletRequest);
         return new ResponseEntity<>(HttpStatus.NOT_IMPLEMENTED);
     }
 }
diff --git a/cps-ncmp-rest/src/main/java/org/onap/cps/ncmp/rest/util/ProvMnSParametersMapper.java b/cps-ncmp-rest/src/main/java/org/onap/cps/ncmp/rest/util/ProvMnSParametersMapper.java
new file mode 100644 (file)
index 0000000..1f7c707
--- /dev/null
@@ -0,0 +1,81 @@
+/*
+ *  ============LICENSE_START=======================================================
+ *  Copyright (C) 2025 OpenInfra Foundation Europe
+ *  ================================================================================
+ *  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.util;
+
+import java.util.List;
+import lombok.RequiredArgsConstructor;
+import org.onap.cps.ncmp.api.exceptions.NcmpException;
+import org.onap.cps.ncmp.impl.dmi.DmiServiceAuthenticationProperties;
+import org.onap.cps.ncmp.impl.inventory.models.YangModelCmHandle;
+import org.onap.cps.ncmp.impl.provmns.model.ClassNameIdGetDataNodeSelectorParameter;
+import org.onap.cps.ncmp.impl.provmns.model.Scope;
+import org.onap.cps.ncmp.impl.utils.http.RestServiceUrlTemplateBuilder;
+import org.onap.cps.ncmp.impl.utils.http.UrlTemplateParameters;
+import org.springframework.stereotype.Service;
+
+@Service
+@RequiredArgsConstructor
+public class ProvMnSParametersMapper {
+
+    private final DmiServiceAuthenticationProperties dmiServiceAuthenticationProperties;
+
+    /**
+     * Creates a UrlTemplateParameters object containing the relevant fields for a get.
+     *
+     * @param scope               Provided className parameter.
+     * @param filter              Filter string.
+     * @param attributes          Attributes List.
+     * @param fields              Fields list
+     * @param dataNodeSelector    dataNodeSelector parameter
+     * @param yangModelCmHandle   yangModelCmHandle object for resolved alternate ID
+     * @return UrlTemplateParameters object.
+     */
+    public UrlTemplateParameters getUrlTemplateParameters(final Scope scope, final String filter,
+                                                      final List<String> attributes, final List<String> fields,
+                                                      final ClassNameIdGetDataNodeSelectorParameter dataNodeSelector,
+                                                      final YangModelCmHandle yangModelCmHandle) {
+
+        return RestServiceUrlTemplateBuilder.newInstance()
+            .queryParameter("scopeType", scope.getScopeType() != null
+                ? scope.getScopeType().getValue() : null)
+            .queryParameter("scopeLevel", scope.getScopeLevel() != null
+                ? scope.getScopeLevel().toString() : null)
+            .queryParameter("filter", filter)
+            .queryParameter("attributes", attributes != null ? attributes.toString() : null)
+            .queryParameter("fields", fields != null ? fields.toString() : null)
+            .queryParameter("dataNodeSelector", dataNodeSelector.getDataNodeSelector() != null
+                ? dataNodeSelector.getDataNodeSelector() : null)
+            .createUrlTemplateParameters(yangModelCmHandle.getDmiServiceName(), "ProvMnS");
+    }
+
+    /**
+     * Check if dataProducerIdentifier is empty or null, if so throw exception.
+     *
+     * @param yangModelCmHandle given yangModelCmHandle.
+     */
+    public void checkDataProducerIdentifier(final YangModelCmHandle yangModelCmHandle) {
+        if (yangModelCmHandle.getDataProducerIdentifier() == null
+            || yangModelCmHandle.getDataProducerIdentifier().isEmpty()) {
+            throw new NcmpException("No data producer identifier registered for cm handle",
+                "Cm Handle " + yangModelCmHandle.getId() + " has empty data producer identifier");
+        }
+    }
+}
index 3de9a44..3e5d0db 100644 (file)
@@ -35,13 +35,23 @@ public class ProvMnsRequestParameters {
 
     private static final String PROVMNS_BASE_PATH = "ProvMnS/v\\d+/";
 
+    /**
+     * Gets alternate id from combining URI-LDN-First-Part, className and Id.
+     *
+     * @return String of Alternate Id.
+     */
+    public String getAlternateId() {
+        return uriLdnFirstPart + "/" + className + "=" + id;
+    }
+
     /**
      * Converts HttpServletRequest to ProvMnsRequestParameters.
      *
      * @param httpServletRequest HttpServletRequest object containing the path
      * @return ProvMnsRequestParameters object containing parsed parameters
      */
-    public static ProvMnsRequestParameters toProvMnsRequestParameters(final HttpServletRequest httpServletRequest) {
+    public static ProvMnsRequestParameters extractProvMnsRequestParameters(
+                                                                        final HttpServletRequest httpServletRequest) {
         final String uriPath = (String) httpServletRequest.getAttribute(
             "org.springframework.web.servlet.HandlerMapping.pathWithinHandlerMapping");
 
index f1f4c91..3a3ef95 100644 (file)
@@ -35,7 +35,9 @@ import org.onap.cps.ncmp.impl.NetworkCmProxyInventoryFacadeImpl
 import org.onap.cps.ncmp.impl.data.NcmpCachedResourceRequestHandler
 import org.onap.cps.ncmp.impl.data.NcmpPassthroughResourceRequestHandler
 import org.onap.cps.ncmp.impl.data.NetworkCmProxyFacade
+import org.onap.cps.ncmp.impl.dmi.DmiRestClient
 import org.onap.cps.ncmp.impl.inventory.InventoryPersistence
+import org.onap.cps.ncmp.impl.utils.AlternateIdMatcher
 import org.onap.cps.ncmp.rest.provmns.exception.InvalidPathException
 import org.onap.cps.ncmp.rest.util.CmHandleStateMapper
 import org.onap.cps.ncmp.rest.util.DataOperationRequestMapper
@@ -45,6 +47,7 @@ import org.onap.cps.api.exceptions.AlreadyDefinedException
 import org.onap.cps.api.exceptions.CpsException
 import org.onap.cps.api.exceptions.DataNodeNotFoundException
 import org.onap.cps.api.exceptions.DataValidationException
+import org.onap.cps.ncmp.rest.util.ProvMnSParametersMapper
 import org.onap.cps.ncmp.rest.util.RestOutputCmHandleMapper
 import org.onap.cps.utils.JsonObjectMapper
 import org.spockframework.spring.SpringBean
@@ -111,6 +114,15 @@ class NetworkCmProxyRestExceptionHandlerSpec extends Specification {
     @SpringBean
     RestOutputCmHandleMapper mockRestOutputCmHandleMapper = Mock()
 
+    @SpringBean
+    ProvMnSParametersMapper provMnSParametersMapper = Mock()
+
+    @SpringBean
+    AlternateIdMatcher alternateIdMatcher = Mock()
+
+    @SpringBean
+    DmiRestClient dmiRestClient = Mock()
+
     @Value('${rest.api.ncmp-base-path}')
     def basePathNcmp
 
index d87dc80..4b468e0 100644 (file)
 package org.onap.cps.ncmp.rest.controller
 
 import com.fasterxml.jackson.databind.ObjectMapper
-import org.onap.cps.ncmp.rest.provmns.model.ResourceOneOf
+import jakarta.servlet.ServletException
+import org.onap.cps.ncmp.impl.dmi.DmiRestClient
+import org.onap.cps.ncmp.impl.inventory.InventoryPersistence
+import org.onap.cps.ncmp.impl.inventory.models.YangModelCmHandle
+import org.onap.cps.ncmp.impl.utils.AlternateIdMatcher
+import org.onap.cps.ncmp.impl.provmns.model.ResourceOneOf
+import org.onap.cps.ncmp.rest.util.ProvMnSParametersMapper
 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
 import org.springframework.boot.test.autoconfigure.web.servlet.WebMvcTest
 import org.springframework.http.HttpStatus
+import org.springframework.http.HttpStatusCode
 import org.springframework.http.MediaType
+import org.springframework.http.ResponseEntity
 import org.springframework.test.web.servlet.MockMvc
 import spock.lang.Specification
 
@@ -39,6 +48,18 @@ import static org.springframework.test.web.servlet.request.MockMvcRequestBuilder
 @WebMvcTest(ProvMnsController)
 class ProvMnsControllerSpec extends Specification {
 
+    @SpringBean
+    ProvMnSParametersMapper provMnSParametersMapper = new ProvMnSParametersMapper()
+
+    @SpringBean
+    AlternateIdMatcher alternateIdMatcher = Mock()
+
+    @SpringBean
+    InventoryPersistence inventoryPersistence = Mock()
+
+    @SpringBean
+    DmiRestClient dmiRestClient = Mock()
+
     @Autowired
     MockMvc mvc
 
@@ -47,37 +68,45 @@ class ProvMnsControllerSpec extends Specification {
     @Value('${rest.api.provmns-base-path}')
     def provMnSBasePath
 
-    def 'Get Resource Data from provmns interface.'() {
+    def 'Get Resource Data from provmns interface #scenario.'() {
         given: 'resource data url'
-            def getUrl = "$provMnSBasePath/v1/A=1/B=2/C=3"
+            def getUrl = "$provMnSBasePath/v1/someUriLdnFirstPart/someClassName=someId"+path
+        and: 'request classes return correct information'
+            inventoryPersistence.getYangModelCmHandle("cm-1") >> new YangModelCmHandle(dmiServiceName: "someDmiService", dataProducerIdentifier: 'someUriLdnFirstPart/someClassName=someId')
+            alternateIdMatcher.getCmHandleId("someUriLdnFirstPart/someClassName=someId") >> "cm-1"
+            dmiRestClient.synchronousGetOperation(*_) >> new ResponseEntity<Object>(HttpStatusCode.valueOf(200))
         when: 'get data resource request is performed'
             def response = mvc.perform(get(getUrl).contentType(MediaType.APPLICATION_JSON)).andReturn().response
-        then: 'response status is Not Implemented (501)'
-            assert response.status == HttpStatus.NOT_IMPLEMENTED.value()
+        then: 'response status is OK (200)'
+            assert response.status == HttpStatus.OK.value()
+        where:
+            scenario                 | path
+            'with no query params'   | ''
+            'with query params'      | '?attributes=[test,query,param]'
     }
 
-    def 'Put Resource Data from provmns interface.'() {
+    def 'Patch Resource Data from provmns interface.'() {
         given: 'resource data url'
-            def putUrl = "$provMnSBasePath/v1/A=1/B=2/C=3"
+            def patchUrl = "$provMnSBasePath/v1/someUriLdnFirstPart/someClassName=someId"
         and: 'an example resource json object'
             def jsonBody = jsonObjectMapper.asJsonString(new ResourceOneOf('test'))
-        when: 'put data resource request is performed'
-            def response = mvc.perform(put(putUrl)
-                    .contentType(MediaType.APPLICATION_JSON)
+        when: 'patch data resource request is performed'
+            def response = mvc.perform(patch(patchUrl)
+                    .contentType(new MediaType('application', 'json-patch+json'))
                     .content(jsonBody))
                     .andReturn().response
         then: 'response status is Not Implemented (501)'
             assert response.status == HttpStatus.NOT_IMPLEMENTED.value()
     }
 
-    def 'Patch Resource Data from provmns interface.'() {
+    def 'Put Resource Data from provmns interface.'() {
         given: 'resource data url'
-            def patchUrl = "$provMnSBasePath/v1/A=1/B=2/C=3"
+            def putUrl = "$provMnSBasePath/v1/someUriLdnFirstPart/someClassName=someId"
         and: 'an example resource json object'
             def jsonBody = jsonObjectMapper.asJsonString(new ResourceOneOf('test'))
-        when: 'patch data resource request is performed'
-            def response = mvc.perform(patch(patchUrl)
-                    .contentType(new MediaType('application', 'json-patch+json'))
+        when: 'put data resource request is performed'
+            def response = mvc.perform(put(putUrl)
+                    .contentType(MediaType.APPLICATION_JSON)
                     .content(jsonBody))
                     .andReturn().response
         then: 'response status is Not Implemented (501)'
@@ -86,19 +115,23 @@ class ProvMnsControllerSpec extends Specification {
 
     def 'Delete Resource Data from provmns interface.'() {
         given: 'resource data url'
-            def deleteUrl = "$provMnSBasePath/v1/A=1/B=2/C=3"
+            def deleteUrl = "$provMnSBasePath/v1/someUriLdnFirstPart/someClassName=someId"
         when: 'delete data resource request is performed'
             def response = mvc.perform(delete(deleteUrl)).andReturn().response
         then: 'response status is Not Implemented (501)'
             assert response.status == HttpStatus.NOT_IMPLEMENTED.value()
     }
 
-    def 'Get Resource Data from provmns interface with query param.'() {
-        given: 'resource data url with query parameter'
-            def getUrl = "$provMnSBasePath/v1/A=1/B=2/C=3?attributes=[test,query,param]"
+    def 'Invalid path passed in to provmns interface, #scenario'() {
+        given: 'an invalid path'
+            def url = "$provMnSBasePath/v1/" + invalidPath
         when: 'get data resource request is performed'
-            def response = mvc.perform(get(getUrl).contentType(MediaType.APPLICATION_JSON)).andReturn().response
-        then: 'response status is Not Implemented (501)'
-            assert response.status == HttpStatus.NOT_IMPLEMENTED.value()
+            mvc.perform(get(url).contentType(MediaType.APPLICATION_JSON))
+        then: 'invalid path exception is thrown'
+            thrown(ServletException)
+        where:
+            scenario                     | invalidPath
+            'Missing URI-LDN-first-part' | 'someClassName=someId'
+            'Missing ClassName and Id'   | 'someUriLdnFirstPart/'
     }
 }
diff --git a/cps-ncmp-rest/src/test/groovy/org/onap/cps/ncmp/rest/util/ProvMnSParametersMapperSpec.groovy b/cps-ncmp-rest/src/test/groovy/org/onap/cps/ncmp/rest/util/ProvMnSParametersMapperSpec.groovy
new file mode 100644 (file)
index 0000000..ee8a961
--- /dev/null
@@ -0,0 +1,63 @@
+/*
+ *  ============LICENSE_START=======================================================
+ *  Copyright (C) 2025 OpenInfra Foundation Europe. All rights reserved.
+ *  ================================================================================
+ *  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.util
+
+import org.onap.cps.ncmp.api.exceptions.NcmpException
+import org.onap.cps.ncmp.impl.inventory.models.YangModelCmHandle
+import org.onap.cps.ncmp.impl.provmns.model.ClassNameIdGetDataNodeSelectorParameter
+import org.onap.cps.ncmp.impl.provmns.model.Scope
+import spock.lang.Specification
+
+class ProvMnSParametersMapperSpec extends Specification{
+
+    def objectUnderTest = new ProvMnSParametersMapper()
+
+    def 'Extract url template parameters for GET'() {
+        when:'a set of given parameters from a call are passed in'
+            def result = objectUnderTest.getUrlTemplateParameters(new Scope(scopeLevel: 1, scopeType: 'BASE_ALL'),
+                    'some-filter', ['some-attribute'], ['some-field'], new ClassNameIdGetDataNodeSelectorParameter(dataNodeSelector: 'some-dataSelector'),
+                    new YangModelCmHandle(dmiServiceName: 'some-dmi-service'))
+        then:'verify object has been mapped correctly'
+            result.urlVariables().get('filter') == 'some-filter'
+    }
+
+    def 'Data Producer Identifier validation.'() {
+        given:'a yangModelCmHandle'
+            def yangModelCmHandle = new YangModelCmHandle(dataProducerIdentifier: 'some-dataProducer-ID')
+        when:'a yangModelCmHandle is passed in'
+            def result = objectUnderTest.checkDataProducerIdentifier(yangModelCmHandle)
+        then: 'no exception thrown for yangModelCmHandle when a data producer is present'
+            noExceptionThrown()
+    }
+
+    def 'Data Producer Identifier validation with #scenario.'() {
+        given:'a yangModelCmHandle'
+            def yangModelCmHandle = new YangModelCmHandle(dataProducerIdentifier: dataProducerId)
+        when:'a data producer identifier is checked'
+            def result = objectUnderTest.checkDataProducerIdentifier(yangModelCmHandle)
+        then: 'exception thrown'
+            thrown(NcmpException)
+        where:
+            scenario    | dataProducerId
+            'null'      | null
+            'blank'     | ''
+
+    }
+}
@@ -1,17 +1,17 @@
 # Ignore generation of all the models for ProvMns
-target/generated-sources/openapi/src/gen/java/org/onap/cps/ncmp/rest/provmns/model/*.java
-target/generated-sources/openapi/src/gen/java/org/onap/cps/ncmp/rest/provmns/api/*.java
+target/generated-sources/openapi/src/gen/java/org/onap/cps/ncmp/impl/provmns/model/*.java
+target/generated-sources/openapi/src/gen/java/org/onap/cps/ncmp/impl/provmns/api/*.java
 
 # Allow generation of the below model for ProvMns
-!target/generated-sources/openapi/src/gen/java/org/onap/cps/ncmp/rest/provmns/model/ClassNameIdPatchDefaultResponse.java
-!target/generated-sources/openapi/src/gen/java/org/onap/cps/ncmp/rest/provmns/model/ErrorResponseDefault.java
-!target/generated-sources/openapi/src/gen/java/org/onap/cps/ncmp/rest/provmns/model/ErrorResponseDefaultOtherProblemsInner.java
-!target/generated-sources/openapi/src/gen/java/org/onap/cps/ncmp/rest/provmns/model/ErrorResponseGet.java
-!target/generated-sources/openapi/src/gen/java/org/onap/cps/ncmp/rest/provmns/model/ErrorResponseGetOtherProblemsInner.java
-!target/generated-sources/openapi/src/gen/java/org/onap/cps/ncmp/rest/provmns/model/ErrorResponsePatch.java
-!target/generated-sources/openapi/src/gen/java/org/onap/cps/ncmp/rest/provmns/model/ErrorResponsePatchOtherProblemsInner.java
-!target/generated-sources/openapi/src/gen/java/org/onap/cps/ncmp/rest/provmns/model/PatchItem.java
-!target/generated-sources/openapi/src/gen/java/org/onap/cps/ncmp/rest/provmns/model/PatchOperation.java
-!target/generated-sources/openapi/src/gen/java/org/onap/cps/ncmp/rest/provmns/model/ResourceOneOf.java
-!target/generated-sources/openapi/src/gen/java/org/onap/cps/ncmp/rest/provmns/model/Scope.java
-!target/generated-sources/openapi/src/gen/java/org/onap/cps/ncmp/rest/provmns/model/ScopeType.java
\ No newline at end of file
+!target/generated-sources/openapi/src/gen/java/org/onap/cps/ncmp/impl/provmns/model/ClassNameIdPatchDefaultResponse.java
+!target/generated-sources/openapi/src/gen/java/org/onap/cps/ncmp/impl/provmns/model/ErrorResponseDefault.java
+!target/generated-sources/openapi/src/gen/java/org/onap/cps/ncmp/impl/provmns/model/ErrorResponseDefaultOtherProblemsInner.java
+!target/generated-sources/openapi/src/gen/java/org/onap/cps/ncmp/impl/provmns/model/ErrorResponseGet.java
+!target/generated-sources/openapi/src/gen/java/org/onap/cps/ncmp/impl/provmns/model/ErrorResponseGetOtherProblemsInner.java
+!target/generated-sources/openapi/src/gen/java/org/onap/cps/ncmp/impl/provmns/model/ErrorResponsePatch.java
+!target/generated-sources/openapi/src/gen/java/org/onap/cps/ncmp/impl/provmns/model/ErrorResponsePatchOtherProblemsInner.java
+!target/generated-sources/openapi/src/gen/java/org/onap/cps/ncmp/impl/provmns/model/PatchItem.java
+!target/generated-sources/openapi/src/gen/java/org/onap/cps/ncmp/impl/provmns/model/PatchOperation.java
+!target/generated-sources/openapi/src/gen/java/org/onap/cps/ncmp/impl/provmns/model/ResourceOneOf.java
+!target/generated-sources/openapi/src/gen/java/org/onap/cps/ncmp/impl/provmns/model/Scope.java
+!target/generated-sources/openapi/src/gen/java/org/onap/cps/ncmp/impl/provmns/model/ScopeType.java
\ No newline at end of file
index 10595ad..5b8e9a7 100644 (file)
             <groupId>jakarta.servlet</groupId>
             <artifactId>jakarta.servlet-api</artifactId>
         </dependency>
+        <dependency>
+            <groupId>io.swagger.core.v3</groupId>
+            <artifactId>swagger-annotations</artifactId>
+        </dependency>
     </dependencies>
+    <build>
+        <plugins>
+            <!-- Swagger code generation. -->
+            <plugin>
+                <groupId>org.openapitools</groupId>
+                <artifactId>openapi-generator-maven-plugin</artifactId>
+                <version>7.12.0</version>
+                <executions>
+                    <execution>
+                        <id>ncmp-code-gen-provmns</id>
+                        <goals>
+                            <goal>generate</goal>
+                        </goals>
+                        <configuration>
+                            <inputSpec>https://forge.3gpp.org/rep/all/5G_APIs/-/raw/REL-18/TS28532_ProvMnS.yaml</inputSpec>
+                            <invokerPackage>org.onap.cps.ncmp.impl.provmns.controller</invokerPackage>
+                            <modelPackage>org.onap.cps.ncmp.impl.provmns.model</modelPackage>
+                            <apiPackage>org.onap.cps.ncmp.impl.provmns.api</apiPackage>
+                            <generatorName>spring</generatorName>
+                            <generateSupportingFiles>false</generateSupportingFiles>
+                            <configOptions>
+                                <sourceFolder>src/gen/java</sourceFolder>
+                                <dateLibrary>java11</dateLibrary>
+                                <interfaceOnly>true</interfaceOnly>
+                                <useSpringBoot3>true</useSpringBoot3>
+                                <useTags>true</useTags>
+                                <openApiNullable>false</openApiNullable>
+                                <skipDefaultInterface>true</skipDefaultInterface>
+                            </configOptions>
+                            <importMappings>
+                                <importMapping>Resource=org.onap.cps.ncmp.impl.provmns.model.Resource</importMapping>
+                                <importMapping>ClassNameIdGetDataNodeSelectorParameter=org.onap.cps.ncmp.impl.provmns.model.ClassNameIdGetDataNodeSelectorParameter</importMapping>
+                            </importMappings>
+                            <ignoreFileOverride>${project.basedir}/.openapi-generator-ignore-provmns</ignoreFileOverride>
+                        </configuration>
+                    </execution>
+                </executions>
+            </plugin>
+        </plugins>
+    </build>
 </project>
index 9b0dc9f..3f49aff 100644 (file)
@@ -103,7 +103,7 @@ public class DmiDataOperations {
         final String jsonRequestBody = getDmiRequestBody(READ, requestId, null, null, yangModelCmHandle);
         final UrlTemplateParameters urlTemplateParameters = getUrlTemplateParameters(cmResourceAddress
                 .datastoreName(), yangModelCmHandle, cmResourceAddress.resourceIdentifier(), options, topic);
-        return dmiRestClient.asynchronousPostOperationWithJsonData(DATA, urlTemplateParameters, jsonRequestBody, READ,
+        return dmiRestClient.asynchronousPostOperation(DATA, urlTemplateParameters, jsonRequestBody, READ,
                 authorization);
     }
 
@@ -127,7 +127,7 @@ public class DmiDataOperations {
         final UrlTemplateParameters urlTemplateParameters = getUrlTemplateParameters(
                 PASSTHROUGH_OPERATIONAL.getDatastoreName(), yangModelCmHandle, "/", options,
                 null);
-        return dmiRestClient.synchronousPostOperationWithJsonData(DATA, urlTemplateParameters, jsonRequestBody, READ,
+        return dmiRestClient.synchronousPostOperation(DATA, urlTemplateParameters, jsonRequestBody, READ,
                 DmiRestClient.NO_AUTHORIZATION);
     }
 
@@ -192,7 +192,7 @@ public class DmiDataOperations {
         final UrlTemplateParameters urlTemplateParameters = getUrlTemplateParameters(
                 PASSTHROUGH_RUNNING.getDatastoreName(), yangModelCmHandle, resourceId, null,
                 null);
-        return dmiRestClient.synchronousPostOperationWithJsonData(DATA, urlTemplateParameters, jsonRequestBody,
+        return dmiRestClient.synchronousPostOperation(DATA, urlTemplateParameters, jsonRequestBody,
                 operationType, authorization);
     }
 
@@ -284,7 +284,7 @@ public class DmiDataOperations {
                     final List<DmiDataOperation> dmiDataOperations = entry.getValue();
                     final String dmiDataOperationRequestAsJsonString
                             = createDmiDataOperationRequestAsJsonString(dmiDataOperations);
-                    return dmiRestClient.asynchronousPostOperationWithJsonData(DATA, urlTemplateParameters,
+                    return dmiRestClient.asynchronousPostOperation(DATA, urlTemplateParameters,
                                     dmiDataOperationRequestAsJsonString, READ, authorization)
                             .then()
                             .onErrorResume(DmiClientRequestException.class, dmiClientRequestException -> {
index a8edbd1..fd9707e 100644 (file)
@@ -73,7 +73,7 @@ public class DmiSubJobRequestHandler {
 
             final UrlTemplateParameters urlTemplateParameters = getUrlTemplateParameters(dataJobMetadata.destination(),
                                                                                          producerKey);
-            final ResponseEntity<Object> responseEntity = dmiRestClient.synchronousPostOperationWithJsonData(
+            final ResponseEntity<Object> responseEntity = dmiRestClient.synchronousPostOperation(
                     RequiredDmiService.DATA,
                     urlTemplateParameters,
                     jsonObjectMapper.asJsonString(subJobWriteRequest),
index ceed489..b0e2b77 100644 (file)
@@ -34,6 +34,7 @@ import lombok.extern.slf4j.Slf4j;
 import org.onap.cps.ncmp.api.data.models.OperationType;
 import org.onap.cps.ncmp.api.exceptions.DmiClientRequestException;
 import org.onap.cps.ncmp.impl.models.RequiredDmiService;
+import org.onap.cps.ncmp.impl.provmns.model.Resource;
 import org.onap.cps.ncmp.impl.utils.http.UrlTemplateParameters;
 import org.onap.cps.utils.JsonObjectMapper;
 import org.springframework.beans.factory.annotation.Qualifier;
@@ -77,14 +78,13 @@ public class DmiRestClient {
      * @return ResponseEntity containing the response from the DMI.
      * @throws DmiClientRequestException If there is an error during the DMI request.
      */
-    public ResponseEntity<Object> synchronousPostOperationWithJsonData(final RequiredDmiService requiredDmiService,
-                                                                       final UrlTemplateParameters
-                                                                               urlTemplateParameters,
-                                                                       final String requestBodyAsJsonString,
-                                                                       final OperationType operationType,
-                                                                       final String authorization) {
+    public ResponseEntity<Object> synchronousPostOperation(final RequiredDmiService requiredDmiService,
+                                                           final UrlTemplateParameters urlTemplateParameters,
+                                                           final String requestBodyAsJsonString,
+                                                           final OperationType operationType,
+                                                           final String authorization) {
         final Mono<ResponseEntity<Object>> responseEntityMono =
-            asynchronousPostOperationWithJsonData(requiredDmiService,
+            asynchronousPostOperation(requiredDmiService,
                     urlTemplateParameters,
                 requestBodyAsJsonString,
                 operationType,
@@ -103,7 +103,7 @@ public class DmiRestClient {
      * @param authorization           The authorization token to be added to the request headers.
      * @return A Mono emitting the response entity containing the server's response.
      */
-    public Mono<ResponseEntity<Object>> asynchronousPostOperationWithJsonData(final RequiredDmiService
+    public Mono<ResponseEntity<Object>> asynchronousPostOperation(final RequiredDmiService
                                                                                       requiredDmiService,
                                                                               final UrlTemplateParameters
                                                                                       urlTemplateParameters,
@@ -120,6 +120,29 @@ public class DmiRestClient {
                 .onErrorMap(throwable -> handleDmiClientException(throwable, operationType.getOperationName()));
     }
 
+    /**
+     * Sends a synchronous (blocking) GET operation to the DMI.
+     *
+     * @param requiredDmiService    Determines if the required service is for a data or model operation.
+     * @param urlTemplateParameters The DMI resource URL template with variables.
+     * @param operationType         The type of operation being executed (for error reporting only).
+     * @return ResponseEntity containing the response from the DMI.
+     * @throws DmiClientRequestException If there is an error during the DMI request.
+     */
+    public ResponseEntity<Resource> synchronousGetOperation(final RequiredDmiService requiredDmiService,
+                                                                        final UrlTemplateParameters
+                                                                            urlTemplateParameters,
+                                                                        final OperationType operationType) {
+        return getWebClient(requiredDmiService)
+            .get()
+            .uri(urlTemplateParameters.urlTemplate(), urlTemplateParameters.urlVariables())
+            .headers(httpHeaders -> configureHttpHeaders(httpHeaders, NO_AUTHORIZATION))
+            .retrieve()
+            .toEntity(Resource.class)
+            .onErrorMap(throwable -> handleDmiClientException(throwable, operationType.getOperationName()))
+            .block();
+    }
+
     /**
      * Retrieves the health status of the DMI plugin.
      * This method performs an HTTP GET request to the DMI health check endpoint specified by the URL template
index 4843b8e..bf7e80a 100644 (file)
@@ -121,7 +121,7 @@ public class DmiModelOperations {
                 .variablePathSegment("cmHandleId", cmHandle)
                 .fixedPathSegment(resourceName)
                 .createUrlTemplateParameters(dmiServiceName, dmiServiceAuthenticationProperties.getDmiBasePath());
-        return dmiRestClient.synchronousPostOperationWithJsonData(MODEL, urlTemplateParameters, jsonRequestBody, READ,
+        return dmiRestClient.synchronousPostOperation(MODEL, urlTemplateParameters, jsonRequestBody, READ,
                 null);
     }
 
  *  ============LICENSE_END=========================================================
  */
 
-package org.onap.cps.ncmp.rest.provmns.model;
+package org.onap.cps.ncmp.impl.provmns.model;
 
 import com.fasterxml.jackson.databind.annotation.JsonDeserialize;
 import com.fasterxml.jackson.databind.annotation.JsonSerialize;
+import org.onap.cps.ncmp.impl.provmns.model.ResourceOneOf;
 
 /**
  * This interface serves as a replacement for the generated Resource class, which has dependencies on the NRM-related
index fa8a346..b7942dd 100644 (file)
@@ -90,7 +90,7 @@ class DmiDataOperationsSpec extends DmiOperationsBaseSpec {
             def responseFromDmi = Mono.just(new ResponseEntity<Object>('{some-key:some-value}', HttpStatus.OK))
             def expectedUrlTemplateWithVariables = getExpectedUrlTemplateWithVariables(expectedOptions, expectedDataStore)
             def expectedJson = '{"operation":"read","cmHandleProperties":' + expectedProperties + ',"moduleSetTag":""}'
-            mockDmiRestClient.asynchronousPostOperationWithJsonData(DATA, expectedUrlTemplateWithVariables, expectedJson, READ, NO_AUTH_HEADER) >> responseFromDmi
+            mockDmiRestClient.asynchronousPostOperation(DATA, expectedUrlTemplateWithVariables, expectedJson, READ, NO_AUTH_HEADER) >> responseFromDmi
         when: 'get resource data is invoked'
             def cmResourceAddress = new CmResourceAddress(expectedDataStore.datastoreName, cmHandleId, resourceIdentifier)
             def result = objectUnderTest.getResourceDataFromDmi(cmResourceAddress, expectedOptions, NO_TOPIC, NO_REQUEST_ID, NO_AUTH_HEADER).block()
@@ -117,11 +117,11 @@ class DmiDataOperationsSpec extends DmiOperationsBaseSpec {
             def responseFromDmi = Mono.just(new ResponseEntity<Object>(HttpStatus.ACCEPTED))
             def expectedUrlTemplateWithVariables = new UrlTemplateParameters('myServiceName/dmi/v1/data?requestId={requestId}&topic={topic}', ['requestId': 'requestId', 'topic': 'my-topic-name'])
             def expectedBatchRequestAsJson = '{"operations":[{"operation":"read","operationId":"operational-14","datastore":"ncmp-datastore:passthrough-operational","options":"some option","resourceIdentifier":"some resource identifier","cmHandles":[{"id":"some-cm-handle","moduleSetTag":"","cmHandleProperties":{"prop1":"val1"}}]}]}'
-            mockDmiRestClient.asynchronousPostOperationWithJsonData(DATA, expectedUrlTemplateWithVariables, _, READ, NO_AUTH_HEADER) >> responseFromDmi
+            mockDmiRestClient.asynchronousPostOperation(DATA, expectedUrlTemplateWithVariables, _, READ, NO_AUTH_HEADER) >> responseFromDmi
         when: 'get resource data for group of cm handles is invoked'
             objectUnderTest.requestResourceDataFromDmi('my-topic-name', dataOperationRequest, 'requestId', NO_AUTH_HEADER)
         then: 'the post operation was called with the expected URL and JSON request body'
-            1 * mockDmiRestClient.asynchronousPostOperationWithJsonData(DATA, expectedUrlTemplateWithVariables, expectedBatchRequestAsJson, READ, NO_AUTH_HEADER)
+            1 * mockDmiRestClient.asynchronousPostOperation(DATA, expectedUrlTemplateWithVariables, expectedBatchRequestAsJson, READ, NO_AUTH_HEADER)
     }
 
     def 'Execute (async) data operation from DMI service with Exception.'() {
@@ -134,7 +134,7 @@ class DmiDataOperationsSpec extends DmiOperationsBaseSpec {
             def actualDataOperationCloudEvent = null
             eventsProducer.sendCloudEvent('my-topic-name', 'my-request-id', _) >> { args -> actualDataOperationCloudEvent = args[2] }
         and: 'a DMI client request exception is thrown when DMI service is called'
-            mockDmiRestClient.asynchronousPostOperationWithJsonData(*_) >> { Mono.error(new DmiClientRequestException(123, '', '', UNKNOWN_ERROR)) }
+            mockDmiRestClient.asynchronousPostOperation(*_) >> { Mono.error(new DmiClientRequestException(123, '', '', UNKNOWN_ERROR)) }
         when: 'attempt to get resource data for group of cm handles is invoked'
             objectUnderTest.requestResourceDataFromDmi('my-topic-name', dataOperationRequest, 'my-request-id', NO_AUTH_HEADER)
         then: 'the event contains the expected error details'
@@ -153,7 +153,7 @@ class DmiDataOperationsSpec extends DmiOperationsBaseSpec {
             def responseFromDmi = new ResponseEntity<Object>(HttpStatus.OK)
             def expectedTemplateWithVariables = new UrlTemplateParameters('myServiceName/dmi/v1/ch/{cmHandleId}/data/ds/{datastore}?resourceIdentifier={resourceIdentifier}&options={options}', ['resourceIdentifier': '/', 'datastore': 'ncmp-datastore:passthrough-operational', 'cmHandleId': cmHandleId, 'options': OPTIONS_PARAM])
             def expectedJson = '{"operation":"read","cmHandleProperties":{"prop1":"val1"},"moduleSetTag":"my-module-set-tag"}'
-            mockDmiRestClient.synchronousPostOperationWithJsonData(DATA, expectedTemplateWithVariables, expectedJson, READ, null) >> responseFromDmi
+            mockDmiRestClient.synchronousPostOperation(DATA, expectedTemplateWithVariables, expectedJson, READ, null) >> responseFromDmi
         when: 'get resource data is invoked'
             def result = objectUnderTest.getAllResourceDataFromDmi(cmHandleId, NO_REQUEST_ID, OPTIONS_PARAM)
         then: 'the result is the response from the DMI service'
@@ -168,7 +168,7 @@ class DmiDataOperationsSpec extends DmiOperationsBaseSpec {
             def expectedUrlTemplateParameters = new UrlTemplateParameters('myServiceName/dmi/v1/ch/{cmHandleId}/data/ds/{datastore}?resourceIdentifier={resourceIdentifier}', ['resourceIdentifier': resourceIdentifier, 'datastore': 'ncmp-datastore:passthrough-running', 'cmHandleId': cmHandleId])
             def expectedJson = '{"operation":"' + expectedOperationInUrl + '","dataType":"some data type","data":"requestData","cmHandleProperties":{"prop1":"val1"},"moduleSetTag":""}'
             def responseFromDmi = new ResponseEntity<Object>(HttpStatus.OK)
-            mockDmiRestClient.synchronousPostOperationWithJsonData(DATA, expectedUrlTemplateParameters, expectedJson, operation, NO_AUTH_HEADER) >> responseFromDmi
+            mockDmiRestClient.synchronousPostOperation(DATA, expectedUrlTemplateParameters, expectedJson, operation, NO_AUTH_HEADER) >> responseFromDmi
         when: 'write resource method is invoked'
             def result = objectUnderTest.writeResourceDataPassThroughRunningFromDmi(cmHandleId, 'parent/child', operation, 'requestData', 'some data type', NO_AUTH_HEADER)
         then: 'the result is the response from the DMI service'
index bc4bb13..b10c96e 100644 (file)
@@ -31,7 +31,7 @@ class DmiSubJobRequestHandlerSpec extends Specification {
             def responseAsKeyValuePairs = [subJobId:'my-sub-job-id']
             def responseEntity = new ResponseEntity<>(responseAsKeyValuePairs, HttpStatus.OK)
             def expectedJson = '{"destination":"d1","dataAcceptType":"t1","dataContentType":"t2","dataProducerId":"prod1","dataJobId":"some-job-id","data":[{"path":"p","op":"operation","moduleSetTag":"tag","value":null,"operationId":"o1"}]}'
-            mockDmiRestClient.synchronousPostOperationWithJsonData(RequiredDmiService.DATA, _, expectedJson, OperationType.CREATE, authorization) >> responseEntity
+            mockDmiRestClient.synchronousPostOperation(RequiredDmiService.DATA, _, expectedJson, OperationType.CREATE, authorization) >> responseEntity
         when: 'sending request to DMI invoked'
             objectUnderTest.sendRequestsToDmi(authorization, dataJobId, dataJobMetadata, dmiWriteOperationsPerProducerKey)
         then: 'the result contains the expected sub-job id'
index 261d11c..cd77592 100644 (file)
@@ -78,7 +78,7 @@ class DmiRestClientSpec extends Specification {
             mockDataServicesWebClient.post() >> mockRequestBody
             mockResponse.toEntity(Object.class) >> Mono.just(new ResponseEntity<>('from Data service', HttpStatus.I_AM_A_TEAPOT))
         when: 'POST operation is invoked fro Data Service'
-            def response = objectUnderTest.synchronousPostOperationWithJsonData(DATA, urlTemplateParameters, 'some json', READ, NO_AUTH_HEADER)
+            def response = objectUnderTest.synchronousPostOperation(DATA, urlTemplateParameters, 'some json', READ, NO_AUTH_HEADER)
         then: 'the output of the method is equal to the output from the test template'
             assert response.statusCode == HttpStatus.I_AM_A_TEAPOT
             assert response.body == 'from Data service'
@@ -89,7 +89,7 @@ class DmiRestClientSpec extends Specification {
             mockModelServicesWebClient.post() >> mockRequestBody
             mockResponse.toEntity(Object.class) >> Mono.just(new ResponseEntity<>('from Model service', HttpStatus.I_AM_A_TEAPOT))
         when: 'POST operation is invoked for Model Service'
-            def response = objectUnderTest.synchronousPostOperationWithJsonData(MODEL, urlTemplateParameters, 'some json', READ, NO_AUTH_HEADER)
+            def response = objectUnderTest.synchronousPostOperation(MODEL, urlTemplateParameters, 'some json', READ, NO_AUTH_HEADER)
         then: 'the output of the method is equal to the output from the test template'
             assert response.statusCode == HttpStatus.I_AM_A_TEAPOT
             assert response.body == 'from Model service'
@@ -100,7 +100,7 @@ class DmiRestClientSpec extends Specification {
             mockDataServicesWebClient.post() >> mockRequestBody
             mockResponse.toEntity(Object.class) >> Mono.error(exceptionType)
         when: 'POST operation is invoked'
-            objectUnderTest.synchronousPostOperationWithJsonData(DATA, urlTemplateParameters, 'some json', READ, NO_AUTH_HEADER)
+            objectUnderTest.synchronousPostOperation(DATA, urlTemplateParameters, 'some json', READ, NO_AUTH_HEADER)
         then: 'a http client exception is thrown'
             def thrown = thrown(DmiClientRequestException)
         and: 'the exception has the relevant details from the error response'
index 4ebfb32..6811ed4 100644 (file)
@@ -58,7 +58,7 @@ class DmiModelOperationsSpec extends DmiOperationsBaseSpec {
         and: 'a positive response from DMI service when it is called with the expected parameters'
             def moduleReferencesAsLisOfMaps = [[moduleName: 'mod1', revision: 'A'], [moduleName: 'mod2', revision: 'X']]
             def responseFromDmi = new ResponseEntity([schemas: moduleReferencesAsLisOfMaps], HttpStatus.OK)
-            mockDmiRestClient.synchronousPostOperationWithJsonData(MODEL, expectedModulesUrlTemplateWithVariables, '{"cmHandleProperties":{},"moduleSetTag":""}', READ, NO_AUTH_HEADER) >> responseFromDmi
+            mockDmiRestClient.synchronousPostOperation(MODEL, expectedModulesUrlTemplateWithVariables, '{"cmHandleProperties":{},"moduleSetTag":""}', READ, NO_AUTH_HEADER) >> responseFromDmi
         when: 'get module references is called'
             def result = objectUnderTest.getModuleReferences(yangModelCmHandle, NO_MODULE_SET_TAG)
         then: 'the result consists of expected module references'
@@ -71,7 +71,7 @@ class DmiModelOperationsSpec extends DmiOperationsBaseSpec {
         and: 'any response from DMI service when it is called with the expected parameters'
             // TODO (toine): production code ignores any error code from DMI, this should be improved in future
             def responseFromDmi = new ResponseEntity(bodyAsMap, HttpStatus.NO_CONTENT)
-            mockDmiRestClient.synchronousPostOperationWithJsonData(*_) >> responseFromDmi
+            mockDmiRestClient.synchronousPostOperation(*_) >> responseFromDmi
         when: 'get module references is called'
             def result = objectUnderTest.getModuleReferences(yangModelCmHandle, NO_MODULE_SET_TAG)
         then: 'the result is empty'
@@ -89,7 +89,7 @@ class DmiModelOperationsSpec extends DmiOperationsBaseSpec {
             mockYangModelCmHandleRetrieval(additionalProperties)
         and: 'a positive response from DMI service when it is called with tha expected parameters'
             def responseFromDmi = new ResponseEntity<String>(HttpStatus.OK)
-            mockDmiRestClient.synchronousPostOperationWithJsonData(MODEL, expectedModulesUrlTemplateWithVariables,
+            mockDmiRestClient.synchronousPostOperation(MODEL, expectedModulesUrlTemplateWithVariables,
                     '{"cmHandleProperties":' + expectedAdditionalPropertiesInRequest + ',"moduleSetTag":""}', READ, NO_AUTH_HEADER) >> responseFromDmi
         when: 'a get module references is called'
             def result = objectUnderTest.getModuleReferences(yangModelCmHandle, NO_MODULE_SET_TAG)
@@ -108,7 +108,7 @@ class DmiModelOperationsSpec extends DmiOperationsBaseSpec {
             def responseFromDmi = new ResponseEntity([[moduleName: 'mod1', revision: 'A', yangSource: 'some yang source'],
                                                       [moduleName: 'mod2', revision: 'C', yangSource: 'other yang source']], HttpStatus.OK)
             def expectedModuleReferencesInRequest = '{"name":"mod1","revision":"A"},{"name":"mod2","revision":"X"}'
-            mockDmiRestClient.synchronousPostOperationWithJsonData(MODEL, expectedModuleResourcesUrlTemplateWithVariables,
+            mockDmiRestClient.synchronousPostOperation(MODEL, expectedModuleResourcesUrlTemplateWithVariables,
                     '{"data":{"modules":[' + expectedModuleReferencesInRequest + ']},"cmHandleProperties":{}}', READ, NO_AUTH_HEADER) >> responseFromDmi
         when: 'get new yang resources from DMI service'
             def result = objectUnderTest.getNewYangResourcesFromDmi(yangModelCmHandle, NO_MODULE_SET_TAG, newModuleReferences)
@@ -124,7 +124,7 @@ class DmiModelOperationsSpec extends DmiOperationsBaseSpec {
         and: 'a positive response from DMI service when it is called with tha expected parameters'
             // TODO (toine): production code ignores any error code from DMI, this should be improved in future
             def responseFromDmi = new ResponseEntity(responseFromDmiBody, HttpStatus.NO_CONTENT)
-            mockDmiRestClient.synchronousPostOperationWithJsonData(*_) >> responseFromDmi
+            mockDmiRestClient.synchronousPostOperation(*_) >> responseFromDmi
         when: 'get new yang resources from DMI service'
             def result = objectUnderTest.getNewYangResourcesFromDmi(yangModelCmHandle, NO_MODULE_SET_TAG, newModuleReferences)
         then: 'the result is empty'
@@ -140,7 +140,7 @@ class DmiModelOperationsSpec extends DmiOperationsBaseSpec {
             mockYangModelCmHandleRetrieval(additionalProperties)
         and: 'a positive response from DMI service when it is called with the expected moduleSetTag, modules and properties'
             def responseFromDmi = new ResponseEntity<>([[moduleName: 'mod1', revision: 'A', yangSource: 'some yang source']], HttpStatus.OK)
-            mockDmiRestClient.synchronousPostOperationWithJsonData(MODEL, expectedModuleResourcesUrlTemplateWithVariables,
+            mockDmiRestClient.synchronousPostOperation(MODEL, expectedModuleResourcesUrlTemplateWithVariables,
                     '{"data":{"modules":[{"name":"mod1","revision":"A"},{"name":"mod2","revision":"X"}]},"cmHandleProperties":' + expectedAdditionalPropertiesInRequest + '}',
                     READ, NO_AUTH_HEADER) >> responseFromDmi
         when: 'get new yang resources from DMI service'
@@ -158,7 +158,7 @@ class DmiModelOperationsSpec extends DmiOperationsBaseSpec {
             mockYangModelCmHandleRetrieval([], moduleSetTag)
         and: 'a positive response from DMI service when it is called with the expected moduleSetTag'
             def responseFromDmi = new ResponseEntity<>([[moduleName: 'mod1', revision: 'A', yangSource: 'some yang source']], HttpStatus.OK)
-            mockDmiRestClient.synchronousPostOperationWithJsonData(MODEL, expectedModuleResourcesUrlTemplateWithVariables,
+            mockDmiRestClient.synchronousPostOperation(MODEL, expectedModuleResourcesUrlTemplateWithVariables,
                 '{' + expectedModuleSetTagInRequest + '"data":{"modules":[{"name":"mod1","revision":"A"},{"name":"mod2","revision":"X"}]},"cmHandleProperties":{}}', READ, NO_AUTH_HEADER) >> responseFromDmi
         when: 'get new yang resources from DMI service'
             def result = objectUnderTest.getNewYangResourcesFromDmi(yangModelCmHandle, moduleSetTag, newModuleReferences)
@@ -179,7 +179,7 @@ class DmiModelOperationsSpec extends DmiOperationsBaseSpec {
         then: 'no resources are returned'
             assert result == [:]
         and: 'no request is sent to DMI'
-            0 * mockDmiRestClient.synchronousPostOperationWithJsonData(*_)
+            0 * mockDmiRestClient.synchronousPostOperation(*_)
     }
 
     def 'Retrieving yang resources from DMI with null DMI properties.'() {
@@ -197,7 +197,7 @@ class DmiModelOperationsSpec extends DmiOperationsBaseSpec {
         when: 'get module references is called'
             objectUnderTest.getModuleReferences(yangModelCmHandle, 'NEW-TAG')
         then: 'a request was sent to DMI with the NEW module set tag in the body'
-            1 * mockDmiRestClient.synchronousPostOperationWithJsonData(*_) >> { args ->
+            1 * mockDmiRestClient.synchronousPostOperation(*_) >> { args ->
                 def requestBodyAsJson = args[2] as String
                 assert requestBodyAsJson.contains('"moduleSetTag":"NEW-TAG"')
                 return new ResponseEntity([schemas: [[moduleName: 'mod1', revision: 'A'], [moduleName: 'mod2', revision: 'X']]], HttpStatus.OK)
@@ -210,7 +210,7 @@ class DmiModelOperationsSpec extends DmiOperationsBaseSpec {
         when: 'get new yang resources from DMI service'
             objectUnderTest.getNewYangResourcesFromDmi(yangModelCmHandle, 'NEW-TAG', newModuleReferences)
         then: 'a request was sent to DMI with the NEW module set tag in the body'
-            1 * mockDmiRestClient.synchronousPostOperationWithJsonData(*_) >> { args ->
+            1 * mockDmiRestClient.synchronousPostOperation(*_) >> { args ->
                 def requestBodyAsJson = args[2] as String
                 assert requestBodyAsJson.contains('"moduleSetTag":"NEW-TAG"')
                 return new ResponseEntity([[moduleName: 'mod1', revision: 'A', yangSource: 'some yang source'],
index a0046f1..3ceb359 100644 (file)
                         <exclude>org/onap/cps/rest/model/*</exclude>
                         <exclude>org/onap/cps/cpspath/parser/antlr4/*</exclude>
                         <exclude>org/onap/cps/ncmp/rest/model/*</exclude>
-                        <exclude>org/onap/cps/ncmp/rest/provmns/model/*</exclude>
                         <exclude>org/onap/cps/**/*MapperImpl.class</exclude>
                         <exclude>org/onap/cps/ncmp/rest/stub/*</exclude>
                         <exclude>org/onap/cps/policyexecutor/stub/model/*</exclude>
+                        <exclude>org/onap/cps/ncmp/impl/provmns/model/*</exclude>
                         <exclude>**/pom.xml</exclude>
                     </excludes>
                 </configuration>
index 556495e..828c374 100644 (file)
@@ -105,6 +105,11 @@ class DmiDispatcher extends Dispatcher {
             case ~'^/dmi/v1/cmwriteJob(.*)$':
                 return mockWriteJobResponse(request)
 
+            // provmns endpoint
+            case ~'^/ProvMnS/v1(.*)$':
+                dmiResourceDataUrl = request.path
+                return mockResponseWithBody(HttpStatus.OK, '{}')
+
             default:
                 throw new IllegalArgumentException('Mock DMI does not implement endpoint ' + request.path)
         }
index 474a480..f3e4cf4 100644 (file)
@@ -21,7 +21,7 @@
 package org.onap.cps.integration.functional.ncmp.provmns
 
 import org.onap.cps.integration.base.CpsIntegrationSpecBase
-import org.onap.cps.ncmp.rest.provmns.model.ResourceOneOf
+import org.onap.cps.ncmp.impl.provmns.model.ResourceOneOf
 import org.springframework.http.MediaType
 
 import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.delete
@@ -34,9 +34,14 @@ import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.
 class ProvMnSRestApiSpec extends CpsIntegrationSpecBase{
 
     def 'Get Resource Data from provmns interface.'() {
+        given: 'a registered cm handle'
+            dmiDispatcher1.moduleNamesPerCmHandleId['ch-1'] = ['M1', 'M2']
+            registerCmHandle(DMI1_URL, 'ch-1', NO_MODULE_SET_TAG, 'A=1/B=2/C=3')
         expect: 'not implemented response on GET endpoint'
             mvc.perform(get("/ProvMnS/v1/A=1/B=2/C=3"))
-                    .andExpect(status().isNotImplemented())
+                    .andExpect(status().is2xxSuccessful())
+        cleanup: 'deregister CM handles'
+            deregisterCmHandle(DMI1_URL, 'ch-1')
     }
 
     def 'Put Resource Data from provmns interface.'() {