Skip to content

Commit

Permalink
Merge pull request #12 from sentemon/postservice-add-graphql-api
Browse files Browse the repository at this point in the history
Added GraphQL Api and Configured MassTransit between microservices
  • Loading branch information
sentemon authored Dec 31, 2024
2 parents e8dad15 + b270fa0 commit da51659
Show file tree
Hide file tree
Showing 56 changed files with 892 additions and 131 deletions.
34 changes: 26 additions & 8 deletions FitnessApp.sln
Original file line number Diff line number Diff line change
Expand Up @@ -30,15 +30,8 @@ EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "PostService.Application.Tests", "backend\tests\PostService\PostService.Application.Tests\PostService.Application.Tests.csproj", "{5325FC87-0538-4AE1-8979-F9597F3FF466}"
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".github", ".github", "{D5A12D19-86E3-4E83-8BF0-D9AF8C2477F3}"
ProjectSection(SolutionItems) = preProject
.github\actions\build-and-test\action.yml = .github\actions\build-and-test\action.yml
.github\workflows\auth.yml = .github\workflows\auth.yml
.github\workflows\post.yml = .github\workflows\post.yml
.github\workflows\frontend.yml = .github\workflows\frontend.yml
.github\workflows\compose-build.yml = .github\workflows\compose-build.yml
EndProjectSection
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "solution-items", "solution-items", "{CE086396-E7FB-46F7-87A9-4570DBF43597}"
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "items", "items", "{CE086396-E7FB-46F7-87A9-4570DBF43597}"
ProjectSection(SolutionItems) = preProject
.dockerignore = .dockerignore
.gitignore = .gitignore
Expand Down Expand Up @@ -66,6 +59,23 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "AuthService", "backend\test
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "AuthService.Domain.Tests", "backend\tests\AuthService\AuthService.Domain.Tests\AuthService.Domain.Tests.csproj", "{528F427F-AFA0-409E-9ED1-AD630CA56BB8}"
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "workflows", "workflows", "{6EFF3C2A-91CF-4511-A60C-D542F1A939CD}"
ProjectSection(SolutionItems) = preProject
.github\workflows\auth.yml = .github\workflows\auth.yml
.github\workflows\compose-build.yml = .github\workflows\compose-build.yml
.github\workflows\frontend.yml = .github\workflows\frontend.yml
.github\workflows\post.yml = .github\workflows\post.yml
EndProjectSection
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "actions", "actions", "{499BA3B4-A81F-4079-B256-CC04927BB06D}"
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "build-and-test", "build-and-test", "{BC8EDBE4-B8BD-4934-B096-0C2E1C1E84C7}"
ProjectSection(SolutionItems) = preProject
.github\actions\build-and-test\action.yml = .github\actions\build-and-test\action.yml
EndProjectSection
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Shared.DTO", "backend\src\Shared\Shared.DTO\Shared.DTO.csproj", "{334DF17C-BD69-47B9-B4D0-ACB5AD6223CF}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
Expand Down Expand Up @@ -128,6 +138,10 @@ Global
{528F427F-AFA0-409E-9ED1-AD630CA56BB8}.Debug|Any CPU.Build.0 = Debug|Any CPU
{528F427F-AFA0-409E-9ED1-AD630CA56BB8}.Release|Any CPU.ActiveCfg = Release|Any CPU
{528F427F-AFA0-409E-9ED1-AD630CA56BB8}.Release|Any CPU.Build.0 = Release|Any CPU
{334DF17C-BD69-47B9-B4D0-ACB5AD6223CF}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{334DF17C-BD69-47B9-B4D0-ACB5AD6223CF}.Debug|Any CPU.Build.0 = Debug|Any CPU
{334DF17C-BD69-47B9-B4D0-ACB5AD6223CF}.Release|Any CPU.ActiveCfg = Release|Any CPU
{334DF17C-BD69-47B9-B4D0-ACB5AD6223CF}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(NestedProjects) = preSolution
{2E44C019-ED30-4671-A878-D2F7B6071024} = {84AE3F00-9A59-4A8D-A737-67AB74E5D1FD}
Expand All @@ -149,5 +163,9 @@ Global
{C494E5BB-0B0C-43D6-82AD-27D1FC26FA29} = {C7DC7946-2C31-4266-BF89-17E15C5B5A63}
{CB2BFFE4-D7CD-47E0-B42A-150DF411B7E9} = {2B7719FF-D7B4-4EFA-B3C5-2240EC19A1A5}
{528F427F-AFA0-409E-9ED1-AD630CA56BB8} = {CB2BFFE4-D7CD-47E0-B42A-150DF411B7E9}
{6EFF3C2A-91CF-4511-A60C-D542F1A939CD} = {D5A12D19-86E3-4E83-8BF0-D9AF8C2477F3}
{499BA3B4-A81F-4079-B256-CC04927BB06D} = {D5A12D19-86E3-4E83-8BF0-D9AF8C2477F3}
{BC8EDBE4-B8BD-4934-B096-0C2E1C1E84C7} = {499BA3B4-A81F-4079-B256-CC04927BB06D}
{334DF17C-BD69-47B9-B4D0-ACB5AD6223CF} = {112E9C63-BF62-49C1-AD19-03855535225B}
EndGlobalSection
EndGlobal
2 changes: 1 addition & 1 deletion backend/src/AuthService/AuthService.Api/Program.cs
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@
builder.Services
.AddPersistenceServices(connectionString)
.AddInfrastructureServices(builder.Configuration)
.AddApplicationServices();
.AddApplicationServices(builder.Configuration);

