From 19379222b7b0003bd0538fdd906ee87a9220ddca Mon Sep 17 00:00:00 2001 From: Bryan Newbold Date: Fri, 28 Oct 2022 00:09:57 -0700 Subject: cli: bug fixes for creation, etc --- adenosine-cli/src/bin/adenosine.rs | 65 ++++++++++++++++++++++++-------------- adenosine-cli/src/lib.rs | 60 ++++++++++++++++++++++++++++++----- 2 files changed, 93 insertions(+), 32 deletions(-) (limited to 'adenosine-cli/src') diff --git a/adenosine-cli/src/bin/adenosine.rs b/adenosine-cli/src/bin/adenosine.rs index 33f74c9..1d6e22d 100644 --- a/adenosine-cli/src/bin/adenosine.rs +++ b/adenosine-cli/src/bin/adenosine.rs @@ -104,7 +104,7 @@ enum BskyCommand { name: String, }, */ - Profile { name: DidOrHost }, + Profile { name: Option }, SearchUsers { query: String }, } @@ -235,7 +235,6 @@ fn run(opt: Opt) -> Result<()> { let result = match opt.cmd { Command::Status => { - // XXX println!("Configuration"); println!(" ATP_HOST: {}", opt.atp_host); if opt.auth_token.is_some() { @@ -252,7 +251,7 @@ fn run(opt: Opt) -> Result<()> { let name = name .map(|v| v.to_string()) .or(jwt_did) - .ok_or(anyhow!("expected a name or auth token"))?; + .ok_or(anyhow!("expected a name, or self via auth token"))?; params.insert("user".to_string(), name.to_string()); xrpc_client.get("com.atproto.repoDescribe", Some(params))? } @@ -262,7 +261,7 @@ fn run(opt: Opt) -> Result<()> { xrpc_client.get("com.atproto.resolveName", Some(params))? } Command::Get { uri, cid } => { - params.insert("did".to_string(), uri.repository.to_string()); + params.insert("user".to_string(), uri.repository.to_string()); params.insert( "collection".to_string(), uri.collection.ok_or(anyhow!("collection required"))?, @@ -278,9 +277,9 @@ fn run(opt: Opt) -> Result<()> { } Command::Ls { uri } => { // TODO: option to print fully-qualified path? + params.insert("user".to_string(), uri.repository.to_string()); if !uri.collection.is_some() { // if a repository, but no collection, list the collections - params.insert("user".to_string(), uri.repository.to_string()); let describe = xrpc_client .get("com.atproto.repoDescribe", Some(params))? .ok_or(anyhow!("expected a repoDescribe response"))?; @@ -296,16 +295,30 @@ fn run(opt: Opt) -> Result<()> { } } else if uri.collection.is_some() && !uri.record.is_some() { // if a collection, but no record, list the records (with extracted timestamps) + params.insert( + "collection".to_string(), + uri.collection.unwrap().to_string(), + ); + let records = xrpc_client + .get("com.atproto.repoListRecords", Some(params))? + .ok_or(anyhow!("expected a repoListRecords response"))?; + for r in records.as_array().unwrap_or(&vec![]).iter() { + println!("{}", r); + } } else { return Err(anyhow!("got too much of a URI to 'ls'")); } None } Command::Create { collection, fields } => { + params.insert( + "did".to_string(), + jwt_did.ok_or(anyhow!("need auth token"))?, + ); params.insert("collection".to_string(), collection); update_params_from_fields(&fields, &mut params); let val = value_from_fields(fields); - xrpc_client.post("com.atproto.repoCreateRecord", Some(params), val)? + xrpc_client.post("com.atproto.repoCreateRecord", Some(params), Some(val))? } Command::Update { uri, fields } => { params.insert("did".to_string(), uri.repository.to_string()); @@ -323,7 +336,7 @@ fn run(opt: Opt) -> Result<()> { .unwrap_or(json!({})); update_params_from_fields(&fields, &mut params); update_value_from_fields(fields, &mut record); - xrpc_client.post("com.atproto.repoPutRecord", Some(params), record)? + xrpc_client.post("com.atproto.repoPutRecord", Some(params), Some(record))? } Command::Delete { uri } => { params.insert("did".to_string(), uri.repository.to_string()); @@ -335,7 +348,7 @@ fn run(opt: Opt) -> Result<()> { "rkey".to_string(), uri.record.ok_or(anyhow!("record key required"))?, ); - xrpc_client.post("com.atproto.repoDeleteRecord", Some(params), json!({}))? + xrpc_client.post("com.atproto.repoDeleteRecord", Some(params), None)? } Command::Xrpc { method, @@ -346,7 +359,7 @@ fn run(opt: Opt) -> Result<()> { let body = value_from_fields(fields); match method { XrpcMethod::Get => xrpc_client.get(&nsid, Some(params))?, - XrpcMethod::Post => xrpc_client.post(&nsid, Some(params), body)?, + XrpcMethod::Post => xrpc_client.post(&nsid, Some(params), Some(body))?, } } Command::Account { @@ -359,28 +372,28 @@ fn run(opt: Opt) -> Result<()> { } => xrpc_client.post( "com.atproto.createAccount", None, - json!({ + Some(json!({ "email": email, "username": username, "password": password, - }), + })), )?, Command::Account { cmd: AccountCommand::Login { username, password }, } => xrpc_client.post( "com.atproto.createSession", None, - json!({ + Some(json!({ "username": username, "password": password, - }), + })), )?, Command::Account { cmd: AccountCommand::Logout, - } => xrpc_client.post("com.atproto.deleteSession", None, json!({}))?, + } => xrpc_client.post("com.atproto.deleteSession", None, None)?, Command::Account { cmd: AccountCommand::Delete, - } => xrpc_client.post("com.atproto.deleteAccount", None, json!({}))?, + } => xrpc_client.post("com.atproto.deleteAccount", None, None)?, Command::Account { cmd: AccountCommand::Info, } => xrpc_client.get("com.atproto.getAccount", None)?, @@ -453,9 +466,9 @@ fn run(opt: Opt) -> Result<()> { xrpc_client.post( "com.atproto.repoCreateRecord", Some(params), - json!({ + Some(json!({ "text": text, - }), + })), )? } Command::Bsky { @@ -469,10 +482,10 @@ fn run(opt: Opt) -> Result<()> { xrpc_client.post( "com.atproto.repoCreateRecord", Some(params), - json!({ + Some(json!({ "subject": uri.to_string(), // TODO: "createdAt": now_timestamp(), - }), + })), )? } Command::Bsky { @@ -486,10 +499,10 @@ fn run(opt: Opt) -> Result<()> { xrpc_client.post( "com.atproto.repoCreateRecord", Some(params), - json!({ + Some(json!({ "subject": uri.to_string(), // TODO: "createdAt": now_timestamp(), - }), + })), )? } Command::Bsky { @@ -503,16 +516,20 @@ fn run(opt: Opt) -> Result<()> { xrpc_client.post( "com.atproto.repoCreateRecord", Some(params), - json!({ + Some(json!({ "subject": uri.to_string(), // TODO: "createdAt": now_timestamp(), - }), + })), )? } Command::Bsky { cmd: BskyCommand::Profile { name }, } => { - params.insert("name".to_string(), name.to_string()); + let name = name + .map(|v| v.to_string()) + .or(jwt_did) + .ok_or(anyhow!("expected a name, or self via auth token"))?; + params.insert("user".to_string(), name.to_string()); xrpc_client.get("app.bsky.getProfile", Some(params))? } Command::Bsky { diff --git a/adenosine-cli/src/lib.rs b/adenosine-cli/src/lib.rs index 6d953ca..2d3cc14 100644 --- a/adenosine-cli/src/lib.rs +++ b/adenosine-cli/src/lib.rs @@ -105,15 +105,19 @@ impl XrpcClient { &self, nsid: &str, params: Option>, - body: Value, + body: Option, ) -> Result> { let params: HashMap = params.unwrap_or(HashMap::new()); - let res = self + let mut req = self .http_client .post(format!("{}/xrpc/{}", self.host, nsid)) - .query(¶ms) - .json(&body) - .send()?; + .query(¶ms); + req = if let Some(b) = body { + req.json(&b) + } else { + req + }; + let res = req.send()?; if res.status() == 400 { let val: Value = res.json()?; return Err(anyhow!( @@ -122,7 +126,11 @@ impl XrpcClient { )); } let res = res.error_for_status()?; - Ok(res.json()?) + if res.content_length() == Some(0) { + Ok(None) + } else { + Ok(res.json()?) + } } pub fn post_cbor_from_reader( @@ -195,11 +203,16 @@ impl FromStr for ArgField { fn from_str(s: &str) -> Result { lazy_static! { - static ref FIELD_RE: Regex = Regex::new(r"^([a-zA-Z_]+)=(=?)(.*)$").unwrap(); + static ref FIELD_RE: Regex = Regex::new(r"^([a-zA-Z_]+)=(=)?(.*)$").unwrap(); } if let Some(captures) = FIELD_RE.captures(s) { let key = captures[1].to_string(); - let val = Value::from_str(&captures[3])?; + let val = + Value::from_str(&captures[3]).unwrap_or(Value::String(captures[3].to_string())); + let val = match val { + Value::String(s) if s.is_empty() => Value::Null, + _ => val, + }; if captures.get(2).is_some() { Ok(ArgField::Query(key, val)) } else { @@ -211,6 +224,37 @@ impl FromStr for ArgField { } } +#[test] +fn test_argfield() { + use serde_json::json; + assert_eq!( + ArgField::from_str("a=3").unwrap(), + ArgField::Body("a".to_string(), json!(3)), + ); + assert_eq!( + ArgField::from_str("a==3").unwrap(), + ArgField::Query("a".to_string(), json!(3)), + ); + assert_eq!( + ArgField::from_str("cream==\"something\"").unwrap(), + ArgField::Query("cream".to_string(), Value::String("something".to_string())) + ); + assert_eq!( + ArgField::from_str("cream==something").unwrap(), + ArgField::Query("cream".to_string(), Value::String("something".to_string())) + ); + assert_eq!( + ArgField::from_str("cream=").unwrap(), + ArgField::Body("cream".to_string(), Value::Null), + ); + + assert!(ArgField::from_str("a").is_err()); + assert!(ArgField::from_str("").is_err()); + assert!(ArgField::from_str("asdf.fee").is_err()); + + assert!(ArgField::from_str("text=\"other value\"").is_ok()); +} + // TODO: what should type signature actually be here... pub fn update_params_from_fields(fields: &[ArgField], params: &mut HashMap) { for f in fields.iter() { -- cgit v1.2.3