Make error messages clearer when required features are not enabled (#1822)

This commit is contained in:
Rushmore Mushambi 2023-04-23 13:08:21 +02:00 committed by GitHub
parent cd16d4af5c
commit a15c8c3564
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
4 changed files with 292 additions and 178 deletions

View file

@ -20,6 +20,7 @@ use crate::api::ExtraFeatures;
use crate::api::Response; use crate::api::Response;
use crate::api::Result; use crate::api::Result;
use crate::api::Surreal; use crate::api::Surreal;
use crate::error::Db as DbError;
use flume::Receiver; use flume::Receiver;
use once_cell::sync::OnceCell; use once_cell::sync::OnceCell;
#[cfg(feature = "protocol-http")] #[cfg(feature = "protocol-http")]
@ -62,99 +63,137 @@ impl Connection for Any {
let mut features = HashSet::new(); let mut features = HashSet::new();
match address.endpoint.scheme() { match address.endpoint.scheme() {
#[cfg(feature = "kv-fdb")]
"fdb" => { "fdb" => {
features.insert(ExtraFeatures::Backup); #[cfg(feature = "kv-fdb")]
engine::local::native::router(address, conn_tx, route_rx); {
conn_rx.into_recv_async().await?? features.insert(ExtraFeatures::Backup);
} engine::local::native::router(address, conn_tx, route_rx);
conn_rx.into_recv_async().await??
#[cfg(feature = "kv-mem")]
"mem" => {
features.insert(ExtraFeatures::Backup);
engine::local::native::router(address, conn_tx, route_rx);
conn_rx.into_recv_async().await??
}
#[cfg(feature = "kv-rocksdb")]
"rocksdb" => {
features.insert(ExtraFeatures::Backup);
engine::local::native::router(address, conn_tx, route_rx);
conn_rx.into_recv_async().await??
}
#[cfg(feature = "kv-rocksdb")]
"file" => {
features.insert(ExtraFeatures::Backup);
engine::local::native::router(address, conn_tx, route_rx);
conn_rx.into_recv_async().await??
}
#[cfg(feature = "kv-tikv")]
"tikv" => {
features.insert(ExtraFeatures::Backup);
engine::local::native::router(address, conn_tx, route_rx);
conn_rx.into_recv_async().await??
}
#[cfg(feature = "protocol-http")]
"http" | "https" => {
features.insert(ExtraFeatures::Auth);
features.insert(ExtraFeatures::Backup);
let headers = http::default_headers();
#[allow(unused_mut)]
let mut builder = ClientBuilder::new().default_headers(headers);
#[cfg(any(feature = "native-tls", feature = "rustls"))]
if let Some(tls) = address.tls_config {
builder = match tls {
#[cfg(feature = "native-tls")]
Tls::Native(config) => builder.use_preconfigured_tls(config),
#[cfg(feature = "rustls")]
Tls::Rust(config) => builder.use_preconfigured_tls(config),
};
} }
let client = builder.build()?;
let base_url = address.endpoint; #[cfg(not(feature = "kv-fdb"))]
engine::remote::http::health( return Err(
client.get(base_url.join(Method::Health.as_str())?), DbError::Ds("Cannot connect to the `foundationdb` storage engine as it is not enabled in this build of SurrealDB".to_owned()).into()
) );
.await?;
engine::remote::http::native::router(base_url, client, route_rx);
} }
#[cfg(feature = "protocol-ws")] "mem" => {
"ws" | "wss" => { #[cfg(feature = "kv-mem")]
features.insert(ExtraFeatures::Auth); {
let url = address.endpoint.join(engine::remote::ws::PATH)?; features.insert(ExtraFeatures::Backup);
#[cfg(any(feature = "native-tls", feature = "rustls"))] engine::local::native::router(address, conn_tx, route_rx);
let maybe_connector = address.tls_config.map(Connector::from); conn_rx.into_recv_async().await??
#[cfg(not(any(feature = "native-tls", feature = "rustls")))] }
let maybe_connector = None;
let config = WebSocketConfig { #[cfg(not(feature = "kv-mem"))]
max_send_queue: match capacity { return Err(
0 => None, DbError::Ds("Cannot connect to the `memory` storage engine as it is not enabled in this build of SurrealDB".to_owned()).into()
capacity => Some(capacity),
},
max_message_size: Some(engine::remote::ws::native::MAX_MESSAGE_SIZE),
max_frame_size: Some(engine::remote::ws::native::MAX_FRAME_SIZE),
accept_unmasked_frames: false,
};
let socket = engine::remote::ws::native::connect(
&url,
Some(config),
maybe_connector.clone(),
)
.await?;
engine::remote::ws::native::router(
url,
maybe_connector,
capacity,
config,
socket,
route_rx,
); );
} }
"file" | "rocksdb" => {
#[cfg(feature = "kv-rocksdb")]
{
features.insert(ExtraFeatures::Backup);
engine::local::native::router(address, conn_tx, route_rx);
conn_rx.into_recv_async().await??
}
#[cfg(not(feature = "kv-rocksdb"))]
return Err(DbError::Ds(
"Cannot connect to the `rocksdb` storage engine as it is not enabled in this build of SurrealDB".to_owned(),
)
.into());
}
"tikv" => {
#[cfg(feature = "kv-tikv")]
{
features.insert(ExtraFeatures::Backup);
engine::local::native::router(address, conn_tx, route_rx);
conn_rx.into_recv_async().await??
}
#[cfg(not(feature = "kv-tikv"))]
return Err(
DbError::Ds("Cannot connect to the `tikv` storage engine as it is not enabled in this build of SurrealDB".to_owned()).into()
);
}
"http" | "https" => {
#[cfg(feature = "protocol-http")]
{
features.insert(ExtraFeatures::Auth);
features.insert(ExtraFeatures::Backup);
let headers = http::default_headers();
#[allow(unused_mut)]
let mut builder = ClientBuilder::new().default_headers(headers);
#[cfg(any(feature = "native-tls", feature = "rustls"))]
if let Some(tls) = address.tls_config {
builder = match tls {
#[cfg(feature = "native-tls")]
Tls::Native(config) => builder.use_preconfigured_tls(config),
#[cfg(feature = "rustls")]
Tls::Rust(config) => builder.use_preconfigured_tls(config),
};
}
let client = builder.build()?;
let base_url = address.endpoint;
engine::remote::http::health(
client.get(base_url.join(Method::Health.as_str())?),
)
.await?;
engine::remote::http::native::router(base_url, client, route_rx);
}
#[cfg(not(feature = "protocol-http"))]
return Err(DbError::Ds(
"Cannot connect to the `HTTP` remote engine as it is not enabled in this build of SurrealDB".to_owned(),
)
.into());
}
"ws" | "wss" => {
#[cfg(feature = "protocol-ws")]
{
features.insert(ExtraFeatures::Auth);
let url = address.endpoint.join(engine::remote::ws::PATH)?;
#[cfg(any(feature = "native-tls", feature = "rustls"))]
let maybe_connector = address.tls_config.map(Connector::from);
#[cfg(not(any(feature = "native-tls", feature = "rustls")))]
let maybe_connector = None;
let config = WebSocketConfig {
max_send_queue: match capacity {
0 => None,
capacity => Some(capacity),
},
max_message_size: Some(engine::remote::ws::native::MAX_MESSAGE_SIZE),
max_frame_size: Some(engine::remote::ws::native::MAX_FRAME_SIZE),
accept_unmasked_frames: false,
};
let socket = engine::remote::ws::native::connect(
&url,
Some(config),
maybe_connector.clone(),
)
.await?;
engine::remote::ws::native::router(
url,
maybe_connector,
capacity,
config,
socket,
route_rx,
);
}
#[cfg(not(feature = "protocol-ws"))]
return Err(DbError::Ds(
"Cannot connect to the `WebSocket` remote engine as it is not enabled in this build of SurrealDB".to_owned(),
)
.into());
}
scheme => { scheme => {
return Err(Error::Scheme(scheme.to_owned()).into()); return Err(Error::Scheme(scheme.to_owned()).into());
} }

View file

@ -15,6 +15,7 @@ use crate::api::ExtraFeatures;
use crate::api::Response; use crate::api::Response;
use crate::api::Result; use crate::api::Result;
use crate::api::Surreal; use crate::api::Surreal;
use crate::error::Db as DbError;
use flume::Receiver; use flume::Receiver;
use once_cell::sync::OnceCell; use once_cell::sync::OnceCell;
use serde::de::DeserializeOwned; use serde::de::DeserializeOwned;
@ -50,69 +51,113 @@ impl Connection for Any {
let mut features = HashSet::new(); let mut features = HashSet::new();
match address.endpoint.scheme() { match address.endpoint.scheme() {
#[cfg(feature = "kv-fdb")]
"fdb" => { "fdb" => {
engine::local::wasm::router(address, conn_tx, route_rx); #[cfg(feature = "kv-fdb")]
if let Err(error) = conn_rx.into_recv_async().await? { {
return Err(error); engine::local::wasm::router(address, conn_tx, route_rx);
if let Err(error) = conn_rx.into_recv_async().await? {
return Err(error);
}
} }
#[cfg(not(feature = "kv-fdb"))]
return Err(
DbError::Ds("Cannot connect to the `foundationdb` storage engine as it is not enabled in this build of SurrealDB".to_owned()).into()
);
} }
#[cfg(feature = "kv-indxdb")]
"indxdb" => { "indxdb" => {
engine::local::wasm::router(address, conn_tx, route_rx); #[cfg(feature = "kv-indxdb")]
if let Err(error) = conn_rx.into_recv_async().await? { {
return Err(error); engine::local::wasm::router(address, conn_tx, route_rx);
if let Err(error) = conn_rx.into_recv_async().await? {
return Err(error);
}
} }
#[cfg(not(feature = "kv-indxdb"))]
return Err(
DbError::Ds("Cannot connect to the `indxdb` storage engine as it is not enabled in this build of SurrealDB".to_owned()).into()
);
} }
#[cfg(feature = "kv-mem")]
"mem" => { "mem" => {
engine::local::wasm::router(address, conn_tx, route_rx); #[cfg(feature = "kv-mem")]
if let Err(error) = conn_rx.into_recv_async().await? { {
return Err(error); engine::local::wasm::router(address, conn_tx, route_rx);
if let Err(error) = conn_rx.into_recv_async().await? {
return Err(error);
}
} }
#[cfg(not(feature = "kv-mem"))]
return Err(
DbError::Ds("Cannot connect to the `memory` storage engine as it is not enabled in this build of SurrealDB".to_owned()).into()
);
} }
#[cfg(feature = "kv-rocksdb")] "file" | "rocksdb" => {
"rocksdb" => { #[cfg(feature = "kv-rocksdb")]
engine::local::wasm::router(address, conn_tx, route_rx); {
if let Err(error) = conn_rx.into_recv_async().await? { engine::local::wasm::router(address, conn_tx, route_rx);
return Err(error); if let Err(error) = conn_rx.into_recv_async().await? {
return Err(error);
}
} }
#[cfg(not(feature = "kv-rocksdb"))]
return Err(DbError::Ds(
"Cannot connect to the `rocksdb` storage engine as it is not enabled in this build of SurrealDB".to_owned(),
)
.into());
} }
#[cfg(feature = "kv-rocksdb")]
"file" => {
engine::local::wasm::router(address, conn_tx, route_rx);
if let Err(error) = conn_rx.into_recv_async().await? {
return Err(error);
}
}
#[cfg(feature = "kv-tikv")]
"tikv" => { "tikv" => {
engine::local::wasm::router(address, conn_tx, route_rx); #[cfg(feature = "kv-tikv")]
if let Err(error) = conn_rx.into_recv_async().await? { {
return Err(error); engine::local::wasm::router(address, conn_tx, route_rx);
if let Err(error) = conn_rx.into_recv_async().await? {
return Err(error);
}
} }
#[cfg(not(feature = "kv-tikv"))]
return Err(
DbError::Ds("Cannot connect to the `tikv` storage engine as it is not enabled in this build of SurrealDB".to_owned()).into()
);
} }
#[cfg(feature = "protocol-http")]
"http" | "https" => { "http" | "https" => {
features.insert(ExtraFeatures::Auth); #[cfg(feature = "protocol-http")]
engine::remote::http::wasm::router(address, conn_tx, route_rx); {
features.insert(ExtraFeatures::Auth);
engine::remote::http::wasm::router(address, conn_tx, route_rx);
}
#[cfg(not(feature = "protocol-http"))]
return Err(DbError::Ds(
"Cannot connect to the `HTTP` remote engine as it is not enabled in this build of SurrealDB".to_owned(),
)
.into());
} }
#[cfg(feature = "protocol-ws")]
"ws" | "wss" => { "ws" | "wss" => {
features.insert(ExtraFeatures::Auth); #[cfg(feature = "protocol-ws")]
let mut address = address; {
address.endpoint = address.endpoint.join(engine::remote::ws::PATH)?; features.insert(ExtraFeatures::Auth);
engine::remote::ws::wasm::router(address, capacity, conn_tx, route_rx); let mut address = address;
if let Err(error) = conn_rx.into_recv_async().await? { address.endpoint = address.endpoint.join(engine::remote::ws::PATH)?;
return Err(error); engine::remote::ws::wasm::router(address, capacity, conn_tx, route_rx);
if let Err(error) = conn_rx.into_recv_async().await? {
return Err(error);
}
} }
#[cfg(not(feature = "protocol-ws"))]
return Err(DbError::Ds(
"Cannot connect to the `WebSocket` remote engine as it is not enabled in this build of SurrealDB".to_owned(),
)
.into());
} }
scheme => { scheme => {

View file

@ -24,7 +24,7 @@ pub enum Error {
#[error("There was an error processing a remote WS request")] #[error("There was an error processing a remote WS request")]
Ws(String), Ws(String),
/// There specified scheme does not match any supported protocol or storage engine /// The specified scheme does not match any supported protocol or storage engine
#[error("Unsupported protocol or storage engine, `{0}`")] #[error("Unsupported protocol or storage engine, `{0}`")]
Scheme(String), Scheme(String),

View file

@ -97,74 +97,104 @@ impl Datastore {
/// ``` /// ```
pub async fn new(path: &str) -> Result<Datastore, Error> { pub async fn new(path: &str) -> Result<Datastore, Error> {
match path { match path {
#[cfg(feature = "kv-mem")]
"memory" => { "memory" => {
info!(target: LOG, "Starting kvs store in {}", path); #[cfg(feature = "kv-mem")]
let v = super::mem::Datastore::new().await.map(|v| Datastore { {
inner: Inner::Mem(v), info!(target: LOG, "Starting kvs store in {}", path);
}); let v = super::mem::Datastore::new().await.map(|v| Datastore {
info!(target: LOG, "Started kvs store in {}", path); inner: Inner::Mem(v),
v });
info!(target: LOG, "Started kvs store in {}", path);
v
}
#[cfg(not(feature = "kv-mem"))]
return Err(Error::Ds("Cannot connect to the `memory` storage engine as it is not enabled in this build of SurrealDB".to_owned()));
} }
// Parse and initiate an File database // Parse and initiate an File database
#[cfg(feature = "kv-rocksdb")]
s if s.starts_with("file:") => { s if s.starts_with("file:") => {
info!(target: LOG, "Starting kvs store at {}", path); #[cfg(feature = "kv-rocksdb")]
let s = s.trim_start_matches("file://"); {
let s = s.trim_start_matches("file:"); info!(target: LOG, "Starting kvs store at {}", path);
let v = super::rocksdb::Datastore::new(s).await.map(|v| Datastore { let s = s.trim_start_matches("file://");
inner: Inner::RocksDB(v), let s = s.trim_start_matches("file:");
}); let v = super::rocksdb::Datastore::new(s).await.map(|v| Datastore {
info!(target: LOG, "Started kvs store at {}", path); inner: Inner::RocksDB(v),
v });
info!(target: LOG, "Started kvs store at {}", path);
v
}
#[cfg(not(feature = "kv-rocksdb"))]
return Err(Error::Ds("Cannot connect to the `rocksdb` storage engine as it is not enabled in this build of SurrealDB".to_owned()));
} }
// Parse and initiate an RocksDB database // Parse and initiate an RocksDB database
#[cfg(feature = "kv-rocksdb")]
s if s.starts_with("rocksdb:") => { s if s.starts_with("rocksdb:") => {
info!(target: LOG, "Starting kvs store at {}", path); #[cfg(feature = "kv-rocksdb")]
let s = s.trim_start_matches("rocksdb://"); {
let s = s.trim_start_matches("rocksdb:"); info!(target: LOG, "Starting kvs store at {}", path);
let v = super::rocksdb::Datastore::new(s).await.map(|v| Datastore { let s = s.trim_start_matches("rocksdb://");
inner: Inner::RocksDB(v), let s = s.trim_start_matches("rocksdb:");
}); let v = super::rocksdb::Datastore::new(s).await.map(|v| Datastore {
info!(target: LOG, "Started kvs store at {}", path); inner: Inner::RocksDB(v),
v });
info!(target: LOG, "Started kvs store at {}", path);
v
}
#[cfg(not(feature = "kv-rocksdb"))]
return Err(Error::Ds("Cannot connect to the `rocksdb` storage engine as it is not enabled in this build of SurrealDB".to_owned()));
} }
// Parse and initiate an IndxDB database // Parse and initiate an IndxDB database
#[cfg(feature = "kv-indxdb")]
s if s.starts_with("indxdb:") => { s if s.starts_with("indxdb:") => {
info!(target: LOG, "Starting kvs store at {}", path); #[cfg(feature = "kv-indxdb")]
let s = s.trim_start_matches("indxdb://"); {
let s = s.trim_start_matches("indxdb:"); info!(target: LOG, "Starting kvs store at {}", path);
let v = super::indxdb::Datastore::new(s).await.map(|v| Datastore { let s = s.trim_start_matches("indxdb://");
inner: Inner::IndxDB(v), let s = s.trim_start_matches("indxdb:");
}); let v = super::indxdb::Datastore::new(s).await.map(|v| Datastore {
info!(target: LOG, "Started kvs store at {}", path); inner: Inner::IndxDB(v),
v });
info!(target: LOG, "Started kvs store at {}", path);
v
}
#[cfg(not(feature = "kv-indxdb"))]
return Err(Error::Ds("Cannot connect to the `indxdb` storage engine as it is not enabled in this build of SurrealDB".to_owned()));
} }
// Parse and initiate a TiKV database // Parse and initiate a TiKV database
#[cfg(feature = "kv-tikv")]
s if s.starts_with("tikv:") => { s if s.starts_with("tikv:") => {
info!(target: LOG, "Connecting to kvs store at {}", path); #[cfg(feature = "kv-tikv")]
let s = s.trim_start_matches("tikv://"); {
let s = s.trim_start_matches("tikv:"); info!(target: LOG, "Connecting to kvs store at {}", path);
let v = super::tikv::Datastore::new(s).await.map(|v| Datastore { let s = s.trim_start_matches("tikv://");
inner: Inner::TiKV(v), let s = s.trim_start_matches("tikv:");
}); let v = super::tikv::Datastore::new(s).await.map(|v| Datastore {
info!(target: LOG, "Connected to kvs store at {}", path); inner: Inner::TiKV(v),
v });
info!(target: LOG, "Connected to kvs store at {}", path);
v
}
#[cfg(not(feature = "kv-tikv"))]
return Err(Error::Ds("Cannot connect to the `tikv` storage engine as it is not enabled in this build of SurrealDB".to_owned()));
} }
// Parse and initiate a FoundationDB database // Parse and initiate a FoundationDB database
#[cfg(feature = "kv-fdb")]
s if s.starts_with("fdb:") => { s if s.starts_with("fdb:") => {
info!(target: LOG, "Connecting to kvs store at {}", path); #[cfg(feature = "kv-fdb")]
let s = s.trim_start_matches("fdb://"); {
let s = s.trim_start_matches("fdb:"); info!(target: LOG, "Connecting to kvs store at {}", path);
let v = super::fdb::Datastore::new(s).await.map(|v| Datastore { let s = s.trim_start_matches("fdb://");
inner: Inner::FDB(v), let s = s.trim_start_matches("fdb:");
}); let v = super::fdb::Datastore::new(s).await.map(|v| Datastore {
info!(target: LOG, "Connected to kvs store at {}", path); inner: Inner::FDB(v),
v });
info!(target: LOG, "Connected to kvs store at {}", path);
v
}
#[cfg(not(feature = "kv-fdb"))]
return Err(Error::Ds("Cannot connect to the `foundationdb` storage engine as it is not enabled in this build of SurrealDB".to_owned()));
} }
// The datastore path is not valid // The datastore path is not valid
_ => { _ => {