Skip to content

Migrate from Trillium [part 7B]: aggregators and tasks#2246

Merged
jcjones merged 1 commit intomainfrom
jcj/axum-migration-part7b
May 7, 2026
Merged

Migrate from Trillium [part 7B]: aggregators and tasks#2246
jcjones merged 1 commit intomainfrom
jcj/axum-migration-part7b

Conversation

@jcjones
Copy link
Copy Markdown
Contributor

@jcjones jcjones commented May 7, 2026

Migrate all aggregator and task routes from Trillium to Axum. These are the "hard handlers" -- they make outbound HTTP calls to DAP aggregator services via trillium_client::Client.

Notable changes:

  • Split aggregators::index into index_shared (GET /api/aggregators) and index_for_account (GET /api/accounts/{id}/aggregators) instead of checking conn.param("account_id") at runtime, to make it more, uh, Axum-ish.
  • admin_create checks actor.is_admin() inline now, and returns NotFound for non-admins (matching existing behavior of hiding admin endpoints). This is somewhat a difference of how we work with middleware in Axum.
  • tasks::delete uses Query<DeleteParams> instead of QueryStrong for the ?force=true parameter
  • tasks::show uses httpdate for the Last-Modified header to match part 7A.

That change to the force query makes it strict: before, any unparseable value (e.g. ?force=strong_with_this_one, ?force=) silently became false, while now if it's not a bool, serde is going to error it out. I think that's an improvement, but I want to call it out.

The Trillium router now contains only admin routes. All Trillium handler functions for aggregators and tasks are removed. Test assertions updated from assert_not_found to assert_response 403 for routes that now go through the Axum extract_entity / FromRequestParts path.

Migrate all aggregator and task routes from Trillium to Axum. These are
the "hard handlers" — they make outbound HTTP calls to DAP aggregator
services via trillium_client::Client.

Notable changes:
- Split aggregators::index into index_shared (GET /api/aggregators) and
  index_for_account (GET /api/accounts/{id}/aggregators) instead of
  checking conn.param("account_id") at runtime
- admin_create checks actor.is_admin() inline, returns NotFound for
  non-admins (matching existing behavior of hiding admin endpoints)
- tasks::delete uses Query<DeleteParams> instead of QueryStrong for the
  ?force=true parameter
- tasks::show uses httpdate for Last-Modified header

That change to the `force` query makes it strict: before, any unparseable value
(e.g. ?force=garbage, ?force=) silently became false, while now if it's not a bool,
serde is going to error it out. I think that's an improvement, but I want to call
it out.

The Trillium router now contains only admin routes. All Trillium handler
functions for aggregators and tasks are removed. Test assertions updated
from assert_not_found to assert_response 403 for routes that now go
through the Axum extract_entity / FromRequestParts path.
@jcjones jcjones marked this pull request as ready for review May 7, 2026 00:05
@jcjones jcjones requested a review from a team as a code owner May 7, 2026 00:05
Comment thread src/routes/aggregators.rs
Comment on lines +157 to +159
if !actor.is_admin() {
return Err(Error::NotFound);
}
Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Here I'm following the model above of inline checking is_admin, but in 7C I am going to create a thin AdminPermissionsActor newtype extractor that wraps PermissionsActor, checks is_admin()and returns Error::NotFound on failure. The handlers then will just extract AdminPermissionsActor instead of PermissionsActor.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I actually decided that was brittle while mucking with it, and the correct way to do this was a route_layer, which you can see here: https://github.com/divviup/divviup-api/pull/2247/changes#diff-2f935612004a95b0b20ea32f896554a8d594d2fd9faa19319e37470a9ff34689R56

Copy link
Copy Markdown
Contributor

@tgeoghegan tgeoghegan left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't think I have any blocking remarks.

Comment thread src/routes/aggregators.rs
Comment thread src/routes/tasks.rs
Comment thread src/routes/tasks.rs
@jcjones jcjones merged commit 91680da into main May 7, 2026
11 of 12 checks passed
@jcjones jcjones deleted the jcj/axum-migration-part7b branch May 7, 2026 23:53
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants