@ministryofjustice/hmpps-digital-prison-reporting-frontend 3.10.2 → 3.10.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -136,4 +136,19 @@ exports.default = {
136
136
  }
137
137
  return redirect;
138
138
  },
139
+ handleError: (error, req) => {
140
+ const filters = Object.keys(req.body)
141
+ .filter((attr) => attr.includes('filters.'))
142
+ .filter((attr) => !!req.body[attr])
143
+ .map((attr) => {
144
+ return { name: attr, value: req.body[attr] };
145
+ });
146
+ return {
147
+ title: 'Request Failed',
148
+ description: 'Your report has failed to generate. The issue has been reported to admin staff',
149
+ retry: true,
150
+ error: error.data,
151
+ filters,
152
+ };
153
+ },
139
154
  };
@@ -1,3 +1,4 @@
1
+ import { Request } from 'express'
1
2
  import Dict = NodeJS.Dict
2
3
  import { components } from '../../types/api'
3
4
  import FilterUtils from '../filters/utils'
@@ -161,4 +162,20 @@ export default {
161
162
 
162
163
  return redirect
163
164
  },
165
+
166
+ handleError: (error: Dict<string>, req: Request) => {
167
+ const filters = Object.keys(req.body)
168
+ .filter((attr) => attr.includes('filters.'))
169
+ .filter((attr) => !!req.body[attr])
170
+ .map((attr) => {
171
+ return { name: attr, value: req.body[attr] }
172
+ })
173
+ return {
174
+ title: 'Request Failed',
175
+ description: 'Your report has failed to generate. The issue has been reported to admin staff',
176
+ retry: true,
177
+ error: error.data,
178
+ filters,
179
+ }
180
+ },
164
181
  }
@@ -17,6 +17,8 @@ const getStatus = async (token, reportId, variantId, executionId, currentStatus,
17
17
  errorMessage = data.userMessage;
18
18
  status = currentStatus === AsyncReport_1.RequestStatus.FINISHED ? AsyncReport_1.RequestStatus.EXPIRED : AsyncReport_1.RequestStatus.FAILED;
19
19
  }
