diff --git a/server/src/apub/comment.rs b/server/src/apub/comment.rs index 17da45a6b..4c2faa21d 100644 --- a/server/src/apub/comment.rs +++ b/server/src/apub/comment.rs @@ -1,5 +1,24 @@ use super::*; +#[derive(Deserialize)] +pub struct CommentQuery { + comment_id: String, +} + +/// Return the post json over HTTP. +pub async fn get_apub_comment( + info: Path, + db: DbPoolParam, +) -> Result, Error> { + let id = info.comment_id.parse::()?; + let comment = Comment::read(&&db.get()?, id)?; + if !comment.deleted { + Ok(create_apub_response(&comment.to_apub(&db.get().unwrap())?)) + } else { + Ok(create_apub_tombstone_response(&comment.to_tombstone()?)) + } +} + impl ToApub for Comment { type Response = Note; diff --git a/server/src/apub/fetcher.rs b/server/src/apub/fetcher.rs index 115ef6ff9..994e75f2b 100644 --- a/server/src/apub/fetcher.rs +++ b/server/src/apub/fetcher.rs @@ -40,6 +40,7 @@ pub enum SearchAcceptedObjects { Person(Box), Group(Box), Page(Box), + Comment(Box), } /// Attempt to parse the query as URL, and fetch an ActivityPub object from it. @@ -47,7 +48,8 @@ pub enum SearchAcceptedObjects { /// Some working examples for use with the docker/federation/ setup: /// http://lemmy_alpha:8540/c/main, or !main@lemmy_alpha:8540 /// http://lemmy_alpha:8540/u/lemmy_alpha, or @lemmy_alpha@lemmy_alpha:8540 -/// http://lemmy_alpha:8540/p/3 +/// http://lemmy_alpha:8540/post/3 +/// http://lemmy_alpha:8540/comment/2 pub fn search_by_apub_id(query: &str, conn: &PgConnection) -> Result { // Parse the shorthand query url let query_url = if query.contains('@') { @@ -99,6 +101,20 @@ pub fn search_by_apub_id(query: &str, conn: &PgConnection) -> Result { + let post_url = c + .object_props + .get_many_in_reply_to_xsd_any_uris() + .unwrap() + .next() + .unwrap() + .to_string(); + // TODO: also fetch parent comments if any + let post = fetch_remote_object(&Url::parse(&post_url)?)?; + upsert_post(&PostForm::from_apub(&post, conn)?, conn)?; + let c = upsert_comment(&CommentForm::from_apub(&c, conn)?, conn)?; + response.comments = vec![CommentView::read(conn, c.id, None)?]; + } } Ok(response) } @@ -198,6 +214,15 @@ fn upsert_post(post_form: &PostForm, conn: &PgConnection) -> Result } } +fn upsert_comment(comment_form: &CommentForm, conn: &PgConnection) -> Result { + let existing = Comment::read_from_apub_id(conn, &comment_form.ap_id); + match existing { + Err(NotFound {}) => Ok(Comment::create(conn, &comment_form)?), + Ok(p) => Ok(Comment::update(conn, p.id, &comment_form)?), + Err(e) => Err(Error::from(e)), + } +} + // TODO It should not be fetching data from a community outbox. // All posts, comments, comment likes, etc should be posts to our community_inbox // The only data we should be periodically fetching (if it hasn't been fetched in the last day diff --git a/server/src/routes/federation.rs b/server/src/routes/federation.rs index c1cb7408a..ed5c25965 100644 --- a/server/src/routes/federation.rs +++ b/server/src/routes/federation.rs @@ -1,4 +1,5 @@ use super::*; +use crate::apub::comment::get_apub_comment; use crate::apub::community::*; use crate::apub::community_inbox::community_inbox; use crate::apub::post::get_apub_post; @@ -28,7 +29,8 @@ pub fn config(cfg: &mut web::ServiceConfig) { // web::get().to(get_apub_community_outbox), // ) .route("/u/{user_name}", web::get().to(get_apub_user_http)) - .route("/post/{post_id}", web::get().to(get_apub_post)), + .route("/post/{post_id}", web::get().to(get_apub_post)) + .route("/comment/{comment_id}", web::get().to(get_apub_comment)), ) // Inboxes dont work with the header guard for some reason. .route("/c/{community_name}/inbox", web::post().to(community_inbox)) diff --git a/ui/src/api_tests/api.spec.ts b/ui/src/api_tests/api.spec.ts index 8734169ac..dcfb5e628 100644 --- a/ui/src/api_tests/api.spec.ts +++ b/ui/src/api_tests/api.spec.ts @@ -68,7 +68,7 @@ describe('main', () => { lemmyBetaAuth = resB.jwt; }); - describe('beta_fetch', () => { + describe('post_search', () => { test('Create test post on alpha and fetch it on beta', async () => { let name = 'A jest test post'; let postForm: PostForm = { @@ -1107,6 +1107,36 @@ describe('main', () => { expect(getPrivateMessagesUnDeletedRes.messages[0].deleted).toBe(false); }); }); + + describe('comment_search', () => { + test('Create comment on alpha and search it', async () => { + let content = 'A jest test federated comment for search'; + let commentForm: CommentForm = { + content, + post_id: 1, + auth: lemmyAlphaAuth, + }; + + let createResponse: CommentResponse = await fetch( + `${lemmyAlphaApiUrl}/comment`, + { + method: 'POST', + headers: { + 'Content-Type': 'application/json', + }, + body: wrapper(commentForm), + } + ).then(d => d.json()); + + let searchUrl = `${lemmyBetaApiUrl}/search?q=${createResponse.comment.ap_id}&type_=All&sort=TopAll`; + let searchResponse: SearchResponse = await fetch(searchUrl, { + method: 'GET', + }).then(d => d.json()); + + // TODO: check more fields + expect(searchResponse.comments[0].content).toBe(content); + }); + }); }); function wrapper(form: any): string {