Skip to content
This repository has been archived by the owner on Aug 3, 2023. It is now read-only.

Commit

Permalink
Add individual write subcommand for Workers KV (#458)
Browse files Browse the repository at this point in the history
Add logic for setting individual key-value pair
  • Loading branch information
gabbifish authored Aug 21, 2019
1 parent 8f3a14e commit a1ff27a
Show file tree
Hide file tree
Showing 5 changed files with 122 additions and 0 deletions.
1 change: 1 addition & 0 deletions Cargo.lock

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

1 change: 1 addition & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@ exitfailure = "0.5.1"
prettytable-rs = "0.8.0"
notify = "4.0.12"
ws = "0.9.0"
url = "2.1.0"
percent-encoding = "1.0.1"

[dev-dependencies]
Expand Down
2 changes: 2 additions & 0 deletions src/commands/kv/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11,12 +11,14 @@ mod delete_namespace;
mod list_namespaces;
mod read_key;
mod rename_namespace;
mod write_key;

pub use create_namespace::create_namespace;
pub use delete_namespace::delete_namespace;
pub use list_namespaces::list_namespaces;
pub use read_key::read_key;
pub use rename_namespace::rename_namespace;
pub use write_key::write_key;

fn api_client() -> Result<HttpApiClient, failure::Error> {
let user = settings::global_user::GlobalUser::new()?;
Expand Down
67 changes: 67 additions & 0 deletions src/commands/kv/write_key.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
// todo(gabbi): This file should use cloudflare-rs instead of our http::auth_client
// when https://github.com/cloudflare/cloudflare-rs/issues/26 is handled (this is
// because the SET key request body is not json--it is the raw value).

use std::fs;

use cloudflare::framework::response::ApiFailure;
use url::Url;

use crate::http;
use crate::settings::global_user::GlobalUser;
use crate::settings::project::Project;
use crate::terminal::message;

pub fn write_key(
project: &Project,
user: &GlobalUser,
id: &str,
key: &str,
value: &str,
is_file: bool,
expiration: Option<&str>,
expiration_ttl: Option<&str>,
) -> Result<(), failure::Error> {
let api_endpoint = format!(
"https://api.cloudflare.com/client/v4/accounts/{}/storage/kv/namespaces/{}/values/{}",
project.account_id, id, key
);

// Add expiration and expiration_ttl query options as necessary.
let mut query_params: Vec<(&str, &str)> = vec![];
match expiration {
Some(exp) => query_params.push(("expiration", exp)),
None => (),
}
match expiration_ttl {
Some(ttl) => query_params.push(("expiration_ttl", ttl)),
None => (),
}
let url = Url::parse_with_params(&api_endpoint, query_params);

// If is_file is true, overwrite value to be the contents of the given
// filename in the 'value' arg.
let mut body_text: String;
if is_file {
body_text = fs::read_to_string(value)?;
} else {
body_text = value.to_string();
}

let client = http::auth_client(user);

let url_into_str = url?.into_string();
let mut res = client.put(&url_into_str).body(body_text).send()?;

if res.status().is_success() {
message::success("Success")
} else {
// This is logic pulled from cloudflare-rs for pretty error formatting right now;
// it will be redundant when we switch to using cloudflare-rs for all API requests.
let parsed = res.json();
let errors = parsed.unwrap_or_default();
super::print_error(ApiFailure::Error(res.status(), errors));
}

Ok(())
}
51 changes: 51 additions & 0 deletions src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -86,6 +86,41 @@ fn run() -> Result<(), failure::Error> {
Arg::with_name("key")
)
)
.subcommand(
SubCommand::with_name("write-key")
.arg(
Arg::with_name("id")
)
.arg(
Arg::with_name("key")
)
.arg(
Arg::with_name("value")
)
.arg(
Arg::with_name("expiration")
.short("e")
.long("expiration")
.takes_value(true)
.value_name("SECONDS")
.help("the time, measured in number of seconds since the UNIX epoch, at which the entries should expire"),
)
.arg(
Arg::with_name("expiration-ttl")
.short("t")
.long("ttl")
.value_name("SECONDS")
.takes_value(true)
.help("the number of seconds for which the entries should be visible before they expire. At least 60"),
)
.arg(
Arg::with_name("file")
.short("f")
.long("file")
.takes_value(false)
.help("the value passed in is a filename; open and upload its contents"),
)
)
)
.subcommand(
SubCommand::with_name("generate")
Expand Down Expand Up @@ -312,8 +347,24 @@ fn run() -> Result<(), failure::Error> {
let user = settings::global_user::GlobalUser::new()?;
let id = read_key_matches.value_of("id").unwrap();
let key = read_key_matches.value_of("key").unwrap();

commands::kv::read_key(&project, &user, id, key)?;
}
("write-key", Some(write_key_matches)) => {
let project = settings::project::Project::new()?;
let user = settings::global_user::GlobalUser::new()?;
let id = write_key_matches.value_of("id").unwrap();
let key = write_key_matches.value_of("key").unwrap();
let value = write_key_matches.value_of("value").unwrap();
let is_file = match write_key_matches.occurrences_of("file") {
1 => true,
_ => false,
};
let expiration = write_key_matches.value_of("expiration");
let ttl = write_key_matches.value_of("expiration-ttl");

commands::kv::write_key(&project, &user, id, key, value, is_file, expiration, ttl)?;
}
("", None) => message::warn("kv expects a subcommand"),
_ => unreachable!(),
}
Expand Down

0 comments on commit a1ff27a

Please sign in to comment.