advised-modules-sync:
sleep-time-ms: 5000
locked-modules-sync:
- sleep-time-ms: 300000
+ sleep-time-ms: 60000
cm-handle-data-sync:
sleep-time-ms: 30000
subscription-forwarding:
import java.time.OffsetDateTime;
import java.util.Collection;
import java.util.Collections;
-import java.util.List;
import java.util.Map;
import lombok.AllArgsConstructor;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
-import org.apache.commons.lang3.StringUtils;
import org.onap.cps.api.CpsAnchorService;
import org.onap.cps.api.CpsDataService;
import org.onap.cps.api.CpsModuleService;
-import org.onap.cps.ncmp.impl.inventory.CmHandleQueryService;
import org.onap.cps.ncmp.impl.inventory.models.CmHandleState;
import org.onap.cps.ncmp.impl.inventory.models.YangModelCmHandle;
-import org.onap.cps.ncmp.impl.utils.YangDataConverter;
import org.onap.cps.spi.CascadeDeleteAllowed;
-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.model.ModuleReference;
import org.onap.cps.utils.ContentType;
import org.onap.cps.utils.JsonObjectMapper;
private final DmiModelOperations dmiModelOperations;
private final CpsModuleService cpsModuleService;
- private final CmHandleQueryService cmHandleQueryService;
private final CpsDataService cpsDataService;
private final CpsAnchorService cpsAnchorService;
private final JsonObjectMapper jsonObjectMapper;
}
private ModuleDelta getModuleDelta(final YangModelCmHandle yangModelCmHandle, final String targetModuleSetTag) {
- final Collection<ModuleReference> allModuleReferences;
final Map<String, String> newYangResources;
-
- final YangModelCmHandle cmHandleWithSameModuleSetTag = getAnyReadyCmHandleByModuleSetTag(targetModuleSetTag);
- if (cmHandleWithSameModuleSetTag == null) {
+ Collection<ModuleReference> allModuleReferences = getModuleReferencesByModuleSetTag(targetModuleSetTag);
+ if (allModuleReferences.isEmpty()) {
allModuleReferences = dmiModelOperations.getModuleReferences(yangModelCmHandle);
newYangResources = dmiModelOperations.getNewYangResourcesFromDmi(yangModelCmHandle,
cpsModuleService.identifyNewModuleReferences(allModuleReferences));
} else {
log.info("Found other cm handle having same module set tag: {}", targetModuleSetTag);
- allModuleReferences = cpsModuleService.getYangResourcesModuleReferences(
- NFP_OPERATIONAL_DATASTORE_DATASPACE_NAME, cmHandleWithSameModuleSetTag.getId());
newYangResources = NO_NEW_MODULES;
}
return new ModuleDelta(allModuleReferences, newYangResources);
}
- private YangModelCmHandle getAnyReadyCmHandleByModuleSetTag(final String moduleSetTag) {
- if (StringUtils.isBlank(moduleSetTag)) {
- return null;
+ private Collection<ModuleReference> getModuleReferencesByModuleSetTag(final String moduleSetTag) {
+ if (moduleSetTag == null || moduleSetTag.trim().isEmpty()) {
+ return Collections.emptyList();
}
- final String escapedModuleSetTag = moduleSetTag.replace("'", "''");
- final List<DataNode> dataNodes = cmHandleQueryService.queryNcmpRegistryByCpsPath(
- NCMP_DMI_REGISTRY_PARENT + "/cm-handles[@module-set-tag='" + escapedModuleSetTag + "']",
- FetchDescendantsOption.DIRECT_CHILDREN_ONLY);
- return dataNodes.stream().map(YangDataConverter::toYangModelCmHandle)
- .filter(cmHandle -> cmHandle.getCompositeState().getCmHandleState() == CmHandleState.READY)
- .findFirst().orElse(null);
+ return cpsModuleService.getModuleReferencesByAttribute(NCMP_DATASPACE_NAME, NCMP_DMI_REGISTRY_ANCHOR,
+ Map.of("module-set-tag", moduleSetTag), Map.of("cm-handle-state", CmHandleState.READY.name()));
}
private void setCmHandleModuleSetTag(final YangModelCmHandle yangModelCmHandle, final String newModuleSetTag) {
cpsDataService.updateNodeLeaves(NCMP_DATASPACE_NAME, NCMP_DMI_REGISTRY_ANCHOR, NCMP_DMI_REGISTRY_PARENT,
jsonForUpdate, OffsetDateTime.now(), ContentType.JSON);
}
+
}
import org.onap.cps.ncmp.impl.inventory.models.YangModelCmHandle
import org.onap.cps.spi.CascadeDeleteAllowed
import org.onap.cps.spi.exceptions.SchemaSetNotFoundException
-import org.onap.cps.spi.model.DataNode
-import org.onap.cps.spi.model.DataNodeBuilder
import org.onap.cps.spi.model.ModuleReference
import org.onap.cps.utils.JsonObjectMapper
import spock.lang.Specification
def mockJsonObjectMapper = Mock(JsonObjectMapper)
def objectUnderTest = new ModuleSyncService(mockDmiModelOperations, mockCpsModuleService,
- mockCmHandleQueries, mockCpsDataService, mockCpsAnchorService, mockJsonObjectMapper)
+ mockCpsDataService, mockCpsAnchorService, mockJsonObjectMapper)
def expectedDataspaceName = NFP_OPERATIONAL_DATASTORE_DATASPACE_NAME
- def static cmHandleWithModuleSetTag = new DataNodeBuilder()
- .withXpath("/dmi-registry/cm-handles[@id='otherId']")
- .withLeaves(['id': 'otherId', 'module-set-tag': 'tag-1'])
- .withAnchor('otherId').build()
def 'Sync model for a NEW cm handle using module set tags: #scenario.'() {
given: 'a cm handle state to be synced'
mockDmiModelOperations.getNewYangResourcesFromDmi(yangModelCmHandle, identifiedNewModuleReferences) >> newModuleNameContentToMap
and: 'the module service identifies #identifiedNewModuleReferences.size() new modules'
mockCpsModuleService.identifyNewModuleReferences(moduleReferences) >> identifiedNewModuleReferences
- and: 'system contains other cm handle with "same tag" (that is READY)'
- mockCmHandleQueries.queryNcmpRegistryByCpsPath(*_) >> existingCmHandlesWithSameTag
+ and: 'the service returns a list of module references when queried with the specified attributes'
+ mockCpsModuleService.getModuleReferencesByAttribute(*_) >> existingModuleReferences
when: 'module sync is triggered'
objectUnderTest.syncAndCreateSchemaSetAndAnchor(yangModelCmHandle)
then: 'create schema set from module is invoked with correct parameters'
and: 'anchor is created with the correct parameters'
1 * mockCpsAnchorService.createAnchor(NFP_OPERATIONAL_DATASTORE_DATASPACE_NAME, 'ch-1', 'ch-1')
where: 'the following parameters are used'
- scenario | existingModuleResourcesInCps | identifiedNewModuleReferences | newModuleNameContentToMap | moduleSetTag | existingCmHandlesWithSameTag
- 'one new module, new tag' | [['module2': '2'], ['module3': '3']] | [new ModuleReference('module1', '1')] | [module1: 'some yang source'] | '' | []
- 'no new module, new tag' | [['module1': '1'], ['module2': '2']] | [] | [:] | 'new-tag-1' | []
- 'same tag' | [['module1': '1'], ['module2': '2']] | [] | [:] | 'same-tag' | [cmHandleWithModuleSetTag]
+ scenario | identifiedNewModuleReferences | newModuleNameContentToMap | moduleSetTag | existingModuleReferences
+ 'one new module, new tag' | [new ModuleReference('module1', '1')] | [module1: 'some yang source'] | '' | []
+ 'no new module, new tag' | [] | [:] | 'new-tag-1' | []
+ 'same tag' | [] | [:] | 'same-tag' | [new ModuleReference('module1', '1'), new ModuleReference('module2', '2')]
}
def 'Upgrade model for an existing cm handle with Module Set Tag where the modules are #scenario'() {
mockCpsModuleService.identifyNewModuleReferences(_) >> []
and: 'CPS-Core returns list of existing module resources for TBD'
mockCpsModuleService.getYangResourcesModuleReferences(*_) >> [ new ModuleReference('module1','1') ]
- and: 'system contains #existingCmHandlesWithSameTag.size() cm handles with same tag'
- mockCmHandleQueries.queryNcmpRegistryByCpsPath(*_) >> existingCmHandlesWithSameTag
+ and: 'the service returns a list of module references when queried with the specified attributes'
+ mockCpsModuleService.getModuleReferencesByAttribute(*_) >> existingModuleReferences
and: 'the other cm handle is a state ready'
mockCmHandleQueries.cmHandleHasState('otherId', CmHandleState.READY) >> true
when: 'module sync is triggered'
and: 'No anchor is created for the upgraded cm handle'
0 * mockCpsAnchorService.createAnchor(*_)
where: 'the following parameters are used'
- scenario | existingCmHandlesWithSameTag
+ scenario | existingModuleReferences
'new' | []
- 'in database' | [cmHandleWithModuleSetTag]
+ 'in database' | [new ModuleReference('module1', '1')]
}
def 'upgrade model for a existing cm handle'() {
and: 'the module service returns some module references'
def moduleReferences = [new ModuleReference('module1', '1'), new ModuleReference('module2', '2')]
mockCpsModuleService.getYangResourcesModuleReferences(*_)>> moduleReferences
- and: 'a cm handle with the same moduleSetTag can be found in the registry'
- mockCmHandleQueries.queryNcmpRegistryByCpsPath(*_) >> [new DataNode(xpath: '/dmi-registry/cm-handles[@id=\'cmHandleId-1\']', leaves: ['id': 'cmHandleId-1'],
- childDataNodes: [new DataNode(xpath: '/dmi-registry/cm-handles[@id=\'cmHandleId-1\']/state', leaves: ['cm-handle-state': 'READY'])])]
+ and: 'the service returns a list of module references when queried with the specified attributes'
+ mockCpsModuleService.getModuleReferencesByAttribute(*_) >> moduleReferences
when: 'module upgrade is triggered'
objectUnderTest.syncAndUpgradeSchemaSet(yangModelCmHandle)
then: 'the upgrade is delegated to the module service (with the correct parameters)'
return moduleReferenceRepository.identifyNewModuleReferences(moduleReferencesToCheck);
}
+ @Override
+ public Collection<ModuleReference> getModuleReferencesByAttribute(final String dataspaceName,
+ final String anchorName,
+ final Map<String, String> parentAttributes,
+ final Map<String, String> childAttributes) {
+ return moduleReferenceRepository.findModuleReferences(dataspaceName, anchorName, parentAttributes,
+ childAttributes);
+ }
+
private Set<YangResourceEntity> synchronizeYangResources(
final Map<String, String> moduleReferenceNameToContentMap) {
final Map<String, YangResourceEntity> checksumToEntityMap = moduleReferenceNameToContentMap.entrySet().stream()
/*-
* ============LICENSE_START=======================================================
- * Copyright (C) 2021-2023 Nordix Foundation.
+ * Copyright (C) 2021-2024 Nordix Foundation.
* Modifications Copyright (C) 2023 TechMahindra Ltd.
* ================================================================================
* Licensed under the Apache License, Version 2.0 (the "License");
package org.onap.cps.spi.repository;
-import jakarta.persistence.EntityManager;
-import jakarta.persistence.PersistenceContext;
import jakarta.persistence.Query;
import jakarta.transaction.Transactional;
import java.util.List;
@Slf4j
public class FragmentRepositoryCpsPathQueryImpl implements FragmentRepositoryCpsPathQuery {
- @PersistenceContext
- private EntityManager entityManager;
-
private final FragmentQueryBuilder fragmentQueryBuilder;
@Override
/*-
* ============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.
package org.onap.cps.spi.repository;
import java.util.Collection;
+import java.util.Map;
import org.onap.cps.spi.model.ModuleReference;
/**
public interface ModuleReferenceQuery {
Collection<ModuleReference> identifyNewModuleReferences(final Collection<ModuleReference> moduleReferencesToCheck);
+
+ Collection<ModuleReference> findModuleReferences(final String dataspaceName, final String anchorName,
+ final Map<String, String> parentAttributes,
+ final Map<String, String> childAttributes);
}
import jakarta.persistence.EntityManager;
import jakarta.persistence.PersistenceContext;
+import jakarta.persistence.Query;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.HashSet;
import java.util.List;
-import lombok.AllArgsConstructor;
+import java.util.Map;
+import java.util.stream.Collectors;
+import lombok.RequiredArgsConstructor;
import lombok.SneakyThrows;
import lombok.extern.slf4j.Slf4j;
import org.onap.cps.spi.model.ModuleReference;
@Slf4j
@Transactional
-@AllArgsConstructor
+@RequiredArgsConstructor
public class ModuleReferenceRepositoryImpl implements ModuleReferenceQuery {
@PersistenceContext
private EntityManager entityManager;
- private TempTableCreator tempTableCreator;
+ private final TempTableCreator tempTableCreator;
@Override
@SneakyThrows
return identifyNewModuleReferencesForCmHandle(tempTableName);
}
+ /**
+ * Finds module references based on specified dataspace, anchor, and attribute filters.
+ * This method constructs and executes a SQL query to retrieve module references. The query applies filters to
+ * parent and child fragments using the provided attribute maps. The `parentAttributes` are used to filter
+ * parent fragments, while `childAttributes` filter child fragments.
+ *
+ * @param dataspaceName the name of the dataspace to filter on.
+ * @param anchorName the name of the anchor to filter on.
+ * @param parentAttributes a map of attributes for filtering parent fragments.
+ * @param childAttributes a map of attributes for filtering child fragments.
+ * @return a collection of {@link ModuleReference} objects that match the specified filters.
+ */
+ @Transactional
+ @SuppressWarnings("unchecked")
+ @Override
+ public Collection<ModuleReference> findModuleReferences(final String dataspaceName, final String anchorName,
+ final Map<String, String> parentAttributes,
+ final Map<String, String> childAttributes) {
+
+ final String parentFragmentWhereClause = buildWhereClause(childAttributes, "parentFragment");
+ final String childFragmentWhereClause = buildWhereClause(parentAttributes, "childFragment");
+
+ final String moduleReferencesSqlQuery = buildModuleReferencesSqlQuery(parentFragmentWhereClause,
+ childFragmentWhereClause);
+
+ final Query query = entityManager.createNativeQuery(moduleReferencesSqlQuery);
+ setQueryParameters(query, parentAttributes, childAttributes, anchorName, dataspaceName);
+ return processQueryResults(query.getResultList());
+ }
+
+ private String buildWhereClause(final Map<String, String> attributes, final String alias) {
+ return attributes.keySet().stream()
+ .map(attributeName -> String.format("%s.attributes->>'%s' = ?", alias, attributeName))
+ .collect(Collectors.joining(" AND "));
+ }
+
+ private void setQueryParameters(final Query query, final Map<String, String> parentAttributes,
+ final Map<String, String> childAttributes, final String anchorName,
+ final String dataspaceName) {
+ final String childAttributeValue = childAttributes.entrySet().iterator().next().getValue();
+ query.setParameter(1, childAttributeValue);
+
+ final String parentAttributeValue = parentAttributes.entrySet().iterator().next().getValue();
+ query.setParameter(2, parentAttributeValue);
+
+ query.setParameter(3, anchorName);
+ query.setParameter(4, dataspaceName);
+ }
+
+ private String buildModuleReferencesSqlQuery(final String parentFragmentClause, final String childFragmentClause) {
+ return """
+ WITH Fragment AS (
+ SELECT childFragment.attributes->>'id' AS schema_set_name
+ FROM fragment parentFragment
+ JOIN fragment childFragment ON parentFragment.parent_id = childFragment.id
+ JOIN anchor anchorInfo ON parentFragment.anchor_id = anchorInfo.id
+ JOIN dataspace dataspaceInfo ON anchorInfo.dataspace_id = dataspaceInfo.id
+ WHERE %s
+ AND %s
+ AND anchorInfo.name = ?
+ AND dataspaceInfo.name = ?
+ LIMIT 1
+ ),
+ SchemaSet AS (
+ SELECT id
+ FROM schema_set
+ WHERE name = (SELECT schema_set_name FROM Fragment)
+ )
+ SELECT yangResource.module_name, yangResource.revision
+ FROM yang_resource yangResource
+ JOIN schema_set_yang_resources schemaSetYangResources
+ ON yangResource.id = schemaSetYangResources.yang_resource_id
+ WHERE schemaSetYangResources.schema_set_id = (SELECT id FROM SchemaSet);
+ """.formatted(parentFragmentClause, childFragmentClause);
+ }
+
+ private Collection<ModuleReference> processQueryResults(final List<Object[]> queryResults) {
+ if (queryResults.isEmpty()) {
+ log.info("No module references found for the provided attributes.");
+ return Collections.emptyList();
+ }
+ return queryResults.stream()
+ .map(queryResult -> {
+ final String name = (String) queryResult[0];
+ final String revision = (String) queryResult[1];
+ return new ModuleReference(name, revision);
+ })
+ .collect(Collectors.toList());
+ }
+
private Collection<ModuleReference> identifyNewModuleReferencesForCmHandle(final String tempTableName) {
final String sql = String.format(
"SELECT %1$s.module_name, %1$s.revision"
for (final Object[] row : resultsAsObjects) {
resultsAsModuleReferences.add(new ModuleReference((String) row[0], (String) row[1]));
}
-
return resultsAsModuleReferences;
}
}
Collection<ModuleReference> identifyNewModuleReferences(
Collection<ModuleReference> moduleReferencesToCheck);
+ /**
+ * Retrieves module references based on the provided dataspace name, anchor name and attribute filters
+ * for both parent and child fragments.
+
+ * This method constructs and executes a SQL query to find module references from a database, using
+ * the specified `dataspaceName`, `anchorName` and two sets of attribute filters: one for parent fragments
+ * and one for child fragments. The method applies these filters to identify the appropriate fragments
+ * and schema sets, and then retrieves the corresponding module references.
+
+ * The SQL query is dynamically built based on the provided attribute filters:
+ * - The `parentAttributes` map is used to filter the parent fragments. The entries in this map are
+ * converted into a WHERE clause for the parent fragments.
+ * - The `childAttributes` map is used to filter the child fragments. This is applied to the child fragments
+ * after filtering the parent fragments.
+ *
+ * @param dataspaceName the name of the dataspace to filter on. It is used to locate the relevant dataspace
+ * in the database.
+ * @param anchorName the name of the anchor to filter on. It is used to locate the relevant anchor within
+ * the dataspace.
+ * @param parentAttributes a map of attributes to filter parent fragments. Each entry in this map represents
+ * an attribute key-value pair used in the WHERE clause for parent fragments.
+ * @param childAttributes a map of attributes to filter child fragments. Each entry in this map represents
+ * an attribute key-value pair used in the WHERE clause for child fragments.
+ * @return a collection of {@link ModuleReference} objects that match the given criteria. Each
+ * {@code ModuleReference} contains information about a module's name and revision.
+ * @implNote The method assumes that both `parentAttributes` and `childAttributes` maps contain at least
+ * one entry. The first entry from `parentAttributes` is used to filter parent fragments,
+ * and the first entry from `childAttributes` is used to filter child fragments.
+ */
+ Collection<ModuleReference> getModuleReferencesByAttribute(final String dataspaceName, final String anchorName,
+ final Map<String, String> parentAttributes,
+ final Map<String, String> childAttributes);
+
}
return cpsModulePersistenceService.identifyNewModuleReferences(moduleReferencesToCheck);
}
+ @Timed(value = "cps.module.service.module.reference.query",
+ description = "Time taken to query list of module references")
+ @Override
+ public Collection<ModuleReference> getModuleReferencesByAttribute(final String dataspaceName,
+ final String anchorName,
+ final Map<String, String> parentAttributes,
+ final Map<String, String> childAttributes) {
+ return cpsModulePersistenceService.getModuleReferencesByAttribute(dataspaceName, anchorName, parentAttributes,
+ childAttributes);
+ }
+
private boolean isCascadeDeleteProhibited(final CascadeDeleteAllowed cascadeDeleteAllowed) {
return CascadeDeleteAllowed.CASCADE_DELETE_PROHIBITED == cascadeDeleteAllowed;
}
Collection<ModuleReference> identifyNewModuleReferences(
Collection<ModuleReference> moduleReferencesToCheck);
+ /**
+ * Retrieves module references based on the specified dataspace, anchor, and attribute filters.
+
+ * Constructs and executes a SQL query to find module references by applying filters for parent and child fragments.
+ * Uses `parentAttributes` for filtering parent fragments and `childAttributes` for filtering child fragments.
+ *
+ * @param dataspaceName the name of the dataspace to filter on.
+ * @param anchorName the name of the anchor to filter on.
+ * @param parentAttributes a map of attributes for filtering parent fragments.
+ * @param childAttributes a map of attributes for filtering child fragments.
+ * @return a collection of {@link ModuleReference} objects matching the criteria.
+ */
+ Collection<ModuleReference> getModuleReferencesByAttribute(final String dataspaceName, final String anchorName,
+ final Map<String, String> parentAttributes,
+ final Map<String, String> childAttributes);
+
}
1 * mockCpsModulePersistenceService.identifyNewModuleReferences(moduleReferencesToCheck)
}
+ def 'Get module references when queried by attributes'() {
+ given: 'a valid dataspace name and anchor name'
+ def dataspaceName = 'someDataspace'
+ def anchorName = 'someAnchor'
+ and: 'a set of parent attributes and child attributes used for filtering'
+ def parentAttributes = ['some-property-key1': 'some-property-val1']
+ def childAttributes = ['some-property-key2': 'some-property-val2']
+ and: 'a list of expected module references returned by the persistence service'
+ def expectedModuleReferences = [new ModuleReference(moduleName: 'some-name', revision: 'some-revision')]
+ mockCpsModulePersistenceService.getModuleReferencesByAttribute(dataspaceName, anchorName, parentAttributes, childAttributes) >> expectedModuleReferences
+ when: 'the method is invoked to retrieve module references by attributes'
+ def actualModuleReferences = objectUnderTest.getModuleReferencesByAttribute(dataspaceName, anchorName, parentAttributes, childAttributes)
+ then: 'the retrieved module references should match the expected module references'
+ assert actualModuleReferences == expectedModuleReferences
+ }
+
+
def 'Getting module definitions with module name'() {
given: 'module persistence service returns module definitions for module name'
def moduleDefinitionsFromPersistenceService = [ new ModuleDefinition('name', 'revision', 'content' ) ]