Skip to content
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

chore: refactor rover-client in pursuit of structured output #557

Merged
merged 23 commits into from
Jul 26, 2021
Merged
Show file tree
Hide file tree
Changes from 19 commits
Commits
Show all changes
23 commits
Select commit Hold shift + click to select a range
ffeb2ad
chore: refactor subgraph check
EverlastingBugstopper May 20, 2021
42f0bc8
chore: refactor subgraph fetch (#575)
EverlastingBugstopper Jun 23, 2021
b5db7ba
chore: refactor subgraph publish (#630)
EverlastingBugstopper Jun 25, 2021
26eda8b
chore: refactor config whoami (#633)
EverlastingBugstopper Jun 28, 2021
82aed51
chore: refactor subgraph delete (#639)
EverlastingBugstopper Jun 28, 2021
d74307b
chore: refactor subgraph list (#640)
EverlastingBugstopper Jun 28, 2021
8d0aabf
chore: refactor subgraph introspect (#641)
EverlastingBugstopper Jun 28, 2021
ca2cdde
chore: refactor graph introspect (#643)
EverlastingBugstopper Jun 30, 2021
7555b91
chore: refactor release update checker (#646)
EverlastingBugstopper Jul 1, 2021
9e83b88
Merge branch 'main' into avery/refactor-subgraph-check
EverlastingBugstopper Jul 6, 2021
e29b87b
chore: begin adding shared types and consolidate check operations (#652)
EverlastingBugstopper Jul 6, 2021
6ac35e1
chore: move GraphRef to rover-client (#664)
EverlastingBugstopper Jul 7, 2021
48cda70
Merge branch 'main' into avery/refactor-subgraph-check
EverlastingBugstopper Jul 13, 2021
9c9e487
chore: refactor the rest of rover-client (#675)
EverlastingBugstopper Jul 15, 2021
deffb4e
Merge branch 'main' into avery/refactor-subgraph-check
EverlastingBugstopper Jul 15, 2021
20fd54f
chore: do not re-export queries
EverlastingBugstopper Jul 15, 2021
a16f4f7
chore: finish wiring OperationCheck error
EverlastingBugstopper Jul 15, 2021
1e7969c
Merge branch 'main' into avery/refactor-subgraph-check
EverlastingBugstopper Jul 19, 2021
3988a5b
chore: adds graphql linter (#677)
EverlastingBugstopper Jul 19, 2021
aba1d85
Merge branch 'main' into avery/refactor-subgraph-check
EverlastingBugstopper Jul 22, 2021
9bc37ec
fix: graph_ref -> graphref
EverlastingBugstopper Jul 22, 2021
5138bcd
feat: structured output (#676)
EverlastingBugstopper Jul 26, 2021
c67d46d
Merge branch 'main' into avery/refactor-subgraph-check
EverlastingBugstopper Jul 26, 2021
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
44 changes: 23 additions & 21 deletions ARCHITECTURE.md
Original file line number Diff line number Diff line change
Expand Up @@ -195,7 +195,7 @@ To add these to our new `graph hello` command, we can copy and paste the field f
pub struct Hello {
/// <NAME>@<VARIANT> of graph in Apollo Studio to publish to.
/// @<VARIANT> may be left off, defaulting to @current
#[structopt(name = "GRAPH_REF", parse(try_from_str = parse_graph_ref))]
#[structopt(name = "GRAPH_REF"))]
#[serde(skip_serializing)]
graph: GraphRef

Expand All @@ -206,12 +206,6 @@ pub struct Hello {
}
```

We'll have to also add some import statements at the top of our file to support parsing this new argument:

```rust
use crate::utils::parsers::{parse_graph_ref, GraphRef};
```

Now if we run the command again, it will complain if we don't provide a graph ref:

```console
Expand All @@ -228,13 +222,13 @@ For more information try --help

##### Setting up a command to work with `rover-client`

Most of Rover's commands make requests to Apollo Studio's API. Rather than handling the request logic in the repository's main package, Rover is structured so that this logic lives in `crates/rover-client`. This is helpful for separation of concerns and testing.
Most of Rover's commands make requests to Apollo Studio's API, or to another GraphQL API. Rather than handling the request logic in the repository's main package, Rover is structured so that this logic lives in `crates/rover-client`. This is helpful for separation of concerns and testing.

To access functionality from `rover-client` in our `rover graph hello` command, we'll need to pass down a client from the entry to our command in `src/command/graph/mod.rs`.

You can do this by changing the `Command::Hello(command) => command.run(),` line to `Command::Hello(command) => command.run(client_config),`.

Then you'll need to change `Hello::run` to accept a `client_config: StudioClientConfig` parameter in `src/command/graph/hello.rs`, and add a `use crate::utils::client::StudioClientConfig` import statement. Then, at the top of the run function, you can create a `StudioClient` by adding `let client = client_config.get_client(&self.profile_name)?;`. You can see examples of this in the other commands.
Then you'll need to change `Hello::run` to accept a `client_config: StudioClientConfig` parameter in `src/command/graph/hello.rs`, and add a `use crate::utils::client::StudioClientConfig` import statement. Then, at the top of the run function, you can create a `StudioClient` by adding `let client = client_config.get_authenticated_client(&self.profile_name)?;`. You can see examples of this in the other commands.

##### Auto-generated help command

Expand Down Expand Up @@ -271,19 +265,19 @@ Whenever you create a new command, make sure to add `#[serde(skip_serializing)]`

##### Adding a query to Apollo Studio

The only piece of the `rover-client` crate that we need to be concerned with for now is the `src/query` directory. This is where all the queries to Apollo Studio live. This directory is roughly organized by the command names as well, but there might be some queries in these directories that are used by multiple commands.
The only piece of the `rover-client` crate that we need to be concerned with for now is the `src/operations` directory. This is where all the queries to Apollo Studio live. This directory is roughly organized by the command names as well, but there might be some queries in these directories that are used by multiple commands.

You can see in the `src/query/graph` directory a number of `.rs` files paired with `.graphql` files. The `.graphql` files are the files where the GraphQL operations live, and the matching `.rs` files contain the logic needed to execute those operations.
You can see in the `src/operations/graph` directory a number of `.rs` files paired with `.graphql` files. The `.graphql` files are the files where the GraphQL operations live, and the matching `.rs` files contain the logic needed to execute those operations.

##### Writing a GraphQL operation

For our basic `graph hello` command, we're going to make a request to Apollo Studio that inquires about the existence of a particular graph, and nothing else. For this, we can use the `Query.service` field.

Create a `hello.graphql` file in `crates/rover-client/src/query/graph` and paste the following into it:
Create a `hello_query.graphql` file in `crates/rover-client/src/operations/graph` and paste the following into it:

```graphql
query GraphHello($graphId: ID!) {
service(id: $graphId) {
query GraphHello($graph_id: ID!) {
service(id: $graph_id) {
deletedAt
}
}
Expand All @@ -295,32 +289,34 @@ This basic GraphQL operation uses a graph's unique ID (which we get from the `Gr

This project uses [graphql-client](https://docs.rs/graphql_client/latest/graphql_client/) to generate types for each raw `.graphql` query that we write.

First, create an empty file at `crates/rover-client/src/query/graph/hello.rs`.
First, create an empty directory at `crates/rover-client/src/operations/graph/hello`, and then in that directory, create a `mod.rs` file to initialize the module.

To start compiling this file, we need to export the module in `crates/rover-client/src/query/graph/mod.rs`:
To start compiling this file, we need to export the module in `crates/rover-client/src/operations/graph/mod.rs`:

```rust
...
/// "Graph hello" command execution
/// "graph hello" command execution
pub mod hello;
```

Back in `hello.rs`, we'll import the following types:
Back in our `hello` module, we'll create a `runner.rs`, and add `mod runner` to our `mod.rs` file.

Then, in `runner.rs`, import the following types:

```rust
use crate::blocking::StudioClient;
use crate::RoverClientError;
use graphql_client::*;
```

Then, we'll create a new struct that will have auto-generated types for the `hello.graphql` file that we created earlier:
Then, we'll create a new struct that will have auto-generated types for the `hello_query.graphql` file that we created earlier:

```rust
#[derive(GraphQLQuery)]
// The paths are relative to the directory where your `Cargo.toml` is located.
// Both json and the GraphQL schema language are supported as sources for the schema
#[graphql(
query_path = "src/query/graph/hello.graphql",
query_path = "src/operations/graph/hello/hello_query.graphql",
schema_path = ".schema/schema.graphql",
response_derives = "PartialEq, Debug, Serialize, Deserialize",
deprecated = "warn"
Expand Down Expand Up @@ -395,7 +391,13 @@ fn build_response(
}
```

This should get you to the point where you can run `rover graph hello <GRAPH_REF>` and see if and when the last graph was deleted. From here, you should be able to follow the examples of other commands to write out tests for the `build_response` function. This is left as an exercise for the reader.
This should get you to the point where you can run `rover graph hello <GRAPH_REF>` and see if and when the last graph was deleted. From here, you should be able to follow the examples of other commands to write out tests for the `build_response` function.

##### Clean up the API

Unfortunately this is not the cleanest API and doesn't match the pattern set by the rest of the commands. Each `rover-client` operation has an input type and an output type, along with a `run` function that takes in a `reqwest::blocking::Client`.

You'll want to define all of the types scoped to this command in `types.rs`, and re-export them from the top level `hello` module, and nothing else.

##### `RoverStdout`

Expand Down
26 changes: 14 additions & 12 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

5 changes: 1 addition & 4 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -53,13 +53,10 @@ git-url-parse = "0.3.1"
git2 = { version = "0.13.20", default-features = false, features = ["vendored-openssl"] }
harmonizer = { version = "0.26.0", optional = true }
heck = "0.3.3"
humantime = "2.1.0"
opener = "0.5.0"
os_info = "3.0"
prettytable-rs = "0.8.0"
reqwest = {version = "0.11", default-features = false, features = ["blocking", "brotli", "gzip", "json", "native-tls-vendored"]}
regex = "1"
semver = "1"
reqwest = {version = "0.11.4", default-features = false, features = ["blocking"] }
serde = "1.0"
serde_json = "1.0"
serde_yaml = "0.8"
Expand Down
60 changes: 60 additions & 0 deletions crates/rover-client/.eslintrc.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
{
"root": true,
"overrides": [
{
"files": ["*.graphql"],
"parser": "@graphql-eslint/eslint-plugin",
"plugins": ["@graphql-eslint"],
"parserOptions": {
"schema": "./.schema/schema.graphql",
"operations": ["./src/operations/**/*.graphql"]
},
"rules": {
"@graphql-eslint/avoid-duplicate-fields": 2,
"@graphql-eslint/avoid-operation-name-prefix": 2,
"@graphql-eslint/avoid-typename-prefix": 2,
"@graphql-eslint/description-style": 2,
"@graphql-eslint/executable-definitions": 2,
"@graphql-eslint/fields-on-correct-type": 2,
"@graphql-eslint/fragments-on-composite-type": 2,
"@graphql-eslint/input-name": 2,
"@graphql-eslint/known-argument-names": 2,
"@graphql-eslint/known-directives": 2,
"@graphql-eslint/known-fragment-names": 2,
"@graphql-eslint/known-type-names": 2,
"@graphql-eslint/lone-anonymous-operation": 2,
"@graphql-eslint/no-anonymous-operations": 2,
"@graphql-eslint/no-case-insensitive-enum-values-duplicates": 2,
"@graphql-eslint/no-deprecated": 2,
"@graphql-eslint/no-fragment-cycles": 2,
"@graphql-eslint/no-hashtag-description": 2,
"@graphql-eslint/no-undefined-variables": 2,
"@graphql-eslint/no-unused-fields": 2,
"@graphql-eslint/no-unused-fragments": 2,
"@graphql-eslint/no-unused-variables": 2,
"@graphql-eslint/one-field-subscriptions": 2,
"@graphql-eslint/overlapping-fields-can-be-merged": 2,
"@graphql-eslint/possible-fragment-spread": 2,
"@graphql-eslint/possible-type-extension": 2,
"@graphql-eslint/provided-required-arguments": 2,
"@graphql-eslint/require-deprecation-reason": 2,
"@graphql-eslint/scalar-leafs": 2,
"@graphql-eslint/strict-id-in-types": 2,
"@graphql-eslint/unique-argument-names": 2,
"@graphql-eslint/unique-directive-names": 2,
"@graphql-eslint/unique-directive-names-per-location": 2,
"@graphql-eslint/unique-enum-value-names": 2,
"@graphql-eslint/unique-field-definition-names": 2,
"@graphql-eslint/unique-fragment-name": 2,
"@graphql-eslint/unique-input-field-names": 2,
"@graphql-eslint/unique-operation-name": 2,
"@graphql-eslint/unique-operation-types": 2,
"@graphql-eslint/unique-type-names": 2,
"@graphql-eslint/unique-variable-names": 2,
"@graphql-eslint/value-literals-of-correct-type": 2,
"@graphql-eslint/variables-are-input-types": 2,
"@graphql-eslint/variables-in-allowed-position": 2
}
}
]
}
8 changes: 6 additions & 2 deletions crates/rover-client/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -13,11 +13,15 @@ houston = {path = "../houston"}
# crates.io deps
camino = "1"
chrono = "0.4"
git-url-parse = "0.3.1"
git2 = { version = "0.13.20", default-features = false, features = ["vendored-openssl"] }
graphql_client = "0.9"
http = "0.2"
regex = "1.5.4"
reqwest = {version = "0.11", default-features = false, features = ["blocking", "json"]}
humantime = "2.1.0"
reqwest = { version = "0.11", default-features = false, features = ["blocking", "brotli", "gzip", "json", "native-tls-vendored"] }
regex = "1"
sdl-encoder = {path = "../sdl-encoder"}
semver = "1"
serde = "1"
serde_json = "1"
thiserror = "1"
Expand Down
Loading