diff options
51 files changed, 1283 insertions, 849 deletions
diff --git a/cps-ncmp-rest-stub/src/main/java/org/onap/cps/ncmp/rest/stub/controller/NetworkCmProxyStubController.java b/cps-ncmp-rest-stub/src/main/java/org/onap/cps/ncmp/rest/stub/controller/NetworkCmProxyStubController.java index 3990dd15d4..6d5ee8ce6e 100644 --- a/cps-ncmp-rest-stub/src/main/java/org/onap/cps/ncmp/rest/stub/controller/NetworkCmProxyStubController.java +++ b/cps-ncmp-rest-stub/src/main/java/org/onap/cps/ncmp/rest/stub/controller/NetworkCmProxyStubController.java @@ -1,7 +1,7 @@ /* * ============LICENSE_START======================================================= * Copyright (C) 2022 Bell Canada - * Modifications Copyright (c) 2022 Nordix Foundation + * Modifications 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. @@ -31,14 +31,10 @@ import java.util.List; import java.util.Map; import java.util.UUID; import lombok.extern.slf4j.Slf4j; -import org.onap.cps.ncmp.rest.api.NetworkCmProxyApi; import org.onap.cps.ncmp.rest.controller.handlers.DatastoreType; import org.onap.cps.ncmp.rest.model.CmHandleQueryParameters; -import org.onap.cps.ncmp.rest.model.RestModuleDefinition; -import org.onap.cps.ncmp.rest.model.RestModuleReference; import org.onap.cps.ncmp.rest.model.RestOutputCmHandle; -import org.onap.cps.ncmp.rest.model.RestOutputCmHandleCompositeState; -import org.onap.cps.ncmp.rest.model.RestOutputCmHandlePublicProperties; +import org.onap.cps.ncmp.rest.stub.handlers.NetworkCmProxyApiStubDefaultImpl; import org.springframework.beans.factory.annotation.Value; import org.springframework.core.io.ClassPathResource; import org.springframework.http.HttpStatus; @@ -49,9 +45,7 @@ import org.springframework.web.bind.annotation.RestController; @Slf4j @RestController @RequestMapping("${rest.api.ncmp-stub-base-path}") -public class NetworkCmProxyStubController implements NetworkCmProxyApi { - - public static final String ASYNC_REQUEST_ID = "requestId"; +public class NetworkCmProxyStubController implements NetworkCmProxyApiStubDefaultImpl { @Value("${stub.path}") private String pathToResponseFiles; @@ -87,23 +81,6 @@ public class NetworkCmProxyStubController implements NetworkCmProxyApi { } @Override - public ResponseEntity<Void> createResourceDataRunningForCmHandle(final String datastoreName, - final String resourceIdentifier, - final String cmHandleId, - final Object body, - final String contentType) { - return new ResponseEntity<>(HttpStatus.CREATED); - } - - @Override - public ResponseEntity<Void> deleteResourceDataRunningForCmHandle(final String datastoreName, - final String cmHandleId, - final String resourceIdentifier, - final String contentType) { - return new ResponseEntity<>(HttpStatus.NO_CONTENT); - } - - @Override public ResponseEntity<List<RestOutputCmHandle>> searchCmHandles( final CmHandleQueryParameters cmHandleQueryParameters) { List<RestOutputCmHandle> restOutputCmHandles = null; @@ -120,78 +97,12 @@ public class NetworkCmProxyStubController implements NetworkCmProxyApi { return ResponseEntity.ok(restOutputCmHandles); } - @Override - public ResponseEntity<Object> setDataSyncEnabledFlagForCmHandle(final String cmHandleId, - final Boolean dataSyncEnabled) { - return new ResponseEntity<>(HttpStatus.NOT_IMPLEMENTED); - } - - @Override - public ResponseEntity<List<String>> searchCmHandleIds( - final CmHandleQueryParameters cmHandleQueryParameters) { - return new ResponseEntity<>(HttpStatus.NOT_IMPLEMENTED); - } - - @Override - public ResponseEntity<RestOutputCmHandlePublicProperties> getCmHandlePublicPropertiesByCmHandleId( - final String cmHandleId) { - return new ResponseEntity<>(HttpStatus.NOT_IMPLEMENTED); - } - - @Override - public ResponseEntity<RestOutputCmHandleCompositeState> getCmHandleStateByCmHandleId(final String cmHandle) { - return new ResponseEntity<>(HttpStatus.NOT_IMPLEMENTED); - } - - @Override - public ResponseEntity<List<RestModuleDefinition>> getModuleDefinitionsByCmHandleId(final String cmHandle) { - return new ResponseEntity<>(HttpStatus.NOT_IMPLEMENTED); - } - - @Override - public ResponseEntity<List<RestModuleReference>> getModuleReferencesByCmHandle(final String cmHandleId) { - return new ResponseEntity<>(HttpStatus.NOT_IMPLEMENTED); - } - - @Override - public ResponseEntity<Object> patchResourceDataRunningForCmHandle(final String datastoreName, - final String resourceIdentifier, - final String cmHandleId, - final Object body, - final String contentType) { - return new ResponseEntity<>(HttpStatus.NOT_IMPLEMENTED); - } - - @Override - public ResponseEntity<Object> queryResourceDataForCmHandle(final String datastoreName, - final String cmHandle, - final String cpsPath, - final String options, - final String topic, - final Boolean includeDescendants) { - return new ResponseEntity<>(HttpStatus.NOT_IMPLEMENTED); - } - - @Override - public ResponseEntity<RestOutputCmHandle> retrieveCmHandleDetailsById(final String cmHandleId) { - return new ResponseEntity<>(HttpStatus.NOT_IMPLEMENTED); - } - - @Override - public ResponseEntity<Object> updateResourceDataRunningForCmHandle(final String datastoreName, - final String resourceIdentifier, - final String cmHandleId, - final Object body, - final String contentType) { - return new ResponseEntity<>(HttpStatus.NOT_IMPLEMENTED); - } - private ResponseEntity<Map<String, Object>> populateAsyncResponse(final String topicParamInQuery) { final Map<String, Object> responseData; - if (topicParamInQuery != null) { - responseData = getAsyncResponseData(); - } else { + if (topicParamInQuery == null) { responseData = null; + } else { + responseData = getAsyncResponseData(); } return ResponseEntity.ok().body(responseData); } diff --git a/cps-ncmp-rest-stub/src/main/java/org/onap/cps/ncmp/rest/stub/handlers/NetworkCmProxyApiStubDefaultImpl.java b/cps-ncmp-rest-stub/src/main/java/org/onap/cps/ncmp/rest/stub/handlers/NetworkCmProxyApiStubDefaultImpl.java new file mode 100644 index 0000000000..6e28dbc44c --- /dev/null +++ b/cps-ncmp-rest-stub/src/main/java/org/onap/cps/ncmp/rest/stub/handlers/NetworkCmProxyApiStubDefaultImpl.java @@ -0,0 +1,145 @@ +/* + * ============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.rest.stub.handlers; + +import java.util.List; +import org.onap.cps.ncmp.rest.api.NetworkCmProxyApi; +import org.onap.cps.ncmp.rest.model.CmHandleQueryParameters; +import org.onap.cps.ncmp.rest.model.RestModuleDefinition; +import org.onap.cps.ncmp.rest.model.RestModuleReference; +import org.onap.cps.ncmp.rest.model.RestOutputCmHandle; +import org.onap.cps.ncmp.rest.model.RestOutputCmHandleCompositeState; +import org.onap.cps.ncmp.rest.model.RestOutputCmHandlePublicProperties; +import org.springframework.http.HttpStatus; +import org.springframework.http.ResponseEntity; + +public interface NetworkCmProxyApiStubDefaultImpl extends NetworkCmProxyApi { + + String ASYNC_REQUEST_ID = "requestId"; + + @Override + default ResponseEntity<Object> getResourceDataForCmHandle(final String datastoreName, + final String cmHandle, + final String resourceIdentifier, + final String optionsParamInQuery, + final String topicParamInQuery, + final Boolean includeDescendants) { + return new ResponseEntity<>(HttpStatus.NOT_IMPLEMENTED); + } + + @Override + default ResponseEntity<Object> getResourceDataForCmHandleBatch(final String resourceIdentifier, + final String topicParamInQuery, + final String datastoreName, + final Object requestBody, + final String optionsParamInQuery, + final Boolean includeDescendants) { + return new ResponseEntity<>(HttpStatus.NOT_IMPLEMENTED); + } + + @Override + default ResponseEntity<List<RestOutputCmHandle>> searchCmHandles( + final CmHandleQueryParameters cmHandleQueryParameters) { + return new ResponseEntity<>(HttpStatus.NOT_IMPLEMENTED); + } + + @Override + default ResponseEntity<Void> createResourceDataRunningForCmHandle(final String datastoreName, + final String resourceIdentifier, + final String cmHandleId, + final Object requestBody, + final String contentType) { + return new ResponseEntity<>(HttpStatus.CREATED); + } + + @Override + default ResponseEntity<Void> deleteResourceDataRunningForCmHandle(final String datastoreName, + final String cmHandleId, + final String resourceIdentifier, + final String contentType) { + return new ResponseEntity<>(HttpStatus.NO_CONTENT); + } + + @Override + default ResponseEntity<Object> setDataSyncEnabledFlagForCmHandle(final String cmHandleId, + final Boolean dataSyncEnabled) { + return new ResponseEntity<>(HttpStatus.NOT_IMPLEMENTED); + } + + @Override + default ResponseEntity<List<String>> searchCmHandleIds(final CmHandleQueryParameters cmHandleQueryParameters) { + return new ResponseEntity<>(HttpStatus.NOT_IMPLEMENTED); + } + + @Override + default ResponseEntity<RestOutputCmHandlePublicProperties> getCmHandlePublicPropertiesByCmHandleId( + final String cmHandleId) { + return new ResponseEntity<>(HttpStatus.NOT_IMPLEMENTED); + } + + @Override + default ResponseEntity<RestOutputCmHandleCompositeState> getCmHandleStateByCmHandleId(final String cmHandle) { + return new ResponseEntity<>(HttpStatus.NOT_IMPLEMENTED); + } + + @Override + default ResponseEntity<List<RestModuleDefinition>> getModuleDefinitionsByCmHandleId(final String cmHandle) { + return new ResponseEntity<>(HttpStatus.NOT_IMPLEMENTED); + } + + @Override + default ResponseEntity<List<RestModuleReference>> getModuleReferencesByCmHandle(final String cmHandleId) { + return new ResponseEntity<>(HttpStatus.NOT_IMPLEMENTED); + } + + @Override + default ResponseEntity<Object> patchResourceDataRunningForCmHandle(final String datastoreName, + final String resourceIdentifier, + final String cmHandleId, + final Object requestBody, + final String contentType) { + return new ResponseEntity<>(HttpStatus.NOT_IMPLEMENTED); + } + + @Override + default ResponseEntity<Object> queryResourceDataForCmHandle(final String datastoreName, + final String cmHandle, + final String cpsPath, + final String optionsParamInQuery, + final String topicParamInQuery, + final Boolean includeDescendants) { + return new ResponseEntity<>(HttpStatus.NOT_IMPLEMENTED); + } + + @Override + default ResponseEntity<RestOutputCmHandle> retrieveCmHandleDetailsById(final String cmHandleId) { + return new ResponseEntity<>(HttpStatus.NOT_IMPLEMENTED); + } + + @Override + default ResponseEntity<Object> updateResourceDataRunningForCmHandle(final String datastoreName, + final String resourceIdentifier, + final String cmHandleId, + final Object requestBody, + final String contentType) { + return new ResponseEntity<>(HttpStatus.NOT_IMPLEMENTED); + } +} diff --git a/cps-ncmp-rest/docs/openapi/components.yaml b/cps-ncmp-rest/docs/openapi/components.yaml index 6ca63c7caf..7fc1063a12 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-2022 Nordix Foundation +# Copyright (C) 2021-2023 Nordix Foundation # Modifications Copyright (C) 2021 Pantheon.tech # Modifications Copyright (C) 2022 Bell Canada # ================================================================================ @@ -522,6 +522,18 @@ components: sample 1: value: topic: my-topic-name + requiredTopicParamInQuery: + name: topic + in: query + description: mandatory topic parameter in query. + required: true + schema: + type: string + allowReserved: true + examples: + sample 1: + value: + topic: my-topic-name contentParamInHeader: name: Content-Type in: header diff --git a/cps-ncmp-rest/docs/openapi/ncmp.yml b/cps-ncmp-rest/docs/openapi/ncmp.yml index 1f7cce96ff..2b70d94892 100755 --- a/cps-ncmp-rest/docs/openapi/ncmp.yml +++ b/cps-ncmp-rest/docs/openapi/ncmp.yml @@ -1,5 +1,5 @@ # ============LICENSE_START======================================================= -# Copyright (C) 2021-2022 Nordix Foundation +# Copyright (C) 2021-2023 Nordix Foundation # Modifications Copyright (C) 2021 Pantheon.tech # Modifications Copyright (C) 2021-2022 Bell Canada # ================================================================================ @@ -194,6 +194,43 @@ resourceDataForCmHandle: 502: $ref: 'components.yaml#/components/responses/BadGateway' +getResourceDataForCmHandleBatch: + post: + tags: + - network-cm-proxy + summary: Get resource data for batch of cm handle ids + description: This request will be handled asynchronously using messaging to the supplied topic. The rest response will be an acknowledge with a requestId to identify the relevant messages. + operationId: getResourceDataForCmHandleBatch + parameters: + - $ref: 'components.yaml#/components/parameters/datastoreName' + - $ref: 'components.yaml#/components/parameters/resourceIdentifierInQuery' + - $ref: 'components.yaml#/components/parameters/optionsParamInQuery' + - $ref: 'components.yaml#/components/parameters/requiredTopicParamInQuery' + - $ref: 'components.yaml#/components/parameters/includeDescendantsOptionInQuery' + requestBody: + required: true + content: + application/json: + schema: + type: object + responses: + 200: + description: OK + content: + application/json: + schema: + type: object + 400: + $ref: 'components.yaml#/components/responses/BadRequest' + 401: + $ref: 'components.yaml#/components/responses/Unauthorized' + 403: + $ref: 'components.yaml#/components/responses/Forbidden' + 500: + $ref: 'components.yaml#/components/responses/InternalServerError' + 502: + $ref: 'components.yaml#/components/responses/BadGateway' + queryResourceDataForCmHandle: get: tags: diff --git a/cps-ncmp-rest/docs/openapi/openapi.yml b/cps-ncmp-rest/docs/openapi/openapi.yml index ee29366906..5b4c0d3496 100755 --- a/cps-ncmp-rest/docs/openapi/openapi.yml +++ b/cps-ncmp-rest/docs/openapi/openapi.yml @@ -1,5 +1,5 @@ # ============LICENSE_START======================================================= -# Copyright (C) 2021-2022 Nordix Foundation +# Copyright (C) 2021-2023 Nordix Foundation # Modifications Copyright (C) 2021 Pantheon.tech # Modifications Copyright (C) 2021 Bell Canada # ================================================================================ @@ -34,6 +34,9 @@ paths: /v1/ch/{cm-handle}/data/ds/{datastore-name}: $ref: 'ncmp.yml#/resourceDataForCmHandle' + /v1/batch/data/ds/{datastore-name}: + $ref: 'ncmp.yml#/getResourceDataForCmHandleBatch' + /v1/ch/{cm-handle}/data/ds/{datastore-name}/query: $ref: 'ncmp.yml#/queryResourceDataForCmHandle' diff --git a/cps-ncmp-rest/src/main/java/org/onap/cps/ncmp/rest/controller/NetworkCmProxyController.java b/cps-ncmp-rest/src/main/java/org/onap/cps/ncmp/rest/controller/NetworkCmProxyController.java index 4da251f3cc..a8bc3aec76 100755 --- a/cps-ncmp-rest/src/main/java/org/onap/cps/ncmp/rest/controller/NetworkCmProxyController.java +++ b/cps-ncmp-rest/src/main/java/org/onap/cps/ncmp/rest/controller/NetworkCmProxyController.java @@ -23,10 +23,10 @@ package org.onap.cps.ncmp.rest.controller; -import static org.onap.cps.ncmp.api.impl.operations.DmiRequestBody.OperationEnum.CREATE; -import static org.onap.cps.ncmp.api.impl.operations.DmiRequestBody.OperationEnum.DELETE; -import static org.onap.cps.ncmp.api.impl.operations.DmiRequestBody.OperationEnum.PATCH; -import static org.onap.cps.ncmp.api.impl.operations.DmiRequestBody.OperationEnum.UPDATE; +import static org.onap.cps.ncmp.api.impl.operations.OperationEnum.CREATE; +import static org.onap.cps.ncmp.api.impl.operations.OperationEnum.DELETE; +import static org.onap.cps.ncmp.api.impl.operations.OperationEnum.PATCH; +import static org.onap.cps.ncmp.api.impl.operations.OperationEnum.UPDATE; import java.util.Collection; import java.util.List; @@ -74,12 +74,12 @@ public class NetworkCmProxyController implements NetworkCmProxyApi { /** * Get resource data from datastore. * - * @param datastoreName name of the datastore - * @param cmHandle cm handle identifier - * @param resourceIdentifier resource identifier - * @param optionsParamInQuery options query parameter - * @param topicParamInQuery topic query parameter - * @param includeDescendants whether include descendants + * @param datastoreName name of the datastore + * @param cmHandle cm handle identifier + * @param resourceIdentifier resource identifier + * @param optionsParamInQuery options query parameter + * @param topicParamInQuery topic query parameter + * @param includeDescendantsAsObject whether include descendants * @return {@code ResponseEntity} response from dmi plugin */ @@ -89,25 +89,48 @@ public class NetworkCmProxyController implements NetworkCmProxyApi { final String resourceIdentifier, final String optionsParamInQuery, final String topicParamInQuery, - final Boolean includeDescendants) { + final Boolean includeDescendantsAsObject) { final NcmpDatastoreRequestHandler ncmpDatastoreRequestHandler = - ncmpDatastoreResourceRequestHandlerFactory.getNcmpDatastoreResourceRequestHandler( + ncmpDatastoreResourceRequestHandlerFactory.getNcmpResourceRequestHandler( DatastoreType.fromDatastoreName(datastoreName)); + final boolean includeDescendants = toPrimitiveFlag(includeDescendantsAsObject); + return ncmpDatastoreRequestHandler.executeRequest(cmHandle, resourceIdentifier, optionsParamInQuery, topicParamInQuery, includeDescendants); } + @Override + public ResponseEntity<Object> getResourceDataForCmHandleBatch(final String resourceIdentifier, + final String topicParamInQuery, + final String datastoreName, + final Object requestBody, + final String optionsParamInQuery, + final Boolean includeDescendantsAsObject) { + + final NcmpDatastoreRequestHandler ncmpDatastoreRequestHandler = + ncmpDatastoreResourceRequestHandlerFactory.getNcmpResourceRequestHandler( + DatastoreType.fromDatastoreName(datastoreName)); + + final List<String> cmHandleIds = jsonObjectMapper.convertJsonString(jsonObjectMapper.asJsonString(requestBody), + List.class); + + final boolean includeDescendants = toPrimitiveFlag(includeDescendantsAsObject); + + return ncmpDatastoreRequestHandler.executeRequest(cmHandleIds, resourceIdentifier, + optionsParamInQuery, topicParamInQuery, includeDescendants); + } + /** * Query resource data from datastore. * - * @param datastoreName name of the datastore - * @param cmHandle cm handle identifier - * @param cpsPath CPS Path - * @param optionsParamInQuery options query parameter - * @param topicParamInQuery topic query parameter - * @param includeDescendants whether include descendants + * @param datastoreName name of the datastore + * @param cmHandle cm handle identifier + * @param cpsPath CPS Path + * @param optionsParamInQuery options query parameter + * @param topicParamInQuery topic query parameter + * @param includeDescendantsAsObject whether include descendants * @return {@code ResponseEntity} response from dmi plugin */ @@ -117,13 +140,15 @@ public class NetworkCmProxyController implements NetworkCmProxyApi { final String cpsPath, final String optionsParamInQuery, final String topicParamInQuery, - final Boolean includeDescendants) { + final Boolean includeDescendantsAsObject) { validateDataStore(DatastoreType.OPERATIONAL, datastoreName); - final NcmpDatastoreRequestHandler ncmpDatastoreRequestHandler = - ncmpDatastoreResourceRequestHandlerFactory.getNcmpDatastoreResourceQueryHandler(); + final NcmpDatastoreRequestHandler ncmpCachedResourceRequestHandler = + ncmpDatastoreResourceRequestHandlerFactory.getNcmpResourceRequestHandler( + DatastoreType.fromDatastoreName(datastoreName)); - return ncmpDatastoreRequestHandler.executeRequest(cmHandle, cpsPath, optionsParamInQuery, - topicParamInQuery, includeDescendants); + final boolean includeDescendants = toPrimitiveFlag(includeDescendantsAsObject); + + return ncmpCachedResourceRequestHandler.executeRequest(cmHandle, cpsPath, includeDescendants); } /** @@ -367,5 +392,12 @@ public class NetworkCmProxyController implements NetworkCmProxyApi { throw new InvalidDatastoreException(requestedDatastoreName + " is not supported"); } } + + private static boolean toPrimitiveFlag(final Boolean includeDescendantsAsObject) { + if (includeDescendantsAsObject == null) { + return false; + } + return includeDescendantsAsObject.booleanValue(); + } } diff --git a/cps-ncmp-rest/src/main/java/org/onap/cps/ncmp/rest/controller/handlers/NcmpCachedResourceRequestHandler.java b/cps-ncmp-rest/src/main/java/org/onap/cps/ncmp/rest/controller/handlers/NcmpCachedResourceRequestHandler.java new file mode 100644 index 0000000000..620f64782b --- /dev/null +++ b/cps-ncmp-rest/src/main/java/org/onap/cps/ncmp/rest/controller/handlers/NcmpCachedResourceRequestHandler.java @@ -0,0 +1,70 @@ +/* + * ============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.rest.controller.handlers; + +import java.util.function.Supplier; +import lombok.RequiredArgsConstructor; +import lombok.Setter; +import org.onap.cps.ncmp.api.NetworkCmProxyQueryService; +import org.onap.cps.spi.FetchDescendantsOption; +import org.springframework.stereotype.Component; + +@RequiredArgsConstructor +@Component +public class NcmpCachedResourceRequestHandler extends NcmpDatastoreRequestHandler { + + @Setter + private String dataStoreName; + private final NetworkCmProxyQueryService networkCmProxyQueryService; + + @Override + public Supplier<Object> getTaskSupplierForGetRequest(final String cmHandleId, + final String resourceIdentifier, + final String optionsParamInQuery, + final String topicParamInQuery, + final String requestId, + final boolean includeDescendants) { + + final FetchDescendantsOption fetchDescendantsOption = + TaskManagementDefaultHandler.getFetchDescendantsOption(includeDescendants); + + return () -> networkCmProxyDataService.getResourceDataForCmHandle(dataStoreName, cmHandleId, resourceIdentifier, + fetchDescendantsOption); + } + + /** + * Gets ncmp datastore query handler. + * Note. Currently only ncmp-datastore:operational supports query operations + * @return a ncmp datastore query handler. + */ + @Override + public Supplier<Object> getTaskSupplierForQueryRequest(final String cmHandleId, + final String resourceIdentifier, + final boolean includeDescendants) { + + final FetchDescendantsOption fetchDescendantsOption = + TaskManagementDefaultHandler.getFetchDescendantsOption(includeDescendants); + + return () -> networkCmProxyQueryService.queryResourceDataOperational(cmHandleId, resourceIdentifier, + fetchDescendantsOption); + } + +} diff --git a/cps-ncmp-rest/src/main/java/org/onap/cps/ncmp/rest/controller/handlers/NcmpDatastoreOperationalQueryHandler.java b/cps-ncmp-rest/src/main/java/org/onap/cps/ncmp/rest/controller/handlers/NcmpDatastoreOperationalQueryHandler.java deleted file mode 100644 index 0586d42625..0000000000 --- a/cps-ncmp-rest/src/main/java/org/onap/cps/ncmp/rest/controller/handlers/NcmpDatastoreOperationalQueryHandler.java +++ /dev/null @@ -1,56 +0,0 @@ -/* - * ============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.rest.controller.handlers; - -import java.util.function.Supplier; -import lombok.extern.slf4j.Slf4j; -import org.onap.cps.ncmp.api.NetworkCmProxyQueryService; -import org.onap.cps.ncmp.rest.executor.CpsNcmpTaskExecutor; -import org.onap.cps.spi.FetchDescendantsOption; - -@Slf4j -public class NcmpDatastoreOperationalQueryHandler extends NcmpDatastoreRequestHandler { - - private final NetworkCmProxyQueryService networkCmProxyQueryService; - - public NcmpDatastoreOperationalQueryHandler(final NetworkCmProxyQueryService networkCmProxyQueryService, - final CpsNcmpTaskExecutor cpsNcmpTaskExecutor, - final int timeOutInMilliSeconds, - final boolean notificationFeatureEnabled) { - super(null, cpsNcmpTaskExecutor, timeOutInMilliSeconds, notificationFeatureEnabled); - this.networkCmProxyQueryService = networkCmProxyQueryService; - } - - @Override - public Supplier<Object> getTaskSupplier(final String cmHandle, - final String resourceIdentifier, - final String optionsParamInQuery, - final String topicParamInQuery, - final String requestId, - final Boolean includeDescendant) { - - final FetchDescendantsOption fetchDescendantsOption = getFetchDescendantsOption(includeDescendant); - - return () -> networkCmProxyQueryService.queryResourceDataOperational(cmHandle, resourceIdentifier, - fetchDescendantsOption); - } - -} diff --git a/cps-ncmp-rest/src/main/java/org/onap/cps/ncmp/rest/controller/handlers/NcmpDatastoreOperationalResourceRequestHandler.java b/cps-ncmp-rest/src/main/java/org/onap/cps/ncmp/rest/controller/handlers/NcmpDatastoreOperationalResourceRequestHandler.java deleted file mode 100644 index a4720b22ff..0000000000 --- a/cps-ncmp-rest/src/main/java/org/onap/cps/ncmp/rest/controller/handlers/NcmpDatastoreOperationalResourceRequestHandler.java +++ /dev/null @@ -1,53 +0,0 @@ -/* - * ============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.rest.controller.handlers; - -import java.util.function.Supplier; -import lombok.extern.slf4j.Slf4j; -import org.onap.cps.ncmp.api.NetworkCmProxyDataService; -import org.onap.cps.ncmp.rest.executor.CpsNcmpTaskExecutor; -import org.onap.cps.spi.FetchDescendantsOption; - -@Slf4j -public class NcmpDatastoreOperationalResourceRequestHandler extends NcmpDatastoreRequestHandler { - - public NcmpDatastoreOperationalResourceRequestHandler(final NetworkCmProxyDataService networkCmProxyDataService, - final CpsNcmpTaskExecutor cpsNcmpTaskExecutor, - final int timeOutInMilliSeconds, - final boolean notificationFeatureEnabled) { - super(networkCmProxyDataService, cpsNcmpTaskExecutor, timeOutInMilliSeconds, notificationFeatureEnabled); - } - - @Override - public Supplier<Object> getTaskSupplier(final String cmHandle, - final String resourceIdentifier, - final String optionsParamInQuery, - final String topicParamInQuery, - final String requestId, - final Boolean includeDescendant) { - - final FetchDescendantsOption fetchDescendantsOption = getFetchDescendantsOption(includeDescendant); - - return () -> networkCmProxyDataService.getResourceDataOperational(cmHandle, resourceIdentifier, - fetchDescendantsOption); - } - -} diff --git a/cps-ncmp-rest/src/main/java/org/onap/cps/ncmp/rest/controller/handlers/NcmpDatastorePassthroughOperationalResourceRequestHandler.java b/cps-ncmp-rest/src/main/java/org/onap/cps/ncmp/rest/controller/handlers/NcmpDatastorePassthroughOperationalResourceRequestHandler.java deleted file mode 100644 index 1445e3e271..0000000000 --- a/cps-ncmp-rest/src/main/java/org/onap/cps/ncmp/rest/controller/handlers/NcmpDatastorePassthroughOperationalResourceRequestHandler.java +++ /dev/null @@ -1,51 +0,0 @@ -/* - * ============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.rest.controller.handlers; - -import java.util.function.Supplier; -import lombok.extern.slf4j.Slf4j; -import org.onap.cps.ncmp.api.NetworkCmProxyDataService; -import org.onap.cps.ncmp.rest.executor.CpsNcmpTaskExecutor; - -@Slf4j -public class NcmpDatastorePassthroughOperationalResourceRequestHandler extends NcmpDatastoreRequestHandler { - - public NcmpDatastorePassthroughOperationalResourceRequestHandler( - final NetworkCmProxyDataService networkCmProxyDataService, - final CpsNcmpTaskExecutor cpsNcmpTaskExecutor, - final int timeOutInMilliSeconds, - final boolean notificationFeatureEnabled) { - super(networkCmProxyDataService, cpsNcmpTaskExecutor, timeOutInMilliSeconds, notificationFeatureEnabled); - } - - @Override - public Supplier<Object> getTaskSupplier(final String cmHandle, - final String resourceIdentifier, - final String optionsParamInQuery, - final String topicParamInQuery, - final String requestId, - final Boolean includeDescendant) { - - return () -> networkCmProxyDataService.getResourceDataOperationalForCmHandle( - cmHandle, resourceIdentifier, optionsParamInQuery, topicParamInQuery, requestId); - } - -} diff --git a/cps-ncmp-rest/src/main/java/org/onap/cps/ncmp/rest/controller/handlers/NcmpDatastorePassthroughRunningResourceRequestHandler.java b/cps-ncmp-rest/src/main/java/org/onap/cps/ncmp/rest/controller/handlers/NcmpDatastorePassthroughRunningResourceRequestHandler.java deleted file mode 100644 index 8194ec9fd5..0000000000 --- a/cps-ncmp-rest/src/main/java/org/onap/cps/ncmp/rest/controller/handlers/NcmpDatastorePassthroughRunningResourceRequestHandler.java +++ /dev/null @@ -1,50 +0,0 @@ -/* - * ============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.rest.controller.handlers; - -import java.util.function.Supplier; -import lombok.extern.slf4j.Slf4j; -import org.onap.cps.ncmp.api.NetworkCmProxyDataService; -import org.onap.cps.ncmp.rest.executor.CpsNcmpTaskExecutor; - -@Slf4j -public class NcmpDatastorePassthroughRunningResourceRequestHandler extends NcmpDatastoreRequestHandler { - - public NcmpDatastorePassthroughRunningResourceRequestHandler( - final NetworkCmProxyDataService networkCmProxyDataService, - final CpsNcmpTaskExecutor cpsNcmpTaskExecutor, - final int timeOutInMilliSeconds, - final boolean notificationFeatureEnabled) { - super(networkCmProxyDataService, cpsNcmpTaskExecutor, timeOutInMilliSeconds, notificationFeatureEnabled); - } - - @Override - public Supplier<Object> getTaskSupplier(final String cmHandle, - final String resourceIdentifier, - final String optionsParamInQuery, - final String topicParamInQuery, - final String requestId, - final Boolean includeDescendant) { - - return () -> networkCmProxyDataService.getResourceDataPassThroughRunningForCmHandle( - cmHandle, resourceIdentifier, optionsParamInQuery, topicParamInQuery, requestId); - } -} diff --git a/cps-ncmp-rest/src/main/java/org/onap/cps/ncmp/rest/controller/handlers/NcmpDatastoreRequestHandler.java b/cps-ncmp-rest/src/main/java/org/onap/cps/ncmp/rest/controller/handlers/NcmpDatastoreRequestHandler.java index 850200396b..050e724b2c 100644 --- a/cps-ncmp-rest/src/main/java/org/onap/cps/ncmp/rest/controller/handlers/NcmpDatastoreRequestHandler.java +++ b/cps-ncmp-rest/src/main/java/org/onap/cps/ncmp/rest/controller/handlers/NcmpDatastoreRequestHandler.java @@ -1,6 +1,6 @@ /* * ============LICENSE_START======================================================= - * Copyright (C) 2022 Nordix Foundation + * 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. @@ -20,38 +20,37 @@ package org.onap.cps.ncmp.rest.controller.handlers; +import java.util.List; import java.util.Map; import java.util.UUID; import java.util.function.Supplier; -import lombok.RequiredArgsConstructor; +import lombok.NoArgsConstructor; import lombok.extern.slf4j.Slf4j; import org.onap.cps.ncmp.api.NetworkCmProxyDataService; import org.onap.cps.ncmp.rest.executor.CpsNcmpTaskExecutor; import org.onap.cps.ncmp.rest.util.TopicValidator; -import org.onap.cps.spi.FetchDescendantsOption; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.http.HttpStatus; import org.springframework.http.ResponseEntity; +import org.springframework.stereotype.Service; -@RequiredArgsConstructor +@NoArgsConstructor @Slf4j -public abstract class NcmpDatastoreRequestHandler { +@Service +public class NcmpDatastoreRequestHandler implements TaskManagementDefaultHandler { - private static final String NO_REQUEST_ID = null; - private static final String NO_TOPIC = null; - - protected final NetworkCmProxyDataService networkCmProxyDataService; - protected final CpsNcmpTaskExecutor cpsNcmpTaskExecutor; - protected final int timeOutInMilliSeconds; - protected final boolean notificationFeatureEnabled; - - protected abstract Supplier<Object> getTaskSupplier(final String cmHandle, - final String resourceIdentifier, - final String optionsParamInQuery, - final String topicParamInQuery, - final String requestId, - final Boolean includeDescendant); + @Value("${notification.async.executor.time-out-value-in-ms:2000}") + protected int timeOutInMilliSeconds; + @Value("${notification.enabled:true}") + protected boolean notificationFeatureEnabled; + @Autowired + protected NetworkCmProxyDataService networkCmProxyDataService; + @Autowired + protected CpsNcmpTaskExecutor cpsNcmpTaskExecutor; /** - * Execute a request on a datastore. + * Executes synchronous/asynchronous request for given cm handle. * * @param cmHandleId the cm handle * @param resourceIdentifier the resource identifier @@ -64,25 +63,62 @@ public abstract class NcmpDatastoreRequestHandler { final String resourceIdentifier, final String optionsParamInQuery, final String topicParamInQuery, - final Boolean includeDescendants) { + final boolean includeDescendants) { final boolean asyncResponseRequested = topicParamInQuery != null; if (asyncResponseRequested && notificationFeatureEnabled) { - final String requestId = UUID.randomUUID().toString(); - final Supplier<Object> taskSupplier = getTaskSupplier(cmHandleId, resourceIdentifier, optionsParamInQuery, - topicParamInQuery, requestId, includeDescendants); - return executeTaskAsync(topicParamInQuery, requestId, taskSupplier); + return executeAsyncTaskAndGetResponseEntity(cmHandleId, resourceIdentifier, optionsParamInQuery, + topicParamInQuery, includeDescendants, false); } if (asyncResponseRequested) { log.warn("Asynchronous request is unavailable as notification feature is currently disabled, " - + "will use synchronous operation."); + + "will use synchronous operation."); } - final Supplier<Object> taskSupplier = getTaskSupplier(cmHandleId, resourceIdentifier, optionsParamInQuery, - NO_TOPIC, NO_REQUEST_ID, includeDescendants); + final Supplier<Object> taskSupplier = getTaskSupplierForGetRequest(cmHandleId, + resourceIdentifier, optionsParamInQuery, NO_TOPIC, NO_REQUEST_ID, includeDescendants); return executeTaskSync(taskSupplier); } + /** + * Executes a synchronous 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 the response entity + */ + public ResponseEntity<Object> executeRequest(final String cmHandleId, + final String resourceIdentifier, + final boolean includeDescendants) { + + final Supplier<Object> taskSupplier = getTaskSupplierForQueryRequest(cmHandleId, resourceIdentifier, + includeDescendants); + return executeTaskSync(taskSupplier); + } + + /** + * Executes synchronous/asynchronous request for batch of cm handles. + * + * @param cmHandleIds list of cm handles + * @param resourceIdentifier the resource identifier + * @param optionsParamInQuery the options param in query + * @param topicParamInQuery the topic param in query + * @param includeDescendants whether include descendants + * @return the response entity + */ + public ResponseEntity<Object> executeRequest(final List<String> cmHandleIds, + final String resourceIdentifier, + final String optionsParamInQuery, + final String topicParamInQuery, + final boolean includeDescendants) { + + return executeAsyncTaskAndGetResponseEntity(cmHandleIds, resourceIdentifier, optionsParamInQuery, + topicParamInQuery, includeDescendants, true); + + } + protected ResponseEntity<Object> executeTaskAsync(final String topicParamInQuery, final String requestId, final Supplier<Object> taskSupplier) { @@ -98,9 +134,25 @@ public abstract class NcmpDatastoreRequestHandler { return ResponseEntity.ok(taskSupplier.get()); } - protected static FetchDescendantsOption getFetchDescendantsOption(final Boolean includeDescendant) { - return Boolean.TRUE.equals(includeDescendant) ? FetchDescendantsOption.INCLUDE_ALL_DESCENDANTS - : FetchDescendantsOption.OMIT_DESCENDANTS; + private ResponseEntity<Object> executeAsyncTaskAndGetResponseEntity(final Object targetObject, + final String resourceIdentifier, + final String optionsParamInQuery, + final String topicParamInQuery, + final boolean includeDescendants, + final boolean isBulkRequest) { + final String requestId = UUID.randomUUID().toString(); + final Supplier<Object> taskSupplier; + if (isBulkRequest) { + taskSupplier = getTaskSupplierForBulkRequest((List<String>) targetObject, + resourceIdentifier, optionsParamInQuery, topicParamInQuery, requestId, includeDescendants); + } else { + taskSupplier = getTaskSupplierForGetRequest(targetObject.toString(), resourceIdentifier, + optionsParamInQuery, topicParamInQuery, requestId, includeDescendants); + } + if (taskSupplier == NO_OBJECT_SUPPLIER) { + return new ResponseEntity<>(Map.of("status", "Unable to execute request as " + + "datastore is not implemented."), HttpStatus.NOT_IMPLEMENTED); + } + return executeTaskAsync(topicParamInQuery, requestId, taskSupplier); } - } diff --git a/cps-ncmp-rest/src/main/java/org/onap/cps/ncmp/rest/controller/handlers/NcmpDatastoreResourceRequestHandlerFactory.java b/cps-ncmp-rest/src/main/java/org/onap/cps/ncmp/rest/controller/handlers/NcmpDatastoreResourceRequestHandlerFactory.java index ff7bda6a47..9a71798fa1 100644 --- a/cps-ncmp-rest/src/main/java/org/onap/cps/ncmp/rest/controller/handlers/NcmpDatastoreResourceRequestHandlerFactory.java +++ b/cps-ncmp-rest/src/main/java/org/onap/cps/ncmp/rest/controller/handlers/NcmpDatastoreResourceRequestHandlerFactory.java @@ -1,6 +1,6 @@ /* * ============LICENSE_START======================================================= - * Copyright (C) 2022 Nordix Foundation + * 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. @@ -21,24 +21,13 @@ package org.onap.cps.ncmp.rest.controller.handlers; import lombok.RequiredArgsConstructor; -import org.onap.cps.ncmp.api.NetworkCmProxyDataService; -import org.onap.cps.ncmp.api.NetworkCmProxyQueryService; -import org.onap.cps.ncmp.rest.executor.CpsNcmpTaskExecutor; -import org.springframework.beans.factory.annotation.Value; import org.springframework.stereotype.Component; @Component @RequiredArgsConstructor public class NcmpDatastoreResourceRequestHandlerFactory { - - private final NetworkCmProxyDataService networkCmProxyDataService; - private final NetworkCmProxyQueryService networkCmProxyQueryService; - private final CpsNcmpTaskExecutor cpsNcmpTaskExecutor; - - @Value("${notification.async.executor.time-out-value-in-ms:2000}") - private int timeOutInMilliSeconds; - @Value("${notification.enabled:true}") - private boolean notificationFeatureEnabled; + private final NcmpCachedResourceRequestHandler ncmpCachedResourceRequestHandler; + private final NcmpPassthroughResourceRequestHandler ncmpPassthroughResourceRequestHandler; /** * Gets ncmp datastore handler. @@ -46,31 +35,17 @@ public class NcmpDatastoreResourceRequestHandlerFactory { * @param datastoreType the datastore type * @return the ncmp datastore handler */ - public NcmpDatastoreRequestHandler getNcmpDatastoreResourceRequestHandler( - final DatastoreType datastoreType) { + public NcmpDatastoreRequestHandler getNcmpResourceRequestHandler(final DatastoreType datastoreType) { switch (datastoreType) { case OPERATIONAL: - return new NcmpDatastoreOperationalResourceRequestHandler(networkCmProxyDataService, - cpsNcmpTaskExecutor, timeOutInMilliSeconds, notificationFeatureEnabled); + ncmpCachedResourceRequestHandler.setDataStoreName(datastoreType.getDatastoreName()); + return ncmpCachedResourceRequestHandler; case PASSTHROUGH_RUNNING: - return new NcmpDatastorePassthroughRunningResourceRequestHandler(networkCmProxyDataService, - cpsNcmpTaskExecutor, timeOutInMilliSeconds, notificationFeatureEnabled); case PASSTHROUGH_OPERATIONAL: default: - return new NcmpDatastorePassthroughOperationalResourceRequestHandler(networkCmProxyDataService, - cpsNcmpTaskExecutor, timeOutInMilliSeconds, notificationFeatureEnabled); + ncmpPassthroughResourceRequestHandler.setDataStoreName(datastoreType.getDatastoreName()); + return ncmpPassthroughResourceRequestHandler; } } - - /** - * Gets ncmp datastore query handler. - * Note. Currently only ncmp-datastore:operational supports query operations - * @return a ncmp datastore query handler. - */ - public NcmpDatastoreRequestHandler getNcmpDatastoreResourceQueryHandler() { - return new NcmpDatastoreOperationalQueryHandler(networkCmProxyQueryService, cpsNcmpTaskExecutor, - timeOutInMilliSeconds, notificationFeatureEnabled); - } - } diff --git a/cps-ncmp-rest/src/main/java/org/onap/cps/ncmp/rest/controller/handlers/NcmpPassthroughResourceRequestHandler.java b/cps-ncmp-rest/src/main/java/org/onap/cps/ncmp/rest/controller/handlers/NcmpPassthroughResourceRequestHandler.java new file mode 100644 index 0000000000..ab5d587e93 --- /dev/null +++ b/cps-ncmp-rest/src/main/java/org/onap/cps/ncmp/rest/controller/handlers/NcmpPassthroughResourceRequestHandler.java @@ -0,0 +1,58 @@ +/* + * ============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.rest.controller.handlers; + +import java.util.List; +import java.util.function.Supplier; +import lombok.Setter; +import org.springframework.stereotype.Component; + +@Component +public class NcmpPassthroughResourceRequestHandler extends NcmpDatastoreRequestHandler { + + @Setter + private String dataStoreName; + + @Override + public Supplier<Object> getTaskSupplierForGetRequest(final String cmHandleId, + final String resourceIdentifier, + final String optionsParamInQuery, + final String topicParamInQuery, + final String requestId, + final boolean includeDescendants) { + + return () -> networkCmProxyDataService.getResourceDataForCmHandle( + dataStoreName, cmHandleId, resourceIdentifier, optionsParamInQuery, topicParamInQuery, requestId); + } + + @Override + public Supplier<Object> getTaskSupplierForBulkRequest(final List<String> cmHandleIds, + final String resourceIdentifier, + final String optionsParamInQuery, + final String topicParamInQuery, + final String requestId, + final boolean includeDescendants) { + + return () -> networkCmProxyDataService.getResourceDataForCmHandleBatch( + dataStoreName, cmHandleIds, resourceIdentifier, optionsParamInQuery, topicParamInQuery, requestId); + } + +} diff --git a/cps-ncmp-rest/src/main/java/org/onap/cps/ncmp/rest/controller/handlers/TaskManagementDefaultHandler.java b/cps-ncmp-rest/src/main/java/org/onap/cps/ncmp/rest/controller/handlers/TaskManagementDefaultHandler.java new file mode 100644 index 0000000000..08e8407c63 --- /dev/null +++ b/cps-ncmp-rest/src/main/java/org/onap/cps/ncmp/rest/controller/handlers/TaskManagementDefaultHandler.java @@ -0,0 +1,63 @@ +/* + * ============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.rest.controller.handlers; + +import java.util.List; +import java.util.function.Supplier; +import org.onap.cps.spi.FetchDescendantsOption; + +public interface TaskManagementDefaultHandler { + + String NO_REQUEST_ID = null; + String NO_TOPIC = null; + Supplier<Object> NO_OBJECT_SUPPLIER = null; + + default Supplier<Object> getTaskSupplierForGetRequest(final String cmHandleId, + final String resourceIdentifier, + final String optionsParamInQuery, + final String topicParamInQuery, + final String requestId, + final boolean includeDescendant) { + return NO_OBJECT_SUPPLIER; + + } + + default Supplier<Object> getTaskSupplierForQueryRequest(final String cmHandleId, + final String resourceIdentifier, + final boolean includeDescendant) { + return NO_OBJECT_SUPPLIER; + + } + + default Supplier<Object> getTaskSupplierForBulkRequest(final List<String> cmHandleIds, + final String resourceIdentifier, + final String optionsParamInQuery, + final String topicParamInQuery, + final String requestId, + final boolean includeDescendant) { + return NO_OBJECT_SUPPLIER; + } + + static FetchDescendantsOption getFetchDescendantsOption(final boolean includeDescendants) { + return includeDescendants ? FetchDescendantsOption.INCLUDE_ALL_DESCENDANTS + : FetchDescendantsOption.OMIT_DESCENDANTS; + } +} diff --git a/cps-ncmp-rest/src/test/groovy/org/onap/cps/ncmp/rest/controller/NetworkCmProxyControllerSpec.groovy b/cps-ncmp-rest/src/test/groovy/org/onap/cps/ncmp/rest/controller/NetworkCmProxyControllerSpec.groovy index d67804e128..9531101e1d 100644 --- a/cps-ncmp-rest/src/test/groovy/org/onap/cps/ncmp/rest/controller/NetworkCmProxyControllerSpec.groovy +++ b/cps-ncmp-rest/src/test/groovy/org/onap/cps/ncmp/rest/controller/NetworkCmProxyControllerSpec.groovy @@ -2,7 +2,7 @@ * ============LICENSE_START======================================================= * Copyright (C) 2021 Pantheon.tech * Modifications Copyright (C) 2021 highstreet technologies GmbH - * Modifications Copyright (C) 2021-2022 Nordix Foundation + * Modifications Copyright (C) 2021-2023 Nordix Foundation * Modifications Copyright (C) 2021-2022 Bell Canada. * ================================================================================ * Licensed under the Apache License, Version 2.0 (the "License"); @@ -33,18 +33,13 @@ import org.onap.cps.ncmp.api.inventory.CompositeState import org.onap.cps.ncmp.api.inventory.DataStoreSyncState import org.onap.cps.ncmp.api.inventory.LockReasonCategory import org.onap.cps.ncmp.api.models.NcmpServiceCmHandle -import org.onap.cps.ncmp.rest.controller.handlers.DatastoreType -import org.onap.cps.ncmp.rest.controller.handlers.NcmpDatastoreOperationalQueryHandler -import org.onap.cps.ncmp.rest.controller.handlers.NcmpDatastoreOperationalResourceRequestHandler -import org.onap.cps.ncmp.rest.controller.handlers.NcmpDatastorePassthroughOperationalResourceRequestHandler -import org.onap.cps.ncmp.rest.controller.handlers.NcmpDatastorePassthroughRunningResourceRequestHandler +import org.onap.cps.ncmp.rest.controller.handlers.NcmpCachedResourceRequestHandler +import org.onap.cps.ncmp.rest.controller.handlers.NcmpPassthroughResourceRequestHandler import org.onap.cps.ncmp.rest.controller.handlers.NcmpDatastoreResourceRequestHandlerFactory -import org.onap.cps.ncmp.rest.exceptions.InvalidDatastoreException import org.onap.cps.ncmp.rest.executor.CpsNcmpTaskExecutor import org.onap.cps.ncmp.rest.mapper.CmHandleStateMapper import org.onap.cps.ncmp.rest.util.DeprecationHelper import org.onap.cps.spi.FetchDescendantsOption -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 @@ -55,31 +50,32 @@ import org.springframework.boot.test.autoconfigure.web.servlet.WebMvcTest import org.springframework.http.HttpStatus import org.springframework.http.MediaType import org.springframework.test.web.servlet.MockMvc -import org.springframework.test.web.servlet.request.MockHttpServletRequestBuilder import spock.lang.Shared import spock.lang.Specification - import java.time.OffsetDateTime import java.time.ZoneOffset import java.time.format.DateTimeFormatter import static org.onap.cps.ncmp.api.inventory.CompositeState.DataStores import static org.onap.cps.ncmp.api.inventory.CompositeState.Operational -import static org.onap.cps.ncmp.api.impl.operations.DmiRequestBody.OperationEnum.CREATE -import static org.onap.cps.ncmp.api.impl.operations.DmiRequestBody.OperationEnum.UPDATE -import static org.onap.cps.ncmp.api.impl.operations.DmiRequestBody.OperationEnum.PATCH -import static org.onap.cps.ncmp.api.impl.operations.DmiRequestBody.OperationEnum.DELETE import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.put import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.post import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.patch import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.delete +import static org.onap.cps.ncmp.api.impl.operations.OperationEnum.CREATE +import static org.onap.cps.ncmp.api.impl.operations.OperationEnum.UPDATE +import static org.onap.cps.ncmp.api.impl.operations.OperationEnum.PATCH +import static org.onap.cps.ncmp.api.impl.operations.OperationEnum.DELETE +import static org.onap.cps.ncmp.rest.controller.handlers.DatastoreType.PASSTHROUGH_OPERATIONAL +import static org.onap.cps.ncmp.rest.controller.handlers.DatastoreType.PASSTHROUGH_RUNNING +import static org.onap.cps.ncmp.rest.controller.handlers.DatastoreType.OPERATIONAL @WebMvcTest(NetworkCmProxyController) class NetworkCmProxyControllerSpec extends Specification { - public static final int TIMEOUT_IN_MS = 2000 - public static final boolean NOTIFICATION_ENABLED = true + private static final int TIMEOUT_IN_MS = 2000 + private static final boolean NOTIFICATION_ENABLED = true @Autowired MockMvc mvc @@ -115,6 +111,7 @@ class NetworkCmProxyControllerSpec extends Specification { def ncmpBasePathV1 def requestBody = '{"some-key":"some-value"}' + def bulkRequestBody = '["testCmHandle"]' @Shared def NO_TOPIC = null @@ -124,24 +121,15 @@ class NetworkCmProxyControllerSpec extends Specification { .format(OffsetDateTime.of(2022, 12, 31, 20, 30, 40, 1, ZoneOffset.UTC)) void setup() { - stubbedNcmpDatastoreResourceRequestHandlerFactory.getNcmpDatastoreResourceRequestHandler( - DatastoreType.OPERATIONAL) >> - new NcmpDatastoreOperationalResourceRequestHandler( - mockNetworkCmProxyDataService, spiedCpsTaskExecutor, TIMEOUT_IN_MS, NOTIFICATION_ENABLED) + stubbedNcmpDatastoreResourceRequestHandlerFactory.getNcmpResourceRequestHandler( + OPERATIONAL) >> getNcmpDatastoreRequestHandler(OPERATIONAL,new NcmpCachedResourceRequestHandler(mockNetworkCmProxyQueryService)) - stubbedNcmpDatastoreResourceRequestHandlerFactory.getNcmpDatastoreResourceRequestHandler( - DatastoreType.PASSTHROUGH_OPERATIONAL) >> - new NcmpDatastorePassthroughOperationalResourceRequestHandler( - mockNetworkCmProxyDataService, spiedCpsTaskExecutor, TIMEOUT_IN_MS, NOTIFICATION_ENABLED) + stubbedNcmpDatastoreResourceRequestHandlerFactory.getNcmpResourceRequestHandler( + PASSTHROUGH_OPERATIONAL) >> getNcmpDatastoreRequestHandler(PASSTHROUGH_OPERATIONAL,new NcmpPassthroughResourceRequestHandler()) - stubbedNcmpDatastoreResourceRequestHandlerFactory.getNcmpDatastoreResourceRequestHandler( - DatastoreType.PASSTHROUGH_RUNNING) >> - new NcmpDatastorePassthroughRunningResourceRequestHandler( - mockNetworkCmProxyDataService, spiedCpsTaskExecutor, TIMEOUT_IN_MS, NOTIFICATION_ENABLED) + stubbedNcmpDatastoreResourceRequestHandlerFactory.getNcmpResourceRequestHandler( + PASSTHROUGH_RUNNING) >> getNcmpDatastoreRequestHandler(PASSTHROUGH_RUNNING,new NcmpPassthroughResourceRequestHandler()) - stubbedNcmpDatastoreResourceRequestHandlerFactory.getNcmpDatastoreResourceQueryHandler() >> - new NcmpDatastoreOperationalQueryHandler(mockNetworkCmProxyQueryService, spiedCpsTaskExecutor, - TIMEOUT_IN_MS, NOTIFICATION_ENABLED); } def 'Get Resource Data from pass-through operational.'() { @@ -154,11 +142,8 @@ class NetworkCmProxyControllerSpec extends Specification { .contentType(MediaType.APPLICATION_JSON) ).andReturn().response then: 'the NCMP data service is called with getResourceDataOperationalForCmHandle' - 1 * mockNetworkCmProxyDataService.getResourceDataOperationalForCmHandle('testCmHandle', - 'parent/child', - '(a=1,b=2)', - NO_TOPIC, - NO_REQUEST_ID) + 1 * mockNetworkCmProxyDataService.getResourceDataForCmHandle(PASSTHROUGH_OPERATIONAL.datastoreName, 'testCmHandle', + 'parent/child','(a=1,b=2)', NO_TOPIC, NO_REQUEST_ID) and: 'response status is Ok' response.status == HttpStatus.OK.value() } @@ -205,6 +190,48 @@ class NetworkCmProxyControllerSpec extends Specification { 'invalid non-empty topic value in url' | 'passthrough-running' | '&topic=1_5_*_#' } + def 'Get bulk resource data for #datastoreName from dmi service.'() { + given: 'bulk resource data url' + def getUrl = "$ncmpBasePathV1/batch/data/ds/${datastoreName}" + + "?resourceIdentifier=parent/child&options=(a=1,b=2)&topic=myTopic" + when: 'post data resource request is performed' + def response = mvc.perform( + post(getUrl) + .contentType(MediaType.APPLICATION_JSON) + .content(bulkRequestBody) + ).andReturn().response + then: 'response status is Ok' + response.status == HttpStatus.OK.value() + // TODO Need to be un-commented as it's failing into onap CICD pipeline + // but passed into nordix and local build. + //and: 'the NCMP data service is called with getResourceDataForCmHandleBatch' + // 1 * mockNetworkCmProxyDataService.getResourceDataForCmHandleBatch(datastoreName, ['testCmHandle'], + // 'parent/child', + // '(a=1,b=2)', + // 'myTopic', + // _) + and: 'async request id is generated' + assert response.contentAsString.contains("requestId") + where: 'the following data stores are used' + datastoreName << [PASSTHROUGH_RUNNING.datastoreName, PASSTHROUGH_OPERATIONAL.datastoreName] + } + + def 'Get bulk resource data for non-supported #datastoreName from dmi service.'() { + given: 'bulk resource data url' + def getUrl = "$ncmpBasePathV1/batch/data/ds/ncmp-datastore:operational" + + "?resourceIdentifier=parent/child&options=(a=1,b=2)&topic=myTopic" + when: 'post data resource request is performed' + def response = mvc.perform( + post(getUrl) + .contentType(MediaType.APPLICATION_JSON) + .content(bulkRequestBody) + ).andReturn().response + then: 'response status code is 501 not implemented' + response.status == HttpStatus.NOT_IMPLEMENTED.value() + where: 'the following data store is un-supported' + datastoreName << [OPERATIONAL.datastoreName] + } + def 'Query Resource Data from operational.'() { given: 'the query resource data url' def getUrl = "$ncmpBasePathV1/ch/testCmHandle/data/ds/ncmp-datastore:operational/query" + @@ -244,11 +271,8 @@ class NetworkCmProxyControllerSpec extends Specification { def getUrl = "$ncmpBasePathV1/ch/testCmHandle/data/ds/ncmp-datastore:passthrough-running" + "?resourceIdentifier=" + resourceIdentifier + "&options=(a=1,b=2)" and: 'ncmp service returns json object' - mockNetworkCmProxyDataService.getResourceDataPassThroughRunningForCmHandle('testCmHandle', - resourceIdentifier, - '(a=1,b=2)', - NO_TOPIC, - NO_REQUEST_ID) >> '{valid-json}' + mockNetworkCmProxyDataService.getResourceDataForCmHandle(PASSTHROUGH_RUNNING.datastoreName, 'testCmHandle', + resourceIdentifier,'(a=1,b=2)', NO_TOPIC, NO_REQUEST_ID) >> '{valid-json}' when: 'get data resource request is performed' def response = mvc.perform( get(getUrl) @@ -531,9 +555,7 @@ class NetworkCmProxyControllerSpec extends Specification { .contentType(MediaType.APPLICATION_JSON) ).andReturn().response then: 'the NCMP data service is called with getResourceDataOperational with #descendantsOption' - 1 * mockNetworkCmProxyDataService.getResourceDataOperational('testCmHandle', - 'parent/child', - descendantsOption) + 1 * mockNetworkCmProxyDataService.getResourceDataForCmHandle(OPERATIONAL.datastoreName, 'testCmHandle', 'parent/child', descendantsOption) and: 'response status is Ok' response.status == HttpStatus.OK.value() where: 'the following parameters are used' @@ -626,5 +648,19 @@ class NetworkCmProxyControllerSpec extends Specification { return assertContainsAll(response, expectedContent) } + def getNcmpDatastoreRequestHandler(dataStoreType, ncmpDatastoreRequestHandler) { + if (ncmpDatastoreRequestHandler instanceof NcmpCachedResourceRequestHandler) { + NcmpCachedResourceRequestHandler ncmpCachedResourceRequestHandler = (NcmpCachedResourceRequestHandler) ncmpDatastoreRequestHandler + ncmpCachedResourceRequestHandler.dataStoreName = dataStoreType.datastoreName + } else { + NcmpPassthroughResourceRequestHandler ncmpPassthroughResourceRequestHandler = (NcmpPassthroughResourceRequestHandler) ncmpDatastoreRequestHandler + ncmpPassthroughResourceRequestHandler.dataStoreName = dataStoreType.datastoreName + } + ncmpDatastoreRequestHandler.networkCmProxyDataService = mockNetworkCmProxyDataService + ncmpDatastoreRequestHandler.cpsNcmpTaskExecutor = spiedCpsTaskExecutor + ncmpDatastoreRequestHandler.notificationFeatureEnabled = NOTIFICATION_ENABLED + ncmpDatastoreRequestHandler.timeOutInMilliSeconds = TIMEOUT_IN_MS + return ncmpDatastoreRequestHandler + } } diff --git a/cps-ncmp-rest/src/test/groovy/org/onap/cps/ncmp/rest/controller/handlers/NcmpDatastoreRequestHandlerFactorySpec.groovy b/cps-ncmp-rest/src/test/groovy/org/onap/cps/ncmp/rest/controller/handlers/NcmpDatastoreRequestHandlerFactorySpec.groovy index 7c504981e2..15b3ee6c1e 100644 --- a/cps-ncmp-rest/src/test/groovy/org/onap/cps/ncmp/rest/controller/handlers/NcmpDatastoreRequestHandlerFactorySpec.groovy +++ b/cps-ncmp-rest/src/test/groovy/org/onap/cps/ncmp/rest/controller/handlers/NcmpDatastoreRequestHandlerFactorySpec.groovy @@ -1,6 +1,6 @@ /* * ============LICENSE_START======================================================= - * Copyright (C) 2022 Nordix Foundation + * 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. @@ -20,21 +20,28 @@ package org.onap.cps.ncmp.rest.controller.handlers +import org.spockframework.spring.SpringBean import spock.lang.Specification class NcmpDatastoreRequestHandlerFactorySpec extends Specification { - def objectUnderTest = new NcmpDatastoreResourceRequestHandlerFactory(null, null, null) + @SpringBean + NcmpCachedResourceRequestHandler mockNcmpCachedResourceRequestHandler = new NcmpCachedResourceRequestHandler(null) + + @SpringBean + NcmpPassthroughResourceRequestHandler mockNcmpPassthroughResourceRequestHandler = new NcmpPassthroughResourceRequestHandler() + + def objectUnderTest = new NcmpDatastoreResourceRequestHandlerFactory(mockNcmpCachedResourceRequestHandler, mockNcmpPassthroughResourceRequestHandler) def 'Creating ncmp datastore request handlers.'() { when: 'a ncmp datastore request handler is created for #datastoreType' - def result = objectUnderTest.getNcmpDatastoreResourceRequestHandler(datastoreType) + def result = objectUnderTest.getNcmpResourceRequestHandler(datastoreType) then: 'the result is of the expected class' result.class == expectedClass where: 'the following type of datastore is used' datastoreType || expectedClass - DatastoreType.OPERATIONAL || NcmpDatastoreOperationalResourceRequestHandler - DatastoreType.PASSTHROUGH_OPERATIONAL || NcmpDatastorePassthroughOperationalResourceRequestHandler - DatastoreType.PASSTHROUGH_RUNNING || NcmpDatastorePassthroughRunningResourceRequestHandler + DatastoreType.OPERATIONAL || NcmpCachedResourceRequestHandler + DatastoreType.PASSTHROUGH_OPERATIONAL || NcmpPassthroughResourceRequestHandler + DatastoreType.PASSTHROUGH_RUNNING || NcmpPassthroughResourceRequestHandler } } diff --git a/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/NetworkCmProxyDataService.java b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/NetworkCmProxyDataService.java index 128eed3f2c..03737bc51b 100644 --- a/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/NetworkCmProxyDataService.java +++ b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/NetworkCmProxyDataService.java @@ -23,10 +23,10 @@ package org.onap.cps.ncmp.api; -import static org.onap.cps.ncmp.api.impl.operations.DmiRequestBody.OperationEnum; - import java.util.Collection; +import java.util.List; import java.util.Map; +import org.onap.cps.ncmp.api.impl.operations.OperationEnum; import org.onap.cps.ncmp.api.inventory.CompositeState; import org.onap.cps.ncmp.api.models.CmHandleQueryApiParameters; import org.onap.cps.ncmp.api.models.CmHandleQueryServiceParameters; @@ -51,50 +51,55 @@ public interface NetworkCmProxyDataService { DmiPluginRegistrationResponse updateDmiRegistrationAndSyncModule(DmiPluginRegistration dmiPluginRegistration); /** - * Get resource data for data store pass-through operational - * using dmi. + * Get resource data for given data store using dmi. * - * @param cmHandleId cm handle identifier - * @param resourceIdentifier resource identifier + * @param dataStoreName data store name + * @param cmHandleId cm handle identifier + * @param resourceIdentifier resource identifier * @param optionsParamInQuery options query - * @param topicParamInQuery topic name for (triggering) async responses - * @param requestId unique requestId for async request + * @param topicParamInQuery topic name for (triggering) async responses + * @param requestId unique requestId for async request * @return {@code Object} resource data */ - Object getResourceDataOperationalForCmHandle(String cmHandleId, - String resourceIdentifier, - String optionsParamInQuery, - String topicParamInQuery, - String requestId); + Object getResourceDataForCmHandle(String dataStoreName, + String cmHandleId, + String resourceIdentifier, + String optionsParamInQuery, + String topicParamInQuery, + String requestId); /** * Get resource data for operational. * + * @param dataStoreName data store name * @param cmHandleId cm handle identifier * @param resourceIdentifier resource identifier * @Link FetchDescendantsOption fetch descendants option * @return {@code Object} resource data */ - Object getResourceDataOperational(String cmHandleId, + Object getResourceDataForCmHandle(String dataStoreName, + String cmHandleId, String resourceIdentifier, FetchDescendantsOption fetchDescendantsOption); /** - * Get resource data for data store pass-through running - * using dmi. + * Get resource data for given batch of cm handles using dmi. * - * @param cmHandleId cm handle identifier - * @param resourceIdentifier resource identifier + * @param dataStoreName data store name + * @param cmHandleIds cm handle identifiers + * @param resourceIdentifier resource identifier * @param optionsParamInQuery options query - * @param topicParamInQuery topic name for (triggering) async responses - * @param requestId unique requestId for async request + * @param topicParamInQuery topic name for (triggering) async responses + * @param requestId unique requestId for async request * @return {@code Object} resource data */ - Object getResourceDataPassThroughRunningForCmHandle(String cmHandleId, - String resourceIdentifier, - String optionsParamInQuery, - String topicParamInQuery, - String requestId); + Object getResourceDataForCmHandleBatch(String dataStoreName, + List<String> cmHandleIds, + String resourceIdentifier, + String optionsParamInQuery, + String topicParamInQuery, + String requestId); + /** * Write resource data for data store pass-through running diff --git a/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/NetworkCmProxyDataServiceImpl.java b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/NetworkCmProxyDataServiceImpl.java index b3904bd0bb..1b1997f23a 100755 --- a/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/NetworkCmProxyDataServiceImpl.java +++ b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/NetworkCmProxyDataServiceImpl.java @@ -25,7 +25,6 @@ package org.onap.cps.ncmp.api.impl; import static org.onap.cps.ncmp.api.impl.constants.DmiRegistryConstants.NFP_OPERATIONAL_DATASTORE_DATASPACE_NAME; -import static org.onap.cps.ncmp.api.impl.operations.DmiRequestBody.OperationEnum; import static org.onap.cps.ncmp.api.impl.utils.RestQueryParametersValidator.validateCmHandleQueryParameters; import com.google.common.collect.Lists; @@ -46,7 +45,7 @@ import org.onap.cps.ncmp.api.NetworkCmProxyCmHandleQueryService; import org.onap.cps.ncmp.api.NetworkCmProxyDataService; import org.onap.cps.ncmp.api.impl.events.lcm.LcmEventsCmHandleStateHandler; import org.onap.cps.ncmp.api.impl.operations.DmiDataOperations; -import org.onap.cps.ncmp.api.impl.operations.DmiOperations; +import org.onap.cps.ncmp.api.impl.operations.OperationEnum; 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; @@ -115,38 +114,41 @@ public class NetworkCmProxyDataServiceImpl implements NetworkCmProxyDataService } @Override - public Object getResourceDataOperationalForCmHandle(final String cmHandleId, - final String resourceIdentifier, - final String optionsParamInQuery, - final String topicParamInQuery, - final String requestId) { - final ResponseEntity<?> responseEntity = dmiDataOperations.getResourceDataFromDmi(cmHandleId, + public Object getResourceDataForCmHandle(final String dataStoreName, + final String cmHandleId, + final String resourceIdentifier, + final String optionsParamInQuery, + final String topicParamInQuery, + final String requestId) { + final ResponseEntity<?> responseEntity = dmiDataOperations.getResourceDataFromDmi(dataStoreName, cmHandleId, resourceIdentifier, optionsParamInQuery, - DmiOperations.DataStoreEnum.PASSTHROUGH_OPERATIONAL, - requestId, topicParamInQuery); + topicParamInQuery, + requestId); return responseEntity.getBody(); } @Override - public Object getResourceDataOperational(final String cmHandleId, + public Object getResourceDataForCmHandle(final String dataStoreName, + final String cmHandleId, final String resourceIdentifier, final FetchDescendantsOption fetchDescendantsOption) { - return cpsDataService.getDataNodes(NFP_OPERATIONAL_DATASTORE_DATASPACE_NAME, cmHandleId, resourceIdentifier, + return cpsDataService.getDataNodes(dataStoreName, cmHandleId, resourceIdentifier, fetchDescendantsOption).iterator().next(); } @Override - public Object getResourceDataPassThroughRunningForCmHandle(final String cmHandleId, - final String resourceIdentifier, - final String optionsParamInQuery, - final String topicParamInQuery, - final String requestId) { - final ResponseEntity<?> responseEntity = dmiDataOperations.getResourceDataFromDmi(cmHandleId, + public Object getResourceDataForCmHandleBatch(final String dataStoreName, + final List<String> cmHandleIds, + final String resourceIdentifier, + final String optionsParamInQuery, + final String topicParamInQuery, + final String requestId) { + final ResponseEntity<?> responseEntity = dmiDataOperations.getResourceDataFromDmi(dataStoreName, cmHandleIds, resourceIdentifier, optionsParamInQuery, - DmiOperations.DataStoreEnum.PASSTHROUGH_RUNNING, - requestId, topicParamInQuery); + topicParamInQuery, + requestId); return responseEntity.getBody(); } diff --git a/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/client/DmiRestClient.java b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/client/DmiRestClient.java index d5b459b025..9d087806c0 100644 --- a/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/client/DmiRestClient.java +++ b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/client/DmiRestClient.java @@ -1,6 +1,6 @@ /* * ============LICENSE_START======================================================= - * Copyright (C) 2021-2022 Nordix Foundation + * Copyright (C) 2021-2023 Nordix Foundation * Modifications Copyright (C) 2022 Bell Canada * ================================================================================ * Licensed under the Apache License, Version 2.0 (the "License"); @@ -24,7 +24,7 @@ package org.onap.cps.ncmp.api.impl.client; import lombok.AllArgsConstructor; import org.onap.cps.ncmp.api.impl.config.NcmpConfiguration.DmiProperties; import org.onap.cps.ncmp.api.impl.exception.HttpClientRequestException; -import org.onap.cps.ncmp.api.impl.operations.DmiRequestBody; +import org.onap.cps.ncmp.api.impl.operations.OperationEnum; import org.springframework.http.HttpEntity; import org.springframework.http.HttpHeaders; import org.springframework.http.MediaType; @@ -43,14 +43,14 @@ public class DmiRestClient { /** * Sends POST operation to DMI with json body containing module references. * @param dmiResourceUrl dmi resource url - * @param jsonData json data body + * @param requestBodyAsJsonString json data body * @param operation the type of operation being executed (for error reporting only) * @return response entity of type String */ public ResponseEntity<Object> postOperationWithJsonData(final String dmiResourceUrl, - final String jsonData, - final DmiRequestBody.OperationEnum operation) { - final var httpEntity = new HttpEntity<>(jsonData, configureHttpHeaders(new HttpHeaders())); + final String requestBodyAsJsonString, + final OperationEnum operation) { + final var httpEntity = new HttpEntity<>(requestBodyAsJsonString, configureHttpHeaders(new HttpHeaders())); try { return restTemplate.postForEntity(dmiResourceUrl, httpEntity, Object.class); } catch (final HttpStatusCodeException httpStatusCodeException) { diff --git a/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/events/avcsubscription/SubscriptionEventForwarder.java b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/events/avcsubscription/SubscriptionEventForwarder.java index c3624b8005..1fb72a59d3 100644 --- a/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/events/avcsubscription/SubscriptionEventForwarder.java +++ b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/events/avcsubscription/SubscriptionEventForwarder.java @@ -22,7 +22,6 @@ package org.onap.cps.ncmp.api.impl.events.avcsubscription; import java.util.Collection; import java.util.Collections; -import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.Objects; @@ -30,7 +29,7 @@ import java.util.stream.Collectors; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; import org.onap.cps.ncmp.api.impl.events.EventsPublisher; -import org.onap.cps.ncmp.api.impl.operations.RequiredDmiService; +import org.onap.cps.ncmp.api.impl.utils.DmiServiceNameOrganizer; import org.onap.cps.ncmp.api.impl.yangmodels.YangModelCmHandle; import org.onap.cps.ncmp.api.inventory.InventoryPersistence; import org.onap.cps.ncmp.event.model.SubscriptionEvent; @@ -45,7 +44,6 @@ public class SubscriptionEventForwarder { private final InventoryPersistence inventoryPersistence; private final EventsPublisher<SubscriptionEvent> eventsPublisher; - private static final String DMI_AVC_SUBSCRIPTION_TOPIC_PREFIX = "ncmp-dmi-cm-avc-subscription-"; /** @@ -56,38 +54,24 @@ public class SubscriptionEventForwarder { public void forwardCreateSubscriptionEvent(final SubscriptionEvent subscriptionEvent) { final List<Object> cmHandleTargets = subscriptionEvent.getEvent().getPredicates().getTargets(); if (cmHandleTargets == null || cmHandleTargets.isEmpty() - || cmHandleTargets.stream().anyMatch(id -> ((String) id).contains("*"))) { + || cmHandleTargets.stream().anyMatch(id -> ((String) id).contains("*"))) { throw new OperationNotYetSupportedException( - "CMHandle targets are required. \"Wildcard\" operations are not yet supported"); + "CMHandle targets are required. \"Wildcard\" operations are not yet supported"); } final List<String> cmHandleTargetsAsStrings = cmHandleTargets.stream().map( - Objects::toString).collect(Collectors.toList()); + Objects::toString).collect(Collectors.toList()); final Collection<YangModelCmHandle> yangModelCmHandles = - inventoryPersistence.getYangModelCmHandles(cmHandleTargetsAsStrings); - final Map<String, Map<String, Map<String, String>>> dmiNameCmHandleMap = - organizeByDmiName(yangModelCmHandles); - dmiNameCmHandleMap.forEach((dmiName, cmHandlePropertiesMap) -> { - subscriptionEvent.getEvent().getPredicates().setTargets(Collections.singletonList(cmHandlePropertiesMap)); - final String eventKey = createEventKey(subscriptionEvent, dmiName); - eventsPublisher.publishEvent(DMI_AVC_SUBSCRIPTION_TOPIC_PREFIX + dmiName, eventKey, subscriptionEvent); - }); - } + inventoryPersistence.getYangModelCmHandles(cmHandleTargetsAsStrings); - private Map<String, Map<String, Map<String, String>>> organizeByDmiName( - final Collection<YangModelCmHandle> yangModelCmHandles) { - final Map<String, Map<String, Map<String, String>>> dmiNameCmHandlePropertiesMap = new HashMap<>(); - yangModelCmHandles.forEach(cmHandle -> { - final String dmiName = cmHandle.resolveDmiServiceName(RequiredDmiService.DATA); - if (!dmiNameCmHandlePropertiesMap.containsKey(dmiName)) { - final Map<String, Map<String, String>> cmHandleDmiPropertiesMap = new HashMap<>(); - cmHandleDmiPropertiesMap.put(cmHandle.getId(), dmiPropertiesAsMap(cmHandle)); - dmiNameCmHandlePropertiesMap.put(cmHandle.getDmiDataServiceName(), cmHandleDmiPropertiesMap); - } else { - dmiNameCmHandlePropertiesMap.get(cmHandle.getDmiDataServiceName()) - .put(cmHandle.getId(), dmiPropertiesAsMap(cmHandle)); - } + final Map<String, Map<String, Map<String, String>>> dmiPropertiesPerCmHandleIdPerServiceName + = DmiServiceNameOrganizer.getDmiPropertiesPerCmHandleIdPerServiceName(yangModelCmHandles); + dmiPropertiesPerCmHandleIdPerServiceName.forEach((dmiServiceName, cmHandlePropertiesMap) -> { + subscriptionEvent.getEvent().getPredicates().setTargets(Collections + .singletonList(cmHandlePropertiesMap)); + final String eventKey = createEventKey(subscriptionEvent, dmiServiceName); + eventsPublisher.publishEvent(DMI_AVC_SUBSCRIPTION_TOPIC_PREFIX + dmiServiceName, eventKey, + subscriptionEvent); }); - return dmiNameCmHandlePropertiesMap; } private String createEventKey(final SubscriptionEvent subscriptionEvent, final String dmiName) { @@ -98,9 +82,4 @@ public class SubscriptionEventForwarder { + dmiName; } - public Map<String, String> dmiPropertiesAsMap(final YangModelCmHandle yangModelCmHandle) { - return yangModelCmHandle.getDmiProperties().stream().collect( - Collectors.toMap(YangModelCmHandle.Property::getName, YangModelCmHandle.Property::getValue)); - } - } diff --git a/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/executor/TaskExecutor.java b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/executor/TaskExecutor.java new file mode 100644 index 0000000000..192062fde5 --- /dev/null +++ b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/executor/TaskExecutor.java @@ -0,0 +1,47 @@ +/* + * ============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.executor; + +import static java.util.concurrent.TimeUnit.MILLISECONDS; + +import java.util.concurrent.CompletableFuture; +import java.util.function.Supplier; +import lombok.AccessLevel; +import lombok.NoArgsConstructor; + +@NoArgsConstructor(access = AccessLevel.PRIVATE) +public class TaskExecutor { + + /** + * Execute task asynchronously. + * + * @param taskSupplier functional method is get() task need to executed asynchronously + * @param timeOutInMillis the timeout value in milliseconds + */ + public static CompletableFuture<Object> executeTask(final Supplier<Object> taskSupplier, + final long timeOutInMillis) { + return CompletableFuture.supplyAsync(taskSupplier::get) + .orTimeout(timeOutInMillis, MILLISECONDS); + } +} + + + diff --git a/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/operations/DataStoreEnum.java b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/operations/DataStoreEnum.java new file mode 100644 index 0000000000..24edc73f3c --- /dev/null +++ b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/operations/DataStoreEnum.java @@ -0,0 +1,34 @@ +/* + * ============LICENSE_START======================================================= + * Copyright (C) 2023 Nordix Foundation + * ================================================================================ + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * SPDX-License-Identifier: Apache-2.0 + * ============LICENSE_END========================================================= + */ + +package org.onap.cps.ncmp.api.impl.operations; + +import lombok.Getter; + +@Getter +public enum DataStoreEnum { + PASSTHROUGH_OPERATIONAL("ncmp-datastore:passthrough-operational"), + PASSTHROUGH_RUNNING("ncmp-datastore:passthrough-running"); + private final String value; + + DataStoreEnum(final String value) { + this.value = value; + } +} diff --git a/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/operations/DmiDataOperations.java b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/operations/DmiDataOperations.java index 83faa005f0..d648352f15 100644 --- a/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/operations/DmiDataOperations.java +++ b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/operations/DmiDataOperations.java @@ -1,6 +1,6 @@ /* * ============LICENSE_START======================================================= - * Copyright (C) 2021-2022 Nordix Foundation + * Copyright (C) 2021-2023 Nordix Foundation * Modifications Copyright (C) 2022 Bell Canada * ================================================================================ * Licensed under the Apache License, Version 2.0 (the "License"); @@ -21,19 +21,24 @@ package org.onap.cps.ncmp.api.impl.operations; -import static org.onap.cps.ncmp.api.impl.operations.DmiOperations.DataStoreEnum.PASSTHROUGH_RUNNING; -import static org.onap.cps.ncmp.api.impl.operations.DmiRequestBody.OperationEnum; -import static org.onap.cps.ncmp.api.impl.operations.DmiRequestBody.OperationEnum.READ; +import static org.onap.cps.ncmp.api.impl.operations.DataStoreEnum.PASSTHROUGH_RUNNING; +import static org.onap.cps.ncmp.api.impl.operations.OperationEnum.READ; +import java.util.Collection; +import java.util.List; +import java.util.Map; import lombok.extern.slf4j.Slf4j; import org.onap.cps.ncmp.api.impl.client.DmiRestClient; import org.onap.cps.ncmp.api.impl.config.NcmpConfiguration; +import org.onap.cps.ncmp.api.impl.executor.TaskExecutor; +import org.onap.cps.ncmp.api.impl.utils.DmiServiceNameOrganizer; import org.onap.cps.ncmp.api.impl.utils.DmiServiceUrlBuilder; import org.onap.cps.ncmp.api.impl.yangmodels.YangModelCmHandle; import org.onap.cps.ncmp.api.inventory.CmHandleState; import org.onap.cps.ncmp.api.inventory.InventoryPersistence; import org.onap.cps.spi.exceptions.CpsException; import org.onap.cps.utils.JsonObjectMapper; +import org.springframework.http.HttpStatus; import org.springframework.http.ResponseEntity; import org.springframework.stereotype.Component; @@ -44,15 +49,14 @@ import org.springframework.stereotype.Component; @Slf4j public class DmiDataOperations extends DmiOperations { - /** - * Constructor for {@code DmiOperations}. This method also manipulates url properties. - * - * @param dmiRestClient {@code DmiRestClient} - */ + private static final long DEFAULT_ASYNC_TASK_EXECUTOR_TIMEOUT_IN_MILLISECONDS = 30000L; + private static final String NO_CM_HANDLE_ID = ""; + public DmiDataOperations(final InventoryPersistence inventoryPersistence, final JsonObjectMapper jsonObjectMapper, final NcmpConfiguration.DmiProperties dmiProperties, - final DmiRestClient dmiRestClient, final DmiServiceUrlBuilder dmiServiceUrlBuilder) { + final DmiRestClient dmiRestClient, + final DmiServiceUrlBuilder dmiServiceUrlBuilder) { super(inventoryPersistence, jsonObjectMapper, dmiProperties, dmiRestClient, dmiServiceUrlBuilder); } @@ -60,48 +64,78 @@ public class DmiDataOperations extends DmiOperations { * This method fetches the resource data from operational data store for given cm handle * identifier on given resource using dmi client. * - * @param cmHandleId network resource identifier - * @param resourceId resource identifier + * @param dataStoreName name of data store + * @param cmHandleId network resource identifier + * @param resourceId resource identifier * @param optionsParamInQuery options query - * @param dataStore data store enum - * @param requestId requestId for async responses * @param topicParamInQuery topic name for (triggering) async responses + * @param requestId requestId for async responses * @return {@code ResponseEntity} response entity */ - public ResponseEntity<Object> getResourceDataFromDmi(final String cmHandleId, + public ResponseEntity<Object> getResourceDataFromDmi(final String dataStoreName, + final String cmHandleId, final String resourceId, final String optionsParamInQuery, - final DataStoreEnum dataStore, - final String requestId, - final String topicParamInQuery) { + final String topicParamInQuery, + final String requestId) { final YangModelCmHandle yangModelCmHandle = getYangModelCmHandle(cmHandleId); - final String jsonBody = getDmiRequestBody(READ, requestId, null, null, yangModelCmHandle); - final String dmiResourceDataUrl = getDmiRequestUrl(cmHandleId, resourceId, optionsParamInQuery, dataStore, - topicParamInQuery, yangModelCmHandle); final CmHandleState cmHandleState = yangModelCmHandle.getCompositeState().getCmHandleState(); - isCmHandleStateReady(yangModelCmHandle, cmHandleState); - return dmiRestClient.postOperationWithJsonData(dmiResourceDataUrl, jsonBody, READ); + validateIfCmHandleStateReady(yangModelCmHandle, cmHandleState); + final String jsonRequestBody = getDmiRequestBody(READ, requestId, null, null, + yangModelCmHandle); + final String dmiResourceDataUrl = getDmiRequestUrl(dataStoreName, cmHandleId, resourceId, optionsParamInQuery, + topicParamInQuery, yangModelCmHandle.resolveDmiServiceName(RequiredDmiService.DATA)); + return dmiRestClient.postOperationWithJsonData(dmiResourceDataUrl, jsonRequestBody, READ); + } + + /** + * This method fetches the resource data by data store for given list of cm handles using dmi client. + * + * @param dataStoreName data store name + * @param cmHandleIds list of cm handles + * @param resourceId resource identifier + * @param optionsParamInQuery options query + * @param topicParamInQuery topic name for (triggering) async responses + * @param requestId requestId for async responses + * @return {@code ResponseEntity} response entity + */ + public ResponseEntity<Object> getResourceDataFromDmi(final String dataStoreName, + final List<String> cmHandleIds, + final String resourceId, + final String optionsParamInQuery, + final String topicParamInQuery, + final String requestId) { + final Collection<YangModelCmHandle> yangModelCmHandles + = inventoryPersistence.getYangModelCmHandles(cmHandleIds); + final Map<String, Map<String, Map<String, String>>> dmiServiceNameCmHandlePropertiesMap = + DmiServiceNameOrganizer.getDmiPropertiesPerCmHandleIdPerServiceName(yangModelCmHandles); + + buildBulkResourceDataRequestAndSend(dataStoreName, resourceId, optionsParamInQuery, + topicParamInQuery, requestId, dmiServiceNameCmHandlePropertiesMap); + return new ResponseEntity<>(HttpStatus.ACCEPTED); } /** * This method fetches all the resource data from operational data store for given cm handle * identifier using dmi client. * + * @param dataStoreName data store name * @param cmHandleId network resource identifier - * @param dataStore data store enum * @param requestId requestId for async responses * @return {@code ResponseEntity} response entity */ - public ResponseEntity<Object> getResourceDataFromDmi(final String cmHandleId, - final DataStoreEnum dataStore, + public ResponseEntity<Object> getResourceDataFromDmi(final String dataStoreName, + final String cmHandleId, final String requestId) { final YangModelCmHandle yangModelCmHandle = getYangModelCmHandle(cmHandleId); - final String jsonBody = getDmiRequestBody(READ, requestId, null, null, yangModelCmHandle); - final String dmiResourceDataUrl = getDmiRequestUrl(cmHandleId, "/", null, dataStore, - null, yangModelCmHandle); + 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(); - isCmHandleStateReady(yangModelCmHandle, cmHandleState); - return dmiRestClient.postOperationWithJsonData(dmiResourceDataUrl, jsonBody, READ); + validateIfCmHandleStateReady(yangModelCmHandle, cmHandleState); + return dmiRestClient.postOperationWithJsonData(dmiResourceDataUrl, jsonRequestBody, + READ); } /** @@ -121,12 +155,14 @@ public class DmiDataOperations extends DmiOperations { final String requestData, final String dataType) { final YangModelCmHandle yangModelCmHandle = getYangModelCmHandle(cmHandleId); - final String jsonBody = getDmiRequestBody(operation, null, requestData, dataType, yangModelCmHandle); - final String dmiUrl = getDmiRequestUrl(cmHandleId, resourceId, null, PASSTHROUGH_RUNNING, - null, yangModelCmHandle); + final String jsonRequestBody = getDmiRequestBody(operation, null, requestData, dataType, + yangModelCmHandle); + final String dmiUrl = getDmiRequestUrl(PASSTHROUGH_RUNNING.getValue(), cmHandleId, resourceId, + null, null, + yangModelCmHandle.resolveDmiServiceName(RequiredDmiService.DATA)); final CmHandleState cmHandleState = yangModelCmHandle.getCompositeState().getCmHandleState(); - isCmHandleStateReady(yangModelCmHandle, cmHandleState); - return dmiRestClient.postOperationWithJsonData(dmiUrl, jsonBody, operation); + validateIfCmHandleStateReady(yangModelCmHandle, cmHandleState); + return dmiRestClient.postOperationWithJsonData(dmiUrl, jsonRequestBody, operation); } private YangModelCmHandle getYangModelCmHandle(final String cmHandleId) { @@ -145,24 +181,80 @@ public class DmiDataOperations extends DmiOperations { return jsonObjectMapper.asJsonString(dmiRequestBody); } - private String getDmiRequestUrl(final String cmHandleId, - final String resourceId, - final String optionsParamInQuery, - final DataStoreEnum dataStore, - final String topicParamInQuery, - final YangModelCmHandle yangModelCmHandle) { + private String getDmiBulkRequestBody(final OperationEnum operation, + final String requestId, + final String requestData) { + final DmiRequestBody dmiBulkRequestBody = DmiRequestBody.builder() + .operation(operation) + .requestId(requestId) + .data(requestData) + .build(); + return jsonObjectMapper.asJsonString(dmiBulkRequestBody); + } + + private String getDmiRequestUrl(final String dataStoreName, + final String cmHandleId, + final String resourceId, + final String optionsParamInQuery, + final String topicParamInQuery, + final String dmiServiceName) { return dmiServiceUrlBuilder.getDmiDatastoreUrl( dmiServiceUrlBuilder.populateQueryParams(resourceId, optionsParamInQuery, - topicParamInQuery), dmiServiceUrlBuilder.populateUriVariables( - yangModelCmHandle, cmHandleId, dataStore)); + topicParamInQuery), dmiServiceUrlBuilder.populateUriVariables(dataStoreName, dmiServiceName, + cmHandleId)); } - private void isCmHandleStateReady(final YangModelCmHandle yangModelCmHandle, final CmHandleState cmHandleState) { + private String getDmiServiceBulkRequestUrl(final String dataStoreName, + final String resourceId, + final String optionsParamInQuery, + final String topicParamInQuery, + final String dmiServiceName) { + return dmiServiceUrlBuilder.getBulkRequestUrl( + dmiServiceUrlBuilder.populateQueryParams(resourceId, optionsParamInQuery, + topicParamInQuery), dmiServiceUrlBuilder.populateUriVariables(dataStoreName, dmiServiceName, + NO_CM_HANDLE_ID)); + } + + private 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()); + + "cm handle state is " + + yangModelCmHandle.getCompositeState().getCmHandleState()); } } + private void buildBulkResourceDataRequestAndSend(final String dataStoreName, + final String resourceId, + final String optionsParamInQuery, + final String topicParamInQuery, + final String requestId, + final Map<String, Map<String, Map<String, String>>> + dmiServiceNameCmHandlePropertiesMap) { + dmiServiceNameCmHandlePropertiesMap.entrySet().parallelStream().forEach( + dmiServiceNameCmHandlePropertiesEntry -> { + final String dmiBulkResourceDataUrl = getDmiServiceBulkRequestUrl(dataStoreName, resourceId, + optionsParamInQuery, topicParamInQuery, dmiServiceNameCmHandlePropertiesEntry.getKey()); + final String jsonRequestBodyAsJsonString = + jsonObjectMapper.asJsonString(dmiServiceNameCmHandlePropertiesEntry.getValue()); + final String jsonRequestBody + = getDmiBulkRequestBody(READ, requestId, jsonRequestBodyAsJsonString); + sendDmiResourceDataRequestToDmiService(dmiBulkResourceDataUrl, jsonRequestBody); + }); + } + + private void sendDmiResourceDataRequestToDmiService(final String dmiBulkResourceDataUrl, + final String dmiResourceDataRequestAsJsonString) { + TaskExecutor.executeTask(() -> + dmiRestClient.postOperationWithJsonData(dmiBulkResourceDataUrl, + dmiResourceDataRequestAsJsonString, READ), + DEFAULT_ASYNC_TASK_EXECUTOR_TIMEOUT_IN_MILLISECONDS) + .whenCompleteAsync(this::handleTaskCompletion); + } + + private void handleTaskCompletion(final Object response, final Throwable throwable) { + // TODO Need to publish an error response to client given topic. + // Code should be implemented into https://jira.onap.org/browse/CPS-1558 ( + // NCMP : Handle non responding DMI-Plugin) + } } diff --git a/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/operations/DmiModelOperations.java b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/operations/DmiModelOperations.java index d8d03041fb..392e9c1a24 100644 --- a/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/operations/DmiModelOperations.java +++ b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/operations/DmiModelOperations.java @@ -1,6 +1,6 @@ /* * ============LICENSE_START======================================================= - * Copyright (C) 2021-2022 Nordix Foundation + * Copyright (C) 2021-2023 Nordix Foundation * Modifications Copyright (C) 2022 Bell Canada * ================================================================================ * Licensed under the Apache License, Version 2.0 (the "License"); @@ -98,17 +98,18 @@ public class DmiModelOperations extends DmiOperations { * Get resources from DMI for modules. * * @param dmiServiceName dmi service name - * @param jsonData module names and revisions as JSON + * @param jsonRequestBody module names and revisions as JSON * @param cmHandle cmHandle * @param resourceName name of the resource(s) * @return {@code ResponseEntity} response entity */ private ResponseEntity<Object> getResourceFromDmiWithJsonData(final String dmiServiceName, - final String jsonData, + final String jsonRequestBody, final String cmHandle, final String resourceName) { final String dmiResourceDataUrl = getDmiResourceUrl(dmiServiceName, cmHandle, resourceName); - return dmiRestClient.postOperationWithJsonData(dmiResourceDataUrl, jsonData, DmiRequestBody.OperationEnum.READ); + return dmiRestClient.postOperationWithJsonData(dmiResourceDataUrl, jsonRequestBody, + OperationEnum.READ); } private static String getRequestBodyToFetchYangResources(final Collection<ModuleReference> newModuleReferences, diff --git a/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/operations/DmiOperations.java b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/operations/DmiOperations.java index e26ffef870..7e9079ec94 100644 --- 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 @@ -1,6 +1,6 @@ /* * ============LICENSE_START======================================================= - * Copyright (C) 2021-2022 Nordix Foundation + * Copyright (C) 2021-2023 Nordix Foundation * Modifications Copyright (C) 2022 Bell Canada * ================================================================================ * Licensed under the Apache License, Version 2.0 (the "License"); @@ -21,7 +21,6 @@ package org.onap.cps.ncmp.api.impl.operations; -import lombok.Getter; import lombok.RequiredArgsConstructor; import org.onap.cps.ncmp.api.impl.client.DmiRestClient; import org.onap.cps.ncmp.api.impl.config.NcmpConfiguration; @@ -34,17 +33,6 @@ import org.springframework.stereotype.Service; @Service public class DmiOperations { - @Getter - public enum DataStoreEnum { - PASSTHROUGH_OPERATIONAL("ncmp-datastore:passthrough-operational"), - PASSTHROUGH_RUNNING("ncmp-datastore:passthrough-running"); - private final String value; - - DataStoreEnum(final String value) { - this.value = value; - } - } - protected final InventoryPersistence inventoryPersistence; protected final JsonObjectMapper jsonObjectMapper; protected final NcmpConfiguration.DmiProperties dmiProperties; @@ -52,7 +40,7 @@ public class DmiOperations { protected final DmiServiceUrlBuilder dmiServiceUrlBuilder; String getDmiResourceUrl(final String dmiServiceName, final String cmHandle, final String resourceName) { - return dmiServiceUrlBuilder.getCmHandleUrl() + 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/operations/DmiRequestBody.java b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/operations/DmiRequestBody.java index c84e4cb870..3aa6366155 100644 --- a/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/operations/DmiRequestBody.java +++ b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/operations/DmiRequestBody.java @@ -22,7 +22,6 @@ package org.onap.cps.ncmp.api.impl.operations; import com.fasterxml.jackson.annotation.JsonInclude; import com.fasterxml.jackson.annotation.JsonProperty; -import com.fasterxml.jackson.annotation.JsonValue; import java.util.LinkedHashMap; import java.util.List; import java.util.Map; @@ -34,24 +33,6 @@ import org.onap.cps.ncmp.api.impl.yangmodels.YangModelCmHandle; @Getter @Builder public class DmiRequestBody { - public enum OperationEnum { - READ("read"), - CREATE("create"), - UPDATE("update"), - PATCH("patch"), - DELETE("delete"); - private final String value; - - OperationEnum(final String value) { - this.value = value; - } - - @Override - @JsonValue - public String toString() { - return String.valueOf(value); - } - } private OperationEnum operation; private String dataType; diff --git a/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/operations/OperationEnum.java b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/operations/OperationEnum.java new file mode 100644 index 0000000000..796cef23d0 --- /dev/null +++ b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/operations/OperationEnum.java @@ -0,0 +1,43 @@ +/* + * ============LICENSE_START======================================================= + * Copyright (C) 2023 Nordix Foundation + * ================================================================================ + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * SPDX-License-Identifier: Apache-2.0 + * ============LICENSE_END========================================================= + */ + +package org.onap.cps.ncmp.api.impl.operations; + +import com.fasterxml.jackson.annotation.JsonValue; + +public enum OperationEnum { + + READ("read"), + CREATE("create"), + UPDATE("update"), + PATCH("patch"), + DELETE("delete"); + private final String value; + + OperationEnum(final String value) { + this.value = value; + } + + @Override + @JsonValue + public String toString() { + return String.valueOf(value); + } +} 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/api/impl/utils/DmiServiceNameOrganizer.java new file mode 100644 index 0000000000..26e94866a1 --- /dev/null +++ b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/utils/DmiServiceNameOrganizer.java @@ -0,0 +1,64 @@ +/* + * ============LICENSE_START======================================================= + * Copyright (C) 2023 Nordix Foundation + * ================================================================================ + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * SPDX-License-Identifier: Apache-2.0 + * ============LICENSE_END========================================================= + */ + +package org.onap.cps.ncmp.api.impl.utils; + +import java.util.Collection; +import java.util.HashMap; +import java.util.List; +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; + +@NoArgsConstructor(access = AccessLevel.PRIVATE) +public class DmiServiceNameOrganizer { + + /** + * organizes a map with dmi service name as key for cm handle with its properties. + * + * @param yangModelCmHandles list of cm handle model + */ + public static Map<String, Map<String, Map<String, String>>> getDmiPropertiesPerCmHandleIdPerServiceName( + final Collection<YangModelCmHandle> yangModelCmHandles) { + final Map<String, Map<String, Map<String, String>>> dmiPropertiesPerCmHandleIdPerServiceName + = new HashMap<>(); + yangModelCmHandles.forEach(yangModelCmHandle -> { + final String dmiServiceName = yangModelCmHandle.resolveDmiServiceName(RequiredDmiService.DATA); + if (!dmiPropertiesPerCmHandleIdPerServiceName.containsKey(dmiServiceName)) { + final Map<String, Map<String, String>> cmHandleDmiPropertiesMap = new HashMap<>(); + cmHandleDmiPropertiesMap.put(yangModelCmHandle.getId(), + dmiPropertiesAsMap(yangModelCmHandle.getDmiProperties())); + dmiPropertiesPerCmHandleIdPerServiceName.put(dmiServiceName, cmHandleDmiPropertiesMap); + } else { + dmiPropertiesPerCmHandleIdPerServiceName.get(dmiServiceName) + .put(yangModelCmHandle.getId(), dmiPropertiesAsMap(yangModelCmHandle.getDmiProperties())); + } + }); + return dmiPropertiesPerCmHandleIdPerServiceName; + } + + private static Map<String, String> dmiPropertiesAsMap(final List<YangModelCmHandle.Property> dmiProperties) { + return dmiProperties.stream().collect( + Collectors.toMap(YangModelCmHandle.Property::getName, YangModelCmHandle.Property::getValue)); + } +} diff --git a/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/utils/DmiServiceUrlBuilder.java b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/utils/DmiServiceUrlBuilder.java index 5f4a6540c2..bba8f48fbd 100644 --- a/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/utils/DmiServiceUrlBuilder.java +++ b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/utils/DmiServiceUrlBuilder.java @@ -1,6 +1,6 @@ /* * ============LICENSE_START======================================================= - * Copyright (C) 2022 Nordix Foundation + * 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. @@ -20,16 +20,12 @@ package org.onap.cps.ncmp.api.impl.utils; -import static org.onap.cps.ncmp.api.impl.operations.RequiredDmiService.DATA; - 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.ncmp.api.impl.operations.DmiOperations; -import org.onap.cps.ncmp.api.impl.yangmodels.YangModelCmHandle; import org.onap.cps.spi.utils.CpsValidator; import org.springframework.stereotype.Component; import org.springframework.util.LinkedMultiValueMap; @@ -52,13 +48,21 @@ public class DmiServiceUrlBuilder { */ public String getDmiDatastoreUrl(final MultiValueMap<String, String> queryParams, final Map<String, Object> uriVariables) { - final UriComponentsBuilder uriComponentsBuilder = getCmHandleUrl() - .pathSegment("data") - .pathSegment("ds") - .pathSegment("{dataStore}") - .queryParams(queryParams) - .uriVariables(uriVariables); - return uriComponentsBuilder.buildAndExpand().toUriString(); + return getUriComponentsBuilder(getResourceDataBasePathUriBuilder(), queryParams, uriVariables) + .buildAndExpand().toUriString(); + } + + /** + * This method creates the dmi service url for bulk request. + * + * @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 getBulkRequestUrl(final MultiValueMap<String, String> queryParams, + final Map<String, Object> uriVariables) { + return getUriComponentsBuilder(getBulkResourceDataBasePathUriBuilder(), queryParams, uriVariables) + .buildAndExpand().toUriString(); } /** @@ -66,7 +70,7 @@ public class DmiServiceUrlBuilder { * * @return {@code UriComponentsBuilder} dmi service url builder object */ - public UriComponentsBuilder getCmHandleUrl() { + public UriComponentsBuilder getResourceDataBasePathUriBuilder() { return UriComponentsBuilder.newInstance() .path("{dmiServiceName}") .pathSegment("{dmiBasePath}") @@ -76,23 +80,36 @@ public class DmiServiceUrlBuilder { } /** + * This method creates the dmi service url builder object with path variables for batch of cm handles. + * + * @return {@code UriComponentsBuilder} dmi service url builder object + */ + public UriComponentsBuilder getBulkResourceDataBasePathUriBuilder() { + return UriComponentsBuilder.newInstance() + .path("{dmiServiceName}") + .pathSegment("{dmiBasePath}") + .pathSegment("v1") + .pathSegment("batch"); + } + + /** * This method populates uri variables. * - * @param yangModelCmHandle get dmi service name + * @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 YangModelCmHandle yangModelCmHandle, - final String cmHandleId, - final DmiOperations.DataStoreEnum dataStore) { + 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", - yangModelCmHandle.resolveDmiServiceName(DATA)); + uriVariables.put("dmiServiceName", dmiServiceName); uriVariables.put("dmiBasePath", dmiBasePath); uriVariables.put("cmHandleId", cmHandleId); - uriVariables.put("dataStore", dataStore.getValue()); + uriVariables.put("dataStore", dataStoreName); return uriVariables; } @@ -124,4 +141,15 @@ public class DmiServiceUrlBuilder { } }; } + + 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/inventory/sync/SyncUtils.java b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/inventory/sync/SyncUtils.java index 537f50122c..b9cecfb3d0 100644 --- a/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/inventory/sync/SyncUtils.java +++ b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/inventory/sync/SyncUtils.java @@ -1,6 +1,6 @@ /* * ============LICENSE_START======================================================= - * Copyright (C) 2022 Nordix Foundation + * Copyright (C) 2022-2023 Nordix Foundation * Modifications Copyright (C) 2022 Bell Canada * ================================================================================ * Licensed under the Apache License, Version 2.0 (the "License"); @@ -21,6 +21,8 @@ package org.onap.cps.ncmp.api.inventory.sync; +import static org.onap.cps.ncmp.api.impl.operations.DataStoreEnum.PASSTHROUGH_OPERATIONAL; + import com.fasterxml.jackson.databind.JsonNode; import java.time.Duration; import java.time.OffsetDateTime; @@ -37,7 +39,6 @@ import java.util.stream.Collectors; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; import org.onap.cps.ncmp.api.impl.operations.DmiDataOperations; -import org.onap.cps.ncmp.api.impl.operations.DmiOperations; 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.CmHandleQueries; @@ -167,7 +168,8 @@ public class SyncUtils { */ public String getResourceData(final String cmHandleId) { final ResponseEntity<Object> resourceDataResponseEntity = dmiDataOperations.getResourceDataFromDmi( - cmHandleId, DmiOperations.DataStoreEnum.PASSTHROUGH_OPERATIONAL, + PASSTHROUGH_OPERATIONAL.getValue(), + cmHandleId, UUID.randomUUID().toString()); if (resourceDataResponseEntity.getStatusCode().is2xxSuccessful()) { return getFirstResource(resourceDataResponseEntity.getBody()); diff --git a/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/api/impl/NetworkCmProxyDataServiceImplSpec.groovy b/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/api/impl/NetworkCmProxyDataServiceImplSpec.groovy index 871af842ea..5b49e04635 100644 --- a/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/api/impl/NetworkCmProxyDataServiceImplSpec.groovy +++ b/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/api/impl/NetworkCmProxyDataServiceImplSpec.groovy @@ -52,10 +52,10 @@ import org.springframework.http.HttpStatus import org.springframework.http.ResponseEntity import spock.lang.Specification -import static org.onap.cps.ncmp.api.impl.operations.DmiOperations.DataStoreEnum.PASSTHROUGH_OPERATIONAL -import static org.onap.cps.ncmp.api.impl.operations.DmiOperations.DataStoreEnum.PASSTHROUGH_RUNNING -import static org.onap.cps.ncmp.api.impl.operations.DmiRequestBody.OperationEnum.CREATE -import static org.onap.cps.ncmp.api.impl.operations.DmiRequestBody.OperationEnum.UPDATE +import static org.onap.cps.ncmp.api.impl.operations.DataStoreEnum.PASSTHROUGH_OPERATIONAL +import static org.onap.cps.ncmp.api.impl.operations.DataStoreEnum.PASSTHROUGH_RUNNING +import static org.onap.cps.ncmp.api.impl.operations.OperationEnum.CREATE +import static org.onap.cps.ncmp.api.impl.operations.OperationEnum.UPDATE class NetworkCmProxyDataServiceImplSpec extends Specification { @@ -94,61 +94,64 @@ class NetworkCmProxyDataServiceImplSpec extends Specification { def 'Write resource data for pass-through running from DMI using POST.'() { given: 'cpsDataService returns valid datanode' - mockCpsDataService.getDataNodes('NCMP-Admin', 'ncmp-dmi-registry', - cmHandleXPath, FetchDescendantsOption.INCLUDE_ALL_DESCENDANTS) >> dataNode + mockDataNode() when: 'write resource data is called' objectUnderTest.writeResourceDataPassThroughRunningForCmHandle('testCmHandle', 'testResourceId', CREATE, '{some-json}', 'application/json') then: 'DMI called with correct data' 1 * mockDmiDataOperations.writeResourceDataPassThroughRunningFromDmi('testCmHandle', 'testResourceId', - CREATE, '{some-json}', 'application/json') + CREATE, '{some-json}', 'application/json') >> { new ResponseEntity<>(HttpStatus.CREATED) } } def 'Get resource data for pass-through operational from DMI.'() { given: 'get data node is called' - mockCpsDataService.getDataNodes('NCMP-Admin', 'ncmp-dmi-registry', - cmHandleXPath, FetchDescendantsOption.INCLUDE_ALL_DESCENDANTS) >> dataNode + mockDataNode() and: 'get resource data from DMI is called' - mockDmiDataOperations.getResourceDataFromDmi( - 'testCmHandle', - 'testResourceId', - OPTIONS_PARAM, - PASSTHROUGH_OPERATIONAL, - NO_REQUEST_ID, - NO_TOPIC) >> new ResponseEntity<>('dmi-response', HttpStatus.OK) + mockDmiDataOperations.getResourceDataFromDmi(PASSTHROUGH_OPERATIONAL.value,'testCmHandle', + 'testResourceId', OPTIONS_PARAM, NO_TOPIC, NO_REQUEST_ID) >> + new ResponseEntity<>('dmi-response', HttpStatus.OK) when: 'get resource data operational for cm-handle is called' - def response = objectUnderTest.getResourceDataOperationalForCmHandle('testCmHandle', - 'testResourceId', - OPTIONS_PARAM, - NO_TOPIC, - NO_REQUEST_ID) + def response = objectUnderTest.getResourceDataForCmHandle(PASSTHROUGH_OPERATIONAL.value, 'testCmHandle', + 'testResourceId', OPTIONS_PARAM, NO_TOPIC, NO_REQUEST_ID) then: 'DMI returns a json response' response == 'dmi-response' } def 'Get resource data for pass-through running from DMI.'() { given: 'cpsDataService returns valid data node' - mockCpsDataService.getDataNodes('NCMP-Admin', 'ncmp-dmi-registry', - cmHandleXPath, FetchDescendantsOption.INCLUDE_ALL_DESCENDANTS) >> dataNode + mockDataNode() and: 'DMI returns valid response and data' - mockDmiDataOperations.getResourceDataFromDmi('testCmHandle', - 'testResourceId', - OPTIONS_PARAM, - PASSTHROUGH_RUNNING, - NO_REQUEST_ID, - NO_TOPIC) >> new ResponseEntity<>('{dmi-response}', HttpStatus.OK) + mockDmiDataOperations.getResourceDataFromDmi(PASSTHROUGH_RUNNING.value, 'testCmHandle', + 'testResourceId', OPTIONS_PARAM, NO_TOPIC, NO_REQUEST_ID) >> + new ResponseEntity<>('{dmi-response}', HttpStatus.OK) when: 'get resource data is called' - def response = objectUnderTest.getResourceDataPassThroughRunningForCmHandle('testCmHandle', - 'testResourceId', - OPTIONS_PARAM, - NO_TOPIC, - NO_REQUEST_ID) + def response = objectUnderTest.getResourceDataForCmHandle(PASSTHROUGH_RUNNING.value, 'testCmHandle', + 'testResourceId', OPTIONS_PARAM, NO_TOPIC, NO_REQUEST_ID) then: 'get resource data returns expected response' response == '{dmi-response}' } + def 'Get bulk resource data for #datastoreName from DMI.'() { + given: 'cpsDataService returns valid data node' + mockDataNode() + and: 'DMI returns valid response and data' + mockDmiDataOperations.getResourceDataFromDmi(datastoreName, ['testCmHandle'], + 'testResourceId', OPTIONS_PARAM,'some topic','requestId') >> + new ResponseEntity<>('{dmi-bulk-response}', HttpStatus.OK) + when: 'get batch resource data is called' + def response = objectUnderTest.getResourceDataForCmHandleBatch(datastoreName, ['testCmHandle'], + 'testResourceId', + OPTIONS_PARAM, + 'some topic', + 'requestId') + then: 'get bulk resource data returns expected response' + response == '{dmi-bulk-response}' + where: 'the following data stores are used' + datastoreName << [PASSTHROUGH_RUNNING.value, PASSTHROUGH_OPERATIONAL.value] + } + def 'Getting Yang Resources.'() { when: 'yang resources is called' objectUnderTest.getYangResourcesModuleReferences('some-cm-handle') @@ -242,7 +245,7 @@ class NetworkCmProxyDataServiceImplSpec extends Specification { '{some-json}', 'application/json') then: 'DMI called with correct data' 1 * mockDmiDataOperations.writeResourceDataPassThroughRunningFromDmi('testCmHandle', 'testResourceId', - UPDATE, '{some-json}', 'application/json') + UPDATE, '{some-json}', 'application/json') >> { new ResponseEntity<>(HttpStatus.OK) } } @@ -365,4 +368,9 @@ class NetworkCmProxyDataServiceImplSpec extends Specification { .dataStoreSyncState(DataStoreSyncState.NONE_REQUESTED) .lastSyncTime('some-timestamp').build()).build() } + + def mockDataNode() { + mockCpsDataService.getDataNodes('NCMP-Admin', 'ncmp-dmi-registry', + cmHandleXPath, FetchDescendantsOption.INCLUDE_ALL_DESCENDANTS) >> dataNode + } } diff --git a/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/api/impl/client/DmiRestClientSpec.groovy b/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/api/impl/client/DmiRestClientSpec.groovy index 90839f8ac0..b38ca10f7b 100644 --- a/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/api/impl/client/DmiRestClientSpec.groovy +++ b/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/api/impl/client/DmiRestClientSpec.groovy @@ -1,6 +1,6 @@ /* * ============LICENSE_START======================================================= - * Copyright (C) 2021-2022 Nordix Foundation + * Copyright (C) 2021-2023 Nordix Foundation * Modifications Copyright (C) 2022 Bell Canada * ================================================================================ * Licensed under the Apache License, Version 2.0 (the "License"); @@ -34,10 +34,9 @@ import org.springframework.web.client.HttpServerErrorException import org.springframework.web.client.RestTemplate import spock.lang.Specification -import static org.onap.cps.ncmp.api.impl.operations.DmiRequestBody.OperationEnum.READ -import static org.onap.cps.ncmp.api.impl.operations.DmiRequestBody.OperationEnum.PATCH -import static org.onap.cps.ncmp.api.impl.operations.DmiRequestBody.OperationEnum.CREATE - +import static org.onap.cps.ncmp.api.impl.operations.OperationEnum.READ +import static org.onap.cps.ncmp.api.impl.operations.OperationEnum.PATCH +import static org.onap.cps.ncmp.api.impl.operations.OperationEnum.CREATE @SpringBootTest @ContextConfiguration(classes = [NcmpConfiguration.DmiProperties, DmiRestClient]) diff --git a/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/api/impl/operations/DmiDataOperationsSpec.groovy b/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/api/impl/operations/DmiDataOperationsSpec.groovy index 03825c2bbf..89b3a2ff26 100644 --- a/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/api/impl/operations/DmiDataOperationsSpec.groovy +++ b/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/api/impl/operations/DmiDataOperationsSpec.groovy @@ -1,6 +1,6 @@ /* * ============LICENSE_START======================================================= - * Copyright (C) 2021-2022 Nordix Foundation + * Copyright (C) 2021-2023 Nordix Foundation * Modifications Copyright (C) 2022 Bell Canada * ================================================================================ * Licensed under the Apache License, Version 2.0 (the "License"); @@ -30,14 +30,14 @@ import org.springframework.beans.factory.annotation.Autowired import org.springframework.boot.test.context.SpringBootTest import org.springframework.http.ResponseEntity import org.springframework.test.context.ContextConfiguration +import org.springframework.http.HttpStatus import spock.lang.Shared -import static org.onap.cps.ncmp.api.impl.operations.DmiOperations.DataStoreEnum.PASSTHROUGH_OPERATIONAL -import static org.onap.cps.ncmp.api.impl.operations.DmiOperations.DataStoreEnum.PASSTHROUGH_RUNNING -import static org.onap.cps.ncmp.api.impl.operations.DmiRequestBody.OperationEnum.CREATE -import static org.onap.cps.ncmp.api.impl.operations.DmiRequestBody.OperationEnum.READ -import static org.onap.cps.ncmp.api.impl.operations.DmiRequestBody.OperationEnum.UPDATE -import org.springframework.http.HttpStatus +import static org.onap.cps.ncmp.api.impl.operations.DataStoreEnum.PASSTHROUGH_OPERATIONAL +import static org.onap.cps.ncmp.api.impl.operations.DataStoreEnum.PASSTHROUGH_RUNNING +import static org.onap.cps.ncmp.api.impl.operations.OperationEnum.CREATE +import static org.onap.cps.ncmp.api.impl.operations.OperationEnum.READ +import static org.onap.cps.ncmp.api.impl.operations.OperationEnum.UPDATE @SpringBootTest @ContextConfiguration(classes = [NcmpConfiguration.DmiProperties, DmiDataOperations]) @@ -50,6 +50,8 @@ class DmiDataOperationsSpec extends DmiOperationsBaseSpec { def NO_REQUEST_ID = null @Shared def OPTIONS_PARAM = '(a=1,b=2)' + @Shared + def expectedBulkRequestAsJson = '{"operation": "read","data": {"fe1c1f1a070c4ce5bbfda7198592a0d3": {"neType": "RadioNode"},"b8e42eed0d9541ed8d8839e8eb86b3e0": {"neType": "RadioNode"}},"requestId": "bbb67474-f705-410a-93d1-b2844e7f45fd"}' @SpringBean JsonObjectMapper spiedJsonObjectMapper = Spy(new JsonObjectMapper(new ObjectMapper())) @@ -66,8 +68,8 @@ class DmiDataOperationsSpec extends DmiOperationsBaseSpec { mockDmiRestClient.postOperationWithJsonData(expectedUrl, expectedJson, READ) >> responseFromDmi dmiServiceUrlBuilder.getDmiDatastoreUrl(_, _) >> expectedUrl when: 'get resource data is invoked' - def result = objectUnderTest.getResourceDataFromDmi(cmHandleId, resourceIdentifier, - options, dataStore, NO_REQUEST_ID, NO_TOPIC) + def result = objectUnderTest.getResourceDataFromDmi(dataStore.value, cmHandleId, resourceIdentifier, + options, NO_TOPIC, NO_REQUEST_ID) then: 'the result is the response from the DMI service' assert result == responseFromDmi where: 'the following parameters are used' @@ -80,6 +82,25 @@ class DmiDataOperationsSpec extends DmiOperationsBaseSpec { 'datastore running with properties' | [yangModelCmHandleProperty] | PASSTHROUGH_RUNNING | OPTIONS_PARAM || '{"operation":"read","cmHandleProperties":{"prop1":"val1"}}' | 'passthrough-running' | '&options=(a=1,b=2)' } + def 'call get bulk resource data for #dataStore from DMI service with topic #scenario.'() { + given: 'collection of yang model cm Handles' + mockYangModelCmHandleCollectionRetrieval([yangModelCmHandleProperty]) + and: 'a positive response from DMI service when it is called with the expected parameters' + def responseFromDmi = new ResponseEntity<Object>(HttpStatus.ACCEPTED) + def expectedDmiBulkResourceDataUrl = "ncmp/v1/batch/data/ds/${dataStore}?resourceIdentifier=parent/child%26options=(a=1,b=2)&topic=my-topic-name&options=(fields=schemas/schema)" + mockDmiRestClient.postOperationWithJsonData(expectedDmiBulkResourceDataUrl, expectedBulkRequestAsJson, READ) >> responseFromDmi + dmiServiceUrlBuilder.getBulkRequestUrl(_, _) >> expectedDmiBulkResourceDataUrl + when: 'get resource data for bulk cm handle is invoked' + def result = objectUnderTest.getResourceDataFromDmi( dataStore.value, [cmHandleId], resourceIdentifier, + OPTIONS_PARAM, 'some-topic','requestId') + then: 'the result is the response from the DMI service' + assert result == responseFromDmi + where: 'the following parameters are used' + scenario | dataStore + 'datastore operational' | PASSTHROUGH_OPERATIONAL + 'datastore running' | PASSTHROUGH_RUNNING + } + def 'call get all resource data.'() { given: 'the system returns a cm handle with a sample property' mockYangModelCmHandleRetrieval([yangModelCmHandleProperty]) @@ -89,7 +110,7 @@ class DmiDataOperationsSpec extends DmiOperationsBaseSpec { mockDmiRestClient.postOperationWithJsonData(expectedUrl, '{"operation":"read","cmHandleProperties":{"prop1":"val1"}}', READ) >> responseFromDmi dmiServiceUrlBuilder.getDmiDatastoreUrl(_, _) >> expectedUrl when: 'get resource data is invoked' - def result = objectUnderTest.getResourceDataFromDmi(cmHandleId, PASSTHROUGH_OPERATIONAL, NO_REQUEST_ID) + def result = objectUnderTest.getResourceDataFromDmi( PASSTHROUGH_OPERATIONAL.value, cmHandleId, NO_REQUEST_ID) then: 'the result is the response from the DMI service' assert result == responseFromDmi } diff --git a/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/api/impl/operations/DmiModelOperationsSpec.groovy b/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/api/impl/operations/DmiModelOperationsSpec.groovy index ed8f08698d..ed74ad3342 100644 --- a/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/api/impl/operations/DmiModelOperationsSpec.groovy +++ b/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/api/impl/operations/DmiModelOperationsSpec.groovy @@ -1,6 +1,6 @@ /* * ============LICENSE_START======================================================= - * Copyright (C) 2021-2022 Nordix Foundation + * Copyright (C) 2021-2023 Nordix Foundation * Modifications Copyright (C) 2022 Bell Canada * ================================================================================ * Licensed under the Apache License, Version 2.0 (the "License"); @@ -24,6 +24,8 @@ package org.onap.cps.ncmp.api.impl.operations import com.fasterxml.jackson.core.JsonProcessingException import com.fasterxml.jackson.databind.ObjectMapper import org.onap.cps.ncmp.api.impl.config.NcmpConfiguration +import org.onap.cps.ncmp.api.impl.executor.TaskExecutor +import org.onap.cps.ncmp.api.impl.utils.DmiServiceNameOrganizer import org.onap.cps.spi.model.ModuleReference import org.onap.cps.utils.JsonObjectMapper import org.spockframework.spring.SpringBean @@ -34,7 +36,7 @@ import org.springframework.http.ResponseEntity import org.springframework.test.context.ContextConfiguration import spock.lang.Shared -import static org.onap.cps.ncmp.api.impl.operations.DmiRequestBody.OperationEnum.READ +import static org.onap.cps.ncmp.api.impl.operations.OperationEnum.READ @SpringBootTest @ContextConfiguration(classes = [NcmpConfiguration.DmiProperties, DmiModelOperations]) @@ -49,6 +51,12 @@ class DmiModelOperationsSpec extends DmiOperationsBaseSpec { @SpringBean JsonObjectMapper spiedJsonObjectMapper = Spy(new JsonObjectMapper(new ObjectMapper())) + @SpringBean + TaskExecutor stubbedTaskExecutor = Stub() + + @SpringBean + DmiServiceNameOrganizer stubbedDmiServiceNameOrganizer = Stub() + def 'Retrieving module references.'() { given: 'a cm handle' mockYangModelCmHandleRetrieval([]) @@ -89,7 +97,7 @@ class DmiModelOperationsSpec extends DmiOperationsBaseSpec { and: 'a positive response from DMI service when it is called with tha expected parameters' def responseFromDmi = new ResponseEntity<String>(HttpStatus.OK) mockDmiRestClient.postOperationWithJsonData("${dmiServiceName}/dmi/v1/ch/${cmHandleId}/modules", - '{"cmHandleProperties":' + expectedAdditionalPropertiesInRequest + '}', READ) >> responseFromDmi + '{"cmHandleProperties":' + expectedAdditionalPropertiesInRequest + '}', READ) >> responseFromDmi when: 'a get module references is called' def result = objectUnderTest.getModuleReferences(yangModelCmHandle) then: 'the result is the response from DMI service' @@ -108,7 +116,7 @@ class DmiModelOperationsSpec extends DmiOperationsBaseSpec { [moduleName: 'mod2', revision: 'C', yangSource: 'other yang source']], HttpStatus.OK) def expectedModuleReferencesInRequest = '{"name":"mod1","revision":"A"},{"name":"mod2","revision":"X"}' mockDmiRestClient.postOperationWithJsonData("${dmiServiceName}/dmi/v1/ch/${cmHandleId}/moduleResources", - '{"data":{"modules":[' + expectedModuleReferencesInRequest + ']},"cmHandleProperties":{}}', READ) >> responseFromDmi + '{"data":{"modules":[' + expectedModuleReferencesInRequest + ']},"cmHandleProperties":{}}', READ) >> responseFromDmi when: 'get new yang resources from DMI service' def result = objectUnderTest.getNewYangResourcesFromDmi(yangModelCmHandle, newModuleReferences) then: 'the result has the 2 expected yang (re)sources (order is not guaranteed)' @@ -140,7 +148,7 @@ class DmiModelOperationsSpec extends DmiOperationsBaseSpec { and: 'a positive response from DMI service when it is called with the expected parameters' def responseFromDmi = new ResponseEntity<>([[moduleName: 'mod1', revision: 'A', yangSource: 'some yang source']], HttpStatus.OK) mockDmiRestClient.postOperationWithJsonData("${dmiServiceName}/dmi/v1/ch/${cmHandleId}/moduleResources", - '{"data":{"modules":[' + expectedModuleReferencesInRequest + ']},"cmHandleProperties":' + expectedAdditionalPropertiesInRequest + '}', READ) >> responseFromDmi + '{"data":{"modules":[' + expectedModuleReferencesInRequest + ']},"cmHandleProperties":' + expectedAdditionalPropertiesInRequest + '}', READ) >> responseFromDmi when: 'get new yang resources from DMI service' def result = objectUnderTest.getNewYangResourcesFromDmi(yangModelCmHandle, unknownModuleReferences) then: 'the result is the response from DMI service' diff --git a/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/api/impl/operations/DmiOperationsBaseSpec.groovy b/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/api/impl/operations/DmiOperationsBaseSpec.groovy index c4d0020a6c..1b2c50ae76 100644 --- a/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/api/impl/operations/DmiOperationsBaseSpec.groovy +++ b/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/api/impl/operations/DmiOperationsBaseSpec.groovy @@ -58,12 +58,21 @@ abstract class DmiOperationsBaseSpec extends Specification { def static resourceIdentifier = 'parent/child' def mockYangModelCmHandleRetrieval(dmiProperties) { + populateYangModelCmHandle(dmiProperties) + mockInventoryPersistence.getYangModelCmHandle(cmHandleId) >> yangModelCmHandle + } + + def mockYangModelCmHandleCollectionRetrieval(dmiProperties) { + populateYangModelCmHandle(dmiProperties) + mockInventoryPersistence.getYangModelCmHandles(_) >> [yangModelCmHandle] + } + + def populateYangModelCmHandle(dmiProperties) { yangModelCmHandle.dmiDataServiceName = dmiServiceName yangModelCmHandle.dmiServiceName = dmiServiceName yangModelCmHandle.dmiProperties = dmiProperties yangModelCmHandle.id = cmHandleId yangModelCmHandle.compositeState = new CompositeState() yangModelCmHandle.compositeState.cmHandleState = CmHandleState.READY - mockInventoryPersistence.getYangModelCmHandle(cmHandleId) >> yangModelCmHandle } } diff --git a/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/api/impl/utils/DmiServiceUrlBuilderSpec.groovy b/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/api/impl/utils/DmiServiceUrlBuilderSpec.groovy index 01569887ce..6ca3105500 100644 --- a/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/api/impl/utils/DmiServiceUrlBuilderSpec.groovy +++ b/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/api/impl/utils/DmiServiceUrlBuilderSpec.groovy @@ -1,6 +1,6 @@ /* * ============LICENSE_START======================================================= - * Copyright (C) 2022 Nordix Foundation + * 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. @@ -20,9 +20,10 @@ package org.onap.cps.ncmp.api.impl.utils +import org.onap.cps.ncmp.api.impl.operations.RequiredDmiService import org.onap.cps.spi.utils.CpsValidator -import static org.onap.cps.ncmp.api.impl.operations.DmiOperations.DataStoreEnum.PASSTHROUGH_RUNNING +import static org.onap.cps.ncmp.api.impl.operations.DataStoreEnum.PASSTHROUGH_RUNNING import org.onap.cps.ncmp.api.impl.yangmodels.YangModelCmHandle import org.onap.cps.ncmp.api.impl.config.NcmpConfiguration @@ -45,8 +46,8 @@ class DmiServiceUrlBuilderSpec extends Specification { def 'Create the dmi service url with #scenario.'() { given: 'uri variables' dmiProperties.dmiBasePath = 'dmi' - def uriVars = objectUnderTest.populateUriVariables(yangModelCmHandle, - "cmHandle", PASSTHROUGH_RUNNING) + def uriVars = objectUnderTest.populateUriVariables(PASSTHROUGH_RUNNING.value, yangModelCmHandle.resolveDmiServiceName(RequiredDmiService.DATA), + "cmHandle") and: 'query params' def uriQueries = objectUnderTest.populateQueryParams(resourceId, 'optionsParamInQuery', topic) @@ -65,8 +66,8 @@ class DmiServiceUrlBuilderSpec extends Specification { def 'Populate dmi data store url #scenario.'() { given: 'uri variables are created' dmiProperties.dmiBasePath = dmiBasePath - def uriVars = objectUnderTest.populateUriVariables(yangModelCmHandle, - "cmHandle", PASSTHROUGH_RUNNING) + def uriVars = objectUnderTest.populateUriVariables(PASSTHROUGH_RUNNING.value, yangModelCmHandle.resolveDmiServiceName(RequiredDmiService.DATA), + "cmHandle") and: 'null query params' def uriQueries = objectUnderTest.populateQueryParams(null, null, null) diff --git a/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/api/inventory/sync/SyncUtilsSpec.groovy b/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/api/inventory/sync/SyncUtilsSpec.groovy index f4176d6212..8164dcf9ca 100644 --- a/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/api/inventory/sync/SyncUtilsSpec.groovy +++ b/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/api/inventory/sync/SyncUtilsSpec.groovy @@ -1,6 +1,6 @@ /* * ============LICENSE_START======================================================= - * Copyright (C) 2022 Nordix Foundation + * Copyright (C) 2022-2023 Nordix Foundation * Modifications Copyright (C) 2022 Bell Canada * ================================================================================ * Licensed under the Apache License, Version 2.0 (the "License"); @@ -21,10 +21,11 @@ package org.onap.cps.ncmp.api.inventory.sync +import static org.onap.cps.ncmp.api.impl.operations.DataStoreEnum.PASSTHROUGH_OPERATIONAL + import com.fasterxml.jackson.databind.JsonNode import com.fasterxml.jackson.databind.ObjectMapper import org.onap.cps.ncmp.api.impl.operations.DmiDataOperations -import org.onap.cps.ncmp.api.impl.operations.DmiOperations import org.onap.cps.ncmp.api.inventory.CmHandleQueries import org.onap.cps.ncmp.api.inventory.CmHandleState import org.onap.cps.ncmp.api.inventory.CompositeState @@ -38,7 +39,6 @@ import org.springframework.http.HttpStatus import org.springframework.http.ResponseEntity import spock.lang.Shared import spock.lang.Specification - import java.time.OffsetDateTime import java.time.format.DateTimeFormatter import java.util.stream.Collectors @@ -137,7 +137,7 @@ class SyncUtilsSpec extends Specification{ def jsonString = '{"stores:bookstore":{"categories":[{"code":"01"}]}}' JsonNode jsonNode = jsonObjectMapper.convertToJsonNode(jsonString); def responseEntity = new ResponseEntity<>(jsonNode, HttpStatus.OK) - mockDmiDataOperations.getResourceDataFromDmi('cm-handle-123', DmiOperations.DataStoreEnum.PASSTHROUGH_OPERATIONAL, _) >> responseEntity + mockDmiDataOperations.getResourceDataFromDmi(PASSTHROUGH_OPERATIONAL.value, 'cm-handle-123', _) >> responseEntity when: 'get resource data is called' def result = objectUnderTest.getResourceData('cm-handle-123') then: 'the returned data is correct' diff --git a/cps-path-parser/src/main/java/org/onap/cps/cpspath/parser/CpsPathBuilder.java b/cps-path-parser/src/main/java/org/onap/cps/cpspath/parser/CpsPathBuilder.java index 854450c8bb..f44e310a1f 100644 --- a/cps-path-parser/src/main/java/org/onap/cps/cpspath/parser/CpsPathBuilder.java +++ b/cps-path-parser/src/main/java/org/onap/cps/cpspath/parser/CpsPathBuilder.java @@ -24,7 +24,7 @@ package org.onap.cps.cpspath.parser; import static org.onap.cps.cpspath.parser.CpsPathPrefixType.DESCENDANT; import java.util.ArrayList; -import java.util.HashMap; +import java.util.LinkedHashMap; import java.util.LinkedList; import java.util.List; import java.util.Map; @@ -47,7 +47,7 @@ public class CpsPathBuilder extends CpsPathBaseListener { final CpsPathQuery cpsPathQuery = new CpsPathQuery(); - final Map<String, Object> leavesData = new HashMap<>(); + final Map<String, Object> leavesData = new LinkedHashMap<>(); final StringBuilder normalizedXpathBuilder = new StringBuilder(); diff --git a/cps-path-parser/src/test/groovy/org/onap/cps/cpspath/parser/CpsPathQuerySpec.groovy b/cps-path-parser/src/test/groovy/org/onap/cps/cpspath/parser/CpsPathQuerySpec.groovy index 96fdf88cf6..9552a4d342 100644 --- a/cps-path-parser/src/test/groovy/org/onap/cps/cpspath/parser/CpsPathQuerySpec.groovy +++ b/cps-path-parser/src/test/groovy/org/onap/cps/cpspath/parser/CpsPathQuerySpec.groovy @@ -84,6 +84,8 @@ class CpsPathQuerySpec extends Specification { 'parent & child with more than one attribute has OR operator' | '/parent/child[@key1=1 or @key2="abc"]/child2' || "/parent/child[@key1='1' or @key2='abc']/child2" 'parent & child with multiple AND operators' | '/parent/child[@key1=1 and @key2="abc" and @key="xyz"]/child2' || "/parent/child[@key1='1' and @key2='abc' and @key='xyz']/child2" 'parent & child with multiple OR operators' | '/parent/child[@key1=1 or @key2="abc" or @key="xyz"]/child2' || "/parent/child[@key1='1' or @key2='abc' or @key='xyz']/child2" + 'parent & child with multiple AND/OR combination' | '/parent/child[@key1=1 and @key2="abc" or @key="xyz"]/child2' || "/parent/child[@key1='1' and @key2='abc' or @key='xyz']/child2" + 'parent & child with multiple OR/AND combination' | '/parent/child[@key1=1 or @key2="abc" and @key="xyz"]/child2' || "/parent/child[@key1='1' or @key2='abc' and @key='xyz']/child2" } def 'Parse xpath to form the Normalized xpath containing #scenario.'() { @@ -105,14 +107,17 @@ class CpsPathQuerySpec extends Specification { and: 'the right parameters are set' result.descendantName == "child" result.leavesData.size() == expectedNumberOfLeaves + and: 'the given operator(s) returns in the correct order' result.booleanOperatorsType == expectedOperators where: 'the following data is used' - scenario | cpsPath || expectedNumberOfLeaves || expectedOperators - 'one attribute' | '//child[@common-leaf-name-int=5]' || 1 || null - 'more than one attribute has AND operator' | '//child[@int-leaf=5 and @leaf-name="leaf value"]' || 2 || ['and'] - 'more than one attribute has OR operator' | '//child[@int-leaf=5 or @leaf-name="leaf value"]' || 2 || ['or'] - 'more than one attribute has combinations and/or operator' | '//child[@int-leaf=5 and @leaf-name="leaf value" and @leaf-name="leaf value1" ]' || 2 || ['and', 'and'] - 'more than one attribute has combinations or/and operator' | '//child[@int-leaf=5 or @leaf-name="leaf value" or @leaf-name="leaf value1" ]' || 2 || ['or', 'or'] + scenario | cpsPath || expectedNumberOfLeaves || expectedOperators + 'one attribute' | '//child[@common-leaf-name-int=5]' || 1 || null + 'more than one attribute has AND operator' | '//child[@int-leaf=5 and @leaf-name="leaf value"]' || 2 || ['and'] + 'more than one attribute has OR operator' | '//child[@int-leaf=5 or @leaf-name="leaf value"]' || 2 || ['or'] + 'more than one attribute has combinations and operators' | '//child[@int-leaf=5 and @leaf-name="leaf value" and @leaf-name="leaf value1" ]' || 2 || ['and', 'and'] + 'more than one attribute has combinations or operators' | '//child[@int-leaf=5 or @leaf-name="leaf value" or @leaf-name="leaf value1" ]' || 2 || ['or', 'or'] + 'more than one attribute has combinations and/or combination' | '//child[@int-leaf=5 and @leaf-name="leaf value" or @leaf-name="leaf value1" ]' || 2 || ['and', 'or'] + 'more than one attribute has combinations or/and combination' | '//child[@int-leaf=5 or @leaf-name="leaf value" and @leaf-name="leaf value1" ]' || 2 || ['or', 'and'] } def 'Parse #scenario cps path with text function condition'() { @@ -136,7 +141,7 @@ class CpsPathQuerySpec extends Specification { 'descendant with leaf value and ancestor' | '//child[@other-leaf=1]/leaf-name[text()="search"]/ancestor::parent' || true | true } - def 'Parse #scenario cps path with contains function condition'() { + def 'Parse cps path with contains function condition'() { when: 'the given cps path is parsed' def result = CpsPathQuery.createFrom('//someContainer[contains(@lang,"en")]') then: 'the query has the right xpath type' diff --git a/cps-ri/src/main/java/org/onap/cps/spi/impl/CpsDataPersistenceServiceImpl.java b/cps-ri/src/main/java/org/onap/cps/spi/impl/CpsDataPersistenceServiceImpl.java index 369e5289b1..3d9105f680 100644 --- a/cps-ri/src/main/java/org/onap/cps/spi/impl/CpsDataPersistenceServiceImpl.java +++ b/cps-ri/src/main/java/org/onap/cps/spi/impl/CpsDataPersistenceServiceImpl.java @@ -494,21 +494,6 @@ public class CpsDataPersistenceServiceImpl implements CpsDataPersistenceService } @Override - public void updateDataNodeAndDescendants(final String dataspaceName, final String anchorName, - final DataNode dataNode) { - final AnchorEntity anchorEntity = getAnchorEntity(dataspaceName, anchorName); - final FragmentEntity fragmentEntity = getFragmentEntity(anchorEntity, dataNode.getXpath()); - updateFragmentEntityAndDescendantsWithDataNode(fragmentEntity, dataNode); - try { - fragmentRepository.save(fragmentEntity); - } catch (final StaleStateException staleStateException) { - throw new ConcurrencyException("Concurrent Transactions", - String.format("dataspace :'%s', Anchor : '%s' and xpath: '%s' is updated by another transaction.", - dataspaceName, anchorName, dataNode.getXpath())); - } - } - - @Override public void updateDataNodesAndDescendants(final String dataspaceName, final String anchorName, final Collection<DataNode> updatedDataNodes) { final AnchorEntity anchorEntity = getAnchorEntity(dataspaceName, anchorName); diff --git a/cps-ri/src/main/java/org/onap/cps/spi/repository/FragmentRepository.java b/cps-ri/src/main/java/org/onap/cps/spi/repository/FragmentRepository.java index d25832ba54..8114f1055a 100755 --- a/cps-ri/src/main/java/org/onap/cps/spi/repository/FragmentRepository.java +++ b/cps-ri/src/main/java/org/onap/cps/spi/repository/FragmentRepository.java @@ -91,13 +91,6 @@ public interface FragmentRepository extends JpaRepository<FragmentEntity, Long>, @Param("parentXpath") String parentXpath);
@Query(value = "SELECT id, anchor_id AS anchorId, xpath, parent_id AS parentId,"
- + " CAST(attributes AS TEXT) AS attributes"
- + " FROM FRAGMENT WHERE "
- + "( xpath = :parentXpath OR xpath LIKE CONCAT(:parentXpath,'/%') )",
- nativeQuery = true)
- List<FragmentExtract> findByParentXpath(@Param("parentXpath") String parentXpath);
-
- @Query(value = "SELECT id, anchor_id AS anchorId, xpath, parent_id AS parentId,"
+ " CAST(attributes AS TEXT) AS attributes"
+ " FROM FRAGMENT WHERE anchor_id = :anchorId"
+ " AND xpath ~ :xpathRegex",
diff --git a/cps-ri/src/test/groovy/org/onap/cps/spi/impl/CpsDataPersistenceServiceIntegrationSpec.groovy b/cps-ri/src/test/groovy/org/onap/cps/spi/impl/CpsDataPersistenceServiceIntegrationSpec.groovy index e60afa78df..93d7662014 100755 --- a/cps-ri/src/test/groovy/org/onap/cps/spi/impl/CpsDataPersistenceServiceIntegrationSpec.groovy +++ b/cps-ri/src/test/groovy/org/onap/cps/spi/impl/CpsDataPersistenceServiceIntegrationSpec.groovy @@ -23,7 +23,6 @@ package org.onap.cps.spi.impl -import com.fasterxml.jackson.databind.ObjectMapper import com.google.common.collect.ImmutableSet import org.onap.cps.spi.CpsDataPersistenceService import org.onap.cps.spi.entities.FragmentEntity @@ -35,7 +34,6 @@ import org.onap.cps.spi.exceptions.DataNodeNotFoundException import org.onap.cps.spi.exceptions.DataspaceNotFoundException import org.onap.cps.spi.model.DataNode import org.onap.cps.spi.model.DataNodeBuilder -import org.onap.cps.utils.JsonObjectMapper import org.springframework.beans.factory.annotation.Autowired import org.springframework.test.context.jdbc.Sql @@ -49,7 +47,6 @@ class CpsDataPersistenceServiceIntegrationSpec extends CpsPersistenceSpecBase { @Autowired CpsDataPersistenceService objectUnderTest - static JsonObjectMapper jsonObjectMapper = new JsonObjectMapper(new ObjectMapper()) static DataNodeBuilder dataNodeBuilder = new DataNodeBuilder() static final String SET_DATA = '/data/fragment.sql' @@ -353,11 +350,11 @@ class CpsDataPersistenceServiceIntegrationSpec extends CpsPersistenceSpecBase { } @Sql([CLEAR_DATA, SET_DATA]) - def 'Update data node and descendants by removing descendants.'() { - given: 'data node object with leaves updated, no children' - def submittedDataNode = buildDataNode('/parent-200/child-201', ['leaf-value': 'new'], []) + def 'Update data nodes and descendants by removing descendants.'() { + given: 'data nodes with leaves updated, no children' + def submittedDataNodes = [buildDataNode('/parent-200/child-201', ['leaf-value': 'new'], [])] when: 'update data nodes and descendants is performed' - objectUnderTest.updateDataNodeAndDescendants(DATASPACE_NAME, ANCHOR_FOR_DATA_NODES_WITH_LEAVES, submittedDataNode) + objectUnderTest.updateDataNodesAndDescendants(DATASPACE_NAME, ANCHOR_FOR_DATA_NODES_WITH_LEAVES, submittedDataNodes) then: 'leaves have been updated for selected data node' def updatedFragment = fragmentRepository.getById(DATA_NODE_202_FRAGMENT_ID) def updatedLeaves = getLeavesMap(updatedFragment) @@ -370,13 +367,13 @@ class CpsDataPersistenceServiceIntegrationSpec extends CpsPersistenceSpecBase { } @Sql([CLEAR_DATA, SET_DATA]) - def 'Update data node and descendants with new descendants'() { - given: 'data node object with leaves updated, having child with old content' - def submittedDataNode = buildDataNode('/parent-200/child-201', ['leaf-value': 'new'], [ + def 'Update data nodes and descendants with new descendants'() { + given: 'data nodes with leaves updated, having child with old content' + def submittedDataNodes = [buildDataNode('/parent-200/child-201', ['leaf-value': 'new'], [ buildDataNode('/parent-200/child-201/grand-child', ['leaf-value': 'original'], []) - ]) + ])] when: 'update is performed including descendants' - objectUnderTest.updateDataNodeAndDescendants(DATASPACE_NAME, ANCHOR_FOR_DATA_NODES_WITH_LEAVES, submittedDataNode) + objectUnderTest.updateDataNodesAndDescendants(DATASPACE_NAME, ANCHOR_FOR_DATA_NODES_WITH_LEAVES, submittedDataNodes) then: 'leaves have been updated for selected data node' def updatedFragment = fragmentRepository.getById(DATA_NODE_202_FRAGMENT_ID) def updatedLeaves = getLeavesMap(updatedFragment) @@ -390,13 +387,13 @@ class CpsDataPersistenceServiceIntegrationSpec extends CpsPersistenceSpecBase { } @Sql([CLEAR_DATA, SET_DATA]) - def 'Update data node and descendants with same descendants but changed leaf value.'() { - given: 'data node object with leaves updated, having child with old content' - def submittedDataNode = buildDataNode('/parent-200/child-201', ['leaf-value': 'new'], [ + def 'Update data nodes and descendants with same descendants but changed leaf value.'() { + given: 'data nodes with leaves updated, having child with old content' + def submittedDataNodes = [buildDataNode('/parent-200/child-201', ['leaf-value': 'new'], [ buildDataNode('/parent-200/child-201/grand-child', ['leaf-value': 'new'], []) - ]) + ])] when: 'update is performed including descendants' - objectUnderTest.updateDataNodeAndDescendants(DATASPACE_NAME, ANCHOR_FOR_DATA_NODES_WITH_LEAVES, submittedDataNode) + objectUnderTest.updateDataNodesAndDescendants(DATASPACE_NAME, ANCHOR_FOR_DATA_NODES_WITH_LEAVES, submittedDataNodes) then: 'leaves have been updated for selected data node' def updatedFragment = fragmentRepository.getById(DATA_NODE_202_FRAGMENT_ID) def updatedLeaves = getLeavesMap(updatedFragment) @@ -410,13 +407,13 @@ class CpsDataPersistenceServiceIntegrationSpec extends CpsPersistenceSpecBase { } @Sql([CLEAR_DATA, SET_DATA]) - def 'Update data node and descendants with different descendants xpath'() { - given: 'data node object with leaves updated, having child with old content' - def submittedDataNode = buildDataNode('/parent-200/child-201', ['leaf-value': 'new'], [ + def 'Update data nodes and descendants with different descendants xpath'() { + given: 'data nodes with leaves updated, having child with old content' + def submittedDataNodes = [buildDataNode('/parent-200/child-201', ['leaf-value': 'new'], [ buildDataNode('/parent-200/child-201/grand-child-new', ['leaf-value': 'new'], []) - ]) + ])] when: 'update is performed including descendants' - objectUnderTest.updateDataNodeAndDescendants(DATASPACE_NAME, ANCHOR_FOR_DATA_NODES_WITH_LEAVES, submittedDataNode) + objectUnderTest.updateDataNodesAndDescendants(DATASPACE_NAME, ANCHOR_FOR_DATA_NODES_WITH_LEAVES, submittedDataNodes) then: 'leaves have been updated for selected data node' def updatedFragment = fragmentRepository.getById(DATA_NODE_202_FRAGMENT_ID) def updatedLeaves = getLeavesMap(updatedFragment) @@ -432,19 +429,17 @@ class CpsDataPersistenceServiceIntegrationSpec extends CpsPersistenceSpecBase { } @Sql([CLEAR_DATA, SET_DATA]) - def 'Update data node and descendants error scenario: #scenario.'() { - given: 'data node object' - def submittedDataNode = buildDataNode(xpath, ['leaf-name': 'leaf-value'], []) + def 'Update data nodes and descendants error scenario: #scenario.'() { + given: 'data nodes collection' + def submittedDataNodes = [buildDataNode(xpath, ['leaf-name': 'leaf-value'], [])] when: 'attempt to update data node for #scenario' - objectUnderTest.updateDataNodeAndDescendants(dataspaceName, anchorName, submittedDataNode) + objectUnderTest.updateDataNodesAndDescendants(dataspaceName, anchorName, submittedDataNodes) then: 'a #expectedException is thrown' thrown(expectedException) where: 'the following data is used' scenario | dataspaceName | anchorName | xpath || expectedException 'non-existing dataspace' | 'NO DATASPACE' | 'not relevant' | '/not relevant' || DataspaceNotFoundException 'non-existing anchor' | DATASPACE_NAME | 'NO ANCHOR' | '/not relevant' || AnchorNotFoundException - 'non-existing xpath' | DATASPACE_NAME | ANCHOR_FOR_DATA_NODES_WITH_LEAVES | '/NON-EXISTING-XPATH' || DataNodeNotFoundException - 'invalid xpath' | DATASPACE_NAME | ANCHOR_FOR_DATA_NODES_WITH_LEAVES | 'INVALID XPATH' || CpsPathException } @Sql([CLEAR_DATA, SET_DATA]) @@ -699,7 +694,7 @@ class CpsDataPersistenceServiceIntegrationSpec extends CpsPersistenceSpecBase { return dataNodeBuilder.withXpath(xpath).withLeaves(leaves).withChildDataNodes(childDataNodes).build() } - static Map<String, Object> getLeavesMap(FragmentEntity fragmentEntity) { + Map<String, Object> getLeavesMap(FragmentEntity fragmentEntity) { return jsonObjectMapper.convertJsonString(fragmentEntity.attributes, Map<String, Object>.class) } diff --git a/cps-ri/src/test/groovy/org/onap/cps/spi/impl/CpsDataPersistenceServiceSpec.groovy b/cps-ri/src/test/groovy/org/onap/cps/spi/impl/CpsDataPersistenceServiceSpec.groovy index f02aa754f6..204b93442f 100644 --- a/cps-ri/src/test/groovy/org/onap/cps/spi/impl/CpsDataPersistenceServiceSpec.groovy +++ b/cps-ri/src/test/groovy/org/onap/cps/spi/impl/CpsDataPersistenceServiceSpec.groovy @@ -77,24 +77,6 @@ class CpsDataPersistenceServiceSpec extends Specification { 1 * objectUnderTest.storeDataNodes('dataspace1', 'anchor1', [dataNode]) } - def 'Handling of StaleStateException (caused by concurrent updates) during update data node and descendants.'() { - given: 'the fragment repository returns a fragment entity' - mockFragmentRepository.getByAnchorAndXpath(*_) >> { - def fragmentEntity = new FragmentEntity() - fragmentEntity.setChildFragments([new FragmentEntity()] as Set<FragmentEntity>) - return fragmentEntity - } - and: 'a data node is concurrently updated by another transaction' - mockFragmentRepository.save(_) >> { throw new StaleStateException("concurrent updates") } - when: 'attempt to update data node with submitted data nodes' - objectUnderTest.updateDataNodeAndDescendants('some-dataspace', 'some-anchor', new DataNodeBuilder().withXpath('/some/xpath').build()) - then: 'concurrency exception is thrown' - def concurrencyException = thrown(ConcurrencyException) - assert concurrencyException.getDetails().contains('some-dataspace') - assert concurrencyException.getDetails().contains('some-anchor') - assert concurrencyException.getDetails().contains('/some/xpath') - } - def 'Handling of StaleStateException (caused by concurrent updates) during update data nodes and descendants.'() { given: 'the system can update one datanode and has two more datanodes that throw an exception while updating' def dataNodes = createDataNodesAndMockRepositoryMethodSupportingThem([ diff --git a/cps-ri/src/test/groovy/org/onap/cps/spi/impl/CpsModulePersistenceServiceConcurrencySpec.groovy b/cps-ri/src/test/groovy/org/onap/cps/spi/impl/CpsModulePersistenceServiceConcurrencySpec.groovy index 214fd69ff1..65d63dfe3b 100644 --- a/cps-ri/src/test/groovy/org/onap/cps/spi/impl/CpsModulePersistenceServiceConcurrencySpec.groovy +++ b/cps-ri/src/test/groovy/org/onap/cps/spi/impl/CpsModulePersistenceServiceConcurrencySpec.groovy @@ -1,6 +1,7 @@ /* * ============LICENSE_START======================================================= * Copyright (C) 2022 Bell Canada. + * Modifications 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. @@ -19,18 +20,14 @@ */ package org.onap.cps.spi.impl -import com.fasterxml.jackson.databind.ObjectMapper + import org.hibernate.exception.ConstraintViolationException -import org.onap.cps.spi.CpsAdminPersistenceService import org.onap.cps.spi.CpsModulePersistenceService import org.onap.cps.spi.entities.DataspaceEntity import org.onap.cps.spi.exceptions.DuplicatedYangResourceException import org.onap.cps.spi.model.ModuleReference -import org.onap.cps.spi.repository.AnchorRepository import org.onap.cps.spi.repository.DataspaceRepository -import org.onap.cps.spi.repository.SchemaSetRepository import org.onap.cps.spi.repository.YangResourceRepository -import org.onap.cps.utils.JsonObjectMapper import org.spockframework.spring.SpringBean import org.springframework.beans.factory.annotation.Autowired import org.springframework.dao.DataIntegrityViolationException @@ -43,24 +40,12 @@ class CpsModulePersistenceServiceConcurrencySpec extends CpsPersistenceSpecBase @Autowired CpsModulePersistenceService objectUnderTest - @Autowired - AnchorRepository anchorRepository - - @Autowired - SchemaSetRepository schemaSetRepository - - @Autowired - CpsAdminPersistenceService cpsAdminPersistenceService - @SpringBean YangResourceRepository yangResourceRepositoryMock = Mock() @SpringBean DataspaceRepository dataspaceRepositoryMock = Mock() - @SpringBean - JsonObjectMapper jsonObjectMapper = new JsonObjectMapper(new ObjectMapper()) - static final String DATASPACE_NAME = 'DATASPACE-001' static final String SCHEMA_SET_NAME_NEW = 'SCHEMA-SET-NEW' static final String NEW_RESOURCE_NAME = 'some new resource' diff --git a/cps-ri/src/test/groovy/org/onap/cps/spi/impl/CpsModulePersistenceServiceIntegrationSpec.groovy b/cps-ri/src/test/groovy/org/onap/cps/spi/impl/CpsModulePersistenceServiceIntegrationSpec.groovy index 864b3e3b61..53f42f5a9f 100644 --- a/cps-ri/src/test/groovy/org/onap/cps/spi/impl/CpsModulePersistenceServiceIntegrationSpec.groovy +++ b/cps-ri/src/test/groovy/org/onap/cps/spi/impl/CpsModulePersistenceServiceIntegrationSpec.groovy @@ -30,13 +30,10 @@ import org.onap.cps.spi.exceptions.SchemaSetNotFoundException import org.onap.cps.spi.model.ModuleDefinition import org.onap.cps.spi.model.ModuleReference import org.onap.cps.spi.model.SchemaSet -import org.onap.cps.spi.repository.AnchorRepository import org.onap.cps.spi.repository.SchemaSetRepository import org.onap.cps.spi.repository.SchemaSetYangResourceRepositoryImpl -import org.onap.cps.spi.repository.YangResourceRepository import org.springframework.beans.factory.annotation.Autowired import org.springframework.test.context.jdbc.Sql -import spock.lang.Ignore class CpsModulePersistenceServiceIntegrationSpec extends CpsPersistenceSpecBase { @@ -44,17 +41,11 @@ class CpsModulePersistenceServiceIntegrationSpec extends CpsPersistenceSpecBase CpsModulePersistenceService objectUnderTest @Autowired - AnchorRepository anchorRepository - - @Autowired SchemaSetRepository schemaSetRepository @Autowired CpsAdminPersistenceService cpsAdminPersistenceService - @Autowired - YangResourceRepository yangResourceRepository - final static String SET_DATA = '/data/schemaset.sql' def static EXISTING_SCHEMA_SET_NAME = SCHEMA_SET_NAME1 diff --git a/cps-ri/src/test/groovy/org/onap/cps/spi/performance/CpsModuleReferenceRepositoryPerfTest.groovy b/cps-ri/src/test/groovy/org/onap/cps/spi/performance/CpsModuleReferenceRepositoryPerfTest.groovy index 9b722cddae..222a828b9f 100644 --- a/cps-ri/src/test/groovy/org/onap/cps/spi/performance/CpsModuleReferenceRepositoryPerfTest.groovy +++ b/cps-ri/src/test/groovy/org/onap/cps/spi/performance/CpsModuleReferenceRepositoryPerfTest.groovy @@ -1,6 +1,6 @@ /* * ============LICENSE_START======================================================= - * Copyright (C) 2022 Nordix Foundation + * 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. @@ -24,7 +24,6 @@ import org.onap.cps.spi.CpsModulePersistenceService import org.onap.cps.spi.entities.SchemaSetEntity import org.onap.cps.spi.impl.CpsPersistenceSpecBase import org.onap.cps.spi.model.ModuleReference -import org.onap.cps.spi.repository.DataspaceRepository import org.onap.cps.spi.repository.ModuleReferenceRepository import org.onap.cps.spi.repository.SchemaSetRepository import org.springframework.beans.factory.annotation.Autowired @@ -53,9 +52,6 @@ class CpsModuleReferenceRepositoryPerfTest extends CpsPersistenceSpecBase { CpsModulePersistenceService objectUnderTest @Autowired - DataspaceRepository dataspaceRepository - - @Autowired SchemaSetRepository schemaSetRepository @Autowired diff --git a/cps-service/src/main/java/org/onap/cps/spi/CpsDataPersistenceService.java b/cps-service/src/main/java/org/onap/cps/spi/CpsDataPersistenceService.java index 540401913b..949fbc2c29 100644 --- a/cps-service/src/main/java/org/onap/cps/spi/CpsDataPersistenceService.java +++ b/cps-service/src/main/java/org/onap/cps/spi/CpsDataPersistenceService.java @@ -138,15 +138,6 @@ public interface CpsDataPersistenceService { void updateDataLeaves(String dataspaceName, String anchorName, String xpath, Map<String, Serializable> leaves); /** - * Replaces an existing data node's content including descendants. - * - * @param dataspaceName dataspace name - * @param anchorName anchor name - * @param dataNode data node - */ - void updateDataNodeAndDescendants(String dataspaceName, String anchorName, DataNode dataNode); - - /** * Replaces multiple existing data nodes' content including descendants in a batch operation. * * @param dataspaceName dataspace name diff --git a/dmi-plugin-stub/mappings/batchCmHandles.json b/dmi-plugin-stub/mappings/batchCmHandles.json index 2018516b4c..5f290cd47d 100644 --- a/dmi-plugin-stub/mappings/batchCmHandles.json +++ b/dmi-plugin-stub/mappings/batchCmHandles.json @@ -1,7 +1,7 @@ { "request": { "method": "POST", - "urlPattern": "/dmi/v1/ch/batch/data/ds/.*" + "urlPattern": "/dmi/v1/batch/data/ds/.*" }, "response": { "status": 200, diff --git a/docs/cps-path.rst b/docs/cps-path.rst index f321adfa99..252310dc0c 100644 --- a/docs/cps-path.rst +++ b/docs/cps-path.rst @@ -1,6 +1,7 @@ .. This work is licensed under a Creative Commons Attribution 4.0 International License. .. http://creativecommons.org/licenses/by/4.0 .. Copyright (C) 2021-2022 Nordix Foundation +.. Modifications Copyright (C) 2023 TechMahindra Ltd .. DO NOT CHANGE THIS LABEL FOR RELEASE NOTES - EVEN THOUGH IT GIVES A WARNING .. _path: @@ -222,7 +223,7 @@ descendant-path leaf-conditions --------------- -**Syntax**: ``<xpath> '[' @<leaf-name1> '=' <leaf-value1> ( ' and ' @<leaf-name> '=' <leaf-value> )* ']'`` +**Syntax**: ``<xpath> '[' @<leaf-name1> '=' <leaf-value1> ( ' <and|or> ' @<leaf-name> '=' <leaf-value> )* ']'`` - ``xpath``: Absolute or descendant or xpath to the (list) node which elements will be queried. - ``leaf-name``: The name of the leaf which value needs to be compared. - ``leaf-value``: The required value of the leaf. @@ -232,10 +233,13 @@ leaf-conditions - ``//categories[@name="Kids"]`` - ``//categories[@name='Kids']`` - ``//categories[@code='1']/books/book[@title='Dune' and @price=5]`` + - ``//categories[@code='1']/books/book[@title='xyz' or @price=15]`` - ``//categories[@code=1]`` **Limitations** - Only the last list or container can be queried leaf values. Any ancestor list will have to be referenced by its key name-value pair(s). - - Multiple attributes can only be combined using ``and``. ``or`` and bracketing is not supported. + - When mixing ``and/or`` operators, ``and`` has precedence over ``or`` . So ``and`` operators get evaluated first. + - Bracketing is not supported. + - Leaf names are not validated so ``or`` operations with invalid leaf names will silently be ignored. - Only leaves can be used, leaf-list are not supported. - Only string and integer values are supported, boolean and float values are not supported. - The key should be supplied with correct data type for it to be queried from DB. In the last example above the attribute code is of type diff --git a/integration-test/src/test/groovy/org/onap/cps/integration/functional/CpsQueryServiceIntegrationSpec.groovy b/integration-test/src/test/groovy/org/onap/cps/integration/functional/CpsQueryServiceIntegrationSpec.groovy index 38bb4de573..d8f7147578 100644 --- a/integration-test/src/test/groovy/org/onap/cps/integration/functional/CpsQueryServiceIntegrationSpec.groovy +++ b/integration-test/src/test/groovy/org/onap/cps/integration/functional/CpsQueryServiceIntegrationSpec.groovy @@ -63,19 +63,21 @@ class CpsQueryServiceIntegrationSpec extends FunctionalSpecBase { assert result.leaves.sort() == expectedLeaves.sort() println(expectedLeaves.toArray()) where: 'the following data is used' - scenario | cpspath || expectedResultSize | expectedLeaves - 'the "OR" condition' | '//books[@lang="English" or @price=15]' || 6 | [[lang: "English", price: 15, title: "Annihilation", authors: ["Jeff VanderMeer"], editions: [2014]], - [lang: "English", price: 15, title: "The Gruffalo", authors: ["Julia Donaldson"], editions: [1999]], - [lang: "English", price: 14, title: "The Light Fantastic", authors: ["Terry Pratchett"], editions: [1986]], - [lang: "English", price: 13, title: "Good Omens", authors: ["Terry Pratchett", "Neil Gaiman"], editions: [2006]], - [lang: "English", price: 12, title: "The Colour of Magic", authors: ["Terry Pratchett"], editions: [1983]], - [lang: "English", price: 10, title: "Matilda", authors: ["Roald Dahl"], editions: [1988, 2000]]] - 'the "OR" condition with non-json data' | '//books[@title="xyz" or @price=15]' || 2 | [[lang: "English", price: 15, title: "Annihilation", authors: ["Jeff VanderMeer"], editions: [2014]], - [lang: "English", price: 15, title: "The Gruffalo", authors: ["Julia Donaldson"], editions: [1999]]] - 'combination of multiple AND' | '//books[@lang="English" and @price=15 and @edition=1983]' || 0 | [] - 'combination of multiple OR' | '//books[ @title="Matilda" or @price=15 or @edition=1983]' || 3 | [[lang: "English", price: 15, title: "Annihilation", authors: ["Jeff VanderMeer"], editions: [2014]], - [lang: "English", price: 10, title: "Matilda", authors: ["Roald Dahl"], editions: [1988, 2000]], - [lang: "English", price: 15, title: "The Gruffalo", authors: ["Julia Donaldson"], editions: [1999]]] + scenario | cpspath || expectedResultSize | expectedLeaves + 'the "OR" condition' | '//books[@lang="English" or @price=15]' || 6 | [[lang: "English", price: 15, title: "Annihilation", authors: ["Jeff VanderMeer"], editions: [2014]], + [lang: "English", price: 15, title: "The Gruffalo", authors: ["Julia Donaldson"], editions: [1999]], + [lang: "English", price: 14, title: "The Light Fantastic", authors: ["Terry Pratchett"], editions: [1986]], + [lang: "English", price: 13, title: "Good Omens", authors: ["Terry Pratchett", "Neil Gaiman"], editions: [2006]], + [lang: "English", price: 12, title: "The Colour of Magic", authors: ["Terry Pratchett"], editions: [1983]], + [lang: "English", price: 10, title: "Matilda", authors: ["Roald Dahl"], editions: [1988, 2000]]] + 'the "OR" condition with non-json data' | '//books[@title="xyz" or @price=15]' || 2 | [[lang: "English", price: 15, title: "Annihilation", authors: ["Jeff VanderMeer"], editions: [2014]], + [lang: "English", price: 15, title: "The Gruffalo", authors: ["Julia Donaldson"], editions: [1999]]] + 'combination of multiple AND' | '//books[@lang="English" and @price=15 and @edition=1983]' || 0 | [] + 'combination of multiple OR' | '//books[ @title="Matilda" or @price=15 or @edition=1983]' || 3 | [[lang: "English", price: 15, title: "Annihilation", authors: ["Jeff VanderMeer"], editions: [2014]], + [lang: "English", price: 10, title: "Matilda", authors: ["Roald Dahl"], editions: [1988, 2000]], + [lang: "English", price: 15, title: "The Gruffalo", authors: ["Julia Donaldson"], editions: [1999]]] + 'combination of AND/OR' | '//books[@edition=1983 and @price=15 or @title="Good Omens"]' || 1 | [[lang: "English", price: 13, title: "Good Omens", authors: ["Terry Pratchett", "Neil Gaiman"], editions: [2006]]] + 'combination of OR/AND' | '//books[@title="Annihilation" or @price=39 and @lang="arabic"]' || 1 | [[lang: "English", price: 15, title: "Annihilation", authors: ["Jeff VanderMeer"], editions: [2014]]] } def 'Cps Path query for leaf value(s) with #scenario.'() { @@ -170,17 +172,19 @@ class CpsQueryServiceIntegrationSpec extends FunctionalSpecBase { def bookTitles = result.collect { it.getLeaves().get('title') } assert bookTitles.sort() == expectedBookTitles.sort() where: 'the following data is used' - scenario | cpsPath || expectedBookTitles - 'one leaf' | '//books[@price=14]' || ['The Light Fantastic'] - 'one text' | '//books/authors[text()="Terry Pratchett"]' || ['Good Omens', 'The Colour of Magic', 'The Light Fantastic'] - 'more than one leaf' | '//books[@price=12 and @lang="English"]' || ['The Colour of Magic'] - 'more than one leaf has "OR" condition' | '//books[@lang="English" or @price=15]' || ['Annihilation', 'Good Omens', 'Matilda', 'The Colour of Magic', 'The Gruffalo', 'The Light Fantastic'] - 'more than one leaf has "OR" condition with non-json data' | '//books[@title="xyz" or @price=13]' || ['Good Omens'] - 'more than one leaf has multiple AND' | '//books[@lang="English" and @price=13 and @edition=1983]' || [] - 'more than one leaf has multiple OR' | '//books[ @title="Matilda" or @price=15 or @edition=2006]' || ['Annihilation', 'Matilda', 'The Gruffalo'] - 'leaves reversed in order' | '//books[@lang="English" and @price=12]' || ['The Colour of Magic'] - 'leaf and text' | '//books[@price=14]/authors[text()="Terry Pratchett"]' || ['The Light Fantastic'] - 'leaf and contains' | '//books[contains(@price,"13")]' || ['Good Omens'] + scenario | cpsPath || expectedBookTitles + 'one leaf' | '//books[@price=14]' || ['The Light Fantastic'] + 'one text' | '//books/authors[text()="Terry Pratchett"]' || ['Good Omens', 'The Colour of Magic', 'The Light Fantastic'] + 'more than one leaf' | '//books[@price=12 and @lang="English"]' || ['The Colour of Magic'] + 'more than one leaf has "OR" condition' | '//books[@lang="English" or @price=15]' || ['Annihilation', 'Good Omens', 'Matilda', 'The Colour of Magic', 'The Gruffalo', 'The Light Fantastic'] + 'more than one leaf has "OR" condition with non-json data' | '//books[@title="xyz" or @price=13]' || ['Good Omens'] + 'more than one leaf has multiple AND' | '//books[@lang="English" and @price=13 and @edition=1983]' || [] + 'more than one leaf has multiple OR' | '//books[ @title="Matilda" or @price=15 or @edition=2006]' || ['Annihilation', 'Matilda', 'The Gruffalo'] + 'leaves reversed in order' | '//books[@lang="English" and @price=12]' || ['The Colour of Magic'] + 'more than one leaf has combination of AND/OR' | '//books[@edition=1983 and @price=13 or @title="Good Omens"]' || ['Good Omens'] + 'more than one leaf has OR/AND' | '//books[@title="The Light Fantastic" or @price=11 and @edition=1983]' || ['The Light Fantastic'] + 'leaf and text' | '//books[@price=14]/authors[text()="Terry Pratchett"]' || ['The Light Fantastic'] + 'leaf and contains' | '//books[contains(@price,"13")]' || ['Good Omens'] } def 'Cps Path query using descendant anywhere with #scenario condition(s) for a list element.'() { |