mirror of
https://github.com/schollz/cowyo.git
synced 2023-08-10 21:13:00 +03:00
Vendoring
This commit is contained in:
16
vendor/github.com/shurcooL/graphql/.travis.yml
generated
vendored
Normal file
16
vendor/github.com/shurcooL/graphql/.travis.yml
generated
vendored
Normal file
@@ -0,0 +1,16 @@
|
||||
sudo: false
|
||||
language: go
|
||||
go:
|
||||
- 1.x
|
||||
- master
|
||||
matrix:
|
||||
allow_failures:
|
||||
- go: master
|
||||
fast_finish: true
|
||||
install:
|
||||
- # Do nothing. This is needed to prevent default install action "go get -t -v ./..." from happening here (we want it to happen inside script step).
|
||||
script:
|
||||
- go get -t -v ./...
|
||||
- diff -u <(echo -n) <(gofmt -d -s .)
|
||||
- go tool vet .
|
||||
- go test -v -race ./...
|
||||
21
vendor/github.com/shurcooL/graphql/LICENSE
generated
vendored
Normal file
21
vendor/github.com/shurcooL/graphql/LICENSE
generated
vendored
Normal file
@@ -0,0 +1,21 @@
|
||||
MIT License
|
||||
|
||||
Copyright (c) 2017 Dmitri Shuralyov
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
219
vendor/github.com/shurcooL/graphql/README.md
generated
vendored
Normal file
219
vendor/github.com/shurcooL/graphql/README.md
generated
vendored
Normal file
@@ -0,0 +1,219 @@
|
||||
graphql
|
||||
=======
|
||||
|
||||
[](https://travis-ci.org/shurcooL/graphql) [](https://godoc.org/github.com/shurcooL/graphql)
|
||||
|
||||
Package `graphql` provides a GraphQL client implementation.
|
||||
|
||||
For more information, see package [`github.com/shurcooL/githubql`](https://github.com/shurcooL/githubql), which is a specialized version targeting GitHub GraphQL API v4. That package is driving the feature development.
|
||||
|
||||
**Status:** In active early research and development. The API will change when opportunities for improvement are discovered; it is not yet frozen.
|
||||
|
||||
Installation
|
||||
------------
|
||||
|
||||
`graphql` requires Go version 1.8 or later.
|
||||
|
||||
```bash
|
||||
go get -u github.com/shurcooL/graphql
|
||||
```
|
||||
|
||||
Usage
|
||||
-----
|
||||
|
||||
Construct a GraphQL client, specifying the GraphQL server URL. Then, you can use it to make GraphQL queries and mutations.
|
||||
|
||||
```Go
|
||||
client := graphql.NewClient("https://example.com/graphql", nil, nil)
|
||||
// Use client...
|
||||
```
|
||||
|
||||
### Authentication
|
||||
|
||||
Some GraphQL servers may require authentication. The `graphql` package does not directly handle authentication. Instead, when creating a new client, you're expected to pass an `http.Client` that performs authentication. The easiest and recommended way to do this is to use the [`golang.org/x/oauth2`](https://golang.org/x/oauth2) package. You'll need an OAuth token with the right scopes. Then:
|
||||
|
||||
```Go
|
||||
import "golang.org/x/oauth2"
|
||||
|
||||
func main() {
|
||||
src := oauth2.StaticTokenSource(
|
||||
&oauth2.Token{AccessToken: os.Getenv("GRAPHQL_TOKEN")},
|
||||
)
|
||||
httpClient := oauth2.NewClient(context.Background(), src)
|
||||
|
||||
client := graphql.NewClient("https://example.com/graphql", httpClient, nil)
|
||||
// Use client...
|
||||
```
|
||||
|
||||
### Simple Query
|
||||
|
||||
To make a GraphQL query, you need to define a corresponding Go type.
|
||||
|
||||
For example, to make the following GraphQL query:
|
||||
|
||||
```GraphQL
|
||||
query {
|
||||
me {
|
||||
name
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
You can define this variable:
|
||||
|
||||
```Go
|
||||
var query struct {
|
||||
Me struct {
|
||||
Name graphql.String
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
And call `client.Query`, passing a pointer to it:
|
||||
|
||||
```Go
|
||||
err := client.Query(context.Background(), &query, nil)
|
||||
if err != nil {
|
||||
// Handle error.
|
||||
}
|
||||
fmt.Println(query.Me.Name)
|
||||
|
||||
// Output: Luke Skywalker
|
||||
```
|
||||
|
||||
### Arguments and Variables
|
||||
|
||||
Often, you'll want to specify arguments on some fields. You can use the `graphql` struct field tag for this.
|
||||
|
||||
For example, to make the following GraphQL query:
|
||||
|
||||
```GraphQL
|
||||
{
|
||||
human(id: "1000") {
|
||||
name
|
||||
height(unit: METER)
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
You can define this variable:
|
||||
|
||||
```Go
|
||||
var q struct {
|
||||
Human struct {
|
||||
Name graphql.String
|
||||
Height graphql.Float `graphql:"height(unit: METER)"`
|
||||
} `graphql:"human(id: \"1000\")"`
|
||||
}
|
||||
```
|
||||
|
||||
And call `client.Query`:
|
||||
|
||||
```Go
|
||||
err := client.Query(context.Background(), &q, nil)
|
||||
if err != nil {
|
||||
// Handle error.
|
||||
}
|
||||
fmt.Println(q.Human.Name)
|
||||
fmt.Println(q.Human.Height)
|
||||
|
||||
// Output:
|
||||
// Luke Skywalker
|
||||
// 1.72
|
||||
```
|
||||
|
||||
However, that'll only work if the arguments are constant and known in advance. Otherwise, you will need to make use of variables. Replace the constants in the struct field tag with variable names:
|
||||
|
||||
```Go
|
||||
var q struct {
|
||||
Human struct {
|
||||
Name graphql.String
|
||||
Height graphql.Float `graphql:"height(unit: $unit)"`
|
||||
} `graphql:"human(id: $id)"`
|
||||
}
|
||||
```
|
||||
|
||||
Then, define a `variables` map with their values:
|
||||
|
||||
```Go
|
||||
variables := map[string]interface{}{
|
||||
"id": graphql.ID(id),
|
||||
"unit": starwars.LengthUnit("METER"),
|
||||
}
|
||||
```
|
||||
|
||||
Finally, call `client.Query` providing `variables`:
|
||||
|
||||
```Go
|
||||
err := client.Query(context.Background(), &q, variables)
|
||||
if err != nil {
|
||||
// Handle error.
|
||||
}
|
||||
```
|
||||
|
||||
### Mutations
|
||||
|
||||
Mutations often require information that you can only find out by performing a query first. Let's suppose you've already done that.
|
||||
|
||||
For example, to make the following GraphQL mutation:
|
||||
|
||||
```GraphQL
|
||||
mutation($ep: Episode!, $review: ReviewInput!) {
|
||||
createReview(episode: $ep, review: $review) {
|
||||
stars
|
||||
commentary
|
||||
}
|
||||
}
|
||||
variables {
|
||||
"ep": "JEDI",
|
||||
"review": {
|
||||
"stars": 5,
|
||||
"commentary": "This is a great movie!"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
You can define:
|
||||
|
||||
```Go
|
||||
var m struct {
|
||||
CreateReview struct {
|
||||
Stars graphql.Int
|
||||
Commentary graphql.String
|
||||
} `graphql:"createReview(episode: $ep, review: $review)"`
|
||||
}
|
||||
variables := map[string]interface{}{
|
||||
"ep": starwars.Episode("JEDI"),
|
||||
"review": starwars.ReviewInput{
|
||||
Stars: graphql.Int(5),
|
||||
Commentary: graphql.String("This is a great movie!"),
|
||||
},
|
||||
}
|
||||
```
|
||||
|
||||
And call `client.Mutate`:
|
||||
|
||||
```Go
|
||||
err := client.Mutate(context.Background(), &m, variables)
|
||||
if err != nil {
|
||||
// Handle error.
|
||||
}
|
||||
fmt.Printf("Created a %v star review: %v\n", m.CreateReview.Stars, m.CreateReview.Commentary)
|
||||
|
||||
// Output:
|
||||
// Created a 5 star review: This is a great movie!
|
||||
```
|
||||
|
||||
Directories
|
||||
-----------
|
||||
|
||||
| Path | Synopsis |
|
||||
|----------------------------------------------------------------------------------------|-----------------------------------------------------------------------------------------------------------------|
|
||||
| [example/graphqldev](https://godoc.org/github.com/shurcooL/graphql/example/graphqldev) | graphqldev is a test program currently being used for developing graphql package. |
|
||||
| [ident](https://godoc.org/github.com/shurcooL/graphql/ident) | Package ident provides functions for parsing and converting identifier names between various naming convention. |
|
||||
| [internal/jsonutil](https://godoc.org/github.com/shurcooL/graphql/internal/jsonutil) | Package jsonutil provides a function for decoding JSON into a GraphQL query data structure. |
|
||||
|
||||
License
|
||||
-------
|
||||
|
||||
- [MIT License](LICENSE)
|
||||
11
vendor/github.com/shurcooL/graphql/doc.go
generated
vendored
Normal file
11
vendor/github.com/shurcooL/graphql/doc.go
generated
vendored
Normal file
@@ -0,0 +1,11 @@
|
||||
// Package graphql provides a GraphQL client implementation.
|
||||
//
|
||||
// For more information, see package github.com/shurcooL/githubql,
|
||||
// which is a specialized version targeting GitHub GraphQL API v4.
|
||||
// That package is driving the feature development.
|
||||
//
|
||||
// Status: In active early research and development. The API will change when
|
||||
// opportunities for improvement are discovered; it is not yet frozen.
|
||||
//
|
||||
// For now, see README for more details.
|
||||
package graphql // import "github.com/shurcooL/graphql"
|
||||
105
vendor/github.com/shurcooL/graphql/example/graphqldev/main.go
generated
vendored
Normal file
105
vendor/github.com/shurcooL/graphql/example/graphqldev/main.go
generated
vendored
Normal file
@@ -0,0 +1,105 @@
|
||||
// graphqldev is a test program currently being used for developing graphql package.
|
||||
// It performs queries against a local test GraphQL server instance.
|
||||
//
|
||||
// It's not meant to be a clean or readable example. But it's functional.
|
||||
// Better, actual examples will be created in the future.
|
||||
package main
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"flag"
|
||||
"log"
|
||||
"net/http"
|
||||
"net/http/httptest"
|
||||
"os"
|
||||
|
||||
graphqlserver "github.com/neelance/graphql-go"
|
||||
"github.com/neelance/graphql-go/example/starwars"
|
||||
"github.com/neelance/graphql-go/relay"
|
||||
"github.com/shurcooL/graphql"
|
||||
)
|
||||
|
||||
func main() {
|
||||
flag.Parse()
|
||||
|
||||
err := run()
|
||||
if err != nil {
|
||||
log.Println(err)
|
||||
}
|
||||
}
|
||||
|
||||
func run() error {
|
||||
// Set up a GraphQL server.
|
||||
schema, err := graphqlserver.ParseSchema(starwars.Schema, &starwars.Resolver{})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
mux := http.NewServeMux()
|
||||
mux.Handle("/query", &relay.Handler{Schema: schema})
|
||||
|
||||
client := graphql.NewClient("/query", &http.Client{Transport: localRoundTripper{handler: mux}}, nil)
|
||||
|
||||
/*
|
||||
query {
|
||||
hero {
|
||||
id
|
||||
name
|
||||
}
|
||||
character(id: "1003") {
|
||||
name
|
||||
friends {
|
||||
name
|
||||
__typename
|
||||
}
|
||||
appearsIn
|
||||
}
|
||||
}
|
||||
*/
|
||||
var q struct {
|
||||
Hero struct {
|
||||
ID graphql.ID
|
||||
Name graphql.String
|
||||
}
|
||||
Character struct {
|
||||
Name graphql.String
|
||||
Friends []struct {
|
||||
Name graphql.String
|
||||
Typename graphql.String `graphql:"__typename"`
|
||||
}
|
||||
AppearsIn []graphql.String
|
||||
} `graphql:"character(id: $characterID)"`
|
||||
}
|
||||
variables := map[string]interface{}{
|
||||
"characterID": graphql.ID("1003"),
|
||||
}
|
||||
err = client.Query(context.Background(), &q, variables)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
print(q)
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// print pretty prints v to stdout. It panics on any error.
|
||||
func print(v interface{}) {
|
||||
w := json.NewEncoder(os.Stdout)
|
||||
w.SetIndent("", "\t")
|
||||
err := w.Encode(v)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
}
|
||||
|
||||
// localRoundTripper is an http.RoundTripper that executes HTTP transactions
|
||||
// by using handler directly, instead of going over an HTTP connection.
|
||||
type localRoundTripper struct {
|
||||
handler http.Handler
|
||||
}
|
||||
|
||||
func (l localRoundTripper) RoundTrip(req *http.Request) (*http.Response, error) {
|
||||
w := httptest.NewRecorder()
|
||||
l.handler.ServeHTTP(w, req)
|
||||
return w.Result(), nil
|
||||
}
|
||||
123
vendor/github.com/shurcooL/graphql/graphql.go
generated
vendored
Normal file
123
vendor/github.com/shurcooL/graphql/graphql.go
generated
vendored
Normal file
@@ -0,0 +1,123 @@
|
||||
package graphql
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"net/http"
|
||||
"reflect"
|
||||
|
||||
"github.com/shurcooL/go/ctxhttp"
|
||||
"github.com/shurcooL/graphql/internal/jsonutil"
|
||||
)
|
||||
|
||||
// Client is a GraphQL client.
|
||||
type Client struct {
|
||||
url string // GraphQL server URL.
|
||||
httpClient *http.Client
|
||||
|
||||
qctx *queryContext
|
||||
}
|
||||
|
||||
// NewClient creates a GraphQL client targeting the specified GraphQL server URL.
|
||||
// If httpClient is nil, then http.DefaultClient is used.
|
||||
// scalars optionally specifies types that are scalars (this matters
|
||||
// when constructing queries from types, scalars are never expanded).
|
||||
func NewClient(url string, httpClient *http.Client, scalars []reflect.Type) *Client {
|
||||
if httpClient == nil {
|
||||
httpClient = http.DefaultClient
|
||||
}
|
||||
return &Client{
|
||||
url: url,
|
||||
httpClient: httpClient,
|
||||
|
||||
qctx: &queryContext{
|
||||
Scalars: scalars,
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
// Query executes a single GraphQL query request,
|
||||
// with a query derived from q, populating the response into it.
|
||||
// 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 {
|
||||
return c.do(ctx, queryOperation, q, variables)
|
||||
}
|
||||
|
||||
// Mutate executes a single GraphQL mutation request,
|
||||
// with a mutation derived from m, populating the response into it.
|
||||
// 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 {
|
||||
return c.do(ctx, mutationOperation, m, variables)
|
||||
}
|
||||
|
||||
// do executes a single GraphQL operation.
|
||||
func (c *Client) do(ctx context.Context, op operationType, v interface{}, variables map[string]interface{}) error {
|
||||
var query string
|
||||
switch op {
|
||||
case queryOperation:
|
||||
query = constructQuery(c.qctx, v, variables)
|
||||
case mutationOperation:
|
||||
query = constructMutation(c.qctx, v, variables)
|
||||
}
|
||||
in := struct {
|
||||
Query string `json:"query"`
|
||||
Variables map[string]interface{} `json:"variables,omitempty"`
|
||||
}{
|
||||
Query: query,
|
||||
Variables: variables,
|
||||
}
|
||||
var buf bytes.Buffer
|
||||
err := json.NewEncoder(&buf).Encode(in)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
resp, err := ctxhttp.Post(ctx, c.httpClient, c.url, "application/json", &buf)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
if resp.StatusCode != http.StatusOK {
|
||||
return fmt.Errorf("unexpected status: %v", resp.Status)
|
||||
}
|
||||
var out struct {
|
||||
Data json.RawMessage
|
||||
Errors errors
|
||||
//Extensions interface{} // Unused.
|
||||
}
|
||||
err = json.NewDecoder(resp.Body).Decode(&out)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if len(out.Errors) > 0 {
|
||||
return out.Errors
|
||||
}
|
||||
err = jsonutil.UnmarshalGraphQL(out.Data, v)
|
||||
return err
|
||||
}
|
||||
|
||||
// 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: https://facebook.github.io/graphql/#sec-Errors.
|
||||
type errors []struct {
|
||||
Message string
|
||||
Locations []struct {
|
||||
Line int
|
||||
Column int
|
||||
}
|
||||
}
|
||||
|
||||
// Error implements error interface.
|
||||
func (e errors) Error() string {
|
||||
return e[0].Message
|
||||
}
|
||||
|
||||
type operationType uint8
|
||||
|
||||
const (
|
||||
queryOperation operationType = iota
|
||||
mutationOperation
|
||||
//subscriptionOperation // Unused.
|
||||
)
|
||||
222
vendor/github.com/shurcooL/graphql/ident/ident.go
generated
vendored
Normal file
222
vendor/github.com/shurcooL/graphql/ident/ident.go
generated
vendored
Normal file
@@ -0,0 +1,222 @@
|
||||
// Package ident provides functions for parsing and converting identifier names
|
||||
// between various naming convention. It has support for MixedCaps, lowerCamelCase,
|
||||
// and SCREAMING_SNAKE_CASE naming conventions.
|
||||
package ident
|
||||
|
||||
import (
|
||||
"strings"
|
||||
"unicode"
|
||||
"unicode/utf8"
|
||||
)
|
||||
|
||||
// ParseMixedCaps parses a MixedCaps identifier name.
|
||||
//
|
||||
// E.g., "ClientMutationID" -> {"Client", "Mutation", "ID"}.
|
||||
func ParseMixedCaps(name string) Name {
|
||||
var words Name
|
||||
|
||||
// Split name at any lower -> Upper or Upper -> Upper,lower transitions.
|
||||
// Check each word for initialisms.
|
||||
runes := []rune(name)
|
||||
w, i := 0, 0 // Index of start of word, scan.
|
||||
for i+1 <= len(runes) {
|
||||
eow := false // Whether we hit the end of a word.
|
||||
if i+1 == len(runes) {
|
||||
eow = true
|
||||
} else if unicode.IsLower(runes[i]) && unicode.IsUpper(runes[i+1]) {
|
||||
// lower -> Upper.
|
||||
eow = true
|
||||
} else if i+2 < len(runes) && unicode.IsUpper(runes[i]) && unicode.IsUpper(runes[i+1]) && unicode.IsLower(runes[i+2]) {
|
||||
// Upper -> Upper,lower. End of acronym, followed by a word.
|
||||
eow = true
|
||||
|
||||
if string(runes[i:i+3]) == "IDs" { // Special case, plural form of ID initialism.
|
||||
eow = false
|
||||
}
|
||||
}
|
||||
i++
|
||||
if !eow {
|
||||
continue
|
||||
}
|
||||
|
||||
// [w, i) is a word.
|
||||
word := string(runes[w:i])
|
||||
if initialism, ok := isInitialism(word); ok {
|
||||
words = append(words, initialism)
|
||||
} else if i1, i2, ok := isTwoInitialisms(word); ok {
|
||||
words = append(words, i1, i2)
|
||||
} else {
|
||||
words = append(words, word)
|
||||
}
|
||||
w = i
|
||||
}
|
||||
return words
|
||||
}
|
||||
|
||||
// ParseLowerCamelCase parses a lowerCamelCase identifier name.
|
||||
//
|
||||
// E.g., "clientMutationId" -> {"client", "Mutation", "Id"}.
|
||||
func ParseLowerCamelCase(name string) Name {
|
||||
var words Name
|
||||
|
||||
// Split name at any Upper letters.
|
||||
runes := []rune(name)
|
||||
w, i := 0, 0 // Index of start of word, scan.
|
||||
for i+1 <= len(runes) {
|
||||
eow := false // Whether we hit the end of a word.
|
||||
if i+1 == len(runes) {
|
||||
eow = true
|
||||
} else if unicode.IsUpper(runes[i+1]) {
|
||||
// Upper letter.
|
||||
eow = true
|
||||
}
|
||||
i++
|
||||
if !eow {
|
||||
continue
|
||||
}
|
||||
|
||||
// [w, i) is a word.
|
||||
words = append(words, string(runes[w:i]))
|
||||
w = i
|
||||
}
|
||||
return words
|
||||
}
|
||||
|
||||
// ParseScreamingSnakeCase parses a SCREAMING_SNAKE_CASE identifier name.
|
||||
//
|
||||
// E.g., "CLIENT_MUTATION_ID" -> {"CLIENT", "MUTATION", "ID"}.
|
||||
func ParseScreamingSnakeCase(name string) Name {
|
||||
var words Name
|
||||
|
||||
// Split name at '_' characters.
|
||||
runes := []rune(name)
|
||||
w, i := 0, 0 // Index of start of word, scan.
|
||||
for i+1 <= len(runes) {
|
||||
eow := false // Whether we hit the end of a word.
|
||||
if i+1 == len(runes) {
|
||||
eow = true
|
||||
} else if runes[i+1] == '_' {
|
||||
// Underscore.
|
||||
eow = true
|
||||
}
|
||||
i++
|
||||
if !eow {
|
||||
continue
|
||||
}
|
||||
|
||||
// [w, i) is a word.
|
||||
words = append(words, string(runes[w:i]))
|
||||
if i < len(runes) && runes[i] == '_' {
|
||||
// Skip underscore.
|
||||
i++
|
||||
}
|
||||
w = i
|
||||
}
|
||||
return words
|
||||
}
|
||||
|
||||
// Name is an identifier name, broken up into individual words.
|
||||
type Name []string
|
||||
|
||||
// ToMixedCaps expresses identifer name in MixedCaps naming convention.
|
||||
//
|
||||
// E.g., "ClientMutationID".
|
||||
func (n Name) ToMixedCaps() string {
|
||||
for i, word := range n {
|
||||
if strings.EqualFold(word, "IDs") { // Special case, plural form of ID initialism.
|
||||
n[i] = "IDs"
|
||||
continue
|
||||
}
|
||||
if initialism, ok := isInitialism(word); ok {
|
||||
n[i] = initialism
|
||||
continue
|
||||
}
|
||||
r, size := utf8.DecodeRuneInString(word)
|
||||
n[i] = string(unicode.ToUpper(r)) + strings.ToLower(word[size:])
|
||||
}
|
||||
return strings.Join(n, "")
|
||||
}
|
||||
|
||||
// ToLowerCamelCase expresses identifer name in lowerCamelCase naming convention.
|
||||
//
|
||||
// E.g., "clientMutationId".
|
||||
func (n Name) ToLowerCamelCase() string {
|
||||
for i, word := range n {
|
||||
if i == 0 {
|
||||
n[i] = strings.ToLower(word)
|
||||
continue
|
||||
}
|
||||
r, size := utf8.DecodeRuneInString(word)
|
||||
n[i] = string(unicode.ToUpper(r)) + strings.ToLower(word[size:])
|
||||
}
|
||||
return strings.Join(n, "")
|
||||
}
|
||||
|
||||
// isInitialism reports whether word is an initialism.
|
||||
func isInitialism(word string) (string, bool) {
|
||||
initialism := strings.ToUpper(word)
|
||||
_, ok := initialisms[initialism]
|
||||
return initialism, ok
|
||||
}
|
||||
|
||||
// isTwoInitialisms reports whether word is two initialisms.
|
||||
func isTwoInitialisms(word string) (string, string, bool) {
|
||||
word = strings.ToUpper(word)
|
||||
for i := 2; i <= len(word)-2; i++ { // Shortest initialism is 2 characters long.
|
||||
_, ok1 := initialisms[word[:i]]
|
||||
_, ok2 := initialisms[word[i:]]
|
||||
if ok1 && ok2 {
|
||||
return word[:i], word[i:], true
|
||||
}
|
||||
}
|
||||
return "", "", false
|
||||
}
|
||||
|
||||
// initialisms is the set of initialisms in the MixedCaps naming convention.
|
||||
// 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": {},
|
||||
"CPU": {},
|
||||
"CSS": {},
|
||||
"DNS": {},
|
||||
"EOF": {},
|
||||
"GUID": {},
|
||||
"HTML": {},
|
||||
"HTTP": {},
|
||||
"HTTPS": {},
|
||||
"ID": {},
|
||||
"IP": {},
|
||||
"JSON": {},
|
||||
"LHS": {},
|
||||
"QPS": {},
|
||||
"RAM": {},
|
||||
"RHS": {},
|
||||
"RPC": {},
|
||||
"SLA": {},
|
||||
"SMTP": {},
|
||||
"SQL": {},
|
||||
"SSH": {},
|
||||
"TCP": {},
|
||||
"TLS": {},
|
||||
"TTL": {},
|
||||
"UDP": {},
|
||||
"UI": {},
|
||||
"UID": {},
|
||||
"UUID": {},
|
||||
"URI": {},
|
||||
"URL": {},
|
||||
"UTF8": {},
|
||||
"VM": {},
|
||||
"XML": {},
|
||||
"XMPP": {},
|
||||
"XSRF": {},
|
||||
"XSS": {},
|
||||
|
||||
// Additional common initialisms.
|
||||
"RSS": {},
|
||||
}
|
||||
164
vendor/github.com/shurcooL/graphql/ident/ident_test.go
generated
vendored
Normal file
164
vendor/github.com/shurcooL/graphql/ident/ident_test.go
generated
vendored
Normal file
@@ -0,0 +1,164 @@
|
||||
package ident_test
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"reflect"
|
||||
"testing"
|
||||
|
||||
"github.com/shurcooL/graphql/ident"
|
||||
)
|
||||
|
||||
func Example_lowerCamelCaseToMixedCaps() {
|
||||
fmt.Println(ident.ParseLowerCamelCase("clientMutationId").ToMixedCaps())
|
||||
|
||||
// Output: ClientMutationID
|
||||
}
|
||||
|
||||
func Example_screamingSnakeCaseToMixedCaps() {
|
||||
fmt.Println(ident.ParseScreamingSnakeCase("CLIENT_MUTATION_ID").ToMixedCaps())
|
||||
|
||||
// Output: ClientMutationID
|
||||
}
|
||||
|
||||
func Example_mixedCapsToLowerCamelCase() {
|
||||
fmt.Println(ident.ParseMixedCaps("ClientMutationID").ToLowerCamelCase())
|
||||
|
||||
// Output: clientMutationId
|
||||
}
|
||||
|
||||
func TestParseMixedCaps(t *testing.T) {
|
||||
tests := []struct {
|
||||
in string
|
||||
want ident.Name
|
||||
}{
|
||||
{in: "ClientMutationID", want: ident.Name{"Client", "Mutation", "ID"}},
|
||||
{in: "StringURLAppend", want: ident.Name{"String", "URL", "Append"}},
|
||||
{in: "URLFrom", want: ident.Name{"URL", "From"}},
|
||||
{in: "SetURL", want: ident.Name{"Set", "URL"}},
|
||||
{in: "UIIP", want: ident.Name{"UI", "IP"}},
|
||||
{in: "URLHTMLFrom", want: ident.Name{"URL", "HTML", "From"}},
|
||||
{in: "SetURLHTML", want: ident.Name{"Set", "URL", "HTML"}},
|
||||
{in: "HTTPSQL", want: ident.Name{"HTTP", "SQL"}},
|
||||
{in: "HTTPSSQL", want: ident.Name{"HTTPS", "SQL"}},
|
||||
{in: "UserIDs", want: ident.Name{"User", "IDs"}},
|
||||
{in: "TeamIDsSorted", want: ident.Name{"Team", "IDs", "Sorted"}},
|
||||
}
|
||||
for _, tc := range tests {
|
||||
got := ident.ParseMixedCaps(tc.in)
|
||||
if !reflect.DeepEqual(got, tc.want) {
|
||||
t.Errorf("got: %q, want: %q", got, tc.want)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestParseLowerCamelCase(t *testing.T) {
|
||||
tests := []struct {
|
||||
in string
|
||||
want ident.Name
|
||||
}{
|
||||
{in: "clientMutationId", want: ident.Name{"client", "Mutation", "Id"}},
|
||||
}
|
||||
for _, tc := range tests {
|
||||
got := ident.ParseLowerCamelCase(tc.in)
|
||||
if !reflect.DeepEqual(got, tc.want) {
|
||||
t.Errorf("got: %q, want: %q", got, tc.want)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestParseScreamingSnakeCase(t *testing.T) {
|
||||
tests := []struct {
|
||||
in string
|
||||
want ident.Name
|
||||
}{
|
||||
{in: "CLIENT_MUTATION_ID", want: ident.Name{"CLIENT", "MUTATION", "ID"}},
|
||||
}
|
||||
for _, tc := range tests {
|
||||
got := ident.ParseScreamingSnakeCase(tc.in)
|
||||
if !reflect.DeepEqual(got, tc.want) {
|
||||
t.Errorf("got: %q, want: %q", got, tc.want)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestWords_ToMixedCaps(t *testing.T) {
|
||||
tests := []struct {
|
||||
in ident.Name
|
||||
want string
|
||||
}{
|
||||
{in: ident.Name{"client", "Mutation", "Id"}, want: "ClientMutationID"},
|
||||
{in: ident.Name{"CLIENT", "MUTATION", "ID"}, want: "ClientMutationID"},
|
||||
}
|
||||
for _, tc := range tests {
|
||||
got := tc.in.ToMixedCaps()
|
||||
if got != tc.want {
|
||||
t.Errorf("got: %q, want: %q", got, tc.want)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestWords_ToLowerCamelCase(t *testing.T) {
|
||||
tests := []struct {
|
||||
in ident.Name
|
||||
want string
|
||||
}{
|
||||
{in: ident.Name{"client", "Mutation", "Id"}, want: "clientMutationId"},
|
||||
{in: ident.Name{"CLIENT", "MUTATION", "ID"}, want: "clientMutationId"},
|
||||
}
|
||||
for _, tc := range tests {
|
||||
got := tc.in.ToLowerCamelCase()
|
||||
if got != tc.want {
|
||||
t.Errorf("got: %q, want: %q", got, tc.want)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestMixedCapsToLowerCamelCase(t *testing.T) {
|
||||
tests := []struct {
|
||||
in string
|
||||
want string
|
||||
}{
|
||||
{in: "DatabaseID", want: "databaseId"},
|
||||
{in: "URL", want: "url"},
|
||||
{in: "ID", want: "id"},
|
||||
{in: "CreatedAt", want: "createdAt"},
|
||||
{in: "Login", want: "login"},
|
||||
{in: "ResetAt", want: "resetAt"},
|
||||
{in: "ID", want: "id"},
|
||||
{in: "IDs", want: "ids"},
|
||||
{in: "IDsAndNames", want: "idsAndNames"},
|
||||
{in: "UserIDs", want: "userIds"},
|
||||
{in: "TeamIDsSorted", want: "teamIdsSorted"},
|
||||
}
|
||||
for _, tc := range tests {
|
||||
got := ident.ParseMixedCaps(tc.in).ToLowerCamelCase()
|
||||
if got != tc.want {
|
||||
t.Errorf("got: %q, want: %q", got, tc.want)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestLowerCamelCaseToMixedCaps(t *testing.T) {
|
||||
tests := []struct {
|
||||
in string
|
||||
want string
|
||||
}{
|
||||
{in: "databaseId", want: "DatabaseID"},
|
||||
{in: "url", want: "URL"},
|
||||
{in: "id", want: "ID"},
|
||||
{in: "createdAt", want: "CreatedAt"},
|
||||
{in: "login", want: "Login"},
|
||||
{in: "resetAt", want: "ResetAt"},
|
||||
{in: "id", want: "ID"},
|
||||
{in: "ids", want: "IDs"},
|
||||
{in: "idsAndNames", want: "IDsAndNames"},
|
||||
{in: "userIds", want: "UserIDs"},
|
||||
{in: "teamIdsSorted", want: "TeamIDsSorted"},
|
||||
}
|
||||
for _, tc := range tests {
|
||||
got := ident.ParseLowerCamelCase(tc.in).ToMixedCaps()
|
||||
if got != tc.want {
|
||||
t.Errorf("got: %q, want: %q", got, tc.want)
|
||||
}
|
||||
}
|
||||
}
|
||||
127
vendor/github.com/shurcooL/graphql/internal/jsonutil/benchmark_test.go
generated
vendored
Normal file
127
vendor/github.com/shurcooL/graphql/internal/jsonutil/benchmark_test.go
generated
vendored
Normal file
@@ -0,0 +1,127 @@
|
||||
package jsonutil_test
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"io"
|
||||
"reflect"
|
||||
"strings"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/shurcooL/graphql"
|
||||
"github.com/shurcooL/graphql/internal/jsonutil"
|
||||
)
|
||||
|
||||
func TestUnmarshalGraphQL_benchmark(t *testing.T) {
|
||||
/*
|
||||
query {
|
||||
viewer {
|
||||
login
|
||||
createdAt
|
||||
}
|
||||
}
|
||||
*/
|
||||
type query struct {
|
||||
Viewer struct {
|
||||
Login graphql.String
|
||||
CreatedAt time.Time
|
||||
}
|
||||
}
|
||||
var got query
|
||||
err := jsonutil.UnmarshalGraphQL([]byte(`{
|
||||
"viewer": {
|
||||
"login": "shurcooL-test",
|
||||
"createdAt": "2017-06-29T04:12:01Z"
|
||||
}
|
||||
}`), &got)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
var want query
|
||||
want.Viewer.Login = "shurcooL-test"
|
||||
want.Viewer.CreatedAt = time.Unix(1498709521, 0).UTC()
|
||||
if !reflect.DeepEqual(got, want) {
|
||||
t.Error("not equal")
|
||||
}
|
||||
}
|
||||
|
||||
func BenchmarkUnmarshalGraphQL(b *testing.B) {
|
||||
type query struct {
|
||||
Viewer struct {
|
||||
Login graphql.String
|
||||
CreatedAt time.Time
|
||||
}
|
||||
}
|
||||
for i := 0; i < b.N; i++ {
|
||||
now := time.Now().UTC()
|
||||
var got query
|
||||
err := jsonutil.UnmarshalGraphQL([]byte(`{
|
||||
"viewer": {
|
||||
"login": "shurcooL-test",
|
||||
"createdAt": "`+now.Format(time.RFC3339Nano)+`"
|
||||
}
|
||||
}`), &got)
|
||||
if err != nil {
|
||||
b.Fatal(err)
|
||||
}
|
||||
var want query
|
||||
want.Viewer.Login = "shurcooL-test"
|
||||
want.Viewer.CreatedAt = now
|
||||
if !reflect.DeepEqual(got, want) {
|
||||
b.Error("not equal")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func BenchmarkJSONUnmarshal(b *testing.B) {
|
||||
type query struct {
|
||||
Viewer struct {
|
||||
Login graphql.String
|
||||
CreatedAt time.Time
|
||||
}
|
||||
}
|
||||
for i := 0; i < b.N; i++ {
|
||||
now := time.Now().UTC()
|
||||
var got query
|
||||
err := json.Unmarshal([]byte(`{
|
||||
"viewer": {
|
||||
"login": "shurcooL-test",
|
||||
"createdAt": "`+now.Format(time.RFC3339Nano)+`"
|
||||
}
|
||||
}`), &got)
|
||||
if err != nil {
|
||||
b.Fatal(err)
|
||||
}
|
||||
var want query
|
||||
want.Viewer.Login = "shurcooL-test"
|
||||
want.Viewer.CreatedAt = now
|
||||
if !reflect.DeepEqual(got, want) {
|
||||
b.Error("not equal")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func BenchmarkJSONTokenize(b *testing.B) {
|
||||
for i := 0; i < b.N; i++ {
|
||||
now := time.Now().UTC()
|
||||
dec := json.NewDecoder(strings.NewReader(`{
|
||||
"viewer": {
|
||||
"login": "shurcooL-test",
|
||||
"createdAt": "` + now.Format(time.RFC3339Nano) + `"
|
||||
}
|
||||
}`))
|
||||
var tokens int
|
||||
for {
|
||||
_, err := dec.Token()
|
||||
if err == io.EOF {
|
||||
break
|
||||
} else if err != nil {
|
||||
b.Error(err)
|
||||
}
|
||||
tokens++
|
||||
}
|
||||
if tokens != 9 {
|
||||
b.Error("not 9 tokens")
|
||||
}
|
||||
}
|
||||
}
|
||||
308
vendor/github.com/shurcooL/graphql/internal/jsonutil/graphql.go
generated
vendored
Normal file
308
vendor/github.com/shurcooL/graphql/internal/jsonutil/graphql.go
generated
vendored
Normal file
@@ -0,0 +1,308 @@
|
||||
// Package jsonutil provides a function for decoding JSON
|
||||
// into a GraphQL query data structure.
|
||||
package jsonutil
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
"reflect"
|
||||
"strings"
|
||||
)
|
||||
|
||||
// UnmarshalGraphQL parses the JSON-encoded GraphQL response data and stores
|
||||
// the result in the GraphQL query data structure pointed to by v.
|
||||
//
|
||||
// The implementation is created on top of the JSON tokenizer available
|
||||
// in "encoding/json".Decoder.
|
||||
func UnmarshalGraphQL(data []byte, v interface{}) error {
|
||||
dec := json.NewDecoder(bytes.NewReader(data))
|
||||
dec.UseNumber()
|
||||
err := (&decoder{tokenizer: dec}).Decode(v)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
tok, err := dec.Token()
|
||||
switch err {
|
||||
case io.EOF:
|
||||
// Expect to get io.EOF. There shouldn't be any more
|
||||
// tokens left after we've decoded v successfully.
|
||||
return nil
|
||||
case nil:
|
||||
return fmt.Errorf("invalid token '%v' after top-level value", tok)
|
||||
default:
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
// decoder is a JSON decoder that performs custom unmarshaling behavior
|
||||
// for GraphQL query data structures. It's implemented on top of a JSON tokenizer.
|
||||
type decoder struct {
|
||||
tokenizer interface {
|
||||
Token() (json.Token, error)
|
||||
}
|
||||
|
||||
// Stack of what part of input JSON we're in the middle of - objects, arrays.
|
||||
parseState []json.Delim
|
||||
|
||||
// Stacks of values where to unmarshal.
|
||||
// The top of each stack is the reflect.Value where to unmarshal next JSON value.
|
||||
//
|
||||
// The reason there's more than one stack is because we might be unmarshaling
|
||||
// a single JSON value into multiple GraphQL fragments or embedded structs, so
|
||||
// we keep track of them all.
|
||||
vs [][]reflect.Value
|
||||
}
|
||||
|
||||
// Decode decodes a single JSON value from d.tokenizer into v.
|
||||
func (d *decoder) Decode(v interface{}) error {
|
||||
rv := reflect.ValueOf(v)
|
||||
if rv.Kind() != reflect.Ptr {
|
||||
return fmt.Errorf("cannot decode into non-pointer %T", v)
|
||||
}
|
||||
d.vs = [][]reflect.Value{{rv.Elem()}}
|
||||
return d.decode()
|
||||
}
|
||||
|
||||
// decode decodes a single JSON value from d.tokenizer into d.vs.
|
||||
func (d *decoder) decode() error {
|
||||
// The loop invariant is that the top of each d.vs stack
|
||||
// is where we try to unmarshal the next JSON value we see.
|
||||
for len(d.vs) > 0 {
|
||||
tok, err := d.tokenizer.Token()
|
||||
if err == io.EOF {
|
||||
return errors.New("unexpected end of JSON input")
|
||||
} else if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
switch {
|
||||
|
||||
// Are we inside an object and seeing next key (rather than end of object)?
|
||||
case d.state() == '{' && tok != json.Delim('}'):
|
||||
key, ok := tok.(string)
|
||||
if !ok {
|
||||
return errors.New("unexpected non-key in JSON input")
|
||||
}
|
||||
someFieldExist := false
|
||||
for i := range d.vs {
|
||||
v := d.vs[i][len(d.vs[i])-1]
|
||||
if v.Kind() == reflect.Ptr {
|
||||
v = v.Elem()
|
||||
}
|
||||
var f reflect.Value
|
||||
if v.Kind() == reflect.Struct {
|
||||
f = fieldByGraphQLName(v, key)
|
||||
if f.IsValid() {
|
||||
someFieldExist = true
|
||||
}
|
||||
}
|
||||
d.vs[i] = append(d.vs[i], f)
|
||||
}
|
||||
if !someFieldExist {
|
||||
return fmt.Errorf("struct field for %s doesn't exist in any of %v places to unmarshal", key, len(d.vs))
|
||||
}
|
||||
|
||||
// We've just consumed the current token, which was the key.
|
||||
// Read the next token, which should be the value, and let the rest of code process it.
|
||||
tok, err = d.tokenizer.Token()
|
||||
if err == io.EOF {
|
||||
return errors.New("unexpected end of JSON input")
|
||||
} else if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Are we inside an array and seeing next value (rather than end of array)?
|
||||
case d.state() == '[' && tok != json.Delim(']'):
|
||||
someSliceExist := false
|
||||
for i := range d.vs {
|
||||
v := d.vs[i][len(d.vs[i])-1]
|
||||
if v.Kind() == reflect.Ptr {
|
||||
v = v.Elem()
|
||||
}
|
||||
var f reflect.Value
|
||||
if v.Kind() == reflect.Slice {
|
||||
v.Set(reflect.Append(v, reflect.Zero(v.Type().Elem()))) // v = append(v, T).
|
||||
f = v.Index(v.Len() - 1)
|
||||
someSliceExist = true
|
||||
}
|
||||
d.vs[i] = append(d.vs[i], f)
|
||||
}
|
||||
if !someSliceExist {
|
||||
return fmt.Errorf("slice doesn't exist in any of %v places to unmarshal", len(d.vs))
|
||||
}
|
||||
}
|
||||
|
||||
switch tok := tok.(type) {
|
||||
case string, json.Number, bool, nil:
|
||||
// Value.
|
||||
|
||||
for i := range d.vs {
|
||||
v := d.vs[i][len(d.vs[i])-1]
|
||||
if !v.IsValid() {
|
||||
continue
|
||||
}
|
||||
err := unmarshalValue(tok, v)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
d.popAllVs()
|
||||
|
||||
case json.Delim:
|
||||
switch tok {
|
||||
case '{':
|
||||
// Start of object.
|
||||
|
||||
d.pushState(tok)
|
||||
|
||||
frontier := make([]reflect.Value, len(d.vs)) // Places to look for GraphQL fragments/embedded structs.
|
||||
for i := range d.vs {
|
||||
v := d.vs[i][len(d.vs[i])-1]
|
||||
frontier[i] = v
|
||||
// TODO: Do this recursively or not? Add a test case if needed.
|
||||
if v.Kind() == reflect.Ptr && v.IsNil() {
|
||||
v.Set(reflect.New(v.Type().Elem())) // v = new(T).
|
||||
}
|
||||
}
|
||||
// Find GraphQL fragments/embedded structs recursively, adding to frontier
|
||||
// as new ones are discovered and exploring them further.
|
||||
for len(frontier) > 0 {
|
||||
v := frontier[0]
|
||||
frontier = frontier[1:]
|
||||
if v.Kind() == reflect.Ptr {
|
||||
v = v.Elem()
|
||||
}
|
||||
if v.Kind() != reflect.Struct {
|
||||
continue
|
||||
}
|
||||
for i := 0; i < v.NumField(); i++ {
|
||||
if isGraphQLFragment(v.Type().Field(i)) || v.Type().Field(i).Anonymous {
|
||||
// Add GraphQL fragment or embedded struct.
|
||||
d.vs = append(d.vs, []reflect.Value{v.Field(i)})
|
||||
frontier = append(frontier, v.Field(i))
|
||||
}
|
||||
}
|
||||
}
|
||||
case '[':
|
||||
// Start of array.
|
||||
|
||||
d.pushState(tok)
|
||||
|
||||
for i := range d.vs {
|
||||
v := d.vs[i][len(d.vs[i])-1]
|
||||
// TODO: Confirm this is needed, write a test case.
|
||||
//if v.Kind() == reflect.Ptr && v.IsNil() {
|
||||
// v.Set(reflect.New(v.Type().Elem())) // v = new(T).
|
||||
//}
|
||||
|
||||
// Reset slice to empty (in case it had non-zero initial value).
|
||||
if v.Kind() == reflect.Ptr {
|
||||
v = v.Elem()
|
||||
}
|
||||
if v.Kind() != reflect.Slice {
|
||||
continue
|
||||
}
|
||||
v.Set(reflect.MakeSlice(v.Type(), 0, 0)) // v = make(T, 0, 0).
|
||||
}
|
||||
case '}', ']':
|
||||
// End of object or array.
|
||||
d.popAllVs()
|
||||
d.popState()
|
||||
default:
|
||||
return errors.New("unexpected delimiter in JSON input")
|
||||
}
|
||||
default:
|
||||
return errors.New("unexpected token in JSON input")
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// pushState pushes a new parse state s onto the stack.
|
||||
func (d *decoder) pushState(s json.Delim) {
|
||||
d.parseState = append(d.parseState, s)
|
||||
}
|
||||
|
||||
// popState pops a parse state (already obtained) off the stack.
|
||||
// The stack must be non-empty.
|
||||
func (d *decoder) popState() {
|
||||
d.parseState = d.parseState[:len(d.parseState)-1]
|
||||
}
|
||||
|
||||
// state reports the parse state on top of stack, or 0 if empty.
|
||||
func (d *decoder) state() json.Delim {
|
||||
if len(d.parseState) == 0 {
|
||||
return 0
|
||||
}
|
||||
return d.parseState[len(d.parseState)-1]
|
||||
}
|
||||
|
||||
// popAllVs pops from all d.vs stacks, keeping only non-empty ones.
|
||||
func (d *decoder) popAllVs() {
|
||||
var nonEmpty [][]reflect.Value
|
||||
for i := range d.vs {
|
||||
d.vs[i] = d.vs[i][:len(d.vs[i])-1]
|
||||
if len(d.vs[i]) > 0 {
|
||||
nonEmpty = append(nonEmpty, d.vs[i])
|
||||
}
|
||||
}
|
||||
d.vs = nonEmpty
|
||||
}
|
||||
|
||||
// fieldByGraphQLName returns a struct field of struct v that matches GraphQL name,
|
||||
// or invalid reflect.Value if none found.
|
||||
func fieldByGraphQLName(v reflect.Value, name string) reflect.Value {
|
||||
for i := 0; i < v.NumField(); i++ {
|
||||
if hasGraphQLName(v.Type().Field(i), name) {
|
||||
return v.Field(i)
|
||||
}
|
||||
}
|
||||
return reflect.Value{}
|
||||
}
|
||||
|
||||
// hasGraphQLName reports whether struct field f has GraphQL name.
|
||||
func hasGraphQLName(f reflect.StructField, name string) bool {
|
||||
value, ok := f.Tag.Lookup("graphql")
|
||||
if !ok {
|
||||
// TODO: caseconv package is relatively slow. Optimize it, then consider using it here.
|
||||
//return caseconv.MixedCapsToLowerCamelCase(f.Name) == name
|
||||
return strings.EqualFold(f.Name, name)
|
||||
}
|
||||
value = strings.TrimSpace(value) // TODO: Parse better.
|
||||
if strings.HasPrefix(value, "...") {
|
||||
// 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 {
|
||||
value = value[:i]
|
||||
}
|
||||
return strings.TrimSpace(value) == name
|
||||
}
|
||||
|
||||
// isGraphQLFragment reports whether struct field f is a GraphQL fragment.
|
||||
func isGraphQLFragment(f reflect.StructField) bool {
|
||||
value, ok := f.Tag.Lookup("graphql")
|
||||
if !ok {
|
||||
return false
|
||||
}
|
||||
value = strings.TrimSpace(value) // TODO: Parse better.
|
||||
return strings.HasPrefix(value, "...")
|
||||
}
|
||||
|
||||
// unmarshalValue unmarshals JSON value into v.
|
||||
func unmarshalValue(value json.Token, v reflect.Value) error {
|
||||
b, err := json.Marshal(value) // TODO: Short-circuit (if profiling says it's worth it).
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if !v.CanAddr() {
|
||||
return fmt.Errorf("value %v is not addressable", v)
|
||||
}
|
||||
return json.Unmarshal(b, v.Addr().Interface())
|
||||
}
|
||||
382
vendor/github.com/shurcooL/graphql/internal/jsonutil/graphql_test.go
generated
vendored
Normal file
382
vendor/github.com/shurcooL/graphql/internal/jsonutil/graphql_test.go
generated
vendored
Normal file
@@ -0,0 +1,382 @@
|
||||
package jsonutil_test
|
||||
|
||||
import (
|
||||
"reflect"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/shurcooL/graphql"
|
||||
"github.com/shurcooL/graphql/internal/jsonutil"
|
||||
)
|
||||
|
||||
func TestUnmarshalGraphQL(t *testing.T) {
|
||||
/*
|
||||
query {
|
||||
me {
|
||||
name
|
||||
height
|
||||
}
|
||||
}
|
||||
*/
|
||||
type query struct {
|
||||
Me struct {
|
||||
Name graphql.String
|
||||
Height graphql.Float
|
||||
}
|
||||
}
|
||||
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_graphqlTag(t *testing.T) {
|
||||
type query struct {
|
||||
Foo graphql.String `graphql:"baz"`
|
||||
}
|
||||
var got query
|
||||
err := jsonutil.UnmarshalGraphQL([]byte(`{
|
||||
"baz": "bar"
|
||||
}`), &got)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
want := query{
|
||||
Foo: "bar",
|
||||
}
|
||||
if !reflect.DeepEqual(got, want) {
|
||||
t.Error("not equal")
|
||||
}
|
||||
}
|
||||
|
||||
func TestUnmarshalGraphQL_jsonTag(t *testing.T) {
|
||||
type query struct {
|
||||
Foo graphql.String `json:"baz"`
|
||||
}
|
||||
var got query
|
||||
err := jsonutil.UnmarshalGraphQL([]byte(`{
|
||||
"foo": "bar"
|
||||
}`), &got)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
want := query{
|
||||
Foo: "bar",
|
||||
}
|
||||
if !reflect.DeepEqual(got, want) {
|
||||
t.Error("not equal")
|
||||
}
|
||||
}
|
||||
|
||||
func TestUnmarshalGraphQL_array(t *testing.T) {
|
||||
type query struct {
|
||||
Foo []graphql.String
|
||||
Bar []graphql.String
|
||||
Baz []graphql.String
|
||||
}
|
||||
var got query
|
||||
err := jsonutil.UnmarshalGraphQL([]byte(`{
|
||||
"foo": [
|
||||
"bar",
|
||||
"baz"
|
||||
],
|
||||
"bar": [],
|
||||
"baz": null
|
||||
}`), &got)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
want := query{
|
||||
Foo: []graphql.String{"bar", "baz"},
|
||||
Bar: []graphql.String{},
|
||||
Baz: []graphql.String(nil),
|
||||
}
|
||||
if !reflect.DeepEqual(got, want) {
|
||||
t.Error("not equal")
|
||||
}
|
||||
}
|
||||
|
||||
// When unmarshaling into an array, its initial value should be overwritten
|
||||
// (rather than appended to).
|
||||
func TestUnmarshalGraphQL_arrayReset(t *testing.T) {
|
||||
var got = []string{"initial"}
|
||||
err := jsonutil.UnmarshalGraphQL([]byte(`["bar", "baz"]`), &got)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
want := []string{"bar", "baz"}
|
||||
if !reflect.DeepEqual(got, want) {
|
||||
t.Error("not equal")
|
||||
}
|
||||
}
|
||||
|
||||
func TestUnmarshalGraphQL_objectArray(t *testing.T) {
|
||||
type query struct {
|
||||
Foo []struct {
|
||||
Name graphql.String
|
||||
}
|
||||
}
|
||||
var got query
|
||||
err := jsonutil.UnmarshalGraphQL([]byte(`{
|
||||
"foo": [
|
||||
{"name": "bar"},
|
||||
{"name": "baz"}
|
||||
]
|
||||
}`), &got)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
want := query{
|
||||
Foo: []struct{ Name graphql.String }{
|
||||
{"bar"},
|
||||
{"baz"},
|
||||
},
|
||||
}
|
||||
if !reflect.DeepEqual(got, want) {
|
||||
t.Error("not equal")
|
||||
}
|
||||
}
|
||||
|
||||
func TestUnmarshalGraphQL_pointer(t *testing.T) {
|
||||
type query struct {
|
||||
Foo *graphql.String
|
||||
Bar *graphql.String
|
||||
}
|
||||
var got query
|
||||
got.Bar = new(graphql.String) // Test that got.Bar gets set to nil.
|
||||
err := jsonutil.UnmarshalGraphQL([]byte(`{
|
||||
"foo": "foo",
|
||||
"bar": null
|
||||
}`), &got)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
want := query{
|
||||
Foo: graphql.NewString("foo"),
|
||||
Bar: nil,
|
||||
}
|
||||
if !reflect.DeepEqual(got, want) {
|
||||
t.Error("not equal")
|
||||
}
|
||||
}
|
||||
|
||||
func TestUnmarshalGraphQL_objectPointerArray(t *testing.T) {
|
||||
type query struct {
|
||||
Foo []*struct {
|
||||
Name graphql.String
|
||||
}
|
||||
}
|
||||
var got query
|
||||
err := jsonutil.UnmarshalGraphQL([]byte(`{
|
||||
"foo": [
|
||||
{"name": "bar"},
|
||||
null,
|
||||
{"name": "baz"}
|
||||
]
|
||||
}`), &got)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
want := query{
|
||||
Foo: []*struct{ Name graphql.String }{
|
||||
{"bar"},
|
||||
nil,
|
||||
{"baz"},
|
||||
},
|
||||
}
|
||||
if !reflect.DeepEqual(got, want) {
|
||||
t.Error("not equal")
|
||||
}
|
||||
}
|
||||
|
||||
func TestUnmarshalGraphQL_pointerWithInlineFragment(t *testing.T) {
|
||||
type actor struct {
|
||||
User struct {
|
||||
DatabaseID uint64
|
||||
} `graphql:"... on User"`
|
||||
Login string
|
||||
}
|
||||
type query struct {
|
||||
Author actor
|
||||
Editor *actor
|
||||
}
|
||||
var got query
|
||||
err := jsonutil.UnmarshalGraphQL([]byte(`{
|
||||
"author": {
|
||||
"databaseId": 1,
|
||||
"login": "test1"
|
||||
},
|
||||
"editor": {
|
||||
"databaseId": 2,
|
||||
"login": "test2"
|
||||
}
|
||||
}`), &got)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
var want query
|
||||
want.Author = actor{
|
||||
User: struct{ DatabaseID uint64 }{1},
|
||||
Login: "test1",
|
||||
}
|
||||
want.Editor = &actor{
|
||||
User: struct{ DatabaseID uint64 }{2},
|
||||
Login: "test2",
|
||||
}
|
||||
|
||||
if !reflect.DeepEqual(got, want) {
|
||||
t.Error("not equal")
|
||||
}
|
||||
}
|
||||
|
||||
func TestUnmarshalGraphQL_multipleValues(t *testing.T) {
|
||||
type query struct {
|
||||
Foo graphql.String
|
||||
}
|
||||
err := jsonutil.UnmarshalGraphQL([]byte(`{"foo": "bar"}{"foo": "baz"}`), new(query))
|
||||
if err == nil {
|
||||
t.Fatal("got error: nil, want: non-nil")
|
||||
}
|
||||
if got, want := err.Error(), "invalid token '{' after top-level value"; got != want {
|
||||
t.Errorf("got error: %v, want: %v", got, want)
|
||||
}
|
||||
}
|
||||
|
||||
func TestUnmarshalGraphQL_union(t *testing.T) {
|
||||
/*
|
||||
{
|
||||
__typename
|
||||
... on ClosedEvent {
|
||||
createdAt
|
||||
actor {login}
|
||||
}
|
||||
... on ReopenedEvent {
|
||||
createdAt
|
||||
actor {login}
|
||||
}
|
||||
}
|
||||
*/
|
||||
type actor struct{ Login graphql.String }
|
||||
type closedEvent struct {
|
||||
Actor actor
|
||||
CreatedAt time.Time
|
||||
}
|
||||
type reopenedEvent struct {
|
||||
Actor actor
|
||||
CreatedAt time.Time
|
||||
}
|
||||
type issueTimelineItem struct {
|
||||
Typename string `graphql:"__typename"`
|
||||
ClosedEvent closedEvent `graphql:"... on ClosedEvent"`
|
||||
ReopenedEvent reopenedEvent `graphql:"... on ReopenedEvent"`
|
||||
}
|
||||
var got issueTimelineItem
|
||||
err := jsonutil.UnmarshalGraphQL([]byte(`{
|
||||
"__typename": "ClosedEvent",
|
||||
"createdAt": "2017-06-29T04:12:01Z",
|
||||
"actor": {
|
||||
"login": "shurcooL-test"
|
||||
}
|
||||
}`), &got)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
want := issueTimelineItem{
|
||||
Typename: "ClosedEvent",
|
||||
ClosedEvent: closedEvent{
|
||||
Actor: actor{
|
||||
Login: "shurcooL-test",
|
||||
},
|
||||
CreatedAt: time.Unix(1498709521, 0).UTC(),
|
||||
},
|
||||
ReopenedEvent: reopenedEvent{
|
||||
Actor: actor{
|
||||
Login: "shurcooL-test",
|
||||
},
|
||||
CreatedAt: time.Unix(1498709521, 0).UTC(),
|
||||
},
|
||||
}
|
||||
if !reflect.DeepEqual(got, want) {
|
||||
t.Error("not equal")
|
||||
}
|
||||
}
|
||||
|
||||
// Issue https://github.com/shurcooL/githubql/issues/18.
|
||||
func TestUnmarshalGraphQL_arrayInsideInlineFragment(t *testing.T) {
|
||||
/*
|
||||
query {
|
||||
search(type: ISSUE, first: 1, query: "type:pr repo:owner/name") {
|
||||
nodes {
|
||||
... on PullRequest {
|
||||
commits(last: 1) {
|
||||
nodes {
|
||||
url
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
*/
|
||||
type query struct {
|
||||
Search struct {
|
||||
Nodes []struct {
|
||||
PullRequest struct {
|
||||
Commits struct {
|
||||
Nodes []struct {
|
||||
URL string `graphql:"url"`
|
||||
}
|
||||
} `graphql:"commits(last: 1)"`
|
||||
} `graphql:"... on PullRequest"`
|
||||
}
|
||||
} `graphql:"search(type: ISSUE, first: 1, query: \"type:pr repo:owner/name\")"`
|
||||
}
|
||||
var got query
|
||||
err := jsonutil.UnmarshalGraphQL([]byte(`{
|
||||
"search": {
|
||||
"nodes": [
|
||||
{
|
||||
"commits": {
|
||||
"nodes": [
|
||||
{
|
||||
"url": "https://example.org/commit/49e1"
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
}`), &got)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
var want query
|
||||
want.Search.Nodes = make([]struct {
|
||||
PullRequest struct {
|
||||
Commits struct {
|
||||
Nodes []struct {
|
||||
URL string `graphql:"url"`
|
||||
}
|
||||
} `graphql:"commits(last: 1)"`
|
||||
} `graphql:"... on PullRequest"`
|
||||
}, 1)
|
||||
want.Search.Nodes[0].PullRequest.Commits.Nodes = make([]struct {
|
||||
URL string `graphql:"url"`
|
||||
}, 1)
|
||||
want.Search.Nodes[0].PullRequest.Commits.Nodes[0].URL = "https://example.org/commit/49e1"
|
||||
if !reflect.DeepEqual(got, want) {
|
||||
t.Error("not equal")
|
||||
}
|
||||
}
|
||||
112
vendor/github.com/shurcooL/graphql/query.go
generated
vendored
Normal file
112
vendor/github.com/shurcooL/graphql/query.go
generated
vendored
Normal file
@@ -0,0 +1,112 @@
|
||||
package graphql
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"io"
|
||||
"reflect"
|
||||
"sort"
|
||||
|
||||
"github.com/shurcooL/graphql/ident"
|
||||
)
|
||||
|
||||
func constructQuery(qctx *queryContext, v interface{}, variables map[string]interface{}) string {
|
||||
query := qctx.Query(v)
|
||||
if variables != nil {
|
||||
return "query(" + queryArguments(variables) + ")" + query
|
||||
}
|
||||
return query
|
||||
}
|
||||
|
||||
func constructMutation(qctx *queryContext, v interface{}, variables map[string]interface{}) string {
|
||||
query := qctx.Query(v)
|
||||
if variables != nil {
|
||||
return "mutation(" + queryArguments(variables) + ")" + query
|
||||
}
|
||||
return "mutation" + query
|
||||
}
|
||||
|
||||
// queryArguments constructs a minified arguments string for variables.
|
||||
//
|
||||
// E.g., map[string]interface{}{"a": Int(123), "b": NewBoolean(true)} -> "$a:Int!$b:Boolean".
|
||||
func queryArguments(variables map[string]interface{}) string {
|
||||
sorted := make([]string, 0, len(variables))
|
||||
for k := range variables {
|
||||
sorted = append(sorted, k)
|
||||
}
|
||||
sort.Strings(sorted)
|
||||
var s string
|
||||
for _, k := range sorted {
|
||||
v := variables[k]
|
||||
s += "$" + k + ":"
|
||||
t := reflect.TypeOf(v)
|
||||
switch t.Kind() {
|
||||
case reflect.Slice, reflect.Array:
|
||||
// TODO: Support t.Elem() being a pointer, if needed. Probably want to do this recursively.
|
||||
s += "[" + t.Elem().Name() + "!]" // E.g., "[IssueState!]".
|
||||
case reflect.Ptr:
|
||||
// Pointer is an optional type, so no "!" at the end.
|
||||
s += t.Elem().Name() // E.g., "Int".
|
||||
default:
|
||||
name := t.Name()
|
||||
if name == "string" { // HACK: Workaround for https://github.com/shurcooL/githubql/issues/12.
|
||||
name = "ID"
|
||||
}
|
||||
// Value is a required type, so add "!" to the end.
|
||||
s += name + "!" // E.g., "Int!".
|
||||
}
|
||||
}
|
||||
return s
|
||||
}
|
||||
|
||||
type queryContext struct {
|
||||
// Scalars are Go types that map to GraphQL scalars, and therefore we don't want to expand them.
|
||||
Scalars []reflect.Type
|
||||
}
|
||||
|
||||
// Query uses writeQuery to recursively construct
|
||||
// a minified query string from the provided struct v.
|
||||
//
|
||||
// E.g., struct{Foo Int, BarBaz *Boolean} -> "{foo,barBaz}".
|
||||
func (c *queryContext) Query(v interface{}) string {
|
||||
var buf bytes.Buffer
|
||||
c.writeQuery(&buf, reflect.TypeOf(v), false)
|
||||
return buf.String()
|
||||
}
|
||||
|
||||
// writeQuery writes a minified query for t to w. If inline is true,
|
||||
// the struct fields of t are inlined into parent struct.
|
||||
func (c *queryContext) writeQuery(w io.Writer, t reflect.Type, inline bool) {
|
||||
switch t.Kind() {
|
||||
case reflect.Ptr, reflect.Slice:
|
||||
c.writeQuery(w, t.Elem(), false)
|
||||
case reflect.Struct:
|
||||
// Special handling of scalar struct types. Don't expand them.
|
||||
for _, scalar := range c.Scalars {
|
||||
if t == scalar {
|
||||
return
|
||||
}
|
||||
}
|
||||
if !inline {
|
||||
io.WriteString(w, "{")
|
||||
}
|
||||
for i := 0; i < t.NumField(); i++ {
|
||||
if i != 0 {
|
||||
io.WriteString(w, ",")
|
||||
}
|
||||
f := t.Field(i)
|
||||
value, ok := f.Tag.Lookup("graphql")
|
||||
inlineField := f.Anonymous && !ok
|
||||
if !inlineField {
|
||||
if ok {
|
||||
io.WriteString(w, value)
|
||||
} else {
|
||||
io.WriteString(w, ident.ParseMixedCaps(f.Name).ToLowerCamelCase())
|
||||
}
|
||||
}
|
||||
c.writeQuery(w, f.Type, inlineField)
|
||||
}
|
||||
if !inline {
|
||||
io.WriteString(w, "}")
|
||||
}
|
||||
}
|
||||
}
|
||||
345
vendor/github.com/shurcooL/graphql/query_test.go
generated
vendored
Normal file
345
vendor/github.com/shurcooL/graphql/query_test.go
generated
vendored
Normal file
@@ -0,0 +1,345 @@
|
||||
package graphql
|
||||
|
||||
import (
|
||||
"net/url"
|
||||
"reflect"
|
||||
"testing"
|
||||
"time"
|
||||
)
|
||||
|
||||
func TestConstructQuery(t *testing.T) {
|
||||
tests := []struct {
|
||||
inV interface{}
|
||||
inVariables map[string]interface{}
|
||||
want string
|
||||
}{
|
||||
{
|
||||
inV: struct {
|
||||
Viewer struct {
|
||||
Login String
|
||||
CreatedAt DateTime
|
||||
ID ID
|
||||
DatabaseID Int
|
||||
}
|
||||
RateLimit struct {
|
||||
Cost Int
|
||||
Limit Int
|
||||
Remaining Int
|
||||
ResetAt DateTime
|
||||
}
|
||||
}{},
|
||||
want: `{viewer{login,createdAt,id,databaseId},rateLimit{cost,limit,remaining,resetAt}}`,
|
||||
},
|
||||
{
|
||||
inV: struct {
|
||||
Repository struct {
|
||||
DatabaseID Int
|
||||
URL URI
|
||||
|
||||
Issue struct {
|
||||
Comments struct {
|
||||
Edges []struct {
|
||||
Node struct {
|
||||
Body String
|
||||
Author struct {
|
||||
Login String
|
||||
}
|
||||
Editor struct {
|
||||
Login String
|
||||
}
|
||||
}
|
||||
Cursor String
|
||||
}
|
||||
} `graphql:"comments(first:1after:\"Y3Vyc29yOjE5NTE4NDI1Ng==\")"`
|
||||
} `graphql:"issue(number:1)"`
|
||||
} `graphql:"repository(owner:\"shurcooL-test\"name:\"test-repo\")"`
|
||||
}{},
|
||||
want: `{repository(owner:"shurcooL-test"name:"test-repo"){databaseId,url,issue(number:1){comments(first:1after:"Y3Vyc29yOjE5NTE4NDI1Ng=="){edges{node{body,author{login},editor{login}},cursor}}}}}`,
|
||||
},
|
||||
{
|
||||
inV: func() interface{} {
|
||||
type actor struct {
|
||||
Login String
|
||||
AvatarURL URI
|
||||
URL URI
|
||||
}
|
||||
|
||||
return struct {
|
||||
Repository struct {
|
||||
DatabaseID Int
|
||||
URL URI
|
||||
|
||||
Issue struct {
|
||||
Comments struct {
|
||||
Edges []struct {
|
||||
Node struct {
|
||||
DatabaseID Int
|
||||
Author actor
|
||||
PublishedAt DateTime
|
||||
LastEditedAt *DateTime
|
||||
Editor *actor
|
||||
Body String
|
||||
ViewerCanUpdate Boolean
|
||||
}
|
||||
Cursor String
|
||||
}
|
||||
} `graphql:"comments(first:1)"`
|
||||
} `graphql:"issue(number:1)"`
|
||||
} `graphql:"repository(owner:\"shurcooL-test\"name:\"test-repo\")"`
|
||||
}{}
|
||||
}(),
|
||||
want: `{repository(owner:"shurcooL-test"name:"test-repo"){databaseId,url,issue(number:1){comments(first:1){edges{node{databaseId,author{login,avatarUrl,url},publishedAt,lastEditedAt,editor{login,avatarUrl,url},body,viewerCanUpdate},cursor}}}}}`,
|
||||
},
|
||||
{
|
||||
inV: func() interface{} {
|
||||
type actor struct {
|
||||
Login String
|
||||
AvatarURL URI `graphql:"avatarUrl(size:72)"`
|
||||
URL URI
|
||||
}
|
||||
|
||||
return struct {
|
||||
Repository struct {
|
||||
Issue struct {
|
||||
Author actor
|
||||
PublishedAt DateTime
|
||||
LastEditedAt *DateTime
|
||||
Editor *actor
|
||||
Body String
|
||||
ReactionGroups []struct {
|
||||
Content ReactionContent
|
||||
Users struct {
|
||||
TotalCount Int
|
||||
}
|
||||
ViewerHasReacted Boolean
|
||||
}
|
||||
ViewerCanUpdate Boolean
|
||||
|
||||
Comments struct {
|
||||
Nodes []struct {
|
||||
DatabaseID Int
|
||||
Author actor
|
||||
PublishedAt DateTime
|
||||
LastEditedAt *DateTime
|
||||
Editor *actor
|
||||
Body String
|
||||
ReactionGroups []struct {
|
||||
Content ReactionContent
|
||||
Users struct {
|
||||
TotalCount Int
|
||||
}
|
||||
ViewerHasReacted Boolean
|
||||
}
|
||||
ViewerCanUpdate Boolean
|
||||
}
|
||||
PageInfo struct {
|
||||
EndCursor String
|
||||
HasNextPage Boolean
|
||||
}
|
||||
} `graphql:"comments(first:1)"`
|
||||
} `graphql:"issue(number:1)"`
|
||||
} `graphql:"repository(owner:\"shurcooL-test\"name:\"test-repo\")"`
|
||||
}{}
|
||||
}(),
|
||||
want: `{repository(owner:"shurcooL-test"name:"test-repo"){issue(number:1){author{login,avatarUrl(size:72),url},publishedAt,lastEditedAt,editor{login,avatarUrl(size:72),url},body,reactionGroups{content,users{totalCount},viewerHasReacted},viewerCanUpdate,comments(first:1){nodes{databaseId,author{login,avatarUrl(size:72),url},publishedAt,lastEditedAt,editor{login,avatarUrl(size:72),url},body,reactionGroups{content,users{totalCount},viewerHasReacted},viewerCanUpdate},pageInfo{endCursor,hasNextPage}}}}}`,
|
||||
},
|
||||
{
|
||||
inV: struct {
|
||||
Repository struct {
|
||||
Issue struct {
|
||||
Body String
|
||||
} `graphql:"issue(number: 1)"`
|
||||
} `graphql:"repository(owner:\"shurcooL-test\"name:\"test-repo\")"`
|
||||
}{},
|
||||
want: `{repository(owner:"shurcooL-test"name:"test-repo"){issue(number: 1){body}}}`,
|
||||
},
|
||||
{
|
||||
inV: struct {
|
||||
Repository struct {
|
||||
Issue struct {
|
||||
Body String
|
||||
} `graphql:"issue(number: $issueNumber)"`
|
||||
} `graphql:"repository(owner: $repositoryOwner, name: $repositoryName)"`
|
||||
}{},
|
||||
inVariables: map[string]interface{}{
|
||||
"repositoryOwner": String("shurcooL-test"),
|
||||
"repositoryName": String("test-repo"),
|
||||
"issueNumber": Int(1),
|
||||
},
|
||||
want: `query($issueNumber:Int!$repositoryName:String!$repositoryOwner:String!){repository(owner: $repositoryOwner, name: $repositoryName){issue(number: $issueNumber){body}}}`,
|
||||
},
|
||||
{
|
||||
inV: struct {
|
||||
Repository struct {
|
||||
Issue struct {
|
||||
ReactionGroups []struct {
|
||||
Users struct {
|
||||
Nodes []struct {
|
||||
Login String
|
||||
}
|
||||
} `graphql:"users(first:10)"`
|
||||
}
|
||||
} `graphql:"issue(number: $issueNumber)"`
|
||||
} `graphql:"repository(owner: $repositoryOwner, name: $repositoryName)"`
|
||||
}{},
|
||||
inVariables: map[string]interface{}{
|
||||
"repositoryOwner": String("shurcooL-test"),
|
||||
"repositoryName": String("test-repo"),
|
||||
"issueNumber": Int(1),
|
||||
},
|
||||
want: `query($issueNumber:Int!$repositoryName:String!$repositoryOwner:String!){repository(owner: $repositoryOwner, name: $repositoryName){issue(number: $issueNumber){reactionGroups{users(first:10){nodes{login}}}}}}`,
|
||||
},
|
||||
// Embedded structs without graphql tag should be inlined in query.
|
||||
{
|
||||
inV: func() interface{} {
|
||||
type actor struct {
|
||||
Login String
|
||||
AvatarURL URI
|
||||
URL URI
|
||||
}
|
||||
type event struct { // Common fields for all events.
|
||||
Actor actor
|
||||
CreatedAt DateTime
|
||||
}
|
||||
type IssueComment struct {
|
||||
Body String
|
||||
}
|
||||
return struct {
|
||||
event // Should be inlined.
|
||||
IssueComment `graphql:"... on IssueComment"` // Should not be, because of graphql tag.
|
||||
CurrentTitle String
|
||||
PreviousTitle String
|
||||
Label struct {
|
||||
Name String
|
||||
Color String
|
||||
}
|
||||
}{}
|
||||
}(),
|
||||
want: `{actor{login,avatarUrl,url},createdAt,... on IssueComment{body},currentTitle,previousTitle,label{name,color}}`,
|
||||
},
|
||||
}
|
||||
for _, tc := range tests {
|
||||
qctx := &queryContext{
|
||||
Scalars: []reflect.Type{
|
||||
reflect.TypeOf(DateTime{}),
|
||||
reflect.TypeOf(URI{}),
|
||||
},
|
||||
}
|
||||
got := constructQuery(qctx, tc.inV, tc.inVariables)
|
||||
if got != tc.want {
|
||||
t.Errorf("\ngot: %q\nwant: %q\n", got, tc.want)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestConstructMutation(t *testing.T) {
|
||||
tests := []struct {
|
||||
inV interface{}
|
||||
inVariables map[string]interface{}
|
||||
want string
|
||||
}{
|
||||
{
|
||||
inV: struct {
|
||||
AddReaction struct {
|
||||
Subject struct {
|
||||
ReactionGroups []struct {
|
||||
Users struct {
|
||||
TotalCount Int
|
||||
}
|
||||
}
|
||||
}
|
||||
} `graphql:"addReaction(input:$input)"`
|
||||
}{},
|
||||
inVariables: map[string]interface{}{
|
||||
"input": AddReactionInput{
|
||||
SubjectID: "MDU6SXNzdWUyMzE1MjcyNzk=",
|
||||
Content: ReactionContentThumbsUp,
|
||||
},
|
||||
},
|
||||
want: `mutation($input:AddReactionInput!){addReaction(input:$input){subject{reactionGroups{users{totalCount}}}}}`,
|
||||
},
|
||||
}
|
||||
for _, tc := range tests {
|
||||
got := constructMutation(&queryContext{}, tc.inV, tc.inVariables)
|
||||
if got != tc.want {
|
||||
t.Errorf("\ngot: %q\nwant: %q\n", got, tc.want)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestQueryArguments(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
in map[string]interface{}
|
||||
want string
|
||||
}{
|
||||
{
|
||||
in: map[string]interface{}{"a": Int(123), "b": NewBoolean(true)},
|
||||
want: "$a:Int!$b:Boolean",
|
||||
},
|
||||
{
|
||||
in: map[string]interface{}{"states": []IssueState{IssueStateOpen, IssueStateClosed}},
|
||||
want: "$states:[IssueState!]",
|
||||
},
|
||||
{
|
||||
in: map[string]interface{}{"states": []IssueState(nil)},
|
||||
want: "$states:[IssueState!]",
|
||||
},
|
||||
{
|
||||
in: map[string]interface{}{"states": [...]IssueState{IssueStateOpen, IssueStateClosed}},
|
||||
want: "$states:[IssueState!]",
|
||||
},
|
||||
{
|
||||
in: map[string]interface{}{"id": ID("someid")},
|
||||
want: "$id:ID!",
|
||||
},
|
||||
}
|
||||
for _, tc := range tests {
|
||||
got := queryArguments(tc.in)
|
||||
if got != tc.want {
|
||||
t.Errorf("%s: got: %q, want: %q", tc.name, got, tc.want)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Custom GraphQL types for testing.
|
||||
type (
|
||||
// DateTime is an ISO-8601 encoded UTC date.
|
||||
DateTime struct{ time.Time }
|
||||
|
||||
// URI is an RFC 3986, RFC 3987, and RFC 6570 (level 4) compliant URI.
|
||||
URI struct{ *url.URL }
|
||||
)
|
||||
|
||||
// IssueState represents the possible states of an issue.
|
||||
type IssueState string
|
||||
|
||||
// The possible states of an issue.
|
||||
const (
|
||||
IssueStateOpen IssueState = "OPEN" // An issue that is still open.
|
||||
IssueStateClosed IssueState = "CLOSED" // An issue that has been closed.
|
||||
)
|
||||
|
||||
// ReactionContent represents emojis that can be attached to Issues, Pull Requests and Comments.
|
||||
type ReactionContent string
|
||||
|
||||
// Emojis that can be attached to Issues, Pull Requests and Comments.
|
||||
const (
|
||||
ReactionContentThumbsUp ReactionContent = "THUMBS_UP" // Represents the 👍 emoji.
|
||||
ReactionContentThumbsDown ReactionContent = "THUMBS_DOWN" // Represents the 👎 emoji.
|
||||
ReactionContentLaugh ReactionContent = "LAUGH" // Represents the 😄 emoji.
|
||||
ReactionContentHooray ReactionContent = "HOORAY" // Represents the 🎉 emoji.
|
||||
ReactionContentConfused ReactionContent = "CONFUSED" // Represents the 😕 emoji.
|
||||
ReactionContentHeart ReactionContent = "HEART" // Represents the ❤️ emoji.
|
||||
)
|
||||
|
||||
// AddReactionInput is an autogenerated input type of AddReaction.
|
||||
type AddReactionInput struct {
|
||||
// The Node ID of the subject to modify. (Required.)
|
||||
SubjectID ID `json:"subjectId"`
|
||||
// The name of the emoji to react with. (Required.)
|
||||
Content ReactionContent `json:"content"`
|
||||
|
||||
// A unique identifier for the client performing the mutation. (Optional.)
|
||||
ClientMutationID *String `json:"clientMutationId,omitempty"`
|
||||
}
|
||||
51
vendor/github.com/shurcooL/graphql/scalar.go
generated
vendored
Normal file
51
vendor/github.com/shurcooL/graphql/scalar.go
generated
vendored
Normal file
@@ -0,0 +1,51 @@
|
||||
package graphql
|
||||
|
||||
// Note: These custom types are meant to be used in queries for now.
|
||||
// But the plan is to switch to using native Go types (string, int, bool, time.Time, etc.).
|
||||
// See https://github.com/shurcooL/githubql/issues/9 for details.
|
||||
//
|
||||
// These custom types currently provide documentation, and their use
|
||||
// is required for sending outbound queries. However, native Go types
|
||||
// can be used for unmarshaling. Once https://github.com/shurcooL/githubql/issues/9
|
||||
// is resolved, native Go types can completely replace these.
|
||||
|
||||
type (
|
||||
// Boolean represents true or false values.
|
||||
Boolean bool
|
||||
|
||||
// Float represents signed double-precision fractional values as
|
||||
// specified by IEEE 754.
|
||||
Float float64
|
||||
|
||||
// ID represents a unique identifier that is Base64 obfuscated. It
|
||||
// is often used to refetch an object or as key for a cache. The ID
|
||||
// type appears in a JSON response as a String; however, it is not
|
||||
// intended to be human-readable. When expected as an input type,
|
||||
// any string (such as "VXNlci0xMA==") or integer (such as 4) input
|
||||
// value will be accepted as an ID.
|
||||
ID interface{}
|
||||
|
||||
// Int represents non-fractional signed whole numeric values.
|
||||
// Int can represent values between -(2^31) and 2^31 - 1.
|
||||
Int int32
|
||||
|
||||
// String represents textual data as UTF-8 character sequences.
|
||||
// This type is most often used by GraphQL to represent free-form
|
||||
// human-readable text.
|
||||
String string
|
||||
)
|
||||
|
||||
// NewBoolean is a helper to make a new *Boolean.
|
||||
func NewBoolean(v Boolean) *Boolean { return &v }
|
||||
|
||||
// NewFloat is a helper to make a new *Float.
|
||||
func NewFloat(v Float) *Float { return &v }
|
||||
|
||||
// NewID is a helper to make a new *ID.
|
||||
func NewID(v ID) *ID { return &v }
|
||||
|
||||
// NewInt is a helper to make a new *Int.
|
||||
func NewInt(v Int) *Int { return &v }
|
||||
|
||||
// NewString is a helper to make a new *String.
|
||||
func NewString(v String) *String { return &v }
|
||||
30
vendor/github.com/shurcooL/graphql/scalar_test.go
generated
vendored
Normal file
30
vendor/github.com/shurcooL/graphql/scalar_test.go
generated
vendored
Normal file
@@ -0,0 +1,30 @@
|
||||
package graphql_test
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/shurcooL/graphql"
|
||||
)
|
||||
|
||||
func TestNewScalars(t *testing.T) {
|
||||
if got := graphql.NewBoolean(false); got == nil {
|
||||
t.Error("NewBoolean returned nil")
|
||||
}
|
||||
if got := graphql.NewFloat(0.0); got == nil {
|
||||
t.Error("NewFloat returned nil")
|
||||
}
|
||||
// ID with underlying type string.
|
||||
if got := graphql.NewID(""); got == nil {
|
||||
t.Error("NewID returned nil")
|
||||
}
|
||||
// ID with underlying type int.
|
||||
if got := graphql.NewID(0); got == nil {
|
||||
t.Error("NewID returned nil")
|
||||
}
|
||||
if got := graphql.NewInt(0); got == nil {
|
||||
t.Error("NewInt returned nil")
|
||||
}
|
||||
if got := graphql.NewString(""); got == nil {
|
||||
t.Error("NewString returned nil")
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user