Skip to content

Commit bbfbf74

Browse files
chore: DRY up reused CLI options (#1155)
* chore: DRY up reused CLI options * fixup architecture.md * chore: factors out SchemaOpt * factor out CheckConfigOpts
1 parent 669cff1 commit bbfbf74

28 files changed

+324
-335
lines changed

ARCHITECTURE.md

+12-15
Original file line numberDiff line numberDiff line change
@@ -191,18 +191,15 @@ All commands under the `graph` namespace accept a required, positional argument
191191
To add these to our new `graph hello` command, we can copy and paste the field from any other `graph` command like so:
192192

193193
```rust
194+
use crate::options::{ProfileOpt, GraphRefOpt};
195+
194196
#[derive(Debug, Serialize, StructOpt)]
195197
pub struct Hello {
196-
/// <NAME>@<VARIANT> of graph in Apollo Studio to publish to.
197-
/// @<VARIANT> may be left off, defaulting to @current
198-
#[structopt(name = "GRAPH_REF"))]
199-
#[serde(skip_serializing)]
200-
graph: GraphRef,
201-
202-
/// Name of configuration profile to use
203-
#[structopt(long = "profile", default_value = "default")]
204-
#[serde(skip_serializing)]
205-
profile_name: String,
198+
#[structopt(flatten)]
199+
graph: GraphRefOpt,
200+
201+
#[structop(flatten)]
202+
profile: ProfileOpt,
206203
}
207204
```
208205

@@ -228,7 +225,7 @@ To access functionality from `rover-client` in our `rover graph hello` command,
228225

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

231-
Then you'll need to change `Hello::run` to accept a `client_config: StudioClientConfig` parameter in `src/command/graph/hello.rs`, and add `use crate::utils::client::StudioClientConfig` and `use rover_client::shared::GraphRef` import statements. 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.
228+
Then you'll need to change `Hello::run` to accept a `client_config: StudioClientConfig` parameter in `src/command/graph/hello.rs`, and add `use crate::utils::client::StudioClientConfig` and `use rover_client::shared::GraphRef` import statements. 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.
232229

233230
##### Auto-generated help command
234231

@@ -357,16 +354,16 @@ It should look something like this (you should make sure you are following the s
357354

358355
```rust
359356
pub fn run(&self, client_config: StudioClientConfig) -> Result<RoverOutput> {
360-
let client = client_config.get_client(&self.profile_name)?;
361-
let graph_ref = self.graph.to_string();
357+
let client = client_config.get_client(&self.profile.name)?;
358+
let graph_ref = self.graph.graph_ref.to_string();
362359
eprintln!(
363360
"Checking deletion of graph {} using credentials from the {} profile.",
364361
Cyan.normal().paint(&graph_ref),
365-
Yellow.normal().paint(&self.profile_name)
362+
Yellow.normal().paint(&self.profile.name)
366363
);
367364
let deleted_at = hello::run(
368365
hello::graph_hello::Variables {
369-
graph_id: self.graph.name.clone(),
366+
graph_id: self.graph.graph_ref.clone(),
370367
},
371368
&client,
372369
)?;

crates/rover-client/src/shared/check_response.rs

+3-3
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ use crate::shared::GraphRef;
66
use crate::RoverClientError;
77

88
use prettytable::format::consts::FORMAT_BOX_CHARS;
9-
use serde::Serialize;
9+
use serde::{Deserialize, Serialize};
1010

1111
use prettytable::{cell, row, Table};
1212
use serde_json::{json, Value};
@@ -162,7 +162,7 @@ pub struct CheckConfig {
162162
pub validation_period: Option<ValidationPeriod>,
163163
}
164164

165-
#[derive(Debug, PartialEq, Eq, Serialize, Clone)]
165+
#[derive(Debug, PartialEq, Eq, Serialize, Deserialize, Clone)]
166166
pub struct ValidationPeriod {
167167
pub from: Period,
168168
pub to: Period,
@@ -187,7 +187,7 @@ impl FromStr for ValidationPeriod {
187187
}
188188
}
189189

190-
#[derive(Debug, Clone, PartialEq, Eq, Serialize)]
190+
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
191191
pub enum Period {
192192
Now,
193193
Past(i64),

crates/rover-client/src/shared/graph_ref.rs

+2-2
Original file line numberDiff line numberDiff line change
@@ -4,9 +4,9 @@ use std::str::FromStr;
44
use crate::RoverClientError;
55

66
use regex::Regex;
7-
use serde::Serialize;
7+
use serde::{Deserialize, Serialize};
88

9-
#[derive(Debug, Serialize, Clone, PartialEq)]
9+
#[derive(Debug, Deserialize, Serialize, Clone, PartialEq)]
1010
pub struct GraphRef {
1111
pub name: String,
1212
pub variant: String,

src/command/config/auth.rs

+5-6
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ use config::Profile;
66
use houston as config;
77

88
use crate::command::RoverOutput;
9-
use crate::{anyhow, Result};
9+
use crate::{anyhow, options::ProfileOpt, Result};
1010

1111
#[derive(Debug, Serialize, StructOpt)]
1212
/// Authenticate a configuration profile with an API key
@@ -20,16 +20,15 @@ use crate::{anyhow, Result};
2020
///
2121
/// Run `rover docs open api-keys` for more details on Apollo's API keys.
2222
pub struct Auth {
23-
#[structopt(long = "profile", default_value = "default")]
24-
#[serde(skip_serializing)]
25-
profile_name: String,
23+
#[structopt(flatten)]
24+
profile: ProfileOpt,
2625
}
2726

2827
impl Auth {
2928
pub fn run(&self, config: config::Config) -> Result<RoverOutput> {
3029
let api_key = api_key_prompt()?;
31-
Profile::set_api_key(&self.profile_name, &config, &api_key)?;
32-
Profile::get_credential(&self.profile_name, &config).map(|_| {
30+
Profile::set_api_key(&self.profile.profile_name, &config, &api_key)?;
31+
Profile::get_credential(&self.profile.profile_name, &config).map(|_| {
3332
eprintln!("Successfully saved API key.");
3433
})?;
3534
Ok(RoverOutput::EmptySuccess)

src/command/config/whoami.rs

+5-6
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ use houston::{mask_key, CredentialOrigin};
77

88
use crate::anyhow;
99
use crate::command::RoverOutput;
10+
use crate::options::ProfileOpt;
1011
use crate::utils::client::StudioClientConfig;
1112
use crate::utils::env::RoverEnvKey;
1213
use crate::Result;
@@ -15,10 +16,8 @@ use houston as config;
1516

1617
#[derive(Debug, Serialize, StructOpt)]
1718
pub struct WhoAmI {
18-
/// Name of configuration profile to use
19-
#[structopt(long = "profile", default_value = "default")]
20-
#[serde(skip_serializing)]
21-
profile_name: String,
19+
#[structopt(flatten)]
20+
profile: ProfileOpt,
2221

2322
/// Unmask the API key that will be sent to Apollo Studio
2423
///
@@ -31,7 +30,7 @@ pub struct WhoAmI {
3130

3231
impl WhoAmI {
3332
pub fn run(&self, client_config: StudioClientConfig) -> Result<RoverOutput> {
34-
let client = client_config.get_authenticated_client(&self.profile_name)?;
33+
let client = client_config.get_authenticated_client(&self.profile.profile_name)?;
3534
eprintln!("Checking identity of your API key against the registry.");
3635

3736
let identity = who_am_i::run(ConfigWhoAmIInput {}, &client)?;
@@ -79,7 +78,7 @@ impl WhoAmI {
7978
message.push_str(&format!("{}: {}", Green.normal().paint("Origin"), &origin));
8079

8180
let credential =
82-
config::Profile::get_credential(&self.profile_name, &client_config.config)?;
81+
config::Profile::get_credential(&self.profile.profile_name, &client_config.config)?;
8382

8483
let maybe_masked_key = if self.insecure_unmask_key {
8584
credential.api_key

src/command/fed2/supergraph/compose.rs

+10-8
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,12 @@
1-
use crate::utils::{
2-
client::StudioClientConfig,
3-
parsers::{parse_file_descriptor, FileDescriptorType},
4-
};
51
use crate::Suggestion;
62
use crate::{anyhow, command::RoverOutput, error::RoverError, Result};
3+
use crate::{
4+
options::ProfileOpt,
5+
utils::{
6+
client::StudioClientConfig,
7+
parsers::{parse_file_descriptor, FileDescriptorType},
8+
},
9+
};
710

811
use serde::Serialize;
912
use structopt::StructOpt;
@@ -15,10 +18,9 @@ pub struct Compose {
1518
#[serde(skip_serializing)]
1619
supergraph_yaml: FileDescriptorType,
1720

18-
/// Name of configuration profile to use
19-
#[structopt(long = "profile", default_value = "default")]
20-
#[serde(skip_serializing)]
21-
_profile_name: String,
21+
#[structopt(flatten)]
22+
#[allow(unused)]
23+
profile: ProfileOpt,
2224
}
2325

2426
impl Compose {

src/command/graph/check.rs

+16-37
Original file line numberDiff line numberDiff line change
@@ -2,48 +2,27 @@ use serde::Serialize;
22
use structopt::StructOpt;
33

44
use rover_client::operations::graph::check::{self, GraphCheckInput};
5-
use rover_client::shared::{CheckConfig, GitContext, GraphRef, ValidationPeriod};
5+
use rover_client::shared::{CheckConfig, GitContext};
66

77
use crate::command::RoverOutput;
8+
use crate::options::{CheckConfigOpts, GraphRefOpt, ProfileOpt, SchemaOpt};
89
use crate::utils::client::StudioClientConfig;
9-
use crate::utils::parsers::{
10-
parse_file_descriptor, parse_query_count_threshold, parse_query_percentage_threshold,
11-
FileDescriptorType,
12-
};
1310
use crate::Result;
1411

1512
#[derive(Debug, Serialize, StructOpt)]
1613
pub struct Check {
17-
/// <NAME>@<VARIANT> of graph in Apollo Studio to validate.
18-
/// @<VARIANT> may be left off, defaulting to @current
19-
#[structopt(name = "GRAPH_REF")]
20-
#[serde(skip_serializing)]
21-
graph: GraphRef,
14+
#[structopt(flatten)]
15+
graph: GraphRefOpt,
2216

23-
/// Name of configuration profile to use
24-
#[structopt(long = "profile", default_value = "default")]
25-
#[serde(skip_serializing)]
26-
profile_name: String,
17+
#[structopt(flatten)]
18+
profile: ProfileOpt,
2719

28-
/// The schema file to check. You can pass `-` to use stdin instead of a file.
29-
#[structopt(long, short = "s", parse(try_from_str = parse_file_descriptor))]
20+
#[structopt(flatten)]
3021
#[serde(skip_serializing)]
31-
schema: FileDescriptorType,
32-
33-
/// The minimum number of times a query or mutation must have been executed
34-
/// in order to be considered in the check operation
35-
#[structopt(long, parse(try_from_str = parse_query_count_threshold))]
36-
query_count_threshold: Option<i64>,
37-
38-
/// Minimum percentage of times a query or mutation must have been executed
39-
/// in the time window, relative to total request count, for it to be
40-
/// considered in the check. Valid numbers are in the range 0 <= x <= 100
41-
#[structopt(long, parse(try_from_str = parse_query_percentage_threshold))]
42-
query_percentage_threshold: Option<f64>,
22+
schema: SchemaOpt,
4323

44-
/// Size of the time window with which to validate schema against (i.e "24h" or "1w 2d 5h")
45-
#[structopt(long)]
46-
validation_period: Option<ValidationPeriod>,
24+
#[structopt(flatten)]
25+
config: CheckConfigOpts,
4726
}
4827

4928
impl Check {
@@ -52,25 +31,25 @@ impl Check {
5231
client_config: StudioClientConfig,
5332
git_context: GitContext,
5433
) -> Result<RoverOutput> {
55-
let client = client_config.get_authenticated_client(&self.profile_name)?;
34+
let client = client_config.get_authenticated_client(&self.profile.profile_name)?;
5635
let proposed_schema = self
5736
.schema
5837
.read_file_descriptor("SDL", &mut std::io::stdin())?;
5938

6039
eprintln!(
6140
"Checking the proposed schema against metrics from {}",
62-
&self.graph
41+
&self.graph.graph_ref
6342
);
6443

6544
let res = check::run(
6645
GraphCheckInput {
67-
graph_ref: self.graph.clone(),
46+
graph_ref: self.graph.graph_ref.clone(),
6847
proposed_schema,
6948
git_context,
7049
config: CheckConfig {
71-
query_count_threshold: self.query_count_threshold,
72-
query_count_threshold_percentage: self.query_percentage_threshold,
73-
validation_period: self.validation_period.clone(),
50+
query_count_threshold: self.config.query_count_threshold,
51+
query_count_threshold_percentage: self.config.query_percentage_threshold,
52+
validation_period: self.config.validation_period.clone(),
7453
},
7554
},
7655
&client,

src/command/graph/delete.rs

+9-14
Original file line numberDiff line numberDiff line change
@@ -3,24 +3,19 @@ use serde::Serialize;
33
use structopt::StructOpt;
44

55
use rover_client::operations::graph::delete::{self, GraphDeleteInput};
6-
use rover_client::shared::GraphRef;
76

87
use crate::command::RoverOutput;
8+
use crate::options::{GraphRefOpt, ProfileOpt};
99
use crate::utils::{self, client::StudioClientConfig};
1010
use crate::Result;
1111

1212
#[derive(Debug, Serialize, StructOpt)]
1313
pub struct Delete {
14-
/// <NAME>@<VARIANT> of graph in Apollo Studio to fetch from.
15-
/// @<VARIANT> may be left off, defaulting to @current
16-
#[structopt(name = "GRAPH_REF")]
17-
#[serde(skip_serializing)]
18-
graph: GraphRef,
14+
#[structopt(flatten)]
15+
graph: GraphRefOpt,
1916

20-
/// Name of configuration profile to use
21-
#[structopt(long = "profile", default_value = "default")]
22-
#[serde(skip_serializing)]
23-
profile_name: String,
17+
#[structopt(flatten)]
18+
profile: ProfileOpt,
2419

2520
/// Skips the step where the command asks for user confirmation before
2621
/// deleting the graph.
@@ -30,13 +25,13 @@ pub struct Delete {
3025

3126
impl Delete {
3227
pub fn run(&self, client_config: StudioClientConfig) -> Result<RoverOutput> {
33-
let client = client_config.get_authenticated_client(&self.profile_name)?;
34-
let graph_ref = self.graph.to_string();
28+
let client = client_config.get_authenticated_client(&self.profile.profile_name)?;
29+
let graph_ref = self.graph.graph_ref.to_string();
3530

3631
eprintln!(
3732
"Deleting {} using credentials from the {} profile.",
3833
Cyan.normal().paint(&graph_ref),
39-
Yellow.normal().paint(&self.profile_name)
34+
Yellow.normal().paint(&self.profile.profile_name)
4035
);
4136

4237
if !self.confirm && !utils::confirm_delete()? {
@@ -46,7 +41,7 @@ impl Delete {
4641

4742
delete::run(
4843
GraphDeleteInput {
49-
graph_ref: self.graph.clone(),
44+
graph_ref: self.graph.graph_ref.clone(),
5045
},
5146
&client,
5247
)?;

src/command/graph/fetch.rs

+9-14
Original file line numberDiff line numberDiff line change
@@ -3,39 +3,34 @@ use serde::Serialize;
33
use structopt::StructOpt;
44

55
use rover_client::operations::graph::fetch::{self, GraphFetchInput};
6-
use rover_client::shared::GraphRef;
76

87
use crate::command::RoverOutput;
8+
use crate::options::{GraphRefOpt, ProfileOpt};
99
use crate::utils::client::StudioClientConfig;
1010
use crate::Result;
1111

1212
#[derive(Debug, Serialize, StructOpt)]
1313
pub struct Fetch {
14-
/// <NAME>@<VARIANT> of graph in Apollo Studio to fetch from.
15-
/// @<VARIANT> may be left off, defaulting to @current
16-
#[structopt(name = "GRAPH_REF")]
17-
#[serde(skip_serializing)]
18-
graph: GraphRef,
14+
#[structopt(flatten)]
15+
graph: GraphRefOpt,
1916

20-
/// Name of configuration profile to use
21-
#[structopt(long = "profile", default_value = "default")]
22-
#[serde(skip_serializing)]
23-
profile_name: String,
17+
#[structopt(flatten)]
18+
profile: ProfileOpt,
2419
}
2520

2621
impl Fetch {
2722
pub fn run(&self, client_config: StudioClientConfig) -> Result<RoverOutput> {
28-
let client = client_config.get_authenticated_client(&self.profile_name)?;
29-
let graph_ref = self.graph.to_string();
23+
let client = client_config.get_authenticated_client(&self.profile.profile_name)?;
24+
let graph_ref = self.graph.graph_ref.to_string();
3025
eprintln!(
3126
"Fetching SDL from {} using credentials from the {} profile.",
3227
Cyan.normal().paint(&graph_ref),
33-
Yellow.normal().paint(&self.profile_name)
28+
Yellow.normal().paint(&self.profile.profile_name)
3429
);
3530

3631
let fetch_response = fetch::run(
3732
GraphFetchInput {
38-
graph_ref: self.graph.clone(),
33+
graph_ref: self.graph.graph_ref.clone(),
3934
},
4035
&client,
4136
)?;

0 commit comments

Comments
 (0)