Skip to content

Implement Common API Endpoint - #8

Open
KianW-DfE wants to merge 14 commits into
mainfrom
feature/SAB-263_implement_common_api_endpoint
Open

Implement Common API Endpoint#8
KianW-DfE wants to merge 14 commits into
mainfrom
feature/SAB-263_implement_common_api_endpoint

Conversation

@KianW-DfE

@KianW-DfE KianW-DfE commented Aug 13, 2026

Copy link
Copy Markdown
Collaborator
  • The API should have Scalar documentation describing the required fields and acceptable valueT
  • The API should provide suitable validation error messages for missing fields
  • The API should have integration tests demonstrating happy paths and validation errors

SAB-263

@github-actions

github-actions Bot commented Aug 13, 2026

Copy link
Copy Markdown

Test Results

25 tests  +6   25 ✅ +6   3s ⏱️ -1s
 3 suites ±0    0 💤 ±0 
 3 files   ±0    0 ❌ ±0 

Results for commit 68160c6. ± Comparison against base commit 87a23d7.

♻️ This comment has been updated with latest results.

@github-actions

github-actions Bot commented Aug 13, 2026

Copy link
Copy Markdown

Summary

Summary
Generated on: 08/13/2026 - 16:04:38
Parser: MultiReport (3x Cobertura)
Assemblies: 4
Classes: 31
Files: 25
Line coverage: 68.6% (243 of 354)
Covered lines: 243
Uncovered lines: 111
Coverable lines: 354
Total lines: 823
Branch coverage: 58.9% (66 of 112)
Covered branches: 66
Total branches: 112
Method coverage: Feature is only available for sponsors
Tag: 60_31718715712

Coverage

SchoolAccount.Collect.Api - 69.1%
Name Line Branch
SchoolAccount.Collect.Api 69.1% 57.5%
Program 100% 100%
SchoolAccount.Collect.Api.DependencyInjection 100% 100%
SchoolAccount.Collect.Api.Endpoints.Organisations.GetByLaestab.GetByLaestab
Endpoint
100% 100%
SchoolAccount.Collect.Api.Endpoints.Status.GetStatus.GetStatusEndpoint 100% 100%
SchoolAccount.Collect.Api.Extensions.EndpointExtensions 100% 92.8%
SchoolAccount.Collect.Api.Extensions.MiddlewareExtensions 100%
SchoolAccount.Collect.Api.Extensions.ResultExtensions 50% 25%
SchoolAccount.Collect.Api.Infrastructure.CustomResults 0% 0%
SchoolAccount.Collect.Api.Infrastructure.GlobalExceptionHandler 8.3%
SchoolAccount.Collect.Api.Middleware.RequestContextLoggingMiddleware 100% 100%
SchoolAccount.Collect.Application - 68.4%
Name Line Branch
SchoolAccount.Collect.Application 68.4% 76.6%
SchoolAccount.Collect.Application.Abstractions.Behaviors.LoggingDecorator.C
ommandBaseHandler
0% 0%
SchoolAccount.Collect.Application.Abstractions.Behaviors.LoggingDecorator.C
ommandBaseHandler
0%
SchoolAccount.Collect.Application.Abstractions.Behaviors.LoggingDecorator.C
ommandHandler<TCommand, TResponse>
0%
SchoolAccount.Collect.Application.Abstractions.Behaviors.LoggingDecorator.C
ommandHandler<TCommand, TResponse>
0% 0%
SchoolAccount.Collect.Application.Abstractions.Behaviors.LoggingDecorator.Q
ueryHandler<TQuery, TResponse>
0% 0%
SchoolAccount.Collect.Application.Abstractions.Behaviors.LoggingDecorator.Q
ueryHandler<TQuery, TResponse>
0%
SchoolAccount.Collect.Application.DependencyInjection 100% 100%
SchoolAccount.Collect.Application.Organisations.GetByLaestab.GetOrganisatio
nByLaestabQuery
100%
SchoolAccount.Collect.Application.Organisations.GetByLaestab.GetOrganisatio
nByLaestabQueryHandler
100%
SchoolAccount.Collect.Application.Organisations.GetByLaestab.LaestabValue 100% 100%
SchoolAccount.Collect.Application.Organisations.GetByLaestab.StatusCalculat
or
100% 100%
SchoolAccount.Collect.Application.Status.GetStatus.GetStatusQuery 100%
SchoolAccount.Collect.Application.Status.GetStatus.GetStatusQueryHandler 100% 75%
SchoolAccount.Collect.Application.Status.GetStatus.GetStatusRequestModel 100%
SchoolAccount.Collect.Application.Status.GetStatus.StatusResponse 100%
SchoolAccount.Collect.Infrastructure - 80%
Name Line Branch
SchoolAccount.Collect.Infrastructure 80% ****
SchoolAccount.Collect.Infrastructure.DependencyInjection 100%
SchoolAccount.Collect.Infrastructure.Time.DateTimeProvider 0%
SchoolAccount.Collect.SharedKernel - 65.7%
Name Line Branch
SchoolAccount.Collect.SharedKernel 65.7% 31.2%
SchoolAccount.Collect.SharedKernel.Error 73.3%
SchoolAccount.Collect.SharedKernel.Result 54.5% 50%
SchoolAccount.Collect.SharedKernel.Result 80% 25%
SchoolAccount.Collect.SharedKernel.ValidationError 0% 0%

