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

Commit

Permalink
Implementation of single and bulk deletion from KV (#452)
Browse files Browse the repository at this point in the history
  • Loading branch information
gabbifish authored Aug 21, 2019
1 parent df8219a commit 408c34e
Show file tree
Hide file tree
Showing 5 changed files with 156 additions and 32 deletions.
66 changes: 66 additions & 0 deletions src/commands/kv/delete_bulk.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
extern crate base64;

use cloudflare::framework::apiclient::ApiClient;
use walkdir::WalkDir;

use std::fs;
use std::fs::metadata;
use std::path::Path;

use cloudflare::endpoints::workerskv::delete_bulk::DeleteBulk;
use failure::bail;

use crate::terminal::message;

pub fn delete_bulk(namespace_id: &str, filename: &Path) -> Result<(), failure::Error> {
let client = super::api_client()?;
let account_id = super::account_id()?;

// If the provided argument for delete_bulk is a json file, parse it
// and delete its listed keys. If the argument is a directory, delete key-value
// pairs where keys are the relative pathnames of files in the directory.
let mut data;
let keys: Result<Vec<String>, failure::Error> = match metadata(filename) {
Ok(ref file_type) if file_type.is_file() => {
data = fs::read_to_string(filename)?;
Ok(serde_json::from_str(&data)?)
}
Ok(ref file_type) if file_type.is_dir() => parse_directory(filename),
Ok(_file_type) => {
// any other file types (namely, symlinks)
bail!(
"{} should be a file or directory, but is a symlink",
filename.display()
)
}
Err(e) => bail!(e),
};

let response = client.request(&DeleteBulk {
account_identifier: &account_id,
namespace_identifier: namespace_id,
bulk_keys: keys?,
});

match response {
Ok(_success) => message::success("Success"),
Err(e) => super::print_error(e),
}

Ok(())
}

fn parse_directory(directory: &Path) -> Result<Vec<String>, failure::Error> {
let mut delete_vec: Vec<String> = Vec::new();
for entry in WalkDir::new(directory) {
let entry = entry.unwrap();
let path = entry.path();
if path.is_file() {
let key = super::generate_key(path, directory)?;

message::working(&format!("Deleting {}...", key.clone()));
delete_vec.push(key);
}
}
Ok(delete_vec)
}
25 changes: 25 additions & 0 deletions src/commands/kv/delete_key.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
use cloudflare::endpoints::workerskv::delete_key::DeleteKey;
use cloudflare::framework::apiclient::ApiClient;

use crate::terminal::message;

pub fn delete_key(id: &str, key: &str) -> Result<(), failure::Error> {
let client = super::api_client()?;
let account_id = super::account_id()?;

let msg = format!("Deleting key \"{}\"", key);
message::working(&msg);

let response = client.request(&DeleteKey {
account_identifier: &account_id,
namespace_identifier: id,
key: key, // this is url encoded within cloudflare-rs
});

match response {
Ok(_success) => message::success("Success"),
Err(e) => super::print_error(e),
}

Ok(())
}
35 changes: 34 additions & 1 deletion src/commands/kv/mod.rs
Original file line number Diff line number Diff line change
@@ -1,12 +1,16 @@
use std::ffi::OsString;
use std::path::Path;

use cloudflare::framework::auth::Credentials;
use cloudflare::framework::response::ApiFailure;

use cloudflare::framework::HttpApiClient;

use crate::settings;
use crate::terminal::message;

mod create_namespace;
mod delete_bulk;
mod delete_key;
mod delete_namespace;
mod list_namespaces;
mod read_key;
Expand All @@ -15,6 +19,8 @@ mod write_bulk;
mod write_key;

pub use create_namespace::create_namespace;
pub use delete_bulk::delete_bulk;
pub use delete_key::delete_key;
pub use delete_namespace::delete_namespace;
pub use list_namespaces::list_namespaces;
pub use read_key::read_key;
Expand Down Expand Up @@ -72,3 +78,30 @@ fn help(error_code: u16) -> &'static str {
_ => "",
}
}

