diff options
author | Toine Siebelink <toine.siebelink@est.tech> | 2023-05-29 08:52:22 +0000 |
---|---|---|
committer | Gerrit Code Review <gerrit@onap.org> | 2023-05-29 08:52:22 +0000 |
commit | 325ac091ddea291c5659fa6e803f132a578deb14 (patch) | |
tree | 3b06c9f393e97e54ae26ef7038123cc750df937c /cps-ncmp-service/src/main/java | |
parent | acae85ffc8eac8dd919b59b1fc804a1d3743eb0f (diff) | |
parent | f232f30bede7d35c71db2d0201695a1416e37323 (diff) |
Merge "NCMP: Update existing Batch endpoint (Moving url param into rest body)"
Diffstat (limited to 'cps-ncmp-service/src/main/java')
14 files changed, 567 insertions, 163 deletions
diff --git a/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/NetworkCmProxyDataService.java b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/NetworkCmProxyDataService.java index 05490d8fc9..046c78879b 100644 --- a/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/NetworkCmProxyDataService.java +++ b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/NetworkCmProxyDataService.java @@ -24,15 +24,15 @@ package org.onap.cps.ncmp.api; import java.util.Collection; -import java.util.List; import java.util.Map; -import org.onap.cps.ncmp.api.impl.operations.OperationEnum; +import org.onap.cps.ncmp.api.impl.operations.OperationType; import org.onap.cps.ncmp.api.inventory.CompositeState; import org.onap.cps.ncmp.api.models.CmHandleQueryApiParameters; import org.onap.cps.ncmp.api.models.CmHandleQueryServiceParameters; import org.onap.cps.ncmp.api.models.DmiPluginRegistration; import org.onap.cps.ncmp.api.models.DmiPluginRegistrationResponse; import org.onap.cps.ncmp.api.models.NcmpServiceCmHandle; +import org.onap.cps.ncmp.api.models.ResourceDataBatchRequest; import org.onap.cps.spi.FetchDescendantsOption; import org.onap.cps.spi.model.ModuleDefinition; import org.onap.cps.spi.model.ModuleReference; @@ -83,22 +83,15 @@ public interface NetworkCmProxyDataService { FetchDescendantsOption fetchDescendantsOption); /** - * Get resource data for given batch of cm handles using dmi. + * Get resource data for batch of cm handles using dmi. * - * @param datastoreName datastore name - * @param cmHandleIds cm handle identifiers - * @param resourceIdentifier resource identifier - * @param optionsParamInQuery options query - * @param topicParamInQuery topic name for (triggering) async responses - * @param requestId unique requestId for async request - * @return {@code Object} resource data + * @param topicParamInQuery topic name for (triggering) async responses + * @param resourceDataBatchRequest cm handle identifiers */ - Object getResourceDataForCmHandleBatch(String datastoreName, - List<String> cmHandleIds, - String resourceIdentifier, - String optionsParamInQuery, - String topicParamInQuery, - String requestId); + void requestResourceDataForCmHandleBatch(String topicParamInQuery, + ResourceDataBatchRequest + resourceDataBatchRequest, + String requestId); /** @@ -106,14 +99,14 @@ public interface NetworkCmProxyDataService { * * @param cmHandleId cm handle identifier * @param resourceIdentifier resource identifier - * @param operation required operation + * @param operationType required operation type * @param requestBody request body to create resource * @param contentType content type in body * @return {@code Object} return data */ Object writeResourceDataPassThroughRunningForCmHandle(String cmHandleId, String resourceIdentifier, - OperationEnum operation, + OperationType operationType, String requestBody, String contentType); diff --git a/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/NetworkCmProxyDataServiceImpl.java b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/NetworkCmProxyDataServiceImpl.java index e478b0053b..536775ec5c 100755 --- a/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/NetworkCmProxyDataServiceImpl.java +++ b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/NetworkCmProxyDataServiceImpl.java @@ -45,7 +45,7 @@ import org.onap.cps.ncmp.api.NetworkCmProxyCmHandleQueryService; import org.onap.cps.ncmp.api.NetworkCmProxyDataService; import org.onap.cps.ncmp.api.impl.events.lcm.LcmEventsCmHandleStateHandler; import org.onap.cps.ncmp.api.impl.operations.DmiDataOperations; -import org.onap.cps.ncmp.api.impl.operations.OperationEnum; +import org.onap.cps.ncmp.api.impl.operations.OperationType; import org.onap.cps.ncmp.api.impl.utils.CmHandleQueryConditions; import org.onap.cps.ncmp.api.impl.utils.InventoryQueryConditions; import org.onap.cps.ncmp.api.impl.utils.YangDataConverter; @@ -63,6 +63,7 @@ import org.onap.cps.ncmp.api.models.CmHandleRegistrationResponse.RegistrationErr import org.onap.cps.ncmp.api.models.DmiPluginRegistration; import org.onap.cps.ncmp.api.models.DmiPluginRegistrationResponse; import org.onap.cps.ncmp.api.models.NcmpServiceCmHandle; +import org.onap.cps.ncmp.api.models.ResourceDataBatchRequest; import org.onap.cps.spi.FetchDescendantsOption; import org.onap.cps.spi.exceptions.AlreadyDefinedExceptionBatch; import org.onap.cps.spi.exceptions.CpsException; @@ -138,28 +139,21 @@ public class NetworkCmProxyDataServiceImpl implements NetworkCmProxyDataService } @Override - public Object getResourceDataForCmHandleBatch(final String datastoreName, - final List<String> cmHandleIds, - final String resourceIdentifier, - final String optionsParamInQuery, - final String topicParamInQuery, - final String requestId) { - final ResponseEntity<?> responseEntity = dmiDataOperations.getResourceDataFromDmi(datastoreName, cmHandleIds, - resourceIdentifier, - optionsParamInQuery, - topicParamInQuery, - requestId); - return responseEntity.getBody(); + public void requestResourceDataForCmHandleBatch(final String topicParamInQuery, + final ResourceDataBatchRequest + resourceDataBatchRequest, + final String requestId) { + dmiDataOperations.requestResourceDataFromDmi(topicParamInQuery, resourceDataBatchRequest, requestId); } @Override public Object writeResourceDataPassThroughRunningForCmHandle(final String cmHandleId, final String resourceIdentifier, - final OperationEnum operation, + final OperationType operationType, final String requestData, final String dataType) { - return dmiDataOperations.writeResourceDataPassThroughRunningFromDmi(cmHandleId, resourceIdentifier, operation, - requestData, dataType); + return dmiDataOperations.writeResourceDataPassThroughRunningFromDmi(cmHandleId, resourceIdentifier, + operationType, requestData, dataType); } @Override diff --git a/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/client/DmiRestClient.java b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/client/DmiRestClient.java index 9d087806c0..136935ba53 100644 --- a/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/client/DmiRestClient.java +++ b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/client/DmiRestClient.java @@ -24,7 +24,7 @@ package org.onap.cps.ncmp.api.impl.client; import lombok.AllArgsConstructor; import org.onap.cps.ncmp.api.impl.config.NcmpConfiguration.DmiProperties; import org.onap.cps.ncmp.api.impl.exception.HttpClientRequestException; -import org.onap.cps.ncmp.api.impl.operations.OperationEnum; +import org.onap.cps.ncmp.api.impl.operations.OperationType; import org.springframework.http.HttpEntity; import org.springframework.http.HttpHeaders; import org.springframework.http.MediaType; @@ -44,17 +44,17 @@ public class DmiRestClient { * Sends POST operation to DMI with json body containing module references. * @param dmiResourceUrl dmi resource url * @param requestBodyAsJsonString json data body - * @param operation the type of operation being executed (for error reporting only) + * @param operationType the type of operation being executed (for error reporting only) * @return response entity of type String */ public ResponseEntity<Object> postOperationWithJsonData(final String dmiResourceUrl, final String requestBodyAsJsonString, - final OperationEnum operation) { + final OperationType operationType) { final var httpEntity = new HttpEntity<>(requestBodyAsJsonString, configureHttpHeaders(new HttpHeaders())); try { return restTemplate.postForEntity(dmiResourceUrl, httpEntity, Object.class); } catch (final HttpStatusCodeException httpStatusCodeException) { - final String exceptionMessage = "Unable to " + operation.toString() + " resource data."; + final String exceptionMessage = "Unable to " + operationType.toString() + " resource data."; throw new HttpClientRequestException(exceptionMessage, httpStatusCodeException.getResponseBodyAsString(), httpStatusCodeException.getRawStatusCode()); } diff --git a/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/operations/OperationEnum.java b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/exception/InvalidOperationException.java index 796cef23d0..17069098cb 100644 --- a/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/operations/OperationEnum.java +++ b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/exception/InvalidOperationException.java @@ -18,26 +18,15 @@ * ============LICENSE_END========================================================= */ -package org.onap.cps.ncmp.api.impl.operations; +package org.onap.cps.ncmp.api.impl.exception; -import com.fasterxml.jackson.annotation.JsonValue; - -public enum OperationEnum { - - READ("read"), - CREATE("create"), - UPDATE("update"), - PATCH("patch"), - DELETE("delete"); - private final String value; - - OperationEnum(final String value) { - this.value = value; - } - - @Override - @JsonValue - public String toString() { - return String.valueOf(value); +public class InvalidOperationException extends RuntimeException { + /** + * Instantiates a new invalid operation exception. + * + * @param message the message + */ + public InvalidOperationException(final String message) { + super(message); } } diff --git a/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/operations/CmHandle.java b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/operations/CmHandle.java new file mode 100644 index 0000000000..618da74543 --- /dev/null +++ b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/operations/CmHandle.java @@ -0,0 +1,42 @@ +/* + * ============LICENSE_START======================================================= + * Copyright (C) 2023 Nordix Foundation + * ================================================================================ + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * SPDX-License-Identifier: Apache-2.0 + * ============LICENSE_END========================================================= + */ + +package org.onap.cps.ncmp.api.impl.operations; + +import com.fasterxml.jackson.annotation.JsonInclude; +import com.fasterxml.jackson.annotation.JsonProperty; +import java.util.Map; +import lombok.Builder; +import lombok.Getter; + +@JsonInclude(JsonInclude.Include.NON_NULL) +@Getter +@Builder +public class CmHandle { + private String id; + + @JsonProperty("cmHandleProperties") + private Map<String, String> dmiProperties; + + public static CmHandle buildCmHandleWithProperties(final String cmHandleId, + final Map<String, String> dmiProperties) { + return CmHandle.builder().id(cmHandleId).dmiProperties(dmiProperties).build(); + } +} diff --git a/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/operations/DmiBatchOperation.java b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/operations/DmiBatchOperation.java new file mode 100644 index 0000000000..76ad0f7b2e --- /dev/null +++ b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/operations/DmiBatchOperation.java @@ -0,0 +1,64 @@ +/* + * ============LICENSE_START======================================================= + * Copyright (C) 2023 Nordix Foundation + * ================================================================================ + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * SPDX-License-Identifier: Apache-2.0 + * ============LICENSE_END========================================================= + */ + +package org.onap.cps.ncmp.api.impl.operations; + +import com.fasterxml.jackson.annotation.JsonInclude; +import com.fasterxml.jackson.annotation.JsonProperty; +import com.fasterxml.jackson.annotation.JsonPropertyOrder; +import java.util.ArrayList; +import java.util.List; +import lombok.Builder; +import lombok.Getter; +import org.onap.cps.ncmp.api.models.BatchOperationDefinition; + +@JsonInclude(JsonInclude.Include.NON_NULL) +@Getter +@Builder +@JsonPropertyOrder({"operation", "operationId", "datastore", "options", "resourceIdentifier", "cmHandles"}) +public class DmiBatchOperation { + + @JsonProperty("operation") + private OperationType operationType; + private String operationId; + private String datastore; + private String options; + private String resourceIdentifier; + + private final List<CmHandle> cmHandles = new ArrayList<>(); + + /** + * Create and initialise a (outgoing) DMI batch operation. + * + * @param batchOperationDefinition batchOperationDefinition definition of incoming of batch request + * @return mapped dmi operation details + */ + public static DmiBatchOperation buildDmiBatchRequestBodyWithoutCmHandles( + final BatchOperationDefinition batchOperationDefinition) { + + return DmiBatchOperation.builder() + .operationType(OperationType.fromOperationName(batchOperationDefinition.getOperation())) + .operationId(batchOperationDefinition.getOperationId()) + .datastore(DatastoreType.fromDatastoreName(batchOperationDefinition.getDatastore()).getDatastoreName()) + .options(batchOperationDefinition.getOptions()) + .resourceIdentifier(batchOperationDefinition.getResourceIdentifier()) + .build(); + } +} diff --git a/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/operations/DmiDataOperations.java b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/operations/DmiDataOperations.java index 1a3952306f..3e8d40a83b 100644 --- a/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/operations/DmiDataOperations.java +++ b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/operations/DmiDataOperations.java @@ -22,25 +22,28 @@ package org.onap.cps.ncmp.api.impl.operations; import static org.onap.cps.ncmp.api.impl.operations.DatastoreType.PASSTHROUGH_RUNNING; -import static org.onap.cps.ncmp.api.impl.operations.OperationEnum.READ; +import static org.onap.cps.ncmp.api.impl.operations.OperationType.READ; import java.util.Collection; import java.util.List; import java.util.Map; +import java.util.Set; +import java.util.stream.Collectors; import lombok.extern.slf4j.Slf4j; import org.onap.cps.ncmp.api.impl.client.DmiRestClient; import org.onap.cps.ncmp.api.impl.config.NcmpConfiguration; import org.onap.cps.ncmp.api.impl.executor.TaskExecutor; -import org.onap.cps.ncmp.api.impl.utils.DmiServiceNameOrganizer; import org.onap.cps.ncmp.api.impl.utils.DmiServiceUrlBuilder; +import org.onap.cps.ncmp.api.impl.utils.ResourceDataBatchRequestUtils; import org.onap.cps.ncmp.api.impl.yangmodels.YangModelCmHandle; import org.onap.cps.ncmp.api.inventory.CmHandleState; import org.onap.cps.ncmp.api.inventory.InventoryPersistence; +import org.onap.cps.ncmp.api.models.ResourceDataBatchRequest; import org.onap.cps.spi.exceptions.CpsException; import org.onap.cps.utils.JsonObjectMapper; -import org.springframework.http.HttpStatus; import org.springframework.http.ResponseEntity; import org.springframework.stereotype.Component; +import org.springframework.util.MultiValueMap; /** * Operations class for DMI data. @@ -50,7 +53,6 @@ import org.springframework.stereotype.Component; public class DmiDataOperations extends DmiOperations { private static final long DEFAULT_ASYNC_TASK_EXECUTOR_TIMEOUT_IN_MILLISECONDS = 30000L; - private static final String NO_CM_HANDLE_ID = ""; public DmiDataOperations(final InventoryPersistence inventoryPersistence, final JsonObjectMapper jsonObjectMapper, @@ -89,39 +91,12 @@ public class DmiDataOperations extends DmiOperations { } /** - * This method fetches the resource data by data store for given list of cm handles using dmi client. - * - * @param dataStoreName data store name - * @param cmHandleIds list of cm handles - * @param resourceId resource identifier - * @param optionsParamInQuery options query - * @param topicParamInQuery topic name for (triggering) async responses - * @param requestId requestId for async responses - * @return {@code ResponseEntity} response entity - */ - public ResponseEntity<Object> getResourceDataFromDmi(final String dataStoreName, - final List<String> cmHandleIds, - final String resourceId, - final String optionsParamInQuery, - final String topicParamInQuery, - final String requestId) { - final Collection<YangModelCmHandle> yangModelCmHandles - = inventoryPersistence.getYangModelCmHandles(cmHandleIds); - final Map<String, Map<String, Map<String, String>>> dmiServiceNameCmHandlePropertiesMap = - DmiServiceNameOrganizer.getDmiPropertiesPerCmHandleIdPerServiceName(yangModelCmHandles); - - buildBulkResourceDataRequestAndSend(dataStoreName, resourceId, optionsParamInQuery, - topicParamInQuery, requestId, dmiServiceNameCmHandlePropertiesMap); - return new ResponseEntity<>(HttpStatus.ACCEPTED); - } - - /** * This method fetches all the resource data from operational data store for given cm handle * identifier using dmi client. * - * @param dataStoreName data store name - * @param cmHandleId network resource identifier - * @param requestId requestId for async responses + * @param dataStoreName data store name + * @param cmHandleId network resource identifier + * @param requestId requestId for async responses * @return {@code ResponseEntity} response entity */ public ResponseEntity<Object> getResourceDataFromDmi(final String dataStoreName, @@ -130,12 +105,37 @@ public class DmiDataOperations extends DmiOperations { final YangModelCmHandle yangModelCmHandle = getYangModelCmHandle(cmHandleId); final String jsonRequestBody = getDmiRequestBody(READ, requestId, null, null, yangModelCmHandle); - final String dmiResourceDataUrl = getDmiRequestUrl(dataStoreName, cmHandleId, "/", null, - null, yangModelCmHandle.resolveDmiServiceName(RequiredDmiService.DATA)); + final String dmiResourceDataUrl = getDmiRequestUrl(dataStoreName, cmHandleId, "/", + null, null, + yangModelCmHandle.resolveDmiServiceName(RequiredDmiService.DATA)); final CmHandleState cmHandleState = yangModelCmHandle.getCompositeState().getCmHandleState(); validateIfCmHandleStateReady(yangModelCmHandle, cmHandleState); - return dmiRestClient.postOperationWithJsonData(dmiResourceDataUrl, jsonRequestBody, - READ); + return dmiRestClient.postOperationWithJsonData(dmiResourceDataUrl, jsonRequestBody, READ); + } + + /** + * This method requests the resource data by data store for given list of cm handles using dmi client. + * The data wil be returned as message on the topic specified. + * + * @param topicParamInQuery topic name for (triggering) async responses + * @param resourceDataBatchRequest batch request for resource data + * @param requestId requestId for as a response + */ + public void requestResourceDataFromDmi(final String topicParamInQuery, + final ResourceDataBatchRequest resourceDataBatchRequest, + final String requestId) { + + final Set<String> cmHandlesIds + = getDistinctCmHandleIdsFromBatchRequest(resourceDataBatchRequest); + + final Collection<YangModelCmHandle> yangModelCmHandles + = getYangModelCmHandlesInReadyState(cmHandlesIds); + + final Map<String, List<DmiBatchOperation>> operationsOutPerDmiServiceName + = ResourceDataBatchRequestUtils.processPerOperationInBatchRequest(resourceDataBatchRequest, + yangModelCmHandles); + + buildBatchRequestUrlAndSendToDmiService(topicParamInQuery, requestId, operationsOutPerDmiServiceName); } /** @@ -143,36 +143,39 @@ public class DmiDataOperations extends DmiOperations { * identifier on given resource using dmi client. * * @param cmHandleId network resource identifier - * @param resourceId resource identifier - * @param operation operation enum - * @param requestData the request data - * @param dataType data type + * @param resourceId resource identifier + * @param operationType operation enum + * @param requestData the request data + * @param dataType data type * @return {@code ResponseEntity} response entity */ public ResponseEntity<Object> writeResourceDataPassThroughRunningFromDmi(final String cmHandleId, final String resourceId, - final OperationEnum operation, + final OperationType operationType, final String requestData, final String dataType) { final YangModelCmHandle yangModelCmHandle = getYangModelCmHandle(cmHandleId); - final String jsonRequestBody = getDmiRequestBody(operation, null, requestData, dataType, + final String jsonRequestBody = getDmiRequestBody(operationType, null, requestData, dataType, yangModelCmHandle); final String dmiUrl = getDmiRequestUrl(PASSTHROUGH_RUNNING.getDatastoreName(), cmHandleId, resourceId, null, null, yangModelCmHandle.resolveDmiServiceName(RequiredDmiService.DATA)); final CmHandleState cmHandleState = yangModelCmHandle.getCompositeState().getCmHandleState(); validateIfCmHandleStateReady(yangModelCmHandle, cmHandleState); - return dmiRestClient.postOperationWithJsonData(dmiUrl, jsonRequestBody, operation); + return dmiRestClient.postOperationWithJsonData(dmiUrl, jsonRequestBody, operationType); } private YangModelCmHandle getYangModelCmHandle(final String cmHandleId) { return inventoryPersistence.getYangModelCmHandle(cmHandleId); } - private String getDmiRequestBody(final OperationEnum operation, final String requestId, final String requestData, - final String dataType, final YangModelCmHandle yangModelCmHandle) { + private String getDmiRequestBody(final OperationType operationType, + final String requestId, + final String requestData, + final String dataType, + final YangModelCmHandle yangModelCmHandle) { final DmiRequestBody dmiRequestBody = DmiRequestBody.builder() - .operation(operation) + .operationType(operationType) .requestId(requestId) .data(requestData) .dataType(dataType) @@ -181,17 +184,6 @@ public class DmiDataOperations extends DmiOperations { return jsonObjectMapper.asJsonString(dmiRequestBody); } - private String getDmiBulkRequestBody(final OperationEnum operation, - final String requestId, - final String requestData) { - final DmiRequestBody dmiBulkRequestBody = DmiRequestBody.builder() - .operation(operation) - .requestId(requestId) - .data(requestData) - .build(); - return jsonObjectMapper.asJsonString(dmiBulkRequestBody); - } - private String getDmiRequestUrl(final String dataStoreName, final String cmHandleId, final String resourceId, @@ -204,15 +196,13 @@ public class DmiDataOperations extends DmiOperations { cmHandleId)); } - private String getDmiServiceBulkRequestUrl(final String dataStoreName, - final String resourceId, - final String optionsParamInQuery, - final String topicParamInQuery, - final String dmiServiceName) { - return dmiServiceUrlBuilder.getBulkRequestUrl( - dmiServiceUrlBuilder.populateQueryParams(resourceId, optionsParamInQuery, - topicParamInQuery), dmiServiceUrlBuilder.populateUriVariables(dataStoreName, dmiServiceName, - NO_CM_HANDLE_ID)); + private String getDmiServiceBatchRequestUrl(final String dmiServiceName, + final String topicParamInQuery, + final String requestId) { + final MultiValueMap<String, String> batchRequestQueryParams = dmiServiceUrlBuilder + .getBatchRequestQueryParams(topicParamInQuery, requestId); + return dmiServiceUrlBuilder.getBatchRequestUrl(batchRequestQueryParams, + dmiServiceUrlBuilder.populateBatchUriVariables(dmiServiceName)); } private void validateIfCmHandleStateReady(final YangModelCmHandle yangModelCmHandle, @@ -224,31 +214,41 @@ public class DmiDataOperations extends DmiOperations { } } - private void buildBulkResourceDataRequestAndSend(final String dataStoreName, - final String resourceId, - final String optionsParamInQuery, - final String topicParamInQuery, - final String requestId, - final Map<String, Map<String, Map<String, String>>> - dmiServiceNameCmHandlePropertiesMap) { - dmiServiceNameCmHandlePropertiesMap.entrySet().parallelStream().forEach( - dmiServiceNameCmHandlePropertiesEntry -> { - final String dmiBulkResourceDataUrl = getDmiServiceBulkRequestUrl(dataStoreName, resourceId, - optionsParamInQuery, topicParamInQuery, dmiServiceNameCmHandlePropertiesEntry.getKey()); - final String jsonRequestBodyAsJsonString = - jsonObjectMapper.asJsonString(dmiServiceNameCmHandlePropertiesEntry.getValue()); - final String jsonRequestBody - = getDmiBulkRequestBody(READ, requestId, jsonRequestBodyAsJsonString); - sendDmiResourceDataRequestToDmiService(dmiBulkResourceDataUrl, jsonRequestBody); - }); + private static Set<String> getDistinctCmHandleIdsFromBatchRequest(final ResourceDataBatchRequest + resourceDataBatchRequest) { + return resourceDataBatchRequest.getBatchOperationDefinitions().stream() + .flatMap(batchOperationDefinition -> + batchOperationDefinition.getCmHandleIds().stream()).collect(Collectors.toSet()); + } + + private Collection<YangModelCmHandle> getYangModelCmHandlesInReadyState(final Set<String> requestedCmHandleIds) { + // TODO Need to publish an error response to client given topic. + // Code should be implemented into https://jira.onap.org/browse/CPS-1614 ( + // NCMP : Error handling for non-ready cm handle state) + return inventoryPersistence.getYangModelCmHandles(requestedCmHandleIds).stream() + .filter(yangModelCmHandle -> yangModelCmHandle.getCompositeState().getCmHandleState() + == CmHandleState.READY).collect(Collectors.toList()); + } + + private void buildBatchRequestUrlAndSendToDmiService(final String topicParamInQuery, + final String requestId, + final Map<String, List<DmiBatchOperation>> + groupsOutPerDmiServiceName) { + + groupsOutPerDmiServiceName.entrySet().forEach(groupsOutPerDmiServiceNameEntry -> { + final String dmiServiceName = groupsOutPerDmiServiceNameEntry.getKey(); + final List<DmiBatchOperation> dmiBatchRequestBodies = groupsOutPerDmiServiceNameEntry.getValue(); + final String dmiBatchResourceDataUrl = getDmiServiceBatchRequestUrl(dmiServiceName, topicParamInQuery, + requestId); + sendBatchRequestToDmiService(dmiBatchResourceDataUrl, dmiBatchRequestBodies); + }); } - private void sendDmiResourceDataRequestToDmiService(final String dmiBulkResourceDataUrl, - final String dmiResourceDataRequestAsJsonString) { - TaskExecutor.executeTask(() -> - dmiRestClient.postOperationWithJsonData(dmiBulkResourceDataUrl, - dmiResourceDataRequestAsJsonString, READ), - DEFAULT_ASYNC_TASK_EXECUTOR_TIMEOUT_IN_MILLISECONDS) + private void sendBatchRequestToDmiService(final String batchResourceDataUrl, + final List<DmiBatchOperation> dmiBatchRequestBodies) { + final String batchRequestBodiesAsJsonString = jsonObjectMapper.asJsonString(dmiBatchRequestBodies); + TaskExecutor.executeTask(() -> dmiRestClient.postOperationWithJsonData(batchResourceDataUrl, + batchRequestBodiesAsJsonString, READ), DEFAULT_ASYNC_TASK_EXECUTOR_TIMEOUT_IN_MILLISECONDS) .whenCompleteAsync(this::handleTaskCompletion); } diff --git a/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/operations/DmiModelOperations.java b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/operations/DmiModelOperations.java index 392e9c1a24..1bbd725646 100644 --- a/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/operations/DmiModelOperations.java +++ b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/operations/DmiModelOperations.java @@ -66,8 +66,7 @@ public class DmiModelOperations extends DmiOperations { * @return module references */ public List<ModuleReference> getModuleReferences(final YangModelCmHandle yangModelCmHandle) { - final DmiRequestBody dmiRequestBody = DmiRequestBody.builder() - .build(); + final DmiRequestBody dmiRequestBody = DmiRequestBody.builder().build(); dmiRequestBody.asDmiProperties(yangModelCmHandle.getDmiProperties()); final ResponseEntity<Object> dmiFetchModulesResponseEntity = getResourceFromDmiWithJsonData( yangModelCmHandle.resolveDmiServiceName(MODEL), @@ -109,7 +108,7 @@ public class DmiModelOperations extends DmiOperations { final String resourceName) { final String dmiResourceDataUrl = getDmiResourceUrl(dmiServiceName, cmHandle, resourceName); return dmiRestClient.postOperationWithJsonData(dmiResourceDataUrl, jsonRequestBody, - OperationEnum.READ); + OperationType.READ); } private static String getRequestBodyToFetchYangResources(final Collection<ModuleReference> newModuleReferences, diff --git a/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/operations/DmiRequestBody.java b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/operations/DmiRequestBody.java index 3aa6366155..6613d3c87c 100644 --- a/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/operations/DmiRequestBody.java +++ b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/operations/DmiRequestBody.java @@ -1,6 +1,6 @@ /* * ============LICENSE_START======================================================= - * Copyright (C) 2021-2022 Nordix Foundation + * Copyright (C) 2021-2023 Nordix Foundation * ================================================================================ * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -22,6 +22,7 @@ package org.onap.cps.ncmp.api.impl.operations; import com.fasterxml.jackson.annotation.JsonInclude; import com.fasterxml.jackson.annotation.JsonProperty; +import com.fasterxml.jackson.annotation.JsonPropertyOrder; import java.util.LinkedHashMap; import java.util.List; import java.util.Map; @@ -32,9 +33,11 @@ import org.onap.cps.ncmp.api.impl.yangmodels.YangModelCmHandle; @JsonInclude(JsonInclude.Include.NON_NULL) @Getter @Builder +@JsonPropertyOrder({"operation", "dataType", "data", "cmHandleProperties", "requestId"}) public class DmiRequestBody { - private OperationEnum operation; + @JsonProperty("operation") + private OperationType operationType; private String dataType; private String data; @JsonProperty("cmHandleProperties") diff --git a/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/operations/OperationType.java b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/operations/OperationType.java new file mode 100644 index 0000000000..fa00d1a15e --- /dev/null +++ b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/operations/OperationType.java @@ -0,0 +1,71 @@ +/* + * ============LICENSE_START======================================================= + * Copyright (C) 2023 Nordix Foundation + * ================================================================================ + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * SPDX-License-Identifier: Apache-2.0 + * ============LICENSE_END========================================================= + */ + +package org.onap.cps.ncmp.api.impl.operations; + +import com.fasterxml.jackson.annotation.JsonValue; +import java.util.Arrays; +import java.util.HashMap; +import java.util.Map; +import lombok.Getter; +import org.onap.cps.ncmp.api.impl.exception.InvalidOperationException; + +@Getter +public enum OperationType { + + READ("read"), + CREATE("create"), + UPDATE("update"), + PATCH("patch"), + DELETE("delete"); + + private final String operationName; + + OperationType(final String operationName) { + this.operationName = operationName; + } + + @Override + @JsonValue + public String toString() { + return String.valueOf(operationName); + } + + private static final Map<String, OperationType> operationNameToOperationEnum = new HashMap<>(); + + static { + Arrays.stream(OperationType.values()).forEach( + operationType -> operationNameToOperationEnum.put(operationType.getOperationName(), operationType)); + } + + /** + * From operation name get operation enum type. + * + * @param operationName the operation name + * @return the operation enum type + */ + public static OperationType fromOperationName(final String operationName) { + final OperationType operationType = operationNameToOperationEnum.get(operationName); + if (null == operationType) { + throw new InvalidOperationException(operationName + " is an invalid operation name"); + } + return operationType; + } +} diff --git a/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/utils/DmiServiceUrlBuilder.java b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/utils/DmiServiceUrlBuilder.java index bba8f48fbd..5c6fa9f0b0 100644 --- a/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/utils/DmiServiceUrlBuilder.java +++ b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/utils/DmiServiceUrlBuilder.java @@ -53,15 +53,17 @@ public class DmiServiceUrlBuilder { } /** - * This method creates the dmi service url for bulk request. + * This method builds batch request url. * - * @param queryParams query param map as key,value pair - * @param uriVariables uri param map as key (placeholder),value pair - * @return {@code String} dmi service url as string + * @param batchRequestQueryParams query param map as key, value pair + * @param batchRequestUriVariables uri param map as key (placeholder), value pair + * @return {@code String} batch request url as string */ - public String getBulkRequestUrl(final MultiValueMap<String, String> queryParams, - final Map<String, Object> uriVariables) { - return getUriComponentsBuilder(getBulkResourceDataBasePathUriBuilder(), queryParams, uriVariables) + public String getBatchRequestUrl(final MultiValueMap<String, String> batchRequestQueryParams, + final Map<String, Object> batchRequestUriVariables) { + return getBatchResourceDataBasePathUriBuilder() + .queryParams(batchRequestQueryParams) + .uriVariables(batchRequestUriVariables) .buildAndExpand().toUriString(); } @@ -84,12 +86,12 @@ public class DmiServiceUrlBuilder { * * @return {@code UriComponentsBuilder} dmi service url builder object */ - public UriComponentsBuilder getBulkResourceDataBasePathUriBuilder() { + public UriComponentsBuilder getBatchResourceDataBasePathUriBuilder() { return UriComponentsBuilder.newInstance() .path("{dmiServiceName}") .pathSegment("{dmiBasePath}") .pathSegment("v1") - .pathSegment("batch"); + .pathSegment("data"); } /** @@ -114,6 +116,20 @@ public class DmiServiceUrlBuilder { } /** + * This method populates uri variables for batch request. + * + * @param dmiServiceName dmi service name + * @return {@code Map<String, Object>} uri variables as map + */ + public Map<String, Object> populateBatchUriVariables(final String dmiServiceName) { + final Map<String, Object> uriVariables = new HashMap<>(); + final String dmiBasePath = dmiProperties.getDmiBasePath(); + uriVariables.put("dmiServiceName", dmiServiceName); + uriVariables.put("dmiBasePath", dmiBasePath); + return uriVariables; + } + + /** * This method is used to populate map from query params. * * @param resourceId unique id of response for valid topic @@ -134,6 +150,21 @@ public class DmiServiceUrlBuilder { return queryParams; } + /** + * This method is used to populate map from query params for batch request. + * + * @param topicParamInQuery topic into url param + * @param requestId unique id of response for valid topic + * @return all valid query params as map + */ + public MultiValueMap<String, String> getBatchRequestQueryParams(final String topicParamInQuery, + final String requestId) { + final MultiValueMap<String, String> queryParams = new LinkedMultiValueMap<>(); + getQueryParamConsumer().accept("topic", topicParamInQuery, queryParams); + getQueryParamConsumer().accept("requestId", requestId, queryParams); + return queryParams; + } + private TriConsumer<String, String, MultiValueMap<String, String>> getQueryParamConsumer() { return (paramName, paramValue, paramMap) -> { if (Strings.isNotEmpty(paramValue)) { diff --git a/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/utils/ResourceDataBatchRequestUtils.java b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/utils/ResourceDataBatchRequestUtils.java new file mode 100644 index 0000000000..e4c9bfb39b --- /dev/null +++ b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/utils/ResourceDataBatchRequestUtils.java @@ -0,0 +1,126 @@ +/* + * ============LICENSE_START======================================================= + * Copyright (C) 2023 Nordix Foundation + * ================================================================================ + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * SPDX-License-Identifier: Apache-2.0 + * ============LICENSE_END========================================================= + */ + +package org.onap.cps.ncmp.api.impl.utils; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.Collections; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Set; +import lombok.AccessLevel; +import lombok.NoArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.onap.cps.ncmp.api.impl.operations.CmHandle; +import org.onap.cps.ncmp.api.impl.operations.DmiBatchOperation; +import org.onap.cps.ncmp.api.impl.yangmodels.YangModelCmHandle; +import org.onap.cps.ncmp.api.models.BatchOperationDefinition; +import org.onap.cps.ncmp.api.models.ResourceDataBatchRequest; + +@Slf4j +@NoArgsConstructor(access = AccessLevel.PRIVATE) +public class ResourceDataBatchRequestUtils { + + private static final String UNKNOWN_SERVICE_NAME = null; + + /** + * Create a list of DMI batch operation per DMI service (name). + * + * @param resourceDataBatchRequestIn incoming batch request details for resource data + * @param yangModelCmHandles involved cm handles represented as YangModelCmHandle (incl. metadata) + * + * @return {@code Map<String, List<DmiBatchOperation>>} Create a list of DMI batch operation per DMI service (name). + */ + public static Map<String, List<DmiBatchOperation>> processPerOperationInBatchRequest( + final ResourceDataBatchRequest resourceDataBatchRequestIn, + final Collection<YangModelCmHandle> yangModelCmHandles) { + + final Map<String, Map<String, Map<String, String>>> dmiPropertiesPerCmHandleIdPerServiceName = + DmiServiceNameOrganizer.getDmiPropertiesPerCmHandleIdPerServiceName(yangModelCmHandles); + + final Map<String, String> dmiServiceNamesPerCmHandleId = + getDmiServiceNamesPerCmHandleId(dmiPropertiesPerCmHandleIdPerServiceName); + + final Map<String, List<DmiBatchOperation>> dmiBatchOperationsOutPerDmiServiceName = new HashMap<>(); + + for (final BatchOperationDefinition batchOperationDefinitionIn : + resourceDataBatchRequestIn.getBatchOperationDefinitions()) { + for (final String cmHandleId : batchOperationDefinitionIn.getCmHandleIds()) { + final String dmiServiceName = dmiServiceNamesPerCmHandleId.get(cmHandleId); + final Map<String, String> cmHandleIdProperties + = dmiPropertiesPerCmHandleIdPerServiceName.get(dmiServiceName).get(cmHandleId); + if (cmHandleIdProperties == null) { + publishErrorMessageToClientTopic(cmHandleId); + } else { + final DmiBatchOperation dmiBatchOperationOut = getOrAddDmiBatchOperation(dmiServiceName, + batchOperationDefinitionIn, dmiBatchOperationsOutPerDmiServiceName); + final CmHandle cmHandle = CmHandle.buildCmHandleWithProperties(cmHandleId, cmHandleIdProperties); + dmiBatchOperationOut.getCmHandles().add(cmHandle); + } + } + } + return dmiBatchOperationsOutPerDmiServiceName; + } + + private static void publishErrorMessageToClientTopic(final String requestedCmHandleId) { + log.warn("cm handle {} not found", requestedCmHandleId); + // TODO Need to publish an error response to client given topic. + // Code should be implemented into https://jira.onap.org/browse/CPS-1583 ( + // NCMP : Handle non-existing cm handles) + } + + private static Map<String, String> getDmiServiceNamesPerCmHandleId( + final Map<String, Map<String, Map<String, String>>> dmiDmiPropertiesPerCmHandleIdPerServiceName) { + final Map<String, String> dmiServiceNamesPerCmHandleId = new HashMap<>(); + for (final Map.Entry<String, Map<String, Map<String, String>>> dmiDmiPropertiesEntry + : dmiDmiPropertiesPerCmHandleIdPerServiceName.entrySet()) { + final String dmiServiceName = dmiDmiPropertiesEntry.getKey(); + final Set<String> cmHandleIds = dmiDmiPropertiesPerCmHandleIdPerServiceName.get(dmiServiceName).keySet(); + for (final String cmHandleId : cmHandleIds) { + dmiServiceNamesPerCmHandleId.put(cmHandleId, dmiServiceName); + } + } + dmiDmiPropertiesPerCmHandleIdPerServiceName.put(UNKNOWN_SERVICE_NAME, Collections.emptyMap()); + return dmiServiceNamesPerCmHandleId; + } + + private static DmiBatchOperation getOrAddDmiBatchOperation(final String dmiServiceName, + final BatchOperationDefinition + batchOperationDefinitionIn, + final Map<String, List<DmiBatchOperation>> + dmiBatchOperationsOutPerDmiServiceName) { + dmiBatchOperationsOutPerDmiServiceName + .computeIfAbsent(dmiServiceName, dmiServiceNameAsKey -> new ArrayList<>()); + final List<DmiBatchOperation> dmiBatchOperationsOut + = dmiBatchOperationsOutPerDmiServiceName.get(dmiServiceName); + final boolean isNewOperation = dmiBatchOperationsOut.isEmpty() + || !dmiBatchOperationsOut.get(dmiBatchOperationsOut.size() - 1).getOperationId() + .equals(batchOperationDefinitionIn.getOperationId()); + if (isNewOperation) { + final DmiBatchOperation newDmiBatchOperationOut = + DmiBatchOperation.buildDmiBatchRequestBodyWithoutCmHandles(batchOperationDefinitionIn); + dmiBatchOperationsOut.add(newDmiBatchOperationOut); + return newDmiBatchOperationOut; + } + return dmiBatchOperationsOut.get(dmiBatchOperationsOut.size() - 1); + } +} diff --git a/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/models/BatchOperationDefinition.java b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/models/BatchOperationDefinition.java new file mode 100644 index 0000000000..04075b3b7c --- /dev/null +++ b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/models/BatchOperationDefinition.java @@ -0,0 +1,49 @@ +/* + * ============LICENSE_START======================================================= + * Copyright (C) 2023 Nordix Foundation + * ================================================================================ + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * SPDX-License-Identifier: Apache-2.0 + * ============LICENSE_END========================================================= + */ + +package org.onap.cps.ncmp.api.models; + +import com.fasterxml.jackson.annotation.JsonIgnoreProperties; +import com.fasterxml.jackson.annotation.JsonInclude; +import com.fasterxml.jackson.annotation.JsonProperty; +import java.util.ArrayList; +import java.util.List; +import javax.validation.Valid; +import lombok.EqualsAndHashCode; +import lombok.Getter; +import lombok.Setter; + +@Getter +@Setter +@EqualsAndHashCode +@JsonInclude(JsonInclude.Include.NON_EMPTY) +@JsonIgnoreProperties(ignoreUnknown = true) +public class BatchOperationDefinition { + + private String operation; + private String operationId; + private String datastore; + private String options; + private String resourceIdentifier; + + @JsonProperty("targetIds") + @Valid + private List<String> cmHandleIds = new ArrayList(); +} diff --git a/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/models/ResourceDataBatchRequest.java b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/models/ResourceDataBatchRequest.java new file mode 100644 index 0000000000..7af107c37a --- /dev/null +++ b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/models/ResourceDataBatchRequest.java @@ -0,0 +1,43 @@ +/* + * ============LICENSE_START======================================================= + * Copyright (C) 2023 Nordix Foundation + * ================================================================================ + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * SPDX-License-Identifier: Apache-2.0 + * ============LICENSE_END========================================================= + */ + +package org.onap.cps.ncmp.api.models; + +import com.fasterxml.jackson.annotation.JsonIgnoreProperties; +import com.fasterxml.jackson.annotation.JsonInclude; +import com.fasterxml.jackson.annotation.JsonProperty; +import java.util.Collections; +import java.util.List; +import javax.validation.Valid; +import lombok.EqualsAndHashCode; +import lombok.Getter; +import lombok.Setter; + +@Getter +@Setter +@EqualsAndHashCode +@JsonInclude(JsonInclude.Include.NON_EMPTY) +@JsonIgnoreProperties(ignoreUnknown = true) +public class ResourceDataBatchRequest { + + @JsonProperty("operations") + @Valid + private List<BatchOperationDefinition> batchOperationDefinitions = Collections.emptyList(); +} |