Skip to content

Commit

Permalink
Changed all response DTOs to define 'required' properties by default.
Browse files Browse the repository at this point in the history
Fixed JsonClient to not require new() constraint on responses
Fixed OpenAPI output for 'nullable' properties in responses.
  • Loading branch information
jezzsantos committed Dec 2, 2024
1 parent 318c497 commit e2c3c27
Show file tree
Hide file tree
Showing 132 changed files with 651 additions and 435 deletions.
4 changes: 2 additions & 2 deletions docs/how-to-guides/020-api-endpoint.md
Original file line number Diff line number Diff line change
Expand Up @@ -477,7 +477,7 @@ An example response type would be:
```c#
public class GetCarResponse : IWebResponse
{
public Car? Car { get; set; }
public required Car Car { get; set; }
}
```

Expand All @@ -486,7 +486,7 @@ However, there is one specific response type that you want to use for your SEARC
```c#
public class SearchAllCarsResponse : SearchResponse
{
public List<Car>? Cars { get; set; }
public List<Car> Cars { get; set; } = [];
}
```

Expand Down
6 changes: 3 additions & 3 deletions src/AncillaryInfrastructure.IntegrationTests/AuditsApiSpec.cs
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,7 @@ public async Task WhenDeliverAudit_ThenDelivers()
OrganizationId = tenantId
});

audits.Content.Value.Audits!.Count.Should().Be(1);
audits.Content.Value.Audits.Count.Should().Be(1);
audits.Content.Value.Audits[0].OrganizationId.Should().Be(tenantId);
audits.Content.Value.Audits[0].MessageTemplate.Should().Be("amessagetemplate");
audits.Content.Value.Audits[0].TemplateArguments.Count.Should().Be(2);
Expand All @@ -82,7 +82,7 @@ public async Task WhenDrainAllAuditsAndNone_ThenDoesNotDrainAny()
OrganizationId = tenantId
});

audits.Content.Value.Audits!.Count.Should().Be(0);
audits.Content.Value.Audits.Count.Should().Be(0);
}
#endif

Expand Down Expand Up @@ -126,7 +126,7 @@ public async Task WhenDrainAllAuditsAndSomeWithUnknownTenancies_ThenDrains()
OrganizationId = tenantId
});

audits.Content.Value.Audits!.Count.Should().Be(2);
audits.Content.Value.Audits.Count.Should().Be(2);
audits.Content.Value.Audits[0].OrganizationId.Should().Be(tenantId);
audits.Content.Value.Audits[0].AuditCode.Should().Be("anauditcode1");
audits.Content.Value.Audits[0].MessageTemplate.Should().Be("amessagetemplate1");
Expand Down
12 changes: 6 additions & 6 deletions src/AncillaryInfrastructure.IntegrationTests/EmailsApiSpec.cs
Original file line number Diff line number Diff line change
Expand Up @@ -69,7 +69,7 @@ public async Task WhenSendEmailAndDeliverySucceeds_ThenDelivered()
req => req.SetJWTBearerToken(login.AccessToken));

var now = DateTime.UtcNow;
deliveries.Content.Value.Emails!.Count.Should().Be(1);
deliveries.Content.Value.Emails.Count.Should().Be(1);
deliveries.Content.Value.Emails[0].Subject.Should().Be("asubject");
deliveries.Content.Value.Emails[0].Body.Should().Be("anhtmlbody");
deliveries.Content.Value.Emails[0].ToEmailAddress.Should().Be("[email protected]");
Expand Down Expand Up @@ -119,7 +119,7 @@ public async Task WhenSendEmailAndDeliveryFails_ThenNotDelivered()
req => req.SetJWTBearerToken(login.AccessToken));

var now = DateTime.UtcNow;
deliveries.Content.Value.Emails!.Count.Should().Be(1);
deliveries.Content.Value.Emails.Count.Should().Be(1);
deliveries.Content.Value.Emails[0].Subject.Should().Be("asubject");
deliveries.Content.Value.Emails[0].Body.Should().Be("anhtmlbody");
deliveries.Content.Value.Emails[0].ToEmailAddress.Should().Be("[email protected]");
Expand Down Expand Up @@ -173,7 +173,7 @@ public async Task WhenSendEmailAndDeliveryFailsFirstTimeAndSucceedsSecondTime_Th
req => req.SetJWTBearerToken(login.AccessToken));

