initial commit

This commit is contained in:
Yui
2025-11-26 16:50:06 -03:00
commit 5644aa0ebf
47 changed files with 800 additions and 0 deletions

View File

@@ -0,0 +1,28 @@
using Application.Interfaces.Services;
using Domain.Entities;
using GreenDonut.Data;
using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.DependencyInjection;
namespace GraphQLTEST.GraphQL.DataLoaders;
public class KillsByUserDataLoader : GroupedDataLoader<int, Kill>{
private readonly IServiceProvider _serviceProvider;
public KillsByUserDataLoader(
IServiceProvider serviceProvider,
IBatchScheduler batchScheduler,
DataLoaderOptions options): base(batchScheduler, options){
_serviceProvider = serviceProvider;
}
protected override async Task<ILookup<int, Kill>> LoadGroupedBatchAsync(IReadOnlyList<int> keys, CancellationToken cancellationToken)
{
await using var scope = _serviceProvider.CreateAsyncScope();
var service = scope.ServiceProvider.GetRequiredService<IKillService>();
return await service.GetKillsByUserId(keys)
.ToAsyncEnumerable()
.ToLookupAsync(k => k.UserId, cancellationToken: cancellationToken);
}
}

View File

@@ -0,0 +1,12 @@
using HotChocolate.Execution;
namespace GraphQLTEST.GraphQL.ErrorFilters;
public class LoggingErrorFilter : IErrorFilter
{
public IError OnError(IError error)
{
Console.WriteLine(error.Exception?.ToString() ?? error.Message);
return error;
}
}

View File

@@ -0,0 +1,23 @@
using System.Collections.ObjectModel;
using Application.Exceptions;
using HotChocolate.Execution;
namespace GraphQLTEST.GraphQL.ErrorFilters;
public class UserFriendlyErrorFilter : IErrorFilter
{
public IError OnError(IError error)
{
if (error.Exception is BaseException baseException)
{
return ErrorBuilder
.FromError(error)
.SetMessage(baseException?.Message ?? "Unknown Error")
.SetExtension("errorCategory", baseException?.Category)
.SetExtension("errorCode", baseException?.GetType().Name)
.Build();
}
return ErrorBuilder.FromError(error).SetMessage(error.Exception?.Message ?? "Unknown Error").Build();
}
}

View File

@@ -0,0 +1,6 @@
namespace GraphQLTEST.GraphQL;
public class MutationType
{
}

View File

@@ -0,0 +1,17 @@
using Application.Interfaces.Services;
using Domain.Entities;
using HotChocolate.Subscriptions;
namespace GraphQLTEST.GraphQL.Mutations;
[ExtendObjectType(nameof(MutationType))]
public class KillMutations
{
public async Task<Kill> RemoveKill(int id, [Service] IKillService service, [Service] ITopicEventSender eventSender, CancellationToken cancellationToken)
{
var kill = service.GetKillById(id);
service.RemoveKill(kill);
await eventSender.SendAsync(nameof(RemoveKill), kill, cancellationToken);
return kill;
}
}

View File

@@ -0,0 +1,32 @@
using Application;
using Application.Interfaces.Services;
using Application.Services;
using Domain.Entities;
using GraphQLTEST.GraphQL.Subscriptions;
using HotChocolate.Subscriptions;
namespace GraphQLTEST.GraphQL.Mutations;
[ExtendObjectType(nameof(MutationType))]
public class UserMutations
{
public async Task<bool> AddUser(string username, string email, string password, [Service] IUserService service) =>
await Task.Run(() => service.CreateUser(username, password, email));
public async Task<Kill> AddKill(
string victim,
double damage,
int userId,
[Service] IKillService killService,
[Service] IUserService userService,
[Service] ITopicEventSender eventSender,
CancellationToken cancellationToken)
{
var user = userService.GetUserById(userId);
var kill = new Kill
{ Id = Random.Shared.Next(0, 100), Damage = damage, User = user, UserId = user.Id, Victim = victim };
killService.AddKill(kill);
await eventSender.SendAsync(nameof(UserSubscriptions.KillAdded), kill, cancellationToken);
return kill;
}
}

View File

@@ -0,0 +1,7 @@
namespace GraphQLTEST.GraphQL.Queries;
[QueryType]
public class KillQueries
{
}

View File

@@ -0,0 +1,13 @@
using Application;
using Application.Interfaces.Services;
using Domain.Entities;
namespace GraphQLTEST.GraphQL.Queries;
[QueryType]
public class UserQueries
{
[UseProjection]
[UseFiltering]
public IEnumerable<User> GetUsers([Service] IUserService service) => service.GetAllUsers();
}

