11import fs from 'fs/promises'
2- import crypto from 'node:crypto'
3- import path from 'node:path'
42
5- import Boom from '@hapi/boom'
63import YAML from 'yaml'
74
85/**
9- * Create a deterministic UUID string
10- * @param {string } seed - the seed string
11- * @returns string
12- */
13- function uuid ( seed ) {
14- const uuidLen = 36
15- const firstSepIdx = 8
16- const secondSepIdx = 13
17- const thirdSepIdx = 18
18- const forthSepIdx = 23
19- const hash = crypto
20- . createHash ( 'sha256' )
21- . update ( seed . toString ( ) )
22- . digest ( 'hex' )
23- . substring ( 0 , uuidLen )
24- const chars = hash . split ( '' )
25-
26- chars [ firstSepIdx ] = '-'
27- chars [ secondSepIdx ] = '-'
28- chars [ secondSepIdx + 1 ] = '4'
29- chars [ thirdSepIdx ] = '-'
30- chars [ thirdSepIdx + 1 ] = '8'
31- chars [ forthSepIdx ] = '-'
32-
33- return chars . join ( '' )
34- }
35-
36- /**
37- * FileFormService
6+ * FileFormService abstract class
387 */
398class FileFormService {
409 /**
41- * @type {string }
42- */
43- #ext
44-
45- /**
46- * @type {PartialFormMetadata }
47- */
48- #defaultMetadata
49-
50- /**
10+ * The map of form metadatas by slug
5111 * @type {Map<string, FormMetadata> }
5212 */
5313 #metadata = new Map ( )
5414
5515 /**
16+ * The map of form definitions by id
5617 * @type {Map<string, FormDefinition> }
5718 */
5819 #definition = new Map ( )
5920
6021 /**
61- * @param {string } ext - the file type extension
62- * @param {PartialFormMetadata } metadata - the default partial form metadata to use for all forms
63- */
64- constructor ( ext , metadata ) {
65- this . #ext = ext . toLowerCase ( )
66- this . #defaultMetadata = metadata
67- }
68-
69- /**
70- * @param {string } dir
71- * @param {PartialFormMetadata } metadata - the partial metadata to use for this form
72- */
73- async addDir ( dir , metadata = this . #defaultMetadata) {
74- const dirents = await fs . readdir ( dir , { withFileTypes : true } )
75- const fileEntries = dirents . filter (
76- ( entry ) =>
77- entry . isFile ( ) &&
78- path . extname ( entry . name ) . toLowerCase ( ) === `.${ this . #ext} `
79- )
80-
81- // Read each file
82- for ( const entry of fileEntries ) {
83- await this . addForm (
84- `${ entry . parentPath } ${ path . sep } ${ entry . name } ` ,
85- metadata
86- )
87- }
88- }
89-
90- /**
91- * @param {string } filepath
92- * @param {PartialFormMetadata } metadata - the metadata to use for this form
22+ * Add form from a file
23+ * @param {string } filepath - the file path
24+ * @param {FormMetadata } metadata - the metadata to use for this form
9325 */
94- async addForm ( filepath , metadata = this . #defaultMetadata ) {
26+ async addForm ( filepath , metadata ) {
9527 const definition = await this . readFormDefintion ( filepath )
96- const filename = path . basename ( filepath )
97- const slug = path . basename ( filename , `.${ this . #ext} ` )
98- const id = uuid ( filename )
99- const title = definition . name ?? slug
100- const fullMetadata = { ...metadata , id, slug, title }
101-
102- this . #metadata. set ( slug , fullMetadata )
103- this . #definition. set ( id , definition )
28+
29+ this . #metadata. set ( metadata . slug , metadata )
30+ this . #definition. set ( metadata . id , definition )
10431 }
10532
10633 /**
107- * @param {string } filepath
34+ * Read the form definition from file
35+ * @param {string } filepath - the file path
10836 * @returns {Promise<FormDefinition> }
10937 */
11038 // eslint-disable-next-line @typescript-eslint/require-await
@@ -116,33 +44,59 @@ class FileFormService {
11644
11745 /**
11846 * Get the form metadata by slug
119- * @param {string } slug
47+ * @param {string } slug - the form slug
12048 * @returns {FormMetadata }
12149 */
12250 getFormMetadata ( slug ) {
12351 const metadata = this . #metadata. get ( slug )
12452
12553 if ( ! metadata ) {
126- throw Boom . notFound ( `Form '${ slug } ' not found` )
54+ throw new Error ( `Form metadata '${ slug } ' not found` )
12755 }
12856
12957 return metadata
13058 }
13159
13260 /**
13361 * Get the form defintion by id
134- * @param {string } id
62+ * @param {string } id - the form id
13563 * @returns {FormDefinition }
13664 */
13765 getFormDefinition ( id ) {
13866 const definition = this . #definition. get ( id )
13967
14068 if ( ! definition ) {
141- throw Boom . notFound ( `Form '${ id } ' not found` )
69+ throw new Error ( `Form definition '${ id } ' not found` )
14270 }
14371
14472 return definition
14573 }
74+
75+ /**
76+ * Returns a FormsService compliant interface
77+ * @returns {import('~/src/server/types.js').FormsService }
78+ */
79+ toFormService ( ) {
80+ return {
81+ /**
82+ * Get the form metadata by slug
83+ * @param {string } slug
84+ * @returns {Promise<FormMetadata> }
85+ */
86+ getFormMetadata : ( slug ) => {
87+ return Promise . resolve ( this . getFormMetadata ( slug ) )
88+ } ,
89+
90+ /**
91+ * Get the form defintion by id
92+ * @param {string } id
93+ * @returns {Promise<FormDefinition> }
94+ */
95+ getFormDefinition : ( id ) => {
96+ return Promise . resolve ( this . getFormDefinition ( id ) )
97+ }
98+ }
99+ }
146100}
147101
148102/**
@@ -151,13 +105,7 @@ class FileFormService {
151105 */
152106export class JsonFileFormService extends FileFormService {
153107 /**
154- * @param {FormMetadata } metadata - the default metadata to use for all forms
155- */
156- constructor ( metadata ) {
157- super ( 'json' , metadata )
158- }
159-
160- /**
108+ * Read the form definition from a json file
161109 * @param {string } filepath
162110 * @returns {Promise<FormDefinition> }
163111 */
@@ -178,13 +126,7 @@ export class JsonFileFormService extends FileFormService {
178126 */
179127export class YamlFileFormService extends FileFormService {
180128 /**
181- * @param {FormMetadata } metadata - the default metadata to use for all forms
182- */
183- constructor ( metadata ) {
184- super ( 'yaml' , metadata )
185- }
186-
187- /**
129+ * Read the form definition from a yaml file
188130 * @param {string } filepath
189131 * @returns {Promise<FormDefinition> }
190132 */
@@ -202,8 +144,3 @@ export class YamlFileFormService extends FileFormService {
202144/**
203145 * @import { FormMetadata, FormDefinition } from '@defra/forms-model'
204146 */
205-
206- /**
207- * Partial FormMetadata
208- * @typedef {Omit<FormMetadata, "id" | "slug" | "title"> } PartialFormMetadata
209- */
0 commit comments