-
Notifications
You must be signed in to change notification settings - Fork 88
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Add tower for use with http/graphql clients #2067
Conversation
07d3389
to
b7f87eb
Compare
f4fd8c6
to
0f5de57
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Overall LGTM! Code is clean and very easy to follow. My only ask would be to add comments and tests to the rover-graphql
and rover-http
crates.
@@ -2,6 +2,7 @@ use std::collections::HashMap; | |||
|
|||
use crate::operations::graph::introspect::runner::graph_introspect_query; | |||
|
|||
#[cfg(test)] |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Is this supposed to be here?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yep. This is a little weird, but this is used only in tests. I had removed the whole thing previously as it wasn't used in the released binary, but then it ended up breaking the tests.
pub struct SubgraphIntrospectInput { | ||
pub headers: HashMap<String, String>, | ||
} | ||
pub struct SubgraphIntrospectInput {} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Could this just be a unit struct?
pub struct SubgraphIntrospectInput {} | |
pub struct SubgraphIntrospectInput; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This could be, but usually I defer to using an empty struct unless it's specifically a marker / empty by design. In this case, we're just not expecting anything as input, but could be expanded to require something later.
All said and done, they're exactly the same in practice it's just a personal preference here.
.map(|result| match result { | ||
Ok(data) => Q::map_response(data), | ||
Err(err) => { | ||
if err.to_string().contains("Cannot query field") { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Is there any way to assert a concrete type here as opposed to a strcmp? Or do we not own this error?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This comes from the errors
field in a GraphQL response, so the error is a little-bit free form and produced by the subgraph's server implementation. There is probably a way to be more specific about this since we kinda know that the error case exists, but I left it alone here mostly because I think it belongs to a separate type of PR to classify that type of error.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
in favor! approving to clear the way; though, tests look like they might be failing (not sure if it's ephemeral--haven't looked) and we might want to figure out whether this should be its own release or what makes sense for it to ship with
0f5de57
to
c61e7c6
Compare
7ccc5c6
to
8b5d98c
Compare
5d7cefe
to
a12fe25
Compare
8abe179
to
e5445cc
Compare
I have removed the touchpoints for this with the existing rover crates (rover, rover-client, sputnik) as to provide the tooling outright, but to be more cautious about how it is implemented through the rover stack. |
## 🐛 Fixes - **Avoid misleading warning when `--output` is not specified - @glasser #2100** In the release of v0.26.1 logic was added to disable the output flag if the Federation version was less than 2.9, however this was being printed even when the `--output` flag was not supplied. This has been corrected. - **Improve `--graph-ref` option - @glasser #2101** In the release of v0.26.0 the `--graph-ref` option was added to `supergraph compose` as well as `rover dev`. However, the behaviour when `--graph-ref` was used in conjunction with `--config` did not work as documented. This is now fixed. Furthermore, both `rover dev` and `supergraph compose`, when using only the `--graph-ref` option, respect the graph ref's Federation version. - **Further improve `--graph-ref` option - @glasser #2105** Improves on the above by fixing some corner cases that prevented #2101 from working as intended ## 🛠 Maintenance - **Update `eslint` to v9.10.0 - @jonathanrainer #2106** - **Update `concurrently` to v9.0.0 - @jonathanrainer #2108** - **Update `manylinux` CI Docker Image to v2024.09.09 - @jonathanrainer #2110** - **Update Rust to v1.81.0 - @jonathanrainer #2107** - **Pass GitHub Tag to GitHub Actions Workflow - @glasser #2109** - **Add `tower` for use with HTTP/GraphQL clients - @dotdat #2067** ## 📚 Documentation - **Fix Glossary links - @Meschreiber @pnodet #2114**
- **Avoid misleading warning when `--output` is not specified - @glasser In the release of v0.26.1 logic was added to disable the output flag if the Federation version was less than 2.9, however this was being printed even when the `--output` flag was not supplied. This has been corrected. - **Improve `--graph-ref` option - @glasser #2101** In the release of v0.26.0 the `--graph-ref` option was added to `supergraph compose` as well as `rover dev`. However, the behaviour when `--graph-ref` was used in conjunction with `--config` did not work as documented. This is now fixed. Furthermore, both `rover dev` and `supergraph compose`, when using only the `--graph-ref` option, respect the graph ref's Federation version. - **Further improve `--graph-ref` option - @glasser #2105** Improves on the above by fixing some corner cases that prevented #2101 from working as intended - **Update `eslint` to v9.10.0 - @jonathanrainer #2106** - **Update `concurrently` to v9.0.0 - @jonathanrainer #2108** - **Update `manylinux` CI Docker Image to v2024.09.09 - @jonathanrainer - **Update Rust to v1.81.0 - @jonathanrainer #2107** - **Pass GitHub Tag to GitHub Actions Workflow - @glasser #2109** - **Add `tower` for use with HTTP/GraphQL clients - @dotdat #2067** - **Fix Glossary links - @Meschreiber @pnodet #2114**
This PR originally started by looking at how we might use clients (studio, graphql, http) that we could construct at the top of a command and then pass through, fully-formed to the place in which it is used.
However, during this discovery, I found a few requirements that needed to be addressed:
Any HTTP client created needs to respect the global
--accept-invalid-hostnames
and--accept-invalid-certs
flags.This is why we construct and re-use a
reqwest::Client and reqwest::ClientBuilder
in our config. When callingget_reqwest_client
andget_reqwest_client_builder
, this configuration is pre-filled.HTTP/GraphQL clients aren't uniform throughout rover commands
There are a number of use cases that a client might perform:
Each one of these use cases may attach their own headers, timeouts, or retry logic, which is why we tend to construct our clients at the last possible moment.
reqwest
supports a few nice things out of the box, such as proxiesThere is a hazard in swapping out
reqwest
for something likehyper
, as we are not entirely aware whether rover users are using any of these hidden features.Given that, there are some improvements we could be make to our client ecosystem that makes the construction of these clients against their specific use cases more transparent / testable / modifiable in the future.
If we look at the use cases laid out currently, we can see that our client construction and use case has a few knobs that generally get tuned:
With that, a tool like tower allows us to perform these types of tasks in a more declarative and composable way than passing around a
ClientBuilder
or manipulating headers ad hoc.Initially,
reqwest
looked incompatible withtower
based on its API. However, tower-reqwest seems to fill in that gap, by providing a way to wrap a client as aService
and apply tower layers to the client.In this PR, I centralize http requests around http types and bytes for a robust body type.
In addition, GraphQL and Studio clients can be reduced to
tower
layers that perform their original tasks.One note here is that the GraphQL layer omits internal retries and instead relies on an HttpService that provides retries (or not) as all retry logic is currently based on HTTP reasoning.