Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions worker-sandbox/.dev.vars
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
SOME_SECRET="super secret"
4 changes: 4 additions & 0 deletions worker-sandbox/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,10 @@ license = "Apache-2.0"
[package.metadata.release]
release = false

# https://github.com/rustwasm/wasm-pack/issues/1247
[package.metadata.wasm-pack.profile.release]
wasm-opt = false

[lib]
crate-type = ["cdylib", "rlib"]
path = "src/lib.rs"
Expand Down
12 changes: 12 additions & 0 deletions worker-sandbox/src/router.rs
Original file line number Diff line number Diff line change
Expand Up @@ -452,6 +452,18 @@ pub fn make_router<'a>(data: SomeSharedData) -> Router<'a, SomeSharedData> {
"/durable/auto-response",
handler!(crate::test::auto_response::handle_auto_response),
)
.get_async(
"/durable/hello",
handler!(crate::test::export_durable_object::handle_hello),
)
.get_async(
"/durable/hello-unique",
handler!(crate::test::export_durable_object::handle_hello_unique),
)
.get_async(
"/durable/storage",
handler!(crate::test::export_durable_object::handle_storage),
)
.get_async(
"/sql-counter/*path",
handler!(sql_counter::handle_sql_counter),
Expand Down
5 changes: 5 additions & 0 deletions worker-sandbox/src/test/durable.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,11 @@ use worker::*;
pub async fn basic_test(env: &Env) -> Result<()> {
let namespace: ObjectNamespace = env.durable_object("MY_CLASS")?;
let id = namespace.id_from_name("A")?;
ensure!(id.name() == Some("A".into()), "Missing name");
ensure!(
namespace.unique_id()?.name().is_none(),
"Expected name property to be absent"
);
let bad = env.durable_object("DFSDF_FAKE_BINDING");
ensure!(bad.is_err(), "Invalid binding did not raise error");

Expand Down
65 changes: 63 additions & 2 deletions worker-sandbox/src/test/export_durable_object.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,13 +10,26 @@ use crate::ensure;

#[durable_object]
pub struct MyClass {
name: String,
state: State,
number: RefCell<usize>,
}

impl DurableObject for MyClass {
fn new(state: State, _env: Env) -> Self {
fn new(state: State, env: Env) -> Self {
// Check that we can re-derive the expected names.
let namespace = env.durable_object("MY_CLASS").unwrap();
let name = if let Some(name) = state.id().name() {
assert!(state.id() == namespace.id_from_name(&name).unwrap());
name
} else {
let id = state.id().to_string();
assert!(state.id() == namespace.id_from_string(&id).unwrap());
id
};

Self {
name,
state,
number: RefCell::new(0),
}
Expand All @@ -25,7 +38,7 @@ impl DurableObject for MyClass {
async fn fetch(&self, req: Request) -> Result<Response> {
let handler = async move {
match req.path().as_str() {
"/hello" => Response::ok("Hello!"),
"/hello" => Response::ok(format!("Hello from {}!", self.name)),
"/storage" => {
let storage = self.state.storage();
let map = [("one".to_string(), 1), ("two".to_string(), 2)]
Expand Down Expand Up @@ -140,3 +153,51 @@ impl DurableObject for MyClass {
.or_else(|err| Response::error(err.to_string(), 500))
}
}

// Route handlers to exercise the Durable Object from tests.
#[worker::send]
pub async fn handle_hello(
_req: Request,
env: Env,
_data: crate::SomeSharedData,
) -> Result<Response> {
let name = "your Durable Object";
let namespace = env.durable_object("MY_CLASS")?;
let id = namespace.id_from_name(name)?;
// Same name gives the same ID
assert!(id == namespace.id_from_name(name)?);

// Same name but different namespaces gives different IDs
let namespace2 = env.durable_object("COUNTER")?;
assert!(id != namespace2.id_from_name(name)?);

let stub = id.get_stub()?;
stub.fetch_with_str("https://fake-host/hello").await
}

#[worker::send]
pub async fn handle_hello_unique(
_req: Request,
env: Env,
_data: crate::SomeSharedData,
) -> Result<Response> {
let namespace = env.durable_object("MY_CLASS")?;
let id = namespace.unique_id()?;
// Different unique IDs should never be equal
assert!(id != namespace.unique_id()?);
// Deriving from the string form of the unique ID gives the same ID
assert!(id == namespace.id_from_string(&id.to_string()).unwrap());
let stub = id.get_stub()?;
stub.fetch_with_str("https://fake-host/hello").await
}

#[worker::send]
pub async fn handle_storage(
_req: Request,
env: Env,
_data: crate::SomeSharedData,
) -> Result<Response> {
let namespace = env.durable_object("MY_CLASS")?;
let stub = namespace.id_from_name("singleton")?.get_stub()?;
stub.fetch_with_str("https://fake-host/storage").await
}
28 changes: 10 additions & 18 deletions worker-sandbox/wrangler.toml
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ type = "javascript"
workers_dev = true
compatibility_date = "2022-09-12" # required
compatibility_flags = ["streams_enable_constructors"]
main = "build/worker/shim.mjs"

kv_namespaces = [
{ binding = "SOME_NAMESPACE", id = "SOME_NAMESPACE", preview_id = "SOME_NAMESPACE" },
Expand Down Expand Up @@ -32,6 +33,7 @@ bindings = [
{ name = "AUTO", class_name = "AutoResponseObject" },
{ name = "SQL_COUNTER", class_name = "SqlCounter" },
{ name = "SQL_ITERATOR", class_name = "SqlIterator" },
{ name = "MY_CLASS", class_name = "MyClass" },
]

[[analytics_engine_datasets]]
Expand All @@ -52,37 +54,27 @@ queue = "my_queue"
binding = "my_queue"
[[r2_buckets]]
binding = 'EMPTY_BUCKET'
bucket_name = 'empty_bucket'
preview_bucket_name = 'empty_bucket'
bucket_name = 'empty-bucket'
preview_bucket_name = 'empty-bucket'

[[r2_buckets]]
binding = 'PUT_BUCKET'
bucket_name = 'put_bucket'
preview_bucket_name = 'put_bucket'
bucket_name = 'put-bucket'
preview_bucket_name = 'put-bucket'

[[r2_buckets]]
binding = 'SEEDED_BUCKET'
bucket_name = 'seeded_bucket'
preview_bucket_name = 'seeded_bucket'
bucket_name = 'seeded-bucket'
preview_bucket_name = 'seeded-bucket'

[[r2_buckets]]
binding = 'DELETE_BUCKET'
bucket_name = 'delete_bucket'
preview_bucket_name = 'delete_bucket'

bucket_name = 'delete-bucket'
preview_bucket_name = 'delete-bucket'

[build]
command = "worker-build --release"

[build.upload]
dir = "build/worker"
format = "modules"
main = "./shim.mjs"

[[build.upload.rules]]
globs = ["**/*.wasm"]
type = "CompiledWasm"

[package.metadata.wasm-pack.profile.dev.wasm-bindgen]
dwarf-debug-info = true

Expand Down
6 changes: 6 additions & 0 deletions worker-sys/src/types/durable_object/id.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,4 +7,10 @@ extern "C" {

#[wasm_bindgen(method, catch, js_name=toString)]
pub fn to_string(this: &DurableObjectId) -> Result<String, JsValue>;

#[wasm_bindgen(method)]
pub fn equals(this: &DurableObjectId, other: &DurableObjectId) -> bool;

#[wasm_bindgen(method, getter)]
pub fn name(this: &DurableObjectId) -> Option<String>;
}
14 changes: 14 additions & 0 deletions worker/src/durable.rs
Original file line number Diff line number Diff line change
Expand Up @@ -176,6 +176,20 @@ impl ObjectId<'_> {
})
.map_err(Error::from)
}

/// The name that was used to create the `ObjectId` via [`id_from_name`](https://developers.cloudflare.com/durable-objects/api/namespace/#idfromname).
/// `None` is returned if the `ObjectId` was constructed using [`unique_id`](https://developers.cloudflare.com/durable-objects/api/namespace/#newuniqueid).
pub fn name(&self) -> Option<String> {
self.inner.name()
}
}

impl PartialEq for ObjectId<'_> {
/// Compare equality between two ObjectIds using [`equals`](<https://developers.cloudflare.com/durable-objects/api/id/#equals>).
/// <div class="warning">The equality check ignores the namespace.</div>
fn eq(&self, other: &Self) -> bool {
self.inner.equals(&other.inner)
}
}

impl Display for ObjectId<'_> {
Expand Down