Spaces:
Build error
Build error
| // 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() | |