Giter Site home page Giter Site logo

apollographql / federation-jvm Goto Github PK

View Code? Open in Web Editor NEW
235.0 38.0 65.0 994 KB

JVM support for Apollo Federation

Home Page: https://www.apollographql.com/docs/federation/

License: MIT License

Java 99.94% Dockerfile 0.06%
federation graphql java spring-graphql

federation-jvm's Introduction

Continuous Integration MIT License Maven Central Join the community forum Join our Discord server

Apollo Federation on the JVM

Apollo Federation is a powerful, open architecture that helps you create a unified supergraph that combines multiple GraphQL APIs. federation-graphql-java-support provides Apollo Federation support for building subgraphs in the graphql-java ecosystem. Individual subgraphs can be run independently of each other but can also specify relationships to the other subgraphs by using Federated directives. See Apollo Federation documentation for details.

graph BT;
  gateway([Supergraph<br/>gateway]);
  serviceA[Users<br/>subgraph];
  serviceB[Products<br/>subgraph];
  serviceC[Reviews<br/>subgraph];
  gateway --- serviceA & serviceB & serviceC;

Modules

Federation JVM Support

federation-graphql-java-support is built on top of graphql-java and provides transformation logic to make your GraphQL schemas Federation compatible. SchemaTransformer adds common Federation type definitions (e.g. _Any scalar, _Entity union, Federation directives, etc) and allows you to easily specify your Federated entity resolvers.

This project also provides a set of Federation aware instrumentations:

  • CacheControlInstrumentation - instrumentation that computes a max age for an operation based on @cacheControl directives
  • FederatedTracingInstrumentation - instrumentation that generates trace information for federated operations

See module README for details.

Subscription HTTP Callback Support for Spring GraphQL

GraphQL subscriptions enable clients to receive continual, real-time updates whenever new data becomes available. Unlike queries and mutations, subscriptions are long-lasting. This means a client can receive multiple updates from a single subscription.

Spring GraphQL provides out of box support for GraphQL subscriptions over WebSockets using graphql-transport-ws protocol. This library adds support for subscriptions using Apollo HTTP callback protocol.

See Apollo Router for additional details about Federation and Subscription support. See module README for library details.

Contact

If you have a specific question about the library or code, please start a discussion in the Apollo community forums.

Contributing

To get started, please fork the repo and checkout a new branch. You can then build the library locally with Gradle

./gradlew clean build

See more info in CONTRIBUTING.md.

After you have your local branch set up, take a look at our open issues to see where you can contribute.

Security

For more info on how to contact the team for security issues, see our Security Policy.

License

This library is licensed under The MIT License (MIT).

federation-jvm's People

Contributors

aivantsov avatar bod avatar caydie-tran avatar craig-day avatar dariuszkuc avatar dependabot[bot] avatar glasser avatar jsegaran avatar kenshih avatar lanceon avatar lennyburdette avatar martinbonnin avatar mcohen75 avatar pcarrier avatar peakematt avatar renovate[bot] avatar rkudryashov avatar sachindshinde avatar samuelandalon avatar setchy avatar svc-secops avatar thejc avatar tinnou avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

federation-jvm's Issues

More non spring examples + proper docs + bunch of questions

Hi there.
I understand how Spring Boot and Spring itself is beneficial (especially for microservices), but I really want to see pure implementation with 2-3 different federated services written in graphql-java and apollo-server that sticks it all as a whole thing using a gateway.

Another question is, will Apollo Engine work with those graphql-java federated services, or they will be a bunch of black boxes until proper apollo-engine instrumentation is implemented? (I mean resolvers tracing and all advanced, not static schema related stuff)

If they indeed will, is Apollo has any plans to implement some sort of library that will help with instrumentation of graphql-java based GraphQL Servers? It's kind of roadblock for us to start to consider it at Revolut.

(we have all backend in Java/Kotlin and we're moving heavily towards microservices, Node.js is not an option with that amount of JVM devs, and I'm as an evangelist of GQL in the company, want to make sure everything is there for GraphQL in JVM world, to start promoting it heavily or even build a prototype for them, also, Spring is forbidden in our company, that's why I'm looking for Spring agnostic implementation)

Need support for adding `includeDirectives` Predicate in SchemaTrandformers

SPQR generate the following GraphQLDirective as part of the schema printer logic @_mappedType, @_mappedInputField and @_mappedOperation which fails the gateway composition. There need to be support for 'includeDirectives' Predicate, so, we could remove the private directives while generating the SDL. If not, once can not easily integrate the microservices running with SPQR graphQL service via gateway

Repeatable @key directive

Can I use multiple @key directive?

