UNPKG

@ministryofjustice/hmpps-digital-prison-reporting-frontend

Version:

The Digital Prison Reporting Frontend contains templates and code to help display data effectively in UI applications.

359 lines (330 loc) 10.8 kB
/* eslint-disable @typescript-eslint/no-explicit-any */ import type { RequestHandler, Router } from 'express' import AsyncPollingUtils from '../components/_async/async-polling/utils' import AsyncRequestListUtils from '../components/user-reports/requested/utils' import UserReportsListUtils from '../components/user-reports/utils' import ErrorSummaryUtils from '../components/error-summary/utils' import AysncRequestUtils from '../components/_async/async-request/utils' import DashboardUtils from '../components/_dashboards/dashboard/utils' import LocalsHelper from '../utils/localsHelper' import AsyncReportUtils from '../utils/report/asyncReportUtils' import logger from '../utils/logger' // Types import { Services } from '../types/Services' import { ReportType } from '../types/UserReports' import { RequestDataResult } from '../types/AsyncReportUtils' export default function routes({ router, services, layoutPath, prefix, }: { router: Router services: Services layoutPath: string prefix: string }) { logger.info('Initialiasing routes: Async reports') /** * * * * * * * * * * * * * * * * * * ERROR & AUTH ROUTES * * * ** * * * * * * * * * * * * * * */ /** * Error Handler * * @param {Request} req * @param {Response} res * @param {NextFunction} next */ const asyncErrorHandler: RequestHandler = async (req, res) => { logger.error(`Error: ${JSON.stringify(req.body)}`) res.render(`dpr/views/async-error`, { layoutPath, ...req.body, ...req.params, error: req.body.error, params: req.params, }) } /** * Authorisation middleware handler * * @param {Request} req * @param {Response} res * @param {NextFunction} next */ const isAuthorisedToViewReport: RequestHandler = async (req, res, next) => { const definition = await AysncRequestUtils.getDefintionByType(req, res, next, services) req.body.definition = definition if (definition?.authorised !== undefined && !definition.authorised) { await unauthorisedReportHandler(req, res, next) } else { next() } } /** * Unauthorised route handler * * @param {Request} req * @param {Response} res * @param {NextFunction} next */ const unauthorisedReportHandler: RequestHandler = async (req, res) => { res.render(`dpr/views/unauthorised-report`, { layoutPath, ...req.body, }) } /** * * * * * * * * * * * * * * * * * * STAGE 1: REQUEST ROUTES * * * ** * * * * * * * * * * * * * * */ /** * REQUEST: Render request handler * * @param {Request} req * @param {Response} res * @param {NextFunction} next */ const renderRequestHandler: RequestHandler = async (req, res, next) => { try { const requestRenderData = <RequestDataResult>await AysncRequestUtils.renderRequest({ req, res, services, next, }) res.render(`dpr/views/async-request`, { layoutPath, postEndpoint: '/dpr/requestReport/', ...requestRenderData, }) } catch (error) { req.body.title = 'Request failed' req.body.errorDescription = `Your ${req.params.type} has failed to generate.` req.body.error = ErrorSummaryUtils.handleError(error, req.params.type) next() } } /** * REQUEST: Make request * * @param {Request} req * @param {Response} res * @param {NextFunction} next */ const asyncRequestHandler: RequestHandler = async (req, res, next) => { try { const redirectToPollingPage = await AysncRequestUtils.request({ req, res, services, next, }) if (redirectToPollingPage) { res.redirect(redirectToPollingPage) } else { res.end() } } catch (error) { const dprError = ErrorSummaryUtils.handleError(error, req.params.type) const filters = AysncRequestUtils.getFiltersFromReqBody(req) req.body = { title: 'Request Failed', errorDescription: `Your ${req.params.type} has failed to generate.`, error: dprError, retry: true, filters, ...req.body, } next() } } /** * REQUEST: Cancel request * * @param {Request} req * @param {Response} res * @param {NextFunction} next */ const cancelRequestHandler: RequestHandler = async (req, res, next) => { try { await AysncRequestUtils.cancelRequest({ req, res, services, }) res.end() } catch (error) { req.body.title = 'Failed to abort request' req.body.errorDescription = 'We were unable to abort the report request for the following reason:' req.body.error = ErrorSummaryUtils.handleError(error) next() } } /** * REQUEST: Remove request * * @param {Request} req * @param {Response} res * @param {NextFunction} next */ const removeRequestedItemHandler: RequestHandler = async (req, res, next) => { const { userId } = LocalsHelper.getValues(res) try { await services.requestedReportService.removeReport(req.body.executionId, userId) res.end() } catch (error) { req.body.title = 'Failed to abort request' req.body.errorDescription = 'We were unable to abort the report request for the following reason:' req.body.error = ErrorSummaryUtils.handleError(error) next() } } /** * * * * * * * * * * * * * * * * * * STAGE 2: POLLING ROUTES * * * ** * * * * * * * * * * * * * * */ /** * POLLING: Render Polling * * @param {Request} req * @param {Response} res * @param {NextFunction} next */ const pollingHandler: RequestHandler = async (req, res, next) => { try { const pollingRenderData = await AsyncPollingUtils.renderPolling({ req, res, services, next, }) res.render(`dpr/views/async-polling`, { layoutPath, ...pollingRenderData, }) } catch (error) { req.body.title = 'Failed to retrieve report status' req.body.errorDescription = 'We were unable to retrieve the report status:' req.body.error = ErrorSummaryUtils.handleError(error) next() } } /** * POLLING: Get Status * * @param {Request} req * @param {Response} res * @param {NextFunction} next */ const getStatusHandler: RequestHandler = async (req, res, next) => { try { const response = await AsyncRequestListUtils.getRequestStatus({ req, res, services }) res.send({ status: response.status }) } catch (error) { res.send({ status: 'FAILED' }) } } /** * POLLING: Get Expired Status * * @param {Request} req * @param {Response} res * @param {NextFunction} next */ const getExpiredStatus: RequestHandler = async (req, res, next) => { try { const response = await UserReportsListUtils.getExpiredStatus({ req, res, services, storeService: services.requestedReportService, }) res.send({ isExpired: response }) } catch (error) { res.send({ status: 'FAILED' }) } } /** * * * * * * * * * * * * * * * * * * STAGE 3: VIEW REPORT ROUTES * * * ** * * * * * * * * * * * * * * */ /** * VIEW REPORT: Render report * * @param {Request} req * @param {Response} res * @param {NextFunction} next */ const viewReportHandler: RequestHandler = async (req, res, next) => { const { type } = req.params try { let template let renderData const params = { req, res, services, next } if (type === ReportType.REPORT) { template = 'report' renderData = await AsyncReportUtils.renderReport(params) } if (type === ReportType.DASHBOARD) { template = 'dashboard' renderData = await DashboardUtils.renderAsyncDashboard(params) } res.render(`dpr/views/${template}`, { layoutPath, ...renderData, }) } catch (error) { const dprError = ErrorSummaryUtils.handleError(error, req.params.type) let refreshLink if (dprError.status === 'EXPIRED') { const { userId } = LocalsHelper.getValues(res) refreshLink = await services.recentlyViewedService.asyncSetToExpiredByTableId(req.params.tableId, userId) } req.body.title = `Failed to retrieve ${type}` req.body.error = dprError req.body.refreshLink = refreshLink next() } } const listingHandler: RequestHandler = async (req, res, next) => { const { requestedReports } = LocalsHelper.getValues(res) res.render(`dpr/views/async-reports`, { title: 'Requested reports', layoutPath, ...(await UserReportsListUtils.renderList({ reportsData: requestedReports, filterFunction: AsyncRequestListUtils.filterReports, res, type: 'requested', })), }) } // 1 - REQUEST router.get( `${prefix}/async/:type/:reportId/:id/request`, isAuthorisedToViewReport, renderRequestHandler, asyncErrorHandler, ) router.post('/dpr/requestReport/', asyncRequestHandler, asyncErrorHandler) // 2 - POLLING router.get(`${prefix}/async/:type/:reportId/:id/request/:executionId`, pollingHandler, asyncErrorHandler) router.post('/dpr/getStatus/', getStatusHandler) router.post('/dpr/cancelRequest/', cancelRequestHandler, asyncErrorHandler) // 3 - VIEw REPORT const viewReportPaths = [ `${prefix}/async/:type/:reportId/:id/request/:tableId/report`, `${prefix}/async/:type/:reportId/:id/request/:tableId/report/:download`, ] router.get(viewReportPaths, isAuthorisedToViewReport, viewReportHandler, asyncErrorHandler) // Homepage widget routes router.post('/dpr/removeRequestedItem/', removeRequestedItemHandler, asyncErrorHandler) router.post('/dpr/getRequestedExpiredStatus/', getExpiredStatus) router.get(`${prefix}/async-reports/requested`, listingHandler) }