Skip to content

CLI should use Agent in requests - get #986 #1043

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 4 commits into
base: develop
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,12 @@ By far most changes relate to `atomic-server`, so if not specified, assume the c
**Changes to JS assets (including the front-end and JS libraries) are not shown here**, but in [`/browser/CHANGELOG`](/browser/CHANGELOG.md).
See [STATUS.md](server/STATUS.md) to learn more about which features will remain stable.

## UNRELEASED

- CLI should use Agent in requests - get #986
- Search endpoint throws error for websocket requests #1047
- Fix search in CLI / atomic_lib #958

## [v0.40.2]

- fix property sort order when importing + add tests #980
Expand Down
14 changes: 0 additions & 14 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

16 changes: 16 additions & 0 deletions cli/src/get.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
use crate::{print::print_resource, Context, SerializeOptions};
use atomic_lib::{errors::AtomicResult, Storelike};

pub fn get_resource(
context: &mut Context,
subject: &str,
serialize: &SerializeOptions,
) -> AtomicResult<()> {
context.read_config();

let store = &mut context.store;
let fetched = store.fetch_resource(subject, store.get_default_agent().ok().as_ref())?;
print_resource(context, &fetched, serialize)?;

Ok(())
}
31 changes: 23 additions & 8 deletions cli/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,8 @@ use dirs::home_dir;
use std::{cell::RefCell, path::PathBuf, sync::Mutex};

mod commit;
mod get;
mod new;
mod path;
mod print;
mod search;

