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
43 changes: 13 additions & 30 deletions src/client/javascripts/file-upload.js
Original file line number Diff line number Diff line change
Expand Up @@ -230,28 +230,13 @@ function reloadPage() {

/**
* Build the upload status URL given the current pathname and the upload ID.
* This only works when called on a file upload page that has a maximum depth of 1 URL segments following the slug.
* @param {string} pathname – e.g. window.location.pathname
* @param {string} uploadId
* @returns {string} e.g. "/form/upload-status/abc123"
* @returns {Promise<string>} e.g. "/form/upload-status/abc123"
*/
export function buildUploadStatusUrl(pathname, uploadId) {
// Remove preview markers and duplicate slashes
const normalisedPath = pathname
.replace(/\/preview\/(draft|live)/g, '')
.replace(/\/{2,}/g, '/')
.replace(/\/$/, '')

const segments = normalisedPath.split('/').filter(Boolean)

// The slug is always the second to last segment
// The prefix is everything before the slug
const prefix =
segments.length > 2
? `/${segments.slice(0, segments.length - 2).join('/')}`
: ''

return `${prefix}/upload-status/${uploadId}`
export function buildUploadStatusUrl(uploadId) {
return fetch('/forms-manifest')
.then((res) => res.json())
.then((manifest) => `${manifest.engineBaseUrl}/upload-status/${uploadId}`)
}

/**
Expand All @@ -269,16 +254,14 @@ function pollUploadStatus(uploadId) {
return
}

const uploadStatusUrl = buildUploadStatusUrl(
window.location.pathname,
uploadId
)

fetch(uploadStatusUrl, {
headers: {
Accept: 'application/json'
}
})
buildUploadStatusUrl(uploadId)
.then((uploadStatusUrl) => {
return fetch(uploadStatusUrl, {
headers: {
Accept: 'application/json'
}
})
})
.then((response) => {
if (!response.ok) {
throw new Error('Network response was not ok')
Expand Down
2 changes: 1 addition & 1 deletion src/server/constants.js
Original file line number Diff line number Diff line change
@@ -1,2 +1,2 @@
export const PREVIEW_PATH_PREFIX = '/preview'
export const FORM_PREFIX = ''
export const FORM_PREFIX = '/my-custom-prefix/with/nested/path'
48 changes: 28 additions & 20 deletions src/server/plugins/engine/configureEnginePlugin.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,11 +4,11 @@ import { type FormDefinition } from '@defra/forms-model'

import { FORM_PREFIX } from '~/src/server/constants.js'
import { FormModel } from '~/src/server/plugins/engine/models/FormModel.js'
import { plugin } from '~/src/server/plugins/engine/plugin.js'
import * as defaultServices from '~/src/server/plugins/engine/services/index.js'
import { formsService } from '~/src/server/plugins/engine/services/localFormsService.js'
import { type PluginOptions } from '~/src/server/plugins/engine/types.js'
import { findPackageRoot } from '~/src/server/plugins/engine/vision.js'
import { plugin as formsPlugin } from '~/src/server/plugins/forms/index.js'
import { type PluginOptions } from '~/src/server/plugins/forms/types.js'
import { devtoolContext } from '~/src/server/plugins/nunjucks/context.js'
import { type CacheService } from '~/src/server/services/cacheService.js'
import { type RouteConfig } from '~/src/server/types.js'
Expand All @@ -25,7 +25,8 @@ export const configureEnginePlugin = async (
}: RouteConfig = {},
cache?: CacheService
): Promise<{
plugin: typeof plugin
// plugin: typeof enginePlugin
plugin: typeof formsPlugin
options: PluginOptions
}> => {
let model: FormModel | undefined
Expand All @@ -45,25 +46,32 @@ export const configureEnginePlugin = async (
}

return {
plugin,
plugin: formsPlugin,
options: {
model,
services: services ?? {
// services for testing, else use the disk loader option for running this service locally
...defaultServices,
formsService: await formsService()
engine: {
model,
services: services ?? {
// services for testing, else use the disk loader option for running this service locally
...defaultServices,
formsService: await formsService()
},
controllers,
cache: cache ?? 'session',
nunjucks: {
baseLayoutPath: 'dxt-devtool-baselayout.html',
paths: [join(findPackageRoot(), 'src/server/devserver')] // custom layout to make it really clear this is not the same as the runner
},
viewContext: devtoolContext,
preparePageEventRequestOptions,
onRequest,
baseUrl: 'http://localhost:3009', // always runs locally
saveAndExit
},
controllers,
cache: cache ?? 'session',
nunjucks: {
baseLayoutPath: 'dxt-devtool-baselayout.html',
paths: [join(findPackageRoot(), 'src/server/devserver')] // custom layout to make it really clear this is not the same as the runner
},
viewContext: devtoolContext,
preparePageEventRequestOptions,
onRequest,
baseUrl: 'http://localhost:3009', // always runs locally
saveAndExit
serverRegistrationOptions: {
routes: {
prefix: FORM_PREFIX
}
}
}
}
}
Expand Down
33 changes: 33 additions & 0 deletions src/server/plugins/forms/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
import { type Plugin, type Server } from '@hapi/hapi'

import enginePlugin from '~/src/server/plugins/engine/index.js'
import { type PluginOptions } from '~/src/server/plugins/forms/types.js'

export const plugin = {
name: '@defra/forms-index',
dependencies: ['@hapi/crumb', '@hapi/yar', 'hapi-pino'],
multiple: true,
async register(server: Server, options?: PluginOptions) {
// routes that should have a predictable URL
server.route({
method: 'GET',
path: '/forms-manifest',
handler: (_, h) => {
const prefix = options?.serverRegistrationOptions?.routes?.prefix ?? ''

return h.response({
engineBaseUrl: prefix
})
}
})

// forms can be prefixed to prevent collisions
await server.register(
{
plugin: enginePlugin,
options: options?.engine
},
options?.serverRegistrationOptions
)
}
} satisfies Plugin<PluginOptions>
8 changes: 8 additions & 0 deletions src/server/plugins/forms/types.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
import { type ServerRegisterOptions } from '@hapi/hapi'

import { type PluginOptions as EnginePluginOptions } from '~/src/server/plugins/engine/types.js'

export interface PluginOptions {
engine: EnginePluginOptions
serverRegistrationOptions?: ServerRegisterOptions
}
121 changes: 59 additions & 62 deletions test/client/javascripts/file-upload.test.js
Original file line number Diff line number Diff line change
@@ -1,7 +1,4 @@
import {
buildUploadStatusUrl,
initFileUpload
} from '~/src/client/javascripts/file-upload.js'
import { initFileUpload } from '~/src/client/javascripts/file-upload.js'

describe('File Upload Client JS', () => {
beforeEach(() => {
Expand Down Expand Up @@ -1345,61 +1342,61 @@ describe('File Upload Client JS', () => {
})
})

describe('buildUploadStatusUrl', () => {
it('works with no prefix', () => {
expect(buildUploadStatusUrl('/my-form/file-upload-page', '123')).toBe(
'/upload-status/123'
)

expect(
buildUploadStatusUrl('/preview/draft/my-form/file-upload-page', '123')
).toBe('/upload-status/123')

expect(
buildUploadStatusUrl('/preview/live/my-form/file-upload-page', '123')
).toBe('/upload-status/123')
})

it('works with a prefix with a single level', () => {
expect(buildUploadStatusUrl('/form/my-form/file-upload-page', '123')).toBe(
'/form/upload-status/123'
)

expect(
buildUploadStatusUrl(
'/form/preview/draft/my-form/file-upload-page',
'123'
)
).toBe('/form/upload-status/123')

expect(
buildUploadStatusUrl('/form/preview/live/my-form/file-upload-page', '123')
).toBe('/form/upload-status/123')
})

it('works with a prefix with multiple levels', () => {
expect(
buildUploadStatusUrl('/org/form/my-form/file-upload-page', '123')
).toBe('/org/form/upload-status/123')

expect(
buildUploadStatusUrl(
'/org/form/preview/draft/my-form/file-upload-page',
'123'
)
).toBe('/org/form/upload-status/123')

expect(
buildUploadStatusUrl(
'/org/form/preview/live/my-form/file-upload-page',
'123'
)
).toBe('/org/form/upload-status/123')
})

it('trims trailing slashes', () => {
expect(buildUploadStatusUrl('/my-form/file-upload-page/', '123')).toBe(
'/upload-status/123'
)
})
})
// describe('buildUploadStatusUrl', () => {
// it('works with no prefix', () => {
// expect(buildUploadStatusUrl('/my-form/file-upload-page', '123')).toBe(
// '/upload-status/123'
// )

// expect(
// buildUploadStatusUrl('/preview/draft/my-form/file-upload-page', '123')
// ).toBe('/upload-status/123')

// expect(
// buildUploadStatusUrl('/preview/live/my-form/file-upload-page', '123')
// ).toBe('/upload-status/123')
// })

// it('works with a prefix with a single level', () => {
// expect(buildUploadStatusUrl('/form/my-form/file-upload-page', '123')).toBe(
// '/form/upload-status/123'
// )

// expect(
// buildUploadStatusUrl(
// '/form/preview/draft/my-form/file-upload-page',
// '123'
// )
// ).toBe('/form/upload-status/123')

// expect(
// buildUploadStatusUrl('/form/preview/live/my-form/file-upload-page', '123')
// ).toBe('/form/upload-status/123')
// })

// it('works with a prefix with multiple levels', () => {
// expect(
// buildUploadStatusUrl('/org/form/my-form/file-upload-page', '123')
// ).toBe('/org/form/upload-status/123')

// expect(
// buildUploadStatusUrl(
// '/org/form/preview/draft/my-form/file-upload-page',
// '123'
// )
// ).toBe('/org/form/upload-status/123')

// expect(
// buildUploadStatusUrl(
// '/org/form/preview/live/my-form/file-upload-page',
// '123'
// )
// ).toBe('/org/form/upload-status/123')
// })

// it('trims trailing slashes', () => {
// expect(buildUploadStatusUrl('/my-form/file-upload-page/', '123')).toBe(
// '/upload-status/123'
// )
// })
// })
Loading