Compare commits

...

6 Commits

Author SHA1 Message Date
107d3ca0bf 🚀 chore(README.md): check off introducing thiserror for better errors in plan for next release
The plan for the next release has been updated to reflect that the [thiserror](https://docs.rs/thiserror/latest/thiserror/) library has been introduced to improve error handling.
2023-04-30 13:12:23 +02:00
3a9abb97c2 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
2023-04-30 13:12:23 +02:00
66ab2f552c
🚀 chore(Cargo.toml): add thiserror crate to dependencies
The thiserror crate has been added to the dependencies in the Cargo.toml file. This crate is used to derive custom error types with automatic source location.
2023-04-30 11:41:12 +02:00
b72920e235
chore(README.md): mark dagger run compatibility and upstream update tasks as completed 2023-04-30 11:39:08 +02:00
c8bdf42e86
refactor(dagger-codegen): remove unnecessary mutability from field variable in for loop 2023-04-30 11:37:55 +02:00
40ece05140
Release dagger-core v0.2.11, dagger-sdk v0.2.22 2023-04-30 10:56:53 +02:00
18 changed files with 228 additions and 83 deletions

6
Cargo.lock generated
View File

@ -317,7 +317,7 @@ dependencies = [
[[package]] [[package]]
name = "dagger-core" name = "dagger-core"
version = "0.2.10" version = "0.2.11"
dependencies = [ dependencies = [
"async-trait", "async-trait",
"base64", "base64",
@ -334,6 +334,7 @@ dependencies = [
"sha2", "sha2",
"tar", "tar",
"tempfile", "tempfile",
"thiserror",
"tokio", "tokio",
"tracing", "tracing",
"tracing-subscriber", "tracing-subscriber",
@ -341,7 +342,7 @@ dependencies = [
[[package]] [[package]]
name = "dagger-sdk" name = "dagger-sdk"
version = "0.2.21" version = "0.2.22"
dependencies = [ dependencies = [
"dagger-core", "dagger-core",
"derive_builder", "derive_builder",
@ -352,6 +353,7 @@ dependencies = [
"rand", "rand",
"serde", "serde",
"serde_json", "serde_json",
"thiserror",
"tokio", "tokio",
"tracing", "tracing",
"tracing-subscriber", "tracing-subscriber",

View File

@ -23,3 +23,4 @@ tracing-subscriber = { version = "0.3.16", features = [
"tracing-log", "tracing-log",
"tracing", "tracing",
] } ] }
thiserror = "1.0.40"

View File

@ -4,14 +4,14 @@ A dagger sdk written in rust for rust.
## Plan for next release ## Plan for next release
- [ ] Introduce [thiserror](https://docs.rs/thiserror/latest/thiserror/) for - [x] Introduce [thiserror](https://docs.rs/thiserror/latest/thiserror/) for
better errors better errors
- [ ] Add compatibility with `dagger run` - [x] Add compatibility with `dagger run`
- [ ] Add open telemetry tracing to the sdk - [ ] Add open telemetry tracing to the sdk
- [ ] Remove `id().await?` from passing to other dagger graphs, this should make - [ ] Remove `id().await?` from passing to other dagger graphs, this should make
the design much cleaner the design much cleaner
- [ ] Start MkBook on how to actually use the sdk - [ ] Start MkBook on how to actually use the sdk
- [ ] Update to newest upstream release - [x] Update to newest upstream release
- [ ] Fix bugs - [ ] Fix bugs
- [x] Run in conjunction with golang and other sdks - [x] Run in conjunction with golang and other sdks
- [ ] Stabilize the initial `Arc<Query>` model into something more extensible - [ ] Stabilize the initial `Arc<Query>` model into something more extensible

View File

@ -8,6 +8,6 @@ edition = "2021"
[dependencies] [dependencies]
clap = "4.1.6" clap = "4.1.6"
color-eyre = "0.6.2" color-eyre = "0.6.2"
dagger-sdk = { path = "../crates/dagger-sdk/", version = "^0.2.21" } dagger-sdk = { path = "../crates/dagger-sdk/", version = "^0.2.22" }
eyre = "0.6.8" eyre = "0.6.8"
tokio = { version = "1.25.0", features = ["full"] } tokio = { version = "1.25.0", features = ["full"] }

View File

@ -13,7 +13,7 @@ use self::generator::DynGenerator;
fn set_schema_parents(mut schema: Schema) -> Schema { fn set_schema_parents(mut schema: Schema) -> Schema {
for t in schema.types.as_mut().into_iter().flatten().flatten() { for t in schema.types.as_mut().into_iter().flatten().flatten() {
let t_parent = t.full_type.clone(); let t_parent = t.full_type.clone();
for mut field in t.full_type.fields.as_mut().into_iter().flatten() { for field in t.full_type.fields.as_mut().into_iter().flatten() {
field.parent_type = Some(t_parent.clone()); field.parent_type = Some(t_parent.clone());
} }
} }

View File

@ -235,8 +235,10 @@ fn render_output_type(funcs: &CommonFunctions, type_ref: &TypeRef) -> rust::Toke
}; };
} }
let dagger_error = rust::import("crate::errors", "DaggerError");
quote! { quote! {
eyre::Result<$output_type> Result<$output_type, $dagger_error>
} }
} }

View File

@ -5,6 +5,33 @@ All notable changes to this project will be documented in this file.
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
## v0.2.11 (2023-04-29)
### New Features
- <csr-id-2a29a66217fa4d6c530ea1ce670c8836383e7051/> dagger-run support
- <csr-id-eb7470c604169d1a15976078c0889d5cc7011257/> update to dagger-5.1
### Commit Statistics
<csr-read-only-do-not-edit/>
- 2 commits contributed to the release.
- 4 days passed between releases.
- 2 commits were understood as [conventional](https://www.conventionalcommits.org).
- 0 issues like '(#ID)' were seen in commit messages
### Commit Details
<csr-read-only-do-not-edit/>
<details><summary>view details</summary>
* **Uncategorized**
- dagger-run support ([`2a29a66`](https://github.com/kjuulh/dagger-sdk/commit/2a29a66217fa4d6c530ea1ce670c8836383e7051))
- update to dagger-5.1 ([`eb7470c`](https://github.com/kjuulh/dagger-sdk/commit/eb7470c604169d1a15976078c0889d5cc7011257))
</details>
## v0.2.10 (2023-04-25) ## v0.2.10 (2023-04-25)
### Bug Fixes ### Bug Fixes
@ -15,7 +42,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
<csr-read-only-do-not-edit/> <csr-read-only-do-not-edit/>
- 1 commit contributed to the release. - 2 commits contributed to the release.
- 21 days passed between releases. - 21 days passed between releases.
- 1 commit was understood as [conventional](https://www.conventionalcommits.org). - 1 commit was understood as [conventional](https://www.conventionalcommits.org).
- 0 issues like '(#ID)' were seen in commit messages - 0 issues like '(#ID)' were seen in commit messages
@ -27,6 +54,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
<details><summary>view details</summary> <details><summary>view details</summary>
* **Uncategorized** * **Uncategorized**
- Release dagger-core v0.2.10 ([`8011c42`](https://github.com/kjuulh/dagger-sdk/commit/8011c42dc077d101b1bccaf231fac17636dd249d))
- delete other files/folder in downloads: #57 ([`9d3c21d`](https://github.com/kjuulh/dagger-sdk/commit/9d3c21d16b4a64eb7a7b1888365a4c4ea56d7225)) - delete other files/folder in downloads: #57 ([`9d3c21d`](https://github.com/kjuulh/dagger-sdk/commit/9d3c21d16b4a64eb7a7b1888365a4c4ea56d7225))
</details> </details>

View File

@ -1,6 +1,6 @@
[package] [package]
name = "dagger-core" name = "dagger-core"
version = "0.2.10" version = "0.2.11"
edition = "2021" edition = "2021"
readme = "README.md" readme = "README.md"
license-file = "LICENSE.MIT" license-file = "LICENSE.MIT"
@ -16,6 +16,7 @@ serde_json = { workspace = true }
tokio = { workspace = true } tokio = { workspace = true }
tracing = { workspace = true } tracing = { workspace = true }
tracing-subscriber = { workspace = true } tracing-subscriber = { workspace = true }
thiserror.workspace = true
base64 = "0.21.0" base64 = "0.21.0"
dirs = "4.0.0" dirs = "4.0.0"

View File

@ -15,7 +15,7 @@ pub struct GraphQLError {
#[derive(Deserialize, Debug, Clone)] #[derive(Deserialize, Debug, Clone)]
#[allow(dead_code)] #[allow(dead_code)]
pub struct GraphQLErrorMessage { pub struct GraphQLErrorMessage {
message: String, pub message: String,
locations: Option<Vec<GraphQLErrorLocation>>, locations: Option<Vec<GraphQLErrorLocation>>,
extensions: Option<HashMap<String, String>>, extensions: Option<HashMap<String, String>>,
path: Option<Vec<GraphQLErrorPathParam>>, path: Option<Vec<GraphQLErrorPathParam>>,

View File

@ -4,13 +4,14 @@ use std::sync::Arc;
use async_trait::async_trait; use async_trait::async_trait;
use base64::engine::general_purpose; use base64::engine::general_purpose;
use base64::Engine; use base64::Engine;
use thiserror::Error;
use crate::connect_params::ConnectParams; use crate::connect_params::ConnectParams;
use crate::gql_client::{ClientConfig, GQLClient}; use crate::gql_client::{ClientConfig, GQLClient};
#[async_trait] #[async_trait]
pub trait GraphQLClient { pub trait GraphQLClient {
async fn query(&self, query: &str) -> eyre::Result<Option<serde_json::Value>>; async fn query(&self, query: &str) -> Result<Option<serde_json::Value>, GraphQLError>;
} }
pub type DynGraphQLClient = Arc<dyn GraphQLClient + Send + Sync>; pub type DynGraphQLClient = Arc<dyn GraphQLClient + Send + Sync>;
@ -40,13 +41,50 @@ impl DefaultGraphQLClient {
#[async_trait] #[async_trait]
impl GraphQLClient for DefaultGraphQLClient { impl GraphQLClient for DefaultGraphQLClient {
async fn query(&self, query: &str) -> eyre::Result<Option<serde_json::Value>> { async fn query(&self, query: &str) -> Result<Option<serde_json::Value>, GraphQLError> {
let res: Option<serde_json::Value> = self let res: Option<serde_json::Value> =
.client self.client.query(&query).await.map_err(map_graphql_error)?;
.query(&query)
.await
.map_err(|r| eyre::anyhow!(r.to_string()))?;
return Ok(res); 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<String>);
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(())
}
}

View File

@ -6,8 +6,37 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
and this project adheres to and this project adheres to
[Semantic Versioning](https://semver.org/spec/v2.0.0.html). [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
## v0.2.22 (2023-04-29)
### New Features
- <csr-id-2a29a66217fa4d6c530ea1ce670c8836383e7051/> dagger-run support
- <csr-id-eb7470c604169d1a15976078c0889d5cc7011257/> update to dagger-5.1
### Commit Statistics
<csr-read-only-do-not-edit/>
- 2 commits contributed to the release.
- 4 days passed between releases.
- 2 commits were understood as [conventional](https://www.conventionalcommits.org).
- 0 issues like '(#ID)' were seen in commit messages
### Commit Details
<csr-read-only-do-not-edit/>
<details><summary>view details</summary>
* **Uncategorized**
- dagger-run support ([`2a29a66`](https://github.com/kjuulh/dagger-sdk/commit/2a29a66217fa4d6c530ea1ce670c8836383e7051))
- update to dagger-5.1 ([`eb7470c`](https://github.com/kjuulh/dagger-sdk/commit/eb7470c604169d1a15976078c0889d5cc7011257))
</details>
## v0.2.21 (2023-04-25) ## v0.2.21 (2023-04-25)
<csr-id-09881ee39bdfb9201d104e4679a51c3b76b5fe27/>
### Chore ### Chore
- <csr-id-09881ee39bdfb9201d104e4679a51c3b76b5fe27/> add new dagger-core-version - <csr-id-09881ee39bdfb9201d104e4679a51c3b76b5fe27/> add new dagger-core-version
@ -16,7 +45,7 @@ and this project adheres to
<csr-read-only-do-not-edit/> <csr-read-only-do-not-edit/>
- 1 commit contributed to the release. - 2 commits contributed to the release.
- 21 days passed between releases. - 21 days passed between releases.
- 1 commit was understood as [conventional](https://www.conventionalcommits.org). - 1 commit was understood as [conventional](https://www.conventionalcommits.org).
- 0 issues like '(#ID)' were seen in commit messages - 0 issues like '(#ID)' were seen in commit messages
@ -28,6 +57,7 @@ and this project adheres to
<details><summary>view details</summary> <details><summary>view details</summary>
* **Uncategorized** * **Uncategorized**
- Release dagger-sdk v0.2.21 ([`6937ef0`](https://github.com/kjuulh/dagger-sdk/commit/6937ef0ace797315013513aa7e2af39a9206a738))
- add new dagger-core-version ([`09881ee`](https://github.com/kjuulh/dagger-sdk/commit/09881ee39bdfb9201d104e4679a51c3b76b5fe27)) - add new dagger-core-version ([`09881ee`](https://github.com/kjuulh/dagger-sdk/commit/09881ee39bdfb9201d104e4679a51c3b76b5fe27))
</details> </details>

View File

@ -1,6 +1,6 @@
[package] [package]
name = "dagger-sdk" name = "dagger-sdk"
version = "0.2.21" version = "0.2.22"
edition = "2021" edition = "2021"
readme = "README.md" readme = "README.md"
license-file = "LICENSE.MIT" license-file = "LICENSE.MIT"
@ -11,7 +11,7 @@ publish = true
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dependencies] [dependencies]
dagger-core = { workspace = true, version = "0.2.10" } dagger-core = { workspace = true, version = "^0.2.11" }
eyre = { workspace = true } eyre = { workspace = true }
tokio = { workspace = true } tokio = { workspace = true }
@ -19,6 +19,7 @@ serde = { workspace = true }
serde_json = { workspace = true } serde_json = { workspace = true }
tracing.workspace = true tracing.workspace = true
tracing-subscriber.workspace = true tracing-subscriber.workspace = true
thiserror.workspace = true
futures = "0.3.28" futures = "0.3.28"
derive_builder = "0.12.0" derive_builder = "0.12.0"

View File

@ -5,20 +5,24 @@ use dagger_core::graphql_client::DefaultGraphQLClient;
use dagger_core::config::Config; use dagger_core::config::Config;
use dagger_core::engine::Engine as DaggerEngine; use dagger_core::engine::Engine as DaggerEngine;
use crate::errors::ConnectError;
use crate::gen::Query; use crate::gen::Query;
use crate::logging::StdLogger; use crate::logging::StdLogger;
use crate::querybuilder::query; use crate::querybuilder::query;
pub type DaggerConn = Arc<Query>; pub type DaggerConn = Arc<Query>;
pub async fn connect() -> eyre::Result<DaggerConn> { pub async fn connect() -> Result<DaggerConn, ConnectError> {
let cfg = Config::new(None, None, None, None, Some(Arc::new(StdLogger::default()))); let cfg = Config::new(None, None, None, None, Some(Arc::new(StdLogger::default())));
connect_opts(cfg).await connect_opts(cfg).await
} }
pub async fn connect_opts(cfg: Config) -> eyre::Result<DaggerConn> { pub async fn connect_opts(cfg: Config) -> Result<DaggerConn, ConnectError> {
let (conn, proc) = DaggerEngine::new().start(&cfg).await?; let (conn, proc) = DaggerEngine::new()
.start(&cfg)
.await
.map_err(ConnectError::FailedToConnect)?;
Ok(Arc::new(Query { Ok(Arc::new(Query {
proc: proc.map(|p| Arc::new(p)), proc: proc.map(|p| Arc::new(p)),

View File

@ -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),
}

View File

@ -1,3 +1,4 @@
use crate::errors::DaggerError;
use crate::querybuilder::Selection; use crate::querybuilder::Selection;
use dagger_core::graphql_client::DynGraphQLClient; use dagger_core::graphql_client::DynGraphQLClient;
use derive_builder::Builder; use derive_builder::Builder;
@ -121,7 +122,7 @@ pub struct CacheVolume {
} }
impl CacheVolume { impl CacheVolume {
pub async fn id(&self) -> eyre::Result<CacheId> { pub async fn id(&self) -> Result<CacheId, DaggerError> {
let query = self.selection.select("id"); let query = self.selection.select("id");
query.execute(self.graphql_client.clone()).await query.execute(self.graphql_client.clone()).await
@ -397,7 +398,7 @@ impl Container {
}; };
} }
/// Retrieves default arguments for future commands. /// Retrieves default arguments for future commands.
pub async fn default_args(&self) -> eyre::Result<Vec<String>> { pub async fn default_args(&self) -> Result<Vec<String>, DaggerError> {
let query = self.selection.select("defaultArgs"); let query = self.selection.select("defaultArgs");
query.execute(self.graphql_client.clone()).await query.execute(self.graphql_client.clone()).await
@ -427,7 +428,7 @@ impl Container {
/// # Arguments /// # Arguments
/// ///
/// * `opt` - optional argument, see inner type for documentation, use <func>_opts to use /// * `opt` - optional argument, see inner type for documentation, use <func>_opts to use
pub async fn endpoint(&self) -> eyre::Result<String> { pub async fn endpoint(&self) -> Result<String, DaggerError> {
let query = self.selection.select("endpoint"); let query = self.selection.select("endpoint");
query.execute(self.graphql_client.clone()).await query.execute(self.graphql_client.clone()).await
@ -441,7 +442,10 @@ impl Container {
/// # Arguments /// # Arguments
/// ///
/// * `opt` - optional argument, see inner type for documentation, use <func>_opts to use /// * `opt` - optional argument, see inner type for documentation, use <func>_opts to use
pub async fn endpoint_opts<'a>(&self, opts: ContainerEndpointOpts<'a>) -> eyre::Result<String> { pub async fn endpoint_opts<'a>(
&self,
opts: ContainerEndpointOpts<'a>,
) -> Result<String, DaggerError> {
let mut query = self.selection.select("endpoint"); let mut query = self.selection.select("endpoint");
if let Some(port) = opts.port { if let Some(port) = opts.port {
@ -454,7 +458,7 @@ impl Container {
query.execute(self.graphql_client.clone()).await query.execute(self.graphql_client.clone()).await
} }
/// Retrieves entrypoint to be prepended to the arguments of all commands. /// Retrieves entrypoint to be prepended to the arguments of all commands.
pub async fn entrypoint(&self) -> eyre::Result<Vec<String>> { pub async fn entrypoint(&self) -> Result<Vec<String>, DaggerError> {
let query = self.selection.select("entrypoint"); let query = self.selection.select("entrypoint");
query.execute(self.graphql_client.clone()).await query.execute(self.graphql_client.clone()).await
@ -464,7 +468,7 @@ impl Container {
/// # Arguments /// # Arguments
/// ///
/// * `name` - The name of the environment variable to retrieve (e.g., "PATH"). /// * `name` - The name of the environment variable to retrieve (e.g., "PATH").
pub async fn env_variable(&self, name: impl Into<String>) -> eyre::Result<String> { pub async fn env_variable(&self, name: impl Into<String>) -> Result<String, DaggerError> {
let mut query = self.selection.select("envVariable"); let mut query = self.selection.select("envVariable");
query = query.arg("name", name.into()); query = query.arg("name", name.into());
@ -531,7 +535,7 @@ impl Container {
} }
/// Exit code of the last executed command. Zero means success. /// 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. /// Will execute default command if none is set, or error if there's no default.
pub async fn exit_code(&self) -> eyre::Result<isize> { pub async fn exit_code(&self) -> Result<isize, DaggerError> {
let query = self.selection.select("exitCode"); let query = self.selection.select("exitCode");
query.execute(self.graphql_client.clone()).await query.execute(self.graphql_client.clone()).await
@ -545,7 +549,7 @@ impl Container {
/// * `path` - Host's destination path (e.g., "./tarball"). /// * `path` - Host's destination path (e.g., "./tarball").
/// Path can be relative to the engine's workdir or absolute. /// Path can be relative to the engine's workdir or absolute.
/// * `opt` - optional argument, see inner type for documentation, use <func>_opts to use /// * `opt` - optional argument, see inner type for documentation, use <func>_opts to use
pub async fn export(&self, path: impl Into<String>) -> eyre::Result<bool> { pub async fn export(&self, path: impl Into<String>) -> Result<bool, DaggerError> {
let mut query = self.selection.select("export"); let mut query = self.selection.select("export");
query = query.arg("path", path.into()); query = query.arg("path", path.into());
@ -566,7 +570,7 @@ impl Container {
&self, &self,
path: impl Into<String>, path: impl Into<String>,
opts: ContainerExportOpts, opts: ContainerExportOpts,
) -> eyre::Result<bool> { ) -> Result<bool, DaggerError> {
let mut query = self.selection.select("export"); let mut query = self.selection.select("export");
query = query.arg("path", path.into()); 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. /// Retrieves a hostname which can be used by clients to reach this container.
/// Currently experimental; set _EXPERIMENTAL_DAGGER_SERVICES_DNS=0 to disable. /// Currently experimental; set _EXPERIMENTAL_DAGGER_SERVICES_DNS=0 to disable.
pub async fn hostname(&self) -> eyre::Result<String> { pub async fn hostname(&self) -> Result<String, DaggerError> {
let query = self.selection.select("hostname"); let query = self.selection.select("hostname");
query.execute(self.graphql_client.clone()).await query.execute(self.graphql_client.clone()).await
} }
/// A unique identifier for this container. /// A unique identifier for this container.
pub async fn id(&self) -> eyre::Result<ContainerId> { pub async fn id(&self) -> Result<ContainerId, DaggerError> {
let query = self.selection.select("id"); let query = self.selection.select("id");
query.execute(self.graphql_client.clone()).await query.execute(self.graphql_client.clone()).await
} }
/// The unique image reference which can only be retrieved immediately after the 'Container.From' call. /// The unique image reference which can only be retrieved immediately after the 'Container.From' call.
pub async fn image_ref(&self) -> eyre::Result<String> { pub async fn image_ref(&self) -> Result<String, DaggerError> {
let query = self.selection.select("imageRef"); let query = self.selection.select("imageRef");
query.execute(self.graphql_client.clone()).await query.execute(self.graphql_client.clone()).await
@ -694,7 +698,7 @@ impl Container {
}; };
} }
/// Retrieves the value of the specified label. /// Retrieves the value of the specified label.
pub async fn label(&self, name: impl Into<String>) -> eyre::Result<String> { pub async fn label(&self, name: impl Into<String>) -> Result<String, DaggerError> {
let mut query = self.selection.select("label"); let mut query = self.selection.select("label");
query = query.arg("name", name.into()); query = query.arg("name", name.into());
@ -712,7 +716,7 @@ impl Container {
}]; }];
} }
/// Retrieves the list of paths where a directory is mounted. /// Retrieves the list of paths where a directory is mounted.
pub async fn mounts(&self) -> eyre::Result<Vec<String>> { pub async fn mounts(&self) -> Result<Vec<String>, DaggerError> {
let query = self.selection.select("mounts"); let query = self.selection.select("mounts");
query.execute(self.graphql_client.clone()).await query.execute(self.graphql_client.clone()).await
@ -763,7 +767,7 @@ impl Container {
}; };
} }
/// The platform this container executes and publishes as. /// The platform this container executes and publishes as.
pub async fn platform(&self) -> eyre::Result<Platform> { pub async fn platform(&self) -> Result<Platform, DaggerError> {
let query = self.selection.select("platform"); let query = self.selection.select("platform");
query.execute(self.graphql_client.clone()).await 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"). /// Formatted as [host]/[user]/[repo]:[tag] (e.g. "docker.io/dagger/dagger:main").
/// * `opt` - optional argument, see inner type for documentation, use <func>_opts to use /// * `opt` - optional argument, see inner type for documentation, use <func>_opts to use
pub async fn publish(&self, address: impl Into<String>) -> eyre::Result<String> { pub async fn publish(&self, address: impl Into<String>) -> Result<String, DaggerError> {
let mut query = self.selection.select("publish"); let mut query = self.selection.select("publish");
query = query.arg("address", address.into()); query = query.arg("address", address.into());
@ -800,7 +804,7 @@ impl Container {
&self, &self,
address: impl Into<String>, address: impl Into<String>,
opts: ContainerPublishOpts, opts: ContainerPublishOpts,
) -> eyre::Result<String> { ) -> Result<String, DaggerError> {
let mut query = self.selection.select("publish"); let mut query = self.selection.select("publish");
query = query.arg("address", address.into()); query = query.arg("address", address.into());
@ -822,20 +826,20 @@ impl Container {
} }
/// The error stream of the last executed command. /// The error stream of the last executed command.
/// Will execute default command if none is set, or error if there's no default. /// Will execute default command if none is set, or error if there's no default.
pub async fn stderr(&self) -> eyre::Result<String> { pub async fn stderr(&self) -> Result<String, DaggerError> {
let query = self.selection.select("stderr"); let query = self.selection.select("stderr");
query.execute(self.graphql_client.clone()).await query.execute(self.graphql_client.clone()).await
} }
/// The output stream of the last executed command. /// The output stream of the last executed command.
/// Will execute default command if none is set, or error if there's no default. /// Will execute default command if none is set, or error if there's no default.
pub async fn stdout(&self) -> eyre::Result<String> { pub async fn stdout(&self) -> Result<String, DaggerError> {
let query = self.selection.select("stdout"); let query = self.selection.select("stdout");
query.execute(self.graphql_client.clone()).await query.execute(self.graphql_client.clone()).await
} }
/// Retrieves the user to be set for all commands. /// Retrieves the user to be set for all commands.
pub async fn user(&self) -> eyre::Result<String> { pub async fn user(&self) -> Result<String, DaggerError> {
let query = self.selection.select("user"); let query = self.selection.select("user");
query.execute(self.graphql_client.clone()).await query.execute(self.graphql_client.clone()).await
@ -1720,7 +1724,7 @@ impl Container {
}; };
} }
/// Retrieves the working directory for all commands. /// Retrieves the working directory for all commands.
pub async fn workdir(&self) -> eyre::Result<String> { pub async fn workdir(&self) -> Result<String, DaggerError> {
let query = self.selection.select("workdir"); let query = self.selection.select("workdir");
query.execute(self.graphql_client.clone()).await query.execute(self.graphql_client.clone()).await
@ -1882,7 +1886,7 @@ impl Directory {
/// # Arguments /// # Arguments
/// ///
/// * `opt` - optional argument, see inner type for documentation, use <func>_opts to use /// * `opt` - optional argument, see inner type for documentation, use <func>_opts to use
pub async fn entries(&self) -> eyre::Result<Vec<String>> { pub async fn entries(&self) -> Result<Vec<String>, DaggerError> {
let query = self.selection.select("entries"); let query = self.selection.select("entries");
query.execute(self.graphql_client.clone()).await query.execute(self.graphql_client.clone()).await
@ -1896,7 +1900,7 @@ impl Directory {
pub async fn entries_opts<'a>( pub async fn entries_opts<'a>(
&self, &self,
opts: DirectoryEntriesOpts<'a>, opts: DirectoryEntriesOpts<'a>,
) -> eyre::Result<Vec<String>> { ) -> Result<Vec<String>, DaggerError> {
let mut query = self.selection.select("entries"); let mut query = self.selection.select("entries");
if let Some(path) = opts.path { if let Some(path) = opts.path {
@ -1910,7 +1914,7 @@ impl Directory {
/// # Arguments /// # Arguments
/// ///
/// * `path` - Location of the copied directory (e.g., "logs/"). /// * `path` - Location of the copied directory (e.g., "logs/").
pub async fn export(&self, path: impl Into<String>) -> eyre::Result<bool> { pub async fn export(&self, path: impl Into<String>) -> Result<bool, DaggerError> {
let mut query = self.selection.select("export"); let mut query = self.selection.select("export");
query = query.arg("path", path.into()); query = query.arg("path", path.into());
@ -1934,7 +1938,7 @@ impl Directory {
}; };
} }
/// The content-addressed identifier of the directory. /// The content-addressed identifier of the directory.
pub async fn id(&self) -> eyre::Result<DirectoryId> { pub async fn id(&self) -> Result<DirectoryId, DaggerError> {
let query = self.selection.select("id"); let query = self.selection.select("id");
query.execute(self.graphql_client.clone()).await query.execute(self.graphql_client.clone()).await
@ -2242,13 +2246,13 @@ pub struct EnvVariable {
impl EnvVariable { impl EnvVariable {
/// The environment variable name. /// The environment variable name.
pub async fn name(&self) -> eyre::Result<String> { pub async fn name(&self) -> Result<String, DaggerError> {
let query = self.selection.select("name"); let query = self.selection.select("name");
query.execute(self.graphql_client.clone()).await query.execute(self.graphql_client.clone()).await
} }
/// The environment variable value. /// The environment variable value.
pub async fn value(&self) -> eyre::Result<String> { pub async fn value(&self) -> Result<String, DaggerError> {
let query = self.selection.select("value"); let query = self.selection.select("value");
query.execute(self.graphql_client.clone()).await query.execute(self.graphql_client.clone()).await
@ -2263,7 +2267,7 @@ pub struct File {
impl File { impl File {
/// Retrieves the contents of the file. /// Retrieves the contents of the file.
pub async fn contents(&self) -> eyre::Result<String> { pub async fn contents(&self) -> Result<String, DaggerError> {
let query = self.selection.select("contents"); let query = self.selection.select("contents");
query.execute(self.graphql_client.clone()).await query.execute(self.graphql_client.clone()).await
@ -2273,7 +2277,7 @@ impl File {
/// # Arguments /// # Arguments
/// ///
/// * `path` - Location of the written directory (e.g., "output.txt"). /// * `path` - Location of the written directory (e.g., "output.txt").
pub async fn export(&self, path: impl Into<String>) -> eyre::Result<bool> { pub async fn export(&self, path: impl Into<String>) -> Result<bool, DaggerError> {
let mut query = self.selection.select("export"); let mut query = self.selection.select("export");
query = query.arg("path", path.into()); query = query.arg("path", path.into());
@ -2281,7 +2285,7 @@ impl File {
query.execute(self.graphql_client.clone()).await query.execute(self.graphql_client.clone()).await
} }
/// Retrieves the content-addressed identifier of the file. /// Retrieves the content-addressed identifier of the file.
pub async fn id(&self) -> eyre::Result<FileId> { pub async fn id(&self) -> Result<FileId, DaggerError> {
let query = self.selection.select("id"); let query = self.selection.select("id");
query.execute(self.graphql_client.clone()).await query.execute(self.graphql_client.clone()).await
@ -2297,7 +2301,7 @@ impl File {
}; };
} }
/// Gets the size of the file, in bytes. /// Gets the size of the file, in bytes.
pub async fn size(&self) -> eyre::Result<isize> { pub async fn size(&self) -> Result<isize, DaggerError> {
let query = self.selection.select("size"); let query = self.selection.select("size");
query.execute(self.graphql_client.clone()).await query.execute(self.graphql_client.clone()).await
@ -2338,7 +2342,7 @@ pub struct GitRefTreeOpts<'a> {
impl GitRef { impl GitRef {
/// The digest of the current value of this ref. /// The digest of the current value of this ref.
pub async fn digest(&self) -> eyre::Result<String> { pub async fn digest(&self) -> Result<String, DaggerError> {
let query = self.selection.select("digest"); let query = self.selection.select("digest");
query.execute(self.graphql_client.clone()).await query.execute(self.graphql_client.clone()).await
@ -2405,7 +2409,7 @@ impl GitRepository {
}; };
} }
/// Lists of branches on the repository. /// Lists of branches on the repository.
pub async fn branches(&self) -> eyre::Result<Vec<String>> { pub async fn branches(&self) -> Result<Vec<String>, DaggerError> {
let query = self.selection.select("branches"); let query = self.selection.select("branches");
query.execute(self.graphql_client.clone()).await query.execute(self.graphql_client.clone()).await
@ -2443,7 +2447,7 @@ impl GitRepository {
}; };
} }
/// Lists of tags on the repository. /// Lists of tags on the repository.
pub async fn tags(&self) -> eyre::Result<Vec<String>> { pub async fn tags(&self) -> Result<Vec<String>, DaggerError> {
let query = self.selection.select("tags"); let query = self.selection.select("tags");
query.execute(self.graphql_client.clone()).await query.execute(self.graphql_client.clone()).await
@ -2609,7 +2613,7 @@ impl HostVariable {
}; };
} }
/// The value of this variable. /// The value of this variable.
pub async fn value(&self) -> eyre::Result<String> { pub async fn value(&self) -> Result<String, DaggerError> {
let query = self.selection.select("value"); let query = self.selection.select("value");
query.execute(self.graphql_client.clone()).await query.execute(self.graphql_client.clone()).await
@ -2624,13 +2628,13 @@ pub struct Label {
impl Label { impl Label {
/// The label name. /// The label name.
pub async fn name(&self) -> eyre::Result<String> { pub async fn name(&self) -> Result<String, DaggerError> {
let query = self.selection.select("name"); let query = self.selection.select("name");
query.execute(self.graphql_client.clone()).await query.execute(self.graphql_client.clone()).await
} }
/// The label value. /// The label value.
pub async fn value(&self) -> eyre::Result<String> { pub async fn value(&self) -> Result<String, DaggerError> {
let query = self.selection.select("value"); let query = self.selection.select("value");
query.execute(self.graphql_client.clone()).await query.execute(self.graphql_client.clone()).await
@ -2645,19 +2649,19 @@ pub struct Port {
impl Port { impl Port {
/// The port description. /// The port description.
pub async fn description(&self) -> eyre::Result<String> { pub async fn description(&self) -> Result<String, DaggerError> {
let query = self.selection.select("description"); let query = self.selection.select("description");
query.execute(self.graphql_client.clone()).await query.execute(self.graphql_client.clone()).await
} }
/// The port number. /// The port number.
pub async fn port(&self) -> eyre::Result<isize> { pub async fn port(&self) -> Result<isize, DaggerError> {
let query = self.selection.select("port"); let query = self.selection.select("port");
query.execute(self.graphql_client.clone()).await query.execute(self.graphql_client.clone()).await
} }
/// The transport layer network protocol. /// The transport layer network protocol.
pub async fn protocol(&self) -> eyre::Result<NetworkProtocol> { pub async fn protocol(&self) -> Result<NetworkProtocol, DaggerError> {
let query = self.selection.select("protocol"); let query = self.selection.select("protocol");
query.execute(self.graphql_client.clone()).await query.execute(self.graphql_client.clone()).await
@ -2692,25 +2696,25 @@ impl Project {
}; };
} }
/// install the project's schema /// install the project's schema
pub async fn install(&self) -> eyre::Result<bool> { pub async fn install(&self) -> Result<bool, DaggerError> {
let query = self.selection.select("install"); let query = self.selection.select("install");
query.execute(self.graphql_client.clone()).await query.execute(self.graphql_client.clone()).await
} }
/// name of the project /// name of the project
pub async fn name(&self) -> eyre::Result<String> { pub async fn name(&self) -> Result<String, DaggerError> {
let query = self.selection.select("name"); let query = self.selection.select("name");
query.execute(self.graphql_client.clone()).await query.execute(self.graphql_client.clone()).await
} }
/// schema provided by the project /// schema provided by the project
pub async fn schema(&self) -> eyre::Result<String> { pub async fn schema(&self) -> Result<String, DaggerError> {
let query = self.selection.select("schema"); let query = self.selection.select("schema");
query.execute(self.graphql_client.clone()).await query.execute(self.graphql_client.clone()).await
} }
/// sdk used to generate code for and/or execute this project /// sdk used to generate code for and/or execute this project
pub async fn sdk(&self) -> eyre::Result<String> { pub async fn sdk(&self) -> Result<String, DaggerError> {
let query = self.selection.select("sdk"); let query = self.selection.select("sdk");
query.execute(self.graphql_client.clone()).await query.execute(self.graphql_client.clone()).await
@ -2825,7 +2829,7 @@ impl Query {
}; };
} }
/// The default platform of the builder. /// The default platform of the builder.
pub async fn default_platform(&self) -> eyre::Result<Platform> { pub async fn default_platform(&self) -> Result<Platform, DaggerError> {
let query = self.selection.select("defaultPlatform"); let query = self.selection.select("defaultPlatform");
query.execute(self.graphql_client.clone()).await query.execute(self.graphql_client.clone()).await
@ -3094,13 +3098,13 @@ pub struct Secret {
impl Secret { impl Secret {
/// The identifier for this secret. /// The identifier for this secret.
pub async fn id(&self) -> eyre::Result<SecretId> { pub async fn id(&self) -> Result<SecretId, DaggerError> {
let query = self.selection.select("id"); let query = self.selection.select("id");
query.execute(self.graphql_client.clone()).await query.execute(self.graphql_client.clone()).await
} }
/// The value of this secret. /// The value of this secret.
pub async fn plaintext(&self) -> eyre::Result<String> { pub async fn plaintext(&self) -> Result<String, DaggerError> {
let query = self.selection.select("plaintext"); let query = self.selection.select("plaintext");
query.execute(self.graphql_client.clone()).await query.execute(self.graphql_client.clone()).await
@ -3115,7 +3119,7 @@ pub struct Socket {
impl Socket { impl Socket {
/// The content-addressed identifier of the socket. /// The content-addressed identifier of the socket.
pub async fn id(&self) -> eyre::Result<SocketId> { pub async fn id(&self) -> Result<SocketId, DaggerError> {
let query = self.selection.select("id"); let query = self.selection.select("id");
query.execute(self.graphql_client.clone()).await query.execute(self.graphql_client.clone()).await

View File

@ -1,6 +1,7 @@
#![deny(warnings)] #![deny(warnings)]
mod client; mod client;
pub mod errors;
mod gen; mod gen;
pub mod logging; pub mod logging;
mod querybuilder; mod querybuilder;

View File

@ -1,9 +1,10 @@
use std::{collections::HashMap, ops::Add, sync::Arc}; use std::{collections::HashMap, ops::Add, sync::Arc};
use dagger_core::graphql_client::DynGraphQLClient; use dagger_core::graphql_client::DynGraphQLClient;
use eyre::Context;
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
use crate::errors::{DaggerError, DaggerUnpackError};
pub fn query() -> Selection { pub fn query() -> Selection {
Selection::default() Selection::default()
} }
@ -92,7 +93,7 @@ impl Selection {
s s
} }
pub fn build(&self) -> eyre::Result<String> { pub fn build(&self) -> Result<String, DaggerError> {
let mut fields = vec!["query".to_string()]; let mut fields = vec!["query".to_string()];
for sel in self.path() { for sel in self.path() {
@ -117,7 +118,7 @@ impl Selection {
Ok(fields.join("{") + &"}".repeat(fields.len() - 1)) Ok(fields.join("{") + &"}".repeat(fields.len() - 1))
} }
pub async fn execute<D>(&self, gql_client: DynGraphQLClient) -> eyre::Result<D> pub async fn execute<D>(&self, gql_client: DynGraphQLClient) -> Result<D, DaggerError>
where where
D: for<'de> Deserialize<'de>, D: for<'de> Deserialize<'de>,
{ {
@ -127,7 +128,7 @@ impl Selection {
let resp: Option<serde_json::Value> = match gql_client.query(&query).await { let resp: Option<serde_json::Value> = match gql_client.query(&query).await {
Ok(r) => r, Ok(r) => r,
Err(e) => eyre::bail!(e), Err(e) => return Err(DaggerError::Query(e)),
}; };
let resp: Option<D> = self.unpack_resp(resp)?; let resp: Option<D> = self.unpack_resp(resp)?;
@ -151,7 +152,10 @@ impl Selection {
selections selections
} }
pub(crate) fn unpack_resp<D>(&self, resp: Option<serde_json::Value>) -> eyre::Result<Option<D>> pub(crate) fn unpack_resp<D>(
&self,
resp: Option<serde_json::Value>,
) -> Result<Option<D>, DaggerError>
where where
D: for<'de> Deserialize<'de>, D: for<'de> Deserialize<'de>,
{ {
@ -161,21 +165,23 @@ impl Selection {
} }
} }
fn unpack_resp_value<D>(&self, r: serde_json::Value) -> eyre::Result<D> fn unpack_resp_value<D>(&self, r: serde_json::Value) -> Result<D, DaggerError>
where where
D: for<'de> Deserialize<'de>, D: for<'de> Deserialize<'de>,
{ {
if let Some(o) = r.as_object() { if let Some(o) = r.as_object() {
let keys = o.keys(); let keys = o.keys();
if keys.len() != 1 { 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(); let first = keys.into_iter().next().unwrap();
return self.unpack_resp_value(o.get(first).unwrap().clone()); return self.unpack_resp_value(o.get(first).unwrap().clone());
} }
serde_json::from_value::<D>(r).context("could not deserialize response") serde_json::from_value::<D>(r)
.map_err(DaggerUnpackError::Deserialize)
.map_err(DaggerError::Unpack)
} }
} }

View File

@ -111,9 +111,9 @@ async fn test_err_message() {
assert_eq!(alpine.is_err(), true); assert_eq!(alpine.is_err(), true);
let err = alpine.expect_err("Tests expect err"); let err = alpine.expect_err("Tests expect err");
let error_msg = r#" let error_msg = r#"failed to query dagger engine: domain error:
GQLClient Error: Look at json field for more details 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 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); assert_eq!(err.to_string().as_str(), error_msg);