nullValuePropertyMappingStrategy = NullValuePropertyMappingStrategy.SET_TO_DEFAULT)
DmiPluginRegistration toDmiPluginRegistration(final RestDmiPluginRegistration restDmiPluginRegistration);
- @Mapping(source = "cmHandle", target = "cmHandleID")
+ @Mapping(source = "cmHandle", target = "cmHandleId")
@Mapping(source = "cmHandleProperties", target = "dmiProperties")
@Mapping(source = "publicCmHandleProperties", target = "publicProperties")
NcmpServiceCmHandle toNcmpServiceCmHandle(final RestInputCmHandle restInputCmHandle);
private RestOutputCmHandle toRestOutputCmHandle(final NcmpServiceCmHandle ncmpServiceCmHandle) {
final RestOutputCmHandle restOutputCmHandle = new RestOutputCmHandle();
final CmHandlePublicProperties cmHandlePublicProperties = new CmHandlePublicProperties();
- restOutputCmHandle.setCmHandle(ncmpServiceCmHandle.getCmHandleID());
+ restOutputCmHandle.setCmHandle(ncmpServiceCmHandle.getCmHandleId());
cmHandlePublicProperties.add(ncmpServiceCmHandle.getPublicProperties());
restOutputCmHandle.setPublicCmHandleProperties(cmHandlePublicProperties);
return restOutputCmHandle;
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'
+ result.createdCmHandles[0].cmHandleId == 'example-id'
and: '(empty) properties are converted correctly'
result.createdCmHandles[0].dmiProperties == expectedDmiProperties
result.createdCmHandles[0].publicProperties == expectedPublicProperties
def cmHandleId = 'Some-Cm-Handle'
def dmiProperties = [ prop:'some DMI property' ]
def publicProperties = [ "public prop":'some public property' ]
- def ncmpServiceCmHandle = new NcmpServiceCmHandle(cmHandleID: cmHandleId, dmiProperties: dmiProperties, publicProperties: publicProperties)
+ def ncmpServiceCmHandle = new NcmpServiceCmHandle(cmHandleId: cmHandleId, dmiProperties: dmiProperties, publicProperties: publicProperties)
and: 'the service method is invoked with the cm handle id'
1 * mockNetworkCmProxyDataService.getNcmpServiceCmHandle('Some-Cm-Handle') >> ncmpServiceCmHandle
when: 'the cm handle details api is invoked'
yangModelCmHandleRetriever.getDmiServiceNamesAndProperties(cmHandleId);
final List<YangModelCmHandle.Property> dmiProperties = yangModelCmHandle.getDmiProperties();
final List<YangModelCmHandle.Property> publicProperties = yangModelCmHandle.getPublicProperties();
- ncmpServiceCmHandle.setCmHandleID(yangModelCmHandle.getId());
+ ncmpServiceCmHandle.setCmHandleId(yangModelCmHandle.getId());
setDmiProperties(dmiProperties, ncmpServiceCmHandle);
setPublicProperties(publicProperties, ncmpServiceCmHandle);
return ncmpServiceCmHandle;
}
- private void setDmiProperties(final List<YangModelCmHandle.Property> dmiProperties,
- final NcmpServiceCmHandle ncmpServiceCmHandle) {
- final Map<String, String> dmiPropertiesMap = new LinkedHashMap<>(dmiProperties.size());
- asPropertiesMap(dmiProperties, dmiPropertiesMap);
- ncmpServiceCmHandle.setDmiProperties(dmiPropertiesMap);
- }
-
- private void setPublicProperties(final List<YangModelCmHandle.Property> publicProperties,
- final NcmpServiceCmHandle ncmpServiceCmHandle) {
- final Map<String, String> publicPropertiesMap = new LinkedHashMap<>();
- asPropertiesMap(publicProperties, publicPropertiesMap);
- ncmpServiceCmHandle.setPublicProperties(publicPropertiesMap);
- }
-
- private void asPropertiesMap(final List<YangModelCmHandle.Property> properties,
- final Map<String, String> propertiesMap) {
- for (final YangModelCmHandle.Property property: properties) {
- propertiesMap.put(property.getName(), property.getValue());
- }
- }
-
/**
* THis method registers a cm handle and initiates modules sync.
*
*/
public List<CmHandleRegistrationResponse> parseAndCreateCmHandlesInDmiRegistrationAndSyncModules(
final DmiPluginRegistration dmiPluginRegistration) {
- return dmiPluginRegistration.getCreatedCmHandles().stream()
- .map(cmHandle ->
- YangModelCmHandle.toYangModelCmHandle(
- dmiPluginRegistration.getDmiPlugin(),
- dmiPluginRegistration.getDmiDataPlugin(),
- dmiPluginRegistration.getDmiModelPlugin(), cmHandle)
- )
- .map(this::registerAndSyncNewCmHandle)
- .collect(Collectors.toList());
- }
-
- private static Object handleResponse(final ResponseEntity<?> responseEntity, final OperationEnum operation) {
- if (responseEntity.getStatusCode().is2xxSuccessful()) {
- return responseEntity.getBody();
- } else {
- final String exceptionMessage = "Unable to " + operation.toString() + " resource data.";
- throw new HttpClientRequestException(exceptionMessage, (String) responseEntity.getBody(),
- responseEntity.getStatusCodeValue());
- }
- }
-
- private CmHandleRegistrationResponse registerAndSyncNewCmHandle(final YangModelCmHandle yangModelCmHandle) {
+ List<CmHandleRegistrationResponse> cmHandleRegistrationResponses = new ArrayList<>();
try {
- CpsValidator.validateNameCharacters(yangModelCmHandle.getId());
- final String cmHandleJsonData = String.format("{\"cm-handles\":[%s]}",
- jsonObjectMapper.asJsonString(yangModelCmHandle));
- cpsDataService.saveListElements(NCMP_DATASPACE_NAME, NCMP_DMI_REGISTRY_ANCHOR, NCMP_DMI_REGISTRY_PARENT,
- cmHandleJsonData, NO_TIMESTAMP);
- syncModulesAndCreateAnchor(yangModelCmHandle);
- return CmHandleRegistrationResponse.createSuccessResponse(yangModelCmHandle.getId());
- } catch (final AlreadyDefinedException alreadyDefinedException) {
- return CmHandleRegistrationResponse.createFailureResponse(
- yangModelCmHandle.getId(), RegistrationError.CM_HANDLE_ALREADY_EXIST);
+ cmHandleRegistrationResponses = dmiPluginRegistration.getCreatedCmHandles().stream()
+ .map(cmHandle ->
+ YangModelCmHandle.toYangModelCmHandle(
+ dmiPluginRegistration.getDmiPlugin(),
+ dmiPluginRegistration.getDmiDataPlugin(),
+ dmiPluginRegistration.getDmiModelPlugin(), cmHandle)
+ )
+ .map(this::registerAndSyncNewCmHandle)
+ .collect(Collectors.toList());
} catch (final DataValidationException dataValidationException) {
- return CmHandleRegistrationResponse.createFailureResponse(yangModelCmHandle.getId(),
- RegistrationError.CM_HANDLE_INVALID_ID);
- } catch (final Exception exception) {
- return CmHandleRegistrationResponse.createFailureResponse(yangModelCmHandle.getId(), exception);
+ cmHandleRegistrationResponses.add(CmHandleRegistrationResponse.createFailureResponse(dmiPluginRegistration
+ .getCreatedCmHandles().stream()
+ .map(NcmpServiceCmHandle::getCmHandleId).findFirst().orElse(null),
+ RegistrationError.CM_HANDLE_INVALID_ID));
}
+ return cmHandleRegistrationResponses;
}
protected void syncModulesAndCreateAnchor(final YangModelCmHandle yangModelCmHandle) {
cmHandleId, resourceIdentifier, optionsParamInQuery, dataStore, requestId, topicParamInQuery);
return handleResponse(responseEntity, OperationEnum.READ);
}
+
+ private void setDmiProperties(final List<YangModelCmHandle.Property> dmiProperties,
+ final NcmpServiceCmHandle ncmpServiceCmHandle) {
+ final Map<String, String> dmiPropertiesMap = new LinkedHashMap<>(dmiProperties.size());
+ asPropertiesMap(dmiProperties, dmiPropertiesMap);
+ ncmpServiceCmHandle.setDmiProperties(dmiPropertiesMap);
+ }
+
+ private void setPublicProperties(final List<YangModelCmHandle.Property> publicProperties,
+ final NcmpServiceCmHandle ncmpServiceCmHandle) {
+ final Map<String, String> publicPropertiesMap = new LinkedHashMap<>();
+ asPropertiesMap(publicProperties, publicPropertiesMap);
+ ncmpServiceCmHandle.setPublicProperties(publicPropertiesMap);
+ }
+
+ private void asPropertiesMap(final List<YangModelCmHandle.Property> properties,
+ final Map<String, String> propertiesMap) {
+ for (final YangModelCmHandle.Property property: properties) {
+ propertiesMap.put(property.getName(), property.getValue());
+ }
+ }
+
+
+ private CmHandleRegistrationResponse registerAndSyncNewCmHandle(final YangModelCmHandle yangModelCmHandle) {
+ try {
+ final String cmHandleJsonData = String.format("{\"cm-handles\":[%s]}",
+ jsonObjectMapper.asJsonString(yangModelCmHandle));
+ cpsDataService.saveListElements(NCMP_DATASPACE_NAME, NCMP_DMI_REGISTRY_ANCHOR, NCMP_DMI_REGISTRY_PARENT,
+ cmHandleJsonData, NO_TIMESTAMP);
+ syncModulesAndCreateAnchor(yangModelCmHandle);
+ return CmHandleRegistrationResponse.createSuccessResponse(yangModelCmHandle.getId());
+ } catch (final AlreadyDefinedException alreadyDefinedException) {
+ return CmHandleRegistrationResponse.createFailureResponse(
+ yangModelCmHandle.getId(), RegistrationError.CM_HANDLE_ALREADY_EXIST);
+ } catch (final Exception exception) {
+ return CmHandleRegistrationResponse.createFailureResponse(yangModelCmHandle.getId(), exception);
+ }
+ }
+
+ private static Object handleResponse(final ResponseEntity<?> responseEntity, final OperationEnum operation) {
+ if (responseEntity.getStatusCode().is2xxSuccessful()) {
+ return responseEntity.getBody();
+ } else {
+ final String exceptionMessage = "Unable to " + operation.toString() + " resource data.";
+ throw new HttpClientRequestException(exceptionMessage, (String) responseEntity.getBody(),
+ responseEntity.getStatusCodeValue());
+ }
+ }
+
}
\ No newline at end of file
final Collection<NcmpServiceCmHandle> ncmpServiceCmHandles) {
final List<CmHandleRegistrationResponse> cmHandleRegistrationResponses = new ArrayList<>();
for (final NcmpServiceCmHandle ncmpServiceCmHandle : ncmpServiceCmHandles) {
- final String cmHandle = ncmpServiceCmHandle.getCmHandleID();
+ final String cmHandle = ncmpServiceCmHandle.getCmHandleId();
try {
CpsValidator.validateNameCharacters(cmHandle);
final String cmHandleXpath = String.format(CM_HANDLE_XPATH_TEMPLATE, cmHandle);
import org.onap.cps.ncmp.api.impl.config.NcmpConfiguration;
import org.onap.cps.ncmp.api.impl.utils.DmiServiceUrlBuilder;
import org.onap.cps.ncmp.api.impl.yangmodels.YangModelCmHandle;
+import org.onap.cps.utils.CpsValidator;
import org.onap.cps.utils.JsonObjectMapper;
import org.springframework.http.ResponseEntity;
import org.springframework.stereotype.Component;
final DataStoreEnum dataStore,
final String requestId,
final String topicParamInQuery) {
+ CpsValidator.validateNameCharacters(cmHandleId);
final YangModelCmHandle yangModelCmHandle =
yangModelCmHandleRetriever.getDmiServiceNamesAndProperties(cmHandleId);
final DmiRequestBody dmiRequestBody = DmiRequestBody.builder()
.build();
dmiRequestBody.asDmiProperties(yangModelCmHandle.getDmiProperties());
final String jsonBody = jsonObjectMapper.asJsonString(dmiRequestBody);
- final var dmiResourceDataUrl = dmiServiceUrlBuilder.getDmiDatastoreUrl(
+ final String dmiResourceDataUrl = dmiServiceUrlBuilder.getDmiDatastoreUrl(
dmiServiceUrlBuilder.populateQueryParams(resourceId, optionsParamInQuery,
topicParamInQuery), dmiServiceUrlBuilder.populateUriVariables(
yangModelCmHandle, cmHandleId, dataStore));
final OperationEnum operation,
final String requestData,
final String dataType) {
+ CpsValidator.validateNameCharacters(cmHandleId);
final YangModelCmHandle yangModelCmHandle =
yangModelCmHandleRetriever.getDmiServiceNamesAndProperties(cmHandleId);
final DmiRequestBody dmiRequestBody = DmiRequestBody.builder()
dmiRequestBody.asDmiProperties(yangModelCmHandle.getDmiProperties());
final String jsonBody = jsonObjectMapper.asJsonString(dmiRequestBody);
final String dmiUrl =
- dmiServiceUrlBuilder.getDmiDatastoreUrl(dmiServiceUrlBuilder.populateQueryParams(resourceId,
- null, null),
- dmiServiceUrlBuilder.populateUriVariables(yangModelCmHandle, cmHandleId, PASSTHROUGH_RUNNING));
+ dmiServiceUrlBuilder.getDmiDatastoreUrl(dmiServiceUrlBuilder.populateQueryParams(resourceId,
+ null, null),
+ dmiServiceUrlBuilder.populateUriVariables(yangModelCmHandle, cmHandleId, PASSTHROUGH_RUNNING));
return dmiRestClient.postOperationWithJsonData(dmiUrl, jsonBody);
}
import org.onap.cps.ncmp.api.models.NcmpServiceCmHandle;
import org.onap.cps.spi.FetchDescendantsOption;
import org.onap.cps.spi.model.DataNode;
+import org.onap.cps.utils.CpsValidator;
import org.springframework.stereotype.Component;
/**
* @return yang model cm handle
*/
public YangModelCmHandle getDmiServiceNamesAndProperties(final String cmHandleId) {
+ CpsValidator.validateNameCharacters(cmHandleId);
final DataNode cmHandleDataNode = getCmHandleDataNode(cmHandleId);
final NcmpServiceCmHandle ncmpServiceCmHandle = new NcmpServiceCmHandle();
- ncmpServiceCmHandle.setCmHandleID(cmHandleId);
+ ncmpServiceCmHandle.setCmHandleId(cmHandleId);
populateCmHandleProperties(cmHandleDataNode, ncmpServiceCmHandle);
return YangModelCmHandle.toYangModelCmHandle(
String.valueOf(cmHandleDataNode.getLeaves().get("dmi-service-name")),
import org.onap.cps.ncmp.api.impl.config.NcmpConfiguration;
import org.onap.cps.ncmp.api.impl.operations.DmiOperations;
import org.onap.cps.ncmp.api.impl.yangmodels.YangModelCmHandle;
+import org.onap.cps.utils.CpsValidator;
import org.springframework.stereotype.Component;
import org.springframework.util.LinkedMultiValueMap;
import org.springframework.util.MultiValueMap;
.pathSegment("{dmiBasePath}")
.pathSegment("v1")
.pathSegment("ch")
- .pathSegment("{cmHandle}");
+ .pathSegment("{cmHandleId}");
}
/**
* This method populates uri variables.
*
* @param yangModelCmHandle get dmi service name
- * @param cmHandle cm handle name for dmi registration
+ * @param cmHandleId cm handle id for dmi registration
* @return {@code String} dmi service url as string
*/
public Map<String, Object> populateUriVariables(final YangModelCmHandle yangModelCmHandle,
- final String cmHandle,
+ final String cmHandleId,
final DmiOperations.DataStoreEnum dataStore) {
+ CpsValidator.validateNameCharacters(cmHandleId);
final Map<String, Object> uriVariables = new HashMap<>();
final String dmiBasePath = dmiProperties.getDmiBasePath();
uriVariables.put("dmiServiceName",
yangModelCmHandle.resolveDmiServiceName(DATA));
uriVariables.put("dmiBasePath", dmiBasePath);
- uriVariables.put("cmHandle", cmHandle);
+ uriVariables.put("cmHandleId", cmHandleId);
uriVariables.put("dataStore", dataStore.getValue());
return uriVariables;
}
import lombok.Setter;
import org.onap.cps.ncmp.api.impl.operations.RequiredDmiService;
import org.onap.cps.ncmp.api.models.NcmpServiceCmHandle;
+import org.onap.cps.utils.CpsValidator;
/**
* Cm Handle which follows the Yang resource dmi registry model when persisting data to DMI or the DB.
final String dmiDataServiceName,
final String dmiModelServiceName,
final NcmpServiceCmHandle ncmpServiceCmHandle) {
+ CpsValidator.validateNameCharacters(ncmpServiceCmHandle.getCmHandleId());
final YangModelCmHandle yangModelCmHandle = new YangModelCmHandle();
- yangModelCmHandle.setId(ncmpServiceCmHandle.getCmHandleID());
+ yangModelCmHandle.setId(ncmpServiceCmHandle.getCmHandleId());
yangModelCmHandle.setDmiServiceName(dmiServiceName);
yangModelCmHandle.setDmiDataServiceName(dmiDataServiceName);
yangModelCmHandle.setDmiModelServiceName(dmiModelServiceName);
@NoArgsConstructor
public class NcmpServiceCmHandle {
- private String cmHandleID;
+ private String cmHandleId;
@JsonSetter(nulls = Nulls.AS_EMPTY)
private Map<String, String> dmiProperties = Collections.emptyMap();
given: 'a cm handle'
def ncmpServiceCmHandle = new NcmpServiceCmHandle()
def dmiServiceName = 'some service name'
- ncmpServiceCmHandle.cmHandleID = 'cm handle id 1'
+ ncmpServiceCmHandle.cmHandleId = 'cm-handle-id-1'
def yangModelCmHandle = YangModelCmHandle.toYangModelCmHandle(dmiServiceName, '' , '', ncmpServiceCmHandle)
and: 'DMI operations returns some module references'
def moduleReferences = [ new ModuleReference(moduleName:'module1',revision:'1'),
class NetworkCmProxyDataServiceImplRegistrationSpec extends Specification {
@Shared
- def ncmpServiceCmHandle = new NcmpServiceCmHandle()
+ def ncmpServiceCmHandle = new NcmpServiceCmHandle(cmHandleId: 'some-cm-handle-id')
@Shared
def cmHandlesArray = ['cmHandle001']
def 'DMI Registration: Create, Update & Delete operations are processed in the right order'() {
given: 'a registration with operations of all three types'
def dmiRegistration = new DmiPluginRegistration(dmiPlugin: 'my-server')
- dmiRegistration.setCreatedCmHandles([new NcmpServiceCmHandle(cmHandleID: 'cmhandle-1', publicProperties: ['publicProp1': 'value'], dmiProperties: [:])])
- dmiRegistration.setUpdatedCmHandles([new NcmpServiceCmHandle(cmHandleID: 'cmhandle-2', publicProperties: ['publicProp1': 'value'], dmiProperties: [:])])
+ dmiRegistration.setCreatedCmHandles([new NcmpServiceCmHandle(cmHandleId: 'cmhandle-1', publicProperties: ['publicProp1': 'value'], dmiProperties: [:])])
+ dmiRegistration.setUpdatedCmHandles([new NcmpServiceCmHandle(cmHandleId: 'cmhandle-2', publicProperties: ['publicProp1': 'value'], dmiProperties: [:])])
dmiRegistration.setRemovedCmHandles(['cmhandle-2'])
when: 'registration is processed'
objectUnderTest.updateDmiRegistrationAndSyncModule(dmiRegistration)
def 'DMI Registration: Response from all operations types are in response'() {
given: 'a registration with operations of all three types'
def dmiRegistration = new DmiPluginRegistration(dmiPlugin: 'my-server')
- dmiRegistration.setCreatedCmHandles([new NcmpServiceCmHandle(cmHandleID: 'cmhandle-1', publicProperties: ['publicProp1': 'value'], dmiProperties: [:])])
- dmiRegistration.setUpdatedCmHandles([new NcmpServiceCmHandle(cmHandleID: 'cmhandle-2', publicProperties: ['publicProp1': 'value'], dmiProperties: [:])])
+ dmiRegistration.setCreatedCmHandles([new NcmpServiceCmHandle(cmHandleId: 'cmhandle-1', publicProperties: ['publicProp1': 'value'], dmiProperties: [:])])
+ dmiRegistration.setUpdatedCmHandles([new NcmpServiceCmHandle(cmHandleId: 'cmhandle-2', publicProperties: ['publicProp1': 'value'], dmiProperties: [:])])
dmiRegistration.setRemovedCmHandles(['cmhandle-2'])
and: 'update cm-handles can be processed successfully'
def updateResponses = [CmHandleRegistrationResponse.createSuccessResponse('cmhandle-2')]
def 'Create CM-Handle Successfully: #scenario.'() {
given: 'a registration without cm-handle properties'
def dmiPluginRegistration = new DmiPluginRegistration(dmiPlugin: 'my-server')
- dmiPluginRegistration.createdCmHandles = [new NcmpServiceCmHandle(cmHandleID: 'cmhandle', dmiProperties: dmiProperties, publicProperties: publicProperties)]
+ dmiPluginRegistration.createdCmHandles = [new NcmpServiceCmHandle(cmHandleId: 'cmhandle', dmiProperties: dmiProperties, publicProperties: publicProperties)]
when: 'registration is updated'
def response = objectUnderTest.updateDmiRegistrationAndSyncModule(dmiPluginRegistration)
then: 'a successful response is received'
def 'Create CM-Handle Multiple Requests: All cm-handles creation requests are processed'() {
given: 'a registration with three cm-handles to be created'
def dmiPluginRegistration = new DmiPluginRegistration(dmiPlugin: 'my-server',
- createdCmHandles: [new NcmpServiceCmHandle(cmHandleID: 'cmhandle1'),
- new NcmpServiceCmHandle(cmHandleID: 'cmhandle2'),
- new NcmpServiceCmHandle(cmHandleID: 'cmhandle3')])
+ createdCmHandles: [new NcmpServiceCmHandle(cmHandleId: 'cmhandle1'),
+ new NcmpServiceCmHandle(cmHandleId: 'cmhandle2'),
+ new NcmpServiceCmHandle(cmHandleId: 'cmhandle3')])
and: 'cm-handle creation is successful for 1st and 3rd; failed for 2nd'
mockCpsDataService.saveListElements(_, _, _, _, _) >> {} >> { throw new RuntimeException("Failed") } >> {}
when: 'registration is updated to create cm-handles'
def 'Create CM-Handle Error Handling: Registration fails: #scenario'() {
given: 'a registration without cm-handle properties'
def dmiPluginRegistration = new DmiPluginRegistration(dmiPlugin: 'my-server')
- dmiPluginRegistration.createdCmHandles = [new NcmpServiceCmHandle(cmHandleID: cmHandleId)]
+ dmiPluginRegistration.createdCmHandles = [new NcmpServiceCmHandle(cmHandleId: cmHandleId)]
and: 'cm-handler registration fails: #scenario'
mockCpsDataService.saveListElements(_, _, _, _, _) >> { throw exception }
when: 'registration is updated'
def objectUnderTest = getObjectUnderTest()
and: 'a registration without cm-handle properties'
def dmiPluginRegistration = new DmiPluginRegistration(dmiPlugin: 'my-server')
- dmiPluginRegistration.createdCmHandles = [new NcmpServiceCmHandle(cmHandleID: 'cmhandle')]
+ dmiPluginRegistration.createdCmHandles = [new NcmpServiceCmHandle(cmHandleId: 'cmhandle')]
and: 'cm-handler models sync fails'
objectUnderTest.syncModulesAndCreateAnchor(*_) >> { throw new RuntimeException('Model-Sync failed') }
when: 'registration is updated'
import org.onap.cps.ncmp.api.impl.exception.HttpClientRequestException
import org.onap.cps.ncmp.api.impl.operations.YangModelCmHandleRetriever
import org.onap.cps.ncmp.api.impl.yangmodels.YangModelCmHandle
+import org.onap.cps.spi.exceptions.DataValidationException
import spock.lang.Shared
import static org.onap.cps.ncmp.api.impl.operations.DmiOperations.DataStoreEnum.PASSTHROUGH_OPERATIONAL
>> { new ResponseEntity<>(HttpStatus.CREATED) }
}
+ def 'Write resource data for pass-through running from DMI using an invalid id.'() {
+ when: 'write resource data is called'
+ objectUnderTest.writeResourceDataPassThroughRunningForCmHandle('invalid cm handle name',
+ 'testResourceId', CREATE,
+ '{some-json}', 'application/json')
+ then: 'exception is thrown'
+ thrown(DataValidationException.class)
+ and: 'DMI is not invoked'
+ 0 * mockDmiDataOperations.writeResourceDataPassThroughRunningFromDmi(_, _, _, _, _)
+ }
+
def 'Write resource data for pass-through running from DMI using POST "not found" response (from DMI).'() {
given: 'cpsDataService returns valid dataNode'
mockCpsDataService.getDataNode('NCMP-Admin', 'ncmp-dmi-registry',
response == 'dmi-response'
}
+ def 'Get resource data for pass-through operational from DMI with invalid name.'() {\
+ when: 'get resource data operational for cm-handle is called'
+ objectUnderTest.getResourceDataOperationalForCmHandle('invalid test cm handle',
+ 'testResourceId',
+ OPTIONS_PARAM,
+ NO_TOPIC,
+ NO_REQUEST_ID)
+ then: 'A data validation Exception is thrown'
+ thrown(DataValidationException)
+ }
+
def 'Get resource data for pass-through operational from DMI with Json Processing Exception.'() {
given: 'cps data service returns valid data node'
mockCpsDataService.getDataNode('NCMP-Admin', 'ncmp-dmi-registry',
response == '{dmi-response}'
}
+ def 'Get resource data for pass-through running from DMI with invalid name.'() {
+ when: 'get resource data operational for cm-handle is called'
+ objectUnderTest.getResourceDataPassThroughRunningForCmHandle('invalid test cm handle',
+ 'testResourceId',
+ OPTIONS_PARAM,
+ NO_TOPIC,
+ NO_REQUEST_ID)
+ then: 'A data validation Exception is thrown'
+ thrown(DataValidationException)
+ }
+
def 'Get resource data for pass-through running from DMI return NOK response.'() {
given: 'cpsDataService returns valid dataNode'
mockCpsDataService.getDataNode('NCMP-Admin', 'ncmp-dmi-registry',
1 * mockCpsModuleService.getYangResourcesModuleReferences('NFP-Operational','some-cm-handle')
}
+ def 'Getting Yang Resources with an invalid #scenario.'() {
+ when: 'yang resources is called'
+ objectUnderTest.getYangResourcesModuleReferences('invalid cm handle with spaces')
+ then: 'a data validation exception is thrown'
+ thrown(DataValidationException)
+ and: 'CPS module services is not invoked'
+ 0 * mockCpsModuleService.getYangResourcesModuleReferences(_, _)
+ }
+
def 'Get cm handle identifiers for the given module names.'() {
when: 'execute a cm handle search for the given module names'
objectUnderTest.executeCmHandleHasAllModulesSearch(['some-module-name'])
when: 'getting cm handle details for a given cm handle id from ncmp service'
def result = objectUnderTest.getNcmpServiceCmHandle('Some-Cm-Handle')
then: 'the result returns the correct data'
- result.cmHandleID == 'Some-Cm-Handle'
+ result.cmHandleId == 'Some-Cm-Handle'
result.dmiProperties ==[ Book:'Romance Novel' ]
result.publicProperties == [ "Public Book":'Public Romance Novel' ]
}
+ def 'Get a cm handle with an invalid id.'() {
+ when: 'getting cm handle details for a given cm handle id with an invalid name'
+ objectUnderTest.getNcmpServiceCmHandle('invalid cm handle with spaces')
+ then: 'an exception is thrown'
+ thrown(DataValidationException)
+ and: 'the yang model cm handle retriever is not invoked'
+ 0 * mockYangModelCmHandleRetriever.getDmiServiceNamesAndProperties(_)
+ }
+
def 'Update resource data for pass-through running from dmi using POST #scenario DMI properties.'() {
given: 'cpsDataService returns valid datanode'
mockCpsDataService.getDataNode('NCMP-Admin', 'ncmp-dmi-registry',
import static org.onap.cps.ncmp.api.models.CmHandleRegistrationResponse.Status
import org.onap.cps.api.CpsDataService
-import org.onap.cps.ncmp.api.models.CmHandleRegistrationResponse
import org.onap.cps.ncmp.api.models.NcmpServiceCmHandle
import org.onap.cps.spi.FetchDescendantsOption
import org.onap.cps.spi.exceptions.DataNodeNotFoundException
given: 'the CPS service return a CM handle'
mockCpsDataService.getDataNode(dataspaceName, anchorName, cmHandleXpath, FetchDescendantsOption.INCLUDE_ALL_DESCENDANTS) >> cmHandleDataNode
and: 'an update cm handle request with public properties updates'
- def cmHandleUpdateRequest = [new NcmpServiceCmHandle(cmHandleID: cmHandleId, publicProperties: updatedPublicProperties)]
+ def cmHandleUpdateRequest = [new NcmpServiceCmHandle(cmHandleId: cmHandleId, publicProperties: updatedPublicProperties)]
when: 'update data node leaves is called with the update request'
objectUnderTest.updateCmHandleProperties(cmHandleUpdateRequest)
then: 'the replace list method is called with correct params'
given: 'the CPS service return a CM handle'
mockCpsDataService.getDataNode(dataspaceName, anchorName, cmHandleXpath, FetchDescendantsOption.INCLUDE_ALL_DESCENDANTS) >> cmHandleDataNode
and: 'an update cm handle request with DMI properties updates'
- def cmHandleUpdateRequest = [new NcmpServiceCmHandle(cmHandleID: cmHandleId, dmiProperties: updatedDmiProperties)]
+ def cmHandleUpdateRequest = [new NcmpServiceCmHandle(cmHandleId: cmHandleId, dmiProperties: updatedDmiProperties)]
when: 'update data node leaves is called with the update request'
objectUnderTest.updateCmHandleProperties(cmHandleUpdateRequest)
then: 'replace list method should is called with correct params'
def cmHandleDataNode = new DataNode(xpath: cmHandleXpath, childDataNodes: originalPropertyDataNodes)
mockCpsDataService.getDataNode(dataspaceName, anchorName, cmHandleXpath, FetchDescendantsOption.INCLUDE_ALL_DESCENDANTS) >> cmHandleDataNode
and: 'an update cm handle request that removes all public properties(existing and non-existing)'
- def cmHandleUpdateRequest = [new NcmpServiceCmHandle(cmHandleID: cmHandleId, publicProperties: ['publicProp3': null, 'publicProp4': null])]
+ def cmHandleUpdateRequest = [new NcmpServiceCmHandle(cmHandleId: cmHandleId, publicProperties: ['publicProp3': null, 'publicProp4': null])]
when: 'update data node leaves is called with the update request'
objectUnderTest.updateCmHandleProperties(cmHandleUpdateRequest)
then: 'the replace list method is not called'
def '#scenario error leads to #exception when we try to update cmHandle'() {
given: 'cm handles request'
- def cmHandleUpdateRequest = [new NcmpServiceCmHandle(cmHandleID: cmHandleId, publicProperties: [:], dmiProperties: [:])]
+ def cmHandleUpdateRequest = [new NcmpServiceCmHandle(cmHandleId: cmHandleId, publicProperties: [:], dmiProperties: [:])]
and: 'data node cannot be found'
mockCpsDataService.getDataNode(*_) >> { throw exception }
when: 'update data node leaves is called using correct parameters'
def 'Multiple update operations in a single request'() {
given: 'cm handles request'
- def cmHandleUpdateRequest = [new NcmpServiceCmHandle(cmHandleID: cmHandleId, publicProperties: ['publicProp1': "value"], dmiProperties: [:]),
- new NcmpServiceCmHandle(cmHandleID: cmHandleId, publicProperties: ['publicProp1': "value"], dmiProperties: [:]),
- new NcmpServiceCmHandle(cmHandleID: cmHandleId, publicProperties: ['publicProp1': "value"], dmiProperties: [:])]
+ def cmHandleUpdateRequest = [new NcmpServiceCmHandle(cmHandleId: cmHandleId, publicProperties: ['publicProp1': "value"], dmiProperties: [:]),
+ new NcmpServiceCmHandle(cmHandleId: cmHandleId, publicProperties: ['publicProp1': "value"], dmiProperties: [:]),
+ new NcmpServiceCmHandle(cmHandleId: cmHandleId, publicProperties: ['publicProp1': "value"], dmiProperties: [:])]
and: 'data node can be found for 1st and 3rd cm-handle but not for 2nd cm-handle'
mockCpsDataService.getDataNode(*_) >> cmHandleDataNode >> { throw new DataNodeNotFoundException('NCMP-Admin', 'ncmp-dmi-registry') } >> cmHandleDataNode
when: 'update data node leaves is called using correct parameters'
def yangModelCmHandle = new YangModelCmHandle()
def static dmiServiceName = 'some service name'
- def static cmHandleId = 'some cm handle'
+ def static cmHandleId = 'some-cm-handle'
def static resourceIdentifier = 'parent/child'
def mockYangModelCmHandleRetrieval(dmiProperties) {
import org.onap.cps.api.CpsDataService
import org.onap.cps.ncmp.api.impl.yangmodels.YangModelCmHandle
+import org.onap.cps.spi.exceptions.DataValidationException
import spock.lang.Shared
import static org.onap.cps.spi.FetchDescendantsOption.INCLUDE_ALL_DESCENDANTS
def objectUnderTest = new YangModelCmHandleRetriever(mockCpsDataService)
- def cmHandleId = 'some cm handle'
+ def cmHandleId = 'some-cm-handle'
def leaves = ["dmi-service-name":"common service name","dmi-data-service-name":"data service name","dmi-model-service-name":"model service name"]
- def xpath = "/dmi-registry/cm-handles[@id='some cm handle']"
+ def xpath = "/dmi-registry/cm-handles[@id='some-cm-handle']"
@Shared
def childDataNodesForCmHandleWithAllProperties = [new DataNode(xpath: "/dmi-registry/cm-handles[@id='some cm handle']/additional-properties[@name='name1']", leaves: ["name":"name1", "value":"value1"]),
new DataNode(xpath: "/dmi-registry/cm-handles[@id='some cm handle']/public-properties[@name='name2']", leaves: ["name":"name2","value":"value2"])]
@Shared
- def childDataNodesForCmHandleWithDMIProperties = [new DataNode(xpath: "/dmi-registry/cm-handles[@id='some cm handle']/additional-properties[@name='name1']", leaves: ["name":"name1", "value":"value1"])]
+ def childDataNodesForCmHandleWithDMIProperties = [new DataNode(xpath: "/dmi-registry/cm-handles[@id='some-cm-handle']/additional-properties[@name='name1']", leaves: ["name":"name1", "value":"value1"])]
@Shared
- def childDataNodesForCmHandleWithPublicProperties = [new DataNode(xpath: "/dmi-registry/cm-handles[@id='some cm handle']/public-properties[@name='name2']", leaves: ["name":"name2","value":"value2"])]
+ def childDataNodesForCmHandleWithPublicProperties = [new DataNode(xpath: "/dmi-registry/cm-handles[@id='some-cm-handle']/public-properties[@name='name2']", leaves: ["name":"name2","value":"value2"])]
def "Retrieve CmHandle using datanode with #scenario."() {
given: 'the cps data service returns a data node from the DMI registry'
'just DMI properties' | childDataNodesForCmHandleWithDMIProperties || [new YangModelCmHandle.Property("name1", "value1")] || []
'just public properties' | childDataNodesForCmHandleWithPublicProperties || [] || [new YangModelCmHandle.Property("name2", "value2")]
}
+
+ def "Retrieve CmHandle using datanode with invalid CmHandle id."() {
+ when: 'retrieving the yang modelled cm handle with an invalid id'
+ def result = objectUnderTest.getDmiServiceNamesAndProperties('cm handle id with spaces')
+ then: 'a data validation exception is thrown'
+ thrown(DataValidationException)
+ and: 'the result is not returned'
+ result == null
+ }
}
def 'Creating yang model cm handle from a service api cm handle.'() {
given: 'a cm handle with properties'
def ncmpServiceCmHandle = new NcmpServiceCmHandle()
+ ncmpServiceCmHandle.cmHandleId = 'cm-handle-id01'
ncmpServiceCmHandle.dmiProperties = [myDmiProperty:'value1']
ncmpServiceCmHandle.publicProperties = [myPublicProperty:'value2']
when: 'it is converted to a yang model cm handle'
def 'Resolve DMI service name: #scenario and #requiredService service require.'() {
given: 'a yang model cm handle'
- def objectUnderTest = YangModelCmHandle.toYangModelCmHandle(dmiServiceName, dmiDataServiceName, dmiModelServiceName, new NcmpServiceCmHandle())
+ def objectUnderTest = YangModelCmHandle.toYangModelCmHandle(dmiServiceName, dmiDataServiceName, dmiModelServiceName, new NcmpServiceCmHandle(cmHandleId: 'cm-handle-id-1'))
expect:
assert objectUnderTest.resolveDmiServiceName(requiredService) == expectedService
where:
class DmiServiceUrlBuilderSpec extends Specification {
@Shared
- YangModelCmHandle yangModelCmHandle = YangModelCmHandle.toYangModelCmHandle("dmiServiceName",
- "dmiDataServiceName", "dmiModuleServiceName", new NcmpServiceCmHandle())
+ YangModelCmHandle yangModelCmHandle = YangModelCmHandle.toYangModelCmHandle('dmiServiceName',
+ 'dmiDataServiceName', 'dmiModuleServiceName', new NcmpServiceCmHandle(cmHandleId: 'some-cm-handle-id'))
- NcmpConfiguration.DmiProperties dmiProperties = new NcmpConfiguration.DmiProperties();
+ NcmpConfiguration.DmiProperties dmiProperties = new NcmpConfiguration.DmiProperties()
def objectUnderTest = new DmiServiceUrlBuilder(dmiProperties)
def 'Create the dmi service url with #scenario.'() {
given: 'uri variables'
- dmiProperties.dmiBasePath = 'dmi';
+ dmiProperties.dmiBasePath = 'dmi'
def uriVars = objectUnderTest.populateUriVariables(yangModelCmHandle,
- "cmHandle", PASSTHROUGH_RUNNING);
+ "cmHandle", PASSTHROUGH_RUNNING)
and: 'query params'
def uriQueries = objectUnderTest.populateQueryParams(resourceId,
- 'optionsParamInQuery', topicParamInQuery);
+ 'optionsParamInQuery', topicParamInQuery)
when: 'a dmi datastore service url is generated'
def dmiServiceUrl = objectUnderTest.getDmiDatastoreUrl(uriQueries, uriVars)
then: 'service url is generated as expected'
def 'Populate dmi data store url #scenario.'() {
given: 'uri variables are created'
- dmiProperties.dmiBasePath = dmiBasePath;
+ dmiProperties.dmiBasePath = dmiBasePath
def uriVars = objectUnderTest.populateUriVariables(yangModelCmHandle,
- "cmHandle", PASSTHROUGH_RUNNING);
+ "cmHandle", PASSTHROUGH_RUNNING)
and: 'null query params'
def uriQueries = objectUnderTest.populateQueryParams(null,
- null, null);
+ null, null)
when: 'a dmi datastore service url is generated'
def dmiServiceUrl = objectUnderTest.getDmiDatastoreUrl(uriQueries, uriVars)
then: 'the created dmi service url matches the expected'
@Sql(CLEAR_DATA)
def 'Create and retrieve a new dataspace.'() {
when: 'a new dataspace is created'
- def dataspaceName = 'some new dataspace'
+ def dataspaceName = 'some-new-dataspace'
objectUnderTest.createDataspace(dataspaceName)
then: 'that dataspace can be retrieved from the dataspace repository'
def dataspaceEntity = dataspaceRepository.findByName(dataspaceName).orElseThrow()
@Sql([CLEAR_DATA, SET_DATA])
def 'Create and retrieve a new anchor.'() {
when: 'a new anchor is created'
- def newAnchorName = 'my new anchor'
+ def newAnchorName = 'my-new-anchor'
objectUnderTest.createAnchor(DATASPACE_NAME, SCHEMA_SET_NAME1, newAnchorName)
then: 'that anchor can be retrieved'
def anchor = objectUnderTest.getAnchor(DATASPACE_NAME, newAnchorName)
@Sql(CLEAR_DATA)
def 'Get all anchors in unknown dataspace.'() {
when: 'attempt to get all anchors in an unknown dataspace'
- objectUnderTest.getAnchors('unknown dataspace')
+ objectUnderTest.getAnchors('unknown-dataspace')
then: 'an DataspaceNotFoundException is thrown'
thrown(DataspaceNotFoundException)
}
final String parentNodeXpath,
final String dataNodeUpdatesAsJson,
final OffsetDateTime observedTimestamp) {
+ CpsValidator.validateNameCharacters(dataspaceName, anchorName);
final Collection<DataNode> dataNodeUpdates =
buildDataNodes(dataspaceName, anchorName,
parentNodeXpath, dataNodeUpdatesAsJson);
- CpsValidator.validateNameCharacters(dataspaceName, anchorName);
for (final DataNode dataNodeUpdate : dataNodeUpdates) {
processDataNodeUpdate(dataspaceName, anchorName, dataNodeUpdate);
}
@Override
public Collection<ModuleReference> getYangResourcesModuleReferences(final String dataspaceName,
final String anchorName) {
- CpsValidator.validateNameCharacters(dataspaceName);
+ CpsValidator.validateNameCharacters(dataspaceName, anchorName);
return cpsModulePersistenceService.getYangResourceModuleReferences(dataspaceName, anchorName);
}
- 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);
}
+ private boolean isCascadeDeleteProhibited(final CascadeDeleteAllowed cascadeDeleteAllowed) {
+ return CascadeDeleteAllowed.CASCADE_DELETE_PROHIBITED == cascadeDeleteAllowed;
+ }
+
}
import org.onap.cps.api.CpsDataService
import org.onap.cps.spi.CpsAdminPersistenceService
+import org.onap.cps.spi.exceptions.DataValidationException
import org.onap.cps.spi.model.Anchor
import org.onap.cps.spi.model.CmHandleQueryParameters
import spock.lang.Specification
1 * mockCpsAdminPersistenceService.createDataspace('someDataspace')
}
+ def 'Create a dataspace with an invalid dataspace name.'() {
+ when: 'create dataspace method is invoked with incorrectly named dataspace'
+ objectUnderTest.createDataspace('Dataspace Name with spaces')
+ then: 'a data validation exception is thrown'
+ thrown(DataValidationException)
+ and: 'the persistence service method is not invoked'
+ 0 * mockCpsAdminPersistenceService.createDataspace(_)
+ }
+
def 'Create anchor method invokes persistence service.'() {
when: 'create anchor method is invoked'
objectUnderTest.createAnchor('someDataspace', 'someSchemaSet', 'someAnchorName')
1 * mockCpsAdminPersistenceService.createAnchor('someDataspace', 'someSchemaSet', 'someAnchorName')
}
+ def 'Create an anchor with an invalid anchor name.'() {
+ when: 'create anchor method is invoked with incorrectly named dataspace'
+ objectUnderTest.createAnchor('someDataspace', 'someSchemaSet', 'Anchor Name With Spaces')
+ then: 'a data validation exception is thrown'
+ thrown(DataValidationException)
+ and: 'the persistence service method is not invoked'
+ 0 * mockCpsAdminPersistenceService.createAnchor(_, _, _)
+ }
+
def 'Retrieve all anchors for dataspace.'() {
given: 'that anchor is associated with the dataspace'
def anchors = [new Anchor()]
objectUnderTest.getAnchors('someDataspace') == anchors
}
+ def 'Retrieve all anchors with an invalid dataspace name.'() {
+ when: 'get anchors is invoked with an invalid dataspace name'
+ objectUnderTest.getAnchors('Dataspace name with spaces')
+ then: 'a data validation exception is thrown'
+ thrown(DataValidationException)
+ and: 'cps admin persistence get anchors is not invoked'
+ 0 * mockCpsAdminPersistenceService.getAnchors(_)
+ }
+
def 'Retrieve all anchors for schema-set.'() {
given: 'that anchor is associated with the dataspace and schemaset'
def anchors = [new Anchor()]
expect: 'the collection provided by persistence service is returned as result'
objectUnderTest.getAnchors('someDataspace', 'someSchemaSet') == anchors
}
+ def 'Retrieve all anchors for schema-set with invalid #scenario.'() {
+ when: 'the collection provided by persistence service is returned as result'
+ objectUnderTest.getAnchors(dataspaceName, schemaSetName)
+ then: 'a data validation exception is thrown'
+ thrown(DataValidationException)
+ and: 'cps admin persistence get anchors is not invoked'
+ 0 * mockCpsAdminPersistenceService.getAnchors(_, _)
+ where: 'the following parameters are used'
+ scenario | dataspaceName | schemaSetName
+ 'dataspace name' | 'dataspace names with spaces' | 'schemaSetName'
+ 'schema set name' | 'dataspaceName' | 'schema set name with spaces'
+ 'dataspace and schema set name' | 'dataspace name with spaces' | 'schema set name with spaces'
+ }
+
def 'Retrieve anchor for dataspace and provided anchor name.'() {
given: 'that anchor name is associated with the dataspace'
assert objectUnderTest.getAnchor('someDataspace','someAnchor') == anchor
}
+ def 'Retrieve anchor with invalid #scenario.'() {
+ when: 'get anchors is invoked with an invalid dataspace name'
+ objectUnderTest.getAnchor(dataspaceName, anchorName)
+ then: 'a data validation exception is thrown'
+ thrown(DataValidationException)
+ and: 'cps admin persistence get anchor is not invoked'
+ 0 * mockCpsAdminPersistenceService.getAnchor(_, _)
+ where: 'the following parameters are used'
+ scenario | dataspaceName | anchorName
+ 'dataspace name' | 'dataspace names with spaces' | 'anchorName'
+ 'anchor name' | 'dataspaceName' | 'anchor name with spaces'
+ 'dataspace and anchor name' | 'dataspace name with spaces' | 'anchor name with spaces'
+ }
+
def 'Delete anchor.'() {
when: 'delete anchor is invoked'
objectUnderTest.deleteAnchor('someDataspace','someAnchor')
1 * mockCpsAdminPersistenceService.deleteAnchor('someDataspace','someAnchor')
}
+ def 'Delete anchor with invalid #scenario.'() {
+ when: 'delete anchor is invoked'
+ objectUnderTest.deleteAnchor(dataspaceName, anchorName)
+ then: 'a data validation exception is thrown'
+ thrown(DataValidationException)
+ and: 'delete data nodes is invoked on the data service with expected parameters'
+ 0 * mockCpsDataService.deleteDataNodes(_,_, _ as OffsetDateTime )
+ and: 'the persistence service method is invoked with same parameters to delete anchor'
+ 0 * mockCpsAdminPersistenceService.deleteAnchor(_,_)
+ where: 'the following parameters are used'
+ scenario | dataspaceName | anchorName
+ 'dataspace name' | 'dataspace names with spaces' | 'anchorName'
+ 'anchor name' | 'dataspaceName' | 'anchor name with spaces'
+ 'dataspace and anchor name' | 'dataspace name with spaces' | 'anchor name with spaces'
+ }
+
def 'Query all anchor identifiers for a dataspace and module names.'() {
given: 'the persistence service is invoked with the expected parameters and returns a list of anchors'
mockCpsAdminPersistenceService.queryAnchors('some-dataspace-name', ['some-module-name']) >> [new Anchor(name:'some-anchor-identifier')]
}
+ def 'Query all anchor identifiers for a dataspace and module names with an invalid dataspace name.'() {
+ when: 'delete anchor is invoked'
+ objectUnderTest.queryAnchorNames('some dataspace name', _ as Collection<String>)
+ then: 'a data validation exception is thrown'
+ thrown(DataValidationException)
+ and: 'delete data nodes is not invoked'
+ 0 * mockCpsAdminPersistenceService.queryAnchors(_, _)
+ }
+
def 'Delete dataspace.'() {
when: 'delete dataspace is invoked'
objectUnderTest.deleteDataspace('someDataspace')
1 * mockCpsAdminPersistenceService.queryCmHandles(cmHandleQueryParameters)
}
+ def 'Delete dataspace with invalid dataspace id.'() {
+ when: 'delete dataspace is invoked'
+ objectUnderTest.deleteDataspace('some dataspace name')
+ then: 'a data validation exception is thrown'
+ thrown(DataValidationException)
+ and: 'associated persistence service method is not invoked'
+ 0 * mockCpsAdminPersistenceService.deleteDataspace(_)
+ }
+
}
import org.onap.cps.spi.FetchDescendantsOption
import org.onap.cps.spi.exceptions.DataValidationException
import org.onap.cps.spi.model.Anchor
+import org.onap.cps.spi.model.DataNode
import org.onap.cps.spi.model.DataNodeBuilder
import org.onap.cps.yang.YangTextSchemaSourceSet
import org.onap.cps.yang.YangTextSchemaSourceSetBuilder
1 * mockNotificationService.processDataUpdatedEvent(anchor, observedTimestamp, '/', Operation.CREATE)
}
+ def 'Saving json data with invalid #scenario.'() {
+ when: 'save data method is invoked with invalid #scenario'
+ objectUnderTest.saveData(dataspaceName, anchorName, _ as String, observedTimestamp)
+ then: 'a data validation exception is thrown'
+ thrown(DataValidationException)
+ and: 'the persistence service method is not invoked'
+ 0 * mockCpsDataPersistenceService.storeDataNode(_, _, _)
+ and: 'data updated event is not sent to notification service'
+ 0 * mockNotificationService.processDataUpdatedEvent(_, _, _, _)
+ where: 'the following parameters are used'
+ scenario | dataspaceName | anchorName
+ 'dataspace name' | 'dataspace names with spaces' | 'anchorName'
+ 'anchor name' | 'dataspaceName' | 'anchor name with spaces'
+ 'dataspace and anchor name' | 'dataspace name with spaces' | 'anchor name with spaces'
+ }
+
def 'Saving child data fragment under existing node.'() {
given: 'schema set for given anchor and dataspace references test-tree model'
setupSchemaSetMocks('test-tree.yang')
1 * mockNotificationService.processDataUpdatedEvent(anchor, observedTimestamp, '/test-tree', Operation.CREATE)
}
+ def 'Saving child data fragment under existing node with invalid #scenario.'() {
+ when: 'save data method is invoked with test-tree and an invalid #scenario'
+ objectUnderTest.saveData(dataspaceName, anchorName, '/test-tree', _ as String, observedTimestamp)
+ then: 'a data validation exception is thrown'
+ thrown(DataValidationException)
+ and: 'the persistence service method is not invoked'
+ 0 * mockCpsDataPersistenceService.addChildDataNode(_, _, _,_)
+ and: 'data updated event is not sent to notification service'
+ 0 * mockNotificationService.processDataUpdatedEvent(_, _, _, _)
+ where: 'the following parameters are used'
+ scenario | dataspaceName | anchorName
+ 'dataspace name' | 'dataspace names with spaces' | 'anchorName'
+ 'anchor name' | 'dataspaceName' | 'anchor name with spaces'
+ 'dataspace and anchor name' | 'dataspace name with spaces' | 'anchor name with spaces'
+ }
+
def 'Saving list element data fragment under existing node.'() {
given: 'schema set for given anchor and dataspace references test-tree model'
setupSchemaSetMocks('test-tree.yang')
thrown(DataValidationException)
}
+ def 'Saving list element data fragment with invalid #scenario.'() {
+ when: 'save data method is invoked with an invalid #scenario'
+ objectUnderTest.saveListElements(dataspaceName, anchorName, '/test-tree', _ as String, observedTimestamp)
+ then: 'a data validation exception is thrown'
+ thrown(DataValidationException)
+ and: 'add list elements persistence method is not invoked'
+ 0 * mockCpsDataPersistenceService.addListElements(_, _, _, _)
+ where: 'the following parameters are used'
+ scenario | dataspaceName | anchorName
+ 'dataspace name' | 'dataspace names with spaces' | 'anchorName'
+ 'anchor name' | 'dataspaceName' | 'anchor name with spaces'
+ 'dataspace and anchor name' | 'dataspace name with spaces' | 'anchor name with spaces'
+ }
+
def 'Get data node with option #fetchDescendantsOption.'() {
def xpath = '/xpath'
def dataNode = new DataNodeBuilder().withXpath(xpath).build()
fetchDescendantsOption << FetchDescendantsOption.values()
}
+ def 'Get data node with option invalid #scenario.'() {
+ when: 'get data node is invoked with #scenario'
+ objectUnderTest.getDataNode(dataspaceName, anchorName, '/test-tree', FetchDescendantsOption.INCLUDE_ALL_DESCENDANTS)
+ then: 'a data validation exception is thrown'
+ thrown(DataValidationException)
+ and: 'get data node persistence service is not invoked'
+ 0 * mockCpsDataPersistenceService.getDataNode(_, _, _, _)
+ where: 'the following parameters are used'
+ scenario | dataspaceName | anchorName
+ 'dataspace name' | 'dataspace names with spaces' | 'anchorName'
+ 'anchor name' | 'dataspaceName' | 'anchor name with spaces'
+ 'dataspace and anchor name' | 'dataspace name with spaces' | 'anchor name with spaces'
+ }
+
def 'Update data node leaves: #scenario.'() {
given: 'schema set for given anchor and dataspace references test-tree model'
setupSchemaSetMocks('test-tree.yang')
'level 2 node' | '/test-tree' | '{"branch": [{"name":"Name"}]}' || '/test-tree/branch[@name=\'Name\']' | ['name': 'Name']
}
+ def 'Update data node with invalid #scenario.'() {
+ when: 'update data method is invoked with json data #jsonData and parent node xpath #parentNodeXpath'
+ objectUnderTest.updateNodeLeaves(dataspaceName, anchorName, '/', '{"test-tree": {"branch": []}}', observedTimestamp)
+ then: 'a data validation exception is thrown'
+ thrown(DataValidationException)
+ and: 'the persistence service method is not invoked'
+ 0 * mockCpsDataPersistenceService.updateDataLeaves(_, _, _, _)
+ and: 'data updated event is not sent to notification service'
+ 0 * mockNotificationService.processDataUpdatedEvent(_, _, _, _)
+ where: 'the following parameters are used'
+ scenario | dataspaceName | anchorName
+ 'dataspace name' | 'dataspace names with spaces' | 'anchorName'
+ 'anchor name' | 'dataspaceName' | 'anchor name with spaces'
+ 'dataspace and anchor name' | 'dataspace name with spaces' | 'anchor name with spaces'
+ }
+
def 'Update list-element data node with : #scenario.'() {
given: 'schema set for given anchor and dataspace references bookstore model'
setupSchemaSetMocks('bookstore.yang')
1 * mockNotificationService.processDataUpdatedEvent(anchor, observedTimestamp, '/bookstore', Operation.UPDATE)
}
+ def 'Update Bookstore node leaves with invalid #scenario' () {
+ when: 'update data method is invoked with an invalid #scenario'
+ objectUnderTest.updateNodeLeavesAndExistingDescendantLeaves(dataspaceName, anchorName,
+ '/bookstore', _ as String, observedTimestamp)
+ then: 'a data validation exception is thrown'
+ thrown(DataValidationException)
+ and: 'the persistence service method is not invoked'
+ 0 * mockCpsDataPersistenceService.updateDataLeaves(_, _, _, _)
+ and: 'the data updated event is not sent to the notification service'
+ 0 * mockNotificationService.processDataUpdatedEvent(_, _, _, _)
+ where: 'the following parameters are used'
+ scenario | dataspaceName | anchorName
+ 'dataspace name' | 'dataspace names with spaces' | 'anchorName'
+ 'anchor name' | 'dataspaceName' | 'anchor name with spaces'
+ 'dataspace and anchor name' | 'dataspace name with spaces' | 'anchor name with spaces'
+ }
+
+
def 'Replace data node: #scenario.'() {
given: 'schema set for given anchor and dataspace references test-tree model'
setupSchemaSetMocks('test-tree.yang')
'level 2 node' | '/test-tree' | '{"branch": [{"name":"Name"}]}' || '/test-tree/branch[@name=\'Name\']'
}
+ def 'Replace data node with invalid #scenario.'() {
+ when: 'replace data method is invoked with invalid #scenario'
+ objectUnderTest.replaceNodeTree(dataspaceName, anchorName, '/', _ as String, observedTimestamp)
+ then: 'a data validation exception is thrown'
+ thrown(DataValidationException)
+ and: 'the persistence service method is not invoked'
+ 0 * mockCpsDataPersistenceService.replaceDataNodeTree(_, _,_)
+ and: 'data updated event is not sent to notification service'
+ 0 * mockNotificationService.processDataUpdatedEvent(_, _, _, _)
+ where: 'the following parameters are used'
+ scenario | dataspaceName | anchorName
+ 'dataspace name' | 'dataspace names with spaces' | 'anchorName'
+ 'anchor name' | 'dataspaceName' | 'anchor name with spaces'
+ 'dataspace and anchor name' | 'dataspace name with spaces' | 'anchor name with spaces'
+ }
+
def 'Replace list content data fragment under parent node.'() {
given: 'schema set for given anchor and dataspace references test-tree model'
setupSchemaSetMocks('test-tree.yang')
thrown(DataValidationException)
}
+ def 'Replace whole list content with an invalid #scenario.'() {
+ when: 'replace list data method is invoked with invalid #scenario'
+ objectUnderTest.replaceListContent(dataspaceName, anchorName, '/test-tree', _ as Collection<DataNode>, observedTimestamp)
+ then: 'a data validation exception is thrown'
+ thrown(DataValidationException)
+ and: 'the persistence service method is not invoked'
+ 0 * mockCpsDataPersistenceService.replaceListContent(_, _,_)
+ and: 'data updated event is not sent to notification service'
+ 0 * mockNotificationService.processDataUpdatedEvent(_, _, _, _)
+ where: 'the following parameters are used'
+ scenario | dataspaceName | anchorName
+ 'dataspace name' | 'dataspace names with spaces' | 'anchorName'
+ 'anchor name' | 'dataspaceName' | 'anchor name with spaces'
+ 'dataspace and anchor name' | 'dataspace name with spaces' | 'anchor name with spaces'
+ }
+
def 'Delete list element under existing node.'() {
given: 'schema set for given anchor and dataspace references test-tree model'
setupSchemaSetMocks('test-tree.yang')
1 * mockNotificationService.processDataUpdatedEvent(anchor, observedTimestamp, '/test-tree/branch', Operation.DELETE)
}
+
+ def 'Delete list element with an invalid #scenario.'() {
+ when: 'delete list data method is invoked with with invalid #scenario'
+ objectUnderTest.deleteDataNode(dataspaceName, anchorName, '/data-node', observedTimestamp)
+ then: 'a data validation exception is thrown'
+ thrown(DataValidationException)
+ and: 'the persistence service method is not invoked'
+ 0 * mockCpsDataPersistenceService.deleteListDataNode(_, _, _)
+ and: 'data updated event is not sent to notification service'
+ 0 * mockNotificationService.processDataUpdatedEvent(_, _, _, _)
+ where: 'the following parameters are used'
+ scenario | dataspaceName | anchorName
+ 'dataspace name' | 'dataspace names with spaces' | 'anchorName'
+ 'anchor name' | 'dataspaceName' | 'anchor name with spaces'
+ 'dataspace and anchor name' | 'dataspace name with spaces' | 'anchor name with spaces'
+ }
+
def 'Delete data node under anchor and dataspace.'() {
given: 'schema set for given anchor and dataspace references test tree model'
setupSchemaSetMocks('test-tree.yang')
1 * mockNotificationService.processDataUpdatedEvent(anchor, observedTimestamp, '/data-node', Operation.DELETE)
}
+ def 'Delete data node with an invalid #scenario.'() {
+ when: 'delete data node method is invoked with invalid #scenario'
+ objectUnderTest.deleteDataNode(dataspaceName, anchorName, '/data-node', observedTimestamp)
+ then: 'a data validation exception is thrown'
+ thrown(DataValidationException)
+ and: 'the persistence service method is not invoked'
+ 0 * mockCpsDataPersistenceService.deleteDataNode(_, _, _)
+ and: 'data updated event is not sent to notification service'
+ 0 * mockNotificationService.processDataUpdatedEvent(_, _, _, _)
+ where: 'the following parameters are used'
+ scenario | dataspaceName | anchorName
+ 'dataspace name' | 'dataspace names with spaces' | 'anchorName'
+ 'anchor name' | 'dataspaceName' | 'anchor name with spaces'
+ 'dataspace and anchor name' | 'dataspace name with spaces' | 'anchor name with spaces'
+ }
+
def 'Delete all data nodes for a given anchor and dataspace.'() {
given: 'schema set for given anchor and dataspace references test tree model'
setupSchemaSetMocks('test-tree.yang')
import org.onap.cps.TestUtils
import org.onap.cps.api.CpsAdminService
+import org.onap.cps.spi.CascadeDeleteAllowed
import org.onap.cps.spi.CpsModulePersistenceService
+import org.onap.cps.spi.exceptions.DataValidationException
import org.onap.cps.spi.exceptions.ModelValidationException
import org.onap.cps.spi.exceptions.SchemaSetInUseException
import org.onap.cps.spi.model.Anchor
1 * mockCpsModulePersistenceService.storeSchemaSet('someDataspace', 'someSchemaSet', yangResourcesNameToContentMap)
}
+ def 'Create a schema set with an invalid #scenario.'() {
+ when: 'create dataspace method is invoked with incorrectly named dataspace'
+ objectUnderTest.createSchemaSet(dataspaceName, schemaSetName, _ as Map<String, String>)
+ then: 'a data validation exception is thrown'
+ thrown(DataValidationException)
+ and: 'the persistence service method is not invoked'
+ 0 * mockCpsModulePersistenceService.storeSchemaSet(_, _, _)
+ where: 'the following parameters are used'
+ scenario | dataspaceName | schemaSetName
+ 'dataspace name' | 'dataspace names with spaces' | 'schemaSetName'
+ 'schema set name name' | 'dataspaceName' | 'schema set name with spaces'
+ 'dataspace and schema set name' | 'dataspace name with spaces' | 'schema set name with spaces'
+ }
+
def 'Create schema set from new modules and existing modules.'() {
given: 'a list of existing modules module reference'
def moduleReferenceForExistingModule = new ModuleReference("test", "2021-10-12","test.org")
1 * mockCpsModulePersistenceService.storeSchemaSetFromModules("someDataspaceName", "someSchemaSetName", [newModule: "newContent"], listOfExistingModulesModuleReference)
}
+ def 'Create schema set from new modules and existing modules with invalid #scenario.'() {
+ when: 'create dataspace method is invoked with incorrectly named dataspace'
+ objectUnderTest.createSchemaSetFromModules(dataspaceName, schemaSetName, _ as Map<String, String>, _ as Collection<ModuleReference>)
+ then: 'a data validation exception is thrown'
+ thrown(DataValidationException)
+ and: 'the persistence service method is not invoked'
+ 0 * mockCpsModulePersistenceService.storeSchemaSetFromModules(_, _, _)
+ where: 'the following parameters are used'
+ scenario | dataspaceName | schemaSetName
+ 'dataspace name' | 'dataspace names with spaces' | 'schemaSetName'
+ 'schema set name name' | 'dataspaceName' | 'schema set name with spaces'
+ 'dataspace and schema set name' | 'dataspace name with spaces' | 'schema set name with spaces'
+ }
+
def 'Create schema set from invalid resources'() {
given: 'Invalid yang resource as name-to-content map'
def yangResourcesNameToContentMap = TestUtils.getYangResourcesAsMap('invalid.yang')
result.getModuleReferences().contains(new ModuleReference('stores', '2020-09-15', 'org:onap:ccsdk:sample'))
}
+ def 'Get a schema set with an invalid #scenario'() {
+ when: 'create dataspace method is invoked with incorrectly named dataspace'
+ objectUnderTest.getSchemaSet(dataspaceName, schemaSetName)
+ then: 'a data validation exception is thrown'
+ thrown(DataValidationException)
+ and: 'the yang resource cache is not invoked'
+ 0 * mockYangTextSchemaSourceSetCache.get(_, _)
+ where: 'the following parameters are used'
+ scenario | dataspaceName | schemaSetName
+ 'dataspace name' | 'dataspace names with spaces' | 'schemaSetName'
+ 'schema set name' | 'dataspaceName' | 'schema set name with spaces'
+ 'dataspace and schema set name' | 'dataspace name with spaces' | 'schema set name with spaces'
+ }
+
def 'Delete schema-set when cascade is allowed.'() {
given: '#numberOfAnchors anchors are associated with schemaset'
def associatedAnchors = createAnchors(numberOfAnchors)
thrown(SchemaSetInUseException)
}
+ def 'Delete a schema set with an invalid #scenario.'() {
+ when: 'create dataspace method is invoked with incorrectly named dataspace'
+ objectUnderTest.deleteSchemaSet(dataspaceName, schemaSetName, CASCADE_DELETE_ALLOWED)
+ then: 'a data validation exception is thrown'
+ thrown(DataValidationException)
+ and: 'anchor deletion is called 0 times'
+ 0 * mockCpsAdminService.deleteAnchor(_, _)
+ and: 'the delete schema set persistence service method is not invoked'
+ 0 * mockCpsModulePersistenceService.deleteSchemaSet(_, _, _)
+ and: 'schema set will be removed from the cache is not invoked'
+ 0 * mockYangTextSchemaSourceSetCache.removeFromCache(_, _)
+ and: 'orphan yang resources are deleted is not invoked'
+ 0 * mockCpsModulePersistenceService.deleteUnusedYangResourceModules()
+ where: 'the following parameters are used'
+ scenario | dataspaceName | schemaSetName
+ 'dataspace name' | 'dataspace names with spaces' | 'schemaSetName'
+ 'schema set name name' | 'dataspaceName' | 'schema set name with spaces'
+ 'dataspace and schema set name' | 'dataspace name with spaces' | 'schema set name with spaces'
+ }
+
def createAnchors(int anchorCount) {
def anchors = []
(0..<anchorCount).each { anchors.add(new Anchor("my-anchor-$it", 'my-dataspace', 'my-schemaset')) }
objectUnderTest.getYangResourceModuleReferences('someDataspaceName') == moduleReferences
}
+ def 'Get all yang resources module references given an invalid dataspace name.'() {
+ when: 'the get yang resources module references method is invoked with an invalid dataspace name'
+ objectUnderTest.getYangResourceModuleReferences('dataspace name with spaces')
+ then: 'a data validation exception is thrown'
+ thrown(DataValidationException)
+ and: 'the persistence service method is not invoked'
+ 0 * mockCpsModulePersistenceService.getYangResourceModuleReferences(_)
+ }
+
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'
objectUnderTest.getYangResourcesModuleReferences('someDataspaceName', 'someAnchorName') == moduleReferences
}
+ def 'Get all yang resources module references given an invalid #scenario.'() {
+ when: 'the get yang resources module references method is invoked with invalid #scenario'
+ objectUnderTest.getYangResourcesModuleReferences(dataspaceName, anchorName)
+ then: 'a data validation exception is thrown'
+ thrown(DataValidationException)
+ and: 'the persistence service method is not invoked'
+ 0 * mockCpsModulePersistenceService.getYangResourceModuleReferences(_, _)
+ where: 'the following parameters are used'
+ scenario | dataspaceName | anchorName
+ 'dataspace name' | 'dataspace names with spaces' | 'anchorName'
+ 'anchor name' | 'dataspaceName' | 'anchor name with spaces'
+ 'dataspace and anchor name' | 'dataspace name with spaces' | 'anchor name with spaces'
+ }
+
def 'Identifying new module references'(){
given: 'module references from cm handle'
def moduleReferencesToCheck = [new ModuleReference('some-module', 'some-revision')]
import org.onap.cps.spi.CpsDataPersistenceService
import org.onap.cps.spi.FetchDescendantsOption
+import org.onap.cps.spi.exceptions.DataValidationException
import spock.lang.Specification
class CpsQueryServiceImplSpec extends Specification {
where: 'all fetch descendants options are supported'
fetchDescendantsOption << FetchDescendantsOption.values()
}
+
+ def 'Query data nodes by cps path with invalid #scenario.'() {
+ when: 'queryDataNodes is invoked'
+ objectUnderTest.queryDataNodes(dataspaceName, anchorName, '/cps-path', FetchDescendantsOption.INCLUDE_ALL_DESCENDANTS)
+ then: 'a data validation exception is thrown'
+ thrown(DataValidationException)
+ and: 'the persistence service is not invoked'
+ 0 * mockCpsDataPersistenceService.queryDataNodes(_, _, _, _)
+ where: 'the following parameters are used'
+ scenario | dataspaceName | anchorName
+ 'dataspace name' | 'dataspace names with spaces' | 'anchorName'
+ 'anchor name' | 'dataspaceName' | 'anchor name with spaces'
+ 'dataspace and anchor name' | 'dataspace name with spaces' | 'anchor name with spaces'
+ }
+
}
/*
* ============LICENSE_START=======================================================
* Copyright (C) 2022 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.
import org.onap.cps.TestUtils
import org.onap.cps.spi.CpsModulePersistenceService
+import org.onap.cps.spi.exceptions.DataValidationException
import org.onap.cps.yang.YangTextSchemaSourceSet
import org.onap.cps.yang.YangTextSchemaSourceSetBuilder
import org.spockframework.spring.SpringBean
0 * mockModuleStoreService.getYangSchemaResources(_, _)
}
+ def 'Cache Hit: with invalid #scenario'() {
+ when: 'schema-set information is asked'
+ objectUnderTest.get(dataspaceName, schemaSetName)
+ then: 'an data validation exception is thrown'
+ thrown(DataValidationException)
+ and: 'module persistence is not invoked'
+ 0 * mockModuleStoreService.getYangSchemaResources(_, _)
+ where: 'the following parameters are used'
+ scenario | dataspaceName | schemaSetName
+ 'dataspace name' | 'dataspace names with spaces' | 'schemaSetName'
+ 'schema set name' | 'dataspaceName' | 'schema set name with spaces'
+ 'dataspace and schema set name' | 'dataspace name with spaces' | 'schema set name with spaces'
+ }
+
def 'Cache Update: when no data exist in the cache'() {
given: 'a schema set exists'
def yangResourcesNameToContentMap = TestUtils.getYangResourcesAsMap('bookstore.yang')
cachedValue.getModuleReferences() == yangTextSchemaSourceSet.getModuleReferences()
}
- def 'Cache Evict: remove when exist'() {
+ def 'Cache Update: with invalid #scenario'() {
+ given: 'a schema set exists'
+ def yangResourcesNameToContentMap = TestUtils.getYangResourcesAsMap('bookstore.yang')
+ def yangTextSchemaSourceSet = YangTextSchemaSourceSetBuilder.of(yangResourcesNameToContentMap)
+ when: 'schema-set information is asked'
+ objectUnderTest.updateCache(dataspaceName, schemaSetName, yangTextSchemaSourceSet)
+ then: 'an data validation exception is thrown'
+ thrown(DataValidationException)
+ and: 'module persistence is not invoked'
+ 0 * mockModuleStoreService.getYangSchemaResources(_, _)
+ where: 'the following parameters are used'
+ scenario | dataspaceName | schemaSetName
+ 'dataspace name' | 'dataspace names with spaces' | 'schemaSetName'
+ 'schema set name' | 'dataspaceName' | 'schema set name with spaces'
+ 'dataspace and schema set name' | 'dataspace name with spaces' | 'schema set name with spaces'
+ }
+
+ def 'Cache Evict:with invalid #scenario'() {
given: 'a schema set exists in cache'
def yangResourcesNameToContentMap = TestUtils.getYangResourcesAsMap('bookstore.yang')
def yangTextSchemaSourceSet = YangTextSchemaSourceSetBuilder.of(yangResourcesNameToContentMap)
assert getCachedValue('my-dataspace', 'my-schemaset') == null
}
+ def 'Cache Evict: remove when exist'() {
+ when: 'cache is evicted for schemaset'
+ objectUnderTest.removeFromCache(dataspaceName, schemaSetName)
+ then: 'an data validation exception is thrown'
+ thrown(DataValidationException)
+ where: 'the following parameters are used'
+ scenario | dataspaceName | schemaSetName
+ 'dataspace name' | 'dataspace names with spaces' | 'schemaSetName'
+ 'schema set name' | 'dataspaceName' | 'schema set name with spaces'
+ 'dataspace and schema set name' | 'dataspace name with spaces' | 'schema set name with spaces'
+ }
+
def 'Cache Evict: remove when does not exist'() {
given: 'cache is empty'
yangResourceCacheImpl.clear()
.. 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
.. _adminGuide:
.. code::
http://<cps-component-service-name>:8081/manage/prometheus
+
+Naming Validation
+-----------------
+
+As part of the Jakarta 3.1.0 release, CPS has added validation to the names of the following components:
+
+ - Dataspace names
+ - Schema Set names
+ - Anchor names
+ - Cm-Handle identifiers
+
+The following characters along with spaces are no longer valid for naming of these components.
+
+.. code::
+
+ !"#$%&'()*+,./\:;<=>?@[]^`{|}~
.. * * * JAKARTA * * *
.. ========================
+Version: 3.1.0
+==============
++--------------------------------------+--------------------------------------------------------+
+| **CPS Project** | |
+| | |
++--------------------------------------+--------------------------------------------------------+
+| **Docker images** | onap/cps-and-ncmp:3.1.0 |
+| | |
++--------------------------------------+--------------------------------------------------------+
+| **Release designation** | 3.1.0 Jakarta |
+| | |
++--------------------------------------+--------------------------------------------------------+
+| **Release date** | |
+| | |
++--------------------------------------+--------------------------------------------------------+
+
+Features
+--------
+ - `CPS-322 <https://jira.onap.org/browse/CPS-322>`_ Implement additional validation for names and identifiers
+
Version: 3.0.0
==============