Skip to content

Commit 26ecfe8

Browse files
authored
Merge pull request #1572 from bcgov/stories/ecer-5118
Stories/ecer 5118: Status endpoint for ICRA eligibility application
2 parents 96db3d9 + c0a99de commit 26ecfe8

File tree

6 files changed

+188
-4
lines changed

6 files changed

+188
-4
lines changed

src/ECER.Clients.RegistryPortal/ECER.Clients.RegistryPortal.Server/ICRA/ICRAEligibilitiesEndpoints.cs

Lines changed: 39 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -89,6 +89,32 @@ public void Register(IEndpointRouteBuilder endpointRouteBuilder)
8989
.WithOpenApi("Submit an ICRA Eligibility", string.Empty, "icra_post")
9090
.RequireAuthorization()
9191
.WithParameterValidation();
92+
93+
endpointRouteBuilder.MapGet("/api/icra/{id}/status", async Task<Results<Ok<ICRAEligibilityStatus>, BadRequest<ProblemDetails>, NotFound<ProblemDetails>>> (string id, HttpContext ctx, IMediator messageBus, IMapper mapper, CancellationToken ct) =>
94+
{
95+
var userId = ctx.User.GetUserContext()?.UserId;
96+
bool IdIsNotGuid = !Guid.TryParse(id, out _);
97+
if (IdIsNotGuid)
98+
{
99+
return TypedResults.BadRequest(new ProblemDetails() { Title = "ICRAEligibilityId is not valid" });
100+
}
101+
102+
var query = new ICRAEligibilitiesQuery
103+
{
104+
ById = id,
105+
ByApplicantId = userId
106+
};
107+
var result = await messageBus.Send(query, ct);
108+
var eligibility = result.Items.FirstOrDefault();
109+
if (eligibility == null)
110+
{
111+
return TypedResults.NotFound(new ProblemDetails() { Title = "ICRA eligibility not found" });
112+
}
113+
return TypedResults.Ok(mapper.Map<ICRAEligibilityStatus>(eligibility));
114+
})
115+
.WithOpenApi("Handles icra eligibility status queries", string.Empty, "icra_status_get")
116+
.RequireAuthorization()
117+
.WithParameterValidation();
92118
}
93119
}
94120

@@ -103,10 +129,8 @@ public record ICRAEligibility()
103129
public string? Id { get; set; }
104130
public string ApplicantId { get; set; } = string.Empty;
105131
public string? PortalStage { get; set; }
106-
107132
public DateTime? SignedDate { get; set; }
108133
public DateTime? CreatedOn { get; set; }
109-
110134
public ICRAStatus Status { get; set; }
111135
public IEnumerable<InternationalCertification> InternationalCertifications { get; set; } = Array.Empty<InternationalCertification>();
112136
public IEnumerable<EmploymentReference> EmploymentReferences { get; set; } = Array.Empty<EmploymentReference>();
@@ -146,6 +170,7 @@ public record EmploymentReference
146170
public string? FirstName { get; set; }
147171
public string? EmailAddress { get; set; }
148172
public string? PhoneNumber { get; set; }
173+
public ICRAStatus? Status { get; set; }
149174
}
150175

151176
public enum ICRAStatus
@@ -163,3 +188,15 @@ public enum ICRAStatus
163188

164189
public record ICRAEligibilitySubmissionRequest(string Id);
165190
public record SubmitICRAEligibilityResponse(ICRAEligibility Eligibility);
191+
192+
public record ICRAEligibilityStatus(string Id, DateTime? CreatedOn, DateTime? SignedDate, ICRAStatus Status)
193+
{
194+
public IEnumerable<InternationalCertification> InternationalCertifications { get; set; } = Array.Empty<InternationalCertification>();
195+
public IEnumerable<EmploymentReferenceStatus> EmploymentReferencesStatus { get; set; } = Array.Empty<EmploymentReferenceStatus>();
196+
}
197+
198+
public record EmploymentReferenceStatus(string Id, Managers.Registry.Contract.Applications.WorkExperienceRefStage? Status, string? FirstName, string? LastName, string? EmailAddress)
199+
{
200+
public string? PhoneNumber { get; set; }
201+
public bool? WillProvideReference { get; set; }
202+
}

