@defra/forms-engine-plugin 1.0.2 → 1.0.3
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/plugins/engine/options.d.ts +7 -0
- package/.server/server/plugins/engine/options.js +36 -0
- package/.server/server/plugins/engine/options.js.map +1 -0
- package/.server/server/plugins/engine/options.test.js +33 -0
- package/.server/server/plugins/engine/options.test.js.map +1 -0
- package/.server/server/plugins/engine/plugin.js +2 -0
- package/.server/server/plugins/engine/plugin.js.map +1 -1
- package/package.json +1 -1
- package/src/server/plugins/engine/options.js +37 -0
- package/src/server/plugins/engine/options.test.js +34 -0
- package/src/server/plugins/engine/plugin.ts +3 -0
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Validates the plugin options against the schema and returns the validated value.
|
|
3
|
+
* @param {PluginOptions} options
|
|
4
|
+
* @returns {PluginOptions}
|
|
5
|
+
*/
|
|
6
|
+
export function validatePluginOptions(options: PluginOptions): PluginOptions;
|
|
7
|
+
import type { PluginOptions } from '~/src/server/plugins/engine/types.js';
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
import Joi from 'joi';
|
|
2
|
+
const pluginRegistrationOptionsSchema = Joi.object({
|
|
3
|
+
model: Joi.object().optional(),
|
|
4
|
+
services: Joi.object().optional(),
|
|
5
|
+
controllers: Joi.object().pattern(Joi.string(), Joi.any()).optional(),
|
|
6
|
+
cacheName: Joi.string().optional(),
|
|
7
|
+
filters: Joi.object().pattern(Joi.string(), Joi.any()).optional(),
|
|
8
|
+
pluginPath: Joi.string().optional(),
|
|
9
|
+
nunjucks: Joi.object({
|
|
10
|
+
baseLayoutPath: Joi.string().required(),
|
|
11
|
+
paths: Joi.array().items(Joi.string()).required()
|
|
12
|
+
}).required(),
|
|
13
|
+
viewContext: Joi.function().required()
|
|
14
|
+
});
|
|
15
|
+
|
|
16
|
+
/**
|
|
17
|
+
* Validates the plugin options against the schema and returns the validated value.
|
|
18
|
+
* @param {PluginOptions} options
|
|
19
|
+
* @returns {PluginOptions}
|
|
20
|
+
*/
|
|
21
|
+
export function validatePluginOptions(options) {
|
|
22
|
+
const result = pluginRegistrationOptionsSchema.validate(options, {
|
|
23
|
+
abortEarly: false
|
|
24
|
+
});
|
|
25
|
+
if (result.error) {
|
|
26
|
+
throw new Error('Invalid plugin options', result.error);
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
// eslint-disable-next-line @typescript-eslint/no-unsafe-return
|
|
30
|
+
return result.value;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
/**
|
|
34
|
+
* @import { PluginOptions } from '~/src/server/plugins/engine/types.js'
|
|
35
|
+
*/
|
|
36
|
+
//# sourceMappingURL=options.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"options.js","names":["Joi","pluginRegistrationOptionsSchema","object","model","optional","services","controllers","pattern","string","any","cacheName","filters","pluginPath","nunjucks","baseLayoutPath","required","paths","array","items","viewContext","function","validatePluginOptions","options","result","validate","abortEarly","error","Error","value"],"sources":["../../../../src/server/plugins/engine/options.js"],"sourcesContent":["import Joi from 'joi'\n\nconst pluginRegistrationOptionsSchema = Joi.object({\n model: Joi.object().optional(),\n services: Joi.object().optional(),\n controllers: Joi.object().pattern(Joi.string(), Joi.any()).optional(),\n cacheName: Joi.string().optional(),\n filters: Joi.object().pattern(Joi.string(), Joi.any()).optional(),\n pluginPath: Joi.string().optional(),\n nunjucks: Joi.object({\n baseLayoutPath: Joi.string().required(),\n paths: Joi.array().items(Joi.string()).required()\n }).required(),\n viewContext: Joi.function().required()\n})\n\n/**\n * Validates the plugin options against the schema and returns the validated value.\n * @param {PluginOptions} options\n * @returns {PluginOptions}\n */\nexport function validatePluginOptions(options) {\n const result = pluginRegistrationOptionsSchema.validate(options, {\n abortEarly: false\n })\n\n if (result.error) {\n throw new Error('Invalid plugin options', result.error)\n }\n\n // eslint-disable-next-line @typescript-eslint/no-unsafe-return\n return result.value\n}\n\n/**\n * @import { PluginOptions } from '~/src/server/plugins/engine/types.js'\n */\n"],"mappings":"AAAA,OAAOA,GAAG,MAAM,KAAK;AAErB,MAAMC,+BAA+B,GAAGD,GAAG,CAACE,MAAM,CAAC;EACjDC,KAAK,EAAEH,GAAG,CAACE,MAAM,CAAC,CAAC,CAACE,QAAQ,CAAC,CAAC;EAC9BC,QAAQ,EAAEL,GAAG,CAACE,MAAM,CAAC,CAAC,CAACE,QAAQ,CAAC,CAAC;EACjCE,WAAW,EAAEN,GAAG,CAACE,MAAM,CAAC,CAAC,CAACK,OAAO,CAACP,GAAG,CAACQ,MAAM,CAAC,CAAC,EAAER,GAAG,CAACS,GAAG,CAAC,CAAC,CAAC,CAACL,QAAQ,CAAC,CAAC;EACrEM,SAAS,EAAEV,GAAG,CAACQ,MAAM,CAAC,CAAC,CAACJ,QAAQ,CAAC,CAAC;EAClCO,OAAO,EAAEX,GAAG,CAACE,MAAM,CAAC,CAAC,CAACK,OAAO,CAACP,GAAG,CAACQ,MAAM,CAAC,CAAC,EAAER,GAAG,CAACS,GAAG,CAAC,CAAC,CAAC,CAACL,QAAQ,CAAC,CAAC;EACjEQ,UAAU,EAAEZ,GAAG,CAACQ,MAAM,CAAC,CAAC,CAACJ,QAAQ,CAAC,CAAC;EACnCS,QAAQ,EAAEb,GAAG,CAACE,MAAM,CAAC;IACnBY,cAAc,EAAEd,GAAG,CAACQ,MAAM,CAAC,CAAC,CAACO,QAAQ,CAAC,CAAC;IACvCC,KAAK,EAAEhB,GAAG,CAACiB,KAAK,CAAC,CAAC,CAACC,KAAK,CAAClB,GAAG,CAACQ,MAAM,CAAC,CAAC,CAAC,CAACO,QAAQ,CAAC;EAClD,CAAC,CAAC,CAACA,QAAQ,CAAC,CAAC;EACbI,WAAW,EAAEnB,GAAG,CAACoB,QAAQ,CAAC,CAAC,CAACL,QAAQ,CAAC;AACvC,CAAC,CAAC;;AAEF;AACA;AACA;AACA;AACA;AACA,OAAO,SAASM,qBAAqBA,CAACC,OAAO,EAAE;EAC7C,MAAMC,MAAM,GAAGtB,+BAA+B,CAACuB,QAAQ,CAACF,OAAO,EAAE;IAC/DG,UAAU,EAAE;EACd,CAAC,CAAC;EAEF,IAAIF,MAAM,CAACG,KAAK,EAAE;IAChB,MAAM,IAAIC,KAAK,CAAC,wBAAwB,EAAEJ,MAAM,CAACG,KAAK,CAAC;EACzD;;EAEA;EACA,OAAOH,MAAM,CAACK,KAAK;AACrB;;AAEA;AACA;AACA","ignoreList":[]}
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import { validatePluginOptions } from "./options.js";
|
|
2
|
+
describe('validatePluginOptions', () => {
|
|
3
|
+
it('returns the validated value for valid options', () => {
|
|
4
|
+
const validOptions = {
|
|
5
|
+
nunjucks: {
|
|
6
|
+
baseLayoutPath: 'dxt-devtool-baselayout.html',
|
|
7
|
+
paths: ['src/server/devserver'] // custom layout to make it really clear this is not the same as the runner
|
|
8
|
+
},
|
|
9
|
+
viewContext: () => {
|
|
10
|
+
return {
|
|
11
|
+
hello: 'world'
|
|
12
|
+
};
|
|
13
|
+
}
|
|
14
|
+
};
|
|
15
|
+
expect(validatePluginOptions(validOptions)).toEqual(validOptions);
|
|
16
|
+
});
|
|
17
|
+
|
|
18
|
+
/**
|
|
19
|
+
* tsc would usually check compliance with the type, but given a user might be using plain JS we still want a test
|
|
20
|
+
*/
|
|
21
|
+
it('fails if a required attribute is missing', () => {
|
|
22
|
+
const invalidOptions = {
|
|
23
|
+
nunjucks: {
|
|
24
|
+
baseLayoutPath: 'dxt-devtool-baselayout.html',
|
|
25
|
+
paths: ['src/server/devserver'] // custom layout to make it really clear this is not the same as the runner
|
|
26
|
+
}
|
|
27
|
+
};
|
|
28
|
+
|
|
29
|
+
// @ts-expect-error -- add a test for JS users
|
|
30
|
+
expect(() => validatePluginOptions(invalidOptions)).toThrow('Invalid plugin options');
|
|
31
|
+
});
|
|
32
|
+
});
|
|
33
|
+
//# sourceMappingURL=options.test.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"options.test.js","names":["validatePluginOptions","describe","it","validOptions","nunjucks","baseLayoutPath","paths","viewContext","hello","expect","toEqual","invalidOptions","toThrow"],"sources":["../../../../src/server/plugins/engine/options.test.js"],"sourcesContent":["import { validatePluginOptions } from '~/src/server/plugins/engine/options.js'\n\ndescribe('validatePluginOptions', () => {\n it('returns the validated value for valid options', () => {\n const validOptions = {\n nunjucks: {\n baseLayoutPath: 'dxt-devtool-baselayout.html',\n paths: ['src/server/devserver'] // custom layout to make it really clear this is not the same as the runner\n },\n viewContext: () => {\n return { hello: 'world' }\n }\n }\n\n expect(validatePluginOptions(validOptions)).toEqual(validOptions)\n })\n\n /**\n * tsc would usually check compliance with the type, but given a user might be using plain JS we still want a test\n */\n it('fails if a required attribute is missing', () => {\n const invalidOptions = {\n nunjucks: {\n baseLayoutPath: 'dxt-devtool-baselayout.html',\n paths: ['src/server/devserver'] // custom layout to make it really clear this is not the same as the runner\n }\n }\n\n // @ts-expect-error -- add a test for JS users\n expect(() => validatePluginOptions(invalidOptions)).toThrow(\n 'Invalid plugin options'\n )\n })\n})\n"],"mappings":"AAAA,SAASA,qBAAqB;AAE9BC,QAAQ,CAAC,uBAAuB,EAAE,MAAM;EACtCC,EAAE,CAAC,+CAA+C,EAAE,MAAM;IACxD,MAAMC,YAAY,GAAG;MACnBC,QAAQ,EAAE;QACRC,cAAc,EAAE,6BAA6B;QAC7CC,KAAK,EAAE,CAAC,sBAAsB,CAAC,CAAC;MAClC,CAAC;MACDC,WAAW,EAAEA,CAAA,KAAM;QACjB,OAAO;UAAEC,KAAK,EAAE;QAAQ,CAAC;MAC3B;IACF,CAAC;IAEDC,MAAM,CAACT,qBAAqB,CAACG,YAAY,CAAC,CAAC,CAACO,OAAO,CAACP,YAAY,CAAC;EACnE,CAAC,CAAC;;EAEF;AACF;AACA;EACED,EAAE,CAAC,0CAA0C,EAAE,MAAM;IACnD,MAAMS,cAAc,GAAG;MACrBP,QAAQ,EAAE;QACRC,cAAc,EAAE,6BAA6B;QAC7CC,KAAK,EAAE,CAAC,sBAAsB,CAAC,CAAC;MAClC;IACF,CAAC;;IAED;IACAG,MAAM,CAAC,MAAMT,qBAAqB,CAACW,cAAc,CAAC,CAAC,CAACC,OAAO,CACzD,wBACF,CAAC;EACH,CAAC,CAAC;AACJ,CAAC,CAAC","ignoreList":[]}
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { validatePluginOptions } from "./options.js";
|
|
1
2
|
import { getRoutes as getFileUploadStatusRoutes } from "./routes/file-upload.js";
|
|
2
3
|
import { makeLoadFormPreHandler } from "./routes/index.js";
|
|
3
4
|
import { getRoutes as getQuestionRoutes } from "./routes/questions.js";
|
|
@@ -10,6 +11,7 @@ export const plugin = {
|
|
|
10
11
|
dependencies: ['@hapi/crumb', '@hapi/yar', 'hapi-pino'],
|
|
11
12
|
multiple: true,
|
|
12
13
|
async register(server, options) {
|
|
14
|
+
options = validatePluginOptions(options);
|
|
13
15
|
const {
|
|
14
16
|
model,
|
|
15
17
|
cacheName,
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"plugin.js","names":["getRoutes","getFileUploadStatusRoutes","makeLoadFormPreHandler","getQuestionRoutes","getRepeaterItemDeleteRoutes","getRepeaterSummaryRoutes","registerVision","CacheService","plugin","name","dependencies","multiple","register","server","options","model","cacheName","keyGenerator","sessionHydrator","nunjucks","nunjucksOptions","viewContext","cacheService","expose","baseLayoutPath","app","itemCache","Map","models","loadFormPreHandler","getRouteOptions","pre","method","postRouteOptions","payload","parse","routes","route"],"sources":["../../../../src/server/plugins/engine/plugin.ts"],"sourcesContent":["import {\n type Lifecycle,\n type Plugin,\n type RouteOptions,\n type Server,\n type ServerRoute\n} from '@hapi/hapi'\n\nimport { type FormModel } from '~/src/server/plugins/engine/models/index.js'\nimport { getRoutes as getFileUploadStatusRoutes } from '~/src/server/plugins/engine/routes/file-upload.js'\nimport { makeLoadFormPreHandler } from '~/src/server/plugins/engine/routes/index.js'\nimport { getRoutes as getQuestionRoutes } from '~/src/server/plugins/engine/routes/questions.js'\nimport { getRoutes as getRepeaterItemDeleteRoutes } from '~/src/server/plugins/engine/routes/repeaters/item-delete.js'\nimport { getRoutes as getRepeaterSummaryRoutes } from '~/src/server/plugins/engine/routes/repeaters/summary.js'\nimport { type PluginOptions } from '~/src/server/plugins/engine/types.js'\nimport { registerVision } from '~/src/server/plugins/engine/vision.js'\nimport {\n type FormRequestPayloadRefs,\n type FormRequestRefs\n} from '~/src/server/routes/types.js'\nimport { CacheService } from '~/src/server/services/index.js'\n\nexport const plugin = {\n name: '@defra/forms-engine-plugin',\n dependencies: ['@hapi/crumb', '@hapi/yar', 'hapi-pino'],\n multiple: true,\n async register(server: Server, options: PluginOptions) {\n const {\n model,\n cacheName,\n keyGenerator,\n sessionHydrator,\n nunjucks: nunjucksOptions,\n viewContext\n } = options\n const cacheService = new CacheService({\n server,\n cacheName,\n options: {\n keyGenerator,\n sessionHydrator\n }\n })\n\n await registerVision(server, options)\n\n server.expose('baseLayoutPath', nunjucksOptions.baseLayoutPath)\n server.expose('viewContext', viewContext)\n server.expose('cacheService', cacheService)\n\n server.app.model = model\n\n // In-memory cache of FormModel items, exposed\n // (for testing purposes) through `server.app.models`\n const itemCache = new Map<string, { model: FormModel; updatedAt: Date }>()\n server.app.models = itemCache\n\n const loadFormPreHandler = makeLoadFormPreHandler(server, options)\n\n const getRouteOptions: RouteOptions<FormRequestRefs> = {\n pre: [\n {\n method:\n loadFormPreHandler as unknown as Lifecycle.Method<FormRequestRefs>\n }\n ]\n }\n\n const postRouteOptions: RouteOptions<FormRequestPayloadRefs> = {\n payload: {\n parse: true\n },\n pre: [\n {\n method:\n loadFormPreHandler as unknown as Lifecycle.Method<FormRequestPayloadRefs>\n }\n ]\n }\n\n const routes = [\n ...getQuestionRoutes(getRouteOptions, postRouteOptions),\n ...getRepeaterSummaryRoutes(getRouteOptions, postRouteOptions),\n ...getRepeaterItemDeleteRoutes(getRouteOptions, postRouteOptions),\n ...getFileUploadStatusRoutes()\n ]\n\n server.route(routes as unknown as ServerRoute[]) // TODO\n }\n} satisfies Plugin<PluginOptions>\n"],"mappings":"AASA,SAASA,SAAS,IAAIC,yBAAyB;AAC/C,SAASC,sBAAsB;AAC/B,SAASF,SAAS,IAAIG,iBAAiB;AACvC,SAASH,SAAS,IAAII,2BAA2B;AACjD,SAASJ,SAAS,IAAIK,wBAAwB;AAE9C,SAASC,cAAc;AAKvB,SAASC,YAAY;AAErB,OAAO,MAAMC,MAAM,GAAG;EACpBC,IAAI,EAAE,4BAA4B;EAClCC,YAAY,EAAE,CAAC,aAAa,EAAE,WAAW,EAAE,WAAW,CAAC;EACvDC,QAAQ,EAAE,IAAI;EACd,MAAMC,QAAQA,CAACC,MAAc,EAAEC,OAAsB,EAAE;
|
|
1
|
+
{"version":3,"file":"plugin.js","names":["validatePluginOptions","getRoutes","getFileUploadStatusRoutes","makeLoadFormPreHandler","getQuestionRoutes","getRepeaterItemDeleteRoutes","getRepeaterSummaryRoutes","registerVision","CacheService","plugin","name","dependencies","multiple","register","server","options","model","cacheName","keyGenerator","sessionHydrator","nunjucks","nunjucksOptions","viewContext","cacheService","expose","baseLayoutPath","app","itemCache","Map","models","loadFormPreHandler","getRouteOptions","pre","method","postRouteOptions","payload","parse","routes","route"],"sources":["../../../../src/server/plugins/engine/plugin.ts"],"sourcesContent":["import {\n type Lifecycle,\n type Plugin,\n type RouteOptions,\n type Server,\n type ServerRoute\n} from '@hapi/hapi'\n\nimport { type FormModel } from '~/src/server/plugins/engine/models/index.js'\nimport { validatePluginOptions } from '~/src/server/plugins/engine/options.js'\nimport { getRoutes as getFileUploadStatusRoutes } from '~/src/server/plugins/engine/routes/file-upload.js'\nimport { makeLoadFormPreHandler } from '~/src/server/plugins/engine/routes/index.js'\nimport { getRoutes as getQuestionRoutes } from '~/src/server/plugins/engine/routes/questions.js'\nimport { getRoutes as getRepeaterItemDeleteRoutes } from '~/src/server/plugins/engine/routes/repeaters/item-delete.js'\nimport { getRoutes as getRepeaterSummaryRoutes } from '~/src/server/plugins/engine/routes/repeaters/summary.js'\nimport { type PluginOptions } from '~/src/server/plugins/engine/types.js'\nimport { registerVision } from '~/src/server/plugins/engine/vision.js'\nimport {\n type FormRequestPayloadRefs,\n type FormRequestRefs\n} from '~/src/server/routes/types.js'\nimport { CacheService } from '~/src/server/services/index.js'\n\nexport const plugin = {\n name: '@defra/forms-engine-plugin',\n dependencies: ['@hapi/crumb', '@hapi/yar', 'hapi-pino'],\n multiple: true,\n async register(server: Server, options: PluginOptions) {\n options = validatePluginOptions(options)\n\n const {\n model,\n cacheName,\n keyGenerator,\n sessionHydrator,\n nunjucks: nunjucksOptions,\n viewContext\n } = options\n const cacheService = new CacheService({\n server,\n cacheName,\n options: {\n keyGenerator,\n sessionHydrator\n }\n })\n\n await registerVision(server, options)\n\n server.expose('baseLayoutPath', nunjucksOptions.baseLayoutPath)\n server.expose('viewContext', viewContext)\n server.expose('cacheService', cacheService)\n\n server.app.model = model\n\n // In-memory cache of FormModel items, exposed\n // (for testing purposes) through `server.app.models`\n const itemCache = new Map<string, { model: FormModel; updatedAt: Date }>()\n server.app.models = itemCache\n\n const loadFormPreHandler = makeLoadFormPreHandler(server, options)\n\n const getRouteOptions: RouteOptions<FormRequestRefs> = {\n pre: [\n {\n method:\n loadFormPreHandler as unknown as Lifecycle.Method<FormRequestRefs>\n }\n ]\n }\n\n const postRouteOptions: RouteOptions<FormRequestPayloadRefs> = {\n payload: {\n parse: true\n },\n pre: [\n {\n method:\n loadFormPreHandler as unknown as Lifecycle.Method<FormRequestPayloadRefs>\n }\n ]\n }\n\n const routes = [\n ...getQuestionRoutes(getRouteOptions, postRouteOptions),\n ...getRepeaterSummaryRoutes(getRouteOptions, postRouteOptions),\n ...getRepeaterItemDeleteRoutes(getRouteOptions, postRouteOptions),\n ...getFileUploadStatusRoutes()\n ]\n\n server.route(routes as unknown as ServerRoute[]) // TODO\n }\n} satisfies Plugin<PluginOptions>\n"],"mappings":"AASA,SAASA,qBAAqB;AAC9B,SAASC,SAAS,IAAIC,yBAAyB;AAC/C,SAASC,sBAAsB;AAC/B,SAASF,SAAS,IAAIG,iBAAiB;AACvC,SAASH,SAAS,IAAII,2BAA2B;AACjD,SAASJ,SAAS,IAAIK,wBAAwB;AAE9C,SAASC,cAAc;AAKvB,SAASC,YAAY;AAErB,OAAO,MAAMC,MAAM,GAAG;EACpBC,IAAI,EAAE,4BAA4B;EAClCC,YAAY,EAAE,CAAC,aAAa,EAAE,WAAW,EAAE,WAAW,CAAC;EACvDC,QAAQ,EAAE,IAAI;EACd,MAAMC,QAAQA,CAACC,MAAc,EAAEC,OAAsB,EAAE;IACrDA,OAAO,GAAGf,qBAAqB,CAACe,OAAO,CAAC;IAExC,MAAM;MACJC,KAAK;MACLC,SAAS;MACTC,YAAY;MACZC,eAAe;MACfC,QAAQ,EAAEC,eAAe;MACzBC;IACF,CAAC,GAAGP,OAAO;IACX,MAAMQ,YAAY,GAAG,IAAIf,YAAY,CAAC;MACpCM,MAAM;MACNG,SAAS;MACTF,OAAO,EAAE;QACPG,YAAY;QACZC;MACF;IACF,CAAC,CAAC;IAEF,MAAMZ,cAAc,CAACO,MAAM,EAAEC,OAAO,CAAC;IAErCD,MAAM,CAACU,MAAM,CAAC,gBAAgB,EAAEH,eAAe,CAACI,cAAc,CAAC;IAC/DX,MAAM,CAACU,MAAM,CAAC,aAAa,EAAEF,WAAW,CAAC;IACzCR,MAAM,CAACU,MAAM,CAAC,cAAc,EAAED,YAAY,CAAC;IAE3CT,MAAM,CAACY,GAAG,CAACV,KAAK,GAAGA,KAAK;;IAExB;IACA;IACA,MAAMW,SAAS,GAAG,IAAIC,GAAG,CAAgD,CAAC;IAC1Ed,MAAM,CAACY,GAAG,CAACG,MAAM,GAAGF,SAAS;IAE7B,MAAMG,kBAAkB,GAAG3B,sBAAsB,CAACW,MAAM,EAAEC,OAAO,CAAC;IAElE,MAAMgB,eAA8C,GAAG;MACrDC,GAAG,EAAE,CACH;QACEC,MAAM,EACJH;MACJ,CAAC;IAEL,CAAC;IAED,MAAMI,gBAAsD,GAAG;MAC7DC,OAAO,EAAE;QACPC,KAAK,EAAE;MACT,CAAC;MACDJ,GAAG,EAAE,CACH;QACEC,MAAM,EACJH;MACJ,CAAC;IAEL,CAAC;IAED,MAAMO,MAAM,GAAG,CACb,GAAGjC,iBAAiB,CAAC2B,eAAe,EAAEG,gBAAgB,CAAC,EACvD,GAAG5B,wBAAwB,CAACyB,eAAe,EAAEG,gBAAgB,CAAC,EAC9D,GAAG7B,2BAA2B,CAAC0B,eAAe,EAAEG,gBAAgB,CAAC,EACjE,GAAGhC,yBAAyB,CAAC,CAAC,CAC/B;IAEDY,MAAM,CAACwB,KAAK,CAACD,MAAkC,CAAC,EAAC;EACnD;AACF,CAAiC","ignoreList":[]}
|
package/package.json
CHANGED
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
import Joi from 'joi'
|
|
2
|
+
|
|
3
|
+
const pluginRegistrationOptionsSchema = Joi.object({
|
|
4
|
+
model: Joi.object().optional(),
|
|
5
|
+
services: Joi.object().optional(),
|
|
6
|
+
controllers: Joi.object().pattern(Joi.string(), Joi.any()).optional(),
|
|
7
|
+
cacheName: Joi.string().optional(),
|
|
8
|
+
filters: Joi.object().pattern(Joi.string(), Joi.any()).optional(),
|
|
9
|
+
pluginPath: Joi.string().optional(),
|
|
10
|
+
nunjucks: Joi.object({
|
|
11
|
+
baseLayoutPath: Joi.string().required(),
|
|
12
|
+
paths: Joi.array().items(Joi.string()).required()
|
|
13
|
+
}).required(),
|
|
14
|
+
viewContext: Joi.function().required()
|
|
15
|
+
})
|
|
16
|
+
|
|
17
|
+
/**
|
|
18
|
+
* Validates the plugin options against the schema and returns the validated value.
|
|
19
|
+
* @param {PluginOptions} options
|
|
20
|
+
* @returns {PluginOptions}
|
|
21
|
+
*/
|
|
22
|
+
export function validatePluginOptions(options) {
|
|
23
|
+
const result = pluginRegistrationOptionsSchema.validate(options, {
|
|
24
|
+
abortEarly: false
|
|
25
|
+
})
|
|
26
|
+
|
|
27
|
+
if (result.error) {
|
|
28
|
+
throw new Error('Invalid plugin options', result.error)
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
// eslint-disable-next-line @typescript-eslint/no-unsafe-return
|
|
32
|
+
return result.value
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
/**
|
|
36
|
+
* @import { PluginOptions } from '~/src/server/plugins/engine/types.js'
|
|
37
|
+
*/
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
import { validatePluginOptions } from '~/src/server/plugins/engine/options.js'
|
|
2
|
+
|
|
3
|
+
describe('validatePluginOptions', () => {
|
|
4
|
+
it('returns the validated value for valid options', () => {
|
|
5
|
+
const validOptions = {
|
|
6
|
+
nunjucks: {
|
|
7
|
+
baseLayoutPath: 'dxt-devtool-baselayout.html',
|
|
8
|
+
paths: ['src/server/devserver'] // custom layout to make it really clear this is not the same as the runner
|
|
9
|
+
},
|
|
10
|
+
viewContext: () => {
|
|
11
|
+
return { hello: 'world' }
|
|
12
|
+
}
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
expect(validatePluginOptions(validOptions)).toEqual(validOptions)
|
|
16
|
+
})
|
|
17
|
+
|
|
18
|
+
/**
|
|
19
|
+
* tsc would usually check compliance with the type, but given a user might be using plain JS we still want a test
|
|
20
|
+
*/
|
|
21
|
+
it('fails if a required attribute is missing', () => {
|
|
22
|
+
const invalidOptions = {
|
|
23
|
+
nunjucks: {
|
|
24
|
+
baseLayoutPath: 'dxt-devtool-baselayout.html',
|
|
25
|
+
paths: ['src/server/devserver'] // custom layout to make it really clear this is not the same as the runner
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
// @ts-expect-error -- add a test for JS users
|
|
30
|
+
expect(() => validatePluginOptions(invalidOptions)).toThrow(
|
|
31
|
+
'Invalid plugin options'
|
|
32
|
+
)
|
|
33
|
+
})
|
|
34
|
+
})
|
|
@@ -7,6 +7,7 @@ import {
|
|
|
7
7
|
} from '@hapi/hapi'
|
|
8
8
|
|
|
9
9
|
import { type FormModel } from '~/src/server/plugins/engine/models/index.js'
|
|
10
|
+
import { validatePluginOptions } from '~/src/server/plugins/engine/options.js'
|
|
10
11
|
import { getRoutes as getFileUploadStatusRoutes } from '~/src/server/plugins/engine/routes/file-upload.js'
|
|
11
12
|
import { makeLoadFormPreHandler } from '~/src/server/plugins/engine/routes/index.js'
|
|
12
13
|
import { getRoutes as getQuestionRoutes } from '~/src/server/plugins/engine/routes/questions.js'
|
|
@@ -25,6 +26,8 @@ export const plugin = {
|
|
|
25
26
|
dependencies: ['@hapi/crumb', '@hapi/yar', 'hapi-pino'],
|
|
26
27
|
multiple: true,
|
|
27
28
|
async register(server: Server, options: PluginOptions) {
|
|
29
|
+
options = validatePluginOptions(options)
|
|
30
|
+
|
|
28
31
|
const {
|
|
29
32
|
model,
|
|
30
33
|
cacheName,
|