From fdecdeccd5ba793396d3cadc01fc282782722f5d Mon Sep 17 00:00:00 2001 From: farodin91 Date: Wed, 12 Oct 2016 22:06:13 +0200 Subject: [PATCH] Add basic implementation for the tags endpoint. --- migrations/001_prerelease/down.sql | 1 + migrations/001_prerelease/up.sql | 9 ++ src/api/r0/mod.rs | 2 + src/api/r0/tags.rs | 171 +++++++++++++++++++++++++++++ src/main.rs | 1 + src/schema.rs | 10 ++ src/server.rs | 6 + src/tags.rs | 63 +++++++++++ 8 files changed, 263 insertions(+) create mode 100644 src/api/r0/tags.rs create mode 100644 src/tags.rs diff --git a/migrations/001_prerelease/down.sql b/migrations/001_prerelease/down.sql index 411c04b8..441c25aa 100644 --- a/migrations/001_prerelease/down.sql +++ b/migrations/001_prerelease/down.sql @@ -6,3 +6,4 @@ DROP TABLE room_aliases; DROP TABLE room_memberships; DROP TABLE rooms; DROP TABLE users; +DROP TABLE room_tags; diff --git a/migrations/001_prerelease/up.sql b/migrations/001_prerelease/up.sql index 672cd89a..7029dfcb 100644 --- a/migrations/001_prerelease/up.sql +++ b/migrations/001_prerelease/up.sql @@ -68,3 +68,12 @@ CREATE TABLE users ( created_at TIMESTAMP NOT NULL DEFAULT now(), updated_at TIMESTAMP NOT NULL DEFAULT now() ); + +CREATE TABLE room_tags( + id BIGSERIAL PRIMARY KEY, + user_id TEXT NOT NULL, + room_id TEXT NOT NULL, + tag TEXT NOT NULL, + content TEXT NOT NULL, + UNIQUE (user_id, room_id, tag) +); diff --git a/src/api/r0/mod.rs b/src/api/r0/mod.rs index ce51d621..6b496978 100644 --- a/src/api/r0/mod.rs +++ b/src/api/r0/mod.rs @@ -14,6 +14,7 @@ pub use self::logout::Logout; pub use self::members::Members; pub use self::registration::Register; pub use self::room_creation::CreateRoom; +pub use self::tags::{DeleteTag, GetTags, PutTag}; pub use self::versions::Versions; mod account; @@ -25,4 +26,5 @@ mod logout; mod members; mod registration; mod room_creation; +mod tags; mod versions; diff --git a/src/api/r0/tags.rs b/src/api/r0/tags.rs new file mode 100644 index 00000000..9f85fb3a --- /dev/null +++ b/src/api/r0/tags.rs @@ -0,0 +1,171 @@ +//! Endpoints for tags. + +use iron::{Chain, Handler, IronResult, Request, Response}; +use iron::status::Status; +use router::Router; +use std::io::Read; + +use db::DB; +use error::ApiError; +use middleware::{AccessTokenAuth, JsonRequest, RoomIdParam, UserIdParam}; +use modifier::SerializableResponse; +use tags::RoomTag; + +/// The `/user/:user_id/rooms/:room_id/tags` endpoint. +pub struct GetTags; + +#[derive(Debug, Serialize)] +struct GetTagsResponse { + tags: Vec, +} + +impl GetTags { + /// Create a `GetTags` with all necessary middleware. + pub fn chain() -> Chain { + let mut chain = Chain::new(GetTags); + + chain.link_before(JsonRequest); + chain.link_before(UserIdParam); + chain.link_before(RoomIdParam); + chain.link_before(AccessTokenAuth); + + chain + } +} + +impl Handler for GetTags { + fn handle(&self, request: &mut Request) -> IronResult { + let user_id = request.extensions.get::() + .expect("UserIdParam should ensure a UserId").clone(); + let room_id = request.extensions.get::() + .expect("RoomIdParam should ensure a RoomId").clone(); + + let connection = DB::from_request(request)?; + + let tags = RoomTag::find(&connection, user_id, room_id)?; + + let response = GetTagsResponse { tags: tags }; + + Ok(Response::with((Status::Ok, SerializableResponse(response)))) + } +} + +/// The `/user/:user_id/rooms/:room_id/tags/:tag` endpoint. +pub struct PutTag; + +impl PutTag { + /// Create a `GetTags` with all necessary middleware. + pub fn chain() -> Chain { + let mut chain = Chain::new(PutTag); + + chain.link_before(JsonRequest); + chain.link_before(UserIdParam); + chain.link_before(RoomIdParam); + chain.link_before(AccessTokenAuth); + + chain + } +} + +impl Handler for PutTag { + fn handle(&self, request: &mut Request) -> IronResult { + let user_id = request.extensions.get::() + .expect("UserIdParam should ensure a UserId").clone(); + let room_id = request.extensions.get::() + .expect("RoomIdParam should ensure a RoomId").clone(); + let params = request.extensions.get::().expect("Params object is missing").clone(); + let tag = match params.find("tag") { + Some(tag) => Ok(String::from(tag)), + None => { + Err(ApiError::missing_param("tag")) + } + }?; + + let mut content = String::new(); + if let Err(_) = request.body.read_to_string(&mut content) { + Err(ApiError::not_found(None))?; + } + + let connection = DB::from_request(request)?; + + RoomTag::upsert(&connection, user_id, room_id, tag, content)?; + + Ok(Response::with(Status::Ok)) + } +} + +/// The `/user/:user_id/rooms/:room_id/tags/:tag` endpoint. +pub struct DeleteTag; + +impl DeleteTag { + /// Create a `GetTags` with all necessary middleware. + pub fn chain() -> Chain { + let mut chain = Chain::new(DeleteTag); + + chain.link_before(JsonRequest); + chain.link_before(UserIdParam); + chain.link_before(RoomIdParam); + chain.link_before(AccessTokenAuth); + + chain + } +} + +impl Handler for DeleteTag { + fn handle(&self, request: &mut Request) -> IronResult { + let user_id = request.extensions.get::() + .expect("UserIdParam should ensure a UserId").clone(); + let room_id = request.extensions.get::() + .expect("RoomIdParam should ensure a RoomId").clone(); + let params = request.extensions.get::().expect("Params object is missing").clone(); + let tag = match params.find("tag") { + Some(tag) => Ok(String::from(tag)), + None => { + Err(ApiError::missing_param("tag")) + } + }?; + + let connection = DB::from_request(request)?; + + RoomTag::delete(&connection, user_id, room_id, tag)?; + + Ok(Response::with(Status::Ok)) + } +} + + +#[cfg(test)] +mod tests { + use test::Test; + use iron::status::Status; + + #[test] + fn get_tag() { + let test = Test::new(); + let access_token = test.create_access_token(); // @carl:ruma.test + + let room_id = test.create_public_room(&access_token); + + let put_tag_path = format!( + "/_matrix/client/r0/user/@carl:ruma.test/rooms/{}/tags/work?access_token={}", + room_id, + access_token + ); + + let response = test.put(&put_tag_path, r"{}"); + assert_eq!(response.status, Status::Ok); + + let get_tag_path = format!( + "/_matrix/client/r0/user/@carl:ruma.test/rooms/{}/tags?access_token={}", + room_id, + access_token + ); + + let response = test.get(&get_tag_path); + assert_eq!(response.status, Status::Ok); + let chunk = response.json().find("tags").unwrap(); + assert!(chunk.is_array()); + let chunk = chunk.as_array().unwrap(); + assert_eq!(chunk.len(), 1); + } +} diff --git a/src/main.rs b/src/main.rs index 5e090a86..118f02ea 100644 --- a/src/main.rs +++ b/src/main.rs @@ -57,6 +57,7 @@ pub mod schema; pub mod server; pub mod swagger; pub mod room_membership; +pub mod tags; #[cfg(test)] pub mod test; pub mod user; diff --git a/src/schema.rs b/src/schema.rs index 85c64c28..32579eb1 100644 --- a/src/schema.rs +++ b/src/schema.rs @@ -88,3 +88,13 @@ table! { content -> Text, } } + +table! { + room_tags { + id -> BigSerial, + user_id -> Text, + room_id -> Text, + tag -> Text, + content -> Text, + } +} diff --git a/src/server.rs b/src/server.rs index bc8b59da..b53f7464 100644 --- a/src/server.rs +++ b/src/server.rs @@ -14,7 +14,9 @@ use api::r0::{ CreateRoom, DeactivateAccount, DeleteRoomAlias, + DeleteTag, GetRoomAlias, + GetTags, JoinRoom, Login, Logout, @@ -22,6 +24,7 @@ use api::r0::{ PutAccountData, PutRoomAccountData, PutRoomAlias, + PutTag, Register, SendMessageEvent, StateMessageEvent, @@ -82,6 +85,9 @@ impl<'a> Server<'a> { ); r0_router.post("/rooms/:room_id/join", JoinRoom::chain()); r0_router.get("/rooms/:room_id/members", Members::chain()); + r0_router.get("/user/:user_id/rooms/:room_id/tags", GetTags::chain()); + r0_router.put("/user/:user_id/rooms/:room_id/tags/:tag", PutTag::chain()); + r0_router.delete("/user/:user_id/rooms/:room_id/tags/:tag", DeleteTag::chain()); let mut r0 = Chain::new(r0_router); diff --git a/src/tags.rs b/src/tags.rs new file mode 100644 index 00000000..38bab0c7 --- /dev/null +++ b/src/tags.rs @@ -0,0 +1,63 @@ + +use diesel::{ + Connection, + ExpressionMethods, + ExecuteDsl, + LoadDsl, + FilterDsl, + SelectDsl, + insert, +}; +use diesel::pg::PgConnection; +use ruma_identifiers::{RoomId, UserId}; + +use error::ApiError; +use schema::room_tags; + +/// A Matrix room membership. +#[derive(Debug, Clone, Serialize, Queryable)] +#[changeset_for(room_tags)] +pub struct RoomTag { + /// The user's ID. + pub user_id: UserId, + /// The room's ID. + pub room_id: RoomId, + /// Tag + pub tag: String, + /// Json content + pub content: String, +} + + +impl RoomTag { + + /// Return `RoomTag` for given `UserId` and `RoomId`. + pub fn find( + connection: &PgConnection, + user_id: UserId, + room_id: RoomId) + -> Result, ApiError> { + + Ok(vec![]) + } + + pub fn upsert( + connection: &PgConnection, + user_id: UserId, + room_id: RoomId, + tag: String, + content: String) + -> Result<(), ApiError> { + + Ok(()) + } + + pub fn delete( + connection: &PgConnection, + user_id: UserId, + room_id: RoomId, + tag: String) + -> Result<(), ApiError> { + Ok(()) + } +}