src/ECER.Clients.RegistryPortal/ECER.Clients.RegistryPortal.Server/ICRA/ICRAEligibilityMapper.cs

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,19 @@ public ICRAEligibilityMapper()
1414
.ReverseMap();
1515
CreateMap<EmploymentReference, Managers.Registry.Contract.ICRA.EmploymentReference>()
1616
.ForMember(dest => dest.Type, opt => opt.Ignore())
17+
.ForMember(d => d.Status, o => o.Ignore())
18+
.ForMember(d => d.WillProvideReference, o => o.Ignore())
1719
.ReverseMap();
20+
CreateMap<Managers.Registry.Contract.ICRA.ICRAEligibility, ICRAEligibilityStatus>()
21+
.ForMember(d => d.EmploymentReferencesStatus, o => o.MapFrom(s => s.EmploymentReferences));
22+
CreateMap<Managers.Registry.Contract.ICRA.EmploymentReference, EmploymentReferenceStatus>()
23+
.ForCtorParam(nameof(EmploymentReference.Id), o => o.MapFrom(s => s.Id))
24+
.ForCtorParam(nameof(EmploymentReference.Status), o => o.MapFrom(s => s.Status))
25+
.ForCtorParam(nameof(EmploymentReference.FirstName), o => o.MapFrom(s => s.FirstName))
26+
.ForCtorParam(nameof(EmploymentReference.LastName), o => o.MapFrom(s => s.LastName))
27+
.ForCtorParam(nameof(EmploymentReference.EmailAddress), o => o.MapFrom(s => s.EmailAddress))
28+
.ForMember(d => d.PhoneNumber, o => o.MapFrom(s => s.PhoneNumber))
29+
.ForMember(d => d.WillProvideReference, o => o.MapFrom(s => s.WillProvideReference));
30+
1831
}
1932
}

src/ECER.Managers.Registry.Contract/ICRA/Contract.cs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -70,6 +70,8 @@ public record EmploymentReference
7070
public string? FirstName { get; set; }
7171
public string? EmailAddress { get; set; }
7272
public string? PhoneNumber { get; set; }
73+
public Applications.WorkExperienceRefStage? Status { get; set; }
74+
public bool? WillProvideReference { get; set; }
7375
public WorkExperienceTypesIcra Type { get; set; }
7476
}
7577

src/ECER.Resources.Documents/ICRA/ICRARepositoryMapper.cs

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -71,7 +71,9 @@ public ICRARepositoryMapper()
7171
.ConvertUsingEnumMapping(o => o.MapByName(true));
7272

7373
CreateMap<EmploymentReference, ecer_WorkExperienceRef>(MemberList.Source)
74-
.ForMember(d => d.ecer_WorkExperienceRefId, opts => opts.MapFrom(s => string.IsNullOrEmpty(s.Id) ? null : s.Id))
74+
.ForSourceMember(s => s.Status, opts => opts.DoNotValidate())
75+
.ForSourceMember(s => s.WillProvideReference, opts => opts.DoNotValidate())
76+
.ForMember(d => d.ecer_WorkExperienceRefId, opts => opts.MapFrom(s => string.IsNullOrEmpty(s.Id)? null : s.Id))
7577
.ForMember(d => d.ecer_FirstName, opts => opts.MapFrom(s => s.FirstName))
7678
.ForMember(d => d.ecer_LastName, opts => opts.MapFrom(s => s.LastName))
7779
.ForMember(d => d.ecer_EmailAddress, opts => opts.MapFrom(s => s.EmailAddress))
@@ -84,7 +86,9 @@ public ICRARepositoryMapper()
8486
.ForMember(d => d.LastName, opts => opts.MapFrom(s => s.ecer_LastName))
8587
.ForMember(d => d.EmailAddress, opts => opts.MapFrom(s => s.ecer_EmailAddress))
8688
.ForMember(d => d.PhoneNumber, opts => opts.MapFrom(s => s.ecer_PhoneNumber))
87-
.ForMember(d => d.Type, opts => opts.MapFrom(s => s.ecer_Type));
89+
.ForMember(d => d.Status, opts => opts.MapFrom(s => s.StatusCode))
90+
.ForMember(d => d.Type, opts => opts.MapFrom(s => s.ecer_Type))
91+
.ForMember(d => d.WillProvideReference, opts => opts.MapFrom(s => s.ecer_WillProvideReference.HasValue ? s.ecer_WillProvideReference.Equals(ecer_YesNoNull.Yes) : default(bool?)));
8892

