@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.
@@ -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
+ })