summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--cps-ncmp-service/pom.xml4
-rw-r--r--cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/operation/DmiOperations.java4
-rw-r--r--cps-ri/pom.xml4
-rwxr-xr-xcps-ri/src/main/java/org/onap/cps/spi/impl/CpsAdminPersistenceServiceImpl.java33
-rwxr-xr-xcps-ri/src/main/java/org/onap/cps/spi/repository/AnchorRepository.java16
-rw-r--r--cps-ri/src/main/java/org/onap/cps/spi/repository/FragmentRepositoryCpsPathQueryImpl.java5
-rw-r--r--cps-ri/src/main/java/org/onap/cps/spi/repository/YangResourceRepository.java33
-rw-r--r--cps-ri/src/test/groovy/org/onap/cps/spi/impl/CpsAdminPersistenceServiceSpec.groovy35
-rw-r--r--cps-ri/src/test/resources/data/anchors-schemaset-modules.sql45
-rwxr-xr-xcps-service/src/main/java/org/onap/cps/spi/CpsAdminPersistenceService.java9
-rw-r--r--cps-service/src/main/java/org/onap/cps/spi/exceptions/ModuleNamesNotFoundException.java41
11 files changed, 203 insertions, 26 deletions
diff --git a/cps-ncmp-service/pom.xml b/cps-ncmp-service/pom.xml
index e82c95e111..b70279303e 100644
--- a/cps-ncmp-service/pom.xml
+++ b/cps-ncmp-service/pom.xml
@@ -66,9 +66,5 @@
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-validation</artifactId>
</dependency>
- <dependency>
- <groupId>org.jetbrains</groupId>
- <artifactId>annotations</artifactId>
- </dependency>
</dependencies>
</project>
diff --git a/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/operation/DmiOperations.java b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/operation/DmiOperations.java
index f7421cdb71..562c330657 100644
--- a/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/operation/DmiOperations.java
+++ b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/operation/DmiOperations.java
@@ -22,7 +22,6 @@ package org.onap.cps.ncmp.api.impl.operation;
import com.fasterxml.jackson.annotation.JsonValue;
import lombok.Getter;
-import org.jetbrains.annotations.NotNull;
import org.onap.cps.ncmp.api.impl.client.DmiRestClient;
import org.springframework.http.HttpHeaders;
import org.springframework.http.ResponseEntity;
@@ -165,7 +164,6 @@ public class DmiOperations {
return dmiRestClient.postOperationWithJsonData(stringBuilder.toString(), jsonBody, new HttpHeaders());
}
- @NotNull
private String getDmiResourceUrl(final String dmiServiceName,
final String cmHandle,
final String resourceName) {
@@ -176,7 +174,6 @@ public class DmiOperations {
return stringBuilder.toString();
}
- @NotNull
private String getDmiDatastoreUrl(final String dmiServiceName,
final String cmHandle,
final String resourceId,
@@ -188,7 +185,6 @@ public class DmiOperations {
return stringBuilder.toString();
}
- @NotNull
private StringBuilder getStringBuilderForPassThroughUrl(final String dmiServiceName,
final String cmHandle,
final String resourceId,
diff --git a/cps-ri/pom.xml b/cps-ri/pom.xml
index b7aa4193d1..8d860b0041 100644
--- a/cps-ri/pom.xml
+++ b/cps-ri/pom.xml
@@ -100,10 +100,6 @@
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
</dependency>
- <dependency>
- <groupId>org.jetbrains</groupId>
- <artifactId>annotations</artifactId>
- </dependency>
<!-- T E S T D E P E N D E N C I E S -->
<dependency>
<groupId>org.codehaus.groovy</groupId>
diff --git a/cps-ri/src/main/java/org/onap/cps/spi/impl/CpsAdminPersistenceServiceImpl.java b/cps-ri/src/main/java/org/onap/cps/spi/impl/CpsAdminPersistenceServiceImpl.java
index 2776c4d67d..2b63169d44 100755
--- a/cps-ri/src/main/java/org/onap/cps/spi/impl/CpsAdminPersistenceServiceImpl.java
+++ b/cps-ri/src/main/java/org/onap/cps/spi/impl/CpsAdminPersistenceServiceImpl.java
@@ -23,6 +23,7 @@
package org.onap.cps.spi.impl;
import java.util.Collection;
+import java.util.List;
import java.util.Set;
import java.util.stream.Collectors;
import javax.transaction.Transactional;
@@ -30,11 +31,13 @@ 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.exceptions.AlreadyDefinedException;
+import org.onap.cps.spi.exceptions.ModuleNamesNotFoundException;
import org.onap.cps.spi.model.Anchor;
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.repository.SchemaSetRepository;
+import org.onap.cps.spi.repository.YangResourceRepository;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.dao.DataIntegrityViolationException;
import org.springframework.stereotype.Component;
@@ -54,6 +57,9 @@ public class CpsAdminPersistenceServiceImpl implements CpsAdminPersistenceServic
@Autowired
private FragmentRepository fragmentRepository;
+ @Autowired
+ private YangResourceRepository yangResourceRepository;
+
@Override
public void createDataspace(final String dataspaceName) {
try {
@@ -88,6 +94,14 @@ public class CpsAdminPersistenceServiceImpl implements CpsAdminPersistenceServic
}
@Override
+ public Collection<Anchor> getAnchors(final String dataspaceName, final Collection<String> inputModuleNames) {
+ validateDataspaceAndModuleNames(dataspaceName, inputModuleNames);
+ final Collection<AnchorEntity> anchorEntities =
+ anchorRepository.getAnchorsByDataspaceNameAndModuleNames(dataspaceName, inputModuleNames);
+ return anchorEntities.stream().map(CpsAdminPersistenceServiceImpl::toAnchor).collect(Collectors.toSet());
+ }
+
+ @Override
public Anchor getAnchor(final String dataspaceName, final String anchorName) {
return toAnchor(getAnchorEntity(dataspaceName, anchorName));
}
@@ -112,4 +126,23 @@ public class CpsAdminPersistenceServiceImpl implements CpsAdminPersistenceServic
.schemaSetName(anchorEntity.getSchemaSet().getName())
.build();
}
+
+ private void validateDataspaceAndModuleNames(final String dataspaceName,
+ final Collection<String> inputModuleNames) {
+ final Collection<String> retrievedModuleNames =
+ yangResourceRepository.findAllModuleReferences(dataspaceName, inputModuleNames)
+ .stream().map(module -> module.getModuleName())
+ .collect(Collectors.toList());
+ if (retrievedModuleNames.isEmpty()) {
+ dataspaceRepository.getByName(dataspaceName);
+ }
+ if (inputModuleNames.size() > retrievedModuleNames.size()) {
+ final List<String> moduleNamesNotFound = inputModuleNames.stream()
+ .filter(moduleName -> !retrievedModuleNames.contains(moduleName))
+ .collect(Collectors.toList());
+ if (!moduleNamesNotFound.isEmpty()) {
+ throw new ModuleNamesNotFoundException(dataspaceName, moduleNamesNotFound);
+ }
+ }
+ }
}
diff --git a/cps-ri/src/main/java/org/onap/cps/spi/repository/AnchorRepository.java b/cps-ri/src/main/java/org/onap/cps/spi/repository/AnchorRepository.java
index 0305555456..6d4cb3c716 100755
--- a/cps-ri/src/main/java/org/onap/cps/spi/repository/AnchorRepository.java
+++ b/cps-ri/src/main/java/org/onap/cps/spi/repository/AnchorRepository.java
@@ -1,6 +1,7 @@
/*
* ============LICENSE_START=======================================================
* Copyright (C) 2021 Pantheon.tech
+ * Modifications Copyright (C) 2021 Nordix Foundation
* ================================================================================
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -27,6 +28,8 @@ import org.onap.cps.spi.entities.DataspaceEntity;
import org.onap.cps.spi.entities.SchemaSetEntity;
import org.onap.cps.spi.exceptions.AnchorNotFoundException;
import org.springframework.data.jpa.repository.JpaRepository;
+import org.springframework.data.jpa.repository.Query;
+import org.springframework.data.repository.query.Param;
public interface AnchorRepository extends JpaRepository<AnchorEntity, Integer> {
@@ -41,4 +44,17 @@ public interface AnchorRepository extends JpaRepository<AnchorEntity, Integer> {
Collection<AnchorEntity> findAllByDataspace(@NotNull DataspaceEntity dataspaceEntity);
Collection<AnchorEntity> findAllBySchemaSet(@NotNull SchemaSetEntity schemaSetEntity);
+
+ @Query(value = "SELECT DISTINCT\n"
+ + "anchor.*\n"
+ + "FROM\n"
+ + "yang_resource\n"
+ + "JOIN schema_set_yang_resources ON "
+ + "schema_set_yang_resources.yang_resource_id = yang_resource.id\n"
+ + "JOIN schema_set ON schema_set.id = schema_set_yang_resources.schema_set_id\n"
+ + "JOIN anchor ON anchor.schema_set_id = schema_set.id\n"
+ + "JOIN dataspace ON dataspace.id = anchor.dataspace_id AND dataspace.name = :dataspaceName\n"
+ + "WHERE yang_resource.module_Name IN (:moduleNames)", nativeQuery = true)
+ Collection<AnchorEntity> getAnchorsByDataspaceNameAndModuleNames(@Param("dataspaceName") String dataspaceName,
+ @Param("moduleNames") Collection<String> moduleNames);
} \ No newline at end of file
diff --git a/cps-ri/src/main/java/org/onap/cps/spi/repository/FragmentRepositoryCpsPathQueryImpl.java b/cps-ri/src/main/java/org/onap/cps/spi/repository/FragmentRepositoryCpsPathQueryImpl.java
index 4aa3e5fb36..fddedcad27 100644
--- a/cps-ri/src/main/java/org/onap/cps/spi/repository/FragmentRepositoryCpsPathQueryImpl.java
+++ b/cps-ri/src/main/java/org/onap/cps/spi/repository/FragmentRepositoryCpsPathQueryImpl.java
@@ -28,8 +28,6 @@ import java.util.Map;
import javax.persistence.EntityManager;
import javax.persistence.PersistenceContext;
import javax.persistence.Query;
-import org.jetbrains.annotations.NotNull;
-import org.jetbrains.annotations.Nullable;
import org.onap.cps.cpspath.parser.CpsPathPrefixType;
import org.onap.cps.cpspath.parser.CpsPathQuery;
import org.onap.cps.spi.entities.FragmentEntity;
@@ -63,7 +61,6 @@ public class FragmentRepositoryCpsPathQueryImpl implements FragmentRepositoryCps
return query.getResultList();
}
- @NotNull
private static String getSimilarToXpathSqlRegex(final CpsPathQuery cpsPathQuery) {
final var xpathRegexBuilder = new StringBuilder();
if (CpsPathPrefixType.ABSOLUTE.equals(cpsPathQuery.getCpsPathPrefixType())) {
@@ -76,13 +73,11 @@ public class FragmentRepositoryCpsPathQueryImpl implements FragmentRepositoryCps
return xpathRegexBuilder.toString();
}
- @NotNull
private static String escapeXpath(final String xpath) {
// See https://jira.onap.org/browse/CPS-500 for limitations of this basic escape mechanism
return xpath.replace("[@", "\\[@");
}
- @Nullable
private static Integer getTextValueAsInt(final CpsPathQuery cpsPathQuery) {
try {
return Integer.parseInt(cpsPathQuery.getTextFunctionConditionValue());
diff --git a/cps-ri/src/main/java/org/onap/cps/spi/repository/YangResourceRepository.java b/cps-ri/src/main/java/org/onap/cps/spi/repository/YangResourceRepository.java
index 764e2dfc74..3f5c43decc 100644
--- a/cps-ri/src/main/java/org/onap/cps/spi/repository/YangResourceRepository.java
+++ b/cps-ri/src/main/java/org/onap/cps/spi/repository/YangResourceRepository.java
@@ -20,6 +20,7 @@
package org.onap.cps.spi.repository;
+import java.util.Collection;
import java.util.List;
import java.util.Set;
import javax.validation.constraints.NotNull;
@@ -41,11 +42,11 @@ public interface YangResourceRepository extends JpaRepository<YangResourceEntity
+ "yang_resource.module_name AS module_name,\n"
+ "yang_resource.revision AS revision\n"
+ "FROM\n"
- + "dataspace dataspace\n"
- + "JOIN schema_set schema_set ON schema_set.dataspace_id = dataspace.id\n"
- + "JOIN schema_set_yang_resources schema_set_yang_resource ON schema_set_yang_resource.schema_set_id = "
+ + "dataspace\n"
+ + "JOIN schema_set ON schema_set.dataspace_id = dataspace.id\n"
+ + "JOIN schema_set_yang_resources ON schema_set_yang_resources.schema_set_id = "
+ "schema_set.id\n"
- + "JOIN yang_resource yang_resource ON yang_resource.id = schema_set_yang_resource.yang_resource_id\n"
+ + "JOIN yang_resource ON yang_resource.id = schema_set_yang_resources.yang_resource_id\n"
+ "WHERE\n"
+ "dataspace.name = :dataspaceName", nativeQuery = true)
Set<YangResourceModuleReference> findAllModuleReferences(@Param("dataspaceName") String dataspaceName);
@@ -54,18 +55,32 @@ public interface YangResourceRepository extends JpaRepository<YangResourceEntity
+ "yang_resource.module_Name AS module_name,\n"
+ "yang_resource.revision AS revision\n"
+ "FROM\n"
- + "dataspace dataspace\n"
- + "JOIN anchor anchor ON anchor.dataspace_id = dataspace.id\n"
- + "JOIN schema_set schema_set ON schema_set.id = anchor.schema_set_id\n"
- + "JOIN schema_set_yang_resources schema_set_yang_resource ON schema_set_yang_resource.schema_set_id = "
+ + "dataspace\n"
+ + "JOIN anchor ON anchor.dataspace_id = dataspace.id\n"
+ + "JOIN schema_set ON schema_set.id = anchor.schema_set_id\n"
+ + "JOIN schema_set_yang_resources ON schema_set_yang_resources.schema_set_id = "
+ "schema_set.id\n"
- + "JOIN yang_resource yang_resource ON yang_resource.id = schema_set_yang_resource.yang_resource_id\n"
+ + "JOIN yang_resource ON yang_resource.id = schema_set_yang_resources.yang_resource_id\n"
+ "WHERE\n"
+ "dataspace.name = :dataspaceName AND\n"
+ "anchor.name =:anchorName", nativeQuery = true)
Set<YangResourceModuleReference> findAllModuleReferences(
@Param("dataspaceName") String dataspaceName, @Param("anchorName") String anchorName);
+ @Query(value = "SELECT DISTINCT\n"
+ + "yang_resource.*\n"
+ + "FROM\n"
+ + "dataspace\n"
+ + "JOIN schema_set ON schema_set.dataspace_id = dataspace.id\n"
+ + "JOIN schema_set_yang_resources ON schema_set_yang_resources.schema_set_id = "
+ + "schema_set.id\n"
+ + "JOIN yang_resource ON yang_resource.id = schema_set_yang_resources.yang_resource_id\n"
+ + "WHERE\n"
+ + "dataspace.name = :dataspaceName and yang_resource.module_Name IN (:moduleNames)", nativeQuery = true)
+ Set<YangResourceModuleReference> findAllModuleReferences(@Param("dataspaceName") String dataspaceName,
+ @Param("moduleNames") Collection<String> moduleNames);
+
+
@Query(value = "SELECT id FROM yang_resource WHERE module_name=:name and revision=:revision", nativeQuery = true)
Long getIdByModuleNameAndRevision(@Param("name") String moduleName, @Param("revision") String revision);
diff --git a/cps-ri/src/test/groovy/org/onap/cps/spi/impl/CpsAdminPersistenceServiceSpec.groovy b/cps-ri/src/test/groovy/org/onap/cps/spi/impl/CpsAdminPersistenceServiceSpec.groovy
index cff8d56321..c37651089f 100644
--- a/cps-ri/src/test/groovy/org/onap/cps/spi/impl/CpsAdminPersistenceServiceSpec.groovy
+++ b/cps-ri/src/test/groovy/org/onap/cps/spi/impl/CpsAdminPersistenceServiceSpec.groovy
@@ -26,6 +26,7 @@ import org.onap.cps.spi.exceptions.AlreadyDefinedException
import org.onap.cps.spi.exceptions.AnchorNotFoundException
import org.onap.cps.spi.exceptions.DataspaceNotFoundException
import org.onap.cps.spi.exceptions.SchemaSetNotFoundException
+import org.onap.cps.spi.exceptions.ModuleNamesNotFoundException
import org.onap.cps.spi.model.Anchor
import org.springframework.beans.factory.annotation.Autowired
import org.springframework.test.context.jdbc.Sql
@@ -37,6 +38,7 @@ class CpsAdminPersistenceServiceSpec extends CpsPersistenceSpecBase {
static final String SET_DATA = '/data/anchor.sql'
+ static final String SAMPLE_DATA_FOR_ANCHORS_WITH_MODULES = '/data/anchors-schemaset-modules.sql'
static final String EMPTY_DATASPACE_NAME = 'DATASPACE-002'
static final Integer DELETED_ANCHOR_ID = 3001
static final Long DELETED_FRAGMENT_ID = 4001
@@ -140,4 +142,37 @@ class CpsAdminPersistenceServiceSpec extends CpsPersistenceSpecBase {
'dataspace does not exist' | 'unknown' | 'not-relevant' || DataspaceNotFoundException
'anchor does not exists' | DATASPACE_NAME | 'unknown' || AnchorNotFoundException
}
+
+ @Sql([CLEAR_DATA, SAMPLE_DATA_FOR_ANCHORS_WITH_MODULES])
+ def 'Get anchors that have #scenario.'() {
+ when: 'all anchor are retrieved for the given dataspace name and module names'
+ def anchors = objectUnderTest.getAnchors('DATASPACE-001', inputModuleNames)
+ then: 'the expected anchors are returned'
+ anchors.size() == expectedAnchors.size()
+ anchors.containsAll(expectedAnchors)
+ where: 'the following data is used'
+ scenario | inputModuleNames || expectedAnchors
+ 'one module' | ['MODULE-NAME-001'] || [buildAnchor('ANCHOR1', 'DATASPACE-001', 'SCHEMA-SET-001')]
+ 'two modules' | ['MODULE-NAME-001', 'MODULE-NAME-002'] || [buildAnchor('ANCHOR1', 'DATASPACE-001', 'SCHEMA-SET-001'), buildAnchor('ANCHOR2', 'DATASPACE-001', 'SCHEMA-SET-002'), buildAnchor('ANCHOR3', 'DATASPACE-001', 'SCHEMA-SET-004')]
+ 'a module attached to multiple anchors' | ['MODULE-NAME-003'] || [buildAnchor('ANCHOR1', 'DATASPACE-001', 'SCHEMA-SET-001'), buildAnchor('ANCHOR2', 'DATASPACE-001', 'SCHEMA-SET-002')]
+ 'same module with different revisions' | ['MODULE-NAME-002'] || [buildAnchor('ANCHOR2', 'DATASPACE-001', 'SCHEMA-SET-002'), buildAnchor('ANCHOR3', 'DATASPACE-001', 'SCHEMA-SET-004')]
+ }
+
+ @Sql([CLEAR_DATA, SAMPLE_DATA_FOR_ANCHORS_WITH_MODULES])
+ def 'Get all anchors for an #scenario.'() {
+ when: 'attempt to get anchors'
+ objectUnderTest.getAnchors(dataspaceName, moduleNames)
+ then: 'the correct exception is thrown with the relevant details'
+ def thrownException = thrown(expectedException)
+ thrownException.details.contains(expectedMessageDetails)
+ where: 'the following data is used'
+ scenario | dataspaceName | moduleNames || expectedException | expectedMessageDetails
+ 'existing module in an unknown dataspace' | 'db-does-not-exist' | ['does-not-matter'] || DataspaceNotFoundException | 'db-does-not-exist'
+ 'unknown module in an existing dataspace' | 'DATASPACE-001' | ['module-does-not-exist'] || ModuleNamesNotFoundException | 'module-does-not-exist'
+ 'unknown module and known module in an existing dataspace' | 'DATASPACE-001' | ['MODULE-NAME-001', 'module-does-not-exist'] || ModuleNamesNotFoundException | 'module-does-not-exist'
+ }
+
+ def buildAnchor(def anchorName, def dataspaceName, def SchemaSetName) {
+ return Anchor.builder().name(anchorName).dataspaceName(dataspaceName).schemaSetName(SchemaSetName).build()
+ }
}
diff --git a/cps-ri/src/test/resources/data/anchors-schemaset-modules.sql b/cps-ri/src/test/resources/data/anchors-schemaset-modules.sql
new file mode 100644
index 0000000000..d2b67f5aea
--- /dev/null
+++ b/cps-ri/src/test/resources/data/anchors-schemaset-modules.sql
@@ -0,0 +1,45 @@
+/*
+ ============LICENSE_START=======================================================
+ Copyright (C) 2021 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=========================================================
+*/
+
+INSERT INTO DATASPACE (ID, NAME) VALUES
+ (1001, 'DATASPACE-001'), (1002, 'DATASPACE-002');
+
+INSERT INTO SCHEMA_SET (ID, NAME, DATASPACE_ID) VALUES
+ (2001, 'SCHEMA-SET-001', 1001),
+ (2002, 'SCHEMA-SET-002', 1002),
+ (2003, 'SCHEMA-SET-003', 1001),
+ (2004, 'SCHEMA-SET-004', 1001);
+
+INSERT INTO YANG_RESOURCE (ID, NAME, CONTENT, CHECKSUM, MODULE_NAME, REVISION) VALUES
+ (3001, 'module1@2020-02-02.yang', 'CONTENT-001', 'checksum1','MODULE-NAME-001',null),
+ (3002, 'module2@2020-02-02.yang', 'CONTENT-002', 'checksum2','MODULE-NAME-002','REVISION-002'),
+ (3003, 'module3@2020-02-02.yang', 'CONTENT-003', 'checksum3','MODULE-NAME-003','REVISION-003'),
+ (3004, 'module4@2020-02-02.yang', 'CONTENT-004', 'checksum4','MODULE-NAME-004','REVISION-004'),
+ (3005, 'module5@2020-03-02.yang', 'CONTENT-005', 'checksum5','MODULE-NAME-002','REVISION-003');
+
+INSERT INTO SCHEMA_SET_YANG_RESOURCES (SCHEMA_SET_ID, YANG_RESOURCE_ID) VALUES
+ (2001, 3001), (2002, 3002),
+ (2001, 3003), (2002, 3003),
+ (2003, 3004), (2004, 3005);
+
+INSERT INTO ANCHOR (ID, NAME, DATASPACE_ID, SCHEMA_SET_ID) VALUES
+ (6001, 'ANCHOR1', 1001, 2001),
+ (6002, 'ANCHOR2', 1001, 2002),
+ (6003, 'ANCHOR3', 1001, 2004);
diff --git a/cps-service/src/main/java/org/onap/cps/spi/CpsAdminPersistenceService.java b/cps-service/src/main/java/org/onap/cps/spi/CpsAdminPersistenceService.java
index b05385fbf4..f29735fa01 100755
--- a/cps-service/src/main/java/org/onap/cps/spi/CpsAdminPersistenceService.java
+++ b/cps-service/src/main/java/org/onap/cps/spi/CpsAdminPersistenceService.java
@@ -59,6 +59,15 @@ public interface CpsAdminPersistenceService {
Collection<Anchor> getAnchors(@NonNull String dataspaceName);
/**
+ * Get anchors for the given dataspace name and collection of module names.
+ *
+ * @param dataspaceName dataspace name
+ * @param moduleNames a collection of module names
+ * @return a collection of anchors
+ */
+ Collection<Anchor> getAnchors(String dataspaceName, Collection<String> moduleNames);
+
+ /**
* Get an anchor in the given dataspace using the anchor name.
*
* @param dataspaceName dataspace name
diff --git a/cps-service/src/main/java/org/onap/cps/spi/exceptions/ModuleNamesNotFoundException.java b/cps-service/src/main/java/org/onap/cps/spi/exceptions/ModuleNamesNotFoundException.java
new file mode 100644
index 0000000000..ee4295ea75
--- /dev/null
+++ b/cps-service/src/main/java/org/onap/cps/spi/exceptions/ModuleNamesNotFoundException.java
@@ -0,0 +1,41 @@
+/*
+ * ============LICENSE_START=======================================================
+ * Copyright (C) 2021 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.spi.exceptions;
+
+import java.util.Collection;
+
+@SuppressWarnings("squid:S110") // Team agreed to accept 6 levels of inheritance for CPS Exceptions
+public class ModuleNamesNotFoundException extends CpsAdminException {
+
+ private static final long serialVersionUID = 3105694256583924137L;
+
+ /**
+ * Constructor.
+ *
+ * @param dataspaceName dataspace name
+ * @param moduleNames module names
+ */
+ public ModuleNamesNotFoundException(final String dataspaceName, final Collection<String> moduleNames) {
+ super("Yang resource not found",
+ String.format("No yang resources found for %s in dataspace %s.",
+ moduleNames, dataspaceName));
+ }
+}