From 3a9abb97c21fdd59755b12e679a9864c2c4ecdc7 Mon Sep 17 00:00:00 2001 From: kjuulh Date: Sun, 30 Apr 2023 12:57:50 +0200 Subject: [PATCH] Add thiserror instead of exposing eyre anonymous errors The change here is to make it easier for the consumer to debug the api. Such that they can `match` on individual errors instead of having to parse text. eyre is convenient, but mostly from a consumers perspective --- Cargo.lock | 2 + crates/dagger-codegen/src/rust/functions.rs | 4 +- crates/dagger-core/Cargo.toml | 1 + crates/dagger-core/src/gql_client.rs | 2 +- crates/dagger-core/src/graphql_client.rs | 52 ++++++++-- crates/dagger-sdk/Cargo.toml | 1 + crates/dagger-sdk/src/client.rs | 10 +- crates/dagger-sdk/src/errors.rs | 27 ++++++ crates/dagger-sdk/src/gen.rs | 100 ++++++++++---------- crates/dagger-sdk/src/lib.rs | 1 + crates/dagger-sdk/src/querybuilder.rs | 22 +++-- crates/dagger-sdk/tests/mod.rs | 6 +- 12 files changed, 157 insertions(+), 71 deletions(-) create mode 100644 crates/dagger-sdk/src/errors.rs diff --git a/Cargo.lock b/Cargo.lock index 1f72fc2..8fd3621 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -334,6 +334,7 @@ dependencies = [ "sha2", "tar", "tempfile", + "thiserror", "tokio", "tracing", "tracing-subscriber", @@ -352,6 +353,7 @@ dependencies = [ "rand", "serde", "serde_json", + "thiserror", "tokio", "tracing", "tracing-subscriber", diff --git a/crates/dagger-codegen/src/rust/functions.rs b/crates/dagger-codegen/src/rust/functions.rs index 7ce020f..d2a26ae 100644 --- a/crates/dagger-codegen/src/rust/functions.rs +++ b/crates/dagger-codegen/src/rust/functions.rs @@ -235,8 +235,10 @@ fn render_output_type(funcs: &CommonFunctions, type_ref: &TypeRef) -> rust::Toke }; } + let dagger_error = rust::import("crate::errors", "DaggerError"); + quote! { - eyre::Result<$output_type> + Result<$output_type, $dagger_error> } } diff --git a/crates/dagger-core/Cargo.toml b/crates/dagger-core/Cargo.toml index be9730f..d7c0d5f 100644 --- a/crates/dagger-core/Cargo.toml +++ b/crates/dagger-core/Cargo.toml @@ -16,6 +16,7 @@ serde_json = { workspace = true } tokio = { workspace = true } tracing = { workspace = true } tracing-subscriber = { workspace = true } +thiserror.workspace = true base64 = "0.21.0" dirs = "4.0.0" diff --git a/crates/dagger-core/src/gql_client.rs b/crates/dagger-core/src/gql_client.rs index a02263f..1348446 100644 --- a/crates/dagger-core/src/gql_client.rs +++ b/crates/dagger-core/src/gql_client.rs @@ -15,7 +15,7 @@ pub struct GraphQLError { #[derive(Deserialize, Debug, Clone)] #[allow(dead_code)] pub struct GraphQLErrorMessage { - message: String, + pub message: String, locations: Option>, extensions: Option>, path: Option>, diff --git a/crates/dagger-core/src/graphql_client.rs b/crates/dagger-core/src/graphql_client.rs index 19ce517..200e7b5 100644 --- a/crates/dagger-core/src/graphql_client.rs +++ b/crates/dagger-core/src/graphql_client.rs @@ -4,13 +4,14 @@ use std::sync::Arc; use async_trait::async_trait; use base64::engine::general_purpose; use base64::Engine; +use thiserror::Error; use crate::connect_params::ConnectParams; use crate::gql_client::{ClientConfig, GQLClient}; #[async_trait] pub trait GraphQLClient { - async fn query(&self, query: &str) -> eyre::Result>; + async fn query(&self, query: &str) -> Result, GraphQLError>; } pub type DynGraphQLClient = Arc; @@ -40,13 +41,50 @@ impl DefaultGraphQLClient { #[async_trait] impl GraphQLClient for DefaultGraphQLClient { - async fn query(&self, query: &str) -> eyre::Result> { - let res: Option = self - .client - .query(&query) - .await - .map_err(|r| eyre::anyhow!(r.to_string()))?; + async fn query(&self, query: &str) -> Result, GraphQLError> { + let res: Option = + self.client.query(&query).await.map_err(map_graphql_error)?; return Ok(res); } } + +fn map_graphql_error(gql_error: crate::gql_client::GraphQLError) -> GraphQLError { + let message = gql_error.message().to_string(); + let json = gql_error.json(); + + if let Some(json) = json { + if !json.is_empty() { + return GraphQLError::DomainError { + message, + fields: GraphqlErrorMessages(json.into_iter().map(|e| e.message).collect()), + }; + } + } + + GraphQLError::HttpError(message) +} + +#[derive(Error, Debug)] +pub enum GraphQLError { + #[error("http error: {0}")] + HttpError(String), + #[error("domain error:\n{message}\n{fields}")] + DomainError { + message: String, + fields: GraphqlErrorMessages, + }, +} + +#[derive(Debug, Clone)] +pub struct GraphqlErrorMessages(Vec); + +impl std::fmt::Display for GraphqlErrorMessages { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + for error in self.0.iter() { + f.write_fmt(format_args!("{error}\n"))?; + } + + Ok(()) + } +} diff --git a/crates/dagger-sdk/Cargo.toml b/crates/dagger-sdk/Cargo.toml index 09078db..7f55e59 100644 --- a/crates/dagger-sdk/Cargo.toml +++ b/crates/dagger-sdk/Cargo.toml @@ -19,6 +19,7 @@ serde = { workspace = true } serde_json = { workspace = true } tracing.workspace = true tracing-subscriber.workspace = true +thiserror.workspace = true futures = "0.3.28" derive_builder = "0.12.0" diff --git a/crates/dagger-sdk/src/client.rs b/crates/dagger-sdk/src/client.rs index 604f67a..cd4e19d 100644 --- a/crates/dagger-sdk/src/client.rs +++ b/crates/dagger-sdk/src/client.rs @@ -5,20 +5,24 @@ use dagger_core::graphql_client::DefaultGraphQLClient; use dagger_core::config::Config; use dagger_core::engine::Engine as DaggerEngine; +use crate::errors::ConnectError; use crate::gen::Query; use crate::logging::StdLogger; use crate::querybuilder::query; pub type DaggerConn = Arc; -pub async fn connect() -> eyre::Result { +pub async fn connect() -> Result { let cfg = Config::new(None, None, None, None, Some(Arc::new(StdLogger::default()))); connect_opts(cfg).await } -pub async fn connect_opts(cfg: Config) -> eyre::Result { - let (conn, proc) = DaggerEngine::new().start(&cfg).await?; +pub async fn connect_opts(cfg: Config) -> Result { + let (conn, proc) = DaggerEngine::new() + .start(&cfg) + .await + .map_err(ConnectError::FailedToConnect)?; Ok(Arc::new(Query { proc: proc.map(|p| Arc::new(p)), diff --git a/crates/dagger-sdk/src/errors.rs b/crates/dagger-sdk/src/errors.rs new file mode 100644 index 0000000..336f0d1 --- /dev/null +++ b/crates/dagger-sdk/src/errors.rs @@ -0,0 +1,27 @@ +use thiserror::Error; + +#[derive(Error, Debug)] +pub enum ConnectError { + #[error("failed to connect to dagger engine")] + FailedToConnect(#[source] eyre::Error), +} + +#[derive(Error, Debug)] +pub enum DaggerError { + #[error("failed to build dagger internal graph")] + Build(#[source] eyre::Error), + #[error("failed to parse input type")] + Serialize(#[source] eyre::Error), + #[error("failed to query dagger engine: {0}")] + Query(#[source] dagger_core::graphql_client::GraphQLError), + #[error("failed to unpack response")] + Unpack(#[source] DaggerUnpackError), +} + +#[derive(Error, Debug)] +pub enum DaggerUnpackError { + #[error("Too many nested objects inside graphql response")] + TooManyNestedObjects, + #[error("failed to deserialize response")] + Deserialize(#[source] serde_json::Error), +} diff --git a/crates/dagger-sdk/src/gen.rs b/crates/dagger-sdk/src/gen.rs index 6a79ab2..e20153d 100644 --- a/crates/dagger-sdk/src/gen.rs +++ b/crates/dagger-sdk/src/gen.rs @@ -1,3 +1,4 @@ +use crate::errors::DaggerError; use crate::querybuilder::Selection; use dagger_core::graphql_client::DynGraphQLClient; use derive_builder::Builder; @@ -121,7 +122,7 @@ pub struct CacheVolume { } impl CacheVolume { - pub async fn id(&self) -> eyre::Result { + pub async fn id(&self) -> Result { let query = self.selection.select("id"); query.execute(self.graphql_client.clone()).await @@ -397,7 +398,7 @@ impl Container { }; } /// Retrieves default arguments for future commands. - pub async fn default_args(&self) -> eyre::Result> { + pub async fn default_args(&self) -> Result, DaggerError> { let query = self.selection.select("defaultArgs"); query.execute(self.graphql_client.clone()).await @@ -427,7 +428,7 @@ impl Container { /// # Arguments /// /// * `opt` - optional argument, see inner type for documentation, use _opts to use - pub async fn endpoint(&self) -> eyre::Result { + pub async fn endpoint(&self) -> Result { let query = self.selection.select("endpoint"); query.execute(self.graphql_client.clone()).await @@ -441,7 +442,10 @@ impl Container { /// # Arguments /// /// * `opt` - optional argument, see inner type for documentation, use _opts to use - pub async fn endpoint_opts<'a>(&self, opts: ContainerEndpointOpts<'a>) -> eyre::Result { + pub async fn endpoint_opts<'a>( + &self, + opts: ContainerEndpointOpts<'a>, + ) -> Result { let mut query = self.selection.select("endpoint"); if let Some(port) = opts.port { @@ -454,7 +458,7 @@ impl Container { query.execute(self.graphql_client.clone()).await } /// Retrieves entrypoint to be prepended to the arguments of all commands. - pub async fn entrypoint(&self) -> eyre::Result> { + pub async fn entrypoint(&self) -> Result, DaggerError> { let query = self.selection.select("entrypoint"); query.execute(self.graphql_client.clone()).await @@ -464,7 +468,7 @@ impl Container { /// # Arguments /// /// * `name` - The name of the environment variable to retrieve (e.g., "PATH"). - pub async fn env_variable(&self, name: impl Into) -> eyre::Result { + pub async fn env_variable(&self, name: impl Into) -> Result { let mut query = self.selection.select("envVariable"); query = query.arg("name", name.into()); @@ -531,7 +535,7 @@ impl Container { } /// Exit code of the last executed command. Zero means success. /// Will execute default command if none is set, or error if there's no default. - pub async fn exit_code(&self) -> eyre::Result { + pub async fn exit_code(&self) -> Result { let query = self.selection.select("exitCode"); query.execute(self.graphql_client.clone()).await @@ -545,7 +549,7 @@ impl Container { /// * `path` - Host's destination path (e.g., "./tarball"). /// Path can be relative to the engine's workdir or absolute. /// * `opt` - optional argument, see inner type for documentation, use _opts to use - pub async fn export(&self, path: impl Into) -> eyre::Result { + pub async fn export(&self, path: impl Into) -> Result { let mut query = self.selection.select("export"); query = query.arg("path", path.into()); @@ -566,7 +570,7 @@ impl Container { &self, path: impl Into, opts: ContainerExportOpts, - ) -> eyre::Result { + ) -> Result { let mut query = self.selection.select("export"); query = query.arg("path", path.into()); @@ -634,19 +638,19 @@ impl Container { } /// Retrieves a hostname which can be used by clients to reach this container. /// Currently experimental; set _EXPERIMENTAL_DAGGER_SERVICES_DNS=0 to disable. - pub async fn hostname(&self) -> eyre::Result { + pub async fn hostname(&self) -> Result { let query = self.selection.select("hostname"); query.execute(self.graphql_client.clone()).await } /// A unique identifier for this container. - pub async fn id(&self) -> eyre::Result { + pub async fn id(&self) -> Result { let query = self.selection.select("id"); query.execute(self.graphql_client.clone()).await } /// The unique image reference which can only be retrieved immediately after the 'Container.From' call. - pub async fn image_ref(&self) -> eyre::Result { + pub async fn image_ref(&self) -> Result { let query = self.selection.select("imageRef"); query.execute(self.graphql_client.clone()).await @@ -694,7 +698,7 @@ impl Container { }; } /// Retrieves the value of the specified label. - pub async fn label(&self, name: impl Into) -> eyre::Result { + pub async fn label(&self, name: impl Into) -> Result { let mut query = self.selection.select("label"); query = query.arg("name", name.into()); @@ -712,7 +716,7 @@ impl Container { }]; } /// Retrieves the list of paths where a directory is mounted. - pub async fn mounts(&self) -> eyre::Result> { + pub async fn mounts(&self) -> Result, DaggerError> { let query = self.selection.select("mounts"); query.execute(self.graphql_client.clone()).await @@ -763,7 +767,7 @@ impl Container { }; } /// The platform this container executes and publishes as. - pub async fn platform(&self) -> eyre::Result { + pub async fn platform(&self) -> Result { let query = self.selection.select("platform"); query.execute(self.graphql_client.clone()).await @@ -778,7 +782,7 @@ impl Container { /// /// Formatted as [host]/[user]/[repo]:[tag] (e.g. "docker.io/dagger/dagger:main"). /// * `opt` - optional argument, see inner type for documentation, use _opts to use - pub async fn publish(&self, address: impl Into) -> eyre::Result { + pub async fn publish(&self, address: impl Into) -> Result { let mut query = self.selection.select("publish"); query = query.arg("address", address.into()); @@ -800,7 +804,7 @@ impl Container { &self, address: impl Into, opts: ContainerPublishOpts, - ) -> eyre::Result { + ) -> Result { let mut query = self.selection.select("publish"); query = query.arg("address", address.into()); @@ -822,20 +826,20 @@ impl Container { } /// The error stream of the last executed command. /// Will execute default command if none is set, or error if there's no default. - pub async fn stderr(&self) -> eyre::Result { + pub async fn stderr(&self) -> Result { let query = self.selection.select("stderr"); query.execute(self.graphql_client.clone()).await } /// The output stream of the last executed command. /// Will execute default command if none is set, or error if there's no default. - pub async fn stdout(&self) -> eyre::Result { + pub async fn stdout(&self) -> Result { let query = self.selection.select("stdout"); query.execute(self.graphql_client.clone()).await } /// Retrieves the user to be set for all commands. - pub async fn user(&self) -> eyre::Result { + pub async fn user(&self) -> Result { let query = self.selection.select("user"); query.execute(self.graphql_client.clone()).await @@ -1720,7 +1724,7 @@ impl Container { }; } /// Retrieves the working directory for all commands. - pub async fn workdir(&self) -> eyre::Result { + pub async fn workdir(&self) -> Result { let query = self.selection.select("workdir"); query.execute(self.graphql_client.clone()).await @@ -1882,7 +1886,7 @@ impl Directory { /// # Arguments /// /// * `opt` - optional argument, see inner type for documentation, use _opts to use - pub async fn entries(&self) -> eyre::Result> { + pub async fn entries(&self) -> Result, DaggerError> { let query = self.selection.select("entries"); query.execute(self.graphql_client.clone()).await @@ -1896,7 +1900,7 @@ impl Directory { pub async fn entries_opts<'a>( &self, opts: DirectoryEntriesOpts<'a>, - ) -> eyre::Result> { + ) -> Result, DaggerError> { let mut query = self.selection.select("entries"); if let Some(path) = opts.path { @@ -1910,7 +1914,7 @@ impl Directory { /// # Arguments /// /// * `path` - Location of the copied directory (e.g., "logs/"). - pub async fn export(&self, path: impl Into) -> eyre::Result { + pub async fn export(&self, path: impl Into) -> Result { let mut query = self.selection.select("export"); query = query.arg("path", path.into()); @@ -1934,7 +1938,7 @@ impl Directory { }; } /// The content-addressed identifier of the directory. - pub async fn id(&self) -> eyre::Result { + pub async fn id(&self) -> Result { let query = self.selection.select("id"); query.execute(self.graphql_client.clone()).await @@ -2242,13 +2246,13 @@ pub struct EnvVariable { impl EnvVariable { /// The environment variable name. - pub async fn name(&self) -> eyre::Result { + pub async fn name(&self) -> Result { let query = self.selection.select("name"); query.execute(self.graphql_client.clone()).await } /// The environment variable value. - pub async fn value(&self) -> eyre::Result { + pub async fn value(&self) -> Result { let query = self.selection.select("value"); query.execute(self.graphql_client.clone()).await @@ -2263,7 +2267,7 @@ pub struct File { impl File { /// Retrieves the contents of the file. - pub async fn contents(&self) -> eyre::Result { + pub async fn contents(&self) -> Result { let query = self.selection.select("contents"); query.execute(self.graphql_client.clone()).await @@ -2273,7 +2277,7 @@ impl File { /// # Arguments /// /// * `path` - Location of the written directory (e.g., "output.txt"). - pub async fn export(&self, path: impl Into) -> eyre::Result { + pub async fn export(&self, path: impl Into) -> Result { let mut query = self.selection.select("export"); query = query.arg("path", path.into()); @@ -2281,7 +2285,7 @@ impl File { query.execute(self.graphql_client.clone()).await } /// Retrieves the content-addressed identifier of the file. - pub async fn id(&self) -> eyre::Result { + pub async fn id(&self) -> Result { let query = self.selection.select("id"); query.execute(self.graphql_client.clone()).await @@ -2297,7 +2301,7 @@ impl File { }; } /// Gets the size of the file, in bytes. - pub async fn size(&self) -> eyre::Result { + pub async fn size(&self) -> Result { let query = self.selection.select("size"); query.execute(self.graphql_client.clone()).await @@ -2338,7 +2342,7 @@ pub struct GitRefTreeOpts<'a> { impl GitRef { /// The digest of the current value of this ref. - pub async fn digest(&self) -> eyre::Result { + pub async fn digest(&self) -> Result { let query = self.selection.select("digest"); query.execute(self.graphql_client.clone()).await @@ -2405,7 +2409,7 @@ impl GitRepository { }; } /// Lists of branches on the repository. - pub async fn branches(&self) -> eyre::Result> { + pub async fn branches(&self) -> Result, DaggerError> { let query = self.selection.select("branches"); query.execute(self.graphql_client.clone()).await @@ -2443,7 +2447,7 @@ impl GitRepository { }; } /// Lists of tags on the repository. - pub async fn tags(&self) -> eyre::Result> { + pub async fn tags(&self) -> Result, DaggerError> { let query = self.selection.select("tags"); query.execute(self.graphql_client.clone()).await @@ -2609,7 +2613,7 @@ impl HostVariable { }; } /// The value of this variable. - pub async fn value(&self) -> eyre::Result { + pub async fn value(&self) -> Result { let query = self.selection.select("value"); query.execute(self.graphql_client.clone()).await @@ -2624,13 +2628,13 @@ pub struct Label { impl Label { /// The label name. - pub async fn name(&self) -> eyre::Result { + pub async fn name(&self) -> Result { let query = self.selection.select("name"); query.execute(self.graphql_client.clone()).await } /// The label value. - pub async fn value(&self) -> eyre::Result { + pub async fn value(&self) -> Result { let query = self.selection.select("value"); query.execute(self.graphql_client.clone()).await @@ -2645,19 +2649,19 @@ pub struct Port { impl Port { /// The port description. - pub async fn description(&self) -> eyre::Result { + pub async fn description(&self) -> Result { let query = self.selection.select("description"); query.execute(self.graphql_client.clone()).await } /// The port number. - pub async fn port(&self) -> eyre::Result { + pub async fn port(&self) -> Result { let query = self.selection.select("port"); query.execute(self.graphql_client.clone()).await } /// The transport layer network protocol. - pub async fn protocol(&self) -> eyre::Result { + pub async fn protocol(&self) -> Result { let query = self.selection.select("protocol"); query.execute(self.graphql_client.clone()).await @@ -2692,25 +2696,25 @@ impl Project { }; } /// install the project's schema - pub async fn install(&self) -> eyre::Result { + pub async fn install(&self) -> Result { let query = self.selection.select("install"); query.execute(self.graphql_client.clone()).await } /// name of the project - pub async fn name(&self) -> eyre::Result { + pub async fn name(&self) -> Result { let query = self.selection.select("name"); query.execute(self.graphql_client.clone()).await } /// schema provided by the project - pub async fn schema(&self) -> eyre::Result { + pub async fn schema(&self) -> Result { let query = self.selection.select("schema"); query.execute(self.graphql_client.clone()).await } /// sdk used to generate code for and/or execute this project - pub async fn sdk(&self) -> eyre::Result { + pub async fn sdk(&self) -> Result { let query = self.selection.select("sdk"); query.execute(self.graphql_client.clone()).await @@ -2825,7 +2829,7 @@ impl Query { }; } /// The default platform of the builder. - pub async fn default_platform(&self) -> eyre::Result { + pub async fn default_platform(&self) -> Result { let query = self.selection.select("defaultPlatform"); query.execute(self.graphql_client.clone()).await @@ -3094,13 +3098,13 @@ pub struct Secret { impl Secret { /// The identifier for this secret. - pub async fn id(&self) -> eyre::Result { + pub async fn id(&self) -> Result { let query = self.selection.select("id"); query.execute(self.graphql_client.clone()).await } /// The value of this secret. - pub async fn plaintext(&self) -> eyre::Result { + pub async fn plaintext(&self) -> Result { let query = self.selection.select("plaintext"); query.execute(self.graphql_client.clone()).await @@ -3115,7 +3119,7 @@ pub struct Socket { impl Socket { /// The content-addressed identifier of the socket. - pub async fn id(&self) -> eyre::Result { + pub async fn id(&self) -> Result { let query = self.selection.select("id"); query.execute(self.graphql_client.clone()).await diff --git a/crates/dagger-sdk/src/lib.rs b/crates/dagger-sdk/src/lib.rs index ac40a66..f37baa2 100644 --- a/crates/dagger-sdk/src/lib.rs +++ b/crates/dagger-sdk/src/lib.rs @@ -1,6 +1,7 @@ #![deny(warnings)] mod client; +pub mod errors; mod gen; pub mod logging; mod querybuilder; diff --git a/crates/dagger-sdk/src/querybuilder.rs b/crates/dagger-sdk/src/querybuilder.rs index 4fd586c..183e268 100644 --- a/crates/dagger-sdk/src/querybuilder.rs +++ b/crates/dagger-sdk/src/querybuilder.rs @@ -1,9 +1,10 @@ use std::{collections::HashMap, ops::Add, sync::Arc}; use dagger_core::graphql_client::DynGraphQLClient; -use eyre::Context; use serde::{Deserialize, Serialize}; +use crate::errors::{DaggerError, DaggerUnpackError}; + pub fn query() -> Selection { Selection::default() } @@ -92,7 +93,7 @@ impl Selection { s } - pub fn build(&self) -> eyre::Result { + pub fn build(&self) -> Result { let mut fields = vec!["query".to_string()]; for sel in self.path() { @@ -117,7 +118,7 @@ impl Selection { Ok(fields.join("{") + &"}".repeat(fields.len() - 1)) } - pub async fn execute(&self, gql_client: DynGraphQLClient) -> eyre::Result + pub async fn execute(&self, gql_client: DynGraphQLClient) -> Result where D: for<'de> Deserialize<'de>, { @@ -127,7 +128,7 @@ impl Selection { let resp: Option = match gql_client.query(&query).await { Ok(r) => r, - Err(e) => eyre::bail!(e), + Err(e) => return Err(DaggerError::Query(e)), }; let resp: Option = self.unpack_resp(resp)?; @@ -151,7 +152,10 @@ impl Selection { selections } - pub(crate) fn unpack_resp(&self, resp: Option) -> eyre::Result> + pub(crate) fn unpack_resp( + &self, + resp: Option, + ) -> Result, DaggerError> where D: for<'de> Deserialize<'de>, { @@ -161,21 +165,23 @@ impl Selection { } } - fn unpack_resp_value(&self, r: serde_json::Value) -> eyre::Result + fn unpack_resp_value(&self, r: serde_json::Value) -> Result where D: for<'de> Deserialize<'de>, { if let Some(o) = r.as_object() { let keys = o.keys(); if keys.len() != 1 { - eyre::bail!("too many nested objects inside graphql response") + return Err(DaggerError::Unpack(DaggerUnpackError::TooManyNestedObjects)); } let first = keys.into_iter().next().unwrap(); return self.unpack_resp_value(o.get(first).unwrap().clone()); } - serde_json::from_value::(r).context("could not deserialize response") + serde_json::from_value::(r) + .map_err(DaggerUnpackError::Deserialize) + .map_err(DaggerError::Unpack) } } diff --git a/crates/dagger-sdk/tests/mod.rs b/crates/dagger-sdk/tests/mod.rs index 9611f64..08d4f61 100644 --- a/crates/dagger-sdk/tests/mod.rs +++ b/crates/dagger-sdk/tests/mod.rs @@ -111,9 +111,9 @@ async fn test_err_message() { assert_eq!(alpine.is_err(), true); let err = alpine.expect_err("Tests expect err"); - let error_msg = r#" -GQLClient Error: Look at json field for more details -Message: pull access denied, repository does not exist or may require authorization: server message: insufficient_scope: authorization failed + let error_msg = r#"failed to query dagger engine: domain error: +Look at json field for more details +pull access denied, repository does not exist or may require authorization: server message: insufficient_scope: authorization failed "#; assert_eq!(err.to_string().as_str(), error_msg);