Skip to content

Commit

Permalink
FEAT: Changing logic to the use case
Browse files Browse the repository at this point in the history
  • Loading branch information
fmattioli committed Jul 10, 2024
1 parent d60d2e9 commit 973b238
Show file tree
Hide file tree
Showing 12 changed files with 246 additions and 75 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -19,12 +19,10 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "TokenManager.Infra.CrossCut
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "TokenManager.Domain", "TokenManager.Domain\TokenManager.Domain.csproj", "{EC94BA98-EAF7-4118-9619-08D077929FDE}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "TokenManager.UnitTests", "..\..\tests\TokenManager.UnitTests\TokenManager.UnitTests.csproj", "{058EA4DF-2449-404C-B219-FBA6754FD5B9}"
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "TokenManager.UnitTests", "..\..\tests\TokenManager.UnitTests\TokenManager.UnitTests.csproj", "{058EA4DF-2449-404C-B219-FBA6754FD5B9}"
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Tests", "Tests", "{00FBC958-54AF-4A97-893C-15667D843B3E}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "TokenManager.IntegrationTests", "..\..\tests\TokenManager.IntegrationTests\TokenManager.IntegrationTests.csproj", "{B747623C-977F-4377-BF24-6F7A00826306}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
Expand Down Expand Up @@ -55,10 +53,6 @@ Global
{058EA4DF-2449-404C-B219-FBA6754FD5B9}.Debug|Any CPU.Build.0 = Debug|Any CPU
{058EA4DF-2449-404C-B219-FBA6754FD5B9}.Release|Any CPU.ActiveCfg = Release|Any CPU
{058EA4DF-2449-404C-B219-FBA6754FD5B9}.Release|Any CPU.Build.0 = Release|Any CPU
{B747623C-977F-4377-BF24-6F7A00826306}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{B747623C-977F-4377-BF24-6F7A00826306}.Debug|Any CPU.Build.0 = Debug|Any CPU
{B747623C-977F-4377-BF24-6F7A00826306}.Release|Any CPU.ActiveCfg = Release|Any CPU
{B747623C-977F-4377-BF24-6F7A00826306}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
Expand All @@ -70,7 +64,6 @@ Global
{3C751694-F055-43CD-B374-62C352D385B3} = {59BDDB9A-A2B6-4366-9522-803AC05E850B}
{EC94BA98-EAF7-4118-9619-08D077929FDE} = {DCC0F26F-67F8-4431-8564-5B9F6E1D3F47}
{058EA4DF-2449-404C-B219-FBA6754FD5B9} = {00FBC958-54AF-4A97-893C-15667D843B3E}
{B747623C-977F-4377-BF24-6F7A00826306} = {00FBC958-54AF-4A97-893C-15667D843B3E}
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {C84D43BB-2657-4F8D-AC09-2736B1514A82}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
using Contracts.Common;
using MediatR;
using TokenManager.Application.Services.Mappers;
using TokenManager.Domain.Entities;
using TokenManager.Domain.Errors;
using TokenManager.Domain.Interfaces;

namespace TokenManager.Application.Services.Commands.Users
Expand All @@ -11,13 +13,28 @@ public class CreateUserCommandHandler(IUserRepository userRepository) : IRequest

public async Task<Result> Handle(CreateUserCommand request, CancellationToken cancellationToken)
{
var resultUserCreated = await _userRepository.CreateNewUserActions(request.Tenant, request.AddUserRequest.ToDomain());
if (resultUserCreated.IsSuccess)
var accessTokenResult = await _userRepository.GetAccessTokenAsync(request.Tenant);
if (accessTokenResult.IsSuccess)
{
return Result.Success();
var user = request.AddUserRequest.ToDomain();
var(IsSuccessStatusCode, contentRequest) = await _userRepository.CreateNewUserAsync(user);
if (IsSuccessStatusCode)
{
await SetUserPasswordAsync(user);
return Result.Success();
}

UserErrors.SetTechnicalMessage(contentRequest);
return Result.Failure(UserErrors.TokenGenerationError);
}

return Result.Failure(resultUserCreated.Error);
return Result.Failure(UserErrors.InvalidUserNameOrPasswordError);
}

