Skip to content

Commit

Permalink
feat: Add additional logging for file uploads and refactor handler lo…
Browse files Browse the repository at this point in the history
…gic (#1286)

* refactor: refactor handleUploadRequest into a standalone handler, instead of a uploadService function. refactor prehandlers into their own directory
* feat: add `showFilenamesOnSummaryPage` to formModel
* fix: display previously uploaded file on file upload pages
  • Loading branch information
jenbutongit authored Aug 15, 2024
1 parent c25fed2 commit a0017fd
Show file tree
Hide file tree
Showing 18 changed files with 517 additions and 176 deletions.
22 changes: 22 additions & 0 deletions e2e/cypress/e2e/runner/files.feature
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
Feature: File upload fields

Background:
Given the form "files" exists
And the form "files-show-filenames-enabled" exists


Scenario Outline: showFilenamesOnSummaryPage shows "Uploaded" or the filename correctly on summary pages
And I navigate to the "<form>" form
When I upload the file "passes.png"
Then I see "<summaryValue>"
Examples:
| form | summaryValue |
| files | Uploaded |
| files-show-filenames-enabled | passes.png |


Scenario: Uploading a file shows "You previously uploaded" message
And I navigate to the "files" form
When I upload the file "passes.png"
And I navigate to "/files/file-one"
Then I see "You previously uploaded the file ‘passes.png’"
6 changes: 6 additions & 0 deletions e2e/cypress/e2e/runner/files.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
import { When } from "@badeball/cypress-cucumber-preprocessor";

When("I upload the file {string}", (filename) => {
cy.get("input[type=file]").attachFile(filename);
cy.findByRole("button", { name: /continue/i }).click();
});
48 changes: 48 additions & 0 deletions e2e/cypress/fixtures/files-show-filenames-enabled.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
{
"startPage": "/file-one",
"pages": [
{
"path": "/file-one",
"components": [
{
"type": "FileUploadField",
"name": "file1",
"title": "File 1",
"options": {
"required": true
},
"schema": {}
}
],
"section": "checkBeforeYouStart",
"next": [
{
"path": "/summary"
}
],
"title": "Upload file 1"
},
{
"path": "/summary",
"controller": "./pages/summary.js",
"title": "Summary",
"components": [],
"next": []
}
],
"lists": [],
"sections": [
{
"name": "checkBeforeYouStart",
"title": "Check before you start"
}
],
"phaseBanner": {},
"fees": [],
"payApiKey": "",
"outputs": [],
"declaration": "<p class=\"govuk-body\">All the answers you have provided are true to the best of your knowledge.</p>",
"version": 2,
"conditions": [],
"showFilenamesOnSummaryPage": true
}
47 changes: 47 additions & 0 deletions e2e/cypress/fixtures/files.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
{
"startPage": "/file-one",
"pages": [
{
"path": "/file-one",
"components": [
{
"type": "FileUploadField",
"name": "file1",
"title": "File 1",
"options": {
"required": true
},
"schema": {}
}
],
"section": "checkBeforeYouStart",
"next": [
{
"path": "/summary"
}
],
"title": "Upload file 1"
},
{
"path": "/summary",
"controller": "./pages/summary.js",
"title": "Summary",
"components": [],
"next": []
}
],
"lists": [],
"sections": [
{
"name": "checkBeforeYouStart",
"title": "Check before you start"
}
],
"phaseBanner": {},
"fees": [],
"payApiKey": "",
"outputs": [],
"declaration": "<p class=\"govuk-body\">All the answers you have provided are true to the best of your knowledge.</p>",
"version": 2,
"conditions": []
}
1 change: 1 addition & 0 deletions model/src/schema/schema.ts
Original file line number Diff line number Diff line change
Expand Up @@ -327,6 +327,7 @@ export const Schema = joi
specialPages: specialPagesSchema.optional(),
feeOptions: feeOptionSchema,
exitOptions: exitSchema.optional(),
showFilenamesOnSummaryPage: joi.boolean().optional(),
});

