Skip to content
Open
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
18 changes: 10 additions & 8 deletions Api/Dominio/Interfaces/IAdministradorServico.cs
Original file line number Diff line number Diff line change
@@ -1,13 +1,15 @@

using MinimalApi.Dominio.Entidades;
using MinimalApi.DTOs;

namespace MinimalApi.Dominio.Interfaces;

public interface IAdministradorServico
namespace MinimalApi.Dominio.Interfaces
{
Administrador? Login(LoginDTO loginDTO);
Administrador Incluir(Administrador administrador);
Administrador? BuscaPorId(int id);
List<Administrador> Todos(int? pagina);
public interface IAdministradorServico
{
void Atualizar(Administrador administrador);
void Apagar(int id);
Administrador? Login(LoginDTO loginDTO);
Administrador Incluir(Administrador administrador);
Administrador? BuscaPorId(int id);
List<Administrador> Todos(int? pagina);
}
}
37 changes: 31 additions & 6 deletions Api/Dominio/Servicos/AdministradorServico.cs
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
using MinimalApi.DTOs;
using MinimalApi.Infraestrutura.Db;
using MinimalApi.Dominio.Interfaces;
using BCrypt.Net;

namespace MinimalApi.Dominio.Servicos;

Expand All @@ -20,27 +21,51 @@ public AdministradorServico(DbContexto contexto)

public Administrador Incluir(Administrador administrador)
{
// Hash da senha antes de salvar
administrador.Senha = BCrypt.Net.BCrypt.HashPassword(administrador.Senha);
_contexto.Administradores.Add(administrador);
_contexto.SaveChanges();

return administrador;
}

public Administrador? Login(LoginDTO loginDTO)
{
var adm = _contexto.Administradores.Where(a => a.Email == loginDTO.Email && a.Senha == loginDTO.Senha).FirstOrDefault();
return adm;
var adm = _contexto.Administradores.FirstOrDefault(a => a.Email == loginDTO.Email);
if (adm != null && BCrypt.Net.BCrypt.Verify(loginDTO.Senha, adm.Senha))
return adm;
return null;
}

public List<Administrador> Todos(int? pagina)
{
var query = _contexto.Administradores.AsQueryable();

int itensPorPagina = 10;

if(pagina != null)
query = query.Skip(((int)pagina - 1) * itensPorPagina).Take(itensPorPagina);

return query.ToList();
}

public void Atualizar(Administrador administrador)
{
var existente = _contexto.Administradores.FirstOrDefault(a => a.Id == administrador.Id);
if (existente != null)
{
existente.Email = administrador.Email;
if (!string.IsNullOrEmpty(administrador.Senha))
existente.Senha = BCrypt.Net.BCrypt.HashPassword(administrador.Senha);
existente.Perfil = administrador.Perfil;
_contexto.Administradores.Update(existente);
_contexto.SaveChanges();
}
}

public void Apagar(int id)
{
var adm = _contexto.Administradores.FirstOrDefault(a => a.Id == id);
if (adm != null)
{
_contexto.Administradores.Remove(adm);
_contexto.SaveChanges();
}
}
}
12 changes: 9 additions & 3 deletions Api/Infraestrutura/Db/DbContexto.cs
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,14 @@ namespace MinimalApi.Infraestrutura.Db;

public class DbContexto : DbContext
{
private readonly IConfiguration _configuracaoAppSettings;
public DbContexto(IConfiguration configuracaoAppSettings)

private readonly IConfiguration? _configuracaoAppSettings;

// Construtor para uso em produção e testes (padrão EF Core)
public DbContexto(DbContextOptions<DbContexto> options) : base(options) { }

// Construtor alternativo para uso manual, se necessário
internal DbContexto(IConfiguration configuracaoAppSettings)
{
_configuracaoAppSettings = configuracaoAppSettings;
}
Expand All @@ -20,7 +26,7 @@ protected override void OnModelCreating(ModelBuilder modelBuilder)
new Administrador {
Id = 1,
Email = "administrador@teste.com",
Senha = "123456",
Senha = "$2a$11$Q9QwQn6QwQn6QwQn6QwQnOQn6QwQn6QwQn6QwQn6QwQn6QwQn6QW", // hash fictício para '123456'
Perfil = "Adm"
}
);
Expand Down
28 changes: 28 additions & 0 deletions Api/Middlewares/ErrorHandlingMiddleware.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
using System.Net;
using System.Text.Json;

namespace MinimalApi.Middlewares;

