diff options
Diffstat (limited to 'cps-ncmp-service/src/main/java/org')
22 files changed, 794 insertions, 178 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 05490d8fc..046c78879 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 e478b0053..536775ec5 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/async/BatchRecordFilterStrategy.java b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/async/BatchRecordFilterStrategy.java new file mode 100644 index 000000000..088e96564 --- /dev/null +++ b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/async/BatchRecordFilterStrategy.java @@ -0,0 +1,50 @@ +/* + * ============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.async; + +import org.apache.commons.lang3.SerializationUtils; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.kafka.listener.adapter.RecordFilterStrategy; + +/** + * Batch Record filter strategy, which helps to filter the consumer records. + * + */ +@Configuration +public class BatchRecordFilterStrategy { + + /** + * Filtering the consumer records based on the eventType header, It + * returns boolean, true means filter the consumer record and false + * means not filter the consumer record. + * @return boolean value. + */ + @Bean + public RecordFilterStrategy<Object, Object> filterBatchDataResponseEvent() { + return consumedRecord -> { + final String headerValue = SerializationUtils + .deserialize(consumedRecord.headers().lastHeader("eventType").value()); + return !(headerValue != null + && headerValue.startsWith("org.onap.cps.ncmp.events.async.BatchDataResponseEvent")); + }; + } +} diff --git a/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/async/NcmpAsyncBatchEventConsumer.java b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/async/NcmpAsyncBatchEventConsumer.java new file mode 100644 index 000000000..2a332d003 --- /dev/null +++ b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/async/NcmpAsyncBatchEventConsumer.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.async; + +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.apache.commons.lang3.SerializationUtils; +import org.apache.kafka.clients.consumer.ConsumerRecord; +import org.onap.cps.ncmp.api.impl.events.EventsPublisher; +import org.onap.cps.ncmp.events.async.BatchDataResponseEventV1; +import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; +import org.springframework.kafka.annotation.KafkaListener; +import org.springframework.stereotype.Component; + +/** + * Listener for cps-ncmp async batch events. + */ +@Component +@Slf4j +@RequiredArgsConstructor +@ConditionalOnProperty(name = "notification.enabled", havingValue = "true", matchIfMissing = true) +public class NcmpAsyncBatchEventConsumer { + + private final EventsPublisher<BatchDataResponseEventV1> eventsPublisher; + + /** + * Consume the BatchDataResponseEvent published by producer to topic 'async-m2m.topic' + * and publish the same to the client specified topic. + * + * @param batchEventConsumerRecord consuming event as a ConsumerRecord. + */ + @KafkaListener( + topics = "${app.ncmp.async-m2m.topic}", + filter = "filterBatchDataResponseEvent", + groupId = "ncmp-batch-event-group", + properties = {"spring.json.value.default.type=org.onap.cps.ncmp.events.async.BatchDataResponseEventV1"}) + public void consumeAndPublish(final ConsumerRecord<String, BatchDataResponseEventV1> batchEventConsumerRecord) { + log.info("Consuming event payload {} ...", batchEventConsumerRecord.value()); + final String eventTarget = SerializationUtils + .deserialize(batchEventConsumerRecord.headers().lastHeader("eventTarget").value()); + final String eventId = SerializationUtils + .deserialize(batchEventConsumerRecord.headers().lastHeader("eventId").value()); + eventsPublisher.publishEvent(eventTarget, eventId, batchEventConsumerRecord.headers(), + batchEventConsumerRecord.value()); + } +} 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 9d087806c..136935ba5 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/events/EventsPublisher.java b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/events/EventsPublisher.java index 4c8462930..b0b091a2f 100644 --- a/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/events/EventsPublisher.java +++ b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/events/EventsPublisher.java @@ -20,13 +20,16 @@ package org.onap.cps.ncmp.api.impl.events; +import java.util.Map; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; import org.apache.kafka.clients.producer.ProducerRecord; import org.apache.kafka.common.header.Headers; +import org.apache.kafka.common.header.internals.RecordHeaders; import org.springframework.kafka.core.KafkaTemplate; import org.springframework.kafka.support.SendResult; import org.springframework.stereotype.Service; +import org.springframework.util.SerializationUtils; import org.springframework.util.concurrent.ListenableFuture; import org.springframework.util.concurrent.ListenableFutureCallback; @@ -70,6 +73,20 @@ public class EventsPublisher<T> { eventFuture.addCallback(handleCallback(topicName)); } + /** + * Generic Event Publisher with headers. + * + * @param topicName valid topic name + * @param eventKey message key + * @param eventHeaders map of event headers + * @param event message payload + */ + public void publishEvent(final String topicName, final String eventKey, final Map<String, Object> eventHeaders, + final T event) { + + publishEvent(topicName, eventKey, convertToKafkaHeaders(eventHeaders), event); + } + private ListenableFutureCallback<SendResult<String, T>> handleCallback(final String topicName) { return new ListenableFutureCallback<>() { @Override @@ -85,4 +102,10 @@ public class EventsPublisher<T> { }; } + private Headers convertToKafkaHeaders(final Map<String, Object> eventMessageHeaders) { + final Headers eventHeaders = new RecordHeaders(); + eventMessageHeaders.forEach((key, value) -> eventHeaders.add(key, SerializationUtils.serialize(value))); + return eventHeaders; + } + } diff --git a/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/events/lcm/LcmEventHeaderMapper.java b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/events/lcm/LcmEventHeaderMapper.java new file mode 100644 index 000000000..f7707d9f7 --- /dev/null +++ b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/events/lcm/LcmEventHeaderMapper.java @@ -0,0 +1,36 @@ +/* + * ============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.events.lcm; + +import org.mapstruct.Mapper; +import org.onap.cps.ncmp.events.lcm.v1.LcmEvent; +import org.onap.cps.ncmp.events.lcm.v1.LcmEventHeader; + +@Mapper(componentModel = "spring") +public interface LcmEventHeaderMapper { + + /** + * Mapper for converting incoming {@link LcmEvent} to outgoing {@link LcmEventHeader}. + */ + + LcmEventHeader toLcmEventHeader(LcmEvent lcmEvent); + +} diff --git a/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/events/lcm/LcmEventsCmHandleStateHandlerImpl.java b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/events/lcm/LcmEventsCmHandleStateHandlerImpl.java index 9d518432a..f42cd39d4 100644 --- a/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/events/lcm/LcmEventsCmHandleStateHandlerImpl.java +++ b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/events/lcm/LcmEventsCmHandleStateHandlerImpl.java @@ -43,7 +43,8 @@ import org.onap.cps.ncmp.api.inventory.CompositeState; import org.onap.cps.ncmp.api.inventory.CompositeStateUtils; import org.onap.cps.ncmp.api.inventory.InventoryPersistence; import org.onap.cps.ncmp.api.models.NcmpServiceCmHandle; -import org.onap.ncmp.cmhandle.event.lcm.LcmEvent; +import org.onap.cps.ncmp.events.lcm.v1.LcmEvent; +import org.onap.cps.ncmp.events.lcm.v1.LcmEventHeader; import org.springframework.scheduling.annotation.Async; import org.springframework.stereotype.Service; @@ -76,7 +77,7 @@ public class LcmEventsCmHandleStateHandlerImpl implements LcmEventsCmHandleState @Override @Timed(value = "cps.ncmp.cmhandle.state.update.batch", - description = "Time taken to update a batch of cm handle states") + description = "Time taken to update a batch of cm handle states") public void updateCmHandleStateBatch(final Map<YangModelCmHandle, CmHandleState> cmHandleStatePerCmHandle) { final Collection<CmHandleTransitionPair> cmHandleTransitionPairs = prepareCmHandleTransitionBatch(cmHandleStatePerCmHandle); @@ -106,9 +107,12 @@ public class LcmEventsCmHandleStateHandlerImpl implements LcmEventsCmHandleState private void publishLcmEvent(final NcmpServiceCmHandle targetNcmpServiceCmHandle, final NcmpServiceCmHandle existingNcmpServiceCmHandle) { final String cmHandleId = targetNcmpServiceCmHandle.getCmHandleId(); + final LcmEventHeader lcmEventHeader = + lcmEventsCreator.populateLcmEventHeader(cmHandleId, targetNcmpServiceCmHandle, + existingNcmpServiceCmHandle); final LcmEvent lcmEvent = lcmEventsCreator.populateLcmEvent(cmHandleId, targetNcmpServiceCmHandle, existingNcmpServiceCmHandle); - lcmEventsService.publishLcmEvent(cmHandleId, lcmEvent); + lcmEventsService.publishLcmEvent(cmHandleId, lcmEvent, lcmEventHeader); } private Collection<CmHandleTransitionPair> prepareCmHandleTransitionBatch( @@ -221,6 +225,7 @@ public class LcmEventsCmHandleStateHandlerImpl implements LcmEventsCmHandleState @Setter @NoArgsConstructor static class CmHandleTransitionPair { + private YangModelCmHandle currentYangModelCmHandle; private YangModelCmHandle targetYangModelCmHandle; } diff --git a/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/events/lcm/LcmEventsCreator.java b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/events/lcm/LcmEventsCreator.java index a72e664dc..3c7c92b12 100644 --- a/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/events/lcm/LcmEventsCreator.java +++ b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/events/lcm/LcmEventsCreator.java @@ -23,13 +23,15 @@ package org.onap.cps.ncmp.api.impl.events.lcm; import java.util.UUID; import lombok.Getter; import lombok.NoArgsConstructor; +import lombok.RequiredArgsConstructor; import lombok.Setter; import lombok.extern.slf4j.Slf4j; import org.onap.cps.ncmp.api.impl.utils.EventDateTimeFormatter; import org.onap.cps.ncmp.api.models.NcmpServiceCmHandle; -import org.onap.ncmp.cmhandle.event.lcm.Event; -import org.onap.ncmp.cmhandle.event.lcm.LcmEvent; -import org.onap.ncmp.cmhandle.event.lcm.Values; +import org.onap.cps.ncmp.events.lcm.v1.Event; +import org.onap.cps.ncmp.events.lcm.v1.LcmEvent; +import org.onap.cps.ncmp.events.lcm.v1.LcmEventHeader; +import org.onap.cps.ncmp.events.lcm.v1.Values; import org.springframework.stereotype.Component; @@ -38,8 +40,11 @@ import org.springframework.stereotype.Component; */ @Slf4j @Component +@RequiredArgsConstructor public class LcmEventsCreator { + private final LcmEventHeaderMapper lcmEventHeaderMapper; + /** * Populate Lifecycle Management Event. * @@ -53,6 +58,20 @@ public class LcmEventsCreator { return createLcmEvent(cmHandleId, targetNcmpServiceCmHandle, existingNcmpServiceCmHandle); } + /** + * Populate Lifecycle Management Event Header. + * + * @param cmHandleId cm handle identifier + * @param targetNcmpServiceCmHandle target ncmp service cmhandle + * @param existingNcmpServiceCmHandle existing ncmp service cmhandle + * @return Populated LcmEventHeader + */ + public LcmEventHeader populateLcmEventHeader(final String cmHandleId, + final NcmpServiceCmHandle targetNcmpServiceCmHandle, + final NcmpServiceCmHandle existingNcmpServiceCmHandle) { + return createLcmEventHeader(cmHandleId, targetNcmpServiceCmHandle, existingNcmpServiceCmHandle); + } + private LcmEvent createLcmEvent(final String cmHandleId, final NcmpServiceCmHandle targetNcmpServiceCmHandle, final NcmpServiceCmHandle existingNcmpServiceCmHandle) { final LcmEventType lcmEventType = @@ -63,6 +82,15 @@ public class LcmEventsCreator { return lcmEvent; } + private LcmEventHeader createLcmEventHeader(final String cmHandleId, + final NcmpServiceCmHandle targetNcmpServiceCmHandle, + final NcmpServiceCmHandle existingNcmpServiceCmHandle) { + final LcmEventType lcmEventType = + LcmEventsCreatorHelper.determineEventType(targetNcmpServiceCmHandle, existingNcmpServiceCmHandle); + final LcmEvent lcmEventWithHeaderInformation = lcmEventHeader(cmHandleId, lcmEventType); + return lcmEventHeaderMapper.toLcmEventHeader(lcmEventWithHeaderInformation); + } + private Event lcmEventPayload(final String eventCorrelationId, final NcmpServiceCmHandle targetNcmpServiceCmHandle, final NcmpServiceCmHandle existingNcmpServiceCmHandle, final LcmEventType lcmEventType) { final Event event = new Event(); diff --git a/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/events/lcm/LcmEventsCreatorHelper.java b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/events/lcm/LcmEventsCreatorHelper.java index 1322b7277..d3b45d4a6 100644 --- a/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/events/lcm/LcmEventsCreatorHelper.java +++ b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/events/lcm/LcmEventsCreatorHelper.java @@ -34,7 +34,7 @@ import lombok.AccessLevel; import lombok.NoArgsConstructor; import lombok.extern.slf4j.Slf4j; import org.onap.cps.ncmp.api.models.NcmpServiceCmHandle; -import org.onap.ncmp.cmhandle.event.lcm.Values; +import org.onap.cps.ncmp.events.lcm.v1.Values; /** * LcmEventsCreatorHelper has helper methods to create LcmEvent. diff --git a/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/events/lcm/LcmEventsService.java b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/events/lcm/LcmEventsService.java index f258b4597..2e1b914b1 100644 --- a/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/events/lcm/LcmEventsService.java +++ b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/events/lcm/LcmEventsService.java @@ -21,10 +21,13 @@ package org.onap.cps.ncmp.api.impl.events.lcm; import io.micrometer.core.annotation.Timed; +import java.util.Map; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; import org.onap.cps.ncmp.api.impl.events.EventsPublisher; -import org.onap.ncmp.cmhandle.event.lcm.LcmEvent; +import org.onap.cps.ncmp.events.lcm.v1.LcmEvent; +import org.onap.cps.ncmp.events.lcm.v1.LcmEventHeader; +import org.onap.cps.utils.JsonObjectMapper; import org.springframework.beans.factory.annotation.Value; import org.springframework.kafka.KafkaException; import org.springframework.stereotype.Service; @@ -39,6 +42,7 @@ import org.springframework.stereotype.Service; public class LcmEventsService { private final EventsPublisher<LcmEvent> eventsPublisher; + private final JsonObjectMapper jsonObjectMapper; @Value("${app.lcm.events.topic:ncmp-events}") private String topicName; @@ -47,17 +51,19 @@ public class LcmEventsService { private boolean notificationsEnabled; /** - * Publish the LcmEvent to the public topic. + * Publish the LcmEvent with header to the public topic. * - * @param cmHandleId Cm Handle Id - * @param lcmEvent Lcm Event + * @param cmHandleId Cm Handle Id + * @param lcmEvent Lcm Event + * @param lcmEventHeader Lcm Event Header */ - @Timed(value = "cps.ncmp.lcm.events.publish", - description = "Time taken to publish a LCM event") - public void publishLcmEvent(final String cmHandleId, final LcmEvent lcmEvent) { + @Timed(value = "cps.ncmp.lcm.events.publish", description = "Time taken to publish a LCM event") + public void publishLcmEvent(final String cmHandleId, final LcmEvent lcmEvent, final LcmEventHeader lcmEventHeader) { if (notificationsEnabled) { try { - eventsPublisher.publishEvent(topicName, cmHandleId, lcmEvent); + final Map<String, Object> lcmEventHeadersMap = + jsonObjectMapper.convertToValueType(lcmEventHeader, Map.class); + eventsPublisher.publishEvent(topicName, cmHandleId, lcmEventHeadersMap, lcmEvent); } catch (final KafkaException e) { log.error("Unable to publish message to topic : {} and cause : {}", topicName, e.getMessage()); } 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 796cef23d..17069098c 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 000000000..618da7454 --- /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 000000000..76ad0f7b2 --- /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 1a3952306..3e8d40a83 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 392e9c1a2..1bbd72564 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 3aa636615..6613d3c87 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 000000000..fa00d1a15 --- /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 bba8f48fb..5c6fa9f0b 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 000000000..e4c9bfb39 --- /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 000000000..04075b3b7 --- /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 000000000..7af107c37 --- /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(); +} |