Skip to content

Commit

Permalink
Add admin function for editing DOB
Browse files Browse the repository at this point in the history
  • Loading branch information
gunndabad committed Feb 5, 2024
1 parent b29ef63 commit 0cc3d1e
Show file tree
Hide file tree
Showing 4 changed files with 276 additions and 0 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
@page "/admin/users/{userId}/date-of-birth"
@model TeacherIdentity.AuthServer.Pages.Admin.EditUserDateOfBirthModel
@{
ViewBag.Title = Html.DisplayNameFor(m => m.DateOfBirth);
}

@section BeforeContent {
<govuk-back-link asp-page="User" asp-route-userId="@Model.UserId" />
}

<div class="govuk-grid-row">
<div class="govuk-grid-column-two-thirds">
<form asp-page="EditUserDateOfBirth" asp-route-userId="@Model.UserId">
<govuk-date-input asp-for="DateOfBirth">
<govuk-date-input-fieldset>
<govuk-date-input-fieldset-legend is-page-heading="true" class="govuk-fieldset__legend--l" />
</govuk-date-input-fieldset>
</govuk-date-input>

<govuk-button type="submit">Continue</govuk-button>
</form>
</div>
</div>
Original file line number Diff line number Diff line change
@@ -0,0 +1,81 @@
using System.ComponentModel.DataAnnotations;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.Filters;
using Microsoft.AspNetCore.Mvc.RazorPages;
using Microsoft.EntityFrameworkCore;
using TeacherIdentity.AuthServer.Events;
using TeacherIdentity.AuthServer.Models;

namespace TeacherIdentity.AuthServer.Pages.Admin;