public class ErrorHandlingMiddleware
{
private readonly RequestDelegate _next;
public ErrorHandlingMiddleware(RequestDelegate next)
{
_next = next;
}

public async Task InvokeAsync(HttpContext context)
{
try
{
await _next(context);
}
catch (Exception ex)
{
context.Response.StatusCode = (int)HttpStatusCode.InternalServerError;
context.Response.ContentType = "application/json";
var result = JsonSerializer.Serialize(new { error = ex.Message });
await context.Response.WriteAsync(result);
}
}
}
62 changes: 55 additions & 7 deletions Api/Startup.cs
Original file line number Diff line number Diff line change
Expand Up @@ -93,17 +93,20 @@ public void ConfigureServices(IServiceCollection services)

public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
app.UseSwagger();
app.UseSwaggerUI();
app.UseSwagger();
app.UseSwaggerUI();

app.UseRouting();
// Middleware de tratamento global de erros
app.UseMiddleware<MinimalApi.Middlewares.ErrorHandlingMiddleware>();

app.UseAuthentication();
app.UseAuthorization();
app.UseRouting();

app.UseCors();
app.UseAuthentication();
app.UseAuthorization();

app.UseEndpoints(endpoints => {
app.UseCors();

app.UseEndpoints(endpoints => {

#region Home
endpoints.MapGet("/", () => Results.Json(new Home())).AllowAnonymous().WithTags("Home");
Expand Down Expand Up @@ -211,6 +214,51 @@ string GerarTokenJwt(Administrador administrador){
.RequireAuthorization()
.RequireAuthorization(new AuthorizeAttribute { Roles = "Adm" })
.WithTags("Administradores");

endpoints.MapPut("/administradores/{id}", ([FromRoute] int id, AdministradorDTO administradorDTO, IAdministradorServico administradorServico) => {
var administrador = administradorServico.BuscaPorId(id);
if(administrador == null) return Results.NotFound();

var validacao = new ErrosDeValidacao{
Mensagens = new List<string>()
};
if(string.IsNullOrEmpty(administradorDTO.Email))
validacao.Mensagens.Add("Email não pode ser vazio");
if(administradorDTO.Perfil == null)
validacao.Mensagens.Add("Perfil não pode ser vazio");
// Senha é opcional no update

if(validacao.Mensagens.Count > 0)
return Results.BadRequest(validacao);

administrador.Email = administradorDTO.Email;
if (!string.IsNullOrEmpty(administradorDTO.Senha))
administrador.Senha = administradorDTO.Senha;
administrador.Perfil = administradorDTO.Perfil.ToString() ?? Perfil.Editor.ToString();

administradorServico.Atualizar(administrador);

return Results.Ok(new AdministradorModelView{
Id = administrador.Id,
Email = administrador.Email,
Perfil = administrador.Perfil
});
})
.RequireAuthorization()
.RequireAuthorization(new AuthorizeAttribute { Roles = "Adm" })
.WithTags("Administradores");

endpoints.MapDelete("/administradores/{id}", ([FromRoute] int id, IAdministradorServico administradorServico) => {
var administrador = administradorServico.BuscaPorId(id);
if(administrador == null) return Results.NotFound();

administradorServico.Apagar(id);

return Results.NoContent();
})
.RequireAuthorization()
.RequireAuthorization(new AuthorizeAttribute { Roles = "Adm" })
.WithTags("Administradores");
#endregion

#region Veiculos
Expand Down
1 change: 1 addition & 0 deletions Api/mininal-api.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
</PropertyGroup>

<ItemGroup>
<PackageReference Include="BCrypt.Net-Next" Version="4.0.2" />
<PackageReference Include="Microsoft.AspNetCore.Authentication.JwtBearer" Version="7.0.14" />
<PackageReference Include="Microsoft.EntityFrameworkCore" Version="7.0.14" />
<PackageReference Include="Microsoft.EntityFrameworkCore.Design" Version="7.0.14">
Expand Down
21 changes: 8 additions & 13 deletions Test/Domain/Servicos/AdministradorServico.cs
Original file line number Diff line number Diff line change
Expand Up @@ -12,17 +12,10 @@ public class AdministradorServicoTest
{
private DbContexto CriarContextoDeTeste()
{
var assemblyPath = Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location);
var path = Path.GetFullPath(Path.Combine(assemblyPath ?? "", "..", "..", ".."));

var builder = new ConfigurationBuilder()
.SetBasePath(path ?? Directory.GetCurrentDirectory())
.AddJsonFile("appsettings.json", optional: false, reloadOnChange: true)
.AddEnvironmentVariables();

var configuration = builder.Build();

return new DbContexto(configuration);
var options = new DbContextOptionsBuilder<DbContexto>()
.UseInMemoryDatabase(databaseName: "TestDb_Admin")
.Options;
return new DbContexto(options);
}


Expand All @@ -31,7 +24,8 @@ public void TestandoSalvarAdministrador()
{
// Arrange
var context = CriarContextoDeTeste();
context.Database.ExecuteSqlRaw("TRUNCATE TABLE Administradores");
context.Administradores.RemoveRange(context.Administradores);
context.SaveChanges();

var adm = new Administrador();
adm.Email = "teste@teste.com";
Expand All @@ -52,7 +46,8 @@ public void TestandoBuscaPorId()
{
// Arrange
var context = CriarContextoDeTeste();
context.Database.ExecuteSqlRaw("TRUNCATE TABLE Administradores");
context.Administradores.RemoveRange(context.Administradores);
context.SaveChanges();

var adm = new Administrador();
adm.Email = "teste@teste.com";
Expand Down
32 changes: 32 additions & 0 deletions Test/Domain/Servicos/AdministradorServicoMockTest.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
using Microsoft.VisualStudio.TestTools.UnitTesting;
using MinimalApi.Dominio.Entidades;
using Test.Mocks;

namespace Test.Domain.Servicos;

[TestClass]
public class AdministradorServicoMockTest
{
[TestMethod]
public void Deve_Atualizar_Administrador()
{
var servico = new AdministradorServicoMock();
var adm = servico.Incluir(new Administrador { Email = "novo@teste.com", Senha = "abc", Perfil = "Editor" });
adm.Email = "atualizado@teste.com";
adm.Perfil = "Adm";
servico.Atualizar(adm);
var atualizado = servico.BuscaPorId(adm.Id);
Assert.AreEqual("atualizado@teste.com", atualizado?.Email);
Assert.AreEqual("Adm", atualizado?.Perfil);
}

[TestMethod]
public void Deve_Apagar_Administrador()
{
var servico = new AdministradorServicoMock();
var adm = servico.Incluir(new Administrador { Email = "apagar@teste.com", Senha = "abc", Perfil = "Editor" });
servico.Apagar(adm.Id);
var apagado = servico.BuscaPorId(adm.Id);
Assert.IsNull(apagado);
}
}
20 changes: 17 additions & 3 deletions Test/Helpers/Setup.cs
Original file line number Diff line number Diff line change
Expand Up @@ -20,13 +20,12 @@ public static void ClassInit(TestContext testContext)

Setup.http = Setup.http.WithWebHostBuilder(builder =>
{
builder.UseSetting("https_port", Setup.PORT).UseEnvironment("Testing");

builder.UseSetting("https_port", Setup.PORT).UseEnvironment("Development");
builder.ConfigureServices(services =>
{
services.AddScoped<IAdministradorServico, AdministradorServicoMock>();
services.AddScoped<IVeiculoServico, VeiculoServicoMock>();
});

});

Setup.client = Setup.http.CreateClient();
Expand All @@ -36,4 +35,19 @@ public static void ClassCleanup()
{
Setup.http.Dispose();
}

public static async Task<string> ObterTokenAdmin()
{
var loginDTO = new MinimalApi.DTOs.LoginDTO
{
Email = "adm@teste.com",
Senha = "123456"
};
var content = new StringContent(System.Text.Json.JsonSerializer.Serialize(loginDTO), System.Text.Encoding.UTF8, "application/json");
var response = await client.PostAsync("/administradores/login", content);
response.EnsureSuccessStatusCode();
var result = await response.Content.ReadAsStringAsync();
var admLogado = System.Text.Json.JsonSerializer.Deserialize<MinimalApi.Dominio.ModelViews.AdministradorLogado>(result, new System.Text.Json.JsonSerializerOptions { PropertyNameCaseInsensitive = true });
return admLogado?.Token ?? string.Empty;
}
}
22 changes: 21 additions & 1 deletion Test/Mocks/AdministradorServicoMock.cs
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,6 @@ public Administrador Incluir(Administrador administrador)
{
administrador.Id = administradores.Count() + 1;
administradores.Add(administrador);

return administrador;
}

Expand All @@ -43,4 +42,25 @@ public List<Administrador> Todos(int? pagina)
{
return administradores;
}

public void Atualizar(Administrador administrador)
{
var existente = administradores.Find(a => a.Id == administrador.Id);
if (existente != null)
{
existente.Email = administrador.Email;
if (!string.IsNullOrEmpty(administrador.Senha))
existente.Senha = administrador.Senha;
existente.Perfil = administrador.Perfil;
}
}

public void Apagar(int id)
{
var adm = administradores.Find(a => a.Id == id);
if (adm != null)
{
administradores.Remove(adm);
}
}
}
Loading