Skip to content

Commit

Permalink
feat: Add filter query on api/v1/analysis
Browse files Browse the repository at this point in the history
  • Loading branch information
JimFuller-RedHat committed Oct 14, 2024
1 parent e029ae9 commit 482d4a4
Show file tree
Hide file tree
Showing 2 changed files with 110 additions and 4 deletions.
54 changes: 54 additions & 0 deletions modules/analysis/src/endpoints.rs
Original file line number Diff line number Diff line change
Expand Up @@ -407,4 +407,58 @@ mod test {
);
Ok(assert_eq!(&response["total"], 2))
}

#[test_context(TrustifyContext)]
#[test(actix_web::test)]
async fn test_retrieve_query_params_endpoint(
ctx: &TrustifyContext,
) -> Result<(), anyhow::Error> {
let app = caller(ctx).await?;
ctx.ingest_documents(["spdx/simple.json"]).await?;

// filter on node_id
let uri = "/api/v1/analysis/dep?q=node_id%3DSPDXRef-A";
let request: Request = TestRequest::get().uri(uri).to_request();
let response: Value = app.call_and_read_body_json(request).await;
assert_eq!(response["items"][0]["name"], "A");
assert_eq!(&response["total"], 1);

// filter on node_id
let uri = "/api/v1/analysis/root-component?q=node_id%3DSPDXRef-B";
let request: Request = TestRequest::get().uri(uri).to_request();
let response: Value = app.call_and_read_body_json(request).await;
assert_eq!(response["items"][0]["name"], "B");
assert_eq!(&response["total"], 1);

// filter on node_id & name
let uri = "/api/v1/analysis/root-component?q=node_id%3DSPDXRef-B%26name%3DB";
let request: Request = TestRequest::get().uri(uri).to_request();
let response: Value = app.call_and_read_body_json(request).await;
assert_eq!(response["items"][0]["name"], "B");
assert_eq!(&response["total"], 1);

// filter on sbom_id (which has urn:uuid: prefix)
let sbom_id = response["items"][0]["sbom_id"].as_str().unwrap();
let uri = format!(
"/api/v1/analysis/root-component?q=sbom_id=urn:uuid:{}",
sbom_id
);
let request: Request = TestRequest::get().uri(uri.clone().as_str()).to_request();
let response: Value = app.call_and_read_body_json(request).await;
assert_eq!(&response["total"], 7);

// negative test
let sbom_id = response["items"][0]["sbom_id"].as_str().unwrap();
let uri = "/api/v1/analysis/root-component?q=sbom_id=urn:uuid:99999999-9999-9999-9999-999999999999";
let request: Request = TestRequest::get().uri(uri).to_request();
let response: Value = app.call_and_read_body_json(request).await;
assert_eq!(&response["total"], 0);

// negative test
let uri = "/api/v1/analysis/root-component?q=node_id%3DSPDXRef-B%26name%3DA";
let request: Request = TestRequest::get().uri(uri).to_request();
let response: Value = app.call_and_read_body_json(request).await;

Ok(assert_eq!(&response["total"], 0))
}
}
60 changes: 56 additions & 4 deletions modules/analysis/src/service.rs
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ use petgraph::visit::{NodeIndexable, VisitMap, Visitable};
use petgraph::Direction;
use sea_query::Order;
use std::str::FromStr;
use trustify_common::db::query::Filtering;
use trustify_common::db::ConnectionOrTransaction;
use trustify_common::purl::Purl;
use trustify_entity::relationship::Relationship;
Expand Down Expand Up @@ -273,6 +274,23 @@ pub async fn load_graphs(
}
}

fn convert_query_to_hashmap(query: &Query) -> HashMap<String, String> {
let mut query_map = HashMap::new();
if query.q.contains('=') {
for pair in query.q.split('&') {
if let Some((key, mut value)) = pair.split_once('=') {
if value.starts_with("urn:uuid:") {
value = value.strip_prefix("urn:uuid:").unwrap_or(value);
}
query_map.insert(key.to_owned(), value.to_owned());
}
}
} else {
query_map.insert("re_name".to_owned(), query.q.clone());
}
query_map
}

impl AnalysisService {
pub fn new(db: Database) -> Self {
GraphMap::get_instance();
Expand Down Expand Up @@ -340,8 +358,9 @@ impl AnalysisService {
) -> Result<PaginatedResults<AncestorSummary>, Error> {
let connection = self.db.connection(&tx);

let graph_query_map = convert_query_to_hashmap(&query);
let search_sbom_node_name_subquery = sbom_node::Entity::find()
.filter(sbom_node::Column::Name.like(format!("%{}%", query.q.as_str())))
.filtering(query)?
.select_only()
.column(sbom_node::Column::SbomId)
.distinct()
Expand Down Expand Up @@ -371,7 +390,23 @@ impl AnalysisService {
.node_indices()
.filter(|&i| {
if let Some(node) = graph.node_weight(i) {
node.name.contains(&query.q.to_string())
if let Some(re_name) = graph_query_map.get("re_name") {
// if no specific url params supplied then use contains search
node.name.contains(re_name)
} else {
// if any specific url params supplied then match equals
let matches_sbom_id = graph_query_map
.get("sbom_id")
.map_or(true, |sbom_id| node.sbom_id.eq(sbom_id));
let matches_node_id = graph_query_map
.get("node_id")
.map_or(true, |node_id| node.node_id.eq(node_id));
let matches_name =
graph_query_map.get("name").map_or(true, |name| {
!name.is_empty() && node.name.eq(name)
});
matches_sbom_id && matches_node_id && matches_name
}
} else {
false // Return false if the node does not exist
}
Expand Down Expand Up @@ -564,8 +599,9 @@ impl AnalysisService {
) -> Result<PaginatedResults<DepSummary>, Error> {
let connection = self.db.connection(&tx);

let graph_query_map = convert_query_to_hashmap(&query);
let search_sbom_node_name_subquery = sbom_node::Entity::find()
.filter(sbom_node::Column::Name.like(format!("%{}%", query.q.as_str())))
.filtering(query)?
.select_only()
.column(sbom_node::Column::SbomId)
.distinct()
Expand Down Expand Up @@ -595,7 +631,23 @@ impl AnalysisService {
.node_indices()
.filter(|&i| {
if let Some(node) = graph.node_weight(i) {
node.name.contains(&query.q.to_string())
if let Some(re_name) = graph_query_map.get("re_name") {
// if no specific url params supplied then use contains search
node.name.contains(re_name)
} else {
// if any specific url params supplied then match equals
let matches_sbom_id = graph_query_map
.get("sbom_id")
.map_or(true, |sbom_id| node.sbom_id.eq(sbom_id));
let matches_node_id = graph_query_map
.get("node_id")
.map_or(true, |node_id| node.node_id.eq(node_id));
let matches_name =
graph_query_map.get("name").map_or(true, |name| {
!name.is_empty() && node.name.eq(name)
});
matches_sbom_id && matches_node_id && matches_name
}
} else {
false // Return false if the node does not exist
}
Expand Down

0 comments on commit 482d4a4

Please sign in to comment.