Change the ASP.net pages to return partial views, and be compatible with the htmlIslands react component.

This commit is contained in:
Colin Dawson 2026-02-02 23:17:46 +00:00
parent 90b23f939c
commit e062d15101
21 changed files with 149 additions and 49 deletions

View File

@ -10,10 +10,12 @@ public interface ICustomFieldManager
Task<IPaginatedData<CustomFieldDefinition>> GetFieldsAsync(Paging paging, CancellationToken cancellationToken);
Task CreateFieldAsync(AuditUserDetails auditUserDetails, CreateCustomField custimField, CancellationToken cancellationToken);
Task EditFieldAsync(AuditUserDetails auditUserDetails, EditCustomFields custimField, CancellationToken cancellationToken);
Task PatchFieldAsync(AuditUserDetails auditUserDetails, IGeneralIdRef Id, PatchCustomFields customField, CancellationToken cancellationToken);
Task<CustomFieldDefinition> GetFieldAsync(IGeneralIdRef id, CancellationToken cancellationToken);
Task<CustomFieldDefinition> GetFieldAsync(string name, CancellationToken cancellationToken);
Task DeleteFieldAsync(AuditUserDetails auditUserDetails, IGeneralIdRef id, CancellationToken cancellationToken);
}

View File

@ -1,6 +1,9 @@
using System.Text.Json.Serialization;
using e_suite.API.Common.models.@base;
using e_suite.API.Common.models.@base;
using e_suite.Database.Core.Tables.UserManager;
using eSuite.Core.CustomFields;
using eSuite.Core.Miscellaneous;
using System.Text.Json.Serialization;
using e_suite.Database.Core.Tables.CustomFields;
namespace e_suite.API.Common.models;
@ -10,3 +13,36 @@ public class EditCustomFields : CustomFieldBase
public GeneralIdRef Id { get; set; } = new GeneralIdRef();
}
[Patches(typeof(CustomField))]
public class PatchCustomFields
{
[JsonPropertyName("fieldType")]
[PatchMap(nameof(CustomField.FieldType))]
public FieldType? FieldType { get; set; }
//Todo add support for this
//[JsonPropertyName("refElementId")]
//public GeneralIdRef? RefElementId { get; set; }
[JsonPropertyName("name")]
[PatchMap(nameof(CustomField.Name))]
public string? Name { get; set; } = string.Empty;
[JsonPropertyName("defaultValue")]
[PatchMap(nameof(CustomField.DefaultValue))]
public string? DefaultValue { get; set; } = string.Empty;
[JsonPropertyName("minEntries")]
[PatchMap(nameof(CustomField.MinEntries))]
public long? MinEntries { get; set; }
[JsonPropertyName("maxEntries")]
[PatchMap(nameof(CustomField.MaxEntries))]
public long? MaxEntries { get; set; }
//Todo add support for this
//[JsonPropertyName("params")]
//[PatchMap(nameof(CustomField.))]
//public string? Parameters { get; set; } = string.Empty;
}

View File

@ -27,8 +27,8 @@ public class LoginGetUnitTests : AccountControllerTestBase
var response = await _accountController.LoginGet(login, CancellationToken.None);
//Assert
Assert.That(response, Is.TypeOf<ViewResult>());
var viewResult = response as ViewResult;
Assert.That(response, Is.TypeOf<PartialViewResult>());
var viewResult = response as PartialViewResult;
Assert.That(viewResult?.ViewName, Is.EqualTo("Login"));
Assert.That(viewResult?.Model, Is.TypeOf<Login>());
@ -56,8 +56,8 @@ public class LoginGetUnitTests : AccountControllerTestBase
var response = await _accountController.LoginGet(login, CancellationToken.None);
//Assert
Assert.That(response, Is.TypeOf<ViewResult>());
var viewResult = response as ViewResult;
Assert.That(response, Is.TypeOf<PartialViewResult>());
var viewResult = response as PartialViewResult;
Assert.That(viewResult?.ViewName, Is.EqualTo("Login"));
Assert.That(viewResult?.Model, Is.TypeOf<Login>());

View File

