diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 8343e47..9047cba 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -13,17 +13,17 @@ jobs: runs-on: ubuntu-latest steps: - uses: actions/checkout@v2 - - name: Use .NET Core 5.0 SDK + - name: Use .NET Core 6.0 SDK uses: actions/setup-dotnet@v1 with: - dotnet-version: '5.0.x' + dotnet-version: '6.0.x' source-url: https://nuget.pkg.github.com/Shane32/index.json env: NUGET_AUTH_TOKEN: ${{secrets.GITHUB_TOKEN}} - name: Install dependencies run: dotnet restore - name: Build solution [Release] - run: dotnet build --no-restore -c Release -p:NoWarn=CS1591 -p:VersionSuffix=$GITHUB_RUN_NUMBER + run: dotnet build --no-restore -c Release -p:VersionSuffix=$GITHUB_RUN_NUMBER - name: Pack solution [Release] run: dotnet pack --no-restore --no-build -c Release -p:VersionSuffix=$GITHUB_RUN_NUMBER -o out - name: Upload artifacts diff --git a/.github/workflows/publish.yml b/.github/workflows/publish.yml index 5692045..b0beb71 100644 --- a/.github/workflows/publish.yml +++ b/.github/workflows/publish.yml @@ -23,17 +23,17 @@ jobs: version="${github_ref:10}" echo version=$version echo "version=$version" >> $GITHUB_ENV - - name: Use .NET Core 5.0 SDK + - name: Use .NET Core 6.0 SDK uses: actions/setup-dotnet@v1 with: - dotnet-version: '5.0.x' + dotnet-version: '6.0.x' source-url: https://api.nuget.org/v3/index.json env: NUGET_AUTH_TOKEN: ${{secrets.NUGET_AUTH_TOKEN}} - name: Install dependencies run: dotnet restore - name: Build solution [Release] - run: dotnet build --no-restore -c Release -p:NoWarn=CS1591 -p:Version=$version + run: dotnet build --no-restore -c Release -p:Version=$version - name: Pack solution [Release] run: dotnet pack --no-restore --no-build -c Release -p:Version=$version -o out - name: Upload artifacts diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 1aa58b5..08ace05 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -21,27 +21,19 @@ jobs: steps: - name: Checkout source uses: actions/checkout@v2 - - name: Use .NET Core 2.1 LTS SDK - uses: actions/setup-dotnet@v1 + - name: Use .NET Core SDK + uses: actions/setup-dotnet@v2 with: - dotnet-version: '2.1.x' - - name: Use .NET Core 3.1 LTS SDK - uses: actions/setup-dotnet@v1 - with: - dotnet-version: '3.1.x' - - name: Use .NET Core 5.0 SDK - uses: actions/setup-dotnet@v1 - with: - dotnet-version: '5.0.x' + dotnet-version: 6.x source-url: https://nuget.pkg.github.com/Shane32/index.json env: NUGET_AUTH_TOKEN: ${{secrets.GITHUB_TOKEN}} - name: Install dependencies run: dotnet restore - name: Build solution [Release] - run: dotnet build --no-restore -c Release -p:NoWarn=CS1591 + run: dotnet build --no-restore -c Release - name: Build solution [Debug] - run: dotnet build --no-restore -p:NoWarn=CS1591 + run: dotnet build --no-restore - name: Test solution [Debug] run: dotnet test --no-restore --no-build -p:CollectCoverage=true -p:CoverletOutputFormat=lcov -p:CoverletOutput=../../${{ matrix.os }}.lcov.info # ==== code coverage reports (ubuntu-latest only) ==== diff --git a/Directory.Build.props b/Directory.Build.props index a7a3d2c..5264937 100644 --- a/Directory.Build.props +++ b/Directory.Build.props @@ -2,7 +2,7 @@ 1.0.0-preview - 8.0 + latest Shane Krueger Shane Krueger MIT @@ -16,11 +16,20 @@ True embedded true + true + true + enable + false + README.md + Recommended + 7.0.0 + 5.0.0 - + + diff --git a/GraphQL.DI.sln b/GraphQL.DI.sln index 0ad736e..4f0c101 100644 --- a/GraphQL.DI.sln +++ b/GraphQL.DI.sln @@ -1,7 +1,7 @@  Microsoft Visual Studio Solution File, Format Version 12.00 -# Visual Studio Version 16 -VisualStudioVersion = 16.0.30611.23 +# Visual Studio Version 17 +VisualStudioVersion = 17.3.32811.315 MinimumVisualStudioVersion = 10.0.40219.1 Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Shane32.GraphQL.DI", "src\GraphQL.DI\Shane32.GraphQL.DI.csproj", "{72C179A6-53BF-48BC-BFE0-BD8CA2618AF6}" EndProject @@ -18,7 +18,14 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution items", "Solution EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Tests", "src\Tests\Tests.csproj", "{BA5F3790-9FE0-4A10-8528-0756334AC727}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Sample", "Sample\Sample.csproj", "{4AB70777-E140-49E4-8173-7809DF67A021}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Sample", "Sample\Sample.csproj", "{4AB70777-E140-49E4-8173-7809DF67A021}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Workflows", "Workflows", "{7432A0AF-9346-41D7-9DA9-D7EFEAF1A401}" + ProjectSection(SolutionItems) = preProject + .github\workflows\build.yml = .github\workflows\build.yml + .github\workflows\publish.yml = .github\workflows\publish.yml + .github\workflows\test.yml = .github\workflows\test.yml + EndProjectSection EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution diff --git a/Sample/DataLoaders/PersonDataLoader.cs b/Sample/DataLoaders/PersonDataLoader.cs index 0ca3b53..8fa88cf 100644 --- a/Sample/DataLoaders/PersonDataLoader.cs +++ b/Sample/DataLoaders/PersonDataLoader.cs @@ -1,27 +1,25 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Threading; -using System.Threading.Tasks; using GraphQL.DataLoader; using Microsoft.EntityFrameworkCore; using Sample.DbModels; -namespace Sample.DataLoaders +namespace Sample.DataLoaders; + +public class PersonDataLoader : DataLoaderBase { - public class PersonDataLoader : DataLoaderBase + private readonly TodoDbContext _db; + + public PersonDataLoader(TodoDbContext db) { - private TodoDbContext _db; - public PersonDataLoader(TodoDbContext db) => _db = db; + _db = db; + } - protected override async Task FetchAsync(IEnumerable> list, CancellationToken cancellationToken) - { - var ids = list.Select(x => x.Key); - var people = await _db.Set().Where(x => ids.Contains(x.Id)).ToDictionaryAsync(x => x.Id, cancellationToken); - foreach (var value in list) { - people.TryGetValue(value.Key, out var person); - value.SetResult(person); - } + protected override async Task FetchAsync(IEnumerable> list, CancellationToken cancellationToken) + { + var ids = list.Select(x => x.Key); + var people = await _db.Set().Where(x => ids.Contains(x.Id)).ToDictionaryAsync(x => x.Id, cancellationToken); + foreach (var value in list) { + people.TryGetValue(value.Key, out var person); + value.SetResult(person); } } } diff --git a/Sample/DbModels/Person.cs b/Sample/DbModels/Person.cs index 033cac3..98118f5 100644 --- a/Sample/DbModels/Person.cs +++ b/Sample/DbModels/Person.cs @@ -1,13 +1,7 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Threading.Tasks; +namespace Sample.DbModels; -namespace Sample.DbModels +public class Person { - public class Person - { - public int Id { get; set; } - public string Name { get; set; } - } + public int Id { get; set; } + public string Name { get; set; } = null!; } diff --git a/Sample/DbModels/Todo.cs b/Sample/DbModels/Todo.cs index 470a1be..3ae521f 100644 --- a/Sample/DbModels/Todo.cs +++ b/Sample/DbModels/Todo.cs @@ -1,20 +1,15 @@ -using System; -using System.Collections.Generic; using System.ComponentModel.DataAnnotations.Schema; -using System.Linq; -using System.Threading.Tasks; -namespace Sample.DbModels +namespace Sample.DbModels; + +public class Todo { - public class Todo - { - public int Id { get; set; } - public string Title { get; set; } - public string Notes { get; set; } - public bool Completed { get; set; } - public DateTime? CompletionDate { get; set; } - public int? CompletedByPersonId { get; set; } - [ForeignKey("CompletedByPersonId")] - public Person CompletedBy { get; set; } - } + public int Id { get; set; } + public string Title { get; set; } = null!; + public string Notes { get; set; } = null!; + public bool Completed { get; set; } + public DateTime? CompletionDate { get; set; } + public int? CompletedByPersonId { get; set; } + [ForeignKey("CompletedByPersonId")] + public Person CompletedBy { get; set; } = null!; } diff --git a/Sample/DoNotMapClrTypeAttribute.cs b/Sample/DoNotMapClrTypeAttribute.cs index 7fc46fd..7f6a87d 100644 --- a/Sample/DoNotMapClrTypeAttribute.cs +++ b/Sample/DoNotMapClrTypeAttribute.cs @@ -1,16 +1,10 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Threading.Tasks; +namespace Sample; -namespace Sample +/// +/// Indicates that GetClrTypeMappings should +/// skip this class when scanning an assembly for CLR type mappings. +/// +[AttributeUsage(AttributeTargets.Class, AllowMultiple = false, Inherited = true)] +public class DoNotMapClrTypeAttribute : Attribute { - /// - /// Indicates that GetClrTypeMappings should - /// skip this class when scanning an assembly for CLR type mappings. - /// - [AttributeUsage(AttributeTargets.Class, AllowMultiple = false, Inherited = true)] - public class DoNotMapClrTypeAttribute : Attribute - { - } } diff --git a/Sample/GraphTypes/MutationType.cs b/Sample/GraphTypes/MutationType.cs index 181e2cf..7d0c838 100644 --- a/Sample/GraphTypes/MutationType.cs +++ b/Sample/GraphTypes/MutationType.cs @@ -1,55 +1,52 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Threading.Tasks; -using GraphQL.Types; +using GraphQL; using GraphQL.DI; -using Sample.DbModels; using Microsoft.EntityFrameworkCore; -using GraphQL; -using System.Threading; +using Sample.DbModels; + +namespace Sample.GraphTypes; -namespace Sample.GraphTypes +public class MutationType : DIObjectGraphType { } +public class Mutation : DIObjectGraphBase { - public class MutationType : DIObjectGraphType { } - public class Mutation : DIObjectGraphBase + private readonly TodoDbContext _db; + + public Mutation(TodoDbContext db) { - public readonly TodoDbContext _db; - public Mutation(TodoDbContext db) => _db = db; + _db = db; + } - public async Task AddTodoAsync([Required] string title, string notes, CancellationToken cancellationToken) - { - var todo = new Todo { - Title = title, - Notes = notes, - }; - _db.Add(todo); - await _db.SaveChangesAsync(cancellationToken); - return todo; - } + public async Task AddTodoAsync([Required] string title, string notes, CancellationToken cancellationToken) + { + var todo = new Todo { + Title = title, + Notes = notes, + }; + _db.Add(todo); + await _db.SaveChangesAsync(cancellationToken); + return todo; + } - public async Task DeleteTodoAsync(int id, CancellationToken cancellationToken) - { - var todo = await _db.Set().Where(x => x.Id == id).SingleOrDefaultAsync(); - if (todo == null) - return false; - _db.Set().Remove(todo); - await _db.SaveChangesAsync(cancellationToken); - return true; - } + public async Task DeleteTodoAsync(int id, CancellationToken cancellationToken) + { + var todo = await _db.Set().Where(x => x.Id == id).SingleOrDefaultAsync(cancellationToken); + if (todo == null) + return false; + _db.Set().Remove(todo); + await _db.SaveChangesAsync(cancellationToken); + return true; + } - public async Task SetCompleteAsync(int id, int completedByPersonId, CancellationToken cancellationToken) - { - var todo = await _db.Set().Where(x => x.Id == id).SingleOrDefaultAsync(cancellationToken); - if (todo == null) - return null; - if (todo.Completed) - throw new ExecutionError($"Task id {id} has already been completed"); - todo.Completed = true; - todo.CompletedByPersonId = completedByPersonId; - todo.CompletionDate = DateTime.Now; - await _db.SaveChangesAsync(cancellationToken); - return todo; - } + public async Task SetCompleteAsync(int id, int completedByPersonId, CancellationToken cancellationToken) + { + var todo = await _db.Set().Where(x => x.Id == id).SingleOrDefaultAsync(cancellationToken); + if (todo == null) + return null; + if (todo.Completed) + throw new ExecutionError($"Task id {id} has already been completed"); + todo.Completed = true; + todo.CompletedByPersonId = completedByPersonId; + todo.CompletionDate = DateTime.Now; + await _db.SaveChangesAsync(cancellationToken); + return todo; } } diff --git a/Sample/GraphTypes/PersonType.cs b/Sample/GraphTypes/PersonType.cs index d8d1a08..d75946f 100644 --- a/Sample/GraphTypes/PersonType.cs +++ b/Sample/GraphTypes/PersonType.cs @@ -1,18 +1,13 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Threading.Tasks; using GraphQL.Types; using Sample.DbModels; -namespace Sample.GraphTypes +namespace Sample.GraphTypes; + +public class PersonType : ObjectGraphType { - public class PersonType : ObjectGraphType + public PersonType() { - public PersonType() - { - Field(x => x.Id); - Field(x => x.Name); - } + Field(x => x.Id); + Field(x => x.Name); } } diff --git a/Sample/GraphTypes/QueryType.cs b/Sample/GraphTypes/QueryType.cs index 7dda6c8..1ff320a 100644 --- a/Sample/GraphTypes/QueryType.cs +++ b/Sample/GraphTypes/QueryType.cs @@ -1,40 +1,33 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Threading.Tasks; -using GraphQL.Types; using GraphQL.DI; -using Sample.DbModels; using Microsoft.EntityFrameworkCore; -using System.Threading; +using Sample.DbModels; -namespace Sample.GraphTypes +namespace Sample.GraphTypes; + +public class QueryType : DIObjectGraphType { } +public class Query : DIObjectGraphBase { - public class QueryType : DIObjectGraphType { } - public class Query : DIObjectGraphBase + private readonly TodoDbContext _db; + public Query(TodoDbContext db) { - private readonly TodoDbContext _db; - public Query(TodoDbContext db) - { - _db = db; - } + _db = db; + } - public async Task> TodosAsync(int? id, IEnumerable ids, int? completedByPersonId, CancellationToken cancellationToken) - { - IQueryable query = _db.Set(); - if (id.HasValue) - query = query.Where(x => x.Id == id); - if (ids != null) - query = query.Where(x => ids.Contains(x.Id)); - if (completedByPersonId != null) - query = query.Where(x => x.CompletedByPersonId == completedByPersonId); + public async Task> TodosAsync(int? id, IEnumerable ids, int? completedByPersonId, CancellationToken cancellationToken) + { + IQueryable query = _db.Set(); + if (id.HasValue) + query = query.Where(x => x.Id == id); + if (ids != null) + query = query.Where(x => ids.Contains(x.Id)); + if (completedByPersonId != null) + query = query.Where(x => x.CompletedByPersonId == completedByPersonId); - return await query.ToListAsync(cancellationToken); - } + return await query.ToListAsync(cancellationToken); + } - public async Task TodoAsync(int id, CancellationToken cancellationToken) - { - return await _db.Set().Where(x => x.Id == id).SingleOrDefaultAsync(cancellationToken); - } + public async Task TodoAsync(int id, CancellationToken cancellationToken) + { + return await _db.Set().Where(x => x.Id == id).SingleOrDefaultAsync(cancellationToken); } } diff --git a/Sample/GraphTypes/TodoType.cs b/Sample/GraphTypes/TodoType.cs index 135c4c5..1a34346 100644 --- a/Sample/GraphTypes/TodoType.cs +++ b/Sample/GraphTypes/TodoType.cs @@ -1,40 +1,38 @@ -using System; using GraphQL.DataLoader; using GraphQL.DI; using Sample.DataLoaders; using Sample.DbModels; -namespace Sample.GraphTypes +namespace Sample.GraphTypes; + +public class TodoType : DIObjectGraphType { - public class TodoType : DIObjectGraphType + // mix and match if you like + public TodoType() { - // mix and match if you like - public TodoType() - { - // some properties defined traditionally - Field(x => x.Id); - Field(x => x.Title); - Field(x => x.Notes, true); - } + // some properties defined traditionally + Field(x => x.Id); + Field(x => x.Title); + Field(x => x.Notes, true); } +} - public class TodoObjType : DIObjectGraphBase - { - private readonly PersonDataLoader _personDataLoader; - public TodoObjType(PersonDataLoader personDataLoader) => _personDataLoader = personDataLoader; +public class TodoObjType : DIObjectGraphBase +{ + private readonly PersonDataLoader _personDataLoader; + public TodoObjType(PersonDataLoader personDataLoader) => _personDataLoader = personDataLoader; - // some properties defined here - // use 'static' and it won't create a dbcontext - public static bool Completed(Todo source) => source.Completed; - public static DateTime? CompletedOn(Todo source) => source.CompletionDate; + // some properties defined here + // use 'static' and it won't create a dbcontext + public static bool Completed(Todo source) => source.Completed; + public static DateTime? CompletedOn(Todo source) => source.CompletionDate; - // here it is using an instance field "Source" - public IDataLoaderResult CompletedBy() - { - if (!Source.CompletedByPersonId.HasValue) - return null; + // here it is using an instance field "Source" + public IDataLoaderResult? CompletedBy() + { + if (!Source.CompletedByPersonId.HasValue) + return null; - return _personDataLoader.LoadAsync(Source.CompletedByPersonId.Value); - } + return _personDataLoader.LoadAsync(Source.CompletedByPersonId.Value); } } diff --git a/Sample/Pages/Error.cshtml.cs b/Sample/Pages/Error.cshtml.cs index 09aa704..1a90cbc 100644 --- a/Sample/Pages/Error.cshtml.cs +++ b/Sample/Pages/Error.cshtml.cs @@ -1,11 +1,6 @@ +using System.Diagnostics; using Microsoft.AspNetCore.Mvc; using Microsoft.AspNetCore.Mvc.RazorPages; -using Microsoft.Extensions.Logging; -using System; -using System.Collections.Generic; -using System.Diagnostics; -using System.Linq; -using System.Threading.Tasks; namespace Sample.Pages { @@ -13,7 +8,7 @@ namespace Sample.Pages [IgnoreAntiforgeryToken] public class ErrorModel : PageModel { - public string RequestId { get; set; } + public string? RequestId { get; set; } public bool ShowRequestId => !string.IsNullOrEmpty(RequestId); diff --git a/Sample/Program.cs b/Sample/Program.cs index 3463b79..265efdd 100644 --- a/Sample/Program.cs +++ b/Sample/Program.cs @@ -1,26 +1,13 @@ -using Microsoft.AspNetCore.Hosting; -using Microsoft.Extensions.Configuration; -using Microsoft.Extensions.Hosting; -using Microsoft.Extensions.Logging; -using System; -using System.Collections.Generic; -using System.Linq; -using System.Threading.Tasks; +namespace Sample; -namespace Sample +public class Program { - public class Program + public static void Main(string[] args) { - public static void Main(string[] args) - { - CreateHostBuilder(args).Build().Run(); - } - - public static IHostBuilder CreateHostBuilder(string[] args) => - Host.CreateDefaultBuilder(args) - .ConfigureWebHostDefaults(webBuilder => - { - webBuilder.UseStartup(); - }); + CreateHostBuilder(args).Build().Run(); } + + public static IHostBuilder CreateHostBuilder(string[] args) => + Host.CreateDefaultBuilder(args) + .ConfigureWebHostDefaults(webBuilder => webBuilder.UseStartup()); } diff --git a/Sample/RequiredAttribute.cs b/Sample/RequiredAttribute.cs index 923dc88..ba102fc 100644 --- a/Sample/RequiredAttribute.cs +++ b/Sample/RequiredAttribute.cs @@ -1,9 +1,8 @@ using GraphQL.Types; -namespace Sample +namespace Sample; + +public class RequiredAttribute : GraphQL.GraphQLAttribute { - public class RequiredAttribute : GraphQL.GraphQLAttribute - { - public override void Modify(TypeInformation typeInformation) => typeInformation.IsNullable = false; - } + public override void Modify(TypeInformation typeInformation) => typeInformation.IsNullable = false; } diff --git a/Sample/Sample.csproj b/Sample/Sample.csproj index ca842a7..2dfb55c 100644 --- a/Sample/Sample.csproj +++ b/Sample/Sample.csproj @@ -1,20 +1,15 @@ - net5.0 - - - - 1701;1702;CS1591 + net6.0 + $(NoWarn);1701;1702;CS1591 - - - - - + + + diff --git a/Sample/Startup.cs b/Sample/Startup.cs index 5a00f11..dc04385 100644 --- a/Sample/Startup.cs +++ b/Sample/Startup.cs @@ -3,73 +3,68 @@ using GraphQL; using GraphQL.AspNetCore3; using GraphQL.DI; -using GraphQL.MicrosoftDI; -using GraphQL.SystemTextJson; -using Microsoft.AspNetCore.Builder; -using Microsoft.AspNetCore.Hosting; -using Microsoft.Extensions.Configuration; -using Microsoft.Extensions.DependencyInjection; -using Microsoft.Extensions.Hosting; using Sample.DataLoaders; -namespace Sample +namespace Sample; + +public class Startup { - public class Startup + public Startup(IConfiguration configuration) { - public Startup(IConfiguration configuration) - { - Configuration = configuration; - } + Configuration = configuration; + } - public IConfiguration Configuration { get; } + public IConfiguration Configuration { get; } - // This method gets called by the runtime. Use this method to add services to the container. - public void ConfigureServices(IServiceCollection services) - { - services.AddRazorPages(); + // This method gets called by the runtime. Use this method to add services to the container. + public void ConfigureServices(IServiceCollection services) + { + services.AddRazorPages(); - services.AddGraphQL(b => b - .AddSchema() - .ConfigureExecutionOptions(opts => opts.UnhandledExceptionDelegate = async e => Debug.WriteLine($"Unhandled exception:\n{e.Exception}\n")) - .AddSystemTextJson() - .AddDIGraphTypes() - .AddClrTypeMappings() - .AddGraphTypes()); + services.AddGraphQL(b => b + .AddSchema() + .ConfigureExecutionOptions(opts => opts.UnhandledExceptionDelegate = e => { + Debug.WriteLine($"Unhandled exception:\n{e.Exception}\n"); + return Task.CompletedTask; + }) + .AddSystemTextJson() + .AddDIGraphTypes() + .AddClrTypeMappings() + .AddGraphTypes()); - //construct temporary database with scoped dbcontext instances - services.AddSingleton(_ => new SqlInstance(builder => new TodoDbContext(builder.Options))); - services.AddSingleton(serviceProvider => serviceProvider.GetRequiredService>().Build().GetAwaiter().GetResult()); - services.AddScoped(serviceProvider => serviceProvider.GetRequiredService>().NewDbContext()); + //construct temporary database with scoped dbcontext instances + services.AddSingleton(_ => new SqlInstance(builder => new TodoDbContext(builder.Options))); + services.AddSingleton(serviceProvider => serviceProvider.GetRequiredService>().Build().GetAwaiter().GetResult()); + services.AddScoped(serviceProvider => serviceProvider.GetRequiredService>().NewDbContext()); - //add some scoped data loaders - services.AddScoped(); - } + //add some scoped data loaders + services.AddScoped(); + } - // This method gets called by the runtime. Use this method to configure the HTTP request pipeline. - public void Configure(IApplicationBuilder app, IWebHostEnvironment env) + // This method gets called by the runtime. Use this method to configure the HTTP request pipeline. + public void Configure(IApplicationBuilder app, IWebHostEnvironment env) + { + if (env.IsDevelopment()) + { + app.UseDeveloperExceptionPage(); + } + else { - if (env.IsDevelopment()) - { - app.UseDeveloperExceptionPage(); - } - else - { - app.UseExceptionHandler("/Error"); - } + app.UseExceptionHandler("/Error"); + } - app.UseStaticFiles(); + app.UseStaticFiles(); - app.UseRouting(); + app.UseRouting(); - app.UseAuthorization(); + app.UseAuthorization(); - app.UseGraphQL(); - app.UseGraphQLGraphiQL(); + app.UseGraphQL(); + app.UseGraphQLGraphiQL(); - app.UseEndpoints(endpoints => - { - endpoints.MapRazorPages(); - }); - } + app.UseEndpoints(endpoints => + { + endpoints.MapRazorPages(); + }); } } diff --git a/Sample/TodoDbContext.cs b/Sample/TodoDbContext.cs index 8eea348..3b26df9 100644 --- a/Sample/TodoDbContext.cs +++ b/Sample/TodoDbContext.cs @@ -1,39 +1,34 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Threading.Tasks; using Microsoft.EntityFrameworkCore; using Sample.DbModels; -namespace Sample +namespace Sample; + +public class TodoDbContext : DbContext { - public class TodoDbContext : DbContext - { - public DbSet Todos { get; set; } = null!; - public DbSet People { get; set; } = null!; + public DbSet Todos { get; set; } = null!; + public DbSet People { get; set; } = null!; - private Todo[] _initialTodoData; - private Person[] _initialPersonData; + private readonly Todo[] _initialTodoData; + private readonly Person[] _initialPersonData; - public TodoDbContext(DbContextOptions options) : - base(options) - { - _initialTodoData = new[] { - new Todo { Id = 1, Title = "Cut the grass" }, - new Todo { Id = 2, Title = "Eat some tacos", Notes = "With lettuce" }, - new Todo { Id = 3, Title = "Write a memo", Completed = true, CompletedByPersonId = 2, CompletionDate = new DateTime(2021, 7, 2, 12, 53, 0) }, - }; - _initialPersonData = new[] { - new Person { Id = 1, Name = "John" }, - new Person { Id = 2, Name = "Anne" }, - }; - } + public TodoDbContext(DbContextOptions options) : + base(options) + { + _initialTodoData = new[] { + new Todo { Id = 1, Title = "Cut the grass" }, + new Todo { Id = 2, Title = "Eat some tacos", Notes = "With lettuce" }, + new Todo { Id = 3, Title = "Write a memo", Completed = true, CompletedByPersonId = 2, CompletionDate = new DateTime(2021, 7, 2, 12, 53, 0) }, + }; + _initialPersonData = new[] { + new Person { Id = 1, Name = "John" }, + new Person { Id = 2, Name = "Anne" }, + }; + } - protected override void OnModelCreating(ModelBuilder model) - { - model.Entity().HasData(_initialTodoData); + protected override void OnModelCreating(ModelBuilder modelBuilder) + { + modelBuilder.Entity().HasData(_initialTodoData); - model.Entity().HasData(_initialPersonData); - } + modelBuilder.Entity().HasData(_initialPersonData); } } diff --git a/Sample/TodoSchema.cs b/Sample/TodoSchema.cs index 6561bf2..ec465c1 100644 --- a/Sample/TodoSchema.cs +++ b/Sample/TodoSchema.cs @@ -1,15 +1,13 @@ -using System; using GraphQL.Types; using Sample.GraphTypes; -namespace Sample +namespace Sample; + +public class TodoSchema : Schema { - public class TodoSchema : Schema + public TodoSchema(IServiceProvider serviceProvider, QueryType query, MutationType mutation) : base(serviceProvider) { - public TodoSchema(IServiceProvider serviceProvider, QueryType query, MutationType mutation) : base(serviceProvider) - { - Query = query; - Mutation = mutation; - } + Query = query; + Mutation = mutation; } } diff --git a/src/GraphQL.DI/DIGraphAttribute.cs b/src/GraphQL.DI/DIGraphAttribute.cs index c8003e8..f3f012b 100644 --- a/src/GraphQL.DI/DIGraphAttribute.cs +++ b/src/GraphQL.DI/DIGraphAttribute.cs @@ -1,41 +1,38 @@ -using System; -using System.Linq; using GraphQL.Types; -namespace GraphQL.DI +namespace GraphQL.DI; + +/// +/// Marks a method's return graph type to be a specified DI graph type. +/// Useful when the return type cannot be inferred (often when it is of type ). +/// +[AttributeUsage(AttributeTargets.Method | AttributeTargets.Field | AttributeTargets.Property)] +public class DIGraphAttribute : GraphQLAttribute { /// /// Marks a method's return graph type to be a specified DI graph type. - /// Useful when the return type cannot be inferred (often when it is of type ). /// - [AttributeUsage(AttributeTargets.Method | AttributeTargets.Field | AttributeTargets.Property)] - public class DIGraphAttribute : GraphQLAttribute + /// A type that inherits . + public DIGraphAttribute(Type graphBaseType) { - /// - /// Marks a method's return graph type to be a specified DI graph type. - /// - /// A type that inherits . - public DIGraphAttribute(Type graphBaseType) - { - GraphBaseType = graphBaseType; - } + GraphBaseType = graphBaseType; + } - /// - public override void Modify(TypeInformation typeInformation) - { - if (typeInformation.IsInputType) - return; - var iface = GraphBaseType.GetInterfaces().FirstOrDefault(x => x.IsGenericType && x.GetGenericTypeDefinition() == typeof(IDIObjectGraphBase<>)); - if (iface != null) { - typeInformation.GraphType = typeof(DIObjectGraphType<,>).MakeGenericType(GraphBaseType, iface.GetGenericArguments()[0]); - } else { - throw new InvalidOperationException($"Type '{GraphBaseType.Name}' does not implement {nameof(IDIObjectGraphBase)}; check {nameof(DIGraphAttribute)} attribute marked on '{typeInformation.MemberInfo.DeclaringType.Name}.{typeInformation.MemberInfo.Name}'."); - } + /// + public override void Modify(TypeInformation typeInformation) + { + if (typeInformation.IsInputType) + return; + var iface = GraphBaseType.GetInterfaces().FirstOrDefault(x => x.IsGenericType && x.GetGenericTypeDefinition() == typeof(IDIObjectGraphBase<>)); + if (iface != null) { + typeInformation.GraphType = typeof(DIObjectGraphType<,>).MakeGenericType(GraphBaseType, iface.GetGenericArguments()[0]); + } else { + throw new InvalidOperationException($"Type '{GraphBaseType.Name}' does not implement {nameof(IDIObjectGraphBase)}; check {nameof(DIGraphAttribute)} attribute marked on '{typeInformation.MemberInfo.DeclaringType.Name}.{typeInformation.MemberInfo.Name}'."); } - - /// - /// The DI graph type that inherits . - /// - public Type GraphBaseType { get; } } + + /// + /// The DI graph type that inherits . + /// + public Type GraphBaseType { get; } } diff --git a/src/GraphQL.DI/DIGraphTypeMappingProvider.cs b/src/GraphQL.DI/DIGraphTypeMappingProvider.cs index e0195df..9c6a2d1 100644 --- a/src/GraphQL.DI/DIGraphTypeMappingProvider.cs +++ b/src/GraphQL.DI/DIGraphTypeMappingProvider.cs @@ -1,19 +1,16 @@ -using System; -using System.Collections.Generic; using GraphQL.Types; -namespace GraphQL.DI -{ - internal class DIGraphTypeMappingProvider : IGraphTypeMappingProvider - { - private readonly Dictionary _typeDictionary; +namespace GraphQL.DI; - public DIGraphTypeMappingProvider(Dictionary typeDictionary) - { - _typeDictionary = typeDictionary; - } +internal class DIGraphTypeMappingProvider : IGraphTypeMappingProvider +{ + private readonly Dictionary _typeDictionary; - public Type? GetGraphTypeFromClrType(Type clrType, bool isInputType, Type? preferredGraphType) - => !isInputType && _typeDictionary.TryGetValue(clrType, out var graphType) ? graphType : preferredGraphType; + public DIGraphTypeMappingProvider(Dictionary typeDictionary) + { + _typeDictionary = typeDictionary; } + + public Type? GetGraphTypeFromClrType(Type clrType, bool isInputType, Type? preferredGraphType) + => !isInputType && _typeDictionary.TryGetValue(clrType, out var graphType) ? graphType : preferredGraphType; } diff --git a/src/GraphQL.DI/DIObjectGraphBase.cs b/src/GraphQL.DI/DIObjectGraphBase.cs index cc757bf..676a1a3 100644 --- a/src/GraphQL.DI/DIObjectGraphBase.cs +++ b/src/GraphQL.DI/DIObjectGraphBase.cs @@ -1,65 +1,65 @@ -using System; -using System.Collections.Generic; -using System.Threading; +using System.Security.Claims; using GraphQL.Execution; using GraphQL.Instrumentation; using GraphQL.Types; using GraphQLParser.AST; -namespace GraphQL.DI +namespace GraphQL.DI; + +/// +/// This is a required base type of all DI-created graph types. may be +/// used if the type is . +/// +public abstract class DIObjectGraphBase : IDIObjectGraphBase, IResolveFieldContext { /// - /// This is a required base type of all DI-created graph types. may be - /// used if the type is . + /// The for the current field. /// - public abstract class DIObjectGraphBase : IDIObjectGraphBase, IResolveFieldContext - { - /// - /// The for the current field. - /// - public IResolveFieldContext Context { get; private set; } = null!; + public IResolveFieldContext Context { get; private set; } = null!; - /// - public TSource Source => (TSource)Context.Source!; + /// + public TSource Source => (TSource)Context.Source!; - /// - public CancellationToken RequestAborted => Context.CancellationToken; + /// + public CancellationToken RequestAborted => Context.CancellationToken; - /// - public IDictionary UserContext => Context.UserContext; + /// + public IDictionary UserContext => Context.UserContext; - /// - public Metrics Metrics => Context.Metrics; + /// + public ClaimsPrincipal? User => Context.User; - IResolveFieldContext IDIObjectGraphBase.Context { get => Context; set => Context = value; } - GraphQLField IResolveFieldContext.FieldAst => Context.FieldAst; - FieldType IResolveFieldContext.FieldDefinition => Context.FieldDefinition; - IObjectGraphType IResolveFieldContext.ParentType => Context.ParentType; - IResolveFieldContext IResolveFieldContext.Parent => Context.Parent!; - IDictionary? IResolveFieldContext.Arguments => Context.Arguments; - object? IResolveFieldContext.RootValue => Context.RootValue; - object IResolveFieldContext.Source => Context.Source!; - ISchema IResolveFieldContext.Schema => Context.Schema; - GraphQLDocument IResolveFieldContext.Document => Context.Document; - GraphQLOperationDefinition IResolveFieldContext.Operation => Context.Operation; - Validation.Variables IResolveFieldContext.Variables => Context.Variables; - CancellationToken IResolveFieldContext.CancellationToken => Context.CancellationToken; - ExecutionErrors IResolveFieldContext.Errors => Context.Errors; - IEnumerable IResolveFieldContext.Path => Context.Path; - IEnumerable IResolveFieldContext.ResponsePath => Context.ResponsePath; - IServiceProvider IResolveFieldContext.RequestServices => Context.RequestServices!; - IExecutionArrayPool IResolveFieldContext.ArrayPool => Context.ArrayPool; - IDictionary? IResolveFieldContext.Directives => Context.Directives; - Dictionary? IResolveFieldContext.SubFields => Context.SubFields; - IReadOnlyDictionary IResolveFieldContext.InputExtensions => Context.InputExtensions; - IDictionary IResolveFieldContext.OutputExtensions => Context.OutputExtensions; - } + /// + public Metrics Metrics => Context.Metrics; - /// - /// This is a required base type of all DI-created graph types. may be - /// used when the type is not . - /// - public abstract class DIObjectGraphBase : DIObjectGraphBase - { - } + IResolveFieldContext IDIObjectGraphBase.Context { get => Context; set => Context = value; } + GraphQLField IResolveFieldContext.FieldAst => Context.FieldAst; + FieldType IResolveFieldContext.FieldDefinition => Context.FieldDefinition; + IObjectGraphType IResolveFieldContext.ParentType => Context.ParentType; + IResolveFieldContext IResolveFieldContext.Parent => Context.Parent!; + IDictionary? IResolveFieldContext.Arguments => Context.Arguments; + object? IResolveFieldContext.RootValue => Context.RootValue; + object IResolveFieldContext.Source => Context.Source!; + ISchema IResolveFieldContext.Schema => Context.Schema; + GraphQLDocument IResolveFieldContext.Document => Context.Document; + GraphQLOperationDefinition IResolveFieldContext.Operation => Context.Operation; + Validation.Variables IResolveFieldContext.Variables => Context.Variables; + CancellationToken IResolveFieldContext.CancellationToken => Context.CancellationToken; + ExecutionErrors IResolveFieldContext.Errors => Context.Errors; + IEnumerable IResolveFieldContext.Path => Context.Path; + IEnumerable IResolveFieldContext.ResponsePath => Context.ResponsePath; + IServiceProvider IResolveFieldContext.RequestServices => Context.RequestServices!; + IExecutionArrayPool IResolveFieldContext.ArrayPool => Context.ArrayPool; + IDictionary? IResolveFieldContext.Directives => Context.Directives; + Dictionary? IResolveFieldContext.SubFields => Context.SubFields; + IReadOnlyDictionary IResolveFieldContext.InputExtensions => Context.InputExtensions; + IDictionary IResolveFieldContext.OutputExtensions => Context.OutputExtensions; +} + +/// +/// This is a required base type of all DI-created graph types. may be +/// used when the type is not . +/// +public abstract class DIObjectGraphBase : DIObjectGraphBase +{ } diff --git a/src/GraphQL.DI/DIObjectGraphType.cs b/src/GraphQL.DI/DIObjectGraphType.cs index 52bcc38..2d0acc3 100644 --- a/src/GraphQL.DI/DIObjectGraphType.cs +++ b/src/GraphQL.DI/DIObjectGraphType.cs @@ -1,97 +1,92 @@ -using System; -using System.Collections.Generic; -using System.Linq; using System.Linq.Expressions; using System.Reflection; -using System.Threading.Tasks; using GraphQL.Types; using Microsoft.Extensions.DependencyInjection; -namespace GraphQL.DI +namespace GraphQL.DI; + +/// +/// Wraps a graph type for use with GraphQL. This class should be registered as a singleton +/// within your dependency injection provider. +/// +public class DIObjectGraphType : DIObjectGraphType where TDIGraph : IDIObjectGraphBase { - /// - /// Wraps a graph type for use with GraphQL. This class should be registered as a singleton - /// within your dependency injection provider. - /// - public class DIObjectGraphType : DIObjectGraphType where TDIGraph : IDIObjectGraphBase - { - } +} - /// - /// Wraps a graph type for use with GraphQL. This class should be registered as a singleton - /// within your dependency injection provider. - /// - public class DIObjectGraphType : AutoRegisteringObjectGraphType - where TDIGraph : IDIObjectGraphBase +/// +/// Wraps a graph type for use with GraphQL. This class should be registered as a singleton +/// within your dependency injection provider. +/// +public class DIObjectGraphType : AutoRegisteringObjectGraphType + where TDIGraph : IDIObjectGraphBase +{ + /// + protected override void ConfigureGraph() { - /// - protected override void ConfigureGraph() - { - // do not configure attributes set on TSource - // base.ConfigureGraph(); + // do not configure attributes set on TSource + // base.ConfigureGraph(); - // configure attributes set on DIObject instead - var name = typeof(TDIGraph).GraphQLName(); - if (name.EndsWith("Graph") && name.Length > 5) - name = name.Substring(0, name.Length - 5); - Name = name; - Description ??= typeof(TDIGraph).Description(); - DeprecationReason ??= typeof(TDIGraph).ObsoleteMessage(); - var attributes = typeof(TDIGraph).GetCustomAttributes(); - foreach (var attr in attributes) { - attr.Modify(this); - } + // configure attributes set on DIObject instead + var name = typeof(TDIGraph).GraphQLName(); + if (name.EndsWith("Graph", StringComparison.Ordinal) && name.Length > 5) + name = name.Substring(0, name.Length - 5); + Name = name; + Description ??= typeof(TDIGraph).Description(); + DeprecationReason ??= typeof(TDIGraph).ObsoleteMessage(); + var attributes = typeof(TDIGraph).GetCustomAttributes(); + foreach (var attr in attributes) { + attr.Modify(this); } + } - // only process methods declared directly on TDIGraph -- not anything declared on TSource - /// - protected override IEnumerable GetRegisteredMembers() - { - // only methods are supported - var methods = typeof(TDIGraph).GetMethods(BindingFlags.Public | BindingFlags.Instance | BindingFlags.Static | BindingFlags.DeclaredOnly) - .Where(x => - !x.ContainsGenericParameters && // exclude methods with open generics - !x.IsSpecialName && // exclude methods generated for properties - x.ReturnType != typeof(void) && // exclude methods which do not return a value - x.ReturnType != typeof(Task) && // exclude methods which do not return a value - x.GetBaseDefinition() == x && // exclude methods which override an inherited class' method (e.g. GetHashCode) - // exclude methods generated for record types: bool Equals(TSourceType) - !(x.Name == "Equals" && !x.IsStatic && x.GetParameters().Length == 1 && x.GetParameters()[0].ParameterType == typeof(TDIGraph) && x.ReturnType == typeof(bool)) && - x.Name != "$"); // exclude methods generated for record types - return methods; - } + // only process methods declared directly on TDIGraph -- not anything declared on TSource + /// + protected override IEnumerable GetRegisteredMembers() + { + // only methods are supported + var methods = typeof(TDIGraph).GetMethods(BindingFlags.Public | BindingFlags.Instance | BindingFlags.Static | BindingFlags.DeclaredOnly) + .Where(x => + !x.ContainsGenericParameters && // exclude methods with open generics + !x.IsSpecialName && // exclude methods generated for properties + x.ReturnType != typeof(void) && // exclude methods which do not return a value + x.ReturnType != typeof(Task) && // exclude methods which do not return a value + x.GetBaseDefinition() == x && // exclude methods which override an inherited class' method (e.g. GetHashCode) + // exclude methods generated for record types: bool Equals(TSourceType) + !(x.Name == "Equals" && !x.IsStatic && x.GetParameters().Length == 1 && x.GetParameters()[0].ParameterType == typeof(TDIGraph) && x.ReturnType == typeof(bool)) && + x.Name != "$"); // exclude methods generated for record types + return methods; + } + + // each field resolver will build a new instance of DIObject + /// + protected override LambdaExpression BuildMemberInstanceExpression(MemberInfo memberInfo) + => (Expression>)((IResolveFieldContext context) => MemberInstanceFunc(context)); - // each field resolver will build a new instance of DIObject - /// - protected override LambdaExpression BuildMemberInstanceExpression(MemberInfo memberInfo) - => (Expression>)((IResolveFieldContext context) => MemberInstanceFunc(context)); + /// + private static TDIGraph MemberInstanceFunc(IResolveFieldContext context) + { + // create a new instance of DIObject, filling in any constructor arguments from DI + var graph = ActivatorUtilities.GetServiceOrCreateInstance(context.RequestServices ?? throw new MissingRequestServicesException()); + // set the context + graph.Context = context; + // return the object + return graph; + } - /// - private TDIGraph MemberInstanceFunc(IResolveFieldContext context) + /// + protected override ArgumentInformation GetArgumentInformation(FieldType fieldType, ParameterInfo parameterInfo) + { + var typeInformation = GetTypeInformation(parameterInfo); + var argumentInfo = new ArgumentInformation(parameterInfo, typeof(TSource), fieldType, typeInformation); + if (argumentInfo.ParameterInfo.ParameterType == typeof(IServiceProvider)) { - // create a new instance of DIObject, filling in any constructor arguments from DI - var graph = ActivatorUtilities.GetServiceOrCreateInstance(context.RequestServices ?? throw new MissingRequestServicesException()); - // set the context - graph.Context = context; - // return the object - return graph; + argumentInfo.SetDelegate(context => context.RequestServices ?? throw new MissingRequestServicesException()); } - - /// - protected override ArgumentInformation GetArgumentInformation(FieldType fieldType, ParameterInfo parameterInfo) + if (argumentInfo.ParameterInfo.Name == "source" && argumentInfo.ParameterInfo.ParameterType == typeof(TSource)) { - var typeInformation = GetTypeInformation(parameterInfo); - var argumentInfo = new ArgumentInformation(parameterInfo, typeof(TSource), fieldType, typeInformation); - if (argumentInfo.ParameterInfo.ParameterType == typeof(IServiceProvider)) - { - argumentInfo.SetDelegate(context => context.RequestServices ?? throw new MissingRequestServicesException()); - } - if (argumentInfo.ParameterInfo.Name == "source" && argumentInfo.ParameterInfo.ParameterType == typeof(TSource)) - { - argumentInfo.SetDelegate(context => (TSource)context.Source); - } - argumentInfo.ApplyAttributes(); - return argumentInfo; + argumentInfo.SetDelegate(context => (TSource?)context.Source); } + argumentInfo.ApplyAttributes(); + return argumentInfo; } } diff --git a/src/GraphQL.DI/GraphQLBuilderExtensions.cs b/src/GraphQL.DI/GraphQLBuilderExtensions.cs index 358ad8a..9c694a1 100644 --- a/src/GraphQL.DI/GraphQLBuilderExtensions.cs +++ b/src/GraphQL.DI/GraphQLBuilderExtensions.cs @@ -1,61 +1,58 @@ -using System; -using System.Linq; using System.Reflection; -namespace GraphQL.DI +namespace GraphQL.DI; + +/// +/// Provides extension methods to configure GraphQL.NET services within a dependency injection framework. +/// +public static class GraphQLBuilderExtensions { /// - /// Provides extension methods to configure GraphQL.NET services within a dependency injection framework. + /// Registers and + /// as generic types. /// - public static class GraphQLBuilderExtensions + public static IGraphQLBuilder AddDIGraphTypes(this IGraphQLBuilder builder) { - /// - /// Registers and - /// as generic types. - /// - public static IGraphQLBuilder AddDIGraphTypes(this IGraphQLBuilder builder) - { - builder.Services.TryRegister(typeof(DIObjectGraphType<>), typeof(DIObjectGraphType<>), ServiceLifetime.Transient); - builder.Services.TryRegister(typeof(DIObjectGraphType<,>), typeof(DIObjectGraphType<,>), ServiceLifetime.Transient); - return builder; - } + builder.Services.TryRegister(typeof(DIObjectGraphType<>), typeof(DIObjectGraphType<>), ServiceLifetime.Transient); + builder.Services.TryRegister(typeof(DIObjectGraphType<,>), typeof(DIObjectGraphType<,>), ServiceLifetime.Transient); + return builder; + } - /// - /// Scans the calling assembly for classes that implement and - /// registers clr type mappings on the schema between that - /// (constructed from that class and its source type), and the source type. - /// Skips classes where the source type is , or where the class is marked with - /// the , or where another graph type would be automatically mapped - /// to the specified type, or where a graph type has already been registered to the specified clr type. - /// - public static IGraphQLBuilder AddDIClrTypeMappings(this IGraphQLBuilder builder) - => AddDIClrTypeMappings(builder, Assembly.GetCallingAssembly()); + /// + /// Scans the calling assembly for classes that implement and + /// registers clr type mappings on the schema between that + /// (constructed from that class and its source type), and the source type. + /// Skips classes where the source type is , or where the class is marked with + /// the , or where another graph type would be automatically mapped + /// to the specified type, or where a graph type has already been registered to the specified clr type. + /// + public static IGraphQLBuilder AddDIClrTypeMappings(this IGraphQLBuilder builder) + => AddDIClrTypeMappings(builder, Assembly.GetCallingAssembly()); - /// - /// Scans the specified assembly for classes that implement and - /// registers clr type mappings on the schema between that - /// (constructed from that class and its source type), and the source type. - /// Skips classes where the source type is or where the class is marked with - /// the . - /// - public static IGraphQLBuilder AddDIClrTypeMappings(this IGraphQLBuilder builder, Assembly assembly) - { - var types = assembly.GetTypes() - .Where(x => x.IsClass && !x.IsAbstract && typeof(IDIObjectGraphBase).IsAssignableFrom(x)) - .Select(x => { - var iface = x.GetInterfaces().FirstOrDefault(x => x.IsGenericType && x.GetGenericTypeDefinition() == typeof(IDIObjectGraphBase<>)); - return (x, iface?.GetGenericArguments()[0]); - }) - .Where(x => x.SourceType != null && x.SourceType != typeof(object) && !x.DIGraphType.IsDefined(typeof(DoNotMapClrTypeAttribute))) - .Select<(Type DIGraphType, Type? SourceType), (Type ClrType, Type GraphType)>(x => (x.SourceType!, typeof(DIObjectGraphType<,>).MakeGenericType(x.DIGraphType, x.SourceType!))) - .ToDictionary(x => x.ClrType, x => x.GraphType); + /// + /// Scans the specified assembly for classes that implement and + /// registers clr type mappings on the schema between that + /// (constructed from that class and its source type), and the source type. + /// Skips classes where the source type is or where the class is marked with + /// the . + /// + public static IGraphQLBuilder AddDIClrTypeMappings(this IGraphQLBuilder builder, Assembly assembly) + { + var types = assembly.GetTypes() + .Where(x => x.IsClass && !x.IsAbstract && typeof(IDIObjectGraphBase).IsAssignableFrom(x)) + .Select(x => { + var iface = x.GetInterfaces().FirstOrDefault(x => x.IsGenericType && x.GetGenericTypeDefinition() == typeof(IDIObjectGraphBase<>)); + return (x, iface?.GetGenericArguments()[0]); + }) + .Where(x => x.SourceType != null && x.SourceType != typeof(object) && !x.DIGraphType.IsDefined(typeof(DoNotMapClrTypeAttribute))) + .Select<(Type DIGraphType, Type? SourceType), (Type ClrType, Type GraphType)>(x => (x.SourceType!, typeof(DIObjectGraphType<,>).MakeGenericType(x.DIGraphType, x.SourceType!))) + .ToDictionary(x => x.ClrType, x => x.GraphType); - if (types.Count == 0) - return builder; + if (types.Count == 0) + return builder; - builder.AddGraphTypeMappingProvider(new DIGraphTypeMappingProvider(types)); + builder.AddGraphTypeMappingProvider(new DIGraphTypeMappingProvider(types)); - return builder; - } + return builder; } } diff --git a/src/GraphQL.DI/IDIObjectGraphBase.cs b/src/GraphQL.DI/IDIObjectGraphBase.cs index a57ca46..1c88589 100644 --- a/src/GraphQL.DI/IDIObjectGraphBase.cs +++ b/src/GraphQL.DI/IDIObjectGraphBase.cs @@ -1,22 +1,21 @@ -namespace GraphQL.DI +namespace GraphQL.DI; + +/// +/// This is a required base type of all DI-created graph types. +/// may be used when the type is not . +/// +public interface IDIObjectGraphBase { /// - /// This is a required base type of all DI-created graph types. - /// may be used when the type is not . + /// The for the current field. /// - public interface IDIObjectGraphBase - { - /// - /// The for the current field. - /// - public IResolveFieldContext Context { get; set; } - } + public IResolveFieldContext Context { get; set; } +} - /// - /// This is a required base type of all DI-created graph types. - /// may be used if the type is . - /// - public interface IDIObjectGraphBase : IDIObjectGraphBase - { - } +/// +/// This is a required base type of all DI-created graph types. +/// may be used if the type is . +/// +public interface IDIObjectGraphBase : IDIObjectGraphBase +{ } diff --git a/src/GraphQL.DI/Shane32.GraphQL.DI.csproj b/src/GraphQL.DI/Shane32.GraphQL.DI.csproj index 4a5f6f2..1a4363c 100644 --- a/src/GraphQL.DI/Shane32.GraphQL.DI.csproj +++ b/src/GraphQL.DI/Shane32.GraphQL.DI.csproj @@ -5,14 +5,12 @@ An extension to the graphql-dotnet library to allow easy creation of GraphQL endpoints in a style similar to ASP.Net Core controllers - enable GraphQL.DI + true - - - + diff --git a/src/Tests/DIObjectGraphBaseTests.cs b/src/Tests/DIObjectGraphBaseTests.cs index 330d19e..c08489d 100644 --- a/src/Tests/DIObjectGraphBaseTests.cs +++ b/src/Tests/DIObjectGraphBaseTests.cs @@ -1,244 +1,233 @@ -using System; -using System.Collections.Generic; -using System.Threading; -using GraphQL; -using GraphQL.DI; -using GraphQL.Execution; using GraphQL.Instrumentation; -using GraphQL.Types; using GraphQLParser.AST; -using Moq; -using Shouldly; -using Xunit; -namespace DIObjectGraphBaseTests +namespace DIObjectGraphBaseTests; + +public class Properties { - public class Properties - { - private readonly DIObjectGraphBase _graph = new CTest(); - private IResolveFieldContext _graphContext => _graph; - private readonly Mock _mockContext = new Mock(MockBehavior.Strict); - private IResolveFieldContext _context => _mockContext.Object; - - public Properties() - { - ((IDIObjectGraphBase)_graph).Context = _context; - ((IDIObjectGraphBase)_graph).Context.ShouldBe(_context); - } - - [Fact] - public void Context() - { - _graph.Context.ShouldBe(_context); - } - - [Fact] - public void RequestAborted() - { - var token = new CancellationTokenSource().Token; - _mockContext.Setup(x => x.CancellationToken).Returns(token); - _graph.RequestAborted.ShouldBe(token); - } - - [Fact] - public void Metrics() - { - var metrics = new Metrics(); - _mockContext.Setup(x => x.Metrics).Returns(metrics); - _graph.Metrics.ShouldBe(metrics); - } - - [Fact] - public void UserContext() - { - var userContext = new Dictionary(); - _mockContext.Setup(x => x.UserContext).Returns(userContext); - _graph.UserContext.ShouldBe(userContext); - } - - [Fact] - public void Source() - { - var source = new object(); - _mockContext.Setup(x => x.Source).Returns(source); - _graph.Source.ShouldBe(source); - } - - [Fact] - public void SourceTyped() - { - _mockContext.Setup(x => x.Source).Returns(3); - var graph = new CTest(); - ((IDIObjectGraphBase)graph).Context = _context; - graph.Source.ShouldBe(3); - } - - [Fact] - public void RFC_FieldAst() - { - var fieldAst = new GraphQLField(); - _mockContext.Setup(x => x.FieldAst).Returns(fieldAst); - _graphContext.FieldAst.ShouldBe(fieldAst); - } - - [Fact] - public void RFC_FieldDefinition() - { - var fieldDef = new FieldType(); - _mockContext.Setup(x => x.FieldDefinition).Returns(fieldDef); - _graphContext.FieldDefinition.ShouldBe(fieldDef); - } - - [Fact] - public void RFC_ParentType() - { - var obj = Mock.Of(); - _mockContext.Setup(x => x.ParentType).Returns(obj); - _graphContext.ParentType.ShouldBe(obj); - } - - [Fact] - public void RFC_Parent() - { - var obj = Mock.Of(); - _mockContext.Setup(x => x.Parent).Returns(obj); - _graphContext.Parent.ShouldBe(obj); - } - - [Fact] - public void RFC_Arguments() - { - var obj = Mock.Of>(); - _mockContext.Setup(x => x.Arguments).Returns(obj); - _graphContext.Arguments.ShouldBe(obj); - } - - [Fact] - public void RFC_RootValue() - { - var obj = new object(); - _mockContext.Setup(x => x.RootValue).Returns(obj); - _graphContext.RootValue.ShouldBe(obj); - } - - [Fact] - public void RFC_Source() - { - var obj = new object(); - _mockContext.Setup(x => x.Source).Returns(obj); - _graphContext.Source.ShouldBe(obj); - } - - [Fact] - public void RFC_Schema() - { - var obj = Mock.Of(); - _mockContext.Setup(x => x.Schema).Returns(obj); - _graphContext.Schema.ShouldBe(obj); - } - - [Fact] - public void RFC_Document() - { - var obj = new GraphQLDocument(); - _mockContext.Setup(x => x.Document).Returns(obj); - _graphContext.Document.ShouldBe(obj); - } - - [Fact] - public void RFC_Operation() - { - var obj = new GraphQLOperationDefinition(); - _mockContext.Setup(x => x.Operation).Returns(obj); - _graphContext.Operation.ShouldBe(obj); - } - - [Fact] - public void RFC_Variables() - { - var obj = new GraphQL.Validation.Variables(); - _mockContext.Setup(x => x.Variables).Returns(obj); - _graphContext.Variables.ShouldBe(obj); - } - - [Fact] - public void RFC_CancellationToken() - { - var obj = new CancellationTokenSource().Token; - _mockContext.Setup(x => x.CancellationToken).Returns(obj); - _graphContext.CancellationToken.ShouldBe(obj); - } - - [Fact] - public void RFC_Errors() - { - var obj = new ExecutionErrors(); - _mockContext.Setup(x => x.Errors).Returns(obj); - _graphContext.Errors.ShouldBe(obj); - } - - [Fact] - public void RFC_Path() - { - var obj = Mock.Of>(); - _mockContext.Setup(x => x.Path).Returns(obj); - Assert.StrictEqual(_graphContext.Path, obj); - } - - [Fact] - public void RFC_ResponsePath() - { - var obj = Mock.Of>(); - _mockContext.Setup(x => x.ResponsePath).Returns(obj); - Assert.StrictEqual(_graphContext.ResponsePath, obj); - } - - [Fact] - public void RFC_SubFields() - { - var obj = new Dictionary(); - _mockContext.Setup(x => x.SubFields).Returns(obj); - _graphContext.SubFields.ShouldBe(obj); - } - - [Fact] - public void RFC_InputExtensions() - { - var obj = Mock.Of>(); - _mockContext.Setup(x => x.InputExtensions).Returns(obj); - _graphContext.InputExtensions.ShouldBe(obj); - } - - [Fact] - public void RFC_OutputExtensions() - { - var obj = Mock.Of>(); - _mockContext.Setup(x => x.OutputExtensions).Returns(obj); - _graphContext.OutputExtensions.ShouldBe(obj); - } - - [Fact] - public void RFC_RequestServices() - { - var obj = Mock.Of(); - _mockContext.Setup(x => x.RequestServices).Returns(obj); - _graphContext.RequestServices.ShouldBe(obj); - } - - [Fact] - public void RFC_ArrayPool() - { - var obj = Mock.Of(); - _mockContext.Setup(x => x.ArrayPool).Returns(obj); - _graphContext.ArrayPool.ShouldBe(obj); - } - - private class CTest : DIObjectGraphBase - { - } - - private class CTest : DIObjectGraphBase - { - } + private readonly DIObjectGraphBase _graph = new CTest(); + private IResolveFieldContext _graphContext => _graph; + private readonly Mock _mockContext = new Mock(MockBehavior.Strict); + private IResolveFieldContext _context => _mockContext.Object; + + public Properties() + { + ((IDIObjectGraphBase)_graph).Context = _context; + ((IDIObjectGraphBase)_graph).Context.ShouldBe(_context); + } + + [Fact] + public void Context() + { + _graph.Context.ShouldBe(_context); + } + + [Fact] + public void RequestAborted() + { + var token = new CancellationTokenSource().Token; + _mockContext.Setup(x => x.CancellationToken).Returns(token); + _graph.RequestAborted.ShouldBe(token); + } + + [Fact] + public void Metrics() + { + var metrics = new Metrics(); + _mockContext.Setup(x => x.Metrics).Returns(metrics); + _graph.Metrics.ShouldBe(metrics); + } + + [Fact] + public void UserContext() + { + var userContext = new Dictionary(); + _mockContext.Setup(x => x.UserContext).Returns(userContext); + _graph.UserContext.ShouldBe(userContext); + } + + [Fact] + public void Source() + { + var source = new object(); + _mockContext.Setup(x => x.Source).Returns(source); + _graph.Source.ShouldBe(source); + } + + [Fact] + public void SourceTyped() + { + _mockContext.Setup(x => x.Source).Returns(3); + var graph = new CTest(); + ((IDIObjectGraphBase)graph).Context = _context; + graph.Source.ShouldBe(3); + } + + [Fact] + public void RFC_FieldAst() + { + var fieldAst = new GraphQLField(); + _mockContext.Setup(x => x.FieldAst).Returns(fieldAst); + _graphContext.FieldAst.ShouldBe(fieldAst); + } + + [Fact] + public void RFC_FieldDefinition() + { + var fieldDef = new FieldType(); + _mockContext.Setup(x => x.FieldDefinition).Returns(fieldDef); + _graphContext.FieldDefinition.ShouldBe(fieldDef); + } + + [Fact] + public void RFC_ParentType() + { + var obj = Mock.Of(); + _mockContext.Setup(x => x.ParentType).Returns(obj); + _graphContext.ParentType.ShouldBe(obj); + } + + [Fact] + public void RFC_Parent() + { + var obj = Mock.Of(); + _mockContext.Setup(x => x.Parent).Returns(obj); + _graphContext.Parent.ShouldBe(obj); + } + + [Fact] + public void RFC_Arguments() + { + var obj = Mock.Of>(); + _mockContext.Setup(x => x.Arguments).Returns(obj); + _graphContext.Arguments.ShouldBe(obj); + } + + [Fact] + public void RFC_RootValue() + { + var obj = new object(); + _mockContext.Setup(x => x.RootValue).Returns(obj); + _graphContext.RootValue.ShouldBe(obj); + } + + [Fact] + public void RFC_Source() + { + var obj = new object(); + _mockContext.Setup(x => x.Source).Returns(obj); + _graphContext.Source.ShouldBe(obj); + } + + [Fact] + public void RFC_Schema() + { + var obj = Mock.Of(); + _mockContext.Setup(x => x.Schema).Returns(obj); + _graphContext.Schema.ShouldBe(obj); + } + + [Fact] + public void RFC_Document() + { + var obj = new GraphQLDocument(); + _mockContext.Setup(x => x.Document).Returns(obj); + _graphContext.Document.ShouldBe(obj); + } + + [Fact] + public void RFC_Operation() + { + var obj = new GraphQLOperationDefinition(); + _mockContext.Setup(x => x.Operation).Returns(obj); + _graphContext.Operation.ShouldBe(obj); + } + + [Fact] + public void RFC_Variables() + { + var obj = new GraphQL.Validation.Variables(); + _mockContext.Setup(x => x.Variables).Returns(obj); + _graphContext.Variables.ShouldBe(obj); + } + + [Fact] + public void RFC_CancellationToken() + { + var obj = new CancellationTokenSource().Token; + _mockContext.Setup(x => x.CancellationToken).Returns(obj); + _graphContext.CancellationToken.ShouldBe(obj); + } + + [Fact] + public void RFC_Errors() + { + var obj = new ExecutionErrors(); + _mockContext.Setup(x => x.Errors).Returns(obj); + _graphContext.Errors.ShouldBe(obj); + } + + [Fact] + public void RFC_Path() + { + var obj = Mock.Of>(); + _mockContext.Setup(x => x.Path).Returns(obj); + Assert.StrictEqual(_graphContext.Path, obj); + } + + [Fact] + public void RFC_ResponsePath() + { + var obj = Mock.Of>(); + _mockContext.Setup(x => x.ResponsePath).Returns(obj); + Assert.StrictEqual(_graphContext.ResponsePath, obj); + } + + [Fact] + public void RFC_SubFields() + { + var obj = new Dictionary(); + _mockContext.Setup(x => x.SubFields).Returns(obj); + _graphContext.SubFields.ShouldBe(obj); + } + + [Fact] + public void RFC_InputExtensions() + { + var obj = Mock.Of>(); + _mockContext.Setup(x => x.InputExtensions).Returns(obj); + _graphContext.InputExtensions.ShouldBe(obj); + } + + [Fact] + public void RFC_OutputExtensions() + { + var obj = Mock.Of>(); + _mockContext.Setup(x => x.OutputExtensions).Returns(obj); + _graphContext.OutputExtensions.ShouldBe(obj); + } + + [Fact] + public void RFC_RequestServices() + { + var obj = Mock.Of(); + _mockContext.Setup(x => x.RequestServices).Returns(obj); + _graphContext.RequestServices.ShouldBe(obj); + } + + [Fact] + public void RFC_ArrayPool() + { + var obj = Mock.Of(); + _mockContext.Setup(x => x.ArrayPool).Returns(obj); + _graphContext.ArrayPool.ShouldBe(obj); + } + + private class CTest : DIObjectGraphBase + { + } + + private class CTest : DIObjectGraphBase + { } } diff --git a/src/Tests/DIObjectGraphTypeTests/Argument.cs b/src/Tests/DIObjectGraphTypeTests/Argument.cs index 0f2c930..efdd795 100644 --- a/src/Tests/DIObjectGraphTypeTests/Argument.cs +++ b/src/Tests/DIObjectGraphTypeTests/Argument.cs @@ -1,180 +1,174 @@ using System.ComponentModel; -using System.Threading; -using GraphQL; -using GraphQL.DI; -using GraphQL.Types; -using Shouldly; -using Xunit; - -namespace DIObjectGraphTypeTests +using System.Globalization; + +namespace DIObjectGraphTypeTests; + +public class Argument : DIObjectGraphTypeTestBase { - public class Argument : DIObjectGraphTypeTestBase - { - [Fact] - public void NonNullValue() - { - Configure(); - VerifyFieldArgument("Field1", "arg", false, 2); - VerifyField("Field1", false, false, 2); - Verify(false); - } - - public class CNonNullValue : DIObjectGraphBase - { - public static int Field1(int arg) => arg; - } - - [Fact] - public void NullableValue() - { - Configure(); - VerifyFieldArgument("Field1", "arg", true, (int?)2); - VerifyField("Field1", true, false, 2); - Verify(false); - } - - public class CNullableValue : DIObjectGraphBase - { - public static int? Field1(int? arg) => arg; - } - - [Fact] - public void NonNullableObject2() - { - Configure(); - VerifyFieldArgument("Field1", "arg", false, "hello"); - VerifyField("Field1", true, false, "hello"); - Verify(false); - } - - public class CNonNullableObject2 : DIObjectGraphBase - { - public static string Field1([System.ComponentModel.DataAnnotations.Required] string arg) => arg; - } - - [Fact] - public void Name() - { - Configure(); - VerifyFieldArgument("Field1", "AltName", true, "hello"); - VerifyField("Field1", true, false, "hello"); - Verify(false); - } - - public class CName : DIObjectGraphBase - { - public static string Field1([Name("AltName")] string arg) => arg; - } - - [Fact] - public void Description() - { - Configure(); - var arg = VerifyFieldArgument("Field1", "arg", true, "hello"); - arg.Description.ShouldBe("TestDescription"); - VerifyField("Field1", true, false, "hello"); - Verify(false); - } - - public class CDescription : DIObjectGraphBase - { - public static string Field1([Description("TestDescription")] string arg) => arg; - } - - [Fact] - public void GraphType() - { - Configure(); - VerifyFieldArgument("Field1", "arg", typeof(IdGraphType), "hello"); - VerifyField("Field1", true, false, "hello"); - Verify(false); - } - - public class CGraphType : DIObjectGraphBase - { - public static string Field1([InputType(typeof(IdGraphType))] string arg) => arg; - } - - [Fact] - public void GraphTypeInherited() - { - Configure(); - VerifyFieldArgument("Field1", "arg", typeof(IdGraphType), "hello"); - VerifyField("Field1", true, false, "hello"); - Verify(false); - } - - public class CGraphTypeInherited : DIObjectGraphBase - { - public static string Field1([MyIdGraphType] string arg) => arg; - } - - public class MyIdGraphTypeAttribute : InputTypeAttribute - { - public MyIdGraphTypeAttribute() : base(typeof(IdGraphType)) { } - } - - [Fact] - public void Id() - { - Configure(); - VerifyFieldArgument("Field1", "arg", typeof(IdGraphType), "hello"); - VerifyField("Field1", true, false, "hello"); - Verify(false); - } - - public class CIdGraphType : DIObjectGraphBase - { - public static string Field1([Id] string arg) => arg; - } - - [Fact] - public void IdNonNull() - { - Configure(); - VerifyFieldArgument("Field1", "arg", typeof(NonNullGraphType), "3"); - VerifyField("Field1", true, false, "3"); - Verify(false); - } - - public class CIdGraphType2 : DIObjectGraphBase - { - public static string Field1([Id] int arg) => arg.ToString(); - } - - [Fact] - public void CancellationToken() - { - using var cts = new CancellationTokenSource(); - var token = cts.Token; - cts.Cancel(); - Configure(); - _contextMock.SetupGet(x => x.CancellationToken).Returns(token).Verifiable(); - VerifyField("Field1", true, false, "hello + True"); - Verify(false); - } - - public class CCancellationToken : DIObjectGraphBase - { - public static string Field1(CancellationToken cancellationToken) => "hello + " + cancellationToken.IsCancellationRequested; - } - - [Fact] - public void DefaultValue() - { - Configure(); - VerifyFieldArgument("Field1", "arg1", false, 2); - VerifyField("Field1", false, false, 2); - VerifyFieldArgument("Field2", "arg2", false); - VerifyField("Field2", false, false, 5); - Verify(false); - } - - public class CDefaultValue : DIObjectGraphBase - { - public static int Field1(int arg1 = 4) => arg1; - public static int Field2(int arg2 = 5) => arg2; - } + [Fact] + public void NonNullValue() + { + Configure(); + VerifyFieldArgument("Field1", "arg", false, 2); + VerifyField("Field1", false, false, 2); + Verify(false); + } + + public class CNonNullValue : DIObjectGraphBase + { + public static int Field1(int arg) => arg; + } + + [Fact] + public void NullableValue() + { + Configure(); + VerifyFieldArgument("Field1", "arg", true, (int?)2); + VerifyField("Field1", true, false, 2); + Verify(false); + } + public class CNullableValue : DIObjectGraphBase + { + public static int? Field1(int? arg) => arg; + } + + [Fact] + public void NonNullableObject2() + { + Configure(); + VerifyFieldArgument("Field1", "arg", false, "hello"); + VerifyField("Field1", true, false, "hello"); + Verify(false); + } + + public class CNonNullableObject2 : DIObjectGraphBase + { + public static string? Field1([System.ComponentModel.DataAnnotations.Required] string? arg) => arg; + } + + [Fact] + public void Name() + { + Configure(); + VerifyFieldArgument("Field1", "AltName", true, "hello"); + VerifyField("Field1", true, false, "hello"); + Verify(false); + } + + public class CName : DIObjectGraphBase + { + public static string? Field1([Name("AltName")] string? arg) => arg; + } + + [Fact] + public void Description() + { + Configure(); + var arg = VerifyFieldArgument("Field1", "arg", true, "hello"); + arg.Description.ShouldBe("TestDescription"); + VerifyField("Field1", true, false, "hello"); + Verify(false); + } + + public class CDescription : DIObjectGraphBase + { + public static string? Field1([Description("TestDescription")] string? arg) => arg; + } + + [Fact] + public void GraphType() + { + Configure(); + VerifyFieldArgument("Field1", "arg", typeof(IdGraphType), "hello"); + VerifyField("Field1", true, false, "hello"); + Verify(false); + } + + public class CGraphType : DIObjectGraphBase + { + public static string? Field1([InputType(typeof(IdGraphType))] string? arg) => arg; } + + [Fact] + public void GraphTypeInherited() + { + Configure(); + VerifyFieldArgument("Field1", "arg", typeof(IdGraphType), "hello"); + VerifyField("Field1", true, false, "hello"); + Verify(false); + } + + public class CGraphTypeInherited : DIObjectGraphBase + { + public static string? Field1([MyIdGraphType] string? arg) => arg; + } + + public class MyIdGraphTypeAttribute : InputTypeAttribute + { + public MyIdGraphTypeAttribute() : base(typeof(IdGraphType)) { } + } + + [Fact] + public void Id() + { + Configure(); + VerifyFieldArgument("Field1", "arg", typeof(IdGraphType), "hello"); + VerifyField("Field1", true, false, "hello"); + Verify(false); + } + + public class CIdGraphType : DIObjectGraphBase + { + public static string? Field1([Id] string? arg) => arg; + } + + [Fact] + public void IdNonNull() + { + Configure(); + VerifyFieldArgument("Field1", "arg", typeof(NonNullGraphType), "3"); + VerifyField("Field1", true, false, "3"); + Verify(false); + } + + public class CIdGraphType2 : DIObjectGraphBase + { + public static string? Field1([Id] int arg) => arg.ToString(CultureInfo.InvariantCulture); + } + + [Fact] + public void CancellationToken() + { + using var cts = new CancellationTokenSource(); + var token = cts.Token; + cts.Cancel(); + Configure(); + _contextMock.SetupGet(x => x.CancellationToken).Returns(token).Verifiable(); + VerifyField("Field1", true, false, "hello + True"); + Verify(false); + } + + public class CCancellationToken : DIObjectGraphBase + { + public static string? Field1(CancellationToken cancellationToken) => "hello + " + cancellationToken.IsCancellationRequested; + } + + [Fact] + public void DefaultValue() + { + Configure(); + VerifyFieldArgument("Field1", "arg1", false, 2); + VerifyField("Field1", false, false, 2); + VerifyFieldArgument("Field2", "arg2", false); + VerifyField("Field2", false, false, 5); + Verify(false); + } + + public class CDefaultValue : DIObjectGraphBase + { + public static int Field1(int arg1 = 4) => arg1; + public static int Field2(int arg2 = 5) => arg2; + } + } diff --git a/src/Tests/DIObjectGraphTypeTests/DIObjectGraphTypeTestBase.cs b/src/Tests/DIObjectGraphTypeTests/DIObjectGraphTypeTestBase.cs index bc10721..49feaa7 100644 --- a/src/Tests/DIObjectGraphTypeTests/DIObjectGraphTypeTestBase.cs +++ b/src/Tests/DIObjectGraphTypeTests/DIObjectGraphTypeTestBase.cs @@ -1,139 +1,129 @@ -using System; -using System.Collections.Generic; -using System.Threading.Tasks; -using GraphQL; -using GraphQL.DI; -using GraphQL.Execution; -using GraphQL.Types; -using Microsoft.Extensions.DependencyInjection; -using Moq; -using Shouldly; +#pragma warning disable CA1051 // Do not declare visible instance fields -namespace DIObjectGraphTypeTests +namespace DIObjectGraphTypeTests; + +public class DIObjectGraphTypeTestBase { - public class DIObjectGraphTypeTestBase - { - protected object _source; - protected IComplexGraphType _graphType; - protected readonly Mock _scopedServiceProviderMock; - protected readonly Mock _scopeMock; - protected readonly Mock _scopeFactoryMock; - protected readonly Mock _serviceProviderMock; - protected readonly Mock _contextMock; - protected readonly Dictionary _arguments = new(); + protected object? _source; + protected IComplexGraphType? _graphType; + protected readonly Mock _scopedServiceProviderMock; + protected readonly Mock _scopeMock; + protected readonly Mock _scopeFactoryMock; + protected readonly Mock _serviceProviderMock; + protected readonly Mock _contextMock; + protected readonly Dictionary _arguments = new(); - public DIObjectGraphTypeTestBase() : base() - { - _scopedServiceProviderMock = new Mock(MockBehavior.Strict); - _scopeMock = new Mock(MockBehavior.Strict); - _scopeMock.Setup(x => x.Dispose()); - _scopeMock.SetupGet(x => x.ServiceProvider).Returns(_scopedServiceProviderMock.Object); - _scopeFactoryMock = new Mock(MockBehavior.Strict); - _scopeFactoryMock.Setup(x => x.CreateScope()).Returns(_scopeMock.Object); - _serviceProviderMock = new Mock(MockBehavior.Strict); - _serviceProviderMock.Setup(x => x.GetService(typeof(IServiceScopeFactory))).Returns(_scopeFactoryMock.Object); - _contextMock = new Mock(MockBehavior.Strict); - _contextMock.SetupGet(x => x.RequestServices).Returns(_serviceProviderMock.Object); - _contextMock.SetupGet(x => x.Source).Returns(() => _source!); - _contextMock.SetupGet(x => x.ParentType).Returns(new ObjectGraphType()); - _contextMock.SetupGet(x => x.Arguments).Returns(() => _arguments); - _contextMock.SetupGet(x => x.Schema).Returns((ISchema)null); - } + public DIObjectGraphTypeTestBase() : base() + { + _scopedServiceProviderMock = new Mock(MockBehavior.Strict); + _scopeMock = new Mock(MockBehavior.Strict); + _scopeMock.Setup(x => x.Dispose()); + _scopeMock.SetupGet(x => x.ServiceProvider).Returns(_scopedServiceProviderMock.Object); + _scopeFactoryMock = new Mock(MockBehavior.Strict); + _scopeFactoryMock.Setup(x => x.CreateScope()).Returns(_scopeMock.Object); + _serviceProviderMock = new Mock(MockBehavior.Strict); + _serviceProviderMock.Setup(x => x.GetService(typeof(IServiceScopeFactory))).Returns(_scopeFactoryMock.Object); + _contextMock = new Mock(MockBehavior.Strict); + _contextMock.SetupGet(x => x.RequestServices).Returns(_serviceProviderMock.Object); + _contextMock.SetupGet(x => x.Source).Returns(() => _source!); + _contextMock.SetupGet(x => x.ParentType).Returns(new ObjectGraphType()); + _contextMock.SetupGet(x => x.Arguments).Returns(() => _arguments); + _contextMock.SetupGet(x => x.Schema).Returns((ISchema)null!); + } - protected IComplexGraphType Configure(bool instance = false, bool scoped = false) where T : DIObjectGraphBase, new() - { - if (instance) { - if (scoped) { - _scopedServiceProviderMock.Setup(x => x.GetService(typeof(T))).Returns(() => new T()).Verifiable(); - } else { - _serviceProviderMock.Setup(x => x.GetService(typeof(T))).Returns(() => new T()).Verifiable(); - } + protected IComplexGraphType Configure(bool instance = false, bool scoped = false) where T : DIObjectGraphBase, new() + { + if (instance) { + if (scoped) { + _scopedServiceProviderMock.Setup(x => x.GetService(typeof(T))).Returns(() => new T()).Verifiable(); + } else { + _serviceProviderMock.Setup(x => x.GetService(typeof(T))).Returns(() => new T()).Verifiable(); } - _graphType = new DIObjectGraphType(); - return _graphType; } + _graphType = new DIObjectGraphType(); + return _graphType; + } - protected FieldType VerifyField(string fieldName, bool nullable, bool concurrent, T returnValue) - { - return VerifyField(fieldName, typeof(T).GetGraphTypeFromType(nullable, TypeMappingMode.OutputType), concurrent, returnValue); - } + protected FieldType VerifyField(string fieldName, bool nullable, bool concurrent, T returnValue) + { + return VerifyField(fieldName, typeof(T).GetGraphTypeFromType(nullable, TypeMappingMode.OutputType), concurrent, returnValue); + } - protected FieldType VerifyField(string fieldName, Type fieldGraphType, bool concurrent, T returnValue) - { - var context = _contextMock.Object; - _graphType.ShouldNotBeNull(); - _graphType.Fields.ShouldNotBeNull(); - var field = _graphType.Fields.Find(fieldName); - field.ShouldNotBeNull(); - field.Type.ShouldBe(fieldGraphType); - field.Resolver.ShouldNotBeNull(); - _contextMock.Setup(x => x.FieldDefinition).Returns(field); - field.Resolver.ResolveAsync(context).Result.ShouldBe(returnValue); - _arguments.Clear(); - return field; - } + protected FieldType VerifyField(string fieldName, Type fieldGraphType, bool concurrent, T returnValue) + { + var context = _contextMock.Object; + _graphType.ShouldNotBeNull(); + _graphType.Fields.ShouldNotBeNull(); + var field = _graphType.Fields.Find(fieldName); + field.ShouldNotBeNull(); + field.Type.ShouldBe(fieldGraphType); + field.Resolver.ShouldNotBeNull(); + _contextMock.Setup(x => x.FieldDefinition).Returns(field); + field.Resolver.ResolveAsync(context).AsTask().GetAwaiter().GetResult().ShouldBe(returnValue); + _arguments.Clear(); + return field; + } - protected async Task VerifyFieldAsync(string fieldName, bool nullable, bool concurrent, T returnValue) - { - var context = _contextMock.Object; - _graphType.ShouldNotBeNull(); - _graphType.Fields.ShouldNotBeNull(); - var field = _graphType.Fields.Find(fieldName); - field.ShouldNotBeNull(); - field.Type.ShouldBe(typeof(T).GetGraphTypeFromType(nullable, TypeMappingMode.OutputType)); - field.Resolver.ShouldNotBeNull(); - _contextMock.Setup(x => x.FieldDefinition).Returns(field); - var final = await field.Resolver.ResolveAsync(context); - final.ShouldBe(returnValue); - _arguments.Clear(); - return field; - } + protected async Task VerifyFieldAsync(string fieldName, bool nullable, bool concurrent, T returnValue) + { + var context = _contextMock.Object; + _graphType.ShouldNotBeNull(); + _graphType.Fields.ShouldNotBeNull(); + var field = _graphType.Fields.Find(fieldName); + field.ShouldNotBeNull(); + field.Type.ShouldBe(typeof(T).GetGraphTypeFromType(nullable, TypeMappingMode.OutputType)); + field.Resolver.ShouldNotBeNull(); + _contextMock.Setup(x => x.FieldDefinition).Returns(field); + var final = await field.Resolver.ResolveAsync(context); + final.ShouldBe(returnValue); + _arguments.Clear(); + return field; + } - protected QueryArgument VerifyFieldArgument(string fieldName, string argumentName, bool nullable, T returnValue) - { - return VerifyFieldArgument(fieldName, argumentName, typeof(T).GetGraphTypeFromType(nullable, TypeMappingMode.InputType), returnValue); - } + protected QueryArgument VerifyFieldArgument(string fieldName, string argumentName, bool nullable, T returnValue) + { + return VerifyFieldArgument(fieldName, argumentName, typeof(T).GetGraphTypeFromType(nullable, TypeMappingMode.InputType), returnValue); + } - protected QueryArgument VerifyFieldArgument(string fieldName, string argumentName, bool nullable) - { - return VerifyFieldArgument(fieldName, argumentName, typeof(T).GetGraphTypeFromType(nullable, TypeMappingMode.InputType)); - } + protected QueryArgument VerifyFieldArgument(string fieldName, string argumentName, bool nullable) + { + return VerifyFieldArgument(fieldName, argumentName, typeof(T).GetGraphTypeFromType(nullable, TypeMappingMode.InputType)); + } - protected QueryArgument VerifyFieldArgument(string fieldName, string argumentName, Type graphType) - { - _graphType.ShouldNotBeNull(); - _graphType.Fields.ShouldNotBeNull(); - var field = _graphType.Fields.Find(fieldName); - field.ShouldNotBeNull(); - field.Arguments.ShouldNotBeNull(); - var argument = field.Arguments.Find(argumentName); - argument.ShouldNotBeNull(); - argument.Type.ShouldBe(graphType); - return argument; - } + protected QueryArgument VerifyFieldArgument(string fieldName, string argumentName, Type graphType) + { + _graphType.ShouldNotBeNull(); + _graphType.Fields.ShouldNotBeNull(); + var field = _graphType.Fields.Find(fieldName); + field.ShouldNotBeNull(); + field.Arguments.ShouldNotBeNull(); + var argument = field.Arguments.Find(argumentName); + argument.ShouldNotBeNull(); + argument.Type.ShouldBe(graphType); + return argument; + } - protected QueryArgument VerifyFieldArgument(string fieldName, string argumentName, Type graphType, T returnValue) - { - var argument = VerifyFieldArgument(fieldName, argumentName, graphType); - _arguments.Add(argumentName, new ArgumentValue(returnValue, ArgumentSource.FieldDefault)); - return argument; - } + protected QueryArgument VerifyFieldArgument(string fieldName, string argumentName, Type graphType, T returnValue) + { + var argument = VerifyFieldArgument(fieldName, argumentName, graphType); + _arguments.Add(argumentName, new ArgumentValue(returnValue, ArgumentSource.FieldDefault)); + return argument; + } - protected void Verify(bool scoped) - { - if (scoped) { - _contextMock.Verify(x => x.RequestServices, Times.Once); - _serviceProviderMock.Verify(x => x.GetService(typeof(IServiceScopeFactory)), Times.Once); - _scopeFactoryMock.Verify(x => x.CreateScope(), Times.Once); - _scopeMock.Verify(x => x.ServiceProvider, Times.Once); - } else { - _serviceProviderMock.Verify(x => x.GetService(typeof(IServiceScopeFactory)), Times.Never); - } - _contextMock.Verify(); - _serviceProviderMock.Verify(); - _scopeMock.Verify(); - _scopeFactoryMock.Verify(); - _scopedServiceProviderMock.Verify(); + protected void Verify(bool scoped) + { + if (scoped) { + _contextMock.Verify(x => x.RequestServices, Times.Once); + _serviceProviderMock.Verify(x => x.GetService(typeof(IServiceScopeFactory)), Times.Once); + _scopeFactoryMock.Verify(x => x.CreateScope(), Times.Once); + _scopeMock.Verify(x => x.ServiceProvider, Times.Once); + } else { + _serviceProviderMock.Verify(x => x.GetService(typeof(IServiceScopeFactory)), Times.Never); } + _contextMock.Verify(); + _serviceProviderMock.Verify(); + _scopeMock.Verify(); + _scopeFactoryMock.Verify(); + _scopedServiceProviderMock.Verify(); } } diff --git a/src/Tests/DIObjectGraphTypeTests/Field.cs b/src/Tests/DIObjectGraphTypeTests/Field.cs index 4066bcb..ba6c0c5 100644 --- a/src/Tests/DIObjectGraphTypeTests/Field.cs +++ b/src/Tests/DIObjectGraphTypeTests/Field.cs @@ -1,522 +1,508 @@ -#nullable enable - -using System; using System.ComponentModel; -using System.Reflection; -using System.Threading.Tasks; -using GraphQL; -using GraphQL.DataLoader; -using GraphQL.DI; -using GraphQL.Types; -using Microsoft.Extensions.DependencyInjection; -using Moq; -using Shouldly; -using Xunit; - -namespace DIObjectGraphTypeTests + +namespace DIObjectGraphTypeTests; + +public class Field : DIObjectGraphTypeTestBase { - public class Field : DIObjectGraphTypeTestBase - { - [Fact] - public void StaticMethod() - { - Configure(); - VerifyField("Field1", nullable: true, concurrent: false, returnValue: "hello"); - Verify(false); - } - - public class CStaticMethod : DIObjectGraphBase - { - public CStaticMethod() => throw new Exception(); - - public static string? Field1() => "hello"; - } - - [Fact] - public void InstanceMethod() - { - Configure(true); - VerifyField("Field1", nullable: true, concurrent: false, returnValue: "hello"); - Verify(false); - } - - public class CInstanceMethod : DIObjectGraphBase - { - public string? Field1() => "hello"; - } - - [Fact] - public void SetsContext() - { - Configure(true); - _graphType.Fields.Find("Field1")!.Resolver!.ResolveAsync(_contextMock.Object).Result.ShouldBe(_contextMock.Object); - } - - public class CVerifyContext : DIObjectGraphBase - { - public object Field1() => Context; - } - - [Fact] - public async Task StaticAsyncMethod() - { - Configure(); - await VerifyFieldAsync("Field1", nullable: true, concurrent: true, returnValue: "hello"); - Verify(false); - } - - public class CStaticAsyncMethod : DIObjectGraphBase - { - public CStaticAsyncMethod() => throw new Exception(); - - public static Task Field1() => Task.FromResult("hello"); - } - - [Fact] - public async Task InstanceAsyncMethod() - { - Configure(true); - await VerifyFieldAsync("Field1", nullable: true, concurrent: false, returnValue: "hello"); - Verify(false); - } - - public class CInstanceAsyncMethod : DIObjectGraphBase - { - public Task Field1() => Task.FromResult("hello"); - } - - [Fact] - public void Name() - { - Configure(); - VerifyField("FieldTest", true, false, "hello"); - Verify(false); - } - - public class CName : DIObjectGraphBase - { - [Name("FieldTest")] - public static string? Field1() => "hello"; - } - - [Fact] - public void Description() - { - Configure(); - VerifyField("Field1", true, false, "hello").Description.ShouldBe("DescriptionTest"); - Verify(false); - } - - public class CDescription : DIObjectGraphBase - { - [Description("DescriptionTest")] - public static string? Field1() => "hello"; - } - - [Fact] - public void Obsolete() - { - Configure(); - VerifyField("Field1", true, false, "hello").DeprecationReason.ShouldBe("ObsoleteTest"); - Verify(false); - } - - public class CObsolete : DIObjectGraphBase - { - [Obsolete("ObsoleteTest")] - public static string? Field1() => "hello"; - } - - [Fact] - public void ImplicitSource() - { - Configure(); - _source = "testSource"; - VerifyField("Field1", true, false, "testSource"); - _contextMock.Verify(x => x.Source, Times.AtLeastOnce); - Verify(false); - } - - public class CImplicitSource : DIObjectGraphBase - { - public static string? Field1(string source) => source; - } - - [Fact] - public void Context() - { - Configure(); - _source = "testSource"; - VerifyField("Field1", true, false, "testSource"); - _contextMock.Verify(x => x.Source, Times.Once); - Verify(false); - } - - public class CContext : DIObjectGraphBase - { - public static string? Field1(IResolveFieldContext context) => (string)context.Source; - } - - [Fact(Skip = "Not supported in this version")] - public void Context_Typed() - { - Configure(); - _source = "testSource"; - VerifyField("Field1", true, false, "testSource"); - _contextMock.Verify(x => x.Source, Times.AtLeastOnce); - Verify(false); - } - - public class CContext_Typed : DIObjectGraphBase - { - public static string? Field1(IResolveFieldContext context) => (string)context.Source; - } - - [Fact(Skip = "Not supported in this version")] - public void Context_WrongType() - { - Should.Throw(() => Configure()); - } - - public class CContext_WrongType : DIObjectGraphBase - { - public static string? Field1(IResolveFieldContext context) => context.Source; - } - - [Fact] - public void Source() - { - Configure(); - _source = "testSource"; - VerifyField("Field1", true, false, "testSource"); - _contextMock.Verify(x => x.Source, Times.Once); - Verify(false); - } - - public class CSource : DIObjectGraphBase - { - public static string? Field1([FromSource] object source) => (string)source; - } - - [Fact] - public void Source_WrongType() - { - Should.Throw(() => Configure()); - } - - public class CSource_WrongType : DIObjectGraphBase - { - public static string Field1([FromSource] string source) => source; - } - - [Fact] - public void ServiceProvider() - { - Configure(); - _serviceProviderMock.Setup(x => x.GetService(typeof(string))).Returns("hello").Verifiable(); - VerifyField("Field1", true, false, "hello"); - Verify(false); - } - - public class CServiceProvider : DIObjectGraphBase - { - public static string? Field1(IServiceProvider serviceProvider) => serviceProvider.GetRequiredService(); - } - - [Fact] - public async Task ServiceProviderForScoped() - { - Configure(); - _scopedServiceProviderMock.Setup(x => x.GetService(typeof(string))).Returns("hello").Verifiable(); - await VerifyFieldAsync("Field1", true, true, "hello"); - Verify(true); - } - - public class CServiceProviderForScoped : DIObjectGraphBase - { - [Scoped] - public static Task Field1(IServiceProvider serviceProvider) => Task.FromResult(serviceProvider.GetRequiredService()); - } - - [Fact] - public void NullableValue() - { - Configure(); - VerifyField("Field1", true, false, (int?)null); - Verify(false); - } - - public class CNullableValue : DIObjectGraphBase - { - public static int? Field1() => null; - } - - [Fact] - public void NonNullableValue() - { - Configure(); - VerifyField("Field1", false, false, 1); - Verify(false); - } - - public class CNonNullableValue : DIObjectGraphBase - { - public static int Field1() => 1; - } - - [Fact] - public void CustomType() - { - Configure(); - VerifyField("Field1", typeof(IdGraphType), false, "hello"); - Verify(false); - } - - public class CCustomType : DIObjectGraphBase - { - [OutputType(typeof(IdGraphType))] - public static string Field1() => "hello"; - } - - [Fact] - public void InheritedCustomType() - { - Configure(); - VerifyField("Field1", typeof(IdGraphType), false, "hello"); - Verify(false); - } - - public class CInheritedCustomType : DIObjectGraphBase - { - [MyIdGraphType] - public static string Field1() => "hello"; - } - - public class MyIdGraphTypeAttribute : OutputTypeAttribute - { - public MyIdGraphTypeAttribute() : base(typeof(IdGraphType)) { } - } - - [Fact] - public void IdType() - { - Configure(); - VerifyField("Field1", typeof(IdGraphType), false, "hello"); - Verify(false); - } - - public class CIdType : DIObjectGraphBase - { - [Id] - public static string? Field1() => "hello"; - } - - [Fact] - public void IdTypeNonNull() - { - Configure(); - VerifyField("Field1", typeof(NonNullGraphType), false, 2); - Verify(false); - } - - public class CIdTypeNonNull : DIObjectGraphBase - { - [Id] - public static int Field1() => 2; - } - - [Fact] - public void IdListType() - { - Configure(); - VerifyField("Field1", typeof(ListGraphType), false, new[] { "hello" }); - Verify(false); - } - - public class CIdListType : DIObjectGraphBase - { - [Id] - public static string?[]? Field1() => new[] { "hello" }; - } - - [Fact] - public void DIGraphType() - { - Configure(); - VerifyField("Field1", typeof(DIObjectGraphType), false, "hello"); - Verify(false); - } - - public class CDIGraphType2 : DIObjectGraphBase - { - public static string? Field1() => "hello"; - } - - public class CDIGraphType : DIObjectGraphBase - { - [DIGraph(typeof(CDIGraphType2))] - public static string? Field1() => "hello"; - } - - [Fact] - public void DIGraphTypeNonNull() - { - Configure(); - VerifyField("Field1", typeof(NonNullGraphType>), false, 2); - Verify(false); - } - - public class CDIGraphTypeNonNull : DIObjectGraphBase - { - [DIGraph(typeof(CDIGraphType2))] - public static int Field1() => 2; - } - - [Fact] - public void DIGraphListType() - { - Configure(); - VerifyField("Field1", typeof(ListGraphType>), false, new[] { "hello" }); - Verify(false); - } - - public class CDIGraphListType : DIObjectGraphBase - { - [DIGraph(typeof(CDIGraphType2))] - public static string?[]? Field1() => new[] { "hello" }; - } - - [Fact] - public void DIGraphTypeInvalid() - { - Should.Throw(() => Configure()); - } - - public class CDIGraphTypeInvalid : DIObjectGraphBase - { - [DIGraph(typeof(string))] - public static string Field1() => "hello"; - } - - [Fact] - public async Task ScopedOnlyWhenSpecifiedWithServices() - { - Configure(); - await VerifyFieldAsync("Field1", true, true, "hello"); - Verify(true); - } - - public class CScopedOnlyWhenSpecified : DIObjectGraphBase - { - [Scoped] - public static Task Field1(IServiceProvider services) => Task.FromResult("hello"); - } - - [Fact] - public async Task RemoveAsyncFromName() - { - Configure(); - await VerifyFieldAsync("Field1", true, true, "hello"); - Verify(false); - } - - public class CRemoveAsyncFromName : DIObjectGraphBase - { - public static Task Field1Async() => Task.FromResult("hello"); - } - - [Fact] - public void SkipVoidMembers() - { - Configure(); - _graphType.Fields.Find("Field1").ShouldBeNull(); - _graphType.Fields.Find("Field2").ShouldBeNull(); - _graphType.Fields.Find("Field3").ShouldNotBeNull(); - } - - public class CSkipVoidMembers : DIObjectGraphBase - { - public static Task Field1() => Task.CompletedTask; - public static void Field2() { } - public static string Field3() => null!; - } - - [Fact] - public void Ignore() - { - Configure(); - _graphType.Fields.Find("Field1").ShouldBeNull(); - _graphType.Fields.Find("Field2").ShouldNotBeNull(); - } - - public class CIgnore : DIObjectGraphBase - { - [Ignore] - public static string Field1() => "hello"; - public static string Field2() => "hello"; - } - - [Fact] - public void AddsMetadata() - { - Configure(); - var field = VerifyField("Field1", true, false, "hello"); - field.GetMetadata("test").ShouldBe("value"); - Verify(false); - } - - public class CAddsMetadata : DIObjectGraphBase - { - [Metadata("test", "value")] - public static string? Field1() => "hello"; - } - - [Fact] - public void AddsInheritedMetadata() - { - Configure(); - var field = VerifyField("Field1", true, false, "hello"); - field.GetMetadata("test").ShouldBe("value2"); - Verify(false); - } - - public class CAddsInheritedMetadata : DIObjectGraphBase - { - [InheritedMetadata("value2")] - public static string? Field1() => "hello"; - } - - private class InheritedMetadata : MetadataAttribute - { - public InheritedMetadata(string value) : base("test", value) { } - } - - [Theory] - [InlineData("Field1", typeof(GraphQLClrOutputTypeReference))] - [InlineData("Field3", typeof(NonNullGraphType>))] - [InlineData("Field5", typeof(GraphQLClrOutputTypeReference))] - [InlineData("Field6", typeof(GraphQLClrOutputTypeReference))] //Need to fix graphql-dotnet bug 2441 first - [InlineData("Field7", typeof(GraphQLClrOutputTypeReference))] - [InlineData("Field8", typeof(NonNullGraphType>))] - [InlineData("Field9", typeof(GraphQLClrOutputTypeReference))] - [InlineData("Field10", typeof(GraphQLClrOutputTypeReference))] - public void SupportsDataLoader(string fieldName, Type graphType) - { - Configure(true); - VerifyField(fieldName, graphType, false, null); - Verify(false); - } - - public class CSupportsDataLoader : DIObjectGraphBase - { - public IDataLoaderResult Field1() => null!; - public IDataLoaderResult Field3() => null!; - public IDataLoaderResult Field5() => null!; - public IDataLoaderResult Field6() => null!; - public Task> Field7() => Task.FromResult>(null); - public Task> Field8() => Task.FromResult>(null!); - public Task> Field9() => Task.FromResult>(null!); - public Task>> Field10() => Task.FromResult>>(null!); - } + [Fact] + public void StaticMethod() + { + Configure(); + VerifyField("Field1", nullable: true, concurrent: false, returnValue: "hello"); + Verify(false); + } + + public class CStaticMethod : DIObjectGraphBase + { + public CStaticMethod() => throw new InvalidOperationException(); + + public static string? Field1() => "hello"; + } + + [Fact] + public void InstanceMethod() + { + Configure(true); + VerifyField("Field1", nullable: true, concurrent: false, returnValue: "hello"); + Verify(false); + } + + public class CInstanceMethod : DIObjectGraphBase + { + public string? Field1() => "hello"; + } + + [Fact] + public void SetsContext() + { + Configure(true); + _graphType!.Fields.Find("Field1")!.Resolver!.ResolveAsync(_contextMock.Object).AsTask().GetAwaiter().GetResult().ShouldBe(_contextMock.Object); + } + + public class CVerifyContext : DIObjectGraphBase + { + public object Field1() => Context; + } + + [Fact] + public async Task StaticAsyncMethod() + { + Configure(); + await VerifyFieldAsync("Field1", nullable: true, concurrent: true, returnValue: "hello"); + Verify(false); + } + + public class CStaticAsyncMethod : DIObjectGraphBase + { + public CStaticAsyncMethod() => throw new InvalidOperationException(); + + public static Task Field1() => Task.FromResult("hello"); + } + + [Fact] + public async Task InstanceAsyncMethod() + { + Configure(true); + await VerifyFieldAsync("Field1", nullable: true, concurrent: false, returnValue: "hello"); + Verify(false); + } + + public class CInstanceAsyncMethod : DIObjectGraphBase + { + public Task Field1() => Task.FromResult("hello"); + } + + [Fact] + public void Name() + { + Configure(); + VerifyField("FieldTest", true, false, "hello"); + Verify(false); + } + + public class CName : DIObjectGraphBase + { + [Name("FieldTest")] + public static string? Field1() => "hello"; + } + + [Fact] + public void Description() + { + Configure(); + VerifyField("Field1", true, false, "hello").Description.ShouldBe("DescriptionTest"); + Verify(false); + } + + public class CDescription : DIObjectGraphBase + { + [Description("DescriptionTest")] + public static string? Field1() => "hello"; + } + + [Fact] + public void Obsolete() + { + Configure(); + VerifyField("Field1", true, false, "hello").DeprecationReason.ShouldBe("ObsoleteTest"); + Verify(false); + } + + public class CObsolete : DIObjectGraphBase + { + [Obsolete("ObsoleteTest")] + public static string? Field1() => "hello"; + } + + [Fact] + public void ImplicitSource() + { + Configure(); + _source = "testSource"; + VerifyField("Field1", true, false, "testSource"); + _contextMock.Verify(x => x.Source, Times.AtLeastOnce); + Verify(false); + } + + public class CImplicitSource : DIObjectGraphBase + { + public static string? Field1(string source) => source; + } + + [Fact] + public void Context() + { + Configure(); + _source = "testSource"; + VerifyField("Field1", true, false, "testSource"); + _contextMock.Verify(x => x.Source, Times.Once); + Verify(false); + } + + public class CContext : DIObjectGraphBase + { + public static string? Field1(IResolveFieldContext context) => (string?)context.Source; + } + + [Fact(Skip = "Not supported in this version")] + public void Context_Typed() + { + Configure(); + _source = "testSource"; + VerifyField("Field1", true, false, "testSource"); + _contextMock.Verify(x => x.Source, Times.AtLeastOnce); + Verify(false); + } + + public class CContext_Typed : DIObjectGraphBase + { + public static string? Field1(IResolveFieldContext context) => (string)context.Source; + } + + [Fact(Skip = "Not supported in this version")] + public void Context_WrongType() + { + Should.Throw(() => Configure()); + } + + public class CContext_WrongType : DIObjectGraphBase + { + public static string? Field1(IResolveFieldContext context) => context.Source; + } + + [Fact] + public void Source() + { + Configure(); + _source = "testSource"; + VerifyField("Field1", true, false, "testSource"); + _contextMock.Verify(x => x.Source, Times.Once); + Verify(false); + } + + public class CSource : DIObjectGraphBase + { + public static string? Field1([FromSource] object source) => (string)source; + } + + [Fact] + public void Source_WrongType() + { + Should.Throw(() => Configure()); + } + + public class CSource_WrongType : DIObjectGraphBase + { + public static string Field1([FromSource] string source) => source; + } + + [Fact] + public void ServiceProvider() + { + Configure(); + _serviceProviderMock.Setup(x => x.GetService(typeof(string))).Returns("hello").Verifiable(); + VerifyField("Field1", true, false, "hello"); + Verify(false); + } + + public class CServiceProvider : DIObjectGraphBase + { + public static string? Field1(IServiceProvider serviceProvider) => serviceProvider.GetRequiredService(); + } + + [Fact] + public async Task ServiceProviderForScoped() + { + Configure(); + _scopedServiceProviderMock.Setup(x => x.GetService(typeof(string))).Returns("hello").Verifiable(); + await VerifyFieldAsync("Field1", true, true, "hello"); + Verify(true); + } + + public class CServiceProviderForScoped : DIObjectGraphBase + { + [Scoped] + public static Task Field1(IServiceProvider serviceProvider) => Task.FromResult(serviceProvider.GetRequiredService()); + } + + [Fact] + public void NullableValue() + { + Configure(); + VerifyField("Field1", true, false, (int?)null); + Verify(false); + } + + public class CNullableValue : DIObjectGraphBase + { + public static int? Field1() => null; + } + + [Fact] + public void NonNullableValue() + { + Configure(); + VerifyField("Field1", false, false, 1); + Verify(false); + } + + public class CNonNullableValue : DIObjectGraphBase + { + public static int Field1() => 1; + } + + [Fact] + public void CustomType() + { + Configure(); + VerifyField("Field1", typeof(IdGraphType), false, "hello"); + Verify(false); + } + + public class CCustomType : DIObjectGraphBase + { + [OutputType(typeof(IdGraphType))] + public static string Field1() => "hello"; + } + + [Fact] + public void InheritedCustomType() + { + Configure(); + VerifyField("Field1", typeof(IdGraphType), false, "hello"); + Verify(false); + } + + public class CInheritedCustomType : DIObjectGraphBase + { + [MyIdGraphType] + public static string Field1() => "hello"; + } + + public class MyIdGraphTypeAttribute : OutputTypeAttribute + { + public MyIdGraphTypeAttribute() : base(typeof(IdGraphType)) { } + } + + [Fact] + public void IdType() + { + Configure(); + VerifyField("Field1", typeof(IdGraphType), false, "hello"); + Verify(false); + } + + public class CIdType : DIObjectGraphBase + { + [Id] + public static string? Field1() => "hello"; + } + + [Fact] + public void IdTypeNonNull() + { + Configure(); + VerifyField("Field1", typeof(NonNullGraphType), false, 2); + Verify(false); + } + + public class CIdTypeNonNull : DIObjectGraphBase + { + [Id] + public static int Field1() => 2; + } + + [Fact] + public void IdListType() + { + Configure(); + VerifyField("Field1", typeof(ListGraphType), false, new[] { "hello" }); + Verify(false); + } + + public class CIdListType : DIObjectGraphBase + { + [Id] + public static string?[]? Field1() => new[] { "hello" }; + } + + [Fact] + public void DIGraphType() + { + Configure(); + VerifyField("Field1", typeof(DIObjectGraphType), false, "hello"); + Verify(false); + } + + public class CDIGraphType2 : DIObjectGraphBase + { + public static string? Field1() => "hello"; + } + + public class CDIGraphType : DIObjectGraphBase + { + [DIGraph(typeof(CDIGraphType2))] + public static string? Field1() => "hello"; + } + + [Fact] + public void DIGraphTypeNonNull() + { + Configure(); + VerifyField("Field1", typeof(NonNullGraphType>), false, 2); + Verify(false); + } + + public class CDIGraphTypeNonNull : DIObjectGraphBase + { + [DIGraph(typeof(CDIGraphType2))] + public static int Field1() => 2; + } + + [Fact] + public void DIGraphListType() + { + Configure(); + VerifyField("Field1", typeof(ListGraphType>), false, new[] { "hello" }); + Verify(false); + } + + public class CDIGraphListType : DIObjectGraphBase + { + [DIGraph(typeof(CDIGraphType2))] + public static string?[]? Field1() => new[] { "hello" }; + } + + [Fact] + public void DIGraphTypeInvalid() + { + Should.Throw(() => Configure()); + } + + public class CDIGraphTypeInvalid : DIObjectGraphBase + { + [DIGraph(typeof(string))] + public static string Field1() => "hello"; + } + + [Fact] + public async Task ScopedOnlyWhenSpecifiedWithServices() + { + Configure(); + await VerifyFieldAsync("Field1", true, true, "hello"); + Verify(true); + } + + public class CScopedOnlyWhenSpecified : DIObjectGraphBase + { + [Scoped] + public static Task Field1(IServiceProvider services) => Task.FromResult("hello"); + } + + [Fact] + public async Task RemoveAsyncFromName() + { + Configure(); + await VerifyFieldAsync("Field1", true, true, "hello"); + Verify(false); + } + + public class CRemoveAsyncFromName : DIObjectGraphBase + { + public static Task Field1Async() => Task.FromResult("hello"); + } + + [Fact] + public void SkipVoidMembers() + { + Configure(); + _graphType!.Fields.Find("Field1").ShouldBeNull(); + _graphType.Fields.Find("Field2").ShouldBeNull(); + _graphType.Fields.Find("Field3").ShouldNotBeNull(); + } + + public class CSkipVoidMembers : DIObjectGraphBase + { + public static Task Field1() => Task.CompletedTask; + public static void Field2() { } + public static string Field3() => null!; + } + + [Fact] + public void Ignore() + { + Configure(); + _graphType!.Fields.Find("Field1").ShouldBeNull(); + _graphType.Fields.Find("Field2").ShouldNotBeNull(); + } + + public class CIgnore : DIObjectGraphBase + { + [Ignore] + public static string Field1() => "hello"; + public static string Field2() => "hello"; + } + + [Fact] + public void AddsMetadata() + { + Configure(); + var field = VerifyField("Field1", true, false, "hello"); + field.GetMetadata("test").ShouldBe("value"); + Verify(false); + } + + public class CAddsMetadata : DIObjectGraphBase + { + [Metadata("test", "value")] + public static string? Field1() => "hello"; + } + + [Fact] + public void AddsInheritedMetadata() + { + Configure(); + var field = VerifyField("Field1", true, false, "hello"); + field.GetMetadata("test").ShouldBe("value2"); + Verify(false); + } + + public class CAddsInheritedMetadata : DIObjectGraphBase + { + [InheritedMetadata("value2")] + public static string? Field1() => "hello"; + } + + private class InheritedMetadata : MetadataAttribute + { + public InheritedMetadata(string value) : base("test", value) { } + } + + [Theory] + [InlineData("Field1", typeof(GraphQLClrOutputTypeReference))] + [InlineData("Field3", typeof(NonNullGraphType>))] + [InlineData("Field5", typeof(GraphQLClrOutputTypeReference))] + [InlineData("Field6", typeof(GraphQLClrOutputTypeReference))] //Need to fix graphql-dotnet bug 2441 first + [InlineData("Field7", typeof(GraphQLClrOutputTypeReference))] + [InlineData("Field8", typeof(NonNullGraphType>))] + [InlineData("Field9", typeof(GraphQLClrOutputTypeReference))] + [InlineData("Field10", typeof(GraphQLClrOutputTypeReference))] + public void SupportsDataLoader(string fieldName, Type graphType) + { + Configure(true); + VerifyField(fieldName, graphType, false, null); + Verify(false); + } + + public class CSupportsDataLoader : DIObjectGraphBase + { + public IDataLoaderResult Field1() => null!; + public IDataLoaderResult Field3() => null!; + public IDataLoaderResult Field5() => null!; + public IDataLoaderResult Field6() => null!; + public Task> Field7() => Task.FromResult>(null!); + public Task> Field8() => Task.FromResult>(null!); + public Task> Field9() => Task.FromResult>(null!); + public Task>> Field10() => Task.FromResult>>(null!); } } diff --git a/src/Tests/DIObjectGraphTypeTests/Graph.cs b/src/Tests/DIObjectGraphTypeTests/Graph.cs index 4961c22..159f556 100644 --- a/src/Tests/DIObjectGraphTypeTests/Graph.cs +++ b/src/Tests/DIObjectGraphTypeTests/Graph.cs @@ -1,82 +1,76 @@ -using System; using System.ComponentModel; -using GraphQL; -using GraphQL.DI; -using Shouldly; -using Xunit; -namespace DIObjectGraphTypeTests +namespace DIObjectGraphTypeTests; + +public class Graph : DIObjectGraphTypeTestBase { - public class Graph : DIObjectGraphTypeTestBase + [Fact] + public void GraphName() + { + Configure().Name.ShouldBe("TestGraphName"); + } + + [Name("TestGraphName")] + public class CGraphName : DIObjectGraphBase { } + + [Fact] + public void GraphDefaultName() + { + Configure().Name.ShouldBe("CGraphDefaultName"); + new DIObjectGraphType().Name.ShouldBe("CGraphDefaultName"); + } + + public class CGraphDefaultName : DIObjectGraphBase { } + + [Fact] + public void GraphDefaultName2() + { + Configure().Name.ShouldBe("CGraphDefaultName2"); + } + + [Fact] + public void GraphDefaultName3() + { + Configure().Name.ShouldBe("CGraphDefaultName"); + new DIObjectGraphType().Name.ShouldBe("CGraphDefaultName"); + } + + public class CGraphDefaultNameGraph : DIObjectGraphBase { } + + public class CGraphDefaultName2 : DIObjectGraphBase { } + public class Class1 { } + + [Fact] + public void GraphDescription() + { + Configure().Description.ShouldBe("TestGraphDescription"); + } + + [Description("TestGraphDescription")] + public class CGraphDescription : DIObjectGraphBase { } + + [Fact] + public void GraphObsolete() { - [Fact] - public void GraphName() - { - Configure().Name.ShouldBe("TestGraphName"); - } - - [Name("TestGraphName")] - public class CGraphName : DIObjectGraphBase { } - - [Fact] - public void GraphDefaultName() - { - Configure().Name.ShouldBe("CGraphDefaultName"); - new DIObjectGraphType().Name.ShouldBe("CGraphDefaultName"); - } - - public class CGraphDefaultName : DIObjectGraphBase { } - - [Fact] - public void GraphDefaultName2() - { - Configure().Name.ShouldBe("CGraphDefaultName2"); - } - - [Fact] - public void GraphDefaultName3() - { - Configure().Name.ShouldBe("CGraphDefaultName"); - new DIObjectGraphType().Name.ShouldBe("CGraphDefaultName"); - } - - public class CGraphDefaultNameGraph : DIObjectGraphBase { } - - public class CGraphDefaultName2 : DIObjectGraphBase { } - public class Class1 { } - - [Fact] - public void GraphDescription() - { - Configure().Description.ShouldBe("TestGraphDescription"); - } - - [Description("TestGraphDescription")] - public class CGraphDescription : DIObjectGraphBase { } - - [Fact] - public void GraphObsolete() - { #pragma warning disable CS0618 // Member is obsolete - Configure().DeprecationReason.ShouldBe("TestDeprecationReason"); + Configure().DeprecationReason.ShouldBe("TestDeprecationReason"); #pragma warning restore CS0618 // Member is obsolete - } + } - [Obsolete("TestDeprecationReason")] - public class CGraphObsolete : DIObjectGraphBase { } + [Obsolete("TestDeprecationReason")] + public class CGraphObsolete : DIObjectGraphBase { } - [Fact] - public void GraphMetadata() - { - Configure().GetMetadata("test").ShouldBe("value"); - } + [Fact] + public void GraphMetadata() + { + Configure().GetMetadata("test").ShouldBe("value"); + } - [Metadata("test", "value")] - public class CGraphMetadata : DIObjectGraphBase { } + [Metadata("test", "value")] + public class CGraphMetadata : DIObjectGraphBase { } - public class CCanOverrideMembers : DIObjectGraphBase - { - public static string Field1() => "hello"; - } + public class CCanOverrideMembers : DIObjectGraphBase + { + public static string Field1() => "hello"; } } diff --git a/src/Tests/DIObjectGraphTypeTests/Services.cs b/src/Tests/DIObjectGraphTypeTests/Services.cs index 49d27c1..5fff87c 100644 --- a/src/Tests/DIObjectGraphTypeTests/Services.cs +++ b/src/Tests/DIObjectGraphTypeTests/Services.cs @@ -1,50 +1,42 @@ -using System; -using System.Threading.Tasks; -using GraphQL; -using GraphQL.DI; -using Shouldly; -using Xunit; +namespace DIObjectGraphTypeTests; -namespace DIObjectGraphTypeTests +public class Services : DIObjectGraphTypeTestBase { - public class Services : DIObjectGraphTypeTestBase + [Fact] + public void Service() { - [Fact] - public void Service() - { - Configure(); - _serviceProviderMock.Setup(x => x.GetService(typeof(string))).Returns("hello").Verifiable(); - VerifyField("Field1", true, false, "hello"); - Verify(false); - } + Configure(); + _serviceProviderMock.Setup(x => x.GetService(typeof(string))).Returns("hello").Verifiable(); + VerifyField("Field1", true, false, "hello"); + Verify(false); + } - public class CService : DIObjectGraphBase - { - public static string Field1([FromServices] string arg) => arg; - } + public class CService : DIObjectGraphBase + { + public static string? Field1([FromServices] string? arg) => arg; + } - [Fact] - public void ServiceMissingThrows() - { - Configure(); - _serviceProviderMock.Setup(x => x.GetService(typeof(string))).Returns((string)null!).Verifiable(); - Should.Throw(() => VerifyField("Field1", true, false, "hello")); - Verify(false); - } + [Fact] + public void ServiceMissingThrows() + { + Configure(); + _serviceProviderMock.Setup(x => x.GetService(typeof(string))).Returns((string)null!).Verifiable(); + Should.Throw(() => VerifyField("Field1", true, false, "hello")); + Verify(false); + } - [Fact] - public async Task ScopedService() - { - Configure(); - _scopedServiceProviderMock.Setup(x => x.GetService(typeof(string))).Returns("hello").Verifiable(); - await VerifyFieldAsync("Field1", true, true, "hello"); - Verify(true); - } + [Fact] + public async Task ScopedService() + { + Configure(); + _scopedServiceProviderMock.Setup(x => x.GetService(typeof(string))).Returns("hello").Verifiable(); + await VerifyFieldAsync("Field1", true, true, "hello"); + Verify(true); + } - public class CScopedService : DIObjectGraphBase - { - [Scoped] - public static Task Field1([FromServices] string arg) => Task.FromResult(arg); - } + public class CScopedService : DIObjectGraphBase + { + [Scoped] + public static Task Field1([FromServices] string? arg) => Task.FromResult(arg); } } diff --git a/src/Tests/Execution/GraphQLBuilderTests.cs b/src/Tests/Execution/GraphQLBuilderTests.cs index 0578887..10ee129 100644 --- a/src/Tests/Execution/GraphQLBuilderTests.cs +++ b/src/Tests/Execution/GraphQLBuilderTests.cs @@ -1,58 +1,52 @@ -using System; -using GraphQL.DI; -using GraphQL.Types; -using Moq; -using Shouldly; -using Xunit; - -namespace Execution +using ServiceLifetime = GraphQL.DI.ServiceLifetime; + +namespace Execution; + +public class GraphQLBuilderTests { - public class GraphQLBuilderTests + private readonly Mock _mockServiceRegister = new Mock(MockBehavior.Strict); + private readonly Mock _mockGraphQLBuilder = new Mock(MockBehavior.Strict); + private IGraphQLBuilder _graphQLBuilder => _mockGraphQLBuilder.Object; + + public GraphQLBuilderTests() + { + _mockGraphQLBuilder.Setup(x => x.Services).Returns(_mockServiceRegister.Object); + } + + [Fact] + public void AddDIGraphTypes() { - private readonly Mock _mockServiceRegister = new Mock(MockBehavior.Strict); - private readonly Mock _mockGraphQLBuilder = new Mock(MockBehavior.Strict); - private IGraphQLBuilder _graphQLBuilder => _mockGraphQLBuilder.Object; - - public GraphQLBuilderTests() - { - _mockGraphQLBuilder.Setup(x => x.Services).Returns(_mockServiceRegister.Object); - } - - [Fact] - public void AddDIGraphTypes() - { - _mockServiceRegister.Setup(x => x.TryRegister(typeof(DIObjectGraphType<>), typeof(DIObjectGraphType<>), ServiceLifetime.Transient, RegistrationCompareMode.ServiceType)).Returns(_mockServiceRegister.Object).Verifiable(); - _mockServiceRegister.Setup(x => x.TryRegister(typeof(DIObjectGraphType<,>), typeof(DIObjectGraphType<,>), ServiceLifetime.Transient, RegistrationCompareMode.ServiceType)).Returns(_mockServiceRegister.Object).Verifiable(); - _graphQLBuilder.AddDIGraphTypes().ShouldBe(_graphQLBuilder); - _mockGraphQLBuilder.Verify(); - } - - [Fact] - public void AddDIClrTypeMappings() - { - IGraphTypeMappingProvider mapper = null; - _mockServiceRegister.Setup(x => x.Register(typeof(IGraphTypeMappingProvider), It.IsAny(), false)) - .Returns((_, m, _) => { - mapper = m; - return _mockServiceRegister.Object; - }); - _graphQLBuilder.AddDIClrTypeMappings(); - mapper.ShouldNotBeNull(); - - mapper.GetGraphTypeFromClrType(typeof(Class1), false, null).ShouldBe(typeof(DIObjectGraphType)); - mapper.GetGraphTypeFromClrType(typeof(Class2), false, null).ShouldBe(typeof(DIObjectGraphType)); - mapper.GetGraphTypeFromClrType(typeof(Class3), false, null).ShouldBeNull(); - - mapper.GetGraphTypeFromClrType(typeof(Class1), false, typeof(Class4)).ShouldBe(typeof(DIObjectGraphType)); - mapper.GetGraphTypeFromClrType(typeof(Class2), false, typeof(Class4)).ShouldBe(typeof(DIObjectGraphType)); - mapper.GetGraphTypeFromClrType(typeof(Class3), false, typeof(Class4)).ShouldBe(typeof(Class4)); - } - - private class Class1 { } - private class Class2 { } - private class Class3 { } - private class Class4 { } - private class Base1 : DIObjectGraphBase { } //don't register because graph1 - private class Base2 : DIObjectGraphBase { } //register because graph2 is input + _mockServiceRegister.Setup(x => x.TryRegister(typeof(DIObjectGraphType<>), typeof(DIObjectGraphType<>), ServiceLifetime.Transient, RegistrationCompareMode.ServiceType)).Returns(_mockServiceRegister.Object).Verifiable(); + _mockServiceRegister.Setup(x => x.TryRegister(typeof(DIObjectGraphType<,>), typeof(DIObjectGraphType<,>), ServiceLifetime.Transient, RegistrationCompareMode.ServiceType)).Returns(_mockServiceRegister.Object).Verifiable(); + _graphQLBuilder.AddDIGraphTypes().ShouldBe(_graphQLBuilder); + _mockGraphQLBuilder.Verify(); } + + [Fact] + public void AddDIClrTypeMappings() + { + IGraphTypeMappingProvider? mapper = null; + _mockServiceRegister.Setup(x => x.Register(typeof(IGraphTypeMappingProvider), It.IsAny(), false)) + .Returns((_, m, _) => { + mapper = m; + return _mockServiceRegister.Object; + }); + _graphQLBuilder.AddDIClrTypeMappings(); + mapper.ShouldNotBeNull(); + + mapper.GetGraphTypeFromClrType(typeof(Class1), false, null).ShouldBe(typeof(DIObjectGraphType)); + mapper.GetGraphTypeFromClrType(typeof(Class2), false, null).ShouldBe(typeof(DIObjectGraphType)); + mapper.GetGraphTypeFromClrType(typeof(Class3), false, null).ShouldBeNull(); + + mapper.GetGraphTypeFromClrType(typeof(Class1), false, typeof(Class4)).ShouldBe(typeof(DIObjectGraphType)); + mapper.GetGraphTypeFromClrType(typeof(Class2), false, typeof(Class4)).ShouldBe(typeof(DIObjectGraphType)); + mapper.GetGraphTypeFromClrType(typeof(Class3), false, typeof(Class4)).ShouldBe(typeof(Class4)); + } + + private class Class1 { } + private class Class2 { } + private class Class3 { } + private class Class4 { } + private class Base1 : DIObjectGraphBase { } //don't register because graph1 + private class Base2 : DIObjectGraphBase { } //register because graph2 is input } diff --git a/src/Tests/Tests.csproj b/src/Tests/Tests.csproj index d225621..41d66ab 100644 --- a/src/Tests/Tests.csproj +++ b/src/Tests/Tests.csproj @@ -1,20 +1,13 @@ - + - netcoreapp3.1 - - false - 9.0 - disable - - - - 1701;1702;1591;IDE1006;IDE0060;0618 + net6.0 + $(NoWarn);1701;1702;1591;IDE1006;IDE0060;0618;CA1707;CA1822;CS1591 - - + + @@ -33,4 +26,16 @@ + + + + + + + + + + + +