private async Task SetUserPasswordAsync(User user)
{
var keycloakUser = await _userRepository.GetUserAsync(user.Username!);
await _userRepository.ResetPasswordAsync(keycloakUser.Value.Id!, user.Password!);
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -14,10 +14,10 @@ public static User ToDomain(this AddUserRequest userRequest)

public static Attributes ToDomain(this AttributesRequest attributes)
{
return new Attributes(attributes.ZoneInfo, attributes.Birthdate, attributes.PhoneNumber, attributes.Gender, attributes.Fullname, attributes.Tenant, attributes.Picture);
return new Attributes(attributes.Tenant, attributes.UserAttributes);
}

public static User ToDomain(this LoginUserRequest loginUserRequest, string tenant)
public static User ToDomain(this LoginUserRequest loginUserRequest)
{
return new User(loginUserRequest.Username, loginUserRequest.Password);
}
Expand All @@ -29,7 +29,8 @@ public static TokenResponse ToTokenResponse(this TokenDetails tokenDetails)
AccessToken = tokenDetails.Access_Token,
ExpiresIn = tokenDetails.Expires_In,
RefreshToken = tokenDetails.Refresh_Token,
RefreshExpiresIn = tokenDetails.Refresh_Expires_In
RefreshExpiresIn = tokenDetails.Refresh_Expires_In,
TokenType = tokenDetails.Token_Type,
};
}
}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
namespace TokenManager.Application.Services.Requests.User
{
public record AttributesRequest(string? Tenant, string? ZoneInfo, string? Birthdate, string? PhoneNumber, string? Gender, string? Fullname, string? Picture);
public record AttributesRequest(string Tenant, Dictionary<string, string> UserAttributes);
}
Original file line number Diff line number Diff line change
@@ -1,13 +1,8 @@
namespace TokenManager.Domain.Entities
{
public class Attributes(string? Zoneinfo, string? Birthdate, string? PhoneNumber, string? Gender, string? Fullname, string? Tenant, string? Picture)
public class Attributes(string Tenant, Dictionary<string, string> UserAttributes)
{
public string? Zoneinfo { get; set; } = Zoneinfo;
public string? Birthdate { get; set; } = Birthdate;
public string? PhoneNumber { get; set; } = PhoneNumber;
public string? Gender { get; set; } = Gender;
public string? Fullname { get; set; } = Fullname;
public Dictionary<string, string> UserAttributes = UserAttributes;
public string? Tenant { get; set; } = Tenant;
public string? Picture { get; set; } = Picture;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -6,9 +6,8 @@ namespace TokenManager.Domain.Interfaces
public interface IUserRepository
{
Task<Result<TokenDetails>> GetAccessTokenAsync(string tenant);
Task<Result> CreateNewUserActions(string tenant, User user);
Task<Result<TokenDetails>> LoginAsync(string tenant, User user);
Task<HttpResponseMessage> CreateNewUserAsync(User user);
Task<(bool result, string content)> CreateNewUserAsync(User user);
Task<Result<User>> GetUserAsync(string userName);
Task<Result> ResetPasswordAsync(string userId, string password);
Task<Result> SendEmailVerificationAsync(string userId);
Expand Down
Original file line number Diff line number Diff line change
@@ -1,9 +1,6 @@
using Feijuca.Keycloak.MultiTenancy.Services;
using Feijuca.Keycloak.MultiTenancy.Services.Models;
using Microsoft.Extensions.DependencyInjection;

using System;

using TokenManager.Domain.Interfaces;
using TokenManager.Infra.Data.Models;
using TokenManager.Infra.Data.Repositories;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,6 @@ namespace TokenManager.Infra.Data.Repositories
public class UserRepository : IUserRepository
{
private readonly IHttpClientFactory _httpClientFactory;
private readonly IAuthService _authService;
private readonly TokenCredentials _tokenCredentials;
private readonly HttpClient _httpClient;
private string _urlUserActions = "";
Expand All @@ -24,10 +23,9 @@ public class UserRepository : IUserRepository
NullValueHandling = Newtonsoft.Json.NullValueHandling.Ignore
};

public UserRepository(IHttpClientFactory httpClientFactory, IAuthService authService, TokenCredentials tokenCredentials)
public UserRepository(IHttpClientFactory httpClientFactory, TokenCredentials tokenCredentials)
{
_httpClientFactory = httpClientFactory;
_authService = authService;
_tokenCredentials = tokenCredentials;
_httpClient = _httpClientFactory.CreateClient("KeycloakClient");
}
Expand All @@ -51,47 +49,28 @@ public async Task<Result<TokenDetails>> GetAccessTokenAsync(string tenant)
var response = await _httpClient.PostAsync(url, requestData);

if (response.IsSuccessStatusCode)
{
{
var content = await response.Content.ReadAsStringAsync();
var result = JsonConvert.DeserializeObject<TokenDetails>(content);
var result = JsonConvert.DeserializeObject<TokenDetails>(content)!;

SetAuthorizationHeader(result.Access_Token);

return Result<TokenDetails>.Success(result!);
}

var responseMessage = await response.Content.ReadAsStringAsync();
UserErrors.SetTechnicalMessage(responseMessage);
return Result<TokenDetails>.Failure(UserErrors.TokenGenerationError);
}

public async Task<Result> CreateNewUserActions(string tenant, User user)
public async Task<(bool result, string content)> CreateNewUserAsync(User user)
{
var tokenBearerResult = await GetAccessTokenAsync(tenant);
if (tokenBearerResult.IsSuccess)
{
ConfigureHttpClient(tenant, tokenBearerResult.Value.Access_Token);

var response = await CreateNewUserAsync(user);

if (response.IsSuccessStatusCode)
{
var keycloakUser = await GetUserAsync(user.Username!);
await ResetPasswordAsync(keycloakUser.Value.Id!, user.Password!);
return Result.Success();
}

var responseMessage = await response.Content.ReadAsStringAsync();
UserErrors.SetTechnicalMessage(responseMessage);
return Result.Failure(UserErrors.TokenGenerationError);
}
SetBaseUrlUserAction(user!.Attributes!.Tenant!);

return Result.Failure(UserErrors.InvalidUserNameOrPasswordError);
}

public async Task<HttpResponseMessage> CreateNewUserAsync(User user)
{
var json = JsonConvert.SerializeObject(user, Settings);
var httpContent = new StringContent(json, Encoding.UTF8, "application/json");
var response = await _httpClient.PostAsync(_urlUserActions, httpContent);
return response;
return (response.IsSuccessStatusCode, await response.Content.ReadAsStringAsync());
}

public async Task<Result<User>> GetUserAsync(string userName)
Expand Down Expand Up @@ -189,15 +168,18 @@ public async Task<Result<TokenDetails>> LoginAsync(string tenant, User user)
return Result<TokenDetails>.Failure(UserErrors.InvalidUserNameOrPasswordError);
}

private void ConfigureHttpClient(string tenant, string accessToken)
private void SetBaseUrlUserAction(string tenant)
{
_httpClient.DefaultRequestHeaders.Authorization = new System.Net.Http.Headers.AuthenticationHeaderValue("Bearer", accessToken);

_urlUserActions = _httpClient.BaseAddress
.AppendPathSegment("admin")
.AppendPathSegment("realms")
.AppendPathSegment(tenant)
.AppendPathSegment("users");
}

private void SetAuthorizationHeader(string accessToken)
{
_httpClient.DefaultRequestHeaders.Authorization = new System.Net.Http.Headers.AuthenticationHeaderValue("Bearer", accessToken);
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,95 @@
using AutoFixture;
using Contracts.Common;
using FluentAssertions;
using Moq;
using TokenManager.Application.Services.Commands.Users;
using TokenManager.Domain.Entities;
using TokenManager.Domain.Errors;
using TokenManager.Domain.Interfaces;

namespace TokenManager.UnitTests.Handlers
{
public class CreateUserCommandHandlerTests
{
private readonly Fixture _autoFixture = new();
private readonly Mock<IUserRepository> _userRepositoryMock = new();
private readonly CreateUserCommandHandler _createUserCommandHandler;

public CreateUserCommandHandlerTests()
{
_createUserCommandHandler = new(_userRepositoryMock.Object);
}

[Fact]
public async Task HandleWhenInformAValidUser_ShouldCreateANewUserAndReturnsTrue()
{
// Arrange
var createUserCommand = _autoFixture.Create<CreateUserCommand>();
var tokenDetails = _autoFixture.Create<TokenDetails>();
var successResult = Result<TokenDetails>.Success(tokenDetails);

_userRepositoryMock
.Setup(x => x.GetAccessTokenAsync(It.IsAny<string>()))
.ReturnsAsync(successResult);

(bool result, string content) createUserResult = (true, "Success message");

_userRepositoryMock
.Setup(x => x.CreateNewUserAsync(It.IsAny<User>()))
.ReturnsAsync(createUserResult);

var user = _autoFixture.Create<User>();
var userResult = Result<User>.Success(user);

_userRepositoryMock
.Setup(x => x.GetUserAsync(It.IsAny<string>()))
.ReturnsAsync(userResult);

_userRepositoryMock
.Setup(x => x.ResetPasswordAsync(It.IsAny<string>(), It.IsAny<string>()))
.ReturnsAsync(userResult);

//Act
var resultHandle = await _createUserCommandHandler.Handle(createUserCommand, CancellationToken.None);

//Assert
_userRepositoryMock
.Verify(x => x.GetAccessTokenAsync(It.IsAny<string>()), Times.Once);

resultHandle.IsSuccess
.Should()
.Be(true);
}

[Fact]
public async Task HandleWhenInformAInvalidUser_ShouldNotCreateANewUserAndReturnsFalse()
{
// Arrange
var createUserCommand = _autoFixture.Create<CreateUserCommand>();

var errorMessage = _autoFixture.Create<string>();
UserErrors.SetTechnicalMessage(errorMessage);
var result = Result.Failure(UserErrors.TokenGenerationError);

_userRepositoryMock
.Setup(x => x.GetAccessTokenAsync(It.IsAny<string>()), Times.Once)
.ReturnsAsync(result);

//Act
var resultHandle = await _createUserCommandHandler.Handle(createUserCommand, CancellationToken.None);

//Assert
resultHandle.IsSuccess
.Should()
.Be(false);

resultHandle.Error.Description
.Should()
.Contain(result.Error.Description);

resultHandle.Error.Code
.Should()
.Contain(result.Error.Code);
}
}
}
Loading

0 comments on commit 973b238

Please sign in to comment.