Skip to content

Commit ee8fa69

Browse files
committed
Materialize project hierarchies in separate table
Signed-off-by: nscuro <nscuro@protonmail.com>
1 parent 4ff92b1 commit ee8fa69

File tree

7 files changed

+630
-104
lines changed

7 files changed

+630
-104
lines changed

commons-persistence/src/main/java/org/dependencytrack/persistence/model/Project.java

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -252,6 +252,14 @@ public void setInactiveSince(Date inactiveSince) {
252252
this.inactiveSince = inactiveSince;
253253
}
254254

255+
public Project getParent() {
256+
return parent;
257+
}
258+
259+
public void setParent(Project parent) {
260+
this.parent = parent;
261+
}
262+
255263
public Collection<Project> getChildren() {
256264
return children;
257265
}
Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,57 @@
1+
/*
2+
* This file is part of Dependency-Track.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*
16+
* SPDX-License-Identifier: Apache-2.0
17+
* Copyright (c) OWASP Foundation. All Rights Reserved.
18+
*/
19+
package org.dependencytrack.persistence.repository;
20+
21+
import io.quarkus.hibernate.orm.panache.PanacheRepository;
22+
import org.dependencytrack.persistence.model.Project;
23+
24+
import jakarta.enterprise.context.ApplicationScoped;
25+
import jakarta.persistence.Query;
26+
import java.util.UUID;
27+
28+
import static org.hibernate.jpa.HibernateHints.HINT_READ_ONLY;
29+
30+
/**
31+
* @since 0.6.0
32+
*/
33+
@ApplicationScoped
34+
public class ProjectRepository implements PanacheRepository<Project> {
35+
36+
public boolean isParentOfActiveChild(final Project parent, final UUID childUuid) {
37+
final Query query = getEntityManager().createNativeQuery("""
38+
SELECT EXISTS(
39+
SELECT 1
40+
FROM "PROJECT_HIERARCHY" AS hierarchy
41+
INNER JOIN "PROJECT" AS child_project
42+
ON child_project."ID" = hierarchy."CHILD_PROJECT_ID"
43+
WHERE hierarchy."PARENT_PROJECT_ID" = :parentId
44+
AND hierarchy."DEPTH" > 0
45+
AND child_project."ID" = (SELECT "ID" FROM "PROJECT" WHERE "UUID" = :childUuid)
46+
AND child_project."INACTIVE_SINCE" IS NULL
47+
)
48+
""");
49+
50+
return (boolean) query
51+
.setParameter("parentId", parent.getId())
52+
.setParameter("childUuid", childUuid)
53+
.setHint(HINT_READ_ONLY, true)
54+
.getSingleResult();
55+
}
56+
57+
}

commons-persistence/src/main/resources/schema.sql

Lines changed: 155 additions & 75 deletions
Large diffs are not rendered by default.
Lines changed: 67 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,67 @@
1+
/*
2+
* This file is part of Dependency-Track.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*
16+
* SPDX-License-Identifier: Apache-2.0
17+
* Copyright (c) OWASP Foundation. All Rights Reserved.
18+
*/
19+
package org.dependencytrack.persistence.repository;
20+
21+
import io.quarkus.test.TestTransaction;
22+
import io.quarkus.test.junit.QuarkusTest;
23+
import org.dependencytrack.persistence.model.Project;
24+
import org.junit.jupiter.api.Test;
25+
26+
import jakarta.inject.Inject;
27+
import java.util.Date;
28+
import java.util.UUID;
29+
30+
import static org.assertj.core.api.Assertions.assertThat;
31+
32+
@QuarkusTest
33+
class ProjectRepositoryTest {
34+
35+
@Inject
36+
ProjectRepository projectRepository;
37+
38+
@Test
39+
@TestTransaction
40+
void testIsParentOfActiveChild() {
41+
final var parentProject = new Project();
42+
parentProject.setId(1);
43+
parentProject.setUuid(UUID.fromString("adbf4d72-6ebc-429a-955b-265a8b8ba997"));
44+
parentProject.setName("acme-app-parent");
45+
projectRepository.persist(parentProject);
46+
47+
final var childProjectA = new Project();
48+
childProjectA.setId(2);
49+
childProjectA.setUuid(UUID.fromString("970829b1-3112-42db-a5ab-73391463e349"));
50+
childProjectA.setParent(parentProject);
51+
childProjectA.setName("acme-app-child-a");
52+
projectRepository.persist(childProjectA);
53+
54+
final var childProjectB = new Project();
55+
childProjectB.setId(3);
56+
childProjectB.setUuid(UUID.fromString("bbf9e846-cc5a-493b-bc9a-ce944795a5ad"));
57+
childProjectB.setParent(parentProject);
58+
childProjectB.setName("acme-app-child-b");
59+
childProjectB.setInactiveSince(new Date());
60+
projectRepository.persist(childProjectB);
61+
62+
assertThat(projectRepository.isParentOfActiveChild(parentProject, childProjectA.getUuid())).isTrue();
63+
assertThat(projectRepository.isParentOfActiveChild(parentProject, childProjectB.getUuid())).isFalse();
64+
assertThat(projectRepository.isParentOfActiveChild(parentProject, parentProject.getUuid())).isFalse();
65+
}
66+
67+
}

0 commit comments

Comments
 (0)