Store mentions in database
This commit is contained in:
parent
06010e4403
commit
4da44159ed
9 changed files with 103 additions and 11 deletions
5
migrations/V0011__mention.sql
Normal file
5
migrations/V0011__mention.sql
Normal file
|
@ -0,0 +1,5 @@
|
|||
CREATE TABLE mention (
|
||||
post_id UUID NOT NULL REFERENCES post (id) ON DELETE CASCADE,
|
||||
profile_id UUID NOT NULL REFERENCES actor_profile (id) ON DELETE CASCADE,
|
||||
PRIMARY KEY (post_id, profile_id)
|
||||
);
|
|
@ -75,6 +75,12 @@ CREATE TABLE media_attachment (
|
|||
created_at TIMESTAMP WITH TIME ZONE NOT NULL DEFAULT now()
|
||||
);
|
||||
|
||||
CREATE TABLE mention (
|
||||
post_id UUID NOT NULL REFERENCES post (id) ON DELETE CASCADE,
|
||||
profile_id UUID NOT NULL REFERENCES actor_profile (id) ON DELETE CASCADE,
|
||||
PRIMARY KEY (post_id, profile_id)
|
||||
);
|
||||
|
||||
CREATE TABLE oauth_token (
|
||||
id SERIAL PRIMARY KEY,
|
||||
owner_id UUID NOT NULL REFERENCES user_account (id) ON DELETE CASCADE,
|
||||
|
|
|
@ -196,6 +196,7 @@ pub async fn process_note(
|
|||
content,
|
||||
in_reply_to_id,
|
||||
attachments: attachments,
|
||||
mentions: vec![],
|
||||
object_id: Some(object.id),
|
||||
created_at: object.published,
|
||||
};
|
||||
|
|
|
@ -5,6 +5,27 @@ use uuid::Uuid;
|
|||
use crate::mastodon_api::accounts::types::Account;
|
||||
use crate::mastodon_api::media::types::Attachment;
|
||||
use crate::models::posts::types::{Post, PostCreateData};
|
||||
use crate::models::profiles::types::DbActorProfile;
|
||||
|
||||
/// https://docs.joinmastodon.org/entities/mention/
|
||||
#[derive(Serialize)]
|
||||
pub struct Mention {
|
||||
id: String,
|
||||
username: String,
|
||||
acct: String,
|
||||
url: String,
|
||||
}
|
||||
|
||||
impl Mention {
|
||||
fn from_profile(profile: DbActorProfile, instance_url: &str) -> Self {
|
||||
Mention {
|
||||
id: profile.id.to_string(),
|
||||
username: profile.username.clone(),
|
||||
acct: profile.acct.clone(),
|
||||
url: profile.actor_id(instance_url).unwrap(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// https://docs.joinmastodon.org/entities/status/
|
||||
#[derive(Serialize)]
|
||||
|
@ -17,6 +38,7 @@ pub struct Status {
|
|||
pub replies_count: i32,
|
||||
pub favourites_count: i32,
|
||||
pub media_attachments: Vec<Attachment>,
|
||||
mentions: Vec<Mention>,
|
||||
|
||||
// Authorized user attributes
|
||||
pub favourited: bool,
|
||||
|
@ -32,6 +54,9 @@ impl Status {
|
|||
let attachments: Vec<Attachment> = post.attachments.into_iter()
|
||||
.map(|item| Attachment::from_db(item, instance_url))
|
||||
.collect();
|
||||
let mentions: Vec<Mention> = post.mentions.into_iter()
|
||||
.map(|item| Mention::from_profile(item, instance_url))
|
||||
.collect();
|
||||
let account = Account::from_profile(post.author, instance_url);
|
||||
Self {
|
||||
id: post.id,
|
||||
|
@ -42,6 +67,7 @@ impl Status {
|
|||
replies_count: post.reply_count,
|
||||
favourites_count: post.reaction_count,
|
||||
media_attachments: attachments,
|
||||
mentions: mentions,
|
||||
favourited: post.actions.map_or(false, |actions| actions.favourited),
|
||||
ipfs_cid: post.ipfs_cid,
|
||||
token_id: post.token_id,
|
||||
|
@ -68,6 +94,7 @@ impl From<StatusData> for PostCreateData {
|
|||
content: value.status,
|
||||
in_reply_to_id: value.in_reply_to_id,
|
||||
attachments: value.media_ids.unwrap_or(vec![]),
|
||||
mentions: vec![],
|
||||
object_id: None,
|
||||
created_at: None,
|
||||
}
|
||||
|
|
|
@ -62,6 +62,8 @@ async fn create_status(
|
|||
&instance.url(),
|
||||
&post_data.content,
|
||||
);
|
||||
post_data.mentions = mention_map.values()
|
||||
.map(|profile| profile.id).collect();
|
||||
let post = create_post(db_client, ¤t_user.id, post_data).await?;
|
||||
// Federate
|
||||
let maybe_in_reply_to = match post.in_reply_to_id {
|
||||
|
|
|
@ -5,7 +5,7 @@ use uuid::Uuid;
|
|||
|
||||
use crate::errors::DatabaseError;
|
||||
use crate::models::posts::helpers::get_actions_for_posts;
|
||||
use crate::models::posts::queries::RELATED_ATTACHMENTS;
|
||||
use crate::models::posts::queries::{RELATED_ATTACHMENTS, RELATED_MENTIONS};
|
||||
use super::types::{EventType, Notification};
|
||||
|
||||
async fn create_notification(
|
||||
|
@ -73,7 +73,8 @@ pub async fn get_notifications(
|
|||
"
|
||||
SELECT
|
||||
notification, sender, post, post_author,
|
||||
{related_attachments}
|
||||
{related_attachments},
|
||||
{related_mentions}
|
||||
FROM notification
|
||||
JOIN actor_profile AS sender
|
||||
ON notification.sender_id = sender.id
|
||||
|
@ -85,6 +86,7 @@ pub async fn get_notifications(
|
|||
ORDER BY notification.created_at DESC
|
||||
",
|
||||
related_attachments=RELATED_ATTACHMENTS,
|
||||
related_mentions=RELATED_MENTIONS,
|
||||
);
|
||||
let rows = db_client.query(
|
||||
statement.as_str(),
|
||||
|
|
|
@ -75,7 +75,8 @@ impl TryFrom<&Row> for Notification {
|
|||
Some(db_post) => {
|
||||
let db_post_author: DbActorProfile = row.try_get("post_author")?;
|
||||
let db_attachments: Vec<DbMediaAttachment> = row.try_get("attachments")?;
|
||||
Some(Post::new(db_post, db_post_author, db_attachments))
|
||||
let db_mentions: Vec<DbActorProfile> = row.try_get("mentions")?;
|
||||
Some(Post::new(db_post, db_post_author, db_attachments, db_mentions))
|
||||
},
|
||||
None => None,
|
||||
};
|
||||
|
|
|
@ -13,6 +13,7 @@ use crate::models::cleanup::{
|
|||
};
|
||||
use crate::models::notifications::queries::create_reply_notification;
|
||||
use crate::models::profiles::queries::update_post_count;
|
||||
use crate::models::profiles::types::DbActorProfile;
|
||||
use super::types::{DbPost, Post, PostCreateData};
|
||||
|
||||
pub const RELATED_ATTACHMENTS: &str =
|
||||
|
@ -21,6 +22,14 @@ pub const RELATED_ATTACHMENTS: &str =
|
|||
FROM media_attachment WHERE post_id = post.id
|
||||
) AS attachments";
|
||||
|
||||
pub const RELATED_MENTIONS: &str =
|
||||
"ARRAY(
|
||||
SELECT actor_profile
|
||||
FROM mention
|
||||
JOIN actor_profile ON mention.profile_id = actor_profile.id
|
||||
WHERE post_id = post.id
|
||||
) AS mentions";
|
||||
|
||||
pub async fn get_home_timeline(
|
||||
db_client: &impl GenericClient,
|
||||
current_user_id: &Uuid,
|
||||
|
@ -31,7 +40,8 @@ pub async fn get_home_timeline(
|
|||
"
|
||||
SELECT
|
||||
post, actor_profile,
|
||||
{related_attachments}
|
||||
{related_attachments},
|
||||
{related_mentions}
|
||||
FROM post
|
||||
JOIN actor_profile ON post.author_id = actor_profile.id
|
||||
WHERE
|
||||
|
@ -46,6 +56,7 @@ pub async fn get_home_timeline(
|
|||
ORDER BY post.created_at DESC
|
||||
",
|
||||
related_attachments=RELATED_ATTACHMENTS,
|
||||
related_mentions=RELATED_MENTIONS,
|
||||
);
|
||||
let rows = db_client.query(
|
||||
statement.as_str(),
|
||||
|
@ -71,13 +82,15 @@ pub async fn get_posts_by_author(
|
|||
"
|
||||
SELECT
|
||||
post, actor_profile,
|
||||
{related_attachments}
|
||||
{related_attachments},
|
||||
{related_mentions}
|
||||
FROM post
|
||||
JOIN actor_profile ON post.author_id = actor_profile.id
|
||||
WHERE {condition}
|
||||
ORDER BY post.created_at DESC
|
||||
",
|
||||
related_attachments=RELATED_ATTACHMENTS,
|
||||
related_mentions=RELATED_MENTIONS,
|
||||
condition=condition,
|
||||
);
|
||||
let rows = db_client.query(
|
||||
|
@ -136,6 +149,25 @@ pub async fn create_post(
|
|||
let db_attachments: Vec<DbMediaAttachment> = attachments_rows.iter()
|
||||
.map(|row| row.try_get("media_attachment"))
|
||||
.collect::<Result<_, _>>()?;
|
||||
// Create mentions
|
||||
let mentions_rows = transaction.query(
|
||||
"
|
||||
INSERT INTO mention (post_id, profile_id)
|
||||
SELECT $1, unnest($2::uuid[])
|
||||
RETURNING (
|
||||
SELECT actor_profile FROM actor_profile
|
||||
WHERE actor_profile.id = profile_id
|
||||
) AS actor_profile
|
||||
",
|
||||
&[&db_post.id, &data.mentions],
|
||||
).await?;
|
||||
if mentions_rows.len() != data.mentions.len() {
|
||||
// Some profiles were not found
|
||||
return Err(DatabaseError::NotFound("profile"));
|
||||
};
|
||||
let db_mentions: Vec<DbActorProfile> = mentions_rows.iter()
|
||||
.map(|row| row.try_get("actor_profile"))
|
||||
.collect::<Result<_, _>>()?;
|
||||
// Update counters
|
||||
let author = update_post_count(&transaction, &db_post.author_id, 1).await?;
|
||||
if let Some(in_reply_to_id) = &db_post.in_reply_to_id {
|
||||
|
@ -152,7 +184,7 @@ pub async fn create_post(
|
|||
}
|
||||
|
||||
transaction.commit().await?;
|
||||
let post = Post::new(db_post, author, db_attachments);
|
||||
let post = Post::new(db_post, author, db_attachments, db_mentions);
|
||||
Ok(post)
|
||||
}
|
||||
|
||||
|
@ -164,12 +196,14 @@ pub async fn get_post_by_id(
|
|||
"
|
||||
SELECT
|
||||
post, actor_profile,
|
||||
{related_attachments}
|
||||
{related_attachments},
|
||||
{related_mentions}
|
||||
FROM post
|
||||
JOIN actor_profile ON post.author_id = actor_profile.id
|
||||
WHERE post.id = $1
|
||||
",
|
||||
related_attachments=RELATED_ATTACHMENTS,
|
||||
related_mentions=RELATED_MENTIONS,
|
||||
);
|
||||
let maybe_row = db_client.query_opt(
|
||||
statement.as_str(),
|
||||
|
@ -208,13 +242,15 @@ pub async fn get_thread(
|
|||
)
|
||||
SELECT
|
||||
post, actor_profile,
|
||||
{related_attachments}
|
||||
{related_attachments},
|
||||
{related_mentions}
|
||||
FROM post
|
||||
JOIN context ON post.id = context.id
|
||||
JOIN actor_profile ON post.author_id = actor_profile.id
|
||||
ORDER BY context.path
|
||||
",
|
||||
related_attachments=RELATED_ATTACHMENTS,
|
||||
related_mentions=RELATED_MENTIONS,
|
||||
);
|
||||
let rows = db_client.query(
|
||||
statement.as_str(),
|
||||
|
@ -237,12 +273,14 @@ pub async fn get_post_by_object_id(
|
|||
"
|
||||
SELECT
|
||||
post, actor_profile,
|
||||
{related_attachments}
|
||||
{related_attachments},
|
||||
{related_mentions}
|
||||
FROM post
|
||||
JOIN actor_profile ON post.author_id = actor_profile.id
|
||||
WHERE post.object_id = $1
|
||||
",
|
||||
related_attachments=RELATED_ATTACHMENTS,
|
||||
related_mentions=RELATED_MENTIONS,
|
||||
);
|
||||
let maybe_row = db_client.query_opt(
|
||||
statement.as_str(),
|
||||
|
@ -261,12 +299,14 @@ pub async fn get_post_by_ipfs_cid(
|
|||
"
|
||||
SELECT
|
||||
post, actor_profile,
|
||||
{related_attachments}
|
||||
{related_attachments},
|
||||
{related_mentions}
|
||||
FROM post
|
||||
JOIN actor_profile ON post.author_id = actor_profile.id
|
||||
WHERE post.ipfs_cid = $1
|
||||
",
|
||||
related_attachments=RELATED_ATTACHMENTS,
|
||||
related_mentions=RELATED_MENTIONS,
|
||||
);
|
||||
let result = db_client.query_opt(
|
||||
statement.as_str(),
|
||||
|
|
|
@ -39,6 +39,7 @@ pub struct Post {
|
|||
pub reply_count: i32,
|
||||
pub reaction_count: i32,
|
||||
pub attachments: Vec<DbMediaAttachment>,
|
||||
pub mentions: Vec<DbActorProfile>,
|
||||
pub object_id: Option<String>,
|
||||
pub ipfs_cid: Option<String>,
|
||||
pub token_id: Option<i32>,
|
||||
|
@ -52,6 +53,7 @@ impl Post {
|
|||
db_post: DbPost,
|
||||
db_author: DbActorProfile,
|
||||
db_attachments: Vec<DbMediaAttachment>,
|
||||
db_mentions: Vec<DbActorProfile>,
|
||||
) -> Self {
|
||||
assert_eq!(db_post.author_id, db_author.id);
|
||||
Self {
|
||||
|
@ -62,6 +64,7 @@ impl Post {
|
|||
reply_count: db_post.reply_count,
|
||||
reaction_count: db_post.reaction_count,
|
||||
attachments: db_attachments,
|
||||
mentions: db_mentions,
|
||||
object_id: db_post.object_id,
|
||||
ipfs_cid: db_post.ipfs_cid,
|
||||
token_id: db_post.token_id,
|
||||
|
@ -83,6 +86,7 @@ impl Default for Post {
|
|||
reply_count: 0,
|
||||
reaction_count: 0,
|
||||
attachments: vec![],
|
||||
mentions: vec![],
|
||||
object_id: None,
|
||||
ipfs_cid: None,
|
||||
token_id: None,
|
||||
|
@ -101,7 +105,8 @@ impl TryFrom<&Row> for Post {
|
|||
let db_post: DbPost = row.try_get("post")?;
|
||||
let db_profile: DbActorProfile = row.try_get("actor_profile")?;
|
||||
let db_attachments: Vec<DbMediaAttachment> = row.try_get("attachments")?;
|
||||
let post = Self::new(db_post, db_profile, db_attachments);
|
||||
let db_mentions: Vec<DbActorProfile> = row.try_get("mentions")?;
|
||||
let post = Self::new(db_post, db_profile, db_attachments, db_mentions);
|
||||
Ok(post)
|
||||
}
|
||||
}
|
||||
|
@ -110,6 +115,7 @@ pub struct PostCreateData {
|
|||
pub content: String,
|
||||
pub in_reply_to_id: Option<Uuid>,
|
||||
pub attachments: Vec<Uuid>,
|
||||
pub mentions: Vec<Uuid>,
|
||||
pub object_id: Option<String>,
|
||||
pub created_at: Option<DateTime<Utc>>,
|
||||
}
|
||||
|
@ -137,6 +143,7 @@ mod tests {
|
|||
content: " ".to_string(),
|
||||
in_reply_to_id: None,
|
||||
attachments: vec![],
|
||||
mentions: vec![],
|
||||
object_id: None,
|
||||
created_at: None,
|
||||
};
|
||||
|
@ -149,6 +156,7 @@ mod tests {
|
|||
content: "test ".to_string(),
|
||||
in_reply_to_id: None,
|
||||
attachments: vec![],
|
||||
mentions: vec![],
|
||||
object_id: None,
|
||||
created_at: None,
|
||||
};
|
||||
|
|
Loading…
Reference in a new issue