diff --git a/server/routes/adhocReportActions/addhocActionController.ts b/server/routes/adhocReportActions/addhocActionController.ts new file mode 100644 index 00000000..cb93b15d --- /dev/null +++ b/server/routes/adhocReportActions/addhocActionController.ts @@ -0,0 +1,53 @@ +/* eslint-disable no-await-in-loop */ +import { Request, Response } from 'express' +import type ReportService from '../../services/reportService' +import { SystemToken } from '../../types/uof' +import NomisMappingService from '../../services/nomisMappingService' +import logger from '../../../log' + +export default class AddhocActionController { + constructor( + private readonly reportService: ReportService, + private readonly nomisMappingService: NomisMappingService, + private readonly systemToken: SystemToken + ) {} + + public updateReport = async (req: Request, res: Response): Promise => { + const { fromReportId, toReportId } = req.params + const fromId = parseInt(fromReportId, 10) + const toId = parseInt(toReportId, 10) + + for (let reportId = fromId; reportId <= toId; reportId += 1) { + try { + const report = await this.reportService.getReport(req.user.username, reportId) + + const { locationId } = report.form.incidentDetails + + if (report.form.incidentDetails.incidentLocationId) { + logger.info( + `Ad-hoc exercise: Report with id ${reportId} already has form_response.incidentDetails.incidentLocationId set` + ) + } else if (locationId) { + const token = await this.systemToken(req.user.username) + + const { dpsLocationId } = + await this.nomisMappingService.getDpsLocationDetailsHavingCorrespondingNomisLocationId(token, locationId) + + report.form.incidentDetails.incidentLocationId = dpsLocationId + + const updatedSection = report.form.incidentDetails + + await this.reportService.update(res.locals.user, reportId, 'incidentDetails', updatedSection) + + logger.info( + `Ad-hoc exercise: Updated report with id ${reportId} by adding form_response.incidentDetails.incidentLocationId mapped to nomis locationId of ${locationId}` + ) + } + } catch (error) { + logger.error(`Ad-hoc exercise: Update of report with id ${reportId} failed.`, error.message) + } + } + + res.send(`Reports with id's ${fromId} to ${toId} processed`) + } +} diff --git a/server/routes/adhocReportActions/adhocActionController.test.ts b/server/routes/adhocReportActions/adhocActionController.test.ts new file mode 100644 index 00000000..9edd05e4 --- /dev/null +++ b/server/routes/adhocReportActions/adhocActionController.test.ts @@ -0,0 +1,159 @@ +import { Request, Response } from 'express' +import { ReportService, NomisMappingService } from '../../services' +import AddhocActionController from './addhocActionController' +import { LocationMapping } from '../../data/nomisMappingClientTypes' +import logger from '../../../log' + +jest.mock('../../services/nomisMappingService') +jest.mock('../../services/reportService') +jest.mock('../../../log') + +const reportService = new ReportService(null, null, null, null, null, null) as jest.Mocked +const nomisMappingService = new NomisMappingService(null) as jest.Mocked + +reportService.getReport = jest.fn() +let req = jest.fn() as unknown as jest.Mocked +let res = jest.fn() as unknown as jest.Mocked +res.send = jest.fn() + +req = { + params: { + fromReportId: '1', + toReportId: '2', + }, + user: { + username: 'user_name', + token: '', + refreshToken: '', + refreshTime: undefined, + }, +} as any + +res = { + locals: { + user: { + username: 'user_name', + token: '', + refreshToken: '', + refreshTime: undefined, + }, + }, + send: jest.fn(), +} as any + +const systemToken = jest.fn() +systemToken.mockResolvedValue('system-token-1') + +const controller = new AddhocActionController(reportService, nomisMappingService, systemToken) + +describe('Adhoc actions', () => { + afterEach(() => { + jest.resetAllMocks() + }) + + describe('updateReport', () => { + it('is successful', async () => { + await controller.updateReport(req, res) + expect(res.send).toHaveBeenCalledWith("Reports with id's 1 to 2 processed") + }) + + it('calls the report service to get reports correctly', async () => { + await controller.updateReport(req, res) + + expect(reportService.getReport).toHaveBeenCalledTimes(2) + expect(reportService.getReport).toHaveBeenCalledWith('user_name', 1) + expect(reportService.getReport).toHaveBeenCalledWith('user_name', 2) + }) + + it('does not do update if incidentLocationId already exists', async () => { + const report = { + form: { + incidentDetails: { + incidentLocationId: 'some-uuid', + plannedUseOfForce: false, + }, + }, + } + + reportService.getReport.mockResolvedValue(report as any) + await controller.updateReport(req, res) + + expect(reportService.update).not.toHaveBeenCalled() + + expect(logger.info).toHaveBeenCalledWith( + 'Ad-hoc exercise: Report with id 1 already has form_response.incidentDetails.incidentLocationId set' + ) + }) + + it('should call update processes but only for a reports that exist', async () => { + const report = { + form: { + incidentDetails: { + locationId: 123456, + plannedUseOfForce: false, + }, + }, + } + + reportService.getReport.mockResolvedValueOnce(report as any) + reportService.getReport.mockRejectedValueOnce(Error(`Report does not exist: 2`)) + + nomisMappingService.getDpsLocationDetailsHavingCorrespondingNomisLocationId.mockResolvedValue({ + dpsLocationId: 'some-uuid', + } as LocationMapping) + + await controller.updateReport(req, res) + + expect(reportService.update).toHaveBeenCalledWith( + { refreshTime: undefined, refreshToken: '', token: '', username: 'user_name' }, + 1, + 'incidentDetails', + { incidentLocationId: 'some-uuid', locationId: 123456, plannedUseOfForce: false } + ) + + expect(logger.info).toHaveBeenCalledWith( + 'Ad-hoc exercise: Updated report with id 1 by adding form_response.incidentDetails.incidentLocationId mapped to nomis locationId of 123456' + ) + expect(logger.error).toHaveBeenLastCalledWith( + 'Ad-hoc exercise: Update of report with id 2 failed.', + 'Report does not exist: 2' + ) + }) + + it('should catch errors', async () => { + req = { + params: { + fromReportId: '1', + toReportId: '1', + }, + user: { + username: 'user_name', + token: '', + refreshToken: '', + refreshTime: undefined, + }, + } as any + + const report = { + form: { + incidentDetails: { + locationId: 123456, + plannedUseOfForce: false, + }, + }, + } + + reportService.getReport.mockResolvedValue(report as any) + + nomisMappingService.getDpsLocationDetailsHavingCorrespondingNomisLocationId.mockResolvedValue({ + dpsLocationId: 'some-uuid', + } as LocationMapping) + + reportService.update.mockRejectedValue(new Error()) + + await controller.updateReport(req, res) + + expect(logger.error).toHaveBeenLastCalledWith('Ad-hoc exercise: Update of report with id 1 failed.', '') + }) + }) +}) diff --git a/server/routes/adhocReportActions/index.ts b/server/routes/adhocReportActions/index.ts new file mode 100644 index 00000000..e0565764 --- /dev/null +++ b/server/routes/adhocReportActions/index.ts @@ -0,0 +1,17 @@ +import express, { Router } from 'express' +import asyncMiddleware from '../../middleware/asyncMiddleware' + +import AddhocActionController from './addhocActionController' +import { Services } from '../../services' + +export default function adhocReportActionRoutes(services: Services): Router { + const { nomisMappingService, reportService, systemToken } = services + + const router = express.Router() + + const controller = new AddhocActionController(reportService, nomisMappingService, systemToken) + + router.get('/update-location-id/:fromReportId/:toReportId', controller.updateReport) + + return router +} diff --git a/server/routes/index.ts b/server/routes/index.ts index e59ce928..aaed8d13 100644 --- a/server/routes/index.ts +++ b/server/routes/index.ts @@ -4,6 +4,8 @@ import flash from 'connect-flash' import creatingReportsRoutes from './creatingReports' import maintainingReportsRoutes from './maintainingReports' import viewingReportsRoutes from './viewingReports' +import adhocReportActionRoutes from './adhocReportActions' + import apiRoutes from './api' import csrf from '../middleware/csrfMiddleware' @@ -22,6 +24,7 @@ export default function Index(authenticationMiddleware: Handler, services: Servi router.use(creatingReportsRoutes(services)) router.use(maintainingReportsRoutes(services)) router.use(viewingReportsRoutes(services)) + router.use(adhocReportActionRoutes(services)) router.use('/api/', apiRoutes(authenticationMiddleware, services)) return router