namespace SchoolAccount.Collect.Application.Status.GetStatus;

[DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.AllConstructors)]
public sealed class GetStatusQueryHandler : IQueryHandler<GetStatusQuery, StatusResponse>

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There's no test coverage on this class, if you look at the sample GetOrganisationByLaestabQueryHandler there's a unit test, probably worth adding one for consistency.

{
var response = new StatusResponse
{
Details = getStatusQuery

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is the kind of thing that might benefit from refactoring.
You basically need a mapper from the Request to your query object.

There's a few options, with various pros and cons. My first stab would be seeing which classes properties are accessed the most in the logic, and see if there's a logical place it fits.

For example, could it be a ToQuery method on GetStatusRequest, which can then be easily unit tested.

Some people prefer to keep the requests slim, without logic, but it keeps the logic near to the only thing that uses it, and avoids have to add static extension methods or builders.

Ukprn = x.Ukprn,
Laestab = laestab,
Interesting = interesting,
Actions = interesting ? new List<Action> { new() } : new(),

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This reads a little odd with the new List<Action> { new() } because of the defaults in the Action and Status.

I'd probably keep the Action and Status without specific defaults and explicitly set the Name and Status explicitly on creation


public sealed record OrganisationResponse
{
public string Id { get; init; } = string.Empty;

@markysoft markysoft Aug 13, 2026

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You might be able to get away without the default string.Empty if you've got an init

@API_HostAddress = http://localhost:5101

### Get status for school with Local Authority
POST {{API_HostAddress}}/status

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe add one for a validation error returned too?

}

[Fact]
public async Task Status_endpoint_should_return_error_messages_for_all_missing_required_parameters_with_empty_academy()

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Tests look really good, do you think it needs separate validation tests for academy and trust rather than covering all possible fields in one?

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I can get rid of them, I'd only put them in to show the different amount of errors you can get depending on which empty fields you input, so trust and academy show different as LocalAuthority isn't required on a trust, but for academy LocalAuthority had 3 required parameters, so I could show the error messages all in one test but it might not show the reason for all those fields being required

public async Task Status_endpoint_should_return_error_messages_for_all_missing_required_parameters_with_empty_academy()
{
// Arrange
var organisation = new Organisation

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You could lean on anonymous objects a bit here and create something like the following to create a skeletal object with fields missing.

var request = new { Organisations = new[] { new { Category = new { } , LocalAuthority = new {} } } };

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants