Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
28 commits
Select commit Hold shift + click to select a range
0e73c0c
Allow projects to be configured as collections with different Project…
rkg-mm Dec 2, 2023
5b559ea
Fix missing default.
rkg-mm Dec 2, 2023
61849be
Add upgrade class to set default project collection logic to NONE for…
rkg-mm Dec 2, 2023
1a5af6f
Fix unit test
rkg-mm Dec 2, 2023
b5fd40b
Exclude collection projects from portfolio metrics calculation to not…
rkg-mm Dec 2, 2023
e4f2a64
Updated copyright
rkg-mm Apr 22, 2024
f3b74c3
Add new Property to unit test data
rkg-mm Apr 23, 2024
5f5c7ab
* Fix failing unit test by enforcing parent loading
rkg-mm May 6, 2024
84698c0
Only dispatch parent metrics update event when metrics of current pro…
rkg-mm May 6, 2024
aed9e55
Fix unit tests
rkg-mm Sep 14, 2024
dfada3e
Remove HighestSemverChild logic, replace by preparation for "isLatest…
rkg-mm Sep 14, 2024
8bd3e1a
Move upgrade logic to v4.13.0 as this is the new target version for t…
rkg-mm Sep 14, 2024
1d81af0
Add collection logic changes to project metrics for further improveme…
rkg-mm Sep 16, 2024
5fc4375
Improve handling of old Metrics DB entries to avoid showing a metric …
rkg-mm Sep 16, 2024
5ad555c
Fix merge issue
rkg-mm Oct 3, 2024
8bf4556
Enable logic for collection projects using isLatest flag
rkg-mm Oct 3, 2024
cd07dcc
Add usage example in docs for collection projects.
rkg-mm Oct 4, 2024
2bf56f0
* Rename LATEST_VERSION_CHILDREN collection logic to AGGREGATE_LATEST…
rkg-mm Oct 4, 2024
f14fc9a
Allow setting NONE as classifier (useful for parents)
rkg-mm Oct 6, 2024
380107d
Merge branch 'master' of github.com:DependencyTrack/dependency-track …
nscuro Dec 7, 2024
f9769d5
Merge pull request #1 from nscuro/issue-2041-resolve-merge-conflicts
rkg-mm Dec 7, 2024
1ddb937
Add ProjectResource tests for collection logic updates
nscuro Dec 8, 2024
0c0d247
Remove workaround for unloading of project parent
nscuro Dec 8, 2024
ce3afdb
Remove outdated warning log and TODO
nscuro Dec 8, 2024
8e2015c
Update `since` in JavaDoc
nscuro Dec 8, 2024
f55d48c
Adjust `null` handling of `collectionLogic` and `collectionLogicChanged`
nscuro Dec 8, 2024
111900a
Update fetchGroup to include `parent` and `collectionLogic`
nscuro Dec 8, 2024
b840f33
Merge pull request #2 from nscuro/issue-2041-resolve-merge-conflicts
rkg-mm Dec 9, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
39 changes: 39 additions & 0 deletions docs/_docs/usage/collection-projects.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
---
title: Collection Projects
category: Usage
chapter: 2
order: 8
---

Dependency-Track does support organizing projects in a tree structure
via parent-child relationships. This can be used to organize projects
for example by department or team, but also to structure a project itself
into different sub-projects and to organize their versions.

Since v4.13 it also supports configuring parents as "Collection Projects",
which allows to define that a parent itself does not host any components,
but instead aggregates metrics of vulnerabilities and policy violations of
its children with one of three possible calculation methods:
* Aggregate all direct children
* Aggregate all direct children which have a tag of your choice
* Aggregate all direct children which are marked as latest version

This allows a wide range use cases to be displayed. See following screenshot
which demonstrates a combination of all 3 possibilities within one product,
which consists of a Web Frontend (tracking DEV, QA, PROD environment), a Backend
with multiple MicroServices (tracked separated by DEV, QA, PROD environment),
and a mobile app (tracking each released version of the app).

![collection project structure](/images/screenshots/collection-projects-structure.png)

This is just one example how you could structure your projects and make use of
collection projects to better visualize the projects state without going down into
each single level. There are many other possibilities how you can organize the portfolio.

Collection projects do not show the usual tabs for components, vulnerabilities etc.
Instead they show a list of projects contained in this collection and their metrics:

![collection projects details](/images/screenshots/collection-projects-details.png)

Collection projects can be easily identified via the "culculator" icon, and hovering it
displays the applied collection logic.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
1 change: 1 addition & 0 deletions src/main/java/org/dependencytrack/model/Classifier.java
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@
* @since 3.0.0
*/
public enum Classifier {
NONE,
APPLICATION,
FRAMEWORK,
LIBRARY,
Expand Down
41 changes: 39 additions & 2 deletions src/main/java/org/dependencytrack/model/Project.java
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,6 @@
import com.github.packageurl.MalformedPackageURLException;
import com.github.packageurl.PackageURL;
import io.swagger.v3.oas.annotations.media.Schema;

import org.dependencytrack.parser.cyclonedx.util.ModelConverter;
import org.dependencytrack.persistence.converter.OrganizationalContactsJsonConverter;
import org.dependencytrack.persistence.converter.OrganizationalEntityJsonConverter;
Expand Down Expand Up @@ -104,11 +103,19 @@
}),
@FetchGroup(name = "METRICS_UPDATE", members = {
@Persistent(name = "id"),
@Persistent(name = "parent"),
@Persistent(name = "collectionLogic"),
@Persistent(name = "collectionTag"),
@Persistent(name = "lastInheritedRiskScore"),
@Persistent(name = "uuid")
}),
@FetchGroup(name = "PARENT", members = {
@Persistent(name = "parent")
}),
@FetchGroup(name = "PORTFOLIO_METRICS_UPDATE", members = {
@Persistent(name = "id"),
@Persistent(name = "lastInheritedRiskScore"),
@Persistent(name = "uuid")
})
})
@JsonInclude(JsonInclude.Include.NON_NULL)
Expand All @@ -123,7 +130,8 @@ public enum FetchGroup {
ALL,
METADATA,
METRICS_UPDATE,
PARENT
PARENT,
PORTFOLIO_METRICS_UPDATE
}

