diff options
5 files changed, 241 insertions, 7 deletions
diff --git a/cps-ncmp-service/pom.xml b/cps-ncmp-service/pom.xml index 45112de173..99a024e65d 100644 --- a/cps-ncmp-service/pom.xml +++ b/cps-ncmp-service/pom.xml @@ -31,6 +31,9 @@ </parent> <artifactId>cps-ncmp-service</artifactId> + <properties> + <minimum-coverage>0.93</minimum-coverage> + </properties> <dependencies> <dependency> diff --git a/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/operations/YangModelCmHandleRetriever.java b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/operations/YangModelCmHandleRetriever.java index 5063e8298a..0edd68c3d5 100644 --- a/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/operations/YangModelCmHandleRetriever.java +++ b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/operations/YangModelCmHandleRetriever.java @@ -1,6 +1,7 @@ /* * ============LICENSE_START======================================================= * Copyright (C) 2021-2022 Nordix Foundation + * Modifications Copyright (C) 2021 Bell Canada * ================================================================================ * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -25,6 +26,8 @@ import java.util.Map; import lombok.AllArgsConstructor; import org.onap.cps.api.CpsDataService; import org.onap.cps.ncmp.api.impl.yangmodels.YangModelCmHandle; +import org.onap.cps.ncmp.api.inventory.CompositeState; +import org.onap.cps.ncmp.api.inventory.CompositeStateBuilder; import org.onap.cps.ncmp.api.models.NcmpServiceCmHandle; import org.onap.cps.spi.FetchDescendantsOption; import org.onap.cps.spi.model.DataNode; @@ -53,7 +56,7 @@ public class YangModelCmHandleRetriever { final DataNode cmHandleDataNode = getCmHandleDataNode(cmHandleId); final NcmpServiceCmHandle ncmpServiceCmHandle = new NcmpServiceCmHandle(); ncmpServiceCmHandle.setCmHandleId(cmHandleId); - populateCmHandleProperties(cmHandleDataNode, ncmpServiceCmHandle); + populateCmHandleDetails(cmHandleDataNode, ncmpServiceCmHandle); return YangModelCmHandle.toYangModelCmHandle( (String) cmHandleDataNode.getLeaves().get("dmi-service-name"), (String) cmHandleDataNode.getLeaves().get("dmi-data-service-name"), @@ -70,19 +73,24 @@ public class YangModelCmHandleRetriever { FetchDescendantsOption.INCLUDE_ALL_DESCENDANTS); } - private static void populateCmHandleProperties(final DataNode cmHandleDataNode, + private static void populateCmHandleDetails(final DataNode cmHandleDataNode, final NcmpServiceCmHandle ncmpServiceCmHandle) { final Map<String, String> dmiProperties = new LinkedHashMap<>(); final Map<String, String> publicProperties = new LinkedHashMap<>(); + final CompositeStateBuilder compositeStateBuilder = new CompositeStateBuilder(); + CompositeState compositeState = compositeStateBuilder.build(); for (final DataNode childDataNode: cmHandleDataNode.getChildDataNodes()) { if (childDataNode.getXpath().contains("/additional-properties[@name=")) { addProperty(childDataNode, dmiProperties); } else if (childDataNode.getXpath().contains("/public-properties[@name=")) { addProperty(childDataNode, publicProperties); + } else if (childDataNode.getXpath().endsWith("/state")) { + compositeState = compositeStateBuilder.fromDataNode(childDataNode).build(); } } ncmpServiceCmHandle.setDmiProperties(dmiProperties); ncmpServiceCmHandle.setPublicProperties(publicProperties); + ncmpServiceCmHandle.setCompositeState(compositeState); } private static void addProperty(final DataNode propertyDataNode, final Map<String, String> propertiesAsMap) { diff --git a/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/inventory/CompositeStateBuilder.java b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/inventory/CompositeStateBuilder.java new file mode 100644 index 0000000000..d8f7080311 --- /dev/null +++ b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/inventory/CompositeStateBuilder.java @@ -0,0 +1,147 @@ +/* + * ============LICENSE_START======================================================= + * Copyright (C) 2022 Bell Canada + * ================================================================================ + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * SPDX-License-Identifier: Apache-2.0 + * ============LICENSE_END========================================================= + */ + +package org.onap.cps.ncmp.api.inventory; + +import org.onap.cps.ncmp.api.inventory.CompositeState.DataStores; +import org.onap.cps.ncmp.api.inventory.CompositeState.LockReason; +import org.onap.cps.ncmp.api.inventory.CompositeState.Operational; +import org.onap.cps.ncmp.api.inventory.CompositeState.Running; +import org.onap.cps.spi.model.DataNode; + +public class CompositeStateBuilder { + + private CmHandleState cmHandleState; + private LockReason lockReason; + private DataStores datastores; + private String lastUpdatedTime; + + /** + * To create the {@link CompositeState}. + * + * @return {@link DataNode} + */ + public CompositeState build() { + final CompositeState compositeState = new CompositeState(); + compositeState.setCmhandleState(cmHandleState); + compositeState.setLockReason(lockReason); + compositeState.setDataStores(datastores); + compositeState.setLastUpdateTime(lastUpdatedTime); + return compositeState; + } + + /** + * To use attributes for creating {@link CompositeState}. + * + * @param cmHandleState for the data node + * @return CompositeStateBuilder + */ + public CompositeStateBuilder withCmHandleState(final CmHandleState cmHandleState) { + this.cmHandleState = cmHandleState; + return this; + } + + /** + * To use attributes for creating {@link CompositeState}. + * + * @param reason for the locked state + * @param details for the locked state + * @return CompositeStateBuilder + */ + public CompositeStateBuilder withLockReason(final String reason, final String details) { + this.lockReason = LockReason.builder().reason(reason).details(details).build(); + return this; + } + + /** + * To use attributes for creating {@link CompositeState}. + * + * @param time for the state change + * @return CompositeStateBuilder + */ + public CompositeStateBuilder withLastUpdatedTime(final String time) { + this.lastUpdatedTime = time; + return this; + } + + /** + * To use attributes for creating {@link CompositeState}. + * + * @param syncState for the locked state + * @param lastSyncTime for the locked state + * @return CompositeStateBuilder + */ + public CompositeStateBuilder withOperationalDataStores(final String syncState, final String lastSyncTime) { + this.datastores = DataStores.builder().operationalDataStore( + Operational.builder().syncState(syncState).lastSyncTime(lastSyncTime).build()).build(); + return this; + } + + /** + * To use attributes for creating {@link CompositeState}. + * + * @param syncState for the locked state + * @param lastSyncTime for the locked state + * @return CompositeStateBuilder + */ + public CompositeStateBuilder withRunningDataStores(final String syncState, final String lastSyncTime) { + this.datastores = DataStores.builder().runningDataStore( + Running.builder().syncState(syncState).lastSyncTime(lastSyncTime).build()).build(); + return this; + } + + /** + * To use dataNode for creating {@link CompositeState}. + * + * @param dataNode for the dataNode + * @return CompositeState + */ + public CompositeStateBuilder fromDataNode(final DataNode dataNode) { + this.cmHandleState = CmHandleState.valueOf((String) dataNode.getLeaves() + .get("cm-handle-state")); + for (final DataNode stateChildNode : dataNode.getChildDataNodes()) { + if (stateChildNode.getXpath().endsWith("/lock-reason")) { + this.lockReason = new LockReason((String) stateChildNode.getLeaves().get("reason"), + (String) stateChildNode.getLeaves().get("details")); + } + if (stateChildNode.getXpath().endsWith("/datastores")) { + for (final DataNode dataStoreNodes : stateChildNode.getChildDataNodes()) { + Operational operationalDataStore = null; + Running runningDataStore = null; + if (dataStoreNodes.getXpath().contains("/operational")) { + operationalDataStore = Operational.builder() + .syncState((String) dataStoreNodes.getLeaves().get("sync-state")) + .lastSyncTime((String) dataStoreNodes.getLeaves().get("last-sync-time")) + .build(); + } else { + runningDataStore = Running.builder() + .syncState((String) dataStoreNodes.getLeaves().get("sync-state")) + .lastSyncTime((String) dataStoreNodes.getLeaves().get("last-sync-time")) + .build(); + } + this.datastores = DataStores.builder().operationalDataStore(operationalDataStore) + .runningDataStore(runningDataStore).build(); + } + } + } + return this; + } + +} diff --git a/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/api/impl/operations/YangModelCmHandleRetrieverSpec.groovy b/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/api/impl/operations/YangModelCmHandleRetrieverSpec.groovy index 5ecc8b0da3..7fbfa779dc 100644 --- a/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/api/impl/operations/YangModelCmHandleRetrieverSpec.groovy +++ b/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/api/impl/operations/YangModelCmHandleRetrieverSpec.groovy @@ -22,6 +22,8 @@ package org.onap.cps.ncmp.api.impl.operations import org.onap.cps.api.CpsDataService import org.onap.cps.ncmp.api.impl.yangmodels.YangModelCmHandle +import org.onap.cps.ncmp.api.inventory.CmHandleState +import org.onap.cps.ncmp.api.inventory.CompositeStateBuilder import org.onap.cps.spi.exceptions.DataValidationException import spock.lang.Shared @@ -40,6 +42,9 @@ class YangModelCmHandleRetrieverSpec extends Specification { def xpath = "/dmi-registry/cm-handles[@id='some-cm-handle']" @Shared + def compositeState = new CompositeStateBuilder().withCmHandleState(CmHandleState.ADVISED).build() + + @Shared def childDataNodesForCmHandleWithAllProperties = [new DataNode(xpath: "/dmi-registry/cm-handles[@id='some cm handle']/additional-properties[@name='name1']", leaves: ["name":"name1", "value":"value1"]), new DataNode(xpath: "/dmi-registry/cm-handles[@id='some cm handle']/public-properties[@name='name2']", leaves: ["name":"name2","value":"value2"])] @@ -49,6 +54,9 @@ class YangModelCmHandleRetrieverSpec extends Specification { @Shared def childDataNodesForCmHandleWithPublicProperties = [new DataNode(xpath: "/dmi-registry/cm-handles[@id='some-cm-handle']/public-properties[@name='name2']", leaves: ["name":"name2","value":"value2"])] + @Shared + def childDataNodesForCmHandleWithState = [new DataNode(xpath: "/dmi-registry/cm-handles[@id='some-cm-handle']/state", leaves: ['cm-handle-state': 'ADVISED'])] + def "Retrieve CmHandle using datanode with #scenario."() { given: 'the cps data service returns a data node from the DMI registry' def dataNode = new DataNode(childDataNodes:childDataNodes, leaves: leaves) @@ -63,12 +71,15 @@ class YangModelCmHandleRetrieverSpec extends Specification { and: 'the expected DMI properties' result.dmiProperties == expectedDmiProperties result.publicProperties == expectedPublicProperties + and: 'the state details are returned' + result.compositeState.cmhandleState == expectedCompositeState where: 'the following parameters are used' - scenario | childDataNodes || expectedDmiProperties || expectedPublicProperties - 'no properties' | [] || [] || [] - 'DMI and public properties' | childDataNodesForCmHandleWithAllProperties || [new YangModelCmHandle.Property("name1", "value1")] || [new YangModelCmHandle.Property("name2", "value2")] - 'just DMI properties' | childDataNodesForCmHandleWithDMIProperties || [new YangModelCmHandle.Property("name1", "value1")] || [] - 'just public properties' | childDataNodesForCmHandleWithPublicProperties || [] || [new YangModelCmHandle.Property("name2", "value2")] + scenario | childDataNodes || expectedDmiProperties || expectedPublicProperties || expectedCompositeState + 'no properties' | [] || [] || [] || null + 'DMI and public properties' | childDataNodesForCmHandleWithAllProperties || [new YangModelCmHandle.Property("name1", "value1")] || [new YangModelCmHandle.Property("name2", "value2")] || null + 'just DMI properties' | childDataNodesForCmHandleWithDMIProperties || [new YangModelCmHandle.Property("name1", "value1")] || [] || null + 'just public properties' | childDataNodesForCmHandleWithPublicProperties || [] || [new YangModelCmHandle.Property("name2", "value2")] || null + 'with state details' | childDataNodesForCmHandleWithState || [] || [] || CmHandleState.ADVISED } def "Retrieve CmHandle using datanode with invalid CmHandle id."() { diff --git a/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/api/inventory/CompositeStateBuilderSpec.groovy b/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/api/inventory/CompositeStateBuilderSpec.groovy new file mode 100644 index 0000000000..2be523915e --- /dev/null +++ b/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/api/inventory/CompositeStateBuilderSpec.groovy @@ -0,0 +1,65 @@ +/* + * ============LICENSE_START======================================================= + * Copyright (C) 2022 Bell Canada + * ================================================================================ + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * SPDX-License-Identifier: Apache-2.0 + * ============LICENSE_END========================================================= + */ + +package org.onap.cps.ncmp.api.inventory + +import org.onap.cps.spi.model.DataNode +import org.onap.cps.spi.model.DataNodeBuilder +import spock.lang.Specification + +import java.time.OffsetDateTime +import java.time.ZoneOffset +import java.time.format.DateTimeFormatter + +class CompositeStateBuilderSpec extends Specification { + + def formattedDateAndTime = DateTimeFormatter.ofPattern("yyyy-MM-dd'T'HH:mm:ss.SSSZ") + .format(OffsetDateTime.of(2022, 12, 31, 20, 30, 40, 1, ZoneOffset.UTC)) + + def static cmHandleId = 'myHandle1' + def static cmHandleXpath = "/dmi-registry/cm-handles[@id='${cmHandleId}/state']" + def static stateDataNodes = [new DataNodeBuilder().withXpath("/dmi-registry/cm-handles[@id='${cmHandleId}']/state/lock-reason") + .withLeaves(['reason': 'lock reason', 'details': 'lock details']).build(), + new DataNodeBuilder().withXpath("/dmi-registry/cm-handles[@id='${cmHandleId}']/state/datastores") + .withChildDataNodes(Arrays.asList(new DataNodeBuilder() + .withXpath("/dmi-registry/cm-handles[@id='${cmHandleId}']/state/datastores/operational") + .withLeaves(['sync-state': 'UNSYNCHRONIZED']).build())).build()] + def static cmHandleDataNode = new DataNode(xpath: cmHandleXpath, childDataNodes: stateDataNodes, leaves: ['cm-handle-state': 'ADVISED']) + + def "Composite State Specification"() { + when: 'using composite state builder ' + def compositeState = new CompositeStateBuilder().withCmHandleState(CmHandleState.ADVISED) + .withLockReason("lock-reason","").withOperationalDataStores("UNSYNCHRONIZED", + formattedDateAndTime.toString()).withLastUpdatedTime(formattedDateAndTime).build(); + then: 'it matches expected cm handle state and data store sync state' + assert compositeState.getCmhandleState() == CmHandleState.ADVISED + assert compositeState.dataStores.operationalDataStore.syncState == 'UNSYNCHRONIZED' + } + + def "Build composite state from DataNode "() { + given: "a Data Node " + def dataNode = new DataNode(leaves: ['cm-handle-state': 'ADVISED']) + when: 'build from data node function is invoked' + def compositeState = new CompositeStateBuilder().fromDataNode(cmHandleDataNode).build() + then: 'it matches expected state model as JSON' + assert compositeState.cmhandleState == CmHandleState.ADVISED + } + +} |