diff options
9 files changed, 291 insertions, 53 deletions
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..50d96f858c --- /dev/null +++ b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/datajobs/DataJobStatusService.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 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 requestId The unique identifier for the overall data job request. + * @param dataProducerJobId The identifier of the data producer job within the DMI system. + * @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. + * @return The current status of the data job as a String. + */ + String getDataJobStatus(final String authorization, + final String dmiServiceName, + final String requestId, + final String dataProducerJobId, + final String dataProducerId); +} 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..a6ecaa1097 --- /dev/null +++ b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/impl/datajobs/DataJobStatusServiceImpl.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 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.dmi.DmiServiceUrlTemplateBuilder; +import org.onap.cps.ncmp.impl.dmi.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 requestId, + final String dataProducerJobId, + final String dataProducerId) { + + final UrlTemplateParameters urlTemplateParameters = buildUrlParameters(dmiServiceName, requestId, + dataProducerJobId, dataProducerId); + return dmiRestClient.getDataJobStatus(urlTemplateParameters, authorization).block(); + } + + private UrlTemplateParameters buildUrlParameters(final String dmiServiceName, + final String requestId, + final String dataProducerJobId, + final String dataProducerId) { + return DmiServiceUrlTemplateBuilder.newInstance() + .fixedPathSegment("dataJob") + .variablePathSegment("requestId", requestId) + .fixedPathSegment("dataProducerJob") + .variablePathSegment("dataProducerJobId", dataProducerJobId) + .fixedPathSegment("status") + .queryParameter("dataProducerId", dataProducerId) + .createUrlTemplateParameters(dmiServiceName, dmiProperties.getDmiBasePath()); + } +} 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 7ac85cbf84..ba6bba9c53 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 @@ -144,6 +144,26 @@ public class DmiRestClient { .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(responseHealthStatus -> responseHealthStatus.path("status").asText()) + .onErrorMap(throwable -> handleDmiClientException(throwable, OperationType.READ.getOperationName())); + } + private WebClient getWebClient(final RequiredDmiService requiredDmiService) { return requiredDmiService.equals(RequiredDmiService.DATA) ? dataServicesWebClient : modelServicesWebClient; } diff --git a/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/impl/datajobs/DataJobStatusServiceImplSpec.groovy b/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/impl/datajobs/DataJobStatusServiceImplSpec.groovy new file mode 100644 index 0000000000..cc042988f6 --- /dev/null +++ b/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/impl/datajobs/DataJobStatusServiceImplSpec.groovy @@ -0,0 +1,54 @@ +/* + * ============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 org.onap.cps.ncmp.impl.dmi.DmiProperties +import org.onap.cps.ncmp.impl.dmi.DmiRestClient +import org.onap.cps.ncmp.impl.dmi.UrlTemplateParameters +import reactor.core.publisher.Mono +import spock.lang.Specification + +class DataJobStatusServiceImplSpec extends Specification { + + def mockDmiRestClient = Mock(DmiRestClient) + def mockDmiProperties = Mock(DmiProperties) + def objectUnderTest = new DataJobStatusServiceImpl(mockDmiRestClient, mockDmiProperties) + + def setup() { + mockDmiProperties.dmiBasePath >> 'dmi' + } + + def 'Forward a data job status query to DMI.' () { + given: 'the required parameters for querying' + def dmiServiceName = 'some-dmi-service' + def requestId = 'some-request-id' + def dataProducerJobId = 'some-data-producer-job-id' + def dataJobId = 'some-data-job-id' + def authorization = 'my authorization header' + def urlParams = new UrlTemplateParameters('some-dmi-service/dmi/v1/dataJob/{requestId}/dataProducerJob/{dataProducerJobId}/status?dataProducerId={dataProducerId}', ['dataProducerJobId':'some-data-producer-job-id', 'dataProducerId':'some-data-job-id', 'requestId':'some-request-id']) + and: 'the rest client returns a status for the given parameters' + mockDmiRestClient.getDataJobStatus(urlParams, authorization) >> Mono.just('some status') + when: 'the job status is queried' + def status = objectUnderTest.getDataJobStatus(authorization, dmiServiceName, requestId, dataProducerJobId, dataJobId) + then: 'the status from the rest client is returned' + assert status == 'some status' + } +} diff --git a/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/impl/dmi/DmiRestClientSpec.groovy b/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/impl/dmi/DmiRestClientSpec.groovy index 3dadf23249..3444d7b86a 100644 --- a/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/impl/dmi/DmiRestClientSpec.groovy +++ b/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/impl/dmi/DmiRestClientSpec.groovy @@ -163,4 +163,16 @@ class DmiRestClientSpec extends Specification { 'DMI basic auth disabled, with NCMP bearer token' | false | BEARER_AUTH_HEADER || BEARER_AUTH_HEADER 'DMI basic auth disabled, with NCMP basic auth' | false | BASIC_AUTH_HEADER || NO_AUTH_HEADER } + + def 'DMI GET Operation for DMI Data Service '() { + given: 'the Data web client returns a valid response entity for the expected parameters' + mockDataServicesWebClient.get() >> mockRequestBody + def jsonNode = jsonObjectMapper.convertJsonString('{"status":"some status"}', JsonNode.class) + ((ObjectNode) jsonNode).put('status', 'some status') + mockResponse.bodyToMono(JsonNode.class) >> Mono.just(jsonNode) + when: 'GET operation is invoked for Data Service' + def response = objectUnderTest.getDataJobStatus(urlTemplateParameters, NO_AUTH_HEADER).block() + then: 'the response equals to the expected value' + assert response == 'some status' + } } diff --git a/integration-test/src/test/groovy/org/onap/cps/integration/base/DmiDispatcher.groovy b/integration-test/src/test/groovy/org/onap/cps/integration/base/DmiDispatcher.groovy index 0b0e33c6f6..5ce2475d7d 100644 --- a/integration-test/src/test/groovy/org/onap/cps/integration/base/DmiDispatcher.groovy +++ b/integration-test/src/test/groovy/org/onap/cps/integration/base/DmiDispatcher.groovy @@ -95,6 +95,10 @@ class DmiDispatcher extends Dispatcher { case ~'^/dmi/v1/writeJob/(.*)$': return mockWriteJobResponse(request) + // get data job status + case ~'^/dmi/v1/dataJob/(.*)/dataProducerJob/(.*)/status(.*)$': + return mockResponseWithBody(HttpStatus.OK, '{"status":"status details from mock service"}') + default: throw new IllegalArgumentException('Mock DMI does not implement endpoint ' + request.path) } diff --git a/integration-test/src/test/groovy/org/onap/cps/integration/functional/ncmp/CmHandleResourceDataSpec.groovy b/integration-test/src/test/groovy/org/onap/cps/integration/functional/ncmp/CmHandleResourceDataSpec.groovy deleted file mode 100644 index 418b3a4088..0000000000 --- a/integration-test/src/test/groovy/org/onap/cps/integration/functional/ncmp/CmHandleResourceDataSpec.groovy +++ /dev/null @@ -1,53 +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.integration.functional.ncmp - -import org.onap.cps.integration.base.CpsIntegrationSpecBase -import org.onap.cps.ncmp.api.data.models.CmResourceAddress -import org.onap.cps.ncmp.impl.data.NetworkCmProxyFacade -import spock.util.concurrent.PollingConditions - -import static org.onap.cps.ncmp.api.data.models.DatastoreType.PASSTHROUGH_OPERATIONAL - -class CmHandleResourceDataSpec extends CpsIntegrationSpecBase { - - NetworkCmProxyFacade objectUnderTest - - def setup() { - dmiDispatcher1.moduleNamesPerCmHandleId['ch-1'] = ['M1', 'M2'] - registerCmHandle(DMI1_URL, 'ch-1', NO_MODULE_SET_TAG) - objectUnderTest = networkCmProxyFacade - } - - def cleanup() { - deregisterCmHandle(DMI1_URL, 'ch-1') - } - - def 'Get resource data having special chars into path & query param value.'() { - when: 'getting the resource data' - def cmResourceAddress = new CmResourceAddress(PASSTHROUGH_OPERATIONAL.datastoreName, 'ch-1', 'parent/child') - objectUnderTest.getResourceDataForCmHandle(cmResourceAddress, '(a=1,b=2)', 'my-client-topic', false, null) - then: 'dmi resource data url is encoded correctly' - new PollingConditions().within(5, () -> { - assert dmiDispatcher1.dmiResourceDataUrl == '/dmi/v1/ch/ch-1/data/ds/ncmp-datastore%3Apassthrough-operational?resourceIdentifier=parent%2Fchild&options=%28a%3D1%2Cb%3D2%29&topic=my-client-topic' - }) - } -} diff --git a/integration-test/src/test/groovy/org/onap/cps/integration/functional/ncmp/DataJobStatusServiceSpec.groovy b/integration-test/src/test/groovy/org/onap/cps/integration/functional/ncmp/DataJobStatusServiceSpec.groovy new file mode 100644 index 0000000000..fdcad2b47b --- /dev/null +++ b/integration-test/src/test/groovy/org/onap/cps/integration/functional/ncmp/DataJobStatusServiceSpec.groovy @@ -0,0 +1,24 @@ +package org.onap.cps.integration.functional.ncmp + +import org.onap.cps.integration.base.CpsIntegrationSpecBase +import org.onap.cps.ncmp.api.datajobs.DataJobStatusService +import org.springframework.beans.factory.annotation.Autowired + +class DataJobStatusServiceSpec extends CpsIntegrationSpecBase { + + @Autowired + DataJobStatusService dataJobStatusService + + def 'Get the status of a data job from DMI.'() { + given: 'the required data about the data job' + def dmiServiceName = DMI1_URL + def requestId = 'some-request-id' + def dataProducerJobId = 'some-data-producer-job-id' + def dataProducerId = 'some-data-producer-id' + def authorization = 'my authorization header' + when: 'the data job status checked' + def result = dataJobStatusService.getDataJobStatus(authorization, dmiServiceName, requestId, dataProducerJobId, dataProducerId) + then: 'the status is that defined in the mock service.' + assert result == 'status details from mock service' + } +} diff --git a/integration-test/src/test/groovy/org/onap/cps/integration/functional/ncmp/DmiUrlEncodingPassthroughSpec.groovy b/integration-test/src/test/groovy/org/onap/cps/integration/functional/ncmp/DmiUrlEncodingPassthroughSpec.groovy new file mode 100644 index 0000000000..4e9b809eff --- /dev/null +++ b/integration-test/src/test/groovy/org/onap/cps/integration/functional/ncmp/DmiUrlEncodingPassthroughSpec.groovy @@ -0,0 +1,65 @@ +/* + * ============LICENSE_START======================================================= + * Copyright (C) 2024 Nordix Foundation + * ================================================================================ + * Licensed under the Apache License, Version 2.0 (the 'License'); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an 'AS IS' BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * SPDX-License-Identifier: Apache-2.0 + * ============LICENSE_END========================================================= + */ + +package org.onap.cps.integration.functional.ncmp + +import org.onap.cps.integration.base.CpsIntegrationSpecBase +import org.springframework.http.MediaType + +import static org.springframework.http.HttpMethod.DELETE +import static org.springframework.http.HttpMethod.GET +import static org.springframework.http.HttpMethod.PATCH +import static org.springframework.http.HttpMethod.POST +import static org.springframework.http.HttpMethod.PUT +import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.request +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status + +class DmiUrlEncodingPassthroughSpec extends CpsIntegrationSpecBase { + + def setup() { + dmiDispatcher1.moduleNamesPerCmHandleId['ch-1'] = ['M1', 'M2'] + registerCmHandle(DMI1_URL, 'ch-1', NO_MODULE_SET_TAG) + } + + def cleanup() { + deregisterCmHandle(DMI1_URL, 'ch-1') + } + + def 'DMI URL encoding for pass-through operational data operations with GET request'() { + when: 'sending a GET pass-through data request to NCMP' + mvc.perform(request(GET, '/ncmp/v1/ch/ch-1/data/ds/ncmp-datastore:passthrough-operational') + .queryParam('resourceIdentifier', 'parent/child') + .queryParam('options', '(a=1,b=2)')) + .andExpect(status().is2xxSuccessful()) + then: 'verify that DMI received the request with the correctly encoded URL' + assert dmiDispatcher1.dmiResourceDataUrl == '/dmi/v1/ch/ch-1/data/ds/ncmp-datastore%3Apassthrough-operational?resourceIdentifier=parent%2Fchild&options=%28a%3D1%2Cb%3D2%29' + } + + def 'DMI URL encoding for pass-through running data operations with POST request'() { + when: 'sending a pass-through data request to NCMP with various HTTP methods' + mvc.perform(request(POST, '/ncmp/v1/ch/ch-1/data/ds/ncmp-datastore:passthrough-running') + .queryParam('resourceIdentifier', 'parent/child') + .contentType(MediaType.APPLICATION_JSON) + .content('{ "some-json": "data" }')) + .andExpect(status().is2xxSuccessful()) + then: 'verify that DMI received the request with the correctly encoded URL' + assert dmiDispatcher1.dmiResourceDataUrl == '/dmi/v1/ch/ch-1/data/ds/ncmp-datastore%3Apassthrough-running?resourceIdentifier=parent%2Fchild' + } +} |