Skip to content
Merged
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
38 changes: 38 additions & 0 deletions docs/adrs/00013-sbom-groups.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@

APPROVED

* 2026-07-06: Added PATCH endpoint for partial assignment updates, API version is now v3

## Context

In order to better organize SBOMs, we do want to group them. The idea is to start out with a simple "folder style"
Expand Down Expand Up @@ -366,6 +368,42 @@ Create a new endpoint to assign an SBOM to groups.
* 403 - if the user was authenticated but not authorized
* 412 - if the `IfMatch` header was present, but its value didn't match the stored revision

### PATCH `/api/v3/group/sbom-assignment`

Partially update SBOM group assignments. Unlike PUT (which replaces all assignments),
this endpoint adds and/or removes specific group assignments while preserving the rest.

Uses cartesian product semantics: each SBOM in `sbom_ids` gets all `add` groups added
and all `remove` groups removed.

#### Request

| part | name | type | description |
|------|------|--------------------------|-----------------------------|
| body | - | `PatchAssignmentRequest` | Groups to add and/or remove |

```rust
#[derive(Serialize, Deserialize)]
struct PatchAssignmentRequest {
/// The IDs of the SBOMs to update.
sbom_ids: Vec<String>,
/// Group IDs to add to each SBOM's assignments.
#[serde(default)]
add: Vec<String>,
/// Group IDs to remove from each SBOM's assignments.
#[serde(default)]
remove: Vec<String>,
}
```

#### Response

* 204 - if the SBOM assignments were successfully updated
* 400 - if the request could not be understood, or one or more group IDs do not exist
* 401 - if the user was not authenticated
* 403 - if the user was authenticated but not authorized
* 404 - if one or more SBOM IDs do not exist

### GET `/api/v2/sbom`

Extend existing endpoint for finding SBOMs to limit search by group.
Expand Down
48 changes: 48 additions & 0 deletions modules/fundamental/src/common/test.rs
Original file line number Diff line number Diff line change
Expand Up @@ -258,6 +258,54 @@ impl UpdateAssignments {
}
}

pub struct PatchAssignments {
sbom_ids: Vec<String>,
add: Vec<String>,
remove: Vec<String>,
expected_status: StatusCode,
}

impl PatchAssignments {
pub fn new(sbom_ids: Vec<String>) -> Self {
Self {
sbom_ids,
add: vec![],
remove: vec![],
expected_status: StatusCode::NO_CONTENT,
}
}

pub fn add_groups(mut self, group_ids: Vec<String>) -> Self {
self.add = group_ids;
self
}

pub fn remove_groups(mut self, group_ids: Vec<String>) -> Self {
self.remove = group_ids;
self
}

pub fn expect_status(mut self, status: StatusCode) -> Self {
self.expected_status = status;
self
}

pub async fn execute(self, app: &impl CallService) -> anyhow::Result<()> {
let request = TestRequest::patch()
.uri("/api/v3/group/sbom-assignment")
.set_json(json!({
"sbom_ids": self.sbom_ids,
"add": self.add,
"remove": self.remove,
}));

let response = app.call_service(request.to_request()).await;
assert_eq!(response.status(), self.expected_status);

Ok(())
}
}

pub struct Group {
pub name: String,
pub description: Option<String>,
Expand Down
34 changes: 32 additions & 2 deletions modules/fundamental/src/sbom_group/endpoints/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ use crate::Error;
use actix_web::{
HttpRequest, HttpResponse, Responder, delete, get,
http::header::{self, ETag, EntityTag, IfMatch},
post, put, web,
patch, post, put, web,
};
use sea_orm::TransactionTrait;
use serde::Serialize;
Expand Down Expand Up @@ -45,7 +45,8 @@ pub fn configure(
.service(delete)
.service(read_assignments)
.service(update_assignments)
.service(bulk_update_assignments);
.service(bulk_update_assignments)
.service(patch_assignments);
}

#[utoipa::path(
Comment thread
sourcery-ai[bot] marked this conversation as resolved.
Expand Down Expand Up @@ -335,3 +336,32 @@ async fn bulk_update_assignments(

Ok(HttpResponse::NoContent().finish())
}

#[utoipa::path(
tag = "sbomGroup",
operation_id = "patchSbomGroupAssignments",
request_body = PatchAssignmentRequest,
responses(
(status = 204, description = "The SBOM assignments were updated"),
(status = 400, description = "The request was not valid"),
(status = 401, description = "The user was not authenticated"),
(status = 403, description = "The user authenticated, but not authorized for this operation"),
(status = 404, description = "One or more SBOMs were not found"),
)
)]
#[patch("/v3/group/sbom-assignment")]
/// Partially update SBOM group assignments
async fn patch_assignments(
service: web::Data<SbomGroupService>,
db: web::Data<db::ReadWrite>,
web::Json(request): web::Json<PatchAssignmentRequest>,
_: Require<UpdateSbom>,
) -> Result<impl Responder, Error> {
let tx = db.begin().await?;
service
.patch_assignments(request.sbom_ids, request.add, request.remove, &tx)
.await?;
tx.commit().await?;

Ok(HttpResponse::NoContent().finish())
}
Loading
Loading