20
+ if (typeof status === 'number')
21
+ status = AsyncReport_1.RequestStatus.FAILED;
20
22
  const res = {
21
23
  status,
22
24
  ...(errorMessage && { errorMessage }),
@@ -42,43 +44,37 @@ exports.default = {
42
44
  getStatus,
43
45
  renderPolling: async ({ req, res, dataSources, asyncReportsStore, next }) => {
44
46
  var _a;
45
- try {
46
- const { token } = res.locals.user || 'token';
47
- const { reportId, variantId, executionId } = req.params;
48
- let reportData = await asyncReportsStore.getReportByExecutionId(executionId);
49
- let statusResponse;
50
- if ((0, exports.timeoutRequest)(reportData.timestamp.requested)) {
51
- statusResponse = {
52
- status: AsyncReport_1.RequestStatus.FAILED,
53
- errorMessage: 'Request taking too long. Request Halted',
54
- };
55
- }
56
- else {
57
- statusResponse = await getStatus(token, reportId, variantId, executionId, reportData.status, dataSources, asyncReportsStore, reportData.dataProductDefinitionsPath);
58
- }
59
- const { status, errorMessage } = statusResponse;
60
- if (statusResponse.reportData)
61
- reportData = statusResponse.reportData;
62
- return {
63
- pollingRenderData: {
64
- reportName: reportData.reportName,
65
- variantName: reportData.name,
66
- variantDescription: reportData.description,
67
- executionId,
68
- reportId,
69
- variantId,
70
- status,
71
- tableId: reportData.tableId,
72
- querySummary: reportData.query.summary,
73
- ...(((_a = reportData.url.report) === null || _a === void 0 ? void 0 : _a.fullUrl) && { reportUrl: reportData.url.report.fullUrl }),
74
- ...(reportData.url.request.fullUrl && { requestUrl: reportData.url.request.fullUrl }),
75
- ...(errorMessage && { errorMessage }),
76
- },
47
+ const { token } = res.locals.user || 'token';
48
+ const { reportId, variantId, executionId } = req.params;
49
+ let reportData = await asyncReportsStore.getReportByExecutionId(executionId);
50
+ let statusResponse;
51
+ if ((0, exports.timeoutRequest)(reportData.timestamp.requested)) {
52
+ statusResponse = {
53
+ status: AsyncReport_1.RequestStatus.FAILED,
54
+ errorMessage: 'Request taking too long. Request Halted',
77
55
  };
78
56
  }
79
- catch (error) {
80
- next(error);
81
- return false;
57
+ else {
58
+ statusResponse = await getStatus(token, reportId, variantId, executionId, reportData.status, dataSources, asyncReportsStore, reportData.dataProductDefinitionsPath);
82
59
  }
60
+ const { status, errorMessage } = statusResponse;
61
+ if (statusResponse.reportData)
62
+ reportData = statusResponse.reportData;
63
+ return {
64
+ pollingRenderData: {
65
+ reportName: reportData.reportName,
66
+ variantName: reportData.name,
67
+ variantDescription: reportData.description,
68
+ executionId,
69
+ reportId,
70
+ variantId,
71
+ status,
72
+ tableId: reportData.tableId,
73
+ querySummary: reportData.query.summary,
74
+ ...(((_a = reportData.url.report) === null || _a === void 0 ? void 0 : _a.fullUrl) && { reportUrl: reportData.url.report.fullUrl }),
75
+ ...(reportData.url.request.fullUrl && { requestUrl: reportData.url.request.fullUrl }),
76
+ ...(errorMessage && { errorMessage }),
77
+ },
78
+ };
83
79
  },
84
80
  };
@@ -35,6 +35,7 @@ const getStatus = async (
35
35
  status = currentStatus === RequestStatus.FINISHED ? RequestStatus.EXPIRED : RequestStatus.FAILED
36
36
  }
37
37
 
38
+ if (typeof status === 'number') status = RequestStatus.FAILED
38
39
  const res: GetStatusUtilsResponse = {
39
40
  status,
40
41
  ...(errorMessage && { errorMessage }),
@@ -57,7 +58,6 @@ export const timeoutRequest = (requestTime: Date) => {
57
58
  const diffMins = Math.round(((diffMs % 86400000) % 3600000) / 60000)
58
59
  return diffMins >= TIMEOUT_MINS_MAX
59
60
  }
60
-
61
61
  interface GetStatusUtilsResponse {
62
62
  status: RequestStatus
63
63
  errorMessage: string
@@ -67,51 +67,46 @@ interface GetStatusUtilsResponse {
67
67
  export default {
68
68
  getStatus,
69
69
  renderPolling: async ({ req, res, dataSources, asyncReportsStore, next }: AsyncReportUtilsParams) => {
70
- try {
71
- const { token } = res.locals.user || 'token'
72
- const { reportId, variantId, executionId } = req.params
73
- let reportData = await asyncReportsStore.getReportByExecutionId(executionId)
70
+ const { token } = res.locals.user || 'token'
71
+ const { reportId, variantId, executionId } = req.params
72
+ let reportData = await asyncReportsStore.getReportByExecutionId(executionId)
74
73
 
75
- let statusResponse
76
- if (timeoutRequest(reportData.timestamp.requested)) {
77
- statusResponse = {
78
- status: RequestStatus.FAILED,
79
- errorMessage: 'Request taking too long. Request Halted',
80
- }
81
- } else {
82
- statusResponse = await getStatus(
83
- token,
84
- reportId,
85
- variantId,
86
- executionId,
87
- reportData.status,
88
- dataSources,
89
- asyncReportsStore,
90
- reportData.dataProductDefinitionsPath,
91
- )
74
+ let statusResponse
75
+ if (timeoutRequest(reportData.timestamp.requested)) {
76
+ statusResponse = {
77
+ status: RequestStatus.FAILED,
78
+ errorMessage: 'Request taking too long. Request Halted',
92
79
  }
93
- const { status, errorMessage } = statusResponse
94
- if (statusResponse.reportData) reportData = statusResponse.reportData
80
+ } else {
81
+ statusResponse = await getStatus(
82
+ token,
83
+ reportId,
84
+ variantId,
85
+ executionId,
86
+ reportData.status,
87
+ dataSources,
88
+ asyncReportsStore,
89
+ reportData.dataProductDefinitionsPath,
90
+ )
91
+ }
92
+ const { status, errorMessage } = statusResponse
93
+ if (statusResponse.reportData) reportData = statusResponse.reportData
95
94
 
96
- return {
97
- pollingRenderData: {
98
- reportName: reportData.reportName,
99
- variantName: reportData.name,
100
- variantDescription: reportData.description,
101
- executionId,
102
- reportId,
103
- variantId,
104
- status,
105
- tableId: reportData.tableId,
106
- querySummary: reportData.query.summary,
107
- ...(reportData.url.report?.fullUrl && { reportUrl: reportData.url.report.fullUrl }),
108
- ...(reportData.url.request.fullUrl && { requestUrl: reportData.url.request.fullUrl }),
109
- ...(errorMessage && { errorMessage }),
110
- },
111
- }
112
- } catch (error) {
113
- next(error)
114
- return false
95
+ return {
96
+ pollingRenderData: {
97
+ reportName: reportData.reportName,
98
+ variantName: reportData.name,
99
+ variantDescription: reportData.description,
100
+ executionId,
101
+ reportId,
102
+ variantId,
103
+ status,
104
+ tableId: reportData.tableId,
105
+ querySummary: reportData.query.summary,
106
+ ...(reportData.url.report?.fullUrl && { reportUrl: reportData.url.report.fullUrl }),
107
+ ...(reportData.url.request.fullUrl && { requestUrl: reportData.url.request.fullUrl }),
108
+ ...(errorMessage && { errorMessage }),
109
+ },
115
110
  }
116
111
  },
117
112
  }
@@ -11,25 +11,19 @@ const utils_2 = __importDefault(require("../async-columns/utils"));
11
11
  const utils_3 = __importDefault(require("../pagination/utils"));
12
12
  const utils_4 = __importDefault(require("../icon-button-list/utils"));
13
13
  const initDataSources = ({ req, res, next, asyncReportsStore, dataSources }) => {
14
- try {
15
- const { token } = res.locals.user || 'token';
16
- const { reportId, reportVariantId, tableId } = req.params;
17
- const { selectedPage = 1, pageSize = 10 } = req.query;
18
- const dataProductDefinitionsPath = req.query.dataProductDefinitionsPath;
19
- const reportDefinitionPromise = dataSources.getDefinition(token, reportId, reportVariantId, dataProductDefinitionsPath);
20
- const reportDataPromise = dataSources.getAsyncReport(token, reportId, reportVariantId, tableId, {
21
- selectedPage: +selectedPage,
22
- pageSize: +pageSize,
23
- dataProductDefinitionsPath,
24
- });
25
- const reportDataCountPromise = dataSources.getAsyncCount(token, tableId);
26
- const stateDataPromise = asyncReportsStore.getReportByTableId(tableId);
27
- return [reportDefinitionPromise, reportDataPromise, reportDataCountPromise, stateDataPromise];
28
- }
29
- catch (error) {
30
- next(error);
31
- return false;
32
- }
14
+ const { token } = res.locals.user || 'token';
15
+ const { reportId, reportVariantId, tableId } = req.params;
16
+ const { selectedPage = 1, pageSize = 10 } = req.query;
17
+ const dataProductDefinitionsPath = req.query.dataProductDefinitionsPath;
18
+ const reportDefinitionPromise = dataSources.getDefinition(token, reportId, reportVariantId, dataProductDefinitionsPath);
19
+ const reportDataPromise = dataSources.getAsyncReport(token, reportId, reportVariantId, tableId, {
20
+ selectedPage: +selectedPage,
21
+ pageSize: +pageSize,
22
+ dataProductDefinitionsPath,
23
+ });
24
+ const reportDataCountPromise = dataSources.getAsyncCount(token, tableId);
25
+ const stateDataPromise = asyncReportsStore.getReportByTableId(tableId);
26
+ return [reportDefinitionPromise, reportDataPromise, reportDataCountPromise, stateDataPromise];
33
27
  };
34
28
  exports.initDataSources = initDataSources;
35
29
  const renderReport = async ({ req, res, next, asyncReportsStore, recentlyViewedStoreService, dataSources, }) => {
@@ -38,8 +32,7 @@ const renderReport = async ({ req, res, next, asyncReportsStore, recentlyViewedS
38
32
  let renderData = {};
39
33
  let reportStateData;
40
34
  if (dataPromises) {
41
- await Promise.all(dataPromises)
42
- .then((resolvedData) => {
35
+ await Promise.all(dataPromises).then((resolvedData) => {
43
36
  const definition = resolvedData[0];
44
37
  const reportData = resolvedData[1];
45
38
  const count = resolvedData[2];
@@ -65,9 +58,6 @@ const renderReport = async ({ req, res, next, asyncReportsStore, recentlyViewedS
65
58
  appliedFilters: query.summary,
66
59
  classification,
67
60
  };
68
- })
69
- .catch((err) => {
70
- next(err);
71
61
  });
72
62
  }
73
63
  if (Object.keys(renderData).length && Object.keys(reportStateData).length) {
@@ -11,30 +11,25 @@ import { AsyncReportUtilsParams } from '../../types/AsyncReportUtils'
11
11
  import { AsyncReportData } from '../../types/AsyncReport'
12
12
 
13
13
  export const initDataSources = ({ req, res, next, asyncReportsStore, dataSources }: AsyncReportUtilsParams) => {
14
- try {
15
- const { token } = res.locals.user || 'token'
16
- const { reportId, reportVariantId, tableId } = req.params
17
- const { selectedPage = 1, pageSize = 10 } = req.query
18
- const dataProductDefinitionsPath = <string>req.query.dataProductDefinitionsPath
19
- const reportDefinitionPromise = dataSources.getDefinition(
20
- token,
21
- reportId,
22
- reportVariantId,
23
- dataProductDefinitionsPath,
24
- )
25
- const reportDataPromise = dataSources.getAsyncReport(token, reportId, reportVariantId, tableId, {
26
- selectedPage: +selectedPage,
27
- pageSize: +pageSize,
28
- dataProductDefinitionsPath,
29
- })
30
- const reportDataCountPromise = dataSources.getAsyncCount(token, tableId)
31
- const stateDataPromise = asyncReportsStore.getReportByTableId(tableId)
14
+ const { token } = res.locals.user || 'token'
15
+ const { reportId, reportVariantId, tableId } = req.params
16
+ const { selectedPage = 1, pageSize = 10 } = req.query
17
+ const dataProductDefinitionsPath = <string>req.query.dataProductDefinitionsPath
18
+ const reportDefinitionPromise = dataSources.getDefinition(
19
+ token,
20
+ reportId,
21
+ reportVariantId,
22
+ dataProductDefinitionsPath,
23
+ )
24
+ const reportDataPromise = dataSources.getAsyncReport(token, reportId, reportVariantId, tableId, {
25
+ selectedPage: +selectedPage,
26
+ pageSize: +pageSize,
27
+ dataProductDefinitionsPath,
28
+ })
29
+ const reportDataCountPromise = dataSources.getAsyncCount(token, tableId)
30
+ const stateDataPromise = asyncReportsStore.getReportByTableId(tableId)
32
31
 
33
- return [reportDefinitionPromise, reportDataPromise, reportDataCountPromise, stateDataPromise]
34
- } catch (error) {
35
- next(error)
36
- return false
37
- }
32
+ return [reportDefinitionPromise, reportDataPromise, reportDataCountPromise, stateDataPromise]
38
33
  }
39
34
 
40
35
  export const renderReport = async ({
@@ -52,40 +47,36 @@ export const renderReport = async ({
52
47
  let renderData = {}
53
48
  let reportStateData: AsyncReportData
54
49
  if (dataPromises) {
55
- await Promise.all(dataPromises)
56
- .then((resolvedData) => {
57
- const definition = resolvedData[0] as unknown as components['schemas']['SingleVariantReportDefinition']
58
- const reportData = <Array<Dict<string>>>resolvedData[1]
59
- const count = <number>resolvedData[2]
60
- reportStateData = <AsyncReportData>resolvedData[3]
50
+ await Promise.all(dataPromises).then((resolvedData) => {
51
+ const definition = resolvedData[0] as unknown as components['schemas']['SingleVariantReportDefinition']
52
+ const reportData = <Array<Dict<string>>>resolvedData[1]
53
+ const count = <number>resolvedData[2]
54
+ reportStateData = <AsyncReportData>resolvedData[3]
61
55
 
62
- const fieldDefinition = definition.variant.specification.fields
63
- const { classification } = definition.variant
56
+ const fieldDefinition = definition.variant.specification.fields
57
+ const { classification } = definition.variant
64
58
 
65
- const columns = ColumnUtils.getColumns(fieldDefinition, <string[]>reqColumns)
66
- const url = parseUrl(req)
67
- const pagination = PaginationUtils.getPaginationData(url, count)
68
- const actions = ReportActionsUtils.initReportActions(definition.variant, reportStateData)
69
- const rows = DataTableUtils.mapData(reportData, fieldDefinition, columns.value)
70
- const head = DataTableUtils.mapAsyncHeader(fieldDefinition, columns.value)
71
- const { reportName, name: variantName, query, description } = reportStateData
59
+ const columns = ColumnUtils.getColumns(fieldDefinition, <string[]>reqColumns)
60
+ const url = parseUrl(req)
61
+ const pagination = PaginationUtils.getPaginationData(url, count)
62
+ const actions = ReportActionsUtils.initReportActions(definition.variant, reportStateData)
63
+ const rows = DataTableUtils.mapData(reportData, fieldDefinition, columns.value)
64
+ const head = DataTableUtils.mapAsyncHeader(fieldDefinition, columns.value)
65
+ const { reportName, name: variantName, query, description } = reportStateData
72
66
 
73
- renderData = {
74
- variantName,
75
- reportName,
76
- description,
77
- rows,
78
- head,
79
- columns,
80
- pagination,
81
- actions,
82
- appliedFilters: query.summary,
83
- classification,
84
- }
85
- })
86
- .catch((err) => {
87
- next(err)
88
- })
67
+ renderData = {
68
+ variantName,
69
+ reportName,
70
+ description,
71
+ rows,
72
+ head,
73
+ columns,
74
+ pagination,
75
+ actions,
76
+ appliedFilters: query.summary,
77
+ classification,
78
+ }
79
+ })
89
80
  }
90
81
 
91
82
  if (Object.keys(renderData).length && Object.keys(reportStateData).length) {
@@ -61,8 +61,7 @@ const setDataFromStatus = (status, requestedReportsData) => {
61
61
  let href;
62
62
  switch (status) {
63
63
  case AsyncReport_1.RequestStatus.FAILED: {
64
- const retryParam = `&retryId=${requestedReportsData.executionId}`;
65
- href = `${requestedReportsData.url.request.fullUrl}${retryParam}`;
64
+ href = `${requestedReportsData.url.polling.fullUrl}`;
66
65
  timestamp = `Failed at: ${new Date(requestedReportsData.timestamp.failed).toLocaleString()}`;
67
66
  break;
68
67
  }
@@ -58,8 +58,7 @@ const setDataFromStatus = (status: RequestStatus, requestedReportsData: AsyncRep
58
58
  let href
59
59
  switch (status) {
60
60
  case RequestStatus.FAILED: {
61
- const retryParam = `&retryId=${requestedReportsData.executionId}`
62
- href = `${requestedReportsData.url.request.fullUrl}${retryParam}`
61
+ href = `${requestedReportsData.url.polling.fullUrl}`
63
62
  timestamp = `Failed at: ${new Date(requestedReportsData.timestamp.failed).toLocaleString()}`
64
63
  break
65
64
  }
@@ -30,16 +30,29 @@ const utils_1 = __importDefault(require("../components/async-filters/utils"));
30
30
  const AsyncReportListUtils = __importStar(require("../components/async-report-list/utils"));
31
31
  const utils_2 = __importDefault(require("../components/async-polling/utils"));
32
32
  function routes({ router, asyncReportsStore, recentlyViewedStoreService, dataSources, layoutPath, templatePath = 'dpr/views/', }) {
33
- // 1 - get filters for the report + make request
34
- router.get('/async-reports/:reportId/:variantId/request', async (req, res, next) => {
35
- res.render(`${templatePath}async-request`, {
36
- title: 'Request Report',
33
+ const asyncErrorHandler = async (req, res) => {
34
+ res.render(`${templatePath}/async-error`, {
37
35
  layoutPath,
38
- postEndpoint: '/requestReport/',
39
- ...(await utils_1.default.renderFilters({ req, res, dataSources, next })),
36
+ ...req.body,
40
37
  });
41
- });
42
- // 2 - handle the post request to request the report data
38
+ };
39
+ const getReportFiltersHandler = async (req, res, next) => {
40
+ try {
41
+ const filtersRenderData = await utils_1.default.renderFilters({ req, res, dataSources, next });
42
+ res.render(`${templatePath}async-request`, {
43
+ title: 'Request Report',
44
+ layoutPath,
45
+ postEndpoint: '/requestReport/',
46
+ ...filtersRenderData,
47
+ });
48
+ }
49
+ catch (error) {
50
+ req.body.title = 'Report Failed';
51
+ req.body.description = 'Your report has failed to generate. The issue has been reported to admin staff';
52
+ req.body.error = error.data;
53
+ next();
54
+ }
55
+ };
43
56
  const asyncRequestHandler = async (req, res, next) => {
44
57
  try {
45
58
  const redirectToPollingPage = await utils_1.default.requestReport({
@@ -58,46 +71,59 @@ function routes({ router, asyncReportsStore, recentlyViewedStoreService, dataSou
58
71
  }
59
72
  }
60
73
  catch (error) {
61
- req.body.error = JSON.parse(error.text);
74
+ req.body = {
75
+ ...req.body,
76
+ ...utils_1.default.handleError(error, req),
77
+ };
62
78
  next();
63
79
  }
64
80
  };
65
- const asyncRequestErrorHandler = async (req, res, next) => {
66
- const filters = Object.keys(req.body)
67
- .filter((attr) => attr.includes('filters.'))
68
- .filter((attr) => !!req.body[attr])
69
- .map((attr) => {
70
- return { name: attr, value: req.body[attr] };
71
- });
72
- res.render(`${templatePath}/async-error`, {
73
- title: 'Request Failed',
74
- layoutPath,
75
- ...req.body,
76
- filters,
77
- });
81
+ const pollingHandler = async (req, res, next) => {
82
+ try {
83
+ const pollingRenderData = await utils_2.default.renderPolling({
84
+ req,
85
+ res,
86
+ dataSources,
87
+ asyncReportsStore,
88
+ next,
89
+ });
90
+ res.render(`${templatePath}/async-polling`, {
91
+ title: 'Report Request Status',
92
+ layoutPath,
93
+ ...pollingRenderData,
94
+ });
95
+ }
96
+ catch (error) {
97
+ req.body.title = 'Failed to retrieve Report status';
98
+ req.body.description = 'We were unable to retrieve the report status:';
99
+ req.body.error = error.data;
100
+ next();
101
+ }
78
102
  };
79
- router.post('/requestReport/', asyncRequestHandler, asyncRequestErrorHandler);
80
- // 3 - polling the status of the request
81
- router.get('/async-reports/:reportId/:variantId/request/:executionId', async (req, res, next) => {
82
- res.render(`${templatePath}/async-polling`, {
83
- title: 'Report Requested',
84
- layoutPath,
85
- ...(await utils_2.default.renderPolling({ req, res, dataSources, asyncReportsStore, next })),
86
- });
87
- });
88
- // 3 - load the report data
89
- router.get('/async-reports/:reportId/:reportVariantId/request/:tableId/report', async (req, res, next) => {
90
- res.render(`${templatePath}async-report`, {
91
- layoutPath,
92
- ...(await AsyncReportListUtils.renderReport({
103
+ const getReportListHandler = async (req, res, next) => {
104
+ try {
105
+ const reportRenderData = await AsyncReportListUtils.renderReport({
93
106
  req,
94
107
  res,
95
108
  dataSources,
96
109
  asyncReportsStore,
97
110
  recentlyViewedStoreService,
98
111
  next,
99
- })),
100
- });
101
- });
112
+ });
113
+ res.render(`${templatePath}async-report`, {
114
+ layoutPath,
115
+ ...reportRenderData,
116
+ });
117
+ }
118
+ catch (error) {
119
+ req.body.title = 'Failed to retrieve Report';
120
+ req.body.description = 'We were unable to retrieve this report for the following reason:';
121
+ next();
122
+ }
123
+ };
124
+ router.get('/async-reports/:reportId/:variantId/request', getReportFiltersHandler, asyncErrorHandler);
125
+ router.post('/requestReport/', asyncRequestHandler, asyncErrorHandler);
126
+ router.get('/async-reports/:reportId/:variantId/request/:executionId', pollingHandler, asyncErrorHandler);
127
+ router.get('/async-reports/:reportId/:reportVariantId/request/:tableId/report', getReportListHandler, asyncErrorHandler);
102
128
  }
103
129
  exports.default = routes;
@@ -21,17 +21,30 @@ export default function routes({
21
21
  layoutPath: string
22
22
  templatePath?: string
23
23
  }) {
24
- // 1 - get filters for the report + make request
25
- router.get('/async-reports/:reportId/:variantId/request', async (req, res, next) => {
26
- res.render(`${templatePath}async-request`, {
27
- title: 'Request Report',
24
+ const asyncErrorHandler: RequestHandler = async (req, res) => {
25
+ res.render(`${templatePath}/async-error`, {
28
26
  layoutPath,
29
- postEndpoint: '/requestReport/',
30
- ...(await AsyncFiltersUtils.renderFilters({ req, res, dataSources, next })),
27
+ ...req.body,
31
28
  })
32
- })
29
+ }
30
+
31
+ const getReportFiltersHandler: RequestHandler = async (req, res, next) => {
32
+ try {
33
+ const filtersRenderData = await AsyncFiltersUtils.renderFilters({ req, res, dataSources, next })
34
+ res.render(`${templatePath}async-request`, {
35
+ title: 'Request Report',
36
+ layoutPath,
37
+ postEndpoint: '/requestReport/',
38
+ ...filtersRenderData,
39
+ })
40
+ } catch (error) {
41
+ req.body.title = 'Report Failed'
42
+ req.body.description = 'Your report has failed to generate. The issue has been reported to admin staff'
43
+ req.body.error = error.data
44
+ next()
45
+ }
46
+ }
33
47
 
34
- // 2 - handle the post request to request the report data
35
48
  const asyncRequestHandler: RequestHandler = async (req, res, next) => {
36
49
  try {
37
50
  const redirectToPollingPage = await AsyncFiltersUtils.requestReport({
@@ -48,49 +61,63 @@ export default function routes({
48
61
  res.end()
49
62
  }
50
63
  } catch (error) {
51
- req.body.error = JSON.parse(error.text)
64
+ req.body = {
65
+ ...req.body,
66
+ ...AsyncFiltersUtils.handleError(error, req),
67
+ }
52
68
  next()
53
69
  }
54
70
  }
55
71
 
56
- const asyncRequestErrorHandler: RequestHandler = async (req, res, next) => {
57
- const filters = Object.keys(req.body)
58
- .filter((attr) => attr.includes('filters.'))
59
- .filter((attr) => !!req.body[attr])
60
- .map((attr) => {
61
- return { name: attr, value: req.body[attr] }
72
+ const pollingHandler: RequestHandler = async (req, res, next) => {
73
+ try {
74
+ const pollingRenderData = await AsyncPollingUtils.renderPolling({
75
+ req,
76
+ res,
77
+ dataSources,
78
+ asyncReportsStore,
79
+ next,
62
80
  })
63
- res.render(`${templatePath}/async-error`, {
64
- title: 'Request Failed',
65
- layoutPath,
66
- ...req.body,
67
- filters,
68
- })
81
+ res.render(`${templatePath}/async-polling`, {
82
+ title: 'Report Request Status',
83
+ layoutPath,
84
+ ...pollingRenderData,
85
+ })
86
+ } catch (error) {
87
+ req.body.title = 'Failed to retrieve Report status'
88
+ req.body.description = 'We were unable to retrieve the report status:'
89
+ req.body.error = error.data
90
+ next()
91
+ }
69
92
  }
70
93
 
71
- router.post('/requestReport/', asyncRequestHandler, asyncRequestErrorHandler)
72
-
73
- // 3 - polling the status of the request
74
- router.get('/async-reports/:reportId/:variantId/request/:executionId', async (req, res, next) => {
75
- res.render(`${templatePath}/async-polling`, {
76
- title: 'Report Requested',
77
- layoutPath,
78
- ...(await AsyncPollingUtils.renderPolling({ req, res, dataSources, asyncReportsStore, next })),
79
- })
80
- })
81
-
82
- // 3 - load the report data
83
- router.get('/async-reports/:reportId/:reportVariantId/request/:tableId/report', async (req, res, next) => {
84
- res.render(`${templatePath}async-report`, {
85
- layoutPath,
86
- ...(await AsyncReportListUtils.renderReport({
94
+ const getReportListHandler: RequestHandler = async (req, res, next) => {
95
+ try {
96
+ const reportRenderData = await AsyncReportListUtils.renderReport({
87
97
  req,
88
98
  res,
89
99
  dataSources,
90
100
  asyncReportsStore,
91
101
  recentlyViewedStoreService,
92
102
  next,
93
- })),
94
- })
95
- })
103
+ })
104
+ res.render(`${templatePath}async-report`, {
105
+ layoutPath,
106
+ ...reportRenderData,
107
+ })
108
+ } catch (error) {
109
+ req.body.title = 'Failed to retrieve Report'
110
+ req.body.description = 'We were unable to retrieve this report for the following reason:'
111
+ next()
112
+ }
113
+ }
114
+
115
+ router.get('/async-reports/:reportId/:variantId/request', getReportFiltersHandler, asyncErrorHandler)
116
+ router.post('/requestReport/', asyncRequestHandler, asyncErrorHandler)
117
+ router.get('/async-reports/:reportId/:variantId/request/:executionId', pollingHandler, asyncErrorHandler)
118
+ router.get(
119
+ '/async-reports/:reportId/:reportVariantId/request/:tableId/report',
120
+ getReportListHandler,
121
+ asyncErrorHandler,
122
+ )
96
123
  }
@@ -12,7 +12,7 @@
12
12
  <p>Status: {{ dprRequestStatusTag(error.status) }}</p>
13
13
 
14
14
  {{ govukErrorSummary({
15
- titleText: "Your report has failed to generate. The issue has been reported to admin staff",
15
+ titleText: description,
16
16
  classes: "govuk-!-margin-top-6",
17
17
  errorList: [
18
18
  {
@@ -31,12 +31,16 @@
31
31
  {% endif %}
32
32
  {% endset %}
33
33
 
34
- {{ dprAsyncRequestDetails( reportName, variantName, description, filtersHtml )}}
34
+ {% if reportName %}
35
+ {{ dprAsyncRequestDetails( reportName, variantName, description, filtersHtml )}}
36
+ {% endif %}
35
37
 
36
- {{ govukButton({
38
+ {% if retry %}
39
+ {{ govukButton({
37
40
  id: "retry-async-request",
38
41
  text: "Retry",
39
42
  classes: "govuk-button govuk-!-margin-top-5",
40
43
  href: href
41
44
  }) }}
45
+ {% endif %}
42
46
  {% endblock %}
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@ministryofjustice/hmpps-digital-prison-reporting-frontend",
3
3
  "description": "The Digital Prison Reporting Frontend contains templates and code to help display data effectively in UI applications.",
4
- "version": "3.10.2",
4
+ "version": "3.10.3",
5
5
  "main": "dpr/assets/js/all.mjs",
6
6
  "sass": "dpr/all.scss",
7
7
  "engines": {
package/package.zip CHANGED
Binary file