From 4f93fdeb8fe16df7ff31cb195caa47fd875c3983 Mon Sep 17 00:00:00 2001 From: Marcus Aspin Date: Wed, 22 Nov 2023 14:17:25 +0000 Subject: [PATCH] PI-1508 Migrate to the new Delius integration API This is part of the move to deprecate Community API. The new API endpoints used in this PR are provided by the dedicated integration service for [HDC Licences and Delius](https://ministryofjustice.github.io/hmpps-probation-integration-services/tech-docs/projects/hdc-licences-and-delius/api-reference.html). The data returned by this API should be the same as before, but please reach out to us on Slack at [#probation-integration-tech](https://moj.enterprise.slack.com/archives/C02HQ4M2YQN) if you see any problems. --- .circleci/config.yml | 2 +- Notes.md | 2 +- README.md | 2 +- docker-compose-full.yml | 2 +- feature.env | 3 +- helm_deploy/licences/templates/_envs.tpl | 3 - helm_deploy/values-dev.yaml | 3 +- helm_deploy/values-preprod.yaml | 3 +- helm_deploy/values-prod.yaml | 3 +- mock-server/app.js | 4 +- mock-server/routes/delius.js | 110 +++++-------- server/config.js | 3 +- server/data/deliusClient.ts | 155 ++++++------------ server/index.js | 4 +- server/services/caseListService.ts | 6 +- server/services/functionalMailboxService.ts | 27 ++- server/services/lduService.js | 9 +- server/services/migrationService.ts | 20 +-- server/services/roService.ts | 50 ++---- server/views/admin/users/migrate.pug | 6 +- test/data/deliusClient.test.js | 83 ++++------ test/routes/contact.test.ts | 11 +- test/services/caseListService.test.ts | 6 +- .../services/functionalMailboxService.test.ts | 63 +++---- test/services/lduService.test.js | 26 +-- test/services/migrationService.test.ts | 44 ++--- test/services/roService.test.ts | 89 ++++------ types/licences.d.ts | 4 +- 28 files changed, 278 insertions(+), 465 deletions(-) diff --git a/.circleci/config.yml b/.circleci/config.yml index ded25735e..948a82a8f 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -128,7 +128,7 @@ jobs: ENABLE_TEST_UTILS: true NOMIS_API_URL: http://localhost:8080/prisonApi NOMIS_AUTH_URL: http://hmpps-auth:9090/auth - DELIUS_API_URL: http://localhost:8080/communityapi + DELIUS_API_URL: http://localhost:8080/delius PROBATION_TEAMS_API_URL: http://localhost:8080/probationteams TOKENVERIFICATION_API_ENABLED: false GOTENBERG_API_URL: http://localhost:3001 diff --git a/Notes.md b/Notes.md index 800385592..5af0cbfb7 100644 --- a/Notes.md +++ b/Notes.md @@ -177,7 +177,7 @@ For, ``` CA and DM: prisonApi, /offender-sentences/home-detention-curfew-candidates -> OffenderSentenceCalc - RO: deliusApi, /staff/staffCode/${deliusStaffCode}/managedOffenders -> OffenderSummary + RO: deliusApi, /staff/${deliusStaffCode}/managedPrisonerIds -> OffenderSummary ``` Every row links to a taskList: hdc/taskList/:bookingId diff --git a/README.md b/README.md index f358da39b..52d066e64 100644 --- a/README.md +++ b/README.md @@ -37,7 +37,7 @@ To run locally you need: - redis - auth server - nomis -- community api +- delius api - probation teams - gotenberg - nomis-user-roles-api diff --git a/docker-compose-full.yml b/docker-compose-full.yml index 0fc427ba5..e0bd6bc3c 100644 --- a/docker-compose-full.yml +++ b/docker-compose-full.yml @@ -29,7 +29,7 @@ services: NOMIS_AUTH_EXTERNAL_URL: "http://localhost:9090/auth" TOKENVERIFICATION_API_URL: "http://token-verification-api:8080" NOMIS_API_URL: "http://stubs:3000/prisonApi" - DELIUS_API_URL: "http://stubs:3000/communityapi" + DELIUS_API_URL: "http://stubs:3000/delius" PROBATION_TEAMS_API_URL: "http://stubs:3000/probationteams" HDC_URL: "http://licences:3000" GOTENBERG_API_URL: "http://gotenberg:3000" diff --git a/feature.env b/feature.env index 9cb04f2e2..fd9b032ff 100644 --- a/feature.env +++ b/feature.env @@ -25,11 +25,10 @@ ADMIN_API_CLIENT_ID=licencesadmin ADMIN_API_CLIENT_SECRET=clientsecret # DELIUS API -DELIUS_API_URL=http://localhost:8080/communityapi +DELIUS_API_URL=http://localhost:8080/delius DELIUS_AUTH_URL=http://localhost:9090/auth DELIUS_API_CLIENT_ID=licencesadmin DELIUS_API_CLIENT_SECRET=clientsecret -DELIUS_API_PREFIX=/api DELIUS_RO_ROLE_ID=LHDCBT002 DELIUS_RO_VARY_ROLE_ID=LHDCBT003 diff --git a/helm_deploy/licences/templates/_envs.tpl b/helm_deploy/licences/templates/_envs.tpl index 54822f915..054d3a70e 100644 --- a/helm_deploy/licences/templates/_envs.tpl +++ b/helm_deploy/licences/templates/_envs.tpl @@ -106,9 +106,6 @@ env: - name: DELIUS_API_URL value: {{ .Values.env.DELIUS_API_URL | quote }} - - name: DELIUS_API_PREFIX - value: {{ .Values.env.DELIUS_API_PREFIX | quote }} - - name: PROBATION_TEAMS_API_URL value: {{ .Values.env.PROBATION_TEAMS_API_URL | quote }} diff --git a/helm_deploy/values-dev.yaml b/helm_deploy/values-dev.yaml index 373982a00..edb461ff5 100644 --- a/helm_deploy/values-dev.yaml +++ b/helm_deploy/values-dev.yaml @@ -37,8 +37,7 @@ env: SCHEDULED_JOBS_OVERLAP: "5000" NOTIFY_ACTIVE_TEMPLATES: "CA_RETURN,CA_DECISION,RO_NEW,RO_TWO_DAYS,RO_DUE,RO_OVERDUE,DM_NEW,DM_TO_CA_RETURN" RO_SERVICE_TYPE: "DELIUS" - DELIUS_API_URL: "https://community-api-secure.test.delius.probation.hmpps.dsd.io" - DELIUS_API_PREFIX: "/secure" + DELIUS_API_URL: "https://hdc-licences-and-delius-dev.hmpps.service.justice.gov.uk" PROBATION_TEAMS_API_URL: "https://probation-teams-dev.prison.service.justice.gov.uk" PRISONER_SEARCH_API_URL: "https://prisoner-search-dev.prison.service.justice.gov.uk" EXIT_LOCATION_URL: "https://digital-dev.prison.service.justice.gov.uk/" diff --git a/helm_deploy/values-preprod.yaml b/helm_deploy/values-preprod.yaml index 5330edece..47ad03380 100644 --- a/helm_deploy/values-preprod.yaml +++ b/helm_deploy/values-preprod.yaml @@ -34,8 +34,7 @@ env: SCHEDULED_JOBS_AUTOSTART: "yes" SCHEDULED_JOBS_OVERLAP: "5000" NOTIFY_ACTIVE_TEMPLATES: "CA_RETURN,CA_DECISION,RO_NEW,DM_NEW,DM_TO_CA_RETURN" - DELIUS_API_URL: "https://community-api-secure.pre-prod.delius.probation.hmpps.dsd.io" - DELIUS_API_PREFIX: "/secure" + DELIUS_API_URL: "https://hdc-licences-and-delius-preprod.hmpps.service.justice.gov.uk" PROBATION_TEAMS_API_URL: "https://probation-teams-preprod.prison.service.justice.gov.uk" PRISONER_SEARCH_API_URL: "https://prisoner-search-preprod.prison.service.justice.gov.uk" EXIT_LOCATION_URL: "https://digital-preprod.prison.service.justice.gov.uk/" diff --git a/helm_deploy/values-prod.yaml b/helm_deploy/values-prod.yaml index 024430d2d..7c2c39a60 100644 --- a/helm_deploy/values-prod.yaml +++ b/helm_deploy/values-prod.yaml @@ -34,8 +34,7 @@ env: SCHEDULED_JOBS_AUTOSTART: "yes" SCHEDULED_JOBS_OVERLAP: "5000" NOTIFY_ACTIVE_TEMPLATES: "CA_RETURN,CA_DECISION,RO_NEW,DM_NEW,DM_TO_CA_RETURN" - DELIUS_API_URL: "https://community-api-secure.probation.service.justice.gov.uk" - DELIUS_API_PREFIX: "/secure" + DELIUS_API_URL: "https://hdc-licences-and-delius.hmpps.service.justice.gov.uk" PROBATION_TEAMS_API_URL: "https://probation-teams.prison.service.justice.gov.uk" PRISONER_SEARCH_API_URL: "https://prisoner-search.prison.service.justice.gov.uk" EXIT_LOCATION_URL: "https://digital.prison.service.justice.gov.uk/" diff --git a/mock-server/app.js b/mock-server/app.js index 1e4fcdecb..8227a21bf 100644 --- a/mock-server/app.js +++ b/mock-server/app.js @@ -42,7 +42,7 @@ app.use('/prisonApi/api/agencies', agenciesRouter) app.use('/prisonApi/api/movements', movementsRouter) app.use('/prisonApi/api/prisoners', prisonersRouter) -app.use('/communityapi/api', deliusRouter) +app.use('/delius', deliusRouter) app.use('/probationteams', probationteamsRouter) app.get('/prisonApi/health/ping', (req, res) => { @@ -53,7 +53,7 @@ app.get('/probationteams/health/ping', (req, res) => { res.send({ status: 'UP' }) }) -app.get('/communityapi/health/ping', (req, res) => { +app.get('/delius/health/ping', (req, res) => { res.send('pong') }) diff --git a/mock-server/routes/delius.js b/mock-server/routes/delius.js index be3e782a2..256ff5bea 100644 --- a/mock-server/routes/delius.js +++ b/mock-server/routes/delius.js @@ -5,19 +5,17 @@ const teamC01T04 = { code: 'C01T04', description: 'OMU A', telephone: '01234567890', - localDeliveryUnit: { code: 'ABC124', description: 'ABC124 delivery unit' }, - district: { code: 'D', description: 'E' }, - borough: { code: 'F', description: 'G' }, + localAdminUnit: { code: 'ABC124', description: 'ABC124 delivery unit' }, } const deliusTeams = [teamC01T04] const AUTH_RO_USER_TEST = { username: 'AUTH_RO_USER_TEST', - staffCode: 'DELIUS_ID_TEST', - staffIdentifier: 1, + code: 'DELIUS_ID_TEST', + staffId: 1, email: 'hdc_test+RO_USER_TEST@digital.justice.gov.uk', - staff: { + name: { forenames: 'FIRSTA', surname: 'LASTA', }, @@ -26,10 +24,10 @@ const AUTH_RO_USER_TEST = { const RO_USER_TEST = { username: 'RO_USER_TEST', - staffCode: 'AUTH_DELIUS_ID_TEST', - staffIdentifier: 2, + code: 'AUTH_DELIUS_ID_TEST', + staffId: 2, email: 'hdc_test+RO_USER_TEST@digital.justice.gov.uk', - staff: { + name: { forenames: 'FIRSTA', surname: 'LASTA', }, @@ -38,10 +36,10 @@ const RO_USER_TEST = { const RO_USER = { username: 'RO_USER', - staffCode: 'DELIUS_ID', - staffIdentifier: 3, + code: 'DELIUS_ID', + staffId: 3, email: 'hdc_test+RO_USER@digital.justice.gov.uk', - staff: { + name: { forenames: 'JESSY', surname: 'SMITH', }, @@ -69,14 +67,22 @@ const staffDetailsByStaffIdentifier = { const router = express.Router() -router.get('/staff/username/:username', (req, res) => { - const { username } = req.params - const staffDetails = staffDetailsByUsername[username] - if (staffDetails) { - res.send(staffDetails) - } else { - res.sendStatus(404) +router.get('/staff', (req, res) => { + const { username, id } = req.query + if (username) { + const staffDetails = staffDetailsByUsername[username] + if (staffDetails) { + res.send(staffDetails) + return + } + } else if (id) { + const staffDetails = staffDetailsByStaffIdentifier[id] + if (staffDetails) { + res.send(staffDetails) + return + } } + res.sendStatus(404) }) /* @@ -112,55 +118,23 @@ router.get('/staff/staffCode/:staffCode/managedOffenders', (req, res) => { }) */ -router.get('/staff/staffIdentifier/:staffIdentifier', (req, res) => { - const { staffIdentifier } = req.params - const staffDetails = staffDetailsByStaffIdentifier[staffIdentifier] - if (staffDetails) { - res.send(staffDetails) - } else { - res.sendStatus(404) - } -}) - -router.get('/staff/staffIdentifier/:staffIdentifier/managedOffenders', (req, res) => { - const { staffIdentifier } = req.params - - const offenders = [ - { - staffIdentifier, - offenderId: 1234567, - nomsNumber: 'A5001DY', - crnNumber: 1234567, - offenderSurname: 'Andrews', - isCurrentRo: true, - isCurrentOm: true, - isCurrentPom: true, - omStartDate: '01/01/2001', - omEndDate: '01/01/2001', - }, - ] - - res.send(offenders) +router.get('/managedPrisonerIds', (req, res) => { + res.send(['A5001DY']) }) -router.get('/offenders/nomsNumber/:nomsNumber/allOffenderManagers', (req, res) => { - const ros = [ - { - isPrisonOffenderManager: false, - isUnallocated: false, - isResponsibleOfficer: true, - staff: { forenames: 'Ryan', surname: 'Orton' }, - staffCode: 'DELIUS_ID', - staffId: 2, - team: teamC01T04, - probationArea: { code: 'ABC', description: 'ABC probation area' }, - }, - ] - - res.send(ros) +router.get('/case/:nomsNumber/communityManager', (req, res) => { + res.send({ + code: 'DELIUS_ID', + staffId: 2, + name: { forenames: 'Ryan', surname: 'Orton' }, + team: teamC01T04, + provider: { code: 'ABC', description: 'ABC probation area' }, + localAdminUnit: { code: 'ABC124', description: 'ABC124 delivery unit' }, + isUnallocated: false, + }) }) -router.get('/probationAreas', (req, res) => { +router.get('/providers', (req, res) => { // return all probation areas const allProbationAreas = probationAreas.map((probArea) => ({ code: probArea.code, @@ -170,11 +144,15 @@ router.get('/probationAreas', (req, res) => { res.send(response) }) -router.get('/probationAreas/code/:code/localDeliveryUnits', (req, res) => { +router.get('/providers/:code', (req, res) => { // return the LDUs for a specific probation area const { code: probationAreaCode } = req.params const probationArea = probationAreas.filter((probArea) => probArea.code === probationAreaCode) - const response = { content: probationArea[0].ldus } + const response = { + code: probationArea[0].code, + description: probationArea[0].description, + localAdminUnits: probationArea[0].ldus, + } res.send(response) }) diff --git a/server/config.js b/server/config.js index e2a54d9be..087e59175 100644 --- a/server/config.js +++ b/server/config.js @@ -58,7 +58,7 @@ module.exports = { }, delius: { - apiUrl: get('DELIUS_API_URL', 'http://localhost:8080/communityapi'), + apiUrl: get('DELIUS_API_URL', 'http://localhost:8080/delius'), authUrl: get('DELIUS_AUTH_URL', get('NOMIS_AUTH_URL', 'http://localhost:9090/auth')), admin: { apiClientId: get('DELIUS_API_CLIENT_ID', get('ADMIN_API_CLIENT_ID', 'licencesadmin')), @@ -73,7 +73,6 @@ module.exports = { maxFreeSockets: 10, freeSocketTimeout: 30000, }, - apiPrefix: get('DELIUS_API_PREFIX', '/api'), // this refers to the 'HDC Digital Update' RBAC which is mapped to LICENCE_RO and GLOBAL_SEARCH in the auth server responsibleOfficerRoleId: get('DELIUS_RO_ROLE_ID', 'LHDCBT002'), // this refers to the 'HDC Digital Update' RBAC which is mapped to LICENCE_RO, GLOBAL_SEARCH and LICENCE_VARY in the auth server diff --git a/server/data/deliusClient.ts b/server/data/deliusClient.ts index 603e38389..7b7c14ab7 100644 --- a/server/data/deliusClient.ts +++ b/server/data/deliusClient.ts @@ -1,153 +1,106 @@ import config from '../config' -export interface KeyValue { - code: string - description: string -} - -export interface Human { - forenames: string - surname: string -} - -export interface Team { - code: string - description: string - telephone: string - localDeliveryUnit: KeyValue - district: KeyValue - borough: KeyValue -} - -export interface StaffDetails { - username?: string - email?: string - staffCode: string - staffIdentifier: number - staff: Human - teams: Team[] +export interface DeliusUser { + username: string + enabled: boolean + roles: string[] } -interface Institution { - institutionId: number - isEstablishment: boolean - code: string +export interface LocalAdminUnit { description: string - institutionName: string - establishmentType: KeyValue - isPrivate: boolean + teams: KeyValue[] } -interface AllTeam { - providerTeamId: number - teamId: number +export interface ProviderWithLaus { code: string description: string - name: string - isPrivate: boolean - externalProvider: KeyValue - scProvider: KeyValue - localDeliveryUnit: KeyValue - district: KeyValue - borough: KeyValue + localAdminUnits: KeyValue[] } -export interface ProbationArea { - probationAreaId: number +export interface CommunityManager { code: string - description: string - nps: boolean - organisation: KeyValue - institution: Institution - teams: AllTeam[] -} - -export interface CommunityOrPrisonOffenderManager { - staffCode: string + /** @deprecated use code instead */ staffId: number - isResponsibleOfficer: boolean - isPrisonOffenderManager: boolean + name: Name + team: KeyValue + provider: KeyValue + localAdminUnit: KeyValue isUnallocated: boolean - staff: Human - team: Team - probationArea: ProbationArea - fromDate: Date } -export interface ProbationAreaSummary { +export interface KeyValue { code: string description: string } -export interface Ldu { - code: string - description: string +export interface Name { + forenames: string + surname: string } -export interface ProbationTeam { +export interface Team { code: string description: string + telephone: string + probationDeliveryUnit: KeyValue + localAdminUnit: KeyValue } -export interface ManagedOffender { - staffCode: string - staffIdentifier: number - offenderId: number - nomsNumber: string - crnNumber: string - offenderSurname: string - isCurrentRo: boolean - isCurrentOm: boolean - isCurrentPom: boolean - omStartDate: Date - omEndDate: Date -} - -export interface Page { - content?: T[] +export interface StaffDetails { + code: string + /** @deprecated use code instead */ + staffId: number + name: Name + teams: Team[] + username?: string + email?: string } export class DeliusClient { constructor(readonly restClient) {} - getStaffDetailsByStaffCode(staffCode): Promise { - return this.restClient.getResource(`/staff/staffCode/${staffCode}`) + getStaffDetailsByStaffCode(staffCode: string): Promise { + return this.restClient.getResource(`/staff/${staffCode}`) } + /** @deprecated use getStaffDetailsByStaffCode instead */ getStaffDetailsByStaffIdentifier(staffIdentifier: number): Promise { - return this.restClient.getResource(`/staff/staffIdentifier/${staffIdentifier}`) + return this.restClient.getResource(`/staff?id=${staffIdentifier}`) } getStaffDetailsByUsername(username: string): Promise { - return this.restClient.getResource(`/staff/username/${username}`) + return this.restClient.getResource(`/staff?username=${username}`) + } + + getManagedPrisonerIdsByStaffCode(staffCode: string): Promise { + return this.restClient.getResource(`/staff/${staffCode}/managedPrisonerIds`) } - getROPrisonersByStaffIdentifier(staffIdentifier: number): Promise> { - return this.restClient.getResource(`/staff/staffIdentifier/${staffIdentifier}/managedOffenders`) + /** @deprecated use getStaffDetailsByStaffCode instead */ + getManagedPrisonerIdsByStaffId(staffIdentifier: number): Promise { + return this.restClient.getResource(`/managedPrisonerIds?staffId=${staffIdentifier}`) } - getAllOffenderManagers(offenderNo: string): Promise> { - return this.restClient.getResource(`/offenders/nomsNumber/${offenderNo}/allOffenderManagers`) + getCommunityManager(offenderNo: string): Promise { + return this.restClient.getResource(`/case/${offenderNo}/communityManager`) } - getAllProbationAreas(): Promise> { - return this.restClient.getResource(`/probationAreas?excludeEstablishments=true&active=true`) + getAllProbationAreas(): Promise { + return this.restClient.getResource(`/providers`) } - async getAllLdusForProbationArea(probationAreaCode: string): Promise> { - const ldus = await this.restClient.getResource(`/probationAreas/code/${probationAreaCode}/localDeliveryUnits`) - return ldus?.content ? ldus : { content: [] } + async getProbationArea(probationAreaCode: string): Promise { + return (await this.restClient.getResource(`/providers/${probationAreaCode}`)) ?? { localAdminUnits: [] } } - async getAllTeamsForLdu(probationAreaCode: string, lduCode: string): Promise> { - const teams = await this.restClient.getResource( - `/probationAreas/code/${probationAreaCode}/localDeliveryUnits/code/${lduCode}/teams` + async getLduWithTeams(probationAreaCode: string, lauCode: string): Promise { + return ( + (await this.restClient.getResource(`/providers/${probationAreaCode}/localAdminUnits/${lauCode}`)) ?? { teams: [] } ) - return teams?.content ? teams : { content: [] } } - async addResponsibleOfficerRole(username: string): Promise { - await this.addRole(username, config.delius.responsibleOfficerRoleId) + addResponsibleOfficerRole(username: string): Promise { + return this.addRole(username, config.delius.responsibleOfficerRoleId) } async addRole(username: string, code: string): Promise { @@ -158,7 +111,7 @@ export class DeliusClient { } } - async getUser(username: string): Promise { + getUser(username: string): Promise { return this.restClient.getResource(`/users/${username}/details`) } } diff --git a/server/index.js b/server/index.js index 7aafa24a6..1f023a68e 100644 --- a/server/index.js +++ b/server/index.js @@ -62,8 +62,8 @@ const conditionsServiceFactory = new ConditionServiceFactory() const deliusClient = new DeliusClient( buildRestClient( clientCredentialsTokenSource(signInService, 'delius'), - `${config.delius.apiUrl}${config.delius.apiPrefix}`, - 'Delius community API', + config.delius.apiUrl, + 'Delius Integration API', { timeout: config.delius.timeout, agent: config.delius.agent } ) ) diff --git a/server/services/caseListService.ts b/server/services/caseListService.ts index 2aa51dd81..082edf85a 100644 --- a/server/services/caseListService.ts +++ b/server/services/caseListService.ts @@ -54,10 +54,10 @@ export = function createCaseListService( const getIdsFromDelius = async (username: string): Promise> => { const staffDetailsResult = await getStaffDetailsFromDelius(username) - return staffDetailsResult.flatMap(({ staffIdentifier }) => - isEmpty(staffIdentifier) + return staffDetailsResult.flatMap(({ staffId }) => + isEmpty(staffId) ? Fail(`Delius did not supply a staff identifier for username ${username}`) - : Success({ staffIdentifier }) + : Success({ staffIdentifier: staffId }) ) } diff --git a/server/services/functionalMailboxService.ts b/server/services/functionalMailboxService.ts index f2616d4ba..cbeae2bd7 100644 --- a/server/services/functionalMailboxService.ts +++ b/server/services/functionalMailboxService.ts @@ -1,12 +1,12 @@ import * as R from 'ramda' import { - LocalDeliveryUnitDto, - ProbationTeamsClient, LduIdentifier, + LocalDeliveryUnitDto, ProbationTeamIdentifier, + ProbationTeamsClient, } from '../data/probationTeamsClient' -import { DeliusClient, Ldu, ProbationArea, ProbationTeam } from '../data/deliusClient' +import { DeliusClient, KeyValue } from '../data/deliusClient' export interface LduMap { [code: string]: { @@ -33,7 +33,7 @@ export interface ProbationTeamMap { } export const mergeLduData = ( - ldus: Ldu[], + ldus: KeyValue[], lduDtos: { [localDeliveryUnitCode: string]: LocalDeliveryUnitDto } ): LduMap => { const lduMap = ldus.reduce((map, { code, description }) => ({ ...map, [code]: { description } }), {}) @@ -44,7 +44,7 @@ export const mergeLduData = ( return R.mergeDeepRight(lduMap, filteredDtos) } -export const mergeProbationTeams = (probationTeams: ProbationTeam[], localDeliveryUnitDto: LocalDeliveryUnitDto) => { +export const mergeProbationTeams = (probationTeams: KeyValue[], localDeliveryUnitDto: LocalDeliveryUnitDto) => { const probationTeamMap = probationTeams.reduce( (map, { code, description }) => ({ ...map, [code]: { description } }), {} @@ -53,7 +53,7 @@ export const mergeProbationTeams = (probationTeams: ProbationTeam[], localDelive return R.mergeDeepRight(probationTeamMap, probationTeamDtoMap) } -export function mergeProbationAreaData(probationAreas: ProbationArea[], probationAreaCodes: string[]) { +export function mergeProbationAreaData(probationAreas: KeyValue[], probationAreaCodes: string[]) { const probationCodeMap = probationAreaCodes.reduce((map, code) => ({ ...map, [code]: {} }), {}) const probationAreaMap = probationAreas.reduce( (map, { code, description }) => ({ ...map, [code]: { description } }), @@ -78,7 +78,7 @@ export class FunctionalMailboxService { ) {} getAllProbationAreas = async () => { - const [{ content: probationAreas } = { content: [] }, probationAreaCodes = []] = await Promise.all([ + const [probationAreas = [], probationAreaCodes = []] = await Promise.all([ this.deliusClient.getAllProbationAreas(), this.probationTeamsClient.getProbationAreaCodes(), ]) @@ -86,8 +86,8 @@ export class FunctionalMailboxService { } getLdusForProbationArea = async (probationAreaCode): Promise => { - const [{ content: ldus }, { localDeliveryUnits = {} } = {}] = await Promise.all([ - this.deliusClient.getAllLdusForProbationArea(probationAreaCode), + const [{ localAdminUnits: ldus }, { localDeliveryUnits = {} } = {}] = await Promise.all([ + this.deliusClient.getProbationArea(probationAreaCode), this.probationTeamsClient.getProbationArea(probationAreaCode), ]) @@ -95,16 +95,15 @@ export class FunctionalMailboxService { } getLduWithProbationTeams = async ({ probationAreaCode, lduCode }): Promise => { - const [{ content: ldus }, { content: probationTeams }, localDeliveryUnitDto] = await Promise.all([ - this.deliusClient.getAllLdusForProbationArea(probationAreaCode), - this.deliusClient.getAllTeamsForLdu(probationAreaCode, lduCode), + const [ldu, localDeliveryUnitDto] = await Promise.all([ + this.deliusClient.getLduWithTeams(probationAreaCode, lduCode), this.probationTeamsClient.getLduWithProbationTeams({ probationAreaCode, lduCode }), ]) return { - description: getDescription(ldus, lduCode), + description: ldu.description ?? '', functionalMailbox: R.propOr('', 'functionalMailbox', localDeliveryUnitDto), - probationTeams: mergeProbationTeams(probationTeams, localDeliveryUnitDto), + probationTeams: mergeProbationTeams(ldu.teams, localDeliveryUnitDto), } } diff --git a/server/services/lduService.js b/server/services/lduService.js index 9c76481ac..77c078733 100644 --- a/server/services/lduService.js +++ b/server/services/lduService.js @@ -15,19 +15,16 @@ const logger = require('../../log') module.exports = function createLduService(deliusClient, activeLduClient) { return { async getAllProbationAreas() { - const { content: probationAreas } = await deliusClient.getAllProbationAreas() + const probationAreas = await deliusClient.getAllProbationAreas() probationAreas.sort((a, b) => a.description.localeCompare(b.description, 'en', { ignorePunctuation: true })) return probationAreas }, async getProbationArea(probationAreaCode) { - const { content: probationAreas } = await deliusClient.getAllProbationAreas() - const probationAreaDescription = probationAreas.find((area) => probationAreaCode === area.code).description - const activeLdus = await activeLduClient.allActiveLdusInArea(probationAreaCode) const activeLdusCodes = activeLdus.map((ldu) => ldu.code) - const { content: ldus } = await deliusClient.getAllLdusForProbationArea(probationAreaCode) + const { description, localAdminUnits: ldus } = await deliusClient.getProbationArea(probationAreaCode) const allLdus = ldus.map((ldu) => ({ code: ldu.code, @@ -41,7 +38,7 @@ module.exports = function createLduService(deliusClient, activeLduClient) { return { code: probationAreaCode, - description: probationAreaDescription, + description, ldus: uniqueLdus, } }, diff --git a/server/services/migrationService.ts b/server/services/migrationService.ts index 8dee44ea2..8ffbc2f6b 100644 --- a/server/services/migrationService.ts +++ b/server/services/migrationService.ts @@ -1,4 +1,4 @@ -import { DeliusClient } from '../data/deliusClient' +import { DeliusClient, DeliusUser, StaffDetails } from '../data/deliusClient' import UserAdminService from './userAdminService' import logger from '../../log' import config from '../config' @@ -66,7 +66,7 @@ export default class MigrationService { return { licenceUser, deliusUser: deliusStaffDetails, authUser, flags } } - private async getDeliusStaffDetails(licenceUser) { + private async getDeliusStaffDetails(licenceUser): Promise { const { staffIdentifier, deliusId } = licenceUser if (staffIdentifier) try { @@ -86,9 +86,9 @@ export default class MigrationService { return null } - private getFlags(deliusUser, authUser, licenceUser, deliusStaffDetails, failedToLoadAuth: boolean) { + private getFlags(deliusUser: DeliusUser, authUser, licenceUser, deliusStaffDetails, failedToLoadAuth: boolean) { const deliusRoles = deliusUser ? deliusUser.roles : [] - const hasRoRole = deliusRoles.map((r) => r.name).includes(config.delius.responsibleOfficerRoleId) + const hasRoRole = deliusRoles.includes(config.delius.responsibleOfficerRoleId) const hasVaryRole = authUser?.roles.includes('LICENCE_VARY') @@ -121,15 +121,12 @@ export default class MigrationService { await this.deliusClient.addRole(deliusUsername, role) } - public async getDeliusRoles(deliusUsername): Promise { + public async getDeliusRoles(deliusUsername: string): Promise { const user = await this.getUserFromDelius(deliusUsername) if (!user) { return null } - const deliusRoles = user.roles || [] - return deliusRoles - .map((r) => r.name) - .filter((r) => [config.delius.responsibleOfficerRoleId, config.delius.responsibleOfficerVaryRoleId].includes(r)) + return user.roles || [] } public async disableAuthAccount(token, nomisUsername) { @@ -151,10 +148,9 @@ export default class MigrationService { } } - private async getUserFromDelius(username) { + private async getUserFromDelius(username: string): Promise { try { - const result = await this.deliusClient.getUser(username) - return result + return await this.deliusClient.getUser(username) } catch (error) { logger.warn(`Problem retrieving user from delius for username: ${username}`, error.stack) return null diff --git a/server/services/roService.ts b/server/services/roService.ts index 3821a2f40..7d05d8be3 100644 --- a/server/services/roService.ts +++ b/server/services/roService.ts @@ -1,4 +1,4 @@ -import { CommunityOrPrisonOffenderManager, DeliusClient, StaffDetails } from '../data/deliusClient' +import { CommunityManager, DeliusClient, StaffDetails } from '../data/deliusClient' import { ResponsibleOfficer, ResponsibleOfficerResult, Result } from '../../types/licences' const setCase = require('case') @@ -12,9 +12,9 @@ export class RoService { readonly nomisClientBuilder ) {} - private async getROPrisonersFromDeliusForStaffIdentifier(staffIdentifier: number): Promise> { + private async getROPrisonersFromDeliusForStaffIdentifier(staffIdentifier: number): Promise { try { - return await this.deliusClient.getROPrisonersByStaffIdentifier(staffIdentifier) + return await this.deliusClient.getManagedPrisonerIdsByStaffId(staffIdentifier) } catch (error) { logger.error(`Problem retrieving RO prisoners for: ${staffIdentifier}`, error.stack) throw error @@ -42,14 +42,10 @@ export class RoService { async getROPrisonersForStaffIdentifier(staffIdentifier: number, token: string) { const nomisClient = this.nomisClientBuilder(token) - const requiredPrisoners = await this.getROPrisonersFromDeliusForStaffIdentifier(staffIdentifier) - if (!requiredPrisoners) { + const requiredIDs = await this.getROPrisonersFromDeliusForStaffIdentifier(staffIdentifier) + if (!requiredIDs) { return null } - - const requiredIDs = requiredPrisoners - .filter((prisoner) => prisoner.nomsNumber) - .map((prisoner) => prisoner.nomsNumber) return nomisClient.getOffenderSentencesByNomisId(requiredIDs) } @@ -63,12 +59,12 @@ export class RoService { logger.info(`findResponsibleOfficerWithOffenderNo: ${offenderNo}`) try { - const offenderManagers = await this.deliusClient.getAllOffenderManagers(offenderNo) - if (!offenderManagers) { + const communityManager = await this.deliusClient.getCommunityManager(offenderNo) + if (!communityManager) { logger.error(`Offender not present in delius: ${offenderNo}`) return { code: NO_OFFENDER_NUMBER, message: 'Offender number not entered in delius' } } - return extractCommunityOffenderManager(offenderNo, offenderManagers) + return toResponsibleOfficer(offenderNo, communityManager) } catch (error) { logger.error(`findResponsibleOfficer for: ${offenderNo}`, error.stack) throw error @@ -76,37 +72,25 @@ export class RoService { } } -function extractCommunityOffenderManager( - offenderNumber: string, - offenderManagers: CommunityOrPrisonOffenderManager[] -): ResponsibleOfficerResult { - const responsibleOfficer = offenderManagers.find((manager) => !manager.isPrisonOffenderManager) - return !responsibleOfficer - ? { code: NO_COM_ASSIGNED, message: `Offender has not been assigned a COM: ${offenderNumber}` } - : toResponsibleOfficer(offenderNumber, responsibleOfficer) -} - -function toResponsibleOfficer( - offenderNumber: string, - offenderManager: CommunityOrPrisonOffenderManager -): ResponsibleOfficer { +function toResponsibleOfficer(offenderNumber: string, offenderManager: CommunityManager): ResponsibleOfficer { const { - staff: { forenames, surname }, - staffCode, + name: { forenames, surname }, + code, staffId, isUnallocated, - team: { localDeliveryUnit, code, description }, - probationArea, + team, + localAdminUnit: localDeliveryUnit, + provider: probationArea, } = offenderManager const name = setCase.capital([forenames, surname].join(' ').trim().toLowerCase()) return { name, isAllocated: !isUnallocated, - deliusId: staffCode, + deliusId: code, staffIdentifier: staffId, nomsNumber: offenderNumber, - teamCode: code, - teamDescription: description, + teamCode: team.code, + teamDescription: team.description, lduCode: localDeliveryUnit.code, lduDescription: localDeliveryUnit.description, probationAreaCode: probationArea.code, diff --git a/server/views/admin/users/migrate.pug b/server/views/admin/users/migrate.pug index 390c51e79..6ce7dee69 100644 --- a/server/views/admin/users/migrate.pug +++ b/server/views/admin/users/migrate.pug @@ -41,13 +41,13 @@ block content h3.heading-medium Delius record if deliusUser div.pure-u-1-3 Name - div.pure-u-2-3.bold #{deliusUser.staff.forenames} #{deliusUser.staff.surname} + div.pure-u-2-3.bold #{deliusUser.name.forenames} #{deliusUser.name.surname} div.pure-u-1-3 Delius staff code - div.pure-u-2-3.bold #{deliusUser.staffCode} + div.pure-u-2-3.bold #{deliusUser.code} div.pure-u-1-3 Delius staff identifier - div.pure-u-2-3.bold #{deliusUser.staffIdentifier} + div.pure-u-2-3.bold #{deliusUser.staffId} if !flags.includes('UNLINKED_ACCOUNT') div.pure-u-1-3 Delius username diff --git a/test/data/deliusClient.test.js b/test/data/deliusClient.test.js index 176134136..df586bc84 100644 --- a/test/data/deliusClient.test.js +++ b/test/data/deliusClient.test.js @@ -13,13 +13,13 @@ describe('deliusClient', () => { let signInService beforeEach(() => { - fakeDelius = nock(`${config.delius.apiUrl}${config.delius.apiPrefix}`) + fakeDelius = nock(config.delius.apiUrl) signInService = new SignInService(null) signInService.getAnonymousClientCredentialsTokens = jest.fn().mockResolvedValue('token') const restClient = buildRestClient( clientCredentialsTokenSource(signInService, 'delius'), - `${config.delius.apiUrl}${config.delius.apiPrefix}`, - 'Delius community API', + config.delius.apiUrl, + 'Delius Integration API', { timeout: config.delius.timeout, agent: config.delius.agent } ) deliusClient = new DeliusClient(restClient) @@ -32,19 +32,19 @@ describe('deliusClient', () => { describe('deliusClient', () => { test('should throw error on GET when no token', () => { signInService.getAnonymousClientCredentialsTokens.mockReturnValue(null) - return expect(deliusClient.getROPrisonersByStaffIdentifier(1)).rejects.toThrow('Error obtaining OAuth token') + return expect(deliusClient.getManagedPrisonerIdsByStaffId(1)).rejects.toThrow('Error obtaining OAuth token') }) }) describe('getStaffBystaffIdentifier', () => { test('should return data from api', () => { - fakeDelius.get(`/staff/staffIdentifier/1`).reply(200, { key: 'value' }) + fakeDelius.get(`/staff?id=1`).reply(200, { key: 'value' }) return expect(deliusClient.getStaffDetailsByStaffIdentifier(1)).resolves.toStrictEqual({ key: 'value' }) }) test('should reject if api fails', () => { - fakeDelius.get(`/staff/staffIdentifier/1`).thrice().reply(500, '1') + fakeDelius.get(`/staff?id=1`).thrice().reply(500, '1') return expect(deliusClient.getStaffDetailsByStaffIdentifier(1)).rejects.toStrictEqual( Error('Internal Server Error') @@ -54,13 +54,13 @@ describe('deliusClient', () => { describe('getStaffByUsername', () => { test('should return data from api', () => { - fakeDelius.get(`/staff/username/1`).reply(200, { key: 'value' }) + fakeDelius.get(`/staff?username=1`).reply(200, { key: 'value' }) return expect(deliusClient.getStaffDetailsByUsername('1')).resolves.toStrictEqual({ key: 'value' }) }) test('should reject if api fails', () => { - fakeDelius.get(`/staff/username/1`).thrice().reply(500) + fakeDelius.get(`/staff?username=1`).thrice().reply(500) return expect(deliusClient.getStaffDetailsByUsername('1')).rejects.toStrictEqual(Error('Internal Server Error')) }) @@ -68,15 +68,15 @@ describe('deliusClient', () => { describe('getROPrisoners', () => { test('should return data from api', () => { - fakeDelius.get(`/staff/staffIdentifier/1/managedOffenders`).reply(200, { key: 'value' }) + fakeDelius.get(`/managedPrisonerIds?staffId=1`).reply(200, { key: 'value' }) - return expect(deliusClient.getROPrisonersByStaffIdentifier(1)).resolves.toStrictEqual({ key: 'value' }) + return expect(deliusClient.getManagedPrisonerIdsByStaffId(1)).resolves.toStrictEqual({ key: 'value' }) }) test('should reject if api fails', () => { - fakeDelius.get(`/staff/staffIdentifier/1/managedOffenders`).thrice().reply(500) + fakeDelius.get(`/managedPrisonerIds?staffId=1`).thrice().reply(500) - return expect(deliusClient.getROPrisonersByStaffIdentifier(1)).rejects.toStrictEqual( + return expect(deliusClient.getManagedPrisonerIdsByStaffId(1)).rejects.toStrictEqual( Error('Internal Server Error') ) }) @@ -84,29 +84,27 @@ describe('deliusClient', () => { describe('getAllOffenderManagers', () => { test('should return data from api', () => { - fakeDelius.get(`/offenders/nomsNumber/1/allOffenderManagers`).reply(200, [{ key: 'value' }]) + fakeDelius.get(`/case/1/communityManager`).reply(200, [{ key: 'value' }]) - return expect(deliusClient.getAllOffenderManagers('1')).resolves.toStrictEqual([{ key: 'value' }]) + return expect(deliusClient.getCommunityManager('1')).resolves.toStrictEqual([{ key: 'value' }]) }) test('should return undefined when not found', () => { - fakeDelius.get(`/offenders/nomsNumber/1/allOffenderManagers`).reply(404) + fakeDelius.get(`/case/1/communityManager`).reply(404) - return expect(deliusClient.getAllOffenderManagers('1')).resolves.toBeUndefined() + return expect(deliusClient.getCommunityManager('1')).resolves.toBeUndefined() }) test('should reject if api fails', () => { - fakeDelius.get(`/offenders/nomsNumber/1/allOffenderManagers`).thrice().reply(500) + fakeDelius.get(`/case/1/communityManager`).thrice().reply(500) - return expect(deliusClient.getAllOffenderManagers('1')).rejects.toStrictEqual(Error('Internal Server Error')) + return expect(deliusClient.getCommunityManager('1')).rejects.toStrictEqual(Error('Internal Server Error')) }) }) describe('getAllProbationAreas', () => { test('should return list of all probation areas', () => { - fakeDelius - .get(`/probationAreas?excludeEstablishments=true&active=true`) - .reply(200, [{ code: 'some code', description: 'some description' }]) + fakeDelius.get(`/providers`).reply(200, [{ code: 'some code', description: 'some description' }]) return expect(deliusClient.getAllProbationAreas()).resolves.toStrictEqual([ { code: 'some code', description: 'some description' }, @@ -116,58 +114,35 @@ describe('deliusClient', () => { describe('getAllLdusForProbationArea', () => { test('should return list of all probation codes', () => { - fakeDelius - .get(`/probationAreas/code/N02/localDeliveryUnits`) - .reply(200, { content: [{ code: 'some code', description: 'some description' }] }) + fakeDelius.get(`/providers/N02`).reply(200, { content: [{ code: 'some code', description: 'some description' }] }) - return expect(deliusClient.getAllLdusForProbationArea('N02')).resolves.toStrictEqual({ + return expect(deliusClient.getProbationArea('N02')).resolves.toStrictEqual({ content: [{ code: 'some code', description: 'some description' }], }) }) test('Probation code not known to Delius', () => { - fakeDelius.get(`/probationAreas/code/N02/localDeliveryUnits`).reply(404) + fakeDelius.get(`/providers/N02`).reply(404) - return expect(deliusClient.getAllLdusForProbationArea('N02')).resolves.toStrictEqual({ content: [] }) + return expect(deliusClient.getProbationArea('N02')).resolves.toStrictEqual({ localAdminUnits: [] }) }) }) describe('getAllTeamsForLdu', () => { test('should return list of all probation codes', () => { fakeDelius - .get('/probationAreas/code/N02/localDeliveryUnits/code/LDU/teams') - .reply(200, { content: [{ code: 'some code', description: 'some description' }] }) + .get('/providers/N02/localAdminUnits/LDU') + .reply(200, { localAdminUnits: [{ code: 'some code', description: 'some description' }] }) - return expect(deliusClient.getAllTeamsForLdu('N02', 'LDU')).resolves.toStrictEqual({ - content: [{ code: 'some code', description: 'some description' }], + return expect(deliusClient.getLduWithTeams('N02', 'LDU')).resolves.toStrictEqual({ + localAdminUnits: [{ code: 'some code', description: 'some description' }], }) }) - test('LDU not known to Delius: 200, but no "content" field', () => { - fakeDelius.get('/probationAreas/code/N02/localDeliveryUnits/code/LDU/teams').reply(200, { - pageable: 'INSTANCE', - totalElements: 0, - last: true, - totalPages: 1, - sort: { - sorted: false, - unsorted: true, - empty: true, - }, - first: true, - number: 0, - size: 0, - numberOfElements: 0, - empty: true, - }) - - return expect(deliusClient.getAllTeamsForLdu('N02', 'LDU')).resolves.toStrictEqual({ content: [] }) - }) - test('LDU not known to Delius: 404', () => { - fakeDelius.get('/probationAreas/code/N02/localDeliveryUnits/code/LDU/teams').reply(404) + fakeDelius.get('/providers/N02/localAdminUnits/LDU').reply(404) - return expect(deliusClient.getAllTeamsForLdu('N02', 'LDU')).resolves.toStrictEqual({ content: [] }) + return expect(deliusClient.getLduWithTeams('N02', 'LDU')).resolves.toStrictEqual({ teams: [] }) }) }) diff --git a/test/routes/contact.test.ts b/test/routes/contact.test.ts index b46949f70..930531c61 100644 --- a/test/routes/contact.test.ts +++ b/test/routes/contact.test.ts @@ -34,17 +34,16 @@ describe('/contact', () => { roService.getStaffByStaffIdentifier.mockResolvedValue({ username: 'username', email: '123456@somewhere.com', - staffCode: 'DELIUS_ID', - staffIdentifier: 999, - staff: { forenames: 'RO', surname: 'Name' }, + code: 'DELIUS_ID', + staffId: 999, + name: { forenames: 'RO', surname: 'Name' }, teams: [ { code: 'TEAM_CODE', description: 'The Team', telephone: '01234567890', - localDeliveryUnit: { code: 'ABC123', description: 'LDU Description' }, - district: { code: 'D', description: 'District' }, - borough: { code: 'B', description: 'Borough' }, + probationDeliveryUnit: { code: 'ABC123', description: 'PDU Description' }, + localAdminUnit: { code: 'ABC123', description: 'LDU Description' }, }, ], }) diff --git a/test/services/caseListService.test.ts b/test/services/caseListService.test.ts index 190f80f26..0227c8938 100644 --- a/test/services/caseListService.test.ts +++ b/test/services/caseListService.test.ts @@ -80,14 +80,14 @@ describe('caseListService', () => { const staffDetailsNoStaffIdentifier = { username: 'username', email: 'email', - staffCode: 'ABC123', - staff: { forenames: 'user', surname: 'name' }, + code: 'ABC123', + name: { forenames: 'user', surname: 'name' }, teams: [], } as StaffDetails const staffDetails: StaffDetails = { ...staffDetailsNoStaffIdentifier, - staffIdentifier: 2, + staffId: 2, } const deliusId1: DeliusIds = { staffIdentifier: 1, deliusUsername: 'deliusUser' } diff --git a/test/services/functionalMailboxService.test.ts b/test/services/functionalMailboxService.test.ts index 51222e4cc..9950b3cec 100644 --- a/test/services/functionalMailboxService.test.ts +++ b/test/services/functionalMailboxService.test.ts @@ -7,28 +7,15 @@ import { import { AuditMock, mockAudit } from '../mockClients' import { ProbationTeamsClient } from '../../server/data/probationTeamsClient' -import { DeliusClient } from '../../server/data/deliusClient' -import type { ProbationArea } from '../../server/data/deliusClient' +import { DeliusClient, LocalAdminUnit, ProviderWithLaus } from '../../server/data/deliusClient' jest.mock('../../server/data/deliusClient') jest.mock('../../server/data/probationTeamsClient') -const BASE_PROBATION_AREA: ProbationArea = Object.freeze({ +const BASE_PROBATION_AREA: ProviderWithLaus = Object.freeze({ code: '', description: '', - probationAreaId: 1, - nps: false, - institution: Object.freeze({ - institutionId: 1, - isEstablishment: false, - code: 'I1', - description: 'I1', - institutionName: 'I1', - establishmentType: { code: 'I1', description: 'I1' }, - isPrivate: false, - }), - organisation: { code: 'org', description: 'org' }, - teams: [], + localAdminUnits: [], }) describe('FunctionalMailboxService', () => { @@ -96,13 +83,11 @@ describe('FunctionalMailboxService', () => { ...BASE_PROBATION_AREA, code: 'A', description: 'PA A', - probationAreaId: 1, }, { ...BASE_PROBATION_AREA, code: 'B', description: 'PA B', - probationAreaId: 2, }, ], ['B', 'C'] @@ -142,12 +127,10 @@ describe('FunctionalMailboxService', () => { beforeEach(initMocks) it('Happy path', async () => { - deliusClient.getAllProbationAreas.mockResolvedValue({ - content: [ - { ...BASE_PROBATION_AREA, code: 'A', description: 'PA A' }, - { ...BASE_PROBATION_AREA, code: 'B', description: 'PA B' }, - ], - }) + deliusClient.getAllProbationAreas.mockResolvedValue([ + { ...BASE_PROBATION_AREA, code: 'A', description: 'PA A' }, + { ...BASE_PROBATION_AREA, code: 'B', description: 'PA B' }, + ]) probationTeamsClient.getProbationAreaCodes.mockResolvedValue(['B', 'C']) expect(await functionalMailboxService.getAllProbationAreas()).toEqual({ @@ -173,8 +156,10 @@ describe('FunctionalMailboxService', () => { beforeEach(initMocks) it('Happy path', async () => { - deliusClient.getAllLdusForProbationArea.mockResolvedValue({ - content: [{ code: 'B', description: 'LDU B' }], + deliusClient.getProbationArea.mockResolvedValue({ + code: 'PA', + description: 'PA 1', + localAdminUnits: [{ code: 'B', description: 'LDU B' }], }) probationTeamsClient.getProbationArea.mockResolvedValue({ probationAreaCode: 'PA', @@ -197,7 +182,7 @@ describe('FunctionalMailboxService', () => { }) it('Handles missing data', async () => { - deliusClient.getAllLdusForProbationArea.mockResolvedValue({ content: [] }) + deliusClient.getProbationArea.mockResolvedValue({ localAdminUnits: [] } as ProviderWithLaus) probationTeamsClient.getProbationArea.mockResolvedValue(undefined) expect(await functionalMailboxService.getLdusForProbationArea('PA')).toEqual({}) @@ -208,20 +193,14 @@ describe('FunctionalMailboxService', () => { beforeEach(initMocks) it('Happy path', async () => { - deliusClient.getAllLdusForProbationArea.mockResolvedValue({ - content: [ - { code: 'A', description: 'LDU A' }, - { code: 'B', description: 'LDU B' }, - { code: 'C', description: 'LDU C' }, - ], - }) - - deliusClient.getAllTeamsForLdu.mockResolvedValue({ - content: [ + deliusClient.getLduWithTeams.mockResolvedValue({ + code: 'B', + description: 'LDU B', + teams: [ { code: 'TA', description: 'Team A' }, { code: 'TB', description: 'Team B' }, ], - }) + } as LocalAdminUnit) probationTeamsClient.getLduWithProbationTeams.mockResolvedValue({ probationAreaCode: 'PA', @@ -257,8 +236,8 @@ describe('FunctionalMailboxService', () => { }) it('No data', async () => { - deliusClient.getAllLdusForProbationArea.mockResolvedValue({ content: [] }) - deliusClient.getAllTeamsForLdu.mockResolvedValue({ content: [] }) + deliusClient.getProbationArea.mockResolvedValue({ localAdminUnits: [] } as ProviderWithLaus) + deliusClient.getLduWithTeams.mockResolvedValue({ teams: [] } as LocalAdminUnit) probationTeamsClient.getLduWithProbationTeams.mockResolvedValue(undefined) expect( @@ -274,8 +253,8 @@ describe('FunctionalMailboxService', () => { }) it('Not found', async () => { - deliusClient.getAllLdusForProbationArea.mockResolvedValue({ content: [] }) - deliusClient.getAllTeamsForLdu.mockResolvedValue({ content: [] }) + deliusClient.getProbationArea.mockResolvedValue({ localAdminUnits: [] } as ProviderWithLaus) + deliusClient.getLduWithTeams.mockResolvedValue({ teams: [] } as LocalAdminUnit) probationTeamsClient.getLduWithProbationTeams.mockResolvedValue(undefined) expect( diff --git a/test/services/lduService.test.js b/test/services/lduService.test.js index faf9a5371..89417131d 100644 --- a/test/services/lduService.test.js +++ b/test/services/lduService.test.js @@ -1,7 +1,7 @@ const createLduService = require('../../server/services/lduService') /** @type {any} */ const deliusClient = { - getAllLdusForProbationArea: jest.fn(), + getProbationArea: jest.fn(), getAllProbationAreas: jest.fn(), } const activeLduClient = { @@ -17,12 +17,10 @@ let lduService describe('lduService', () => { beforeEach(async () => { lduService = await createLduService(deliusClient, activeLduClient) - deliusClient.getAllProbationAreas.mockResolvedValue({ - content: [ - { code: 'ABC123', description: 'desc-1' }, - { code: 'DEF345', description: 'desc-2' }, - ], - }) + deliusClient.getAllProbationAreas.mockResolvedValue([ + { code: 'ABC123', description: 'desc-1' }, + { code: 'DEF345', description: 'desc-2' }, + ]) }) afterEach(() => { @@ -49,10 +47,12 @@ describe('lduService', () => { }) describe('getProbationArea', () => { - it(`Should return all the LDUs from Delius for London and cross match with those in active_local_delivery_units table in DB. + it(`Should return all the LDUs from Delius for London and cross match with those in active_local_delivery_units table in DB. Those that cross-matchs will have a 'active = true' status`, async () => { - deliusClient.getAllLdusForProbationArea.mockResolvedValue({ - content: [ + deliusClient.getProbationArea.mockResolvedValue({ + code: 'Lon', + description: 'London', + localAdminUnits: [ { code: 'ham', description: 'Hampstead' }, { code: 'wtl', description: 'Waterloo' }, { code: 'pic', description: 'Picadilly' }, @@ -76,8 +76,10 @@ describe('lduService', () => { }) it('Should remove duplicate ldus and only show the first description', async () => { - deliusClient.getAllLdusForProbationArea.mockResolvedValue({ - content: [ + deliusClient.getProbationArea.mockResolvedValue({ + code: 'Lon', + description: 'London', + localAdminUnits: [ { code: 'ham', description: 'Hampstead' }, { code: 'pic', description: 'Picadilly' }, { code: 'ham', description: 'Hampstead2' }, diff --git a/test/services/migrationService.test.ts b/test/services/migrationService.test.ts index e42062d68..b49728c12 100644 --- a/test/services/migrationService.test.ts +++ b/test/services/migrationService.test.ts @@ -1,6 +1,6 @@ import MigrationService, { Flag } from '../../server/services/migrationService' import { delius } from '../../server/config' -import { DeliusClient, StaffDetails } from '../../server/data/deliusClient' +import { DeliusClient, DeliusUser, StaffDetails } from '../../server/data/deliusClient' jest.mock('../../server/data/deliusClient') @@ -65,19 +65,6 @@ describe('MigrationService', () => { }) describe('getDeliusRoles', () => { - test('get roles filters out non HDC roles', async () => { - deliusClient.getUser.mockResolvedValue({ - roles: [ - { name: delius.responsibleOfficerRoleId }, - { name: delius.responsibleOfficerVaryRoleId }, - { name: 'other-role' }, - ], - }) - - const result = await migrationService.getDeliusRoles('user-1') - - expect(result).toStrictEqual([delius.responsibleOfficerRoleId, delius.responsibleOfficerVaryRoleId]) - }) test('get roles when missing user', async () => { deliusClient.getUser.mockResolvedValue(null) @@ -98,9 +85,9 @@ describe('MigrationService', () => { email: 'user@gov.uk', } as StaffDetails) deliusClient.getUser.mockResolvedValue({ - roles: [{ name: delius.responsibleOfficerRoleId }], + roles: [delius.responsibleOfficerRoleId], enabled: true, - }) + } as DeliusUser) const result = await migrationService.getStaffDetails('token', 'user-1') @@ -133,9 +120,9 @@ describe('MigrationService', () => { email: 'user@gov.uk', } as StaffDetails) deliusClient.getUser.mockResolvedValue({ - roles: [{ name: delius.responsibleOfficerRoleId }], + roles: [delius.responsibleOfficerRoleId], enabled: true, - }) + } as DeliusUser) const result = await migrationService.getStaffDetails('token', 'user-1') @@ -168,7 +155,7 @@ describe('MigrationService', () => { nomisClient.getAuthUser.mockResolvedValue({ username: 'user-1', enabled: true }) - deliusClient.getStaffDetailsByStaffIdentifier.mockResolvedValue({ staffIdentifier: 1 } as StaffDetails) + deliusClient.getStaffDetailsByStaffIdentifier.mockResolvedValue({ staffId: 1 } as StaffDetails) const result = await migrationService.getStaffDetails('token', 'user-1') @@ -179,7 +166,7 @@ describe('MigrationService', () => { username: 'user-1', }, deliusUser: { - staffIdentifier: 1, + staffId: 1, }, flags: [Flag.UNLINKED_ACCOUNT], licenceUser: { @@ -203,10 +190,11 @@ describe('MigrationService', () => { deliusClient.getStaffDetailsByStaffIdentifier.mockResolvedValue({ username: 'delius-user', - staffIdentifier: 1, + staffId: 1, email: 'delius-user@gov.uk', } as StaffDetails) deliusClient.getUser.mockResolvedValue({ + username: 'delius-user', roles: [], enabled: true, }) @@ -221,7 +209,7 @@ describe('MigrationService', () => { }, deliusUser: { email: 'delius-user@gov.uk', - staffIdentifier: 1, + staffId: 1, username: 'delius-user', }, flags: [Flag.REQUIRES_RO_ROLE, Flag.EMAIL_MISMATCH, Flag.USERNAME_MISMATCH], @@ -247,12 +235,12 @@ describe('MigrationService', () => { deliusClient.getStaffDetailsByStaffIdentifier.mockResolvedValue({ username: 'delius-user', - staffIdentifier: 1, + staffId: 1, } as StaffDetails) deliusClient.getUser.mockResolvedValue({ roles: [], enabled: true, - }) + } as DeliusUser) const result = await migrationService.getStaffDetails('token', 'user-1') @@ -263,7 +251,7 @@ describe('MigrationService', () => { username: 'user-1', }, deliusUser: { - staffIdentifier: 1, + staffId: 1, username: 'delius-user', }, flags: [Flag.REQUIRES_RO_ROLE, Flag.EMAIL_MISMATCH, Flag.USERNAME_MISMATCH], @@ -316,7 +304,8 @@ describe('MigrationService', () => { email: 'user@gov.uk', } as StaffDetails) deliusClient.getUser.mockResolvedValue({ - roles: [{ name: delius.responsibleOfficerRoleId }], + username: 'user-1', + roles: [delius.responsibleOfficerRoleId], enabled: true, }) @@ -356,7 +345,8 @@ describe('MigrationService', () => { email: 'user@gov.uk', } as StaffDetails) deliusClient.getUser.mockResolvedValue({ - roles: [{ name: delius.responsibleOfficerRoleId }], + username: 'user-1', + roles: [delius.responsibleOfficerRoleId], enabled: true, }) diff --git a/test/services/roService.test.ts b/test/services/roService.test.ts index bcfdc4e08..2fdff020a 100644 --- a/test/services/roService.test.ts +++ b/test/services/roService.test.ts @@ -1,11 +1,5 @@ import { RoService } from '../../server/services/roService' -import { - CommunityOrPrisonOffenderManager, - DeliusClient, - ManagedOffender, - ProbationArea, - Team, -} from '../../server/data/deliusClient' +import { DeliusClient, StaffDetails } from '../../server/data/deliusClient' jest.mock('../../server/data/deliusClient') @@ -28,12 +22,13 @@ describe('roService', () => { omEndDate: undefined, } - const roPrisoners = [ - { ...prototypeRoPrisoner, nomsNumber: 'A' }, - { ...prototypeRoPrisoner, nomsNumber: 'B' }, - { ...prototypeRoPrisoner, nomsNumber: 'C' }, - ] - const staffDetails = { staffCode: 'N02A008', staffIdentifier: 1, staff: { forenames: 'x', surname: 'x' }, teams: [] } + const roPrisoners = ['A', 'B', 'C'] + const staffDetails: StaffDetails = { + code: 'N02A008', + staffId: 1, + name: { forenames: 'x', surname: 'x' }, + teams: [], + } beforeEach(() => { nomisClient = { @@ -44,7 +39,7 @@ describe('roService', () => { const nomisClientBuilder = jest.fn().mockReturnValue(nomisClient) deliusClient = new DeliusClient(undefined) as jest.Mocked - deliusClient.getROPrisonersByStaffIdentifier.mockResolvedValue(roPrisoners) + deliusClient.getManagedPrisonerIdsByStaffId.mockResolvedValue(roPrisoners) deliusClient.getStaffDetailsByStaffIdentifier.mockResolvedValue(staffDetails) deliusClient.getStaffDetailsByUsername.mockResolvedValue(staffDetails) @@ -81,35 +76,28 @@ describe('roService', () => { describe('getROPrisoners', () => { test('should call getROPrisoners from deliusClient && getOffenderSentencesByNomisId from nomisClient', async () => { - deliusClient.getROPrisonersByStaffIdentifier.mockResolvedValue(roPrisoners) + deliusClient.getManagedPrisonerIdsByStaffId.mockResolvedValue(roPrisoners) await service.getROPrisonersForStaffIdentifier(123, 'token') - expect(deliusClient.getROPrisonersByStaffIdentifier).toHaveBeenCalled() + expect(deliusClient.getManagedPrisonerIdsByStaffId).toHaveBeenCalled() expect(nomisClient.getOffenderSentencesByNomisId).toHaveBeenCalled() expect(nomisClient.getOffenderSentencesByNomisId).toHaveBeenCalledWith(['A', 'B', 'C']) }) test('should not call getOffenderSentencesByNomisId when no results from getROPrisoners', async () => { - deliusClient.getROPrisonersByStaffIdentifier.mockResolvedValue([]) + deliusClient.getManagedPrisonerIdsByStaffId.mockResolvedValue([]) await service.getROPrisonersForStaffIdentifier(123, 'token') - expect(deliusClient.getROPrisonersByStaffIdentifier).toHaveBeenCalled() + expect(deliusClient.getManagedPrisonerIdsByStaffId).toHaveBeenCalled() expect(nomisClient.getOffenderSentencesByNomisId).toHaveBeenCalled() }) - test('should not call getOffenderSentencesByNomisId when no offender numbers are returned from getROPrisoners', async () => { - deliusClient.getROPrisonersByStaffIdentifier.mockResolvedValue([{}, {}, {}] as ManagedOffender[]) - await service.getROPrisonersForStaffIdentifier(123, 'token') - expect(deliusClient.getROPrisonersByStaffIdentifier).toHaveBeenCalled() - expect(nomisClient.getOffenderSentencesByNomisId).toHaveBeenCalledWith([]) - }) - test('should return empty array and explanation message if no eligible releases found', async () => { - deliusClient.getROPrisonersByStaffIdentifier.mockResolvedValue([]) + deliusClient.getManagedPrisonerIdsByStaffId.mockResolvedValue([]) const result = await service.getROPrisonersForStaffIdentifier(123, 'token') expect(result).toEqual([]) }) test('should return empty array when staff member not found in delius', async () => { - deliusClient.getROPrisonersByStaffIdentifier.mockResolvedValue(undefined) + deliusClient.getManagedPrisonerIdsByStaffId.mockResolvedValue(undefined) const result = await service.getROPrisonersForStaffIdentifier(123, 'token') expect(result).toEqual(null) }) @@ -117,33 +105,27 @@ describe('roService', () => { describe('findResponsibleOfficer', () => { test('should call the api with the offenderNo', async () => { - deliusClient.getAllOffenderManagers.mockResolvedValue([]) + deliusClient.getCommunityManager.mockResolvedValue(null) await service.findResponsibleOfficer('123', 'token') expect(nomisClient.getBooking).toHaveBeenCalled() expect(nomisClient.getBooking).toHaveBeenCalledWith('123') - expect(deliusClient.getAllOffenderManagers).toHaveBeenCalled() - expect(deliusClient.getAllOffenderManagers).toHaveBeenCalledWith(1) + expect(deliusClient.getCommunityManager).toHaveBeenCalled() + expect(deliusClient.getCommunityManager).toHaveBeenCalledWith(1) }) test('should return the found COM', () => { - deliusClient.getAllOffenderManagers.mockResolvedValue([ - { - isResponsibleOfficer: true, - staff: { forenames: 'Jo', surname: 'Smith' }, - staffCode: 'CODE-1', - staffId: 1, - isUnallocated: false, - team: { - localDeliveryUnit: { code: 'LDU-1', description: 'LDU-1 Description' }, - code: 'TEAM_1', - description: 'The Team', - } as Team, - probationArea: { code: 'PROB-1', description: 'PROB-1 Description' } as ProbationArea, - } as CommunityOrPrisonOffenderManager, - ]) + deliusClient.getCommunityManager.mockResolvedValue({ + code: 'CODE-1', + staffId: 1, + name: { forenames: 'Jo', surname: 'Smith' }, + team: { code: 'TEAM_1', description: 'The Team' }, + provider: { code: 'PROB-1', description: 'PROB-1 Description' }, + localAdminUnit: { code: 'LDU-1', description: 'LDU-1 Description' }, + isUnallocated: false, + }) const expectedComData = { deliusId: 'CODE-1', @@ -162,26 +144,13 @@ describe('roService', () => { return expect(service.findResponsibleOfficer('123', 'token')).resolves.toEqual(expectedComData) }) - test('offender has not been assigned a COM', () => { - deliusClient.getAllOffenderManagers.mockResolvedValue([ - { isPrisonOffenderManager: true } as CommunityOrPrisonOffenderManager, - ]) - - const expectedComData = { - code: 'NO_COM_ASSIGNED', - message: 'Offender has not been assigned a COM: 1', - } - - return expect(service.findResponsibleOfficer('123', 'token')).resolves.toEqual(expectedComData) - }) - test('should throw if error in api when getting ro', () => { - deliusClient.getAllOffenderManagers.mockRejectedValue(new Error('dead')) + deliusClient.getCommunityManager.mockRejectedValue(new Error('dead')) return expect(service.findResponsibleOfficer('123', 'token')).rejects.toEqual(Error('dead')) }) test('should return message when 404 in api when getting RO relationship', () => { - deliusClient.getAllOffenderManagers.mockResolvedValue(undefined) + deliusClient.getCommunityManager.mockResolvedValue(undefined) return expect(service.findResponsibleOfficer('123', 'token')).resolves.toEqual({ code: 'NO_OFFENDER_NUMBER', message: 'Offender number not entered in delius', diff --git a/types/licences.d.ts b/types/licences.d.ts index 55e9e00d7..2c74f2b5a 100644 --- a/types/licences.d.ts +++ b/types/licences.d.ts @@ -1,4 +1,4 @@ -import { ProbationAreaSummary } from '../server/data/deliusClient' +import { KeyValue } from '../server/data/deliusClient' export interface ResponsibleOfficer { deliusId: string @@ -133,7 +133,7 @@ export interface LduStatus { } export interface LduService { - getAllProbationAreas: () => Promise> + getAllProbationAreas: () => Promise> getProbationArea: (probationAreaCode: string) => Promise updateActiveLdus: (probationAreaCode: string, activeLdus: string[]) => Promise }