Manual Implementation of ProvMnS controller 14/142014/6
authorseanbeirne <sean.beirne@est.tech>
Tue, 12 Aug 2025 15:04:47 +0000 (16:04 +0100)
committerseanbeirne <sean.beirne@est.tech>
Mon, 22 Sep 2025 13:43:13 +0000 (14:43 +0100)
- Implementation of ProvMnS using spring

Issue-ID: CPS-2966
Change-Id: Ia767b2779aa7b741230f295886f898246daf025c
Signed-off-by: seanbeirne <sean.beirne@est.tech>
cps-ncmp-rest/.openapi-generator-ignore-provmns
cps-ncmp-rest/src/main/java/org/onap/cps/ncmp/rest/controller/NetworkCmProxyRestExceptionHandler.java
cps-ncmp-rest/src/main/java/org/onap/cps/ncmp/rest/controller/ProvMnS.java [new file with mode: 0644]
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/provmns/exception/InvalidPathException.java [new file with mode: 0644]
cps-ncmp-rest/src/main/java/org/onap/cps/ncmp/rest/util/ProvMnsRequestParameters.java [new file with mode: 0644]
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
integration-test/src/test/groovy/org/onap/cps/integration/functional/ncmp/provmns/ProvMnSRestApiSpec.groovy

index 1f910ae..2c7ad93 100644 (file)
@@ -1,5 +1,6 @@
 # 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
 
 # Allow generation of the below model for ProvMns
 !target/generated-sources/openapi/src/gen/java/org/onap/cps/ncmp/rest/provmns/model/ClassNameIdPatchDefaultResponse.java
index 4b5ef17..4bb07b6 100644 (file)
@@ -41,6 +41,7 @@ import org.onap.cps.ncmp.api.exceptions.ServerNcmpException;
 import org.onap.cps.ncmp.rest.model.DmiErrorMessage;
 import org.onap.cps.ncmp.rest.model.DmiErrorMessageDmiResponse;
 import org.onap.cps.ncmp.rest.model.ErrorMessage;
+import org.onap.cps.ncmp.rest.provmns.exception.InvalidPathException;
 import org.springframework.http.HttpStatus;
 import org.springframework.http.ResponseEntity;
 import org.springframework.http.converter.HttpMessageNotReadableException;
@@ -101,6 +102,11 @@ public class NetworkCmProxyRestExceptionHandler {
         return buildErrorResponse(HttpStatus.PAYLOAD_TOO_LARGE, exception);
     }
 