var now = DateTime.UtcNow;
deliveries.Content.Value.Emails!.Count.Should().Be(1);
deliveries.Content.Value.Emails.Count.Should().Be(1);
deliveries.Content.Value.Emails[0].Subject.Should().Be("asubject");
deliveries.Content.Value.Emails[0].Body.Should().Be("anhtmlbody");
deliveries.Content.Value.Emails[0].ToEmailAddress.Should().Be("[email protected]");
Expand Down Expand Up @@ -230,7 +230,7 @@ await Api.PostAsync(new ConfirmEmailDeliveredRequest
req => req.SetJWTBearerToken(login.AccessToken));

var now = DateTime.UtcNow;
deliveries.Content.Value.Emails!.Count.Should().Be(1);
deliveries.Content.Value.Emails.Count.Should().Be(1);
deliveries.Content.Value.Emails[0].Subject.Should().Be("asubject");
deliveries.Content.Value.Emails[0].Body.Should().Be("anhtmlbody");
deliveries.Content.Value.Emails[0].ToEmailAddress.Should().Be("[email protected]");
Expand Down Expand Up @@ -289,7 +289,7 @@ await Api.PostAsync(new ConfirmEmailDeliveryFailedRequest
req => req.SetJWTBearerToken(login.AccessToken));

var now = DateTime.UtcNow;
deliveries.Content.Value.Emails!.Count.Should().Be(1);
deliveries.Content.Value.Emails.Count.Should().Be(1);
deliveries.Content.Value.Emails[0].Subject.Should().Be("asubject");
deliveries.Content.Value.Emails[0].Body.Should().Be("anhtmlbody");
deliveries.Content.Value.Emails[0].ToEmailAddress.Should().Be("[email protected]");
Expand Down Expand Up @@ -342,7 +342,7 @@ public async Task WhenSearchEmailDeliveriesWithTags_TheReturnsEmails()
},
req => req.SetJWTBearerToken(login.AccessToken));

deliveries.Content.Value.Emails!.Count.Should().Be(1);
deliveries.Content.Value.Emails.Count.Should().Be(1);
deliveries.Content.Value.Emails[0].Subject.Should().Be("asubject");
deliveries.Content.Value.Emails[0].Tags.Count.Should().Be(3);
deliveries.Content.Value.Emails[0].Tags[0].Should().Be("atag1");
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,7 @@ public async Task WhenGetFeatureFlag_ThenReturnsFlag()
var result = await Api.GetAsync(request, req => req.SetHMACAuth(request, "asecret"));

result.StatusCode.Should().Be(HttpStatusCode.OK);
result.Content.Value.Flag!.Name.Should().Be(Flag.TestingOnly.Name);
result.Content.Value.Flag.Name.Should().Be(Flag.TestingOnly.Name);
_featureFlags.LastGetFlag.Should().Be(Flag.TestingOnly.Name);
#endif
}
Expand Down
16 changes: 8 additions & 8 deletions src/AncillaryInfrastructure.IntegrationTests/MailgunApiSpec.cs
Original file line number Diff line number Diff line change
Expand Up @@ -83,7 +83,7 @@ public async Task WhenNotifyMailgunEventAndDeliveredEvent_ThenReturnsOk()
var deliveries = await Api.GetAsync(new SearchEmailDeliveriesRequest(),
req => req.SetJWTBearerToken(login.AccessToken));

deliveries.Content.Value.Emails!.Count.Should().Be(1);
deliveries.Content.Value.Emails.Count.Should().Be(1);
deliveries.Content.Value.Emails[0].IsSent.Should().BeTrue();
deliveries.Content.Value.Emails[0].IsDelivered.Should().BeTrue();
deliveries.Content.Value.Emails[0].DeliveredAt.Should().Be(deliveredAt.FromUnixTimestamp());
Expand Down Expand Up @@ -131,7 +131,7 @@ public async Task WhenNotifyMailgunEventAndTemporaryFailedEvent_ThenReturnsOk()
var deliveries = await Api.GetAsync(new SearchEmailDeliveriesRequest(),
req => req.SetJWTBearerToken(login.AccessToken));