I have schema like:
type SomeType @key(fields: "field1") @key(fields: "field2") {

When I try run application I get following exception:
The directive 'key' MUST be defined as a repeatable directive if its repeated on an SDL element

If I forcely define 'key' directive in my schema like that : directive @key(fields: _FieldSet!) repeatable on OBJECT | INTERFACE I get following:
'key' is a repeatable directive and you have used a non repeatable access method

Could you help me to coup with it?

Extract interfaces meant for integrations to api artifact

Extract HTTPRequestHeaders interface (only API integration interface right now) to its own artifact:

Benefits:

  • Any framework that chooses to implement HTTPRequestHeaders shouldn't be required to pull in the remainder of all the transitive libraries this project depends upon.

  • My company has protobuf as a banned library, and so we are required to shade protobuf and federation-jvm libraries in order to get federated tracing functioning. By separating interfaces to a separate api artifact, we can shade federation-jvm, but leave the HTTPRequestHeaders interface in its original location. This allows other 3rd party libraries we use to continue to implement HTTPRequestHeaders, and allows the optimization logic in inspecting the headers to still function while allowing those 3rd party libraries remain decoupled/insulated from the shading concern.

federation-jvm 0.4.0 | graphql-java 14 is not compatible with graphql-java-tools 5.2.4

Hi Team,
Having issues when using the federation-jvm 0.4.0 which requires graphql-java 14.0. This combination is having issues when when using with graphql-java-tools 5.2.4 which internally uses graphql-java 9.2 version internally.

Is there any updates or springboot example with query resolvers which works with the federation-jvm version 0.4.0

Please check and let me know if any updates.

Thanks,
Raph

Move build system to Gradle?

Any thoughts about moving to Gradle for the build system? One immediate reason for doing so would be to use in-memory GPG keys in CI and automating the release process. It might be doable with Maven too but it looks more complicated (need to store a cryped keystore in the repo or restore it from env variables which both do not sound really fun things to do).

Overall, Gradle looks more flexible and more in-line with tools we use for other projects like apollo-android but maybe there are reasons to stick with Maven?

Null values when levels of nested queries is increased

I have two services : user and review
(The link to the source code )
Query 1

query {
  users {
    userid
    name
    reviews {
      review
    }
  }
}

returns the expected output:

{
  "data": {
    "users": [
      {
        "userid": "1",
        "name": "Cameron Ellis",
        "reviews": [
          {
            "review": "Excellent"
          },
          {
            "review": "Bavo"
          }
        ]
      }
    ]
  }
}

However when i increase the nesting to one more level.
i get nulls in the output

Query 2

query {
  reviews {
    review
    user {
      userid
      name
      reviews {
        review
      }
    }
  }
}

produces as output

{
  "data": {
    "reviews": [
      {
        "review": "Excellent",
        "user": {
          "userid": "1",
          "name": "Cameron Ellis",
          "reviews": null
        }
      },
      {
        "review": "Bavo",
        "user": {
          "userid": "1",
          "name": "Cameron Ellis",
          "reviews": null
        }
      }
    ]
  }
}

How is it that in the first query , i get the reviews of Cameron Ellis but not in the second query.
What am i missing?

The link to the source code

Thanks

Question to the SchemaTransformer logic

Hello, just wonder if you can help me understand the logic behind the following requirement:

If your schema does not contain any types annotated with the @key directive, nothing else is required. You can build a transformed GraphQLSchema with SchemaTransformer#build(), and confirm it exposes query { _schema { sdl } }.

Otherwise, all types annotated with @key will be part of the _Entity union type, and reachable through query { _entities(representations: [Any!]!) { โ€ฆ } }

According to the specification, if I extend another type I have to add @key directive. But in case if I just want to reference another type and I don't extend it with any additional attributes, why do I have to define data fetcher that will never be invoked?

Moreover, I've just looked through the code of SchemaTransformer.java, and it doesn't validate that particular type with directive @key defined has data fetcher, it is enough if there is at least one defined.

This looks like something that need to be changed.

Query on using Gateway

Hi,

Thanks for this project.

Are you using Apollo Gateway for gateway implementation? Does Federation-jvm act as gateway instead apollo gateway?
We want to use Java for Gateway or server implementation instead of using Node. Can you suggest us how to do this?

Thanks,

Clarify status of the project

Hi there,

First thank you for trying to filling the gap between graphql-java & the GraphQL Federation spec. Really appreciate it.

As Federation is going to be more and more popular and some people will want to use it (like me) in a Java project, it would be great to know the current status of this project/lib.

As it seems, the project looks pretty early and maybe not suitable for production use.

It would be interesting to know what you (the creators/maintainers) think of the current version of the lib & what's missing - in short a kind of a roadmap.

Also, can we expect this project to go along ? Are you willing to continue its development further ?

Anyway, thank you for this work and thank you for any insight you can give us so we can plan -or not- any Federated project :)

Quotes within docstrings result in unparsable schema from _Service.sdl

Example:

type Query {
    """Field docstring contains a "quoted" field."""
    fooField: String
}

When queried from sdl becomes

schema {
  query: Query
}

type Query {
 "Field docstring contains a "quoted" field."
  fooField: String
}

The description string should be surrounded in triple quotes as in the original schema.

Having issues with post request with headers to the backend spring boot graphql endpoint

Currently we do have federated jvm spring boot application which takes in couple of headers and does some header validation on federated jvm spring boot application(backend) and based on that it performs some task.

But it constantly throws NPE since the headers are not getting passed to the federated jvm spring boot application(backend), Looking at my curl or postman.

Screen Shot 2020-06-11 at 2 34 58 PM

This is happening only with federated integration other wise with plain graphql implementation the query fetches me data back.

Looked more into details looks like the headers dont get sent back to the federated spring boot implementation.

020-06-11T14:29:51.218Z DEBUG ExecutionStrategy [{}]: 'c09859cf-da90-49ae-873d-c0d3607968ad', field '/getCustomerService' fetch threw exception

java.lang.NullPointerException: null

Can you help on this?

Remove Subscription type and custom directives from printed sdl

This is follow up for the issue I have raised before #35

Some things have changed in library, so I am raising this again.

We are still using custom implementation of federation support as this is not working for us.

apollo-gateway doesn't support Subscriptions and even if it did it probably won't match our transfer mechanism for it, so it should be removed from printed sdl. We still want to support Subscriptions on individual graphql servers, but currently not on gateway.

Similarly printed sdl includes custom directive definitions and directives themselves, but they are not really useful for gateway as well, so they should be removed from printed sdl as well. Similarly you can argue that it might be similar how introspection is not showing where directives are applied, but in this case we still need federation directives to be present on printed sdl.