@ -28,8 +28,8 @@ public class LoginPostUnitTests : AccountControllerTestBase
var response = await _accountController.LoginPost(login, CancellationToken.None);
//Assert
Assert.That(response, Is.TypeOf<ViewResult>());
var viewResult = response as ViewResult;
Assert.That(response, Is.TypeOf<PartialViewResult>());
var viewResult = response as PartialViewResult;
Assert.That(viewResult?.ViewName, Is.EqualTo("Login"));
Assert.That(viewResult?.Model, Is.TypeOf<Login>());
@ -75,8 +75,8 @@ public class LoginPostUnitTests : AccountControllerTestBase
var response = await _accountController.LoginPost(login, CancellationToken.None);
//Assert
Assert.That(response, Is.TypeOf<ViewResult>());
var viewResult = response as ViewResult;
Assert.That(response, Is.TypeOf<PartialViewResult>());
var viewResult = response as PartialViewResult;
Assert.That(viewResult?.ViewName, Is.EqualTo("Login"));
Assert.That(viewResult?.Model, Is.TypeOf<Login>());
@ -101,8 +101,8 @@ public class LoginPostUnitTests : AccountControllerTestBase
var response = await _accountController.LoginPost(login, CancellationToken.None);
//Assert
Assert.That(response, Is.TypeOf<ViewResult>());
var viewResult = response as ViewResult;
Assert.That(response, Is.TypeOf<PartialViewResult>());
var viewResult = response as PartialViewResult;
Assert.That(viewResult?.ViewName, Is.EqualTo("Login"));
Assert.That(viewResult?.Model, Is.TypeOf<Login>());
@ -166,8 +166,8 @@ public class LoginPostUnitTests : AccountControllerTestBase
var response = await _accountController.LoginPost(login, CancellationToken.None);
//Assert
Assert.That(response, Is.TypeOf<ViewResult>());
var viewResult = response as ViewResult;
Assert.That(response, Is.TypeOf<PartialViewResult>());
var viewResult = response as PartialViewResult;
Assert.That(viewResult?.ViewName, Is.EqualTo("Login"));
Assert.That(viewResult?.Model, Is.TypeOf<Login>());
@ -196,8 +196,8 @@ public class LoginPostUnitTests : AccountControllerTestBase
var response = await _accountController.LoginPost(login, CancellationToken.None);
//Assert
Assert.That(response, Is.TypeOf<ViewResult>());
var viewResult = response as ViewResult;
Assert.That(response, Is.TypeOf<PartialViewResult>());
var viewResult = response as PartialViewResult;
Assert.That(viewResult?.ViewName, Is.EqualTo("Login"));
Assert.That(viewResult?.Model, Is.TypeOf<Login>());

View File

@ -42,8 +42,8 @@ public class ProfileGetUnitTests : AccountControllerTestBase
var response = await _accountController.ProfileGet(CancellationToken.None);
//Assert
Assert.That(response, Is.TypeOf<ViewResult>());
var viewResult = response as ViewResult;
Assert.That(response, Is.TypeOf<PartialViewResult>());
var viewResult = response as PartialViewResult;
Assert.That(viewResult?.ViewName, Is.EqualTo("Profile"));
Assert.That(viewResult?.Model, Is.TypeOf<Models.UserProfile>());

View File

@ -71,9 +71,9 @@ public class AccountController : ESuiteController
return LoginView(login);
}
private ViewResult LoginView(Login? login)
private ActionResult LoginView(Login? login)
{
return View("Login", login);
return PartialView("Login", login);
}
/// <summary>
@ -236,7 +236,7 @@ public class AccountController : ESuiteController
var profile = await _userManager.GetProfile(User.Email(), cancellationToken);
var userProfile = profile.ToUserProfile();
return View("Profile", userProfile);
return PartialView("Profile", userProfile);
}
/// <summary>

View File

@ -1,5 +1,6 @@
using e_suite.API.Common;
using e_suite.API.Common.models;
using e_suite.Database.Core.Tables.CustomFields;
using e_suite.Utilities.Pagination;
using eSuite.API.security;
using eSuite.API.Utilities;
@ -104,6 +105,28 @@ public class CustomFieldsController : ESuiteControllerBase
return Ok();
}
/// <summary>
/// Patching is useful when you only want to update a few fields of the user rather than the whole object.
/// </summary>
/// <param name="id"></param>
/// <param name="customFields"></param>>
/// <param name="cancellationToken"></param>
/// <returns></returns>
[Route("field")]
[HttpPatch]
[AccessKey(SecurityAccess.EditField)]
[ProducesResponseType(StatusCodes.Status200OK)]
[ProducesResponseType(StatusCodes.Status400BadRequest)]
public async Task<IActionResult> PatchField(
[FromQuery] IGeneralIdRef id,
[FromBody] PatchCustomFields customFields,
CancellationToken cancellationToken = default!
)
{
await _customFieldManager.PatchFieldAsync(AuditUserDetails, id, customFields, cancellationToken);
return Ok();
}
/// <summary>
/// Delete a custom field by giving a Id
/// </summary>

View File

