Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
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
7 changes: 7 additions & 0 deletions entity/src/sbom_package.rs
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,13 @@ pub enum Relation {
)]
Cpe,

#[sea_orm(
belongs_to = "super::sbom_package_cpe_ref::Entity",
from = "(Column::SbomId)",
to = "(super::sbom_package_cpe_ref::Column::SbomId)"
)]
CpeSbomId,

#[sea_orm(
belongs_to = "super::sbom_package_license::Entity",
from = "(Column::SbomId, Column::NodeId)",
Expand Down
96 changes: 95 additions & 1 deletion modules/analysis/src/endpoints/tests/latest_filters.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
use super::req::*;
use crate::test::caller;
use rstest::*;
use serde_json::json;
use serde_json::{Value, json};
use test_context::test_context;
use trustify_test_context::{TrustifyContext, subset::ContainsSubset};

Expand Down Expand Up @@ -470,3 +470,97 @@ async fn test_tc2578(

Ok(())
}

#[test_context(TrustifyContext)]
#[test_log::test(actix_web::test)]
async fn tc_3170_3a(ctx: &TrustifyContext) -> Result<(), anyhow::Error> {
let app = caller(ctx).await?;

ctx.ingest_documents([
"cyclonedx/rh/latest_filters/container/quay_builder_qemu_rhcos_rhel8_2025-02-24/quay-builder-qemu-rhcos-rhel-8-product.json",
"cyclonedx/rh/latest_filters/container/quay_builder_qemu_rhcos_rhel8_2025-02-24/quay-builder-qemu-rhcos-rhel-8-image-index.json",
"cyclonedx/rh/latest_filters/container/quay_builder_qemu_rhcos_rhel8_2025-02-24/quay-builder-qemu-rhcos-rhel-8-amd64.json",
])
.await?;

let req = Req {
what: What::Q("purl~pkg:oci/quay-builder-qemu-rhcos-rhel8"),
ancestors: Some(10),
..Default::default()
};

// request latest
let latest_response = app
.req(Req {
latest: true,
..req
})
.await?;
log::info!("{:?}", latest_response);

assert_eq!(latest_response["total"], 8);

Ok(())
}
#[test_context(TrustifyContext)]
#[test_log::test(actix_web::test)]
async fn tc_3170_3b(ctx: &TrustifyContext) -> Result<(), anyhow::Error> {
let app = caller(ctx).await?;

ctx.ingest_documents([
"cyclonedx/rh/latest_filters/container/quay_builder_qemu_rhcos_rhel8_2025-02-24/quay-builder-qemu-rhcos-rhel-8-product.json",
"cyclonedx/rh/latest_filters/container/quay_builder_qemu_rhcos_rhel8_2025-02-24/quay-builder-qemu-rhcos-rhel-8-image-index.json",
"cyclonedx/rh/latest_filters/container/quay_builder_qemu_rhcos_rhel8_2025-02-24/quay-builder-qemu-rhcos-rhel-8-amd64.json",
])
.await?;

let req = Req {
what: What::Q("purl~pkg:oci/quay-builder-qemu-rhcos-rhel8"),
ancestors: Some(10),
..Default::default()
};

// request latest
let mut latest_response = app
.req(Req {
latest: true,
..req
})
.await?;

sort(&mut latest_response["items"]);

// request all
let mut response = app
.req(Req {
latest: false,
..req
})
.await?;

sort(&mut response["items"]);

// must yield the same result

assert_eq!(response, latest_response);

Ok(())
}

/// sort all entries by node_id. This includes recursive sorting of ancestors/descendants.
fn sort(json: &mut Value) {
let Value::Array(items) = json else {
return;
};

// sort list

items.sort_unstable_by(|a, b| a["node_id"].as_str().cmp(&b["node_id"].as_str()));

// now sort child entries

for item in items {
sort(&mut item["ancestors"]);
sort(&mut item["descendants"]);
}
}
2 changes: 1 addition & 1 deletion modules/analysis/src/service/load.rs
Original file line number Diff line number Diff line change
Expand Up @@ -409,7 +409,7 @@ impl InnerService {
.column(cpe::Column::Id)
.column_as(Expr::cust(rank_sql), "rank") // Use the selected SQL string
.join(JoinType::LeftJoin, sbom_package_relation.def())
.join(JoinType::LeftJoin, sbom_package::Relation::Cpe.def())
.join(JoinType::LeftJoin, sbom_package::Relation::CpeSbomId.def())
.join(
JoinType::LeftJoin,
sbom_package_cpe_ref::Relation::Cpe.def(),
Expand Down
Loading