Skip to content

Commit 99f876b

Browse files
authored
Return parsed DomainName alongside Address when resolving NameOrAddress (#161)
* 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 262d851 commit 99f876b

File tree

3 files changed

+68
-15
lines changed

3 files changed

+68
-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: 59 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,9 @@ use axum::headers;
1010
use axum::response::IntoResponse;
1111
use bytestring::ByteString;
1212
use http::{HeaderName, HeaderValue, Request, StatusCode};
13+
1314
use spacetimedb::address::Address;
15+
use spacetimedb_lib::name::DomainName;
1416

1517
use crate::routes::database::DomainParsingRejection;
1618
use crate::{log_and_500, ControlNodeDelegate};
@@ -75,20 +77,48 @@ impl NameOrAddress {
7577
}
7678
}
7779

80+
/// Resolve this [`NameOrAddress`].
81+
///
82+
/// If `self` is a [`NameOrAddress::Address`], the inner [`Address`] is
83+
/// returned in a [`ResolvedAddress`] without a [`DomainName`].
84+
///
85+
/// Otherwise, if `self` is a [`NameOrAddress::Name`], the [`Address`] is
86+
/// looked up by that name in the SpacetimeDB DNS and returned in a
87+
/// [`ResolvedAddress`] alongside `Some` [`DomainName`].
88+
///
89+
/// Errors are returned if [`NameOrAddress::Name`] cannot be parsed into a
90+
/// [`DomainName`], or the DNS lookup fails.
91+
///
92+
/// An `Ok` result is itself a [`Result`], which is `Err(DomainName)` if the
93+
/// given [`NameOrAddress::Name`] is not registered in the SpacetimeDB DNS,
94+
/// i.e. no corresponding [`Address`] exists.
7895
pub async fn try_resolve(
7996
&self,
8097
ctx: &(impl ControlNodeDelegate + ?Sized),
81-
) -> axum::response::Result<Result<Address, &str>> {
98+
) -> axum::response::Result<Result<ResolvedAddress, DomainName>> {
8299
Ok(match self {
83-
NameOrAddress::Address(addr) => Ok(*addr),
84-
NameOrAddress::Name(name) => {
100+
Self::Address(addr) => Ok(ResolvedAddress {
101+
address: *addr,
102+
domain: None,
103+
}),
104+
Self::Name(name) => {
85105
let domain = name.parse().map_err(DomainParsingRejection)?;
86-
ctx.spacetime_dns(&domain).await.map_err(log_and_500)?.ok_or(name)
106+
let address = ctx.spacetime_dns(&domain).await.map_err(log_and_500)?;
107+
match address {
108+
Some(address) => Ok(ResolvedAddress {
109+
address,
110+
domain: Some(domain),
111+
}),
112+
None => Err(domain),
113+
}
87114
}
88115
})
89116
}
90117

91-
pub async fn resolve(&self, ctx: &(impl ControlNodeDelegate + ?Sized)) -> axum::response::Result<Address> {
118+
/// A variant of [`Self::try_resolve()`] which maps to a 400 (Bad Request)
119+
/// response if `self` is a [`NameOrAddress::Name`] for which no
120+
/// corresponding [`Address`] is found in the SpacetimeDB DNS.
121+
pub async fn resolve(&self, ctx: &(impl ControlNodeDelegate + ?Sized)) -> axum::response::Result<ResolvedAddress> {
92122
self.try_resolve(ctx).await?.map_err(|_| StatusCode::BAD_REQUEST.into())
93123
}
94124
}
@@ -116,3 +146,27 @@ impl fmt::Display for NameOrAddress {
116146
}
117147
}
118148
}
149+
150+
/// A resolved [`NameOrAddress`].
151+
///
152+
/// Constructed by [`NameOrAddress::try_resolve()`].
153+
pub struct ResolvedAddress {
154+
address: Address,
155+
domain: Option<DomainName>,
156+
}
157+
158+
impl ResolvedAddress {
159+
pub fn address(&self) -> &Address {
160+
&self.address
161+
}
162+
163+
pub fn domain(&self) -> Option<&DomainName> {
164+
self.domain.as_ref()
165+
}
166+
}
167+
168+
impl From<ResolvedAddress> for Address {
169+
fn from(value: ResolvedAddress) -> Self {
170+
value.address
171+
}
172+
}

0 commit comments

Comments
 (0)