+    @ExceptionHandler({InvalidPathException.class})
+    public static ResponseEntity<Object> invalidPathExceptions(final Exception exception) {
+        return buildErrorResponse(HttpStatus.UNPROCESSABLE_ENTITY, exception);
+    }
+
     private static ResponseEntity<Object> buildErrorResponse(final HttpStatus status, final Exception exception) {
         if (exception.getCause() != null || !(exception instanceof CpsException)) {
             log.error("Exception occurred", exception);
diff --git a/cps-ncmp-rest/src/main/java/org/onap/cps/ncmp/rest/controller/ProvMnS.java b/cps-ncmp-rest/src/main/java/org/onap/cps/ncmp/rest/controller/ProvMnS.java
new file mode 100644 (file)
index 0000000..77109b5
--- /dev/null
@@ -0,0 +1,286 @@
+/*
+ *  ============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.controller;
+
+import io.swagger.v3.oas.annotations.Operation;
+import io.swagger.v3.oas.annotations.Parameter;
+import io.swagger.v3.oas.annotations.enums.ParameterIn;
+import io.swagger.v3.oas.annotations.media.Content;
+import io.swagger.v3.oas.annotations.media.Schema;
+import io.swagger.v3.oas.annotations.responses.ApiResponse;
+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.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;
+import org.springframework.web.bind.annotation.PatchMapping;
+import org.springframework.web.bind.annotation.PutMapping;
+import org.springframework.web.bind.annotation.RequestBody;
+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.
+     * The attributes and fields parameter of the query components allow
+     * to select the resource properties to be returned.
+     *
+     * @param httpServletRequest (required)
+     * @param scope This parameter extends the set of targeted resources beyond the base resource identified
+     *              with the path component of the URI.
+     *              No scoping mechanism is specified in the present document. (optional)
+     * @param filter This parameter reduces the targeted set of resources by applying a filter to the scoped set of
+     *               resource representations. Only resource representations for which the filter construct evaluates
+     *               to "true" are targeted. (optional)
+     * @param attributes This parameter specifies the attributes of the scoped resources that are returned. (optional)
+     * @param fields This parameter specifies the attribute field of the scoped resources that are returned. (optional)
+     * @param dataNodeSelector This parameter contains an expression allowing
+     *                         to conditionally select data nodes. (optional)
+     * @return Success case "200 OK".
+     *          The resources identified in the request for retrieval are returned in the response message body.
+     *          In case the attributes or fields query parameters are used,
+     *          only the selected attributes or sub-attributes are returned.
+     *          The response message body is constructed according to the hierarchical response
+     *          construction method (TS 32.158 [15]). (status code 200) or Error case. (status code 200)
+     */
+    @Operation(
+        operationId = "getMoi",
+        summary = "Reads one or multiple resources",
+        description = "With HTTP GET resources are read. "
+            + "The resources to be retrieved are identified with the target URI. "
+            + "The attributes and fields parameter of the query components allow"
+            + " to select the resource properties to be returned.",
+        responses = {
+            @ApiResponse(responseCode = "200", description = "Success case (\"200 OK\"). "
+                + "The resources identified in the request for retrieval are returned in the response message body. "
+                + "In case the attributes or fields query parameters are used, "
+                + "only the selected attributes or sub-attributes are returned. "
+                + "The response message body is constructed according to the "
+                + "hierarchical response construction method (TS 32.158 [15]).", content = {
+                    @Content(mediaType = "application/json", schema = @Schema(implementation = Resource.class))
+                    }),
+            @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 = ErrorResponseGet.class))
+            })
+        }
+    )
+    @GetMapping(
+        value = "v1/**",
+        produces = { "application/json"}
+    )
+
+    ResponseEntity<Resource> getMoi(
+        HttpServletRequest httpServletRequest,
+        @Parameter(name = "scope", description = "This parameter extends the set of targeted resources beyond the "
+            + "base resource identified with the path component of the URI. "
+            + "No scoping mechanism is specified in the present document.", in = ParameterIn.QUERY) @Valid Scope scope,
+        @Parameter(name = "filter", description = "This parameter reduces the targeted set of resources by applying"
+            + " a filter to the scoped set of resource representations. "
+            + "Only resource representations for which the filter construct evaluates to \"true\" are targeted.",
+            in = ParameterIn.QUERY) @Valid @RequestParam(value = "filter", required = false) String filter,
+        @Parameter(name = "attributes", description = "This parameter specifies the attributes of the scoped "
+            + "resources that are returned.", in = ParameterIn.QUERY)
+        @Valid @RequestParam(value = "attributes", required = false)
+        List<String> attributes,
+        @Parameter(name = "fields", description = "This parameter specifies the attribute field "
+            + "of the scoped resources that are returned.", in = ParameterIn.QUERY)
+        @Valid @RequestParam(value = "fields", required = false) List<String> fields,
+        @Parameter(name = "dataNodeSelector", description = "This parameter contains an expression "
+            + "allowing to conditionally select data nodes.", in = ParameterIn.QUERY) @Valid
+        ClassNameIdGetDataNodeSelectorParameter dataNodeSelector
+    );
+
+
+    /**
+     * PATCH /{URI-LDN-first-part}/{className}={id} : Patches one or multiple resources
+     * With HTTP PATCH resources are created, updated or deleted.
+     * The resources to be modified are identified with the target URI (base resource)
+     * and the patch document included in the request message body.
+     *
+     * @param httpServletRequest (required)
+     * @param resource The request body describes changes to be made to the target resources.
+     *                 The following patch media types are available
+     *                 - "application/json-patch+json" (RFC 6902)
+     *                 - "application/3gpp-json-patch+json" (TS 32.158) (required)
+     * @return Success case ("200 OK").
+     *         This status code is returned when the updated resource representations
+     *         shall be returned for some reason.
+     *         The resource representations are returned in the response message body.
+     *         The response message body is constructed according to the hierarchical
+     *         response construction method (TS 32.158 [15]) (status code 200)
+     *         or Success case ("204 No Content"). This status code is returned when there is no need to
+     *         return the updated resource representations. The response message body is empty. (status code 204)
+     *         or Error case. (status code 200)
+     */
+    @Operation(
+        operationId = "patchMoi",
+        summary = "Patches one or multiple resources",
+        description = "With HTTP PATCH resources are created, updated or deleted. "
+            + "The resources to be modified are identified with the target URI (base resource) "
+            + "and the patch document included in the request message body.",
+        responses = {
+            @ApiResponse(responseCode = "200", description = "Success case (\"200 OK\"). "
+                + "This status code is returned when the updated the resource representations shall be returned "
+                + "for some reason. The resource representations are returned in the response message body. The "
+                + "response message body is constructed according to the hierarchical response construction method "
+                + "(TS 32.158 [15])", content = {
+                    @Content(mediaType = "application/json", schema = @Schema(implementation = Resource.class))
+                }),
+            @ApiResponse(responseCode = "204", description = "Success case (\"204 No Content\"). "
+                + "This status code is returned when there is no need to return the updated resource representations. "
+                + "The response message 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 = ClassNameIdPatchDefaultResponse.class))
+            })
+        }
+    )
+    @PatchMapping(
+        value = "v1/**",
+        produces = { "application/json" },
+        consumes = {"application/json-patch+json", "application/3gpp-json-patch+json" }
+    )
+
+    ResponseEntity<Resource> patchMoi(
+        HttpServletRequest httpServletRequest,
+        @Parameter(name = "Resource", description = "The request body describes changes to be made to the target "
+            + "resources. The following patch media types are available   "
+            + "- \"application/json-patch+json\" (RFC 6902)   "
+            + "- \"application/3gpp-json-patch+json\" (TS 32.158)", required = true) @Valid @RequestBody
+        Resource resource
+    );
+
+
+    /**
+     * PUT /{URI-LDN-first-part}/{className}={id} : Replaces a complete single resource or
+     * creates it if it does not exist
+     * With HTTP PUT a complete resource is replaced or created if it does not exist.
+     * The target resource is identified by the target URI.
+     *
+     * @param httpServletRequest (required)
+     * @param resource  (required)
+     * @return Success case ("200 OK"). This status code shall be returned when the resource is replaced,
+     *         and when the replaced resource representation is not identical to the resource representation in
+     *         the request. This status code may be returned when the resource is updated and when the updated
+     *         resource representation is identical to the resource representation in the request.
+     *         The representation of the updated resource is returned in the response message body. (status code 200)
+     *         or Success case ("201 Created"). This status code shall be returned when the resource
+     *         is created.
+     *         The representation of the created resource is returned in the response message body. (status code 201)
+     *         or Success case ("204 No Content"). This status code may be returned only when the replaced
+     *         resource representation is identical to the representation in the request.
+     *         The response has no message body. (status code 204)
+     *         or Error case. (status code 200)
+     */
+    @Operation(
+        operationId = "putMoi",
+        summary = "Replaces a complete single resource or creates it if it does not exist",
+        description = "With HTTP PUT a complete resource is replaced or created if it does not exist. "
+            + "The target resource is identified by the target URI.",
+        responses = {
+            @ApiResponse(responseCode = "200", description = "Success case (\"200 OK\"). "
+                + "This status code shall be returned when the resource is replaced, and when the replaced "
+                + "resource representation is not identical to the resource representation in the request. "
+                + "This status code may be returned when the resource is updated and when the updated resource "
+                + "representation is identical to the resource representation in the request. "
+                + "The representation of the updated resource is returned in the response message body.", content = {
+                    @Content(mediaType = "application/json", schema = @Schema(implementation = Resource.class))
+                }),
+            @ApiResponse(responseCode = "201", description = "Success case (\"201 Created\"). "
+                + "This status code shall be returned when the resource is created. The representation of"
+                + " the created resource is returned in the response message body.", content = {
+                    @Content(mediaType = "application/json", schema = @Schema(implementation = Resource.class))
+                }),
+            @ApiResponse(responseCode = "204", description = "Success case (\"204 No Content\"). "
+                + "This status code may be returned only when the replaced resource representation is identical "
+                + "to the representation in the request. The response has no message body."),
+            @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))
+            })
+        }
+    )
+    @PutMapping(
+        value = "v1/**",
+        produces = { "application/json" },
+        consumes = { "application/json" }
+    )
+
+    ResponseEntity<Resource> putMoi(
+        HttpServletRequest httpServletRequest,
+        @Parameter(name = "Resource",
+            description = "The request body describes the resource that has been created or replaced", required = true)
+        @Valid @RequestBody Resource resource
+    );
+
+}
\ No newline at end of file
index 0422544..d7193f0 100644 (file)
 package org.onap.cps.ncmp.rest.controller;
 
 
