Skip to content

Commit dca9cd6

Browse files
Add name method to ObjectId, fixes #760 (#761)
* Add name method to ObjectId, fixes #760 - Fix up worker-sandbox/wrangler.toml to get 'npx wrangler dev' to work (on macOS) - Add SOME_SECRET to .dev.vars to fix /durable/:id endpoint - Add handlers for /hello and /storage endpoints in export_durable_object.rs in order to test ObjectId::name() * Use wasm_bindgen getter attribute to access 'name' property * Add 'equals' method for ObjectId --------- Co-authored-by: Guy Bedford <[email protected]>
1 parent 5125a44 commit dca9cd6

File tree

8 files changed

+115
-20
lines changed

8 files changed

+115
-20
lines changed

worker-sandbox/.dev.vars

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
SOME_SECRET="super secret"

worker-sandbox/Cargo.toml

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,10 @@ license = "Apache-2.0"
88
[package.metadata.release]
99
release = false
1010

11+
# https://github.com/rustwasm/wasm-pack/issues/1247
12+
[package.metadata.wasm-pack.profile.release]
13+
wasm-opt = false
14+
1115
[lib]
1216
crate-type = ["cdylib", "rlib"]
1317
path = "src/lib.rs"

worker-sandbox/src/router.rs

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -452,6 +452,18 @@ pub fn make_router<'a>(data: SomeSharedData) -> Router<'a, SomeSharedData> {
452452
"/durable/auto-response",
453453
handler!(crate::test::auto_response::handle_auto_response),
454454
)
455+
.get_async(
456+
"/durable/hello",
457+
handler!(crate::test::export_durable_object::handle_hello),
458+
)
459+
.get_async(
460+
"/durable/hello-unique",
461+
handler!(crate::test::export_durable_object::handle_hello_unique),
462+
)
463+
.get_async(
464+
"/durable/storage",
465+
handler!(crate::test::export_durable_object::handle_storage),
466+
)
455467
.get_async(
456468
"/sql-counter/*path",
457469
handler!(sql_counter::handle_sql_counter),

worker-sandbox/src/test/durable.rs

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,11 @@ use worker::*;
55
pub async fn basic_test(env: &Env) -> Result<()> {
66
let namespace: ObjectNamespace = env.durable_object("MY_CLASS")?;
77
let id = namespace.id_from_name("A")?;
8+
ensure!(id.name() == Some("A".into()), "Missing name");
9+
ensure!(
10+
namespace.unique_id()?.name().is_none(),
11+
"Expected name property to be absent"
12+
);
813
let bad = env.durable_object("DFSDF_FAKE_BINDING");
914
ensure!(bad.is_err(), "Invalid binding did not raise error");
1015

worker-sandbox/src/test/export_durable_object.rs

Lines changed: 63 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -10,13 +10,26 @@ use crate::ensure;
1010

1111
#[durable_object]
1212
pub struct MyClass {
13+
name: String,
1314
state: State,
1415
number: RefCell<usize>,
1516
}
1617

1718
impl DurableObject for MyClass {
18-
fn new(state: State, _env: Env) -> Self {
19+
fn new(state: State, env: Env) -> Self {
20+
// Check that we can re-derive the expected names.
21+
let namespace = env.durable_object("MY_CLASS").unwrap();
22+
let name = if let Some(name) = state.id().name() {
23+
assert!(state.id() == namespace.id_from_name(&name).unwrap());
24+
name
25+
} else {
26+
let id = state.id().to_string();
27+
assert!(state.id() == namespace.id_from_string(&id).unwrap());
28+
id
29+
};
30+
1931
Self {
32+
name,
2033
state,
2134
number: RefCell::new(0),
2235
}
@@ -25,7 +38,7 @@ impl DurableObject for MyClass {
2538
async fn fetch(&self, req: Request) -> Result<Response> {
2639
let handler = async move {
2740
match req.path().as_str() {
28-
"/hello" => Response::ok("Hello!"),
41+
"/hello" => Response::ok(format!("Hello from {}!", self.name)),
2942
"/storage" => {
3043
let storage = self.state.storage();
3144
let map = [("one".to_string(), 1), ("two".to_string(), 2)]
@@ -140,3 +153,51 @@ impl DurableObject for MyClass {
140153
.or_else(|err| Response::error(err.to_string(), 500))
141154
}
142155
}
156+
157+
// Route handlers to exercise the Durable Object from tests.
158+
#[worker::send]
159+
pub async fn handle_hello(
160+
_req: Request,
161+
env: Env,
162+
_data: crate::SomeSharedData,
163+
) -> Result<Response> {
164+
let name = "your Durable Object";
165+
let namespace = env.durable_object("MY_CLASS")?;
166+
let id = namespace.id_from_name(name)?;
167+
// Same name gives the same ID
168+
assert!(id == namespace.id_from_name(name)?);
169+
170+
// Same name but different namespaces gives different IDs
171+
let namespace2 = env.durable_object("COUNTER")?;
172+
assert!(id != namespace2.id_from_name(name)?);
173+
174+
let stub = id.get_stub()?;
175+
stub.fetch_with_str("https://fake-host/hello").await
176+
}
177+
178+
#[worker::send]
179+
pub async fn handle_hello_unique(
180+
_req: Request,
181+
env: Env,
182+
_data: crate::SomeSharedData,
183+
) -> Result<Response> {
184+
let namespace = env.durable_object("MY_CLASS")?;
185+
let id = namespace.unique_id()?;
186+
// Different unique IDs should never be equal
187+
assert!(id != namespace.unique_id()?);
188+
// Deriving from the string form of the unique ID gives the same ID
189+
assert!(id == namespace.id_from_string(&id.to_string()).unwrap());
190+
let stub = id.get_stub()?;
191+
stub.fetch_with_str("https://fake-host/hello").await
192+
}
193+
194+
#[worker::send]
195+
pub async fn handle_storage(
196+
_req: Request,
197+
env: Env,
198+
_data: crate::SomeSharedData,
199+
) -> Result<Response> {
200+
let namespace = env.durable_object("MY_CLASS")?;
201+
let stub = namespace.id_from_name("singleton")?.get_stub()?;
202+
stub.fetch_with_str("https://fake-host/storage").await
203+
}

worker-sandbox/wrangler.toml

Lines changed: 10 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ type = "javascript"
33
workers_dev = true
44
compatibility_date = "2022-09-12" # required
55
compatibility_flags = ["streams_enable_constructors"]
6+
main = "build/worker/shim.mjs"
67

78
kv_namespaces = [
89
{ binding = "SOME_NAMESPACE", id = "SOME_NAMESPACE", preview_id = "SOME_NAMESPACE" },
@@ -32,6 +33,7 @@ bindings = [
3233
{ name = "AUTO", class_name = "AutoResponseObject" },
3334
{ name = "SQL_COUNTER", class_name = "SqlCounter" },
3435
{ name = "SQL_ITERATOR", class_name = "SqlIterator" },
36+
{ name = "MY_CLASS", class_name = "MyClass" },
3537
]
3638

3739
[[analytics_engine_datasets]]
@@ -52,37 +54,27 @@ queue = "my_queue"
5254
binding = "my_queue"
5355
[[r2_buckets]]
5456
binding = 'EMPTY_BUCKET'
55-
bucket_name = 'empty_bucket'
56-
preview_bucket_name = 'empty_bucket'
57+
bucket_name = 'empty-bucket'
58+
preview_bucket_name = 'empty-bucket'
5759

5860
[[r2_buckets]]
5961
binding = 'PUT_BUCKET'
60-
bucket_name = 'put_bucket'
61-
preview_bucket_name = 'put_bucket'
62+
bucket_name = 'put-bucket'
63+
preview_bucket_name = 'put-bucket'
6264

6365
[[r2_buckets]]
6466
binding = 'SEEDED_BUCKET'
65-
bucket_name = 'seeded_bucket'
66-
preview_bucket_name = 'seeded_bucket'
67+
bucket_name = 'seeded-bucket'
68+
preview_bucket_name = 'seeded-bucket'
6769

6870
[[r2_buckets]]
6971
binding = 'DELETE_BUCKET'
70-
bucket_name = 'delete_bucket'
71-
preview_bucket_name = 'delete_bucket'
72-
72+
bucket_name = 'delete-bucket'
73+
preview_bucket_name = 'delete-bucket'
7374

7475
[build]
7576
command = "worker-build --release"
7677

77-
[build.upload]
78-
dir = "build/worker"
79-
format = "modules"
80-
main = "./shim.mjs"
81-
82-
[[build.upload.rules]]
83-
globs = ["**/*.wasm"]
84-
type = "CompiledWasm"
85-
8678
[package.metadata.wasm-pack.profile.dev.wasm-bindgen]
8779
dwarf-debug-info = true
8880

worker-sys/src/types/durable_object/id.rs

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,4 +7,10 @@ extern "C" {
77

88
#[wasm_bindgen(method, catch, js_name=toString)]
99
pub fn to_string(this: &DurableObjectId) -> Result<String, JsValue>;
10+
11+
#[wasm_bindgen(method)]
12+
pub fn equals(this: &DurableObjectId, other: &DurableObjectId) -> bool;
13+
14+
#[wasm_bindgen(method, getter)]
15+
pub fn name(this: &DurableObjectId) -> Option<String>;
1016
}

worker/src/durable.rs

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -176,6 +176,20 @@ impl ObjectId<'_> {
176176
})
177177
.map_err(Error::from)
178178
}
179+
180+
/// The name that was used to create the `ObjectId` via [`id_from_name`](https://developers.cloudflare.com/durable-objects/api/namespace/#idfromname).
181+
/// `None` is returned if the `ObjectId` was constructed using [`unique_id`](https://developers.cloudflare.com/durable-objects/api/namespace/#newuniqueid).
182+
pub fn name(&self) -> Option<String> {
183+
self.inner.name()
184+
}
185+
}
186+
187+
impl PartialEq for ObjectId<'_> {
188+
/// Compare equality between two ObjectIds using [`equals`](<https://developers.cloudflare.com/durable-objects/api/id/#equals>).
189+
/// <div class="warning">The equality check ignores the namespace.</div>
190+
fn eq(&self, other: &Self) -> bool {
191+
self.inner.equals(&other.inner)
192+
}
179193
}
180194

181195
impl Display for ObjectId<'_> {

0 commit comments

Comments
 (0)