Files
Luke c7af2d4cd3 Deprecate ReducerContext::identity in favor of database_identity (#4843)
## Summary
- add `ReducerContext::database_identity()` in Rust bindings
- deprecate `ReducerContext::identity()` and keep it as a compatibility
alias
- update reducer docs example to use `ctx.database_identity()`
- add C# reducer-context equivalent: `DatabaseIdentity` and obsolete
`Identity` alias
- update Rust/C# module test callsites to the new API name
- update C# codegen snapshots for generated `ReducerContext` API output

## Why
Issue #3201 reports user confusion between reducer `ctx.identity()`
(database/module identity) and `ctx.sender`. This change clarifies
naming while preserving compatibility.

## Validation
- `cargo check -p spacetimedb -p module-test` (Passed)
- `dotnet test crates/bindings-csharp/Codegen.Tests/Codegen.Tests.csproj
--nologo` (Passed)
- `dotnet test crates/bindings-csharp/Runtime.Tests/Runtime.Tests.csproj
--nologo` (Failed) pre-existing unrelated failure:
- `Runtime.Tests/JwtClaimsTest.cs(10,23): CS1729: 'JwtClaims' does not
contain a constructor that takes 2 arguments`

## Compatibility
- Rust: `identity()` still works but is deprecated in favor of
`database_identity()`.
- C#: `Identity` still works but is marked `[Obsolete]` in favor of
`DatabaseIdentity`.

Closes #3201
2026-05-04 19:50:39 +00:00

120 lines
4.1 KiB
C++

#ifndef SPACETIMEDB_TX_CONTEXT_H
#define SPACETIMEDB_TX_CONTEXT_H
#include <spacetimedb/reducer_context.h>
namespace SpacetimeDB {
/**
* @brief Transaction context for procedures
*
* TxContext wraps a ReducerContext to provide transactional database access.
* It's analogous to Rust's TxContext which is passed to closures in
* `ctx.with_tx()` and `ctx.try_with_tx()`.
*
* Design: Mimics Rust's Deref trait for consistent API
* =====================================================
* In Rust, TxContext implements Deref<Target=ReducerContext>, which means:
* - Reducers use: ctx.db.table()
* - Transactions use: tx.db.table() (Deref auto-dereferences)
*
* C++ doesn't support operator. overloading, so we explicitly expose
* ReducerContext fields as public references to achieve the same ergonomics:
* - Reducers use: ctx.db[table]
* - Transactions use: tx.db[table] (same syntax!)
*
* Tradeoff: TxContext is 40 bytes instead of 8 bytes (storing 5 references),
* but this is negligible as TxContext is stack-allocated and short-lived.
* The consistent API is worth the minor memory cost.
*
* All database operations are part of an anonymous transaction:
* - Transaction commits when the callback returns successfully
* - Transaction rolls back if the callback throws or returns error
*
* Example usage:
* @code
* SPACETIMEDB_PROCEDURE(void, insert_user, ProcedureContext ctx, std::string name) {
* ctx.with_tx([&](TxContext& tx) {
* // Access authentication (same as in reducers)
* if (tx.sender_auth().has_jwt()) {
* auto jwt = tx.sender_auth().get_jwt();
* // ...
* }
* // Database operations here are transactional (same syntax as reducers)
* tx.db[users].insert(User{name});
* });
* }
* @endcode
*/
struct TxContext {
private:
ReducerContext& ctx_;
public:
// Public references to ReducerContext fields for consistent API with Rust
// In Rust, Deref makes tx.db work the same as ctx.db
// In C++, we explicitly expose references where possible and provide
// accessors for fields exposed as methods on ReducerContext.
DatabaseContext& db;
const Timestamp& timestamp;
const std::optional<ConnectionId>& connection_id;
// Constructor - initializes all reference members
explicit TxContext(ReducerContext& ctx)
: ctx_(ctx),
db(ctx.db),
timestamp(ctx.timestamp),
connection_id(ctx.connection_id) {}
// Access to ReducerContext methods
Identity sender() const { return ctx_.sender(); }
const AuthCtx& sender_auth() const { return ctx_.sender_auth(); }
Identity database_identity() const { return ctx_.database_identity(); }
[[deprecated("Use database_identity() instead.")]]
Identity identity() const { return database_identity(); }
StdbRng& rng() const { return ctx_.rng(); }
/**
* Generate a new random UUID v4.
*
* Creates a random UUID using the transaction's deterministic RNG.
*
* Example:
* @code
* SPACETIMEDB_PROCEDURE(void, create_session, ProcedureContext ctx) {
* ctx.with_tx([&](TxContext& tx) {
* Uuid session_id = tx.new_uuid_v4();
* tx.db[sessions].insert(Session{session_id});
* });
* }
* @endcode
*
* @return A new UUID v4
*/
Uuid new_uuid_v4() const { return ctx_.new_uuid_v4(); }
/**
* Generate a new UUID v7.
*
* Creates a time-ordered UUID with the transaction's timestamp, a monotonic counter,
* and random bytes from the transaction's deterministic RNG.
*
* Example:
* @code
* SPACETIMEDB_PROCEDURE(void, create_user, ProcedureContext ctx, std::string name) {
* ctx.with_tx([&](TxContext& tx) {
* Uuid user_id = tx.new_uuid_v7();
* tx.db[users].insert(User{user_id, name});
* });
* }
* @endcode
*
* @return A new UUID v7
*/
Uuid new_uuid_v7() const { return ctx_.new_uuid_v7(); }
};
} // namespace SpacetimeDB
#endif // SPACETIMEDB_TX_CONTEXT_H