Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion src/config/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -111,7 +111,7 @@ export const config = convict({
*/
sessionTimeout: {
format: Number,
default: oneHour * 24, // 1 day
default: oneHour * 168, // 7 days
env: 'SESSION_TIMEOUT'
},
confirmationSessionTimeout: {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,11 @@ import {
} from '~/src/server/plugins/engine/helpers.js'
import { type FormModel } from '~/src/server/plugins/engine/models/index.js'
import { PageController } from '~/src/server/plugins/engine/pageControllers/PageController.js'
import { extendFileRetention } from '~/src/server/plugins/engine/services/formSubmissionService.js'
import { type FilePersistData } from '~/src/server/plugins/engine/services/types.js'
import {
FileStatus,
type FileState,
type FormContext,
type FormContextRequest,
type FormPageViewModel,
Expand Down Expand Up @@ -321,7 +325,45 @@ export class QuestionPageController extends PageController {
}

const { cacheService } = request.services([])
return cacheService.setState(request, updated)
const newState = await cacheService.setState(request, updated)

await this.updateFileRetention(newState)

return newState
}

/**
* Checks the updated session state for any file upload information and,
* if found, calls the file retention service to extend the files' S3 expiration.
* @param state - The current session state.
*/
private async updateFileRetention(state: FormSubmissionState): Promise<void> {
if (!state.upload) {
return
}

const files: FilePersistData[] = []

const uploads = state.upload ?? {}
Object.keys(uploads).forEach((uploadKey) => {
const uploadData = uploads[uploadKey]
if (Array.isArray(uploadData.files) && uploadData.files.length > 0) {
uploadData.files.forEach((file: FileState) => {
// Only extend retention if the file upload is complete otherwise we'll run into a race condition.
if (file.status.form.file.fileStatus === FileStatus.complete) {
files.push({
fileId: file.status.form.file.fileId
})
}
})
}
})

if (files.length > 0) {
const retrievalKey =
this.model.def.outputEmail ?? 'defraforms@defra.gov.uk'
await extendFileRetention(files, retrievalKey)
}
}

makeGetRouteHandler() {
Expand Down
31 changes: 25 additions & 6 deletions src/server/plugins/engine/services/formSubmissionService.js
Original file line number Diff line number Diff line change
@@ -1,19 +1,19 @@
import { config } from '~/src/config/index.js'
import { postJson } from '~/src/server/services/httpService.js'

const submissionUrl = config.get('submissionUrl')

/**
* Persist files by extending the time-to-live to 30 days
* @param {{fileId: string, initiatedRetrievalKey: string}[]} files - batch of files to persist
* @param {string} persistedRetrievalKey - final retrieval key when submitting
* @param {FilePersistData[]} files Files to persist.
* @param {string} retrievalKey Final retrieval key when submitting (usually the output email).
* @returns {Promise<object>} The result payload.
*/
export async function persistFiles(files, persistedRetrievalKey) {
export async function persistFiles(files, retrievalKey) {
const postJsonByType = /** @type {typeof postJson<object>} */ (postJson)

const payload = {
files,
persistedRetrievalKey
retrievalKey,
files
}

const result = await postJsonByType(`${submissionUrl}/files/persist`, {
Expand All @@ -23,6 +23,21 @@ export async function persistFiles(files, persistedRetrievalKey) {
return result
}

/**
* Extend the retention time for uploaded files.
* @param {FilePersistData[]} files Array of file objects.
* @param {string} retrievalKey The key (usually the user's email) to be used for persistence.
* @returns {Promise<object>} The result payload.
*/
export async function extendFileRetention(files, retrievalKey) {
const payload = {
retrievalKey,
files
}
const result = await postJson(`${submissionUrl}/file/extend`, { payload })
return result
}

/**
* Submit form
* @param {SubmitPayload} data - submission data
Expand All @@ -44,3 +59,7 @@ export async function submit(data) {
/**
* @import { SubmitPayload, SubmitResponsePayload } from '@defra/forms-model'
*/

/**
* @import { FilePersistData } from '~/src/server/plugins/engine/services/types.js'
*/
5 changes: 5 additions & 0 deletions src/server/plugins/engine/services/types.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
/**
* @typedef {object} FilePersistData
* @property {string} fileId The unique identifier for the file.
* @property {string} [initiatedRetrievalKey] The retrieval key that was assigned when the file was initiated.
*/
3 changes: 2 additions & 1 deletion src/server/plugins/session.ts
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,8 @@ export default {
// storeBlank: false,
cookieOptions: {
password: config.get('sessionCookiePassword'),
isSecure: config.get('isProduction')
isSecure: config.get('isProduction'),
ttl: 7 * 24 * 60 * 60 * 1000 // 7 days
}
}
} satisfies ServerRegisterPluginObject<YarOptions>
3 changes: 2 additions & 1 deletion src/server/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import {

import { type FormModel } from '~/src/server/plugins/engine/models/index.js'
import { type DetailItem } from '~/src/server/plugins/engine/models/types.js'
import { type FilePersistData } from '~/src/server/plugins/engine/services/types.js'
import {
type FormRequestPayload,
type FormStatus
Expand All @@ -22,7 +23,7 @@ export interface FormsService {

export interface FormSubmissionService {
persistFiles: (
files: { fileId: string; initiatedRetrievalKey: string }[],
files: FilePersistData[],
persistedRetrievalKey: string
) => Promise<object>
submit: (data: SubmitPayload) => Promise<SubmitResponsePayload | undefined>
Expand Down
2 changes: 1 addition & 1 deletion src/server/views/help/cookies.html
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ <h2 class="govuk-heading-m">Essential cookies</h2>
[
{ text: "session" },
{ text: "Remembers the information you enter" },
{ text: "When you close the browser, or after 24 hours" }
{ text: "When you close the browser, or after 7 days" }
],
[
{ text: "crumb" },
Expand Down