Skip to content

Commit

Permalink
fix: fixed logic that checked whether new federation features are sup…
Browse files Browse the repository at this point in the history
…ported

Old logic was just comparing imported spec to a specific version instead of a range.
  • Loading branch information
dariuszkuc committed Feb 15, 2023
1 parent 743f004 commit 3bf838e
Show file tree
Hide file tree
Showing 4 changed files with 123 additions and 25 deletions.
Original file line number Diff line number Diff line change
@@ -1,10 +1,9 @@
package com.apollographql.federation.graphqljava.directives;

import static com.apollographql.federation.graphqljava.Federation.FEDERATION_SPEC_V2_1;
import static com.apollographql.federation.graphqljava.Federation.FEDERATION_SPEC_V2_3;
import static com.apollographql.federation.graphqljava.FederationDirectives.loadFederationSpecDefinitions;

import com.apollographql.federation.graphqljava.exceptions.MultipleFederationLinksException;
import com.apollographql.federation.graphqljava.exceptions.UnsupportedFederationVersionException;
import com.apollographql.federation.graphqljava.exceptions.UnsupportedLinkImportException;
import graphql.language.Argument;
import graphql.language.ArrayValue;
Expand Down Expand Up @@ -67,14 +66,13 @@ private static Stream<SDLNamedDefinition> loadDefinitions(Directive linkDirectiv

final Argument urlArgument = linkDirective.getArgument("url");
final String specLink = ((StringValue) urlArgument.getValue()).getValue();
final boolean allowComposeableDirective = FEDERATION_SPEC_V2_1.equals(specLink);
final boolean allowInterfaceObjectDirective = FEDERATION_SPEC_V2_3.equals(specLink);

if (!allowComposeableDirective && imports.containsKey("@composeDirective")) {
final int federationVersion = parseFederationVersion(specLink);
if (imports.containsKey("@composeDirective") && !isComposeDirectiveSupported(federationVersion)) {
throw new UnsupportedLinkImportException("@composeDirective");
}

if (!allowInterfaceObjectDirective && imports.containsKey("@interfaceObject")) {
if (imports.containsKey("@interfaceObject") && !isInterfaceObjectSupported(federationVersion)) {
throw new UnsupportedLinkImportException("@interfaceObject");
}

Expand All @@ -86,6 +84,22 @@ private static Stream<SDLNamedDefinition> loadDefinitions(Directive linkDirectiv
.transform(definition, new LinkImportsRenamingVisitor(imports)));
}

private static int parseFederationVersion(String specLink) {
final String versionString = specLink.substring(specLink.length() - 3);
try {
return Math.round(Float.parseFloat(versionString) * 10);
} catch (Exception e) {
throw new UnsupportedFederationVersionException(specLink);
}
}
private static boolean isComposeDirectiveSupported(int federationVersion) {
return federationVersion >= 21;
}

private static boolean isInterfaceObjectSupported(int federationVersion) {
return federationVersion >= 23;
}

private static Stream<Directive> getFederationLinkDirectives(SchemaDefinition schemaDefinition) {
return schemaDefinition.getDirectives("link").stream()
.filter(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,4 +5,8 @@ public class UnsupportedFederationVersionException extends RuntimeException {
public UnsupportedFederationVersionException(String federationSpec) {
super("Specified federation spec = " + federationSpec + " is currently not supported");
}

public UnsupportedFederationVersionException(String federationSpec, Exception e) {
super("Specified federation spec = " + federationSpec + " is currently not supported", e);
}
}
Original file line number Diff line number Diff line change
@@ -1,33 +1,81 @@
extend schema
@link(url: "https://specs.apollo.dev/federation/v2.0",
import: ["@key", "@shareable", "@provides", "@external", "@tag", "@extends", "@override", "@inaccessible"])
@link(
url: "https://specs.apollo.dev/federation/v2.3"
import: [
"@composeDirective"
"@extends"
"@external"
"@key"
"@inaccessible"
"@interfaceObject"
"@override"
"@provides"
"@requires"
"@shareable"
"@tag"
]
)
@link(url: "https://myspecs.dev/myCustomDirective/v1.0", import: ["@custom"])
@composeDirective(name: "@custom")

type Product @key(fields: "id") @key(fields: "sku package") @key(fields: "sku variation { id }") {
directive @custom on OBJECT

type Product
@custom
@key(fields: "id")
@key(fields: "sku package")
@key(fields: "sku variation { id }") {
id: ID!
sku: String
package: String
variation: ProductVariation
dimensions: ProductDimension
createdBy: User @provides(fields: "totalProductsCreated")
notes: String @tag(name: "internal")
research: [ProductResearch!]!
}

type DeprecatedProduct @key(fields: "sku package") {
sku: String!
package: String!
reason: String
createdBy: User
}

type ProductVariation {
id: ID!
id: ID!
}

type ProductResearch @key(fields: "study { caseNumber }") {
study: CaseStudy!
outcome: String
}

type CaseStudy {
caseNumber: ID!
description: String
}

type ProductDimension @shareable {
size: String
weight: Float
unit: String @inaccessible
size: String
weight: Float
unit: String @inaccessible
}

type Query {
product(id: ID!): Product
product(id: ID!): Product
deprecatedProduct(sku: String!, package: String!): DeprecatedProduct @deprecated(reason: "Use product query instead")
}

type User @key(fields: "email") {
email: ID!
name: String @shareable @override(from: "users")
totalProductsCreated: Int @external
averageProductsCreatedPerYear: Int @requires(fields: "totalProductsCreated yearsOfEmployment")
email: ID! @external
name: String @override(from: "users")
totalProductsCreated: Int @external
yearsOfEmployment: Int! @external
}

type Inventory @interfaceObject @key(fields: "id") {
id: ID!
deprecatedProducts: [DeprecatedProduct!]!
}
Original file line number Diff line number Diff line change
@@ -1,15 +1,19 @@
schema @link(import : ["@key", "@shareable", "@provides", "@external", "@tag", "@extends", "@override", "@inaccessible"], url : "https://specs.apollo.dev/federation/v2.0"){
schema @composeDirective(name : "@custom") @link(import : ["@composeDirective", "@extends", "@external", "@key", "@inaccessible", "@interfaceObject", "@override", "@provides", "@requires", "@shareable", "@tag"], url : "https://specs.apollo.dev/federation/v2.3") @link(import : ["@custom"], url : "https://myspecs.dev/myCustomDirective/v1.0"){
query: Query
}

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

directive @custom on OBJECT

directive @extends on OBJECT | INTERFACE

directive @external on OBJECT | FIELD_DEFINITION

directive @federation__requires(fields: federation__FieldSet!) on FIELD_DEFINITION

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

directive @interfaceObject on OBJECT

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

directive @link(as: String, import: [link__Import], url: String!) repeatable on SCHEMA
Expand All @@ -18,18 +22,38 @@ directive @override(from: String!) on FIELD_DEFINITION

directive @provides(fields: federation__FieldSet!) on FIELD_DEFINITION

directive @shareable on OBJECT | FIELD_DEFINITION
directive @requires(fields: federation__FieldSet!) on FIELD_DEFINITION

directive @shareable repeatable on OBJECT | FIELD_DEFINITION

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 | User
union _Entity = DeprecatedProduct | Inventory | Product | ProductResearch | User

type CaseStudy {
caseNumber: ID!
description: String
}

type DeprecatedProduct @key(fields : "sku package", resolvable : true) {
createdBy: User
package: String!
reason: String
sku: String!
}

type Product @key(fields : "id", resolvable : true) @key(fields : "sku package", resolvable : true) @key(fields : "sku variation { id }", resolvable : true) {
type Inventory @interfaceObject @key(fields : "id", resolvable : true) {
deprecatedProducts: [DeprecatedProduct!]!
id: ID!
}

type Product @custom @key(fields : "id", resolvable : true) @key(fields : "sku package", resolvable : true) @key(fields : "sku variation { id }", resolvable : true) {
createdBy: User @provides(fields : "totalProductsCreated")
dimensions: ProductDimension
id: ID!
notes: String @tag(name : "internal")
package: String
research: [ProductResearch!]!
sku: String
variation: ProductVariation
}
Expand All @@ -40,20 +64,28 @@ type ProductDimension @shareable {
weight: Float
}

type ProductResearch @key(fields : "study { caseNumber }", resolvable : true) {
outcome: String
study: CaseStudy!
}

type ProductVariation {
id: ID!
}

type Query {
_entities(representations: [_Any!]!): [_Entity]!
_service: _Service!
deprecatedProduct(package: String!, sku: String!): DeprecatedProduct @deprecated(reason : "Use product query instead")
product(id: ID!): Product
}

type User @key(fields : "email", resolvable : true) {
email: ID!
name: String @override(from : "users") @shareable
averageProductsCreatedPerYear: Int @requires(fields : "totalProductsCreated yearsOfEmployment")
email: ID! @external
name: String @override(from : "users")
totalProductsCreated: Int @external
yearsOfEmployment: Int! @external
}

type _Service {
Expand Down

0 comments on commit 3bf838e

Please sign in to comment.