-
Notifications
You must be signed in to change notification settings - Fork 190
Split Construction of Graph Types into individual modules #121
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
base: master
Are you sure you want to change the base?
Changes from 2 commits
9a55028
ca1f6fc
bef2064
22d63d1
d7c8b70
ceefd06
5f03f96
de04f5b
6eddb19
1ce9c77
2090c19
c2cf1a8
368092f
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,69 @@ | ||
| using Example.Repositories; | ||
| using GraphQL.Types; | ||
| using Microsoft.Extensions.DependencyInjection; | ||
| using System; | ||
| using System.Collections.Generic; | ||
|
|
||
| namespace Example.GraphQL | ||
| { | ||
| public interface IOperation | ||
| { | ||
| IEnumerable<IFieldType> RegisterFields(); | ||
| } | ||
|
|
||
| public interface IDogOperation : IOperation { } | ||
|
|
||
| public interface ICatOperation : IOperation { } | ||
|
|
||
| public class DogOperation : ObjectGraphType<object>, IDogOperation | ||
| { | ||
| private readonly IServiceProvider _serviceProvider; | ||
|
|
||
| public DogOperation(IServiceProvider serviceProvider) | ||
| { | ||
| _serviceProvider = serviceProvider; | ||
| } | ||
|
|
||
| public IEnumerable<IFieldType> RegisterFields() | ||
| { | ||
| var fields = new List<IFieldType> | ||
| { | ||
| Field<StringGraphType>("say", resolve: context => "woof woof woof"), | ||
| GetBreedListField(), | ||
| GetImageDetailsField() | ||
| }; | ||
|
|
||
| return fields; | ||
| } | ||
|
|
||
| private IFieldType GetBreedListField() | ||
| { | ||
| return Field<NonNullGraphType<ListGraphType<NonNullGraphType<DogType>>>>("dogBreeds", resolve: context => | ||
| { | ||
| using var scope = _serviceProvider.CreateScope(); | ||
| var dogRepository = scope.ServiceProvider.GetRequiredService<DogRepository>(); | ||
| var dogs = dogRepository.GetDogs(); | ||
| return dogs; | ||
| }); | ||
| } | ||
|
|
||
| private IFieldType GetImageDetailsField() | ||
| { | ||
| return FieldAsync<NonNullGraphType<ImageDetailsType>>("dogImageDetails", resolve: async context => | ||
| { | ||
| using var scope = _serviceProvider.CreateScope(); | ||
| var imageDetailsRepository = scope.ServiceProvider.GetRequiredService<DogImageDetailsRepository>(); | ||
| var imageDetails = await imageDetailsRepository.GetDogImageDetails(); | ||
| return imageDetails; | ||
| }); | ||
| } | ||
| } | ||
|
|
||
| public class CatSayOperation : ObjectGraphType<object>, ICatOperation | ||
| { | ||
| public IEnumerable<IFieldType> RegisterFields() | ||
| { | ||
| return new List<IFieldType> { Field<StringGraphType>("say", resolve: context => "meow meow meow") }; | ||
|
sungam3r marked this conversation as resolved.
Outdated
|
||
| } | ||
| } | ||
| } | ||
| Original file line number | Diff line number | Diff line change | ||||
|---|---|---|---|---|---|---|
| @@ -0,0 +1,38 @@ | ||||||
| using GraphQL.Types; | ||||||
| using System.Collections.Generic; | ||||||
|
|
||||||
| namespace Example.GraphQL | ||||||
| { | ||||||
| public class Queries | ||||||
| { | ||||||
| public class DogQuery : ObjectGraphType<object> | ||||||
| { | ||||||
| public DogQuery(IEnumerable<IDogOperation> dogOperations) | ||||||
| { | ||||||
| foreach(var dogOperation in dogOperations) | ||||||
|
sungam3r marked this conversation as resolved.
Outdated
|
||||||
| { | ||||||
| var fields = dogOperation.RegisterFields(); | ||||||
| foreach(var field in fields) | ||||||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
|
||||||
| { | ||||||
| AddField((FieldType)field); | ||||||
| } | ||||||
| } | ||||||
| } | ||||||
| } | ||||||
|
|
||||||
| public class CatQuery : ObjectGraphType<object> | ||||||
| { | ||||||
| public CatQuery(IEnumerable<ICatOperation> catOperations) | ||||||
| { | ||||||
| foreach(var catOperation in catOperations) | ||||||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
|
||||||
| { | ||||||
| var fields = catOperation.RegisterFields(); | ||||||
| foreach (var field in fields) | ||||||
| { | ||||||
| AddField((FieldType)field); | ||||||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Maybe it's better to use
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Can you give me some more details here? Do you have any doc handy for One reason I am doing the way I am right now, is to be non-opinionated on the setup. I give control to the implementor of Again, I might be talking about something different but will wait for information on
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I'm not really sure how the use of
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. You can see a demonstration of a type-first schema here: https://github.com/graphql-dotnet/graphql-dotnet/tree/master/src/GraphQL.StarWars.TypeFirst The entire schema can be constructed via: services.AddGraphQL(b => b
.AddSystemTextJson()
.AddAutoSchema<StarWarsQuery>(c => c.WithMutation<StarWarsMutation>()));You will notice there are no 'graph types' but rather simple CLR classes decorated with attributes where necessary
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. In my own graphs, I use a slight variation of this to allow for DI injection of scoped services, which is not possible with the sample seen there. However, the code exists in the tests here: I also published it as a NuGet package here:
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. @EmmanuelPonnudurai I'm fine with example as is without |
||||||
| } | ||||||
| } | ||||||
| } | ||||||
| } | ||||||
| } | ||||||
| } | ||||||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,28 @@ | ||
| using GraphQL.Types; | ||
|
|
||
| namespace Example.GraphQL | ||
| { | ||
| public class DogType : ObjectGraphType<Dog> | ||
| { | ||
| public DogType() | ||
| { | ||
| Field(x => x.Breed); | ||
| } | ||
| } | ||
|
|
||
| public class CatType : ObjectGraphType<Dog> | ||
| { | ||
| public CatType() | ||
| { | ||
| Field(x => x.Breed); | ||
| } | ||
| } | ||
|
|
||
| public class ImageDetailsType : ObjectGraphType<ImageDetails> | ||
| { | ||
| public ImageDetailsType() | ||
| { | ||
| Field(x => x.Url); | ||
| } | ||
| } | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,17 @@ | ||
| namespace Example | ||
| { | ||
| public class Dog | ||
| { | ||
| public string Breed { get; set; } | ||
| } | ||
|
|
||
| public class Cat | ||
| { | ||
| public string Breed { get; set; } | ||
|
sungam3r marked this conversation as resolved.
Outdated
|
||
| } | ||
|
|
||
| public class ImageDetails | ||
| { | ||
| public string Url { get; set; } | ||
| } | ||
| } | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,15 +1,27 @@ | ||
| using Microsoft.AspNetCore.Hosting; | ||
| using Microsoft.Extensions.Hosting; | ||
| using System; | ||
| using System.Threading.Tasks; | ||
|
|
||
| namespace Example | ||
| { | ||
| public class Program | ||
| { | ||
| public static Task Main(string[] args) => Host | ||
| .CreateDefaultBuilder(args) | ||
| .ConfigureWebHostDefaults(builder => builder.UseStartup<Startup>()) | ||
| .Build() | ||
| .RunAsync(); | ||
| public static Task Main(string[] args) | ||
| { | ||
| try | ||
| { | ||
| return Host | ||
| .CreateDefaultBuilder(args) | ||
| .ConfigureWebHostDefaults(builder => builder.UseStartup<Startup>()) | ||
| .Build() | ||
| .RunAsync(); | ||
| } | ||
| catch (System.Exception ex) | ||
| { | ||
| Console.WriteLine(ex); | ||
| return Task.FromResult(0); | ||
| } | ||
|
sungam3r marked this conversation as resolved.
Outdated
|
||
| } | ||
| } | ||
| } | ||
| Original file line number | Diff line number | Diff line change | ||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| @@ -0,0 +1,17 @@ | ||||||||||||||
| using System.Collections.Generic; | ||||||||||||||
| using System.Linq; | ||||||||||||||
|
|
||||||||||||||
| namespace Example.Repositories | ||||||||||||||
| { | ||||||||||||||
| public class CatRepository | ||||||||||||||
| { | ||||||||||||||
| private static readonly List<Cat> Cats = new() | ||||||||||||||
| { | ||||||||||||||
| new Cat{ Breed = "Abyssinian" }, | ||||||||||||||
| new Cat{ Breed = "American Bobtail" }, | ||||||||||||||
| new Cat{ Breed = "Burmese" } | ||||||||||||||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
|
||||||||||||||
| }; | ||||||||||||||
|
|
||||||||||||||
| public IEnumerable<Cat> GetCats() => Cats.AsEnumerable(); | ||||||||||||||
| } | ||||||||||||||
| } | ||||||||||||||
| Original file line number | Diff line number | Diff line change | ||
|---|---|---|---|---|
| @@ -0,0 +1,23 @@ | ||||
| using System.Net.Http; | ||||
| using System.Threading.Tasks; | ||||
|
|
||||
| namespace Example.Repositories | ||||
| { | ||||
| public class DogImageDetailsRepository | ||||
| { | ||||
| private readonly IHttpClientFactory _httpClientFactory; | ||||
|
|
||||
| public DogImageDetailsRepository(IHttpClientFactory httpClientFactory) | ||||
| { | ||||
| _httpClientFactory = httpClientFactory; | ||||
| } | ||||
|
|
||||
| public async Task<ImageDetails> GetDogImageDetails() | ||||
| { | ||||
| var client = _httpClientFactory.CreateClient("DogsApi"); | ||||
| var result = await client.GetStringAsync("api/breeds/image/random"); | ||||
|
|
||||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
|
||||
| return new ImageDetails { Url = result }; | ||||
| } | ||||
| } | ||||
| } | ||||
| Original file line number | Diff line number | Diff line change | ||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| @@ -0,0 +1,17 @@ | ||||||||||||||
| using System.Collections.Generic; | ||||||||||||||
| using System.Linq; | ||||||||||||||
|
|
||||||||||||||
| namespace Example.Repositories | ||||||||||||||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Please use file-scoped namespaces. We will convert codebase to use filescoped namespaces some time.
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I mean only for new files. Or just leave it as is, not a big deal. |
||||||||||||||
| { | ||||||||||||||
| public class DogRepository | ||||||||||||||
| { | ||||||||||||||
| private static readonly List<Dog> Dogs = new() | ||||||||||||||
| { | ||||||||||||||
| new Dog{ Breed = "Doberman" }, | ||||||||||||||
| new Dog{ Breed = "Pit Bull" }, | ||||||||||||||
| new Dog{ Breed = "German Shepard" } | ||||||||||||||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
|
||||||||||||||
| }; | ||||||||||||||
|
|
||||||||||||||
| public IEnumerable<Dog> GetDogs() => Dogs.AsEnumerable(); | ||||||||||||||
| } | ||||||||||||||
| } | ||||||||||||||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,3 +1,5 @@ | ||
| using Example.GraphQL; | ||
| using Example.Repositories; | ||
| using GraphQL; | ||
| using GraphQL.MicrosoftDI; | ||
| using GraphQL.Server; | ||
|
|
@@ -16,6 +18,12 @@ public class Startup | |
| { | ||
| public void ConfigureServices(IServiceCollection services) | ||
| { | ||
| services.AddScoped<DogRepository>(); | ||
| services.AddScoped<DogImageDetailsRepository>(); | ||
| services.AddScoped<CatRepository>(); | ||
|
|
||
| services.AddOperations(); | ||
|
|
||
| services.AddGraphQL(b => b | ||
| .AddHttpMiddleware<DogSchema>() | ||
| .AddHttpMiddleware<CatSchema>() | ||
|
|
@@ -28,6 +36,10 @@ public void ConfigureServices(IServiceCollection services) | |
|
|
||
| services.AddLogging(builder => builder.AddConsole()); | ||
| services.AddHttpContextAccessor(); | ||
| services.AddHttpClient("DogsApi", x => | ||
| { | ||
| x.BaseAddress = new System.Uri("https://dog.ceo/"); | ||
| }); | ||
| } | ||
|
|
||
| public void Configure(IApplicationBuilder app, IWebHostEnvironment env) | ||
|
|
@@ -40,6 +52,15 @@ public void Configure(IApplicationBuilder app, IWebHostEnvironment env) | |
|
|
||
| app.UseGraphQLPlayground(new PlaygroundOptions { GraphQLEndPoint = "/api/dogs" }, "/ui/dogs"); | ||
| app.UseGraphQLPlayground(new PlaygroundOptions { GraphQLEndPoint = "/api/cats" }, "/ui/cats"); | ||
| } | ||
| } | ||
|
|
||
| public static class StartupExtensions | ||
| { | ||
| public static void AddOperations(this IServiceCollection services) | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I would inline this method into ConfigureServices. |
||
| { | ||
| services.AddSingleton<IDogOperation, DogOperation>(); | ||
| services.AddSingleton<ICatOperation, CatSayOperation>(); | ||
| } | ||
| } | ||
| } | ||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Field<StringGraphType>(appends field to graph type and returns reference to it.