@ministryofjustice/hmpps-digital-prison-reporting-frontend 3.31.2 → 3.31.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.
@@ -122,6 +122,7 @@ const getActions = ({ refresh, print, share, copy, download, }) => {
122
122
  reportName: download.reportName,
123
123
  name: download.name,
124
124
  columns: download.columns,
125
+ definitionPath: download.definitionPath,
125
126
  },
126
127
  ariaLabelText: !download.enabled
127
128
  ? `${BUTTON_TEMPLATES.downloadable.ariaLabelText}, disabled`
@@ -128,6 +128,7 @@ const getActions = ({
128
128
  tableId: string
129
129
  columns: string[]
130
130
  type: ReportType
131
+ definitionPath: string
131
132
  }
132
133
  }): ReportAction[] => {
133
134
  const actions: ReportAction[] = []
@@ -181,6 +182,7 @@ const getActions = ({
181
182
  reportName: download.reportName,
182
183
  name: download.name,
183
184
  columns: download.columns,
185
+ definitionPath: download.definitionPath,
184
186
  },
185
187
  ariaLabelText: !download.enabled
186
188
  ? `${BUTTON_TEMPLATES.downloadable.ariaLabelText}, disabled`
@@ -222,5 +224,6 @@ interface ReportAction {
222
224
  reportName?: string
223
225
  name?: string
224
226
  columns: string[]
227
+ definitionPath: string
225
228
  }
226
229
  }
@@ -27,7 +27,7 @@
27
27
  <input type="hidden" name="type" value="{{ button.attributes.type }}">
28
28
  <input type="hidden" name="tableId" value="{{ button.attributes.tableId }}">
29
29
  <input type="hidden" name="reportName" value="{{ button.attributes.reportName }}">
30
- <input type="hidden" name="variantName" value="{{ button.attributes.variantName }}">
30
+ <input type="hidden" name="name" value="{{ button.attributes.name }}">
31
31
  <input type="hidden" name="cols" value="{{ button.attributes.columns | json }}">
32
32
  </form>
33
33
  {% endif %}
@@ -92,7 +92,7 @@ class RequestedReportService extends userStoreService_1.default {
92
92
  case UserReports_1.RequestStatus.FINISHED:
93
93
  report.timestamp.completed = ts;
94
94
  report.url.report.pathname = `${report.url.request.pathname}/${tableId}/report${(0, urlHelper_1.getDpdPathSuffix)(report.dataProductDefinitionsPath)}`;
95
- report.url.report.fullUrl = `${report.url.origin}${report.url.report.pathname}${(0, urlHelper_1.getDpdPathSuffix)(report.dataProductDefinitionsPath)}`;
95
+ report.url.report.fullUrl = `${report.url.origin}${report.url.report.pathname}`;
96
96
  break;
97
97
  case UserReports_1.RequestStatus.SUBMITTED:
98
98
  report.timestamp.requested = ts;
@@ -101,9 +101,7 @@ export default class RequestedReportService extends UserStoreService {
101
101
  report.url.report.pathname = `${report.url.request.pathname}/${tableId}/report${getDpdPathSuffix(
102
102
  report.dataProductDefinitionsPath,
103
103
  )}`
104
- report.url.report.fullUrl = `${report.url.origin}${report.url.report.pathname}${getDpdPathSuffix(
105
- report.dataProductDefinitionsPath,
106
- )}`
104
+ report.url.report.fullUrl = `${report.url.origin}${report.url.report.pathname}`
107
105
  break
108
106
  case RequestStatus.SUBMITTED:
109
107
  report.timestamp.requested = ts
@@ -115,6 +113,7 @@ export default class RequestedReportService extends UserStoreService {
115
113
  report.timestamp.lastViewed = ts
116
114
  break
117
115
  }
116
+
118
117
  return report
119
118
  }
120
119
  }
@@ -90,6 +90,7 @@ export default class UserStoreItemBuilder {
90
90
  },
91
91
  },
92
92
  }
93
+
93
94
  return this
94
95
  }
95
96
 
@@ -1,21 +1,21 @@
1
1
  "use strict";
2
- var __importDefault = (this && this.__importDefault) || function (mod) {
3
- return (mod && mod.__esModule) ? mod : { "default": mod };
4
- };
5
2
  Object.defineProperty(exports, "__esModule", { value: true });
6
3
  const json_2_csv_1 = require("json-2-csv");
