From 0bb7429150c1623cc7d9fbb439f17269076a7cd5 Mon Sep 17 00:00:00 2001 From: Alex Date: Wed, 31 Aug 2022 15:18:22 +0300 Subject: [PATCH] add specifiedBy directive (#532) --- example/social/introspect.json | 37 ++++++++++++++++- example/starwars/introspect.json | 37 ++++++++++++++++- graphql_test.go | 70 ++++++++++++++++++++++++++++---- internal/schema/meta.go | 7 ++++ introspection/introspection.go | 14 +++++++ 5 files changed, 155 insertions(+), 10 deletions(-) diff --git a/example/social/introspect.json b/example/social/introspect.json index f344b600..795803f7 100644 --- a/example/social/introspect.json +++ b/example/social/introspect.json @@ -70,6 +70,29 @@ "INLINE_FRAGMENT" ], "name": "skip" + }, + { + "args": [ + { + "defaultValue": null, + "description": "The URL should point to a human-readable specification of the data format, serialization, and coercion rules.", + "name": "url", + "type": { + "kind": "NON_NULL", + "name": null, + "ofType": { + "kind": "SCALAR", + "name": "String", + "ofType": null + } + } + } + ], + "description": "Provides a scalar specification URL for specifying the behavior of custom scalar types.", + "locations": [ + "SCALAR" + ], + "name": "specifiedBy" } ], "mutationType": null, @@ -1339,6 +1362,18 @@ "name": "__Type", "ofType": null } + }, + { + "args": [], + "deprecationReason": null, + "description": null, + "isDeprecated": false, + "name": "specifiedByURL", + "type": { + "kind": "SCALAR", + "name": "String", + "ofType": null + } } ], "inputFields": null, @@ -1408,4 +1443,4 @@ } ] } -} \ No newline at end of file +} diff --git a/example/starwars/introspect.json b/example/starwars/introspect.json index 89ed5a3f..a25eace3 100644 --- a/example/starwars/introspect.json +++ b/example/starwars/introspect.json @@ -70,6 +70,29 @@ "INLINE_FRAGMENT" ], "name": "skip" + }, + { + "args": [ + { + "defaultValue": null, + "description": "The URL should point to a human-readable specification of the data format, serialization, and coercion rules.", + "name": "url", + "type": { + "kind": "NON_NULL", + "name": null, + "ofType": { + "kind": "SCALAR", + "name": "String", + "ofType": null + } + } + } + ], + "description": "Provides a scalar specification URL for specifying the behavior of custom scalar types.", + "locations": [ + "SCALAR" + ], + "name": "specifiedBy" } ], "mutationType": { @@ -1981,6 +2004,18 @@ "name": "__Type", "ofType": null } + }, + { + "args": [], + "deprecationReason": null, + "description": null, + "isDeprecated": false, + "name": "specifiedByURL", + "type": { + "kind": "SCALAR", + "name": "String", + "ofType": null + } } ], "inputFields": null, @@ -2050,4 +2085,4 @@ } ] } -} \ No newline at end of file +} diff --git a/graphql_test.go b/graphql_test.go index c82c0e5a..f906f14b 100644 --- a/graphql_test.go +++ b/graphql_test.go @@ -626,7 +626,7 @@ func TestEmbeddedStruct(t *testing.T) { type Query { course: Course! } - + type Course { name: String! createdAt: String! @@ -1494,6 +1494,40 @@ func TestDeprecatedDirective(t *testing.T) { }) } +func TestSpecifiedByDirective(t *testing.T) { + gqltesting.RunTests(t, []*gqltesting.Test{ + { + Schema: graphql.MustParseSchema(` + schema { + query: Query + } + type Query { + } + scalar UUID @specifiedBy( + url: "https://tools.ietf.org/html/rfc4122" + ) + `, &struct{}{}), + Query: ` + query { + __type(name: "UUID") { + name + specifiedByURL + } + } + `, + Variables: map[string]interface{}{}, + ExpectedResult: ` + { + "__type": { + "name": "UUID", + "specifiedByURL": "https://tools.ietf.org/html/rfc4122" + } + } + `, + }, + }) +} + type testBadEnumResolver struct{} func (r *testBadEnumResolver) Hero() *testBadEnumCharacterResolver { @@ -1854,11 +1888,11 @@ func TestTypeName(t *testing.T) { } } } - + fragment Droid on Droid { name __typename - } + } `, RawResponse: true, ExpectedResult: `{"hero":{"__typename":"Droid","name":"R2-D2"}}`, @@ -2478,6 +2512,26 @@ func TestIntrospection(t *testing.T) { } } ] + }, + { + "name": "specifiedBy", + "description": "Provides a scalar specification URL for specifying the behavior of custom scalar types.", + "locations": [ + "SCALAR" + ], + "args": [ + { + "name": "url", + "description": "The URL should point to a human-readable specification of the data format, serialization, and coercion rules.", + "type": { + "kind": "NON_NULL", + "ofType": { + "kind": "SCALAR", + "name": "String" + } + } + } + ] } ] } @@ -3800,7 +3854,7 @@ func TestPanicAmbiguity(t *testing.T) { name: String! university: University! } - + type University { name: String! } @@ -4300,11 +4354,11 @@ func TestQueryVariablesValidation(t *testing.T) { required: String! optional: String } - + type SearchResults { match: String } - + type Query { search(filter: SearchFilter!): [SearchResults!]! }`, &queryVarResolver{}, graphql.UseFieldResolvers()), @@ -4325,11 +4379,11 @@ func TestQueryVariablesValidation(t *testing.T) { required: String! optional: String } - + type SearchResults { match: String } - + type Query { search(filter: SearchFilter!): [SearchResults!]! }`, &queryVarResolver{}, graphql.UseFieldResolvers()), diff --git a/internal/schema/meta.go b/internal/schema/meta.go index 2268b7e7..154c07cc 100644 --- a/internal/schema/meta.go +++ b/internal/schema/meta.go @@ -59,6 +59,12 @@ var metaSrc = ` reason: String = "No longer supported" ) on FIELD_DEFINITION | ENUM_VALUE + # Provides a scalar specification URL for specifying the behavior of custom scalar types. + directive @specifiedBy( + # The URL should point to a human-readable specification of the data format, serialization, and coercion rules. + url: String! + ) on SCALAR + # A Directive provides a way to describe alternate runtime execution and type validation behavior in a GraphQL document. # # In some cases, you need to provide options to alter GraphQL's execution behavior @@ -179,6 +185,7 @@ var metaSrc = ` enumValues(includeDeprecated: Boolean = false): [__EnumValue!] inputFields: [__InputValue!] ofType: __Type + specifiedByURL: String } # An enum describing what kind of type a given ` + "`" + `__Type` + "`" + ` is. diff --git a/introspection/introspection.go b/introspection/introspection.go index 3df66055..2eb728f4 100644 --- a/introspection/introspection.go +++ b/introspection/introspection.go @@ -189,6 +189,20 @@ func (r *Type) OfType() *Type { } } +func (r *Type) SpecifiedByURL() *string { + switch t := r.typ.(type) { + case *types.ScalarTypeDefinition: + if d := t.Directives.Get("specifiedBy"); d != nil { + arg := d.Arguments.MustGet("url") + url := arg.Deserialize(nil).(string) + return &url + } + default: + return nil + } + return nil +} + type Field struct { field *types.FieldDefinition }