deliveries.Content.Value.Emails!.Count.Should().Be(1);
deliveries.Content.Value.Emails.Count.Should().Be(1);
deliveries.Content.Value.Emails[0].IsSent.Should().BeTrue();
deliveries.Content.Value.Emails[0].IsDelivered.Should().BeFalse();
deliveries.Content.Value.Emails[0].DeliveredAt.Should().BeNull();
Expand Down Expand Up @@ -179,12 +179,12 @@ public async Task WhenNotifyMailgunEventAndPermanentFailedEvent_ThenReturnsOk()
var deliveries = await Api.GetAsync(new SearchEmailDeliveriesRequest(),
req => req.SetJWTBearerToken(login.AccessToken));

deliveries.Content.Value.Emails![0].IsSent.Should().BeTrue();
deliveries.Content.Value.Emails![0].IsDelivered.Should().BeFalse();
deliveries.Content.Value.Emails![0].DeliveredAt.Should().BeNull();
deliveries.Content.Value.Emails![0].IsDeliveryFailed.Should().BeTrue();
deliveries.Content.Value.Emails![0].FailedDeliveryAt.Should().Be(failedAt.FromUnixTimestamp());
deliveries.Content.Value.Emails![0].FailedDeliveryReason.Should().Be("areason");
deliveries.Content.Value.Emails[0].IsSent.Should().BeTrue();
deliveries.Content.Value.Emails[0].IsDelivered.Should().BeFalse();
deliveries.Content.Value.Emails[0].DeliveredAt.Should().BeNull();
deliveries.Content.Value.Emails[0].IsDeliveryFailed.Should().BeTrue();
deliveries.Content.Value.Emails[0].FailedDeliveryAt.Should().Be(failedAt.FromUnixTimestamp());
deliveries.Content.Value.Emails[0].FailedDeliveryReason.Should().Be("areason");
}

private async Task<string> DeliveryEmailAsync()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -62,7 +62,7 @@ public async Task WhenNotifyProvisioning_ThenNotifies()
Id = tenantId
}, req => req.SetJWTBearerToken(login.AccessToken));

organization.Content.Value.Settings!.Count.Should().Be(3);
organization.Content.Value.Settings.Count.Should().Be(3);
organization.Content.Value.Settings["aname1"].Should().Be("avalue");
organization.Content.Value.Settings["aname2"].Should().Be("99");
organization.Content.Value.Settings["aname3"].Should().Be("True");
Expand Down Expand Up @@ -107,7 +107,7 @@ public async Task WhenDrainAllProvisioningsAndSomeWithUnknownTenancies_ThenDrain
Id = tenantId!
}, req => req.SetJWTBearerToken(login.AccessToken));

organization.Content.Value.Settings!.Count.Should().Be(3);
organization.Content.Value.Settings.Count.Should().Be(3);
organization.Content.Value.Settings["aname1"].Should().Be("avalue1");
organization.Content.Value.Settings["aname2"].Should().Be("99");
organization.Content.Value.Settings["aname3"].Should().Be("True");
Expand Down
12 changes: 6 additions & 6 deletions src/AncillaryInfrastructure.IntegrationTests/SmsesApiSpec.cs
Original file line number Diff line number Diff line change
Expand Up @@ -65,7 +65,7 @@ public async Task WhenSendSmsAndDeliverySucceeds_ThenDelivered()
req => req.SetJWTBearerToken(login.AccessToken));

var now = DateTime.UtcNow;
deliveries.Content.Value.Smses!.Count.Should().Be(1);
deliveries.Content.Value.Smses.Count.Should().Be(1);
deliveries.Content.Value.Smses[0].Body.Should().Be("anhtmlbody");
deliveries.Content.Value.Smses[0].ToPhoneNumber.Should().Be("+6498876986");
deliveries.Content.Value.Smses[0].Attempts.Should()
Expand Down Expand Up @@ -109,7 +109,7 @@ public async Task WhenSendSmsAndDeliveryFails_ThenNotDelivered()
req => req.SetJWTBearerToken(login.AccessToken));

