summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--cps-application/src/main/resources/application.yml6
-rw-r--r--cps-ncmp-rest/docs/openapi/components.yaml45
-rwxr-xr-xcps-ncmp-rest/docs/openapi/ncmp-inventory.yml40
-rwxr-xr-xcps-ncmp-rest/docs/openapi/ncmp.yml (renamed from cps-ncmp-rest/docs/openapi/ncmproxy.yml)36
-rwxr-xr-xcps-ncmp-rest/docs/openapi/openapi-inventory.yml28
-rwxr-xr-xcps-ncmp-rest/docs/openapi/openapi.yml21
-rw-r--r--cps-ncmp-rest/pom.xml38
-rwxr-xr-xcps-ncmp-rest/src/main/java/org/onap/cps/ncmp/rest/controller/NetworkCmProxyController.java71
-rwxr-xr-xcps-ncmp-rest/src/main/java/org/onap/cps/ncmp/rest/controller/NetworkCmProxyInventoryController.java70
-rw-r--r--cps-ncmp-rest/src/test/groovy/org/onap/cps/ncmp/rest/controller/NetworkCmProxyControllerSpec.groovy50
-rw-r--r--cps-ncmp-rest/src/test/groovy/org/onap/cps/ncmp/rest/controller/NetworkCmProxyInventoryControllerSpec.groovy65
-rw-r--r--cps-ncmp-rest/src/test/resources/application.yml2
-rw-r--r--cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/NetworkCmProxyDataService.java20
-rwxr-xr-xcps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/NetworkCmProxyDataServiceImpl.java20
-rw-r--r--cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/operation/DmiOperations.java72
-rw-r--r--cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/api/impl/NetworkCmProxyDataServiceImplSpec.groovy30
-rw-r--r--cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/api/impl/operation/DmiOperationsSpec.groovy18
-rw-r--r--cps-ri/src/main/java/org/onap/cps/spi/impl/CpsDataPersistenceServiceImpl.java127
-rwxr-xr-xcps-ri/src/test/groovy/org/onap/cps/spi/impl/CpsDataPersistenceServiceIntegrationSpec.groovy74
-rwxr-xr-xcps-ri/src/test/resources/data/fragment.sql9
-rw-r--r--docker-compose/README.md1
-rwxr-xr-xdocs/index.rst1
-rw-r--r--docs/modeling.rst11
-rw-r--r--docs/requirements-docs.txt17
-rw-r--r--docs/tox.ini6
25 files changed, 574 insertions, 304 deletions
diff --git a/cps-application/src/main/resources/application.yml b/cps-application/src/main/resources/application.yml
index 42addf1b0c..6717d4e92b 100644
--- a/cps-application/src/main/resources/application.yml
+++ b/cps-application/src/main/resources/application.yml
@@ -25,6 +25,7 @@ rest:
api:
cps-base-path: /cps/api
ncmp-base-path: /ncmp
+ ncmp-inventory-base-path: /ncmpInventory
spring:
main:
@@ -93,10 +94,13 @@ springdoc:
url: /api-docs/cps-core/openapi.yaml
- name: cps-ncmp
url: /api-docs/cps-ncmp/openapi.yaml
+ - name: cps-ncmp-inventory
+ url: /api-docs/cps-ncmp/openapi-inventory.yaml
+
security:
# comma-separated uri patterns which do not require authorization
- permit-uri: /manage/**,/swagger-ui/**,/swagger-resources/**,/api-docs
+ permit-uri: /manage/**,/swagger-ui.html,/swagger-ui/**,/swagger-resources/**,/api-docs/**
auth:
username: ${CPS_USERNAME}
password: ${CPS_PASSWORD}
diff --git a/cps-ncmp-rest/docs/openapi/components.yaml b/cps-ncmp-rest/docs/openapi/components.yaml
index ffb8dde113..d35919da4b 100644
--- a/cps-ncmp-rest/docs/openapi/components.yaml
+++ b/cps-ncmp-rest/docs/openapi/components.yaml
@@ -104,13 +104,27 @@ components:
schema:
type: string
default: /
- resourceIdentifierInPath:
+ resourceIdentifierInQuery:
name: resourceIdentifier
- in: path
- description: Resource identifier to get/set the resource data
+ in: query
+ description: The format of resource identifier depend on the associated DMI Plugin implementation. For ONAP DMI Plugin it will be RESTConf paths but it can really be anything.
required: true
+ allowReserved: true
schema:
type: string
+ examples:
+ sample1:
+ value:
+ resourceIdentifier: \parent\child
+ sample2:
+ value:
+ resourceIdentifier: \parent\listElement[key=value]
+ sample3:
+ value:
+ resourceIdentifier: \parent\listElement[key=value]\grandChild
+ sample4:
+ value:
+ resourceIdentifier: parent=1,child=abc
acceptParamInHeader:
name: Accept
in: header
@@ -119,21 +133,24 @@ components:
schema:
type: string
enum: [ application/json, application/yang-data+json ]
- fieldsParamInQuery:
- name: fields
+ optionsParamInQuery:
+ name: options
in: query
- description: Fields parameter to filter resource
+ description: options parameter in query, it is mandatory to wrap key(s)=value(s) in parenthesis'()'.
required: false
schema:
type: string
- depthParamInQuery:
- name: depth
- in: query
- description: Depth parameter for response
- required: false
- schema:
- type: integer
- minimum: 1
+ allowReserved: true
+ examples:
+ sample1:
+ value:
+ options: (key1=value1,key2=value2)
+ sample2:
+ value:
+ options: (key1=value1,key2=value1/value2)
+ sample3:
+ value:
+ options: (key1=10,key2=value2,key3=[val31;val32])
contentParamInHeader:
name: Content-Type
in: header
diff --git a/cps-ncmp-rest/docs/openapi/ncmp-inventory.yml b/cps-ncmp-rest/docs/openapi/ncmp-inventory.yml
new file mode 100755
index 0000000000..b0a50aa83a
--- /dev/null
+++ b/cps-ncmp-rest/docs/openapi/ncmp-inventory.yml
@@ -0,0 +1,40 @@
+# ============LICENSE_START=======================================================
+# Copyright (C) 2021 Bell Canada
+# ================================================================================
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+# SPDX-License-Identifier: Apache-2.0
+# ============LICENSE_END=========================================================
+
+updateDmiRegistration:
+ post:
+ description: Register a DMI Plugin with any new, updated or removed CM Handles.
+ tags:
+ - network-cm-proxy-inventory
+ summary: DMI notifies NCMP of new CM Handles
+ operationId: updateDmiPluginRegistration
+ requestBody:
+ required: true
+ content:
+ application/json:
+ schema:
+ $ref: 'components.yaml#/components/schemas/RestDmiPluginRegistration'
+ responses:
+ 201:
+ $ref: 'components.yaml#/components/responses/Created'
+ 400:
+ $ref: 'components.yaml#/components/responses/BadRequest'
+ 401:
+ $ref: 'components.yaml#/components/responses/Unauthorized'
+ 403:
+ $ref: 'components.yaml#/components/responses/Forbidden'
diff --git a/cps-ncmp-rest/docs/openapi/ncmproxy.yml b/cps-ncmp-rest/docs/openapi/ncmp.yml
index 2e5eba754d..52245c3cc0 100755
--- a/cps-ncmp-rest/docs/openapi/ncmproxy.yml
+++ b/cps-ncmp-rest/docs/openapi/ncmp.yml
@@ -1,6 +1,7 @@
# ============LICENSE_START=======================================================
# Copyright (C) 2021 Nordix Foundation
# Modifications Copyright (C) 2021 Pantheon.tech
+# Modifications Copyright (C) 2021 Bell Canada
# ================================================================================
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
@@ -179,29 +180,6 @@ listNodeByCmHandleAndXpath:
404:
$ref: 'components.yaml#/components/responses/NotFound'
-updateDmiRegistration:
- post:
- description: Register a DMI Plugin with any new, updated or removed CM Handles.
- tags:
- - network-cm-proxy
- summary: DMI notifies NCMP of new CM Handles
- operationId: updateDmiPluginRegistration
- requestBody:
- required: true
- content:
- application/json:
- schema:
- $ref: 'components.yaml#/components/schemas/RestDmiPluginRegistration'
- responses:
- 201:
- $ref: 'components.yaml#/components/responses/Created'
- 400:
- $ref: 'components.yaml#/components/responses/BadRequest'
- 401:
- $ref: 'components.yaml#/components/responses/Unauthorized'
- 403:
- $ref: 'components.yaml#/components/responses/Forbidden'
-
getResourceDataForPassthroughOperational:
get:
tags:
@@ -211,10 +189,9 @@ getResourceDataForPassthroughOperational:
operationId: getResourceDataOperationalForCmHandle
parameters:
- $ref: 'components.yaml#/components/parameters/cmHandleInPath'
- - $ref: 'components.yaml#/components/parameters/resourceIdentifierInPath'
+ - $ref: 'components.yaml#/components/parameters/resourceIdentifierInQuery'
- $ref: 'components.yaml#/components/parameters/acceptParamInHeader'
- - $ref: 'components.yaml#/components/parameters/fieldsParamInQuery'
- - $ref: 'components.yaml#/components/parameters/depthParamInQuery'
+ - $ref: 'components.yaml#/components/parameters/optionsParamInQuery'
responses:
200:
$ref: 'components.yaml#/components/responses/Ok'
@@ -236,10 +213,9 @@ resourceDataForPassthroughRunning:
operationId: getResourceDataRunningForCmHandle
parameters:
- $ref: 'components.yaml#/components/parameters/cmHandleInPath'
- - $ref: 'components.yaml#/components/parameters/resourceIdentifierInPath'
+ - $ref: 'components.yaml#/components/parameters/resourceIdentifierInQuery'
- $ref: 'components.yaml#/components/parameters/acceptParamInHeader'
- - $ref: 'components.yaml#/components/parameters/fieldsParamInQuery'
- - $ref: 'components.yaml#/components/parameters/depthParamInQuery'
+ - $ref: 'components.yaml#/components/parameters/optionsParamInQuery'
responses:
200:
$ref: 'components.yaml#/components/responses/Ok'
@@ -259,7 +235,7 @@ resourceDataForPassthroughRunning:
operationId: createResourceDataRunningForCmHandle
parameters:
- $ref: 'components.yaml#/components/parameters/cmHandleInPath'
- - $ref: 'components.yaml#/components/parameters/resourceIdentifierInPath'
+ - $ref: 'components.yaml#/components/parameters/resourceIdentifierInQuery'
- $ref: 'components.yaml#/components/parameters/contentParamInHeader'
requestBody:
required: true
diff --git a/cps-ncmp-rest/docs/openapi/openapi-inventory.yml b/cps-ncmp-rest/docs/openapi/openapi-inventory.yml
new file mode 100755
index 0000000000..ee09d050f3
--- /dev/null
+++ b/cps-ncmp-rest/docs/openapi/openapi-inventory.yml
@@ -0,0 +1,28 @@
+# ============LICENSE_START=======================================================
+# Copyright (C) 2021 Bell Canada
+# ================================================================================
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+# SPDX-License-Identifier: Apache-2.0
+# ============LICENSE_END=========================================================
+
+openapi: 3.0.1
+info:
+ title: NCMP Inventory API
+ description: NCMP Inventory API
+ version: "1.0"
+servers:
+ - url: /ncmpInventory
+paths:
+ /v1/ch:
+ $ref: 'ncmp-inventory.yml#/updateDmiRegistration'
diff --git a/cps-ncmp-rest/docs/openapi/openapi.yml b/cps-ncmp-rest/docs/openapi/openapi.yml
index 12356b5887..69c2a117d6 100755
--- a/cps-ncmp-rest/docs/openapi/openapi.yml
+++ b/cps-ncmp-rest/docs/openapi/openapi.yml
@@ -27,25 +27,22 @@ servers:
- url: /ncmp
paths:
/v1/cm-handles/{cm-handle}/node:
- $ref: 'ncmproxy.yml#/nodeByCmHandleAndXpath'
+ $ref: 'ncmp.yml#/nodeByCmHandleAndXpath'
/v1/cm-handles/{cm-handle}/list-node:
- $ref: 'ncmproxy.yml#/listNodeByCmHandleAndXpath'
+ $ref: 'ncmp.yml#/listNodeByCmHandleAndXpath'
/v1/cm-handles/{cm-handle}/nodes/query:
- $ref: 'ncmproxy.yml#/nodesByCmHandleAndCpsPath'
+ $ref: 'ncmp.yml#/nodesByCmHandleAndCpsPath'
/v1/cm-handles/{cm-handle}/nodes:
- $ref: 'ncmproxy.yml#/nodesByCmHandleAndXpath'
+ $ref: 'ncmp.yml#/nodesByCmHandleAndXpath'
- /v1/ch:
- $ref: 'ncmproxy.yml#/updateDmiRegistration'
+ /v1/ch/{cm-handle}/data/ds/ncmp-datastore:passthrough-operational:
+ $ref: 'ncmp.yml#/getResourceDataForPassthroughOperational'
- /v1/ch/{cm-handle}/data/ds/ncmp-datastore:passthrough-operational/{resourceIdentifier}:
- $ref: 'ncmproxy.yml#/getResourceDataForPassthroughOperational'
-
- /v1/ch/{cm-handle}/data/ds/ncmp-datastore:passthrough-running/{resourceIdentifier}:
- $ref: 'ncmproxy.yml#/resourceDataForPassthroughRunning'
+ /v1/ch/{cm-handle}/data/ds/ncmp-datastore:passthrough-running:
+ $ref: 'ncmp.yml#/resourceDataForPassthroughRunning'
/v1/ch/{cm-handle}/modules:
- $ref: 'ncmproxy.yml#/fetchModuleReferencesByCmHandle' \ No newline at end of file
+ $ref: 'ncmp.yml#/fetchModuleReferencesByCmHandle' \ No newline at end of file
diff --git a/cps-ncmp-rest/pom.xml b/cps-ncmp-rest/pom.xml
index b47ea17331..c4c72bb93b 100644
--- a/cps-ncmp-rest/pom.xml
+++ b/cps-ncmp-rest/pom.xml
@@ -112,7 +112,7 @@
<artifactId>swagger-codegen-maven-plugin</artifactId>
<executions>
<execution>
- <id>code-gen</id>
+ <id>ncmp-code-gen</id>
<goals>
<goal>generate</goal>
</goals>
@@ -131,6 +131,40 @@
</configOptions>
</configuration>
</execution>
+ <execution>
+ <id>ncmp-code-gen-inventory</id>
+ <goals>
+ <goal>generate</goal>
+ </goals>
+ <configuration>
+ <inputSpec>${project.basedir}/docs/openapi/openapi-inventory.yml</inputSpec>
+ <invokerPackage>org.onap.cps.ncmp.rest.controller</invokerPackage>
+ <modelPackage>org.onap.cps.ncmp.rest.model</modelPackage>
+ <apiPackage>org.onap.cps.ncmp.rest.api</apiPackage>
+ <language>spring</language>
+ <generateSupportingFiles>false</generateSupportingFiles>
+ <configOptions>
+ <sourceFolder>src/gen/java</sourceFolder>
+ <dateLibrary>java11</dateLibrary>
+ <interfaceOnly>true</interfaceOnly>
+ <useTags>true</useTags>
+ </configOptions>
+ </configuration>
+ </execution>
+ <execution>
+ <id>ncmp-inventory-openapi-yaml-gen</id>
+ <goals>
+ <goal>generate</goal>
+ </goals>
+ <phase>compile</phase>
+ <configuration>
+ <inputSpec>${project.basedir}/docs/openapi/openapi-inventory.yml</inputSpec>
+ <language>openapi-yaml</language>
+ <configOptions>
+ <outputFile>openapi-inventory.yaml</outputFile>
+ </configOptions>
+ </configuration>
+ </execution>
</executions>
</plugin>
<plugin>
@@ -148,7 +182,7 @@
<resource>
<directory>${project.basedir}/target/generated-sources/swagger/</directory>
<includes>
- <include>openapi.yaml</include>
+ <include>openapi*.yaml</include>
</includes>
</resource>
</resources>
diff --git a/cps-ncmp-rest/src/main/java/org/onap/cps/ncmp/rest/controller/NetworkCmProxyController.java b/cps-ncmp-rest/src/main/java/org/onap/cps/ncmp/rest/controller/NetworkCmProxyController.java
index a4d94cebfd..9b15a7890d 100755
--- a/cps-ncmp-rest/src/main/java/org/onap/cps/ncmp/rest/controller/NetworkCmProxyController.java
+++ b/cps-ncmp-rest/src/main/java/org/onap/cps/ncmp/rest/controller/NetworkCmProxyController.java
@@ -3,6 +3,7 @@
* Copyright (C) 2021 Pantheon.tech
* Modifications (C) 2021 Nordix Foundation
* Modification Copyright (C) 2021 highstreet technologies GmbH
+ * Modifications (C) 2021 Bell Canada
* ================================================================================
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -21,18 +22,13 @@
package org.onap.cps.ncmp.rest.controller;
-
-import com.fasterxml.jackson.databind.ObjectMapper;
import com.google.gson.Gson;
import com.google.gson.GsonBuilder;
import java.util.Collection;
import javax.validation.Valid;
-import javax.validation.constraints.Min;
import javax.validation.constraints.NotNull;
import org.onap.cps.ncmp.api.NetworkCmProxyDataService;
-import org.onap.cps.ncmp.api.models.DmiPluginRegistration;
import org.onap.cps.ncmp.rest.api.NetworkCmProxyApi;
-import org.onap.cps.ncmp.rest.model.RestDmiPluginRegistration;
import org.onap.cps.spi.FetchDescendantsOption;
import org.onap.cps.spi.model.DataNode;
import org.onap.cps.spi.model.ModuleReference;
@@ -50,17 +46,12 @@ public class NetworkCmProxyController implements NetworkCmProxyApi {
private final NetworkCmProxyDataService networkCmProxyDataService;
- private final ObjectMapper objectMapper;
-
/**
* Constructor Injection for Dependencies.
* @param networkCmProxyDataService Data Service Interface
- * @param objectMapper Object Mapper
*/
- public NetworkCmProxyController(final NetworkCmProxyDataService networkCmProxyDataService,
- final ObjectMapper objectMapper) {
+ public NetworkCmProxyController(final NetworkCmProxyDataService networkCmProxyDataService) {
this.networkCmProxyDataService = networkCmProxyDataService;
- this.objectMapper = objectMapper;
}
/**
@@ -102,19 +93,6 @@ public class NetworkCmProxyController implements NetworkCmProxyApi {
}
/**
- * Update DMI Plugin Registration (used for first registration also).
- * @param restDmiPluginRegistration the registration data
- */
- @Override
- public ResponseEntity<Void> updateDmiPluginRegistration(
- final @Valid RestDmiPluginRegistration restDmiPluginRegistration) {
- final DmiPluginRegistration dmiPluginRegistration =
- convertRestObjectToJavaApiObject(restDmiPluginRegistration);
- networkCmProxyDataService.updateDmiRegistrationAndSyncModule(dmiPluginRegistration);
- return new ResponseEntity<>(HttpStatus.CREATED);
- }
-
- /**
* Query Data Nodes.
* @deprecated This Method is no longer used as part of NCMP.
*/
@@ -158,22 +136,19 @@ public class NetworkCmProxyController implements NetworkCmProxyApi {
*
* @param cmHandle cm handle identifier
* @param resourceIdentifier resource identifier
- * @param accept accept header parameter
- * @param fields fields query parameter
- * @param depth depth query parameter
+ * @param acceptParamInHeader accept header parameter
+ * @param optionsParamInQuery options query parameter
* @return {@code ResponseEntity} response from dmi plugin
*/
@Override
public ResponseEntity<Object> getResourceDataOperationalForCmHandle(final String cmHandle,
- final String resourceIdentifier,
- final String accept,
- final @Valid String fields,
- final @Min(1) @Valid Integer depth) {
+ final @NotNull @Valid String resourceIdentifier,
+ final String acceptParamInHeader,
+ final @Valid String optionsParamInQuery) {
final Object responseObject = networkCmProxyDataService.getResourceDataOperationalForCmHandle(cmHandle,
resourceIdentifier,
- accept,
- fields,
- depth);
+ acceptParamInHeader,
+ optionsParamInQuery);
return ResponseEntity.ok(responseObject);
}
@@ -182,22 +157,19 @@ public class NetworkCmProxyController implements NetworkCmProxyApi {
*
* @param cmHandle cm handle identifier
* @param resourceIdentifier resource identifier
- * @param accept accept header parameter
- * @param fields fields query parameter
- * @param depth depth query parameter
+ * @param acceptParamInHeader accept header parameter
+ * @param optionsParamInQuery options query parameter
* @return {@code ResponseEntity} response from dmi plugin
*/
@Override
public ResponseEntity<Object> getResourceDataRunningForCmHandle(final String cmHandle,
- final String resourceIdentifier,
- final String accept,
- final @Valid String fields,
- final @Min(1) @Valid Integer depth) {
+ final @NotNull @Valid String resourceIdentifier,
+ final String acceptParamInHeader,
+ final @Valid String optionsParamInQuery) {
final Object responseObject = networkCmProxyDataService.getResourceDataPassThroughRunningForCmHandle(cmHandle,
resourceIdentifier,
- accept,
- fields,
- depth);
+ acceptParamInHeader,
+ optionsParamInQuery);
return ResponseEntity.ok(responseObject);
}
@@ -205,15 +177,15 @@ public class NetworkCmProxyController implements NetworkCmProxyApi {
* Create resource data in datastore pass through running
* for given cm-handle.
*
- * @param cmHandle cm handle identifier
* @param resourceIdentifier resource identifier
+ * @param cmHandle cm handle identifier
* @param requestBody requestBody
* @param contentType content type of body
* @return {@code ResponseEntity} response from dmi plugi
*/
@Override
- public ResponseEntity<Void> createResourceDataRunningForCmHandle(final String cmHandle,
- final String resourceIdentifier,
+ public ResponseEntity<Void> createResourceDataRunningForCmHandle(final String resourceIdentifier,
+ final String cmHandle,
final String requestBody,
final String contentType) {
networkCmProxyDataService.createResourceDataPassThroughRunningForCmHandle(cmHandle,
@@ -228,9 +200,4 @@ public class NetworkCmProxyController implements NetworkCmProxyApi {
return new ResponseEntity<>(new Gson().toJson(moduleReferences), HttpStatus.OK);
}
- private DmiPluginRegistration convertRestObjectToJavaApiObject(
- final RestDmiPluginRegistration restDmiPluginRegistration) {
- return objectMapper.convertValue(restDmiPluginRegistration, DmiPluginRegistration.class);
- }
-
}
diff --git a/cps-ncmp-rest/src/main/java/org/onap/cps/ncmp/rest/controller/NetworkCmProxyInventoryController.java b/cps-ncmp-rest/src/main/java/org/onap/cps/ncmp/rest/controller/NetworkCmProxyInventoryController.java
new file mode 100755
index 0000000000..3b72cec389
--- /dev/null
+++ b/cps-ncmp-rest/src/main/java/org/onap/cps/ncmp/rest/controller/NetworkCmProxyInventoryController.java
@@ -0,0 +1,70 @@
+/*
+ * ============LICENSE_START=======================================================
+ * Copyright (C) 2021 Bell Canada
+ * ================================================================================
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ * ============LICENSE_END=========================================================
+ */
+
+package org.onap.cps.ncmp.rest.controller;
+
+
+import com.fasterxml.jackson.databind.ObjectMapper;
+import javax.validation.Valid;
+import org.onap.cps.ncmp.api.NetworkCmProxyDataService;
+import org.onap.cps.ncmp.api.models.DmiPluginRegistration;
+import org.onap.cps.ncmp.rest.api.NetworkCmProxyInventoryApi;
+import org.onap.cps.ncmp.rest.model.RestDmiPluginRegistration;
+import org.springframework.http.HttpStatus;
+import org.springframework.http.ResponseEntity;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.RestController;
+
+@RestController
+@RequestMapping("${rest.api.ncmp-inventory-base-path}")
+public class NetworkCmProxyInventoryController implements NetworkCmProxyInventoryApi {
+
+ private final NetworkCmProxyDataService networkCmProxyDataService;
+ private final ObjectMapper objectMapper;
+
+ /**
+ * Constructor Injection for Dependencies.
+ * @param networkCmProxyDataService Data Service Interface
+ * @param objectMapper Object Mapper
+ */
+ public NetworkCmProxyInventoryController(final NetworkCmProxyDataService networkCmProxyDataService,
+ final ObjectMapper objectMapper) {
+ this.networkCmProxyDataService = networkCmProxyDataService;
+ this.objectMapper = objectMapper;
+ }
+
+ /**
+ * Update DMI Plugin Registration (used for first registration also).
+ * @param restDmiPluginRegistration the registration data
+ */
+ @Override
+ public ResponseEntity<Void> updateDmiPluginRegistration(
+ final @Valid RestDmiPluginRegistration restDmiPluginRegistration) {
+ final DmiPluginRegistration dmiPluginRegistration =
+ convertRestObjectToJavaApiObject(restDmiPluginRegistration);
+ networkCmProxyDataService.updateDmiRegistrationAndSyncModule(dmiPluginRegistration);
+ return new ResponseEntity<>(HttpStatus.CREATED);
+ }
+
+ private DmiPluginRegistration convertRestObjectToJavaApiObject(
+ final RestDmiPluginRegistration restDmiPluginRegistration) {
+ return objectMapper.convertValue(restDmiPluginRegistration, DmiPluginRegistration.class);
+ }
+
+}
diff --git a/cps-ncmp-rest/src/test/groovy/org/onap/cps/ncmp/rest/controller/NetworkCmProxyControllerSpec.groovy b/cps-ncmp-rest/src/test/groovy/org/onap/cps/ncmp/rest/controller/NetworkCmProxyControllerSpec.groovy
index 9f2b4e19a3..342f41b26f 100644
--- a/cps-ncmp-rest/src/test/groovy/org/onap/cps/ncmp/rest/controller/NetworkCmProxyControllerSpec.groovy
+++ b/cps-ncmp-rest/src/test/groovy/org/onap/cps/ncmp/rest/controller/NetworkCmProxyControllerSpec.groovy
@@ -31,9 +31,7 @@ import static org.springframework.test.web.servlet.request.MockMvcRequestBuilder
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.post
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.put
-import com.fasterxml.jackson.databind.ObjectMapper
import com.google.gson.Gson
-import org.onap.cps.TestUtils
import org.onap.cps.ncmp.api.NetworkCmProxyDataService
import org.onap.cps.spi.model.DataNodeBuilder
import org.spockframework.spring.SpringBean
@@ -54,9 +52,6 @@ class NetworkCmProxyControllerSpec extends Specification {
@SpringBean
NetworkCmProxyDataService mockNetworkCmProxyDataService = Mock()
- @SpringBean
- ObjectMapper objectMapper = new ObjectMapper()
-
@Value('${rest.api.ncmp-base-path}/v1')
def ncmpBasePathV1
@@ -175,25 +170,10 @@ class NetworkCmProxyControllerSpec extends Specification {
response.contentAsString.contains('"leaf":"value"')
}
- def 'Register CM Handle Event' () {
- given: 'jsonData'
- def jsonData = TestUtils.getResourceFileContent('dmi-registration.json')
- when: 'post request is performed'
- def response = mvc.perform(
- post("$ncmpBasePathV1/ch")
- .contentType(MediaType.APPLICATION_JSON)
- .content(jsonData)
- ).andReturn().response
- then: 'the cm handles are registered with the service'
- 1 * mockNetworkCmProxyDataService.updateDmiRegistrationAndSyncModule(_)
- and: 'response status is created'
- response.status == HttpStatus.CREATED.value()
- }
-
def 'Get Resource Data from pass-through operational.' () {
given: 'resource data url'
def getUrl = "$ncmpBasePathV1/ch/testCmHandle/data/ds/ncmp-datastore:passthrough-operational" +
- "/testResourceIdentifier?fields=testFields&depth=5"
+ "?resourceIdentifier=parent/child&options=(a=1,b=2)"
when: 'get data resource request is performed'
def response = mvc.perform(
get(getUrl)
@@ -202,24 +182,22 @@ class NetworkCmProxyControllerSpec extends Specification {
).andReturn().response
then: 'the NCMP data service is called with getResourceDataOperationalForCmHandle'
1 * mockNetworkCmProxyDataService.getResourceDataOperationalForCmHandle('testCmHandle',
- 'testResourceIdentifier',
+ 'parent/child',
'application/json',
- 'testFields',
- 5)
+ '(a=1,b=2)')
and: 'response status is Ok'
response.status == HttpStatus.OK.value()
}
- def 'Get Resource Data from pass-through running.' () {
+ def 'Get Resource Data from pass-through running with #scenario value in resource identifier param.' () {
given: 'resource data url'
def getUrl = "$ncmpBasePathV1/ch/testCmHandle/data/ds/ncmp-datastore:passthrough-running" +
- "/testResourceIdentifier?fields=testFields&depth=5"
+ "?resourceIdentifier=" + resourceIdentifier + "&options=(a=1,b=2)"
and: 'ncmp service returns json object'
mockNetworkCmProxyDataService.getResourceDataPassThroughRunningForCmHandle('testCmHandle',
- 'testResourceIdentifier',
- 'application/json',
- 'testFields',
- 5) >> '{valid-json}'
+ resourceIdentifier,
+ 'application/json',
+ '(a=1,b=2)') >> '{valid-json}'
when: 'get data resource request is performed'
def response = mvc.perform(
get(getUrl)
@@ -230,12 +208,20 @@ class NetworkCmProxyControllerSpec extends Specification {
response.status == HttpStatus.OK.value()
and: 'response contains valid object body'
response.getContentAsString() == '{valid-json}'
+ where: 'tokens are used in the resource identifier parameter'
+ scenario | resourceIdentifier
+ '/' | 'id/with/slashes'
+ '?' | 'idWith?'
+ ',' | 'idWith,'
+ '=' | 'idWith='
+ '[]' | 'idWith[]'
+ '? needs to be encoded as %3F' | 'idWith%3F'
}
def 'Create Resource Data from pass-through running with #scenario.' () {
given: 'resource data url'
def getUrl = "$ncmpBasePathV1/ch/testCmHandle/data/ds/ncmp-datastore:passthrough-running" +
- "/testResourceIdentifier"
+ "?resourceIdentifier=parent/child"
when: 'get data resource request is performed'
def response = mvc.perform(
post(getUrl)
@@ -244,7 +230,7 @@ class NetworkCmProxyControllerSpec extends Specification {
).andReturn().response
then: 'ncmp service method to create resource called'
1 * mockNetworkCmProxyDataService.createResourceDataPassThroughRunningForCmHandle('testCmHandle',
- 'testResourceIdentifier', requestBody, 'application/json;charset=UTF-8')
+ 'parent/child', requestBody, 'application/json;charset=UTF-8')
and: 'resource is created'
response.status == HttpStatus.CREATED.value()
where: 'given request body'
diff --git a/cps-ncmp-rest/src/test/groovy/org/onap/cps/ncmp/rest/controller/NetworkCmProxyInventoryControllerSpec.groovy b/cps-ncmp-rest/src/test/groovy/org/onap/cps/ncmp/rest/controller/NetworkCmProxyInventoryControllerSpec.groovy
new file mode 100644
index 0000000000..e558ac45bf
--- /dev/null
+++ b/cps-ncmp-rest/src/test/groovy/org/onap/cps/ncmp/rest/controller/NetworkCmProxyInventoryControllerSpec.groovy
@@ -0,0 +1,65 @@
+/*
+ * ============LICENSE_START=======================================================
+ * Copyright (C) 2021 Bell Canada
+ * ================================================================================
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ * ============LICENSE_END=========================================================
+ */
+
+package org.onap.cps.ncmp.rest.controller
+
+import com.fasterxml.jackson.databind.ObjectMapper
+import org.onap.cps.TestUtils
+import org.onap.cps.ncmp.api.NetworkCmProxyDataService
+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.context.annotation.Import
+import org.springframework.http.HttpStatus
+import org.springframework.http.MediaType
+import org.springframework.test.web.servlet.MockMvc
+import spock.lang.Specification
+import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.post
+
+@WebMvcTest(NetworkCmProxyInventoryController)
+@Import(ObjectMapper)
+class NetworkCmProxyInventoryControllerSpec extends Specification {
+
+ @Autowired
+ MockMvc mvc
+
+ @SpringBean
+ NetworkCmProxyDataService mockNetworkCmProxyDataService = Mock()
+
+ @Value('${rest.api.ncmp-inventory-base-path}/v1')
+ def ncmpBasePathV1
+
+ def 'Register CM Handle Event' () {
+ given: 'jsonData'
+ def jsonData = TestUtils.getResourceFileContent('dmi-registration.json')
+ when: 'post request is performed'
+ def response = mvc.perform(
+ post("$ncmpBasePathV1/ch")
+ .contentType(MediaType.APPLICATION_JSON)
+ .content(jsonData)
+ ).andReturn().response
+ then: 'the cm handles are registered with the service'
+ 1 * mockNetworkCmProxyDataService.updateDmiRegistrationAndSyncModule(_)
+ and: 'response status is created'
+ response.status == HttpStatus.CREATED.value()
+ }
+
+}
+
diff --git a/cps-ncmp-rest/src/test/resources/application.yml b/cps-ncmp-rest/src/test/resources/application.yml
index 848738a764..f2ca8c759b 100644
--- a/cps-ncmp-rest/src/test/resources/application.yml
+++ b/cps-ncmp-rest/src/test/resources/application.yml
@@ -20,4 +20,4 @@
rest:
api:
ncmp-base-path: /ncmp
-spring:
+ ncmp-inventory-base-path: /ncmpInventory
diff --git a/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/NetworkCmProxyDataService.java b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/NetworkCmProxyDataService.java
index 2f91ed30e1..cb2f78206c 100644
--- a/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/NetworkCmProxyDataService.java
+++ b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/NetworkCmProxyDataService.java
@@ -114,16 +114,14 @@ public interface NetworkCmProxyDataService {
*
* @param cmHandle cm handle
* @param resourceIdentifier resource identifier
- * @param accept accept param
- * @param fields fields query
- * @param depth depth query
+ * @param acceptParamInHeader accept param
+ * @param optionsParamInQuery options query
* @return {@code Object} resource data
*/
Object getResourceDataOperationalForCmHandle(@NotNull String cmHandle,
@NotNull String resourceIdentifier,
- String accept,
- String fields,
- Integer depth);
+ String acceptParamInHeader,
+ String optionsParamInQuery);
/**
* Get resource data for data store pass-through running
@@ -131,16 +129,14 @@ public interface NetworkCmProxyDataService {
*
* @param cmHandle cm handle
* @param resourceIdentifier resource identifier
- * @param acceptParam accept param
- * @param fields fields query
- * @param depth depth query
+ * @param acceptParamInHeader accept param
+ * @param optionsParamInQuery options query
* @return {@code Object} resource data
*/
Object getResourceDataPassThroughRunningForCmHandle(@NotNull String cmHandle,
@NotNull String resourceIdentifier,
- String acceptParam,
- String fields,
- Integer depth);
+ String acceptParamInHeader,
+ String optionsParamInQuery);
/**
* Create resource data for data store pass-through running
diff --git a/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/NetworkCmProxyDataServiceImpl.java b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/NetworkCmProxyDataServiceImpl.java
index 2395fb5db4..5594a2a7a5 100755
--- a/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/NetworkCmProxyDataServiceImpl.java
+++ b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/NetworkCmProxyDataServiceImpl.java
@@ -168,9 +168,8 @@ public class NetworkCmProxyDataServiceImpl implements NetworkCmProxyDataService
@Override
public Object getResourceDataOperationalForCmHandle(final @NotNull String cmHandle,
final @NotNull String resourceIdentifier,
- final String acceptParam,
- final String fieldsQueryParam,
- final Integer depthQueryParam) {
+ final String acceptParamInHeader,
+ final String optionsParamInQuery) {
final DataNode cmHandleDataNode = fetchDataNodeFromDmiRegistryForCmHandle(cmHandle);
final String dmiServiceName = String.valueOf(cmHandleDataNode.getLeaves().get(NCMP_DMI_SERVICE_NAME));
@@ -178,9 +177,8 @@ public class NetworkCmProxyDataServiceImpl implements NetworkCmProxyDataService
final ResponseEntity<Object> response = dmiOperations.getResourceDataOperationalFromDmi(dmiServiceName,
cmHandle,
resourceIdentifier,
- fieldsQueryParam,
- depthQueryParam,
- acceptParam,
+ optionsParamInQuery,
+ acceptParamInHeader,
dmiRequestBody);
return handleResponse(response);
}
@@ -188,18 +186,16 @@ public class NetworkCmProxyDataServiceImpl implements NetworkCmProxyDataService
@Override
public Object getResourceDataPassThroughRunningForCmHandle(final @NotNull String cmHandle,
final @NotNull String resourceIdentifier,
- final String acceptParam,
- final String fields,
- final Integer depth) {
+ final String acceptParamInHeader,
+ final String optionsParamInQuery) {
final DataNode cmHandleDataNode = fetchDataNodeFromDmiRegistryForCmHandle(cmHandle);
final String dmiServiceName = String.valueOf(cmHandleDataNode.getLeaves().get(NCMP_DMI_SERVICE_NAME));
final String dmiRequestBody = getGenericRequestBody(cmHandleDataNode);
final ResponseEntity<Object> response = dmiOperations.getResourceDataPassThroughRunningFromDmi(dmiServiceName,
cmHandle,
resourceIdentifier,
- fields,
- depth,
- acceptParam,
+ optionsParamInQuery,
+ acceptParamInHeader,
dmiRequestBody);
return handleResponse(response);
}
diff --git a/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/operation/DmiOperations.java b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/operation/DmiOperations.java
index 71af3d4cfe..363b0ef9b2 100644
--- a/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/operation/DmiOperations.java
+++ b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/operation/DmiOperations.java
@@ -52,6 +52,9 @@ public class DmiOperations {
private static final String DMI_CM_HANDLE_PATH = "/v1/ch/{cmHandle}";
private static final String DMI_CM_HANDLE_DATASTORE_PATH = DMI_CM_HANDLE_PATH + "/data/ds";
private static final String URL_SEPARATOR = "/";
+ private static final String RESOURCE_IDENTIFIER = "resourceIdentifier";
+ private static final String OPTIONS_QUERY_KEY = "options";
+
/**
* Constructor for {@code DmiOperations}. This method also manipulates url properties.
@@ -102,22 +105,20 @@ public class DmiOperations {
* @param dmiServiceName dmi service name
* @param cmHandle network resource identifier
* @param resourceId resource identifier
- * @param fieldsQuery fields query
- * @param depthQuery depth query
- * @param acceptParam accept parameter
+ * @param optionsParamInQuery options query
+ * @param acceptParamInHeader accept parameter
* @param jsonBody json body for put operation
* @return {@code ResponseEntity} response entity
*/
public ResponseEntity<Object> getResourceDataOperationalFromDmi(final String dmiServiceName,
final String cmHandle,
final String resourceId,
- final String fieldsQuery,
- final Integer depthQuery,
- final String acceptParam,
+ final String optionsParamInQuery,
+ final String acceptParamInHeader,
final String jsonBody) {
final var dmiResourceDataUrl = getDmiDatastoreUrl(dmiServiceName, cmHandle, resourceId,
- fieldsQuery, depthQuery, DataStoreEnum.PASSTHROUGH_OPERATIONAL);
- final var httpHeaders = prepareHeader(acceptParam);
+ optionsParamInQuery, DataStoreEnum.PASSTHROUGH_OPERATIONAL);
+ final var httpHeaders = prepareHeader(acceptParamInHeader);
return dmiRestClient.putOperationWithJsonData(dmiResourceDataUrl, jsonBody, httpHeaders);
}
@@ -128,22 +129,20 @@ public class DmiOperations {
* @param dmiServiceName dmi service name
* @param cmHandle network resource identifier
* @param resourceId resource identifier
- * @param fieldsQuery fields query
- * @param depthQuery depth query
- * @param acceptParam accept parameter
+ * @param optionsParamInQuery fields query
+ * @param acceptParamInHeader accept parameter
* @param jsonBody json body for put operation
* @return {@code ResponseEntity} response entity
*/
public ResponseEntity<Object> getResourceDataPassThroughRunningFromDmi(final String dmiServiceName,
final String cmHandle,
final String resourceId,
- final String fieldsQuery,
- final Integer depthQuery,
- final String acceptParam,
+ final String optionsParamInQuery,
+ final String acceptParamInHeader,
final String jsonBody) {
final var dmiResourceDataUrl = getDmiDatastoreUrl(dmiServiceName, cmHandle, resourceId,
- fieldsQuery, depthQuery, DataStoreEnum.PASSTHROUGH_RUNNING);
- final var httpHeaders = prepareHeader(acceptParam);
+ optionsParamInQuery, DataStoreEnum.PASSTHROUGH_RUNNING);
+ final var httpHeaders = prepareHeader(acceptParamInHeader);
return dmiRestClient.putOperationWithJsonData(dmiResourceDataUrl, jsonBody, httpHeaders);
}
@@ -161,7 +160,7 @@ public class DmiOperations {
final String cmHandle,
final String resourceId,
final String jsonBody) {
- final var stringBuilder = getStringBuilderForPassThroughRunningUrl(dmiServiceName,
+ final var stringBuilder = getStringBuilderForPassThroughUrl(dmiServiceName,
cmHandle, resourceId, DataStoreEnum.PASSTHROUGH_RUNNING);
return dmiRestClient.postOperationWithJsonData(stringBuilder.toString(), jsonBody, new HttpHeaders());
}
@@ -181,50 +180,35 @@ public class DmiOperations {
private String getDmiDatastoreUrl(final String dmiServiceName,
final String cmHandle,
final String resourceId,
- final String fieldsQuery,
- final Integer depthQuery,
+ final String optionsParamInQuery,
final DataStoreEnum dataStoreEnum) {
- final var stringBuilder = getStringBuilderForPassThroughRunningUrl(dmiServiceName,
+ final var stringBuilder = getStringBuilderForPassThroughUrl(dmiServiceName,
cmHandle, resourceId, dataStoreEnum);
- appendFieldsAndDepth(stringBuilder, fieldsQuery, depthQuery);
+ appendOptionsQuery(stringBuilder, optionsParamInQuery);
return stringBuilder.toString();
}
@NotNull
- private StringBuilder getStringBuilderForPassThroughRunningUrl(final String dmiServiceName,
- final String cmHandle,
- final String resourceId,
- final DataStoreEnum dataStoreEnum) {
+ private StringBuilder getStringBuilderForPassThroughUrl(final String dmiServiceName,
+ final String cmHandle,
+ final String resourceId,
+ final DataStoreEnum dataStoreEnum) {
final var stringBuilder = new StringBuilder(dmiServiceName);
stringBuilder.append(DMI_API_PATH);
stringBuilder.append(DMI_CM_HANDLE_DATASTORE_PATH.replace("{cmHandle}", cmHandle));
stringBuilder.append(URL_SEPARATOR + dataStoreEnum.getValue());
- stringBuilder.append(URL_SEPARATOR + resourceId);
+ stringBuilder.append("?" + RESOURCE_IDENTIFIER + "=" + resourceId);
return stringBuilder;
}
- private void appendFieldsAndDepth(final StringBuilder stringBuilder,
- final String fieldsQuery,
- final Integer depthQuery) {
- final var doesFieldExists = (fieldsQuery != null && !fieldsQuery.isEmpty());
- if (doesFieldExists) {
- stringBuilder.append("?").append("fields=").append(fieldsQuery);
- }
- if (depthQuery != null) {
- if (doesFieldExists) {
- stringBuilder.append("&");
- } else {
- stringBuilder.append("?");
- }
- stringBuilder.append("depth=").append(depthQuery);
- }
+ private void appendOptionsQuery(final StringBuilder stringBuilder,
+ final String optionsParamInQuery) {
+ stringBuilder.append("&").append(OPTIONS_QUERY_KEY).append("=").append(optionsParamInQuery);
}
private HttpHeaders prepareHeader(final String acceptParam) {
final var httpHeaders = new HttpHeaders();
- if (acceptParam != null && !acceptParam.isEmpty()) {
- httpHeaders.set(HttpHeaders.ACCEPT, acceptParam);
- }
+ httpHeaders.set(HttpHeaders.ACCEPT, acceptParam);
return httpHeaders;
}
}
diff --git a/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/api/impl/NetworkCmProxyDataServiceImplSpec.groovy b/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/api/impl/NetworkCmProxyDataServiceImplSpec.groovy
index e38386fdd2..b5d4713959 100644
--- a/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/api/impl/NetworkCmProxyDataServiceImplSpec.groovy
+++ b/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/api/impl/NetworkCmProxyDataServiceImplSpec.groovy
@@ -231,16 +231,14 @@ class NetworkCmProxyDataServiceImplSpec extends Specification {
mockDmiOperations.getResourceDataOperationalFromDmi('testDmiService',
'testCmHandle',
'testResourceId',
- 'testFieldQuery',
- 5,
+ '(a=1,b=2)',
'testAcceptParam',
'{"operation":"read","cmHandleProperties":{"testName":"testValue"}}') >> new ResponseEntity<>('result-json', HttpStatus.OK)
when: 'get resource data is called'
def response = objectUnderTest.getResourceDataOperationalForCmHandle('testCmHandle',
'testResourceId',
'testAcceptParam',
- 'testFieldQuery',
- 5)
+ '(a=1,b=2)')
then: 'dmi returns ok response'
response == 'result-json'
}
@@ -259,8 +257,7 @@ class NetworkCmProxyDataServiceImplSpec extends Specification {
def response = objectUnderTest.getResourceDataOperationalForCmHandle('testCmHandle',
'testResourceId',
'testAcceptParam',
- 'testFieldQuery',
- 5)
+ '(a=1,b=2)')
then: 'exception is thrown with the expected details'
def exceptionThrown = thrown(NcmpException.class)
exceptionThrown.details == 'testException'
@@ -276,8 +273,7 @@ class NetworkCmProxyDataServiceImplSpec extends Specification {
mockDmiOperations.getResourceDataOperationalFromDmi('testDmiService',
'testCmHandle',
'testResourceId',
- 'testFieldQuery',
- 5,
+ '(a=1,b=2)',
'testAcceptParam',
'{"operation":"read","cmHandleProperties":{"testName":"testValue"}}')
>> new ResponseEntity<>('NOK-json', HttpStatus.NOT_FOUND)
@@ -285,8 +281,7 @@ class NetworkCmProxyDataServiceImplSpec extends Specification {
def response = objectUnderTest.getResourceDataOperationalForCmHandle('testCmHandle',
'testResourceId',
'testAcceptParam',
- 'testFieldQuery',
- 5)
+ '(a=1,b=2)')
then: 'exception is thrown'
def exceptionThrown = thrown(NcmpException.class)
and: 'details contains the original response'
@@ -303,16 +298,14 @@ class NetworkCmProxyDataServiceImplSpec extends Specification {
mockDmiOperations.getResourceDataPassThroughRunningFromDmi('testDmiService',
'testCmHandle',
'testResourceId',
- 'testFieldQuery',
- 5,
+ '(a=1,b=2)',
'testAcceptParam',
'{"operation":"read","cmHandleProperties":{"testName":"testValue"}}') >> new ResponseEntity<>('{result-json}', HttpStatus.OK)
when: 'get resource data is called'
def response = objectUnderTest.getResourceDataPassThroughRunningForCmHandle('testCmHandle',
'testResourceId',
'testAcceptParam',
- 'testFieldQuery',
- 5)
+ '(a=1,b=2)')
then: 'get resource data returns expected response'
response == '{result-json}'
}
@@ -331,8 +324,7 @@ class NetworkCmProxyDataServiceImplSpec extends Specification {
def response = objectUnderTest.getResourceDataPassThroughRunningForCmHandle('testCmHandle',
'testResourceId',
'testAcceptParam',
- 'testFieldQuery',
- 5)
+ '(a=1,b=2)')
then: 'exception is thrown with the expected details'
def exceptionThrown = thrown(NcmpException.class)
exceptionThrown.details == 'testException'
@@ -348,8 +340,7 @@ class NetworkCmProxyDataServiceImplSpec extends Specification {
mockDmiOperations.getResourceDataPassThroughRunningFromDmi('testDmiService',
'testCmHandle',
'testResourceId',
- 'testFieldQuery',
- 5,
+ '(a=1,b=2)',
'testAcceptParam',
'{"operation":"read","cmHandleProperties":{"testName":"testValue"}}')
>> new ResponseEntity<>('NOK-json', HttpStatus.NOT_FOUND)
@@ -357,8 +348,7 @@ class NetworkCmProxyDataServiceImplSpec extends Specification {
def response = objectUnderTest.getResourceDataPassThroughRunningForCmHandle('testCmHandle',
'testResourceId',
'testAcceptParam',
- 'testFieldQuery',
- 5)
+ '(a=1,b=2)')
then: 'exception is thrown'
def exceptionThrown = thrown(NcmpException.class)
and: 'details contains the original response'
diff --git a/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/api/impl/operation/DmiOperationsSpec.groovy b/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/api/impl/operation/DmiOperationsSpec.groovy
index 6a1ce1a18b..8e0fb76a56 100644
--- a/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/api/impl/operation/DmiOperationsSpec.groovy
+++ b/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/api/impl/operation/DmiOperationsSpec.groovy
@@ -42,13 +42,12 @@ class DmiOperationsSpec extends Specification {
def 'call get resource data for pass-through:operational datastore from DMI.'() {
given: 'expected url'
def expectedUrl = 'testDmiBasePath/dmi/v1/ch/testCmhandle/data/ds' +
- '/ncmp-datastore:passthrough-operational/testResourceId?fields=testFieldsQuery&depth=10'
+ '/ncmp-datastore:passthrough-operational?resourceIdentifier=parent/child&options=(a=1,b=2)'
when: 'get resource data is called to DMI'
objectUnderTest.getResourceDataOperationalFromDmi('testDmiBasePath',
'testCmhandle',
- 'testResourceId',
- 'testFieldsQuery',
- 10,
+ 'parent/child',
+ '(a=1,b=2)',
'testAcceptJson',
'testJsonbody')
then: 'the put operation is executed with the correct URL'
@@ -57,13 +56,12 @@ class DmiOperationsSpec extends Specification {
def 'call get resource data for pass-through:running datastore from DMI.'() {
given: 'expected url'
def expectedUrl = 'testDmiBasePath/dmi/v1/ch/testCmhandle/data/ds' +
- '/ncmp-datastore:passthrough-running/testResourceId?fields=testFieldsQuery&depth=10'
+ '/ncmp-datastore:passthrough-running?resourceIdentifier=parent/child&options=(a=1,b=2)'
when: 'get resource data is called to DMI'
objectUnderTest.getResourceDataPassThroughRunningFromDmi('testDmiBasePath',
'testCmhandle',
- 'testResourceId',
- 'testFieldsQuery',
- 10,
+ 'parent/child',
+ '(a=1,b=2)',
'testAcceptJson',
'testJsonbody')
then: 'the put operation is executed with the correct URL'
@@ -72,11 +70,11 @@ class DmiOperationsSpec extends Specification {
def 'call create resource data for pass-through:running datastore from DMI.'() {
given: 'expected url'
def expectedUrl = 'testDmiBasePath/dmi/v1/ch/testCmhandle/data/ds' +
- '/ncmp-datastore:passthrough-running/testResourceId'
+ '/ncmp-datastore:passthrough-running?resourceIdentifier=parent/child'
when: 'get resource data is called to DMI'
objectUnderTest.createResourceDataPassThroughRunningFromDmi('testDmiBasePath',
'testCmhandle',
- 'testResourceId',
+ 'parent/child',
'testJsonbody')
then: 'the put operation is executed with the correct URL'
1 * mockDmiRestClient.postOperationWithJsonData(expectedUrl, 'testJsonbody', _ as HttpHeaders)
diff --git a/cps-ri/src/main/java/org/onap/cps/spi/impl/CpsDataPersistenceServiceImpl.java b/cps-ri/src/main/java/org/onap/cps/spi/impl/CpsDataPersistenceServiceImpl.java
index c57723d322..f924c70453 100644
--- a/cps-ri/src/main/java/org/onap/cps/spi/impl/CpsDataPersistenceServiceImpl.java
+++ b/cps-ri/src/main/java/org/onap/cps/spi/impl/CpsDataPersistenceServiceImpl.java
@@ -86,7 +86,7 @@ public class CpsDataPersistenceServiceImpl implements CpsDataPersistenceService
this.dataspaceRepository = dataspaceRepository;
this.anchorRepository = anchorRepository;
this.fragmentRepository = fragmentRepository;
- this.objectMapper = new ObjectMapper();
+ objectMapper = new ObjectMapper();
}
private static final Gson GSON = new GsonBuilder().create();
@@ -97,7 +97,7 @@ public class CpsDataPersistenceServiceImpl implements CpsDataPersistenceService
public void addChildDataNode(final String dataspaceName, final String anchorName, final String parentXpath,
final DataNode dataNode) {
final FragmentEntity parentFragment = getFragmentByXpath(dataspaceName, anchorName, parentXpath);
- final var fragmentEntity =
+ final FragmentEntity fragmentEntity =
toFragmentEntity(parentFragment.getDataspace(), parentFragment.getAnchor(), dataNode);
parentFragment.getChildFragments().add(fragmentEntity);
try {
@@ -131,9 +131,9 @@ public class CpsDataPersistenceServiceImpl implements CpsDataPersistenceService
@Override
public void storeDataNode(final String dataspaceName, final String anchorName, final DataNode dataNode) {
- final var dataspaceEntity = dataspaceRepository.getByName(dataspaceName);
- final var anchorEntity = anchorRepository.getByDataspaceAndName(dataspaceEntity, anchorName);
- final var fragmentEntity = convertToFragmentWithAllDescendants(dataspaceEntity, anchorEntity,
+ final DataspaceEntity dataspaceEntity = dataspaceRepository.getByName(dataspaceName);
+ final AnchorEntity anchorEntity = anchorRepository.getByDataspaceAndName(dataspaceEntity, anchorName);
+ final FragmentEntity fragmentEntity = convertToFragmentWithAllDescendants(dataspaceEntity, anchorEntity,
dataNode);
try {
fragmentRepository.save(fragmentEntity);
@@ -153,7 +153,7 @@ public class CpsDataPersistenceServiceImpl implements CpsDataPersistenceService
*/
private static FragmentEntity convertToFragmentWithAllDescendants(final DataspaceEntity dataspaceEntity,
final AnchorEntity anchorEntity, final DataNode dataNodeToBeConverted) {
- final var parentFragment = toFragmentEntity(dataspaceEntity, anchorEntity, dataNodeToBeConverted);
+ final FragmentEntity parentFragment = toFragmentEntity(dataspaceEntity, anchorEntity, dataNodeToBeConverted);
final Builder<FragmentEntity> childFragmentsImmutableSetBuilder = ImmutableSet.builder();
for (final DataNode childDataNode : dataNodeToBeConverted.getChildDataNodes()) {
final FragmentEntity childFragment =
@@ -189,14 +189,14 @@ public class CpsDataPersistenceServiceImpl implements CpsDataPersistenceService
@Override
public DataNode getDataNode(final String dataspaceName, final String anchorName, final String xpath,
final FetchDescendantsOption fetchDescendantsOption) {
- final var fragmentEntity = getFragmentByXpath(dataspaceName, anchorName, xpath);
+ final FragmentEntity fragmentEntity = getFragmentByXpath(dataspaceName, anchorName, xpath);
return toDataNode(fragmentEntity, fetchDescendantsOption);
}
private FragmentEntity getFragmentByXpath(final String dataspaceName, final String anchorName,
final String xpath) {
- final var dataspaceEntity = dataspaceRepository.getByName(dataspaceName);
- final var anchorEntity = anchorRepository.getByDataspaceAndName(dataspaceEntity, anchorName);
+ final DataspaceEntity dataspaceEntity = dataspaceRepository.getByName(dataspaceName);
+ final AnchorEntity anchorEntity = anchorRepository.getByDataspaceAndName(dataspaceEntity, anchorName);
if (isRootXpath(xpath)) {
return fragmentRepository.findFirstRootByDataspaceAndAnchor(dataspaceEntity, anchorEntity);
} else {
@@ -208,8 +208,8 @@ public class CpsDataPersistenceServiceImpl implements CpsDataPersistenceService
@Override
public List<DataNode> queryDataNodes(final String dataspaceName, final String anchorName, final String cpsPath,
final FetchDescendantsOption fetchDescendantsOption) {
- final var dataspaceEntity = dataspaceRepository.getByName(dataspaceName);
- final var anchorEntity = anchorRepository.getByDataspaceAndName(dataspaceEntity, anchorName);
+ final DataspaceEntity dataspaceEntity = dataspaceRepository.getByName(dataspaceName);
+ final AnchorEntity anchorEntity = anchorRepository.getByDataspaceAndName(dataspaceEntity, anchorName);
final CpsPathQuery cpsPathQuery;
try {
cpsPathQuery = CpsPathQuery.createFrom(cpsPath);
@@ -231,11 +231,11 @@ public class CpsDataPersistenceServiceImpl implements CpsDataPersistenceService
private static Set<String> processAncestorXpath(final List<FragmentEntity> fragmentEntities,
final CpsPathQuery cpsPathQuery) {
final Set<String> ancestorXpath = new HashSet<>();
- final var pattern =
+ final Pattern pattern =
Pattern.compile("([\\s\\S]*\\/" + Pattern.quote(cpsPathQuery.getAncestorSchemaNodeIdentifier())
+ REG_EX_FOR_OPTIONAL_LIST_INDEX + "\\/[\\s\\S]*");
for (final FragmentEntity fragmentEntity : fragmentEntities) {
- final var matcher = pattern.matcher(fragmentEntity.getXpath());
+ final Matcher matcher = pattern.matcher(fragmentEntity.getXpath());
if (matcher.matches()) {
ancestorXpath.add(matcher.group(1));
}
@@ -276,15 +276,14 @@ public class CpsDataPersistenceServiceImpl implements CpsDataPersistenceService
@Override
public void updateDataLeaves(final String dataspaceName, final String anchorName, final String xpath,
final Map<String, Object> leaves) {
- final var fragmentEntity = getFragmentByXpath(dataspaceName, anchorName, xpath);
+ final FragmentEntity fragmentEntity = getFragmentByXpath(dataspaceName, anchorName, xpath);
fragmentEntity.setAttributes(GSON.toJson(leaves));
fragmentRepository.save(fragmentEntity);
}
@Override
- public void replaceDataNodeTree(final String dataspaceName, final String anchorName,
- final DataNode dataNode) {
- final var fragmentEntity = getFragmentByXpath(dataspaceName, anchorName, dataNode.getXpath());
+ public void replaceDataNodeTree(final String dataspaceName, final String anchorName, final DataNode dataNode) {
+ final FragmentEntity fragmentEntity = getFragmentByXpath(dataspaceName, anchorName, dataNode.getXpath());
replaceDataNodeTree(fragmentEntity, dataNode);
try {
fragmentRepository.save(fragmentEntity);
@@ -296,51 +295,105 @@ public class CpsDataPersistenceServiceImpl implements CpsDataPersistenceService
}
}
- private void replaceDataNodeTree(final FragmentEntity existingFragmentEntity, final DataNode submittedDataNode) {
+ private static void replaceDataNodeTree(final FragmentEntity existingFragmentEntity,
+ final DataNode submittedDataNode) {
existingFragmentEntity.setAttributes(GSON.toJson(submittedDataNode.getLeaves()));
final Map<String, FragmentEntity> existingChildrenByXpath = existingFragmentEntity.getChildFragments()
.stream().collect(Collectors.toMap(FragmentEntity::getXpath, childFragmentEntity -> childFragmentEntity));
- final var updatedChildFragments = new HashSet<FragmentEntity>();
+ final Collection updatedChildFragments = new HashSet<FragmentEntity>();
for (final DataNode submittedChildDataNode : submittedDataNode.getChildDataNodes()) {
final FragmentEntity childFragment;
- if (existingChildrenByXpath.containsKey(submittedChildDataNode.getXpath())) {
- childFragment = existingChildrenByXpath.get(submittedChildDataNode.getXpath());
- replaceDataNodeTree(childFragment, submittedChildDataNode);
- } else {
+ if (isNewDataNode(submittedChildDataNode, existingChildrenByXpath)) {
childFragment = convertToFragmentWithAllDescendants(
existingFragmentEntity.getDataspace(), existingFragmentEntity.getAnchor(), submittedChildDataNode);
+ } else {
+ childFragment = existingChildrenByXpath.get(submittedChildDataNode.getXpath());
+ replaceDataNodeTree(childFragment, submittedChildDataNode);
}
updatedChildFragments.add(childFragment);
}
- existingFragmentEntity.setChildFragments(updatedChildFragments);
+ existingFragmentEntity.getChildFragments().clear();
+ existingFragmentEntity.getChildFragments().addAll(updatedChildFragments);
}
@Override
@Transactional
public void replaceListDataNodes(final String dataspaceName, final String anchorName, final String parentNodeXpath,
- final Collection<DataNode> dataNodes) {
- final var parentEntity = getFragmentByXpath(dataspaceName, anchorName, parentNodeXpath);
- final var firstChildNodeXpath = dataNodes.iterator().next().getXpath();
- final var listNodeXpath = firstChildNodeXpath.substring(0, firstChildNodeXpath.lastIndexOf("["));
- removeListNodeDescendants(parentEntity, listNodeXpath);
- final Set<FragmentEntity> childFragmentEntities = dataNodes.stream().map(
- dataNode -> convertToFragmentWithAllDescendants(
- parentEntity.getDataspace(), parentEntity.getAnchor(), dataNode)
- ).collect(Collectors.toUnmodifiableSet());
- parentEntity.getChildFragments().addAll(childFragmentEntities);
+ final Collection<DataNode> replacementDataNodes) {
+ final FragmentEntity parentEntity = getFragmentByXpath(dataspaceName, anchorName, parentNodeXpath);
+ final String listNodeXpathPrefix = getListNodeXpathPrefix(replacementDataNodes);
+ final Map<String, FragmentEntity> existingListElementFragmentEntitiesByXPath =
+ extractListElementFragmentEntitiesByXPath(parentEntity.getChildFragments(), listNodeXpathPrefix);
+ removeExistingListElements(parentEntity.getChildFragments(), existingListElementFragmentEntitiesByXPath);
+ final Set<FragmentEntity> updatedChildFragmentEntities = new HashSet<>();
+ for (final DataNode replacementDataNode : replacementDataNodes) {
+ final FragmentEntity existingListNodeElementEntity =
+ existingListElementFragmentEntitiesByXPath.get(replacementDataNode.getXpath());
+ final FragmentEntity entityToBeAdded = getFragmentForReplacement(parentEntity, replacementDataNode,
+ existingListNodeElementEntity);
+
+ updatedChildFragmentEntities.add(entityToBeAdded);
+ }
+ parentEntity.getChildFragments().addAll(updatedChildFragmentEntities);
fragmentRepository.save(parentEntity);
}
+ private static void removeExistingListElements(
+ final Collection<FragmentEntity> fragmentEntities,
+ final Map<String, FragmentEntity> existingListElementFragmentEntitiesByXPath) {
+ fragmentEntities.removeAll(existingListElementFragmentEntitiesByXPath.values());
+ }
+
+ private static String getListNodeXpathPrefix(final Collection<DataNode> replacementDataNodes) {
+ final String firstChildNodeXpath = replacementDataNodes.iterator().next().getXpath();
+ return firstChildNodeXpath.substring(0, firstChildNodeXpath.lastIndexOf("[") + 1);
+ }
+
+ private static FragmentEntity getFragmentForReplacement(final FragmentEntity parentEntity,
+ final DataNode replacementDataNode,
+ final FragmentEntity existingListNodeElementEntity) {
+ if (existingListNodeElementEntity == null) {
+ return convertToFragmentWithAllDescendants(
+ parentEntity.getDataspace(), parentEntity.getAnchor(), replacementDataNode);
+ }
+ if (replacementDataNode.getChildDataNodes().isEmpty()) {
+ copyAttributesFromReplacementDataNode(existingListNodeElementEntity, replacementDataNode);
+ existingListNodeElementEntity.getChildFragments().clear();
+ } else {
+ replaceDataNodeTree(existingListNodeElementEntity, replacementDataNode);
+ }
+ return existingListNodeElementEntity;
+ }
+
+ private static boolean isNewDataNode(final DataNode replacementDataNode,
+ final Map<String, FragmentEntity> existingListNodeElementsByXpath) {
+ return !existingListNodeElementsByXpath.containsKey(replacementDataNode.getXpath());
+ }
+
+ private static void copyAttributesFromReplacementDataNode(final FragmentEntity existingListNodeElementEntity,
+ final DataNode replacementDataNode) {
+ final FragmentEntity replacementFragmentEntity =
+ FragmentEntity.builder().attributes(GSON.toJson(replacementDataNode.getLeaves())).build();
+ existingListNodeElementEntity.setAttributes(replacementFragmentEntity.getAttributes());
+ }
+
+ private static Map<String, FragmentEntity> extractListElementFragmentEntitiesByXPath(
+ final Set<FragmentEntity> childEntities, final String listNodeXpathPrefix) {
+ return childEntities.stream()
+ .filter(fragmentEntity -> fragmentEntity.getXpath().startsWith(listNodeXpathPrefix))
+ .collect(Collectors.toMap(FragmentEntity::getXpath, fragmentEntity -> fragmentEntity));
+ }
+
@Override
@Transactional
public void deleteListDataNodes(final String dataspaceName, final String anchorName, final String listNodeXpath) {
- final var parentNodeXpath = listNodeXpath.substring(0, listNodeXpath.lastIndexOf('/'));
- final var parentEntity = getFragmentByXpath(dataspaceName, anchorName, parentNodeXpath);
- final var descendantNode = listNodeXpath.substring(listNodeXpath.lastIndexOf('/'));
+ final String parentNodeXpath = listNodeXpath.substring(0, listNodeXpath.lastIndexOf('/'));
+ final FragmentEntity parentEntity = getFragmentByXpath(dataspaceName, anchorName, parentNodeXpath);
+ final String descendantNode = listNodeXpath.substring(listNodeXpath.lastIndexOf('/'));
final Matcher descendantNodeHasListNodeKey = Pattern.compile(REG_EX_FOR_LIST_NODE_KEY).matcher(descendantNode);
final boolean xpathPointsToAValidChildNodeWithKey = parentEntity.getChildFragments().stream().anyMatch(
diff --git a/cps-ri/src/test/groovy/org/onap/cps/spi/impl/CpsDataPersistenceServiceIntegrationSpec.groovy b/cps-ri/src/test/groovy/org/onap/cps/spi/impl/CpsDataPersistenceServiceIntegrationSpec.groovy
index e2316e8636..144b18b535 100755
--- a/cps-ri/src/test/groovy/org/onap/cps/spi/impl/CpsDataPersistenceServiceIntegrationSpec.groovy
+++ b/cps-ri/src/test/groovy/org/onap/cps/spi/impl/CpsDataPersistenceServiceIntegrationSpec.groovy
@@ -32,10 +32,12 @@ import org.onap.cps.spi.exceptions.DataNodeNotFoundException
import org.onap.cps.spi.exceptions.DataspaceNotFoundException
import org.onap.cps.spi.model.DataNode
import org.onap.cps.spi.model.DataNodeBuilder
+import org.spockframework.util.CollectionUtil
import org.springframework.beans.factory.annotation.Autowired
import org.springframework.test.context.jdbc.Sql
import javax.validation.ConstraintViolationException
+import java.util.stream.Collectors
import static org.onap.cps.spi.FetchDescendantsOption.INCLUDE_ALL_DESCENDANTS
import static org.onap.cps.spi.FetchDescendantsOption.OMIT_DESCENDANTS
@@ -55,6 +57,8 @@ class CpsDataPersistenceServiceIntegrationSpec extends CpsPersistenceSpecBase {
static final long UPDATE_DATA_NODE_SUB_FRAGMENT_ID = 4203L
static final long LIST_DATA_NODE_PARENT201_FRAGMENT_ID = 4206L
static final long LIST_DATA_NODE_PARENT203_FRAGMENT_ID = 4214L
+ static final long LIST_DATA_NODE_PARENT204_FRAGMENT_ID = 4219L
+ static final long LIST_DATA_NODE_PARENT205_FRAGMENT_ID = 4221L
static final long LIST_DATA_NODE_CHILD202_FRAGMENT_ID = 4204L
static final long LIST_DATA_NODE_PARENT202_FRAGMENT_ID = 4211L
@@ -385,19 +389,79 @@ class CpsDataPersistenceServiceIntegrationSpec extends CpsPersistenceSpecBase {
given: 'list node data fragment as a collection of data nodes'
def listNodeCollection = buildDataNodeCollection(listNodeXpaths)
when: 'list-node elements replaced within the existing parent node'
- objectUnderTest.replaceListDataNodes(DATASPACE_NAME, ANCHOR_NAME3, '/parent-201', listNodeCollection)
+ objectUnderTest.replaceListDataNodes(DATASPACE_NAME, ANCHOR_NAME3, parentXpath, listNodeCollection)
then: 'child list elements are updated as expected, non-list element remains as is'
- def parentFragment = fragmentRepository.getById(LIST_DATA_NODE_PARENT201_FRAGMENT_ID)
+ def parentFragment = fragmentRepository.getById(listNodeFragmentID)
def allChildXpaths = parentFragment.getChildFragments().collect { it.getXpath() }
assert allChildXpaths.size() == expectedChildXpaths.size()
assert allChildXpaths.containsAll(expectedChildXpaths)
where: 'following parameters were used'
- scenario | listNodeXpaths || expectedChildXpaths
- 'existing list-node' | ['/parent-201/child-204[@key="B"]'] || ['/parent-201/child-203', '/parent-201/child-204[@key="B"]']
- 'non-existing list-node' | ['/parent-201/child-205[@key="1"]'] || ['/parent-201/child-203', '/parent-201/child-204[@key="A"]', '/parent-201/child-204[@key="X"]', '/parent-201/child-205[@key="1"]']
+ scenario | listNodeXpaths |parentXpath |listNodeFragmentID || expectedChildXpaths
+ 'existing list node with non existing key' | ['/parent-201/child-204[@key="B"]'] | '/parent-201' | LIST_DATA_NODE_PARENT201_FRAGMENT_ID || ['/parent-201/child-203', '/parent-201/child-204[@key="B"]']
+ 'non existing list node with non existing key' | ['/parent-201/child-205[@key="1"]'] | '/parent-201' | LIST_DATA_NODE_PARENT201_FRAGMENT_ID || ['/parent-201/child-203', '/parent-201/child-204[@key="A"]', '/parent-201/child-204[@key="X"]', '/parent-201/child-205[@key="1"]']
+ 'existing list node with 1 existing key' | ['/parent-201/child-204[@key="X"]'] | '/parent-201' | LIST_DATA_NODE_PARENT201_FRAGMENT_ID || ['/parent-201/child-203', '/parent-201/child-204[@key="X"]']
+ 'existing list-node with combined keys' | ['/parent-202/child-205[@key="A"]'] | '/parent-202' | LIST_DATA_NODE_PARENT202_FRAGMENT_ID || ['/parent-202/child-206[@key="A"]', '/parent-202/child-205[@key="A"]']
+ 'existing grandchild list-node' | ['/parent-200/child-202/grand-child-202[@key="E"]'] | '/parent-200/child-202' | LIST_DATA_NODE_CHILD202_FRAGMENT_ID || ['/parent-200/child-202/grand-child-202[@key="E"]']
+ 'existing list node with two list nodes' | ['/parent-201/child-204[@key="new X"]', '/parent-201/child-204[@key="Y"]'] | '/parent-201' | LIST_DATA_NODE_PARENT201_FRAGMENT_ID || ['/parent-201/child-203', '/parent-201/child-204[@key="new X"]', '/parent-201/child-204[@key="Y"]']
+ 'existing list node with compounded list node' | ['/parent-202/child-205[@key="A" and @key2="B"]'] | '/parent-202' | LIST_DATA_NODE_PARENT202_FRAGMENT_ID || ['/parent-202/child-206[@key="A"]', '/parent-202/child-205[@key="A" and @key2="B"]']
+ 'existing list node with list node with parent with key value' | ['/parent-204[@key="L"]/child-210[@key="N"]'] | '/parent-204[@key="L"]' | LIST_DATA_NODE_PARENT204_FRAGMENT_ID || ['/parent-204[@key="L"]/child-210[@key="N"]']
}
@Sql([CLEAR_DATA, SET_DATA])
+ def 'Replace list-node that has children with #scenario'() {
+ given: 'list node data fragment with child data node fragments'
+ def grandChildDataNodes = buildDataNodeCollection(grandChildXpaths)
+ def listNode = new DataNodeBuilder().withXpath(childXpath).withChildDataNodes(grandChildDataNodes).build()
+ when: 'list-node elements replaced within the existing parent node'
+ objectUnderTest.replaceListDataNodes(DATASPACE_NAME, ANCHOR_NAME3, parentXpath, [ listNode ])
+ then: 'child list elements are updated as expected with non-list elements remaining as is'
+ def parentFragment = fragmentRepository.getById(listNodeFragmentId)
+ def allChildXpaths = parentFragment.getChildFragments().collect { it.getXpath() }
+ assert allChildXpaths.size() == expectedChildXpaths.size()
+ assert allChildXpaths.containsAll(expectedChildXpaths)
+ and: 'grandchild list elements are updated as expected'
+ def allGrandChildXpaths = parentFragment.getChildFragments().collect(){
+ it.getChildFragments().collect(){
+ it.getXpath()}}
+ allGrandChildXpaths.removeIf(list -> list.isEmpty())
+ def grandChildXpathsToList = allGrandChildXpaths.stream().flatMap(List::stream).collect(Collectors.toList())
+ def expectedGrandChildXpaths = grandChildXpaths
+ assert grandChildXpathsToList.size() == expectedGrandChildXpaths.size()
+ assert grandChildXpathsToList.containsAll(expectedGrandChildXpaths)
+ where: 'the following parameters are used'
+ scenario | parentXpath | childXpath | grandChildXpaths | expectedChildXpaths | listNodeFragmentId
+ 'existing grandchild of list node' | '/parent-203' | '/parent-203/child-204[@key="X"]' | ['/parent-203/child-204/grandchild[@key="2"]'] | ['/parent-203/child-203', '/parent-203/child-204[@key="X"]'] | LIST_DATA_NODE_PARENT203_FRAGMENT_ID
+ 'existing grandchild of list node with two new nodes' | '/parent-203' | '/parent-203/child-204[@key="X"]' | ['/parent-203/child-204/grandchild[@key="2"]' , '/parent-203/child-204/grandchild[@key="3"]'] | ['/parent-203/child-203', '/parent-203/child-204[@key="X"]'] | LIST_DATA_NODE_PARENT203_FRAGMENT_ID
+ 'existing grandchild with compound list node' | '/parent-205' | '/parent-205/child-205[@key="X"]' | ['/parent-205/child-205/grand-child-206[@key="Y" and @key2="Z"]'] | ['/parent-205/child-205', '/parent-205/child-205[@key="X"]'] | LIST_DATA_NODE_PARENT205_FRAGMENT_ID
+ 'two existing list node with a new node' | '/parent-205' | '/parent-205/child-205[@key="X"]' | ['/parent-205/child-205/grandchild[@key="A"]'] | ['/parent-205/child-205', '/parent-205/child-205[@key="X"]'] | LIST_DATA_NODE_PARENT205_FRAGMENT_ID
+ }
+
+ @Sql([CLEAR_DATA, SET_DATA])
+ def 'Replace list-node content of #scenario with grandchildren.'() {
+ given: 'list node data fragment as a collection of data nodes'
+ def listNodeCollection = buildDataNodeCollection(listNodeXpaths)
+ when: 'list-node elements replaced within the existing parent node'
+ objectUnderTest.replaceListDataNodes(DATASPACE_NAME, ANCHOR_NAME3, parentXpath, listNodeCollection)
+ then: 'child list elements are updated as expected with non-list elements remaining as is'
+ def parentFragment = fragmentRepository.getById(listNodeFragmentID)
+ def allChildXpaths = parentFragment.getChildFragments().collect { it.getXpath() }
+ assert allChildXpaths.size() == expectedChildXpaths.size()
+ assert allChildXpaths.containsAll(expectedChildXpaths)
+ and: 'grandchild list elements are updated as expected'
+ def allGrandChildXpaths = parentFragment.getChildFragments().collect {
+ it.getChildFragments().collect {
+ it.getXpath()}}
+ allGrandChildXpaths.removeIf(list -> list.isEmpty())
+ assert allGrandChildXpaths.size() == expectedGrandChildXpaths.size()
+ assert allGrandChildXpaths.containsAll(expectedGrandChildXpaths)
+ where: 'following parameters were used'
+ scenario | listNodeXpaths | parentXpath | listNodeFragmentID || expectedChildXpaths | expectedGrandChildXpaths
+ 'existing list node with existing keys' | ['/parent-203/child-204[@key="X"]'] | '/parent-203' | LIST_DATA_NODE_PARENT203_FRAGMENT_ID || ['/parent-203/child-203', '/parent-203/child-204[@key="X"]'] | []
+ 'non existing list node with existing keys' | ['/parent-203/child-204[@key="V"]'] | '/parent-203' | LIST_DATA_NODE_PARENT203_FRAGMENT_ID || ['/parent-203/child-203', '/parent-203/child-204[@key="V"]'] | []
+ }
+
+
+ @Sql([CLEAR_DATA, SET_DATA])
def 'Replace list-node fragment error scenario: #scenario.'() {
given: 'list node data fragment as a collection of data nodes'
def listNodeCollection = buildDataNodeCollection(listNodeXpaths)
diff --git a/cps-ri/src/test/resources/data/fragment.sql b/cps-ri/src/test/resources/data/fragment.sql
index 886e6e1751..f6e887e017 100755
--- a/cps-ri/src/test/resources/data/fragment.sql
+++ b/cps-ri/src/test/resources/data/fragment.sql
@@ -61,4 +61,11 @@ INSERT INTO FRAGMENT (ID, DATASPACE_ID, ANCHOR_ID, PARENT_ID, XPATH, ATTRIBUTES)
(4215, 1001, 3003, 4214, '/parent-203/child-203', '{}'),
(4216, 1001, 3003, 4214, '/parent-203/child-204[@key="A"]', '{"key": "A"}'),
(4217, 1001, 3003, 4214, '/parent-203/child-204[@key="X"]', '{"key": "X"}'),
- (4218, 1001, 3003, 4217, '/parent-203/child-204[@key="X"]/grand-child-204[@key2="Y"]', '{"key": "X", "key2": "Y"}'); \ No newline at end of file
+ (4218, 1001, 3003, 4217, '/parent-203/child-204[@key="X"]/grand-child-204[@key2="Y"]', '{"key": "X", "key2": "Y"}'),
+ (4219, 1001, 3003, null, '/parent-204[@key="L"]', '{"key": "L"}'),
+ (4220, 1001, 3003, 4219, '/parent-204[@key="L"]/child-210[@key="M"]', '{"key": "M"}'),
+ (4221, 1001, 3003, null, '/parent-205', '{"leaf-value": "original"}'),
+ (4222, 1001, 3003, 4221, '/parent-205/child-205', '{}'),
+ (4223, 1001, 3003, 4221, '/parent-205/child-205[@key="X"]', '{"key": "X"}'),
+ (4224, 1001, 3003, 4223, '/parent-205/child-205[@key="X"]/grand-child-206[@key="Y"]', '{"key": "Y", "key2": "Z"}'),
+ (4225, 1001, 3003, 4223, '/parent-205/child-205[@key="X"]/grand-child-206[@key="Y" and @key2="Z"]', '{"key": "Y", "key2": "Z"}'); \ No newline at end of file
diff --git a/docker-compose/README.md b/docker-compose/README.md
index ae26868224..3e6ab8367e 100644
--- a/docker-compose/README.md
+++ b/docker-compose/README.md
@@ -106,6 +106,7 @@ Swagger UI and Open API specifications are available to discover service endpoin
* `http://localhost:<port-number>/swagger-ui.html`
* `http://localhost:<port-number>/api-docs/cps-core/openapi.yaml`
* `http://localhost:<port-number>/api-docs/cps-ncmp/openapi.yaml`
+* `http://localhost:<port-number>/api-docs/cps-ncmp/openapi-inventory.yaml`
with <port-number> being either `8080` if running the plain Java build or retrieved using following command
if running from `docker-compose`:
diff --git a/docs/index.rst b/docs/index.rst
index 3e6f1414b7..ec9753ff00 100755
--- a/docs/index.rst
+++ b/docs/index.rst
@@ -22,7 +22,6 @@ CPS-Core Documentation
design.rst
modeling.rst
deployment.rst
- cps-path.rst
release-notes.rst
ONAP DMI Plugin Documentation
diff --git a/docs/modeling.rst b/docs/modeling.rst
index 4e46e71b7a..22c4b0ba32 100644
--- a/docs/modeling.rst
+++ b/docs/modeling.rst
@@ -50,4 +50,13 @@ Data
Querying
-- **CPS Path** is used to query data nodes. The CPS Path is described in detail in :doc:`cps-path`.
+- **CPS Path** is used to query data nodes. The CPS Path is described in detail in the :doc:`cps-path` sub-page.
+
+CPS Path
+========
+
+.. toctree::
+ :maxdepth: 1
+
+ cps-path.rst
+
diff --git a/docs/requirements-docs.txt b/docs/requirements-docs.txt
index c7912888ea..5a3d2f17f4 100644
--- a/docs/requirements-docs.txt
+++ b/docs/requirements-docs.txt
@@ -1,16 +1 @@
-tox
-Sphinx
-doc8
-docutils
-setuptools
-six
-sphinx_bootstrap_theme
-lfdocs-conf~=0.7.5
-funcparserlib~=1.0.0a0
-sphinxcontrib.blockdiag~=2.0.0
-sphinxcontrib-needs<0.6.0
-sphinxcontrib.plantuml~=0.21
-sphinxcontrib.nwdiag~=2.0.0
-sphinxcontrib-seqdiag~=2.0.0
-sphinxcontrib-swaggerdoc~=0.1.7
-sphinx-rtd-theme~=1.0.0
+lfdocs-conf \ No newline at end of file
diff --git a/docs/tox.ini b/docs/tox.ini
index edac8c35fc..4e9449e241 100644
--- a/docs/tox.ini
+++ b/docs/tox.ini
@@ -6,6 +6,8 @@ skipsdist = true
[testenv:docs]
basepython = python3
deps = -r{toxinidir}/requirements-docs.txt
+ -chttps://git.onap.org/doc/plain/etc/upper-constraints.os.txt
+ -chttps://git.onap.org/doc/plain/etc/upper-constraints.onap.txt
commands =
sphinx-build -b html -n -d {envtmpdir}/doctrees ./ {toxinidir}/_build/html
echo "Generated docs available in {toxinidir}/_build/html"
@@ -16,7 +18,9 @@ whitelist_externals =
[testenv:docs-linkcheck]
basepython = python3
-#deps = -r{toxinidir}/requirements-docs.txt
+deps = -r{toxinidir}/requirements-docs.txt
+ -chttps://git.onap.org/doc/plain/etc/upper-constraints.os.txt
+ -chttps://git.onap.org/doc/plain/etc/upper-constraints.onap.txt
commands = echo "Link Checking not enforced"
#commands = sphinx-build -b linkcheck -d {envtmpdir}/doctrees ./ {toxinidir}/_build/linkcheck
whitelist_externals = echo