Mapper to group Subscription Details for DMI

- introduced a mapper to group a collection of subscription
  keys(datastore cmhandle and xpath ) based on datastore and xpath
- the information will be used to send out to the dmi plugin

Issue-ID: CPS-2312
Change-Id: I99f509eb5b4a8c3a7fb1078ae86490d894f22b6c
Signed-off-by: mpriyank <priyank.maheshwari@est.tech>
diff --git a/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/impl/cmnotificationsubscription/dmi/DmiCmSubscriptionDetailsPerDmiMapper.java b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/impl/cmnotificationsubscription/dmi/DmiCmSubscriptionDetailsPerDmiMapper.java
new file mode 100644
index 0000000..423c603
--- /dev/null
+++ b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/impl/cmnotificationsubscription/dmi/DmiCmSubscriptionDetailsPerDmiMapper.java
@@ -0,0 +1,103 @@
+/*
+ *  ============LICENSE_START=======================================================
+ *  Copyright (C) 2024 Nordix Foundation
+ *  ================================================================================
+ *  Licensed under the Apache License, Version 2.0 (the "License");
+ *  you may not use this file except in compliance with the License.
+ *  You may obtain a copy of the License at
+ *
+ *        http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ *
+ *  SPDX-License-Identifier: Apache-2.0
+ * ============LICENSE_END=========================================================
+ */
+
+package org.onap.cps.ncmp.impl.cmnotificationsubscription.dmi;
+
+import static org.onap.cps.ncmp.api.data.models.DatastoreType.fromDatastoreName;
+import static org.onap.cps.ncmp.impl.cmnotificationsubscription.models.CmSubscriptionStatus.PENDING;
+
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+import java.util.stream.Collectors;
+import org.onap.cps.ncmp.api.data.models.DatastoreType;
+import org.onap.cps.ncmp.impl.cmnotificationsubscription.models.DmiCmSubscriptionDetails;
+import org.onap.cps.ncmp.impl.cmnotificationsubscription.models.DmiCmSubscriptionKey;
+import org.onap.cps.ncmp.impl.cmnotificationsubscription.models.DmiCmSubscriptionPredicate;
+import org.springframework.stereotype.Component;
+
+@Component
+public class DmiCmSubscriptionDetailsPerDmiMapper {
+
+    /**
+     * Maps Dmi Subscription Keys grouped by Dmi Plugin to DmiCmSubscriptionDetails per Dmi plugin.
+     *
+     * @param subscribersPerDmi Details managed by each dmi plugin
+     * @return Grouped Dmi Subscription details per dmi plugin
+     */
+    public Map<String, DmiCmSubscriptionDetails> toDmiCmSubscriptionsPerDmi(
+            final Map<String, Collection<DmiCmSubscriptionKey>> subscribersPerDmi) {
+
+        final Map<String, DmiCmSubscriptionDetails> dmiSubscriptionsPerDmi = new HashMap<>();
+
+        subscribersPerDmi.forEach((dmiPluginName, dmiCmSubscriptionKeys) -> {
+            final Map<DatastoreTypeAndXpath, List<DmiCmSubscriptionKey>> groupedByDatastoreTypeAndXpath =
+                    groupByDatastoreTypeAndXpath(dmiCmSubscriptionKeys);
+
+            final List<DmiCmSubscriptionPredicate> dmiSubscriptionPredicates =
+                    createDmiCmSubscriptionPredicates(groupedByDatastoreTypeAndXpath);
+
+            final DmiCmSubscriptionDetails dmiCmSubscriptionDetails =
+                    new DmiCmSubscriptionDetails(dmiSubscriptionPredicates, PENDING);
+
+            dmiSubscriptionsPerDmi.put(dmiPluginName, dmiCmSubscriptionDetails);
+        });
+
+        return dmiSubscriptionsPerDmi;
+    }
+
+    private static Map<DatastoreTypeAndXpath, List<DmiCmSubscriptionKey>> groupByDatastoreTypeAndXpath(
+            final Collection<DmiCmSubscriptionKey> dmiCmSubscriptionKeys) {
+        return dmiCmSubscriptionKeys.stream().collect(Collectors.groupingBy(
+                datastoreTypeAndXpath -> new DatastoreTypeAndXpath(
+                        fromDatastoreName(datastoreTypeAndXpath.datastoreName()), datastoreTypeAndXpath.xpath())));
+    }
+
+    private static List<DmiCmSubscriptionPredicate> createDmiCmSubscriptionPredicates(
+            final Map<DatastoreTypeAndXpath, List<DmiCmSubscriptionKey>> groupedByDatastoreTypeAndXpath) {
+        final List<DmiCmSubscriptionPredicate> dmiCmSubscriptionPredicates = new ArrayList<>();
+
+        for (final Map.Entry<DatastoreTypeAndXpath, List<DmiCmSubscriptionKey>> datastoreTypeXpathGroupEntry :
+                groupedByDatastoreTypeAndXpath.entrySet()) {
+            final DatastoreTypeAndXpath datastoreTypeAndXpath = datastoreTypeXpathGroupEntry.getKey();
+            final Set<String> cmHandleIds = new HashSet<>();
+
+            for (final DmiCmSubscriptionKey dmiCmSubscriptionKey : datastoreTypeXpathGroupEntry.getValue()) {
+                cmHandleIds.add(dmiCmSubscriptionKey.cmHandleId());
+            }
+
+            final Set<String> xpaths = Collections.singleton(datastoreTypeAndXpath.xpath());
+            dmiCmSubscriptionPredicates.add(
+                    new DmiCmSubscriptionPredicate(cmHandleIds, datastoreTypeAndXpath.datastoreType(), xpaths));
+        }
+
+        return dmiCmSubscriptionPredicates;
+    }
+
+
+    private record DatastoreTypeAndXpath(DatastoreType datastoreType, String xpath) { }
+
+}
+
diff --git a/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/impl/cmnotificationsubscription/models/DmiCmSubscriptionKey.java b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/impl/cmnotificationsubscription/models/DmiCmSubscriptionKey.java
new file mode 100644
index 0000000..edc3c56
--- /dev/null
+++ b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/impl/cmnotificationsubscription/models/DmiCmSubscriptionKey.java
@@ -0,0 +1,30 @@
+/*
+ *  ============LICENSE_START=======================================================
+ *  Copyright (C) 2024 Nordix Foundation
+ *  ================================================================================
+ *  Licensed under the Apache License, Version 2.0 (the "License");
+ *  you may not use this file except in compliance with the License.
+ *  You may obtain a copy of the License at
+ *
+ *        http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ *
+ *  SPDX-License-Identifier: Apache-2.0
+ * ============LICENSE_END=========================================================
+ */
+
+package org.onap.cps.ncmp.impl.cmnotificationsubscription.models;
+
+/**
+ * Key used to find the records to be sent to the DMI plugin.
+ *
+ * @param datastoreName datastore name
+ * @param cmHandleId    cmhandle id
+ * @param xpath         xpath
+ */
+public record DmiCmSubscriptionKey(String datastoreName, String cmHandleId, String xpath) { }
diff --git a/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/impl/cmnotificationsubscription/dmi/DmiCmSubscriptionDetailsPerDmiMapperSpec.groovy b/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/impl/cmnotificationsubscription/dmi/DmiCmSubscriptionDetailsPerDmiMapperSpec.groovy
new file mode 100644
index 0000000..5d74f45
--- /dev/null
+++ b/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/impl/cmnotificationsubscription/dmi/DmiCmSubscriptionDetailsPerDmiMapperSpec.groovy
@@ -0,0 +1,52 @@
+/*
+ * ============LICENSE_START=======================================================
+ *  Copyright (C) 2024 Nordix Foundation
+ *  ================================================================================
+ *  Licensed under the Apache License, Version 2.0 (the "License");
+ *  you may not use this file except in compliance with the License.
+ *  You may obtain a copy of the License at
+ *
+ *        http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ *
+ *  SPDX-License-Identifier: Apache-2.0
+ *  ============LICENSE_END=========================================================
+ */
+
+package org.onap.cps.ncmp.impl.cmnotificationsubscription.dmi
+
+import org.onap.cps.ncmp.impl.cmnotificationsubscription.models.DmiCmSubscriptionKey
+import spock.lang.Specification
+
+class DmiCmSubscriptionDetailsPerDmiMapperSpec extends Specification {
+
+    def objectUnderTest = new DmiCmSubscriptionDetailsPerDmiMapper()
+
+    def 'Check for grouping of Dmi Subscription Details'() {
+        given: 'details in the form of datastore , cmhandle and xpath'
+            def subscribersPerDmi = [
+                'dmi-1': [
+                    new DmiCmSubscriptionKey('ncmp-datastore:passthrough-operational', 'ch-1', '/a/b'),
+                    new DmiCmSubscriptionKey('ncmp-datastore:passthrough-operational', 'ch-2', '/a/b')
+                ],
+                'dmi-2': [
+                    new DmiCmSubscriptionKey('ncmp-datastore:passthrough-running', 'ch-3', '/c/d'),
+                    new DmiCmSubscriptionKey('ncmp-datastore:passthrough-running', 'ch-3', '/e/f')
+                ]
+            ]
+        when: 'we try to map the values based on datastore and xpath'
+            def result = objectUnderTest.toDmiCmSubscriptionsPerDmi(subscribersPerDmi)
+        then: 'the mapped values are grouped as expected for dmi-1'
+            assert result['dmi-1'].dmiCmSubscriptionPredicates.size() == 1
+            assert result['dmi-1'].dmiCmSubscriptionPredicates[0].targetCmHandleIds.containsAll(['ch-1', 'ch-2'])
+        and: 'similarly for dmi-2'
+            assert result['dmi-2'].dmiCmSubscriptionPredicates.size() == 2
+            assert result['dmi-2'].dmiCmSubscriptionPredicates[0].targetCmHandleIds.contains('ch-3')
+            assert result['dmi-2'].dmiCmSubscriptionPredicates[1].targetCmHandleIds.contains('ch-3')
+    }
+}