From b63015929c20a5340166b32fef9d29cda6f17a44 Mon Sep 17 00:00:00 2001 From: Oliver Scott Date: Fri, 17 Nov 2023 15:07:06 +0000 Subject: [PATCH 1/5] Fix v2 toggle issues --- .../src/app/services/clients/api-client.ts | 108 ++++++++++++++++++ .../src/app/services/search.service.ts | 2 +- .../JudiciaryAccountsController.cs | 40 +++++++ .../Mappers/JudgeResponseMapper.cs | 12 ++ 4 files changed, 161 insertions(+), 1 deletion(-) diff --git a/AdminWebsite/AdminWebsite/ClientApp/src/app/services/clients/api-client.ts b/AdminWebsite/AdminWebsite/ClientApp/src/app/services/clients/api-client.ts index 7d82b0903..ad36cf16b 100644 --- a/AdminWebsite/AdminWebsite/ClientApp/src/app/services/clients/api-client.ts +++ b/AdminWebsite/AdminWebsite/ClientApp/src/app/services/clients/api-client.ts @@ -1867,6 +1867,114 @@ export class BHClient extends ApiClientBase { return _observableOf(null as any); } + /** + * Find judges and court rooms accounts list by email search term. + * @param body (optional) The email address search term. + * @return Success + */ + postJudgesBySearchTerm(body: string | undefined): Observable { + let url_ = this.baseUrl + '/api/judiciary/judges'; + url_ = url_.replace(/[?&]$/, ''); + + const content_ = JSON.stringify(body); + + let options_: any = { + body: content_, + observe: 'response', + responseType: 'blob', + headers: new HttpHeaders({ + 'Content-Type': 'application/json-patch+json', + Accept: 'application/json' + }) + }; + + return _observableFrom(this.transformOptions(options_)) + .pipe( + _observableMergeMap(transformedOptions_ => { + return this.http.request('post', url_, transformedOptions_); + }) + ) + .pipe( + _observableMergeMap((response_: any) => { + return this.processPostJudgesBySearchTerm(response_); + }) + ) + .pipe( + _observableCatch((response_: any) => { + if (response_ instanceof HttpResponseBase) { + try { + return this.processPostJudgesBySearchTerm(response_ as any); + } catch (e) { + return _observableThrow(e) as any as Observable; + } + } else return _observableThrow(response_) as any as Observable; + }) + ); + } + + protected processPostJudgesBySearchTerm(response: HttpResponseBase): Observable { + const status = response.status; + const responseBlob = + response instanceof HttpResponse + ? response.body + : (response as any).error instanceof Blob + ? (response as any).error + : undefined; + + let _headers: any = {}; + if (response.headers) { + for (let key of response.headers.keys()) { + _headers[key] = response.headers.get(key); + } + } + if (status === 500) { + return blobToText(responseBlob).pipe( + _observableMergeMap((_responseText: string) => { + let result500: any = null; + let resultData500 = _responseText === '' ? null : JSON.parse(_responseText, this.jsonParseReviver); + result500 = UnexpectedErrorResponse.fromJS(resultData500); + return throwException('Server Error', status, _responseText, _headers, result500); + }) + ); + } else if (status === 200) { + return blobToText(responseBlob).pipe( + _observableMergeMap((_responseText: string) => { + let result200: any = null; + let resultData200 = _responseText === '' ? null : JSON.parse(_responseText, this.jsonParseReviver); + if (Array.isArray(resultData200)) { + result200 = [] as any; + for (let item of resultData200) result200!.push(JudgeResponse.fromJS(item)); + } else { + result200 = null; + } + return _observableOf(result200); + }) + ); + } else if (status === 400) { + return blobToText(responseBlob).pipe( + _observableMergeMap((_responseText: string) => { + let result400: any = null; + let resultData400 = _responseText === '' ? null : JSON.parse(_responseText, this.jsonParseReviver); + result400 = ProblemDetails.fromJS(resultData400); + return throwException('Bad Request', status, _responseText, _headers, result400); + }) + ); + } else if (status === 401) { + return blobToText(responseBlob).pipe( + _observableMergeMap((_responseText: string) => { + return throwException('Unauthorized', status, _responseText, _headers); + }) + ); + } else if (status !== 200 && status !== 204) { + return blobToText(responseBlob).pipe( + _observableMergeMap((_responseText: string) => { + return throwException('An unexpected server error occurred.', status, _responseText, _headers); + }) + ); + } + return _observableOf(null as any); + } + /** * Find judiciary person list by email search term. * @param body (optional) The email address search term. diff --git a/AdminWebsite/AdminWebsite/ClientApp/src/app/services/search.service.ts b/AdminWebsite/AdminWebsite/ClientApp/src/app/services/search.service.ts index 9ad11800e..3dc39637a 100644 --- a/AdminWebsite/AdminWebsite/ClientApp/src/app/services/search.service.ts +++ b/AdminWebsite/AdminWebsite/ClientApp/src/app/services/search.service.ts @@ -102,6 +102,6 @@ export class SearchService { } searchJudgeAccounts(term): Observable> { - return this.bhClient.postJudiciaryPersonBySearchTerm(term); + return this.bhClient.postJudgesBySearchTerm(term); } } diff --git a/AdminWebsite/AdminWebsite/Controllers/JudiciaryAccountsController.cs b/AdminWebsite/AdminWebsite/Controllers/JudiciaryAccountsController.cs index 4c4642470..6eb0a4045 100644 --- a/AdminWebsite/AdminWebsite/Controllers/JudiciaryAccountsController.cs +++ b/AdminWebsite/AdminWebsite/Controllers/JudiciaryAccountsController.cs @@ -34,6 +34,46 @@ public JudiciaryAccountsController(IUserAccountService userAccountService, JavaS _bookingsApiClient = bookingsApiClient; _testSettings = testSettings.Value; } + + /// + /// Find judges and court rooms accounts list by email search term. + /// + /// The email address search term. + /// The list of judges + [HttpPost("judges", Name = "PostJudgesBySearchTerm")] + [SwaggerOperation(OperationId = "PostJudgesBySearchTerm")] + [ProducesResponseType(typeof(List), (int)HttpStatusCode.OK)] + [ProducesResponseType((int)HttpStatusCode.BadRequest)] + public async Task>> PostJudgesBySearchTermAsync([FromBody] string term) + { + try + { + term = _encoder.Encode(term); + var searchTerm = new SearchTermRequest(term); + + var courtRoomJudgesTask = _userAccountService.SearchJudgesByEmail(searchTerm.Term); + var eJudiciaryJudgesTask = GetEjudiciaryJudgesBySearchTermAsync(searchTerm); + + await Task.WhenAll(courtRoomJudgesTask, eJudiciaryJudgesTask); + + var courtRoomJudges = await courtRoomJudgesTask; + var eJudiciaryJudges = (await eJudiciaryJudgesTask).Select(x => JudgeResponseMapper.MapTo(x)); + + var allJudges = courtRoomJudges.Concat(eJudiciaryJudges) + .OrderBy(x => x.Email).Take(20).ToList(); + + return Ok(allJudges); + } + catch (BookingsApiException e) + { + if (e.StatusCode == (int)HttpStatusCode.BadRequest) + { + return BadRequest(e.Response); + } + + throw; + } + } /// /// Find judiciary person list by email search term. diff --git a/AdminWebsite/AdminWebsite/Mappers/JudgeResponseMapper.cs b/AdminWebsite/AdminWebsite/Mappers/JudgeResponseMapper.cs index 776c852d3..0a8a0ae4f 100644 --- a/AdminWebsite/AdminWebsite/Mappers/JudgeResponseMapper.cs +++ b/AdminWebsite/AdminWebsite/Mappers/JudgeResponseMapper.cs @@ -18,5 +18,17 @@ public static JudgeResponse MapTo(JudgeResponse personResponse) }; } + + public static JudgeResponse MapTo(PersonResponse personResponse) + { + return new JudgeResponse + { + FirstName = personResponse.FirstName, + LastName = personResponse.LastName, + Email = personResponse.Username, + AccountType = JudgeAccountType.Judiciary, + ContactEmail = personResponse.ContactEmail + }; + } } } From 9637c8578ae1a6727f613c842d2ede96d4d89f6b Mon Sep 17 00:00:00 2001 From: Oliver Scott Date: Fri, 17 Nov 2023 15:22:41 +0000 Subject: [PATCH 2/5] Reinstate api tests --- .../JudiciaryAccountsControllerTest.cs | 95 ++++++++++++++++++- 1 file changed, 94 insertions(+), 1 deletion(-) diff --git a/AdminWebsite/AdminWebsite.UnitTests/Controllers/JudiciaryAccountsControllerTest.cs b/AdminWebsite/AdminWebsite.UnitTests/Controllers/JudiciaryAccountsControllerTest.cs index cb0d776e3..5a2500bea 100644 --- a/AdminWebsite/AdminWebsite.UnitTests/Controllers/JudiciaryAccountsControllerTest.cs +++ b/AdminWebsite/AdminWebsite.UnitTests/Controllers/JudiciaryAccountsControllerTest.cs @@ -1,4 +1,5 @@ -using AdminWebsite.Configuration; +using System; +using AdminWebsite.Configuration; using AdminWebsite.Contracts.Responses; using AdminWebsite.Services; using AdminWebsite.UnitTests.Helper; @@ -8,6 +9,7 @@ using Moq; using NUnit.Framework; using System.Collections.Generic; +using System.Linq; using System.Net; using System.Text.Encodings.Web; using System.Threading.Tasks; @@ -323,5 +325,96 @@ public void Should_pass_on_exception_request_from_bookings_api_for_judiciary_per Assert.ThrowsAsync(() => _controller.PostJudiciaryPersonBySearchTermAsync(term)); _bookingsApiClient.Verify(x => x.PostJudiciaryPersonBySearchTermAsync(It.IsAny()), Times.Once); } + + [Test] + [TestCase(false, false)] + [TestCase(false, true)] + [TestCase(true, false)] + [TestCase(true, true)] + public async Task PostJudgesBySearchTerm_should_return_judiciary_and_courtroom_accounts_if_match_to_search_term(bool withJudiciary, bool withCourtroom) + { + _judiciaryResponse = new List + { + new() + { + Email = "jackman@judiciary.net", + FirstName = "Jack", + LastName = "Mann", + WorkPhone = "", + Title = "Mr", + FullName = "Mr Jack Mann", + PersonalCode = "12345678" + } + }; + _bookingsApiClient.Setup(x => x.PostJudiciaryPersonBySearchTermAsync(It.IsAny())) + .ReturnsAsync(withJudiciary ? _judiciaryResponse : new List()); + + var _courtRoomResponse = new List + { + new JudgeResponse + { + FirstName = "FirstName1", + LastName = "FirstName2", + Email = "judge.1@judiciary.net", + ContactEmail = "judge1@personal.com" + }, + new JudgeResponse + { + FirstName = "FirstName3", + LastName = "LastName3", + Email = "judge.3@judiciary.net", + ContactEmail = "judge3@personal.com" + }, + new JudgeResponse + { + FirstName = "FirstName2", + LastName = "LastName2", + Email = "judge.2@judiciary.net", // Out of order to test order + ContactEmail = "judge2@personal.com" + } + }; + + _userAccountService.Setup(x => x.SearchJudgesByEmail(It.IsAny())) + .ReturnsAsync(withCourtroom ? _courtRoomResponse : new List()); + + + var searchTerm = "judici"; + var result = await _controller.PostJudgesBySearchTermAsync(searchTerm); + + var okRequestResult = (OkObjectResult)result.Result; + okRequestResult.StatusCode.Should().NotBeNull(); + var personRespList = (List)okRequestResult.Value; + + var expectedJudiciaryCount = withJudiciary ? _judiciaryResponse.Count : 0; + var expectedCourtRoomCount = withCourtroom ? _courtRoomResponse.Count : 0; + + var expectedTotal = expectedJudiciaryCount + expectedCourtRoomCount; + + personRespList.Count.Should().Be(expectedTotal); + if(!withJudiciary && withCourtroom) // Only courtroom is set up to test order + { + Assert.That(personRespList, Is.EquivalentTo(_courtRoomResponse)); + Assert.That(personRespList, Is.Not.EqualTo(_courtRoomResponse)); + Assert.That(personRespList, Is.EqualTo(_courtRoomResponse.OrderBy(x => x.Email))); + } + } + + [Test] + public async Task PostJudgesBySearchTerm_should_pass_on_bad_request_from_bookings_api_for_judge_accounts() + { + _bookingsApiClient.Setup(x => x.PostJudiciaryPersonBySearchTermAsync(It.IsAny())) + .ThrowsAsync(ClientException.ForBookingsAPI(HttpStatusCode.BadRequest)); + + var response = await _controller.PostJudgesBySearchTermAsync("term"); + response.Result.Should().BeOfType(); + } + + [Test] + public void PostJudgesBySearchTerm_should_pass_on_exception_request_from_bookings_api_for_judges_accounts() + { + _bookingsApiClient.Setup(x => x.PostJudiciaryPersonBySearchTermAsync(It.IsAny())) + .ThrowsAsync(ClientException.ForBookingsAPI(HttpStatusCode.InternalServerError)); + Assert.ThrowsAsync(() => _controller.PostJudgesBySearchTermAsync("term")); + } } } \ No newline at end of file From 48d3546c675d487a939c933f599d6cd773270b12 Mon Sep 17 00:00:00 2001 From: Oliver Scott Date: Fri, 17 Nov 2023 15:40:00 +0000 Subject: [PATCH 3/5] Update search service --- .../ClientApp/src/app/services/search.service.spec.ts | 8 ++++---- .../ClientApp/src/app/services/search.service.ts | 2 +- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/AdminWebsite/AdminWebsite/ClientApp/src/app/services/search.service.spec.ts b/AdminWebsite/AdminWebsite/ClientApp/src/app/services/search.service.spec.ts index 37ba2896e..feb116b53 100644 --- a/AdminWebsite/AdminWebsite/ClientApp/src/app/services/search.service.spec.ts +++ b/AdminWebsite/AdminWebsite/ClientApp/src/app/services/search.service.spec.ts @@ -136,15 +136,15 @@ describe('SearchService', () => { beforeEach(() => { clientApiSpy = jasmine.createSpyObj('BHClient', [ 'postPersonBySearchTerm', - 'searchForJudiciaryPerson', 'postJudiciaryPersonBySearchTerm', + 'postJudgesBySearchTerm', 'getStaffMembersBySearchTerm' ]); clientApiSpy.postPersonBySearchTerm.and.returnValue(of(personList)); clientApiSpy.getStaffMembersBySearchTerm.and.returnValue(of(staffMemberList)); - clientApiSpy.searchForJudiciaryPerson.and.returnValue(of(judiciaryPersonList)); - clientApiSpy.postJudiciaryPersonBySearchTerm.and.returnValue(of(judgeList)); + clientApiSpy.postJudiciaryPersonBySearchTerm.and.returnValue(of(judiciaryPersonList)); + clientApiSpy.postJudgesBySearchTerm.and.returnValue(of(judgeList)); launchDarklyServiceSpy.getFlag.withArgs(FeatureFlags.eJudFeature).and.returnValue(of(true)); @@ -166,8 +166,8 @@ describe('SearchService', () => { beforeEach(() => { clientApiSpy = jasmine.createSpyObj('BHClient', [ 'postPersonBySearchTerm', - 'searchForJudiciaryPerson', 'postJudiciaryPersonBySearchTerm', + 'postJudgesBySearchTerm', 'getStaffMembersBySearchTerm' ]); diff --git a/AdminWebsite/AdminWebsite/ClientApp/src/app/services/search.service.ts b/AdminWebsite/AdminWebsite/ClientApp/src/app/services/search.service.ts index 3dc39637a..05c79cdbf 100644 --- a/AdminWebsite/AdminWebsite/ClientApp/src/app/services/search.service.ts +++ b/AdminWebsite/AdminWebsite/ClientApp/src/app/services/search.service.ts @@ -94,7 +94,7 @@ export class SearchService { } searchJudiciaryEntries(term): Observable> { - return this.bhClient.searchForJudiciaryPerson(term); + return this.bhClient.postJudiciaryPersonBySearchTerm(term); } searchStaffMemberAccounts(term): Observable> { From e244341e5cace519857bfd9889baf2b80d42124a Mon Sep 17 00:00:00 2001 From: William Craig Date: Mon, 20 Nov 2023 16:08:08 +0000 Subject: [PATCH 4/5] Added null coalesc for non populated johs --- .../ClientApp/src/app/services/booking-details.service.ts | 2 +- .../ClientApp/src/app/services/video-hearings.service.ts | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/AdminWebsite/AdminWebsite/ClientApp/src/app/services/booking-details.service.ts b/AdminWebsite/AdminWebsite/ClientApp/src/app/services/booking-details.service.ts index 1fd27317b..5be767032 100644 --- a/AdminWebsite/AdminWebsite/ClientApp/src/app/services/booking-details.service.ts +++ b/AdminWebsite/AdminWebsite/ClientApp/src/app/services/booking-details.service.ts @@ -47,7 +47,7 @@ export class BookingDetailsService { const judges: Array = []; const judicialMembers: Array = []; - const mappedJohs = hearingResponse.judiciary_participants.map( + const mappedJohs = hearingResponse.judiciary_participants?.map( j => new JudiciaryParticipantDetailsModel( j.title, diff --git a/AdminWebsite/AdminWebsite/ClientApp/src/app/services/video-hearings.service.ts b/AdminWebsite/AdminWebsite/ClientApp/src/app/services/video-hearings.service.ts index 3dc4af6c5..5b921913b 100644 --- a/AdminWebsite/AdminWebsite/ClientApp/src/app/services/video-hearings.service.ts +++ b/AdminWebsite/AdminWebsite/ClientApp/src/app/services/video-hearings.service.ts @@ -326,7 +326,7 @@ export class VideoHearingsService { hearing.status = response.status; hearing.audio_recording_required = response.audio_recording_required; hearing.endpoints = this.mapEndpointResponseToEndpointModel(response.endpoints, response.participants); - hearing.judiciaryParticipants = response.judiciary_participants.map(judiciaryParticipant => + hearing.judiciaryParticipants = response.judiciary_participants?.map(judiciaryParticipant => JudicialMemberDto.fromJudiciaryParticipantResponse(judiciaryParticipant) ); hearing.isConfirmed = Boolean(response.confirmed_date); From db65c7b27287dcfdd2fd2fade3b6cd860bd88258 Mon Sep 17 00:00:00 2001 From: William Craig Date: Mon, 20 Nov 2023 17:36:50 +0000 Subject: [PATCH 5/5] Check fo null judicary participant before mapping --- .../ClientApp/src/app/services/video-hearings.service.ts | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/AdminWebsite/AdminWebsite/ClientApp/src/app/services/video-hearings.service.ts b/AdminWebsite/AdminWebsite/ClientApp/src/app/services/video-hearings.service.ts index 5b921913b..fb85fe813 100644 --- a/AdminWebsite/AdminWebsite/ClientApp/src/app/services/video-hearings.service.ts +++ b/AdminWebsite/AdminWebsite/ClientApp/src/app/services/video-hearings.service.ts @@ -217,9 +217,11 @@ export class VideoHearingsService { hearing.scheduled_date_time = new Date(booking.scheduled_date_time); hearing.scheduled_duration = booking.scheduled_duration; hearing.participants = this.mapParticipantModelToEditParticipantRequest(booking.participants); - hearing.judiciary_participants = this.mapJudicialMemberDtoToJudiciaryParticipantRequest(booking.judiciaryParticipants); hearing.audio_recording_required = booking.audio_recording_required; hearing.endpoints = this.mapEndpointModelToEditEndpointRequest(booking.endpoints); + if (booking.judiciaryParticipants?.length > 0) { + hearing.judiciary_participants = this.mapJudicialMemberDtoToJudiciaryParticipantRequest(booking.judiciaryParticipants); + } return hearing; }