Skip to content

Commit e7c6d68

Browse files
committed
Return parsed DomainName alongside Address when resolving NameOrAddress
Helps to avoid repeatedly parsing `DomainName`, and to simplify the `/database/publish` handler (not included in this patch).
1 parent dec2955 commit e7c6d68

File tree

3 files changed

+70
-15
lines changed

3 files changed

+70
-15
lines changed

crates/client-api/src/routes/database.rs

Lines changed: 8 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -69,7 +69,7 @@ pub async fn call(
6969

7070
let args = ReducerArgs::Json(body);
7171

72-
let address = name_or_address.resolve(&*worker_ctx).await?;
72+
let address = name_or_address.resolve(&*worker_ctx).await?.into();
7373
let database = worker_ctx_find_database(&*worker_ctx, &address).await?.ok_or_else(|| {
7474
log::error!("Could not find database: {}", address.to_hex());
7575
(StatusCode::NOT_FOUND, "No such database.")
@@ -254,7 +254,7 @@ pub async fn describe(
254254
Query(DescribeQueryParams { expand }): Query<DescribeQueryParams>,
255255
auth: SpacetimeAuthHeader,
256256
) -> axum::response::Result<impl IntoResponse> {
257-
let address = name_or_address.resolve(&*worker_ctx).await?;
257+
let address = name_or_address.resolve(&*worker_ctx).await?.into();
258258
let database = worker_ctx_find_database(&*worker_ctx, &address)
259259
.await?
260260
.ok_or((StatusCode::NOT_FOUND, "No such database."))?;
@@ -308,7 +308,7 @@ pub async fn catalog(
308308
Query(DescribeQueryParams { expand }): Query<DescribeQueryParams>,
309309
auth: SpacetimeAuthHeader,
310310
) -> axum::response::Result<impl IntoResponse> {
311-
let address = name_or_address.resolve(&*worker_ctx).await?;
311+
let address = name_or_address.resolve(&*worker_ctx).await?.into();
312312
let database = worker_ctx_find_database(&*worker_ctx, &address)
313313
.await?
314314
.ok_or((StatusCode::NOT_FOUND, "No such database."))?;
@@ -354,7 +354,7 @@ pub async fn info(
354354
State(worker_ctx): State<Arc<dyn WorkerCtx>>,
355355
Path(InfoParams { name_or_address }): Path<InfoParams>,
356356
) -> axum::response::Result<impl IntoResponse> {
357-
let address = name_or_address.resolve(&*worker_ctx).await?;
357+
let address = name_or_address.resolve(&*worker_ctx).await?.into();
358358
let database = worker_ctx_find_database(&*worker_ctx, &address)
359359
.await?
360360
.ok_or((StatusCode::NOT_FOUND, "No such database."))?;
@@ -403,7 +403,7 @@ pub async fn logs(
403403
// Should all the others change?
404404
let auth = auth_or_unauth(auth)?;
405405

406-
let address = name_or_address.resolve(&*worker_ctx).await?;
406+
let address = name_or_address.resolve(&*worker_ctx).await?.into();
407407
let database = worker_ctx_find_database(&*worker_ctx, &address)
408408
.await?
409409
.ok_or((StatusCode::NOT_FOUND, "No such database."))?;
@@ -503,7 +503,7 @@ pub async fn sql(
503503
// which queries this identity is allowed to execute against the database.
504504
let auth = auth.get_or_create(&*worker_ctx).await?;
505505

506-
let address = name_or_address.resolve(&*worker_ctx).await?;
506+
let address = name_or_address.resolve(&*worker_ctx).await?.into();
507507
let database = worker_ctx_find_database(&*worker_ctx, &address)
508508
.await?
509509
.ok_or((StatusCode::NOT_FOUND, "No such database."))?;
@@ -800,9 +800,8 @@ pub async fn publish(
800800
// Parse the address or convert the name to a usable address
801801
let db_address = if let Some(name_or_address) = name_or_address.clone() {
802802
match name_or_address.try_resolve(&*ctx).await? {
803-
Ok(address) => address,
804-
Err(name) => {
805-
let domain = name.parse().map_err(DomainParsingRejection)?;
803+
Ok(resolved) => resolved.into(),
804+
Err(domain) => {
806805
// Client specified a name which doesn't yet exist
807806
// Create a new DNS record and a new address to assign to it
808807
let address = ctx.control_db().alloc_spacetime_address().await.map_err(log_and_500)?;

crates/client-api/src/routes/subscribe.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -41,7 +41,7 @@ pub async fn handle_websocket(
4141
) -> axum::response::Result<impl IntoResponse> {
4242
let auth = auth.get_or_create(&*worker_ctx).await?;
4343

44-
let address = name_or_address.resolve(&*worker_ctx).await?;
44+
let address = name_or_address.resolve(&*worker_ctx).await?.into();
4545

4646
let (res, ws_upgrade, protocol) =
4747
ws.select_protocol([(BIN_PROTOCOL, Protocol::Binary), (TEXT_PROTOCOL, Protocol::Text)]);

crates/client-api/src/util.rs

Lines changed: 61 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,9 @@ use axum::headers;
99
use axum::response::IntoResponse;
1010
use bytestring::ByteString;
1111
use http::{HeaderName, HeaderValue, Request, StatusCode};
12+
1213
use spacetimedb::address::Address;
14+
use spacetimedb_lib::name::DomainName;
1315

1416
use crate::routes::database::DomainParsingRejection;
1517
use crate::{log_and_500, ControlNodeDelegate};
@@ -74,20 +76,50 @@ impl NameOrAddress {
7476
}
7577
}
7678

79+
/// Resolve this [`NameOrAddress`].
80+
///
81+
/// If `self` is a [`NameOrAddress::Address`], the returned
82+
/// [`ResolvedAddress`] contains only an [`Address`] and a `None`
83+
/// [`DomainName`].
84+
///
85+
/// Otherwise, if `self` is a [`NameOrAddress::Name`], the [`Address`] is
86+
/// looked up by that name in the SpacetimeDB DNS and the returned
87+
/// [`ResolvedAddress`] contains both an [`Address`] and `Some`
88+
/// [`DomainName`].
89+
///
90+
/// Errors are returned if [`NameOrAddress::Name`] cannot be parsed into a
91+
/// [`DomainName`], or the DNS lookup fails.
92+
///
93+
/// An `Ok` result is itself a [`Result`], which is `Err(DomainName)` if the
94+
/// given [`NameOrAddress::Name`] is not registered in the SpacetimeDB DNS,
95+
/// i.e. no corresponding [`Address`] exists.
7796
pub async fn try_resolve(
7897
&self,
7998
ctx: &(impl ControlNodeDelegate + ?Sized),
80-
) -> axum::response::Result<Result<Address, &str>> {
99+
) -> axum::response::Result<Result<ResolvedAddress, DomainName>> {
81100
Ok(match self {
82-
NameOrAddress::Address(addr) => Ok(*addr),
83-
NameOrAddress::Name(name) => {
101+
Self::Address(addr) => Ok(ResolvedAddress {
102+
address: *addr,
103+
domain: None,
104+
}),
105+
Self::Name(name) => {
84106
let domain = name.parse().map_err(DomainParsingRejection)?;
85-
ctx.spacetime_dns(&domain).await.map_err(log_and_500)?.ok_or(name)
107+
let address = ctx.spacetime_dns(&domain).await.map_err(log_and_500)?;
108+
match address {
109+
Some(address) => Ok(ResolvedAddress {
110+
address,
111+
domain: Some(domain),
112+
}),
113+
None => Err(domain),
114+
}
86115
}
87116
})
88117
}
89118

90-
pub async fn resolve(&self, ctx: &(impl ControlNodeDelegate + ?Sized)) -> axum::response::Result<Address> {
119+
/// A variant of [`Self::try_resolve()`] which maps to a 400 (Bad Request)
120+
/// response if `self` is a [`NameOrAddress::Name`] for which no
121+
/// corresponding [`Address`] is found in the SpacetimeDB DNS.
122+
pub async fn resolve(&self, ctx: &(impl ControlNodeDelegate + ?Sized)) -> axum::response::Result<ResolvedAddress> {
91123
self.try_resolve(ctx).await?.map_err(|_| StatusCode::BAD_REQUEST.into())
92124
}
93125
}
@@ -106,3 +138,27 @@ impl<'de> serde::Deserialize<'de> for NameOrAddress {
106138
})
107139
}
108140
}
141+
142+
/// A resolved [`NameOrAddress`].
143+
///
144+
/// Constructed by [`NameOrAddress::try_resolve()`].
145+
pub struct ResolvedAddress {
146+
address: Address,
147+
domain: Option<DomainName>,
148+
}
149+
150+
impl ResolvedAddress {
151+
pub fn address(&self) -> &Address {
152+
&self.address
153+
}
154+
155+
pub fn domain(&self) -> Option<&DomainName> {
156+
self.domain.as_ref()
157+
}
158+
}
159+
160+
impl From<ResolvedAddress> for Address {
161+
fn from(value: ResolvedAddress) -> Self {
162+
value.address
163+
}
164+
}

0 commit comments

Comments
 (0)