<modelVersion>4.0.0</modelVersion>
<groupId>org.onap.cps</groupId>
<artifactId>checkstyle</artifactId>
- <version>2.1.0-SNAPSHOT</version>
+ <version>3.0.0-SNAPSHOT</version>
<properties>
<nexusproxy>https://nexus.onap.org</nexusproxy>
<parent>
<groupId>org.onap.cps</groupId>
<artifactId>cps-parent</artifactId>
- <version>2.1.0-SNAPSHOT</version>
+ <version>3.0.0-SNAPSHOT</version>
<relativePath>../cps-parent/pom.xml</relativePath>
</parent>
<modelVersion>4.0.0</modelVersion>
<groupId>org.onap.cps</groupId>
<artifactId>cps-bom</artifactId>
- <version>2.1.0-SNAPSHOT</version>
+ <version>3.0.0-SNAPSHOT</version>
<packaging>pom</packaging>
<description>This artifact contains dependencyManagement declarations of all published CPS components.</description>
<modelVersion>4.0.0</modelVersion>
<groupId>org.onap.cps</groupId>
<artifactId>cps-dependencies</artifactId>
- <version>2.1.0-SNAPSHOT</version>
+ <version>3.0.0-SNAPSHOT</version>
<packaging>pom</packaging>
<name>${project.groupId}:${project.artifactId}</name>
<snapshotNexusPath>/content/repositories/snapshots/</snapshotNexusPath>
<sonar.skip>true</sonar.skip>
<testcontainers.version>1.15.1</testcontainers.version>
+ <mapstruct.version>1.4.2.Final</mapstruct.version>
</properties>
<distributionManagement>
<artifactId>log4j-to-slf4j</artifactId>
<version>2.17.1</version>
</dependency>
+ <dependency>
+ <groupId>org.mapstruct</groupId>
+ <artifactId>mapstruct</artifactId>
+ <version>${mapstruct.version}</version>
+ </dependency>
+ <dependency>
+ <groupId>org.mapstruct</groupId>
+ <artifactId>mapstruct-processor</artifactId>
+ <version>${mapstruct.version}</version>
+ </dependency>
</dependencies>
</dependencyManagement>
</project>
<parent>
<groupId>org.onap.cps</groupId>
<artifactId>cps-parent</artifactId>
- <version>2.1.0-SNAPSHOT</version>
+ <version>3.0.0-SNAPSHOT</version>
<relativePath>../cps-parent/pom.xml</relativePath>
</parent>
dmiPlugin:
type: string
example: my-dmi-plugin
+ default: ""
dmiDataPlugin:
type: string
example: my-dmi-data-plugin
+ default: ""
dmiModelPlugin:
type: string
example: my-dmi-model-plugin
+ default: ""
createdCmHandles:
type: array
items:
schema:
$ref: 'components.yaml#/components/schemas/RestDmiPluginRegistration'
responses:
- 201:
- $ref: 'components.yaml#/components/responses/Created'
+ 204:
+ $ref: 'components.yaml#/components/responses/NoContent'
400:
$ref: 'components.yaml#/components/responses/BadRequest'
401:
<parent>
<groupId>org.onap.cps</groupId>
<artifactId>cps-parent</artifactId>
- <version>2.1.0-SNAPSHOT</version>
+ <version>3.0.0-SNAPSHOT</version>
<relativePath>../cps-parent/pom.xml</relativePath>
</parent>
<groupId>io.swagger.core.v3</groupId>
<artifactId>swagger-annotations</artifactId>
</dependency>
+ <dependency>
+ <groupId>org.mapstruct</groupId>
+ <artifactId>mapstruct</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>org.mapstruct</groupId>
+ <artifactId>mapstruct-processor</artifactId>
+ </dependency>
<!-- T E S T D E P E N D E N C I E S -->
<dependency>
<groupId>org.codehaus.groovy</groupId>
/*
* ============LICENSE_START=======================================================
* Copyright (C) 2021 Bell Canada
+ * Modifications Copyright (C) 2022 Nordix Foundation
* ================================================================================
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
+ *
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
package org.onap.cps.ncmp.rest.controller;
-
-import com.fasterxml.jackson.databind.ObjectMapper;
import javax.validation.Valid;
+import lombok.RequiredArgsConstructor;
import org.onap.cps.ncmp.api.NetworkCmProxyDataService;
-import org.onap.cps.ncmp.api.models.DmiPluginRegistration;
import org.onap.cps.ncmp.rest.api.NetworkCmProxyInventoryApi;
import org.onap.cps.ncmp.rest.model.RestDmiPluginRegistration;
import org.springframework.http.HttpStatus;
@RestController
@RequestMapping("${rest.api.ncmp-inventory-base-path}")
+@RequiredArgsConstructor
public class NetworkCmProxyInventoryController implements NetworkCmProxyInventoryApi {
private final NetworkCmProxyDataService networkCmProxyDataService;
- private final ObjectMapper objectMapper;
-
- /**
- * Constructor Injection for Dependencies.
- * @param networkCmProxyDataService Data Service Interface
- * @param objectMapper Object Mapper
- */
- public NetworkCmProxyInventoryController(final NetworkCmProxyDataService networkCmProxyDataService,
- final ObjectMapper objectMapper) {
- this.networkCmProxyDataService = networkCmProxyDataService;
- this.objectMapper = objectMapper;
- }
+ private final RestInputMapper restInputMapper;
/**
* Update DMI Plugin Registration (used for first registration also).
@Override
public ResponseEntity<Void> updateDmiPluginRegistration(
final @Valid RestDmiPluginRegistration restDmiPluginRegistration) {
- final DmiPluginRegistration dmiPluginRegistration =
- convertRestObjectToJavaApiObject(restDmiPluginRegistration);
- networkCmProxyDataService.updateDmiRegistrationAndSyncModule(dmiPluginRegistration);
- return new ResponseEntity<>(HttpStatus.CREATED);
- }
-
- private DmiPluginRegistration convertRestObjectToJavaApiObject(
- final RestDmiPluginRegistration restDmiPluginRegistration) {
- return objectMapper.convertValue(restDmiPluginRegistration, DmiPluginRegistration.class);
+ networkCmProxyDataService.updateDmiRegistrationAndSyncModule(
+ restInputMapper.toDmiPluginRegistration(restDmiPluginRegistration));
+ return new ResponseEntity<>(HttpStatus.NO_CONTENT);
}
}
+
+
--- /dev/null
+/*
+ * ============LICENSE_START=======================================================
+ * Copyright (C) 2022 Nordix Foundation
+ * ================================================================================
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ * ============LICENSE_END=========================================================
+ */
+
+package org.onap.cps.ncmp.rest.controller;
+
+import org.mapstruct.Mapper;
+import org.mapstruct.Mapping;
+import org.mapstruct.Mappings;
+import org.mapstruct.NullValueMappingStrategy;
+import org.mapstruct.NullValuePropertyMappingStrategy;
+import org.onap.cps.ncmp.api.models.CmHandle;
+import org.onap.cps.ncmp.api.models.DmiPluginRegistration;
+import org.onap.cps.ncmp.rest.model.RestCmHandle;
+import org.onap.cps.ncmp.rest.model.RestDmiPluginRegistration;
+
+@Mapper(componentModel = "spring", nullValueMappingStrategy = NullValueMappingStrategy.RETURN_DEFAULT,
+ nullValuePropertyMappingStrategy = NullValuePropertyMappingStrategy.SET_TO_DEFAULT)
+public interface RestInputMapper {
+
+ DmiPluginRegistration toDmiPluginRegistration(final RestDmiPluginRegistration restDmiPluginRegistration);
+
+ @Mappings({
+ @Mapping(source = "cmHandle", target = "cmHandleID"),
+ @Mapping(source = "cmHandleProperties", target = "dmiProperties"),
+ @Mapping(source = "publicCmHandleProperties", target = "publicProperties")
+ })
+ CmHandle toCmHandle(final RestCmHandle restCmHandle);
+
+}
import org.onap.cps.ncmp.api.impl.exception.NcmpException;
import org.onap.cps.ncmp.api.impl.exception.ServerNcmpException;
import org.onap.cps.ncmp.rest.controller.NetworkCmProxyController;
+import org.onap.cps.ncmp.rest.controller.NetworkCmProxyInventoryController;
import org.onap.cps.ncmp.rest.model.ErrorMessage;
import org.onap.cps.spi.exceptions.CpsException;
+import org.onap.cps.spi.exceptions.DataValidationException;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
+import org.springframework.http.converter.HttpMessageNotReadableException;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.RestControllerAdvice;
* Exception handler with error message return.
*/
@Slf4j
-@RestControllerAdvice(assignableTypes = {NetworkCmProxyController.class})
+@RestControllerAdvice(assignableTypes = {NetworkCmProxyController.class, NetworkCmProxyInventoryController.class})
@NoArgsConstructor(access = AccessLevel.PACKAGE)
public class NetworkCmProxyRestExceptionHandler {
return buildErrorResponse(HttpStatus.BAD_REQUEST, exception);
}
+ @ExceptionHandler({DataValidationException.class, HttpMessageNotReadableException.class})
+ public static ResponseEntity<Object> handleDataValidatedExceptions(final Exception exception) {
+ return buildErrorResponse(HttpStatus.BAD_REQUEST, exception);
+ }
+
private static ResponseEntity<Object> buildErrorResponse(final HttpStatus status, final Exception exception) {
if (exception.getCause() != null || !(exception instanceof CpsException)) {
log.error("Exception occurred", exception);
* 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.
import com.fasterxml.jackson.databind.ObjectMapper
import org.onap.cps.TestUtils
import org.onap.cps.ncmp.api.NetworkCmProxyDataService
-import org.onap.cps.ncmp.api.models.CmHandle
import org.onap.cps.ncmp.api.models.DmiPluginRegistration
+import org.onap.cps.ncmp.rest.model.RestDmiPluginRegistration
+import org.onap.cps.utils.JsonObjectMapper
import org.spockframework.spring.SpringBean
import org.springframework.beans.factory.annotation.Autowired
import org.springframework.beans.factory.annotation.Value
@SpringBean
NetworkCmProxyDataService mockNetworkCmProxyDataService = Mock()
+ @SpringBean
+ RestInputMapper restInputMapper = Mock()
+
+ DmiPluginRegistration mockDmiPluginRegistration = Mock()
+
+ JsonObjectMapper jsonObjectMapper = new JsonObjectMapper(new ObjectMapper())
+
@Value('${rest.api.ncmp-inventory-base-path}/v1')
def ncmpBasePathV1
- def 'Register CM Handle Event' () {
- given: 'jsonData'
- def jsonData = TestUtils.getResourceFileContent('dmi-registration.json')
- when: 'post request is performed'
+ def 'Dmi plugin registration #scenario' () {
+ given: 'a dmi plugin registration with #scenario'
+ def jsonData = TestUtils.getResourceFileContent(dmiRegistrationJson)
+ and: 'the expected rest input as an object'
+ def expectedRestDmiPluginRegistration = jsonObjectMapper.convertJsonString(jsonData, RestDmiPluginRegistration)
+ and: 'the converter returns a dmi registration (only for the expected input object)'
+ restInputMapper.toDmiPluginRegistration(expectedRestDmiPluginRegistration) >> mockDmiPluginRegistration
+ when: 'post request is performed & registration is called with correct DMI plugin information'
def response = mvc.perform(
post("$ncmpBasePathV1/ch")
- .contentType(MediaType.APPLICATION_JSON)
- .content(jsonData)
+ .contentType(MediaType.APPLICATION_JSON)
+ .content(jsonData)
).andReturn().response
- then: 'the cm handles are registered with the service'
- 1 * mockNetworkCmProxyDataService.updateDmiRegistrationAndSyncModule(_)
- and: 'response status is created'
- response.status == HttpStatus.CREATED.value()
+ then: 'the converted object is forwarded to the registration service'
+ 1 * mockNetworkCmProxyDataService.updateDmiRegistrationAndSyncModule(mockDmiPluginRegistration)
+ and: 'response status is no content'
+ response.status == HttpStatus.NO_CONTENT.value()
+ where: 'the following registration json is used'
+ scenario | dmiRegistrationJson
+ 'multiple services, added, updated and removed cm handles and many properties' | 'dmi_registration_all_singing_and_dancing.json'
+ 'updated cm handle with updated/new and removed properties' | 'dmi_registration_updates_only.json'
+ 'without any properties' | 'dmi_registration_without_properties.json'
}
- def 'Dmi plugin registration with #scenario' () {
- given: 'jsonData, cmHandle, & DmiPluginRegistration'
- def jsonData = TestUtils.getResourceFileContent('dmi_registration_combined_valid.json' )
- def cmHandle = new CmHandle(cmHandleID : 'example-name')
- def expectedDmiPluginRegistration = new DmiPluginRegistration(
- dmiPlugin: 'service1',
- dmiDataPlugin: '',
- dmiModelPlugin: '',
- createdCmHandles: [cmHandle])
+ def 'Dmi plugin registration with invalid json' () {
+ given: 'a dmi plugin registration with #scenario'
+ def jsonDataWithUndefinedDataLabel = '{"notAdmiPlugin":""}'
when: 'post request is performed & registration is called with correct DMI plugin information'
def response = mvc.perform(
post("$ncmpBasePathV1/ch")
.contentType(MediaType.APPLICATION_JSON)
- .content(jsonData)
+ .content(jsonDataWithUndefinedDataLabel)
).andReturn().response
- then: 'no NcmpException is thrown & updateDmiRegistrationAndSyncModule is called with correct parameters'
- 1 * mockNetworkCmProxyDataService.updateDmiRegistrationAndSyncModule({
- it.getDmiPlugin() == expectedDmiPluginRegistration.getDmiPlugin()
- it.getDmiDataPlugin() == expectedDmiPluginRegistration.getDmiDataPlugin()
- it.getDmiModelPlugin() == expectedDmiPluginRegistration.getDmiModelPlugin()
- it.getCreatedCmHandles().get(0).getCmHandleID() == expectedDmiPluginRegistration.getCreatedCmHandles().get(0).getCmHandleID()
- })
+ then: 'response status is bad request'
+ response.status == HttpStatus.BAD_REQUEST.value()
}
-}
+}
--- /dev/null
+/*
+ * ============LICENSE_START=======================================================
+ * Copyright (C) 2022 Nordix Foundation
+ * ================================================================================
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ * ============LICENSE_END=========================================================
+ */
+
+package org.onap.cps.ncmp.rest.controller
+
+import org.mapstruct.factory.Mappers
+import org.onap.cps.ncmp.rest.model.RestCmHandle
+import org.onap.cps.ncmp.rest.model.RestDmiPluginRegistration
+import spock.lang.Specification
+
+class RestInputMapperSpec extends Specification {
+
+ def objectUnderTest = Mappers.getMapper(RestInputMapper.class)
+
+ def 'Convert a created REST CM Handle Input to an NCMP Service CM Handle with #scenario'() {
+ given: 'a rest cm handle input'
+ def inputRestCmHandle = new RestCmHandle(cmHandle : 'example-id', cmHandleProperties: dmiProperties,
+ publicCmHandleProperties: publicProperties)
+ def restDmiPluginRegistration = new RestDmiPluginRegistration(
+ createdCmHandles: [inputRestCmHandle])
+ when: 'to plugin dmi registration is called'
+ def result = objectUnderTest.toDmiPluginRegistration(restDmiPluginRegistration)
+ then: 'the result returns the correct number of cm handles'
+ result.createdCmHandles.size() == 1
+ and: 'the converted cm handle has the same id'
+ result.createdCmHandles[0].cmHandleID == 'example-id'
+ and: '(empty) properties are converted correctly'
+ result.createdCmHandles[0].dmiProperties == expectedDmiProperties
+ result.createdCmHandles[0].publicProperties == expectedPublicProperties
+ where: 'the following parameters are used'
+ scenario | dmiProperties | publicProperties || expectedDmiProperties | expectedPublicProperties
+ 'dmi and public properties' | ['Property-Example': 'example property'] | ['Public-Property-Example': 'public example property'] || ['Property-Example': 'example property'] | ['Public-Property-Example': 'public example property']
+ 'no properties' | null | null || [:] | [:]
+ }
+
+ def 'Handling empty dmi registration'() {
+ given: 'a rest cm handle input without any cm handles'
+ def restDmiPluginRegistration = new RestDmiPluginRegistration()
+ when: 'to plugin dmi registration is called'
+ def result = objectUnderTest.toDmiPluginRegistration(restDmiPluginRegistration)
+ then: 'unspecified lists remain as empty lists'
+ assert result.createdCmHandles == []
+ assert result.updatedCmHandles == []
+ assert result.removedCmHandles == []
+ }
+
+}
* 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.
import groovy.json.JsonSlurper
import org.modelmapper.ModelMapper
+import org.onap.cps.TestUtils
import org.onap.cps.ncmp.api.NetworkCmProxyDataService
import org.onap.cps.ncmp.api.impl.exception.DmiRequestException
import org.onap.cps.ncmp.api.impl.exception.ServerNcmpException
+import org.onap.cps.ncmp.rest.controller.RestInputMapper
import org.onap.cps.spi.exceptions.CpsException
+import org.onap.cps.spi.exceptions.DataValidationException
import org.onap.cps.utils.JsonObjectMapper
import org.spockframework.spring.SpringBean
import org.springframework.beans.factory.annotation.Autowired
import org.springframework.beans.factory.annotation.Value
import org.springframework.boot.test.autoconfigure.web.servlet.WebMvcTest
+import org.springframework.http.MediaType
import org.springframework.test.web.servlet.MockMvc
import spock.lang.Shared
import spock.lang.Specification
+import static org.onap.cps.ncmp.rest.exceptions.NetworkCmProxyRestExceptionHandlerSpec.ApiType.NCMP
+import static org.onap.cps.ncmp.rest.exceptions.NetworkCmProxyRestExceptionHandlerSpec.ApiType.NCMPINVENTORY
import static org.springframework.http.HttpStatus.BAD_REQUEST
import static org.springframework.http.HttpStatus.INTERNAL_SERVER_ERROR
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get
+import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.post
@WebMvcTest
class NetworkCmProxyRestExceptionHandlerSpec extends Specification {
@SpringBean
JsonObjectMapper jsonObjectMapper = Stub()
+ @SpringBean
+ RestInputMapper restInputMapper = Mock()
+
@Value('${rest.api.ncmp-base-path}')
- def basePath
+ def basePathNcmp
- def dataNodeBaseEndpoint
+ @Value('${rest.api.ncmp-inventory-base-path}')
+ def basePathNcmpInventory
+
+ def dataNodeBaseEndpointNcmp
+ def dataNodeBaseEndpointNcmpInventory
@Shared
def errorMessage = 'some error message'
def errorDetails = 'some error details'
def setup() {
- dataNodeBaseEndpoint = "$basePath/v1"
+ dataNodeBaseEndpointNcmp = "$basePathNcmp/v1"
+ dataNodeBaseEndpointNcmpInventory = "$basePathNcmpInventory/v1"
}
def 'Get request with generic #scenario exception returns correct HTTP Status.'() {
when: 'an exception is thrown by the service'
- setupTestException(exception)
- def response = performTestRequest()
+ setupTestException(exception, NCMP)
+ def response = performTestRequest(NCMP)
then: 'an HTTP response is returned with correct message and details'
assertTestResponse(response, expectedErrorCode, errorMessage, expectedErrorDetails)
where:
'other' | new IllegalStateException(errorMessage) || null | INTERNAL_SERVER_ERROR
}
- def setupTestException(exception){
- mockNetworkCmProxyDataService.getYangResourcesModuleReferences('testCmHandle')>>
- { throw exception}
+ def 'Post request with exception returns correct HTTP Status.'() {
+ given: 'the service throws data validation exception'
+ def exception = new DataValidationException(errorMessage, errorDetails)
+ setupTestException(exception, NCMPINVENTORY)
+ when: 'the HTTP request is made'
+ def response = performTestRequest(NCMPINVENTORY)
+ then: 'an HTTP response is returned with correct message and details'
+ assertTestResponse(response, BAD_REQUEST, errorMessage, errorDetails)
}
- def performTestRequest(){
- return mvc.perform(get("$dataNodeBaseEndpoint/ch/testCmHandle/modules")).andReturn().response
+ def setupTestException(exception, apiType) {
+ if (NCMP == apiType) {
+ mockNetworkCmProxyDataService.getYangResourcesModuleReferences(*_) >> { throw exception }
+ }
+ mockNetworkCmProxyDataService.updateDmiRegistrationAndSyncModule(*_) >> { throw exception }
+ }
+
+ def performTestRequest(apiType) {
+ if (NCMP == apiType) {
+ return mvc.perform(get("$dataNodeBaseEndpointNcmp/ch/testCmHandle/modules")).andReturn().response
+ }
+ def jsonData = TestUtils.getResourceFileContent('dmi_registration_all_singing_and_dancing.json')
+ return mvc.perform(post("$dataNodeBaseEndpointNcmpInventory/ch").contentType(MediaType.APPLICATION_JSON).content(jsonData)).andReturn().response
}
static void assertTestResponse(response, expectedStatus , expectedErrorMessage , expectedErrorDetails) {
assert content['message'] == expectedErrorMessage
assert expectedErrorDetails == null || content['details'] == expectedErrorDetails
}
+
+ enum ApiType {
+ NCMP,
+ NCMPINVENTORY;
+ }
}
+++ /dev/null
-{
- "dmiPlugin": "onap-dmi-plugin",
- "createdCmHandles": [
- {
- "cmHandle": "example-name",
- "cmHandleProperties": {
- "subSystemId" : "system-001"
- },
- "publicCmHandleProperties": {
- "area" : "south"
- }
- }
- ]
-}
\ No newline at end of file
--- /dev/null
+{
+ "dmiDataPlugin":"service2",
+ "dmiModelPlugin":"service3",
+ "createdCmHandles":[
+ {
+ "cmHandle":"ch1(new)",
+ "cmHandleProperties":{
+ "dmiProp1":"ch1-dmi1",
+ "dmiProp2":"ch1-dmi2"
+ },
+ "publicCmHandleProperties":{
+ "pubProp1":"ch1-pub1",
+ "pubProp2":"ch1-pub2"
+ }
+ },
+ {
+ "cmHandle":"ch2(new)",
+ "cmHandleProperties":{
+ "dmiProp1":"ch2-dmi1",
+ "dmiProp2":"ch2-dmi2"
+ },
+ "publicCmHandleProperties":{
+ "pubProp1":"ch2-pub1",
+ "pubProp2":"ch2-pub2"
+ }
+ }
+ ],
+ "updatedCmHandles":[
+ {
+ "cmHandle":"ch3(upd)",
+ "cmHandleProperties":{
+ "dmiProp1":"ch3-dmi1"
+ },
+ "publicCmHandleProperties":{
+ "pubProp2":"ch3-pub1"
+ }
+ }
+ ],
+ "removedCmHandles":[
+ "ch4",
+ "ch5"
+ ]
+}
+++ /dev/null
-{
- "dmiPlugin": "service1",
- "dmiDataPlugin": "",
- "dmiModelPlugin": "",
- "createdCmHandles": [{
- "cmHandle": "example-name"
- }]
-}
\ No newline at end of file
--- /dev/null
+{
+ "dmiPlugin": "service1",
+ "updatedCmHandles":[
+ {
+ "cmHandle":"ch3(upd)",
+ "cmHandleProperties":{
+ "dmiProp1":"ch3-dmi1",
+ "dmiProp2":null
+ }
+ }
+ ]
+}
--- /dev/null
+{
+ "dmiPlugin":"",
+ "dmiDataPlugin":"service2",
+ "dmiModelPlugin":"service3",
+ "createdCmHandles":[
+ {
+ "cmHandle": "ch1(new)"
+ }
+ ]
+}
<parent>
<groupId>org.onap.cps</groupId>
<artifactId>cps-parent</artifactId>
- <version>2.1.0-SNAPSHOT</version>
+ <version>3.0.0-SNAPSHOT</version>
<relativePath>../cps-parent/pom.xml</relativePath>
</parent>
import static org.onap.cps.spi.CascadeDeleteAllowed.CASCADE_DELETE_ALLOWED;
import com.fasterxml.jackson.core.JsonProcessingException;
-import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
+import java.util.stream.Collectors;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.onap.cps.api.CpsAdminService;
public void updateDmiRegistrationAndSyncModule(final DmiPluginRegistration dmiPluginRegistration) {
dmiPluginRegistration.validateDmiPluginRegistration();
try {
- if (dmiPluginRegistration.getCreatedCmHandles() != null) {
+ if (!dmiPluginRegistration.getCreatedCmHandles().isEmpty()) {
parseAndCreateCmHandlesInDmiRegistrationAndSyncModules(dmiPluginRegistration);
}
- if (dmiPluginRegistration.getUpdatedCmHandles() != null) {
+ if (!dmiPluginRegistration.getUpdatedCmHandles().isEmpty()) {
parseAndUpdateCmHandlesInDmiRegistration(dmiPluginRegistration);
}
- if (dmiPluginRegistration.getRemovedCmHandles() != null) {
- parseAndRemoveCmHandlesInDmiRegistration(dmiPluginRegistration);
- }
+ parseAndRemoveCmHandlesInDmiRegistration(dmiPluginRegistration);
} catch (final JsonProcessingException | DataNodeNotFoundException e) {
final String errorMessage = String.format(
- "Error occurred while processing the CM-handle registration request [%s] ,caused by : [%s]",
- dmiPluginRegistration, e.getMessage());
+ "Error occurred while processing the CM-handle registration request, caused by : [%s]",
+ e.getMessage());
throw new DataValidationException(errorMessage, e.getMessage(), e);
}
}
}
private void syncAndCreateSchemaSet(final PersistenceCmHandle persistenceCmHandle) {
- final List<ModuleReference> moduleReferencesFromCmHandle =
+ final Collection<ModuleReference> moduleReferencesFromCmHandle =
dmiModelOperations.getModuleReferences(persistenceCmHandle);
- final List<ModuleReference> existingModuleReferences = new ArrayList<>();
- final List<ModuleReference> unknownModuleReferences = new ArrayList<>();
- prepareModuleSubsets(moduleReferencesFromCmHandle, existingModuleReferences, unknownModuleReferences);
- final Map<String, String> newYangResourcesModuleNameToContentMap;
- if (unknownModuleReferences.isEmpty()) {
- newYangResourcesModuleNameToContentMap = new HashMap<>();
+ final Collection<ModuleReference> identifiedNewModuleReferencesFromCmHandle = cpsModuleService
+ .identifyNewModuleReferences(moduleReferencesFromCmHandle);
+
+ final Collection<ModuleReference> existingModuleReferencesFromCmHandle =
+ moduleReferencesFromCmHandle.stream().filter(moduleReferenceFromCmHandle ->
+ !identifiedNewModuleReferencesFromCmHandle.contains(moduleReferenceFromCmHandle)
+ ).collect(Collectors.toList());
+
+ final Map<String, String> newModuleNameToContentMap;
+ if (identifiedNewModuleReferencesFromCmHandle.isEmpty()) {
+ newModuleNameToContentMap = new HashMap<>();
} else {
- newYangResourcesModuleNameToContentMap = dmiModelOperations.getNewYangResourcesFromDmi(persistenceCmHandle,
- unknownModuleReferences);
+ newModuleNameToContentMap = dmiModelOperations.getNewYangResourcesFromDmi(persistenceCmHandle,
+ identifiedNewModuleReferencesFromCmHandle);
}
cpsModuleService
.createSchemaSetFromModules(NFP_OPERATIONAL_DATASTORE_DATASPACE_NAME, persistenceCmHandle.getId(),
- newYangResourcesModuleNameToContentMap, existingModuleReferences);
- }
-
- private void prepareModuleSubsets(final List<ModuleReference> moduleReferencesFromCmHandle,
- final List<ModuleReference> existingModuleReferences,
- final List<ModuleReference> unknownModuleReferences) {
-
- final Collection<ModuleReference> knownModuleReferencesInCps =
- cpsModuleService.getYangResourceModuleReferences(NFP_OPERATIONAL_DATASTORE_DATASPACE_NAME);
-
- for (final ModuleReference moduleReferenceFromDmiForCmHandle : moduleReferencesFromCmHandle) {
- if (knownModuleReferencesInCps.contains(moduleReferenceFromDmiForCmHandle)) {
- existingModuleReferences.add(moduleReferenceFromDmiForCmHandle);
- } else {
- unknownModuleReferences.add(moduleReferenceFromDmiForCmHandle);
- }
- }
+ newModuleNameToContentMap, existingModuleReferencesFromCmHandle);
}
private void createAnchor(final PersistenceCmHandle persistenceCmHandle) {
import com.google.gson.JsonArray;
import com.google.gson.JsonObject;
import java.util.ArrayList;
+import java.util.Collection;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
* Retrieve yang resources from dmi for any modules that CPS-NCMP hasn't cached before.
*
* @param persistenceCmHandle the persistenceCmHandle
- * @param unknownModuleReferences the unknown module references
+ * @param newModuleReferences the unknown module references
* @return yang resources as map of module name to yang(re)source
*/
public Map<String, String> getNewYangResourcesFromDmi(final PersistenceCmHandle persistenceCmHandle,
- final List<ModuleReference> unknownModuleReferences) {
+ final Collection<ModuleReference> newModuleReferences) {
final String jsonWithDataAndDmiProperties = getRequestBodyToFetchYangResources(
- unknownModuleReferences, persistenceCmHandle.getDmiProperties());
+ newModuleReferences, persistenceCmHandle.getDmiProperties());
final ResponseEntity<Object> responseEntity = getResourceFromDmiWithJsonData(
persistenceCmHandle.resolveDmiServiceName(MODEL),
jsonWithDataAndDmiProperties,
return dmiRestClient.postOperationWithJsonData(dmiResourceDataUrl, jsonData, new HttpHeaders());
}
- private static String getRequestBodyToFetchYangResources(final List<ModuleReference> unknownModuleReferences,
+ private static String getRequestBodyToFetchYangResources(final Collection<ModuleReference> newModuleReferences,
final List<PersistenceCmHandle.Property> dmiProperties) {
- final JsonArray moduleReferencesAsJson = getModuleReferencesAsJson(unknownModuleReferences);
+ final JsonArray moduleReferencesAsJson = getModuleReferencesAsJson(newModuleReferences);
final JsonObject data = new JsonObject();
data.add("modules", moduleReferencesAsJson);
final JsonObject jsonRequestObject = new JsonObject();
return jsonRequestObject.toString();
}
- private static JsonArray getModuleReferencesAsJson(final List<ModuleReference> unknownModuleReferences) {
+ private static JsonArray getModuleReferencesAsJson(final Collection<ModuleReference> unknownModuleReferences) {
final JsonArray moduleReferences = new JsonArray();
for (final ModuleReference moduleReference : unknownModuleReferences) {
package org.onap.cps.ncmp.api.models;
-import com.fasterxml.jackson.annotation.JsonProperty;
import com.fasterxml.jackson.annotation.JsonSetter;
import com.fasterxml.jackson.annotation.Nulls;
import java.util.Collections;
@NoArgsConstructor
public class CmHandle {
- @JsonProperty("cmHandle")
private String cmHandleID;
@JsonSetter(nulls = Nulls.AS_EMPTY)
- @JsonProperty("cmHandleProperties")
private Map<String, String> dmiProperties = Collections.emptyMap();
@JsonSetter(nulls = Nulls.AS_EMPTY)
- @JsonProperty("publicCmHandleProperties")
private Map<String, String> publicProperties = Collections.emptyMap();
}
import com.fasterxml.jackson.annotation.JsonInclude;
import com.fasterxml.jackson.annotation.JsonInclude.Include;
-import com.fasterxml.jackson.annotation.JsonSetter;
-import com.fasterxml.jackson.annotation.Nulls;
import com.google.common.base.Strings;
+import java.util.Collections;
import java.util.List;
import lombok.Getter;
import lombok.Setter;
@JsonInclude(Include.NON_NULL)
public class DmiPluginRegistration {
- @JsonSetter(nulls = Nulls.AS_EMPTY)
private String dmiPlugin;
- @JsonSetter(nulls = Nulls.AS_EMPTY)
private String dmiDataPlugin;
- @JsonSetter(nulls = Nulls.AS_EMPTY)
private String dmiModelPlugin;
- private List<CmHandle> createdCmHandles;
+ private List<CmHandle> createdCmHandles = Collections.emptyList();
- private List<CmHandle> updatedCmHandles;
+ private List<CmHandle> updatedCmHandles = Collections.emptyList();
- private List<String> removedCmHandles;
+ private List<String> removedCmHandles = Collections.emptyList();
/**
* Validates plugin service names.
public class PersistenceCmHandlesList {
@JsonProperty("cm-handles")
- private List<PersistenceCmHandle> persistenceCmHandles = new ArrayList<>();
+ private final List<PersistenceCmHandle> persistenceCmHandles = new ArrayList<>();
/**
* Create a PersistenceCmHandleList given all service names and a collection of cmHandles.
def cmHandle = new CmHandle()
def dmiServiceName = 'some service name'
cmHandle.cmHandleID = 'cm handle id 1'
- cmHandle.dmiProperties = dmiProperties
def persistenceCmHandle = PersistenceCmHandle.toPersistenceCmHandle(dmiServiceName, '' , '', cmHandle)
and: 'DMI operations returns some module references'
def moduleReferences = [ new ModuleReference(moduleName:'module1',revision:'1'),
new ModuleReference(moduleName:'module2',revision:'2') ]
mockDmiModelOperations.getModuleReferences(persistenceCmHandle) >> moduleReferences
and: 'CPS-Core returns list of existing module resources'
- mockCpsModuleService.getYangResourceModuleReferences(expectedDataspaceName) >> existingModuleResourcesInCps
+ mockCpsModuleService.getYangResourceModuleReferences(expectedDataspaceName) >> toModuleReference(existingModuleResourcesInCps)
and: 'DMI-Plugin returns resource(s) for "new" module(s)'
mockDmiModelOperations.getNewYangResourcesFromDmi(persistenceCmHandle, [new ModuleReference('module1', '1')]) >> yangResourceToContentMap
when: 'module sync is triggered'
+ mockCpsModuleService.identifyNewModuleReferences(moduleReferences) >> toModuleReference(identifiedNewModuleReferences)
objectUnderTest.syncModulesAndCreateAnchor(persistenceCmHandle)
then: 'the CPS module service is called once with the correct parameters'
- 1 * mockCpsModuleService.createSchemaSetFromModules(expectedDataspaceName, persistenceCmHandle.getId(), yangResourceToContentMap, expectedKnownModules)
+ 1 * mockCpsModuleService.createSchemaSetFromModules(expectedDataspaceName, persistenceCmHandle.getId(), yangResourceToContentMap, toModuleReference(expectedKnownModules))
and: 'admin service create anchor method has been called with correct parameters'
1 * mockCpsAdminService.createAnchor(expectedDataspaceName, persistenceCmHandle.getId(), persistenceCmHandle.getId())
where: 'the following parameters are used'
- scenario | dmiProperties | existingModuleResourcesInCps | yangResourceToContentMap || expectedKnownModules
- 'one unknown module' | ['name1': 'value1'] | [new ModuleReference('module2', '2'), new ModuleReference('module3', '3')] | [module1: 'some yang source'] || [new ModuleReference('module2', '2')]
- 'no add. properties' | [:] | [new ModuleReference('module2', '2'), new ModuleReference('module3', '3')] | [module1: 'some yang source'] || [new ModuleReference('module2', '2')]
- 'no unknown module' | [:] | [new ModuleReference('module1', '1'), new ModuleReference('module2', '2')] | [:] || [new ModuleReference('module1', '1'), new ModuleReference('module2', '2')]
+ scenario | existingModuleResourcesInCps | identifiedNewModuleReferences | yangResourceToContentMap || expectedKnownModules
+ 'one new module' | [['module2' : '2'], ['module3' : '3']] | [['module1' : '1']] | [module1: 'some yang source'] || [['module2' : '2']]
+ 'no add. properties' | [['module2' : '2'], ['module3' : '3']] | [['module1' : '1']] | [module1: 'some yang source'] || [['module2' : '2']]
+ 'no new module' | [['module1' : '1'], ['module2' : '2']] | [] | [:] || [['module1' : '1'], ['module2' : '2']]
}
+
+ def toModuleReference(moduleReferenceAsMap) {
+ def moduleReferences = [].withDefault { [:] }
+ moduleReferenceAsMap.forEach(property ->
+ property.forEach((moduleName, revision) -> {
+ moduleReferences.add(new ModuleReference('moduleName' : moduleName, 'revision' : revision))
+ }))
+ return moduleReferences
+ }
+
}
expectedCallsToSaveNode * mockCpsDataService.saveListElements('NCMP-Admin', 'ncmp-dmi-registry',
'/dmi-registry', expectedJsonData, noTimestamp)
and: 'update data node leaves is called with correct parameters'
- expectedCallsToPropertyHandler * mockNetworkCmProxyDataServicePropertyHandler.updateCmHandleProperties(updatedCmHandles)
+ expectedCallsToUpdateCmHandleProperty * mockNetworkCmProxyDataServicePropertyHandler.updateCmHandleProperties(updatedCmHandles)
and: 'delete schema set is invoked with the correct parameters'
expectedCallsToDeleteSchemaSetAndListElement * mockCpsModuleService.deleteSchemaSet('NFP-Operational', 'cmHandle001', CASCADE_DELETE_ALLOWED)
and: 'delete list or list element is invoked with the correct parameters'
expectedCallsToDeleteSchemaSetAndListElement * mockCpsDataService.deleteListOrListElement('NCMP-Admin',
'ncmp-dmi-registry', "/dmi-registry/cm-handles[@id='cmHandle001']", noTimestamp)
where:
- scenario | createdCmHandles | updatedCmHandles | removedCmHandles || expectedCallsToSaveNode | expectedCallsToDeleteSchemaSetAndListElement | expectedCallsToPropertyHandler
- 'create' | [persistenceCmHandle] | [] | [] || 1 | 0 | 1
+ scenario | createdCmHandles | updatedCmHandles | removedCmHandles || expectedCallsToSaveNode | expectedCallsToDeleteSchemaSetAndListElement | expectedCallsToUpdateCmHandleProperty
+ 'create' | [persistenceCmHandle] | [] | [] || 1 | 0 | 0
'update' | [] | [persistenceCmHandle] | [] || 0 | 0 | 1
- 'delete' | [] | [] | cmHandlesArray || 0 | 1 | 1
+ 'delete' | [] | [] | cmHandlesArray || 0 | 1 | 0
'create, update and delete' | [persistenceCmHandle] | [persistenceCmHandle] | cmHandlesArray || 1 | 1 | 1
- 'no valid data' | null | null | null || 0 | 0 | 0
+ 'no valid data' | [] | [] | [] || 0 | 0 | 0
}
def 'Register a DMI Plugin for the given cm-handle(s) without DMI properties.'() {
'/dmi-registry', expectedJsonData, noTimestamp)
}
- def 'Register a DMI Plugin for a given cm-handle(s) with JSON processing errors during #scenario process.'() {
+ def 'Register a DMI Plugin for a given cm-handle(s) with JSON processing errors during process.'() {
given: 'a registration without cm-handle properties '
NetworkCmProxyDataServiceImpl objectUnderTest = getObjectUnderTestWithModelSyncDisabled()
def dmiPluginRegistration = new DmiPluginRegistration(dmiPlugin:'some-plugin')
- dmiPluginRegistration.createdCmHandles = createdCmHandles
- dmiPluginRegistration.updatedCmHandles = updatedCmHandles
+ dmiPluginRegistration.createdCmHandles = [persistenceCmHandle]
and: 'an json processing exception occurs'
spiedJsonObjectMapper.asJsonString(_) >> { throw (new JsonProcessingException('')) }
when: 'registration is updated and modules are synced'
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 for the given cm-handle(s) with no data found during delete process.'() {
<groupId>org.onap.cps</groupId>
<artifactId>cps-parent</artifactId>
- <version>2.1.0-SNAPSHOT</version>
+ <version>3.0.0-SNAPSHOT</version>
<packaging>pom</packaging>
<properties>
<parent>
<groupId>org.onap.cps</groupId>
<artifactId>cps-parent</artifactId>
- <version>2.1.0-SNAPSHOT</version>
+ <version>3.0.0-SNAPSHOT</version>
<relativePath>../cps-parent/pom.xml</relativePath>
</parent>
# ============LICENSE_START=======================================================
-# Copyright (c) 2021 Bell Canada.
+# Copyright (c) 2021-2022 Bell Canada.
# Modifications Copyright (C) 2021-2022 Nordix Foundation
# ================================================================================
# Licensed under the Apache License, Version 2.0 (the "License");
example: my-schema-set
examples:
- dataSampleRequest:
+ dataSample:
value:
test:bookstore:
bookstore-name: Chapters
- code: 02
name: kids
- dataSampleResponse:
- summary: The data node returned does not include the root node. This is being investigated as a part of CPS-461
- value:
- bookstore-name: Chapters
- categories:
- - code: 01
- name: SciFi
- - code: 02
- name: kids
-
parameters:
dataspaceNameInQuery:
name: dataspace-name
schema:
type: object
examples:
- dataSampleResponse:
+ dataSample:
value: ""
Created:
description: Created
# ============LICENSE_START=======================================================
-# Copyright (c) 2021 Bell Canada.
+# Copyright (c) 2021-2022 Bell Canada.
# Modifications Copyright (C) 2021-2022 Nordix Foundation
# ================================================================================
# Licensed under the Apache License, Version 2.0 (the "License");
schema:
type: object
examples:
- dataSampleResponse:
- $ref: 'components.yml#/components/examples/dataSampleResponse'
+ dataSample:
+ $ref: 'components.yml#/components/examples/dataSample'
'400':
$ref: 'components.yml#/components/responses/BadRequest'
'401':
schema:
type: object
examples:
- dataSampleRequest:
- $ref: 'components.yml#/components/examples/dataSampleRequest'
+ dataSample:
+ $ref: 'components.yml#/components/examples/dataSample'
responses:
'201':
$ref: 'components.yml#/components/responses/Created'
schema:
type: object
examples:
- dataSampleRequest:
- $ref: 'components.yml#/components/examples/dataSampleRequest'
+ dataSample:
+ $ref: 'components.yml#/components/examples/dataSample'
responses:
'200':
$ref: 'components.yml#/components/responses/Ok'
schema:
type: object
examples:
- dataSampleRequest:
- $ref: 'components.yml#/components/examples/dataSampleRequest'
+ dataSample:
+ $ref: 'components.yml#/components/examples/dataSample'
responses:
'201':
$ref: 'components.yml#/components/responses/Created'
schema:
type: object
examples:
- dataSampleRequest:
- $ref: 'components.yml#/components/examples/dataSampleRequest'
+ dataSample:
+ $ref: 'components.yml#/components/examples/dataSample'
responses:
'200':
$ref: 'components.yml#/components/responses/Ok'
schema:
type: object
examples:
- dataSampleRequest:
- $ref: 'components.yml#/components/examples/dataSampleRequest'
+ dataSample:
+ $ref: 'components.yml#/components/examples/dataSample'
responses:
'200':
$ref: 'components.yml#/components/responses/Ok'
# ============LICENSE_START=======================================================
# Copyright (C) 2021 Nordix Foundation
-# Copyright (C) 2021-2022 Nordix Foundation
+# Modifications Copyright (c) 2022 Bell Canada.
# ================================================================================
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
schema:
type: object
examples:
- dataSampleResponse:
- $ref: 'components.yml#/components/examples/dataSampleResponse'
+ dataSample:
+ $ref: 'components.yml#/components/examples/dataSample'
'400':
$ref: 'components.yml#/components/responses/BadRequest'
'401':
<parent>
<groupId>org.onap.cps</groupId>
<artifactId>cps-parent</artifactId>
- <version>2.1.0-SNAPSHOT</version>
+ <version>3.0.0-SNAPSHOT</version>
<relativePath>../cps-parent/pom.xml</relativePath>
</parent>
<parent>\r
<groupId>org.onap.cps</groupId>\r
<artifactId>cps-parent</artifactId>\r
- <version>2.1.0-SNAPSHOT</version>\r
+ <version>3.0.0-SNAPSHOT</version>\r
<relativePath>../cps-parent/pom.xml</relativePath>\r
</parent>\r
\r
/*
* ============LICENSE_START=======================================================
- * Copyright (C) 2020 Nordix Foundation
+ * Copyright (C) 2020-2022 Nordix Foundation
* Modifications Copyright (C) 2020-2022 Bell Canada.
* Modifications Copyright (C) 2021 Pantheon.tech
* ================================================================================
import java.util.regex.Pattern;
import java.util.stream.Collectors;
import javax.transaction.Transactional;
+import lombok.AllArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.codec.digest.DigestUtils;
import org.apache.commons.lang3.StringUtils;
import org.onap.cps.spi.exceptions.DuplicatedYangResourceException;
import org.onap.cps.spi.exceptions.ModelValidationException;
import org.onap.cps.spi.model.ModuleReference;
-import org.onap.cps.spi.repository.AnchorRepository;
import org.onap.cps.spi.repository.DataspaceRepository;
-import org.onap.cps.spi.repository.FragmentRepository;
+import org.onap.cps.spi.repository.ModuleReferenceRepository;
import org.onap.cps.spi.repository.SchemaSetRepository;
import org.onap.cps.spi.repository.YangResourceRepository;
import org.opendaylight.yangtools.yang.common.Revision;
import org.opendaylight.yangtools.yang.model.repo.api.RevisionSourceIdentifier;
import org.opendaylight.yangtools.yang.model.repo.api.YangTextSchemaSource;
import org.opendaylight.yangtools.yang.parser.rfc7950.repo.YangModelDependencyInfo;
-import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.dao.DataIntegrityViolationException;
import org.springframework.retry.annotation.Backoff;
import org.springframework.retry.annotation.Retryable;
import org.springframework.stereotype.Component;
-
-@Component
@Slf4j
+@Component
+@AllArgsConstructor
public class CpsModulePersistenceServiceImpl implements CpsModulePersistenceService {
private static final String YANG_RESOURCE_CHECKSUM_CONSTRAINT_NAME = "yang_resource_checksum_key";
private static final Pattern RFC6020_RECOMMENDED_FILENAME_PATTERN = Pattern
.compile("([\\w-]+)@(\\d{4}-\\d{2}-\\d{2})(?:\\.yang)?", Pattern.CASE_INSENSITIVE);
- @Autowired
private YangResourceRepository yangResourceRepository;
- @Autowired
private SchemaSetRepository schemaSetRepository;
- @Autowired
private DataspaceRepository dataspaceRepository;
- @Autowired
- private AnchorRepository anchorRepository;
-
- @Autowired
- private FragmentRepository fragmentRepository;
-
- @Autowired
private CpsAdminPersistenceService cpsAdminPersistenceService;
+ private ModuleReferenceRepository moduleReferenceRepository;
+
@Override
public Map<String, String> getYangSchemaResources(final String dataspaceName, final String schemaSetName) {
final var dataspaceEntity = dataspaceRepository.getByName(dataspaceName);
@Retryable(value = DuplicatedYangResourceException.class, maxAttempts = 5, backoff =
@Backoff(random = true, delay = 200, maxDelay = 2000, multiplier = 2))
public void storeSchemaSet(final String dataspaceName, final String schemaSetName,
- final Map<String, String> yangResourcesNameToContentMap) {
+ final Map<String, String> moduleReferenceNameToContentMap) {
final var dataspaceEntity = dataspaceRepository.getByName(dataspaceName);
- final var yangResourceEntities = synchronizeYangResources(yangResourcesNameToContentMap);
+ final var yangResourceEntities = synchronizeYangResources(moduleReferenceNameToContentMap);
final var schemaSetEntity = new SchemaSetEntity();
schemaSetEntity.setName(schemaSetName);
schemaSetEntity.setDataspace(dataspaceEntity);
@Retryable(value = DuplicatedYangResourceException.class, maxAttempts = 5, backoff =
@Backoff(random = true, delay = 200, maxDelay = 2000, multiplier = 2))
public void storeSchemaSetFromModules(final String dataspaceName, final String schemaSetName,
- final Map<String, String> newYangResourcesModuleNameToContentMap,
- final List<ModuleReference> moduleReferences) {
- storeSchemaSet(dataspaceName, schemaSetName, newYangResourcesModuleNameToContentMap);
+ final Map<String, String> newModuleNameToContentMap,
+ final Collection<ModuleReference> moduleReferences) {
+ storeSchemaSet(dataspaceName, schemaSetName, newModuleNameToContentMap);
final var dataspaceEntity = dataspaceRepository.getByName(dataspaceName);
final var schemaSetEntity =
schemaSetRepository.getByDataspaceAndName(dataspaceEntity, schemaSetName);
yangResourceRepository.deleteOrphans();
}
- private Set<YangResourceEntity> synchronizeYangResources(final Map<String, String> yangResourcesNameToContentMap) {
- final Map<String, YangResourceEntity> checksumToEntityMap = yangResourcesNameToContentMap.entrySet().stream()
+ @Override
+ public Collection<ModuleReference> identifyNewModuleReferences(
+ final Collection<ModuleReference> moduleReferencesToCheck) {
+ return moduleReferenceRepository.identifyNewModuleReferences(moduleReferencesToCheck);
+ }
+
+ private Set<YangResourceEntity> synchronizeYangResources(
+ final Map<String, String> moduleReferenceNameToContentMap) {
+ final Map<String, YangResourceEntity> checksumToEntityMap = moduleReferenceNameToContentMap.entrySet().stream()
.map(entry -> {
final String checksum = DigestUtils.sha256Hex(entry.getValue().getBytes(StandardCharsets.UTF_8));
final Map<String, String> moduleNameAndRevisionMap = createModuleNameAndRevisionMap(entry.getKey(),
--- /dev/null
+/*-
+ * ============LICENSE_START=======================================================
+ * Copyright (C) 2022 Nordix Foundation.
+ * ================================================================================
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ * ============LICENSE_END=========================================================
+ */
+
+package org.onap.cps.spi.repository;
+
+import java.util.Collection;
+import org.onap.cps.spi.model.ModuleReference;
+
+/**
+ * This interface is used in conjunction with {@link ModuleReferenceRepository} to create native sql queries.
+ */
+public interface ModuleReferenceQuery {
+
+ Collection<ModuleReference> identifyNewModuleReferences(
+ final Collection<ModuleReference> moduleReferencesToCheck);
+
+}
--- /dev/null
+/*
+ * ============LICENSE_START=======================================================
+ * Copyright (C) 2022 Nordix Foundation.
+ * ================================================================================
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ * ============LICENSE_END=========================================================
+ */
+
+package org.onap.cps.spi.repository;
+
+import java.util.Collection;
+import org.onap.cps.spi.entities.YangResourceEntity;
+import org.onap.cps.spi.model.ModuleReference;
+import org.springframework.data.jpa.repository.JpaRepository;
+import org.springframework.stereotype.Repository;
+
+@Repository
+public interface ModuleReferenceRepository extends
+ JpaRepository<YangResourceEntity, Long>, ModuleReferenceQuery {
+
+ Collection<ModuleReference> identifyNewModuleReferences(
+ final Collection<ModuleReference> moduleReferencesToCheck);
+
+}
\ No newline at end of file
--- /dev/null
+/*-
+ * ============LICENSE_START=======================================================
+ * Copyright (C) 2022 Nordix Foundation.
+ * ================================================================================
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ * ============LICENSE_END=========================================================
+ */
+
+package org.onap.cps.spi.repository;
+
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.List;
+import java.util.UUID;
+import javax.persistence.EntityManager;
+import javax.persistence.PersistenceContext;
+import lombok.SneakyThrows;
+import lombok.extern.slf4j.Slf4j;
+import org.onap.cps.spi.model.ModuleReference;
+import org.springframework.transaction.annotation.Transactional;
+
+@Slf4j
+@Transactional
+public class ModuleReferenceRepositoryImpl implements ModuleReferenceQuery {
+
+ @PersistenceContext
+ private EntityManager entityManager;
+
+ @Override
+ @SneakyThrows
+ public Collection<ModuleReference> identifyNewModuleReferences(
+ final Collection<ModuleReference> moduleReferencesToCheck) {
+
+ if (moduleReferencesToCheck == null || moduleReferencesToCheck.isEmpty()) {
+ return Collections.EMPTY_LIST;
+ }
+
+ final String tempTableName = "moduleReferencesToCheckTemp"
+ + UUID.randomUUID().toString().replaceAll("-", "");
+
+ createTemporaryTable(tempTableName);
+ insertDataIntoTable(tempTableName, moduleReferencesToCheck);
+
+ return identifyNewModuleReferencesForCmHandle(tempTableName);
+ }
+
+ private void createTemporaryTable(final String tempTableName) {
+ final StringBuilder sqlStringBuilder = new StringBuilder("CREATE TEMPORARY TABLE " + tempTableName + "(");
+ sqlStringBuilder.append(" id INT GENERATED ALWAYS AS IDENTITY PRIMARY KEY,");
+ sqlStringBuilder.append(" module_name varchar NOT NULL,");
+ sqlStringBuilder.append(" revision varchar NOT NULL");
+ sqlStringBuilder.append(");");
+
+ entityManager.createNativeQuery(sqlStringBuilder.toString()).executeUpdate();
+ }
+
+ private void insertDataIntoTable(final String tempTableName, final Collection<ModuleReference> moduleReferences) {
+ final StringBuilder sqlStringBuilder = new StringBuilder("INSERT INTO " + tempTableName);
+ sqlStringBuilder.append(" (module_name, revision) ");
+ sqlStringBuilder.append(" VALUES ");
+
+ for (final ModuleReference moduleReference : moduleReferences) {
+ sqlStringBuilder.append("('");
+ sqlStringBuilder.append(moduleReference.getModuleName());
+ sqlStringBuilder.append("', '");
+ sqlStringBuilder.append(moduleReference.getRevision());
+ sqlStringBuilder.append("'),");
+ }
+
+ // replace last ',' with ';'
+ sqlStringBuilder.replace(sqlStringBuilder.length() - 1, sqlStringBuilder.length(), ";");
+
+ entityManager.createNativeQuery(sqlStringBuilder.toString()).executeUpdate();
+ }
+
+ private Collection<ModuleReference> identifyNewModuleReferencesForCmHandle(final String tempTableName) {
+ final String sql = String.format(
+ "SELECT %1$s.module_name, %1$s.revision"
+ + " FROM %1$s LEFT JOIN yang_resource"
+ + " ON yang_resource.module_name=%1$s.module_name"
+ + " AND yang_resource.revision=%1$s.revision"
+ + " WHERE yang_resource.module_name IS NULL;", tempTableName);
+
+ final List<Object[]> resultsAsObjects =
+ entityManager.createNativeQuery(sql).getResultList();
+
+ final List<ModuleReference> resultsAsModuleReferences = new ArrayList<>(resultsAsObjects.size());
+ for (final Object[] row : resultsAsObjects) {
+ resultsAsModuleReferences.add(new ModuleReference((String) row[0], (String) row[1]));
+ }
+
+ return resultsAsModuleReferences;
+ }
+}
/*
* ============LICENSE_START=======================================================
* Copyright (C) 2020 Pantheon.tech
- * Modifications Copyright (C) Nordix Foundation
+ * Modifications Copyright (C) 2021-2022 Nordix Foundation
* ================================================================================
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
/*
* ============LICENSE_START=======================================================
- * Copyright (C) 2021 Nordix Foundation
+ * Copyright (C) 2021-2022 Nordix Foundation
* Modifications Copyright (C) 2021-2022 Bell Canada.
* ================================================================================
* Licensed under the Apache License, Version 2.0 (the 'License');
import org.onap.cps.spi.entities.YangResourceEntity
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.ModuleReferenceRepository
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
static final String SET_DATA = '/data/schemaset.sql'
static final String EXISTING_SCHEMA_SET_NAME = SCHEMA_SET_NAME1
static final String SCHEMA_SET_NAME_NO_ANCHORS = 'SCHEMA-SET-100'
- static final String SCHEMA_SET_NAME_WITH_ANCHORS_AND_DATA = 'SCHEMA-SET-101'
static final String SCHEMA_SET_NAME_NEW = 'SCHEMA-SET-NEW'
static final String NEW_RESOURCE_NAME = 'some new resource'
.revision(NEW_RESOURCE_REVISION).build()
def newYangResourcesNameToContentMap = [(NEW_RESOURCE_NAME):NEW_RESOURCE_CONTENT]
- 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() {
schemaSetRepository.findByDataspaceAndName(dataspaceEntity, SCHEMA_SET_NAME_NO_ANCHORS).isPresent() == false
}
+ @Sql([CLEAR_DATA, SET_DATA])
+ def 'Identifying new module references where #scenario'() {
+ when: 'identifyNewModuleReferences is called'
+ def result = objectUnderTest.identifyNewModuleReferences(moduleReferences)
+ then: 'the correct module reference collection is returned'
+ assert result == expectedResult
+ where: 'the following data is used'
+ scenario | moduleReferences || expectedResult
+ 'new module references exist' | toModuleReference([['some module 1' : 'some revision 1'], ['some module 2' : 'some revision 2']]) || toModuleReference([['some module 1' : 'some revision 1'], ['some module 2' : 'some revision 2']])
+ 'no new module references exist' | [] || []
+ 'module references collection is null'| null || []
+ }
+
@Sql([CLEAR_DATA, SET_DATA])
def 'Delete schema set error scenario: #scenario.'() {
when: 'attempt to delete a schema set where #scenario'
yangResourceEntity.revision == expectedYangResourceRevision
}
+ def toModuleReference(moduleReferenceAsMap) {
+ def moduleReferences = [].withDefault { [:] }
+ moduleReferenceAsMap.forEach(property ->
+ property.forEach((moduleName, revision) -> {
+ moduleReferences.add(new ModuleReference('moduleName' : moduleName, 'revision' : revision))
+ }))
+ return moduleReferences
+ }
+
}
/*
* ============LICENSE_START=======================================================
* Copyright (c) 2021 Bell Canada.
+ * Modifications Copyright (C) 2022 Nordix Foundation
* ================================================================================
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
package org.onap.cps.spi.impl
import org.hibernate.exception.ConstraintViolationException
+import org.onap.cps.spi.CpsAdminPersistenceService
import org.onap.cps.spi.CpsModulePersistenceService
import org.onap.cps.spi.exceptions.DuplicatedYangResourceException
import org.onap.cps.spi.repository.DataspaceRepository
+import org.onap.cps.spi.repository.ModuleReferenceRepository
import org.onap.cps.spi.repository.SchemaSetRepository
import org.onap.cps.spi.repository.YangResourceRepository
import org.springframework.dao.DataIntegrityViolationException
def dataspaceRepositoryMock = Mock(DataspaceRepository)
def yangResourceRepositoryMock = Mock(YangResourceRepository)
def schemaSetRepositoryMock = Mock(SchemaSetRepository)
+ def cpsAdminPersistenceServiceMock = Mock(CpsAdminPersistenceService)
+ def moduleReferenceRepositoryMock = Mock(ModuleReferenceRepository)
// Constants
def yangResourceName = 'my-yang-resource-name'
anotherIntegrityException = new DataIntegrityViolationException("another integrity exception")
def setup() {
- objectUnderTest = new CpsModulePersistenceServiceImpl()
- objectUnderTest.dataspaceRepository = dataspaceRepositoryMock
- objectUnderTest.yangResourceRepository = yangResourceRepositoryMock
- objectUnderTest.schemaSetRepository = schemaSetRepositoryMock
+ objectUnderTest = new CpsModulePersistenceServiceImpl(yangResourceRepositoryMock, schemaSetRepositoryMock,
+ dataspaceRepositoryMock, cpsAdminPersistenceServiceMock, moduleReferenceRepositoryMock)
}
def 'Store schema set error scenario: #scenario.'() {
<parent>\r
<groupId>org.onap.cps</groupId>\r
<artifactId>cps-parent</artifactId>\r
- <version>2.1.0-SNAPSHOT</version>\r
+ <version>3.0.0-SNAPSHOT</version>\r
<relativePath>../cps-parent/pom.xml</relativePath>\r
</parent>\r
\r
/*
* ============LICENSE_START=======================================================
- * Copyright (C) 2020 Nordix Foundation
+ * Copyright (C) 2020-2022 Nordix Foundation
* Modifications Copyright (C) 2020-2021 Pantheon.tech
* ================================================================================
* Licensed under the Apache License, Version 2.0 (the "License");
package org.onap.cps.api;
import java.util.Collection;
-import java.util.List;
import java.util.Map;
import org.checkerframework.checker.nullness.qual.NonNull;
import org.onap.cps.spi.CascadeDeleteAllowed;
/**
* Create a schema set from new modules and existing modules.
- *
- * @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 moduleReferences List of YANG resources module references of the modules
- * needed for this handle that are already in CPS
+ * @param dataspaceName Dataspace name
+ * @param schemaSetName schema set name
+ * @param newModuleNameToContentMap YANG resources map where key is a module name and value is content
+ * @param moduleReferences List of YANG resources module references of the modules
*/
void createSchemaSetFromModules(@NonNull String dataspaceName, @NonNull String schemaSetName,
- @NonNull Map<String, String> newYangResourcesModuleNameToContentMap,
- @NonNull List<ModuleReference> moduleReferences);
+ @NonNull Map<String, String> newModuleNameToContentMap,
+ Collection<ModuleReference> moduleReferences);
/**
* Read schema set in the given dataspace.
* @return a list of ModuleReference objects
*/
Collection<ModuleReference> getYangResourcesModuleReferences(String dataspaceName, String anchorName);
+
+ /**
+ * Identify previously unknown Yang Resource module references.
+ *
+ * @param moduleReferencesToCheck the moduleReferencesToCheck
+ * @returns collection of module references
+ */
+ Collection<ModuleReference> identifyNewModuleReferences(
+ Collection<ModuleReference> moduleReferencesToCheck);
+
}
/*
* ============LICENSE_START=======================================================
- * Copyright (C) 2020-2021 Nordix Foundation
+ * Copyright (C) 2020-2022 Nordix Foundation
* Modifications Copyright (C) 2020-2021 Pantheon.tech
* Modifications Copyright (C) 2022 Bell Canada
* ================================================================================
package org.onap.cps.api.impl;
import java.util.Collection;
-import java.util.List;
import java.util.Map;
import lombok.AllArgsConstructor;
import org.onap.cps.api.CpsAdminService;
@Override
public void createSchemaSetFromModules(final String dataspaceName, final String schemaSetName,
- final Map<String, String> newYangResourcesModuleNameToContentMap,
- final List<ModuleReference> moduleReferences) {
+ final Map<String, String> newModuleNameToContentMap,
+ final Collection<ModuleReference> moduleReferences) {
cpsModulePersistenceService.storeSchemaSetFromModules(dataspaceName, schemaSetName,
- newYangResourcesModuleNameToContentMap, moduleReferences);
+ newModuleNameToContentMap, moduleReferences);
}
private boolean isCascadeDeleteProhibited(final CascadeDeleteAllowed cascadeDeleteAllowed) {
return CascadeDeleteAllowed.CASCADE_DELETE_PROHIBITED == cascadeDeleteAllowed;
}
+
+ @Override
+ public Collection<ModuleReference> identifyNewModuleReferences(
+ final Collection<ModuleReference> moduleReferencesToCheck) {
+ return cpsModulePersistenceService.identifyNewModuleReferences(moduleReferencesToCheck);
+ }
+
}
/*
* ============LICENSE_START=======================================================
- * Copyright (C) 2020 Nordix Foundation
+ * Copyright (C) 2020-2022 Nordix Foundation
* Modifications Copyright (C) 2020-2022 Bell Canada.
* ================================================================================
* Licensed under the Apache License, Version 2.0 (the "License");
package org.onap.cps.spi;
import java.util.Collection;
-import java.util.List;
import java.util.Map;
import org.onap.cps.spi.model.ModuleReference;
*
* @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 newModuleNameToContentMap YANG resources map where key is a module name and value is content
* @param moduleReferences List of YANG resources module references
*/
void storeSchemaSetFromModules(String dataspaceName, String schemaSetName,
- Map<String, String> newYangResourcesModuleNameToContentMap, List<ModuleReference> moduleReferences);
+ Map<String, String> newModuleNameToContentMap, Collection<ModuleReference> moduleReferences);
/**
* Deletes Schema Set.
* Remove unused Yang Resource Modules.
*/
void deleteUnusedYangResourceModules();
+
+ /**
+ * Identify new module references from those returned by a node compared to what is in CPS already.
+ *
+ * @param moduleReferencesToCheck the module references ot check
+ * @returns Collection of {@link ModuleReference} of previously unknown module references
+ */
+ Collection<ModuleReference> identifyNewModuleReferences(
+ Collection<ModuleReference> moduleReferencesToCheck);
+
}
/*
* ============LICENSE_START=======================================================
- * Copyright (C) 2020-2021 Nordix Foundation
+ * Copyright (C) 2020-2022 Nordix Foundation
* Modifications Copyright (C) 2020-2021 Pantheon.tech
* Modifications Copyright (C) 2020-2022 Bell Canada.
* ================================================================================
class CpsModuleServiceImplSpec extends Specification {
- def mockModuleStoreService = Mock(CpsModulePersistenceService)
+ def mockCpsModulePersistenceService = Mock(CpsModulePersistenceService)
def mockCpsAdminService = Mock(CpsAdminService)
def mockYangTextSchemaSourceSetCache = Mock(YangTextSchemaSourceSetCache)
- def objectUnderTest = new CpsModuleServiceImpl(mockModuleStoreService, mockYangTextSchemaSourceSetCache, mockCpsAdminService)
+ def objectUnderTest = new CpsModuleServiceImpl(mockCpsModulePersistenceService, mockYangTextSchemaSourceSetCache, mockCpsAdminService)
def 'Create schema set.'() {
given: 'Valid yang resource as name-to-content map'
when: 'Create schema set method is invoked'
objectUnderTest.createSchemaSet('someDataspace', 'someSchemaSet', yangResourcesNameToContentMap)
then: 'Parameters are validated and processing is delegated to persistence service'
- 1 * mockModuleStoreService.storeSchemaSet('someDataspace', 'someSchemaSet', yangResourcesNameToContentMap)
+ 1 * mockCpsModulePersistenceService.storeSchemaSet('someDataspace', 'someSchemaSet', yangResourcesNameToContentMap)
}
def 'Create schema set from new modules and existing modules.'() {
when: 'create schema set from modules method is invoked'
objectUnderTest.createSchemaSetFromModules("someDataspaceName", "someSchemaSetName", [newModule: "newContent"], listOfExistingModulesModuleReference)
then: 'processing is delegated to persistence service'
- 1 * mockModuleStoreService.storeSchemaSetFromModules("someDataspaceName", "someSchemaSetName", [newModule: "newContent"], listOfExistingModulesModuleReference)
-
+ 1 * mockCpsModulePersistenceService.storeSchemaSetFromModules("someDataspaceName", "someSchemaSetName", [newModule: "newContent"], listOfExistingModulesModuleReference)
}
def 'Create schema set from invalid resources'() {
then: 'anchor deletion is called #numberOfAnchors times'
numberOfAnchors * mockCpsAdminService.deleteAnchor('my-dataspace', _)
and: 'persistence service method is invoked with same parameters'
- 1 * mockModuleStoreService.deleteSchemaSet('my-dataspace', 'my-schemaset')
+ 1 * mockCpsModulePersistenceService.deleteSchemaSet('my-dataspace', 'my-schemaset')
and: 'schema set will be removed from the cache'
1 * mockYangTextSchemaSourceSetCache.removeFromCache('my-dataspace', 'my-schemaset')
and: 'orphan yang resources are deleted'
- 1 * mockModuleStoreService.deleteUnusedYangResourceModules()
+ 1 * mockCpsModulePersistenceService.deleteUnusedYangResourceModules()
where: 'following parameters are used'
numberOfAnchors << [0, 3]
}
then: 'no anchors are deleted'
0 * mockCpsAdminService.deleteAnchor(_, _)
and: 'persistence service method is invoked with same parameters'
- 1 * mockModuleStoreService.deleteSchemaSet('my-dataspace', 'my-schemaset')
+ 1 * mockCpsModulePersistenceService.deleteSchemaSet('my-dataspace', 'my-schemaset')
and: 'schema set will be removed from the cache'
1 * mockYangTextSchemaSourceSetCache.removeFromCache('my-dataspace', 'my-schemaset')
and: 'orphan yang resources are deleted'
- 1 * mockModuleStoreService.deleteUnusedYangResourceModules()
+ 1 * mockCpsModulePersistenceService.deleteUnusedYangResourceModules()
}
def 'Delete schema-set when cascade is prohibited and schema-set has anchors.'() {
def 'Get all yang resources module references.'() {
given: 'an already present module reference'
def moduleReferences = [new ExtendedModuleReference()]
- mockModuleStoreService.getYangResourceModuleReferences('someDataspaceName') >> moduleReferences
+ mockCpsModulePersistenceService.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.getYangResourceModuleReferences('someDataspaceName', 'someAnchorName') >> moduleReferences
+ mockCpsModulePersistenceService.getYangResourceModuleReferences('someDataspaceName', 'someAnchorName') >> moduleReferences
expect: 'the list provided by persistence service is returned as result'
objectUnderTest.getYangResourcesModuleReferences('someDataspaceName', 'someAnchorName') == moduleReferences
}
+
+ def 'Identifying new module references'(){
+ given: 'module references from cm handle'
+ def moduleReferencesToCheck = [new ModuleReference('some-module', 'some-revision')]
+ when: 'identifyNewModuleReferences is called'
+ objectUnderTest.identifyNewModuleReferences(moduleReferencesToCheck)
+ then: 'cps module persistence service is called with module references to check'
+ 1 * mockCpsModulePersistenceService.identifyNewModuleReferences(moduleReferencesToCheck);
+ }
}
${ncmpInventoryBasePath} /ncmpInventory
${ncmpBasePath} /ncmp
${dmiUrl} http://${DMI_HOST}:${DMI_PORT}
-${jsonData} {"dmiPlugin":"${dmiUrl}","dmiDataPlugin":null,"dmiModelPlugin":null,"createdCmHandles":[{"cmHandle":"PNFDemo","cmHandleProperties":{"Book1":"Sci-Fi Book"},"publicCmHandleProperties":{"Contact":"storeemail@bookstore.com"}}],"updatedCmHandles":[{"cmHandle":"PNFDemo","cmHandleProperties":{"Book1":"Romance Book"},"publicCmHandleProperties":{"Contact":"newemailforstore@bookstore.com"}}]}
+${jsonData} {"dmiPlugin":"${dmiUrl}","dmiDataPlugin":"","dmiModelPlugin":"","createdCmHandles":[{"cmHandle":"PNFDemo","cmHandleProperties":{"Book1":"Sci-Fi Book"},"publicCmHandleProperties":{"Contact":"storeemail@bookstore.com"}}],"updatedCmHandles":[{"cmHandle":"PNFDemo","cmHandleProperties":{"Book1":"Romance Book"},"publicCmHandleProperties":{"Contact":"newemailforstore@bookstore.com"}}]}
*** Test Cases ***
Register node, update data node and sync modules.
${uri}= Set Variable ${ncmpInventoryBasePath}/v1/ch
${headers}= Create Dictionary Content-Type=application/json Authorization=${auth}
${response}= POST On Session CPS_URL ${uri} headers=${headers} data=${jsonData}
- Should Be Equal As Strings ${response.status_code} 201
+ Should Be Equal As Strings ${response.status_code} 204
Get modules for registered data node
${uri}= Set Variable ${ncmpBasePath}/v1/ch/PNFDemo/modules
$ref: '#/components/schemas/RestDmiPluginRegistration'
required: true
responses:
- "201":
- description: Created
+ "204":
+ description: No Content
content: {}
"400":
description: Bad Request
.. This work is licensed under a Creative Commons Attribution 4.0 International License.
.. http://creativecommons.org/licenses/by/4.0
-.. Copyright (C) 2021 Nordix Foundation
+.. Copyright (C) 2021-2022 Nordix Foundation
.. DO NOT CHANGE THIS LABEL FOR RELEASE NOTES - EVEN THOUGH IT GIVES A WARNING
.. _release_notes:
.. * * * JAKARTA * * *
.. ========================
-Version: 2.1.0-SNAPSHOT
+Version: 3.0.0-SNAPSHOT
=======================
This section lists the main changes & fixes merged into master (snapshot) version of CPS-NCMP. This information is here to assist developers that want experiment/test using our latest code bases directly. Stability of this is not guaranteed.
- `CPS-741 <https://jira.onap.org/browse/CPS-741>`_ Re sync after removing cm handles
- `CPS-777 <https://jira.onap.org/browse/CPS-777>`_ Ensure all DMI operations use POST method
- `CPS-780 <https://jira.onap.org/browse/CPS-780>`_ Add examples for parameters, request and response in openapi yaml for cps-core
+ - `CPS-837 <https://jira.onap.org/browse/CPS-837>`_ Add Remove and Update properties (DMI and Public) as part of CM Handle Registration update
Bug Fixes
---------
- `CPS-788 <https://jira.onap.org/browse/CPS-788>`_ Yang Resource formatting is incorrect
- `CPS-783 <https://jira.onap.org/browse/CPS-783>`_ Remove cm handle does not completely remove all cm handle information
- `CPS-841 <https://jira.onap.org/browse/CPS-841>`_ Upgrade log4j to 2.17.1 as recommended by ONAP SECCOM
- - `CPS-867 <https://jira.onap.org/browse/CPS-867>`_ Database port made configurable through env variable DB_PORT
- `CPS-856 <https://jira.onap.org/browse/CPS-856>`_ Retry mechanism not working for concurrent CmHandle registration
+ - `CPS-867 <https://jira.onap.org/browse/CPS-867>`_ Database port made configurable through env variable DB_PORT
+ - `CPS-887 <https://jira.onap.org/browse/CPS-887>`_ Increase performance of cmHandle registration for large number of schema sets in DB
+ - `CPS-892 <https://jira.onap.org/browse/CPS-892>`_ Fixed the response code during CM-Handle Registration from 201 CREATED to 204 NO_CONTENT
Known Limitations, Issues and Workarounds
-----------------------------------------
<parent>
<groupId>org.onap.cps</groupId>
<artifactId>cps-parent</artifactId>
- <version>2.1.0-SNAPSHOT</version>
+ <version>3.0.0-SNAPSHOT</version>
<relativePath>../cps-parent/pom.xml</relativePath>
</parent>
<modelVersion>4.0.0</modelVersion>
\r
<groupId>org.onap.cps</groupId>\r
<artifactId>cps-aggregator</artifactId>\r
- <version>2.1.0-SNAPSHOT</version>\r
+ <version>3.0.0-SNAPSHOT</version>\r
<packaging>pom</packaging>\r
\r
<name>cps</name>\r
<modelVersion>4.0.0</modelVersion>
<groupId>org.onap.cps</groupId>
<artifactId>spotbugs</artifactId>
- <version>2.1.0-SNAPSHOT</version>
+ <version>3.0.0-SNAPSHOT</version>
<properties>
<nexusproxy>https://nexus.onap.org</nexusproxy>
# ============LICENSE_START=======================================================
# Copyright (C) 2021 Nordix Foundation
+# Modifications Copyright (C) 2022 Bell Canada.
# ================================================================================
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# 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=2
-minor=1
+major=3
+minor=0
patch=0
base_version=${major}.${minor}.${patch}