commit - cec1a3c7758f8fd6368f9066c7bb23580f2cd4d9
commit + 7bb538f44fe2e83c810af33436ca585d4be05b2c
blob - /dev/null
blob + 4c944815a4bb123f565845369d144f93ffccc233 (mode 644)
--- /dev/null
+++ man/gql.1
+.Dd
+.Dt GQL 1
+.Sh NAME
+.Nm gql
+.Nd execute GraphQL queries
+.Sh SYNOPSIS
+.Nm
+.Op Fl k Ar file
+.Op Fl r Ar file
+.Op Fl v Ar var=value
+.Ar url
+.Op Ar
+.Sh DESCRIPTION
+.Nm
+executes the queries read from the named files
+.Pq or from the standard input if none specified
+to the GraphQL API served at
+.Ar url .
+The response is written to the standard output.
+.Pp
+The following flags are understood:
+.Bl -tag -width indent
+.It Fl k Ar file
+Reads the API key from
+.Pa file
+to be be included in the Authorization field
+of the HTTP request as a Bearer token.
+.It Fl r Ar file
+Loads the JSON-encoded variables object
+from
+.Pa file .
+.It Fl v Ar var=value
+Sets the named variable
+.Ar var
+to
+.Ar value .
+Any variables loaded from a file via the
+.Fl r
+flag will be discarded.
+.El
+
+.Sh EXIT STATUS
+.Ex
blob - /dev/null
blob + 064e8bc2904e37848f24e9a3a708901ad80c1ff0 (mode 644)
--- /dev/null
+++ src/gql/gql.go
+package main
+
+import (
+ "bytes"
+ "encoding/json"
+ "flag"
+ "fmt"
+ "io"
+ "log"
+ "net/http"
+ "os"
+ "strings"
+)
+
+const usage string = "gql [-k file] [-r file] [-v var=value ] url [file]"
+
+// Firefox ESR (Extended Support Release).
+const firefox = "Mozilla/5.0 (X11; Intel Linux x86_64; rv:140.0) Gecko/20100101 Firefox/140.0"
+
+type Query struct {
+ Query string `json:"query"`
+ Variables any `json:"variables,omitempty"`
+}
+
+var tokenFlag = flag.String("k", "", "file containing api key")
+var varFlag = flag.String("v", "", "variable in key=value format")
+var rFlag = flag.String("r", "", "file containing variables in json format")
+
+var token string
+
+func init() {
+ flag.Parse()
+}
+
+func main() {
+ if len(flag.Args()) == 0 {
+ fmt.Println("usage:", usage)
+ os.Exit(2)
+ }
+
+ if *tokenFlag != "" {
+ b, err := os.ReadFile(*tokenFlag)
+ if err != nil {
+ log.Fatalf("read key file: %v", err)
+ }
+ token = string(bytes.TrimSpace(b))
+ }
+
+ var query Query
+ if *varFlag != "" {
+ k, v, ok := strings.Cut(*varFlag, "=")
+ if !ok {
+ log.Fatalf("missing = separator in variable spec %q", *varFlag)
+ }
+ query.Variables = map[string]string{k: v}
+ } else if *rFlag != "" {
+ f, err := os.Open(*rFlag)
+ if err != nil {
+ log.Fatalf("open variables file: %v", err)
+ }
+ defer f.Close()
+ if err := json.NewDecoder(f).Decode(&query.Variables); err != nil {
+ log.Fatalf("parse variables from %s: %v", *rFlag, err)
+ }
+ }
+
+ url := flag.Arg(0)
+
+ rd := os.Stdin
+ if len(flag.Args()) == 2 {
+ f, err := os.Open(flag.Arg(1))
+ if err != nil {
+ log.Fatal(err)
+ }
+ defer f.Close()
+ rd = f
+ }
+ b, err := io.ReadAll(rd)
+ if err != nil {
+ log.Fatalf("read query: %v", err)
+ }
+ query.Query = string(bytes.TrimSpace(b))
+
+ buf := &bytes.Buffer{}
+ if err := json.NewEncoder(buf).Encode(&query); err != nil {
+ log.Fatalf("encode graphql query: %v", err)
+ }
+ println(buf.String())
+ os.Exit(1)
+
+ req, err := http.NewRequest(http.MethodPost, url, buf)
+ if err != nil {
+ log.Fatal(err)
+ }
+ if token != "" {
+ req.Header.Set("Authorization", "Bearer "+token)
+ }
+ req.Header.Set("User-Agent", firefox)
+ req.Header.Set("Content-Type", "application/json")
+
+ resp, err := http.DefaultClient.Do(req)
+ if err != nil {
+ log.Fatal(err)
+ }
+ fmt.Fprintln(os.Stderr, resp.Status)
+ io.Copy(os.Stdout, resp.Body)
+}