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

Bring in latest changes from shurcool/graphql #10

Merged
merged 10 commits into from
Oct 6, 2023
Merged
2 changes: 1 addition & 1 deletion .github/workflows/lint.yml
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ jobs:
- name: Set up Go
uses: actions/setup-go@v4
with:
go-version: '1.16'
go-version: '1.21'

- name: Lint
uses: golangci/golangci-lint-action@v3
Expand Down
2 changes: 1 addition & 1 deletion .github/workflows/test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ jobs:
fail-fast: false
matrix:
os: [ubuntu-latest, windows-latest, macos-latest]
go: [1.16]
go: [1.21]
runs-on: ${{ matrix.os }}

steps:
Expand Down
6 changes: 2 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,6 @@ Package `graphql` provides a GraphQL client implementation, and is forked from `
Installation
------------

`graphql` requires Go version 1.16 or later.

```bash
go get -u github.com/cli/shurcooL-graphql
```
Expand Down Expand Up @@ -130,7 +128,7 @@ var q struct {
Then, define a `variables` map with their values:

```Go
variables := map[string]interface{}{
variables := map[string]any{
"id": graphql.ID(id),
"unit": starwars.LengthUnit("METER"),
}
Expand Down Expand Up @@ -250,7 +248,7 @@ var m struct {
Commentary graphql.String
} `graphql:"createReview(episode: $ep, review: $review)"`
}
variables := map[string]interface{}{
variables := map[string]any{
"ep": starwars.Episode("JEDI"),
"review": starwars.ReviewInput{
Stars: graphql.Int(5),
Expand Down
4 changes: 1 addition & 3 deletions go.mod
Original file line number Diff line number Diff line change
@@ -1,5 +1,3 @@
module github.com/cli/shurcooL-graphql

go 1.16

require golang.org/x/net v0.15.0
go 1.21
40 changes: 0 additions & 40 deletions go.sum
Original file line number Diff line number Diff line change
@@ -1,40 +0,0 @@
github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY=
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
golang.org/x/crypto v0.13.0/go.mod h1:y6Z2r+Rw4iayiXXAIxJIDAJ1zMW4yaTpebo8fPOliYc=
golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4=
golang.org/x/mod v0.8.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs=
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c=
golang.org/x/net v0.6.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs=
golang.org/x/net v0.10.0/go.mod h1:0qNGK6F8kojg2nk9dLZ2mShWaEBan6FAoqfSigmmuDg=
golang.org/x/net v0.15.0 h1:ugBLEUaxABaB5AJqW9enI0ACdci2RUd4eP51NTBvuJ8=
golang.org/x/net v0.15.0/go.mod h1:idbUs1IY1+zTqbi8yxTbhexhEEk5ur9LInksu6HrEpk=
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.12.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k=
golang.org/x/term v0.8.0/go.mod h1:xPskH00ivmX89bAKVGSKKtLOWNx2+17Eiy94tnKShWo=
golang.org/x/term v0.12.0/go.mod h1:owVbMEjm3cBLCHdkQu9b1opXd4ETQWc3BhuQGKgXgvU=
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
golang.org/x/text v0.9.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8=
golang.org/x/text v0.13.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE=
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc=
golang.org/x/tools v0.6.0/go.mod h1:Xwgl3UAJ/d3gWutnCtw505GrjyAbvKui8lOU390QaIU=
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
38 changes: 21 additions & 17 deletions graphql.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,18 +5,17 @@ import (
"context"
"encoding/json"
"fmt"
"io/ioutil"
"io"
"net/http"
"strings"

"github.com/cli/shurcooL-graphql/internal/jsonutil"
"golang.org/x/net/context/ctxhttp"
)

// Client is a GraphQL client.
type Client struct {
url string // GraphQL server URL.
httpClient *http.Client
url string // GraphQL server URL.
httpClient *http.Client // Non-nil.
}

// NewClient creates a GraphQL client targeting the specified GraphQL server URL.
Expand All @@ -34,29 +33,29 @@ func NewClient(url string, httpClient *http.Client) *Client {
// Query executes a single GraphQL query request,
// with a query derived from q, populating the response into it.
// Argument q should be a pointer to struct that corresponds to the GraphQL schema.
func (c *Client) Query(ctx context.Context, q interface{}, variables map[string]interface{}) error {
func (c *Client) Query(ctx context.Context, q any, variables map[string]any) error {
return c.do(ctx, queryOperation, q, variables, "")
}

// QueryNamed is the same as Query but allows a name to be specified for the query.
func (c *Client) QueryNamed(ctx context.Context, queryName string, q interface{}, variables map[string]interface{}) error {
func (c *Client) QueryNamed(ctx context.Context, queryName string, q any, variables map[string]any) error {
return c.do(ctx, queryOperation, q, variables, queryName)
}

// Mutate executes a single GraphQL mutation request,
// with a mutation derived from m, populating the response into it.
// Argument m should be a pointer to struct that corresponds to the GraphQL schema.
func (c *Client) Mutate(ctx context.Context, m interface{}, variables map[string]interface{}) error {
func (c *Client) Mutate(ctx context.Context, m any, variables map[string]any) error {
return c.do(ctx, mutationOperation, m, variables, "")
}

// MutateNamed is the same as Mutate but allows a name to be specified for the mutation.
func (c *Client) MutateNamed(ctx context.Context, queryName string, m interface{}, variables map[string]interface{}) error {
func (c *Client) MutateNamed(ctx context.Context, queryName string, m any, variables map[string]any) error {
return c.do(ctx, mutationOperation, m, variables, queryName)
}

// do executes a single GraphQL operation.
func (c *Client) do(ctx context.Context, op operationType, v interface{}, variables map[string]interface{}, queryName string) error {
func (c *Client) do(ctx context.Context, op operationType, v any, variables map[string]any, queryName string) error {
var query string
switch op {
case queryOperation:
Expand All @@ -65,8 +64,8 @@ func (c *Client) do(ctx context.Context, op operationType, v interface{}, variab
query = constructMutation(v, variables, queryName)
}
in := struct {
Query string `json:"query"`
Variables map[string]interface{} `json:"variables,omitempty"`
Query string `json:"query"`
Variables map[string]any `json:"variables,omitempty"`
}{
Query: query,
Variables: variables,
Expand All @@ -76,19 +75,24 @@ func (c *Client) do(ctx context.Context, op operationType, v interface{}, variab
if err != nil {
return err
}
resp, err := ctxhttp.Post(ctx, c.httpClient, c.url, "application/json", &buf)
req, err := http.NewRequestWithContext(ctx, http.MethodPost, c.url, &buf)
if err != nil {
return err
}
req.Header.Set("Content-Type", "application/json")
resp, err := c.httpClient.Do(req)
Comment on lines +78 to +83
Copy link
Author

Choose a reason for hiding this comment

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

This is the change that allows for dropping golang.org/x/net as a dependency.

if err != nil {
return err
}
defer resp.Body.Close()
if resp.StatusCode != http.StatusOK {
body, _ := ioutil.ReadAll(resp.Body)
body, _ := io.ReadAll(resp.Body)
return fmt.Errorf("non-200 OK status code: %v body: %q", resp.Status, body)
}
var out struct {
Data *json.RawMessage
Errors Errors
//Extensions interface{} // Unused.
//Extensions any // Unused.
}
err = json.NewDecoder(resp.Body).Decode(&out)
if err != nil {
Expand All @@ -111,15 +115,15 @@ func (c *Client) do(ctx context.Context, op operationType, v interface{}, variab
// Errors represents the "errors" array in a response from a GraphQL server.
// If returned via error interface, the slice is expected to contain at least 1 element.
//
// Specification: http://spec.graphql.org/June2018/#sec-Errors
// Specification: https://spec.graphql.org/October2021/#sec-Errors.
type Errors []struct {
Message string
Locations []struct {
Line int
Column int
}
Path []interface{}
Extensions map[string]interface{}
Path []any
Extensions map[string]any
Type string
}

Expand Down
5 changes: 2 additions & 3 deletions graphql_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@ package graphql_test
import (
"context"
"io"
"io/ioutil"
"net/http"
"net/http/httptest"
"testing"
Expand Down Expand Up @@ -144,7 +143,7 @@ func TestClient_Query_emptyVariables(t *testing.T) {
Name string
}
}
err := client.Query(context.Background(), &q, map[string]interface{}{})
err := client.Query(context.Background(), &q, map[string]any{})
if err != nil {
t.Fatal(err)
}
Expand All @@ -166,7 +165,7 @@ func (l localRoundTripper) RoundTrip(req *http.Request) (*http.Response, error)
}

func mustRead(r io.Reader) string {
b, err := ioutil.ReadAll(r)
b, err := io.ReadAll(r)
if err != nil {
panic(err)
}
Expand Down
17 changes: 9 additions & 8 deletions ident/ident.go
Original file line number Diff line number Diff line change
Expand Up @@ -118,7 +118,7 @@ func ParseScreamingSnakeCase(name string) Name {
// Name is an identifier name, broken up into individual words.
type Name []string

// ToMixedCaps expresses identifer name in MixedCaps naming convention.
// ToMixedCaps expresses identifier name in MixedCaps naming convention.
//
// E.g., "ClientMutationID".
func (n Name) ToMixedCaps() string {
Expand All @@ -141,7 +141,7 @@ func (n Name) ToMixedCaps() string {
return strings.Join(n, "")
}

// ToLowerCamelCase expresses identifer name in lowerCamelCase naming convention.
// ToLowerCamelCase expresses identifier name in lowerCamelCase naming convention.
//
// E.g., "clientMutationId".
func (n Name) ToLowerCamelCase() string {
Expand Down Expand Up @@ -180,8 +180,6 @@ func isTwoInitialisms(word string) (string, string, bool) {
// Only add entries that are highly unlikely to be non-initialisms.
// For instance, "ID" is fine (Freudian code is rare), but "AND" is not.
var initialisms = map[string]struct{}{
// These are the common initialisms from golint. Keep them in sync
// with https://gotools.org/github.com/golang/lint#commonInitialisms.
"ACL": {},
"API": {},
"ASCII": {},
Expand All @@ -201,6 +199,7 @@ var initialisms = map[string]struct{}{
"RAM": {},
"RHS": {},
"RPC": {},
"RSS": {},
"SLA": {},
"SMTP": {},
"SQL": {},
Expand All @@ -211,18 +210,15 @@ var initialisms = map[string]struct{}{
"UDP": {},
"UI": {},
"UID": {},
"UUID": {},
"URI": {},
"URL": {},
"UTF8": {},
"UUID": {},
"VM": {},
"XML": {},
"XMPP": {},
"XSRF": {},
"XSS": {},

// Additional common initialisms.
"RSS": {},
}

// isBrand reports whether word is a brand.
Expand All @@ -237,4 +233,9 @@ func isBrand(word string) (string, bool) {
// Only add entries that are highly unlikely to be non-brands.
var brands = map[string]string{
"github": "GitHub",
"gitlab": "GitLab",
"devops": "DevOps", // For https://en.wikipedia.org/wiki/DevOps.
// For https://docs.github.com/en/graphql/reference/enums#fundingplatform.
"issuehunt": "IssueHunt",
"lfx": "LFX",
}
1 change: 1 addition & 0 deletions ident/ident_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -89,6 +89,7 @@ func TestName_ToMixedCaps(t *testing.T) {
{in: ident.Name{"client", "Mutation", "Id"}, want: "ClientMutationID"},
{in: ident.Name{"CLIENT", "MUTATION", "ID"}, want: "ClientMutationID"},
{in: ident.Name{"github", "logo"}, want: "GitHubLogo"},
{in: ident.Name{"AZURE", "DEVOPS"}, want: "AzureDevOps"},
}
for _, tc := range tests {
got := tc.in.ToMixedCaps()
Expand Down
11 changes: 5 additions & 6 deletions internal/jsonutil/graphql.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ import (
//
// The implementation is created on top of the JSON tokenizer available
// in "encoding/json".Decoder.
func UnmarshalGraphQL(data []byte, v interface{}) error {
func UnmarshalGraphQL(data []byte, v any) error {
dec := json.NewDecoder(bytes.NewReader(data))
dec.UseNumber()
err := (&decoder{tokenizer: dec}).Decode(v)
Expand Down Expand Up @@ -57,7 +57,7 @@ type decoder struct {
}

// Decode decodes a single JSON value from d.tokenizer into v.
func (d *decoder) Decode(v interface{}) error {
func (d *decoder) Decode(v any) error {
rv := reflect.ValueOf(v)
if rv.Kind() != reflect.Ptr {
return fmt.Errorf("cannot decode into non-pointer %T", v)
Expand Down Expand Up @@ -280,10 +280,9 @@ func hasGraphQLName(f reflect.StructField, name string) bool {
// GraphQL fragment. It doesn't have a name.
return false
}
if i := strings.Index(value, "("); i != -1 {
value = value[:i]
}
if i := strings.Index(value, ":"); i != -1 {
// Cut off anything that follows the field name,
// such as field arguments, aliases, directives.
if i := strings.IndexAny(value, "(:@"); i != -1 {
value = value[:i]
}
return strings.TrimSpace(value) == name
Expand Down
33 changes: 33 additions & 0 deletions internal/jsonutil/graphql_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -267,6 +267,39 @@ func TestUnmarshalGraphQL_multipleValues(t *testing.T) {
}
}

func TestUnmarshalGraphQL_directives(t *testing.T) {
/*
query {
me {
name @include(if: true)
height @skip(if: false)
}
}
*/
type query struct {
Me struct {
Name graphql.String `graphql:"name @include(if: true)"`
Height graphql.Float `graphql:"height @skip(if: false)"`
}
}
var got query
err := jsonutil.UnmarshalGraphQL([]byte(`{
"me": {
"name": "Luke Skywalker",
"height": 1.72
}
}`), &got)
if err != nil {
t.Fatal(err)
}
var want query
want.Me.Name = "Luke Skywalker"
want.Me.Height = 1.72
if !reflect.DeepEqual(got, want) {
t.Error("not equal")
}
}

func TestUnmarshalGraphQL_union(t *testing.T) {
/*
{
Expand Down
Loading
Loading