Skip to content

Commit f510c4b

Browse files
committed
feat: add apollo schema export
1 parent 5e6b93b commit f510c4b

File tree

5 files changed

+135
-1
lines changed

5 files changed

+135
-1
lines changed

CHANGELOG.md

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,9 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
66
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
77

88
## [Unreleased]
9+
### Feat
10+
11+
- Add schema export to apollo
912

1013
## [0.2.4]
1114

Cargo.toml

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,3 +25,6 @@ tokio = { version = "1", features = ["full"] }
2525
reqwest = { version = "0.11.*", features = ["json"] }
2626
async-trait = "0.1.*"
2727
serde_json = "1.0.*" # A JSON serialization file format
28+
sha2 = "0.9.*" # Pure Rust implementation of the SHA-2 hash function family including SHA-224, SHA-256, SHA-384, a…
29+
anyhow = "1.0.*" # Flexible concrete Error type built on std::error::Error
30+
uuid = { version = "0.8.*", features = ["v4"] } # A library to generate and parse UUIDs.

README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,7 @@ _Tested at Rust version: `rustc 1.53.0 (53cb7b09b 2021-06-17)`_
3535
* Client segmentation
3636
* Additional data to segment your queries by visitors
3737
* Tracing
38+
* Schema export to studio
3839

3940
## Examples
4041

@@ -44,7 +45,6 @@ Incoming.
4445

4546
* Gzip compression
4647
* Error traces
47-
* Automatic Schema export to studio
4848

4949
## References
5050

src/lib.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
//! # Apollo Studio Extension for Performance Tracing for async_graphql crates
22
mod packages;
33
mod proto;
4+
pub mod register;
45

56
#[macro_use]
67
extern crate tracing;

src/register.rs

Lines changed: 127 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,127 @@
1+
//! # Apollo Studio Extension for Registering Schema
2+
use async_graphql::{ObjectType, Schema, SubscriptionType};
3+
use reqwest::Client;
4+
use sha2::{Digest, Sha256};
5+
use uuid::Uuid;
6+
7+
const SCHEMA_URL: &str = "https://schema-reporting.api.apollographql.com/api/graphql";
8+
const TARGET_LOG: &str = "apollo-studio-extension-register";
9+
const VERSION: &str = env!("CARGO_PKG_VERSION");
10+
const RUNTIME_VERSION: &str = "Rust - No runtime version provided yet";
11+
12+
/**
13+
* Compute the SHA256 of a Schema
14+
* Usefull for Apollo Studio
15+
*/
16+
pub fn sha<Q: ObjectType + 'static, M: ObjectType + 'static, S: SubscriptionType + 'static>(
17+
schema: Schema<Q, M, S>,
18+
) -> String {
19+
let mut hasher = Sha256::new();
20+
let schema_sdl = schema.sdl();
21+
let schema_bytes = schema_sdl.as_bytes();
22+
hasher.update(schema_bytes);
23+
let sha_from_schema = Sha256::digest(schema_bytes);
24+
format!("{:x}", sha_from_schema)
25+
}
26+
27+
/// Register your schema to Apollo Studio
28+
///
29+
/// * `authorization_token` - Token to send schema to apollo Studio.
30+
/// * `schema` - async_graphql generated schema.
31+
/// * `server_id` - An ID that's unique for each instance of your edge server. Unlike bootId, this value should persist across an instance's restarts. In a Kubernetes cluster, this might be the pod name, whereas the container can restart.
32+
/// * `variant` - The name of the graph variant to register the schema to. The default value is current.
33+
/// * `user_version` - An arbitrary string you can set to distinguish data sent by different versions of your edge server. For example, this can be the SHA of the Git commit for your deployed server code. We plan to make this value visible in Apollo Studio.
34+
/// * `platform` - The infrastructure environment that your edge server is running in (localhost, kubernetes/deployment, aws lambda, google cloud run, google cloud function, AWS ECS, etc.)
35+
#[instrument(err, skip(schema))]
36+
pub async fn register<
37+
Q: ObjectType + 'static,
38+
M: ObjectType + 'static,
39+
S: SubscriptionType + 'static,
40+
>(
41+
authorization_token: &str,
42+
schema: Schema<Q, M, S>,
43+
server_id: &str,
44+
variant: &str,
45+
user_version: &str,
46+
platform: &str,
47+
) -> anyhow::Result<()> {
48+
info!(
49+
target: TARGET_LOG,
50+
message = "Apollo Studio - Register Schema"
51+
);
52+
let client = Client::new();
53+
let schema_sdl = schema.sdl();
54+
let sha_from_schema = sha(schema);
55+
let boot_id = Uuid::new_v4();
56+
57+
let mutation = format!(
58+
r#"
59+
mutation($schema: String!) {{
60+
me {{
61+
... on ServiceMutation {{
62+
reportServerInfo(
63+
info: {{
64+
bootId: "{:?}"
65+
serverId: "{}"
66+
executableSchemaId: "{}"
67+
graphVariant: "{}"
68+
platform: "{}"
69+
libraryVersion: "{}"
70+
runtimeVersion: "{}"
71+
userVersion: "{}"
72+
}}
73+
executableSchema: $schema
74+
) {{
75+
__typename
76+
... on ReportServerInfoError {{
77+
code
78+
message
79+
}}
80+
inSeconds
81+
withExecutableSchema
82+
}}
83+
}}
84+
}}
85+
}}
86+
"#,
87+
boot_id,
88+
server_id,
89+
sha_from_schema,
90+
variant,
91+
platform,
92+
format!("async-studio-extension {}", VERSION),
93+
RUNTIME_VERSION,
94+
user_version
95+
);
96+
97+
let result = client
98+
.post(SCHEMA_URL)
99+
.json(&serde_json::json!({
100+
"query": mutation,
101+
"variables": {
102+
"schema": schema_sdl,
103+
},
104+
}))
105+
.header("content-type", "application/json")
106+
.header("X-Api-Key", authorization_token)
107+
.send()
108+
.await;
109+
110+
match result {
111+
Ok(data) => {
112+
info!(
113+
target: TARGET_LOG,
114+
message = "Schema correctly registered",
115+
response = &tracing::field::debug(&data)
116+
);
117+
let text = data.text().await;
118+
debug!(target: TARGET_LOG, data = ?text);
119+
Ok(())
120+
}
121+
Err(err) => {
122+
let status_code = err.status();
123+
error!(target: TARGET_LOG, status = ?status_code, error = ?err);
124+
Err(anyhow::anyhow!(err))
125+
}
126+
}
127+
}

0 commit comments

Comments
 (0)