var now = DateTime.UtcNow;
deliveries.Content.Value.Smses!.Count.Should().Be(1);
deliveries.Content.Value.Smses.Count.Should().Be(1);
deliveries.Content.Value.Smses[0].Body.Should().Be("anhtmlbody");
deliveries.Content.Value.Smses[0].ToPhoneNumber.Should().Be("+6498876986");
deliveries.Content.Value.Smses[0].Attempts.Should()
Expand Down Expand Up @@ -157,7 +157,7 @@ public async Task WhenSendSmsAndDeliveryFailsFirstTimeAndSucceedsSecondTime_Then
req => req.SetJWTBearerToken(login.AccessToken));

var now = DateTime.UtcNow;
deliveries.Content.Value.Smses!.Count.Should().Be(1);
deliveries.Content.Value.Smses.Count.Should().Be(1);
deliveries.Content.Value.Smses[0].Body.Should().Be("anhtmlbody");
deliveries.Content.Value.Smses[0].ToPhoneNumber.Should().Be("+6498876986");
deliveries.Content.Value.Smses[0].Attempts.Should().HaveCount(2);
Expand Down Expand Up @@ -208,7 +208,7 @@ await Api.PostAsync(new ConfirmSmsDeliveredRequest
req => req.SetJWTBearerToken(login.AccessToken));

var now = DateTime.UtcNow;
deliveries.Content.Value.Smses!.Count.Should().Be(1);
deliveries.Content.Value.Smses.Count.Should().Be(1);
deliveries.Content.Value.Smses[0].Body.Should().Be("anhtmlbody");
deliveries.Content.Value.Smses[0].ToPhoneNumber.Should().Be("+6498876986");
deliveries.Content.Value.Smses[0].Attempts.Should()
Expand Down Expand Up @@ -261,7 +261,7 @@ await Api.PostAsync(new ConfirmSmsDeliveryFailedRequest
req => req.SetJWTBearerToken(login.AccessToken));

var now = DateTime.UtcNow;
deliveries.Content.Value.Smses!.Count.Should().Be(1);
deliveries.Content.Value.Smses.Count.Should().Be(1);
deliveries.Content.Value.Smses[0].Body.Should().Be("anhtmlbody");
deliveries.Content.Value.Smses[0].ToPhoneNumber.Should().Be("+6498876986");
deliveries.Content.Value.Smses[0].Attempts.Should()
Expand Down Expand Up @@ -308,7 +308,7 @@ public async Task WhenSearchSmsDeliveriesWithTags_TheReturnsSmses()
},
req => req.SetJWTBearerToken(login.AccessToken));

deliveries.Content.Value.Smses!.Count.Should().Be(1);
deliveries.Content.Value.Smses.Count.Should().Be(1);
deliveries.Content.Value.Smses[0].Tags.Count.Should().Be(3);
deliveries.Content.Value.Smses[0].Tags[0].Should().Be("atag1");
deliveries.Content.Value.Smses[0].Tags[1].Should().Be("atag2");
Expand Down
4 changes: 2 additions & 2 deletions src/AncillaryInfrastructure.IntegrationTests/TwilioApiSpec.cs
Original file line number Diff line number Diff line change
Expand Up @@ -64,7 +64,7 @@ public async Task WhenNotifyTwilioEventAndDeliveredEvent_ThenReturnsOk()
var deliveries = await Api.GetAsync(new SearchSmsDeliveriesRequest(),
req => req.SetJWTBearerToken(login.AccessToken));