@PrimaryKey
Expand Down Expand Up @@ -189,6 +197,15 @@ public enum FetchGroup {
@Extension(vendorName = "datanucleus", key = "enum-check-constraint", value = "true")
private Classifier classifier;

@Persistent
@Column(name = "COLLECTION_LOGIC", jdbcType = "VARCHAR", allowsNull = "true")
@Extension(vendorName = "datanucleus", key = "enum-check-constraint", value = "true")
private ProjectCollectionLogic collectionLogic;

@Persistent(defaultFetchGroup = "true")
@Column(name = "COLLECTION_TAG", allowsNull = "true")
private Tag collectionTag;

@Persistent
@Index(name = "PROJECT_CPE_IDX")
@Size(max = 255)
Expand Down Expand Up @@ -395,6 +412,26 @@ public void setClassifier(Classifier classifier) {
this.classifier = classifier;
}

public ProjectCollectionLogic getCollectionLogic() {
return collectionLogic == null
? ProjectCollectionLogic.NONE
: collectionLogic;
}

public void setCollectionLogic(ProjectCollectionLogic collectionLogic) {
this.collectionLogic = collectionLogic != ProjectCollectionLogic.NONE
? collectionLogic
: null;
}

public Tag getCollectionTag() {
return collectionTag;
}

public void setCollectionTag(Tag collectionTag) {
this.collectionTag = collectionTag;
}

public String getCpe() {
return cpe;
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
/*
* This file is part of Dependency-Track.
*
* 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
* Copyright (c) OWASP Foundation. All Rights Reserved.
*/
package org.dependencytrack.model;

/**
* Defines various types of logics to be applied to collection projects.
* Collection projects don't contain own components, instead collect their metrics and
* data from their children. The logic to apply when calculating this data is defined
* by this type.
*
* @author Ralf King
* @since 4.13.0
*/
public enum ProjectCollectionLogic {
/**
* Project is not a collection project
*/
NONE,
/**
* Aggregate data from all direct children. Respects collection logic of
* direct children collections.
*/
AGGREGATE_DIRECT_CHILDREN,
/**
* Aggregate all direct children which have a specific tag
*/
AGGREGATE_DIRECT_CHILDREN_WITH_TAG,
/**
* Aggregate all direct children marked with isLatest flag.
*/
AGGREGATE_LATEST_VERSION_CHILDREN
}
28 changes: 28 additions & 0 deletions src/main/java/org/dependencytrack/model/ProjectMetrics.java
Original file line number Diff line number Diff line change
Expand Up @@ -171,6 +171,14 @@ public class ProjectMetrics implements Serializable {
@Column(name = "POLICYVIOLATIONS_OPERATIONAL_UNAUDITED", allowsNull = "true") // New column, must allow nulls on existing data bases)
private Integer policyViolationsOperationalUnaudited;

@Persistent
@Column(name = "COLLECTION_LOGIC", allowsNull = "true")
private ProjectCollectionLogic collectionLogic;

@Persistent
@Column(name = "COLLECTION_LOGIC_CHANGED", allowsNull = "false", defaultValue = "false")
private boolean collectionLogicChanged = false;

@Persistent
@Column(name = "FIRST_OCCURRENCE", allowsNull = "false")
@NotNull
Expand Down Expand Up @@ -425,6 +433,26 @@ public void setPolicyViolationsOperationalUnaudited(int policyViolationsOperatio
this.policyViolationsOperationalUnaudited = policyViolationsOperationalUnaudited;
}

public ProjectCollectionLogic getCollectionLogic() {
return collectionLogic == null
? ProjectCollectionLogic.NONE
: collectionLogic;
}

public void setCollectionLogic(ProjectCollectionLogic collectionLogic) {
this.collectionLogic = collectionLogic != ProjectCollectionLogic.NONE
? collectionLogic
: null;
}

public boolean isCollectionLogicChanged() {
return collectionLogicChanged;
}

public void setCollectionLogicChanged(boolean collectionLogicChanged) {
this.collectionLogicChanged = collectionLogicChanged;
}

public Date getFirstOccurrence() {
return firstOccurrence;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -835,6 +835,18 @@ public long deleteComponentPropertyByUuid(final Component component, final UUID
}
}

@Override
public boolean hasComponents(final Project project) {
final Query<Component> query = pm.newQuery(Component.class, "project == :project");
query.setParameters(project);
query.setResult("count(this)");
try {
return query.executeResultUnique(Long.class) > 0;
} finally {
query.closeAll();
}
}

public void synchronizeComponentProperties(final Component component, final List<ComponentProperty> properties) {
assertPersistent(component, "component must be persistent");

Expand Down
Loading