Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Code-First Federation 2 Support #3921

Open
wants to merge 127 commits into
base: addfederation_extension
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from 123 commits
Commits
Show all changes
127 commits
Select commit Hold shift + click to select a range
16862a4
Code-First and Schema-First Federation 2 support
Mithras Nov 14, 2022
be54759
Merge from master
Shane32 Feb 26, 2024
0f412fb
Merge remote-tracking branch 'origin/develop' into federation2
Shane32 Feb 26, 2024
335e745
Fix merge errors
Shane32 Feb 26, 2024
4e9a5da
Remove docker files
Shane32 Feb 26, 2024
9d4dc28
Remove vscode file changes
Shane32 Feb 26, 2024
85c10c2
Merge branch 'develop' into federation2
Shane32 Feb 26, 2024
4127a59
Fix project so it builds; switch FederatedSchemaPrinter out
Shane32 Feb 26, 2024
6ecc706
Remove NeverType
Shane32 Feb 26, 2024
c79d5dd
Add directives to schema and build link via ApplyDirective
Shane32 Feb 26, 2024
914737f
reformat as raw string
Shane32 Feb 26, 2024
74903a9
Merge branch 'develop' into federation2
Shane32 Feb 26, 2024
38a35b1
Reconfigure extension methods to use IMetadataWriter; remove schema b…
Shane32 Feb 26, 2024
b5ff451
Update service tests; rename keys to camel case
Shane32 Feb 26, 2024
0b27222
reformat
Shane32 Mar 5, 2024
70eb300
Fix tests
Shane32 Mar 5, 2024
644f374
Refactor EntityResolver
Shane32 Mar 5, 2024
19d7c01
Consolidate async federation resolvers
Shane32 Mar 5, 2024
1e1329a
Refactor GetListType()
Shane32 Mar 5, 2024
fa61ddc
Support serial and parallel execution strategies; make deserializatio…
Shane32 Mar 5, 2024
aac7f7d
Update comments, api approvals, changed test
Shane32 Mar 5, 2024
d6478c5
Add comments
Shane32 Mar 5, 2024
d518f5b
Merge from develop
Shane32 Mar 6, 2024
183e940
Validate _entities arguments prior to execution of query
Shane32 Mar 6, 2024
c157c03
Update api approvals
Shane32 Mar 6, 2024
c49f423
Do not attempt to convert to dictionary types
Shane32 Mar 7, 2024
d17eed1
Merge from develop
Shane32 Mar 13, 2024
20d5c52
Updates for IListConverter changes
Shane32 Mar 13, 2024
99a02d4
Update nullability
Shane32 May 25, 2024
be10ade
Update api approvals
Shane32 May 25, 2024
e7ed5ad
Update Sample1 to use AddFederation; mark old federation classes obso…
Shane32 May 26, 2024
526215f
Update
Shane32 May 27, 2024
a806d1a
Fix for federation v1
Shane32 May 27, 2024
2989bf3
Update api approvals
Shane32 May 27, 2024
fb5dae9
Update to default to federation 2 sdl
Shane32 May 27, 2024
f10b29d
Fixes for type-first
Shane32 May 28, 2024
81f81c6
Fix sample3
Shane32 May 28, 2024
6c4c595
Fix link__purpose
Shane32 May 28, 2024
f0c29d8
Fix fieldset
Shane32 May 28, 2024
b6e77d7
Update expected response
Shane32 May 28, 2024
225b6ca
Reorganize namespaces; add XML comments
Shane32 May 28, 2024
0057422
Update
Shane32 May 28, 2024
db842be
Update tests
Shane32 May 28, 2024
eca3483
Merge branch 'develop' into federation2
Shane32 May 28, 2024
79419bb
Update formatting
Shane32 May 28, 2024
bbcc66c
Update solution file
Shane32 May 28, 2024
8d44e56
Sort usings
Shane32 May 28, 2024
873bcfe
Update to support subgraphs with no root fields and/or no resolvable …
Shane32 Jun 2, 2024
33b8441
Merge branch 'develop' into federation2
Shane32 Jun 2, 2024
77b2109
Update
Shane32 Jun 2, 2024
a61a4e7
Updates
Shane32 Jun 2, 2024
6721929
Merge from develop
Shane32 Jun 4, 2024
641521c
Update API tests
Shane32 Jun 4, 2024
d626147
Update
Shane32 Jun 4, 2024
c6a9896
Remove AddServices and AddEntities
Shane32 Jun 7, 2024
088b089
Cleanup/add extension methods
Shane32 Jun 7, 2024
21d4aee
Update namespaces and overloads
Shane32 Jun 7, 2024
00f6cea
Add GraphQL Federation v2 required types
Shane32 Jun 7, 2024
9545b9c
Rename to EntityGraphType
Shane32 Jun 7, 2024
8c1aecb
Change federation graph types to public
Shane32 Jun 7, 2024
25ef4cd
Merge fed_types
Shane32 Jun 7, 2024
fed26ea
Add federation directives, metadata extension methods, and attributes
Shane32 Jun 7, 2024
a403c13
Merge from fed2_metadata
Shane32 Jun 7, 2024
026f7a1
Merge remote-tracking branch 'origin/develop' into fed2_metadata
Shane32 Jun 7, 2024
67448a5
Update
Shane32 Jun 7, 2024
baef5bd
Update notes
Shane32 Jun 7, 2024
7358889
Update
Shane32 Jun 7, 2024
a0a5ca5
Update
Shane32 Jun 7, 2024
08b328d
Update
Shane32 Jun 7, 2024
427b285
Update attribute targets
Shane32 Jun 7, 2024
bf97a2b
Merge from develop
Shane32 Jun 7, 2024
8b9651c
Merge from develop
Shane32 Jun 7, 2024
ff162af
Merge from fed2_metadata
Shane32 Jun 7, 2024
4122f5b
Add urls
Shane32 Jun 7, 2024
23879f9
Update
Shane32 Jun 7, 2024
f479a38
Merge branch 'fed2_metadata' into federation2
Shane32 Jun 7, 2024
e139811
Add descriptions to methods
Shane32 Jun 7, 2024
d77373a
Update xml comments
Shane32 Jun 7, 2024
47d42fa
Ensure key only works for object and interface types
Shane32 Jun 7, 2024
f9892e0
Merge from fed2_metadata
Shane32 Jun 7, 2024
5029956
Revamp federation resolvers
Shane32 Jun 7, 2024
555a74a
Update
Shane32 Jun 7, 2024
8ac7020
Fix RemoveFederationTypesVisitor
Shane32 Jun 7, 2024
5ac3541
Merge branch 'fix_removefederationtypesvisitor' into federation2
Shane32 Jun 7, 2024
f8d72cf
Merge from federation_resolver
Shane32 Jun 7, 2024
8fa13a9
Update
Shane32 Jun 7, 2024
d8fd1b3
Merge from federation_resolver
Shane32 Jun 7, 2024
e10e697
rename
Shane32 Jun 7, 2024
b956c8b
Rename
Shane32 Jun 7, 2024
b2034fd
Update
Shane32 Jun 7, 2024
5bf504a
Update
Shane32 Jun 7, 2024
4e857f0
Update
Shane32 Jun 7, 2024
88a4293
Merge from develop
Shane32 Jun 8, 2024
2bcfdb5
Merge from develop
Shane32 Jun 8, 2024
85d59bb
Merge from develop
Shane32 Jun 8, 2024
12d1f08
Update notes to remove warning about caching
Shane32 Jun 8, 2024
dedaa71
Add tests
Shane32 Jun 8, 2024
22b3b24
Merge from federation_resolver
Shane32 Jun 8, 2024
edecbd1
Update api approvals
Shane32 Jun 8, 2024
81be581
Add test for wrong value types
Shane32 Jun 8, 2024
6bc99e9
Create const for representations
Shane32 Jun 8, 2024
c7be0e2
Invert if
Shane32 Jun 8, 2024
c62e481
Add more code comments
Shane32 Jun 8, 2024
6499605
Update with more tests
Shane32 Jun 9, 2024
fd119dc
Update
Shane32 Jun 9, 2024
a8c91e8
Merge from federation_resolver
Shane32 Jun 9, 2024
0741ff5
Merge from develop
Shane32 Jun 9, 2024
5862163
Merge branch 'federation_resolver' into federation2
Shane32 Jun 9, 2024
ed6c1b6
Fix formatting
Shane32 Jun 9, 2024
77f33ad
Change 'record' to 'class'.
Shane32 Jun 9, 2024
a38eeb2
Update xml comments
Shane32 Jun 9, 2024
02f7c42
Update error message
Shane32 Jun 9, 2024
b0eacc6
Merge branch 'federation_resolver' into federation2
Shane32 Jun 9, 2024
5698ec4
Merge from develop
Shane32 Jun 9, 2024
859a6c4
Merge from federation_resolver_attribute
Shane32 Jun 10, 2024
70a4389
Merge branch 'federation_resolver_attribute' into federation2
Shane32 Jun 10, 2024
54ec34c
Update
Shane32 Jun 10, 2024
b66d5af
Update
Shane32 Jun 10, 2024
e350afa
Merge branch 'federation_resolver_attribute' into federation2
Shane32 Jun 17, 2024
a5e2acf
Merge branch 'federation_resolver_attribute' into federation2
Shane32 Jun 17, 2024
d3fb532
Merge branch 'addfederation_extension' into federation2
Shane32 Jun 18, 2024
36fd28b
Merge branch 'addfederation_extension' into federation2
Shane32 Jun 20, 2024
d76d866
Merge branch 'addfederation_extension' into federation2
Shane32 Jun 20, 2024
ef1133d
Merge branch 'addfederation_extension' into federation2
Shane32 Jun 20, 2024
fd39d2f
test
Shane32 Jun 22, 2024
0ba7fe7
Update
Shane32 Jun 22, 2024
07cbcfa
Update
Shane32 Jun 22, 2024
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
58 changes: 58 additions & 0 deletions .github/workflows/test-code.yml
Original file line number Diff line number Diff line change
Expand Up @@ -109,12 +109,24 @@ jobs:
- name: Build Sample2
working-directory: src
run: dotnet build GraphQL.Federation.SchemaFirst.Sample2
- name: Build Sample3
working-directory: src
run: dotnet build GraphQL.Federation.CodeFirst.Sample3
- name: Build Sample4
working-directory: src
run: dotnet build GraphQL.Federation.TypeFirst.Sample4
- name: Start Sample1 on port 5601
working-directory: src
run: dotnet run --no-build --project GraphQL.Federation.SchemaFirst.Sample1 &
- name: Start Sample2 on port 5602
working-directory: src
run: dotnet run --no-build --project GraphQL.Federation.SchemaFirst.Sample2 &
- name: Start Sample3 on port 5603
working-directory: src
run: dotnet run --no-build --project GraphQL.Federation.CodeFirst.Sample3 &
- name: Start Sample4 on port 5604
working-directory: src
run: dotnet run --no-build --project GraphQL.Federation.TypeFirst.Sample4 &
- name: Install Apollo Rover CLI
run: |
curl -sSL https://rover.apollo.dev/nix/latest | sh
Expand Down Expand Up @@ -152,6 +164,36 @@ jobs:
done
echo "Timed out after 30 seconds, step failed."
exit 1
- name: Wait for Sample3 to spin up
run: |
for i in {1..60}
do
echo "Request $i to the URL..."
response=$(curl -s -o /dev/null -w "%{http_code}" http://localhost:5603)
if [ $response -eq 200 ]; then
echo "Received 200 response, step completed."
exit 0
fi
echo "Did not receive a 200 response, sleeping for 0.5 second..."
sleep 0.5
done
echo "Timed out after 30 seconds, step failed."
exit 1
- name: Wait for Sample4 to spin up
run: |
for i in {1..60}
do
echo "Request $i to the URL..."
response=$(curl -s -o /dev/null -w "%{http_code}" http://localhost:5604)
if [ $response -eq 200 ]; then
echo "Received 200 response, step completed."
exit 0
fi
echo "Did not receive a 200 response, sleeping for 0.5 second..."
sleep 0.5
done
echo "Timed out after 30 seconds, step failed."
exit 1
- name: Build supergraph with federation-${{ matrix.federationversion }}-supergraph-config.yaml
working-directory: src/Federation
run: rover supergraph compose --config ./federation-${{ matrix.federationversion }}-supergraph-config.yaml --elv2-license=accept > supergraph.graphql
Expand Down Expand Up @@ -185,6 +227,11 @@ jobs:
curl -X POST -H "Content-Type: application/json" -d @federation-request-1.json http://127.0.0.1:4000 > response-1.json
curl -X POST -H "Content-Type: application/json" -d @federation-request-2.json http://127.0.0.1:4000 > response-2.json
curl -X POST -H "Content-Type: application/json" -d @federation-request-3.json http://127.0.0.1:4000 > response-3.json
- name: Run additional GraphQL queries
if: matrix.federationversion == 2
working-directory: src/Federation
run: |
curl -X POST -H "Content-Type: application/json" -d @federation-request-4.json http://127.0.0.1:4000 > response-4.json
- name: Print query result 1
working-directory: src/Federation
run: cat response-1.json
Expand All @@ -194,6 +241,10 @@ jobs:
- name: Print query result 3
working-directory: src/Federation
run: cat response-3.json
- name: Print query result 4
if: matrix.federationversion == 2
working-directory: src/Federation
run: cat response-4.json
- name: Compare query result 1 to expected response
working-directory: src/Federation
run: |
Expand All @@ -212,6 +263,13 @@ jobs:
jq . response-3.json > actual-response-3.json
jq . federation-response-3.json > expected-response-3.json
diff -b actual-response-3.json expected-response-3.json
- name: Compare query result 4 to expected response
if: matrix.federationversion == 2
working-directory: src/Federation
run: |
jq . response-4.json > actual-response-4.json
jq . federation-response-4.json > expected-response-4.json
diff -b actual-response-4.json expected-response-4.json

buildcheck:
needs:
Expand Down
8 changes: 8 additions & 0 deletions src/Federation/federation-2-supergraph-config.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -8,3 +8,11 @@ subgraphs:
routing_url: http://localhost:5602/graphql
schema:
subgraph_url: http://localhost:5602/graphql
sample3:
routing_url: http://localhost:5603/graphql
schema:
subgraph_url: http://localhost:5603/graphql
sample4:
routing_url: http://localhost:5604/graphql
schema:
subgraph_url: http://localhost:5604/graphql
3 changes: 3 additions & 0 deletions src/Federation/federation-request-4.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
{
"query": "{ categories { id name products { id name category { id name } reviews { id content product { id name } author { id username reviews { id content } } } } } }"
}
118 changes: 118 additions & 0 deletions src/Federation/federation-response-4.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,118 @@
{
"data": {
"categories": [
{
"id": "1",
"name": "Category 1",
"products": [
{
"id": "1",
"name": "Product 1",
"category": {
"id": "1",
"name": "Category 1"
},
"reviews": [
{
"id": "1",
"content": "Review 1",
"product": {
"id": "1",
"name": "Product 1"
},
"author": {
"id": "1",
"username": "Username 1",
"reviews": [
{
"id": "1",
"content": "Review 1"
},
{
"id": "3",
"content": "Review 3"
}
]
}
},
{
"id": "2",
"content": "Review 2",
"product": {
"id": "1",
"name": "Product 1"
},
"author": {
"id": "2",
"username": "Username 2",
"reviews": [
{
"id": "2",
"content": "Review 2"
}
]
}
}
]
},
{
"id": "2",
"name": "Product 2",
"category": {
"id": "1",
"name": "Category 1"
},
"reviews": [
{
"id": "3",
"content": "Review 3",
"product": {
"id": "2",
"name": "Product 2"
},
"author": {
"id": "1",
"username": "Username 1",
"reviews": [
{
"id": "1",
"content": "Review 1"
},
{
"id": "3",
"content": "Review 3"
}
]
}
}
]
}
]
},
{
"id": "2",
"name": "Category 2",
"products": [
{
"id": "3",
"name": "Product 3",
"category": {
"id": "2",
"name": "Category 2"
},
"reviews": []
},
{
"id": "4",
"name": "Product 4",
"category": {
"id": "2",
"name": "Category 2"
},
"reviews": []
}
]
}
]
}
}
30 changes: 30 additions & 0 deletions src/GraphQL.Federation.CodeFirst.Sample3/Data.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
using GraphQL.Federation.CodeFirst.Sample3.Models;

namespace GraphQL.Federation.CodeFirst.Sample3;

public class Data
{
private readonly List<Review> _reviews = new()
{
new Review { Id = 1, ProductId = 1, UserId = 1, Content = "Review 1" },
new Review { Id = 2, ProductId = 1, UserId = 2, Content = "Review 2" },
new Review { Id = 3, ProductId = 2, UserId = 1, Content = "Review 3" },
};

public Task<IEnumerable<Review>> GetReviewsAsync() => Task.FromResult<IEnumerable<Review>>(_reviews);

public Task<Review?> GetReviewByIdAsync(int id)
{
return Task.FromResult(_reviews.SingleOrDefault(x => x.Id == id));
}

public Task<IEnumerable<Review>> GetReviewsByProductIdAsync(int productId)
{
return Task.FromResult(_reviews.Where(x => x.ProductId == productId));
}

public Task<IEnumerable<Review>> GetReviewsByUserIdAsync(int userId)
{
return Task.FromResult(_reviews.Where(x => x.UserId == userId));
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
<Project Sdk="Microsoft.NET.Sdk.Web">

<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>net7.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
<IsPackable>false</IsPackable>
<NoWarn>$(NoWarn);CS1591</NoWarn>
</PropertyGroup>

<ItemGroup>
<PackageReference Include="GraphQL.Server.Ui.Playground" Version="7.*" />
<PackageReference Include="Microsoft.Extensions.DependencyInjection" Version="7.*" />
<PackageReference Include="Microsoft.Extensions.Options" Version="7.*" />
</ItemGroup>

<ItemGroup>
<ProjectReference Include="..\GraphQL.MicrosoftDI\GraphQL.MicrosoftDI.csproj" />
<ProjectReference Include="..\GraphQL.SystemTextJson\GraphQL.SystemTextJson.csproj" />
</ItemGroup>

</Project>
6 changes: 6 additions & 0 deletions src/GraphQL.Federation.CodeFirst.Sample3/Models/Product.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
namespace GraphQL.Federation.CodeFirst.Sample3.Models;

public class Product
{
public required int Id { get; set; }
}
9 changes: 9 additions & 0 deletions src/GraphQL.Federation.CodeFirst.Sample3/Models/Review.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
namespace GraphQL.Federation.CodeFirst.Sample3.Models;

public class Review
{
public required int Id { get; set; }
public required int ProductId { get; set; }
public required int UserId { get; set; }
public required string Content { get; set; }
}
6 changes: 6 additions & 0 deletions src/GraphQL.Federation.CodeFirst.Sample3/Models/User.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
namespace GraphQL.Federation.CodeFirst.Sample3.Models;

public class User
{
public required int Id { get; set; }
}
67 changes: 67 additions & 0 deletions src/GraphQL.Federation.CodeFirst.Sample3/Program.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
using GraphQL.Federation.CodeFirst.Sample3.Schema;
using GraphQL.Transport;
using GraphQL.Types;
using GraphQL.Utilities;

namespace GraphQL.Federation.CodeFirst.Sample3;

public class Program
{
public static async Task Main(string[] args)
{
// Configure services
var builder = WebApplication.CreateBuilder(args);
builder.Services.AddSingleton<Data>();
builder.Services.AddGraphQL(b => b
.AddSchema<Schema3>()
.AddSystemTextJson()
.AddGraphTypes()
.AddFederation());

// Build the web application
var app = builder.Build();

// Some simple GraphQL middleware (NOTE: it is suggested to use the GraphQL.Server.Transports.AspNetCore package instead)
app.MapPost("/graphql", GraphQLHttpMiddlewareAsync);

// Add a UI package for testing
app.UseGraphQLPlayground("/");

// Optional: ensure that the schema builds and initializes
{
var schema = app.Services.GetRequiredService<ISchema>();
schema.Initialize();
}

// Start the application
await app.RunAsync().ConfigureAwait(false);
}

private static async Task GraphQLHttpMiddlewareAsync(HttpContext context)
{
// NOTE: it is suggested to use the GraphQL.Server.Transports.AspNetCore package instead

// pull the serializer and executer from the DI container
var serializer = context.RequestServices.GetRequiredService<IGraphQLSerializer>();
var executer = context.RequestServices.GetRequiredService<IDocumentExecuter>();

// read the request (ignores the content-type header)
var request = await serializer.ReadAsync<GraphQLRequest>(context.Request.Body, context.RequestAborted).ConfigureAwait(false)
?? throw new InvalidOperationException("Could not read request");

// execute the request
var result = await executer.ExecuteAsync(options =>
{
options.Query = request.Query;
options.OperationName = request.OperationName;
options.Variables = request.Variables;
options.RequestServices = context.RequestServices;
options.CancellationToken = context.RequestAborted;
}).ConfigureAwait(false);

// write the response
context.Response.StatusCode = 200; // always OK even for errors
context.Response.ContentType = "application/json";
await serializer.WriteAsync(context.Response.Body, result, context.RequestAborted).ConfigureAwait(false);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
{
"profiles": {
"GraphQL.Federation.Sample3": {
"commandName": "Project",
"launchBrowser": true,
"environmentVariables": {
"ASPNETCORE_ENVIRONMENT": "Development"
},
"applicationUrl": "https://localhost:56847;http://localhost:56848"
}
}
}
Loading
Loading