deliveries.Content.Value.Smses!.Count.Should().Be(1);
deliveries.Content.Value.Smses.Count.Should().Be(1);
deliveries.Content.Value.Smses[0].IsSent.Should().BeTrue();
deliveries.Content.Value.Smses[0].IsDelivered.Should().BeTrue();
deliveries.Content.Value.Smses[0].DeliveredAt.Should().Be(deliveredAt);
Expand Down Expand Up @@ -92,7 +92,7 @@ public async Task WhenNotifyTwilioEventAndFailedEvent_ThenReturnsOk()
var deliveries = await Api.GetAsync(new SearchSmsDeliveriesRequest(),
req => req.SetJWTBearerToken(login.AccessToken));

deliveries.Content.Value.Smses!.Count.Should().Be(1);
deliveries.Content.Value.Smses.Count.Should().Be(1);
deliveries.Content.Value.Smses[0].IsSent.Should().BeTrue();
deliveries.Content.Value.Smses[0].IsDelivered.Should().BeFalse();
deliveries.Content.Value.Smses[0].DeliveredAt.Should().BeNull();
Expand Down
41 changes: 21 additions & 20 deletions src/ApiHost1/Api/TestingOnly/TestingWebApi.cs
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
#if TESTINGONLY
using Application.Interfaces;
using Application.Persistence.Interfaces;
using Common;
using Common.Extensions;
Expand Down Expand Up @@ -167,50 +168,50 @@ public async Task<ApiEmptyResult> GetInsecure(
return () => new Result<EmptyResponse, Error>();
}

public async Task<ApiResult<string, StringMessageTestingOnlyResponse>> OpenApiGet(
OpenApiGetTestingOnlyRequest request, CancellationToken cancellationToken)
public async Task<ApiPostResult<string, OpenApiTestingOnlyResponse>> OpenApiFormUrlEncoded(
OpenApiPostFormUrlEncodedTestingOnlyRequest request, CancellationToken cancellationToken)
{
await Task.CompletedTask;
return () => new Result<StringMessageTestingOnlyResponse, Error>(new StringMessageTestingOnlyResponse
{ Message = $"amessage{request.RequiredField}" });
return () =>
new PostResult<OpenApiTestingOnlyResponse>(
new OpenApiTestingOnlyResponse { ARequiredField = "", Message = $"amessage{request.RequiredField}" },
"alocation");
}

public async Task<ApiPostResult<string, StringMessageTestingOnlyResponse>> OpenApiMultiPartForm(
OpenApiPostMultiPartFormDataTestingOnlyRequest request, CancellationToken cancellationToken)
public async Task<ApiResult<string, OpenApiTestingOnlyResponse>> OpenApiGet(
OpenApiGetTestingOnlyRequest request, CancellationToken cancellationToken)
{
await Task.CompletedTask;
return () =>
new PostResult<StringMessageTestingOnlyResponse>(
new StringMessageTestingOnlyResponse { Message = $"amessage{request.RequiredField}" },
"alocation");
return () => new Result<OpenApiTestingOnlyResponse, Error>(new OpenApiTestingOnlyResponse
{ ARequiredField = "", Message = $"amessage{request.RequiredField}" });
}

