From b1b437ce1f83ebfb8808964846c964cac5e907c2 Mon Sep 17 00:00:00 2001 From: bryan newbold Date: Mon, 20 Feb 2023 02:02:59 -0800 Subject: app_bsky: expand post (and post view) types --- adenosine-pds/src/db_bsky.rs | 184 +++++++++++++++++++---------------- adenosine-pds/src/web.rs | 6 +- adenosine-pds/templates/account.html | 2 +- adenosine-pds/templates/macro.html | 10 +- adenosine-pds/templates/thread.html | 6 +- adenosine/src/app_bsky/mod.rs | 140 +++++++++++++++++++++----- 6 files changed, 226 insertions(+), 122 deletions(-) diff --git a/adenosine-pds/src/db_bsky.rs b/adenosine-pds/src/db_bsky.rs index 834053e..5e968af 100644 --- a/adenosine-pds/src/db_bsky.rs +++ b/adenosine-pds/src/db_bsky.rs @@ -59,7 +59,7 @@ pub fn bsky_mutate_db(db: &mut AtpDatabase, did: &Did, mutations: Vec) } // TODO: should probably return Result>? -pub fn bsky_get_profile(srv: &mut AtpService, did: &Did) -> Result { +pub fn bsky_get_profile(srv: &mut AtpService, did: &Did) -> Result { // first get the profile record let mut profile_cid: Option = None; let commit_cid = match srv.repo.lookup_commit(did)? { @@ -106,7 +106,7 @@ pub fn bsky_get_profile(srv: &mut AtpService, did: &Did) -> Result Result Result { }) } -fn feed_row_to_item(srv: &mut AtpService, row: FeedRow) -> Result { +fn feed_row_to_item(srv: &mut AtpService, row: FeedRow) -> Result { let record_ipld = srv.repo.get_ipld(&row.item_post_cid)?; + let post_record: app_bsky::Post = serde_json::from_value(ipld_into_json_value(record_ipld))?; let uri = format!( "at://{}/{}/{}", row.item_did, "app.bsky.feed.post", row.item_post_tid @@ -200,29 +201,42 @@ fn feed_row_to_item(srv: &mut AtpService, row: FeedRow) -> Result Result { - let mut feed: Vec = vec![]; + let mut feed: Vec = vec![]; // TODO: also handle reposts let rows = { let mut stmt = srv.atp_db @@ -243,7 +257,7 @@ pub fn bsky_get_timeline(srv: &mut AtpService, did: &Did) -> Result Result { - let mut feed: Vec = vec![]; + let mut feed: Vec = vec![]; // TODO: also handle reposts let rows = { let mut stmt = srv.atp_db @@ -285,7 +299,7 @@ pub fn bsky_get_thread( _ => Err(anyhow!("expected a record in uri: {}", uri))?, }; - // post itself, as a app_bsky::FeedItem + // post itself, as a app_bsky::FeedPostView let post_items = { let mut stmt = srv.atp_db .conn @@ -321,40 +335,47 @@ pub fn bsky_get_thread( rows }; for row in rows { - let item = feed_row_to_item(srv, row)?; - children.push(app_bsky::ThreadItem { - uri: item.uri, - cid: item.cid, - author: item.author, - record: item.record, - embed: item.embed, + let item = feed_row_to_item(srv, row)?.post; + children.push(app_bsky::ThreadPostView { + post: app_bsky::PostView { + uri: item.uri, + cid: item.cid, + author: item.author, + record: item.record, + embed: item.embed, + repostedBy: None, + replyCount: item.replyCount, + upvoteCount: item.upvoteCount, + downvoteCount: 0, + repostCount: item.repostCount, + indexedAt: item.indexedAt, + viewer: None, + }, // don't want a loop here parent: None, - replyCount: item.replyCount, // only going to depth of one here replies: None, - upvoteCount: item.upvoteCount, - downvoteCount: 0, - repostCount: item.repostCount, - indexedAt: item.indexedAt, - myState: None, }); } - let post = app_bsky::ThreadItem { - uri: post_item.uri, - cid: post_item.cid, - author: post_item.author, - record: post_item.record, - embed: post_item.embed, + let pip = post_item.post; + let post = app_bsky::ThreadPostView { + post: app_bsky::PostView { + uri: pip.uri, + cid: pip.cid, + author: pip.author, + record: pip.record, + embed: pip.embed, + repostedBy: None, + replyCount: pip.replyCount, + upvoteCount: pip.upvoteCount, + downvoteCount: 0, + repostCount: pip.repostCount, + indexedAt: pip.indexedAt, + viewer: None, + }, parent, - replyCount: post_item.replyCount, replies: Some(children), - upvoteCount: post_item.upvoteCount, - downvoteCount: 0, - repostCount: post_item.repostCount, - indexedAt: post_item.indexedAt, - myState: None, }; Ok(app_bsky::PostThread { thread: post }) } @@ -587,32 +608,29 @@ fn test_bsky_feeds() { assert_eq!(alice_feed.feed.len(), 3); assert_eq!( - alice_feed.feed[2].uri, + alice_feed.feed[2].post.uri, format!("at://{}/{}/{}", alice_did, post_nsid, alice_post1_tid) ); // TODO: CID - assert_eq!(alice_feed.feed[2].author.did, alice_did.to_string()); - assert_eq!(alice_feed.feed[2].author.handle, "alice.test"); - assert_eq!(alice_feed.feed[2].repostedBy, None); - assert_eq!( - alice_feed.feed[2].record["text"].as_str().unwrap(), - "alice first post" - ); - assert_eq!(alice_feed.feed[2].embed, None); - assert_eq!(alice_feed.feed[2].replyCount, 0); - assert_eq!(alice_feed.feed[2].repostCount, 0); - assert_eq!(alice_feed.feed[2].upvoteCount, 1); - assert_eq!(alice_feed.feed[2].downvoteCount, 0); - - assert_eq!(alice_feed.feed[1].author.did, alice_did.to_string()); - assert_eq!(alice_feed.feed[1].replyCount, 0); - assert_eq!(alice_feed.feed[1].repostCount, 1); - assert_eq!(alice_feed.feed[1].upvoteCount, 0); - - assert_eq!(alice_feed.feed[0].author.did, alice_did.to_string()); - assert_eq!(alice_feed.feed[0].replyCount, 1); - assert_eq!(alice_feed.feed[0].repostCount, 0); - assert_eq!(alice_feed.feed[0].upvoteCount, 0); + assert_eq!(alice_feed.feed[2].post.author.did, alice_did.to_string()); + assert_eq!(alice_feed.feed[2].post.author.handle, "alice.test"); + assert_eq!(alice_feed.feed[2].post.repostedBy, None); + assert_eq!(alice_feed.feed[2].post.record.text, "alice first post"); + assert_eq!(alice_feed.feed[2].post.embed, None); + assert_eq!(alice_feed.feed[2].post.replyCount, 0); + assert_eq!(alice_feed.feed[2].post.repostCount, 0); + assert_eq!(alice_feed.feed[2].post.upvoteCount, 1); + assert_eq!(alice_feed.feed[2].post.downvoteCount, 0); + + assert_eq!(alice_feed.feed[1].post.author.did, alice_did.to_string()); + assert_eq!(alice_feed.feed[1].post.replyCount, 0); + assert_eq!(alice_feed.feed[1].post.repostCount, 1); + assert_eq!(alice_feed.feed[1].post.upvoteCount, 0); + + assert_eq!(alice_feed.feed[0].post.author.did, alice_did.to_string()); + assert_eq!(alice_feed.feed[0].post.replyCount, 1); + assert_eq!(alice_feed.feed[0].post.repostCount, 0); + assert_eq!(alice_feed.feed[0].post.upvoteCount, 0); // test bob timeline: should include alice posts let bob_timeline = bsky_get_timeline(&mut srv, &bob_did).unwrap(); @@ -622,12 +640,12 @@ fn test_bsky_feeds() { } assert_eq!(bob_timeline.feed.len(), 3); assert_eq!( - bob_timeline.feed[2].uri, + bob_timeline.feed[2].post.uri, format!("at://{}/{}/{}", alice_did, post_nsid, alice_post1_tid) ); // TODO: CID - assert_eq!(bob_timeline.feed[2].author.did, alice_did.to_string()); - assert_eq!(bob_timeline.feed[2].author.handle, "alice.test"); + assert_eq!(bob_timeline.feed[2].post.author.did, alice_did.to_string()); + assert_eq!(bob_timeline.feed[2].post.author.handle, "alice.test"); // test bob feed: should include repost and reply let bob_feed = bsky_get_author_feed(&mut srv, &bob_did).unwrap(); @@ -644,8 +662,8 @@ fn test_bsky_feeds() { // TODO: "is a repost" (check record?) */ - assert_eq!(bob_feed.feed[0].author.did, bob_did.to_string()); - assert_eq!(bob_feed.feed[0].author.handle, "bob.test"); + assert_eq!(bob_feed.feed[0].post.author.did, bob_did.to_string()); + assert_eq!(bob_feed.feed[0].post.author.handle, "bob.test"); // test carol timeline: should include bob's repost and reply let carol_timeline = bsky_get_timeline(&mut srv, &carol_did).unwrap(); @@ -734,15 +752,15 @@ fn test_bsky_thread() { let post = bsky_get_thread(&mut srv, &AtUri::from_str(&bob_post1_uri).unwrap(), None) .unwrap() .thread; - assert_eq!(post.author.did, bob_did.to_string()); - assert_eq!(post.author.handle, "bob.test".to_string()); - assert_eq!(post.embed, None); - assert_eq!(post.replyCount, 1); - assert_eq!(post.repostCount, 0); - assert_eq!(post.upvoteCount, 0); + assert_eq!(post.post.author.did, bob_did.to_string()); + assert_eq!(post.post.author.handle, "bob.test".to_string()); + assert_eq!(post.post.embed, None); + assert_eq!(post.post.replyCount, 1); + assert_eq!(post.post.repostCount, 0); + assert_eq!(post.post.upvoteCount, 0); assert_eq!(post.replies.as_ref().unwrap().len(), 1); let post_replies = post.replies.unwrap(); - assert_eq!(post_replies[0].author.did, alice_did.to_string()); + assert_eq!(post_replies[0].post.author.did, alice_did.to_string()); // TODO: root URI, etc } diff --git a/adenosine-pds/src/web.rs b/adenosine-pds/src/web.rs index 8f15a49..fc75a93 100644 --- a/adenosine-pds/src/web.rs +++ b/adenosine-pds/src/web.rs @@ -29,8 +29,8 @@ pub struct AboutView { pub struct AccountView { pub domain: String, pub did: Did, - pub profile: app_bsky::Profile, - pub feed: Vec, + pub profile: app_bsky::ProfileView, + pub feed: Vec, } #[derive(Template)] @@ -40,7 +40,7 @@ pub struct ThreadView { pub did: Did, pub collection: Nsid, pub tid: Tid, - pub post: app_bsky::ThreadItem, + pub post: app_bsky::ThreadPostView, } #[derive(Template)] diff --git a/adenosine-pds/templates/account.html b/adenosine-pds/templates/account.html index b19621d..f45b9ef 100644 --- a/adenosine-pds/templates/account.html +++ b/adenosine-pds/templates/account.html @@ -32,7 +32,7 @@ {% endif %} {% for item in feed %} - {% call macro::feed_item(item) %} + {% call macro::feed_item(item.post) %} {% endfor %} diff --git a/adenosine-pds/templates/macro.html b/adenosine-pds/templates/macro.html index 7c61230..795400b 100644 --- a/adenosine-pds/templates/macro.html +++ b/adenosine-pds/templates/macro.html @@ -11,8 +11,8 @@
- {% if item.record["createdAt"].as_str().is_some() %} - {{ item.record["createdAt"].as_str().unwrap() }} + {% if item.record.createdAt.is_some() %} + {{ item.record.createdAt.as_ref().unwrap() }} {% else %} {{ item.indexedAt }} {% endif %} @@ -25,16 +25,16 @@ {% endif %} @{{ item.author.handle }}
-{{ item.record["text"].as_str().unwrap() }} +{{ item.record.text }}
[{{ item.upvoteCount }} upvote / {{ item.repostCount }} repost / {{ item.replyCount }} reply] [inspect] -{% if item.record.get("reply").is_some() %} +{% if item.record.reply.is_some() %}
-reply to: {{ item.record["reply"]["uri"] }} +reply to: {{ item.record.reply.as_ref().unwrap().parent.uri }} {% endif %} diff --git a/adenosine-pds/templates/thread.html b/adenosine-pds/templates/thread.html index 608c971..c0a8684 100644 --- a/adenosine-pds/templates/thread.html +++ b/adenosine-pds/templates/thread.html @@ -4,16 +4,16 @@ {% block main %} {% if post.parent.is_some() %} - {% call macro::feed_item(post.parent.as_ref().unwrap()) %} + {% call macro::feed_item(post.parent.as_ref().unwrap().post) %}
---
{% endif %} -{% call macro::feed_item(post) %} +{% call macro::feed_item(post.post) %} {% if post.replies.is_some() && post.replies.as_ref().unwrap().len() > 0 %}
--- replies ---
{% for item in post.replies.as_ref().unwrap() %} - {% call macro::feed_item(item) %} + {% call macro::feed_item(item.post) %} {% endfor %} {% endif %} diff --git a/adenosine/src/app_bsky/mod.rs b/adenosine/src/app_bsky/mod.rs index 18a0449..d828bcb 100644 --- a/adenosine/src/app_bsky/mod.rs +++ b/adenosine/src/app_bsky/mod.rs @@ -8,6 +8,12 @@ pub struct Subject { pub cid: Option, } +#[derive(Debug, serde::Serialize, serde::Deserialize, Clone, PartialEq, Eq)] +pub struct StrongRef { + pub uri: String, + pub cid: String, +} + /// Generic over Re-post and Like #[allow(non_snake_case)] #[derive(Debug, serde::Serialize, serde::Deserialize, Clone, PartialEq, Eq)] @@ -55,7 +61,7 @@ pub struct DeclRef { #[allow(non_snake_case)] #[derive(Debug, serde::Serialize, serde::Deserialize, Clone, PartialEq, Eq)] -pub struct Profile { +pub struct ProfileView { pub did: String, pub declaration: DeclRef, pub handle: String, @@ -67,13 +73,14 @@ pub struct Profile { pub followsCount: u64, pub membersCount: u64, pub postsCount: u64, - pub myState: serde_json::Value, + pub viewer: serde_json::Value, } +/// for Timeline or AuthorFeed #[allow(non_snake_case)] #[derive(Debug, serde::Serialize, serde::Deserialize, Clone, PartialEq, Eq)] pub struct GenericFeed { - pub feed: Vec, + pub feed: Vec, } #[allow(non_snake_case)] @@ -86,20 +93,48 @@ pub struct User { #[allow(non_snake_case)] #[derive(Debug, serde::Serialize, serde::Deserialize, Clone, PartialEq, Eq)] -pub struct FeedItem { +pub struct UserView { + pub did: String, + pub handle: String, + pub declaration: DeclRef, + pub displayName: Option, + pub avatar: Option, + pub viewer: Option, +} + +#[allow(non_snake_case)] +#[derive(Debug, serde::Serialize, serde::Deserialize, Clone, PartialEq, Eq)] +pub struct PostView { pub uri: String, pub cid: String, - pub author: User, - pub repostedBy: Option, - pub record: Value, - //pub embed?: RecordEmbed | ExternalEmbed | UnknownEmbed, - pub embed: Option, + pub author: UserView, + pub repostedBy: Option, + pub record: Post, + pub embed: Option, pub replyCount: u64, pub repostCount: u64, pub upvoteCount: u64, pub downvoteCount: u64, pub indexedAt: String, - pub myState: Option, + pub viewer: Option, +} + +#[allow(non_snake_case)] +#[derive(Debug, serde::Serialize, serde::Deserialize, Clone, PartialEq, Eq)] +pub struct ThreadPostView { + pub post: PostView, + // TODO: 'parent' and 'replies' should allow "NotFoundPost" for references that point to an + // unknown URI + pub parent: Option>, + pub replies: Option>, +} + +#[allow(non_snake_case)] +#[derive(Debug, serde::Serialize, serde::Deserialize, Clone, PartialEq, Eq)] +pub struct FeedPostView { + pub post: PostView, + pub reply: Option, + pub reason: Option, } #[allow(non_snake_case)] @@ -107,41 +142,92 @@ pub struct FeedItem { pub struct Post { pub text: String, pub reply: Option, + pub entities: Option>, + pub embed: Option, pub createdAt: Option, } #[allow(non_snake_case)] #[derive(Debug, serde::Serialize, serde::Deserialize, Clone, PartialEq, Eq)] pub struct PostReply { + // TODO: these should be StrongRef pub parent: Subject, pub root: Subject, } #[allow(non_snake_case)] #[derive(Debug, serde::Serialize, serde::Deserialize, Clone, PartialEq, Eq)] -pub struct PostThread { - pub thread: ThreadItem, +pub struct PostEntity { + pub index: TextSlice, + pub r#type: String, + pub value: String, +} + +#[allow(non_snake_case)] +#[derive(Debug, serde::Serialize, serde::Deserialize, Clone, PartialEq, Eq)] +pub struct TextSlice { + pub start: u64, + pub end: u64, +} + +#[allow(non_snake_case)] +#[derive(Debug, serde::Serialize, serde::Deserialize, Clone, PartialEq, Eq)] +pub struct PostEmbed { + pub external: Option, + pub images: Option>, } -// TODO: 'parent' and 'replies' should allow "NotFoundPost" for references that point to an unknown -// URI #[allow(non_snake_case)] #[derive(Debug, serde::Serialize, serde::Deserialize, Clone, PartialEq, Eq)] -pub struct ThreadItem { +pub struct PostEmbedView { + pub external: Option, + pub images: Option>, +} + +#[allow(non_snake_case)] +#[derive(Debug, serde::Serialize, serde::Deserialize, Clone, PartialEq, Eq)] +pub struct EmbedExternal { + pub uri: String, + pub title: String, + pub description: String, + pub thumb: Option, +} + +#[allow(non_snake_case)] +#[derive(Debug, serde::Serialize, serde::Deserialize, Clone, PartialEq, Eq)] +pub struct EmbedExternalView { pub uri: String, + pub title: String, + pub description: String, + pub thumb: Option, +} + +#[allow(non_snake_case)] +#[derive(Debug, serde::Serialize, serde::Deserialize, Clone, PartialEq, Eq)] +pub struct EmbedImage { + pub image: Blob, + pub alt: String, +} + +#[allow(non_snake_case)] +#[derive(Debug, serde::Serialize, serde::Deserialize, Clone, PartialEq, Eq)] +pub struct Blob { pub cid: String, - pub author: User, - pub record: Value, - //pub embed?: RecordEmbed | ExternalEmbed | UnknownEmbed, - pub embed: Option, - pub parent: Option>, - pub replyCount: u64, - pub replies: Option>, - pub repostCount: u64, - pub upvoteCount: u64, - pub downvoteCount: u64, - pub indexedAt: String, - pub myState: Option, + pub mimeType: String, +} + +#[allow(non_snake_case)] +#[derive(Debug, serde::Serialize, serde::Deserialize, Clone, PartialEq, Eq)] +pub struct EmbedImageView { + pub thumb: String, + pub fullsize: String, + pub alt: String, +} + +#[allow(non_snake_case)] +#[derive(Debug, serde::Serialize, serde::Deserialize, Clone, PartialEq, Eq)] +pub struct PostThread { + pub thread: ThreadPostView, } #[allow(non_snake_case)] -- cgit v1.2.3