@ministryofjustice/hmpps-digital-prison-reporting-frontend 4.28.3 → 4.28.4
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.
- package/dpr/middleware/setUpDprResources.js +9 -0
- package/dpr/middleware/setUpDprResources.js.map +3 -3
- package/dpr/middleware/setUpDprResources.ts +9 -0
- package/dpr/routes/journeys/download-report/utils.js +7 -2
- package/dpr/routes/journeys/download-report/utils.js.map +3 -3
- package/dpr/routes/journeys/download-report/utils.ts +7 -2
- package/package.json +1 -1
|
@@ -39,6 +39,7 @@ var import_node = require("@sentry/node");
|
|
|
39
39
|
var import_definitionUtils = __toESM(require("../utils/definitionUtils"));
|
|
40
40
|
var import_localsHelper = __toESM(require("../utils/localsHelper"));
|
|
41
41
|
var import_featureFlagService = require("../services/featureFlagService");
|
|
42
|
+
var import_logger = __toESM(require("../utils/logger"));
|
|
42
43
|
const getQueryParamAsString = (query, name) => query[name] ? query[name].toString() : null;
|
|
43
44
|
const getDefinitionsPath = (query) => getQueryParamAsString(query, "dataProductDefinitionsPath");
|
|
44
45
|
const deriveDefinitionsPath = (query) => {
|
|
@@ -98,6 +99,14 @@ const setFeatures = async (res, featureFlagService) => {
|
|
|
98
99
|
throw e;
|
|
99
100
|
});
|
|
100
101
|
res.app.locals.featureFlags.flags = Object.fromEntries(flags.flags.map((flag) => [flag.key, flag]));
|
|
102
|
+
import_logger.default.info(
|
|
103
|
+
{
|
|
104
|
+
flags: Object.fromEntries(
|
|
105
|
+
Object.entries(res.app.locals.featureFlags.flags).map(([k, v]) => [k, { key: v.key, enabled: v.enabled }])
|
|
106
|
+
)
|
|
107
|
+
},
|
|
108
|
+
"Feature Flags updated."
|
|
109
|
+
);
|
|
101
110
|
}
|
|
102
111
|
};
|
|
103
112
|
const populateValidationErrors = (req, res) => {
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../../dpr/middleware/setUpDprResources.ts"],
|
|
4
|
-
"sourcesContent": ["/* eslint-disable @typescript-eslint/no-explicit-any */\nimport { RequestHandler, Response, Request, ErrorRequestHandler, NextFunction } from 'express'\nimport type { ParsedQs } from 'qs'\nimport { HTTPError } from 'superagent'\nimport type { Environment } from 'nunjucks'\nimport { captureException } from '@sentry/node'\nimport { Services } from '../types/Services'\nimport { RequestedReport, StoredReportData } from '../types/UserReports'\nimport DefinitionUtils from '../utils/definitionUtils'\nimport { BookmarkStoreData } from '../types/Bookmark'\nimport { DprConfig } from '../types/DprConfig'\nimport localsHelper from '../utils/localsHelper'\nimport { FeatureFlagService, isBooleanFlagEnabledOrMissing } from '../services/featureFlagService'\n\nconst getQueryParamAsString = (query: ParsedQs, name: string) => (query[name] ? query[name].toString() : null)\nconst getDefinitionsPath = (query: ParsedQs) => getQueryParamAsString(query, 'dataProductDefinitionsPath')\n\nconst deriveDefinitionsPath = (query: ParsedQs): string | null => {\n const definitionsPath = getDefinitionsPath(query)\n if (definitionsPath) {\n return definitionsPath\n }\n\n return null\n}\n\nexport const errorRequestHandler =\n (layoutPath: string): ErrorRequestHandler =>\n (error: HTTPError, _req: Request, res: Response, next: NextFunction) => {\n if (error.status === 401 || error.status === 403) {\n return res.render('dpr/routes/authError.njk', {\n layoutPath,\n message: 'Sorry, there is a problem with authenticating your request',\n })\n }\n captureException(error)\n if (error.status >= 400) {\n return res.render('dpr/routes/serviceProblem.njk', {\n layoutPath,\n })\n }\n return next(error)\n }\n\nexport const setupResources = (\n services: Services,\n layoutPath: string,\n env: Environment,\n config?: DprConfig,\n): RequestHandler => {\n return async (req, res, next) => {\n populateValidationErrors(req, res)\n try {\n await setFeatures(res, services.featureFlagService)\n await populateDefinitions(services, req, res, config)\n await populateRequestedReports(services, res)\n setupRequestAwareNunjucks(env, res)\n return next()\n } catch (error) {\n return errorRequestHandler(layoutPath)(error, req, res, next)\n }\n }\n}\n\nconst setupRequestAwareNunjucks = (env: Environment, res: Response) => {\n env.addGlobal('getLocals', () => ({ locals: { ...res.locals, ...res.app.locals } }))\n}\n\nconst setFeatures = async (res: Response, featureFlagService: FeatureFlagService) => {\n if (res.app.locals.featureFlags === undefined) {\n res.app.locals.featureFlags = {\n flags: {},\n lastUpdated: new Date().getTime() - 601 * 1000,\n }\n }\n const { featureFlags } = res.app.locals\n const currentTime = new Date().getTime()\n const timeSinceLastUpdatedSeconds = (currentTime - featureFlags.lastUpdated) / 1000\n const shouldUpdate = timeSinceLastUpdatedSeconds > 600\n if (shouldUpdate) {\n // Refresh every 10 mins\n res.app.locals.featureFlags.lastUpdated = currentTime\n const flags = await featureFlagService.getFlags().catch((e) => {\n res.app.locals.featureFlags.lastUpdated = currentTime - 601 * 1000\n throw e\n })\n res.app.locals.featureFlags.flags = Object.fromEntries(flags.flags.map((flag) => [flag.key, flag]))\n }\n}\n\nconst populateValidationErrors = (req: Request, res: Response) => {\n const errors = req.flash(`DPR_ERRORS`)\n if (errors && errors[0]) {\n res.locals['validationErrors'] = JSON.parse(errors[0])\n }\n}\n\nexport const populateDefinitions = async (services: Services, req: Request, res: Response, config?: DprConfig) => {\n // Get the DPD path from the query\n const { token, dprUser } = localsHelper.getValues(res)\n\n const dpdPathFromQuery = deriveDefinitionsPath(req.query)\n const dpdPathFromBody = req.body?.dataProductDefinitionsPath\n const definitionsPathFromQuery = dpdPathFromQuery || dpdPathFromBody\n\n if (definitionsPathFromQuery) {\n res.locals['dpdPathFromQuery'] = true\n }\n\n // Get the DPD path from the config\n const dpdPathFromConfig = config?.dataProductDefinitionsPath\n if (dpdPathFromConfig) {\n res.locals['dpdPathFromConfig'] = true\n }\n\n // query takes presedence over config\n res.locals['definitionsPath'] = definitionsPathFromQuery || dpdPathFromConfig\n res.locals['pathSuffix'] = `?dataProductDefinitionsPath=${res.locals['definitionsPath']}`\n\n let selectedProductCollectionId: string | undefined\n if (token) {\n selectedProductCollectionId = await services.productCollectionStoreService.getSelectedProductCollectionId(\n dprUser.id,\n )\n }\n\n res.locals['definitions'] =\n (await Promise.all([\n services.reportingService.getDefinitions(token, res.locals['definitionsPath']),\n selectedProductCollectionId &&\n services.productCollectionService.getProductCollection(token, selectedProductCollectionId),\n ]).then(([defs, selectedProductCollection]) => {\n if (selectedProductCollection && selectedProductCollection) {\n const productIds = selectedProductCollection.products.map((product) => product.productId)\n return defs.filter((def) => productIds.includes(def.id))\n }\n return defs\n })) ?? []\n}\n\nexport const populateRequestedReports = async (services: Services, res: Response) => {\n const { dprUser } = localsHelper.getValues(res)\n if (dprUser.id) {\n const { definitions, definitionsPath } = res.locals\n\n const recent = await services.recentlyViewedService.getAllReports(dprUser.id)\n await services.requestedReportService.cleanList(dprUser.id, recent)\n const requested = await services.requestedReportService.getAllReports(dprUser.id)\n\n res.locals['requestedReports'] = !definitionsPath\n ? requested\n : requested.filter((report: RequestedReport) => {\n return DefinitionUtils.getCurrentVariantDefinition(definitions, report.reportId, report.id)\n })\n\n res.locals['recentlyViewedReports'] = !definitionsPath\n ? recent\n : recent.filter((report: StoredReportData) => {\n return DefinitionUtils.getCurrentVariantDefinition(definitions, report.reportId, report.id)\n })\n\n res.locals['downloadingEnabled'] = services.downloadPermissionService.enabled\n res.locals['bookmarkingEnabled'] = services.bookmarkService.enabled\n res.locals['collectionsEnabled'] = services.productCollectionService.enabled\n res.locals['requestMissingEnabled'] = services.missingReportService.enabled\n res.locals['saveDefaultsEnabled'] = isBooleanFlagEnabledOrMissing('saveDefaultsEnabled', res.app)\n\n if (res.locals['bookmarkingEnabled']) {\n const bookmarks = await services.bookmarkService.getAllBookmarks(dprUser.id)\n res.locals['bookmarks'] = !definitionsPath\n ? bookmarks\n : bookmarks.filter((bookmark: BookmarkStoreData) => {\n return DefinitionUtils.getCurrentVariantDefinition(definitions, bookmark.reportId, bookmark.id)\n })\n }\n }\n}\n\nexport default setupResources\n"],
|
|
5
|
-
"mappings": ";;;;;;;;;;;;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAKA,kBAAiC;AAGjC,6BAA4B;AAG5B,0BAAyB;AACzB,gCAAkE;
|
|
6
|
-
"names": ["localsHelper", "DefinitionUtils"]
|
|
4
|
+
"sourcesContent": ["/* eslint-disable @typescript-eslint/no-explicit-any */\nimport { RequestHandler, Response, Request, ErrorRequestHandler, NextFunction } from 'express'\nimport type { ParsedQs } from 'qs'\nimport { HTTPError } from 'superagent'\nimport type { Environment } from 'nunjucks'\nimport { captureException } from '@sentry/node'\nimport { Services } from '../types/Services'\nimport { RequestedReport, StoredReportData } from '../types/UserReports'\nimport DefinitionUtils from '../utils/definitionUtils'\nimport { BookmarkStoreData } from '../types/Bookmark'\nimport { DprConfig } from '../types/DprConfig'\nimport localsHelper from '../utils/localsHelper'\nimport { FeatureFlagService, isBooleanFlagEnabledOrMissing } from '../services/featureFlagService'\nimport logger from '../utils/logger'\n\nconst getQueryParamAsString = (query: ParsedQs, name: string) => (query[name] ? query[name].toString() : null)\nconst getDefinitionsPath = (query: ParsedQs) => getQueryParamAsString(query, 'dataProductDefinitionsPath')\n\nconst deriveDefinitionsPath = (query: ParsedQs): string | null => {\n const definitionsPath = getDefinitionsPath(query)\n if (definitionsPath) {\n return definitionsPath\n }\n\n return null\n}\n\nexport const errorRequestHandler =\n (layoutPath: string): ErrorRequestHandler =>\n (error: HTTPError, _req: Request, res: Response, next: NextFunction) => {\n if (error.status === 401 || error.status === 403) {\n return res.render('dpr/routes/authError.njk', {\n layoutPath,\n message: 'Sorry, there is a problem with authenticating your request',\n })\n }\n captureException(error)\n if (error.status >= 400) {\n return res.render('dpr/routes/serviceProblem.njk', {\n layoutPath,\n })\n }\n return next(error)\n }\n\nexport const setupResources = (\n services: Services,\n layoutPath: string,\n env: Environment,\n config?: DprConfig,\n): RequestHandler => {\n return async (req, res, next) => {\n populateValidationErrors(req, res)\n try {\n await setFeatures(res, services.featureFlagService)\n await populateDefinitions(services, req, res, config)\n await populateRequestedReports(services, res)\n setupRequestAwareNunjucks(env, res)\n return next()\n } catch (error) {\n return errorRequestHandler(layoutPath)(error, req, res, next)\n }\n }\n}\n\nconst setupRequestAwareNunjucks = (env: Environment, res: Response) => {\n env.addGlobal('getLocals', () => ({ locals: { ...res.locals, ...res.app.locals } }))\n}\n\nconst setFeatures = async (res: Response, featureFlagService: FeatureFlagService) => {\n if (res.app.locals.featureFlags === undefined) {\n res.app.locals.featureFlags = {\n flags: {},\n lastUpdated: new Date().getTime() - 601 * 1000,\n }\n }\n const { featureFlags } = res.app.locals\n const currentTime = new Date().getTime()\n const timeSinceLastUpdatedSeconds = (currentTime - featureFlags.lastUpdated) / 1000\n const shouldUpdate = timeSinceLastUpdatedSeconds > 600\n if (shouldUpdate) {\n // Refresh every 10 mins\n res.app.locals.featureFlags.lastUpdated = currentTime\n const flags = await featureFlagService.getFlags().catch((e) => {\n res.app.locals.featureFlags.lastUpdated = currentTime - 601 * 1000\n throw e\n })\n res.app.locals.featureFlags.flags = Object.fromEntries(flags.flags.map((flag) => [flag.key, flag]))\n logger.info(\n {\n flags: Object.fromEntries(\n Object.entries(res.app.locals.featureFlags.flags).map(([k, v]) => [k, { key: v.key, enabled: v.enabled }]),\n ),\n },\n 'Feature Flags updated.',\n )\n }\n}\n\nconst populateValidationErrors = (req: Request, res: Response) => {\n const errors = req.flash(`DPR_ERRORS`)\n if (errors && errors[0]) {\n res.locals['validationErrors'] = JSON.parse(errors[0])\n }\n}\n\nexport const populateDefinitions = async (services: Services, req: Request, res: Response, config?: DprConfig) => {\n // Get the DPD path from the query\n const { token, dprUser } = localsHelper.getValues(res)\n\n const dpdPathFromQuery = deriveDefinitionsPath(req.query)\n const dpdPathFromBody = req.body?.dataProductDefinitionsPath\n const definitionsPathFromQuery = dpdPathFromQuery || dpdPathFromBody\n\n if (definitionsPathFromQuery) {\n res.locals['dpdPathFromQuery'] = true\n }\n\n // Get the DPD path from the config\n const dpdPathFromConfig = config?.dataProductDefinitionsPath\n if (dpdPathFromConfig) {\n res.locals['dpdPathFromConfig'] = true\n }\n\n // query takes presedence over config\n res.locals['definitionsPath'] = definitionsPathFromQuery || dpdPathFromConfig\n res.locals['pathSuffix'] = `?dataProductDefinitionsPath=${res.locals['definitionsPath']}`\n\n let selectedProductCollectionId: string | undefined\n if (token) {\n selectedProductCollectionId = await services.productCollectionStoreService.getSelectedProductCollectionId(\n dprUser.id,\n )\n }\n\n res.locals['definitions'] =\n (await Promise.all([\n services.reportingService.getDefinitions(token, res.locals['definitionsPath']),\n selectedProductCollectionId &&\n services.productCollectionService.getProductCollection(token, selectedProductCollectionId),\n ]).then(([defs, selectedProductCollection]) => {\n if (selectedProductCollection && selectedProductCollection) {\n const productIds = selectedProductCollection.products.map((product) => product.productId)\n return defs.filter((def) => productIds.includes(def.id))\n }\n return defs\n })) ?? []\n}\n\nexport const populateRequestedReports = async (services: Services, res: Response) => {\n const { dprUser } = localsHelper.getValues(res)\n if (dprUser.id) {\n const { definitions, definitionsPath } = res.locals\n\n const recent = await services.recentlyViewedService.getAllReports(dprUser.id)\n await services.requestedReportService.cleanList(dprUser.id, recent)\n const requested = await services.requestedReportService.getAllReports(dprUser.id)\n\n res.locals['requestedReports'] = !definitionsPath\n ? requested\n : requested.filter((report: RequestedReport) => {\n return DefinitionUtils.getCurrentVariantDefinition(definitions, report.reportId, report.id)\n })\n\n res.locals['recentlyViewedReports'] = !definitionsPath\n ? recent\n : recent.filter((report: StoredReportData) => {\n return DefinitionUtils.getCurrentVariantDefinition(definitions, report.reportId, report.id)\n })\n\n res.locals['downloadingEnabled'] = services.downloadPermissionService.enabled\n res.locals['bookmarkingEnabled'] = services.bookmarkService.enabled\n res.locals['collectionsEnabled'] = services.productCollectionService.enabled\n res.locals['requestMissingEnabled'] = services.missingReportService.enabled\n res.locals['saveDefaultsEnabled'] = isBooleanFlagEnabledOrMissing('saveDefaultsEnabled', res.app)\n\n if (res.locals['bookmarkingEnabled']) {\n const bookmarks = await services.bookmarkService.getAllBookmarks(dprUser.id)\n res.locals['bookmarks'] = !definitionsPath\n ? bookmarks\n : bookmarks.filter((bookmark: BookmarkStoreData) => {\n return DefinitionUtils.getCurrentVariantDefinition(definitions, bookmark.reportId, bookmark.id)\n })\n }\n }\n}\n\nexport default setupResources\n"],
|
|
5
|
+
"mappings": "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",
|
|
6
|
+
"names": ["logger", "localsHelper", "DefinitionUtils"]
|
|
7
7
|
}
|
|
@@ -11,6 +11,7 @@ import { BookmarkStoreData } from '../types/Bookmark'
|
|
|
11
11
|
import { DprConfig } from '../types/DprConfig'
|
|
12
12
|
import localsHelper from '../utils/localsHelper'
|
|
13
13
|
import { FeatureFlagService, isBooleanFlagEnabledOrMissing } from '../services/featureFlagService'
|
|
14
|
+
import logger from '../utils/logger'
|
|
14
15
|
|
|
15
16
|
const getQueryParamAsString = (query: ParsedQs, name: string) => (query[name] ? query[name].toString() : null)
|
|
16
17
|
const getDefinitionsPath = (query: ParsedQs) => getQueryParamAsString(query, 'dataProductDefinitionsPath')
|
|
@@ -85,6 +86,14 @@ const setFeatures = async (res: Response, featureFlagService: FeatureFlagService
|
|
|
85
86
|
throw e
|
|
86
87
|
})
|
|
87
88
|
res.app.locals.featureFlags.flags = Object.fromEntries(flags.flags.map((flag) => [flag.key, flag]))
|
|
89
|
+
logger.info(
|
|
90
|
+
{
|
|
91
|
+
flags: Object.fromEntries(
|
|
92
|
+
Object.entries(res.app.locals.featureFlags.flags).map(([k, v]) => [k, { key: v.key, enabled: v.enabled }]),
|
|
93
|
+
),
|
|
94
|
+
},
|
|
95
|
+
'Feature Flags updated.',
|
|
96
|
+
)
|
|
88
97
|
}
|
|
89
98
|
}
|
|
90
99
|
|
|
@@ -38,6 +38,7 @@ var import_UserReports = require("../../../types/UserReports");
|
|
|
38
38
|
var import_localsHelper = __toESM(require("../../../utils/localsHelper"));
|
|
39
39
|
var import_ReportQuery = __toESM(require("../../../types/ReportQuery"));
|
|
40
40
|
var import_featureFlagService = require("../../../services/featureFlagService");
|
|
41
|
+
var import_logger = __toESM(require("../../../utils/logger"));
|
|
41
42
|
const convertToCsv = (reportData, options) => {
|
|
42
43
|
const csvData = (0, import_json_2_csv.json2csv)(reportData, options);
|
|
43
44
|
return csvData;
|
|
@@ -154,7 +155,8 @@ const downloadReport = async ({
|
|
|
154
155
|
} = req.body;
|
|
155
156
|
const { downloadPermissionService } = services;
|
|
156
157
|
const canDownloadReport = await downloadPermissionService.downloadEnabledForReport(dprUser.id, reportId, id);
|
|
157
|
-
const
|
|
158
|
+
const streamingEnabledInFlipt = (0, import_featureFlagService.isBooleanFlagExplicitlyEnabled)("streamingDownloadEnabled", res.app);
|
|
159
|
+
const streamingEnabledInEnv = process.env["STREAMING_DOWNLOAD_ENABLED"] === "true";
|
|
158
160
|
if (!canDownloadReport) {
|
|
159
161
|
res.redirect(redirect);
|
|
160
162
|
} else {
|
|
@@ -173,7 +175,10 @@ const downloadReport = async ({
|
|
|
173
175
|
queryParams
|
|
174
176
|
});
|
|
175
177
|
} else {
|
|
176
|
-
|
|
178
|
+
import_logger.default.info(`Streaming enabled in Flipt: ${streamingEnabledInFlipt}`);
|
|
179
|
+
import_logger.default.info(`Streaming enabled in Env: ${streamingEnabledInEnv}`);
|
|
180
|
+
if (streamingEnabledInFlipt || streamingEnabledInEnv) {
|
|
181
|
+
import_logger.default.info(`Initiating streaming...`);
|
|
177
182
|
const streamDownloadQueryParams = {
|
|
178
183
|
dataProductDefinitionsPath,
|
|
179
184
|
...columns && { columns: JSON.parse(columns) },
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../../../../dpr/routes/journeys/download-report/utils.ts"],
|
|
4
|
-
"sourcesContent": ["import { Response, Request } from 'express'\nimport { json2csv, Json2CsvOptions } from 'json-2-csv'\nimport { KeysList } from 'json-2-csv/lib/types'\nimport { Services } from '../../../types/Services'\nimport Dict = NodeJS.Dict\nimport { LoadType } from '../../../types/UserReports'\nimport { components } from '../../../types/api'\nimport LocalsHelper from '../../../utils/localsHelper'\nimport { Template } from '../../../types/Templates'\nimport ReportQuery from '../../../types/ReportQuery'\nimport { isBooleanFlagExplicitlyEnabled } from '../../../services/featureFlagService'\n\nconst convertToCsv = (reportData: Dict<string>[], options: Json2CsvOptions) => {\n const csvData = json2csv(reportData, options)\n return csvData\n}\n\nexport const getKeys = (reportData: Dict<string>[], fields: components['schemas']['FieldDefinition'][]): KeysList => {\n const keys: KeysList = []\n const keyNames: string[] = []\n reportData.forEach((row) => {\n Object.keys(row).forEach((key) => {\n const field = fields.find((f) => f.name === key)\n if (field && !keyNames.includes(key)) {\n keyNames.push(key)\n keys.push({\n field: key,\n title: field.display,\n })\n }\n })\n })\n return keys\n}\n\nconst applyColumnsAndSort = (data: Dict<string>[], columns: string[]) => {\n return data.map((row) => {\n return Object.keys(row)\n .filter((key) => columns.includes(key))\n .reduce((obj: Dict<string>, key) => {\n // eslint-disable-next-line no-param-reassign\n obj[key] = row[key]\n return obj\n }, {})\n })\n}\n\nconst removeHtmlTags = (\n reportData: Dict<string>[],\n reportDefinition: components['schemas']['SingleVariantReportDefinition'],\n) => {\n // Find HMTL field + name\n const { specification } = reportDefinition.variant\n if (specification) {\n const { fields } = specification\n const htmlFields = fields.filter((field) => {\n return field.type === 'HTML'\n })\n\n if (htmlFields.length) {\n reportData.map((d) => {\n htmlFields.forEach((field) => {\n const { name } = field\n const colValue = d[name]\n if (colValue) {\n const innerText = /target=\"_blank\">(.*?)<\\/a>/g.exec(colValue)\n // eslint-disable-next-line prefer-destructuring, no-param-reassign\n d[name] = innerText ? innerText[1] : colValue\n }\n })\n return d\n })\n }\n }\n return reportData\n}\n\nconst streamDownloadAsyncData = async (args: {\n services: Services\n token: string\n tableId: string\n reportId: string\n id: string\n queryParams: {\n dataProductDefinitionsPath: string\n columns: string[]\n sortedAsc?: string\n sortColumn?: string\n }\n res: Response\n}) => {\n const { token, services, tableId, reportId, id, queryParams, res } = args\n const query: Record<string, string | string[]> = {\n ...queryParams,\n }\n return services.reportingService.downloadAsyncReport(token, reportId, id, tableId, query, res)\n}\n\nconst dowloadAsyncData = async (args: {\n services: Services\n token: string\n tableId: string\n reportId: string\n id: string\n queryParams: {\n dataProductDefinitionsPath: string\n sortedAsc?: string\n sortColumn?: string\n }\n}) => {\n const { token, services, tableId, reportId, id, queryParams } = args\n const pageSize = await services.reportingService.getAsyncCount(token, tableId)\n const query: Record<string, string | string[]> = {\n ...queryParams,\n pageSize: pageSize.toString(),\n }\n return services.reportingService.getAsyncReport(token, reportId, id, tableId, query)\n}\n\nconst downloadSyncData = async (args: {\n definition: components['schemas']['SingleVariantReportDefinition']\n services: Services\n token: string\n queryParams: {\n dataProductDefinitionsPath: string\n sortedAsc?: string\n sortColumn?: string\n }\n}) => {\n const { token, services, queryParams, definition } = args\n const { variant } = definition\n const { resourceName, specification } = variant\n let data: Dict<string>[] = []\n\n if (specification) {\n const countReportQuery = new ReportQuery({\n fields: specification.fields,\n template: specification.template as Template,\n queryParams,\n definitionsPath: <string>queryParams.dataProductDefinitionsPath,\n })\n const count = await services.reportingService.getCount(resourceName, token, countReportQuery)\n\n const dataReportQuery = new ReportQuery({\n fields: specification.fields,\n template: specification.template as Template,\n queryParams: {\n ...queryParams,\n pageSize: count.toString(),\n },\n definitionsPath: <string>queryParams.dataProductDefinitionsPath,\n })\n const reportData = await services.reportingService.getListWithWarnings(resourceName, token, dataReportQuery)\n data = reportData.data\n }\n\n return data\n}\n\nexport const downloadReport = async ({\n req,\n services,\n res,\n redirect,\n loadType,\n}: {\n req: Request\n services: Services\n res: Response\n redirect: string\n loadType?: LoadType\n}) => {\n const { dprUser, token } = LocalsHelper.getValues(res)\n const {\n reportId,\n id,\n tableId,\n dataProductDefinitionsPath,\n reportName,\n name,\n cols: columns,\n sortedAsc,\n sortColumn,\n } = req.body\n const { downloadPermissionService } = services\n const canDownloadReport = await downloadPermissionService.downloadEnabledForReport(dprUser.id, reportId, id)\n const
|
|
5
|
-
"mappings": ";;;;;;;;;;;;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AACA,wBAA0C;AAI1C,yBAAyB;AAEzB,0BAAyB;AAEzB,yBAAwB;AACxB,gCAA+C;
|
|
6
|
-
"names": ["ReportQuery", "LocalsHelper"]
|
|
4
|
+
"sourcesContent": ["import { Response, Request } from 'express'\nimport { json2csv, Json2CsvOptions } from 'json-2-csv'\nimport { KeysList } from 'json-2-csv/lib/types'\nimport { Services } from '../../../types/Services'\nimport Dict = NodeJS.Dict\nimport { LoadType } from '../../../types/UserReports'\nimport { components } from '../../../types/api'\nimport LocalsHelper from '../../../utils/localsHelper'\nimport { Template } from '../../../types/Templates'\nimport ReportQuery from '../../../types/ReportQuery'\nimport { isBooleanFlagExplicitlyEnabled } from '../../../services/featureFlagService'\nimport logger from '../../../utils/logger'\n\nconst convertToCsv = (reportData: Dict<string>[], options: Json2CsvOptions) => {\n const csvData = json2csv(reportData, options)\n return csvData\n}\n\nexport const getKeys = (reportData: Dict<string>[], fields: components['schemas']['FieldDefinition'][]): KeysList => {\n const keys: KeysList = []\n const keyNames: string[] = []\n reportData.forEach((row) => {\n Object.keys(row).forEach((key) => {\n const field = fields.find((f) => f.name === key)\n if (field && !keyNames.includes(key)) {\n keyNames.push(key)\n keys.push({\n field: key,\n title: field.display,\n })\n }\n })\n })\n return keys\n}\n\nconst applyColumnsAndSort = (data: Dict<string>[], columns: string[]) => {\n return data.map((row) => {\n return Object.keys(row)\n .filter((key) => columns.includes(key))\n .reduce((obj: Dict<string>, key) => {\n // eslint-disable-next-line no-param-reassign\n obj[key] = row[key]\n return obj\n }, {})\n })\n}\n\nconst removeHtmlTags = (\n reportData: Dict<string>[],\n reportDefinition: components['schemas']['SingleVariantReportDefinition'],\n) => {\n // Find HMTL field + name\n const { specification } = reportDefinition.variant\n if (specification) {\n const { fields } = specification\n const htmlFields = fields.filter((field) => {\n return field.type === 'HTML'\n })\n\n if (htmlFields.length) {\n reportData.map((d) => {\n htmlFields.forEach((field) => {\n const { name } = field\n const colValue = d[name]\n if (colValue) {\n const innerText = /target=\"_blank\">(.*?)<\\/a>/g.exec(colValue)\n // eslint-disable-next-line prefer-destructuring, no-param-reassign\n d[name] = innerText ? innerText[1] : colValue\n }\n })\n return d\n })\n }\n }\n return reportData\n}\n\nconst streamDownloadAsyncData = async (args: {\n services: Services\n token: string\n tableId: string\n reportId: string\n id: string\n queryParams: {\n dataProductDefinitionsPath: string\n columns: string[]\n sortedAsc?: string\n sortColumn?: string\n }\n res: Response\n}) => {\n const { token, services, tableId, reportId, id, queryParams, res } = args\n const query: Record<string, string | string[]> = {\n ...queryParams,\n }\n return services.reportingService.downloadAsyncReport(token, reportId, id, tableId, query, res)\n}\n\nconst dowloadAsyncData = async (args: {\n services: Services\n token: string\n tableId: string\n reportId: string\n id: string\n queryParams: {\n dataProductDefinitionsPath: string\n sortedAsc?: string\n sortColumn?: string\n }\n}) => {\n const { token, services, tableId, reportId, id, queryParams } = args\n const pageSize = await services.reportingService.getAsyncCount(token, tableId)\n const query: Record<string, string | string[]> = {\n ...queryParams,\n pageSize: pageSize.toString(),\n }\n return services.reportingService.getAsyncReport(token, reportId, id, tableId, query)\n}\n\nconst downloadSyncData = async (args: {\n definition: components['schemas']['SingleVariantReportDefinition']\n services: Services\n token: string\n queryParams: {\n dataProductDefinitionsPath: string\n sortedAsc?: string\n sortColumn?: string\n }\n}) => {\n const { token, services, queryParams, definition } = args\n const { variant } = definition\n const { resourceName, specification } = variant\n let data: Dict<string>[] = []\n\n if (specification) {\n const countReportQuery = new ReportQuery({\n fields: specification.fields,\n template: specification.template as Template,\n queryParams,\n definitionsPath: <string>queryParams.dataProductDefinitionsPath,\n })\n const count = await services.reportingService.getCount(resourceName, token, countReportQuery)\n\n const dataReportQuery = new ReportQuery({\n fields: specification.fields,\n template: specification.template as Template,\n queryParams: {\n ...queryParams,\n pageSize: count.toString(),\n },\n definitionsPath: <string>queryParams.dataProductDefinitionsPath,\n })\n const reportData = await services.reportingService.getListWithWarnings(resourceName, token, dataReportQuery)\n data = reportData.data\n }\n\n return data\n}\n\nexport const downloadReport = async ({\n req,\n services,\n res,\n redirect,\n loadType,\n}: {\n req: Request\n services: Services\n res: Response\n redirect: string\n loadType?: LoadType\n}) => {\n const { dprUser, token } = LocalsHelper.getValues(res)\n const {\n reportId,\n id,\n tableId,\n dataProductDefinitionsPath,\n reportName,\n name,\n cols: columns,\n sortedAsc,\n sortColumn,\n } = req.body\n const { downloadPermissionService } = services\n const canDownloadReport = await downloadPermissionService.downloadEnabledForReport(dprUser.id, reportId, id)\n const streamingEnabledInFlipt = isBooleanFlagExplicitlyEnabled('streamingDownloadEnabled', res.app)\n const streamingEnabledInEnv = process.env['STREAMING_DOWNLOAD_ENABLED'] === 'true'\n\n if (!canDownloadReport) {\n res.redirect(redirect)\n } else {\n const definition = await services.reportingService.getDefinition(token, reportId, id, dataProductDefinitionsPath)\n const queryParams = {\n dataProductDefinitionsPath,\n ...(sortedAsc && { sortedAsc }),\n ...(sortColumn && { sortColumn }),\n }\n\n let reportData\n if (loadType === LoadType.SYNC) {\n reportData = await downloadSyncData({\n definition,\n services,\n token,\n queryParams,\n })\n } else {\n logger.info(`Streaming enabled in Flipt: ${streamingEnabledInFlipt}`)\n logger.info(`Streaming enabled in Env: ${streamingEnabledInEnv}`)\n if (streamingEnabledInFlipt || streamingEnabledInEnv) {\n logger.info(`Initiating streaming...`)\n const streamDownloadQueryParams = {\n dataProductDefinitionsPath,\n ...(columns && { columns: JSON.parse(columns) }),\n ...(sortedAsc && { sortedAsc }),\n ...(sortColumn && { sortColumn }),\n }\n await streamDownloadAsyncData({\n services,\n token,\n reportId,\n id,\n tableId,\n queryParams: streamDownloadQueryParams,\n res,\n })\n return\n }\n\n reportData = await dowloadAsyncData({\n services,\n token,\n reportId,\n id,\n tableId,\n queryParams,\n })\n }\n if (columns) {\n reportData = applyColumnsAndSort(reportData, JSON.parse(columns))\n }\n reportData = removeHtmlTags(reportData, definition)\n const fields = definition.variant.specification?.fields || []\n const keys: KeysList = getKeys(reportData, fields)\n const csvData = convertToCsv(reportData, { keys, emptyFieldValue: '' })\n\n res.setHeader('Content-Type', 'application/json')\n res.setHeader('Content-disposition', `attachment; filename=${reportName}-${name}-${new Date().toISOString()}.csv`)\n res.end(csvData)\n }\n}\n\nexport default {\n downloadReport,\n getKeys,\n}\n"],
|
|
5
|
+
"mappings": "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",
|
|
6
|
+
"names": ["ReportQuery", "LocalsHelper", "logger"]
|
|
7
7
|
}
|
|
@@ -9,6 +9,7 @@ import LocalsHelper from '../../../utils/localsHelper'
|
|
|
9
9
|
import { Template } from '../../../types/Templates'
|
|
10
10
|
import ReportQuery from '../../../types/ReportQuery'
|
|
11
11
|
import { isBooleanFlagExplicitlyEnabled } from '../../../services/featureFlagService'
|
|
12
|
+
import logger from '../../../utils/logger'
|
|
12
13
|
|
|
13
14
|
const convertToCsv = (reportData: Dict<string>[], options: Json2CsvOptions) => {
|
|
14
15
|
const csvData = json2csv(reportData, options)
|
|
@@ -184,7 +185,8 @@ export const downloadReport = async ({
|
|
|
184
185
|
} = req.body
|
|
185
186
|
const { downloadPermissionService } = services
|
|
186
187
|
const canDownloadReport = await downloadPermissionService.downloadEnabledForReport(dprUser.id, reportId, id)
|
|
187
|
-
const
|
|
188
|
+
const streamingEnabledInFlipt = isBooleanFlagExplicitlyEnabled('streamingDownloadEnabled', res.app)
|
|
189
|
+
const streamingEnabledInEnv = process.env['STREAMING_DOWNLOAD_ENABLED'] === 'true'
|
|
188
190
|
|
|
189
191
|
if (!canDownloadReport) {
|
|
190
192
|
res.redirect(redirect)
|
|
@@ -205,7 +207,10 @@ export const downloadReport = async ({
|
|
|
205
207
|
queryParams,
|
|
206
208
|
})
|
|
207
209
|
} else {
|
|
208
|
-
|
|
210
|
+
logger.info(`Streaming enabled in Flipt: ${streamingEnabledInFlipt}`)
|
|
211
|
+
logger.info(`Streaming enabled in Env: ${streamingEnabledInEnv}`)
|
|
212
|
+
if (streamingEnabledInFlipt || streamingEnabledInEnv) {
|
|
213
|
+
logger.info(`Initiating streaming...`)
|
|
209
214
|
const streamDownloadQueryParams = {
|
|
210
215
|
dataProductDefinitionsPath,
|
|
211
216
|
...(columns && { columns: JSON.parse(columns) }),
|
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": "4.28.
|
|
4
|
+
"version": "4.28.4",
|
|
5
5
|
"main": "dpr/all",
|
|
6
6
|
"sass": "dpr/all.scss",
|
|
7
7
|
"engines": {
|