@ -8,7 +8,7 @@
<div class="logo"></div>
<div className="loginForm">
<h1>Login</h1>
@using (Html.BeginForm(FormMethod.Post, true, null))
@using (Html.BeginForm(method: FormMethod.Post, antiforgery:true, htmlAttributes: new { data_full_page_redirect = "true" }))
{
<fieldset>
@Html.AntiForgeryToken()
@ -44,7 +44,7 @@
@Html.LabelFor(m => m.Email)
</div>
<div class="editor-field">
@Html.EditorFor(m => m.Email, new { htmlAttributes = new { type = "email", autocomplete = "username" } })
@Html.EditorFor(m => m.Email, new { htmlAttributes = new { type = "email", autocomplete = "username", @class = "form-control" } })
@Html.ValidationMessageFor(m => m.Email)
</div>

View File

@ -2,7 +2,8 @@
@{
ViewBag.Title = "e-suite";
Layout = "~/Views/Shared/_layout.cshtml";
//Layout = "~/Views/Shared/_layout.cshtml";
Layout = null;
}
<div>
@ -41,6 +42,5 @@
@await Html.PartialAsync("_loginMethodChooser")
<button type="submit" class="btn btn-primary btn-spaced" name="Submit">Save</button>
<a href="/">Home</a>
}
</div>

View File

