Skip to content

Commit 772e5aa

Browse files
feat(query): add schema flag (#10052)
### Description Adds schema flag which easily lets you get the turbo graphql schema ### Testing Instructions Snapshot test added to `tests/query.rs`
1 parent 246234e commit 772e5aa

File tree

4 files changed

+3414
-6
lines changed

4 files changed

+3414
-6
lines changed

crates/turborepo-lib/src/cli/mod.rs

+9-2
Original file line numberDiff line numberDiff line change
@@ -729,6 +729,8 @@ pub enum Command {
729729
/// Pass variables to the query via a JSON file
730730
#[clap(short = 'V', long, requires = "query")]
731731
variables: Option<Utf8PathBuf>,
732+
#[clap(long, conflicts_with = "query")]
733+
schema: bool,
732734
/// The query to run, either a file path or a query string
733735
query: Option<String>,
734736
},
@@ -1589,17 +1591,22 @@ pub async fn run(
15891591
})?;
15901592
Ok(exit_code)
15911593
}
1592-
Command::Query { query, variables } => {
1594+
Command::Query {
1595+
query,
1596+
variables,
1597+
schema,
1598+
} => {
15931599
warn!("query command is experimental and may change in the future");
15941600
let query = query.clone();
15951601
let variables = variables.clone();
1602+
let schema = *schema;
15961603
let event = CommandEventBuilder::new("query").with_parent(&root_telemetry);
15971604
event.track_call();
15981605

15991606
let base = CommandBase::new(cli_args, repo_root, version, color_config)?;
16001607
event.track_ui_mode(base.opts.run_opts.ui_mode);
16011608

1602-
let query = query::run(base, event, query, variables.as_deref()).await?;
1609+
let query = query::run(base, event, query, variables.as_deref(), schema).await?;
16031610

16041611
Ok(query)
16051612
}

crates/turborepo-lib/src/commands/query.rs

+105-4
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,106 @@ use crate::{
1515
signal::SignalHandler,
1616
};
1717

18+
const SCHEMA_QUERY: &str = "query IntrospectionQuery {
19+
__schema {
20+
queryType {
21+
name
22+
}
23+
mutationType {
24+
name
25+
}
26+
subscriptionType {
27+
name
28+
}
29+
types {
30+
...FullType
31+
}
32+
directives {
33+
name
34+
description
35+
locations
36+
args {
37+
...InputValue
38+
}
39+
}
40+
}
41+
}
42+
43+
fragment FullType on __Type {
44+
kind
45+
name
46+
description
47+
fields(includeDeprecated: true) {
48+
name
49+
description
50+
args {
51+
...InputValue
52+
}
53+
type {
54+
...TypeRef
55+
}
56+
isDeprecated
57+
deprecationReason
58+
}
59+
inputFields {
60+
...InputValue
61+
}
62+
interfaces {
63+
...TypeRef
64+
}
65+
enumValues(includeDeprecated: true) {
66+
name
67+
description
68+
isDeprecated
69+
deprecationReason
70+
}
71+
possibleTypes {
72+
...TypeRef
73+
}
74+
}
75+
76+
fragment InputValue on __InputValue {
77+
name
78+
description
79+
type {
80+
...TypeRef
81+
}
82+
defaultValue
83+
}
84+
85+
fragment TypeRef on __Type {
86+
kind
87+
name
88+
ofType {
89+
kind
90+
name
91+
ofType {
92+
kind
93+
name
94+
ofType {
95+
kind
96+
name
97+
ofType {
98+
kind
99+
name
100+
ofType {
101+
kind
102+
name
103+
ofType {
104+
kind
105+
name
106+
ofType {
107+
kind
108+
name
109+
}
110+
}
111+
}
112+
}
113+
}
114+
}
115+
}
116+
}";
117+
18118
#[derive(Debug, Diagnostic, Error)]
19119
#[error("{message}")]
20120
struct QueryError {
@@ -59,6 +159,7 @@ pub async fn run(
59159
telemetry: CommandEventBuilder,
60160
query: Option<String>,
61161
variables_path: Option<&Utf8Path>,
162+
include_schema: bool,
62163
) -> Result<i32, Error> {
63164
let signal = get_signal()?;
64165
let handler = SignalHandler::new(signal);
@@ -67,7 +168,7 @@ pub async fn run(
67168
.add_all_tasks()
68169
.do_not_validate_engine();
69170
let run = run_builder.build(&handler, telemetry).await?;
70-
171+
let query = query.as_deref().or(include_schema.then_some(SCHEMA_QUERY));
71172
if let Some(query) = query {
72173
let trimmed_query = query.trim();
73174
// If the arg starts with "query" or "mutation", and ends in a bracket, it's
@@ -80,7 +181,7 @@ pub async fn run(
80181
{
81182
query
82183
} else {
83-
fs::read_to_string(AbsoluteSystemPathBuf::from_unknown(run.repo_root(), query))?
184+
&fs::read_to_string(AbsoluteSystemPathBuf::from_unknown(run.repo_root(), query))?
84185
};
85186

86187
let schema = Schema::new(
@@ -98,13 +199,13 @@ pub async fn run(
98199
.transpose()?
99200
.unwrap_or_default();
100201

101-
let request = Request::new(&query).variables(variables);
202+
let request = Request::new(query).variables(variables);
102203

103204
let result = schema.execute(request).await;
104205
println!("{}", serde_json::to_string_pretty(&result)?);
105206
if !result.errors.is_empty() {
106207
for error in result.errors {
107-
let error = QueryError::new(error, query.clone());
208+
let error = QueryError::new(error, query.to_string());
108209
eprintln!("{:?}", Report::new(error));
109210
}
110211
}

crates/turborepo/tests/query.rs

+1
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ fn test_query() -> Result<(), anyhow::Error> {
1818
"get the indirect dependencies of `my-app`" => ["query { packages(filter: {equal: { field: NAME, value: \"my-app\" } }) { items { indirectDependencies { items { name } } } } }"],
1919
"get all dependencies of `my-app`" => ["query { packages(filter: {equal: { field: NAME, value: \"my-app\" } }) { items { allDependencies { items { name } } } } }"],
2020
"get package graph" => ["query { packageGraph { nodes { items { name } } edges { items { source target } } } }"],
21+
"get schema" => ["--schema"],
2122
);
2223

2324
Ok(())

0 commit comments

Comments
 (0)