Gemini
Initial commit
fce10de
// Ths contents of this file are taken from https://github.com/shurcooL/graphql/blob/ed46e5a4646634fc16cb07c3b8db389542cc8847/query.go
// because they are not exported by the module, and we would like to use them in building the githubv4mock test utility.
//
// The original license, copied from https://github.com/shurcooL/graphql/blob/ed46e5a4646634fc16cb07c3b8db389542cc8847/LICENSE
//
// 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.
package githubv4mock
import (
"bytes"
"encoding/json"
"io"
"reflect"
"sort"
"github.com/shurcooL/graphql/ident"
)
func constructQuery(v any, variables map[string]any) string {
query := query(v)
if len(variables) > 0 {
return "query(" + queryArguments(variables) + ")" + query
}
return query
}
func constructMutation(v any, variables map[string]any) string {
query := query(v)
if len(variables) > 0 {
return "mutation(" + queryArguments(variables) + ")" + query
}
return "mutation" + query
}
// queryArguments constructs a minified arguments string for variables.
//
// E.g., map[string]any{"a": Int(123), "b": NewBoolean(true)} -> "$a:Int!$b:Boolean".
func queryArguments(variables map[string]any) string {
// Sort keys in order to produce deterministic output for testing purposes.
// TODO: If tests can be made to work with non-deterministic output, then no need to sort.
keys := make([]string, 0, len(variables))
for k := range variables {
keys = append(keys, k)
}
sort.Strings(keys)
var buf bytes.Buffer
for _, k := range keys {
_, _ = io.WriteString(&buf, "$")
_, _ = io.WriteString(&buf, k)
_, _ = io.WriteString(&buf, ":")
writeArgumentType(&buf, reflect.TypeOf(variables[k]), true)
// Don't insert a comma here.
// Commas in GraphQL are insignificant, and we want minified output.
// See https://spec.graphql.org/October2021/#sec-Insignificant-Commas.
}
return buf.String()
}
// writeArgumentType writes a minified GraphQL type for t to w.
// value indicates whether t is a value (required) type or pointer (optional) type.
// If value is true, then "!" is written at the end of t.
func writeArgumentType(w io.Writer, t reflect.Type, value bool) {
if t.Kind() == reflect.Ptr {
// Pointer is an optional type, so no "!" at the end of the pointer's underlying type.
writeArgumentType(w, t.Elem(), false)
return
}
switch t.Kind() {
case reflect.Slice, reflect.Array:
// List. E.g., "[Int]".
_, _ = io.WriteString(w, "[")
writeArgumentType(w, t.Elem(), true)
_, _ = io.WriteString(w, "]")
default:
// Named type. E.g., "Int".
name := t.Name()
if name == "string" { // HACK: Workaround for https://github.com/shurcooL/githubv4/issues/12.
name = "ID"
}
_, _ = io.WriteString(w, name)
}
if value {
// Value is a required type, so add "!" to the end.
_, _ = io.WriteString(w, "!")
}
}
// 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 query(v any) string {
var buf bytes.Buffer
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 writeQuery(w io.Writer, t reflect.Type, inline bool) {
switch t.Kind() {
case reflect.Ptr, reflect.Slice:
writeQuery(w, t.Elem(), false)
case reflect.Struct:
// If the type implements json.Unmarshaler, it's a scalar. Don't expand it.
if reflect.PointerTo(t).Implements(jsonUnmarshaler) {
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())
}
}
writeQuery(w, f.Type, inlineField)
}
if !inline {
_, _ = io.WriteString(w, "}")
}
}
}
var jsonUnmarshaler = reflect.TypeOf((*json.Unmarshaler)(nil)).Elem()