Merge "Xpath to NodeId invalid"
diff --git a/INFO.yaml b/INFO.yaml
index a411e37..ca4f250 100755
--- a/INFO.yaml
+++ b/INFO.yaml
@@ -52,11 +52,6 @@
company: 'Bell Canada'
id: 'puthuparambil.aditya'
timezone: 'Europe/Dublin'
- - name: 'Renu Kumari'
- email: 'renu.kumari@bell.ca'
- company: 'Bell Canada'
- id: 'renukumari'
- timezone: 'America/Toronto'
- name: 'Joseph Keenan'
email: 'joseph.keenan@est.tech'
company: 'Ericsson Software Technology'
diff --git a/cps-application/src/main/resources/application.yml b/cps-application/src/main/resources/application.yml
index 9b6f41e..f7a06c5 100644
--- a/cps-application/src/main/resources/application.yml
+++ b/cps-application/src/main/resources/application.yml
@@ -45,13 +45,12 @@
username: ${DB_USERNAME}
password: ${DB_PASSWORD}
driverClassName: org.postgresql.Driver
- initialization-mode: always
hikari:
minimumIdle: 5
maximumPoolSize: 80
- idleTimeout: 120000
- connectionTimeout: 300000
- leakDetectionThreshold: 300000
+ idleTimeout: 60000
+ connectionTimeout: 120000
+ leakDetectionThreshold: 30000
pool-name: CpsDatabasePool
cache:
@@ -91,6 +90,9 @@
default-property-inclusion: NON_NULL
serialization:
FAIL_ON_EMPTY_BEANS: false
+ sql:
+ init:
+ mode: ALWAYS
app:
ncmp:
async-m2m:
diff --git a/cps-ncmp-rest-stub/src/main/java/org/onap/cps/ncmp/rest/stub/controller/NetworkCmProxyStubController.java b/cps-ncmp-rest-stub/src/main/java/org/onap/cps/ncmp/rest/stub/controller/NetworkCmProxyStubController.java
index 67af6f1..bb919b5 100644
--- a/cps-ncmp-rest-stub/src/main/java/org/onap/cps/ncmp/rest/stub/controller/NetworkCmProxyStubController.java
+++ b/cps-ncmp-rest-stub/src/main/java/org/onap/cps/ncmp/rest/stub/controller/NetworkCmProxyStubController.java
@@ -30,10 +30,9 @@
import java.util.List;
import java.util.Map;
import java.util.UUID;
-import javax.validation.Valid;
-import javax.validation.constraints.NotNull;
import lombok.extern.slf4j.Slf4j;
import org.onap.cps.ncmp.rest.api.NetworkCmProxyApi;
+import org.onap.cps.ncmp.rest.controller.handlers.DatastoreType;
import org.onap.cps.ncmp.rest.model.CmHandleQueryParameters;
import org.onap.cps.ncmp.rest.model.RestModuleDefinition;
import org.onap.cps.ncmp.rest.model.RestModuleReference;
@@ -58,20 +57,55 @@
private String pathToResponseFiles;
@Override
- public ResponseEntity<Void> createResourceDataRunningForCmHandle(@NotNull @Valid final String resourceIdentifier,
- final String cmHandleId, @Valid final Object body, final String contentType) {
+ public ResponseEntity<Object> getResourceDataForCmHandle(final String dataStoreName,
+ final String cmHandle,
+ final String resourceIdentifier,
+ final String optionsParamInQuery,
+ final String topicParamInQuery,
+ final Boolean includeDescendants) {
+ if (DatastoreType.PASSTHROUGH_OPERATIONAL == DatastoreType.fromDatastoreName(dataStoreName)) {
+ final ResponseEntity<Map<String, Object>> asyncResponse = populateAsyncResponse(topicParamInQuery);
+ final Map<String, Object> asyncResponseData = asyncResponse.getBody();
+ Object responseObject = null;
+ // read JSON file and map/convert to java POJO
+ final ClassPathResource resource =
+ new ClassPathResource(pathToResponseFiles + "passthrough-operational-example.json");
+ try (InputStream inputStream = resource.getInputStream()) {
+ final String string = new String(inputStream.readAllBytes(), StandardCharsets.UTF_8);
+ final ObjectMapper mapper = new ObjectMapper();
+ responseObject = mapper.readValue(string, Object.class);
+ } catch (final IOException exception) {
+ log.error("Error reading the file.", exception);
+ return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).build();
+ }
+ if (asyncResponseData == null) {
+ return ResponseEntity.ok(responseObject);
+ }
+ return ResponseEntity.ok(asyncResponse);
+ }
+ return new ResponseEntity<>(HttpStatus.NOT_IMPLEMENTED);
+ }
+
+ @Override
+ public ResponseEntity<Void> createResourceDataRunningForCmHandle(final String datastoreName,
+ final String resourceIdentifier,
+ final String cmHandleId,
+ final Object body,
+ final String contentType) {
return new ResponseEntity<>(HttpStatus.CREATED);
}
@Override
- public ResponseEntity<Void> deleteResourceDataRunningForCmHandle(final String cmHandleId,
- @NotNull @Valid final String resourceIdentifier, final String contentType) {
+ public ResponseEntity<Void> deleteResourceDataRunningForCmHandle(final String datastoreName,
+ final String cmHandleId,
+ final String resourceIdentifier,
+ final String contentType) {
return new ResponseEntity<>(HttpStatus.NO_CONTENT);
}
@Override
public ResponseEntity<List<RestOutputCmHandle>> searchCmHandles(
- final CmHandleQueryParameters cmHandleQueryParameters) {
+ final CmHandleQueryParameters cmHandleQueryParameters) {
List<RestOutputCmHandle> restOutputCmHandles = null;
// read JSON file and map/convert to java POJO
final ClassPathResource resource = new ClassPathResource(pathToResponseFiles + "cmHandlesSearch.json");
@@ -88,19 +122,19 @@
@Override
public ResponseEntity<Object> setDataSyncEnabledFlagForCmHandle(final String cmHandleId,
- final Boolean dataSyncEnabled) {
+ final Boolean dataSyncEnabled) {
return new ResponseEntity<>(HttpStatus.NOT_IMPLEMENTED);
}
@Override
public ResponseEntity<List<String>> searchCmHandleIds(
- final CmHandleQueryParameters cmHandleQueryParameters) {
+ final CmHandleQueryParameters cmHandleQueryParameters) {
return new ResponseEntity<>(HttpStatus.NOT_IMPLEMENTED);
}
@Override
public ResponseEntity<RestOutputCmHandlePublicProperties> getCmHandlePublicPropertiesByCmHandleId(
- final String cmHandleId) {
+ final String cmHandleId) {
return new ResponseEntity<>(HttpStatus.NOT_IMPLEMENTED);
}
@@ -119,48 +153,12 @@
return new ResponseEntity<>(HttpStatus.NOT_IMPLEMENTED);
}
- /**
- * Get resource data from operational datastore.
- *
- * @param cmHandleId cm handle identifier
- * @param resourceIdentifier resource identifier
- * @param optionsParamInQuery options query parameter
- * @param topicParamInQuery topic query parameter
- * @return {@code ResponseEntity} response from dmi plugin
- */
@Override
- public ResponseEntity<Object> getResourceDataOperationalForCmHandle(final String cmHandleId,
- final String resourceIdentifier, final String optionsParamInQuery, final String topicParamInQuery) {
- final ResponseEntity<Map<String, Object>> asyncResponse = populateAsyncResponse(topicParamInQuery);
- final Map<String, Object> asyncResponseData = asyncResponse.getBody();
- Object responseObject = null;
- // read JSON file and map/convert to java POJO
- final ClassPathResource resource = new ClassPathResource(pathToResponseFiles
- + "passthrough-operational-example.json");
- try (InputStream inputStream = resource.getInputStream()) {
- final String string = new String(inputStream.readAllBytes(), StandardCharsets.UTF_8);
- final ObjectMapper mapper = new ObjectMapper();
- responseObject = mapper.readValue(string, Object.class);
- } catch (final IOException exception) {
- log.error("Error reading the file.", exception);
- return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).build();
- }
- if (asyncResponseData == null) {
- return ResponseEntity.ok(responseObject);
- }
- return ResponseEntity.ok(asyncResponse);
-
- }
-
- @Override
- public ResponseEntity<Object> getResourceDataRunningForCmHandle(final String cmHandleId,
- final String resourceIdentifier, final String options, final String topic) {
- return new ResponseEntity<>(HttpStatus.NOT_IMPLEMENTED);
- }
-
- @Override
- public ResponseEntity<Object> patchResourceDataRunningForCmHandle(final String resourceIdentifier,
- final String cmHandleId, final Object body, final String contentType) {
+ public ResponseEntity<Object> patchResourceDataRunningForCmHandle(final String datastoreName,
+ final String resourceIdentifier,
+ final String cmHandleId,
+ final Object body,
+ final String contentType) {
return new ResponseEntity<>(HttpStatus.NOT_IMPLEMENTED);
}
@@ -170,8 +168,11 @@
}
@Override
- public ResponseEntity<Object> updateResourceDataRunningForCmHandle(@NotNull @Valid final String resourceIdentifier,
- final String cmHandleId, @Valid final Object body, final String contentType) {
+ public ResponseEntity<Object> updateResourceDataRunningForCmHandle(final String datastoreName,
+ final String resourceIdentifier,
+ final String cmHandleId,
+ final Object body,
+ final String contentType) {
return new ResponseEntity<>(HttpStatus.NOT_IMPLEMENTED);
}
diff --git a/cps-ncmp-rest/docs/openapi/components.yaml b/cps-ncmp-rest/docs/openapi/components.yaml
index 427f083..7ca09ce 100644
--- a/cps-ncmp-rest/docs/openapi/components.yaml
+++ b/cps-ncmp-rest/docs/openapi/components.yaml
@@ -86,7 +86,7 @@
type: array
items:
type: string
- example: [my-cm-handle1, my-cm-handle2, my-cm-handle3]
+ example: [ my-cm-handle1, my-cm-handle2, my-cm-handle3 ]
DmiPluginRegistrationErrorResponse:
type: object
properties:
@@ -124,14 +124,14 @@
type: string
example: my-cm-handle
cmHandleProperties:
- $ref: '#/components/schemas/RestCmHandleProperties'
+ $ref: '#/components/schemas/RestCmHandleProperties'
publicCmHandleProperties:
- $ref: '#/components/schemas/RestCmHandleProperties'
+ $ref: '#/components/schemas/RestCmHandleProperties'
RestCmHandleProperties:
- type: object
- additionalProperties:
- type: string
- example: my-property
+ type: object
+ additionalProperties:
+ type: string
+ example: my-property
#Response Schemas
RestModuleReference:
@@ -288,21 +288,21 @@
examples:
dataSampleRequest:
- summary: Sample request
- description: Sample request body
- value:
- test:bookstore:
- bookstore-name: Chapters
- categories:
- - code: '01'
- name: SciFi
- books:
+ summary: Sample request
+ description: Sample request body
+ value:
+ test:bookstore:
+ bookstore-name: Chapters
+ categories:
+ - code: '01'
+ name: SciFi
+ books:
- authors:
- Iain M. Banks
- Ursula K. Le Guin
- - code: '02'
- name: kids
- books:
+ - code: '02'
+ name: kids
+ books:
- authors:
- Philip Pullman
@@ -351,22 +351,22 @@
- Philip Pullman
dataSampleResponse:
- summary: Sample response
- description: Sample response for selecting 'sample 1'.
- value:
- bookstore:
- categories:
- - code: '01'
- books:
- - authors:
- - Iain M. Banks
- - Ursula K. Le Guin
- name: SciFi
- - code: '02'
- books:
- - authors:
- - Philip Pullman
- name: kids
+ summary: Sample response
+ description: Sample response for selecting 'sample 1'.
+ value:
+ bookstore:
+ categories:
+ - code: '01'
+ books:
+ - authors:
+ - Iain M. Banks
+ - Ursula K. Le Guin
+ name: SciFi
+ - code: '02'
+ books:
+ - authors:
+ - Philip Pullman
+ name: kids
allCmHandleQueryParameters:
value:
@@ -448,7 +448,7 @@
includeDescendantsOptionInQuery:
name: include-descendants
in: query
- description: include-descendants
+ description: Determines if descendants are included in response
required: false
schema:
type: boolean
@@ -526,6 +526,14 @@
type: string
default: application/json
example: application/yang-data+json
+ datastoreName:
+ name: ncmp-datastore-name
+ in: path
+ description: The type of the requested data
+ required: true
+ schema:
+ type: string
+ example: ncmp-datastore:operational
responses:
NotFound:
@@ -555,9 +563,9 @@
schema:
$ref: '#/components/schemas/ErrorMessage'
example:
- status: 403
- message: Forbidden error message
- details: Forbidden error details
+ status: 403
+ message: Forbidden error message
+ details: Forbidden error details
BadRequest:
description: Bad Request
content:
@@ -565,9 +573,9 @@
schema:
$ref: '#/components/schemas/ErrorMessage'
example:
- status: 400 BAD_REQUEST
- message: Bad request error message
- details: Bad request error details
+ status: 400 BAD_REQUEST
+ message: Bad request error message
+ details: Bad request error details
Conflict:
description: Conflict
content:
@@ -575,9 +583,9 @@
schema:
$ref: '#/components/schemas/ErrorMessage'
example:
- status: 409 CONFLICT
- message: Conflict error message
- details: Conflict error details
+ status: 409 CONFLICT
+ message: Conflict error message
+ details: Conflict error details
NotImplemented:
description: The given path has not been implemented
content:
@@ -585,9 +593,9 @@
schema:
$ref: '#/components/schemas/ErrorMessage'
example:
- status: 501
- message: Not implemented error message
- details: Not implemented error details
+ status: 501
+ message: Not implemented error message
+ details: Not implemented error details
Ok:
description: OK
content:
@@ -596,10 +604,10 @@
type: object
Created:
description: Created
- content: {}
+ content: { }
NoContent:
description: No Content
- content: {}
+ content: { }
InternalServerError:
description: Internal Server Error
content:
diff --git a/cps-ncmp-rest/docs/openapi/ncmp.yml b/cps-ncmp-rest/docs/openapi/ncmp.yml
index 4266fc4..38db26f 100755
--- a/cps-ncmp-rest/docs/openapi/ncmp.yml
+++ b/cps-ncmp-rest/docs/openapi/ncmp.yml
@@ -17,18 +17,21 @@
#
# SPDX-License-Identifier: Apache-2.0
# ============LICENSE_END=========================================================
-getResourceDataForPassthroughOperational:
+
+resourceDataForCmHandle:
get:
tags:
- network-cm-proxy
- summary: Get resource data from pass-through operational for cm handle
- description: Get resource data from pass-through operational for given cm handle
- operationId: getResourceDataOperationalForCmHandle
+ summary: Get resource data for cm handle
+ description: Get resource data for given cm handle
+ operationId: getResourceDataForCmHandle
parameters:
+ - $ref: 'components.yaml#/components/parameters/datastoreName'
- $ref: 'components.yaml#/components/parameters/cmHandleInPath'
- $ref: 'components.yaml#/components/parameters/resourceIdentifierInQuery'
- $ref: 'components.yaml#/components/parameters/optionsParamInQuery'
- $ref: 'components.yaml#/components/parameters/topicParamInQuery'
+ - $ref: 'components.yaml#/components/parameters/includeDescendantsOptionInQuery'
responses:
200:
description: OK
@@ -50,38 +53,6 @@
502:
$ref: 'components.yaml#/components/responses/BadGateway'
-resourceDataForPassthroughRunning:
- get:
- tags:
- - network-cm-proxy
- summary: Get resource data from pass-through running for cm handle
- description: Get resource data from pass-through running for given cm handle
- operationId: getResourceDataRunningForCmHandle
- parameters:
- - $ref: 'components.yaml#/components/parameters/cmHandleInPath'
- - $ref: 'components.yaml#/components/parameters/resourceIdentifierInQuery'
- - $ref: 'components.yaml#/components/parameters/optionsParamInQuery'
- - $ref: 'components.yaml#/components/parameters/topicParamInQuery'
- responses:
- 200:
- description: OK
- content:
- application/json:
- schema:
- type: object
- examples:
- dataSampleResponse:
- $ref: 'components.yaml#/components/examples/dataSampleResponse'
- 400:
- $ref: 'components.yaml#/components/responses/BadRequest'
- 401:
- $ref: 'components.yaml#/components/responses/Unauthorized'
- 403:
- $ref: 'components.yaml#/components/responses/Forbidden'
- 500:
- $ref: 'components.yaml#/components/responses/InternalServerError'
- 502:
- $ref: 'components.yaml#/components/responses/BadGateway'
post:
tags:
- network-cm-proxy
@@ -89,6 +60,7 @@
description: create resource data from pass-through running for given cm handle
operationId: createResourceDataRunningForCmHandle
parameters:
+ - $ref: 'components.yaml#/components/parameters/datastoreName'
- $ref: 'components.yaml#/components/parameters/cmHandleInPath'
- $ref: 'components.yaml#/components/parameters/resourceIdentifierInQuery'
- $ref: 'components.yaml#/components/parameters/contentParamInHeader'
@@ -128,6 +100,7 @@
description: Update resource data from pass-through running for the given cm handle
operationId: updateResourceDataRunningForCmHandle
parameters:
+ - $ref: 'components.yaml#/components/parameters/datastoreName'
- $ref: 'components.yaml#/components/parameters/cmHandleInPath'
- $ref: 'components.yaml#/components/parameters/resourceIdentifierInQuery'
- $ref: 'components.yaml#/components/parameters/contentParamInHeader'
@@ -167,6 +140,7 @@
description: Patch resource data from pass-through running for the given cm handle
operationId: patchResourceDataRunningForCmHandle
parameters:
+ - $ref: 'components.yaml#/components/parameters/datastoreName'
- $ref: 'components.yaml#/components/parameters/cmHandleInPath'
- $ref: 'components.yaml#/components/parameters/resourceIdentifierInQuery'
- $ref: 'components.yaml#/components/parameters/contentParamInHeader'
@@ -200,6 +174,7 @@
description: Delete resource data from pass-through running for a given cm handle
operationId: deleteResourceDataRunningForCmHandle
parameters:
+ - $ref: 'components.yaml#/components/parameters/datastoreName'
- $ref: 'components.yaml#/components/parameters/cmHandleInPath'
- $ref: 'components.yaml#/components/parameters/resourceIdentifierInQuery'
- $ref: 'components.yaml#/components/parameters/contentParamInHeader'
diff --git a/cps-ncmp-rest/docs/openapi/openapi.yml b/cps-ncmp-rest/docs/openapi/openapi.yml
index 8e02066..4c546be 100755
--- a/cps-ncmp-rest/docs/openapi/openapi.yml
+++ b/cps-ncmp-rest/docs/openapi/openapi.yml
@@ -26,11 +26,8 @@
servers:
- url: /ncmp
paths:
- /v1/ch/{cm-handle}/data/ds/ncmp-datastore:passthrough-operational:
- $ref: 'ncmp.yml#/getResourceDataForPassthroughOperational'
-
- /v1/ch/{cm-handle}/data/ds/ncmp-datastore:passthrough-running:
- $ref: 'ncmp.yml#/resourceDataForPassthroughRunning'
+ /v1/ch/{cm-handle}/data/ds/{ncmp-datastore-name}:
+ $ref: 'ncmp.yml#/resourceDataForCmHandle'
/v1/ch/{cm-handle}/modules:
$ref: 'ncmp.yml#/fetchModuleReferencesByCmHandle'
diff --git a/cps-ncmp-rest/pom.xml b/cps-ncmp-rest/pom.xml
index 6a700c3..b3021d2 100644
--- a/cps-ncmp-rest/pom.xml
+++ b/cps-ncmp-rest/pom.xml
@@ -185,7 +185,8 @@
<goal>copy-resources</goal>
</goals>
<configuration>
- <outputDirectory>${project.basedir}/target/classes/static/api-docs/cps-ncmp</outputDirectory>
+ <outputDirectory>${project.basedir}/target/classes/static/api-docs/cps-ncmp
+ </outputDirectory>
<resources>
<resource>
<directory>${project.basedir}/target/generated-sources/swagger/</directory>
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 d2ed393..2f6668a 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
@@ -29,21 +29,19 @@
import static org.onap.cps.ncmp.api.impl.operations.DmiRequestBody.OperationEnum.UPDATE;
import java.util.List;
-import java.util.Map;
import java.util.Set;
-import java.util.UUID;
import java.util.stream.Collectors;
-import javax.validation.Valid;
-import javax.validation.constraints.NotNull;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.onap.cps.ncmp.api.NetworkCmProxyDataService;
-import org.onap.cps.ncmp.api.impl.exception.InvalidTopicException;
import org.onap.cps.ncmp.api.inventory.CompositeState;
import org.onap.cps.ncmp.api.models.CmHandleQueryApiParameters;
import org.onap.cps.ncmp.api.models.NcmpServiceCmHandle;
import org.onap.cps.ncmp.rest.api.NetworkCmProxyApi;
-import org.onap.cps.ncmp.rest.executor.CpsNcmpTaskExecutor;
+import org.onap.cps.ncmp.rest.controller.handlers.DatastoreType;
+import org.onap.cps.ncmp.rest.controller.handlers.NcmpDatastoreResourceRequestHandler;
+import org.onap.cps.ncmp.rest.controller.handlers.NcmpDatastoreResourceRequestHandlerFactory;
+import org.onap.cps.ncmp.rest.exceptions.InvalidDatastoreException;
import org.onap.cps.ncmp.rest.mapper.CmHandleStateMapper;
import org.onap.cps.ncmp.rest.model.CmHandlePublicProperties;
import org.onap.cps.ncmp.rest.model.CmHandleQueryParameters;
@@ -53,9 +51,7 @@
import org.onap.cps.ncmp.rest.model.RestOutputCmHandleCompositeState;
import org.onap.cps.ncmp.rest.model.RestOutputCmHandlePublicProperties;
import org.onap.cps.ncmp.rest.util.DeprecationHelper;
-import org.onap.cps.utils.CpsValidator;
import org.onap.cps.utils.JsonObjectMapper;
-import org.springframework.beans.factory.annotation.Value;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.RequestMapping;
@@ -68,94 +64,65 @@
public class NetworkCmProxyController implements NetworkCmProxyApi {
private static final String NO_BODY = null;
- private static final String NO_REQUEST_ID = null;
- private static final String NO_TOPIC = null;
private final NetworkCmProxyDataService networkCmProxyDataService;
private final JsonObjectMapper jsonObjectMapper;
-
private final DeprecationHelper deprecationHelper;
private final NcmpRestInputMapper ncmpRestInputMapper;
private final CmHandleStateMapper cmHandleStateMapper;
- private final CpsNcmpTaskExecutor cpsNcmpTaskExecutor;
- @Value("${notification.async.executor.time-out-value-in-ms:2000}")
- private int timeOutInMilliSeconds;
- @Value("${notification.enabled:true}")
- private boolean asyncEnabled;
+ private final NcmpDatastoreResourceRequestHandlerFactory ncmpDatastoreResourceRequestHandlerFactory;
/**
- * Get resource data from operational datastore.
+ * Get resource data from datastore.
*
- * @param cmHandle cm handle identifier
- * @param resourceIdentifier resource identifier
+ * @param datastoreName name of the datastore
+ * @param cmHandle cm handle identifier
+ * @param resourceIdentifier resource identifier
* @param optionsParamInQuery options query parameter
- * @param topicParamInQuery topic query parameter
+ * @param topicParamInQuery topic query parameter
+ * @param includeDescendants whether include descendants
* @return {@code ResponseEntity} response from dmi plugin
*/
+
@Override
- public ResponseEntity<Object> getResourceDataOperationalForCmHandle(final String cmHandle,
- final @NotNull @Valid String resourceIdentifier,
- final @Valid String optionsParamInQuery,
- final @Valid String topicParamInQuery) {
- if (asyncEnabled && isValidTopic(topicParamInQuery)) {
- final String requestId = UUID.randomUUID().toString();
- log.info("Received Async passthrough-operational request with id {}", requestId);
- cpsNcmpTaskExecutor.executeTask(() ->
- networkCmProxyDataService.getResourceDataOperationalForCmHandle(
- cmHandle, resourceIdentifier, optionsParamInQuery, topicParamInQuery, requestId
- ), timeOutInMilliSeconds
- );
- return ResponseEntity.ok(Map.of("requestId", requestId));
- } else {
- log.warn("Asynchronous messaging is currently disabled for passthrough-operational."
- + " Will use synchronous operation.");
- }
+ public ResponseEntity<Object> getResourceDataForCmHandle(final String datastoreName,
+ final String cmHandle,
+ final String resourceIdentifier,
+ final String optionsParamInQuery,
+ final String topicParamInQuery,
+ final Boolean includeDescendants) {
- final Object responseObject = networkCmProxyDataService.getResourceDataOperationalForCmHandle(
- cmHandle, resourceIdentifier, optionsParamInQuery, NO_TOPIC, NO_REQUEST_ID);
+ final NcmpDatastoreResourceRequestHandler ncmpDatastoreResourceRequestHandler =
+ ncmpDatastoreResourceRequestHandlerFactory.getNcmpDatastoreResourceRequestHandler(
+ DatastoreType.fromDatastoreName(datastoreName));
- return ResponseEntity.ok(responseObject);
+ return ncmpDatastoreResourceRequestHandler.getResourceData(cmHandle, resourceIdentifier,
+ optionsParamInQuery, topicParamInQuery, includeDescendants);
}
/**
- * Get resource data from pass-through running datastore.
+ * Patch resource data from passthrough-running.
*
- * @param cmHandle cm handle identifier
* @param resourceIdentifier resource identifier
- * @param optionsParamInQuery options query parameter
- * @param topicParamInQuery topic query parameter
+ * @param datastoreName name of the datastore
+ * @param cmHandle cm handle identifier
+ * @param requestBody the request body
+ * @param contentType content type of body
* @return {@code ResponseEntity} response from dmi plugin
*/
- @Override
- public ResponseEntity<Object> getResourceDataRunningForCmHandle(final String cmHandle,
- final @NotNull @Valid String resourceIdentifier,
- final @Valid String optionsParamInQuery,
- final @Valid String topicParamInQuery) {
- if (asyncEnabled && isValidTopic(topicParamInQuery)) {
- final String requestId = UUID.randomUUID().toString();
- log.info("Received Async passthrough-running request with id {}", requestId);
- cpsNcmpTaskExecutor.executeTask(() ->
- networkCmProxyDataService.getResourceDataPassThroughRunningForCmHandle(
- cmHandle, resourceIdentifier, optionsParamInQuery, topicParamInQuery, requestId
- ), timeOutInMilliSeconds
- );
- return ResponseEntity.ok(Map.of("requestId", requestId));
- } else {
- log.warn("Asynchronous messaging is currently disabled for passthrough-running."
- + " Will use synchronous operation.");
- }
-
- final Object responseObject = networkCmProxyDataService.getResourceDataPassThroughRunningForCmHandle(
- cmHandle, resourceIdentifier, optionsParamInQuery, NO_TOPIC, NO_REQUEST_ID);
-
- return ResponseEntity.ok(responseObject);
- }
@Override
public ResponseEntity<Object> patchResourceDataRunningForCmHandle(final String resourceIdentifier,
- final String cmHandle,
- final Object requestBody, final String contentType) {
- final Object responseObject = networkCmProxyDataService.writeResourceDataPassThroughRunningForCmHandle(cmHandle,
- resourceIdentifier, PATCH, jsonObjectMapper.asJsonString(requestBody), contentType);
+ final String datastoreName,
+ final String cmHandle,
+ final Object requestBody,
+ final String contentType) {
+
+ acceptPassthroughRunningOnly(datastoreName);
+
+ final Object responseObject = networkCmProxyDataService
+ .writeResourceDataPassThroughRunningForCmHandle(
+ cmHandle, resourceIdentifier, PATCH,
+ jsonObjectMapper.asJsonString(requestBody), contentType);
return ResponseEntity.ok(responseObject);
}
@@ -163,14 +130,21 @@
* Create resource data in datastore pass-through running for given cm-handle.
*
* @param resourceIdentifier resource identifier
- * @param cmHandle cm handle identifier
- * @param requestBody the request body
- * @param contentType content type of body
+ * @param datastoreName name of the datastore
+ * @param cmHandle cm handle identifier
+ * @param requestBody the request body
+ * @param contentType content type of body
* @return {@code ResponseEntity} response from dmi plugin
*/
@Override
public ResponseEntity<Void> createResourceDataRunningForCmHandle(final String resourceIdentifier,
- final String cmHandle, final Object requestBody, final String contentType) {
+ final String datastoreName,
+ final String cmHandle,
+ final Object requestBody,
+ final String contentType) {
+
+ acceptPassthroughRunningOnly(datastoreName);
+
networkCmProxyDataService.writeResourceDataPassThroughRunningForCmHandle(cmHandle,
resourceIdentifier, CREATE, jsonObjectMapper.asJsonString(requestBody), contentType);
return new ResponseEntity<>(HttpStatus.CREATED);
@@ -180,36 +154,45 @@
* Update resource data in datastore pass-through running for given cm-handle.
*
* @param resourceIdentifier resource identifier
- * @param cmHandle cm handle identifier
- * @param requestBody the request body
- * @param contentType content type of the body
+ * @param datastoreName name of the datastore
+ * @param cmHandle cm handle identifier
+ * @param requestBody the request body
+ * @param contentType content type of the body
* @return response entity
*/
+
@Override
public ResponseEntity<Object> updateResourceDataRunningForCmHandle(final String resourceIdentifier,
+ final String datastoreName,
final String cmHandle,
final Object requestBody,
final String contentType) {
+ acceptPassthroughRunningOnly(datastoreName);
+
networkCmProxyDataService.writeResourceDataPassThroughRunningForCmHandle(cmHandle,
resourceIdentifier, UPDATE, jsonObjectMapper.asJsonString(requestBody), contentType);
return new ResponseEntity<>(HttpStatus.OK);
}
-
/**
- * Delete resource data in datastore pass-through running for a given cm-handle.
+ * Delete resource data in datastore pass-through running for a given cm-handle.
*
+ * @param datastoreName name of the datastore
+ * @param cmHandle cm handle identifier
* @param resourceIdentifier resource identifier
- * @param cmHandle cm handle identifier
- * @param contentType content type of the body
+ * @param contentType content type of the body
* @return response entity no content if request is successful
*/
@Override
- public ResponseEntity<Void> deleteResourceDataRunningForCmHandle(final String cmHandle,
+ public ResponseEntity<Void> deleteResourceDataRunningForCmHandle(final String datastoreName,
+ final String cmHandle,
final String resourceIdentifier,
final String contentType) {
+
+ acceptPassthroughRunningOnly(datastoreName);
+
networkCmProxyDataService.writeResourceDataPassThroughRunningForCmHandle(cmHandle,
- resourceIdentifier, DELETE, NO_BODY, contentType);
+ resourceIdentifier, DELETE, NO_BODY, contentType);
return new ResponseEntity<>(HttpStatus.NO_CONTENT);
}
@@ -240,7 +223,7 @@
*/
@Override
public ResponseEntity<List<String>> searchCmHandleIds(
- final CmHandleQueryParameters cmHandleQueryParameters) {
+ final CmHandleQueryParameters cmHandleQueryParameters) {
final CmHandleQueryApiParameters cmHandleQueryApiParameters =
jsonObjectMapper.convertToValueType(cmHandleQueryParameters, CmHandleQueryApiParameters.class);
final Set<String> cmHandleIds = networkCmProxyDataService.executeCmHandleIdSearch(cmHandleQueryApiParameters);
@@ -249,6 +232,7 @@
/**
* Search for Cm Handle and Properties by Name.
+ *
* @param cmHandleId cm-handle identifier
* @return cm handle and its properties
*/
@@ -261,33 +245,35 @@
/**
* Get Cm Handle Properties by Cm Handle Id.
+ *
* @param cmHandleId cm-handle identifier
* @return cm handle properties
*/
@Override
public ResponseEntity<RestOutputCmHandlePublicProperties> getCmHandlePublicPropertiesByCmHandleId(
- final String cmHandleId) {
+ final String cmHandleId) {
final CmHandlePublicProperties cmHandlePublicProperties = new CmHandlePublicProperties();
cmHandlePublicProperties.add(networkCmProxyDataService.getCmHandlePublicProperties(cmHandleId));
final RestOutputCmHandlePublicProperties restOutputCmHandlePublicProperties =
- new RestOutputCmHandlePublicProperties();
+ new RestOutputCmHandlePublicProperties();
restOutputCmHandlePublicProperties.setPublicCmHandleProperties(cmHandlePublicProperties);
return ResponseEntity.ok(restOutputCmHandlePublicProperties);
}
/**
* Get Cm Handle State by Cm Handle Id.
+ *
* @param cmHandleId cm-handle identifier
* @return cm handle state
*/
@Override
public ResponseEntity<RestOutputCmHandleCompositeState> getCmHandleStateByCmHandleId(
- final String cmHandleId) {
+ final String cmHandleId) {
final CompositeState cmHandleState = networkCmProxyDataService.getCmHandleCompositeState(cmHandleId);
final RestOutputCmHandleCompositeState restOutputCmHandleCompositeState =
- new RestOutputCmHandleCompositeState();
+ new RestOutputCmHandleCompositeState();
restOutputCmHandleCompositeState.setState(
- cmHandleStateMapper.toCmHandleCompositeStateExternalLockReason(cmHandleState));
+ cmHandleStateMapper.toCmHandleCompositeStateExternalLockReason(cmHandleState));
return ResponseEntity.ok(restOutputCmHandleCompositeState);
}
@@ -314,26 +300,27 @@
*/
public ResponseEntity<List<RestModuleReference>> getModuleReferencesByCmHandle(final String cmHandle) {
final List<RestModuleReference> restModuleReferences =
- networkCmProxyDataService.getYangResourcesModuleReferences(cmHandle).stream()
- .map(ncmpRestInputMapper::toRestModuleReference)
- .collect(Collectors.toList());
+ networkCmProxyDataService.getYangResourcesModuleReferences(cmHandle).stream()
+ .map(ncmpRestInputMapper::toRestModuleReference)
+ .collect(Collectors.toList());
return new ResponseEntity<>(restModuleReferences, HttpStatus.OK);
}
/**
* Set the data sync enabled flag, along with the data sync state for the specified cm handle.
*
- * @param cmHandleId cm handle id
+ * @param cmHandleId cm handle id
* @param dataSyncEnabledFlag data sync enabled flag
* @return response entity ok if request is successful
*/
@Override
public ResponseEntity<Object> setDataSyncEnabledFlagForCmHandle(final String cmHandleId,
- final Boolean dataSyncEnabledFlag) {
+ final Boolean dataSyncEnabledFlag) {
networkCmProxyDataService.setDataSyncEnabled(cmHandleId, dataSyncEnabledFlag);
return new ResponseEntity<>(HttpStatus.OK);
}
+
private RestOutputCmHandle toRestOutputCmHandle(final NcmpServiceCmHandle ncmpServiceCmHandle) {
final RestOutputCmHandle restOutputCmHandle = new RestOutputCmHandle();
final CmHandlePublicProperties cmHandlePublicProperties = new CmHandlePublicProperties();
@@ -345,15 +332,12 @@
return restOutputCmHandle;
}
- private static boolean isValidTopic(final String topicName) {
- if (topicName == null) {
- return false;
- }
- if (CpsValidator.validateTopicName(topicName)) {
- return true;
- }
- throw new InvalidTopicException("Topic name " + topicName + " is invalid", "invalid topic");
- }
+ private void acceptPassthroughRunningOnly(final String datastoreName) {
+ final DatastoreType datastoreType = DatastoreType.fromDatastoreName(datastoreName);
+ if (DatastoreType.PASSTHROUGH_RUNNING != datastoreType) {
+ throw new InvalidDatastoreException(datastoreName + " is not supported");
+ }
+ }
}
diff --git a/cps-ncmp-rest/src/main/java/org/onap/cps/ncmp/rest/controller/handlers/DatastoreType.java b/cps-ncmp-rest/src/main/java/org/onap/cps/ncmp/rest/controller/handlers/DatastoreType.java
new file mode 100644
index 0000000..e8ab997
--- /dev/null
+++ b/cps-ncmp-rest/src/main/java/org/onap/cps/ncmp/rest/controller/handlers/DatastoreType.java
@@ -0,0 +1,67 @@
+/*
+ * ============LICENSE_START=======================================================
+ * Copyright (C) 2022 Nordix Foundation
+ * ================================================================================
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ * ============LICENSE_END=========================================================
+ */
+
+package org.onap.cps.ncmp.rest.controller.handlers;
+
+
+import java.util.Arrays;
+import java.util.HashMap;
+import java.util.Map;
+import lombok.Getter;
+import org.onap.cps.ncmp.rest.exceptions.InvalidDatastoreException;
+
+@Getter
+public enum DatastoreType {
+
+ OPERATIONAL("ncmp-datastore:operational"),
+ PASSTHROUGH_RUNNING("ncmp-datastore:passthrough-running"),
+ PASSTHROUGH_OPERATIONAL("ncmp-datastore:passthrough-operational");
+
+ DatastoreType(final String datastoreName) {
+ this.datastoreName = datastoreName;
+ }
+
+ private final String datastoreName;
+ private static final Map<String, DatastoreType> datastoreNameToDatastoreType = new HashMap<>();
+
+ static {
+ Arrays.stream(DatastoreType.values()).forEach(
+ type -> datastoreNameToDatastoreType.put(type.getDatastoreName(), type));
+ }
+
+ /**
+ * From datastore name get datastore type.
+ *
+ * @param datastoreName the datastore name
+ * @return the datastore type
+ */
+ public static DatastoreType fromDatastoreName(final String datastoreName) {
+
+ final DatastoreType datastoreType = datastoreNameToDatastoreType.get(datastoreName);
+
+ if (null == datastoreType) {
+ throw new InvalidDatastoreException(datastoreName + " is an invalid datastore name");
+ }
+
+ return datastoreType;
+ }
+
+}
+
diff --git a/cps-ncmp-rest/src/main/java/org/onap/cps/ncmp/rest/controller/handlers/NcmpDatastoreOperationalResourceRequestHandler.java b/cps-ncmp-rest/src/main/java/org/onap/cps/ncmp/rest/controller/handlers/NcmpDatastoreOperationalResourceRequestHandler.java
new file mode 100644
index 0000000..6ed9b8c
--- /dev/null
+++ b/cps-ncmp-rest/src/main/java/org/onap/cps/ncmp/rest/controller/handlers/NcmpDatastoreOperationalResourceRequestHandler.java
@@ -0,0 +1,55 @@
+/*
+ * ============LICENSE_START=======================================================
+ * Copyright (C) 2022 Nordix Foundation
+ * ================================================================================
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ * ============LICENSE_END=========================================================
+ */
+
+package org.onap.cps.ncmp.rest.controller.handlers;
+
+import java.util.function.Supplier;
+import lombok.extern.slf4j.Slf4j;
+import org.onap.cps.ncmp.api.NetworkCmProxyDataService;
+import org.onap.cps.ncmp.rest.executor.CpsNcmpTaskExecutor;
+import org.onap.cps.spi.FetchDescendantsOption;
+
+@Slf4j
+public class NcmpDatastoreOperationalResourceRequestHandler extends NcmpDatastoreResourceRequestHandler {
+
+ public NcmpDatastoreOperationalResourceRequestHandler(final NetworkCmProxyDataService networkCmProxyDataService,
+ final CpsNcmpTaskExecutor cpsNcmpTaskExecutor,
+ final int timeOutInMilliSeconds,
+ final boolean notificationFeatureEnabled) {
+ super(networkCmProxyDataService, cpsNcmpTaskExecutor, timeOutInMilliSeconds, notificationFeatureEnabled);
+ }
+
+ @Override
+ public Supplier<Object> getTask(final String cmHandle,
+ final String resourceIdentifier,
+ final String optionsParamInQuery,
+ final String topicParamInQuery,
+ final String requestId,
+ final Boolean includeDescendant) {
+
+ final FetchDescendantsOption fetchDescendantsOption =
+ Boolean.TRUE.equals(includeDescendant) ? FetchDescendantsOption.INCLUDE_ALL_DESCENDANTS
+ : FetchDescendantsOption.OMIT_DESCENDANTS;
+
+ return () -> networkCmProxyDataService.getResourceDataOperational(cmHandle, resourceIdentifier,
+ fetchDescendantsOption);
+ }
+
+}
diff --git a/cps-ncmp-rest/src/main/java/org/onap/cps/ncmp/rest/controller/handlers/NcmpDatastorePassthroughOperationalResourceRequestHandler.java b/cps-ncmp-rest/src/main/java/org/onap/cps/ncmp/rest/controller/handlers/NcmpDatastorePassthroughOperationalResourceRequestHandler.java
new file mode 100644
index 0000000..196e5bd
--- /dev/null
+++ b/cps-ncmp-rest/src/main/java/org/onap/cps/ncmp/rest/controller/handlers/NcmpDatastorePassthroughOperationalResourceRequestHandler.java
@@ -0,0 +1,51 @@
+/*
+ * ============LICENSE_START=======================================================
+ * Copyright (C) 2022 Nordix Foundation
+ * ================================================================================
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ * ============LICENSE_END=========================================================
+ */
+
+package org.onap.cps.ncmp.rest.controller.handlers;
+
+import java.util.function.Supplier;
+import lombok.extern.slf4j.Slf4j;
+import org.onap.cps.ncmp.api.NetworkCmProxyDataService;
+import org.onap.cps.ncmp.rest.executor.CpsNcmpTaskExecutor;
+
+@Slf4j
+public class NcmpDatastorePassthroughOperationalResourceRequestHandler extends NcmpDatastoreResourceRequestHandler {
+
+ public NcmpDatastorePassthroughOperationalResourceRequestHandler(
+ final NetworkCmProxyDataService networkCmProxyDataService,
+ final CpsNcmpTaskExecutor cpsNcmpTaskExecutor,
+ final int timeOutInMilliSeconds,
+ final boolean notificationFeatureEnabled) {
+ super(networkCmProxyDataService, cpsNcmpTaskExecutor, timeOutInMilliSeconds, notificationFeatureEnabled);
+ }
+
+ @Override
+ public Supplier<Object> getTask(final String cmHandle,
+ final String resourceIdentifier,
+ final String optionsParamInQuery,
+ final String topicParamInQuery,
+ final String requestId,
+ final Boolean includeDescendant) {
+
+ return () -> networkCmProxyDataService.getResourceDataOperationalForCmHandle(
+ cmHandle, resourceIdentifier, optionsParamInQuery, topicParamInQuery, requestId);
+ }
+
+}
diff --git a/cps-ncmp-rest/src/main/java/org/onap/cps/ncmp/rest/controller/handlers/NcmpDatastorePassthroughRunningResourceRequestHandler.java b/cps-ncmp-rest/src/main/java/org/onap/cps/ncmp/rest/controller/handlers/NcmpDatastorePassthroughRunningResourceRequestHandler.java
new file mode 100644
index 0000000..5bf16b7
--- /dev/null
+++ b/cps-ncmp-rest/src/main/java/org/onap/cps/ncmp/rest/controller/handlers/NcmpDatastorePassthroughRunningResourceRequestHandler.java
@@ -0,0 +1,50 @@
+/*
+ * ============LICENSE_START=======================================================
+ * Copyright (C) 2022 Nordix Foundation
+ * ================================================================================
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ * ============LICENSE_END=========================================================
+ */
+
+package org.onap.cps.ncmp.rest.controller.handlers;
+
+import java.util.function.Supplier;
+import lombok.extern.slf4j.Slf4j;
+import org.onap.cps.ncmp.api.NetworkCmProxyDataService;
+import org.onap.cps.ncmp.rest.executor.CpsNcmpTaskExecutor;
+
+@Slf4j
+public class NcmpDatastorePassthroughRunningResourceRequestHandler extends NcmpDatastoreResourceRequestHandler {
+
+ public NcmpDatastorePassthroughRunningResourceRequestHandler(
+ final NetworkCmProxyDataService networkCmProxyDataService,
+ final CpsNcmpTaskExecutor cpsNcmpTaskExecutor,
+ final int timeOutInMilliSeconds,
+ final boolean notificationFeatureEnabled) {
+ super(networkCmProxyDataService, cpsNcmpTaskExecutor, timeOutInMilliSeconds, notificationFeatureEnabled);
+ }
+
+ @Override
+ public Supplier<Object> getTask(final String cmHandle,
+ final String resourceIdentifier,
+ final String optionsParamInQuery,
+ final String topicParamInQuery,
+ final String requestId,
+ final Boolean includeDescendant) {
+
+ return () -> networkCmProxyDataService.getResourceDataPassThroughRunningForCmHandle(
+ cmHandle, resourceIdentifier, optionsParamInQuery, topicParamInQuery, requestId);
+ }
+}
diff --git a/cps-ncmp-rest/src/main/java/org/onap/cps/ncmp/rest/controller/handlers/NcmpDatastoreResourceRequestHandler.java b/cps-ncmp-rest/src/main/java/org/onap/cps/ncmp/rest/controller/handlers/NcmpDatastoreResourceRequestHandler.java
new file mode 100644
index 0000000..a6d313b
--- /dev/null
+++ b/cps-ncmp-rest/src/main/java/org/onap/cps/ncmp/rest/controller/handlers/NcmpDatastoreResourceRequestHandler.java
@@ -0,0 +1,92 @@
+/*
+ * ============LICENSE_START=======================================================
+ * Copyright (C) 2022 Nordix Foundation
+ * ================================================================================
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ * ============LICENSE_END=========================================================
+ */
+
+package org.onap.cps.ncmp.rest.controller.handlers;
+
+import java.util.Map;
+import java.util.UUID;
+import java.util.function.Supplier;
+import lombok.RequiredArgsConstructor;
+import lombok.extern.slf4j.Slf4j;
+import org.onap.cps.ncmp.api.NetworkCmProxyDataService;
+import org.onap.cps.ncmp.rest.executor.CpsNcmpTaskExecutor;
+import org.onap.cps.ncmp.rest.util.TopicValidator;
+import org.springframework.http.ResponseEntity;
+
+@RequiredArgsConstructor
+@Slf4j
+public abstract class NcmpDatastoreResourceRequestHandler {
+
+ private static final String NO_REQUEST_ID = null;
+ private static final String NO_TOPIC = null;
+
+ protected final NetworkCmProxyDataService networkCmProxyDataService;
+ protected final CpsNcmpTaskExecutor cpsNcmpTaskExecutor;
+ protected final int timeOutInMilliSeconds;
+ protected final boolean notificationFeatureEnabled;
+
+ protected abstract Supplier<Object> getTask(final String cmHandle,
+ final String resourceIdentifier,
+ final String optionsParamInQuery,
+ final String topicParamInQuery,
+ final String requestId,
+ final Boolean includeDescendant);
+
+
+ /**
+ * Get resource data from datastore.
+ *
+ * @param cmHandleId the cm handle
+ * @param resourceIdentifier the resource identifier
+ * @param optionsParamInQuery the options param in query
+ * @param topicParamInQuery the topic param in query
+ * @param includeDescendants whether include descendants
+ * @return the response entity
+ */
+ public ResponseEntity<Object> getResourceData(final String cmHandleId,
+ final String resourceIdentifier,
+ final String optionsParamInQuery,
+ final String topicParamInQuery,
+ final Boolean includeDescendants) {
+
+ final String requestId = UUID.randomUUID().toString();
+ final boolean asyncResponseRequested = topicParamInQuery != null;
+
+ if (asyncResponseRequested && notificationFeatureEnabled) {
+ TopicValidator.validateTopicName(topicParamInQuery);
+ log.debug("Received Async request with id {}", requestId);
+ cpsNcmpTaskExecutor.executeTask(
+ getTask(cmHandleId, resourceIdentifier, optionsParamInQuery, topicParamInQuery, requestId,
+ includeDescendants), timeOutInMilliSeconds);
+
+ return ResponseEntity.ok(Map.of("requestId", requestId));
+ }
+
+ if (asyncResponseRequested) {
+ log.warn("Asynchronous messaging is currently disabled, will use synchronous operation.");
+ }
+
+ final Object responseObject =
+ getTask(cmHandleId, resourceIdentifier, optionsParamInQuery, NO_TOPIC, NO_REQUEST_ID,
+ includeDescendants).get();
+
+ return ResponseEntity.ok(responseObject);
+ }
+}
diff --git a/cps-ncmp-rest/src/main/java/org/onap/cps/ncmp/rest/controller/handlers/NcmpDatastoreResourceRequestHandlerFactory.java b/cps-ncmp-rest/src/main/java/org/onap/cps/ncmp/rest/controller/handlers/NcmpDatastoreResourceRequestHandlerFactory.java
new file mode 100644
index 0000000..35bd578
--- /dev/null
+++ b/cps-ncmp-rest/src/main/java/org/onap/cps/ncmp/rest/controller/handlers/NcmpDatastoreResourceRequestHandlerFactory.java
@@ -0,0 +1,64 @@
+/*
+ * ============LICENSE_START=======================================================
+ * Copyright (C) 2022 Nordix Foundation
+ * ================================================================================
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ * ============LICENSE_END=========================================================
+ */
+
+package org.onap.cps.ncmp.rest.controller.handlers;
+
+import lombok.RequiredArgsConstructor;
+import org.onap.cps.ncmp.api.NetworkCmProxyDataService;
+import org.onap.cps.ncmp.rest.executor.CpsNcmpTaskExecutor;
+import org.springframework.beans.factory.annotation.Value;
+import org.springframework.stereotype.Component;
+
+@Component
+@RequiredArgsConstructor
+public class NcmpDatastoreResourceRequestHandlerFactory {
+
+ private final NetworkCmProxyDataService networkCmProxyDataService;
+ private final CpsNcmpTaskExecutor cpsNcmpTaskExecutor;
+
+ @Value("${notification.async.executor.time-out-value-in-ms:2000}")
+ private int timeOutInMilliSeconds;
+ @Value("${notification.enabled:true}")
+ private boolean notificationFeatureEnabled;
+
+ /**
+ * Gets ncmp datastore handler.
+ *
+ * @param datastoreType the datastore type
+ * @return the ncmp datastore handler
+ */
+ public NcmpDatastoreResourceRequestHandler getNcmpDatastoreResourceRequestHandler(
+ final DatastoreType datastoreType) {
+
+ switch (datastoreType) {
+ case OPERATIONAL:
+ return new NcmpDatastoreOperationalResourceRequestHandler(networkCmProxyDataService,
+ cpsNcmpTaskExecutor, timeOutInMilliSeconds, notificationFeatureEnabled);
+ case PASSTHROUGH_RUNNING:
+ return new NcmpDatastorePassthroughRunningResourceRequestHandler(networkCmProxyDataService,
+ cpsNcmpTaskExecutor, timeOutInMilliSeconds, notificationFeatureEnabled);
+ case PASSTHROUGH_OPERATIONAL:
+ return new NcmpDatastorePassthroughOperationalResourceRequestHandler(networkCmProxyDataService,
+ cpsNcmpTaskExecutor, timeOutInMilliSeconds, notificationFeatureEnabled);
+ default:
+ return null;
+ }
+ }
+}
diff --git a/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/exception/InvalidTopicException.java b/cps-ncmp-rest/src/main/java/org/onap/cps/ncmp/rest/exceptions/InvalidDatastoreException.java
similarity index 71%
copy from cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/exception/InvalidTopicException.java
copy to cps-ncmp-rest/src/main/java/org/onap/cps/ncmp/rest/exceptions/InvalidDatastoreException.java
index b56ca7b..ff13a93 100644
--- a/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/exception/InvalidTopicException.java
+++ b/cps-ncmp-rest/src/main/java/org/onap/cps/ncmp/rest/exceptions/InvalidDatastoreException.java
@@ -18,23 +18,15 @@
* ============LICENSE_END=========================================================
*/
-package org.onap.cps.ncmp.api.impl.exception;
+package org.onap.cps.ncmp.rest.exceptions;
-import lombok.Getter;
-
-public class InvalidTopicException extends RuntimeException {
-
- @Getter
- final String details;
-
+public class InvalidDatastoreException extends RuntimeException {
/**
- * Constructor.
+ * Instantiates a new Invalid datastore exception.
*
- * @param message the error message
- * @param details the error details
+ * @param message the message
*/
- public InvalidTopicException(final String message, final String details) {
+ public InvalidDatastoreException(final String message) {
super(message);
- this.details = details;
}
}
diff --git a/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/exception/InvalidTopicException.java b/cps-ncmp-rest/src/main/java/org/onap/cps/ncmp/rest/exceptions/InvalidTopicException.java
similarity index 96%
rename from cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/exception/InvalidTopicException.java
rename to cps-ncmp-rest/src/main/java/org/onap/cps/ncmp/rest/exceptions/InvalidTopicException.java
index b56ca7b..6a52d58 100644
--- a/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/exception/InvalidTopicException.java
+++ b/cps-ncmp-rest/src/main/java/org/onap/cps/ncmp/rest/exceptions/InvalidTopicException.java
@@ -18,7 +18,7 @@
* ============LICENSE_END=========================================================
*/
-package org.onap.cps.ncmp.api.impl.exception;
+package org.onap.cps.ncmp.rest.exceptions;
import lombok.Getter;
diff --git a/cps-ncmp-rest/src/main/java/org/onap/cps/ncmp/rest/exceptions/NetworkCmProxyRestExceptionHandler.java b/cps-ncmp-rest/src/main/java/org/onap/cps/ncmp/rest/exceptions/NetworkCmProxyRestExceptionHandler.java
index c723733..58a60d2 100755
--- a/cps-ncmp-rest/src/main/java/org/onap/cps/ncmp/rest/exceptions/NetworkCmProxyRestExceptionHandler.java
+++ b/cps-ncmp-rest/src/main/java/org/onap/cps/ncmp/rest/exceptions/NetworkCmProxyRestExceptionHandler.java
@@ -25,7 +25,6 @@
import lombok.extern.slf4j.Slf4j;
import org.onap.cps.ncmp.api.impl.exception.DmiRequestException;
import org.onap.cps.ncmp.api.impl.exception.HttpClientRequestException;
-import org.onap.cps.ncmp.api.impl.exception.InvalidTopicException;
import org.onap.cps.ncmp.api.impl.exception.NcmpException;
import org.onap.cps.ncmp.api.impl.exception.ServerNcmpException;
import org.onap.cps.ncmp.rest.controller.NetworkCmProxyController;
@@ -33,6 +32,8 @@
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.spi.exceptions.AlreadyDefinedException;
+import org.onap.cps.spi.exceptions.AlreadyDefinedExceptionBatch;
import org.onap.cps.spi.exceptions.CpsException;
import org.onap.cps.spi.exceptions.DataNodeNotFoundException;
import org.onap.cps.spi.exceptions.DataValidationException;
@@ -60,7 +61,7 @@
*/
@ExceptionHandler
public static ResponseEntity<Object> handleInternalServerErrorExceptions(
- final Exception exception) {
+ final Exception exception) {
return buildErrorResponse(HttpStatus.INTERNAL_SERVER_ERROR, exception);
}
@@ -75,12 +76,17 @@
return wrapDmiErrorResponse(HttpStatus.BAD_GATEWAY, httpClientRequestException);
}
- @ExceptionHandler({DmiRequestException.class, DataValidationException.class, HttpMessageNotReadableException.class,
- InvalidTopicException.class})
+ @ExceptionHandler({DmiRequestException.class, DataValidationException.class,
+ HttpMessageNotReadableException.class, InvalidTopicException.class, InvalidDatastoreException.class})
public static ResponseEntity<Object> handleDmiRequestExceptions(final Exception exception) {
return buildErrorResponse(HttpStatus.BAD_REQUEST, exception);
}
+ @ExceptionHandler({AlreadyDefinedException.class, AlreadyDefinedExceptionBatch.class })
+ public static ResponseEntity<Object> handleAlreadyDefinedExceptions(final Exception exception) {
+ return buildErrorResponse(HttpStatus.CONFLICT, exception);
+ }
+
@ExceptionHandler({DataNodeNotFoundException.class})
public static ResponseEntity<Object> handleNotFoundExceptions(final CpsException exception) {
return buildErrorResponse(HttpStatus.NOT_FOUND, exception);
@@ -105,7 +111,8 @@
return new ResponseEntity<>(errorMessage, status);
}
- private static ResponseEntity<Object> wrapDmiErrorResponse(final HttpStatus httpStatus,
+ private static ResponseEntity<Object> wrapDmiErrorResponse(
+ final HttpStatus httpStatus,
final HttpClientRequestException httpClientRequestException) {
final var dmiErrorMessage = new DmiErrorMessage();
final var dmiErrorResponse = new DmiErrorMessageDmiresponse();
diff --git a/cps-ncmp-rest/src/main/java/org/onap/cps/ncmp/rest/util/TopicValidator.java b/cps-ncmp-rest/src/main/java/org/onap/cps/ncmp/rest/util/TopicValidator.java
new file mode 100644
index 0000000..313e7bc
--- /dev/null
+++ b/cps-ncmp-rest/src/main/java/org/onap/cps/ncmp/rest/util/TopicValidator.java
@@ -0,0 +1,44 @@
+/*
+ * ============LICENSE_START=======================================================
+ * Copyright (C) 2022 Nordix Foundation
+ * ================================================================================
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ * ============LICENSE_END=========================================================
+ */
+
+package org.onap.cps.ncmp.rest.util;
+
+import java.util.regex.Pattern;
+import org.onap.cps.ncmp.rest.exceptions.InvalidTopicException;
+
+public class TopicValidator {
+
+ private static final Pattern TOPIC_NAME_PATTERN = Pattern.compile("^[a-zA-Z0-9]([._-](?![._-])|"
+ + "[a-zA-Z0-9]){0,120}[a-zA-Z0-9]$");
+
+ /**
+ * Validate kafka topic name pattern.
+ *
+ * @param topicName name of the topic to be validated
+ *
+ * @throws InvalidTopicException if the topic is not valid
+ */
+ public static void validateTopicName(final String topicName) {
+ if (!TOPIC_NAME_PATTERN.matcher(topicName).matches()) {
+ throw new InvalidTopicException("Topic name " + topicName + " is invalid", "invalid topic");
+ }
+ }
+
+}
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 d568a5a..b6194bc 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
@@ -23,39 +23,27 @@
package org.onap.cps.ncmp.rest.controller
+import com.fasterxml.jackson.databind.ObjectMapper
import org.mapstruct.factory.Mappers
+import org.onap.cps.TestUtils
+import org.onap.cps.ncmp.api.NetworkCmProxyDataService
import org.onap.cps.ncmp.api.inventory.CmHandleState
import org.onap.cps.ncmp.api.inventory.CompositeState
-import org.onap.cps.ncmp.api.inventory.LockReasonCategory
import org.onap.cps.ncmp.api.inventory.DataStoreSyncState
+import org.onap.cps.ncmp.api.inventory.LockReasonCategory
import org.onap.cps.ncmp.api.models.NcmpServiceCmHandle
-import org.onap.cps.ncmp.rest.mapper.CmHandleStateMapper
+import org.onap.cps.ncmp.rest.controller.handlers.DatastoreType
+import org.onap.cps.ncmp.rest.controller.handlers.NcmpDatastoreOperationalResourceRequestHandler
+import org.onap.cps.ncmp.rest.controller.handlers.NcmpDatastorePassthroughOperationalResourceRequestHandler
+import org.onap.cps.ncmp.rest.controller.handlers.NcmpDatastorePassthroughRunningResourceRequestHandler
+import org.onap.cps.ncmp.rest.controller.handlers.NcmpDatastoreResourceRequestHandlerFactory
import org.onap.cps.ncmp.rest.executor.CpsNcmpTaskExecutor
+import org.onap.cps.ncmp.rest.mapper.CmHandleStateMapper
import org.onap.cps.ncmp.rest.util.DeprecationHelper
+import org.onap.cps.spi.FetchDescendantsOption
import org.onap.cps.spi.model.ModuleDefinition
-import spock.lang.Shared
-
-import java.time.OffsetDateTime
-import java.time.ZoneOffset
-import java.time.format.DateTimeFormatter
-
-import static org.onap.cps.ncmp.api.impl.operations.DmiRequestBody.OperationEnum.PATCH
-import static org.onap.cps.ncmp.api.inventory.CompositeState.DataStores
-import static org.onap.cps.ncmp.api.inventory.CompositeState.Operational
-import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.delete
-import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get
-import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.patch
-import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.post
-import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.put
-import static org.onap.cps.ncmp.api.impl.operations.DmiRequestBody.OperationEnum.CREATE
-import static org.onap.cps.ncmp.api.impl.operations.DmiRequestBody.OperationEnum.UPDATE
-import static org.onap.cps.ncmp.api.impl.operations.DmiRequestBody.OperationEnum.DELETE
-
-import com.fasterxml.jackson.databind.ObjectMapper
-import org.onap.cps.TestUtils
import org.onap.cps.spi.model.ModuleReference
import org.onap.cps.utils.JsonObjectMapper
-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
@@ -63,11 +51,32 @@
import org.springframework.http.HttpStatus
import org.springframework.http.MediaType
import org.springframework.test.web.servlet.MockMvc
+import org.springframework.test.web.servlet.request.MockHttpServletRequestBuilder
+import spock.lang.Shared
import spock.lang.Specification
+import java.time.OffsetDateTime
+import java.time.ZoneOffset
+import java.time.format.DateTimeFormatter
+
+import static org.onap.cps.ncmp.api.inventory.CompositeState.DataStores
+import static org.onap.cps.ncmp.api.inventory.CompositeState.Operational
+import static org.onap.cps.ncmp.api.impl.operations.DmiRequestBody.OperationEnum.CREATE
+import static org.onap.cps.ncmp.api.impl.operations.DmiRequestBody.OperationEnum.UPDATE
+import static org.onap.cps.ncmp.api.impl.operations.DmiRequestBody.OperationEnum.PATCH
+import static org.onap.cps.ncmp.api.impl.operations.DmiRequestBody.OperationEnum.DELETE
+import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get
+import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.put
+import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.post
+import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.patch
+import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.delete
+
@WebMvcTest(NetworkCmProxyController)
class NetworkCmProxyControllerSpec extends Specification {
+ public static final int TIMEOUT_IN_MS = 2000
+ public static final boolean NOTIFICATION_ENABLED = true
+
@Autowired
MockMvc mvc
@@ -92,6 +101,9 @@
@SpringBean
DeprecationHelper stubbedDeprecationHelper = Stub()
+ @SpringBean
+ NcmpDatastoreResourceRequestHandlerFactory stubbedNcmpDatastoreResourceRequestHandlerFactory = Stub()
+
@Value('${rest.api.ncmp-base-path}/v1')
def ncmpBasePathV1
@@ -104,21 +116,38 @@
def formattedDateAndTime = DateTimeFormatter.ofPattern("yyyy-MM-dd'T'HH:mm:ss.SSSZ")
.format(OffsetDateTime.of(2022, 12, 31, 20, 30, 40, 1, ZoneOffset.UTC))
+ void setup() {
+ stubbedNcmpDatastoreResourceRequestHandlerFactory.getNcmpDatastoreResourceRequestHandler(
+ DatastoreType.OPERATIONAL) >>
+ new NcmpDatastoreOperationalResourceRequestHandler(
+ mockNetworkCmProxyDataService, spiedCpsTaskExecutor, TIMEOUT_IN_MS, NOTIFICATION_ENABLED)
+
+ stubbedNcmpDatastoreResourceRequestHandlerFactory.getNcmpDatastoreResourceRequestHandler(
+ DatastoreType.PASSTHROUGH_OPERATIONAL) >>
+ new NcmpDatastorePassthroughOperationalResourceRequestHandler(
+ mockNetworkCmProxyDataService, spiedCpsTaskExecutor, TIMEOUT_IN_MS, NOTIFICATION_ENABLED)
+
+ stubbedNcmpDatastoreResourceRequestHandlerFactory.getNcmpDatastoreResourceRequestHandler(
+ DatastoreType.PASSTHROUGH_RUNNING) >>
+ new NcmpDatastorePassthroughRunningResourceRequestHandler(
+ mockNetworkCmProxyDataService, spiedCpsTaskExecutor, TIMEOUT_IN_MS, NOTIFICATION_ENABLED)
+ }
+
def 'Get Resource Data from pass-through operational.'() {
given: 'resource data url'
def getUrl = "$ncmpBasePathV1/ch/testCmHandle/data/ds/ncmp-datastore:passthrough-operational" +
- "?resourceIdentifier=parent/child&options=(a=1,b=2)"
+ "?resourceIdentifier=parent/child&options=(a=1,b=2)"
when: 'get data resource request is performed'
def response = mvc.perform(
- get(getUrl)
- .contentType(MediaType.APPLICATION_JSON)
+ get(getUrl)
+ .contentType(MediaType.APPLICATION_JSON)
).andReturn().response
then: 'the NCMP data service is called with getResourceDataOperationalForCmHandle'
1 * mockNetworkCmProxyDataService.getResourceDataOperationalForCmHandle('testCmHandle',
- 'parent/child',
- '(a=1,b=2)',
- NO_TOPIC,
- NO_REQUEST_ID)
+ 'parent/child',
+ '(a=1,b=2)',
+ NO_TOPIC,
+ NO_REQUEST_ID)
and: 'response status is Ok'
response.status == HttpStatus.OK.value()
}
@@ -126,22 +155,22 @@
def 'Get Resource Data from #datastoreInUrl with #scenario.'() {
given: 'resource data url'
def getUrl = "$ncmpBasePathV1/ch/testCmHandle/data/ds/ncmp-datastore:${datastoreInUrl}" +
- "?resourceIdentifier=parent/child&options=(a=1,b=2)${topicQueryParam}"
+ "?resourceIdentifier=parent/child&options=(a=1,b=2)${topicQueryParam}"
when: 'get data resource request is performed'
def response = mvc.perform(
- get(getUrl).contentType(MediaType.APPLICATION_JSON)).andReturn().response
+ get(getUrl).contentType(MediaType.APPLICATION_JSON)).andReturn().response
then: 'task executor is called appropriate number of times'
- expectedNumberOfExecutorExecutions * spiedCpsTaskExecutor.executeTask(_, 2000)
+ expectedNumberOfExecutorExecutions * spiedCpsTaskExecutor.executeTask(_, TIMEOUT_IN_MS)
and: 'response status is expected'
response.status == HttpStatus.OK.value()
where: 'the following parameters are used'
- scenario | datastoreInUrl | topicQueryParam || expectedTopicName | expectedNumberOfExecutorExecutions
- 'url with valid topic' | 'passthrough-operational' | '&topic=my-topic-name' || 'my-topic-name' | 1
- 'no topic in url' | 'passthrough-operational' | '' || NO_TOPIC | 0
- 'null topic in url' | 'passthrough-operational' | '&topic=null' || 'null' | 1
- 'url with valid topic' | 'passthrough-running' | '&topic=my-topic-name' || 'my-topic-name' | 1
- 'no topic in url' | 'passthrough-running' | '' || NO_TOPIC | 0
- 'null topic in url' | 'passthrough-running' | '&topic=null' || 'null' | 1
+ scenario | datastoreInUrl | topicQueryParam || expectedTopicName | expectedNumberOfExecutorExecutions
+ 'url with valid topic' | 'passthrough-operational' | '&topic=my-topic-name' || 'my-topic-name' | 1
+ 'no topic in url' | 'passthrough-operational' | '' || NO_TOPIC | 0
+ 'null topic in url' | 'passthrough-operational' | '&topic=null' || 'null' | 1
+ 'url with valid topic' | 'passthrough-running' | '&topic=my-topic-name' || 'my-topic-name' | 1
+ 'no topic in url' | 'passthrough-running' | '' || NO_TOPIC | 0
+ 'null topic in url' | 'passthrough-running' | '&topic=null' || 'null' | 1
}
def 'Fail to get Resource Data from #datastoreInUrl when #scenario.'() {
@@ -168,17 +197,17 @@
def 'Get Resource Data from pass-through running with #scenario value in resource identifier param.'() {
given: 'resource data url'
def getUrl = "$ncmpBasePathV1/ch/testCmHandle/data/ds/ncmp-datastore:passthrough-running" +
- "?resourceIdentifier=" + resourceIdentifier + "&options=(a=1,b=2)"
+ "?resourceIdentifier=" + resourceIdentifier + "&options=(a=1,b=2)"
and: 'ncmp service returns json object'
mockNetworkCmProxyDataService.getResourceDataPassThroughRunningForCmHandle('testCmHandle',
- resourceIdentifier,
- '(a=1,b=2)',
- NO_TOPIC,
- NO_REQUEST_ID) >> '{valid-json}'
+ resourceIdentifier,
+ '(a=1,b=2)',
+ NO_TOPIC,
+ NO_REQUEST_ID) >> '{valid-json}'
when: 'get data resource request is performed'
def response = mvc.perform(
- get(getUrl)
- .contentType(MediaType.APPLICATION_JSON)
+ get(getUrl)
+ .contentType(MediaType.APPLICATION_JSON)
).andReturn().response
then: 'response status is Ok'
response.status == HttpStatus.OK.value()
@@ -194,7 +223,7 @@
'? needs to be encoded as %3F' | 'idWith%3F'
}
- def 'Update resource data from pass-through running.' () {
+ def 'Update resource data from pass-through running.'() {
given: 'update resource data url'
def updateUrl = "$ncmpBasePathV1/ch/testCmHandle/data/ds/ncmp-datastore:passthrough-running" +
"?resourceIdentifier=parent/child"
@@ -210,15 +239,14 @@
response.status == HttpStatus.OK.value()
}
- def 'Create Resource Data from pass-through running with #scenario.' () {
+ def 'Create Resource Data from pass-through running with #scenario.'() {
given: 'resource data url'
def url = "$ncmpBasePathV1/ch/testCmHandle/data/ds/ncmp-datastore:passthrough-running" +
- "?resourceIdentifier=parent/child"
- def requestBody = '{"some-key":"some-value"}'
+ "?resourceIdentifier=parent/child"
when: 'create resource request is performed'
def response = mvc.perform(
- post(url)
- .contentType(MediaType.APPLICATION_JSON_VALUE).content(requestBody)
+ post(url)
+ .contentType(MediaType.APPLICATION_JSON_VALUE).content(requestBody)
).andReturn().response
then: 'ncmp service method to create resource called'
1 * mockNetworkCmProxyDataService.writeResourceDataPassThroughRunningForCmHandle('testCmHandle',
@@ -227,14 +255,14 @@
response.status == HttpStatus.CREATED.value()
}
- def 'Get module references for the given dataspace and cm handle.' () {
+ def 'Get module references for the given dataspace and cm handle.'() {
given: 'get module references url'
def getUrl = "$ncmpBasePathV1/ch/some-cmhandle/modules"
when: 'get module resource request is performed'
- def response =mvc.perform(get(getUrl)).andReturn().response
+ def response = mvc.perform(get(getUrl)).andReturn().response
then: 'ncmp service method to get yang resource module references is called'
mockNetworkCmProxyDataService.getYangResourcesModuleReferences('some-cmhandle')
- >> [new ModuleReference(moduleName: 'some-name1',revision: '2021-10-03')]
+ >> [new ModuleReference(moduleName: 'some-name1', revision: '2021-10-03')]
and: 'response contains an array with the module name and revision'
response.getContentAsString() == '[{"moduleName":"some-name1","revision":"2021-10-03"}]'
and: 'response returns an OK http code'
@@ -248,15 +276,15 @@
and: 'the service method is invoked with module names and returns two cm handles'
def cmHandle1 = new NcmpServiceCmHandle()
cmHandle1.cmHandleId = 'some-cmhandle-id1'
- cmHandle1.publicProperties = [color:'yellow']
+ cmHandle1.publicProperties = [color: 'yellow']
def cmHandle2 = new NcmpServiceCmHandle()
cmHandle2.cmHandleId = 'some-cmhandle-id2'
- cmHandle2.publicProperties = [color:'green']
+ cmHandle2.publicProperties = [color: 'green']
mockNetworkCmProxyDataService.executeCmHandleSearch(_) >> [cmHandle1, cmHandle2]
when: 'the searches api is invoked'
def response = mvc.perform(post(searchesEndpoint)
- .contentType(MediaType.APPLICATION_JSON)
- .content(jsonString)).andReturn().response
+ .contentType(MediaType.APPLICATION_JSON)
+ .content(jsonString)).andReturn().response
then: 'response status returns OK'
response.status == HttpStatus.OK.value()
and: 'the expected response content is returned'
@@ -268,14 +296,15 @@
def cmHandleDetailsEndpoint = "$ncmpBasePathV1/ch/some-cm-handle"
and: 'an existing ncmp service cm handle'
def cmHandleId = 'some-cm-handle'
- def dmiProperties = [ prop:'some DMI property' ]
- def publicProperties = [ "public prop":'some public property' ]
+ def dmiProperties = [prop: 'some DMI property']
+ def publicProperties = ["public prop": 'some public property']
def compositeState = compositeStateTestObject()
def ncmpServiceCmHandle = new NcmpServiceCmHandle(cmHandleId: cmHandleId, dmiProperties: dmiProperties, publicProperties: publicProperties, compositeState: compositeState)
and: 'the service method is invoked with the cm handle id'
1 * mockNetworkCmProxyDataService.getNcmpServiceCmHandle('some-cm-handle') >> ncmpServiceCmHandle
when: 'the cm handle details api is invoked'
- def response = mvc.perform(get(cmHandleDetailsEndpoint)).andReturn().response
+ def response = mvc.perform(
+ get(cmHandleDetailsEndpoint)).andReturn().response
then: 'the correct response is returned'
response.status == HttpStatus.OK.value()
and: 'the response contains the public properties'
@@ -286,30 +315,34 @@
!response.contentAsString.contains("some DMI property")
}
- def 'Get Cm Handle public properties by Cm Handle id.' () {
+ def 'Get Cm Handle public properties by Cm Handle id.'() {
given: 'a cm handle properties endpoint'
def cmHandlePropertiesEndpoint = "$ncmpBasePathV1/ch/some-cm-handle/properties"
and: 'some cm handle public properties'
- def publicProperties = [ 'public prop':'some public property' ]
+ def publicProperties = ['public prop': 'some public property']
and: 'the service method is invoked with the cm handle id returning the cm handle public properties'
- 1 * mockNetworkCmProxyDataService.getCmHandlePublicProperties('some-cm-handle') >> publicProperties
+ 1 * mockNetworkCmProxyDataService
+ .getCmHandlePublicProperties('some-cm-handle') >> publicProperties
when: 'the cm handle properties api is invoked'
- def response = mvc.perform(get(cmHandlePropertiesEndpoint)).andReturn().response
+ def response = mvc.perform(
+ get(cmHandlePropertiesEndpoint)).andReturn().response
then: 'the correct response is returned'
response.status == HttpStatus.OK.value()
and: 'the response contains the public properties'
assertContainsPublicProperties(response)
}
- def 'Get Cm Handle composite state by Cm Handle id.' () {
+ def 'Get Cm Handle composite state by Cm Handle id.'() {
given: 'a cm handle state endpoint'
def cmHandlePropertiesEndpoint = "$ncmpBasePathV1/ch/some-cm-handle/state"
and: 'some cm handle composite state'
def compositeState = compositeStateTestObject()
and: 'the service method is invoked with the cm handle id returning the cm handle composite state'
- 1 * mockNetworkCmProxyDataService.getCmHandleCompositeState('some-cm-handle') >> compositeState
+ 1 * mockNetworkCmProxyDataService
+ .getCmHandleCompositeState('some-cm-handle') >> compositeState
when: 'the cm handle state api is invoked'
- def response = mvc.perform(get(cmHandlePropertiesEndpoint)).andReturn().response
+ def response = mvc.perform(
+ get(cmHandlePropertiesEndpoint)).andReturn().response
then: 'the correct response is returned'
response.status == HttpStatus.OK.value()
and: 'the response contains the cm handle state'
@@ -323,13 +356,14 @@
and: 'the service method is invoked with module names and returns two cm handles'
def cmHandel1 = new NcmpServiceCmHandle()
cmHandel1.cmHandleId = 'some-cmhandle-id1'
- cmHandel1.publicProperties = [color:'yellow']
+ cmHandel1.publicProperties = [color: 'yellow']
def cmHandel2 = new NcmpServiceCmHandle()
cmHandel2.cmHandleId = 'some-cmhandle-id2'
- cmHandel2.publicProperties = [color:'green']
+ cmHandel2.publicProperties = [color: 'green']
mockNetworkCmProxyDataService.executeCmHandleSearch(_) >> [cmHandel1, cmHandel2]
when: 'the searches api is invoked'
- def response = mvc.perform(post(searchesEndpoint)
+ def response = mvc.perform(
+ post(searchesEndpoint)
.contentType(MediaType.APPLICATION_JSON)
.content(jsonString)).andReturn().response
then: 'an empty cm handle identifier is returned'
@@ -342,9 +376,10 @@
and: 'the service method is invoked with module names and returns cm handle ids'
1 * mockNetworkCmProxyDataService.executeCmHandleIdSearch(_) >> ['some-cmhandle-id1', 'some-cmhandle-id2']
when: 'the searches api is invoked'
- def response = mvc.perform(post(searchesEndpoint)
- .contentType(MediaType.APPLICATION_JSON)
- .content('{}')).andReturn().response
+ def response = mvc.perform(
+ post(searchesEndpoint)
+ .contentType(MediaType.APPLICATION_JSON)
+ .content('{}')).andReturn().response
then: 'cm handle ids are returned'
response.contentAsString == '["some-cmhandle-id1","some-cmhandle-id2"]'
}
@@ -353,37 +388,39 @@
when: 'the searches api is invoked'
def searchesEndpoint = "$ncmpBasePathV1/ch/id-searches"
def invalidInputData = '{invalidJson}'
- def response = mvc.perform(post(searchesEndpoint)
+ def response = mvc.perform(
+ post(searchesEndpoint)
.contentType(MediaType.APPLICATION_JSON)
.content(invalidInputData)).andReturn().response
then: 'BAD_REQUEST is returned'
response.getStatus() == 400
}
- def 'Patch resource data in pass-through running datastore.' () {
+ def 'Patch resource data in pass-through running datastore.'() {
given: 'patch resource data url'
def url = "$ncmpBasePathV1/ch/testCmHandle/data/ds/ncmp-datastore:passthrough-running" +
- "?resourceIdentifier=parent/child"
+ "?resourceIdentifier=parent/child"
when: 'patch data resource request is performed'
def response = mvc.perform(
- patch(url)
- .contentType(MediaType.APPLICATION_JSON)
- .accept(MediaType.APPLICATION_JSON).content(requestBody)
+ patch(url)
+ .contentType(MediaType.APPLICATION_JSON)
+ .accept(MediaType.APPLICATION_JSON).content(requestBody)
).andReturn().response
then: 'ncmp service method to update resource is called'
1 * mockNetworkCmProxyDataService.writeResourceDataPassThroughRunningForCmHandle('testCmHandle',
- 'parent/child', PATCH, requestBody, 'application/json;charset=UTF-8')
+ 'parent/child', PATCH, requestBody, 'application/json;charset=UTF-8')
and: 'the response status is OK'
response.status == HttpStatus.OK.value()
}
- def 'Delete resource data in pass-through running datastore.' () {
+ def 'Delete resource data in pass-through running datastore.'() {
given: 'delete resource data url'
def url = "$ncmpBasePathV1/ch/testCmHandle/data/ds/ncmp-datastore:passthrough-running" +
- "?resourceIdentifier=parent/child"
+ "?resourceIdentifier=parent/child"
when: 'delete data resource request is performed'
def response = mvc.perform(
- delete(url).contentType(MediaType.APPLICATION_JSON).accept(MediaType.APPLICATION_JSON)).andReturn().response
+ delete(url)
+ .contentType(MediaType.APPLICATION_JSON).accept(MediaType.APPLICATION_JSON)).andReturn().response
then: 'the ncmp service method to delete resource is called (with null as body)'
1 * mockNetworkCmProxyDataService.writeResourceDataPassThroughRunningForCmHandle('testCmHandle',
'parent/child', DELETE, null, 'application/json;charset=UTF-8')
@@ -394,12 +431,12 @@
def 'Get resource data from DMI with valid topic i.e. async request for #scenario'() {
given: 'resource data url'
def getUrl = "$ncmpBasePathV1/ch/testCmHandle/data/ds/ncmp-datastore:${datastoreInUrl}" +
- "?resourceIdentifier=parent/child&options=(a=1,b=2)&topic=my-topic-name"
+ "?resourceIdentifier=parent/child&options=(a=1,b=2)&topic=my-topic-name"
when: 'get data resource request is performed'
def response = mvc.perform(
- get(getUrl)
- .contentType(MediaType.APPLICATION_JSON)
- .accept(MediaType.APPLICATION_JSON_VALUE)
+ get(getUrl)
+ .contentType(MediaType.APPLICATION_JSON)
+ .accept(MediaType.APPLICATION_JSON_VALUE)
).andReturn().response
then: 'async request id is generated'
assert response.contentAsString.contains("requestId")
@@ -409,32 +446,93 @@
':passthrough-running' | 'passthrough-running'
}
- def 'Get module definitions based on cmHandleId.' () {
+ def 'Get module definitions based on cmHandleId.'() {
when: 'get module definition request is performed'
- def response = mvc.perform(get("$ncmpBasePathV1/ch/some-cmhandle/modules/definitions"))
- .andReturn().response
+ def response = mvc.perform(
+ get("$ncmpBasePathV1/ch/some-cmhandle/modules/definitions"))
+ .andReturn().response
then: 'ncmp service method to get module definitions is called'
mockNetworkCmProxyDataService.getModuleDefinitionsByCmHandleId('some-cmhandle')
- >> [new ModuleDefinition('sampleModuleName', '2021-10-03',
- 'module sampleModuleName{ sample module content }')]
+ >> [new ModuleDefinition('sampleModuleName', '2021-10-03',
+ 'module sampleModuleName{ sample module content }')]
and: 'response contains an array with the module name, revision and content'
response.getContentAsString() == '[{"moduleName":"sampleModuleName","revision":"2021-10-03","content":"module sampleModuleName{ sample module content }"}]'
and: 'response returns an OK http code'
response.status == HttpStatus.OK.value()
}
- def 'Set the data sync enabled based on the cm handle id and the data sync flag is #scenario' () {
+ def 'Set the data sync enabled based on the cm handle id and the data sync flag is #scenario'() {
when: 'the set data sync enabled request is invoked'
- def response = mvc.perform(put("$ncmpBasePathV1/ch/some-cm-handle-id/data-sync?dataSyncEnabled=" + dataSyncEnabledFlag))
- .andReturn().response
+ def response = mvc.perform(
+ put("$ncmpBasePathV1/ch/some-cm-handle-id/data-sync?dataSyncEnabled=" + dataSyncEnabledFlag))
+ .andReturn().response
then: 'method to set data sync enabled is called'
1 * mockNetworkCmProxyDataService.setDataSyncEnabled('some-cm-handle-id', dataSyncEnabledFlag)
and: 'the response returns an OK http code'
response.status == HttpStatus.OK.value()
where: 'the following parameters are used'
- scenario | dataSyncEnabledFlag
- 'enabled' | true
- 'disabled' | false
+ scenario | dataSyncEnabledFlag
+ 'enabled' | true
+ 'disabled' | false
+ }
+
+ def 'Get Resource Data from operational with or without descendants'() {
+ given: 'resource data url with descendants #enabled'
+ def getUrl = "$ncmpBasePathV1/ch/testCmHandle/data/ds/ncmp-datastore:operational" +
+ "?resourceIdentifier=parent/child&include-descendants=${enabled}"
+ when: 'get data resource request is performed'
+ def response = mvc.perform(
+ get(getUrl)
+ .contentType(MediaType.APPLICATION_JSON)
+ ).andReturn().response
+ then: 'the NCMP data service is called with getResourceDataOperational with #descendantsOption'
+ 1 * mockNetworkCmProxyDataService.getResourceDataOperational('testCmHandle',
+ 'parent/child',
+ descendantsOption)
+ and: 'response status is Ok'
+ response.status == HttpStatus.OK.value()
+ where: 'the following parameters are used'
+ enabled | descendantsOption
+ false | FetchDescendantsOption.OMIT_DESCENDANTS
+ true | FetchDescendantsOption.INCLUDE_ALL_DESCENDANTS
+ }
+
+ def 'Attempt execute #operation rest operation on resource data with #scenario'() {
+ given: 'resource data url'
+ def url = "$ncmpBasePathV1/ch/testCmHandle/data/ds/${datastoreInUrl}?resourceIdentifier=parent/child"
+ when: 'selected request for data resource is performed on url'
+ def response = mvc.perform(
+ executeRestOperation(operation, url))
+ .andReturn().response
+ then: 'the response status is as expected'
+ assert response.status == HttpStatus.BAD_REQUEST.value()
+ and: 'the response is as expected'
+ assert response.getContentAsString().contains(datastoreInUrl)
+ where: 'the following parameters are used'
+ scenario | operation | datastoreInUrl
+ 'unsupported datastore' | 'POST' | 'ncmp-datastore:operational'
+ 'invalid datastore' | 'POST' | 'invalid'
+ 'unsupported datastore' | 'PUT' | 'ncmp-datastore:operational'
+ 'invalid datastore' | 'PUT' | 'invalid'
+ 'unsupported datastore' | 'PATCH' | 'ncmp-datastore:operational'
+ 'invalid datastore' | 'PATCH' | 'invalid'
+ 'unsupported datastore' | 'DELETE' | 'ncmp-datastore:operational'
+ 'invalid datastore' | 'DELETE' | 'invalid'
+ }
+
+ def executeRestOperation(operation, url) {
+ if (operation == 'POST') {
+ return post(url).contentType(MediaType.APPLICATION_JSON_VALUE).content(requestBody)
+ }
+ if (operation == 'PUT') {
+ return put(url).contentType(MediaType.APPLICATION_JSON_VALUE).content(requestBody)
+ }
+ if (operation == 'PATCH') {
+ return patch(url).contentType(MediaType.APPLICATION_JSON_VALUE).content(requestBody)
+ }
+ if (operation == 'DELETE') {
+ return delete(url).contentType(MediaType.APPLICATION_JSON_VALUE)
+ }
}
def dataStores() {
@@ -453,7 +551,7 @@
}
def assertContainsAll(response, assertContent) {
- assertContent.forEach( string -> { assert(response.contentAsString.contains(string)) })
+ assertContent.forEach(string -> { assert (response.contentAsString.contains(string)) })
return void
}
@@ -476,8 +574,8 @@
def assertContainsPublicProperties(response) {
def expectedContent = [
- '"publicCmHandleProperties":' ,
- '"public prop"' ,
+ '"publicCmHandleProperties":',
+ '"public prop"',
'"some public property"'
]
return assertContainsAll(response, expectedContent)
diff --git a/cps-ncmp-rest/src/test/groovy/org/onap/cps/ncmp/rest/controller/handlers/NcmpDatastoreResourceRequestHandlerFactorySpec.groovy b/cps-ncmp-rest/src/test/groovy/org/onap/cps/ncmp/rest/controller/handlers/NcmpDatastoreResourceRequestHandlerFactorySpec.groovy
new file mode 100644
index 0000000..3f7a8a5
--- /dev/null
+++ b/cps-ncmp-rest/src/test/groovy/org/onap/cps/ncmp/rest/controller/handlers/NcmpDatastoreResourceRequestHandlerFactorySpec.groovy
@@ -0,0 +1,20 @@
+package org.onap.cps.ncmp.rest.controller.handlers
+
+import spock.lang.Specification
+
+class NcmpDatastoreResourceRequestHandlerFactorySpec extends Specification {
+
+ def objectUnderTest = new NcmpDatastoreResourceRequestHandlerFactory(null, null)
+
+ def 'Creating ncmp datastore request handlers.'() {
+ when: 'a ncmp datastore request handler is created for #datastoreType'
+ def result = objectUnderTest.getNcmpDatastoreResourceRequestHandler(datastoreType)
+ then: 'the result is of the expected class'
+ result.class == expectedClass
+ where: 'the following type of datastore is used'
+ datastoreType || expectedClass
+ DatastoreType.OPERATIONAL || NcmpDatastoreOperationalResourceRequestHandler
+ DatastoreType.PASSTHROUGH_OPERATIONAL || NcmpDatastorePassthroughOperationalResourceRequestHandler
+ DatastoreType.PASSTHROUGH_RUNNING || NcmpDatastorePassthroughRunningResourceRequestHandler
+ }
+}
\ No newline at end of file
diff --git a/cps-ncmp-rest/src/test/groovy/org/onap/cps/ncmp/rest/exceptions/NetworkCmProxyRestExceptionHandlerSpec.groovy b/cps-ncmp-rest/src/test/groovy/org/onap/cps/ncmp/rest/exceptions/NetworkCmProxyRestExceptionHandlerSpec.groovy
index ce908e7..9d1077f 100644
--- a/cps-ncmp-rest/src/test/groovy/org/onap/cps/ncmp/rest/exceptions/NetworkCmProxyRestExceptionHandlerSpec.groovy
+++ b/cps-ncmp-rest/src/test/groovy/org/onap/cps/ncmp/rest/exceptions/NetworkCmProxyRestExceptionHandlerSpec.groovy
@@ -29,9 +29,12 @@
import org.onap.cps.ncmp.api.impl.exception.HttpClientRequestException
import org.onap.cps.ncmp.api.impl.exception.ServerNcmpException
import org.onap.cps.ncmp.rest.controller.NcmpRestInputMapper
-import org.onap.cps.ncmp.rest.mapper.CmHandleStateMapper
+import org.onap.cps.ncmp.rest.controller.handlers.NcmpDatastoreResourceRequestHandlerFactory
import org.onap.cps.ncmp.rest.executor.CpsNcmpTaskExecutor
+import org.onap.cps.ncmp.rest.mapper.CmHandleStateMapper
import org.onap.cps.ncmp.rest.util.DeprecationHelper
+import org.onap.cps.spi.exceptions.AlreadyDefinedException
+import org.onap.cps.spi.exceptions.AlreadyDefinedExceptionBatch
import org.onap.cps.spi.exceptions.CpsException
import org.onap.cps.spi.exceptions.DataNodeNotFoundException
import org.onap.cps.spi.exceptions.DataValidationException
@@ -48,9 +51,7 @@
import static org.onap.cps.ncmp.rest.exceptions.NetworkCmProxyRestExceptionHandlerSpec.ApiType.NCMP
import static org.onap.cps.ncmp.rest.exceptions.NetworkCmProxyRestExceptionHandlerSpec.ApiType.NCMPINVENTORY
-import static org.springframework.http.HttpStatus.BAD_REQUEST
-import static org.springframework.http.HttpStatus.INTERNAL_SERVER_ERROR
-import static org.springframework.http.HttpStatus.NOT_FOUND
+import static org.springframework.http.HttpStatus.*
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.post
@@ -78,6 +79,9 @@
@SpringBean
DeprecationHelper stubbedDeprecationHelper = Stub()
+ @SpringBean
+ NcmpDatastoreResourceRequestHandlerFactory mockedNcmpDatastoreResourceRequestHandlerFactory = Mock()
+
@Value('${rest.api.ncmp-base-path}')
def basePathNcmp
@@ -104,13 +108,15 @@
then: 'an HTTP response is returned with correct message and details'
assertTestResponse(response, expectedErrorCode, expectedErrorMessage, expectedErrorDetails)
where:
- scenario | exception || expectedErrorDetails | expectedErrorMessage | expectedErrorCode
- 'CPS' | new CpsException(sampleErrorMessage, sampleErrorDetails) || sampleErrorDetails | sampleErrorMessage | INTERNAL_SERVER_ERROR
- 'NCMP-server' | new ServerNcmpException(sampleErrorMessage, sampleErrorDetails) || null | sampleErrorMessage | INTERNAL_SERVER_ERROR
- 'NCMP-client' | new DmiRequestException(sampleErrorMessage, sampleErrorDetails) || null | sampleErrorMessage | BAD_REQUEST
- 'DataNode Validation' | new DataNodeNotFoundException('myDataspaceName', 'myAnchorName') || null | 'DataNode not found' | NOT_FOUND
- 'other' | new IllegalStateException(sampleErrorMessage) || null | sampleErrorMessage | INTERNAL_SERVER_ERROR
- 'Data Node Not Found' | new DataNodeNotFoundException('myDataspaceName', 'myAnchorName') || 'DataNode not found' | 'DataNode not found' | NOT_FOUND
+ scenario | exception || expectedErrorDetails | expectedErrorMessage | expectedErrorCode
+ 'CPS' | new CpsException(sampleErrorMessage, sampleErrorDetails) || sampleErrorDetails | sampleErrorMessage | INTERNAL_SERVER_ERROR
+ 'NCMP-server' | new ServerNcmpException(sampleErrorMessage, sampleErrorDetails) || null | sampleErrorMessage | INTERNAL_SERVER_ERROR
+ 'NCMP-client' | new DmiRequestException(sampleErrorMessage, sampleErrorDetails) || null | sampleErrorMessage | BAD_REQUEST
+ 'DataNode Validation' | new DataNodeNotFoundException('myDataspaceName', 'myAnchorName') || null | 'DataNode not found' | NOT_FOUND
+ 'other' | new IllegalStateException(sampleErrorMessage) || null | sampleErrorMessage | INTERNAL_SERVER_ERROR
+ 'Data Node Not Found' | new DataNodeNotFoundException('myDataspaceName', 'myAnchorName') || 'DataNode not found' | 'DataNode not found' | NOT_FOUND
+ 'Existing entry' | new AlreadyDefinedException('name',null) || 'name already exists' | 'Already defined exception' | CONFLICT
+ 'Existing entries' | new AlreadyDefinedExceptionBatch(["x[@id='abc']"]) || 'Check logs for details' | null | CONFLICT
}
def 'Post request with exception returns correct HTTP Status.'() {
@@ -125,7 +131,7 @@
def 'Failing DMI Request - passthrough scenario'() {
given: 'failing DMI request'
- setupTestException(new HttpClientRequestException('Error Message Details NCMP', 'Bad Request from DMI', 400) , NCMP)
+ setupTestException(new HttpClientRequestException('Error Message Details NCMP', 'Bad Request from DMI', 400), NCMP)
when: 'the DMI request is executed'
def response = performTestRequest(NCMP)
then: 'NCMP service responds with 502 Bad Gateway status'
@@ -150,11 +156,11 @@
return mvc.perform(post("$dataNodeBaseEndpointNcmpInventory/ch").contentType(MediaType.APPLICATION_JSON).content(jsonData)).andReturn().response
}
- static void assertTestResponse(response, expectedStatus , expectedErrorMessage , expectedErrorDetails) {
+ static void assertTestResponse(response, expectedStatus, expectedErrorMessage, expectedErrorDetails) {
assert response.status == expectedStatus.value()
def content = new JsonSlurper().parseText(response.contentAsString)
assert content['status'].toString().contains(expectedStatus.toString())
- assert content['message'].toString().contains(expectedErrorMessage)
+ assert expectedErrorMessage == null || content['message'].toString().contains(expectedErrorMessage)
assert expectedErrorDetails == null || content['details'].toString().contains(expectedErrorDetails)
}
diff --git a/cps-ncmp-rest/src/test/groovy/org/onap/cps/ncmp/rest/util/TopicValidatorSpec.groovy b/cps-ncmp-rest/src/test/groovy/org/onap/cps/ncmp/rest/util/TopicValidatorSpec.groovy
new file mode 100644
index 0000000..e626e15
--- /dev/null
+++ b/cps-ncmp-rest/src/test/groovy/org/onap/cps/ncmp/rest/util/TopicValidatorSpec.groovy
@@ -0,0 +1,47 @@
+/*
+ * ============LICENSE_START=======================================================
+ * Copyright (C) 2022 Nordix Foundation
+ * ================================================================================
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ * ============LICENSE_END=========================================================
+ */
+
+package org.onap.cps.ncmp.rest.util
+
+import org.onap.cps.ncmp.rest.exceptions.InvalidTopicException
+import spock.lang.Specification
+
+class TopicValidatorSpec extends Specification {
+
+ def 'Valid topic name validation.'() {
+ when: 'a valid topic name is validated'
+ TopicValidator.validateTopicName('my-valid-topic')
+ then: 'no exception is thrown'
+ noExceptionThrown()
+ }
+
+ def 'Validating invalid topic names.'() {
+ when: 'the invalid topic name is validated'
+ TopicValidator.validateTopicName(topicName)
+ then: 'boolean response will be returned for #scenario'
+ thrown(InvalidTopicException)
+ where: 'the following names are used'
+ scenario | topicName
+ 'empty topic' | ''
+ 'blank topic' | ' '
+ 'invalid non empty topic' | '1_5_*_#'
+ }
+
+}
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 45dba21..0ea0674 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
@@ -33,6 +33,7 @@
import org.onap.cps.ncmp.api.models.DmiPluginRegistration;
import org.onap.cps.ncmp.api.models.DmiPluginRegistrationResponse;
import org.onap.cps.ncmp.api.models.NcmpServiceCmHandle;
+import org.onap.cps.spi.FetchDescendantsOption;
import org.onap.cps.spi.model.ModuleDefinition;
import org.onap.cps.spi.model.ModuleReference;
@@ -67,6 +68,18 @@
String requestId);
/**
+ * Get resource data for operational.
+ *
+ * @param cmHandleId cm handle identifier
+ * @param resourceIdentifier resource identifier
+ * @Link FetchDescendantsOption fetch descendants option
+ * @return {@code Object} resource data
+ */
+ Object getResourceDataOperational(String cmHandleId,
+ String resourceIdentifier,
+ FetchDescendantsOption fetchDescendantsOption);
+
+ /**
* Get resource data for data store pass-through running
* using dmi.
*
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 5b072f3..3f440d6 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
@@ -23,6 +23,7 @@
package org.onap.cps.ncmp.api.impl;
+import static org.onap.cps.ncmp.api.impl.constants.DmiRegistryConstants.NFP_OPERATIONAL_DATASTORE_DATASPACE_NAME;
import static org.onap.cps.ncmp.api.impl.operations.DmiRequestBody.OperationEnum;
import static org.onap.cps.utils.CmHandleQueryRestParametersValidator.validateCmHandleQueryParameters;
@@ -57,7 +58,8 @@
import org.onap.cps.ncmp.api.models.DmiPluginRegistration;
import org.onap.cps.ncmp.api.models.DmiPluginRegistrationResponse;
import org.onap.cps.ncmp.api.models.NcmpServiceCmHandle;
-import org.onap.cps.spi.exceptions.AlreadyDefinedException;
+import org.onap.cps.spi.FetchDescendantsOption;
+import org.onap.cps.spi.exceptions.AlreadyDefinedExceptionBatch;
import org.onap.cps.spi.exceptions.CpsException;
import org.onap.cps.spi.exceptions.DataNodeNotFoundException;
import org.onap.cps.spi.exceptions.DataValidationException;
@@ -116,24 +118,32 @@
final String topicParamInQuery,
final String requestId) {
final ResponseEntity<?> responseEntity = dmiDataOperations.getResourceDataFromDmi(cmHandleId,
- resourceIdentifier,
- optionsParamInQuery,
- DmiOperations.DataStoreEnum.PASSTHROUGH_OPERATIONAL,
- requestId, topicParamInQuery);
+ resourceIdentifier,
+ optionsParamInQuery,
+ DmiOperations.DataStoreEnum.PASSTHROUGH_OPERATIONAL,
+ requestId, topicParamInQuery);
return responseEntity.getBody();
}
@Override
+ public Object getResourceDataOperational(final String cmHandleId,
+ final String resourceIdentifier,
+ final FetchDescendantsOption fetchDescendantsOption) {
+ return cpsDataService.getDataNode(NFP_OPERATIONAL_DATASTORE_DATASPACE_NAME, cmHandleId, resourceIdentifier,
+ fetchDescendantsOption);
+ }
+
+ @Override
public Object getResourceDataPassThroughRunningForCmHandle(final String cmHandleId,
final String resourceIdentifier,
final String optionsParamInQuery,
final String topicParamInQuery,
final String requestId) {
final ResponseEntity<?> responseEntity = dmiDataOperations.getResourceDataFromDmi(cmHandleId,
- resourceIdentifier,
- optionsParamInQuery,
- DmiOperations.DataStoreEnum.PASSTHROUGH_RUNNING,
- requestId, topicParamInQuery);
+ resourceIdentifier,
+ optionsParamInQuery,
+ DmiOperations.DataStoreEnum.PASSTHROUGH_RUNNING,
+ requestId, topicParamInQuery);
return responseEntity.getBody();
}
@@ -145,7 +155,7 @@
final String dataType) {
CpsValidator.validateNameCharacters(cmHandleId);
return dmiDataOperations.writeResourceDataPassThroughRunningFromDmi(cmHandleId, resourceIdentifier, operation,
- requestData, dataType);
+ requestData, dataType);
}
@Override
@@ -196,29 +206,29 @@
* Set the data sync enabled flag, along with the data sync state
* based on the data sync enabled boolean for the cm handle id provided.
*
- * @param cmHandleId cm handle id
+ * @param cmHandleId cm handle id
* @param dataSyncEnabled data sync enabled flag
*/
@Override
public void setDataSyncEnabled(final String cmHandleId, final boolean dataSyncEnabled) {
CpsValidator.validateNameCharacters(cmHandleId);
final CompositeState compositeState = inventoryPersistence
- .getCmHandleState(cmHandleId);
+ .getCmHandleState(cmHandleId);
if (compositeState.getDataSyncEnabled().equals(dataSyncEnabled)) {
log.info("Data-Sync Enabled flag is already: {} ", dataSyncEnabled);
} else if (compositeState.getCmHandleState() != CmHandleState.READY) {
throw new CpsException("State mismatch exception.", "Cm-Handle not in READY state. Cm handle state is: "
- + compositeState.getCmHandleState());
+ + compositeState.getCmHandleState());
} else {
final DataStoreSyncState dataStoreSyncState = compositeState.getDataStores()
- .getOperationalDataStore().getDataStoreSyncState();
+ .getOperationalDataStore().getDataStoreSyncState();
if (!dataSyncEnabled && dataStoreSyncState == DataStoreSyncState.SYNCHRONIZED) {
- cpsDataService.deleteDataNode("NFP-Operational", cmHandleId,
- "/netconf-state", OffsetDateTime.now());
+ cpsDataService.deleteDataNode(NFP_OPERATIONAL_DATASTORE_DATASPACE_NAME, cmHandleId,
+ "/netconf-state", OffsetDateTime.now());
}
CompositeStateUtils.setDataSyncEnabledFlagWithDataSyncState(dataSyncEnabled, compositeState);
inventoryPersistence.saveCmHandleState(cmHandleId,
- compositeState);
+ compositeState);
}
}
@@ -233,9 +243,7 @@
final Set<NcmpServiceCmHandle> ncmpServiceCmHandles =
cmHandleQueries.getCmHandlesByDmiPluginIdentifier(dmiPluginIdentifier);
final Set<String> cmHandleIds = new HashSet<>(ncmpServiceCmHandles.size());
- ncmpServiceCmHandles.forEach(cmHandle -> {
- cmHandleIds.add(cmHandle.getCmHandleId());
- });
+ ncmpServiceCmHandles.forEach(cmHandle -> cmHandleIds.add(cmHandle.getCmHandleId()));
return cmHandleIds;
}
@@ -262,7 +270,7 @@
public Map<String, String> getCmHandlePublicProperties(final String cmHandleId) {
CpsValidator.validateNameCharacters(cmHandleId);
final YangModelCmHandle yangModelCmHandle =
- inventoryPersistence.getYangModelCmHandle(cmHandleId);
+ inventoryPersistence.getYangModelCmHandle(cmHandleId);
final List<YangModelCmHandle.Property> yangModelPublicProperties = yangModelCmHandle.getPublicProperties();
final Map<String, String> cmHandlePublicProperties = new HashMap<>();
YangDataConverter.asPropertiesMap(yangModelPublicProperties, cmHandlePublicProperties);
@@ -290,14 +298,18 @@
public List<CmHandleRegistrationResponse> parseAndCreateCmHandlesInDmiRegistrationAndSyncModules(
final DmiPluginRegistration dmiPluginRegistration) {
List<CmHandleRegistrationResponse> cmHandleRegistrationResponses = new ArrayList<>();
+ final Map<YangModelCmHandle, CmHandleState> cmHandleStatePerCmHandle = new HashMap<>();
try {
- cmHandleRegistrationResponses = dmiPluginRegistration.getCreatedCmHandles().stream()
- .map(cmHandle ->
- YangModelCmHandle.toYangModelCmHandle(
- dmiPluginRegistration.getDmiPlugin(),
- dmiPluginRegistration.getDmiDataPlugin(),
- dmiPluginRegistration.getDmiModelPlugin(),
- cmHandle)).map(this::registerNewCmHandle).collect(Collectors.toList());
+ dmiPluginRegistration.getCreatedCmHandles()
+ .forEach(cmHandle -> {
+ final YangModelCmHandle yangModelCmHandle = YangModelCmHandle.toYangModelCmHandle(
+ dmiPluginRegistration.getDmiPlugin(),
+ dmiPluginRegistration.getDmiDataPlugin(),
+ dmiPluginRegistration.getDmiModelPlugin(),
+ cmHandle);
+ cmHandleStatePerCmHandle.put(yangModelCmHandle, CmHandleState.ADVISED);
+ });
+ cmHandleRegistrationResponses = registerNewCmHandles(cmHandleStatePerCmHandle);
} catch (final DataValidationException dataValidationException) {
cmHandleRegistrationResponses.add(CmHandleRegistrationResponse.createFailureResponse(dmiPluginRegistration
.getCreatedCmHandles().stream()
@@ -346,15 +358,19 @@
inventoryPersistence.deleteListOrListElement("/dmi-registry/cm-handles[@id='" + cmHandleId + "']");
}
- private CmHandleRegistrationResponse registerNewCmHandle(final YangModelCmHandle yangModelCmHandle) {
+ private List<CmHandleRegistrationResponse> registerNewCmHandles(final Map<YangModelCmHandle, CmHandleState>
+ cmHandleStatePerCmHandle) {
+ final List<String> cmHandleIds = cmHandleStatePerCmHandle.keySet().stream().map(YangModelCmHandle::getId)
+ .collect(Collectors.toList());
try {
- lcmEventsCmHandleStateHandler.updateCmHandleState(yangModelCmHandle, CmHandleState.ADVISED);
- return CmHandleRegistrationResponse.createSuccessResponse(yangModelCmHandle.getId());
- } catch (final AlreadyDefinedException alreadyDefinedException) {
- return CmHandleRegistrationResponse.createFailureResponse(
- yangModelCmHandle.getId(), RegistrationError.CM_HANDLE_ALREADY_EXIST);
+ lcmEventsCmHandleStateHandler.updateCmHandleStateBatch(cmHandleStatePerCmHandle);
+ return CmHandleRegistrationResponse.createSuccessResponses(cmHandleIds);
+ } catch (final AlreadyDefinedExceptionBatch alreadyDefinedExceptionBatch) {
+ return CmHandleRegistrationResponse.createFailureResponses(
+ alreadyDefinedExceptionBatch.getAlreadyDefinedXpaths(),
+ RegistrationError.CM_HANDLE_ALREADY_EXIST);
} catch (final Exception exception) {
- return CmHandleRegistrationResponse.createFailureResponse(yangModelCmHandle.getId(), exception);
+ return CmHandleRegistrationResponse.createFailureResponses(cmHandleIds, exception);
}
}
}
diff --git a/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/client/DmiRestClient.java b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/client/DmiRestClient.java
index d457f26..d5b459b 100644
--- a/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/client/DmiRestClient.java
+++ b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/client/DmiRestClient.java
@@ -56,7 +56,7 @@
} catch (final HttpStatusCodeException httpStatusCodeException) {
final String exceptionMessage = "Unable to " + operation.toString() + " resource data.";
throw new HttpClientRequestException(exceptionMessage, httpStatusCodeException.getResponseBodyAsString(),
- httpStatusCodeException.getRawStatusCode());
+ httpStatusCodeException.getRawStatusCode());
}
}
diff --git a/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/yangmodels/YangModelCmHandle.java b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/yangmodels/YangModelCmHandle.java
index 45e2754..f842ddb 100644
--- a/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/yangmodels/YangModelCmHandle.java
+++ b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/yangmodels/YangModelCmHandle.java
@@ -30,6 +30,7 @@
import java.util.Map;
import lombok.AllArgsConstructor;
import lombok.Data;
+import lombok.EqualsAndHashCode;
import lombok.Getter;
import lombok.NoArgsConstructor;
import lombok.Setter;
@@ -46,8 +47,10 @@
@Setter
@NoArgsConstructor
@JsonInclude(Include.NON_NULL)
+@EqualsAndHashCode(onlyExplicitlyIncluded = true)
public class YangModelCmHandle {
+ @EqualsAndHashCode.Include
private String id;
@JsonProperty("dmi-service-name")
diff --git a/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/inventory/CmHandleQueries.java b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/inventory/CmHandleQueries.java
index 569e91e..daabbb5 100644
--- a/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/inventory/CmHandleQueries.java
+++ b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/inventory/CmHandleQueries.java
@@ -20,37 +20,14 @@
package org.onap.cps.ncmp.api.inventory;
-import static org.onap.cps.ncmp.api.impl.utils.YangDataConverter.convertYangModelCmHandleToNcmpServiceCmHandle;
-import static org.onap.cps.spi.FetchDescendantsOption.INCLUDE_ALL_DESCENDANTS;
-import static org.onap.cps.spi.FetchDescendantsOption.OMIT_DESCENDANTS;
-
-import java.util.Collection;
-import java.util.Collections;
-import java.util.HashMap;
-import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
-import java.util.stream.Collectors;
-import lombok.RequiredArgsConstructor;
-import org.onap.cps.ncmp.api.impl.utils.YangDataConverter;
import org.onap.cps.ncmp.api.models.NcmpServiceCmHandle;
-import org.onap.cps.spi.CpsDataPersistenceService;
import org.onap.cps.spi.FetchDescendantsOption;
import org.onap.cps.spi.model.DataNode;
-import org.springframework.stereotype.Component;
-@RequiredArgsConstructor
-@Component
-public class CmHandleQueries {
-
- private static final String NCMP_DATASPACE_NAME = "NCMP-Admin";
- private static final String NCMP_DMI_REGISTRY_ANCHOR = "ncmp-dmi-registry";
-
- private final CpsDataPersistenceService cpsDataPersistenceService;
- private static final Map<String, NcmpServiceCmHandle> NO_QUERY_TO_EXECUTE = null;
- private static final String ANCESTOR_CM_HANDLES = "/ancestor::cm-handles";
-
+public interface CmHandleQueries {
/**
* Query CmHandles based on PublicProperties.
@@ -58,52 +35,17 @@
* @param publicPropertyQueryPairs public properties for query
* @return CmHandles which have these public properties
*/
- public Map<String, NcmpServiceCmHandle> queryCmHandlePublicProperties(
- final Map<String, String> publicPropertyQueryPairs) {
- if (publicPropertyQueryPairs.isEmpty()) {
- return Collections.emptyMap();
- }
- Map<String, NcmpServiceCmHandle> cmHandleIdToNcmpServiceCmHandles = null;
- for (final Map.Entry<String, String> publicPropertyQueryPair : publicPropertyQueryPairs.entrySet()) {
- final String cpsPath = "//public-properties[@name=\"" + publicPropertyQueryPair.getKey()
- + "\" and @value=\"" + publicPropertyQueryPair.getValue() + "\"]";
-
- final Collection<DataNode> dataNodes = queryCmHandleDataNodesByCpsPath(cpsPath, INCLUDE_ALL_DESCENDANTS);
- if (cmHandleIdToNcmpServiceCmHandles == null) {
- cmHandleIdToNcmpServiceCmHandles = collectDataNodesToNcmpServiceCmHandles(dataNodes);
- } else {
- final Collection<String> cmHandleIdsToRetain = dataNodes.parallelStream()
- .map(dataNode -> dataNode.getLeaves().get("id").toString()).collect(Collectors.toSet());
- cmHandleIdToNcmpServiceCmHandles.keySet().retainAll(cmHandleIdsToRetain);
- }
- if (cmHandleIdToNcmpServiceCmHandles.isEmpty()) {
- break;
- }
- }
- return cmHandleIdToNcmpServiceCmHandles;
- }
+ Map<String, NcmpServiceCmHandle> queryCmHandlePublicProperties(Map<String, String> publicPropertyQueryPairs);
/**
* Combine Maps of CmHandles.
*
- * @param firstQuery first CmHandles Map
+ * @param firstQuery first CmHandles Map
* @param secondQuery second CmHandles Map
* @return combined Map of CmHandles
*/
- public Map<String, NcmpServiceCmHandle> combineCmHandleQueries(
- final Map<String, NcmpServiceCmHandle> firstQuery,
- final Map<String, NcmpServiceCmHandle> secondQuery) {
- if (firstQuery == NO_QUERY_TO_EXECUTE && secondQuery == NO_QUERY_TO_EXECUTE) {
- return NO_QUERY_TO_EXECUTE;
- } else if (firstQuery == NO_QUERY_TO_EXECUTE) {
- return secondQuery;
- } else if (secondQuery == NO_QUERY_TO_EXECUTE) {
- return firstQuery;
- } else {
- firstQuery.keySet().retainAll(secondQuery.keySet());
- return firstQuery;
- }
- }
+ Map<String, NcmpServiceCmHandle> combineCmHandleQueries(Map<String, NcmpServiceCmHandle> firstQuery,
+ Map<String, NcmpServiceCmHandle> secondQuery);
/**
* Method which returns cm handles by the cm handles state.
@@ -111,10 +53,7 @@
* @param cmHandleState cm handle state
* @return a list of cm handles
*/
- public List<DataNode> queryCmHandlesByState(final CmHandleState cmHandleState) {
- return queryCmHandleDataNodesByCpsPath("//state[@cm-handle-state=\"" + cmHandleState + "\"]",
- INCLUDE_ALL_DESCENDANTS);
- }
+ List<DataNode> queryCmHandlesByState(CmHandleState cmHandleState);
/**
* Method to return data nodes representing the cm handles.
@@ -122,49 +61,24 @@
* @param cpsPath cps path for which the cmHandle is requested
* @return a list of data nodes representing the cm handles.
*/
- public List<DataNode> queryCmHandleDataNodesByCpsPath(final String cpsPath,
- final FetchDescendantsOption fetchDescendantsOption) {
- return cpsDataPersistenceService.queryDataNodes(NCMP_DATASPACE_NAME, NCMP_DMI_REGISTRY_ANCHOR,
- cpsPath + ANCESTOR_CM_HANDLES, fetchDescendantsOption);
- }
+ List<DataNode> queryCmHandleDataNodesByCpsPath(String cpsPath, FetchDescendantsOption fetchDescendantsOption);
/**
* Method to check the state of a cm handle with given id.
*
- * @param cmHandleId cm handle id
+ * @param cmHandleId cm handle id
* @param requiredCmHandleState the required state of the cm handle
* @return a boolean, true if the state is equal to the required state
*/
- public boolean cmHandleHasState(final String cmHandleId, final CmHandleState requiredCmHandleState) {
- final DataNode stateDataNode = getCmHandleState(cmHandleId);
- final String cmHandleStateAsString = (String) stateDataNode.getLeaves().get("cm-handle-state");
- return CmHandleState.valueOf(cmHandleStateAsString).equals(requiredCmHandleState);
- }
+ boolean cmHandleHasState(String cmHandleId, CmHandleState requiredCmHandleState);
/**
* Method which returns cm handles by the operational sync state of cm handle.
+ *
* @param dataStoreSyncState sync state
* @return a list of cm handles
*/
- public List<DataNode> queryCmHandlesByOperationalSyncState(final DataStoreSyncState dataStoreSyncState) {
- return queryCmHandleDataNodesByCpsPath("//state/datastores" + "/operational[@sync-state=\""
- + dataStoreSyncState + "\"]", FetchDescendantsOption.OMIT_DESCENDANTS);
- }
-
- private Map<String, NcmpServiceCmHandle> collectDataNodesToNcmpServiceCmHandles(
- final Collection<DataNode> dataNodes) {
- final Map<String, NcmpServiceCmHandle> cmHandleIdToNcmpServiceCmHandle = new HashMap<>();
- dataNodes.forEach(dataNode -> {
- final NcmpServiceCmHandle ncmpServiceCmHandle = createNcmpServiceCmHandle(dataNode);
- cmHandleIdToNcmpServiceCmHandle.put(ncmpServiceCmHandle.getCmHandleId(), ncmpServiceCmHandle);
- });
- return cmHandleIdToNcmpServiceCmHandle;
- }
-
- private NcmpServiceCmHandle createNcmpServiceCmHandle(final DataNode dataNode) {
- return convertYangModelCmHandleToNcmpServiceCmHandle(YangDataConverter
- .convertCmHandleToYangModel(dataNode, dataNode.getLeaves().get("id").toString()));
- }
+ List<DataNode> queryCmHandlesByOperationalSyncState(DataStoreSyncState dataStoreSyncState);
/**
* Get all cm handles by DMI plugin identifier.
@@ -172,34 +86,5 @@
* @param dmiPluginIdentifier DMI plugin identifier
* @return set of cm handles
*/
- public Set<NcmpServiceCmHandle> getCmHandlesByDmiPluginIdentifier(final String dmiPluginIdentifier) {
- final Map<String, DataNode> cmHandleAsDataNodePerCmHandleId = new HashMap<>();
- for (final ModelledDmiServiceLeaves modelledDmiServiceLeaf : ModelledDmiServiceLeaves.values()) {
- for (final DataNode cmHandleAsDataNode: getCmHandlesByDmiPluginIdentifierAndDmiProperty(
- dmiPluginIdentifier,
- modelledDmiServiceLeaf.getLeafName())) {
- cmHandleAsDataNodePerCmHandleId.put(
- cmHandleAsDataNode.getLeaves().get("id").toString(), cmHandleAsDataNode);
- }
- }
- final Set<NcmpServiceCmHandle> ncmpServiceCmHandles = new HashSet<>(cmHandleAsDataNodePerCmHandleId.size());
- cmHandleAsDataNodePerCmHandleId.values().forEach(
- cmHandleAsDataNode -> ncmpServiceCmHandles.add(createNcmpServiceCmHandle(cmHandleAsDataNode)));
- return ncmpServiceCmHandles;
- }
-
- private List<DataNode> getCmHandlesByDmiPluginIdentifierAndDmiProperty(final String dmiPluginIdentifier,
- final String dmiProperty) {
- return cpsDataPersistenceService.queryDataNodes(NCMP_DATASPACE_NAME, NCMP_DMI_REGISTRY_ANCHOR,
- "/dmi-registry/cm-handles[@" + dmiProperty + "='" + dmiPluginIdentifier + "']",
- OMIT_DESCENDANTS);
- }
-
- private DataNode getCmHandleState(final String cmHandleId) {
- final String xpath = "/dmi-registry/cm-handles[@id='" + cmHandleId + "']/state";
- return cpsDataPersistenceService.getDataNode(NCMP_DATASPACE_NAME, NCMP_DMI_REGISTRY_ANCHOR,
- xpath, OMIT_DESCENDANTS);
- }
+ Set<NcmpServiceCmHandle> getCmHandlesByDmiPluginIdentifier(String dmiPluginIdentifier);
}
-
-
diff --git a/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/inventory/CmHandleQueriesImpl.java b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/inventory/CmHandleQueriesImpl.java
new file mode 100644
index 0000000..e9e2fca
--- /dev/null
+++ b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/inventory/CmHandleQueriesImpl.java
@@ -0,0 +1,168 @@
+/*
+ * ============LICENSE_START=======================================================
+ * Copyright (C) 2022 Nordix Foundation
+ * ================================================================================
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ * ============LICENSE_END=========================================================
+ */
+
+package org.onap.cps.ncmp.api.inventory;
+
+import static org.onap.cps.ncmp.api.impl.utils.YangDataConverter.convertYangModelCmHandleToNcmpServiceCmHandle;
+import static org.onap.cps.spi.FetchDescendantsOption.INCLUDE_ALL_DESCENDANTS;
+import static org.onap.cps.spi.FetchDescendantsOption.OMIT_DESCENDANTS;
+
+import java.util.Collection;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+import java.util.stream.Collectors;
+import lombok.RequiredArgsConstructor;
+import org.onap.cps.ncmp.api.impl.utils.YangDataConverter;
+import org.onap.cps.ncmp.api.models.NcmpServiceCmHandle;
+import org.onap.cps.spi.CpsDataPersistenceService;
+import org.onap.cps.spi.FetchDescendantsOption;
+import org.onap.cps.spi.model.DataNode;
+import org.springframework.stereotype.Component;
+
+@RequiredArgsConstructor
+@Component
+public class CmHandleQueriesImpl implements CmHandleQueries {
+
+ private static final String NCMP_DATASPACE_NAME = "NCMP-Admin";
+ private static final String NCMP_DMI_REGISTRY_ANCHOR = "ncmp-dmi-registry";
+
+ private final CpsDataPersistenceService cpsDataPersistenceService;
+ private static final Map<String, NcmpServiceCmHandle> NO_QUERY_TO_EXECUTE = null;
+ private static final String ANCESTOR_CM_HANDLES = "/ancestor::cm-handles";
+
+
+ @Override
+ public Map<String, NcmpServiceCmHandle> queryCmHandlePublicProperties(
+ final Map<String, String> publicPropertyQueryPairs) {
+ if (publicPropertyQueryPairs.isEmpty()) {
+ return Collections.emptyMap();
+ }
+ Map<String, NcmpServiceCmHandle> cmHandleIdToNcmpServiceCmHandles = null;
+ for (final Map.Entry<String, String> publicPropertyQueryPair : publicPropertyQueryPairs.entrySet()) {
+ final String cpsPath = "//public-properties[@name=\"" + publicPropertyQueryPair.getKey()
+ + "\" and @value=\"" + publicPropertyQueryPair.getValue() + "\"]";
+
+ final Collection<DataNode> dataNodes = queryCmHandleDataNodesByCpsPath(cpsPath, INCLUDE_ALL_DESCENDANTS);
+ if (cmHandleIdToNcmpServiceCmHandles == null) {
+ cmHandleIdToNcmpServiceCmHandles = collectDataNodesToNcmpServiceCmHandles(dataNodes);
+ } else {
+ final Collection<String> cmHandleIdsToRetain = dataNodes.parallelStream()
+ .map(dataNode -> dataNode.getLeaves().get("id").toString()).collect(Collectors.toSet());
+ cmHandleIdToNcmpServiceCmHandles.keySet().retainAll(cmHandleIdsToRetain);
+ }
+ if (cmHandleIdToNcmpServiceCmHandles.isEmpty()) {
+ break;
+ }
+ }
+ return cmHandleIdToNcmpServiceCmHandles;
+ }
+
+ @Override
+ public Map<String, NcmpServiceCmHandle> combineCmHandleQueries(final Map<String, NcmpServiceCmHandle> firstQuery,
+ final Map<String, NcmpServiceCmHandle> secondQuery) {
+ if (firstQuery == NO_QUERY_TO_EXECUTE && secondQuery == NO_QUERY_TO_EXECUTE) {
+ return NO_QUERY_TO_EXECUTE;
+ } else if (firstQuery == NO_QUERY_TO_EXECUTE) {
+ return secondQuery;
+ } else if (secondQuery == NO_QUERY_TO_EXECUTE) {
+ return firstQuery;
+ } else {
+ firstQuery.keySet().retainAll(secondQuery.keySet());
+ return firstQuery;
+ }
+ }
+
+ @Override
+ public List<DataNode> queryCmHandlesByState(final CmHandleState cmHandleState) {
+ return queryCmHandleDataNodesByCpsPath("//state[@cm-handle-state=\"" + cmHandleState + "\"]",
+ INCLUDE_ALL_DESCENDANTS);
+ }
+
+ @Override
+ public List<DataNode> queryCmHandleDataNodesByCpsPath(final String cpsPath,
+ final FetchDescendantsOption fetchDescendantsOption) {
+ return cpsDataPersistenceService.queryDataNodes(NCMP_DATASPACE_NAME, NCMP_DMI_REGISTRY_ANCHOR,
+ cpsPath + ANCESTOR_CM_HANDLES, fetchDescendantsOption);
+ }
+
+ @Override
+ public boolean cmHandleHasState(final String cmHandleId, final CmHandleState requiredCmHandleState) {
+ final DataNode stateDataNode = getCmHandleState(cmHandleId);
+ final String cmHandleStateAsString = (String) stateDataNode.getLeaves().get("cm-handle-state");
+ return CmHandleState.valueOf(cmHandleStateAsString).equals(requiredCmHandleState);
+ }
+
+ @Override
+ public List<DataNode> queryCmHandlesByOperationalSyncState(final DataStoreSyncState dataStoreSyncState) {
+ return queryCmHandleDataNodesByCpsPath("//state/datastores" + "/operational[@sync-state=\""
+ + dataStoreSyncState + "\"]", FetchDescendantsOption.OMIT_DESCENDANTS);
+ }
+
+ private Map<String, NcmpServiceCmHandle> collectDataNodesToNcmpServiceCmHandles(
+ final Collection<DataNode> dataNodes) {
+ final Map<String, NcmpServiceCmHandle> cmHandleIdToNcmpServiceCmHandle = new HashMap<>();
+ dataNodes.forEach(dataNode -> {
+ final NcmpServiceCmHandle ncmpServiceCmHandle = createNcmpServiceCmHandle(dataNode);
+ cmHandleIdToNcmpServiceCmHandle.put(ncmpServiceCmHandle.getCmHandleId(), ncmpServiceCmHandle);
+ });
+ return cmHandleIdToNcmpServiceCmHandle;
+ }
+
+ private NcmpServiceCmHandle createNcmpServiceCmHandle(final DataNode dataNode) {
+ return convertYangModelCmHandleToNcmpServiceCmHandle(YangDataConverter
+ .convertCmHandleToYangModel(dataNode, dataNode.getLeaves().get("id").toString()));
+ }
+
+ @Override
+ public Set<NcmpServiceCmHandle> getCmHandlesByDmiPluginIdentifier(final String dmiPluginIdentifier) {
+ final Map<String, DataNode> cmHandleAsDataNodePerCmHandleId = new HashMap<>();
+ for (final ModelledDmiServiceLeaves modelledDmiServiceLeaf : ModelledDmiServiceLeaves.values()) {
+ for (final DataNode cmHandleAsDataNode: getCmHandlesByDmiPluginIdentifierAndDmiProperty(
+ dmiPluginIdentifier,
+ modelledDmiServiceLeaf.getLeafName())) {
+ cmHandleAsDataNodePerCmHandleId.put(
+ cmHandleAsDataNode.getLeaves().get("id").toString(), cmHandleAsDataNode);
+ }
+ }
+ final Set<NcmpServiceCmHandle> ncmpServiceCmHandles = new HashSet<>(cmHandleAsDataNodePerCmHandleId.size());
+ cmHandleAsDataNodePerCmHandleId.values().forEach(
+ cmHandleAsDataNode -> ncmpServiceCmHandles.add(createNcmpServiceCmHandle(cmHandleAsDataNode)));
+ return ncmpServiceCmHandles;
+ }
+
+ private List<DataNode> getCmHandlesByDmiPluginIdentifierAndDmiProperty(final String dmiPluginIdentifier,
+ final String dmiProperty) {
+ return cpsDataPersistenceService.queryDataNodes(NCMP_DATASPACE_NAME, NCMP_DMI_REGISTRY_ANCHOR,
+ "/dmi-registry/cm-handles[@" + dmiProperty + "='" + dmiPluginIdentifier + "']",
+ OMIT_DESCENDANTS);
+ }
+
+ private DataNode getCmHandleState(final String cmHandleId) {
+ final String xpath = "/dmi-registry/cm-handles[@id='" + cmHandleId + "']/state";
+ return cpsDataPersistenceService.getDataNode(NCMP_DATASPACE_NAME, NCMP_DMI_REGISTRY_ANCHOR,
+ xpath, OMIT_DESCENDANTS);
+ }
+}
+
+
diff --git a/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/inventory/InventoryPersistence.java b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/inventory/InventoryPersistence.java
index 9174dc7..bfc3a9a 100644
--- a/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/inventory/InventoryPersistence.java
+++ b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/inventory/InventoryPersistence.java
@@ -1,7 +1,6 @@
/*
* ============LICENSE_START=======================================================
* Copyright (C) 2022 Nordix Foundation
- * Modifications Copyright (C) 2022 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,57 +20,15 @@
package org.onap.cps.ncmp.api.inventory;
-import static org.onap.cps.ncmp.api.impl.constants.DmiRegistryConstants.NFP_OPERATIONAL_DATASTORE_DATASPACE_NAME;
-import static org.onap.cps.ncmp.api.impl.constants.DmiRegistryConstants.NO_TIMESTAMP;
-import static org.onap.cps.spi.CascadeDeleteAllowed.CASCADE_DELETE_ALLOWED;
-import static org.onap.cps.spi.FetchDescendantsOption.INCLUDE_ALL_DESCENDANTS;
-
-import java.time.OffsetDateTime;
-import java.util.ArrayList;
import java.util.Collection;
-import java.util.HashMap;
-import java.util.List;
import java.util.Map;
-import lombok.RequiredArgsConstructor;
-import lombok.extern.slf4j.Slf4j;
-import org.onap.cps.api.CpsDataService;
-import org.onap.cps.api.CpsModuleService;
-import org.onap.cps.ncmp.api.impl.utils.YangDataConverter;
import org.onap.cps.ncmp.api.impl.yangmodels.YangModelCmHandle;
-import org.onap.cps.spi.CpsAdminPersistenceService;
-import org.onap.cps.spi.CpsDataPersistenceService;
-import org.onap.cps.spi.FetchDescendantsOption;
-import org.onap.cps.spi.exceptions.SchemaSetNotFoundException;
import org.onap.cps.spi.model.Anchor;
import org.onap.cps.spi.model.DataNode;
import org.onap.cps.spi.model.ModuleDefinition;
import org.onap.cps.spi.model.ModuleReference;
-import org.onap.cps.utils.CpsValidator;
-import org.onap.cps.utils.JsonObjectMapper;
-import org.springframework.stereotype.Component;
-@Slf4j
-@RequiredArgsConstructor
-@Component
-public class InventoryPersistence {
-
- private static final String NCMP_DATASPACE_NAME = "NCMP-Admin";
-
- private static final String NCMP_DMI_REGISTRY_ANCHOR = "ncmp-dmi-registry";
-
- private static final String NCMP_DMI_REGISTRY_PARENT = "/dmi-registry";
-
- private static final String CM_HANDLE_XPATH_TEMPLATE = "/dmi-registry/cm-handles[@id='" + "%s" + "']";
-
- private final JsonObjectMapper jsonObjectMapper;
-
- private final CpsDataService cpsDataService;
-
- private final CpsModuleService cpsModuleService;
-
- private final CpsDataPersistenceService cpsDataPersistenceService;
-
- private final CpsAdminPersistenceService cpsAdminPersistenceService;
+public interface InventoryPersistence {
/**
* Get the Cm Handle Composite State from the data node.
@@ -79,50 +36,30 @@
* @param cmHandleId cm handle id
* @return the cm handle composite state
*/
- public CompositeState getCmHandleState(final String cmHandleId) {
- final DataNode stateAsDataNode = cpsDataService.getDataNode(NCMP_DATASPACE_NAME, NCMP_DMI_REGISTRY_ANCHOR,
- String.format(CM_HANDLE_XPATH_TEMPLATE, cmHandleId) + "/state",
- FetchDescendantsOption.INCLUDE_ALL_DESCENDANTS);
- return new CompositeStateBuilder().fromDataNode(stateAsDataNode).build();
- }
+ CompositeState getCmHandleState(String cmHandleId);
/**
* Save the cm handles state.
*
- * @param cmHandleId cm handle id
+ * @param cmHandleId cm handle id
* @param compositeState composite state
*/
- public void saveCmHandleState(final String cmHandleId, final CompositeState compositeState) {
- final String cmHandleJsonData = String.format("{\"state\":%s}",
- jsonObjectMapper.asJsonString(compositeState));
- cpsDataService.updateDataNodeAndDescendants(NCMP_DATASPACE_NAME, NCMP_DMI_REGISTRY_ANCHOR,
- String.format(CM_HANDLE_XPATH_TEMPLATE, cmHandleId),
- cmHandleJsonData, OffsetDateTime.now());
- }
+ void saveCmHandleState(String cmHandleId, CompositeState compositeState);
/**
* Save all cm handles states in batch.
*
* @param cmHandleStatePerCmHandleId contains cm handle id and updated state
*/
- public void saveCmHandleStateBatch(final Map<String, CompositeState> cmHandleStatePerCmHandleId) {
- final Map<String, String> cmHandlesJsonDataMap = new HashMap<>();
- cmHandleStatePerCmHandleId.forEach((cmHandleId, compositeState) -> cmHandlesJsonDataMap.put(
- String.format(CM_HANDLE_XPATH_TEMPLATE, cmHandleId),
- String.format("{\"state\":%s}", jsonObjectMapper.asJsonString(compositeState))));
- cpsDataService.updateDataNodesAndDescendants(NCMP_DATASPACE_NAME, NCMP_DMI_REGISTRY_ANCHOR,
- cmHandlesJsonDataMap, OffsetDateTime.now());
- }
+ void saveCmHandleStateBatch(Map<String, CompositeState> cmHandleStatePerCmHandleId);
/**
* This method retrieves DMI service name, DMI properties and the state for a given cm handle.
+ *
* @param cmHandleId the id of the cm handle
* @return yang model cm handle
*/
- public YangModelCmHandle getYangModelCmHandle(final String cmHandleId) {
- CpsValidator.validateNameCharacters(cmHandleId);
- return YangDataConverter.convertCmHandleToYangModel(getCmHandleDataNode(cmHandleId), cmHandleId);
- }
+ YangModelCmHandle getYangModelCmHandle(String cmHandleId);
/**
* Method to return module definitions by cmHandleId.
@@ -130,9 +67,7 @@
* @param cmHandleId cm handle ID
* @return a collection of module definitions (moduleName, revision and yang resource content)
*/
- public Collection<ModuleDefinition> getModuleDefinitionsByCmHandleId(final String cmHandleId) {
- return cpsModuleService.getModuleDefinitionsByAnchorName(NFP_OPERATIONAL_DATASTORE_DATASPACE_NAME, cmHandleId);
- }
+ Collection<ModuleDefinition> getModuleDefinitionsByCmHandleId(String cmHandleId);
/**
* Method to return module references by cmHandleId.
@@ -140,60 +75,35 @@
* @param cmHandleId cm handle ID
* @return a collection of module references (moduleName and revision)
*/
- public Collection<ModuleReference> getYangResourcesModuleReferences(final String cmHandleId) {
- CpsValidator.validateNameCharacters(cmHandleId);
- return cpsModuleService.getYangResourcesModuleReferences(NFP_OPERATIONAL_DATASTORE_DATASPACE_NAME, cmHandleId);
- }
+ Collection<ModuleReference> getYangResourcesModuleReferences(String cmHandleId);
/**
* Method to save cmHandle.
*
* @param yangModelCmHandle cmHandle represented as Yang Model
*/
- public void saveCmHandle(final YangModelCmHandle yangModelCmHandle) {
- final String cmHandleJsonData =
- String.format("{\"cm-handles\":[%s]}", jsonObjectMapper.asJsonString(yangModelCmHandle));
- cpsDataService.saveListElements(NCMP_DATASPACE_NAME, NCMP_DMI_REGISTRY_ANCHOR, NCMP_DMI_REGISTRY_PARENT,
- cmHandleJsonData, NO_TIMESTAMP);
- }
+ void saveCmHandle(YangModelCmHandle yangModelCmHandle);
/**
* Method to save batch of cm handles.
*
* @param yangModelCmHandles cm handle represented as Yang Models
*/
- public void saveCmHandleBatch(final Collection<YangModelCmHandle> yangModelCmHandles) {
- final List<String> cmHandlesJsonData = new ArrayList<>();
- yangModelCmHandles.forEach(yangModelCmHandle -> cmHandlesJsonData.add(
- String.format("{\"cm-handles\":[%s]}", jsonObjectMapper.asJsonString(yangModelCmHandle))));
- cpsDataService.saveListElementsBatch(NCMP_DATASPACE_NAME, NCMP_DMI_REGISTRY_ANCHOR,
- NCMP_DMI_REGISTRY_PARENT, cmHandlesJsonData, NO_TIMESTAMP);
- }
+ void saveCmHandleBatch(Collection<YangModelCmHandle> yangModelCmHandles);
/**
* Method to delete a list or a list element.
*
* @param listElementXpath list element xPath
*/
- public void deleteListOrListElement(final String listElementXpath) {
- cpsDataService.deleteListOrListElement(NCMP_DATASPACE_NAME, NCMP_DMI_REGISTRY_ANCHOR,
- listElementXpath, NO_TIMESTAMP);
- }
+ void deleteListOrListElement(String listElementXpath);
/**
* Method to delete a schema set.
*
* @param schemaSetName schema set name
*/
- public void deleteSchemaSetWithCascade(final String schemaSetName) {
- try {
- CpsValidator.validateNameCharacters(schemaSetName);
- cpsModuleService.deleteSchemaSet(NFP_OPERATIONAL_DATASTORE_DATASPACE_NAME, schemaSetName,
- CASCADE_DELETE_ALLOWED);
- } catch (final SchemaSetNotFoundException schemaSetNotFoundException) {
- log.warn("Schema set {} does not exist or already deleted", schemaSetName);
- }
- }
+ void deleteSchemaSetWithCascade(String schemaSetName);
/**
* Get data node via xpath.
@@ -201,10 +111,7 @@
* @param xpath xpath
* @return data node
*/
- public DataNode getDataNode(final String xpath) {
- return cpsDataPersistenceService.getDataNode(NCMP_DATASPACE_NAME, NCMP_DMI_REGISTRY_ANCHOR,
- xpath, INCLUDE_ALL_DESCENDANTS);
- }
+ DataNode getDataNode(String xpath);
/**
* Get data node of given cm handle.
@@ -212,9 +119,7 @@
* @param cmHandleId cmHandle ID
* @return data node
*/
- public DataNode getCmHandleDataNode(final String cmHandleId) {
- return this.getDataNode(String.format(CM_HANDLE_XPATH_TEMPLATE, cmHandleId));
- }
+ DataNode getCmHandleDataNode(String cmHandleId);
/**
* Query anchors via module names.
@@ -222,37 +127,27 @@
* @param moduleNamesForQuery module names
* @return Collection of anchors
*/
- public Collection<Anchor> queryAnchors(final Collection<String> moduleNamesForQuery) {
- return cpsAdminPersistenceService.queryAnchors(NFP_OPERATIONAL_DATASTORE_DATASPACE_NAME, moduleNamesForQuery);
- }
+ Collection<Anchor> queryAnchors(Collection<String> moduleNamesForQuery);
/**
* Method to get all anchors.
*
* @return Collection of anchors
*/
- public Collection<Anchor> getAnchors() {
- return cpsAdminPersistenceService.getAnchors(NFP_OPERATIONAL_DATASTORE_DATASPACE_NAME);
- }
+ Collection<Anchor> getAnchors();
/**
* Replaces list content by removing all existing elements and inserting the given new elements as data nodes.
*
- * @param parentNodeXpath parent node xpath
- * @param dataNodes datanodes representing the updated data
+ * @param parentNodeXpath parent node xpath
+ * @param dataNodes datanodes representing the updated data
*/
- public void replaceListContent(final String parentNodeXpath, final Collection<DataNode> dataNodes) {
- cpsDataService.replaceListContent(NCMP_DATASPACE_NAME, NCMP_DMI_REGISTRY_ANCHOR,
- parentNodeXpath, dataNodes, NO_TIMESTAMP);
- }
+ void replaceListContent(String parentNodeXpath, Collection<DataNode> dataNodes);
/**
* Deletes data node for given anchor and dataspace.
*
* @param dataNodeXpath data node xpath
*/
- public void deleteDataNode(final String dataNodeXpath) {
- cpsDataService.deleteDataNode(NCMP_DATASPACE_NAME, NCMP_DMI_REGISTRY_ANCHOR, dataNodeXpath,
- NO_TIMESTAMP);
- }
-}
\ No newline at end of file
+ void deleteDataNode(String dataNodeXpath);
+}
diff --git a/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/inventory/InventoryPersistenceImpl.java b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/inventory/InventoryPersistenceImpl.java
new file mode 100644
index 0000000..99edfdb
--- /dev/null
+++ b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/inventory/InventoryPersistenceImpl.java
@@ -0,0 +1,186 @@
+/*
+ * ============LICENSE_START=======================================================
+ * Copyright (C) 2022 Nordix Foundation
+ * Modifications Copyright (C) 2022 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.api.inventory;
+
+import static org.onap.cps.ncmp.api.impl.constants.DmiRegistryConstants.NFP_OPERATIONAL_DATASTORE_DATASPACE_NAME;
+import static org.onap.cps.ncmp.api.impl.constants.DmiRegistryConstants.NO_TIMESTAMP;
+import static org.onap.cps.spi.CascadeDeleteAllowed.CASCADE_DELETE_ALLOWED;
+import static org.onap.cps.spi.FetchDescendantsOption.INCLUDE_ALL_DESCENDANTS;
+
+import java.time.OffsetDateTime;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import lombok.RequiredArgsConstructor;
+import lombok.extern.slf4j.Slf4j;
+import org.onap.cps.api.CpsDataService;
+import org.onap.cps.api.CpsModuleService;
+import org.onap.cps.ncmp.api.impl.utils.YangDataConverter;
+import org.onap.cps.ncmp.api.impl.yangmodels.YangModelCmHandle;
+import org.onap.cps.spi.CpsAdminPersistenceService;
+import org.onap.cps.spi.CpsDataPersistenceService;
+import org.onap.cps.spi.FetchDescendantsOption;
+import org.onap.cps.spi.exceptions.SchemaSetNotFoundException;
+import org.onap.cps.spi.model.Anchor;
+import org.onap.cps.spi.model.DataNode;
+import org.onap.cps.spi.model.ModuleDefinition;
+import org.onap.cps.spi.model.ModuleReference;
+import org.onap.cps.utils.CpsValidator;
+import org.onap.cps.utils.JsonObjectMapper;
+import org.springframework.stereotype.Component;
+
+@Slf4j
+@RequiredArgsConstructor
+@Component
+public class InventoryPersistenceImpl implements InventoryPersistence {
+
+ private static final String NCMP_DATASPACE_NAME = "NCMP-Admin";
+
+ private static final String NCMP_DMI_REGISTRY_ANCHOR = "ncmp-dmi-registry";
+
+ private static final String NCMP_DMI_REGISTRY_PARENT = "/dmi-registry";
+
+ private static final String CM_HANDLE_XPATH_TEMPLATE = "/dmi-registry/cm-handles[@id='" + "%s" + "']";
+
+ private final JsonObjectMapper jsonObjectMapper;
+
+ private final CpsDataService cpsDataService;
+
+ private final CpsModuleService cpsModuleService;
+
+ private final CpsDataPersistenceService cpsDataPersistenceService;
+
+ private final CpsAdminPersistenceService cpsAdminPersistenceService;
+
+ @Override
+ public CompositeState getCmHandleState(final String cmHandleId) {
+ final DataNode stateAsDataNode = cpsDataService.getDataNode(NCMP_DATASPACE_NAME, NCMP_DMI_REGISTRY_ANCHOR,
+ String.format(CM_HANDLE_XPATH_TEMPLATE, cmHandleId) + "/state",
+ FetchDescendantsOption.INCLUDE_ALL_DESCENDANTS);
+ return new CompositeStateBuilder().fromDataNode(stateAsDataNode).build();
+ }
+
+ @Override
+ public void saveCmHandleState(final String cmHandleId, final CompositeState compositeState) {
+ final String cmHandleJsonData = String.format("{\"state\":%s}",
+ jsonObjectMapper.asJsonString(compositeState));
+ cpsDataService.updateDataNodeAndDescendants(NCMP_DATASPACE_NAME, NCMP_DMI_REGISTRY_ANCHOR,
+ String.format(CM_HANDLE_XPATH_TEMPLATE, cmHandleId),
+ cmHandleJsonData, OffsetDateTime.now());
+ }
+
+ @Override
+ public void saveCmHandleStateBatch(final Map<String, CompositeState> cmHandleStatePerCmHandleId) {
+ final Map<String, String> cmHandlesJsonDataMap = new HashMap<>();
+ cmHandleStatePerCmHandleId.forEach((cmHandleId, compositeState) -> cmHandlesJsonDataMap.put(
+ String.format(CM_HANDLE_XPATH_TEMPLATE, cmHandleId),
+ String.format("{\"state\":%s}", jsonObjectMapper.asJsonString(compositeState))));
+ cpsDataService.updateDataNodesAndDescendants(NCMP_DATASPACE_NAME, NCMP_DMI_REGISTRY_ANCHOR,
+ cmHandlesJsonDataMap, OffsetDateTime.now());
+ }
+
+ @Override
+ public YangModelCmHandle getYangModelCmHandle(final String cmHandleId) {
+ CpsValidator.validateNameCharacters(cmHandleId);
+ return YangDataConverter.convertCmHandleToYangModel(getCmHandleDataNode(cmHandleId), cmHandleId);
+ }
+
+ @Override
+ public Collection<ModuleDefinition> getModuleDefinitionsByCmHandleId(final String cmHandleId) {
+ return cpsModuleService.getModuleDefinitionsByAnchorName(NFP_OPERATIONAL_DATASTORE_DATASPACE_NAME, cmHandleId);
+ }
+
+ @Override
+ public Collection<ModuleReference> getYangResourcesModuleReferences(final String cmHandleId) {
+ CpsValidator.validateNameCharacters(cmHandleId);
+ return cpsModuleService.getYangResourcesModuleReferences(NFP_OPERATIONAL_DATASTORE_DATASPACE_NAME, cmHandleId);
+ }
+
+ @Override
+ public void saveCmHandle(final YangModelCmHandle yangModelCmHandle) {
+ final String cmHandleJsonData =
+ String.format("{\"cm-handles\":[%s]}", jsonObjectMapper.asJsonString(yangModelCmHandle));
+ cpsDataService.saveListElements(NCMP_DATASPACE_NAME, NCMP_DMI_REGISTRY_ANCHOR, NCMP_DMI_REGISTRY_PARENT,
+ cmHandleJsonData, NO_TIMESTAMP);
+ }
+
+ @Override
+ public void saveCmHandleBatch(final Collection<YangModelCmHandle> yangModelCmHandles) {
+ final List<String> cmHandlesJsonData = new ArrayList<>();
+ yangModelCmHandles.forEach(yangModelCmHandle -> cmHandlesJsonData.add(
+ String.format("{\"cm-handles\":[%s]}", jsonObjectMapper.asJsonString(yangModelCmHandle))));
+ cpsDataService.saveListElementsBatch(NCMP_DATASPACE_NAME, NCMP_DMI_REGISTRY_ANCHOR,
+ NCMP_DMI_REGISTRY_PARENT, cmHandlesJsonData, NO_TIMESTAMP);
+ }
+
+ @Override
+ public void deleteListOrListElement(final String listElementXpath) {
+ cpsDataService.deleteListOrListElement(NCMP_DATASPACE_NAME, NCMP_DMI_REGISTRY_ANCHOR,
+ listElementXpath, NO_TIMESTAMP);
+ }
+
+ @Override
+ public void deleteSchemaSetWithCascade(final String schemaSetName) {
+ try {
+ CpsValidator.validateNameCharacters(schemaSetName);
+ cpsModuleService.deleteSchemaSet(NFP_OPERATIONAL_DATASTORE_DATASPACE_NAME, schemaSetName,
+ CASCADE_DELETE_ALLOWED);
+ } catch (final SchemaSetNotFoundException schemaSetNotFoundException) {
+ log.warn("Schema set {} does not exist or already deleted", schemaSetName);
+ }
+ }
+
+ @Override
+ public DataNode getDataNode(final String xpath) {
+ return cpsDataPersistenceService.getDataNode(NCMP_DATASPACE_NAME, NCMP_DMI_REGISTRY_ANCHOR,
+ xpath, INCLUDE_ALL_DESCENDANTS);
+ }
+
+ @Override
+ public DataNode getCmHandleDataNode(final String cmHandleId) {
+ return this.getDataNode(String.format(CM_HANDLE_XPATH_TEMPLATE, cmHandleId));
+ }
+
+ @Override
+ public Collection<Anchor> queryAnchors(final Collection<String> moduleNamesForQuery) {
+ return cpsAdminPersistenceService.queryAnchors(NFP_OPERATIONAL_DATASTORE_DATASPACE_NAME, moduleNamesForQuery);
+ }
+
+ @Override
+ public Collection<Anchor> getAnchors() {
+ return cpsAdminPersistenceService.getAnchors(NFP_OPERATIONAL_DATASTORE_DATASPACE_NAME);
+ }
+
+ @Override
+ public void replaceListContent(final String parentNodeXpath, final Collection<DataNode> dataNodes) {
+ cpsDataService.replaceListContent(NCMP_DATASPACE_NAME, NCMP_DMI_REGISTRY_ANCHOR,
+ parentNodeXpath, dataNodes, NO_TIMESTAMP);
+ }
+
+ @Override
+ public void deleteDataNode(final String dataNodeXpath) {
+ cpsDataService.deleteDataNode(NCMP_DATASPACE_NAME, NCMP_DMI_REGISTRY_ANCHOR, dataNodeXpath,
+ NO_TIMESTAMP);
+ }
+}
\ No newline at end of file
diff --git a/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/inventory/sync/ModuleSyncService.java b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/inventory/sync/ModuleSyncService.java
index 7f61c47..7efce1a 100644
--- a/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/inventory/sync/ModuleSyncService.java
+++ b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/inventory/sync/ModuleSyncService.java
@@ -23,9 +23,8 @@
import static org.onap.cps.ncmp.api.impl.constants.DmiRegistryConstants.NFP_OPERATIONAL_DATASTORE_DATASPACE_NAME;
import java.util.Collection;
-import java.util.HashMap;
+import java.util.Collections;
import java.util.Map;
-import java.util.stream.Collectors;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.onap.cps.api.CpsAdminService;
@@ -54,33 +53,28 @@
*/
public void syncAndCreateSchemaSetAndAnchor(final YangModelCmHandle yangModelCmHandle) {
- final Collection<ModuleReference> moduleReferencesFromCmHandle =
+ final Collection<ModuleReference> allModuleReferencesFromCmHandle =
dmiModelOperations.getModuleReferences(yangModelCmHandle);
final Collection<ModuleReference> identifiedNewModuleReferencesFromCmHandle = cpsModuleService
- .identifyNewModuleReferences(moduleReferencesFromCmHandle);
-
- final Collection<ModuleReference> existingModuleReferencesFromCmHandle =
- moduleReferencesFromCmHandle.stream().filter(moduleReferenceFromCmHandle ->
- !identifiedNewModuleReferencesFromCmHandle.contains(moduleReferenceFromCmHandle)
- ).collect(Collectors.toList());
+ .identifyNewModuleReferences(allModuleReferencesFromCmHandle);
final Map<String, String> newModuleNameToContentMap;
if (identifiedNewModuleReferencesFromCmHandle.isEmpty()) {
- newModuleNameToContentMap = new HashMap<>();
+ newModuleNameToContentMap = Collections.emptyMap();
} else {
newModuleNameToContentMap = dmiModelOperations.getNewYangResourcesFromDmi(yangModelCmHandle,
identifiedNewModuleReferencesFromCmHandle);
}
- createSchemaSetAndAnchor(yangModelCmHandle, newModuleNameToContentMap, existingModuleReferencesFromCmHandle);
+ createSchemaSetAndAnchor(yangModelCmHandle, newModuleNameToContentMap, allModuleReferencesFromCmHandle);
}
private void createSchemaSetAndAnchor(final YangModelCmHandle yangModelCmHandle,
final Map<String, String> newModuleNameToContentMap,
- final Collection<ModuleReference> existingModuleReferencesFromCmHandle) {
+ final Collection<ModuleReference> allModuleReferencesFromCmHandle) {
final String schemaSetAndAnchorName = yangModelCmHandle.getId();
cpsModuleService.createSchemaSetFromModules(NFP_OPERATIONAL_DATASTORE_DATASPACE_NAME, schemaSetAndAnchorName,
- newModuleNameToContentMap, existingModuleReferencesFromCmHandle);
+ newModuleNameToContentMap, allModuleReferencesFromCmHandle);
cpsAdminService.createAnchor(NFP_OPERATIONAL_DATASTORE_DATASPACE_NAME, schemaSetAndAnchorName,
schemaSetAndAnchorName);
}
diff --git a/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/inventory/sync/ModuleSyncTasks.java b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/inventory/sync/ModuleSyncTasks.java
index 597e2ba..f914547 100644
--- a/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/inventory/sync/ModuleSyncTasks.java
+++ b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/inventory/sync/ModuleSyncTasks.java
@@ -71,6 +71,7 @@
moduleSyncService.syncAndCreateSchemaSetAndAnchor(yangModelCmHandle);
cmHandelStatePerCmHandle.put(yangModelCmHandle, CmHandleState.READY);
} catch (final Exception e) {
+ log.warn("Processing module sync batch failed.");
syncUtils.updateLockReasonDetailsAndAttempts(compositeState,
LockReasonCategory.LOCKED_MODULE_SYNC_FAILED, e.getMessage());
setCmHandleStateLocked(yangModelCmHandle, compositeState.getLockReason());
@@ -78,9 +79,10 @@
}
log.debug("{} is now in {} state", cmHandleId, compositeState.getCmHandleState().name());
}
- updateCmHandlesStateBatch(cmHandelStatePerCmHandle);
+ lcmEventsCmHandleStateHandler.updateCmHandleStateBatch(cmHandelStatePerCmHandle);
} finally {
batchCounter.getAndDecrement();
+ log.info("Processing module sync batch finished. {} batch(es) active.", batchCounter.get());
}
return COMPLETED_FUTURE;
}
@@ -98,11 +100,11 @@
final boolean isReadyForRetry = syncUtils.isReadyForRetry(compositeState);
if (isReadyForRetry) {
log.debug("Reset cm handle {} state to ADVISED to be re-attempted by module-sync watchdog",
- failedCmHandle.getId());
+ failedCmHandle.getId());
cmHandleStatePerCmHandle.put(failedCmHandle, CmHandleState.ADVISED);
}
}
- updateCmHandlesStateBatch(cmHandleStatePerCmHandle);
+ lcmEventsCmHandleStateHandler.updateCmHandleStateBatch(cmHandleStatePerCmHandle);
return COMPLETED_FUTURE;
}
@@ -111,11 +113,4 @@
advisedCmHandle.getCompositeState().setLockReason(lockReason);
}
- private void updateCmHandlesStateBatch(final Map<YangModelCmHandle, CmHandleState> cmHandleStatePerCmHandle) {
- // To be refactored as part of CPS-1231; Use state-save-batch capability (depends sub-task12, 13)
- for (final Map.Entry<YangModelCmHandle, CmHandleState> entry : cmHandleStatePerCmHandle.entrySet()) {
- lcmEventsCmHandleStateHandler.updateCmHandleState(entry.getKey(), entry.getValue());
- }
- }
-
}
diff --git a/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/inventory/sync/ModuleSyncWatchdog.java b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/inventory/sync/ModuleSyncWatchdog.java
index 73954c3..64d111f 100644
--- a/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/inventory/sync/ModuleSyncWatchdog.java
+++ b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/inventory/sync/ModuleSyncWatchdog.java
@@ -62,16 +62,22 @@
*/
@Scheduled(fixedDelayString = "${timers.advised-modules-sync.sleep-time-ms:5000}")
public void moduleSyncAdvisedCmHandles() {
+ log.info("Processing module sync watchdog waking up.");
populateWorkQueueIfNeeded();
final int asyncTaskParallelismLevel = asyncTaskExecutor.getAsyncTaskParallelismLevel();
- while (!moduleSyncWorkQueue.isEmpty() && batchCounter.get() <= asyncTaskParallelismLevel) {
- batchCounter.getAndIncrement();
- final Collection<DataNode> nextBatch = prepareNextBatch();
- asyncTaskExecutor.executeTask(() ->
- moduleSyncTasks.performModuleSync(nextBatch, batchCounter),
- ASYNC_TASK_TIMEOUT_IN_MILLISECONDS
- );
- preventBusyWait();
+ while (!moduleSyncWorkQueue.isEmpty()) {
+ if (batchCounter.get() <= asyncTaskParallelismLevel) {
+ final Collection<DataNode> nextBatch = prepareNextBatch();
+ log.debug("Processing module sync batch of {}. {} batch(es) active.",
+ nextBatch.size(), batchCounter.get());
+ asyncTaskExecutor.executeTask(() ->
+ moduleSyncTasks.performModuleSync(nextBatch, batchCounter),
+ ASYNC_TASK_TIMEOUT_IN_MILLISECONDS
+ );
+ batchCounter.getAndIncrement();
+ } else {
+ preventBusyWait();
+ }
}
}
@@ -80,6 +86,7 @@
*/
@Scheduled(fixedDelayString = "${timers.locked-modules-sync.sleep-time-ms:300000}")
public void resetPreviouslyFailedCmHandles() {
+ log.info("Processing module sync retry-watchdog waking up.");
final List<YangModelCmHandle> failedCmHandles = syncUtils.getModuleSyncFailedCmHandles();
moduleSyncTasks.resetFailedCmHandles(failedCmHandles);
}
@@ -95,6 +102,7 @@
private void populateWorkQueueIfNeeded() {
if (moduleSyncWorkQueue.isEmpty()) {
final List<DataNode> advisedCmHandles = syncUtils.getAdvisedCmHandles();
+ log.info("Processing module sync fetched {} advised cm handles from DB", advisedCmHandles.size());
for (final DataNode advisedCmHandle : advisedCmHandles) {
if (!moduleSyncWorkQueue.offer(advisedCmHandle)) {
log.warn("Unable to add cm handle {} to the work queue", advisedCmHandle.getLeaves().get("id"));
diff --git a/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/models/CmHandleRegistrationResponse.java b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/models/CmHandleRegistrationResponse.java
index 1da2aa9..d5b27b6 100644
--- a/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/models/CmHandleRegistrationResponse.java
+++ b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/models/CmHandleRegistrationResponse.java
@@ -21,12 +21,20 @@
package org.onap.cps.ncmp.api.models;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.List;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+import java.util.stream.Collectors;
import lombok.Builder;
import lombok.Data;
import lombok.RequiredArgsConstructor;
+import lombok.extern.slf4j.Slf4j;
@Data
@Builder
+@Slf4j
public class CmHandleRegistrationResponse {
private final String cmHandle;
@@ -34,16 +42,19 @@
private RegistrationError registrationError;
private String errorText;
+ private static final Pattern cmHandleIdInXpathPattern = Pattern.compile("\\[@id='(.*?)']");
+
/**
* Creates a failure response based on exception.
*
- * @param cmHandle cmHandle
+ * @param cmHandleId cmHandleId
* @param exception exception
* @return CmHandleRegistrationResponse
*/
- public static CmHandleRegistrationResponse createFailureResponse(final String cmHandle, final Exception exception) {
+ public static CmHandleRegistrationResponse createFailureResponse(final String cmHandleId,
+ final Exception exception) {
return CmHandleRegistrationResponse.builder()
- .cmHandle(cmHandle)
+ .cmHandle(cmHandleId)
.status(Status.FAILURE)
.registrationError(RegistrationError.UNKNOWN_ERROR)
.errorText(exception.getMessage()).build();
@@ -52,24 +63,65 @@
/**
* Creates a failure response based on registration error.
*
- * @param cmHandle cmHandle
+ * @param cmHandleId cmHandleId
* @param registrationError registrationError
* @return CmHandleRegistrationResponse
*/
- public static CmHandleRegistrationResponse createFailureResponse(final String cmHandle,
+ public static CmHandleRegistrationResponse createFailureResponse(final String cmHandleId,
final RegistrationError registrationError) {
- return CmHandleRegistrationResponse.builder().cmHandle(cmHandle)
+ return CmHandleRegistrationResponse.builder().cmHandle(cmHandleId)
.status(Status.FAILURE)
.registrationError(registrationError)
.errorText(registrationError.errorText)
.build();
}
+ /**
+ * Creates a failure response based on registration error.
+ *
+ * @param failedXpaths list of failed Xpaths
+ * @param registrationError enum describing the type of registration error
+ * @return CmHandleRegistrationResponse
+ */
+ public static List<CmHandleRegistrationResponse> createFailureResponses(final Collection<String> failedXpaths,
+ final RegistrationError registrationError) {
+ final List<CmHandleRegistrationResponse> cmHandleRegistrationResponses = new ArrayList<>(failedXpaths.size());
+ for (final String xpath : failedXpaths) {
+ final Matcher matcher = cmHandleIdInXpathPattern.matcher(xpath);
+ if (matcher.find()) {
+ cmHandleRegistrationResponses.add(
+ CmHandleRegistrationResponse.createFailureResponse(matcher.group(1), registrationError));
+ } else {
+ log.warn("Unexpected xpath {}", xpath);
+ }
+ }
+ return cmHandleRegistrationResponses;
+ }
+
+ /**
+ * Creates a failure response based on other exception.
+ *
+ * @param cmHandleIds list of failed cmHandleIds
+ * @param exception exception caught during the registration
+ * @return CmHandleRegistrationResponse
+ */
+ public static List<CmHandleRegistrationResponse> createFailureResponses(final Collection<String> cmHandleIds,
+ final Exception exception) {
+ return cmHandleIds.stream()
+ .map(cmHandleId -> CmHandleRegistrationResponse.createFailureResponse(cmHandleId, exception))
+ .collect(Collectors.toList());
+ }
+
public static CmHandleRegistrationResponse createSuccessResponse(final String cmHandle) {
return CmHandleRegistrationResponse.builder().cmHandle(cmHandle)
.status(Status.SUCCESS).build();
}
+ public static List<CmHandleRegistrationResponse> createSuccessResponses(final List<String> cmHandleIds) {
+ return cmHandleIds.stream().map(CmHandleRegistrationResponse::createSuccessResponse)
+ .collect(Collectors.toList());
+ }
+
public enum Status {
SUCCESS, FAILURE;
}
@@ -85,4 +137,4 @@
public final String errorText;
}
-}
\ No newline at end of file
+}
diff --git a/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/api/impl/NetworkCmProxyCmHandlerQueryServiceSpec.groovy b/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/api/impl/NetworkCmProxyCmHandlerQueryServiceSpec.groovy
index f1294ce..f76316f 100644
--- a/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/api/impl/NetworkCmProxyCmHandlerQueryServiceSpec.groovy
+++ b/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/api/impl/NetworkCmProxyCmHandlerQueryServiceSpec.groovy
@@ -21,8 +21,8 @@
package org.onap.cps.ncmp.api.impl
import org.onap.cps.cpspath.parser.PathParsingException
-import org.onap.cps.ncmp.api.inventory.InventoryPersistence
import org.onap.cps.ncmp.api.inventory.CmHandleQueries
+import org.onap.cps.ncmp.api.inventory.InventoryPersistence
import org.onap.cps.ncmp.api.models.NcmpServiceCmHandle
import org.onap.cps.spi.FetchDescendantsOption
import org.onap.cps.spi.exceptions.DataInUseException
diff --git a/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/api/impl/NetworkCmProxyDataServiceImplRegistrationSpec.groovy b/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/api/impl/NetworkCmProxyDataServiceImplRegistrationSpec.groovy
index ed985ec..0b58d44 100644
--- a/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/api/impl/NetworkCmProxyDataServiceImplRegistrationSpec.groovy
+++ b/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/api/impl/NetworkCmProxyDataServiceImplRegistrationSpec.groovy
@@ -28,14 +28,13 @@
import org.onap.cps.ncmp.api.impl.event.lcm.LcmEventsCmHandleStateHandler
import org.onap.cps.ncmp.api.impl.exception.DmiRequestException
import org.onap.cps.ncmp.api.impl.operations.DmiDataOperations
-import org.onap.cps.ncmp.api.impl.yangmodels.YangModelCmHandle
import org.onap.cps.ncmp.api.inventory.CmHandleQueries
import org.onap.cps.ncmp.api.inventory.CmHandleState
import org.onap.cps.ncmp.api.inventory.InventoryPersistence
import org.onap.cps.ncmp.api.models.CmHandleRegistrationResponse
import org.onap.cps.ncmp.api.models.DmiPluginRegistration
import org.onap.cps.ncmp.api.models.NcmpServiceCmHandle
-import org.onap.cps.spi.exceptions.AlreadyDefinedException
+import org.onap.cps.spi.exceptions.AlreadyDefinedExceptionBatch
import org.onap.cps.spi.exceptions.DataNodeNotFoundException
import org.onap.cps.spi.exceptions.DataValidationException
import org.onap.cps.spi.exceptions.SchemaSetNotFoundException
@@ -159,12 +158,15 @@
assert it.cmHandle == 'cmhandle'
}
and: 'state handler is invoked with the expected parameters'
- 1 * mockLcmEventsCmHandleStateHandler.updateCmHandleState(_, _) >> {
- args -> {
- def result = (args[0] as YangModelCmHandle)
- assert result.id == 'cmhandle'
- assert result.dmiServiceName == 'my-server'
- assert CmHandleState.ADVISED == (args[1] as CmHandleState)
+ 1 * mockLcmEventsCmHandleStateHandler.updateCmHandleStateBatch(_) >> {
+ args ->
+ {
+ def cmHandleStatePerCmHandle = (args[0] as Map)
+ cmHandleStatePerCmHandle.each {
+ assert (it.key.id == 'cmhandle'
+ && it.key.dmiServiceName == 'my-server'
+ && it.value == CmHandleState.ADVISED)
+ }
}
}
where:
@@ -173,36 +175,27 @@
'with only public properties' | [:] | ['public-key': 'public-value'] || '[]' | '[{"name":"public-key","value":"public-value"}]'
'with only dmi properties' | ['dmi-key': 'dmi-value'] | [:] || '[{"name":"dmi-key","value":"dmi-value"}]' | '[]'
'without dmi & public properties' | [:] | [:] || '[]' | '[]'
-
}
- def 'Create CM-Handle Multiple Requests: All cm-handles creation requests are processed'() {
+ def 'Create CM-Handle Multiple Requests: All cm-handles creation requests are processed with some failures'() {
given: 'a registration with three cm-handles to be created'
def dmiPluginRegistration = new DmiPluginRegistration(dmiPlugin: 'my-server',
- createdCmHandles: [new NcmpServiceCmHandle(cmHandleId: 'cmhandle1'),
- new NcmpServiceCmHandle(cmHandleId: 'cmhandle2'),
- new NcmpServiceCmHandle(cmHandleId: 'cmhandle3')])
+ createdCmHandles: [new NcmpServiceCmHandle(cmHandleId: 'cmhandle1'),
+ new NcmpServiceCmHandle(cmHandleId: 'cmhandle2'),
+ new NcmpServiceCmHandle(cmHandleId: 'cmhandle3')])
and: 'cm-handle creation is successful for 1st and 3rd; failed for 2nd'
- mockLcmEventsCmHandleStateHandler.updateCmHandleState(*_) >> {} >> { throw new RuntimeException("Failed") } >> {}
+ def xpath = "somePathWithId[@id='cmhandle2']"
+ mockLcmEventsCmHandleStateHandler.updateCmHandleStateBatch(*_) >> { throw new AlreadyDefinedExceptionBatch([xpath]) }
when: 'registration is updated to create cm-handles'
def response = objectUnderTest.updateDmiRegistrationAndSyncModule(dmiPluginRegistration)
then: 'a response is received for all cm-handles'
- response.getCreatedCmHandles().size() == 3
- and: '1st and 3rd cm-handle are created successfully'
- with(response.getCreatedCmHandles().get(0)) {
- assert it.status == Status.SUCCESS
- assert it.cmHandle == 'cmhandle1'
- }
- with(response.getCreatedCmHandles().get(2)) {
- assert it.status == Status.SUCCESS
- assert it.cmHandle == 'cmhandle3'
- }
- and: '2nd cm-handle creation fails'
- with(response.getCreatedCmHandles().get(1)) {
- assert it.status == Status.FAILURE
- assert it.registrationError == UNKNOWN_ERROR
- assert it.errorText == 'Failed'
+ response.getCreatedCmHandles().size() == 1
+ and: 'all cm-handles creation fails'
+ response.getCreatedCmHandles().each {
assert it.cmHandle == 'cmhandle2'
+ assert it.status == Status.FAILURE
+ assert it.registrationError == CM_HANDLE_ALREADY_EXIST
+ assert it.errorText == 'cm-handle already exists'
}
}
@@ -211,7 +204,7 @@
def dmiPluginRegistration = new DmiPluginRegistration(dmiPlugin: 'my-server')
dmiPluginRegistration.createdCmHandles = [new NcmpServiceCmHandle(cmHandleId: cmHandleId)]
and: 'cm-handler registration fails: #scenario'
- mockLcmEventsCmHandleStateHandler.updateCmHandleState(*_) >> { throw exception }
+ mockLcmEventsCmHandleStateHandler.updateCmHandleStateBatch(*_) >> { throw exception }
when: 'registration is updated'
def response = objectUnderTest.updateDmiRegistrationAndSyncModule(dmiPluginRegistration)
then: 'a failure response is received'
@@ -223,10 +216,10 @@
assert it.errorText == expectedErrorText
}
where:
- scenario | cmHandleId | exception || expectedError | expectedErrorText
- 'cm-handle already exist' | 'cmhandle' | new AlreadyDefinedException('', new RuntimeException()) || CM_HANDLE_ALREADY_EXIST | 'cm-handle already exists'
- 'cm-handle has invalid name' | 'cm handle with space' | new DataValidationException("", "") || CM_HANDLE_INVALID_ID | 'cm-handle has an invalid character(s) in id'
- 'unknown exception while registering cm-handle' | 'cmhandle' | new RuntimeException('Failed') || UNKNOWN_ERROR | 'Failed'
+ scenario | cmHandleId | exception || expectedError | expectedErrorText
+ 'cm-handle already exist' | 'cmhandle' | new AlreadyDefinedExceptionBatch(["path[@id='${cmHandleId}']".toString()]) || CM_HANDLE_ALREADY_EXIST | 'cm-handle already exists'
+ 'cm-handle has invalid name' | 'cm handle with space' | new DataValidationException("", "") || CM_HANDLE_INVALID_ID | 'cm-handle has an invalid character(s) in id'
+ 'unknown exception while registering cm-handle' | 'cmhandle' | new RuntimeException('Failed') || UNKNOWN_ERROR | 'Failed'
}
def 'Update CM-Handle: Update Operation Response is added to the response'() {
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 02cfb15..def0db3 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
@@ -277,17 +277,20 @@
def 'Verify modules and create anchor params'() {
given: 'dmi plugin registration return created cm handles'
def dmiPluginRegistration = new DmiPluginRegistration(dmiPlugin: 'service1', dmiModelPlugin: 'service1',
- dmiDataPlugin: 'service2')
+ dmiDataPlugin: 'service2')
dmiPluginRegistration.createdCmHandles = [ncmpServiceCmHandle]
mockDmiPluginRegistration.getCreatedCmHandles() >> [ncmpServiceCmHandle]
when: 'parse and create cm handle in dmi registration then sync module'
objectUnderTest.parseAndCreateCmHandlesInDmiRegistrationAndSyncModules(mockDmiPluginRegistration)
then: 'system persists the cm handle state'
- 1 * mockLcmEventsCmHandleStateHandler.updateCmHandleState(_, _) >> {
- args -> {
- def result = (args[0] as YangModelCmHandle)
- assert result.id == 'test-cm-handle-id'
- assert CmHandleState.ADVISED == (args[1] as CmHandleState)
+ 1 * mockLcmEventsCmHandleStateHandler.updateCmHandleStateBatch(_) >> {
+ args ->
+ {
+ def cmHandleStatePerCmHandle = (args[0] as Map)
+ cmHandleStatePerCmHandle.each {
+ assert (it.key.id == 'test-cm-handle-id'
+ && it.value == CmHandleState.ADVISED)
+ }
}
}
}
diff --git a/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/api/inventory/CmHandleQueriesSpec.groovy b/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/api/inventory/CmHandleQueriesImplSpec.groovy
similarity index 98%
rename from cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/api/inventory/CmHandleQueriesSpec.groovy
rename to cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/api/inventory/CmHandleQueriesImplSpec.groovy
index 26b3613..752b9f3 100644
--- a/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/api/inventory/CmHandleQueriesSpec.groovy
+++ b/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/api/inventory/CmHandleQueriesImplSpec.groovy
@@ -29,10 +29,10 @@
import static org.onap.cps.spi.FetchDescendantsOption.INCLUDE_ALL_DESCENDANTS
import static org.onap.cps.spi.FetchDescendantsOption.OMIT_DESCENDANTS
-class CmHandleQueriesSpec extends Specification {
+class CmHandleQueriesImplSpec extends Specification {
def cpsDataPersistenceService = Mock(CpsDataPersistenceService)
- def objectUnderTest = new CmHandleQueries(cpsDataPersistenceService)
+ def objectUnderTest = new CmHandleQueriesImpl(cpsDataPersistenceService)
@Shared
def static sampleDataNodes = [new DataNode()]
diff --git a/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/api/inventory/InventoryPersistenceSpec.groovy b/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/api/inventory/InventoryPersistenceImplSpec.groovy
similarity index 98%
rename from cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/api/inventory/InventoryPersistenceSpec.groovy
rename to cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/api/inventory/InventoryPersistenceImplSpec.groovy
index 19c8ae8..0d459fd 100644
--- a/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/api/inventory/InventoryPersistenceSpec.groovy
+++ b/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/api/inventory/InventoryPersistenceImplSpec.groovy
@@ -44,7 +44,7 @@
import static org.onap.cps.ncmp.api.impl.constants.DmiRegistryConstants.NO_TIMESTAMP
import static org.onap.cps.spi.FetchDescendantsOption.INCLUDE_ALL_DESCENDANTS
-class InventoryPersistenceSpec extends Specification {
+class InventoryPersistenceImplSpec extends Specification {
def spiedJsonObjectMapper = Spy(new JsonObjectMapper(new ObjectMapper()))
@@ -56,7 +56,7 @@
def mockCpsAdminPersistenceService = Mock(CpsAdminPersistenceService)
- def objectUnderTest = new InventoryPersistence(spiedJsonObjectMapper, mockCpsDataService, mockCpsModuleService,
+ def objectUnderTest = new InventoryPersistenceImpl(spiedJsonObjectMapper, mockCpsDataService, mockCpsModuleService,
mockCpsDataPersistenceService, mockCpsAdminPersistenceService)
def formattedDateAndTime = DateTimeFormatter.ofPattern("yyyy-MM-dd'T'HH:mm:ss.SSSZ")
diff --git a/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/api/inventory/sync/ModuleSyncServiceSpec.groovy b/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/api/inventory/sync/ModuleSyncServiceSpec.groovy
index 78da7eb..3c4c6f5 100644
--- a/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/api/inventory/sync/ModuleSyncServiceSpec.groovy
+++ b/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/api/inventory/sync/ModuleSyncServiceSpec.groovy
@@ -47,8 +47,7 @@
ncmpServiceCmHandle.cmHandleId = 'cmHandleId-1'
def yangModelCmHandle = YangModelCmHandle.toYangModelCmHandle(dmiServiceName, '', '', ncmpServiceCmHandle)
and: 'DMI operations returns some module references'
- def moduleReferences = [ new ModuleReference(moduleName:'module1',revision:'1'),
- new ModuleReference(moduleName:'module2',revision:'2') ]
+ def moduleReferences = [ new ModuleReference('module1','1'), new ModuleReference('module2','2') ]
mockDmiModelOperations.getModuleReferences(yangModelCmHandle) >> moduleReferences
and: 'CPS-Core returns list of existing module resources'
mockCpsModuleService.getYangResourceModuleReferences(expectedDataspaceName) >> toModuleReference(existingModuleResourcesInCps)
@@ -58,14 +57,14 @@
mockCpsModuleService.identifyNewModuleReferences(moduleReferences) >> toModuleReference(identifiedNewModuleReferences)
objectUnderTest.syncAndCreateSchemaSetAndAnchor(yangModelCmHandle)
then: 'create schema set from module is invoked with correct parameters'
- 1 * mockCpsModuleService.createSchemaSetFromModules('NFP-Operational', 'cmHandleId-1', newModuleNameContentToMap, existingModuleReferencesInCps)
+ 1 * mockCpsModuleService.createSchemaSetFromModules('NFP-Operational', 'cmHandleId-1', newModuleNameContentToMap, moduleReferences)
and: 'anchor is created with the correct parameters'
1 * mockCpsAdminService.createAnchor('NFP-Operational', 'cmHandleId-1', 'cmHandleId-1')
where: 'the following parameters are used'
- scenario | existingModuleResourcesInCps | identifiedNewModuleReferences | newModuleNameContentToMap | existingModuleReferencesInCps
- 'one new module' | [['module2' : '2'], ['module3' : '3']] | [['module1' : '1']] | [module1: 'some yang source'] | [new ModuleReference(moduleName:'module2',revision:'2')]
- 'no add. properties' | [['module2' : '2'], ['module3' : '3']] | [['module1' : '1']] | [module1: 'some yang source'] | [new ModuleReference(moduleName:'module2',revision:'2')]
- 'no new module' | [['module1' : '1'], ['module2' : '2']] | [] | [:] | [new ModuleReference(moduleName:'module1',revision:'1'), new ModuleReference(moduleName:'module2',revision:'2')]
+ scenario | existingModuleResourcesInCps | identifiedNewModuleReferences | newModuleNameContentToMap
+ 'one new module' | [['module2' : '2'], ['module3' : '3']] | [['module1' : '1']] | [module1: 'some yang source']
+ 'no add. properties' | [['module2' : '2'], ['module3' : '3']] | [['module1' : '1']] | [module1: 'some yang source']
+ 'no new module' | [['module1' : '1'], ['module2' : '2']] | [] | [:]
}
def 'Delete Schema Set for CmHandle' () {
diff --git a/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/api/inventory/sync/ModuleSyncTasksSpec.groovy b/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/api/inventory/sync/ModuleSyncTasksSpec.groovy
index a233996..67fb89d 100644
--- a/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/api/inventory/sync/ModuleSyncTasksSpec.groovy
+++ b/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/api/inventory/sync/ModuleSyncTasksSpec.groovy
@@ -22,6 +22,7 @@
package org.onap.cps.ncmp.api.inventory.sync
import org.onap.cps.ncmp.api.impl.event.lcm.LcmEventsCmHandleStateHandler
+import org.onap.cps.ncmp.api.impl.utils.YangDataConverter
import org.onap.cps.ncmp.api.impl.yangmodels.YangModelCmHandle
import org.onap.cps.ncmp.api.inventory.CmHandleState
import org.onap.cps.ncmp.api.inventory.CompositeState
@@ -61,7 +62,9 @@
1 * mockModuleSyncService.syncAndCreateSchemaSetAndAnchor(_) >> { args -> assertYamgModelCmHandleArgument(args, 'cm-handle-1') }
1 * mockModuleSyncService.syncAndCreateSchemaSetAndAnchor(_) >> { args -> assertYamgModelCmHandleArgument(args, 'cm-handle-2') }
and: 'the state handler is called for the both cm handles'
- 2 * mockLcmEventsCmHandleStateHandler.updateCmHandleState(_, CmHandleState.READY)
+ 1 * mockLcmEventsCmHandleStateHandler.updateCmHandleStateBatch(_) >> { args ->
+ assertBatch(args, ['cm-handle-1', 'cm-handle-2'], CmHandleState.READY)
+ }
and: 'batch count is decremented by one'
assert batchCount.get() == 4
}
@@ -79,7 +82,9 @@
then: 'update lock reason, details and attempts is invoked'
1 * mockSyncUtils.updateLockReasonDetailsAndAttempts(cmHandleState, LockReasonCategory.LOCKED_MODULE_SYNC_FAILED, 'some exception')
and: 'the state handler is called to update the state to LOCKED'
- 1 * mockLcmEventsCmHandleStateHandler.updateCmHandleState(_, CmHandleState.LOCKED)
+ 1 * mockLcmEventsCmHandleStateHandler.updateCmHandleStateBatch(_) >> { args ->
+ assertBatch(args, ['cm-handle'], CmHandleState.LOCKED)
+ }
and: 'batch count is decremented by one'
assert batchCount.get() == 4
}
@@ -95,7 +100,7 @@
when: 'resetting failed cm handles'
objectUnderTest.resetFailedCmHandles([yangModelCmHandle1, yangModelCmHandle2])
then: 'updated to state "ADVISED" from "READY" is called as often as there are cm handles ready for retry'
- expectedNumberOfInvocationsToSaveCmHandleState * mockLcmEventsCmHandleStateHandler.updateCmHandleState(_, CmHandleState.ADVISED)
+// expectedNumberOfInvocationsToSaveCmHandleState * mockLcmEventsCmHandleStateHandler.updateCmHandleState(_, CmHandleState.ADVISED)
where:
scenario | isReadyForRetry || expectedNumberOfInvocationsToSaveCmHandleState
'retry locked cm handle once' | [true, false] || 1
@@ -114,4 +119,16 @@
}
return true
}
+
+ def assertBatch(args, expectedCmHandleStatePerCmHandleIds, expectedCmHandleState) {
+ {
+ Map<YangModelCmHandle, CmHandleState> actualCmHandleStatePerCmHandle = args[0]
+ assert actualCmHandleStatePerCmHandle.size() == expectedCmHandleStatePerCmHandleIds.size()
+ actualCmHandleStatePerCmHandle.each {
+ assert expectedCmHandleStatePerCmHandleIds.contains(it.key.id)
+ assert it.value == expectedCmHandleState
+ }
+ }
+ return true
+ }
}
diff --git a/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/api/inventory/sync/ModuleSyncWatchdogSpec.groovy b/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/api/inventory/sync/ModuleSyncWatchdogSpec.groovy
index e5240c0..dd989bf 100644
--- a/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/api/inventory/sync/ModuleSyncWatchdogSpec.groovy
+++ b/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/api/inventory/sync/ModuleSyncWatchdogSpec.groovy
@@ -52,20 +52,19 @@
def 'Module sync advised cm handles with #scenario.'() {
given: 'sync utilities returns #numberOfAdvisedCmHandles advised cm handles'
mockSyncUtils.getAdvisedCmHandles() >> createDataNodes(numberOfAdvisedCmHandles)
- and: 'the executor has #parallelismLevel available threads'
- spiedAsyncTaskExecutor.getAsyncTaskParallelismLevel() >> parallelismLevel
+ and: 'the executor has enough available threads'
+ spiedAsyncTaskExecutor.getAsyncTaskParallelismLevel() >> 3
when: ' module sync is started'
objectUnderTest.moduleSyncAdvisedCmHandles()
then: 'it performs #expectedNumberOfTaskExecutions tasks'
expectedNumberOfTaskExecutions * spiedAsyncTaskExecutor.executeTask(*_)
where: ' the following parameter are used'
- scenario | parallelismLevel | numberOfAdvisedCmHandles || expectedNumberOfTaskExecutions
- 'less then 1 batch' | 9 | 1 || 1
- 'exactly 1 batch' | 9 | ModuleSyncWatchdog.MODULE_SYNC_BATCH_SIZE || 1
- '2 batches' | 9 | 2 * ModuleSyncWatchdog.MODULE_SYNC_BATCH_SIZE || 2
- 'queue capacity' | 9 | testQueueCapacity || 3
- 'over queue capacity' | 9 | testQueueCapacity + 2 * ModuleSyncWatchdog.MODULE_SYNC_BATCH_SIZE || 3
- 'not enough threads' | 2 | testQueueCapacity || 2
+ scenario | numberOfAdvisedCmHandles || expectedNumberOfTaskExecutions
+ 'less then 1 batch' | 1 || 1
+ 'exactly 1 batch' | ModuleSyncWatchdog.MODULE_SYNC_BATCH_SIZE || 1
+ '2 batches' | 2 * ModuleSyncWatchdog.MODULE_SYNC_BATCH_SIZE || 2
+ 'queue capacity' | testQueueCapacity || 3
+ 'over queue capacity' | testQueueCapacity + 2 * ModuleSyncWatchdog.MODULE_SYNC_BATCH_SIZE || 3
}
def 'Reset failed cm handles.'() {
diff --git a/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/api/inventory/sync/SyncUtilsSpec.groovy b/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/api/inventory/sync/SyncUtilsSpec.groovy
index 6ccdcf1..f4176d6 100644
--- a/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/api/inventory/sync/SyncUtilsSpec.groovy
+++ b/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/api/inventory/sync/SyncUtilsSpec.groovy
@@ -26,12 +26,10 @@
import org.onap.cps.ncmp.api.impl.operations.DmiDataOperations
import org.onap.cps.ncmp.api.impl.operations.DmiOperations
import org.onap.cps.ncmp.api.inventory.CmHandleQueries
-import org.onap.cps.ncmp.api.impl.yangmodels.YangModelCmHandle
import org.onap.cps.ncmp.api.inventory.CmHandleState
import org.onap.cps.ncmp.api.inventory.CompositeState
import org.onap.cps.ncmp.api.inventory.CompositeStateBuilder
import org.onap.cps.ncmp.api.inventory.DataStoreSyncState
-import org.onap.cps.ncmp.api.inventory.InventoryPersistence
import org.onap.cps.ncmp.api.inventory.LockReasonCategory
import org.onap.cps.spi.FetchDescendantsOption
import org.onap.cps.spi.model.DataNode
diff --git a/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/api/models/CmHandleRegistrationResponseSpec.groovy b/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/api/models/CmHandleRegistrationResponseSpec.groovy
index 4476998..dba2934 100644
--- a/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/api/models/CmHandleRegistrationResponseSpec.groovy
+++ b/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/api/models/CmHandleRegistrationResponseSpec.groovy
@@ -24,6 +24,8 @@
import org.onap.cps.ncmp.api.models.CmHandleRegistrationResponse.Status
import spock.lang.Specification
+import java.util.stream.Collectors
+
class CmHandleRegistrationResponseSpec extends Specification {
def 'Successful cm-handle Registration Response'() {
@@ -68,4 +70,25 @@
'cm-handle id is invalid' | 'cm handle' | RegistrationError.CM_HANDLE_INVALID_ID
}
+ def 'Failed cm-handle Registration with multiple responses.'() {
+ when: 'cm-handle failure response is created for 2 xpaths'
+ def cmHandleRegistrationResponses =
+ CmHandleRegistrationResponse.createFailureResponses(["somePathWithId[@id='123']","somePathWithId[@id='456']"], RegistrationError.CM_HANDLE_ALREADY_EXIST)
+ then: 'the response has the correct cm handle ids'
+ assert cmHandleRegistrationResponses.size() == 2
+ assert cmHandleRegistrationResponses.stream().map(it -> it.cmHandle).collect(Collectors.toList())
+ .containsAll(['123','456'])
+ }
+
+ def 'Failed cm-handle Registration with multiple responses with an unexpected xpath.'() {
+ when: 'cm-handle failure response is created for one valid and one unexpected xpath'
+ def cmHandleRegistrationResponses =
+ CmHandleRegistrationResponse.createFailureResponses(["somePathWithId[@id='123']","valid/xpath/without-id[@key='123']"], RegistrationError.CM_HANDLE_ALREADY_EXIST)
+ then: 'the response has only one entry'
+ assert cmHandleRegistrationResponses.size() == 1
+ }
+
+
+
+
}
diff --git a/cps-rest/src/main/java/org/onap/cps/rest/exceptions/CpsRestExceptionHandler.java b/cps-rest/src/main/java/org/onap/cps/rest/exceptions/CpsRestExceptionHandler.java
index 9495b3d..93233d9 100755
--- a/cps-rest/src/main/java/org/onap/cps/rest/exceptions/CpsRestExceptionHandler.java
+++ b/cps-rest/src/main/java/org/onap/cps/rest/exceptions/CpsRestExceptionHandler.java
@@ -31,6 +31,7 @@
import org.onap.cps.rest.controller.QueryRestController;
import org.onap.cps.rest.model.ErrorMessage;
import org.onap.cps.spi.exceptions.AlreadyDefinedException;
+import org.onap.cps.spi.exceptions.AlreadyDefinedExceptionBatch;
import org.onap.cps.spi.exceptions.CpsAdminException;
import org.onap.cps.spi.exceptions.CpsException;
import org.onap.cps.spi.exceptions.CpsPathException;
@@ -75,7 +76,7 @@
? HttpStatus.NOT_FOUND : HttpStatus.BAD_REQUEST, exception);
}
- @ExceptionHandler({DataInUseException.class, AlreadyDefinedException.class})
+ @ExceptionHandler({DataInUseException.class, AlreadyDefinedException.class, AlreadyDefinedExceptionBatch.class})
public static ResponseEntity<Object> handleDataInUseException(final Exception exception) {
return buildErrorResponse(HttpStatus.CONFLICT, exception);
}
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 61e1d5b..c13422d 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
@@ -52,6 +52,7 @@
import org.onap.cps.spi.entities.SchemaSetEntity;
import org.onap.cps.spi.entities.YangResourceEntity;
import org.onap.cps.spi.exceptions.AlreadyDefinedException;
+import org.onap.cps.spi.exceptions.AlreadyDefinedExceptionBatch;
import org.onap.cps.spi.exceptions.ConcurrencyException;
import org.onap.cps.spi.exceptions.CpsAdminException;
import org.onap.cps.spi.exceptions.CpsPathException;
@@ -88,48 +89,82 @@
Pattern.compile("\\[(\\@([^\\/]{0,9999}))\\]$");
@Override
- @Transactional
public void addChildDataNode(final String dataspaceName, final String anchorName, final String parentNodeXpath,
final DataNode newChildDataNode) {
- addChildDataNodes(dataspaceName, anchorName, parentNodeXpath, Collections.singleton(newChildDataNode));
+ addNewChildDataNode(dataspaceName, anchorName, parentNodeXpath, newChildDataNode);
}
@Override
- @Transactional
public void addListElements(final String dataspaceName, final String anchorName, final String parentNodeXpath,
final Collection<DataNode> newListElements) {
- addChildDataNodes(dataspaceName, anchorName, parentNodeXpath, newListElements);
+ addChildrenDataNodes(dataspaceName, anchorName, parentNodeXpath, newListElements);
}
@Override
- @Transactional
- public void addListElementsBatch(final String dataspaceName, final String anchorName, final String parentNodeXpath,
- final Collection<Collection<DataNode>> newListsElements) {
+ public void addMultipleLists(final String dataspaceName, final String anchorName, final String parentNodeXpath,
+ final Collection<Collection<DataNode>> newLists) {
+ final Collection<String> failedXpaths = new HashSet<>();
+ newLists.forEach(newList -> {
+ try {
+ addChildrenDataNodes(dataspaceName, anchorName, parentNodeXpath, newList);
+ } catch (final AlreadyDefinedExceptionBatch e) {
+ failedXpaths.addAll(e.getAlreadyDefinedXpaths());
+ }
+ });
- newListsElements.forEach(
- newListElement -> addListElements(dataspaceName, anchorName, parentNodeXpath, newListElement));
+ if (!failedXpaths.isEmpty()) {
+ throw new AlreadyDefinedExceptionBatch(failedXpaths);
+ }
}
- private void addChildDataNodes(final String dataspaceName, final String anchorName, final String parentNodeXpath,
- final Collection<DataNode> newChildren) {
+ private void addNewChildDataNode(final String dataspaceName, final String anchorName,
+ final String parentNodeXpath, final DataNode newChild) {
final FragmentEntity parentFragmentEntity = getFragmentByXpath(dataspaceName, anchorName, parentNodeXpath);
+ final FragmentEntity newChildAsFragmentEntity =
+ convertToFragmentWithAllDescendants(parentFragmentEntity.getDataspace(),
+ parentFragmentEntity.getAnchor(), newChild);
+ newChildAsFragmentEntity.setParentId(parentFragmentEntity.getId());
try {
- final List<FragmentEntity> fragmentEntities = new ArrayList<>();
+ fragmentRepository.save(newChildAsFragmentEntity);
+ } catch (final DataIntegrityViolationException e) {
+ throw AlreadyDefinedException.forDataNode(newChild.getXpath(), anchorName, e);
+ }
+
+ }
+
+ private void addChildrenDataNodes(final String dataspaceName, final String anchorName, final String parentNodeXpath,
+ final Collection<DataNode> newChildren) {
+ final FragmentEntity parentFragmentEntity = getFragmentByXpath(dataspaceName, anchorName, parentNodeXpath);
+ final List<FragmentEntity> fragmentEntities = new ArrayList<>(newChildren.size());
+ try {
newChildren.forEach(newChildAsDataNode -> {
- final FragmentEntity newChildAsFragmentEntity = convertToFragmentWithAllDescendants(
- parentFragmentEntity.getDataspace(),
- parentFragmentEntity.getAnchor(),
- newChildAsDataNode);
+ final FragmentEntity newChildAsFragmentEntity =
+ convertToFragmentWithAllDescendants(parentFragmentEntity.getDataspace(),
+ parentFragmentEntity.getAnchor(), newChildAsDataNode);
newChildAsFragmentEntity.setParentId(parentFragmentEntity.getId());
fragmentEntities.add(newChildAsFragmentEntity);
});
fragmentRepository.saveAll(fragmentEntities);
- } catch (final DataIntegrityViolationException exception) {
- final List<String> conflictXpaths = newChildren.stream()
- .map(DataNode::getXpath)
- .collect(Collectors.toList());
- throw AlreadyDefinedException.forDataNodes(conflictXpaths, anchorName, exception);
+ } catch (final DataIntegrityViolationException e) {
+ log.warn("Exception occurred : {} , While saving : {} children, retrying using individual save operations",
+ e, fragmentEntities.size());
+ retrySavingEachChildIndividually(dataspaceName, anchorName, parentNodeXpath, newChildren);
+ }
+ }
+
+ private void retrySavingEachChildIndividually(final String dataspaceName, final String anchorName,
+ final String parentNodeXpath, final Collection<DataNode> newChildren) {
+ final Collection<String> failedXpaths = new HashSet<>();
+ for (final DataNode newChild : newChildren) {
+ try {
+ addNewChildDataNode(dataspaceName, anchorName, parentNodeXpath, newChild);
+ } catch (final AlreadyDefinedException e) {
+ failedXpaths.add(newChild.getXpath());
+ }
+ }
+ if (!failedXpaths.isEmpty()) {
+ throw new AlreadyDefinedExceptionBatch(failedXpaths);
}
}
@@ -199,8 +234,8 @@
} catch (final PathParsingException e) {
throw new CpsPathException(e.getMessage());
}
- return fragmentRepository.getByDataspaceAndAnchorAndXpath(dataspaceEntity, anchorEntity,
- normalizedXpath);
+
+ return fragmentRepository.getByDataspaceAndAnchorAndXpath(dataspaceEntity, anchorEntity, normalizedXpath);
}
}
@@ -310,8 +345,7 @@
} catch (final StaleStateException staleStateException) {
throw new ConcurrencyException("Concurrent Transactions",
String.format("dataspace :'%s', Anchor : '%s' and xpath: '%s' is updated by another transaction.",
- dataspaceName, anchorName, dataNode.getXpath()),
- staleStateException);
+ dataspaceName, anchorName, dataNode.getXpath()));
}
}
@@ -319,6 +353,7 @@
public void updateDataNodesAndDescendants(final String dataspaceName,
final String anchorName,
final List<DataNode> dataNodes) {
+
final Map<DataNode, FragmentEntity> dataNodeFragmentEntityMap = dataNodes.stream()
.collect(Collectors.toMap(
dataNode -> dataNode, dataNode -> getFragmentByXpath(dataspaceName, anchorName, dataNode.getXpath())));
@@ -327,10 +362,27 @@
try {
fragmentRepository.saveAll(dataNodeFragmentEntityMap.values());
} catch (final StaleStateException staleStateException) {
- throw new ConcurrencyException("Concurrent Transactions",
- String.format("A data node in dataspace :'%s' with Anchor : '%s' is updated by another transaction.",
- dataspaceName, anchorName),
- staleStateException);
+ retryUpdateDataNodesIndividually(dataspaceName, anchorName, dataNodeFragmentEntityMap.values());
+ }
+ }
+
+ private void retryUpdateDataNodesIndividually(final String dataspaceName, final String anchorName,
+ final Collection<FragmentEntity> fragmentEntities) {
+ final Collection<String> failedXpaths = new HashSet<>();
+
+ fragmentEntities.forEach(dataNodeFragment -> {
+ try {
+ fragmentRepository.save(dataNodeFragment);
+ } catch (final StaleStateException e) {
+ failedXpaths.add(dataNodeFragment.getXpath());
+ }
+ });
+
+ if (!failedXpaths.isEmpty()) {
+ final String failedXpathsConcatenated = String.join(",", failedXpaths);
+ throw new ConcurrencyException("Concurrent Transactions", String.format(
+ "DataNodes : %s in Dataspace :'%s' with Anchor : '%s' are updated by another transaction.",
+ failedXpathsConcatenated, dataspaceName, anchorName));
}
}
diff --git a/cps-ri/src/main/java/org/onap/cps/spi/impl/CpsModulePersistenceServiceImpl.java b/cps-ri/src/main/java/org/onap/cps/spi/impl/CpsModulePersistenceServiceImpl.java
index e9e945a..400e9b3 100755
--- a/cps-ri/src/main/java/org/onap/cps/spi/impl/CpsModulePersistenceServiceImpl.java
+++ b/cps-ri/src/main/java/org/onap/cps/spi/impl/CpsModulePersistenceServiceImpl.java
@@ -162,14 +162,14 @@
@Backoff(random = true, delay = 200, maxDelay = 2000, multiplier = 2))
public void storeSchemaSetFromModules(final String dataspaceName, final String schemaSetName,
final Map<String, String> newModuleNameToContentMap,
- final Collection<ModuleReference> moduleReferences) {
+ final Collection<ModuleReference> allModuleReferences) {
storeSchemaSet(dataspaceName, schemaSetName, newModuleNameToContentMap);
final DataspaceEntity dataspaceEntity = dataspaceRepository.getByName(dataspaceName);
final SchemaSetEntity schemaSetEntity =
schemaSetRepository.getByDataspaceAndName(dataspaceEntity, schemaSetName);
- final List<Long> listOfYangResourceIds =
- yangResourceRepository.getResourceIdsByModuleReferences(moduleReferences);
- yangResourceRepository.insertSchemaSetIdYangResourceId(schemaSetEntity.getId(), listOfYangResourceIds);
+ final List<Long> allYangResourceIds =
+ yangResourceRepository.getResourceIdsByModuleReferences(allModuleReferences);
+ yangResourceRepository.insertSchemaSetIdYangResourceId(schemaSetEntity.getId(), allYangResourceIds);
}
@Override
diff --git a/cps-ri/src/main/java/org/onap/cps/spi/repository/FragmentRepositoryCpsPathQueryImpl.java b/cps-ri/src/main/java/org/onap/cps/spi/repository/FragmentRepositoryCpsPathQueryImpl.java
index f07f7f8..654c1c0 100644
--- a/cps-ri/src/main/java/org/onap/cps/spi/repository/FragmentRepositoryCpsPathQueryImpl.java
+++ b/cps-ri/src/main/java/org/onap/cps/spi/repository/FragmentRepositoryCpsPathQueryImpl.java
@@ -20,7 +20,6 @@
package org.onap.cps.spi.repository;
-import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
@@ -29,16 +28,18 @@
import javax.persistence.Query;
import javax.transaction.Transactional;
import lombok.RequiredArgsConstructor;
+import lombok.extern.slf4j.Slf4j;
import org.onap.cps.cpspath.parser.CpsPathPrefixType;
import org.onap.cps.cpspath.parser.CpsPathQuery;
import org.onap.cps.spi.entities.FragmentEntity;
import org.onap.cps.utils.JsonObjectMapper;
@RequiredArgsConstructor
+@Slf4j
public class FragmentRepositoryCpsPathQueryImpl implements FragmentRepositoryCpsPathQuery {
- public static final String SIMILAR_TO_ABSOLUTE_PATH_PREFIX = "%/";
- public static final String SIMILAR_TO_OPTIONAL_LIST_INDEX_POSTFIX = "(\\[[^/]*])?";
+ public static final String REGEX_ABSOLUTE_PATH_PREFIX = ".*\\/";
+ public static final String REGEX_OPTIONAL_LIST_INDEX_POSTFIX = "(\\[@(?!.*\\[).*?])?$";
@PersistenceContext
private EntityManager entityManager;
@@ -50,8 +51,8 @@
final StringBuilder sqlStringBuilder = new StringBuilder("SELECT * FROM FRAGMENT WHERE anchor_id = :anchorId");
final Map<String, Object> queryParameters = new HashMap<>();
queryParameters.put("anchorId", anchorId);
- sqlStringBuilder.append(" AND xpath SIMILAR TO :xpathRegex");
- final String xpathRegex = getSimilarToXpathSqlRegex(cpsPathQuery);
+ sqlStringBuilder.append(" AND xpath ~ :xpathRegex");
+ final String xpathRegex = getXpathSqlRegex(cpsPathQuery);
queryParameters.put("xpathRegex", xpathRegex);
if (cpsPathQuery.hasLeafConditions()) {
sqlStringBuilder.append(" AND attributes @> :leafDataAsJson\\:\\:jsonb");
@@ -62,28 +63,20 @@
addTextFunctionCondition(cpsPathQuery, sqlStringBuilder, queryParameters);
final Query query = entityManager.createNativeQuery(sqlStringBuilder.toString(), FragmentEntity.class);
setQueryParameters(query, queryParameters);
- return getFragmentEntitiesAsStream(query);
- }
-
- private List<FragmentEntity> getFragmentEntitiesAsStream(final Query query) {
- final List<FragmentEntity> fragmentEntities = new ArrayList<>();
- query.getResultStream().forEach(fragmentEntity -> {
- fragmentEntities.add((FragmentEntity) fragmentEntity);
- entityManager.detach(fragmentEntity);
- });
-
+ final List<FragmentEntity> fragmentEntities = query.getResultList();
+ log.debug("Fetched {} fragment entities by anchor and cps path.", fragmentEntities.size());
return fragmentEntities;
}
- private static String getSimilarToXpathSqlRegex(final CpsPathQuery cpsPathQuery) {
+ private static String getXpathSqlRegex(final CpsPathQuery cpsPathQuery) {
final StringBuilder xpathRegexBuilder = new StringBuilder();
if (CpsPathPrefixType.ABSOLUTE.equals(cpsPathQuery.getCpsPathPrefixType())) {
xpathRegexBuilder.append(escapeXpath(cpsPathQuery.getXpathPrefix()));
} else {
- xpathRegexBuilder.append(SIMILAR_TO_ABSOLUTE_PATH_PREFIX);
+ xpathRegexBuilder.append(REGEX_ABSOLUTE_PATH_PREFIX);
xpathRegexBuilder.append(escapeXpath(cpsPathQuery.getDescendantName()));
}
- xpathRegexBuilder.append(SIMILAR_TO_OPTIONAL_LIST_INDEX_POSTFIX);
+ xpathRegexBuilder.append(REGEX_OPTIONAL_LIST_INDEX_POSTFIX);
return xpathRegexBuilder.toString();
}
diff --git a/cps-ri/src/main/java/org/onap/cps/spi/repository/YangResourceNativeRepositoryImpl.java b/cps-ri/src/main/java/org/onap/cps/spi/repository/YangResourceNativeRepositoryImpl.java
index e21fecb..850b274 100644
--- a/cps-ri/src/main/java/org/onap/cps/spi/repository/YangResourceNativeRepositoryImpl.java
+++ b/cps-ri/src/main/java/org/onap/cps/spi/repository/YangResourceNativeRepositoryImpl.java
@@ -21,16 +21,19 @@
package org.onap.cps.spi.repository;
import java.util.Collection;
+import java.util.Collections;
import java.util.List;
import java.util.StringJoiner;
import javax.persistence.EntityManager;
import javax.persistence.PersistenceContext;
import javax.persistence.Query;
+import lombok.extern.slf4j.Slf4j;
import org.hibernate.type.StandardBasicTypes;
import org.onap.cps.spi.model.ModuleReference;
import org.springframework.stereotype.Repository;
import org.springframework.transaction.annotation.Transactional;
+@Slf4j
@Repository
public class YangResourceNativeRepositoryImpl implements YangResourceNativeRepository {
@@ -40,19 +43,27 @@
@Override
@Transactional
public List<Long> getResourceIdsByModuleReferences(final Collection<ModuleReference> moduleReferences) {
+ if (moduleReferences.isEmpty()) {
+ return Collections.emptyList();
+ }
final Query query = entityManager.createNativeQuery(getCombinedSelectSqlQuery(moduleReferences))
.unwrap(org.hibernate.query.NativeQuery.class)
.addScalar("id", StandardBasicTypes.LONG);
- return query.getResultList();
+ final List<Long> yangResourceIds = query.getResultList();
+ if (yangResourceIds.size() != moduleReferences.size()) {
+ log.warn("ModuleReferences size : {} and QueryResult size : {}", moduleReferences.size(),
+ yangResourceIds.size());
+ }
+ return yangResourceIds;
}
private String getCombinedSelectSqlQuery(final Collection<ModuleReference> moduleReferences) {
final StringJoiner sqlQueryJoiner = new StringJoiner(" UNION ALL ");
- moduleReferences.stream().forEach(moduleReference -> {
+ moduleReferences.forEach(moduleReference ->
sqlQueryJoiner.add(String.format("SELECT id FROM yang_resource WHERE module_name='%s' and revision='%s'",
moduleReference.getModuleName(),
- moduleReference.getRevision()));
- });
+ moduleReference.getRevision()))
+ );
return sqlQueryJoiner.toString();
}
}
diff --git a/cps-ri/src/test/groovy/org/onap/cps/spi/impl/CpsDataPersistenceQueryDataNodeSpec.groovy b/cps-ri/src/test/groovy/org/onap/cps/spi/impl/CpsDataPersistenceQueryDataNodeSpec.groovy
index 36b378a..be2f8fe 100644
--- a/cps-ri/src/test/groovy/org/onap/cps/spi/impl/CpsDataPersistenceQueryDataNodeSpec.groovy
+++ b/cps-ri/src/test/groovy/org/onap/cps/spi/impl/CpsDataPersistenceQueryDataNodeSpec.groovy
@@ -50,7 +50,7 @@
scenario | cpsPath | includeDescendantsOption || expectedNumberOfParentNodes | expectedNumberOfChildNodes
'String and no descendants' | '/shops/shop[@id=1]/categories[@code=1]/book[@title="Dune"]' | OMIT_DESCENDANTS || 1 | 0
'Integer and descendants' | '/shops/shop[@id=1]/categories[@code=1]/book[@price=5]' | INCLUDE_ALL_DESCENDANTS || 1 | 1
- 'No condition no descendants' | '/shops/shop[@id=1]/categories' | OMIT_DESCENDANTS || 2 | 0
+ 'No condition no descendants' | '/shops/shop[@id=1]/categories' | OMIT_DESCENDANTS || 3 | 0
}
@Sql([CLEAR_DATA, SET_DATA])
@@ -91,16 +91,19 @@
assert result[i].getXpath() == expectedXPaths[i]
}
where: 'the following data is used'
- scenario | cpsPath || expectedXPaths
- 'fully unique descendant name' | '//categories[@code=2]' || ["/shops/shop[@id='1']/categories[@code='2']", "/shops/shop[@id='2']/categories[@code='1']", "/shops/shop[@id='2']/categories[@code='2']"]
- 'descendant name match end of other node' | '//book' || ["/shops/shop[@id='1']/categories[@code='1']/book", "/shops/shop[@id='1']/categories[@code='2']/book"]
- 'descendant with text condition on leaf' | '//book/title[text()="Chapters"]' || ["/shops/shop[@id='1']/categories[@code='2']/book"]
- 'descendant with text condition case mismatch' | '//book/title[text()="chapters"]' || []
- 'descendant with text condition on int leaf' | '//book/price[text()="5"]' || ["/shops/shop[@id='1']/categories[@code='1']/book"]
- 'descendant with text condition on leaf-list' | '//book/labels[text()="special offer"]' || ["/shops/shop[@id='1']/categories[@code='1']/book"]
- 'descendant with text condition partial match' | '//book/labels[text()="special"]' || []
- 'descendant with text condition (existing) empty string' | '//book/labels[text()=""]' || ["/shops/shop[@id='1']/categories[@code='1']/book"]
- 'descendant with text condition on int leaf-list' | '//book/editions[text()="2000"]' || ["/shops/shop[@id='1']/categories[@code='2']/book"]
+ scenario | cpsPath || expectedXPaths
+ 'fully unique descendant name' | '//categories[@code=2]' || ["/shops/shop[@id='1']/categories[@code='2']", "/shops/shop[@id='2']/categories[@code='1']", "/shops/shop[@id='2']/categories[@code='2']"]
+ 'descendant name match end of other node' | '//book' || ["/shops/shop[@id='1']/categories[@code='1']/book", "/shops/shop[@id='1']/categories[@code='2']/book"]
+ 'descendant with text condition on leaf' | '//book/title[text()="Chapters"]' || ["/shops/shop[@id='1']/categories[@code='2']/book"]
+ 'descendant with text condition case mismatch' | '//book/title[text()="chapters"]' || []
+ 'descendant with text condition on int leaf' | '//book/price[text()="5"]' || ["/shops/shop[@id='1']/categories[@code='1']/book"]
+ 'descendant with text condition on leaf-list' | '//book/labels[text()="special offer"]' || ["/shops/shop[@id='1']/categories[@code='1']/book"]
+ 'descendant with text condition partial match' | '//book/labels[text()="special"]' || []
+ 'descendant with text condition (existing) empty string' | '//book/labels[text()=""]' || ["/shops/shop[@id='1']/categories[@code='1']/book"]
+ 'descendant with text condition on int leaf-list' | '//book/editions[text()="2000"]' || ["/shops/shop[@id='1']/categories[@code='2']/book"]
+ 'descendant name match of leaf containing /' | '//categories/type[text()="text/with/slash"]' || ["/shops/shop[@id='1']/categories[@code='string/with/slash/']"]
+ 'descendant with text condition on leaf containing /' | '//categories[@code=\'string/with/slash\']' || ["/shops/shop[@id='1']/categories[@code='string/with/slash/']"]
+ 'descendant with text condition on leaf containing [' | '//book/author[@Address="String[with]square[bracket]"]'|| []
}
@Sql([CLEAR_DATA, SET_DATA])
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 acc243b..5e15ca7 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
@@ -27,6 +27,7 @@
import org.onap.cps.spi.CpsDataPersistenceService
import org.onap.cps.spi.entities.FragmentEntity
import org.onap.cps.spi.exceptions.AlreadyDefinedException
+import org.onap.cps.spi.exceptions.AlreadyDefinedExceptionBatch
import org.onap.cps.spi.exceptions.AnchorNotFoundException
import org.onap.cps.spi.exceptions.CpsAdminException
import org.onap.cps.spi.exceptions.CpsPathException
@@ -48,6 +49,7 @@
CpsDataPersistenceService objectUnderTest
static final JsonObjectMapper jsonObjectMapper = new JsonObjectMapper(new ObjectMapper())
+ static final DataNodeBuilder dataNodeBuilder = new DataNodeBuilder()
static final String SET_DATA = '/data/fragment.sql'
static final int DATASPACE_1001_ID = 1001L
@@ -165,7 +167,7 @@
def grandChild = buildDataNode('/parent-201/child-204[@key="NEW1"]/grand-child-204[@key2="NEW1-CHILD"]', [leave:'value'], [])
listElements[0].childDataNodes = [grandChild]
when: 'the new data node (list elements) are added to an existing parent node'
- objectUnderTest.addListElementsBatch(DATASPACE_NAME, ANCHOR_NAME3, '/parent-201', [listElements])
+ objectUnderTest.addMultipleLists(DATASPACE_NAME, ANCHOR_NAME3, '/parent-201', [listElements])
then: 'new entries are successfully persisted, parent node now contains 5 children (2 new + 3 existing before)'
def parentFragment = fragmentRepository.getById(LIST_DATA_NODE_PARENT201_FRAGMENT_ID)
def allChildXpaths = parentFragment.childFragments.collect { it.xpath }
@@ -179,17 +181,41 @@
}
@Sql([CLEAR_DATA, SET_DATA])
+ def 'Add multiple list with a mix of existing and new elements'() {
+ given: 'two new child list elements for an existing parent'
+ def existingDataNode = dataNodeBuilder.withXpath('/parent-100/child-001').withLeaves(['id': '001']).build()
+ def newDataNode1 = dataNodeBuilder.withXpath('/parent-100/child-new1').withLeaves(['id': 'new1']).build()
+ def newDataNode2 = dataNodeBuilder.withXpath('/parent-200/child-new2').withLeaves(['id': 'new2']).build()
+ def dataNodeList1 = [existingDataNode, newDataNode1]
+ def dataNodeList2 = [newDataNode2]
+ when: 'duplicate data node is requested to be added'
+ objectUnderTest.addMultipleLists(DATASPACE_NAME, ANCHOR_NAME3, '/', [dataNodeList1,dataNodeList2])
+ then: 'already defined batch exception is thrown'
+ def thrown = thrown(AlreadyDefinedExceptionBatch)
+ and: 'it only contains the xpath(s) of the duplicated elements'
+ assert thrown.alreadyDefinedXpaths.size() == 1
+ assert thrown.alreadyDefinedXpaths.contains('/parent-100/child-001')
+ and: 'it does NOT contains the xpaths of the new element that were not combined with existing elements'
+ assert !thrown.alreadyDefinedXpaths.contains('/parent-100/child-new1')
+ assert !thrown.alreadyDefinedXpaths.contains('/parent-100/child-new1')
+ and: 'the new entity is inserted correctly'
+ def dataspaceEntity = dataspaceRepository.getByName(DATASPACE_NAME)
+ def anchorEntity = anchorRepository.getByDataspaceAndName(dataspaceEntity, ANCHOR_NAME3)
+ fragmentRepository.findByDataspaceAndAnchorAndXpath(dataspaceEntity, anchorEntity, '/parent-200/child-new2').isPresent()
+ }
+
+ @Sql([CLEAR_DATA, SET_DATA])
def 'Add list element error scenario: #scenario.'() {
given: 'list element as a collection of data nodes'
- def listElementCollection = toDataNodes(listElementXpaths)
+ def listElements = toDataNodes(listElementXpaths)
when: 'attempt to add list elements to parent node'
- objectUnderTest.addListElements(DATASPACE_NAME, ANCHOR_NAME3, parentNodeXpath, listElementCollection)
+ objectUnderTest.addListElements(DATASPACE_NAME, ANCHOR_NAME3, parentNodeXpath, listElements)
then: 'a #expectedException is thrown'
thrown(expectedException)
where: 'following parameters were used'
scenario | parentNodeXpath | listElementXpaths || expectedException
'parent node does not exist' | '/unknown' | ['irrelevant'] || DataNodeNotFoundException
- 'data fragment already exists' | '/parent-201' | ["/parent-201/child-204[@key='A']"] || AlreadyDefinedException
+ 'data fragment already exists' | '/parent-201' | ["/parent-201/child-204[@key='A']"] || AlreadyDefinedExceptionBatch
}
@Sql([CLEAR_DATA, SET_DATA])
@@ -559,8 +585,9 @@
return xpaths.collect { new DataNodeBuilder().withXpath(it).build() }
}
+
static DataNode buildDataNode(xpath, leaves, childDataNodes) {
- return new DataNodeBuilder().withXpath(xpath).withLeaves(leaves).withChildDataNodes(childDataNodes).build()
+ return dataNodeBuilder.withXpath(xpath).withLeaves(leaves).withChildDataNodes(childDataNodes).build()
}
static Map<String, Object> getLeavesMap(FragmentEntity fragmentEntity) {
diff --git a/cps-ri/src/test/groovy/org/onap/cps/spi/impl/CpsDataPersistenceServiceSpec.groovy b/cps-ri/src/test/groovy/org/onap/cps/spi/impl/CpsDataPersistenceServiceSpec.groovy
index 1bbf358..470b03a 100644
--- a/cps-ri/src/test/groovy/org/onap/cps/spi/impl/CpsDataPersistenceServiceSpec.groovy
+++ b/cps-ri/src/test/groovy/org/onap/cps/spi/impl/CpsDataPersistenceServiceSpec.groovy
@@ -86,17 +86,25 @@
assert concurrencyException.getDetails().contains('/some/xpath')
}
- def 'Handling of StaleStateException (caused by concurrent updates) during update data nodes and descendants.'() {
- given: 'the fragment repository returns a list of fragment entities'
- mockFragmentRepository.getByDataspaceAndAnchorAndXpath(*_) >> new FragmentEntity()
- and: 'a data node is concurrently updated by another transaction'
+ def 'Handling of StaleStateException (caused by concurrent updates) during update data nodes and descendants.'() {
+ given: 'the system contains and can update one datanode'
+ def dataNode1 = mockDataNodeAndFragmentEntity('/node1', 'OK')
+ and: 'the system contains two more datanodes that throw an exception while updating'
+ def dataNode2 = mockDataNodeAndFragmentEntity('/node2', 'EXCEPTION')
+ def dataNode3 = mockDataNodeAndFragmentEntity('/node3', 'EXCEPTION')
+ and: 'the batch update will therefore also fail'
mockFragmentRepository.saveAll(*_) >> { throw new StaleStateException("concurrent updates") }
- when: 'attempt to update data node with submitted data nodes'
- objectUnderTest.updateDataNodesAndDescendants('some-dataspace', 'some-anchor', [])
+ when: 'attempt batch update data nodes'
+ objectUnderTest.updateDataNodesAndDescendants('some-dataspace', 'some-anchor', [dataNode1, dataNode2, dataNode3])
then: 'concurrency exception is thrown'
- def concurrencyException = thrown(ConcurrencyException)
- assert concurrencyException.getDetails().contains('some-dataspace')
- assert concurrencyException.getDetails().contains('some-anchor')
+ def thrown = thrown(ConcurrencyException)
+ assert thrown.message == 'Concurrent Transactions'
+ and: 'it does not contain the successfull datanode'
+ assert !thrown.details.contains('/node1')
+ and: 'it contains the failed datanodes'
+ assert thrown.details.contains('/node2')
+ assert thrown.details.contains('/node3')
+
}
def 'Retrieving a data node with a property JSON value of #scenario'() {
@@ -193,4 +201,14 @@
assert fragmentEntities.size() == 2
}})
}
+
+ def mockDataNodeAndFragmentEntity(xpath, scenario) {
+ def dataNode = new DataNodeBuilder().withXpath(xpath).build()
+ def fragmentEntity = new FragmentEntity(xpath: xpath, childFragments: [])
+ mockFragmentRepository.getByDataspaceAndAnchorAndXpath(_, _, xpath) >> fragmentEntity
+ if ('EXCEPTION' == scenario) {
+ mockFragmentRepository.save(fragmentEntity) >> { throw new StaleStateException("concurrent updates") }
+ }
+ return dataNode
+ }
}
\ No newline at end of file
diff --git a/cps-ri/src/test/groovy/org/onap/cps/spi/impl/CpsModulePersistenceServiceIntegrationSpec.groovy b/cps-ri/src/test/groovy/org/onap/cps/spi/impl/CpsModulePersistenceServiceIntegrationSpec.groovy
index eac2887..f9ebc52 100644
--- a/cps-ri/src/test/groovy/org/onap/cps/spi/impl/CpsModulePersistenceServiceIntegrationSpec.groovy
+++ b/cps-ri/src/test/groovy/org/onap/cps/spi/impl/CpsModulePersistenceServiceIntegrationSpec.groovy
@@ -31,8 +31,10 @@
import org.onap.cps.spi.repository.AnchorRepository
import org.onap.cps.spi.repository.SchemaSetRepository
import org.onap.cps.spi.repository.SchemaSetYangResourceRepositoryImpl
+import org.onap.cps.spi.repository.YangResourceRepository
import org.springframework.beans.factory.annotation.Autowired
import org.springframework.test.context.jdbc.Sql
+import spock.lang.Ignore
class CpsModulePersistenceServiceIntegrationSpec extends CpsPersistenceSpecBase {
@@ -48,6 +50,9 @@
@Autowired
CpsAdminPersistenceService cpsAdminPersistenceService
+ @Autowired
+ YangResourceRepository yangResourceRepository
+
final static String SET_DATA = '/data/schemaset.sql'
def static EXISTING_SCHEMA_SET_NAME = SCHEMA_SET_NAME1
@@ -77,6 +82,20 @@
}
@Sql([CLEAR_DATA, SET_DATA])
+ def 'Getting yang resource ids from module references'() {
+ when: 'getting yang resources for #scenario'
+ def result = yangResourceRepository.getResourceIdsByModuleReferences(moduleReferences)
+ then: 'the result contains the expected number entries'
+ assert result.size() == expectedResultSize
+ where: 'the following module references are provided'
+ scenario | moduleReferences || expectedResultSize
+ '2 valid module references' | [ new ModuleReference('MODULE-NAME-002','REVISION-002'), new ModuleReference('MODULE-NAME-003','REVISION-002') ] || 2
+ '1 invalid module reference' | [ new ModuleReference('NOT EXIST','IRRELEVANT') ] || 0
+ '1 valid and 1 invalid module reference' | [ new ModuleReference('MODULE-NAME-002','REVISION-002'), new ModuleReference('NOT EXIST','IRRELEVANT') ] || 1
+ 'no module references' | [] || 0
+ }
+
+ @Sql([CLEAR_DATA, SET_DATA])
def 'Store schema set error scenario: #scenario.'() {
when: 'attempt to store schema set #schemaSetName in dataspace #dataspaceName'
objectUnderTest.storeSchemaSet(dataspaceName, schemaSetName, newYangResourcesNameToContentMap)
diff --git a/cps-ri/src/test/resources/data/cps-path-query.sql b/cps-ri/src/test/resources/data/cps-path-query.sql
index b6000cf..fa711cb 100644
--- a/cps-ri/src/test/resources/data/cps-path-query.sql
+++ b/cps-ri/src/test/resources/data/cps-path-query.sql
@@ -55,10 +55,12 @@
(2, 1001, 1003, 1, '/shops/shop[@id=''1'']', '{"id" : 1, "type" : "bookstore"}'),
(3, 1001, 1003, 2, '/shops/shop[@id=''1'']/categories[@code=''1'']', '{"code" : 1, "type" : "bookstore", "name": "SciFi"}'),
(4, 1001, 1003, 2, '/shops/shop[@id=''1'']/categories[@code=''2'']', '{"code" : 2, "type" : "bookstore", "name": "Fiction"}'),
+ (31, 1001, 1003, 2, '/shops/shop[@id=''1'']/categories[@code=''string/with/slash/'']', '{"code" : "string/with/slash", "type" : "text/with/slash", "name": "Fiction"}'),
(5, 1001, 1003, 3, '/shops/shop[@id=''1'']/categories[@code=''1'']/book', '{"price" : 5, "title" : "Dune", "labels" : ["special offer","classics",""]}'),
(6, 1001, 1003, 4, '/shops/shop[@id=''1'']/categories[@code=''2'']/book', '{"price" : 15, "title" : "Chapters", "editions" : [2000,2010,2020]}'),
(7, 1001, 1003, 5, '/shops/shop[@id=''1'']/categories[@code=''1'']/book/author[@FirstName=''Joe'' and @Surname=''Bloggs'']', '{"FirstName" : "Joe", "Surname": "Bloggs","title": "Dune"}'),
- (8, 1001, 1003, 6, '/shops/shop[@id=''1'']/categories[@code=''2'']/book/author[@FirstName=''Joe'' and @Surname=''Smith'']', '{"FirstName" : "Joe", "Surname": "Smith","title": "Chapters"}');
+ (8, 1001, 1003, 6, '/shops/shop[@id=''1'']/categories[@code=''2'']/book/author[@FirstName=''Joe'' and @Surname=''Smith'']', '{"FirstName" : "Joe", "Surname": "Smith","title": "Chapters"}'),
+ (32, 1001, 1003, 6, '/shops/shop[@id=''1'']/categories[@code=''2'']/book/author[@FirstName=''Joe'' and @Address=''string[with]square[brackets]'']', '{"FirstName" : "Joe", "Address": "string[with]square[brackets]","title": "Chapters"}');
INSERT INTO FRAGMENT (ID, DATASPACE_ID, ANCHOR_ID, PARENT_ID, XPATH, ATTRIBUTES) VALUES
(9, 1001, 1003, 1, '/shops/shop[@id=''2'']', '{"type" : "bookstore"}'),
diff --git a/cps-service/src/main/java/org/onap/cps/api/CpsModuleService.java b/cps-service/src/main/java/org/onap/cps/api/CpsModuleService.java
index 5e8eb9f..6b17e82 100644
--- a/cps-service/src/main/java/org/onap/cps/api/CpsModuleService.java
+++ b/cps-service/src/main/java/org/onap/cps/api/CpsModuleService.java
@@ -50,11 +50,11 @@
* @param dataspaceName Dataspace name
* @param schemaSetName schema set name
* @param newModuleNameToContentMap YANG resources map where key is a module name and value is content
- * @param moduleReferences List of YANG resources module references of the modules
+ * @param allModuleReferences All YANG resource module references
*/
void createSchemaSetFromModules(String dataspaceName, String schemaSetName,
Map<String, String> newModuleNameToContentMap,
- Collection<ModuleReference> moduleReferences);
+ Collection<ModuleReference> allModuleReferences);
/**
* Read schema set in the given dataspace.
diff --git a/cps-service/src/main/java/org/onap/cps/api/impl/CpsDataServiceImpl.java b/cps-service/src/main/java/org/onap/cps/api/impl/CpsDataServiceImpl.java
index 6bf4935..b6aa04b 100755
--- a/cps-service/src/main/java/org/onap/cps/api/impl/CpsDataServiceImpl.java
+++ b/cps-service/src/main/java/org/onap/cps/api/impl/CpsDataServiceImpl.java
@@ -97,7 +97,7 @@
CpsValidator.validateNameCharacters(dataspaceName, anchorName);
final Collection<Collection<DataNode>> listElementDataNodeCollections =
buildDataNodes(dataspaceName, anchorName, parentNodeXpath, jsonDataList);
- cpsDataPersistenceService.addListElementsBatch(dataspaceName, anchorName, parentNodeXpath,
+ cpsDataPersistenceService.addMultipleLists(dataspaceName, anchorName, parentNodeXpath,
listElementDataNodeCollections);
processDataUpdatedEventAsync(dataspaceName, anchorName, parentNodeXpath, UPDATE, observedTimestamp);
}
diff --git a/cps-service/src/main/java/org/onap/cps/api/impl/CpsModuleServiceImpl.java b/cps-service/src/main/java/org/onap/cps/api/impl/CpsModuleServiceImpl.java
index ff725a6..20b4a23 100644
--- a/cps-service/src/main/java/org/onap/cps/api/impl/CpsModuleServiceImpl.java
+++ b/cps-service/src/main/java/org/onap/cps/api/impl/CpsModuleServiceImpl.java
@@ -60,10 +60,10 @@
@Override
public void createSchemaSetFromModules(final String dataspaceName, final String schemaSetName,
final Map<String, String> newModuleNameToContentMap,
- final Collection<ModuleReference> moduleReferences) {
+ final Collection<ModuleReference> allModuleReferences) {
CpsValidator.validateNameCharacters(dataspaceName, schemaSetName);
cpsModulePersistenceService.storeSchemaSetFromModules(dataspaceName, schemaSetName,
- newModuleNameToContentMap, moduleReferences);
+ newModuleNameToContentMap, allModuleReferences);
}
diff --git a/cps-service/src/main/java/org/onap/cps/spi/CpsDataPersistenceService.java b/cps-service/src/main/java/org/onap/cps/spi/CpsDataPersistenceService.java
index 8b45ae7..cd0cefc 100644
--- a/cps-service/src/main/java/org/onap/cps/spi/CpsDataPersistenceService.java
+++ b/cps-service/src/main/java/org/onap/cps/spi/CpsDataPersistenceService.java
@@ -66,15 +66,15 @@
Collection<DataNode> listElementsCollection);
/**
- * Adds list child elements to a Fragment.
+ * Add multiple lists of data nodes to a parent node at the same time.
*
* @param dataspaceName dataspace name
* @param anchorName anchor name
* @param parentNodeXpath parent node xpath
- * @param listElementsCollections collections of data nodes representing list elements
+ * @param newLists collections of lists of data nodes representing list elements
*/
- void addListElementsBatch(String dataspaceName, String anchorName, String parentNodeXpath,
- Collection<Collection<DataNode>> listElementsCollections);
+ void addMultipleLists(String dataspaceName, String anchorName, String parentNodeXpath,
+ Collection<Collection<DataNode>> newLists);
/**
* Retrieves datanode by XPath for given dataspace and anchor.
diff --git a/cps-service/src/main/java/org/onap/cps/spi/CpsModulePersistenceService.java b/cps-service/src/main/java/org/onap/cps/spi/CpsModulePersistenceService.java
index db2cb60..aaf6b38 100755
--- a/cps-service/src/main/java/org/onap/cps/spi/CpsModulePersistenceService.java
+++ b/cps-service/src/main/java/org/onap/cps/spi/CpsModulePersistenceService.java
@@ -43,13 +43,13 @@
/**
* Stores a schema set from new modules and existing modules.
*
- * @param dataspaceName Dataspace name
- * @param schemaSetName Schema set name
+ * @param dataspaceName Dataspace name
+ * @param schemaSetName Schema set name
* @param newModuleNameToContentMap YANG resources map where key is a module name and value is content
- * @param moduleReferences List of YANG resources module references
+ * @param allModuleReferences All YANG resources module references
*/
void storeSchemaSetFromModules(String dataspaceName, String schemaSetName,
- Map<String, String> newModuleNameToContentMap, Collection<ModuleReference> moduleReferences);
+ Map<String, String> newModuleNameToContentMap, Collection<ModuleReference> allModuleReferences);
/**
* Deletes Schema Set.
diff --git a/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/exception/InvalidTopicException.java b/cps-service/src/main/java/org/onap/cps/spi/exceptions/AlreadyDefinedExceptionBatch.java
similarity index 64%
copy from cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/exception/InvalidTopicException.java
copy to cps-service/src/main/java/org/onap/cps/spi/exceptions/AlreadyDefinedExceptionBatch.java
index b56ca7b..0ba656a 100644
--- a/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/exception/InvalidTopicException.java
+++ b/cps-service/src/main/java/org/onap/cps/spi/exceptions/AlreadyDefinedExceptionBatch.java
@@ -15,26 +15,20 @@
* limitations under the License.
*
* SPDX-License-Identifier: Apache-2.0
- * ============LICENSE_END=========================================================
+ * ============LICENSE_END=========================================================
*/
-package org.onap.cps.ncmp.api.impl.exception;
+package org.onap.cps.spi.exceptions;
+import java.util.Collection;
import lombok.Getter;
-public class InvalidTopicException extends RuntimeException {
+public class AlreadyDefinedExceptionBatch extends RuntimeException {
@Getter
- final String details;
+ private final Collection<String> alreadyDefinedXpaths;
- /**
- * Constructor.
- *
- * @param message the error message
- * @param details the error details
- */
- public InvalidTopicException(final String message, final String details) {
- super(message);
- this.details = details;
+ public AlreadyDefinedExceptionBatch(final Collection<String> alreadyDefinedXPaths) {
+ this.alreadyDefinedXpaths = alreadyDefinedXPaths;
}
}
diff --git a/cps-service/src/main/java/org/onap/cps/spi/exceptions/ConcurrencyException.java b/cps-service/src/main/java/org/onap/cps/spi/exceptions/ConcurrencyException.java
index 3a8a94b..b5eae93 100644
--- a/cps-service/src/main/java/org/onap/cps/spi/exceptions/ConcurrencyException.java
+++ b/cps-service/src/main/java/org/onap/cps/spi/exceptions/ConcurrencyException.java
@@ -20,8 +20,8 @@
public class ConcurrencyException extends CpsException {
- public ConcurrencyException(final String message, final String details, final Throwable cause) {
- super(message, details, cause);
+ public ConcurrencyException(final String message, final String details) {
+ super(message, details);
}
}
diff --git a/cps-service/src/main/java/org/onap/cps/utils/CpsValidator.java b/cps-service/src/main/java/org/onap/cps/utils/CpsValidator.java
index 28b49c9..f3774d9 100644
--- a/cps-service/src/main/java/org/onap/cps/utils/CpsValidator.java
+++ b/cps-service/src/main/java/org/onap/cps/utils/CpsValidator.java
@@ -22,7 +22,6 @@
import com.google.common.collect.Lists;
import java.util.Collection;
-import java.util.regex.Pattern;
import lombok.AccessLevel;
import lombok.NoArgsConstructor;
import lombok.extern.slf4j.Slf4j;
@@ -33,30 +32,25 @@
public final class CpsValidator {
private static final char[] UNSUPPORTED_NAME_CHARACTERS = "!\" #$%&'()*+,./\\:;<=>?@[]^`{|}~".toCharArray();
- private static final Pattern TOPIC_NAME_PATTERN = Pattern.compile("^[a-zA-Z0-9]([._-](?![._-])|"
- + "[a-zA-Z0-9]){0,120}[a-zA-Z0-9]$");
/**
* Validate characters in names within cps.
+ *
* @param names names of data to be validated
*/
public static void validateNameCharacters(final String... names) {
for (final String name : names) {
- final Collection<Character> charactersOfName = Lists.charactersOf(name);
+ final Collection<Character> charactersOfName = Lists.charactersOf(name);
for (final char unsupportedCharacter : UNSUPPORTED_NAME_CHARACTERS) {
if (charactersOfName.contains(unsupportedCharacter)) {
throw new DataValidationException("Name or ID Validation Error.",
- name + " invalid token encountered at position " + (name.indexOf(unsupportedCharacter) + 1));
+ name + " invalid token encountered at position "
+ + (name.indexOf(unsupportedCharacter) + 1));
}
}
}
}
- /**
- * Validate kafka topic name pattern.
- * @param topicName name of the topic to be validated
- */
- public static boolean validateTopicName(final String topicName) {
- return topicName != null && TOPIC_NAME_PATTERN.matcher(topicName).matches();
- }
+
+
}
diff --git a/cps-service/src/test/groovy/org/onap/cps/api/impl/CpsDataServiceImplSpec.groovy b/cps-service/src/test/groovy/org/onap/cps/api/impl/CpsDataServiceImplSpec.groovy
index ab960df..3f28f0a 100644
--- a/cps-service/src/test/groovy/org/onap/cps/api/impl/CpsDataServiceImplSpec.groovy
+++ b/cps-service/src/test/groovy/org/onap/cps/api/impl/CpsDataServiceImplSpec.groovy
@@ -143,7 +143,7 @@
def jsonData = '{"branch": [{"name": "A"}, {"name": "B"}]}'
objectUnderTest.saveListElementsBatch(dataspaceName, anchorName, '/test-tree', [jsonData], observedTimestamp)
then: 'the persistence service method is invoked with correct parameters'
- 1 * mockCpsDataPersistenceService.addListElementsBatch(dataspaceName, anchorName, '/test-tree',_) >> {
+ 1 * mockCpsDataPersistenceService.addMultipleLists(dataspaceName, anchorName, '/test-tree',_) >> {
args -> {
def listElementsCollection = args[3] as Collection<Collection<DataNode>>
assert listElementsCollection.size() == 1
diff --git a/cps-service/src/test/groovy/org/onap/cps/utils/CpsValidatorSpec.groovy b/cps-service/src/test/groovy/org/onap/cps/utils/CpsValidatorSpec.groovy
index ce728ef..ea7a5d6 100644
--- a/cps-service/src/test/groovy/org/onap/cps/utils/CpsValidatorSpec.groovy
+++ b/cps-service/src/test/groovy/org/onap/cps/utils/CpsValidatorSpec.groovy
@@ -25,7 +25,6 @@
class CpsValidatorSpec extends Specification {
-
def 'Validating a valid string.'() {
when: 'the string is validated using a valid name'
CpsValidator.validateNameCharacters('name-with-no-spaces')
@@ -46,17 +45,4 @@
'position 9' | 'nameWith Space' || 'nameWith Space invalid token encountered at position 9'
}
- def 'Validating topic names.'() {
- when: 'the topic name is validated'
- def isValidTopicName = CpsValidator.validateTopicName(topicName)
- then: 'boolean response will be returned for #scenario'
- assert isValidTopicName == booleanResponse
- where: 'the following names are used'
- scenario | topicName || booleanResponse
- 'valid topic' | 'my-topic-name' || true
- 'empty topic' | '' || false
- 'blank topic' | ' ' || false
- 'null topic' | null || false
- 'invalid non empty topic' | '1_5_*_#' || false
- }
}
diff --git a/docs/api/swagger/cps/openapi.yaml b/docs/api/swagger/cps/openapi.yaml
index 983252f..874f2a0 100644
--- a/docs/api/swagger/cps/openapi.yaml
+++ b/docs/api/swagger/cps/openapi.yaml
@@ -45,6 +45,16 @@
schema:
type: string
example: my-resource
+ "400":
+ description: Bad Request
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/ErrorMessage'
+ example:
+ status: 400
+ message: Bad Request
+ details: The provided request is not valid
"401":
description: Unauthorized
content:
diff --git a/docs/api/swagger/ncmp/openapi-inventory.yaml b/docs/api/swagger/ncmp/openapi-inventory.yaml
index 30896f6..2d34f0a 100644
--- a/docs/api/swagger/ncmp/openapi-inventory.yaml
+++ b/docs/api/swagger/ncmp/openapi-inventory.yaml
@@ -20,7 +20,7 @@
$ref: '#/components/schemas/RestDmiPluginRegistration'
required: true
responses:
- "204":
+ "200":
description: No Content
content: {}
"400":
@@ -54,6 +54,90 @@
message: Forbidden error message
details: Forbidden error details
"500":
+ description: Partial or Complete failure. The error details are provided
+ in the response body and all supported error codes are documented in the
+ example.
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/DmiPluginRegistrationErrorResponse'
+ example:
+ failedCreatedCmHandles:
+ - cmHandle: my-cm-handle-01
+ errorCode: "00"
+ errorText: Unknown error. <error-details>
+ - cmHandle: my-cm-handle-02
+ errorCode: "01"
+ errorText: cm-handle already exists
+ - cmHandle: my-cm-handle-03
+ errorCode: "03"
+ errorText: cm-handle has an invalid character(s) in id
+ failedUpdatedCmHandles:
+ - cmHandle: my-cm-handle-01
+ errorCode: "00"
+ errorText: Unknown error. <error-details>
+ - cmHandle: my-cm-handle-02
+ errorCode: "02"
+ errorText: cm-handle does not exist
+ - cmHandle: my-cm-handle-03
+ errorCode: "03"
+ errorText: cm-handle has an invalid character(s) in id
+ failedRemovedCmHandles:
+ - cmHandle: my-cm-handle-01
+ errorCode: "00"
+ errorText: Unknown error. <error-details>
+ - cmHandle: my-cm-handle-02
+ errorCode: "02"
+ errorText: cm-handle does not exists
+ - cmHandle: my-cm-handle-03
+ errorCode: "03"
+ errorText: cm-handle has an invalid character(s) in id
+ /v1/ch/cmHandles:
+ get:
+ tags:
+ - network-cm-proxy-inventory
+ summary: "Get all cm handle IDs for a registered DMI plugin (DMI plugin, DMI\
+ \ data plugin, DMI model plugin)"
+ description: Get all cm handle IDs for a registered DMI plugin
+ operationId: getAllCmHandleIdsForRegisteredDmi
+ parameters:
+ - name: dmi-plugin-identifier
+ in: query
+ description: dmi-plugin-identifier
+ required: true
+ schema:
+ type: string
+ example: my-dmi-plugin
+ responses:
+ "200":
+ description: OK
+ content:
+ application/json:
+ schema:
+ type: array
+ items:
+ type: string
+ "401":
+ description: Unauthorized
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/ErrorMessage'
+ example:
+ status: 401
+ message: Unauthorized error message
+ details: Unauthorized error details
+ "403":
+ description: Forbidden
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/ErrorMessage'
+ example:
+ status: 403
+ message: Forbidden error message
+ details: Forbidden error details
+ "500":
description: Internal Server Error
content:
application/json:
@@ -123,3 +207,30 @@
type: string
details:
type: string
+ DmiPluginRegistrationErrorResponse:
+ type: object
+ properties:
+ failedCreatedCmHandles:
+ type: array
+ items:
+ $ref: '#/components/schemas/CmHandlerRegistrationErrorResponse'
+ failedUpdatedCmHandles:
+ type: array
+ items:
+ $ref: '#/components/schemas/CmHandlerRegistrationErrorResponse'
+ failedRemovedCmHandles:
+ type: array
+ items:
+ $ref: '#/components/schemas/CmHandlerRegistrationErrorResponse'
+ CmHandlerRegistrationErrorResponse:
+ type: object
+ properties:
+ cmHandle:
+ type: string
+ example: my-cm-handle
+ errorCode:
+ type: string
+ example: "00"
+ errorText:
+ type: string
+ example: Unknown error. <error-details>
diff --git a/docs/api/swagger/ncmp/openapi.yaml b/docs/api/swagger/ncmp/openapi.yaml
index a43190b..44cc2b8 100644
--- a/docs/api/swagger/ncmp/openapi.yaml
+++ b/docs/api/swagger/ncmp/openapi.yaml
@@ -6,14 +6,21 @@
servers:
- url: /ncmp
paths:
- /v1/ch/{cm-handle}/data/ds/ncmp-datastore:passthrough-operational:
+ /v1/ch/{cm-handle}/data/ds/{ncmp-datastore-name}:
get:
tags:
- network-cm-proxy
- summary: Get resource data from pass-through operational for cm handle
- description: Get resource data from pass-through operational for given cm handle
- operationId: getResourceDataOperationalForCmHandle
+ summary: Get resource data for cm handle
+ description: Get resource data for given cm handle
+ operationId: getResourceDataForCmHandle
parameters:
+ - name: ncmp-datastore-name
+ in: path
+ description: The type of the requested data
+ required: true
+ schema:
+ type: string
+ example: ncmp-datastore:operational
- name: cm-handle
in: path
description: "The identifier for a network function, network element, subnetwork\
@@ -71,6 +78,13 @@
sample 1:
value:
topic: my-topic-name
+ - name: include-descendants
+ in: query
+ description: Determines if descendants are included in response
+ required: false
+ schema:
+ type: boolean
+ default: false
responses:
"200":
description: OK
@@ -128,134 +142,7 @@
schema:
$ref: '#/components/schemas/DmiErrorMessage'
example:
- message: "Bad Gateway Error Message NCMP"
- dmi-response:
- http-code: 400
- body: Bad Request
-
- /v1/ch/{cm-handle}/data/ds/ncmp-datastore:passthrough-running:
- get:
- tags:
- - network-cm-proxy
- summary: Get resource data from pass-through running for cm handle
- description: Get resource data from pass-through running for given cm handle
- operationId: getResourceDataRunningForCmHandle
- parameters:
- - name: cm-handle
- in: path
- description: "The identifier for a network function, network element, subnetwork\
- \ or any other cm object by managed Network CM Proxy"
- required: true
- schema:
- type: string
- example: my-cm-handle
- - name: resourceIdentifier
- 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:
- sample 1:
- value:
- resourceIdentifier: \shops\bookstore
- sample 2:
- value:
- resourceIdentifier: "\\shops\\bookstore\\categories[@code=1]"
- sample 3:
- value:
- resourceIdentifier: "parent=shops,child=bookstore"
- - name: options
- in: query
- description: "options parameter in query, it is mandatory to wrap key(s)=value(s)\
- \ in parenthesis'()'. The format of options parameter depend on the associated\
- \ DMI Plugin implementation."
- required: false
- allowReserved: true
- schema:
- type: string
- examples:
- sample 1:
- value:
- options: (depth=3)
- sample 2:
- value:
- options: (fields=book)
- sample 3:
- value:
- options: "(depth=2,fields=book/authors)"
- - name: topic
- in: query
- description: topic parameter in query.
- required: false
- allowReserved: true
- schema:
- type: string
- examples:
- sample 1:
- value:
- topic: my-topic-name
- responses:
- "200":
- description: OK
- content:
- application/json:
- schema:
- type: object
- examples:
- dataSampleResponse:
- $ref: '#/components/examples/dataSampleResponse'
- "400":
- description: Bad Request
- content:
- application/json:
- schema:
- $ref: '#/components/schemas/ErrorMessage'
- example:
- status: 400 BAD_REQUEST
- message: Bad request error message
- details: Bad request error details
- "401":
- description: Unauthorized
- content:
- application/json:
- schema:
- $ref: '#/components/schemas/ErrorMessage'
- example:
- status: 401
- message: Unauthorized error message
- details: Unauthorized error details
- "403":
- description: Forbidden
- content:
- application/json:
- schema:
- $ref: '#/components/schemas/ErrorMessage'
- example:
- status: 403
- message: Forbidden error message
- details: Forbidden error details
- "500":
- description: Internal Server Error
- content:
- application/json:
- schema:
- $ref: '#/components/schemas/ErrorMessage'
- example:
- status: 500
- message: Internal Server Error
- details: Internal Server Error occurred
- "502":
- description: Bad Gateway
- content:
- application/json:
- schema:
- $ref: '#/components/schemas/DmiErrorMessage'
- example:
- message: "Bad Gateway Error Message NCMP"
+ message: Bad Gateway Error Message NCMP
dmi-response:
http-code: 400
body: Bad Request
@@ -267,6 +154,13 @@
handle
operationId: updateResourceDataRunningForCmHandle
parameters:
+ - name: ncmp-datastore-name
+ in: path
+ description: The type of the requested data
+ required: true
+ schema:
+ type: string
+ example: ncmp-datastore:operational
- name: cm-handle
in: path
description: "The identifier for a network function, network element, subnetwork\
@@ -372,7 +266,7 @@
schema:
$ref: '#/components/schemas/DmiErrorMessage'
example:
- message: "Bad Gateway Error Message NCMP"
+ message: Bad Gateway Error Message NCMP
dmi-response:
http-code: 400
body: Bad Request
@@ -383,6 +277,13 @@
description: create resource data from pass-through running for given cm handle
operationId: createResourceDataRunningForCmHandle
parameters:
+ - name: ncmp-datastore-name
+ in: path
+ description: The type of the requested data
+ required: true
+ schema:
+ type: string
+ example: ncmp-datastore:operational
- name: cm-handle
in: path
description: "The identifier for a network function, network element, subnetwork\
@@ -485,7 +386,7 @@
schema:
$ref: '#/components/schemas/DmiErrorMessage'
example:
- message: "Bad Gateway Error Message NCMP"
+ message: Bad Gateway Error Message NCMP
dmi-response:
http-code: 400
body: Bad Request
@@ -496,6 +397,13 @@
description: Delete resource data from pass-through running for a given cm handle
operationId: deleteResourceDataRunningForCmHandle
parameters:
+ - name: ncmp-datastore-name
+ in: path
+ description: The type of the requested data
+ required: true
+ schema:
+ type: string
+ example: ncmp-datastore:operational
- name: cm-handle
in: path
description: "The identifier for a network function, network element, subnetwork\
@@ -593,7 +501,7 @@
schema:
$ref: '#/components/schemas/DmiErrorMessage'
example:
- message: "Bad Gateway Error Message NCMP"
+ message: Bad Gateway Error Message NCMP
dmi-response:
http-code: 400
body: Bad Request
@@ -605,6 +513,13 @@
handle
operationId: patchResourceDataRunningForCmHandle
parameters:
+ - name: ncmp-datastore-name
+ in: path
+ description: The type of the requested data
+ required: true
+ schema:
+ type: string
+ example: ncmp-datastore:operational
- name: cm-handle
in: path
description: "The identifier for a network function, network element, subnetwork\
@@ -704,7 +619,7 @@
schema:
$ref: '#/components/schemas/DmiErrorMessage'
example:
- message: "Bad Gateway Error Message NCMP"
+ message: Bad Gateway Error Message NCMP
dmi-response:
http-code: 400
body: Bad Request
@@ -774,19 +689,95 @@
status: 500
message: Internal Server Error
details: Internal Server Error occurred
+ /v1/ch/{cm-handle}/modules/definitions:
+ get:
+ tags:
+ - network-cm-proxy
+ summary: "Fetch all module definitions (name, revision, yang resource) for a\
+ \ given cm handle"
+ description: "Fetch all module definitions (name, revision, yang resource) for\
+ \ a given cm handle"
+ operationId: getModuleDefinitionsByCmHandleId
+ parameters:
+ - name: cm-handle
+ in: path
+ description: "The identifier for a network function, network element, subnetwork\
+ \ or any other cm object by managed Network CM Proxy"
+ required: true
+ schema:
+ type: string
+ example: my-cm-handle
+ responses:
+ "200":
+ description: OK
+ content:
+ application/json:
+ schema:
+ type: array
+ items:
+ $ref: '#/components/schemas/RestModuleDefinition'
+ "401":
+ description: Unauthorized
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/ErrorMessage'
+ example:
+ status: 401
+ message: Unauthorized error message
+ details: Unauthorized error details
+ "403":
+ description: Forbidden
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/ErrorMessage'
+ example:
+ status: 403
+ message: Forbidden error message
+ details: Forbidden error details
+ "500":
+ description: Internal Server Error
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/ErrorMessage'
+ example:
+ status: 500
+ message: Internal Server Error
+ details: Internal Server Error occurred
/v1/ch/searches:
post:
tags:
- network-cm-proxy
summary: Execute cm handle search using the available conditions
- description: Execute cm handle searches using 'hasAllModules' condition to get
- all cm handles for the given module names
- operationId: executeCmHandleSearch
+ description: Execute cm handle query search and return a list of cm handle details.
+ Any number of conditions can be applied. To be included in the result a cm-handle
+ must fulfill ALL the conditions. An empty collection will be returned in the
+ case that the cm handle does not match a condition. For more on cm handle
+ query search please refer to <a href="https://docs.onap.org/projects/onap-cps/en/latest/ncmp-cmhandle-querying.html">cm
+ handle query search Read the Docs</a>.<br/>By supplying a CPS Path it is possible
+ to query on any data related to the cm handle. For more on CPS Path please
+ refer to <a href="https://docs.onap.org/projects/onap-cps/en/latest/cps-path.html">CPS
+ Path Read the Docs</a>. The cm handle ancestor is automatically returned for
+ this query.
+ operationId: searchCmHandles
requestBody:
content:
application/json:
schema:
- $ref: '#/components/schemas/Conditions'
+ $ref: '#/components/schemas/CmHandleQueryParameters'
+ examples:
+ Cm handle properties query:
+ $ref: '#/components/examples/pubPropCmHandleQueryParameters'
+ Cm handle modules query:
+ $ref: '#/components/examples/modulesCmHandleQueryParameters'
+ All cm handle query parameters:
+ $ref: '#/components/examples/allCmHandleQueryParameters'
+ Cm handle with CPS path state query:
+ $ref: '#/components/examples/cpsPathCmHandleStateQueryParameters'
+ Cm handle with data sync flag query:
+ $ref: '#/components/examples/cpsPathCmHandleDataSyncQueryParameters'
required: true
responses:
"200":
@@ -794,7 +785,9 @@
content:
application/json:
schema:
- $ref: '#/components/schemas/CmHandles'
+ type: array
+ items:
+ $ref: '#/components/schemas/RestOutputCmHandle'
"400":
description: Bad Request
content:
@@ -878,6 +871,151 @@
status: 401
message: Unauthorized error message
details: Unauthorized error details
+ "404":
+ description: The specified resource was not found
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/ErrorMessage'
+ example:
+ status: 400
+ message: Not found error message
+ details: Not found error details
+ "500":
+ description: Internal Server Error
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/ErrorMessage'
+ example:
+ status: 500
+ message: Internal Server Error
+ details: Internal Server Error occurred
+ /v1/ch/{cm-handle}/properties:
+ get:
+ tags:
+ - network-cm-proxy
+ summary: Get CM handle properties
+ description: Get CM handle properties by cm handle id
+ operationId: getCmHandlePublicPropertiesByCmHandleId
+ parameters:
+ - name: cm-handle
+ in: path
+ description: "The identifier for a network function, network element, subnetwork\
+ \ or any other cm object by managed Network CM Proxy"
+ required: true
+ schema:
+ type: string
+ example: my-cm-handle
+ responses:
+ "200":
+ description: OK
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/RestOutputCmHandlePublicProperties'
+ "400":
+ description: Bad Request
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/ErrorMessage'
+ example:
+ status: 400 BAD_REQUEST
+ message: Bad request error message
+ details: Bad request error details
+ "401":
+ description: Unauthorized
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/ErrorMessage'
+ example:
+ status: 401
+ message: Unauthorized error message
+ details: Unauthorized error details
+ "404":
+ description: The specified resource was not found
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/ErrorMessage'
+ example:
+ status: 400
+ message: Not found error message
+ details: Not found error details
+ "500":
+ description: Internal Server Error
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/ErrorMessage'
+ example:
+ status: 500
+ message: Internal Server Error
+ details: Internal Server Error occurred
+ /v1/ch/id-searches:
+ post:
+ tags:
+ - network-cm-proxy
+ summary: Execute cm handle query upon a given set of query parameters
+ description: Execute cm handle query search and return a list of cm handle ids.
+ Any number of conditions can be applied. To be included in the result a cm-handle
+ must fulfill ALL the conditions. An empty collection will be returned in the
+ case that the cm handle does not match a condition. For more on cm handle
+ query search please refer to <a href="https://docs.onap.org/projects/onap-cps/en/latest/ncmp-cmhandle-querying.html">cm
+ handle query search Read the Docs</a>.<br/>By supplying a CPS Path it is possible
+ to query on any data related to the cm handle. For more on CPS Path please
+ refer to <a href="https://docs.onap.org/projects/onap-cps/en/latest/cps-path.html">CPS
+ Path Read the Docs</a>. The cm handle ancestor is automatically returned for
+ this query.
+ operationId: searchCmHandleIds
+ requestBody:
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/CmHandleQueryParameters'
+ examples:
+ Cm handle properties query:
+ $ref: '#/components/examples/pubPropCmHandleQueryParameters'
+ Cm handle modules query:
+ $ref: '#/components/examples/modulesCmHandleQueryParameters'
+ All cm handle query parameters:
+ $ref: '#/components/examples/allCmHandleQueryParameters'
+ Cm handle with CPS path state query:
+ $ref: '#/components/examples/cpsPathCmHandleStateQueryParameters'
+ Cm handle with data sync flag query:
+ $ref: '#/components/examples/cpsPathCmHandleDataSyncQueryParameters'
+ required: true
+ responses:
+ "200":
+ description: OK
+ content:
+ application/json:
+ schema:
+ type: array
+ items:
+ type: string
+ "400":
+ description: Bad Request
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/ErrorMessage'
+ example:
+ status: 400 BAD_REQUEST
+ message: Bad request error message
+ details: Bad request error details
+ "401":
+ description: Unauthorized
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/ErrorMessage'
+ example:
+ status: 401
+ message: Unauthorized error message
+ details: Unauthorized error details
"403":
description: Forbidden
content:
@@ -908,6 +1046,152 @@
status: 500
message: Internal Server Error
details: Internal Server Error occurred
+ /v1/ch/{cm-handle}/state:
+ get:
+ tags:
+ - network-cm-proxy
+ summary: Get CM handle state
+ description: Get CM handle state by cm handle id
+ operationId: getCmHandleStateByCmHandleId
+ parameters:
+ - name: cm-handle
+ in: path
+ description: "The identifier for a network function, network element, subnetwork\
+ \ or any other cm object by managed Network CM Proxy"
+ required: true
+ schema:
+ type: string
+ example: my-cm-handle
+ responses:
+ "200":
+ description: OK
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/RestOutputCmHandleCompositeState'
+ "400":
+ description: Bad Request
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/ErrorMessage'
+ example:
+ status: 400 BAD_REQUEST
+ message: Bad request error message
+ details: Bad request error details
+ "401":
+ description: Unauthorized
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/ErrorMessage'
+ example:
+ status: 401
+ message: Unauthorized error message
+ details: Unauthorized error details
+ "404":
+ description: The specified resource was not found
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/ErrorMessage'
+ example:
+ status: 400
+ message: Not found error message
+ details: Not found error details
+ "500":
+ description: Internal Server Error
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/ErrorMessage'
+ example:
+ status: 500
+ message: Internal Server Error
+ details: Internal Server Error occurred
+ /v1/ch/{cm-handle}/data-sync:
+ put:
+ tags:
+ - network-cm-proxy
+ summary: Set the Data Sync Enabled Flag
+ description: Set the data sync enabled flag to true or false for a specified
+ Cm-Handle. This will in turn set the data sync state to UNSYNCHRONIZED and
+ NONE_REQUESTED respectfully.
+ operationId: setDataSyncEnabledFlagForCmHandle
+ parameters:
+ - name: cm-handle
+ in: path
+ description: "The identifier for a network function, network element, subnetwork\
+ \ or any other cm object by managed Network CM Proxy"
+ required: true
+ schema:
+ type: string
+ example: my-cm-handle
+ - name: dataSyncEnabled
+ in: query
+ description: Is used to enable or disable the data synchronization flag
+ required: true
+ schema:
+ type: boolean
+ example: true
+ responses:
+ "200":
+ description: OK
+ content:
+ application/json:
+ schema:
+ type: object
+ "400":
+ description: Bad Request
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/ErrorMessage'
+ example:
+ status: 400 BAD_REQUEST
+ message: Bad request error message
+ details: Bad request error details
+ "401":
+ description: Unauthorized
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/ErrorMessage'
+ example:
+ status: 401
+ message: Unauthorized error message
+ details: Unauthorized error details
+ "403":
+ description: Forbidden
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/ErrorMessage'
+ example:
+ status: 403
+ message: Forbidden error message
+ details: Forbidden error details
+ "500":
+ description: Internal Server Error
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/ErrorMessage'
+ example:
+ status: 500
+ message: Internal Server Error
+ details: Internal Server Error occurred
+ "502":
+ description: Bad Gateway
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/DmiErrorMessage'
+ example:
+ message: Bad Gateway Error Message NCMP
+ dmi-response:
+ http-code: 400
+ body: Bad Request
components:
schemas:
ErrorMessage:
@@ -920,23 +1204,15 @@
type: string
details:
type: string
- # DMI Server Exception Schema
DmiErrorMessage:
title: DMI Error Message
type: object
properties:
message:
type: string
- example: "Bad Gateway Error Message NCMP"
+ example: Bad Gateway Error Message NCMP
dmi-response:
- type: object
- properties:
- http-code:
- type: integer
- example: 400
- body:
- type: string
- example: Bad Request
+ $ref: '#/components/schemas/DmiErrorMessage_dmiresponse'
RestModuleReference:
title: Module reference details
type: object
@@ -947,45 +1223,59 @@
revision:
type: string
example: my-module-revision
- Conditions:
+ RestModuleDefinition:
+ title: Module definitions
type: object
properties:
+ moduleName:
+ type: string
+ example: my-module-name
+ revision:
+ type: string
+ example: 2020-09-15T00:00:00.000+00:00
+ content:
+ type: string
+ example: "module stores {\n yang-version 1.1;\n namespace \"org:onap:ccsdk:sample\"\
+ ;\n prefix book-store;\n revision \"2020-09-15\" {\n description\n\
+ \ \"Sample Model\";\n }\n}\n"
+ CmHandleQueryParameters:
+ title: Cm Handle query parameters for executing cm handle search
+ type: object
+ properties:
+ cmHandleQueryParameters:
+ type: array
+ items:
+ $ref: '#/components/schemas/ConditionProperties'
conditions:
- $ref: '#/components/schemas/ConditionsData'
- ConditionsData:
- type: array
- items:
- $ref: '#/components/schemas/ConditionProperties'
+ type: array
+ description: "not necessary, it is just for backward compatibility"
+ deprecated: true
+ items:
+ $ref: '#/components/schemas/OldConditionProperties'
ConditionProperties:
properties:
+ conditionName:
+ type: string
+ conditionParameters:
+ type: array
+ items:
+ type: object
+ additionalProperties:
+ type: string
+ OldConditionProperties:
+ properties:
name:
type: string
- example: hasAllModules
conditionParameters:
- $ref: '#/components/schemas/ModuleNamesAsJsonArray'
- ModuleNamesAsJsonArray:
- type: array
- items:
- $ref: '#/components/schemas/ModuleNameAsJsonObject'
+ type: array
+ items:
+ $ref: '#/components/schemas/ModuleNameAsJsonObject'
+ deprecated: true
ModuleNameAsJsonObject:
properties:
moduleName:
type: string
example: my-module
- CmHandles:
- type: object
- properties:
- cmHandles:
- $ref: '#/components/schemas/CmHandleProperties'
- CmHandleProperties:
- type: array
- items:
- $ref: '#/components/schemas/CmHandleProperty'
- CmHandleProperty:
- properties:
- cmHandleId:
- type: string
- example: my-cm-handle-id
RestOutputCmHandle:
title: CM handle Details
type: object
@@ -995,6 +1285,8 @@
example: my-cm-handle1
publicCmHandleProperties:
$ref: '#/components/schemas/CmHandlePublicProperties'
+ state:
+ $ref: '#/components/schemas/CmHandleCompositeState'
CmHandlePublicProperties:
type: array
items:
@@ -1002,6 +1294,66 @@
additionalProperties:
type: string
example: Book Type
+ CmHandleCompositeState:
+ type: object
+ properties:
+ cmHandleState:
+ type: string
+ example: ADVISED
+ lockReason:
+ $ref: '#/components/schemas/lock-reason'
+ lastUpdateTime:
+ type: string
+ example: 2022-12-31T20:30:40.000+0000
+ dataSyncEnabled:
+ type: boolean
+ example: false
+ dataSyncState:
+ $ref: '#/components/schemas/dataStores'
+ lock-reason:
+ type: object
+ properties:
+ reason:
+ type: string
+ example: LOCKED_MISBEHAVING
+ details:
+ type: string
+ example: locked due to failure in module sync
+ dataStores:
+ type: object
+ properties:
+ operational:
+ $ref: '#/components/schemas/sync-state'
+ running:
+ $ref: '#/components/schemas/sync-state'
+ sync-state:
+ type: object
+ properties:
+ syncState:
+ type: string
+ example: NONE_REQUESTED
+ lastSyncTime:
+ type: string
+ example: 2022-12-31T20:30:40.000+0000
+ RestOutputCmHandlePublicProperties:
+ type: object
+ properties:
+ publicCmHandleProperties:
+ $ref: '#/components/schemas/CmHandlePublicProperties'
+ RestOutputCmHandleCompositeState:
+ type: object
+ properties:
+ state:
+ $ref: '#/components/schemas/CmHandleCompositeState'
+ DmiErrorMessage_dmiresponse:
+ type: object
+ properties:
+ http-code:
+ type: integer
+ example: 400
+ body:
+ type: string
+ example: Bad Request
examples:
dataSampleResponse:
summary: Sample response
@@ -1081,3 +1433,47 @@
books:
- authors:
- Philip Pullman
+ pubPropCmHandleQueryParameters:
+ value:
+ cmHandleQueryParameters:
+ - conditionName: hasAllProperties
+ conditionParameters:
+ - Color: yellow
+ - Shape: circle
+ - Size: small
+ modulesCmHandleQueryParameters:
+ value:
+ cmHandleQueryParameters:
+ - conditionName: hasAllModules
+ conditionParameters:
+ - moduleName: my-module-1
+ - moduleName: my-module-2
+ - moduleName: my-module-3
+ allCmHandleQueryParameters:
+ value:
+ cmHandleQueryParameters:
+ - conditionName: hasAllModules
+ conditionParameters:
+ - moduleName: my-module-1
+ - moduleName: my-module-2
+ - moduleName: my-module-3
+ - conditionName: hasAllProperties
+ conditionParameters:
+ - Color: yellow
+ - Shape: circle
+ - Size: small
+ - conditionName: cmHandleWithCpsPath
+ conditionParameters:
+ - cpsPath: "//state[@cm-handle-state='ADVISED']"
+ cpsPathCmHandleStateQueryParameters:
+ value:
+ cmHandleQueryParameters:
+ - conditionName: cmHandleWithCpsPath
+ conditionParameters:
+ - cpsPath: "//state[@cm-handle-state='LOCKED']"
+ cpsPathCmHandleDataSyncQueryParameters:
+ value:
+ cmHandleQueryParameters:
+ - conditionName: cmHandleWithCpsPath
+ conditionParameters:
+ - cpsPath: "//state[@data-sync-enabled='true']"
diff --git a/docs/release-notes.rst b/docs/release-notes.rst
index 58dc060..d4f9843 100755
--- a/docs/release-notes.rst
+++ b/docs/release-notes.rst
@@ -39,6 +39,11 @@
Features
--------
- `CPS-322 <https://jira.onap.org/browse/CPS-322>`_ Implement additional validation for names and identifiers
+ - `CPS-1136 <https://jira.onap.org/browse/CPS-1136>`_ Get all cm handles by DMI plugin Identifier
+ - `CPS-1001 <https://jira.onap.org/browse/CPS-1001>`_ Add CPS-E-05 endpoint for Read data, NCMP-Operational Datastore
+
+Bug Fixes
+---------
.. ========================
.. * * * JAKARTA * * *
diff --git a/releases/3.1.0-container.yaml b/releases/3.1.0-container.yaml
new file mode 100644
index 0000000..cc11ed2
--- /dev/null
+++ b/releases/3.1.0-container.yaml
@@ -0,0 +1,8 @@
+distribution_type: container
+container_release_tag: 3.1.0
+project: cps
+log_dir: cps-maven-docker-stage-master/690/
+ref: 9697e76c319e4cf59fc494216a720393545503a9
+containers:
+ - name: 'cps-and-ncmp'
+ version: '3.1.0-20220914T140025Z'
diff --git a/releases/3.1.0.yaml b/releases/3.1.0.yaml
new file mode 100644
index 0000000..1258b26
--- /dev/null
+++ b/releases/3.1.0.yaml
@@ -0,0 +1,4 @@
+distribution_type: maven
+log_dir: cps-maven-stage-master/696/
+project: cps
+version: 3.1.0