+import jakarta.servlet.http.HttpServletRequest;
 import java.util.List;
 import lombok.RequiredArgsConstructor;
-import org.onap.cps.ncmp.rest.provmns.api.DefaultApi;
+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.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 DefaultApi {
+public class ProvMnsController implements ProvMnS {
 
     /**
      * Replaces a complete single resource or creates it if it does not exist.
      *
-     * @param className               Class name of the targeted resource
-     * @param id                      Identifier of the targeted resource
+     * @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> classNameidPut(final String className, final String id, final Resource resource) {
+    public ResponseEntity<Resource> putMoi(final HttpServletRequest httpServletRequest, final Resource resource) {
+        final ProvMnsRequestParameters provMnsRequestParameters =
+            ProvMnsRequestParameters.toProvMnsRequestParameters(httpServletRequest);
         return new ResponseEntity<>(HttpStatus.NOT_IMPLEMENTED);
     }
 
     /**
      * Reads one or multiple resources.
      *
-     * @param className               Class name of the targeted resource
-     * @param id                      Identifier of the targeted resource
+     * @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.
@@ -73,35 +76,39 @@ public class ProvMnsController implements DefaultApi {
      *                                in the response message body.
      */
     @Override
-    public ResponseEntity<Resource> classNameidGet(final String className, final String id, final Scope scope,
+    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);
     }
 
     /**
      * Patches (Create, Update or Delete) one or multiple resources.
      *
-     * @param className               Class name of the targeted resource
-     * @param id                      Identifier of the targeted resource
+     * @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> classNameidPatch(final String className, final String id, final Resource resource) {
+    public ResponseEntity<Resource> patchMoi(final HttpServletRequest httpServletRequest, final Resource resource) {
+        final ProvMnsRequestParameters requestParameters =
+            ProvMnsRequestParameters.toProvMnsRequestParameters(httpServletRequest);
         return new ResponseEntity<>(HttpStatus.NOT_IMPLEMENTED);
     }
 
     /**
      * Delete one or multiple resources.
      *
-     * @param className               Class name of the targeted resource
-     * @param id                      Identifier of the targeted resource
+     * @param httpServletRequest      URI request including path
      * @return {@code ResponseEntity} The response body is empty, HTTP status returned.
      */
     @Override
-    public ResponseEntity<Void> classNameidDelete(final String className, final String id) {
+    public ResponseEntity<Void> deleteMoi(final HttpServletRequest httpServletRequest) {
+        final ProvMnsRequestParameters requestParameters =
+            ProvMnsRequestParameters.toProvMnsRequestParameters(httpServletRequest);
         return new ResponseEntity<>(HttpStatus.NOT_IMPLEMENTED);
     }
 }
diff --git a/cps-ncmp-rest/src/main/java/org/onap/cps/ncmp/rest/provmns/exception/InvalidPathException.java b/cps-ncmp-rest/src/main/java/org/onap/cps/ncmp/rest/provmns/exception/InvalidPathException.java
new file mode 100644 (file)
index 0000000..f9cc5cc
--- /dev/null
@@ -0,0 +1,40 @@
+/*
+ *  ============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.provmns.exception;
+
+import org.onap.cps.ncmp.api.exceptions.NcmpException;
+
+public class InvalidPathException extends NcmpException {
+
+    private static final String INVALID_PATH_DETAILS_FORMAT =
+        "%s not a valid path";
+
+    /**
+     * Constructor.
+     *
+     * @param path provmns uri path
+     */
+    public InvalidPathException(final String path) {
+        super("not a valid path", String.format(INVALID_PATH_DETAILS_FORMAT,
+            path));
+    }
+
+}
diff --git a/cps-ncmp-rest/src/main/java/org/onap/cps/ncmp/rest/util/ProvMnsRequestParameters.java b/cps-ncmp-rest/src/main/java/org/onap/cps/ncmp/rest/util/ProvMnsRequestParameters.java
new file mode 100644 (file)
index 0000000..3de9a44
--- /dev/null
@@ -0,0 +1,66 @@
+/*
+ *  ============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 jakarta.servlet.http.HttpServletRequest;
+import lombok.Getter;
+import lombok.Setter;
+import org.onap.cps.ncmp.rest.provmns.exception.InvalidPathException;
+
+@Getter
+@Setter
+public class ProvMnsRequestParameters {
+
+    private String uriLdnFirstPart;
+    private String className;
+    private String id;
+
+    private static final String PROVMNS_BASE_PATH = "ProvMnS/v\\d+/";
+
+    /**
+     * Converts HttpServletRequest to ProvMnsRequestParameters.
+     *
+     * @param httpServletRequest HttpServletRequest object containing the path
+     * @return ProvMnsRequestParameters object containing parsed parameters
+     */
+    public static ProvMnsRequestParameters toProvMnsRequestParameters(final HttpServletRequest httpServletRequest) {
+        final String uriPath = (String) httpServletRequest.getAttribute(
+            "org.springframework.web.servlet.HandlerMapping.pathWithinHandlerMapping");
+
+        final String[] pathVariables = uriPath.split(PROVMNS_BASE_PATH);
+        final int lastSlashIndex = pathVariables[1].lastIndexOf('/');
+        if (lastSlashIndex == -1) {
+            throw new InvalidPathException(uriPath);
+        }
+        final ProvMnsRequestParameters provMnsRequestParameters = new ProvMnsRequestParameters();
+        provMnsRequestParameters.setUriLdnFirstPart(pathVariables[1].substring(0, lastSlashIndex));
+        final String classNameAndId = pathVariables[1].substring(lastSlashIndex + 1);
+
+        final String[] splitClassNameId = classNameAndId.split("=", 2);
+        if (splitClassNameId.length != 2) {
+            throw new InvalidPathException(uriPath);
+        }
+        provMnsRequestParameters.setClassName(splitClassNameId[0]);
+        provMnsRequestParameters.setId(splitClassNameId[1]);
+
+        return provMnsRequestParameters;
+    }
+}
index 7b16df5..f1f4c91 100644 (file)
@@ -36,6 +36,7 @@ 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.inventory.InventoryPersistence
+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
 import org.onap.cps.ncmp.rest.util.DeprecationHelper
