Add API method for retrieving tag timeline
This commit is contained in:
parent
a4dd06d6e9
commit
da918d2296
4 changed files with 130 additions and 4 deletions
|
@ -132,6 +132,7 @@ POST /api/v1/statuses/{status_id}/unfavourite
|
||||||
POST /api/v1/statuses/{status_id}/reblog
|
POST /api/v1/statuses/{status_id}/reblog
|
||||||
POST /api/v1/statuses/{status_id}/unreblog
|
POST /api/v1/statuses/{status_id}/unreblog
|
||||||
GET /api/v1/timelines/home
|
GET /api/v1/timelines/home
|
||||||
|
GET /api/v1/timelines/tag/{hashtag}
|
||||||
```
|
```
|
||||||
|
|
||||||
Additional methods:
|
Additional methods:
|
||||||
|
|
|
@ -18,7 +18,7 @@ paths:
|
||||||
description: Post does not belong to user
|
description: Post does not belong to user
|
||||||
404:
|
404:
|
||||||
description: Post not found
|
description: Post not found
|
||||||
/api/v1/{status_id}/make_permanent:
|
/api/v1/statuses/{status_id}/make_permanent:
|
||||||
post:
|
post:
|
||||||
summary: Save post to IPFS
|
summary: Save post to IPFS
|
||||||
parameters:
|
parameters:
|
||||||
|
@ -38,7 +38,7 @@ paths:
|
||||||
description: IPFS integration is not enabled
|
description: IPFS integration is not enabled
|
||||||
422:
|
422:
|
||||||
description: Post already saved to IPFS
|
description: Post already saved to IPFS
|
||||||
/api/v1/{status_id}/signature:
|
/api/v1/statuses/{status_id}/signature:
|
||||||
get:
|
get:
|
||||||
summary: Sign post data with instance key
|
summary: Sign post data with instance key
|
||||||
parameters:
|
parameters:
|
||||||
|
@ -69,7 +69,7 @@ paths:
|
||||||
description: Ethereum integration is not enabled
|
description: Ethereum integration is not enabled
|
||||||
422:
|
422:
|
||||||
description: Post is not saved to IPFS
|
description: Post is not saved to IPFS
|
||||||
/api/v1/{status_id}/token_minted:
|
/api/v1/statuses/{status_id}/token_minted:
|
||||||
post:
|
post:
|
||||||
summary: Register transaction that mints a token
|
summary: Register transaction that mints a token
|
||||||
parameters:
|
parameters:
|
||||||
|
@ -95,6 +95,40 @@ paths:
|
||||||
description: Post not found
|
description: Post not found
|
||||||
422:
|
422:
|
||||||
description: Transaction already registered
|
description: Transaction already registered
|
||||||
|
/api/v1/timelines/tag/{hashtag}:
|
||||||
|
get:
|
||||||
|
summary: View public posts containing the given hashtag
|
||||||
|
parameters:
|
||||||
|
- name: hashtag
|
||||||
|
in: path
|
||||||
|
description: Hashtag name
|
||||||
|
required: true
|
||||||
|
schema:
|
||||||
|
type: string
|
||||||
|
- name: max_id
|
||||||
|
in: query
|
||||||
|
description: Return results older than this ID.
|
||||||
|
required: false
|
||||||
|
schema:
|
||||||
|
type: string
|
||||||
|
format: uuid
|
||||||
|
- name: limit
|
||||||
|
in: query
|
||||||
|
description: Maximum number of results to return.
|
||||||
|
required: false
|
||||||
|
schema:
|
||||||
|
type: integer
|
||||||
|
default: 20
|
||||||
|
responses:
|
||||||
|
200:
|
||||||
|
description: Successful operation
|
||||||
|
content:
|
||||||
|
application/json:
|
||||||
|
schema:
|
||||||
|
description: Post list
|
||||||
|
type: array
|
||||||
|
items:
|
||||||
|
$ref: '#/components/schemas/Status'
|
||||||
|
|
||||||
components:
|
components:
|
||||||
parameters:
|
parameters:
|
||||||
|
@ -113,6 +147,14 @@ components:
|
||||||
id:
|
id:
|
||||||
type: string
|
type: string
|
||||||
format: uuid
|
format: uuid
|
||||||
|
content:
|
||||||
|
description: HTML-encoded status content.
|
||||||
|
type: string
|
||||||
|
tags:
|
||||||
|
description: Hashtags used within the status content.
|
||||||
|
type: array
|
||||||
|
items:
|
||||||
|
$ref: '#/components/schemas/Tag'
|
||||||
ipfs_cid:
|
ipfs_cid:
|
||||||
type: string
|
type: string
|
||||||
nullable: true
|
nullable: true
|
||||||
|
@ -121,3 +163,11 @@ components:
|
||||||
type: string
|
type: string
|
||||||
nullable: true
|
nullable: true
|
||||||
example: '0x5fe80cdea7f...'
|
example: '0x5fe80cdea7f...'
|
||||||
|
Tag:
|
||||||
|
type: object
|
||||||
|
properties:
|
||||||
|
name:
|
||||||
|
description: 'The value of the hashtag after the # sign.'
|
||||||
|
type: string
|
||||||
|
url:
|
||||||
|
description: A link to the hashtag on the instance.
|
||||||
|
|
|
@ -11,7 +11,7 @@ use crate::models::posts::helpers::{
|
||||||
get_actions_for_posts,
|
get_actions_for_posts,
|
||||||
get_reposted_posts,
|
get_reposted_posts,
|
||||||
};
|
};
|
||||||
use crate::models::posts::queries::get_home_timeline;
|
use crate::models::posts::queries::{get_home_timeline, get_posts_by_tag};
|
||||||
use super::types::TimelineQueryParams;
|
use super::types::TimelineQueryParams;
|
||||||
|
|
||||||
#[get("/home")]
|
#[get("/home")]
|
||||||
|
@ -42,7 +42,42 @@ async fn home_timeline(
|
||||||
Ok(HttpResponse::Ok().json(statuses))
|
Ok(HttpResponse::Ok().json(statuses))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[get("/tag/{hashtag}")]
|
||||||
|
async fn hashtag_timeline(
|
||||||
|
auth: Option<BearerAuth>,
|
||||||
|
config: web::Data<Config>,
|
||||||
|
db_pool: web::Data<Pool>,
|
||||||
|
web::Path(hashtag): web::Path<String>,
|
||||||
|
query_params: web::Query<TimelineQueryParams>,
|
||||||
|
) -> Result<HttpResponse, HttpError> {
|
||||||
|
let db_client = &**get_database_client(&db_pool).await?;
|
||||||
|
let maybe_current_user = match auth {
|
||||||
|
Some(auth) => Some(get_current_user(db_client, auth.token()).await?),
|
||||||
|
None => None,
|
||||||
|
};
|
||||||
|
let mut posts = get_posts_by_tag(
|
||||||
|
db_client,
|
||||||
|
&hashtag,
|
||||||
|
query_params.max_id,
|
||||||
|
query_params.limit,
|
||||||
|
).await?;
|
||||||
|
get_reposted_posts(db_client, posts.iter_mut().collect()).await?;
|
||||||
|
if let Some(user) = maybe_current_user {
|
||||||
|
get_actions_for_posts(
|
||||||
|
db_client,
|
||||||
|
&user.id,
|
||||||
|
posts.iter_mut().collect(),
|
||||||
|
).await?;
|
||||||
|
};
|
||||||
|
let statuses: Vec<Status> = posts
|
||||||
|
.into_iter()
|
||||||
|
.map(|post| Status::from_post(post, &config.instance_url()))
|
||||||
|
.collect();
|
||||||
|
Ok(HttpResponse::Ok().json(statuses))
|
||||||
|
}
|
||||||
|
|
||||||
pub fn timeline_api_scope() -> Scope {
|
pub fn timeline_api_scope() -> Scope {
|
||||||
web::scope("/api/v1/timelines")
|
web::scope("/api/v1/timelines")
|
||||||
.service(home_timeline)
|
.service(home_timeline)
|
||||||
|
.service(hashtag_timeline)
|
||||||
}
|
}
|
||||||
|
|
|
@ -329,6 +329,46 @@ pub async fn get_posts_by_author(
|
||||||
Ok(posts)
|
Ok(posts)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub async fn get_posts_by_tag(
|
||||||
|
db_client: &impl GenericClient,
|
||||||
|
tag_name: &str,
|
||||||
|
max_post_id: Option<Uuid>,
|
||||||
|
limit: i64,
|
||||||
|
) -> Result<Vec<Post>, DatabaseError> {
|
||||||
|
let statement = format!(
|
||||||
|
"
|
||||||
|
SELECT
|
||||||
|
post, actor_profile,
|
||||||
|
{related_attachments},
|
||||||
|
{related_mentions},
|
||||||
|
{related_tags}
|
||||||
|
FROM post
|
||||||
|
JOIN actor_profile ON post.author_id = actor_profile.id
|
||||||
|
WHERE
|
||||||
|
post.visibility = {visibility_public}
|
||||||
|
AND EXISTS (
|
||||||
|
SELECT 1 FROM post_tag JOIN tag ON post_tag.tag_id = tag.id
|
||||||
|
WHERE post_tag.post_id = post.id AND tag.tag_name = $1
|
||||||
|
)
|
||||||
|
AND ($2::uuid IS NULL OR post.id < $2)
|
||||||
|
ORDER BY post.id DESC
|
||||||
|
LIMIT $3
|
||||||
|
",
|
||||||
|
related_attachments=RELATED_ATTACHMENTS,
|
||||||
|
related_mentions=RELATED_MENTIONS,
|
||||||
|
related_tags=RELATED_TAGS,
|
||||||
|
visibility_public=i16::from(&Visibility::Public),
|
||||||
|
);
|
||||||
|
let rows = db_client.query(
|
||||||
|
statement.as_str(),
|
||||||
|
&[&tag_name.to_lowercase(), &max_post_id, &limit],
|
||||||
|
).await?;
|
||||||
|
let posts: Vec<Post> = rows.iter()
|
||||||
|
.map(Post::try_from)
|
||||||
|
.collect::<Result<_, _>>()?;
|
||||||
|
Ok(posts)
|
||||||
|
}
|
||||||
|
|
||||||
pub async fn get_post_by_id(
|
pub async fn get_post_by_id(
|
||||||
db_client: &impl GenericClient,
|
db_client: &impl GenericClient,
|
||||||
post_id: &Uuid,
|
post_id: &Uuid,
|
||||||
|
|
Loading…
Reference in a new issue