8993
CreateMap<ICRAWorkExperienceReferenceSubmissionRequest, ecer_WorkExperienceRef>(MemberList.Source)
9094
.ForSourceMember(s => s.CountryId, opts => opts.DoNotValidate())
@@ -103,6 +107,7 @@ public ICRARepositoryMapper()
103107
.ForMember(d => d.ecer_RelationshiptoApplicant, opts => opts.MapFrom(s => s.ReferenceRelationship))
104108
.ForMember(d => d.ecer_WillProvideReference, opts => opts.MapFrom(s => s.WillProvideReference ? ecer_YesNoNull.Yes : ecer_YesNoNull.No))
105109
.ForMember(d => d.ecer_DateSigned, opts => opts.MapFrom(s => s.DateSigned));
110+
106111
}
107112

108113
public static string IdOrEmpty(EntityReference? reference) =>

src/ECER.Resources.Documents/ICRA/IICRARepository.cs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -62,7 +62,10 @@ public record EmploymentReference
6262
public string? FirstName { get; set; }
6363
public string? EmailAddress { get; set; }
6464
public string? PhoneNumber { get; set; }
65+
public WorkExperienceRefStage? Status { get; set; }
66+
public bool? WillProvideReference { get; set; }
6567
public WorkExperienceTypesIcra Type { get; set; }
68+
6669
}
6770

6871
public record ICRAWorkExperienceReferenceSubmissionRequest

src/ECER.Tests/Integration/RegistryApi/IcraTests.cs

Lines changed: 124 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -145,6 +145,130 @@ await Host.Scenario(_ =>
145145
});
146146
}
147147

