Skip to content
This repository has been archived by the owner on Jun 29, 2023. It is now read-only.

feat: extract client #48

Merged
merged 2 commits into from
Mar 19, 2023
Merged

feat: extract client #48

merged 2 commits into from
Mar 19, 2023

Conversation

kjuulh
Copy link
Owner

@kjuulh kjuulh commented Mar 18, 2023

This extracts the client (strategy pattern), this is so that it is will be possible to test the actual querier, without hitting / requiring the dagger-engine running.

@kjuulh kjuulh changed the title feat/extract client feat: extract client Mar 18, 2023
@kjuulh kjuulh enabled auto-merge (squash) March 18, 2023 23:54
@kjuulh
Copy link
Owner Author

kjuulh commented Mar 18, 2023

context: I've got an idea of how to get this to work.

First of all the current problem is that I'd like to be able to have a generic reqwest client, such that.

#[async_trait]
pub trait GQLClient {
  async fn <T>query(&self, query: &str) -> eyre::Result<T>
    where T: for<'de> Deserializer<'de>;
]

The problem is that T or rather Deserializer isn't object safe. As such I've got a few ideas. Either return the raw result, this means swapping out gql_client to reqwest pure, this is not a huge deal, but something I'd rather have to avoid. It would give one less dependency though. But now that I am doing this shiz, I thought I'd explore how to solve this problem.

One object is to erase the generics using: https://github.com/dtolnay/erased-serde. This would change the structure of the code a bit.

trait Querializer {}

trait ErasedGqlClient {
    fn erased_query(&self, querializer: &dyn Querializer);
}

trait GqlClient {
    // Not object safe because of this generic method.
    fn query<Q: Querializer>(&self, querializer: Q);
}

impl GqlClient for dyn ErasedGqlClient {
    fn query<Q: Querializer>(&self, querializer: Q) {
        self.erased_query(&querializer)
    }
}

impl<T> ErasedGqlClient for T
where
    T: GqlClient,
{
    fn erased_fn(&self, querializer: &dyn Querializer) {
        self.query(querializer)
    }
}

    pub async fn execute<D>(&self, gql_client: ErasedGqlClient) -> eyre::Result<D>
    where
        D: for<'de> Deserialize<'de>,
    {
        let query = self.build()?;

        tracing::trace!(query = query.as_str(), "dagger-query");

        let resp: Option<serde_json::Value> = match gql_client.query(&query).await {
            Ok(r) => r,
            Err(e) => eyre::bail!(e),
        };

        let resp: Option<D> = self.unpack_resp(resp)?;

        Ok(resp.unwrap())
    }

or something along those lines. It may actually have to be done in the generated code to be successful. I could also go full ham and add an implementation for each object to get rid of the generic type, but that probably would be overkill and a pain to mock yourself.

@kjuulh kjuulh merged commit 11d2093 into main Mar 19, 2023
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Expose a trait for the querier instead of hard coded reqwest so that we can unit test the generated graphql
1 participant