diff options
Diffstat (limited to 'cps-ncmp-service/src/main')
175 files changed, 5424 insertions, 3507 deletions
diff --git a/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/NcmpResponseStatus.java b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/NcmpResponseStatus.java index bdc3dee772..8cfad7dbf6 100644 --- a/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/NcmpResponseStatus.java +++ b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/NcmpResponseStatus.java @@ -26,14 +26,12 @@ import lombok.Getter; public enum NcmpResponseStatus { SUCCESS("0", "Successfully applied changes"), - SUCCESSFULLY_APPLIED_SUBSCRIPTION("1", "successfully applied subscription"), + CM_DATA_SUBSCRIPTION_ACCEPTED("1", "ACCEPTED"), CM_HANDLES_NOT_FOUND("100", "cm handle id(s) not found"), CM_HANDLES_NOT_READY("101", "cm handle(s) not ready"), DMI_SERVICE_NOT_RESPONDING("102", "dmi plugin service is not responding"), UNABLE_TO_READ_RESOURCE_DATA("103", "dmi plugin service is not able to read resource data"), - PARTIALLY_APPLIED_SUBSCRIPTION("104", "partially applied subscription"), - SUBSCRIPTION_NOT_APPLICABLE("105", "subscription not applicable for all cm handles"), - SUBSCRIPTION_PENDING("106", "subscription pending for all cm handles"), + CM_DATA_SUBSCRIPTION_REJECTED("104", "REJECTED"), UNKNOWN_ERROR("108", "Unknown error"), CM_HANDLE_ALREADY_EXIST("109", "cm-handle already exists"), CM_HANDLE_INVALID_ID("110", "cm-handle has an invalid character(s) in id"), 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 deleted file mode 100644 index 20545d711d..0000000000 --- a/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/NetworkCmProxyDataService.java +++ /dev/null @@ -1,204 +0,0 @@ -/* - * ============LICENSE_START======================================================= - * Copyright (C) 2021 highstreet technologies GmbH - * Modifications Copyright (C) 2021-2024 Nordix Foundation - * Modifications Copyright (C) 2021 Pantheon.tech - * Modifications Copyright (C) 2022 Bell Canada - * ================================================================================ - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * 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; - -import java.util.Collection; -import java.util.Map; -import org.onap.cps.ncmp.api.impl.inventory.CompositeState; -import org.onap.cps.ncmp.api.impl.operations.OperationType; -import org.onap.cps.ncmp.api.models.CmHandleQueryApiParameters; -import org.onap.cps.ncmp.api.models.CmHandleQueryServiceParameters; -import org.onap.cps.ncmp.api.models.CmResourceAddress; -import org.onap.cps.ncmp.api.models.DataOperationRequest; -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.spi.FetchDescendantsOption; -import org.onap.cps.spi.model.ModuleDefinition; -import org.onap.cps.spi.model.ModuleReference; - -/* - * Datastore interface for handling CPS data. - */ -public interface NetworkCmProxyDataService { - - /** - * Registration of New CM Handles. - * - * @param dmiPluginRegistration Dmi Plugin Registration - * @return dmiPluginRegistrationResponse - */ - DmiPluginRegistrationResponse updateDmiRegistrationAndSyncModule(DmiPluginRegistration dmiPluginRegistration); - - /** - * Get resource data for given data store using dmi. - * - * @param cmResourceAddress target datastore, cm handle and resource identifier - * @param optionsParamInQuery options query - * @param topicParamInQuery topic name for (triggering) async responses - * @param requestId unique requestId for async request - * @param authorization contents of Authorization header, or null if not present - * @return {@code Object} resource data - */ - Object getResourceDataForCmHandle(CmResourceAddress cmResourceAddress, - String optionsParamInQuery, - String topicParamInQuery, - String requestId, - String authorization); - - /** - * Get resource data for operational. - * - * @param cmResourceAddress target datastore, cm handle and resource identifier - * @Link FetchDescendantsOption fetch descendants option - * @return {@code Object} resource data - */ - Object getResourceDataForCmHandle(CmResourceAddress cmResourceAddress, - FetchDescendantsOption fetchDescendantsOption); - - /** - * Execute (async) data operation for group of cm handles using dmi. - * - * @param topicParamInQuery topic name for (triggering) async responses - * @param dataOperationRequest contains a list of operation definitions(multiple operations) - * @param requestId request ID - * @param authorization contents of Authorization header, or null if not present - */ - void executeDataOperationForCmHandles(String topicParamInQuery, - DataOperationRequest dataOperationRequest, - String requestId, - String authorization); - - - /** - * Write resource data for data store pass-through running using dmi for given cm-handle. - * - * @param cmHandleId cm handle identifier - * @param resourceIdentifier resource identifier - * @param operationType required operation type - * @param requestBody request body to create resource - * @param contentType content type in body - * @param authorization contents of Authorization header, or null if not present - * @return {@code Object} return data - */ - Object writeResourceDataPassThroughRunningForCmHandle(String cmHandleId, - String resourceIdentifier, - OperationType operationType, - String requestBody, - String contentType, - String authorization); - - /** - * Retrieve module references for the given cm handle. - * - * @param cmHandleId cm handle identifier - * @return a collection of modules names and revisions - */ - Collection<ModuleReference> getYangResourcesModuleReferences(String cmHandleId); - - /** - * Retrieve module definitions for the given cm handle. - * - * @param cmHandleId cm handle identifier - * @return a collection of module definition (moduleName, revision and yang resource content) - */ - Collection<ModuleDefinition> getModuleDefinitionsByCmHandleId(String cmHandleId); - - /** - * Get module definitions for the given parameters. - * - * @param cmHandleId cm-handle identifier - * @param moduleName module name - * @param moduleRevision the revision of the module - * @return list of module definitions (module name, revision, yang resource content) - */ - Collection<ModuleDefinition> getModuleDefinitionsByCmHandleAndModule(String cmHandleId, - String moduleName, - String moduleRevision); - - /** - * Query cm handle details by cm handle's name. - * - * @param cmHandleId cm handle identifier - * @return a collection of cm handle details. - */ - NcmpServiceCmHandle getNcmpServiceCmHandle(String cmHandleId); - - /** - * Get cm handle public properties by cm handle id. - * - * @param cmHandleId cm handle identifier - * @return a collection of cm handle public properties. - */ - Map<String, String> getCmHandlePublicProperties(String cmHandleId); - - /** - * Get cm handle composite state by cm handle id. - * - * @param cmHandleId cm handle identifier - * @return a cm handle composite state - */ - CompositeState getCmHandleCompositeState(String cmHandleId); - - /** - * Query and return cm handles that match the given query parameters. - * - * @param cmHandleQueryApiParameters the cm handle query parameters - * @return collection of cm handles - */ - Collection<NcmpServiceCmHandle> executeCmHandleSearch(CmHandleQueryApiParameters cmHandleQueryApiParameters); - - /** - * Query and return cm handle ids that match the given query parameters. - * - * @param cmHandleQueryApiParameters the cm handle query parameters - * @return collection of cm handle ids - */ - Collection<String> executeCmHandleIdSearch(CmHandleQueryApiParameters cmHandleQueryApiParameters); - - /** - * Set the data sync enabled flag, along with the data sync state to true or false based on the cm handle id. - * - * @param cmHandleId cm handle id - * @param dataSyncEnabled data sync enabled flag - */ - void setDataSyncEnabled(String cmHandleId, Boolean dataSyncEnabled); - - /** - * Get all cm handle IDs by DMI plugin identifier. - * - * @param dmiPluginIdentifier DMI plugin identifier - * @return collection of cm handle IDs - */ - Collection<String> getAllCmHandleIdsByDmiPluginIdentifier(String dmiPluginIdentifier); - - /** - * Get all cm handle IDs by various search criteria. - * - * @param cmHandleQueryServiceParameters cm handle query parameters - * @return collection of cm handle IDs - */ - Collection<String> executeCmHandleIdSearchForInventory(CmHandleQueryServiceParameters - cmHandleQueryServiceParameters); -} diff --git a/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/exception/InvalidDatastoreException.java b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/data/exceptions/InvalidDatastoreException.java index 6cfa159b20..740785e3be 100644 --- a/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/exception/InvalidDatastoreException.java +++ b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/data/exceptions/InvalidDatastoreException.java @@ -18,7 +18,7 @@ * ============LICENSE_END========================================================= */ -package org.onap.cps.ncmp.api.impl.exception; +package org.onap.cps.ncmp.api.data.exceptions; public class InvalidDatastoreException extends RuntimeException { /** diff --git a/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/exception/InvalidOperationException.java b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/data/exceptions/InvalidOperationException.java index 17069098cb..7669dd05f6 100644 --- a/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/exception/InvalidOperationException.java +++ b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/data/exceptions/InvalidOperationException.java @@ -18,7 +18,7 @@ * ============LICENSE_END========================================================= */ -package org.onap.cps.ncmp.api.impl.exception; +package org.onap.cps.ncmp.api.data.exceptions; public class InvalidOperationException extends RuntimeException { /** diff --git a/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/data/exceptions/OperationNotSupportedException.java b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/data/exceptions/OperationNotSupportedException.java new file mode 100644 index 0000000000..eea846a0f6 --- /dev/null +++ b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/data/exceptions/OperationNotSupportedException.java @@ -0,0 +1,32 @@ +/* + * ============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.data.exceptions; + +public class OperationNotSupportedException extends RuntimeException { + /** + * Instantiates a new not implemented operation exception. + * + * @param message the message + */ + public OperationNotSupportedException(final String message) { + super(message); + } +} diff --git a/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/data/models/CmResourceAddress.java b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/data/models/CmResourceAddress.java new file mode 100644 index 0000000000..98a343b92e --- /dev/null +++ b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/data/models/CmResourceAddress.java @@ -0,0 +1,41 @@ +/* + * ============LICENSE_START======================================================= + * Copyright (C) 2024 Nordix Foundation + * ================================================================================ + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * SPDX-License-Identifier: Apache-2.0 + * ============LICENSE_END========================================================= + */ + +package org.onap.cps.ncmp.api.data.models; + +import lombok.AccessLevel; +import lombok.Getter; +import lombok.RequiredArgsConstructor; +import org.onap.cps.ncmp.config.CpsApplicationContext; +import org.onap.cps.ncmp.impl.utils.AlternateIdMatcher; + +@Getter +@RequiredArgsConstructor +public class CmResourceAddress { + + private final String datastoreName; + @Getter(AccessLevel.NONE) + private final String cmHandleReference; + private final String resourceIdentifier; + + public String getResolvedCmHandleId() { + return CpsApplicationContext.getCpsBean(AlternateIdMatcher.class).getCmHandleId(cmHandleReference); + } +} diff --git a/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/models/DataOperationDefinition.java b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/data/models/DataOperationDefinition.java index 366d845832..d1ff1a5815 100644 --- a/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/models/DataOperationDefinition.java +++ b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/data/models/DataOperationDefinition.java @@ -18,7 +18,7 @@ * ============LICENSE_END========================================================= */ -package org.onap.cps.ncmp.api.models; +package org.onap.cps.ncmp.api.data.models; import com.fasterxml.jackson.annotation.JsonIgnoreProperties; import com.fasterxml.jackson.annotation.JsonInclude; diff --git a/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/models/DataOperationRequest.java b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/data/models/DataOperationRequest.java index a4d070c38d..b104f587fb 100644 --- a/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/models/DataOperationRequest.java +++ b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/data/models/DataOperationRequest.java @@ -18,7 +18,7 @@ * ============LICENSE_END========================================================= */ -package org.onap.cps.ncmp.api.models; +package org.onap.cps.ncmp.api.data.models; import com.fasterxml.jackson.annotation.JsonIgnoreProperties; import com.fasterxml.jackson.annotation.JsonInclude; diff --git a/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/operations/DatastoreType.java b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/data/models/DatastoreType.java index 6520c05c08..a483341b65 100644 --- a/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/operations/DatastoreType.java +++ b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/data/models/DatastoreType.java @@ -18,14 +18,14 @@ * ============LICENSE_END========================================================= */ -package org.onap.cps.ncmp.api.impl.operations; +package org.onap.cps.ncmp.api.data.models; import java.util.Arrays; import java.util.HashMap; import java.util.Map; import lombok.Getter; -import org.onap.cps.ncmp.api.impl.exception.InvalidDatastoreException; +import org.onap.cps.ncmp.api.data.exceptions.InvalidDatastoreException; @Getter public enum DatastoreType { 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/data/models/OperationType.java index fa00d1a15e..870e24fac4 100644 --- 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/data/models/OperationType.java @@ -18,14 +18,12 @@ * ============LICENSE_END========================================================= */ -package org.onap.cps.ncmp.api.impl.operations; +package org.onap.cps.ncmp.api.data.models; import com.fasterxml.jackson.annotation.JsonValue; -import java.util.Arrays; -import java.util.HashMap; -import java.util.Map; +import java.util.Locale; import lombok.Getter; -import org.onap.cps.ncmp.api.impl.exception.InvalidOperationException; +import org.onap.cps.ncmp.api.data.exceptions.InvalidOperationException; @Getter public enum OperationType { @@ -48,13 +46,6 @@ public enum OperationType { 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. * @@ -62,10 +53,10 @@ public enum OperationType { * @return the operation enum type */ public static OperationType fromOperationName(final String operationName) { - final OperationType operationType = operationNameToOperationEnum.get(operationName); - if (null == operationType) { + try { + return OperationType.valueOf(operationName.toUpperCase(Locale.ENGLISH)); + } catch (final IllegalArgumentException e) { 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/datajobs/DataJobResultService.java b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/datajobs/DataJobResultService.java new file mode 100644 index 0000000000..c6b7a8bd04 --- /dev/null +++ b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/datajobs/DataJobResultService.java @@ -0,0 +1,45 @@ +/* + * ============LICENSE_START======================================================= + * Copyright (C) 2024 Nordix Foundation + * ================================================================================ + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * SPDX-License-Identifier: Apache-2.0 + * ============LICENSE_END========================================================= + */ + +package org.onap.cps.ncmp.api.datajobs; + +/** + * Service interface to retrieve the result of a data job. + * The operations interact with a DMI Plugin to retrieve data job results. + */ +public interface DataJobResultService { + + /** + * Retrieves the result of a specific data job. + * + * @param authorization The authorization header from the REST request. + * @param dmiServiceName The name of the DMI Service relevant to the data job. + * @param dataProducerId The ID of the producer registered by DMI, used for operations related to this request. + * This could include alternate IDs or specific identifiers. + * @param dataProducerJobId The identifier of the data producer job within the DMI system. + * @param destination The destination of the results: Kafka topic name or S3 bucket name. + * @return The result of the data job. + */ + String getDataJobResult(final String authorization, + final String dmiServiceName, + final String dataProducerId, + final String dataProducerJobId, + final String destination); +} diff --git a/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/DataJobService.java b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/datajobs/DataJobService.java index 6122afc808..255b3847eb 100644 --- a/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/DataJobService.java +++ b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/datajobs/DataJobService.java @@ -18,29 +18,40 @@ * ============LICENSE_END========================================================= */ -package org.onap.cps.ncmp.api; +package org.onap.cps.ncmp.api.datajobs; -import org.onap.cps.ncmp.api.models.datajob.DataJobMetadata; -import org.onap.cps.ncmp.api.models.datajob.DataJobReadRequest; -import org.onap.cps.ncmp.api.models.datajob.DataJobWriteRequest; +import java.util.List; +import org.onap.cps.ncmp.api.datajobs.models.DataJobMetadata; +import org.onap.cps.ncmp.api.datajobs.models.DataJobReadRequest; +import org.onap.cps.ncmp.api.datajobs.models.DataJobWriteRequest; +import org.onap.cps.ncmp.api.datajobs.models.SubJobWriteResponse; public interface DataJobService { /** * process read data job operations. * - * @param dataJobId Unique identifier of the job within the request + * @param authorization the authorization header from the REST request + * @param dataJobId unique identifier of the job within the request * @param dataJobMetadata data job request headers * @param dataJobReadRequest read data job request */ - void readDataJob(String dataJobId, DataJobMetadata dataJobMetadata, DataJobReadRequest dataJobReadRequest); + void readDataJob(String authorization, + String dataJobId, + DataJobMetadata dataJobMetadata, + DataJobReadRequest dataJobReadRequest); /** * process write data job operations. * - * @param dataJobId Unique identifier of the job within the request + * @param authorization the authorization header from the REST request + * @param dataJobId unique identifier of the job within the request * @param dataJobMetadata data job request headers * @param dataJobWriteRequest write data job request + * @return a list of sub-job write responses */ - void writeDataJob(String dataJobId, DataJobMetadata dataJobMetadata, DataJobWriteRequest dataJobWriteRequest); + List<SubJobWriteResponse> writeDataJob(String authorization, + String dataJobId, + DataJobMetadata dataJobMetadata, + DataJobWriteRequest dataJobWriteRequest); }
\ No newline at end of file diff --git a/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/datajobs/DataJobStatusService.java b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/datajobs/DataJobStatusService.java new file mode 100644 index 0000000000..9cfc49f1d4 --- /dev/null +++ b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/datajobs/DataJobStatusService.java @@ -0,0 +1,43 @@ +/* + * ============LICENSE_START======================================================= + * Copyright (C) 2024 Nordix Foundation + * ================================================================================ + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * SPDX-License-Identifier: Apache-2.0 + * ============LICENSE_END========================================================= + */ + +package org.onap.cps.ncmp.api.datajobs; + +/** + * Service interface to check the status of a data job. + * The operations interact with a DMI Plugin to retrieve data job statuses. + */ +public interface DataJobStatusService { + + /** + * Retrieves the current status of a specific data job. + * + * @param authorization The authorization header from the REST request. + * @param dmiServiceName The name of the DMI Service relevant to the data job. + * @param dataProducerId The ID of the producer registered by DMI, used for operations related to this request. + * This could include alternate IDs or specific identifiers. + * @param dataProducerJobId The identifier of the data producer job within the DMI system. + * @return The current status of the data job as a String. + */ + String getDataJobStatus(final String authorization, + final String dmiServiceName, + final String dataProducerId, + final String dataProducerJobId); +} diff --git a/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/models/datajob/DataJobMetadata.java b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/datajobs/models/DataJobMetadata.java index dc8037b86f..564352d8db 100644 --- a/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/models/datajob/DataJobMetadata.java +++ b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/datajobs/models/DataJobMetadata.java @@ -18,7 +18,7 @@ * ============LICENSE_END========================================================= */ -package org.onap.cps.ncmp.api.models.datajob; +package org.onap.cps.ncmp.api.datajobs.models; /** * Metadata of read/write data job request. diff --git a/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/models/datajob/DataJobReadRequest.java b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/datajobs/models/DataJobReadRequest.java index f861c3d498..19408b1da1 100644 --- a/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/models/datajob/DataJobReadRequest.java +++ b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/datajobs/models/DataJobReadRequest.java @@ -18,7 +18,7 @@ * ============LICENSE_END========================================================= */ -package org.onap.cps.ncmp.api.models.datajob; +package org.onap.cps.ncmp.api.datajobs.models; import java.util.List; diff --git a/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/models/datajob/DataJobWriteRequest.java b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/datajobs/models/DataJobWriteRequest.java index 254e198b81..d8961b17c0 100644 --- a/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/models/datajob/DataJobWriteRequest.java +++ b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/datajobs/models/DataJobWriteRequest.java @@ -18,7 +18,7 @@ * ============LICENSE_END========================================================= */ -package org.onap.cps.ncmp.api.models.datajob; +package org.onap.cps.ncmp.api.datajobs.models; import java.util.List; diff --git a/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/datajobs/models/DmiWriteOperation.java b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/datajobs/models/DmiWriteOperation.java new file mode 100644 index 0000000000..7e9ca7988b --- /dev/null +++ b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/datajobs/models/DmiWriteOperation.java @@ -0,0 +1,43 @@ +/* + * ============LICENSE_START======================================================= + * Copyright (C) 2024 Nordix Foundation. + * ================================================================================ + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * SPDX-License-Identifier: Apache-2.0 + * ============LICENSE_END========================================================= + */ + +package org.onap.cps.ncmp.api.datajobs.models; + +import java.util.Map; + +/** + * Describes the write data job operation to be forwarded to dmi. + * + * @param path Identifier of a managed object (MO) on a network element. Defines the resource on which + * operation is executed. Typically, is Fully Distinguished Name (FDN). + * @param op Describes the operation to execute. The value can be as below: + * e.g. "add", "replace", "remove", "action" etc. + * @param moduleSetTag The module set tag of the CM Handle. + * @param value The value to be written depends on the type of operation. + * @param operationId Unique identifier of the operation within the request. + * @param privateProperties Contains the private properties of a Cm Handle. + */ +public record DmiWriteOperation( + String path, + String op, + String moduleSetTag, + Object value, + String operationId, + Map<String, String> privateProperties) {} diff --git a/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/datajobs/models/ProducerKey.java b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/datajobs/models/ProducerKey.java new file mode 100644 index 0000000000..ac6b7f8622 --- /dev/null +++ b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/datajobs/models/ProducerKey.java @@ -0,0 +1,36 @@ +/* + * ============LICENSE_START======================================================= + * Copyright (C) 2024 Nordix Foundation + * ================================================================================ + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * SPDX-License-Identifier: Apache-2.0 + * ============LICENSE_END========================================================= + */ + +package org.onap.cps.ncmp.api.datajobs.models; + +/** + * Composite key created from the DMI Service name and a data producer identifier. + * Helps to group of the sub job request for a given DMI Plugin. + * + * @param dmiServiceName Describes the name of the relevant DMI service. + * @param dataProducerIdentifier The name of a data producer identifier from a Cm Handle. + */ +public record ProducerKey(String dmiServiceName, String dataProducerIdentifier) { + + @Override + public String toString() { + return dmiServiceName + "#" + dataProducerIdentifier; + } +}
\ No newline at end of file diff --git a/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/models/datajob/ReadOperation.java b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/datajobs/models/ReadOperation.java index d2b0738969..2459e4cc2e 100644 --- a/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/models/datajob/ReadOperation.java +++ b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/datajobs/models/ReadOperation.java @@ -18,7 +18,7 @@ * ============LICENSE_END========================================================= */ -package org.onap.cps.ncmp.api.models.datajob; +package org.onap.cps.ncmp.api.datajobs.models; import java.util.List; diff --git a/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/datajobs/models/SubJobWriteRequest.java b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/datajobs/models/SubJobWriteRequest.java new file mode 100644 index 0000000000..a7a6573279 --- /dev/null +++ b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/datajobs/models/SubJobWriteRequest.java @@ -0,0 +1,44 @@ +/* + * ============LICENSE_START======================================================= + * Copyright (C) 2024 Nordix Foundation. + * ================================================================================ + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * SPDX-License-Identifier: Apache-2.0 + * ============LICENSE_END========================================================= + */ + +package org.onap.cps.ncmp.api.datajobs.models; + +import java.util.Collection; + +/** + * Request data for a write operation by the DMI Plugin. + * + * @param destination The destination of the results. ( e.g. S3 Bucket) + * @param dataAcceptType Define the data response accept type. + * e.g. "application/vnd.3gpp.object-tree-hierarchical+json", + * "application/vnd.3gpp.object-tree-flat+json" etc. + * @param dataContentType Define the data request content type. + * e.g. "application/3gpp-json-patch+json" etc. + * @param dataProducerId Identifier of the data producer. + * @param dataJobId Identifier for the overall Datajob + * @param data A collection of outgoing write operations. + */ +public record SubJobWriteRequest ( + String destination, + String dataAcceptType, + String dataContentType, + String dataProducerId, + String dataJobId, + Collection<DmiWriteOperation> data) {}
\ No newline at end of file diff --git a/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/models/CmResourceAddress.java b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/datajobs/models/SubJobWriteResponse.java index 21d82fcf56..9cdd8e0590 100644 --- a/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/models/CmResourceAddress.java +++ b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/datajobs/models/SubJobWriteResponse.java @@ -1,12 +1,12 @@ /* * ============LICENSE_START======================================================= - * Copyright (C) 2024 Nordix Foundation + * Copyright (C) 2024 Nordix Foundation. * ================================================================================ * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * - * http://www.apache.org/licenses/LICENSE-2.0 + * 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, @@ -18,8 +18,13 @@ * ============LICENSE_END========================================================= */ -package org.onap.cps.ncmp.api.models; +package org.onap.cps.ncmp.api.datajobs.models; -public record CmResourceAddress(String datastoreName, String cmHandleId, String resourceIdentifier) { - -} +/** + * Request data for a write operation towards DMI Plugin. + * + * @param subJobId Identifier of the sub-job from DMI. + * @param dmiServiceName The provided name of the DMI service from the request. + * @param dataProducerId Identifier of the data producer. + */ +public record SubJobWriteResponse(String subJobId, String dmiServiceName, String dataProducerId) {}
\ No newline at end of file diff --git a/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/models/datajob/WriteOperation.java b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/datajobs/models/WriteOperation.java index c2f6504ce2..807e03f06f 100644 --- a/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/models/datajob/WriteOperation.java +++ b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/datajobs/models/WriteOperation.java @@ -18,7 +18,7 @@ * ============LICENSE_END========================================================= */ -package org.onap.cps.ncmp.api.models.datajob; +package org.onap.cps.ncmp.api.datajobs.models; /** * Holds information of write data job operation. diff --git a/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/exceptions/DmiClientRequestException.java b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/exceptions/DmiClientRequestException.java new file mode 100644 index 0000000000..65ccba8018 --- /dev/null +++ b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/exceptions/DmiClientRequestException.java @@ -0,0 +1,54 @@ +/* + * ============LICENSE_START======================================================= + * Copyright (C) 2022-2024 Nordix Foundation + * ================================================================================ + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * SPDX-License-Identifier: Apache-2.0 + * ============LICENSE_END========================================================= + */ + +package org.onap.cps.ncmp.api.exceptions; + +import lombok.Getter; +import org.onap.cps.ncmp.api.NcmpResponseStatus; + +/** + * Http Client Request exception from dmi service. + */ +@Getter +public class DmiClientRequestException extends NcmpException { + + private static final long serialVersionUID = 6659897770659834797L; + final NcmpResponseStatus ncmpResponseStatus; + final String message; + final String responseBodyAsString; + final int httpStatusCode; + + /** + * Constructor to form exception for dmi service response. + * + * @param httpStatusCode http response code from the client + * @param message response message from the client + * @param responseBodyAsString response body from the client + * @param ncmpResponseStatus ncmp status message and code + */ + public DmiClientRequestException(final int httpStatusCode, final String message, final String responseBodyAsString, + final NcmpResponseStatus ncmpResponseStatus) { + super(message, responseBodyAsString); + this.httpStatusCode = httpStatusCode; + this.message = message; + this.responseBodyAsString = responseBodyAsString; + this.ncmpResponseStatus = ncmpResponseStatus; + } +} diff --git a/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/exception/DmiRequestException.java b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/exceptions/DmiRequestException.java index d41ca70bca..b2cfbc01be 100644 --- a/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/exception/DmiRequestException.java +++ b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/exceptions/DmiRequestException.java @@ -18,7 +18,7 @@ * ============LICENSE_END========================================================= */ -package org.onap.cps.ncmp.api.impl.exception; +package org.onap.cps.ncmp.api.exceptions; /** * Client Based Network CM Proxy exception. diff --git a/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/exceptions/InvalidTopicException.java b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/exceptions/InvalidTopicException.java new file mode 100644 index 0000000000..07b186dae4 --- /dev/null +++ b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/exceptions/InvalidTopicException.java @@ -0,0 +1,40 @@ +/* + * ============LICENSE_START======================================================= + * Copyright (C) 2022 Nordix Foundation + * ================================================================================ + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * SPDX-License-Identifier: Apache-2.0 + * ============LICENSE_END========================================================= + */ + +package org.onap.cps.ncmp.api.exceptions; + +import lombok.Getter; + +public class InvalidTopicException extends RuntimeException { + + @Getter + final String details; + + /** + * Constructor. + * + * @param message the error message + * @param details the error details + */ + public InvalidTopicException(final String message, final String details) { + super(message); + this.details = details; + } +} diff --git a/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/exception/NcmpException.java b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/exceptions/NcmpException.java index 2c75b5d992..6754965866 100644 --- a/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/exception/NcmpException.java +++ b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/exceptions/NcmpException.java @@ -18,7 +18,7 @@ * ============LICENSE_END========================================================= */ -package org.onap.cps.ncmp.api.impl.exception; +package org.onap.cps.ncmp.api.exceptions; import lombok.Getter; diff --git a/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/exceptions/PayloadTooLargeException.java b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/exceptions/PayloadTooLargeException.java new file mode 100644 index 0000000000..edb295dbbf --- /dev/null +++ b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/exceptions/PayloadTooLargeException.java @@ -0,0 +1,31 @@ +/* + * ============LICENSE_START======================================================= + * Copyright (C) 2024 Nordix Foundation + * ================================================================================ + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * SPDX-License-Identifier: Apache-2.0 + * ============LICENSE_END========================================================= + */ + +package org.onap.cps.ncmp.api.exceptions; + +public class PayloadTooLargeException extends RuntimeException { + + /** + * Instantiates a new payload too large exception. + */ + public PayloadTooLargeException(final String message) { + super(message); + } +} diff --git a/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/exception/HttpClientRequestException.java b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/exceptions/PolicyExecutorException.java index 9d307e5d2e..333c12271b 100644 --- a/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/exception/HttpClientRequestException.java +++ b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/exceptions/PolicyExecutorException.java @@ -1,6 +1,6 @@ /* * ============LICENSE_START======================================================= - * Copyright (C) 2022 Nordix Foundation + * Copyright (C) 2024 Nordix Foundation * ================================================================================ * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -18,28 +18,25 @@ * ============LICENSE_END========================================================= */ -package org.onap.cps.ncmp.api.impl.exception; +package org.onap.cps.ncmp.api.exceptions; import lombok.Getter; /** - * Http Client Request exception for passthrough scenarios. + * Exception to be used when policy execution fails or does not allow to proceed. */ @Getter -public class HttpClientRequestException extends NcmpException { +public class PolicyExecutorException extends NcmpException { - private static final long serialVersionUID = 6659897770659834797L; - final Integer httpStatus; + private static final long serialVersionUID = 6659897770659834798L; /** - * Constructor to form exception for passthrough scenarios. + * Constructor to form exception for policy executor responses. * - * @param message message details from NCMP - * @param details response body from the client available as details - * @param httpStatus http status code from the client + * @param message response message + * @param details response details */ - public HttpClientRequestException(final String message, final String details, final Integer httpStatus) { + public PolicyExecutorException(final String message, final String details) { super(message, details); - this.httpStatus = httpStatus; } } diff --git a/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/exception/ServerNcmpException.java b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/exceptions/ServerNcmpException.java index a03a2d3e6c..03dc3bc8dd 100644 --- a/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/exception/ServerNcmpException.java +++ b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/exceptions/ServerNcmpException.java @@ -18,7 +18,7 @@ * ============LICENSE_END========================================================= */ -package org.onap.cps.ncmp.api.impl.exception; +package org.onap.cps.ncmp.api.exceptions; /** * Server Based Network CM Proxy exception. diff --git a/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/DataJobServiceImpl.java b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/DataJobServiceImpl.java deleted file mode 100644 index b4377b84f2..0000000000 --- a/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/DataJobServiceImpl.java +++ /dev/null @@ -1,43 +0,0 @@ -/* - * ============LICENSE_START======================================================= - * Copyright (C) 2024 Nordix Foundation - * ================================================================================ - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - * SPDX-License-Identifier: Apache-2.0 - * ============LICENSE_END========================================================= - */ - -package org.onap.cps.ncmp.api.impl; - -import lombok.extern.slf4j.Slf4j; -import org.onap.cps.ncmp.api.DataJobService; -import org.onap.cps.ncmp.api.models.datajob.DataJobMetadata; -import org.onap.cps.ncmp.api.models.datajob.DataJobReadRequest; -import org.onap.cps.ncmp.api.models.datajob.DataJobWriteRequest; - -@Slf4j -public class DataJobServiceImpl implements DataJobService { - - @Override - public void readDataJob(final String dataJobId, final DataJobMetadata dataJobMetadata, - final DataJobReadRequest dataJobReadRequest) { - log.info("data job id for read operation is: {}", dataJobId); - } - - @Override - public void writeDataJob(final String dataJobId, final DataJobMetadata dataJobMetadata, - final DataJobWriteRequest dataJobWriteRequest) { - log.info("data job id for write operation is: {}", dataJobId); - } -} 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 deleted file mode 100644 index 798a280c8a..0000000000 --- a/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/client/DmiRestClient.java +++ /dev/null @@ -1,102 +0,0 @@ -/* - * ============LICENSE_START======================================================= - * Copyright (C) 2021-2023 Nordix Foundation - * Modifications Copyright (C) 2022 Bell Canada - * ================================================================================ - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * 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.client; - -import com.fasterxml.jackson.databind.JsonNode; -import java.util.Locale; -import lombok.RequiredArgsConstructor; -import lombok.extern.slf4j.Slf4j; -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.OperationType; -import org.springframework.http.HttpEntity; -import org.springframework.http.HttpHeaders; -import org.springframework.http.MediaType; -import org.springframework.http.ResponseEntity; -import org.springframework.stereotype.Component; -import org.springframework.web.client.HttpStatusCodeException; -import org.springframework.web.client.RestTemplate; - -@Component -@RequiredArgsConstructor -@Slf4j -public class DmiRestClient { - - private static final String HEALTH_CHECK_URL_EXTENSION = "/actuator/health"; - private static final String NOT_SPECIFIED = ""; - private final RestTemplate restTemplate; - private final DmiProperties dmiProperties; - - /** - * Sends POST operation to DMI with json body containing module references. - * - * @param dmiResourceUrl dmi resource url - * @param requestBodyAsJsonString json data body - * @param operationType the type of operation being executed (for error reporting only) - * @param authorization contents of Authorization header, or null if not present - * @return response entity of type String - */ - public ResponseEntity<Object> postOperationWithJsonData(final String dmiResourceUrl, - final String requestBodyAsJsonString, - final OperationType operationType, - final String authorization) { - final var httpEntity = new HttpEntity<>(requestBodyAsJsonString, configureHttpHeaders(new HttpHeaders(), - authorization)); - try { - return restTemplate.postForEntity(dmiResourceUrl, httpEntity, Object.class); - } catch (final HttpStatusCodeException httpStatusCodeException) { - final String exceptionMessage = "Unable to " + operationType.toString() + " resource data."; - throw new HttpClientRequestException(exceptionMessage, httpStatusCodeException.getResponseBodyAsString(), - httpStatusCodeException.getStatusCode().value()); - } - } - - /** - * Get DMI plugin health status. - * - * @param dmiPluginBaseUrl the base URL of the dmi-plugin - * @return plugin health status ("UP" is all OK, "" (not-specified) in case of any exception) - */ - public String getDmiHealthStatus(final String dmiPluginBaseUrl) { - final HttpEntity<Object> httpHeaders = new HttpEntity<>(configureHttpHeaders(new HttpHeaders(), null)); - try { - final JsonNode responseHealthStatus = - restTemplate.getForObject(dmiPluginBaseUrl + HEALTH_CHECK_URL_EXTENSION, - JsonNode.class, httpHeaders); - return responseHealthStatus == null ? NOT_SPECIFIED : - responseHealthStatus.get("status").asText(); - } catch (final Exception e) { - log.warn("Failed to retrieve health status from {}. Error Message: {}", dmiPluginBaseUrl, e.getMessage()); - return NOT_SPECIFIED; - } - } - - private HttpHeaders configureHttpHeaders(final HttpHeaders httpHeaders, final String authorization) { - if (dmiProperties.isDmiBasicAuthEnabled()) { - httpHeaders.setBasicAuth(dmiProperties.getAuthUsername(), dmiProperties.getAuthPassword()); - } else if (authorization != null && authorization.toLowerCase(Locale.getDefault()).startsWith("bearer ")) { - httpHeaders.add(HttpHeaders.AUTHORIZATION, authorization); - } - httpHeaders.setContentType(MediaType.APPLICATION_JSON); - return httpHeaders; - } -} diff --git a/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/config/NcmpConfiguration.java b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/config/NcmpConfiguration.java deleted file mode 100644 index c6ff116a7f..0000000000 --- a/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/config/NcmpConfiguration.java +++ /dev/null @@ -1,114 +0,0 @@ -/* - * ============LICENSE_START======================================================= - * 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. - * 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.config; - -import java.util.Arrays; -import lombok.AccessLevel; -import lombok.Getter; -import lombok.RequiredArgsConstructor; -import org.apache.hc.client5.http.config.ConnectionConfig; -import org.apache.hc.client5.http.impl.classic.CloseableHttpClient; -import org.apache.hc.client5.http.impl.classic.HttpClients; -import org.apache.hc.client5.http.impl.io.PoolingHttpClientConnectionManager; -import org.apache.hc.client5.http.impl.io.PoolingHttpClientConnectionManagerBuilder; -import org.apache.hc.core5.util.TimeValue; -import org.apache.hc.core5.util.Timeout; -import org.springframework.beans.factory.annotation.Value; -import org.springframework.beans.factory.config.ConfigurableBeanFactory; -import org.springframework.boot.context.properties.EnableConfigurationProperties; -import org.springframework.boot.web.client.RestTemplateBuilder; -import org.springframework.context.annotation.Bean; -import org.springframework.context.annotation.Configuration; -import org.springframework.context.annotation.Scope; -import org.springframework.http.MediaType; -import org.springframework.http.client.ClientHttpRequestFactory; -import org.springframework.http.client.HttpComponentsClientHttpRequestFactory; -import org.springframework.http.converter.json.MappingJackson2HttpMessageConverter; -import org.springframework.stereotype.Component; -import org.springframework.web.client.RestTemplate; - -@Configuration -@EnableConfigurationProperties(HttpClientConfiguration.class) -@RequiredArgsConstructor(access = AccessLevel.PROTECTED) -public class NcmpConfiguration { - - @Getter - @Component - public static class DmiProperties { - @Value("${ncmp.dmi.auth.username}") - private String authUsername; - @Value("${ncmp.dmi.auth.password}") - private String authPassword; - @Value("${ncmp.dmi.api.base-path}") - private String dmiBasePath; - @Value("${ncmp.dmi.auth.enabled}") - private boolean dmiBasicAuthEnabled; - } - - /** - * Rest template bean. - * - * @param restTemplateBuilder the rest template builder - * @param httpClientConfiguration the http client configuration - * @return rest template instance - */ - @Bean - @Scope(ConfigurableBeanFactory.SCOPE_SINGLETON) - public static RestTemplate restTemplate(final RestTemplateBuilder restTemplateBuilder, - final HttpClientConfiguration httpClientConfiguration) { - - final ConnectionConfig connectionConfig = ConnectionConfig.copy(ConnectionConfig.DEFAULT) - .setConnectTimeout(Timeout.of(httpClientConfiguration.getConnectionTimeoutInSeconds())) - .build(); - - final PoolingHttpClientConnectionManager connectionManager = PoolingHttpClientConnectionManagerBuilder.create() - .setDefaultConnectionConfig(connectionConfig) - .setMaxConnTotal(httpClientConfiguration.getMaximumConnectionsTotal()) - .setMaxConnPerRoute(httpClientConfiguration.getMaximumConnectionsPerRoute()) - .build(); - - final CloseableHttpClient httpClient = HttpClients.custom() - .setConnectionManager(connectionManager) - .evictExpiredConnections() - .evictIdleConnections( - TimeValue.of(httpClientConfiguration.getIdleConnectionEvictionThresholdInSeconds())) - .build(); - - final ClientHttpRequestFactory requestFactory = new HttpComponentsClientHttpRequestFactory(httpClient); - - final RestTemplate restTemplate = restTemplateBuilder - .requestFactory(() -> requestFactory) - .setConnectTimeout(httpClientConfiguration.getConnectionTimeoutInSeconds()) - .build(); - - setRestTemplateMessageConverters(restTemplate); - return restTemplate; - } - - private static void setRestTemplateMessageConverters(final RestTemplate restTemplate) { - final MappingJackson2HttpMessageConverter mappingJackson2HttpMessageConverter = - new MappingJackson2HttpMessageConverter(); - mappingJackson2HttpMessageConverter.setSupportedMediaTypes( - Arrays.asList(MediaType.APPLICATION_JSON, MediaType.TEXT_PLAIN)); - restTemplate.getMessageConverters().add(mappingJackson2HttpMessageConverter); - } - -} diff --git a/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/events/cmsubscription/CmNotificationSubscriptionDelta.java b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/events/cmsubscription/CmNotificationSubscriptionDelta.java deleted file mode 100644 index 8a4beb9560..0000000000 --- a/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/events/cmsubscription/CmNotificationSubscriptionDelta.java +++ /dev/null @@ -1,75 +0,0 @@ -/* - * ============LICENSE_START======================================================= - * Copyright (C) 2024 Nordix Foundation - * ================================================================================ - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - * SPDX-License-Identifier: Apache-2.0 - * ============LICENSE_END========================================================= - */ - -package org.onap.cps.ncmp.api.impl.events.cmsubscription; - -import java.util.ArrayList; -import java.util.HashSet; -import java.util.List; -import java.util.Set; -import lombok.RequiredArgsConstructor; -import org.onap.cps.ncmp.api.impl.events.cmsubscription.model.DmiCmNotificationSubscriptionPredicate; -import org.onap.cps.ncmp.api.impl.events.cmsubscription.service.CmNotificationSubscriptionPersistenceService; -import org.onap.cps.ncmp.api.impl.operations.DatastoreType; -import org.springframework.stereotype.Component; - -@Component -@RequiredArgsConstructor -public class CmNotificationSubscriptionDelta { - - private final CmNotificationSubscriptionPersistenceService cmNotificationSubscriptionPersistenceService; - - /** - * Get the delta for a given predicates list. - * - * @param dmiCmNotificationSubscriptionPredicates list of DmiCmNotificationSubscriptionPredicates - * @return delta list of DmiCmNotificationSubscriptionPredicates - */ - public List<DmiCmNotificationSubscriptionPredicate> getDelta( - final List<DmiCmNotificationSubscriptionPredicate> dmiCmNotificationSubscriptionPredicates) { - final List<DmiCmNotificationSubscriptionPredicate> delta = new ArrayList<>(); - - for (final DmiCmNotificationSubscriptionPredicate cmNotificationSubscriptionPredicate: - dmiCmNotificationSubscriptionPredicates) { - - final Set<String> targetCmHandleIds = new HashSet<>(); - final Set<String> xpaths = new HashSet<>(); - final DatastoreType datastoreType = cmNotificationSubscriptionPredicate.getDatastoreType(); - - for (final String cmHandleId : cmNotificationSubscriptionPredicate.getTargetCmHandleIds()) { - for (final String xpath : cmNotificationSubscriptionPredicate.getXpaths()) { - if (!cmNotificationSubscriptionPersistenceService.isOngoingCmNotificationSubscription(datastoreType, - cmHandleId, xpath)) { - xpaths.add(xpath); - targetCmHandleIds.add(cmHandleId); - - } - } - } - - final DmiCmNotificationSubscriptionPredicate predicateDelta = - new DmiCmNotificationSubscriptionPredicate(targetCmHandleIds, datastoreType, xpaths); - - delta.add(predicateDelta); - } - return delta; - } - -} diff --git a/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/events/cmsubscription/CmNotificationSubscriptionEventsHandler.java b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/events/cmsubscription/CmNotificationSubscriptionEventsHandler.java deleted file mode 100644 index 50a5df537d..0000000000 --- a/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/events/cmsubscription/CmNotificationSubscriptionEventsHandler.java +++ /dev/null @@ -1,70 +0,0 @@ -/* - * ============LICENSE_START======================================================= - * Copyright (c) 2024 Nordix Foundation. - * ================================================================================ - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an 'AS IS' BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - * SPDX-License-Identifier: Apache-2.0 - * ============LICENSE_END========================================================= - */ - -package org.onap.cps.ncmp.api.impl.events.cmsubscription; - -import lombok.RequiredArgsConstructor; -import org.onap.cps.ncmp.api.impl.events.cmsubscription.producer.CmNotificationSubscriptionDmiInEventProducer; -import org.onap.cps.ncmp.api.impl.events.cmsubscription.producer.CmNotificationSubscriptionNcmpOutEventProducer; -import org.onap.cps.ncmp.events.cmnotificationsubscription_merge1_0_0.ncmp_to_dmi.CmNotificationSubscriptionDmiInEvent; -import org.onap.cps.ncmp.events.cmsubscription_merge1_0_0.ncmp_to_client.CmNotificationSubscriptionNcmpOutEvent; -import org.springframework.stereotype.Component; - -@Component -@RequiredArgsConstructor -public class CmNotificationSubscriptionEventsHandler { - private final CmNotificationSubscriptionNcmpOutEventProducer cmNotificationSubscriptionNcmpOutEventProducer; - private final CmNotificationSubscriptionDmiInEventProducer cmNotificationSubscriptionDmiInEventProducer; - - /** - * Publish the event to the client who requested the subscription with key as subscription id and event is Cloud - * Event compliant. - * - * @param subscriptionId Cm Subscription id - * @param eventType Type of event - * @param cmNotificationSubscriptionNcmpOutEvent Cm Notification Subscription Event for the - * client - * @param isScheduledEvent Determines if the event is to be scheduled - * or published now - */ - public void publishCmNotificationSubscriptionNcmpOutEvent(final String subscriptionId, final String eventType, - final CmNotificationSubscriptionNcmpOutEvent - cmNotificationSubscriptionNcmpOutEvent, - final boolean isScheduledEvent) { - cmNotificationSubscriptionNcmpOutEventProducer.publishCmNotificationSubscriptionNcmpOutEvent(subscriptionId, - eventType, cmNotificationSubscriptionNcmpOutEvent, isScheduledEvent); - } - - /** - * Publish the event to the provided dmi plugin with key as subscription id and the event is in Cloud Event format. - * - * @param subscriptionId Cm Subscription id - * @param dmiPluginName Dmi Plugin Name - * @param eventType Type of event - * @param cmNotificationSubscriptionDmiInEvent Cm Notification Subscription event for Dmi - */ - public void publishCmNotificationSubscriptionDmiInEvent(final String subscriptionId, final String dmiPluginName, - final String eventType, - final CmNotificationSubscriptionDmiInEvent - cmNotificationSubscriptionDmiInEvent) { - cmNotificationSubscriptionDmiInEventProducer.publishCmNotificationSubscriptionDmiInEvent(subscriptionId, - dmiPluginName, eventType, cmNotificationSubscriptionDmiInEvent); - } -} diff --git a/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/events/cmsubscription/CmNotificationSubscriptionMappersHandler.java b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/events/cmsubscription/CmNotificationSubscriptionMappersHandler.java deleted file mode 100644 index 73f9563ecf..0000000000 --- a/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/events/cmsubscription/CmNotificationSubscriptionMappersHandler.java +++ /dev/null @@ -1,78 +0,0 @@ -/* - * ============LICENSE_START======================================================= - * Copyright (C) 2024 Nordix Foundation - * ================================================================================ - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - * SPDX-License-Identifier: Apache-2.0 - * ============LICENSE_END========================================================= - */ - -package org.onap.cps.ncmp.api.impl.events.cmsubscription; - -import java.util.List; -import java.util.Map; -import lombok.RequiredArgsConstructor; -import org.onap.cps.ncmp.api.impl.events.cmsubscription.mapper.CmNotificationSubscriptionDmiInEventMapper; -import org.onap.cps.ncmp.api.impl.events.cmsubscription.mapper.CmNotificationSubscriptionNcmpOutEventMapper; -import org.onap.cps.ncmp.api.impl.events.cmsubscription.model.DmiCmNotificationSubscriptionDetails; -import org.onap.cps.ncmp.api.impl.events.cmsubscription.model.DmiCmNotificationSubscriptionPredicate; -import org.onap.cps.ncmp.events.cmnotificationsubscription_merge1_0_0.ncmp_to_dmi.CmNotificationSubscriptionDmiInEvent; -import org.onap.cps.ncmp.events.cmsubscription_merge1_0_0.ncmp_to_client.CmNotificationSubscriptionNcmpOutEvent; -import org.springframework.stereotype.Component; - -@Component -@RequiredArgsConstructor -public class CmNotificationSubscriptionMappersHandler { - - private final CmNotificationSubscriptionDmiInEventMapper cmNotificationSubscriptionDmiInEventMapper; - private final CmNotificationSubscriptionNcmpOutEventMapper cmNotificationSubscriptionNcmpOutEventMapper; - - /** - * Mapper to form a request for the DMI Plugin for the Cm Notification Subscription. - * - * @param dmiCmNotificationSubscriptionPredicates Collection of Cm Notification Subscription predicates - * @return cm notification subscription dmi in event - */ - public CmNotificationSubscriptionDmiInEvent toCmNotificationSubscriptionDmiInEvent( - final List<DmiCmNotificationSubscriptionPredicate> dmiCmNotificationSubscriptionPredicates) { - return cmNotificationSubscriptionDmiInEventMapper.toCmNotificationSubscriptionDmiInEvent( - dmiCmNotificationSubscriptionPredicates); - } - - /** - * Mapper to form a response for the client for the Cm Notification Subscription. - * - * @param subscriptionId Cm Notification Subscription id - * @param dmiCmNotificationSubscriptionDetailsMap contains CmNotificationSubscriptionDetails per dmi plugin - * @return CmNotificationSubscriptionNcmpOutEvent to sent back to the client - */ - public CmNotificationSubscriptionNcmpOutEvent toCmNotificationSubscriptionNcmpOutEvent(final String subscriptionId, - final Map<String, DmiCmNotificationSubscriptionDetails> dmiCmNotificationSubscriptionDetailsMap) { - return cmNotificationSubscriptionNcmpOutEventMapper.toCmNotificationSubscriptionNcmpOutEvent(subscriptionId, - dmiCmNotificationSubscriptionDetailsMap); - } - - /** - * Mapper to form a rejected response for the client for the Cm Notification Subscription Request. - * - * @param subscriptionId subscription id - * @param rejectedTargetFilters list of rejected target filters for the subscription request - * @return to sent back to the client - */ - public CmNotificationSubscriptionNcmpOutEvent toCmNotificationSubscriptionNcmpOutEventForRejectedRequest( - final String subscriptionId, final List<String> rejectedTargetFilters) { - return cmNotificationSubscriptionNcmpOutEventMapper.toCmNotificationSubscriptionNcmpOutEventForRejectedRequest( - subscriptionId, rejectedTargetFilters); - } -} diff --git a/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/events/cmsubscription/CmNotificationSubscriptionNcmpOutEventPublishingTask.java b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/events/cmsubscription/CmNotificationSubscriptionNcmpOutEventPublishingTask.java deleted file mode 100644 index f7dd51e637..0000000000 --- a/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/events/cmsubscription/CmNotificationSubscriptionNcmpOutEventPublishingTask.java +++ /dev/null @@ -1,63 +0,0 @@ -/* - * ============LICENSE_START======================================================= - * Copyright (C) 2024 Nordix Foundation - * ================================================================================ - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - * SPDX-License-Identifier: Apache-2.0 - * ============LICENSE_END========================================================= - */ - -package org.onap.cps.ncmp.api.impl.events.cmsubscription; - -import static org.onap.cps.ncmp.api.impl.events.cmsubscription.producer.CmNotificationSubscriptionNcmpOutEventProducer.buildAndGetCmNotificationNcmpOutEventAsCloudEvent; - -import io.cloudevents.CloudEvent; -import java.util.Map; -import lombok.RequiredArgsConstructor; -import lombok.extern.slf4j.Slf4j; -import org.onap.cps.events.EventsPublisher; -import org.onap.cps.ncmp.api.impl.events.cmsubscription.model.DmiCmNotificationSubscriptionDetails; -import org.onap.cps.ncmp.events.cmsubscription_merge1_0_0.ncmp_to_client.CmNotificationSubscriptionNcmpOutEvent; -import org.onap.cps.utils.JsonObjectMapper; - -@Slf4j -@RequiredArgsConstructor -public class CmNotificationSubscriptionNcmpOutEventPublishingTask implements Runnable { - - private final String topicName; - private final String subscriptionId; - private final String eventType; - private final EventsPublisher<CloudEvent> eventsPublisher; - private final JsonObjectMapper jsonObjectMapper; - private final CmNotificationSubscriptionMappersHandler cmNotificationSubscriptionMappersHandler; - private final DmiCmNotificationSubscriptionCacheHandler dmiCmNotificationSubscriptionCacheHandler; - - /** - * Delegating the responsibility of publishing CmNotificationSubscriptionNcmpOutEvent as a separate task which will - * be called after a specified delay. - */ - @Override - public void run() { - final Map<String, DmiCmNotificationSubscriptionDetails> dmiCmNotificationSubscriptionDetailsMap = - dmiCmNotificationSubscriptionCacheHandler.get(subscriptionId); - final CmNotificationSubscriptionNcmpOutEvent cmNotificationSubscriptionNcmpOutEvent = - cmNotificationSubscriptionMappersHandler.toCmNotificationSubscriptionNcmpOutEvent(subscriptionId, - dmiCmNotificationSubscriptionDetailsMap); - eventsPublisher.publishCloudEvent(topicName, subscriptionId, - buildAndGetCmNotificationNcmpOutEventAsCloudEvent(jsonObjectMapper, subscriptionId, eventType, - cmNotificationSubscriptionNcmpOutEvent)); - dmiCmNotificationSubscriptionCacheHandler - .removeAcceptedAndRejectedDmiCmNotificationSubscriptionEntries(subscriptionId); - } -} diff --git a/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/events/cmsubscription/DmiCmNotificationSubscriptionCacheHandler.java b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/events/cmsubscription/DmiCmNotificationSubscriptionCacheHandler.java deleted file mode 100644 index b5370bf1ed..0000000000 --- a/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/events/cmsubscription/DmiCmNotificationSubscriptionCacheHandler.java +++ /dev/null @@ -1,193 +0,0 @@ -/* - * ============LICENSE_START======================================================= - * Copyright (C) 2024 Nordix Foundation - * ================================================================================ - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - * SPDX-License-Identifier: Apache-2.0 - * ============LICENSE_END========================================================= - */ - -package org.onap.cps.ncmp.api.impl.events.cmsubscription; - -import static org.onap.cps.ncmp.api.impl.events.cmsubscription.model.CmNotificationSubscriptionStatus.PENDING; - -import java.util.ArrayList; -import java.util.Collection; -import java.util.HashMap; -import java.util.HashSet; -import java.util.List; -import java.util.Map; -import java.util.Set; -import java.util.stream.Collectors; -import lombok.RequiredArgsConstructor; -import org.onap.cps.ncmp.api.impl.events.cmsubscription.model.CmNotificationSubscriptionStatus; -import org.onap.cps.ncmp.api.impl.events.cmsubscription.model.DmiCmNotificationSubscriptionDetails; -import org.onap.cps.ncmp.api.impl.events.cmsubscription.model.DmiCmNotificationSubscriptionPredicate; -import org.onap.cps.ncmp.api.impl.events.cmsubscription.service.CmNotificationSubscriptionPersistenceService; -import org.onap.cps.ncmp.api.impl.inventory.InventoryPersistence; -import org.onap.cps.ncmp.api.impl.operations.DatastoreType; -import org.onap.cps.ncmp.api.impl.yangmodels.YangModelCmHandle; -import org.onap.cps.ncmp.events.cmnotificationsubscription_merge1_0_0.client_to_ncmp.Predicate; -import org.springframework.stereotype.Component; - -@Component -@RequiredArgsConstructor -public class DmiCmNotificationSubscriptionCacheHandler { - - private final CmNotificationSubscriptionPersistenceService cmNotificationSubscriptionPersistenceService; - private final Map<String, Map<String, DmiCmNotificationSubscriptionDetails>> cmNotificationSubscriptionCache; - private final InventoryPersistence inventoryPersistence; - - /** - * Adds new subscription to the subscription cache. - * - * @param subscriptionId subscription id - * @param predicates subscription request predicates - */ - public void add(final String subscriptionId, final List<Predicate> predicates) { - cmNotificationSubscriptionCache.put(subscriptionId, createDmiCmNotificationSubscriptionsPerDmi(predicates)); - } - - /** - * Get cm notification subscription cache entry via subscription id. - * - * @param subscriptionId subscription id - * @return map of dmi cm notification subscriptions per dmi - */ - public Map<String, DmiCmNotificationSubscriptionDetails> get(final String subscriptionId) { - return cmNotificationSubscriptionCache.get(subscriptionId); - } - - - /** - * Remove cache entries with CmNotificationSubscriptionStatus ACCEPTED/REJECTED via subscription id. - * - * @param subscriptionId subscription id as key in CM notification Subscription cache. - */ - public void removeAcceptedAndRejectedDmiCmNotificationSubscriptionEntries(final String subscriptionId) { - final Map<String, DmiCmNotificationSubscriptionDetails> dmiCmNotificationSubscriptionsPerDmi = - cmNotificationSubscriptionCache.get(subscriptionId); - final Map<String, DmiCmNotificationSubscriptionDetails> updatedDmiCmNotificationSubscriptionsPerDmi = - dmiCmNotificationSubscriptionsPerDmi.entrySet().stream().filter( - dmiCmNotificationSubscription -> - !isAcceptedOrRejected(dmiCmNotificationSubscription.getValue())) - .collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue)); - cmNotificationSubscriptionCache.put(subscriptionId, updatedDmiCmNotificationSubscriptionsPerDmi); - } - - /** - * Creates map of subscription details per DMI. - * - * @param predicates CM Subscription Create Request Predicates - * @return Map of DmiCmNotificationSubscription per DMI plugin - */ - public Map<String, DmiCmNotificationSubscriptionDetails> createDmiCmNotificationSubscriptionsPerDmi( - final List<Predicate> predicates) { - final Map<String, DmiCmNotificationSubscriptionDetails> dmiCmNotificationSubscriptionDetailsPerDmi = - new HashMap<>(); - for (final Predicate requestPredicate : predicates) { - final List<String> targetFilter = requestPredicate.getTargetFilter(); - final DatastoreType datastoreType = DatastoreType.fromDatastoreName( - requestPredicate.getScopeFilter().getDatastore().toString()); - final Set<String> xpaths = new HashSet<>(requestPredicate.getScopeFilter().getXpathFilter()); - final Map<String, Set<String>> targetCmHandlesByDmiMap = groupTargetCmHandleIdsByDmi(targetFilter); - for (final Map.Entry<String, Set<String>> targetCmHandlesByDmi: targetCmHandlesByDmiMap.entrySet()) { - final DmiCmNotificationSubscriptionPredicate dmiCmNotificationSubscriptionPredicate = - new DmiCmNotificationSubscriptionPredicate(targetCmHandlesByDmi.getValue(), - datastoreType, xpaths); - updateDmiCmNotificationSubscriptionDetailsPerDmi(targetCmHandlesByDmi.getKey(), - dmiCmNotificationSubscriptionPredicate, - dmiCmNotificationSubscriptionDetailsPerDmi); - } - } - return dmiCmNotificationSubscriptionDetailsPerDmi; - } - - /** - * Update status in map of subscription details per DMI. - * - * @param subscriptionId String of subscription Id - * @param dmiServiceName String of dmiServiceName - * @param status String of status - * - */ - public void updateDmiCmNotificationSubscriptionStatusPerDmi(final String subscriptionId, - final String dmiServiceName, final CmNotificationSubscriptionStatus status) { - final Map<String, DmiCmNotificationSubscriptionDetails> dmiCmNotificationSubscriptionDetailsPerDmi = - cmNotificationSubscriptionCache.get(subscriptionId); - dmiCmNotificationSubscriptionDetailsPerDmi.get(dmiServiceName).setCmNotificationSubscriptionStatus(status); - cmNotificationSubscriptionCache.put(subscriptionId, dmiCmNotificationSubscriptionDetailsPerDmi); - - } - - /** - * Persist map of subscription details per DMI. - * - * @param subscriptionId String of subscription Id - * @param dmiServiceName String of dmiServiceName - * - */ - public void persistIntoDatabasePerDmi(final String subscriptionId, final String dmiServiceName) { - final List<DmiCmNotificationSubscriptionPredicate> dmiCmNotificationSubscriptionPredicateList = - cmNotificationSubscriptionCache.get(subscriptionId).get(dmiServiceName) - .getDmiCmNotificationSubscriptionPredicates(); - for (final DmiCmNotificationSubscriptionPredicate dmiCmNotificationSubscriptionPredicate: - dmiCmNotificationSubscriptionPredicateList) { - final DatastoreType datastoreType = dmiCmNotificationSubscriptionPredicate.getDatastoreType(); - final Set<String> cmHandles = dmiCmNotificationSubscriptionPredicate.getTargetCmHandleIds(); - final Set<String> xpaths = dmiCmNotificationSubscriptionPredicate.getXpaths(); - - for (final String cmHandle: cmHandles) { - for (final String xpath: xpaths) { - cmNotificationSubscriptionPersistenceService.addCmNotificationSubscription(datastoreType, cmHandle, - xpath, subscriptionId); - } - } - } - } - - private void updateDmiCmNotificationSubscriptionDetailsPerDmi( - final String dmiServiceName, - final DmiCmNotificationSubscriptionPredicate dmiCmNotificationSubscriptionPredicate, - final Map<String, DmiCmNotificationSubscriptionDetails> dmiCmNotificationSubscriptionDetailsPerDmi) { - if (dmiCmNotificationSubscriptionDetailsPerDmi.containsKey(dmiServiceName)) { - dmiCmNotificationSubscriptionDetailsPerDmi.get(dmiServiceName) - .getDmiCmNotificationSubscriptionPredicates().add(dmiCmNotificationSubscriptionPredicate); - } else { - dmiCmNotificationSubscriptionDetailsPerDmi.put(dmiServiceName, - new DmiCmNotificationSubscriptionDetails( - new ArrayList<>(List.of(dmiCmNotificationSubscriptionPredicate)), - PENDING)); - } - } - - private Map<String, Set<String>> groupTargetCmHandleIdsByDmi(final List<String> targetCmHandleIds) { - final Map<String, Set<String>> targetCmHandlesByDmiServiceNames = new HashMap<>(); - final Collection<YangModelCmHandle> yangModelCmHandles = - inventoryPersistence.getYangModelCmHandles(targetCmHandleIds); - - for (final YangModelCmHandle yangModelCmHandle : yangModelCmHandles) { - final String dmiServiceName = yangModelCmHandle.getDmiServiceName(); - targetCmHandlesByDmiServiceNames.putIfAbsent(dmiServiceName, new HashSet<>()); - targetCmHandlesByDmiServiceNames.get(dmiServiceName).add(yangModelCmHandle.getId()); - } - return targetCmHandlesByDmiServiceNames; - } - - private boolean isAcceptedOrRejected( - final DmiCmNotificationSubscriptionDetails dmiCmNotificationSubscription) { - return dmiCmNotificationSubscription.getCmNotificationSubscriptionStatus().toString().equals("ACCEPTED") - || dmiCmNotificationSubscription.getCmNotificationSubscriptionStatus().toString().equals("REJECTED"); - } -}
\ No newline at end of file diff --git a/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/events/cmsubscription/consumer/CmNotificationSubscriptionDmiOutEventConsumer.java b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/events/cmsubscription/consumer/CmNotificationSubscriptionDmiOutEventConsumer.java deleted file mode 100644 index fb89aae3f8..0000000000 --- a/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/events/cmsubscription/consumer/CmNotificationSubscriptionDmiOutEventConsumer.java +++ /dev/null @@ -1,103 +0,0 @@ -/* - * ============LICENSE_START======================================================= - * Copyright (C) 2024 Nordix Foundation - * ================================================================================ - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - * SPDX-License-Identifier: Apache-2.0 - * ============LICENSE_END========================================================= - */ - -package org.onap.cps.ncmp.api.impl.events.cmsubscription.consumer; - -import static org.onap.cps.ncmp.api.impl.events.mapper.CloudEventMapper.toTargetEvent; - -import io.cloudevents.CloudEvent; -import java.util.Map; -import lombok.RequiredArgsConstructor; -import lombok.extern.slf4j.Slf4j; -import org.apache.kafka.clients.consumer.ConsumerRecord; -import org.onap.cps.ncmp.api.impl.events.cmsubscription.CmNotificationSubscriptionEventsHandler; -import org.onap.cps.ncmp.api.impl.events.cmsubscription.CmNotificationSubscriptionMappersHandler; -import org.onap.cps.ncmp.api.impl.events.cmsubscription.DmiCmNotificationSubscriptionCacheHandler; -import org.onap.cps.ncmp.api.impl.events.cmsubscription.model.CmNotificationSubscriptionStatus; -import org.onap.cps.ncmp.api.impl.events.cmsubscription.model.DmiCmNotificationSubscriptionDetails; -import org.onap.cps.ncmp.events.cmnotificationsubscription_merge1_0_0.dmi_to_ncmp.CmNotificationSubscriptionDmiOutEvent; -import org.onap.cps.ncmp.events.cmsubscription_merge1_0_0.ncmp_to_client.CmNotificationSubscriptionNcmpOutEvent; -import org.springframework.kafka.annotation.KafkaListener; -import org.springframework.stereotype.Component; - -@Component -@Slf4j -@RequiredArgsConstructor -public class CmNotificationSubscriptionDmiOutEventConsumer { - - private final DmiCmNotificationSubscriptionCacheHandler dmiCmNotificationSubscriptionCacheHandler; - private final CmNotificationSubscriptionEventsHandler cmNotificationSubscriptionEventsHandler; - private final CmNotificationSubscriptionMappersHandler cmNotificationSubscriptionMappersHandler; - - /** - * Consume the Cm Notification Subscription event from the dmi-plugin. - * - * @param cmNotificationSubscriptionDmiOutEventConsumerRecord the event to be consumed - */ - @KafkaListener(topics = "${app.ncmp.avc.subscription-response-topic}", - containerFactory = "cloudEventConcurrentKafkaListenerContainerFactory") - public void consumeCmNotificationSubscriptionDmiOutEvent( - final ConsumerRecord<String, CloudEvent> cmNotificationSubscriptionDmiOutEventConsumerRecord) { - final CloudEvent cloudEvent = cmNotificationSubscriptionDmiOutEventConsumerRecord.value(); - final CmNotificationSubscriptionDmiOutEvent cmNotificationSubscriptionDmiOutEvent = - toTargetEvent(cloudEvent, CmNotificationSubscriptionDmiOutEvent.class); - final String correlationId = String.valueOf(cloudEvent.getExtension("correlationid")); - if ("subscriptionCreateResponse".equals(cloudEvent.getType()) && cmNotificationSubscriptionDmiOutEvent != null - && correlationId != null) { - handleCmSubscriptionCreate(correlationId, cmNotificationSubscriptionDmiOutEvent); - } - } - - private void handleCmSubscriptionCreate(final String correlationId, - final CmNotificationSubscriptionDmiOutEvent cmNotificationSubscriptionDmiOutEvent) { - final String subscriptionId = correlationId.split("#")[0]; - final String dmiPluginName = correlationId.split("#")[1]; - - if ("ACCEPTED".equals(cmNotificationSubscriptionDmiOutEvent.getData().getStatusMessage())) { - handleCacheStatusPerDmi(subscriptionId, dmiPluginName, CmNotificationSubscriptionStatus.ACCEPTED); - dmiCmNotificationSubscriptionCacheHandler.persistIntoDatabasePerDmi(subscriptionId, dmiPluginName); - handleEventsStatusPerDmi(subscriptionId); - } - - if ("REJECTED".equals(cmNotificationSubscriptionDmiOutEvent.getData().getStatusMessage())) { - handleCacheStatusPerDmi(subscriptionId, dmiPluginName, CmNotificationSubscriptionStatus.REJECTED); - handleEventsStatusPerDmi(subscriptionId); - } - - log.info("Cm Subscription with id : {} handled by the dmi-plugin : {} has the status : {}", subscriptionId, - dmiPluginName, cmNotificationSubscriptionDmiOutEvent.getData().getStatusMessage()); - } - - private void handleCacheStatusPerDmi(final String subscriptionId, final String dmiPluginName, - final CmNotificationSubscriptionStatus cmNotificationSubscriptionStatus) { - dmiCmNotificationSubscriptionCacheHandler.updateDmiCmNotificationSubscriptionStatusPerDmi(subscriptionId, - dmiPluginName, cmNotificationSubscriptionStatus); - } - - private void handleEventsStatusPerDmi(final String subscriptionId) { - final Map<String, DmiCmNotificationSubscriptionDetails> dmiCmNotificationSubscriptionDetailsPerDmi = - dmiCmNotificationSubscriptionCacheHandler.get(subscriptionId); - final CmNotificationSubscriptionNcmpOutEvent cmNotificationSubscriptionNcmpOutEvent = - cmNotificationSubscriptionMappersHandler.toCmNotificationSubscriptionNcmpOutEvent(subscriptionId, - dmiCmNotificationSubscriptionDetailsPerDmi); - cmNotificationSubscriptionEventsHandler.publishCmNotificationSubscriptionNcmpOutEvent(subscriptionId, - "subscriptionCreateResponse", cmNotificationSubscriptionNcmpOutEvent, false); - } -} diff --git a/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/events/cmsubscription/mapper/CmNotificationSubscriptionNcmpOutEventMapper.java b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/events/cmsubscription/mapper/CmNotificationSubscriptionNcmpOutEventMapper.java deleted file mode 100644 index ea21751691..0000000000 --- a/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/events/cmsubscription/mapper/CmNotificationSubscriptionNcmpOutEventMapper.java +++ /dev/null @@ -1,114 +0,0 @@ -/* - * ============LICENSE_START======================================================= - * Copyright (C) 2024 Nordix Foundation - * ================================================================================ - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - * SPDX-License-Identifier: Apache-2.0 - * ============LICENSE_END========================================================= - */ - -package org.onap.cps.ncmp.api.impl.events.cmsubscription.mapper; - -import java.util.ArrayList; -import java.util.List; -import java.util.Map; -import lombok.RequiredArgsConstructor; -import org.onap.cps.ncmp.api.impl.events.cmsubscription.model.CmNotificationSubscriptionStatus; -import org.onap.cps.ncmp.api.impl.events.cmsubscription.model.DmiCmNotificationSubscriptionDetails; -import org.onap.cps.ncmp.api.impl.events.cmsubscription.model.DmiCmNotificationSubscriptionPredicate; -import org.onap.cps.ncmp.events.cmsubscription_merge1_0_0.ncmp_to_client.CmNotificationSubscriptionNcmpOutEvent; -import org.onap.cps.ncmp.events.cmsubscription_merge1_0_0.ncmp_to_client.Data; -import org.springframework.stereotype.Component; - -@Component -@RequiredArgsConstructor -public class CmNotificationSubscriptionNcmpOutEventMapper { - - /** - * Mapper to form a response for the client for the Cm Notification Subscription. - * - * @param subscriptionId Cm Notification Subscription Id - * @param dmiCmNotificationSubscriptionDetailsMap contains CmNotificationSubscriptionDetails per dmi plugin - * @return CmNotificationSubscriptionNcmpOutEvent to sent back to the client - */ - public CmNotificationSubscriptionNcmpOutEvent toCmNotificationSubscriptionNcmpOutEvent(final String subscriptionId, - final Map<String, DmiCmNotificationSubscriptionDetails> dmiCmNotificationSubscriptionDetailsMap) { - - final CmNotificationSubscriptionNcmpOutEvent cmNotificationSubscriptionNcmpOutEvent = - new CmNotificationSubscriptionNcmpOutEvent(); - final Data cmSubscriptionData = new Data(); - cmSubscriptionData.setSubscriptionId(subscriptionId); - populateCmNotificationSubscriptionNcmpOutEventWithCmHandleIds(dmiCmNotificationSubscriptionDetailsMap, - cmSubscriptionData); - cmNotificationSubscriptionNcmpOutEvent.setData(cmSubscriptionData); - - return cmNotificationSubscriptionNcmpOutEvent; - } - - /** - * Mapper to form a rejected response for the client for the Cm Notification Subscription Request. - * - * @param subscriptionId subscription id - * @param rejectedTargetFilters list of rejected target filters for the subscription request - * @return to sent back to the client - */ - public CmNotificationSubscriptionNcmpOutEvent toCmNotificationSubscriptionNcmpOutEventForRejectedRequest( - final String subscriptionId, final List<String> rejectedTargetFilters) { - final CmNotificationSubscriptionNcmpOutEvent cmNotificationSubscriptionNcmpOutEvent = - new CmNotificationSubscriptionNcmpOutEvent(); - final Data cmSubscriptionData = new Data(); - cmSubscriptionData.setSubscriptionId(subscriptionId); - cmSubscriptionData.setRejectedTargets(rejectedTargetFilters); - cmNotificationSubscriptionNcmpOutEvent.setData(cmSubscriptionData); - return cmNotificationSubscriptionNcmpOutEvent; - } - - private void populateCmNotificationSubscriptionNcmpOutEventWithCmHandleIds( - final Map<String, DmiCmNotificationSubscriptionDetails> dmiCmNotificationSubscriptionDetailsMap, - final Data cmSubscriptionData) { - - final List<String> acceptedCmHandleIds = new ArrayList<>(); - final List<String> pendingCmHandleIds = new ArrayList<>(); - final List<String> rejectedCmHandleIds = new ArrayList<>(); - - dmiCmNotificationSubscriptionDetailsMap.forEach((dmiPluginName, dmiCmNotificationSubscriptionDetails) -> { - final CmNotificationSubscriptionStatus cmNotificationSubscriptionStatus = - dmiCmNotificationSubscriptionDetails.getCmNotificationSubscriptionStatus(); - final List<DmiCmNotificationSubscriptionPredicate> dmiCmNotificationSubscriptionPredicates = - dmiCmNotificationSubscriptionDetails.getDmiCmNotificationSubscriptionPredicates(); - - switch (cmNotificationSubscriptionStatus) { - case ACCEPTED -> acceptedCmHandleIds.addAll( - extractCmHandleIds(dmiCmNotificationSubscriptionPredicates)); - case PENDING -> pendingCmHandleIds.addAll(extractCmHandleIds(dmiCmNotificationSubscriptionPredicates)); - default -> rejectedCmHandleIds.addAll(extractCmHandleIds(dmiCmNotificationSubscriptionPredicates)); - } - }); - - cmSubscriptionData.setAcceptedTargets(acceptedCmHandleIds); - cmSubscriptionData.setPendingTargets(pendingCmHandleIds); - cmSubscriptionData.setRejectedTargets(rejectedCmHandleIds); - - } - - private List<String> extractCmHandleIds( - final List<DmiCmNotificationSubscriptionPredicate> dmiCmNotificationSubscriptionPredicates) { - final List<String> cmHandleIds = new ArrayList<>(); - dmiCmNotificationSubscriptionPredicates.forEach(dmiCmNotificationSubscriptionPredicate -> cmHandleIds.addAll( - dmiCmNotificationSubscriptionPredicate.getTargetCmHandleIds())); - - return cmHandleIds; - } - -} diff --git a/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/events/cmsubscription/producer/CmNotificationSubscriptionNcmpOutEventProducer.java b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/events/cmsubscription/producer/CmNotificationSubscriptionNcmpOutEventProducer.java deleted file mode 100644 index ac5de07f0a..0000000000 --- a/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/events/cmsubscription/producer/CmNotificationSubscriptionNcmpOutEventProducer.java +++ /dev/null @@ -1,146 +0,0 @@ -/* - * ============LICENSE_START======================================================= - * Copyright (C) 2024 Nordix Foundation - * ================================================================================ - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - * SPDX-License-Identifier: Apache-2.0 - * ============LICENSE_END========================================================= - */ - -package org.onap.cps.ncmp.api.impl.events.cmsubscription.producer; - -import io.cloudevents.CloudEvent; -import io.cloudevents.core.builder.CloudEventBuilder; -import java.net.URI; -import java.util.Map; -import java.util.UUID; -import java.util.concurrent.ConcurrentHashMap; -import java.util.concurrent.Executors; -import java.util.concurrent.ScheduledExecutorService; -import java.util.concurrent.ScheduledFuture; -import java.util.concurrent.TimeUnit; -import lombok.RequiredArgsConstructor; -import lombok.extern.slf4j.Slf4j; -import org.onap.cps.events.EventsPublisher; -import org.onap.cps.ncmp.api.impl.events.cmsubscription.CmNotificationSubscriptionMappersHandler; -import org.onap.cps.ncmp.api.impl.events.cmsubscription.CmNotificationSubscriptionNcmpOutEventPublishingTask; -import org.onap.cps.ncmp.api.impl.events.cmsubscription.DmiCmNotificationSubscriptionCacheHandler; -import org.onap.cps.ncmp.events.cmsubscription_merge1_0_0.ncmp_to_client.CmNotificationSubscriptionNcmpOutEvent; -import org.onap.cps.utils.JsonObjectMapper; -import org.springframework.beans.factory.annotation.Value; -import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; -import org.springframework.stereotype.Component; - -@Component -@Slf4j -@RequiredArgsConstructor -@ConditionalOnProperty(name = "notification.enabled", havingValue = "true", matchIfMissing = true) -public class CmNotificationSubscriptionNcmpOutEventProducer { - - @Value("${app.ncmp.avc.subscription-outcome-topic}") - private String cmNotificationSubscriptionNcmpOutEventTopic; - - @Value("${ncmp.timers.subscription-forwarding.dmi-response-timeout-ms}") - private Integer cmNotificationSubscriptionDmiOutEventTimeoutInMs; - - private final EventsPublisher<CloudEvent> eventsPublisher; - private final JsonObjectMapper jsonObjectMapper; - private final CmNotificationSubscriptionMappersHandler cmNotificationSubscriptionMappersHandler; - private final DmiCmNotificationSubscriptionCacheHandler dmiCmNotificationSubscriptionCacheHandler; - private final ScheduledExecutorService scheduledExecutorService = Executors.newSingleThreadScheduledExecutor(); - private static final Map<String, ScheduledFuture<?>> scheduledTasksPerSubscriptionId = new ConcurrentHashMap<>(); - - /** - * Publish the event to the client who requested the subscription with key as subscription id and event is Cloud - * Event compliant. - * - * @param subscriptionId Cm Subscription Id - * @param eventType Type of event - * @param cmNotificationSubscriptionNcmpOutEvent Cm Notification Subscription Event for the - * client - * @param isScheduledEvent Determines if the event is to be scheduled - * or published now - */ - public void publishCmNotificationSubscriptionNcmpOutEvent(final String subscriptionId, final String eventType, - final CmNotificationSubscriptionNcmpOutEvent - cmNotificationSubscriptionNcmpOutEvent, - final boolean isScheduledEvent) { - - if (isScheduledEvent && !scheduledTasksPerSubscriptionId.containsKey(subscriptionId)) { - final ScheduledFuture<?> scheduledFuture = - scheduleAndPublishCmNotificationSubscriptionNcmpOutEvent(subscriptionId, eventType); - scheduledTasksPerSubscriptionId.putIfAbsent(subscriptionId, scheduledFuture); - log.debug("Scheduled the CmNotificationSubscriptionEvent for subscriptionId : {}", subscriptionId); - } else { - cancelScheduledTaskForSubscriptionId(subscriptionId); - publishCmNotificationSubscriptionNcmpOutEventNow(subscriptionId, eventType, - cmNotificationSubscriptionNcmpOutEvent); - log.info("Published CmNotificationSubscriptionEvent on demand for subscriptionId : {}", subscriptionId); - } - } - - private ScheduledFuture<?> scheduleAndPublishCmNotificationSubscriptionNcmpOutEvent(final String subscriptionId, - final String eventType) { - final CmNotificationSubscriptionNcmpOutEventPublishingTask - cmNotificationSubscriptionNcmpOutEventPublishingTask = - new CmNotificationSubscriptionNcmpOutEventPublishingTask(cmNotificationSubscriptionNcmpOutEventTopic, - subscriptionId, eventType, eventsPublisher, jsonObjectMapper, - cmNotificationSubscriptionMappersHandler, dmiCmNotificationSubscriptionCacheHandler); - return scheduledExecutorService.schedule(cmNotificationSubscriptionNcmpOutEventPublishingTask, - cmNotificationSubscriptionDmiOutEventTimeoutInMs, TimeUnit.MILLISECONDS); - } - - private void cancelScheduledTaskForSubscriptionId(final String subscriptionId) { - - final ScheduledFuture<?> scheduledFuture = scheduledTasksPerSubscriptionId.get(subscriptionId); - if (scheduledFuture != null) { - scheduledFuture.cancel(true); - scheduledTasksPerSubscriptionId.remove(subscriptionId); - } - - } - - - private void publishCmNotificationSubscriptionNcmpOutEventNow(final String subscriptionId, final String eventType, - final CmNotificationSubscriptionNcmpOutEvent - cmNotificationSubscriptionNcmpOutEvent) { - final CloudEvent cmNotificationSubscriptionNcmpOutEventAsCloudEvent = - buildAndGetCmNotificationNcmpOutEventAsCloudEvent(jsonObjectMapper, subscriptionId, eventType, - cmNotificationSubscriptionNcmpOutEvent); - eventsPublisher.publishCloudEvent(cmNotificationSubscriptionNcmpOutEventTopic, subscriptionId, - cmNotificationSubscriptionNcmpOutEventAsCloudEvent); - dmiCmNotificationSubscriptionCacheHandler - .removeAcceptedAndRejectedDmiCmNotificationSubscriptionEntries(subscriptionId); - } - - /** - * Get an NCMP out event as cloud event. - * - * @param jsonObjectMapper JSON object mapper - * @param subscriptionId subscription id - * @param eventType event type - * @param cmNotificationSubscriptionNcmpOutEvent cm notification subscription NCMP out event - * @return cm notification subscription NCMP out event as cloud event - */ - public static CloudEvent buildAndGetCmNotificationNcmpOutEventAsCloudEvent( - final JsonObjectMapper jsonObjectMapper, final String subscriptionId, final String eventType, - final CmNotificationSubscriptionNcmpOutEvent cmNotificationSubscriptionNcmpOutEvent) { - - return CloudEventBuilder.v1().withId(UUID.randomUUID().toString()).withType(eventType) - .withSource(URI.create("NCMP")).withDataSchema(URI.create("org.onap.ncmp.cm.subscription:1.0.0")) - .withExtension("correlationid", subscriptionId) - .withData(jsonObjectMapper.asJsonBytes(cmNotificationSubscriptionNcmpOutEvent)).build(); - } - -} diff --git a/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/events/cmsubscription/service/CmNotificationSubscriptionHandlerServiceImpl.java b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/events/cmsubscription/service/CmNotificationSubscriptionHandlerServiceImpl.java deleted file mode 100644 index 7872ba0a34..0000000000 --- a/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/events/cmsubscription/service/CmNotificationSubscriptionHandlerServiceImpl.java +++ /dev/null @@ -1,98 +0,0 @@ -/* - * ============LICENSE_START======================================================= - * Copyright (C) 2024 Nordix Foundation - * ================================================================================ - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - * SPDX-License-Identifier: Apache-2.0 - * ============LICENSE_END========================================================= - */ - -package org.onap.cps.ncmp.api.impl.events.cmsubscription.service; - -import java.util.ArrayList; -import java.util.List; -import java.util.Map; -import java.util.Set; -import java.util.stream.Collectors; -import lombok.RequiredArgsConstructor; -import org.onap.cps.ncmp.api.impl.events.cmsubscription.CmNotificationSubscriptionDelta; -import org.onap.cps.ncmp.api.impl.events.cmsubscription.CmNotificationSubscriptionEventsHandler; -import org.onap.cps.ncmp.api.impl.events.cmsubscription.CmNotificationSubscriptionMappersHandler; -import org.onap.cps.ncmp.api.impl.events.cmsubscription.DmiCmNotificationSubscriptionCacheHandler; -import org.onap.cps.ncmp.api.impl.events.cmsubscription.model.DmiCmNotificationSubscriptionDetails; -import org.onap.cps.ncmp.api.impl.events.cmsubscription.model.DmiCmNotificationSubscriptionPredicate; -import org.onap.cps.ncmp.events.cmnotificationsubscription_merge1_0_0.client_to_ncmp.CmNotificationSubscriptionNcmpInEvent; -import org.onap.cps.ncmp.events.cmnotificationsubscription_merge1_0_0.client_to_ncmp.Predicate; -import org.onap.cps.ncmp.events.cmnotificationsubscription_merge1_0_0.ncmp_to_dmi.CmNotificationSubscriptionDmiInEvent; -import org.onap.cps.ncmp.events.cmsubscription_merge1_0_0.ncmp_to_client.CmNotificationSubscriptionNcmpOutEvent; -import org.springframework.stereotype.Service; - -@Service -@RequiredArgsConstructor -public class CmNotificationSubscriptionHandlerServiceImpl implements CmNotificationSubscriptionHandlerService { - - private final CmNotificationSubscriptionPersistenceService cmNotificationSubscriptionPersistenceService; - private final CmNotificationSubscriptionDelta cmNotificationSubscriptionDelta; - private final CmNotificationSubscriptionMappersHandler cmNotificationSubscriptionMappersHandler; - private final CmNotificationSubscriptionEventsHandler cmNotificationSubscriptionEventsHandler; - private final DmiCmNotificationSubscriptionCacheHandler dmiCmNotificationSubscriptionCacheHandler; - - @Override - public void processSubscriptionCreateRequest( - final CmNotificationSubscriptionNcmpInEvent cmNotificationSubscriptionNcmpInEvent) { - final String subscriptionId = cmNotificationSubscriptionNcmpInEvent.getData().getSubscriptionId(); - final List<Predicate> predicates = cmNotificationSubscriptionNcmpInEvent.getData().getPredicates(); - - if (cmNotificationSubscriptionPersistenceService.isUniqueSubscriptionId(subscriptionId)) { - dmiCmNotificationSubscriptionCacheHandler.add(subscriptionId, predicates); - sendSubscriptionCreateRequestToDmi(subscriptionId); - scheduleCmNotificationSubscriptionNcmpOutEventResponse(subscriptionId); - } else { - rejectAndPublishCmNotificationSubscriptionCreateRequest(subscriptionId, - predicates); - } - } - - private void scheduleCmNotificationSubscriptionNcmpOutEventResponse(final String subscriptionId) { - cmNotificationSubscriptionEventsHandler.publishCmNotificationSubscriptionNcmpOutEvent(subscriptionId, - "subscriptionCreateResponse", null, true); - } - - private void rejectAndPublishCmNotificationSubscriptionCreateRequest(final String subscriptionId, - final List<Predicate> predicates) { - final Set<String> subscriptionTargetFilters = - predicates.stream().flatMap(predicate -> predicate.getTargetFilter().stream()) - .collect(Collectors.toSet()); - final CmNotificationSubscriptionNcmpOutEvent cmNotificationSubscriptionNcmpOutEvent = - cmNotificationSubscriptionMappersHandler.toCmNotificationSubscriptionNcmpOutEventForRejectedRequest( - subscriptionId, new ArrayList<>(subscriptionTargetFilters)); - cmNotificationSubscriptionEventsHandler.publishCmNotificationSubscriptionNcmpOutEvent(subscriptionId, - "subscriptionCreateResponse", cmNotificationSubscriptionNcmpOutEvent, false); - } - - private void sendSubscriptionCreateRequestToDmi(final String subscriptionId) { - final Map<String, DmiCmNotificationSubscriptionDetails> dmiCmNotificationSubscriptionDetailsMap = - dmiCmNotificationSubscriptionCacheHandler.get(subscriptionId); - dmiCmNotificationSubscriptionDetailsMap.forEach((dmiPluginName, dmiCmNotificationSubscriptionDetails) -> { - final List<DmiCmNotificationSubscriptionPredicate> dmiCmNotificationSubscriptionPredicates = - cmNotificationSubscriptionDelta.getDelta( - dmiCmNotificationSubscriptionDetails.getDmiCmNotificationSubscriptionPredicates()); - final CmNotificationSubscriptionDmiInEvent cmNotificationSubscriptionDmiInEvent = - cmNotificationSubscriptionMappersHandler.toCmNotificationSubscriptionDmiInEvent( - dmiCmNotificationSubscriptionPredicates); - cmNotificationSubscriptionEventsHandler.publishCmNotificationSubscriptionDmiInEvent(subscriptionId, - dmiPluginName, "subscriptionCreateRequest", cmNotificationSubscriptionDmiInEvent); - }); - } -}
\ No newline at end of file diff --git a/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/events/cmsubscription/service/CmNotificationSubscriptionPersistenceService.java b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/events/cmsubscription/service/CmNotificationSubscriptionPersistenceService.java deleted file mode 100644 index 3bb40c3b7e..0000000000 --- a/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/events/cmsubscription/service/CmNotificationSubscriptionPersistenceService.java +++ /dev/null @@ -1,84 +0,0 @@ -/* - * ============LICENSE_START======================================================= - * Copyright (C) 2024 Nordix Foundation - * ================================================================================ - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - * SPDX-License-Identifier: Apache-2.0 - * ============LICENSE_END========================================================= - */ - -package org.onap.cps.ncmp.api.impl.events.cmsubscription.service; - -import java.util.Collection; -import org.onap.cps.ncmp.api.impl.operations.DatastoreType; - -public interface CmNotificationSubscriptionPersistenceService { - - String NCMP_DATASPACE_NAME = "NCMP-Admin"; - String CM_SUBSCRIPTIONS_ANCHOR_NAME = "cm-data-subscriptions"; - - /** - * Check if we have an ongoing cm subscription based on the parameters. - * - * @param datastoreType the susbcription target datastore type - * @param cmHandleId the id of the cm handle for the susbcription - * @param xpath the target xpath - * @return true for ongoing cmsubscription , otherwise false - */ - boolean isOngoingCmNotificationSubscription(final DatastoreType datastoreType, final String cmHandleId, - final String xpath); - - /** - * Check if the subscription ID is unique against ongoing subscriptions. - * - * @param subscriptionId subscription ID - * @return true if subscriptionId is not used in active subscriptions, otherwise false - */ - boolean isUniqueSubscriptionId(final String subscriptionId); - - /** - * Get all ongoing cm notification subscription based on the parameters. - * - * @param datastoreType the susbcription target datastore type - * @param cmHandleId the id of the cm handle for the susbcription - * @param xpath the target xpath - * @return collection of subscription ids of ongoing cm notification subscription - */ - Collection<String> getOngoingCmNotificationSubscriptionIds(final DatastoreType datastoreType, - final String cmHandleId, final String xpath); - - /** - * Add cm notification subscription. - * - * @param datastoreType the susbcription target datastore type - * @param cmHandleId the id of the cm handle for the susbcription - * @param xpath the target xpath - * @param newSubscriptionId subscription id to be added - */ - void addCmNotificationSubscription(final DatastoreType datastoreType, final String cmHandleId, - final String xpath, final String newSubscriptionId); - - /** - * Remove cm notification Subscription. - * - * @param datastoreType the susbcription target datastore type - * @param cmHandleId the id of the cm handle for the susbcription - * @param xpath the target xpath - * @param subscriptionId subscription id to remove - */ - void removeCmNotificationSubscription(final DatastoreType datastoreType, final String cmHandleId, - final String xpath, final String subscriptionId); - -} - 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 deleted file mode 100644 index a9ec1241bc..0000000000 --- a/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/operations/DmiDataOperations.java +++ /dev/null @@ -1,291 +0,0 @@ -/* - * ============LICENSE_START======================================================= - * Copyright (C) 2021-2024 Nordix Foundation - * Modifications Copyright (C) 2022 Bell Canada - * ================================================================================ - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * 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 static org.onap.cps.ncmp.api.NcmpResponseStatus.DMI_SERVICE_NOT_RESPONDING; -import static org.onap.cps.ncmp.api.NcmpResponseStatus.UNABLE_TO_READ_RESOURCE_DATA; -import static org.onap.cps.ncmp.api.impl.operations.DatastoreType.PASSTHROUGH_RUNNING; -import static org.onap.cps.ncmp.api.impl.operations.OperationType.READ; - -import io.micrometer.core.annotation.Timed; -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.NcmpResponseStatus; -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.exception.HttpClientRequestException; -import org.onap.cps.ncmp.api.impl.inventory.CmHandleState; -import org.onap.cps.ncmp.api.impl.inventory.InventoryPersistence; -import org.onap.cps.ncmp.api.impl.utils.DmiServiceUrlBuilder; -import org.onap.cps.ncmp.api.impl.utils.data.operation.ResourceDataOperationRequestUtils; -import org.onap.cps.ncmp.api.impl.yangmodels.YangModelCmHandle; -import org.onap.cps.ncmp.api.models.CmResourceAddress; -import org.onap.cps.ncmp.api.models.DataOperationRequest; -import org.onap.cps.spi.exceptions.CpsException; -import org.onap.cps.utils.JsonObjectMapper; -import org.springframework.http.ResponseEntity; -import org.springframework.stereotype.Component; -import org.springframework.util.LinkedMultiValueMap; -import org.springframework.util.MultiValueMap; -import org.springframework.web.util.UriComponentsBuilder; - -/** - * Operations class for DMI data. - */ -@Component -@Slf4j -public class DmiDataOperations extends DmiOperations { - - public DmiDataOperations(final InventoryPersistence inventoryPersistence, - final JsonObjectMapper jsonObjectMapper, - final NcmpConfiguration.DmiProperties dmiProperties, - final DmiRestClient dmiRestClient, - final DmiServiceUrlBuilder dmiServiceUrlBuilder) { - super(inventoryPersistence, jsonObjectMapper, dmiProperties, dmiRestClient, dmiServiceUrlBuilder); - } - - /** - * This method fetches the resource data from operational data store for given cm handle - * identifier on given resource using dmi client. - * - * @param cmResourceAddress target datastore, cm handle and resource identifier - * @param optionsParamInQuery options query - * @param topicParamInQuery topic name for (triggering) async responses - * @param requestId requestId for async responses - * @param authorization contents of Authorization header, or null if not present - * @return {@code ResponseEntity} response entity - */ - @Timed(value = "cps.ncmp.dmi.get", - description = "Time taken to fetch the resource data from operational data store for given cm handle " - + "identifier on given resource using dmi client") - public ResponseEntity<Object> getResourceDataFromDmi(final CmResourceAddress cmResourceAddress, - final String optionsParamInQuery, - final String topicParamInQuery, - final String requestId, - final String authorization) { - final YangModelCmHandle yangModelCmHandle = getYangModelCmHandle(cmResourceAddress.cmHandleId()); - final CmHandleState cmHandleState = yangModelCmHandle.getCompositeState().getCmHandleState(); - validateIfCmHandleStateReady(yangModelCmHandle, cmHandleState); - final String jsonRequestBody = getDmiRequestBody(READ, requestId, null, null, yangModelCmHandle); - final String dmiResourceDataUrl = getDmiRequestUrl(cmResourceAddress.datastoreName(), - cmResourceAddress.cmHandleId(), cmResourceAddress.resourceIdentifier(), optionsParamInQuery, - topicParamInQuery, yangModelCmHandle.resolveDmiServiceName(RequiredDmiService.DATA)); - return dmiRestClient.postOperationWithJsonData(dmiResourceDataUrl, jsonRequestBody, READ, authorization); - } - - /** - * 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 - * @return {@code ResponseEntity} response entity - */ - public ResponseEntity<Object> getResourceDataFromDmi(final String dataStoreName, - final String cmHandleId, - final String requestId) { - 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 CmHandleState cmHandleState = yangModelCmHandle.getCompositeState().getCmHandleState(); - validateIfCmHandleStateReady(yangModelCmHandle, cmHandleState); - return dmiRestClient.postOperationWithJsonData(dmiResourceDataUrl, jsonRequestBody, READ, null); - } - - /** - * 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 dataOperationRequest data operation request to execute operations - * @param requestId requestId for as a response - * @param authorization contents of Authorization header, or null if not present - */ - public void requestResourceDataFromDmi(final String topicParamInQuery, - final DataOperationRequest dataOperationRequest, - final String requestId, - final String authorization) { - - final Set<String> cmHandlesIds - = getDistinctCmHandleIdsFromDataOperationRequest(dataOperationRequest); - - final Collection<YangModelCmHandle> yangModelCmHandles - = inventoryPersistence.getYangModelCmHandles(cmHandlesIds); - - final Map<String, List<DmiDataOperation>> operationsOutPerDmiServiceName - = ResourceDataOperationRequestUtils.processPerDefinitionInDataOperationsRequest(topicParamInQuery, - requestId, dataOperationRequest, yangModelCmHandles); - - buildDataOperationRequestUrlAndSendToDmiService(topicParamInQuery, requestId, operationsOutPerDmiServiceName, - authorization); - } - - /** - * This method creates the resource data from pass-through running data store for given cm handle - * identifier on given resource using dmi client. - * - * @param cmHandleId network resource identifier - * @param resourceId resource identifier - * @param operationType operation enum - * @param requestData the request data - * @param dataType data type - * @param authorization contents of Authorization header, or null if not present - * @return {@code ResponseEntity} response entity - */ - public ResponseEntity<Object> writeResourceDataPassThroughRunningFromDmi(final String cmHandleId, - final String resourceId, - final OperationType operationType, - final String requestData, - final String dataType, - final String authorization) { - final YangModelCmHandle yangModelCmHandle = getYangModelCmHandle(cmHandleId); - 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, operationType, authorization); - } - - private YangModelCmHandle getYangModelCmHandle(final String cmHandleId) { - return inventoryPersistence.getYangModelCmHandle(cmHandleId); - } - - private String getDmiRequestBody(final OperationType operationType, - final String requestId, - final String requestData, - final String dataType, - final YangModelCmHandle yangModelCmHandle) { - final DmiRequestBody dmiRequestBody = DmiRequestBody.builder() - .operationType(operationType) - .requestId(requestId) - .data(requestData) - .dataType(dataType) - .build(); - dmiRequestBody.asDmiProperties(yangModelCmHandle.getDmiProperties()); - return jsonObjectMapper.asJsonString(dmiRequestBody); - } - - private String getDmiRequestUrl(final String dataStoreName, - final String cmHandleId, - final String resourceId, - final String optionsParamInQuery, - final String topicParamInQuery, - final String dmiServiceName) { - return dmiServiceUrlBuilder.getDmiDatastoreUrl( - dmiServiceUrlBuilder.populateQueryParams(resourceId, optionsParamInQuery, - topicParamInQuery), dmiServiceUrlBuilder.populateUriVariables(dataStoreName, dmiServiceName, - cmHandleId)); - } - - private String getDmiServiceDataOperationRequestUrl(final String dmiServiceName, - final String topicParamInQuery, - final String requestId) { - final MultiValueMap<String, String> dataOperationRequestQueryParams = dmiServiceUrlBuilder - .getDataOperationRequestQueryParams(topicParamInQuery, requestId); - return dmiServiceUrlBuilder.getDataOperationRequestUrl(dataOperationRequestQueryParams, - dmiServiceUrlBuilder.populateDataOperationRequestUriVariables(dmiServiceName)); - } - - private void validateIfCmHandleStateReady(final YangModelCmHandle yangModelCmHandle, - final CmHandleState cmHandleState) { - if (cmHandleState != CmHandleState.READY) { - throw new CpsException("State mismatch exception.", "Cm-Handle not in READY state. " - + "cm handle state is " - + yangModelCmHandle.getCompositeState().getCmHandleState()); - } - } - - private static Set<String> getDistinctCmHandleIdsFromDataOperationRequest(final DataOperationRequest - dataOperationRequest) { - return dataOperationRequest.getDataOperationDefinitions().stream() - .flatMap(dataOperationDefinition -> - dataOperationDefinition.getCmHandleIds().stream()).collect(Collectors.toSet()); - } - - private void buildDataOperationRequestUrlAndSendToDmiService(final String topicParamInQuery, - final String requestId, - final Map<String, List<DmiDataOperation>> - groupsOutPerDmiServiceName, - final String authorization) { - - groupsOutPerDmiServiceName.forEach((dmiServiceName, dmiDataOperationRequestBodies) -> { - final String dmiDataOperationResourceUrl = - getDmiServiceDataOperationRequestUrl(dmiServiceName, topicParamInQuery, requestId); - sendDataOperationRequestToDmiService(dmiDataOperationResourceUrl, dmiDataOperationRequestBodies, - authorization); - }); - } - - private void sendDataOperationRequestToDmiService(final String dataOperationResourceUrl, - final List<DmiDataOperation> dmiDataOperationRequestBodies, - final String authorization) { - final DmiDataOperationRequest dmiDataOperationRequest = DmiDataOperationRequest.builder() - .operations(dmiDataOperationRequestBodies).build(); - final String dmiDataOperationRequestAsJsonString = - jsonObjectMapper.asJsonString(dmiDataOperationRequest); - try { - dmiRestClient.postOperationWithJsonData(dataOperationResourceUrl, dmiDataOperationRequestAsJsonString, READ, - authorization); - } catch (final Exception exception) { - handleTaskCompletionException(exception, dataOperationResourceUrl, dmiDataOperationRequestBodies); - } - } - - private void handleTaskCompletionException(final Throwable throwable, - final String dataOperationResourceUrl, - final List<DmiDataOperation> dmiDataOperationRequestBodies) { - if (throwable != null) { - final MultiValueMap<String, String> dataOperationResourceUrlParameters = - UriComponentsBuilder.fromUriString(dataOperationResourceUrl).build().getQueryParams(); - final String topicName = dataOperationResourceUrlParameters.get("topic").get(0); - final String requestId = dataOperationResourceUrlParameters.get("requestId").get(0); - - final MultiValueMap<DmiDataOperation, Map<NcmpResponseStatus, List<String>>> - cmHandleIdsPerResponseCodesPerOperation = new LinkedMultiValueMap<>(); - - dmiDataOperationRequestBodies.forEach(dmiDataOperationRequestBody -> { - final List<String> cmHandleIds = dmiDataOperationRequestBody.getCmHandles().stream() - .map(CmHandle::getId).toList(); - if (throwable.getCause() instanceof HttpClientRequestException) { - cmHandleIdsPerResponseCodesPerOperation.add(dmiDataOperationRequestBody, - Map.of(UNABLE_TO_READ_RESOURCE_DATA, cmHandleIds)); - } else { - cmHandleIdsPerResponseCodesPerOperation.add(dmiDataOperationRequestBody, - Map.of(DMI_SERVICE_NOT_RESPONDING, cmHandleIds)); - } - }); - ResourceDataOperationRequestUtils.publishErrorMessageToClientTopic(topicName, requestId, - cmHandleIdsPerResponseCodesPerOperation); - } - } -} diff --git a/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/operations/DmiOperations.java b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/operations/DmiOperations.java deleted file mode 100644 index c8d73eac63..0000000000 --- a/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/operations/DmiOperations.java +++ /dev/null @@ -1,49 +0,0 @@ -/* - * ============LICENSE_START======================================================= - * Copyright (C) 2021-2023 Nordix Foundation - * Modifications Copyright (C) 2022 Bell Canada - * ================================================================================ - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * 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 lombok.RequiredArgsConstructor; -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.inventory.InventoryPersistence; -import org.onap.cps.ncmp.api.impl.utils.DmiServiceUrlBuilder; -import org.onap.cps.utils.JsonObjectMapper; -import org.springframework.stereotype.Service; - -@RequiredArgsConstructor -@Service -public class DmiOperations { - - protected final InventoryPersistence inventoryPersistence; - protected final JsonObjectMapper jsonObjectMapper; - protected final NcmpConfiguration.DmiProperties dmiProperties; - protected final DmiRestClient dmiRestClient; - protected final DmiServiceUrlBuilder dmiServiceUrlBuilder; - - String getDmiResourceUrl(final String dmiServiceName, final String cmHandle, final String resourceName) { - return dmiServiceUrlBuilder.getResourceDataBasePathUriBuilder() - .pathSegment("{resourceName}") - .buildAndExpand(dmiServiceName, dmiProperties.getDmiBasePath(), cmHandle, resourceName).toUriString(); - } - - -} diff --git a/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/utils/AlternateIdChecker.java b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/utils/AlternateIdChecker.java deleted file mode 100644 index 60f39fcea0..0000000000 --- a/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/utils/AlternateIdChecker.java +++ /dev/null @@ -1,152 +0,0 @@ -/* - * ============LICENSE_START======================================================== - * Copyright (c) 2024 Nordix Foundation. - * ================================================================================ - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an 'AS IS' BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - * SPDX-License-Identifier: Apache-2.0 - * ============LICENSE_END========================================================= - */ - -package org.onap.cps.ncmp.api.impl.utils; - -import java.util.ArrayList; -import java.util.Collection; -import java.util.HashSet; -import java.util.Set; -import lombok.RequiredArgsConstructor; -import lombok.extern.slf4j.Slf4j; -import org.apache.commons.lang3.StringUtils; -import org.onap.cps.ncmp.api.impl.inventory.InventoryPersistence; -import org.onap.cps.ncmp.api.impl.yangmodels.YangModelCmHandle; -import org.onap.cps.ncmp.api.models.NcmpServiceCmHandle; -import org.onap.cps.spi.exceptions.DataNodeNotFoundException; -import org.springframework.stereotype.Service; - -@Service -@Slf4j -@RequiredArgsConstructor -public class AlternateIdChecker { - - public enum Operation { - CREATE, UPDATE - } - - private final InventoryPersistence inventoryPersistence; - - private static final String NO_CURRENT_ALTERNATE_ID = ""; - - /** - * Check if the alternate can be applied to the given cm handle (id). - * Conditions: - * - proposed alternate is blank (it wil be ignored) - * - proposed alternate is same as current (no change) - * - proposed alternate is not in use for a different cm handle (in the DB) - * - * @param cmHandleId cm handle id - * @param proposedAlternateId proposed alternate id - * @return true if the new alternate id not in use or equal to current alternate id, false otherwise - */ - public boolean canApplyAlternateId(final String cmHandleId, final String proposedAlternateId) { - String currentAlternateId = ""; - try { - final YangModelCmHandle yangModelCmHandle = inventoryPersistence.getYangModelCmHandle(cmHandleId); - currentAlternateId = yangModelCmHandle.getAlternateId(); - } catch (final DataNodeNotFoundException dataNodeNotFoundException) { - // work with blank current alternate id - } - return this.canApplyAlternateId(cmHandleId, currentAlternateId, proposedAlternateId); - } - - /** - * Check if the alternate can be applied to the given cm handle. - * Conditions: - * - proposed alternate is blank (it wil be ignored) - * - proposed alternate is same as current (no change) - * - proposed alternate is not in use for a different cm handle (in the DB) - * - * @param cmHandleId cm handle id - * @param currentAlternateId current alternate id - * @param proposedAlternateId new alternate id - * @return true if the new alternate id not in use or equal to current alternate id, false otherwise - */ - public boolean canApplyAlternateId(final String cmHandleId, - final String currentAlternateId, - final String proposedAlternateId) { - if (StringUtils.isBlank(currentAlternateId)) { - if (alternateIdAlreadyInDb(proposedAlternateId)) { - log.warn("Alternate id update ignored, cannot update cm handle {}, alternate id is already " - + "assigned to a different cm handle", cmHandleId); - return false; - } - return true; - } - if (currentAlternateId.equals(proposedAlternateId)) { - return true; - } - log.warn("Alternate id update ignored, cannot update cm handle {}, already has an alternate id of {}", - cmHandleId, currentAlternateId); - return false; - } - - /** - * Check all alternate ids of a batch of cm handles. - * Includes cross-checks in the batch itself for duplicates. Only the first entry encountered wil be accepted. - * - * @param newNcmpServiceCmHandles the proposed new cm handles - * @param operation type of operation being executed - * @return collection of cm handles ids which are acceptable - */ - public Collection<String> getIdsOfCmHandlesWithRejectedAlternateId( - final Collection<NcmpServiceCmHandle> newNcmpServiceCmHandles, - final Operation operation) { - final Set<String> acceptedAlternateIds = new HashSet<>(newNcmpServiceCmHandles.size()); - final Collection<String> rejectedCmHandleIds = new ArrayList<>(); - for (final NcmpServiceCmHandle ncmpServiceCmHandle : newNcmpServiceCmHandles) { - final String cmHandleId = ncmpServiceCmHandle.getCmHandleId(); - final String proposedAlternateId = ncmpServiceCmHandle.getAlternateId(); - if (isProposedAlternateIdAcceptable(proposedAlternateId, operation, acceptedAlternateIds, cmHandleId)) { - acceptedAlternateIds.add(proposedAlternateId); - } else { - rejectedCmHandleIds.add(cmHandleId); - } - } - return rejectedCmHandleIds; - } - - private boolean isProposedAlternateIdAcceptable(final String proposedAlternateId, final Operation operation, - final Set<String> acceptedAlternateIds, final String cmHandleId) { - if (StringUtils.isEmpty(proposedAlternateId)) { - return true; - } - if (acceptedAlternateIds.contains(proposedAlternateId)) { - log.warn("Alternate id update ignored, cannot update cm handle {}, alternate id is already " - + "assigned to a different cm handle (in this batch)", cmHandleId); - return false; - } - if (Operation.CREATE.equals(operation)) { - return canApplyAlternateId(cmHandleId, NO_CURRENT_ALTERNATE_ID, proposedAlternateId); - } - return canApplyAlternateId(cmHandleId, proposedAlternateId); - } - - private boolean alternateIdAlreadyInDb(final String alternateId) { - try { - inventoryPersistence.getCmHandleDataNodeByAlternateId(alternateId); - } catch (final DataNodeNotFoundException dataNodeNotFoundException) { - return false; - } - return true; - } - -} diff --git a/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/utils/DataNodeHelper.java b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/utils/DataNodeHelper.java deleted file mode 100644 index c032d1e8a4..0000000000 --- a/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/utils/DataNodeHelper.java +++ /dev/null @@ -1,100 +0,0 @@ -/* - * ============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.io.Serializable; -import java.util.Collection; -import java.util.HashMap; -import java.util.List; -import java.util.Map; -import java.util.stream.Collectors; -import java.util.stream.Stream; -import lombok.AccessLevel; -import lombok.NoArgsConstructor; -import org.onap.cps.spi.model.DataNode; - -@NoArgsConstructor(access = AccessLevel.PRIVATE) -public class DataNodeHelper { - - /** - * The nested DataNode object is being flattened. - * - * @param dataNode object. - * @return DataNode as stream. - */ - public static Stream<DataNode> flatten(final DataNode dataNode) { - return Stream.concat(Stream.of(dataNode), - dataNode.getChildDataNodes().stream().flatMap(DataNodeHelper::flatten)); - } - - /** - * The leaves for each DataNode is listed as map. - * - * @param dataNodes as collection - * @return list of map for the all leaves - */ - public static List<Map<String, Serializable>> getDataNodeLeaves(final Collection<DataNode> dataNodes) { - return dataNodes.stream() - .flatMap(DataNodeHelper::flatten) - .map(DataNode::getLeaves) - .collect(Collectors.toList()); - } - - /** - * Extracts the mapping of cm handle id to status with details from nodes leaves. - * - * @param dataNodeLeaves as a list of map - * @return cm handle id to status and details mapping - */ - public static Map<String, Map<String, String>> cmHandleIdToStatusAndDetailsAsMap( - final List<Map<String, Serializable>> dataNodeLeaves) { - return dataNodeLeaves.stream() - .filter(entryset -> entryset.values().contains("PENDING") - || entryset.values().contains("ACCEPTED") - || entryset.values().contains("REJECTED")) - .collect( - HashMap<String, Map<String, String>>::new, - (result, entry) -> { - final String cmHandleId = (String) entry.get("cmHandleId"); - final String status = (String) entry.get("status"); - final String details = (String) entry.get("details"); - - if (cmHandleId != null && status != null) { - result.put(cmHandleId, new HashMap<>()); - result.get(cmHandleId).put("status", status); - result.get(cmHandleId).put("details", details == null ? "" : details); - } - }, - HashMap::putAll - ); - } - - /** - * Extracts the mapping of cm handle id to status with details from data node collection. - * - * @param dataNodes as a collection - * @return cm handle id to status and details mapping - */ - public static Map<String, Map<String, String>> cmHandleIdToStatusAndDetailsAsMapFromDataNode( - final Collection<DataNode> dataNodes) { - return cmHandleIdToStatusAndDetailsAsMap(getDataNodeLeaves(dataNodes)); - } -} 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 deleted file mode 100644 index 04acaa5e9b..0000000000 --- a/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/utils/DmiServiceUrlBuilder.java +++ /dev/null @@ -1,186 +0,0 @@ -/* - * ============LICENSE_START======================================================= - * Copyright (C) 2022-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.HashMap; -import java.util.Map; -import lombok.RequiredArgsConstructor; -import org.apache.logging.log4j.util.Strings; -import org.apache.logging.log4j.util.TriConsumer; -import org.onap.cps.ncmp.api.impl.config.NcmpConfiguration; -import org.onap.cps.spi.utils.CpsValidator; -import org.springframework.stereotype.Component; -import org.springframework.util.LinkedMultiValueMap; -import org.springframework.util.MultiValueMap; -import org.springframework.web.util.UriComponentsBuilder; - -@Component -@RequiredArgsConstructor -public class DmiServiceUrlBuilder { - - private final NcmpConfiguration.DmiProperties dmiProperties; - private final CpsValidator cpsValidator; - - /** - * This method creates the dmi service 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 - */ - public String getDmiDatastoreUrl(final MultiValueMap<String, String> queryParams, - final Map<String, Object> uriVariables) { - return getUriComponentsBuilder(getResourceDataBasePathUriBuilder(), queryParams, uriVariables) - .buildAndExpand().toUriString(); - } - - /** - * This method builds data operation request url. - * - * @param dataoperationRequestQueryParams query param map as key, value pair - * @param dataoperationRequestUriVariables uri param map as key (placeholder), value pair - * @return {@code String} data operation request url as string - */ - public String getDataOperationRequestUrl(final MultiValueMap<String, String> dataoperationRequestQueryParams, - final Map<String, Object> dataoperationRequestUriVariables) { - return getDataOperationResourceDataBasePathUriBuilder() - .queryParams(dataoperationRequestQueryParams) - .uriVariables(dataoperationRequestUriVariables) - .buildAndExpand().toUriString(); - } - - /** - * This method creates the dmi service url builder object with path variables. - * - * @return {@code UriComponentsBuilder} dmi service url builder object - */ - public UriComponentsBuilder getResourceDataBasePathUriBuilder() { - return UriComponentsBuilder.newInstance() - .path("{dmiServiceName}") - .pathSegment("{dmiBasePath}") - .pathSegment("v1") - .pathSegment("ch") - .pathSegment("{cmHandleId}"); - } - - /** - * This method creates the dmi service url builder object with path variables for data operation request. - * - * @return {@code UriComponentsBuilder} dmi service url builder object - */ - public UriComponentsBuilder getDataOperationResourceDataBasePathUriBuilder() { - return UriComponentsBuilder.newInstance() - .path("{dmiServiceName}") - .pathSegment("{dmiBasePath}") - .pathSegment("v1") - .pathSegment("data"); - } - - /** - * This method populates uri variables. - * - * @param dataStoreName data store name - * @param dmiServiceName dmi service name - * @param cmHandleId cm handle id for dmi registration - * @return {@code String} dmi service url as string - */ - public Map<String, Object> populateUriVariables(final String dataStoreName, - final String dmiServiceName, - final String cmHandleId) { - cpsValidator.validateNameCharacters(cmHandleId); - final Map<String, Object> uriVariables = new HashMap<>(); - final String dmiBasePath = dmiProperties.getDmiBasePath(); - uriVariables.put("dmiServiceName", dmiServiceName); - uriVariables.put("dmiBasePath", dmiBasePath); - uriVariables.put("cmHandleId", cmHandleId); - uriVariables.put("dataStore", dataStoreName); - return uriVariables; - } - - /** - * This method populates uri variables for data operation request. - * - * @param dmiServiceName dmi service name - * @return {@code Map<String, Object>} uri variables as map - */ - public Map<String, Object> populateDataOperationRequestUriVariables(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 - * @param optionsParamInQuery options into url param - * @param topicParamInQuery topic into url param - * @return all valid query params as map - */ - public MultiValueMap<String, String> populateQueryParams(final String resourceId, - final String optionsParamInQuery, - final String topicParamInQuery) { - final MultiValueMap<String, String> queryParams = new LinkedMultiValueMap<>(); - getQueryParamConsumer().accept("resourceIdentifier", - resourceId, queryParams); - getQueryParamConsumer().accept("options", optionsParamInQuery, queryParams); - if (Strings.isNotEmpty(topicParamInQuery)) { - getQueryParamConsumer().accept("topic", topicParamInQuery, queryParams); - } - return queryParams; - } - - /** - * This method is used to populate map from query params for data operation 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> getDataOperationRequestQueryParams(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)) { - paramMap.add(paramName, paramValue); - } - }; - } - - private UriComponentsBuilder getUriComponentsBuilder(final UriComponentsBuilder uriComponentsBuilder, - final MultiValueMap<String, String> queryParams, - final Map<String, Object> uriVariables) { - return uriComponentsBuilder - .pathSegment("data") - .pathSegment("ds") - .pathSegment("{dataStore}") - .queryParams(queryParams) - .uriVariables(uriVariables); - } -} diff --git a/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/yangmodels/YangModelCmDataSubscriptionEvent.java b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/yangmodels/YangModelCmDataSubscriptionEvent.java deleted file mode 100644 index e527d99f2f..0000000000 --- a/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/yangmodels/YangModelCmDataSubscriptionEvent.java +++ /dev/null @@ -1,76 +0,0 @@ -/* - * ============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.yangmodels; - -import com.fasterxml.jackson.annotation.JsonInclude; -import com.fasterxml.jackson.annotation.JsonInclude.Include; -import com.fasterxml.jackson.annotation.JsonProperty; -import java.util.List; -import lombok.AllArgsConstructor; -import lombok.Data; -import lombok.EqualsAndHashCode; -import lombok.Getter; -import lombok.NoArgsConstructor; -import lombok.Setter; - -/** - * Subscription event model to persist data into DB. - * Yang model subscription event - */ -@Getter -@Setter -@NoArgsConstructor -@JsonInclude(Include.NON_NULL) -@EqualsAndHashCode(onlyExplicitlyIncluded = true) -public class YangModelCmDataSubscriptionEvent { - - @EqualsAndHashCode.Include - @JsonProperty("name") - private String name; - - private List<CmHandle> cmHandles; - - @AllArgsConstructor - @Data - @JsonInclude(JsonInclude.Include.NON_NULL) - public static class CmHandle { - - @JsonProperty() - private final String id; - - private final List<Filter> filters; - } - - @AllArgsConstructor - @Data - @JsonInclude(JsonInclude.Include.NON_NULL) - public static class Filter { - - @JsonProperty() - private final String id; - - @JsonProperty() - private final List<String> subscribers; - } -} - - diff --git a/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/inventory/NetworkCmProxyInventoryFacade.java b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/inventory/NetworkCmProxyInventoryFacade.java new file mode 100644 index 0000000000..1fa801c3c5 --- /dev/null +++ b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/inventory/NetworkCmProxyInventoryFacade.java @@ -0,0 +1,221 @@ +/* + * ============LICENSE_START======================================================= + * Copyright (C) 2021 highstreet technologies GmbH + * Modifications Copyright (C) 2021-2024 Nordix Foundation + * Modifications Copyright (C) 2021 Pantheon.tech + * Modifications Copyright (C) 2021-2022 Bell Canada + * Modifications Copyright (C) 2023 TechMahindra Ltd. + * ================================================================================ + * 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.inventory; + +import static org.onap.cps.ncmp.impl.inventory.CmHandleQueryParametersValidator.validateCmHandleQueryParameters; + +import java.util.Collection; +import java.util.Map; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.onap.cps.ncmp.api.inventory.models.CmHandleQueryApiParameters; +import org.onap.cps.ncmp.api.inventory.models.CmHandleQueryServiceParameters; +import org.onap.cps.ncmp.api.inventory.models.CompositeState; +import org.onap.cps.ncmp.api.inventory.models.DmiPluginRegistration; +import org.onap.cps.ncmp.api.inventory.models.DmiPluginRegistrationResponse; +import org.onap.cps.ncmp.api.inventory.models.NcmpServiceCmHandle; +import org.onap.cps.ncmp.impl.inventory.CmHandleQueryService; +import org.onap.cps.ncmp.impl.inventory.CmHandleRegistrationService; +import org.onap.cps.ncmp.impl.inventory.InventoryPersistence; +import org.onap.cps.ncmp.impl.inventory.ParameterizedCmHandleQueryService; +import org.onap.cps.ncmp.impl.inventory.models.CmHandleQueryConditions; +import org.onap.cps.ncmp.impl.inventory.models.InventoryQueryConditions; +import org.onap.cps.ncmp.impl.inventory.models.YangModelCmHandle; +import org.onap.cps.ncmp.impl.inventory.trustlevel.TrustLevelManager; +import org.onap.cps.ncmp.impl.utils.AlternateIdMatcher; +import org.onap.cps.ncmp.impl.utils.YangDataConverter; +import org.onap.cps.spi.model.ModuleDefinition; +import org.onap.cps.spi.model.ModuleReference; +import org.onap.cps.utils.JsonObjectMapper; +import org.springframework.stereotype.Service; + +@Slf4j +@Service +@RequiredArgsConstructor +public class NetworkCmProxyInventoryFacade { + + private final CmHandleRegistrationService cmHandleRegistrationService; + private final CmHandleQueryService cmHandleQueryService; + private final ParameterizedCmHandleQueryService parameterizedCmHandleQueryService; + private final InventoryPersistence inventoryPersistence; + private final JsonObjectMapper jsonObjectMapper; + private final TrustLevelManager trustLevelManager; + private final AlternateIdMatcher alternateIdMatcher; + + /** + * Registration of Created, Removed, Updated or Upgraded CM Handles. + * + * @param dmiPluginRegistration Dmi Plugin Registration details + * @return dmiPluginRegistrationResponse + */ + public DmiPluginRegistrationResponse updateDmiRegistrationAndSyncModule( + final DmiPluginRegistration dmiPluginRegistration) { + return cmHandleRegistrationService.updateDmiRegistrationAndSyncModule(dmiPluginRegistration); + } + + /** + * Get all cm handle IDs by DMI plugin identifier. + * + * @param dmiPluginIdentifier DMI plugin identifier + * @return collection of cm handle IDs + */ + public Collection<String> getAllCmHandleIdsByDmiPluginIdentifier(final String dmiPluginIdentifier) { + return cmHandleQueryService.getCmHandleIdsByDmiPluginIdentifier(dmiPluginIdentifier); + } + + /** + * Get all cm handle IDs by various properties. + * + * @param cmHandleQueryServiceParameters cm handle query parameters + * @return collection of cm handle IDs + */ + public Collection<String> executeParameterizedCmHandleIdSearch( + final CmHandleQueryServiceParameters cmHandleQueryServiceParameters) { + validateCmHandleQueryParameters(cmHandleQueryServiceParameters, InventoryQueryConditions.ALL_CONDITION_NAMES); + return parameterizedCmHandleQueryService.queryCmHandleIdsForInventory(cmHandleQueryServiceParameters); + } + + + /** + * Retrieve module references for the given cm handle reference. + * + * @param cmHandleReference cm handle or alternate id identifier + * @return a collection of modules names and revisions + */ + public Collection<ModuleReference> getYangResourcesModuleReferences(final String cmHandleReference) { + final String cmHandleId = alternateIdMatcher.getCmHandleId(cmHandleReference); + return inventoryPersistence.getYangResourcesModuleReferences(cmHandleId); + } + + /** + * Retrieve module definitions for the given cm handle. + * + * @param cmHandleReference cm handle or alternate id identifier + * @return a collection of module definition (moduleName, revision and yang resource content) + */ + public Collection<ModuleDefinition> getModuleDefinitionsByCmHandleReference(final String cmHandleReference) { + final String cmHandleId = alternateIdMatcher.getCmHandleId(cmHandleReference); + return inventoryPersistence.getModuleDefinitionsByCmHandleId(cmHandleId); + } + + /** + * Get module definitions for the given parameters. + * + * @param cmHandleReference cm handle or alternate id identifier + * @param moduleName module name + * @param moduleRevision the revision of the module + * @return list of module definitions (module name, revision, yang resource content) + */ + public Collection<ModuleDefinition> getModuleDefinitionsByCmHandleAndModule(final String cmHandleReference, + final String moduleName, + final String moduleRevision) { + final String cmHandleId = alternateIdMatcher.getCmHandleId(cmHandleReference); + return inventoryPersistence.getModuleDefinitionsByCmHandleAndModule(cmHandleId, moduleName, moduleRevision); + } + + /** + * Retrieve cm handles with details for the given query parameters. + * + * @param cmHandleQueryApiParameters cm handle query parameters + * @return cm handles with details + */ + public Collection<NcmpServiceCmHandle> executeCmHandleSearch( + final CmHandleQueryApiParameters cmHandleQueryApiParameters) { + final CmHandleQueryServiceParameters cmHandleQueryServiceParameters = jsonObjectMapper.convertToValueType( + cmHandleQueryApiParameters, CmHandleQueryServiceParameters.class); + validateCmHandleQueryParameters(cmHandleQueryServiceParameters, CmHandleQueryConditions.ALL_CONDITION_NAMES); + final Collection<NcmpServiceCmHandle> ncmpServiceCmHandles = + parameterizedCmHandleQueryService.queryCmHandles(cmHandleQueryServiceParameters); + ncmpServiceCmHandles.forEach(this::applyCurrentTrustLevel); + return ncmpServiceCmHandles; + } + + /** + * Retrieve cm handle ids for the given query parameters. + * + * @param cmHandleQueryApiParameters cm handle query parameters + * @return cm handle ids + */ + public Collection<String> executeCmHandleIdSearch(final CmHandleQueryApiParameters cmHandleQueryApiParameters) { + final CmHandleQueryServiceParameters cmHandleQueryServiceParameters = jsonObjectMapper.convertToValueType( + cmHandleQueryApiParameters, CmHandleQueryServiceParameters.class); + validateCmHandleQueryParameters(cmHandleQueryServiceParameters, CmHandleQueryConditions.ALL_CONDITION_NAMES); + return parameterizedCmHandleQueryService.queryCmHandleIds(cmHandleQueryServiceParameters); + } + + /** + * Set the data sync enabled flag, along with the data sync state + * based on the data sync enabled boolean for the cm handle id provided. + * + * @param cmHandleId cm handle id + * @param dataSyncEnabledTargetValue data sync enabled flag + */ + public void setDataSyncEnabled(final String cmHandleId, final Boolean dataSyncEnabledTargetValue) { + cmHandleRegistrationService.setDataSyncEnabled(cmHandleId, dataSyncEnabledTargetValue); + } + + /** + * Retrieve cm handle details for a given cm handle reference. + * + * @param cmHandleReference cm handle or alternate identifier + * @return cm handle details + */ + public NcmpServiceCmHandle getNcmpServiceCmHandle(final String cmHandleReference) { + final String cmHandleId = alternateIdMatcher.getCmHandleId(cmHandleReference); + final NcmpServiceCmHandle ncmpServiceCmHandle = YangDataConverter.toNcmpServiceCmHandle( + inventoryPersistence.getYangModelCmHandle(cmHandleId)); + applyCurrentTrustLevel(ncmpServiceCmHandle); + return ncmpServiceCmHandle; + } + + /** + * Get cm handle public properties for a given cm handle or alternate id. + * + * @param cmHandleReference cm handle or alternate identifier + * @return cm handle public properties + */ + public Map<String, String> getCmHandlePublicProperties(final String cmHandleReference) { + final String cmHandleId = alternateIdMatcher.getCmHandleId(cmHandleReference); + final YangModelCmHandle yangModelCmHandle = inventoryPersistence.getYangModelCmHandle(cmHandleId); + return YangDataConverter.toPropertiesMap(yangModelCmHandle.getPublicProperties()); + } + + /** + * Get cm handle composite state for a given cm handle id. + * + * @param cmHandleReference cm handle or alternate identifier + * @return cm handle state + */ + public CompositeState getCmHandleCompositeState(final String cmHandleReference) { + final String cmHandleId = alternateIdMatcher.getCmHandleId(cmHandleReference); + return inventoryPersistence.getYangModelCmHandle(cmHandleId).getCompositeState(); + } + + private void applyCurrentTrustLevel(final NcmpServiceCmHandle ncmpServiceCmHandle) { + ncmpServiceCmHandle.setCurrentTrustLevel(trustLevelManager + .getEffectiveTrustLevel(ncmpServiceCmHandle.getCmHandleId())); + } + +} diff --git a/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/models/CmHandleQueryApiParameters.java b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/inventory/models/CmHandleQueryApiParameters.java index dd8dcd60ac..596fb94a36 100644 --- a/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/models/CmHandleQueryApiParameters.java +++ b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/inventory/models/CmHandleQueryApiParameters.java @@ -18,7 +18,7 @@ * ============LICENSE_END========================================================= */ -package org.onap.cps.ncmp.api.models; +package org.onap.cps.ncmp.api.inventory.models; import com.fasterxml.jackson.annotation.JsonIgnoreProperties; import com.fasterxml.jackson.annotation.JsonInclude; diff --git a/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/models/CmHandleQueryServiceParameters.java b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/inventory/models/CmHandleQueryServiceParameters.java index 5eeafaca33..13915918e5 100644 --- a/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/models/CmHandleQueryServiceParameters.java +++ b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/inventory/models/CmHandleQueryServiceParameters.java @@ -18,7 +18,7 @@ * ============LICENSE_END========================================================= */ -package org.onap.cps.ncmp.api.models; +package org.onap.cps.ncmp.api.inventory.models; import com.fasterxml.jackson.annotation.JsonInclude; import com.fasterxml.jackson.annotation.JsonInclude.Include; diff --git a/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/models/CmHandleRegistrationResponse.java b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/inventory/models/CmHandleRegistrationResponse.java index 52b8d6926a..7523f77af2 100644 --- a/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/models/CmHandleRegistrationResponse.java +++ b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/inventory/models/CmHandleRegistrationResponse.java @@ -19,7 +19,7 @@ * ============LICENSE_END========================================================= */ -package org.onap.cps.ncmp.api.models; +package org.onap.cps.ncmp.api.inventory.models; import static org.onap.cps.ncmp.api.NcmpResponseStatus.UNKNOWN_ERROR; @@ -31,7 +31,7 @@ import lombok.Builder; import lombok.Data; import lombok.extern.slf4j.Slf4j; import org.onap.cps.ncmp.api.NcmpResponseStatus; -import org.onap.cps.ncmp.api.impl.utils.YangDataConverter; +import org.onap.cps.ncmp.impl.utils.YangDataConverter; @Data @Builder diff --git a/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/inventory/CompositeState.java b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/inventory/models/CompositeState.java index 5b88f560eb..ca4fde2b62 100644 --- a/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/inventory/CompositeState.java +++ b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/inventory/models/CompositeState.java @@ -18,7 +18,7 @@ * ============LICENSE_END========================================================= */ -package org.onap.cps.ncmp.api.impl.inventory; +package org.onap.cps.ncmp.api.inventory.models; import com.fasterxml.jackson.annotation.JsonInclude; import com.fasterxml.jackson.annotation.JsonProperty; @@ -29,6 +29,9 @@ import lombok.Data; import lombok.Getter; import lombok.NoArgsConstructor; import lombok.Setter; +import org.onap.cps.ncmp.impl.inventory.DataStoreSyncState; +import org.onap.cps.ncmp.impl.inventory.models.CmHandleState; +import org.onap.cps.ncmp.impl.inventory.models.LockReasonCategory; /** * State Model to store state corresponding to the Yang resource dmi-registry model. diff --git a/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/inventory/CompositeStateBuilder.java b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/inventory/models/CompositeStateBuilder.java index 2fbe2b2f78..59d7aa2b44 100644 --- a/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/inventory/CompositeStateBuilder.java +++ b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/inventory/models/CompositeStateBuilder.java @@ -19,11 +19,14 @@ * ============LICENSE_END========================================================= */ -package org.onap.cps.ncmp.api.impl.inventory; +package org.onap.cps.ncmp.api.inventory.models; -import org.onap.cps.ncmp.api.impl.inventory.CompositeState.DataStores; -import org.onap.cps.ncmp.api.impl.inventory.CompositeState.LockReason; -import org.onap.cps.ncmp.api.impl.inventory.CompositeState.Operational; +import org.onap.cps.ncmp.api.inventory.models.CompositeState.DataStores; +import org.onap.cps.ncmp.api.inventory.models.CompositeState.LockReason; +import org.onap.cps.ncmp.api.inventory.models.CompositeState.Operational; +import org.onap.cps.ncmp.impl.inventory.DataStoreSyncState; +import org.onap.cps.ncmp.impl.inventory.models.CmHandleState; +import org.onap.cps.ncmp.impl.inventory.models.LockReasonCategory; import org.onap.cps.spi.model.DataNode; public class CompositeStateBuilder { diff --git a/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/models/ConditionApiProperties.java b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/inventory/models/ConditionApiProperties.java index 5cb2ed376c..61d9f4a250 100644 --- a/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/models/ConditionApiProperties.java +++ b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/inventory/models/ConditionApiProperties.java @@ -18,7 +18,7 @@ * ============LICENSE_END========================================================= */ -package org.onap.cps.ncmp.api.models; +package org.onap.cps.ncmp.api.inventory.models; import com.fasterxml.jackson.annotation.JsonInclude; import com.fasterxml.jackson.annotation.JsonProperty; diff --git a/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/models/DmiPluginRegistration.java b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/inventory/models/DmiPluginRegistration.java index 7d6a8e1407..44e2ebe320 100644 --- a/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/models/DmiPluginRegistration.java +++ b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/inventory/models/DmiPluginRegistration.java @@ -18,7 +18,7 @@ * ============LICENSE_END========================================================= */ -package org.onap.cps.ncmp.api.models; +package org.onap.cps.ncmp.api.inventory.models; import com.fasterxml.jackson.annotation.JsonInclude; import com.fasterxml.jackson.annotation.JsonInclude.Include; @@ -27,8 +27,8 @@ import java.util.Collections; import java.util.List; import lombok.Getter; import lombok.Setter; -import org.onap.cps.ncmp.api.impl.exception.DmiRequestException; -import org.onap.cps.ncmp.api.impl.exception.NcmpException; +import org.onap.cps.ncmp.api.exceptions.DmiRequestException; +import org.onap.cps.ncmp.api.exceptions.NcmpException; /** * Dmi Registry request object. diff --git a/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/models/DmiPluginRegistrationResponse.java b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/inventory/models/DmiPluginRegistrationResponse.java index ee034176e3..736ca620d0 100644 --- a/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/models/DmiPluginRegistrationResponse.java +++ b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/inventory/models/DmiPluginRegistrationResponse.java @@ -19,7 +19,7 @@ * ============LICENSE_END========================================================= */ -package org.onap.cps.ncmp.api.models; +package org.onap.cps.ncmp.api.inventory.models; import java.util.Collections; import java.util.List; @@ -33,4 +33,4 @@ public class DmiPluginRegistrationResponse { private List<CmHandleRegistrationResponse> updatedCmHandles = Collections.emptyList(); private List<CmHandleRegistrationResponse> removedCmHandles = Collections.emptyList(); private List<CmHandleRegistrationResponse> upgradedCmHandles = Collections.emptyList(); -}
\ No newline at end of file +} diff --git a/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/models/NcmpServiceCmHandle.java b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/inventory/models/NcmpServiceCmHandle.java index 676eebc4d6..3ebceed9d7 100644 --- a/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/models/NcmpServiceCmHandle.java +++ b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/inventory/models/NcmpServiceCmHandle.java @@ -18,7 +18,7 @@ * ============LICENSE_END========================================================= */ -package org.onap.cps.ncmp.api.models; +package org.onap.cps.ncmp.api.inventory.models; import com.fasterxml.jackson.annotation.JsonSetter; import com.fasterxml.jackson.annotation.Nulls; @@ -27,8 +27,6 @@ import java.util.Map; import lombok.Getter; import lombok.NoArgsConstructor; import lombok.Setter; -import org.onap.cps.ncmp.api.impl.inventory.CompositeState; -import org.onap.cps.ncmp.api.impl.trustlevel.TrustLevel; import org.springframework.validation.annotation.Validated; /** @@ -59,6 +57,9 @@ public class NcmpServiceCmHandle { private TrustLevel registrationTrustLevel; @JsonSetter(nulls = Nulls.AS_EMPTY) + private TrustLevel currentTrustLevel; + + @JsonSetter(nulls = Nulls.AS_EMPTY) private String alternateId; @JsonSetter(nulls = Nulls.AS_EMPTY) diff --git a/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/trustlevel/TrustLevel.java b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/inventory/models/TrustLevel.java index f130604a64..83e6ecf912 100644 --- a/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/trustlevel/TrustLevel.java +++ b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/inventory/models/TrustLevel.java @@ -18,7 +18,7 @@ * ============LICENSE_END========================================================= */ -package org.onap.cps.ncmp.api.impl.trustlevel; +package org.onap.cps.ncmp.api.inventory.models; import lombok.Getter; diff --git a/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/models/UpgradedCmHandles.java b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/inventory/models/UpgradedCmHandles.java index 61cd99ac8f..9f48ae968c 100644 --- a/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/models/UpgradedCmHandles.java +++ b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/inventory/models/UpgradedCmHandles.java @@ -18,7 +18,7 @@ * ============LICENSE_END========================================================= */ -package org.onap.cps.ncmp.api.models; +package org.onap.cps.ncmp.api.inventory.models; import com.fasterxml.jackson.annotation.JsonInclude; import java.util.Collections; diff --git a/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/models/YangResource.java b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/inventory/models/YangResource.java index 7975777aa5..1c0ca5d8cd 100644 --- a/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/models/YangResource.java +++ b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/inventory/models/YangResource.java @@ -18,7 +18,7 @@ * ============LICENSE_END========================================================= */ -package org.onap.cps.ncmp.api.models; +package org.onap.cps.ncmp.api.inventory.models; import lombok.Data; diff --git a/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/utils/context/CpsApplicationContext.java b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/config/CpsApplicationContext.java index b14cf0d0db..fbc31adcf3 100644 --- a/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/utils/context/CpsApplicationContext.java +++ b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/config/CpsApplicationContext.java @@ -18,7 +18,7 @@ * ============LICENSE_END========================================================= */ -package org.onap.cps.ncmp.api.impl.utils.context; +package org.onap.cps.ncmp.config; import org.springframework.context.ApplicationContext; import org.springframework.context.ApplicationContextAware; @@ -48,4 +48,4 @@ public class CpsApplicationContext implements ApplicationContextAware { private static synchronized void setCpsApplicationContext(final ApplicationContext cpsApplicationContext) { CpsApplicationContext.applicationContext = cpsApplicationContext; } -}
\ No newline at end of file +} diff --git a/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/config/DmiHttpClientConfig.java b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/config/DmiHttpClientConfig.java new file mode 100644 index 0000000000..8eebb89a0a --- /dev/null +++ b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/config/DmiHttpClientConfig.java @@ -0,0 +1,57 @@ +/* + * ============LICENSE_START======================================================= + * Copyright (C) 2023-2024 Nordix Foundation. + * ================================================================================ + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * SPDX-License-Identifier: Apache-2.0 + * ============LICENSE_END========================================================= + */ + +package org.onap.cps.ncmp.config; + +import lombok.Getter; +import lombok.Setter; +import org.springframework.boot.context.properties.ConfigurationProperties; +import org.springframework.context.annotation.Configuration; + +@Getter +@Setter +@Configuration +@ConfigurationProperties(prefix = "ncmp.dmi.httpclient") +public class DmiHttpClientConfig { + + private final DataServices dataServices = new DataServices(); + private final ModelServices modelServices = new ModelServices(); + private final HealthCheckServices healthCheckServices = new HealthCheckServices(); + + @Getter + @Setter + public static class DataServices extends ServiceConfig { + private String connectionProviderName = "dataConnectionPool"; + } + + @Getter + @Setter + public static class ModelServices extends ServiceConfig { + private String connectionProviderName = "modelConnectionPool"; + } + + @Getter + @Setter + public static class HealthCheckServices extends ServiceConfig { + private String connectionProviderName = "healthConnectionPool"; + private int maximumConnectionsTotal = 10; + private int pendingAcquireMaxCount = 5; + } +} diff --git a/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/config/kafka/KafkaConfig.java b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/config/KafkaConfig.java index 167df5a98d..3d3c3db482 100644 --- a/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/config/kafka/KafkaConfig.java +++ b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/config/KafkaConfig.java @@ -18,13 +18,17 @@ * ============LICENSE_END========================================================= */ -package org.onap.cps.ncmp.api.impl.config.kafka; +package org.onap.cps.ncmp.config; import io.cloudevents.CloudEvent; +import io.opentelemetry.instrumentation.kafkaclients.v2_6.TracingConsumerInterceptor; +import io.opentelemetry.instrumentation.kafkaclients.v2_6.TracingProducerInterceptor; import java.time.Duration; import java.util.Map; import lombok.RequiredArgsConstructor; +import org.apache.kafka.clients.consumer.ConsumerConfig; import org.apache.kafka.clients.producer.ProducerConfig; +import org.springframework.beans.factory.annotation.Value; import org.springframework.boot.autoconfigure.kafka.KafkaProperties; import org.springframework.boot.ssl.SslBundles; import org.springframework.context.annotation.Bean; @@ -52,6 +56,9 @@ public class KafkaConfig<T> { private final KafkaProperties kafkaProperties; + @Value("${cps.tracing.enabled:false}") + private boolean tracingEnabled; + private static final SslBundles NO_SSL = null; /** @@ -64,6 +71,10 @@ public class KafkaConfig<T> { public ProducerFactory<String, T> legacyEventProducerFactory() { final Map<String, Object> producerConfigProperties = kafkaProperties.buildProducerProperties(NO_SSL); producerConfigProperties.put(ProducerConfig.VALUE_SERIALIZER_CLASS_CONFIG, JsonSerializer.class); + if (tracingEnabled) { + producerConfigProperties.put( + ProducerConfig.INTERCEPTOR_CLASSES_CONFIG, TracingProducerInterceptor.class.getName()); + } return new DefaultKafkaProducerFactory<>(producerConfigProperties); } @@ -77,6 +88,10 @@ public class KafkaConfig<T> { public ConsumerFactory<String, T> legacyEventConsumerFactory() { final Map<String, Object> consumerConfigProperties = kafkaProperties.buildConsumerProperties(NO_SSL); consumerConfigProperties.put("spring.deserializer.value.delegate.class", JsonDeserializer.class); + if (tracingEnabled) { + consumerConfigProperties.put( + ConsumerConfig.INTERCEPTOR_CLASSES_CONFIG, TracingConsumerInterceptor.class.getName()); + } return new DefaultKafkaConsumerFactory<>(consumerConfigProperties); } @@ -90,6 +105,9 @@ public class KafkaConfig<T> { public KafkaTemplate<String, T> legacyEventKafkaTemplate() { final KafkaTemplate<String, T> kafkaTemplate = new KafkaTemplate<>(legacyEventProducerFactory()); kafkaTemplate.setConsumerFactory(legacyEventConsumerFactory()); + if (tracingEnabled) { + kafkaTemplate.setObservationEnabled(true); + } return kafkaTemplate; } @@ -104,6 +122,9 @@ public class KafkaConfig<T> { new ConcurrentKafkaListenerContainerFactory<>(); containerFactory.setConsumerFactory(legacyEventConsumerFactory()); containerFactory.getContainerProperties().setAuthExceptionRetryInterval(Duration.ofSeconds(10)); + if (tracingEnabled) { + containerFactory.getContainerProperties().setObservationEnabled(true); + } return containerFactory; } @@ -116,6 +137,10 @@ public class KafkaConfig<T> { @Bean public ProducerFactory<String, CloudEvent> cloudEventProducerFactory() { final Map<String, Object> producerConfigProperties = kafkaProperties.buildProducerProperties(NO_SSL); + if (tracingEnabled) { + producerConfigProperties.put( + ProducerConfig.INTERCEPTOR_CLASSES_CONFIG, TracingProducerInterceptor.class.getName()); + } return new DefaultKafkaProducerFactory<>(producerConfigProperties); } @@ -128,6 +153,10 @@ public class KafkaConfig<T> { @Bean public ConsumerFactory<String, CloudEvent> cloudEventConsumerFactory() { final Map<String, Object> consumerConfigProperties = kafkaProperties.buildConsumerProperties(NO_SSL); + if (tracingEnabled) { + consumerConfigProperties.put( + ConsumerConfig.INTERCEPTOR_CLASSES_CONFIG, TracingConsumerInterceptor.class.getName()); + } return new DefaultKafkaConsumerFactory<>(consumerConfigProperties); } @@ -142,6 +171,9 @@ public class KafkaConfig<T> { final KafkaTemplate<String, CloudEvent> kafkaTemplate = new KafkaTemplate<>(cloudEventProducerFactory()); kafkaTemplate.setConsumerFactory(cloudEventConsumerFactory()); + if (tracingEnabled) { + kafkaTemplate.setObservationEnabled(true); + } return kafkaTemplate; } @@ -157,6 +189,9 @@ public class KafkaConfig<T> { new ConcurrentKafkaListenerContainerFactory<>(); containerFactory.setConsumerFactory(cloudEventConsumerFactory()); containerFactory.getContainerProperties().setAuthExceptionRetryInterval(Duration.ofSeconds(10)); + if (tracingEnabled) { + containerFactory.getContainerProperties().setObservationEnabled(true); + } return containerFactory; } diff --git a/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/config/OpenTelemetryConfig.java b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/config/OpenTelemetryConfig.java new file mode 100644 index 0000000000..a6a82b7936 --- /dev/null +++ b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/config/OpenTelemetryConfig.java @@ -0,0 +1,141 @@ +/* + * ============LICENSE_START======================================================= + * Copyright (C) 2024 Nordix Foundation + * ================================================================================ + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * SPDX-License-Identifier: Apache-2.0 + * ============LICENSE_END========================================================= + */ + +package org.onap.cps.ncmp.config; + +import io.micrometer.observation.ObservationPredicate; +import io.micrometer.observation.ObservationRegistry; +import io.opentelemetry.exporter.otlp.http.trace.OtlpHttpSpanExporter; +import io.opentelemetry.exporter.otlp.trace.OtlpGrpcSpanExporter; +import io.opentelemetry.sdk.extension.trace.jaeger.sampler.JaegerRemoteSampler; +import io.opentelemetry.sdk.trace.samplers.Sampler; +import jakarta.annotation.PostConstruct; +import java.time.Duration; +import java.util.Arrays; +import java.util.List; +import java.util.stream.Collectors; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.boot.actuate.autoconfigure.observation.ObservationRegistryCustomizer; +import org.springframework.boot.autoconfigure.condition.ConditionalOnExpression; +import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.http.server.observation.ServerRequestObservationContext; +import org.springframework.util.AntPathMatcher; +import org.springframework.util.PathMatcher; + +/** + * Configuration class for setting up OpenTelemetry tracing in a Spring Boot application. + * This class provides beans for OTLP exporters (gRPC and HTTP), a Jaeger remote sampler, + * and customizes the ObservationRegistry to exclude certain endpoints from being observed. + */ +@Configuration +public class OpenTelemetryConfig { + + @Value("${spring.application.name:cps-application}") + private String serviceId; + + @Value("${cps.tracing.exporter.endpoint:http://onap-otel-collector:4317}") + private String tracingExporterEndpointUrl; + + @Value("${cps.tracing.sampler.jaeger_remote.endpoint:http://onap-otel-collector:14250}") + private String jaegerRemoteSamplerUrl; + + @Value("${cps.tracing.excluded-observation-names:tasks.scheduled.execution}") + private String excludedObservationNamesAsCsv; + + private static final int JAEGER_REMOTE_SAMPLER_POLLING_INTERVAL_IN_SECONDS = 30; + + private List<String> excludedObservationNames; + + /** + * Initializes the excludedObservationNames after the bean's properties have been set. + * This method is called by the Spring container during bean initialization. + */ + @PostConstruct + public void init() { + excludedObservationNames = Arrays.stream(excludedObservationNamesAsCsv.split(",")) + .map(String::trim) + .collect(Collectors.toList()); + } + + /** + * Creates an OTLP Exporter with gRPC protocol. + * + * @return OtlpGrpcSpanExporter bean if tracing is enabled and the exporter protocol is gRPC + */ + @Bean + @ConditionalOnExpression( + "${cps.tracing.enabled} && 'grpc'.equals('${cps.tracing.exporter.protocol}')") + public OtlpGrpcSpanExporter createOtlpExporterGrpc() { + return OtlpGrpcSpanExporter.builder().setEndpoint(tracingExporterEndpointUrl).build(); + } + + /** + * Creates an OTLP Exporter with HTTP protocol. + * + * @return OtlpHttpSpanExporter bean if tracing is enabled and the exporter protocol is HTTP + */ + @Bean + @ConditionalOnExpression( + "${cps.tracing.enabled} && 'http'.equals('${cps.tracing.exporter.protocol}')") + public OtlpHttpSpanExporter createOtlpExporterHttp() { + return OtlpHttpSpanExporter.builder().setEndpoint(tracingExporterEndpointUrl).build(); + } + + /** + * Creates a Jaeger Remote Sampler. + * + * @return JaegerRemoteSampler bean if tracing is enabled + */ + @Bean + @ConditionalOnProperty("cps.tracing.enabled") + public JaegerRemoteSampler createJaegerRemoteSampler() { + return JaegerRemoteSampler.builder() + .setEndpoint(jaegerRemoteSamplerUrl) + .setPollingInterval(Duration.ofSeconds(JAEGER_REMOTE_SAMPLER_POLLING_INTERVAL_IN_SECONDS)) + .setInitialSampler(Sampler.alwaysOff()) + .setServiceName(serviceId) + .build(); + } + + /** + * Customizes the ObservationRegistry to exclude /actuator/** endpoints from being observed. + * + * @return ObservationRegistryCustomizer bean if tracing is enabled + */ + @Bean + @ConditionalOnProperty("cps.tracing.enabled") + public ObservationRegistryCustomizer<ObservationRegistry> skipActuatorEndpointsFromObservation() { + final PathMatcher pathMatcher = new AntPathMatcher("/"); + return registry -> + registry.observationConfig().observationPredicate(observationPredicate(pathMatcher)); + } + + private ObservationPredicate observationPredicate(final PathMatcher pathMatcher) { + return (observationName, context) -> { + if (context instanceof ServerRequestObservationContext observationContext) { + return !pathMatcher.match("/actuator/**", observationContext.getCarrier().getRequestURI()); + } else { + return !excludedObservationNames.contains(observationName); + } + }; + } +} diff --git a/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/config/HttpClientConfiguration.java b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/config/PolicyExecutorHttpClientConfig.java index d547e31c68..30e7cd5e0b 100644 --- a/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/config/HttpClientConfiguration.java +++ b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/config/PolicyExecutorHttpClientConfig.java @@ -1,6 +1,6 @@ /* * ============LICENSE_START======================================================= - * Copyright (C) 2023 Nordix Foundation. + * Copyright (C) 2024 Nordix Foundation. * ================================================================================ * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -18,40 +18,30 @@ * ============LICENSE_END========================================================= */ -package org.onap.cps.ncmp.api.impl.config; +package org.onap.cps.ncmp.config; -import java.time.Duration; -import java.time.temporal.ChronoUnit; +import jakarta.annotation.PostConstruct; import lombok.Getter; import lombok.Setter; import org.springframework.boot.context.properties.ConfigurationProperties; -import org.springframework.boot.convert.DurationUnit; +import org.springframework.context.annotation.Configuration; @Getter @Setter -@ConfigurationProperties(prefix = "ncmp.dmi.httpclient", ignoreUnknownFields = true) -public class HttpClientConfiguration { - - /** - * The maximum time to establish a connection. - */ - @DurationUnit(ChronoUnit.SECONDS) - private Duration connectionTimeoutInSeconds = Duration.ofSeconds(180); - - /** - * The maximum number of open connections per route. - */ - private int maximumConnectionsPerRoute = 50; - - /** - * The maximum total number of open connections. - */ - private int maximumConnectionsTotal = maximumConnectionsPerRoute * 2; - - /** - * The duration after which idle connections are evicted. - */ - @DurationUnit(ChronoUnit.SECONDS) - private Duration idleConnectionEvictionThresholdInSeconds = Duration.ofSeconds(5); - +@Configuration +@ConfigurationProperties(prefix = "ncmp.policy-executor.httpclient") +public class PolicyExecutorHttpClientConfig { + + private final AllServices allServices = new AllServices(); + + @Getter + @Setter + public static class AllServices extends ServiceConfig { + private String connectionProviderName = "policyExecutorConfig"; + } + + @PostConstruct + public void increaseReadTimeoutOfWebClient() { + allServices.setReadTimeoutInSeconds(allServices.getReadTimeoutInSeconds() + 10); + } } diff --git a/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/config/ServiceConfig.java b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/config/ServiceConfig.java new file mode 100644 index 0000000000..f1fce0c7c6 --- /dev/null +++ b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/config/ServiceConfig.java @@ -0,0 +1,36 @@ +/* + * ============LICENSE_START======================================================= + * Copyright (C) 2024 Nordix Foundation. + * ================================================================================ + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * SPDX-License-Identifier: Apache-2.0 + * ============LICENSE_END========================================================= + */ + +package org.onap.cps.ncmp.config; + +import lombok.Getter; +import lombok.Setter; + +@Getter +@Setter +public abstract class ServiceConfig { + private String connectionProviderName = ""; + private int maximumInMemorySizeInMegabytes = 1; + private int maximumConnectionsTotal = 1; + private int pendingAcquireMaxCount = 1; + private Integer connectionTimeoutInSeconds = 1; + private long readTimeoutInSeconds = 1; + private long writeTimeoutInSeconds = 1; +} diff --git a/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/exception/NcmpStartUpException.java b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/exceptions/NcmpStartUpException.java index 7ac26f54fd..7ffbe2e5cb 100644 --- a/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/exception/NcmpStartUpException.java +++ b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/exceptions/NcmpStartUpException.java @@ -18,7 +18,9 @@ * ============LICENSE_END========================================================= */ -package org.onap.cps.ncmp.api.impl.exception; +package org.onap.cps.ncmp.exceptions; + +import org.onap.cps.ncmp.api.exceptions.NcmpException; /** * NCMP start up exception. @@ -34,4 +36,4 @@ public class NcmpStartUpException extends NcmpException { public NcmpStartUpException(final String message, final String details) { super(message, details); } -}
\ No newline at end of file +} diff --git a/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/exception/NoAlternateIdParentFoundException.java b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/exceptions/NoAlternateIdMatchFoundException.java index 2e6cd3308c..7c5fc3be2b 100644 --- a/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/exception/NoAlternateIdParentFoundException.java +++ b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/exceptions/NoAlternateIdMatchFoundException.java @@ -18,22 +18,23 @@ * ============LICENSE_END========================================================= */ -package org.onap.cps.ncmp.api.impl.exception; +package org.onap.cps.ncmp.exceptions; import java.io.Serial; +import org.onap.cps.ncmp.api.exceptions.NcmpException; -public class NoAlternateIdParentFoundException extends NcmpException { +public class NoAlternateIdMatchFoundException extends NcmpException { @Serial private static final long serialVersionUID = -2412915490233422945L; - private static final String ALTERNATE_ID_NOT_FOUND = "No matching (parent) cm handle found using alternate ids"; + private static final String ALTERNATE_ID_NOT_FOUND = "No matching cm handle found using alternate ids"; /** * Constructor. * * @param cpsPath datanode cpsPath */ - public NoAlternateIdParentFoundException(final String cpsPath) { + public NoAlternateIdMatchFoundException(final String cpsPath) { super(ALTERNATE_ID_NOT_FOUND, String.format("cannot find a datanode with alternate id %s", cpsPath)); } } diff --git a/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/impl/cache/HazelcastCacheConfig.java b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/impl/cache/HazelcastCacheConfig.java new file mode 100644 index 0000000000..d911fc61b9 --- /dev/null +++ b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/impl/cache/HazelcastCacheConfig.java @@ -0,0 +1,104 @@ +/* + * ============LICENSE_START======================================================== + * Copyright (C) 2023-2024 Nordix Foundation + * ================================================================================ + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * SPDX-License-Identifier: Apache-2.0 + * ============LICENSE_END========================================================= + */ + +package org.onap.cps.ncmp.impl.cache; + +import com.hazelcast.config.Config; +import com.hazelcast.config.MapConfig; +import com.hazelcast.config.NamedConfig; +import com.hazelcast.config.QueueConfig; +import com.hazelcast.config.RestEndpointGroup; +import com.hazelcast.config.SetConfig; +import com.hazelcast.core.Hazelcast; +import com.hazelcast.core.HazelcastInstance; +import lombok.extern.slf4j.Slf4j; +import org.springframework.beans.factory.annotation.Value; + +/** + * Core infrastructure of the hazelcast distributed cache. + */ +@Slf4j +public class HazelcastCacheConfig { + + @Value("${hazelcast.cluster-name}") + protected String clusterName; + + @Value("${hazelcast.mode.kubernetes.enabled}") + protected boolean cacheKubernetesEnabled; + + @Value("${hazelcast.mode.kubernetes.service-name}") + protected String cacheKubernetesServiceName; + + protected HazelcastInstance createHazelcastInstance(final String hazelcastInstanceName, + final NamedConfig namedConfig) { + return Hazelcast.newHazelcastInstance(initializeConfig(hazelcastInstanceName, namedConfig)); + } + + private Config initializeConfig(final String instanceName, final NamedConfig namedConfig) { + final Config config = new Config(instanceName); + if (namedConfig instanceof MapConfig) { + config.addMapConfig((MapConfig) namedConfig); + } + if (namedConfig instanceof QueueConfig) { + config.addQueueConfig((QueueConfig) namedConfig); + } + if (namedConfig instanceof SetConfig) { + config.addSetConfig((SetConfig) namedConfig); + } + + config.setClusterName(clusterName); + config.setClassLoader(org.onap.cps.spi.model.Dataspace.class.getClassLoader()); + exposeClusterInformation(config); + updateDiscoveryMode(config); + return config; + } + + protected static MapConfig createMapConfig(final String configName) { + final MapConfig mapConfig = new MapConfig(configName); + mapConfig.setBackupCount(1); + return mapConfig; + } + + protected static QueueConfig createQueueConfig(final String configName) { + final QueueConfig commonQueueConfig = new QueueConfig(configName); + commonQueueConfig.setBackupCount(1); + return commonQueueConfig; + } + + protected static SetConfig createSetConfig(final String configName) { + final SetConfig commonSetConfig = new SetConfig(configName); + commonSetConfig.setBackupCount(1); + return commonSetConfig; + } + + protected void updateDiscoveryMode(final Config config) { + if (cacheKubernetesEnabled) { + log.info("Enabling kubernetes mode with service-name : {}", cacheKubernetesServiceName); + config.getNetworkConfig().getJoin().getKubernetesConfig().setEnabled(true) + .setProperty("service-name", cacheKubernetesServiceName); + } + } + + protected void exposeClusterInformation(final Config config) { + config.getNetworkConfig().getRestApiConfig().setEnabled(true) + .enableGroups(RestEndpointGroup.HEALTH_CHECK, RestEndpointGroup.CLUSTER_READ); + } + +} diff --git a/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/config/embeddedcache/CmNotificationSubscriptionCacheConfig.java b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/impl/cmnotificationsubscription/cache/CmSubscriptionConfig.java index 1d6da90a9a..e890d7288c 100644 --- a/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/config/embeddedcache/CmNotificationSubscriptionCacheConfig.java +++ b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/impl/cmnotificationsubscription/cache/CmSubscriptionConfig.java @@ -18,18 +18,18 @@ * ============LICENSE_END========================================================= */ -package org.onap.cps.ncmp.api.impl.config.embeddedcache; +package org.onap.cps.ncmp.impl.cmnotificationsubscription.cache; import com.hazelcast.config.MapConfig; import com.hazelcast.map.IMap; import java.util.Map; -import org.onap.cps.cache.HazelcastCacheConfig; -import org.onap.cps.ncmp.api.impl.events.cmsubscription.model.DmiCmNotificationSubscriptionDetails; +import org.onap.cps.ncmp.impl.cache.HazelcastCacheConfig; +import org.onap.cps.ncmp.impl.cmnotificationsubscription.models.DmiCmSubscriptionDetails; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; @Configuration -public class CmNotificationSubscriptionCacheConfig extends HazelcastCacheConfig { +public class CmSubscriptionConfig extends HazelcastCacheConfig { private static final MapConfig cmNotificationSubscriptionCacheMapConfig = createMapConfig("cmNotificationSubscriptionCacheMapConfig"); @@ -42,7 +42,7 @@ public class CmNotificationSubscriptionCacheConfig extends HazelcastCacheConfig * @return configured map of subscription events. */ @Bean - public IMap<String, Map<String, DmiCmNotificationSubscriptionDetails>> cmNotificationSubscriptionCache() { + public IMap<String, Map<String, DmiCmSubscriptionDetails>> cmNotificationSubscriptionCache() { return createHazelcastInstance("hazelCastInstanceCmNotificationSubscription", cmNotificationSubscriptionCacheMapConfig).getMap("cmNotificationSubscriptionCache"); } diff --git a/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/impl/cmnotificationsubscription/cache/DmiCacheHandler.java b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/impl/cmnotificationsubscription/cache/DmiCacheHandler.java new file mode 100644 index 0000000000..b5ab7f6525 --- /dev/null +++ b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/impl/cmnotificationsubscription/cache/DmiCacheHandler.java @@ -0,0 +1,227 @@ +/* + * ============LICENSE_START======================================================= + * Copyright (C) 2024 Nordix Foundation + * ================================================================================ + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * SPDX-License-Identifier: Apache-2.0 + * ============LICENSE_END========================================================= + */ + +package org.onap.cps.ncmp.impl.cmnotificationsubscription.cache; + +import static org.onap.cps.ncmp.impl.cmnotificationsubscription.models.CmSubscriptionStatus.PENDING; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.HashMap; +import java.util.HashSet; +import java.util.List; +import java.util.Map; +import java.util.Set; +import java.util.stream.Collectors; +import lombok.RequiredArgsConstructor; +import org.onap.cps.ncmp.api.data.models.DatastoreType; +import org.onap.cps.ncmp.impl.cmnotificationsubscription.models.CmSubscriptionStatus; +import org.onap.cps.ncmp.impl.cmnotificationsubscription.models.DmiCmSubscriptionDetails; +import org.onap.cps.ncmp.impl.cmnotificationsubscription.models.DmiCmSubscriptionPredicate; +import org.onap.cps.ncmp.impl.cmnotificationsubscription.utils.CmSubscriptionPersistenceService; +import org.onap.cps.ncmp.impl.cmnotificationsubscription_1_0_0.client_to_ncmp.Predicate; +import org.onap.cps.ncmp.impl.inventory.InventoryPersistence; +import org.onap.cps.ncmp.impl.inventory.models.YangModelCmHandle; +import org.springframework.stereotype.Component; + +@Component +@RequiredArgsConstructor +public class DmiCacheHandler { + + private final CmSubscriptionPersistenceService cmSubscriptionPersistenceService; + private final Map<String, Map<String, DmiCmSubscriptionDetails>> cmNotificationSubscriptionCache; + private final InventoryPersistence inventoryPersistence; + + /** + * Adds subscription to the subscription cache. + * + * @param subscriptionId subscription id + * @param predicates subscription request predicates + */ + public void add(final String subscriptionId, final List<Predicate> predicates) { + cmNotificationSubscriptionCache.put(subscriptionId, createDmiSubscriptionsPerDmi(predicates)); + } + + /** + * Adds subscription to the subscription cache. + * + * @param subscriptionId subscription id + * @param dmiCmSubscriptionDetailsPerDmi map of dmi cm notification subscription details per dmi + */ + public void add(final String subscriptionId, + final Map<String, DmiCmSubscriptionDetails> + dmiCmSubscriptionDetailsPerDmi) { + cmNotificationSubscriptionCache.put(subscriptionId, dmiCmSubscriptionDetailsPerDmi); + } + + /** + * Get cm notification subscription cache entry via subscription id. + * + * @param subscriptionId subscription id + * @return map of dmi cm notification subscriptions per dmi + */ + public Map<String, DmiCmSubscriptionDetails> get(final String subscriptionId) { + return cmNotificationSubscriptionCache.get(subscriptionId); + } + + + /** + * Remove cache entries with CmNotificationSubscriptionStatus ACCEPTED/REJECTED via subscription id. + * + * @param subscriptionId subscription id as key in CM notification Subscription cache. + */ + public void removeAcceptedAndRejectedDmiSubscriptionEntries(final String subscriptionId) { + final Map<String, DmiCmSubscriptionDetails> dmiSubscriptionsPerDmi = + cmNotificationSubscriptionCache.get(subscriptionId); + final Map<String, DmiCmSubscriptionDetails> updatedDmiSubscriptionsPerDmi = + dmiSubscriptionsPerDmi.entrySet().stream() + .filter(dmiCmNotificationSubscription -> !isAcceptedOrRejected( + dmiCmNotificationSubscription.getValue())) + .collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue)); + cmNotificationSubscriptionCache.put(subscriptionId, updatedDmiSubscriptionsPerDmi); + } + + /** + * Creates map of subscription details per DMI. + * + * @param predicates CM Subscription Create Request Predicates + * @return Map of DmiCmNotificationSubscription per DMI plugin + */ + public Map<String, DmiCmSubscriptionDetails> createDmiSubscriptionsPerDmi( + final List<Predicate> predicates) { + final Map<String, DmiCmSubscriptionDetails> dmiSubscriptionsPerDmi = + new HashMap<>(); + for (final Predicate requestPredicate : predicates) { + final List<String> targetFilter = requestPredicate.getTargetFilter(); + final DatastoreType datastoreType = DatastoreType.fromDatastoreName( + requestPredicate.getScopeFilter().getDatastore().toString()); + final Set<String> xpaths = new HashSet<>(requestPredicate.getScopeFilter().getXpathFilter()); + final Map<String, Set<String>> targetCmHandlesByDmiMap = groupTargetCmHandleIdsByDmi(targetFilter); + for (final Map.Entry<String, Set<String>> targetCmHandlesByDmi: targetCmHandlesByDmiMap.entrySet()) { + final DmiCmSubscriptionPredicate dmiCmSubscriptionPredicate = + new DmiCmSubscriptionPredicate(targetCmHandlesByDmi.getValue(), + datastoreType, xpaths); + updateDmiSubscriptionDetailsPerDmi(targetCmHandlesByDmi.getKey(), + dmiCmSubscriptionPredicate, + dmiSubscriptionsPerDmi); + } + } + return dmiSubscriptionsPerDmi; + } + + /** + * Update status in map of subscription details per DMI. + * + * @param subscriptionId String of subscription Id + * @param dmiServiceName String of dmiServiceName + * @param status String of status + * + */ + public void updateDmiSubscriptionStatus(final String subscriptionId, final String dmiServiceName, + final CmSubscriptionStatus status) { + final Map<String, DmiCmSubscriptionDetails> dmiSubscriptionsPerDmi = + cmNotificationSubscriptionCache.get(subscriptionId); + dmiSubscriptionsPerDmi.get(dmiServiceName).setCmSubscriptionStatus(status); + cmNotificationSubscriptionCache.put(subscriptionId, dmiSubscriptionsPerDmi); + } + + /** + * Persist map of subscription details per DMI. + * + * @param subscriptionId String of subscription Id + * @param dmiServiceName String of dmiServiceName + * + */ + public void persistIntoDatabasePerDmi(final String subscriptionId, final String dmiServiceName) { + final List<DmiCmSubscriptionPredicate> dmiCmSubscriptionPredicates = + cmNotificationSubscriptionCache.get(subscriptionId).get(dmiServiceName) + .getDmiCmSubscriptionPredicates(); + for (final DmiCmSubscriptionPredicate dmiCmSubscriptionPredicate : dmiCmSubscriptionPredicates) { + final DatastoreType datastoreType = dmiCmSubscriptionPredicate.getDatastoreType(); + final Set<String> cmHandles = dmiCmSubscriptionPredicate.getTargetCmHandleIds(); + final Set<String> xpaths = dmiCmSubscriptionPredicate.getXpaths(); + + for (final String cmHandle: cmHandles) { + for (final String xpath: xpaths) { + cmSubscriptionPersistenceService.addCmSubscription(datastoreType, cmHandle, + xpath, subscriptionId); + } + } + } + } + + /** + * Remove subscription from database per DMI service name. + * + * @param subscriptionId String of subscription id + * @param dmiServiceName String of dmiServiceName + * + */ + public void removeFromDatabase(final String subscriptionId, final String dmiServiceName) { + final List<DmiCmSubscriptionPredicate> dmiCmSubscriptionPredicates = + cmNotificationSubscriptionCache.get(subscriptionId).get(dmiServiceName) + .getDmiCmSubscriptionPredicates(); + for (final DmiCmSubscriptionPredicate dmiCmSubscriptionPredicate : dmiCmSubscriptionPredicates) { + final DatastoreType datastoreType = dmiCmSubscriptionPredicate.getDatastoreType(); + final Set<String> cmHandles = dmiCmSubscriptionPredicate.getTargetCmHandleIds(); + final Set<String> xpaths = dmiCmSubscriptionPredicate.getXpaths(); + + for (final String cmHandle: cmHandles) { + for (final String xpath: xpaths) { + cmSubscriptionPersistenceService.removeCmSubscription(datastoreType, + cmHandle, xpath, subscriptionId); + } + } + } + } + + private void updateDmiSubscriptionDetailsPerDmi( + final String dmiServiceName, + final DmiCmSubscriptionPredicate dmiCmSubscriptionPredicate, + final Map<String, DmiCmSubscriptionDetails> dmiSubscriptionsPerDmi) { + if (dmiSubscriptionsPerDmi.containsKey(dmiServiceName)) { + dmiSubscriptionsPerDmi.get(dmiServiceName) + .getDmiCmSubscriptionPredicates().add(dmiCmSubscriptionPredicate); + } else { + dmiSubscriptionsPerDmi.put(dmiServiceName, + new DmiCmSubscriptionDetails( + new ArrayList<>(List.of(dmiCmSubscriptionPredicate)), + PENDING)); + } + } + + private Map<String, Set<String>> groupTargetCmHandleIdsByDmi(final List<String> targetCmHandleIds) { + final Map<String, Set<String>> targetCmHandlesByDmiServiceNames = new HashMap<>(); + final Collection<YangModelCmHandle> yangModelCmHandles = + inventoryPersistence.getYangModelCmHandles(targetCmHandleIds); + + for (final YangModelCmHandle yangModelCmHandle : yangModelCmHandles) { + final String dmiServiceName = yangModelCmHandle.getDmiServiceName(); + targetCmHandlesByDmiServiceNames.putIfAbsent(dmiServiceName, new HashSet<>()); + targetCmHandlesByDmiServiceNames.get(dmiServiceName).add(yangModelCmHandle.getId()); + } + return targetCmHandlesByDmiServiceNames; + } + + private boolean isAcceptedOrRejected(final DmiCmSubscriptionDetails dmiCmSubscription) { + return dmiCmSubscription.getCmSubscriptionStatus().toString().equals("ACCEPTED") + || dmiCmSubscription.getCmSubscriptionStatus().toString().equals("REJECTED"); + } +} diff --git a/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/events/avc/AvcEventConsumer.java b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/impl/cmnotificationsubscription/cmavc/CmAvcEventConsumer.java index f635f1a80b..9e90eabbc4 100644 --- a/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/events/avc/AvcEventConsumer.java +++ b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/impl/cmnotificationsubscription/cmavc/CmAvcEventConsumer.java @@ -18,11 +18,9 @@ * ============LICENSE_END========================================================= */ -package org.onap.cps.ncmp.api.impl.events.avc; +package org.onap.cps.ncmp.impl.cmnotificationsubscription.cmavc; import io.cloudevents.CloudEvent; -import io.cloudevents.core.builder.CloudEventBuilder; -import java.util.UUID; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; import org.apache.kafka.clients.consumer.ConsumerRecord; @@ -33,13 +31,13 @@ import org.springframework.kafka.annotation.KafkaListener; import org.springframework.stereotype.Component; /** - * Listener for AVC events. + * Listener for AVC events based on Cm Subscriptions. */ @Component @Slf4j @RequiredArgsConstructor @ConditionalOnProperty(name = "notification.enabled", havingValue = "true", matchIfMissing = true) -public class AvcEventConsumer { +public class CmAvcEventConsumer { @Value("${app.ncmp.avc.cm-events-topic}") @@ -50,15 +48,14 @@ public class AvcEventConsumer { /** * Incoming AvcEvent in the form of Consumer Record. * - * @param avcEventConsumerRecord Incoming raw consumer record + * @param cmAvcEventAsConsumerRecord Incoming raw consumer record */ @KafkaListener(topics = "${app.dmi.cm-events.topic}", containerFactory = "cloudEventConcurrentKafkaListenerContainerFactory") - public void consumeAndForward(final ConsumerRecord<String, CloudEvent> avcEventConsumerRecord) { - log.debug("Consuming AVC event {} ...", avcEventConsumerRecord.value()); - final String newEventId = UUID.randomUUID().toString(); - final CloudEvent outgoingAvcEvent = - CloudEventBuilder.from(avcEventConsumerRecord.value()).withId(newEventId).build(); - eventsPublisher.publishCloudEvent(cmEventsTopicName, newEventId, outgoingAvcEvent); + public void consumeAndForward( + final ConsumerRecord<String, CloudEvent> cmAvcEventAsConsumerRecord) { + final CloudEvent outgoingAvcEvent = cmAvcEventAsConsumerRecord.value(); + log.debug("Consuming AVC event {} ...", outgoingAvcEvent); + eventsPublisher.publishCloudEvent(cmEventsTopicName, outgoingAvcEvent.getId(), outgoingAvcEvent); } } diff --git a/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/impl/cmnotificationsubscription/dmi/DmiCmSubscriptionDetailsPerDmiMapper.java b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/impl/cmnotificationsubscription/dmi/DmiCmSubscriptionDetailsPerDmiMapper.java new file mode 100644 index 0000000000..423c603a92 --- /dev/null +++ b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/impl/cmnotificationsubscription/dmi/DmiCmSubscriptionDetailsPerDmiMapper.java @@ -0,0 +1,103 @@ +/* + * ============LICENSE_START======================================================= + * Copyright (C) 2024 Nordix Foundation + * ================================================================================ + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * SPDX-License-Identifier: Apache-2.0 + * ============LICENSE_END========================================================= + */ + +package org.onap.cps.ncmp.impl.cmnotificationsubscription.dmi; + +import static org.onap.cps.ncmp.api.data.models.DatastoreType.fromDatastoreName; +import static org.onap.cps.ncmp.impl.cmnotificationsubscription.models.CmSubscriptionStatus.PENDING; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.Collections; +import java.util.HashMap; +import java.util.HashSet; +import java.util.List; +import java.util.Map; +import java.util.Set; +import java.util.stream.Collectors; +import org.onap.cps.ncmp.api.data.models.DatastoreType; +import org.onap.cps.ncmp.impl.cmnotificationsubscription.models.DmiCmSubscriptionDetails; +import org.onap.cps.ncmp.impl.cmnotificationsubscription.models.DmiCmSubscriptionKey; +import org.onap.cps.ncmp.impl.cmnotificationsubscription.models.DmiCmSubscriptionPredicate; +import org.springframework.stereotype.Component; + +@Component +public class DmiCmSubscriptionDetailsPerDmiMapper { + + /** + * Maps Dmi Subscription Keys grouped by Dmi Plugin to DmiCmSubscriptionDetails per Dmi plugin. + * + * @param subscribersPerDmi Details managed by each dmi plugin + * @return Grouped Dmi Subscription details per dmi plugin + */ + public Map<String, DmiCmSubscriptionDetails> toDmiCmSubscriptionsPerDmi( + final Map<String, Collection<DmiCmSubscriptionKey>> subscribersPerDmi) { + + final Map<String, DmiCmSubscriptionDetails> dmiSubscriptionsPerDmi = new HashMap<>(); + + subscribersPerDmi.forEach((dmiPluginName, dmiCmSubscriptionKeys) -> { + final Map<DatastoreTypeAndXpath, List<DmiCmSubscriptionKey>> groupedByDatastoreTypeAndXpath = + groupByDatastoreTypeAndXpath(dmiCmSubscriptionKeys); + + final List<DmiCmSubscriptionPredicate> dmiSubscriptionPredicates = + createDmiCmSubscriptionPredicates(groupedByDatastoreTypeAndXpath); + + final DmiCmSubscriptionDetails dmiCmSubscriptionDetails = + new DmiCmSubscriptionDetails(dmiSubscriptionPredicates, PENDING); + + dmiSubscriptionsPerDmi.put(dmiPluginName, dmiCmSubscriptionDetails); + }); + + return dmiSubscriptionsPerDmi; + } + + private static Map<DatastoreTypeAndXpath, List<DmiCmSubscriptionKey>> groupByDatastoreTypeAndXpath( + final Collection<DmiCmSubscriptionKey> dmiCmSubscriptionKeys) { + return dmiCmSubscriptionKeys.stream().collect(Collectors.groupingBy( + datastoreTypeAndXpath -> new DatastoreTypeAndXpath( + fromDatastoreName(datastoreTypeAndXpath.datastoreName()), datastoreTypeAndXpath.xpath()))); + } + + private static List<DmiCmSubscriptionPredicate> createDmiCmSubscriptionPredicates( + final Map<DatastoreTypeAndXpath, List<DmiCmSubscriptionKey>> groupedByDatastoreTypeAndXpath) { + final List<DmiCmSubscriptionPredicate> dmiCmSubscriptionPredicates = new ArrayList<>(); + + for (final Map.Entry<DatastoreTypeAndXpath, List<DmiCmSubscriptionKey>> datastoreTypeXpathGroupEntry : + groupedByDatastoreTypeAndXpath.entrySet()) { + final DatastoreTypeAndXpath datastoreTypeAndXpath = datastoreTypeXpathGroupEntry.getKey(); + final Set<String> cmHandleIds = new HashSet<>(); + + for (final DmiCmSubscriptionKey dmiCmSubscriptionKey : datastoreTypeXpathGroupEntry.getValue()) { + cmHandleIds.add(dmiCmSubscriptionKey.cmHandleId()); + } + + final Set<String> xpaths = Collections.singleton(datastoreTypeAndXpath.xpath()); + dmiCmSubscriptionPredicates.add( + new DmiCmSubscriptionPredicate(cmHandleIds, datastoreTypeAndXpath.datastoreType(), xpaths)); + } + + return dmiCmSubscriptionPredicates; + } + + + private record DatastoreTypeAndXpath(DatastoreType datastoreType, String xpath) { } + +} + diff --git a/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/events/cmsubscription/mapper/CmNotificationSubscriptionDmiInEventMapper.java b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/impl/cmnotificationsubscription/dmi/DmiInEventMapper.java index 489401f26e..4ce4ef36cf 100644 --- a/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/events/cmsubscription/mapper/CmNotificationSubscriptionDmiInEventMapper.java +++ b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/impl/cmnotificationsubscription/dmi/DmiInEventMapper.java @@ -18,7 +18,7 @@ * ============LICENSE_END========================================================= */ -package org.onap.cps.ncmp.api.impl.events.cmsubscription.mapper; +package org.onap.cps.ncmp.impl.cmnotificationsubscription.dmi; import java.util.ArrayList; import java.util.HashSet; @@ -27,46 +27,44 @@ import java.util.List; import java.util.Map; import java.util.Set; import lombok.RequiredArgsConstructor; -import org.onap.cps.ncmp.api.impl.events.cmsubscription.model.DmiCmNotificationSubscriptionPredicate; -import org.onap.cps.ncmp.api.impl.inventory.InventoryPersistence; -import org.onap.cps.ncmp.events.cmnotificationsubscription_merge1_0_0.ncmp_to_dmi.CmNotificationSubscriptionDmiInEvent; -import org.onap.cps.ncmp.events.cmnotificationsubscription_merge1_0_0.ncmp_to_dmi.Cmhandle; -import org.onap.cps.ncmp.events.cmnotificationsubscription_merge1_0_0.ncmp_to_dmi.Data; -import org.onap.cps.ncmp.events.cmnotificationsubscription_merge1_0_0.ncmp_to_dmi.Predicate; -import org.onap.cps.ncmp.events.cmnotificationsubscription_merge1_0_0.ncmp_to_dmi.ScopeFilter; +import org.onap.cps.ncmp.impl.cmnotificationsubscription.models.DmiCmSubscriptionPredicate; +import org.onap.cps.ncmp.impl.cmnotificationsubscription_1_0_0.ncmp_to_dmi.CmHandle; +import org.onap.cps.ncmp.impl.cmnotificationsubscription_1_0_0.ncmp_to_dmi.Data; +import org.onap.cps.ncmp.impl.cmnotificationsubscription_1_0_0.ncmp_to_dmi.DmiInEvent; +import org.onap.cps.ncmp.impl.cmnotificationsubscription_1_0_0.ncmp_to_dmi.Predicate; +import org.onap.cps.ncmp.impl.cmnotificationsubscription_1_0_0.ncmp_to_dmi.ScopeFilter; +import org.onap.cps.ncmp.impl.inventory.InventoryPersistence; import org.springframework.stereotype.Component; @Component @RequiredArgsConstructor -public class CmNotificationSubscriptionDmiInEventMapper { +public class DmiInEventMapper { private final InventoryPersistence inventoryPersistence; /** * Mapper to form a request for the DMI Plugin for the Cm Notification Subscription. * - * @param dmiCmNotificationSubscriptionPredicates Collection of Cm Notification Subscription predicates - * @return CmNotificationSubscriptionDmiInEvent to be sent to DMI Plugin + * @param dmiCmSubscriptionPredicates Collection of Cm Notification Subscription predicates + * @return DmiInEvent to be sent to DMI Plugin */ - public CmNotificationSubscriptionDmiInEvent toCmNotificationSubscriptionDmiInEvent( - final List<DmiCmNotificationSubscriptionPredicate> dmiCmNotificationSubscriptionPredicates) { - final CmNotificationSubscriptionDmiInEvent cmNotificationSubscriptionDmiInEvent = - new CmNotificationSubscriptionDmiInEvent(); + public DmiInEvent toDmiInEvent(final List<DmiCmSubscriptionPredicate> dmiCmSubscriptionPredicates) { + final DmiInEvent dmiInEvent = new DmiInEvent(); final Data cmSubscriptionData = new Data(); - cmSubscriptionData.setPredicates(mapToDmiInEventPredicates(dmiCmNotificationSubscriptionPredicates)); - cmSubscriptionData.setCmhandles(mapToCmSubscriptionCmhandleWithPrivateProperties( - extractUniqueCmHandleIds(dmiCmNotificationSubscriptionPredicates))); - cmNotificationSubscriptionDmiInEvent.setData(cmSubscriptionData); - return cmNotificationSubscriptionDmiInEvent; + cmSubscriptionData.setPredicates(mapToDmiInEventPredicates(dmiCmSubscriptionPredicates)); + cmSubscriptionData.setCmHandles(mapToCmSubscriptionCmHandleWithPrivateProperties( + extractUniqueCmHandleIds(dmiCmSubscriptionPredicates))); + dmiInEvent.setData(cmSubscriptionData); + return dmiInEvent; } private List<Predicate> mapToDmiInEventPredicates( - final List<DmiCmNotificationSubscriptionPredicate> dmiCmNotificationSubscriptionPredicates) { + final List<DmiCmSubscriptionPredicate> dmiCmSubscriptionPredicates) { final List<Predicate> predicates = new ArrayList<>(); - dmiCmNotificationSubscriptionPredicates.forEach(dmiCmNotificationSubscriptionPredicate -> { + dmiCmSubscriptionPredicates.forEach(dmiCmNotificationSubscriptionPredicate -> { final Predicate predicate = new Predicate(); final ScopeFilter scopeFilter = new ScopeFilter(); scopeFilter.setDatastore(ScopeFilter.Datastore.fromValue( @@ -81,12 +79,12 @@ public class CmNotificationSubscriptionDmiInEventMapper { } - private List<Cmhandle> mapToCmSubscriptionCmhandleWithPrivateProperties(final Set<String> cmHandleIds) { + private List<CmHandle> mapToCmSubscriptionCmHandleWithPrivateProperties(final Set<String> cmHandleIds) { - final List<Cmhandle> cmSubscriptionCmHandles = new ArrayList<>(); + final List<CmHandle> cmSubscriptionCmHandles = new ArrayList<>(); inventoryPersistence.getYangModelCmHandles(cmHandleIds).forEach(yangModelCmHandle -> { - final Cmhandle cmhandle = new Cmhandle(); + final CmHandle cmhandle = new CmHandle(); final Map<String, String> cmhandleDmiProperties = new LinkedHashMap<>(); yangModelCmHandle.getDmiProperties() .forEach(dmiProperty -> cmhandleDmiProperties.put(dmiProperty.getName(), dmiProperty.getValue())); @@ -99,11 +97,10 @@ public class CmNotificationSubscriptionDmiInEventMapper { } - private Set<String> extractUniqueCmHandleIds( - final List<DmiCmNotificationSubscriptionPredicate> dmiCmNotificationSubscriptionPredicates) { + private Set<String> extractUniqueCmHandleIds(final List<DmiCmSubscriptionPredicate> dmiCmSubscriptionPredicates) { final Set<String> cmHandleIds = new HashSet<>(); - dmiCmNotificationSubscriptionPredicates.forEach(dmiCmNotificationSubscriptionPredicate -> cmHandleIds.addAll( + dmiCmSubscriptionPredicates.forEach(dmiCmNotificationSubscriptionPredicate -> cmHandleIds.addAll( dmiCmNotificationSubscriptionPredicate.getTargetCmHandleIds())); return cmHandleIds; } diff --git a/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/events/cmsubscription/producer/CmNotificationSubscriptionDmiInEventProducer.java b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/impl/cmnotificationsubscription/dmi/DmiInEventProducer.java index 9fbe26848f..c62916f05c 100644 --- a/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/events/cmsubscription/producer/CmNotificationSubscriptionDmiInEventProducer.java +++ b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/impl/cmnotificationsubscription/dmi/DmiInEventProducer.java @@ -18,56 +18,53 @@ * ============LICENSE_END========================================================= */ -package org.onap.cps.ncmp.api.impl.events.cmsubscription.producer; +package org.onap.cps.ncmp.impl.cmnotificationsubscription.dmi; import io.cloudevents.CloudEvent; import io.cloudevents.core.builder.CloudEventBuilder; import java.net.URI; import java.util.UUID; import lombok.RequiredArgsConstructor; -import lombok.extern.slf4j.Slf4j; import org.onap.cps.events.EventsPublisher; -import org.onap.cps.ncmp.events.cmnotificationsubscription_merge1_0_0.ncmp_to_dmi.CmNotificationSubscriptionDmiInEvent; +import org.onap.cps.ncmp.impl.cmnotificationsubscription_1_0_0.ncmp_to_dmi.DmiInEvent; import org.onap.cps.utils.JsonObjectMapper; import org.springframework.beans.factory.annotation.Value; import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; import org.springframework.stereotype.Component; @Component -@Slf4j @RequiredArgsConstructor @ConditionalOnProperty(name = "notification.enabled", havingValue = "true", matchIfMissing = true) -public class CmNotificationSubscriptionDmiInEventProducer { +public class DmiInEventProducer { private final EventsPublisher<CloudEvent> eventsPublisher; private final JsonObjectMapper jsonObjectMapper; - @Value("${app.ncmp.avc.subscription-forward-topic-prefix}") - private String cmNotificationSubscriptionDmiInEventTopic; + @Value("${app.ncmp.avc.cm-subscription-dmi-in}") + private String dmiInEventTopic; /** * Publish the event to the provided dmi plugin with key as subscription id and the event is in Cloud Event format. * - * @param subscriptionId Cm Subscription Id - * @param dmiPluginName Dmi Plugin Name - * @param eventType Type of event - * @param cmNotificationSubscriptionDmiInEvent Cm Notification Subscription event for Dmi + * @param subscriptionId Cm Subscription Id + * @param dmiPluginName Dmi Plugin Name + * @param eventType Type of event + * @param dmiInEvent Cm Notification Subscription event for Dmi */ - public void publishCmNotificationSubscriptionDmiInEvent(final String subscriptionId, final String dmiPluginName, - final String eventType, final CmNotificationSubscriptionDmiInEvent cmNotificationSubscriptionDmiInEvent) { - eventsPublisher.publishCloudEvent(cmNotificationSubscriptionDmiInEventTopic, subscriptionId, - buildAndGetCmNotificationDmiInEventAsCloudEvent(subscriptionId, dmiPluginName, eventType, - cmNotificationSubscriptionDmiInEvent)); + public void publishDmiInEvent(final String subscriptionId, final String dmiPluginName, + final String eventType, final DmiInEvent dmiInEvent) { + eventsPublisher.publishCloudEvent(dmiInEventTopic, subscriptionId, + buildAndGetDmiInEventAsCloudEvent(subscriptionId, dmiPluginName, eventType, dmiInEvent)); } - private CloudEvent buildAndGetCmNotificationDmiInEventAsCloudEvent(final String subscriptionId, - final String dmiPluginName, final String eventType, - final CmNotificationSubscriptionDmiInEvent cmNotificationSubscriptionDmiInEvent) { + private CloudEvent buildAndGetDmiInEventAsCloudEvent(final String subscriptionId, + final String dmiPluginName, final String eventType, final DmiInEvent dmiInEvent) { return CloudEventBuilder.v1().withId(UUID.randomUUID().toString()).withType(eventType) - .withSource(URI.create("NCMP")).withDataSchema(URI.create("org.onap.ncmp.dmi.cm.subscription:1.0.0")) - .withExtension("correlationid", subscriptionId.concat("#").concat(dmiPluginName)) - .withData(jsonObjectMapper.asJsonBytes(cmNotificationSubscriptionDmiInEvent)).build(); + .withSource(URI.create("NCMP")) + .withDataSchema(URI.create("org.onap.ncmp.dmi.cm.subscription:1.0.0")) + .withExtension("correlationid", subscriptionId.concat("#").concat(dmiPluginName)) + .withData(jsonObjectMapper.asJsonBytes(dmiInEvent)).build(); } diff --git a/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/impl/cmnotificationsubscription/dmi/DmiOutEventConsumer.java b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/impl/cmnotificationsubscription/dmi/DmiOutEventConsumer.java new file mode 100644 index 0000000000..20c7c7b13d --- /dev/null +++ b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/impl/cmnotificationsubscription/dmi/DmiOutEventConsumer.java @@ -0,0 +1,115 @@ +/* + * ============LICENSE_START======================================================= + * Copyright (C) 2024 Nordix Foundation + * ================================================================================ + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * SPDX-License-Identifier: Apache-2.0 + * ============LICENSE_END========================================================= + */ + +package org.onap.cps.ncmp.impl.cmnotificationsubscription.dmi; + +import static org.onap.cps.ncmp.api.NcmpResponseStatus.CM_DATA_SUBSCRIPTION_ACCEPTED; +import static org.onap.cps.ncmp.api.NcmpResponseStatus.CM_DATA_SUBSCRIPTION_REJECTED; +import static org.onap.cps.ncmp.utils.events.CloudEventMapper.toTargetEvent; + +import io.cloudevents.CloudEvent; +import java.util.Map; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.apache.kafka.clients.consumer.ConsumerRecord; +import org.onap.cps.ncmp.api.NcmpResponseStatus; +import org.onap.cps.ncmp.impl.cmnotificationsubscription.cache.DmiCacheHandler; +import org.onap.cps.ncmp.impl.cmnotificationsubscription.models.CmSubscriptionStatus; +import org.onap.cps.ncmp.impl.cmnotificationsubscription.models.DmiCmSubscriptionDetails; +import org.onap.cps.ncmp.impl.cmnotificationsubscription.ncmp.NcmpOutEventMapper; +import org.onap.cps.ncmp.impl.cmnotificationsubscription.ncmp.NcmpOutEventProducer; +import org.onap.cps.ncmp.impl.cmnotificationsubscription_1_0_0.dmi_to_ncmp.Data; +import org.onap.cps.ncmp.impl.cmnotificationsubscription_1_0_0.dmi_to_ncmp.DmiOutEvent; +import org.onap.cps.ncmp.impl.cmnotificationsubscription_1_0_0.ncmp_to_client.NcmpOutEvent; +import org.springframework.kafka.annotation.KafkaListener; +import org.springframework.stereotype.Component; + +@Component +@Slf4j +@RequiredArgsConstructor +public class DmiOutEventConsumer { + + private final DmiCacheHandler dmiCacheHandler; + private final NcmpOutEventProducer ncmpOutEventProducer; + private final NcmpOutEventMapper ncmpOutEventMapper; + + private static final String CM_SUBSCRIPTION_CORRELATION_ID_SEPARATOR = "#"; + + /** + * Consume the Cm Notification Subscription event from the dmi-plugin. + * + * @param dmiOutEventAsConsumerRecord the event to be consumed + */ + @KafkaListener(topics = "${app.ncmp.avc.cm-subscription-dmi-out}", + containerFactory = "cloudEventConcurrentKafkaListenerContainerFactory") + public void consumeDmiOutEvent(final ConsumerRecord<String, CloudEvent> dmiOutEventAsConsumerRecord) { + final CloudEvent cloudEvent = dmiOutEventAsConsumerRecord.value(); + final DmiOutEvent dmiOutEvent = toTargetEvent(cloudEvent, DmiOutEvent.class); + final String correlationId = String.valueOf(cloudEvent.getExtension("correlationid")); + if (dmiOutEvent != null && correlationId != null) { + final String eventType = cloudEvent.getType(); + handleDmiOutEvent(correlationId, eventType, dmiOutEvent); + } + } + + private void handleDmiOutEvent(final String correlationId, final String eventType, + final DmiOutEvent dmiOutEvent) { + final String subscriptionId = correlationId.split(CM_SUBSCRIPTION_CORRELATION_ID_SEPARATOR)[0]; + final String dmiPluginName = correlationId.split(CM_SUBSCRIPTION_CORRELATION_ID_SEPARATOR)[1]; + + if (checkStatusCodeAndMessage(CM_DATA_SUBSCRIPTION_ACCEPTED, dmiOutEvent.getData())) { + handleCacheStatusPerDmi(subscriptionId, dmiPluginName, CmSubscriptionStatus.ACCEPTED); + if (eventType.equals("subscriptionCreateResponse")) { + dmiCacheHandler.persistIntoDatabasePerDmi(subscriptionId, dmiPluginName); + } + if (eventType.equals("subscriptionDeleteResponse")) { + dmiCacheHandler.removeFromDatabase(subscriptionId, dmiPluginName); + } + handleEventsStatusPerDmi(subscriptionId, eventType); + } + + if (checkStatusCodeAndMessage(CM_DATA_SUBSCRIPTION_REJECTED, dmiOutEvent.getData())) { + handleCacheStatusPerDmi(subscriptionId, dmiPluginName, CmSubscriptionStatus.REJECTED); + handleEventsStatusPerDmi(subscriptionId, eventType); + } + + log.info("Cm Subscription with id : {} handled by the dmi-plugin : {} has the status : {}", subscriptionId, + dmiPluginName, dmiOutEvent.getData().getStatusMessage()); + } + + private void handleCacheStatusPerDmi(final String subscriptionId, final String dmiPluginName, + final CmSubscriptionStatus cmSubscriptionStatus) { + dmiCacheHandler.updateDmiSubscriptionStatus(subscriptionId, dmiPluginName, + cmSubscriptionStatus); + } + + private void handleEventsStatusPerDmi(final String subscriptionId, final String eventType) { + final Map<String, DmiCmSubscriptionDetails> dmiSubscriptionsPerDmi = dmiCacheHandler.get(subscriptionId); + final NcmpOutEvent ncmpOutEvent = ncmpOutEventMapper.toNcmpOutEvent(subscriptionId, dmiSubscriptionsPerDmi); + ncmpOutEventProducer.publishNcmpOutEvent(subscriptionId, eventType, ncmpOutEvent, false); + } + + private boolean checkStatusCodeAndMessage(final NcmpResponseStatus ncmpResponseStatus, + final Data dmiOutData) { + return ncmpResponseStatus.getCode().equals(dmiOutData.getStatusCode()) + && ncmpResponseStatus.getMessage() + .equals(dmiOutData.getStatusMessage()); + } +} diff --git a/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/events/cmsubscription/model/CmNotificationSubscriptionStatus.java b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/impl/cmnotificationsubscription/models/CmSubscriptionStatus.java index 68d54fac95..5b7c46ed00 100644 --- a/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/events/cmsubscription/model/CmNotificationSubscriptionStatus.java +++ b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/impl/cmnotificationsubscription/models/CmSubscriptionStatus.java @@ -18,15 +18,15 @@ * ============LICENSE_END========================================================= */ -package org.onap.cps.ncmp.api.impl.events.cmsubscription.model; +package org.onap.cps.ncmp.impl.cmnotificationsubscription.models; -public enum CmNotificationSubscriptionStatus { +public enum CmSubscriptionStatus { ACCEPTED("ACCEPTED"), REJECTED("REJECTED"), PENDING("PENDING"); private final String cmNotificationSubscriptionStatusValue; - CmNotificationSubscriptionStatus(final String cmNotificationSubscriptionStatusValue) { + CmSubscriptionStatus(final String cmNotificationSubscriptionStatusValue) { this.cmNotificationSubscriptionStatusValue = cmNotificationSubscriptionStatusValue; } } diff --git a/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/events/cmsubscription/model/DmiCmNotificationSubscriptionDetails.java b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/impl/cmnotificationsubscription/models/DmiCmSubscriptionDetails.java index 95757e7240..dbc607ad27 100644 --- a/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/events/cmsubscription/model/DmiCmNotificationSubscriptionDetails.java +++ b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/impl/cmnotificationsubscription/models/DmiCmSubscriptionDetails.java @@ -18,7 +18,7 @@ * ============LICENSE_END========================================================= */ -package org.onap.cps.ncmp.api.impl.events.cmsubscription.model; +package org.onap.cps.ncmp.impl.cmnotificationsubscription.models; import java.util.List; import lombok.AllArgsConstructor; @@ -28,8 +28,8 @@ import lombok.Setter; @Getter @Setter @AllArgsConstructor -public class DmiCmNotificationSubscriptionDetails { +public class DmiCmSubscriptionDetails { - private List<DmiCmNotificationSubscriptionPredicate> dmiCmNotificationSubscriptionPredicates; - private CmNotificationSubscriptionStatus cmNotificationSubscriptionStatus; + private List<DmiCmSubscriptionPredicate> dmiCmSubscriptionPredicates; + private CmSubscriptionStatus cmSubscriptionStatus; } diff --git a/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/impl/cmnotificationsubscription/models/DmiCmSubscriptionKey.java b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/impl/cmnotificationsubscription/models/DmiCmSubscriptionKey.java new file mode 100644 index 0000000000..edc3c566be --- /dev/null +++ b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/impl/cmnotificationsubscription/models/DmiCmSubscriptionKey.java @@ -0,0 +1,30 @@ +/* + * ============LICENSE_START======================================================= + * Copyright (C) 2024 Nordix Foundation + * ================================================================================ + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * SPDX-License-Identifier: Apache-2.0 + * ============LICENSE_END========================================================= + */ + +package org.onap.cps.ncmp.impl.cmnotificationsubscription.models; + +/** + * Key used to find the records to be sent to the DMI plugin. + * + * @param datastoreName datastore name + * @param cmHandleId cmhandle id + * @param xpath xpath + */ +public record DmiCmSubscriptionKey(String datastoreName, String cmHandleId, String xpath) { } diff --git a/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/events/cmsubscription/model/DmiCmNotificationSubscriptionPredicate.java b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/impl/cmnotificationsubscription/models/DmiCmSubscriptionPredicate.java index 9c4c3f64e3..84d3aead8c 100644 --- a/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/events/cmsubscription/model/DmiCmNotificationSubscriptionPredicate.java +++ b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/impl/cmnotificationsubscription/models/DmiCmSubscriptionPredicate.java @@ -18,18 +18,18 @@ * ============LICENSE_END========================================================= */ -package org.onap.cps.ncmp.api.impl.events.cmsubscription.model; +package org.onap.cps.ncmp.impl.cmnotificationsubscription.models; import java.util.Set; import lombok.AllArgsConstructor; import lombok.Getter; import lombok.Setter; -import org.onap.cps.ncmp.api.impl.operations.DatastoreType; +import org.onap.cps.ncmp.api.data.models.DatastoreType; @Getter @Setter @AllArgsConstructor -public class DmiCmNotificationSubscriptionPredicate { +public class DmiCmSubscriptionPredicate { private Set<String> targetCmHandleIds; private DatastoreType datastoreType; diff --git a/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/impl/cmnotificationsubscription/models/DmiCmSubscriptionTuple.java b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/impl/cmnotificationsubscription/models/DmiCmSubscriptionTuple.java new file mode 100644 index 0000000000..cd4a15af47 --- /dev/null +++ b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/impl/cmnotificationsubscription/models/DmiCmSubscriptionTuple.java @@ -0,0 +1,34 @@ +/* + * ============LICENSE_START======================================================= + * Copyright (C) 2024 Nordix Foundation + * ================================================================================ + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * SPDX-License-Identifier: Apache-2.0 + * ============LICENSE_END========================================================= + */ + +package org.onap.cps.ncmp.impl.cmnotificationsubscription.models; + +import java.util.Collection; +import java.util.Map; + +/** + * Tuple to be used during for to delete usecase. + * + * @param lastRemainingSubscriptionsPerDmi subscriptions that are used by only one subscriber grouped per dmi + * @param overlappingSubscriptionsPerDmi subscriptions that are shared by multiple subscribers grouped per dmi + */ +public record DmiCmSubscriptionTuple(Map<String, Collection<DmiCmSubscriptionKey>> lastRemainingSubscriptionsPerDmi, + Map<String, Collection<DmiCmSubscriptionKey>> overlappingSubscriptionsPerDmi) { +} diff --git a/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/impl/cmnotificationsubscription/ncmp/CmSubscriptionComparator.java b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/impl/cmnotificationsubscription/ncmp/CmSubscriptionComparator.java new file mode 100644 index 0000000000..d7f15a2c72 --- /dev/null +++ b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/impl/cmnotificationsubscription/ncmp/CmSubscriptionComparator.java @@ -0,0 +1,83 @@ +/* + * ============LICENSE_START======================================================= + * Copyright (C) 2024 Nordix Foundation + * ================================================================================ + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * SPDX-License-Identifier: Apache-2.0 + * ============LICENSE_END========================================================= + */ + +package org.onap.cps.ncmp.impl.cmnotificationsubscription.ncmp; + +import java.util.ArrayList; +import java.util.HashSet; +import java.util.List; +import java.util.Set; +import lombok.RequiredArgsConstructor; +import org.onap.cps.ncmp.api.data.models.DatastoreType; +import org.onap.cps.ncmp.impl.cmnotificationsubscription.models.DmiCmSubscriptionPredicate; +import org.onap.cps.ncmp.impl.cmnotificationsubscription.utils.CmSubscriptionPersistenceService; +import org.springframework.stereotype.Component; + +@Component +@RequiredArgsConstructor +public class CmSubscriptionComparator { + + private final CmSubscriptionPersistenceService cmSubscriptionPersistenceService; + + /** + * Get the new Dmi Predicates for a given predicates list. + * + * @param existingDmiCmSubscriptionPredicates list of DmiCmNotificationSubscriptionPredicates + * @return new list of DmiCmNotificationSubscriptionPredicates + */ + public List<DmiCmSubscriptionPredicate> getNewDmiSubscriptionPredicates( + final List<DmiCmSubscriptionPredicate> existingDmiCmSubscriptionPredicates) { + final List<DmiCmSubscriptionPredicate> newDmiCmSubscriptionPredicates = + new ArrayList<>(); + + for (final DmiCmSubscriptionPredicate dmiCmSubscriptionPredicate : existingDmiCmSubscriptionPredicates) { + + final Set<String> targetCmHandleIds = new HashSet<>(); + final Set<String> xpaths = new HashSet<>(); + final DatastoreType datastoreType = dmiCmSubscriptionPredicate.getDatastoreType(); + + for (final String cmHandleId : dmiCmSubscriptionPredicate.getTargetCmHandleIds()) { + for (final String xpath : dmiCmSubscriptionPredicate.getXpaths()) { + if (!cmSubscriptionPersistenceService.isOngoingCmSubscription(datastoreType, + cmHandleId, xpath)) { + xpaths.add(xpath); + targetCmHandleIds.add(cmHandleId); + + } + } + } + + populateValidDmiSubscriptionPredicates(targetCmHandleIds, xpaths, datastoreType, + newDmiCmSubscriptionPredicates); + } + return newDmiCmSubscriptionPredicates; + } + + private void populateValidDmiSubscriptionPredicates(final Set<String> targetCmHandleIds, + final Set<String> xpaths, final DatastoreType datastoreType, + final List<DmiCmSubscriptionPredicate> dmiCmSubscriptionPredicates) { + if (!(targetCmHandleIds.isEmpty() || xpaths.isEmpty())) { + final DmiCmSubscriptionPredicate dmiCmSubscriptionPredicate = + new DmiCmSubscriptionPredicate(targetCmHandleIds, datastoreType, xpaths); + dmiCmSubscriptionPredicates.add(dmiCmSubscriptionPredicate); + } + } + +} diff --git a/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/events/cmsubscription/service/CmNotificationSubscriptionHandlerService.java b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/impl/cmnotificationsubscription/ncmp/CmSubscriptionHandler.java index 536693ee4e..90c5c575e6 100644 --- a/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/events/cmsubscription/service/CmNotificationSubscriptionHandlerService.java +++ b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/impl/cmnotificationsubscription/ncmp/CmSubscriptionHandler.java @@ -18,18 +18,26 @@ * ============LICENSE_END========================================================= */ -package org.onap.cps.ncmp.api.impl.events.cmsubscription.service; +package org.onap.cps.ncmp.impl.cmnotificationsubscription.ncmp; -import org.onap.cps.ncmp.events.cmnotificationsubscription_merge1_0_0.client_to_ncmp.CmNotificationSubscriptionNcmpInEvent; +import java.util.List; +import org.onap.cps.ncmp.impl.cmnotificationsubscription_1_0_0.client_to_ncmp.Predicate; -public interface CmNotificationSubscriptionHandlerService { +public interface CmSubscriptionHandler { /** - * Process cm notification subscription request. + * Process cm notification subscription create request. * - * @param cmNotificationSubscriptionNcmpInEvent CM Notification Subscription event + * @param subscriptionId subscription id + * @param predicates subscription predicates */ - void processSubscriptionCreateRequest( - final CmNotificationSubscriptionNcmpInEvent cmNotificationSubscriptionNcmpInEvent); + void processSubscriptionCreateRequest(final String subscriptionId, final List<Predicate> predicates); -} + /** + * Process cm notification subscription delete request. + * + * @param subscriptionId subscription id + */ + void processSubscriptionDeleteRequest(final String subscriptionId); + +}
\ No newline at end of file diff --git a/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/impl/cmnotificationsubscription/ncmp/CmSubscriptionHandlerImpl.java b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/impl/cmnotificationsubscription/ncmp/CmSubscriptionHandlerImpl.java new file mode 100644 index 0000000000..1cdc7ed3e0 --- /dev/null +++ b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/impl/cmnotificationsubscription/ncmp/CmSubscriptionHandlerImpl.java @@ -0,0 +1,229 @@ +/* + * ============LICENSE_START======================================================= + * Copyright (C) 2024 Nordix Foundation + * ================================================================================ + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * SPDX-License-Identifier: Apache-2.0 + * ============LICENSE_END========================================================= + */ + +package org.onap.cps.ncmp.impl.cmnotificationsubscription.ncmp; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.HashMap; +import java.util.HashSet; +import java.util.List; +import java.util.Map; +import java.util.Set; +import java.util.regex.Matcher; +import java.util.regex.Pattern; +import java.util.stream.Collectors; +import lombok.RequiredArgsConstructor; +import org.onap.cps.ncmp.impl.cmnotificationsubscription.cache.DmiCacheHandler; +import org.onap.cps.ncmp.impl.cmnotificationsubscription.dmi.DmiCmSubscriptionDetailsPerDmiMapper; +import org.onap.cps.ncmp.impl.cmnotificationsubscription.dmi.DmiInEventMapper; +import org.onap.cps.ncmp.impl.cmnotificationsubscription.dmi.DmiInEventProducer; +import org.onap.cps.ncmp.impl.cmnotificationsubscription.models.CmSubscriptionStatus; +import org.onap.cps.ncmp.impl.cmnotificationsubscription.models.DmiCmSubscriptionDetails; +import org.onap.cps.ncmp.impl.cmnotificationsubscription.models.DmiCmSubscriptionKey; +import org.onap.cps.ncmp.impl.cmnotificationsubscription.models.DmiCmSubscriptionPredicate; +import org.onap.cps.ncmp.impl.cmnotificationsubscription.models.DmiCmSubscriptionTuple; +import org.onap.cps.ncmp.impl.cmnotificationsubscription.utils.CmSubscriptionPersistenceService; +import org.onap.cps.ncmp.impl.cmnotificationsubscription_1_0_0.client_to_ncmp.Predicate; +import org.onap.cps.ncmp.impl.cmnotificationsubscription_1_0_0.ncmp_to_client.NcmpOutEvent; +import org.onap.cps.ncmp.impl.cmnotificationsubscription_1_0_0.ncmp_to_dmi.DmiInEvent; +import org.onap.cps.ncmp.impl.inventory.InventoryPersistence; +import org.onap.cps.spi.model.DataNode; +import org.springframework.stereotype.Service; + +@Service +@RequiredArgsConstructor +public class CmSubscriptionHandlerImpl implements CmSubscriptionHandler { + + private static final Pattern SUBSCRIPTION_KEY_FROM_XPATH_PATTERN = Pattern.compile( + "^/datastores/datastore\\[@name='([^']*)']/cm-handles/cm-handle\\[@id='([^']*)']/" + + "filters/filter\\[@xpath='(.*)']$"); + + private final CmSubscriptionPersistenceService cmSubscriptionPersistenceService; + private final CmSubscriptionComparator cmSubscriptionComparator; + private final NcmpOutEventMapper ncmpOutEventMapper; + private final DmiInEventMapper dmiInEventMapper; + private final DmiCmSubscriptionDetailsPerDmiMapper dmiCmSubscriptionDetailsPerDmiMapper; + private final NcmpOutEventProducer ncmpOutEventProducer; + private final DmiInEventProducer dmiInEventProducer; + private final DmiCacheHandler dmiCacheHandler; + private final InventoryPersistence inventoryPersistence; + + @Override + public void processSubscriptionCreateRequest(final String subscriptionId, final List<Predicate> predicates) { + if (cmSubscriptionPersistenceService.isUniqueSubscriptionId(subscriptionId)) { + dmiCacheHandler.add(subscriptionId, predicates); + handleNewCmSubscription(subscriptionId); + scheduleNcmpOutEventResponse(subscriptionId, "subscriptionCreateResponse"); + } else { + rejectAndPublishCreateRequest(subscriptionId, predicates); + } + } + + @Override + public void processSubscriptionDeleteRequest(final String subscriptionId) { + final Collection<DataNode> subscriptionDataNodes = + cmSubscriptionPersistenceService.getAllNodesForSubscriptionId(subscriptionId); + final DmiCmSubscriptionTuple dmiCmSubscriptionTuple = + getLastRemainingAndOverlappingSubscriptionsPerDmi(subscriptionDataNodes); + dmiCacheHandler.add(subscriptionId, mergeDmiCmSubscriptionDetailsPerDmiMaps(dmiCmSubscriptionTuple)); + if (dmiCmSubscriptionTuple.lastRemainingSubscriptionsPerDmi().isEmpty()) { + acceptAndPublishDeleteRequest(subscriptionId); + } else { + sendSubscriptionDeleteRequestToDmi(subscriptionId, + dmiCmSubscriptionDetailsPerDmiMapper.toDmiCmSubscriptionsPerDmi( + dmiCmSubscriptionTuple.lastRemainingSubscriptionsPerDmi())); + scheduleNcmpOutEventResponse(subscriptionId, "subscriptionDeleteResponse"); + } + } + + private Map<String, DmiCmSubscriptionDetails> mergeDmiCmSubscriptionDetailsPerDmiMaps( + final DmiCmSubscriptionTuple dmiCmSubscriptionTuple) { + final Map<String, DmiCmSubscriptionDetails> lastRemainingDmiSubscriptionsPerDmi = + dmiCmSubscriptionDetailsPerDmiMapper.toDmiCmSubscriptionsPerDmi( + dmiCmSubscriptionTuple.lastRemainingSubscriptionsPerDmi()); + final Map<String, DmiCmSubscriptionDetails> overlappingDmiSubscriptionsPerDmi = + dmiCmSubscriptionDetailsPerDmiMapper.toDmiCmSubscriptionsPerDmi( + dmiCmSubscriptionTuple.overlappingSubscriptionsPerDmi()); + final Map<String, DmiCmSubscriptionDetails> mergedDmiSubscriptionsPerDmi = + new HashMap<>(lastRemainingDmiSubscriptionsPerDmi); + overlappingDmiSubscriptionsPerDmi.forEach((dmiServiceName, dmiCmSubscriptionDetails) -> + mergedDmiSubscriptionsPerDmi.merge(dmiServiceName, dmiCmSubscriptionDetails, + this::mergeDmiCmSubscriptionDetails)); + return mergedDmiSubscriptionsPerDmi; + } + + private DmiCmSubscriptionDetails mergeDmiCmSubscriptionDetails( + final DmiCmSubscriptionDetails dmiCmSubscriptionDetails, + final DmiCmSubscriptionDetails otherDmiCmSubscriptionDetails) { + final List<DmiCmSubscriptionPredicate> mergedDmiCmSubscriptionPredicates = + new ArrayList<>(dmiCmSubscriptionDetails.getDmiCmSubscriptionPredicates()); + mergedDmiCmSubscriptionPredicates.addAll(otherDmiCmSubscriptionDetails.getDmiCmSubscriptionPredicates()); + return new DmiCmSubscriptionDetails(mergedDmiCmSubscriptionPredicates, CmSubscriptionStatus.PENDING); + } + + private void scheduleNcmpOutEventResponse(final String subscriptionId, final String eventType) { + ncmpOutEventProducer.publishNcmpOutEvent(subscriptionId, eventType, null, true); + } + + private void rejectAndPublishCreateRequest(final String subscriptionId, final List<Predicate> predicates) { + final Set<String> subscriptionTargetFilters = + predicates.stream().flatMap(predicate -> predicate.getTargetFilter().stream()) + .collect(Collectors.toSet()); + final NcmpOutEvent ncmpOutEvent = ncmpOutEventMapper.toNcmpOutEventForRejectedRequest(subscriptionId, + new ArrayList<>(subscriptionTargetFilters)); + ncmpOutEventProducer.publishNcmpOutEvent(subscriptionId, "subscriptionCreateResponse", ncmpOutEvent, false); + } + + private void acceptAndPublishDeleteRequest(final String subscriptionId) { + final Set<String> dmiServiceNames = dmiCacheHandler.get(subscriptionId).keySet(); + for (final String dmiServiceName : dmiServiceNames) { + dmiCacheHandler.updateDmiSubscriptionStatus(subscriptionId, dmiServiceName, + CmSubscriptionStatus.ACCEPTED); + dmiCacheHandler.removeFromDatabase(subscriptionId, dmiServiceName); + } + final NcmpOutEvent ncmpOutEvent = ncmpOutEventMapper.toNcmpOutEvent(subscriptionId, + dmiCacheHandler.get(subscriptionId)); + ncmpOutEventProducer.publishNcmpOutEvent(subscriptionId, "subscriptionDeleteResponse", ncmpOutEvent, + false); + } + + private void handleNewCmSubscription(final String subscriptionId) { + final Map<String, DmiCmSubscriptionDetails> dmiSubscriptionsPerDmi = + dmiCacheHandler.get(subscriptionId); + dmiSubscriptionsPerDmi.forEach((dmiPluginName, dmiSubscriptionDetails) -> { + final List<DmiCmSubscriptionPredicate> dmiCmSubscriptionPredicates = + cmSubscriptionComparator.getNewDmiSubscriptionPredicates( + dmiSubscriptionDetails.getDmiCmSubscriptionPredicates()); + + if (dmiCmSubscriptionPredicates.isEmpty()) { + acceptAndPersistCmSubscriptionPerDmi(subscriptionId, dmiPluginName); + } else { + publishDmiInEventPerDmi(subscriptionId, dmiPluginName, dmiCmSubscriptionPredicates); + } + }); + } + + private void publishDmiInEventPerDmi(final String subscriptionId, final String dmiPluginName, + final List<DmiCmSubscriptionPredicate> dmiCmSubscriptionPredicates) { + final DmiInEvent dmiInEvent = dmiInEventMapper.toDmiInEvent(dmiCmSubscriptionPredicates); + dmiInEventProducer.publishDmiInEvent(subscriptionId, dmiPluginName, + "subscriptionCreateRequest", dmiInEvent); + } + + private void acceptAndPersistCmSubscriptionPerDmi(final String subscriptionId, final String dmiPluginName) { + dmiCacheHandler.updateDmiSubscriptionStatus(subscriptionId, dmiPluginName, + CmSubscriptionStatus.ACCEPTED); + dmiCacheHandler.persistIntoDatabasePerDmi(subscriptionId, dmiPluginName); + } + + private void sendSubscriptionDeleteRequestToDmi(final String subscriptionId, + final Map<String, DmiCmSubscriptionDetails> + dmiCmSubscriptionsPerDmi) { + dmiCmSubscriptionsPerDmi.forEach((dmiPluginName, dmiCmSubscriptionDetails) -> { + final DmiInEvent dmiInEvent = + dmiInEventMapper.toDmiInEvent( + dmiCmSubscriptionDetails.getDmiCmSubscriptionPredicates()); + dmiInEventProducer.publishDmiInEvent(subscriptionId, + dmiPluginName, "subscriptionDeleteRequest", dmiInEvent); + }); + } + + + private DmiCmSubscriptionTuple getLastRemainingAndOverlappingSubscriptionsPerDmi( + final Collection<DataNode> subscriptionNodes) { + final Map<String, Collection<DmiCmSubscriptionKey>> lastRemainingSubscriptionsPerDmi = new HashMap<>(); + final Map<String, Collection<DmiCmSubscriptionKey>> overlappingSubscriptionsPerDmi = new HashMap<>(); + + for (final DataNode subscriptionNode : subscriptionNodes) { + final DmiCmSubscriptionKey dmiCmSubscriptionKey = extractCmSubscriptionKey(subscriptionNode.getXpath()); + final String dmiServiceName = inventoryPersistence.getYangModelCmHandle( + dmiCmSubscriptionKey.cmHandleId()).getDmiServiceName(); + final List<String> subscribers = (List<String>) subscriptionNode.getLeaves().get("subscriptionIds"); + populateDmiCmSubscriptionTuple(subscribers, overlappingSubscriptionsPerDmi, + lastRemainingSubscriptionsPerDmi, dmiServiceName, dmiCmSubscriptionKey); + } + return new DmiCmSubscriptionTuple(lastRemainingSubscriptionsPerDmi, overlappingSubscriptionsPerDmi); + } + + private static void populateDmiCmSubscriptionTuple(final List<String> subscribers, + final Map<String, Collection<DmiCmSubscriptionKey>> + overlappingSubscriptionsPerDmi, + final Map<String, Collection<DmiCmSubscriptionKey>> + lastRemainingSubscriptionsPerDmi, + final String dmiServiceName, + final DmiCmSubscriptionKey dmiCmSubscriptionKey) { + final Map<String, Collection<DmiCmSubscriptionKey>> targetMap = + subscribers.size() > 1 ? overlappingSubscriptionsPerDmi : lastRemainingSubscriptionsPerDmi; + targetMap.computeIfAbsent(dmiServiceName, dmiName -> new HashSet<>()).add(dmiCmSubscriptionKey); + } + + private DmiCmSubscriptionKey extractCmSubscriptionKey(final String xpath) { + final Matcher matcher = SUBSCRIPTION_KEY_FROM_XPATH_PATTERN.matcher(xpath); + if (matcher.find()) { + final String datastoreName = matcher.group(1); + final String cmHandleId = matcher.group(2); + final String filterXpath = matcher.group(3); + return new DmiCmSubscriptionKey(datastoreName, cmHandleId, filterXpath); + } + throw new IllegalArgumentException("DataNode xpath does not represent a subscription key"); + } + +}
\ No newline at end of file diff --git a/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/events/cmsubscription/consumer/CmNotificationSubscriptionNcmpInEventConsumer.java b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/impl/cmnotificationsubscription/ncmp/NcmpInEventConsumer.java index 70135b3079..cba64e0e94 100644 --- a/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/events/cmsubscription/consumer/CmNotificationSubscriptionNcmpInEventConsumer.java +++ b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/impl/cmnotificationsubscription/ncmp/NcmpInEventConsumer.java @@ -18,49 +18,52 @@ * ============LICENSE_END========================================================= */ -package org.onap.cps.ncmp.api.impl.events.cmsubscription.consumer; +package org.onap.cps.ncmp.impl.cmnotificationsubscription.ncmp; -import static org.onap.cps.ncmp.api.impl.events.mapper.CloudEventMapper.toTargetEvent; +import static org.onap.cps.ncmp.utils.events.CloudEventMapper.toTargetEvent; import io.cloudevents.CloudEvent; +import java.util.List; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; import org.apache.kafka.clients.consumer.ConsumerRecord; -import org.onap.cps.ncmp.api.impl.events.cmsubscription.service.CmNotificationSubscriptionHandlerService; -import org.onap.cps.ncmp.events.cmnotificationsubscription_merge1_0_0.client_to_ncmp.CmNotificationSubscriptionNcmpInEvent; -import org.springframework.beans.factory.annotation.Value; +import org.onap.cps.ncmp.impl.cmnotificationsubscription_1_0_0.client_to_ncmp.NcmpInEvent; +import org.onap.cps.ncmp.impl.cmnotificationsubscription_1_0_0.client_to_ncmp.Predicate; import org.springframework.kafka.annotation.KafkaListener; import org.springframework.stereotype.Component; @Component @Slf4j @RequiredArgsConstructor -public class CmNotificationSubscriptionNcmpInEventConsumer { +public class NcmpInEventConsumer { - private final CmNotificationSubscriptionHandlerService cmNotificationSubscriptionHandlerService; - - @Value("${notification.enabled:true}") - private boolean notificationFeatureEnabled; + private final CmSubscriptionHandler cmSubscriptionHandler; /** * Consume the specified event. * - * @param subscriptionEventConsumerRecord the event to be consumed + * @param ncmpInEventAsConsumerRecord the event to be consumed */ - @KafkaListener(topics = "${app.ncmp.avc.subscription-topic}", + @KafkaListener(topics = "${app.ncmp.avc.cm-subscription-ncmp-in}", containerFactory = "cloudEventConcurrentKafkaListenerContainerFactory") - public void consumeSubscriptionEvent(final ConsumerRecord<String, CloudEvent> subscriptionEventConsumerRecord) { - final CloudEvent cloudEvent = subscriptionEventConsumerRecord.value(); - final CmNotificationSubscriptionNcmpInEvent cmNotificationSubscriptionNcmpInEvent = - toTargetEvent(cloudEvent, CmNotificationSubscriptionNcmpInEvent.class); + public void consumeSubscriptionEvent(final ConsumerRecord<String, CloudEvent> ncmpInEventAsConsumerRecord) { + final CloudEvent cloudEvent = ncmpInEventAsConsumerRecord.value(); + final NcmpInEvent ncmpInEvent = + toTargetEvent(cloudEvent, NcmpInEvent.class); log.info("Subscription with name {} to be mapped to hazelcast object...", - cmNotificationSubscriptionNcmpInEvent.getData().getSubscriptionId()); + ncmpInEvent.getData().getSubscriptionId()); - final String subscriptionId = cmNotificationSubscriptionNcmpInEvent.getData().getSubscriptionId(); + final String subscriptionId = ncmpInEvent.getData().getSubscriptionId(); + final List<Predicate> predicates = ncmpInEvent.getData().getPredicates(); if ("subscriptionCreateRequest".equals(cloudEvent.getType())) { - log.info("Subscription for source {} with subscription id {} ...", cloudEvent.getSource(), subscriptionId); - cmNotificationSubscriptionHandlerService.processSubscriptionCreateRequest( - cmNotificationSubscriptionNcmpInEvent); + log.info("Subscription create request for source {} with subscription id {} ...", + cloudEvent.getSource(), subscriptionId); + cmSubscriptionHandler.processSubscriptionCreateRequest(subscriptionId, predicates); + } + if ("subscriptionDeleteRequest".equals(cloudEvent.getType())) { + log.info("Subscription delete request for source {} with subscription id {} ...", + cloudEvent.getSource(), subscriptionId); + cmSubscriptionHandler.processSubscriptionDeleteRequest(subscriptionId); } } -}
\ No newline at end of file +} diff --git a/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/impl/cmnotificationsubscription/ncmp/NcmpOutEventMapper.java b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/impl/cmnotificationsubscription/ncmp/NcmpOutEventMapper.java new file mode 100644 index 0000000000..afff9d1298 --- /dev/null +++ b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/impl/cmnotificationsubscription/ncmp/NcmpOutEventMapper.java @@ -0,0 +1,114 @@ +/* + * ============LICENSE_START======================================================= + * Copyright (C) 2024 Nordix Foundation + * ================================================================================ + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * SPDX-License-Identifier: Apache-2.0 + * ============LICENSE_END========================================================= + */ + +package org.onap.cps.ncmp.impl.cmnotificationsubscription.ncmp; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.HashSet; +import java.util.List; +import java.util.Map; +import lombok.RequiredArgsConstructor; +import org.onap.cps.ncmp.impl.cmnotificationsubscription.models.CmSubscriptionStatus; +import org.onap.cps.ncmp.impl.cmnotificationsubscription.models.DmiCmSubscriptionDetails; +import org.onap.cps.ncmp.impl.cmnotificationsubscription.models.DmiCmSubscriptionPredicate; +import org.onap.cps.ncmp.impl.cmnotificationsubscription_1_0_0.ncmp_to_client.Data; +import org.onap.cps.ncmp.impl.cmnotificationsubscription_1_0_0.ncmp_to_client.NcmpOutEvent; +import org.springframework.stereotype.Component; + +@Component +@RequiredArgsConstructor +public class NcmpOutEventMapper { + + /** + * Mapper to form a response for the client for the Cm Notification Subscription. + * + * @param subscriptionId Cm Notification Subscription Id + * @param dmiSubscriptionsPerDmi contains CmNotificationSubscriptionDetails per dmi plugin + * @return CmNotificationSubscriptionNcmpOutEvent to sent back to the client + */ + public NcmpOutEvent toNcmpOutEvent(final String subscriptionId, + final Map<String, DmiCmSubscriptionDetails> dmiSubscriptionsPerDmi) { + + final NcmpOutEvent ncmpOutEvent = new NcmpOutEvent(); + final Data cmSubscriptionData = new Data(); + cmSubscriptionData.setSubscriptionId(subscriptionId); + populateNcmpOutEventWithCmHandleIds(dmiSubscriptionsPerDmi, + cmSubscriptionData); + ncmpOutEvent.setData(cmSubscriptionData); + + return ncmpOutEvent; + } + + /** + * Mapper to form a rejected response for the client for the Cm Notification Subscription Request. + * + * @param subscriptionId subscription id + * @param rejectedTargetFilters list of rejected target filters for the subscription request + * @return to sent back to the client + */ + public NcmpOutEvent toNcmpOutEventForRejectedRequest(final String subscriptionId, + final List<String> rejectedTargetFilters) { + final NcmpOutEvent ncmpOutEvent = new NcmpOutEvent(); + final Data cmSubscriptionData = new Data(); + cmSubscriptionData.setSubscriptionId(subscriptionId); + cmSubscriptionData.setRejectedTargets(rejectedTargetFilters); + ncmpOutEvent.setData(cmSubscriptionData); + return ncmpOutEvent; + } + + private void populateNcmpOutEventWithCmHandleIds( + final Map<String, DmiCmSubscriptionDetails> dmiSubscriptionsPerDmi, + final Data cmSubscriptionData) { + + final Collection<String> acceptedCmHandleIds = new HashSet<>(); + final Collection<String> pendingCmHandleIds = new HashSet<>(); + final Collection<String> rejectedCmHandleIds = new HashSet<>(); + + dmiSubscriptionsPerDmi.forEach((dmiPluginName, dmiSubscriptionDetails) -> { + final CmSubscriptionStatus cmSubscriptionStatus = + dmiSubscriptionDetails.getCmSubscriptionStatus(); + final List<DmiCmSubscriptionPredicate> dmiCmSubscriptionPredicates = + dmiSubscriptionDetails.getDmiCmSubscriptionPredicates(); + + switch (cmSubscriptionStatus) { + case ACCEPTED -> acceptedCmHandleIds.addAll( + extractCmHandleIds(dmiCmSubscriptionPredicates)); + case PENDING -> pendingCmHandleIds.addAll(extractCmHandleIds(dmiCmSubscriptionPredicates)); + default -> rejectedCmHandleIds.addAll(extractCmHandleIds(dmiCmSubscriptionPredicates)); + } + }); + + cmSubscriptionData.setAcceptedTargets(acceptedCmHandleIds); + cmSubscriptionData.setPendingTargets(pendingCmHandleIds); + cmSubscriptionData.setRejectedTargets(rejectedCmHandleIds); + + } + + private List<String> extractCmHandleIds( + final List<DmiCmSubscriptionPredicate> dmiCmSubscriptionPredicates) { + final List<String> cmHandleIds = new ArrayList<>(); + dmiCmSubscriptionPredicates.forEach(dmiSubscriptionPredicate -> cmHandleIds.addAll( + dmiSubscriptionPredicate.getTargetCmHandleIds())); + + return cmHandleIds; + } + +} diff --git a/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/impl/cmnotificationsubscription/ncmp/NcmpOutEventProducer.java b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/impl/cmnotificationsubscription/ncmp/NcmpOutEventProducer.java new file mode 100644 index 0000000000..3371d59f7a --- /dev/null +++ b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/impl/cmnotificationsubscription/ncmp/NcmpOutEventProducer.java @@ -0,0 +1,139 @@ +/* + * ============LICENSE_START======================================================= + * Copyright (C) 2024 Nordix Foundation + * ================================================================================ + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * SPDX-License-Identifier: Apache-2.0 + * ============LICENSE_END========================================================= + */ + +package org.onap.cps.ncmp.impl.cmnotificationsubscription.ncmp; + +import io.cloudevents.CloudEvent; +import io.cloudevents.core.builder.CloudEventBuilder; +import java.net.URI; +import java.util.Map; +import java.util.UUID; +import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.Executors; +import java.util.concurrent.ScheduledExecutorService; +import java.util.concurrent.ScheduledFuture; +import java.util.concurrent.TimeUnit; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.onap.cps.events.EventsPublisher; +import org.onap.cps.ncmp.impl.cmnotificationsubscription.cache.DmiCacheHandler; +import org.onap.cps.ncmp.impl.cmnotificationsubscription_1_0_0.ncmp_to_client.NcmpOutEvent; +import org.onap.cps.utils.JsonObjectMapper; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; +import org.springframework.stereotype.Component; + +@Component +@Slf4j +@RequiredArgsConstructor +@ConditionalOnProperty(name = "notification.enabled", havingValue = "true", matchIfMissing = true) +public class NcmpOutEventProducer { + + @Value("${app.ncmp.avc.cm-subscription-ncmp-out}") + private String ncmpOutEventTopic; + + @Value("${ncmp.timers.subscription-forwarding.dmi-response-timeout-ms}") + private Integer dmiOutEventTimeoutInMs; + + private final EventsPublisher<CloudEvent> eventsPublisher; + private final JsonObjectMapper jsonObjectMapper; + private final NcmpOutEventMapper ncmpOutEventMapper; + private final DmiCacheHandler dmiCacheHandler; + private final ScheduledExecutorService scheduledExecutorService = Executors.newSingleThreadScheduledExecutor(); + private static final Map<String, ScheduledFuture<?>> scheduledTasksPerSubscriptionIdAndEventType = + new ConcurrentHashMap<>(); + + /** + * Publish the event to the client who requested the subscription with key as subscription id and event is Cloud + * Event compliant. + * + * @param subscriptionId Cm Subscription Id + * @param eventType Type of event + * @param ncmpOutEvent Cm Notification Subscription Event for the + * client + * @param isScheduledEvent Determines if the event is to be scheduled + * or published now + */ + public void publishNcmpOutEvent(final String subscriptionId, final String eventType, + final NcmpOutEvent ncmpOutEvent, final boolean isScheduledEvent) { + + final String taskKey = subscriptionId.concat(eventType); + + if (isScheduledEvent && !scheduledTasksPerSubscriptionIdAndEventType.containsKey(taskKey)) { + final ScheduledFuture<?> scheduledFuture = scheduleAndPublishNcmpOutEvent(subscriptionId, eventType); + scheduledTasksPerSubscriptionIdAndEventType.putIfAbsent(taskKey, scheduledFuture); + log.debug("Scheduled the Cm Subscription Event for subscriptionId : {} and eventType : {}", subscriptionId, + eventType); + } else { + cancelScheduledTask(taskKey); + if (ncmpOutEvent != null) { + publishNcmpOutEventNow(subscriptionId, eventType, ncmpOutEvent); + log.debug("Published Cm Subscription Event on demand for subscriptionId : {} and eventType : {}", + subscriptionId, eventType); + } + } + } + + private ScheduledFuture<?> scheduleAndPublishNcmpOutEvent(final String subscriptionId, final String eventType) { + final NcmpOutEventPublishingTask ncmpOutEventPublishingTask = + new NcmpOutEventPublishingTask(ncmpOutEventTopic, subscriptionId, eventType, eventsPublisher, + jsonObjectMapper, ncmpOutEventMapper, dmiCacheHandler); + return scheduledExecutorService.schedule(ncmpOutEventPublishingTask, dmiOutEventTimeoutInMs, + TimeUnit.MILLISECONDS); + } + + private void cancelScheduledTask(final String taskKey) { + + final ScheduledFuture<?> scheduledFuture = scheduledTasksPerSubscriptionIdAndEventType.get(taskKey); + if (scheduledFuture != null) { + scheduledFuture.cancel(true); + scheduledTasksPerSubscriptionIdAndEventType.remove(taskKey); + } + + } + + + private void publishNcmpOutEventNow(final String subscriptionId, final String eventType, + final NcmpOutEvent ncmpOutEvent) { + final CloudEvent ncmpOutEventAsCloudEvent = + buildAndGetNcmpOutEventAsCloudEvent(jsonObjectMapper, subscriptionId, eventType, ncmpOutEvent); + eventsPublisher.publishCloudEvent(ncmpOutEventTopic, subscriptionId, ncmpOutEventAsCloudEvent); + dmiCacheHandler.removeAcceptedAndRejectedDmiSubscriptionEntries(subscriptionId); + } + + /** + * Get an NCMP out event as cloud event. + * + * @param jsonObjectMapper JSON object mapper + * @param subscriptionId subscription id + * @param eventType event type + * @param ncmpOutEvent cm notification subscription NCMP out event + * @return cm notification subscription NCMP out event as cloud event + */ + public static CloudEvent buildAndGetNcmpOutEventAsCloudEvent(final JsonObjectMapper jsonObjectMapper, + final String subscriptionId, final String eventType, final NcmpOutEvent ncmpOutEvent) { + + return CloudEventBuilder.v1().withId(UUID.randomUUID().toString()).withType(eventType) + .withSource(URI.create("NCMP")).withDataSchema(URI.create("org.onap.ncmp.cm.subscription:1.0.0")) + .withExtension("correlationid", subscriptionId) + .withData(jsonObjectMapper.asJsonBytes(ncmpOutEvent)).build(); + } + +} diff --git a/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/impl/cmnotificationsubscription/ncmp/NcmpOutEventPublishingTask.java b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/impl/cmnotificationsubscription/ncmp/NcmpOutEventPublishingTask.java new file mode 100644 index 0000000000..f8f253d275 --- /dev/null +++ b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/impl/cmnotificationsubscription/ncmp/NcmpOutEventPublishingTask.java @@ -0,0 +1,62 @@ +/* + * ============LICENSE_START======================================================= + * Copyright (C) 2024 Nordix Foundation + * ================================================================================ + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * SPDX-License-Identifier: Apache-2.0 + * ============LICENSE_END========================================================= + */ + +package org.onap.cps.ncmp.impl.cmnotificationsubscription.ncmp; + +import static org.onap.cps.ncmp.impl.cmnotificationsubscription.ncmp.NcmpOutEventProducer.buildAndGetNcmpOutEventAsCloudEvent; + +import io.cloudevents.CloudEvent; +import java.util.Map; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.onap.cps.events.EventsPublisher; +import org.onap.cps.ncmp.impl.cmnotificationsubscription.cache.DmiCacheHandler; +import org.onap.cps.ncmp.impl.cmnotificationsubscription.models.DmiCmSubscriptionDetails; +import org.onap.cps.ncmp.impl.cmnotificationsubscription_1_0_0.ncmp_to_client.NcmpOutEvent; +import org.onap.cps.utils.JsonObjectMapper; + +@Slf4j +@RequiredArgsConstructor +public class NcmpOutEventPublishingTask implements Runnable { + + private final String topicName; + private final String subscriptionId; + private final String eventType; + private final EventsPublisher<CloudEvent> eventsPublisher; + private final JsonObjectMapper jsonObjectMapper; + private final NcmpOutEventMapper ncmpOutEventMapper; + private final DmiCacheHandler dmiCacheHandler; + + /** + * Delegating the responsibility of publishing NcmpOutEvent as a separate task which will + * be called after a specified delay. + */ + @Override + public void run() { + final Map<String, DmiCmSubscriptionDetails> dmiSubscriptionsPerDmi = + dmiCacheHandler.get(subscriptionId); + final NcmpOutEvent ncmpOutEvent = ncmpOutEventMapper.toNcmpOutEvent(subscriptionId, + dmiSubscriptionsPerDmi); + eventsPublisher.publishCloudEvent(topicName, subscriptionId, + buildAndGetNcmpOutEventAsCloudEvent(jsonObjectMapper, subscriptionId, eventType, + ncmpOutEvent)); + dmiCacheHandler.removeAcceptedAndRejectedDmiSubscriptionEntries(subscriptionId); + } +} diff --git a/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/events/cmsubscription/service/CmNotificationSubscriptionPersistenceServiceImpl.java b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/impl/cmnotificationsubscription/utils/CmSubscriptionPersistenceService.java index 0adf225fe4..6b5ed908b8 100644 --- a/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/events/cmsubscription/service/CmNotificationSubscriptionPersistenceServiceImpl.java +++ b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/impl/cmnotificationsubscription/utils/CmSubscriptionPersistenceService.java @@ -1,6 +1,7 @@ /* * ============LICENSE_START======================================================= * Copyright (C) 2024 Nordix Foundation + * Modifications Copyright (C) 2024 TechMahindra Ltd. * ================================================================================ * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -18,8 +19,9 @@ * ============LICENSE_END========================================================= */ -package org.onap.cps.ncmp.api.impl.events.cmsubscription.service; +package org.onap.cps.ncmp.impl.cmnotificationsubscription.utils; +import static org.onap.cps.spi.FetchDescendantsOption.DIRECT_CHILDREN_ONLY; import static org.onap.cps.spi.FetchDescendantsOption.OMIT_DESCENDANTS; import java.io.Serializable; @@ -32,7 +34,7 @@ import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; import org.onap.cps.api.CpsDataService; import org.onap.cps.api.CpsQueryService; -import org.onap.cps.ncmp.api.impl.operations.DatastoreType; +import org.onap.cps.ncmp.api.data.models.DatastoreType; import org.onap.cps.spi.model.DataNode; import org.onap.cps.utils.ContentType; import org.onap.cps.utils.JsonObjectMapper; @@ -41,14 +43,21 @@ import org.springframework.stereotype.Service; @Slf4j @Service @RequiredArgsConstructor -public class CmNotificationSubscriptionPersistenceServiceImpl implements CmNotificationSubscriptionPersistenceService { +public class CmSubscriptionPersistenceService { + + private static final String NCMP_DATASPACE_NAME = "NCMP-Admin"; + private static final String CM_SUBSCRIPTIONS_ANCHOR_NAME = "cm-data-subscriptions"; private static final String SUBSCRIPTION_ANCHOR_NAME = "cm-data-subscriptions"; private static final String CPS_PATH_QUERY_FOR_CM_SUBSCRIPTION_WITH_DATASTORE_AND_CMHANDLE = """ - /datastores/datastore[@name='%s']/cm-handles/cm-handle[@id='%s']/filters + /datastores/datastore[@name='%s']/cm-handles/cm-handle[@id='%s'] """.trim(); + private static final String CPS_PATH_QUERY_FOR_CM_SUBSCRIPTION_FILTERS_WITH_DATASTORE_AND_CMHANDLE = + CPS_PATH_QUERY_FOR_CM_SUBSCRIPTION_WITH_DATASTORE_AND_CMHANDLE + "/filters"; + private static final String CPS_PATH_QUERY_FOR_CM_SUBSCRIPTION_WITH_DATASTORE_CMHANDLE_AND_XPATH = - CPS_PATH_QUERY_FOR_CM_SUBSCRIPTION_WITH_DATASTORE_AND_CMHANDLE + "/filter[@xpath='%s']"; + CPS_PATH_QUERY_FOR_CM_SUBSCRIPTION_FILTERS_WITH_DATASTORE_AND_CMHANDLE + "/filter[@xpath='%s']"; + private static final String CPS_PATH_QUERY_FOR_CM_SUBSCRIPTION_WITH_ID = """ //filter/subscriptionIds[text()='%s'] @@ -58,22 +67,40 @@ public class CmNotificationSubscriptionPersistenceServiceImpl implements CmNotif private final CpsQueryService cpsQueryService; private final CpsDataService cpsDataService; - @Override - public boolean isOngoingCmNotificationSubscription(final DatastoreType datastoreType, final String cmHandleId, - final String xpath) { - return !getOngoingCmNotificationSubscriptionIds(datastoreType, cmHandleId, xpath).isEmpty(); + /** + * Check if we have an ongoing cm subscription based on the parameters. + * + * @param datastoreType the susbcription target datastore type + * @param cmHandleId the id of the cm handle for the susbcription + * @param xpath the target xpath + * @return true for ongoing cmsubscription , otherwise false + */ + public boolean isOngoingCmSubscription(final DatastoreType datastoreType, final String cmHandleId, + final String xpath) { + return !getOngoingCmSubscriptionIds(datastoreType, cmHandleId, xpath).isEmpty(); } - @Override + /** + * Check if the subscription ID is unique against ongoing subscriptions. + * + * @param subscriptionId subscription ID + * @return true if subscriptionId is not used in active subscriptions, otherwise false + */ public boolean isUniqueSubscriptionId(final String subscriptionId) { return cpsQueryService.queryDataNodes(NCMP_DATASPACE_NAME, SUBSCRIPTION_ANCHOR_NAME, - CPS_PATH_QUERY_FOR_CM_SUBSCRIPTION_WITH_ID.formatted(subscriptionId), - OMIT_DESCENDANTS).isEmpty(); + CPS_PATH_QUERY_FOR_CM_SUBSCRIPTION_WITH_ID.formatted(subscriptionId), OMIT_DESCENDANTS).isEmpty(); } - @Override - public Collection<String> getOngoingCmNotificationSubscriptionIds(final DatastoreType datastoreType, - final String cmHandleId, final String xpath) { + /** + * Get all ongoing cm notification subscription based on the parameters. + * + * @param datastoreType the susbcription target datastore type + * @param cmHandleId the id of the cm handle for the susbcription + * @param xpath the target xpath + * @return collection of subscription ids of ongoing cm notification subscription + */ + public Collection<String> getOngoingCmSubscriptionIds(final DatastoreType datastoreType, + final String cmHandleId, final String xpath) { final String isOngoingCmSubscriptionCpsPathQuery = CPS_PATH_QUERY_FOR_CM_SUBSCRIPTION_WITH_DATASTORE_CMHANDLE_AND_XPATH.formatted( @@ -87,31 +114,44 @@ public class CmNotificationSubscriptionPersistenceServiceImpl implements CmNotif return (List<String>) existingNodes.iterator().next().getLeaves().get("subscriptionIds"); } - @Override - public void addCmNotificationSubscription(final DatastoreType datastoreType, final String cmHandleId, - final String xpath, final String subscriptionId) { - final Collection<String> subscriptionIds = getOngoingCmNotificationSubscriptionIds(datastoreType, - cmHandleId, xpath); + /** + * Add cm notification subscription. + * + * @param datastoreType the susbcription target datastore type + * @param cmHandleId the id of the cm handle for the susbcription + * @param xpath the target xpath + * @param newSubscriptionId subscription id to be added + */ + public void addCmSubscription(final DatastoreType datastoreType, final String cmHandleId, + final String xpath, final String newSubscriptionId) { + final Collection<String> subscriptionIds = + getOngoingCmSubscriptionIds(datastoreType, cmHandleId, xpath); if (subscriptionIds.isEmpty()) { - addFirstSubscriptionForDatastoreCmHandleAndXpath(datastoreType, cmHandleId, xpath, subscriptionId); - } else if (!subscriptionIds.contains(subscriptionId)) { - subscriptionIds.add(subscriptionId); + addFirstSubscriptionForDatastoreCmHandleAndXpath(datastoreType, cmHandleId, xpath, newSubscriptionId); + } else if (!subscriptionIds.contains(newSubscriptionId)) { + subscriptionIds.add(newSubscriptionId); saveSubscriptionDetails(datastoreType, cmHandleId, xpath, subscriptionIds); } } - @Override - public void removeCmNotificationSubscription(final DatastoreType datastoreType, final String cmHandleId, - final String xpath, final String subscriptionId) { - final Collection<String> subscriptionIds = getOngoingCmNotificationSubscriptionIds(datastoreType, - cmHandleId, xpath); + /** + * Remove cm notification Subscription. + * + * @param datastoreType the susbcription target datastore type + * @param cmHandleId the id of the cm handle for the susbcription + * @param xpath the target xpath + * @param subscriptionId subscription id to remove + */ + public void removeCmSubscription(final DatastoreType datastoreType, final String cmHandleId, + final String xpath, final String subscriptionId) { + final Collection<String> subscriptionIds = + getOngoingCmSubscriptionIds(datastoreType, cmHandleId, xpath); if (subscriptionIds.remove(subscriptionId)) { - if (isOngoingCmNotificationSubscription(datastoreType, cmHandleId, xpath)) { - saveSubscriptionDetails(datastoreType, cmHandleId, xpath, subscriptionIds); - log.info("There are subscribers left for the following cps path {} :", - CPS_PATH_QUERY_FOR_CM_SUBSCRIPTION_WITH_DATASTORE_CMHANDLE_AND_XPATH.formatted( - datastoreType.getDatastoreName(), cmHandleId, escapeQuotesByDoublingThem(xpath))); - } else { + saveSubscriptionDetails(datastoreType, cmHandleId, xpath, subscriptionIds); + log.info("There are subscribers left for the following cps path {} :", + CPS_PATH_QUERY_FOR_CM_SUBSCRIPTION_WITH_DATASTORE_CMHANDLE_AND_XPATH.formatted( + datastoreType.getDatastoreName(), cmHandleId, escapeQuotesByDoublingThem(xpath))); + if (subscriptionIds.isEmpty()) { log.info("No subscribers left for the following cps path {} :", CPS_PATH_QUERY_FOR_CM_SUBSCRIPTION_WITH_DATASTORE_CMHANDLE_AND_XPATH.formatted( datastoreType.getDatastoreName(), cmHandleId, escapeQuotesByDoublingThem(xpath))); @@ -120,49 +160,74 @@ public class CmNotificationSubscriptionPersistenceServiceImpl implements CmNotif } } + /** + * Retrieve all existing dataNodes for given subscription id. + * + * @param subscriptionId subscription id + * @return collection of DataNodes + */ + public Collection<DataNode> getAllNodesForSubscriptionId(final String subscriptionId) { + return cpsQueryService.queryDataNodes(NCMP_DATASPACE_NAME, SUBSCRIPTION_ANCHOR_NAME, + CPS_PATH_QUERY_FOR_CM_SUBSCRIPTION_WITH_ID.formatted(subscriptionId), + OMIT_DESCENDANTS); + } + private void deleteListOfSubscriptionsFor(final DatastoreType datastoreType, final String cmHandleId, - final String xpath) { + final String xpath) { cpsDataService.deleteDataNode(NCMP_DATASPACE_NAME, SUBSCRIPTION_ANCHOR_NAME, CPS_PATH_QUERY_FOR_CM_SUBSCRIPTION_WITH_DATASTORE_CMHANDLE_AND_XPATH.formatted( datastoreType.getDatastoreName(), cmHandleId, escapeQuotesByDoublingThem(xpath)), OffsetDateTime.now()); + final Collection<DataNode> existingFiltersForCmHandle = + cpsQueryService.queryDataNodes(NCMP_DATASPACE_NAME, CM_SUBSCRIPTIONS_ANCHOR_NAME, + CPS_PATH_QUERY_FOR_CM_SUBSCRIPTION_FILTERS_WITH_DATASTORE_AND_CMHANDLE.formatted( + datastoreType.getDatastoreName(), cmHandleId), + DIRECT_CHILDREN_ONLY).iterator().next() + .getChildDataNodes(); + if (existingFiltersForCmHandle.isEmpty()) { + removeCmHandleFromDatastore(datastoreType.getDatastoreName(), cmHandleId); + } + } + + private void removeCmHandleFromDatastore(final String datastoreName, final String cmHandleId) { + cpsDataService.deleteDataNode(NCMP_DATASPACE_NAME, SUBSCRIPTION_ANCHOR_NAME, + CPS_PATH_QUERY_FOR_CM_SUBSCRIPTION_WITH_DATASTORE_AND_CMHANDLE.formatted(datastoreName, cmHandleId), + OffsetDateTime.now()); } private boolean isFirstSubscriptionForCmHandle(final DatastoreType datastoreType, final String cmHandleId) { return cpsQueryService.queryDataNodes(NCMP_DATASPACE_NAME, CM_SUBSCRIPTIONS_ANCHOR_NAME, - CPS_PATH_QUERY_FOR_CM_SUBSCRIPTION_WITH_DATASTORE_AND_CMHANDLE.formatted( - datastoreType.getDatastoreName(), cmHandleId), - OMIT_DESCENDANTS).isEmpty(); + CPS_PATH_QUERY_FOR_CM_SUBSCRIPTION_FILTERS_WITH_DATASTORE_AND_CMHANDLE.formatted( + datastoreType.getDatastoreName(), cmHandleId), OMIT_DESCENDANTS).isEmpty(); } private void addFirstSubscriptionForDatastoreCmHandleAndXpath(final DatastoreType datastoreType, - final String cmHandleId, - final String xpath, - final String subscriptionId) { + final String cmHandleId, final String xpath, final String subscriptionId) { final Collection<String> newSubscriptionList = Collections.singletonList(subscriptionId); final String subscriptionDetailsAsJson = getSubscriptionDetailsAsJson(xpath, newSubscriptionList); if (isFirstSubscriptionForCmHandle(datastoreType, cmHandleId)) { - final String parentXpath = "/datastores/datastore[@name='%s']/cm-handles" - .formatted(datastoreType.getDatastoreName()); - final String subscriptionAsJson = String.format("{\"cm-handle\":[{\"id\":\"%s\",\"filters\":%s}]}", - cmHandleId, subscriptionDetailsAsJson); + final String parentXpath = + "/datastores/datastore[@name='%s']/cm-handles".formatted(datastoreType.getDatastoreName()); + final String subscriptionAsJson = + String.format("{\"cm-handle\":[{\"id\":\"%s\",\"filters\":%s}]}", cmHandleId, + subscriptionDetailsAsJson); cpsDataService.saveData(NCMP_DATASPACE_NAME, SUBSCRIPTION_ANCHOR_NAME, parentXpath, subscriptionAsJson, OffsetDateTime.now(), ContentType.JSON); } else { cpsDataService.saveListElements(NCMP_DATASPACE_NAME, CM_SUBSCRIPTIONS_ANCHOR_NAME, - CPS_PATH_QUERY_FOR_CM_SUBSCRIPTION_WITH_DATASTORE_AND_CMHANDLE.formatted( - datastoreType.getDatastoreName(), cmHandleId), - subscriptionDetailsAsJson, OffsetDateTime.now()); + CPS_PATH_QUERY_FOR_CM_SUBSCRIPTION_FILTERS_WITH_DATASTORE_AND_CMHANDLE.formatted( + datastoreType.getDatastoreName(), cmHandleId), subscriptionDetailsAsJson, + OffsetDateTime.now(), ContentType.JSON); } } - private void saveSubscriptionDetails(final DatastoreType datastoreType, final String cmHandleId, - final String xpath, - final Collection<String> subscriptionIds) { + private void saveSubscriptionDetails(final DatastoreType datastoreType, final String cmHandleId, final String xpath, + final Collection<String> subscriptionIds) { final String subscriptionDetailsAsJson = getSubscriptionDetailsAsJson(xpath, subscriptionIds); cpsDataService.updateNodeLeaves(NCMP_DATASPACE_NAME, CM_SUBSCRIPTIONS_ANCHOR_NAME, - CPS_PATH_QUERY_FOR_CM_SUBSCRIPTION_WITH_DATASTORE_AND_CMHANDLE.formatted( - datastoreType.getDatastoreName(), cmHandleId), subscriptionDetailsAsJson, OffsetDateTime.now()); + CPS_PATH_QUERY_FOR_CM_SUBSCRIPTION_FILTERS_WITH_DATASTORE_AND_CMHANDLE.formatted( + datastoreType.getDatastoreName(), cmHandleId), subscriptionDetailsAsJson, OffsetDateTime.now(), + ContentType.JSON); } private String getSubscriptionDetailsAsJson(final String xpath, final Collection<String> subscriptionIds) { @@ -174,4 +239,6 @@ public class CmNotificationSubscriptionPersistenceServiceImpl implements CmNotif private static String escapeQuotesByDoublingThem(final String inputXpath) { return inputXpath.replace("'", "''"); } + } + diff --git a/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/impl/data/DmiDataOperations.java b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/impl/data/DmiDataOperations.java new file mode 100644 index 0000000000..301b8195e4 --- /dev/null +++ b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/impl/data/DmiDataOperations.java @@ -0,0 +1,310 @@ +/* + * ============LICENSE_START======================================================= + * Copyright (C) 2021-2024 Nordix Foundation + * Modifications Copyright (C) 2022 Bell Canada + * ================================================================================ + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * 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.impl.data; + +import static org.onap.cps.ncmp.api.data.models.DatastoreType.PASSTHROUGH_OPERATIONAL; +import static org.onap.cps.ncmp.api.data.models.DatastoreType.PASSTHROUGH_RUNNING; +import static org.onap.cps.ncmp.api.data.models.OperationType.READ; +import static org.onap.cps.ncmp.impl.models.RequiredDmiService.DATA; + +import io.micrometer.core.annotation.Timed; +import java.util.Collection; +import java.util.List; +import java.util.Map; +import java.util.Set; +import java.util.stream.Collectors; +import lombok.RequiredArgsConstructor; +import org.onap.cps.ncmp.api.NcmpResponseStatus; +import org.onap.cps.ncmp.api.data.models.CmResourceAddress; +import org.onap.cps.ncmp.api.data.models.DataOperationRequest; +import org.onap.cps.ncmp.api.data.models.OperationType; +import org.onap.cps.ncmp.api.exceptions.DmiClientRequestException; +import org.onap.cps.ncmp.impl.data.models.DmiDataOperation; +import org.onap.cps.ncmp.impl.data.models.DmiDataOperationRequest; +import org.onap.cps.ncmp.impl.data.models.DmiOperationCmHandle; +import org.onap.cps.ncmp.impl.data.policyexecutor.PolicyExecutor; +import org.onap.cps.ncmp.impl.data.utils.DmiDataOperationsHelper; +import org.onap.cps.ncmp.impl.dmi.DmiProperties; +import org.onap.cps.ncmp.impl.dmi.DmiRestClient; +import org.onap.cps.ncmp.impl.inventory.InventoryPersistence; +import org.onap.cps.ncmp.impl.inventory.models.CmHandleState; +import org.onap.cps.ncmp.impl.inventory.models.YangModelCmHandle; +import org.onap.cps.ncmp.impl.models.DmiRequestBody; +import org.onap.cps.ncmp.impl.utils.http.RestServiceUrlTemplateBuilder; +import org.onap.cps.ncmp.impl.utils.http.UrlTemplateParameters; +import org.onap.cps.spi.exceptions.CpsException; +import org.onap.cps.utils.JsonObjectMapper; +import org.springframework.http.ResponseEntity; +import org.springframework.stereotype.Service; +import org.springframework.util.LinkedMultiValueMap; +import org.springframework.util.MultiValueMap; +import org.springframework.web.util.UriComponentsBuilder; +import reactor.core.publisher.Flux; +import reactor.core.publisher.Mono; + +/** + * Operations class for DMI data. + */ +@RequiredArgsConstructor +@Service +public class DmiDataOperations { + + private final InventoryPersistence inventoryPersistence; + private final JsonObjectMapper jsonObjectMapper; + private final DmiProperties dmiProperties; + private final DmiRestClient dmiRestClient; + private final PolicyExecutor policyExecutor; + + /** + * This method fetches the resource data from the operational data store for a given CM handle + * identifier on the specified resource using the DMI client. + * + * @param cmResourceAddress Target datastore, CM handle, and resource identifier. + * @param options Options query string. + * @param topic Topic name for triggering asynchronous responses. + * @param requestId Request ID for asynchronous responses. + * @param authorization Contents of the Authorization header, or null if not present. + * @return {@code Mono<ResponseEntity<Object>>} A reactive type representing the response entity. + */ + @Timed(value = "cps.ncmp.dmi.get", + description = "Time taken to fetch the resource data from operational data store for given cm handle " + + "identifier on given resource using dmi client") + public Mono<ResponseEntity<Object>> getResourceDataFromDmi(final CmResourceAddress cmResourceAddress, + final String options, + final String topic, + final String requestId, + final String authorization) { + final YangModelCmHandle yangModelCmHandle = getYangModelCmHandle(cmResourceAddress.getResolvedCmHandleId()); + final CmHandleState cmHandleState = yangModelCmHandle.getCompositeState().getCmHandleState(); + validateIfCmHandleStateReady(yangModelCmHandle, cmHandleState); + final String jsonRequestBody = getDmiRequestBody(READ, requestId, null, null, yangModelCmHandle); + final UrlTemplateParameters urlTemplateParameters = getUrlTemplateParameters(cmResourceAddress + .getDatastoreName(), yangModelCmHandle, cmResourceAddress.getResourceIdentifier(), options, topic); + return dmiRestClient.asynchronousPostOperationWithJsonData(DATA, urlTemplateParameters, jsonRequestBody, READ, + authorization); + } + + /** + * This method fetches all the resource data from operational data store for given cm handle + * identifier using dmi client. + * Note: this method is only used for DataSync + * + * @param cmHandleId network resource identifier + * @param requestId requestId for async responses + * @return {@code ResponseEntity} response entity + */ + public ResponseEntity<Object> getAllResourceDataFromDmi(final String cmHandleId, final String requestId) { + final YangModelCmHandle yangModelCmHandle = getYangModelCmHandle(cmHandleId); + final CmHandleState cmHandleState = yangModelCmHandle.getCompositeState().getCmHandleState(); + validateIfCmHandleStateReady(yangModelCmHandle, cmHandleState); + + final String jsonRequestBody = getDmiRequestBody(READ, requestId, null, null, yangModelCmHandle); + final UrlTemplateParameters urlTemplateParameters = getUrlTemplateParameters( + PASSTHROUGH_OPERATIONAL.getDatastoreName(), yangModelCmHandle, "/", null, + null); + return dmiRestClient.synchronousPostOperationWithJsonData(DATA, urlTemplateParameters, jsonRequestBody, READ, + null); + } + + /** + * 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 dataOperationRequest data operation request to execute operations + * @param requestId requestId for as a response + * @param authorization contents of Authorization header, or null if not present + */ + public void requestResourceDataFromDmi(final String topicParamInQuery, + final DataOperationRequest dataOperationRequest, + final String requestId, + final String authorization) { + + final Set<String> cmHandlesIds = getDistinctCmHandleIds(dataOperationRequest); + + final Collection<YangModelCmHandle> yangModelCmHandles + = inventoryPersistence.getYangModelCmHandles(cmHandlesIds); + + final Map<String, List<DmiDataOperation>> operationsOutPerDmiServiceName + = DmiDataOperationsHelper.processPerDefinitionInDataOperationsRequest(topicParamInQuery, + requestId, dataOperationRequest, yangModelCmHandles); + + asyncSendMultipleRequest(requestId, topicParamInQuery, operationsOutPerDmiServiceName, + authorization); + } + + /** + * This method creates the resource data from pass-through running data store for given cm handle + * identifier on given resource using dmi client. + * + * @param cmHandleId network resource identifier + * @param resourceId resource identifier + * @param operationType operation enum + * @param requestData the request data + * @param dataType data type + * @param authorization contents of Authorization header, or null if not present + * @return {@code ResponseEntity} response entity + */ + public ResponseEntity<Object> writeResourceDataPassThroughRunningFromDmi(final String cmHandleId, + final String resourceId, + final OperationType operationType, + final String requestData, + final String dataType, + final String authorization) { + final CmResourceAddress cmResourceAddress = + new CmResourceAddress(PASSTHROUGH_RUNNING.getDatastoreName(), cmHandleId, resourceId); + + final YangModelCmHandle yangModelCmHandle = getYangModelCmHandle(cmResourceAddress.getResolvedCmHandleId()); + + policyExecutor.checkPermission(yangModelCmHandle, operationType, authorization, resourceId, requestData); + + final CmHandleState cmHandleState = yangModelCmHandle.getCompositeState().getCmHandleState(); + validateIfCmHandleStateReady(yangModelCmHandle, cmHandleState); + + final String jsonRequestBody = getDmiRequestBody(operationType, null, requestData, dataType, + yangModelCmHandle); + final UrlTemplateParameters urlTemplateParameters = getUrlTemplateParameters( + PASSTHROUGH_RUNNING.getDatastoreName(), yangModelCmHandle, resourceId, null, + null); + return dmiRestClient.synchronousPostOperationWithJsonData(DATA, urlTemplateParameters, jsonRequestBody, + operationType, authorization); + } + + private YangModelCmHandle getYangModelCmHandle(final String cmHandleId) { + return inventoryPersistence.getYangModelCmHandle(cmHandleId); + } + + private String getDmiRequestBody(final OperationType operationType, + final String requestId, + final String requestData, + final String dataType, + final YangModelCmHandle yangModelCmHandle) { + final DmiRequestBody dmiRequestBody = DmiRequestBody.builder() + .operationType(operationType) + .requestId(requestId) + .data(requestData) + .dataType(dataType) + .moduleSetTag(yangModelCmHandle.getModuleSetTag()) + .build(); + dmiRequestBody.asDmiProperties(yangModelCmHandle.getDmiProperties()); + return jsonObjectMapper.asJsonString(dmiRequestBody); + } + + private UrlTemplateParameters getUrlTemplateParameters(final String datastoreName, + final YangModelCmHandle yangModelCmHandle, + final String resourceIdentifier, + final String optionsParamInQuery, + final String topicParamInQuery) { + final String dmiServiceName = yangModelCmHandle.resolveDmiServiceName(DATA); + return RestServiceUrlTemplateBuilder.newInstance() + .fixedPathSegment("ch") + .variablePathSegment("cmHandleId", yangModelCmHandle.getId()) + .fixedPathSegment("data") + .fixedPathSegment("ds") + .variablePathSegment("datastore", datastoreName) + .queryParameter("resourceIdentifier", resourceIdentifier) + .queryParameter("options", optionsParamInQuery) + .queryParameter("topic", topicParamInQuery) + .createUrlTemplateParameters(dmiServiceName, dmiProperties.getDmiBasePath()); + } + + private UrlTemplateParameters getUrlTemplateParameters(final String dmiServiceName, + final String requestId, + final String topicParamInQuery) { + return RestServiceUrlTemplateBuilder.newInstance() + .fixedPathSegment("data") + .queryParameter("requestId", requestId) + .queryParameter("topic", topicParamInQuery) + .createUrlTemplateParameters(dmiServiceName, dmiProperties.getDmiBasePath()); + } + + private void validateIfCmHandleStateReady(final YangModelCmHandle yangModelCmHandle, + final CmHandleState cmHandleState) { + if (cmHandleState != CmHandleState.READY) { + throw new CpsException("State mismatch exception.", "Cm-Handle not in READY state. " + + "cm handle state is " + + yangModelCmHandle.getCompositeState().getCmHandleState()); + } + } + + private static Set<String> getDistinctCmHandleIds(final DataOperationRequest dataOperationRequest) { + return dataOperationRequest.getDataOperationDefinitions().stream() + .flatMap(dataOperationDefinition -> + dataOperationDefinition.getCmHandleIds().stream()).collect(Collectors.toSet()); + } + + private void asyncSendMultipleRequest(final String requestId, final String topicParamInQuery, + final Map<String, List<DmiDataOperation>> dmiDataOperationsPerDmi, + final String authorization) { + + Flux.fromIterable(dmiDataOperationsPerDmi.entrySet()) + .flatMap(entry -> { + final String dmiServiceName = entry.getKey(); + final UrlTemplateParameters urlTemplateParameters = getUrlTemplateParameters(dmiServiceName, + requestId, topicParamInQuery); + final List<DmiDataOperation> dmiDataOperations = entry.getValue(); + final String dmiDataOperationRequestAsJsonString + = createDmiDataOperationRequestAsJsonString(dmiDataOperations); + return dmiRestClient.asynchronousPostOperationWithJsonData(DATA, urlTemplateParameters, + dmiDataOperationRequestAsJsonString, READ, authorization) + .then() + .onErrorResume(DmiClientRequestException.class, dmiClientRequestException -> { + final String dataOperationResourceUrl = UriComponentsBuilder + .fromUriString(urlTemplateParameters.urlTemplate()) + .buildAndExpand(urlTemplateParameters.urlVariables()) + .toUriString(); + handleTaskCompletionException(dmiClientRequestException, dataOperationResourceUrl, + dmiDataOperations); + return Mono.empty(); + }); + }).subscribe(); + } + + private String createDmiDataOperationRequestAsJsonString( + final List<DmiDataOperation> dmiDataOperationRequestBodies) { + final DmiDataOperationRequest dmiDataOperationRequest = DmiDataOperationRequest.builder() + .operations(dmiDataOperationRequestBodies) + .build(); + return jsonObjectMapper.asJsonString(dmiDataOperationRequest); + } + + private void handleTaskCompletionException(final DmiClientRequestException dmiClientRequestException, + final String dataOperationResourceUrl, + final List<DmiDataOperation> dmiDataOperations) { + final MultiValueMap<String, String> dataOperationResourceUrlParameters = + UriComponentsBuilder.fromUriString(dataOperationResourceUrl).build().getQueryParams(); + final String topicName = dataOperationResourceUrlParameters.get("topic").get(0); + final String requestId = dataOperationResourceUrlParameters.get("requestId").get(0); + + final MultiValueMap<DmiDataOperation, Map<NcmpResponseStatus, List<String>>> + cmHandleIdsPerResponseCodesPerOperation = new LinkedMultiValueMap<>(); + + dmiDataOperations.forEach(dmiDataOperationRequestBody -> { + final List<String> cmHandleIds = dmiDataOperationRequestBody.getCmHandles().stream() + .map(DmiOperationCmHandle::getId).toList(); + cmHandleIdsPerResponseCodesPerOperation.add(dmiDataOperationRequestBody, + Map.of(dmiClientRequestException.getNcmpResponseStatus(), cmHandleIds)); + }); + DmiDataOperationsHelper.publishErrorMessageToClientTopic(topicName, requestId, + cmHandleIdsPerResponseCodesPerOperation); + } +} diff --git a/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/impl/data/NcmpCachedResourceRequestHandler.java b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/impl/data/NcmpCachedResourceRequestHandler.java new file mode 100644 index 0000000000..01022cc03e --- /dev/null +++ b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/impl/data/NcmpCachedResourceRequestHandler.java @@ -0,0 +1,75 @@ +/* + * ============LICENSE_START======================================================= + * Copyright (C) 2022-2024 Nordix Foundation + * ================================================================================ + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * SPDX-License-Identifier: Apache-2.0 + * ============LICENSE_END========================================================= + */ + +package org.onap.cps.ncmp.impl.data; + +import java.util.Collection; +import lombok.RequiredArgsConstructor; +import org.onap.cps.api.CpsDataService; +import org.onap.cps.ncmp.api.data.models.CmResourceAddress; +import org.onap.cps.spi.FetchDescendantsOption; +import org.onap.cps.spi.model.DataNode; +import org.springframework.stereotype.Service; +import reactor.core.publisher.Mono; + +@Service +@RequiredArgsConstructor +public class NcmpCachedResourceRequestHandler extends NcmpDatastoreRequestHandler { + + private final CpsDataService cpsDataService; + private final NetworkCmProxyQueryService networkCmProxyQueryService; + + /** + * Executes a synchronous query request for given cm handle. + * Note. Currently only ncmp-datastore:operational supports query operations. + * + * @param cmHandleId the cm handle + * @param resourceIdentifier the resource identifier + * @param includeDescendants whether include descendants + * @return a collection of data nodes + */ + public Collection<DataNode> executeRequest(final String cmHandleId, final String resourceIdentifier, + final boolean includeDescendants) { + final FetchDescendantsOption fetchDescendantsOption = getFetchDescendantsOption(includeDescendants); + return networkCmProxyQueryService.queryResourceDataOperational(cmHandleId, resourceIdentifier, + fetchDescendantsOption); + } + + @Override + protected Mono<Object> getResourceDataForCmHandle(final CmResourceAddress cmResourceAddress, + final String optionsParamInQuery, + final String topicParamInQuery, + final String requestId, + final boolean includeDescendants, + final String authorization) { + final FetchDescendantsOption fetchDescendantsOption = getFetchDescendantsOption(includeDescendants); + + final DataNode dataNode = cpsDataService.getDataNodes(cmResourceAddress.getDatastoreName(), + cmResourceAddress.getResolvedCmHandleId(), + cmResourceAddress.getResourceIdentifier(), + fetchDescendantsOption).iterator().next(); + return Mono.justOrEmpty(dataNode); + } + + private static FetchDescendantsOption getFetchDescendantsOption(final boolean includeDescendants) { + return includeDescendants ? FetchDescendantsOption.INCLUDE_ALL_DESCENDANTS + : FetchDescendantsOption.OMIT_DESCENDANTS; + } +} diff --git a/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/impl/data/NcmpDatastoreRequestHandler.java b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/impl/data/NcmpDatastoreRequestHandler.java new file mode 100644 index 0000000000..f0a8c6c5d2 --- /dev/null +++ b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/impl/data/NcmpDatastoreRequestHandler.java @@ -0,0 +1,96 @@ +/* + * ============LICENSE_START======================================================= + * Copyright (C) 2022-2024 Nordix Foundation + * ================================================================================ + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * SPDX-License-Identifier: Apache-2.0 + * ============LICENSE_END========================================================= + */ + +package org.onap.cps.ncmp.impl.data; + +import java.util.Map; +import java.util.UUID; +import lombok.extern.slf4j.Slf4j; +import org.onap.cps.ncmp.api.data.models.CmResourceAddress; +import org.onap.cps.ncmp.utils.events.TopicValidator; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.stereotype.Service; +import reactor.core.publisher.Mono; + +@Slf4j +@Service +public abstract class NcmpDatastoreRequestHandler { + + private static final String NO_REQUEST_ID = null; + private static final String NO_TOPIC = null; + + @Value("${notification.async.executor.time-out-value-in-ms:60000}") + protected int timeOutInMilliSeconds; + @Value("${notification.enabled:true}") + protected boolean notificationFeatureEnabled; + + /** + * Executes synchronous/asynchronous get request for given cm handle. + * + * @param cmResourceAddress the name of the datastore, cm handle and resource identifier + * @param options options to pass through to dmi client + * @param topic topic (optional) for asynchronous responses + * @param includeDescendants whether include descendants + * @param authorization contents of Authorization header, or null if not present + * @return the result object, depends on use op topic. With topic a map object with request id is returned + * otherwise the result of the request. + */ + public Object executeRequest(final CmResourceAddress cmResourceAddress, + final String options, + final String topic, + final boolean includeDescendants, + final String authorization) { + + final boolean asyncResponseRequested = topic != null; + if (asyncResponseRequested && notificationFeatureEnabled) { + return getResourceDataAsynchronously(cmResourceAddress, options, topic, includeDescendants, authorization); + } + + if (asyncResponseRequested) { + log.warn("Asynchronous request is unavailable as notification feature is currently disabled, " + + "will use synchronous operation."); + } + final Mono<Object> resourceDataMono = getResourceDataForCmHandle(cmResourceAddress, options, + NO_TOPIC, NO_REQUEST_ID, includeDescendants, authorization); + return resourceDataMono.block(); + } + + private Map<String, String> getResourceDataAsynchronously(final CmResourceAddress cmResourceAddress, + final String options, + final String topic, + final boolean includeDescendants, + final String authorization) { + TopicValidator.validateTopicName(topic); + final String requestId = UUID.randomUUID().toString(); + getResourceDataForCmHandle(cmResourceAddress, options, topic, requestId, includeDescendants, authorization) + .doOnSuccess(result -> + log.debug("Async operation succeeded for request id {}: {}", requestId, result)) + .subscribe(); + log.debug("Received Async request with id {}", requestId); + return Map.of("requestId", requestId); + } + + protected abstract Mono<Object> getResourceDataForCmHandle(final CmResourceAddress cmResourceAddress, + final String optionsParamInQuery, + final String topicParamInQuery, + final String requestId, + final boolean includeDescendant, + final String authorization); +} diff --git a/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/impl/data/NcmpPassthroughResourceRequestHandler.java b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/impl/data/NcmpPassthroughResourceRequestHandler.java new file mode 100644 index 0000000000..a21210c376 --- /dev/null +++ b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/impl/data/NcmpPassthroughResourceRequestHandler.java @@ -0,0 +1,102 @@ +/* + * ============LICENSE_START======================================================= + * Copyright (C) 2022-2024 Nordix Foundation + * ================================================================================ + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * SPDX-License-Identifier: Apache-2.0 + * ============LICENSE_END========================================================= + */ + +package org.onap.cps.ncmp.impl.data; + +import static org.onap.cps.ncmp.api.data.models.DatastoreType.OPERATIONAL; +import static org.onap.cps.ncmp.api.data.models.OperationType.READ; + +import java.util.Map; +import java.util.UUID; +import lombok.RequiredArgsConstructor; +import org.onap.cps.ncmp.api.data.exceptions.InvalidDatastoreException; +import org.onap.cps.ncmp.api.data.exceptions.OperationNotSupportedException; +import org.onap.cps.ncmp.api.data.models.CmResourceAddress; +import org.onap.cps.ncmp.api.data.models.DataOperationRequest; +import org.onap.cps.ncmp.api.data.models.DatastoreType; +import org.onap.cps.ncmp.api.data.models.OperationType; +import org.onap.cps.ncmp.api.exceptions.PayloadTooLargeException; +import org.onap.cps.ncmp.utils.events.TopicValidator; +import org.springframework.stereotype.Service; +import reactor.core.publisher.Mono; + +@Service +@RequiredArgsConstructor +public class NcmpPassthroughResourceRequestHandler extends NcmpDatastoreRequestHandler { + + private final DmiDataOperations dmiDataOperations; + + private static final int MAXIMUM_CM_HANDLES_PER_OPERATION = 200; + private static final String PAYLOAD_TOO_LARGE_TEMPLATE = "Operation '%s' affects too many (%d) cm handles"; + + /** + * Executes asynchronous request for group of cm handles to resource data. + * + * @param topic the topic param in query + * @param dataOperationRequest data operation request details for resource data + * @param authorization contents of Authorization header, or null if not present + * @return a map with one entry of request Id for success or status and error when async feature is disabled + */ + public Map<String, String> executeAsynchronousRequest(final String topic, + final DataOperationRequest dataOperationRequest, + final String authorization) { + validateDataOperationRequest(topic, dataOperationRequest); + if (!notificationFeatureEnabled) { + return Map.of("status", + "Asynchronous request is unavailable as notification feature is currently disabled."); + } + final String requestId = UUID.randomUUID().toString(); + dmiDataOperations.requestResourceDataFromDmi(topic, dataOperationRequest, requestId, authorization); + return Map.of("requestId", requestId); + } + + @Override + protected Mono<Object> getResourceDataForCmHandle(final CmResourceAddress cmResourceAddress, + final String options, + final String topic, + final String requestId, + final boolean includeDescendants, + final String authorization) { + + return dmiDataOperations.getResourceDataFromDmi(cmResourceAddress, options, topic, requestId, authorization) + .flatMap(responseEntity -> Mono.justOrEmpty(responseEntity.getBody())); + } + + private void validateDataOperationRequest(final String topicParamInQuery, + final DataOperationRequest dataOperationRequest) { + TopicValidator.validateTopicName(topicParamInQuery); + dataOperationRequest.getDataOperationDefinitions().forEach(dataOperationDefinition -> { + if (OperationType.fromOperationName(dataOperationDefinition.getOperation()) != READ) { + throw new OperationNotSupportedException( + dataOperationDefinition.getOperation() + " operation not yet supported"); + } + if (DatastoreType.fromDatastoreName(dataOperationDefinition.getDatastore()) == OPERATIONAL) { + throw new InvalidDatastoreException(dataOperationDefinition.getDatastore() + + " datastore is not supported"); + } + if (dataOperationDefinition.getCmHandleIds().size() > MAXIMUM_CM_HANDLES_PER_OPERATION) { + final String errorMessage = String.format(PAYLOAD_TOO_LARGE_TEMPLATE, + dataOperationDefinition.getOperationId(), + dataOperationDefinition.getCmHandleIds().size()); + throw new PayloadTooLargeException(errorMessage); + } + }); + } +} diff --git a/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/impl/data/NetworkCmProxyFacade.java b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/impl/data/NetworkCmProxyFacade.java new file mode 100644 index 0000000000..5343a2e131 --- /dev/null +++ b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/impl/data/NetworkCmProxyFacade.java @@ -0,0 +1,129 @@ +/* + * ============LICENSE_START======================================================= + * Copyright (C) 2021 highstreet technologies GmbH + * Modifications Copyright (C) 2021-2024 Nordix Foundation + * Modifications Copyright (C) 2021 Pantheon.tech + * Modifications Copyright (C) 2021-2022 Bell Canada + * Modifications Copyright (C) 2023 TechMahindra Ltd. + * ================================================================================ + * 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.impl.data; + +import static org.onap.cps.ncmp.api.data.models.DatastoreType.OPERATIONAL; + +import java.util.Collection; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.onap.cps.ncmp.api.data.models.CmResourceAddress; +import org.onap.cps.ncmp.api.data.models.DataOperationRequest; +import org.onap.cps.ncmp.api.data.models.DatastoreType; +import org.onap.cps.ncmp.api.data.models.OperationType; +import org.onap.cps.ncmp.impl.utils.AlternateIdMatcher; +import org.onap.cps.spi.model.DataNode; +import org.springframework.stereotype.Service; + +@Slf4j +@Service +@RequiredArgsConstructor +public class NetworkCmProxyFacade { + + private final NcmpCachedResourceRequestHandler ncmpCachedResourceRequestHandler; + private final NcmpPassthroughResourceRequestHandler ncmpPassthroughResourceRequestHandler; + private final DmiDataOperations dmiDataOperations; + private final AlternateIdMatcher alternateIdMatcher; + + /** + * Fetches resource data for a given data store using DMI (Data Management Interface). + * This method retrieves data based on the provided CmResourceAddress and additional query parameters. + * It supports asynchronous processing and handles authorization if required. + * + * @param cmResourceAddress The target data store, including the CM handle and resource identifier. + * This parameter must not be null. + * @param optionsParamInQuery Additional query parameters that may influence the data retrieval process, + * such as filters or limits. This parameter can be null. + * @param topicParamInQuery The topic name for triggering asynchronous responses. If specified, + * the response will be sent to this topic. This parameter can be null. + * @param includeDescendants include (all) descendants or not + * @param authorization The contents of the Authorization header. This parameter can be null + * if authorization is not required. + * @return the result object, depends on use op topic. With topic a map object with request id is returned + * otherwise the result of the request. + */ + public Object getResourceDataForCmHandle(final CmResourceAddress cmResourceAddress, + final String optionsParamInQuery, + final String topicParamInQuery, + final Boolean includeDescendants, + final String authorization) { + + final NcmpDatastoreRequestHandler ncmpDatastoreRequestHandler + = getNcmpDatastoreRequestHandler(cmResourceAddress.getDatastoreName()); + return ncmpDatastoreRequestHandler.executeRequest(cmResourceAddress, optionsParamInQuery, + topicParamInQuery, includeDescendants, authorization); + } + + /** + * Executes asynchronous request for group of cm handles to resource data. + * + * @param topic the topic param in query + * @param dataOperationRequest data operation request details for resource data + * @param authorization contents of Authorization header, or null if not present + * @return a map with one entry of request Id for success or status and error when async feature is disabled + */ + public Object executeDataOperationForCmHandles(final String topic, + final DataOperationRequest dataOperationRequest, + final String authorization) { + return ncmpPassthroughResourceRequestHandler.executeAsynchronousRequest(topic, + dataOperationRequest, + authorization); + } + + public Collection<DataNode> queryResourceDataForCmHandle(final String cmHandle, + final String cpsPath, + final Boolean includeDescendants) { + return ncmpCachedResourceRequestHandler.executeRequest(cmHandle, cpsPath, includeDescendants); + } + + /** + * Write resource data for data store pass-through running using dmi for given cm-handle. + * + * @param cmHandleReference cm handle or alternate identifier + * @param resourceIdentifier resource identifier + * @param operationType required operation type + * @param requestData request body to create resource + * @param dataType content type in body + * @param authorization contents of Authorization header, or null if not present + * @return {@code Object} return data + */ + public Object writeResourceDataPassThroughRunningForCmHandle(final String cmHandleReference, + final String resourceIdentifier, + final OperationType operationType, + final String requestData, + final String dataType, + final String authorization) { + return dmiDataOperations.writeResourceDataPassThroughRunningFromDmi(cmHandleReference, resourceIdentifier, + operationType, requestData, dataType, authorization); + } + + private NcmpDatastoreRequestHandler getNcmpDatastoreRequestHandler(final String datastoreName) { + if (OPERATIONAL.equals(DatastoreType.fromDatastoreName(datastoreName))) { + return ncmpCachedResourceRequestHandler; + } + return ncmpPassthroughResourceRequestHandler; + } + +} diff --git a/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/impl/data/NetworkCmProxyQueryService.java b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/impl/data/NetworkCmProxyQueryService.java new file mode 100644 index 0000000000..39abdc56a0 --- /dev/null +++ b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/impl/data/NetworkCmProxyQueryService.java @@ -0,0 +1,50 @@ +/* + * ============LICENSE_START======================================================= + * Copyright (C) 2022-2024 Nordix Foundation + * ================================================================================ + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * SPDX-License-Identifier: Apache-2.0 + * ============LICENSE_END========================================================= + */ + +package org.onap.cps.ncmp.impl.data; + +import java.util.Collection; +import org.onap.cps.spi.FetchDescendantsOption; +import org.onap.cps.spi.model.DataNode; + +/* + * Datastore interface for handling cached CPS data query requests. + */ +public interface NetworkCmProxyQueryService { + + /** + * Fetches operational resource data based on the provided CM handle identifier and CPS path. + * This method retrieves data nodes from the specified path within the context of a given CM handle. + * It supports options for fetching descendant nodes. + * + * @param cmHandleId The CM handle identifier, which uniquely identifies the CM handle. + * This parameter must not be null. + * @param cpsPath The CPS (Control Plane Service) path specifying the location of the + * resource data within the CM handle. This parameter must not be null. + * @param fetchDescendantsOption The option specifying whether to fetch descendant nodes along with the specified + * resource data. + * @return {@code Collection<DataNode>} A collection of DataNode objects representing the resource data + * retrieved from the specified path. The collection may include descendant nodes based on the + * fetchDescendantsOption. + */ + Collection<DataNode> queryResourceDataOperational(String cmHandleId, + String cpsPath, + FetchDescendantsOption fetchDescendantsOption); +} diff --git a/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/NetworkCmProxyQueryServiceImpl.java b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/impl/data/NetworkCmProxyQueryServiceImpl.java index d8353f3029..021924a616 100644 --- a/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/NetworkCmProxyQueryServiceImpl.java +++ b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/impl/data/NetworkCmProxyQueryServiceImpl.java @@ -18,15 +18,16 @@ * ============LICENSE_END========================================================= */ -package org.onap.cps.ncmp.api.impl; +package org.onap.cps.ncmp.impl.data; -import static org.onap.cps.ncmp.api.impl.ncmppersistence.NcmpPersistence.NFP_OPERATIONAL_DATASTORE_DATASPACE_NAME; +import static org.onap.cps.ncmp.impl.inventory.NcmpPersistence.NFP_OPERATIONAL_DATASTORE_DATASPACE_NAME; +import java.util.Collection; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; import org.onap.cps.api.CpsQueryService; -import org.onap.cps.ncmp.api.NetworkCmProxyQueryService; import org.onap.cps.spi.FetchDescendantsOption; +import org.onap.cps.spi.model.DataNode; import org.springframework.stereotype.Service; @Slf4j @@ -37,9 +38,9 @@ public class NetworkCmProxyQueryServiceImpl implements NetworkCmProxyQueryServic private final CpsQueryService cpsQueryService; @Override - public Object queryResourceDataOperational(final String cmHandleId, - final String cpsPath, - final FetchDescendantsOption fetchDescendantsOption) { + public Collection<DataNode> queryResourceDataOperational(final String cmHandleId, + final String cpsPath, + final FetchDescendantsOption fetchDescendantsOption) { return cpsQueryService.queryDataNodes(NFP_OPERATIONAL_DATASTORE_DATASPACE_NAME, cmHandleId, cpsPath, fetchDescendantsOption); } diff --git a/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/async/AsyncRestRequestResponseEventConsumer.java b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/impl/data/async/AsyncRestRequestResponseEventConsumer.java index 993e3d63db..f14bb15842 100644 --- a/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/async/AsyncRestRequestResponseEventConsumer.java +++ b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/impl/data/async/AsyncRestRequestResponseEventConsumer.java @@ -18,7 +18,7 @@ * ============LICENSE_END========================================================= */ -package org.onap.cps.ncmp.api.impl.async; +package org.onap.cps.ncmp.impl.data.async; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; diff --git a/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/async/DataOperationEventConsumer.java b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/impl/data/async/DataOperationEventConsumer.java index 9bb7fae4d9..6f368da2d0 100644 --- a/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/async/DataOperationEventConsumer.java +++ b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/impl/data/async/DataOperationEventConsumer.java @@ -18,7 +18,7 @@ * ============LICENSE_END========================================================= */ -package org.onap.cps.ncmp.api.impl.async; +package org.onap.cps.ncmp.impl.data.async; import io.cloudevents.CloudEvent; import io.cloudevents.kafka.impl.KafkaHeaders; diff --git a/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/async/NcmpAsyncRequestResponseEventMapper.java b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/impl/data/async/NcmpAsyncRequestResponseEventMapper.java index 46a11b82a0..21d40339b2 100644 --- a/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/async/NcmpAsyncRequestResponseEventMapper.java +++ b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/impl/data/async/NcmpAsyncRequestResponseEventMapper.java @@ -18,7 +18,7 @@ * ============LICENSE_END========================================================= */ -package org.onap.cps.ncmp.api.impl.async; +package org.onap.cps.ncmp.impl.data.async; import java.time.ZonedDateTime; import java.time.format.DateTimeFormatter; diff --git a/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/async/RecordFilterStrategies.java b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/impl/data/async/RecordFilterStrategies.java index 0404790408..2615672374 100644 --- a/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/async/RecordFilterStrategies.java +++ b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/impl/data/async/RecordFilterStrategies.java @@ -18,7 +18,7 @@ * ============LICENSE_END========================================================= */ -package org.onap.cps.ncmp.api.impl.async; +package org.onap.cps.ncmp.impl.data.async; import io.cloudevents.CloudEvent; import io.cloudevents.kafka.impl.KafkaHeaders; diff --git a/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/operations/DmiDataOperation.java b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/impl/data/models/DmiDataOperation.java index 2e66ac0bf1..649d3ad374 100644 --- a/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/operations/DmiDataOperation.java +++ b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/impl/data/models/DmiDataOperation.java @@ -18,7 +18,7 @@ * ============LICENSE_END========================================================= */ -package org.onap.cps.ncmp.api.impl.operations; +package org.onap.cps.ncmp.impl.data.models; import com.fasterxml.jackson.annotation.JsonInclude; import com.fasterxml.jackson.annotation.JsonPropertyOrder; @@ -26,7 +26,9 @@ import java.util.ArrayList; import java.util.List; import lombok.Builder; import lombok.Getter; -import org.onap.cps.ncmp.api.models.DataOperationDefinition; +import org.onap.cps.ncmp.api.data.models.DataOperationDefinition; +import org.onap.cps.ncmp.api.data.models.DatastoreType; +import org.onap.cps.ncmp.api.data.models.OperationType; @JsonInclude(JsonInclude.Include.NON_NULL) @Getter @@ -40,7 +42,7 @@ public class DmiDataOperation { private String options; private String resourceIdentifier; - private final List<CmHandle> cmHandles = new ArrayList<>(); + private final List<DmiOperationCmHandle> cmHandles = new ArrayList<>(); /** * Create and initialise a (outgoing) DMI data operation. diff --git a/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/operations/DmiDataOperationRequest.java b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/impl/data/models/DmiDataOperationRequest.java index 8ee1d905bf..f342f04a1a 100644 --- a/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/operations/DmiDataOperationRequest.java +++ b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/impl/data/models/DmiDataOperationRequest.java @@ -18,7 +18,7 @@ * ============LICENSE_END========================================================= */ -package org.onap.cps.ncmp.api.impl.operations; +package org.onap.cps.ncmp.impl.data.models; import com.fasterxml.jackson.annotation.JsonInclude; import java.util.List; 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/impl/data/models/DmiOperationCmHandle.java index 618da74543..6926b6890e 100644 --- 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/impl/data/models/DmiOperationCmHandle.java @@ -18,7 +18,7 @@ * ============LICENSE_END========================================================= */ -package org.onap.cps.ncmp.api.impl.operations; +package org.onap.cps.ncmp.impl.data.models; import com.fasterxml.jackson.annotation.JsonInclude; import com.fasterxml.jackson.annotation.JsonProperty; @@ -29,14 +29,21 @@ import lombok.Getter; @JsonInclude(JsonInclude.Include.NON_NULL) @Getter @Builder -public class CmHandle { +public class DmiOperationCmHandle { private String id; @JsonProperty("cmHandleProperties") private Map<String, String> dmiProperties; + private String moduleSetTag; - public static CmHandle buildCmHandleWithProperties(final String cmHandleId, - final Map<String, String> dmiProperties) { - return CmHandle.builder().id(cmHandleId).dmiProperties(dmiProperties).build(); + /** + * Builds Dmi Operation Cm Handle object with all its associated properties. + */ + public static DmiOperationCmHandle buildDmiOperationCmHandle(final String cmHandleId, + final Map<String, String> dmiProperties, + final String moduleSetTag) { + return DmiOperationCmHandle.builder().id(cmHandleId) + .dmiProperties(dmiProperties).moduleSetTag(moduleSetTag) + .build(); } } diff --git a/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/impl/data/policyexecutor/PolicyExecutor.java b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/impl/data/policyexecutor/PolicyExecutor.java new file mode 100644 index 0000000000..caed28a648 --- /dev/null +++ b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/impl/data/policyexecutor/PolicyExecutor.java @@ -0,0 +1,216 @@ +/* + * ============LICENSE_START======================================================= + * Copyright (C) 2024 Nordix Foundation + * ================================================================================ + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * SPDX-License-Identifier: Apache-2.0 + * ============LICENSE_END========================================================= + */ + +package org.onap.cps.ncmp.impl.data.policyexecutor; + +import com.fasterxml.jackson.core.JsonProcessingException; +import com.fasterxml.jackson.databind.JsonNode; +import com.fasterxml.jackson.databind.ObjectMapper; +import java.time.Duration; +import java.time.temporal.ChronoUnit; +import java.util.Collections; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.concurrent.TimeoutException; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.onap.cps.ncmp.api.data.models.OperationType; +import org.onap.cps.ncmp.api.exceptions.NcmpException; +import org.onap.cps.ncmp.api.exceptions.PolicyExecutorException; +import org.onap.cps.ncmp.impl.inventory.models.YangModelCmHandle; +import org.onap.cps.ncmp.impl.utils.http.RestServiceUrlTemplateBuilder; +import org.onap.cps.ncmp.impl.utils.http.UrlTemplateParameters; +import org.springframework.beans.factory.annotation.Qualifier; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.http.HttpHeaders; +import org.springframework.http.ResponseEntity; +import org.springframework.stereotype.Service; +import org.springframework.web.reactive.function.BodyInserters; +import org.springframework.web.reactive.function.client.WebClient; + +@Slf4j +@Service +@RequiredArgsConstructor +public class PolicyExecutor { + + @Value("${ncmp.policy-executor.enabled:false}") + private boolean enabled; + + @Value("${ncmp.policy-executor.defaultDecision:deny}") + private String defaultDecision; + + @Value("${ncmp.policy-executor.server.address:http://policy-executor}") + private String serverAddress; + + @Value("${ncmp.policy-executor.server.port:8080}") + private String serverPort; + + @Value("${ncmp.policy-executor.httpclient.all-services.readTimeoutInSeconds:30}") + private long readTimeoutInSeconds; + + @Qualifier("policyExecutorWebClient") + private final WebClient policyExecutorWebClient; + + private final ObjectMapper objectMapper; + + /** + * Use the Policy Executor to check permission for a cm write operation. + * Wil throw an exception when the operation is not permitted (work in progress) + * + * @param yangModelCmHandle the cm handle involved + * @param operationType the write operation + * @param authorization the original rest authorization token (can be used to determine the client) + * @param resourceIdentifier the resource identifier (can be blank) + * @param changeRequestAsJson the change details from the original rest request in json format + */ + public void checkPermission(final YangModelCmHandle yangModelCmHandle, + final OperationType operationType, + final String authorization, + final String resourceIdentifier, + final String changeRequestAsJson) { + log.trace("Policy Executor Enabled: {}", enabled); + if (enabled) { + ResponseEntity<JsonNode> responseEntity = null; + try { + responseEntity = + getPolicyExecutorResponse(yangModelCmHandle, operationType, authorization, resourceIdentifier, + changeRequestAsJson); + } catch (final RuntimeException runtimeException) { + processException(runtimeException); + } + if (responseEntity == null) { + log.warn("No valid response from Policy Executor, ignored"); + return; + } + if (responseEntity.getStatusCode().is2xxSuccessful()) { + if (responseEntity.getBody() == null) { + log.warn("No valid response body from Policy Executor, ignored"); + return; + } + processSuccessResponse(responseEntity.getBody()); + } else { + processNon2xxResponse(responseEntity.getStatusCode().value()); + } + } + } + + private Map<String, Object> getSingleRequestAsMap(final YangModelCmHandle yangModelCmHandle, + final OperationType operationType, + final String resourceIdentifier, + final String changeRequestAsJson) { + final Map<String, Object> data = new HashMap<>(4); + data.put("cmHandleId", yangModelCmHandle.getId()); + data.put("resourceIdentifier", resourceIdentifier); + data.put("targetIdentifier", yangModelCmHandle.getAlternateId()); + if (!OperationType.DELETE.equals(operationType)) { + try { + final Object changeRequestAsObject = objectMapper.readValue(changeRequestAsJson, Object.class); + data.put("cmChangeRequest", changeRequestAsObject); + } catch (final JsonProcessingException e) { + throw new NcmpException("Cannot convert Change Request data to Object", + "Invalid Json: " + changeRequestAsJson); + } + } + final Map<String, Object> request = new HashMap<>(2); + request.put("schema", getAssociatedPolicyDataSchemaName(operationType)); + request.put("data", data); + return request; + } + + private static String getAssociatedPolicyDataSchemaName(final OperationType operationType) { + return "urn:cps:org.onap.cps.ncmp.policy-executor.ncmp-" + operationType.getOperationName() + "-schema:1.0.0"; + } + + private Object createBodyAsObject(final List<Object> requests) { + final Map<String, Object> bodyAsMap = new HashMap<>(2); + bodyAsMap.put("decisionType", "allow"); + bodyAsMap.put("requests", requests); + return bodyAsMap; + } + + private ResponseEntity<JsonNode> getPolicyExecutorResponse(final YangModelCmHandle yangModelCmHandle, + final OperationType operationType, + final String authorization, + final String resourceIdentifier, + final String changeRequestAsJson) { + final Map<String, Object> requestAsMap = getSingleRequestAsMap(yangModelCmHandle, + operationType, + resourceIdentifier, + changeRequestAsJson); + + final Object bodyAsObject = createBodyAsObject(Collections.singletonList(requestAsMap)); + + final UrlTemplateParameters urlTemplateParameters = RestServiceUrlTemplateBuilder.newInstance() + .fixedPathSegment("execute") + .createUrlTemplateParameters(String.format("%s:%s", serverAddress, serverPort), + "policy-executor/api"); + + return policyExecutorWebClient.post() + .uri(urlTemplateParameters.urlTemplate(), urlTemplateParameters.urlVariables()) + .header(HttpHeaders.AUTHORIZATION, authorization) + .body(BodyInserters.fromValue(bodyAsObject)) + .retrieve() + .toEntity(JsonNode.class) + .timeout(Duration.of(readTimeoutInSeconds, ChronoUnit.SECONDS)) + .block(); + } + + private void processNon2xxResponse(final int httpStatusCode) { + processFallbackResponse("Policy Executor returned HTTP Status code " + httpStatusCode + "."); + } + + private void processException(final RuntimeException runtimeException) { + if (runtimeException.getCause() instanceof TimeoutException) { + processFallbackResponse("Policy Executor request timed out."); + } else { + log.warn("Request to Policy Executor failed with unexpected exception", runtimeException); + throw runtimeException; + } + } + + private void processFallbackResponse(final String message) { + final String decisionId = "N/A"; + final String decision = defaultDecision; + final String warning = message + " Falling back to configured default decision: " + defaultDecision; + log.warn(warning); + processDecision(decisionId, decision, warning); + } + + private static void processSuccessResponse(final JsonNode responseBody) { + final String decisionId = responseBody.path("decisionId").asText("unknown id"); + final String decision = responseBody.path("decision").asText("unknown"); + final String messageFromPolicyExecutor = responseBody.path("message").asText(); + processDecision(decisionId, decision, messageFromPolicyExecutor); + } + + private static void processDecision(final String decisionId, final String decision, final String details) { + log.trace("Policy Executor decision id: {} ", decisionId); + if ("allow".equals(decision)) { + log.trace("Operation allowed."); + } else { + log.warn("Policy Executor decision: {}", decision); + log.warn("Policy Executor message: {}", details); + final String message = "Operation not allowed. Decision id " + decisionId + " : " + decision; + throw new PolicyExecutorException(message, details); + } + } + +} diff --git a/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/utils/data/operation/DataOperationEventCreator.java b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/impl/data/utils/DataOperationEventCreator.java index 42bad89f52..d74abb9935 100644 --- a/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/utils/data/operation/DataOperationEventCreator.java +++ b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/impl/data/utils/DataOperationEventCreator.java @@ -18,7 +18,7 @@ * ============LICENSE_END========================================================= */ -package org.onap.cps.ncmp.api.impl.utils.data.operation; +package org.onap.cps.ncmp.impl.data.utils; import io.cloudevents.CloudEvent; import java.util.ArrayList; @@ -29,11 +29,11 @@ import lombok.AccessLevel; import lombok.NoArgsConstructor; import lombok.extern.slf4j.Slf4j; import org.onap.cps.ncmp.api.NcmpResponseStatus; -import org.onap.cps.ncmp.api.impl.events.NcmpEvent; -import org.onap.cps.ncmp.api.impl.operations.DmiDataOperation; import org.onap.cps.ncmp.events.async1_0_0.Data; import org.onap.cps.ncmp.events.async1_0_0.DataOperationEvent; import org.onap.cps.ncmp.events.async1_0_0.Response; +import org.onap.cps.ncmp.impl.data.models.DmiDataOperation; +import org.onap.cps.ncmp.utils.events.NcmpEvent; import org.springframework.util.MultiValueMap; @Slf4j diff --git a/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/utils/data/operation/ResourceDataOperationRequestUtils.java b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/impl/data/utils/DmiDataOperationsHelper.java index 4b016b37d1..3104be5539 100644 --- a/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/utils/data/operation/ResourceDataOperationRequestUtils.java +++ b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/impl/data/utils/DmiDataOperationsHelper.java @@ -18,7 +18,7 @@ * ============LICENSE_END========================================================= */ -package org.onap.cps.ncmp.api.impl.utils.data.operation; +package org.onap.cps.ncmp.impl.data.utils; import static org.onap.cps.ncmp.api.NcmpResponseStatus.CM_HANDLES_NOT_FOUND; import static org.onap.cps.ncmp.api.NcmpResponseStatus.CM_HANDLES_NOT_READY; @@ -31,27 +31,26 @@ import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.Set; -import java.util.concurrent.TimeoutException; import java.util.stream.Collectors; import lombok.AccessLevel; import lombok.NoArgsConstructor; import lombok.extern.slf4j.Slf4j; import org.onap.cps.events.EventsPublisher; import org.onap.cps.ncmp.api.NcmpResponseStatus; -import org.onap.cps.ncmp.api.impl.inventory.CmHandleState; -import org.onap.cps.ncmp.api.impl.operations.CmHandle; -import org.onap.cps.ncmp.api.impl.operations.DmiDataOperation; -import org.onap.cps.ncmp.api.impl.utils.DmiServiceNameOrganizer; -import org.onap.cps.ncmp.api.impl.utils.context.CpsApplicationContext; -import org.onap.cps.ncmp.api.impl.yangmodels.YangModelCmHandle; -import org.onap.cps.ncmp.api.models.DataOperationDefinition; -import org.onap.cps.ncmp.api.models.DataOperationRequest; +import org.onap.cps.ncmp.api.data.models.DataOperationDefinition; +import org.onap.cps.ncmp.api.data.models.DataOperationRequest; +import org.onap.cps.ncmp.config.CpsApplicationContext; +import org.onap.cps.ncmp.impl.data.models.DmiDataOperation; +import org.onap.cps.ncmp.impl.data.models.DmiOperationCmHandle; +import org.onap.cps.ncmp.impl.dmi.DmiServiceNameOrganizer; +import org.onap.cps.ncmp.impl.inventory.models.CmHandleState; +import org.onap.cps.ncmp.impl.inventory.models.YangModelCmHandle; import org.springframework.util.LinkedMultiValueMap; import org.springframework.util.MultiValueMap; -@Slf4j @NoArgsConstructor(access = AccessLevel.PRIVATE) -public class ResourceDataOperationRequestUtils { +@Slf4j +public class DmiDataOperationsHelper { private static final String UNKNOWN_SERVICE_NAME = null; @@ -81,6 +80,8 @@ public class ResourceDataOperationRequestUtils { final Map<String, String> dmiServiceNamesPerCmHandleId = getDmiServiceNamesPerCmHandleId(dmiPropertiesPerCmHandleIdPerServiceName); + final Map<String, String> moduleSetTagPerCmHandle = getModuleSetTagPerCmHandleId(yangModelCmHandles); + for (final DataOperationDefinition dataOperationDefinitionIn : dataOperationRequestIn.getDataOperationDefinitions()) { final List<String> nonExistingCmHandleIds = new ArrayList<>(); @@ -97,9 +98,10 @@ public class ResourceDataOperationRequestUtils { } else { final DmiDataOperation dmiBatchOperationOut = getOrAddDmiBatchOperation(dmiServiceName, dataOperationDefinitionIn, dmiDataOperationsOutPerDmiServiceName); - final CmHandle cmHandle = CmHandle.buildCmHandleWithProperties(cmHandleId, - cmHandleIdProperties); - dmiBatchOperationOut.getCmHandles().add(cmHandle); + final DmiOperationCmHandle dmiOperationCmHandle = DmiOperationCmHandle + .buildDmiOperationCmHandle(cmHandleId, cmHandleIdProperties, + moduleSetTagPerCmHandle.get(cmHandleId)); + dmiBatchOperationOut.getCmHandles().add(dmiOperationCmHandle); } } } @@ -114,56 +116,12 @@ public class ResourceDataOperationRequestUtils { return dmiDataOperationsOutPerDmiServiceName; } - /** - * Handles the async task completion for an entire data, publishing errors to client topic on task failure. - * - * @param topicParamInQuery client given topic - * @param requestId unique identifier per request - * @param dataOperationRequest incoming data operation request details - * @param throwable error cause, or null if task completed with no exception - */ - public static void handleAsyncTaskCompletionForDataOperationsRequest( - final String topicParamInQuery, - final String requestId, - final DataOperationRequest dataOperationRequest, - final Throwable throwable) { - if (throwable == null) { - log.info("Data operations request {} completed.", requestId); - } else if (throwable instanceof TimeoutException) { - log.error("Data operations request {} timed out.", requestId); - ResourceDataOperationRequestUtils.publishErrorMessageToClientTopicForEntireOperation(topicParamInQuery, - requestId, dataOperationRequest, NcmpResponseStatus.DMI_SERVICE_NOT_RESPONDING); - } else { - log.error("Data operations request {} failed.", requestId, throwable); - ResourceDataOperationRequestUtils.publishErrorMessageToClientTopicForEntireOperation(topicParamInQuery, - requestId, dataOperationRequest, NcmpResponseStatus.UNKNOWN_ERROR); - } - } - - /** - * Creates data operation cloud event for when the entire data operation fails and publishes it to client topic. - * - * @param topicParamInQuery client given topic - * @param requestId unique identifier per request - * @param dataOperationRequestIn incoming data operation request details - * @param ncmpResponseStatus response code to be sent for all cm handle ids in all operations - */ - private static void publishErrorMessageToClientTopicForEntireOperation( - final String topicParamInQuery, - final String requestId, - final DataOperationRequest dataOperationRequestIn, - final NcmpResponseStatus ncmpResponseStatus) { - - final MultiValueMap<DmiDataOperation, Map<NcmpResponseStatus, List<String>>> - cmHandleIdsPerResponseCodesPerOperation = new LinkedMultiValueMap<>(); - - for (final DataOperationDefinition dataOperationDefinitionIn : - dataOperationRequestIn.getDataOperationDefinitions()) { - cmHandleIdsPerResponseCodesPerOperation.add( - DmiDataOperation.buildDmiDataOperationRequestBodyWithoutCmHandles(dataOperationDefinitionIn), - Map.of(ncmpResponseStatus, dataOperationDefinitionIn.getCmHandleIds())); - } - publishErrorMessageToClientTopic(topicParamInQuery, requestId, cmHandleIdsPerResponseCodesPerOperation); + private static Map<String, String> getModuleSetTagPerCmHandleId( + final Collection<YangModelCmHandle> yangModelCmHandles) { + final Map<String, String> moduleSetTagPerCmHandle = new HashMap<>(yangModelCmHandles.size()); + yangModelCmHandles.forEach(yangModelCmHandle -> + moduleSetTagPerCmHandle.put(yangModelCmHandle.getId(), yangModelCmHandle.getModuleSetTag())); + return moduleSetTagPerCmHandle; } /** @@ -182,6 +140,8 @@ public class ResourceDataOperationRequestUtils { final CloudEvent dataOperationCloudEvent = DataOperationEventCreator.createDataOperationEvent(clientTopic, requestId, cmHandleIdsPerResponseCodesPerOperation); final EventsPublisher<CloudEvent> eventsPublisher = CpsApplicationContext.getCpsBean(EventsPublisher.class); + log.warn("publishing error message to client topic: {} ,requestId: {}, data operation cloud event id: {}", + clientTopic, requestId, dataOperationCloudEvent.getId()); eventsPublisher.publishCloudEvent(clientTopic, requestId, dataOperationCloudEvent); } } diff --git a/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/impl/datajobs/DataJobResultServiceImpl.java b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/impl/datajobs/DataJobResultServiceImpl.java new file mode 100644 index 0000000000..8934c088a1 --- /dev/null +++ b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/impl/datajobs/DataJobResultServiceImpl.java @@ -0,0 +1,55 @@ +/* + * ============LICENSE_START======================================================= + * Copyright (C) 2024 Nordix Foundation + * ================================================================================ + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * SPDX-License-Identifier: Apache-2.0 + * ============LICENSE_END========================================================= + */ + +package org.onap.cps.ncmp.impl.datajobs; + +import lombok.RequiredArgsConstructor; +import org.onap.cps.ncmp.api.datajobs.DataJobResultService; +import org.onap.cps.ncmp.impl.dmi.DmiProperties; +import org.onap.cps.ncmp.impl.dmi.DmiRestClient; +import org.onap.cps.ncmp.impl.utils.http.RestServiceUrlTemplateBuilder; +import org.onap.cps.ncmp.impl.utils.http.UrlTemplateParameters; +import org.springframework.stereotype.Service; + +@Service +@RequiredArgsConstructor +public class DataJobResultServiceImpl implements DataJobResultService { + + private final DmiRestClient dmiRestClient; + private final DmiProperties dmiProperties; + + @Override + public String getDataJobResult(final String authorization, + final String dmiServiceName, + final String dataProducerId, + final String dataProducerJobId, + final String destination) { + final UrlTemplateParameters urlTemplateParameters = RestServiceUrlTemplateBuilder.newInstance() + .fixedPathSegment("cmwriteJob") + .fixedPathSegment("dataProducer") + .variablePathSegment("dataProducerId", dataProducerId) + .fixedPathSegment("dataProducerJob") + .variablePathSegment("dataProducerJobId", dataProducerJobId) + .fixedPathSegment("result") + .queryParameter("destination", destination) + .createUrlTemplateParameters(dmiServiceName, dmiProperties.getDmiBasePath()); + return dmiRestClient.getDataJobResult(urlTemplateParameters, authorization).block(); + } +} diff --git a/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/impl/datajobs/DataJobServiceImpl.java b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/impl/datajobs/DataJobServiceImpl.java new file mode 100644 index 0000000000..04c3ad2fc6 --- /dev/null +++ b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/impl/datajobs/DataJobServiceImpl.java @@ -0,0 +1,67 @@ +/* + * ============LICENSE_START======================================================= + * Copyright (C) 2024 Nordix Foundation + * ================================================================================ + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * SPDX-License-Identifier: Apache-2.0 + * ============LICENSE_END========================================================= + */ + +package org.onap.cps.ncmp.impl.datajobs; + +import java.util.List; +import java.util.Map; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.onap.cps.ncmp.api.datajobs.DataJobService; +import org.onap.cps.ncmp.api.datajobs.models.DataJobMetadata; +import org.onap.cps.ncmp.api.datajobs.models.DataJobReadRequest; +import org.onap.cps.ncmp.api.datajobs.models.DataJobWriteRequest; +import org.onap.cps.ncmp.api.datajobs.models.DmiWriteOperation; +import org.onap.cps.ncmp.api.datajobs.models.ProducerKey; +import org.onap.cps.ncmp.api.datajobs.models.SubJobWriteResponse; +import org.springframework.stereotype.Service; + +@Slf4j +@Service +@RequiredArgsConstructor +public class DataJobServiceImpl implements DataJobService { + + private final DmiSubJobRequestHandler dmiSubJobClient; + private final WriteRequestExaminer writeRequestExaminer; + + @Override + public void readDataJob(final String authorization, + final String dataJobId, + final DataJobMetadata dataJobMetadata, + final DataJobReadRequest dataJobReadRequest) { + log.info("data job id for read operation is: {}", dataJobId); + } + + @Override + public List<SubJobWriteResponse> writeDataJob(final String authorization, + final String dataJobId, + final DataJobMetadata dataJobMetadata, + final DataJobWriteRequest dataJobWriteRequest) { + log.info("data job id for write operation is: {}", dataJobId); + + final Map<ProducerKey, List<DmiWriteOperation>> dmiWriteOperationsPerProducerKey = + writeRequestExaminer.splitDmiWriteOperationsFromRequest(dataJobId, dataJobWriteRequest); + + return dmiSubJobClient.sendRequestsToDmi(authorization, + dataJobId, + dataJobMetadata, + dmiWriteOperationsPerProducerKey); + } +} diff --git a/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/impl/datajobs/DataJobStatusServiceImpl.java b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/impl/datajobs/DataJobStatusServiceImpl.java new file mode 100644 index 0000000000..1cfb8a9dff --- /dev/null +++ b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/impl/datajobs/DataJobStatusServiceImpl.java @@ -0,0 +1,66 @@ +/* + * ============LICENSE_START======================================================= + * Copyright (C) 2024 Nordix Foundation + * ================================================================================ + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * SPDX-License-Identifier: Apache-2.0 + * ============LICENSE_END========================================================= + */ + +package org.onap.cps.ncmp.impl.datajobs; + +import lombok.RequiredArgsConstructor; +import org.onap.cps.ncmp.api.datajobs.DataJobStatusService; +import org.onap.cps.ncmp.impl.dmi.DmiProperties; +import org.onap.cps.ncmp.impl.dmi.DmiRestClient; +import org.onap.cps.ncmp.impl.utils.http.RestServiceUrlTemplateBuilder; +import org.onap.cps.ncmp.impl.utils.http.UrlTemplateParameters; +import org.springframework.stereotype.Service; + +/** + * Implementation of {@link DataJobStatusService} interface. + * The operations interact with a DMI Plugin to retrieve data job statuses. + */ +@Service +@RequiredArgsConstructor +public class DataJobStatusServiceImpl implements DataJobStatusService { + + private final DmiRestClient dmiRestClient; + private final DmiProperties dmiProperties; + + @Override + public String getDataJobStatus(final String authorization, + final String dmiServiceName, + final String dataProducerId, + final String dataProducerJobId) { + + final UrlTemplateParameters urlTemplateParameters = buildUrlParameters(dmiServiceName, + dataProducerId, + dataProducerJobId); + return dmiRestClient.getDataJobStatus(urlTemplateParameters, authorization).block(); + } + + private UrlTemplateParameters buildUrlParameters(final String dmiServiceName, + final String dataProducerId, + final String dataProducerJobId) { + return RestServiceUrlTemplateBuilder.newInstance() + .fixedPathSegment("cmwriteJob") + .fixedPathSegment("dataProducer") + .variablePathSegment("dataProducerId", dataProducerId) + .fixedPathSegment("dataProducerJob") + .variablePathSegment("dataProducerJobId", dataProducerJobId) + .fixedPathSegment("status") + .createUrlTemplateParameters(dmiServiceName, dmiProperties.getDmiBasePath()); + } +} diff --git a/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/impl/datajobs/DmiSubJobRequestHandler.java b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/impl/datajobs/DmiSubJobRequestHandler.java new file mode 100644 index 0000000000..a118d53e7e --- /dev/null +++ b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/impl/datajobs/DmiSubJobRequestHandler.java @@ -0,0 +1,96 @@ +/* + * ============LICENSE_START======================================================= + * Copyright (C) 2024 Nordix Foundation + * ================================================================================ + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * SPDX-License-Identifier: Apache-2.0 + * ============LICENSE_END========================================================= + */ + +package org.onap.cps.ncmp.impl.datajobs; + +import java.util.ArrayList; +import java.util.List; +import java.util.Map; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.onap.cps.ncmp.api.data.models.OperationType; +import org.onap.cps.ncmp.api.datajobs.models.DataJobMetadata; +import org.onap.cps.ncmp.api.datajobs.models.DmiWriteOperation; +import org.onap.cps.ncmp.api.datajobs.models.ProducerKey; +import org.onap.cps.ncmp.api.datajobs.models.SubJobWriteRequest; +import org.onap.cps.ncmp.api.datajobs.models.SubJobWriteResponse; +import org.onap.cps.ncmp.impl.dmi.DmiProperties; +import org.onap.cps.ncmp.impl.dmi.DmiRestClient; +import org.onap.cps.ncmp.impl.models.RequiredDmiService; +import org.onap.cps.ncmp.impl.utils.http.RestServiceUrlTemplateBuilder; +import org.onap.cps.ncmp.impl.utils.http.UrlTemplateParameters; +import org.onap.cps.utils.JsonObjectMapper; +import org.springframework.http.ResponseEntity; +import org.springframework.stereotype.Service; + +@Slf4j +@Service +@RequiredArgsConstructor +public class DmiSubJobRequestHandler { + + private final DmiRestClient dmiRestClient; + private final DmiProperties dmiProperties; + private final JsonObjectMapper jsonObjectMapper; + + /** + * Sends sub-job write requests to the DMI Plugin. + * + * @param authorization the authorization header from the REST request + * @param dataJobId data job identifier + * @param dataJobMetadata the data job's metadata + * @param dmiWriteOperationsPerProducerKey a collection of write requests per producer key + * @return a list of sub-job write responses + */ + public List<SubJobWriteResponse> sendRequestsToDmi(final String authorization, + final String dataJobId, + final DataJobMetadata dataJobMetadata, + final Map<ProducerKey, List<DmiWriteOperation>> dmiWriteOperationsPerProducerKey) { + final List<SubJobWriteResponse> subJobWriteResponses = new ArrayList<>(dmiWriteOperationsPerProducerKey.size()); + dmiWriteOperationsPerProducerKey.forEach((producerKey, dmi3ggpWriteOperations) -> { + final SubJobWriteRequest subJobWriteRequest = new SubJobWriteRequest(dataJobMetadata.destination(), + dataJobMetadata.dataAcceptType(), + dataJobMetadata.dataContentType(), + producerKey.dataProducerIdentifier(), + dataJobId, + dmi3ggpWriteOperations); + + final UrlTemplateParameters urlTemplateParameters = getUrlTemplateParameters(dataJobMetadata.destination(), + producerKey); + final ResponseEntity<Object> responseEntity = dmiRestClient.synchronousPostOperationWithJsonData( + RequiredDmiService.DATA, + urlTemplateParameters, + jsonObjectMapper.asJsonString(subJobWriteRequest), + OperationType.CREATE, + authorization); + final SubJobWriteResponse subJobWriteResponse = jsonObjectMapper + .convertToValueType(responseEntity.getBody(), SubJobWriteResponse.class); + log.debug("Sub job write response: {}", subJobWriteResponse); + subJobWriteResponses.add(subJobWriteResponse); + }); + return subJobWriteResponses; + } + + private UrlTemplateParameters getUrlTemplateParameters(final String destination, final ProducerKey producerKey) { + return RestServiceUrlTemplateBuilder.newInstance() + .fixedPathSegment("cmwriteJob") + .queryParameter("destination", destination) + .createUrlTemplateParameters(producerKey.dmiServiceName(), dmiProperties.getDmiBasePath()); + } +} diff --git a/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/impl/datajobs/WriteRequestExaminer.java b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/impl/datajobs/WriteRequestExaminer.java new file mode 100644 index 0000000000..70d08dccdc --- /dev/null +++ b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/impl/datajobs/WriteRequestExaminer.java @@ -0,0 +1,109 @@ +/* + * ============LICENSE_START======================================================= + * Copyright (C) 2024 Nordix Foundation + * ================================================================================ + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * SPDX-License-Identifier: Apache-2.0 + * ============LICENSE_END========================================================= + */ + +package org.onap.cps.ncmp.impl.datajobs; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.LinkedHashMap; +import java.util.List; +import java.util.Map; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.onap.cps.ncmp.api.datajobs.models.DataJobWriteRequest; +import org.onap.cps.ncmp.api.datajobs.models.DmiWriteOperation; +import org.onap.cps.ncmp.api.datajobs.models.ProducerKey; +import org.onap.cps.ncmp.api.datajobs.models.WriteOperation; +import org.onap.cps.ncmp.impl.inventory.models.YangModelCmHandle; +import org.onap.cps.ncmp.impl.utils.AlternateIdMatcher; +import org.onap.cps.ncmp.impl.utils.YangDataConverter; +import org.onap.cps.spi.model.DataNode; +import org.springframework.stereotype.Service; + +@Slf4j +@Service +@RequiredArgsConstructor +public class WriteRequestExaminer { + + private final AlternateIdMatcher alternateIdMatcher; + private static final String PATH_SEPARATOR = "/"; + + /** + * Splitting incoming data job write request into Dmi Write Operations by ProducerKey. + * + * @param dataJobId data job identifier + * @param dataJobWriteRequest incoming data job write request + * @return {@code Map} map of Dmi Write Operations per Producer Key + */ + public Map<ProducerKey, List<DmiWriteOperation>> splitDmiWriteOperationsFromRequest( + final String dataJobId, + final DataJobWriteRequest dataJobWriteRequest) { + final Map<ProducerKey, List<DmiWriteOperation>> dmiWriteOperationsPerProducerKey = new HashMap<>(); + for (final WriteOperation writeOperation : dataJobWriteRequest.data()) { + examineWriteOperation(dataJobId, dmiWriteOperationsPerProducerKey, writeOperation); + } + return dmiWriteOperationsPerProducerKey; + } + + private void examineWriteOperation(final String dataJobId, + final Map<ProducerKey, List<DmiWriteOperation>> dmiWriteOperationsPerProducerKey, + final WriteOperation writeOperation) { + log.debug("data job id for write operation is: {}", dataJobId); + final DataNode dataNode = alternateIdMatcher + .getCmHandleDataNodeByLongestMatchingAlternateId(writeOperation.path(), PATH_SEPARATOR); + + final DmiWriteOperation dmiWriteOperation = createDmiWriteOperation(writeOperation, dataNode); + + final ProducerKey producerKey = createProducerKey(dataNode); + final List<DmiWriteOperation> dmiWriteOperations; + if (dmiWriteOperationsPerProducerKey.containsKey(producerKey)) { + dmiWriteOperations = dmiWriteOperationsPerProducerKey.get(producerKey); + } else { + dmiWriteOperations = new ArrayList<>(); + dmiWriteOperationsPerProducerKey.put(producerKey, dmiWriteOperations); + } + dmiWriteOperations.add(dmiWriteOperation); + } + + private ProducerKey createProducerKey(final DataNode dataNode) { + return new ProducerKey((String) dataNode.getLeaves().get("dmi-service-name"), + (String) dataNode.getLeaves().get("data-producer-identifier")); + } + + private DmiWriteOperation createDmiWriteOperation(final WriteOperation writeOperation, + final DataNode dataNode) { + return new DmiWriteOperation( + writeOperation.path(), + writeOperation.op(), + (String) dataNode.getLeaves().get("module-set-tag"), + writeOperation.value(), + writeOperation.operationId(), + getPrivatePropertiesFromDataNode(dataNode)); + } + + private Map<String, String> getPrivatePropertiesFromDataNode(final DataNode dataNode) { + final YangModelCmHandle yangModelCmHandle = YangDataConverter.toYangModelCmHandle(dataNode); + final Map<String, String> cmHandleDmiProperties = new LinkedHashMap<>(); + yangModelCmHandle.getDmiProperties() + .forEach(dmiProperty -> cmHandleDmiProperties.put(dmiProperty.getName(), dmiProperty.getValue())); + return cmHandleDmiProperties; + } + +} diff --git a/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/impl/dmi/DmiProperties.java b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/impl/dmi/DmiProperties.java new file mode 100644 index 0000000000..2f60460da3 --- /dev/null +++ b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/impl/dmi/DmiProperties.java @@ -0,0 +1,55 @@ +/* + * ============LICENSE_START======================================================= + * Copyright (C) 2024 Nordix Foundation. + * ================================================================================ + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * SPDX-License-Identifier: Apache-2.0 + * ============LICENSE_END========================================================= + */ + +package org.onap.cps.ncmp.impl.dmi; + +import lombok.AccessLevel; +import lombok.Getter; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.stereotype.Component; + +@Getter +@Component +public class DmiProperties { + @Value("${ncmp.dmi.auth.username}") + private String authUsername; + @Value("${ncmp.dmi.auth.password}") + private String authPassword; + @Getter(AccessLevel.NONE) + @Value("${ncmp.dmi.api.base-path}") + private String dmiBasePath; + @Value("${ncmp.dmi.auth.enabled}") + private boolean dmiBasicAuthEnabled; + + /** + * Removes both leading and trailing slashes if they are present. + * + * @return dmi base path without any slashes ("/") + */ + public String getDmiBasePath() { + if (dmiBasePath.startsWith("/")) { + dmiBasePath = dmiBasePath.substring(1); + } + if (dmiBasePath.endsWith("/")) { + dmiBasePath = dmiBasePath.substring(0, dmiBasePath.length() - 1); + } + return dmiBasePath; + } +} diff --git a/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/impl/dmi/DmiRestClient.java b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/impl/dmi/DmiRestClient.java new file mode 100644 index 0000000000..a177272dff --- /dev/null +++ b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/impl/dmi/DmiRestClient.java @@ -0,0 +1,225 @@ +/* + * ============LICENSE_START======================================================= + * Copyright (C) 2021-2024 Nordix Foundation + * Modifications Copyright (C) 2022 Bell Canada + * ================================================================================ + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * 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.impl.dmi; + +import static org.onap.cps.ncmp.api.NcmpResponseStatus.DMI_SERVICE_NOT_RESPONDING; +import static org.onap.cps.ncmp.api.NcmpResponseStatus.UNABLE_TO_READ_RESOURCE_DATA; +import static org.onap.cps.ncmp.api.NcmpResponseStatus.UNKNOWN_ERROR; +import static org.springframework.http.HttpStatus.INTERNAL_SERVER_ERROR; +import static org.springframework.http.HttpStatus.REQUEST_TIMEOUT; + +import com.fasterxml.jackson.databind.JsonNode; +import java.util.Locale; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.onap.cps.ncmp.api.data.models.OperationType; +import org.onap.cps.ncmp.api.exceptions.DmiClientRequestException; +import org.onap.cps.ncmp.impl.models.RequiredDmiService; +import org.onap.cps.ncmp.impl.utils.http.UrlTemplateParameters; +import org.onap.cps.utils.JsonObjectMapper; +import org.springframework.beans.factory.annotation.Qualifier; +import org.springframework.http.HttpHeaders; +import org.springframework.http.HttpStatus; +import org.springframework.http.ResponseEntity; +import org.springframework.stereotype.Component; +import org.springframework.web.client.HttpServerErrorException; +import org.springframework.web.reactive.function.BodyInserters; +import org.springframework.web.reactive.function.client.WebClient; +import org.springframework.web.reactive.function.client.WebClientRequestException; +import org.springframework.web.reactive.function.client.WebClientResponseException; +import reactor.core.publisher.Mono; + +@Component +@RequiredArgsConstructor +@Slf4j +public class DmiRestClient { + + private static final String NOT_SPECIFIED = ""; + private static final String NO_AUTHORIZATION = null; + + private final DmiProperties dmiProperties; + private final JsonObjectMapper jsonObjectMapper; + @Qualifier("dataServicesWebClient") + private final WebClient dataServicesWebClient; + @Qualifier("modelServicesWebClient") + private final WebClient modelServicesWebClient; + @Qualifier("healthChecksWebClient") + private final WebClient healthChecksWebClient; + + /** + * Sends a synchronous (blocking) POST operation to the DMI with a JSON body containing module references. + * + * @param requiredDmiService Determines if the required service is for a data or model operation. + * @param urlTemplateParameters The DMI resource URL template with variables. + * @param requestBodyAsJsonString JSON data body. + * @param operationType The type of operation being executed (for error reporting only). + * @param authorization Contents of the Authorization header, or null if not present. + * @return ResponseEntity containing the response from the DMI. + * @throws DmiClientRequestException If there is an error during the DMI request. + */ + public ResponseEntity<Object> synchronousPostOperationWithJsonData(final RequiredDmiService requiredDmiService, + final UrlTemplateParameters + urlTemplateParameters, + final String requestBodyAsJsonString, + final OperationType operationType, + final String authorization) { + final Mono<ResponseEntity<Object>> responseEntityMono = + asynchronousPostOperationWithJsonData(requiredDmiService, + urlTemplateParameters, + requestBodyAsJsonString, + operationType, + authorization); + return responseEntityMono.block(); + } + + /** + * Asynchronously performs an HTTP POST operation with the given JSON data. + * + * @param requiredDmiService The service object required for retrieving or configuring the WebClient. + * @param urlTemplateParameters The URL template with variables for the POST request. + * @param requestBodyAsJsonString The JSON string that will be sent as the request body. + * @param operationType An enumeration or object that holds information about the type of operation + * being performed. + * @param authorization The authorization token to be added to the request headers. + * @return A Mono emitting the response entity containing the server's response. + */ + public Mono<ResponseEntity<Object>> asynchronousPostOperationWithJsonData(final RequiredDmiService + requiredDmiService, + final UrlTemplateParameters + urlTemplateParameters, + final String requestBodyAsJsonString, + final OperationType operationType, + final String authorization) { + final WebClient webClient = getWebClient(requiredDmiService); + return webClient.post() + .uri(urlTemplateParameters.urlTemplate(), urlTemplateParameters.urlVariables()) + .headers(httpHeaders -> configureHttpHeaders(httpHeaders, authorization)) + .body(BodyInserters.fromValue(requestBodyAsJsonString)) + .retrieve() + .toEntity(Object.class) + .onErrorMap(throwable -> handleDmiClientException(throwable, operationType.getOperationName())); + } + + /** + * Retrieves the health status of the DMI plugin. + * This method performs an HTTP GET request to the DMI health check endpoint specified by the URL template + * parameters. If the response status code indicates a client error (4xx) or a server error (5xx), it logs a warning + * and returns an empty Mono. In case of an error during the request, it logs the exception and returns a default + * value of "NOT_SPECIFIED". If the response body contains a JSON node with a "status" field, the value of this + * field is returned. + * + * @param urlTemplateParameters the URL template parameters for the DMI health check endpoint + * @return a Mono emitting the health status as a String, or "NOT_SPECIFIED" if an error occurs + */ + public Mono<String> getDmiHealthStatus(final UrlTemplateParameters urlTemplateParameters) { + return healthChecksWebClient.get() + .uri(urlTemplateParameters.urlTemplate(), urlTemplateParameters.urlVariables()) + .headers(httpHeaders -> configureHttpHeaders(httpHeaders, NO_AUTHORIZATION)) + .retrieve() + .bodyToMono(JsonNode.class) + .map(responseHealthStatus -> responseHealthStatus.path("status").asText()) + .onErrorResume(Exception.class, ex -> { + log.warn("Failed to retrieve health status from {}. Status: {}", + urlTemplateParameters.urlTemplate(), ex.getMessage()); + return Mono.empty(); + }) + .defaultIfEmpty(NOT_SPECIFIED); + } + + /** + * Retrieves the status of a data job from the DMI service. + * + * @param urlTemplateParameters The URL template parameters for the DMI data job status endpoint. + * @param authorization The authorization token to be added to the request headers. + * @return A Mono emitting the status of the data job as a String. + * @throws DmiClientRequestException If there is an error during the DMI request. + */ + public Mono<String> getDataJobStatus(final UrlTemplateParameters urlTemplateParameters, + final String authorization) { + + return dataServicesWebClient.get() + .uri(urlTemplateParameters.urlTemplate(), urlTemplateParameters.urlVariables()) + .headers(httpHeaders -> configureHttpHeaders(httpHeaders, authorization)) + .retrieve() + .bodyToMono(JsonNode.class) + .map(jsonNode -> jsonNode.path("status").asText()) + .onErrorMap(throwable -> handleDmiClientException(throwable, OperationType.READ.getOperationName())); + } + + /** + * Retrieves the result of a data job from the DMI service. + * + * @param urlTemplateParameters The URL template parameters for the DMI data job status endpoint. + * @param authorization The authorization token to be added to the request headers. + * @return A Mono emitting the result of the data job as a String. + * @throws DmiClientRequestException If there is an error during the DMI request. + */ + public Mono<String> getDataJobResult(final UrlTemplateParameters urlTemplateParameters, + final String authorization) { + return dataServicesWebClient.get() + .uri(urlTemplateParameters.urlTemplate(), urlTemplateParameters.urlVariables()) + .headers(httpHeaders -> configureHttpHeaders(httpHeaders, authorization)) + .retrieve().bodyToMono(String.class) + .onErrorMap(throwable -> handleDmiClientException(throwable, + OperationType.READ.getOperationName())); + } + + private WebClient getWebClient(final RequiredDmiService requiredDmiService) { + return requiredDmiService.equals(RequiredDmiService.DATA) ? dataServicesWebClient : modelServicesWebClient; + } + + private void configureHttpHeaders(final HttpHeaders httpHeaders, final String authorization) { + if (dmiProperties.isDmiBasicAuthEnabled()) { + httpHeaders.setBasicAuth(dmiProperties.getAuthUsername(), dmiProperties.getAuthPassword()); + } else if (authorization != null && authorization.toLowerCase(Locale.getDefault()).startsWith("bearer ")) { + httpHeaders.add(HttpHeaders.AUTHORIZATION, authorization); + } + } + + private DmiClientRequestException handleDmiClientException(final Throwable throwable, final String operationType) { + if (throwable instanceof WebClientResponseException webClientResponseException) { + if (webClientResponseException.getStatusCode().isSameCodeAs(REQUEST_TIMEOUT)) { + throw new DmiClientRequestException(webClientResponseException.getStatusCode().value(), + webClientResponseException.getMessage(), + jsonObjectMapper.asJsonString(webClientResponseException.getResponseBodyAsString()), + DMI_SERVICE_NOT_RESPONDING); + } + throw new DmiClientRequestException(webClientResponseException.getStatusCode().value(), + webClientResponseException.getMessage(), + jsonObjectMapper.asJsonString(webClientResponseException.getResponseBodyAsString()), + UNABLE_TO_READ_RESOURCE_DATA); + + } + final String exceptionMessage = "Unable to " + operationType + " resource data."; + if (throwable instanceof WebClientRequestException webClientRequestException) { + throw new DmiClientRequestException(HttpStatus.SERVICE_UNAVAILABLE.value(), + webClientRequestException.getMessage(), + exceptionMessage, DMI_SERVICE_NOT_RESPONDING); + } + if (throwable instanceof HttpServerErrorException httpServerErrorException) { + throw new DmiClientRequestException(httpServerErrorException.getStatusCode().value(), exceptionMessage, + httpServerErrorException.getResponseBodyAsString(), DMI_SERVICE_NOT_RESPONDING); + } + throw new DmiClientRequestException(INTERNAL_SERVER_ERROR.value(), exceptionMessage, throwable.getMessage(), + UNKNOWN_ERROR); + } +} diff --git a/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/utils/DmiServiceNameOrganizer.java b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/impl/dmi/DmiServiceNameOrganizer.java index 26e94866a1..37e982d8cb 100644 --- a/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/utils/DmiServiceNameOrganizer.java +++ b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/impl/dmi/DmiServiceNameOrganizer.java @@ -18,7 +18,7 @@ * ============LICENSE_END========================================================= */ -package org.onap.cps.ncmp.api.impl.utils; +package org.onap.cps.ncmp.impl.dmi; import java.util.Collection; import java.util.HashMap; @@ -27,8 +27,8 @@ import java.util.Map; import java.util.stream.Collectors; import lombok.AccessLevel; import lombok.NoArgsConstructor; -import org.onap.cps.ncmp.api.impl.operations.RequiredDmiService; -import org.onap.cps.ncmp.api.impl.yangmodels.YangModelCmHandle; +import org.onap.cps.ncmp.impl.inventory.models.YangModelCmHandle; +import org.onap.cps.ncmp.impl.models.RequiredDmiService; @NoArgsConstructor(access = AccessLevel.PRIVATE) public class DmiServiceNameOrganizer { diff --git a/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/impl/dmi/DmiWebClientsConfiguration.java b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/impl/dmi/DmiWebClientsConfiguration.java new file mode 100644 index 0000000000..4134a56ead --- /dev/null +++ b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/impl/dmi/DmiWebClientsConfiguration.java @@ -0,0 +1,68 @@ +/* + * ============LICENSE_START======================================================= + * Copyright (C) 2024 Nordix Foundation. + * ================================================================================ + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * SPDX-License-Identifier: Apache-2.0 + * ============LICENSE_END========================================================= + */ + +package org.onap.cps.ncmp.impl.dmi; + +import lombok.RequiredArgsConstructor; +import org.onap.cps.ncmp.config.DmiHttpClientConfig; +import org.onap.cps.ncmp.impl.utils.http.WebClientConfiguration; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.web.reactive.function.client.WebClient; + +@Configuration +@RequiredArgsConstructor +public class DmiWebClientsConfiguration extends WebClientConfiguration { + + private final DmiHttpClientConfig dmiHttpClientConfig; + + /** + * Configures and creates a web client bean for DMI data services. + * + * @param webClientBuilder The builder instance to create the WebClient. + * @return a WebClient instance configured for data services. + */ + @Bean + public WebClient dataServicesWebClient(final WebClient.Builder webClientBuilder) { + return configureWebClient(webClientBuilder, dmiHttpClientConfig.getDataServices()); + } + + /** + * Configures and creates a web client bean for DMI model services. + * + * @param webClientBuilder The builder instance to create the WebClient. + * @return a WebClient instance configured for model services. + */ + @Bean + public WebClient modelServicesWebClient(final WebClient.Builder webClientBuilder) { + return configureWebClient(webClientBuilder, dmiHttpClientConfig.getModelServices()); + } + + /** + * Configures and creates a web client bean for DMI health check services. + * + * @param webClientBuilder The builder instance to create the WebClient. + * @return a WebClient instance configured for health check services. + */ + @Bean + public WebClient healthChecksWebClient(final WebClient.Builder webClientBuilder) { + return configureWebClient(webClientBuilder, dmiHttpClientConfig.getHealthCheckServices()); + } +} diff --git a/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/impl/inventory/AlternateIdChecker.java b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/impl/inventory/AlternateIdChecker.java new file mode 100644 index 0000000000..3600d6da32 --- /dev/null +++ b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/impl/inventory/AlternateIdChecker.java @@ -0,0 +1,115 @@ +/* + * ============LICENSE_START======================================================== + * Copyright (c) 2024 Nordix Foundation. + * ================================================================================ + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an 'AS IS' BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * SPDX-License-Identifier: Apache-2.0 + * ============LICENSE_END========================================================= + */ + +package org.onap.cps.ncmp.impl.inventory; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.Set; +import java.util.stream.Collectors; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.apache.commons.lang3.StringUtils; +import org.onap.cps.ncmp.api.inventory.models.NcmpServiceCmHandle; +import org.onap.cps.spi.exceptions.DataNodeNotFoundException; +import org.springframework.stereotype.Service; + +@Service +@Slf4j +@RequiredArgsConstructor +public class AlternateIdChecker { + + public enum Operation { + CREATE, UPDATE + } + + private final InventoryPersistence inventoryPersistence; + + private static final String NO_CURRENT_ALTERNATE_ID = ""; + + /** + * Check all alternate ids of a batch of cm handles. + * Includes cross-checks in the batch itself for duplicates. Only the first entry encountered wil be accepted. + * + * @param newNcmpServiceCmHandles the proposed new cm handles + * @param operation type of operation being executed + * @return collection of cm handles ids which are acceptable + */ + public Collection<String> getIdsOfCmHandlesWithRejectedAlternateId( + final Collection<NcmpServiceCmHandle> newNcmpServiceCmHandles, + final Operation operation) { + final Set<String> assignedAlternateIds = getAlternateIdsAlreadyInDb(newNcmpServiceCmHandles); + final Collection<String> rejectedCmHandleIds = new ArrayList<>(); + for (final NcmpServiceCmHandle ncmpServiceCmHandle : newNcmpServiceCmHandles) { + final String cmHandleId = ncmpServiceCmHandle.getCmHandleId(); + final String proposedAlternateId = ncmpServiceCmHandle.getAlternateId(); + if (isProposedAlternateIdAcceptable(proposedAlternateId, operation, assignedAlternateIds, cmHandleId)) { + assignedAlternateIds.add(proposedAlternateId); + } else { + rejectedCmHandleIds.add(cmHandleId); + } + } + return rejectedCmHandleIds; + } + + private boolean isProposedAlternateIdAcceptable(final String proposedAlternateId, final Operation operation, + final Set<String> assignedAlternateIds, final String cmHandleId) { + if (StringUtils.isBlank(proposedAlternateId)) { + return true; + } + final String currentAlternateId = getCurrentAlternateId(operation, cmHandleId); + if (currentAlternateId.equals(proposedAlternateId)) { + return true; + } + if (StringUtils.isNotBlank(currentAlternateId)) { + log.warn("Alternate id update ignored, cannot update cm handle {}, already has an alternate id of {}", + cmHandleId, currentAlternateId); + return false; + } + if (assignedAlternateIds.contains(proposedAlternateId)) { + log.warn("Alternate id update ignored, cannot update cm handle {}, alternate id is already " + + "assigned to a different cm handle", cmHandleId); + return false; + } + return true; + } + + private Set<String> getAlternateIdsAlreadyInDb(final Collection<NcmpServiceCmHandle> ncmpServiceCmHandles) { + final Set<String> alternateIdsToCheck = ncmpServiceCmHandles.stream() + .map(NcmpServiceCmHandle::getAlternateId) + .filter(StringUtils::isNotBlank) + .collect(Collectors.toSet()); + return inventoryPersistence.getCmHandleDataNodesByAlternateIds(alternateIdsToCheck).stream() + .map(dataNode -> (String) dataNode.getLeaves().get("alternate-id")) + .collect(Collectors.toSet()); + } + + private String getCurrentAlternateId(final Operation operation, final String cmHandleId) { + if (operation == Operation.UPDATE) { + try { + return inventoryPersistence.getYangModelCmHandle(cmHandleId).getAlternateId(); + } catch (final DataNodeNotFoundException dataNodeNotFoundException) { + // work with blank current alternate id + } + } + return NO_CURRENT_ALTERNATE_ID; + } + +} diff --git a/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/utils/RestQueryParametersValidator.java b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/impl/inventory/CmHandleQueryParametersValidator.java index 2ef97cab5c..08954024b7 100644 --- a/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/utils/RestQueryParametersValidator.java +++ b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/impl/inventory/CmHandleQueryParametersValidator.java @@ -18,7 +18,7 @@ * ============LICENSE_END========================================================= */ -package org.onap.cps.ncmp.api.impl.utils; +package org.onap.cps.ncmp.impl.inventory; import com.google.common.base.Strings; import java.util.Collection; @@ -26,12 +26,12 @@ import java.util.Map; import lombok.AccessLevel; import lombok.NoArgsConstructor; import lombok.extern.slf4j.Slf4j; -import org.onap.cps.ncmp.api.models.CmHandleQueryServiceParameters; +import org.onap.cps.ncmp.api.inventory.models.CmHandleQueryServiceParameters; import org.onap.cps.spi.exceptions.DataValidationException; @Slf4j @NoArgsConstructor(access = AccessLevel.PRIVATE) -public class RestQueryParametersValidator { +public class CmHandleQueryParametersValidator { /** * Validate query parameters. @@ -55,7 +55,7 @@ public class RestQueryParametersValidator { "Empty 'conditionsParameters' - please supply a valid condition parameter."); } cmHandleQueryParameter.getConditionParameters().forEach( - RestQueryParametersValidator::validateConditionParameter + CmHandleQueryParametersValidator::validateConditionParameter ); } ); diff --git a/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/inventory/CmHandleQueries.java b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/impl/inventory/CmHandleQueryService.java index 81467dbb3e..95c3c77b71 100644 --- a/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/inventory/CmHandleQueries.java +++ b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/impl/inventory/CmHandleQueryService.java @@ -1,6 +1,6 @@ /* * ============LICENSE_START======================================================= - * Copyright (C) 2022-2023 Nordix Foundation + * Copyright (C) 2022-2024 Nordix Foundation * ================================================================================ * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -18,26 +18,26 @@ * ============LICENSE_END========================================================= */ -package org.onap.cps.ncmp.api.impl.inventory; +package org.onap.cps.ncmp.impl.inventory; import java.util.Collection; -import java.util.List; import java.util.Map; +import org.onap.cps.ncmp.impl.inventory.models.CmHandleState; import org.onap.cps.spi.FetchDescendantsOption; import org.onap.cps.spi.model.DataNode; -public interface CmHandleQueries { +public interface CmHandleQueryService { /** - * Query CmHandles based on additional (private) properties. + * Query Cm Handles based on additional (private) properties. * * @param additionalPropertyQueryPairs private properties for query - * @return Ids of CmHandles which have these private properties + * @return Ids of Cm Handles which have these private properties */ Collection<String> queryCmHandleAdditionalProperties(Map<String, String> additionalPropertyQueryPairs); /** - * Query CmHandles based on public properties. + * Query Cm Handles based on public properties. * * @param publicPropertyQueryPairs public properties for query * @return CmHandles which have these public properties @@ -45,10 +45,10 @@ public interface CmHandleQueries { Collection<String> queryCmHandlePublicProperties(Map<String, String> publicPropertyQueryPairs); /** - * Query CmHandles based on Trust Level. + * Query Cm Handles based on Trust Level. * * @param trustLevelPropertyQueryPairs trust level properties for query - * @return CmHandles which have desired trust level + * @return Ids of Cm Handles which have desired trust level */ Collection<String> queryCmHandlesByTrustLevel(Map<String, String> trustLevelPropertyQueryPairs); @@ -56,9 +56,9 @@ public interface CmHandleQueries { * Method which returns cm handles by the cm handles state. * * @param cmHandleState cm handle state - * @return a list of cm handles + * @return a list of data nodes representing the cm handles. */ - List<DataNode> queryCmHandlesByState(CmHandleState cmHandleState); + Collection<DataNode> queryCmHandlesByState(CmHandleState cmHandleState); /** * Method to return data nodes with ancestor representing the cm handles. @@ -66,8 +66,7 @@ public interface CmHandleQueries { * @param cpsPath cps path for which the cmHandle is requested * @return a list of data nodes representing the cm handles. */ - List<DataNode> queryCmHandleAncestorsByCpsPath(String cpsPath, - FetchDescendantsOption fetchDescendantsOption); + Collection<DataNode> queryCmHandleAncestorsByCpsPath(String cpsPath, FetchDescendantsOption fetchDescendantsOption); /** * Method to return data nodes representing the cm handles. @@ -75,7 +74,7 @@ public interface CmHandleQueries { * @param cpsPath cps path for which the cmHandle is requested * @return a list of data nodes representing the cm handles. */ - List<DataNode> queryNcmpRegistryByCpsPath(String cpsPath, FetchDescendantsOption fetchDescendantsOption); + Collection<DataNode> queryNcmpRegistryByCpsPath(String cpsPath, FetchDescendantsOption fetchDescendantsOption); /** * Method to check the state of a cm handle with given id. @@ -90,15 +89,16 @@ public interface CmHandleQueries { * Method which returns cm handles by the operational sync state of cm handle. * * @param dataStoreSyncState sync state - * @return a list of cm handles + * @return a list of data nodes representing the cm handles. */ - List<DataNode> queryCmHandlesByOperationalSyncState(DataStoreSyncState dataStoreSyncState); + Collection<DataNode> queryCmHandlesByOperationalSyncState(DataStoreSyncState dataStoreSyncState); /** * Get all cm handles ids by DMI plugin identifier. * * @param dmiPluginIdentifier DMI plugin identifier - * @return collection of cm handles + * @return collection of cm handle ids */ Collection<String> getCmHandleIdsByDmiPluginIdentifier(String dmiPluginIdentifier); + } diff --git a/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/inventory/CmHandleQueriesImpl.java b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/impl/inventory/CmHandleQueryServiceImpl.java index 6cffb4d274..71e7384208 100644 --- a/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/inventory/CmHandleQueriesImpl.java +++ b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/impl/inventory/CmHandleQueryServiceImpl.java @@ -19,38 +19,42 @@ * ============LICENSE_END========================================================= */ -package org.onap.cps.ncmp.api.impl.inventory; +package org.onap.cps.ncmp.impl.inventory; -import static org.onap.cps.ncmp.api.impl.ncmppersistence.NcmpPersistence.NCMP_DATASPACE_NAME; -import static org.onap.cps.ncmp.api.impl.ncmppersistence.NcmpPersistence.NCMP_DMI_REGISTRY_ANCHOR; -import static org.onap.cps.ncmp.api.impl.ncmppersistence.NcmpPersistence.NCMP_DMI_REGISTRY_PARENT; +import static org.onap.cps.ncmp.impl.inventory.NcmpPersistence.NCMP_DATASPACE_NAME; +import static org.onap.cps.ncmp.impl.inventory.NcmpPersistence.NCMP_DMI_REGISTRY_ANCHOR; +import static org.onap.cps.ncmp.impl.inventory.NcmpPersistence.NCMP_DMI_REGISTRY_PARENT; import static org.onap.cps.spi.FetchDescendantsOption.INCLUDE_ALL_DESCENDANTS; import static org.onap.cps.spi.FetchDescendantsOption.OMIT_DESCENDANTS; import java.util.Collection; import java.util.Collections; import java.util.HashSet; -import java.util.List; import java.util.Map; import java.util.stream.Collectors; import lombok.RequiredArgsConstructor; -import org.onap.cps.ncmp.api.impl.config.embeddedcache.TrustLevelCacheConfig; -import org.onap.cps.ncmp.api.impl.inventory.enums.PropertyType; -import org.onap.cps.ncmp.api.impl.trustlevel.TrustLevel; -import org.onap.cps.spi.CpsDataPersistenceService; +import org.onap.cps.api.CpsDataService; +import org.onap.cps.api.CpsQueryService; +import org.onap.cps.cpspath.parser.CpsPathUtil; +import org.onap.cps.impl.utils.CpsValidator; +import org.onap.cps.ncmp.api.inventory.models.TrustLevel; +import org.onap.cps.ncmp.impl.inventory.models.CmHandleState; +import org.onap.cps.ncmp.impl.inventory.models.ModelledDmiServiceLeaves; +import org.onap.cps.ncmp.impl.inventory.models.PropertyType; +import org.onap.cps.ncmp.impl.inventory.trustlevel.TrustLevelCacheConfig; import org.onap.cps.spi.FetchDescendantsOption; import org.onap.cps.spi.model.DataNode; -import org.onap.cps.spi.utils.CpsValidator; import org.springframework.beans.factory.annotation.Qualifier; import org.springframework.stereotype.Component; @RequiredArgsConstructor @Component -public class CmHandleQueriesImpl implements CmHandleQueries { +public class CmHandleQueryServiceImpl implements CmHandleQueryService { private static final String DESCENDANT_PATH = "//"; private static final String ANCESTOR_CM_HANDLES = "/ancestor::cm-handles"; - private final CpsDataPersistenceService cpsDataPersistenceService; + private final CpsDataService cpsDataService; + private final CpsQueryService cpsQueryService; @Qualifier(TrustLevelCacheConfig.TRUST_LEVEL_PER_DMI_PLUGIN) private final Map<String, TrustLevel> trustLevelPerDmiPlugin; @@ -79,21 +83,24 @@ public class CmHandleQueriesImpl implements CmHandleQueries { } @Override - public List<DataNode> queryCmHandlesByState(final CmHandleState cmHandleState) { + public Collection<DataNode> queryCmHandlesByState(final CmHandleState cmHandleState) { return queryCmHandleAncestorsByCpsPath("//state[@cm-handle-state=\"" + cmHandleState + "\"]", INCLUDE_ALL_DESCENDANTS); } @Override - public List<DataNode> queryNcmpRegistryByCpsPath(final String cpsPath, + public Collection<DataNode> queryNcmpRegistryByCpsPath(final String cpsPath, final FetchDescendantsOption fetchDescendantsOption) { - return cpsDataPersistenceService.queryDataNodes(NCMP_DATASPACE_NAME, NCMP_DMI_REGISTRY_ANCHOR, - cpsPath, fetchDescendantsOption); + return cpsQueryService.queryDataNodes(NCMP_DATASPACE_NAME, NCMP_DMI_REGISTRY_ANCHOR, cpsPath, + fetchDescendantsOption); } @Override - public List<DataNode> queryCmHandleAncestorsByCpsPath(final String cpsPath, + public Collection<DataNode> queryCmHandleAncestorsByCpsPath(final String cpsPath, final FetchDescendantsOption fetchDescendantsOption) { + if (CpsPathUtil.getCpsPathQuery(cpsPath).getXpathPrefix().endsWith("/cm-handles")) { + return queryNcmpRegistryByCpsPath(cpsPath, fetchDescendantsOption); + } return queryNcmpRegistryByCpsPath(cpsPath + ANCESTOR_CM_HANDLES, fetchDescendantsOption); } @@ -105,7 +112,7 @@ public class CmHandleQueriesImpl implements CmHandleQueries { } @Override - public List<DataNode> queryCmHandlesByOperationalSyncState(final DataStoreSyncState dataStoreSyncState) { + public Collection<DataNode> queryCmHandlesByOperationalSyncState(final DataStoreSyncState dataStoreSyncState) { return queryCmHandleAncestorsByCpsPath("//state/datastores" + "/operational[@sync-state=\"" + dataStoreSyncState + "\"]", FetchDescendantsOption.OMIT_DESCENDANTS); } @@ -174,9 +181,9 @@ public class CmHandleQueriesImpl implements CmHandleQueries { return cmHandleIds; } - private List<DataNode> getCmHandlesByDmiPluginIdentifierAndDmiProperty(final String dmiPluginIdentifier, - final String dmiProperty) { - return cpsDataPersistenceService.queryDataNodes(NCMP_DATASPACE_NAME, NCMP_DMI_REGISTRY_ANCHOR, + private Collection<DataNode> getCmHandlesByDmiPluginIdentifierAndDmiProperty(final String dmiPluginIdentifier, + final String dmiProperty) { + return cpsQueryService.queryDataNodes(NCMP_DATASPACE_NAME, NCMP_DMI_REGISTRY_ANCHOR, NCMP_DMI_REGISTRY_PARENT + "/cm-handles[@" + dmiProperty + "='" + dmiPluginIdentifier + "']", OMIT_DESCENDANTS); } @@ -184,7 +191,7 @@ public class CmHandleQueriesImpl implements CmHandleQueries { private DataNode getCmHandleState(final String cmHandleId) { cpsValidator.validateNameCharacters(cmHandleId); final String xpath = NCMP_DMI_REGISTRY_PARENT + "/cm-handles[@id='" + cmHandleId + "']/state"; - return cpsDataPersistenceService.getDataNodes(NCMP_DATASPACE_NAME, NCMP_DMI_REGISTRY_ANCHOR, + return cpsDataService.getDataNodes(NCMP_DATASPACE_NAME, NCMP_DMI_REGISTRY_ANCHOR, xpath, OMIT_DESCENDANTS).iterator().next(); } } 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/impl/inventory/CmHandleRegistrationService.java index 6aa09767be..d9f7e38993 100755..100644 --- 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/impl/inventory/CmHandleRegistrationService.java @@ -1,7 +1,6 @@ /* * ============LICENSE_START======================================================= - * Copyright (C) 2021 highstreet technologies GmbH - * Modifications Copyright (C) 2021-2024 Nordix Foundation + * Copyright (C) 2021-2024 Nordix Foundation * Modifications Copyright (C) 2021 Pantheon.tech * Modifications Copyright (C) 2021-2022 Bell Canada * Modifications Copyright (C) 2023 TechMahindra Ltd. @@ -22,23 +21,23 @@ * ============LICENSE_END========================================================= */ -package org.onap.cps.ncmp.api.impl; +package org.onap.cps.ncmp.impl.inventory; import static org.onap.cps.ncmp.api.NcmpResponseStatus.ALTERNATE_ID_ALREADY_ASSOCIATED; import static org.onap.cps.ncmp.api.NcmpResponseStatus.CM_HANDLES_NOT_FOUND; import static org.onap.cps.ncmp.api.NcmpResponseStatus.CM_HANDLES_NOT_READY; import static org.onap.cps.ncmp.api.NcmpResponseStatus.CM_HANDLE_ALREADY_EXIST; import static org.onap.cps.ncmp.api.NcmpResponseStatus.CM_HANDLE_INVALID_ID; -import static org.onap.cps.ncmp.api.impl.inventory.LockReasonCategory.MODULE_UPGRADE; -import static org.onap.cps.ncmp.api.impl.ncmppersistence.NcmpPersistence.NCMP_DMI_REGISTRY_PARENT; -import static org.onap.cps.ncmp.api.impl.ncmppersistence.NcmpPersistence.NFP_OPERATIONAL_DATASTORE_DATASPACE_NAME; -import static org.onap.cps.ncmp.api.impl.utils.RestQueryParametersValidator.validateCmHandleQueryParameters; +import static org.onap.cps.ncmp.impl.inventory.NcmpPersistence.NCMP_DMI_REGISTRY_PARENT; +import static org.onap.cps.ncmp.impl.inventory.NcmpPersistence.NFP_OPERATIONAL_DATASTORE_DATASPACE_NAME; +import static org.onap.cps.ncmp.impl.inventory.models.LockReasonCategory.MODULE_UPGRADE; import com.google.common.collect.Lists; import com.hazelcast.map.IMap; import java.time.OffsetDateTime; import java.util.ArrayList; import java.util.Collection; +import java.util.Collections; import java.util.HashMap; import java.util.HashSet; import java.util.List; @@ -49,77 +48,52 @@ import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; import org.apache.commons.lang3.StringUtils; import org.onap.cps.api.CpsDataService; -import org.onap.cps.ncmp.api.NetworkCmProxyCmHandleQueryService; -import org.onap.cps.ncmp.api.NetworkCmProxyDataService; -import org.onap.cps.ncmp.api.impl.config.embeddedcache.TrustLevelCacheConfig; -import org.onap.cps.ncmp.api.impl.events.lcm.LcmEventsCmHandleStateHandler; -import org.onap.cps.ncmp.api.impl.inventory.CmHandleQueries; -import org.onap.cps.ncmp.api.impl.inventory.CmHandleState; -import org.onap.cps.ncmp.api.impl.inventory.CompositeState; -import org.onap.cps.ncmp.api.impl.inventory.CompositeStateBuilder; -import org.onap.cps.ncmp.api.impl.inventory.CompositeStateUtils; -import org.onap.cps.ncmp.api.impl.inventory.DataStoreSyncState; -import org.onap.cps.ncmp.api.impl.inventory.InventoryPersistence; -import org.onap.cps.ncmp.api.impl.inventory.sync.ModuleOperationsUtils; -import org.onap.cps.ncmp.api.impl.operations.DmiDataOperations; -import org.onap.cps.ncmp.api.impl.operations.OperationType; -import org.onap.cps.ncmp.api.impl.trustlevel.TrustLevel; -import org.onap.cps.ncmp.api.impl.trustlevel.TrustLevelManager; -import org.onap.cps.ncmp.api.impl.utils.AlternateIdChecker; -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; -import org.onap.cps.ncmp.api.impl.yangmodels.YangModelCmHandle; -import org.onap.cps.ncmp.api.models.CmHandleQueryApiParameters; -import org.onap.cps.ncmp.api.models.CmHandleQueryServiceParameters; -import org.onap.cps.ncmp.api.models.CmHandleRegistrationResponse; -import org.onap.cps.ncmp.api.models.CmResourceAddress; -import org.onap.cps.ncmp.api.models.DataOperationRequest; -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.spi.FetchDescendantsOption; +import org.onap.cps.ncmp.api.inventory.models.CmHandleRegistrationResponse; +import org.onap.cps.ncmp.api.inventory.models.CompositeState; +import org.onap.cps.ncmp.api.inventory.models.CompositeStateBuilder; +import org.onap.cps.ncmp.api.inventory.models.DmiPluginRegistration; +import org.onap.cps.ncmp.api.inventory.models.DmiPluginRegistrationResponse; +import org.onap.cps.ncmp.api.inventory.models.NcmpServiceCmHandle; +import org.onap.cps.ncmp.api.inventory.models.TrustLevel; +import org.onap.cps.ncmp.impl.inventory.models.CmHandleState; +import org.onap.cps.ncmp.impl.inventory.models.YangModelCmHandle; +import org.onap.cps.ncmp.impl.inventory.sync.ModuleOperationsUtils; +import org.onap.cps.ncmp.impl.inventory.sync.lcm.LcmEventsCmHandleStateHandler; +import org.onap.cps.ncmp.impl.inventory.trustlevel.TrustLevelManager; import org.onap.cps.spi.exceptions.AlreadyDefinedException; import org.onap.cps.spi.exceptions.CpsException; import org.onap.cps.spi.exceptions.DataNodeNotFoundException; import org.onap.cps.spi.exceptions.DataValidationException; -import org.onap.cps.spi.model.ModuleDefinition; -import org.onap.cps.spi.model.ModuleReference; -import org.onap.cps.utils.JsonObjectMapper; -import org.springframework.beans.factory.annotation.Qualifier; -import org.springframework.http.ResponseEntity; import org.springframework.stereotype.Service; @Slf4j @Service @RequiredArgsConstructor -public class NetworkCmProxyDataServiceImpl implements NetworkCmProxyDataService { +public class CmHandleRegistrationService { private static final int DELETE_BATCH_SIZE = 100; - private final JsonObjectMapper jsonObjectMapper; - private final DmiDataOperations dmiDataOperations; - private final NetworkCmProxyDataServicePropertyHandler networkCmProxyDataServicePropertyHandler; + + private final CmHandleRegistrationServicePropertyHandler cmHandleRegistrationServicePropertyHandler; private final InventoryPersistence inventoryPersistence; - private final CmHandleQueries cmHandleQueries; - private final NetworkCmProxyCmHandleQueryService networkCmProxyCmHandleQueryService; - private final LcmEventsCmHandleStateHandler lcmEventsCmHandleStateHandler; private final CpsDataService cpsDataService; + private final LcmEventsCmHandleStateHandler lcmEventsCmHandleStateHandler; private final IMap<String, Object> moduleSyncStartedOnCmHandles; - - @Qualifier(TrustLevelCacheConfig.TRUST_LEVEL_PER_DMI_PLUGIN) - private final Map<String, TrustLevel> trustLevelPerDmiPlugin; - private final TrustLevelManager trustLevelManager; private final AlternateIdChecker alternateIdChecker; - @Override + /** + * Registration of Created, Removed, Updated or Upgraded CM Handles. + * + * @param dmiPluginRegistration Dmi Plugin Registration details + * @return dmiPluginRegistrationResponse + */ public DmiPluginRegistrationResponse updateDmiRegistrationAndSyncModule( final DmiPluginRegistration dmiPluginRegistration) { dmiPluginRegistration.validateDmiPluginRegistration(); final DmiPluginRegistrationResponse dmiPluginRegistrationResponse = new DmiPluginRegistrationResponse(); - setTrustLevelPerDmiPlugin(dmiPluginRegistration); + trustLevelManager.registerDmiPlugin(dmiPluginRegistration); processRemovedCmHandles(dmiPluginRegistration, dmiPluginRegistrationResponse); @@ -132,95 +106,6 @@ public class NetworkCmProxyDataServiceImpl implements NetworkCmProxyDataService return dmiPluginRegistrationResponse; } - @Override - public Object getResourceDataForCmHandle(final CmResourceAddress cmResourceAddress, - final String optionsParamInQuery, - final String topicParamInQuery, - final String requestId, - final String authorization) { - final ResponseEntity<?> responseEntity = dmiDataOperations.getResourceDataFromDmi(cmResourceAddress, - optionsParamInQuery, - topicParamInQuery, - requestId, - authorization); - return responseEntity.getBody(); - } - - @Override - public Object getResourceDataForCmHandle(final CmResourceAddress cmResourceAddress, - final FetchDescendantsOption fetchDescendantsOption) { - return cpsDataService.getDataNodes(cmResourceAddress.datastoreName(), - cmResourceAddress.cmHandleId(), - cmResourceAddress.resourceIdentifier(), - fetchDescendantsOption).iterator().next(); - } - - @Override - public void executeDataOperationForCmHandles(final String topicParamInQuery, - final DataOperationRequest dataOperationRequest, - final String requestId, - final String authorization) { - dmiDataOperations.requestResourceDataFromDmi(topicParamInQuery, dataOperationRequest, requestId, - authorization); - } - - @Override - public Object writeResourceDataPassThroughRunningForCmHandle(final String cmHandleId, - final String resourceIdentifier, - final OperationType operationType, - final String requestData, - final String dataType, - final String authorization) { - return dmiDataOperations.writeResourceDataPassThroughRunningFromDmi(cmHandleId, resourceIdentifier, - operationType, requestData, dataType, authorization); - } - - @Override - public Collection<ModuleReference> getYangResourcesModuleReferences(final String cmHandleId) { - return inventoryPersistence.getYangResourcesModuleReferences(cmHandleId); - } - - @Override - public Collection<ModuleDefinition> getModuleDefinitionsByCmHandleId(final String cmHandleId) { - return inventoryPersistence.getModuleDefinitionsByCmHandleId(cmHandleId); - } - - @Override - public Collection<ModuleDefinition> getModuleDefinitionsByCmHandleAndModule(final String cmHandleId, - final String moduleName, - final String moduleRevision) { - return inventoryPersistence.getModuleDefinitionsByCmHandleAndModule(cmHandleId, moduleName, moduleRevision); - } - - /** - * Retrieve cm handles with details for the given query parameters. - * - * @param cmHandleQueryApiParameters cm handle query parameters - * @return cm handles with details - */ - @Override - public Collection<NcmpServiceCmHandle> executeCmHandleSearch( - final CmHandleQueryApiParameters cmHandleQueryApiParameters) { - final CmHandleQueryServiceParameters cmHandleQueryServiceParameters = jsonObjectMapper.convertToValueType( - cmHandleQueryApiParameters, CmHandleQueryServiceParameters.class); - validateCmHandleQueryParameters(cmHandleQueryServiceParameters, CmHandleQueryConditions.ALL_CONDITION_NAMES); - return networkCmProxyCmHandleQueryService.queryCmHandles(cmHandleQueryServiceParameters); - } - - /** - * Retrieve cm handle ids for the given query parameters. - * - * @param cmHandleQueryApiParameters cm handle query parameters - * @return cm handle ids - */ - @Override - public Collection<String> executeCmHandleIdSearch(final CmHandleQueryApiParameters cmHandleQueryApiParameters) { - final CmHandleQueryServiceParameters cmHandleQueryServiceParameters = jsonObjectMapper.convertToValueType( - cmHandleQueryApiParameters, CmHandleQueryServiceParameters.class); - validateCmHandleQueryParameters(cmHandleQueryServiceParameters, CmHandleQueryConditions.ALL_CONDITION_NAMES); - return networkCmProxyCmHandleQueryService.queryCmHandleIds(cmHandleQueryServiceParameters); - } - /** * Set the data sync enabled flag, along with the data sync state * based on the data sync enabled boolean for the cm handle id provided. @@ -228,7 +113,6 @@ public class NetworkCmProxyDataServiceImpl implements NetworkCmProxyDataService * @param cmHandleId cm handle id * @param dataSyncEnabledTargetValue data sync enabled flag */ - @Override public void setDataSyncEnabled(final String cmHandleId, final Boolean dataSyncEnabledTargetValue) { final CompositeState compositeState = inventoryPersistence.getCmHandleState(cmHandleId); if (dataSyncEnabledTargetValue.equals(compositeState.getDataSyncEnabled())) { @@ -252,70 +136,8 @@ public class NetworkCmProxyDataServiceImpl implements NetworkCmProxyDataService } } - /** - * Get all cm handle IDs by DMI plugin identifier. - * - * @param dmiPluginIdentifier DMI plugin identifier - * @return set of cm handle IDs - */ - @Override - public Collection<String> getAllCmHandleIdsByDmiPluginIdentifier(final String dmiPluginIdentifier) { - return cmHandleQueries.getCmHandleIdsByDmiPluginIdentifier(dmiPluginIdentifier); - } - - /** - * Get all cm handle IDs by various properties. - * - * @param cmHandleQueryServiceParameters cm handle query parameters - * @return set of cm handle IDs - */ - @Override - public Collection<String> executeCmHandleIdSearchForInventory( - final CmHandleQueryServiceParameters cmHandleQueryServiceParameters) { - validateCmHandleQueryParameters(cmHandleQueryServiceParameters, InventoryQueryConditions.ALL_CONDITION_NAMES); - return networkCmProxyCmHandleQueryService.queryCmHandleIdsForInventory(cmHandleQueryServiceParameters); - } - - /** - * Retrieve cm handle details for a given cm handle. - * - * @param cmHandleId cm handle identifier - * @return cm handle details - */ - @Override - public NcmpServiceCmHandle getNcmpServiceCmHandle(final String cmHandleId) { - return YangDataConverter.convertYangModelCmHandleToNcmpServiceCmHandle( - inventoryPersistence.getYangModelCmHandle(cmHandleId)); - } - - /** - * Get cm handle public properties for a given cm handle id. - * - * @param cmHandleId cm handle identifier - * @return cm handle public properties - */ - @Override - public Map<String, String> getCmHandlePublicProperties(final String cmHandleId) { - final YangModelCmHandle yangModelCmHandle = inventoryPersistence.getYangModelCmHandle(cmHandleId); - final List<YangModelCmHandle.Property> yangModelPublicProperties = yangModelCmHandle.getPublicProperties(); - final Map<String, String> cmHandlePublicProperties = new HashMap<>(); - YangDataConverter.asPropertiesMap(yangModelPublicProperties, cmHandlePublicProperties); - return cmHandlePublicProperties; - } - - /** - * Get cm handle composite state for a given cm handle id. - * - * @param cmHandleId cm handle identifier - * @return cm handle state - */ - @Override - public CompositeState getCmHandleCompositeState(final String cmHandleId) { - return inventoryPersistence.getYangModelCmHandle(cmHandleId).getCompositeState(); - } - protected void processRemovedCmHandles(final DmiPluginRegistration dmiPluginRegistration, - final DmiPluginRegistrationResponse dmiPluginRegistrationResponse) { + final DmiPluginRegistrationResponse dmiPluginRegistrationResponse) { final List<String> tobeRemovedCmHandleIds = dmiPluginRegistration.getRemovedCmHandles(); final List<CmHandleRegistrationResponse> cmHandleRegistrationResponses = new ArrayList<>(tobeRemovedCmHandleIds.size()); @@ -326,7 +148,7 @@ public class NetworkCmProxyDataServiceImpl implements NetworkCmProxyDataService final Set<String> notDeletedCmHandles = new HashSet<>(); for (final List<String> tobeRemovedCmHandleBatch : Lists.partition(tobeRemovedCmHandleIds, DELETE_BATCH_SIZE)) { try { - batchDeleteCmHandlesFromDbAndModuleSyncMap(tobeRemovedCmHandleBatch); + batchDeleteCmHandlesFromDbAndCaches(tobeRemovedCmHandleBatch); tobeRemovedCmHandleBatch.forEach(cmHandleId -> cmHandleRegistrationResponses.add(CmHandleRegistrationResponse.createSuccessResponse(cmHandleId))); @@ -348,7 +170,7 @@ public class NetworkCmProxyDataServiceImpl implements NetworkCmProxyDataService } protected void processCreatedCmHandles(final DmiPluginRegistration dmiPluginRegistration, - final DmiPluginRegistrationResponse dmiPluginRegistrationResponse) { + final DmiPluginRegistrationResponse dmiPluginRegistrationResponse) { final List<NcmpServiceCmHandle> ncmpServiceCmHandles = dmiPluginRegistration.getCreatedCmHandles(); final List<CmHandleRegistrationResponse> failedCmHandleRegistrationResponses = new ArrayList<>(); @@ -378,8 +200,8 @@ public class NetworkCmProxyDataServiceImpl implements NetworkCmProxyDataService } protected void processUpdatedCmHandles(final DmiPluginRegistration dmiPluginRegistration, - final DmiPluginRegistrationResponse dmiPluginRegistrationResponse) { - dmiPluginRegistrationResponse.setUpdatedCmHandles(networkCmProxyDataServicePropertyHandler + final DmiPluginRegistrationResponse dmiPluginRegistrationResponse) { + dmiPluginRegistrationResponse.setUpdatedCmHandles(cmHandleRegistrationServicePropertyHandler .updateCmHandleProperties(dmiPluginRegistration.getUpdatedCmHandles())); } @@ -405,67 +227,24 @@ public class NetworkCmProxyDataServiceImpl implements NetworkCmProxyDataService } } else { cmHandleUpgradeResponses.add( - CmHandleRegistrationResponse.createFailureResponse(cmHandleId, CM_HANDLES_NOT_READY)); + CmHandleRegistrationResponse.createFailureResponse(cmHandleId, CM_HANDLES_NOT_READY)); } } catch (final DataNodeNotFoundException dataNodeNotFoundException) { log.error("Unable to find data node for cm handle id : {} , caused by : {}", - cmHandleId, dataNodeNotFoundException.getMessage()); + cmHandleId, dataNodeNotFoundException.getMessage()); cmHandleUpgradeResponses.add( - CmHandleRegistrationResponse.createFailureResponse(cmHandleId, CM_HANDLES_NOT_FOUND)); + CmHandleRegistrationResponse.createFailureResponse(cmHandleId, CM_HANDLES_NOT_FOUND)); } catch (final DataValidationException dataValidationException) { log.error("Unable to upgrade cm handle id: {}, caused by : {}", - cmHandleId, dataValidationException.getMessage()); + cmHandleId, dataValidationException.getMessage()); cmHandleUpgradeResponses.add( - CmHandleRegistrationResponse.createFailureResponse(cmHandleId, CM_HANDLE_INVALID_ID)); + CmHandleRegistrationResponse.createFailureResponse(cmHandleId, CM_HANDLE_INVALID_ID)); } } cmHandleUpgradeResponses.addAll(upgradeCmHandles(acceptedCmHandleStatePerCmHandle)); dmiPluginRegistrationResponse.setUpgradedCmHandles(cmHandleUpgradeResponses); } - private Collection<String> checkAlternateIds( - final List<NcmpServiceCmHandle> cmHandlesToBeCreated, - final List<CmHandleRegistrationResponse> cmHandleRegistrationResponses) { - final Collection<String> rejectedCmHandleIds = alternateIdChecker - .getIdsOfCmHandlesWithRejectedAlternateId(cmHandlesToBeCreated, AlternateIdChecker.Operation.CREATE); - cmHandleRegistrationResponses.addAll(CmHandleRegistrationResponse.createFailureResponses( - rejectedCmHandleIds, ALTERNATE_ID_ALREADY_ASSOCIATED)); - return rejectedCmHandleIds; - } - - private List<String> persistCmHandlesWithState(final DmiPluginRegistration dmiPluginRegistration, - final DmiPluginRegistrationResponse dmiPluginRegistrationResponse, - final List<NcmpServiceCmHandle> cmHandlesToBeCreated, - final Collection<String> rejectedCmHandleIds) { - final List<String> succeededCmHandleIds = new ArrayList<>(cmHandlesToBeCreated.size()); - final List<YangModelCmHandle> yangModelCmHandlesToRegister = new ArrayList<>(cmHandlesToBeCreated.size()); - final List<CmHandleRegistrationResponse> cmHandleRegistrationResponses = - new ArrayList<>(cmHandlesToBeCreated.size()); - for (final NcmpServiceCmHandle ncmpServiceCmHandle: cmHandlesToBeCreated) { - if (!rejectedCmHandleIds.contains(ncmpServiceCmHandle.getCmHandleId())) { - yangModelCmHandlesToRegister.add(getYangModelCmHandle(dmiPluginRegistration, ncmpServiceCmHandle)); - cmHandleRegistrationResponses.add( - CmHandleRegistrationResponse.createSuccessResponse(ncmpServiceCmHandle.getCmHandleId())); - succeededCmHandleIds.add(ncmpServiceCmHandle.getCmHandleId()); - } - } - lcmEventsCmHandleStateHandler.initiateStateAdvised(yangModelCmHandlesToRegister); - dmiPluginRegistrationResponse.setCreatedCmHandles(cmHandleRegistrationResponses); - return succeededCmHandleIds; - } - - private YangModelCmHandle getYangModelCmHandle(final DmiPluginRegistration dmiPluginRegistration, - final NcmpServiceCmHandle ncmpServiceCmHandle) { - return YangModelCmHandle.toYangModelCmHandle( - dmiPluginRegistration.getDmiPlugin(), - dmiPluginRegistration.getDmiDataPlugin(), - dmiPluginRegistration.getDmiModelPlugin(), - ncmpServiceCmHandle, - ncmpServiceCmHandle.getModuleSetTag(), - ncmpServiceCmHandle.getAlternateId(), - ncmpServiceCmHandle.getDataProducerIdentifier()); - } - private void processTrustLevels(final Collection<NcmpServiceCmHandle> cmHandlesToBeCreated, final Collection<String> succeededCmHandleIds) { final Map<String, TrustLevel> initialTrustLevelPerCmHandleId = new HashMap<>(cmHandlesToBeCreated.size()); @@ -475,7 +254,7 @@ public class NetworkCmProxyDataServiceImpl implements NetworkCmProxyDataService ncmpServiceCmHandle.getRegistrationTrustLevel()); } } - trustLevelManager.handleInitialRegistrationOfTrustLevels(initialTrustLevelPerCmHandleId); + trustLevelManager.registerCmHandles(initialTrustLevelPerCmHandleId); } private static boolean moduleUpgradeCanBeSkipped(final YangModelCmHandle yangModelCmHandle, @@ -489,14 +268,14 @@ public class NetworkCmProxyDataServiceImpl implements NetworkCmProxyDataService private static void updateYangModelCmHandleForUpgrade(final YangModelCmHandle yangModelCmHandle, final String upgradedModuleSetTag) { final String lockReasonWithModuleSetTag = String.format(ModuleOperationsUtils.MODULE_SET_TAG_MESSAGE_FORMAT, - upgradedModuleSetTag); + upgradedModuleSetTag); yangModelCmHandle.setCompositeState(new CompositeStateBuilder().withCmHandleState(CmHandleState.READY) - .withLockReason(MODULE_UPGRADE, lockReasonWithModuleSetTag).build()); + .withLockReason(MODULE_UPGRADE, lockReasonWithModuleSetTag).build()); } private CmHandleRegistrationResponse deleteCmHandleAndGetCmHandleRegistrationResponse(final String cmHandleId) { try { - deleteCmHandleFromDbAndModuleSyncMap(cmHandleId); + deleteCmHandleFromDbAndCaches(cmHandleId); return CmHandleRegistrationResponse.createSuccessResponse(cmHandleId); } catch (final DataNodeNotFoundException dataNodeNotFoundException) { log.error("Unable to find dataNode for cmHandleId : {} , caused by : {}", @@ -519,15 +298,17 @@ public class NetworkCmProxyDataServiceImpl implements NetworkCmProxyDataService lcmEventsCmHandleStateHandler.updateCmHandleStateBatch(cmHandleStatePerCmHandle); } - private void deleteCmHandleFromDbAndModuleSyncMap(final String cmHandleId) { + private void deleteCmHandleFromDbAndCaches(final String cmHandleId) { inventoryPersistence.deleteSchemaSetWithCascade(cmHandleId); inventoryPersistence.deleteDataNode(NCMP_DMI_REGISTRY_PARENT + "/cm-handles[@id='" + cmHandleId + "']"); + trustLevelManager.removeCmHandles(Collections.singleton(cmHandleId)); removeDeletedCmHandleFromModuleSyncMap(cmHandleId); } - private void batchDeleteCmHandlesFromDbAndModuleSyncMap(final Collection<String> cmHandleIds) { + private void batchDeleteCmHandlesFromDbAndCaches(final Collection<String> cmHandleIds) { inventoryPersistence.deleteSchemaSetsWithCascade(cmHandleIds); inventoryPersistence.deleteDataNodes(mapCmHandleIdsToXpaths(cmHandleIds)); + trustLevelManager.removeCmHandles(cmHandleIds); cmHandleIds.forEach(this::removeDeletedCmHandleFromModuleSyncMap); } @@ -561,12 +342,48 @@ public class NetworkCmProxyDataServiceImpl implements NetworkCmProxyDataService return cmHandleStatePerCmHandle.keySet().stream().map(YangModelCmHandle::getId).toList(); } - private void setTrustLevelPerDmiPlugin(final DmiPluginRegistration dmiPluginRegistration) { - if (DmiPluginRegistration.isNullEmptyOrBlank(dmiPluginRegistration.getDmiDataPlugin())) { - trustLevelPerDmiPlugin.put(dmiPluginRegistration.getDmiPlugin(), TrustLevel.COMPLETE); - } else { - trustLevelPerDmiPlugin.put(dmiPluginRegistration.getDmiDataPlugin(), TrustLevel.COMPLETE); + private Collection<String> checkAlternateIds( + final List<NcmpServiceCmHandle> cmHandlesToBeCreated, + final List<CmHandleRegistrationResponse> cmHandleRegistrationResponses) { + final Collection<String> rejectedCmHandleIds = alternateIdChecker + .getIdsOfCmHandlesWithRejectedAlternateId(cmHandlesToBeCreated, AlternateIdChecker.Operation.CREATE); + cmHandleRegistrationResponses.addAll(CmHandleRegistrationResponse.createFailureResponses( + rejectedCmHandleIds, ALTERNATE_ID_ALREADY_ASSOCIATED)); + return rejectedCmHandleIds; + } + + private List<String> persistCmHandlesWithState(final DmiPluginRegistration dmiPluginRegistration, + final DmiPluginRegistrationResponse dmiPluginRegistrationResponse, + final List<NcmpServiceCmHandle> cmHandlesToBeCreated, + final Collection<String> rejectedCmHandleIds) { + final List<String> succeededCmHandleIds = new ArrayList<>(cmHandlesToBeCreated.size()); + final List<YangModelCmHandle> yangModelCmHandlesToRegister = new ArrayList<>(cmHandlesToBeCreated.size()); + final List<CmHandleRegistrationResponse> cmHandleRegistrationResponses = + new ArrayList<>(cmHandlesToBeCreated.size()); + for (final NcmpServiceCmHandle ncmpServiceCmHandle: cmHandlesToBeCreated) { + if (!rejectedCmHandleIds.contains(ncmpServiceCmHandle.getCmHandleId())) { + yangModelCmHandlesToRegister.add(getYangModelCmHandle(dmiPluginRegistration, ncmpServiceCmHandle)); + cmHandleRegistrationResponses.add( + CmHandleRegistrationResponse.createSuccessResponse(ncmpServiceCmHandle.getCmHandleId())); + succeededCmHandleIds.add(ncmpServiceCmHandle.getCmHandleId()); + } } + lcmEventsCmHandleStateHandler.initiateStateAdvised(yangModelCmHandlesToRegister); + dmiPluginRegistrationResponse.setCreatedCmHandles(cmHandleRegistrationResponses); + return succeededCmHandleIds; } + private YangModelCmHandle getYangModelCmHandle(final DmiPluginRegistration dmiPluginRegistration, + final NcmpServiceCmHandle ncmpServiceCmHandle) { + return YangModelCmHandle.toYangModelCmHandle( + dmiPluginRegistration.getDmiPlugin(), + dmiPluginRegistration.getDmiDataPlugin(), + dmiPluginRegistration.getDmiModelPlugin(), + ncmpServiceCmHandle, + ncmpServiceCmHandle.getModuleSetTag(), + ncmpServiceCmHandle.getAlternateId(), + ncmpServiceCmHandle.getDataProducerIdentifier()); + } + + } diff --git a/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/NetworkCmProxyDataServicePropertyHandler.java b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/impl/inventory/CmHandleRegistrationServicePropertyHandler.java index f861910024..308ead1270 100644 --- a/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/NetworkCmProxyDataServicePropertyHandler.java +++ b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/impl/inventory/CmHandleRegistrationServicePropertyHandler.java @@ -2,7 +2,7 @@ * ============LICENSE_START======================================================= * Copyright (C) 2022-2024 Nordix Foundation * Modifications Copyright (C) 2022 Bell Canada - * Modifications Copyright (C) 2023 TechMahindra Ltd. + * Modifications Copyright (C) 2024 TechMahindra Ltd. * ================================================================================ * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -20,16 +20,16 @@ * ============LICENSE_END========================================================= */ -package org.onap.cps.ncmp.api.impl; +package org.onap.cps.ncmp.impl.inventory; import static org.onap.cps.ncmp.api.NcmpResponseStatus.ALTERNATE_ID_ALREADY_ASSOCIATED; import static org.onap.cps.ncmp.api.NcmpResponseStatus.CM_HANDLES_NOT_FOUND; import static org.onap.cps.ncmp.api.NcmpResponseStatus.CM_HANDLE_INVALID_ID; -import static org.onap.cps.ncmp.api.impl.NetworkCmProxyDataServicePropertyHandler.PropertyType.DMI_PROPERTY; -import static org.onap.cps.ncmp.api.impl.NetworkCmProxyDataServicePropertyHandler.PropertyType.PUBLIC_PROPERTY; -import static org.onap.cps.ncmp.api.impl.ncmppersistence.NcmpPersistence.NCMP_DATASPACE_NAME; -import static org.onap.cps.ncmp.api.impl.ncmppersistence.NcmpPersistence.NCMP_DMI_REGISTRY_ANCHOR; -import static org.onap.cps.ncmp.api.impl.ncmppersistence.NcmpPersistence.NCMP_DMI_REGISTRY_PARENT; +import static org.onap.cps.ncmp.impl.inventory.CmHandleRegistrationServicePropertyHandler.PropertyType.DMI_PROPERTY; +import static org.onap.cps.ncmp.impl.inventory.CmHandleRegistrationServicePropertyHandler.PropertyType.PUBLIC_PROPERTY; +import static org.onap.cps.ncmp.impl.inventory.NcmpPersistence.NCMP_DATASPACE_NAME; +import static org.onap.cps.ncmp.impl.inventory.NcmpPersistence.NCMP_DMI_REGISTRY_ANCHOR; +import static org.onap.cps.ncmp.impl.inventory.NcmpPersistence.NCMP_DMI_REGISTRY_PARENT; import com.google.common.collect.ImmutableMap; import java.time.OffsetDateTime; @@ -44,27 +44,26 @@ import java.util.regex.Matcher; import java.util.regex.Pattern; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; +import org.apache.commons.lang3.StringUtils; import org.onap.cps.api.CpsDataService; -import org.onap.cps.ncmp.api.impl.inventory.InventoryPersistence; -import org.onap.cps.ncmp.api.impl.utils.AlternateIdChecker; -import org.onap.cps.ncmp.api.impl.utils.YangDataConverter; -import org.onap.cps.ncmp.api.impl.yangmodels.YangModelCmHandle; -import org.onap.cps.ncmp.api.models.CmHandleRegistrationResponse; -import org.onap.cps.ncmp.api.models.NcmpServiceCmHandle; +import org.onap.cps.ncmp.api.inventory.models.CmHandleRegistrationResponse; +import org.onap.cps.ncmp.api.inventory.models.NcmpServiceCmHandle; +import org.onap.cps.ncmp.impl.inventory.models.YangModelCmHandle; +import org.onap.cps.ncmp.impl.utils.YangDataConverter; import org.onap.cps.spi.exceptions.DataNodeNotFoundException; import org.onap.cps.spi.exceptions.DataValidationException; import org.onap.cps.spi.model.DataNode; import org.onap.cps.spi.model.DataNodeBuilder; +import org.onap.cps.utils.ContentType; import org.onap.cps.utils.JsonObjectMapper; import org.springframework.stereotype.Service; -import org.springframework.util.StringUtils; @Slf4j @Service @RequiredArgsConstructor //Accepting the security hotspot as the string checked is generated from inside code and not user input. @SuppressWarnings("squid:S5852") -public class NetworkCmProxyDataServicePropertyHandler { +public class CmHandleRegistrationServicePropertyHandler { private final InventoryPersistence inventoryPersistence; private final CpsDataService cpsDataService; @@ -113,8 +112,7 @@ public class NetworkCmProxyDataServicePropertyHandler { private void processUpdates(final DataNode existingCmHandleDataNode, final NcmpServiceCmHandle updatedNcmpServiceCmHandle) { - setAndUpdateCmHandleField( - updatedNcmpServiceCmHandle.getCmHandleId(), "alternate-id", updatedNcmpServiceCmHandle.getAlternateId()); + updateAlternateId(updatedNcmpServiceCmHandle); updateDataProducerIdentifier(existingCmHandleDataNode, updatedNcmpServiceCmHandle); if (!updatedNcmpServiceCmHandle.getPublicProperties().isEmpty()) { updateProperties(existingCmHandleDataNode, PUBLIC_PROPERTY, @@ -125,18 +123,27 @@ public class NetworkCmProxyDataServicePropertyHandler { } } + private void updateAlternateId(final NcmpServiceCmHandle ncmpServiceCmHandle) { + final String newAlternateId = ncmpServiceCmHandle.getAlternateId(); + if (StringUtils.isNotBlank(newAlternateId)) { + setAndUpdateCmHandleField(ncmpServiceCmHandle.getCmHandleId(), "alternate-id", newAlternateId); + } + } + private void updateDataProducerIdentifier(final DataNode cmHandleDataNode, final NcmpServiceCmHandle ncmpServiceCmHandle) { final String newDataProducerIdentifier = ncmpServiceCmHandle.getDataProducerIdentifier(); - if (StringUtils.hasText(newDataProducerIdentifier)) { - final YangModelCmHandle yangModelCmHandle = - YangDataConverter.convertCmHandleToYangModel(cmHandleDataNode); + if (StringUtils.isNotBlank(newDataProducerIdentifier)) { + final YangModelCmHandle yangModelCmHandle = YangDataConverter.toYangModelCmHandle(cmHandleDataNode); final String existingDataProducerIdentifier = yangModelCmHandle.getDataProducerIdentifier(); - if (StringUtils.hasText(existingDataProducerIdentifier)) { + if (StringUtils.isNotBlank(existingDataProducerIdentifier)) { if (!existingDataProducerIdentifier.equals(newDataProducerIdentifier)) { log.warn("Unable to update dataProducerIdentifier for cmHandle {}. " + "Value for dataProducerIdentifier has been set previously.", ncmpServiceCmHandle.getCmHandleId()); + } else { + log.debug("dataProducerIdentifier for cmHandle {} is already set to {}.", + ncmpServiceCmHandle.getCmHandleId(), newDataProducerIdentifier); } } else { setAndUpdateCmHandleField( @@ -222,7 +229,7 @@ public class NetworkCmProxyDataServicePropertyHandler { cmHandleData.put(fieldName, newFieldValue); dmiRegistryData.put("cm-handles", cmHandleData); cpsDataService.updateNodeLeaves(NCMP_DATASPACE_NAME, NCMP_DMI_REGISTRY_ANCHOR, NCMP_DMI_REGISTRY_PARENT, - jsonObjectMapper.asJsonString(dmiRegistryData), OffsetDateTime.now()); + jsonObjectMapper.asJsonString(dmiRegistryData), OffsetDateTime.now(), ContentType.JSON); log.debug("Updating {} for cmHandle {} with value : {})", fieldName, cmHandleIdToUpdate, newFieldValue); } diff --git a/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/inventory/CompositeStateUtils.java b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/impl/inventory/CompositeStateUtils.java index 35ad54fdef..685d204633 100644 --- a/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/inventory/CompositeStateUtils.java +++ b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/impl/inventory/CompositeStateUtils.java @@ -18,11 +18,13 @@ * ============LICENSE_END========================================================= */ -package org.onap.cps.ncmp.api.impl.inventory; +package org.onap.cps.ncmp.impl.inventory; import lombok.AccessLevel; import lombok.NoArgsConstructor; import lombok.extern.slf4j.Slf4j; +import org.onap.cps.ncmp.api.inventory.models.CompositeState; +import org.onap.cps.ncmp.impl.inventory.models.CmHandleState; /** * It will have all the utility method responsible for handling the composite state. diff --git a/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/inventory/DataStoreSyncState.java b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/impl/inventory/DataStoreSyncState.java index b92d152bfa..a260ce9cbe 100644 --- a/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/inventory/DataStoreSyncState.java +++ b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/impl/inventory/DataStoreSyncState.java @@ -18,7 +18,7 @@ * ============LICENSE_END========================================================= */ -package org.onap.cps.ncmp.api.impl.inventory; +package org.onap.cps.ncmp.impl.inventory; public enum DataStoreSyncState { SYNCHRONIZED, UNSYNCHRONIZED, NONE_REQUESTED diff --git a/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/inventory/InventoryPersistence.java b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/impl/inventory/InventoryPersistence.java index 184b12570e..a0d3a3eaee 100644 --- a/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/inventory/InventoryPersistence.java +++ b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/impl/inventory/InventoryPersistence.java @@ -19,13 +19,13 @@ * ============LICENSE_END========================================================= */ -package org.onap.cps.ncmp.api.impl.inventory; +package org.onap.cps.ncmp.impl.inventory; import java.util.Collection; import java.util.List; import java.util.Map; -import org.onap.cps.ncmp.api.impl.ncmppersistence.NcmpPersistence; -import org.onap.cps.ncmp.api.impl.yangmodels.YangModelCmHandle; +import org.onap.cps.ncmp.api.inventory.models.CompositeState; +import org.onap.cps.ncmp.impl.inventory.models.YangModelCmHandle; import org.onap.cps.spi.model.DataNode; import org.onap.cps.spi.model.ModuleDefinition; import org.onap.cps.spi.model.ModuleReference; @@ -130,14 +130,12 @@ public interface InventoryPersistence extends NcmpPersistence { DataNode getCmHandleDataNodeByAlternateId(String alternateId); /** - * Get data node that matches longest alternate id by removing elements (as defined by the separator string) - * from right to left. + * Get data nodes for the given batch of alternate ids. * - * @param alternateId alternate ID - * @param separator a string that separates each element from the next. - * @return data node + * @param alternateIds alternate IDs + * @return data nodes */ - DataNode getCmHandleDataNodeByLongestMatchAlternateId(final String alternateId, final String separator); + Collection<DataNode> getCmHandleDataNodesByAlternateIds(Collection<String> alternateIds); /** * Get collection of data nodes of given cm handles. @@ -154,4 +152,12 @@ public interface InventoryPersistence extends NcmpPersistence { * @return Collection of CM handle Ids */ Collection<String> getCmHandleIdsWithGivenModules(Collection<String> moduleNamesForQuery); + + /** + * Check database if cm handle id exists if not return false. + * + * @param cmHandleId cmHandle Id + * @return Boolean + */ + boolean isExistingCmHandleId(String cmHandleId); } diff --git a/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/inventory/InventoryPersistenceImpl.java b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/impl/inventory/InventoryPersistenceImpl.java index bf54fe5d96..06c3f8d2f4 100644 --- a/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/inventory/InventoryPersistenceImpl.java +++ b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/impl/inventory/InventoryPersistenceImpl.java @@ -2,7 +2,7 @@ * ============LICENSE_START======================================================= * Copyright (C) 2022-2024 Nordix Foundation * Modifications Copyright (C) 2022 Bell Canada - * Modifications Copyright (C) 2023 TechMahindra Ltd. + * Modifications Copyright (C) 2024 TechMahindra Ltd. * ================================================================================ * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -20,7 +20,7 @@ * ============LICENSE_END========================================================= */ -package org.onap.cps.ncmp.api.impl.inventory; +package org.onap.cps.ncmp.impl.inventory; import static org.onap.cps.spi.FetchDescendantsOption.OMIT_DESCENDANTS; @@ -32,21 +32,23 @@ import java.util.Collections; import java.util.HashMap; import java.util.List; import java.util.Map; +import java.util.stream.Collectors; import lombok.extern.slf4j.Slf4j; -import org.apache.commons.lang3.StringUtils; import org.onap.cps.api.CpsAnchorService; import org.onap.cps.api.CpsDataService; import org.onap.cps.api.CpsModuleService; -import org.onap.cps.ncmp.api.impl.exception.NoAlternateIdParentFoundException; -import org.onap.cps.ncmp.api.impl.utils.YangDataConverter; -import org.onap.cps.ncmp.api.impl.yangmodels.YangModelCmHandle; +import org.onap.cps.impl.utils.CpsValidator; +import org.onap.cps.ncmp.api.inventory.models.CompositeState; +import org.onap.cps.ncmp.api.inventory.models.CompositeStateBuilder; +import org.onap.cps.ncmp.impl.inventory.models.YangModelCmHandle; +import org.onap.cps.ncmp.impl.utils.YangDataConverter; import org.onap.cps.spi.FetchDescendantsOption; import org.onap.cps.spi.exceptions.DataNodeNotFoundException; import org.onap.cps.spi.exceptions.DataValidationException; import org.onap.cps.spi.model.DataNode; import org.onap.cps.spi.model.ModuleDefinition; import org.onap.cps.spi.model.ModuleReference; -import org.onap.cps.spi.utils.CpsValidator; +import org.onap.cps.utils.ContentType; import org.onap.cps.utils.JsonObjectMapper; import org.springframework.stereotype.Component; @@ -59,7 +61,7 @@ public class InventoryPersistenceImpl extends NcmpPersistenceImpl implements Inv private final CpsModuleService cpsModuleService; private final CpsAnchorService cpsAnchorService; private final CpsValidator cpsValidator; - private final CmHandleQueries cmHandleQueries; + private final CmHandleQueryService cmHandleQueryService; /** * initialize an inventory persistence object. @@ -72,12 +74,13 @@ public class InventoryPersistenceImpl extends NcmpPersistenceImpl implements Inv */ public InventoryPersistenceImpl(final JsonObjectMapper jsonObjectMapper, final CpsDataService cpsDataService, final CpsModuleService cpsModuleService, final CpsValidator cpsValidator, - final CpsAnchorService cpsAnchorService, final CmHandleQueries cmHandleQueries) { + final CpsAnchorService cpsAnchorService, + final CmHandleQueryService cmHandleQueryService) { super(jsonObjectMapper, cpsDataService, cpsModuleService, cpsValidator); this.cpsModuleService = cpsModuleService; this.cpsAnchorService = cpsAnchorService; this.cpsValidator = cpsValidator; - this.cmHandleQueries = cmHandleQueries; + this.cmHandleQueryService = cmHandleQueryService; } @@ -94,7 +97,7 @@ public class InventoryPersistenceImpl extends NcmpPersistenceImpl implements Inv public void saveCmHandleState(final String cmHandleId, final CompositeState compositeState) { final String cmHandleJsonData = createStateJsonData(jsonObjectMapper.asJsonString(compositeState)); cpsDataService.updateDataNodeAndDescendants(NCMP_DATASPACE_NAME, NCMP_DMI_REGISTRY_ANCHOR, - getXPathForCmHandleById(cmHandleId), cmHandleJsonData, OffsetDateTime.now()); + getXPathForCmHandleById(cmHandleId), cmHandleJsonData, OffsetDateTime.now(), ContentType.JSON); } @Override @@ -104,14 +107,14 @@ public class InventoryPersistenceImpl extends NcmpPersistenceImpl implements Inv getXPathForCmHandleById(cmHandleId), createStateJsonData(jsonObjectMapper.asJsonString(compositeState)))); cpsDataService.updateDataNodesAndDescendants(NCMP_DATASPACE_NAME, NCMP_DMI_REGISTRY_ANCHOR, - cmHandlesJsonDataMap, OffsetDateTime.now()); + cmHandlesJsonDataMap, OffsetDateTime.now(), ContentType.JSON); } @Override public YangModelCmHandle getYangModelCmHandle(final String cmHandleId) { cpsValidator.validateNameCharacters(cmHandleId); final DataNode dataNode = getCmHandleDataNodeByCmHandleId(cmHandleId).iterator().next(); - return YangDataConverter.convertCmHandleToYangModel(dataNode); + return YangDataConverter.toYangModelCmHandle(dataNode); } @Override @@ -126,7 +129,7 @@ public class InventoryPersistenceImpl extends NcmpPersistenceImpl implements Inv dataValidationException.getMessage()); } }); - return YangDataConverter.convertDataNodesToYangModelCmHandles(getCmHandleDataNodes(validCmHandleIds)); + return YangDataConverter.toYangModelCmHandles(getCmHandleDataNodes(validCmHandleIds)); } @Override @@ -160,7 +163,7 @@ public class InventoryPersistenceImpl extends NcmpPersistenceImpl implements Inv Lists.partition(yangModelCmHandles, CMHANDLE_BATCH_SIZE)) { final String cmHandlesJsonData = createCmHandlesJsonData(yangModelCmHandleBatch); cpsDataService.saveListElements(NCMP_DATASPACE_NAME, NCMP_DMI_REGISTRY_ANCHOR, - NCMP_DMI_REGISTRY_PARENT, cmHandlesJsonData, NO_TIMESTAMP); + NCMP_DMI_REGISTRY_PARENT, cmHandlesJsonData, NO_TIMESTAMP, ContentType.JSON); } } @@ -172,7 +175,7 @@ public class InventoryPersistenceImpl extends NcmpPersistenceImpl implements Inv @Override public DataNode getCmHandleDataNodeByAlternateId(final String alternateId) { final String cpsPathForCmHandleByAlternateId = getCpsPathForCmHandleByAlternateId(alternateId); - final Collection<DataNode> dataNodes = cmHandleQueries + final Collection<DataNode> dataNodes = cmHandleQueryService .queryNcmpRegistryByCpsPath(cpsPathForCmHandleByAlternateId, OMIT_DESCENDANTS); if (dataNodes.isEmpty()) { throw new DataNodeNotFoundException(NCMP_DATASPACE_NAME, NCMP_DMI_REGISTRY_ANCHOR, @@ -182,16 +185,12 @@ public class InventoryPersistenceImpl extends NcmpPersistenceImpl implements Inv } @Override - public DataNode getCmHandleDataNodeByLongestMatchAlternateId(final String alternateId, final String separator) { - String bestMatch = alternateId; - while (StringUtils.isNotEmpty(bestMatch)) { - try { - return getCmHandleDataNodeByAlternateId(bestMatch); - } catch (final DataNodeNotFoundException ignored) { - bestMatch = getParentPath(bestMatch, separator); - } + public Collection<DataNode> getCmHandleDataNodesByAlternateIds(final Collection<String> alternateIds) { + if (alternateIds.isEmpty()) { + return Collections.emptyList(); } - throw new NoAlternateIdParentFoundException(alternateId); + final String cpsPathForCmHandlesByAlternateIds = getCpsPathForCmHandlesByAlternateIds(alternateIds); + return cmHandleQueryService.queryNcmpRegistryByCpsPath(cpsPathForCmHandlesByAlternateIds, OMIT_DESCENDANTS); } @Override @@ -206,6 +205,15 @@ public class InventoryPersistenceImpl extends NcmpPersistenceImpl implements Inv return cpsAnchorService.queryAnchorNames(NFP_OPERATIONAL_DATASTORE_DATASPACE_NAME, moduleNamesForQuery); } + @Override + public boolean isExistingCmHandleId(final String cmHandleId) { + try { + return !getCmHandleDataNodeByCmHandleId(cmHandleId).isEmpty(); + } catch (final DataNodeNotFoundException exception) { + return false; + } + } + private static String getXPathForCmHandleById(final String cmHandleId) { return NCMP_DMI_REGISTRY_PARENT + "/cm-handles[@id='" + cmHandleId + "']"; } @@ -214,6 +222,11 @@ public class InventoryPersistenceImpl extends NcmpPersistenceImpl implements Inv return NCMP_DMI_REGISTRY_PARENT + "/cm-handles[@alternate-id='" + alternateId + "']"; } + private static String getCpsPathForCmHandlesByAlternateIds(final Collection<String> alternateIds) { + return alternateIds.stream().collect(Collectors.joining("' or @alternate-id='", + NCMP_DMI_REGISTRY_PARENT + "/cm-handles[@alternate-id='", "']")); + } + private static String createStateJsonData(final String state) { return "{\"state\":" + state + "}"; } @@ -221,9 +234,4 @@ public class InventoryPersistenceImpl extends NcmpPersistenceImpl implements Inv private String createCmHandlesJsonData(final List<YangModelCmHandle> yangModelCmHandles) { return "{\"cm-handles\":" + jsonObjectMapper.asJsonString(yangModelCmHandles) + "}"; } - - private static String getParentPath(final String path, final String separator) { - final int lastSeparatorIndex = path.lastIndexOf(separator); - return lastSeparatorIndex < 0 ? "" : path.substring(0, lastSeparatorIndex); - } } diff --git a/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/ncmppersistence/NcmpPersistence.java b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/impl/inventory/NcmpPersistence.java index d72b5d58f1..5271485e3b 100644 --- a/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/ncmppersistence/NcmpPersistence.java +++ b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/impl/inventory/NcmpPersistence.java @@ -18,7 +18,7 @@ * ============LICENSE_END========================================================= */ -package org.onap.cps.ncmp.api.impl.ncmppersistence; +package org.onap.cps.ncmp.impl.inventory; import java.time.OffsetDateTime; import java.util.Collection; diff --git a/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/inventory/NcmpPersistenceImpl.java b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/impl/inventory/NcmpPersistenceImpl.java index 6a2d6d810b..905b09ef74 100644 --- a/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/inventory/NcmpPersistenceImpl.java +++ b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/impl/inventory/NcmpPersistenceImpl.java @@ -18,7 +18,7 @@ * ============LICENSE_END========================================================= */ -package org.onap.cps.ncmp.api.impl.inventory; +package org.onap.cps.ncmp.impl.inventory; import static org.onap.cps.spi.CascadeDeleteAllowed.CASCADE_DELETE_ALLOWED; import static org.onap.cps.spi.FetchDescendantsOption.INCLUDE_ALL_DESCENDANTS; @@ -29,11 +29,10 @@ import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; import org.onap.cps.api.CpsDataService; import org.onap.cps.api.CpsModuleService; -import org.onap.cps.ncmp.api.impl.ncmppersistence.NcmpPersistence; +import org.onap.cps.impl.utils.CpsValidator; import org.onap.cps.spi.FetchDescendantsOption; import org.onap.cps.spi.exceptions.SchemaSetNotFoundException; import org.onap.cps.spi.model.DataNode; -import org.onap.cps.spi.utils.CpsValidator; import org.onap.cps.utils.JsonObjectMapper; import org.springframework.stereotype.Component; diff --git a/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/NetworkCmProxyCmHandleQueryService.java b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/impl/inventory/ParameterizedCmHandleQueryService.java index 06522f80cf..e5848c0dfa 100644 --- a/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/NetworkCmProxyCmHandleQueryService.java +++ b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/impl/inventory/ParameterizedCmHandleQueryService.java @@ -18,13 +18,13 @@ * ============LICENSE_END========================================================= */ -package org.onap.cps.ncmp.api; +package org.onap.cps.ncmp.impl.inventory; import java.util.Collection; -import org.onap.cps.ncmp.api.models.CmHandleQueryServiceParameters; -import org.onap.cps.ncmp.api.models.NcmpServiceCmHandle; +import org.onap.cps.ncmp.api.inventory.models.CmHandleQueryServiceParameters; +import org.onap.cps.ncmp.api.inventory.models.NcmpServiceCmHandle; -public interface NetworkCmProxyCmHandleQueryService { +public interface ParameterizedCmHandleQueryService { /** * Query and return cm handle ids that match the given query parameters. * Supported query types: @@ -63,7 +63,8 @@ public interface NetworkCmProxyCmHandleQueryService { Collection<NcmpServiceCmHandle> queryCmHandles(CmHandleQueryServiceParameters cmHandleQueryServiceParameters); /** - * Query and return all cm handle objects. + * Get all cm handle objects. + * Note: it is similar to all the queries above but simply no conditions and hence not 'parameterized' * * @return collection of cm handles */ diff --git a/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/NetworkCmProxyCmHandleQueryServiceImpl.java b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/impl/inventory/ParameterizedCmHandleQueryServiceImpl.java index 8890d14ae1..34eeaccf8f 100644 --- a/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/NetworkCmProxyCmHandleQueryServiceImpl.java +++ b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/impl/inventory/ParameterizedCmHandleQueryServiceImpl.java @@ -18,16 +18,16 @@ * ============LICENSE_END========================================================= */ -package org.onap.cps.ncmp.api.impl; - -import static org.onap.cps.ncmp.api.impl.ncmppersistence.NcmpPersistence.NCMP_DMI_REGISTRY_PARENT; -import static org.onap.cps.ncmp.api.impl.utils.CmHandleQueryConditions.HAS_ALL_MODULES; -import static org.onap.cps.ncmp.api.impl.utils.CmHandleQueryConditions.HAS_ALL_PROPERTIES; -import static org.onap.cps.ncmp.api.impl.utils.CmHandleQueryConditions.WITH_CPS_PATH; -import static org.onap.cps.ncmp.api.impl.utils.CmHandleQueryConditions.WITH_TRUST_LEVEL; -import static org.onap.cps.ncmp.api.impl.utils.RestQueryParametersValidator.validateCpsPathConditionProperties; -import static org.onap.cps.ncmp.api.impl.utils.RestQueryParametersValidator.validateModuleNameConditionProperties; -import static org.onap.cps.ncmp.api.impl.utils.YangDataConverter.convertYangModelCmHandleToNcmpServiceCmHandle; +package org.onap.cps.ncmp.impl.inventory; + +import static org.onap.cps.ncmp.impl.inventory.CmHandleQueryParametersValidator.validateCpsPathConditionProperties; +import static org.onap.cps.ncmp.impl.inventory.CmHandleQueryParametersValidator.validateModuleNameConditionProperties; +import static org.onap.cps.ncmp.impl.inventory.NcmpPersistence.NCMP_DMI_REGISTRY_PARENT; +import static org.onap.cps.ncmp.impl.inventory.models.CmHandleQueryConditions.HAS_ALL_MODULES; +import static org.onap.cps.ncmp.impl.inventory.models.CmHandleQueryConditions.HAS_ALL_PROPERTIES; +import static org.onap.cps.ncmp.impl.inventory.models.CmHandleQueryConditions.WITH_CPS_PATH; +import static org.onap.cps.ncmp.impl.inventory.models.CmHandleQueryConditions.WITH_TRUST_LEVEL; +import static org.onap.cps.ncmp.impl.utils.YangDataConverter.toNcmpServiceCmHandle; import static org.onap.cps.spi.FetchDescendantsOption.DIRECT_CHILDREN_ONLY; import static org.onap.cps.spi.FetchDescendantsOption.OMIT_DESCENDANTS; @@ -42,15 +42,12 @@ import java.util.stream.Collectors; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; import org.onap.cps.cpspath.parser.PathParsingException; -import org.onap.cps.ncmp.api.NetworkCmProxyCmHandleQueryService; -import org.onap.cps.ncmp.api.impl.inventory.CmHandleQueries; -import org.onap.cps.ncmp.api.impl.inventory.InventoryPersistence; -import org.onap.cps.ncmp.api.impl.inventory.enums.PropertyType; -import org.onap.cps.ncmp.api.impl.utils.InventoryQueryConditions; -import org.onap.cps.ncmp.api.impl.utils.YangDataConverter; -import org.onap.cps.ncmp.api.impl.yangmodels.YangModelCmHandle; -import org.onap.cps.ncmp.api.models.CmHandleQueryServiceParameters; -import org.onap.cps.ncmp.api.models.NcmpServiceCmHandle; +import org.onap.cps.ncmp.api.inventory.models.CmHandleQueryServiceParameters; +import org.onap.cps.ncmp.api.inventory.models.NcmpServiceCmHandle; +import org.onap.cps.ncmp.impl.inventory.models.InventoryQueryConditions; +import org.onap.cps.ncmp.impl.inventory.models.PropertyType; +import org.onap.cps.ncmp.impl.inventory.models.YangModelCmHandle; +import org.onap.cps.ncmp.impl.utils.YangDataConverter; import org.onap.cps.spi.exceptions.DataValidationException; import org.onap.cps.spi.model.ConditionProperties; import org.onap.cps.spi.model.DataNode; @@ -59,29 +56,30 @@ import org.springframework.stereotype.Service; @Service @Slf4j @RequiredArgsConstructor -public class NetworkCmProxyCmHandleQueryServiceImpl implements NetworkCmProxyCmHandleQueryService { +public class ParameterizedCmHandleQueryServiceImpl implements ParameterizedCmHandleQueryService { private static final Collection<String> NO_QUERY_TO_EXECUTE = null; - private final CmHandleQueries cmHandleQueries; + private final CmHandleQueryService cmHandleQueryService; private final InventoryPersistence inventoryPersistence; @Override public Collection<String> queryCmHandleIds( final CmHandleQueryServiceParameters cmHandleQueryServiceParameters) { return executeQueries(cmHandleQueryServiceParameters, - this::executeCpsPathQuery, - this::queryCmHandlesByPublicProperties, - this::executeModuleNameQuery, + this::executeCpsPathQuery, + this::queryCmHandlesByPublicProperties, + this::executeModuleNameQuery, this::queryCmHandlesByTrustLevel); } @Override public Collection<String> queryCmHandleIdsForInventory( - final CmHandleQueryServiceParameters cmHandleQueryServiceParameters) { + final CmHandleQueryServiceParameters cmHandleQueryServiceParameters) { return executeQueries(cmHandleQueryServiceParameters, - this::queryCmHandlesByPublicProperties, - this::queryCmHandlesByPrivateProperties, - this::queryCmHandlesByDmiPlugin); + this::executeCpsPathQuery, + this::queryCmHandlesByPublicProperties, + this::queryCmHandlesByPrivateProperties, + this::queryCmHandlesByDmiPlugin); } @Override @@ -115,7 +113,7 @@ public class NetworkCmProxyCmHandleQueryServiceImpl implements NetworkCmProxyCmH final String dmiPluginIdentifierValue = dmiPropertyQueryPairs .get(PropertyType.DMI_PLUGIN.getYangContainerName()); - return cmHandleQueries.getCmHandleIdsByDmiPluginIdentifier(dmiPluginIdentifierValue); + return cmHandleQueryService.getCmHandleIdsByDmiPluginIdentifier(dmiPluginIdentifierValue); } private Collection<String> queryCmHandlesByPrivateProperties( @@ -128,7 +126,7 @@ public class NetworkCmProxyCmHandleQueryServiceImpl implements NetworkCmProxyCmH if (privatePropertyQueryPairs.isEmpty()) { return NO_QUERY_TO_EXECUTE; } - return cmHandleQueries.queryCmHandleAdditionalProperties(privatePropertyQueryPairs); + return cmHandleQueryService.queryCmHandleAdditionalProperties(privatePropertyQueryPairs); } private Collection<String> queryCmHandlesByPublicProperties( @@ -141,7 +139,7 @@ public class NetworkCmProxyCmHandleQueryServiceImpl implements NetworkCmProxyCmH if (publicPropertyQueryPairs.isEmpty()) { return NO_QUERY_TO_EXECUTE; } - return cmHandleQueries.queryCmHandlePublicProperties(publicPropertyQueryPairs); + return cmHandleQueryService.queryCmHandlePublicProperties(publicPropertyQueryPairs); } private Collection<String> queryCmHandlesByTrustLevel(final CmHandleQueryServiceParameters @@ -154,7 +152,7 @@ public class NetworkCmProxyCmHandleQueryServiceImpl implements NetworkCmProxyCmH if (trustLevelPropertyQueryPairs.isEmpty()) { return NO_QUERY_TO_EXECUTE; } - return cmHandleQueries.queryCmHandlesByTrustLevel(trustLevelPropertyQueryPairs); + return cmHandleQueryService.queryCmHandlesByTrustLevel(trustLevelPropertyQueryPairs); } private Collection<String> executeModuleNameQuery( @@ -180,7 +178,7 @@ public class NetworkCmProxyCmHandleQueryServiceImpl implements NetworkCmProxyCmH } try { cpsPathQueryResult = collectCmHandleIdsFromDataNodes( - cmHandleQueries.queryCmHandleAncestorsByCpsPath( + cmHandleQueryService.queryCmHandleAncestorsByCpsPath( cpsPathCondition.get("cpsPath"), OMIT_DESCENDANTS)); } catch (final PathParsingException pathParsingException) { throw new DataValidationException(pathParsingException.getMessage(), pathParsingException.getDetails(), @@ -235,13 +233,13 @@ public class NetworkCmProxyCmHandleQueryServiceImpl implements NetworkCmProxyCmH final Collection<NcmpServiceCmHandle> ncmpServiceCmHandles = new ArrayList<>(yangModelcmHandles.size()); yangModelcmHandles.forEach(yangModelcmHandle -> - ncmpServiceCmHandles.add(YangDataConverter.convertYangModelCmHandleToNcmpServiceCmHandle(yangModelcmHandle)) + ncmpServiceCmHandles.add(YangDataConverter.toNcmpServiceCmHandle(yangModelcmHandle)) ); return ncmpServiceCmHandles; } private NcmpServiceCmHandle createNcmpServiceCmHandle(final DataNode dataNode) { - return convertYangModelCmHandleToNcmpServiceCmHandle(YangDataConverter.convertCmHandleToYangModel(dataNode)); + return toNcmpServiceCmHandle(YangDataConverter.toYangModelCmHandle(dataNode)); } private Collection<String> executeQueries(final CmHandleQueryServiceParameters cmHandleQueryServiceParameters, diff --git a/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/utils/CmHandleQueryConditions.java b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/impl/inventory/models/CmHandleQueryConditions.java index a59776036a..6be5c8ba16 100644 --- a/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/utils/CmHandleQueryConditions.java +++ b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/impl/inventory/models/CmHandleQueryConditions.java @@ -18,7 +18,7 @@ * ============LICENSE_END========================================================= */ -package org.onap.cps.ncmp.api.impl.utils; +package org.onap.cps.ncmp.impl.inventory.models; import java.util.Arrays; import java.util.Collection; diff --git a/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/inventory/CmHandleState.java b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/impl/inventory/models/CmHandleState.java index 5485ee7e80..9a4b3e2a83 100644 --- a/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/inventory/CmHandleState.java +++ b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/impl/inventory/models/CmHandleState.java @@ -18,7 +18,7 @@ * ============LICENSE_END========================================================= */ -package org.onap.cps.ncmp.api.impl.inventory; +package org.onap.cps.ncmp.impl.inventory.models; public enum CmHandleState { ADVISED, READY, LOCKED, DELETING, DELETED diff --git a/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/utils/InventoryQueryConditions.java b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/impl/inventory/models/InventoryQueryConditions.java index 9437cf0bac..e0b54d2197 100644 --- a/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/utils/InventoryQueryConditions.java +++ b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/impl/inventory/models/InventoryQueryConditions.java @@ -18,7 +18,7 @@ * ============LICENSE_END========================================================= */ -package org.onap.cps.ncmp.api.impl.utils; +package org.onap.cps.ncmp.impl.inventory.models; import java.util.Arrays; import java.util.List; @@ -32,7 +32,8 @@ public enum InventoryQueryConditions { HAS_ALL_PROPERTIES("hasAllProperties"), HAS_ALL_ADDITIONAL_PROPERTIES("hasAllAdditionalProperties"), - CM_HANDLE_WITH_DMI_PLUGIN("cmHandleWithDmiPlugin"); + CM_HANDLE_WITH_DMI_PLUGIN("cmHandleWithDmiPlugin"), + WITH_CPS_PATH("cmHandleWithCpsPath"); public static final List<String> ALL_CONDITION_NAMES = Arrays.stream(InventoryQueryConditions.values()) .map(InventoryQueryConditions::getName).collect(Collectors.toList()); diff --git a/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/inventory/LockReasonCategory.java b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/impl/inventory/models/LockReasonCategory.java index e2b2c6b4ae..1003aeca2d 100644 --- a/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/inventory/LockReasonCategory.java +++ b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/impl/inventory/models/LockReasonCategory.java @@ -18,7 +18,7 @@ * ============LICENSE_END========================================================= */ -package org.onap.cps.ncmp.api.impl.inventory; +package org.onap.cps.ncmp.impl.inventory.models; public enum LockReasonCategory { MODULE_SYNC_FAILED, diff --git a/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/inventory/ModelledDmiServiceLeaves.java b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/impl/inventory/models/ModelledDmiServiceLeaves.java index b8a1ba02db..e9c4957d0c 100644 --- a/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/inventory/ModelledDmiServiceLeaves.java +++ b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/impl/inventory/models/ModelledDmiServiceLeaves.java @@ -18,7 +18,7 @@ * ============LICENSE_END========================================================= */ -package org.onap.cps.ncmp.api.impl.inventory; +package org.onap.cps.ncmp.impl.inventory.models; public enum ModelledDmiServiceLeaves { DMI_SERVICE_NAME("dmi-service-name"), diff --git a/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/inventory/enums/PropertyType.java b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/impl/inventory/models/PropertyType.java index 08bfe98e5c..cc8b094b4c 100644 --- a/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/inventory/enums/PropertyType.java +++ b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/impl/inventory/models/PropertyType.java @@ -18,7 +18,7 @@ * ============LICENSE_END========================================================= */ -package org.onap.cps.ncmp.api.impl.inventory.enums; +package org.onap.cps.ncmp.impl.inventory.models; import lombok.AllArgsConstructor; import lombok.Getter; diff --git a/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/yangmodels/YangModelCmHandle.java b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/impl/inventory/models/YangModelCmHandle.java index 2ca2b2eb0d..76ee286635 100644 --- a/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/yangmodels/YangModelCmHandle.java +++ b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/impl/inventory/models/YangModelCmHandle.java @@ -18,8 +18,7 @@ * ============LICENSE_END========================================================= */ - -package org.onap.cps.ncmp.api.impl.yangmodels; +package org.onap.cps.ncmp.impl.inventory.models; import com.fasterxml.jackson.annotation.JsonInclude; import com.fasterxml.jackson.annotation.JsonInclude.Include; @@ -34,9 +33,9 @@ import lombok.Getter; import lombok.NoArgsConstructor; import lombok.Setter; import org.apache.commons.lang3.StringUtils; -import org.onap.cps.ncmp.api.impl.inventory.CompositeState; -import org.onap.cps.ncmp.api.impl.operations.RequiredDmiService; -import org.onap.cps.ncmp.api.models.NcmpServiceCmHandle; +import org.onap.cps.ncmp.api.inventory.models.CompositeState; +import org.onap.cps.ncmp.api.inventory.models.NcmpServiceCmHandle; +import org.onap.cps.ncmp.impl.models.RequiredDmiService; /** * Cm Handle which follows the Yang resource dmi registry model when persisting data to DMI or the DB. @@ -126,10 +125,9 @@ public class YangModelCmHandle { yangModelCmHandle.setDmiServiceName(dmiServiceName); yangModelCmHandle.setDmiDataServiceName(dmiDataServiceName); yangModelCmHandle.setDmiModelServiceName(dmiModelServiceName); - yangModelCmHandle.setModuleSetTag(moduleSetTag == null ? StringUtils.EMPTY : moduleSetTag); - yangModelCmHandle.setAlternateId(alternateId == null ? StringUtils.EMPTY : alternateId); - yangModelCmHandle.setDataProducerIdentifier( - dataProducerIdentifier == null ? StringUtils.EMPTY : dataProducerIdentifier); + yangModelCmHandle.setModuleSetTag(StringUtils.trimToEmpty(moduleSetTag)); + yangModelCmHandle.setAlternateId(StringUtils.trimToEmpty(alternateId)); + yangModelCmHandle.setDataProducerIdentifier(StringUtils.trimToEmpty(dataProducerIdentifier)); yangModelCmHandle.setDmiProperties(asYangModelCmHandleProperties(ncmpServiceCmHandle.getDmiProperties())); yangModelCmHandle.setPublicProperties(asYangModelCmHandleProperties( ncmpServiceCmHandle.getPublicProperties())); diff --git a/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/inventory/sync/executor/AsyncTaskExecutor.java b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/impl/inventory/sync/AsyncTaskExecutor.java index 2d5e7a1abc..b8bb64f537 100644 --- a/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/inventory/sync/executor/AsyncTaskExecutor.java +++ b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/impl/inventory/sync/AsyncTaskExecutor.java @@ -18,7 +18,7 @@ * ============LICENSE_END========================================================= */ -package org.onap.cps.ncmp.api.impl.inventory.sync.executor; +package org.onap.cps.ncmp.impl.inventory.sync; import static java.util.concurrent.TimeUnit.MILLISECONDS; diff --git a/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/inventory/sync/DataSyncWatchdog.java b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/impl/inventory/sync/DataSyncWatchdog.java index 6f089a57fb..c3973236f0 100644 --- a/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/inventory/sync/DataSyncWatchdog.java +++ b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/impl/inventory/sync/DataSyncWatchdog.java @@ -1,6 +1,6 @@ /* * ============LICENSE_START======================================================= - * Copyright (C) 2022-2023 Nordix Foundation + * Copyright (C) 2022-2024 Nordix Foundation * ================================================================================ * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -18,9 +18,9 @@ * ============LICENSE_END========================================================= */ -package org.onap.cps.ncmp.api.impl.inventory.sync; +package org.onap.cps.ncmp.impl.inventory.sync; -import static org.onap.cps.ncmp.api.impl.ncmppersistence.NcmpPersistence.NFP_OPERATIONAL_DATASTORE_DATASPACE_NAME; +import static org.onap.cps.ncmp.impl.inventory.NcmpPersistence.NFP_OPERATIONAL_DATASTORE_DATASPACE_NAME; import com.hazelcast.map.IMap; import java.time.OffsetDateTime; @@ -29,10 +29,9 @@ import java.util.function.Consumer; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; import org.onap.cps.api.CpsDataService; -import org.onap.cps.ncmp.api.impl.config.embeddedcache.SynchronizationCacheConfig; -import org.onap.cps.ncmp.api.impl.inventory.CompositeState; -import org.onap.cps.ncmp.api.impl.inventory.DataStoreSyncState; -import org.onap.cps.ncmp.api.impl.inventory.InventoryPersistence; +import org.onap.cps.ncmp.api.inventory.models.CompositeState; +import org.onap.cps.ncmp.impl.inventory.DataStoreSyncState; +import org.onap.cps.ncmp.impl.inventory.InventoryPersistence; import org.springframework.scheduling.annotation.Scheduled; import org.springframework.stereotype.Service; @@ -61,21 +60,22 @@ public class DataSyncWatchdog { moduleOperationsUtils.getUnsynchronizedReadyCmHandles().forEach(unSynchronizedReadyCmHandle -> { final String cmHandleId = unSynchronizedReadyCmHandle.getId(); if (hasPushedIntoSemaphoreMap(cmHandleId)) { - log.debug("Executing data sync on {}", cmHandleId); + log.info("Executing data sync on {}", cmHandleId); final CompositeState compositeState = inventoryPersistence .getCmHandleState(cmHandleId); final String resourceData = moduleOperationsUtils.getResourceData(cmHandleId); if (resourceData == null) { - log.debug("Error retrieving resource data for Cm-Handle: {}", cmHandleId); + log.error("Error retrieving resource data for Cm-Handle: {}", cmHandleId); } else { cpsDataService.saveData(NFP_OPERATIONAL_DATASTORE_DATASPACE_NAME, cmHandleId, resourceData, OffsetDateTime.now()); setSyncStateToSynchronized().accept(compositeState); inventoryPersistence.saveCmHandleState(cmHandleId, compositeState); updateDataSyncSemaphoreMap(cmHandleId); + log.info("Data sync finished for {}", cmHandleId); } } else { - log.debug("{} already processed by another instance", cmHandleId); + log.info("{} already processed by another instance", cmHandleId); } }); log.debug("No Cm-Handles currently found in READY State and Operational Sync State is UNSYNCHRONIZED"); 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/impl/inventory/sync/DmiModelOperations.java index 798f6de810..8ba70b3a31 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/impl/inventory/sync/DmiModelOperations.java @@ -19,48 +19,42 @@ * ============LICENSE_END========================================================= */ -package org.onap.cps.ncmp.api.impl.operations; +package org.onap.cps.ncmp.impl.inventory.sync; -import static org.onap.cps.ncmp.api.impl.operations.RequiredDmiService.MODEL; +import static org.onap.cps.ncmp.api.data.models.OperationType.READ; +import static org.onap.cps.ncmp.impl.models.RequiredDmiService.MODEL; import com.google.gson.JsonArray; -import com.google.gson.JsonElement; import com.google.gson.JsonObject; -import com.google.gson.JsonParser; 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 org.onap.cps.ncmp.api.impl.client.DmiRestClient; -import org.onap.cps.ncmp.api.impl.config.NcmpConfiguration; -import org.onap.cps.ncmp.api.impl.inventory.InventoryPersistence; -import org.onap.cps.ncmp.api.impl.utils.DmiServiceUrlBuilder; -import org.onap.cps.ncmp.api.impl.yangmodels.YangModelCmHandle; -import org.onap.cps.ncmp.api.models.YangResource; +import lombok.RequiredArgsConstructor; +import org.onap.cps.ncmp.api.inventory.models.YangResource; +import org.onap.cps.ncmp.impl.dmi.DmiProperties; +import org.onap.cps.ncmp.impl.dmi.DmiRestClient; +import org.onap.cps.ncmp.impl.inventory.models.YangModelCmHandle; +import org.onap.cps.ncmp.impl.models.DmiRequestBody; +import org.onap.cps.ncmp.impl.utils.http.RestServiceUrlTemplateBuilder; +import org.onap.cps.ncmp.impl.utils.http.UrlTemplateParameters; import org.onap.cps.spi.model.ModuleReference; import org.onap.cps.utils.JsonObjectMapper; import org.springframework.http.ResponseEntity; -import org.springframework.stereotype.Component; +import org.springframework.stereotype.Service; /** * Operations class for DMI Model. */ -@Component -public class DmiModelOperations extends DmiOperations { +@RequiredArgsConstructor +@Service +public class DmiModelOperations { - /** - * Constructor for {@code DmiOperations}. This method also manipulates url properties. - * - * @param dmiRestClient {@code DmiRestClient} - */ - public DmiModelOperations(final InventoryPersistence inventoryPersistence, - final JsonObjectMapper jsonObjectMapper, - final NcmpConfiguration.DmiProperties dmiProperties, - final DmiRestClient dmiRestClient, final DmiServiceUrlBuilder dmiServiceUrlBuilder) { - super(inventoryPersistence, jsonObjectMapper, dmiProperties, dmiRestClient, dmiServiceUrlBuilder); - } + private final JsonObjectMapper jsonObjectMapper; + private final DmiProperties dmiProperties; + private final DmiRestClient dmiRestClient; /** * Retrieves module references. @@ -113,9 +107,13 @@ public class DmiModelOperations extends DmiOperations { final String jsonRequestBody, final String cmHandle, final String resourceName) { - final String dmiResourceDataUrl = getDmiResourceUrl(dmiServiceName, cmHandle, resourceName); - return dmiRestClient.postOperationWithJsonData(dmiResourceDataUrl, jsonRequestBody, - OperationType.READ, null); + final UrlTemplateParameters urlTemplateParameters = RestServiceUrlTemplateBuilder.newInstance() + .fixedPathSegment("ch") + .variablePathSegment("cmHandleId", cmHandle) + .fixedPathSegment(resourceName) + .createUrlTemplateParameters(dmiServiceName, dmiProperties.getDmiBasePath()); + return dmiRestClient.synchronousPostOperationWithJsonData(MODEL, urlTemplateParameters, jsonRequestBody, READ, + null); } private static String getRequestBodyToFetchYangResources(final Collection<ModuleReference> newModuleReferences, @@ -126,8 +124,7 @@ public class DmiModelOperations extends DmiOperations { data.add("modules", moduleReferencesAsJson); final JsonObject jsonRequestObject = new JsonObject(); if (!moduleSetTag.isEmpty()) { - final JsonElement moduleSetTagAsJson = JsonParser.parseString(moduleSetTag); - jsonRequestObject.add("moduleSetTag", moduleSetTagAsJson); + jsonRequestObject.addProperty("moduleSetTag", moduleSetTag); } jsonRequestObject.add("data", data); jsonRequestObject.add("cmHandleProperties", toJsonObject(dmiProperties)); diff --git a/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/inventory/sync/ModuleOperationsUtils.java b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/impl/inventory/sync/ModuleOperationsUtils.java index 794ca5b1b6..aae1769968 100644 --- a/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/inventory/sync/ModuleOperationsUtils.java +++ b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/impl/inventory/sync/ModuleOperationsUtils.java @@ -19,15 +19,11 @@ * ============LICENSE_END========================================================= */ -package org.onap.cps.ncmp.api.impl.inventory.sync; - -import static org.onap.cps.ncmp.api.impl.operations.DatastoreType.PASSTHROUGH_OPERATIONAL; +package org.onap.cps.ncmp.impl.inventory.sync; import com.fasterxml.jackson.databind.JsonNode; -import java.time.Duration; -import java.time.OffsetDateTime; -import java.time.format.DateTimeFormatter; import java.util.ArrayList; +import java.util.Collection; import java.util.Collections; import java.util.HashMap; import java.util.Iterator; @@ -38,14 +34,14 @@ import java.util.regex.Matcher; import java.util.regex.Pattern; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; -import org.onap.cps.ncmp.api.impl.inventory.CmHandleQueries; -import org.onap.cps.ncmp.api.impl.inventory.CmHandleState; -import org.onap.cps.ncmp.api.impl.inventory.CompositeState; -import org.onap.cps.ncmp.api.impl.inventory.DataStoreSyncState; -import org.onap.cps.ncmp.api.impl.inventory.LockReasonCategory; -import org.onap.cps.ncmp.api.impl.operations.DmiDataOperations; -import org.onap.cps.ncmp.api.impl.utils.YangDataConverter; -import org.onap.cps.ncmp.api.impl.yangmodels.YangModelCmHandle; +import org.onap.cps.ncmp.api.inventory.models.CompositeState; +import org.onap.cps.ncmp.impl.data.DmiDataOperations; +import org.onap.cps.ncmp.impl.inventory.CmHandleQueryService; +import org.onap.cps.ncmp.impl.inventory.DataStoreSyncState; +import org.onap.cps.ncmp.impl.inventory.models.CmHandleState; +import org.onap.cps.ncmp.impl.inventory.models.LockReasonCategory; +import org.onap.cps.ncmp.impl.inventory.models.YangModelCmHandle; +import org.onap.cps.ncmp.impl.utils.YangDataConverter; import org.onap.cps.spi.FetchDescendantsOption; import org.onap.cps.spi.model.DataNode; import org.onap.cps.utils.JsonObjectMapper; @@ -57,14 +53,13 @@ import org.springframework.stereotype.Service; @RequiredArgsConstructor public class ModuleOperationsUtils { - private final CmHandleQueries cmHandleQueries; + private final CmHandleQueryService cmHandleQueryService; private final DmiDataOperations dmiDataOperations; private final JsonObjectMapper jsonObjectMapper; private static final String RETRY_ATTEMPT_KEY = "attempt"; private static final String MODULE_SET_TAG_KEY = "moduleSetTag"; public static final String MODULE_SET_TAG_MESSAGE_FORMAT = "Upgrade to ModuleSetTag: %s"; - private static final String LOCK_REASON_DETAILS_MSG_FORMAT = - MODULE_SET_TAG_MESSAGE_FORMAT + " Attempt #%d failed: %s"; + private static final String LOCK_REASON_DETAILS_MSG_FORMAT = " Attempt #%d failed: %s"; private static final Pattern retryAttemptPattern = Pattern.compile("Attempt #(\\d+) failed:.+"); private static final Pattern moduleSetTagPattern = Pattern.compile("Upgrade to ModuleSetTag: (\\S+)"); private static final String CPS_PATH_CM_HANDLES_MODEL_SYNC_FAILED_OR_UPGRADE = """ @@ -77,8 +72,9 @@ public class ModuleOperationsUtils { * * @return cm handles (data nodes) in ADVISED state (empty list if none found) */ - public List<DataNode> getAdvisedCmHandles() { - final List<DataNode> advisedCmHandlesAsDataNodes = cmHandleQueries.queryCmHandlesByState(CmHandleState.ADVISED); + public Collection<DataNode> getAdvisedCmHandles() { + final Collection<DataNode> advisedCmHandlesAsDataNodes = + cmHandleQueryService.queryCmHandlesByState(CmHandleState.ADVISED); log.debug("Total number of fetched advised cm handle(s) is (are) {}", advisedCmHandlesAsDataNodes.size()); return advisedCmHandlesAsDataNodes; } @@ -91,13 +87,13 @@ public class ModuleOperationsUtils { * return empty list if not found */ public List<YangModelCmHandle> getUnsynchronizedReadyCmHandles() { - final List<DataNode> unsynchronizedCmHandles = cmHandleQueries + final Collection<DataNode> unsynchronizedCmHandles = cmHandleQueryService .queryCmHandlesByOperationalSyncState(DataStoreSyncState.UNSYNCHRONIZED); final List<YangModelCmHandle> yangModelCmHandles = new ArrayList<>(); for (final DataNode unsynchronizedCmHandle : unsynchronizedCmHandles) { final String cmHandleId = unsynchronizedCmHandle.getLeaves().get("id").toString(); - if (cmHandleQueries.cmHandleHasState(cmHandleId, CmHandleState.READY)) { + if (cmHandleQueryService.cmHandleHasState(cmHandleId, CmHandleState.READY)) { yangModelCmHandles.addAll(convertCmHandlesDataNodesToYangModelCmHandles( Collections.singletonList(unsynchronizedCmHandle))); } @@ -111,31 +107,33 @@ public class ModuleOperationsUtils { * * @return a random LOCKED yang model cm handle, return null if not found */ - public List<YangModelCmHandle> getCmHandlesThatFailedModelSyncOrUpgrade() { - final List<DataNode> lockedCmHandlesAsDataNodeList - = cmHandleQueries.queryCmHandleAncestorsByCpsPath(CPS_PATH_CM_HANDLES_MODEL_SYNC_FAILED_OR_UPGRADE, + public Collection<YangModelCmHandle> getCmHandlesThatFailedModelSyncOrUpgrade() { + final Collection<DataNode> lockedCmHandlesAsDataNodeList + = cmHandleQueryService.queryCmHandleAncestorsByCpsPath(CPS_PATH_CM_HANDLES_MODEL_SYNC_FAILED_OR_UPGRADE, FetchDescendantsOption.INCLUDE_ALL_DESCENDANTS); return convertCmHandlesDataNodesToYangModelCmHandles(lockedCmHandlesAsDataNodeList); } /** - * Update Composite State attempts counter and set new lock reason and details. + * Updates the lock reason message and attempt counter for the provided CompositeState. + * This method increments the attempt counter and updates the lock reason message, + * including the module set tag if available. * - * @param lockReasonCategory lock reason category - * @param errorMessage error message + * @param compositeState the composite state of the CM handle + * @param lockReasonCategory the lock reason category for the CM handle + * @param errorMessage the error message to include in the lock reason message */ - public void updateLockReasonDetailsAndAttempts(final CompositeState compositeState, - final LockReasonCategory lockReasonCategory, - final String errorMessage) { - int attempt = 1; - final Map<String, String> compositeStateDetails - = getLockedCompositeStateDetails(compositeState.getLockReason()); - if (!compositeStateDetails.isEmpty() && compositeStateDetails.containsKey(RETRY_ATTEMPT_KEY)) { - attempt = 1 + Integer.parseInt(compositeStateDetails.get(RETRY_ATTEMPT_KEY)); - } - final String moduleSetTag = compositeStateDetails.getOrDefault(MODULE_SET_TAG_KEY, ""); + public void updateLockReasonWithAttempts(final CompositeState compositeState, + final LockReasonCategory lockReasonCategory, + final String errorMessage) { + final Map<String, String> lockedStateDetails = getLockedCompositeStateDetails(compositeState.getLockReason()); + final int nextAttemptCount = calculateNextAttemptCount(lockedStateDetails); + final String moduleSetTag = lockedStateDetails.getOrDefault(MODULE_SET_TAG_KEY, ""); + + final String lockReasonMessage = buildLockReasonDetails(moduleSetTag, nextAttemptCount, errorMessage); + compositeState.setLockReason(CompositeState.LockReason.builder() - .details(String.format(LOCK_REASON_DETAILS_MSG_FORMAT, moduleSetTag, attempt, errorMessage)) + .details(lockReasonMessage) .lockReasonCategory(lockReasonCategory) .build()); } @@ -166,48 +164,15 @@ public class ModuleOperationsUtils { return Collections.emptyMap(); } - /** - * Check if a module sync retry is needed. - * - * @param compositeState the composite state currently in the locked state - * @return if the retry mechanism should be attempted - */ - public boolean needsModuleSyncRetryOrUpgrade(final CompositeState compositeState) { - final OffsetDateTime time = OffsetDateTime.parse(compositeState.getLastUpdateTime(), - DateTimeFormatter.ofPattern("yyyy-MM-dd'T'HH:mm:ss.SSSZ")); - final CompositeState.LockReason lockReason = compositeState.getLockReason(); - - final boolean moduleUpgrade = LockReasonCategory.MODULE_UPGRADE == lockReason.getLockReasonCategory(); - if (moduleUpgrade) { - log.info("Locked for module upgrade"); - return true; - } - - final boolean failedDuringModuleSync = LockReasonCategory.MODULE_SYNC_FAILED - == lockReason.getLockReasonCategory(); - final boolean failedDuringModuleUpgrade = LockReasonCategory.MODULE_UPGRADE_FAILED - == lockReason.getLockReasonCategory(); - - if (failedDuringModuleSync || failedDuringModuleUpgrade) { - log.info("Locked for module {} (last attempt failed).", failedDuringModuleSync ? "sync" : "upgrade"); - return isRetryDue(lockReason, time); - } - log.info("Locked for other reason"); - return false; - } - - /** - * Get the Resourece Data from Node through DMI Passthrough service. + * Get the Resource Data from Node through DMI Passthrough service. * * @param cmHandleId cm handle id * @return optional string containing the resource data */ public String getResourceData(final String cmHandleId) { - final ResponseEntity<Object> resourceDataResponseEntity = dmiDataOperations.getResourceDataFromDmi( - PASSTHROUGH_OPERATIONAL.getDatastoreName(), - cmHandleId, - UUID.randomUUID().toString()); + final ResponseEntity<Object> resourceDataResponseEntity = dmiDataOperations.getAllResourceDataFromDmi( + cmHandleId, UUID.randomUUID().toString()); if (resourceDataResponseEntity.getStatusCode().is2xxSuccessful()) { return getFirstResource(resourceDataResponseEntity.getBody()); } @@ -239,27 +204,23 @@ public class ModuleOperationsUtils { return jsonObjectMapper.asJsonString(Map.of(firstElement.getKey(), firstElement.getValue())); } - private List<YangModelCmHandle> convertCmHandlesDataNodesToYangModelCmHandles( - final List<DataNode> cmHandlesAsDataNodeList) { - return cmHandlesAsDataNodeList.stream().map(YangDataConverter::convertCmHandleToYangModel).toList(); + private Collection<YangModelCmHandle> convertCmHandlesDataNodesToYangModelCmHandles( + final Collection<DataNode> cmHandlesAsDataNodeList) { + return cmHandlesAsDataNodeList.stream().map(YangDataConverter::toYangModelCmHandle).toList(); } - private boolean isRetryDue(final CompositeState.LockReason compositeStateLockReason, final OffsetDateTime time) { - final int timeInMinutesUntilNextAttempt; - final Map<String, String> compositeStateDetails = getLockedCompositeStateDetails(compositeStateLockReason); - if (compositeStateDetails.isEmpty()) { - timeInMinutesUntilNextAttempt = 1; - log.info("First Attempt: no current attempts found."); - } else { - timeInMinutesUntilNextAttempt = (int) Math.pow(2, Integer.parseInt(compositeStateDetails - .get(RETRY_ATTEMPT_KEY))); - } - final int timeSinceLastAttempt = (int) Duration.between(time, OffsetDateTime.now()).toMinutes(); - if (timeInMinutesUntilNextAttempt >= timeSinceLastAttempt) { - log.info("Time until next attempt is {} minutes: ", timeInMinutesUntilNextAttempt - timeSinceLastAttempt); - return false; + private int calculateNextAttemptCount(final Map<String, String> compositeStateDetails) { + return compositeStateDetails.containsKey(RETRY_ATTEMPT_KEY) + ? 1 + Integer.parseInt(compositeStateDetails.get(RETRY_ATTEMPT_KEY)) + : 1; + } + + private String buildLockReasonDetails(final String moduleSetTag, final int attempt, final String errorMessage) { + if (moduleSetTag.isEmpty()) { + return String.format(LOCK_REASON_DETAILS_MSG_FORMAT, attempt, errorMessage); } - log.info("Retry due now"); - return true; + return String.format(MODULE_SET_TAG_MESSAGE_FORMAT + " " + LOCK_REASON_DETAILS_MSG_FORMAT, + moduleSetTag, attempt, errorMessage); } + } diff --git a/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/inventory/sync/ModuleSyncService.java b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/impl/inventory/sync/ModuleSyncService.java index e257112fc3..ca0f1c6a6d 100644 --- a/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/inventory/sync/ModuleSyncService.java +++ b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/impl/inventory/sync/ModuleSyncService.java @@ -1,6 +1,7 @@ /* * ============LICENSE_START======================================================= * Copyright (C) 2022-2024 Nordix Foundation + * Modifications Copyright (C) 2024 TechMahindra Ltd. * ================================================================================ * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -18,35 +19,29 @@ * ============LICENSE_END========================================================= */ -package org.onap.cps.ncmp.api.impl.inventory.sync; +package org.onap.cps.ncmp.impl.inventory.sync; -import static org.onap.cps.ncmp.api.impl.ncmppersistence.NcmpPersistence.NCMP_DATASPACE_NAME; -import static org.onap.cps.ncmp.api.impl.ncmppersistence.NcmpPersistence.NCMP_DMI_REGISTRY_ANCHOR; -import static org.onap.cps.ncmp.api.impl.ncmppersistence.NcmpPersistence.NCMP_DMI_REGISTRY_PARENT; -import static org.onap.cps.ncmp.api.impl.ncmppersistence.NcmpPersistence.NFP_OPERATIONAL_DATASTORE_DATASPACE_NAME; +import static org.onap.cps.ncmp.impl.inventory.NcmpPersistence.NCMP_DATASPACE_NAME; +import static org.onap.cps.ncmp.impl.inventory.NcmpPersistence.NCMP_DMI_REGISTRY_ANCHOR; +import static org.onap.cps.ncmp.impl.inventory.NcmpPersistence.NCMP_DMI_REGISTRY_PARENT; +import static org.onap.cps.ncmp.impl.inventory.NcmpPersistence.NFP_OPERATIONAL_DATASTORE_DATASPACE_NAME; import java.time.OffsetDateTime; import java.util.Collection; import java.util.Collections; -import java.util.List; import java.util.Map; import lombok.AllArgsConstructor; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; -import org.apache.commons.lang3.StringUtils; import org.onap.cps.api.CpsAnchorService; import org.onap.cps.api.CpsDataService; import org.onap.cps.api.CpsModuleService; -import org.onap.cps.ncmp.api.impl.inventory.CmHandleQueries; -import org.onap.cps.ncmp.api.impl.inventory.CmHandleState; -import org.onap.cps.ncmp.api.impl.operations.DmiModelOperations; -import org.onap.cps.ncmp.api.impl.utils.YangDataConverter; -import org.onap.cps.ncmp.api.impl.yangmodels.YangModelCmHandle; +import org.onap.cps.ncmp.impl.inventory.models.CmHandleState; +import org.onap.cps.ncmp.impl.inventory.models.YangModelCmHandle; import org.onap.cps.spi.CascadeDeleteAllowed; -import org.onap.cps.spi.FetchDescendantsOption; import org.onap.cps.spi.exceptions.SchemaSetNotFoundException; -import org.onap.cps.spi.model.DataNode; import org.onap.cps.spi.model.ModuleReference; +import org.onap.cps.utils.ContentType; import org.onap.cps.utils.JsonObjectMapper; import org.springframework.stereotype.Service; @@ -57,7 +52,6 @@ public class ModuleSyncService { private final DmiModelOperations dmiModelOperations; private final CpsModuleService cpsModuleService; - private final CmHandleQueries cmHandleQueries; private final CpsDataService cpsDataService; private final CpsAnchorService cpsAnchorService; private final JsonObjectMapper jsonObjectMapper; @@ -112,40 +106,32 @@ public class ModuleSyncService { } private ModuleDelta getModuleDelta(final YangModelCmHandle yangModelCmHandle, final String targetModuleSetTag) { - final Collection<ModuleReference> allModuleReferences; final Map<String, String> newYangResources; - - final YangModelCmHandle cmHandleWithSameModuleSetTag = getAnyReadyCmHandleByModuleSetTag(targetModuleSetTag); - if (cmHandleWithSameModuleSetTag == null) { + Collection<ModuleReference> allModuleReferences = getModuleReferencesByModuleSetTag(targetModuleSetTag); + if (allModuleReferences.isEmpty()) { allModuleReferences = dmiModelOperations.getModuleReferences(yangModelCmHandle); newYangResources = dmiModelOperations.getNewYangResourcesFromDmi(yangModelCmHandle, cpsModuleService.identifyNewModuleReferences(allModuleReferences)); } else { log.info("Found other cm handle having same module set tag: {}", targetModuleSetTag); - allModuleReferences = cpsModuleService.getYangResourcesModuleReferences( - NFP_OPERATIONAL_DATASTORE_DATASPACE_NAME, cmHandleWithSameModuleSetTag.getId()); newYangResources = NO_NEW_MODULES; } return new ModuleDelta(allModuleReferences, newYangResources); } - private YangModelCmHandle getAnyReadyCmHandleByModuleSetTag(final String moduleSetTag) { - if (StringUtils.isBlank(moduleSetTag)) { - return null; + private Collection<ModuleReference> getModuleReferencesByModuleSetTag(final String moduleSetTag) { + if (moduleSetTag == null || moduleSetTag.trim().isEmpty()) { + return Collections.emptyList(); } - final String escapedModuleSetTag = moduleSetTag.replace("'", "''"); - final List<DataNode> dataNodes = cmHandleQueries.queryNcmpRegistryByCpsPath( - NCMP_DMI_REGISTRY_PARENT + "/cm-handles[@module-set-tag='" + escapedModuleSetTag + "']", - FetchDescendantsOption.DIRECT_CHILDREN_ONLY); - return dataNodes.stream().map(YangDataConverter::convertCmHandleToYangModel) - .filter(cmHandle -> cmHandle.getCompositeState().getCmHandleState() == CmHandleState.READY) - .findFirst().orElse(null); + return cpsModuleService.getModuleReferencesByAttribute(NCMP_DATASPACE_NAME, NCMP_DMI_REGISTRY_ANCHOR, + Map.of("module-set-tag", moduleSetTag), Map.of("cm-handle-state", CmHandleState.READY.name())); } private void setCmHandleModuleSetTag(final YangModelCmHandle yangModelCmHandle, final String newModuleSetTag) { final String jsonForUpdate = jsonObjectMapper.asJsonString(Map.of( "cm-handles", Map.of("id", yangModelCmHandle.getId(), "module-set-tag", newModuleSetTag))); cpsDataService.updateNodeLeaves(NCMP_DATASPACE_NAME, NCMP_DMI_REGISTRY_ANCHOR, NCMP_DMI_REGISTRY_PARENT, - jsonForUpdate, OffsetDateTime.now()); + jsonForUpdate, OffsetDateTime.now(), ContentType.JSON); } + } diff --git a/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/inventory/sync/ModuleSyncTasks.java b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/impl/inventory/sync/ModuleSyncTasks.java index 590cb56c48..c6deb79d4d 100644 --- a/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/inventory/sync/ModuleSyncTasks.java +++ b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/impl/inventory/sync/ModuleSyncTasks.java @@ -18,24 +18,23 @@ * ============LICENSE_END========================================================= */ -package org.onap.cps.ncmp.api.impl.inventory.sync; +package org.onap.cps.ncmp.impl.inventory.sync; import com.hazelcast.map.IMap; import java.util.Collection; import java.util.HashMap; -import java.util.List; import java.util.Map; import java.util.concurrent.CompletableFuture; import java.util.concurrent.atomic.AtomicInteger; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; -import org.onap.cps.ncmp.api.impl.events.lcm.LcmEventsCmHandleStateHandler; -import org.onap.cps.ncmp.api.impl.inventory.CmHandleState; -import org.onap.cps.ncmp.api.impl.inventory.CompositeState; -import org.onap.cps.ncmp.api.impl.inventory.InventoryPersistence; -import org.onap.cps.ncmp.api.impl.inventory.LockReasonCategory; -import org.onap.cps.ncmp.api.impl.utils.YangDataConverter; -import org.onap.cps.ncmp.api.impl.yangmodels.YangModelCmHandle; +import org.onap.cps.ncmp.api.inventory.models.CompositeState; +import org.onap.cps.ncmp.impl.inventory.InventoryPersistence; +import org.onap.cps.ncmp.impl.inventory.models.CmHandleState; +import org.onap.cps.ncmp.impl.inventory.models.LockReasonCategory; +import org.onap.cps.ncmp.impl.inventory.models.YangModelCmHandle; +import org.onap.cps.ncmp.impl.inventory.sync.lcm.LcmEventsCmHandleStateHandler; +import org.onap.cps.ncmp.impl.utils.YangDataConverter; import org.onap.cps.spi.model.DataNode; import org.springframework.stereotype.Component; @@ -64,8 +63,7 @@ public class ModuleSyncTasks { = new HashMap<>(cmHandlesAsDataNodes.size()); for (final DataNode cmHandleAsDataNode : cmHandlesAsDataNodes) { final String cmHandleId = String.valueOf(cmHandleAsDataNode.getLeaves().get("id")); - final YangModelCmHandle yangModelCmHandle = - YangDataConverter.convertCmHandleToYangModel(cmHandleAsDataNode); + final YangModelCmHandle yangModelCmHandle = YangDataConverter.toYangModelCmHandle(cmHandleAsDataNode); final CompositeState compositeState = inventoryPersistence.getCmHandleState(cmHandleId); final boolean inUpgrade = ModuleOperationsUtils.inUpgradeOrUpgradeFailed(compositeState); try { @@ -81,7 +79,7 @@ public class ModuleSyncTasks { log.warn("Processing of {} module failed due to reason {}.", cmHandleId, e.getMessage()); final LockReasonCategory lockReasonCategory = inUpgrade ? LockReasonCategory.MODULE_UPGRADE_FAILED : LockReasonCategory.MODULE_SYNC_FAILED; - moduleOperationsUtils.updateLockReasonDetailsAndAttempts(compositeState, + moduleOperationsUtils.updateLockReasonWithAttempts(compositeState, lockReasonCategory, e.getMessage()); setCmHandleStateLocked(yangModelCmHandle, compositeState.getLockReason()); cmHandelStatePerCmHandle.put(yangModelCmHandle, CmHandleState.LOCKED); @@ -97,23 +95,23 @@ public class ModuleSyncTasks { } /** - * Reset state to "ADVISED" for any previously failed cm handles. + * Resets the state of failed CM handles and updates their status to ADVISED for retry. + + * This method processes a collection of failed CM handles, logs their lock reason, and resets their state + * to ADVISED. Once reset, it updates the CM handle states in a batch to allow for re-attempt by the module-sync + * watchdog. * - * @param failedCmHandles previously failed (locked) cm handles + * @param failedCmHandles a collection of CM handles that have failed and need their state reset */ - public void resetFailedCmHandles(final List<YangModelCmHandle> failedCmHandles) { + public void resetFailedCmHandles(final Collection<YangModelCmHandle> failedCmHandles) { final Map<YangModelCmHandle, CmHandleState> cmHandleStatePerCmHandle = new HashMap<>(failedCmHandles.size()); for (final YangModelCmHandle failedCmHandle : failedCmHandles) { final CompositeState compositeState = failedCmHandle.getCompositeState(); - final boolean isReadyForRetry = moduleOperationsUtils.needsModuleSyncRetryOrUpgrade(compositeState); - log.info("Retry for cmHandleId : {} is {}", failedCmHandle.getId(), isReadyForRetry); - if (isReadyForRetry) { - final String resetCmHandleId = failedCmHandle.getId(); - log.debug("Reset cm handle {} state to ADVISED to be re-attempted by module-sync watchdog", - resetCmHandleId); - cmHandleStatePerCmHandle.put(failedCmHandle, CmHandleState.ADVISED); - removeResetCmHandleFromModuleSyncMap(resetCmHandleId); - } + final String resetCmHandleId = failedCmHandle.getId(); + log.debug("Resetting CM handle {} state to ADVISED for retry by the module-sync watchdog. Lock reason: {}", + failedCmHandle.getId(), compositeState.getLockReason().getLockReasonCategory().name()); + cmHandleStatePerCmHandle.put(failedCmHandle, CmHandleState.ADVISED); + removeResetCmHandleFromModuleSyncMap(resetCmHandleId); } lcmEventsCmHandleStateHandler.updateCmHandleStateBatch(cmHandleStatePerCmHandle); } diff --git a/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/inventory/sync/ModuleSyncWatchdog.java b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/impl/inventory/sync/ModuleSyncWatchdog.java index 249232d230..bc7d6cdf67 100644 --- a/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/inventory/sync/ModuleSyncWatchdog.java +++ b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/impl/inventory/sync/ModuleSyncWatchdog.java @@ -1,6 +1,6 @@ /* * ============LICENSE_START======================================================= - * Copyright (C) 2022-2023 Nordix Foundation + * Copyright (C) 2022-2024 Nordix Foundation * Modifications Copyright (C) 2022 Bell Canada * ================================================================================ * Licensed under the Apache License, Version 2.0 (the "License"); @@ -19,21 +19,18 @@ * ============LICENSE_END========================================================= */ -package org.onap.cps.ncmp.api.impl.inventory.sync; +package org.onap.cps.ncmp.impl.inventory.sync; import com.hazelcast.map.IMap; import java.util.Collection; import java.util.HashSet; -import java.util.List; import java.util.concurrent.BlockingQueue; import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.AtomicInteger; import lombok.Getter; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; -import org.onap.cps.ncmp.api.impl.config.embeddedcache.SynchronizationCacheConfig; -import org.onap.cps.ncmp.api.impl.inventory.sync.executor.AsyncTaskExecutor; -import org.onap.cps.ncmp.api.impl.yangmodels.YangModelCmHandle; +import org.onap.cps.ncmp.impl.inventory.models.YangModelCmHandle; import org.onap.cps.spi.model.DataNode; import org.springframework.scheduling.annotation.Scheduled; import org.springframework.stereotype.Service; @@ -85,10 +82,10 @@ public class ModuleSyncWatchdog { /** * Find any failed (locked) cm handles and change state back to 'ADVISED'. */ - @Scheduled(fixedDelayString = "${ncmp.timers.locked-modules-sync.sleep-time-ms:300000}") + @Scheduled(fixedDelayString = "${ncmp.timers.locked-modules-sync.sleep-time-ms:15000}") public void resetPreviouslyFailedCmHandles() { log.info("Processing module sync retry-watchdog waking up."); - final List<YangModelCmHandle> failedCmHandles + final Collection<YangModelCmHandle> failedCmHandles = moduleOperationsUtils.getCmHandlesThatFailedModelSyncOrUpgrade(); log.info("Retrying {} cmHandles", failedCmHandles.size()); moduleSyncTasks.resetFailedCmHandles(failedCmHandles); @@ -105,7 +102,7 @@ public class ModuleSyncWatchdog { private void populateWorkQueueIfNeeded() { if (moduleSyncWorkQueue.isEmpty()) { - final List<DataNode> advisedCmHandles = moduleOperationsUtils.getAdvisedCmHandles(); + final Collection<DataNode> advisedCmHandles = moduleOperationsUtils.getAdvisedCmHandles(); log.info("Processing module sync fetched {} advised cm handles from DB", advisedCmHandles.size()); for (final DataNode advisedCmHandle : advisedCmHandles) { if (!moduleSyncWorkQueue.offer(advisedCmHandle)) { diff --git a/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/config/embeddedcache/SynchronizationCacheConfig.java b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/impl/inventory/sync/SynchronizationCacheConfig.java index 62a380ca5c..8ef98bc32a 100644 --- a/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/config/embeddedcache/SynchronizationCacheConfig.java +++ b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/impl/inventory/sync/SynchronizationCacheConfig.java @@ -18,14 +18,14 @@ * ============LICENSE_END========================================================= */ -package org.onap.cps.ncmp.api.impl.config.embeddedcache; +package org.onap.cps.ncmp.impl.inventory.sync; import com.hazelcast.config.MapConfig; import com.hazelcast.config.QueueConfig; import com.hazelcast.map.IMap; import java.util.concurrent.BlockingQueue; import lombok.extern.slf4j.Slf4j; -import org.onap.cps.cache.HazelcastCacheConfig; +import org.onap.cps.ncmp.impl.cache.HazelcastCacheConfig; import org.onap.cps.spi.model.DataNode; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; diff --git a/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/inventory/sync/config/WatchdogSchedulingConfigurer.java b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/impl/inventory/sync/WatchdogSchedulingConfigurer.java index 1aaee2708f..203c5a1a20 100644 --- a/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/inventory/sync/config/WatchdogSchedulingConfigurer.java +++ b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/impl/inventory/sync/WatchdogSchedulingConfigurer.java @@ -18,7 +18,7 @@ * ============LICENSE_END========================================================= */ -package org.onap.cps.ncmp.api.impl.inventory.sync.config; +package org.onap.cps.ncmp.impl.inventory.sync; import java.util.concurrent.ThreadPoolExecutor; import org.springframework.context.annotation.Bean; @@ -53,4 +53,4 @@ public class WatchdogSchedulingConfigurer implements SchedulingConfigurer { taskScheduler.initialize(); return taskScheduler; } -}
\ No newline at end of file +} 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/impl/inventory/sync/lcm/LcmEventHeaderMapper.java index f7707d9f76..7395838306 100644 --- 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/impl/inventory/sync/lcm/LcmEventHeaderMapper.java @@ -18,7 +18,7 @@ * ============LICENSE_END========================================================= */ -package org.onap.cps.ncmp.api.impl.events.lcm; +package org.onap.cps.ncmp.impl.inventory.sync.lcm; import org.mapstruct.Mapper; import org.onap.cps.ncmp.events.lcm.v1.LcmEvent; diff --git a/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/events/lcm/LcmEventType.java b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/impl/inventory/sync/lcm/LcmEventType.java index a8d00f7e31..4bc2f10218 100644 --- a/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/events/lcm/LcmEventType.java +++ b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/impl/inventory/sync/lcm/LcmEventType.java @@ -18,7 +18,7 @@ * ============LICENSE_END========================================================= */ -package org.onap.cps.ncmp.api.impl.events.lcm; +package org.onap.cps.ncmp.impl.inventory.sync.lcm; public enum LcmEventType { diff --git a/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/events/lcm/LcmEventsCmHandleStateHandler.java b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/impl/inventory/sync/lcm/LcmEventsCmHandleStateHandler.java index 8274772d2c..6cce153269 100644 --- a/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/events/lcm/LcmEventsCmHandleStateHandler.java +++ b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/impl/inventory/sync/lcm/LcmEventsCmHandleStateHandler.java @@ -18,12 +18,12 @@ * ============LICENSE_END========================================================= */ -package org.onap.cps.ncmp.api.impl.events.lcm; +package org.onap.cps.ncmp.impl.inventory.sync.lcm; import java.util.Collection; import java.util.Map; -import org.onap.cps.ncmp.api.impl.inventory.CmHandleState; -import org.onap.cps.ncmp.api.impl.yangmodels.YangModelCmHandle; +import org.onap.cps.ncmp.impl.inventory.models.CmHandleState; +import org.onap.cps.ncmp.impl.inventory.models.YangModelCmHandle; /** * The implementation of it should handle the persisting of composite state and delegate the request to publish the diff --git a/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/events/lcm/LcmEventsCmHandleStateHandlerAsyncHelper.java b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/impl/inventory/sync/lcm/LcmEventsCmHandleStateHandlerAsyncHelper.java index 3742719e26..cf7921c350 100644 --- a/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/events/lcm/LcmEventsCmHandleStateHandlerAsyncHelper.java +++ b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/impl/inventory/sync/lcm/LcmEventsCmHandleStateHandlerAsyncHelper.java @@ -18,15 +18,15 @@ * ============LICENSE_END========================================================= */ -package org.onap.cps.ncmp.api.impl.events.lcm; +package org.onap.cps.ncmp.impl.inventory.sync.lcm; import java.util.Collection; import lombok.RequiredArgsConstructor; -import org.onap.cps.ncmp.api.impl.utils.YangDataConverter; -import org.onap.cps.ncmp.api.impl.yangmodels.YangModelCmHandle; -import org.onap.cps.ncmp.api.models.NcmpServiceCmHandle; +import org.onap.cps.ncmp.api.inventory.models.NcmpServiceCmHandle; import org.onap.cps.ncmp.events.lcm.v1.LcmEvent; import org.onap.cps.ncmp.events.lcm.v1.LcmEventHeader; +import org.onap.cps.ncmp.impl.inventory.models.YangModelCmHandle; +import org.onap.cps.ncmp.impl.utils.YangDataConverter; import org.springframework.scheduling.annotation.Async; import org.springframework.stereotype.Service; @@ -74,6 +74,6 @@ public class LcmEventsCmHandleStateHandlerAsyncHelper { } private static NcmpServiceCmHandle toNcmpServiceCmHandle(final YangModelCmHandle yangModelCmHandle) { - return YangDataConverter.convertYangModelCmHandleToNcmpServiceCmHandle(yangModelCmHandle); + return YangDataConverter.toNcmpServiceCmHandle(yangModelCmHandle); } } 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/impl/inventory/sync/lcm/LcmEventsCmHandleStateHandlerImpl.java index 0ed95adff2..b1b7b955f7 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/impl/inventory/sync/lcm/LcmEventsCmHandleStateHandlerImpl.java @@ -18,12 +18,12 @@ * ============LICENSE_END========================================================= */ -package org.onap.cps.ncmp.api.impl.events.lcm; +package org.onap.cps.ncmp.impl.inventory.sync.lcm; -import static org.onap.cps.ncmp.api.impl.inventory.CmHandleState.ADVISED; -import static org.onap.cps.ncmp.api.impl.inventory.CmHandleState.DELETED; -import static org.onap.cps.ncmp.api.impl.inventory.CmHandleState.LOCKED; -import static org.onap.cps.ncmp.api.impl.inventory.CmHandleState.READY; +import static org.onap.cps.ncmp.impl.inventory.models.CmHandleState.ADVISED; +import static org.onap.cps.ncmp.impl.inventory.models.CmHandleState.DELETED; +import static org.onap.cps.ncmp.impl.inventory.models.CmHandleState.LOCKED; +import static org.onap.cps.ncmp.impl.inventory.models.CmHandleState.READY; import io.micrometer.core.annotation.Timed; import java.util.ArrayList; @@ -37,13 +37,13 @@ import lombok.NoArgsConstructor; import lombok.RequiredArgsConstructor; import lombok.Setter; import lombok.extern.slf4j.Slf4j; -import org.onap.cps.ncmp.api.impl.inventory.CmHandleState; -import org.onap.cps.ncmp.api.impl.inventory.CompositeState; -import org.onap.cps.ncmp.api.impl.inventory.CompositeStateUtils; -import org.onap.cps.ncmp.api.impl.inventory.InventoryPersistence; -import org.onap.cps.ncmp.api.impl.utils.YangDataConverter; -import org.onap.cps.ncmp.api.impl.yangmodels.YangModelCmHandle; -import org.onap.cps.ncmp.api.models.NcmpServiceCmHandle; +import org.onap.cps.ncmp.api.inventory.models.CompositeState; +import org.onap.cps.ncmp.api.inventory.models.NcmpServiceCmHandle; +import org.onap.cps.ncmp.impl.inventory.CompositeStateUtils; +import org.onap.cps.ncmp.impl.inventory.InventoryPersistence; +import org.onap.cps.ncmp.impl.inventory.models.CmHandleState; +import org.onap.cps.ncmp.impl.inventory.models.YangModelCmHandle; +import org.onap.cps.ncmp.impl.utils.YangDataConverter; import org.springframework.stereotype.Service; @Slf4j @@ -194,7 +194,7 @@ public class LcmEventsCmHandleStateHandlerImpl implements LcmEventsCmHandleState } private NcmpServiceCmHandle toNcmpServiceCmHandle(final YangModelCmHandle yangModelCmHandle) { - return YangDataConverter.convertYangModelCmHandleToNcmpServiceCmHandle(yangModelCmHandle); + return YangDataConverter.toNcmpServiceCmHandle(yangModelCmHandle); } @Getter 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/impl/inventory/sync/lcm/LcmEventsCreator.java index fa27be158a..5137515758 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/impl/inventory/sync/lcm/LcmEventsCreator.java @@ -18,7 +18,7 @@ * ============LICENSE_END========================================================= */ -package org.onap.cps.ncmp.api.impl.events.lcm; +package org.onap.cps.ncmp.impl.inventory.sync.lcm; import java.util.UUID; import lombok.Getter; @@ -26,12 +26,12 @@ 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.cps.ncmp.api.inventory.models.NcmpServiceCmHandle; 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.onap.cps.ncmp.impl.utils.EventDateTimeFormatter; import org.springframework.stereotype.Component; @@ -128,4 +128,4 @@ public class LcmEventsCreator { private Values newValues; } -}
\ No newline at end of file +} 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/impl/inventory/sync/lcm/LcmEventsCreatorHelper.java index 19d9ba5c0d..348894d1b4 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/impl/inventory/sync/lcm/LcmEventsCreatorHelper.java @@ -18,12 +18,12 @@ * ============LICENSE_END========================================================= */ -package org.onap.cps.ncmp.api.impl.events.lcm; +package org.onap.cps.ncmp.impl.inventory.sync.lcm; -import static org.onap.cps.ncmp.api.impl.events.lcm.LcmEventType.CREATE; -import static org.onap.cps.ncmp.api.impl.events.lcm.LcmEventType.DELETE; -import static org.onap.cps.ncmp.api.impl.events.lcm.LcmEventType.UPDATE; -import static org.onap.cps.ncmp.api.impl.inventory.CmHandleState.DELETED; +import static org.onap.cps.ncmp.impl.inventory.models.CmHandleState.DELETED; +import static org.onap.cps.ncmp.impl.inventory.sync.lcm.LcmEventType.CREATE; +import static org.onap.cps.ncmp.impl.inventory.sync.lcm.LcmEventType.DELETE; +import static org.onap.cps.ncmp.impl.inventory.sync.lcm.LcmEventType.UPDATE; import com.google.common.collect.MapDifference; import com.google.common.collect.Maps; @@ -33,7 +33,7 @@ import java.util.Map; import lombok.AccessLevel; import lombok.NoArgsConstructor; import lombok.extern.slf4j.Slf4j; -import org.onap.cps.ncmp.api.models.NcmpServiceCmHandle; +import org.onap.cps.ncmp.api.inventory.models.NcmpServiceCmHandle; import org.onap.cps.ncmp.events.lcm.v1.Values; /** 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/impl/inventory/sync/lcm/LcmEventsService.java index f51b58c3ef..192667175e 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/impl/inventory/sync/lcm/LcmEventsService.java @@ -18,15 +18,20 @@ * ============LICENSE_END========================================================= */ -package org.onap.cps.ncmp.api.impl.events.lcm; +package org.onap.cps.ncmp.impl.inventory.sync.lcm; -import io.micrometer.core.annotation.Timed; +import io.micrometer.core.instrument.MeterRegistry; +import io.micrometer.core.instrument.Tag; +import io.micrometer.core.instrument.Timer; +import java.util.ArrayList; +import java.util.List; import java.util.Map; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; import org.onap.cps.events.EventsPublisher; 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.onap.cps.utils.JsonObjectMapper; import org.springframework.beans.factory.annotation.Value; import org.springframework.kafka.KafkaException; @@ -41,8 +46,12 @@ import org.springframework.stereotype.Service; @RequiredArgsConstructor public class LcmEventsService { + private static final Tag TAG_METHOD = Tag.of("method", "publishLcmEvent"); + private static final Tag TAG_CLASS = Tag.of("class", LcmEventsService.class.getName()); + private static final String UNAVAILABLE_CM_HANDLE_STATE = "N/A"; private final EventsPublisher<LcmEvent> eventsPublisher; private final JsonObjectMapper jsonObjectMapper; + private final MeterRegistry meterRegistry; @Value("${app.lcm.events.topic:ncmp-events}") private String topicName; @@ -51,24 +60,58 @@ public class LcmEventsService { private boolean notificationsEnabled; /** - * Publish the LcmEvent with header to the public topic. + * Publishes an LCM event to the dedicated topic with optional notification headers. + * Capture and log KafkaException If an error occurs while publishing the event to Kafka * - * @param cmHandleId Cm Handle Id - * @param lcmEvent Lcm Event - * @param lcmEventHeader Lcm Event Header + * @param cmHandleId Cm Handle Id associated with the LCM event + * @param lcmEvent The LCM event object to be published + * @param lcmEventHeader Optional headers associated with the LCM event */ - @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) { + final Timer.Sample timerSample = Timer.start(meterRegistry); try { 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()); + } finally { + recordMetrics(lcmEvent, timerSample); } } else { log.debug("Notifications disabled."); } } + + private void recordMetrics(final LcmEvent lcmEvent, final Timer.Sample timerSample) { + final List<Tag> tags = new ArrayList<>(4); + tags.add(TAG_CLASS); + tags.add(TAG_METHOD); + + final String oldCmHandleState = extractCmHandleStateValue(lcmEvent.getEvent().getOldValues()); + tags.add(Tag.of("oldCmHandleState", oldCmHandleState)); + + final String newCmHandleState = extractCmHandleStateValue(lcmEvent.getEvent().getNewValues()); + tags.add(Tag.of("newCmHandleState", newCmHandleState)); + + timerSample.stop(Timer.builder("cps.ncmp.lcm.events.publish") + .description("Time taken to publish a LCM event") + .tags(tags) + .register(meterRegistry)); + } + + /** + * Extracts the CM handle state value from the given Values object. + * If the provided Values object or its CM handle state is null, returns a default value. + * + * @param values The Values object containing CM handle state information. + * @return The CM handle state value as a string, or a default value if null. + */ + private String extractCmHandleStateValue(final Values values) { + return (values != null && values.getCmHandleState() != null) + ? values.getCmHandleState().value() + : UNAVAILABLE_CM_HANDLE_STATE; + } } diff --git a/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/trustlevel/DeviceHeartbeatConsumer.java b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/impl/inventory/trustlevel/DeviceTrustLevelMessageConsumer.java index 230a370d65..efcbb78ace 100644 --- a/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/trustlevel/DeviceHeartbeatConsumer.java +++ b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/impl/inventory/trustlevel/DeviceTrustLevelMessageConsumer.java @@ -18,41 +18,41 @@ * ============LICENSE_END========================================================= */ -package org.onap.cps.ncmp.api.impl.trustlevel; +package org.onap.cps.ncmp.impl.inventory.trustlevel; import io.cloudevents.CloudEvent; import io.cloudevents.kafka.impl.KafkaHeaders; import lombok.RequiredArgsConstructor; import org.apache.kafka.clients.consumer.ConsumerRecord; -import org.onap.cps.ncmp.api.impl.events.mapper.CloudEventMapper; +import org.onap.cps.ncmp.api.inventory.models.TrustLevel; import org.onap.cps.ncmp.events.trustlevel.DeviceTrustLevel; +import org.onap.cps.ncmp.utils.events.CloudEventMapper; import org.springframework.kafka.annotation.KafkaListener; import org.springframework.stereotype.Component; @Component @RequiredArgsConstructor -public class DeviceHeartbeatConsumer { +public class DeviceTrustLevelMessageConsumer { private static final String CLOUD_EVENT_ID_HEADER_NAME = "ce_id"; private final TrustLevelManager trustLevelManager; /** - * Listening the device heartbeats. + * Listening to the device trust level updates. * - * @param deviceHeartbeatConsumerRecord Device Heartbeat record. + * @param consumerRecord Device trust level record. */ @KafkaListener(topics = "${app.dmi.device-heartbeat.topic}", containerFactory = "cloudEventConcurrentKafkaListenerContainerFactory") - public void heartbeatListener(final ConsumerRecord<String, CloudEvent> deviceHeartbeatConsumerRecord) { + public void deviceTrustLevelListener(final ConsumerRecord<String, CloudEvent> consumerRecord) { - final String cmHandleId = KafkaHeaders.getParsedKafkaHeader(deviceHeartbeatConsumerRecord.headers(), + final String cmHandleId = KafkaHeaders.getParsedKafkaHeader(consumerRecord.headers(), CLOUD_EVENT_ID_HEADER_NAME); final DeviceTrustLevel deviceTrustLevel = - CloudEventMapper.toTargetEvent(deviceHeartbeatConsumerRecord.value(), DeviceTrustLevel.class); - final TrustLevel newDeviceTrustLevel = TrustLevel.valueOf(deviceTrustLevel.getData().getTrustLevel()); - trustLevelManager.handleUpdateOfDeviceTrustLevel(cmHandleId, newDeviceTrustLevel); - + CloudEventMapper.toTargetEvent(consumerRecord.value(), DeviceTrustLevel.class); + final String trustLevelAsString = deviceTrustLevel.getData().getTrustLevel(); + trustLevelManager.updateCmHandleTrustLevel(cmHandleId, TrustLevel.valueOf(trustLevelAsString)); } } diff --git a/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/trustlevel/dmiavailability/DmiPluginWatchDog.java b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/impl/inventory/trustlevel/DmiPluginTrustLevelWatchDog.java index d6d6fd6bc1..7581c4af7a 100644 --- a/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/trustlevel/dmiavailability/DmiPluginWatchDog.java +++ b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/impl/inventory/trustlevel/DmiPluginTrustLevelWatchDog.java @@ -18,17 +18,17 @@ * ============LICENSE_END========================================================= */ -package org.onap.cps.ncmp.api.impl.trustlevel.dmiavailability; +package org.onap.cps.ncmp.impl.inventory.trustlevel; import java.util.Collection; import java.util.Map; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; -import org.onap.cps.ncmp.api.NetworkCmProxyDataService; -import org.onap.cps.ncmp.api.impl.client.DmiRestClient; -import org.onap.cps.ncmp.api.impl.config.embeddedcache.TrustLevelCacheConfig; -import org.onap.cps.ncmp.api.impl.trustlevel.TrustLevel; -import org.onap.cps.ncmp.api.impl.trustlevel.TrustLevelManager; +import org.onap.cps.ncmp.api.inventory.models.TrustLevel; +import org.onap.cps.ncmp.impl.dmi.DmiRestClient; +import org.onap.cps.ncmp.impl.inventory.CmHandleQueryService; +import org.onap.cps.ncmp.impl.utils.http.RestServiceUrlTemplateBuilder; +import org.onap.cps.ncmp.impl.utils.http.UrlTemplateParameters; import org.springframework.beans.factory.annotation.Qualifier; import org.springframework.scheduling.annotation.Scheduled; import org.springframework.stereotype.Service; @@ -36,10 +36,10 @@ import org.springframework.stereotype.Service; @Slf4j @RequiredArgsConstructor @Service -public class DmiPluginWatchDog { +public class DmiPluginTrustLevelWatchDog { private final DmiRestClient dmiRestClient; - private final NetworkCmProxyDataService networkCmProxyDataService; + private final CmHandleQueryService cmHandleQueryService; private final TrustLevelManager trustLevelManager; @Qualifier(TrustLevelCacheConfig.TRUST_LEVEL_PER_DMI_PLUGIN) @@ -52,10 +52,8 @@ public class DmiPluginWatchDog { */ @Scheduled(fixedDelayString = "${ncmp.timers.trust-level.dmi-availability-watchdog-ms:30000}") public void checkDmiAvailability() { - trustLevelPerDmiPlugin.entrySet().forEach(entry -> { + trustLevelPerDmiPlugin.forEach((dmiServiceName, oldDmiTrustLevel) -> { final TrustLevel newDmiTrustLevel; - final TrustLevel oldDmiTrustLevel = entry.getValue(); - final String dmiServiceName = entry.getKey(); final String dmiHealthStatus = getDmiHealthStatus(dmiServiceName); log.debug("The health status for dmi-plugin: {} is {}", dmiServiceName, dmiHealthStatus); @@ -68,13 +66,15 @@ public class DmiPluginWatchDog { log.debug("The Dmi Plugin: {} has already the same trust level: {}", dmiServiceName, newDmiTrustLevel); } else { final Collection<String> cmHandleIds = - networkCmProxyDataService.getAllCmHandleIdsByDmiPluginIdentifier(dmiServiceName); - trustLevelManager.handleUpdateOfDmiTrustLevel(dmiServiceName, cmHandleIds, newDmiTrustLevel); + cmHandleQueryService.getCmHandleIdsByDmiPluginIdentifier(dmiServiceName); + trustLevelManager.updateDmi(dmiServiceName, cmHandleIds, newDmiTrustLevel); } }); } - private String getDmiHealthStatus(final String dmiServiceName) { - return dmiRestClient.getDmiHealthStatus(dmiServiceName); + private String getDmiHealthStatus(final String dmiServiceBaseUrl) { + final UrlTemplateParameters urlTemplateParameters = RestServiceUrlTemplateBuilder.newInstance() + .createUrlTemplateParametersForHealthCheck(dmiServiceBaseUrl); + return dmiRestClient.getDmiHealthStatus(urlTemplateParameters).block(); } } diff --git a/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/config/embeddedcache/TrustLevelCacheConfig.java b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/impl/inventory/trustlevel/TrustLevelCacheConfig.java index 440cd3ded1..0e9eaf5090 100644 --- a/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/config/embeddedcache/TrustLevelCacheConfig.java +++ b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/impl/inventory/trustlevel/TrustLevelCacheConfig.java @@ -18,12 +18,12 @@ * ============LICENSE_END========================================================= */ -package org.onap.cps.ncmp.api.impl.config.embeddedcache; +package org.onap.cps.ncmp.impl.inventory.trustlevel; import com.hazelcast.config.MapConfig; import java.util.Map; -import org.onap.cps.cache.HazelcastCacheConfig; -import org.onap.cps.ncmp.api.impl.trustlevel.TrustLevel; +import org.onap.cps.ncmp.api.inventory.models.TrustLevel; +import org.onap.cps.ncmp.impl.cache.HazelcastCacheConfig; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; diff --git a/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/trustlevel/TrustLevelManager.java b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/impl/inventory/trustlevel/TrustLevelManager.java index 9d65a66d8d..afe6ad5c1c 100644 --- a/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/trustlevel/TrustLevelManager.java +++ b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/impl/inventory/trustlevel/TrustLevelManager.java @@ -18,17 +18,18 @@ * ============LICENSE_END========================================================= */ -package org.onap.cps.ncmp.api.impl.trustlevel; +package org.onap.cps.ncmp.impl.inventory.trustlevel; import java.util.Collection; import java.util.Map; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; -import org.onap.cps.ncmp.api.impl.config.embeddedcache.TrustLevelCacheConfig; -import org.onap.cps.ncmp.api.impl.events.avc.ncmptoclient.AvcEventPublisher; -import org.onap.cps.ncmp.api.impl.inventory.InventoryPersistence; -import org.onap.cps.ncmp.api.impl.operations.RequiredDmiService; -import org.onap.cps.ncmp.api.impl.yangmodels.YangModelCmHandle; +import org.onap.cps.ncmp.api.inventory.models.DmiPluginRegistration; +import org.onap.cps.ncmp.api.inventory.models.TrustLevel; +import org.onap.cps.ncmp.impl.inventory.InventoryPersistence; +import org.onap.cps.ncmp.impl.inventory.models.YangModelCmHandle; +import org.onap.cps.ncmp.impl.models.RequiredDmiService; +import org.onap.cps.ncmp.utils.events.CmAvcEventPublisher; import org.springframework.beans.factory.annotation.Qualifier; import org.springframework.stereotype.Service; @@ -44,16 +45,31 @@ public class TrustLevelManager { private final Map<String, TrustLevel> trustLevelPerDmiPlugin; private final InventoryPersistence inventoryPersistence; - private final AvcEventPublisher avcEventPublisher; + private final CmAvcEventPublisher cmAvcEventPublisher; private static final String AVC_CHANGED_ATTRIBUTE_NAME = "trustLevel"; private static final String AVC_NO_OLD_VALUE = null; /** + * Add dmi plugins to the cache. + * + * @param dmiPluginRegistration a dmi plugin being registered + */ + public void registerDmiPlugin(final DmiPluginRegistration dmiPluginRegistration) { + final String dmiServiceName; + if (DmiPluginRegistration.isNullEmptyOrBlank(dmiPluginRegistration.getDmiDataPlugin())) { + dmiServiceName = dmiPluginRegistration.getDmiPlugin(); + } else { + dmiServiceName = dmiPluginRegistration.getDmiDataPlugin(); + } + trustLevelPerDmiPlugin.put(dmiServiceName, TrustLevel.COMPLETE); + } + + /** * Add cmHandles to the cache and publish notification for initial trust level of cmHandles if it is NONE. * * @param cmHandlesToBeCreated a list of cmHandles being created */ - public void handleInitialRegistrationOfTrustLevels(final Map<String, TrustLevel> cmHandlesToBeCreated) { + public void registerCmHandles(final Map<String, TrustLevel> cmHandlesToBeCreated) { for (final Map.Entry<String, TrustLevel> entry : cmHandlesToBeCreated.entrySet()) { final String cmHandleId = entry.getKey(); if (trustLevelPerCmHandle.containsKey(cmHandleId)) { @@ -65,7 +81,7 @@ public class TrustLevelManager { } trustLevelPerCmHandle.put(cmHandleId, initialTrustLevel); if (TrustLevel.NONE.equals(initialTrustLevel)) { - avcEventPublisher.publishAvcEvent(cmHandleId, + cmAvcEventPublisher.publishAvcEvent(cmHandleId, AVC_CHANGED_ATTRIBUTE_NAME, AVC_NO_OLD_VALUE, initialTrustLevel.name()); @@ -82,15 +98,15 @@ public class TrustLevelManager { * @param affectedCmHandleIds cm handle ids belonging to dmi service name * @param newDmiTrustLevel new trust level of the dmi plugin */ - public void handleUpdateOfDmiTrustLevel(final String dmiServiceName, - final Collection<String> affectedCmHandleIds, - final TrustLevel newDmiTrustLevel) { + public void updateDmi(final String dmiServiceName, + final Collection<String> affectedCmHandleIds, + final TrustLevel newDmiTrustLevel) { final TrustLevel oldDmiTrustLevel = trustLevelPerDmiPlugin.get(dmiServiceName); trustLevelPerDmiPlugin.put(dmiServiceName, newDmiTrustLevel); for (final String affectedCmHandleId : affectedCmHandleIds) { - final TrustLevel deviceTrustLevel = trustLevelPerCmHandle.get(affectedCmHandleId); - final TrustLevel oldEffectiveTrustLevel = deviceTrustLevel.getEffectiveTrustLevel(oldDmiTrustLevel); - final TrustLevel newEffectiveTrustLevel = deviceTrustLevel.getEffectiveTrustLevel(newDmiTrustLevel); + final TrustLevel cmHandleTrustLevel = trustLevelPerCmHandle.get(affectedCmHandleId); + final TrustLevel oldEffectiveTrustLevel = cmHandleTrustLevel.getEffectiveTrustLevel(oldDmiTrustLevel); + final TrustLevel newEffectiveTrustLevel = cmHandleTrustLevel.getEffectiveTrustLevel(newDmiTrustLevel); sendAvcNotificationIfRequired(affectedCmHandleId, oldEffectiveTrustLevel, newEffectiveTrustLevel); } } @@ -100,23 +116,52 @@ public class TrustLevelManager { * changed. * * @param cmHandleId cm handle id - * @param newDeviceTrustLevel new trust level of the device + * @param newCmHandleTrustLevel new trust level of the device */ - public void handleUpdateOfDeviceTrustLevel(final String cmHandleId, - final TrustLevel newDeviceTrustLevel) { - final YangModelCmHandle yangModelCmHandle = inventoryPersistence.getYangModelCmHandle(cmHandleId); - final String dmiServiceName = yangModelCmHandle.resolveDmiServiceName(RequiredDmiService.DATA); + public void updateCmHandleTrustLevel(final String cmHandleId, + final TrustLevel newCmHandleTrustLevel) { + final String dmiServiceName = getDmiServiceName(cmHandleId); final TrustLevel dmiTrustLevel = trustLevelPerDmiPlugin.get(dmiServiceName); - final TrustLevel oldDeviceTrustLevel = trustLevelPerCmHandle.get(cmHandleId); + final TrustLevel oldCmHandleTrustLevel = trustLevelPerCmHandle.get(cmHandleId); - final TrustLevel oldEffectiveTrustLevel = oldDeviceTrustLevel.getEffectiveTrustLevel(dmiTrustLevel); - final TrustLevel newEffectiveTrustLevel = newDeviceTrustLevel.getEffectiveTrustLevel(dmiTrustLevel); + final TrustLevel oldEffectiveTrustLevel = oldCmHandleTrustLevel.getEffectiveTrustLevel(dmiTrustLevel); + final TrustLevel newEffectiveTrustLevel = newCmHandleTrustLevel.getEffectiveTrustLevel(dmiTrustLevel); - trustLevelPerCmHandle.put(cmHandleId, newDeviceTrustLevel); + trustLevelPerCmHandle.put(cmHandleId, newCmHandleTrustLevel); sendAvcNotificationIfRequired(cmHandleId, oldEffectiveTrustLevel, newEffectiveTrustLevel); } + /** + * Select effective trust level among device and dmi plugin. + * + * @param cmHandleId cm handle id + * @return TrustLevel effective trust level + */ + public TrustLevel getEffectiveTrustLevel(final String cmHandleId) { + final TrustLevel dmiTrustLevel = TrustLevel.COMPLETE; // TODO: CPS-2375 + final TrustLevel cmHandleTrustLevel = trustLevelPerCmHandle.get(cmHandleId); + return dmiTrustLevel.getEffectiveTrustLevel(cmHandleTrustLevel); + } + + /** + * Remove cm handle trust level from the cache. + * + * @param cmHandleIds cm handle ids to be removed from the cache + */ + public void removeCmHandles(final Collection<String> cmHandleIds) { + for (final String cmHandleId : cmHandleIds) { + if (trustLevelPerCmHandle.remove(cmHandleId) == null) { + log.debug("Removed Cm handle: {} is not in trust level cache", cmHandleId); + } + } + } + + private String getDmiServiceName(final String cmHandleId) { + final YangModelCmHandle yangModelCmHandle = inventoryPersistence.getYangModelCmHandle(cmHandleId); + return yangModelCmHandle.resolveDmiServiceName(RequiredDmiService.DATA); + } + private void sendAvcNotificationIfRequired(final String notificationCandidateCmHandleId, final TrustLevel oldEffectiveTrustLevel, final TrustLevel newEffectiveTrustLevel) { @@ -126,7 +171,7 @@ public class TrustLevelManager { } else { log.info("The trust level for Cm Handle: {} is now: {} ", notificationCandidateCmHandleId, newEffectiveTrustLevel); - avcEventPublisher.publishAvcEvent(notificationCandidateCmHandleId, + cmAvcEventPublisher.publishAvcEvent(notificationCandidateCmHandleId, AVC_CHANGED_ATTRIBUTE_NAME, oldEffectiveTrustLevel.name(), newEffectiveTrustLevel.name()); 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/impl/models/DmiRequestBody.java index f1032f818f..6b05385693 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/impl/models/DmiRequestBody.java @@ -18,7 +18,7 @@ * ============LICENSE_END========================================================= */ -package org.onap.cps.ncmp.api.impl.operations; +package org.onap.cps.ncmp.impl.models; import com.fasterxml.jackson.annotation.JsonInclude; import com.fasterxml.jackson.annotation.JsonProperty; @@ -28,7 +28,8 @@ import java.util.List; import java.util.Map; import lombok.Builder; import lombok.Getter; -import org.onap.cps.ncmp.api.impl.yangmodels.YangModelCmHandle; +import org.onap.cps.ncmp.api.data.models.OperationType; +import org.onap.cps.ncmp.impl.inventory.models.YangModelCmHandle; @JsonInclude(JsonInclude.Include.NON_NULL) @Getter diff --git a/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/operations/RequiredDmiService.java b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/impl/models/RequiredDmiService.java index 7e39766f3f..c0c4f73f2a 100644 --- a/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/operations/RequiredDmiService.java +++ b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/impl/models/RequiredDmiService.java @@ -18,7 +18,7 @@ * ============LICENSE_END========================================================= */ -package org.onap.cps.ncmp.api.impl.operations; +package org.onap.cps.ncmp.impl.models; /** * Enmm to determine if the required service is for a data or model operation. diff --git a/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/impl/policyexecutor/PolicyExecutorWebClientConfiguration.java b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/impl/policyexecutor/PolicyExecutorWebClientConfiguration.java new file mode 100644 index 0000000000..333030ccd2 --- /dev/null +++ b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/impl/policyexecutor/PolicyExecutorWebClientConfiguration.java @@ -0,0 +1,46 @@ +/* + * ============LICENSE_START======================================================= + * Copyright (C) 2024 Nordix Foundation. + * ================================================================================ + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * SPDX-License-Identifier: Apache-2.0 + * ============LICENSE_END========================================================= + */ + +package org.onap.cps.ncmp.impl.policyexecutor; + +import lombok.RequiredArgsConstructor; +import org.onap.cps.ncmp.config.PolicyExecutorHttpClientConfig; +import org.onap.cps.ncmp.impl.utils.http.WebClientConfiguration; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.web.reactive.function.client.WebClient; + +@Configuration +@RequiredArgsConstructor +public class PolicyExecutorWebClientConfiguration extends WebClientConfiguration { + + private final PolicyExecutorHttpClientConfig policyExecutorHttpClientConfig; + + /** + * Configures and creates a web client bean for Policy Executor. + * + * @param webClientBuilder The builder instance to create the WebClient. + * @return a WebClient instance configured for Policy Executor. + */ + @Bean + public WebClient policyExecutorWebClient(final WebClient.Builder webClientBuilder) { + return configureWebClient(webClientBuilder, policyExecutorHttpClientConfig.getAllServices()); + } +} diff --git a/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/impl/utils/AlternateIdMatcher.java b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/impl/utils/AlternateIdMatcher.java new file mode 100644 index 0000000000..c526dfb297 --- /dev/null +++ b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/impl/utils/AlternateIdMatcher.java @@ -0,0 +1,78 @@ +/* + * ============LICENSE_START======================================================= + * Copyright (C) 2024 Nordix Foundation + * ================================================================================ + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * SPDX-License-Identifier: Apache-2.0 + * ============LICENSE_END========================================================= + */ + +package org.onap.cps.ncmp.impl.utils; + +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.apache.commons.lang3.StringUtils; +import org.onap.cps.ncmp.exceptions.NoAlternateIdMatchFoundException; +import org.onap.cps.ncmp.impl.inventory.InventoryPersistence; +import org.onap.cps.spi.exceptions.DataNodeNotFoundException; +import org.onap.cps.spi.model.DataNode; +import org.springframework.stereotype.Service; + +@Slf4j +@Service +@RequiredArgsConstructor +public class AlternateIdMatcher { + + private final InventoryPersistence inventoryPersistence; + + /** + * Get data node that matches longest alternate id by removing elements (as defined by the separator string) + * from right to left. + * + * @param alternateId alternate ID + * @param separator a string that separates each element from the next. + * @return data node + */ + public DataNode getCmHandleDataNodeByLongestMatchingAlternateId(final String alternateId, final String separator) { + String bestMatch = alternateId; + while (StringUtils.isNotEmpty(bestMatch)) { + try { + return inventoryPersistence.getCmHandleDataNodeByAlternateId(bestMatch); + } catch (final DataNodeNotFoundException ignored) { + bestMatch = getParentPath(bestMatch, separator); + } + } + throw new NoAlternateIdMatchFoundException(alternateId); + } + + /** + * Get cm handle Id from given cmHandleReference. + * + * @param cmHandleReference cm handle or alternate identifier + * @return cm handle id string + */ + public String getCmHandleId(final String cmHandleReference) { + if (inventoryPersistence.isExistingCmHandleId(cmHandleReference)) { + return cmHandleReference; + } else { + return inventoryPersistence.getCmHandleDataNodeByAlternateId(cmHandleReference) + .getLeaves().get("id").toString(); + } + } + + private String getParentPath(final String path, final String separator) { + final int lastSeparatorIndex = path.lastIndexOf(separator); + return lastSeparatorIndex < 0 ? "" : path.substring(0, lastSeparatorIndex); + } +} diff --git a/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/utils/EventDateTimeFormatter.java b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/impl/utils/EventDateTimeFormatter.java index 5dd6827126..9284c0fab7 100644 --- a/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/utils/EventDateTimeFormatter.java +++ b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/impl/utils/EventDateTimeFormatter.java @@ -18,7 +18,7 @@ * ============LICENSE_END========================================================= */ -package org.onap.cps.ncmp.api.impl.utils; +package org.onap.cps.ncmp.impl.utils; import java.time.OffsetDateTime; import java.time.ZonedDateTime; diff --git a/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/utils/YangDataConverter.java b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/impl/utils/YangDataConverter.java index 07b92892a9..ac0c44e1c3 100644 --- a/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/utils/YangDataConverter.java +++ b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/impl/utils/YangDataConverter.java @@ -18,7 +18,7 @@ * ============LICENSE_END========================================================= */ -package org.onap.cps.ncmp.api.impl.utils; +package org.onap.cps.ncmp.impl.utils; import java.util.Collection; import java.util.LinkedHashMap; @@ -30,10 +30,10 @@ import java.util.stream.Collectors; import lombok.AccessLevel; import lombok.NoArgsConstructor; import lombok.extern.slf4j.Slf4j; -import org.onap.cps.ncmp.api.impl.inventory.CompositeState; -import org.onap.cps.ncmp.api.impl.inventory.CompositeStateBuilder; -import org.onap.cps.ncmp.api.impl.yangmodels.YangModelCmHandle; -import org.onap.cps.ncmp.api.models.NcmpServiceCmHandle; +import org.onap.cps.ncmp.api.inventory.models.CompositeState; +import org.onap.cps.ncmp.api.inventory.models.CompositeStateBuilder; +import org.onap.cps.ncmp.api.inventory.models.NcmpServiceCmHandle; +import org.onap.cps.ncmp.impl.inventory.models.YangModelCmHandle; import org.onap.cps.spi.model.DataNode; @NoArgsConstructor(access = AccessLevel.PRIVATE) @@ -43,12 +43,12 @@ public class YangDataConverter { private static final Pattern cmHandleIdInXpathPattern = Pattern.compile("\\[@id='(.*?)']"); /** - * This method convert yang model cm handle to ncmp service cm handle. + * This method converts yang model cm handle to ncmp service cm handle. * @param yangModelCmHandle the yang model of the cm handle * @return ncmp service cm handle */ - public static NcmpServiceCmHandle convertYangModelCmHandleToNcmpServiceCmHandle( - final YangModelCmHandle yangModelCmHandle) { + public static NcmpServiceCmHandle toNcmpServiceCmHandle( + final YangModelCmHandle yangModelCmHandle) { final NcmpServiceCmHandle ncmpServiceCmHandle = new NcmpServiceCmHandle(); final List<YangModelCmHandle.Property> dmiProperties = yangModelCmHandle.getDmiProperties(); final List<YangModelCmHandle.Property> publicProperties = yangModelCmHandle.getPublicProperties(); @@ -63,23 +63,24 @@ public class YangDataConverter { } /** - * This method convert yang model cm handle properties to simple map. + * This method converts yang model cm handle properties to simple map. * @param properties the yang model cm handle properties - * @param propertiesMap the String, String map for the results + * @return simple map representing the properties */ - public static void asPropertiesMap(final List<YangModelCmHandle.Property> properties, - final Map<String, String> propertiesMap) { + public static Map<String, String> toPropertiesMap(final List<YangModelCmHandle.Property> properties) { + final Map<String, String> propertiesMap = new LinkedHashMap<>(properties.size()); for (final YangModelCmHandle.Property property : properties) { propertiesMap.put(property.getName(), property.getValue()); } + return propertiesMap; } /** - * This method convert cm handle data node to yang model cm handle. + * This method converts cm handle data node to yang model cm handle. * @param cmHandleDataNode the datanode of the cm handle * @return yang model cm handle */ - public static YangModelCmHandle convertCmHandleToYangModel(final DataNode cmHandleDataNode) { + public static YangModelCmHandle toYangModelCmHandle(final DataNode cmHandleDataNode) { final NcmpServiceCmHandle ncmpServiceCmHandle = new NcmpServiceCmHandle(); final String cmHandleId = cmHandleDataNode.getLeaves().get("id").toString(); ncmpServiceCmHandle.setCmHandleId(cmHandleId); @@ -96,18 +97,17 @@ public class YangDataConverter { } /** - * This method convert cm handle data nodes to yang model cm handles. + * This method converts cm handle data nodes to yang model cm handles. * @param cmHandleDataNodes the datanode of the cm handle * @return yang model cm handles */ - public static Collection<YangModelCmHandle> convertDataNodesToYangModelCmHandles( + public static Collection<YangModelCmHandle> toYangModelCmHandles( final Collection<DataNode> cmHandleDataNodes) { - return cmHandleDataNodes.stream().map(YangDataConverter::convertCmHandleToYangModel) - .collect(Collectors.toList()); + return cmHandleDataNodes.stream().map(YangDataConverter::toYangModelCmHandle).collect(Collectors.toList()); } /** - * This method extract cm handle id from xpath of data node. + * This method extracts cm handle id from xpath of data node. * @param xpath for data node of the cm handle * @return cm handle Id */ @@ -145,15 +145,11 @@ public class YangDataConverter { private static void setDmiProperties(final List<YangModelCmHandle.Property> dmiProperties, final NcmpServiceCmHandle ncmpServiceCmHandle) { - final Map<String, String> dmiPropertiesMap = new LinkedHashMap<>(dmiProperties.size()); - asPropertiesMap(dmiProperties, dmiPropertiesMap); - ncmpServiceCmHandle.setDmiProperties(dmiPropertiesMap); + ncmpServiceCmHandle.setDmiProperties(toPropertiesMap(dmiProperties)); } private static void setPublicProperties(final List<YangModelCmHandle.Property> publicProperties, final NcmpServiceCmHandle ncmpServiceCmHandle) { - final Map<String, String> publicPropertiesMap = new LinkedHashMap<>(); - asPropertiesMap(publicProperties, publicPropertiesMap); - ncmpServiceCmHandle.setPublicProperties(publicPropertiesMap); + ncmpServiceCmHandle.setPublicProperties(toPropertiesMap(publicProperties)); } } diff --git a/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/impl/utils/http/RestServiceUrlTemplateBuilder.java b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/impl/utils/http/RestServiceUrlTemplateBuilder.java new file mode 100644 index 0000000000..c850ca94a0 --- /dev/null +++ b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/impl/utils/http/RestServiceUrlTemplateBuilder.java @@ -0,0 +1,134 @@ +/* + * ============LICENSE_START======================================================= + * Copyright (C) 2022-2024 Nordix Foundation + * ================================================================================ + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * SPDX-License-Identifier: Apache-2.0 + * ============LICENSE_END========================================================= + */ + +package org.onap.cps.ncmp.impl.utils.http; + +import java.util.Collections; +import java.util.HashMap; +import java.util.LinkedHashMap; +import java.util.Map; +import lombok.NoArgsConstructor; +import org.apache.commons.lang3.StringUtils; +import org.apache.logging.log4j.util.Strings; +import org.springframework.web.util.UriComponentsBuilder; + +@NoArgsConstructor +public class RestServiceUrlTemplateBuilder { + + private final UriComponentsBuilder uriComponentsBuilder = UriComponentsBuilder.newInstance(); + private static final String FIXED_PATH_SEGMENT = null; + private static final String VERSION_SEGMENT = "v1"; + private final Map<String, String> pathSegments = new LinkedHashMap<>(); + private final Map<String, String> queryParameters = new LinkedHashMap<>(); + + /** + * Static factory method to create a new instance of DmiServiceUrlTemplateBuilder. + * + * @return a new instance of DmiServiceUrlTemplateBuilder + */ + public static RestServiceUrlTemplateBuilder newInstance() { + return new RestServiceUrlTemplateBuilder(); + } + + /** + * Add a fixed pathSegment to the URL. + * + * @param pathSegment the path segment + * @return this builder instance + */ + public RestServiceUrlTemplateBuilder fixedPathSegment(final String pathSegment) { + pathSegments.put(pathSegment, FIXED_PATH_SEGMENT); + return this; + } + + /** + * Add a variable pathSegment to the URL. + * Do NOT add { } braces. the builder will take care of that + * + * @param pathSegment the name of the variable path segment (with { and } + * @param value the value to be insert in teh URL for the given variable path segment + * @return this builder instance + */ + public RestServiceUrlTemplateBuilder variablePathSegment(final String pathSegment, final String value) { + pathSegments.put(pathSegment, value); + return this; + } + + /** + * Add a query parameter to the URL. + * Do NOT encode as the builder wil take care of encoding + * + * @param queryParameterName the name of the variable + * @param queryParameterValue the value of the variable (only Strings are supported). + * + * @return this builder instance + */ + public RestServiceUrlTemplateBuilder queryParameter(final String queryParameterName, + final String queryParameterValue) { + if (Strings.isNotBlank(queryParameterValue)) { + queryParameters.put(queryParameterName, queryParameterValue); + } + return this; + } + + /** + * Constructs a URL template with variables based on the accumulated path segments and query parameters. + * + * @param serviceBaseUrl the base URL of the service, e.g., "http://dmi-service.com". + * @param basePath the base path of the service + * @return a UrlTemplateParameters instance containing the complete URL template and URL variables + */ + public UrlTemplateParameters createUrlTemplateParameters(final String serviceBaseUrl, final String basePath) { + this.uriComponentsBuilder.pathSegment(basePath).pathSegment(VERSION_SEGMENT); + final Map<String, String> urlTemplateVariables = new HashMap<>(); + + pathSegments.forEach((pathSegmentName, variablePathValue) -> { + if (StringUtils.equals(variablePathValue, FIXED_PATH_SEGMENT)) { + this.uriComponentsBuilder.pathSegment(pathSegmentName); + } else { + this.uriComponentsBuilder.pathSegment("{" + pathSegmentName + "}"); + urlTemplateVariables.put(pathSegmentName, variablePathValue); + } + }); + + queryParameters.forEach((paramName, paramValue) -> { + this.uriComponentsBuilder.queryParam(paramName, "{" + paramName + "}"); + urlTemplateVariables.put(paramName, paramValue); + }); + + final String urlTemplate = serviceBaseUrl + this.uriComponentsBuilder.build().toUriString(); + return new UrlTemplateParameters(urlTemplate, urlTemplateVariables); + } + + /** + * Constructs a URL for a spring actuator health check based on the given base URL. + * + * @param serviceBaseUrl the base URL of the service, e.g., "http://dmi-service.com". + * @return a {@link UrlTemplateParameters} instance containing the complete URL template and empty URL variables, + * suitable for DMI health check. + */ + public UrlTemplateParameters createUrlTemplateParametersForHealthCheck(final String serviceBaseUrl) { + this.uriComponentsBuilder.pathSegment("actuator").pathSegment("health"); + + final String urlTemplate = serviceBaseUrl + this.uriComponentsBuilder.build().toUriString(); + return new UrlTemplateParameters(urlTemplate, Collections.emptyMap()); + } + +} diff --git a/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/impl/utils/http/UrlTemplateParameters.java b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/impl/utils/http/UrlTemplateParameters.java new file mode 100644 index 0000000000..839af71823 --- /dev/null +++ b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/impl/utils/http/UrlTemplateParameters.java @@ -0,0 +1,30 @@ +/* + * ============LICENSE_START======================================================= + * Copyright (C) 2024 Nordix Foundation + * ================================================================================ + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * SPDX-License-Identifier: Apache-2.0 + * ============LICENSE_END========================================================= + */ + +package org.onap.cps.ncmp.impl.utils.http; + +import java.util.Map; + +/** + * Represents a URL template with associated variables for dynamic substitution. + * This record encapsulates a URL template string and a map of variables used for substitution within the template. + */ +public record UrlTemplateParameters(String urlTemplate, Map<String, String> urlVariables) { +} diff --git a/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/impl/utils/http/WebClientConfiguration.java b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/impl/utils/http/WebClientConfiguration.java new file mode 100644 index 0000000000..d8e8350345 --- /dev/null +++ b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/impl/utils/http/WebClientConfiguration.java @@ -0,0 +1,83 @@ +/* + * ============LICENSE_START======================================================= + * Copyright (C) 2024 Nordix Foundation. + * ================================================================================ + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * SPDX-License-Identifier: Apache-2.0 + * ============LICENSE_END========================================================= + */ + +package org.onap.cps.ncmp.impl.utils.http; + +import edu.umd.cs.findbugs.annotations.SuppressFBWarnings; +import io.netty.channel.ChannelOption; +import io.netty.handler.timeout.ReadTimeoutHandler; +import io.netty.handler.timeout.WriteTimeoutHandler; +import io.netty.resolver.DefaultAddressResolverGroup; +import java.time.Duration; +import java.util.concurrent.TimeUnit; +import org.onap.cps.ncmp.config.ServiceConfig; +import org.springframework.http.HttpHeaders; +import org.springframework.http.MediaType; +import org.springframework.http.client.reactive.ReactorClientHttpConnector; +import org.springframework.web.reactive.function.client.WebClient; +import reactor.netty.http.client.HttpClient; +import reactor.netty.resources.ConnectionProvider; + +/** + * Configures and creates WebClient beans for various rest services such as DMI and Policy Executor. + * The configuration utilizes Netty-based HttpClient with custom connection settings, read and write timeouts, + * and initializes WebClient with these settings to ensure optimal performance and resource management. + */ +public class WebClientConfiguration { + + private static final Duration DEFAULT_RESPONSE_TIMEOUT = Duration.ofSeconds(30); + + protected WebClient configureWebClient(final WebClient.Builder webClientBuilder, + final ServiceConfig serviceConfig) { + final ConnectionProvider connectionProvider = getConnectionProvider(serviceConfig); + final HttpClient httpClient = createHttpClient(serviceConfig, connectionProvider); + return buildAndGetWebClient(webClientBuilder, httpClient, serviceConfig.getMaximumInMemorySizeInMegabytes()); + } + + private static HttpClient createHttpClient(final ServiceConfig serviceConfig, + final ConnectionProvider connectionProvider) { + return HttpClient.create(connectionProvider) + .responseTimeout(DEFAULT_RESPONSE_TIMEOUT) + .option(ChannelOption.CONNECT_TIMEOUT_MILLIS, serviceConfig.getConnectionTimeoutInSeconds() * 1000) + .doOnConnected(connection -> connection.addHandlerLast(new ReadTimeoutHandler( + serviceConfig.getReadTimeoutInSeconds(), TimeUnit.SECONDS)).addHandlerLast( + new WriteTimeoutHandler(serviceConfig.getWriteTimeoutInSeconds(), TimeUnit.SECONDS))) + .resolver(DefaultAddressResolverGroup.INSTANCE) + .compress(true); + } + + @SuppressFBWarnings("BC_UNCONFIRMED_CAST_OF_RETURN_VALUE") + private static ConnectionProvider getConnectionProvider(final ServiceConfig serviceConfig) { + return ConnectionProvider.builder(serviceConfig.getConnectionProviderName()) + .maxConnections(serviceConfig.getMaximumConnectionsTotal()) + .pendingAcquireMaxCount(serviceConfig.getPendingAcquireMaxCount()) + .build(); + } + + private WebClient buildAndGetWebClient(final WebClient.Builder webClientBuilder, final HttpClient httpClient, + final int maximumInMemorySizeInMegabytes) { + return webClientBuilder + .defaultHeaders(header -> header.set(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_JSON_VALUE)) + .defaultHeaders(header -> header.set(HttpHeaders.ACCEPT, MediaType.APPLICATION_JSON_VALUE)) + .clientConnector(new ReactorClientHttpConnector(httpClient)) + .codecs(configurer -> configurer.defaultCodecs() + .maxInMemorySize(maximumInMemorySizeInMegabytes * 1024 * 1024)).build(); + } +} diff --git a/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/init/AbstractModelLoader.java b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/init/AbstractModelLoader.java index 4cc8cdaa66..6d51eb48b8 100644 --- a/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/init/AbstractModelLoader.java +++ b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/init/AbstractModelLoader.java @@ -33,13 +33,13 @@ import org.onap.cps.api.CpsAnchorService; import org.onap.cps.api.CpsDataService; import org.onap.cps.api.CpsDataspaceService; import org.onap.cps.api.CpsModuleService; -import org.onap.cps.ncmp.api.impl.exception.NcmpStartUpException; +import org.onap.cps.ncmp.exceptions.NcmpStartUpException; import org.onap.cps.spi.CascadeDeleteAllowed; import org.onap.cps.spi.exceptions.AlreadyDefinedException; import org.onap.cps.utils.JsonObjectMapper; import org.springframework.beans.factory.annotation.Value; import org.springframework.boot.SpringApplication; -import org.springframework.boot.context.event.ApplicationReadyEvent; +import org.springframework.boot.context.event.ApplicationStartedEvent; @Slf4j @RequiredArgsConstructor @@ -61,12 +61,12 @@ abstract class AbstractModelLoader implements ModelLoader { long retryTimeMs; @Override - public void onApplicationEvent(@NonNull final ApplicationReadyEvent applicationReadyEvent) { + public void onApplicationEvent(@NonNull final ApplicationStartedEvent applicationStartedEvent) { try { onboardOrUpgradeModel(); } catch (final NcmpStartUpException ncmpStartUpException) { log.error("Onboarding model for NCMP failed: {} ", ncmpStartUpException.getMessage()); - SpringApplication.exit(applicationReadyEvent.getApplicationContext(), () -> EXIT_CODE_ON_ERROR); + SpringApplication.exit(applicationStartedEvent.getApplicationContext(), () -> EXIT_CODE_ON_ERROR); } } diff --git a/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/init/CmDataSubscriptionModelLoader.java b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/init/CmDataSubscriptionModelLoader.java index a0b7bd5826..780b240b6c 100644 --- a/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/init/CmDataSubscriptionModelLoader.java +++ b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/init/CmDataSubscriptionModelLoader.java @@ -20,7 +20,7 @@ package org.onap.cps.ncmp.init; -import static org.onap.cps.ncmp.api.impl.ncmppersistence.NcmpPersistence.NCMP_DATASPACE_NAME; +import static org.onap.cps.ncmp.impl.inventory.NcmpPersistence.NCMP_DATASPACE_NAME; import static org.onap.cps.utils.ContentType.JSON; import java.time.OffsetDateTime; @@ -29,8 +29,8 @@ import org.onap.cps.api.CpsAnchorService; import org.onap.cps.api.CpsDataService; import org.onap.cps.api.CpsDataspaceService; import org.onap.cps.api.CpsModuleService; -import org.onap.cps.ncmp.api.impl.exception.NcmpStartUpException; -import org.onap.cps.ncmp.api.impl.operations.DatastoreType; +import org.onap.cps.ncmp.api.data.models.DatastoreType; +import org.onap.cps.ncmp.exceptions.NcmpStartUpException; import org.onap.cps.spi.exceptions.AlreadyDefinedException; import org.springframework.stereotype.Service; diff --git a/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/init/InventoryModelLoader.java b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/init/InventoryModelLoader.java index 7c25953f0d..76d12f290c 100644 --- a/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/init/InventoryModelLoader.java +++ b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/init/InventoryModelLoader.java @@ -20,9 +20,9 @@ package org.onap.cps.ncmp.init; -import static org.onap.cps.ncmp.api.impl.ncmppersistence.NcmpPersistence.NCMP_DATASPACE_NAME; -import static org.onap.cps.ncmp.api.impl.ncmppersistence.NcmpPersistence.NCMP_DMI_REGISTRY_ANCHOR; -import static org.onap.cps.ncmp.api.impl.ncmppersistence.NcmpPersistence.NFP_OPERATIONAL_DATASTORE_DATASPACE_NAME; +import static org.onap.cps.ncmp.impl.inventory.NcmpPersistence.NCMP_DATASPACE_NAME; +import static org.onap.cps.ncmp.impl.inventory.NcmpPersistence.NCMP_DMI_REGISTRY_ANCHOR; +import static org.onap.cps.ncmp.impl.inventory.NcmpPersistence.NFP_OPERATIONAL_DATASTORE_DATASPACE_NAME; import lombok.extern.slf4j.Slf4j; import org.onap.cps.api.CpsAnchorService; diff --git a/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/init/ModelLoader.java b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/init/ModelLoader.java index c61bf1c9b1..9832ba3f9e 100644 --- a/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/init/ModelLoader.java +++ b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/init/ModelLoader.java @@ -1,6 +1,6 @@ /* * ============LICENSE_START======================================================= - * Copyright (C) 2023 Nordix Foundation + * Copyright (C) 2023-2024 Nordix Foundation * ================================================================================ * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -21,13 +21,13 @@ package org.onap.cps.ncmp.init; import lombok.NonNull; -import org.springframework.boot.context.event.ApplicationReadyEvent; +import org.springframework.boot.context.event.ApplicationStartedEvent; import org.springframework.context.ApplicationListener; -public interface ModelLoader extends ApplicationListener<ApplicationReadyEvent> { +public interface ModelLoader extends ApplicationListener<ApplicationStartedEvent> { @Override - void onApplicationEvent(@NonNull ApplicationReadyEvent applicationReadyEvent); + void onApplicationEvent(@NonNull ApplicationStartedEvent applicationStartedEvent); void onboardOrUpgradeModel(); diff --git a/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/events/mapper/CloudEventMapper.java b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/utils/events/CloudEventMapper.java index 4120970e52..4462b169e2 100644 --- a/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/events/mapper/CloudEventMapper.java +++ b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/utils/events/CloudEventMapper.java @@ -18,7 +18,7 @@ * ============LICENSE_END========================================================= */ -package org.onap.cps.ncmp.api.impl.events.mapper; +package org.onap.cps.ncmp.utils.events; import com.fasterxml.jackson.databind.ObjectMapper; import io.cloudevents.CloudEvent; diff --git a/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/events/avc/ncmptoclient/AvcEventPublisher.java b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/utils/events/CmAvcEventPublisher.java index 7afe606f4f..2a9717cc1a 100644 --- a/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/events/avc/ncmptoclient/AvcEventPublisher.java +++ b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/utils/events/CmAvcEventPublisher.java @@ -18,7 +18,7 @@ * ============LICENSE_END========================================================= */ -package org.onap.cps.ncmp.api.impl.events.avc.ncmptoclient; +package org.onap.cps.ncmp.utils.events; import io.cloudevents.CloudEvent; import java.util.Collections; @@ -26,7 +26,6 @@ import java.util.HashMap; import java.util.Map; import lombok.RequiredArgsConstructor; import org.onap.cps.events.EventsPublisher; -import org.onap.cps.ncmp.api.impl.events.NcmpEvent; import org.onap.cps.ncmp.events.avc.ncmp_to_client.Avc; import org.onap.cps.ncmp.events.avc.ncmp_to_client.AvcEvent; import org.onap.cps.ncmp.events.avc.ncmp_to_client.Data; @@ -35,7 +34,7 @@ import org.springframework.stereotype.Service; @Service @RequiredArgsConstructor -public class AvcEventPublisher { +public class CmAvcEventPublisher { private final EventsPublisher<CloudEvent> eventsPublisher; diff --git a/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/events/NcmpEvent.java b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/utils/events/NcmpEvent.java index 248db9805c..8d3190eb00 100644 --- a/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/events/NcmpEvent.java +++ b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/utils/events/NcmpEvent.java @@ -18,7 +18,7 @@ * ============LICENSE_END========================================================= */ -package org.onap.cps.ncmp.api.impl.events; +package org.onap.cps.ncmp.utils.events; import io.cloudevents.CloudEvent; import io.cloudevents.core.builder.CloudEventBuilder; @@ -27,8 +27,8 @@ import java.util.Map; import java.util.UUID; import lombok.Builder; import org.apache.commons.lang3.StringUtils; -import org.onap.cps.ncmp.api.impl.utils.EventDateTimeFormatter; -import org.onap.cps.ncmp.api.impl.utils.context.CpsApplicationContext; +import org.onap.cps.ncmp.config.CpsApplicationContext; +import org.onap.cps.ncmp.impl.utils.EventDateTimeFormatter; import org.onap.cps.utils.JsonObjectMapper; @Builder diff --git a/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/NetworkCmProxyQueryService.java b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/utils/events/TopicValidator.java index 340806b892..07da7f76a0 100644 --- a/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/NetworkCmProxyQueryService.java +++ b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/utils/events/TopicValidator.java @@ -18,24 +18,30 @@ * ============LICENSE_END========================================================= */ -package org.onap.cps.ncmp.api; +package org.onap.cps.ncmp.utils.events; -import org.onap.cps.spi.FetchDescendantsOption; +import java.util.regex.Pattern; +import lombok.AccessLevel; +import lombok.NoArgsConstructor; +import org.onap.cps.ncmp.api.exceptions.InvalidTopicException; -/* - * Datastore interface for handling cached CPS data query requests. - */ -public interface NetworkCmProxyQueryService { +@NoArgsConstructor(access = AccessLevel.PRIVATE) +public class TopicValidator { + + private static final Pattern TOPIC_NAME_PATTERN = Pattern.compile("^[a-zA-Z0-9]([._-](?![._-])|" + + "[a-zA-Z0-9]){0,120}[a-zA-Z0-9]$"); /** - * Get resource data for operational. + * Validate kafka topic name pattern. * - * @param cmHandleId cm handle identifier - * @param cpsPath cps path - * @Link FetchDescendantsOption fetch descendants option - * @return {@code Object} resource data + * @param topicName name of the topic to be validated + * + * @throws InvalidTopicException if the topic is not valid */ - Object queryResourceDataOperational(String cmHandleId, - String cpsPath, - FetchDescendantsOption fetchDescendantsOption); + public static void validateTopicName(final String topicName) { + if (!TOPIC_NAME_PATTERN.matcher(topicName).matches()) { + throw new InvalidTopicException("Topic name " + topicName + " is invalid", "invalid topic"); + } + } + } |