Skip to content

Commit

Permalink
Add prison api builder to test memory (#202)
Browse files Browse the repository at this point in the history
  • Loading branch information
danielburnley authored Jun 21, 2023
1 parent 7717a51 commit 04caac5
Show file tree
Hide file tree
Showing 5 changed files with 327 additions and 5 deletions.
18 changes: 18 additions & 0 deletions server/data/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,19 +3,37 @@
* Do appinsights first as it does some magic instrumentation work, i.e. it affects other 'require's
* In particular, applicationinsights automatically collects bunyan logs
*/
import config, { ApiConfig } from '../config'
import { buildAppInsightsClient, initialiseAppInsights } from '../utils/azureAppInsights'
import HmppsAuthClient from './hmppsAuthClient'
import { PrisonApiClient } from './interfaces/prisonApiClient'
import PrisonApiRestClientTwo from './prisonApiClientTwo'

import { createRedisClient } from './redisClient'
import RestClient, { RestClientBuilder as CreateRestClientBuilder } from './restClient'
import TokenStore from './tokenStore'

initialiseAppInsights()
buildAppInsightsClient()

type RestClientBuilder<T> = (token: string) => T

export default function restClientBuilder<T>(
name: string,
options: ApiConfig,
constructor: new (client: RestClient) => T,
): RestClientBuilder<T> {
const restClient = CreateRestClientBuilder(name, options)
return token => new constructor(restClient(token))
}

export const dataAccess = () => ({
hmppsAuthClient: new HmppsAuthClient(new TokenStore(createRedisClient())),
prisonApiClientBuilder: restClientBuilder<PrisonApiClient>(
'Prison API',
config.apis.prisonApi,
PrisonApiRestClientTwo,
),
})

export type DataAccess = ReturnType<typeof dataAccess>
Expand Down
297 changes: 297 additions & 0 deletions server/data/prisonApiClientTwo.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,297 @@
import { Readable } from 'stream'
import config from '../config'
import RestClient from './restClient'
import { CaseLoadsDummyDataA } from './localMockData/caseLoad'
import { CaseLoad } from '../interfaces/caseLoad'
import { NonAssociationDetails } from '../interfaces/nonAssociationDetails'
import nonAssociationDetailsDummyData from './localMockData/nonAssociations'
import { PrisonApiClient } from './interfaces/prisonApiClient'
import { AccountBalances } from '../interfaces/accountBalances'
import { AdjudicationSummary } from '../interfaces/adjudicationSummary'
import { VisitSummary } from '../interfaces/visitSummary'
import { VisitBalances } from '../interfaces/visitBalances'
import { Assessment } from '../interfaces/prisonApi/assessment'
import { ContactDetail } from '../interfaces/staffContacts'
import { mapToQueryString } from '../utils/utils'
import { CaseNote } from '../interfaces/caseNote'
import { ScheduledEvent } from '../interfaces/scheduledEvent'
import dummyScheduledEvents from './localMockData/eventsForToday'
import { PrisonerDetail } from '../interfaces/prisonerDetail'
import { InmateDetail } from '../interfaces/prisonApi/inmateDetail'
import { PersonalCareNeeds } from '../interfaces/personalCareNeeds'
import { OffenderActivitiesHistory } from '../interfaces/offenderActivitiesHistory'
import { OffenderAttendanceHistory } from '../interfaces/offenderAttendanceHistory'
import { SecondaryLanguage } from '../interfaces/prisonApi/secondaryLanguage'
import { PagedListQueryParams, PagedList } from '../interfaces/prisonApi/pagedList'
import { PropertyContainer } from '../interfaces/prisonApi/propertyContainer'
import { CourtCase } from '../interfaces/prisonApi/courtCase'
import { OffenceHistoryDetail } from '../interfaces/prisonApi/offenceHistoryDetail'
import { OffenderSentenceTerms } from '../interfaces/prisonApi/offenderSentenceTerms'
import { PrisonerSentenceDetails } from '../interfaces/prisonerSentenceDetails'
import { Address } from '../interfaces/prisonApi/address'
import { OffenderContacts } from '../interfaces/prisonApi/offenderContacts'
import { ReferenceCode, ReferenceCodeDomain } from '../interfaces/prisonApi/referenceCode'
import { ReasonableAdjustments } from '../interfaces/prisonApi/reasonableAdjustment'
import { CaseNoteUsage } from '../interfaces/prisonApi/caseNoteUsage'
import { formatDateISO } from '../utils/dateHelpers'
import { CaseNoteCount } from '../interfaces/prisonApi/caseNoteCount'
import { CourtDateResults } from '../interfaces/courtDateResults'
import { MainOffence } from '../interfaces/prisonApi/mainOffence'
import { FullStatus } from '../interfaces/prisonApi/fullStatus'
import { SentenceSummary } from '../interfaces/prisonApi/sentenceSummary'
import { OffenderIdentifier } from '../interfaces/prisonApi/offenderIdentifier'
import { StaffRole } from '../interfaces/prisonApi/staffRole'

export default class PrisonApiRestClientTwo implements PrisonApiClient {
constructor(private restClient: RestClient) {}

private async get<T>(args: object, localMockData?: T): Promise<T> {
try {
return await this.restClient.get<T>(args)
} catch (error) {
if (config.localMockData === 'true' && localMockData) {
return localMockData
}
return error
}
}

async getOffenderAttendanceHistory(
prisonerNumber: string,
fromDate: string,
toDate: string,
): Promise<OffenderAttendanceHistory> {
return this.get<OffenderAttendanceHistory>({
path: `/api/offender-activities/${prisonerNumber}/attendance-history?fromDate=${fromDate}&toDate=${toDate}&page=0&size=20`,
})
}

async getUserCaseLoads(): Promise<CaseLoad[]> {
return this.get<CaseLoad[]>({ path: '/api/users/me/caseLoads', query: 'allCaseloads=true' }, CaseLoadsDummyDataA)
}

async getPrisonerImage(offenderNumber: string, fullSizeImage: boolean): Promise<Readable> {
try {
return await this.restClient.stream({
path: `/api/bookings/offenderNo/${offenderNumber}/image/data?fullSizeImage=${fullSizeImage}`,
})
} catch (error) {
return error
}
}

async getNonAssociationDetails(prisonerNumber: string): Promise<NonAssociationDetails> {
return this.get<NonAssociationDetails>(
{ path: `/api/offenders/${prisonerNumber}/non-association-details` },
nonAssociationDetailsDummyData,
)
}

async getAccountBalances(bookingId: number): Promise<AccountBalances> {
return this.get<AccountBalances>({
path: `/api/bookings/${bookingId}/balances`,
})
}

async getAdjudications(bookingId: number): Promise<AdjudicationSummary> {
return this.get<AdjudicationSummary>({ path: `/api/bookings/${bookingId}/adjudications` })
}

async getVisitSummary(bookingId: number): Promise<VisitSummary> {
return this.get<VisitSummary>({ path: `/api/bookings/${bookingId}/visits/summary` })
}

async getVisitBalances(prisonerNumber: string): Promise<VisitBalances> {
return this.get<VisitBalances>({
path: `/api/bookings/offenderNo/${prisonerNumber}/visit/balances`,
})
}

async getAssessments(bookingId: number): Promise<Assessment[]> {
return this.get<Assessment[]>({ path: `/api/bookings/${bookingId}/assessments` })
}

async getEventsScheduledForToday(bookingId: number): Promise<ScheduledEvent[]> {
return this.get<ScheduledEvent[]>(
{
path: `/api/bookings/${bookingId}/events/today`,
},
dummyScheduledEvents,
)
}

async getBookingContacts(bookingId: number): Promise<ContactDetail> {
try {
return await this.restClient.get<ContactDetail>({ path: `/api/bookings/${bookingId}/contacts` })
} catch (error) {
return error
}
}

async getPrisoner(prisonerNumber: string): Promise<PrisonerDetail> {
const prisoner = await this.get<PrisonerDetail>({ path: `/api/prisoners/${prisonerNumber}` })
// API returns array with one entry, so extract this to return a single object
if (Array.isArray(prisoner)) {
return prisoner[0]
}
return prisoner
}

async getCaseNoteSummaryByTypes(params: object): Promise<CaseNote[]> {
try {
return await this.restClient.get<CaseNote[]>({ path: `/api/case-notes/summary?${mapToQueryString(params)}` })
} catch (error) {
return error
}
}

async getInmateDetail(bookingId: number): Promise<InmateDetail> {
return this.get<InmateDetail>({ path: `/api/bookings/${bookingId}` })
}

async getPersonalCareNeeds(bookingId: number, types?: string[]): Promise<PersonalCareNeeds> {
let query
if (types?.length) {
query = `type=${types.join()}`
}
return this.get<PersonalCareNeeds>({ path: `/api/bookings/${bookingId}/personal-care-needs`, query })
}

async getOffenderActivitiesHistory(
prisonerNumber: string,
earliestEndDate: string,
): Promise<OffenderActivitiesHistory> {
try {
return await this.restClient.get<OffenderActivitiesHistory>({
path: `/api/offender-activities/${prisonerNumber}/activities-history?earliestEndDate=${earliestEndDate}`,
})
} catch (error) {
return error
}
}

async getSecondaryLanguages(bookingId: number): Promise<SecondaryLanguage[]> {
return this.get<SecondaryLanguage[]>({ path: `/api/bookings/${bookingId}/secondary-languages` })
}

async getAlerts(bookingId: number, queryParams?: PagedListQueryParams): Promise<PagedList> {
// Set defaults then apply queryParams
const params: PagedListQueryParams = {
size: queryParams?.showAll ? 9999 : 20,
...queryParams,
}
return this.get<PagedList>({ path: `/api/bookings/${bookingId}/alerts/v2`, query: mapToQueryString(params) })
}

async getProperty(bookingId: number): Promise<PropertyContainer[]> {
return this.get<PropertyContainer[]>({ path: `/api/bookings/${bookingId}/property` })
}

async getCourtCases(bookingId: number): Promise<CourtCase[]> {
return this.get<CourtCase[]>({ path: `/api/bookings/${bookingId}/court-cases` })
}

async getOffenceHistory(prisonerNumber: string): Promise<OffenceHistoryDetail[]> {
return this.get<OffenceHistoryDetail[]>({ path: `/api/bookings/offenderNo/${prisonerNumber}/offenceHistory` })
}

async getSentenceTerms(bookingId: number): Promise<OffenderSentenceTerms[]> {
return this.get<OffenderSentenceTerms[]>({
path: `/api/offender-sentences/booking/${bookingId}/sentenceTerms?filterBySentenceTermCodes=IMP&filterBySentenceTermCodes=LIC`,
})
}

async getPrisonerSentenceDetails(prisonerNumber: string): Promise<PrisonerSentenceDetails> {
try {
return this.get<PrisonerSentenceDetails>({ path: `/api/offenders/${prisonerNumber}/sentences` })
} catch (error) {
return error
}
}

async getAddresses(prisonerNumber: string): Promise<Address[]> {
return this.get<Address[]>({ path: `/api/offenders/${prisonerNumber}/addresses` })
}

async getAddressesForPerson(personId: number): Promise<Address[]> {
return this.get<Address[]>({ path: `/api/persons/${personId}/addresses` })
}

async getOffenderContacts(prisonerNumber: string): Promise<OffenderContacts> {
return this.get<OffenderContacts>({ path: `/api/offenders/${prisonerNumber}/contacts` })
}

async getImage(imageId: string, getFullSizedImage: boolean): Promise<Readable> {
try {
return await this.restClient.stream({
path: `/api/images/${imageId}/data?fullSizeImage=${getFullSizedImage}`,
})
} catch (error) {
return error
}
}

async getReferenceCodesByDomain(domain: ReferenceCodeDomain): Promise<ReferenceCode[]> {
return this.get<ReferenceCode[]>({
path: `/api/reference-domains/domains/${domain}`,
headers: { 'page-limit': '1000' },
})
}

async getReasonableAdjustments(bookingId: number, treatmentCodes: string[]): Promise<ReasonableAdjustments> {
return this.get<ReasonableAdjustments>({
path: `/api/bookings/${bookingId}/reasonable-adjustments?type=${treatmentCodes.join()}`,
})
}

async getCaseNotesUsage(prisonerNumber: string): Promise<CaseNoteUsage[]> {
const today = formatDateISO(new Date())
return this.get({
path: `/api/case-notes/usage`,
query: `offenderNo=${prisonerNumber}&toDate=${today}&numMonths=1200`,
})
}

async getCaseNoteCount(
bookingId: number,
type: string,
subType: string,
fromDate: string,
toDate: string,
): Promise<CaseNoteCount> {
return this.get({
path: `/api/bookings/${bookingId}/caseNotes/${type}/${subType}/count`,
query: `fromDate=${fromDate}&toDate=${toDate}`,
})
}

async getMainOffence(bookingId: number): Promise<MainOffence[]> {
return this.get({ path: `/api/bookings/${bookingId}/mainOffence` })
}

async getFullStatus(prisonerNumber: string): Promise<FullStatus> {
return this.get({ path: `/api/prisoners/${prisonerNumber}/full-status` })
}

async getCourtDateResults(prisonerNumber: string): Promise<CourtDateResults[]> {
return this.get<CourtDateResults[]>({
path: `/api/digital-warrant/court-date-results/${prisonerNumber}`,
})
}

async getIdentifiers(bookingId: number): Promise<OffenderIdentifier[]> {
return this.get<OffenderIdentifier[]>({
path: `/api/bookings/${bookingId}/identifiers`,
})
}

async getSentenceSummary(prisonerNumber: string): Promise<SentenceSummary> {
return this.get<SentenceSummary>({
path: `/api/offenders/${prisonerNumber}/booking/latest/sentence-summary`,
})
}

async getStaffRoles(staffId: number, agencyId: string): Promise<StaffRole[]> {
return this.get<StaffRole[]>({ path: `/api/staff/${staffId}/${agencyId}/roles` })
}
}
4 changes: 4 additions & 0 deletions server/data/restClient.ts
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,10 @@ interface StreamRequest {
errorLogger?: (e: UnsanitisedError) => void
}

export function RestClientBuilder(name: string, config: ApiConfig) {
return (token: string): RestClient => new RestClient(name, config, token)
}

export default class RestClient {
agent: Agent

Expand Down
4 changes: 2 additions & 2 deletions server/services/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,9 @@ import PrisonerSearchService from './prisonerSearch'
import UserService from './userService'

export const services = () => {
const { hmppsAuthClient } = dataAccess()
const { hmppsAuthClient, prisonApiClientBuilder } = dataAccess()
const userService = new UserService(hmppsAuthClient)
const offenderService = new OffenderService()
const offenderService = new OffenderService(prisonApiClientBuilder)
const commonApiRoutes = new CommonApiRoutes(offenderService)

return {
Expand Down
9 changes: 6 additions & 3 deletions server/services/offenderService.ts
Original file line number Diff line number Diff line change
@@ -1,12 +1,15 @@
import { Readable } from 'stream'
import PrisonApiClient from '../data/prisonApiClient'
import { RestClientBuilder } from '../data'
import { PrisonApiClient } from '../data/interfaces/prisonApiClient'

export default class OffenderService {
constructor(private readonly prisonClientBuilder: RestClientBuilder<PrisonApiClient>) {}

getPrisonerImage(token: string, offenderNumber: string): Promise<Readable> {
return new PrisonApiClient(token).getPrisonerImage(offenderNumber, true)
return this.prisonClientBuilder(token).getPrisonerImage(offenderNumber, true)
}

getImage(token: string, imageId: string): Promise<Readable> {
return new PrisonApiClient(token).getImage(imageId, true)
return this.prisonClientBuilder(token).getImage(imageId, true)
}
}

0 comments on commit 04caac5

Please sign in to comment.