Skip to content

Commit 9bcbd03

Browse files
committed
Allow for request scoped services instances in fastify plugin.
1 parent e82a134 commit 9bcbd03

File tree

4 files changed

+70
-65
lines changed

4 files changed

+70
-65
lines changed

conformance/src/fastify/app.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,8 @@ import { jsConformanceApiPlugin } from "./jsConformanceApiPlugin.js";
66

77
const app: FastifyPluginAsync<FastifyServerOptions> = async (fastify): Promise<void> => {
88
const conformanceApiPluginOptions: ConformanceApiPluginOptions = {
9-
api: new ConformanceApiService(conformanceTestsJson.tests),
9+
serviceOrFactory: () =>
10+
new ConformanceApiService(conformanceTestsJson.tests),
1011
caseInsenstiveQueryStringKeys: true,
1112
includeErrorDetails: true,
1213
};

conformance/src/fastify/conformanceApiPlugin.ts

Lines changed: 28 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,24 @@
11
// DO NOT EDIT: generated by fsdgenjs
22
/* eslint-disable */
33

4-
import { FastifyPluginAsync, RegisterOptions } from 'fastify';
5-
import { IServiceResult, IServiceError } from 'facility-core';
4+
import type * as fastifyTypes from 'fastify';
5+
import type { IServiceResult, IServiceError } from 'facility-core';
66

7-
export type ConformanceApiPluginOptions = RegisterOptions & {
8-
api: IConformanceApi;
7+
export type ConformanceApiPluginOptions = fastifyTypes.RegisterOptions & {
8+
/** The `IConformanceApi` service implementation. Can be a service instance or a factory function which is called on each request. */
9+
serviceOrFactory: IConformanceApi | ((req: fastifyTypes.FastifyRequest) => IConformanceApi);
10+
11+
/** Whether to make query string keys case insensitive. Defalts to false. */
912
caseInsenstiveQueryStringKeys?: boolean;
13+
14+
/** Whether to include error details in the response. Defaults to false. */
1015
includeErrorDetails?: boolean;
1116
}
1217

13-
export const conformanceApiPlugin: FastifyPluginAsync<ConformanceApiPluginOptions> = async (fastify, opts) => {
14-
const { api, caseInsenstiveQueryStringKeys, includeErrorDetails } = opts;
18+
export const conformanceApiPlugin: fastifyTypes.FastifyPluginAsync<ConformanceApiPluginOptions> = async (fastify, opts) => {
19+
const { serviceOrFactory, caseInsenstiveQueryStringKeys, includeErrorDetails } = opts;
20+
21+
const getService = typeof serviceOrFactory === 'function' ? serviceOrFactory : () => serviceOrFactory;
1522

1623
for (const jsonSchema of jsonSchemas) {
1724
fastify.addSchema(jsonSchema);
@@ -66,7 +73,7 @@ export const conformanceApiPlugin: FastifyPluginAsync<ConformanceApiPluginOption
6673
handler: async function (req, res) {
6774
const request: Partial<IGetApiInfoRequest> = {};
6875

69-
const result = await api.getApiInfo(request as IGetApiInfoRequest);
76+
const result = await getService(req).getApiInfo(request as IGetApiInfoRequest);
7077

7178
if (result.error) {
7279
const status = result.error.code && standardErrorCodes[result.error.code];
@@ -102,7 +109,7 @@ export const conformanceApiPlugin: FastifyPluginAsync<ConformanceApiPluginOption
102109
const query = req.query as Record<string, string>;
103110
if (typeof query['q'] === 'string') request.query = query['q'];
104111

105-
const result = await api.getWidgets(request as IGetWidgetsRequest);
112+
const result = await getService(req).getWidgets(request as IGetWidgetsRequest);
106113

107114
if (result.error) {
108115
const status = result.error.code && standardErrorCodes[result.error.code];
@@ -132,7 +139,7 @@ export const conformanceApiPlugin: FastifyPluginAsync<ConformanceApiPluginOption
132139

133140
request.widget = req.body as never;
134141

135-
const result = await api.createWidget(request as ICreateWidgetRequest);
142+
const result = await getService(req).createWidget(request as ICreateWidgetRequest);
136143

137144
if (result.error) {
138145
const status = result.error.code && standardErrorCodes[result.error.code];
@@ -172,7 +179,7 @@ export const conformanceApiPlugin: FastifyPluginAsync<ConformanceApiPluginOption
172179
const headers = req.headers as Record<string, string>;
173180
if (typeof headers['if-none-match'] === 'string') request.ifNotETag = headers['if-none-match'];
174181

175-
const result = await api.getWidget(request as IGetWidgetRequest);
182+
const result = await getService(req).getWidget(request as IGetWidgetRequest);
176183

177184
if (result.error) {
178185
const status = result.error.code && standardErrorCodes[result.error.code];
@@ -217,7 +224,7 @@ export const conformanceApiPlugin: FastifyPluginAsync<ConformanceApiPluginOption
217224
const headers = req.headers as Record<string, string>;
218225
if (typeof headers['if-match'] === 'string') request.ifETag = headers['if-match'];
219226

220-
const result = await api.deleteWidget(request as IDeleteWidgetRequest);
227+
const result = await getService(req).deleteWidget(request as IDeleteWidgetRequest);
221228

222229
if (result.error) {
223230
const status = result.error.code && standardErrorCodes[result.error.code];
@@ -257,7 +264,7 @@ export const conformanceApiPlugin: FastifyPluginAsync<ConformanceApiPluginOption
257264

258265
request.ids = req.body as never;
259266

260-
const result = await api.getWidgetBatch(request as IGetWidgetBatchRequest);
267+
const result = await getService(req).getWidgetBatch(request as IGetWidgetBatchRequest);
261268

262269
if (result.error) {
263270
const status = result.error.code && standardErrorCodes[result.error.code];
@@ -297,7 +304,7 @@ export const conformanceApiPlugin: FastifyPluginAsync<ConformanceApiPluginOption
297304
request.field = body.field;
298305
request.matrix = body.matrix;
299306

300-
const result = await api.mirrorFields(request as IMirrorFieldsRequest);
307+
const result = await getService(req).mirrorFields(request as IMirrorFieldsRequest);
301308

302309
if (result.error) {
303310
const status = result.error.code && standardErrorCodes[result.error.code];
@@ -335,7 +342,7 @@ export const conformanceApiPlugin: FastifyPluginAsync<ConformanceApiPluginOption
335342
if (typeof query['enum'] === 'string') request.enum = query['enum'] as Answer;
336343
if (typeof query['datetime'] === 'string') request.datetime = query['datetime'];
337344

338-
const result = await api.checkQuery(request as ICheckQueryRequest);
345+
const result = await getService(req).checkQuery(request as ICheckQueryRequest);
339346

340347
if (result.error) {
341348
const status = result.error.code && standardErrorCodes[result.error.code];
@@ -373,7 +380,7 @@ export const conformanceApiPlugin: FastifyPluginAsync<ConformanceApiPluginOption
373380
if (typeof params['enum'] === 'string') request.enum = params['enum'] as Answer;
374381
if (typeof params['datetime'] === 'string') request.datetime = params['datetime'];
375382

376-
const result = await api.checkPath(request as ICheckPathRequest);
383+
const result = await getService(req).checkPath(request as ICheckPathRequest);
377384

378385
if (result.error) {
379386
const status = result.error.code && standardErrorCodes[result.error.code];
@@ -411,7 +418,7 @@ export const conformanceApiPlugin: FastifyPluginAsync<ConformanceApiPluginOption
411418
if (typeof headers['enum'] === 'string') request.enum = headers['enum'] as Answer;
412419
if (typeof headers['datetime'] === 'string') request.datetime = headers['datetime'];
413420

414-
const result = await api.mirrorHeaders(request as IMirrorHeadersRequest);
421+
const result = await getService(req).mirrorHeaders(request as IMirrorHeadersRequest);
415422

416423
if (result.error) {
417424
const status = result.error.code && standardErrorCodes[result.error.code];
@@ -467,7 +474,7 @@ export const conformanceApiPlugin: FastifyPluginAsync<ConformanceApiPluginOption
467474
const body = req.body as Record<string, never>;
468475
request.normal = body.normal;
469476

470-
const result = await api.mixed(request as IMixedRequest);
477+
const result = await getService(req).mixed(request as IMixedRequest);
471478

472479
if (result.error) {
473480
const status = result.error.code && standardErrorCodes[result.error.code];
@@ -526,7 +533,7 @@ export const conformanceApiPlugin: FastifyPluginAsync<ConformanceApiPluginOption
526533
request.hasWidget = body.hasWidget;
527534
request.point = body.point;
528535

529-
const result = await api.required(request as IRequiredRequest);
536+
const result = await getService(req).required(request as IRequiredRequest);
530537

531538
if (result.error) {
532539
const status = result.error.code && standardErrorCodes[result.error.code];
@@ -559,7 +566,7 @@ export const conformanceApiPlugin: FastifyPluginAsync<ConformanceApiPluginOption
559566

560567
request.content = req.body as never;
561568

562-
const result = await api.mirrorBytes(request as IMirrorBytesRequest);
569+
const result = await getService(req).mirrorBytes(request as IMirrorBytesRequest);
563570

564571
if (result.error) {
565572
const status = result.error.code && standardErrorCodes[result.error.code];
@@ -596,7 +603,7 @@ export const conformanceApiPlugin: FastifyPluginAsync<ConformanceApiPluginOption
596603

597604
request.content = req.body as never;
598605

599-
const result = await api.mirrorText(request as IMirrorTextRequest);
606+
const result = await getService(req).mirrorText(request as IMirrorTextRequest);
600607

601608
if (result.error) {
602609
const status = result.error.code && standardErrorCodes[result.error.code];
@@ -630,7 +637,7 @@ export const conformanceApiPlugin: FastifyPluginAsync<ConformanceApiPluginOption
630637

631638
request.content = req.body as never;
632639

633-
const result = await api.bodyTypes(request as IBodyTypesRequest);
640+
const result = await getService(req).bodyTypes(request as IBodyTypesRequest);
634641

635642
if (result.error) {
636643
const status = result.error.code && standardErrorCodes[result.error.code];

conformance/src/fastify/jsConformanceApiPlugin.js

Lines changed: 18 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -2,9 +2,10 @@
22
/* eslint-disable */
33
'use strict';
44

5-
65
export const jsConformanceApiPlugin = async (fastify, opts) => {
7-
const { api, caseInsenstiveQueryStringKeys, includeErrorDetails } = opts;
6+
const { serviceOrFactory, caseInsenstiveQueryStringKeys, includeErrorDetails } = opts;
7+
8+
const getService = typeof serviceOrFactory === 'function' ? serviceOrFactory : () => serviceOrFactory;
89

910
for (const jsonSchema of jsonSchemas) {
1011
fastify.addSchema(jsonSchema);
@@ -59,7 +60,7 @@ export const jsConformanceApiPlugin = async (fastify, opts) => {
5960
handler: async function (req, res) {
6061
const request = {};
6162

62-
const result = await api.getApiInfo(request);
63+
const result = await getService(req).getApiInfo(request);
6364

6465
if (result.error) {
6566
const status = result.error.code && standardErrorCodes[result.error.code];
@@ -95,7 +96,7 @@ export const jsConformanceApiPlugin = async (fastify, opts) => {
9596
const query = req.query;
9697
if (typeof query['q'] === 'string') request.query = query['q'];
9798

98-
const result = await api.getWidgets(request);
99+
const result = await getService(req).getWidgets(request);
99100

100101
if (result.error) {
101102
const status = result.error.code && standardErrorCodes[result.error.code];
@@ -125,7 +126,7 @@ export const jsConformanceApiPlugin = async (fastify, opts) => {
125126

126127
request.widget = req.body;
127128

128-
const result = await api.createWidget(request);
129+
const result = await getService(req).createWidget(request);
129130

130131
if (result.error) {
131132
const status = result.error.code && standardErrorCodes[result.error.code];
@@ -165,7 +166,7 @@ export const jsConformanceApiPlugin = async (fastify, opts) => {
165166
const headers = req.headers;
166167
if (typeof headers['if-none-match'] === 'string') request.ifNotETag = headers['if-none-match'];
167168

168-
const result = await api.getWidget(request);
169+
const result = await getService(req).getWidget(request);
169170

170171
if (result.error) {
171172
const status = result.error.code && standardErrorCodes[result.error.code];
@@ -210,7 +211,7 @@ export const jsConformanceApiPlugin = async (fastify, opts) => {
210211
const headers = req.headers;
211212
if (typeof headers['if-match'] === 'string') request.ifETag = headers['if-match'];
212213

213-
const result = await api.deleteWidget(request);
214+
const result = await getService(req).deleteWidget(request);
214215

215216
if (result.error) {
216217
const status = result.error.code && standardErrorCodes[result.error.code];
@@ -250,7 +251,7 @@ export const jsConformanceApiPlugin = async (fastify, opts) => {
250251

251252
request.ids = req.body;
252253

253-
const result = await api.getWidgetBatch(request);
254+
const result = await getService(req).getWidgetBatch(request);
254255

255256
if (result.error) {
256257
const status = result.error.code && standardErrorCodes[result.error.code];
@@ -290,7 +291,7 @@ export const jsConformanceApiPlugin = async (fastify, opts) => {
290291
request.field = body.field;
291292
request.matrix = body.matrix;
292293

293-
const result = await api.mirrorFields(request);
294+
const result = await getService(req).mirrorFields(request);
294295

295296
if (result.error) {
296297
const status = result.error.code && standardErrorCodes[result.error.code];
@@ -328,7 +329,7 @@ export const jsConformanceApiPlugin = async (fastify, opts) => {
328329
if (typeof query['enum'] === 'string') request.enum = query['enum'];
329330
if (typeof query['datetime'] === 'string') request.datetime = query['datetime'];
330331

331-
const result = await api.checkQuery(request);
332+
const result = await getService(req).checkQuery(request);
332333

333334
if (result.error) {
334335
const status = result.error.code && standardErrorCodes[result.error.code];
@@ -366,7 +367,7 @@ export const jsConformanceApiPlugin = async (fastify, opts) => {
366367
if (typeof params['enum'] === 'string') request.enum = params['enum'];
367368
if (typeof params['datetime'] === 'string') request.datetime = params['datetime'];
368369

369-
const result = await api.checkPath(request);
370+
const result = await getService(req).checkPath(request);
370371

371372
if (result.error) {
372373
const status = result.error.code && standardErrorCodes[result.error.code];
@@ -404,7 +405,7 @@ export const jsConformanceApiPlugin = async (fastify, opts) => {
404405
if (typeof headers['enum'] === 'string') request.enum = headers['enum'];
405406
if (typeof headers['datetime'] === 'string') request.datetime = headers['datetime'];
406407

407-
const result = await api.mirrorHeaders(request);
408+
const result = await getService(req).mirrorHeaders(request);
408409

409410
if (result.error) {
410411
const status = result.error.code && standardErrorCodes[result.error.code];
@@ -460,7 +461,7 @@ export const jsConformanceApiPlugin = async (fastify, opts) => {
460461
const body = req.body;
461462
request.normal = body.normal;
462463

463-
const result = await api.mixed(request);
464+
const result = await getService(req).mixed(request);
464465

465466
if (result.error) {
466467
const status = result.error.code && standardErrorCodes[result.error.code];
@@ -519,7 +520,7 @@ export const jsConformanceApiPlugin = async (fastify, opts) => {
519520
request.hasWidget = body.hasWidget;
520521
request.point = body.point;
521522

522-
const result = await api.required(request);
523+
const result = await getService(req).required(request);
523524

524525
if (result.error) {
525526
const status = result.error.code && standardErrorCodes[result.error.code];
@@ -552,7 +553,7 @@ export const jsConformanceApiPlugin = async (fastify, opts) => {
552553

553554
request.content = req.body;
554555

555-
const result = await api.mirrorBytes(request);
556+
const result = await getService(req).mirrorBytes(request);
556557

557558
if (result.error) {
558559
const status = result.error.code && standardErrorCodes[result.error.code];
@@ -589,7 +590,7 @@ export const jsConformanceApiPlugin = async (fastify, opts) => {
589590

590591
request.content = req.body;
591592

592-
const result = await api.mirrorText(request);
593+
const result = await getService(req).mirrorText(request);
593594

594595
if (result.error) {
595596
const status = result.error.code && standardErrorCodes[result.error.code];
@@ -623,7 +624,7 @@ export const jsConformanceApiPlugin = async (fastify, opts) => {
623624

624625
request.content = req.body;
625626

626-
const result = await api.bodyTypes(request);
627+
const result = await getService(req).bodyTypes(request);
627628

628629
if (result.error) {
629630
const status = result.error.code && standardErrorCodes[result.error.code];

src/Facility.CodeGen.JavaScript/JavaScriptGenerator.cs

Lines changed: 22 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -567,43 +567,39 @@ private CodeGenOutput GenerateFastifyPluginOutput(ServiceInfo service)
567567
{
568568
WriteFileHeader(code);
569569

570-
if (!TypeScript)
571-
code.WriteLine("'use strict';");
572-
573-
code.WriteLine();
574-
575-
var fastifyImports = new List<string>();
576-
if (TypeScript)
577-
{
578-
fastifyImports.Add("FastifyPluginAsync");
579-
fastifyImports.Add("RegisterOptions");
580-
}
581-
582-
WriteImports(code, fastifyImports, "fastify");
583-
584-
var facilityImports = new List<string>();
585570
if (TypeScript)
586571
{
587-
facilityImports.Add("IServiceResult");
588-
facilityImports.Add("IServiceError");
589-
}
590-
WriteImports(code, facilityImports, "facility-core");
572+
code.WriteLine();
573+
code.WriteLine("import type * as fastifyTypes from 'fastify';");
574+
code.WriteLine("import type { IServiceResult, IServiceError } from 'facility-core';");
591575

592-
if (TypeScript)
593-
{
594576
code.WriteLine();
595-
using (code.Block($"export type {capModuleName}PluginOptions = RegisterOptions & {{", "}"))
577+
using (code.Block($"export type {capModuleName}PluginOptions = fastifyTypes.RegisterOptions & {{", "}"))
596578
{
597-
code.WriteLine($"api: I{capModuleName};");
579+
WriteJsDoc(code, $"The `I{capModuleName}` service implementation. Can be a service instance or a factory function which is called on each request.");
580+
code.WriteLine($"serviceOrFactory: I{capModuleName} | ((req: fastifyTypes.FastifyRequest) => I{capModuleName});");
581+
582+
code.WriteLine();
583+
WriteJsDoc(code, "Whether to make query string keys case insensitive. Defalts to false.");
598584
code.WriteLine("caseInsenstiveQueryStringKeys?: boolean;");
585+
586+
code.WriteLine();
587+
WriteJsDoc(code, "Whether to include error details in the response. Defaults to false.");
599588
code.WriteLine("includeErrorDetails?: boolean;");
600589
}
601590
}
591+
else
592+
{
593+
code.WriteLine("'use strict';");
594+
}
602595

603596
code.WriteLine();
604-
using (code.Block($"export const {camelCaseModuleName}Plugin" + IfTypeScript($": FastifyPluginAsync<{capModuleName}PluginOptions>") + " = async (fastify, opts) => {", "}"))
597+
using (code.Block($"export const {camelCaseModuleName}Plugin" + IfTypeScript($": fastifyTypes.FastifyPluginAsync<{capModuleName}PluginOptions>") + " = async (fastify, opts) => {", "}"))
605598
{
606-
code.WriteLine("const { api, caseInsenstiveQueryStringKeys, includeErrorDetails } = opts;");
599+
code.WriteLine("const { serviceOrFactory, caseInsenstiveQueryStringKeys, includeErrorDetails } = opts;");
600+
601+
code.WriteLine();
602+
code.WriteLine("const getService = typeof serviceOrFactory === 'function' ? serviceOrFactory : () => serviceOrFactory;");
607603

608604
code.WriteLine();
609605
using (code.Block("for (const jsonSchema of jsonSchemas) {", "}"))
@@ -750,7 +746,7 @@ private CodeGenOutput GenerateFastifyPluginOutput(ServiceInfo service)
750746
}
751747

752748
code.WriteLine();
753-
code.WriteLine($"const result = await api.{methodName}(request{IfTypeScript($" as I{capMethodName}Request")});");
749+
code.WriteLine($"const result = await getService(req).{methodName}(request{IfTypeScript($" as I{capMethodName}Request")});");
754750

755751
code.WriteLine();
756752
using (code.Block("if (result.error) {", "}"))

0 commit comments

Comments
 (0)