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

Commit

Permalink
Merge pull request #215 from cloudflare/feat-add-kv_namespaces-bindings
Browse files Browse the repository at this point in the history
Add kv_namespaces in metadata
  • Loading branch information
xtuc authored Jul 3, 2019
2 parents ec7698a + d7fbd78 commit 35f7d0e
Show file tree
Hide file tree
Showing 6 changed files with 78 additions and 50 deletions.
3 changes: 3 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -143,6 +143,9 @@ There are two types of configuration that `wrangler` uses: global user and per p
This key is optional if you are using a workers.dev subdomain and is only required for `publish --release`.
- `webpack_config`: This is the path to the webpack configuration file for your worker. This is optional and
defaults to `webpack.config.js`
- `kv-namespaces`: Bind kv namespaces to your worker. Must be created in the dashboard before. An array of:
- `binding`: name that will be used to bind the kv-namespace to your script, in JavaScript.
- `id`: Identifer of the namespace (can be found on the Cloudflare dashboard).

## ⚓ Installation

Expand Down
61 changes: 51 additions & 10 deletions src/commands/build/wranglerjs/bundle.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,12 +6,13 @@ use std::fs::File;
use std::io::prelude::*;
use std::path::{Path, PathBuf};

use crate::terminal::message;
use log::info;

use crate::commands::build::wranglerjs::output::WranglerjsOutput;
use crate::settings::binding::Binding;
use crate::settings::metadata;
use crate::terminal::message;
use crate::settings::project::KvNamespace;

// Directory where we should write the {Bundle}. It represents the built
// artifact.
Expand All @@ -34,7 +35,11 @@ impl Bundle {
Bundle { out }
}

