Skip to content

Commit b9af10d

Browse files
authored
feat: add support for federation 2.9 (#417)
1 parent 28e1065 commit b9af10d

File tree

10 files changed

+415
-1
lines changed

10 files changed

+415
-1
lines changed

graphql-java-support/src/main/java/com/apollographql/federation/graphqljava/Federation.java

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,7 @@ public final class Federation {
3535
public static final String FEDERATION_SPEC_V2_6 = "https://specs.apollo.dev/federation/v2.6";
3636
public static final String FEDERATION_SPEC_V2_7 = "https://specs.apollo.dev/federation/v2.7";
3737
public static final String FEDERATION_SPEC_V2_8 = "https://specs.apollo.dev/federation/v2.8";
38+
public static final String FEDERATION_SPEC_V2_9 = "https://specs.apollo.dev/federation/v2.9";
3839

3940
private static final SchemaGenerator.Options defaultGeneratorOptions =
4041
SchemaGenerator.Options.defaultOptions();

graphql-java-support/src/main/java/com/apollographql/federation/graphqljava/FederationDirectives.java

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99
import static com.apollographql.federation.graphqljava.Federation.FEDERATION_SPEC_V2_6;
1010
import static com.apollographql.federation.graphqljava.Federation.FEDERATION_SPEC_V2_7;
1111
import static com.apollographql.federation.graphqljava.Federation.FEDERATION_SPEC_V2_8;
12+
import static com.apollographql.federation.graphqljava.Federation.FEDERATION_SPEC_V2_9;
1213
import static graphql.introspection.Introspection.DirectiveLocation.FIELD_DEFINITION;
1314
import static graphql.introspection.Introspection.DirectiveLocation.INTERFACE;
1415
import static graphql.introspection.Introspection.DirectiveLocation.OBJECT;
@@ -232,6 +233,8 @@ public static List<SDLNamedDefinition> loadFederationSpecDefinitions(String fede
232233
return loadFed2Definitions("definitions_fed2_7.graphqls");
233234
case FEDERATION_SPEC_V2_8:
234235
return loadFed2Definitions("definitions_fed2_8.graphqls");
236+
case FEDERATION_SPEC_V2_9:
237+
return loadFed2Definitions("definitions_fed2_9.graphqls");
235238
default:
236239
throw new UnsupportedFederationVersionException(federationSpec);
237240
}

graphql-java-support/src/main/java/com/apollographql/federation/graphqljava/directives/LinkDirectiveProcessor.java

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,11 @@ public final class LinkDirectiveProcessor {
3232
"@interfaceObject", 23,
3333
"@authenticated", 25,
3434
"@requiresScopes", 25,
35-
"@policy", 26);
35+
"@policy", 26,
36+
"@context", 28,
37+
"@fromContext", 28,
38+
"@cost", 29,
39+
"@listSize", 29);
3640

3741
private LinkDirectiveProcessor() {}
3842

Lines changed: 136 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,136 @@
1+
#
2+
# https://specs.apollo.dev/federation/v2.0/federation-v2.0.graphql
3+
#
4+
5+
directive @key(fields: FieldSet!, resolvable: Boolean = true) repeatable on OBJECT | INTERFACE
6+
directive @requires(fields: FieldSet!) on FIELD_DEFINITION
7+
directive @provides(fields: FieldSet!) on FIELD_DEFINITION
8+
directive @external on OBJECT | FIELD_DEFINITION
9+
directive @extends on OBJECT | INTERFACE
10+
directive @inaccessible on
11+
| FIELD_DEFINITION
12+
| OBJECT
13+
| INTERFACE
14+
| UNION
15+
| ENUM
16+
| ENUM_VALUE
17+
| SCALAR
18+
| INPUT_OBJECT
19+
| INPUT_FIELD_DEFINITION
20+
| ARGUMENT_DEFINITION
21+
directive @tag(name: String!) repeatable on
22+
| FIELD_DEFINITION
23+
| INTERFACE
24+
| OBJECT
25+
| UNION
26+
| ARGUMENT_DEFINITION
27+
| SCALAR
28+
| ENUM
29+
| ENUM_VALUE
30+
| INPUT_OBJECT
31+
| INPUT_FIELD_DEFINITION
32+
scalar FieldSet
33+
34+
#
35+
# https://specs.apollo.dev/link/v1.0/link-v1.0.graphql
36+
#
37+
38+
directive @link(
39+
url: String!,
40+
as: String,
41+
import: [Import],
42+
for: Purpose)
43+
repeatable on SCHEMA
44+
45+
scalar Import
46+
47+
enum Purpose {
48+
SECURITY
49+
EXECUTION
50+
}
51+
52+
#
53+
# federation-v2.1
54+
#
55+
56+
directive @composeDirective(name: String!) repeatable on SCHEMA
57+
58+
#
59+
# federation-v2.2
60+
#
61+
62+
directive @shareable repeatable on FIELD_DEFINITION | OBJECT
63+
64+
#
65+
# federation-v2.3
66+
#
67+
68+
directive @interfaceObject on OBJECT
69+
70+
#
71+
# federation-v2.5
72+
#
73+
74+
directive @authenticated on
75+
ENUM
76+
| FIELD_DEFINITION
77+
| INTERFACE
78+
| OBJECT
79+
| SCALAR
80+
81+
directive @requiresScopes(scopes: [[Scope!]!]!) on
82+
ENUM
83+
| FIELD_DEFINITION
84+
| INTERFACE
85+
| OBJECT
86+
| SCALAR
87+
88+
scalar Scope
89+
90+
#
91+
# federation-v2.6
92+
#
93+
94+
directive @policy(policies: [[Policy!]!]!) on
95+
ENUM
96+
| FIELD_DEFINITION
97+
| INTERFACE
98+
| OBJECT
99+
| SCALAR
100+
101+
scalar Policy
102+
103+
#
104+
# federation-v2.7
105+
#
106+
107+
directive @override(from: String!, label: String) on FIELD_DEFINITION
108+
109+
#
110+
# federation-v2.8
111+
#
112+
113+
scalar ContextFieldValue
114+
115+
directive @context(name: String!) repeatable on INTERFACE | OBJECT | UNION
116+
117+
directive @fromContext(field: ContextFieldValue) on ARGUMENT_DEFINITION
118+
119+
#
120+
# federation-v2.9
121+
#
122+
123+
directive @cost(weight: Int!) on
124+
ARGUMENT_DEFINITION
125+
| ENUM
126+
| FIELD_DEFINITION
127+
| INPUT_FIELD_DEFINITION
128+
| OBJECT
129+
| SCALAR
130+
131+
directive @listSize(
132+
assumedSize: Int,
133+
slicingArguments: [String!],
134+
sizedFields: [String!],
135+
requireOneSlicingArgument: Boolean = true
136+
) on FIELD_DEFINITION

graphql-java-support/src/test/java/com/apollographql/federation/graphqljava/FederationTest.java

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -332,6 +332,14 @@ public void verifyFederationV2Transformation_context() {
332332
verifyFederationTransformation("schemas/context/schema.graphql");
333333
}
334334

335+
@Test
336+
public void verifyFederationV2Transformation_contextFromUnsupportedVersion_throwsException() {
337+
final String schemaSDL = FileUtils.readResource("schemas/invalidSpecVersionContext.graphql");
338+
assertThrows(
339+
UnsupportedLinkImportException.class,
340+
() -> Federation.transform(schemaSDL).fetchEntities(env -> null).build());
341+
}
342+
335343
@Test
336344
public void
337345
verifyFederationV2Transformation_requiresScopesFromUnsupportedVersion_throwsException() {
@@ -370,6 +378,19 @@ public void verifyFederationV2Transformation_scalarsDefinedInSchemaButNotWired()
370378
verifyFederationTransformation("schemas/scalars/schema.graphql");
371379
}
372380

381+
@Test
382+
public void verifyFederationV2Transformation_cost() {
383+
verifyFederationTransformation("schemas/cost/schema.graphql");
384+
}
385+
386+
@Test
387+
public void verifyFederationV2Transformation_costFromUnsupportedVersion_throwsException() {
388+
final String schemaSDL = FileUtils.readResource("schemas/invalidSpecVersionCost.graphql");
389+
assertThrows(
390+
UnsupportedLinkImportException.class,
391+
() -> Federation.transform(schemaSDL).fetchEntities(env -> null).build());
392+
}
393+
373394
private void verifyFederationTransformation(String schemaFileName) {
374395
final RuntimeWiring runtimeWiring = RuntimeWiring.newRuntimeWiring().build();
375396
verifyFederationTransformation(schemaFileName, runtimeWiring);
Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
extend schema
2+
@link(
3+
url: "https://specs.apollo.dev/federation/v2.9"
4+
import: ["@cost", "@key", "@listSize"]
5+
)
6+
7+
type Product @key(fields: "id") {
8+
id: ID!
9+
name: String!
10+
child: Child! @cost(weight: 5)
11+
}
12+
13+
type Child @key(fields: "id") {
14+
id: ID!
15+
details(productName: String): [String!] @listSize(assumedSize: 10)
16+
}
17+
18+
type Query {
19+
product(id: ID!): Product
20+
}
Lines changed: 81 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,81 @@
1+
schema @link(import : ["@cost", "@key", "@listSize"], url : "https://specs.apollo.dev/federation/v2.9"){
2+
query: Query
3+
}
4+
5+
directive @cost(weight: Int!) on SCALAR | OBJECT | FIELD_DEFINITION | ARGUMENT_DEFINITION | ENUM | INPUT_FIELD_DEFINITION
6+
7+
directive @federation__authenticated on SCALAR | OBJECT | FIELD_DEFINITION | INTERFACE | ENUM
8+
9+
directive @federation__composeDirective(name: String!) repeatable on SCHEMA
10+
11+
directive @federation__context(name: String!) repeatable on OBJECT | INTERFACE | UNION
12+
13+
directive @federation__extends on OBJECT | INTERFACE
14+
15+
directive @federation__external on OBJECT | FIELD_DEFINITION
16+
17+
directive @federation__fromContext(field: federation__ContextFieldValue) on ARGUMENT_DEFINITION
18+
19+
directive @federation__interfaceObject on OBJECT
20+
21+
directive @federation__override(from: String!, label: String) on FIELD_DEFINITION
22+
23+
directive @federation__policy(policies: [[federation__Policy!]!]!) on SCALAR | OBJECT | FIELD_DEFINITION | INTERFACE | ENUM
24+
25+
directive @federation__provides(fields: federation__FieldSet!) on FIELD_DEFINITION
26+
27+
directive @federation__requires(fields: federation__FieldSet!) on FIELD_DEFINITION
28+
29+
directive @federation__requiresScopes(scopes: [[federation__Scope!]!]!) on SCALAR | OBJECT | FIELD_DEFINITION | INTERFACE | ENUM
30+
31+
directive @federation__shareable repeatable on OBJECT | FIELD_DEFINITION
32+
33+
directive @inaccessible on SCALAR | OBJECT | FIELD_DEFINITION | ARGUMENT_DEFINITION | INTERFACE | UNION | ENUM | ENUM_VALUE | INPUT_OBJECT | INPUT_FIELD_DEFINITION
34+
35+
directive @key(fields: federation__FieldSet!, resolvable: Boolean = true) repeatable on OBJECT | INTERFACE
36+
37+
directive @link(as: String, for: link__Purpose, import: [link__Import], url: String!) repeatable on SCHEMA
38+
39+
directive @listSize(assumedSize: Int, requireOneSlicingArgument: Boolean = true, sizedFields: [String!], slicingArguments: [String!]) on FIELD_DEFINITION
40+
41+
directive @tag(name: String!) repeatable on SCALAR | OBJECT | FIELD_DEFINITION | ARGUMENT_DEFINITION | INTERFACE | UNION | ENUM | ENUM_VALUE | INPUT_OBJECT | INPUT_FIELD_DEFINITION
42+
43+
union _Entity = Child | Product
44+
45+
type Child @key(fields : "id", resolvable : true) {
46+
details(productName: String): [String!] @listSize(assumedSize : 10, requireOneSlicingArgument : true)
47+
id: ID!
48+
}
49+
50+
type Product @key(fields : "id", resolvable : true) {
51+
child: Child! @cost(weight : 5)
52+
id: ID!
53+
name: String!
54+
}
55+
56+
type Query {
57+
_entities(representations: [_Any!]!): [_Entity]!
58+
_service: _Service!
59+
product(id: ID!): Product
60+
}
61+
62+
type _Service {
63+
sdl: String!
64+
}
65+
66+
enum link__Purpose {
67+
EXECUTION
68+
SECURITY
69+
}
70+
71+
scalar _Any
72+
73+
scalar federation__ContextFieldValue
74+
75+
scalar federation__FieldSet
76+
77+
scalar federation__Policy
78+
79+
scalar federation__Scope
80+
81+
scalar link__Import

0 commit comments

Comments
 (0)