Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

CDPS-227 Add prison api builder to test memory #202

Merged
merged 1 commit into from
Jun 21, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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)
}
}