pub fn write(&self, wranglerjs_output: &WranglerjsOutput) -> Result<(), failure::Error> {
pub fn write(
&self,
wranglerjs_output: &WranglerjsOutput,
kv_namespaces: Vec<KvNamespace>,
) -> Result<(), failure::Error> {
let bundle_path = Path::new(&self.out);
if !bundle_path.exists() {
fs::create_dir(bundle_path)?;
Expand All @@ -52,7 +57,7 @@ impl Bundle {

script_file.write_all(script.as_bytes())?;

let metadata = create_metadata(self).expect("could not create metadata");
let metadata = create_metadata(self, &kv_namespaces).expect("could not create metadata");
let mut metadata_file = File::create(self.metadata_path())?;
metadata_file.write_all(metadata.as_bytes())?;

Expand Down Expand Up @@ -112,13 +117,23 @@ pub fn create_prologue() -> String {
}

// This metadata describe the bindings on the Worker.
fn create_metadata(bundle: &Bundle) -> Result<String, serde_json::error::Error> {
fn create_metadata(
bundle: &Bundle,
kv_namespaces: &Vec<KvNamespace>,
) -> Result<String, serde_json::error::Error> {
let mut bindings = vec![];

if bundle.has_wasm() {
bindings.push(Binding::new_wasm_module(
bundle.get_wasm_binding(),
bundle.get_wasm_binding(),
bundle.get_wasm_binding(), // name
bundle.get_wasm_binding(), // part
));
}

for namespace in kv_namespaces {
bindings.push(Binding::new_kv_namespace(
namespace.binding.clone(), // local_binding
namespace.id.clone(), // namespace_id
));
}

Expand Down Expand Up @@ -154,7 +169,7 @@ mod tests {
};
let bundle = Bundle::new_at(out.clone());

bundle.write(&wranglerjs_output).unwrap();
bundle.write(&wranglerjs_output, vec![]).unwrap();
assert!(Path::new(&bundle.metadata_path()).exists());
let contents =
fs::read_to_string(&bundle.metadata_path()).expect("could not read metadata");
Expand All @@ -175,7 +190,7 @@ mod tests {
};
let bundle = Bundle::new_at(out.clone());

bundle.write(&wranglerjs_output).unwrap();
bundle.write(&wranglerjs_output, vec![]).unwrap();
assert!(Path::new(&bundle.script_path()).exists());
assert!(!Path::new(&bundle.wasm_path()).exists());

Expand All @@ -193,7 +208,7 @@ mod tests {
};
let bundle = Bundle::new_at(out.clone());

bundle.write(&wranglerjs_output).unwrap();
bundle.write(&wranglerjs_output, vec![]).unwrap();
assert!(Path::new(&bundle.wasm_path()).exists());
assert!(bundle.has_wasm());

Expand All @@ -211,7 +226,7 @@ mod tests {
};
let bundle = Bundle::new_at(out.clone());

bundle.write(&wranglerjs_output).unwrap();
bundle.write(&wranglerjs_output, vec![]).unwrap();
assert!(Path::new(&bundle.metadata_path()).exists());
let contents =
fs::read_to_string(&bundle.metadata_path()).expect("could not read metadata");
Expand All @@ -224,6 +239,32 @@ mod tests {
cleanup(out);
}

#[test]
fn it_writes_the_bundle_kv_metadata() {
let out = create_temp_dir("it_writes_the_bundle_kv_metadata");
let wranglerjs_output = WranglerjsOutput {
errors: vec![],
script: "".to_string(),
wasm: None,
dist_to_clean: None,
};
let bundle = Bundle::new_at(out.clone());

let kv_namespaces = vec![KvNamespace {
binding: "binding".to_string(),
id: "id".to_string(),
}];

bundle.write(&wranglerjs_output, kv_namespaces).unwrap();
assert!(Path::new(&bundle.metadata_path()).exists());
let contents =
fs::read_to_string(&bundle.metadata_path()).expect("could not read metadata");

assert_eq!(contents, r#"{"body_part":"script","bindings":[{"type":"kv_namespace","name":"binding","namespace_id":"id"}]}"#);

cleanup(out);
}

#[test]
fn it_has_errors() {
let wranglerjs_output = WranglerjsOutput {
Expand Down
8 changes: 7 additions & 1 deletion src/commands/build/wranglerjs/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -45,8 +45,11 @@ pub fn run_build(project: &Project) -> Result<(), failure::Error> {
failure::bail!("Webpack returned an error");
}

let kv_namespaces = project.kv_namespaces.clone().unwrap_or(Vec::new());
let kv_namespaces_len = kv_namespaces.clone().len();

bundle
.write(&wranglerjs_output)
.write(&wranglerjs_output, kv_namespaces)
.expect("could not write bundle to disk");

let mut msg = format!(
Expand All @@ -56,6 +59,9 @@ pub fn run_build(project: &Project) -> Result<(), failure::Error> {
if bundle.has_wasm() {
msg = format!("{} and Wasm size is {}", msg, wranglerjs_output.wasm_size());
}
if kv_namespaces_len > 0 {
msg = format!("{} and has {} kv namespaces", msg, kv_namespaces_len);
}
message::success(&msg);
Ok(())
} else {
Expand Down
38 changes: 0 additions & 38 deletions src/commands/publish/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,6 @@ pub fn publish(user: &GlobalUser, project: &Project, release: bool) -> Result<()

validate_project(project, release)?;
commands::build(&project)?;
create_kv_namespaces(user, &project)?;
publish_script(&user, &project, release)?;
if release {
info!("release mode detected, making a route...");
Expand All @@ -43,43 +42,6 @@ pub fn publish(user: &GlobalUser, project: &Project, release: bool) -> Result<()
Ok(())
}

pub fn create_kv_namespaces(user: &GlobalUser, project: &Project) -> Result<(), failure::Error> {
let kv_addr = format!(
"https://api.cloudflare.com/client/v4/accounts/{}/storage/kv/namespaces",
project.account_id,
);

let client = http::auth_client(user);

if let Some(namespaces) = &project.kv_namespaces {
for namespace in namespaces {
info!("Attempting to create namespace '{}'", namespace);

let mut map = HashMap::new();
map.insert("title", namespace);

let request = client.post(&kv_addr).json(&map).send();

if let Err(error) = request {
// A 400 is returned if the account already owns a namespace with this title.
//
// https://api.cloudflare.com/#workers-kv-namespace-create-a-namespace
match error.status() {
Some(code) if code == 400 => {
info!("Namespace '{}' already exists, continuing.", namespace)
}
_ => {
info!("Error when creating namespace '{}'", namespace);
failure::bail!("⛔ Something went wrong! Error: {}", error)
}
}
}
info!("Namespace '{}' exists now", namespace)
}
}
Ok(())
}

fn publish_script(
user: &GlobalUser,
project: &Project,
Expand Down
8 changes: 8 additions & 0 deletions src/settings/binding.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,10 +5,18 @@ use serde::Serialize;
pub enum Binding {
#[allow(non_camel_case_types)]
wasm_module { name: String, part: String },
#[allow(non_camel_case_types)]
kv_namespace { name: String, namespace_id: String },
}

impl Binding {
pub fn new_wasm_module(name: String, part: String) -> Binding {
Binding::wasm_module { name, part }
}
pub fn new_kv_namespace(local_binding: String, namespace_id: String) -> Binding {
Binding::kv_namespace {
name: local_binding,
namespace_id,
}
}
}
10 changes: 9 additions & 1 deletion src/settings/project.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,14 @@ use log::info;
use config::{Config, Environment, File};
use serde::{Deserialize, Serialize};

#[derive(Deserialize, Serialize, Debug, Clone)]
pub struct KvNamespace {
// name that will be used to bind the JavaScript value to the worker
pub binding: String,
// identifier of the KV namespace
pub id: String,
}

#[derive(Clone, Debug, Deserialize, Serialize)]
pub struct Project {
pub name: String,
Expand All @@ -21,7 +29,7 @@ pub struct Project {
pub route: Option<String>,
pub routes: Option<HashMap<String, String>>,
#[serde(rename = "kv-namespaces")]
pub kv_namespaces: Option<Vec<String>>,
pub kv_namespaces: Option<Vec<KvNamespace>>,
}

#[derive(Clone, Debug, Deserialize, Serialize)]
Expand Down

0 comments on commit 35f7d0e

Please sign in to comment.