@@ -64,6 +65,7 @@ import static org.springframework.http.HttpStatus.CONFLICT
 import static org.springframework.http.HttpStatus.INTERNAL_SERVER_ERROR
 import static org.springframework.http.HttpStatus.NOT_FOUND
 import static org.springframework.http.HttpStatus.PAYLOAD_TOO_LARGE
+import static org.springframework.http.HttpStatus.UNPROCESSABLE_ENTITY
 import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get
 import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.post
 
@@ -148,6 +150,7 @@ class NetworkCmProxyRestExceptionHandlerSpec extends Specification {
             'Existing entries'      | AlreadyDefinedException.forDataNodes(['A', 'B'], 'myAnchorName')          || CONFLICT              | 'Already defined exception' | '2 data node(s) already exist'
             'Operation too large'   | new PayloadTooLargeException(sampleErrorMessage)                          || PAYLOAD_TOO_LARGE     | sampleErrorMessage          | 'Check logs'
             'Policy Executor'       | new PolicyExecutorException(sampleErrorMessage, sampleErrorDetails, null) || CONFLICT              | sampleErrorMessage          | sampleErrorDetails
+            'Invalid Path'          | new InvalidPathException("some invalid path")                             || UNPROCESSABLE_ENTITY  | 'not a valid path'          | 'some invalid path not a valid path'
     }
 
     def 'Post request with exception returns correct HTTP Status.'() {
index fbaa0c1..d87dc80 100644 (file)
@@ -23,7 +23,6 @@ package org.onap.cps.ncmp.rest.controller
 import com.fasterxml.jackson.databind.ObjectMapper
 import org.onap.cps.ncmp.rest.provmns.model.ResourceOneOf
 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
@@ -50,7 +49,7 @@ class ProvMnsControllerSpec extends Specification {
 
     def 'Get Resource Data from provmns interface.'() {
         given: 'resource data url'
-            def getUrl = "$provMnSBasePath/test=another"
+            def getUrl = "$provMnSBasePath/v1/A=1/B=2/C=3"
         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)'
@@ -59,7 +58,7 @@ class ProvMnsControllerSpec extends Specification {
 
     def 'Put Resource Data from provmns interface.'() {
         given: 'resource data url'
-            def putUrl = "$provMnSBasePath/test=another"
+            def putUrl = "$provMnSBasePath/v1/A=1/B=2/C=3"
         and: 'an example resource json object'
             def jsonBody = jsonObjectMapper.asJsonString(new ResourceOneOf('test'))
         when: 'put data resource request is performed'
@@ -73,7 +72,7 @@ class ProvMnsControllerSpec extends Specification {
 
     def 'Patch Resource Data from provmns interface.'() {
         given: 'resource data url'
-            def patchUrl = "$provMnSBasePath/test=another"
+            def patchUrl = "$provMnSBasePath/v1/A=1/B=2/C=3"
         and: 'an example resource json object'
             def jsonBody = jsonObjectMapper.asJsonString(new ResourceOneOf('test'))
         when: 'patch data resource request is performed'
@@ -87,11 +86,19 @@ class ProvMnsControllerSpec extends Specification {
 
     def 'Delete Resource Data from provmns interface.'() {
         given: 'resource data url'
-            def deleteUrl = "$provMnSBasePath/test=another"
+            def deleteUrl = "$provMnSBasePath/v1/A=1/B=2/C=3"
         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]"
+        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()
+    }
 }
index 29222a9..474a480 100644 (file)
@@ -30,11 +30,12 @@ import static org.springframework.test.web.servlet.request.MockMvcRequestBuilder
 import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.put
 import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status
 
+@SuppressWarnings('SpellCheckingInspection')
 class ProvMnSRestApiSpec extends CpsIntegrationSpecBase{
 
     def 'Get Resource Data from provmns interface.'() {
         expect: 'not implemented response on GET endpoint'
-            mvc.perform(get("/ProvMnS/SampleClassName=SampleId"))
+            mvc.perform(get("/ProvMnS/v1/A=1/B=2/C=3"))
                     .andExpect(status().isNotImplemented())
     }
 
@@ -42,7 +43,7 @@ class ProvMnSRestApiSpec extends CpsIntegrationSpecBase{
         given: 'an example resource json body'
             def jsonBody = jsonObjectMapper.asJsonString(new ResourceOneOf('test'))
         expect: 'not implemented response on PUT endpoint'
-            mvc.perform(put("/ProvMnS/SampleClassName=SampleId")
+            mvc.perform(put("/ProvMnS/v1/A=1/B=2/C=3")
                     .contentType(MediaType.APPLICATION_JSON)
                     .content(jsonBody))
                     .andExpect(status().isNotImplemented())
@@ -52,7 +53,7 @@ class ProvMnSRestApiSpec extends CpsIntegrationSpecBase{
         given: 'an example resource json body'
             def jsonBody = jsonObjectMapper.asJsonString(new ResourceOneOf('test'))
         expect: 'not implemented response on PATCH endpoint'
-            mvc.perform(patch("/ProvMnS/SampleClassName=SampleId")
+            mvc.perform(patch("/ProvMnS/v1/A=1/B=2/C=3")
                     .contentType(new MediaType('application', 'json-patch+json'))
                     .content(jsonBody))
                     .andExpect(status().isNotImplemented())
@@ -60,7 +61,7 @@ class ProvMnSRestApiSpec extends CpsIntegrationSpecBase{
 
     def 'Delete Resource Data from provmns interface.'() {
         expect: 'not implemented response on DELETE endpoint'
-            mvc.perform(delete("/ProvMnS/SampleClassName=SampleId"))
+            mvc.perform(delete("/ProvMnS/v1/A=1/B=2/C=3"))
                     .andExpect(status().isNotImplemented())
     }
 }