@ -1,5 +1,3 @@
using System.Linq.Expressions;
using System.Text.Json;
using e_suite.API.Common;
using e_suite.API.Common.exceptions;
using e_suite.API.Common.models;
@ -11,6 +9,8 @@ using e_suite.Database.Core.Tables.CustomFields;
using e_suite.Utilities.Pagination;
using eSuite.Core.CustomFields;
using eSuite.Core.Miscellaneous;
using System.Linq.Expressions;
using System.Text.Json;
namespace e_suite.Modules.CustomFieldsManager;
@ -26,12 +26,14 @@ public class CustomFieldManager : ICustomFieldManager
private readonly ICustomFieldRepository _customFieldRepository;
private readonly ICustomFieldReferenceObjectRepository _customFieldReferenceObjectRepository;
private readonly ICustomFieldHelper _customFieldHelper;
private readonly IPatchFactory _patchFactory;
public CustomFieldManager(ICustomFieldRepository customFieldRepository, ICustomFieldReferenceObjectRepository customFieldReferenceObjectRepository, ICustomFieldHelper customFieldHelper)
public CustomFieldManager(ICustomFieldRepository customFieldRepository, ICustomFieldReferenceObjectRepository customFieldReferenceObjectRepository, ICustomFieldHelper customFieldHelper, IPatchFactory patchFactory)
{
_customFieldRepository = customFieldRepository;
_customFieldReferenceObjectRepository = customFieldReferenceObjectRepository;
_customFieldHelper = customFieldHelper;
_patchFactory = patchFactory;
}
private async Task<ValueTuple<bool, long?>> ValidateFieldDefinition(FieldType fieldType, IGeneralIdRef? idRef, CancellationToken cancellationToken)
@ -212,11 +214,8 @@ public class CustomFieldManager : ICustomFieldManager
CancellationToken cancellationToken
)
{
await _customFieldRepository.TransactionAsync(async () =>
await AlterField(auditUserDetails, customField.Id, async customFieldOriginal =>
{
var customFieldOriginal = await _customFieldRepository.GetByIdAsync(customField.Id, cancellationToken);
Assert(customFieldOriginal);
var originalFieldType = customFieldOriginal!.FieldType;
var refId = await AssertFieldReference(customField.FieldType, customField.RefElementId!, cancellationToken);
@ -233,12 +232,43 @@ public class CustomFieldManager : ICustomFieldManager
customFieldOriginal.MinEntries = customField.MinEntries;
await DealWithParameters(auditUserDetails, customField, originalFieldType, customFieldOriginal, refId, cancellationToken);
}, cancellationToken);
}
private async Task AlterField(
AuditUserDetails auditUserDetails,
IGeneralIdRef Id,
Func<CustomField, Task> applyChanges,
CancellationToken cancellationToken
)
{
await _customFieldRepository.TransactionAsync(async () =>
{
var customFieldOriginal = await _customFieldRepository.GetByIdAsync(Id, cancellationToken);
Assert(customFieldOriginal);
await applyChanges(customFieldOriginal);
Assert(customFieldOriginal);
await _customFieldRepository.EditAsync(auditUserDetails, customFieldOriginal, cancellationToken);
});
}
public async Task PatchFieldAsync(
AuditUserDetails auditUserDetails,
IGeneralIdRef Id,
PatchCustomFields customField,
CancellationToken cancellationToken
)
{
var patch = _patchFactory.Create(customField);
await AlterField(auditUserDetails, Id, async editUser =>
{
await patch.ApplyTo(customField);
}, cancellationToken);
}
private async Task DealWithParameters(
AuditUserDetails auditUserDetails,
CustomFieldBase customField,

View File

@ -7,7 +7,7 @@ using eSuite.Core.CustomFields;
namespace e_suite.Modules.CustomFieldManagerUnitTests.CustomFieldHelperUnitTests;
[TestFixture]
public class CustomFieldValuesListUnitTests : CustomFieldsTestBase
public class CustomFieldValuesListUnitTests : CustomFieldsTestBase<object>
{
[SetUp]
public override async Task Setup() => await base.Setup();

View File

@ -3,7 +3,7 @@
namespace e_suite.Modules.CustomFieldManagerUnitTests.CustomFieldHelperUnitTests;
[TestFixture]
public class GetFormTemplateByGeneralRefIdAsyncUnitTests : CustomFieldsTestBase
public class GetFormTemplateByGeneralRefIdAsyncUnitTests : CustomFieldsTestBase<object>
{
[SetUp]
public override async Task Setup() => await base.Setup();

View File

@ -3,7 +3,7 @@
namespace e_suite.Modules.CustomFieldManagerUnitTests.CustomFieldHelperUnitTests;
[TestFixture]
public class GetGlossaryByGeneralRefIdAsyncUnitTests : CustomFieldsTestBase
public class GetGlossaryByGeneralRefIdAsyncUnitTests : CustomFieldsTestBase<object>
{
[SetUp]
public override async Task Setup() => await base.Setup();

View File

@ -5,7 +5,7 @@ using eSuite.Core.CustomFields;
namespace e_suite.Modules.CustomFieldManagerUnitTests.CustomFieldHelperUnitTests;
[TestFixture]
public class TranslateToCustomFieldDefinitionAsyncUnitTests : CustomFieldsTestBase
public class TranslateToCustomFieldDefinitionAsyncUnitTests : CustomFieldsTestBase<object>
{
[SetUp]
public override async Task Setup() => await base.Setup();

View File

@ -7,7 +7,7 @@ using Sequence = e_suite.Database.Core.Tables.Sequences.Sequence;
namespace e_suite.Modules.CustomFieldManagerUnitTests.CustomFieldManagerUnitTests;
public class CreateCustomFieldUnitTest : CustomFieldsTestBase
public class CreateCustomFieldUnitTest : CustomFieldsTestBase<object>
{
[SetUp]
public override async Task Setup() => await base.Setup();
@ -27,7 +27,7 @@ public class CreateCustomFieldUnitTest : CustomFieldsTestBase
MaxEntries = 5,
Parameters = "{\"multiLine\":false}"
};
_customFieldManager = new CustomFieldManager(_fakeCustomFieldRepository!, _fakeCustomFieldReferenceRepository, _customFieldHelper);
_customFieldManager = new CustomFieldManager(_fakeCustomFieldRepository!, _fakeCustomFieldReferenceRepository, _customFieldHelper, PatchFactoryMock.Object);
_customFieldRepository.Setup(x => x.CreateAsync(It.IsAny<AuditUserDetails>(), It.Is<CustomField>(x => x.Name == "Test"), It.IsAny<CancellationToken>()));
//Act

View File

@ -2,7 +2,7 @@
namespace e_suite.Modules.CustomFieldManagerUnitTests.CustomFieldManagerUnitTests;
public class DeactivateCustomFieldUnitTest : CustomFieldsTestBase
public class DeactivateCustomFieldUnitTest : CustomFieldsTestBase<object>
{
[SetUp]
public override async Task Setup() => await base.Setup();
@ -21,7 +21,7 @@ public class DeactivateCustomFieldUnitTest : CustomFieldsTestBase
Id = 5,
};
_fakeCustomFieldRepository.CustomFields.Add(customField);
_customFieldManager = new CustomFieldManager(_fakeCustomFieldRepository, _customFieldReferenceObjectRepository.Object, _customFieldHelper);
_customFieldManager = new CustomFieldManager(_fakeCustomFieldRepository, _customFieldReferenceObjectRepository.Object, _customFieldHelper, PatchFactoryMock.Object);
Assert.ThrowsAsync<NotFoundException>(() => _customFieldManager.DeleteFieldAsync(auditResult, new GeneralIdRef { Guid = Guid.NewGuid() }, default));
}
@ -46,7 +46,7 @@ public class DeactivateCustomFieldUnitTest : CustomFieldsTestBase
[Test]
public void DeactivateNotexistingCustomfieldthrowsException()
{
_customFieldManager = new CustomFieldManager(_fakeCustomFieldRepository, _customFieldReferenceObjectRepository.Object, _customFieldHelper);
_customFieldManager = new CustomFieldManager(_fakeCustomFieldRepository, _customFieldReferenceObjectRepository.Object, _customFieldHelper, PatchFactoryMock.Object);
Assert.ThrowsAsync<NotFoundException>(() => _customFieldManager.DeleteFieldAsync(auditResult, new GeneralIdRef { Guid = Guid.NewGuid() }, default));
}

View File

@ -7,7 +7,7 @@ using Sequence = e_suite.Database.Core.Tables.Sequences.Sequence;
namespace e_suite.Modules.CustomFieldManagerUnitTests.CustomFieldManagerUnitTests;
public class EditCustomFieldUnitTests : CustomFieldsTestBase
public class EditCustomFieldUnitTests : CustomFieldsTestBase<object>
{
[SetUp]

View File

@ -2,7 +2,7 @@ using e_suite.API.Common.exceptions;
namespace e_suite.Modules.CustomFieldManagerUnitTests.CustomFieldManagerUnitTests;
public class GetCustomByIdFieldUnitTest : CustomFieldsTestBase
public class GetCustomByIdFieldUnitTest : CustomFieldsTestBase<object>
{
[SetUp]
public override async Task Setup()

View File

@ -2,7 +2,7 @@
namespace e_suite.Modules.CustomFieldManagerUnitTests.CustomFieldManagerUnitTests;
public class GetFieldByNameUnitTest : CustomFieldsTestBase
public class GetFieldByNameUnitTest : CustomFieldsTestBase<object>
{
[SetUp]
public override async Task Setup() => await base.Setup();
@ -21,7 +21,7 @@ public class GetFieldByNameUnitTest : CustomFieldsTestBase
Id = 15,
Name = name,
};
_customFieldManager = new CustomFieldManager(_fakeCustomFieldRepository, _customFieldReferenceObjectRepository.Object, _customFieldHelper);
_customFieldManager = new CustomFieldManager(_fakeCustomFieldRepository, _customFieldReferenceObjectRepository.Object, _customFieldHelper, PatchFactoryMock.Object);
_fakeCustomFieldRepository.CustomFields.Add(customField);
var res = await _customFieldManager.GetFieldAsync(name,default);
Assert.Multiple(() =>

View File

@ -2,7 +2,7 @@
namespace e_suite.Modules.CustomFieldManagerUnitTests.CustomFieldManagerUnitTests;
public class GetFieldsAsyncUnitTests : CustomFieldsTestBase
public class GetFieldsAsyncUnitTests : CustomFieldsTestBase<object>
{
[SetUp]
public override async Task Setup() => await base.Setup();

View File

@ -5,7 +5,7 @@ using e_suite.UnitTestCore;
namespace e_suite.Modules.CustomFieldManagerUnitTests;
public class CustomFieldsTestBase : TestBase
public class CustomFieldsTestBase<T> : TestBase
{
protected Mock<ICustomFieldRepository> _customFieldRepository = null!;
protected CustomFieldManager _customFieldManager = null!;
@ -14,6 +14,8 @@ public class CustomFieldsTestBase : TestBase
protected Mock<ICustomFieldReferenceObjectRepository> _customFieldReferenceObjectRepository = null!;
protected FakeCustomFieldReferenceRepository _fakeCustomFieldReferenceRepository = null!;
protected ICustomFieldHelper _customFieldHelper = null!;
protected Mock<IPatchFactory> PatchFactoryMock = null!;
protected Mock<IPatch<T>> PatchMock = null!;
public override async Task Setup()
{
@ -29,6 +31,13 @@ public class CustomFieldsTestBase : TestBase
_customFieldReferenceObjectRepository = new Mock<ICustomFieldReferenceObjectRepository>();
_fakeCustomFieldReferenceRepository = new FakeCustomFieldReferenceRepository();
_customFieldHelper = new CustomFieldHelper(_fakeCustomFieldReferenceRepository);
_customFieldManager = new CustomFieldManager(_fakeCustomFieldRepository, _fakeCustomFieldReferenceRepository, _customFieldHelper);
PatchFactoryMock = new Mock<IPatchFactory>();
PatchMock = new Mock<IPatch<T>>();
PatchFactoryMock
.Setup(f => f.Create(It.IsAny<T>()))
.Returns(PatchMock.Object);
_customFieldManager = new CustomFieldManager(_fakeCustomFieldRepository, _fakeCustomFieldReferenceRepository, _customFieldHelper, PatchFactoryMock.Object);
}
}