The new FederationSdlPrinter has a lot of options to control some of these things, but there is no way to pass them as SchemaTransformer just sets them up.

Currently Federation and SchemaTransformer are really restricted where users don't have many options and I would like to change it to allow passing FederationSdlPrinter.Options and include new options to control what schema root types (Query, Subscription, Mutation) needs printed.

I am happy to raise PR for both of the issues, but I just wanted to know what are your thoughts on this before I spend more time on this as my previous issue was just closed.

Update `federation-jvm` to publish to Maven Central

As the first step toward moving off JCenter and onto Maven Central, we need to:

  1. Get a Sonatype Jira account, and get access to the group ID com.apollographql.federation. It seems the standard process for this is to file a Jira issue on the Sonatype OSSRH project board and ask for current devs on that group ID to confirm (Iโ€™ve filed it at https://issues.sonatype.org/browse/OSSRH-65026 and Slackโ€™d Martijn and Martin to confirm).
  2. Merge a PR with the POM file and doc changes needed. That is:
    a. Fix all POMs to ensure they follow the Maven Central requirements.
    a. Add the org.apache.maven.plugins:maven-gpg-plugin Maven plugin and configure to do GPG signing.
    b. Add the org.sonatype.plugins:nexus-staging-maven-plugin Maven plugin to deploy to Maven Central, and remove any Bintray-related POM elements.
    c. Update the RELEASING.md doc to describe the new releasing process.
  3. To test the flow, cut a new release candidate. Weโ€™ll try using that release shortly after in Gravity infra to confirm all is well.
  4. If all is well, cut a release. This unblocks those who don't want to use JCenter.

Remove Subscription type and custom directives from sdl

Hi,

Gateway is freaking out if there is Subscription type or custom directives in sdl response, so they should be filtered out.

I don't have time to create proper PR, but it could look something like this if someone wants to try it.

com.apollographql.federation.graphqljava.SchemaTransformer

public final GraphQLSchema build() throws SchemaProblem {
//... unchanged
final GraphQLSchema sdlSchema = GraphQLSchema.newSchema()
        .query(originalSchema.getQueryType())
        .additionalTypes(originalSchema.getAdditionalTypes())
        .codeRegistry(originalSchema.getCodeRegistry())
        .build(); // create copy of original schema but remove subscription type
        final String sdl = sdl(sdlSchema);
//... unchanged
}
private String sdl(final GraphQLSchema schema) {
    final SchemaPrinter.Options options = SchemaPrinter.Options.defaultOptions()
        .includeScalarTypes(true)
        .includeExtendedScalarTypes(true)
        .includeSchemaDefintion(false)
        .includeDirectives(true);

    // use regex to cut out custom directives from sdl 
    return new SchemaPrinter(options).print(schema)
        .replaceAll("\\s?@\\b(?!(extends|external|key|provides|requires|deprecated)\\b)\\w+(?:\\s*\\(.*?\\))?", "");
  }

Thanks,
Reinis

ClassNotFoundException: graphql.execution.ExecutionPath

try to using FederatedTracingInstrumentation.class from federation-graphql-java-support inside a quarkus 1.10.5.Final microservice that use graphql-java 16.1 (instead of 15.0 version ) these exception is throwed:
Caused by: java.lang.ClassNotFoundException: graphql.execution.ExecutionPath
at java.base/jdk.internal.loader.BuiltinClassLoader.loadClass(BuiltinClassLoader.java:602)
at java.base/jdk.internal.loader.ClassLoaders$AppClassLoader.loadClass(ClassLoaders.java:178)
at java.base/java.lang.ClassLoader.loadClass(ClassLoader.java:522)
at io.quarkus.bootstrap.classloading.QuarkusClassLoader.loadClass(QuarkusClassLoader.java:412)
at io.quarkus.bootstrap.classloading.QuarkusClassLoader.loadClass(QuarkusClassLoader.java:365)
... 24 more

What would a JVM implementation of the Reviews service look like?

Hello,

I'm trying to get a started with federation, our services are generally all in Java. As a start I'm trying to mimic the apollo federation example services from here: https://www.apollographql.com/docs/federation/implementing-services/ but the JVM equivalent.

I'm having a hard time getting my head around it all.

Here's my schema for the Reviews service:

type Review @key(fields: "id") {
  id: ID!
  body: String
  author:String
  product: Product
}

extend type Product @key(fields: "upc") {
  upc: String! @external
  reviews: [Review]
}

And in my code I load that up like so:

  Federation.transform(sdl, runtimeWiring)
      .resolveEntityType(entityTypeResolver)
      .fetchEntities(entityFetcher)
      .build();

But then I get this error:

graphql.schema.idl.errors.SchemaProblem: errors=[The extension 'Product' type [@8:1] is missing its base underlying type]

	at com.apollographql.federation.graphqljava.Federation.transform(Federation.java:31) ~[federation-graphql-java-support-0.5.0.jar:na]
	at com.apollographql.federation.graphqljava.Federation.transform(Federation.java:43) ~[federation-graphql-java-support-0.5.0.jar:na]

And if I leave the extends out. The service will start, but the gateway complains that the type Product is defined twice.

So, now I'm confused as to what should it be then?

Improve API: remove a call

For example, if I have two services planet-service and satellite-service, and in the former one base type Planet is defined:

type Planet @key(fields: "id") {
    id: ID!
    name: String!
}

then it is needed to create transformed schema as follows:

        val transformedGraphQLSchema = Federation.transform(InputStreamReader(schemaResource), createRuntimeWiring())
            // todo remove
            .fetchEntities {}
            .resolveEntityType(planetTypeResolver)
            .build()

I suggest that it would be better to get rid of one call instead of passing a useless lambda to it. Currently, GraphQL API call throws an exception ("Missing a data fetcher for _entities") if fetchEntities has not been called. The problem may appear only in a service that has not defined @extends GraphQL types.

Empty `type Query {\n}` in the sdl response

I encountered this issue when Apollo-Gateway has trouble recognizing sdl from a graphql service served from federation-jvm. The schema is copied from Apollo-Gateway sample for the inventory service, with a little modification by removing the extend keyword in the front and adding @extends directive:

  type Product @key(fields: "upc") @extends {
    upc: String! @external
    weight: Int @external
    price: Int @external
    shippingEstimate: Int @requires(fields: "price weight")
  }

This schema works well in the NodeJs version of graphql federation. However, with the same schema copied to the side of federation-jvm, Apollo-Gateway stopped to recognize the schema. The following message showed in the Apollo-Gateway log:

Encountered error when loading productsExt at http://localhost:9000/graphql: Syntax Error: Expected Name, found }

Looking at the response body of sdl query from federation-jvm, the @extends was pulled in front of @Keyword and there is an empty type Query {\n}.

{
  "data": {
    "_service": {
      "sdl": "type Product @extends @key(fields : \"upc\") {\n  price: Int @external\n  shippingEstimate: Int @requires(fields : \"price weight\")\n  upc: String! @external\n  weight: Int @external\n}\n\ntype Query {\n}\n"
    }
  }
}

The order of the @extends directive seemed to have no negative effect, as verified in by switching the order of @extends and @keyword in the NodeJS graphql federation. The empty type Query {} is probably the cause.

Support for graphql-java version 17.+

Can you please make a release that is compatible with graphql-java/graphql-java version 17.+
There are some breaking changes to the
graphql-java-support/src/main/java/com/apollographql/federation/graphqljava/FederationSdlPrinter.java

because the Directive GraphQLArgument object has changed, but the fixes can be found easily at:

https://github.com/graphql-java/graphql-java/blob/b525ada641bade0ea509669b28cb1920b33a2709/src/main/java/graphql/schema/idl/SchemaPrinter.java

since it appears this is basically a copy of that code to begin with.

The only other thing that failed was bootstrapping some of the spring tests which I did not spend the time to decode.

The new version of graphql has performance improvements that seem worthwhile. I am using Netflix DGS framework and in order for them to upgrade, there has to be a version of this library working with graphql-java V 17.+

Is there a hook to customize HTTP requests to downstream Gql servers?

We have a use case for which we need to pass a structured context object downstream to multiple services.
This context object cannot really be part of the graphql schema itself, because it is added by a middleware internally. It is not part of the "public" graphql schema.
Also, it is a bit too big to be passed in the headers, and it is structured, so not really a good fit either.

Is there a way to customize the HTTP requests that are sent between the federation gateway and the underlying GraphQL servers?

Something like that seems doable with the NodeJS Apollo Gateway:
https://www.apollographql.com/docs/apollo-server/federation/gateway/#customizing-requests-and-responses

Need an alternative integration strategy to signal FederatedTracingInstrumentation whether trace should be done for request

We need another strategy for integration of the federated tracing to provide various frameworks (and consumers of them) more flexibility in implementing the short-circuiting path when the header is not present. This will make it so implementors are not forced to have their context implement HTTPRequestHeaders and provides much more flexibility with integrations of FederatedTracing and frameworks like DGS and others, that do not implement HTTPRequestHeaders in their execution context objects.

Today, without this capability, upon integrating FederatedTracingInstrumentation into base graphql libraries that are used by services throughout my company that are using other frameworks (ie: DGS), the execution context objects do not implement the HTTPRequestHeaders interface. This results in their services being forced into an "tracing enabled all the time" stance or disable federated tracing altogether - consuming teams do not have the appetite to always have that enabled for every request and would like the headers to be honored. Our team supporting federation want the tracing enabled in all services that participate in federation. With this change, we have many more options at our disposal for getting it working across many more codebases.

ApolloGateway doesn't recognize federation-jvm hosted graphql

_query { _service { sdl } } on federation-jvm gives

{
  "errors": [],
  "data": {
    "_service": {
      "sdl": "schema {\n  query: Query\n}\n\ntype Query {\n  me: User\n}\n\ntype User @key(fields : \"id\") {\n  id: ID!\n  name: String\n  username: String\n}\n"
    }
  },
  "extensions": null,
  "dataPresent": true
}

while ApolloGateway is expecting a different format with the same schema.grapql file:

{
  "data": {
    "_service": {
      "sdl": "extend type Query {\n  me: User\n}\n\ntype User @key(fields: \"id\") {\n  id: ID!\n  name: String\n  username: String\n}\n"
    }
  }
}

https://github.com/apollographql/federation-demo/blob/master/services/accounts/index.js#L4

ApolloGateway doesn't like the empty Array in .errors. ApolloGateway considers this as not having a schema, and it also complains when having schema {\n query: Query\n}\n\n in sdl.

Once the sdl string is patched and the empty array .errors is removed, the Graphql schema defined in federation-jvm starts to appears on ApolloGateway.

snippet to patch the sql string:

final String modified = sdl.replace("schema {\n  query: Query\n}\n\n", "");
_service.put("sdl", modified);

Async entity DataFetcher

Hi,

Is the following error expected? If it is, what changes are needed in order to be supported?

@Override
    public Object get(DataFetchingEnvironment env) {
        return env.<List<Map<String, Object>>>getArgument(_Entity.argumentName)
                .stream()
                .map(values -> {
                    if ("User".equals(values.get("__typename"))) {
                        final Object id = values.get("id");
                        if (id instanceof String) {
                            GraphQLContext context = env.getContext();
                            context.getCtx().put("__typename", "User");
                            return this.queryResolver.getUserById((String) id); // this returns a completable future
                                                                               // returning mono/flux with appropriate wrappers is the same
                        }
                    }
                    return null;
                })
                .collect(toList());
    }

the type resolver:

@Override
    public GraphQLObjectType getType(TypeResolutionEnvironment env) {
        final GraphQLContext context = env.getContext();

        String typename = (String) context.getCtx().get("__typename");

        return Optional.ofNullable(typename)
                .map(s -> env.getSchema().getObjectType(typename))
                .orElse(null);
    }

The following error is present when making the query :

query { _entities(representations: [{__typename: \"User\", id: \"a\"}]) {... on User {id name}}}

{"errors":[{"message":"Exception while fetching data (/_entities[0]/id) : Expected source object to be an instance of 'com.hsbc.federation.users.api.domain.User' but instead got 'java.util.concurrent.CompletableFuture'","locations":[{"line":1,"column":83}],"path":["_entities",0,"id"],"extensions":{"classification":"DataFetchingException"}},{"message":"Exception while fetching data (/_entities[0]/name) : Expected source object to be an instance of 'com.hsbc.federation.users.api.domain.User' but instead got 'java.util.concurrent.CompletableFuture'","locations":[{"line":1,"column":86}],"path":["_entities",0,"name"],"extensions":{"classification":"DataFetchingException"}}],"data":{"_entities":[null]}}

If I change the return type to a concrete instance of User, everything works as expected.

Add query to the spring example InventorySchemaProvider

We are finding the spring example quite useful. Would you be able to add an example of how to add queries (i.e. to fetch topProducts) to the InventorySchemaProvider ?

I have tried adding the following to the schema:
{
type Query {
topProducts: [Product]
}
}

And then adding a Resolver class:
@component
public class TopProductResolver implements GraphQLQueryResolver {
public List topProducts() {
List productList = new ArrayList();
productList.add(new Product("4e495531-3909-47e4-8846-3315744d1ffa", 77));
return productList;
}
}

But the resolver is never invoked and instead I get null in the response.

Thanks,

Edu

Printed SDL returned by the service capabilities query should not return schema element

The Federation Specification appears to indicate that the printed SDL returned by the _service query should not return the root schema element with root operation definitions.

https://www.apollographql.com/docs/apollo-server/federation/federation-spec/#fetch-service-capabilities

The current implementation adds the schema node with root operations defined. The gateway fails to create a merged schema with the following error:

(node:63550) UnhandledPromiseRejectionWarning: GraphQLSchemaValidationError: Query root type must be provided.
    at ApolloGateway.createSchema (.../node_modules/@apollo/gateway/dist/index.js:92:19)
    at ApolloGateway.<anonymous> (.../node_modules/@apollo/gateway/dist/index.js:81:22)
    at Generator.next (<anonymous>)
    at fulfilled (.../node_modules/@apollo/gateway/dist/index.js:4:58)
    at <anonymous>
    at process._tickCallback (internal/process/next_tick.js:189:7)

apollo/gateway: 0.6.5
apollo-server: 2.6.2

Getting Argument value on a child resolver.

The method dataFetchingEnvironment.getArgument(name) always gives values, regardless whether the value is hardcoded in the query, or passed through a graphql variable reference.

However, to get the argument value on a child resolver, it may return a VariableReference object than a plain value (in class IntValue for example). The .name field on the VariableReference can be used as a key on dataFetchingEnvironment.variables to get the value. This snippet roughly demonstrates the process:

    private Integer get(DataFetchingEnvironment dataFetchingEnvironment) {
        List<Selection> selections = dataFetchingEnvironment.getField().getSelectionSet().getSelections();
        VariableReference ref = (VariableReference) ((Field)selections.get(0)).getArguments().get(0).getValue();
        return (Integer) dataFetchingEnvironment.getVariables().get(ref.getName());

So I have been using custom logic to get argument value from the resolver at the child nodes. But we prefer to use something provided by the library so that the dataFetchingEnvironment.variables is transparent to the developers.

Is there a simpler way provided by the library that already achieves this?

Apollo Gateway error "Type Query must define one or more fields."

Hi,

Possible duplicate of issue 2

Using the spring-example provided by federation-jvm available here to run a federated query through Apollo Federation Demo, is failing with the following error:

Encountered error when loading policy at http://localhost:9001/graphql: request to http://localhost:9001/graphql failed, reason: connect ECONNREFUSED 127.0.0.1:9001
(node:32190) UnhandledPromiseRejectionWarning: GraphQLSchemaValidationError: Type Query must define one or more fields.
    at ApolloGateway.createSchema (/Users/Shared/workspace/graphql-apollo/node_modules/@apollo/gateway/dist/index.js:105:19)
    at ApolloGateway.<anonymous> (/Users/Shared/workspace/graphql-apollo/node_modules/@apollo/gateway/dist/index.js:95:32)

Spring JVM Service

Some changes were made to the base federation-jvm Spring example to provide a query to return static data - summary of these changes:

policy.graphql

type Query {
    getPolicy: Policy!
}

type Policy {
    policyNumber: String!
    isInForce: Boolean
}

SchemaProvider

@Component
public class PolicySchemaProvider extends DefaultGraphQLSchemaProvider implements GraphQLSchemaProvider {
    public PolicySchemaProvider(@Value("classpath:schemas/policy.graphql") Resource sdl) throws IOException {
        super(Federation.transform(sdl.getFile(), PolicyQueries.getRuntimeWiring())
                .build());
    }
}

RuntimeWiring

public static RuntimeWiring getRuntimeWiring(){
    return RuntimeWiring.newRuntimeWiring()
            .type("Query", builder -> builder.dataFetcher("getPolicy", new StaticDataFetcher(Policy.builder().policyNumber("123456").isInForce(true).build())))
            .build();
}

Running query { _service { sdl } } produces the following output:

{
    "data": {
        "_service": {
            "sdl": "type Policy {\n  isInForce: Boolean\n  policyNumber: String!\n}\n\ntype Query {\n  getPolicy: Policy!\n}\n"
        }
    }
}

Running the following query directly against the Spring service produces desired result:

query{ 
    getPolicy { policyNumber isInForce }
}

and result:

{
    "data": {
        "getPolicy": {
            "policyNumber": "123456",
            "isInForce": true
        }
    }
}

Gateway

gateway.js

Updated to point to the federation-jvm service at http://localhost:9001/graphql

const { ApolloServer } = require("apollo-server");
const { ApolloGateway } = require("@apollo/gateway");

const gateway = new ApolloGateway({
  serviceList: [
    { name: "policy", url: "http://localhost:9001/graphql" }
  ]
});

(async () => {
  const { schema, executor } = await gateway.load();

  const server = new ApolloServer({ schema, executor });

  server.listen().then(({ url }) => {
    console.log(`๐Ÿš€ Server ready at ${url}`);
  });
})();

Produces the error: GraphQLSchemaValidationError: Type Query must define one or more fields.

Note that when the policy service is added to the others present in the demo, we do not get the error but the federated schema does not include the query.

const gateway = new ApolloGateway({
  serviceList: [
    { name: "accounts", url: "http://localhost:4001/graphql" },
    { name: "reviews", url: "http://localhost:4002/graphql" },
    { name: "products", url: "http://localhost:4003/graphql" },
    { name: "inventory", url: "http://localhost:4004/graphql" },
    { name: "policy", url: "http://localhost:9001/graphql" }
  ]
});

Federated schema only includes the query from the products and accounts service, not the federated-jvm policy service (getPolicy).

directive @key(fields: String!) on OBJECT | INTERFACE
directive @extends on OBJECT
directive @external on OBJECT | FIELD_DEFINITION
directive @requires(fields: String!) on FIELD_DEFINITION
directive @provides(fields: String!) on FIELD_DEFINITION
type Policy {
  isInForce: Boolean
  policyNumber: String!
}

type Product {
  upc: String!
  name: String
  price: Int
  weight: Int
  reviews: [Review]
  inStock: Boolean
  shippingEstimate: Int
}

type Query {
  me: User
  topProducts(first: Int = 5): [Product]
}

type Review {
  id: ID!
  body: String
  author: User
  product: Product
}

type User {
  id: ID!
  name: String
  username: String
  reviews: [Review]
}

Gateway implementation

Hey, I love this project. I was wondering if there is a plan for a java gateway implementation, I understand this is the federation service project.

_Any scalar not handling variable correctly

query($arg1: String!, $arg2: String!, $arg3: String!){
  _entities(representations: [{
    __typename: "SomeEntity",
    arg1: $arg1, 
    arg2: $arg2, 
    arg3: $arg3
  }]) {
    ... on SomeEntity {
      someField
    }
  }
}

fails on this line Looks like it's missing the case of VariableReference.

It's minor because gateway doesn't send this kind of query, but it can happen on manual testing and unit tests.

Federation#ensureQueryTypeExists seems to not account for fields added through `extends`

I have a bunch of services that use a base library to get their basics going, and in the base library is where I combine all the schemas (some services have many). This is working fine, however after calling Federation#transform(final TypeDefinitionRegistry typeRegistry, final RuntimeWiring runtimeWiring) the Query type comes back empty. I tracked it down to Federation#ensureQueryTypeExists returning true and thus queryTypeShouldBeEmpty gets set to true.

It seems that the method doesn't account for fields added through extends, so for now I'm adding a randomly generated field to the Query type before calling transform. This fixes the problem, but it'd be better to not have to do that.

Provide some sensible defaults that can be activated through a simple mechanism (eg: inheritance)

Creating a separate ticket to follow through with the ideas raised as part of PR #103 around providing some automation / sensible defaults that would allow Java Graph Builders to more easily add the necessary federation queries and resolvers.

Some related material:

Below is a copy of the original comments

@setchy Thanks for the suggestions! I do think it would be nice for users to:

  • Have more automation around defining _entities fetchers and _Entity type resolvers.
  • Have some sane defaults they can activate through some simple mechanisms (e.g. inheritance).

With regards to your specific suggestions:

  • could the string references for any federated types (ie: "Product") be replaced by something powered by parsing any SDL types with the @extends directive
  • could the string references for key fields (ie: "upc") be replaced by something powered by parsing any SDL types with the @key(fields: "...") directive

For some clarifying context, a GraphQL type is an entity type if it has an @key directive, or if it implements an interface with an @key directive. Since we're given the GraphQLSchema in Federation#transform(), we can indeed use this to determine:

  • The GraphQL type names for all entity types.
  • The possible key fields for each entity type.
    • Note that an entity type can have multiple @key usages, and that each @key usage may specify multiple fields (additionally with nesting).
    • Note that a federation reference passed to the _entities resolver may additionally contain extra fields as needed by @requires.
    • Parsing/validating fields is going to be a bit trickier here, as the grammar for fields is basically a SelectionSet without braces with some additional limitations/validation rules, and I haven't looked into how difficult it would be for graphql-java to parse this particular symbol with a separate set of validation rules.

For the switch statements in the _entities fetcher and the _Entity type resolver, we could automate that if we let users register wirings for each entity type, e.g. looking like

public interface EntityWiring {
    // The GraphQL type name of the entity.
    public String getName();

    // Resolves a federation reference to an object representing the entity type.
    public Object resolveReference(Map<String, Object> reference);

    // Determines whether an object represents this specific entity type.
    public boolean isType(Object object);
}

We could then validate that they provide wirings for every entity type present in the schema. We could probably infer some of these automatically if the user's using a library like graphql-java-annotations where we have a well-defined correspondence between Java types and GraphQL types.

Regarding the automation of parsing of the federation reference, we could eliminate at least some boilerplate and provide type safety using something similar to JSON object mapping, where the user provides POJO classes and we validate them against the parsed @key and @requires fields. Their resolveReference() function could then take in their own class instead of having to walk Map<String, Object>. (If we can leverage code-generation tools, we could additionally generate the POJO classes for the user.)

  • could their be a base class that provides a default implementation of the resolveReference which each type object could extend from. In most cases it would be creating a new object, setting the @external fields with the matching @key fields and returning that new object. The type or field level resolvers could then add the necessary service layer call to populate the additional data (ie: "quantity", "inStock")

I'm not sure if we could do this exactly as written, as resolveReference() would be static and statics can't be overridden via inheritance. This also becomes trickier for the cases of multiple @keys, nested fields in @keys, and @requires fields. A potential solution that comes to mind is one where the user provides constructors that take in their POJO class, and we use reflection or annotations to detect those constructors to set up resolveReference() in an EntityWiring.

Circling back to this PR, while I believe it would be nice to provide automation and reduce boilerplate, I wouldn't say it's in scope for this PR. If you're interested in contributing to federation-jvm, you can file a GitHub issue referencing this thread and we can work through developing a more concrete proposal. ๐Ÿ™‚

Originally posted by @sachindshinde in #103 (comment)

Make release notes discoverable for Renovate

We use Renovate Bot to keep our application dependencies up-to-date. Overnight our bot detected the new 0.6.4 release, but it wasn't able to pull in any detailed release notes.

My understanding is renovate can pull from a few places, one being a CHANGELOG.md at the root of the repository. apollo-server is an example repository which correctly pulls in the change / release notes from its CHANGELOG.md

I raised a similar ticket a few weeks back for the federation libraries - see apollographql/federation#636.

Federation-jvm is Gateway?

Hi Team,
Need your advise urgently. I need to use dgs-federation example (pls refer below url), but i need to java based gateway (not the apollo gateway which is node based)
https://github.com/Netflix/dgs-federation-example

Is your Federation-jvm acts as Gateway? Can i replace in the above dgs example in place of apollo gateway?

Appreciate your help.

Schema transformation fails when two schemas that have a type with the same name are transformed

When two schemas are transformed that have a type with the same name, schema transformation fails with an error indicating that duplicate types are not allowed.

graphql.AssertException: All types within a GraphQL schema must have unique names. No two provided types may have the same name.
No provided type may have a name which conflicts with any built in types (including Scalar and Introspection types).
You have redefined the type 'SomeType' from being a 'GraphQLObjectType' to a 'GraphQLObjectType'

This appears to be caused by the fact that there is just one instance of the _Entity type definition. I'm not sure of the precise mechanism, but somewhere along the way graphql-java appears to be modifying types and/or definition instances in such a way that retains references from prior schemas.

To avoid this I think it may be best to always create new type and definition instances when transforming schemas.

With a schema like:

type Query {
}

type SomeType @key(fields: "theKey") {
  theKey: ID!
  field1: String!
  field2: Int!
}

The following test reproduces the problem:

@Test
void testSchemaTransformationIsolated() {

    final GraphQLSchema schema = SchemaUtils.buildSchema(TestUtils.readResource("isolated.graphql"));

    final GraphQLSchema federatedSchema1 = Federation.transform(schema)
            .resolveEntityType(env -> null)
            .fetchEntities(environment -> null)
            .build();

    final GraphQLSchema schema2 = SchemaUtils.buildSchema(TestUtils.readResource("isolated.graphql"));

    final GraphQLSchema federatedSchema2 = Federation.transform(schema2)
            .resolveEntityType(env -> null)
            .fetchEntities(environment -> null)
            .build();
}

The failure:

graphql.AssertException: All types within a GraphQL schema must have unique names. No two provided types may have the same name.
No provided type may have a name which conflicts with any built in types (including Scalar and Introspection types).
You have redefined the type 'SomeType' from being a 'GraphQLObjectType' to a 'GraphQLObjectType'

	at graphql.schema.GraphQLTypeCollectingVisitor.assertTypeUniqueness(GraphQLTypeCollectingVisitor.java:105)
	at graphql.schema.GraphQLTypeCollectingVisitor.visitGraphQLObjectType(GraphQLTypeCollectingVisitor.java:38)
	at graphql.schema.GraphQLObjectType.accept(GraphQLObjectType.java:163)
	at graphql.schema.TypeTraverser$TraverserDelegateVisitor.enter(TypeTraverser.java:71)
	at graphql.util.Traverser.traverse(Traverser.java:144)
	at graphql.schema.TypeTraverser.doTraverse(TypeTraverser.java:58)
	at graphql.schema.TypeTraverser.depthFirst(TypeTraverser.java:50)
	at graphql.schema.TypeTraverser.depthFirst(TypeTraverser.java:38)
	at graphql.schema.SchemaUtil.allTypes(SchemaUtil.java:42)
	at graphql.schema.GraphQLSchema.<init>(GraphQLSchema.java:103)
	at graphql.schema.GraphQLSchema.<init>(GraphQLSchema.java:36)
	at graphql.schema.GraphQLSchema$Builder.build(GraphQLSchema.java:412)
	at com.apollographql.federation.graphqljava.SchemaTransformer.build(SchemaTransformer.java:119)
	at com.apollographql.federation.graphqljava.FederationTest.testSchemaTransformationIsolated(FederationTest.java:104)
	at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
	at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
	at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
	at java.lang.reflect.Method.invoke(Method.java:498)
	at org.junit.platform.commons.util.ReflectionUtils.invokeMethod(ReflectionUtils.java:628)
	at org.junit.jupiter.engine.execution.ExecutableInvoker.invoke(ExecutableInvoker.java:117)
	at org.junit.jupiter.engine.descriptor.TestMethodTestDescriptor.lambda$invokeTestMethod$7(TestMethodTestDescriptor.java:184)
	at org.junit.platform.engine.support.hierarchical.ThrowableCollector.execute(ThrowableCollector.java:73)
	at org.junit.jupiter.engine.descriptor.TestMethodTestDescriptor.invokeTestMethod(TestMethodTestDescriptor.java:180)
	at org.junit.jupiter.engine.descriptor.TestMethodTestDescriptor.execute(TestMethodTestDescriptor.java:127)
	at org.junit.jupiter.engine.descriptor.TestMethodTestDescriptor.execute(TestMethodTestDescriptor.java:68)
	at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$5(NodeTestTask.java:135)
	at org.junit.platform.engine.support.hierarchical.ThrowableCollector.execute(ThrowableCollector.java:73)
	at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$7(NodeTestTask.java:125)
	at org.junit.platform.engine.support.hierarchical.Node.around(Node.java:135)
	at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$8(NodeTestTask.java:123)
	at org.junit.platform.engine.support.hierarchical.ThrowableCollector.execute(ThrowableCollector.java:73)
	at org.junit.platform.engine.support.hierarchical.NodeTestTask.executeRecursively(NodeTestTask.java:122)
	at org.junit.platform.engine.support.hierarchical.NodeTestTask.execute(NodeTestTask.java:80)
	at java.util.ArrayList.forEach(ArrayList.java:1249)
	at org.junit.platform.engine.support.hierarchical.SameThreadHierarchicalTestExecutorService.invokeAll(SameThreadHierarchicalTestExecutorService.java:38)
	at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$5(NodeTestTask.java:139)
	at org.junit.platform.engine.support.hierarchical.ThrowableCollector.execute(ThrowableCollector.java:73)
	at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$7(NodeTestTask.java:125)
	at org.junit.platform.engine.support.hierarchical.Node.around(Node.java:135)
	at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$8(NodeTestTask.java:123)
	at org.junit.platform.engine.support.hierarchical.ThrowableCollector.execute(ThrowableCollector.java:73)
	at org.junit.platform.engine.support.hierarchical.NodeTestTask.executeRecursively(NodeTestTask.java:122)
	at org.junit.platform.engine.support.hierarchical.NodeTestTask.execute(NodeTestTask.java:80)
	at java.util.ArrayList.forEach(ArrayList.java:1249)
	at org.junit.platform.engine.support.hierarchical.SameThreadHierarchicalTestExecutorService.invokeAll(SameThreadHierarchicalTestExecutorService.java:38)
	at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$5(NodeTestTask.java:139)
	at org.junit.platform.engine.support.hierarchical.ThrowableCollector.execute(ThrowableCollector.java:73)
	at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$7(NodeTestTask.java:125)
	at org.junit.platform.engine.support.hierarchical.Node.around(Node.java:135)
	at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$8(NodeTestTask.java:123)
	at org.junit.platform.engine.support.hierarchical.ThrowableCollector.execute(ThrowableCollector.java:73)
	at org.junit.platform.engine.support.hierarchical.NodeTestTask.executeRecursively(NodeTestTask.java:122)
	at org.junit.platform.engine.support.hierarchical.NodeTestTask.execute(NodeTestTask.java:80)
	at org.junit.platform.engine.support.hierarchical.SameThreadHierarchicalTestExecutorService.submit(SameThreadHierarchicalTestExecutorService.java:32)
	at org.junit.platform.engine.support.hierarchical.HierarchicalTestExecutor.execute(HierarchicalTestExecutor.java:57)
	at org.junit.platform.engine.support.hierarchical.HierarchicalTestEngine.execute(HierarchicalTestEngine.java:51)
	at org.junit.platform.launcher.core.DefaultLauncher.execute(DefaultLauncher.java:229)
	at org.junit.platform.launcher.core.DefaultLauncher.lambda$execute$6(DefaultLauncher.java:197)
	at org.junit.platform.launcher.core.DefaultLauncher.withInterceptedStreams(DefaultLauncher.java:211)
	at org.junit.platform.launcher.core.DefaultLauncher.execute(DefaultLauncher.java:191)
	at org.junit.platform.launcher.core.DefaultLauncher.execute(DefaultLauncher.java:128)
	at com.intellij.junit5.JUnit5IdeaTestRunner.startRunnerWithArgs(JUnit5IdeaTestRunner.java:69)
	at com.intellij.rt.execution.junit.IdeaTestRunner$Repeater.startRunnerWithArgs(IdeaTestRunner.java:47)
	at com.intellij.rt.execution.junit.JUnitStarter.prepareStreamsAndStart(JUnitStarter.java:242)
	at com.intellij.rt.execution.junit.JUnitStarter.main(JUnitStarter.java:70)

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.