Skip to content

Commit ab5004f

Browse files
committed
add specified by url directive
1 parent 23090f6 commit ab5004f

4 files changed

Lines changed: 66 additions & 8 deletions

File tree

‎src/main/java/graphql/Directives.java‎

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@
1010
import static graphql.introspection.Introspection.DirectiveLocation.FIELD_DEFINITION;
1111
import static graphql.introspection.Introspection.DirectiveLocation.FRAGMENT_SPREAD;
1212
import static graphql.introspection.Introspection.DirectiveLocation.INLINE_FRAGMENT;
13+
import static graphql.introspection.Introspection.DirectiveLocation.SCALAR;
1314
import static graphql.schema.GraphQLArgument.newArgument;
1415
import static graphql.schema.GraphQLNonNull.nonNull;
1516

@@ -64,11 +65,23 @@ public class Directives {
6465
.description("Marks the field or enum value as deprecated")
6566
.argument(newArgument()
6667
.name("reason")
67-
.type(nonNull(GraphQLString))
68+
.type(GraphQLString)
6869
.defaultValue("No longer supported")
6970
.description("The reason for the deprecation"))
7071
.validLocations(FIELD_DEFINITION, ENUM_VALUE)
7172
.build();
7273

74+
/**
75+
* The "specifiedBy" directive allows to provide a specification URL for a Scala
76+
*/
77+
public static final GraphQLDirective SpecifiedByDirective = GraphQLDirective.newDirective()
78+
.name("specifiedBy")
79+
.description("Specification details for a custom Scalar")
80+
.argument(newArgument()
81+
.name("url")
82+
.type(nonNull(GraphQLString))
83+
.description("An URL which specifies the custom Scalar"))
84+
.validLocations(SCALAR)
85+
.build();
7386

7487
}

‎src/main/java/graphql/schema/GraphQLSchema.java‎

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -525,6 +525,10 @@ GraphQLSchema buildImpl(boolean afterTransform) {
525525
additionalDirectives.add(Directives.DeprecatedDirective);
526526
}
527527

528+
if (additionalDirectives.stream().noneMatch(d -> d.getName().equals(Directives.SpecifiedByDirective.getName()))) {
529+
additionalDirectives.add(Directives.SpecifiedByDirective);
530+
}
531+
528532
// grab the legacy code things from types
529533
final GraphQLSchema tempSchema = new GraphQLSchema(this, afterTransform);
530534
codeRegistry = codeRegistry.transform(codeRegistryBuilder -> schemaUtil.extractCodeFromTypes(codeRegistryBuilder, tempSchema));

‎src/main/java/graphql/schema/idl/SchemaGenerator.java‎

Lines changed: 27 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
package graphql.schema.idl;
22

3+
import graphql.Directives;
34
import graphql.GraphQLError;
45
import graphql.PublicApi;
56
import graphql.introspection.Introspection.DirectiveLocation;
@@ -18,6 +19,7 @@
1819
import graphql.language.OperationTypeDefinition;
1920
import graphql.language.ScalarTypeDefinition;
2021
import graphql.language.ScalarTypeExtensionDefinition;
22+
import graphql.language.StringValue;
2123
import graphql.language.Type;
2224
import graphql.language.TypeDefinition;
2325
import graphql.language.TypeName;
@@ -54,6 +56,7 @@
5456
import graphql.schema.idl.errors.NotAnInputTypeError;
5557
import graphql.schema.idl.errors.NotAnOutputTypeError;
5658
import graphql.schema.idl.errors.SchemaProblem;
59+
import graphql.util.FpKit;
5760