builder.Services
.AddGraphQLServer()
Expand Down
5 changes: 5 additions & 0 deletions backend/src/AuthService/AuthService.Api/appsettings.json
Original file line number Diff line number Diff line change
Expand Up @@ -17,5 +17,10 @@
"AdminUsername": "admin",
"AdminPassword": "admin",
"PublicKey": "MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA1Q/E+u/w56fl1C67jv2glu/wjy50re4UbVqJHX+T84hN8lGjiHRxqmEgdsyArhiXK0XBbbolPJ+jLTXY7KAKXXtsCjNOV/WLCKFi9Gdn2vgrmD+g2BatpRvO33nytwlDZSkZSnFpTY3io6ZcMCB+YQK5i+2QOz1gahJt5Dac+bwZ96d3x+dEP+0lZ3+3VTe0bKWRMDked2f8E4K9mvywyUEf3Ihe/2YVhXZyUnshMrRmn6ZZ4DvpQcrHMtGMCBiB5N6pllxBu5XAcxYPRxZtx+q4GSU1bCA9es4RNgLTBGP8GQXVVqlq80j8oxR1SSigqLNbA7qcGi4+rmWg/ohdkQIDAQAB"
},
"RabbitMq": {
"Host": "localhost",
"Username": "guest",
"Password": "guest"
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -8,9 +8,14 @@

<ItemGroup>
<ProjectReference Include="..\..\Shared\Shared.Application\Shared.Application.csproj" />
<ProjectReference Include="..\..\Shared\Shared.DTO\Shared.DTO.csproj" />
<ProjectReference Include="..\AuthService.Domain\AuthService.Domain.csproj" />
<ProjectReference Include="..\AuthService.Infrastructure\AuthService.Infrastructure.csproj" />
<ProjectReference Include="..\AuthService.Persistence\AuthService.Persistence.csproj" />
</ItemGroup>

<ItemGroup>
<PackageReference Include="MassTransit.RabbitMQ" Version="8.0.0" />
</ItemGroup>

</Project>
Original file line number Diff line number Diff line change
@@ -1,20 +1,24 @@
using AuthService.Infrastructure.Interfaces;
using AuthService.Infrastructure.Models;
using AuthService.Persistence;
using MassTransit;
using Shared.Application.Abstractions;
using Shared.Application.Common;
using Shared.DTO;

namespace AuthService.Application.Commands.Register;

public class RegisterCommandHandler : ICommandHandler<RegisterCommand, KeycloakTokenResponse>
{
private readonly AuthDbContext _context;
private readonly IAuthService _authService;
private readonly IPublishEndpoint _publishEndpoint;

public RegisterCommandHandler(AuthDbContext context, IAuthService authService)
public RegisterCommandHandler(AuthDbContext context, IAuthService authService, IPublishEndpoint publishEndpoint)
{
_context = context;
_authService = authService;
_publishEndpoint = publishEndpoint;
}

public async Task<IResult<KeycloakTokenResponse, Error>> HandleAsync(RegisterCommand command)
Expand All @@ -30,6 +34,15 @@ public async Task<IResult<KeycloakTokenResponse, Error>> HandleAsync(RegisterCom
_context.Users.Add(user);
await _context.SaveChangesAsync();

await _publishEndpoint.Publish(new UserCreatedEvent(
user.Id,
user.FirstName,
user.LastName,
user.Username.Value,
user.ImageUrl,
user.CreatedAt
));

var token = await _authService.LoginAsync(command.RegisterDto.Username, command.RegisterDto.Password);

return Result<KeycloakTokenResponse>.Success(token);
Expand Down
Original file line number Diff line number Diff line change
@@ -1,21 +1,25 @@
using AuthService.Domain.Constants;
using AuthService.Infrastructure.Interfaces;
using AuthService.Persistence;
using MassTransit;
using Microsoft.EntityFrameworkCore;
using Shared.Application.Abstractions;
using Shared.Application.Common;
using Shared.DTO;

namespace AuthService.Application.Commands.UpdateUser;

public class UpdateUserCommandHandler : ICommandHandler<UpdateUserCommand, string>
{
private readonly IUserService _userService;
private readonly AuthDbContext _context;
private readonly IPublishEndpoint _publishEndpoint;

public UpdateUserCommandHandler(IUserService userService, AuthDbContext context)
public UpdateUserCommandHandler(IUserService userService, AuthDbContext context, IPublishEndpoint publishEndpoint)
{
_userService = userService;
_context = context;
_publishEndpoint = publishEndpoint;
}

public async Task<IResult<string, Error>> HandleAsync(UpdateUserCommand command)
Expand Down Expand Up @@ -48,6 +52,14 @@ public async Task<IResult<string, Error>> HandleAsync(UpdateUserCommand command)
);

await _context.SaveChangesAsync();

await _publishEndpoint.Publish(new UserUpdatedEvent(
user.Id,
user.FirstName,
user.LastName,
user.Username.Value,
user.ImageUrl
));

return Result<string>.Success(ResponseMessages.UserUpdatedSuccessfully);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,13 +7,16 @@
using AuthService.Application.Commands.VerifyEmail;
using AuthService.Application.Queries.GetUserById;
using AuthService.Application.Queries.GetUserByUsername;
using AuthService.Domain.Constants;
using MassTransit;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;

namespace AuthService.Application;

public static class DependencyInjection
{
public static IServiceCollection AddApplicationServices(this IServiceCollection services)
public static IServiceCollection AddApplicationServices(this IServiceCollection services, IConfiguration configuration)
{
services.AddScoped<LoginCommandHandler>();
services.AddScoped<LogoutCommandHandler>();
Expand All @@ -24,6 +27,24 @@ public static IServiceCollection AddApplicationServices(this IServiceCollection
services.AddScoped<VerifyEmailCommandHandler>();
services.AddScoped<GetUserByIdQueryHandler>();
services.AddScoped<GetUserByUsernameQueryHandler>();

var rabbitMqHost = configuration[AppSettingsConstants.RabbitMqHost] ?? throw new ArgumentException("RabbitMQ Host is not configured.");
var rabbitMqUsername = configuration[AppSettingsConstants.RabbitMqUsername] ?? throw new ArgumentException("RabbitMQ Username is not configured.");
var rabbitMqPassword = configuration[AppSettingsConstants.RabbitMqPassword] ?? throw new ArgumentException("RabbitMQ Password is not configured.");

services.AddMassTransit(busConfigurator =>
{
busConfigurator.UsingRabbitMq((context, configurator) =>
{
configurator.Host(rabbitMqHost, host =>
{
host.Username(rabbitMqUsername);
host.Password(rabbitMqPassword);
});

configurator.ConfigureEndpoints(context);
});
});

return services;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,4 +16,8 @@ public static class AppSettingsConstants
public const string AdminUsername = "AdminUsername";
public const string AdminPassword = "AdminPassword";
public const string PublicKey = "PublicKey";

public const string RabbitMqHost = "RabbitMq:Host";
public const string RabbitMqUsername = "RabbitMq:Username";
public const string RabbitMqPassword = "RabbitMq:Password";
}
1 change: 1 addition & 0 deletions backend/src/AuthService/Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ COPY AuthService/AuthService.Domain/AuthService.Domain.csproj AuthService/AuthSe
COPY AuthService/AuthService.Infrastructure/AuthService.Infrastructure.csproj AuthService/AuthService.Infrastructure/
COPY AuthService/AuthService.Persistence/AuthService.Persistence.csproj AuthService/AuthService.Persistence/
COPY Shared/Shared.Application/Shared.Application.csproj Shared/Shared.Application/
COPY Shared/Shared.DTO/Shared.DTO.csproj Shared/Shared.DTO/

RUN dotnet restore AuthService/AuthService.Api/AuthService.Api.csproj

Expand Down
1 change: 1 addition & 0 deletions backend/src/PostService/Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ COPY PostService/PostService.Domain/PostService.Domain.csproj PostService/PostSe
COPY PostService/PostService.Infrastructure/PostService.Infrastructure.csproj PostService/PostService.Infrastructure/
COPY PostService/PostService.Persistence/PostService.Persistence.csproj PostService/PostService.Persistence/
COPY Shared/Shared.Application/Shared.Application.csproj Shared/Shared.Application/
COPY Shared/Shared.DTO/Shared.DTO.csproj Shared/Shared.DTO/

RUN dotnet restore PostService/PostService.Api/PostService.Api.csproj

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
using System.Security.Claims;
using PostService.Application.Commands.AddComment;
using PostService.Application.Commands.DeleteComment;
using PostService.Application.DTOs;

namespace PostService.Api.GraphQL.Mutations;

public class CommentMutation
{
private readonly IHttpContextAccessor _httpContextAccessor;

public CommentMutation(IHttpContextAccessor httpContextAccessor)
{
_httpContextAccessor = httpContextAccessor;
}

public async Task<CommentDto> CreateComment(CreateCommentDto input, [Service] AddCommentCommandHandler addCommentCommandHandler)
{
var userId = _httpContextAccessor.HttpContext?.User.FindFirst(ClaimTypes.NameIdentifier)?.Value;
var command = new AddCommentCommand(input, userId);

var result = await addCommentCommandHandler.HandleAsync(command);

if (!result.IsSuccess)
{
throw new GraphQLException(new Error(result.Error.Message));
}

return result.Response;
}

public async Task<string> DeleteComment(Guid id, Guid postId, [Service] DeleteCommentCommandHandler deleteCommentCommandHandler)
{
var userId = _httpContextAccessor.HttpContext?.User.FindFirst(ClaimTypes.NameIdentifier)?.Value;
var command = new DeleteCommentCommand(id, postId, userId);

var result = await deleteCommentCommandHandler.HandleAsync(command);

if (!result.IsSuccess)
{
throw new GraphQLException(new Error(result.Error.Message));
}

return result.Response;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
using System.Security.Claims;
using PostService.Application.Commands.AddLike;
using PostService.Application.Commands.DeleteLike;
using PostService.Application.DTOs;

namespace PostService.Api.GraphQL.Mutations;

public class LikeMutation
{
private readonly IHttpContextAccessor _httpContextAccessor;

public LikeMutation(IHttpContextAccessor httpContextAccessor)
{
_httpContextAccessor = httpContextAccessor;
}

public async Task<LikeDto> AddLike(Guid postId, [Service] AddLikeCommandHandler addLikeCommandHandler)
{
var userId = _httpContextAccessor.HttpContext?.User.FindFirst(ClaimTypes.NameIdentifier)?.Value;
var command = new AddLikeCommand(postId, userId);

var result = await addLikeCommandHandler.HandleAsync(command);

if (!result.IsSuccess)
{
throw new GraphQLException(new Error(result.Error.Message));
}

return result.Response;
}

public async Task<string> DeleteLike(Guid id, Guid postId, [Service] DeleteLikeCommandHandler deleteLikeCommandHandler)
{
var userId = _httpContextAccessor.HttpContext?.User.FindFirst(ClaimTypes.NameIdentifier)?.Value;
var command = new DeleteLikeCommand(id, postId, userId);

var result = await deleteLikeCommandHandler.HandleAsync(command);

if (!result.IsSuccess)
{
throw new GraphQLException(new Error(result.Error.Message));
}

return result.Response;
}
}

This file was deleted.

Loading

0 comments on commit da51659

Please sign in to comment.