Skip to content

Commit

Permalink
CB-29 Add UsersController with custom endpoints
Browse files Browse the repository at this point in the history
  • Loading branch information
izzat5233 committed Jan 4, 2025
1 parent cc7e017 commit 601f221
Show file tree
Hide file tree
Showing 6 changed files with 223 additions and 1 deletion.
14 changes: 14 additions & 0 deletions ChatbotBuilderApi.Application/Users/UsersApplicationErrors.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
using ChatbotBuilderApi.Domain.Core.Primitives;

namespace ChatbotBuilderApi.Application.Users;

public static class UsersApplicationErrors
{
public static readonly Error UserNotFound = Error.NotFound(
"Users.UserNotFound",
"User not found.");

public static readonly Error OldPasswordRequired = Error.BadRequest(
"Users.OldPasswordRequired",
"Old password is required.");
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
namespace ChatbotBuilderApi.Presentation.Users.Requests;

public sealed class RegisterUserRequest
{
public required string UserName { get; init; }
public required string Email { get; init; }
public required string Password { get; init; }
public string? FirstName { get; init; }
public string? LastName { get; init; }
}
10 changes: 10 additions & 0 deletions ChatbotBuilderApi.Presentation/Users/Requests/UpdateUserRequest.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
namespace ChatbotBuilderApi.Presentation.Users.Requests;

public sealed class UpdateUserRequest
{
public string? FirstName { get; init; }
public string? LastName { get; init; }
public string? NewEmail { get; init; }
public string? NewPassword { get; init; }
public string? OldPassword { get; init; }
}
180 changes: 180 additions & 0 deletions ChatbotBuilderApi.Presentation/Users/UsersController.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,180 @@
using Asp.Versioning;
using ChatbotBuilderApi.Application.Core.Shared;
using ChatbotBuilderApi.Application.Users;
using ChatbotBuilderApi.Domain.Users;
using ChatbotBuilderApi.Presentation.Core.Abstract;
using ChatbotBuilderApi.Presentation.Core.Attributes;
using ChatbotBuilderApi.Presentation.Core.Extensions;
using ChatbotBuilderApi.Presentation.Core.Responses;
using ChatbotBuilderApi.Presentation.Users.Requests;
using ChatbotBuilderApi.Presentation.Users.ViewModels;
using MediatR;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Identity;
using Microsoft.AspNetCore.Mvc;

namespace ChatbotBuilderApi.Presentation.Users;

[ApiController]
[ApiVersion("1")]
[Route("api/v{version:apiVersion}/users")]
[Authorize]
public sealed class UsersController : AbstractController
{
private readonly UserManager<User> _userManager;

public UsersController(ISender sender, UserManager<User> userManager) : base(sender)
{
_userManager = userManager;
}

/// <summary>
/// Gets a user by ID.
/// </summary>
/// <param name="id"></param>
/// <returns></returns>
[HttpGet("{id:guid}")]
[AllowAnonymous]
[ProducesResponseType(typeof(UserViewModel), StatusCodes.Status200OK)]
[ProducesError(StatusCodes.Status404NotFound)]
public async Task<ActionResult<UserViewModel>> GetUserById(
[FromRoute] Guid id)
{
var user = await _userManager.FindByIdAsync(id.ToString());
if (user == null)
{
return Result
.Failure(UsersApplicationErrors.UserNotFound)
.ToProblemDetails();
}

return Ok(new UserViewModel(
user.Id,
user.UserName!,
user.Email!,
user.FirstName,
user.LastName
));
}

/// <summary>
/// Registers a new user.
/// </summary>
/// <param name="userRequest"></param>
/// <returns></returns>
[HttpPost]
[AllowAnonymous]
[ProducesResponseType(typeof(CreateResponse), StatusCodes.Status201Created)]
[ProducesError(StatusCodes.Status400BadRequest)]
public async Task<ActionResult<CreateResponse>> RegisterUser(
[FromBody] RegisterUserRequest userRequest)
{
var user = new User
{
UserName = userRequest.UserName,
Email = userRequest.Email,
FirstName = userRequest.FirstName,
LastName = userRequest.LastName
};

var result = await _userManager.CreateAsync(user, userRequest.Password);
return result.Succeeded
? CreatedAtAction(
nameof(GetUserById),
new { id = user.Id },
new CreateResponse(user.Id))
: result.ToProblemDetails();
}

/// <summary>
/// Gets the currently logged-in user.
/// </summary>
/// <returns></returns>
[HttpGet]
[ProducesResponseType(typeof(UserViewModel), StatusCodes.Status200OK)]
[ProducesError(StatusCodes.Status404NotFound)]
public async Task<ActionResult<UserViewModel>> GetCurrentUser()
{
var user = await _userManager.GetUserAsync(User);
if (user == null)
{
return Result
.Failure(UsersApplicationErrors.UserNotFound)
.ToProblemDetails();
}

return Ok(new UserViewModel(
user.Id,
user.UserName!,
user.Email!,
user.FirstName,
user.LastName
));
}

/// <summary>
/// Updates the currently logged-in user.
/// </summary>
/// <param name="request"></param>
/// <returns></returns>
[HttpPut]
[ProducesResponseType(StatusCodes.Status204NoContent)]
[ProducesError(StatusCodes.Status400BadRequest)]
[ProducesError(StatusCodes.Status404NotFound)]
public async Task<IActionResult> UpdateCurrentUser(
[FromBody] UpdateUserRequest request)
{
var user = await _userManager.GetUserAsync(User);
if (user == null)
{
return Result
.Failure(UsersApplicationErrors.UserNotFound)
.ToProblemDetails();
}

if (!string.IsNullOrEmpty(request.FirstName))
{
user.FirstName = request.FirstName;
}

if (!string.IsNullOrEmpty(request.LastName))
{
user.LastName = request.LastName;
}

if (!string.IsNullOrEmpty(request.NewEmail))
{
var setEmailResult = await _userManager.SetEmailAsync(user, request.NewEmail);
if (!setEmailResult.Succeeded)
{
return setEmailResult.ToProblemDetails();
}
}

if (!string.IsNullOrEmpty(request.NewPassword))
{
if (string.IsNullOrEmpty(request.OldPassword))
{
return Result
.Failure(UsersApplicationErrors.OldPasswordRequired)
.ToProblemDetails();
}

var changePasswordResult = await _userManager.ChangePasswordAsync(
user,
request.OldPassword,
request.NewPassword);

if (!changePasswordResult.Succeeded)
{
return changePasswordResult.ToProblemDetails();
}
}

var updateResult = await _userManager.UpdateAsync(user);
return updateResult.Succeeded
? Ok(new { Message = "User information updated successfully." })
: updateResult.ToProblemDetails();
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
namespace ChatbotBuilderApi.Presentation.Users.ViewModels;

public sealed record UserViewModel(
Guid Id,
string UserName,
string Email,
string? FirstName,
string? LastName);
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ public static void AddPersistenceServices(this IServiceCollection services, ICon
throw new ArgumentException("AppDbContextConnection not found"));
});

services.AddIdentityCore<User>()
services.AddIdentityCore<User>(options => options.User.RequireUniqueEmail = true)
.AddRoles<Role>()
.AddEntityFrameworkStores<AppDbContext>()
.AddApiEndpoints()
Expand Down

0 comments on commit 601f221

Please sign in to comment.