5861
import java.util.ArrayDeque;
5962
import java.util.ArrayList;
@@ -120,6 +123,7 @@ public static Options defaultOptions() {
120123
* directive definition syntax before use.
121124
*
122125
* @param flag the value to use
126+
*
123127
* @return the new options
124128
*/
125129
public Options enforceSchemaDirectives(boolean flag) {
@@ -236,7 +240,9 @@ public SchemaGenerator() {
236240
*
237241
* @param typeRegistry this can be obtained via {@link SchemaParser#parse(String)}
238242
* @param wiring this can be built using {@link RuntimeWiring#newRuntimeWiring()}
243+
*
239244
* @return an executable schema
245+
*
240246
* @throws SchemaProblem if there are problems in assembling a schema such as missing type resolvers or no operations defined
241247
*/
242248
public GraphQLSchema makeExecutableSchema(TypeDefinitionRegistry typeRegistry, RuntimeWiring wiring) throws SchemaProblem {
@@ -250,15 +256,17 @@ public GraphQLSchema makeExecutableSchema(TypeDefinitionRegistry typeRegistry, R
250256
* @param options the controlling options
251257
* @param typeRegistry this can be obtained via {@link SchemaParser#parse(String)}
252258
* @param wiring this can be built using {@link RuntimeWiring#newRuntimeWiring()}
259+
*
253260
* @return an executable schema
261+
*
254262
* @throws SchemaProblem if there are problems in assembling a schema such as missing type resolvers or no operations defined
255263
*/
256264
public GraphQLSchema makeExecutableSchema(Options options, TypeDefinitionRegistry typeRegistry, RuntimeWiring wiring) throws SchemaProblem {
257265

258266
TypeDefinitionRegistry typeRegistryCopy = new TypeDefinitionRegistry();
259267
typeRegistryCopy.merge(typeRegistry);
260268

261-
schemaGeneratorHelper.addDeprecatedDirectiveDefinition(typeRegistryCopy);
269+
schemaGeneratorHelper.addDirectivesIncludedByDefault(typeRegistryCopy);
262270

263271
List<GraphQLError> errors = typeChecker.checkTypeRegistry(typeRegistryCopy, wiring, options.enforceSchemaDirectives);
264272
if (!errors.isEmpty()) {
@@ -374,6 +382,7 @@ private GraphQLObjectType buildOperation(BuildContext buildCtx, OperationTypeDef
374382
* but then we build the rest of the types specified and put them in as additional types
375383
*
376384
* @param buildCtx the context we need to work out what we are doing
385+
*
377386
* @return the additional types not referenced from the top level operations
378387
*/
379388
private Set<GraphQLType> buildAdditionalTypes(BuildContext buildCtx) {
@@ -410,6 +419,7 @@ private Set<GraphQLDirective> buildAdditionalDirectives(BuildContext buildCtx) {
410419
*
411420
* @param buildCtx the context we need to work out what we are doing
412421
* @param rawType the type to be built
422+
*
413423
* @return an output type
414424
*/
415425
@SuppressWarnings({"unchecked", "TypeParameterUnusedInFormals"})
@@ -703,9 +713,13 @@ private GraphQLScalarType buildScalar(BuildContext buildCtx, ScalarTypeDefinitio
703713
scalar = scalar.transform(builder -> builder
704714
.definition(typeDefinition)
705715
.comparatorRegistry(buildCtx.getComparatorRegistry())
706-
.withDirectives(
707-
buildDirectives(typeDefinition.getDirectives(),
708-
directivesOf(extensions), SCALAR, buildCtx.getDirectiveDefinitions(), buildCtx.getComparatorRegistry())
716+
.specifiedBy(getSpecifiedByUrl(typeDefinition))
717+
.withDirectives(buildDirectives(
718+
typeDefinition.getDirectives(),
719+
directivesOf(extensions),
720+
SCALAR,
721+
buildCtx.getDirectiveDefinitions(),
722+
buildCtx.getComparatorRegistry())
709723
));
710724
//
711725
// only allow modification of custom scalars
@@ -714,6 +728,15 @@ private GraphQLScalarType buildScalar(BuildContext buildCtx, ScalarTypeDefinitio
714728
return scalar;
715729
}
716730

731+
private String getSpecifiedByUrl(ScalarTypeDefinition scalarTypeDefinition) {
732+
Optional<Directive> specifiedByDirective = FpKit.findOne(scalarTypeDefinition.getDirectives(),
733+
directiveDefinition -> directiveDefinition.getName().equals(Directives.SpecifiedByDirective.getName()));
734+
return specifiedByDirective.map(directive -> {
735+
StringValue url = (StringValue) directive.getArgument("url").getValue();
736+
return url.getValue();
737+
}).orElse(null);
738+
}
739+
717740
private GraphQLFieldDefinition buildField(BuildContext buildCtx, TypeDefinition parentType, FieldDefinition
718741
fieldDef) {
719742
GraphQLFieldDefinition.Builder builder = GraphQLFieldDefinition.newFieldDefinition();

‎src/main/java/graphql/schema/idl/SchemaGeneratorHelper.java‎

Lines changed: 21 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
package graphql.schema.idl;
22

3+
import graphql.Directives;
34
import graphql.Internal;
45
import graphql.Scalars;
56
import graphql.introspection.Introspection.DirectiveLocation;
@@ -45,6 +46,7 @@
4546
import static graphql.Assert.assertTrue;
4647
import static graphql.introspection.Introspection.DirectiveLocation.ENUM_VALUE;
4748
import static graphql.introspection.Introspection.DirectiveLocation.FIELD_DEFINITION;
49+
import static graphql.introspection.Introspection.DirectiveLocation.SCALAR;
4850
import static graphql.introspection.Introspection.DirectiveLocation.valueOf;
4951
import static graphql.language.DirectiveLocation.newDirectiveLocation;
5052
import static graphql.schema.GraphQLList.list;
@@ -64,19 +66,34 @@ public class SchemaGeneratorHelper {
6466

6567
static final String NO_LONGER_SUPPORTED = "No longer supported";
6668
static final DirectiveDefinition DEPRECATED_DIRECTIVE_DEFINITION;
69+
static final DirectiveDefinition SPECIFIED_BY_DIRECTIVE_DEFINITION;
6770

6871
static {
69-
DEPRECATED_DIRECTIVE_DEFINITION = DirectiveDefinition.newDirectiveDefinition().name("deprecated")
72+
DEPRECATED_DIRECTIVE_DEFINITION = DirectiveDefinition.newDirectiveDefinition()
73+
.name(Directives.DeprecatedDirective.getName())
7074
.directiveLocation(newDirectiveLocation().name(FIELD_DEFINITION.name()).build())
7175
.directiveLocation(newDirectiveLocation().name(ENUM_VALUE.name()).build())
76+
.description(createDescription("Marks the field or enum value as deprecated"))
7277
.inputValueDefinition(
7378
InputValueDefinition.newInputValueDefinition()
7479
.name("reason")
7580
.description(createDescription("The reason for the deprecation"))
7681
.type(TypeName.newTypeName().name("String").build())
7782
.defaultValue(StringValue.newStringValue().value(NO_LONGER_SUPPORTED).build())
7883
.build())
79-
.description(createDescription("Marks the field or enum value as deprecated")).build();
84+
.build();
85+
86+
SPECIFIED_BY_DIRECTIVE_DEFINITION = DirectiveDefinition.newDirectiveDefinition()
87+
.name(Directives.SpecifiedByDirective.getName())
88+
.directiveLocation(newDirectiveLocation().name(SCALAR.name()).build())
89+
.description(createDescription("Specification details for a custom Scalar"))
90+
.inputValueDefinition(
91+
InputValueDefinition.newInputValueDefinition()
92+
.name("url")
93+
.description(createDescription("An URL which specifies the custom Scalar"))
94+
.type(TypeName.newTypeName().name("String").build())
95+
.build())
96+
.build();
8097
}
8198

8299
private static Description createDescription(String s) {
@@ -168,9 +185,10 @@ public String buildDeprecationReason(List<Directive> directives) {
168185
return null;
169186
}
170187

171-
public void addDeprecatedDirectiveDefinition(TypeDefinitionRegistry typeRegistry) {
188+
public void addDirectivesIncludedByDefault(TypeDefinitionRegistry typeRegistry) {
172189
// we synthesize this into the type registry - no need for them to add it
173190
typeRegistry.add(DEPRECATED_DIRECTIVE_DEFINITION);
191+
typeRegistry.add(SPECIFIED_BY_DIRECTIVE_DEFINITION);
174192
}
175193

176194
/**

0 commit comments

Comments
 (0)