From 51e2c8418bfc2a92b6669a28b976cb91afcdcf9d Mon Sep 17 00:00:00 2001 From: oxalica Date: Fri, 6 Sep 2024 02:26:14 -0400 Subject: [PATCH] Include cid in item responses and update docs --- blahd/docs/webapi.yaml | 8 ++-- blahd/src/main.rs | 87 ++++++++++++++++++++---------------------- src/types.rs | 7 ++++ 3 files changed, 53 insertions(+), 49 deletions(-) diff --git a/blahd/docs/webapi.yaml b/blahd/docs/webapi.yaml index 6788f65..1a2b756 100644 --- a/blahd/docs/webapi.yaml +++ b/blahd/docs/webapi.yaml @@ -164,7 +164,7 @@ paths: content: application/json: schema: - $ref: WithSig + $ref: WithItemId> example: sig: 99a77e836538268839ed3419c649eefb043cb51d448f641cc2a1c523811aab4aacd09f92e7c0688ffd659bfc6acb764fea79979a491e132bf6a56dd23adc1d09 signee: @@ -200,7 +200,7 @@ paths: content: application/json: schema: - $ref: WithSig + $ref: WithSig example: sig: 99a77e836538268839ed3419c649eefb043cb51d448f641cc2a1c523811aab4aacd09f92e7c0688ffd659bfc6acb764fea79979a491e132bf6a56dd23adc1d09 signee: @@ -260,7 +260,7 @@ components: attrs: type: int64 last_chat: - $ref: 'WithSig' + $ref: 'WithItemId>' RoomMetadata: type: object @@ -280,6 +280,6 @@ components: items: type: array items: - $ref: 'WithSig' + $ref: 'WithItemId>' skip_token: type: string diff --git a/blahd/src/main.rs b/blahd/src/main.rs index f17a892..0e99291 100644 --- a/blahd/src/main.rs +++ b/blahd/src/main.rs @@ -13,7 +13,7 @@ use axum::{Json, Router}; use axum_extra::extract::WithRejection as R; use blah::types::{ ChatItem, ChatPayload, CreateRoomPayload, Id, MemberPermission, RoomAdminOp, RoomAdminPayload, - RoomAttrs, ServerPermission, Signee, UserKey, WithSig, + RoomAttrs, ServerPermission, Signee, UserKey, WithItemId, WithSig, }; use config::Config; use database::Database; @@ -205,7 +205,7 @@ struct ListRoomParams { filter: ListRoomFilter, // Workaround: serde(flatten) breaks deserialization // See: https://github.com/nox/serde_urlencoded/issues/33 - skip_token: Option, + skip_token: Option, top: Option, } @@ -226,10 +226,9 @@ async fn room_list( top: params.top, }; let page_len = pagination.effective_page_len(&st); - let start_rid = pagination.skip_token.unwrap_or(0); + let start_rid = pagination.skip_token.unwrap_or(Id(0)); let query = |sql: &str, params: &[(&str, &dyn ToSql)]| -> Result { - let mut last_rid = None; let rooms = st .db .get() @@ -237,21 +236,23 @@ async fn room_list( .query_map(params, |row| { // TODO: Extract this into a function. let rid = row.get("rid")?; - last_rid = Some(rid); let title = row.get("title")?; let attrs = row.get("attrs")?; let last_chat = row - .get::<_, Option>("userkey")? - .map(|user| { - Ok::<_, rusqlite::Error>(ChatItem { - sig: row.get("sig")?, - signee: Signee { - nonce: row.get("nonce")?, - timestamp: row.get("timestamp")?, - user, - payload: ChatPayload { - rich_text: row.get("rich_text")?, - room: rid, + .get::<_, Option>("cid")? + .map(|cid| { + Ok::<_, rusqlite::Error>(WithItemId { + cid, + item: ChatItem { + sig: row.get("sig")?, + signee: Signee { + nonce: row.get("nonce")?, + timestamp: row.get("timestamp")?, + user: row.get("userkey")?, + payload: ChatPayload { + rich_text: row.get("rich_text")?, + room: rid, + }, }, }, }) @@ -265,7 +266,8 @@ async fn room_list( }) })? .collect::, _>>()?; - let skip_token = (rooms.len() == page_len).then_some(()).and(last_rid); + let skip_token = + (rooms.len() == page_len).then(|| rooms.last().expect("page must not be empty").rid); Ok(RoomList { rooms, skip_token }) }; @@ -273,7 +275,7 @@ async fn room_list( ListRoomFilter::Public => query( r" SELECT `rid`, `title`, `attrs`, - `last_author`.`userkey` AS `userkey`, `timestamp`, `nonce`, `sig`, `rich_text` + `cid`, `last_author`.`userkey`, `timestamp`, `nonce`, `sig`, `rich_text` FROM `room` LEFT JOIN `room_item` USING (`rid`) LEFT JOIN `user` AS `last_author` USING (`uid`) @@ -295,7 +297,7 @@ async fn room_list( r" SELECT `rid`, `title`, `attrs`, - `last_author`.`userkey`, `timestamp`, `nonce`, `sig`, `rich_text` + `cid`, `last_author`.`userkey`, `timestamp`, `nonce`, `sig`, `rich_text` FROM `user` JOIN `room_member` USING (`uid`) JOIN `room` USING (`rid`) @@ -408,7 +410,7 @@ async fn room_create( #[serde(deny_unknown_fields, rename_all = "camelCase")] struct Pagination { /// A opaque token from previous response to fetch the next page. - skip_token: Option, + skip_token: Option, /// Maximum page size. top: Option, } @@ -424,9 +426,9 @@ impl Pagination { #[derive(Debug, Serialize)] struct RoomItems { - items: Vec, + items: Vec>, #[serde(skip_serializing_if = "Option::is_none")] - skip_token: Option, + skip_token: Option, } async fn room_get_item( @@ -440,11 +442,7 @@ async fn room_get_item( get_room_if_readable(&conn, rid, auth.into_optional()?.as_ref(), |_row| Ok(()))?; query_room_items(&st, &conn, rid, pagination)? }; - let items = items.into_iter().map(|(_, item)| item).collect(); - Ok(Json(RoomItems { - items, - skip_token: skip_token.map(|x| x.to_string()), - })) + Ok(Json(RoomItems { items, skip_token })) } async fn room_get_metadata( @@ -482,7 +480,7 @@ async fn room_get_feed( let items = items .into_iter() - .map(|(cid, item)| { + .map(|WithItemId { cid, item }| { let time = SystemTime::UNIX_EPOCH + Duration::from_secs(item.signee.timestamp); let author = FeedAuthor { name: item.signee.user.to_string(), @@ -578,7 +576,7 @@ pub struct RoomMetadata { /// Optional extra information. Only included by the global room list response. #[serde(skip_serializing_if = "Option::is_none")] - pub last_chat: Option, + pub last_chat: Option>, } fn get_room_if_readable( @@ -610,8 +608,6 @@ fn get_room_if_readable( .ok_or_else(|| error_response!(StatusCode::NOT_FOUND, "not_found", "room not found")) } -type ChatItemWithId = (u64, ChatItem); - /// Get room items with pagination parameters, /// return a page of items and the next skip_token if this is not the last page. fn query_room_items( @@ -619,7 +615,7 @@ fn query_room_items( conn: &Connection, rid: Id, pagination: Pagination, -) -> Result<(Vec, Option), ApiError> { +) -> Result<(Vec>, Option), ApiError> { let page_len = pagination.effective_page_len(st); let mut stmt = conn.prepare( r" @@ -640,25 +636,26 @@ fn query_room_items( ":limit": page_len, }, |row| { - let cid = row.get::<_, u64>("cid")?; - let item = ChatItem { - sig: row.get("sig")?, - signee: Signee { - nonce: row.get("nonce")?, - timestamp: row.get("timestamp")?, - user: row.get("userkey")?, - payload: ChatPayload { - room: rid, - rich_text: row.get("rich_text")?, + Ok(WithItemId { + cid: row.get("cid")?, + item: ChatItem { + sig: row.get("sig")?, + signee: Signee { + nonce: row.get("nonce")?, + timestamp: row.get("timestamp")?, + user: row.get("userkey")?, + payload: ChatPayload { + room: rid, + rich_text: row.get("rich_text")?, + }, }, }, - }; - Ok((cid, item)) + }) }, )? .collect::>>()?; let skip_token = - (items.len() == page_len).then(|| items.last().expect("page must not be empty").0); + (items.len() == page_len).then(|| items.last().expect("page must not be empty").cid); Ok((items, skip_token)) } diff --git a/src/types.rs b/src/types.rs index 2954ce7..1124323 100644 --- a/src/types.rs +++ b/src/types.rs @@ -24,6 +24,13 @@ impl fmt::Display for Id { } } +#[derive(Debug, Serialize, Deserialize)] +pub struct WithItemId { + pub cid: Id, + #[serde(flatten)] + pub item: T, +} + #[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)] #[serde(transparent)] pub struct UserKey(#[serde(with = "hex::serde")] pub [u8; PUBLIC_KEY_LENGTH]);