Skip to content

Commit 282eb11

Browse files
author
Ran Magen
authored
Introduce code generation from .graphql files for making queries (#52)
Josh and Alex want to make the CLI make some GraphQL requests. We have a plan to use https://github.com/graphql-rust/graphql-client with its code generation features along with a schema that we'll keep up to date as a build phase. To unblock them, this PR adds 2 example queries and tests to show how this works. I verified this works locally by using my personal api key and removing the `should_panic`. Their existing code saves the api key on the file system after authenticating. We should therefore create a `Client` instance that's instantiated with the api key `auth` creates. See the documentation for the library we're using and see examples here: https://github.com/graphql-rust/graphql-client/tree/master/graphql_client/tests
1 parent 6e6a244 commit 282eb11

File tree

9 files changed

+184
-15
lines changed

9 files changed

+184
-15
lines changed

.github/workflows/test.yml

Lines changed: 15 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -71,25 +71,26 @@ jobs:
7171
rustup default ${{ matrix.rust }}
7272
7373
- name: Run Tests
74-
if: matrix.build != 'linux-stable'
74+
# if: matrix.build != 'linux-stable'
7575
run: cargo test
7676
env:
7777
RUST_LOG: warn,apollo-cli=info
7878
RUST_BACKTRACE: 1
79-
80-
- name: Run cargo-tarpaulin
81-
if: matrix.build == 'linux-stable'
82-
uses: actions-rs/[email protected]
83-
with:
84-
version: '0.9.0'
85-
timeout: 300 # 5 mins for test coverage
86-
args: '--ignore-tests -- --test-threads 1'
79+
80+
# Restore this when we get coverage working well -- integration tests on binary packages don't report coverage correctly.
81+
# - name: Run cargo-tarpaulin
82+
# if: matrix.build == 'linux-stable'
83+
# uses: actions-rs/[email protected]
84+
# with:
85+
# version: '0.9.0'
86+
# timeout: 600 # 10 mins for test coverage
87+
# args: '--ignore-tests'
8788

88-
- name: Upload to codecov.io
89-
if: matrix.build == 'linux-stable'
90-
uses: codecov/[email protected]
91-
with:
92-
token: ${{secrets.CODECOV_TOKEN}}
89+
# - name: Upload to codecov.io
90+
# if: matrix.build == 'linux-stable'
91+
# uses: codecov/[email protected]
92+
# with:
93+
# token: ${{secrets.CODECOV_TOKEN}}
9394

9495
test_worker:
9596
name: Test CDN and installer

cli/.graphqlconfig

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
{
2+
"name": "Apollo Graph Manager GraphQL Schema",
3+
"schemaPath": ".schema/schema.graphql",
4+
"includes": ["graphql/**.graphql"],
5+
"extensions": {
6+
"endpoints": {
7+
"Default GraphQL Endpoint": {
8+
"url": "https://engine-graphql.apollographql.com/api/graphql",
9+
"headers": {
10+
"user-agent": "JS GraphQL"
11+
},
12+
"introspect": false
13+
}
14+
}
15+
}
16+
}

cli/Cargo.toml

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,10 @@ console = "0.11.3"
2121
dirs = "2.0.2"
2222
env_logger = "0.7.1"
2323
graphql-parser = { path = "../graphql-parser" }
24+
graphql_client = "0.9.0"
25+
http = "0.2.1"
2426
human-panic = "1.0.3"
27+
json = "0.12.4"
2528
log = { version = "0.4.8", features = ["std"] }
2629
openssl = { version = "0.10.29", optional = true }
2730
reqwest = { version = "0.10.6", features = ["blocking", "json"] }

cli/graphql/get_schema_query.graphql

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
# Provided as example. Remove at some point if not used.
2+
3+
query GetSchemaQuery($serviceId: ID!, $variant: String!) {
4+
service(id: $serviceId) {
5+
schema(tag: $variant) {
6+
hash
7+
}
8+
}
9+
}
10+

cli/graphql/me_query.graphql

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
# Provided as example. Remove at some point if not used.
2+
3+
query MeQuery {
4+
me {
5+
__typename
6+
id
7+
name
8+
}
9+
}

cli/src/cli.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@ use crate::version::command_name;
1717
global_setting = AppSettings::DontCollapseArgsInUsage,
1818
global_setting = AppSettings::VersionlessSubcommands,
1919
)]
20-
/// The [Experimental] Apollo CLI, for supporting all your graphql needs :)
20+
/// The _Experimental_ Apollo CLI, for supporting all your graphql needs :)
2121
pub struct Apollo {
2222
#[structopt(subcommand)]
2323
pub command: Option<Subcommand>,

cli/src/client/mod.rs

Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,55 @@
1+
use graphql_client::{GraphQLQuery, Response};
2+
use http::Uri;
3+
use reqwest::blocking;
4+
use std::env;
5+
use std::error::Error;
6+
7+
mod queries;
8+
9+
static PROD_GQL_API_URL: &str = "https://engine-graphql.apollographql.com/api/graphql";
10+
11+
fn api_uri() -> Uri {
12+
env::var("APOLLO_API_URL")
13+
.ok()
14+
.and_then(|url| url.parse::<Uri>().ok())
15+
.unwrap_or_else(|| PROD_GQL_API_URL.parse::<Uri>().unwrap())
16+
}
17+
18+
pub struct Client {
19+
api_key: String,
20+
uri: Uri,
21+
reqwest: blocking::Client,
22+
}
23+
24+
impl Client {
25+
fn from(api_key: String) -> Client {
26+
Client {
27+
api_key,
28+
uri: api_uri(),
29+
reqwest: blocking::Client::new(),
30+
}
31+
}
32+
33+
fn send<Q: GraphQLQuery>(
34+
&self,
35+
variables: Q::Variables,
36+
) -> Result<Response<Q::ResponseData>, Box<dyn Error>> {
37+
let request_body = Q::build_query(variables);
38+
39+
let res = self
40+
.reqwest
41+
.post(&self.uri.to_string())
42+
.header("Content-Type", "application/json")
43+
.header("apollographql-client-name", "experimental-apollo-cli")
44+
.header(
45+
"apollographql-client-version",
46+
env::var("CARGO_PKG_VERSION").unwrap(),
47+
)
48+
.header("x-api-key", &self.api_key)
49+
.json(&request_body)
50+
.send()?;
51+
52+
let response_body: Response<Q::ResponseData> = res.json()?;
53+
Ok(response_body)
54+
}
55+
}

cli/src/client/queries.rs

Lines changed: 74 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,74 @@
1+
use crate::client::Client;
2+
use graphql_client::*;
3+
use std::error::Error;
4+
5+
// We have to declare types for scalars in the schema.
6+
// type GraphQLDocument = String;
7+
8+
#[derive(GraphQLQuery)]
9+
#[graphql(
10+
query_path = "graphql/get_schema_query.graphql",
11+
schema_path = ".schema/schema.graphql",
12+
response_derives = "PartialEq, Debug",
13+
deprecated = "warn"
14+
)]
15+
pub struct GetSchemaQuery;
16+
17+
pub fn get_schema_query(
18+
client: &Client,
19+
variables: get_schema_query::Variables,
20+
) -> Result<Option<String>, Box<dyn Error>> {
21+
let response_body: Response<get_schema_query::ResponseData> =
22+
client.send::<GetSchemaQuery>(variables)?;
23+
24+
let hash: Option<String> = response_body.data.and_then(|data| {
25+
data.service
26+
.and_then(|service| service.schema.map(|schema| schema.hash))
27+
});
28+
29+
Ok(hash)
30+
}
31+
32+
#[derive(GraphQLQuery)]
33+
#[graphql(
34+
query_path = "graphql/me_query.graphql",
35+
schema_path = ".schema/schema.graphql",
36+
response_derives = "PartialEq, Debug",
37+
deprecated = "warn"
38+
)]
39+
pub struct MeQuery;
40+
41+
pub fn me_query(client: &Client) -> Result<Option<me_query::MeQueryMe>, Box<dyn Error>> {
42+
let response_body: Response<me_query::ResponseData> =
43+
client.send::<MeQuery>(me_query::Variables {})?;
44+
45+
Ok(response_body.data.and_then(|data| data.me))
46+
}
47+
48+
#[cfg(test)]
49+
mod tests {
50+
#[test]
51+
#[should_panic] // will not panic with a real API key with access to "my-service".
52+
#[ignore]
53+
fn get_schema_query() {
54+
use super::*;
55+
let client = Client::from("todo_get_api_key".to_owned());
56+
57+
let vars = get_schema_query::Variables {
58+
service_id: "my-service".to_owned(),
59+
variant: "current".to_owned(),
60+
};
61+
let hash = get_schema_query(&client, vars).unwrap().unwrap();
62+
println!("{}", hash);
63+
}
64+
65+
#[test]
66+
#[should_panic] // will not panic with a real API key.
67+
#[ignore]
68+
fn me_query() {
69+
use super::*;
70+
let client = Client::from("todo_get_api_key".to_owned());
71+
let me = me_query(&client).unwrap().unwrap();
72+
println!("{:?}", me);
73+
}
74+
}

cli/src/main.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
extern crate text_io;
33

44
mod cli;
5+
mod client;
56
mod commands;
67
mod config;
78
mod errors;

0 commit comments

Comments
 (0)