[Authorize(AuthorizationPolicies.GetAnIdentitySupport)]
public class EditUserDateOfBirthModel : PageModel
{
private readonly TeacherIdentityServerDbContext _dbContext;
private readonly IClock _clock;

public EditUserDateOfBirthModel(TeacherIdentityServerDbContext dbContext, IClock clock)
{
_dbContext = dbContext;
_clock = clock;
}

[FromRoute]
public Guid UserId { get; set; }

[BindProperty]
[Display(Name = "Change date of birth")]
[Required(ErrorMessage = "Enter a date of birth")]
public DateOnly? DateOfBirth { get; set; }

public void OnGet()
{
}

public async Task<IActionResult> OnPost()
{
if (!ModelState.IsValid)
{
return this.PageWithErrors();
}

var user = await _dbContext.Users.Where(u => u.UserType == UserType.Default && u.UserId == UserId).SingleAsync();

var changes = user.DateOfBirth != DateOfBirth ? UserUpdatedEventChanges.DateOfBirth : UserUpdatedEventChanges.None;

user.DateOfBirth = DateOfBirth!.Value;

if (changes != UserUpdatedEventChanges.None)
{
_dbContext.AddEvent(new UserUpdatedEvent()
{
Source = UserUpdatedEventSource.SupportUi,
UpdatedByClientId = null,
UpdatedByUserId = User.GetUserId(),
CreatedUtc = _clock.UtcNow,
User = Events.User.FromModel(user),
Changes = changes
});

await _dbContext.SaveChangesAsync();

TempData.SetFlashSuccess("Date of birth changed successfully");
}

return RedirectToPage("User", new { UserId });
}

public override async Task OnPageHandlerExecutionAsync(PageHandlerExecutingContext context, PageHandlerExecutionDelegate next)
{
var user = await _dbContext.Users.Where(u => u.UserType == UserType.Default && u.UserId == UserId).SingleOrDefaultAsync();

if (user is null)
{
context.Result = NotFound();
return;
}

await next();
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -80,6 +80,9 @@
<govuk-summary-list-row>
<govuk-summary-list-row-key>Date of birth</govuk-summary-list-row-key>
<govuk-summary-list-row-value>@(Model.DateOfBirth.HasValue ? Model.DateOfBirth.Value.ToString("d MMMM yyyy") : "")</govuk-summary-list-row-value>
<govuk-summary-list-row-actions>
<govuk-summary-list-row-action asp-page="EditUserDateOfBirth" asp-route-userId="@Model.UserId" visually-hidden-text="date of birth">Change</govuk-summary-list-row-action>
</govuk-summary-list-row-actions>
</govuk-summary-list-row>
<govuk-summary-list-row>
<govuk-summary-list-row-key>DQT record</govuk-summary-list-row-key>
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,169 @@
using Microsoft.EntityFrameworkCore;
using TeacherIdentity.AuthServer.Events;

namespace TeacherIdentity.AuthServer.Tests.EndpointTests.Admin;

public class EditUserDateOfBirthTests : TestBase
{
public EditUserDateOfBirthTests(HostFixture hostFixture)
: base(hostFixture)
{
}

[Fact]
public async Task Get_UnauthenticatedUser_RedirectsToSignIn()
{
var user = await TestData.CreateUser(userType: Models.UserType.Default);

await UnauthenticatedUser_RedirectsToSignIn(HttpMethod.Get, $"/admin/users/{user.UserId}/date-of-birth");
}

[Fact]
public async Task Get_AuthenticatedUserDoesNotHavePermission_ReturnsForbidden()
{
var user = await TestData.CreateUser(userType: Models.UserType.Default);

await AuthenticatedUserDoesNotHavePermission_ReturnsForbidden(HttpMethod.Get, $"/admin/users/{user.UserId}/date-of-birth");
}

[Fact]
public async Task Get_UserDoesNotExist_ReturnsNotFound()
{
// Arrange
var userId = Guid.NewGuid();
var request = new HttpRequestMessage(HttpMethod.Get, $"/admin/users/{userId}/date-of-birth");

// Act
var response = await HttpClient.SendAsync(request);

// Assert
Assert.Equal(StatusCodes.Status404NotFound, (int)response.StatusCode);
}

[Fact]
public async Task Get_UserIsNotDefaultType_ReturnsNotFound()
{
// Arrange
var user = await TestData.CreateUser(userType: Models.UserType.Staff);
var request = new HttpRequestMessage(HttpMethod.Get, $"/admin/users/{user.UserId}/date-of-birth");

// Act
var response = await HttpClient.SendAsync(request);

// Assert
Assert.Equal(StatusCodes.Status404NotFound, (int)response.StatusCode);
}

[Fact]
public async Task Get_ValidRequest_RendersExpectedContent()
{
// Arrange
var user = await TestData.CreateUser(userType: Models.UserType.Default);
var request = new HttpRequestMessage(HttpMethod.Get, $"/admin/users/{user.UserId}/date-of-birth");

// Act
var response = await HttpClient.SendAsync(request);

// Assert
Assert.Equal(StatusCodes.Status200OK, (int)response.StatusCode);
}

[Fact]
public async Task Post_UnauthenticatedUser_RedirectsToSignIn()
{
var user = await TestData.CreateUser(userType: Models.UserType.Default);

await UnauthenticatedUser_RedirectsToSignIn(HttpMethod.Post, $"/admin/users/{user.UserId}/date-of-birth");
}

[Fact]
public async Task Post_AuthenticatedUserDoesNotHavePermission_ReturnsForbidden()
{
var user = await TestData.CreateUser(userType: Models.UserType.Default);

await AuthenticatedUserDoesNotHavePermission_ReturnsForbidden(HttpMethod.Post, $"/admin/users/{user.UserId}/date-of-birth");
}

[Fact]
public async Task Post_UserDoesNotExist_ReturnsNotFound()
{
// Arrange
var userId = Guid.NewGuid();
var request = new HttpRequestMessage(HttpMethod.Post, $"/admin/users/{userId}/date-of-birth");

// Act
var response = await HttpClient.SendAsync(request);

// Assert
Assert.Equal(StatusCodes.Status404NotFound, (int)response.StatusCode);
}

[Fact]
public async Task Post_UserIsNotDefaultType_ReturnsNotFound()
{
// Arrange
var user = await TestData.CreateUser(userType: Models.UserType.Staff);
var request = new HttpRequestMessage(HttpMethod.Post, $"/admin/users/{user.UserId}/date-of-birth");

// Act
var response = await HttpClient.SendAsync(request);

// Assert
Assert.Equal(StatusCodes.Status404NotFound, (int)response.StatusCode);
}

[Theory]
[InlineData(false, false, UserUpdatedEventChanges.None)]
[InlineData(true, true, UserUpdatedEventChanges.DateOfBirth)]
public async Task Post_ValidRequest_SetsUserNameEmitsEventAndRedirects(
bool changeDateOfBirth,
bool expectEvent,
UserUpdatedEventChanges expectedChanges)
{
// Arrange
var user = await TestData.CreateUser(userType: Models.UserType.Default);

var newDateOfBirth = changeDateOfBirth ? user.DateOfBirth!.Value.AddDays(1) : user.DateOfBirth!.Value;

var request = new HttpRequestMessage(HttpMethod.Post, $"/admin/users/{user.UserId}/date-of-birth")
{
Content = new FormUrlEncodedContentBuilder()
{
{ "DateOfBirth.Day", newDateOfBirth.Day },
{ "DateOfBirth.Month", newDateOfBirth.Month },
{ "DateOfBirth.Year", newDateOfBirth.Year },
}
};

// Act
var response = await HttpClient.SendAsync(request);

// Assert
Assert.Equal(StatusCodes.Status302Found, (int)response.StatusCode);
Assert.Equal($"/admin/users/{user.UserId}", response.Headers.Location?.OriginalString);

await TestData.WithDbContext(async dbContext =>
{
user = await dbContext.Users.SingleAsync(u => u.UserId == user.UserId);
Assert.Equal(newDateOfBirth, user.DateOfBirth);
});

if (expectEvent)
{
EventObserver.AssertEventsSaved(
e =>
{
var userUpdatedEvent = Assert.IsType<UserUpdatedEvent>(e);
Assert.Equal(Clock.UtcNow, userUpdatedEvent.CreatedUtc);
Assert.Equal(UserUpdatedEventSource.SupportUi, userUpdatedEvent.Source);
Assert.Equal(expectedChanges, userUpdatedEvent.Changes);
Assert.Equal(user.UserId, userUpdatedEvent.User.UserId);
Assert.Equal(TestUsers.AdminUserWithAllRoles.UserId, userUpdatedEvent.UpdatedByUserId);
});
}
else
{
EventObserver.AssertEventsSaved();
}
}
}

0 comments on commit 0cc3d1e

Please sign in to comment.