Skip to content

Commit

Permalink
feat: federation 2.5 support (#347)
Browse files Browse the repository at this point in the history
Adds support for [Federation specification
v2.5](https://www.apollographql.com/docs/federation/federation-versions#v25).

Changes:

* new `@authenticated` directive

```graphql
directive @authenticated on
    ENUM
  | FIELD_DEFINITION
  | INTERFACE
  | OBJECT
  | SCALAR
```

* new `@requiresScopes` directive

```graphql
directive @requiresScopes(scopes: [[Scope!]!]!) on
    ENUM
  | FIELD_DEFINITION
  | INTERFACE
  | OBJECT
  | SCALAR

scalar Scope
```
  • Loading branch information
dariuszkuc authored Sep 6, 2023
1 parent e78025f commit 0ad2582
Show file tree
Hide file tree
Showing 11 changed files with 256 additions and 7 deletions.
2 changes: 1 addition & 1 deletion .github/workflows/compatibility.yml
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ jobs:
run: ./gradlew bootJar

- name: Compatibility Test
uses: apollographql/federation-subgraph-compatibility@v1
uses: apollographql/federation-subgraph-compatibility@v2
with:
compose: 'docker-compose.yaml'
schema: 'src/main/resources/graphql/schema.graphqls'
Expand Down
2 changes: 1 addition & 1 deletion compatibility/Dockerfile
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
FROM gradle:8.2.1-jdk17
FROM openjdk:17

EXPOSE 4001
RUN mkdir /app
Expand Down
2 changes: 1 addition & 1 deletion compatibility/src/main/resources/graphql/schema.graphqls
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
extend schema
@link(
url: "https://specs.apollo.dev/federation/v2.3",
url: "https://specs.apollo.dev/federation/v2.5",
import: [
"@composeDirective",
"@extends",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -27,10 +27,10 @@ public final class Federation {

public static final String FEDERATION_SPEC_V2_0 = "https://specs.apollo.dev/federation/v2.0";
public static final String FEDERATION_SPEC_V2_1 = "https://specs.apollo.dev/federation/v2.1";

public static final String FEDERATION_SPEC_V2_2 = "https://specs.apollo.dev/federation/v2.2";

public static final String FEDERATION_SPEC_V2_3 = "https://specs.apollo.dev/federation/v2.3";
public static final String FEDERATION_SPEC_V2_4 = "https://specs.apollo.dev/federation/v2.4";
public static final String FEDERATION_SPEC_V2_5 = "https://specs.apollo.dev/federation/v2.5";

private static final SchemaGenerator.Options generatorOptions =
SchemaGenerator.Options.defaultOptions();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@
import static com.apollographql.federation.graphqljava.Federation.FEDERATION_SPEC_V2_1;
import static com.apollographql.federation.graphqljava.Federation.FEDERATION_SPEC_V2_2;
import static com.apollographql.federation.graphqljava.Federation.FEDERATION_SPEC_V2_3;
import static com.apollographql.federation.graphqljava.Federation.FEDERATION_SPEC_V2_4;
import static com.apollographql.federation.graphqljava.Federation.FEDERATION_SPEC_V2_5;
import static graphql.introspection.Introspection.DirectiveLocation.FIELD_DEFINITION;
import static graphql.introspection.Introspection.DirectiveLocation.INTERFACE;
import static graphql.introspection.Introspection.DirectiveLocation.OBJECT;
Expand All @@ -15,7 +17,13 @@

import com.apollographql.federation.graphqljava.exceptions.UnsupportedFederationVersionException;
import graphql.PublicApi;
import graphql.language.*;
import graphql.language.DirectiveDefinition;
import graphql.language.DirectiveLocation;
import graphql.language.Document;
import graphql.language.InputValueDefinition;
import graphql.language.NonNullType;
import graphql.language.SDLNamedDefinition;
import graphql.language.TypeName;
import graphql.parser.Parser;
import graphql.schema.GraphQLArgument;
import graphql.schema.GraphQLDirective;
Expand All @@ -25,7 +33,12 @@
import java.io.InputStream;
import java.io.InputStreamReader;
import java.nio.charset.StandardCharsets;
import java.util.*;
import java.util.Arrays;
import java.util.Collections;
import java.util.Comparator;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Set;
import java.util.stream.Collectors;
import java.util.stream.Stream;

Expand Down Expand Up @@ -206,7 +219,10 @@ public static List<SDLNamedDefinition> loadFederationSpecDefinitions(String fede
case FEDERATION_SPEC_V2_2:
return loadFed2Definitions("definitions_fed2_2.graphqls");
case FEDERATION_SPEC_V2_3:
case FEDERATION_SPEC_V2_4:
return loadFed2Definitions("definitions_fed2_3.graphqls");
case FEDERATION_SPEC_V2_5:
return loadFed2Definitions("definitions_fed2_5.graphqls");
default:
throw new UnsupportedFederationVersionException(federationSpec);
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,83 @@
#
# https://specs.apollo.dev/federation/v2.0/federation-v2.0.graphql
#

directive @key(fields: FieldSet!, resolvable: Boolean = true) repeatable on OBJECT | INTERFACE
directive @requires(fields: FieldSet!) on FIELD_DEFINITION
directive @provides(fields: FieldSet!) on FIELD_DEFINITION
directive @external on OBJECT | FIELD_DEFINITION
directive @extends on OBJECT | INTERFACE
directive @override(from: String!) on FIELD_DEFINITION
directive @inaccessible on
| FIELD_DEFINITION
| OBJECT
| INTERFACE
| UNION
| ENUM
| ENUM_VALUE
| SCALAR
| INPUT_OBJECT
| INPUT_FIELD_DEFINITION
| ARGUMENT_DEFINITION
directive @tag(name: String!) repeatable on
| FIELD_DEFINITION
| INTERFACE
| OBJECT
| UNION
| ARGUMENT_DEFINITION
| SCALAR
| ENUM
| ENUM_VALUE
| INPUT_OBJECT
| INPUT_FIELD_DEFINITION
scalar FieldSet

#
# https://specs.apollo.dev/link/v1.0/link-v1.0.graphql
#

directive @link(
url: String!,
as: String,
import: [Import])
repeatable on SCHEMA

scalar Import

#
# federation-v2.1
#

directive @composeDirective(name: String!) repeatable on SCHEMA

#
# federation-v2.2
#

directive @shareable repeatable on FIELD_DEFINITION | OBJECT

#
# federation-v2.3
#

directive @interfaceObject on OBJECT

#
# federation-v2.5
#

directive @authenticated on
ENUM
| FIELD_DEFINITION
| INTERFACE
| OBJECT
| SCALAR

directive @requiresScopes(scopes: [[Scope!]!]!) on
ENUM
| FIELD_DEFINITION
| INTERFACE
| OBJECT
| SCALAR

scalar Scope
Original file line number Diff line number Diff line change
Expand Up @@ -299,6 +299,16 @@ public void verifyFederationV2Transformation_nonResolvableKey_doesNotRequireReso
FederatedSchemaVerifier.verifyServiceSDL(federatedSchema, expectedFederatedSchemaSDL);
}

@Test
public void verifyFederationV2Transformation_authorization() {
verifyFederationTransformation("schemas/authorization.graphql", true);
}

@Test
public void verifyFederationV2Transformation_customAuthenticated() {
verifyFederationTransformation("schemas/customAuthenticated.graphql", true);
}

private GraphQLSchema verifyFederationTransformation(
String schemaFileName, boolean isFederationV2) {
final RuntimeWiring runtimeWiring = RuntimeWiring.newRuntimeWiring().build();
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
extend schema @link(url: "https://specs.apollo.dev/federation/v2.5", import: ["@authenticated", "@key", "@requiresScopes", "Scope", "FieldSet"])

type Product @key(fields: "id") {
id: ID!
name: String!
supplier: String @requiresScopes(scopes: [["scopeA"]])
}

type Query {
product(id: ID!): Product @authenticated
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
schema @link(import : ["@authenticated", "@key", "@requiresScopes", "Scope", "FieldSet"], url : "https://specs.apollo.dev/federation/v2.5"){
query: Query
}

directive @authenticated on SCALAR | OBJECT | FIELD_DEFINITION | INTERFACE | ENUM

directive @federation__composeDirective(name: String!) repeatable on SCHEMA

directive @federation__extends on OBJECT | INTERFACE

directive @federation__external on OBJECT | FIELD_DEFINITION

directive @federation__interfaceObject on OBJECT

directive @federation__override(from: String!) on FIELD_DEFINITION

directive @federation__provides(fields: FieldSet!) on FIELD_DEFINITION

directive @federation__requires(fields: FieldSet!) on FIELD_DEFINITION

directive @federation__shareable repeatable on OBJECT | FIELD_DEFINITION

directive @inaccessible on SCALAR | OBJECT | FIELD_DEFINITION | ARGUMENT_DEFINITION | INTERFACE | UNION | ENUM | ENUM_VALUE | INPUT_OBJECT | INPUT_FIELD_DEFINITION

directive @key(fields: FieldSet!, resolvable: Boolean = true) repeatable on OBJECT | INTERFACE

directive @link(as: String, import: [link__Import], url: String!) repeatable on SCHEMA

directive @requiresScopes(scopes: [[Scope!]!]!) on SCALAR | OBJECT | FIELD_DEFINITION | INTERFACE | ENUM

directive @tag(name: String!) repeatable on SCALAR | OBJECT | FIELD_DEFINITION | ARGUMENT_DEFINITION | INTERFACE | UNION | ENUM | ENUM_VALUE | INPUT_OBJECT | INPUT_FIELD_DEFINITION

union _Entity = Product

type Product @key(fields : "id", resolvable : true) {
id: ID!
name: String!
supplier: String @requiresScopes(scopes : [["scopeA"]])
}

type Query {
_entities(representations: [_Any!]!): [_Entity]!
_service: _Service!
product(id: ID!): Product @authenticated
}

type _Service {
sdl: String!
}

scalar FieldSet

scalar Scope

scalar _Any

scalar link__Import
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
extend schema @link(url: "https://specs.apollo.dev/federation/v2.5", import: ["@key"])

directive @authenticated(role: [String!]!) on FIELD_DEFINITION

type Product @key(fields: "id") {
id: ID!
name: String!
supplier: String @authenticated(role: ["manager"])
}

type Query {
product(id: ID!): Product
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
schema @link(import : ["@key"], url : "https://specs.apollo.dev/federation/v2.5"){
query: Query
}

directive @authenticated(role: [String!]!) on FIELD_DEFINITION

directive @federation__authenticated on SCALAR | OBJECT | FIELD_DEFINITION | INTERFACE | ENUM

directive @federation__composeDirective(name: String!) repeatable on SCHEMA

directive @federation__extends on OBJECT | INTERFACE

directive @federation__external on OBJECT | FIELD_DEFINITION

directive @federation__interfaceObject on OBJECT

directive @federation__override(from: String!) on FIELD_DEFINITION

directive @federation__provides(fields: federation__FieldSet!) on FIELD_DEFINITION

directive @federation__requires(fields: federation__FieldSet!) on FIELD_DEFINITION

directive @federation__requiresScopes(scopes: [[federation__Scope!]!]!) on SCALAR | OBJECT | FIELD_DEFINITION | INTERFACE | ENUM

directive @federation__shareable repeatable on OBJECT | FIELD_DEFINITION

directive @inaccessible on SCALAR | OBJECT | FIELD_DEFINITION | ARGUMENT_DEFINITION | INTERFACE | UNION | ENUM | ENUM_VALUE | INPUT_OBJECT | INPUT_FIELD_DEFINITION

directive @key(fields: federation__FieldSet!, resolvable: Boolean = true) repeatable on OBJECT | INTERFACE

directive @link(as: String, import: [link__Import], url: String!) repeatable on SCHEMA

directive @tag(name: String!) repeatable on SCALAR | OBJECT | FIELD_DEFINITION | ARGUMENT_DEFINITION | INTERFACE | UNION | ENUM | ENUM_VALUE | INPUT_OBJECT | INPUT_FIELD_DEFINITION

union _Entity = Product

type Product @key(fields : "id", resolvable : true) {
id: ID!
name: String!
supplier: String @authenticated(role : ["manager"])
}

type Query {
_entities(representations: [_Any!]!): [_Entity]!
_service: _Service!
product(id: ID!): Product
}

type _Service {
sdl: String!
}

scalar _Any

scalar federation__FieldSet

scalar federation__Scope

scalar link__Import

0 comments on commit 0ad2582

Please sign in to comment.