From: egernug Date: Tue, 10 Mar 2026 09:10:12 +0000 (+0000) Subject: Register new Datajob (Bulk) DMIs (Step 2) X-Git-Url: https://gerrit.onap.org/r/gitweb?a=commitdiff_plain;h=cab813a175952935d19b98727c2701610dab43c4;p=cps.git Register new Datajob (Bulk) DMIs (Step 2) - Added all logic for new Bulk DMIs - Removed validation od DMI Registration - Changed WriteRequestExaminer to use DATAJOBS_WRITE - Changed WebClient logic to include new DMIs - Refactored service resolution logic with fallback strategy - Extended test cases to reflect new fields Issue-ID: CPS-3179 Change-Id: I97490270b0accdff81a70ec7ae96631c6754518d Signed-off-by: egernug --- diff --git a/cps-ncmp-rest/docs/openapi/components.yaml b/cps-ncmp-rest/docs/openapi/components.yaml index 002dca19ee..61521a8382 100644 --- a/cps-ncmp-rest/docs/openapi/components.yaml +++ b/cps-ncmp-rest/docs/openapi/components.yaml @@ -1,5 +1,5 @@ # ============LICENSE_START======================================================= -# Copyright (C) 2021-2025 OpenInfra Foundation Europe. All rights reserved. +# Copyright (C) 2021-2026 OpenInfra Foundation Europe. All rights reserved. # Modifications Copyright (C) 2021 Pantheon.tech # Modifications Copyright (C) 2022 Bell Canada # ================================================================================ @@ -64,6 +64,14 @@ components: type: string example: my-dmi-model-plugin default: "" + dmiDatajobsReadPlugin: + type: string + example: my-dmi-datajobs-read-plugin + default: "" + dmiDatajobsWritePlugin: + type: string + example: my-dmi-datajobs-write-plugin + default: "" createdCmHandles: type: array items: diff --git a/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/inventory/models/DmiPluginRegistration.java b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/inventory/models/DmiPluginRegistration.java index 44e2ebe320..baf341b5b8 100644 --- a/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/inventory/models/DmiPluginRegistration.java +++ b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/inventory/models/DmiPluginRegistration.java @@ -1,6 +1,6 @@ /* * ============LICENSE_START======================================================= - * Copyright (C) 2021-2024 Nordix Foundation + * Copyright (C) 2021-2026 OpenInfra Foundation Europe. All rights reserved. * ================================================================================ * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -22,13 +22,10 @@ package org.onap.cps.ncmp.api.inventory.models; import com.fasterxml.jackson.annotation.JsonInclude; import com.fasterxml.jackson.annotation.JsonInclude.Include; -import com.google.common.base.Strings; import java.util.Collections; import java.util.List; import lombok.Getter; import lombok.Setter; -import org.onap.cps.ncmp.api.exceptions.DmiRequestException; -import org.onap.cps.ncmp.api.exceptions.NcmpException; /** * Dmi Registry request object. @@ -44,6 +41,10 @@ public class DmiPluginRegistration { private String dmiModelPlugin; + private String dmiDatajobsReadPlugin; + + private String dmiDatajobsWritePlugin; + private List createdCmHandles = Collections.emptyList(); private List updatedCmHandles = Collections.emptyList(); @@ -52,38 +53,4 @@ public class DmiPluginRegistration { private UpgradedCmHandles upgradedCmHandles = new UpgradedCmHandles(); - /** - * Validates plugin service names. - * @throws NcmpException if validation fails. - */ - public void validateDmiPluginRegistration() throws NcmpException { - final String combinedServiceName = dmiPlugin; - final String dataServiceName = dmiDataPlugin; - final String modelsServiceName = dmiModelPlugin; - - String errorMessage = null; - - if (isNullEmptyOrBlank(combinedServiceName)) { - if ((isNullEmptyOrBlank(dataServiceName) && isNullEmptyOrBlank(modelsServiceName))) { - errorMessage = "No DMI plugin service names"; - } else { - if (isNullEmptyOrBlank(dataServiceName) || isNullEmptyOrBlank(modelsServiceName)) { - errorMessage = "Cannot register just a Data or Model plugin service name"; - } - } - } else { - if (!isNullEmptyOrBlank(dataServiceName) || !isNullEmptyOrBlank(modelsServiceName)) { - errorMessage = "Cannot register combined plugin service name and other service names"; - } - } - - if (errorMessage != null) { - throw new DmiRequestException(errorMessage, "Please supply correct plugin information."); - } - } - - public static boolean isNullEmptyOrBlank(final String serviceName) { - return Strings.isNullOrEmpty(serviceName) || serviceName.isBlank(); - } - } diff --git a/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/inventory/models/NcmpServiceCmHandle.java b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/inventory/models/NcmpServiceCmHandle.java index 2f571f1c3d..85fad9057b 100644 --- a/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/inventory/models/NcmpServiceCmHandle.java +++ b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/inventory/models/NcmpServiceCmHandle.java @@ -1,6 +1,6 @@ /* * ============LICENSE_START======================================================= - * Copyright (C) 2021-2024 Nordix Foundation + * Copyright (C) 2021-2026 Nordix Foundation * ================================================================================ * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -50,6 +50,12 @@ public class NcmpServiceCmHandle { @JsonSetter(nulls = Nulls.AS_EMPTY) private String dmiModelServiceName; + @JsonSetter(nulls = Nulls.AS_EMPTY) + private String dmiDatajobsReadServiceName; + + @JsonSetter(nulls = Nulls.AS_EMPTY) + private String dmiDatajobsWriteServiceName; + @JsonSetter(nulls = Nulls.AS_EMPTY) private Map additionalProperties = Collections.emptyMap(); 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 index fd9707eb06..f9a463c66f 100644 --- 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 @@ -1,6 +1,6 @@ /* * ============LICENSE_START======================================================= - * Copyright (C) 2024-2025 OpenInfra Foundation Europe. All rights reserved. + * Copyright (C) 2024-2026 OpenInfra Foundation Europe. All rights reserved. * ================================================================================ * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -74,7 +74,7 @@ public class DmiSubJobRequestHandler { final UrlTemplateParameters urlTemplateParameters = getUrlTemplateParameters(dataJobMetadata.destination(), producerKey); final ResponseEntity responseEntity = dmiRestClient.synchronousPostOperation( - RequiredDmiService.DATA, + RequiredDmiService.DATAJOBS_WRITE, urlTemplateParameters, jsonObjectMapper.asJsonString(subJobWriteRequest), OperationType.CREATE, 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 index e6c3db8059..ac9aa3af51 100644 --- 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 @@ -103,7 +103,7 @@ public class WriteRequestExaminer { private ProducerKey createProducerKey(final YangModelCmHandle yangModelCmHandle) { final String dmiDataServiceName = - DmiServiceNameResolver.resolveDmiServiceName(RequiredDmiService.DATA, yangModelCmHandle); + DmiServiceNameResolver.resolveDmiServiceName(RequiredDmiService.DATAJOBS_WRITE, yangModelCmHandle); return new ProducerKey(dmiDataServiceName, yangModelCmHandle.getDataProducerIdentifier()); } 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 index d1172ced15..f1c3078acf 100644 --- 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 @@ -25,7 +25,7 @@ import static org.onap.cps.ncmp.api.NcmpResponseStatus.DMI_SERVICE_NOT_RESPONDIN 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.onap.cps.ncmp.api.data.models.OperationType.READ; -import static org.onap.cps.ncmp.impl.models.RequiredDmiService.DATA; +import static org.onap.cps.ncmp.impl.models.RequiredDmiService.MODEL; import static org.springframework.http.HttpStatus.INTERNAL_SERVER_ERROR; import static org.springframework.http.HttpStatus.REQUEST_TIMEOUT; @@ -250,7 +250,7 @@ public class DmiRestClient { } private WebClient getWebClient(final RequiredDmiService requiredDmiService) { - return DATA.equals(requiredDmiService) ? dataServicesWebClient : modelServicesWebClient; + return requiredDmiService == MODEL ? modelServicesWebClient : dataServicesWebClient; } private void configureHttpHeaders(final HttpHeaders httpHeaders, final String authorization) { diff --git a/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/impl/dmi/DmiServiceNameResolver.java b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/impl/dmi/DmiServiceNameResolver.java index e9378daaec..935322b050 100644 --- a/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/impl/dmi/DmiServiceNameResolver.java +++ b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/impl/dmi/DmiServiceNameResolver.java @@ -1,6 +1,6 @@ /* * ============LICENSE_START======================================================= - * Copyright (C) 2024 Nordix Foundation + * Copyright (C) 2024-2026 OpenInfra Foundation Europe. All rights reserved. * ================================================================================ * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -20,6 +20,11 @@ package org.onap.cps.ncmp.impl.dmi; +import static org.onap.cps.ncmp.impl.models.RequiredDmiService.DATA; +import static org.onap.cps.ncmp.impl.models.RequiredDmiService.DATAJOBS_READ; +import static org.onap.cps.ncmp.impl.models.RequiredDmiService.DATAJOBS_WRITE; +import static org.onap.cps.ncmp.impl.models.RequiredDmiService.MODEL; + import lombok.AccessLevel; import lombok.NoArgsConstructor; import org.apache.commons.lang3.StringUtils; @@ -28,6 +33,12 @@ import org.onap.cps.ncmp.api.inventory.models.NcmpServiceCmHandle; import org.onap.cps.ncmp.impl.inventory.models.YangModelCmHandle; import org.onap.cps.ncmp.impl.models.RequiredDmiService; +/** + * Resolves DMI service names based on the required service type. + * Resolution follows a priority order: specific service name first, then falls back to the combined service name. + * For example, if DATA service is required and dmiDataServiceName is set, it will be used; + * otherwise, the combined dmiServiceName will be returned. + */ @NoArgsConstructor(access = AccessLevel.PRIVATE) public class DmiServiceNameResolver { @@ -43,7 +54,9 @@ public class DmiServiceNameResolver { return resolveDmiServiceName(requiredService, yangModelCmHandle.getDmiServiceName(), yangModelCmHandle.getDmiDataServiceName(), - yangModelCmHandle.getDmiModelServiceName()); + yangModelCmHandle.getDmiModelServiceName(), + yangModelCmHandle.getDmiDatajobsReadServiceName(), + yangModelCmHandle.getDmiDatajobsWriteServiceName()); } /** @@ -58,7 +71,9 @@ public class DmiServiceNameResolver { return resolveDmiServiceName(requiredService, ncmpServiceCmHandle.getDmiServiceName(), ncmpServiceCmHandle.getDmiDataServiceName(), - ncmpServiceCmHandle.getDmiModelServiceName()); + ncmpServiceCmHandle.getDmiModelServiceName(), + ncmpServiceCmHandle.getDmiDatajobsReadServiceName(), + ncmpServiceCmHandle.getDmiDatajobsWriteServiceName()); } /** @@ -73,19 +88,29 @@ public class DmiServiceNameResolver { return resolveDmiServiceName(requiredService, dmiPluginRegistration.getDmiPlugin(), dmiPluginRegistration.getDmiDataPlugin(), - dmiPluginRegistration.getDmiModelPlugin()); + dmiPluginRegistration.getDmiModelPlugin(), + dmiPluginRegistration.getDmiDatajobsReadPlugin(), + dmiPluginRegistration.getDmiDatajobsWritePlugin()); } private static String resolveDmiServiceName(final RequiredDmiService requiredService, final String dmiServiceName, final String dmiDataServiceName, - final String dmiModelServiceName) { - if (StringUtils.isBlank(dmiServiceName)) { - if (RequiredDmiService.DATA.equals(requiredService)) { - return dmiDataServiceName; - } + final String dmiModelServiceName, + final String dmiDatajobsReadServiceName, + final String dmiDatajobsWriteServiceName) { + if (DATA.equals(requiredService) && StringUtils.isNotBlank(dmiDataServiceName)) { + return dmiDataServiceName; + } + if (MODEL.equals(requiredService) && StringUtils.isNotBlank(dmiModelServiceName)) { return dmiModelServiceName; } + if (DATAJOBS_READ.equals(requiredService) && StringUtils.isNotBlank(dmiDatajobsReadServiceName)) { + return dmiDatajobsReadServiceName; + } + if (DATAJOBS_WRITE.equals(requiredService) && StringUtils.isNotBlank(dmiDatajobsWriteServiceName)) { + return dmiDatajobsWriteServiceName; + } return dmiServiceName; } diff --git a/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/impl/inventory/CmHandleRegistrationService.java b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/impl/inventory/CmHandleRegistrationService.java index 5327f2f2c3..d64f0f3e02 100644 --- a/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/impl/inventory/CmHandleRegistrationService.java +++ b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/impl/inventory/CmHandleRegistrationService.java @@ -1,6 +1,6 @@ /* * ============LICENSE_START======================================================= - * Copyright (C) 2021-2025 OpenInfra Foundation Europe. All rights reserved. + * Copyright (C) 2021-2026 OpenInfra Foundation Europe. All rights reserved. * Modifications Copyright (C) 2021 Pantheon.tech * Modifications Copyright (C) 2021-2022 Bell Canada * Modifications Copyright (C) 2023 Deutsche Telekom AG @@ -93,7 +93,6 @@ public class CmHandleRegistrationService { */ public DmiPluginRegistrationResponse updateDmiRegistration(final DmiPluginRegistration dmiPluginRegistration) { - dmiPluginRegistration.validateDmiPluginRegistration(); final DmiPluginRegistrationResponse dmiPluginRegistrationResponse = new DmiPluginRegistrationResponse(); trustLevelManager.registerDmiPlugin(dmiPluginRegistration); diff --git a/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/impl/inventory/models/YangModelCmHandle.java b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/impl/inventory/models/YangModelCmHandle.java index 858f4696e4..3d14a53d4e 100644 --- a/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/impl/inventory/models/YangModelCmHandle.java +++ b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/impl/inventory/models/YangModelCmHandle.java @@ -102,6 +102,8 @@ public class YangModelCmHandle { copy.dmiServiceName = original.getDmiServiceName(); copy.dmiDataServiceName = original.getDmiDataServiceName(); copy.dmiModelServiceName = original.getDmiModelServiceName(); + copy.dmiDatajobsReadServiceName = original.getDmiDatajobsReadServiceName(); + copy.dmiDatajobsWriteServiceName = original.getDmiDatajobsWriteServiceName(); copy.compositeState = original.getCompositeState() == null ? null : new CompositeState(original.getCompositeState()); copy.additionalProperties = original.getAdditionalProperties() @@ -140,6 +142,8 @@ public class YangModelCmHandle { yangModelCmHandle.setDmiServiceName(dmiPluginRegistration.getDmiPlugin()); yangModelCmHandle.setDmiDataServiceName(dmiPluginRegistration.getDmiDataPlugin()); yangModelCmHandle.setDmiModelServiceName(dmiPluginRegistration.getDmiModelPlugin()); + yangModelCmHandle.setDmiDatajobsReadServiceName(dmiPluginRegistration.getDmiDatajobsReadPlugin()); + yangModelCmHandle.setDmiDatajobsWriteServiceName(dmiPluginRegistration.getDmiDatajobsWritePlugin()); yangModelCmHandle.setModuleSetTag(StringUtils.trimToEmpty(moduleSetTag)); yangModelCmHandle.setAlternateId(StringUtils.trimToEmpty(alternateId)); yangModelCmHandle.setDataProducerIdentifier(StringUtils.trimToEmpty(dataProducerIdentifier)); diff --git a/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/impl/models/RequiredDmiService.java b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/impl/models/RequiredDmiService.java index cb85205f87..fa59ecb6f5 100644 --- a/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/impl/models/RequiredDmiService.java +++ b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/impl/models/RequiredDmiService.java @@ -1,6 +1,6 @@ /* * ============LICENSE_START======================================================= - * Copyright (C) 2021-2025 OpenInfra Foundation Europe. All rights reserved. + * Copyright (C) 2021-2026 OpenInfra Foundation Europe. All rights reserved. * ================================================================================ * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -21,8 +21,8 @@ package org.onap.cps.ncmp.impl.models; /** - * Enum to determine if the required service is for a data or model operation. + * Enum to determine if the required service is for a data, model, or data job operation. */ public enum RequiredDmiService { - DATA, MODEL + DATA, MODEL, DATAJOBS_READ, DATAJOBS_WRITE } diff --git a/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/impl/utils/YangDataConverter.java b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/impl/utils/YangDataConverter.java index a79f2b1384..d707a658ff 100644 --- a/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/impl/utils/YangDataConverter.java +++ b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/impl/utils/YangDataConverter.java @@ -1,6 +1,6 @@ /* * ============LICENSE_START======================================================= - * Copyright (C) 2022-2025 OpenInfra Foundation Europe. All rights reserved. + * Copyright (C) 2022-2026 OpenInfra Foundation Europe. All rights reserved. * ================================================================================ * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -56,6 +56,8 @@ public class YangDataConverter { ncmpServiceCmHandle.setDmiServiceName(yangModelCmHandle.getDmiServiceName()); ncmpServiceCmHandle.setDmiDataServiceName(yangModelCmHandle.getDmiDataServiceName()); ncmpServiceCmHandle.setDmiModelServiceName(yangModelCmHandle.getDmiModelServiceName()); + ncmpServiceCmHandle.setDmiDatajobsReadServiceName(yangModelCmHandle.getDmiDatajobsReadServiceName()); + ncmpServiceCmHandle.setDmiDatajobsWriteServiceName(yangModelCmHandle.getDmiDatajobsWriteServiceName()); ncmpServiceCmHandle.setCompositeState(yangModelCmHandle.getCompositeState()); ncmpServiceCmHandle.setModuleSetTag(yangModelCmHandle.getModuleSetTag()); ncmpServiceCmHandle.setAlternateId(yangModelCmHandle.getAlternateId()); @@ -95,6 +97,10 @@ public class YangDataConverter { dmiPluginRegistration.setDmiPlugin(safeGetLeafValue(cmHandleDataNode, "dmi-service-name")); dmiPluginRegistration.setDmiDataPlugin(safeGetLeafValue(cmHandleDataNode, "dmi-data-service-name")); dmiPluginRegistration.setDmiModelPlugin(safeGetLeafValue(cmHandleDataNode, "dmi-model-service-name")); + dmiPluginRegistration.setDmiDatajobsReadPlugin(safeGetLeafValue( + cmHandleDataNode, "dmi-datajobs-read-service")); + dmiPluginRegistration.setDmiDatajobsWritePlugin(safeGetLeafValue( + cmHandleDataNode, "dmi-datajobs-write-service")); return YangModelCmHandle.toYangModelCmHandle( dmiPluginRegistration, ncmpServiceCmHandle, diff --git a/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/api/exceptions/DmiRequestExceptionSpec.groovy b/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/api/exceptions/DmiRequestExceptionSpec.groovy new file mode 100644 index 0000000000..d4930115a4 --- /dev/null +++ b/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/api/exceptions/DmiRequestExceptionSpec.groovy @@ -0,0 +1,36 @@ +/* + * ============LICENSE_START======================================================= + * Copyright (C) 2025-2026 OpenInfra Foundation Europe. All rights reserved. + * ================================================================================ + * 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 spock.lang.Specification + +class DmiRequestExceptionSpec extends Specification { + + def objectUnderTest = new DmiRequestException('my message', 'my details') + + def 'A DMI request exception.'() { + expect: 'the exception has the correct message' + objectUnderTest.message == 'my message' + and: 'the exception has the correct details' + objectUnderTest.details == 'my details' + } + +} diff --git a/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/impl/datajobs/DmiSubJobRequestHandlerSpec.groovy b/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/impl/datajobs/DmiSubJobRequestHandlerSpec.groovy index b10c96ed5b..e125fa34a5 100644 --- a/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/impl/datajobs/DmiSubJobRequestHandlerSpec.groovy +++ b/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/impl/datajobs/DmiSubJobRequestHandlerSpec.groovy @@ -1,3 +1,24 @@ +/* + * ============LICENSE_START======================================================= + * Copyright (C) 2024-2026 OpenInfra Foundation Europe. All rights reserved. + * ================================================================================ + * 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 com.fasterxml.jackson.databind.ObjectMapper @@ -8,6 +29,7 @@ import org.onap.cps.ncmp.api.datajobs.models.ProducerKey import org.onap.cps.ncmp.impl.dmi.DmiServiceAuthenticationProperties import org.onap.cps.ncmp.impl.dmi.DmiRestClient 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.http.HttpStatus import org.springframework.http.ResponseEntity @@ -31,7 +53,7 @@ class DmiSubJobRequestHandlerSpec extends Specification { def responseAsKeyValuePairs = [subJobId:'my-sub-job-id'] def responseEntity = new ResponseEntity<>(responseAsKeyValuePairs, HttpStatus.OK) def expectedJson = '{"destination":"d1","dataAcceptType":"t1","dataContentType":"t2","dataProducerId":"prod1","dataJobId":"some-job-id","data":[{"path":"p","op":"operation","moduleSetTag":"tag","value":null,"operationId":"o1"}]}' - mockDmiRestClient.synchronousPostOperation(RequiredDmiService.DATA, _, expectedJson, OperationType.CREATE, authorization) >> responseEntity + mockDmiRestClient.synchronousPostOperation(RequiredDmiService.DATAJOBS_WRITE, _ as UrlTemplateParameters, expectedJson, OperationType.CREATE, authorization) >> responseEntity when: 'sending request to DMI invoked' objectUnderTest.sendRequestsToDmi(authorization, dataJobId, dataJobMetadata, dmiWriteOperationsPerProducerKey) then: 'the result contains the expected sub-job id' diff --git a/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/impl/datajobs/WriteRequestExaminerSpec.groovy b/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/impl/datajobs/WriteRequestExaminerSpec.groovy index d9500699f5..e2445795f1 100644 --- a/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/impl/datajobs/WriteRequestExaminerSpec.groovy +++ b/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/impl/datajobs/WriteRequestExaminerSpec.groovy @@ -1,6 +1,6 @@ /* * ============LICENSE_START======================================================= - * Copyright (C) 2024-2025 OpenInfra Foundation Europe. All rights reserved. + * Copyright (C) 2024-2026 OpenInfra Foundation Europe. All rights reserved. * ================================================================================ * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -92,16 +92,16 @@ class WriteRequestExaminerSpec extends Specification { } def 'Validate the creation of a ProducerKey with correct dmiservicename.'() { - given: 'yangModelCmHandles with service name: "#dmiServiceName" and data service name: "#dataServiceName"' - def yangModelCmHandle = new YangModelCmHandle(dmiServiceName: dmiServiceName, dmiDataServiceName: dataServiceName, dataProducerIdentifier: 'dpi1') + given: 'a yang model cm handle' + def yangModelCmHandle = new YangModelCmHandle(dmiServiceName: dmiServiceName, dmiDatajobsWriteServiceName: datajobsWriteServiceName, dataProducerIdentifier: 'dpi1') when: 'the ProducerKey is created' def result = objectUnderTest.createProducerKey(yangModelCmHandle).toString() - then: 'we get the ProducerKey with the correct service name' + then: 'the ProducerKey has the correct service name' assert result == expectedProducerKey - where: 'the following services are registered' - dmiServiceName | dataServiceName || expectedProducerKey - 'dmi-service-name' | '' || 'dmi-service-name#dpi1' - '' | 'dmi-data-service-name' || 'dmi-data-service-name#dpi1' - 'dmi-service-name' | 'dmi-data-service-name' || 'dmi-service-name#dpi1' + where: + scenario | dmiServiceName | datajobsWriteServiceName || expectedProducerKey + 'fallback to common service' | 'dmi-service-name' | '' || 'dmi-service-name#dpi1' + 'specific datajobs write service' | '' | 'dmi-datajobs-write-service' || 'dmi-datajobs-write-service#dpi1' + 'datajobs write takes precedence' | 'dmi-service-name' | 'dmi-datajobs-write-service' || 'dmi-datajobs-write-service#dpi1' } } diff --git a/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/impl/inventory/CmHandleRegistrationServiceSpec.groovy b/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/impl/inventory/CmHandleRegistrationServiceSpec.groovy index 979fba1f31..b4d02aeb21 100644 --- a/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/impl/inventory/CmHandleRegistrationServiceSpec.groovy +++ b/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/impl/inventory/CmHandleRegistrationServiceSpec.groovy @@ -1,6 +1,6 @@ /* * ============LICENSE_START======================================================= - * Copyright (C) 2021-2025 OpenInfra Foundation Europe. All rights reserved. + * Copyright (C) 2021-2026 OpenInfra Foundation Europe. All rights reserved. * Modifications Copyright (C) 2022 Bell Canada * ================================================================================ * Licensed under the Apache License, Version 2.0 (the "License"); @@ -27,7 +27,6 @@ import org.onap.cps.api.exceptions.AlreadyDefinedException import org.onap.cps.api.exceptions.CpsException import org.onap.cps.api.exceptions.DataNodeNotFoundException import org.onap.cps.api.exceptions.DataValidationException -import org.onap.cps.ncmp.api.exceptions.DmiRequestException import org.onap.cps.ncmp.api.inventory.DataStoreSyncState import org.onap.cps.ncmp.api.inventory.models.CmHandleRegistrationResponse import org.onap.cps.ncmp.api.inventory.models.CompositeState @@ -157,46 +156,6 @@ class CmHandleRegistrationServiceSpec extends Specification { assert result.upgradedCmHandles[0].ncmpResponseStatus == UNKNOWN_ERROR } - def 'Create CM-handle Validation: Registration with valid Service names: #scenario'() { - given: 'a registration ' - def dmiPluginRegistration = new DmiPluginRegistration(dmiPlugin: dmiPlugin, dmiModelPlugin: dmiModelPlugin, - dmiDataPlugin: dmiDataPlugin) - dmiPluginRegistration.createdCmHandles = [ncmpServiceCmHandle] - when: 'update registration and sync module is called with correct DMI plugin information' - objectUnderTest.updateDmiRegistration(dmiPluginRegistration) - then: 'create cm handles registration and sync modules is called with the correct plugin information' - 1 * objectUnderTest.processCreatedCmHandles(dmiPluginRegistration, _) - where: - scenario | dmiPlugin | dmiModelPlugin | dmiDataPlugin || expectedDmiPluginRegisteredName - 'combined DMI plugin' | 'service1' | '' | '' || 'service1' - 'data & model DMI plugins' | '' | 'service1' | 'service2' || 'service2' - 'data & model using same service' | '' | 'service1' | 'service1' || 'service1' - } - - def 'Create CM-handle Validation: Invalid DMI plugin service name with #scenario'() { - given: 'a registration ' - def dmiPluginRegistration = new DmiPluginRegistration(dmiPlugin: dmiPlugin, dmiModelPlugin: dmiModelPlugin, - dmiDataPlugin: dmiDataPlugin) - dmiPluginRegistration.createdCmHandles = [ncmpServiceCmHandle] - when: 'registration is called with incorrect DMI plugin information' - objectUnderTest.updateDmiRegistration(dmiPluginRegistration) - then: 'a DMI Request Exception is thrown with correct message details' - def exceptionThrown = thrown(DmiRequestException.class) - assert exceptionThrown.getMessage().contains(expectedMessageDetails) - and: 'registration is not called' - 0 * objectUnderTest.processCreatedCmHandles(*_) - where: - scenario | dmiPlugin | dmiModelPlugin | dmiDataPlugin || expectedMessageDetails - 'empty DMI plugins' | '' | '' | '' || 'No DMI plugin service names' - 'blank DMI plugins' | ' ' | ' ' | ' ' || 'No DMI plugin service names' - 'null DMI plugins' | null | null | null || 'No DMI plugin service names' - 'all DMI plugins' | 'service1' | 'service2' | 'service3' || 'Cannot register combined plugin service name and other service names' - '(combined)DMI and Data Plugin' | 'service1' | '' | 'service2' || 'Cannot register combined plugin service name and other service names' - '(combined)DMI and model Plugin' | 'service1' | 'service2' | '' || 'Cannot register combined plugin service name and other service names' - 'only model DMI plugin' | '' | 'service1' | '' || 'Cannot register just a Data or Model plugin service name' - 'only data DMI plugin' | '' | '' | 'service1' || 'Cannot register just a Data or Model plugin service name' - } - def 'Create CM-Handle Successfully: #scenario.'() { given: 'a registration without cm-handle properties' def dmiPluginRegistration = new DmiPluginRegistration(dmiPlugin: 'my-server') @@ -225,6 +184,30 @@ class CmHandleRegistrationServiceSpec extends Specification { 'without additional & public properties' | [:] | [:] || [:] | [:] } + def 'Create CM-handle with data jobs plugins: #scenario'() { + given: 'a registration with data jobs plugins' + def dmiPluginRegistration = new DmiPluginRegistration( + dmiPlugin: dmiPlugin, + dmiDataPlugin: dmiDataPlugin, + dmiModelPlugin: dmiModelPlugin, + dmiDatajobsReadPlugin: dmiDatajobsReadPlugin, + dmiDatajobsWritePlugin: dmiDatajobsWritePlugin + ) + dmiPluginRegistration.createdCmHandles = [new NcmpServiceCmHandle(cmHandleId: 'cmhandle')] + when: 'registration is updated' + def response = objectUnderTest.updateDmiRegistration(dmiPluginRegistration) + then: 'a successful response is received' + response.createdCmHandles.size() == 1 + response.createdCmHandles[0].status == Status.SUCCESS + where: + scenario | dmiPlugin | dmiDataPlugin | dmiModelPlugin | dmiDatajobsReadPlugin | dmiDatajobsWritePlugin + 'only data jobs write plugin' | '' | '' | '' | '' | 'datajobs-write-service' + 'only data jobs read plugin' | '' | '' | '' | 'datajobs-read-service' | '' + 'both data jobs plugins' | '' | '' | '' | 'datajobs-read-service' | 'datajobs-write-service' + 'combined with data jobs plugins' | 'combined' | '' | '' | 'datajobs-read-service' | 'datajobs-write-service' + 'all plugins including data jobs' | '' | 'data-svc' | 'model-svc' | 'datajobs-read-service' | 'datajobs-write-service' + } + def 'Add CM-Handle #scenario.'() { given: ' registration details for one cm handles' def dmiPluginRegistration = new DmiPluginRegistration(dmiPlugin: 'my-server', diff --git a/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/impl/inventory/models/YangModelCmHandleSpec.groovy b/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/impl/inventory/models/YangModelCmHandleSpec.groovy index a9b6b00b2d..acd3810ac9 100644 --- a/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/impl/inventory/models/YangModelCmHandleSpec.groovy +++ b/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/impl/inventory/models/YangModelCmHandleSpec.groovy @@ -1,6 +1,6 @@ /* * ============LICENSE_START======================================================= - * Copyright (C) 2021-2025 OpenInfra Foundation Europe. All rights reserved. + * Copyright (C) 2021-2026 OpenInfra Foundation Europe. All rights reserved. * ================================================================================ * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -30,6 +30,8 @@ import org.onap.cps.ncmp.api.inventory.DataStoreSyncState import spock.lang.Specification import static org.onap.cps.ncmp.impl.models.RequiredDmiService.DATA +import static org.onap.cps.ncmp.impl.models.RequiredDmiService.DATAJOBS_READ +import static org.onap.cps.ncmp.impl.models.RequiredDmiService.DATAJOBS_WRITE import static org.onap.cps.ncmp.impl.models.RequiredDmiService.MODEL class YangModelCmHandleSpec extends Specification { @@ -73,26 +75,27 @@ class YangModelCmHandleSpec extends Specification { def 'Resolve DMI service name: #scenario and #requiredService service require.'() { given: 'a yang model cm handle' - def dmiPluginRegistration = new DmiPluginRegistration( - dmiPlugin: dmiServiceName, - dmiDataPlugin: dmiDataServiceName, - dmiModelPlugin: dmiModelServiceName + def yangModelCmHandle = new YangModelCmHandle( + dmiServiceName: dmiServiceName, + dmiDataServiceName: dmiDataServiceName, + dmiModelServiceName: dmiModelServiceName, + dmiDatajobsReadServiceName: dmiDatajobsReadServiceName, + dmiDatajobsWriteServiceName: dmiDatajobsWriteServiceName ) - def objectUnderTest = YangModelCmHandle.toYangModelCmHandle(dmiPluginRegistration, new NcmpServiceCmHandle(cmHandleId: 'cm-handle-id-1'),'', '', '', '', '') expect: - assert objectUnderTest.resolveDmiServiceName(requiredService) == expectedService + assert yangModelCmHandle.resolveDmiServiceName(requiredService) == expectedService where: - scenario | dmiServiceName | dmiDataServiceName | dmiModelServiceName | requiredService || expectedService - 'common service registered' | 'common service' | 'does not matter' | 'does not matter' | DATA || 'common service' - 'common service registered' | 'common service' | 'does not matter' | 'does not matter' | MODEL || 'common service' - 'common service empty' | '' | 'data service' | 'does not matter' | DATA || 'data service' - 'common service empty' | '' | 'does not matter' | 'model service' | MODEL || 'model service' - 'common service blank' | ' ' | 'data service' | 'does not matter' | DATA || 'data service' - 'common service blank' | ' ' | 'does not matter' | 'model service' | MODEL || 'model service' - 'common service null ' | null | 'data service' | 'does not matter' | DATA || 'data service' - 'common service null' | null | 'does not matter' | 'model service' | MODEL || 'model service' - 'only model service registered' | null | null | 'does not matter' | DATA || null - 'only data service registered' | null | 'does not matter' | null | MODEL || null + scenario | dmiServiceName | dmiDataServiceName | dmiModelServiceName | dmiDatajobsReadServiceName | dmiDatajobsWriteServiceName | requiredService || expectedService + 'specific data service registered' | 'common service' | 'data service' | 'does not matter' | null | null | DATA || 'data service' + 'specific model service registered' | 'common service' | 'does not matter' | 'model service' | null | null | MODEL || 'model service' + 'specific datajobs read service' | 'common service' | 'does not matter' | 'does not matter' | 'datajobs-read' | null | DATAJOBS_READ || 'datajobs-read' + 'specific datajobs write service' | 'common service' | 'does not matter' | 'does not matter' | null | 'datajobs-write' | DATAJOBS_WRITE || 'datajobs-write' + 'fallback to common for data' | 'common service' | null | 'does not matter' | null | null | DATA || 'common service' + 'fallback to common for model' | 'common service' | 'does not matter' | null | null | null | MODEL || 'common service' + 'fallback to common for datajobs read' | 'common service' | 'does not matter' | 'does not matter' | null | null | DATAJOBS_READ || 'common service' + 'fallback to common for datajobs write' | 'common service' | 'does not matter' | 'does not matter' | null | null | DATAJOBS_WRITE || 'common service' + 'blank specific ignored, uses fallback' | 'common service' | ' ' | 'does not matter' | null | null | DATA || 'common service' + 'no services available returns null' | null | null | null | null | null | DATA || null } def 'Yang Model Cm Handle Deep Copy.'() { diff --git a/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/impl/utils/YangDataConverterSpec.groovy b/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/impl/utils/YangDataConverterSpec.groovy index 1729d75f32..53ade25bba 100644 --- a/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/impl/utils/YangDataConverterSpec.groovy +++ b/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/impl/utils/YangDataConverterSpec.groovy @@ -1,6 +1,6 @@ /* * ============LICENSE_START======================================================== - * Copyright (C) 2022-2025 OpenInfra Foundation Europe. All rights reserved. + * Copyright (C) 2022-2026 OpenInfra Foundation Europe. All rights reserved. * ================================================================================ * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -20,6 +20,8 @@ package org.onap.cps.ncmp.impl.utils +import org.onap.cps.ncmp.impl.inventory.models.YangModelCmHandle + import static org.onap.cps.ncmp.api.inventory.models.CmHandleState.ADVISED import org.onap.cps.api.model.DataNode @@ -54,6 +56,29 @@ class YangDataConverterSpec extends Specification{ assert yangModelCmHandle.compositeState.cmHandleState == ADVISED } + def 'Convert a cm handle data node with data jobs service names.'() { + given: 'a datanode with data jobs service names' + def dataNodeCmHandle = new DataNode(leaves:['id':'ch-1', 'dmi-service-name': 'dmi1', + 'dmi-datajobs-read-service': 'dmi-read', 'dmi-datajobs-write-service': 'dmi-write']) + when: 'the dataNode is converted' + def yangModelCmHandle = YangDataConverter.toYangModelCmHandle(dataNodeCmHandle) + then: 'the data jobs service names are set' + assert yangModelCmHandle.dmiDatajobsReadServiceName == 'dmi-read' + assert yangModelCmHandle.dmiDatajobsWriteServiceName == 'dmi-write' + } + + def 'Convert yang model cm handle to ncmp service cm handle with data jobs service names.'() { + given: 'a yang model cm handle with data jobs service names' + def yangModelCmHandle = new YangModelCmHandle(id: 'ch-1', dmiServiceName: 'dmi1', + dmiDatajobsReadServiceName: 'dmi-read', dmiDatajobsWriteServiceName: 'dmi-write', + additionalProperties: [], publicProperties: []) + when: 'converted to ncmp service cm handle' + def ncmpServiceCmHandle = YangDataConverter.toNcmpServiceCmHandle(yangModelCmHandle) + then: 'the data jobs service names are preserved' + assert ncmpServiceCmHandle.dmiDatajobsReadServiceName == 'dmi-read' + assert ncmpServiceCmHandle.dmiDatajobsWriteServiceName == 'dmi-write' + } + def 'Convert multiple cm handle data nodes'(){ given: 'two data nodes in a collection' def dataNodes = [new DataNode(xpath:'/dmi-registry/cm-handles[@id=\'some-cm-handle\']', leaves: ['id':'some-cm-handle']), diff --git a/docs/api/swagger/cps/openapi.yaml b/docs/api/swagger/cps/openapi.yaml index 6b00856299..fa4cfd7e81 100644 --- a/docs/api/swagger/cps/openapi.yaml +++ b/docs/api/swagger/cps/openapi.yaml @@ -2250,6 +2250,16 @@ paths: default: false example: true type: boolean + - description: Content type in header + in: header + name: Content-Type + required: false + schema: + default: application/json + enum: + - application/json + - application/xml + type: string responses: "200": content: @@ -2259,6 +2269,12 @@ paths: $ref: '#/components/examples/deltaReportSample' schema: type: object + application/xml: + examples: + dataSample: + $ref: '#/components/examples/deltaReportSampleXml' + schema: + type: object description: OK "400": content: @@ -3040,6 +3056,14 @@ components: name: Funny target-data: name: Comic + deltaReportSampleXml: + value: " replace /bookstore/categories[@code='1']\ + \ SciFi Comic\ + \ remove /bookstore/categories[@code='2']\ + \ 2 kids \ + \ create /bookstore/categories[@code='3']\ + \ 3 Fiction \ + \ " dataSampleAcrossAnchors: value: - anchorName: bookstore1 diff --git a/docs/api/swagger/ncmp/openapi-inventory.yaml b/docs/api/swagger/ncmp/openapi-inventory.yaml index 61f666701e..98479f6b2a 100644 --- a/docs/api/swagger/ncmp/openapi-inventory.yaml +++ b/docs/api/swagger/ncmp/openapi-inventory.yaml @@ -312,6 +312,7 @@ components: schemas: RestDmiPluginRegistration: example: + dmiDatajobsReadPlugin: my-dmi-datajobs-read-plugin updatedCmHandles: - cmHandle: my-cm-handle alternateId: "Subnetwork=Europe,ManagedElement=X123" @@ -359,6 +360,7 @@ components: trustLevel: COMPLETE dmiProperties: my-dmi-property dmiPlugin: my-dmi-plugin + dmiDatajobsWritePlugin: my-dmi-datajobs-write-plugin dmiModelPlugin: my-dmi-model-plugin upgradedCmHandles: cmHandles: @@ -384,6 +386,14 @@ components: default: "" example: my-dmi-model-plugin type: string + dmiDatajobsReadPlugin: + default: "" + example: my-dmi-datajobs-read-plugin + type: string + dmiDatajobsWritePlugin: + default: "" + example: my-dmi-datajobs-write-plugin + type: string createdCmHandles: items: $ref: '#/components/schemas/RestInputCmHandle'