@defra/forms-engine-plugin 0.1.9 → 0.1.10
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/.server/server/forms/test.yaml +363 -0
- package/.server/server/utils/file-form-service.js +134 -0
- package/.server/server/utils/file-form-service.js.map +1 -0
- package/.server/server/utils/file-form-service.test.js +52 -0
- package/.server/server/utils/file-form-service.test.js.map +1 -0
- package/README.md +3 -1
- package/package.json +6 -3
- package/src/server/forms/test.yaml +363 -0
- package/src/server/plugins/engine/components/AutocompleteField.test.ts +5 -5
- package/src/server/plugins/engine/components/CheckboxesField.test.ts +7 -7
- package/src/server/plugins/engine/components/List.test.ts +3 -0
- package/src/server/plugins/engine/components/RadiosField.test.ts +5 -5
- package/src/server/plugins/engine/components/SelectField.test.ts +5 -5
- package/src/server/plugins/engine/pageControllers/QuestionPageController.test.ts +4 -0
- package/src/server/utils/file-form-service.js +144 -0
- package/src/server/utils/file-form-service.test.js +79 -0
|
@@ -0,0 +1,144 @@
|
|
|
1
|
+
import fs from 'fs/promises'
|
|
2
|
+
import path from 'node:path'
|
|
3
|
+
|
|
4
|
+
import YAML from 'yaml'
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* FileFormService class
|
|
8
|
+
*/
|
|
9
|
+
export class FileFormService {
|
|
10
|
+
/**
|
|
11
|
+
* The map of form metadatas by slug
|
|
12
|
+
* @type {Map<string, FormMetadata>}
|
|
13
|
+
*/
|
|
14
|
+
#metadata = new Map()
|
|
15
|
+
|
|
16
|
+
/**
|
|
17
|
+
* The map of form definitions by id
|
|
18
|
+
* @type {Map<string, FormDefinition>}
|
|
19
|
+
*/
|
|
20
|
+
#definition = new Map()
|
|
21
|
+
|
|
22
|
+
/**
|
|
23
|
+
* Add form from a file
|
|
24
|
+
* @param {string} filepath - the file path
|
|
25
|
+
* @param {FormMetadata} metadata - the metadata to use for this form
|
|
26
|
+
* @returns {Promise<FormDefinition>}
|
|
27
|
+
*/
|
|
28
|
+
async addForm(filepath, metadata) {
|
|
29
|
+
const definition = await this.readForm(filepath)
|
|
30
|
+
|
|
31
|
+
this.#metadata.set(metadata.slug, metadata)
|
|
32
|
+
this.#definition.set(metadata.id, definition)
|
|
33
|
+
|
|
34
|
+
return definition
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
/**
|
|
38
|
+
* Read the form definition from file
|
|
39
|
+
* @param {string} filepath - the file path
|
|
40
|
+
* @returns {Promise<FormDefinition>}
|
|
41
|
+
*/
|
|
42
|
+
async readForm(filepath) {
|
|
43
|
+
const ext = path.extname(filepath).toLowerCase()
|
|
44
|
+
|
|
45
|
+
switch (ext) {
|
|
46
|
+
case '.json':
|
|
47
|
+
return this.readJsonForm(filepath)
|
|
48
|
+
case '.yaml':
|
|
49
|
+
return this.readYamlForm(filepath)
|
|
50
|
+
default:
|
|
51
|
+
throw new Error(`Invalid file extension '${ext}'`)
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
/**
|
|
56
|
+
* Read the form definition from a json file
|
|
57
|
+
* @param {string} filepath - the file path
|
|
58
|
+
* @returns {Promise<FormDefinition>}
|
|
59
|
+
*/
|
|
60
|
+
async readJsonForm(filepath) {
|
|
61
|
+
/**
|
|
62
|
+
* @type {FormDefinition}
|
|
63
|
+
*/
|
|
64
|
+
// eslint-disable-next-line @typescript-eslint/no-unsafe-assignment
|
|
65
|
+
const definition = JSON.parse(await fs.readFile(filepath, 'utf8'))
|
|
66
|
+
|
|
67
|
+
return definition
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
/**
|
|
71
|
+
* Read the form definition from a yaml file
|
|
72
|
+
* @param {string} filepath - the file path
|
|
73
|
+
* @returns {Promise<FormDefinition>}
|
|
74
|
+
*/
|
|
75
|
+
async readYamlForm(filepath) {
|
|
76
|
+
/**
|
|
77
|
+
* @type {FormDefinition}
|
|
78
|
+
*/
|
|
79
|
+
// eslint-disable-next-line @typescript-eslint/no-unsafe-assignment
|
|
80
|
+
const definition = YAML.parse(await fs.readFile(filepath, 'utf8'))
|
|
81
|
+
|
|
82
|
+
return definition
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
/**
|
|
86
|
+
* Get the form metadata by slug
|
|
87
|
+
* @param {string} slug - the form slug
|
|
88
|
+
* @returns {FormMetadata}
|
|
89
|
+
*/
|
|
90
|
+
getFormMetadata(slug) {
|
|
91
|
+
const metadata = this.#metadata.get(slug)
|
|
92
|
+
|
|
93
|
+
if (!metadata) {
|
|
94
|
+
throw new Error(`Form metadata '${slug}' not found`)
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
return metadata
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
/**
|
|
101
|
+
* Get the form defintion by id
|
|
102
|
+
* @param {string} id - the form id
|
|
103
|
+
* @returns {FormDefinition}
|
|
104
|
+
*/
|
|
105
|
+
getFormDefinition(id) {
|
|
106
|
+
const definition = this.#definition.get(id)
|
|
107
|
+
|
|
108
|
+
if (!definition) {
|
|
109
|
+
throw new Error(`Form definition '${id}' not found`)
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
return definition
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
/**
|
|
116
|
+
* Returns a FormsService compliant interface
|
|
117
|
+
* @returns {import('~/src/server/types.js').FormsService}
|
|
118
|
+
*/
|
|
119
|
+
toFormsService() {
|
|
120
|
+
return {
|
|
121
|
+
/**
|
|
122
|
+
* Get the form metadata by slug
|
|
123
|
+
* @param {string} slug
|
|
124
|
+
* @returns {Promise<FormMetadata>}
|
|
125
|
+
*/
|
|
126
|
+
getFormMetadata: (slug) => {
|
|
127
|
+
return Promise.resolve(this.getFormMetadata(slug))
|
|
128
|
+
},
|
|
129
|
+
|
|
130
|
+
/**
|
|
131
|
+
* Get the form defintion by id
|
|
132
|
+
* @param {string} id
|
|
133
|
+
* @returns {Promise<FormDefinition>}
|
|
134
|
+
*/
|
|
135
|
+
getFormDefinition: (id) => {
|
|
136
|
+
return Promise.resolve(this.getFormDefinition(id))
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
/**
|
|
143
|
+
* @import { FormMetadata, FormDefinition } from '@defra/forms-model'
|
|
144
|
+
*/
|
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
import { FormStatus } from '~/src/server/routes/types.js'
|
|
2
|
+
import { FileFormService } from '~/src/server/utils/file-form-service.js'
|
|
3
|
+
|
|
4
|
+
// Create the metadata which is shared for all forms
|
|
5
|
+
const now = new Date()
|
|
6
|
+
const user = { id: 'user', displayName: 'Username' }
|
|
7
|
+
const author = {
|
|
8
|
+
createdAt: now,
|
|
9
|
+
createdBy: user,
|
|
10
|
+
updatedAt: now,
|
|
11
|
+
updatedBy: user
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
const metadata = {
|
|
15
|
+
id: '95e92559-968d-44ae-8666-2b1ad3dffd31',
|
|
16
|
+
slug: 'example-form',
|
|
17
|
+
title: 'Example form',
|
|
18
|
+
organisation: 'Defra',
|
|
19
|
+
teamName: 'Team name',
|
|
20
|
+
teamEmail: 'team@defra.gov.uk',
|
|
21
|
+
submissionGuidance: "Thanks for your submission, we'll be in touch",
|
|
22
|
+
notificationEmail: 'email@domain.com',
|
|
23
|
+
...author,
|
|
24
|
+
live: author
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
describe('File Form Service', () => {
|
|
28
|
+
it('should load JSON files from disk', async () => {
|
|
29
|
+
const loader = new FileFormService()
|
|
30
|
+
|
|
31
|
+
const definition = await loader.addForm(
|
|
32
|
+
'src/server/forms/test.json',
|
|
33
|
+
metadata
|
|
34
|
+
)
|
|
35
|
+
|
|
36
|
+
const formsService = loader.toFormsService()
|
|
37
|
+
expect(await formsService.getFormMetadata(metadata.slug)).toBe(metadata)
|
|
38
|
+
expect(
|
|
39
|
+
await formsService.getFormDefinition(metadata.id, FormStatus.Draft)
|
|
40
|
+
).toBe(definition)
|
|
41
|
+
|
|
42
|
+
expect(() => loader.getFormMetadata('invalid-slug')).toThrow(
|
|
43
|
+
"Form metadata 'invalid-slug' not found"
|
|
44
|
+
)
|
|
45
|
+
expect(() => loader.getFormDefinition('invalid-id')).toThrow(
|
|
46
|
+
"Form definition 'invalid-id' not found"
|
|
47
|
+
)
|
|
48
|
+
})
|
|
49
|
+
|
|
50
|
+
it('should load YAML files from disk', async () => {
|
|
51
|
+
const loader = new FileFormService()
|
|
52
|
+
|
|
53
|
+
const definition = await loader.addForm(
|
|
54
|
+
'src/server/forms/test.yaml',
|
|
55
|
+
metadata
|
|
56
|
+
)
|
|
57
|
+
|
|
58
|
+
const formsService = loader.toFormsService()
|
|
59
|
+
expect(await formsService.getFormMetadata(metadata.slug)).toBe(metadata)
|
|
60
|
+
expect(
|
|
61
|
+
await formsService.getFormDefinition(metadata.id, FormStatus.Draft)
|
|
62
|
+
).toBe(definition)
|
|
63
|
+
|
|
64
|
+
expect(() => loader.getFormMetadata('invalid-slug')).toThrow(
|
|
65
|
+
"Form metadata 'invalid-slug' not found"
|
|
66
|
+
)
|
|
67
|
+
expect(() => loader.getFormDefinition('invalid-id')).toThrow(
|
|
68
|
+
"Form definition 'invalid-id' not found"
|
|
69
|
+
)
|
|
70
|
+
})
|
|
71
|
+
|
|
72
|
+
it("should throw if the file isn't JSON or YAML", async () => {
|
|
73
|
+
const loader = new FileFormService()
|
|
74
|
+
|
|
75
|
+
await expect(
|
|
76
|
+
loader.addForm('src/server/forms/test.txt', metadata)
|
|
77
|
+
).rejects.toThrow("Invalid file extension '.txt'")
|
|
78
|
+
})
|
|
79
|
+
})
|