diff --git a/.gitignore b/.gitignore index 3d725761b..93f0915cf 100644 --- a/.gitignore +++ b/.gitignore @@ -1,2 +1,3 @@ .DS_Store -.idea \ No newline at end of file +.idea +go*.tar.gz diff --git a/.mentat/precommit.sh b/.mentat/precommit.sh new file mode 100644 index 000000000..f43a7e22d --- /dev/null +++ b/.mentat/precommit.sh @@ -0,0 +1,4 @@ +export PATH=$PATH:/usr/local/go/bin +go fmt ./... +go vet ./... +go test -v -cover -race ./... \ No newline at end of file diff --git a/.mentat/setup.sh b/.mentat/setup.sh new file mode 100644 index 000000000..9ab22d84c --- /dev/null +++ b/.mentat/setup.sh @@ -0,0 +1,4 @@ +curl -OL https://go.dev/dl/go1.21.5.linux-amd64.tar.gz +tar -C /usr/local -xzf go1.21.5.linux-amd64.tar.gz +export PATH=$PATH:/usr/local/go/bin +go mod download \ No newline at end of file diff --git a/examples/http/main.go b/examples/http/main.go index a1f2836e8..385c4ef0e 100644 --- a/examples/http/main.go +++ b/examples/http/main.go @@ -17,10 +17,11 @@ type user struct { var data map[string]user /* - Create User object type with fields "id" and "name" by using GraphQLObjectTypeConfig: - - Name: name of object type - - Fields: a map of fields by using GraphQLFields - Setup type of field use GraphQLFieldConfig +Create User object type with fields "id" and "name" by using GraphQLObjectTypeConfig: + - Name: name of object type + - Fields: a map of fields by using GraphQLFields + +Setup type of field use GraphQLFieldConfig */ var userType = graphql.NewObject( graphql.ObjectConfig{ @@ -37,13 +38,14 @@ var userType = graphql.NewObject( ) /* - Create Query object type with fields "user" has type [userType] by using GraphQLObjectTypeConfig: - - Name: name of object type - - Fields: a map of fields by using GraphQLFields - Setup type of field use GraphQLFieldConfig to define: - - Type: type of field - - Args: arguments to query with current field - - Resolve: function to query data using params from [Args] and return value with current type +Create Query object type with fields "user" has type [userType] by using GraphQLObjectTypeConfig: + - Name: name of object type + - Fields: a map of fields by using GraphQLFields + +Setup type of field use GraphQLFieldConfig to define: + - Type: type of field + - Args: arguments to query with current field + - Resolve: function to query data using params from [Args] and return value with current type */ var queryType = graphql.NewObject( graphql.ObjectConfig{ @@ -97,7 +99,7 @@ func main() { http.ListenAndServe(":8080", nil) } -//Helper function to import json from file to map +// Helper function to import json from file to map func importJSONDataFromFile(fileName string, result interface{}) (isOK bool) { isOK = true content, err := ioutil.ReadFile(fileName) diff --git a/examples/sql-nullstring/main.go b/examples/sql-nullstring/main.go index da54f4d6b..03e2a71a7 100644 --- a/examples/sql-nullstring/main.go +++ b/examples/sql-nullstring/main.go @@ -1,255 +1,255 @@ -package main - -import ( - "database/sql" - "encoding/json" - "fmt" - "github.com/graphql-go/graphql" - "github.com/graphql-go/graphql/language/ast" - "log" -) - -// NullString to be used in place of sql.NullString -type NullString struct { - sql.NullString -} - -// MarshalJSON from the json.Marshaler interface -func (v NullString) MarshalJSON() ([]byte, error) { - if v.Valid { - return json.Marshal(v.String) - } - return json.Marshal(nil) -} - -// UnmarshalJSON from the json.Unmarshaler interface -func (v *NullString) UnmarshalJSON(data []byte) error { - var x *string - if err := json.Unmarshal(data, &x); err != nil { - return err - } - if x != nil { - v.String = *x - v.Valid = true - } else { - v.Valid = false - } - return nil -} - -// NewNullString create a new null string. Empty string evaluates to an -// "invalid" NullString -func NewNullString(value string) *NullString { - var null NullString - if value != "" { - null.String = value - null.Valid = true - return &null - } - null.Valid = false - return &null -} - -// SerializeNullString serializes `NullString` to a string -func SerializeNullString(value interface{}) interface{} { - switch value := value.(type) { - case NullString: - return value.String - case *NullString: - v := *value - return v.String - default: - return nil - } -} - -// ParseNullString parses GraphQL variables from `string` to `CustomID` -func ParseNullString(value interface{}) interface{} { - switch value := value.(type) { - case string: - return NewNullString(value) - case *string: - return NewNullString(*value) - default: - return nil - } -} - -// ParseLiteralNullString parses GraphQL AST value to `NullString`. -func ParseLiteralNullString(valueAST ast.Value) interface{} { - switch valueAST := valueAST.(type) { - case *ast.StringValue: - return NewNullString(valueAST.Value) - default: - return nil - } -} - -// NullableString graphql *Scalar type based of NullString -var NullableString = graphql.NewScalar(graphql.ScalarConfig{ - Name: "NullableString", - Description: "The `NullableString` type repesents a nullable SQL string.", - Serialize: SerializeNullString, - ParseValue: ParseNullString, - ParseLiteral: ParseLiteralNullString, -}) - -/* -CREATE TABLE persons ( - favorite_dog TEXT -- is a nullable field - ); - -*/ - -// Person noqa -type Person struct { - Name string `json:"name"` - FavoriteDog *NullString `json:"favorite_dog"` // Some people don't like dogs ¯\_(ツ)_/¯ -} - -// PersonType noqa -var PersonType = graphql.NewObject(graphql.ObjectConfig{ - Name: "Person", - Fields: graphql.Fields{ - "name": &graphql.Field{ - Type: graphql.String, - }, - "favorite_dog": &graphql.Field{ - Type: NullableString, - }, - }, -}) - -func main() { - schema, err := graphql.NewSchema(graphql.SchemaConfig{ - Query: graphql.NewObject(graphql.ObjectConfig{ - Name: "Query", - Fields: graphql.Fields{ - "people": &graphql.Field{ - Type: graphql.NewList(PersonType), - Args: graphql.FieldConfigArgument{ - "favorite_dog": &graphql.ArgumentConfig{ - Type: NullableString, - }, - }, - Resolve: func(p graphql.ResolveParams) (interface{}, error) { - dog, dogOk := p.Args["favorite_dog"].(*NullString) - people := []Person{ - Person{Name: "Alice", FavoriteDog: NewNullString("Yorkshire Terrier")}, - // `Bob`'s favorite dog will be saved as null in the database - Person{Name: "Bob", FavoriteDog: NewNullString("")}, - Person{Name: "Chris", FavoriteDog: NewNullString("French Bulldog")}, - } - switch { - case dogOk: - log.Printf("favorite_dog from arguments: %+v", dog) - dogPeople := make([]Person, 0) - for _, p := range people { - if p.FavoriteDog.Valid { - if p.FavoriteDog.String == dog.String { - dogPeople = append(dogPeople, p) - } - } - } - return dogPeople, nil - default: - return people, nil - } - }, - }, - }, - }), - }) - if err != nil { - log.Fatal(err) - } - query := ` -query { - people { - name - favorite_dog - } -}` - queryWithArgument := ` -query { - people(favorite_dog: "Yorkshire Terrier") { - name - favorite_dog - } -}` - r1 := graphql.Do(graphql.Params{ - Schema: schema, - RequestString: query, - }) - r2 := graphql.Do(graphql.Params{ - Schema: schema, - RequestString: queryWithArgument, - }) - if len(r1.Errors) > 0 { - log.Fatal(r1) - } - if len(r2.Errors) > 0 { - log.Fatal(r1) - } - b1, err := json.MarshalIndent(r1, "", " ") - if err != nil { - log.Fatal(err) - } - b2, err := json.MarshalIndent(r2, "", " ") - if err != nil { - log.Fatal(err) - - } - fmt.Printf("\nQuery: %+v\n", string(query)) - fmt.Printf("\nResult: %+v\n", string(b1)) - fmt.Printf("\nQuery (with arguments): %+v\n", string(queryWithArgument)) - fmt.Printf("\nResult (with arguments): %+v\n", string(b2)) -} - -/* Output: -Query: -query { - people { - name - favorite_dog - } -} - -Result: { - "data": { - "people": [ - { - "favorite_dog": "Yorkshire Terrier", - "name": "Alice" - }, - { - "favorite_dog": "", - "name": "Bob" - }, - { - "favorite_dog": "French Bulldog", - "name": "Chris" - } - ] - } -} - -Query (with arguments): -query { - people(favorite_dog: "Yorkshire Terrier") { - name - favorite_dog - } -} - -Result (with arguments): { - "data": { - "people": [ - { - "favorite_dog": "Yorkshire Terrier", - "name": "Alice" - } - ] - } -} -*/ +package main + +import ( + "database/sql" + "encoding/json" + "fmt" + "github.com/graphql-go/graphql" + "github.com/graphql-go/graphql/language/ast" + "log" +) + +// NullString to be used in place of sql.NullString +type NullString struct { + sql.NullString +} + +// MarshalJSON from the json.Marshaler interface +func (v NullString) MarshalJSON() ([]byte, error) { + if v.Valid { + return json.Marshal(v.String) + } + return json.Marshal(nil) +} + +// UnmarshalJSON from the json.Unmarshaler interface +func (v *NullString) UnmarshalJSON(data []byte) error { + var x *string + if err := json.Unmarshal(data, &x); err != nil { + return err + } + if x != nil { + v.String = *x + v.Valid = true + } else { + v.Valid = false + } + return nil +} + +// NewNullString create a new null string. Empty string evaluates to an +// "invalid" NullString +func NewNullString(value string) *NullString { + var null NullString + if value != "" { + null.String = value + null.Valid = true + return &null + } + null.Valid = false + return &null +} + +// SerializeNullString serializes `NullString` to a string +func SerializeNullString(value interface{}) interface{} { + switch value := value.(type) { + case NullString: + return value.String + case *NullString: + v := *value + return v.String + default: + return nil + } +} + +// ParseNullString parses GraphQL variables from `string` to `CustomID` +func ParseNullString(value interface{}) interface{} { + switch value := value.(type) { + case string: + return NewNullString(value) + case *string: + return NewNullString(*value) + default: + return nil + } +} + +// ParseLiteralNullString parses GraphQL AST value to `NullString`. +func ParseLiteralNullString(valueAST ast.Value) interface{} { + switch valueAST := valueAST.(type) { + case *ast.StringValue: + return NewNullString(valueAST.Value) + default: + return nil + } +} + +// NullableString graphql *Scalar type based of NullString +var NullableString = graphql.NewScalar(graphql.ScalarConfig{ + Name: "NullableString", + Description: "The `NullableString` type repesents a nullable SQL string.", + Serialize: SerializeNullString, + ParseValue: ParseNullString, + ParseLiteral: ParseLiteralNullString, +}) + +/* +CREATE TABLE persons ( + favorite_dog TEXT -- is a nullable field + ); + +*/ + +// Person noqa +type Person struct { + Name string `json:"name"` + FavoriteDog *NullString `json:"favorite_dog"` // Some people don't like dogs ¯\_(ツ)_/¯ +} + +// PersonType noqa +var PersonType = graphql.NewObject(graphql.ObjectConfig{ + Name: "Person", + Fields: graphql.Fields{ + "name": &graphql.Field{ + Type: graphql.String, + }, + "favorite_dog": &graphql.Field{ + Type: NullableString, + }, + }, +}) + +func main() { + schema, err := graphql.NewSchema(graphql.SchemaConfig{ + Query: graphql.NewObject(graphql.ObjectConfig{ + Name: "Query", + Fields: graphql.Fields{ + "people": &graphql.Field{ + Type: graphql.NewList(PersonType), + Args: graphql.FieldConfigArgument{ + "favorite_dog": &graphql.ArgumentConfig{ + Type: NullableString, + }, + }, + Resolve: func(p graphql.ResolveParams) (interface{}, error) { + dog, dogOk := p.Args["favorite_dog"].(*NullString) + people := []Person{ + Person{Name: "Alice", FavoriteDog: NewNullString("Yorkshire Terrier")}, + // `Bob`'s favorite dog will be saved as null in the database + Person{Name: "Bob", FavoriteDog: NewNullString("")}, + Person{Name: "Chris", FavoriteDog: NewNullString("French Bulldog")}, + } + switch { + case dogOk: + log.Printf("favorite_dog from arguments: %+v", dog) + dogPeople := make([]Person, 0) + for _, p := range people { + if p.FavoriteDog.Valid { + if p.FavoriteDog.String == dog.String { + dogPeople = append(dogPeople, p) + } + } + } + return dogPeople, nil + default: + return people, nil + } + }, + }, + }, + }), + }) + if err != nil { + log.Fatal(err) + } + query := ` +query { + people { + name + favorite_dog + } +}` + queryWithArgument := ` +query { + people(favorite_dog: "Yorkshire Terrier") { + name + favorite_dog + } +}` + r1 := graphql.Do(graphql.Params{ + Schema: schema, + RequestString: query, + }) + r2 := graphql.Do(graphql.Params{ + Schema: schema, + RequestString: queryWithArgument, + }) + if len(r1.Errors) > 0 { + log.Fatal(r1) + } + if len(r2.Errors) > 0 { + log.Fatal(r1) + } + b1, err := json.MarshalIndent(r1, "", " ") + if err != nil { + log.Fatal(err) + } + b2, err := json.MarshalIndent(r2, "", " ") + if err != nil { + log.Fatal(err) + + } + fmt.Printf("\nQuery: %+v\n", string(query)) + fmt.Printf("\nResult: %+v\n", string(b1)) + fmt.Printf("\nQuery (with arguments): %+v\n", string(queryWithArgument)) + fmt.Printf("\nResult (with arguments): %+v\n", string(b2)) +} + +/* Output: +Query: +query { + people { + name + favorite_dog + } +} + +Result: { + "data": { + "people": [ + { + "favorite_dog": "Yorkshire Terrier", + "name": "Alice" + }, + { + "favorite_dog": "", + "name": "Bob" + }, + { + "favorite_dog": "French Bulldog", + "name": "Chris" + } + ] + } +} + +Query (with arguments): +query { + people(favorite_dog: "Yorkshire Terrier") { + name + favorite_dog + } +} + +Result (with arguments): { + "data": { + "people": [ + { + "favorite_dog": "Yorkshire Terrier", + "name": "Alice" + } + ] + } +} +*/ diff --git a/language/parser/parser.go b/language/parser/parser.go index 4ae3dc335..2a3cc72f7 100644 --- a/language/parser/parser.go +++ b/language/parser/parser.go @@ -1562,7 +1562,8 @@ func unexpectedEmpty(parser *Parser, beginLoc int, openKind, closeKind lexer.Tok return gqlerrors.NewSyntaxError(parser.Source, beginLoc, description) } -// Returns list of parse nodes, determined by +// Returns list of parse nodes, determined by +// // the parseFn. This list begins with a lex token of openKind // and ends with a lex token of closeKind. Advances the parser // to the next lex token after the closing token. diff --git a/schema.go b/schema.go index c468e3425..2a39d857e 100644 --- a/schema.go +++ b/schema.go @@ -16,21 +16,23 @@ type TypeMap map[string]Type // query, mutation (optional) and subscription (optional). A schema definition is then supplied to the // validator and executor. // Example: -// myAppSchema, err := NewSchema(SchemaConfig({ -// Query: MyAppQueryRootType, -// Mutation: MyAppMutationRootType, -// Subscription: MyAppSubscriptionRootType, -// }); +// +// myAppSchema, err := NewSchema(SchemaConfig({ +// Query: MyAppQueryRootType, +// Mutation: MyAppMutationRootType, +// Subscription: MyAppSubscriptionRootType, +// }); +// // Note: If an array of `directives` are provided to GraphQLSchema, that will be // the exact list of directives represented and allowed. If `directives` is not // provided then a default set of the specified directives (e.g. @include and // @skip) will be used. If you wish to provide *additional* directives to these // specified directives, you must explicitly declare them. Example: // -// const MyAppSchema = new GraphQLSchema({ -// ... -// directives: specifiedDirectives.concat([ myCustomDirective ]), -// }) +// const MyAppSchema = new GraphQLSchema({ +// ... +// directives: specifiedDirectives.concat([ myCustomDirective ]), +// }) type Schema struct { typeMap TypeMap directives []*Directive @@ -143,8 +145,8 @@ func NewSchema(config SchemaConfig) (Schema, error) { return schema, nil } -//Added Check implementation of interfaces at runtime.. -//Add Implementations at Runtime.. +// Added Check implementation of interfaces at runtime.. +// Add Implementations at Runtime.. func (gq *Schema) AddImplementation() error { // Keep track of all implementations by interface name. @@ -179,8 +181,8 @@ func (gq *Schema) AddImplementation() error { return nil } -//Edited. To check add Types at RunTime.. -//Append Runtime schema to typeMap +// Edited. To check add Types at RunTime.. +// Append Runtime schema to typeMap func (gq *Schema) AppendType(objectType Type) error { if objectType.Error() != nil { return objectType.Error() diff --git a/util.go b/util.go index ae374c336..c6af9f47b 100644 --- a/util.go +++ b/util.go @@ -11,9 +11,11 @@ const TAG = "json" // can't take recursive slice type // e.g -// type Person struct{ -// Friends []Person -// } +// +// type Person struct{ +// Friends []Person +// } +// // it will throw panic stack-overflow func BindFields(obj interface{}) Fields { t := reflect.TypeOf(obj)