Skip to content

Commit 92bd4bb

Browse files
kistzgefjon
andauthored
[Rust] Capability based DbContext (#5307)
# Description of Changes An evolution of #4707 where i explored adding a `db_read_only` method to `DbContext`. (hence i would appreciate your thoughts @gefjon ) This was the wrong appraoch in retrospect because there are still some annoyances with this. Furthermore there is really no benefit to adding the associated type `DbView` and is just making the ergonomics worse. So here is the new approach which solved all my problems: Making a trait for every capability which the various contexts are providing because the Databse is only one of them. These capabilities are (and pretty much the whole relevant div for this pr because the impl blocks are trivial) and should be self explanatory: ```rust pub trait CtxDbRead { fn db_read_only(&self) -> &LocalReadOnly; } pub trait CtxDbWrite: CtxDbRead { fn db(&self) -> &Local; } pub trait CtxWithSender { fn sender(&self) -> Identity; } pub trait CtxWithTimestamp { fn timestamp(&self) -> Timestamp; } pub trait CtxWithHttp { fn http(&self) -> &HttpClient; } ``` ## Why is this relevant? Lets look at an example building on the previous pr: You have abstracted your code in a trait which you can call for every context e.g. authorization. Now this does not work because the sender method is not available on `DbContext`. ```rust impl<Db: CtxDbRead> Authorization for Db { fn test(&self,args:Args) { self.db_read_only().do_i_have_perms().find(self.sender()); //ERROR: no sender method } ``` Now this is really annoying since you now have to pass additional parameters to the method. Instead we can now specific these capabilities in the type system: ```rust impl<Db: CtxDbRead+CtxWithSender> Authorization for Db { fn test(&self,args:Args) { self.db_read_only().do_i_have_perms().find(self.sender()); //WORKS NOW YAY } ``` Additonally there could be also a `+ CtxWithTimestamp` if you wanted to for example store a last logged in date or smth (you get the idea) Now this is far better because `.sender` is available for `ViewContext` for example so you can authorize with the same method. ## Alternatives/Bikeshedding I chose the names CtxWith because really the `Contexts` are the common denominator and not the `Database`. Thats also the reason why its `CtxDbRead` because you are expressing: "All context where i get read access to the databse (e.g. everything). Other names have felt worse. Also the deprecation can be removed but i think this approach is strictly superior and i dont think there are currently many people relying on it. # API and ABI breaking changes None. one deprecation for the old `DbContext` but this can also be removed if desired. # Expected complexity level and risk 1. Additive change with extremly minimal surface # Testing - [x] Works for my project --------- Signed-off-by: Kilian Strunz <93079615+kistz@users.noreply.github.com> Co-authored-by: Phoebe Goldman <phoebe@goldman-tribe.org>
1 parent b44156d commit 92bd4bb

5 files changed

Lines changed: 333 additions & 34 deletions

File tree

crates/bindings/src/http.rs

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ use crate::StdbRng;
1313
#[cfg(feature = "unstable")]
1414
use crate::{try_with_tx, with_tx, Timestamp, TxContext};
1515
use bytes::Bytes;
16-
#[cfg(all(feature = "rand", feature = "unstable"))]
16+
#[cfg(all(feature = "rand08", feature = "unstable"))]
1717
use rand08::RngCore;
1818
#[cfg(feature = "unstable")]
1919
use spacetimedb_lib::db::raw_def::v10::MethodOrAny;
@@ -22,10 +22,10 @@ use spacetimedb_lib::http as st_http;
2222
use spacetimedb_lib::http::{character_is_acceptable_for_route_path, ACCEPTABLE_ROUTE_PATH_CHARS_HUMAN_DESCRIPTION};
2323
#[cfg(feature = "unstable")]
2424
use spacetimedb_lib::Identity;
25-
#[cfg(all(feature = "unstable", feature = "rand"))]
25+
#[cfg(all(feature = "unstable", feature = "rand08"))]
2626
use spacetimedb_lib::Uuid;
2727
use spacetimedb_lib::{bsatn, TimeDuration};
28-
#[cfg(all(feature = "unstable", feature = "rand"))]
28+
#[cfg(all(feature = "unstable", feature = "rand08"))]
2929
use std::cell::Cell;
3030
#[cfg(all(feature = "unstable", feature = "rand08"))]
3131
use std::cell::OnceCell;
@@ -72,7 +72,7 @@ pub use spacetimedb_bindings_macro::http_handler as handler;
7272

7373
/// Register a [`Router`](struct@Router) to route HTTP requests to handlers.
7474
///
75-
/// This should annotate a function of no arguments which returns a [`Router`](struct@router).
75+
/// This should annotate a function of no arguments which returns a [`Router`](struct@Router).
7676
///
7777
/// ```no_run
7878
/// # use spacetimedb::http::{handler, router, Request, Response, Body, HandlerContext, Router};
@@ -109,7 +109,7 @@ pub struct HandlerContext {
109109

110110
/// A counter used for generating UUIDv7 values.
111111
/// **Note:** must be 0..=u32::MAX
112-
#[cfg(feature = "rand")]
112+
#[cfg(feature = "rand08")]
113113
pub(crate) counter_uuid: Cell<u32>,
114114
}
115115

@@ -121,7 +121,7 @@ impl HandlerContext {
121121
http: HttpClient {},
122122
#[cfg(feature = "rand08")]
123123
rng: OnceCell::new(),
124-
#[cfg(feature = "rand")]
124+
#[cfg(feature = "rand08")]
125125
counter_uuid: Cell::new(0),
126126
}
127127
}
@@ -148,15 +148,15 @@ impl HandlerContext {
148148
}
149149

150150
/// Create a new random [`Uuid`] `v4` using the built-in RNG.
151-
#[cfg(feature = "rand")]
151+
#[cfg(feature = "rand08")]
152152
pub fn new_uuid_v4(&self) -> anyhow::Result<Uuid> {
153153
let mut bytes = [0u8; 16];
154154
self.rng().try_fill_bytes(&mut bytes)?;
155155
Ok(Uuid::from_random_bytes_v4(bytes))
156156
}
157157

158158
/// Create a new sortable [`Uuid`] `v7` using the built-in RNG, counter and timestamp.
159-
#[cfg(feature = "rand")]
159+
#[cfg(feature = "rand08")]
160160
pub fn new_uuid_v7(&self) -> anyhow::Result<Uuid> {
161161
let mut random_bytes = [0u8; 4];
162162
self.rng().try_fill_bytes(&mut random_bytes)?;

0 commit comments

Comments
 (0)