From e1a8827a587949f3ac0c7a299e2745820ab368e1 Mon Sep 17 00:00:00 2001 From: Owen Jacobson Date: Sun, 24 Aug 2025 03:56:02 -0400 Subject: Stop returning an HTTP body from `POST /api/invite/:id`. As with the previous commits, the body was never actually being used. --- docs/api/invitations.md | 39 ++++++-------------------------- src/event/handlers/stream/test/invite.rs | 14 ++++++++++-- src/invite/app.rs | 6 ++--- src/invite/handlers/accept/mod.rs | 8 +++---- src/invite/handlers/accept/test.rs | 9 ++++---- src/user/history.rs | 1 + 6 files changed, 31 insertions(+), 46 deletions(-) diff --git a/docs/api/invitations.md b/docs/api/invitations.md index 04e92c8..9b86b9e 100644 --- a/docs/api/invitations.md +++ b/docs/api/invitations.md @@ -39,8 +39,7 @@ The request must be an empty JSON object. ### Success -This endpoint will respond with a status of -`200 Okay` when successful. The body of the response will be a JSON object describing the new invitation: +This endpoint will respond with a status of `200 Okay` when successful. The body of the response will be a JSON object describing the new invitation: ```json { @@ -58,8 +57,7 @@ The response will include the following fields: | `issuer` | string | The user ID of the invitation's issuer. | | `issued_at` | string | The timestamp from which the invitation will expire. | -Clients and their operators are responsible for delivering the invitation to the invitee. Clients are strongly recommended to construct a URL for the invitation so that the invitee can take action on it easily. The included client supports URLs of the format -`https://example.net/invite/:id` (with the `:id` placeholder substituted with the invitation's ID). +Clients and their operators are responsible for delivering the invitation to the invitee. Clients are strongly recommended to construct a URL for the invitation so that the invitee can take action on it easily. The included client supports URLs of the format `https://example.net/invite/:id` (with the `:id` placeholder substituted with the invitation's ID). ## `GET /api/invite/:id` @@ -75,8 +73,7 @@ This endpoint requires the following path parameter: ### On success -This endpoint will respond with a status of -`200 Okay` when successful. The body of the response will be a JSON object describing the invitation: +This endpoint will respond with a status of `200 Okay` when successful. The body of the response will be a JSON object describing the invitation: ```json { @@ -96,13 +93,11 @@ The response will include the following fields: | `issuer` | string | The name of the invitation's issuer. | | `issued_at` | string | The timestamp from which the invitation will expire. | -Clients should present the -`issuer` to the user when presenting an invitation, so as to personalize the invitation and help them understand their connection with the service. +Clients should present the `issuer` to the user when presenting an invitation, so as to personalize the invitation and help them understand their connection with the service. ### Invitation not found -This endpoint will respond with a status of -`404 Not Found` when the invitation ID either does not exist, or has already been accepted. +This endpoint will respond with a status of `404 Not Found` when the invitation ID either does not exist, or has already been accepted. ## `POST /api/invite/:id` @@ -146,29 +141,9 @@ The proposed `name` must be valid. The precise definition of valid is still up i -This endpoint will respond with a status of -`200 Okay` when successful. The body of the response will be a JSON object describing the newly-created user: - -```json -{ - "id": "Uabcd1234", - "name": "Andrea" -} -``` - -The response will include the following fields: - -| Field | Type | Description | -| :----- | :----- | :------------------------------------------------------------------------------------------------------------------------------------------------- | -| `id` | string | A unique identifier for the newly-created user. This can be used to associate the user with other events, or to make API calls targeting the user. | -| `name` | string | The user's name. | - -The returned name may not be identical to the name requested, as the name will be converted to [normalization form C](http://www.unicode.org/reports/tr15/) automatically. The returned name will include this normalization; the service will use the normalized name elsewhere, and does not store the originally requested name. - -The provided password will also be converted to normalization form C. However, the normalized password is not returned to the client. +This endpoint will respond with a status of `204 No Content` when successful. -The response will include a `Set-Cookie` header for the -`identity` cookie, providing the client with a newly-minted identity token associated with the login created for this request. See the [authentication](./authentication.md) section for details on how this cookie may be used. +The response will include a `Set-Cookie` header for the `identity` cookie, providing the client with a newly-minted identity token associated with the login created for this request. See the [authentication](./authentication.md) section for details on how this cookie may be used. The cookie will expire if it is not used regularly. diff --git a/src/event/handlers/stream/test/invite.rs b/src/event/handlers/stream/test/invite.rs index 01372ce..f2d6950 100644 --- a/src/event/handlers/stream/test/invite.rs +++ b/src/event/handlers/stream/test/invite.rs @@ -28,11 +28,16 @@ async fn accepting_invite() { // Accept the invite let (name, password) = fixtures::user::propose(); - let (joiner, _) = app + let joiner = app .invites() .accept(&invite.id, &name, &password, &fixtures::now()) .await .expect("accepting an invite succeeds"); + let (_, joiner) = app + .tokens() + .validate(&joiner, &fixtures::now()) + .await + .expect("a newly-issued secret is valid"); // Expect a login created event @@ -57,11 +62,16 @@ async fn previously_accepted_invite() { // Accept the invite let (name, password) = fixtures::user::propose(); - let (joiner, _) = app + let joiner = app .invites() .accept(&invite.id, &name, &password, &fixtures::now()) .await .expect("accepting an invite succeeds"); + let (_, joiner) = app + .tokens() + .validate(&joiner, &fixtures::now()) + .await + .expect("a newly-issued secret is valid"); // Subscribe diff --git a/src/invite/app.rs b/src/invite/app.rs index a286a8f..14c1440 100644 --- a/src/invite/app.rs +++ b/src/invite/app.rs @@ -47,7 +47,7 @@ impl<'a> Invites<'a> { name: &Name, password: &Password, accepted_at: &DateTime, - ) -> Result<(User, Secret), AcceptError> { + ) -> Result { let create = Create::begin(name, password, accepted_at); let mut tx = self.db.begin().await?; @@ -74,9 +74,9 @@ impl<'a> Invites<'a> { let secret = tx.tokens().issue(stored.user(), accepted_at).await?; tx.commit().await?; - let login = stored.publish(self.events); + let _ = stored.publish(self.events); - Ok((login.as_created(), secret)) + Ok(secret) } pub async fn expire(&self, relative_to: &DateTime) -> Result<(), sqlx::Error> { diff --git a/src/invite/handlers/accept/mod.rs b/src/invite/handlers/accept/mod.rs index 9fa4d6a..cdf385f 100644 --- a/src/invite/handlers/accept/mod.rs +++ b/src/invite/handlers/accept/mod.rs @@ -7,12 +7,12 @@ use axum::{ use crate::{ app::App, clock::RequestedAt, + empty::Empty, error::{Internal, NotFound}, invite::{app, handlers::PathInfo}, name::Name, password::Password, token::extract::IdentityCookie, - user::User, }; #[cfg(test)] @@ -24,14 +24,14 @@ pub async fn handler( identity: IdentityCookie, Path(invite): Path, Json(request): Json, -) -> Result<(IdentityCookie, Json), Error> { - let (login, secret) = app +) -> Result<(IdentityCookie, Empty), Error> { + let secret = app .invites() .accept(&invite, &request.name, &request.password, &accepted_at) .await .map_err(Error)?; let identity = identity.set(secret); - Ok((identity, Json(login))) + Ok((identity, Empty)) } #[derive(serde::Deserialize)] diff --git a/src/invite/handlers/accept/test.rs b/src/invite/handlers/accept/test.rs index 7139985..adc7aa4 100644 --- a/src/invite/handlers/accept/test.rs +++ b/src/invite/handlers/accept/test.rs @@ -1,6 +1,6 @@ use axum::extract::{Json, Path, State}; -use crate::{invite::app::AcceptError, name::Name, test::fixtures}; +use crate::{empty::Empty, invite::app::AcceptError, name::Name, test::fixtures}; #[tokio::test] async fn valid_invite() { @@ -18,7 +18,7 @@ async fn valid_invite() { name: name.clone(), password: password.clone(), }; - let (identity, Json(response)) = super::handler( + let (identity, Empty) = super::handler( State(app.clone()), fixtures::now(), identity, @@ -31,7 +31,6 @@ async fn valid_invite() { // Verify the response assert!(identity.secret().is_some()); - assert_eq!(name, response.name); // Verify that the issued token is valid @@ -43,7 +42,7 @@ async fn valid_invite() { .validate(&secret, &fixtures::now()) .await .expect("newly-issued identity cookie is valid"); - assert_eq!(response, login); + assert_eq!(name, login.name); // Verify that the given credentials can log in @@ -57,7 +56,7 @@ async fn valid_invite() { .validate(&secret, &fixtures::now()) .await .expect("validating a newly-issued token secret succeeds"); - assert_eq!(response, login); + assert_eq!(name, login.name); } #[tokio::test] diff --git a/src/user/history.rs b/src/user/history.rs index 72e0aee..4f99130 100644 --- a/src/user/history.rs +++ b/src/user/history.rs @@ -20,6 +20,7 @@ impl History { // if this returns a redacted or modified version of the user. If we implement // renames by redacting the original name, then this should return the edited // user, not the original, even if that's not how it was "as created.") + #[cfg(test)] pub fn as_created(&self) -> User { self.user.clone() } -- cgit v1.2.3