diff options
113 files changed, 1105 insertions, 1040 deletions
diff --git a/.github/workflows/gerrit-verify.yaml b/.github/workflows/gerrit-verify.yaml new file mode 100644 index 0000000000..91849215af --- /dev/null +++ b/.github/workflows/gerrit-verify.yaml @@ -0,0 +1,151 @@ +--- +name: Call Gerrit Verify + +# yamllint disable-line rule:truthy +on: + workflow_dispatch: + inputs: + GERRIT_BRANCH: + description: "Branch that change is against" + required: true + type: string + GERRIT_CHANGE_ID: + description: "The ID for the change" + required: true + type: string + GERRIT_CHANGE_NUMBER: + description: "The Gerrit number" + required: true + type: string + GERRIT_CHANGE_URL: + description: "URL to the change" + required: true + type: string + GERRIT_EVENT_TYPE: + description: "Type of Gerrit event" + required: true + type: string + GERRIT_PATCHSET_NUMBER: + description: "The patch number for the change" + required: true + type: string + GERRIT_PATCHSET_REVISION: + description: "The revision sha" + required: true + type: string + GERRIT_PROJECT: + description: "Project in Gerrit" + required: true + type: string + GERRIT_REFSPEC: + description: "Gerrit refspec of change" + required: true + type: string + secrets: + GERRIT_SSH_PRIVKEY: + description: "SSH Key for the authorized user account" + required: true + +concurrency: + # yamllint disable-line rule:line-length + group: gerrit-verify-${{ github.workflow }}-${{ github.event.inputs.GERRIT_BRANCH}}-${{ github.event.inputs.GERRIT_CHANGE_ID || github.run_id }} + cancel-in-progress: true + +jobs: + prepare: + runs-on: ubuntu-latest + steps: + - name: Clear votes + # yamllint disable-line rule:line-length + uses: lfit/gerrit-review-action@9627b9a144f2a2cad70707ddfae87c87dce60729 # v0.8 + with: + host: ${{ vars.GERRIT_SERVER }} + username: ${{ vars.GERRIT_SSH_USER }} + key: ${{ secrets.GERRIT_SSH_PRIVKEY }} + known_hosts: ${{ vars.GERRIT_KNOWN_HOSTS }} + gerrit-change-number: ${{ inputs.GERRIT_CHANGE_NUMBER }} + gerrit-patchset-number: ${{ inputs.GERRIT_PATCHSET_NUMBER }} + vote-type: clear + comment-only: true + - name: Allow replication + run: sleep 10s + + actionlint: + needs: prepare + runs-on: ubuntu-latest + steps: + - name: Gerrit Checkout + # yamllint disable-line rule:line-length + uses: lfit/checkout-gerrit-change-action@54d751e8bd167bc91f7d665dabe33fae87aaaa63 # v0.9 + with: + gerrit-refspec: ${{ inputs.GERRIT_REFSPEC }} + gerrit-project: ${{ inputs.GERRIT_PROJECT }} + gerrit-url: ${{ vars.GERRIT_URL }} + delay: "0s" + - name: Download actionlint + id: get_actionlint + # yamllint disable-line rule:line-length + run: bash <(curl https://raw.githubusercontent.com/rhysd/actionlint/main/scripts/download-actionlint.bash) + shell: bash + - name: Check workflow files + run: ${{ steps.get_actionlint.outputs.executable }} -color + shell: bash + + # run pre-commit tox env separately to get use of more parallel processing + pre-commit: + needs: prepare + runs-on: ubuntu-latest + steps: + - name: Gerrit Checkout + # yamllint disable-line rule:line-length + uses: lfit/checkout-gerrit-change-action@54d751e8bd167bc91f7d665dabe33fae87aaaa63 # v0.9 + with: + gerrit-refspec: ${{ inputs.GERRIT_REFSPEC }} + gerrit-project: ${{ inputs.GERRIT_PROJECT }} + gerrit-url: ${{ vars.GERRIT_URL }} + delay: "0s" + # yamllint disable-line rule:line-length + - uses: actions/setup-python@0a5c61591373683505ea898e09a3ea4f39ef2b9c # v5.0.0 + with: + python-version: "3.11" + - name: Run static analysis and format checkers + run: pipx run pre-commit run --all-files --show-diff-on-failure + + checkov-scan: + needs: prepare + runs-on: ubuntu-latest + steps: + - name: Gerrit Checkout + # yamllint disable-line rule:line-length + uses: lfit/checkout-gerrit-change-action@54d751e8bd167bc91f7d665dabe33fae87aaaa63 # v0.9 + with: + gerrit-refspec: ${{ inputs.GERRIT_REFSPEC }} + gerrit-project: ${{ inputs.GERRIT_PROJECT }} + gerrit-url: ${{ vars.GERRIT_URL }} + delay: "0s" + submodules: "true" + - name: Checkov GitHub Action + uses: bridgecrewio/checkov-action@v12 + with: + output_format: cli,sarif + output_file_path: console,results.sarif + + vote: + if: ${{ always() }} + needs: [prepare, actionlint, pre-commit, checkov-scan] + runs-on: ubuntu-latest + steps: + - name: Get conclusion + uses: im-open/workflow-conclusion@e4f7c4980600fbe0818173e30931d3550801b992 # v2.2.3 + - name: Set vote + # yamllint disable-line rule:line-length + uses: lfit/gerrit-review-action@9627b9a144f2a2cad70707ddfae87c87dce60729 # v0.8 + with: + host: ${{ vars.GERRIT_SERVER }} + username: ${{ vars.GERRIT_SSH_USER }} + key: ${{ secrets.GERRIT_SSH_PRIVKEY }} + known_hosts: ${{ vars.GERRIT_KNOWN_HOSTS }} + gerrit-change-number: ${{ inputs.GERRIT_CHANGE_NUMBER }} + gerrit-patchset-number: ${{ inputs.GERRIT_PATCHSET_NUMBER }} + vote-type: ${{ env.WORKFLOW_CONCLUSION }} + comment-only: true diff --git a/cps-application/src/main/resources/application.yml b/cps-application/src/main/resources/application.yml index b2cbe7ff4d..b97eabacb8 100644 --- a/cps-application/src/main/resources/application.yml +++ b/cps-application/src/main/resources/application.yml @@ -190,6 +190,7 @@ logging: ncmp: policy-executor: enabled: ${POLICY_SERVICE_ENABLED:false} + defaultDecision: "allow" server: address: ${POLICY_SERVICE_URL:http://policy-executor-stub} port: ${POLICY_SERVICE_PORT:8093} @@ -228,7 +229,7 @@ ncmp: advised-modules-sync: sleep-time-ms: 5000 locked-modules-sync: - sleep-time-ms: 60000 + sleep-time-ms: 15000 cm-handle-data-sync: sleep-time-ms: 30000 subscription-forwarding: diff --git a/cps-application/src/test/java/org/onap/cps/architecture/LayeredArchitectureTest.java b/cps-application/src/test/java/org/onap/cps/architecture/LayeredArchitectureTest.java index 91452a7fc5..82fdc7f487 100644 --- a/cps-application/src/test/java/org/onap/cps/architecture/LayeredArchitectureTest.java +++ b/cps-application/src/test/java/org/onap/cps/architecture/LayeredArchitectureTest.java @@ -38,9 +38,9 @@ public class LayeredArchitectureTest { private static final String REST_CONTROLLER_PACKAGE = "org.onap.cps.rest.."; private static final String NCMP_REST_PACKAGE = "org.onap.cps.ncmp.rest.."; private static final String API_SERVICE_PACKAGE = "org.onap.cps.api.."; - private static final String SPI_SERVICE_PACKAGE = "org.onap.cps.spi.."; + private static final String SPI_SERVICE_PACKAGE = "org.onap.cps.ri.."; private static final String NCMP_SERVICE_PACKAGE = "org.onap.cps.ncmp.api.."; - private static final String SPI_REPOSITORY_PACKAGE = "org.onap.cps.spi.repository.."; + private static final String SPI_REPOSITORY_PACKAGE = "org.onap.cps.ri.repository.."; private static final String YANG_SCHEMA_PACKAGE = "org.onap.cps.yang.."; private static final String NOTIFICATION_PACKAGE = "org.onap.cps.notification.."; private static final String CPS_UTILS_PACKAGE = "org.onap.cps.utils.."; diff --git a/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/inventory/NetworkCmProxyInventoryFacade.java b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/inventory/NetworkCmProxyInventoryFacade.java index cde4eacbf2..1fa801c3c5 100644 --- a/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/inventory/NetworkCmProxyInventoryFacade.java +++ b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/inventory/NetworkCmProxyInventoryFacade.java @@ -26,7 +26,6 @@ package org.onap.cps.ncmp.api.inventory; import static org.onap.cps.ncmp.impl.inventory.CmHandleQueryParametersValidator.validateCmHandleQueryParameters; -import java.util.ArrayList; import java.util.Collection; import java.util.Map; import lombok.RequiredArgsConstructor; @@ -45,7 +44,6 @@ import org.onap.cps.ncmp.impl.inventory.models.CmHandleQueryConditions; import org.onap.cps.ncmp.impl.inventory.models.InventoryQueryConditions; import org.onap.cps.ncmp.impl.inventory.models.YangModelCmHandle; import org.onap.cps.ncmp.impl.inventory.trustlevel.TrustLevelManager; -import org.onap.cps.ncmp.impl.models.RequiredDmiService; import org.onap.cps.ncmp.impl.utils.AlternateIdMatcher; import org.onap.cps.ncmp.impl.utils.YangDataConverter; import org.onap.cps.spi.model.ModuleDefinition; @@ -148,13 +146,9 @@ public class NetworkCmProxyInventoryFacade { final CmHandleQueryServiceParameters cmHandleQueryServiceParameters = jsonObjectMapper.convertToValueType( cmHandleQueryApiParameters, CmHandleQueryServiceParameters.class); validateCmHandleQueryParameters(cmHandleQueryServiceParameters, CmHandleQueryConditions.ALL_CONDITION_NAMES); - final Collection<YangModelCmHandle> yangModelCmHandles = - parameterizedCmHandleQueryService.queryCmHandles(cmHandleQueryServiceParameters); - final Collection<NcmpServiceCmHandle> ncmpServiceCmHandles = new ArrayList<>(yangModelCmHandles.size()); - for (final YangModelCmHandle yangModelCmHandle : yangModelCmHandles) { - final NcmpServiceCmHandle ncmpServiceCmHandle = toNcmpServiceCmHandleWithTrustLevel(yangModelCmHandle); - ncmpServiceCmHandles.add(ncmpServiceCmHandle); - } + final Collection<NcmpServiceCmHandle> ncmpServiceCmHandles = + parameterizedCmHandleQueryService.queryCmHandles(cmHandleQueryServiceParameters); + ncmpServiceCmHandles.forEach(this::applyCurrentTrustLevel); return ncmpServiceCmHandles; } @@ -190,8 +184,10 @@ public class NetworkCmProxyInventoryFacade { */ public NcmpServiceCmHandle getNcmpServiceCmHandle(final String cmHandleReference) { final String cmHandleId = alternateIdMatcher.getCmHandleId(cmHandleReference); - final YangModelCmHandle yangModelCmHandle = inventoryPersistence.getYangModelCmHandle(cmHandleId); - return toNcmpServiceCmHandleWithTrustLevel(yangModelCmHandle); + final NcmpServiceCmHandle ncmpServiceCmHandle = YangDataConverter.toNcmpServiceCmHandle( + inventoryPersistence.getYangModelCmHandle(cmHandleId)); + applyCurrentTrustLevel(ncmpServiceCmHandle); + return ncmpServiceCmHandle; } /** @@ -217,12 +213,9 @@ public class NetworkCmProxyInventoryFacade { return inventoryPersistence.getYangModelCmHandle(cmHandleId).getCompositeState(); } - private NcmpServiceCmHandle toNcmpServiceCmHandleWithTrustLevel(final YangModelCmHandle yangModelCmHandle) { - final NcmpServiceCmHandle ncmpServiceCmHandle = YangDataConverter.toNcmpServiceCmHandle(yangModelCmHandle); - final String dmiServiceName = yangModelCmHandle.resolveDmiServiceName(RequiredDmiService.DATA); - ncmpServiceCmHandle.setCurrentTrustLevel( - trustLevelManager.getEffectiveTrustLevel(dmiServiceName, ncmpServiceCmHandle.getCmHandleId())); - return ncmpServiceCmHandle; + private void applyCurrentTrustLevel(final NcmpServiceCmHandle ncmpServiceCmHandle) { + ncmpServiceCmHandle.setCurrentTrustLevel(trustLevelManager + .getEffectiveTrustLevel(ncmpServiceCmHandle.getCmHandleId())); } } diff --git a/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/config/PolicyExecutorHttpClientConfig.java b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/config/PolicyExecutorHttpClientConfig.java index 0903c671b5..30e7cd5e0b 100644 --- a/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/config/PolicyExecutorHttpClientConfig.java +++ b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/config/PolicyExecutorHttpClientConfig.java @@ -20,6 +20,7 @@ package org.onap.cps.ncmp.config; +import jakarta.annotation.PostConstruct; import lombok.Getter; import lombok.Setter; import org.springframework.boot.context.properties.ConfigurationProperties; @@ -38,4 +39,9 @@ public class PolicyExecutorHttpClientConfig { public static class AllServices extends ServiceConfig { private String connectionProviderName = "policyExecutorConfig"; } + + @PostConstruct + public void increaseReadTimeoutOfWebClient() { + allServices.setReadTimeoutInSeconds(allServices.getReadTimeoutInSeconds() + 10); + } } diff --git a/cps-service/src/main/java/org/onap/cps/cache/HazelcastCacheConfig.java b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/impl/cache/HazelcastCacheConfig.java index 418de9b17a..d911fc61b9 100644 --- a/cps-service/src/main/java/org/onap/cps/cache/HazelcastCacheConfig.java +++ b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/impl/cache/HazelcastCacheConfig.java @@ -18,7 +18,7 @@ * ============LICENSE_END========================================================= */ -package org.onap.cps.cache; +package org.onap.cps.ncmp.impl.cache; import com.hazelcast.config.Config; import com.hazelcast.config.MapConfig; diff --git a/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/impl/cmnotificationsubscription/cache/CmSubscriptionConfig.java b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/impl/cmnotificationsubscription/cache/CmSubscriptionConfig.java index a4f9be357f..e890d7288c 100644 --- a/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/impl/cmnotificationsubscription/cache/CmSubscriptionConfig.java +++ b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/impl/cmnotificationsubscription/cache/CmSubscriptionConfig.java @@ -23,7 +23,7 @@ package org.onap.cps.ncmp.impl.cmnotificationsubscription.cache; import com.hazelcast.config.MapConfig; import com.hazelcast.map.IMap; import java.util.Map; -import org.onap.cps.cache.HazelcastCacheConfig; +import org.onap.cps.ncmp.impl.cache.HazelcastCacheConfig; import org.onap.cps.ncmp.impl.cmnotificationsubscription.models.DmiCmSubscriptionDetails; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; diff --git a/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/impl/data/policyexecutor/PolicyExecutor.java b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/impl/data/policyexecutor/PolicyExecutor.java index 96771e30f1..caed28a648 100644 --- a/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/impl/data/policyexecutor/PolicyExecutor.java +++ b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/impl/data/policyexecutor/PolicyExecutor.java @@ -23,16 +23,18 @@ package org.onap.cps.ncmp.impl.data.policyexecutor; import com.fasterxml.jackson.core.JsonProcessingException; import com.fasterxml.jackson.databind.JsonNode; import com.fasterxml.jackson.databind.ObjectMapper; +import java.time.Duration; +import java.time.temporal.ChronoUnit; import java.util.Collections; import java.util.HashMap; import java.util.List; import java.util.Map; +import java.util.concurrent.TimeoutException; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; import org.onap.cps.ncmp.api.data.models.OperationType; import org.onap.cps.ncmp.api.exceptions.NcmpException; import org.onap.cps.ncmp.api.exceptions.PolicyExecutorException; -import org.onap.cps.ncmp.api.exceptions.ServerNcmpException; import org.onap.cps.ncmp.impl.inventory.models.YangModelCmHandle; import org.onap.cps.ncmp.impl.utils.http.RestServiceUrlTemplateBuilder; import org.onap.cps.ncmp.impl.utils.http.UrlTemplateParameters; @@ -52,12 +54,18 @@ public class PolicyExecutor { @Value("${ncmp.policy-executor.enabled:false}") private boolean enabled; + @Value("${ncmp.policy-executor.defaultDecision:deny}") + private String defaultDecision; + @Value("${ncmp.policy-executor.server.address:http://policy-executor}") private String serverAddress; @Value("${ncmp.policy-executor.server.port:8080}") private String serverPort; + @Value("${ncmp.policy-executor.httpclient.all-services.readTimeoutInSeconds:30}") + private long readTimeoutInSeconds; + @Qualifier("policyExecutorWebClient") private final WebClient policyExecutorWebClient; @@ -80,26 +88,26 @@ public class PolicyExecutor { final String changeRequestAsJson) { log.trace("Policy Executor Enabled: {}", enabled); if (enabled) { - final ResponseEntity<JsonNode> responseEntity = - getPolicyExecutorResponse(yangModelCmHandle, operationType, authorization, resourceIdentifier, - changeRequestAsJson); - + ResponseEntity<JsonNode> responseEntity = null; + try { + responseEntity = + getPolicyExecutorResponse(yangModelCmHandle, operationType, authorization, resourceIdentifier, + changeRequestAsJson); + } catch (final RuntimeException runtimeException) { + processException(runtimeException); + } if (responseEntity == null) { - log.warn("No valid response from policy, ignored"); + log.warn("No valid response from Policy Executor, ignored"); return; } - if (responseEntity.getStatusCode().is2xxSuccessful()) { if (responseEntity.getBody() == null) { - log.warn("No valid response body from policy, ignored"); + log.warn("No valid response body from Policy Executor, ignored"); return; } - processResponse(responseEntity.getBody()); + processSuccessResponse(responseEntity.getBody()); } else { - log.warn("Policy Executor invocation failed with status {}", - responseEntity.getStatusCode().value()); - throw new ServerNcmpException("Policy Executor invocation failed", "HTTP status code: " - + responseEntity.getStatusCode().value()); + processNon2xxResponse(responseEntity.getStatusCode().value()); } } } @@ -143,8 +151,6 @@ public class PolicyExecutor { final String authorization, final String resourceIdentifier, final String changeRequestAsJson) { - - final Map<String, Object> requestAsMap = getSingleRequestAsMap(yangModelCmHandle, operationType, resourceIdentifier, @@ -163,21 +169,46 @@ public class PolicyExecutor { .body(BodyInserters.fromValue(bodyAsObject)) .retrieve() .toEntity(JsonNode.class) + .timeout(Duration.of(readTimeoutInSeconds, ChronoUnit.SECONDS)) .block(); } - private static void processResponse(final JsonNode responseBody) { + private void processNon2xxResponse(final int httpStatusCode) { + processFallbackResponse("Policy Executor returned HTTP Status code " + httpStatusCode + "."); + } + + private void processException(final RuntimeException runtimeException) { + if (runtimeException.getCause() instanceof TimeoutException) { + processFallbackResponse("Policy Executor request timed out."); + } else { + log.warn("Request to Policy Executor failed with unexpected exception", runtimeException); + throw runtimeException; + } + } + + private void processFallbackResponse(final String message) { + final String decisionId = "N/A"; + final String decision = defaultDecision; + final String warning = message + " Falling back to configured default decision: " + defaultDecision; + log.warn(warning); + processDecision(decisionId, decision, warning); + } + + private static void processSuccessResponse(final JsonNode responseBody) { final String decisionId = responseBody.path("decisionId").asText("unknown id"); - log.trace("Policy Executor Decision ID: {} ", decisionId); final String decision = responseBody.path("decision").asText("unknown"); + final String messageFromPolicyExecutor = responseBody.path("message").asText(); + processDecision(decisionId, decision, messageFromPolicyExecutor); + } + + private static void processDecision(final String decisionId, final String decision, final String details) { + log.trace("Policy Executor decision id: {} ", decisionId); if ("allow".equals(decision)) { - log.trace("Policy Executor allows the operation"); + log.trace("Operation allowed."); } else { log.warn("Policy Executor decision: {}", decision); - final String details = responseBody.path("message").asText(); log.warn("Policy Executor message: {}", details); - final String message = "Policy Executor did not allow request. Decision #" - + decisionId + " : " + decision; + final String message = "Operation not allowed. Decision id " + decisionId + " : " + decision; throw new PolicyExecutorException(message, details); } } diff --git a/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/impl/inventory/CmHandleQueryServiceImpl.java b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/impl/inventory/CmHandleQueryServiceImpl.java index f32008d482..71e7384208 100644 --- a/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/impl/inventory/CmHandleQueryServiceImpl.java +++ b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/impl/inventory/CmHandleQueryServiceImpl.java @@ -36,6 +36,7 @@ import lombok.RequiredArgsConstructor; import org.onap.cps.api.CpsDataService; import org.onap.cps.api.CpsQueryService; import org.onap.cps.cpspath.parser.CpsPathUtil; +import org.onap.cps.impl.utils.CpsValidator; import org.onap.cps.ncmp.api.inventory.models.TrustLevel; import org.onap.cps.ncmp.impl.inventory.models.CmHandleState; import org.onap.cps.ncmp.impl.inventory.models.ModelledDmiServiceLeaves; @@ -43,7 +44,6 @@ import org.onap.cps.ncmp.impl.inventory.models.PropertyType; import org.onap.cps.ncmp.impl.inventory.trustlevel.TrustLevelCacheConfig; import org.onap.cps.spi.FetchDescendantsOption; import org.onap.cps.spi.model.DataNode; -import org.onap.cps.spi.utils.CpsValidator; import org.springframework.beans.factory.annotation.Qualifier; import org.springframework.stereotype.Component; diff --git a/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/impl/inventory/InventoryPersistenceImpl.java b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/impl/inventory/InventoryPersistenceImpl.java index 13cde86b6e..06c3f8d2f4 100644 --- a/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/impl/inventory/InventoryPersistenceImpl.java +++ b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/impl/inventory/InventoryPersistenceImpl.java @@ -37,6 +37,7 @@ import lombok.extern.slf4j.Slf4j; import org.onap.cps.api.CpsAnchorService; import org.onap.cps.api.CpsDataService; import org.onap.cps.api.CpsModuleService; +import org.onap.cps.impl.utils.CpsValidator; import org.onap.cps.ncmp.api.inventory.models.CompositeState; import org.onap.cps.ncmp.api.inventory.models.CompositeStateBuilder; import org.onap.cps.ncmp.impl.inventory.models.YangModelCmHandle; @@ -47,7 +48,6 @@ import org.onap.cps.spi.exceptions.DataValidationException; import org.onap.cps.spi.model.DataNode; import org.onap.cps.spi.model.ModuleDefinition; import org.onap.cps.spi.model.ModuleReference; -import org.onap.cps.spi.utils.CpsValidator; import org.onap.cps.utils.ContentType; import org.onap.cps.utils.JsonObjectMapper; import org.springframework.stereotype.Component; diff --git a/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/impl/inventory/NcmpPersistenceImpl.java b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/impl/inventory/NcmpPersistenceImpl.java index e44b6ba342..905b09ef74 100644 --- a/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/impl/inventory/NcmpPersistenceImpl.java +++ b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/impl/inventory/NcmpPersistenceImpl.java @@ -29,10 +29,10 @@ import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; import org.onap.cps.api.CpsDataService; import org.onap.cps.api.CpsModuleService; +import org.onap.cps.impl.utils.CpsValidator; import org.onap.cps.spi.FetchDescendantsOption; import org.onap.cps.spi.exceptions.SchemaSetNotFoundException; import org.onap.cps.spi.model.DataNode; -import org.onap.cps.spi.utils.CpsValidator; import org.onap.cps.utils.JsonObjectMapper; import org.springframework.stereotype.Component; diff --git a/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/impl/inventory/ParameterizedCmHandleQueryService.java b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/impl/inventory/ParameterizedCmHandleQueryService.java index 03ec30b73b..e5848c0dfa 100644 --- a/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/impl/inventory/ParameterizedCmHandleQueryService.java +++ b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/impl/inventory/ParameterizedCmHandleQueryService.java @@ -22,7 +22,7 @@ package org.onap.cps.ncmp.impl.inventory; import java.util.Collection; import org.onap.cps.ncmp.api.inventory.models.CmHandleQueryServiceParameters; -import org.onap.cps.ncmp.impl.inventory.models.YangModelCmHandle; +import org.onap.cps.ncmp.api.inventory.models.NcmpServiceCmHandle; public interface ParameterizedCmHandleQueryService { /** @@ -51,14 +51,22 @@ public interface ParameterizedCmHandleQueryService { Collection<String> queryCmHandleIdsForInventory(CmHandleQueryServiceParameters cmHandleQueryServiceParameters); /** - * Query and return yang model cm handle objects that match the given query parameters. + * Query and return cm handle objects that match the given query parameters. * Supported query types: * public properties * modules * cps-path * * @param cmHandleQueryServiceParameters the cm handle query parameters - * @return collection of yang model cm handles + * @return collection of cm handles */ - Collection<YangModelCmHandle> queryCmHandles(CmHandleQueryServiceParameters cmHandleQueryServiceParameters); + Collection<NcmpServiceCmHandle> queryCmHandles(CmHandleQueryServiceParameters cmHandleQueryServiceParameters); + + /** + * Get all cm handle objects. + * Note: it is similar to all the queries above but simply no conditions and hence not 'parameterized' + * + * @return collection of cm handles + */ + Collection<NcmpServiceCmHandle> getAllCmHandles(); } diff --git a/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/impl/inventory/ParameterizedCmHandleQueryServiceImpl.java b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/impl/inventory/ParameterizedCmHandleQueryServiceImpl.java index 84229e2895..34eeaccf8f 100644 --- a/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/impl/inventory/ParameterizedCmHandleQueryServiceImpl.java +++ b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/impl/inventory/ParameterizedCmHandleQueryServiceImpl.java @@ -27,6 +27,7 @@ import static org.onap.cps.ncmp.impl.inventory.models.CmHandleQueryConditions.HA import static org.onap.cps.ncmp.impl.inventory.models.CmHandleQueryConditions.HAS_ALL_PROPERTIES; import static org.onap.cps.ncmp.impl.inventory.models.CmHandleQueryConditions.WITH_CPS_PATH; import static org.onap.cps.ncmp.impl.inventory.models.CmHandleQueryConditions.WITH_TRUST_LEVEL; +import static org.onap.cps.ncmp.impl.utils.YangDataConverter.toNcmpServiceCmHandle; import static org.onap.cps.spi.FetchDescendantsOption.DIRECT_CHILDREN_ONLY; import static org.onap.cps.spi.FetchDescendantsOption.OMIT_DESCENDANTS; @@ -39,8 +40,10 @@ import java.util.Map; import java.util.function.Function; import java.util.stream.Collectors; import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; import org.onap.cps.cpspath.parser.PathParsingException; import org.onap.cps.ncmp.api.inventory.models.CmHandleQueryServiceParameters; +import org.onap.cps.ncmp.api.inventory.models.NcmpServiceCmHandle; import org.onap.cps.ncmp.impl.inventory.models.InventoryQueryConditions; import org.onap.cps.ncmp.impl.inventory.models.PropertyType; import org.onap.cps.ncmp.impl.inventory.models.YangModelCmHandle; @@ -51,6 +54,7 @@ import org.onap.cps.spi.model.DataNode; import org.springframework.stereotype.Service; @Service +@Slf4j @RequiredArgsConstructor public class ParameterizedCmHandleQueryServiceImpl implements ParameterizedCmHandleQueryService { @@ -79,18 +83,22 @@ public class ParameterizedCmHandleQueryServiceImpl implements ParameterizedCmHan } @Override - public Collection<YangModelCmHandle> queryCmHandles( - final CmHandleQueryServiceParameters cmHandleQueryServiceParameters) { + public Collection<NcmpServiceCmHandle> queryCmHandles( + final CmHandleQueryServiceParameters cmHandleQueryServiceParameters) { + if (cmHandleQueryServiceParameters.getCmHandleQueryParameters().isEmpty()) { return getAllCmHandles(); } + final Collection<String> cmHandleIds = queryCmHandleIds(cmHandleQueryServiceParameters); - return inventoryPersistence.getYangModelCmHandles(cmHandleIds); + + return getNcmpServiceCmHandles(cmHandleIds); } - private Collection<YangModelCmHandle> getAllCmHandles() { + @Override + public Collection<NcmpServiceCmHandle> getAllCmHandles() { final DataNode dataNode = inventoryPersistence.getDataNode(NCMP_DMI_REGISTRY_PARENT).iterator().next(); - return dataNode.getChildDataNodes().stream().map(YangDataConverter::toYangModelCmHandle).toList(); + return dataNode.getChildDataNodes().stream().map(this::createNcmpServiceCmHandle).collect(Collectors.toSet()); } private Collection<String> queryCmHandlesByDmiPlugin( @@ -218,6 +226,22 @@ public class ParameterizedCmHandleQueryServiceImpl implements ParameterizedCmHan return collectCmHandleIdsFromDataNodes(dataNode.getChildDataNodes()); } + private Collection<NcmpServiceCmHandle> getNcmpServiceCmHandles(final Collection<String> cmHandleIds) { + final Collection<YangModelCmHandle> yangModelcmHandles + = inventoryPersistence.getYangModelCmHandles(cmHandleIds); + + final Collection<NcmpServiceCmHandle> ncmpServiceCmHandles = new ArrayList<>(yangModelcmHandles.size()); + + yangModelcmHandles.forEach(yangModelcmHandle -> + ncmpServiceCmHandles.add(YangDataConverter.toNcmpServiceCmHandle(yangModelcmHandle)) + ); + return ncmpServiceCmHandles; + } + + private NcmpServiceCmHandle createNcmpServiceCmHandle(final DataNode dataNode) { + return toNcmpServiceCmHandle(YangDataConverter.toYangModelCmHandle(dataNode)); + } + private Collection<String> executeQueries(final CmHandleQueryServiceParameters cmHandleQueryServiceParameters, final Function<CmHandleQueryServiceParameters, Collection<String>>... queryFunctions) { diff --git a/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/impl/inventory/sync/ModuleOperationsUtils.java b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/impl/inventory/sync/ModuleOperationsUtils.java index 97f1e8e70f..aae1769968 100644 --- a/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/impl/inventory/sync/ModuleOperationsUtils.java +++ b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/impl/inventory/sync/ModuleOperationsUtils.java @@ -22,9 +22,6 @@ package org.onap.cps.ncmp.impl.inventory.sync; import com.fasterxml.jackson.databind.JsonNode; -import java.time.Duration; -import java.time.OffsetDateTime; -import java.time.format.DateTimeFormatter; import java.util.ArrayList; import java.util.Collection; import java.util.Collections; @@ -62,8 +59,7 @@ public class ModuleOperationsUtils { private static final String RETRY_ATTEMPT_KEY = "attempt"; private static final String MODULE_SET_TAG_KEY = "moduleSetTag"; public static final String MODULE_SET_TAG_MESSAGE_FORMAT = "Upgrade to ModuleSetTag: %s"; - private static final String LOCK_REASON_DETAILS_MSG_FORMAT = - MODULE_SET_TAG_MESSAGE_FORMAT + " Attempt #%d failed: %s"; + private static final String LOCK_REASON_DETAILS_MSG_FORMAT = " Attempt #%d failed: %s"; private static final Pattern retryAttemptPattern = Pattern.compile("Attempt #(\\d+) failed:.+"); private static final Pattern moduleSetTagPattern = Pattern.compile("Upgrade to ModuleSetTag: (\\S+)"); private static final String CPS_PATH_CM_HANDLES_MODEL_SYNC_FAILED_OR_UPGRADE = """ @@ -119,23 +115,25 @@ public class ModuleOperationsUtils { } /** - * Update Composite State attempts counter and set new lock reason and details. + * Updates the lock reason message and attempt counter for the provided CompositeState. + * This method increments the attempt counter and updates the lock reason message, + * including the module set tag if available. * - * @param lockReasonCategory lock reason category - * @param errorMessage error message + * @param compositeState the composite state of the CM handle + * @param lockReasonCategory the lock reason category for the CM handle + * @param errorMessage the error message to include in the lock reason message */ - public void updateLockReasonDetailsAndAttempts(final CompositeState compositeState, - final LockReasonCategory lockReasonCategory, - final String errorMessage) { - int attempt = 1; - final Map<String, String> compositeStateDetails - = getLockedCompositeStateDetails(compositeState.getLockReason()); - if (!compositeStateDetails.isEmpty() && compositeStateDetails.containsKey(RETRY_ATTEMPT_KEY)) { - attempt = 1 + Integer.parseInt(compositeStateDetails.get(RETRY_ATTEMPT_KEY)); - } - final String moduleSetTag = compositeStateDetails.getOrDefault(MODULE_SET_TAG_KEY, ""); + public void updateLockReasonWithAttempts(final CompositeState compositeState, + final LockReasonCategory lockReasonCategory, + final String errorMessage) { + final Map<String, String> lockedStateDetails = getLockedCompositeStateDetails(compositeState.getLockReason()); + final int nextAttemptCount = calculateNextAttemptCount(lockedStateDetails); + final String moduleSetTag = lockedStateDetails.getOrDefault(MODULE_SET_TAG_KEY, ""); + + final String lockReasonMessage = buildLockReasonDetails(moduleSetTag, nextAttemptCount, errorMessage); + compositeState.setLockReason(CompositeState.LockReason.builder() - .details(String.format(LOCK_REASON_DETAILS_MSG_FORMAT, moduleSetTag, attempt, errorMessage)) + .details(lockReasonMessage) .lockReasonCategory(lockReasonCategory) .build()); } @@ -166,37 +164,6 @@ public class ModuleOperationsUtils { return Collections.emptyMap(); } - - /** - * Check if a module sync retry is needed. - * - * @param compositeState the composite state currently in the locked state - * @return if the retry mechanism should be attempted - */ - public boolean needsModuleSyncRetryOrUpgrade(final CompositeState compositeState) { - final OffsetDateTime time = OffsetDateTime.parse(compositeState.getLastUpdateTime(), - DateTimeFormatter.ofPattern("yyyy-MM-dd'T'HH:mm:ss.SSSZ")); - final CompositeState.LockReason lockReason = compositeState.getLockReason(); - - final boolean moduleUpgrade = LockReasonCategory.MODULE_UPGRADE == lockReason.getLockReasonCategory(); - if (moduleUpgrade) { - log.info("Locked for module upgrade"); - return true; - } - - final boolean failedDuringModuleSync = LockReasonCategory.MODULE_SYNC_FAILED - == lockReason.getLockReasonCategory(); - final boolean failedDuringModuleUpgrade = LockReasonCategory.MODULE_UPGRADE_FAILED - == lockReason.getLockReasonCategory(); - - if (failedDuringModuleSync || failedDuringModuleUpgrade) { - log.info("Locked for module {} (last attempt failed).", failedDuringModuleSync ? "sync" : "upgrade"); - return isRetryDue(lockReason, time); - } - log.info("Locked for other reason"); - return false; - } - /** * Get the Resource Data from Node through DMI Passthrough service. * @@ -242,22 +209,18 @@ public class ModuleOperationsUtils { return cmHandlesAsDataNodeList.stream().map(YangDataConverter::toYangModelCmHandle).toList(); } - private boolean isRetryDue(final CompositeState.LockReason compositeStateLockReason, final OffsetDateTime time) { - final int timeInMinutesUntilNextAttempt; - final Map<String, String> compositeStateDetails = getLockedCompositeStateDetails(compositeStateLockReason); - if (compositeStateDetails.isEmpty()) { - timeInMinutesUntilNextAttempt = 1; - log.info("First Attempt: no current attempts found."); - } else { - timeInMinutesUntilNextAttempt = (int) Math.pow(2, Integer.parseInt(compositeStateDetails - .get(RETRY_ATTEMPT_KEY))); - } - final int timeSinceLastAttempt = (int) Duration.between(time, OffsetDateTime.now()).toMinutes(); - if (timeInMinutesUntilNextAttempt >= timeSinceLastAttempt) { - log.info("Time until next attempt is {} minutes: ", timeInMinutesUntilNextAttempt - timeSinceLastAttempt); - return false; + private int calculateNextAttemptCount(final Map<String, String> compositeStateDetails) { + return compositeStateDetails.containsKey(RETRY_ATTEMPT_KEY) + ? 1 + Integer.parseInt(compositeStateDetails.get(RETRY_ATTEMPT_KEY)) + : 1; + } + + private String buildLockReasonDetails(final String moduleSetTag, final int attempt, final String errorMessage) { + if (moduleSetTag.isEmpty()) { + return String.format(LOCK_REASON_DETAILS_MSG_FORMAT, attempt, errorMessage); } - log.info("Retry due now"); - return true; + return String.format(MODULE_SET_TAG_MESSAGE_FORMAT + " " + LOCK_REASON_DETAILS_MSG_FORMAT, + moduleSetTag, attempt, errorMessage); } + } diff --git a/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/impl/inventory/sync/ModuleSyncTasks.java b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/impl/inventory/sync/ModuleSyncTasks.java index 8e5c9f3066..c6deb79d4d 100644 --- a/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/impl/inventory/sync/ModuleSyncTasks.java +++ b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/impl/inventory/sync/ModuleSyncTasks.java @@ -79,7 +79,7 @@ public class ModuleSyncTasks { log.warn("Processing of {} module failed due to reason {}.", cmHandleId, e.getMessage()); final LockReasonCategory lockReasonCategory = inUpgrade ? LockReasonCategory.MODULE_UPGRADE_FAILED : LockReasonCategory.MODULE_SYNC_FAILED; - moduleOperationsUtils.updateLockReasonDetailsAndAttempts(compositeState, + moduleOperationsUtils.updateLockReasonWithAttempts(compositeState, lockReasonCategory, e.getMessage()); setCmHandleStateLocked(yangModelCmHandle, compositeState.getLockReason()); cmHandelStatePerCmHandle.put(yangModelCmHandle, CmHandleState.LOCKED); @@ -95,23 +95,23 @@ public class ModuleSyncTasks { } /** - * Reset state to "ADVISED" for any previously failed cm handles. + * Resets the state of failed CM handles and updates their status to ADVISED for retry. + + * This method processes a collection of failed CM handles, logs their lock reason, and resets their state + * to ADVISED. Once reset, it updates the CM handle states in a batch to allow for re-attempt by the module-sync + * watchdog. * - * @param failedCmHandles previously failed (locked) cm handles + * @param failedCmHandles a collection of CM handles that have failed and need their state reset */ public void resetFailedCmHandles(final Collection<YangModelCmHandle> failedCmHandles) { final Map<YangModelCmHandle, CmHandleState> cmHandleStatePerCmHandle = new HashMap<>(failedCmHandles.size()); for (final YangModelCmHandle failedCmHandle : failedCmHandles) { final CompositeState compositeState = failedCmHandle.getCompositeState(); - final boolean isReadyForRetry = moduleOperationsUtils.needsModuleSyncRetryOrUpgrade(compositeState); - log.info("Retry for cmHandleId : {} is {}", failedCmHandle.getId(), isReadyForRetry); - if (isReadyForRetry) { - final String resetCmHandleId = failedCmHandle.getId(); - log.debug("Reset cm handle {} state to ADVISED to be re-attempted by module-sync watchdog", - resetCmHandleId); - cmHandleStatePerCmHandle.put(failedCmHandle, CmHandleState.ADVISED); - removeResetCmHandleFromModuleSyncMap(resetCmHandleId); - } + final String resetCmHandleId = failedCmHandle.getId(); + log.debug("Resetting CM handle {} state to ADVISED for retry by the module-sync watchdog. Lock reason: {}", + failedCmHandle.getId(), compositeState.getLockReason().getLockReasonCategory().name()); + cmHandleStatePerCmHandle.put(failedCmHandle, CmHandleState.ADVISED); + removeResetCmHandleFromModuleSyncMap(resetCmHandleId); } lcmEventsCmHandleStateHandler.updateCmHandleStateBatch(cmHandleStatePerCmHandle); } diff --git a/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/impl/inventory/sync/ModuleSyncWatchdog.java b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/impl/inventory/sync/ModuleSyncWatchdog.java index cc724e1f07..bc7d6cdf67 100644 --- a/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/impl/inventory/sync/ModuleSyncWatchdog.java +++ b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/impl/inventory/sync/ModuleSyncWatchdog.java @@ -82,7 +82,7 @@ public class ModuleSyncWatchdog { /** * Find any failed (locked) cm handles and change state back to 'ADVISED'. */ - @Scheduled(fixedDelayString = "${ncmp.timers.locked-modules-sync.sleep-time-ms:300000}") + @Scheduled(fixedDelayString = "${ncmp.timers.locked-modules-sync.sleep-time-ms:15000}") public void resetPreviouslyFailedCmHandles() { log.info("Processing module sync retry-watchdog waking up."); final Collection<YangModelCmHandle> failedCmHandles diff --git a/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/impl/inventory/sync/SynchronizationCacheConfig.java b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/impl/inventory/sync/SynchronizationCacheConfig.java index 76b33cc8d9..8ef98bc32a 100644 --- a/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/impl/inventory/sync/SynchronizationCacheConfig.java +++ b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/impl/inventory/sync/SynchronizationCacheConfig.java @@ -25,7 +25,7 @@ import com.hazelcast.config.QueueConfig; import com.hazelcast.map.IMap; import java.util.concurrent.BlockingQueue; import lombok.extern.slf4j.Slf4j; -import org.onap.cps.cache.HazelcastCacheConfig; +import org.onap.cps.ncmp.impl.cache.HazelcastCacheConfig; import org.onap.cps.spi.model.DataNode; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; diff --git a/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/impl/inventory/trustlevel/TrustLevelCacheConfig.java b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/impl/inventory/trustlevel/TrustLevelCacheConfig.java index b64d3a24b3..0e9eaf5090 100644 --- a/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/impl/inventory/trustlevel/TrustLevelCacheConfig.java +++ b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/impl/inventory/trustlevel/TrustLevelCacheConfig.java @@ -22,8 +22,8 @@ package org.onap.cps.ncmp.impl.inventory.trustlevel; import com.hazelcast.config.MapConfig; import java.util.Map; -import org.onap.cps.cache.HazelcastCacheConfig; import org.onap.cps.ncmp.api.inventory.models.TrustLevel; +import org.onap.cps.ncmp.impl.cache.HazelcastCacheConfig; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; diff --git a/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/impl/inventory/trustlevel/TrustLevelManager.java b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/impl/inventory/trustlevel/TrustLevelManager.java index aff0e19981..afe6ad5c1c 100644 --- a/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/impl/inventory/trustlevel/TrustLevelManager.java +++ b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/impl/inventory/trustlevel/TrustLevelManager.java @@ -138,8 +138,8 @@ public class TrustLevelManager { * @param cmHandleId cm handle id * @return TrustLevel effective trust level */ - public TrustLevel getEffectiveTrustLevel(final String dmiServiceName, final String cmHandleId) { - final TrustLevel dmiTrustLevel = trustLevelPerDmiPlugin.get(dmiServiceName); + public TrustLevel getEffectiveTrustLevel(final String cmHandleId) { + final TrustLevel dmiTrustLevel = TrustLevel.COMPLETE; // TODO: CPS-2375 final TrustLevel cmHandleTrustLevel = trustLevelPerCmHandle.get(cmHandleId); return dmiTrustLevel.getEffectiveTrustLevel(cmHandleTrustLevel); } diff --git a/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/config/PolicyExecutorHttpClientConfigSpec.groovy b/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/config/PolicyExecutorHttpClientConfigSpec.groovy index ca71c345c1..b988f9e171 100644 --- a/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/config/PolicyExecutorHttpClientConfigSpec.groovy +++ b/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/config/PolicyExecutorHttpClientConfigSpec.groovy @@ -41,8 +41,13 @@ class PolicyExecutorHttpClientConfigSpec extends Specification { assert maximumConnectionsTotal == 32 assert pendingAcquireMaxCount == 33 assert connectionTimeoutInSeconds == 34 - assert readTimeoutInSeconds == 35 assert writeTimeoutInSeconds == 36 } } + + def 'Increased read timeout.'() { + expect: 'Read timeout is 10 seconds more then configured to enable a separate timeout method in policy executor with the required timeout' + assert policyExecutorHttpClientConfig.allServices.readTimeoutInSeconds == 35 + 10 + + } } diff --git a/cps-service/src/test/groovy/org/onap/cps/cache/HazelcastCacheConfigSpec.groovy b/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/impl/cache/HazelcastCacheConfigSpec.groovy index 022cd74ea6..e7eb893b03 100644 --- a/cps-service/src/test/groovy/org/onap/cps/cache/HazelcastCacheConfigSpec.groovy +++ b/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/impl/cache/HazelcastCacheConfigSpec.groovy @@ -18,7 +18,7 @@ * ============LICENSE_END========================================================= */ -package org.onap.cps.cache +package org.onap.cps.ncmp.impl.cache import com.hazelcast.config.Config import com.hazelcast.config.RestEndpointGroup diff --git a/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/impl/data/policyexecutor/PolicyExecutorConfigurationSpec.groovy b/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/impl/data/policyexecutor/PolicyExecutorConfigurationSpec.groovy index c859bb0a09..960e6b32f3 100644 --- a/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/impl/data/policyexecutor/PolicyExecutorConfigurationSpec.groovy +++ b/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/impl/data/policyexecutor/PolicyExecutorConfigurationSpec.groovy @@ -39,7 +39,9 @@ class PolicyExecutorConfigurationSpec extends Specification { def 'Policy executor configuration properties.'() { expect: 'properties used from application.yml' assert objectUnderTest.enabled + assert objectUnderTest.defaultDecision == 'some default decision' assert objectUnderTest.serverAddress == 'http://localhost' assert objectUnderTest.serverPort == '8785' + assert objectUnderTest.readTimeoutInSeconds == 35 } } diff --git a/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/impl/data/policyexecutor/PolicyExecutorSpec.groovy b/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/impl/data/policyexecutor/PolicyExecutorSpec.groovy index 63a915ab64..46c0ddeb93 100644 --- a/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/impl/data/policyexecutor/PolicyExecutorSpec.groovy +++ b/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/impl/data/policyexecutor/PolicyExecutorSpec.groovy @@ -28,7 +28,6 @@ import com.fasterxml.jackson.databind.JsonNode import com.fasterxml.jackson.databind.ObjectMapper import org.onap.cps.ncmp.api.exceptions.NcmpException import org.onap.cps.ncmp.api.exceptions.PolicyExecutorException -import org.onap.cps.ncmp.api.exceptions.ServerNcmpException import org.onap.cps.ncmp.impl.inventory.models.YangModelCmHandle import org.slf4j.LoggerFactory import org.springframework.http.HttpStatus @@ -37,6 +36,8 @@ import org.springframework.web.reactive.function.client.WebClient import reactor.core.publisher.Mono import spock.lang.Specification +import java.util.concurrent.TimeoutException + import static org.onap.cps.ncmp.api.data.models.OperationType.CREATE import static org.onap.cps.ncmp.api.data.models.OperationType.DELETE import static org.onap.cps.ncmp.api.data.models.OperationType.PATCH @@ -69,52 +70,90 @@ class PolicyExecutorSpec extends Specification { ((Logger) LoggerFactory.getLogger(PolicyExecutor)).detachAndStopAllAppenders() } - def 'Permission check with allow response.'() { + def 'Permission check with "allow" decision.'() { given: 'allow response' mockResponse([decision:'allow'], HttpStatus.OK) when: 'permission is checked for an operation' objectUnderTest.checkPermission(new YangModelCmHandle(), operationType, 'my credentials','my resource',someValidJson) then: 'system logs the operation is allowed' - assert getLogEntry(2) == 'Policy Executor allows the operation' + assert getLogEntry(2) == 'Operation allowed.' and: 'no exception occurs' noExceptionThrown() where: 'all write operations are tested' operationType << [ CREATE, DELETE, PATCH, UPDATE ] } - def 'Permission check with other response (not allowed).'() { + def 'Permission check with "other" decision (not allowed).'() { given: 'other response' mockResponse([decision:'other', decisionId:123, message:'I dont like Mondays' ], HttpStatus.OK) when: 'permission is checked for an operation' objectUnderTest.checkPermission(new YangModelCmHandle(), PATCH, 'my credentials','my resource',someValidJson) then: 'Policy Executor exception is thrown' def thrownException = thrown(PolicyExecutorException) - assert thrownException.message == 'Policy Executor did not allow request. Decision #123 : other' + assert thrownException.message == 'Operation not allowed. Decision id 123 : other' assert thrownException.details == 'I dont like Mondays' } - def 'Permission check with non 2xx response.'() { - given: 'other response' + def 'Permission check with non-2xx response and "allow" default decision.'() { + given: 'other http response' mockResponse([], HttpStatus.I_AM_A_TEAPOT) + and: 'the configured default decision is "allow"' + objectUnderTest.defaultDecision = 'allow' when: 'permission is checked for an operation' objectUnderTest.checkPermission(new YangModelCmHandle(), PATCH, 'my credentials','my resource',someValidJson) - then: 'Server Ncmp exception is thrown' - def thrownException = thrown(ServerNcmpException) - assert thrownException.message == 'Policy Executor invocation failed' - assert thrownException.details == 'HTTP status code: 418' + then: 'No exception is thrown' + noExceptionThrown() + } + + def 'Permission check with non-2xx response and "other" default decision.'() { + given: 'other http response' + mockResponse([], HttpStatus.I_AM_A_TEAPOT) + and: 'the configured default decision is NOT "allow"' + objectUnderTest.defaultDecision = 'deny by default' + when: 'permission is checked for an operation' + objectUnderTest.checkPermission(new YangModelCmHandle(), PATCH, 'my credentials', 'my resource', someValidJson) + then: 'Policy Executor exception is thrown' + def thrownException = thrown(PolicyExecutorException) + assert thrownException.message == 'Operation not allowed. Decision id N/A : deny by default' + assert thrownException.details == 'Policy Executor returned HTTP Status code 418. Falling back to configured default decision: deny by default' } def 'Permission check with invalid response from Policy Executor.'() { given: 'invalid response from Policy executor' mockResponseSpec.toEntity(*_) >> invalidResponse when: 'permission is checked for an operation' - objectUnderTest.checkPermission(new YangModelCmHandle(), CREATE, 'my credentials','my resource',someValidJson) + objectUnderTest.checkPermission(new YangModelCmHandle(), CREATE, 'my credentials', 'my resource', someValidJson) then: 'system logs the expected message' assert getLogEntry(1) == expectedMessage where: 'following invalid responses are received' - invalidResponse || expectedMessage - Mono.empty() || 'No valid response from policy, ignored' - Mono.just(new ResponseEntity<>(null, HttpStatus.OK)) || 'No valid response body from policy, ignored' + invalidResponse || expectedMessage + Mono.empty() || 'No valid response from Policy Executor, ignored' + Mono.just(new ResponseEntity<>(null, HttpStatus.OK)) || 'No valid response body from Policy Executor, ignored' + } + + def 'Permission check with timeout exception.'() { + given: 'a timeout during the request' + def cause = new TimeoutException() + mockResponseSpec.toEntity(*_) >> { throw new RuntimeException(cause) } + and: 'the configured default decision is NOT "allow"' + objectUnderTest.defaultDecision = 'deny by default' + when: 'permission is checked for an operation' + objectUnderTest.checkPermission(new YangModelCmHandle(), CREATE, 'my credentials', 'my resource', someValidJson) + then: 'Policy Executor exception is thrown' + def thrownException = thrown(PolicyExecutorException) + assert thrownException.message == 'Operation not allowed. Decision id N/A : deny by default' + assert thrownException.details == 'Policy Executor request timed out. Falling back to configured default decision: deny by default' + } + + def 'Permission check with other runtime exception.'() { + given: 'some other runtime exception' + def originalException = new RuntimeException() + mockResponseSpec.toEntity(*_) >> { throw originalException} + when: 'permission is checked for an operation' + objectUnderTest.checkPermission(new YangModelCmHandle(), CREATE, 'my credentials', 'my resource', someValidJson) + then: 'The original exception is thrown' + def thrownException = thrown(RuntimeException) + assert thrownException == originalException } def 'Permission check with an invalid change request json.'() { diff --git a/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/impl/inventory/CmHandleQueryServiceImplSpec.groovy b/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/impl/inventory/CmHandleQueryServiceImplSpec.groovy index cb3c4ffec1..7e34fe2822 100644 --- a/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/impl/inventory/CmHandleQueryServiceImplSpec.groovy +++ b/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/impl/inventory/CmHandleQueryServiceImplSpec.groovy @@ -23,10 +23,10 @@ package org.onap.cps.ncmp.impl.inventory import org.onap.cps.api.CpsDataService import org.onap.cps.api.CpsQueryService +import org.onap.cps.impl.utils.CpsValidator import org.onap.cps.ncmp.api.inventory.models.TrustLevel import org.onap.cps.ncmp.impl.inventory.models.CmHandleState import org.onap.cps.spi.model.DataNode -import org.onap.cps.spi.utils.CpsValidator import spock.lang.Shared import spock.lang.Specification diff --git a/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/impl/inventory/InventoryPersistenceImplSpec.groovy b/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/impl/inventory/InventoryPersistenceImplSpec.groovy index fdf12a880d..1830f1331d 100644 --- a/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/impl/inventory/InventoryPersistenceImplSpec.groovy +++ b/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/impl/inventory/InventoryPersistenceImplSpec.groovy @@ -26,6 +26,7 @@ import com.fasterxml.jackson.databind.ObjectMapper import org.onap.cps.api.CpsAnchorService import org.onap.cps.api.CpsDataService import org.onap.cps.api.CpsModuleService +import org.onap.cps.impl.utils.CpsValidator import org.onap.cps.ncmp.api.inventory.models.CompositeState import org.onap.cps.ncmp.impl.inventory.models.CmHandleState import org.onap.cps.ncmp.impl.inventory.models.YangModelCmHandle @@ -35,7 +36,6 @@ import org.onap.cps.spi.exceptions.DataNodeNotFoundException import org.onap.cps.spi.model.DataNode import org.onap.cps.spi.model.ModuleDefinition import org.onap.cps.spi.model.ModuleReference -import org.onap.cps.spi.utils.CpsValidator import org.onap.cps.utils.ContentType import org.onap.cps.utils.JsonObjectMapper import spock.lang.Shared diff --git a/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/impl/inventory/NetworkCmProxyInventoryFacadeSpec.groovy b/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/impl/inventory/NetworkCmProxyInventoryFacadeSpec.groovy index 9e07de48bf..fec07556eb 100644 --- a/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/impl/inventory/NetworkCmProxyInventoryFacadeSpec.groovy +++ b/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/impl/inventory/NetworkCmProxyInventoryFacadeSpec.groovy @@ -244,8 +244,7 @@ class NetworkCmProxyInventoryFacadeSpec extends Specification { and: 'query cm handle method returns two cm handles' mockParameterizedCmHandleQueryService.queryCmHandles( spiedJsonObjectMapper.convertToValueType(cmHandleQueryApiParameters, CmHandleQueryServiceParameters.class)) - >> [new YangModelCmHandle(id: 'ch-0', dmiProperties: [], publicProperties: []), - new YangModelCmHandle(id: 'ch-1', dmiProperties: [], publicProperties: [])] + >> [new NcmpServiceCmHandle(cmHandleId: 'ch-0'), new NcmpServiceCmHandle(cmHandleId: 'ch-1')] and: 'a trust level for cm handles' mockTrustLevelManager.getEffectiveTrustLevel(*_) >> TrustLevel.COMPLETE when: 'execute cm handle search is called' diff --git a/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/impl/inventory/ParameterizedCmHandleQueryServiceSpec.groovy b/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/impl/inventory/ParameterizedCmHandleQueryServiceSpec.groovy index 08644202c6..013bace04d 100644 --- a/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/impl/inventory/ParameterizedCmHandleQueryServiceSpec.groovy +++ b/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/impl/inventory/ParameterizedCmHandleQueryServiceSpec.groovy @@ -1,6 +1,6 @@ /* * ============LICENSE_START======================================================= - * Copyright (C) 2022-2024 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. @@ -22,6 +22,7 @@ package org.onap.cps.ncmp.impl.inventory import org.onap.cps.cpspath.parser.PathParsingException import org.onap.cps.ncmp.api.inventory.models.CmHandleQueryServiceParameters +import org.onap.cps.ncmp.api.inventory.models.NcmpServiceCmHandle import org.onap.cps.ncmp.impl.inventory.models.YangModelCmHandle import org.onap.cps.spi.FetchDescendantsOption import org.onap.cps.spi.exceptions.DataInUseException @@ -138,10 +139,10 @@ class ParameterizedCmHandleQueryServiceSpec extends Specification { and: 'the inventory service is called with teh correct if and returns a yang model cm handle' 1 * mockInventoryPersistence.getYangModelCmHandles(['ch1']) >> [new YangModelCmHandle(id: 'abc', dmiProperties: [new YangModelCmHandle.Property('name','value')], publicProperties: [])] - and: 'the expected cm handle(s) are returned as Yang Model cm handles' - assert result[0] instanceof YangModelCmHandle + and: 'the expected cm handle(s) are returned as NCMP Service cm handles' + assert result[0] instanceof NcmpServiceCmHandle assert result.size() == 1 - assert result[0].dmiProperties.size() == 1 + assert result[0].dmiProperties == [name:'value'] } def 'Query cm handle ids when the query is empty.'() { @@ -164,7 +165,7 @@ class ParameterizedCmHandleQueryServiceSpec extends Specification { def result = objectUnderTest.queryCmHandles(cmHandleQueryParameters) then: 'the correct cm handles are returned' assert result.size() == 4 - assert result.id.containsAll('PNFDemo1', 'PNFDemo2', 'PNFDemo3', 'PNFDemo4') + assert result.cmHandleId.containsAll('PNFDemo1', 'PNFDemo2', 'PNFDemo3', 'PNFDemo4') } def 'Query CMHandleId with #scenario.' () { diff --git a/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/impl/inventory/sync/ModuleOperationsUtilsSpec.groovy b/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/impl/inventory/sync/ModuleOperationsUtilsSpec.groovy index babe8101dd..65b7ff6d6f 100644 --- a/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/impl/inventory/sync/ModuleOperationsUtilsSpec.groovy +++ b/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/impl/inventory/sync/ModuleOperationsUtilsSpec.groovy @@ -40,12 +40,8 @@ import org.springframework.context.annotation.AnnotationConfigApplicationContext import org.springframework.http.HttpStatus import org.springframework.http.ResponseEntity import spock.lang.Specification - -import java.time.OffsetDateTime -import java.time.format.DateTimeFormatter import java.util.stream.Collectors -import static org.onap.cps.ncmp.impl.inventory.models.LockReasonCategory.LOCKED_MISBEHAVING import static org.onap.cps.ncmp.impl.inventory.models.LockReasonCategory.MODULE_SYNC_FAILED import static org.onap.cps.ncmp.impl.inventory.models.LockReasonCategory.MODULE_UPGRADE import static org.onap.cps.ncmp.impl.inventory.models.LockReasonCategory.MODULE_UPGRADE_FAILED @@ -60,17 +56,12 @@ class ModuleOperationsUtilsSpec extends Specification{ def objectUnderTest = new ModuleOperationsUtils(mockCmHandleQueries, mockDmiDataOperations, jsonObjectMapper) - def static neverUpdatedBefore = '1900-01-01T00:00:00.000+0100' - - def static now = OffsetDateTime.now() - - def static nowAsString = DateTimeFormatter.ofPattern("yyyy-MM-dd'T'HH:mm:ss.SSSZ").format(now) - def static dataNode = new DataNode(leaves: ['id': 'cm-handle-123']) def applicationContext = new AnnotationConfigApplicationContext() def logger = (Logger) LoggerFactory.getLogger(ModuleOperationsUtils) + def loggingListAppender void setup() { @@ -103,7 +94,7 @@ class ModuleOperationsUtilsSpec extends Specification{ given: 'A locked state' def compositeState = new CompositeState(lockReason: lockReason) when: 'update cm handle details and attempts is called' - objectUnderTest.updateLockReasonDetailsAndAttempts(compositeState, MODULE_SYNC_FAILED, 'new error message') + objectUnderTest.updateLockReasonWithAttempts(compositeState, MODULE_SYNC_FAILED, 'new error message') then: 'the composite state lock reason and details are updated' assert compositeState.lockReason.lockReasonCategory == MODULE_SYNC_FAILED assert compositeState.lockReason.details.contains(expectedDetails) @@ -118,14 +109,14 @@ class ModuleOperationsUtilsSpec extends Specification{ def compositeState = new CompositeStateBuilder().withCmHandleState(CmHandleState.LOCKED) .withLockReason(MODULE_UPGRADE, "Upgrade to ModuleSetTag: " + moduleSetTag).build() when: 'update cm handle details' - objectUnderTest.updateLockReasonDetailsAndAttempts(compositeState, MODULE_UPGRADE_FAILED, 'new error message') + objectUnderTest.updateLockReasonWithAttempts(compositeState, MODULE_UPGRADE_FAILED, 'new error message') then: 'the composite state lock reason and details are updated' assert compositeState.lockReason.lockReasonCategory == MODULE_UPGRADE_FAILED - assert compositeState.lockReason.details.contains("Upgrade to ModuleSetTag: " + expectedDetails) + assert compositeState.lockReason.details.contains(expectedDetails) where: scenario | moduleSetTag || expectedDetails - 'a module set tag' | 'someModuleSetTag' || 'someModuleSetTag' - 'empty module set tag' | '' || '' + 'a module set tag' | 'someModuleSetTag' || 'Upgrade to ModuleSetTag: someModuleSetTag' + 'empty module set tag' | '' || 'Attempt' } def 'Get all locked cm-Handles where lock reasons are model sync failed or upgrade'() { @@ -135,49 +126,9 @@ class ModuleOperationsUtilsSpec extends Specification{ when: 'get locked Misbehaving cm handle is called' def result = objectUnderTest.getCmHandlesThatFailedModelSyncOrUpgrade() then: 'the returned cm handle collection is the correct size' - result.size() == 1 + assert result.size() == 1 and: 'the correct cm handle is returned' - result[0].id == 'cm-handle-123' - } - - def 'Retry Locked Cm-Handle where the last update time is #scenario'() { - given: 'Last update was #lastUpdateMinutesAgo minutes ago (-1 means never)' - def lastUpdatedTime = DateTimeFormatter.ofPattern("yyyy-MM-dd'T'HH:mm:ss.SSSZ").format(now.minusMinutes(lastUpdateMinutesAgo)) - if (lastUpdateMinutesAgo < 0 ) { - lastUpdatedTime = neverUpdatedBefore - } - when: 'checking to see if cm handle is ready for retry' - def result = objectUnderTest.needsModuleSyncRetryOrUpgrade(new CompositeStateBuilder() - .withLockReason(MODULE_SYNC_FAILED, lockDetails) - .withLastUpdatedTime(lastUpdatedTime).build()) - then: 'retry is only attempted when expected' - assert result == retryExpected - and: 'logs contain related information' - def logs = loggingListAppender.list.toString() - assert logs.contains(logReason) - where: 'the following parameters are used' - scenario | lastUpdateMinutesAgo | lockDetails | logReason || retryExpected - 'never attempted before' | -1 | 'Fist attempt:' | 'First Attempt:' || true - '1st attempt, last attempt > 2 minute ago' | 3 | 'Attempt #1 failed: some error' | 'Retry due now' || true - '2nd attempt, last attempt < 4 minutes ago' | 1 | 'Attempt #2 failed: some error' | 'Time until next attempt is 3 minutes:' || false - '2nd attempt, last attempt > 4 minutes ago' | 5 | 'Attempt #2 failed: some error' | 'Retry due now' || true - } - - def 'Retry Locked Cm-Handle with lock reasons (category) #lockReasonCategory'() { - when: 'checking to see if cm handle is ready for retry' - def result = objectUnderTest.needsModuleSyncRetryOrUpgrade(new CompositeStateBuilder() - .withLockReason(lockReasonCategory, 'some details') - .withLastUpdatedTime(nowAsString).build()) - then: 'verify retry attempts' - assert !result - and: 'logs contain related information' - def logs = loggingListAppender.list.toString() - assert logs.contains(logReason) - where: 'the following lock reasons occurred' - scenario | lockReasonCategory || logReason - 'module upgrade' | MODULE_UPGRADE_FAILED || 'First Attempt:' - 'module sync failed' | MODULE_SYNC_FAILED || 'First Attempt:' - 'lock misbehaving' | LOCKED_MISBEHAVING || 'Locked for other reason' + assert result[0].id == 'cm-handle-123' } def 'Get a Cm-Handle where #scenario'() { @@ -197,19 +148,25 @@ class ModuleOperationsUtilsSpec extends Specification{ 'all Cm-Handle synchronized' | [] | false || 0 | [] as Set } - def 'Get resource data through DMI Operations #scenario'() { - given: 'the inventory persistence service returns a collection of data nodes' + def 'Retrieve resource data from DMI operations for #scenario'() { + given: 'a JSON string representing the resource data' def jsonString = '{"stores:bookstore":{"categories":[{"code":"01"}]}}' - JsonNode jsonNode = jsonObjectMapper.convertToJsonNode(jsonString); - def responseEntity = new ResponseEntity<>(jsonNode, HttpStatus.OK) + JsonNode jsonNode = jsonObjectMapper.convertToJsonNode(jsonString) + and: 'DMI operations are mocked to return a response based on the scenario' + def responseEntity = new ResponseEntity<>(statusCode == HttpStatus.OK ? jsonNode : null, statusCode) mockDmiDataOperations.getAllResourceDataFromDmi('cm-handle-123', _) >> responseEntity when: 'get resource data is called' def result = objectUnderTest.getResourceData('cm-handle-123') - then: 'the returned data is correct' - result == jsonString + then: 'the returned data matches the expected result' + assert result == expectedResult + where: + scenario | statusCode | expectedResult + 'successful response' | HttpStatus.OK | '{"stores:bookstore":{"categories":[{"code":"01"}]}}' + 'response with not found status' | HttpStatus.NOT_FOUND | null + 'response with internal server error' | HttpStatus.INTERNAL_SERVER_ERROR | null } - def 'Extract module set tag and number of attempt when lock reason contains #scenario'() { + def 'Extract module set tag and number of attempt when lock reason contains #scenario'() { expect: 'lock reason details are extracted correctly' def result = objectUnderTest.getLockedCompositeStateDetails(new CompositeStateBuilder().withLockReason(MODULE_UPGRADE, lockReasonDetails).build().lockReason) and: 'the result contains the correct moduleSetTag' diff --git a/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/impl/inventory/sync/ModuleSyncTasksSpec.groovy b/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/impl/inventory/sync/ModuleSyncTasksSpec.groovy index ee49f2f901..160744a7d7 100644 --- a/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/impl/inventory/sync/ModuleSyncTasksSpec.groovy +++ b/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/impl/inventory/sync/ModuleSyncTasksSpec.groovy @@ -32,16 +32,15 @@ import org.onap.cps.ncmp.api.inventory.models.CompositeState import org.onap.cps.ncmp.api.inventory.models.CompositeStateBuilder import org.onap.cps.ncmp.impl.inventory.InventoryPersistence import org.onap.cps.ncmp.impl.inventory.models.CmHandleState -import org.onap.cps.ncmp.impl.inventory.models.LockReasonCategory import org.onap.cps.ncmp.impl.inventory.models.YangModelCmHandle import org.onap.cps.ncmp.impl.inventory.sync.lcm.LcmEventsCmHandleStateHandler import org.onap.cps.spi.model.DataNode import org.slf4j.LoggerFactory import spock.lang.Specification - import java.util.concurrent.atomic.AtomicInteger import static org.onap.cps.ncmp.impl.inventory.models.LockReasonCategory.MODULE_SYNC_FAILED +import static org.onap.cps.ncmp.impl.inventory.models.LockReasonCategory.MODULE_UPGRADE import static org.onap.cps.ncmp.impl.inventory.models.LockReasonCategory.MODULE_UPGRADE_FAILED class ModuleSyncTasksSpec extends Specification { @@ -96,70 +95,52 @@ class ModuleSyncTasksSpec extends Specification { assert batchCount.get() == 4 } - def 'Module Sync ADVISED cm handle with failure during sync.'() { - given: 'a cm handle in an ADVISED state' - def cmHandle = cmHandleAsDataNodeByIdAndState('cm-handle', CmHandleState.ADVISED) - and: 'the inventory persistence cm handle returns a ADVISED state for the cm handle' - def cmHandleState = new CompositeState(cmHandleState: CmHandleState.ADVISED) - 1 * mockInventoryPersistence.getCmHandleState('cm-handle') >> cmHandleState - and: 'module sync service attempts to sync the cm handle and throws an exception' - 1 * mockModuleSyncService.syncAndCreateSchemaSetAndAnchor(*_) >> { throw new Exception('some exception') } + def 'Handle CM handle failure during #scenario and log MODULE_UPGRADE lock reason'() { + given: 'a CM handle in LOCKED state with a specific lock reason' + def cmHandle = cmHandleAsDataNodeByIdAndState('cm-handle', CmHandleState.LOCKED) + def expectedCmHandleState = new CompositeState(cmHandleState: CmHandleState.LOCKED, lockReason: CompositeState + .LockReason.builder().lockReasonCategory(lockReasonCategory).details(lockReasonDetails).build()) + 1 * mockInventoryPersistence.getCmHandleState('cm-handle') >> expectedCmHandleState + and: 'module sync service attempts to sync/upgrade the CM handle and throws an exception' + mockModuleSyncService.syncAndCreateSchemaSetAndAnchor(_) >> { throw new Exception('some exception') } + mockModuleSyncService.syncAndUpgradeSchemaSet(_) >> { throw new Exception('some exception') } when: 'module sync is executed' objectUnderTest.performModuleSync([cmHandle], batchCount) - then: 'update lock reason, details and attempts is invoked' - 1 * mockSyncUtils.updateLockReasonDetailsAndAttempts(cmHandleState, MODULE_SYNC_FAILED, 'some exception') + then: 'lock reason is updated with number of attempts' + 1 * mockSyncUtils.updateLockReasonWithAttempts(expectedCmHandleState, expectedLockReasonCategory, 'some exception') and: 'the state handler is called to update the state to LOCKED' 1 * mockLcmEventsCmHandleStateHandler.updateCmHandleStateBatch(_) >> { args -> assertBatch(args, ['cm-handle'], CmHandleState.LOCKED) } and: 'batch count is decremented by one' assert batchCount.get() == 4 - } - - def 'Failed cm handle during #scenario.'() { - given: 'a cm handle in LOCKED state' - def cmHandle = cmHandleAsDataNodeByIdAndState('cm-handle', CmHandleState.LOCKED) - and: 'the inventory persistence cm handle returns a LOCKED state with reason for the cm handle' - def expectedCmHandleState = new CompositeState(cmHandleState: cmHandleState, lockReason: CompositeState - .LockReason.builder().lockReasonCategory(lockReasonCategory).details(lockReasonDetails).build()) - 1 * mockInventoryPersistence.getCmHandleState('cm-handle') >> expectedCmHandleState - and: 'module sync service attempts to sync/upgrade the cm handle and throws an exception' - mockModuleSyncService.syncAndCreateSchemaSetAndAnchor(*_) >> { throw new Exception('some exception') } - mockModuleSyncService.syncAndUpgradeSchemaSet(*_) >> { throw new Exception('some exception') } - when: 'module sync is executed' - objectUnderTest.performModuleSync([cmHandle], batchCount) - then: 'update lock reason, details and attempts is invoked' - 1 * mockSyncUtils.updateLockReasonDetailsAndAttempts(expectedCmHandleState, expectedLockReasonCategory, 'some exception') where: - scenario | cmHandleState | lockReasonCategory | lockReasonDetails || expectedLockReasonCategory - 'module upgrade' | CmHandleState.LOCKED | MODULE_UPGRADE_FAILED | 'Upgrade to ModuleSetTag: some-module-set-tag' || MODULE_UPGRADE_FAILED - 'module sync' | CmHandleState.LOCKED | MODULE_SYNC_FAILED | 'some lock details' || MODULE_SYNC_FAILED + scenario | lockReasonCategory | lockReasonDetails || expectedLockReasonCategory + 'module sync' | MODULE_SYNC_FAILED | 'some lock details' || MODULE_SYNC_FAILED + 'module upgrade' | MODULE_UPGRADE_FAILED | 'Upgrade to ModuleSetTag: some-module-set-tag' || MODULE_UPGRADE_FAILED + 'module upgrade' | MODULE_UPGRADE | 'Upgrade in progress' || MODULE_UPGRADE_FAILED } + def 'Reset failed CM Handles #scenario.'() { given: 'cm handles in an locked state' def lockedState = new CompositeStateBuilder().withCmHandleState(CmHandleState.LOCKED) - .withLockReason(LockReasonCategory.MODULE_SYNC_FAILED, '').withLastUpdatedTimeNow().build() + .withLockReason(MODULE_SYNC_FAILED, '').withLastUpdatedTimeNow().build() def yangModelCmHandle1 = new YangModelCmHandle(id: 'cm-handle-1', compositeState: lockedState) def yangModelCmHandle2 = new YangModelCmHandle(id: 'cm-handle-2', compositeState: lockedState) - def expectedCmHandleStatePerCmHandle = [(yangModelCmHandle1): CmHandleState.ADVISED] + def expectedCmHandleStatePerCmHandle + = [(yangModelCmHandle1): CmHandleState.ADVISED, (yangModelCmHandle2): CmHandleState.ADVISED] and: 'clear in progress map' resetModuleSyncStartedOnCmHandles(moduleSyncStartedOnCmHandles) and: 'add cm handle entry into progress map' moduleSyncStartedOnCmHandles.put('cm-handle-1', 'started') moduleSyncStartedOnCmHandles.put('cm-handle-2', 'started') - and: 'sync utils retry locked cm handle returns #isReadyForRetry' - mockSyncUtils.needsModuleSyncRetryOrUpgrade(lockedState) >>> isReadyForRetry when: 'resetting failed cm handles' objectUnderTest.resetFailedCmHandles([yangModelCmHandle1, yangModelCmHandle2]) then: 'updated to state "ADVISED" from "READY" is called as often as there are cm handles ready for retry' - expectedNumberOfInvocationsToUpdateCmHandleState * mockLcmEventsCmHandleStateHandler.updateCmHandleStateBatch(expectedCmHandleStatePerCmHandle) - and: 'after reset performed size of in progress map' - assert moduleSyncStartedOnCmHandles.size() == inProgressMapSize - where: - scenario | isReadyForRetry | inProgressMapSize || expectedNumberOfInvocationsToUpdateCmHandleState - 'retry locked cm handle' | [true, false] | 1 || 1 - 'do not retry locked cm handle' | [false, false] | 2 || 0 + 1 * mockLcmEventsCmHandleStateHandler.updateCmHandleStateBatch(expectedCmHandleStatePerCmHandle) + and: 'after reset performed progress map is empty' + assert moduleSyncStartedOnCmHandles.size() == 0 } def 'Module Sync ADVISED cm handle without entry in progress map.'() { @@ -189,6 +170,24 @@ class ModuleSyncTasksSpec extends Specification { assert loggingEvent.formattedMessage == 'ch-1 removed from in progress map' } + def 'Sync and upgrade CM handle if in upgrade state for #scenario'() { + given: 'a CM handle in an upgrade state' + def cmHandle = cmHandleAsDataNodeByIdAndState('cm-handle', CmHandleState.LOCKED) + def compositeState = new CompositeState(lockReason: CompositeState.LockReason.builder().lockReasonCategory(lockReasonCategory).build()) + 1 * mockInventoryPersistence.getCmHandleState('cm-handle') >> compositeState + when: 'module sync is executed' + objectUnderTest.performModuleSync([cmHandle], batchCount) + then: 'the module sync service should attempt to sync and upgrade the CM handle' + 1 * mockModuleSyncService.syncAndUpgradeSchemaSet(_) >> { args -> + assert args[0].id == 'cm-handle' + } + where: 'the following lock reasons are used' + scenario | lockReasonCategory + 'module upgrade' | MODULE_UPGRADE + 'module upgrade failed' | MODULE_UPGRADE_FAILED + } + + def 'Remove non-existing cm handle id from hazelcast map'() { given: 'hazelcast map does not contains cm handle id' def result = moduleSyncStartedOnCmHandles.get('non-existing-cm-handle') diff --git a/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/impl/inventory/trustlevel/TrustLevelManagerSpec.groovy b/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/impl/inventory/trustlevel/TrustLevelManagerSpec.groovy index 7dc9602e46..fe762f891a 100644 --- a/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/impl/inventory/trustlevel/TrustLevelManagerSpec.groovy +++ b/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/impl/inventory/trustlevel/TrustLevelManagerSpec.groovy @@ -25,6 +25,7 @@ import org.onap.cps.ncmp.api.inventory.models.TrustLevel import org.onap.cps.ncmp.impl.inventory.InventoryPersistence import org.onap.cps.ncmp.impl.inventory.models.YangModelCmHandle import org.onap.cps.ncmp.utils.events.CmAvcEventPublisher +import spock.lang.Ignore import spock.lang.Specification class TrustLevelManagerSpec extends Specification { @@ -135,13 +136,15 @@ class TrustLevelManagerSpec extends Specification { 0 * mockAttributeValueChangeEventPublisher.publishAvcEvent(*_) } + @Ignore + // TODO: CPS-2375 def 'Select effective trust level among CmHandle and dmi plugin'() { given: 'a non trusted dmi' trustLevelPerDmiPlugin.put('my-dmi', TrustLevel.NONE) and: 'a trusted CmHandle' trustLevelPerCmHandle.put('ch-1', TrustLevel.COMPLETE) when: 'effective trust level selected' - def effectiveTrustLevel = objectUnderTest.getEffectiveTrustLevel('my-dmi', 'ch-1') + def effectiveTrustLevel = objectUnderTest.getEffectiveTrustLevel('ch-1') then: 'effective trust level is trusted' assert effectiveTrustLevel == TrustLevel.NONE } diff --git a/cps-ncmp-service/src/test/resources/application.yml b/cps-ncmp-service/src/test/resources/application.yml index c76831da74..df3375d5d0 100644 --- a/cps-ncmp-service/src/test/resources/application.yml +++ b/cps-ncmp-service/src/test/resources/application.yml @@ -83,6 +83,7 @@ ncmp: policy-executor: enabled: true + defaultDecision: "some default decision" server: address: http://localhost port: 8785 diff --git a/cps-path-parser/src/main/antlr4/org/onap/cps/cpspath/parser/antlr4/CpsPath.g4 b/cps-path-parser/src/main/antlr4/org/onap/cps/cpspath/parser/antlr4/CpsPath.g4 index be8d968fcb..444702db48 100644 --- a/cps-path-parser/src/main/antlr4/org/onap/cps/cpspath/parser/antlr4/CpsPath.g4 +++ b/cps-path-parser/src/main/antlr4/org/onap/cps/cpspath/parser/antlr4/CpsPath.g4 @@ -29,19 +29,21 @@ grammar CpsPath ; cpsPath : ( prefix | descendant ) multipleLeafConditions? textFunctionCondition? containsFunctionCondition? ancestorAxis? EOF ; -ancestorAxis : SLASH KW_ANCESTOR COLONCOLON ancestorPath ; +slash : SLASH ; -ancestorPath : yangElement ( SLASH yangElement)* ; +ancestorAxis : KW_ANCESTOR_AXIS_PREFIX ancestorPath ; -textFunctionCondition : SLASH leafName OB KW_TEXT_FUNCTION EQ StringLiteral CB ; +ancestorPath : yangElement ( slash yangElement)* ; + +textFunctionCondition : slash leafName OB KW_TEXT_FUNCTION EQ StringLiteral CB ; containsFunctionCondition : OB KW_CONTAINS_FUNCTION OP AT leafName COMMA StringLiteral CP CB ; -parent : ( SLASH yangElement)* ; +parent : ( slash yangElement)* ; -prefix : parent SLASH containerName ; +prefix : parent slash containerName ; -descendant : SLASH prefix ; +descendant : slash prefix ; yangElement : containerName listElementRef? ; @@ -85,7 +87,8 @@ KW_ANCESTOR : 'ancestor' ; KW_AND : 'and' ; KW_TEXT_FUNCTION: 'text()' ; KW_OR : 'or' ; -KW_CONTAINS_FUNCTION: 'contains' ; +KW_CONTAINS_FUNCTION : 'contains' ; +KW_ANCESTOR_AXIS_PREFIX : SLASH KW_ANCESTOR COLONCOLON ; IntegerLiteral : FragDigits ; // Add below type definitions for leafvalue comparision in https://jira.onap.org/browse/CPS-440 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 ac535f5efc..ed7dbecc18 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 @@ -36,6 +36,8 @@ import org.onap.cps.cpspath.parser.antlr4.CpsPathParser.TextFunctionConditionCon public class CpsPathBuilder extends CpsPathBaseListener { + private static final String NO_PARENT_PATH = ""; + private static final String OPEN_BRACKET = "["; private static final String CLOSE_BRACKET = "]"; @@ -46,7 +48,7 @@ public class CpsPathBuilder extends CpsPathBaseListener { private final StringBuilder normalizedXpathBuilder = new StringBuilder(); - private final StringBuilder normalizedAncestorPathBuilder = new StringBuilder(); + private int startIndexOfAncestorSchemaNodeIdentifier = 0; private boolean processingAncestorAxis = false; @@ -55,13 +57,24 @@ public class CpsPathBuilder extends CpsPathBaseListener { private final List<String> booleanOperators = new ArrayList<>(); @Override + public void exitSlash(final CpsPathParser.SlashContext ctx) { + normalizedXpathBuilder.append("/"); + } + + @Override public void exitPrefix(final PrefixContext ctx) { cpsPathQuery.setXpathPrefix(normalizedXpathBuilder.toString()); } @Override public void exitParent(final CpsPathParser.ParentContext ctx) { - cpsPathQuery.setNormalizedParentPath(normalizedXpathBuilder.toString()); + final String normalizedParentPath; + if (normalizedXpathBuilder.toString().equals("/")) { + normalizedParentPath = NO_PARENT_PATH; + } else { + normalizedParentPath = normalizedXpathBuilder.toString(); + } + cpsPathQuery.setNormalizedParentPath(normalizedParentPath); } @Override @@ -87,8 +100,7 @@ public class CpsPathBuilder extends CpsPathBaseListener { @Override public void exitDescendant(final DescendantContext ctx) { cpsPathQuery.setCpsPathPrefixType(DESCENDANT); - cpsPathQuery.setDescendantName(normalizedXpathBuilder.substring(1)); - normalizedXpathBuilder.insert(0, "/"); + cpsPathQuery.setDescendantName(normalizedXpathBuilder.substring(2)); } @Override @@ -107,12 +119,15 @@ public class CpsPathBuilder extends CpsPathBaseListener { @Override public void enterAncestorAxis(final AncestorAxisContext ctx) { processingAncestorAxis = true; + normalizedXpathBuilder.append("/ancestor::"); + startIndexOfAncestorSchemaNodeIdentifier = normalizedXpathBuilder.length(); } @Override public void exitAncestorAxis(final AncestorAxisContext ctx) { - cpsPathQuery.setAncestorSchemaNodeIdentifier(normalizedAncestorPathBuilder.substring(1)); processingAncestorAxis = false; + cpsPathQuery.setAncestorSchemaNodeIdentifier( + normalizedXpathBuilder.substring(startIndexOfAncestorSchemaNodeIdentifier)); } @Override @@ -130,17 +145,11 @@ public class CpsPathBuilder extends CpsPathBaseListener { @Override public void enterListElementRef(final CpsPathParser.ListElementRefContext ctx) { normalizedXpathBuilder.append(OPEN_BRACKET); - if (processingAncestorAxis) { - normalizedAncestorPathBuilder.append(OPEN_BRACKET); - } } @Override public void exitListElementRef(final CpsPathParser.ListElementRefContext ctx) { normalizedXpathBuilder.append(CLOSE_BRACKET); - if (processingAncestorAxis) { - normalizedAncestorPathBuilder.append(CLOSE_BRACKET); - } } CpsPathQuery build() { @@ -153,20 +162,15 @@ public class CpsPathBuilder extends CpsPathBaseListener { @Override public void exitContainerName(final CpsPathParser.ContainerNameContext ctx) { final String containerName = ctx.getText(); - normalizedXpathBuilder.append("/") - .append(containerName); - containerNames.add(containerName); - if (processingAncestorAxis) { - normalizedAncestorPathBuilder.append("/").append(containerName); + normalizedXpathBuilder.append(containerName); + if (!processingAncestorAxis) { + containerNames.add(containerName); } } private void leafContext(final String leafName, final String operator, final Object comparisonValue) { leafConditions.add(new CpsPathQuery.LeafCondition(leafName, operator, comparisonValue)); appendCondition(normalizedXpathBuilder, leafName, operator, comparisonValue); - if (processingAncestorAxis) { - appendCondition(normalizedAncestorPathBuilder, leafName, operator, comparisonValue); - } } private void appendCondition(final StringBuilder currentNormalizedPathBuilder, final String name, 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 d0df3c745a..16430d2fa5 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 @@ -91,13 +91,14 @@ class CpsPathQuerySpec extends Specification { 'descendant with leaf condition has "<" operator' | '//cps-path[@key<10]' || "//cps-path[@key<'10']" 'descendant with leaf condition has ">=" operator' | '//cps-path[@key>=8]' || "//cps-path[@key>='8']" 'descendant with leaf condition has "<=" operator' | '//cps-path[@key<=12]' || "//cps-path[@key<='12']" - 'descendant with leaf value and ancestor' | '//cps-path[@key=1]/ancestor:parent[@key=1]' || "//cps-path[@key='1']/ancestor:parent[@key='1']" + 'descendant with leaf value and ancestor' | '//cps-path[@key=1]/ancestor::parent[@key=1]' || "//cps-path[@key='1']/ancestor::parent[@key='1']" 'parent & child' | '/parent/child' || '/parent/child' 'parent leaf of type Integer & child' | '/parent/child[@code=1]/child2' || "/parent/child[@code='1']/child2" 'parent leaf with double quotes' | '/parent/child[@code="1"]/child2' || "/parent/child[@code='1']/child2" 'parent leaf with double quotes inside single quotes' | '/parent/child[@code=\'"1"\']/child2' || "/parent/child[@code='\"1\"']/child2" 'parent leaf with single quotes inside double quotes' | '/parent/child[@code="\'1\'"]/child2' || "/parent/child[@code='''1''']/child2" 'leaf with single quotes inside double quotes' | '/parent/child[@code="\'1\'"]' || "/parent/child[@code='''1''']" + 'leaf with single quotes inside single quotes' | "/parent/child[@code='I''m quoted']" || "/parent/child[@code='I''m quoted']" 'leaf with more than one attribute' | '/parent/child[@key1=1 and @key2="abc"]' || "/parent/child[@key1='1' and @key2='abc']" 'parent & child with more than one attribute' | '/parent/child[@key1=1 and @key2="abc"]/child2' || "/parent/child[@key1='1' and @key2='abc']/child2" 'leaf with more than one attribute has OR operator' | '/parent/child[@key1=1 or @key2="abc"]' || "/parent/child[@key1='1' or @key2='abc']" diff --git a/cps-path-parser/src/test/groovy/org/onap/cps/cpspath/parser/CpsPathUtilSpec.groovy b/cps-path-parser/src/test/groovy/org/onap/cps/cpspath/parser/CpsPathUtilSpec.groovy index d62f337b75..29bb3c7b58 100644 --- a/cps-path-parser/src/test/groovy/org/onap/cps/cpspath/parser/CpsPathUtilSpec.groovy +++ b/cps-path-parser/src/test/groovy/org/onap/cps/cpspath/parser/CpsPathUtilSpec.groovy @@ -1,6 +1,6 @@ /* * ============LICENSE_START======================================================= - * Copyright (C) 2022 Nordix Foundation + * Copyright (C) 2022-2024 Nordix Foundation * ================================================================================ * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -36,21 +36,40 @@ class CpsPathUtilSpec extends Specification { 'single quotes' | "/parent/child[@common-leaf-name='123']" } - def 'Normalized parent xpaths'() { - when: 'a given xpath with #scenario is parsed' - def result = CpsPathUtil.getNormalizedParentXpath(xpath) + def 'Normalized parent paths of absolute paths'() { + when: 'a given cps path is parsed' + def result = CpsPathUtil.getNormalizedParentXpath(cpsPath) then: 'the result is the expected parent path' assert result == expectedParentPath - where: 'the following xpaths are used' - scenario | xpath || expectedParentPath - 'no child' | '/parent' || '' - 'child and parent' | '/parent/child' || '/parent' - 'grand child' | '/parent/child/grandChild' || '/parent/child' - 'parent & top is list element' | '/parent[@id=1]/child' || "/parent[@id='1']" - 'parent is list element' | '/parent/child[@id=1]/grandChild' || "/parent/child[@id='1']" - 'parent is list element with /' | "/parent/child[@id='a/b']/grandChild" || "/parent/child[@id='a/b']" - 'parent is list element with [' | "/parent/child[@id='a[b']/grandChild" || "/parent/child[@id='a[b']" - 'parent is list element using "' | '/parent/child[@id="x"]/grandChild' || "/parent/child[@id='x']" + where: 'the following absolute cps paths are used' + cpsPath || expectedParentPath + '/parent' || '' + '/parent/child' || '/parent' + '/parent/child/grandChild' || '/parent/child' + '/parent[@id=1]/child' || "/parent[@id='1']" + '/parent/child[@id=1]/grandChild' || "/parent/child[@id='1']" + '/parent/child/grandChild[@id="x"]' || "/parent/child" + '/parent/ancestor::grandparent' || '' + '/parent/child/ancestor::grandparent' || '/parent' + '/parent/child/name[text()="value"]' || '/parent' + } + + def 'Normalized parent paths of descendant paths'() { + when: 'a given cps path is parsed' + def result = CpsPathUtil.getNormalizedParentXpath(cpsPath) + then: 'the result is the expected parent path' + assert result == expectedParentPath + where: 'the following descendant cps paths are used' + cpsPath || expectedParentPath + '//parent' || '' + '//parent/child' || '//parent' + '//parent/child/grandChild' || '//parent/child' + '//parent[@id=1]/child' || "//parent[@id='1']" + '//parent/child[@id=1]/grandChild' || "//parent/child[@id='1']" + '//parent/child/grandChild[@id="x"]' || "//parent/child" + '//parent/ancestor::grandparent' || '' + '//parent/child/ancestor::grandparent' || '//parent' + '//parent/child/name[text()="value"]' || '//parent' } def 'Get node ID sequence for given xpath'() { @@ -67,17 +86,19 @@ class CpsPathUtilSpec extends Specification { 'parent is list element' | '/parent/child[@id=1]/grandChild' || ["parent","child","grandChild"] 'parent is list element with /' | "/parent/child[@id='a/b']/grandChild" || ["parent","child","grandChild"] 'parent is list element with [' | "/parent/child[@id='a[b']/grandChild" || ["parent","child","grandChild"] + 'does not include ancestor node' | '/parent/child/ancestor::grandparent' || ["parent","child"] } def 'Recognizing (absolute) xpaths to List elements'() { expect: 'check for list returns the correct values' assert CpsPathUtil.isPathToListElement(xpath) == expectList where: 'the following xpaths are used' - xpath || expectList - '/parent[@id=1]' || true - '/parent[@id=1]/child' || false - '/parent/child[@id=1]' || true - '//child[@id=1]' || false + xpath || expectList + '/parent[@id=1]' || true + '/parent[@id=1]/child' || false + '/parent/child[@id=1]' || true + '//child[@id=1]' || false + '/parent/ancestor::grandparent[@id=1]' || false } def 'Parsing Exception'() { diff --git a/cps-rest/src/main/java/org/onap/cps/rest/controller/DataRestController.java b/cps-rest/src/main/java/org/onap/cps/rest/controller/DataRestController.java index 6015e0e3ae..f86073fb06 100755 --- a/cps-rest/src/main/java/org/onap/cps/rest/controller/DataRestController.java +++ b/cps-rest/src/main/java/org/onap/cps/rest/controller/DataRestController.java @@ -2,7 +2,7 @@ * ============LICENSE_START======================================================= * Copyright (C) 2020-2022 Bell Canada. * Modifications Copyright (C) 2021 Pantheon.tech - * Modifications Copyright (C) 2021-2023 Nordix Foundation + * Modifications Copyright (C) 2021-2024 Nordix Foundation * Modifications Copyright (C) 2022-2024 TechMahindra Ltd. * Modifications Copyright (C) 2022 Deutsche Telekom AG * ================================================================================ @@ -37,9 +37,11 @@ import java.util.List; import java.util.Map; import lombok.RequiredArgsConstructor; import org.apache.commons.lang3.StringUtils; +import org.onap.cps.api.CpsAnchorService; import org.onap.cps.api.CpsDataService; import org.onap.cps.rest.api.CpsDataApi; import org.onap.cps.spi.FetchDescendantsOption; +import org.onap.cps.spi.model.Anchor; import org.onap.cps.spi.model.DataNode; import org.onap.cps.spi.model.DeltaReport; import org.onap.cps.utils.ContentType; @@ -63,6 +65,7 @@ public class DataRestController implements CpsDataApi { private static final DateTimeFormatter ISO_TIMESTAMP_FORMATTER = DateTimeFormatter.ofPattern(ISO_TIMESTAMP_FORMAT); private final CpsDataService cpsDataService; + private final CpsAnchorService cpsAnchorService; private final JsonObjectMapper jsonObjectMapper; private final PrefixResolver prefixResolver; @@ -112,7 +115,8 @@ public class DataRestController implements CpsDataApi { ? FetchDescendantsOption.INCLUDE_ALL_DESCENDANTS : FetchDescendantsOption.OMIT_DESCENDANTS; final DataNode dataNode = cpsDataService.getDataNodes(dataspaceName, anchorName, xpath, fetchDescendantsOption).iterator().next(); - final String prefix = prefixResolver.getPrefix(dataspaceName, anchorName, dataNode.getXpath()); + final Anchor anchor = cpsAnchorService.getAnchor(dataspaceName, anchorName); + final String prefix = prefixResolver.getPrefix(anchor, dataNode.getXpath()); return new ResponseEntity<>(DataMapUtils.toDataMapWithIdentifier(dataNode, prefix), HttpStatus.OK); } @@ -127,8 +131,9 @@ public class DataRestController implements CpsDataApi { final Collection<DataNode> dataNodes = cpsDataService.getDataNodes(dataspaceName, anchorName, xpath, fetchDescendantsOption); final List<Map<String, Object>> dataMaps = new ArrayList<>(dataNodes.size()); + final Anchor anchor = cpsAnchorService.getAnchor(dataspaceName, anchorName); for (final DataNode dataNode: dataNodes) { - final String prefix = prefixResolver.getPrefix(dataspaceName, anchorName, dataNode.getXpath()); + final String prefix = prefixResolver.getPrefix(anchor, dataNode.getXpath()); final Map<String, Object> dataMap = DataMapUtils.toDataMapWithIdentifier(dataNode, prefix); dataMaps.add(dataMap); } diff --git a/cps-rest/src/main/java/org/onap/cps/rest/controller/QueryRestController.java b/cps-rest/src/main/java/org/onap/cps/rest/controller/QueryRestController.java index 5334b48143..547be669ae 100644 --- a/cps-rest/src/main/java/org/onap/cps/rest/controller/QueryRestController.java +++ b/cps-rest/src/main/java/org/onap/cps/rest/controller/QueryRestController.java @@ -1,6 +1,6 @@ /* * ============LICENSE_START======================================================= - * Copyright (C) 2021-2022 Nordix Foundation + * Copyright (C) 2021-2024 Nordix Foundation * Modifications Copyright (C) 2022 Bell Canada. * Modifications Copyright (C) 2022-2023 TechMahindra Ltd. * ================================================================================ @@ -29,10 +29,12 @@ import java.util.HashMap; import java.util.List; import java.util.Map; import lombok.RequiredArgsConstructor; +import org.onap.cps.api.CpsAnchorService; import org.onap.cps.api.CpsQueryService; import org.onap.cps.rest.api.CpsQueryApi; import org.onap.cps.spi.FetchDescendantsOption; import org.onap.cps.spi.PaginationOption; +import org.onap.cps.spi.model.Anchor; import org.onap.cps.spi.model.DataNode; import org.onap.cps.utils.DataMapUtils; import org.onap.cps.utils.JsonObjectMapper; @@ -48,6 +50,7 @@ import org.springframework.web.bind.annotation.RestController; public class QueryRestController implements CpsQueryApi { private final CpsQueryService cpsQueryService; + private final CpsAnchorService cpsAnchorService; private final JsonObjectMapper jsonObjectMapper; private final PrefixResolver prefixResolver; @@ -87,14 +90,15 @@ public class QueryRestController implements CpsQueryApi { cpsPath, fetchDescendantsOption, paginationOption); final List<Map<String, Object>> dataNodesAsListOfMaps = new ArrayList<>(dataNodes.size()); String prefix = null; - final Map<String, List<DataNode>> anchorDataNodeListMap = prepareDataNodesForAnchor(dataNodes); - for (final Map.Entry<String, List<DataNode>> anchorDataNodesMapEntry : anchorDataNodeListMap.entrySet()) { + final Map<String, List<DataNode>> dataNodesPerAnchor = groupDataNodesPerAnchor(dataNodes); + for (final Map.Entry<String, List<DataNode>> dataNodesPerAnchorEntry : dataNodesPerAnchor.entrySet()) { + final String anchorName = dataNodesPerAnchorEntry.getKey(); if (prefix == null) { - prefix = prefixResolver.getPrefix(dataspaceName, anchorDataNodesMapEntry.getKey(), - anchorDataNodesMapEntry.getValue().get(0).getXpath()); + final Anchor anchor = cpsAnchorService.getAnchor(dataspaceName, anchorName); + prefix = prefixResolver.getPrefix(anchor, dataNodesPerAnchorEntry.getValue().get(0).getXpath()); } final Map<String, Object> dataMap = DataMapUtils.toDataMapWithIdentifierAndAnchor( - anchorDataNodesMapEntry.getValue(), anchorDataNodesMapEntry.getKey(), prefix); + dataNodesPerAnchorEntry.getValue(), anchorName, prefix); dataNodesAsListOfMaps.add(dataMap); } final Integer totalPages = getTotalPages(dataspaceName, cpsPath, paginationOption); @@ -112,7 +116,7 @@ public class QueryRestController implements CpsQueryApi { : (int) Math.ceil((double) totalAnchors / paginationOption.getPageSize()); } - private Map<String, List<DataNode>> prepareDataNodesForAnchor(final Collection<DataNode> dataNodes) { + private Map<String, List<DataNode>> groupDataNodesPerAnchor(final Collection<DataNode> dataNodes) { final Map<String, List<DataNode>> dataNodesMapForAnchor = new HashMap<>(); for (final DataNode dataNode : dataNodes) { List<DataNode> dataNodesInAnchor = dataNodesMapForAnchor.get(dataNode.getAnchorName()); @@ -130,10 +134,11 @@ public class QueryRestController implements CpsQueryApi { final Collection<DataNode> dataNodes = cpsQueryService.queryDataNodes(dataspaceName, anchorName, cpsPath, fetchDescendantsOption); final List<Map<String, Object>> dataNodesAsListOfMaps = new ArrayList<>(dataNodes.size()); + final Anchor anchor = cpsAnchorService.getAnchor(dataspaceName, anchorName); String prefix = null; for (final DataNode dataNode : dataNodes) { if (prefix == null) { - prefix = prefixResolver.getPrefix(dataspaceName, anchorName, dataNode.getXpath()); + prefix = prefixResolver.getPrefix(anchor, dataNode.getXpath()); } final Map<String, Object> dataMap = DataMapUtils.toDataMapWithIdentifier(dataNode, prefix); dataNodesAsListOfMaps.add(dataMap); diff --git a/cps-rest/src/test/groovy/org/onap/cps/rest/controller/DataRestControllerSpec.groovy b/cps-rest/src/test/groovy/org/onap/cps/rest/controller/DataRestControllerSpec.groovy index 5241f61bd9..e101ea6c41 100755 --- a/cps-rest/src/test/groovy/org/onap/cps/rest/controller/DataRestControllerSpec.groovy +++ b/cps-rest/src/test/groovy/org/onap/cps/rest/controller/DataRestControllerSpec.groovy @@ -1,6 +1,6 @@ /* * ============LICENSE_START======================================================= - * Copyright (C) 2021-2022 Nordix Foundation + * Copyright (C) 2021-2024 Nordix Foundation * Modifications Copyright (C) 2021 Pantheon.tech * Modifications Copyright (C) 2021-2022 Bell Canada. * Modifications Copyright (C) 2022 Deutsche Telekom AG @@ -26,6 +26,7 @@ package org.onap.cps.rest.controller import com.fasterxml.jackson.databind.ObjectMapper import groovy.json.JsonSlurper +import org.onap.cps.api.CpsAnchorService import org.onap.cps.api.CpsDataService import org.onap.cps.spi.FetchDescendantsOption import org.onap.cps.spi.model.DataNode @@ -63,6 +64,9 @@ class DataRestControllerSpec extends Specification { CpsDataService mockCpsDataService = Mock() @SpringBean + CpsAnchorService mockCpsAnchorService = Mock() + + @SpringBean JsonObjectMapper jsonObjectMapper = new JsonObjectMapper(new ObjectMapper()) @SpringBean diff --git a/cps-rest/src/test/groovy/org/onap/cps/rest/controller/QueryRestControllerSpec.groovy b/cps-rest/src/test/groovy/org/onap/cps/rest/controller/QueryRestControllerSpec.groovy index c30a63fd46..80b287cda8 100644 --- a/cps-rest/src/test/groovy/org/onap/cps/rest/controller/QueryRestControllerSpec.groovy +++ b/cps-rest/src/test/groovy/org/onap/cps/rest/controller/QueryRestControllerSpec.groovy @@ -1,6 +1,6 @@ /* * ============LICENSE_START======================================================= - * Copyright (C) 2021-2022 Nordix Foundation + * Copyright (C) 2021-2024 Nordix Foundation * Modifications Copyright (C) 2021-2022 Bell Canada. * Modifications Copyright (C) 2021 Pantheon.tech * Modifications Copyright (C) 2022-2023 TechMahindra Ltd. @@ -23,18 +23,13 @@ package org.onap.cps.rest.controller -import org.onap.cps.spi.PaginationOption -import org.onap.cps.utils.PrefixResolver - -import static org.onap.cps.spi.FetchDescendantsOption.DIRECT_CHILDREN_ONLY -import static org.onap.cps.spi.FetchDescendantsOption.INCLUDE_ALL_DESCENDANTS -import static org.onap.cps.spi.FetchDescendantsOption.OMIT_DESCENDANTS -import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get - import com.fasterxml.jackson.databind.ObjectMapper -import org.onap.cps.utils.JsonObjectMapper +import org.onap.cps.api.CpsAnchorService import org.onap.cps.api.CpsQueryService +import org.onap.cps.spi.PaginationOption import org.onap.cps.spi.model.DataNodeBuilder +import org.onap.cps.utils.JsonObjectMapper +import org.onap.cps.utils.PrefixResolver import org.spockframework.spring.SpringBean import org.springframework.beans.factory.annotation.Autowired import org.springframework.beans.factory.annotation.Value @@ -43,6 +38,11 @@ import org.springframework.http.HttpStatus import org.springframework.test.web.servlet.MockMvc import spock.lang.Specification +import static org.onap.cps.spi.FetchDescendantsOption.DIRECT_CHILDREN_ONLY +import static org.onap.cps.spi.FetchDescendantsOption.INCLUDE_ALL_DESCENDANTS +import static org.onap.cps.spi.FetchDescendantsOption.OMIT_DESCENDANTS +import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get + @WebMvcTest(QueryRestController) class QueryRestControllerSpec extends Specification { @@ -50,6 +50,9 @@ class QueryRestControllerSpec extends Specification { CpsQueryService mockCpsQueryService = Mock() @SpringBean + CpsAnchorService mockCpsAnchorService = Mock() + + @SpringBean JsonObjectMapper jsonObjectMapper = new JsonObjectMapper(new ObjectMapper()) @SpringBean diff --git a/cps-ri/src/main/java/org/onap/cps/spi/impl/CpsAdminPersistenceServiceImpl.java b/cps-ri/src/main/java/org/onap/cps/ri/CpsAdminPersistenceServiceImpl.java index 13710dbec0..b85b0f9b98 100755 --- a/cps-ri/src/main/java/org/onap/cps/spi/impl/CpsAdminPersistenceServiceImpl.java +++ b/cps-ri/src/main/java/org/onap/cps/ri/CpsAdminPersistenceServiceImpl.java @@ -21,24 +21,24 @@ * ============LICENSE_END========================================================= */ -package org.onap.cps.spi.impl; +package org.onap.cps.ri; import jakarta.transaction.Transactional; import java.util.Collection; import java.util.stream.Collectors; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; +import org.onap.cps.ri.models.AnchorEntity; +import org.onap.cps.ri.models.DataspaceEntity; +import org.onap.cps.ri.models.SchemaSetEntity; +import org.onap.cps.ri.repository.AnchorRepository; +import org.onap.cps.ri.repository.DataspaceRepository; +import org.onap.cps.ri.repository.SchemaSetRepository; import org.onap.cps.spi.CpsAdminPersistenceService; -import org.onap.cps.spi.entities.AnchorEntity; -import org.onap.cps.spi.entities.DataspaceEntity; -import org.onap.cps.spi.entities.SchemaSetEntity; import org.onap.cps.spi.exceptions.AlreadyDefinedException; import org.onap.cps.spi.exceptions.DataspaceInUseException; import org.onap.cps.spi.model.Anchor; import org.onap.cps.spi.model.Dataspace; -import org.onap.cps.spi.repository.AnchorRepository; -import org.onap.cps.spi.repository.DataspaceRepository; -import org.onap.cps.spi.repository.SchemaSetRepository; import org.springframework.dao.DataIntegrityViolationException; import org.springframework.stereotype.Component; diff --git a/cps-ri/src/main/java/org/onap/cps/spi/impl/CpsDataPersistenceServiceImpl.java b/cps-ri/src/main/java/org/onap/cps/ri/CpsDataPersistenceServiceImpl.java index fd47793a7a..ec46fea4cb 100644 --- a/cps-ri/src/main/java/org/onap/cps/spi/impl/CpsDataPersistenceServiceImpl.java +++ b/cps-ri/src/main/java/org/onap/cps/ri/CpsDataPersistenceServiceImpl.java @@ -21,7 +21,7 @@ * ============LICENSE_END========================================================= */ -package org.onap.cps.spi.impl; +package org.onap.cps.ri; import static org.onap.cps.spi.PaginationOption.NO_PAGINATION; @@ -48,12 +48,16 @@ import org.hibernate.StaleStateException; import org.onap.cps.cpspath.parser.CpsPathQuery; import org.onap.cps.cpspath.parser.CpsPathUtil; import org.onap.cps.cpspath.parser.PathParsingException; +import org.onap.cps.ri.models.AnchorEntity; +import org.onap.cps.ri.models.DataspaceEntity; +import org.onap.cps.ri.models.FragmentEntity; +import org.onap.cps.ri.repository.AnchorRepository; +import org.onap.cps.ri.repository.DataspaceRepository; +import org.onap.cps.ri.repository.FragmentRepository; +import org.onap.cps.ri.utils.SessionManager; import org.onap.cps.spi.CpsDataPersistenceService; import org.onap.cps.spi.FetchDescendantsOption; import org.onap.cps.spi.PaginationOption; -import org.onap.cps.spi.entities.AnchorEntity; -import org.onap.cps.spi.entities.DataspaceEntity; -import org.onap.cps.spi.entities.FragmentEntity; import org.onap.cps.spi.exceptions.AlreadyDefinedException; import org.onap.cps.spi.exceptions.ConcurrencyException; import org.onap.cps.spi.exceptions.CpsAdminException; @@ -62,10 +66,6 @@ import org.onap.cps.spi.exceptions.DataNodeNotFoundException; import org.onap.cps.spi.exceptions.DataNodeNotFoundExceptionBatch; import org.onap.cps.spi.model.DataNode; import org.onap.cps.spi.model.DataNodeBuilder; -import org.onap.cps.spi.repository.AnchorRepository; -import org.onap.cps.spi.repository.DataspaceRepository; -import org.onap.cps.spi.repository.FragmentRepository; -import org.onap.cps.spi.utils.SessionManager; import org.onap.cps.utils.JsonObjectMapper; import org.springframework.dao.DataIntegrityViolationException; import org.springframework.stereotype.Service; diff --git a/cps-ri/src/main/java/org/onap/cps/spi/impl/CpsModulePersistenceServiceImpl.java b/cps-ri/src/main/java/org/onap/cps/ri/CpsModulePersistenceServiceImpl.java index 2c4cc7486b..6f491ba3b7 100755 --- a/cps-ri/src/main/java/org/onap/cps/spi/impl/CpsModulePersistenceServiceImpl.java +++ b/cps-ri/src/main/java/org/onap/cps/ri/CpsModulePersistenceServiceImpl.java @@ -21,7 +21,7 @@ * ============LICENSE_END========================================================= */ -package org.onap.cps.spi.impl; +package org.onap.cps.ri; import static com.google.common.base.Preconditions.checkNotNull; @@ -48,21 +48,21 @@ import lombok.extern.slf4j.Slf4j; import org.apache.commons.codec.digest.DigestUtils; import org.apache.commons.lang3.StringUtils; import org.hibernate.exception.ConstraintViolationException; +import org.onap.cps.ri.models.DataspaceEntity; +import org.onap.cps.ri.models.SchemaSetEntity; +import org.onap.cps.ri.models.YangResourceEntity; +import org.onap.cps.ri.models.YangResourceModuleReference; +import org.onap.cps.ri.repository.DataspaceRepository; +import org.onap.cps.ri.repository.ModuleReferenceRepository; +import org.onap.cps.ri.repository.SchemaSetRepository; +import org.onap.cps.ri.repository.YangResourceRepository; import org.onap.cps.spi.CpsModulePersistenceService; -import org.onap.cps.spi.entities.DataspaceEntity; -import org.onap.cps.spi.entities.SchemaSetEntity; -import org.onap.cps.spi.entities.YangResourceEntity; -import org.onap.cps.spi.entities.YangResourceModuleReference; import org.onap.cps.spi.exceptions.AlreadyDefinedException; import org.onap.cps.spi.exceptions.DuplicatedYangResourceException; import org.onap.cps.spi.exceptions.ModelValidationException; 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.DataspaceRepository; -import org.onap.cps.spi.repository.ModuleReferenceRepository; -import org.onap.cps.spi.repository.SchemaSetRepository; -import org.onap.cps.spi.repository.YangResourceRepository; import org.opendaylight.yangtools.yang.common.Revision; import org.opendaylight.yangtools.yang.model.repo.api.RevisionSourceIdentifier; import org.opendaylight.yangtools.yang.model.repo.api.YangTextSchemaSource; diff --git a/cps-ri/src/main/java/org/onap/cps/spi/entities/AnchorEntity.java b/cps-ri/src/main/java/org/onap/cps/ri/models/AnchorEntity.java index ac06b0b5a4..bf9e25daf1 100644 --- a/cps-ri/src/main/java/org/onap/cps/spi/entities/AnchorEntity.java +++ b/cps-ri/src/main/java/org/onap/cps/ri/models/AnchorEntity.java @@ -18,7 +18,7 @@ * ============LICENSE_END========================================================= */ -package org.onap.cps.spi.entities; +package org.onap.cps.ri.models; import jakarta.persistence.Column; diff --git a/cps-ri/src/main/java/org/onap/cps/spi/entities/DataspaceEntity.java b/cps-ri/src/main/java/org/onap/cps/ri/models/DataspaceEntity.java index ddfb09c942..689ae2c000 100644 --- a/cps-ri/src/main/java/org/onap/cps/spi/entities/DataspaceEntity.java +++ b/cps-ri/src/main/java/org/onap/cps/ri/models/DataspaceEntity.java @@ -19,7 +19,7 @@ * ============LICENSE_END========================================================= */ -package org.onap.cps.spi.entities; +package org.onap.cps.ri.models; import jakarta.persistence.Column; import jakarta.persistence.Entity; diff --git a/cps-ri/src/main/java/org/onap/cps/spi/entities/FragmentEntity.java b/cps-ri/src/main/java/org/onap/cps/ri/models/FragmentEntity.java index c763f61f8f..2c851b60a5 100644 --- a/cps-ri/src/main/java/org/onap/cps/spi/entities/FragmentEntity.java +++ b/cps-ri/src/main/java/org/onap/cps/ri/models/FragmentEntity.java @@ -19,7 +19,7 @@ * ============LICENSE_END========================================================= */ -package org.onap.cps.spi.entities; +package org.onap.cps.ri.models; import jakarta.persistence.CascadeType; import jakarta.persistence.Column; diff --git a/cps-ri/src/main/java/org/onap/cps/spi/entities/SchemaSetEntity.java b/cps-ri/src/main/java/org/onap/cps/ri/models/SchemaSetEntity.java index e07f766ed0..e99f79e330 100644 --- a/cps-ri/src/main/java/org/onap/cps/spi/entities/SchemaSetEntity.java +++ b/cps-ri/src/main/java/org/onap/cps/ri/models/SchemaSetEntity.java @@ -17,7 +17,7 @@ * ============LICENSE_END========================================================= */ -package org.onap.cps.spi.entities; +package org.onap.cps.ri.models; import jakarta.persistence.Column; import jakarta.persistence.Entity; diff --git a/cps-ri/src/main/java/org/onap/cps/spi/entities/YangResourceEntity.java b/cps-ri/src/main/java/org/onap/cps/ri/models/YangResourceEntity.java index 0c54baa4df..2b2d7924db 100644 --- a/cps-ri/src/main/java/org/onap/cps/spi/entities/YangResourceEntity.java +++ b/cps-ri/src/main/java/org/onap/cps/ri/models/YangResourceEntity.java @@ -18,7 +18,7 @@ * ============LICENSE_END========================================================= */ -package org.onap.cps.spi.entities; +package org.onap.cps.ri.models; import jakarta.persistence.Column; import jakarta.persistence.Entity; diff --git a/cps-ri/src/main/java/org/onap/cps/spi/entities/YangResourceModuleReference.java b/cps-ri/src/main/java/org/onap/cps/ri/models/YangResourceModuleReference.java index 3c39c6baac..28ef56d38a 100644 --- a/cps-ri/src/main/java/org/onap/cps/spi/entities/YangResourceModuleReference.java +++ b/cps-ri/src/main/java/org/onap/cps/ri/models/YangResourceModuleReference.java @@ -18,7 +18,7 @@ * ============LICENSE_END========================================================= */ -package org.onap.cps.spi.entities; +package org.onap.cps.ri.models; import org.springframework.beans.factory.annotation.Value; diff --git a/cps-ri/src/main/java/org/onap/cps/spi/repository/AnchorRepository.java b/cps-ri/src/main/java/org/onap/cps/ri/repository/AnchorRepository.java index d78a016c2e..7fe14b3173 100755 --- a/cps-ri/src/main/java/org/onap/cps/spi/repository/AnchorRepository.java +++ b/cps-ri/src/main/java/org/onap/cps/ri/repository/AnchorRepository.java @@ -18,13 +18,13 @@ * ============LICENSE_END========================================================= */ -package org.onap.cps.spi.repository; +package org.onap.cps.ri.repository; import java.util.Collection; import java.util.Optional; -import org.onap.cps.spi.entities.AnchorEntity; -import org.onap.cps.spi.entities.DataspaceEntity; -import org.onap.cps.spi.entities.SchemaSetEntity; +import org.onap.cps.ri.models.AnchorEntity; +import org.onap.cps.ri.models.DataspaceEntity; +import org.onap.cps.ri.models.SchemaSetEntity; import org.onap.cps.spi.exceptions.AnchorNotFoundException; import org.springframework.data.jpa.repository.JpaRepository; import org.springframework.data.jpa.repository.Modifying; diff --git a/cps-ri/src/main/java/org/onap/cps/spi/repository/DataspaceRepository.java b/cps-ri/src/main/java/org/onap/cps/ri/repository/DataspaceRepository.java index b1ce127c4a..b79d802d95 100755 --- a/cps-ri/src/main/java/org/onap/cps/spi/repository/DataspaceRepository.java +++ b/cps-ri/src/main/java/org/onap/cps/ri/repository/DataspaceRepository.java @@ -18,10 +18,10 @@ * ============LICENSE_END========================================================= */ -package org.onap.cps.spi.repository; +package org.onap.cps.ri.repository; import java.util.Optional; -import org.onap.cps.spi.entities.DataspaceEntity; +import org.onap.cps.ri.models.DataspaceEntity; import org.onap.cps.spi.exceptions.DataspaceNotFoundException; import org.springframework.data.jpa.repository.JpaRepository; import org.springframework.stereotype.Repository; diff --git a/cps-ri/src/main/java/org/onap/cps/spi/repository/FragmentPrefetchRepository.java b/cps-ri/src/main/java/org/onap/cps/ri/repository/FragmentPrefetchRepository.java index 2460db869a..6813995d99 100644 --- a/cps-ri/src/main/java/org/onap/cps/spi/repository/FragmentPrefetchRepository.java +++ b/cps-ri/src/main/java/org/onap/cps/ri/repository/FragmentPrefetchRepository.java @@ -18,11 +18,11 @@ * ============LICENSE_END========================================================= */ -package org.onap.cps.spi.repository; +package org.onap.cps.ri.repository; import java.util.Collection; +import org.onap.cps.ri.models.FragmentEntity; import org.onap.cps.spi.FetchDescendantsOption; -import org.onap.cps.spi.entities.FragmentEntity; public interface FragmentPrefetchRepository { Collection<FragmentEntity> prefetchDescendantsOfFragmentEntities( diff --git a/cps-ri/src/main/java/org/onap/cps/spi/repository/FragmentPrefetchRepositoryImpl.java b/cps-ri/src/main/java/org/onap/cps/ri/repository/FragmentPrefetchRepositoryImpl.java index c187f20ea9..bcf01b3d76 100644 --- a/cps-ri/src/main/java/org/onap/cps/spi/repository/FragmentPrefetchRepositoryImpl.java +++ b/cps-ri/src/main/java/org/onap/cps/ri/repository/FragmentPrefetchRepositoryImpl.java @@ -18,7 +18,7 @@ * ============LICENSE_END========================================================= */ -package org.onap.cps.spi.repository; +package org.onap.cps.ri.repository; import java.sql.Connection; import java.util.Collection; @@ -29,9 +29,9 @@ import java.util.function.Function; import java.util.stream.Collectors; import java.util.stream.Stream; import lombok.RequiredArgsConstructor; +import org.onap.cps.ri.models.AnchorEntity; +import org.onap.cps.ri.models.FragmentEntity; import org.onap.cps.spi.FetchDescendantsOption; -import org.onap.cps.spi.entities.AnchorEntity; -import org.onap.cps.spi.entities.FragmentEntity; import org.springframework.jdbc.core.JdbcTemplate; import org.springframework.jdbc.core.PreparedStatementSetter; import org.springframework.jdbc.core.RowMapper; diff --git a/cps-ri/src/main/java/org/onap/cps/spi/repository/FragmentQueryBuilder.java b/cps-ri/src/main/java/org/onap/cps/ri/repository/FragmentQueryBuilder.java index eb61d56632..b8bbf59c23 100644 --- a/cps-ri/src/main/java/org/onap/cps/spi/repository/FragmentQueryBuilder.java +++ b/cps-ri/src/main/java/org/onap/cps/ri/repository/FragmentQueryBuilder.java @@ -19,7 +19,7 @@ * ============LICENSE_END========================================================= */ -package org.onap.cps.spi.repository; +package org.onap.cps.ri.repository; import jakarta.persistence.EntityManager; import jakarta.persistence.PersistenceContext; @@ -32,12 +32,12 @@ import java.util.Queue; import lombok.RequiredArgsConstructor; import org.onap.cps.cpspath.parser.CpsPathPrefixType; import org.onap.cps.cpspath.parser.CpsPathQuery; +import org.onap.cps.ri.models.AnchorEntity; +import org.onap.cps.ri.models.DataspaceEntity; +import org.onap.cps.ri.models.FragmentEntity; +import org.onap.cps.ri.utils.EscapeUtils; import org.onap.cps.spi.PaginationOption; -import org.onap.cps.spi.entities.AnchorEntity; -import org.onap.cps.spi.entities.DataspaceEntity; -import org.onap.cps.spi.entities.FragmentEntity; import org.onap.cps.spi.exceptions.CpsPathException; -import org.onap.cps.spi.utils.EscapeUtils; import org.springframework.stereotype.Component; @RequiredArgsConstructor diff --git a/cps-ri/src/main/java/org/onap/cps/spi/repository/FragmentRepository.java b/cps-ri/src/main/java/org/onap/cps/ri/repository/FragmentRepository.java index 1a31d2b499..8edc3f2311 100755 --- a/cps-ri/src/main/java/org/onap/cps/spi/repository/FragmentRepository.java +++ b/cps-ri/src/main/java/org/onap/cps/ri/repository/FragmentRepository.java @@ -1,140 +1,140 @@ -/*
- * ============LICENSE_START=======================================================
- * Copyright (C) 2021-2023 Nordix Foundation.
- * Modifications Copyright (C) 2020-2021 Bell Canada.
- * Modifications Copyright (C) 2020-2021 Pantheon.tech.
- * Modifications Copyright (C) 2023 TechMahindra Ltd.
- * ================================================================================
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- *
- * SPDX-License-Identifier: Apache-2.0
- * ============LICENSE_END=========================================================
- */
-
-package org.onap.cps.spi.repository;
-
-import java.util.Collection;
-import java.util.List;
-import java.util.Optional;
-import org.onap.cps.spi.entities.AnchorEntity;
-import org.onap.cps.spi.entities.DataspaceEntity;
-import org.onap.cps.spi.entities.FragmentEntity;
-import org.onap.cps.spi.exceptions.DataNodeNotFoundException;
-import org.onap.cps.spi.utils.EscapeUtils;
-import org.springframework.data.jpa.repository.JpaRepository;
-import org.springframework.data.jpa.repository.Modifying;
-import org.springframework.data.jpa.repository.Query;
-import org.springframework.data.repository.query.Param;
-import org.springframework.stereotype.Repository;
-
-@Repository
-public interface FragmentRepository extends JpaRepository<FragmentEntity, Long>, FragmentRepositoryCpsPathQuery,
- FragmentPrefetchRepository {
-
- Optional<FragmentEntity> findByAnchorAndXpath(AnchorEntity anchorEntity, String xpath);
-
- default FragmentEntity getByAnchorAndXpath(final AnchorEntity anchorEntity, final String xpath) {
- return findByAnchorAndXpath(anchorEntity, xpath).orElseThrow(() ->
- new DataNodeNotFoundException(anchorEntity.getDataspace().getName(), anchorEntity.getName(), xpath));
- }
-
- @Query(value = "SELECT * FROM fragment WHERE anchor_id = :anchorId AND xpath = ANY (:xpaths)",
- nativeQuery = true)
- List<FragmentEntity> findByAnchorIdAndXpathIn(@Param("anchorId") long anchorId,
- @Param("xpaths") String[] xpaths);
-
- default List<FragmentEntity> findByAnchorAndXpathIn(final AnchorEntity anchorEntity,
- final Collection<String> xpaths) {
- return findByAnchorIdAndXpathIn(anchorEntity.getId(), xpaths.toArray(new String[0]));
- }
-
- @Query(value = "SELECT * FROM fragment WHERE anchor_id = :anchorId \n"
- + "AND xpath LIKE :escapedXpath||'[@%]' AND xpath NOT LIKE :escapedXpath||'[@%]/%[@%]'",
- nativeQuery = true)
- List<FragmentEntity> findListByAnchorIdAndEscapedXpath(@Param("anchorId") long anchorId,
- @Param("escapedXpath") String escapedXpath);
-
- default List<FragmentEntity> findListByAnchorAndXpath(final AnchorEntity anchorEntity, final String xpath) {
- final String escapedXpath = EscapeUtils.escapeForSqlLike(xpath);
- return findListByAnchorIdAndEscapedXpath(anchorEntity.getId(), escapedXpath);
- }
-
- @Query(value = "SELECT fragment.* FROM fragment JOIN anchor ON anchor.id = fragment.anchor_id "
- + "WHERE dataspace_id = :dataspaceId AND xpath = ANY (:xpaths)", nativeQuery = true)
- List<FragmentEntity> findByDataspaceIdAndXpathIn(@Param("dataspaceId") int dataspaceId,
- @Param("xpaths") String[] xpaths);
-
- default List<FragmentEntity> findByDataspaceAndXpathIn(final DataspaceEntity dataspaceEntity,
- final Collection<String> xpaths) {
- return findByDataspaceIdAndXpathIn(dataspaceEntity.getId(), xpaths.toArray(new String[0]));
- }
-
- @Query(value = "SELECT * FROM fragment WHERE anchor_id IN (:anchorIds)"
- + " AND xpath = ANY (:xpaths)", nativeQuery = true)
- List<FragmentEntity> findByAnchorIdsAndXpathIn(@Param("anchorIds") Long[] anchorIds,
- @Param("xpaths") String[] xpaths);
-
- @Query(value = "SELECT * FROM fragment WHERE anchor_id = :anchorId LIMIT 1", nativeQuery = true)
- Optional<FragmentEntity> findOneByAnchorId(@Param("anchorId") long anchorId);
-
- @Modifying
- @Query(value = "DELETE FROM fragment WHERE anchor_id = ANY (:anchorIds)", nativeQuery = true)
- void deleteByAnchorIdIn(@Param("anchorIds") long[] anchorIds);
-
- default void deleteByAnchorIn(final Collection<AnchorEntity> anchorEntities) {
- deleteByAnchorIdIn(anchorEntities.stream().map(AnchorEntity::getId).mapToLong(id -> id).toArray());
- }
-
- @Modifying
- @Query(value = "DELETE FROM fragment WHERE anchor_id = :anchorId AND xpath = ANY (:xpaths)", nativeQuery = true)
- void deleteByAnchorIdAndXpaths(@Param("anchorId") long anchorId, @Param("xpaths") String[] xpaths);
-
- default void deleteByAnchorIdAndXpaths(final long anchorId, final Collection<String> xpaths) {
- deleteByAnchorIdAndXpaths(anchorId, xpaths.toArray(new String[0]));
- }
-
- @Modifying
- @Query(value = "DELETE FROM fragment f WHERE anchor_id = :anchorId AND xpath LIKE ANY (:xpathPatterns)",
- nativeQuery = true)
- void deleteByAnchorIdAndXpathLikeAny(@Param("anchorId") long anchorId,
- @Param("xpathPatterns") String[] xpathPatterns);
-
- default void deleteListsByAnchorIdAndXpaths(long anchorId, Collection<String> xpaths) {
- deleteByAnchorIdAndXpathLikeAny(anchorId,
- xpaths.stream().map(xpath -> EscapeUtils.escapeForSqlLike(xpath) + "[@%").toArray(String[]::new));
- }
-
- @Query(value = "SELECT xpath FROM fragment WHERE anchor_id = :anchorId AND xpath = ANY (:xpaths)",
- nativeQuery = true)
- List<String> findAllXpathByAnchorIdAndXpathIn(@Param("anchorId") long anchorId,
- @Param("xpaths") String[] xpaths);
-
- default List<String> findAllXpathByAnchorAndXpathIn(final AnchorEntity anchorEntity,
- final Collection<String> xpaths) {
- return findAllXpathByAnchorIdAndXpathIn(anchorEntity.getId(), xpaths.toArray(new String[0]));
- }
-
- @Query(value = "SELECT EXISTS(SELECT 1 FROM fragment WHERE anchor_id = :anchorId"
- + " AND xpath LIKE :xpathPattern LIMIT 1)", nativeQuery = true)
- boolean existsByAnchorIdAndParentXpathAndXpathLike(@Param("anchorId") long anchorId,
- @Param("xpathPattern") String xpathPattern);
-
- default boolean existsByAnchorAndXpathStartsWith(final AnchorEntity anchorEntity, final String xpath) {
- return existsByAnchorIdAndParentXpathAndXpathLike(anchorEntity.getId(),
- EscapeUtils.escapeForSqlLike(xpath) + "%");
- }
-
- @Query(value = "SELECT * FROM fragment WHERE anchor_id = :anchorId AND parent_id IS NULL", nativeQuery = true)
- List<FragmentEntity> findRootsByAnchorId(@Param("anchorId") long anchorId);
-
-}
+/* + * ============LICENSE_START======================================================= + * Copyright (C) 2021-2023 Nordix Foundation. + * Modifications Copyright (C) 2020-2021 Bell Canada. + * Modifications Copyright (C) 2020-2021 Pantheon.tech. + * Modifications Copyright (C) 2023 TechMahindra Ltd. + * ================================================================================ + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * SPDX-License-Identifier: Apache-2.0 + * ============LICENSE_END========================================================= + */ + +package org.onap.cps.ri.repository; + +import java.util.Collection; +import java.util.List; +import java.util.Optional; +import org.onap.cps.ri.models.AnchorEntity; +import org.onap.cps.ri.models.DataspaceEntity; +import org.onap.cps.ri.models.FragmentEntity; +import org.onap.cps.ri.utils.EscapeUtils; +import org.onap.cps.spi.exceptions.DataNodeNotFoundException; +import org.springframework.data.jpa.repository.JpaRepository; +import org.springframework.data.jpa.repository.Modifying; +import org.springframework.data.jpa.repository.Query; +import org.springframework.data.repository.query.Param; +import org.springframework.stereotype.Repository; + +@Repository +public interface FragmentRepository extends JpaRepository<FragmentEntity, Long>, FragmentRepositoryCpsPathQuery, + FragmentPrefetchRepository { + + Optional<FragmentEntity> findByAnchorAndXpath(AnchorEntity anchorEntity, String xpath); + + default FragmentEntity getByAnchorAndXpath(final AnchorEntity anchorEntity, final String xpath) { + return findByAnchorAndXpath(anchorEntity, xpath).orElseThrow(() -> + new DataNodeNotFoundException(anchorEntity.getDataspace().getName(), anchorEntity.getName(), xpath)); + } + + @Query(value = "SELECT * FROM fragment WHERE anchor_id = :anchorId AND xpath = ANY (:xpaths)", + nativeQuery = true) + List<FragmentEntity> findByAnchorIdAndXpathIn(@Param("anchorId") long anchorId, + @Param("xpaths") String[] xpaths); + + default List<FragmentEntity> findByAnchorAndXpathIn(final AnchorEntity anchorEntity, + final Collection<String> xpaths) { + return findByAnchorIdAndXpathIn(anchorEntity.getId(), xpaths.toArray(new String[0])); + } + + @Query(value = "SELECT * FROM fragment WHERE anchor_id = :anchorId \n" + + "AND xpath LIKE :escapedXpath||'[@%]' AND xpath NOT LIKE :escapedXpath||'[@%]/%[@%]'", + nativeQuery = true) + List<FragmentEntity> findListByAnchorIdAndEscapedXpath(@Param("anchorId") long anchorId, + @Param("escapedXpath") String escapedXpath); + + default List<FragmentEntity> findListByAnchorAndXpath(final AnchorEntity anchorEntity, final String xpath) { + final String escapedXpath = EscapeUtils.escapeForSqlLike(xpath); + return findListByAnchorIdAndEscapedXpath(anchorEntity.getId(), escapedXpath); + } + + @Query(value = "SELECT fragment.* FROM fragment JOIN anchor ON anchor.id = fragment.anchor_id " + + "WHERE dataspace_id = :dataspaceId AND xpath = ANY (:xpaths)", nativeQuery = true) + List<FragmentEntity> findByDataspaceIdAndXpathIn(@Param("dataspaceId") int dataspaceId, + @Param("xpaths") String[] xpaths); + + default List<FragmentEntity> findByDataspaceAndXpathIn(final DataspaceEntity dataspaceEntity, + final Collection<String> xpaths) { + return findByDataspaceIdAndXpathIn(dataspaceEntity.getId(), xpaths.toArray(new String[0])); + } + + @Query(value = "SELECT * FROM fragment WHERE anchor_id IN (:anchorIds)" + + " AND xpath = ANY (:xpaths)", nativeQuery = true) + List<FragmentEntity> findByAnchorIdsAndXpathIn(@Param("anchorIds") Long[] anchorIds, + @Param("xpaths") String[] xpaths); + + @Query(value = "SELECT * FROM fragment WHERE anchor_id = :anchorId LIMIT 1", nativeQuery = true) + Optional<FragmentEntity> findOneByAnchorId(@Param("anchorId") long anchorId); + + @Modifying + @Query(value = "DELETE FROM fragment WHERE anchor_id = ANY (:anchorIds)", nativeQuery = true) + void deleteByAnchorIdIn(@Param("anchorIds") long[] anchorIds); + + default void deleteByAnchorIn(final Collection<AnchorEntity> anchorEntities) { + deleteByAnchorIdIn(anchorEntities.stream().map(AnchorEntity::getId).mapToLong(id -> id).toArray()); + } + + @Modifying + @Query(value = "DELETE FROM fragment WHERE anchor_id = :anchorId AND xpath = ANY (:xpaths)", nativeQuery = true) + void deleteByAnchorIdAndXpaths(@Param("anchorId") long anchorId, @Param("xpaths") String[] xpaths); + + default void deleteByAnchorIdAndXpaths(final long anchorId, final Collection<String> xpaths) { + deleteByAnchorIdAndXpaths(anchorId, xpaths.toArray(new String[0])); + } + + @Modifying + @Query(value = "DELETE FROM fragment f WHERE anchor_id = :anchorId AND xpath LIKE ANY (:xpathPatterns)", + nativeQuery = true) + void deleteByAnchorIdAndXpathLikeAny(@Param("anchorId") long anchorId, + @Param("xpathPatterns") String[] xpathPatterns); + + default void deleteListsByAnchorIdAndXpaths(long anchorId, Collection<String> xpaths) { + deleteByAnchorIdAndXpathLikeAny(anchorId, + xpaths.stream().map(xpath -> EscapeUtils.escapeForSqlLike(xpath) + "[@%").toArray(String[]::new)); + } + + @Query(value = "SELECT xpath FROM fragment WHERE anchor_id = :anchorId AND xpath = ANY (:xpaths)", + nativeQuery = true) + List<String> findAllXpathByAnchorIdAndXpathIn(@Param("anchorId") long anchorId, + @Param("xpaths") String[] xpaths); + + default List<String> findAllXpathByAnchorAndXpathIn(final AnchorEntity anchorEntity, + final Collection<String> xpaths) { + return findAllXpathByAnchorIdAndXpathIn(anchorEntity.getId(), xpaths.toArray(new String[0])); + } + + @Query(value = "SELECT EXISTS(SELECT 1 FROM fragment WHERE anchor_id = :anchorId" + + " AND xpath LIKE :xpathPattern LIMIT 1)", nativeQuery = true) + boolean existsByAnchorIdAndParentXpathAndXpathLike(@Param("anchorId") long anchorId, + @Param("xpathPattern") String xpathPattern); + + default boolean existsByAnchorAndXpathStartsWith(final AnchorEntity anchorEntity, final String xpath) { + return existsByAnchorIdAndParentXpathAndXpathLike(anchorEntity.getId(), + EscapeUtils.escapeForSqlLike(xpath) + "%"); + } + + @Query(value = "SELECT * FROM fragment WHERE anchor_id = :anchorId AND parent_id IS NULL", nativeQuery = true) + List<FragmentEntity> findRootsByAnchorId(@Param("anchorId") long anchorId); + +} diff --git a/cps-ri/src/main/java/org/onap/cps/spi/repository/FragmentRepositoryCpsPathQuery.java b/cps-ri/src/main/java/org/onap/cps/ri/repository/FragmentRepositoryCpsPathQuery.java index 9c279618b0..49c8e76ab5 100644 --- a/cps-ri/src/main/java/org/onap/cps/spi/repository/FragmentRepositoryCpsPathQuery.java +++ b/cps-ri/src/main/java/org/onap/cps/ri/repository/FragmentRepositoryCpsPathQuery.java @@ -19,14 +19,14 @@ * ============LICENSE_END========================================================= */ -package org.onap.cps.spi.repository; +package org.onap.cps.ri.repository; import java.util.List; import org.onap.cps.cpspath.parser.CpsPathQuery; +import org.onap.cps.ri.models.AnchorEntity; +import org.onap.cps.ri.models.DataspaceEntity; +import org.onap.cps.ri.models.FragmentEntity; import org.onap.cps.spi.PaginationOption; -import org.onap.cps.spi.entities.AnchorEntity; -import org.onap.cps.spi.entities.DataspaceEntity; -import org.onap.cps.spi.entities.FragmentEntity; public interface FragmentRepositoryCpsPathQuery { List<FragmentEntity> findByAnchorAndCpsPath(AnchorEntity anchorEntity, CpsPathQuery cpsPathQuery); diff --git a/cps-ri/src/main/java/org/onap/cps/spi/repository/FragmentRepositoryCpsPathQueryImpl.java b/cps-ri/src/main/java/org/onap/cps/ri/repository/FragmentRepositoryCpsPathQueryImpl.java index 9c98f7f7d9..01b2813cd3 100644 --- a/cps-ri/src/main/java/org/onap/cps/spi/repository/FragmentRepositoryCpsPathQueryImpl.java +++ b/cps-ri/src/main/java/org/onap/cps/ri/repository/FragmentRepositoryCpsPathQueryImpl.java @@ -19,7 +19,7 @@ * ============LICENSE_END========================================================= */ -package org.onap.cps.spi.repository; +package org.onap.cps.ri.repository; import jakarta.persistence.Query; import jakarta.transaction.Transactional; @@ -27,10 +27,10 @@ import java.util.List; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; import org.onap.cps.cpspath.parser.CpsPathQuery; +import org.onap.cps.ri.models.AnchorEntity; +import org.onap.cps.ri.models.DataspaceEntity; +import org.onap.cps.ri.models.FragmentEntity; import org.onap.cps.spi.PaginationOption; -import org.onap.cps.spi.entities.AnchorEntity; -import org.onap.cps.spi.entities.DataspaceEntity; -import org.onap.cps.spi.entities.FragmentEntity; @RequiredArgsConstructor @Slf4j diff --git a/cps-ri/src/main/java/org/onap/cps/spi/repository/ModuleReferenceQuery.java b/cps-ri/src/main/java/org/onap/cps/ri/repository/ModuleReferenceQuery.java index 4082307384..ad0f9c5c61 100644 --- a/cps-ri/src/main/java/org/onap/cps/spi/repository/ModuleReferenceQuery.java +++ b/cps-ri/src/main/java/org/onap/cps/ri/repository/ModuleReferenceQuery.java @@ -18,7 +18,7 @@ * ============LICENSE_END========================================================= */ -package org.onap.cps.spi.repository; +package org.onap.cps.ri.repository; import java.util.Collection; import java.util.Map; diff --git a/cps-ri/src/main/java/org/onap/cps/spi/repository/ModuleReferenceRepository.java b/cps-ri/src/main/java/org/onap/cps/ri/repository/ModuleReferenceRepository.java index 15ffa372f9..e9b866cc19 100644 --- a/cps-ri/src/main/java/org/onap/cps/spi/repository/ModuleReferenceRepository.java +++ b/cps-ri/src/main/java/org/onap/cps/ri/repository/ModuleReferenceRepository.java @@ -18,9 +18,9 @@ * ============LICENSE_END========================================================= */ -package org.onap.cps.spi.repository; +package org.onap.cps.ri.repository; -import org.onap.cps.spi.entities.YangResourceEntity; +import org.onap.cps.ri.models.YangResourceEntity; import org.springframework.data.jpa.repository.JpaRepository; import org.springframework.stereotype.Repository; diff --git a/cps-ri/src/main/java/org/onap/cps/spi/repository/ModuleReferenceRepositoryImpl.java b/cps-ri/src/main/java/org/onap/cps/ri/repository/ModuleReferenceRepositoryImpl.java index 6cc8234c90..c160fb1e38 100644 --- a/cps-ri/src/main/java/org/onap/cps/spi/repository/ModuleReferenceRepositoryImpl.java +++ b/cps-ri/src/main/java/org/onap/cps/ri/repository/ModuleReferenceRepositoryImpl.java @@ -18,7 +18,7 @@ * ============LICENSE_END========================================================= */ -package org.onap.cps.spi.repository; +package org.onap.cps.ri.repository; import jakarta.persistence.EntityManager; import jakarta.persistence.PersistenceContext; diff --git a/cps-ri/src/main/java/org/onap/cps/spi/repository/SchemaSetRepository.java b/cps-ri/src/main/java/org/onap/cps/ri/repository/SchemaSetRepository.java index 3c5f973cb0..9357a5c6a7 100644 --- a/cps-ri/src/main/java/org/onap/cps/spi/repository/SchemaSetRepository.java +++ b/cps-ri/src/main/java/org/onap/cps/ri/repository/SchemaSetRepository.java @@ -19,13 +19,13 @@ * ============LICENSE_END========================================================= */ -package org.onap.cps.spi.repository; +package org.onap.cps.ri.repository; import java.util.Collection; import java.util.List; import java.util.Optional; -import org.onap.cps.spi.entities.DataspaceEntity; -import org.onap.cps.spi.entities.SchemaSetEntity; +import org.onap.cps.ri.models.DataspaceEntity; +import org.onap.cps.ri.models.SchemaSetEntity; import org.onap.cps.spi.exceptions.SchemaSetNotFoundException; import org.springframework.data.jpa.repository.JpaRepository; import org.springframework.data.jpa.repository.Modifying; diff --git a/cps-ri/src/main/java/org/onap/cps/spi/repository/SchemaSetYangResourceRepository.java b/cps-ri/src/main/java/org/onap/cps/ri/repository/SchemaSetYangResourceRepository.java index aacebd63b3..8350d5728c 100644 --- a/cps-ri/src/main/java/org/onap/cps/spi/repository/SchemaSetYangResourceRepository.java +++ b/cps-ri/src/main/java/org/onap/cps/ri/repository/SchemaSetYangResourceRepository.java @@ -18,7 +18,7 @@ * ============LICENSE_END========================================================= */ -package org.onap.cps.spi.repository; +package org.onap.cps.ri.repository; import java.util.List; diff --git a/cps-ri/src/main/java/org/onap/cps/spi/repository/SchemaSetYangResourceRepositoryImpl.java b/cps-ri/src/main/java/org/onap/cps/ri/repository/SchemaSetYangResourceRepositoryImpl.java index c786a62d0c..287bcda01b 100644 --- a/cps-ri/src/main/java/org/onap/cps/spi/repository/SchemaSetYangResourceRepositoryImpl.java +++ b/cps-ri/src/main/java/org/onap/cps/ri/repository/SchemaSetYangResourceRepositoryImpl.java @@ -18,7 +18,7 @@ * ============LICENSE_END========================================================= */ -package org.onap.cps.spi.repository; +package org.onap.cps.ri.repository; import jakarta.persistence.EntityManager; import jakarta.persistence.PersistenceContext; diff --git a/cps-ri/src/main/java/org/onap/cps/spi/repository/TempTableCreator.java b/cps-ri/src/main/java/org/onap/cps/ri/repository/TempTableCreator.java index 5804b2dc5d..cc83ab7d94 100644 --- a/cps-ri/src/main/java/org/onap/cps/spi/repository/TempTableCreator.java +++ b/cps-ri/src/main/java/org/onap/cps/ri/repository/TempTableCreator.java @@ -18,7 +18,7 @@ * ============LICENSE_END========================================================= */ -package org.onap.cps.spi.repository; +package org.onap.cps.ri.repository; import jakarta.persistence.EntityManager; import jakarta.persistence.PersistenceContext; @@ -31,7 +31,7 @@ import java.util.UUID; import java.util.stream.Collectors; import lombok.AllArgsConstructor; import lombok.extern.slf4j.Slf4j; -import org.onap.cps.spi.utils.EscapeUtils; +import org.onap.cps.ri.utils.EscapeUtils; import org.springframework.stereotype.Component; import org.springframework.transaction.annotation.Transactional; diff --git a/cps-ri/src/main/java/org/onap/cps/spi/repository/YangResourceNativeRepository.java b/cps-ri/src/main/java/org/onap/cps/ri/repository/YangResourceNativeRepository.java index 9ae32b3e78..ef7b12dc9c 100644 --- a/cps-ri/src/main/java/org/onap/cps/spi/repository/YangResourceNativeRepository.java +++ b/cps-ri/src/main/java/org/onap/cps/ri/repository/YangResourceNativeRepository.java @@ -18,7 +18,7 @@ * ============LICENSE_END========================================================= */ -package org.onap.cps.spi.repository; +package org.onap.cps.ri.repository; import java.util.Collection; import java.util.List; diff --git a/cps-ri/src/main/java/org/onap/cps/spi/repository/YangResourceNativeRepositoryImpl.java b/cps-ri/src/main/java/org/onap/cps/ri/repository/YangResourceNativeRepositoryImpl.java index c84ff427e6..c65ab7d6fa 100644 --- a/cps-ri/src/main/java/org/onap/cps/spi/repository/YangResourceNativeRepositoryImpl.java +++ b/cps-ri/src/main/java/org/onap/cps/ri/repository/YangResourceNativeRepositoryImpl.java @@ -18,7 +18,7 @@ * ============LICENSE_END========================================================= */ -package org.onap.cps.spi.repository; +package org.onap.cps.ri.repository; import jakarta.persistence.EntityManager; import jakarta.persistence.PersistenceContext; diff --git a/cps-ri/src/main/java/org/onap/cps/spi/repository/YangResourceRepository.java b/cps-ri/src/main/java/org/onap/cps/ri/repository/YangResourceRepository.java index 8be0d9a33b..9a11592310 100644 --- a/cps-ri/src/main/java/org/onap/cps/spi/repository/YangResourceRepository.java +++ b/cps-ri/src/main/java/org/onap/cps/ri/repository/YangResourceRepository.java @@ -18,13 +18,13 @@ * ============LICENSE_END========================================================= */ -package org.onap.cps.spi.repository; +package org.onap.cps.ri.repository; import java.util.Collection; import java.util.List; import java.util.Set; -import org.onap.cps.spi.entities.YangResourceEntity; -import org.onap.cps.spi.entities.YangResourceModuleReference; +import org.onap.cps.ri.models.YangResourceEntity; +import org.onap.cps.ri.models.YangResourceModuleReference; import org.springframework.data.jpa.repository.JpaRepository; import org.springframework.data.jpa.repository.Modifying; import org.springframework.data.jpa.repository.Query; diff --git a/cps-ri/src/main/java/org/onap/cps/spi/config/CpsSessionFactory.java b/cps-ri/src/main/java/org/onap/cps/ri/utils/CpsSessionFactory.java index 5241ea0096..c0291176f4 100644 --- a/cps-ri/src/main/java/org/onap/cps/spi/config/CpsSessionFactory.java +++ b/cps-ri/src/main/java/org/onap/cps/ri/utils/CpsSessionFactory.java @@ -18,15 +18,15 @@ * ============LICENSE_END========================================================= */ -package org.onap.cps.spi.config; +package org.onap.cps.ri.utils; import org.hibernate.HibernateException; import org.hibernate.Session; import org.hibernate.SessionFactory; -import org.onap.cps.spi.entities.AnchorEntity; -import org.onap.cps.spi.entities.DataspaceEntity; -import org.onap.cps.spi.entities.SchemaSetEntity; -import org.onap.cps.spi.entities.YangResourceEntity; +import org.onap.cps.ri.models.AnchorEntity; +import org.onap.cps.ri.models.DataspaceEntity; +import org.onap.cps.ri.models.SchemaSetEntity; +import org.onap.cps.ri.models.YangResourceEntity; import org.springframework.beans.factory.config.ConfigurableBeanFactory; import org.springframework.context.annotation.Scope; import org.springframework.stereotype.Component; diff --git a/cps-ri/src/main/java/org/onap/cps/spi/impl/utils/CpsValidatorImpl.java b/cps-ri/src/main/java/org/onap/cps/ri/utils/CpsValidatorImpl.java index c727388b25..4f942a37ea 100644 --- a/cps-ri/src/main/java/org/onap/cps/spi/impl/utils/CpsValidatorImpl.java +++ b/cps-ri/src/main/java/org/onap/cps/ri/utils/CpsValidatorImpl.java @@ -18,16 +18,16 @@ * ============LICENSE_END========================================================= */ -package org.onap.cps.spi.impl.utils; +package org.onap.cps.ri.utils; import com.google.common.collect.Lists; import java.util.Arrays; import java.util.Collection; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; +import org.onap.cps.impl.utils.CpsValidator; import org.onap.cps.spi.PaginationOption; import org.onap.cps.spi.exceptions.DataValidationException; -import org.onap.cps.spi.utils.CpsValidator; import org.springframework.stereotype.Component; @Slf4j diff --git a/cps-ri/src/main/java/org/onap/cps/spi/utils/EscapeUtils.java b/cps-ri/src/main/java/org/onap/cps/ri/utils/EscapeUtils.java index 2b61d39503..5323ae6bc9 100644 --- a/cps-ri/src/main/java/org/onap/cps/spi/utils/EscapeUtils.java +++ b/cps-ri/src/main/java/org/onap/cps/ri/utils/EscapeUtils.java @@ -18,7 +18,7 @@ * ============LICENSE_END========================================================= */ -package org.onap.cps.spi.utils; +package org.onap.cps.ri.utils; import lombok.AccessLevel; import lombok.NoArgsConstructor; diff --git a/cps-ri/src/main/java/org/onap/cps/spi/utils/SessionManager.java b/cps-ri/src/main/java/org/onap/cps/ri/utils/SessionManager.java index 6150bf9dbe..b81a0bd39d 100644 --- a/cps-ri/src/main/java/org/onap/cps/spi/utils/SessionManager.java +++ b/cps-ri/src/main/java/org/onap/cps/ri/utils/SessionManager.java @@ -18,7 +18,7 @@ * ============LICENSE_END========================================================= */ -package org.onap.cps.spi.utils; +package org.onap.cps.ri.utils; import com.google.common.util.concurrent.TimeLimiter; import com.google.common.util.concurrent.UncheckedExecutionException; @@ -36,13 +36,12 @@ import lombok.extern.slf4j.Slf4j; import org.hibernate.HibernateException; import org.hibernate.LockMode; import org.hibernate.Session; -import org.onap.cps.spi.config.CpsSessionFactory; -import org.onap.cps.spi.entities.AnchorEntity; -import org.onap.cps.spi.entities.DataspaceEntity; +import org.onap.cps.ri.models.AnchorEntity; +import org.onap.cps.ri.models.DataspaceEntity; +import org.onap.cps.ri.repository.AnchorRepository; +import org.onap.cps.ri.repository.DataspaceRepository; import org.onap.cps.spi.exceptions.SessionManagerException; import org.onap.cps.spi.exceptions.SessionTimeoutException; -import org.onap.cps.spi.repository.AnchorRepository; -import org.onap.cps.spi.repository.DataspaceRepository; import org.springframework.beans.factory.config.ConfigurableBeanFactory; import org.springframework.context.annotation.Scope; import org.springframework.stereotype.Component; diff --git a/cps-ri/src/main/java/org/onap/cps/spi/utils/TimeLimiterProvider.java b/cps-ri/src/main/java/org/onap/cps/ri/utils/TimeLimiterProvider.java index 2bd7ac3763..10031c0b28 100644 --- a/cps-ri/src/main/java/org/onap/cps/spi/utils/TimeLimiterProvider.java +++ b/cps-ri/src/main/java/org/onap/cps/ri/utils/TimeLimiterProvider.java @@ -18,7 +18,7 @@ * ============LICENSE_END========================================================= */ -package org.onap.cps.spi.utils; +package org.onap.cps.ri.utils; import com.google.common.util.concurrent.SimpleTimeLimiter; import com.google.common.util.concurrent.TimeLimiter; diff --git a/cps-ri/src/test/groovy/org/onap/cps/spi/impl/CpsDataPersistenceServiceSpec.groovy b/cps-ri/src/test/groovy/org/onap/cps/ri/CpsDataPersistenceServiceImplSpec.groovy index c72c3046e8..36bf55e2db 100644 --- a/cps-ri/src/test/groovy/org/onap/cps/spi/impl/CpsDataPersistenceServiceSpec.groovy +++ b/cps-ri/src/test/groovy/org/onap/cps/ri/CpsDataPersistenceServiceImplSpec.groovy @@ -18,29 +18,29 @@ * ============LICENSE_END========================================================= */ -package org.onap.cps.spi.impl +package org.onap.cps.ri import com.fasterxml.jackson.databind.ObjectMapper import org.hibernate.StaleStateException +import org.onap.cps.ri.models.AnchorEntity +import org.onap.cps.ri.models.DataspaceEntity +import org.onap.cps.ri.models.FragmentEntity +import org.onap.cps.ri.repository.AnchorRepository +import org.onap.cps.ri.repository.DataspaceRepository +import org.onap.cps.ri.repository.FragmentRepository +import org.onap.cps.ri.utils.SessionManager import org.onap.cps.spi.FetchDescendantsOption -import org.onap.cps.spi.entities.AnchorEntity -import org.onap.cps.spi.entities.DataspaceEntity -import org.onap.cps.spi.entities.FragmentEntity - import org.onap.cps.spi.exceptions.ConcurrencyException import org.onap.cps.spi.exceptions.DataValidationException import org.onap.cps.spi.model.DataNode import org.onap.cps.spi.model.DataNodeBuilder -import org.onap.cps.spi.repository.AnchorRepository -import org.onap.cps.spi.repository.DataspaceRepository -import org.onap.cps.spi.repository.FragmentRepository -import org.onap.cps.spi.utils.SessionManager import org.onap.cps.utils.JsonObjectMapper import org.springframework.dao.DataIntegrityViolationException import spock.lang.Specification + import java.util.stream.Collectors -class CpsDataPersistenceServiceSpec extends Specification { +class CpsDataPersistenceServiceImplSpec extends Specification { def mockDataspaceRepository = Mock(DataspaceRepository) def mockAnchorRepository = Mock(AnchorRepository) diff --git a/cps-ri/src/test/groovy/org/onap/cps/spi/impl/CpsModulePersistenceServiceConcurrencySpec.groovy b/cps-ri/src/test/groovy/org/onap/cps/ri/CpsModulePersistenceServiceConcurrencySpec.groovy index 2e4dba2e9b..b892fe4dae 100644 --- a/cps-ri/src/test/groovy/org/onap/cps/spi/impl/CpsModulePersistenceServiceConcurrencySpec.groovy +++ b/cps-ri/src/test/groovy/org/onap/cps/ri/CpsModulePersistenceServiceConcurrencySpec.groovy @@ -18,19 +18,19 @@ * SPDX-License-Identifier: Apache-2.0 * ============LICENSE_END========================================================= */ -package org.onap.cps.spi.impl +package org.onap.cps.ri import org.hibernate.exception.ConstraintViolationException +import org.onap.cps.ri.models.DataspaceEntity +import org.onap.cps.ri.models.SchemaSetEntity +import org.onap.cps.ri.repository.DataspaceRepository +import org.onap.cps.ri.repository.ModuleReferenceRepository +import org.onap.cps.ri.repository.SchemaSetRepository +import org.onap.cps.ri.repository.YangResourceRepository import org.onap.cps.spi.CpsAdminPersistenceService import org.onap.cps.spi.CpsModulePersistenceService -import org.onap.cps.spi.entities.DataspaceEntity -import org.onap.cps.spi.entities.SchemaSetEntity import org.onap.cps.spi.exceptions.DuplicatedYangResourceException 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.onap.cps.spi.repository.YangResourceRepository import org.spockframework.spring.SpringBean import org.springframework.beans.factory.annotation.Autowired import org.springframework.boot.test.context.SpringBootTest diff --git a/cps-ri/src/test/groovy/org/onap/cps/spi/impl/CpsModulePersistenceServiceSpec.groovy b/cps-ri/src/test/groovy/org/onap/cps/ri/CpsModulePersistenceServiceImplSpec.groovy index 3447a1c599..1b61ff39c0 100644 --- a/cps-ri/src/test/groovy/org/onap/cps/spi/impl/CpsModulePersistenceServiceSpec.groovy +++ b/cps-ri/src/test/groovy/org/onap/cps/ri/CpsModulePersistenceServiceImplSpec.groovy @@ -17,25 +17,26 @@ * ============LICENSE_END========================================================= */ -package org.onap.cps.spi.impl +package org.onap.cps.ri import org.hibernate.exception.ConstraintViolationException +import org.onap.cps.ri.models.SchemaSetEntity +import org.onap.cps.ri.repository.DataspaceRepository +import org.onap.cps.ri.repository.ModuleReferenceRepository +import org.onap.cps.ri.repository.SchemaSetRepository +import org.onap.cps.ri.repository.YangResourceRepository import org.onap.cps.spi.CpsModulePersistenceService -import org.onap.cps.spi.entities.SchemaSetEntity import org.onap.cps.spi.exceptions.DuplicatedYangResourceException 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.onap.cps.spi.repository.YangResourceRepository import org.springframework.dao.DataIntegrityViolationException import spock.lang.Specification + import java.sql.SQLException /** * Specification unit test class for CPS module persistence service. */ -class CpsModulePersistenceServiceSpec extends Specification { +class CpsModulePersistenceServiceImplSpec extends Specification { CpsModulePersistenceService objectUnderTest diff --git a/cps-ri/src/test/groovy/org/onap/cps/spi/impl/utils/CpsValidatorSpec.groovy b/cps-ri/src/test/groovy/org/onap/cps/ri/utils/CpsValidatorImplSpec.groovy index 8d348443c7..d57bf25058 100644 --- a/cps-ri/src/test/groovy/org/onap/cps/spi/impl/utils/CpsValidatorSpec.groovy +++ b/cps-ri/src/test/groovy/org/onap/cps/ri/utils/CpsValidatorImplSpec.groovy @@ -18,13 +18,14 @@ * ============LICENSE_END========================================================= */ -package org.onap.cps.spi.impl.utils +package org.onap.cps.ri.utils + import org.onap.cps.spi.PaginationOption import org.onap.cps.spi.exceptions.DataValidationException import spock.lang.Specification -class CpsValidatorSpec extends Specification { +class CpsValidatorImplSpec extends Specification { def objectUnderTest = new CpsValidatorImpl() diff --git a/cps-ri/src/test/groovy/org/onap/cps/spi/utils/EscapeUtilsSpec.groovy b/cps-ri/src/test/groovy/org/onap/cps/ri/utils/EscapeUtilsSpec.groovy index 52330e6251..8afd9695a7 100644 --- a/cps-ri/src/test/groovy/org/onap/cps/spi/utils/EscapeUtilsSpec.groovy +++ b/cps-ri/src/test/groovy/org/onap/cps/ri/utils/EscapeUtilsSpec.groovy @@ -18,7 +18,7 @@ * ============LICENSE_END========================================================= */ -package org.onap.cps.spi.utils +package org.onap.cps.ri.utils import spock.lang.Specification diff --git a/cps-ri/src/test/groovy/org/onap/cps/spi/utils/SessionManagerSpec.groovy b/cps-ri/src/test/groovy/org/onap/cps/ri/utils/SessionManagerSpec.groovy index feda338b80..b50a20c124 100644 --- a/cps-ri/src/test/groovy/org/onap/cps/spi/utils/SessionManagerSpec.groovy +++ b/cps-ri/src/test/groovy/org/onap/cps/ri/utils/SessionManagerSpec.groovy @@ -18,20 +18,18 @@ * ============LICENSE_END========================================================= */ -package org.onap.cps.spi.utils +package org.onap.cps.ri.utils import com.google.common.util.concurrent.TimeLimiter import com.google.common.util.concurrent.UncheckedExecutionException import org.hibernate.HibernateException +import org.hibernate.Session import org.hibernate.Transaction -import org.onap.cps.spi.config.CpsSessionFactory -import org.onap.cps.spi.entities.AnchorEntity +import org.onap.cps.ri.models.AnchorEntity +import org.onap.cps.ri.repository.AnchorRepository +import org.onap.cps.ri.repository.DataspaceRepository import org.onap.cps.spi.exceptions.SessionManagerException -import org.onap.cps.spi.repository.AnchorRepository -import org.onap.cps.spi.repository.DataspaceRepository import spock.lang.Specification -import org.hibernate.Session -import java.util.concurrent.ExecutionException class SessionManagerSpec extends Specification { diff --git a/cps-service/pom.xml b/cps-service/pom.xml index 37a45957f3..2a9c75f4a6 100644 --- a/cps-service/pom.xml +++ b/cps-service/pom.xml @@ -70,11 +70,6 @@ <artifactId>gson</artifactId> </dependency> <dependency> - <!-- Hazelcast provide Distributed Caches --> - <groupId>com.hazelcast</groupId> - <artifactId>hazelcast-spring</artifactId> - </dependency> - <dependency> <groupId>io.micrometer</groupId> <artifactId>micrometer-core</artifactId> </dependency> diff --git a/cps-service/src/main/java/org/onap/cps/api/impl/CpsAnchorServiceImpl.java b/cps-service/src/main/java/org/onap/cps/api/impl/CpsAnchorServiceImpl.java index c31e51b174..5ca0fe63d4 100644 --- a/cps-service/src/main/java/org/onap/cps/api/impl/CpsAnchorServiceImpl.java +++ b/cps-service/src/main/java/org/onap/cps/api/impl/CpsAnchorServiceImpl.java @@ -23,10 +23,10 @@ package org.onap.cps.api.impl; import java.util.Collection; import lombok.RequiredArgsConstructor; import org.onap.cps.api.CpsAnchorService; +import org.onap.cps.impl.utils.CpsValidator; import org.onap.cps.spi.CpsAdminPersistenceService; import org.onap.cps.spi.CpsDataPersistenceService; import org.onap.cps.spi.model.Anchor; -import org.onap.cps.spi.utils.CpsValidator; import org.springframework.stereotype.Service; @Service diff --git a/cps-service/src/main/java/org/onap/cps/api/impl/CpsDataServiceImpl.java b/cps-service/src/main/java/org/onap/cps/api/impl/CpsDataServiceImpl.java index 165d62cede..eed4f09bf0 100644 --- a/cps-service/src/main/java/org/onap/cps/api/impl/CpsDataServiceImpl.java +++ b/cps-service/src/main/java/org/onap/cps/api/impl/CpsDataServiceImpl.java @@ -42,6 +42,7 @@ import org.onap.cps.api.CpsDeltaService; import org.onap.cps.cpspath.parser.CpsPathUtil; import org.onap.cps.events.CpsDataUpdateEventsService; import org.onap.cps.events.model.Data.Operation; +import org.onap.cps.impl.utils.CpsValidator; import org.onap.cps.spi.CpsDataPersistenceService; import org.onap.cps.spi.FetchDescendantsOption; import org.onap.cps.spi.exceptions.DataValidationException; @@ -49,7 +50,6 @@ import org.onap.cps.spi.model.Anchor; import org.onap.cps.spi.model.DataNode; import org.onap.cps.spi.model.DataNodeBuilder; import org.onap.cps.spi.model.DeltaReport; -import org.onap.cps.spi.utils.CpsValidator; import org.onap.cps.utils.ContentType; import org.onap.cps.utils.DataMapUtils; import org.onap.cps.utils.JsonObjectMapper; @@ -400,8 +400,7 @@ public class CpsDataServiceImpl implements CpsDataService { private List<Map<String, Object>> prefixResolver(final Anchor anchor, final Collection<DataNode> dataNodes) { final List<Map<String, Object>> prefixToDataNodes = new ArrayList<>(dataNodes.size()); for (final DataNode dataNode: dataNodes) { - final String prefix = prefixResolver - .getPrefix(anchor.getDataspaceName(), anchor.getName(), dataNode.getXpath()); + final String prefix = prefixResolver.getPrefix(anchor, dataNode.getXpath()); final Map<String, Object> prefixToDataNode = DataMapUtils.toDataMapWithIdentifier(dataNode, prefix); prefixToDataNodes.add(prefixToDataNode); } diff --git a/cps-service/src/main/java/org/onap/cps/api/impl/CpsDataspaceServiceImpl.java b/cps-service/src/main/java/org/onap/cps/api/impl/CpsDataspaceServiceImpl.java index a7f5da4874..6bccf2a865 100644 --- a/cps-service/src/main/java/org/onap/cps/api/impl/CpsDataspaceServiceImpl.java +++ b/cps-service/src/main/java/org/onap/cps/api/impl/CpsDataspaceServiceImpl.java @@ -26,9 +26,9 @@ package org.onap.cps.api.impl; import java.util.Collection; import lombok.RequiredArgsConstructor; import org.onap.cps.api.CpsDataspaceService; +import org.onap.cps.impl.utils.CpsValidator; import org.onap.cps.spi.CpsAdminPersistenceService; import org.onap.cps.spi.model.Dataspace; -import org.onap.cps.spi.utils.CpsValidator; import org.springframework.stereotype.Service; @Service diff --git a/cps-service/src/main/java/org/onap/cps/api/impl/CpsDeltaServiceImpl.java b/cps-service/src/main/java/org/onap/cps/api/impl/CpsDeltaServiceImpl.java index 4df3a28145..7819568ae1 100644 --- a/cps-service/src/main/java/org/onap/cps/api/impl/CpsDeltaServiceImpl.java +++ b/cps-service/src/main/java/org/onap/cps/api/impl/CpsDeltaServiceImpl.java @@ -20,7 +20,6 @@ package org.onap.cps.api.impl; - import java.io.Serializable; import java.util.ArrayList; import java.util.Collection; diff --git a/cps-service/src/main/java/org/onap/cps/api/impl/CpsModuleServiceImpl.java b/cps-service/src/main/java/org/onap/cps/api/impl/CpsModuleServiceImpl.java index 34610f3455..a600b22b61 100644 --- a/cps-service/src/main/java/org/onap/cps/api/impl/CpsModuleServiceImpl.java +++ b/cps-service/src/main/java/org/onap/cps/api/impl/CpsModuleServiceImpl.java @@ -30,6 +30,7 @@ import java.util.stream.Collectors; import lombok.RequiredArgsConstructor; import org.onap.cps.api.CpsAnchorService; import org.onap.cps.api.CpsModuleService; +import org.onap.cps.impl.utils.CpsValidator; import org.onap.cps.spi.CascadeDeleteAllowed; import org.onap.cps.spi.CpsModulePersistenceService; import org.onap.cps.spi.exceptions.SchemaSetInUseException; @@ -37,7 +38,6 @@ import org.onap.cps.spi.model.Anchor; 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.utils.CpsValidator; import org.onap.cps.yang.TimedYangTextSchemaSourceSetBuilder; import org.onap.cps.yang.YangTextSchemaSourceSet; import org.springframework.stereotype.Service; diff --git a/cps-service/src/main/java/org/onap/cps/api/impl/CpsQueryServiceImpl.java b/cps-service/src/main/java/org/onap/cps/api/impl/CpsQueryServiceImpl.java index 1d7a7ceeb0..d1c98986e6 100644 --- a/cps-service/src/main/java/org/onap/cps/api/impl/CpsQueryServiceImpl.java +++ b/cps-service/src/main/java/org/onap/cps/api/impl/CpsQueryServiceImpl.java @@ -25,11 +25,11 @@ import io.micrometer.core.annotation.Timed; import java.util.Collection; import lombok.RequiredArgsConstructor; import org.onap.cps.api.CpsQueryService; +import org.onap.cps.impl.utils.CpsValidator; import org.onap.cps.spi.CpsDataPersistenceService; import org.onap.cps.spi.FetchDescendantsOption; import org.onap.cps.spi.PaginationOption; import org.onap.cps.spi.model.DataNode; -import org.onap.cps.spi.utils.CpsValidator; import org.springframework.stereotype.Service; @Service diff --git a/cps-service/src/main/java/org/onap/cps/api/impl/YangTextSchemaSourceSetCache.java b/cps-service/src/main/java/org/onap/cps/api/impl/YangTextSchemaSourceSetCache.java index 4fdae5a307..8b85dfca32 100644 --- a/cps-service/src/main/java/org/onap/cps/api/impl/YangTextSchemaSourceSetCache.java +++ b/cps-service/src/main/java/org/onap/cps/api/impl/YangTextSchemaSourceSetCache.java @@ -27,8 +27,8 @@ import io.micrometer.core.instrument.Metrics; import java.util.Map; import java.util.concurrent.atomic.AtomicInteger; import lombok.RequiredArgsConstructor; +import org.onap.cps.impl.utils.CpsValidator; import org.onap.cps.spi.CpsModulePersistenceService; -import org.onap.cps.spi.utils.CpsValidator; import org.onap.cps.yang.YangTextSchemaSourceSet; import org.onap.cps.yang.YangTextSchemaSourceSetBuilder; import org.springframework.cache.annotation.CacheConfig; diff --git a/cps-service/src/main/java/org/onap/cps/cache/AnchorDataCacheConfig.java b/cps-service/src/main/java/org/onap/cps/cache/AnchorDataCacheConfig.java deleted file mode 100644 index efe19c6cb7..0000000000 --- a/cps-service/src/main/java/org/onap/cps/cache/AnchorDataCacheConfig.java +++ /dev/null @@ -1,45 +0,0 @@ -/* - * ============LICENSE_START======================================================== - * Copyright (C) 2022-2023 Nordix Foundation - * ================================================================================ - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - * SPDX-License-Identifier: Apache-2.0 - * ============LICENSE_END========================================================= - */ - -package org.onap.cps.cache; - -import com.hazelcast.config.MapConfig; -import com.hazelcast.map.IMap; -import org.springframework.context.annotation.Bean; -import org.springframework.context.annotation.Configuration; - -/** - * Core infrastructure of the hazelcast distributed cache for anchor data config use cases. - */ -@Configuration -public class AnchorDataCacheConfig extends HazelcastCacheConfig { - - private static final MapConfig anchorDataCacheMapConfig = createMapConfig("anchorDataCacheMapConfig"); - - /** - * Distributed instance of anchor data cache that contains module prefix by anchor name as properties. - * - * @return configured map of anchor data cache - */ - @Bean - public IMap<String, AnchorDataCacheEntry> anchorDataCache() { - return createHazelcastInstance("hazelCastInstanceCpsCore", anchorDataCacheMapConfig).getMap("anchorDataCache"); - } -} diff --git a/cps-service/src/main/java/org/onap/cps/cache/AnchorDataCacheEntry.java b/cps-service/src/main/java/org/onap/cps/cache/AnchorDataCacheEntry.java deleted file mode 100644 index 41adbdd5dc..0000000000 --- a/cps-service/src/main/java/org/onap/cps/cache/AnchorDataCacheEntry.java +++ /dev/null @@ -1,44 +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.cache; - -import java.io.Serializable; -import java.util.HashMap; -import java.util.Map; - -public class AnchorDataCacheEntry implements Serializable { - - private static final long serialVersionUID = 2111243947810370698L; - - private Map<String, Serializable> properties = new HashMap<>(); - - public Object getProperty(final String propertyName) { - return properties.get(propertyName); - } - - public void setProperty(final String propertyName, final Serializable value) { - properties.put(propertyName, value); - } - - public boolean hasProperty(final String propertyName) { - return properties.containsKey(propertyName); - } -} diff --git a/cps-service/src/main/java/org/onap/cps/spi/utils/CpsValidator.java b/cps-service/src/main/java/org/onap/cps/impl/utils/CpsValidator.java index ceb75c09b2..fd677eb2d2 100644 --- a/cps-service/src/main/java/org/onap/cps/spi/utils/CpsValidator.java +++ b/cps-service/src/main/java/org/onap/cps/impl/utils/CpsValidator.java @@ -18,7 +18,7 @@ * ============LICENSE_END========================================================= */ -package org.onap.cps.spi.utils; +package org.onap.cps.impl.utils; import org.onap.cps.spi.PaginationOption; diff --git a/cps-service/src/main/java/org/onap/cps/utils/PrefixResolver.java b/cps-service/src/main/java/org/onap/cps/utils/PrefixResolver.java index 35dc7347b2..c3097cc552 100644 --- a/cps-service/src/main/java/org/onap/cps/utils/PrefixResolver.java +++ b/cps-service/src/main/java/org/onap/cps/utils/PrefixResolver.java @@ -1,6 +1,6 @@ /* * ============LICENSE_START======================================================= - * Copyright (C) 2022-2023 Nordix Foundation. + * Copyright (C) 2022-2024 Nordix Foundation. * ================================================================================ * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -20,53 +20,25 @@ package org.onap.cps.utils; -import com.hazelcast.map.IMap; -import java.io.Serializable; -import java.util.HashMap; -import java.util.Map; -import java.util.concurrent.TimeUnit; import lombok.RequiredArgsConstructor; -import org.onap.cps.api.CpsAnchorService; import org.onap.cps.api.impl.YangTextSchemaSourceSetCache; -import org.onap.cps.cache.AnchorDataCacheEntry; import org.onap.cps.cpspath.parser.CpsPathPrefixType; import org.onap.cps.cpspath.parser.CpsPathQuery; import org.onap.cps.cpspath.parser.CpsPathUtil; import org.onap.cps.spi.model.Anchor; import org.onap.cps.yang.YangTextSchemaSourceSet; -import org.opendaylight.yangtools.yang.common.QNameModule; +import org.opendaylight.yangtools.yang.common.QName; import org.opendaylight.yangtools.yang.model.api.DataNodeContainer; -import org.opendaylight.yangtools.yang.model.api.DataSchemaNode; import org.opendaylight.yangtools.yang.model.api.Module; import org.opendaylight.yangtools.yang.model.api.SchemaContext; +import org.opendaylight.yangtools.yang.model.api.SchemaNode; import org.springframework.stereotype.Component; @Component @RequiredArgsConstructor public class PrefixResolver { - private static final long ANCHOR_DATA_CACHE_TTL_SECS = TimeUnit.HOURS.toSeconds(1); - - private static final String CACHE_ENTRY_PROPERTY_NAME = "prefixPerContainerName"; - - private final CpsAnchorService cpsAnchorService; - private final YangTextSchemaSourceSetCache yangTextSchemaSourceSetCache; - private final IMap<String, AnchorDataCacheEntry> anchorDataCache; - - /** - * Get the module prefix for the given xpath for a dataspace and anchor name. - * - * @param dataspaceName the name of the dataspace - * @param anchorName the name of the anchor the xpath belongs to - * @param xpath the xpath to prefix a prefix for - * @return the prefix of the module the top level element of given xpath - */ - public String getPrefix(final String dataspaceName, final String anchorName, final String xpath) { - final Anchor anchor = cpsAnchorService.getAnchor(dataspaceName, anchorName); - return getPrefix(anchor, xpath); - } - /** * Get the module prefix for the given xpath under the given anchor. * @@ -75,57 +47,25 @@ public class PrefixResolver { * @return the prefix of the module the top level element of given xpath */ public String getPrefix(final Anchor anchor, final String xpath) { - final Map<String, String> prefixPerContainerName = getPrefixPerContainerName(anchor); - return getPrefixForTopContainer(prefixPerContainerName, xpath); - } - - private Map<String, String> getPrefixPerContainerName(final Anchor anchor) { - if (anchorDataCache.containsKey(anchor.getName())) { - final AnchorDataCacheEntry anchorDataCacheEntry = anchorDataCache.get(anchor.getName()); - if (anchorDataCacheEntry.hasProperty(CACHE_ENTRY_PROPERTY_NAME)) { - return (Map) anchorDataCacheEntry.getProperty(CACHE_ENTRY_PROPERTY_NAME); - } - } - return createAndCachePrefixPerContainerNameMap(anchor); - } - - private String getPrefixForTopContainer(final Map<String, String> prefixPerContainerName, - final String xpath) { final CpsPathQuery cpsPathQuery = CpsPathUtil.getCpsPathQuery(xpath); - if (cpsPathQuery.getCpsPathPrefixType() == CpsPathPrefixType.ABSOLUTE) { - final String topLevelContainerName = cpsPathQuery.getContainerNames().get(0); - if (prefixPerContainerName.containsKey(topLevelContainerName)) { - return prefixPerContainerName.get(topLevelContainerName); - } + if (cpsPathQuery.getCpsPathPrefixType() != CpsPathPrefixType.ABSOLUTE) { + return ""; } - return ""; - } + final String topLevelContainerName = cpsPathQuery.getContainerNames().get(0); - private Map<String, String> createAndCachePrefixPerContainerNameMap(final Anchor anchor) { final YangTextSchemaSourceSet yangTextSchemaSourceSet = - yangTextSchemaSourceSetCache.get(anchor.getDataspaceName(), anchor.getSchemaSetName()); + yangTextSchemaSourceSetCache.get(anchor.getDataspaceName(), anchor.getSchemaSetName()); final SchemaContext schemaContext = yangTextSchemaSourceSet.getSchemaContext(); - final Map<QNameModule, String> prefixPerQNameModule = new HashMap<>(schemaContext.getModules().size()); - for (final Module module : schemaContext.getModules()) { - prefixPerQNameModule.put(module.getQNameModule(), module.getPrefix()); - } - final HashMap<String, String> prefixPerContainerName = new HashMap<>(); - for (final DataSchemaNode dataSchemaNode : schemaContext.getChildNodes()) { - if (dataSchemaNode instanceof DataNodeContainer) { - final String containerName = dataSchemaNode.getQName().getLocalName(); - final String prefix = prefixPerQNameModule.get(dataSchemaNode.getQName().getModule()); - prefixPerContainerName.put(containerName, prefix); - } - } - cachePrefixPerContainerNameMap(anchor.getName(), prefixPerContainerName); - return prefixPerContainerName; - } - private void cachePrefixPerContainerNameMap(final String anchorName, - final Serializable prefixPerContainerName) { - final AnchorDataCacheEntry anchorDataCacheEntry = new AnchorDataCacheEntry(); - anchorDataCacheEntry.setProperty(CACHE_ENTRY_PROPERTY_NAME, prefixPerContainerName); - anchorDataCache.put(anchorName, anchorDataCacheEntry, ANCHOR_DATA_CACHE_TTL_SECS, TimeUnit.SECONDS); + return schemaContext.getChildNodes().stream() + .filter(DataNodeContainer.class::isInstance) + .map(SchemaNode::getQName) + .filter(qname -> qname.getLocalName().equals(topLevelContainerName)) + .findFirst() + .map(QName::getModule) + .flatMap(schemaContext::findModule) + .map(Module::getPrefix) + .orElse(""); } } diff --git a/cps-service/src/test/groovy/org/onap/cps/api/impl/CpsAnchorServiceImplSpec.groovy b/cps-service/src/test/groovy/org/onap/cps/api/impl/CpsAnchorServiceImplSpec.groovy index e58a5024b5..ccf943a470 100644 --- a/cps-service/src/test/groovy/org/onap/cps/api/impl/CpsAnchorServiceImplSpec.groovy +++ b/cps-service/src/test/groovy/org/onap/cps/api/impl/CpsAnchorServiceImplSpec.groovy @@ -20,11 +20,11 @@ package org.onap.cps.api.impl +import org.onap.cps.impl.utils.CpsValidator import org.onap.cps.spi.CpsAdminPersistenceService import org.onap.cps.spi.CpsDataPersistenceService import org.onap.cps.spi.exceptions.ModuleNamesNotFoundException import org.onap.cps.spi.model.Anchor -import org.onap.cps.spi.utils.CpsValidator import spock.lang.Specification class CpsAnchorServiceImplSpec extends Specification { diff --git a/cps-service/src/test/groovy/org/onap/cps/api/impl/CpsDataServiceImplSpec.groovy b/cps-service/src/test/groovy/org/onap/cps/api/impl/CpsDataServiceImplSpec.groovy index a296716b59..9846b30158 100644 --- a/cps-service/src/test/groovy/org/onap/cps/api/impl/CpsDataServiceImplSpec.groovy +++ b/cps-service/src/test/groovy/org/onap/cps/api/impl/CpsDataServiceImplSpec.groovy @@ -23,14 +23,15 @@ package org.onap.cps.api.impl -import com.fasterxml.jackson.databind.ObjectMapper import ch.qos.logback.classic.Level import ch.qos.logback.classic.Logger import ch.qos.logback.core.read.ListAppender +import com.fasterxml.jackson.databind.ObjectMapper import org.onap.cps.TestUtils import org.onap.cps.api.CpsAnchorService import org.onap.cps.api.CpsDeltaService import org.onap.cps.events.CpsDataUpdateEventsService +import org.onap.cps.impl.utils.CpsValidator import org.onap.cps.spi.CpsDataPersistenceService import org.onap.cps.spi.FetchDescendantsOption import org.onap.cps.spi.exceptions.ConcurrencyException @@ -40,12 +41,11 @@ import org.onap.cps.spi.exceptions.SessionManagerException import org.onap.cps.spi.exceptions.SessionTimeoutException import org.onap.cps.spi.model.Anchor import org.onap.cps.spi.model.DataNodeBuilder -import org.onap.cps.spi.utils.CpsValidator import org.onap.cps.utils.ContentType -import org.onap.cps.utils.YangParser -import org.onap.cps.utils.YangParserHelper import org.onap.cps.utils.JsonObjectMapper import org.onap.cps.utils.PrefixResolver +import org.onap.cps.utils.YangParser +import org.onap.cps.utils.YangParserHelper import org.onap.cps.yang.TimedYangTextSchemaSourceSetBuilder import org.onap.cps.yang.YangTextSchemaSourceSet import org.onap.cps.yang.YangTextSchemaSourceSetBuilder @@ -53,6 +53,7 @@ import org.slf4j.LoggerFactory import org.springframework.context.annotation.AnnotationConfigApplicationContext import spock.lang.Shared import spock.lang.Specification + import java.time.OffsetDateTime import static org.onap.cps.events.model.Data.Operation.DELETE diff --git a/cps-service/src/test/groovy/org/onap/cps/api/impl/CpsDataspaceServiceImplSpec.groovy b/cps-service/src/test/groovy/org/onap/cps/api/impl/CpsDataspaceServiceImplSpec.groovy index 8e17594bd1..ac7d4c0aa7 100644 --- a/cps-service/src/test/groovy/org/onap/cps/api/impl/CpsDataspaceServiceImplSpec.groovy +++ b/cps-service/src/test/groovy/org/onap/cps/api/impl/CpsDataspaceServiceImplSpec.groovy @@ -20,9 +20,9 @@ package org.onap.cps.api.impl +import org.onap.cps.impl.utils.CpsValidator import org.onap.cps.spi.CpsAdminPersistenceService import org.onap.cps.spi.model.Dataspace -import org.onap.cps.spi.utils.CpsValidator import spock.lang.Specification class CpsDataspaceServiceImplSpec extends Specification { diff --git a/cps-service/src/test/groovy/org/onap/cps/api/impl/CpsModuleServiceImplSpec.groovy b/cps-service/src/test/groovy/org/onap/cps/api/impl/CpsModuleServiceImplSpec.groovy index 62eba0c397..1831506563 100644 --- a/cps-service/src/test/groovy/org/onap/cps/api/impl/CpsModuleServiceImplSpec.groovy +++ b/cps-service/src/test/groovy/org/onap/cps/api/impl/CpsModuleServiceImplSpec.groovy @@ -23,19 +23,15 @@ package org.onap.cps.api.impl -import org.onap.cps.api.CpsAnchorService - -import static org.onap.cps.spi.CascadeDeleteAllowed.CASCADE_DELETE_ALLOWED -import static org.onap.cps.spi.CascadeDeleteAllowed.CASCADE_DELETE_PROHIBITED - import org.onap.cps.TestUtils +import org.onap.cps.api.CpsAnchorService +import org.onap.cps.impl.utils.CpsValidator import org.onap.cps.spi.CpsModulePersistenceService import org.onap.cps.spi.exceptions.DuplicatedYangResourceException import org.onap.cps.spi.exceptions.ModelValidationException import org.onap.cps.spi.exceptions.SchemaSetInUseException -import org.onap.cps.spi.model.ModuleDefinition -import org.onap.cps.spi.utils.CpsValidator import org.onap.cps.spi.model.Anchor +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.yang.TimedYangTextSchemaSourceSetBuilder @@ -43,6 +39,9 @@ import org.onap.cps.yang.YangTextSchemaSourceSet import org.onap.cps.yang.YangTextSchemaSourceSetBuilder import spock.lang.Specification +import static org.onap.cps.spi.CascadeDeleteAllowed.CASCADE_DELETE_ALLOWED +import static org.onap.cps.spi.CascadeDeleteAllowed.CASCADE_DELETE_PROHIBITED + class CpsModuleServiceImplSpec extends Specification { def mockCpsModulePersistenceService = Mock(CpsModulePersistenceService) diff --git a/cps-service/src/test/groovy/org/onap/cps/api/impl/CpsQueryServiceImplSpec.groovy b/cps-service/src/test/groovy/org/onap/cps/api/impl/CpsQueryServiceImplSpec.groovy index 1ad5017919..3b10669ddb 100644 --- a/cps-service/src/test/groovy/org/onap/cps/api/impl/CpsQueryServiceImplSpec.groovy +++ b/cps-service/src/test/groovy/org/onap/cps/api/impl/CpsQueryServiceImplSpec.groovy @@ -21,10 +21,10 @@ package org.onap.cps.api.impl +import org.onap.cps.impl.utils.CpsValidator import org.onap.cps.spi.CpsDataPersistenceService import org.onap.cps.spi.FetchDescendantsOption import org.onap.cps.spi.PaginationOption -import org.onap.cps.spi.utils.CpsValidator import spock.lang.Specification class CpsQueryServiceImplSpec extends Specification { diff --git a/cps-service/src/test/groovy/org/onap/cps/api/impl/E2ENetworkSliceSpec.groovy b/cps-service/src/test/groovy/org/onap/cps/api/impl/E2ENetworkSliceSpec.groovy index 9e55e8f10a..05c8983fc2 100755 --- a/cps-service/src/test/groovy/org/onap/cps/api/impl/E2ENetworkSliceSpec.groovy +++ b/cps-service/src/test/groovy/org/onap/cps/api/impl/E2ENetworkSliceSpec.groovy @@ -28,13 +28,13 @@ import org.onap.cps.TestUtils import org.onap.cps.api.CpsAnchorService
import org.onap.cps.api.CpsDeltaService
import org.onap.cps.events.CpsDataUpdateEventsService
+import org.onap.cps.impl.utils.CpsValidator
import org.onap.cps.spi.CpsDataPersistenceService
import org.onap.cps.spi.CpsModulePersistenceService
import org.onap.cps.spi.model.Anchor
-import org.onap.cps.spi.utils.CpsValidator
+import org.onap.cps.utils.ContentType
import org.onap.cps.utils.JsonObjectMapper
import org.onap.cps.utils.PrefixResolver
-import org.onap.cps.utils.ContentType
import org.onap.cps.utils.YangParser
import org.onap.cps.utils.YangParserHelper
import org.onap.cps.yang.TimedYangTextSchemaSourceSetBuilder
diff --git a/cps-service/src/test/groovy/org/onap/cps/api/impl/YangTextSchemaSourceSetCacheSpec.groovy b/cps-service/src/test/groovy/org/onap/cps/api/impl/YangTextSchemaSourceSetCacheSpec.groovy index a9f50ee5b0..189e28521b 100644 --- a/cps-service/src/test/groovy/org/onap/cps/api/impl/YangTextSchemaSourceSetCacheSpec.groovy +++ b/cps-service/src/test/groovy/org/onap/cps/api/impl/YangTextSchemaSourceSetCacheSpec.groovy @@ -22,6 +22,7 @@ package org.onap.cps.api.impl import org.onap.cps.TestUtils +import org.onap.cps.impl.utils.CpsValidator import org.onap.cps.spi.CpsModulePersistenceService import org.onap.cps.yang.YangTextSchemaSourceSet import org.onap.cps.yang.YangTextSchemaSourceSetBuilder @@ -34,8 +35,6 @@ import org.springframework.cache.annotation.EnableCaching import org.springframework.cache.caffeine.CaffeineCacheManager import org.springframework.test.context.ContextConfiguration import spock.lang.Specification -import org.onap.cps.spi.utils.CpsValidator - @SpringBootTest @EnableCaching diff --git a/cps-service/src/test/groovy/org/onap/cps/cache/AnchorDataCacheConfigSpec.groovy b/cps-service/src/test/groovy/org/onap/cps/cache/AnchorDataCacheConfigSpec.groovy deleted file mode 100644 index 010308c42f..0000000000 --- a/cps-service/src/test/groovy/org/onap/cps/cache/AnchorDataCacheConfigSpec.groovy +++ /dev/null @@ -1,80 +0,0 @@ -/* - * ============LICENSE_START======================================================= - * Copyright (C) 2022-2023 Nordix Foundation - * ================================================================================ - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - * SPDX-License-Identifier: Apache-2.0 - * ============LICENSE_END========================================================= - */ - -package org.onap.cps.cache - -import com.hazelcast.config.Config -import com.hazelcast.core.Hazelcast -import com.hazelcast.map.IMap -import org.springframework.beans.factory.annotation.Autowired -import org.springframework.boot.test.context.SpringBootTest -import org.springframework.test.context.ContextConfiguration -import spock.lang.Specification - -@SpringBootTest -@ContextConfiguration(classes = [AnchorDataCacheConfig]) -class AnchorDataCacheConfigSpec extends Specification { - - @Autowired - private IMap<String, AnchorDataCacheEntry> anchorDataCache - - def 'Embedded (hazelcast) cache for Anchor Data.'() { - expect: 'system is able to create an instance of the Anchor data cache' - assert null != anchorDataCache - and: 'there is at least 1 instance' - assert Hazelcast.allHazelcastInstances.size() > 0 - and: 'anchorDataCache is present' - assert Hazelcast.allHazelcastInstances.name.contains('hazelCastInstanceCpsCore') - } - - def 'Verify configs for Distributed Caches'(){ - given: 'the Anchor Data Cache config' - def anchorDataCacheConfig = Hazelcast.getHazelcastInstanceByName('hazelCastInstanceCpsCore').config - def anchorDataCacheMapConfig = anchorDataCacheConfig.mapConfigs.get('anchorDataCacheMapConfig') - expect: 'system created instance with correct config' - assert anchorDataCacheConfig.clusterName == 'cps-and-ncmp-test-caches' - assert anchorDataCacheMapConfig.backupCount == 1 - assert anchorDataCacheMapConfig.asyncBackupCount == 0 - } - - def 'Verify deployment network configs for Distributed Caches'() { - given: 'the Anchor Data Cache config' - def anchorDataCacheNetworkConfig = Hazelcast.getHazelcastInstanceByName('hazelCastInstanceCpsCore').config.networkConfig - expect: 'system created instance with correct config' - assert anchorDataCacheNetworkConfig.join.autoDetectionConfig.enabled - assert !anchorDataCacheNetworkConfig.join.kubernetesConfig.enabled - } - - def 'Verify network config'() { - given: 'Synchronization config object and test configuration' - def objectUnderTest = new AnchorDataCacheConfig() - def testConfig = new Config() - when: 'kubernetes properties are enabled' - objectUnderTest.cacheKubernetesEnabled = true - objectUnderTest.cacheKubernetesServiceName = 'test-service-name' - and: 'method called to update the discovery mode' - objectUnderTest.updateDiscoveryMode(testConfig) - then: 'applied properties are reflected' - assert testConfig.networkConfig.join.kubernetesConfig.enabled - assert testConfig.networkConfig.join.kubernetesConfig.properties.get('service-name') == 'test-service-name' - - } - -} diff --git a/cps-service/src/test/groovy/org/onap/cps/cache/AnchorDataCacheEntrySpec.groovy b/cps-service/src/test/groovy/org/onap/cps/cache/AnchorDataCacheEntrySpec.groovy deleted file mode 100644 index f38b45d17c..0000000000 --- a/cps-service/src/test/groovy/org/onap/cps/cache/AnchorDataCacheEntrySpec.groovy +++ /dev/null @@ -1,40 +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.cache - - -import spock.lang.Specification - -class AnchorDataCacheEntrySpec extends Specification { - - def objectUnderTest = new AnchorDataCacheEntry() - - def 'Anchor Data Cache Properties Management.'() { - when: 'a property named "sample" is added to the cache' - objectUnderTest.setProperty('sample', 123) - then: 'the cache has that property' - assert objectUnderTest.hasProperty('sample') - and: 'the value is correct' - assert objectUnderTest.getProperty('sample') == 123 - and: 'the cache does not have an an object called "something else"' - assert objectUnderTest.hasProperty('something else') == false - } -} diff --git a/cps-service/src/test/groovy/org/onap/cps/utils/PrefixResolverSpec.groovy b/cps-service/src/test/groovy/org/onap/cps/utils/PrefixResolverSpec.groovy index b975de6555..13b042f1ae 100644 --- a/cps-service/src/test/groovy/org/onap/cps/utils/PrefixResolverSpec.groovy +++ b/cps-service/src/test/groovy/org/onap/cps/utils/PrefixResolverSpec.groovy @@ -1,6 +1,6 @@ /* * ============LICENSE_START======================================================= - * Copyright (C) 2021-2023 Nordix Foundation + * Copyright (C) 2021-2024 Nordix Foundation * Modifications Copyright (C) 2021 Pantheon.tech * Modifications Copyright (C) 2021-2022 Bell Canada. * ================================================================================ @@ -22,11 +22,8 @@ package org.onap.cps.utils -import com.hazelcast.map.IMap import org.onap.cps.TestUtils -import org.onap.cps.api.CpsAnchorService import org.onap.cps.api.impl.YangTextSchemaSourceSetCache -import org.onap.cps.cache.AnchorDataCacheEntry import org.onap.cps.spi.model.Anchor import org.onap.cps.yang.YangTextSchemaSourceSet import org.onap.cps.yang.YangTextSchemaSourceSetBuilder @@ -34,13 +31,9 @@ import spock.lang.Specification class PrefixResolverSpec extends Specification { - def mockCpsAnchorService = Mock(CpsAnchorService) - def mockYangTextSchemaSourceSetCache = Mock(YangTextSchemaSourceSetCache) - def mockAnchorDataCache = Mock(IMap<String, AnchorDataCacheEntry>) - - def objectUnderTest = new PrefixResolver(mockCpsAnchorService, mockYangTextSchemaSourceSetCache, mockAnchorDataCache) + def objectUnderTest = new PrefixResolver(mockYangTextSchemaSourceSetCache) def mockYangTextSchemaSourceSet = Mock(YangTextSchemaSourceSet) @@ -48,29 +41,16 @@ class PrefixResolverSpec extends Specification { def schemaContext = YangTextSchemaSourceSetBuilder.of(yangResourceNameToContent).getSchemaContext() - def setup() { - given: 'an anchor for the test-tree model' - def anchor = new Anchor(dataspaceName: 'testDataspace', name: 'testAnchor') - and: 'the system can get this anchor' - mockCpsAnchorService.getAnchor('testDataspace', 'testAnchor') >> anchor - and: 'the schema source cache contains the schema context for the test-tree module' - mockYangTextSchemaSourceSet.getSchemaContext() >> schemaContext - } + def anchor = new Anchor(dataspaceName: 'testDataspace', name: 'testAnchor') def 'get xpath prefix using node schema context'() { + given: 'the schema source cache contains the schema context for the test-tree module' + mockYangTextSchemaSourceSet.getSchemaContext() >> schemaContext + mockYangTextSchemaSourceSetCache.get(*_) >> mockYangTextSchemaSourceSet when: 'the prefix of the yang module is retrieved' - def result = objectUnderTest.getPrefix('testDataspace', 'testAnchor', xpath) + def result = objectUnderTest.getPrefix(anchor, xpath) then: 'the expected prefix is returned' result == expectedPrefix - and: 'the cache is updated for the given anchor with a map of prefixes per top level container (just one one this case)' - 1 * mockAnchorDataCache.put('testAnchor',_ , _ ,_) >> { args -> { - def prefixPerContainerName = args[1].getProperty("prefixPerContainerName") - assert prefixPerContainerName.size() == 1 - assert prefixPerContainerName.get('test-tree') == 'tree' - } - } - and: 'schema source cache is used (i.e. need to build schema context)' - 1 * mockYangTextSchemaSourceSetCache.get(*_) >> mockYangTextSchemaSourceSet where: 'the following scenarios are applied' xpath || expectedPrefix '/test-tree' || 'tree' @@ -82,37 +62,4 @@ class PrefixResolverSpec extends Specification { '/not-defined' || '' } - def 'get prefix with populated anchor data cache with #scenario cache entry'() { - given: 'anchor data cache is populated for the anchor with a prefix for top level container named #cachedTopLevelContainerName' - def anchorDataCacheEntry = new AnchorDataCacheEntry() - def prefixPerContainerName = [(cachedTopLevelContainerName): 'cachedPrefix'] - anchorDataCacheEntry.setProperty('prefixPerContainerName',prefixPerContainerName) - mockAnchorDataCache.containsKey('testAnchor') >> true - mockAnchorDataCache.get('testAnchor') >> anchorDataCacheEntry - when: 'the prefix of the yang module is retrieved' - def result = objectUnderTest.getPrefix('testDataspace', 'testAnchor', '/test-tree') - then: 'the expected prefix is returned' - result == expectedPrefix - and: 'schema source cache is not used (i.e. no need to build schema context)' - 0 * mockYangTextSchemaSourceSetCache.get(*_) - where: 'the following scenarios are applied' - scenario | cachedTopLevelContainerName || expectedPrefix - 'matching' | 'test-tree' || 'cachedPrefix' - 'non-matching' | 'other' || '' - } - - def 'get prefix with other (non relevant) data in anchor data cache'() { - given: 'anchor data cache is populated with non relevant other property' - def anchorDataCacheEntry = new AnchorDataCacheEntry() - anchorDataCacheEntry.setProperty('something else', 'does not matter') - mockAnchorDataCache.containsKey('testAnchor') >> true - mockAnchorDataCache.get('testAnchor') >> anchorDataCacheEntry - when: 'the prefix of the yang module is retrieved' - def result = objectUnderTest.getPrefix('testDataspace', 'testAnchor', '/test-tree') - then: 'the expected prefix is returned' - result == 'tree' - and: 'schema source cache is used (i.e. need to build schema context)' - 1 * mockYangTextSchemaSourceSetCache.get(*_) >> mockYangTextSchemaSourceSet - } - } diff --git a/cps-service/src/test/resources/application.yml b/cps-service/src/test/resources/application.yml index b666885f22..be71d37d2a 100644 --- a/cps-service/src/test/resources/application.yml +++ b/cps-service/src/test/resources/application.yml @@ -1,6 +1,6 @@ # ============LICENSE_START======================================================= # Copyright (c) 2021 Bell Canada. -# Modification Copyright (C) 2022 Nordix Foundation. +# Modification Copyright (C) 2022-2024 Nordix Foundation. # ================================================================================ # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. @@ -41,11 +41,3 @@ spring: logging: level: org.apache.kafka: ERROR - -# Custom Hazelcast Config. -hazelcast: - cluster-name: "cps-and-ncmp-test-caches" - mode: - kubernetes: - enabled: false - service-name: "cps-and-ncmp-service" diff --git a/docker-compose/config/nginx/nginx.conf b/docker-compose/config/nginx/nginx.conf index 17fad1b876..61fed515c3 100644 --- a/docker-compose/config/nginx/nginx.conf +++ b/docker-compose/config/nginx/nginx.conf @@ -20,7 +20,9 @@ http { # Add more server entries here for scaling or load balancing upstream cps-and-ncmp { - server cps-and-ncmp:8080; + least_conn; + server docker-compose-cps-and-ncmp-1:8080; + server docker-compose-cps-and-ncmp-2:8080; } server { diff --git a/docs/admin-guide.rst b/docs/admin-guide.rst index 1c4d7455f0..4a40f9b29c 100644 --- a/docs/admin-guide.rst +++ b/docs/admin-guide.rst @@ -111,9 +111,9 @@ Execute CPS service that you want to calculate total elapsed time and log as sho .. code-block:: - 2022-01-28 18:39:17.679 DEBUG [cps-application,e17da1571e518c59,e17da1571e518c59] 11128 --- [tp1901272535-29] o.onap.cps.aop.CpsLoggingAspectService : Execution time of : DataspaceRepository.getByName() with argument[s] = [test42] having result = org.onap.cps.spi.entities.DataspaceEntity@68ded236 :: 205 ms + 2022-01-28 18:39:17.679 DEBUG [cps-application,e17da1571e518c59,e17da1571e518c59] 11128 --- [tp1901272535-29] o.onap.cps.aop.CpsLoggingAspectService : Execution time of : DataspaceRepository.getByName() with argument[s] = [test42] having result = org.onap.cps.impl.models.DataspaceEntity@68ded236 :: 205 ms - 2022-01-28 18:39:17.726 DEBUG [cps-application,e17da1571e518c59,e17da1571e518c59] 11128 --- [tp1901272535-29] o.onap.cps.aop.CpsLoggingAspectService : Execution time of : AnchorRepository.getByDataspaceAndName() with argument[s] = [org.onap.cps.spi.entities.DataspaceEntity@68ded236, bookstore] having result = org.onap.cps.spi.entities.AnchorEntity@71c47fb1 :: 46 ms + 2022-01-28 18:39:17.726 DEBUG [cps-application,e17da1571e518c59,e17da1571e518c59] 11128 --- [tp1901272535-29] o.onap.cps.aop.CpsLoggingAspectService : Execution time of : AnchorRepository.getByDataspaceAndName() with argument[s] = [org.onap.cps.impl.models.DataspaceEntity@68ded236, bookstore] having result = org.onap.cps.impl.models.AnchorEntity@71c47fb1 :: 46 ms 2022-01-28 18:39:17.768 DEBUG [cps-application,e17da1571e518c59,e17da1571e518c59] 11128 --- [tp1901272535-29] o.onap.cps.aop.CpsLoggingAspectService : Execution time of : CpsAdminPersistenceServiceImpl.getAnchor() with argument[s] = [test42, bookstore] having result = Anchor(name=bookstore, dataspaceName=test42, schemaSetName=bookstore) :: 299 ms diff --git a/integration-test/src/test/groovy/org/onap/cps/integration/base/CpsIntegrationSpecBase.groovy b/integration-test/src/test/groovy/org/onap/cps/integration/base/CpsIntegrationSpecBase.groovy index bd53c4ea13..587cbae619 100644 --- a/integration-test/src/test/groovy/org/onap/cps/integration/base/CpsIntegrationSpecBase.groovy +++ b/integration-test/src/test/groovy/org/onap/cps/integration/base/CpsIntegrationSpecBase.groovy @@ -39,10 +39,10 @@ import org.onap.cps.ncmp.impl.inventory.ParameterizedCmHandleQueryService import org.onap.cps.ncmp.impl.inventory.models.CmHandleState import org.onap.cps.ncmp.impl.inventory.sync.ModuleSyncWatchdog import org.onap.cps.ncmp.impl.utils.AlternateIdMatcher +import org.onap.cps.ri.repository.DataspaceRepository +import org.onap.cps.ri.utils.SessionManager import org.onap.cps.spi.exceptions.DataspaceNotFoundException import org.onap.cps.spi.model.DataNode -import org.onap.cps.spi.repository.DataspaceRepository -import org.onap.cps.spi.utils.SessionManager import org.onap.cps.utils.ContentType import org.onap.cps.utils.JsonObjectMapper import org.springframework.beans.factory.annotation.Autowired @@ -72,7 +72,7 @@ import static org.onap.cps.ncmp.impl.inventory.NcmpPersistence.NCMP_DMI_REGISTRY @AutoConfigureMockMvc @EnableJpaRepositories(basePackageClasses = [DataspaceRepository]) @ComponentScan(basePackages = ['org.onap.cps']) -@EntityScan('org.onap.cps.spi.entities') +@EntityScan('org.onap.cps.ri.models') abstract class CpsIntegrationSpecBase extends Specification { @Shared diff --git a/integration-test/src/test/groovy/org/onap/cps/integration/base/PolicyDispatcher.groovy b/integration-test/src/test/groovy/org/onap/cps/integration/base/PolicyDispatcher.groovy index 69792d7ed8..c93a5274e6 100644 --- a/integration-test/src/test/groovy/org/onap/cps/integration/base/PolicyDispatcher.groovy +++ b/integration-test/src/test/groovy/org/onap/cps/integration/base/PolicyDispatcher.groovy @@ -24,10 +24,12 @@ package org.onap.cps.integration.base import okhttp3.mockwebserver.Dispatcher import okhttp3.mockwebserver.MockResponse import okhttp3.mockwebserver.RecordedRequest +import org.springframework.beans.factory.annotation.Value import org.springframework.http.HttpHeaders import org.springframework.http.HttpStatus import org.springframework.http.MediaType import org.testcontainers.shaded.com.fasterxml.jackson.databind.ObjectMapper +import java.util.concurrent.TimeUnit /** * This class simulates responses from the Policy Execution server in NCMP integration tests. @@ -53,11 +55,14 @@ class PolicyDispatcher extends Dispatcher { def targetIdentifier = body.get('requests').get(0).get('data').get('targetIdentifier') def responseAsMap = [:] responseAsMap.put('decisionId',1) + if (targetIdentifier == "mock slow response") { + TimeUnit.SECONDS.sleep(2) // One second more then configured readTimeoutInSeconds + } if (allowAll || targetIdentifier == 'fdn1') { responseAsMap.put('decision','allow') responseAsMap.put('message','') } else { - responseAsMap.put('decision','deny') + responseAsMap.put('decision','deny from mock server (dispatcher)') responseAsMap.put('message','I only like fdn1') } def responseAsString = objectMapper.writeValueAsString(responseAsMap) diff --git a/integration-test/src/test/groovy/org/onap/cps/integration/functional/cps/ModuleServiceIntegrationSpec.groovy b/integration-test/src/test/groovy/org/onap/cps/integration/functional/cps/ModuleServiceIntegrationSpec.groovy index 0e465d84a0..5f4ba3456c 100644 --- a/integration-test/src/test/groovy/org/onap/cps/integration/functional/cps/ModuleServiceIntegrationSpec.groovy +++ b/integration-test/src/test/groovy/org/onap/cps/integration/functional/cps/ModuleServiceIntegrationSpec.groovy @@ -215,7 +215,8 @@ class ModuleServiceIntegrationSpec extends FunctionalSpecBase { when: 'all schema sets are retrieved' def result = objectUnderTest.getSchemaSets(FUNCTIONAL_TEST_DATASPACE_1) then: 'the result contains all expected schema sets' - assert result.name == [ 'bookstoreSchemaSet', 'newSchema1' ] + assert result.name.size() == 2 + assert result.name.containsAll('bookstoreSchemaSet', 'newSchema1') cleanup: objectUnderTest.deleteSchemaSetsWithCascade(FUNCTIONAL_TEST_DATASPACE_1, ['newSchema1']) } diff --git a/integration-test/src/test/groovy/org/onap/cps/integration/functional/cps/SessionManagerIntegrationSpec.groovy b/integration-test/src/test/groovy/org/onap/cps/integration/functional/cps/SessionManagerIntegrationSpec.groovy index 428d5f9014..ad153d6a4a 100644 --- a/integration-test/src/test/groovy/org/onap/cps/integration/functional/cps/SessionManagerIntegrationSpec.groovy +++ b/integration-test/src/test/groovy/org/onap/cps/integration/functional/cps/SessionManagerIntegrationSpec.groovy @@ -21,8 +21,8 @@ package org.onap.cps.integration.functional.cps import org.onap.cps.integration.base.FunctionalSpecBase +import org.onap.cps.ri.utils.SessionManager import org.onap.cps.spi.exceptions.SessionManagerException -import org.onap.cps.spi.utils.SessionManager class SessionManagerIntegrationSpec extends FunctionalSpecBase { diff --git a/integration-test/src/test/groovy/org/onap/cps/integration/functional/ncmp/CmHandleCreateSpec.groovy b/integration-test/src/test/groovy/org/onap/cps/integration/functional/ncmp/CmHandleCreateSpec.groovy index d27badccb2..10a9f15e21 100644 --- a/integration-test/src/test/groovy/org/onap/cps/integration/functional/ncmp/CmHandleCreateSpec.groovy +++ b/integration-test/src/test/groovy/org/onap/cps/integration/functional/ncmp/CmHandleCreateSpec.groovy @@ -180,17 +180,20 @@ class CmHandleCreateSpec extends CpsIntegrationSpecBase { }) when: 'DMI is available for retry' - dmiDispatcher1.moduleNamesPerCmHandleId = ['ch-1': ['M1', 'M2']] + dmiDispatcher1.moduleNamesPerCmHandleId = ['ch-1': ['M1', 'M2'], 'ch-2': ['M1', 'M2']] dmiDispatcher1.isAvailable = true - and: 'the LOCKED CM handle retry time elapses (actually just subtract 3 minutes from handles lastUpdateTime)' - overrideCmHandleLastUpdateTime('ch-1', OffsetDateTime.now().minusMinutes(3)) - then: 'CM-handle goes to READY state' + then: 'Both CM-handles go to READY state' new PollingConditions().within(MODULE_SYNC_WAIT_TIME_IN_SECONDS, () -> { - assert objectUnderTest.getCmHandleCompositeState('ch-1').cmHandleState == CmHandleState.READY + ['ch-1', 'ch-2'].each { cmHandleId -> + assert objectUnderTest.getCmHandleCompositeState(cmHandleId).cmHandleState == CmHandleState.READY + } }) - and: 'CM-handle has expected modules' - assert ['M1', 'M2'] == objectUnderTest.getYangResourcesModuleReferences('ch-1').moduleName.sort() + + and: 'Both CM-handles have expected modules' + ['ch-1', 'ch-2'].each { cmHandleId -> + assert objectUnderTest.getYangResourcesModuleReferences(cmHandleId).moduleName.sort() == ['M1', 'M2'] + } cleanup: 'deregister CM handles' deregisterCmHandles(DMI1_URL, ['ch-1', 'ch-2']) diff --git a/integration-test/src/test/groovy/org/onap/cps/integration/functional/ncmp/PolicyExecutorIntegrationSpec.groovy b/integration-test/src/test/groovy/org/onap/cps/integration/functional/ncmp/PolicyExecutorIntegrationSpec.groovy index 99f245ae8c..1d4d19bee0 100644 --- a/integration-test/src/test/groovy/org/onap/cps/integration/functional/ncmp/PolicyExecutorIntegrationSpec.groovy +++ b/integration-test/src/test/groovy/org/onap/cps/integration/functional/ncmp/PolicyExecutorIntegrationSpec.groovy @@ -20,6 +20,7 @@ package org.onap.cps.integration.functional.ncmp +import com.fasterxml.jackson.databind.ObjectMapper import org.onap.cps.integration.base.CpsIntegrationSpecBase import org.springframework.http.HttpHeaders import org.springframework.http.MediaType @@ -29,18 +30,22 @@ import static org.springframework.test.web.servlet.request.MockMvcRequestBuilder class PolicyExecutorIntegrationSpec extends CpsIntegrationSpecBase { + def objectMapper = new ObjectMapper() + def setup() { // Enable mocked policy executor logic policyDispatcher.allowAll = false; - //minimum setup for 2 cm handles with alternate ids - dmiDispatcher1.moduleNamesPerCmHandleId = ['ch-1': [], 'ch-2': []] + //minimum setup for cm handles with alternate ids + dmiDispatcher1.moduleNamesPerCmHandleId = ['ch-1': [], 'ch-2': [], 'ch-3':[]] registerCmHandle(DMI1_URL, 'ch-1', NO_MODULE_SET_TAG, 'fdn1') registerCmHandle(DMI1_URL, 'ch-2', NO_MODULE_SET_TAG, 'fdn2') + registerCmHandle(DMI1_URL, 'ch-3', NO_MODULE_SET_TAG, 'mock slow response') } def cleanup() { deregisterCmHandle(DMI1_URL, 'ch-1') deregisterCmHandle(DMI1_URL, 'ch-2') + deregisterCmHandle(DMI1_URL, 'ch-3') } def 'Policy Executor create request with #scenario.'() { @@ -53,11 +58,17 @@ class PolicyExecutorIntegrationSpec extends CpsIntegrationSpecBase { .andReturn().response then: 'the expected status code is returned' response.getStatus() == execpectedStatusCode + and: 'when not allowed the response body contains the expected message' + if (expectedMessage!='allow') { + def bodyAsMap = objectMapper.readValue(response.getContentAsByteArray(), Map.class) + assert bodyAsMap.get('message').endsWith(expectedMessage) + } where: 'following parameters are used' - scenario | cmHandle | authorization || execpectedStatusCode - 'accepted cm handle' | 'ch-1' | 'mock expects "ABC"' || 201 - 'un-accepted cm handle' | 'ch-2' | 'mock expects "ABC"' || 409 - 'invalid authorization' | 'ch-1' | 'something else' || 500 + scenario | cmHandle | authorization || execpectedStatusCode || expectedMessage + 'accepted cm handle' | 'ch-1' | 'mock expects "ABC"' || 201 || 'allow' + 'un-accepted cm handle' | 'ch-2' | 'mock expects "ABC"' || 409 || 'deny from mock server (dispatcher)' + 'timeout' | 'ch-3' | 'mock expects "ABC"' || 409 || 'test default decision' + 'invalid authorization' | 'ch-1' | 'something else' || 500 || '401 Unauthorized from POST http://localhost:8790/policy-executor/api/v1/execute' } } diff --git a/integration-test/src/test/resources/application.yml b/integration-test/src/test/resources/application.yml index 760dad01af..793acc6395 100644 --- a/integration-test/src/test/resources/application.yml +++ b/integration-test/src/test/resources/application.yml @@ -215,6 +215,7 @@ ncmp: policy-executor: enabled: true + defaultDecision: "test default decision" server: address: http://localhost port: 8790 @@ -224,7 +225,7 @@ ncmp: maximumConnectionsTotal: 10 pendingAcquireMaxCount: 10 connectionTimeoutInSeconds: 30 - readTimeoutInSeconds: 30 + readTimeoutInSeconds: 1 writeTimeoutInSeconds: 30 hazelcast: diff --git a/k6-tests/ncmp/common/cmhandle-crud.js b/k6-tests/ncmp/common/cmhandle-crud.js index 7fab62abd8..0fea1a44f2 100644 --- a/k6-tests/ncmp/common/cmhandle-crud.js +++ b/k6-tests/ncmp/common/cmhandle-crud.js @@ -66,7 +66,7 @@ function createCmHandlePayload(cmHandleIds) { } function getNumberOfReadyCmHandles() { - const response = executeCmHandleIdSearch('readyCmHandles'); + const response = executeCmHandleIdSearch('cps-path-for-ready-cm-handles'); const arrayOfCmHandleIds = JSON.parse(response.body); return arrayOfCmHandleIds.length; } diff --git a/k6-tests/ncmp/common/search-base.js b/k6-tests/ncmp/common/search-base.js index a6424fe5d0..1e98e9b07b 100644 --- a/k6-tests/ncmp/common/search-base.js +++ b/k6-tests/ncmp/common/search-base.js @@ -36,19 +36,34 @@ function executeSearchRequest(searchType, scenario) { } const SEARCH_PARAMETERS_PER_SCENARIO = { - "module-and-properties": { + "no-filter": { + "cmHandleQueryParameters": [] + }, + "module": { "cmHandleQueryParameters": [ { "conditionName": "hasAllModules", "conditionParameters": [{"moduleName": "ietf-yang-types"}] - }, + } + ] + }, + "property": { + "cmHandleQueryParameters": [ { "conditionName": "hasAllProperties", "conditionParameters": [{"Color": "yellow"}] } ] }, - "readyCmHandles": { + "trust-level": { + "cmHandleQueryParameters": [ + { + "conditionName": "cmHandleWithTrustLevel", + "conditionParameters": [ {"trustLevel": "COMPLETE"} ] + } + ] + }, + "cps-path-for-ready-cm-handles": { "cmHandleQueryParameters": [ { "conditionName": "cmHandleWithCpsPath", diff --git a/k6-tests/ncmp/common/utils.js b/k6-tests/ncmp/common/utils.js index 8ee6d10123..b52866c3fb 100644 --- a/k6-tests/ncmp/common/utils.js +++ b/k6-tests/ncmp/common/utils.js @@ -87,8 +87,16 @@ export function makeCustomSummaryReport(testResults, scenarioConfig) { makeSummaryCsvLine('0', 'HTTP request failures for all tests', 'rate of failed requests', 'http_req_failed', 0, testResults, scenarioConfig), makeSummaryCsvLine('1', 'Registration of CM-handles', 'CM-handles/second', 'cmhandles_created_per_second', 110, testResults, scenarioConfig), makeSummaryCsvLine('2', 'De-registration of CM-handles', 'CM-handles/second', 'cmhandles_deleted_per_second', 80, testResults, scenarioConfig), - makeSummaryCsvLine('3', 'CM-handle ID search with Module and Property filter', 'milliseconds', 'id_search_duration', 4000, testResults, scenarioConfig), - makeSummaryCsvLine('4', 'CM-handle search with Module and Property filter', 'milliseconds', 'cm_search_duration', 30000, testResults, scenarioConfig), + makeSummaryCsvLine('3a', 'CM-handle ID search with No filter', 'milliseconds', 'id_search_nofilter_duration', 4000, testResults, scenarioConfig), + makeSummaryCsvLine('3b', 'CM-handle ID search with Module filter', 'milliseconds', 'id_search_module_duration', 4000, testResults, scenarioConfig), + makeSummaryCsvLine('3c', 'CM-handle ID search with Property filter', 'milliseconds', 'id_search_property_duration', 4000, testResults, scenarioConfig), + makeSummaryCsvLine('3d', 'CM-handle ID search with Cps Path filter', 'milliseconds', 'id_search_cpspath_duration', 4000, testResults, scenarioConfig), + makeSummaryCsvLine('3e', 'CM-handle ID search with Trust Level filter', 'milliseconds', 'id_search_trustlevel_duration', 4000, testResults, scenarioConfig), + makeSummaryCsvLine('4a', 'CM-handle search with No filter', 'milliseconds', 'cm_search_nofilter_duration', 30000, testResults, scenarioConfig), + makeSummaryCsvLine('4b', 'CM-handle search with Module filter', 'milliseconds', 'cm_search_module_duration', 30000, testResults, scenarioConfig), + makeSummaryCsvLine('4c', 'CM-handle search with Property filter', 'milliseconds', 'cm_search_property_duration', 30000, testResults, scenarioConfig), + makeSummaryCsvLine('4d', 'CM-handle search with Cps Path filter', 'milliseconds', 'cm_search_cpspath_duration', 30000, testResults, scenarioConfig), + makeSummaryCsvLine('4e', 'CM-handle search with Trust Level filter', 'milliseconds', 'cm_search_trustlevel_duration', 30000, testResults, scenarioConfig), makeSummaryCsvLine('5a', 'NCMP overhead for Synchronous single CM-handle pass-through read', 'milliseconds', 'ncmp_overhead_passthrough_read', 40, testResults, scenarioConfig), makeSummaryCsvLine('5b', 'NCMP overhead for Synchronous single CM-handle pass-through read with alternate id', 'milliseconds', 'ncmp_overhead_passthrough_read_alt_id', 60, testResults, scenarioConfig), makeSummaryCsvLine('6a', 'NCMP overhead for Synchronous single CM-handle pass-through write', 'milliseconds', 'ncmp_overhead_passthrough_write', 30, testResults, scenarioConfig), diff --git a/k6-tests/ncmp/ncmp-kpi.js b/k6-tests/ncmp/ncmp-kpi.js index 05500a60a3..8815511bdd 100644 --- a/k6-tests/ncmp/ncmp-kpi.js +++ b/k6-tests/ncmp/ncmp-kpi.js @@ -37,8 +37,16 @@ let passthroughReadNcmpOverheadTrend = new Trend('ncmp_overhead_passthrough_read let passthroughReadNcmpOverheadTrendWithAlternateId = new Trend('ncmp_overhead_passthrough_read_alt_id', true); let passthroughWriteNcmpOverheadTrend = new Trend('ncmp_overhead_passthrough_write', true); let passthroughWriteNcmpOverheadTrendWithAlternateId = new Trend('ncmp_overhead_passthrough_write_alt_id', true); -let idSearchDurationTrend = new Trend('id_search_duration', true); -let cmSearchDurationTrend = new Trend('cm_search_duration', true); +let idSearchNoFilterDurationTrend = new Trend('id_search_nofilter_duration', true); +let idSearchModuleDurationTrend = new Trend('id_search_module_duration', true); +let idSearchPropertyDurationTrend = new Trend('id_search_property_duration', true); +let idSearchCpsPathDurationTrend = new Trend('id_search_cpspath_duration', true); +let idSearchTrustLevelDurationTrend = new Trend('id_search_trustlevel_duration', true); +let cmSearchNoFilterDurationTrend = new Trend('cm_search_nofilter_duration', true); +let cmSearchModuleDurationTrend = new Trend('cm_search_module_duration', true); +let cmSearchPropertyDurationTrend = new Trend('cm_search_property_duration', true); +let cmSearchCpsPathDurationTrend = new Trend('cm_search_cpspath_duration', true); +let cmSearchTrustLevelDurationTrend = new Trend('cm_search_trustlevel_duration', true); let legacyBatchReadCmHandlesPerSecondTrend = new Trend('legacy_batch_read_cmhandles_per_second', false); const legacyBatchEventReader = new Reader({ @@ -47,6 +55,7 @@ const legacyBatchEventReader = new Reader({ }); const DURATION = '15m'; +const LEGACY_BATCH_THROUGHPUT_TEST_START_TIME = '15m30s'; export const options = { setupTimeout: '8m', @@ -55,37 +64,85 @@ export const options = { passthrough_read_scenario: { executor: 'constant-vus', exec: 'passthroughReadScenario', - vus: 4, + vus: 2, duration: DURATION, }, passthrough_read_alt_id_scenario: { executor: 'constant-vus', exec: 'passthroughReadAltIdScenario', - vus: 4, + vus: 2, duration: DURATION, }, passthrough_write_scenario: { executor: 'constant-vus', exec: 'passthroughWriteScenario', - vus: 4, + vus: 2, duration: DURATION, }, passthrough_write_alt_id_scenario: { executor: 'constant-vus', exec: 'passthroughWriteAltIdScenario', - vus: 4, + vus: 2, + duration: DURATION, + }, + cm_handle_id_search_nofilter_scenario: { + executor: 'constant-vus', + exec: 'cmHandleIdSearchNoFilterScenario', + vus: 1, + duration: DURATION, + }, + cm_handle_search_nofilter_scenario: { + executor: 'constant-vus', + exec: 'cmHandleSearchNoFilterScenario', + vus: 1, + duration: DURATION, + }, + cm_handle_id_search_module_scenario: { + executor: 'constant-vus', + exec: 'cmHandleIdSearchModuleScenario', + vus: 1, + duration: DURATION, + }, + cm_handle_search_module_scenario: { + executor: 'constant-vus', + exec: 'cmHandleSearchModuleScenario', + vus: 1, + duration: DURATION, + }, + cm_handle_id_search_property_scenario: { + executor: 'constant-vus', + exec: 'cmHandleIdSearchPropertyScenario', + vus: 1, + duration: DURATION, + }, + cm_handle_search_property_scenario: { + executor: 'constant-vus', + exec: 'cmHandleSearchPropertyScenario', + vus: 1, + duration: DURATION, + }, + cm_handle_id_search_cpspath_scenario: { + executor: 'constant-vus', + exec: 'cmHandleIdSearchCpsPathScenario', + vus: 1, + duration: DURATION, + }, + cm_handle_search_cpspath_scenario: { + executor: 'constant-vus', + exec: 'cmHandleSearchCpsPathScenario', + vus: 1, duration: DURATION, }, - cm_handle_id_search_scenario: { + cm_handle_id_search_trustlevel_scenario: { executor: 'constant-vus', - exec: 'cmHandleIdSearchScenario', - vus: 5, + exec: 'cmHandleIdSearchTrustLevelScenario', + vus: 1, duration: DURATION, }, - cm_handle_search_scenario: { + cm_handle_search_trustlevel_scenario: { executor: 'constant-vus', - exec: 'cmHandleSearchScenario', - vus: 5, + exec: 'cmHandleSearchTrustLevelScenario', + vus: 1, duration: DURATION, }, legacy_batch_produce_scenario: { @@ -93,14 +150,14 @@ export const options = { exec: 'legacyBatchProduceScenario', vus: 2, iterations: LEGACY_BATCH_THROUGHPUT_TEST_NUMBER_OF_REQUESTS, - maxDuration: DURATION, + startTime: LEGACY_BATCH_THROUGHPUT_TEST_START_TIME, }, legacy_batch_consume_scenario: { executor: 'per-vu-iterations', exec: 'legacyBatchConsumeScenario', vus: 1, iterations: 1, - maxDuration: DURATION, + startTime: LEGACY_BATCH_THROUGHPUT_TEST_START_TIME, } }, thresholds: { @@ -111,8 +168,16 @@ export const options = { 'ncmp_overhead_passthrough_write': ['avg <= 40'], 'ncmp_overhead_passthrough_read_alt_id': ['avg <= 40'], 'ncmp_overhead_passthrough_write_alt_id': ['avg <= 40'], - 'id_search_duration': ['avg <= 2000'], - 'cm_search_duration': ['avg <= 15000'], + 'id_search_nofilter_duration': ['avg <= 2000'], + 'id_search_module_duration': ['avg <= 2000'], + 'id_search_property_duration': ['avg <= 2000'], + 'id_search_cpspath_duration': ['avg <= 2000'], + 'id_search_trustlevel_duration': ['avg <= 2000'], + 'cm_search_nofilter_duration': ['avg <= 15000'], + 'cm_search_module_duration': ['avg <= 15000'], + 'cm_search_property_duration': ['avg <= 15000'], + 'cm_search_cpspath_duration': ['avg <= 15000'], + 'cm_search_trustlevel_duration': ['avg <= 15000'], 'legacy_batch_read_cmhandles_per_second': ['avg >= 150'], }, }; @@ -187,19 +252,83 @@ export function passthroughWriteAltIdScenario() { } } -export function cmHandleIdSearchScenario() { - const response = executeCmHandleIdSearch('module-and-properties'); - if (check(response, { 'CM handle ID search status equals 200': (r) => r.status === 200 }) - && check(response, { 'CM handle ID search returned expected CM-handles': (r) => r.json('#') === TOTAL_CM_HANDLES })) { - idSearchDurationTrend.add(response.timings.duration); +export function cmHandleIdSearchNoFilterScenario() { + const response = executeCmHandleIdSearch('no-filter'); + if (check(response, { 'CM handle ID no-filter search status equals 200': (r) => r.status === 200 }) + && check(response, { 'CM handle ID no-filter search returned expected CM-handles': (r) => r.json('#') === TOTAL_CM_HANDLES })) { + idSearchNoFilterDurationTrend.add(response.timings.duration); + } +} + +export function cmHandleSearchNoFilterScenario() { + const response = executeCmHandleSearch('no-filter'); + if (check(response, { 'CM handle no-filter search status equals 200': (r) => r.status === 200 }) + && check(response, { 'CM handle no-filter search returned expected CM-handles': (r) => r.json('#') === TOTAL_CM_HANDLES })) { + cmSearchNoFilterDurationTrend.add(response.timings.duration); + } +} + +export function cmHandleIdSearchModuleScenario() { + const response = executeCmHandleIdSearch('module'); + if (check(response, { 'CM handle ID module search status equals 200': (r) => r.status === 200 }) + && check(response, { 'CM handle ID module search returned expected CM-handles': (r) => r.json('#') === TOTAL_CM_HANDLES })) { + idSearchModuleDurationTrend.add(response.timings.duration); + } +} + +export function cmHandleSearchModuleScenario() { + const response = executeCmHandleSearch('module'); + if (check(response, { 'CM handle module search status equals 200': (r) => r.status === 200 }) + && check(response, { 'CM handle module search returned expected CM-handles': (r) => r.json('#') === TOTAL_CM_HANDLES })) { + cmSearchModuleDurationTrend.add(response.timings.duration); + } +} + +export function cmHandleIdSearchPropertyScenario() { + const response = executeCmHandleIdSearch('property'); + if (check(response, { 'CM handle ID property search status equals 200': (r) => r.status === 200 }) + && check(response, { 'CM handle ID property search returned expected CM-handles': (r) => r.json('#') === TOTAL_CM_HANDLES })) { + idSearchPropertyDurationTrend.add(response.timings.duration); + } +} + +export function cmHandleSearchPropertyScenario() { + const response = executeCmHandleSearch('property'); + if (check(response, { 'CM handle property search status equals 200': (r) => r.status === 200 }) + && check(response, { 'CM handle property search returned expected CM-handles': (r) => r.json('#') === TOTAL_CM_HANDLES })) { + cmSearchPropertyDurationTrend.add(response.timings.duration); + } +} + +export function cmHandleIdSearchCpsPathScenario() { + const response = executeCmHandleIdSearch('cps-path-for-ready-cm-handles'); + if (check(response, { 'CM handle ID cps path search status equals 200': (r) => r.status === 200 }) + && check(response, { 'CM handle ID cps path search returned expected CM-handles': (r) => r.json('#') === TOTAL_CM_HANDLES })) { + idSearchCpsPathDurationTrend.add(response.timings.duration); + } +} + +export function cmHandleSearchCpsPathScenario() { + const response = executeCmHandleSearch('cps-path-for-ready-cm-handles'); + if (check(response, { 'CM handle cps path search status equals 200': (r) => r.status === 200 }) + && check(response, { 'CM handle cps path search returned expected CM-handles': (r) => r.json('#') === TOTAL_CM_HANDLES })) { + cmSearchCpsPathDurationTrend.add(response.timings.duration); + } +} + +export function cmHandleIdSearchTrustLevelScenario() { + const response = executeCmHandleIdSearch('trust-level'); + if (check(response, { 'CM handle ID trust level search status equals 200': (r) => r.status === 200 }) + && check(response, { 'CM handle ID trust level search returned expected CM-handles': (r) => r.json('#') === TOTAL_CM_HANDLES })) { + idSearchTrustLevelDurationTrend.add(response.timings.duration); } } -export function cmHandleSearchScenario() { - const response = executeCmHandleSearch('module-and-properties'); - if (check(response, { 'CM handle search status equals 200': (r) => r.status === 200 }) - && check(response, { 'CM handle search returned expected CM-handles': (r) => r.json('#') === TOTAL_CM_HANDLES })) { - cmSearchDurationTrend.add(response.timings.duration); +export function cmHandleSearchTrustLevelScenario() { + const response = executeCmHandleSearch('trust-level'); + if (check(response, { 'CM handle trust level search status equals 200': (r) => r.status === 200 }) + && check(response, { 'CM handle trust level search returned expected CM-handles': (r) => r.json('#') === TOTAL_CM_HANDLES })) { + cmSearchTrustLevelDurationTrend.add(response.timings.duration); } } |