1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
use std::collections::HashSet;
use cloudflare::framework::response::ApiFailure;
use anyhow::Result;
use percent_encoding::{utf8_percent_encode, AsciiSet, CONTROLS};
use crate::http;
use crate::settings::toml::Target;
pub mod bulk;
pub mod key;
pub mod namespace;
pub fn format_error(e: ApiFailure) -> String {
http::format_error(e, Some(&kv_help))
}
fn kv_help(error_code: u16) -> &'static str {
match error_code {
7003 | 7000 => {
"Your configuration file is likely missing the field \"account_id\", which is required to write to Workers KV."
}
10010 | 10011 | 10012 | 10013 | 10014 | 10018 => {
"Run `wrangler kv:namespace list` to see your existing namespaces with IDs"
}
10009 => "Run `wrangler kv:key list` to see your existing keys", 10022 | 10024 | 10030 => "See documentation",
10021 | 10035 | 10038 => "Consider moving this namespace",
_ => "",
}
}
fn check_duplicate_namespaces(target: &Target) -> bool {
let mut binding_names: HashSet<String> = HashSet::new();
for namespace in &target.kv_namespaces {
if binding_names.contains(&namespace.binding) {
return true;
} else {
binding_names.insert(namespace.binding.clone());
}
}
false
}
pub fn get_namespace_id(target: &Target, binding: &str) -> Result<String> {
if check_duplicate_namespaces(target) {
anyhow::bail!(
"Namespace binding \"{}\" is duplicated in \"{}\"",
binding,
target.name
)
}
for namespace in &target.kv_namespaces {
if namespace.binding == binding {
return Ok(namespace.id.to_string());
}
}
anyhow::bail!(
"Namespace binding \"{}\" not found in \"{}\"",
binding,
target.name
)
}
const KV_ASCII_SET: &AsciiSet = &CONTROLS.add(b'/');
fn url_encode_key(key: &str) -> String {
utf8_percent_encode(key, KV_ASCII_SET).to_string()
}
#[cfg(test)]
mod tests {
use crate::commands::kv;
use crate::settings::toml::{KvNamespace, Target, TargetType};
#[test]
fn it_can_detect_duplicate_bindings() {
let target_with_dup_kv_bindings = Target {
account_id: None.into(),
kv_namespaces: vec![
KvNamespace {
id: "fake".to_string(),
binding: "KV".to_string(),
},
KvNamespace {
id: "fake".to_string(),
binding: "KV".to_string(),
},
],
r2_buckets: Vec::new(),
durable_objects: None,
migrations: None,
name: "test-target".to_string(),
target_type: TargetType::Webpack,
webpack_config: None,
site: None,
vars: None,
text_blobs: None,
build: None,
wasm_modules: None,
usage_model: None,
compatibility_date: None,
compatibility_flags: Vec::new(),
};
assert!(kv::get_namespace_id(&target_with_dup_kv_bindings, "").is_err());
}
#[test]
fn it_encodes_slash() {
assert_eq!(kv::url_encode_key("/slash"), "%2Fslash");
}
#[test]
fn it_doesnt_double_encode_slash() {
assert_eq!(kv::url_encode_key("%2Fslash"), "%2Fslash");
}
}