Skip to content

Commit bae794f

Browse files
authored
Merge pull request #4276 from rstata/is-4259
2 parents 739403f + 95b06a1 commit bae794f

File tree

2 files changed

+102
-0
lines changed

2 files changed

+102
-0
lines changed

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

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -931,6 +931,30 @@ public Builder additionalDirective(GraphQLDirective additionalDirective) {
931931
return this;
932932
}
933933

934+
/**
935+
* Clears all directives from this builder, including any that were previously added
936+
* via {@link #additionalDirective(GraphQLDirective)} or {@link #additionalDirectives(Set)}.
937+
* Built-in directives ({@code @include}, {@code @skip}, {@code @deprecated}, etc.) will
938+
* always be added back automatically at build time by {@code ensureBuiltInDirectives()}.
939+
* <p>
940+
* This is useful when transforming a schema to replace all non-built-in directives:
941+
* <pre>{@code
942+
* schema.transform(builder -> {
943+
* List<GraphQLDirective> nonBuiltIns = schema.getDirectives().stream()
944+
* .filter(d -> !Directives.isBuiltInDirective(d))
945+
* .collect(toList());
946+
* builder.clearDirectives()
947+
* .additionalDirectives(transform(nonBuiltIns));
948+
* })
949+
* }</pre>
950+
*
951+
* @return this builder
952+
*/
953+
public Builder clearDirectives() {
954+
this.additionalDirectives.clear();
955+
return this;
956+
}
957+
934958
public Builder withSchemaDirectives(GraphQLDirective... directives) {
935959
for (GraphQLDirective directive : directives) {
936960
withSchemaDirective(directive);

src/test/groovy/graphql/schema/GraphQLSchemaTest.groovy

Lines changed: 78 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ package graphql.schema
33
import graphql.AssertException
44
import graphql.Directives
55
import graphql.ExecutionInput
6+
import graphql.introspection.Introspection.DirectiveLocation
67
import graphql.GraphQL
78
import graphql.TestUtil
89
import graphql.language.Directive
@@ -168,6 +169,83 @@ class GraphQLSchemaTest extends Specification {
168169
schema = schema.transform({ builder -> builder })
169170
then: "all 7 built-in directives are still present"
170171
schema.directives.size() == 7
172+
173+
when: "clearDirectives is called"
174+
schema = basicSchemaBuilder().clearDirectives().build()
175+
then: "all 7 built-in directives are still present because ensureBuiltInDirectives re-adds them"
176+
schema.directives.size() == 7
177+
178+
when: "clearDirectives is called and additional directives are added"
179+
schema = basicSchemaBuilder().clearDirectives()
180+
.additionalDirective(GraphQLDirective.newDirective()
181+
.name("custom")
182+
.validLocations(DirectiveLocation.FIELD)
183+
.build())
184+
.build()
185+
then: "all 7 built-in directives are present plus the additional one"
186+
schema.directives.size() == 8
187+
schema.getDirective("custom") != null
188+
}
189+
190+
def "clearDirectives supports replacing non-built-in directives in a schema transform"() {
191+
given: "a schema with a custom directive"
192+
def originalDirective = GraphQLDirective.newDirective()
193+
.name("custom")
194+
.description("v1")
195+
.validLocations(DirectiveLocation.FIELD)
196+
.build()
197+
def schema = basicSchemaBuilder()
198+
.additionalDirective(originalDirective)
199+
.build()
200+
assert schema.directives.size() == 8
201+
202+
when: "the schema is transformed to replace the custom directive"
203+
def replacementDirective = GraphQLDirective.newDirective()
204+
.name("custom")
205+
.description("v2")
206+
.validLocations(DirectiveLocation.FIELD)
207+
.build()
208+
def newSchema = schema.transform({ builder ->
209+
def nonBuiltIns = schema.getDirectives().findAll { !Directives.isBuiltInDirective(it) }
210+
.collect { it.getName() == "custom" ? replacementDirective : it }
211+
builder.clearDirectives()
212+
.additionalDirectives(new LinkedHashSet<>(nonBuiltIns))
213+
})
214+
215+
then: "all 7 built-in directives are still present"
216+
newSchema.directives.size() == 8
217+
newSchema.getDirective("include") != null
218+
newSchema.getDirective("skip") != null
219+
newSchema.getDirective("deprecated") != null
220+
221+
and: "the custom directive has the updated description"
222+
newSchema.getDirective("custom").description == "v2"
223+
}
224+
225+
def "clearDirectives then adding directives gives expected ordering"() {
226+
given: "a non-standard directive and a customized built-in directive"
227+
def nonStandard = GraphQLDirective.newDirective()
228+
.name("custom")
229+
.validLocations(DirectiveLocation.FIELD)
230+
.build()
231+
def skipWithCustomDesc = Directives.SkipDirective.transform({ b ->
232+
b.description("custom skip description")
233+
})
234+
235+
when: "clearDirectives is called, then the non-standard directive is added, then the customized built-in is added after it"
236+
def schema = basicSchemaBuilder()
237+
.clearDirectives()
238+
.additionalDirective(nonStandard)
239+
.additionalDirective(skipWithCustomDesc)
240+
.build()
241+
242+
then: "unoverridden built-ins come first (in BUILT_IN_DIRECTIVES order, skip excluded), then user-supplied in insertion order"
243+
def names = schema.directives.collect { it.name }
244+
names == ["include", "deprecated", "specifiedBy", "oneOf", "defer",
245+
"experimental_disableErrorPropagation", "custom", "skip"]
246+
247+
and: "the customized skip directive retains its custom description"
248+
schema.getDirective("skip").description == "custom skip description"
171249
}
172250

173251
def "clear additional types works as expected"() {

0 commit comments

Comments
 (0)