aboutsummaryrefslogtreecommitdiffstats
path: root/cps-ri
diff options
context:
space:
mode:
Diffstat (limited to 'cps-ri')
-rwxr-xr-xcps-ri/src/main/java/org/onap/cps/spi/entities/FragmentEntity.java5
-rw-r--r--cps-ri/src/main/java/org/onap/cps/spi/impl/CpsDataPersistenceServiceImpl.java21
-rw-r--r--cps-ri/src/main/java/org/onap/cps/spi/repository/FragmentNativeRepository.java28
-rw-r--r--cps-ri/src/main/java/org/onap/cps/spi/repository/FragmentNativeRepositoryImpl.java61
-rw-r--r--cps-ri/src/test/groovy/org/onap/cps/spi/impl/CpsDataPersistenceQueryDataNodeSpec.groovy14
-rwxr-xr-xcps-ri/src/test/groovy/org/onap/cps/spi/impl/CpsDataPersistenceServiceIntegrationSpec.groovy6
-rw-r--r--cps-ri/src/test/groovy/org/onap/cps/spi/impl/CpsDataPersistenceServiceSpec.groovy5
-rw-r--r--cps-ri/src/test/groovy/org/onap/cps/spi/performance/CpsDataPersistenceServiceDeletePerfTest.groovy20
-rw-r--r--cps-ri/src/test/groovy/org/onap/cps/spi/performance/CpsDataPersistenceServicePerfTest.groovy2
9 files changed, 138 insertions, 24 deletions
diff --git a/cps-ri/src/main/java/org/onap/cps/spi/entities/FragmentEntity.java b/cps-ri/src/main/java/org/onap/cps/spi/entities/FragmentEntity.java
index 2fdfa0528f..2ffbb4ae0e 100755
--- a/cps-ri/src/main/java/org/onap/cps/spi/entities/FragmentEntity.java
+++ b/cps-ri/src/main/java/org/onap/cps/spi/entities/FragmentEntity.java
@@ -1,6 +1,6 @@
/*
* ============LICENSE_START=======================================================
- * Copyright (C) 2020 Nordix Foundation.
+ * Copyright (C) 2020-2023 Nordix Foundation.
* Modifications Copyright (C) 2021 Pantheon.tech
* ================================================================================
* Licensed under the Apache License, Version 2.0 (the "License");
@@ -40,6 +40,7 @@ import javax.validation.constraints.NotNull;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
+import lombok.EqualsAndHashCode;
import lombok.Getter;
import lombok.NoArgsConstructor;
import lombok.Setter;
@@ -58,6 +59,7 @@ import org.hibernate.annotations.TypeDef;
@Entity
@Table(name = "fragment")
@TypeDef(name = "jsonb", typeClass = JsonBinaryType.class)
+@EqualsAndHashCode(onlyExplicitlyIncluded = true)
public class FragmentEntity implements Serializable {
private static final long serialVersionUID = 7737669789097119667L;
@@ -68,6 +70,7 @@ public class FragmentEntity implements Serializable {
@NotNull
@Column(columnDefinition = "text")
+ @EqualsAndHashCode.Include
private String xpath;
@Column(name = "parent_id")
diff --git a/cps-ri/src/main/java/org/onap/cps/spi/impl/CpsDataPersistenceServiceImpl.java b/cps-ri/src/main/java/org/onap/cps/spi/impl/CpsDataPersistenceServiceImpl.java
index 06ee8ecada..d2b7273fe1 100644
--- a/cps-ri/src/main/java/org/onap/cps/spi/impl/CpsDataPersistenceServiceImpl.java
+++ b/cps-ri/src/main/java/org/onap/cps/spi/impl/CpsDataPersistenceServiceImpl.java
@@ -61,6 +61,7 @@ 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.FragmentNativeRepository;
import org.onap.cps.spi.repository.FragmentQueryBuilder;
import org.onap.cps.spi.repository.FragmentRepository;
import org.onap.cps.spi.utils.SessionManager;
@@ -78,6 +79,7 @@ public class CpsDataPersistenceServiceImpl implements CpsDataPersistenceService
private final FragmentRepository fragmentRepository;
private final JsonObjectMapper jsonObjectMapper;
private final SessionManager sessionManager;
+ private final FragmentNativeRepository fragmentNativeRepositoryImpl;
private static final String REG_EX_FOR_OPTIONAL_LIST_INDEX = "(\\[@[\\s\\S]+?]){0,1})";
@@ -263,17 +265,26 @@ public class CpsDataPersistenceServiceImpl implements CpsDataPersistenceService
final DataspaceEntity dataspaceEntity = dataspaceRepository.getByName(dataspaceName);
final AnchorEntity anchorEntity = anchorRepository.getByDataspaceAndName(dataspaceEntity, anchorName);
- final Set<String> normalizedXpaths = new HashSet<>(xpaths.size());
- for (final String xpath : xpaths) {
+ final Collection<String> nonRootXpaths = new HashSet<>(xpaths);
+ final boolean haveRootXpath = nonRootXpaths.removeIf(CpsDataPersistenceServiceImpl::isRootXpath);
+
+ final Collection<String> normalizedXpaths = new HashSet<>(nonRootXpaths.size());
+ for (final String xpath : nonRootXpaths) {
try {
normalizedXpaths.add(CpsPathUtil.getNormalizedXpath(xpath));
} catch (final PathParsingException e) {
log.warn("Error parsing xpath \"{}\" in getDataNodes: {}", xpath, e.getMessage());
}
}
+ final Collection<FragmentEntity> fragmentEntities =
+ new HashSet<>(fragmentRepository.findByAnchorAndMultipleCpsPaths(anchorEntity.getId(), normalizedXpaths));
+
+ if (haveRootXpath) {
+ final List<FragmentExtract> fragmentExtracts = fragmentRepository.getTopLevelFragments(dataspaceEntity,
+ anchorEntity);
+ fragmentEntities.addAll(FragmentEntityArranger.toFragmentEntityTrees(anchorEntity, fragmentExtracts));
+ }
- final List<FragmentEntity> fragmentEntities =
- fragmentRepository.findByAnchorAndMultipleCpsPaths(anchorEntity.getId(), normalizedXpaths);
return toDataNodes(fragmentEntities, fetchDescendantsOption);
}
@@ -645,7 +656,7 @@ public class CpsDataPersistenceServiceImpl implements CpsDataPersistenceService
private boolean deleteDataNode(final FragmentEntity parentFragmentEntity, final String targetXpath) {
final String normalizedTargetXpath = CpsPathUtil.getNormalizedXpath(targetXpath);
if (parentFragmentEntity.getXpath().equals(normalizedTargetXpath)) {
- fragmentRepository.delete(parentFragmentEntity);
+ fragmentNativeRepositoryImpl.deleteFragmentEntity(parentFragmentEntity.getId());
return true;
}
if (parentFragmentEntity.getChildFragments()
diff --git a/cps-ri/src/main/java/org/onap/cps/spi/repository/FragmentNativeRepository.java b/cps-ri/src/main/java/org/onap/cps/spi/repository/FragmentNativeRepository.java
new file mode 100644
index 0000000000..4cfd79dee3
--- /dev/null
+++ b/cps-ri/src/main/java/org/onap/cps/spi/repository/FragmentNativeRepository.java
@@ -0,0 +1,28 @@
+/*
+ * ============LICENSE_START=======================================================
+ * Copyright (C) 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.spi.repository;
+
+/**
+ * This interface is used in delete fragment entity by id with child using native sql queries.
+ */
+public interface FragmentNativeRepository {
+ void deleteFragmentEntity(long fragmentEntityId);
+}
diff --git a/cps-ri/src/main/java/org/onap/cps/spi/repository/FragmentNativeRepositoryImpl.java b/cps-ri/src/main/java/org/onap/cps/spi/repository/FragmentNativeRepositoryImpl.java
new file mode 100644
index 0000000000..57dca568f2
--- /dev/null
+++ b/cps-ri/src/main/java/org/onap/cps/spi/repository/FragmentNativeRepositoryImpl.java
@@ -0,0 +1,61 @@
+/*
+ * ============LICENSE_START=======================================================
+ * Copyright (C) 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.spi.repository;
+
+import java.sql.PreparedStatement;
+import javax.persistence.EntityManager;
+import javax.persistence.PersistenceContext;
+import org.hibernate.Session;
+import org.springframework.stereotype.Repository;
+
+@Repository
+public class FragmentNativeRepositoryImpl implements FragmentNativeRepository {
+
+ private static final String DROP_FRAGMENT_CONSTRAINT
+ = "ALTER TABLE fragment DROP CONSTRAINT fragment_parent_id_fkey;";
+ private static final String ADD_FRAGMENT_CONSTRAINT_WITH_CASCADE
+ = "ALTER TABLE fragment ADD CONSTRAINT fragment_parent_id_fkey FOREIGN KEY (parent_id) "
+ + "REFERENCES fragment (id) ON DELETE CASCADE;";
+ private static final String DELETE_FRAGMENT = "DELETE FROM fragment WHERE id =?;";
+ private static final String ADD_ORIGINAL_FRAGMENT_CONSTRAINT
+ = "ALTER TABLE fragment ADD CONSTRAINT fragment_parent_id_fkey FOREIGN KEY (parent_id) "
+ + "REFERENCES fragment (id) ON DELETE NO ACTION;";
+
+ @PersistenceContext
+ private EntityManager entityManager;
+
+ @Override
+ public void deleteFragmentEntity(final long fragmentEntityId) {
+ final Session session = entityManager.unwrap(Session.class);
+ session.doWork(connection -> {
+ try (PreparedStatement preparedStatement = connection.prepareStatement(
+ DROP_FRAGMENT_CONSTRAINT
+ + ADD_FRAGMENT_CONSTRAINT_WITH_CASCADE
+ + DELETE_FRAGMENT
+ + DROP_FRAGMENT_CONSTRAINT
+ + ADD_ORIGINAL_FRAGMENT_CONSTRAINT)) {
+ preparedStatement.setLong(1, fragmentEntityId);
+ preparedStatement.executeUpdate();
+ }
+ });
+ }
+}
+
diff --git a/cps-ri/src/test/groovy/org/onap/cps/spi/impl/CpsDataPersistenceQueryDataNodeSpec.groovy b/cps-ri/src/test/groovy/org/onap/cps/spi/impl/CpsDataPersistenceQueryDataNodeSpec.groovy
index b6d2c5d65e..ba8425fef5 100644
--- a/cps-ri/src/test/groovy/org/onap/cps/spi/impl/CpsDataPersistenceQueryDataNodeSpec.groovy
+++ b/cps-ri/src/test/groovy/org/onap/cps/spi/impl/CpsDataPersistenceQueryDataNodeSpec.groovy
@@ -3,6 +3,7 @@
* Copyright (C) 2021-2022 Nordix Foundation
* Modifications Copyright (C) 2021 Pantheon.tech
* Modifications Copyright (C) 2021 Bell Canada.
+ * 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.
@@ -22,6 +23,7 @@
package org.onap.cps.spi.impl
import org.onap.cps.spi.CpsDataPersistenceService
+import org.onap.cps.spi.FetchDescendantsOption
import org.onap.cps.spi.exceptions.CpsPathException
import org.springframework.beans.factory.annotation.Autowired
import org.springframework.test.context.jdbc.Sql
@@ -41,7 +43,7 @@ class CpsDataPersistenceQueryDataNodeSpec extends CpsPersistenceSpecBase {
@Sql([CLEAR_DATA, SET_DATA])
def 'Cps Path query for leaf value(s) with : #scenario.'() {
when: 'a query is executed to get a data node by the given cps path'
- def result = objectUnderTest.queryDataNodes(DATASPACE_NAME, ANCHOR_FOR_SHOP_EXAMPLE, cpsPath, includeDescendantsOption)
+ def result = objectUnderTest.queryDataNodes(DATASPACE_NAME, ANCHOR_FOR_SHOP_EXAMPLE, cpsPath, fetchDescendantsOption)
then: 'the correct number of parent nodes are returned'
result.size() == expectedNumberOfParentNodes
then: 'the correct data is returned'
@@ -49,10 +51,12 @@ class CpsDataPersistenceQueryDataNodeSpec extends CpsPersistenceSpecBase {
assert it.getChildDataNodes().size() == expectedNumberOfChildNodes
}
where: 'the following data is used'
- scenario | cpsPath | includeDescendantsOption || expectedNumberOfParentNodes | expectedNumberOfChildNodes
- 'String and no descendants' | '/shops/shop[@id=1]/categories[@code=1]/book[@title="Dune"]' | OMIT_DESCENDANTS || 1 | 0
- 'Integer and descendants' | '/shops/shop[@id=1]/categories[@code=1]/book[@price=5]' | INCLUDE_ALL_DESCENDANTS || 1 | 1
- 'No condition no descendants' | '/shops/shop[@id=1]/categories' | OMIT_DESCENDANTS || 3 | 0
+ scenario | cpsPath | fetchDescendantsOption || expectedNumberOfParentNodes | expectedNumberOfChildNodes
+ 'String and no descendants' | '/shops/shop[@id=1]/categories[@code=1]/book[@title="Dune"]' | OMIT_DESCENDANTS || 1 | 0
+ 'Integer and descendants' | '/shops/shop[@id=1]/categories[@code=1]/book[@price=5]' | INCLUDE_ALL_DESCENDANTS || 1 | 1
+ 'No condition no descendants' | '/shops/shop[@id=1]/categories' | OMIT_DESCENDANTS || 3 | 0
+ 'Integer and level 1 descendants' | '/shops' | new FetchDescendantsOption(1) || 1 | 5
+ 'Integer and level 2 descendants' | '/shops/shop[@id=1]' | new FetchDescendantsOption(2) || 1 | 3
}
@Sql([CLEAR_DATA, SET_DATA])
diff --git a/cps-ri/src/test/groovy/org/onap/cps/spi/impl/CpsDataPersistenceServiceIntegrationSpec.groovy b/cps-ri/src/test/groovy/org/onap/cps/spi/impl/CpsDataPersistenceServiceIntegrationSpec.groovy
index 6252fff56c..5f48469c01 100755
--- a/cps-ri/src/test/groovy/org/onap/cps/spi/impl/CpsDataPersistenceServiceIntegrationSpec.groovy
+++ b/cps-ri/src/test/groovy/org/onap/cps/spi/impl/CpsDataPersistenceServiceIntegrationSpec.groovy
@@ -20,6 +20,7 @@
* SPDX-License-Identifier: Apache-2.0
* ============LICENSE_END=========================================================
*/
+
package org.onap.cps.spi.impl
import com.fasterxml.jackson.databind.ObjectMapper
@@ -303,6 +304,7 @@ class CpsDataPersistenceServiceIntegrationSpec extends CpsPersistenceSpecBase {
assert results.size() == expectedResultSize
where: 'following parameters were used'
scenario | inputXpaths || expectedResultSize
+ '0 nodes' | [] || 0
'1 node' | ["/parent-200"] || 1
'2 unique nodes' | ["/parent-200", "/parent-201"] || 2
'3 unique nodes' | ["/parent-200", "/parent-201", "/parent-202"] || 3
@@ -314,6 +316,10 @@ class CpsDataPersistenceServiceIntegrationSpec extends CpsPersistenceSpecBase {
'existing and non-existing xpaths' | ["/parent-200", "/NO-XPATH", "/parent-201"] || 2
'invalid xpath' | ["INVALID XPATH"] || 0
'valid and invalid xpaths' | ["/parent-200", "INVALID XPATH", "/parent-201"] || 2
+ 'root xpath' | ["/"] || 7
+ 'empty (root) xpath' | [""] || 7
+ 'root and top-level xpaths' | ["/", "/parent-200", "/parent-201"] || 7
+ 'root and child xpaths' | ["/", "/parent-200/child-201"] || 8
}
@Sql([CLEAR_DATA, SET_DATA])
diff --git a/cps-ri/src/test/groovy/org/onap/cps/spi/impl/CpsDataPersistenceServiceSpec.groovy b/cps-ri/src/test/groovy/org/onap/cps/spi/impl/CpsDataPersistenceServiceSpec.groovy
index 87e59c60dc..5dab87eec4 100644
--- a/cps-ri/src/test/groovy/org/onap/cps/spi/impl/CpsDataPersistenceServiceSpec.groovy
+++ b/cps-ri/src/test/groovy/org/onap/cps/spi/impl/CpsDataPersistenceServiceSpec.groovy
@@ -32,6 +32,7 @@ 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.FragmentNativeRepository
import org.onap.cps.spi.repository.FragmentRepository
import org.onap.cps.spi.utils.SessionManager
import org.onap.cps.utils.JsonObjectMapper
@@ -45,8 +46,10 @@ class CpsDataPersistenceServiceSpec extends Specification {
def mockFragmentRepository = Mock(FragmentRepository)
def jsonObjectMapper = new JsonObjectMapper(new ObjectMapper())
def mockSessionManager = Mock(SessionManager)
+ def stubFragmentNativeRepository = Stub(FragmentNativeRepository)
- def objectUnderTest = Spy(new CpsDataPersistenceServiceImpl(mockDataspaceRepository, mockAnchorRepository, mockFragmentRepository, jsonObjectMapper, mockSessionManager))
+ def objectUnderTest = Spy(new CpsDataPersistenceServiceImpl(mockDataspaceRepository, mockAnchorRepository,
+ mockFragmentRepository, jsonObjectMapper, mockSessionManager, stubFragmentNativeRepository))
def 'Storing data nodes individually when batch operation fails'(){
given: 'two data nodes and supporting repository mock behavior'
diff --git a/cps-ri/src/test/groovy/org/onap/cps/spi/performance/CpsDataPersistenceServiceDeletePerfTest.groovy b/cps-ri/src/test/groovy/org/onap/cps/spi/performance/CpsDataPersistenceServiceDeletePerfTest.groovy
index 5aae285d7b..4dd4823c95 100644
--- a/cps-ri/src/test/groovy/org/onap/cps/spi/performance/CpsDataPersistenceServiceDeletePerfTest.groovy
+++ b/cps-ri/src/test/groovy/org/onap/cps/spi/performance/CpsDataPersistenceServiceDeletePerfTest.groovy
@@ -61,8 +61,8 @@ class CpsDataPersistenceServiceDeletePerfTest extends CpsPersistencePerfSpecBase
}
stopWatch.stop()
def deleteDurationInMillis = stopWatch.getTotalTimeMillis()
- then: 'delete duration is under 6000 milliseconds'
- assert deleteDurationInMillis < 6000
+ then: 'delete duration is under 300 milliseconds'
+ assert deleteDurationInMillis < 300
}
def 'Delete 50 grandchildren (that have no descendants)'() {
@@ -74,8 +74,8 @@ class CpsDataPersistenceServiceDeletePerfTest extends CpsPersistencePerfSpecBase
}
stopWatch.stop()
def deleteDurationInMillis = stopWatch.getTotalTimeMillis()
- then: 'delete duration is under 500 milliseconds'
- assert deleteDurationInMillis < 500
+ then: 'delete duration is under 350 milliseconds'
+ assert deleteDurationInMillis < 350
}
def 'Delete 1 large data node with many descendants'() {
@@ -84,8 +84,8 @@ class CpsDataPersistenceServiceDeletePerfTest extends CpsPersistencePerfSpecBase
objectUnderTest.deleteDataNode(PERF_DATASPACE, PERF_ANCHOR, PERF_TEST_PARENT)
stopWatch.stop()
def deleteDurationInMillis = stopWatch.getTotalTimeMillis()
- then: 'delete duration is under 2500 milliseconds'
- assert deleteDurationInMillis < 2500
+ then: 'delete duration is under 250 milliseconds'
+ assert deleteDurationInMillis < 250
}
@Sql([CLEAR_DATA, PERF_TEST_DATA])
@@ -108,8 +108,8 @@ class CpsDataPersistenceServiceDeletePerfTest extends CpsPersistencePerfSpecBase
}
stopWatch.stop()
def deleteDurationInMillis = stopWatch.getTotalTimeMillis()
- then: 'delete duration is under 4000 milliseconds'
- assert deleteDurationInMillis < 4000
+ then: 'delete duration is under 1000 milliseconds'
+ assert deleteDurationInMillis < 1000
}
def 'Delete 10 list elements with keys'() {
@@ -122,8 +122,8 @@ class CpsDataPersistenceServiceDeletePerfTest extends CpsPersistencePerfSpecBase
}
stopWatch.stop()
def deleteDurationInMillis = stopWatch.getTotalTimeMillis()
- then: 'delete duration is under 6000 milliseconds'
- assert deleteDurationInMillis < 6000
+ then: 'delete duration is under 1200 milliseconds'
+ assert deleteDurationInMillis < 1200
}
@Sql([CLEAR_DATA, PERF_TEST_DATA])
diff --git a/cps-ri/src/test/groovy/org/onap/cps/spi/performance/CpsDataPersistenceServicePerfTest.groovy b/cps-ri/src/test/groovy/org/onap/cps/spi/performance/CpsDataPersistenceServicePerfTest.groovy
index 2346239dff..0407490274 100644
--- a/cps-ri/src/test/groovy/org/onap/cps/spi/performance/CpsDataPersistenceServicePerfTest.groovy
+++ b/cps-ri/src/test/groovy/org/onap/cps/spi/performance/CpsDataPersistenceServicePerfTest.groovy
@@ -29,8 +29,6 @@ import org.onap.cps.spi.repository.FragmentRepository
import org.springframework.beans.factory.annotation.Autowired
import org.springframework.test.context.jdbc.Sql
-import java.util.concurrent.TimeUnit
-
import static org.onap.cps.spi.FetchDescendantsOption.INCLUDE_ALL_DESCENDANTS
import static org.onap.cps.spi.FetchDescendantsOption.OMIT_DESCENDANTS