Skip to content

Commit

Permalink
feat: Apollo Federation v2 support (#4)
Browse files Browse the repository at this point in the history
  • Loading branch information
dariuszkuc committed Oct 12, 2023
1 parent e17fb99 commit b8c82e6
Show file tree
Hide file tree
Showing 71 changed files with 2,546 additions and 232 deletions.
15 changes: 6 additions & 9 deletions .github/workflows/compatibility.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -10,22 +10,19 @@ jobs:
compatibility:
timeout-minutes: 30
runs-on: ubuntu-latest
defaults:
run:
working-directory: compatibility

steps:
- uses: actions/checkout@v3
- name: Setup .NET
uses: actions/setup-dotnet@v3
with:
dotnet-version: 7.0.x
# - name: Restore dependencies
# run: dotnet restore
# - name: Build
# run: dotnet build --no-restore
# - name: Generate schema file
# run: dotnet run --project compatibility/Products.csproj schema export --output products.graphql
- name: Restore dependencies
run: dotnet restore
- name: Build
run: dotnet build --no-restore
- name: Generate schema file
run: dotnet run --project compatibility/Products.csproj schema export --output products.graphql
- name: Compatibility Test
uses: apollographql/federation-subgraph-compatibility@v2
with:
Expand Down
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ obj
out

# compatibility stuff
products.graphql
package.json
package-lock.json
results.md
Expand Down
97 changes: 93 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,8 @@ var builder = WebApplication.CreateBuilder(args);

builder.Services
.AddGraphQLServer()
.AddApolloFederation()
// .AddApolloFederation() // use this instead if you want to opt-in to fed v1
.AddApolloFederationV2()
// register your types and services
;

Expand All @@ -51,7 +52,7 @@ Apollo Federation requires subgraphs to provide some additional metadata to make
the supergraph by the specified `@key`s. Since entities can be extended by various subgraphs, we need an extra entry point to access the entities, i.e. subgraphs need to
implement reference resolvers for entities that they support.

Currently `ApolloGraphQL.HotChocolate.Federation` supports only Apollo Federation v1. See [Apollo documentation](https://www.apollographql.com/docs/federation/) for additional Federation details.
See [Apollo documentation](https://www.apollographql.com/docs/federation/) for additional Federation details.

### Annotation

Expand Down Expand Up @@ -97,14 +98,23 @@ type Product @key(fields: "id") {

#### Federation Attributes

Directives
Federation v1 directives

* `Key` applicable on objects, see [`@key` documentation](https://www.apollographql.com/docs/federation/federated-types/federated-directives#key)
* `Extends` applicable on objects, see [`@extends` documentation](https://www.apollographql.com/docs/federation/federated-types/federated-directives#extends)
* `External` applicable on fields, see [`@external` documentation](https://www.apollographql.com/docs/federation/federated-types/federated-directives#external)
* `Key` applicable on objects, see [`@key` documentation](https://www.apollographql.com/docs/federation/federated-types/federated-directives#key)
* `Provides` applicable on fields, see [`@provides` documentation](https://www.apollographql.com/docs/federation/federated-types/federated-directives#provides)
* `Requires` applicable on fields, see [`@requires` documentation](https://www.apollographql.com/docs/federation/federated-types/federated-directives#requires)

Federation v2 directives (includes all of the v1 directives)

* `ApolloTag` applicable on schema, see [`@tag` documentation](https://www.apollographql.com/docs/federation/federated-types/federated-directives#tag)
* `ComposeDirective` applicable on schema, see [`@composeDirective` documentation](https://www.apollographql.com/docs/federation/federated-types/federated-directives#composedirective)
* `Inaccessible` applicable on all type definitions, see [`@inaccessible` documentation](https://www.apollographql.com/docs/federation/federated-types/federated-directives#inaccessible)
* `InterfaceObject` applicable on objects, see [`@interfaceObject` documentation](https://www.apollographql.com/docs/federation/federated-types/federated-directives#interfaceobject)
* `Link` applicable on schema, see [`@link` documentation](https://www.apollographql.com/docs/federation/federated-types/federated-directives#the-link-directive)
* `Shareable` applicable on schema, see [`@shareable` documentation](https://www.apollographql.com/docs/federation/federated-types/federated-directives#shareable)

Entity resolution

* `Map` applicable on entity resolver method paramaters, allows you to map complex argument to a simpler representation value, e.g. `[Map("foo.bar")] string bar`
Expand Down Expand Up @@ -159,6 +169,85 @@ type Product @key(fields: "id") {
}
```

#### Descriptor Extensions

Federation v1 directives

* `ExtendsType` applicable on objects, see [`@extends` documentation](https://www.apollographql.com/docs/federation/federated-types/federated-directives#extends)
* `External` applicable on fields, see [`@external` documentation](https://www.apollographql.com/docs/federation/federated-types/federated-directives#external)
* `Key(fieldset)` applicable on objects, see [`@key` documentation](https://www.apollographql.com/docs/federation/federated-types/federated-directives#key)
* `Provides(fieldset)` applicable on fields, see [`@provides` documentation](https://www.apollographql.com/docs/federation/federated-types/federated-directives#provides)
* `Requires(fieldset)` applicable on fields, see [`@requires` documentation](https://www.apollographql.com/docs/federation/federated-types/federated-directives#requires)

Federation v2 directives (includes all of the v1 directives)

* `ApolloTag` applicable on all type definitions, see [`@tag` documentation](https://www.apollographql.com/docs/federation/federated-types/federated-directives#tag)
* `ComposeDirective(name)` applicable on schema, see [`@composeDirective` documentation](https://www.apollographql.com/docs/federation/federated-types/federated-directives#composedirective)
* `Inaccessible` applicable on all type definitions, see [`@inaccessible` documentation](https://www.apollographql.com/docs/federation/federated-types/federated-directives#inaccessible)
* `InterfaceObject` applicable on objects, see [`@interfaceObject` documentation](https://www.apollographql.com/docs/federation/federated-types/federated-directives#interfaceobject)
* `Key(fieldset, resolvable?)` applicable on objects, see [`@key` documentation](https://www.apollographql.com/docs/federation/federated-types/federated-directives#key)
* `Link(url, [import]?)` applicable on schema, see [`@link` documentation](https://www.apollographql.com/docs/federation/federated-types/federated-directives#the-link-directive)
* `Shareable` applicable on fields and objects, see [`@shareable` documentation](https://www.apollographql.com/docs/federation/federated-types/federated-directives#shareable)

Entity resolution

* you have to provide `ResolveReferenceWith` function to be able to resolve the entities

### Advanced Use Cases

#### Generating schema at build time

See [HotChocolate documentation](https://chillicream.com/docs/hotchocolate/v13/server/command-line) for details on the server support for command line interface.

```csharp
var builder = WebApplication.CreateBuilder(args);

builder.Services
.AddGraphQLServer()
.AddApolloFederationV2()
// register your types and services
;

var app = builder.Build();
app.MapGraphQL();
app.RunWithGraphQLCommands();
```

You can then generate your schema by running

```shell
dotnet run -- schema export --output schema.graphql
```

#### `@composedDirective` usage

By default, Supergraph schema excludes all custom directives. The `@composeDirective`` is used to specify custom directives that should be preserved in the Supergraph schema.

`ApolloGraphQL.HotChocolate.Federation` provides common `FederatedSchema` class that automatically includes Apollo Federation v2 `@link` definition. When applying any custom
schema directives, you should extend this class and add required attributes/directives.

When applying `@composedDirective` you also need to `@link` it your specification. Your custom schema should then be passed to the `AddApolloFederationV2` extension.

```csharp
[ComposeDirective("@custom")]
[Link("https://myspecs.dev/myCustomDirective/v1.0", new string[] { "@custom" })]
public class CustomSchema : FederatedSchema
{
}

var builder = WebApplication.CreateBuilder(args);

builder.Services
.AddGraphQLServer()
.AddApolloFederationV2(new CustomSchema())
// register your types and services
;

var app = builder.Build();
app.MapGraphQL();
app.Run();
```

## Contact

If you have a specific question about the library or code, please start a discussion in the [Apollo community forums](https://community.apollographql.com/) or start a conversation on our [Discord server](https://discord.gg/graphos).
Expand Down
4 changes: 3 additions & 1 deletion compatibility/Program.cs
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,9 @@

builder.Services
.AddGraphQLServer()
.AddApolloFederation()
.AddApolloFederationV2(new CustomSchema())
.AddType<CustomDirectiveType>()
.AddType<Inventory>()
.AddQueryType<Query>()
.RegisterService<Data>();

Expand Down
6 changes: 6 additions & 0 deletions compatibility/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,12 @@ Install compatibility script
npm install --dev @apollo/federation-subgraph-compatibility
```

Generate test schema

```shell
dotnet run --project compatibility/Products.csproj schema export --output products.graphql
```

Run compatibility tests

```shell
Expand Down
11 changes: 11 additions & 0 deletions compatibility/Types/CustomAttribute.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
using HotChocolate.Types.Descriptors;

namespace Products;

public sealed class CustomAttribute : ObjectTypeDescriptorAttribute
{
protected override void OnConfigure(IDescriptorContext context, IObjectTypeDescriptor descriptor, Type type)
{
descriptor.Directive(CustomDirectiveType.CustomDirectiveName);
}
}
11 changes: 11 additions & 0 deletions compatibility/Types/CustomDirectiveType.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
namespace Products;

public sealed class CustomDirectiveType : DirectiveType
{
public const string CustomDirectiveName = "custom";

protected override void Configure(IDirectiveTypeDescriptor descriptor)
=> descriptor
.Name(CustomDirectiveName)
.Location(DirectiveLocation.Object);
}
11 changes: 11 additions & 0 deletions compatibility/Types/CustomSchema.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
using ApolloGraphQL.HotChocolate.Federation;
using ApolloGraphQL.HotChocolate.Federation.Two;

namespace Products;

[ComposeDirective("@custom")]
[Link("https://myspecs.dev/myCustomDirective/v1.0", new string[] { "@custom" })]
public class CustomSchema : FederatedSchema
{

}
4 changes: 4 additions & 0 deletions compatibility/Types/Data.cs
Original file line number Diff line number Diff line change
Expand Up @@ -27,4 +27,8 @@ public class Data
{
new ("apollo-federation-v1", "@apollo/federation-v1", "Migrate to Federation V2", DefaultUser)
};

public List<Inventory> Inventories() => new List<Inventory> {
new ("apollo-oss")
};
}
28 changes: 28 additions & 0 deletions compatibility/Types/Inventory.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
using ApolloGraphQL.HotChocolate.Federation;

namespace Products;

[Key("id")]
[InterfaceObject]
public class Inventory
{

public Inventory(string id)
{
Id = id;
}

[ID]
public string Id { get; }

public List<DeprecatedProduct> DeprecatedProducts(Data repository) => repository.DeprecatedProducts;

[ReferenceResolver]
public static Inventory? GetInventoryById(
string id,
Data repository)
{
return repository.Inventories().FirstOrDefault(
r => r.Id.Equals(id));
}
}
2 changes: 2 additions & 0 deletions compatibility/Types/Product.cs
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ namespace Products;
[Key("id")]
[Key("sku package")]
[Key("sku variation { id }")]
[Custom]
public class Product
{
public Product(string id, string? sku, string? package, ProductVariation? variation, ProductDimension? dimensions, User? createdBy, string? notes, List<ProductResearch> research)
Expand Down Expand Up @@ -33,6 +34,7 @@ public Product(string id, string? sku, string? package, ProductVariation? variat
[Provides("totalProductsCreated")]
public User? CreatedBy { get; }

[ApolloTag("internal")]
public string? Notes { get; }

public List<ProductResearch> Research { get; }
Expand Down
4 changes: 4 additions & 0 deletions compatibility/Types/ProductDimension.cs
Original file line number Diff line number Diff line change
@@ -1,5 +1,8 @@
using ApolloGraphQL.HotChocolate.Federation;

namespace Products;

[Shareable]
public class ProductDimension
{
public ProductDimension(string size, double weight, string? unit)
Expand All @@ -13,5 +16,6 @@ public ProductDimension(string size, double weight, string? unit)

public double? Weight { get; }

[Inaccessible]
public string? Unit { get; }
}
3 changes: 0 additions & 3 deletions compatibility/Types/Query.cs
Original file line number Diff line number Diff line change
@@ -1,8 +1,5 @@
using ApolloGraphQL.HotChocolate.Federation;

namespace Products;

[ExtendServiceType]
public class Query
{
public Product? GetProduct([ID] string id, Data repository)
Expand Down
3 changes: 2 additions & 1 deletion compatibility/Types/User.cs
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
namespace Products;

[Key("email")]
[ExtendServiceType]
[Extends]
public class User
{
public User(string email, string? name)
Expand All @@ -26,6 +26,7 @@ public User(string email, string? name)
[External]
public string Email { get; set; }

[Override("users")]
public string? Name { get; }

[External]
Expand Down
50 changes: 0 additions & 50 deletions compatibility/products.graphql

This file was deleted.

2 changes: 1 addition & 1 deletion examples/AnnotationBased/Inventory/Product.cs
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
namespace Inventory;

[Key("upc")]
[ExtendServiceType]
[Extends]
public class Product
{
public Product(string upc)
Expand Down
2 changes: 1 addition & 1 deletion examples/AnnotationBased/Reviews/Product.cs
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
namespace Reviews;

[Key("upc")]
[ExtendServiceType]
[Extends]
public class Product
{
public Product(string upc)
Expand Down

0 comments on commit b8c82e6

Please sign in to comment.