148+
[Fact]
149+
public async Task GetIcraEligibilityStatus_BadId_ReturnsBadRequest()
150+
{
151+
await Host.Scenario(_ =>
152+
{
153+
_.WithExistingUser(this.Fixture.AuthenticatedBcscUserIdentity, this.Fixture.AuthenticatedBcscUser);
154+
_.Get.Url($"/api/icra/not-a-guid/status");
155+
_.StatusCodeShouldBe(HttpStatusCode.BadRequest);
156+
});
157+
}
158+
159+
[Fact]
160+
public async Task GetIcraEligibilityStatus_NotFound_ReturnsNotFound()
161+
{
162+
var invalidId = Guid.NewGuid().ToString();
163+
await Host.Scenario(_ =>
164+
{
165+
_.WithExistingUser(this.Fixture.AuthenticatedBcscUserIdentity, this.Fixture.AuthenticatedBcscUser);
166+
_.Get.Url($"/api/icra/{invalidId}/status");
167+
_.StatusCodeShouldBe(HttpStatusCode.NotFound);
168+
});
169+
}
170+
171+
[Fact]
172+
public async Task GetIcraEligibilityStatus_ReturnsExpectedFields()
173+
{
174+
var eligibility = new Clients.RegistryPortal.Server.ICRA.ICRAEligibility
175+
{
176+
ApplicantId = this.Fixture.AuthenticatedBcscUser.Id.ToString(),
177+
Status = Clients.RegistryPortal.Server.ICRA.ICRAStatus.Draft,
178+
InternationalCertifications = new List<Clients.RegistryPortal.Server.ICRA.InternationalCertification>
179+
{
180+
new Clients.RegistryPortal.Server.ICRA.InternationalCertification
181+
{
182+
CertificateStatus = Clients.RegistryPortal.Server.ICRA.CertificateStatus.Valid,
183+
CertificateTitle = faker.Company.CatchPhrase(),
184+
IssueDate = faker.Date.Past(),
185+
ExpiryDate = faker.Date.Soon(),
186+
CountryId = this.Fixture.Country.ecer_CountryId!.Value.ToString(),
187+
}
188+
}
189+
};
190+
191+
var saveResponse = await Host.Scenario(_ =>
192+
{
193+
_.WithExistingUser(this.Fixture.AuthenticatedBcscUserIdentity, this.Fixture.AuthenticatedBcscUser);
194+
_.Put.Json(new SaveDraftICRAEligibilityRequest(eligibility)).ToUrl($"/api/icra/");
195+
_.StatusCodeShouldBeOk();
196+
});
197+
198+
var saved = (await saveResponse.ReadAsJsonAsync<DraftICRAEligibilityResponse>()).ShouldNotBeNull().Eligibility;
199+
200+
var statusResponse = await Host.Scenario(_ =>
201+
{
202+
_.WithExistingUser(this.Fixture.AuthenticatedBcscUserIdentity, this.Fixture.AuthenticatedBcscUser);
203+
_.Get.Url($"/api/icra/{saved.Id}/status");
204+
_.StatusCodeShouldBeOk();
205+
});
206+
207+
var status = await statusResponse.ReadAsJsonAsync<Clients.RegistryPortal.Server.ICRA.ICRAEligibilityStatus>();
208+
status.ShouldNotBeNull();
209+
status.Id.ShouldBe(saved.Id);
210+
status.Status.ShouldBe(Clients.RegistryPortal.Server.ICRA.ICRAStatus.Draft);
211+
status.InternationalCertifications.ShouldNotBeNull();
212+
status.InternationalCertifications.Count().ShouldBe(1);
213+
214+
// employment references status should be present and empty (none added yet)
215+
status.EmploymentReferencesStatus.ShouldNotBeNull();
216+
status.EmploymentReferencesStatus.Count().ShouldBe(0);
217+
218+
await SetEligibilityToIneligible(saved.Id!);
219+
}
220+
221+
[Fact]
222+
public async Task GetIcraEligibilityStatus_IncludesEmploymentReferencesStatuses()
223+
{
224+
var eligibility = new Clients.RegistryPortal.Server.ICRA.ICRAEligibility
225+
{
226+
ApplicantId = this.Fixture.AuthenticatedBcscUser.Id.ToString(),
227+
Status = Clients.RegistryPortal.Server.ICRA.ICRAStatus.Draft,
228+
EmploymentReferences = new[]
229+
{
230+
new Clients.RegistryPortal.Server.ICRA.EmploymentReference { FirstName = "John", LastName = "Doe", EmailAddress = "[email protected]" },
231+
new Clients.RegistryPortal.Server.ICRA.EmploymentReference { FirstName = "Jane", LastName = "Smith", EmailAddress = "[email protected]" }
232+
},
233+
InternationalCertifications = new List<Clients.RegistryPortal.Server.ICRA.InternationalCertification>
234+
{
235+
new Clients.RegistryPortal.Server.ICRA.InternationalCertification
236+
{
237+
CertificateStatus = Clients.RegistryPortal.Server.ICRA.CertificateStatus.Valid,
238+
CertificateTitle = faker.Company.CatchPhrase(),
239+
IssueDate = faker.Date.Past(),
240+
ExpiryDate = faker.Date.Soon(),
241+
CountryId = this.Fixture.Country.ecer_CountryId!.Value.ToString(),
242+
}
243+
}
244+
};
245+
246+
var saveResponse = await Host.Scenario(_ =>
247+
{
248+
_.WithExistingUser(this.Fixture.AuthenticatedBcscUserIdentity, this.Fixture.AuthenticatedBcscUser);
249+
_.Put.Json(new SaveDraftICRAEligibilityRequest(eligibility)).ToUrl($"/api/icra/");
250+
_.StatusCodeShouldBeOk();
251+
});
252+
253+
var saved = (await saveResponse.ReadAsJsonAsync<DraftICRAEligibilityResponse>()).ShouldNotBeNull().Eligibility;
254+
255+
var statusResponse = await Host.Scenario(_ =>
256+
{
257+
_.WithExistingUser(this.Fixture.AuthenticatedBcscUserIdentity, this.Fixture.AuthenticatedBcscUser);
258+
_.Get.Url($"/api/icra/{saved.Id}/status");
259+
_.StatusCodeShouldBeOk();
260+
});
261+
262+
var status = await statusResponse.ReadAsJsonAsync<Clients.RegistryPortal.Server.ICRA.ICRAEligibilityStatus>();
263+
status.ShouldNotBeNull();
264+
status.EmploymentReferencesStatus.ShouldNotBeNull();
265+
status.EmploymentReferencesStatus.Count().ShouldBe(2);
266+
status.EmploymentReferencesStatus.All(r => !string.IsNullOrWhiteSpace(r.FirstName)).ShouldBeTrue();
267+
status.EmploymentReferencesStatus.All(r => r.Status.HasValue).ShouldBeTrue();
268+
269+
await SetEligibilityToIneligible(saved.Id!);
270+
}
271+
148272
[Fact]
149273
public async Task SaveDraftIcraEligibility_WithMismatchedIds_ReturnsBadRequest()
150274
{

0 commit comments

Comments
 (0)