Skip to content

Commit f828194

Browse files
authored
Merge pull request #3003 from finos/new-error-ux
New error UX
2 parents 576660b + 9b09360 commit f828194

File tree

24 files changed

+495
-234
lines changed

24 files changed

+495
-234
lines changed

Cargo.lock

Lines changed: 4 additions & 2 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

packages/perspective-workspace/test/js/table.spec.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -93,7 +93,7 @@ function tests(context, compare) {
9393
// NOTE This is the error message we expect when `restore()` is called
9494
// without a `Table`, subject to change.
9595
expect(result).toEqual(
96-
"Error: Trying to draw the viewer with no table attached"
96+
"Error: Failed to construct table from JsValue(undefined)"
9797
);
9898
await page.evaluate(async () => {
9999
await workspace.replaceTable(

rust/perspective-client/src/rust/client.rs

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -67,7 +67,7 @@ type Box2Fn<I, J, O> = Box<dyn Fn(I, J) -> O + Send + Sync + 'static>;
6767

6868
type Subscriptions<C> = Arc<RwLock<HashMap<u32, C>>>;
6969
type OnErrorCallback =
70-
Box2Fn<Option<String>, Option<ReconnectCallback>, BoxFuture<'static, Result<(), ClientError>>>;
70+
Box2Fn<ClientError, Option<ReconnectCallback>, BoxFuture<'static, Result<(), ClientError>>>;
7171
type OnceCallback = Box<dyn FnOnce(Response) -> ClientResult<()> + Send + Sync + 'static>;
7272
type SendCallback = Arc<
7373
dyn for<'a> Fn(&'a Request) -> BoxFuture<'a, Result<(), Box<dyn Error + Send + Sync>>>
@@ -241,7 +241,7 @@ impl Client {
241241
/// Handle an exception from the underlying transport.
242242
pub async fn handle_error<T, U>(
243243
&self,
244-
message: Option<String>,
244+
message: ClientError,
245245
reconnect: Option<T>,
246246
) -> ClientResult<()>
247247
where
@@ -262,12 +262,14 @@ impl Client {
262262
}));
263263

264264
tasks.await.into_iter().collect::<Result<(), _>>()?;
265+
self.subscriptions.write().await.clear();
266+
self.subscriptions_once.write().await.clear();
265267
Ok(())
266268
}
267269

268270
pub async fn on_error<T, U, V>(&self, on_error: T) -> ClientResult<u32>
269271
where
270-
T: Fn(Option<String>, Option<ReconnectCallback>) -> U + Clone + Send + Sync + 'static,
272+
T: Fn(ClientError, Option<ReconnectCallback>) -> U + Clone + Send + Sync + 'static,
271273
U: Future<Output = V> + Send + 'static,
272274
V: Into<Result<(), ClientError>> + Sync + 'static,
273275
{

rust/perspective-client/src/rust/utils/mod.rs

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@ use thiserror::*;
2525

2626
use crate::proto;
2727

28-
#[derive(Error, Debug)]
28+
#[derive(Clone, Error, Debug)]
2929
pub enum ClientError {
3030
#[error("View not found")]
3131
ViewNotFound,
@@ -61,7 +61,7 @@ pub enum ClientError {
6161
BadTableOptions,
6262

6363
#[error("External error: {0}")]
64-
ExternalError(#[from] Box<dyn std::error::Error + Send + Sync>),
64+
ExternalError(Arc<Box<dyn std::error::Error + Send + Sync>>),
6565

6666
#[error("Undecipherable proto message")]
6767
ProtoError(#[from] prost::EncodeError),
@@ -72,6 +72,12 @@ pub enum ClientError {
7272

7373
pub type ClientResult<T> = Result<T, ClientError>;
7474

75+
impl From<Box<dyn std::error::Error + Send + Sync>> for ClientError {
76+
fn from(value: Box<dyn std::error::Error + Send + Sync>) -> Self {
77+
ClientError::ExternalError(Arc::new(value))
78+
}
79+
}
80+
7581
impl<'a, A> From<std::sync::PoisonError<std::sync::MutexGuard<'a, A>>> for ClientError {
7682
fn from(_: std::sync::PoisonError<std::sync::MutexGuard<'a, A>>) -> Self {
7783
ClientError::Internal("Lock Error".to_owned())

rust/perspective-js/Cargo.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -69,6 +69,7 @@ ts-rs = { version = "10.0.0", features = [
6969
"serde-json-impl",
7070
"no-serde-warnings",
7171
] }
72+
thiserror = "1.0.55"
7273
tracing = { version = ">=0.1.36" }
7374
tracing-subscriber = "0.3.15"
7475
console_error_panic_hook = "0.1.6"

rust/perspective-js/src/rust/client.rs

Lines changed: 5 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -26,9 +26,9 @@ use wasm_bindgen::prelude::*;
2626
use wasm_bindgen_derive::TryFromJsValue;
2727
use wasm_bindgen_futures::{JsFuture, future_to_promise};
2828

29-
use crate::TableDataExt;
3029
pub use crate::table::*;
3130
use crate::utils::{ApiError, ApiResult, JsValueSerdeExt, LocalPollLoop};
31+
use crate::{TableDataExt, apierror};
3232

3333
#[wasm_bindgen]
3434
extern "C" {
@@ -72,7 +72,6 @@ impl ProxySession {
7272
Ok(())
7373
}
7474

75-
7675
pub async fn close(self) {
7776
self.0.close().await;
7877
}
@@ -221,14 +220,10 @@ impl Client {
221220

222221
#[doc(hidden)]
223222
#[wasm_bindgen]
224-
pub async fn handle_error(
225-
&self,
226-
error: Option<String>,
227-
reconnect: Option<Function>,
228-
) -> ApiResult<()> {
223+
pub async fn handle_error(&self, error: String, reconnect: Option<Function>) -> ApiResult<()> {
229224
self.client
230225
.handle_error(
231-
error,
226+
ClientError::Unknown(error),
232227
reconnect.map(|reconnect| {
233228
let reconnect =
234229
JsReconnect::from(move |()| match reconnect.call0(&JsValue::UNDEFINED) {
@@ -262,7 +257,7 @@ impl Client {
262257
#[wasm_bindgen]
263258
pub async fn on_error(&self, callback: Function) -> ApiResult<u32> {
264259
let callback = JsReconnect::from(
265-
move |(message, reconnect): (Option<String>, Option<ReconnectCallback>)| {
260+
move |(message, reconnect): (ClientError, Option<ReconnectCallback>)| {
266261
let cl: Closure<dyn Fn() -> js_sys::Promise> = Closure::new(move || {
267262
let reconnect = reconnect.clone();
268263
future_to_promise(async move {
@@ -276,7 +271,7 @@ impl Client {
276271

277272
if let Err(e) = callback.call2(
278273
&JsValue::UNDEFINED,
279-
&JsValue::from(message),
274+
&JsValue::from(apierror!(ClientError(message))),
280275
&cl.into_js_value(),
281276
) {
282277
tracing::warn!("{:?}", e);

rust/perspective-js/src/rust/table_data.rs

Lines changed: 4 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ use perspective_client::{ColumnType, TableData, TableReadFormat, UpdateData};
1616
use wasm_bindgen::convert::TryFromJsValue;
1717
use wasm_bindgen::prelude::*;
1818

19+
use crate::apierror;
1920
use crate::utils::{ApiError, ApiResult, JsValueSerdeExt, ToApiError};
2021
pub use crate::view::*;
2122

@@ -40,7 +41,6 @@ impl Vec<(String, ColumnType)> {
4041
#[ext]
4142
pub(crate) impl TableData {
4243
fn from_js_value(value: &JsValue, format: Option<TableReadFormat>) -> ApiResult<TableData> {
43-
let err_fn = || JsValue::from(format!("Failed to construct Table {:?}", value));
4444
if let Some(result) = UpdateData::from_js_value_partial(value, format)? {
4545
Ok(result.into())
4646
} else if value.is_instance_of::<Object>() && Reflect::has(value, &"__get_model".into())? {
@@ -71,10 +71,10 @@ pub(crate) impl TableData {
7171
let json = JSON::stringify(value)?.as_string().into_apierror()?;
7272
Ok(UpdateData::JsonColumns(json).into())
7373
} else {
74-
Err(err_fn().into())
74+
Err(apierror!(TableError(value.clone())))
7575
}
7676
} else {
77-
Err(err_fn().into())
77+
Err(apierror!(TableError(value.clone())))
7878
}
7979
}
8080
}
@@ -85,9 +85,8 @@ pub(crate) impl UpdateData {
8585
value: &JsValue,
8686
format: Option<TableReadFormat>,
8787
) -> ApiResult<Option<UpdateData>> {
88-
let err_fn = || JsValue::from(format!("Failed to construct Table {:?}", value));
8988
if value.is_undefined() {
90-
Err(err_fn().into())
89+
Err(apierror!(TableError(value.clone())))
9190
} else if value.is_string() {
9291
match format {
9392
None | Some(TableReadFormat::Csv) => {

0 commit comments

Comments
 (0)