Expand Down Expand Up @@ -45,9 +45,9 @@ enum Commands {
Visit https://docs.atomicdata.dev/core/paths.html for more info about paths. \
")]
Get {
/// The subject URL, shortname or path to be fetched
#[arg(required = true, num_args = 1..)]
path: Vec<String>,
/// The subject URL
#[arg(required = true)]
subject: String,

/// Serialization format
#[arg(long, value_enum, default_value = "pretty")]
Expand Down Expand Up @@ -98,18 +98,27 @@ enum Commands {
/// The search query
#[arg(required = true)]
query: String,
/// Subject URL of the parent Resource to filter by
#[arg(long)]
parent: Option<String>,
/// Serialization format
#[arg(long, value_enum, default_value = "pretty")]
as_: SerializeOptions,
},
/// List all bookmarks
List,
/// Validates the store
#[command(hide = true)]
Validate,
/// Print the current agent
Agent,
}

#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, ValueEnum)]
pub enum SerializeOptions {
Pretty,
Json,
JsonAd,
NTriples,
}

Expand All @@ -118,6 +127,7 @@ impl Into<Format> for SerializeOptions {
match self {
SerializeOptions::Pretty => Format::Pretty,
SerializeOptions::Json => Format::Json,
SerializeOptions::JsonAd => Format::JsonAd,
SerializeOptions::NTriples => Format::NTriples,
}
}
Expand Down Expand Up @@ -151,6 +161,7 @@ impl Context {
name: None,
public_key: generate_public_key(&write_ctx.private_key).public,
});
self.store.set_server_url(&write_ctx.server);
write_ctx
}
}
Expand Down Expand Up @@ -238,8 +249,8 @@ fn exec_command(context: &mut Context) -> AtomicResult<()> {
return Err("Feature not available. Compile with `native` feature.".into());
}
}
Commands::Get { path, as_ } => {
path::get_path(context, &path, &as_)?;
Commands::Get { subject, as_ } => {
get::get_resource(context, &subject, &as_)?;
}
Commands::List => {
list(context);
Expand All @@ -257,12 +268,16 @@ fn exec_command(context: &mut Context) -> AtomicResult<()> {
} => {
commit::set(context, &subject, &property, &value)?;
}
Commands::Search { query } => {
search::search(context, query)?;
Commands::Search { query, parent, as_ } => {
search::search(context, query, parent, &as_)?;
}
Commands::Validate => {
validate(context);
}
Commands::Agent => {
let agent = context.read_config();
println!("{}", agent.agent);
}
};
Ok(())
}
Expand Down
36 changes: 0 additions & 36 deletions cli/src/path.rs

This file was deleted.

34 changes: 16 additions & 18 deletions cli/src/search.rs
Original file line number Diff line number Diff line change
@@ -1,29 +1,27 @@
use atomic_lib::{errors::AtomicResult, urls, Storelike};
use atomic_lib::{errors::AtomicResult, Storelike};

pub fn search(context: &crate::Context, query: String) -> AtomicResult<()> {
use crate::print::print_resource;

pub fn search(
context: &crate::Context,
query: String,
parent: Option<String>,
serialize: &crate::SerializeOptions,
) -> AtomicResult<()> {
context.read_config();
let opts = atomic_lib::client::search::SearchOpts {
limit: Some(10),
include: Some(true),
parents: Some(vec![parent.unwrap_or_default()]),
..Default::default()
};
let subject = atomic_lib::client::search::build_search_subject(
&context.read_config().server,
&query,
opts,
);
let resource = context.store.get_resource(&subject)?;
let members = resource
.get(urls::ENDPOINT_RESULTS)
.expect("No members?")
.to_subjects(None)
.unwrap();
if members.is_empty() {
println!("No results found.");
println!("URL: {}", subject);
let resources = context.store.search(&query, opts)?;
if resources.is_empty() {
println!("No results found for query: {}", query);
return Ok(());
} else {
for member in members {
println!("{}", member);
for member in resources {
print_resource(context, &member, serialize)?;
}
}
Ok(())
Expand Down
2 changes: 1 addition & 1 deletion lib/defaults/default_store.json
Original file line number Diff line number Diff line change
Expand Up @@ -710,7 +710,7 @@
},
{
"@id": "https://atomicdata.dev/properties/published-at",
"https://atomicdata.dev/properties/datatype": "https://atomicdata.dev/datatypes/timestamp",
"https://atomicdata.dev/properties/datatype": "https://atomicdata.dev/datatypes/date",
"https://atomicdata.dev/properties/description": "DateTime at which an item is made public.",
"https://atomicdata.dev/properties/isA": [
"https://atomicdata.dev/classes/Property"
Expand Down
25 changes: 19 additions & 6 deletions lib/src/agents.rs
Original file line number Diff line number Diff line change
Expand Up @@ -85,7 +85,7 @@ impl Agent {
pub fn new(name: Option<&str>, store: &impl Storelike) -> AtomicResult<Agent> {
let keypair = generate_keypair()?;

Ok(Agent::new_from_private_key(name, store, &keypair.private))
Agent::new_from_private_key(name, store, &keypair.private)
}

/// Creates a new Agent on this server, using the server's Server URL.
Expand All @@ -94,16 +94,16 @@ impl Agent {
name: Option<&str>,
store: &impl Storelike,
private_key: &str,
) -> Agent {
) -> AtomicResult<Agent> {
let keypair = generate_public_key(private_key);

Agent {
Ok(Agent {
private_key: Some(keypair.private),
public_key: keypair.public.clone(),
subject: format!("{}/agents/{}", store.get_server_url(), keypair.public),
subject: format!("{}/agents/{}", store.get_server_url()?, keypair.public),
name: name.map(|x| x.to_owned()),
created_at: crate::utils::now(),
}
})
}

/// Creates a new Agent on this server, using the server's Server URL.
Expand All @@ -114,7 +114,7 @@ impl Agent {
Ok(Agent {
private_key: None,
public_key: public_key.into(),
subject: format!("{}/agents/{}", store.get_server_url(), public_key),
subject: format!("{}/agents/{}", store.get_server_url()?, public_key),
name: None,
created_at: crate::utils::now(),
})
Expand Down Expand Up @@ -211,6 +211,19 @@ pub fn verify_public_key(public_key: &str) -> AtomicResult<()> {
Ok(())
}

impl From<Agent> for ForAgent {
fn from(agent: Agent) -> Self {
agent.subject.into()
}
}

impl<'a> From<&'a Agent> for ForAgent {
fn from(agent: &'a Agent) -> Self {
let subject: String = agent.subject.clone();
subject.into()
}
}

#[cfg(test)]
mod test {
#[cfg(test)]
Expand Down
33 changes: 24 additions & 9 deletions lib/src/client/helpers.rs
Original file line number Diff line number Diff line change
Expand Up @@ -54,18 +54,33 @@ pub fn fetch_body(
if !url.starts_with("http") {
return Err(format!("Could not fetch url '{}', must start with http.", url).into());
}
if let Some(agent) = client_agent {
get_authentication_headers(url, agent)?;
}

let agent = ureq::builder()
let client = ureq::builder()
.timeout(std::time::Duration::from_secs(2))
.build();
let resp = agent
.get(url)
.set("Accept", content_type)
.call()
.map_err(|e| format!("Error when server tried fetching {} : {}", url, e))?;

let mut req = client.get(url);
if let Some(agent) = client_agent {
let headers = get_authentication_headers(url, agent)?;
for (key, value) in headers {
req = req.set(key.as_str(), value.as_str());
}
}

let resp = match req.set("Accept", content_type).call() {
Ok(response) => response,
Err(ureq::Error::Status(status, response)) => {
let body = response
.into_string()
.unwrap_or_else(|_| "<failed to read response body>".to_string());
return Err(format!(
"Error when fetching {}: Status: {}. Body: {}",
url, status, body
)
.into());
}
Err(e) => return Err(format!("Error when fetching {}: {}", url, e).into()),
};
let status = resp.status();
let body = resp
.into_string()
Expand Down
Loading
Loading