7
- const fs_extra_1 = __importDefault(require("fs-extra"));
8
- const logger_1 = __importDefault(require("./logger"));
9
- const convertToCsv = (reportData) => {
10
- const csvData = (0, json_2_csv_1.json2csv)(reportData);
4
+ const convertToCsv = (reportData, options) => {
5
+ const csvData = (0, json_2_csv_1.json2csv)(reportData, options);
11
6
  return csvData;
12
7
  };
13
- const saveToFile = async (csvData, reportName, variantName) => {
14
- const filepath = `./download/${reportName}-${variantName}-${new Date().toISOString()}.csv`;
15
- await fs_extra_1.default.outputFile(filepath, csvData);
16
- return {
17
- filepath,
18
- };
8
+ const getKeys = (reportData, reportDefinition) => {
9
+ const { fields } = reportDefinition.variant.specification;
10
+ const keys = [];
11
+ Object.keys(reportData[0]).forEach((key) => {
12
+ const field = fields.find((f) => f.name === key);
13
+ keys.push({
14
+ field: key,
15
+ title: field.display,
16
+ });
17
+ });
18
+ return keys;
19
19
  };
20
20
  const applyColumnsAndSort = (data, columns) => {
21
21
  return data.map((row) => {
@@ -33,7 +33,7 @@ exports.default = {
33
33
  var _a, _b;
34
34
  const userId = ((_a = res.locals.user) === null || _a === void 0 ? void 0 : _a.uuid) ? res.locals.user.uuid : 'userId';
35
35
  const token = ((_b = res.locals.user) === null || _b === void 0 ? void 0 : _b.token) ? res.locals.user.token : 'token';
36
- const { reportId, id, tableId, dataProductDefinitionsPath, reportName, variantName, cols: columns } = req.body;
36
+ const { reportId, id, tableId, dataProductDefinitionsPath, reportName, name, cols: columns } = req.body;
37
37
  const canDownload = await services.downloadPermissionService.downloadEnabled(userId, reportId, id);
38
38
  if (!canDownload) {
39
39
  res.redirect(`/async/report/${reportId}/${id}/request/${tableId}/report/download-disabled`);
@@ -42,20 +42,15 @@ exports.default = {
42
42
  let reportData = await services.reportingService.getAsyncReport(token, reportId, id, tableId, {
43
43
  dataProductDefinitionsPath,
44
44
  });
45
+ const reportDefinition = await services.reportingService.getDefinition(token, reportId, id, dataProductDefinitionsPath);
45
46
  if (columns) {
46
47
  reportData = applyColumnsAndSort(reportData, JSON.parse(columns));
47
48
  }
48
- const csvData = convertToCsv(reportData);
49
- const fileData = await saveToFile(csvData, reportName, variantName);
50
- res.download(fileData.filepath, (err) => {
51
- if (err) {
52
- logger_1.default.error(err);
53
- }
54
- else {
55
- logger_1.default.info(`Download completed: ${userId}: ${reportName} - ${variantName}`);
56
- fs_extra_1.default.unlinkSync(fileData.filepath);
57
- }
58
- });
49
+ const keys = getKeys(reportData, reportDefinition);
50
+ const csvData = convertToCsv(reportData, { keys });
51
+ res.setHeader('Content-Type', 'application/json');
52
+ res.setHeader('Content-disposition', `attachment; filename=${reportName}-${name}-${new Date().toISOString()}.csv`);
53
+ res.end(csvData);
59
54
  }
60
55
  },
61
56
  };
@@ -1,21 +1,30 @@
1
1
  import { Response, Request } from 'express'
2
- import { json2csv } from 'json-2-csv'
3
- import fs from 'fs-extra'
2
+ import { json2csv, Json2CsvOptions } from 'json-2-csv'
3
+ import { KeysList } from 'json-2-csv/lib/types'
4
4
  import { Services } from '../types/Services'
5
5
  import Dict = NodeJS.Dict
6
- import logger from './logger'
6
+ import { components } from '../types/api'
7
7
 
8
- const convertToCsv = (reportData: Dict<string>[]) => {
9
- const csvData = json2csv(reportData)
8
+ const convertToCsv = (reportData: Dict<string>[], options: Json2CsvOptions) => {
9
+ const csvData = json2csv(reportData, options)
10
10
  return csvData
11
11
  }
12
12
 
13
- const saveToFile = async (csvData: string, reportName: string, variantName: string) => {
14
- const filepath = `./download/${reportName}-${variantName}-${new Date().toISOString()}.csv`
15
- await fs.outputFile(filepath, csvData)
16
- return {
17
- filepath,
18
- }
13
+ const getKeys = (
14
+ reportData: Dict<string>[],
15
+ reportDefinition: components['schemas']['SingleVariantReportDefinition'],
16
+ ): KeysList => {
17
+ const { fields } = reportDefinition.variant.specification
18
+ const keys: KeysList = []
19
+ Object.keys(reportData[0]).forEach((key) => {
20
+ const field = fields.find((f) => f.name === key)
21
+ keys.push({
22
+ field: key,
23
+ title: field.display,
24
+ })
25
+ })
26
+
27
+ return keys
19
28
  }
20
29
 
21
30
  const applyColumnsAndSort = (data: Dict<string>[], columns: string[]) => {
@@ -35,7 +44,7 @@ export default {
35
44
  const userId = res.locals.user?.uuid ? res.locals.user.uuid : 'userId'
36
45
  const token = res.locals.user?.token ? res.locals.user.token : 'token'
37
46
 
38
- const { reportId, id, tableId, dataProductDefinitionsPath, reportName, variantName, cols: columns } = req.body
47
+ const { reportId, id, tableId, dataProductDefinitionsPath, reportName, name, cols: columns } = req.body
39
48
 
40
49
  const canDownload = await services.downloadPermissionService.downloadEnabled(userId, reportId, id)
41
50
  if (!canDownload) {
@@ -44,21 +53,22 @@ export default {
44
53
  let reportData = await services.reportingService.getAsyncReport(token, reportId, id, tableId, {
45
54
  dataProductDefinitionsPath,
46
55
  })
56
+ const reportDefinition = await services.reportingService.getDefinition(
57
+ token,
58
+ reportId,
59
+ id,
60
+ dataProductDefinitionsPath,
61
+ )
47
62
 
48
63
  if (columns) {
49
64
  reportData = applyColumnsAndSort(reportData, JSON.parse(columns))
50
65
  }
51
- const csvData = convertToCsv(reportData)
52
- const fileData = await saveToFile(csvData, reportName, variantName)
66
+ const keys: KeysList = getKeys(reportData, reportDefinition)
67
+ const csvData = convertToCsv(reportData, { keys })
53
68
 
54
- res.download(fileData.filepath, (err) => {
55
- if (err) {
56
- logger.error(err)
57
- } else {
58
- logger.info(`Download completed: ${userId}: ${reportName} - ${variantName}`)
59
- fs.unlinkSync(fileData.filepath)
60
- }
61
- })
69
+ res.setHeader('Content-Type', 'application/json')
70
+ res.setHeader('Content-disposition', `attachment; filename=${reportName}-${name}-${new Date().toISOString()}.csv`)
71
+ res.end(csvData)
62
72
  }
63
73
  },
64
74
  }
@@ -126,7 +126,7 @@ const getReport = async ({ req, res, services }) => {
126
126
  };
127
127
  exports.getReport = getReport;
128
128
  const setActions = (csrfToken, variant, requestData, columns) => {
129
- const { reportName, name, id, variantId, reportId, executionId, tableId, type } = requestData;
129
+ const { reportName, name, id, variantId, reportId, executionId, tableId, type, dataProductDefinitionsPath } = requestData;
130
130
  const url = requestData.url.request.fullUrl;
131
131
  const { printable } = variant;
132
132
  const ID = variantId || id;
@@ -141,6 +141,7 @@ const setActions = (csrfToken, variant, requestData, columns) => {
141
141
  tableId,
142
142
  type: type || UserReports_1.ReportType.REPORT,
143
143
  columns: columns.value,
144
+ definitionPath: dataProductDefinitionsPath,
144
145
  },
145
146
  print: {
146
147
  enabled: printable,
@@ -174,7 +174,8 @@ const setActions = (
174
174
  requestData: RequestedReport,
175
175
  columns: Columns,
176
176
  ) => {
177
- const { reportName, name, id, variantId, reportId, executionId, tableId, type } = requestData
177
+ const { reportName, name, id, variantId, reportId, executionId, tableId, type, dataProductDefinitionsPath } =
178
+ requestData
178
179
  const url = requestData.url.request.fullUrl
179
180
  const { printable } = variant
180
181
 
@@ -191,6 +192,7 @@ const setActions = (
191
192
  tableId,
192
193
  type: type || ReportType.REPORT,
193
194
  columns: columns.value,
195
+ definitionPath: dataProductDefinitionsPath,
194
196
  },
195
197
  print: {
196
198
  enabled: printable,
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.31.2",
4
+ "version": "3.31.3",
5
5
  "main": "dpr/assets/js/all.mjs",
6
6
  "sass": "dpr/all.scss",
7
7
  "engines": {
package/package.zip CHANGED
Binary file