aboutsummaryrefslogtreecommitdiffstats
path: root/adenosine-cli
diff options
context:
space:
mode:
authorBryan Newbold <bnewbold@robocracy.org>2022-10-28 00:09:57 -0700
committerBryan Newbold <bnewbold@robocracy.org>2022-10-28 00:19:32 -0700
commit19379222b7b0003bd0538fdd906ee87a9220ddca (patch)
tree44422c8e2ecd596487f963a6ecf52aeb6a4321cc /adenosine-cli
parent5bb9fd92bbaea2d9f3719f954aff12e7d7384fea (diff)
downloadadenosine-19379222b7b0003bd0538fdd906ee87a9220ddca.tar.gz
adenosine-19379222b7b0003bd0538fdd906ee87a9220ddca.zip
cli: bug fixes for creation, etc
Diffstat (limited to 'adenosine-cli')
-rw-r--r--adenosine-cli/src/bin/adenosine.rs65
-rw-r--r--adenosine-cli/src/lib.rs60
2 files changed, 93 insertions, 32 deletions
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<DidOrHost> },
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<HashMap<String, String>>,
- body: Value,
+ body: Option<Value>,
) -> Result<Option<Value>> {
let params: HashMap<String, String> = params.unwrap_or(HashMap::new());
- let res = self
+ let mut req = self
.http_client
.post(format!("{}/xrpc/{}", self.host, nsid))
- .query(&params)
- .json(&body)
- .send()?;
+ .query(&params);
+ 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<R: std::io::Read>(
@@ -195,11 +203,16 @@ impl FromStr for ArgField {
fn from_str(s: &str) -> Result<Self, Self::Err> {
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<String, String>) {
for f in fields.iter() {