From fd4a60bc05a68b38df2f318ab43c808b3adc2853 Mon Sep 17 00:00:00 2001 From: Mendy Berger <12537668+MendyBerger@users.noreply.github.com> Date: Wed, 22 May 2024 14:34:32 -0400 Subject: [PATCH] feat(be): Added recently like call --- backend/api/sqlx-data.json | 22 ++++++++++++++++++ backend/api/src/db/jig.rs | 26 +++++++++++++++++++++ backend/api/src/http/endpoints/jig.rs | 33 +++++++++++++++++++++++++-- shared/rust/src/api/endpoints/jig.rs | 13 ++++++++++- shared/rust/src/domain/jig.rs | 24 +++++++++++++++++++ 5 files changed, 115 insertions(+), 3 deletions(-) diff --git a/backend/api/sqlx-data.json b/backend/api/sqlx-data.json index 366bd99d2..65cde61e4 100644 --- a/backend/api/sqlx-data.json +++ b/backend/api/sqlx-data.json @@ -15409,6 +15409,28 @@ }, "query": "\n with cte as (\n select (array_agg(course.id))[1]\n from course_data \"cd\"\n inner join course on (draft_id = cd.id or (live_id = cd.id and cd.last_synced_at is not null and published_at is not null))\n left join course_admin_data \"admin\" on admin.course_id = course.id\n left join course_data_resource \"resource\" on cd.id = resource.course_data_id\n where (author_id = $1 or $1 is null)\n and (cd.draft_or_live = $2 or $2 is null)\n and (blocked = $5 or $5 is null)\n and (cd.privacy_level = any($3) or $3 = array[]::smallint[])\n and (resource.resource_type_id = any($4) or $4 = array[]::uuid[])\n group by coalesce(updated_at, created_at)\n )\n select count(*) as \"count!\" from unnest(array(select cte.array_agg from cte)) with ordinality t(id, ord)\n" }, + "e9c111c270b73f07f798be13ab0bd94518ee730997e60f7781cc81601aa5af72": { + "describe": { + "columns": [ + { + "name": "jig_id", + "ordinal": 0, + "type_info": "Uuid" + } + ], + "nullable": [ + false + ], + "parameters": { + "Left": [ + "Uuid", + "Int8", + "Int8" + ] + } + }, + "query": "\n select jig_id\n from jig_like\n where user_id = $1\n order by created_at desc\n offset $2\n limit $3\n \n " + }, "e9f633a72fa4fad7e52f4dd94aa9c6d1c2fd6b3f256fd21248297f254a5fbb79": { "describe": { "columns": [], diff --git a/backend/api/src/db/jig.rs b/backend/api/src/db/jig.rs index f2366439c..3da55d87a 100644 --- a/backend/api/src/db/jig.rs +++ b/backend/api/src/db/jig.rs @@ -1681,6 +1681,32 @@ select exists ( Ok(exists) } +pub async fn list_liked( + db: &PgPool, + user_id: UserId, + page: u32, + page_limit: u32, +) -> sqlx::Result> { + let rows = sqlx::query!( + r#" + select jig_id + from jig_like + where user_id = $1 + order by created_at desc + offset $2 + limit $3 + + "#, + user_id.0, + (page * page_limit) as i32, + page_limit as i32, + ) + .fetch_all(db) + .await?; + + Ok(rows.into_iter().map(|row| JigId(row.jig_id)).collect()) +} + #[instrument(skip(db))] pub async fn get_jig_playlists( db: &sqlx::Pool, diff --git a/backend/api/src/http/endpoints/jig.rs b/backend/api/src/http/endpoints/jig.rs index 1e250cd55..8ae8e1321 100644 --- a/backend/api/src/http/endpoints/jig.rs +++ b/backend/api/src/http/endpoints/jig.rs @@ -4,7 +4,10 @@ use actix_web::{ }; use futures::try_join; use ji_core::settings::RuntimeSettings; -use shared::domain::{jig::JigTrendingResponse, user::UserScope}; +use shared::domain::{ + jig::{JigTrendingResponse, ListLikedResponse}, + user::UserScope, +}; use shared::{ api::{endpoints::jig, ApiEndpoint, PathParts}, domain::{ @@ -431,6 +434,26 @@ async fn unlike( Ok(HttpResponse::NoContent().finish()) } +/// Get users liked jigs +async fn list_liked( + db: Data, + claims: TokenUser, + query: Option::Req>>, +) -> Result::Res>, error::Server> { + let user_id = claims.user_id(); + let query = query.map_or_else(Default::default, Query::into_inner); + + let page_limit = page_limit(query.page_limit).await?; + + let ids = db::jig::list_liked(&*db, user_id, query.page.unwrap_or(0), page_limit).await?; + + let jigs = db::jig::get_by_ids(&db, &ids, DraftOrLive::Live, Some(user_id)) + .await + .into_anyhow()?; + + Ok(Json(ListLikedResponse { jigs })) +} + /// Add a play to a jig async fn play(db: Data, path: web::Path) -> Result { db::jig::jig_play(&*db, path.into_inner()).await?; @@ -462,7 +485,9 @@ pub(crate) async fn page_limit(page_limit: Option) -> anyhow::Result { if let Some(limit) = page_limit { match limit > 0 && limit <= MAX_PAGE_LIMIT { true => Ok(limit), - false => Err(anyhow::anyhow!("Page limit should be within 1-100")), + false => Err(anyhow::anyhow!( + "Page limit should be within 1-{MAX_PAGE_LIMIT}" + )), } } else { Ok(DEFAULT_PAGE_LIMIT) @@ -571,6 +596,10 @@ pub fn configure(cfg: &mut ServiceConfig) { ::Path::PATH, jig::Trending::METHOD.route().to(trending), ) + .route( + ::Path::PATH, + jig::ListLiked::METHOD.route().to(list_liked), + ) .route( ::Path::PATH, jig::UpdateDraftData::METHOD.route().to(update_draft), diff --git a/shared/rust/src/api/endpoints/jig.rs b/shared/rust/src/api/endpoints/jig.rs index 7c3de3188..07d8689ad 100644 --- a/shared/rust/src/api/endpoints/jig.rs +++ b/shared/rust/src/api/endpoints/jig.rs @@ -9,7 +9,8 @@ use crate::{ JigId, JigLikePath, JigLikedPath, JigLikedResponse, JigPlayPath, JigPublishPath, JigResponse, JigSearchPath, JigSearchQuery, JigSearchResponse, JigTransferAdminPath, JigTrendingPath, JigTrendingResponse, JigUnlikePath, JigUpdateAdminDataRequest, - JigUpdateDraftDataPath, JigUpdateDraftDataRequest, + JigUpdateDraftDataPath, JigUpdateDraftDataRequest, ListLikedPath, ListLikedRequest, + ListLikedResponse, }, CreateResponse, }, @@ -153,6 +154,16 @@ impl ApiEndpoint for Trending { const METHOD: Method = Method::Get; } +/// List user's liked JIGs. +pub struct ListLiked; +impl ApiEndpoint for ListLiked { + type Req = ListLikedRequest; + type Res = ListLikedResponse; + type Path = ListLikedPath; + type Err = EmptyError; + const METHOD: Method = Method::Get; +} + /// Clone a JIG. This clones both the draft and live. /// /// # Authorization diff --git a/shared/rust/src/domain/jig.rs b/shared/rust/src/domain/jig.rs index 5d19ef94a..f35e3263d 100644 --- a/shared/rust/src/domain/jig.rs +++ b/shared/rust/src/domain/jig.rs @@ -761,6 +761,30 @@ pub struct JigTrendingResponse { pub jigs: Vec, } +make_path_parts!(ListLikedPath => "/v1/jig/likes"); + +/// Response for request for list of liked jigs. +#[derive(Serialize, Deserialize, Clone, Debug)] +#[serde(rename_all = "camelCase")] +pub struct ListLikedRequest { + /// The page number of the jigs to get. + #[serde(default)] + #[serde(skip_serializing_if = "Option::is_none")] + pub page: Option, + + /// The hits per page to be returned + #[serde(default)] + #[serde(skip_serializing_if = "Option::is_none")] + pub page_limit: Option, +} +/// Response for request for list of liked jigs. +#[derive(Serialize, Deserialize, Clone, Debug)] +#[serde(rename_all = "camelCase")] +pub struct ListLikedResponse { + /// the jigs returned. + pub jigs: Vec, +} + /// Response for successfully finding the draft of a jig. #[derive(Serialize, Deserialize, Clone, Debug)] #[serde(rename_all = "camelCase")]