Skip to content

Commit

Permalink
Move GraphQl api to separate project (#70)
Browse files Browse the repository at this point in the history
Co-authored-by: admin <admin@DESKTOP-HNFM8BH>
  • Loading branch information
gs1993 and admin authored Dec 9, 2023
1 parent fa674ac commit cdc3478
Show file tree
Hide file tree
Showing 19 changed files with 350 additions and 112 deletions.
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -59,7 +59,7 @@ Employing [Compiled Queries](https://github.com/gs1993/SO/blob/master/SO/Logic/R

[REST](https://github.com/gs1993/SO/tree/master/SO/Api/Controllers) - with [Swagger](https://github.com/domaindrivendev/Swashbuckle.AspNetCore) documentation

[GraphQL](https://github.com/gs1993/SO/tree/master/SO/Api/GraphQL) - using [Grpc.AspNetCore](https://github.com/grpc/grpc-dotnet)
[GraphQL](https://github.com/gs1993/SO/tree/master/SO/ApiGraphQl) - using [ChilliCream GraphQL Platform](https://github.com/ChilliCream/graphql-platform)

[gRPC](https://github.com/gs1993/SO/tree/master/SO/Api/Grpc) - using [Google.Protobuf](https://github.com/protocolbuffers/protobuf)

Expand Down
1 change: 0 additions & 1 deletion SO/Api/Api.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,6 @@
</PropertyGroup>

<ItemGroup>
<None Remove="appsettings.Development.json" />
<None Remove="appsettings.json" />
</ItemGroup>

Expand Down
21 changes: 0 additions & 21 deletions SO/Api/GraphQL/Mutation.cs

This file was deleted.

18 changes: 0 additions & 18 deletions SO/Api/GraphQL/Subscription.cs

This file was deleted.

27 changes: 0 additions & 27 deletions SO/Api/GraphQL/Types/PostDetailsDtoType.cs

This file was deleted.

21 changes: 0 additions & 21 deletions SO/Api/GraphQL/Types/PostListDtoType.cs

This file was deleted.

14 changes: 1 addition & 13 deletions SO/Api/Startup.cs
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
using Api.GraphQL;
using Api.Utils;
using Api.Utils;
using ElasticSoDatabase.Utils;
using FluentValidation;
using GrpcPostServer;
Expand Down Expand Up @@ -58,7 +57,6 @@ public void ConfigureServices(IServiceCollection services)
});

AddControllers(services);
AddGraphQL(services);

services.AddElasticsearch(
Configuration.GetValue<string>("Elasticsearch:Url"),
Expand Down Expand Up @@ -113,16 +111,6 @@ private void AddDbContext(IServiceCollection services)
services.AddDbContexts(commandConnectionString, queryConectionString);
}

private static void AddGraphQL(IServiceCollection services)
{
services
.AddGraphQLServer()
.AddQueryType<Query>()
.AddMutationType<Mutation>()
.AddSubscriptionType<Subscription>()
.AddInMemorySubscriptions();
}

private static void AddControllers(IServiceCollection services)
{
services
Expand Down
21 changes: 21 additions & 0 deletions SO/ApiGraphQl/ApiGraphQl.csproj
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
<Project Sdk="Microsoft.NET.Sdk.Web">

<PropertyGroup>
<TargetFramework>net6.0</TargetFramework>
<Nullable>enable</Nullable>
<ImplicitUsings>enable</ImplicitUsings>
<UserSecretsId>656cca3d-9e2f-40e4-99e6-5d40688db1ca</UserSecretsId>
<DockerDefaultTargetOS>Linux</DockerDefaultTargetOS>
</PropertyGroup>

<ItemGroup>
<PackageReference Include="HotChocolate.AspNetCore" Version="13.7.0" />
</ItemGroup>

<ItemGroup>
<ProjectReference Include="..\Api\Api.csproj" />
<ProjectReference Include="..\Logic\Logic.csproj" />
<ProjectReference Include="..\Services\ElasticSoDatabase\ElasticSoDatabase.csproj" />
</ItemGroup>

</Project>
22 changes: 22 additions & 0 deletions SO/ApiGraphQl/Dockerfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
#See https://aka.ms/customizecontainer to learn how to customize your debug container and how Visual Studio uses this Dockerfile to build your images for faster debugging.

FROM mcr.microsoft.com/dotnet/aspnet:6.0 AS base
WORKDIR /app
EXPOSE 80
EXPOSE 443

FROM mcr.microsoft.com/dotnet/sdk:6.0 AS build
WORKDIR /src
COPY ["ApiGraphQl/ApiGraphQl.csproj", "ApiGraphQl/"]
RUN dotnet restore "ApiGraphQl/ApiGraphQl.csproj"
COPY . .
WORKDIR "/src/ApiGraphQl"
RUN dotnet build "ApiGraphQl.csproj" -c Release -o /app/build

FROM build AS publish
RUN dotnet publish "ApiGraphQl.csproj" -c Release -o /app/publish /p:UseAppHost=false

FROM base AS final
WORKDIR /app
COPY --from=publish /app/publish .
ENTRYPOINT ["dotnet", "ApiGraphQl.dll"]
6 changes: 6 additions & 0 deletions SO/ApiGraphQl/GraphQlSchema/Errors/BusinessLogicError.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
namespace ApiGraphQl.GraphQlSchema.Errors
{
public record BusinessLogicError(string Message)
{
}
}
29 changes: 29 additions & 0 deletions SO/ApiGraphQl/GraphQlSchema/Errors/ValidationError.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
namespace ApiGraphQl.GraphQlSchema.Errors
{
public record ValidationError
{
public string Message { get; }

public ValidationError(FluentValidation.Results.ValidationResult validationResult)
{
if (validationResult is null)
throw new ArgumentNullException(nameof(validationResult));
if (validationResult.IsValid)
throw new ArgumentException("Invalid validationResult state", nameof(validationResult));

var errors = validationResult.Errors?
.Select(x => x.ErrorMessage)?
.ToArray() ?? Array.Empty<string>();

Message = string.Join(" ", errors);
}

public ValidationError(string message)
{
if (string.IsNullOrWhiteSpace(message))
throw new ArgumentNullException(nameof(message));

Message = message;
}
}
}
36 changes: 36 additions & 0 deletions SO/ApiGraphQl/GraphQlSchema/Mutation.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
using Api.Args.Post;
using ApiGraphQl.GraphQlSchema.Errors;
using FluentValidation;
using Logic.BoundedContexts.Posts.Commands;
using MediatR;
using System.ComponentModel.DataAnnotations;

namespace ApiGraphQl.GraphQlSchema
{
public partial class Mutation
{
public async Task<MutationResult<int, ValidationError, BusinessLogicError>> UpVote(int postId, int userId,
IMediator mediator, IValidatorFactory validatorFactory)
{
if (postId < 0)
return new ValidationError("Invalid post id");

var validationResult = validatorFactory.GetValidator<UpVoteArgs>().Validate(new UpVoteArgs
{
UserId = userId
});
if (!validationResult.IsValid)
return new ValidationError(validationResult);

var result = await mediator.Send(new UpVoteCommand
(
postId: postId,
userId: userId
));
if (result.IsFailure)
return new BusinessLogicError(result.Error);

return 1;
}
}
}
12 changes: 4 additions & 8 deletions SO/Api/GraphQL/Query.cs → SO/ApiGraphQl/GraphQlSchema/Query.cs
Original file line number Diff line number Diff line change
@@ -1,22 +1,18 @@
using Api.Args.Post;
using Dawn;
using FluentValidation;
using HotChocolate;
using HotChocolate.Subscriptions;
using Logic.Queries.Posts.Dtos;
using Logic.Read.Posts.Dtos;
using Logic.Read.Posts.Queries;
using MediatR;
using System;
using System.Collections.Generic;
using System.Threading.Tasks;

namespace Api.GraphQL
namespace ApiGraphQl.GraphQlSchema
{
public class Query
{
public async Task<IReadOnlyList<PostListDto>> GetPostsPage(int offset, int limit,
[Service] IMediator mediator, [Service] ITopicEventSender eventSender, [Service] IValidatorFactory validatorFactory)
IMediator mediator, ITopicEventSender eventSender, IValidatorFactory validatorFactory)
{
var validationResult = validatorFactory.GetValidator<GetArgs>().Validate(new GetArgs
{
Expand All @@ -40,7 +36,7 @@ public async Task<IReadOnlyList<PostListDto>> GetPostsPage(int offset, int limit
}

public async Task<IReadOnlyList<PostListDto>> GetLastest(int size,
[Service] IMediator mediator, [Service] ITopicEventSender eventSender, [Service] IValidatorFactory validatorFactory)
IMediator mediator, ITopicEventSender eventSender, IValidatorFactory validatorFactory)
{
var validationResult = validatorFactory.GetValidator<GetLastestArgs>().Validate(new GetLastestArgs
{
Expand All @@ -60,7 +56,7 @@ public async Task<IReadOnlyList<PostListDto>> GetLastest(int size,
}

public async Task<PostDetailsDto> Get(int id,
[Service] IMediator mediator, [Service] ITopicEventSender eventSender)
IMediator mediator, ITopicEventSender eventSender)
{
Guard.Argument(id).Positive();

Expand Down
67 changes: 67 additions & 0 deletions SO/ApiGraphQl/Program.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
using Api;
using ApiGraphQl.GraphQlSchema;
using ApiGraphQl.Utils;
using ElasticSoDatabase.Utils;
using FluentValidation;
using HotChocolate.Subscriptions;
using Logic.Utils;
using Logic.Utils.Db;
using MediatR;
using Microsoft.FeatureManagement;
using Microsoft.FeatureManagement.FeatureFilters;

var builder = WebApplication.CreateBuilder(args);

builder.Services.AddSingleton<IDateTimeProvider, DateTimeProvider>();

string commandConnectionString = builder.Configuration.GetConnectionString("SO_Database");
string queryConectionString = builder.Configuration.GetConnectionString("SO_ReadonlyDatabase");

builder.Services.AddDbContexts(commandConnectionString, queryConectionString);

builder.Services.AddElasticsearch(
builder.Configuration.GetValue<string>("Elasticsearch:Url"),
builder.Configuration.GetValue<string>("Elasticsearch:DefaultIndexName"));

builder.Services.AddMediatR(
typeof(DatabaseContext).Assembly,
typeof(ElasticsearchExtensions).Assembly);

AddFeatureFlags(builder);

AddFluentValidation(builder);

builder.Services
.AddGraphQLServer()
.RegisterService<IMediator>()
.RegisterService<IValidatorFactory>()
.RegisterService<ITopicEventSender>()
.AddQueryType<Query>()
.AddMutationType<Mutation>()
.AddMutationConventions()
.AddInMemorySubscriptions();

var app = builder.Build();

app.MapGraphQL();

app.Run();


static void AddFeatureFlags(WebApplicationBuilder builder)
{
builder.Services.AddSingleton<IHttpContextAccessor, HttpContextAccessor>();
builder.Services.AddSingleton<ITargetingContextAccessor, HttpContextTargetingContextAccessor>();

builder.Services
.AddFeatureManagement(builder.Configuration.GetSection("FeatureFlags"))
.AddFeatureFilter<PercentageFilter>()
.AddFeatureFilter<TimeWindowFilter>()
.AddFeatureFilter<TargetingFilter>();
}

static void AddFluentValidation(WebApplicationBuilder builder)
{
builder.Services.AddValidatorsFromAssemblyContaining<Startup>();
builder.Services.AddTransient<IValidatorFactory, ServiceProviderValidatorFactory>();
}
24 changes: 24 additions & 0 deletions SO/ApiGraphQl/Properties/launchSettings.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
{
"profiles": {
"ApiGraphQl": {
"commandName": "Project",
"launchBrowser": false,
"launchUrl": "graphql",
"environmentVariables": {
"ASPNETCORE_ENVIRONMENT": "Development"
},
"dotnetRunMessages": true,
"applicationUrl": "https://localhost:7039;http://localhost:5121"
},
"Docker": {
"commandName": "Docker",
"launchBrowser": true,
"launchUrl": "{Scheme}://{ServiceHost}:{ServicePort}/graphql",
"environmentVariables": {
"ASPNETCORE_URLS": "https://+:443;http://+:80"
},
"publishAllPorts": true,
"useSSL": true
}
}
}
Loading

0 comments on commit cdc3478

Please sign in to comment.