diff options
author | bryan newbold <bnewbold@robocracy.org> | 2023-08-20 19:56:22 -0700 |
---|---|---|
committer | bryan newbold <bnewbold@robocracy.org> | 2023-08-20 19:56:22 -0700 |
commit | 23416af782a179a658711e958e977e19ae97067b (patch) | |
tree | 09e605dbd105c0628f6614e421c0dd43bb8ce3dc | |
parent | cc1dea6ea34260201c1df09959c7c4566d16d918 (diff) | |
download | adenosine-23416af782a179a658711e958e977e19ae97067b.tar.gz adenosine-23416af782a179a658711e958e977e19ae97067b.zip |
many updates from spring 2023 lex refactor
-rw-r--r-- | adenosine-cli/src/bin/adenosine.rs | 105 | ||||
-rw-r--r-- | adenosine-cli/src/pretty.rs | 6 | ||||
-rw-r--r-- | adenosine-pds/src/bin/adenosine-pds.rs | 1 | ||||
-rw-r--r-- | adenosine-pds/src/db.rs | 3 | ||||
-rw-r--r-- | adenosine-pds/src/db_bsky.rs | 117 | ||||
-rw-r--r-- | adenosine-pds/src/lib.rs | 56 | ||||
-rw-r--r-- | adenosine-pds/src/web.rs | 6 | ||||
-rw-r--r-- | adenosine-pds/templates/macro.html | 2 | ||||
-rw-r--r-- | adenosine/src/app_bsky/mod.rs | 169 | ||||
-rw-r--r-- | adenosine/src/com_atproto/mod.rs | 6 | ||||
-rw-r--r-- | adenosine/src/com_atproto/repo/mod.rs | 20 | ||||
-rw-r--r-- | adenosine/src/xrpc.rs | 6 | ||||
-rw-r--r-- | extra/adenosine.1 | 11 | ||||
-rw-r--r-- | extra/adenosine.1.md | 8 | ||||
-rw-r--r-- | extra/adenosine.1.scdoc | 7 |
15 files changed, 319 insertions, 204 deletions
diff --git a/adenosine-cli/src/bin/adenosine.rs b/adenosine-cli/src/bin/adenosine.rs index 7f81191..6900b20 100644 --- a/adenosine-cli/src/bin/adenosine.rs +++ b/adenosine-cli/src/bin/adenosine.rs @@ -122,7 +122,6 @@ enum AccountCommand { /// Deletes the current login session Logout, /// Fetches account metadata for the current session - Info, // TODO: CreateRevocationKey or CreateDid CreateInvite { #[structopt(short = "-u", default_value = "1")] @@ -133,7 +132,7 @@ enum AccountCommand { #[derive(StructOpt)] enum RepoCommand { /// Get the current 'root' commit for a DID - Root { + Head { /// Repository DID, or uses the current session account did: Option<DidOrHost>, }, @@ -371,15 +370,21 @@ fn run(opt: Opt) -> Result<()> { .as_ref() .map(|v| v.to_string()) .unwrap_or(require_auth_did(&opt, &mut xrpc_client)?.to_string()); - params.insert("user".to_string(), name); - xrpc_client.get(&Nsid::from_str("com.atproto.repo.describe")?, Some(params))? + params.insert("repo".to_string(), name); + xrpc_client.get( + &Nsid::from_str("com.atproto.repo.describeRepo")?, + Some(params), + )? } Command::Resolve { name } => { - params.insert("name".to_string(), name.to_string()); - xrpc_client.get(&Nsid::from_str("com.atproto.handle.resolve")?, Some(params))? + params.insert("handle".to_string(), name.to_string()); + xrpc_client.get( + &Nsid::from_str("com.atproto.identity.resolveHandle")?, + Some(params), + )? } Command::Get { uri, cid } => { - params.insert("user".to_string(), uri.repository.to_string()); + params.insert("repo".to_string(), uri.repository.to_string()); params.insert( "collection".to_string(), uri.collection.ok_or(anyhow!("collection required"))?, @@ -395,12 +400,15 @@ fn run(opt: Opt) -> Result<()> { } Command::Ls { uri } => { // TODO: option to print fully-qualified path? - params.insert("user".to_string(), uri.repository.to_string()); + params.insert("repo".to_string(), uri.repository.to_string()); if uri.collection.is_none() { // if a repository, but no collection, list the collections let describe = xrpc_client - .get(&Nsid::from_str("com.atproto.repo.describe")?, Some(params))? - .ok_or(anyhow!("expected a repo.describe response"))?; + .get( + &Nsid::from_str("com.atproto.repo.describeRepo")?, + Some(params), + )? + .ok_or(anyhow!("expected a repo.describeRepo response"))?; for c in describe["collections"] .as_array() .ok_or(anyhow!("expected collection list"))? @@ -439,7 +447,7 @@ fn run(opt: Opt) -> Result<()> { &Nsid::from_str("com.atproto.repo.createRecord")?, None, Some(json!({ - "did": did, + "repo": did, "collection": collection, // TODO: "validate" (boolean) "record": val @@ -457,7 +465,7 @@ fn run(opt: Opt) -> Result<()> { .clone() .ok_or(anyhow!("collection required"))?; let rkey = uri.record.clone().ok_or(anyhow!("record key required"))?; - params.insert("did".to_string(), did.clone()); + params.insert("repo".to_string(), did.clone()); params.insert("collection".to_string(), collection.clone()); params.insert("rkey".to_string(), rkey.clone()); // fetch existing, extend map with fields, put the updated value @@ -469,7 +477,7 @@ fn run(opt: Opt) -> Result<()> { &Nsid::from_str("com.atproto.repo.putRecord")?, None, Some(json!({ - "did": did, + "repo": did, "collection": collection, "rkey": rkey, "record": record, @@ -488,7 +496,7 @@ fn run(opt: Opt) -> Result<()> { &Nsid::from_str("com.atproto.repo.deleteRecord")?, None, Some(json!({ - "did": did, + "repo": did, "collection": collection, "rkey": rkey, })), @@ -531,7 +539,7 @@ fn run(opt: Opt) -> Result<()> { body["inviteCode"] = json!(code); } xrpc_client.post( - &Nsid::from_str("com.atproto.account.create")?, + &Nsid::from_str("com.atproto.server.createAccount")?, None, Some(body), )? @@ -539,7 +547,7 @@ fn run(opt: Opt) -> Result<()> { Command::Account { cmd: AccountCommand::Login { handle, password }, } => xrpc_client.post( - &Nsid::from_str("com.atproto.session.create")?, + &Nsid::from_str("com.atproto.server.createSession")?, None, Some(json!({ "handle": handle, @@ -548,25 +556,34 @@ fn run(opt: Opt) -> Result<()> { )?, Command::Account { cmd: AccountCommand::Refresh, - } => xrpc_client.post(&Nsid::from_str("com.atproto.session.refresh")?, None, None)?, + } => xrpc_client.post( + &Nsid::from_str("com.atproto.server.refreshSession")?, + None, + None, + )?, Command::Account { cmd: AccountCommand::Logout, - } => xrpc_client.post(&Nsid::from_str("com.atproto.session.delete")?, None, None)?, + } => xrpc_client.post( + &Nsid::from_str("com.atproto.server.deleteSession")?, + None, + None, + )?, Command::Account { cmd: AccountCommand::Delete, - } => xrpc_client.post(&Nsid::from_str("com.atproto.account.delete")?, None, None)?, - Command::Account { - cmd: AccountCommand::Info, - } => xrpc_client.get(&Nsid::from_str("com.atproto.account.get")?, None)?, + } => xrpc_client.post( + &Nsid::from_str("com.atproto.server.deleteAccount")?, + None, + None, + )?, Command::Account { cmd: AccountCommand::CreateInvite { uses }, } => xrpc_client.post( - &Nsid::from_str("com.atproto.account.createInviteCode")?, + &Nsid::from_str("com.atproto.server.createInviteCode")?, None, Some(json!({ "useCount": uses })), )?, Command::Repo { - cmd: RepoCommand::Root { did }, + cmd: RepoCommand::Head { did }, } => { let did = match did { Some(DidOrHost::Host(_)) => return Err(anyhow!("expected a DID, not a hostname")), @@ -574,7 +591,7 @@ fn run(opt: Opt) -> Result<()> { None => jwt_did.ok_or(anyhow!("expected a DID"))?, }; params.insert("did".to_string(), did); - xrpc_client.get(&Nsid::from_str("com.atproto.sync.getRoot")?, Some(params))? + xrpc_client.get(&Nsid::from_str("com.atproto.sync.getHead")?, Some(params))? } Command::Repo { cmd: RepoCommand::Export { did, from }, @@ -617,7 +634,7 @@ fn run(opt: Opt) -> Result<()> { .as_ref() .map(|v| v.to_string()) .unwrap_or(require_auth_did(&opt, &mut xrpc_client)?.to_string()); - params.insert("author".to_string(), name); + params.insert("actor".to_string(), name); let resp = xrpc_client.get( &Nsid::from_str("app.bsky.feed.getAuthorFeed")?, Some(params), @@ -630,7 +647,7 @@ fn run(opt: Opt) -> Result<()> { .iter() { let val: serde_json::Value = val.clone(); - let fi: app_bsky::FeedPostView = serde_json::from_value(val)?; + let fi: app_bsky::FeedViewPost = serde_json::from_value(val)?; pretty::pp_feed_post_view(&fi)?; } None @@ -652,7 +669,7 @@ fn run(opt: Opt) -> Result<()> { { let val: serde_json::Value = val.clone(); //print_result_json(Some(val.clone()))?; - let fi: app_bsky::FeedPostView = serde_json::from_value(val)?; + let fi: app_bsky::FeedViewPost = serde_json::from_value(val)?; pretty::pp_feed_post_view(&fi)?; } None @@ -677,7 +694,7 @@ fn run(opt: Opt) -> Result<()> { resp["thread"] .as_object() .ok_or(anyhow!("expected thread from getPostThread"))?; - let tpv: app_bsky::ThreadPostView = serde_json::from_value(resp["thread"].clone())?; + let tpv: app_bsky::ThreadViewPost = serde_json::from_value(resp["thread"].clone())?; pretty::pp_thread_post_view(&tpv)?; None } else { @@ -688,7 +705,10 @@ fn run(opt: Opt) -> Result<()> { cmd: BskyCommand::Notifications, } => { require_auth_did(&opt, &mut xrpc_client)?; - xrpc_client.get(&Nsid::from_str("app.bsky.notification.list")?, None)? + xrpc_client.get( + &Nsid::from_str("app.bsky.notification.listNotifications")?, + None, + )? } Command::Bsky { cmd: BskyCommand::Post { ref text }, @@ -698,7 +718,7 @@ fn run(opt: Opt) -> Result<()> { &Nsid::from_str("com.atproto.repo.createRecord")?, None, Some(json!({ - "did": did, + "repo": did, "collection": "app.bsky.feed.post", "record": { "text": text, @@ -711,7 +731,7 @@ fn run(opt: Opt) -> Result<()> { cmd: BskyCommand::Repost { ref uri }, } => { let did = require_auth_did(&opt, &mut xrpc_client)?; - params.insert("user".to_string(), uri.repository.to_string()); + params.insert("repo".to_string(), uri.repository.to_string()); params.insert( "collection".to_string(), uri.collection @@ -732,7 +752,7 @@ fn run(opt: Opt) -> Result<()> { &Nsid::from_str("com.atproto.repo.createRecord")?, None, Some(json!({ - "did": did, + "repo": did, "collection": "app.bsky.feed.repost", "record": { "subject": { @@ -748,7 +768,7 @@ fn run(opt: Opt) -> Result<()> { cmd: BskyCommand::Like { ref uri }, } => { let did = require_auth_did(&opt, &mut xrpc_client)?; - params.insert("user".to_string(), uri.repository.to_string()); + params.insert("repo".to_string(), uri.repository.to_string()); params.insert( "collection".to_string(), uri.collection @@ -769,11 +789,10 @@ fn run(opt: Opt) -> Result<()> { &Nsid::from_str("com.atproto.repo.createRecord")?, None, Some(json!({ - "did": did, - "collection": "app.bsky.feed.vote", + "repo": did, + "collection": "app.bsky.feed.like", "record": { "subject": { "uri": uri.to_string(), "cid": cid }, - "direction": "up", "createdAt": created_at_now(), }, })), @@ -787,7 +806,7 @@ fn run(opt: Opt) -> Result<()> { &Nsid::from_str("com.atproto.repo.createRecord")?, None, Some(json!({ - "did": did, + "repo": did, "collection": "app.bsky.graph.follow", "record": { "subject": { "did": uri.to_string() }, @@ -807,10 +826,14 @@ fn run(opt: Opt) -> Result<()> { xrpc_client.get(&Nsid::from_str("app.bsky.actor.getProfile")?, Some(params))? } Command::Bsky { - cmd: BskyCommand::SearchUsers { query }, + cmd: BskyCommand::SearchUsers { ref query }, } => { - params.insert("term".to_string(), query); - xrpc_client.get(&Nsid::from_str("app.bsky.actor.search")?, Some(params))? + require_auth_did(&opt, &mut xrpc_client)?; + params.insert("term".to_string(), query.to_string()); + xrpc_client.get( + &Nsid::from_str("app.bsky.actor.searchActors")?, + Some(params), + )? } }; print_result_json(result)?; diff --git a/adenosine-cli/src/pretty.rs b/adenosine-cli/src/pretty.rs index 764a46f..a3cb5d6 100644 --- a/adenosine-cli/src/pretty.rs +++ b/adenosine-cli/src/pretty.rs @@ -1,9 +1,9 @@ -use adenosine::app_bsky::{FeedPostView, PostView, ThreadPostView}; +use adenosine::app_bsky::{FeedViewPost, PostView, ThreadViewPost}; use anyhow::Result; use std::io::Write; use termcolor::{Color, ColorChoice, ColorSpec, StandardStream, WriteColor}; -pub fn pp_thread_post_view(tpv: &ThreadPostView) -> Result<()> { +pub fn pp_thread_post_view(tpv: &ThreadViewPost) -> Result<()> { // TODO: this could do better if let Some(parent) = &tpv.parent { pp_thread_post_view(parent)?; @@ -31,7 +31,7 @@ pub fn pp_thread_post_view(tpv: &ThreadPostView) -> Result<()> { Ok(()) } -pub fn pp_feed_post_view(fpv: &FeedPostView) -> Result<()> { +pub fn pp_feed_post_view(fpv: &FeedViewPost) -> Result<()> { let mut stdout = StandardStream::stdout(ColorChoice::Always); if let Some(repost) = &fpv.reason { diff --git a/adenosine-pds/src/bin/adenosine-pds.rs b/adenosine-pds/src/bin/adenosine-pds.rs index e92c131..5e8842c 100644 --- a/adenosine-pds/src/bin/adenosine-pds.rs +++ b/adenosine-pds/src/bin/adenosine-pds.rs @@ -211,6 +211,7 @@ fn main() -> Result<()> { did_plc, } => { let req = com_atproto::AccountRequest { + did: None, email, handle: handle.clone(), password, diff --git a/adenosine-pds/src/db.rs b/adenosine-pds/src/db.rs index 4cbece6..2dac721 100644 --- a/adenosine-pds/src/db.rs +++ b/adenosine-pds/src/db.rs @@ -124,6 +124,7 @@ impl AtpDatabase { Ok(com_atproto::Session { did: did.to_string(), name: handle.to_string(), + email: None, accessJwt: jwt.to_string(), refreshJwt: jwt, }) @@ -193,7 +194,7 @@ impl AtpDatabase { // need to re-compute the CID from DagCbor re-encoding, I guess. bleh. let block = Block::<DefaultParams>::encode(DagCborCodec, Code::Sha2_256, &val)?; let cid = *block.cid(); - let post: app_bsky::Post = serde_json::from_value(ipld_into_json_value(val))?; + let post: app_bsky::PostRecord = serde_json::from_value(ipld_into_json_value(val))?; let (reply_to_parent_uri, reply_to_root_uri) = match post.reply { Some(ref reply) => (Some(reply.parent.uri.clone()), Some(reply.root.uri.clone())), None => (None, None), diff --git a/adenosine-pds/src/db_bsky.rs b/adenosine-pds/src/db_bsky.rs index b80ff4f..c603235 100644 --- a/adenosine-pds/src/db_bsky.rs +++ b/adenosine-pds/src/db_bsky.rs @@ -8,7 +8,6 @@ use adenosine::repo::Mutation; use anyhow::anyhow; use libipld::Cid; use rusqlite::params; -use serde_json::json; use std::str::FromStr; /// Handles updating the database with creation, update, deletion of arbitrary records @@ -59,7 +58,10 @@ pub fn bsky_mutate_db(db: &mut AtpDatabase, did: &Did, mutations: Vec<Mutation>) } // TODO: should probably return Result<Option<Profile>>? -pub fn bsky_get_profile(srv: &mut AtpService, did: &Did) -> Result<app_bsky::ProfileView> { +pub fn bsky_get_profile_detailed( + srv: &mut AtpService, + did: &Did, +) -> Result<app_bsky::ProfileViewDetailed> { // first get the profile record let mut profile_cid: Option<Cid> = None; let commit_cid = match srv.repo.lookup_commit(did)? { @@ -78,7 +80,7 @@ pub fn bsky_get_profile(srv: &mut AtpService, did: &Did) -> Result<app_bsky::Pro if let Some(cid) = profile_cid { let record: app_bsky::ProfileRecord = serde_json::from_value(ipld_into_json_value(srv.repo.get_ipld(&cid)?))?; - (Some(record.displayName), record.description) + (record.displayName, record.description) } else { (None, None) }; @@ -102,22 +104,33 @@ pub fn bsky_get_profile(srv: &mut AtpService, did: &Did) -> Result<app_bsky::Pro .conn .prepare_cached("SELECT COUNT(*) FROM bsky_follow WHERE subject_did = $1")?; let followers_count: u64 = stmt.query_row(params!(did.to_string()), |row| row.get(0))?; - let decl = app_bsky::DeclRef { - actorType: "app.bsky.system.actorUser".to_string(), - cid: "bafyreid27zk7lbis4zw5fz4podbvbs4fc5ivwji3dmrwa6zggnj4bnd57u".to_string(), - }; - Ok(app_bsky::ProfileView { + Ok(app_bsky::ProfileViewDetailed { did: did.to_string(), handle, - creator: did.to_string(), displayName: display_name, description, - declaration: decl, + avatar: None, + banner: None, followersCount: followers_count, followsCount: follows_count, postsCount: post_count, - membersCount: 0, - viewer: json!({}), + viewer: None, + labels: None, + indexedAt: None, + }) +} + +pub fn bsky_get_profile(srv: &mut AtpService, did: &Did) -> Result<app_bsky::ProfileView> { + let d = bsky_get_profile_detailed(srv, did)?; + Ok(app_bsky::ProfileView { + did: d.did, + handle: d.handle, + displayName: d.displayName, + description: d.description, + avatar: d.avatar, + indexedAt: d.indexedAt, + viewer: d.viewer, + labels: d.labels, }) } @@ -177,9 +190,10 @@ fn feed_row(row: &rusqlite::Row) -> Result<FeedRow> { }) } -fn feed_row_to_item(srv: &mut AtpService, row: FeedRow) -> Result<app_bsky::FeedPostView> { +fn feed_row_to_item(srv: &mut AtpService, row: FeedRow) -> Result<app_bsky::FeedViewPost> { 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 post_record: app_bsky::PostRecord = + 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 @@ -201,31 +215,27 @@ fn feed_row_to_item(srv: &mut AtpService, row: FeedRow) -> Result<app_bsky::Feed .prepare_cached("SELECT COUNT(*) FROM bsky_post WHERE reply_to_parent_uri = $1")?; let reply_count: u64 = stmt.query_row(params!(uri), |row| row.get(0))?; - let decl = app_bsky::DeclRef { - actorType: "app.bsky.system.actorUser".to_string(), - cid: "bafyreid27zk7lbis4zw5fz4podbvbs4fc5ivwji3dmrwa6zggnj4bnd57u".to_string(), - }; - let feed_item = app_bsky::FeedPostView { + let feed_item = app_bsky::FeedViewPost { post: app_bsky::PostView { uri, cid: row.item_post_cid.to_string(), - author: app_bsky::UserView { + author: app_bsky::ProfileViewBasic { did: row.item_did.to_string(), handle: row.item_handle, - declaration: decl, // TODO: displayName: None, avatar: None, viewer: None, + labels: None, }, record: post_record, embed: None, replyCount: reply_count, repostCount: repost_count, - upvoteCount: like_count, - downvoteCount: 0, + likeCount: like_count, indexedAt: row.indexed_at, viewer: None, + labels: None, }, // TODO: reason: None, @@ -235,7 +245,7 @@ fn feed_row_to_item(srv: &mut AtpService, row: FeedRow) -> Result<app_bsky::Feed } pub fn bsky_get_timeline(srv: &mut AtpService, did: &Did) -> Result<app_bsky::GenericFeed> { - let mut feed: Vec<app_bsky::FeedPostView> = vec![]; + let mut feed: Vec<app_bsky::FeedViewPost> = vec![]; // TODO: also handle reposts let rows = { let mut stmt = srv.atp_db @@ -252,11 +262,11 @@ pub fn bsky_get_timeline(srv: &mut AtpService, did: &Did) -> Result<app_bsky::Ge for row in rows { feed.push(feed_row_to_item(srv, row)?); } - Ok(app_bsky::GenericFeed { feed }) + Ok(app_bsky::GenericFeed { feed, cursor: None }) } pub fn bsky_get_author_feed(srv: &mut AtpService, did: &Did) -> Result<app_bsky::GenericFeed> { - let mut feed: Vec<app_bsky::FeedPostView> = vec![]; + let mut feed: Vec<app_bsky::FeedViewPost> = vec![]; // TODO: also handle reposts let rows = { let mut stmt = srv.atp_db @@ -273,7 +283,7 @@ pub fn bsky_get_author_feed(srv: &mut AtpService, did: &Did) -> Result<app_bsky: for row in rows { feed.push(feed_row_to_item(srv, row)?); } - Ok(app_bsky::GenericFeed { feed }) + Ok(app_bsky::GenericFeed { feed, cursor: None }) } // TODO: this is a partial implementation @@ -298,7 +308,7 @@ pub fn bsky_get_thread( _ => Err(anyhow!("expected a record in uri: {}", uri))?, }; - // post itself, as a app_bsky::FeedPostView + // post itself, as a app_bsky::FeedViewPost let post_items = { let mut stmt = srv.atp_db .conn @@ -335,7 +345,7 @@ pub fn bsky_get_thread( }; for row in rows { let item = feed_row_to_item(srv, row)?.post; - children.push(app_bsky::ThreadPostView { + children.push(app_bsky::ThreadViewPost { post: Some(app_bsky::PostView { uri: item.uri, cid: item.cid, @@ -343,11 +353,11 @@ pub fn bsky_get_thread( record: item.record, embed: item.embed, replyCount: item.replyCount, - upvoteCount: item.upvoteCount, - downvoteCount: 0, + likeCount: item.likeCount, repostCount: item.repostCount, indexedAt: item.indexedAt, viewer: None, + labels: None, }), // don't want a loop here parent: None, @@ -356,11 +366,14 @@ pub fn bsky_get_thread( // for "notfound" uri: None, notFound: None, + // for "blocked" variant + blocked: None, + author: None, }); } let pip = post_item.post; - let post = app_bsky::ThreadPostView { + let post = app_bsky::ThreadViewPost { post: Some(app_bsky::PostView { uri: pip.uri, cid: pip.cid, @@ -368,17 +381,20 @@ pub fn bsky_get_thread( record: pip.record, embed: pip.embed, replyCount: pip.replyCount, - upvoteCount: pip.upvoteCount, - downvoteCount: 0, + likeCount: pip.likeCount, repostCount: pip.repostCount, indexedAt: pip.indexedAt, viewer: None, + labels: None, }), parent, replies: Some(children), // for "notfound" variant uri: None, notFound: None, + // for "blocked" variant + blocked: None, + author: None, }; Ok(app_bsky::PostThread { thread: post }) } @@ -394,6 +410,7 @@ fn test_bsky_profile() { let mut srv = AtpService::new_ephemeral().unwrap(); let req = com_atproto::AccountRequest { + did: None, email: "test@bogus.com".to_string(), handle: "handle.test".to_string(), password: "bogus".to_string(), @@ -402,7 +419,7 @@ fn test_bsky_profile() { }; let session = create_account(&mut srv, &req, true).unwrap(); let did = Did::from_str(&session.did).unwrap(); - let profile = bsky_get_profile(&mut srv, &did).unwrap(); + let profile = bsky_get_profile_detailed(&mut srv, &did).unwrap(); assert_eq!(profile.did, session.did); assert_eq!(profile.handle, req.handle); assert_eq!(profile.displayName, None); @@ -412,21 +429,25 @@ fn test_bsky_profile() { assert_eq!(profile.postsCount, 0); let record = app_bsky::ProfileRecord { - displayName: "Test Name".to_string(), + displayName: Some("Test Name".to_string()), description: Some("short description".to_string()), + avatar: None, + banner: None, }; bsky_update_profile(&mut srv, &did, record.clone()).unwrap(); let profile = bsky_get_profile(&mut srv, &did).unwrap(); - assert_eq!(profile.displayName, Some(record.displayName)); + assert_eq!(profile.displayName, record.displayName); assert_eq!(profile.description, record.description); let record = app_bsky::ProfileRecord { - displayName: "New Test Name".to_string(), + displayName: Some("New Test Name".to_string()), description: Some("longer description".to_string()), + avatar: None, + banner: None, }; bsky_update_profile(&mut srv, &did, record.clone()).unwrap(); let profile = bsky_get_profile(&mut srv, &did).unwrap(); - assert_eq!(profile.displayName, Some(record.displayName)); + assert_eq!(profile.displayName, record.displayName); assert_eq!(profile.description, record.description); let mutations = vec![ @@ -461,7 +482,7 @@ fn test_bsky_profile() { .unwrap(); bsky_mutate_db(&mut srv.atp_db, &did, mutations).unwrap(); - let profile = bsky_get_profile(&mut srv, &did).unwrap(); + let profile = bsky_get_profile_detailed(&mut srv, &did).unwrap(); assert_eq!(profile.followersCount, 1); assert_eq!(profile.followsCount, 2); assert_eq!(profile.postsCount, 3); @@ -482,6 +503,7 @@ fn test_bsky_feeds() { let mut srv = AtpService::new_ephemeral().unwrap(); let alice_did = { let req = com_atproto::AccountRequest { + did: None, email: "alice@bogus.com".to_string(), handle: "alice.test".to_string(), password: "bogus".to_string(), @@ -493,6 +515,7 @@ fn test_bsky_feeds() { }; let bob_did = { let req = com_atproto::AccountRequest { + did: None, email: "bob@bogus.com".to_string(), handle: "bob.test".to_string(), password: "bogus".to_string(), @@ -504,6 +527,7 @@ fn test_bsky_feeds() { }; let carol_did = { let req = com_atproto::AccountRequest { + did: None, email: "carol@bogus.com".to_string(), handle: "carol.test".to_string(), password: "bogus".to_string(), @@ -596,7 +620,7 @@ fn test_bsky_feeds() { bsky_mutate_db(&mut srv.atp_db, &carol_did, mutations).unwrap(); // test alice profile: counts should be updated - let alice_profile = bsky_get_profile(&mut srv, &alice_did).unwrap(); + let alice_profile = bsky_get_profile_detailed(&mut srv, &alice_did).unwrap(); assert_eq!(alice_profile.followersCount, 1); assert_eq!(alice_profile.followsCount, 0); assert_eq!(alice_profile.postsCount, 3); @@ -621,18 +645,17 @@ fn test_bsky_feeds() { 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[2].post.likeCount, 1); 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[1].post.likeCount, 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); + assert_eq!(alice_feed.feed[0].post.likeCount, 0); // test bob timeline: should include alice posts let bob_timeline = bsky_get_timeline(&mut srv, &bob_did).unwrap(); @@ -687,6 +710,7 @@ fn test_bsky_thread() { let mut srv = AtpService::new_ephemeral().unwrap(); let alice_did = { let req = com_atproto::AccountRequest { + did: None, email: "alice@bogus.com".to_string(), handle: "alice.test".to_string(), password: "bogus".to_string(), @@ -698,6 +722,7 @@ fn test_bsky_thread() { }; let bob_did = { let req = com_atproto::AccountRequest { + did: None, email: "bob@bogus.com".to_string(), handle: "bob.test".to_string(), password: "bogus".to_string(), @@ -758,7 +783,7 @@ fn test_bsky_thread() { assert_eq!(ppost.embed, None); assert_eq!(ppost.replyCount, 1); assert_eq!(ppost.repostCount, 0); - assert_eq!(ppost.upvoteCount, 0); + assert_eq!(ppost.likeCount, 0); assert_eq!(post.replies.as_ref().unwrap().len(), 1); let post_replies = post.replies.unwrap(); diff --git a/adenosine-pds/src/lib.rs b/adenosine-pds/src/lib.rs index c267135..cb41cb9 100644 --- a/adenosine-pds/src/lib.rs +++ b/adenosine-pds/src/lib.rs @@ -303,7 +303,7 @@ fn xrpc_get_handler( ) } "com.atproto.repo.getRecord" => { - let did = Did::from_str(&xrpc_required_param(request, "user")?)?; + let did = Did::from_str(&xrpc_required_param(request, "repo")?)?; let collection = Nsid::from_str(&xrpc_required_param(request, "collection")?)?; let rkey = Tid::from_str(&xrpc_required_param(request, "rkey")?)?; let mut srv = srv.lock().or(Err(XrpcError::MutexPoisoned))?; @@ -317,8 +317,8 @@ fn xrpc_get_handler( Err(e) => Err(e), } } - "com.atproto.sync.getRoot" => { - let did = Did::from_str(&xrpc_required_param(request, "did")?)?; + "com.atproto.sync.getHead" => { + let did = Did::from_str(&xrpc_required_param(request, "repo")?)?; let mut srv = srv.lock().or(Err(XrpcError::MutexPoisoned))?; srv.repo .lookup_commit(&did)? @@ -329,7 +329,7 @@ fn xrpc_get_handler( // TODO: limit, before, after, tid, reverse // TODO: handle non-DID 'user' // TODO: limit result set size - let did = Did::from_str(&xrpc_required_param(request, "user")?)?; + let did = Did::from_str(&xrpc_required_param(request, "repo")?)?; let collection = Nsid::from_str(&xrpc_required_param(request, "collection")?)?; let mut record_list: Vec<Value> = vec![]; let mut srv = srv.lock().or(Err(XrpcError::MutexPoisoned))?; @@ -350,15 +350,6 @@ fn xrpc_get_handler( } Ok(json!({ "records": record_list })) } - "com.atproto.session.get" => { - let mut srv = srv.lock().or(Err(XrpcError::MutexPoisoned))?; - let auth_did = &xrpc_check_auth_header(&mut srv, request, None)?; - let handle = srv - .atp_db - .resolve_did(auth_did)? - .expect("registered account has handle"); - Ok(json!({"did": auth_did.to_string(), "handle": handle})) - } "com.atproto.handle.resolve" => { let handle = xrpc_required_param(request, "handle")?; let mut srv = srv.lock().or(Err(XrpcError::MutexPoisoned))?; @@ -370,17 +361,17 @@ fn xrpc_get_handler( } } "com.atproto.repo.describe" => { - let did = Did::from_str(&xrpc_required_param(request, "user")?)?; + let did = Did::from_str(&xrpc_required_param(request, "repo")?)?; let mut srv = srv.lock().or(Err(XrpcError::MutexPoisoned))?; let did_doc = srv.atp_db.get_did_doc(&did)?; let collections: Vec<String> = srv.repo.collections(&did)?; let desc = com_atproto::repo::Describe { - name: did.to_string(), // TODO: handle? + handle: did.to_string(), // TODO: handle? did: did.to_string(), didDoc: did_doc, collections, - nameIsCorrect: true, + handleIsCorrect: true, }; Ok(json!(desc)) } @@ -408,7 +399,7 @@ fn xrpc_get_handler( } "app.bsky.feed.getAuthorFeed" => { // TODO did or handle - let did = Did::from_str(&xrpc_required_param(request, "author")?)?; + let did = Did::from_str(&xrpc_required_param(request, "actor")?)?; let mut srv = srv.lock().unwrap(); Ok(json!(bsky_get_author_feed(&mut srv, &did)?)) } @@ -434,7 +425,7 @@ fn xrpc_get_handler( let _auth_did = &xrpc_check_auth_header(&mut srv, request, None)?; Ok(json!({"count": 0})) } - "app.bsky.notification.list" => { + "app.bsky.notification.listNotifications" => { // TODO: actual implementation let mut srv = srv.lock().or(Err(XrpcError::MutexPoisoned))?; let _auth_did = &xrpc_check_auth_header(&mut srv, request, None)?; @@ -447,7 +438,7 @@ fn xrpc_get_handler( } fn xrpc_get_repo_handler(srv: &Mutex<AtpService>, request: &Request) -> Result<Vec<u8>> { - let did = Did::from_str(&xrpc_required_param(request, "did")?)?; + let did = Did::from_str(&xrpc_required_param(request, "repo")?)?; let mut srv = srv.lock().or(Err(XrpcError::MutexPoisoned))?; // TODO: don't unwrap here let commit_cid = srv.repo.lookup_commit(&did)?.unwrap(); @@ -520,7 +511,7 @@ fn xrpc_post_handler( request: &Request, ) -> Result<serde_json::Value> { match method { - "com.atproto.account.create" => { + "com.atproto.server.createAccount" => { // validate account request let req: com_atproto::AccountRequest = rouille::input::json_input(request) .map_err(|e| XrpcError::BadRequest(format!("failed to parse JSON body: {e}")))?; @@ -546,18 +537,18 @@ fn xrpc_post_handler( let sess = create_account(&mut srv, &req, true)?; Ok(json!(sess)) } - "com.atproto.session.create" => { + "com.atproto.server.createSession" => { let req: com_atproto::SessionRequest = rouille::input::json_input(request) .map_err(|e| XrpcError::BadRequest(format!("failed to parse JSON body: {e}")))?; let mut srv = srv.lock().unwrap(); let keypair = srv.pds_keypair.clone(); Ok(json!(srv.atp_db.create_session( - &req.handle, + &req.identifier, &req.password, &keypair )?)) } - "com.atproto.session.refresh" => { + "com.atproto.server.refreshSession" => { // actually just returns current session, because we don't implement refresh let mut srv = srv.lock().unwrap(); let did = xrpc_check_auth_header(&mut srv, request, None)?; @@ -576,11 +567,12 @@ fn xrpc_post_handler( Ok(json!(com_atproto::Session { did: did.to_string(), name: handle, + email: None, accessJwt: jwt.to_string(), refreshJwt: jwt.to_string(), })) } - "com.atproto.session.delete" => { + "com.atproto.server.deleteSession" => { let mut srv = srv.lock().unwrap(); let _did = xrpc_check_auth_header(&mut srv, request, None)?; let header = request @@ -597,10 +589,10 @@ fn xrpc_post_handler( }; Ok(json!({})) } - "com.atproto.repo.batchWrite" => { + "com.atproto.repo.applyWrites" => { let batch: com_atproto::repo::BatchWriteBody = rouille::input::json_input(request)?; // TODO: validate edits against schemas - let did = Did::from_str(&batch.did)?; + let did = Did::from_str(&batch.repo)?; let mut srv = srv.lock().unwrap(); let _auth_did = &xrpc_check_auth_header(&mut srv, request, Some(&did))?; let mut mutations: Vec<Mutation> = Default::default(); @@ -613,12 +605,12 @@ fn xrpc_post_handler( .as_ref() .map(|t| Tid::from_str(t).unwrap()) .unwrap_or_else(|| srv.tid_gen.next_tid()), - json_value_into_ipld(w.value.clone()), + json_value_into_ipld(w.value.clone().unwrap()), ), "update" => Mutation::Update( Nsid::from_str(&w.collection)?, Tid::from_str(w.rkey.as_ref().unwrap())?, - json_value_into_ipld(w.value.clone()), + json_value_into_ipld(w.value.clone().unwrap()), ), "delete" => Mutation::Delete( Nsid::from_str(&w.collection)?, @@ -685,7 +677,7 @@ fn xrpc_post_handler( } "com.atproto.sync.updateRepo" => { // TODO: all other XRPC POST methods removed params (eg, 'did' in this case) - let did = Did::from_str(&xrpc_required_param(request, "did")?)?; + let did = Did::from_str(&xrpc_required_param(request, "repo")?)?; // important that this read is before we take the mutex, because it could be slow! let mut car_bytes: Vec<u8> = Default::default(); // TODO: unwrap() @@ -769,7 +761,7 @@ fn account_view_handler( Ok(AccountView { domain: host.to_string(), did: did.clone(), - profile: bsky_get_profile(&mut srv, &did)?, + profile: bsky_get_profile_detailed(&mut srv, &did)?, feed: bsky_get_author_feed(&mut srv, &did)?.feed, } .render()?) @@ -809,11 +801,11 @@ fn repo_view_handler(srv: &Mutex<AtpService>, did: &str, request: &Request) -> R let commit = srv.repo.get_commit(commit_cid)?; let collections: Vec<String> = srv.repo.collections(&did)?; let desc = com_atproto::repo::Describe { - name: did.to_string(), // TODO + handle: did.to_string(), // TODO did: did.to_string(), didDoc: did_doc, collections, - nameIsCorrect: true, + handleIsCorrect: true, }; Ok(RepoView { diff --git a/adenosine-pds/src/web.rs b/adenosine-pds/src/web.rs index fc75a93..80e6f15 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::ProfileView, - pub feed: Vec<app_bsky::FeedPostView>, + pub profile: app_bsky::ProfileViewDetailed, + pub feed: Vec<app_bsky::FeedViewPost>, } #[derive(Template)] @@ -40,7 +40,7 @@ pub struct ThreadView { pub did: Did, pub collection: Nsid, pub tid: Tid, - pub post: app_bsky::ThreadPostView, + pub post: app_bsky::ThreadViewPost, } #[derive(Template)] diff --git a/adenosine-pds/templates/macro.html b/adenosine-pds/templates/macro.html index 795400b..d586adb 100644 --- a/adenosine-pds/templates/macro.html +++ b/adenosine-pds/templates/macro.html @@ -28,7 +28,7 @@ {{ item.record.text }} <br> <span class="counts"> - [<a href="#">{{ item.upvoteCount }} upvote</a> / <a href="#">{{ item.repostCount }} repost</a> / <a href="#">{{ item.replyCount }} reply</a>] + [<a href="#">{{ item.likeCount }} like</a> / <a href="#">{{ item.repostCount }} repost</a> / <a href="#">{{ item.replyCount }} reply</a>] <a href="{{ item.uri|aturi_to_path }}" class="pink">[inspect]</a> </span> diff --git a/adenosine/src/app_bsky/mod.rs b/adenosine/src/app_bsky/mod.rs index 76352f5..61063fb 100644 --- a/adenosine/src/app_bsky/mod.rs +++ b/adenosine/src/app_bsky/mod.rs @@ -26,7 +26,6 @@ pub struct RefRecord { #[derive(Debug, serde::Serialize, serde::Deserialize, Clone, PartialEq, Eq)] pub struct FollowSubject { pub did: String, - // pub declarationCid: String, } #[allow(non_snake_case)] @@ -39,128 +38,158 @@ pub struct FollowRecord { #[allow(non_snake_case)] #[derive(Debug, serde::Serialize, serde::Deserialize, Clone, PartialEq, Eq)] pub struct ProfileRecord { - pub displayName: String, + pub displayName: Option<String>, pub description: Option<String>, + pub avatar: Option<Blob>, + pub banner: Option<Blob>, + // TODO: self-labels } -// app.bsky.system.actorUser or app.bsky.system.actorScene -#[allow(non_snake_case)] -#[derive(Debug, serde::Serialize, serde::Deserialize, Clone, PartialEq, Eq)] -pub struct Declaration { - pub actorType: String, -} - -// actorType: app.bsky.system.actorUser -// cid: bafyreid27zk7lbis4zw5fz4podbvbs4fc5ivwji3dmrwa6zggnj4bnd57u #[allow(non_snake_case)] #[derive(Debug, serde::Serialize, serde::Deserialize, Clone, PartialEq, Eq)] -pub struct DeclRef { - pub actorType: String, - pub cid: String, +pub struct Label { + pub src: String, + pub uri: String, + pub cid: Option<String>, + pub val: String, + pub neg: Option<bool>, + pub cts: String, } #[allow(non_snake_case)] #[derive(Debug, serde::Serialize, serde::Deserialize, Clone, PartialEq, Eq)] pub struct ProfileView { pub did: String, - pub declaration: DeclRef, pub handle: String, - // for simple accounts, 'creator' is just the did - pub creator: String, pub displayName: Option<String>, pub description: Option<String>, - pub followersCount: u64, - pub followsCount: u64, - pub membersCount: u64, - pub postsCount: u64, - pub viewer: serde_json::Value, + pub avatar: Option<String>, + pub indexedAt: Option<String>, + pub viewer: Option<ViewerState>, + pub labels: Option<Vec<Label>>, } -/// for Timeline or AuthorFeed #[allow(non_snake_case)] #[derive(Debug, serde::Serialize, serde::Deserialize, Clone, PartialEq, Eq)] -pub struct GenericFeed { - pub feed: Vec<FeedPostView>, +pub struct ViewerState { + pub muted: Option<bool>, + pub mutedByList: Option<Value>, // TODO + pub blockedBy: Option<bool>, + pub blocking: Option<String>, + pub following: Option<String>, + pub followedBy: Option<String>, } #[allow(non_snake_case)] #[derive(Debug, serde::Serialize, serde::Deserialize, Clone, PartialEq, Eq)] -pub struct User { +pub struct ProfileViewBasic { pub did: String, pub handle: String, pub displayName: Option<String>, + pub avatar: Option<String>, + pub viewer: Option<Value>, + pub labels: Option<Vec<Label>>, } #[allow(non_snake_case)] #[derive(Debug, serde::Serialize, serde::Deserialize, Clone, PartialEq, Eq)] -pub struct UserView { +pub struct ProfileViewDetailed { pub did: String, pub handle: String, - pub declaration: DeclRef, pub displayName: Option<String>, + pub description: Option<String>, pub avatar: Option<String>, - pub viewer: Option<Value>, + pub banner: Option<String>, + pub followersCount: u64, + pub followsCount: u64, + pub postsCount: u64, + pub indexedAt: Option<String>, + pub viewer: Option<ViewerState>, + pub labels: Option<Vec<Label>>, } +/// for Timeline or AuthorFeed +#[allow(non_snake_case)] +#[derive(Debug, serde::Serialize, serde::Deserialize, Clone, PartialEq, Eq)] +pub struct GenericFeed { + pub cursor: Option<String>, + pub feed: Vec<FeedViewPost>, +} +/* XXX: +#[allow(non_snake_case)] +#[derive(Debug, serde::Serialize, serde::Deserialize, Clone, PartialEq, Eq)] +pub struct User { + pub did: String, + pub handle: String, + pub displayName: Option<String>, +} +*/ + #[allow(non_snake_case)] #[derive(Debug, serde::Serialize, serde::Deserialize, Clone, PartialEq, Eq)] pub struct PostView { pub uri: String, pub cid: String, - pub author: UserView, - pub record: Post, + pub author: ProfileViewBasic, + pub record: PostRecord, pub embed: Option<PostEmbedView>, pub replyCount: u64, pub repostCount: u64, - pub upvoteCount: u64, - pub downvoteCount: u64, + pub likeCount: u64, pub indexedAt: String, pub viewer: Option<Value>, + pub labels: Option<Vec<Label>>, } #[allow(non_snake_case)] #[derive(Debug, serde::Serialize, serde::Deserialize, Clone, PartialEq, Eq)] -pub struct ThreadPostView { +pub struct ThreadViewPost { // TODO: doing this as the intersetion of #threadViewPost and #notFoundPost. actually it is // supposed to be a union type // #notFoundPost fields (uri and notFound actually required) pub uri: Option<String>, pub notFound: Option<bool>, + // #blockedPost fields (uri and blocked actually required) + pub blocked: Option<bool>, + pub author: Option<Value>, // #threadViewPost fields (post actually required) pub post: Option<PostView>, - pub parent: Option<Box<ThreadPostView>>, - pub replies: Option<Vec<ThreadPostView>>, + pub parent: Option<Box<ThreadViewPost>>, + pub replies: Option<Vec<ThreadViewPost>>, } #[allow(non_snake_case)] #[derive(Debug, serde::Serialize, serde::Deserialize, Clone, PartialEq, Eq)] -pub struct FeedPostView { +pub struct FeedViewPost { pub post: PostView, - pub reply: Option<PostReply>, + pub reply: Option<ReplyRef>, // TODO: this could extend to other "reasons" in the future - pub reason: Option<RepostReason>, + pub reason: Option<ReasonRepost>, } #[allow(non_snake_case)] #[derive(Debug, serde::Serialize, serde::Deserialize, Clone, PartialEq, Eq)] -pub struct RepostReason { - pub by: UserView, +pub struct ReasonRepost { + pub by: ProfileViewBasic, pub indexedAt: String, } #[allow(non_snake_case)] #[derive(Debug, serde::Serialize, serde::Deserialize, Clone, PartialEq, Eq)] -pub struct Post { +pub struct PostRecord { pub text: String, - pub reply: Option<PostReply>, pub entities: Option<Vec<PostEntity>>, + pub facets: Option<Vec<RichtextFacet>>, + pub reply: Option<ReplyRef>, pub embed: Option<PostEmbed>, + pub langs: Option<Vec<String>>, + pub labels: Option<Vec<String>>, pub createdAt: Option<String>, } #[allow(non_snake_case)] #[derive(Debug, serde::Serialize, serde::Deserialize, Clone, PartialEq, Eq)] -pub struct PostReply { +pub struct ReplyRef { // TODO: these should be StrongRef pub parent: Subject, pub root: Subject, @@ -168,6 +197,28 @@ pub struct PostReply { #[allow(non_snake_case)] #[derive(Debug, serde::Serialize, serde::Deserialize, Clone, PartialEq, Eq)] +pub struct RichtextFacet { + pub index: ByteSlice, + pub features: Vec<FacetFeature>, +} + +#[allow(non_snake_case)] +#[derive(Debug, serde::Serialize, serde::Deserialize, Clone, PartialEq, Eq)] +pub struct FacetFeature { + // TODO: this is a hack; actually separate mention and link types + pub did: Option<String>, + pub uri: Option<String>, +} + +#[allow(non_snake_case)] +#[derive(Debug, serde::Serialize, serde::Deserialize, Clone, PartialEq, Eq)] +pub struct ByteSlice { + pub byteStart: u64, + pub byteEnd: u64, +} + +#[allow(non_snake_case)] +#[derive(Debug, serde::Serialize, serde::Deserialize, Clone, PartialEq, Eq)] pub struct PostEntity { pub index: TextSlice, pub r#type: String, @@ -186,13 +237,17 @@ pub struct TextSlice { pub struct PostEmbed { pub external: Option<EmbedExternal>, pub images: Option<Vec<EmbedImage>>, + pub record: Option<StrongRef>, + pub recordWithMedia: Option<Value>, // TODO } #[allow(non_snake_case)] #[derive(Debug, serde::Serialize, serde::Deserialize, Clone, PartialEq, Eq)] pub struct PostEmbedView { - pub external: Option<EmbedExternalView>, pub images: Option<Vec<EmbedImageView>>, + pub external: Option<EmbedExternalView>, + pub record: Option<Value>, // TODO + pub recordWithMedia: Option<Value>, // TODO } #[allow(non_snake_case)] @@ -223,8 +278,20 @@ pub struct EmbedImage { #[allow(non_snake_case)] #[derive(Debug, serde::Serialize, serde::Deserialize, Clone, PartialEq, Eq)] pub struct Blob { - pub cid: String, + #[serde(rename = "$type")] + pub blob_type: Option<String>, + #[serde(rename = "ref")] + pub link: Option<CidLink>, + pub cid: Option<String>, // deprecated pub mimeType: String, + pub size: Option<u64>, +} + +#[allow(non_snake_case)] +#[derive(Debug, serde::Serialize, serde::Deserialize, Clone, PartialEq, Eq)] +pub struct CidLink { + #[serde(rename = "$link")] + pub cid: String, } #[allow(non_snake_case)] @@ -238,9 +305,10 @@ pub struct EmbedImageView { #[allow(non_snake_case)] #[derive(Debug, serde::Serialize, serde::Deserialize, Clone, PartialEq, Eq)] pub struct PostThread { - pub thread: ThreadPostView, + pub thread: ThreadViewPost, } +/* XXX #[allow(non_snake_case)] #[derive(Debug, serde::Serialize, serde::Deserialize, Clone, PartialEq, Eq)] pub struct FollowTarget { @@ -252,11 +320,12 @@ pub struct FollowTarget { pub createdAt: Option<String>, pub indexedAt: String, } +*/ #[allow(non_snake_case)] #[derive(Debug, serde::Serialize, serde::Deserialize, Clone, PartialEq, Eq)] -pub struct Follow { - // TODO: nested follow list? +pub struct FollowList { pub subject: Subject, - pub follows: FollowTarget, + pub cursor: Option<String>, + pub follows: Vec<ProfileView>, } diff --git a/adenosine/src/com_atproto/mod.rs b/adenosine/src/com_atproto/mod.rs index 8e2317a..7a6cbc0 100644 --- a/adenosine/src/com_atproto/mod.rs +++ b/adenosine/src/com_atproto/mod.rs @@ -7,15 +7,16 @@ pub mod repo; pub struct AccountRequest { pub email: String, pub handle: String, - pub password: String, + pub did: Option<String>, pub inviteCode: Option<String>, + pub password: String, pub recoveryKey: Option<String>, } #[allow(non_snake_case)] #[derive(Debug, serde::Deserialize, serde::Serialize, PartialEq, Eq)] pub struct SessionRequest { - pub handle: String, + pub identifier: String, pub password: String, } @@ -24,6 +25,7 @@ pub struct SessionRequest { pub struct Session { pub did: String, pub name: String, + pub email: Option<String>, pub accessJwt: String, pub refreshJwt: String, } diff --git a/adenosine/src/com_atproto/repo/mod.rs b/adenosine/src/com_atproto/repo/mod.rs index aa66e98..de5b56b 100644 --- a/adenosine/src/com_atproto/repo/mod.rs +++ b/adenosine/src/com_atproto/repo/mod.rs @@ -3,11 +3,11 @@ #[allow(non_snake_case)] #[derive(Debug, serde::Serialize, serde::Deserialize, Clone, PartialEq, Eq)] pub struct Describe { - pub name: String, + pub handle: String, pub did: String, pub didDoc: serde_json::Value, pub collections: Vec<String>, - pub nameIsCorrect: bool, + pub handleIsCorrect: bool, } #[allow(non_snake_case)] @@ -15,7 +15,10 @@ pub struct Describe { pub struct CreateRecord { pub did: String, pub collection: String, + pub rkey: Option<String>, + pub validate: Option<bool>, pub record: serde_json::Value, + pub swapCommit: Option<String>, } #[allow(non_snake_case)] @@ -24,7 +27,10 @@ pub struct PutRecord { pub did: String, pub collection: String, pub rkey: String, + pub validate: Option<bool>, pub record: serde_json::Value, + pub swapRecord: Option<String>, + pub swapCommit: Option<String>, } #[allow(non_snake_case)] @@ -33,21 +39,25 @@ pub struct DeleteRecord { pub did: String, pub collection: String, pub rkey: String, + pub swapRecord: Option<String>, + pub swapCommit: Option<String>, } #[allow(non_snake_case)] #[derive(Debug, serde::Serialize, serde::Deserialize, Clone, PartialEq, Eq)] pub struct BatchWriteBody { - pub did: String, + pub repo: String, + pub validate: Option<bool>, pub writes: Vec<BatchWrite>, + pub swapCommit: Option<String>, } #[allow(non_snake_case)] #[derive(Debug, serde::Serialize, serde::Deserialize, Clone, PartialEq, Eq)] pub struct BatchWrite { - #[serde(rename = "type")] + #[serde(rename = "$type")] pub op_type: String, pub collection: String, pub rkey: Option<String>, - pub value: serde_json::Value, + pub value: Option<serde_json::Value>, } diff --git a/adenosine/src/xrpc.rs b/adenosine/src/xrpc.rs index 6a89ba1..2a185ce 100644 --- a/adenosine/src/xrpc.rs +++ b/adenosine/src/xrpc.rs @@ -106,7 +106,11 @@ impl XrpcClient { /// Uses refresh token to update auth token pub fn auth_refresh(&mut self) -> Result<()> { self.auth_token = self.refresh_token.clone(); - let resp = self.post(&Nsid::from_str("com.atproto.session.refresh")?, None, None)?; + let resp = self.post( + &Nsid::from_str("com.atproto.server.refreshSession")?, + None, + None, + )?; let resp = resp.ok_or(anyhow!("missing session auth info"))?; self.auth_token = resp["accessJwt"].as_str().map(|s| s.to_string()); self.refresh_token = resp["refreshJwt"].as_str().map(|s| s.to_string()); diff --git a/extra/adenosine.1 b/extra/adenosine.1 index 060977e..658bca8 100644 --- a/extra/adenosine.1 +++ b/extra/adenosine.1 @@ -5,7 +5,7 @@ .nh .ad l .\" Begin generated content: -.TH "adenosine" "1" "2023-03-05" "adenosine CLI Client Manual Page" +.TH "adenosine" "1" "2023-08-21" "adenosine CLI Client Manual Page" .P .SH NAME .P @@ -150,11 +150,6 @@ Query by partial handle .P \fBaccount register --email <email> --password <password> --handle <handle>\fR .P -\fBaccount info\fR -.RS 4 -Fetches account metadata for the current session -.P -.RE \fBaccount login --password <password> --handle <handle>\fR .RS 4 Create a new authenticated session @@ -177,9 +172,9 @@ Dump raw binary repository as CAR format to stdout Read raw binary repository as CAR format from stdin, and import to PDS .P .RE -\fBrepo root [did]\fR +\fBrepo head [did]\fR .RS 4 -Get the current '\&root'\& commit for a DID +Get the latest commit for a DID .P .RE .SH OPTIONS diff --git a/extra/adenosine.1.md b/extra/adenosine.1.md index a724f3c..7f70eb3 100644 --- a/extra/adenosine.1.md +++ b/extra/adenosine.1.md @@ -129,10 +129,6 @@ parameters and body fields. Body fields only used for \"post\" requests. **account register \--email \<email\> \--password \<password\> \--handle \<handle\>** -**account info** - -> Fetches account metadata for the current session - **account login \--password \<password\> \--handle \<handle\>** > Create a new authenticated session @@ -151,9 +147,9 @@ parameters and body fields. Body fields only used for \"post\" requests. > Read raw binary repository as CAR format from stdin, and import to PDS -**repo root \[did\]** +**repo head \[did\]** -> Get the current \'root\' commit for a DID +> Get the latest commit for a DID # OPTIONS diff --git a/extra/adenosine.1.scdoc b/extra/adenosine.1.scdoc index e6e63ef..aa09334 100644 --- a/extra/adenosine.1.scdoc +++ b/extra/adenosine.1.scdoc @@ -107,9 +107,6 @@ body fields. Body fields only used for "post" requests. *account register --email <email> --password <password> --handle <handle>* -*account info* - Fetches account metadata for the current session - *account login --password <password> --handle <handle>* Create a new authenticated session @@ -124,8 +121,8 @@ body fields. Body fields only used for "post" requests. *repo import [did]* Read raw binary repository as CAR format from stdin, and import to PDS -*repo root [did]* - Get the current 'root' commit for a DID +*repo head [did]* + Get the latest commit for a DID # OPTIONS |