public async Task<ApiPostResult<string, StringMessageTestingOnlyResponse>> OpenApiFormUrlEncoded(
OpenApiPostFormUrlEncodedTestingOnlyRequest request, CancellationToken cancellationToken)
public async Task<ApiPostResult<string, OpenApiTestingOnlyResponse>> OpenApiMultiPartForm(
OpenApiPostMultiPartFormDataTestingOnlyRequest request, CancellationToken cancellationToken)
{
await Task.CompletedTask;
return () =>
new PostResult<StringMessageTestingOnlyResponse>(
new StringMessageTestingOnlyResponse { Message = $"amessage{request.RequiredField}" },
new PostResult<OpenApiTestingOnlyResponse>(
new OpenApiTestingOnlyResponse { ARequiredField = "", Message = $"amessage{request.RequiredField}" },
"alocation");
}

public async Task<ApiPostResult<string, StringMessageTestingOnlyResponse>> OpenApiPost(
public async Task<ApiPostResult<string, OpenApiTestingOnlyResponse>> OpenApiPost(
OpenApiPostTestingOnlyRequest request, CancellationToken cancellationToken)
{
await Task.CompletedTask;
return () =>
new PostResult<StringMessageTestingOnlyResponse>(
new StringMessageTestingOnlyResponse { Message = $"amessage{request.RequiredField}" },
new PostResult<OpenApiTestingOnlyResponse>(
new OpenApiTestingOnlyResponse { ARequiredField = "", Message = $"amessage{request.RequiredField}" },
"alocation");
}

public async Task<ApiPutPatchResult<string, StringMessageTestingOnlyResponse>> OpenApiPut(
public async Task<ApiPutPatchResult<string, OpenApiTestingOnlyResponse>> OpenApiPut(
OpenApiPutTestingOnlyRequest request, CancellationToken cancellationToken)
{
await Task.CompletedTask;
return () =>
new StringMessageTestingOnlyResponse { Message = $"amessage{request.RequiredField}" };
new OpenApiTestingOnlyResponse { ARequiredField = "", Message = $"amessage{request.RequiredField}" };
}

public async Task<ApiEmptyResult> PostInsecure(
Expand Down Expand Up @@ -284,7 +285,7 @@ public async Task<ApiSearchResult<string, StatusesTestingOnlySearchResponse>> St
await Task.CompletedTask;
return () =>
new Result<StatusesTestingOnlySearchResponse, Error>(new StatusesTestingOnlySearchResponse
{ Messages = new List<string> { "amessage" } });
{ Messages = ["amessage"], Metadata = new SearchResultMetadata() });
}

public async Task<ApiResult<string, StringMessageTestingOnlyResponse>> ValidationsUnvalidated(
Expand Down
12 changes: 6 additions & 6 deletions src/BookingsInfrastructure.IntegrationTests/BookingsApiSpec.cs
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ public async Task WhenMakeBooking_ThenReturnsBooking()
EndUtc = end
}, req => req.SetJWTBearerToken(login.AccessToken));

var booking = result.Content.Value.Booking!;
var booking = result.Content.Value.Booking;
var location = result.Headers.Location?.ToString();
location.Should().BeNull();
booking.Id.Should().NotBeEmpty();
Expand All @@ -60,12 +60,12 @@ public async Task WhenSearchAllBookings_ThenReturnsBookings()
CarId = car.Id,
StartUtc = start,
EndUtc = end
}, req => req.SetJWTBearerToken(login.AccessToken))).Content.Value.Booking!;
}, req => req.SetJWTBearerToken(login.AccessToken))).Content.Value.Booking;

var result =
await Api.GetAsync(new SearchAllBookingsRequest(), req => req.SetJWTBearerToken(login.AccessToken));

var bookings = result.Content.Value.Bookings!;
var bookings = result.Content.Value.Bookings;
bookings.Count.Should().Be(1);
bookings[0].Id.Should().Be(booking.Id);
bookings[0].BorrowerId.Should().Be(login.User.Id);
Expand All @@ -87,7 +87,7 @@ public async Task WhenCancelBooking_ThenRemovesUnavailability()
CarId = car.Id,
StartUtc = start,
EndUtc = end
}, req => req.SetJWTBearerToken(login.AccessToken))).Content.Value.Booking!;
}, req => req.SetJWTBearerToken(login.AccessToken))).Content.Value.Booking;

await Api.DeleteAsync(new CancelBookingRequest
{
Expand All @@ -98,7 +98,7 @@ await Api.DeleteAsync(new CancelBookingRequest
var unavailabilities = (await Api.GetAsync(new SearchAllCarUnavailabilitiesRequest
{
Id = car.Id
}, req => req.SetJWTBearerToken(login.AccessToken))).Content.Value.Unavailabilities!;
}, req => req.SetJWTBearerToken(login.AccessToken))).Content.Value.Unavailabilities;

unavailabilities.Count.Should().Be(0);
#endif
Expand All @@ -120,6 +120,6 @@ private async Task<Car> RegisterNewCarAsync(LoginDetails login)
NumberPlate = "aplate"
}, req => req.SetJWTBearerToken(login.AccessToken));

return car.Content.Value.Car!;
return car.Content.Value.Car;
}
}
Loading

0 comments on commit e2c3c27

Please sign in to comment.