<modelVersion>4.0.0</modelVersion>
<groupId>org.onap.cps</groupId>
<artifactId>checkstyle</artifactId>
- <version>3.4.6-SNAPSHOT</version>
+ <version>3.4.7-SNAPSHOT</version>
<profiles>
<profile>
<parent>
<groupId>org.onap.cps</groupId>
<artifactId>cps-parent</artifactId>
- <version>3.4.6-SNAPSHOT</version>
+ <version>3.4.7-SNAPSHOT</version>
<relativePath>../cps-parent/pom.xml</relativePath>
</parent>
<modelVersion>4.0.0</modelVersion>
<groupId>org.onap.cps</groupId>
<artifactId>cps-bom</artifactId>
- <version>3.4.6-SNAPSHOT</version>
+ <version>3.4.7-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>3.4.6-SNAPSHOT</version>
+ <version>3.4.7-SNAPSHOT</version>
<packaging>pom</packaging>
<name>${project.groupId}:${project.artifactId}</name>
<parent>
<groupId>org.onap.cps</groupId>
<artifactId>cps-parent</artifactId>
- <version>3.4.6-SNAPSHOT</version>
+ <version>3.4.7-SNAPSHOT</version>
<relativePath>../cps-parent/pom.xml</relativePath>
</parent>
<parent>
<groupId>org.onap.cps</groupId>
<artifactId>cps-parent</artifactId>
- <version>3.4.6-SNAPSHOT</version>
+ <version>3.4.7-SNAPSHOT</version>
<relativePath>../cps-parent/pom.xml</relativePath>
</parent>
<parent>
<groupId>org.onap.cps</groupId>
<artifactId>cps-ncmp-rest-stub</artifactId>
- <version>3.4.6-SNAPSHOT</version>
+ <version>3.4.7-SNAPSHOT</version>
</parent>
<artifactId>cps-ncmp-rest-stub-app</artifactId>
<parent>
<groupId>org.onap.cps</groupId>
<artifactId>cps-ncmp-rest-stub</artifactId>
- <version>3.4.6-SNAPSHOT</version>
+ <version>3.4.7-SNAPSHOT</version>
</parent>
<artifactId>cps-ncmp-rest-stub-service</artifactId>
<parent>
<groupId>org.onap.cps</groupId>
<artifactId>cps-parent</artifactId>
- <version>3.4.6-SNAPSHOT</version>
+ <version>3.4.7-SNAPSHOT</version>
<relativePath>../cps-parent/pom.xml</relativePath>
</parent>
<parent>
<groupId>org.onap.cps</groupId>
<artifactId>cps-parent</artifactId>
- <version>3.4.6-SNAPSHOT</version>
+ <version>3.4.7-SNAPSHOT</version>
<relativePath>../cps-parent/pom.xml</relativePath>
</parent>
final DataOperationRequest dataOperationRequest,
final String authorization) {
return ncmpPassthroughResourceRequestHandler.executeRequest(topicParamInQuery,
- dataOperationRequestMapper.toDataOperationRequest(dataOperationRequest));
+ dataOperationRequestMapper.toDataOperationRequest(dataOperationRequest), authorization);
}
/**
*
* @param topicParamInQuery the topic param in query
* @param dataOperationRequest data operation request details for resource data
+ * @param authorization contents of Authorization header, or null if not present
* @return the response entity
*/
public ResponseEntity<Object> executeRequest(final String topicParamInQuery,
- final DataOperationRequest
- dataOperationRequest) {
+ final DataOperationRequest dataOperationRequest,
+ final String authorization) {
validateDataOperationRequest(topicParamInQuery, dataOperationRequest);
if (!notificationFeatureEnabled) {
return ResponseEntity.ok(Map.of("status",
"Asynchronous request is unavailable as notification feature is currently disabled."));
}
- return getRequestIdAndSendDataOperationRequestToDmiService(topicParamInQuery, dataOperationRequest);
+ return getRequestIdAndSendDataOperationRequestToDmiService(topicParamInQuery, dataOperationRequest,
+ authorization);
}
@Override
authorization);
}
- private ResponseEntity<Object> getRequestIdAndSendDataOperationRequestToDmiService(final String topicParamInQuery,
- final DataOperationRequest
- dataOperationRequest) {
+ private ResponseEntity<Object> getRequestIdAndSendDataOperationRequestToDmiService(
+ final String topicParamInQuery,
+ final DataOperationRequest dataOperationRequest,
+ final String authorization) {
final String requestId = UUID.randomUUID().toString();
cpsNcmpTaskExecutor.executeTask(
- getTaskSupplierForDataOperationRequest(topicParamInQuery, dataOperationRequest, requestId),
+ getTaskSupplierForDataOperationRequest(topicParamInQuery, dataOperationRequest, requestId, authorization),
timeOutInMilliSeconds);
return ResponseEntity.ok(Map.of("requestId", requestId));
}
private Supplier<Object> getTaskSupplierForDataOperationRequest(final String topicParamInQuery,
final DataOperationRequest dataOperationRequest,
- final String requestId) {
+ final String requestId,
+ final String authorization) {
return () -> {
networkCmProxyDataService.executeDataOperationForCmHandles(topicParamInQuery,
dataOperationRequest,
- requestId);
+ requestId,
+ authorization);
return noReturn;
};
}
and: 'notification feature is turned on/off'
objectUnderTest.notificationFeatureEnabled = notificationFeatureEnabled
when: 'data operation request is executed'
- objectUnderTest.executeRequest('someTopic', new DataOperationRequest())
+ objectUnderTest.executeRequest('someTopic', new DataOperationRequest(), NO_AUTH_HEADER)
then: 'the task is executed in an async fashion or not'
expectedCalls * spiedCpsNcmpTaskExecutor.executeTask(*_)
where: 'the following parameters are used'
and: ' a flag to track the network service call'
def networkServiceMethodCalled = false
and: 'the (mocked) service will use the flag to indicate it is called'
- mockNetworkCmProxyDataService.executeDataOperationForCmHandles('myTopic', dataOperationRequest, _) >> {
+ mockNetworkCmProxyDataService.executeDataOperationForCmHandles('myTopic', dataOperationRequest, _, NO_AUTH_HEADER) >> {
networkServiceMethodCalled = true
}
when: 'data operation request is executed'
- objectUnderTest.executeRequest('myTopic', dataOperationRequest)
+ objectUnderTest.executeRequest('myTopic', dataOperationRequest, NO_AUTH_HEADER)
then: 'the task is executed in an async fashion'
1 * spiedCpsNcmpTaskExecutor.executeTask(*_)
and: 'the network service is invoked'
def dataOperationDefinition = new DataOperationDefinition(operation: 'read', datastore: datastore)
when: 'data operation request is executed'
def dataOperationRequest = new DataOperationRequest(dataOperationDefinitions: [dataOperationDefinition])
- objectUnderTest.executeRequest('myTopic', dataOperationRequest)
+ objectUnderTest.executeRequest('myTopic', dataOperationRequest, NO_AUTH_HEADER)
then: 'the correct error is thrown'
def thrown = thrown(InvalidDatastoreException)
assert thrown.message.contains(expectedErrorMessage)
and: 'a data operation definition with operation: #operation'
def dataOperationDefinition = new DataOperationDefinition(operation: operation, datastore: 'ncmp-datastore:passthrough-running')
when: 'bulk request is executed'
- objectUnderTest.executeRequest('someTopic', new DataOperationRequest(dataOperationDefinitions:[dataOperationDefinition]))
+ objectUnderTest.executeRequest('someTopic', new DataOperationRequest(dataOperationDefinitions:[dataOperationDefinition]), NO_AUTH_HEADER)
then: 'the expected type of exception is thrown'
thrown(expectedException)
where: 'the following operations are used'
<parent>
<groupId>org.onap.cps</groupId>
<artifactId>cps-parent</artifactId>
- <version>3.4.6-SNAPSHOT</version>
+ <version>3.4.7-SNAPSHOT</version>
<relativePath>../cps-parent/pom.xml</relativePath>
</parent>
*
* @param topicParamInQuery topic name for (triggering) async responses
* @param dataOperationRequest contains a list of operation definitions(multiple operations)
+ * @param requestId request ID
+ * @param authorization contents of Authorization header, or null if not present
*/
void executeDataOperationForCmHandles(String topicParamInQuery,
DataOperationRequest dataOperationRequest,
- String requestId);
+ String requestId,
+ String authorization);
/**
@Override
public void executeDataOperationForCmHandles(final String topicParamInQuery,
- final DataOperationRequest
- dataOperationRequest,
- final String requestId) {
- dmiDataOperations.requestResourceDataFromDmi(topicParamInQuery, dataOperationRequest, requestId);
+ final DataOperationRequest dataOperationRequest,
+ final String requestId,
+ final String authorization) {
+ dmiDataOperations.requestResourceDataFromDmi(topicParamInQuery, dataOperationRequest, requestId,
+ authorization);
}
@Override
final List<NcmpServiceCmHandle> cmHandlesToBeCreated,
final List<CmHandleRegistrationResponse> cmHandleRegistrationResponses) {
final Collection<String> rejectedCmHandleIds = alternateIdChecker
- .getIdsOfCmHandlesWithRejectedAlternateId(cmHandlesToBeCreated);
+ .getIdsOfCmHandlesWithRejectedAlternateId(cmHandlesToBeCreated, AlternateIdChecker.Operation.CREATE);
cmHandleRegistrationResponses.addAll(CmHandleRegistrationResponse.createFailureResponses(
rejectedCmHandleIds, ALTERNATE_ID_ALREADY_ASSOCIATED));
return rejectedCmHandleIds;
package org.onap.cps.ncmp.api.impl;
+import static org.onap.cps.ncmp.api.NcmpResponseStatus.ALTERNATE_ID_ALREADY_ASSOCIATED;
import static org.onap.cps.ncmp.api.NcmpResponseStatus.CM_HANDLES_NOT_FOUND;
import static org.onap.cps.ncmp.api.NcmpResponseStatus.CM_HANDLE_INVALID_ID;
import static org.onap.cps.ncmp.api.impl.NetworkCmProxyDataServicePropertyHandler.PropertyType.DMI_PROPERTY;
import org.onap.cps.api.CpsDataService;
import org.onap.cps.ncmp.api.impl.inventory.InventoryPersistence;
import org.onap.cps.ncmp.api.impl.utils.AlternateIdChecker;
-import org.onap.cps.ncmp.api.impl.utils.YangDataConverter;
-import org.onap.cps.ncmp.api.impl.yangmodels.YangModelCmHandle;
import org.onap.cps.ncmp.api.models.CmHandleRegistrationResponse;
import org.onap.cps.ncmp.api.models.NcmpServiceCmHandle;
import org.onap.cps.spi.exceptions.DataNodeNotFoundException;
private final AlternateIdChecker alternateIdChecker;
/**
- * Iterates over incoming ncmpServiceCmHandles and update the dataNodes based on the updated attributes.
+ * Iterates over incoming updatedNcmpServiceCmHandles and update the dataNodes based on the updated attributes.
* The attributes which are not passed will remain as is.
*
- * @param ncmpServiceCmHandles collection of ncmpServiceCmHandles
+ * @param updatedNcmpServiceCmHandles collection of CmHandles
*/
public List<CmHandleRegistrationResponse> updateCmHandleProperties(
- final Collection<NcmpServiceCmHandle> ncmpServiceCmHandles) {
- final List<CmHandleRegistrationResponse> cmHandleRegistrationResponses
- = new ArrayList<>(ncmpServiceCmHandles.size());
- for (final NcmpServiceCmHandle ncmpServiceCmHandle : ncmpServiceCmHandles) {
- final String cmHandleId = ncmpServiceCmHandle.getCmHandleId();
- try {
- final DataNode existingCmHandleDataNode = inventoryPersistence
- .getCmHandleDataNodeByCmHandleId(cmHandleId).iterator().next();
- updateAlternateId(existingCmHandleDataNode, ncmpServiceCmHandle);
- processUpdates(existingCmHandleDataNode, ncmpServiceCmHandle);
- cmHandleRegistrationResponses.add(CmHandleRegistrationResponse.createSuccessResponse(cmHandleId));
- } catch (final DataNodeNotFoundException e) {
- log.error("Unable to find dataNode for cmHandleId : {} , caused by : {}", cmHandleId, e.getMessage());
- cmHandleRegistrationResponses.add(
- CmHandleRegistrationResponse.createFailureResponse(cmHandleId, CM_HANDLES_NOT_FOUND));
- } catch (final DataValidationException e) {
- log.error("Unable to update cm handle : {}, caused by : {}", cmHandleId, e.getMessage());
- cmHandleRegistrationResponses.add(
- CmHandleRegistrationResponse.createFailureResponse(cmHandleId, CM_HANDLE_INVALID_ID));
- } catch (final Exception exception) {
- log.error("Unable to update cmHandle : {} , caused by : {}", cmHandleId, exception.getMessage());
- cmHandleRegistrationResponses.add(
- CmHandleRegistrationResponse.createFailureResponse(cmHandleId, exception));
+ final Collection<NcmpServiceCmHandle> updatedNcmpServiceCmHandles) {
+ final Collection<String> rejectedCmHandleIds = alternateIdChecker
+ .getIdsOfCmHandlesWithRejectedAlternateId(updatedNcmpServiceCmHandles, AlternateIdChecker.Operation.UPDATE);
+ final List<CmHandleRegistrationResponse> failureResponses =
+ CmHandleRegistrationResponse.createFailureResponses(rejectedCmHandleIds, ALTERNATE_ID_ALREADY_ASSOCIATED);
+ final List<CmHandleRegistrationResponse> cmHandleRegistrationResponses = new ArrayList<>(failureResponses);
+ for (final NcmpServiceCmHandle updatedNcmpServiceCmHandle : updatedNcmpServiceCmHandles) {
+ final String cmHandleId = updatedNcmpServiceCmHandle.getCmHandleId();
+ if (!rejectedCmHandleIds.contains(cmHandleId)) {
+ try {
+ final DataNode existingCmHandleDataNode = inventoryPersistence
+ .getCmHandleDataNodeByCmHandleId(cmHandleId).iterator().next();
+ processUpdates(existingCmHandleDataNode, updatedNcmpServiceCmHandle);
+ cmHandleRegistrationResponses.add(CmHandleRegistrationResponse.createSuccessResponse(cmHandleId));
+ } catch (final DataNodeNotFoundException e) {
+ log.error("Unable to find dataNode for cmHandleId : {} , caused by : {}", cmHandleId,
+ e.getMessage());
+ cmHandleRegistrationResponses.add(
+ CmHandleRegistrationResponse.createFailureResponse(cmHandleId, CM_HANDLES_NOT_FOUND));
+ } catch (final DataValidationException e) {
+ log.error("Unable to update cm handle : {}, caused by : {}", cmHandleId, e.getMessage());
+ cmHandleRegistrationResponses.add(
+ CmHandleRegistrationResponse.createFailureResponse(cmHandleId, CM_HANDLE_INVALID_ID));
+ } catch (final Exception exception) {
+ log.error("Unable to update cmHandle : {} , caused by : {}", cmHandleId, exception.getMessage());
+ cmHandleRegistrationResponses.add(
+ CmHandleRegistrationResponse.createFailureResponse(cmHandleId, exception));
+ }
}
}
return cmHandleRegistrationResponses;
}
- private void updateAlternateId(final DataNode existingCmHandleDataNode,
- final NcmpServiceCmHandle ncmpServiceCmHandle) {
- final YangModelCmHandle yangModelCmHandle =
- YangDataConverter.convertCmHandleToYangModel(existingCmHandleDataNode);
- final String currentAlternateId = yangModelCmHandle.getAlternateId();
- final String newAlternateId = ncmpServiceCmHandle.getAlternateId();
- if (alternateIdChecker.canApplyAlternateId(ncmpServiceCmHandle.getCmHandleId(),
- currentAlternateId, newAlternateId)) {
- setAndUpdateAlternateId(yangModelCmHandle, newAlternateId);
+ private void processUpdates(final DataNode existingCmHandleDataNode,
+ final NcmpServiceCmHandle updatedNcmpServiceCmHandle) {
+ updateAlternateId(updatedNcmpServiceCmHandle);
+ if (!updatedNcmpServiceCmHandle.getPublicProperties().isEmpty()) {
+ updateProperties(existingCmHandleDataNode, PUBLIC_PROPERTY,
+ updatedNcmpServiceCmHandle.getPublicProperties());
+ }
+ if (!updatedNcmpServiceCmHandle.getDmiProperties().isEmpty()) {
+ updateProperties(existingCmHandleDataNode, DMI_PROPERTY, updatedNcmpServiceCmHandle.getDmiProperties());
}
}
- private void processUpdates(final DataNode existingCmHandleDataNode, final NcmpServiceCmHandle incomingCmHandle) {
- if (!incomingCmHandle.getPublicProperties().isEmpty()) {
- updateProperties(existingCmHandleDataNode, PUBLIC_PROPERTY, incomingCmHandle.getPublicProperties());
- }
- if (!incomingCmHandle.getDmiProperties().isEmpty()) {
- updateProperties(existingCmHandleDataNode, DMI_PROPERTY, incomingCmHandle.getDmiProperties());
- }
+ private void updateAlternateId(final NcmpServiceCmHandle updatedNcmpServiceCmHandle) {
+ final String updatedAlternateId = updatedNcmpServiceCmHandle.getAlternateId();
+ final String cmHandleId = updatedNcmpServiceCmHandle.getCmHandleId();
+ final Map<String, String> cmHandleProperties = new HashMap<>(2);
+ cmHandleProperties.put("id", cmHandleId);
+ cmHandleProperties.put("alternate-id", updatedAlternateId);
+ final Map<String, Map<String, String>> dmiRegistryProperties = new HashMap<>(1);
+ dmiRegistryProperties.put("cm-handles", cmHandleProperties);
+ cpsDataService.updateNodeLeaves(NCMP_DATASPACE_NAME, NCMP_DMI_REGISTRY_ANCHOR, NCMP_DMI_REGISTRY_PARENT,
+ jsonObjectMapper.asJsonString(dmiRegistryProperties), OffsetDateTime.now());
+ log.debug("Updating alternateId for cmHandle {} with value : {})", cmHandleId, updatedAlternateId);
}
private void updateProperties(final DataNode existingCmHandleDataNode, final PropertyType propertyType,
- final Map<String, String> incomingProperties) {
+ final Map<String, String> updatedProperties) {
final Collection<DataNode> replacementPropertyDataNodes =
- getReplacementDataNodes(existingCmHandleDataNode, propertyType, incomingProperties);
+ getReplacementDataNodes(existingCmHandleDataNode, propertyType, updatedProperties);
replacementPropertyDataNodes.addAll(
- getUnchangedPropertyDataNodes(existingCmHandleDataNode, propertyType, incomingProperties));
+ getUnchangedPropertyDataNodes(existingCmHandleDataNode, propertyType, updatedProperties));
if (replacementPropertyDataNodes.isEmpty()) {
removeAllProperties(existingCmHandleDataNode, propertyType);
} else {
}
private Collection<DataNode> getUnchangedPropertyDataNodes(final DataNode existingCmHandleDataNode,
- final PropertyType propertyType, final Map<String, String> incomingProperties) {
+ final PropertyType propertyType,
+ final Map<String, String> updatedProperties) {
final Collection<DataNode> unchangedPropertyDataNodes = new HashSet<>();
for (final DataNode existingPropertyDataNode : existingCmHandleDataNode.getChildDataNodes()) {
final Matcher matcher = propertyType.propertyXpathPattern.matcher(existingPropertyDataNode.getXpath());
if (matcher.find()) {
final String keyName = matcher.group(2);
- if (!incomingProperties.containsKey(keyName)) {
+ if (!updatedProperties.containsKey(keyName)) {
unchangedPropertyDataNodes.add(existingPropertyDataNode);
}
}
}
private Collection<DataNode> getReplacementDataNodes(final DataNode existingCmHandleDataNode,
- final PropertyType propertyType, final Map<String, String> incomingProperties) {
+ final PropertyType propertyType,
+ final Map<String, String> updatedProperties) {
final Collection<DataNode> replacementPropertyDataNodes = new HashSet<>();
- incomingProperties.forEach((updatedAttributeKey, updatedAttributeValue) -> {
+ updatedProperties.forEach((updatedAttributeKey, updatedAttributeValue) -> {
final String propertyXpath = getAttributeXpath(existingCmHandleDataNode, propertyType, updatedAttributeKey);
if (updatedAttributeValue != null) {
log.info("Creating a new DataNode with xpath {} , key : {} and value : {}", propertyXpath,
}
private String getAttributeXpath(final DataNode cmHandle, final PropertyType propertyType,
- final String attributeKey) {
+ final String attributeKey) {
return cmHandle.getXpath() + "/" + propertyType.xpathPrefix + String.format("[@name='%s']", attributeKey);
}
return new DataNodeBuilder().withXpath(xpath).withLeaves(ImmutableMap.copyOf(updatedLeaves)).build();
}
- private void setAndUpdateAlternateId(final YangModelCmHandle upgradedCmHandle, final String alternateId) {
- final Map<String, Map<String, String>> dmiRegistryProperties = new HashMap<>(1);
- final Map<String, String> cmHandleProperties = new HashMap<>(2);
- cmHandleProperties.put("id", upgradedCmHandle.getId());
- cmHandleProperties.put("alternate-id", alternateId);
- dmiRegistryProperties.put("cm-handles", cmHandleProperties);
- cpsDataService.updateNodeLeaves(NCMP_DATASPACE_NAME, NCMP_DMI_REGISTRY_ANCHOR, NCMP_DMI_REGISTRY_PARENT,
- jsonObjectMapper.asJsonString(dmiRegistryProperties), OffsetDateTime.now());
- log.info("Updating alternateId for cmHandle {} with value : {})", upgradedCmHandle.getId(), alternateId);
- }
-
enum PropertyType {
DMI_PROPERTY("additional-properties"), PUBLIC_PROPERTY("public-properties");
* @param topicParamInQuery topic name for (triggering) async responses
* @param dataOperationRequest data operation request to execute operations
* @param requestId requestId for as a response
+ * @param authorization contents of Authorization header, or null if not present
*/
public void requestResourceDataFromDmi(final String topicParamInQuery,
final DataOperationRequest dataOperationRequest,
- final String requestId) {
+ final String requestId,
+ final String authorization) {
final Set<String> cmHandlesIds
= getDistinctCmHandleIdsFromDataOperationRequest(dataOperationRequest);
= ResourceDataOperationRequestUtils.processPerDefinitionInDataOperationsRequest(topicParamInQuery,
requestId, dataOperationRequest, yangModelCmHandles);
- buildDataOperationRequestUrlAndSendToDmiService(topicParamInQuery, requestId, operationsOutPerDmiServiceName);
+ buildDataOperationRequestUrlAndSendToDmiService(topicParamInQuery, requestId, operationsOutPerDmiServiceName,
+ authorization);
}
/**
private void buildDataOperationRequestUrlAndSendToDmiService(final String topicParamInQuery,
final String requestId,
final Map<String, List<DmiDataOperation>>
- groupsOutPerDmiServiceName) {
+ groupsOutPerDmiServiceName,
+ final String authorization) {
groupsOutPerDmiServiceName.forEach((dmiServiceName, dmiDataOperationRequestBodies) -> {
final String dmiDataOperationResourceUrl =
getDmiServiceDataOperationRequestUrl(dmiServiceName, topicParamInQuery, requestId);
- sendDataOperationRequestToDmiService(dmiDataOperationResourceUrl, dmiDataOperationRequestBodies);
+ sendDataOperationRequestToDmiService(dmiDataOperationResourceUrl, dmiDataOperationRequestBodies,
+ authorization);
});
}
private void sendDataOperationRequestToDmiService(final String dataOperationResourceUrl,
- final List<DmiDataOperation> dmiDataOperationRequestBodies) {
+ final List<DmiDataOperation> dmiDataOperationRequestBodies,
+ final String authorization) {
final DmiDataOperationRequest dmiDataOperationRequest = DmiDataOperationRequest.builder()
.operations(dmiDataOperationRequestBodies).build();
final String dmiDataOperationRequestAsJsonString =
jsonObjectMapper.asJsonString(dmiDataOperationRequest);
TaskExecutor.executeTask(() -> dmiRestClient.postOperationWithJsonData(dataOperationResourceUrl,
- dmiDataOperationRequestAsJsonString, READ, null),
+ dmiDataOperationRequestAsJsonString, READ, authorization),
DEFAULT_ASYNC_TASK_EXECUTOR_TIMEOUT_IN_MILLISECONDS)
.whenCompleteAsync((response, throwable) -> handleTaskCompletionException(throwable,
dataOperationResourceUrl, dmiDataOperationRequestBodies));
@RequiredArgsConstructor
public class AlternateIdChecker {
+ public enum Operation {
+ CREATE, UPDATE
+ }
+
private final InventoryPersistence inventoryPersistence;
private static final String NO_CURRENT_ALTERNATE_ID = "";
}
/**
- * Check all alternate ids of a batch of NEW cm handles.
+ * Check all alternate ids of a batch of cm handles.
* Includes cross-checks in the batch itself for duplicates. Only the first entry encountered wil be accepted.
- * This method can only be used for NEW cm handle registrations NOT for updating existing ones
*
* @param newNcmpServiceCmHandles the proposed new cm handles
+ * @param operation type of operation being executed
* @return collection of cm handles ids which are acceptable
*/
public Collection<String> getIdsOfCmHandlesWithRejectedAlternateId(
- final Collection<NcmpServiceCmHandle> newNcmpServiceCmHandles) {
+ final Collection<NcmpServiceCmHandle> newNcmpServiceCmHandles,
+ final Operation operation) {
final Set<String> acceptedAlternateIds = new HashSet<>(newNcmpServiceCmHandles.size());
final Collection<String> rejectedCmHandleIds = new ArrayList<>();
for (final NcmpServiceCmHandle ncmpServiceCmHandle : newNcmpServiceCmHandles) {
log.warn("Alternate id update ignored, cannot update cm handle {}, alternate id is already "
+ "assigned to a different cm handle (in this batch)", cmHandleId);
} else {
- isAcceptable = canApplyAlternateId(cmHandleId, NO_CURRENT_ALTERNATE_ID, proposedAlternateId);
+ if (Operation.CREATE.equals(operation)) {
+ isAcceptable = canApplyAlternateId(cmHandleId, NO_CURRENT_ALTERNATE_ID, proposedAlternateId);
+ } else {
+ isAcceptable = canApplyAlternateId(cmHandleId, proposedAlternateId);
+ }
}
}
if (isAcceptable) {
def setup() {
// always accept all cm handles
- mockAlternateIdChecker.getIdsOfCmHandlesWithRejectedAlternateId(_) >> []
+ mockAlternateIdChecker.getIdsOfCmHandlesWithRejectedAlternateId(*_) >> []
// always can find all cm handles in DB
mockInventoryPersistence.getYangModelCmHandles(_) >> { args -> args[0].collect { new YangModelCmHandle(id:it) } }
'an unexpected exception' | 'cmhandle' | new RuntimeException('Failed') || UNKNOWN_ERROR | 'Failed'
}
- def 'Adding data to alternate id caches.'() {
- given: 'a registration with three CM Handles to be created'
- def ncmpServiceCmHandles = [new NcmpServiceCmHandle(cmHandleId: 'cmhandle1', alternateId: 'my-alternate-id-1')]
- def dmiPluginRegistration = new DmiPluginRegistration(dmiPlugin: 'my-server', createdCmHandles: ncmpServiceCmHandles)
- when: 'the DMI plugin registration happens'
- objectUnderTest.updateDmiRegistrationAndSyncModule(dmiPluginRegistration)
- then: 'the new alternate id is added to the cache'
- 1 * mockAlternateIdChecker.getIdsOfCmHandlesWithRejectedAlternateId(ncmpServiceCmHandles) >> ['cmhandle1']
- }
-
}
given: 'cpsDataService returns valid data node'
def dataOperationRequest = getDataOperationRequest(datastoreName)
when: 'request resource data for data operation is called'
- objectUnderTest.executeDataOperationForCmHandles('some topic', dataOperationRequest, 'requestId')
+ objectUnderTest.executeDataOperationForCmHandles('some topic', dataOperationRequest, 'requestId', NO_AUTH_HEADER)
then: 'request resource data for data operation returns expected response'
- 1 * mockDmiDataOperations.requestResourceDataFromDmi('some topic', dataOperationRequest, 'requestId')
+ 1 * mockDmiDataOperations.requestResourceDataFromDmi('some topic', dataOperationRequest, 'requestId', NO_AUTH_HEADER)
where: 'the following data stores are used'
datastoreName << [PASSTHROUGH_RUNNING.datastoreName, PASSTHROUGH_OPERATIONAL.datastoreName]
}
dmiPluginRegistration.createdCmHandles = [ncmpServiceCmHandle]
mockDmiPluginRegistration.getCreatedCmHandles() >> [ncmpServiceCmHandle]
and: 'no rejected cm handles because of alternate ids'
- mockAlternateIdChecker.getIdsOfCmHandlesWithRejectedAlternateId(_) >> []
+ mockAlternateIdChecker.getIdsOfCmHandlesWithRejectedAlternateId(*_) >> []
when: 'parse and create cm handle in dmi registration then sync module'
mockDmiPluginRegistration.createdCmHandles = ['test-cm-handle-id']
objectUnderTest.processCreatedCmHandles(mockDmiPluginRegistration, new DmiPluginRegistrationResponse())
def mockAlternateIdChecker = Mock(AlternateIdChecker)
def objectUnderTest = new NetworkCmProxyDataServicePropertyHandler(mockInventoryPersistence, mockCpsDataService, jsonObjectMapper, mockAlternateIdChecker)
+
+ def setup() {
+ // Always accept all alternate IDs
+ mockAlternateIdChecker.getIdsOfCmHandlesWithRejectedAlternateId(*_) >> []
+ }
+
def static cmHandleId = 'myHandle1'
def static cmHandleXpath = "/dmi-registry/cm-handles[@id='${cmHandleId}']"
when: 'update data node leaves is called with the update request'
objectUnderTest.updateCmHandleProperties(cmHandleUpdateRequest)
then: 'the replace list method is called with correct params'
- 1 * mockInventoryPersistence.replaceListContent(cmHandleXpath,_) >> { args ->
+ 1 * mockInventoryPersistence.replaceListContent(cmHandleXpath, _) >> { args ->
{
assert args[1].leaves.size() == expectedPropertiesAfterUpdate.size()
assert args[1].leaves.containsAll(convertToProperties(expectedPropertiesAfterUpdate))
assert it.errorText == expectedErrorText
}
where:
- scenario | cmHandleId | exception || expectedError | expectedErrorText
- 'Cm Handle does not exist' | 'cmHandleId' | new DataNodeNotFoundException(NCMP_DATASPACE_NAME, NCMP_DMI_REGISTRY_ANCHOR) || CM_HANDLES_NOT_FOUND | 'cm handle id(s) not found'
- 'Unknown' | 'cmHandleId' | new RuntimeException('Failed') || UNKNOWN_ERROR | 'Failed'
- 'Invalid cm handle id' | 'cmHandleId with spaces' | new DataValidationException('Name Validation Error.', cmHandleId + 'contains an invalid character') || CM_HANDLE_INVALID_ID | 'cm-handle has an invalid character(s) in id'
+ scenario | cmHandleId | exception || expectedError | expectedErrorText
+ 'Cm Handle does not exist' | 'cmHandleId' | new DataNodeNotFoundException(NCMP_DATASPACE_NAME, NCMP_DMI_REGISTRY_ANCHOR) || CM_HANDLES_NOT_FOUND | 'cm handle id(s) not found'
+ 'Unknown' | 'cmHandleId' | new RuntimeException('Failed') || UNKNOWN_ERROR | 'Failed'
+ 'Invalid cm handle id' | 'cmHandleId with spaces' | new DataValidationException('Name Validation Error.', cmHandleId + 'contains an invalid character') || CM_HANDLE_INVALID_ID | 'cm-handle has an invalid character(s) in id'
}
def 'Multiple update operations in a single request'() {
assert it.errorText == 'cm handle id(s) not found'
}
then: 'the replace list method is called twice'
- 2 * mockInventoryPersistence.replaceListContent(cmHandleXpath,_)
+ 2 * mockInventoryPersistence.replaceListContent(cmHandleXpath, _)
}
- def 'Update CM Handle Alternate ID with #scenario'() {
- given: 'an existing cm handle'
- DataNode existingCmHandleDataNode = new DataNode(xpath: cmHandleXpath, leaves: ['id': cmHandleId])
- and: 'an update request with an alternate id'
- def ncmpServiceCmHandle = new NcmpServiceCmHandle(cmHandleId: cmHandleId, alternateId: 'alt-1')
- when: 'update alternate id method is called with the update request'
- objectUnderTest.updateAlternateId(existingCmHandleDataNode, ncmpServiceCmHandle)
- then: 'the update node leaves method is invoked as many times as expected'
- callsToDataService * mockCpsDataService.updateNodeLeaves('NCMP-Admin', 'ncmp-dmi-registry', '/dmi-registry', _, _) >>
+ def 'Update alternate id of existing CM Handle.'() {
+ given: 'cm handles request'
+ def cmHandleUpdateRequest = [new NcmpServiceCmHandle(cmHandleId: cmHandleId, alternateId: 'alt-1')]
+ and: 'a data node found'
+ def dataNode = new DataNode(xpath: cmHandleXpath, leaves: ['id': cmHandleId, 'alternate-id': 'alt-1'])
+ mockInventoryPersistence.getCmHandleDataNodeByCmHandleId(cmHandleId) >> [dataNode]
+ when: 'cm handle properties is updated'
+ def response = objectUnderTest.updateCmHandleProperties(cmHandleUpdateRequest)
+ then: 'the update is delegated to cps data service with correct parameters'
+ 1 * mockCpsDataService.updateNodeLeaves('NCMP-Admin', 'ncmp-dmi-registry', '/dmi-registry', _, _) >>
{ args ->
assert args[3].contains('alt-1')
}
- mockAlternateIdChecker.canApplyAlternateId(cmHandleId, '','alt-1') >> isNewMapping
- where: 'following updates are attempted'
- scenario | isNewMapping || callsToDataService
- 'new alternate id ' | true || 1
- 'existing alternate id' | false || 0
+ and: 'one successful registration response'
+ response.size() == 1
+ and: 'the response shows success for the given cm handle id'
+ assert response[0].status == Status.SUCCESS
+ assert response[0].cmHandle == cmHandleId
}
- def 'Alternate ID removed from cache when persisting fails.'() {
- given: 'an existing data node and an update request with an alternate id'
- def ncmpServiceCmHandle = new NcmpServiceCmHandle(cmHandleId: cmHandleId, alternateId: 'alt-1')
- DataNode existingCmHandleDataNode = new DataNode(xpath: cmHandleXpath, leaves: ['id': cmHandleId, 'alternate-id': null])
- and: 'an applicable alternate id for the cm handle'
- mockAlternateIdChecker.canApplyAlternateId(cmHandleId, '','alt-1') >> true
- and: 'but an exception occurs while saving'
- def originalException = new NullPointerException('some exception')
- mockCpsDataService.updateNodeLeaves(*_) >> { throw originalException }
- when: 'updating of alternate id called'
- objectUnderTest.updateAlternateId(existingCmHandleDataNode, ncmpServiceCmHandle)
- then: 'the original exception is thrown up'
- def thrownException = thrown(NullPointerException)
- assert thrownException == originalException
+ def 'Update with rejected alternate id.'() {
+ given: 'cm handles request'
+ def updatedNcmpServiceCmHandles = [new NcmpServiceCmHandle(cmHandleId: cmHandleId, alternateId: 'alt-1')]
+ and: 'a data node found'
+ def dataNode = new DataNode(xpath: cmHandleXpath, leaves: ['id': cmHandleId, 'alternate-id': 'alt-1'])
+ mockInventoryPersistence.getCmHandleDataNodeByCmHandleId(cmHandleId) >> [dataNode]
+ when: 'attempt to update the cm handle'
+ def response = objectUnderTest.updateCmHandleProperties(updatedNcmpServiceCmHandles)
+ then: 'the update is NOT delegated to cps data service'
+ 0 * mockCpsDataService.updateNodeLeaves(*_)
+ and: 'the alternate id checker rejects the given cm handle (override default setup behavior)'
+ mockAlternateIdChecker.getIdsOfCmHandlesWithRejectedAlternateId(*_) >> [cmHandleId]
+ and: 'the response shows a failure for the given cm handle id'
+ assert response[0].status == Status.FAILURE
+ assert response[0].cmHandle == cmHandleId
}
def convertToProperties(expectedPropertiesAfterUpdateAsMap) {
postOperationWithJsonDataMethodCalled = true
}
when: 'get resource data for group of cm handles are invoked'
- objectUnderTest.requestResourceDataFromDmi('my-topic-name', dataOperationRequest, 'requestId')
+ objectUnderTest.requestResourceDataFromDmi('my-topic-name', dataOperationRequest, 'requestId', NO_AUTH_HEADER)
then: 'validate the post operation was called and ncmp generated dmi request body json args'
new PollingConditions().within(1) {
assert postOperationWithJsonDataMethodCalled == true
package org.onap.cps.ncmp.api.impl.utils
-
import org.onap.cps.ncmp.api.impl.inventory.InventoryPersistence
import org.onap.cps.ncmp.api.impl.yangmodels.YangModelCmHandle
import org.onap.cps.ncmp.api.models.NcmpServiceCmHandle
'other alternate id' || false
}
- def 'Check a batch of NEW cm handles with #scenario.'() {
+ def 'Check a batch of created cm handles with #scenario.'() {
given: 'a batch of 2 new cm handles alternate id ids #alt1 and #alt2'
def batch = [new NcmpServiceCmHandle(cmHandleId: 'ch-1', alternateId: alt1),
new NcmpServiceCmHandle(cmHandleId: 'ch-2', alternateId: alt2)]
- and: 'the database already contains cm handle(s) with these alternate ids: #alreadyinDb'
+ and: 'the database already contains cm handle(s) with these alternate ids: #altAlreadyInDb'
mockInventoryPersistenceService.getCmHandleDataNodeByAlternateId(_) >>
{ args -> altAlreadyInDb.contains(args[0]) ? new DataNode() : throwDataNodeNotFoundException() }
when: 'the batch of new cm handles is checked'
- def result = objectUnderTest.getIdsOfCmHandlesWithRejectedAlternateId(batch)
- then: 'the result only contains the ids of the acceptable cm handles'
- assert result.contains('ch-1') == rejectCh1
- assert result.contains('ch-2') == rejectCh2
+ def result = objectUnderTest.getIdsOfCmHandlesWithRejectedAlternateId(batch, AlternateIdChecker.Operation.CREATE)
+ then: 'the result contains ids of the rejected cm handles'
+ assert result == expectedRejectedCmHandleIds
where: 'the following alternate ids are used'
- scenario | alt1 | alt2 | altAlreadyInDb || rejectCh1 | rejectCh2
- 'no alternate ids' | '' | '' | ['dont matter'] || false | false
- 'new alternate ids' | 'fdn1' | 'fdn2' | ['other fdn'] || false | false
- 'one already used alternate id' | 'fdn1' | 'fdn2' | ['fdn1'] || true | false
- 'two already used alternate ids' | 'fdn1' | 'fdn2' | ['fdn1','fdn2'] || true | true
- 'duplicate alternate id in batch' | 'fdn1' | 'fdn1' | ['dont matter'] || false | true
+ scenario | alt1 | alt2 | altAlreadyInDb || expectedRejectedCmHandleIds
+ 'no alternate ids' | '' | '' | ['dont matter'] || []
+ 'new alternate ids' | 'fdn1' | 'fdn2' | ['other fdn'] || []
+ 'one already used alternate id' | 'fdn1' | 'fdn2' | ['fdn1'] || ['ch-1']
+ 'duplicate alternate id in batch' | 'fdn1' | 'fdn1' | ['dont matter'] || ['ch-2']
+ }
+
+ def 'Check a batch of updates to existing cm handles with #scenario.'() {
+ given: 'a batch of 1 existing cm handle update alternate id to #proposedAlt'
+ def batch = [new NcmpServiceCmHandle(cmHandleId: 'ch-1', alternateId: proposedAlt)]
+ and: 'the database already contains a cm handle with alternate id: #altAlreadyInDb'
+ mockInventoryPersistenceService.getCmHandleDataNodeByAlternateId(_) >>
+ { args -> altAlreadyInDb.equals(args[0]) ? new DataNode() : throwDataNodeNotFoundException() }
+ mockInventoryPersistenceService.getYangModelCmHandle(_) >> new YangModelCmHandle(alternateId: altAlreadyInDb)
+ when: 'the batch of cm handle updates is checked'
+ def result = objectUnderTest.getIdsOfCmHandlesWithRejectedAlternateId(batch, AlternateIdChecker.Operation.UPDATE)
+ then: 'the result contains ids of the rejected cm handles'
+ assert result == expectedRejectedCmHandleIds
+ where: 'the following parameters are used'
+ scenario | proposedAlt | altAlreadyInDb || expectedRejectedCmHandleIds
+ 'no alternate id' | 'fdn1' | '' || []
+ 'used the same alternate id' | 'fdn1' | 'fdn1' || []
+ 'used different alternate id' | 'otherFdn' | 'fdn1' || ['ch-1']
}
def throwDataNodeNotFoundException() {
<groupId>org.onap.cps</groupId>
<artifactId>cps-parent</artifactId>
- <version>3.4.6-SNAPSHOT</version>
+ <version>3.4.7-SNAPSHOT</version>
<packaging>pom</packaging>
<properties>
<parent>
<groupId>org.onap.cps</groupId>
<artifactId>cps-parent</artifactId>
- <version>3.4.6-SNAPSHOT</version>
+ <version>3.4.7-SNAPSHOT</version>
<relativePath>../cps-parent/pom.xml</relativePath>
</parent>
<parent>
<groupId>org.onap.cps</groupId>
<artifactId>cps-parent</artifactId>
- <version>3.4.6-SNAPSHOT</version>
+ <version>3.4.7-SNAPSHOT</version>
<relativePath>../cps-parent/pom.xml</relativePath>
</parent>
<parent>\r
<groupId>org.onap.cps</groupId>\r
<artifactId>cps-parent</artifactId>\r
- <version>3.4.6-SNAPSHOT</version>\r
+ <version>3.4.7-SNAPSHOT</version>\r
<relativePath>../cps-parent/pom.xml</relativePath>\r
</parent>\r
\r
<parent>
<groupId>org.onap.cps</groupId>
<artifactId>cps-parent</artifactId>
- <version>3.4.6-SNAPSHOT</version>
+ <version>3.4.7-SNAPSHOT</version>
<relativePath>../cps-parent/pom.xml</relativePath>
</parent>
<parent>
<groupId>org.onap.cps</groupId>
<artifactId>dmi-plugin-demo-and-csit-stub</artifactId>
- <version>3.4.6-SNAPSHOT</version>
+ <version>3.4.7-SNAPSHOT</version>
</parent>
<artifactId>dmi-plugin-demo-and-csit-stub-app</artifactId>
<parent>
<groupId>org.onap.cps</groupId>
<artifactId>dmi-plugin-demo-and-csit-stub</artifactId>
- <version>3.4.6-SNAPSHOT</version>
+ <version>3.4.7-SNAPSHOT</version>
</parent>
<artifactId>dmi-plugin-demo-and-csit-stub-service</artifactId>
<parent>
<groupId>org.onap.cps</groupId>
<artifactId>cps-parent</artifactId>
- <version>3.4.6-SNAPSHOT</version>
+ <version>3.4.7-SNAPSHOT</version>
<relativePath>../cps-parent/pom.xml</relativePath>
</parent>
schema:
$ref: '#/components/schemas/ErrorMessage'
description: Bad Request
- "401":
- content:
- application/json:
- example:
- status: 401
- message: Unauthorized request
- details: This request is unauthorized
- schema:
- $ref: '#/components/schemas/ErrorMessage'
- description: Unauthorized
"403":
content:
application/json:
schema:
$ref: '#/components/schemas/ErrorMessage'
description: Bad Request
- "401":
- content:
- application/json:
- example:
- status: 401
- message: Unauthorized request
- details: This request is unauthorized
- schema:
- $ref: '#/components/schemas/ErrorMessage'
- description: Unauthorized
"403":
content:
application/json:
schema:
$ref: '#/components/schemas/ErrorMessage'
description: Bad Request
- "401":
- content:
- application/json:
- example:
- status: 401
- message: Unauthorized request
- details: This request is unauthorized
- schema:
- $ref: '#/components/schemas/ErrorMessage'
- description: Unauthorized
"403":
content:
application/json:
schema:
$ref: '#/components/schemas/ErrorMessage'
description: Bad Request
- "401":
- content:
- application/json:
- example:
- status: 401
- message: Unauthorized request
- details: This request is unauthorized
- schema:
- $ref: '#/components/schemas/ErrorMessage'
- description: Unauthorized
"403":
content:
application/json:
schema:
$ref: '#/components/schemas/ErrorMessage'
description: Bad Request
- "401":
- content:
- application/json:
- example:
- status: 401
- message: Unauthorized request
- details: This request is unauthorized
- schema:
- $ref: '#/components/schemas/ErrorMessage'
- description: Unauthorized
"403":
content:
application/json:
schema:
$ref: '#/components/schemas/ErrorMessage'
description: Bad Request
- "401":
- content:
- application/json:
- example:
- status: 401
- message: Unauthorized request
- details: This request is unauthorized
- schema:
- $ref: '#/components/schemas/ErrorMessage'
- description: Unauthorized
"403":
content:
application/json:
schema:
$ref: '#/components/schemas/ErrorMessage'
description: Bad Request
- "401":
- content:
- application/json:
- example:
- status: 401
- message: Unauthorized request
- details: This request is unauthorized
- schema:
- $ref: '#/components/schemas/ErrorMessage'
- description: Unauthorized
"403":
content:
application/json:
schema:
$ref: '#/components/schemas/ErrorMessage'
description: Bad Request
- "401":
- content:
- application/json:
- example:
- status: 401
- message: Unauthorized request
- details: This request is unauthorized
- schema:
- $ref: '#/components/schemas/ErrorMessage'
- description: Unauthorized
"403":
content:
application/json:
schema:
$ref: '#/components/schemas/ErrorMessage'
description: Bad Request
- "401":
- content:
- application/json:
- example:
- status: 401
- message: Unauthorized request
- details: This request is unauthorized
- schema:
- $ref: '#/components/schemas/ErrorMessage'
- description: Unauthorized
"403":
content:
application/json:
schema:
$ref: '#/components/schemas/ErrorMessage'
description: Bad Request
- "401":
- content:
- application/json:
- example:
- status: 401
- message: Unauthorized request
- details: This request is unauthorized
- schema:
- $ref: '#/components/schemas/ErrorMessage'
- description: Unauthorized
"403":
content:
application/json:
schema:
$ref: '#/components/schemas/ErrorMessage'
description: Bad Request
- "401":
- content:
- application/json:
- example:
- status: 401
- message: Unauthorized request
- details: This request is unauthorized
- schema:
- $ref: '#/components/schemas/ErrorMessage'
- description: Unauthorized
"403":
content:
application/json:
schema:
$ref: '#/components/schemas/ErrorMessage'
description: Bad Request
- "401":
- content:
- application/json:
- example:
- status: 401
- message: Unauthorized request
- details: This request is unauthorized
- schema:
- $ref: '#/components/schemas/ErrorMessage'
- description: Unauthorized
"403":
content:
application/json:
schema:
$ref: '#/components/schemas/ErrorMessage'
description: Bad Request
- "401":
- content:
- application/json:
- example:
- status: 401
- message: Unauthorized request
- details: This request is unauthorized
- schema:
- $ref: '#/components/schemas/ErrorMessage'
- description: Unauthorized
"403":
content:
application/json:
schema:
$ref: '#/components/schemas/ErrorMessage'
description: Bad Request
- "401":
- content:
- application/json:
- example:
- status: 401
- message: Unauthorized request
- details: This request is unauthorized
- schema:
- $ref: '#/components/schemas/ErrorMessage'
- description: Unauthorized
"403":
content:
application/json:
schema:
$ref: '#/components/schemas/ErrorMessage'
description: Bad Request
- "401":
- content:
- application/json:
- example:
- status: 401
- message: Unauthorized request
- details: This request is unauthorized
- schema:
- $ref: '#/components/schemas/ErrorMessage'
- description: Unauthorized
"403":
content:
application/json:
schema:
$ref: '#/components/schemas/ErrorMessage'
description: Bad Request
- "401":
- content:
- application/json:
- example:
- status: 401
- message: Unauthorized request
- details: This request is unauthorized
- schema:
- $ref: '#/components/schemas/ErrorMessage'
- description: Unauthorized
"403":
content:
application/json:
schema:
$ref: '#/components/schemas/ErrorMessage'
description: Bad Request
- "401":
- content:
- application/json:
- example:
- status: 401
- message: Unauthorized request
- details: This request is unauthorized
- schema:
- $ref: '#/components/schemas/ErrorMessage'
- description: Unauthorized
"403":
content:
application/json:
schema:
$ref: '#/components/schemas/ErrorMessage'
description: Bad Request
- "401":
- content:
- application/json:
- example:
- status: 401
- message: Unauthorized request
- details: This request is unauthorized
- schema:
- $ref: '#/components/schemas/ErrorMessage'
- description: Unauthorized
"403":
content:
application/json:
schema:
$ref: '#/components/schemas/ErrorMessage'
description: Bad Request
- "401":
- content:
- application/json:
- example:
- status: 401
- message: Unauthorized request
- details: This request is unauthorized
- schema:
- $ref: '#/components/schemas/ErrorMessage'
- description: Unauthorized
"403":
content:
application/json:
schema:
$ref: '#/components/schemas/ErrorMessage'
description: Bad Request
- "401":
- content:
- application/json:
- example:
- status: 401
- message: Unauthorized request
- details: This request is unauthorized
- schema:
- $ref: '#/components/schemas/ErrorMessage'
- description: Unauthorized
"403":
content:
application/json:
schema:
$ref: '#/components/schemas/ErrorMessage'
description: Bad Request
- "401":
- content:
- application/json:
- example:
- status: 401
- message: Unauthorized request
- details: This request is unauthorized
- schema:
- $ref: '#/components/schemas/ErrorMessage'
- description: Unauthorized
"403":
content:
application/json:
schema:
$ref: '#/components/schemas/ErrorMessage'
description: Bad Request
- "401":
- content:
- application/json:
- example:
- status: 401
- message: Unauthorized request
- details: This request is unauthorized
- schema:
- $ref: '#/components/schemas/ErrorMessage'
- description: Unauthorized
"403":
content:
application/json:
schema:
$ref: '#/components/schemas/ErrorMessage'
description: Bad Request
- "401":
- content:
- application/json:
- example:
- status: 401
- message: Unauthorized request
- details: This request is unauthorized
- schema:
- $ref: '#/components/schemas/ErrorMessage'
- description: Unauthorized
"403":
content:
application/json:
schema:
$ref: '#/components/schemas/ErrorMessage'
description: Bad Request
- "401":
- content:
- application/json:
- example:
- status: 401
- message: Unauthorized request
- details: This request is unauthorized
- schema:
- $ref: '#/components/schemas/ErrorMessage'
- description: Unauthorized
"403":
content:
application/json:
schema:
$ref: '#/components/schemas/ErrorMessage'
description: Bad Request
- "401":
- content:
- application/json:
- example:
- status: 401
- message: Unauthorized request
- details: This request is unauthorized
- schema:
- $ref: '#/components/schemas/ErrorMessage'
- description: Unauthorized
"403":
content:
application/json:
schema:
$ref: '#/components/schemas/ErrorMessage'
description: Bad Request
- "401":
- content:
- application/json:
- example:
- status: 401
- message: Unauthorized request
- details: This request is unauthorized
- schema:
- $ref: '#/components/schemas/ErrorMessage'
- description: Unauthorized
"403":
content:
application/json:
schema:
$ref: '#/components/schemas/ErrorMessage'
description: Bad Request
- "401":
- content:
- application/json:
- example:
- status: 401
- message: Unauthorized request
- details: This request is unauthorized
- schema:
- $ref: '#/components/schemas/ErrorMessage'
- description: Unauthorized
"403":
content:
application/json:
schema:
$ref: '#/components/schemas/ErrorMessage'
description: Bad Request
- "401":
- content:
- application/json:
- example:
- status: 401
- message: Unauthorized request
- details: This request is unauthorized
- schema:
- $ref: '#/components/schemas/ErrorMessage'
- description: Unauthorized
"403":
content:
application/json:
schema:
$ref: '#/components/schemas/ErrorMessage'
description: Bad Request
- Unauthorized:
- content:
- application/json:
- example:
- status: 401
- message: Unauthorized request
- details: This request is unauthorized
- schema:
- $ref: '#/components/schemas/ErrorMessage'
- description: Unauthorized
Forbidden:
content:
application/json:
schema:
$ref: '#/components/schemas/ErrorMessage'
description: Bad Request
- "401":
- content:
- application/json:
- example:
- status: 401
- message: Unauthorized error message
- details: Unauthorized error details
- schema:
- $ref: '#/components/schemas/ErrorMessage'
- description: Unauthorized
"403":
content:
application/json:
type: string
type: array
description: OK
- "401":
- content:
- application/json:
- example:
- status: 401
- message: Unauthorized error message
- details: Unauthorized error details
- schema:
- $ref: '#/components/schemas/ErrorMessage'
- description: Unauthorized
"403":
content:
application/json:
type: string
type: array
description: OK
- "401":
- content:
- application/json:
- example:
- status: 401
- message: Unauthorized error message
- details: Unauthorized error details
- schema:
- $ref: '#/components/schemas/ErrorMessage'
- description: Unauthorized
"403":
content:
application/json:
schema:
$ref: '#/components/schemas/ErrorMessage'
description: Bad Request
- Unauthorized:
- content:
- application/json:
- example:
- status: 401
- message: Unauthorized error message
- details: Unauthorized error details
- schema:
- $ref: '#/components/schemas/ErrorMessage'
- description: Unauthorized
Forbidden:
content:
application/json:
default: application/json
example: application/yang-data+json
type: string
+ - description: Authorization parameter for request.
+ in: header
+ name: Authorization
+ required: false
+ schema:
+ type: string
responses:
"204":
content: {}
schema:
$ref: '#/components/schemas/ErrorMessage'
description: Bad Request
- "401":
- content:
- application/json:
- example:
- status: 401
- message: Unauthorized error message
- details: Unauthorized error details
- schema:
- $ref: '#/components/schemas/ErrorMessage'
- description: Unauthorized
"403":
content:
application/json:
schema:
default: false
type: boolean
+ - description: Authorization parameter for request.
+ in: header
+ name: Authorization
+ required: false
+ schema:
+ type: string
responses:
"200":
content:
schema:
$ref: '#/components/schemas/ErrorMessage'
description: Bad Request
- "401":
- content:
- application/json:
- example:
- status: 401
- message: Unauthorized error message
- details: Unauthorized error details
- schema:
- $ref: '#/components/schemas/ErrorMessage'
- description: Unauthorized
"403":
content:
application/json:
default: application/json
example: application/yang-data+json
type: string
+ - description: Authorization parameter for request.
+ in: header
+ name: Authorization
+ required: false
+ schema:
+ type: string
requestBody:
content:
'*/*':
schema:
$ref: '#/components/schemas/ErrorMessage'
description: Bad Request
- "401":
- content:
- application/json:
- example:
- status: 401
- message: Unauthorized error message
- details: Unauthorized error details
- schema:
- $ref: '#/components/schemas/ErrorMessage'
- description: Unauthorized
"403":
content:
application/json:
default: application/json
example: application/yang-data+json
type: string
+ - description: Authorization parameter for request.
+ in: header
+ name: Authorization
+ required: false
+ schema:
+ type: string
requestBody:
content:
application/json:
schema:
$ref: '#/components/schemas/ErrorMessage'
description: Bad Request
- "401":
- content:
- application/json:
- example:
- status: 401
- message: Unauthorized error message
- details: Unauthorized error details
- schema:
- $ref: '#/components/schemas/ErrorMessage'
- description: Unauthorized
"403":
content:
application/json:
default: application/json
example: application/yang-data+json
type: string
+ - description: Authorization parameter for request.
+ in: header
+ name: Authorization
+ required: false
+ schema:
+ type: string
requestBody:
content:
application/json:
schema:
$ref: '#/components/schemas/ErrorMessage'
description: Bad Request
- "401":
- content:
- application/json:
- example:
- status: 401
- message: Unauthorized error message
- details: Unauthorized error details
- schema:
- $ref: '#/components/schemas/ErrorMessage'
- description: Unauthorized
"403":
content:
application/json:
required: true
schema:
type: string
+ - description: Authorization parameter for request.
+ in: header
+ name: Authorization
+ required: false
+ schema:
+ type: string
requestBody:
content:
application/json:
schema:
$ref: '#/components/schemas/ErrorMessage'
description: Bad Request
- "401":
- content:
- application/json:
- example:
- status: 401
- message: Unauthorized error message
- details: Unauthorized error details
- schema:
- $ref: '#/components/schemas/ErrorMessage'
- description: Unauthorized
"403":
content:
application/json:
schema:
$ref: '#/components/schemas/ErrorMessage'
description: Bad Request
- "401":
- content:
- application/json:
- example:
- status: 401
- message: Unauthorized error message
- details: Unauthorized error details
- schema:
- $ref: '#/components/schemas/ErrorMessage'
- description: Unauthorized
"403":
content:
application/json:
schema:
$ref: '#/components/schemas/ErrorMessage'
description: Bad Request
- "401":
- content:
- application/json:
- example:
- status: 401
- message: Unauthorized error message
- details: Unauthorized error details
- schema:
- $ref: '#/components/schemas/ErrorMessage'
- description: Unauthorized
"403":
content:
application/json:
$ref: '#/components/schemas/RestModuleDefinition'
type: array
description: OK
- "401":
- content:
- application/json:
- example:
- status: 401
- message: Unauthorized error message
- details: Unauthorized error details
- schema:
- $ref: '#/components/schemas/ErrorMessage'
- description: Unauthorized
"403":
content:
application/json:
schema:
$ref: '#/components/schemas/ErrorMessage'
description: Bad Request
- "401":
- content:
- application/json:
- example:
- status: 401
- message: Unauthorized error message
- details: Unauthorized error details
- schema:
- $ref: '#/components/schemas/ErrorMessage'
- description: Unauthorized
"403":
content:
application/json:
schema:
$ref: '#/components/schemas/ErrorMessage'
description: Bad Request
- "401":
- content:
- application/json:
- example:
- status: 401
- message: Unauthorized error message
- details: Unauthorized error details
- schema:
- $ref: '#/components/schemas/ErrorMessage'
- description: Unauthorized
"404":
content:
application/json:
schema:
$ref: '#/components/schemas/ErrorMessage'
description: Bad Request
- "401":
- content:
- application/json:
- example:
- status: 401
- message: Unauthorized error message
- details: Unauthorized error details
- schema:
- $ref: '#/components/schemas/ErrorMessage'
- description: Unauthorized
"404":
content:
application/json:
schema:
$ref: '#/components/schemas/ErrorMessage'
description: Bad Request
- "401":
- content:
- application/json:
- example:
- status: 401
- message: Unauthorized error message
- details: Unauthorized error details
- schema:
- $ref: '#/components/schemas/ErrorMessage'
- description: Unauthorized
"403":
content:
application/json:
schema:
$ref: '#/components/schemas/ErrorMessage'
description: Bad Request
- "401":
- content:
- application/json:
- example:
- status: 401
- message: Unauthorized error message
- details: Unauthorized error details
- schema:
- $ref: '#/components/schemas/ErrorMessage'
- description: Unauthorized
"404":
content:
application/json:
schema:
$ref: '#/components/schemas/ErrorMessage'
description: Bad Request
- "401":
- content:
- application/json:
- example:
- status: 401
- message: Unauthorized error message
- details: Unauthorized error details
- schema:
- $ref: '#/components/schemas/ErrorMessage'
- description: Unauthorized
"403":
content:
application/json:
schema:
default: false
type: boolean
+ authorizationParamInHeader:
+ description: Authorization parameter for request.
+ in: header
+ name: Authorization
+ required: false
+ schema:
+ type: string
contentParamInHeader:
description: "Content parameter for request, if content parameter is null, default\
\ value is application/json."
schema:
$ref: '#/components/schemas/ErrorMessage'
description: Bad Request
- Unauthorized:
- content:
- application/json:
- example:
- status: 401
- message: Unauthorized error message
- details: Unauthorized error details
- schema:
- $ref: '#/components/schemas/ErrorMessage'
- description: Unauthorized
Forbidden:
content:
application/json:
.. * * * NEW DELHI * * *
.. =========================
+Version: 3.4.7
+==============
+
+Release Data
+------------
+
++--------------------------------------+--------------------------------------------------------+
+| **CPS Project** | |
+| | |
++--------------------------------------+--------------------------------------------------------+
+| **Docker images** | onap/cps-and-ncmp:3.4.7 |
+| | |
++--------------------------------------+--------------------------------------------------------+
+| **Release designation** | 3.4.7 New Delhi |
+| | |
++--------------------------------------+--------------------------------------------------------+
+| **Release date** | Not yet released |
+| | |
++--------------------------------------+--------------------------------------------------------+
+
+Bug Fixes
+---------
+3.4.7
+
+Features
+--------
+
Version: 3.4.6
==============
| **Release designation** | 3.4.6 New Delhi |
| | |
+--------------------------------------+--------------------------------------------------------+
-| **Release date** | Not yet released |
+| **Release date** | 2024 February 29 |
| | |
+--------------------------------------+--------------------------------------------------------+
Bug Fixes
---------
3.4.6
+ - `CPS-2126 <https://jira.onap.org/browse/CPS-2126>`_ Passing HTTP Authorization Bearer Token to DMI Plugins.
Features
--------
+ - `CPS-2133 <https://jira.onap.org/browse/CPS-2133>`_ Revert Uplift of Spring Boot version from 3.2.2 to 3.1.2
+
+Notes
+-----
+This release brings improvements to compatibility with Service Mesh and for that below measures are been taken.
+
+Basic authorization provided using Spring security is been removed from CPS-Core and NCMP and hence authorization is no longer enforced.(basic auth header will be ignored, but is still allowed).
+NCMP will propagate a bearer token to DMI conditionally.
+401 Unauthorized will not be returned. Best effort has been made to ensure backwards compatibility.
Version: 3.4.5
==============
<parent>
<groupId>org.onap.cps</groupId>
<artifactId>cps-parent</artifactId>
- <version>3.4.6-SNAPSHOT</version>
+ <version>3.4.7-SNAPSHOT</version>
<relativePath>../cps-parent/pom.xml</relativePath>
</parent>
<modelVersion>4.0.0</modelVersion>
import org.springframework.beans.factory.annotation.Autowired
import org.springframework.boot.autoconfigure.EnableAutoConfiguration
import org.springframework.boot.autoconfigure.domain.EntityScan
+import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc
import org.springframework.boot.test.context.SpringBootTest
import org.springframework.context.annotation.ComponentScan
import org.springframework.data.jpa.repository.config.EnableJpaRepositories
import org.springframework.http.HttpStatus
import org.springframework.http.MediaType
import org.springframework.test.web.client.MockRestServiceServer
+import org.springframework.test.web.servlet.MockMvc
import org.springframework.web.client.RestTemplate
import org.testcontainers.spock.Testcontainers
import spock.lang.Shared
import static org.springframework.test.web.client.match.MockRestRequestMatchers.requestTo
import static org.springframework.test.web.client.response.MockRestResponseCreators.withStatus
-@SpringBootTest(classes = [CpsDataspaceService])
+@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.MOCK, classes = [CpsDataspaceService])
@Testcontainers
@EnableAutoConfiguration
+@AutoConfigureMockMvc
@EnableJpaRepositories(basePackageClasses = [DataspaceRepository])
@ComponentScan(basePackages = ['org.onap.cps'])
@EntityScan('org.onap.cps.spi.entities')
@Shared
DatabaseTestContainer databaseTestContainer = DatabaseTestContainer.getInstance()
+ @Autowired
+ MockMvc mvc;
+
@Autowired
CpsDataspaceService cpsDataspaceService
--- /dev/null
+/*
+ * ============LICENSE_START=======================================================
+ * Copyright (C) 2024 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.integration.functional
+
+import java.time.Duration
+import org.onap.cps.integration.base.CpsIntegrationSpecBase
+import org.springframework.http.HttpHeaders
+import org.springframework.http.HttpStatus
+import org.springframework.http.MediaType
+import org.springframework.test.web.client.match.MockRestRequestMatchers
+
+import static org.springframework.http.HttpMethod.GET
+import static org.springframework.http.HttpMethod.DELETE
+import static org.springframework.http.HttpMethod.PATCH
+import static org.springframework.http.HttpMethod.POST
+import static org.springframework.http.HttpMethod.PUT
+import static org.springframework.test.web.client.match.MockRestRequestMatchers.method
+import static org.springframework.test.web.client.match.MockRestRequestMatchers.requestTo
+import static org.springframework.test.web.client.response.MockRestResponseCreators.withStatus
+import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.request
+import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status
+
+class NcmpBearerTokenPassthroughSpec extends CpsIntegrationSpecBase {
+
+ static final NO_MODULE_SET_TAG = ''
+ static final MODULE_REFERENCES_RESPONSE = readResourceDataFile('mock-dmi-responses/bookStoreAWithModules_M1_M2_Response.json')
+ static final MODULE_RESOURCES_RESPONSE = readResourceDataFile('mock-dmi-responses/bookStoreAWithModules_M1_M2_ResourcesResponse.json')
+
+ def setup() {
+ registerCmHandle(DMI_URL, 'ch-1', NO_MODULE_SET_TAG, MODULE_REFERENCES_RESPONSE, MODULE_RESOURCES_RESPONSE)
+ }
+
+ def cleanup() {
+ deregisterCmHandle(DMI_URL, 'ch-1')
+ }
+
+ def 'Bearer token is passed from NCMP to DMI in pass-through data operations.'() {
+ given: 'DMI will expect to receive a request with a bearer token'
+ def targetDmiUrl = "$DMI_URL/dmi/v1/ch/ch-1/data/ds/ncmp-datastore:passthrough-running?resourceIdentifier=my-resource-id"
+ mockDmiServer.expect(requestTo(targetDmiUrl))
+ .andExpect(MockRestRequestMatchers.header(HttpHeaders.AUTHORIZATION, 'Bearer some-bearer-token'))
+ .andRespond(withStatus(HttpStatus.OK).contentType(MediaType.APPLICATION_JSON))
+
+ when: 'a pass-through data request is sent to NCMP with a bearer token'
+ mvc.perform(request(httpMethod, '/ncmp/v1/ch/ch-1/data/ds/ncmp-datastore:passthrough-running')
+ .queryParam('resourceIdentifier', 'my-resource-id')
+ .contentType(MediaType.APPLICATION_JSON)
+ .content('{ "some-json": "data" }')
+ .header(HttpHeaders.AUTHORIZATION, 'Bearer some-bearer-token'))
+ .andExpect(status().is2xxSuccessful())
+
+ then: 'DMI has received request with bearer token'
+ mockDmiServer.verify()
+
+ where: 'all HTTP operations are applied'
+ httpMethod << [GET, POST, PUT, PATCH, DELETE]
+ }
+
+ def 'Basic auth header is NOT passed from NCMP to DMI in pass-through data operations.'() {
+ given: 'DMI will expect to receive a request with no authorization header'
+ def targetDmiUrl = "$DMI_URL/dmi/v1/ch/ch-1/data/ds/ncmp-datastore:passthrough-running?resourceIdentifier=my-resource-id"
+ mockDmiServer.expect(requestTo(targetDmiUrl))
+ .andExpect(MockRestRequestMatchers.headerDoesNotExist(HttpHeaders.AUTHORIZATION))
+ .andRespond(withStatus(HttpStatus.OK).contentType(MediaType.APPLICATION_JSON))
+
+ when: 'a pass-through data request is sent to NCMP with basic authentication'
+ mvc.perform(request(httpMethod, '/ncmp/v1/ch/ch-1/data/ds/ncmp-datastore:passthrough-running')
+ .queryParam('resourceIdentifier', 'my-resource-id')
+ .contentType(MediaType.APPLICATION_JSON)
+ .content('{ "some-json": "data" }')
+ .header(HttpHeaders.AUTHORIZATION, 'Basic Y3BzdXNlcjpjcHNyMGNrcyE='))
+ .andExpect(status().is2xxSuccessful())
+
+ then: 'DMI has received request with no authorization header'
+ mockDmiServer.verify()
+
+ where: 'all HTTP operations are applied'
+ httpMethod << [GET, POST, PUT, PATCH, DELETE]
+ }
+
+ def 'Bearer token is passed from NCMP to DMI in async batch pass-through data operation.'() {
+ given: 'DMI will expect to receive a request with a bearer token'
+ mockDmiServer.expect(method(POST))
+ .andExpect(MockRestRequestMatchers.header(HttpHeaders.AUTHORIZATION, 'Bearer some-bearer-token'))
+ .andRespond(withStatus(HttpStatus.OK).contentType(MediaType.APPLICATION_JSON))
+
+ when: 'a pass-through async data request is sent to NCMP with a bearer token'
+ def requestBody = """{"operations": [{
+ "operation": "read",
+ "operationId": "operational-1",
+ "datastore": "ncmp-datastore:passthrough-running",
+ "resourceIdentifier": "my-resource-id",
+ "targetIds": ["ch-1"]
+ }]}"""
+ mvc.perform(request(POST, '/ncmp/v1/data')
+ .queryParam('topic', 'my-topic')
+ .contentType(MediaType.APPLICATION_JSON)
+ .content(requestBody)
+ .header(HttpHeaders.AUTHORIZATION, 'Bearer some-bearer-token'))
+ .andExpect(status().is2xxSuccessful())
+
+ then: 'DMI will receive the async request with bearer token'
+ mockDmiServer.verify(Duration.ofSeconds(1))
+ }
+
+}
topic: ${DMI_DEVICE_HEARTBEAT_TOPIC:dmi-device-heartbeat}
notification:
- enabled: false
+ enabled: true
async:
executor:
core-pool-size: 2
<parent>
<groupId>org.onap.cps</groupId>
<artifactId>cps-parent</artifactId>
- <version>3.4.6-SNAPSHOT</version>
+ <version>3.4.7-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>3.4.6-SNAPSHOT</version>\r
+ <version>3.4.7-SNAPSHOT</version>\r
<packaging>pom</packaging>\r
\r
<name>cps</name>\r
--- /dev/null
+distribution_type: container
+container_release_tag: 3.4.6
+project: cps
+log_dir: cps-maven-docker-stage-master/938/
+ref: 4a978d3c66da16bc96b54cba807138fc9b0c79fa
+containers:
+ - name: 'cps-and-ncmp'
+ version: '3.4.6-20240229T165311Z'
--- /dev/null
+distribution_type: maven
+log_dir: cps-maven-stage-master/946/
+project: cps
+version: 3.4.6
\ No newline at end of file
<modelVersion>4.0.0</modelVersion>
<groupId>org.onap.cps</groupId>
<artifactId>spotbugs</artifactId>
- <version>3.4.6-SNAPSHOT</version>
+ <version>3.4.7-SNAPSHOT</version>
<properties>
<nexusproxy>https://nexus.onap.org</nexusproxy>
major=3
minor=4
-patch=6
+patch=7
base_version=${major}.${minor}.${patch}