Skip to content

Commit

Permalink
make it possible to read headers from a file on the generate-sdl command
Browse files Browse the repository at this point in the history
  • Loading branch information
benweint committed May 31, 2024
1 parent 3c4dc48 commit a40087b
Show file tree
Hide file tree
Showing 3 changed files with 67 additions and 16 deletions.
4 changes: 2 additions & 2 deletions NOTES.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@

- [x] Make `gquil help <subcommand>` show help instead of an error
- [x] Make help text link back to a path for feedback, issues
- [ ] Add a CONTRIBUTING.md file with info about development, processes
- [x] Add a CONTRIBUTING.md file with info about development, processes
- [ ] Add examples to the in-tool documentation
- [ ] Add a manpage

Expand All @@ -18,7 +18,7 @@

## Argument handling

- [ ] Make it possible to read header values from a file ala `curl -H @filename`
- [x] Make it possible to read header values from a file ala `curl -H @filename`

## Error handling

Expand Down
73 changes: 64 additions & 9 deletions pkg/commands/generate_sdl.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ import (

type GenerateSDLCmd struct {
Endpoint string `arg:"" help:"The GraphQL introspection endpoint URL to fetch from."`
Headers []string `name:"header" short:"H" help:"Set headers on the introspection request. Format: <key>: <value>."`
Headers []string `name:"header" short:"H" help:"Set custom headers on the introspection request, e.g. for authentication. Format: <key>: <value>. May be specified multiple times. Header values may be read from a file with the syntax @<filename>, e.g. --header @my-headers.txt."`
Trace bool `name:"trace" help:"Dump the introspection HTTP request and response to stderr for debugging."`

OutputOptions
Expand All @@ -32,7 +32,7 @@ Example:
--header 'origin: https://docs.developer.yelp.com' \
https://api.yelp.com/v3/graphql
Note that since GraphQL's introspection schema does not expose information about the application sites of most directives, the generated SDL will lack any applied directives (with the exception of @deprecated, which is exposed via the introspection system)
Note that since GraphQL's introspection schema does not expose information about the application sites of most directives, the generated SDL will lack any applied directives (with the exception of @deprecated, which is exposed via the introspection system).
If your GraphQL endpoint requires authentication or other special headers, you can set custom headers on the issued request using the --header flag.`
}
Expand All @@ -48,7 +48,12 @@ func (c *GenerateSDLCmd) Run(ctx Context) error {
traceOut = os.Stderr
}

client := introspection.NewClient(c.Endpoint, parseHeaders(c.Headers), sv, traceOut)
headers, err := parseHeaders(c.Headers)
if err != nil {
return fmt.Errorf("failed to parse custom header: %w", err)
}

client := introspection.NewClient(c.Endpoint, headers, sv, traceOut)
s, err := client.FetchSchemaAst()
if err != nil {
return err
Expand Down Expand Up @@ -77,13 +82,63 @@ func (c *GenerateSDLCmd) Run(ctx Context) error {
return nil
}

func parseHeaders(raw []string) http.Header {
func parseHeaders(raw []string) (http.Header, error) {
result := http.Header{}
for _, rawHeader := range raw {
parts := strings.SplitN(rawHeader, ":", 2)
key := parts[0]
value := strings.TrimLeft(parts[1], " ")
result[key] = append(result[key], value)
parsedHeaders, err := parseHeaderValue(rawHeader)
if err != nil {
return nil, err
}
for key, vals := range parsedHeaders {
for _, val := range vals {
result.Add(key, val)
}
}
}
return result, nil
}

func parseHeaderValue(raw string) (http.Header, error) {
if strings.HasPrefix(raw, "@") {
return parseHeadersFromFile(strings.TrimPrefix(raw, "@"))
}

key, val, err := parseHeaderString(raw)
if err != nil {
return nil, err
}
return http.Header{
key: []string{val},
}, nil
}

func parseHeadersFromFile(path string) (http.Header, error) {
raw, err := os.ReadFile(path)
if err != nil {
return nil, err
}

result := http.Header{}
lines := strings.Split(string(raw), "\n")
for _, line := range lines {
if line == "" {
continue
}
key, val, err := parseHeaderString(line)
if err != nil {
return nil, err
}
result.Add(key, val)
}
return result, nil
}

func parseHeaderString(raw string) (string, string, error) {
parts := strings.SplitN(raw, ":", 2)
if len(parts) != 2 {
return "", "", fmt.Errorf("invalid header value '%s', expected format '<key>: <value>'", raw)
}
return result
key := parts[0]
value := strings.TrimLeft(parts[1], " ")
return key, value, nil
}
6 changes: 1 addition & 5 deletions pkg/introspection/client.go
Original file line number Diff line number Diff line change
Expand Up @@ -120,11 +120,7 @@ func (c *Client) issueQuery(query string, vars map[string]any, operation string)
return nil, fmt.Errorf("failed to create introspection request: %w", err)
}

for k, vs := range c.headers {
for _, v := range vs {
req.Header.Set(k, v)
}
}
req.Header = c.headers

if c.traceOut != nil {
requestDump, err := httputil.DumpRequestOut(req, true)
Expand Down

0 comments on commit a40087b

Please sign in to comment.