diff options
60 files changed, 899 insertions, 254 deletions
@@ -39,7 +39,7 @@ mailing_list: realtime_discussion: '' meetings: - type: 'zoom' - agenda: 'https://wiki.onap.org/pages/viewpage.action?pageId=84644224' + agenda: 'https://wiki.onap.org/pages/viewpage.action?pageId=111117075' url: 'https://zoom.us/j/836561560?pwd=TTZNcFhXTWYxMmZ4SlgzcVZZQXluUT09' server: 'n/a' channel: 'n/a' diff --git a/checkstyle/pom.xml b/checkstyle/pom.xml index 3b7f3ed32d..2843c11b25 100644 --- a/checkstyle/pom.xml +++ b/checkstyle/pom.xml @@ -25,7 +25,7 @@ <modelVersion>4.0.0</modelVersion> <groupId>org.onap.cps</groupId> <artifactId>checkstyle</artifactId> - <version>1.1.0-SNAPSHOT</version> + <version>2.0.1-SNAPSHOT</version> <properties> <nexusproxy>https://nexus.onap.org</nexusproxy> diff --git a/cps-application/pom.xml b/cps-application/pom.xml index 9ce0006ce1..dfa469745e 100755 --- a/cps-application/pom.xml +++ b/cps-application/pom.xml @@ -27,7 +27,7 @@ <parent> <groupId>org.onap.cps</groupId> <artifactId>cps-parent</artifactId> - <version>1.1.0-SNAPSHOT</version> + <version>2.0.1-SNAPSHOT</version> <relativePath>../cps-parent/pom.xml</relativePath> </parent> diff --git a/cps-application/src/main/resources/application.yml b/cps-application/src/main/resources/application.yml index 82c6e0aed1..42addf1b0c 100644 --- a/cps-application/src/main/resources/application.yml +++ b/cps-application/src/main/resources/application.yml @@ -74,12 +74,14 @@ notification: topic: ${CPS_CHANGE_EVENT_TOPIC:cps.data-updated-events}
filters:
enabled-dataspaces: ${NOTIFICATION_DATASPACE_FILTER_PATTERNS:""}
- async-executor:
- core-pool-size: 2
- max-pool-size: 10
- queue-capacity: 500
- wait-for-tasks-to-complete-on-shutdown: true
- thread-name-prefix: Async-
+ async:
+ enabled: false
+ executor:
+ core-pool-size: 2
+ max-pool-size: 10
+ queue-capacity: 500
+ wait-for-tasks-to-complete-on-shutdown: true
+ thread-name-prefix: Async-
springdoc:
diff --git a/cps-bom/pom.xml b/cps-bom/pom.xml index 1f8789cd57..148b14508d 100644 --- a/cps-bom/pom.xml +++ b/cps-bom/pom.xml @@ -25,7 +25,7 @@ <modelVersion>4.0.0</modelVersion> <groupId>org.onap.cps</groupId> <artifactId>cps-bom</artifactId> - <version>1.1.0-SNAPSHOT</version> + <version>2.0.1-SNAPSHOT</version> <packaging>pom</packaging> <description>This artifact contains dependencyManagement declarations of all published CPS components.</description> diff --git a/cps-dependencies/pom.xml b/cps-dependencies/pom.xml index 1cd36f6838..348244de37 100755 --- a/cps-dependencies/pom.xml +++ b/cps-dependencies/pom.xml @@ -23,7 +23,7 @@ <modelVersion>4.0.0</modelVersion> <groupId>org.onap.cps</groupId> <artifactId>cps-dependencies</artifactId> - <version>1.1.0-SNAPSHOT</version> + <version>2.0.1-SNAPSHOT</version> <packaging>pom</packaging> <name>${project.groupId}:${project.artifactId}</name> diff --git a/cps-events/pom.xml b/cps-events/pom.xml index 3844408463..3dc5f1e0ac 100644 --- a/cps-events/pom.xml +++ b/cps-events/pom.xml @@ -24,7 +24,7 @@ <parent> <groupId>org.onap.cps</groupId> <artifactId>cps-parent</artifactId> - <version>1.1.0-SNAPSHOT</version> + <version>2.0.1-SNAPSHOT</version> <relativePath>../cps-parent/pom.xml</relativePath> </parent> diff --git a/cps-ncmp-rest/docs/openapi/ncmproxy.yml b/cps-ncmp-rest/docs/openapi/ncmproxy.yml index 138337d24e..5e2957f340 100755 --- a/cps-ncmp-rest/docs/openapi/ncmproxy.yml +++ b/cps-ncmp-rest/docs/openapi/ncmproxy.yml @@ -280,4 +280,31 @@ resourceDataForPassthroughRunning: 403: $ref: 'components.yaml#/components/responses/Forbidden' 404: + $ref: 'components.yaml#/components/responses/NotFound' + +fetchModuleReferencesByCmHandle: + get: + description: fetch all module references (name and revision) for a given cm handle + tags: + - network-cm-proxy + summary: Fetch all module references (name and revision) for a given cm handle + operationId: getModuleReferencesByCmHandle + parameters: + - $ref: 'components.yaml#/components/parameters/cmHandleInPath' + responses: + 200: + description: OK + content: + application/json: + schema: + type: string + example: [{"moduleName": "nc-notifications", "revision": "2008-07-14"}] + $ref: 'components.yaml#/components/responses/Ok' + 400: + $ref: 'components.yaml#/components/responses/BadRequest' + 401: + $ref: 'components.yaml#/components/responses/Unauthorized' + 403: + $ref: 'components.yaml#/components/responses/Forbidden' + 404: $ref: 'components.yaml#/components/responses/NotFound'
\ No newline at end of file diff --git a/cps-ncmp-rest/docs/openapi/openapi.yml b/cps-ncmp-rest/docs/openapi/openapi.yml index 8d8684a355..12356b5887 100755 --- a/cps-ncmp-rest/docs/openapi/openapi.yml +++ b/cps-ncmp-rest/docs/openapi/openapi.yml @@ -45,4 +45,7 @@ paths: $ref: 'ncmproxy.yml#/getResourceDataForPassthroughOperational' /v1/ch/{cm-handle}/data/ds/ncmp-datastore:passthrough-running/{resourceIdentifier}: - $ref: 'ncmproxy.yml#/resourceDataForPassthroughRunning'
\ No newline at end of file + $ref: 'ncmproxy.yml#/resourceDataForPassthroughRunning' + + /v1/ch/{cm-handle}/modules: + $ref: 'ncmproxy.yml#/fetchModuleReferencesByCmHandle'
\ No newline at end of file diff --git a/cps-ncmp-rest/pom.xml b/cps-ncmp-rest/pom.xml index d3de688e23..e77ede50dc 100644 --- a/cps-ncmp-rest/pom.xml +++ b/cps-ncmp-rest/pom.xml @@ -27,7 +27,7 @@ <parent> <groupId>org.onap.cps</groupId> <artifactId>cps-parent</artifactId> - <version>1.1.0-SNAPSHOT</version> + <version>2.0.1-SNAPSHOT</version> <relativePath>../cps-parent/pom.xml</relativePath> </parent> 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 f5ffdbeb92..b78241662e 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 @@ -35,6 +35,7 @@ import org.onap.cps.ncmp.rest.api.NetworkCmProxyApi; import org.onap.cps.ncmp.rest.model.RestDmiPluginRegistration; import org.onap.cps.spi.FetchDescendantsOption; import org.onap.cps.spi.model.DataNode; +import org.onap.cps.spi.model.ModuleReference; import org.onap.cps.utils.DataMapUtils; import org.springframework.http.HttpStatus; import org.springframework.http.ResponseEntity; @@ -220,6 +221,13 @@ public class NetworkCmProxyController implements NetworkCmProxyApi { return new ResponseEntity<>(HttpStatus.CREATED); } + @Override + public ResponseEntity<Object> getModuleReferencesByCmHandle(final String cmHandle) { + final Collection<ModuleReference> + moduleReferences = networkCmProxyDataService.getYangResourcesModuleReferences(cmHandle); + return new ResponseEntity<>(new Gson().toJson(moduleReferences), HttpStatus.OK); + } + private DmiPluginRegistration convertRestObjectToJavaApiObject( final RestDmiPluginRegistration restDmiPluginRegistration) { return objectMapper.convertValue(restDmiPluginRegistration, DmiPluginRegistration.class); diff --git a/cps-ncmp-rest/src/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 73ccd6e3c2..613243e28b 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 @@ -22,6 +22,8 @@ package org.onap.cps.ncmp.rest.controller +import org.onap.cps.spi.model.ModuleReference + import static org.onap.cps.spi.FetchDescendantsOption.INCLUDE_ALL_DESCENDANTS import static org.onap.cps.spi.FetchDescendantsOption.OMIT_DESCENDANTS import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get @@ -246,5 +248,19 @@ class NetworkCmProxyControllerSpec extends Specification { and: 'resource is created' response.status == HttpStatus.CREATED.value() } + + 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 + then: 'ncmp service method to get yang resource module references is called' + mockNetworkCmProxyDataService.getYangResourcesModuleReferences('some-cmhandle') + >> [new ModuleReference(moduleName: 'some-name1',revision: 'some-revision1')] + and: 'response contains an array with the module name and revision' + response.getContentAsString() == '[{"moduleName":"some-name1","revision":"some-revision1"}]' + and: 'response returns an OK http code' + response.status == HttpStatus.OK.value() + } } diff --git a/cps-ncmp-service/pom.xml b/cps-ncmp-service/pom.xml index fc01c3b1ca..294b290161 100644 --- a/cps-ncmp-service/pom.xml +++ b/cps-ncmp-service/pom.xml @@ -26,7 +26,7 @@ <parent> <groupId>org.onap.cps</groupId> <artifactId>cps-parent</artifactId> - <version>1.1.0-SNAPSHOT</version> + <version>2.0.1-SNAPSHOT</version> <relativePath>../cps-parent/pom.xml</relativePath> </parent> 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 0693f61e44..60669b9169 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 @@ -28,6 +28,7 @@ import org.checkerframework.checker.nullness.qual.NonNull; import org.onap.cps.ncmp.api.models.DmiPluginRegistration; import org.onap.cps.spi.FetchDescendantsOption; import org.onap.cps.spi.model.DataNode; +import org.onap.cps.spi.model.ModuleReference; /* * Datastore interface for handling CPS data. @@ -154,4 +155,12 @@ public interface NetworkCmProxyDataService { @NotNull String resourceIdentifier, @NotNull Object requestBody, String contentType); + + /** + * Retrieve module references for the given cm handle. + * + * @param cmHandle cm handle + * @return a collection of modules names and revisions + */ + Collection<ModuleReference> getYangResourcesModuleReferences(@NotNull String cmHandle); } diff --git a/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/JsonUtils.java b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/JsonUtils.java new file mode 100644 index 0000000000..6768777e17 --- /dev/null +++ b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/JsonUtils.java @@ -0,0 +1,52 @@ +/* + * ============LICENSE_START======================================================= + * Copyright (C) 2021 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.impl; + +public class JsonUtils { + + private static final String BACK_SLASH = "\\"; + private static final String NEW_LINE = "\n"; + private static final String QUOTE = "\""; + + private JsonUtils() { + throw new IllegalStateException(); + } + + /** + * Remove redundant beginning and end characters. + * @param input string to format + * @return formatted string + */ + public static String removeWrappingTokens(final String input) { + return input.substring(1, input.length() - 1); + } + + /** + * Remove redundant escape characters. + * @param input string to format + * @return formatted string + */ + public static String removeRedundantEscapeCharacters(final String input) { + return input.replace(BACK_SLASH + "n", NEW_LINE) + .replace(BACK_SLASH + QUOTE, QUOTE) + .replace(BACK_SLASH + BACK_SLASH + QUOTE, BACK_SLASH + QUOTE); + } +} 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 dfe5603431..a28b73c427 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 @@ -76,6 +76,8 @@ public class NetworkCmProxyDataServiceImpl implements NetworkCmProxyDataService private static final String NCMP_DMI_SERVICE_NAME = "dmi-service-name"; + private static final String REVISION = "revision"; + private CpsDataService cpsDataService; private ObjectMapper objectMapper; @@ -88,8 +90,6 @@ public class NetworkCmProxyDataServiceImpl implements NetworkCmProxyDataService private CpsAdminService cpsAdminService; - public static final String NO_NAMESPACE = null; - /** * Constructor Injection for Dependencies. * @param dmiOperations DMI operation @@ -215,7 +215,7 @@ public class NetworkCmProxyDataServiceImpl implements NetworkCmProxyDataService .cmHandleProperties(cmHandlePropertiesMap) .build(); final var dmiRequestBody = prepareOperationBody(dmiRequestBodyObject); - final ResponseEntity<Void> responseEntity = dmiOperations + final ResponseEntity<String> responseEntity = dmiOperations .createResourceDataPassThroughRunningFromDmi(dmiServiceName, cmHandle, resourceIdentifier, @@ -223,13 +223,17 @@ public class NetworkCmProxyDataServiceImpl implements NetworkCmProxyDataService handleResponseForPost(responseEntity); } + @Override + public Collection<ModuleReference> getYangResourcesModuleReferences(final String cmHandle) { + return cpsModuleService.getYangResourcesModuleReferences(NF_PROXY_DATASPACE_NAME, cmHandle); + } + private DataNode fetchDataNodeFromDmiRegistryForCmHandle(final String cmHandle) { final String xpathForDmiRegistryToFetchCmHandle = "/dmi-registry/cm-handles[@id='" + cmHandle + "']"; - final var dataNode = cpsDataService.getDataNode(NCMP_DATASPACE_NAME, + return cpsDataService.getDataNode(NCMP_DATASPACE_NAME, NCMP_DMI_REGISTRY_ANCHOR, xpathForDmiRegistryToFetchCmHandle, FetchDescendantsOption.INCLUDE_ALL_DESCENDANTS); - return dataNode; } private String prepareOperationBody(final GenericRequestBody requestBodyObject) { @@ -242,9 +246,9 @@ public class NetworkCmProxyDataServiceImpl implements NetworkCmProxyDataService } } - private Map<String, String> getCmHandlePropertiesAsMap(final Collection<DataNode> cmHandlePropertiesList) { - if (cmHandlePropertiesList == null || cmHandlePropertiesList.size() == 0) { - return null; + private static Map<String, String> getCmHandlePropertiesAsMap(final Collection<DataNode> cmHandlePropertiesList) { + if (cmHandlePropertiesList == null || cmHandlePropertiesList.isEmpty()) { + return Collections.emptyMap(); } final Map<String, String> cmHandlePropertiesMap = new LinkedHashMap<>(); for (final var node: cmHandlePropertiesList) { @@ -254,7 +258,7 @@ public class NetworkCmProxyDataServiceImpl implements NetworkCmProxyDataService return cmHandlePropertiesMap; } - private Object handleResponse(final @NotNull ResponseEntity<Object> responseEntity) { + private static Object handleResponse(final @NotNull ResponseEntity<Object> responseEntity) { if (responseEntity.getStatusCode() == HttpStatus.OK) { return responseEntity.getBody(); } else { @@ -264,8 +268,8 @@ public class NetworkCmProxyDataServiceImpl implements NetworkCmProxyDataService } } - private void handleResponseForPost(final @NotNull ResponseEntity<Void> responseEntity) { - if (responseEntity.getStatusCode() != HttpStatus.CREATED) { + private static void handleResponseForPost(final @NotNull ResponseEntity<String> responseEntity) { + if (responseEntity.getStatusCode() != HttpStatus.OK) { throw new NcmpException("Not able to create resource data.", "DMI status code: " + responseEntity.getStatusCodeValue() + ", DMI response body: " + responseEntity.getBody()); @@ -275,11 +279,11 @@ public class NetworkCmProxyDataServiceImpl implements NetworkCmProxyDataService private String getGenericRequestBody(final DataNode cmHandleDataNode) { final Collection<DataNode> cmHandlePropertiesList = cmHandleDataNode.getChildDataNodes(); final Map<String, String> cmHandlePropertiesMap = getCmHandlePropertiesAsMap(cmHandlePropertiesList); - final var requetBodyObject = GenericRequestBody.builder() + final var requestBodyObject = GenericRequestBody.builder() .operation(GenericRequestBody.OperationEnum.READ) .cmHandleProperties(cmHandlePropertiesMap) .build(); - return prepareOperationBody(requetBodyObject); + return prepareOperationBody(requestBodyObject); } private void parseAndUpdateCmHandlesInDmiRegistration(final DmiPluginRegistration dmiPluginRegistration) { @@ -295,10 +299,7 @@ public class NetworkCmProxyDataServiceImpl implements NetworkCmProxyDataService cpsDataService.updateNodeLeavesAndExistingDescendantLeaves(NCMP_DATASPACE_NAME, NCMP_DMI_REGISTRY_ANCHOR, "/dmi-registry", cmHandlesJsonData, NO_TIMESTAMP); } catch (final JsonProcessingException e) { - log.error("Parsing error occurred while converting Object to JSON DMI Registry."); - throw new DataValidationException( - "Parsing error occurred while processing DMI Plugin Registration" + dmiPluginRegistration, e - .getMessage(), e); + handleJsonProcessingException(dmiPluginRegistration, e); } } @@ -310,26 +311,40 @@ public class NetworkCmProxyDataServiceImpl implements NetworkCmProxyDataService final PersistenceCmHandle persistenceCmHandle = toPersistenceCmHandle(dmiPluginRegistration.getDmiPlugin(), cmHandle); persistenceCmHandlesList.add(persistenceCmHandle); - createAnchorAndSyncModel(persistenceCmHandle); } final String cmHandleJsonData = objectMapper.writeValueAsString(persistenceCmHandlesList); - cpsDataService.saveListNodeData(NCMP_DATASPACE_NAME, NCMP_DMI_REGISTRY_ANCHOR, "/dmi-registry", - cmHandleJsonData, NO_TIMESTAMP); + + registerAndSyncNode(persistenceCmHandlesList, cmHandleJsonData); } catch (final JsonProcessingException e) { - log.error("Parsing error occurred while converting Object to JSON for DMI Registry."); - throw new DataValidationException( - "Parsing error occurred while processing DMI Plugin Registration" + dmiPluginRegistration, e - .getMessage(), e); + handleJsonProcessingException(dmiPluginRegistration, e); } } - private PersistenceCmHandle toPersistenceCmHandle(final String dmiPluginService, - final CmHandle cmHandle) { + private static void handleJsonProcessingException(final DmiPluginRegistration dmiPluginRegistration, + final JsonProcessingException e) { + final String message = "Parsing error occurred while processing DMI Plugin Registration" + + dmiPluginRegistration; + log.error(message); + throw new DataValidationException(message, e.getMessage(), e); + } + + private void registerAndSyncNode(final PersistenceCmHandlesList persistenceCmHandlesList, + final String cmHandleJsonData) { + cpsDataService.saveListNodeData(NCMP_DATASPACE_NAME, NCMP_DMI_REGISTRY_ANCHOR, "/dmi-registry", + cmHandleJsonData, NO_TIMESTAMP); + + for (final PersistenceCmHandle persistenceCmHandle : persistenceCmHandlesList.getPersistenceCmHandles()) { + createAnchorAndSyncModel(persistenceCmHandle); + } + } + + private static PersistenceCmHandle toPersistenceCmHandle(final String dmiPluginService, + final CmHandle cmHandle) { final PersistenceCmHandle persistenceCmHandle = new PersistenceCmHandle(); persistenceCmHandle.setDmiServiceName(dmiPluginService); persistenceCmHandle.setId(cmHandle.getCmHandleID()); if (cmHandle.getCmHandleProperties() == null) { - persistenceCmHandle.setAdditionalProperties(Collections.EMPTY_MAP); + persistenceCmHandle.setAdditionalProperties(Collections.emptyMap()); } else { persistenceCmHandle.setAdditionalProperties(cmHandle.getCmHandleProperties()); } @@ -350,31 +365,64 @@ public class NetworkCmProxyDataServiceImpl implements NetworkCmProxyDataService protected void createAnchorAndSyncModel(final PersistenceCmHandle cmHandle) { final var modulesForCmHandle = dmiOperations.getResourceFromDmi(cmHandle.getDmiServiceName(), cmHandle.getId(), "modules"); - - final List<ModuleReference> moduleReferencesFromDmiForCmHandle = getModuleReferences(modulesForCmHandle); - - final var knownModuleReferencesInCps = cpsModuleService.getAllYangResourcesModuleReferences(); - + final List<ModuleReference> moduleReferencesFromDmiForCmHandle = + getModuleReferences(modulesForCmHandle); + final var knownModuleReferencesInCps = + cpsModuleService.getYangResourceModuleReferences(NF_PROXY_DATASPACE_NAME); final List<ModuleReference> existingModuleReferences = new ArrayList<>(); + + final List<ModuleReference> unknownModuleReferences = new ArrayList<>(); for (final ModuleReference moduleReferenceFromDmiForCmHandle : moduleReferencesFromDmiForCmHandle) { if (knownModuleReferencesInCps.contains(moduleReferenceFromDmiForCmHandle)) { existingModuleReferences.add(moduleReferenceFromDmiForCmHandle); + } else { + unknownModuleReferences.add(moduleReferenceFromDmiForCmHandle); } } + final JsonObject requestBodyAsJson = getRequestBodyAsJson(unknownModuleReferences); + final Map<String, String> newYangResourcesModuleNameToContentMap = - getNewYangResources(cmHandle); + getNewYangResources(cmHandle, requestBodyAsJson.toString()); - cpsModuleService.createSchemaSetFromModules(NCMP_DATASPACE_NAME, cmHandle.getId(), + cpsModuleService.createSchemaSetFromModules(NF_PROXY_DATASPACE_NAME, cmHandle.getId(), newYangResourcesModuleNameToContentMap, existingModuleReferences); - cpsAdminService.createAnchor(NCMP_DATASPACE_NAME, cmHandle.getId(), cmHandle.getId()); + cpsAdminService.createAnchor(NF_PROXY_DATASPACE_NAME, cmHandle.getId(), cmHandle.getId()); + } + + private static JsonObject getRequestBodyAsJson(final List<ModuleReference> unknownModuleReferences) { + + final JsonObject requestBodyAsJson = new JsonObject(); + requestBodyAsJson.addProperty("operation", "read"); + + final JsonArray moduleReferencesAsJson = getModuleReferencesAsJson(unknownModuleReferences); + + final JsonObject data = new JsonObject(); + data.add("modules", moduleReferencesAsJson); + requestBodyAsJson.add("data", data); + + return requestBodyAsJson; } - private Map<String, String> getNewYangResources(final PersistenceCmHandle cmHandle) { - final var moduleResourcesAsJsonString = dmiOperations.getResourceFromDmi( - cmHandle.getDmiServiceName(), cmHandle.getId(), "moduleResources"); - final JsonArray moduleResources = new Gson().fromJson(moduleResourcesAsJsonString.getBody(), JsonArray.class); + private static JsonArray getModuleReferencesAsJson(final List<ModuleReference> unknownModuleReferences) { + final JsonArray moduleReferences = new JsonArray(); + + for (final ModuleReference moduleReference : unknownModuleReferences) { + final JsonObject moduleReferenceAsJson = new JsonObject(); + moduleReferenceAsJson.addProperty("name", moduleReference.getModuleName()); + moduleReferenceAsJson.addProperty(REVISION, moduleReference.getRevision()); + moduleReferences.add(moduleReferenceAsJson); + } + return moduleReferences; + } + + private Map<String, String> getNewYangResources(final PersistenceCmHandle cmHandle, final String jsonData) { + final var moduleResourcesAsJsonString = dmiOperations.getResourceFromDmiWithJsonData( + cmHandle.getDmiServiceName(), jsonData, cmHandle.getId(), "moduleResources"); + + final JsonArray moduleResources = new Gson().fromJson(moduleResourcesAsJsonString.getBody(), + JsonArray.class); final Map<String, String> newYangResourcesModuleNameToContentMap = new HashMap<>(); for (final JsonElement moduleResource : moduleResources) { @@ -384,29 +432,34 @@ public class NetworkCmProxyDataServiceImpl implements NetworkCmProxyDataService return newYangResourcesModuleNameToContentMap; } - private YangResource toYangResource(final JsonObject yangResourceAsJson) { + private static YangResource toYangResource(final JsonObject yangResourceAsJson) { final YangResource yangResource = new YangResource(); yangResource.setModuleName(yangResourceAsJson.get("moduleName").getAsString()); yangResource.setRevision(yangResourceAsJson.get("revision").getAsString()); - yangResource.setYangSource(yangResourceAsJson.get("yangSource").getAsString()); + final String yangSourceJson = yangResourceAsJson.get("yangSource").getAsString(); + + String yangSource = JsonUtils.removeWrappingTokens(yangSourceJson); + yangSource = JsonUtils.removeRedundantEscapeCharacters(yangSource); + yangResource.setYangSource(yangSource); + return yangResource; } - private List<ModuleReference> getModuleReferences(final ResponseEntity<String> response) { + private static List<ModuleReference> getModuleReferences(final ResponseEntity<String> response) { final List<ModuleReference> modulesFromDmiForCmHandle = new ArrayList<>(); final JsonObject convertedObject = new Gson().fromJson(response.getBody(), JsonObject.class); final JsonArray moduleReferencesAsJson = convertedObject.getAsJsonArray("schemas"); for (final JsonElement moduleReferenceAsJson : moduleReferencesAsJson) { - final ModuleReference moduleReference = toModuleReference((JsonObject) moduleReferenceAsJson); + final ModuleReference moduleReference = + toModuleReference((JsonObject) moduleReferenceAsJson); modulesFromDmiForCmHandle.add(moduleReference); } return modulesFromDmiForCmHandle; } - private ModuleReference toModuleReference(final JsonObject moduleReferenceAsJson) { + private static ModuleReference toModuleReference(final JsonObject moduleReferenceAsJson) { final var moduleReference = new ModuleReference(); - moduleReference.setName(moduleReferenceAsJson.get("moduleName").getAsString()); - moduleReference.setNamespace(NO_NAMESPACE); + moduleReference.setModuleName(moduleReferenceAsJson.get("moduleName").getAsString()); moduleReference.setRevision(moduleReferenceAsJson.get("revision").getAsString()); return moduleReference; } 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 af691f6341..fc70708f92 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 @@ -46,10 +46,18 @@ public class DmiRestClient { return restTemplate.exchange(dmiResourceUrl, HttpMethod.PUT, httpEntity, Object.class); } - public ResponseEntity<Void> postOperationWithJsonData(final String dmiResourceUrl, - final String jsonData, final HttpHeaders headers) { - final var httpEntity = new HttpEntity<>(jsonData, configureHttpHeaders(headers)); - return restTemplate.postForEntity(dmiResourceUrl, httpEntity, Void.class); + /** + * Sends POST operation to DMI with json body containing module references. + * @param dmiResourceUrl dmi resource url + * @param jsonData json data body + * @param httpHeaders http headers + * @return response entity of type String + */ + public ResponseEntity<String> postOperationWithJsonData(final String dmiResourceUrl, + final String jsonData, + final HttpHeaders httpHeaders) { + final var httpEntity = new HttpEntity<>(jsonData, configureHttpHeaders(httpHeaders)); + return restTemplate.postForEntity(dmiResourceUrl, httpEntity, String.class); } private HttpHeaders configureHttpHeaders(final HttpHeaders httpHeaders) { @@ -58,6 +66,12 @@ public class DmiRestClient { return httpHeaders; } + /** + * Sends POST operation to DMI. + * @param dmiResourceUrl dmi resource url + * @param httpHeaders http headers + * @return response entity of type String + */ public ResponseEntity<String> postOperation(final String dmiResourceUrl, final HttpHeaders httpHeaders) { final var httpEntity = new HttpEntity<>(configureHttpHeaders(httpHeaders)); return restTemplate.exchange(dmiResourceUrl, HttpMethod.POST, httpEntity, String.class); diff --git a/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/config/NcmpConfiguration.java b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/config/NcmpConfiguration.java index a834bfcd90..c4e82d3290 100644 --- a/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/config/NcmpConfiguration.java +++ b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/config/NcmpConfiguration.java @@ -41,7 +41,7 @@ public class NcmpConfiguration { } @Bean - public RestTemplate restTemplate(final RestTemplateBuilder restTemplateBuilder) { + public static RestTemplate restTemplate(final RestTemplateBuilder restTemplateBuilder) { return restTemplateBuilder.build(); } -}
\ No newline at end of file +} diff --git a/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/exception/NcmpException.java b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/exception/NcmpException.java index ff53464096..2c75b5d992 100644 --- a/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/exception/NcmpException.java +++ b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/exception/NcmpException.java @@ -43,17 +43,5 @@ public class NcmpException extends RuntimeException { this.details = details; } - /** - * Constructor. - * - * @param message the error message - * @param details the error details - * @param cause the cause of the exception - */ - public NcmpException(final String message, final String details, final Throwable cause) { - super(message, cause); - this.details = details; - } - } diff --git a/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/operation/DmiOperations.java b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/operation/DmiOperations.java index d6feaf3adf..71af3d4cfe 100644 --- a/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/operation/DmiOperations.java +++ b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/operation/DmiOperations.java @@ -76,7 +76,23 @@ public class DmiOperations { final var dmiResourceDataUrl = getDmiResourceUrl(dmiServiceName, cmHandle, resourceName); final var httpHeaders = new HttpHeaders(); return dmiRestClient.postOperation(dmiResourceDataUrl, httpHeaders); + } + /** + * Get resources from DMI for modules. + * + * @param dmiServiceName dmi service name + * @param jsonData module names and revisions as JSON + * @param cmHandle cmHandle + * @param resourceName name of the resource(s) + * @return {@code ResponseEntity} response entity + */ + public ResponseEntity<String> getResourceFromDmiWithJsonData(final String dmiServiceName, + final String jsonData, + final String cmHandle, + final String resourceName) { + final String dmiResourceDataUrl = getDmiResourceUrl(dmiServiceName, cmHandle, resourceName); + return dmiRestClient.postOperationWithJsonData(dmiResourceDataUrl, jsonData, new HttpHeaders()); } /** @@ -141,7 +157,7 @@ public class DmiOperations { * @param jsonBody json body for put operation * @return {@code ResponseEntity} response entity */ - public ResponseEntity<Void> createResourceDataPassThroughRunningFromDmi(final String dmiServiceName, + public ResponseEntity<String> createResourceDataPassThroughRunningFromDmi(final String dmiServiceName, final String cmHandle, final String resourceId, final String jsonBody) { diff --git a/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/models/PersistenceCmHandlesList.java b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/models/PersistenceCmHandlesList.java index beeb00f116..f35abf60b6 100644 --- a/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/models/PersistenceCmHandlesList.java +++ b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/models/PersistenceCmHandlesList.java @@ -23,11 +23,13 @@ package org.onap.cps.ncmp.api.models; import com.fasterxml.jackson.annotation.JsonProperty; import java.util.ArrayList; import java.util.List; +import lombok.Getter; +@Getter public class PersistenceCmHandlesList { @JsonProperty("cm-handles") - private List<PersistenceCmHandle> persistenceCmHandles; + private List<PersistenceCmHandle> persistenceCmHandles = new ArrayList<>(); /** * Add a persistenceCmHandle. @@ -35,10 +37,6 @@ public class PersistenceCmHandlesList { * @param persistenceCmHandle the persistenceCmHandle to add */ public void add(final PersistenceCmHandle persistenceCmHandle) { - if (persistenceCmHandles == null) { - persistenceCmHandles = new ArrayList<>(); - } persistenceCmHandles.add(persistenceCmHandle); } - } diff --git a/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/api/impl/JsonUtilsSpec.groovy b/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/api/impl/JsonUtilsSpec.groovy new file mode 100644 index 0000000000..be27dfad5d --- /dev/null +++ b/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/api/impl/JsonUtilsSpec.groovy @@ -0,0 +1,51 @@ +/* + * ============LICENSE_START======================================================= + * Copyright (C) 2021 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.impl + +import spock.lang.Specification + +class JsonUtilsSpec extends Specification { + def 'Remove redundant escape characters.'() { + expect: 'removing redundant escape characters returns the correct output for #scenario' + JsonUtils.removeRedundantEscapeCharacters(input) == expectedOutput + where: 'the following input is used' + scenario | input || expectedOutput + 'two lines' | 'line1\\nline2' || 'line1\nline2' + 'a string inside quotes' | 'a \\"word in quotes\\"' || 'a "word in quotes"' + 'quotes inside quotes (double escape)' | '\\"quotes \\\\\\"inside\\\\\\" quotes\\"' || '"quotes \\"inside\\" quotes"' // human readable: "quotes \"inside\" quotes" + } + def 'Remove wrapping tokens.'() { + expect: 'removing wrapping tokens returns the correct output for #scenario' + JsonUtils.removeWrappingTokens(input) == expectedOutput + where: 'the following input is used' + scenario | input || expectedOutput + 'a string in quotes' | '"abc"' || 'abc' + 'a string in apostrophes' | "'abc'" || 'abc' + 'a string inside any other tokens' | 'abcde' || 'bcd' + } + + def 'Cannot use constructor.'() { + when: 'attempt to construct object' + new JsonUtils() + then: 'an exception is thrown' + thrown(IllegalStateException) + } +} + 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 b42db57233..8739355c8b 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 @@ -36,6 +36,8 @@ import org.onap.cps.ncmp.api.models.DmiPluginRegistration import org.onap.cps.ncmp.api.models.PersistenceCmHandle import org.onap.cps.ncmp.utils.TestUtils import org.onap.cps.spi.FetchDescendantsOption +import org.onap.cps.spi.exceptions.DataNodeNotFoundException +import org.onap.cps.spi.exceptions.DataValidationException import org.onap.cps.spi.model.DataNode import org.onap.cps.spi.model.ModuleReference import org.springframework.http.HttpStatus @@ -56,16 +58,15 @@ class NetworkCmProxyDataServiceImplSpec extends Specification { def mockCpsModuleService = Mock(CpsModuleService) def mockCpsAdminService = Mock(CpsAdminService) def mockDmiProperties = Mock(NcmpConfiguration.DmiProperties) + def spyObjectMapper = Spy(ObjectMapper) def objectUnderTest = new NetworkCmProxyDataServiceImpl(mockDmiOperations, mockCpsModuleService, - mockCpsDataService, mockCpsQueryService, mockCpsAdminService, new ObjectMapper()) + mockCpsDataService, mockCpsQueryService, mockCpsAdminService, spyObjectMapper) def cmHandle = 'some handle' def noTimestamp = null def cmHandleXPath = "/dmi-registry/cm-handles[@id='testCmHandle']" def cmHandleForModelSync = new PersistenceCmHandle(id:'some cm handle', dmiServiceName: 'some service name') - def expectedDataspaceNameForModleSync = 'NCMP-Admin' - def NO_NAMESPACE = null def expectedDataspaceName = 'NFP-Operational' def 'Query data nodes by cps path with #fetchDescendantsOption.'() { @@ -156,12 +157,12 @@ class NetworkCmProxyDataServiceImplSpec extends Specification { 'ncmp-dmi-registry', "/dmi-registry/cm-handles[@id='cmHandle001']", noTimestamp) where: - scenario | createdCmHandles | updatedCmHandles | removedCmHandles || expectedCallsToSaveNode | expectedCallsToUpdateNode | expectedCallsToDeleteListDataNode - 'create' | [persistenceCmHandle ] | [] | [] || 1 | 0 | 0 - 'update' | [] | [persistenceCmHandle ] | [] || 0 | 1 | 0 - 'delete' | [] | [] | cmHandlesArray || 0 | 0 | 1 - 'create, update and delete' | [persistenceCmHandle ] | [persistenceCmHandle ] | cmHandlesArray || 1 | 1 | 1 - + scenario | createdCmHandles | updatedCmHandles | removedCmHandles || expectedCallsToSaveNode | expectedCallsToUpdateNode | expectedCallsToDeleteListDataNode + 'create' | [persistenceCmHandle] | [] | [] || 1 | 0 | 0 + 'update' | [] | [persistenceCmHandle] | [] || 0 | 1 | 0 + 'delete' | [] | [] | cmHandlesArray || 0 | 0 | 1 + 'create, update and delete' | [persistenceCmHandle] | [persistenceCmHandle] | cmHandlesArray || 1 | 1 | 1 + 'no valid data' | null | null | null || 0 | 0 | 0 } def 'Register a DMI Plugin for the given cmHandle without additional properties.'() { @@ -171,7 +172,7 @@ class NetworkCmProxyDataServiceImplSpec extends Specification { dmiPluginRegistration.dmiPlugin = 'my-server' persistenceCmHandle.cmHandleID = '123' persistenceCmHandle.cmHandleProperties = null - dmiPluginRegistration.createdCmHandles = [persistenceCmHandle ] + dmiPluginRegistration.createdCmHandles = [persistenceCmHandle] def expectedJsonData = '{"cm-handles":[{"id":"123","dmi-service-name":"my-server","additional-properties":[]}]}' when: 'registration is updated' objectUnderTest.updateDmiRegistrationAndSyncModule(dmiPluginRegistration) @@ -180,28 +181,58 @@ class NetworkCmProxyDataServiceImplSpec extends Specification { '/dmi-registry', expectedJsonData, noTimestamp) } + def 'Register a DMI Plugin with JSON processing errors during #scenario.'() { + given: 'a registration without cmHandle properties ' + NetworkCmProxyDataServiceImpl objectUnderTest = getObjectUnderTestWithModelSyncDisabled() + def dmiPluginRegistration = new DmiPluginRegistration() + dmiPluginRegistration.createdCmHandles = createdCmHandles + dmiPluginRegistration.updatedCmHandles = updatedCmHandles + and: 'an JSON processing exception occurs' + spyObjectMapper.writeValueAsString(_) >> { throw (new JsonProcessingException('')) } + when: 'registration is updated' + objectUnderTest.updateDmiRegistrationAndSyncModule(dmiPluginRegistration) + then: 'a data validation exception is thrown' + thrown(DataValidationException) + where: + scenario | createdCmHandles | updatedCmHandles + 'create' | [persistenceCmHandle] | [] + 'update' | [] | [persistenceCmHandle] + } + + def 'Register a DMI Plugin with no data found during delete.'() { + given: 'a registration without cmHandle properties ' + NetworkCmProxyDataServiceImpl objectUnderTest = getObjectUnderTestWithModelSyncDisabled() + def dmiPluginRegistration = new DmiPluginRegistration() + dmiPluginRegistration.removedCmHandles = ['some cm handle'] + and: 'an JSON processing exception occurs' + mockCpsDataService.deleteListNodeData(*_) >> { throw (new DataNodeNotFoundException('','')) } + when: 'registration is updated' + objectUnderTest.updateDmiRegistrationAndSyncModule(dmiPluginRegistration) + then: 'no exception is thrown' + noExceptionThrown() + } + def 'Get resource data for pass-through operational from dmi.'() { given: 'data node representing cmHandle and its properties' def cmHandleDataNode = getCmHandleDataNodeForTest() + and: 'data node is got from data service' + mockCpsDataService.getDataNode('NCMP-Admin', 'ncmp-dmi-registry', + cmHandleXPath, FetchDescendantsOption.INCLUDE_ALL_DESCENDANTS) >> cmHandleDataNode + and: 'resource data is got from DMI' + mockDmiOperations.getResourceDataOperationalFromDmi('testDmiService', + 'testCmHandle', + 'testResourceId', + 'testFieldQuery', + 5, + 'testAcceptParam', + '{"operation":"read","cmHandleProperties":{"testName":"testValue"}}') >> new ResponseEntity<>('result-json', HttpStatus.OK) when: 'get resource data is called' def response = objectUnderTest.getResourceDataOperationalForCmHandle('testCmHandle', 'testResourceId', 'testAcceptParam', 'testFieldQuery', 5) - then: 'cps data service is being called once to get data node' - 1 * mockCpsDataService.getDataNode('NCMP-Admin', 'ncmp-dmi-registry', - cmHandleXPath, FetchDescendantsOption.INCLUDE_ALL_DESCENDANTS) >> cmHandleDataNode - and: 'dmi operation is being called to get resource data' - 1 * mockDmiOperations.getResourceDataOperationalFromDmi('testDmiService', - 'testCmHandle', - 'testResourceId', - 'testFieldQuery', - 5, - 'testAcceptParam', - '{"operation":"read","cmHandleProperties":{"testName":"testValue"}}') >> - new ResponseEntity<>('result-json', HttpStatus.OK) - and: 'dmi returns ok response' + then: 'dmi returns ok response' response == 'result-json' } @@ -214,15 +245,16 @@ class NetworkCmProxyDataServiceImplSpec extends Specification { and: 'objectMapper not able to parse object' def mockObjectMapper = Mock(ObjectMapper) objectUnderTest.objectMapper = mockObjectMapper - mockObjectMapper.writeValueAsString(_) >> { throw new JsonProcessingException("testException") } + mockObjectMapper.writeValueAsString(_) >> { throw new JsonProcessingException('testException') } when: 'get resource data is called' def response = objectUnderTest.getResourceDataOperationalForCmHandle('testCmHandle', 'testResourceId', 'testAcceptParam', 'testFieldQuery', 5) - then: 'exception is thrown' - thrown(NcmpException.class) + then: 'exception is thrown with the expected details' + def exceptionThrown = thrown(NcmpException.class) + exceptionThrown.details == 'testException' } def 'Get resource data for pass-through operational from dmi return NOK response.'() { @@ -247,7 +279,9 @@ class NetworkCmProxyDataServiceImplSpec extends Specification { 'testFieldQuery', 5) then: 'exception is thrown' - thrown(NcmpException.class) + def exceptionThrown = thrown(NcmpException.class) + and: 'details contains the original response' + exceptionThrown.details.contains('NOK-json') } def 'Get resource data for pass-through running from dmi.'() { @@ -283,15 +317,16 @@ class NetworkCmProxyDataServiceImplSpec extends Specification { and: 'objectMapper not able to parse object' def mockObjectMapper = Mock(ObjectMapper) objectUnderTest.objectMapper = mockObjectMapper - mockObjectMapper.writeValueAsString(_) >> { throw new JsonProcessingException("testException") } + mockObjectMapper.writeValueAsString(_) >> { throw new JsonProcessingException('testException') } when: 'get resource data is called' def response = objectUnderTest.getResourceDataPassThroughRunningForCmHandle('testCmHandle', 'testResourceId', 'testAcceptParam', 'testFieldQuery', 5) - then: 'exception is thrown' - thrown(NcmpException.class) + then: 'exception is thrown with the expected details' + def exceptionThrown = thrown(NcmpException.class) + exceptionThrown.details == 'testException' } def 'Get resource data for pass-through running from dmi return NOK response.'() { @@ -316,13 +351,15 @@ class NetworkCmProxyDataServiceImplSpec extends Specification { 'testFieldQuery', 5) then: 'exception is thrown' - thrown(NcmpException.class) + def exceptionThrown = thrown(NcmpException.class) + and: 'details contains the original response' + exceptionThrown.details.contains('NOK-json') } def 'Write resource data for pass-through running from dmi using POST.'() { given: 'data node representing cmHandle and its properties' def cmHandleDataNode = getCmHandleDataNodeForTest() - and: 'cpsDataService returns valid dataNode' + and: 'cpsDataService returns valid cm-handle datanode' mockCpsDataService.getDataNode('NCMP-Admin', 'ncmp-dmi-registry', cmHandleXPath, FetchDescendantsOption.INCLUDE_ALL_DESCENDANTS) >> cmHandleDataNode when: 'get resource data is called' @@ -334,7 +371,7 @@ class NetworkCmProxyDataServiceImplSpec extends Specification { 'testCmHandle', 'testResourceId', '{"operation":"create","dataType":"application/json","data":"{some-json}","cmHandleProperties":{"testName":"testValue"}}') - >> { new ResponseEntity<>(HttpStatus.CREATED) } + >> { new ResponseEntity<>(HttpStatus.OK) } } def 'Write resource data for pass-through running from dmi using POST "not found" response (from DMI).'() { @@ -344,36 +381,45 @@ class NetworkCmProxyDataServiceImplSpec extends Specification { mockCpsDataService.getDataNode('NCMP-Admin', 'ncmp-dmi-registry', cmHandleXPath, FetchDescendantsOption.INCLUDE_ALL_DESCENDANTS) >> cmHandleDataNode and: 'dmi throws exception' - 1 * mockDmiOperations.createResourceDataPassThroughRunningFromDmi(_ as String, _ as String, _ as String, _ as String) + mockDmiOperations.createResourceDataPassThroughRunningFromDmi(_ as String, _ as String, _ as String, _ as String) >> { new ResponseEntity<>(HttpStatus.NOT_FOUND) } when: 'get resource data is called' objectUnderTest.createResourceDataPassThroughRunningForCmHandle('testCmHandle', 'testResourceId', '{some-json}', 'application/json') then: 'exception is thrown' - thrown(NcmpException.class) + def exceptionThrown = thrown(NcmpException.class) + and: 'details contains (not found) error code: 404' + exceptionThrown.details.contains('404') } def 'Sync model for a (new) cm handle with #scenario'() { given: 'DMI PLug-in returns a list of module references' getModulesForCmHandle() - def knownModule1 = new ModuleReference('module1', NO_NAMESPACE, '1') - def knownOtherModule = new ModuleReference('some other module', NO_NAMESPACE, 'some revision') + def knownModule1 = new ModuleReference('module1', '1') + def knownOtherModule = new ModuleReference('some other module', 'some revision') and: 'CPS-Core returns list of known modules' - mockCpsModuleService.getAllYangResourcesModuleReferences() >> [knownModule1, knownOtherModule] + mockCpsModuleService.getYangResourceModuleReferences(_) >> [knownModule1, knownOtherModule] and: 'DMI-Plugin returns resource(s) for "new" module(s)' def moduleResources = new ResponseEntity<String>(sdncReponseBody, HttpStatus.OK) - mockDmiOperations.getResourceFromDmi(_, cmHandleForModelSync.getId(), 'moduleResources') >> moduleResources + mockDmiOperations.getResourceFromDmiWithJsonData(_, _, _, 'moduleResources') >> moduleResources when: 'module Sync is triggered' objectUnderTest.createAnchorAndSyncModel(cmHandleForModelSync) then: 'the CPS module service is called once with the correct parameters' - 1 * mockCpsModuleService.createSchemaSetFromModules(expectedDataspaceNameForModleSync, cmHandleForModelSync.getId(), expectedYangResourceToContentMap , [knownModule1]) + 1 * mockCpsModuleService.createSchemaSetFromModules(expectedDataspaceName, cmHandleForModelSync.getId(), expectedYangResourceToContentMap, [knownModule1]) and: 'admin service create anchor method has been called with correct parameters' - 1 * mockCpsAdminService.createAnchor(expectedDataspaceNameForModleSync, cmHandleForModelSync.getId(), cmHandleForModelSync.getId()) + 1 * mockCpsAdminService.createAnchor(expectedDataspaceName, cmHandleForModelSync.getId(), cmHandleForModelSync.getId()) where: 'the following responses are recieved from SDNC' - scenario | sdncReponseBody || expectedYangResourceToContentMap - 'one unknown module' | '[{"moduleName" : "someModule", "revision" : "1","yangSource": "someResource"}]' || [someModule: 'someResource'] - 'no unknown module' | '[]' || [:] + scenario | sdncReponseBody || expectedYangResourceToContentMap + 'one unknown module' | '[{"moduleName" : "someModule", "revision" : "1","yangSource": "[some yang source]"}]' || [someModule: 'some yang source'] + 'no unknown module' | '[]' || [:] + } + + def 'Getting Yang Resources.'() { + when: 'yang resources is called' + objectUnderTest.getYangResourcesModuleReferences('some cm handle') + then: 'CPS module services is invoked for the correct dataspace and cm handle' + 1 * mockCpsModuleService.getYangResourcesModuleReferences('NFP-Operational','some cm handle') } def getModulesForCmHandle() { @@ -387,7 +433,7 @@ class NetworkCmProxyDataServiceImplSpec extends Specification { def getObjectUnderTestWithModelSyncDisabled() { def objectUnderTest = Spy(new NetworkCmProxyDataServiceImpl(mockDmiOperations, mockCpsModuleService, - mockCpsDataService, mockCpsQueryService, mockCpsAdminService, new ObjectMapper())) + mockCpsDataService, mockCpsQueryService, mockCpsAdminService, spyObjectMapper)) objectUnderTest.createAnchorAndSyncModel(_) >> null return objectUnderTest } diff --git a/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/api/impl/client/DmiRestClientSpec.groovy b/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/api/impl/client/DmiRestClientSpec.groovy index 879c73c27e..bf6179ba1b 100644 --- a/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/api/impl/client/DmiRestClientSpec.groovy +++ b/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/api/impl/client/DmiRestClientSpec.groovy @@ -21,24 +21,30 @@ package org.onap.cps.ncmp.api.impl.client import org.onap.cps.ncmp.api.impl.config.NcmpConfiguration +import org.spockframework.spring.SpringBean +import org.springframework.beans.factory.annotation.Autowired +import org.springframework.boot.test.context.SpringBootTest import org.springframework.http.HttpEntity import org.springframework.http.HttpHeaders +import org.springframework.http.HttpMethod import org.springframework.http.ResponseEntity +import org.springframework.test.context.ContextConfiguration import org.springframework.web.client.RestTemplate import spock.lang.Specification -import org.springframework.http.HttpMethod +@SpringBootTest +@ContextConfiguration(classes = [NcmpConfiguration.DmiProperties, DmiRestClient]) class DmiRestClientSpec extends Specification { - def mockDmiProperties = Mock(NcmpConfiguration.DmiProperties) - def mockRestTemplate = Mock(RestTemplate) - def objectUnderTest = new DmiRestClient(mockRestTemplate, mockDmiProperties) + @SpringBean + RestTemplate mockRestTemplate = Mock(RestTemplate) + + @Autowired + DmiRestClient objectUnderTest def 'DMI PUT operation.'() { given: 'a PUT url' def getResourceDataUrl = 'http://some-uri/getResourceDataUrl' - and: 'dmi properties' - setupTestConfigurationData() and: 'the rest template returns a valid response entity' def mockResponseEntity = Mock(ResponseEntity) mockRestTemplate.exchange(getResourceDataUrl, HttpMethod.PUT, _ as HttpEntity, Object.class) >> mockResponseEntity @@ -51,19 +57,13 @@ class DmiRestClientSpec extends Specification { def 'DMI POST operation.'() { given: 'a POST url' def getResourceDataUrl = 'http://some-uri/createResourceDataUrl' - and: 'dmi properties' - setupTestConfigurationData() and: 'the rest template returns a valid response entity' def mockResponseEntity = Mock(ResponseEntity) - mockRestTemplate.postForEntity(getResourceDataUrl, _ as HttpEntity, Void.class) >> mockResponseEntity + mockRestTemplate.postForEntity(getResourceDataUrl, _ as HttpEntity, String.class) >> mockResponseEntity when: 'POST operation is invoked' def result = objectUnderTest.postOperationWithJsonData(getResourceDataUrl, 'json-data', new HttpHeaders()) then: 'the output of the method is equal to the output from the test template' result == mockResponseEntity } - def setupTestConfigurationData() { - mockDmiProperties.authUsername >> 'some-username' - mockDmiProperties.authPassword >> 'some-password' - } -}
\ No newline at end of file +} diff --git a/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/api/impl/config/NcmpConfigurationSpec.groovy b/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/api/impl/config/NcmpConfigurationSpec.groovy new file mode 100644 index 0000000000..dd4c1375b0 --- /dev/null +++ b/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/api/impl/config/NcmpConfigurationSpec.groovy @@ -0,0 +1,52 @@ +/* + * ============LICENSE_START======================================================= + * Copyright (C) 2021 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.impl.config + +import org.springframework.beans.factory.annotation.Autowired +import org.springframework.boot.test.context.SpringBootTest +import org.springframework.boot.web.client.RestTemplateBuilder +import org.springframework.test.context.ContextConfiguration +import org.springframework.web.client.RestTemplate +import spock.lang.Specification + +@SpringBootTest +@ContextConfiguration(classes = [NcmpConfiguration.DmiProperties]) +class NcmpConfigurationSpec extends Specification{ + + @Autowired + NcmpConfiguration.DmiProperties dmiProperties + + def 'DMI Properties.'() { + expect: 'properties are set to values in test configuration yaml file' + dmiProperties.authUsername == 'some-user' + dmiProperties.authPassword == 'some-password' + } + + def 'Rest Template creation.'() { + given: 'a rest template builder' + def mockRestTemplateBuilder = Mock(RestTemplateBuilder) + def expectedRestTemplate = Mock(RestTemplate) + mockRestTemplateBuilder.build() >> expectedRestTemplate + when: 'a rest template is created' + def result = NcmpConfiguration.restTemplate(mockRestTemplateBuilder) + then: 'the rest template from the builder is returned' + assert result == expectedRestTemplate + } +} diff --git a/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/api/impl/operation/DmiOperationsSpec.groovy b/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/api/impl/operation/DmiOperationsSpec.groovy index 987ab2bca1..6a1ce1a18b 100644 --- a/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/api/impl/operation/DmiOperationsSpec.groovy +++ b/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/api/impl/operation/DmiOperationsSpec.groovy @@ -92,4 +92,18 @@ class DmiOperationsSpec extends Specification { then: 'the post operation is executed with the correct URL' 1 * mockDmiRestClient.postOperation(expectedUrl, _ as HttpHeaders) } + + def 'Call get resource from dmi with json data.'() { + given: 'expected url & json data' + def requestBody = 'some json' + def expectedUrl = 'testDmiBasePath/dmi/v1/ch/testCmHandle/modules' + def expectedHttpHeaders = new HttpHeaders() + when: 'get resource data is called to dmi' + objectUnderTest.getResourceFromDmiWithJsonData('testDmiBasePath', + requestBody, + 'testCmHandle', + 'modules') + then: 'the post operation is executed with the correct URL and json data' + 1 * mockDmiRestClient.postOperationWithJsonData(expectedUrl, requestBody, expectedHttpHeaders) + } }
\ No newline at end of file diff --git a/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/api/models/PersistenceCmHandleSpec.groovy b/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/api/models/PersistenceCmHandleSpec.groovy new file mode 100644 index 0000000000..bfed795f76 --- /dev/null +++ b/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/api/models/PersistenceCmHandleSpec.groovy @@ -0,0 +1,42 @@ +/* + * ============LICENSE_START======================================================= + * Copyright (C) 2021 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.models + +import spock.lang.Specification + +class PersistenceCmHandleSpec extends Specification { + + def objectUnderTest = new PersistenceCmHandle() + + def 'Setting and getting additional properties.'() { + given: 'a map of one property is added' + objectUnderTest.setAdditionalProperties([myProperty: 'some value']) + when: 'the additional properties are retrieved' + def result = objectUnderTest.getAdditionalProperties() + then: 'the result has the right size' + assert result.size() == 1 + and: 'the property in the result has the correct name and value' + def actualAdditionalProperty = result.get(0) + def expectedAdditionalProperty = new PersistenceCmHandle.AdditionalProperty('myProperty','some value') + assert actualAdditionalProperty.name == expectedAdditionalProperty.name + assert actualAdditionalProperty.value == expectedAdditionalProperty.value + } + +} diff --git a/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/api/models/moduleReferenceSpec.groovy b/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/api/models/moduleReferenceSpec.groovy index d6cb4635d8..444a25804b 100644 --- a/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/api/models/moduleReferenceSpec.groovy +++ b/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/api/models/moduleReferenceSpec.groovy @@ -1,14 +1,33 @@ +/* + * ============LICENSE_START======================================================= + * Copyright (C) 2021 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.models -import org.onap.cps.spi.model.ModuleReference +import org.onap.cps.spi.model.ExtendedModuleReference import spock.lang.Specification class moduleReferenceSpec extends Specification { def 'lombok data annotation correctly implements toString() and hashCode() methods'() { given: 'two moduleReference objects' - def moduleReference1 = new ModuleReference('module1', "some namespace", '1') - def moduleReference2 = new ModuleReference('module1', "some namespace", '1') + def moduleReference1 = new ExtendedModuleReference('module1', "some namespace", '1') + def moduleReference2 = new ExtendedModuleReference('module1', "some namespace", '1') when: 'lombok generated methods are called' then: 'the methods exist and behaviour is accurate' assert moduleReference1.toString() == moduleReference2.toString() diff --git a/cps-ncmp-service/src/test/resources/application.yml b/cps-ncmp-service/src/test/resources/application.yml new file mode 100644 index 0000000000..71ac2c9a03 --- /dev/null +++ b/cps-ncmp-service/src/test/resources/application.yml @@ -0,0 +1,23 @@ +# ============LICENSE_START======================================================= +# Copyright (C) 2021 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========================================================= + +dmi: + auth: + username: some-user + password: some-password + diff --git a/cps-parent/pom.xml b/cps-parent/pom.xml index e8861006e0..3de50c3c14 100755 --- a/cps-parent/pom.xml +++ b/cps-parent/pom.xml @@ -32,7 +32,7 @@ <groupId>org.onap.cps</groupId> <artifactId>cps-parent</artifactId> - <version>1.1.0-SNAPSHOT</version> + <version>2.0.1-SNAPSHOT</version> <packaging>pom</packaging> <properties> diff --git a/cps-path-parser/pom.xml b/cps-path-parser/pom.xml index 38ba9acf31..cb454c2ac1 100644 --- a/cps-path-parser/pom.xml +++ b/cps-path-parser/pom.xml @@ -23,7 +23,7 @@ <parent> <groupId>org.onap.cps</groupId> <artifactId>cps-parent</artifactId> - <version>1.1.0-SNAPSHOT</version> + <version>2.0.1-SNAPSHOT</version> <relativePath>../cps-parent/pom.xml</relativePath> </parent> diff --git a/cps-rest/pom.xml b/cps-rest/pom.xml index c4af3803b5..a3e5fd9481 100755 --- a/cps-rest/pom.xml +++ b/cps-rest/pom.xml @@ -27,7 +27,7 @@ <parent> <groupId>org.onap.cps</groupId> <artifactId>cps-parent</artifactId> - <version>1.1.0-SNAPSHOT</version> + <version>2.0.1-SNAPSHOT</version> <relativePath>../cps-parent/pom.xml</relativePath> </parent> diff --git a/cps-ri/pom.xml b/cps-ri/pom.xml index 273cb2e72f..00911f83f6 100644 --- a/cps-ri/pom.xml +++ b/cps-ri/pom.xml @@ -26,7 +26,7 @@ <parent>
<groupId>org.onap.cps</groupId>
<artifactId>cps-parent</artifactId>
- <version>1.1.0-SNAPSHOT</version>
+ <version>2.0.1-SNAPSHOT</version>
<relativePath>../cps-parent/pom.xml</relativePath>
</parent>
@@ -96,6 +96,10 @@ <groupId>org.apache.commons</groupId>
<artifactId>commons-lang3</artifactId>
</dependency>
+ <dependency>
+ <groupId>com.fasterxml.jackson.core</groupId>
+ <artifactId>jackson-databind</artifactId>
+ </dependency>
<!-- T E S T D E P E N D E N C I E S -->
<dependency>
<groupId>org.codehaus.groovy</groupId>
@@ -140,6 +144,7 @@ </dependency>
</dependencies>
+
<build>
<plugins>
<plugin>
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 af010f4fcd..c57723d322 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 @@ -24,12 +24,15 @@ package org.onap.cps.spi.impl; import static org.onap.cps.spi.FetchDescendantsOption.INCLUDE_ALL_DESCENDANTS; +import com.fasterxml.jackson.core.JsonProcessingException; +import com.fasterxml.jackson.databind.ObjectMapper; import com.google.common.collect.ImmutableSet; import com.google.common.collect.ImmutableSet.Builder; import com.google.gson.Gson; import com.google.gson.GsonBuilder; import java.util.Collection; import java.util.Collections; +import java.util.HashMap; import java.util.HashSet; import java.util.List; import java.util.Map; @@ -50,6 +53,7 @@ import org.onap.cps.spi.exceptions.AlreadyDefinedException; import org.onap.cps.spi.exceptions.ConcurrencyException; import org.onap.cps.spi.exceptions.CpsPathException; import org.onap.cps.spi.exceptions.DataNodeNotFoundException; +import org.onap.cps.spi.exceptions.DataValidationException; import org.onap.cps.spi.model.DataNode; import org.onap.cps.spi.model.DataNodeBuilder; import org.onap.cps.spi.repository.AnchorRepository; @@ -68,6 +72,8 @@ public class CpsDataPersistenceServiceImpl implements CpsDataPersistenceService private FragmentRepository fragmentRepository; + private final ObjectMapper objectMapper; + /** * Constructor. * @@ -80,11 +86,12 @@ public class CpsDataPersistenceServiceImpl implements CpsDataPersistenceService this.dataspaceRepository = dataspaceRepository; this.anchorRepository = anchorRepository; this.fragmentRepository = fragmentRepository; + this.objectMapper = new ObjectMapper(); } private static final Gson GSON = new GsonBuilder().create(); private static final String REG_EX_FOR_OPTIONAL_LIST_INDEX = "(\\[@[\\s\\S]+?]){0,1})"; - private static final String REG_EX_FOR_LIST_NODE_KEY = "\\[(\\@([^/]*?))+( and)*\\]$"; + private static final String REG_EX_FOR_LIST_NODE_KEY = "\\[(\\@([^/]*?)){0,99}( and)*\\]$"; @Override public void addChildDataNode(final String dataspaceName, final String anchorName, final String parentXpath, @@ -236,17 +243,27 @@ public class CpsDataPersistenceServiceImpl implements CpsDataPersistenceService return ancestorXpath; } - private static DataNode toDataNode(final FragmentEntity fragmentEntity, + private DataNode toDataNode(final FragmentEntity fragmentEntity, final FetchDescendantsOption fetchDescendantsOption) { - final Map<String, Object> leaves = GSON.fromJson(fragmentEntity.getAttributes(), Map.class); final List<DataNode> childDataNodes = getChildDataNodes(fragmentEntity, fetchDescendantsOption); + Map<String, Object> leaves = new HashMap<>(); + if (fragmentEntity.getAttributes() != null) { + try { + leaves = objectMapper.readValue(fragmentEntity.getAttributes(), Map.class); + } catch (final JsonProcessingException jsonProcessingException) { + final String message = "Parsing error occurred while processing fragmentEntity attributes."; + log.error(message); + throw new DataValidationException(message, + jsonProcessingException.getMessage(), jsonProcessingException); + } + } return new DataNodeBuilder() .withXpath(fragmentEntity.getXpath()) .withLeaves(leaves) .withChildDataNodes(childDataNodes).build(); } - private static List<DataNode> getChildDataNodes(final FragmentEntity fragmentEntity, + private List<DataNode> getChildDataNodes(final FragmentEntity fragmentEntity, final FetchDescendantsOption fetchDescendantsOption) { if (fetchDescendantsOption == INCLUDE_ALL_DESCENDANTS) { return fragmentEntity.getChildFragments().stream() @@ -327,10 +344,10 @@ public class CpsDataPersistenceServiceImpl implements CpsDataPersistenceService final Matcher descendantNodeHasListNodeKey = Pattern.compile(REG_EX_FOR_LIST_NODE_KEY).matcher(descendantNode); final boolean xpathPointsToAValidChildNodeWithKey = parentEntity.getChildFragments().stream().anyMatch( - (fragment) -> fragment.getXpath().equals(listNodeXpath)); + fragment -> fragment.getXpath().equals(listNodeXpath)); final boolean xpathPointsToAValidChildNodeWithoutKey = parentEntity.getChildFragments().stream().anyMatch( - (fragment) -> fragment.getXpath().replaceAll(REG_EX_FOR_LIST_NODE_KEY, "").equals(listNodeXpath)); + fragment -> fragment.getXpath().replaceAll(REG_EX_FOR_LIST_NODE_KEY, "").equals(listNodeXpath)); if ((descendantNodeHasListNodeKey.find() && xpathPointsToAValidChildNodeWithKey) || 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 1c7828f32e..e0f54265ab 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 @@ -116,11 +116,21 @@ public class CpsModulePersistenceServiceImpl implements CpsModulePersistenceServ } @Override - public List<ModuleReference> getAllYangResourcesModuleReferences() { - final List<YangResourceModuleReference> yangResourceModuleReferenceList = - yangResourceRepository.findAllModuleNameAndRevision(); + public Collection<ModuleReference> getYangResourceModuleReferences(final String dataspaceName) { + final Set<YangResourceModuleReference> yangResourceModuleReferenceList = + yangResourceRepository.findAllModuleReferences(dataspaceName); return yangResourceModuleReferenceList.stream().map(CpsModulePersistenceServiceImpl::toModuleReference) - .collect(Collectors.toList()); + .collect(Collectors.toList()); + } + + @Override + public Collection<ModuleReference> getYangResourceModuleReferences(final String dataspaceName, + final String anchorName) { + final Set<YangResourceModuleReference> yangResourceModuleReferenceList = + yangResourceRepository + .findAllModuleReferences(dataspaceName, anchorName); + return yangResourceModuleReferenceList.stream().map(CpsModulePersistenceServiceImpl::toModuleReference) + .collect(Collectors.toList()); } @Override @@ -148,15 +158,15 @@ public class CpsModulePersistenceServiceImpl implements CpsModulePersistenceServ @Transactional public void storeSchemaSetFromModules(final String dataspaceName, final String schemaSetName, final Map<String, String> newYangResourcesModuleNameToContentMap, - final List<ModuleReference> moduleReferenceList) { + final List<ModuleReference> moduleReferences) { storeSchemaSet(dataspaceName, schemaSetName, newYangResourcesModuleNameToContentMap); final var dataspaceEntity = dataspaceRepository.getByName(dataspaceName); final var schemaSetEntity = schemaSetRepository.getByDataspaceAndName(dataspaceEntity, schemaSetName); final List<Long> listOfYangResourceIds = new ArrayList<>(); - moduleReferenceList.forEach(moduleReference -> + moduleReferences.forEach(moduleReference -> listOfYangResourceIds.add(yangResourceRepository.getIdByModuleNameAndRevision( - moduleReference.getName(), moduleReference.getRevision()))); + moduleReference.getModuleName(), moduleReference.getRevision()))); yangResourceRepository.insertSchemaSetIdYangResourceId(schemaSetEntity.getId(), listOfYangResourceIds); } @@ -325,10 +335,11 @@ public class CpsModulePersistenceServiceImpl implements CpsModulePersistenceServ return checksum; } - private static ModuleReference toModuleReference(final YangResourceModuleReference yangResourceModuleReference) { + private static ModuleReference toModuleReference( + final YangResourceModuleReference yangResourceModuleReference) { return ModuleReference.builder() - .name(yangResourceModuleReference.getModuleName()) - .revision(yangResourceModuleReference.getRevision()) - .build(); + .moduleName(yangResourceModuleReference.getModuleName()) + .revision(yangResourceModuleReference.getRevision()) + .build(); } } diff --git a/cps-ri/src/main/java/org/onap/cps/spi/repository/YangResourceRepository.java b/cps-ri/src/main/java/org/onap/cps/spi/repository/YangResourceRepository.java index 0b48eaaf63..b16b284a2a 100644 --- a/cps-ri/src/main/java/org/onap/cps/spi/repository/YangResourceRepository.java +++ b/cps-ri/src/main/java/org/onap/cps/spi/repository/YangResourceRepository.java @@ -37,8 +37,32 @@ public interface YangResourceRepository extends JpaRepository<YangResourceEntity List<YangResourceEntity> findAllByChecksumIn(@NotNull Set<String> checksum); - @Query(value = "SELECT module_name, revision FROM yang_resource", nativeQuery = true) - List<YangResourceModuleReference> findAllModuleNameAndRevision(); + @Query(value = "SELECT DISTINCT\n" + + "yr.module_name AS module_name,\n" + + "yr.revision AS revision\n" + + "FROM\n" + + "dataspace d\n" + + "JOIN schema_set ss ON ss.dataspace_id = d.id\n" + + "JOIN schema_set_yang_resources ssyr ON ssyr.schema_set_id = ss.id\n" + + "JOIN yang_resource yr ON yr.id = ssyr.yang_resource_id\n" + + "WHERE\n" + + "d.name = :dataspaceName", nativeQuery = true) + Set<YangResourceModuleReference> findAllModuleReferences(@Param("dataspaceName") String dataspaceName); + + @Query(value = "SELECT DISTINCT\n" + + "yr.module_Name AS module_name,\n" + + "yr.revision AS revision\n" + + "FROM\n" + + "dataspace d\n" + + "JOIN anchor a ON a.dataspace_id = d.id\n" + + "JOIN schema_set ss ON ss.dataspace_id = a.dataspace_id\n" + + "JOIN schema_set_yang_resources ssyr ON ssyr.schema_set_id = ss.id\n" + + "JOIN yang_resource yr ON yr.id = ssyr.yang_resource_id\n" + + "WHERE\n" + + "d.name = :dataspaceName AND\n" + + "a.name =:anchorName", nativeQuery = true) + Set<YangResourceModuleReference> findAllModuleReferences( + @Param("dataspaceName") String dataspaceName, @Param("anchorName") String anchorName); @Query(value = "SELECT id FROM yang_resource WHERE module_name=:name and revision=:revision", nativeQuery = true) Long getIdByModuleNameAndRevision(@Param("name") String moduleName, @Param("revision") String revision); 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 8217a4fb0d..e2316e8636 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 @@ -21,11 +21,6 @@ */ package org.onap.cps.spi.impl -import org.onap.cps.spi.exceptions.DataValidationException - -import static org.onap.cps.spi.FetchDescendantsOption.INCLUDE_ALL_DESCENDANTS -import static org.onap.cps.spi.FetchDescendantsOption.OMIT_DESCENDANTS - import com.google.common.collect.ImmutableSet import com.google.gson.Gson import com.google.gson.GsonBuilder @@ -42,6 +37,9 @@ import org.springframework.test.context.jdbc.Sql import javax.validation.ConstraintViolationException +import static org.onap.cps.spi.FetchDescendantsOption.INCLUDE_ALL_DESCENDANTS +import static org.onap.cps.spi.FetchDescendantsOption.OMIT_DESCENDANTS + class CpsDataPersistenceServiceIntegrationSpec extends CpsPersistenceSpecBase { @Autowired @@ -155,17 +153,25 @@ class CpsDataPersistenceServiceIntegrationSpec extends CpsPersistenceSpecBase { } @Sql([CLEAR_DATA, SET_DATA]) - def 'Add list-node fragment with multiple elements.'() { - given: 'list node data fragment as a collection of data nodes' + def 'Add list-node fragment with multiple elements including an element with a child datanode.'() { + given: 'two new data nodes for an existing list' def listNodeXpaths = ['/parent-201/child-204[@key="B"]', '/parent-201/child-204[@key="C"]'] def listNodeCollection = buildDataNodeCollection(listNodeXpaths) - when: 'list-node elements added to existing parent node' + and: 'a child node for one of the new data nodes' + def childDataNode = buildDataNode('/parent-201/child-204[@key="C"]/grand-child-204[@key2="Z"]', [leave:'value'], []) + listNodeCollection.iterator().next().childDataNodes = [childDataNode] + when: 'the data nodes (list elements) are added to existing parent node' objectUnderTest.addListDataNodes(DATASPACE_NAME, ANCHOR_NAME3, '/parent-201', listNodeCollection) then: 'new entries 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.getChildFragments().collect { it.getXpath() } assert allChildXpaths.size() == 5 assert allChildXpaths.containsAll(listNodeXpaths) + and: 'the child node of the new list entry is also present' + def dataspaceEntity = dataspaceRepository.getByName(DATASPACE_NAME) + def anchorEntity = anchorRepository.getByDataspaceAndName(dataspaceEntity, ANCHOR_NAME3) + def listElementChild = fragmentRepository.findByDataspaceAndAnchorAndXpath(dataspaceEntity, anchorEntity, childDataNode.xpath) + assert listElementChild.isPresent() } @Sql([CLEAR_DATA, SET_DATA]) 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 5ed3ae3e8c..162a56682a 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 @@ -19,8 +19,10 @@ package org.onap.cps.spi.impl import org.hibernate.StaleStateException +import org.onap.cps.spi.FetchDescendantsOption import org.onap.cps.spi.entities.FragmentEntity import org.onap.cps.spi.exceptions.ConcurrencyException +import org.onap.cps.spi.exceptions.DataValidationException import org.onap.cps.spi.model.DataNodeBuilder import org.onap.cps.spi.repository.AnchorRepository import org.onap.cps.spi.repository.DataspaceRepository @@ -68,5 +70,43 @@ class CpsDataPersistenceServiceSpec extends Specification { assert concurrencyException.getDetails().contains(parentXpath) } + def 'Retrieving a data node with a property JSON value of #scenario'() { + given: 'a fragment with a property JSON value of #scenario' + mockFragmentRepository.getByDataspaceAndAnchorAndXpath(_, _, _) >> { + new FragmentEntity(childFragments: Collections.emptySet(), + attributes: "{\"some attribute\": ${dataString}}") + } + when: 'getting the data node represented by this fragment' + def dataNode = objectUnderTest.getDataNode('my-dataspace', 'my-anchor', + 'parent-01', FetchDescendantsOption.INCLUDE_ALL_DESCENDANTS) + then: 'the leaf is of the correct value and data type' + def attributeValue = dataNode.leaves.get('some attribute') + assert attributeValue == expectedValue + assert attributeValue.class == expectedDataClass + where: 'the following Data Type is passed' + scenario | dataString || expectedValue | expectedDataClass + 'just numbers' | '15174' || 15174 | Integer + 'number with dot' | '15174.32' || 15174.32 | Double + 'number with 0 value after dot' | '15174.0' || 15174.0 | Double + 'number with 0 value before dot' | '0.32' || 0.32 | Double + 'number higher than max int' | '2147483648' || 2147483648 | Long + 'just text' | '"Test"' || 'Test' | String + 'number with exponent' | '1.2345e5' || 1.2345e5 | Double + 'number higher than max int with dot' | '123456789101112.0' || 123456789101112.0 | Double + 'text and numbers' | '"String = \'1234\'"' || "String = '1234'" | String + 'number as String' | '"12345"' || '12345' | String + } + + def 'Retrieving a data node with invalid JSON'() { + given: 'a fragment with invalid JSON' + mockFragmentRepository.getByDataspaceAndAnchorAndXpath(_, _, _) >> { + new FragmentEntity(childFragments: Collections.emptySet(), attributes: '{invalid json') + } + when: 'getting the data node represented by this fragment' + def dataNode = objectUnderTest.getDataNode('my-dataspace', 'my-anchor', + 'parent-01', FetchDescendantsOption.INCLUDE_ALL_DESCENDANTS) + then: 'a data validation exception is thrown' + thrown(DataValidationException) + } } 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 7a16a97d4b..7e42200799 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 @@ -20,22 +20,23 @@ */ package org.onap.cps.spi.impl -import static org.onap.cps.spi.CascadeDeleteAllowed.CASCADE_DELETE_ALLOWED -import static org.onap.cps.spi.CascadeDeleteAllowed.CASCADE_DELETE_PROHIBITED - import org.onap.cps.spi.CpsAdminPersistenceService import org.onap.cps.spi.CpsModulePersistenceService import org.onap.cps.spi.entities.YangResourceEntity -import org.onap.cps.spi.exceptions.DataspaceNotFoundException import org.onap.cps.spi.exceptions.AlreadyDefinedException +import org.onap.cps.spi.exceptions.DataspaceNotFoundException import org.onap.cps.spi.exceptions.SchemaSetInUseException import org.onap.cps.spi.exceptions.SchemaSetNotFoundException import org.onap.cps.spi.model.ModuleReference +import org.onap.cps.spi.model.ExtendedModuleReference import org.onap.cps.spi.repository.AnchorRepository import org.onap.cps.spi.repository.SchemaSetRepository import org.springframework.beans.factory.annotation.Autowired import org.springframework.test.context.jdbc.Sql +import static org.onap.cps.spi.CascadeDeleteAllowed.CASCADE_DELETE_ALLOWED +import static org.onap.cps.spi.CascadeDeleteAllowed.CASCADE_DELETE_PROHIBITED + class CpsModulePersistenceServiceIntegrationSpec extends CpsPersistenceSpecBase { @Autowired @@ -71,13 +72,13 @@ class CpsModulePersistenceServiceIntegrationSpec extends CpsPersistenceSpecBase static final String NEW_RESOURCE_CHECKSUM = 'b13faef573ed1374139d02c40d8ce09c80ea1dc70e63e464c1ed61568d48d539' static final String NEW_RESOURCE_MODULE_NAME = 'stores' static final String NEW_RESOURCE_REVISION = '2020-09-15' - static final ModuleReference newModuleReference = ModuleReference.builder().name(NEW_RESOURCE_MODULE_NAME) + static final ExtendedModuleReference newModuleReference = ExtendedModuleReference.builder().name(NEW_RESOURCE_MODULE_NAME) .revision(NEW_RESOURCE_REVISION).build() def newYangResourcesNameToContentMap = [(NEW_RESOURCE_NAME):NEW_RESOURCE_CONTENT] - def allYangResourcesModuleAndRevisionList = [ModuleReference.builder().build(),ModuleReference.builder().build(), - ModuleReference.builder().build(),ModuleReference.builder().build(), - ModuleReference.builder().build(), newModuleReference] + def allYangResourcesModuleAndRevisionList = [new ExtendedModuleReference(name: 'MODULE-NAME-002',namespace:null, revision: 'REVISION-002'), new ExtendedModuleReference(name: 'MODULE-NAME-003',namespace:null, revision: 'REVISION-003'), + new ExtendedModuleReference(name: 'MODULE-NAME-004',namespace:null, revision: 'REVISION-004'), ExtendedModuleReference.builder().build(), + ExtendedModuleReference.builder().build(), newModuleReference] def dataspaceEntity def setup() { @@ -109,7 +110,7 @@ class CpsModulePersistenceServiceIntegrationSpec extends CpsPersistenceSpecBase def 'Store and retrieve new schema set from new modules and existing modules.'() { given: 'map of new modules, a list of existing modules, module reference' def mapOfNewModules = [newModule1: 'module newmodule { yang-version 1.1; revision "2021-10-12" { } }'] - def moduleReferenceForExistingModule = new ModuleReference("test","test.org","2021-10-12") + def moduleReferenceForExistingModule = new ModuleReference("test","2021-10-12") def listOfExistingModulesModuleReference = [moduleReferenceForExistingModule] def mapOfExistingModule = [test: 'module test { yang-version 1.1; revision "2021-10-12" { } }'] objectUnderTest.storeSchemaSet(DATASPACE_NAME, "someSchemaSetName", mapOfExistingModule) @@ -135,13 +136,27 @@ class CpsModulePersistenceServiceIntegrationSpec extends CpsPersistenceSpecBase } @Sql([CLEAR_DATA, SET_DATA]) - def 'Retrieving all yang resources module references.'() { - given: 'a new schema set is stored' - objectUnderTest.storeSchemaSet(DATASPACE_NAME, SCHEMA_SET_NAME_NEW, newYangResourcesNameToContentMap) - when: 'all yang resources module references are retrieved' - def result = objectUnderTest.getAllYangResourcesModuleReferences() + def 'Retrieving all yang resources module references for the given dataspace.'() { + given: 'a dataspace name' + def dataspaceName = 'DATASPACE-002' + when: 'all yang resources module references are retrieved for the given dataspace name' + def result = objectUnderTest.getYangResourceModuleReferences(dataspaceName) then: 'the correct resources are returned' - result.sort() == allYangResourcesModuleAndRevisionList.sort() + result.sort() == [new ModuleReference(moduleName: 'MODULE-NAME-005', revision: 'REVISION-002'), + new ModuleReference(moduleName: 'MODULE-NAME-006', revision: 'REVISION-006')] + } + + @Sql([CLEAR_DATA, SET_DATA]) + def 'Retrieving module names and revisions for the given anchor.'() { + given: 'a dataspace name and anchor name' + def dataspaceName = 'DATASPACE-001' + def anchorName = 'ANCHOR1' + when: 'all yang resources module references are retrieved for the given anchor' + def result = objectUnderTest.getYangResourceModuleReferences(dataspaceName, anchorName) + then: 'the correct module names and revisions are returned' + result.sort() == [new ModuleReference(moduleName: null, revision: null), new ModuleReference(moduleName: 'MODULE-NAME-002', revision: 'REVISION-002'), + new ModuleReference(moduleName: 'MODULE-NAME-003', revision: 'REVISION-002'), + new ModuleReference(moduleName: 'MODULE-NAME-004', revision: 'REVISION-004')] } @Sql([CLEAR_DATA, SET_DATA]) diff --git a/cps-ri/src/test/groovy/org/onap/cps/spi/impl/CpsPersistenceSpecBase.groovy b/cps-ri/src/test/groovy/org/onap/cps/spi/impl/CpsPersistenceSpecBase.groovy index 5132632452..8ec5c90e9c 100644 --- a/cps-ri/src/test/groovy/org/onap/cps/spi/impl/CpsPersistenceSpecBase.groovy +++ b/cps-ri/src/test/groovy/org/onap/cps/spi/impl/CpsPersistenceSpecBase.groovy @@ -55,6 +55,7 @@ class CpsPersistenceSpecBase extends Specification { static final String CLEAR_DATA = '/data/clear-all.sql' static final String DATASPACE_NAME = 'DATASPACE-001' + static final String DATASPACE_NAME2 = 'DATASPACE-002' static final String SCHEMA_SET_NAME1 = 'SCHEMA-SET-001' static final String SCHEMA_SET_NAME2 = 'SCHEMA-SET-002' static final String ANCHOR_NAME1 = 'ANCHOR-001' diff --git a/cps-ri/src/test/resources/data/schemaset.sql b/cps-ri/src/test/resources/data/schemaset.sql index adfcfa172e..61600356b9 100644 --- a/cps-ri/src/test/resources/data/schemaset.sql +++ b/cps-ri/src/test/resources/data/schemaset.sql @@ -24,26 +24,32 @@ INSERT INTO DATASPACE (ID, NAME) VALUES (1001, 'DATASPACE-001'), (1002, 'DATASPACE-002'); INSERT INTO SCHEMA_SET (ID, NAME, DATASPACE_ID) VALUES - (2001, 'SCHEMA-SET-001', 1001), (2002, 'SCHEMA-SET-002', 1001), + (2001, 'SCHEMA-SET-001', 1001), + (2002, 'SCHEMA-SET-002', 1001), (2100, 'SCHEMA-SET-100', 1001), -- for removal, not referenced by anchors - (2101, 'SCHEMA-SET-101', 1001); -- for removal, having anchor and data associated - -INSERT INTO YANG_RESOURCE (ID, NAME, CONTENT, CHECKSUM) VALUES - (3001, 'module1@2020-02-02.yang', 'CONTENT-001', 'e8bdda931099310de66532e08c3fafec391db29f55c81927b168f6aa8f81b73b'), - (3002, 'module2@2020-02-02.yang', 'CONTENT-002', '7e7d48afbe066ed0a890a09081859046d3dde52300dfcdb13be5b20780353a11'), - (3003, 'module3@2020-02-02.yang', 'CONTENT-003', 'ca20c45fec8547633f05ff8905c48ffa7b02b94ec3ad4ed79922e6ba40779df3'), - (3004, 'module4@2020-02-02.yang', 'CONTENT-004', 'f6ed09d343562e4d4ae5140f3c6a55df9c53f6da8e30dda8cbd9eaf9cd449be0'), - (3100, 'orphan@2020-02-02.yang', 'ORPHAN', 'checksum'); -- for auto-removal as orphan + (2101, 'SCHEMA-SET-101', 1001), -- for removal, having anchor and data associated + (2003, 'SCHEMA-SET-003', 1002), + (2004, 'SCHEMA-SET-004', 1002); + +INSERT INTO YANG_RESOURCE (ID, NAME, CONTENT, CHECKSUM, MODULE_NAME, REVISION) VALUES + (3001, 'module1@2020-02-02.yang', 'CONTENT-001', 'e8bdda931099310de66532e08c3fafec391db29f55c81927b168f6aa8f81b73b',null,null), + (3002, 'module2@2020-02-02.yang', 'CONTENT-002', '7e7d48afbe066ed0a890a09081859046d3dde52300dfcdb13be5b20780353a11','MODULE-NAME-002','REVISION-002'), + (3003, 'module3@2020-02-02.yang', 'CONTENT-003', 'ca20c45fec8547633f05ff8905c48ffa7b02b94ec3ad4ed79922e6ba40779df3','MODULE-NAME-003','REVISION-002'), + (3004, 'module4@2020-02-02.yang', 'CONTENT-004', 'f6ed09d343562e4d4ae5140f3c6a55df9c53f6da8e30dda8cbd9eaf9cd449be0','MODULE-NAME-004','REVISION-004'), + (3100, 'orphan@2020-02-02.yang', 'ORPHAN', 'checksum',null,null), -- for auto-removal as orphan + (3005, 'module5@2020-02-02.yang', 'CONTENT-005', 'checksum-005','MODULE-NAME-005','REVISION-002'), + (3006, 'module6@2020-02-02.yang', 'CONTENT-006', 'checksum-006','MODULE-NAME-006','REVISION-006'); INSERT INTO SCHEMA_SET_YANG_RESOURCES (SCHEMA_SET_ID, YANG_RESOURCE_ID) VALUES (2001, 3001), (2001, 3002), (2002, 3003), (2002, 3004), (2100, 3003), (2100, 3100), -- orphan removal case - (2101, 3003), (2101, 3004); + (2101, 3003), (2101, 3004), + (2003, 3005), (2004, 3006); INSERT INTO ANCHOR (ID, NAME, DATASPACE_ID, SCHEMA_SET_ID) VALUES -- anchors for removal (6001, 'ANCHOR1', 1001, 2101), (6002, 'ANCHOR2', 1001, 2101); INSERT INTO FRAGMENT (ID, XPATH, ANCHOR_ID, DATASPACE_ID) VALUES - (7001, '/XPATH', 6001, 1001); + (7001, '/XPATH', 6001, 1001);
\ No newline at end of file diff --git a/cps-service/pom.xml b/cps-service/pom.xml index c69ead09e9..c8daccca4d 100644 --- a/cps-service/pom.xml +++ b/cps-service/pom.xml @@ -28,7 +28,7 @@ <parent>
<groupId>org.onap.cps</groupId>
<artifactId>cps-parent</artifactId>
- <version>1.1.0-SNAPSHOT</version>
+ <version>2.0.1-SNAPSHOT</version>
<relativePath>../cps-parent/pom.xml</relativePath>
</parent>
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 5c40331d74..1dccf49c9b 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 @@ -21,6 +21,7 @@ package org.onap.cps.api; +import java.util.Collection; import java.util.List; import java.util.Map; import org.checkerframework.checker.nullness.qual.NonNull; @@ -51,12 +52,12 @@ public interface CpsModuleService { * @param dataspaceName Dataspace name * @param schemaSetName schema set name * @param newYangResourcesModuleNameToContentMap YANG resources map where key is a module name and value is content - * @param existingModuleReferences List of YANG resources module references of the modules + * @param moduleReferences List of YANG resources module references of the modules * needed for this handle that are already in CPS */ void createSchemaSetFromModules(@NonNull String dataspaceName, @NonNull String schemaSetName, @NonNull Map<String, String> newYangResourcesModuleNameToContentMap, - @NonNull List<ModuleReference> existingModuleReferences); + @NonNull List<ModuleReference> moduleReferences); /** * Read schema set in the given dataspace. @@ -80,9 +81,19 @@ public interface CpsModuleService { @NonNull CascadeDeleteAllowed cascadeDeleteAllowed); /** - * Retrieve all modules and revisions known by CPS for all Yang Resources. + * Retrieve module references for the given dataspace name. * + * @param dataspaceName dataspace name + * @return a list of ModuleReference objects + */ + Collection<ModuleReference> getYangResourceModuleReferences(String dataspaceName); + + /** + * Retrieve module references for the given dataspace name and anchor name. + * + * @param dataspaceName dataspace name + * @param anchorName anchor name * @return a list of ModuleReference objects */ - List<ModuleReference> getAllYangResourcesModuleReferences(); + Collection<ModuleReference> getYangResourcesModuleReferences(String dataspaceName, String anchorName); } 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 0597d380a5..10326413c3 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 @@ -21,6 +21,7 @@ package org.onap.cps.api.impl; +import java.util.Collection; import java.util.List; import java.util.Map; import org.onap.cps.api.CpsModuleService; @@ -53,9 +54,9 @@ public class CpsModuleServiceImpl implements CpsModuleService { @Override public void createSchemaSetFromModules(final String dataspaceName, final String schemaSetName, final Map<String, String> newYangResourcesModuleNameToContentMap, - final List<ModuleReference> existingModuleReferences) { + final List<ModuleReference> moduleReferences) { cpsModulePersistenceService.storeSchemaSetFromModules(dataspaceName, schemaSetName, - newYangResourcesModuleNameToContentMap, existingModuleReferences); + newYangResourcesModuleNameToContentMap, moduleReferences); } @@ -64,7 +65,7 @@ public class CpsModuleServiceImpl implements CpsModuleService { final var yangTextSchemaSourceSet = yangTextSchemaSourceSetCache .get(dataspaceName, schemaSetName); return SchemaSet.builder().name(schemaSetName).dataspaceName(dataspaceName) - .moduleReferences(yangTextSchemaSourceSet.getModuleReferences()).build(); + .extendedModuleReferences(yangTextSchemaSourceSet.getModuleReferences()).build(); } @Override @@ -74,8 +75,13 @@ public class CpsModuleServiceImpl implements CpsModuleService { } @Override - public List<ModuleReference> getAllYangResourcesModuleReferences() { - return cpsModulePersistenceService.getAllYangResourcesModuleReferences(); + public Collection<ModuleReference> getYangResourceModuleReferences(final String dataspaceName) { + return cpsModulePersistenceService.getYangResourceModuleReferences(dataspaceName); } + @Override + public Collection<ModuleReference> getYangResourcesModuleReferences(final String dataspaceName, + final String anchorName) { + return cpsModulePersistenceService.getYangResourceModuleReferences(dataspaceName, anchorName); + } } diff --git a/cps-service/src/main/java/org/onap/cps/config/AsyncConfig.java b/cps-service/src/main/java/org/onap/cps/config/AsyncConfig.java index 4c961598e4..2667ef4909 100644 --- a/cps-service/src/main/java/org/onap/cps/config/AsyncConfig.java +++ b/cps-service/src/main/java/org/onap/cps/config/AsyncConfig.java @@ -22,6 +22,7 @@ package org.onap.cps.config; import javax.validation.constraints.Min; import lombok.Setter; +import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; import org.springframework.boot.context.properties.ConfigurationProperties; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; @@ -32,7 +33,8 @@ import org.springframework.validation.annotation.Validated; @EnableAsync @Configuration -@ConfigurationProperties("notification.async-executor") +@ConditionalOnProperty(name = "notification.async.enabled", havingValue = "true", matchIfMissing = false) +@ConfigurationProperties("notification.async.executor") @Validated @Setter public class AsyncConfig { 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 7ad109d815..9b50f9e917 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 @@ -21,6 +21,7 @@ package org.onap.cps.spi; +import java.util.Collection; import java.util.List; import java.util.Map; import org.checkerframework.checker.nullness.qual.NonNull; @@ -48,11 +49,11 @@ public interface CpsModulePersistenceService { * @param dataspaceName Dataspace name * @param schemaSetName Schema set name * @param newYangResourcesModuleNameToContentMap YANG resources map where key is a module name and value is content - * @param moduleReferenceList List of YANG resources module references + * @param moduleReferences List of YANG resources module references */ void storeSchemaSetFromModules(@NonNull String dataspaceName, @NonNull String schemaSetName, @NonNull Map<String, String> newYangResourcesModuleNameToContentMap, - @NonNull List<ModuleReference> moduleReferenceList); + @NonNull List<ModuleReference> moduleReferences); /** * Deletes Schema Set. @@ -89,9 +90,19 @@ public interface CpsModulePersistenceService { @NonNull String anchorName); /** - * Returns all YANG resources module references. + * Returns YANG resources module references for the given dataspace name. * - * @return List of all YANG resources module information in the database + * @param dataspaceName dataspace name + * @return Collection of all YANG resources module information in the database + */ + Collection<ModuleReference> getYangResourceModuleReferences(String dataspaceName); + + /** + * Get YANG resource module references for the given anchor name and dataspace name. + * + * @param dataspaceName dataspace name + * @param anchorName anchor name + * @return a collection of module names and revisions */ - List<ModuleReference> getAllYangResourcesModuleReferences(); + Collection<ModuleReference> getYangResourceModuleReferences(String dataspaceName, String anchorName); } diff --git a/cps-service/src/main/java/org/onap/cps/spi/model/DataNode.java b/cps-service/src/main/java/org/onap/cps/spi/model/DataNode.java index 5773bc45c9..8e9dff873a 100644 --- a/cps-service/src/main/java/org/onap/cps/spi/model/DataNode.java +++ b/cps-service/src/main/java/org/onap/cps/spi/model/DataNode.java @@ -38,7 +38,7 @@ public class DataNode { private String dataspace; private String schemaSetName; private String anchorName; - private ModuleReference moduleReference; + private ExtendedModuleReference extendedModuleReference; private String xpath; private Map<String, Object> leaves = Collections.emptyMap(); private Collection<String> xpathsChildren; diff --git a/cps-service/src/main/java/org/onap/cps/spi/model/ExtendedModuleReference.java b/cps-service/src/main/java/org/onap/cps/spi/model/ExtendedModuleReference.java new file mode 100644 index 0000000000..5e9c8d0cd7 --- /dev/null +++ b/cps-service/src/main/java/org/onap/cps/spi/model/ExtendedModuleReference.java @@ -0,0 +1,42 @@ +/* + * ============LICENSE_START======================================================= + * Copyright (C) 2020 Nordix Foundation. + * Modifications Copyright 2020-2021 Pantheon.tech + * ================================================================================ + * 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.spi.model; + +import java.io.Serializable; +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; + +@Data +@Builder +@NoArgsConstructor +@AllArgsConstructor +public class ExtendedModuleReference implements Serializable { + + private static final long serialVersionUID = 1L; + + private String name; + private String namespace; + private String revision; + +} diff --git a/cps-service/src/main/java/org/onap/cps/spi/model/ModuleReference.java b/cps-service/src/main/java/org/onap/cps/spi/model/ModuleReference.java index f9aa2b5904..9b73f8ff0f 100644 --- a/cps-service/src/main/java/org/onap/cps/spi/model/ModuleReference.java +++ b/cps-service/src/main/java/org/onap/cps/spi/model/ModuleReference.java @@ -1,7 +1,6 @@ /* * ============LICENSE_START======================================================= - * Copyright (C) 2020 Nordix Foundation. - * Modifications Copyright 2020-2021 Pantheon.tech + * Copyright (C) 2021 Nordix Foundation. * ================================================================================ * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -33,10 +32,7 @@ import lombok.NoArgsConstructor; @AllArgsConstructor public class ModuleReference implements Serializable { - private static final long serialVersionUID = 1L; - - private String name; - private String namespace; + private static final long serialVersionUID = -1761408847591042599L; + private String moduleName; private String revision; - } diff --git a/cps-service/src/main/java/org/onap/cps/spi/model/SchemaSet.java b/cps-service/src/main/java/org/onap/cps/spi/model/SchemaSet.java index caf7800d9b..4df7893e2c 100644 --- a/cps-service/src/main/java/org/onap/cps/spi/model/SchemaSet.java +++ b/cps-service/src/main/java/org/onap/cps/spi/model/SchemaSet.java @@ -35,5 +35,5 @@ public class SchemaSet implements Serializable { private static final long serialVersionUID = 1464791260718603291L; private String name; private String dataspaceName; - private List<ModuleReference> moduleReferences; + private List<ExtendedModuleReference> extendedModuleReferences; } diff --git a/cps-service/src/main/java/org/onap/cps/yang/YangTextSchemaSourceSet.java b/cps-service/src/main/java/org/onap/cps/yang/YangTextSchemaSourceSet.java index 32ee324c54..2c9d374b14 100644 --- a/cps-service/src/main/java/org/onap/cps/yang/YangTextSchemaSourceSet.java +++ b/cps-service/src/main/java/org/onap/cps/yang/YangTextSchemaSourceSet.java @@ -21,7 +21,7 @@ package org.onap.cps.yang; import java.util.List; import org.checkerframework.checker.nullness.qual.NonNull; -import org.onap.cps.spi.model.ModuleReference; +import org.onap.cps.spi.model.ExtendedModuleReference; import org.opendaylight.yangtools.yang.model.api.SchemaContext; /** @@ -35,7 +35,7 @@ public interface YangTextSchemaSourceSet { * @return list of ModuleRef */ @NonNull - List<ModuleReference> getModuleReferences(); + List<ExtendedModuleReference> getModuleReferences(); /** * Return SchemaContext for given YangSchema. diff --git a/cps-service/src/main/java/org/onap/cps/yang/YangTextSchemaSourceSetBuilder.java b/cps-service/src/main/java/org/onap/cps/yang/YangTextSchemaSourceSetBuilder.java index 3a65369dc7..5cbfd6222d 100644 --- a/cps-service/src/main/java/org/onap/cps/yang/YangTextSchemaSourceSetBuilder.java +++ b/cps-service/src/main/java/org/onap/cps/yang/YangTextSchemaSourceSetBuilder.java @@ -35,7 +35,7 @@ import java.util.stream.Collectors; import lombok.NoArgsConstructor; import org.onap.cps.spi.exceptions.CpsException; import org.onap.cps.spi.exceptions.ModelValidationException; -import org.onap.cps.spi.model.ModuleReference; +import org.onap.cps.spi.model.ExtendedModuleReference; import org.opendaylight.yangtools.yang.common.Revision; import org.opendaylight.yangtools.yang.model.api.Module; import org.opendaylight.yangtools.yang.model.api.SchemaContext; @@ -88,14 +88,14 @@ public final class YangTextSchemaSourceSetBuilder { } @Override - public List<ModuleReference> getModuleReferences() { + public List<ExtendedModuleReference> getModuleReferences() { return schemaContext.getModules().stream() .map(YangTextSchemaSourceSetImpl::toModuleReference) .collect(Collectors.toList()); } - private static ModuleReference toModuleReference(final Module module) { - return ModuleReference.builder() + private static ExtendedModuleReference toModuleReference(final Module module) { + return ExtendedModuleReference.builder() .name(module.getName()) .namespace(module.getQNameModule().getNamespace().toString()) .revision(module.getRevision().map(Revision::toString).orElse(null)) diff --git a/cps-service/src/test/groovy/org/onap/cps/api/impl/CpsModuleServiceImplSpec.groovy b/cps-service/src/test/groovy/org/onap/cps/api/impl/CpsModuleServiceImplSpec.groovy index d719b3d24e..2c23aa1bc8 100644 --- a/cps-service/src/test/groovy/org/onap/cps/api/impl/CpsModuleServiceImplSpec.groovy +++ b/cps-service/src/test/groovy/org/onap/cps/api/impl/CpsModuleServiceImplSpec.groovy @@ -25,6 +25,7 @@ package org.onap.cps.api.impl import org.onap.cps.TestUtils import org.onap.cps.spi.CpsModulePersistenceService import org.onap.cps.spi.exceptions.ModelValidationException +import org.onap.cps.spi.model.ExtendedModuleReference import org.onap.cps.spi.model.ModuleReference import org.spockframework.spring.SpringBean import org.springframework.beans.factory.annotation.Autowired @@ -63,7 +64,7 @@ class CpsModuleServiceImplSpec extends Specification { def 'Create schema set from new modules and existing modules.'() { given: 'a list of existing modules module reference' - def moduleReferenceForExistingModule = new ModuleReference("test", "test.org", "2021-10-12") + def moduleReferenceForExistingModule = new ExtendedModuleReference("test", "test.org", "2021-10-12") def listOfExistingModulesModuleReference = [moduleReferenceForExistingModule] when: 'create schema set from modules method is invoked' objectUnderTest.createSchemaSetFromModules("someDataspaceName", "someSchemaSetName", [newModule: "newContent"], listOfExistingModulesModuleReference) @@ -90,7 +91,7 @@ class CpsModuleServiceImplSpec extends Specification { then: 'the correct schema set is returned' result.getName().contains('someSchemaSet') result.getDataspaceName().contains('someDataspace') - result.getModuleReferences().contains(new ModuleReference('stores', 'org:onap:ccsdk:sample', '2020-09-15')) + result.getExtendedModuleReferences().contains(new ExtendedModuleReference('stores', 'org:onap:ccsdk:sample', '2020-09-15')) } def 'Schema set caching.'() { @@ -117,9 +118,18 @@ class CpsModuleServiceImplSpec extends Specification { def 'Get all yang resources module references.'(){ given: 'an already present module reference' + def moduleReferences = [new ExtendedModuleReference()] + mockModuleStoreService.getYangResourceModuleReferences('someDataspaceName') >> moduleReferences + expect: 'the list provided by persistence service is returned as result' + objectUnderTest.getYangResourceModuleReferences('someDataspaceName') == moduleReferences + } + + + def 'Get all yang resources module references for the given dataspace name and anchor name.'(){ + given: 'the module store service service returns a list module references' def moduleReferences = [new ModuleReference()] - mockModuleStoreService.getAllYangResourcesModuleReferences() >> moduleReferences + mockModuleStoreService.getYangResourceModuleReferences('someDataspaceName', 'someAnchorName') >> moduleReferences expect: 'the list provided by persistence service is returned as result' - objectUnderTest.getAllYangResourcesModuleReferences() == moduleReferences + objectUnderTest.getYangResourcesModuleReferences('someDataspaceName', 'someAnchorName') == moduleReferences } } diff --git a/cps-service/src/test/groovy/org/onap/cps/notification/NotificationServiceSpec.groovy b/cps-service/src/test/groovy/org/onap/cps/notification/NotificationServiceSpec.groovy index 875113d225..ca704edb4c 100644 --- a/cps-service/src/test/groovy/org/onap/cps/notification/NotificationServiceSpec.groovy +++ b/cps-service/src/test/groovy/org/onap/cps/notification/NotificationServiceSpec.groovy @@ -36,7 +36,6 @@ import spock.lang.Specification import java.util.concurrent.TimeUnit @SpringBootTest -@EnableAsync @EnableConfigurationProperties @ContextConfiguration(classes = [NotificationProperties, NotificationService, NotificationErrorHandler, AsyncConfig]) class NotificationServiceSpec extends Specification { @@ -105,10 +104,4 @@ class NotificationServiceSpec extends Specification { 1 * spyNotificationErrorHandler.onException(_, _, _, _) } - NotificationService createNotificationService(boolean notificationEnabled) { - spyNotificationProperties = Spy(notificationProperties) - spyNotificationProperties.isEnabled() >> notificationEnabled - return new NotificationService(spyNotificationProperties, mockNotificationPublisher, - mockCpsDataUpdatedEventFactory, spyNotificationErrorHandler) - } } diff --git a/cps-service/src/test/resources/application.yml b/cps-service/src/test/resources/application.yml index b1546d74f3..436c3d4c34 100644 --- a/cps-service/src/test/resources/application.yml +++ b/cps-service/src/test/resources/application.yml @@ -22,10 +22,12 @@ notification: enabled-dataspaces: ".*-published,.*-important" enabled: true topic: cps-event - async-executor: - core-pool-size: 2 - max-pool-size: 10 - queue-capacity: 0 + async: + enabled: true + executor: + core-pool-size: 2 + max-pool-size: 10 + queue-capacity: 0 spring: kafka: @@ -32,7 +32,7 @@ <groupId>org.onap.cps</groupId>
<artifactId>cps-aggregator</artifactId>
- <version>1.1.0-SNAPSHOT</version>
+ <version>2.0.1-SNAPSHOT</version>
<packaging>pom</packaging>
<name>cps</name>
diff --git a/releases/2.0.0-container.yaml b/releases/2.0.0-container.yaml new file mode 100644 index 0000000000..7021569a7b --- /dev/null +++ b/releases/2.0.0-container.yaml @@ -0,0 +1,9 @@ +distribution_type: container +container_release_tag: 2.0.0 +project: cps +log_dir: cps-maven-docker-stage-master/319/ +ref: d9b5bb295553f0adb2a09d341ad1e3f95c8c995d +tag_release: true +containers: + - name: 'cps-and-ncmp' + version: '2.0.0-20210913T194429Z' diff --git a/releases/2.0.0.yaml b/releases/2.0.0.yaml new file mode 100644 index 0000000000..202467a1d6 --- /dev/null +++ b/releases/2.0.0.yaml @@ -0,0 +1,5 @@ +distribution_type: maven +log_dir: cps-maven-stage-master/319/ +project: cps +version: 2.0.0 +tag_release: false diff --git a/spotbugs/pom.xml b/spotbugs/pom.xml index 6b2d8c1142..96bcac2e38 100644 --- a/spotbugs/pom.xml +++ b/spotbugs/pom.xml @@ -25,7 +25,7 @@ <modelVersion>4.0.0</modelVersion> <groupId>org.onap.cps</groupId> <artifactId>spotbugs</artifactId> - <version>1.1.0-SNAPSHOT</version> + <version>2.0.1-SNAPSHOT</version> <properties> <nexusproxy>https://nexus.onap.org</nexusproxy> diff --git a/version.properties b/version.properties index 04bd78a1e9..67022d0f32 100755 --- a/version.properties +++ b/version.properties @@ -19,8 +19,8 @@ # Note that these variables cannot be structured (e.g. : version.release or version.snapshot etc... ) # because they are used in Jenkins, whose plug-in doesn't support this -major=1 -minor=1 +major=2 +minor=0 patch=0 base_version=${major}.${minor}.${patch} |