View File

@@ -0,0 +1,6 @@
namespace GraphQLTEST.GraphQL;
public class SubscriptionType
{
}

View File

@@ -0,0 +1,13 @@
using Domain.Entities;
using GraphQLTEST.GraphQL.Mutations;
using HotChocolate.Execution;
using HotChocolate.Subscriptions;
namespace GraphQLTEST.GraphQL.Subscriptions;
[ExtendObjectType(nameof(SubscriptionType))]
public class UserSubscriptions
{
[Subscribe]
public Kill KillAdded([EventMessage] Kill kill) => kill;
}

View File

@@ -0,0 +1,12 @@
using Domain.Entities;
namespace GraphQLTEST.GraphQL.Types;
public class KillNode : ObjectType<Kill>
{
protected override void Configure(IObjectTypeDescriptor<Kill> descriptor)
{
descriptor.Ignore(x => x.Id);
descriptor.Ignore(x => x.UserId);
}
}

View File

@@ -0,0 +1,26 @@
using System.Security.Principal;
using Application.Interfaces.Services;
using Domain.Entities;
using GraphQLTEST.GraphQL.DataLoaders;
using GreenDonut.Data;
using HotChocolate.Types.Pagination;
namespace GraphQLTEST.GraphQL.Types;
public class UserNode : ObjectType<User>
{
protected override void Configure(IObjectTypeDescriptor<User> descriptor)
{
descriptor.Ignore(x => x.Id);
descriptor.Ignore(x => x.Password);
descriptor.Field(x => x.Kills)
.UsePaging()
.UseFiltering()
.ParentRequires<User>(x => nameof(x.Id))
.Resolve(async (r, t) =>
{
User parent = r.Parent<User>();
return await r.DataLoader<KillsByUserDataLoader>().LoadAsync(parent.Id, t);
});
}
}

View File

@@ -0,0 +1,30 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>net10.0</TargetFramework>
<LangVersion>latest</LangVersion>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
<RootNamespace>GraphQLTEST</RootNamespace>
<OutputType>Exe</OutputType>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="GreenDonut.Data" Version="16.0.0-p.10.1" />
<PackageReference Include="GreenDonut.Data.EntityFramework" Version="16.0.0-p.10.1" />
<PackageReference Include="HotChocolate" Version="16.0.0-p.10.1" />
<PackageReference Include="HotChocolate.AspNetCore" Version="16.0.0-p.10.1" />
<PackageReference Include="HotChocolate.AspNetCore.CommandLine" Version="16.0.0-p.10.1" />
<PackageReference Include="HotChocolate.AspNetCore.Subscriptions" Version="10.5.5" />
<PackageReference Include="HotChocolate.Data" Version="16.0.0-p.10.1" />
<PackageReference Include="HotChocolate.Types.Analyzers" Version="16.0.0-p.10.1" />
<PackageReference Include="Microsoft.AspNetCore.Http" Version="2.3.0" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\Application\Application.csproj" />
<ProjectReference Include="..\Domain\Domain.csproj" />
<ProjectReference Include="..\Infrastructure\Infrastructure.csproj" />
</ItemGroup>
</Project>

42
Presentation/Program.cs Normal file
View File

@@ -0,0 +1,42 @@
using Application;
using GraphQLTEST.GraphQL;
using GraphQLTEST.GraphQL.ErrorFilters;
using GraphQLTEST.GraphQL.Mutations;
using Infrastructure;
using Infrastructure.Database;
using Microsoft.AspNetCore.Builder;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
var builder = WebApplication.CreateBuilder(args);
builder.Services.AddApplication();
builder.Services.AddInfrastructure();
builder
.Services.AddGraphQLServer()
.AddMutationType<MutationType>()
.AddSubscriptionType<SubscriptionType>()
.AddInMemorySubscriptions()
.AddTypes()
.AddPagingArguments()
.AddQueryContext()
.AddSorting()
.AddFiltering()
.AddProjections()
.AddErrorFilter<LoggingErrorFilter>()
.AddErrorFilter<UserFriendlyErrorFilter>()
.AddMutationConventions(
new MutationConventionOptions
{
InputArgumentName = "input",
InputTypeNamePattern = "{MutationName}Input",
PayloadTypeNamePattern = "{MutationName}Payload",
PayloadErrorTypeNamePattern = "{MutationName}Error",
PayloadErrorsFieldName = "errors",
ApplyToAllMutations = true,
});
var app = builder.Build();
app.MapGraphQL();
app.RunWithGraphQLCommands(args);

View File

@@ -0,0 +1 @@
[assembly: Module("Types")]