Skip to content

Commit a40f85b

Browse files
authored
Merge pull request #4 from jioo/v2.1.1
v2.1.1
2 parents a123935 + 1f891f3 commit a40f85b

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

60 files changed

+1295
-159
lines changed

readme.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,9 @@ cd .\tests\Api\
4848

4949
# Run tests
5050
dotnet test
51+
52+
# or Run with file watcher
53+
dotnet watch test
5154
```
5255

5356
### Local Publish ###
@@ -62,6 +65,7 @@ dotnet-cake build.cake --task="Publish"
6265
* Swagger for api documentation (URL: http://localhost:5000/swagger/index.html)
6366
* CQRS Pattern (Command Query Responsibility Segregation)
6467
* Authentication based on Identity Framework & JWT Bearer
68+
* Integration Tests with XUnit
6569
* Material design
6670
* Realtime update on employee logs
6771
* ![Realtime Demo](realtime-demo.gif)

src/Api/Constants/AttendanceConfig.cs

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,6 @@ public class AttendanceConfig
44
{
55
public const string TimeIn = "09:00";
66
public const string TimeOut = "18:00";
7-
public const string GracePeriod = "15";
8-
7+
public const int GracePeriod = 15;
98
}
109
}

src/Api/Entities/Config.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,6 @@ public class Config
77
public Guid Id { get; set; }
88
public string TimeIn { get; set; }
99
public string TimeOut { get; set; }
10-
public string GracePeriod { get; set; }
10+
public int GracePeriod { get; set; }
1111
}
1212
}

src/Api/Extensions/ListExtensions.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ namespace WebApi.Extensions
77
public static class ListExtensions
88
{
99
/// <summary>
10-
///
10+
/// Apply sort filters based on parameters
1111
/// </summary>
1212
/// <param name="queryable"></param>
1313
/// <param name="parameters"></param>

src/Api/Features/Accounts/AccountsController.cs

Lines changed: 41 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,10 @@
44
using Microsoft.AspNetCore.Authorization;
55
using WebApi.Entities;
66
using MediatR;
7+
using System.Security.Claims;
8+
using Microsoft.AspNetCore.Http;
9+
using WebApi.Features.Employees;
10+
using WebApi.Utils;
711

812
namespace WebApi.Features.Accounts
913
{
@@ -12,24 +16,37 @@ namespace WebApi.Features.Accounts
1216
public class AccountsController : ControllerBase
1317
{
1418
private readonly IMediator _mediator;
19+
private readonly IHttpContextAccessor _httpContext;
1520

16-
public AccountsController(IMediator mediator)
21+
public AccountsController(IMediator mediator, IHttpContextAccessor httpContext)
1722
{
1823
_mediator = mediator;
24+
_httpContext = httpContext;
1925
}
2026

2127
// POST: api/accounts/register
28+
/// <summary>
29+
/// Register new employee
30+
/// </summary>
31+
/// <remarks>
32+
/// Unique card no. and username filter will be applied
33+
/// </remarks>
34+
/// <param name="viewModel"></param>
2235
[Authorize(Roles = "Admin")]
2336
[HttpPost("register")]
37+
[ProducesResponseType(typeof(EmployeeViewModel), StatusCodes.Status201Created)]
38+
[ProducesResponseType(typeof(ErrorHandler), StatusCodes.Status400BadRequest)]
2439
public async Task<IActionResult> Register(RegisterViewModel viewModel)
2540
{
2641
// mediator from Features/Employees
2742
var isCardExist = await _mediator.Send(new Employees.IsCardExists.Query(Guid.Empty, viewModel.CardNo));
28-
if (isCardExist) return BadRequest("Card No. is already in use");
43+
if (isCardExist)
44+
return BadRequest(new ErrorHandler{ Description = "Card No. is already in use" });
2945

3046
// mediator from Features/Employees
3147
var isUsernameExist = await _mediator.Send(new Auth.IsUserExists.Query(viewModel.UserName));
32-
if(isUsernameExist) return BadRequest($"Username {viewModel.UserName} is already taken");
48+
if (isUsernameExist)
49+
return BadRequest(new ErrorHandler{ Description = $"Username {viewModel.UserName} is already taken" });
3350

3451
// Create user account
3552
var employeeInfo = await _mediator.Send(new Register.Command(viewModel));
@@ -38,28 +55,44 @@ public async Task<IActionResult> Register(RegisterViewModel viewModel)
3855
}
3956

4057
// PUT: api/accounts/update-password
58+
/// <summary>
59+
/// Update an Employee password
60+
/// </summary>
61+
/// <param name="viewModel"></param>
4162
[Authorize(Roles = "Admin")]
4263
[HttpPut("update-password")]
43-
public async Task<IActionResult> UpdatePassword(ChangePasswordViewModel viewModel)
64+
[ProducesResponseType(StatusCodes.Status200OK)]
65+
[ProducesResponseType(typeof(ErrorHandler), StatusCodes.Status400BadRequest)]
66+
public async Task<IActionResult> UpdatePassword(UpdatePasswordViewModel viewModel)
4467
{
4568
// Change a specific Employee account's password
4669
var result = await _mediator.Send(new UpdatePassword.Command(viewModel));
47-
if(!result) return StatusCode(500);
70+
if (!result)
71+
return BadRequest(new ErrorHandler{ Description = "Unable to update password." });
4872

4973
return Ok();
5074
}
5175

5276
// PUT: api/accounts/change-password
77+
/// <summary>
78+
/// Update your current password
79+
/// </summary>
80+
/// <param name="viewModel"></param>
5381
[HttpPut("change-password")]
82+
[ProducesResponseType(StatusCodes.Status200OK)]
83+
[ProducesResponseType(typeof(ErrorHandler), StatusCodes.Status400BadRequest)]
5484
public async Task<IActionResult> ChangePassword(ChangePasswordViewModel viewModel)
5585
{
5686
// Check if Old password is correct
57-
var validatePassword = await _mediator.Send(new Auth.ValidatePassword.Query(viewModel.UserName, viewModel.OldPassword));
58-
if (!validatePassword) return BadRequest("Incorrect password");
87+
var currentUser = _httpContext.HttpContext.User.FindFirstValue(ClaimTypes.NameIdentifier);
88+
var validatePassword = await _mediator.Send(new Auth.ValidatePassword.Query(currentUser, viewModel.OldPassword));
89+
if (!validatePassword)
90+
return BadRequest(new ErrorHandler{ Description = "Incorrect password." });
5991

6092
// Change account password
6193
var result = await _mediator.Send(new ChangePassword.Command(viewModel));
62-
if(!result.Succeeded) return BadRequest("Unable to change password");
94+
if (!result.Succeeded)
95+
return BadRequest(new ErrorHandler{ Description = "Unable to change password." });
6396

6497
return Ok();
6598
}

src/Api/Features/Accounts/ChangePassword.cs

Lines changed: 4 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
using System;
22
using System.Linq;
3+
using System.Security.Claims;
34
using System.Threading;
45
using System.Threading.Tasks;
56
using MediatR;
@@ -9,9 +10,6 @@
910

1011
namespace WebApi.Features.Accounts
1112
{
12-
/// <summary>
13-
/// Change account password
14-
/// </summary>
1513
public class ChangePassword
1614
{
1715
public class Command : IRequest<IdentityResult>
@@ -40,9 +38,7 @@ public async Task<IdentityResult> Handle(Command request, CancellationToken canc
4038
try
4139
{
4240
// Get the username in sub type claim
43-
var username = _httpContext.HttpContext.User.Claims
44-
.First(m => m.Type == "http://schemas.xmlsoap.org/ws/2005/05/identity/claims/nameidentifier")
45-
.Value;
41+
var username = _httpContext.HttpContext.User.FindFirstValue(ClaimTypes.NameIdentifier);
4642

4743
// Get account details
4844
var user = await _manager.FindByNameAsync(username);
@@ -51,7 +47,8 @@ public async Task<IdentityResult> Handle(Command request, CancellationToken canc
5147
return await _manager.ChangePasswordAsync(
5248
user,
5349
request.ViewModel.OldPassword,
54-
request.ViewModel.NewPassword);
50+
request.ViewModel.NewPassword
51+
);
5552
}
5653
catch (Exception e)
5754
{
Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,13 @@
1+
using System.ComponentModel.DataAnnotations;
2+
13
namespace WebApi.Features.Accounts
24
{
35
public class ChangePasswordViewModel
46
{
5-
public string UserName { get; set; }
7+
[Required]
68
public string OldPassword { get; set; }
9+
[Required]
10+
[StringLength(100, ErrorMessage = "The {0} must be at least {2} characters long.", MinimumLength = 6)]
711
public string NewPassword { get; set; }
812
}
913
}

src/Api/Features/Accounts/Register.cs

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -9,9 +9,6 @@
99

1010
namespace WebApi.Features.Accounts
1111
{
12-
/// <summary>
13-
/// Create employee account
14-
/// </summary>
1512
public class Register
1613
{
1714
public class Command : IRequest<EmployeeViewModel>

src/Api/Features/Accounts/UpdatePassword.cs

Lines changed: 9 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11

22
using System;
3+
using System.Linq;
34
using System.Threading;
45
using System.Threading.Tasks;
56
using MediatR;
@@ -8,19 +9,16 @@
89

910
namespace WebApi.Features.Accounts
1011
{
11-
/// <summary>
12-
/// Change a specific Employee account's password
13-
/// </summary>
1412
public class UpdatePassword
1513
{
1614
public class Command : IRequest<bool>
1715
{
18-
public Command(ChangePasswordViewModel viewModel)
16+
public Command(UpdatePasswordViewModel viewModel)
1917
{
2018
ViewModel = viewModel;
2119
}
2220

23-
public ChangePasswordViewModel ViewModel { get; }
21+
public UpdatePasswordViewModel ViewModel { get; }
2422
}
2523

2624
public class CommandHandler : IRequestHandler<Command, bool>
@@ -36,14 +34,19 @@ public async Task<bool> Handle(Command request, CancellationToken cancellationTo
3634
{
3735
try
3836
{
37+
// Validate User
38+
var validateUser = _manager.Users.FirstOrDefault(m => m.UserName == request.ViewModel.UserName);
39+
if(validateUser == null)
40+
return false;
41+
3942
// Get account details
4043
var user = await _manager.FindByNameAsync(request.ViewModel.UserName);
4144

4245
// Remove the existing password
4346
await _manager.RemovePasswordAsync(user);
4447

4548
// Add the new password
46-
var result = await _manager.AddPasswordAsync(user, request.ViewModel.NewPassword);
49+
var result = await _manager.AddPasswordAsync(user, request.ViewModel.Password);
4750

4851
return (result.Succeeded) ? true : false;
4952
}
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
using System.ComponentModel.DataAnnotations;
2+
3+
namespace WebApi.Features.Accounts
4+
{
5+
public class UpdatePasswordViewModel
6+
{
7+
[Required]
8+
public string UserName { get; set; }
9+
[Required]
10+
[StringLength(100, ErrorMessage = "The {0} must be at least {2} characters long.", MinimumLength = 6)]
11+
public string Password { get; set; }
12+
}
13+
}

0 commit comments

Comments
 (0)