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

Add header flag parser and passthru for introspection #351

Merged
merged 1 commit into from
Mar 23, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
7 changes: 5 additions & 2 deletions crates/rover-client/src/query/graph/introspect.rs
Original file line number Diff line number Diff line change
Expand Up @@ -25,9 +25,12 @@ pub struct IntrospectionResponse {

/// The main function to be used from this module. This function fetches a
/// schema from apollo studio and returns it in either sdl (default) or json format
pub fn run(client: &Client) -> Result<IntrospectionResponse, RoverClientError> {
pub fn run(
client: &Client,
headers: &HashMap<String, String>,
) -> Result<IntrospectionResponse, RoverClientError> {
let variables = introspection_query::Variables {};
let response_data = client.post::<IntrospectionQuery>(variables, &HashMap::new())?;
let response_data = client.post::<IntrospectionQuery>(variables, headers)?;
build_response(response_data)
}

Expand Down
23 changes: 21 additions & 2 deletions src/command/graph/introspect.rs
Original file line number Diff line number Diff line change
@@ -1,26 +1,45 @@
use crate::Result;
use serde::Serialize;
use std::collections::HashMap;
use structopt::StructOpt;
use url::Url;

use rover_client::{blocking::Client, query::graph::introspect};

use crate::command::RoverStdout;
use crate::utils::parsers::parse_url;
use crate::utils::parsers::{parse_header, parse_url};

#[derive(Debug, Serialize, StructOpt)]
pub struct Introspect {
/// The endpoint of the graph to introspect
#[structopt(parse(try_from_str = parse_url))]
#[serde(skip_serializing)]
pub endpoint: Url,

/// headers to pass to the endpoint. Values must be key:value pairs.
/// If a value has a space in it, use quotes around the pair,
/// ex. -H "Auth:some key"

// The `name` here is for the help text and error messages, to print like
// --header <key:value> rather than the plural field name --header <headers>
#[structopt(name="key:value", multiple=true, long="header", short="H", parse(try_from_str = parse_header))]
#[serde(skip_serializing)]
pub headers: Option<Vec<(String, String)>>,
}

impl Introspect {
pub fn run(&self) -> Result<RoverStdout> {
let client = Client::new(&self.endpoint.to_string());

let introspection_response = introspect::run(&client)?;
// add the flag headers to a hashmap to pass along to rover-client
let mut headers = HashMap::new();
if self.headers.is_some() {
for (key, value) in self.headers.clone().unwrap() {
headers.insert(key, value);
}
}

let introspection_response = introspect::run(&client, &headers)?;

Ok(RoverStdout::Introspection(introspection_response.result))
}
Expand Down
13 changes: 13 additions & 0 deletions src/utils/parsers.rs
Original file line number Diff line number Diff line change
Expand Up @@ -133,6 +133,19 @@ pub fn parse_url(url: &str) -> Result<Url> {
Ok(res)
}

/// Parses a key:value pair from a string and returns a tuple of key:value.
/// If a full key:value can't be parsed, it will error.
pub fn parse_header(header: &str) -> Result<(String, String)> {
// only split once, a header's value may have a ":" in it, but not a key. Right?
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think if it has a :, it'll likely be in a quoted string. The only : that's likely to be there is the one that separates the header key and the header value. The values are just going to be comma separated. That is to say the parsing you have below seems correct to me.

From the spec:

message-header = field-name ":" [ field-value ]
field-name     = token
field-value    = *( field-content | LWS )
field-content  = <the OCTETs making up the field-value
                 and consisting of either *TEXT or combinations
                 of token, separators, and quoted-string>

let pair: Vec<&str> = header.splitn(2, ':').collect();
if pair.len() < 2 {
let msg = format!("Could not parse \"key:value\" pair for provided header: \"{}\". Headers must be provided in key:value pairs, with quotes around the pair if there are any spaces in the key or value.", header);
Err(RoverError::parse_error(msg))
} else {
Ok((pair[0].to_string(), pair[1].to_string()))
}
}

#[cfg(test)]
mod tests {
use super::{parse_graph_ref, parse_schema_source, GraphRef, SchemaSource};
Expand Down