summaryrefslogtreecommitdiff
path: root/src/invite
diff options
context:
space:
mode:
authorojacobson <ojacobson@noreply.codeberg.org>2025-06-21 04:22:52 +0200
committerojacobson <ojacobson@noreply.codeberg.org>2025-06-21 04:22:52 +0200
commitcd1dc0dab4b46bc5712070812192d5ce34071470 (patch)
treec94f5a42f7e734b81892c1289a1d2b566706ba7c /src/invite
parentd84ba5cd09b713fac2f193d5c05af7415ea6742d (diff)
parent4e3d5ccac99b24934c972e088cd7eb02bb95df06 (diff)
Reorganize and consolidate HTTP routes.
HTTP routes are now defined in a single, unified module, pulling them out of the topical modules they were formerly part of. This is intended to improve the navigability of the codebase. Previously, finding the handler corresponding to a specific endpoint required prior familiarity, though in practice you could usually guess from topic area. Now, all routes are defined in one place; if you know the path, you can read down the list to find the handler. Handlers themselves live with the domain they are most appropriately "part of," generally (in this version, universally) in a `handlers` submodule. The handlers themselves have been flattened down; rather than representing a path and a method, they now represent a named operation (which is suspiciously similar to the path in most cases). This means that we no longer have constructs like `crate::ui::routes::ch::channel` - it's now `crate::ui::handlers::channel` instead. ## Disclaimer I Solemnly Swear I Didn't Change Any Handlers. ## Prior art I've inadvertently reinvented Django's `urls.py` convention, and I've opted to lean into that. Merges flatter-routes-reorg into main.
Diffstat (limited to 'src/invite')
-rw-r--r--src/invite/handlers/accept/mod.rs (renamed from src/invite/routes/invite/post.rs)7
-rw-r--r--src/invite/handlers/accept/test.rs (renamed from src/invite/routes/invite/test/post.rs)30
-rw-r--r--src/invite/handlers/get/mod.rs (renamed from src/invite/routes/invite/get.rs)7
-rw-r--r--src/invite/handlers/get/test.rs (renamed from src/invite/routes/invite/test/get.rs)12
-rw-r--r--src/invite/handlers/issue/mod.rs (renamed from src/invite/routes/post.rs)3
-rw-r--r--src/invite/handlers/issue/test.rs (renamed from src/invite/routes/test.rs)5
-rw-r--r--src/invite/handlers/mod.rs9
-rw-r--r--src/invite/mod.rs8
-rw-r--r--src/invite/routes/invite/mod.rs6
-rw-r--r--src/invite/routes/invite/test/mod.rs2
-rw-r--r--src/invite/routes/mod.rs18
11 files changed, 47 insertions, 60 deletions
diff --git a/src/invite/routes/invite/post.rs b/src/invite/handlers/accept/mod.rs
index 58d15c2..0d5f08a 100644
--- a/src/invite/routes/invite/post.rs
+++ b/src/invite/handlers/accept/mod.rs
@@ -8,17 +8,20 @@ use crate::{
app::App,
clock::RequestedAt,
error::{Internal, NotFound},
- invite::app,
+ invite::{app, handlers::PathInfo},
name::Name,
token::extract::IdentityCookie,
user::{Password, User},
};
+#[cfg(test)]
+mod test;
+
pub async fn handler(
State(app): State<App>,
RequestedAt(accepted_at): RequestedAt,
identity: IdentityCookie,
- Path(invite): Path<super::PathInfo>,
+ Path(invite): Path<PathInfo>,
Json(request): Json<Request>,
) -> Result<(IdentityCookie, Json<User>), Error> {
let (login, secret) = app
diff --git a/src/invite/routes/invite/test/post.rs b/src/invite/handlers/accept/test.rs
index b204b32..cb13900 100644
--- a/src/invite/routes/invite/test/post.rs
+++ b/src/invite/handlers/accept/test.rs
@@ -1,10 +1,6 @@
use axum::extract::{Json, Path, State};
-use crate::{
- invite::{app::AcceptError, routes::invite::post},
- name::Name,
- test::fixtures,
-};
+use crate::{invite::app::AcceptError, name::Name, test::fixtures};
#[tokio::test]
async fn valid_invite() {
@@ -18,11 +14,11 @@ async fn valid_invite() {
let (name, password) = fixtures::user::propose();
let identity = fixtures::cookie::not_logged_in();
- let request = post::Request {
+ let request = super::Request {
name: name.clone(),
password: password.clone(),
};
- let (identity, Json(response)) = post::handler(
+ let (identity, Json(response)) = super::handler(
State(app.clone()),
fixtures::now(),
identity,
@@ -70,11 +66,11 @@ async fn nonexistent_invite() {
let (name, password) = fixtures::user::propose();
let identity = fixtures::cookie::not_logged_in();
- let request = post::Request {
+ let request = super::Request {
name: name.clone(),
password: password.clone(),
};
- let post::Error(error) = post::handler(
+ let super::Error(error) = super::handler(
State(app.clone()),
fixtures::now(),
identity,
@@ -106,11 +102,11 @@ async fn expired_invite() {
let (name, password) = fixtures::user::propose();
let identity = fixtures::cookie::not_logged_in();
- let request = post::Request {
+ let request = super::Request {
name: name.clone(),
password: password.clone(),
};
- let post::Error(error) = post::handler(
+ let super::Error(error) = super::handler(
State(app.clone()),
fixtures::now(),
identity,
@@ -143,11 +139,11 @@ async fn accepted_invite() {
let (name, password) = fixtures::user::propose();
let identity = fixtures::cookie::not_logged_in();
- let request = post::Request {
+ let request = super::Request {
name: name.clone(),
password: password.clone(),
};
- let post::Error(error) = post::handler(
+ let super::Error(error) = super::handler(
State(app.clone()),
fixtures::now(),
identity,
@@ -186,11 +182,11 @@ async fn conflicting_name() {
let password = fixtures::user::propose_password();
let identity = fixtures::cookie::not_logged_in();
- let request = post::Request {
+ let request = super::Request {
name: conflicting_name.clone(),
password: password.clone(),
};
- let post::Error(error) = post::handler(
+ let super::Error(error) = super::handler(
State(app.clone()),
fixtures::now(),
identity,
@@ -220,11 +216,11 @@ async fn invalid_name() {
let name = fixtures::user::propose_invalid_name();
let password = fixtures::user::propose_password();
let identity = fixtures::cookie::not_logged_in();
- let request = post::Request {
+ let request = super::Request {
name: name.clone(),
password: password.clone(),
};
- let post::Error(error) = post::handler(
+ let super::Error(error) = super::handler(
State(app.clone()),
fixtures::now(),
identity,
diff --git a/src/invite/routes/invite/get.rs b/src/invite/handlers/get/mod.rs
index f862833..bb72586 100644
--- a/src/invite/routes/invite/get.rs
+++ b/src/invite/handlers/get/mod.rs
@@ -6,12 +6,15 @@ use axum::{
use crate::{
app::App,
error::{Internal, NotFound},
- invite::{Id, Summary},
+ invite::{Id, Summary, handlers::PathInfo},
};
+#[cfg(test)]
+mod test;
+
pub async fn handler(
State(app): State<App>,
- Path(invite): Path<super::PathInfo>,
+ Path(invite): Path<PathInfo>,
) -> Result<Json<Summary>, Error> {
app.invites()
.get(&invite)
diff --git a/src/invite/routes/invite/test/get.rs b/src/invite/handlers/get/test.rs
index 0dc8a79..0f2f725 100644
--- a/src/invite/routes/invite/test/get.rs
+++ b/src/invite/handlers/get/test.rs
@@ -1,6 +1,6 @@
use axum::extract::{Json, Path, State};
-use crate::{invite::routes::invite::get, test::fixtures};
+use crate::test::fixtures;
#[tokio::test]
async fn valid_invite() {
@@ -12,7 +12,7 @@ async fn valid_invite() {
// Call endpoint
- let Json(response) = get::handler(State(app), Path(invite.id))
+ let Json(response) = super::handler(State(app), Path(invite.id))
.await
.expect("get for an existing invite succeeds");
@@ -31,13 +31,13 @@ async fn nonexistent_invite() {
// Call endpoint
let invite = fixtures::invite::fictitious();
- let error = get::handler(State(app), Path(invite.clone()))
+ let error = super::handler(State(app), Path(invite.clone()))
.await
.expect_err("get for a nonexistent invite fails");
// Verify response
- assert!(matches!(error, get::Error::NotFound(error_id) if invite == error_id));
+ assert!(matches!(error, super::Error::NotFound(error_id) if invite == error_id));
}
#[tokio::test]
@@ -55,11 +55,11 @@ async fn expired_invite() {
// Call endpoint
- let error = get::handler(State(app), Path(invite.id.clone()))
+ let error = super::handler(State(app), Path(invite.id.clone()))
.await
.expect_err("get for an expired invite fails");
// Verify response
- assert!(matches!(error, get::Error::NotFound(error_id) if invite.id == error_id));
+ assert!(matches!(error, super::Error::NotFound(error_id) if invite.id == error_id));
}
diff --git a/src/invite/routes/post.rs b/src/invite/handlers/issue/mod.rs
index f7ca76c..6085f7a 100644
--- a/src/invite/routes/post.rs
+++ b/src/invite/handlers/issue/mod.rs
@@ -4,6 +4,9 @@ use crate::{
app::App, clock::RequestedAt, error::Internal, invite::Invite, token::extract::Identity,
};
+#[cfg(test)]
+mod test;
+
pub async fn handler(
State(app): State<App>,
RequestedAt(issued_at): RequestedAt,
diff --git a/src/invite/routes/test.rs b/src/invite/handlers/issue/test.rs
index 4ea8a3d..2bf5400 100644
--- a/src/invite/routes/test.rs
+++ b/src/invite/handlers/issue/test.rs
@@ -1,6 +1,5 @@
use axum::extract::{Json, State};
-use super::post;
use crate::test::fixtures;
#[tokio::test]
@@ -13,11 +12,11 @@ async fn create_invite() {
// Call the endpoint
- let Json(invite) = post::handler(
+ let Json(invite) = super::handler(
State(app),
issued_at.clone(),
issuer.clone(),
- Json(post::Request {}),
+ Json(super::Request {}),
)
.await
.expect("creating an invite always succeeds");
diff --git a/src/invite/handlers/mod.rs b/src/invite/handlers/mod.rs
new file mode 100644
index 0000000..07a2bbe
--- /dev/null
+++ b/src/invite/handlers/mod.rs
@@ -0,0 +1,9 @@
+mod accept;
+mod get;
+mod issue;
+
+type PathInfo = crate::invite::Id;
+
+pub use accept::handler as accept;
+pub use get::handler as get;
+pub use issue::handler as issue;
diff --git a/src/invite/mod.rs b/src/invite/mod.rs
index 2d32fda..74b9ceb 100644
--- a/src/invite/mod.rs
+++ b/src/invite/mod.rs
@@ -1,11 +1,11 @@
+use crate::{clock::DateTime, normalize::nfc, user};
+
pub mod app;
+pub mod handlers;
mod id;
mod repo;
-mod routes;
-
-use crate::{clock::DateTime, normalize::nfc, user};
-pub use self::{id::Id, routes::router};
+pub use self::id::Id;
#[derive(Debug, serde::Serialize)]
pub struct Invite {
diff --git a/src/invite/routes/invite/mod.rs b/src/invite/routes/invite/mod.rs
deleted file mode 100644
index c22029a..0000000
--- a/src/invite/routes/invite/mod.rs
+++ /dev/null
@@ -1,6 +0,0 @@
-pub mod get;
-pub mod post;
-#[cfg(test)]
-pub mod test;
-
-type PathInfo = crate::invite::Id;
diff --git a/src/invite/routes/invite/test/mod.rs b/src/invite/routes/invite/test/mod.rs
deleted file mode 100644
index d6c1f06..0000000
--- a/src/invite/routes/invite/test/mod.rs
+++ /dev/null
@@ -1,2 +0,0 @@
-mod get;
-mod post;
diff --git a/src/invite/routes/mod.rs b/src/invite/routes/mod.rs
deleted file mode 100644
index d83efc6..0000000
--- a/src/invite/routes/mod.rs
+++ /dev/null
@@ -1,18 +0,0 @@
-use axum::{
- Router,
- routing::{get, post},
-};
-
-use crate::app::App;
-
-mod invite;
-mod post;
-#[cfg(test)]
-mod test;
-
-pub fn router() -> Router<App> {
- Router::new()
- .route("/api/invite", post(post::handler))
- .route("/api/invite/{invite}", get(invite::get::handler))
- .route("/api/invite/{invite}", post(invite::post::handler))
-}