// Courtesy of Steve Kalabnik's PoC :) Used for bulk operations (write, delete)
fn generate_key(path: &Path, directory: &Path) -> Result<String, failure::Error> {
let path = path.strip_prefix(directory).unwrap();

// next, we have to re-build the paths: if we're on Windows, we have paths with
// `\` as separators. But we want to use `/` as separators. Because that's how URLs
// work.
let mut path_with_forward_slash = OsString::new();

for (i, component) in path.components().enumerate() {
// we don't want a leading `/`, so skip that
if i > 0 {
path_with_forward_slash.push("/");
}

path_with_forward_slash.push(component);
}

// if we have a non-utf8 path here, it will fail, but that's not realistically going to happen
let path = path_with_forward_slash.to_str().expect(&format!(
"found a non-UTF-8 path, {:?}",
path_with_forward_slash
));

Ok(path.to_string())
}
30 changes: 1 addition & 29 deletions src/commands/kv/write_bulk.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@ extern crate base64;
use cloudflare::framework::apiclient::ApiClient;
use walkdir::WalkDir;

use std::ffi::OsString;
use std::fs;
use std::fs::metadata;
use std::path::Path;
Expand Down Expand Up @@ -59,7 +58,7 @@ fn parse_directory(directory: &Path) -> Result<Vec<KeyValuePair>, failure::Error
let entry = entry.unwrap();
let path = entry.path();
if path.is_file() {
let key = generate_key(path, directory)?;
let key = super::generate_key(path, directory)?;

let value = std::fs::read(path)?;

Expand All @@ -77,30 +76,3 @@ fn parse_directory(directory: &Path) -> Result<Vec<KeyValuePair>, failure::Error
}
Ok(upload_vec)
}

// Courtesy of Steve Kalabnik's PoC :)
fn generate_key(path: &Path, directory: &Path) -> Result<String, failure::Error> {
let path = path.strip_prefix(directory).unwrap();

// next, we have to re-build the paths: if we're on Windows, we have paths with
// `\` as separators. But we want to use `/` as separators. Because that's how URLs
// work.
let mut path_with_forward_slash = OsString::new();

for (i, component) in path.components().enumerate() {
// we don't want a leading `/`, so skip that
if i > 0 {
path_with_forward_slash.push("/");
}

path_with_forward_slash.push(component);
}

// if we have a non-utf8 path here, it will fail, but that's not realistically going to happen
let path = path_with_forward_slash.to_str().expect(&format!(
"found a non-UTF-8 path, {:?}",
path_with_forward_slash
));

Ok(path.to_string())
}
32 changes: 30 additions & 2 deletions src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -136,6 +136,24 @@ fn run() -> Result<(), failure::Error> {
.index(2),
)
)
.subcommand(
SubCommand::with_name("delete-key")
.arg(
Arg::with_name("id")
)
.arg(
Arg::with_name("key")
)
)
.subcommand(
SubCommand::with_name("delete-bulk")
.arg(
Arg::with_name("id")
)
.arg(
Arg::with_name("path")
)
)
)
.subcommand(
SubCommand::with_name("generate")
Expand Down Expand Up @@ -380,8 +398,18 @@ fn run() -> Result<(), failure::Error> {
}
("write-bulk", Some(write_bulk_matches)) => {
let id = write_bulk_matches.value_of("id").unwrap();
let filename = write_bulk_matches.value_of("path").unwrap();
commands::kv::write_bulk(id, Path::new(filename))?;
let path = write_bulk_matches.value_of("path").unwrap();
commands::kv::write_bulk(id, Path::new(path))?;
}
("delete-key", Some(delete_matches)) => {
let id = delete_matches.value_of("id").unwrap();
let key = delete_matches.value_of("key").unwrap();
commands::kv::delete_key(id, key)?;
}
("delete-bulk", Some(delete_matches)) => {
let id = delete_matches.value_of("id").unwrap();
let path = delete_matches.value_of("path").unwrap();
commands::kv::delete_bulk(id, Path::new(path))?;
}
("", None) => message::warn("kv expects a subcommand"),
_ => unreachable!(),
Expand Down

0 comments on commit 408c34e

Please sign in to comment.