From 4ba9e3127df69fa645027958ca1132c2a0bea9aa Mon Sep 17 00:00:00 2001 From: Michael Staib Date: Thu, 24 Jan 2019 23:09:54 +0100 Subject: [PATCH] Added stitching example --- Stitching/ContractSchema/ContractStorage.cs | 55 +++++++++++++ Stitching/ContractSchema/ContractType.cs | 14 ++++ Stitching/ContractSchema/IContract.cs | 9 +++ .../ContractSchema/LifeInsuranceContract.cs | 12 +++ .../LifeInsuranceContractType.cs | 16 ++++ Stitching/ContractSchema/Program.cs | 25 ++++++ Stitching/ContractSchema/Query.cs | 45 +++++++++++ Stitching/ContractSchema/QueryType.cs | 20 +++++ Stitching/ContractSchema/SomeOtherContract.cs | 14 ++++ .../ContractSchema/SomeOtherContractType.cs | 23 ++++++ Stitching/ContractSchema/Startup.cs | 39 ++++++++++ Stitching/ContractSchema/contract.csproj | 27 +++++++ Stitching/CustomerSchema/Consultant.cs | 9 +++ Stitching/CustomerSchema/ConsultantType.cs | 15 ++++ Stitching/CustomerSchema/Customer.cs | 10 +++ .../CustomerOrConsultantType.cs | 15 ++++ .../CustomerSchema/CustomerRepository.cs | 43 ++++++++++ Stitching/CustomerSchema/CustomerResolver.cs | 21 +++++ Stitching/CustomerSchema/CustomerType.cs | 20 +++++ .../CustomerSchema/ICustomerOrConsultant.cs | 7 ++ Stitching/CustomerSchema/Program.cs | 25 ++++++ Stitching/CustomerSchema/Query.cs | 48 ++++++++++++ Stitching/CustomerSchema/QueryType.cs | 27 +++++++ Stitching/CustomerSchema/Startup.cs | 35 +++++++++ Stitching/CustomerSchema/customer.csproj | 27 +++++++ Stitching/StitchedSchema/Contract.graphql | 18 +++++ Stitching/StitchedSchema/Customer.graphql | 19 +++++ Stitching/StitchedSchema/Program.cs | 24 ++++++ Stitching/StitchedSchema/Startup.cs | 78 +++++++++++++++++++ Stitching/StitchedSchema/Stitching.graphql | 40 ++++++++++ Stitching/StitchedSchema/stitched.csproj | 34 ++++++++ 31 files changed, 814 insertions(+) create mode 100644 Stitching/ContractSchema/ContractStorage.cs create mode 100644 Stitching/ContractSchema/ContractType.cs create mode 100644 Stitching/ContractSchema/IContract.cs create mode 100644 Stitching/ContractSchema/LifeInsuranceContract.cs create mode 100644 Stitching/ContractSchema/LifeInsuranceContractType.cs create mode 100644 Stitching/ContractSchema/Program.cs create mode 100644 Stitching/ContractSchema/Query.cs create mode 100644 Stitching/ContractSchema/QueryType.cs create mode 100644 Stitching/ContractSchema/SomeOtherContract.cs create mode 100644 Stitching/ContractSchema/SomeOtherContractType.cs create mode 100644 Stitching/ContractSchema/Startup.cs create mode 100644 Stitching/ContractSchema/contract.csproj create mode 100644 Stitching/CustomerSchema/Consultant.cs create mode 100644 Stitching/CustomerSchema/ConsultantType.cs create mode 100644 Stitching/CustomerSchema/Customer.cs create mode 100644 Stitching/CustomerSchema/CustomerOrConsultantType.cs create mode 100644 Stitching/CustomerSchema/CustomerRepository.cs create mode 100644 Stitching/CustomerSchema/CustomerResolver.cs create mode 100644 Stitching/CustomerSchema/CustomerType.cs create mode 100644 Stitching/CustomerSchema/ICustomerOrConsultant.cs create mode 100644 Stitching/CustomerSchema/Program.cs create mode 100644 Stitching/CustomerSchema/Query.cs create mode 100644 Stitching/CustomerSchema/QueryType.cs create mode 100644 Stitching/CustomerSchema/Startup.cs create mode 100644 Stitching/CustomerSchema/customer.csproj create mode 100644 Stitching/StitchedSchema/Contract.graphql create mode 100644 Stitching/StitchedSchema/Customer.graphql create mode 100644 Stitching/StitchedSchema/Program.cs create mode 100644 Stitching/StitchedSchema/Startup.cs create mode 100644 Stitching/StitchedSchema/Stitching.graphql create mode 100644 Stitching/StitchedSchema/stitched.csproj diff --git a/Stitching/ContractSchema/ContractStorage.cs b/Stitching/ContractSchema/ContractStorage.cs new file mode 100644 index 0000000..57295c7 --- /dev/null +++ b/Stitching/ContractSchema/ContractStorage.cs @@ -0,0 +1,55 @@ +using System; +using System.Collections.Generic; +using System.Threading.Tasks; + +namespace Demo.Contracts +{ + public class ContractStorage + { + public List Contracts { get; } = new List + { + new LifeInsuranceContract + { + Id = "1", + CustomerId= "1", + Premium = 123456.11 + }, + new LifeInsuranceContract + { + Id = "2", + CustomerId= "1", + Premium = 456789.12 + }, + new LifeInsuranceContract + { + Id = "3", + CustomerId = "2", + Premium = 789.12 + }, + new SomeOtherContract + { + Id = "1", + CustomerId= "1", + ExpiryDate = new DateTime(2015, 2, 1, 0,0,0, DateTimeKind.Utc) + }, + new SomeOtherContract + { + Id = "2", + CustomerId= "2", + ExpiryDate = new DateTime(2015, 5, 1, 0,0,0, DateTimeKind.Utc) + }, + new SomeOtherContract + { + Id = "3", + CustomerId= "3", + ExpiryDate = new DateTime(2017, 1, 30, 0,0,0, DateTimeKind.Utc) + }, + new SomeOtherContract + { + Id = "4", + CustomerId= "3", + ExpiryDate = new DateTime(2020, 1, 1, 0,0,0, DateTimeKind.Utc) + } + }; + } +} diff --git a/Stitching/ContractSchema/ContractType.cs b/Stitching/ContractSchema/ContractType.cs new file mode 100644 index 0000000..ff5b38b --- /dev/null +++ b/Stitching/ContractSchema/ContractType.cs @@ -0,0 +1,14 @@ +using HotChocolate.Types; + +namespace Demo.Contracts +{ + public class ContractType + : InterfaceType + { + protected override void Configure(IInterfaceTypeDescriptor descriptor) + { + descriptor.Name("Contract"); + descriptor.Field("id").Type>(); + } + } +} diff --git a/Stitching/ContractSchema/IContract.cs b/Stitching/ContractSchema/IContract.cs new file mode 100644 index 0000000..64397c4 --- /dev/null +++ b/Stitching/ContractSchema/IContract.cs @@ -0,0 +1,9 @@ +namespace Demo.Contracts +{ + public interface IContract + { + string Id { get; } + + string CustomerId { get; } + } +} diff --git a/Stitching/ContractSchema/LifeInsuranceContract.cs b/Stitching/ContractSchema/LifeInsuranceContract.cs new file mode 100644 index 0000000..93a577a --- /dev/null +++ b/Stitching/ContractSchema/LifeInsuranceContract.cs @@ -0,0 +1,12 @@ +namespace Demo.Contracts +{ + public class LifeInsuranceContract + : IContract + { + public string Id { get; set; } + + public string CustomerId { get; set; } + + public double Premium { get; set; } + } +} diff --git a/Stitching/ContractSchema/LifeInsuranceContractType.cs b/Stitching/ContractSchema/LifeInsuranceContractType.cs new file mode 100644 index 0000000..c629ecf --- /dev/null +++ b/Stitching/ContractSchema/LifeInsuranceContractType.cs @@ -0,0 +1,16 @@ +using HotChocolate.Types; + +namespace Demo.Contracts +{ + public class LifeInsuranceContractType + : ObjectType + { + protected override void Configure( + IObjectTypeDescriptor descriptor) + { + descriptor.Interface(); + descriptor.Field(t => t.Id).Type>(); + descriptor.Field(t => t.CustomerId).Ignore(); + } + } +} diff --git a/Stitching/ContractSchema/Program.cs b/Stitching/ContractSchema/Program.cs new file mode 100644 index 0000000..4ec4019 --- /dev/null +++ b/Stitching/ContractSchema/Program.cs @@ -0,0 +1,25 @@ +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System.Threading.Tasks; +using Microsoft.AspNetCore; +using Microsoft.AspNetCore.Hosting; +using Microsoft.Extensions.Configuration; +using Microsoft.Extensions.Logging; + +namespace Demo.Contracts +{ + public class Program + { + public static void Main(string[] args) + { + CreateWebHostBuilder(args).Build().Run(); + } + + public static IWebHostBuilder CreateWebHostBuilder(string[] args) => + WebHost.CreateDefaultBuilder(args) + .UseUrls("http://localhost:5051") + .UseStartup(); + } +} diff --git a/Stitching/ContractSchema/Query.cs b/Stitching/ContractSchema/Query.cs new file mode 100644 index 0000000..363b8f7 --- /dev/null +++ b/Stitching/ContractSchema/Query.cs @@ -0,0 +1,45 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using HotChocolate.Types.Relay; + +namespace Demo.Contracts +{ + public class Query + { + private readonly IdSerializer _idSerializer = new IdSerializer(); + private readonly ContractStorage _contractStorage; + + public Query(ContractStorage contractStorage) + { + _contractStorage = contractStorage + ?? throw new ArgumentNullException(nameof(contractStorage)); + } + + public IContract GetContract(string contractId) + { + IdValue value = _idSerializer.Deserialize(contractId); + + if (value.TypeName == nameof(LifeInsuranceContract)) + { + return _contractStorage.Contracts + .OfType() + .FirstOrDefault(t => t.Id.Equals(value.Value)); + } + else + { + return _contractStorage.Contracts + .OfType() + .FirstOrDefault(t => t.Id.Equals(value.Value)); + } + } + + public IEnumerable GetContracts(string customerId) + { + IdValue value = _idSerializer.Deserialize(customerId); + + return _contractStorage.Contracts + .Where(t => t.CustomerId.Equals(value.Value)); + } + } +} diff --git a/Stitching/ContractSchema/QueryType.cs b/Stitching/ContractSchema/QueryType.cs new file mode 100644 index 0000000..c012283 --- /dev/null +++ b/Stitching/ContractSchema/QueryType.cs @@ -0,0 +1,20 @@ +using HotChocolate.Types; + +namespace Demo.Contracts +{ + public class QueryType + : ObjectType + { + protected override void Configure( + IObjectTypeDescriptor descriptor) + { + descriptor.Field(t => t.GetContract(default)) + .Argument("contractId", a => a.Type>()) + .Type(); + + descriptor.Field(t => t.GetContracts(default)) + .Argument("customerId", a => a.Type>()) + .Type>>(); + } + } +} diff --git a/Stitching/ContractSchema/SomeOtherContract.cs b/Stitching/ContractSchema/SomeOtherContract.cs new file mode 100644 index 0000000..54c1572 --- /dev/null +++ b/Stitching/ContractSchema/SomeOtherContract.cs @@ -0,0 +1,14 @@ +using System; + +namespace Demo.Contracts +{ + public class SomeOtherContract + : IContract + { + public string Id { get; set; } + + public string CustomerId { get; set; } + + public DateTime ExpiryDate { get; set; } + } +} diff --git a/Stitching/ContractSchema/SomeOtherContractType.cs b/Stitching/ContractSchema/SomeOtherContractType.cs new file mode 100644 index 0000000..303d676 --- /dev/null +++ b/Stitching/ContractSchema/SomeOtherContractType.cs @@ -0,0 +1,23 @@ +using HotChocolate.Types; + +namespace Demo.Contracts +{ + public class SomeOtherContractType + : ObjectType + { + protected override void Configure( + IObjectTypeDescriptor descriptor) + { + descriptor.Interface(); + + descriptor.Field(t => t.Id) + .Type>(); + + descriptor.Field(t => t.CustomerId) + .Ignore(); + + descriptor.Field(t => t.ExpiryDate) + .Type>(); + } + } +} diff --git a/Stitching/ContractSchema/Startup.cs b/Stitching/ContractSchema/Startup.cs new file mode 100644 index 0000000..71ea528 --- /dev/null +++ b/Stitching/ContractSchema/Startup.cs @@ -0,0 +1,39 @@ +using HotChocolate; +using HotChocolate.AspNetCore; +using Demo.Contracts; +using Microsoft.AspNetCore.Builder; +using Microsoft.AspNetCore.Hosting; +using Microsoft.Extensions.DependencyInjection; + +namespace Demo.Contracts +{ + public class Startup + { + public void ConfigureServices(IServiceCollection services) + { + services.AddSingleton(); + + // Add GraphQL Services + services.AddGraphQL(Schema.Create(c => + { + c.RegisterQueryType(); + c.RegisterType(); + c.RegisterType(); + + c.UseGlobalObjectIdentifier(); + })); + } + + // This method gets called by the runtime. Use this method to configure the HTTP request pipeline. + public void Configure(IApplicationBuilder app, IHostingEnvironment env) + { + if (env.IsDevelopment()) + { + app.UseDeveloperExceptionPage(); + } + + app.UseGraphQL(); + app.UsePlayground(); + } + } +} diff --git a/Stitching/ContractSchema/contract.csproj b/Stitching/ContractSchema/contract.csproj new file mode 100644 index 0000000..c43b593 --- /dev/null +++ b/Stitching/ContractSchema/contract.csproj @@ -0,0 +1,27 @@ + + + + netcoreapp2.1 + 7.2 + + + + Full + true + + + + pdbonly + true + + + + + + + + + + + + \ No newline at end of file diff --git a/Stitching/CustomerSchema/Consultant.cs b/Stitching/CustomerSchema/Consultant.cs new file mode 100644 index 0000000..903e6c5 --- /dev/null +++ b/Stitching/CustomerSchema/Consultant.cs @@ -0,0 +1,9 @@ +namespace Demo.Customers +{ + public class Consultant + : ICustomerOrConsultant + { + public string Id { get; set; } + public string Name { get; set; } + } +} diff --git a/Stitching/CustomerSchema/ConsultantType.cs b/Stitching/CustomerSchema/ConsultantType.cs new file mode 100644 index 0000000..d3d3e94 --- /dev/null +++ b/Stitching/CustomerSchema/ConsultantType.cs @@ -0,0 +1,15 @@ +using HotChocolate.Types; + +namespace Demo.Customers +{ + public class ConsultantType + : ObjectType + { + protected override void Configure( + IObjectTypeDescriptor descriptor) + { + descriptor.Field(t => t.Id).Type>(); + descriptor.Field(t => t.Name).Type>(); + } + } +} diff --git a/Stitching/CustomerSchema/Customer.cs b/Stitching/CustomerSchema/Customer.cs new file mode 100644 index 0000000..a0c5abc --- /dev/null +++ b/Stitching/CustomerSchema/Customer.cs @@ -0,0 +1,10 @@ +namespace Demo.Customers +{ + public class Customer + : ICustomerOrConsultant + { + public string Id { get; set; } + public string Name { get; set; } + public string ConsultantId { get; set; } + } +} diff --git a/Stitching/CustomerSchema/CustomerOrConsultantType.cs b/Stitching/CustomerSchema/CustomerOrConsultantType.cs new file mode 100644 index 0000000..e6aed53 --- /dev/null +++ b/Stitching/CustomerSchema/CustomerOrConsultantType.cs @@ -0,0 +1,15 @@ +using HotChocolate.Types; + +namespace Demo.Customers +{ + public class CustomerOrConsultantType + : UnionType + { + protected override void Configure(IUnionTypeDescriptor descriptor) + { + descriptor.Name("CustomerOrConsultant"); + descriptor.Type(); + descriptor.Type(); + } + } +} diff --git a/Stitching/CustomerSchema/CustomerRepository.cs b/Stitching/CustomerSchema/CustomerRepository.cs new file mode 100644 index 0000000..0b11ec7 --- /dev/null +++ b/Stitching/CustomerSchema/CustomerRepository.cs @@ -0,0 +1,43 @@ +using System.Collections.Generic; + +namespace Demo.Customers +{ + public class CustomerRepository + { + public List Customers { get; } = new List + { + new Customer + { + Id = "1", + Name = "Freddy Freeman", + ConsultantId = "1" + }, + new Customer + { + Id = "2", + Name = "Carol Danvers", + ConsultantId = "1" + }, + new Customer + { + Id = "3", + Name = "Walter Lawson", + ConsultantId = "2" + } + }; + + public List Consultants { get; } = new List + { + new Consultant + { + Id = "1", + Name = "Jordan Belfort", + }, + new Consultant + { + Id = "2", + Name = "Gordon Gekko", + } + }; + } +} diff --git a/Stitching/CustomerSchema/CustomerResolver.cs b/Stitching/CustomerSchema/CustomerResolver.cs new file mode 100644 index 0000000..f30784e --- /dev/null +++ b/Stitching/CustomerSchema/CustomerResolver.cs @@ -0,0 +1,21 @@ +using System; +using System.Linq; +using HotChocolate; + +namespace Demo.Customers +{ + public class CustomerResolver + { + public Consultant GetConsultant( + Customer customer, + [Service]CustomerRepository repository) + { + if (customer.ConsultantId != null) + { + return repository.Consultants.FirstOrDefault( + t => t.Id.Equals(customer.ConsultantId)); + } + return null; + } + } +} diff --git a/Stitching/CustomerSchema/CustomerType.cs b/Stitching/CustomerSchema/CustomerType.cs new file mode 100644 index 0000000..79e2d17 --- /dev/null +++ b/Stitching/CustomerSchema/CustomerType.cs @@ -0,0 +1,20 @@ +using HotChocolate.Types; + +namespace Demo.Customers +{ + public class CustomerType + : ObjectType + { + protected override void Configure( + IObjectTypeDescriptor descriptor) + { + descriptor.Field(t => t.Id).Type>(); + descriptor.Field(t => t.Name).Type>(); + descriptor.Field(t => t.ConsultantId).Ignore(); + + descriptor.Field( + t => t.GetConsultant(default, default)) + .Type(); + } + } +} diff --git a/Stitching/CustomerSchema/ICustomerOrConsultant.cs b/Stitching/CustomerSchema/ICustomerOrConsultant.cs new file mode 100644 index 0000000..17abcb0 --- /dev/null +++ b/Stitching/CustomerSchema/ICustomerOrConsultant.cs @@ -0,0 +1,7 @@ +namespace Demo.Customers +{ + public interface ICustomerOrConsultant + { + + } +} diff --git a/Stitching/CustomerSchema/Program.cs b/Stitching/CustomerSchema/Program.cs new file mode 100644 index 0000000..15df5b0 --- /dev/null +++ b/Stitching/CustomerSchema/Program.cs @@ -0,0 +1,25 @@ +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System.Threading.Tasks; +using Microsoft.AspNetCore; +using Microsoft.AspNetCore.Hosting; +using Microsoft.Extensions.Configuration; +using Microsoft.Extensions.Logging; + +namespace Demo.Customers +{ + public class Program + { + public static void Main(string[] args) + { + CreateWebHostBuilder(args).Build().Run(); + } + + public static IWebHostBuilder CreateWebHostBuilder(string[] args) => + WebHost.CreateDefaultBuilder(args) + .UseUrls("http://localhost:5050") + .UseStartup(); + } +} diff --git a/Stitching/CustomerSchema/Query.cs b/Stitching/CustomerSchema/Query.cs new file mode 100644 index 0000000..f4a25e9 --- /dev/null +++ b/Stitching/CustomerSchema/Query.cs @@ -0,0 +1,48 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using HotChocolate.Types.Relay; + +namespace Demo.Customers +{ + public class Query + { + private readonly IdSerializer _idSerializer = new IdSerializer(); + private readonly CustomerRepository _repository; + + public Query(CustomerRepository repository) + { + _repository = repository + ?? throw new ArgumentNullException(nameof(repository)); + } + + public Customer GetCustomer(string id) + { + IdValue value = _idSerializer.Deserialize(id); + return _repository.Customers + .FirstOrDefault(t => t.Id.Equals(value.Value)); + } + + public IEnumerable GetCustomers() + { + return _repository.Customers; + } + + public Consultant GetConsultant(string id) + { + IdValue value = _idSerializer.Deserialize(id); + return _repository.Consultants + .FirstOrDefault(t => t.Id.Equals(value.Value)); + } + + public ICustomerOrConsultant GetCustomerOrConsultant(string id) + { + IdValue value = _idSerializer.Deserialize(id); + if (value.TypeName == "Consultant") + { + return GetConsultant(id); + } + return GetCustomer(id); + } + } +} diff --git a/Stitching/CustomerSchema/QueryType.cs b/Stitching/CustomerSchema/QueryType.cs new file mode 100644 index 0000000..183bae0 --- /dev/null +++ b/Stitching/CustomerSchema/QueryType.cs @@ -0,0 +1,27 @@ +using HotChocolate.Types; + +namespace Demo.Customers +{ + public class QueryType + : ObjectType + { + protected override void Configure( + IObjectTypeDescriptor descriptor) + { + descriptor.Field(t => t.GetCustomer(default)) + .Argument("id", a => a.Type>()) + .Type(); + + descriptor.Field(t => t.GetCustomers()) + .Type>>>(); + + descriptor.Field(t => t.GetConsultant(default)) + .Argument("id", a => a.Type>()) + .Type(); + + descriptor.Field(t => t.GetCustomerOrConsultant(default)) + .Argument("id", a => a.Type>()) + .Type(); + } + } +} diff --git a/Stitching/CustomerSchema/Startup.cs b/Stitching/CustomerSchema/Startup.cs new file mode 100644 index 0000000..4bcace5 --- /dev/null +++ b/Stitching/CustomerSchema/Startup.cs @@ -0,0 +1,35 @@ +using HotChocolate; +using HotChocolate.AspNetCore; +using Demo.Customers; +using Microsoft.AspNetCore.Builder; +using Microsoft.AspNetCore.Hosting; +using Microsoft.Extensions.DependencyInjection; + +namespace Demo.Customers +{ + public class Startup + { + public void ConfigureServices(IServiceCollection services) + { + services.AddSingleton(); + + // Add GraphQL Services + services.AddGraphQL(Schema.Create(c => + { + c.RegisterQueryType(); + c.UseGlobalObjectIdentifier(); + })); + } + + public void Configure(IApplicationBuilder app, IHostingEnvironment env) + { + if (env.IsDevelopment()) + { + app.UseDeveloperExceptionPage(); + } + + app.UseGraphQL(); + app.UsePlayground(); + } + } +} diff --git a/Stitching/CustomerSchema/customer.csproj b/Stitching/CustomerSchema/customer.csproj new file mode 100644 index 0000000..c43b593 --- /dev/null +++ b/Stitching/CustomerSchema/customer.csproj @@ -0,0 +1,27 @@ + + + + netcoreapp2.1 + 7.2 + + + + Full + true + + + + pdbonly + true + + + + + + + + + + + + \ No newline at end of file diff --git a/Stitching/StitchedSchema/Contract.graphql b/Stitching/StitchedSchema/Contract.graphql new file mode 100644 index 0000000..5ba63e2 --- /dev/null +++ b/Stitching/StitchedSchema/Contract.graphql @@ -0,0 +1,18 @@ +type Query { + contract(contractId: ID!): Contract + contracts(customerId: ID!): [Contract!] +} + +interface Contract { + id: ID! +} + +type LifeInsuranceContract implements Contract { + id: ID! + premium: Float +} + +type SomeOtherContract implements Contract { + id: ID! + expiryDate: DateTime +} diff --git a/Stitching/StitchedSchema/Customer.graphql b/Stitching/StitchedSchema/Customer.graphql new file mode 100644 index 0000000..a020001 --- /dev/null +++ b/Stitching/StitchedSchema/Customer.graphql @@ -0,0 +1,19 @@ +type Query { + customer(id: ID!): Customer + consultant(id: ID!): Consultant + customerOrConsultant(id: ID!): CustomerOrConsultant + customers: [Customer!]! +} + +type Customer { + id: ID! + name: String! + consultant: Consultant +} + +type Consultant { + id: ID! + name: String! +} + +union CustomerOrConsultant = Customer | Consultant diff --git a/Stitching/StitchedSchema/Program.cs b/Stitching/StitchedSchema/Program.cs new file mode 100644 index 0000000..77b1e86 --- /dev/null +++ b/Stitching/StitchedSchema/Program.cs @@ -0,0 +1,24 @@ +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System.Threading.Tasks; +using Microsoft.AspNetCore; +using Microsoft.AspNetCore.Hosting; +using Microsoft.Extensions.Configuration; +using Microsoft.Extensions.Logging; + +namespace Demo.Stitching +{ + public class Program + { + public static void Main(string[] args) + { + CreateWebHostBuilder(args).Build().Run(); + } + + public static IWebHostBuilder CreateWebHostBuilder(string[] args) => + WebHost.CreateDefaultBuilder(args) + .UseStartup(); + } +} diff --git a/Stitching/StitchedSchema/Startup.cs b/Stitching/StitchedSchema/Startup.cs new file mode 100644 index 0000000..d30a50a --- /dev/null +++ b/Stitching/StitchedSchema/Startup.cs @@ -0,0 +1,78 @@ +using System; +using System.IO; +using HotChocolate; +using HotChocolate.AspNetCore; +using HotChocolate.Execution; +using HotChocolate.Execution.Configuration; +using HotChocolate.Types; +using Microsoft.AspNetCore.Builder; +using Microsoft.AspNetCore.Hosting; +using Microsoft.Extensions.DependencyInjection; + +namespace Demo.Stitching +{ + public class Startup + { + public void ConfigureServices(IServiceCollection services) + { + // Setup the clients that shall be used to access the remote endpoints. + services.AddHttpClient("customer", client => + { + client.BaseAddress = new Uri("http://127.0.0.1:5050"); + }); + + services.AddHttpClient("contract", client => + { + client.BaseAddress = new Uri("http://127.0.0.1:5051"); + }); + + services.AddRemoteQueryExecutor(b => b + .SetSchemaName("customer") + .SetSchema(File.ReadAllText("./Customer.graphql"))); + + services.AddRemoteQueryExecutor(b => b + .SetSchemaName("contract") + .SetSchema(File.ReadAllText("./Contract.graphql")) + .AddScalarType()); + + services.AddGraphQL(Schema.Create( + File.ReadAllText("./Stitching.graphql"), + c => + { + c.RegisterType(); + + // you can add middlewars on a stitched schema just like on local schemas: + c.Use(next => async context => + { + await next(context); + + // so just to prove that we can do anything we do with a local schema we are make all strings upper case. + if (context.Field.Type.NamedType() is StringType + && context.Result is string s) + { + context.Result = s.ToUpperInvariant(); + } + }); + + c.UseSchemaStitching(); + }) + .MakeExecutable(b => b.UseStitchingPipeline( + new QueryExecutionOptions + { + // this enables appollo tracing on the stitched schema + EnableTracing = true + }))); + } + + public void Configure(IApplicationBuilder app, IHostingEnvironment env) + { + if (env.IsDevelopment()) + { + app.UseDeveloperExceptionPage(); + } + + app.UseGraphQL(); + app.UsePlayground(); + } + } +} diff --git a/Stitching/StitchedSchema/Stitching.graphql b/Stitching/StitchedSchema/Stitching.graphql new file mode 100644 index 0000000..cc9166c --- /dev/null +++ b/Stitching/StitchedSchema/Stitching.graphql @@ -0,0 +1,40 @@ +type Query { + customer(id: ID!): Customer @schema(name: "customer") @delegate + customerOrConsultant(id: ID!): CustomerOrConsultant + @schema(name: "customer") + @delegate + contracts(id: ID!): [Contract!] + @schema(name: "contract") + @delegate(path: "contracts(customerId:$arguments:id)") + customers: [Customer!]! @schema(name: "customer") @delegate +} + +type Customer { + id: ID! + name: String! + consultant: Consultant + contracts: [Contract!] + @schema(name: "contract") + @delegate(path: "contracts(customerId:$fields:id)") +} + +type Consultant { + id: ID! + name: String! +} + +interface Contract { + id: ID! +} + +type LifeInsuranceContract implements Contract { + id: ID! + premium: Float +} + +type SomeOtherContract implements Contract { + id: ID! + expiryDate: DateTime +} + +union CustomerOrConsultant = Customer | Consultant diff --git a/Stitching/StitchedSchema/stitched.csproj b/Stitching/StitchedSchema/stitched.csproj new file mode 100644 index 0000000..0db6995 --- /dev/null +++ b/Stitching/StitchedSchema/stitched.csproj @@ -0,0 +1,34 @@ + + + + netcoreapp2.1 + 7.2 + + + + Full + true + + + + pdbonly + true + + + + + + + + + + + + + + + Always + + + + \ No newline at end of file