/**
Expand Down
3 changes: 3 additions & 0 deletions runner/src/server/plugins/engine/models/FormModel.ts
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,7 @@ export class FormModel {
specialPages: FormDefinition["specialPages"];
exitOptions?: ExitOptions;
allowExit: boolean = false;
showFilenamesOnSummaryPage: boolean = false;

constructor(def, options) {
const result = Schema.validate(def, { abortEarly: false });
Expand Down Expand Up @@ -120,6 +121,8 @@ export class FormModel {
this.allowExit = true;
}

this.showFilenamesOnSummaryPage = def.showFilenamesOnSummaryPage ?? false;

// @ts-ignore
this.pages = def.pages.map((pageDef) => this.makePage(pageDef));
this.startPage = this.pages.find((page) => page.path === def.startPage);
Expand Down
14 changes: 13 additions & 1 deletion runner/src/server/plugins/engine/models/SummaryViewModel.ts
Original file line number Diff line number Diff line change
Expand Up @@ -158,6 +158,8 @@ export class SummaryViewModel {
const items: any[] = [];
let sectionState = section ? state[section.name] || {} : state;

sectionState.originalFilenames = state.originalFilenames ?? {};

const sectionPages = relevantPages.filter(
(page) => page.section === section
);
Expand Down Expand Up @@ -352,7 +354,7 @@ function Item(
});
}

return {
const item = {
name: component.name,
path: page.path,
label: component.localisedString(component.title),
Expand All @@ -365,4 +367,14 @@ function Item(
dataType: component.dataType,
immutable: component.options.disableChangingFromSummary,
};

if (
component.type === "FileUploadField" &&
model.showFilenamesOnSummaryPage
) {
item.filename =
sectionState.originalFilenames[component.name]?.originalFilename;
}

return item;
}
Original file line number Diff line number Diff line change
Expand Up @@ -611,7 +611,7 @@ export class PageControllerBase {
/**
* other file related errors.. assuming file fields will be on their own page. This will replace all other errors from the page if not..
*/
if (preHandlerErrors) {
if (preHandlerErrors?.length) {
const reformattedErrors: any[] = [];
preHandlerErrors.forEach((error) => {
const reformatted = error;
Expand Down
20 changes: 10 additions & 10 deletions runner/src/server/plugins/engine/plugin.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,11 @@ import { FormPayload } from "./types";
import { shouldLogin } from "server/plugins/auth";
import config from "../../config";
import * as exit from "./pluginHandlers/exit";
import {
getFiles,
handleUpload,
validateContentTypes,
} from "./pluginHandlers/files/prehandlers";

configure([
// Configure Nunjucks to allow rendering of content that is revealed conditionally.
Expand Down Expand Up @@ -280,15 +285,6 @@ export const plugin = {

const { uploadService } = server.services([]);

const handleFiles = (request: HapiRequest, h: HapiResponseToolkit) => {
const { path, id } = request.params;
const model = forms[id];
const page = model?.pages.find(
(page) => normalisePath(page.path) === normalisePath(path)
);
return uploadService.handleUploadRequest(request, h, page.pageDef);
};

//TODO:- Merge with POST /{id}/{path*} route, and move to ./pluginHandlers/id/*
const postHandler = async (
request: HapiRequest,
Expand Down Expand Up @@ -330,7 +326,11 @@ export const plugin = {
return h.continue;
},
},
pre: [{ method: handleFiles }],
pre: [
{ method: getFiles, assign: "files" },
{ method: validateContentTypes, assign: "validFiles" },
{ method: handleUpload },
],
handler: postHandler,
},
});
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
import { HapiRequest, HapiResponseToolkit } from "server/types";

/**
* Prehandler that parses the payload and finds Buffers (i.e. files).
*/
export function getFiles(request: HapiRequest, _h: HapiResponseToolkit) {
const { uploadService } = request.services([]);
const files = uploadService.fileStreamsFromPayload(request.payload);
if (files.length) {
request.server.logger.info(
{ id: request.yar.id, path: request.path },
`Found ${uploadService.fileSummary(files)} to process on ${request.path}`
);
return files;
}

return null;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,119 @@
import { HapiRequest, HapiResponseToolkit } from "server/types";

const parsedError = (key: string, error?: string) => {
return {
path: key,
href: `#${key}`,
name: key,
text: error,
};
};

export async function handleUpload(
request: HapiRequest,
h: HapiResponseToolkit
) {
const { cacheService, uploadService } = request.services([]);
const state = await cacheService.getState(request);
let originalFilenames = state?.originalFilenames ?? {};
let logger = request.server.logger;
const files = request.pre.files;

if (!files) {
return h.continue;
}

/**
* files is an array of tuples containing key and value.
* value may be an array of file data where multiple files have been uploaded
*/
const validFiles = request.pre.validFiles;

for (const entry of validFiles) {
const [fieldName, streams] = entry;
const loggerIdentifier = uploadService.getLoggerIdentifier(request, {
fieldName,
});
const previousUpload = originalFilenames[fieldName];

if (previousUpload) {
logger.info(
loggerIdentifier,
`User is attempting to overwrite ${fieldName} with location ${previousUpload.location}`
);
}

let response;

try {
response = await uploadService.uploadDocuments(streams);
} catch (err) {
if (err.data?.res) {
const { error } = uploadService.parsedDocumentUploadResponse(err.data);
request.pre.errors = [
...request.pre.errors,
parsedError(fieldName, error),
];
} else if (err.code === "EPIPE") {
// ignore this error, it happens when the request is responded to by the doc upload service before the
// body has finished being sent. A valid response is still received.
logger.warn(loggerIdentifier, `Ignoring EPIPE response ${err.message}`);
} else {
logger.error(
{ ...loggerIdentifier, err },
`Error uploading document: ${err.message}`
);
request.pre.errors = [
...(h.request.pre.errors || []),
parsedError(fieldName, err),
];
}
}

const { location, warning, error } = response;

if (location) {
const originalFilename = streams
.map((stream) => stream.hapi?.filename)
.join(", ");

logger.info(
loggerIdentifier,
`Uploaded ${fieldName} successfully to ${location}`
);

originalFilenames[fieldName] = { location, originalFilename };
const {
originalFilenames: updatedFilenames,
} = await cacheService.mergeState(request, { originalFilenames });

logger.info(
{ ...loggerIdentifier, allFiles: updatedFilenames },
`Updated originalFileNames for user`
);
request.payload[fieldName] = location;
originalFilenames = updatedFilenames;
}

if (warning) {
request.pre.warning = warning;
logger.warn(
loggerIdentifier,
`File was uploaded successfully but there was a warning ${warning}`
);
}

if (error) {
logger.error(
loggerIdentifier,
`Document upload API responded with an error ${error}`
);
request.pre.errors = [
...(request.pre.errors || []),
parsedError(fieldName, error),
];
}
}

return h.continue;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
export { getFiles } from "./getFiles";
export { validateContentTypes } from "./validateContentTypes";
export { handleUpload } from "./handleUpload";
Loading

0 comments on commit a0017fd

Please sign in to comment.