Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
21 changes: 21 additions & 0 deletions .editorconfig
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,27 @@ indent_size = 2

#### Core EditorConfig Options ####

dotnet_naming_rule.interfaces_should_be_prefixed_with_i.severity = error
dotnet_naming_rule.interfaces_should_be_prefixed_with_i.symbols = interface_symbols
dotnet_naming_rule.interfaces_should_be_prefixed_with_i.style = prefix_interface_with_i

dotnet_naming_symbols.interface_symbols.applicable_kinds = interface
dotnet_naming_symbols.interface_symbols.applicable_accessibilities = public, internal, private, protected, protected_internal, private_protected

dotnet_naming_style.prefix_interface_with_i.required_prefix = I
dotnet_naming_style.prefix_interface_with_i.capitalization = pascal_case

dotnet_naming_rule.enums_should_be_prefixed_with_e.severity = error
dotnet_naming_rule.enums_should_be_prefixed_with_e.symbols = enum_symbols
dotnet_naming_rule.enums_should_be_prefixed_with_e.style = prefix_enum_with_e

dotnet_naming_symbols.enum_symbols.applicable_kinds = enum
dotnet_naming_symbols.enum_symbols.applicable_accessibilities = public, internal, private, protected, protected_internal, private_protected

dotnet_naming_style.prefix_enum_with_e.required_prefix = E
dotnet_naming_style.prefix_enum_with_e.capitalization = pascal_case


# Indentation and spacing
indent_size = 4
tab_width = 4
Expand Down
31 changes: 31 additions & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
name: 🚀 CI Pipeline

on:
push:
branches:
- main
pull_request:
branches:
- main

jobs:
test-and-build:
runs-on: ubuntu-latest

steps:
- name: 📥 Checkout code
uses: actions/checkout@v4

- name: ⚙️ Setup .NET 9
uses: actions/setup-dotnet@v4
with:
dotnet-version: '9.0.x'

- name: 📦 Restore NuGet packages
run: dotnet restore

- name: 🔨 Build in Release mode
run: dotnet build -c Release --no-restore

- name: 🧪 Run all tests
run: dotnet test -c Release --no-build --verbosity minimal
5 changes: 5 additions & 0 deletions Directory.Packages.props
Original file line number Diff line number Diff line change
Expand Up @@ -46,4 +46,9 @@
<PackageVersion Include="Microsoft.EntityFrameworkCore.InMemory" Version="9.0.6" />
<PackageVersion Include="MockQueryable.Moq" Version="7.0.3" />
</ItemGroup>
<!-- Pacotes AWS -->
<ItemGroup>
<PackageVersion Include="AWSSDK.Extensions.NETCore.Setup" Version="4.0.2" />
<PackageVersion Include="AWSSDK.SimpleEmail" Version="4.0.0.14" />
</ItemGroup>
</Project>
14 changes: 7 additions & 7 deletions src/SnackFlow.Api/Common/Api/BuilderExtension.cs
Original file line number Diff line number Diff line change
Expand Up @@ -6,21 +6,21 @@
using Microsoft.AspNetCore.Mvc;
using Microsoft.IdentityModel.Tokens;
using Microsoft.OpenApi.Models;
using SnackFlow.Application.Abstractions.Services;
using SnackFlow.Infrastructure.Persistence;
using SnackFlow.Infrastructure.Persistence.Identity;
using SnackFlow.Infrastructure.Services.Abstractions;
using SnackFlow.Infrastructure.Settings;

namespace SnackFlow.Api.Common.Api;

public static class BuilderExtension
{
public static async Task AddPipeline(this WebApplicationBuilder builder)
public static void AddPipeline(this WebApplicationBuilder builder)
{
builder.AddDocumentationApi();
builder.AddDependencyInjection();
builder.AddConfigurations();
await builder.AddSecurity();
builder.AddSecurity();
}

private static void AddDependencyInjection(this WebApplicationBuilder builder)
Expand Down Expand Up @@ -66,7 +66,7 @@ private static void AddConfigurations(this WebApplicationBuilder builder)
});
}

