diff --git a/src/main/java/org/dependencytrack/persistence/ComponentQueryManager.java b/src/main/java/org/dependencytrack/persistence/ComponentQueryManager.java
index 8fe96e680d..5c656a34b1 100644
--- a/src/main/java/org/dependencytrack/persistence/ComponentQueryManager.java
+++ b/src/main/java/org/dependencytrack/persistence/ComponentQueryManager.java
@@ -166,7 +166,7 @@ public PaginatedResult getComponents(final Project project, final boolean includ
" && !("+
" SELECT FROM org.dependencytrack.model.RepositoryMetaComponent m " +
" WHERE m.name == this.name " +
- " && m.namespace == this.group " +
+ " && (m.namespace == this.group || (m.namespace == null && this.group == null)) " +
" && m.latestVersion != this.version " +
" && this.purl.matches('pkg:' + m.repositoryType.toString().toLowerCase() + '/%') " +
" ).isEmpty()";
diff --git a/src/test/java/org/dependencytrack/resources/v1/ComponentResourceTest.java b/src/test/java/org/dependencytrack/resources/v1/ComponentResourceTest.java
index a5d14a8783..f58d6568b1 100644
--- a/src/test/java/org/dependencytrack/resources/v1/ComponentResourceTest.java
+++ b/src/test/java/org/dependencytrack/resources/v1/ComponentResourceTest.java
@@ -119,6 +119,50 @@ private Project prepareProject() throws MalformedPackageURLException {
return project;
}
+ /**
+ * Generate a project with ungrouped dependencies
+ * @return A project with 10 dependencies:
+ * - 7 outdated dependencies
+ * - 3 recent dependencies
+ * @throws MalformedPackageURLException
+ */
+ private Project prepareProjectUngroupedComponents() throws MalformedPackageURLException {
+ final Project project = qm.createProject("Ungrouped Application", null, null, null, null, null, true, false);
+ final List directDepencencies = new ArrayList<>();
+ // Generate 10 dependencies
+ for (int i = 0; i < 10; i++) {
+ Component component = new Component();
+ component.setProject(project);
+ component.setName("component-name-"+i);
+ component.setVersion(String.valueOf(i)+".0");
+ component.setPurl(new PackageURL(RepositoryType.PYPI.toString(), null, "component-name-"+i , String.valueOf(i)+".0", null, null));
+ component = qm.createComponent(component, false);
+ // direct depencencies
+ if (i < 4) {
+ // 4 direct depencencies, 6 transitive depencencies
+ directDepencencies.add("{\"uuid\":\"" + component.getUuid() + "\"}");
+ }
+ // Recent & Outdated
+ if ((i < 7)) {
+ final var metaComponent = new RepositoryMetaComponent();
+ metaComponent.setRepositoryType(RepositoryType.PYPI);
+ metaComponent.setName("component-name-"+i);
+ metaComponent.setLatestVersion(String.valueOf(i+1)+".0");
+ metaComponent.setLastCheck(new Date());
+ qm.persist(metaComponent);
+ } else {
+ final var metaComponent = new RepositoryMetaComponent();
+ metaComponent.setRepositoryType(RepositoryType.PYPI);
+ metaComponent.setName("component-name-"+i);
+ metaComponent.setLatestVersion(String.valueOf(i)+".0");
+ metaComponent.setLastCheck(new Date());
+ qm.persist(metaComponent);
+ }
+ }
+ project.setDirectDependencies("[" + String.join(",", directDepencencies.toArray(new String[0])) + "]");
+ return project;
+ }
+
@Test
public void getOutdatedComponentsTest() throws MalformedPackageURLException {
final Project project = prepareProject();
@@ -136,6 +180,23 @@ public void getOutdatedComponentsTest() throws MalformedPackageURLException {
assertThat(json).hasSize(100); // Default page size is 100
}
+ @Test
+ public void getUngroupedOutdatedComponentsTest() throws MalformedPackageURLException {
+ final Project project = prepareProjectUngroupedComponents();
+
+ final Response response = jersey.target(V1_COMPONENT + "/project/" + project.getUuid())
+ .queryParam("onlyOutdated", true)
+ .queryParam("onlyDirect", false)
+ .request()
+ .header(X_API_KEY, apiKey)
+ .get(Response.class);
+ assertThat(response.getStatus()).isEqualTo(HttpStatus.SC_OK);
+ assertThat(response.getHeaderString(TOTAL_COUNT_HEADER)).isEqualTo("7"); // 7 outdated dependencies, direct and transitive
+
+ final JsonArray json = parseJsonArray(response);
+ assertThat(json).hasSize(7);
+ }
+
@Test
public void getOutdatedDirectComponentsTest() throws MalformedPackageURLException {
final Project project = prepareProject();
@@ -153,6 +214,23 @@ public void getOutdatedDirectComponentsTest() throws MalformedPackageURLExceptio
assertThat(json).hasSize(75);
}
+ @Test
+ public void getUngroupedOutdatedDirectComponentsTest() throws MalformedPackageURLException {
+ final Project project = prepareProjectUngroupedComponents();
+
+ final Response response = jersey.target(V1_COMPONENT + "/project/" + project.getUuid())
+ .queryParam("onlyOutdated", true)
+ .queryParam("onlyDirect", true)
+ .request()
+ .header(X_API_KEY, apiKey)
+ .get(Response.class);
+ assertThat(response.getStatus()).isEqualTo(HttpStatus.SC_OK);
+ assertThat(response.getHeaderString(TOTAL_COUNT_HEADER)).isEqualTo("4"); // 4 outdated direct dependencies
+
+ final JsonArray json = parseJsonArray(response);
+ assertThat(json).hasSize(4);
+ }
+
@Test
public void getAllComponentsTest() throws MalformedPackageURLException {
final Project project = prepareProject();