private static async Task AddSecurity(this WebApplicationBuilder builder)
private static void AddSecurity(this WebApplicationBuilder builder)
{
var accessToken =
builder.Configuration.GetSection(nameof(AccessTokenSettings)).Get<AccessTokenSettings>()
Expand All @@ -76,14 +76,14 @@ private static async Task AddSecurity(this WebApplicationBuilder builder)
builder.Configuration.GetSection(nameof(RefreshTokenSettings)).Get<RefreshTokenSettings>()
?? throw new InvalidOperationException($"{nameof(RefreshTokenSettings)} configuration not found");

await using var provider = builder.Services.BuildServiceProvider();
using var provider = builder.Services.BuildServiceProvider();
var certificateService = provider.GetRequiredService<ICertificateService>();
var accessCertificate = await certificateService.LoadCertificateAsync(
var accessCertificate = certificateService.LoadCertificateAsync(
accessToken.Key,
accessToken.Password
);

var refreshCertificate = await certificateService.LoadCertificateAsync(
var refreshCertificate = certificateService.LoadCertificateAsync(
refreshToken.Key,
refreshToken.Password
);
Expand Down
2 changes: 1 addition & 1 deletion src/SnackFlow.Api/Program.cs
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
using SnackFlow.Api.Common.Api;

var builder = WebApplication.CreateBuilder(args);
await builder.AddPipeline();
builder.AddPipeline();

var app = builder.Build();
app.UsePipeline();
Expand Down
4 changes: 4 additions & 0 deletions src/SnackFlow.Api/appsettings.json
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,10 @@
"ConnectionStrings": {
"DefaultConnection": "Host=localhost;Database=snackflow_db;Username=postgres;Password=root;Port=5432"
},
"AWS": {
"Profile": "default",
"Region": "us-east-1"
},
"AccessTokenSettings": {
"Key": "Common/Certificates/access-token-jwt-key.pfx",
"Password": "root",
Expand Down
12 changes: 12 additions & 0 deletions src/SnackFlow.Application/Abstractions/Events/IApplicationEvent.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
using MediatR;

namespace SnackFlow.Application.Abstractions.Events;

/// <summary>
/// Representa uma interface marcadora para eventos no nível de aplicação dentro do domínio.
/// Um evento de aplicação é usado para comunicar mudanças de estado, ações ou ocorrências
/// que são significativas para a camada de aplicação.
/// Esta interface estende a interface <see cref="INotification"/> fornecida pelo MediatR,
/// permitindo o padrão publish-subscribe na aplicação.
/// </summary>
public interface IApplicationEvent : INotification;
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
using MediatR;

namespace SnackFlow.Application.Abstractions.Events;

/// <summary>
/// Representa um manipulador de eventos no nível de aplicação que implementa a interface <see cref="IApplicationEvent"/>.
/// Um manipulador de eventos de aplicação é responsável por processar eventos específicos da camada
/// de aplicação e executar a lógica de negócio correspondente.
/// Esta interface integra-se com a biblioteca MediatR para suportar o padrão de notificação de eventos.
/// </summary>
/// <typeparam name="T">
/// O tipo do evento de aplicação sendo manipulado. Deve implementar a interface <see cref="IApplicationEvent"/>.
/// </typeparam>
public interface IApplicationEventHandler<in T> : INotificationHandler<T>
where T : IApplicationEvent;
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
using MediatR;
using SnackFlow.Domain.Abstractions;

namespace SnackFlow.Application.Abstractions.Events;

/// <summary>
/// Representa um manipulador de eventos de domínio no contexto do padrão Domain-Driven Design (DDD).
/// Um manipulador de eventos de domínio é responsável por encapsular a lógica que reage a
/// um determinado evento de domínio e processa as operações de negócio correspondentes.
/// Esta interface integra-se com a biblioteca MediatR para suportar notificações de eventos.
/// </summary>
/// <typeparam name="T">
/// O tipo do evento de domínio sendo manipulado. Deve implementar a interface <see cref="IDomainEvent"/>.
/// </typeparam>
public interface IDomainEventHandler<in T> : INotificationHandler<T>
where T : IDomainEvent;
Original file line number Diff line number Diff line change
@@ -1,11 +1,11 @@
using System.Security.Cryptography.X509Certificates;

namespace SnackFlow.Infrastructure.Services.Abstractions;
namespace SnackFlow.Application.Abstractions.Services;

/// <summary>
/// Define um serviço para carregamento de certificados X.509.
/// </summary>
public interface ICertificateService
{
Task<X509Certificate2> LoadCertificateAsync(string key, string password);
X509Certificate2 LoadCertificateAsync(string key, string password);
}
19 changes: 19 additions & 0 deletions src/SnackFlow.Application/Abstractions/Services/IEmailService.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
using SnackFlow.Domain.Enums;

namespace SnackFlow.Application.Abstractions.Services;

/// <summary>
/// Define um serviço para envio de emails.
/// </summary>
public interface IEmailService
{
/// <summary>
/// Envia um e-mail de forma assíncrona.
/// </summary>
/// <param name="to">O endereço de e-mail do destinatário.</param>
/// <param name="subject">O assunto do e-mail.</param>
/// <param name="body">O conteúdo do corpo do e-mail.</param>
/// <param name="emailAddress">O endereço de e-mail do remetente, representado como uma enumeração.</param>
/// <returns>Uma tarefa que representa a operação assíncrona de envio do e-mail.</returns>
Task SendAsync(string to, string subject, string body, EEmailAddress emailAddress);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
using SnackFlow.Domain.Enums;

namespace SnackFlow.Application.Abstractions.Services;

/// <summary>
/// Define um serviço para gerenciar e gerar templates de e-mail dinamicamente com base nos dados e tipo de template especificados.
/// </summary>
/// <typeparam name="T">O tipo do modelo de dados a ser usado para renderizar o template.</typeparam>
public interface IEmailTemplateService<in T> where T : class
{
/// <summary>
/// Recupera e processa de forma assíncrona um template de email com base no público-alvo, tipo de template e modelo de dados especificados.
/// Substitui os placeholders no template pelos valores correspondentes fornecidos nos dados.
/// </summary>
/// <param name="audience">O público-alvo do template de email, especificado como <see cref="EEmailAudience"/>.</param>
/// <param name="template">O tipo de template de email a ser recuperado, especificado como <see cref="EEmailTemplate"/>.</param>
/// <param name="data">O modelo de dados contendo as propriedades para substituir os placeholders no template.</param>
/// <returns>Uma task que representa a operação assíncrona. O resultado da task contém o template de email processado como uma string.</returns>
Task<string> GetTemplateAsync(EEmailAudience audience, EEmailTemplate template, T data);
}
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,7 @@ public async Task<Result<UpdateCompanyCommandResponse>> Handle(UpdateCompanyComm
}
catch (Exception ex)
{
logger.LogError(ex.Message, ex.StackTrace);
logger.LogError(ex, $"error in class: {nameof(UpdateCompanyCommandHandler)}");
await unitOfWork.RollbackTransactionAsync(cancellationToken);
throw;
}
Expand Down
15 changes: 15 additions & 0 deletions src/SnackFlow.Domain/Enums/EEmailAddress.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
using System.ComponentModel;

namespace SnackFlow.Domain.Enums;

public enum EEmailAddress
{
[Description("noreply@snackflow.app")]
NoReply = 1,
[Description("contact@snackflow.app")]
Contact = 2,
[Description("security@snackflow.app")]
Security = 3,
[Description("support@snackflow.app")]
Support = 4
}
11 changes: 11 additions & 0 deletions src/SnackFlow.Domain/Enums/EEmailAudience.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
using System.ComponentModel;

namespace SnackFlow.Domain.Enums;

public enum EEmailAudience
{
[Description("company")]
Company = 1,
[Description("user")]
User = 2
}
27 changes: 27 additions & 0 deletions src/SnackFlow.Domain/Enums/EEmailTemplate.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
using System.ComponentModel;

namespace SnackFlow.Domain.Enums;

public enum EEmailTemplate
{
[Description("welcome.html")]
Welcome = 1,

[Description("account-activation.html")]
AccountActivation = 2,

[Description("password-reset.html")]
PasswordReset = 3,

[Description("email-verification.html")]
EmailVerification = 4,

[Description("report-send.html")]
ReportSend = 5,

[Description("login-notification.html")]
LoginNotification = 6,

[Description("system-maintenance.html")]
SystemMaintenance = 7
}
16 changes: 13 additions & 3 deletions src/SnackFlow.Infrastructure/DependencyInjection.cs
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
using SnackFlow.Infrastructure.Persistence;
using Amazon.SimpleEmail;
using SnackFlow.Infrastructure.Persistence;
using SnackFlow.Infrastructure.Persistence.Interceptors;
using SnackFlow.Infrastructure.Persistence.Repositories;
using Microsoft.EntityFrameworkCore;
Expand All @@ -7,9 +8,10 @@
using Microsoft.Extensions.Logging;
using Serilog;
using Serilog.Events;
using SnackFlow.Application.Abstractions.Services;
using SnackFlow.Domain.Repositories;
using SnackFlow.Infrastructure.Services;
using SnackFlow.Infrastructure.Services.Abstractions;
using SnackFlow.Infrastructure.Services.EmailTemplateService;

namespace SnackFlow.Infrastructure;

Expand All @@ -27,6 +29,7 @@ public static void AddInfrastructure(this IServiceCollection services, IConfigur
services.AddPersistence(configuration);
services.AddRepositories();
services.AddServices();
services.AddAwsServices(configuration);
services.AddHealthChecksConfiguration(configuration);
}

Expand Down Expand Up @@ -80,7 +83,14 @@ private static void AddRepositories(this IServiceCollection services)
private static void AddServices(this IServiceCollection services)
{
services.AddTransient<ICertificateService, CertificateService>();
services.AddTransient<ISecretService, AwsSecretService>();
services.AddTransient(typeof(IEmailTemplateService<>), typeof(EmailTemplateService<>));
services.AddTransient<IEmailService, EmailService>();
}

private static void AddAwsServices(this IServiceCollection services, IConfiguration configuration)
{
services.AddDefaultAWSOptions(configuration.GetAWSOptions());
services.AddAWSService<IAmazonSimpleEmailService>();
}

private static void AddHealthChecksConfiguration(this IServiceCollection services, IConfiguration configuration)
Expand Down

This file was deleted.

Loading