Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Allow for request scoped service instances in fastify plugin. #71

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 6 commits into from
Feb 12, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion Directory.Build.props
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
<Project>

<PropertyGroup>
<VersionPrefix>3.2.0</VersionPrefix>
<VersionPrefix>3.3.0</VersionPrefix>
<PackageValidationBaselineVersion>3.1.0</PackageValidationBaselineVersion>
<LangVersion>12.0</LangVersion>
<Nullable>enable</Nullable>
Expand Down
5 changes: 5 additions & 0 deletions ReleaseNotes.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,11 @@

These are the NuGet package releases. See also [npm Release Notes](ReleaseNotesNpm.md).

## 3.3.0

* Allow for request scoped services in fastify plugin.
* Make fastify plugin support "experimental" for now.

## 3.2.0

* Support `float` field type.
Expand Down
580 changes: 329 additions & 251 deletions conformance/package-lock.json

Large diffs are not rendered by default.

8 changes: 4 additions & 4 deletions conformance/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -16,16 +16,16 @@
"devDependencies": {
"@types/chai": "^4.2.18",
"@types/mocha": "^8.2.2",
"@types/node": "^12.20.15",
"@types/node": "^22.1.0",
"@types/node-fetch": "^2.5.10",
"chai": "^4.3.4",
"fastify-cli": "^5.9.0",
"fastify-cli": "^7.3.0",
"mocha": "^10.0.0",
"node-fetch": "^2.6.7",
"typescript": "^5.2.2"
"typescript": "^5.7.2"
},
"dependencies": {
"facility-core": "file:../ts",
"fastify": "^4.25.1"
"fastify": "^5.2.1"
}
}
3 changes: 2 additions & 1 deletion conformance/src/fastify/app.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,8 @@ import { jsConformanceApiPlugin } from "./jsConformanceApiPlugin.js";

const app: FastifyPluginAsync<FastifyServerOptions> = async (fastify): Promise<void> => {
const conformanceApiPluginOptions: ConformanceApiPluginOptions = {
api: new ConformanceApiService(conformanceTestsJson.tests),
serviceOrFactory: () =>
new ConformanceApiService(conformanceTestsJson.tests),
caseInsenstiveQueryStringKeys: true,
includeErrorDetails: true,
};
Expand Down
136 changes: 72 additions & 64 deletions conformance/src/fastify/conformanceApiPlugin.ts
Original file line number Diff line number Diff line change
@@ -1,45 +1,25 @@
// DO NOT EDIT: generated by fsdgenjs
/* eslint-disable */

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

const standardErrorCodes: { [code: string]: number } = {
'NotModified': 304,
'InvalidRequest': 400,
'NotAuthenticated': 401,
'NotAuthorized': 403,
'NotFound': 404,
'Conflict': 409,
'RequestTooLarge': 413,
'TooManyRequests': 429,
'InternalError': 500,
'ServiceUnavailable': 503,
'NotAdmin': 403,
'TooHappy': 500,
};
export type ConformanceApiPluginOptions = fastifyTypes.RegisterOptions & {
/** The `IConformanceApi` service implementation. Can be a service instance or a factory function which is called on each request. */
serviceOrFactory: IConformanceApi | ((req: fastifyTypes.FastifyRequest) => IConformanceApi);

function parseBoolean(value: string | undefined) {
if (typeof value === 'string') {
const lowerValue = value.toLowerCase();
if (lowerValue === 'true') {
return true;
}
if (lowerValue === 'false') {
return false;
}
}
return undefined;
}

export type ConformanceApiPluginOptions = RegisterOptions & {
api: IConformanceApi;
/** Whether to make query string keys case insensitive. Defalts to false. */
caseInsenstiveQueryStringKeys?: boolean;

/** Whether to include error details in the response. Defaults to false. */
includeErrorDetails?: boolean;
}

export const conformanceApiPlugin: FastifyPluginAsync<ConformanceApiPluginOptions> = async (fastify, opts) => {
const { api, caseInsenstiveQueryStringKeys, includeErrorDetails } = opts;
/** EXPERIMENTAL: The generated code for this plugin is subject to change/removal without a major version bump. */
export const conformanceApiPlugin: fastifyTypes.FastifyPluginAsync<ConformanceApiPluginOptions> = async (fastify, opts) => {
const { serviceOrFactory, caseInsenstiveQueryStringKeys, includeErrorDetails } = opts;

const getService = typeof serviceOrFactory === 'function' ? serviceOrFactory : () => serviceOrFactory;

for (const jsonSchema of jsonSchemas) {
fastify.addSchema(jsonSchema);
Expand Down Expand Up @@ -70,7 +50,7 @@ export const conformanceApiPlugin: FastifyPluginAsync<ConformanceApiPluginOption
for (const key of Object.keys(query)) {
const lowerKey = key.toLowerCase();
if (lowerKey !== key) {
query[lowerKey] = query[key];
query[lowerKey] = query[key] as string;
delete query[key];
}
}
Expand All @@ -92,9 +72,9 @@ export const conformanceApiPlugin: FastifyPluginAsync<ConformanceApiPluginOption
},
},
handler: async function (req, res) {
const request: IGetApiInfoRequest = {};
const request: Partial<IGetApiInfoRequest> = {};

const result = await api.getApiInfo(request);
const result = await getService(req).getApiInfo(request as IGetApiInfoRequest);

if (result.error) {
const status = result.error.code && standardErrorCodes[result.error.code];
Expand Down Expand Up @@ -125,12 +105,12 @@ export const conformanceApiPlugin: FastifyPluginAsync<ConformanceApiPluginOption
},
},
handler: async function (req, res) {
const request: IGetWidgetsRequest = {};
const request: Partial<IGetWidgetsRequest> = {};

const query = req.query as Record<string, string>;
if (typeof query['q'] === 'string') request.query = query['q'];

const result = await api.getWidgets(request);
const result = await getService(req).getWidgets(request as IGetWidgetsRequest);

if (result.error) {
const status = result.error.code && standardErrorCodes[result.error.code];
Expand All @@ -156,11 +136,11 @@ export const conformanceApiPlugin: FastifyPluginAsync<ConformanceApiPluginOption
},
},
handler: async function (req, res) {
const request: ICreateWidgetRequest = {};
const request: Partial<ICreateWidgetRequest> = {};

request.widget = req.body as never;

const result = await api.createWidget(request);
const result = await getService(req).createWidget(request as ICreateWidgetRequest);

if (result.error) {
const status = result.error.code && standardErrorCodes[result.error.code];
Expand Down Expand Up @@ -192,15 +172,15 @@ export const conformanceApiPlugin: FastifyPluginAsync<ConformanceApiPluginOption
},
},
handler: async function (req, res) {
const request: IGetWidgetRequest = {};
const request: Partial<IGetWidgetRequest> = {};

const params = req.params as Record<string, string>;
if (typeof params['id'] === 'string') request.id = parseInt(params['id'], 10);

const headers = req.headers as Record<string, string>;
if (typeof headers['if-none-match'] === 'string') request.ifNotETag = headers['if-none-match'];

const result = await api.getWidget(request);
const result = await getService(req).getWidget(request as IGetWidgetRequest);

if (result.error) {
const status = result.error.code && standardErrorCodes[result.error.code];
Expand Down Expand Up @@ -237,15 +217,15 @@ export const conformanceApiPlugin: FastifyPluginAsync<ConformanceApiPluginOption
},
},
handler: async function (req, res) {
const request: IDeleteWidgetRequest = {};
const request: Partial<IDeleteWidgetRequest> = {};

const params = req.params as Record<string, string>;
if (typeof params['id'] === 'string') request.id = parseInt(params['id'], 10);

const headers = req.headers as Record<string, string>;
if (typeof headers['if-match'] === 'string') request.ifETag = headers['if-match'];

const result = await api.deleteWidget(request);
const result = await getService(req).deleteWidget(request as IDeleteWidgetRequest);

if (result.error) {
const status = result.error.code && standardErrorCodes[result.error.code];
Expand Down Expand Up @@ -281,11 +261,11 @@ export const conformanceApiPlugin: FastifyPluginAsync<ConformanceApiPluginOption
},
},
handler: async function (req, res) {
const request: IGetWidgetBatchRequest = {};
const request: Partial<IGetWidgetBatchRequest> = {};

request.ids = req.body as never;

const result = await api.getWidgetBatch(request);
const result = await getService(req).getWidgetBatch(request as IGetWidgetBatchRequest);

if (result.error) {
const status = result.error.code && standardErrorCodes[result.error.code];
Expand Down Expand Up @@ -319,13 +299,13 @@ export const conformanceApiPlugin: FastifyPluginAsync<ConformanceApiPluginOption
},
},
handler: async function (req, res) {
const request: IMirrorFieldsRequest = {};
const request: Partial<IMirrorFieldsRequest> = {};

const body = req.body as Record<string, never>;
request.field = body.field;
request.matrix = body.matrix;

const result = await api.mirrorFields(request);
const result = await getService(req).mirrorFields(request as IMirrorFieldsRequest);

if (result.error) {
const status = result.error.code && standardErrorCodes[result.error.code];
Expand All @@ -351,7 +331,7 @@ export const conformanceApiPlugin: FastifyPluginAsync<ConformanceApiPluginOption
},
},
handler: async function (req, res) {
const request: ICheckQueryRequest = {};
const request: Partial<ICheckQueryRequest> = {};

const query = req.query as Record<string, string>;
if (typeof query['string'] === 'string') request.string = query['string'];
Expand All @@ -364,7 +344,7 @@ export const conformanceApiPlugin: FastifyPluginAsync<ConformanceApiPluginOption
if (typeof query['enum'] === 'string') request.enum = query['enum'] as Answer;
if (typeof query['datetime'] === 'string') request.datetime = query['datetime'];

const result = await api.checkQuery(request);
const result = await getService(req).checkQuery(request as ICheckQueryRequest);

if (result.error) {
const status = result.error.code && standardErrorCodes[result.error.code];
Expand All @@ -390,7 +370,7 @@ export const conformanceApiPlugin: FastifyPluginAsync<ConformanceApiPluginOption
},
},
handler: async function (req, res) {
const request: ICheckPathRequest = {};
const request: Partial<ICheckPathRequest> = {};

const params = req.params as Record<string, string>;
if (typeof params['string'] === 'string') request.string = params['string'];
Expand All @@ -403,7 +383,7 @@ export const conformanceApiPlugin: FastifyPluginAsync<ConformanceApiPluginOption
if (typeof params['enum'] === 'string') request.enum = params['enum'] as Answer;
if (typeof params['datetime'] === 'string') request.datetime = params['datetime'];

const result = await api.checkPath(request);
const result = await getService(req).checkPath(request as ICheckPathRequest);

if (result.error) {
const status = result.error.code && standardErrorCodes[result.error.code];
Expand All @@ -429,7 +409,7 @@ export const conformanceApiPlugin: FastifyPluginAsync<ConformanceApiPluginOption
},
},
handler: async function (req, res) {
const request: IMirrorHeadersRequest = {};
const request: Partial<IMirrorHeadersRequest> = {};

const headers = req.headers as Record<string, string>;
if (typeof headers['string'] === 'string') request.string = headers['string'];
Expand All @@ -442,7 +422,7 @@ export const conformanceApiPlugin: FastifyPluginAsync<ConformanceApiPluginOption
if (typeof headers['enum'] === 'string') request.enum = headers['enum'] as Answer;
if (typeof headers['datetime'] === 'string') request.datetime = headers['datetime'];

const result = await api.mirrorHeaders(request);
const result = await getService(req).mirrorHeaders(request as IMirrorHeadersRequest);

if (result.error) {
const status = result.error.code && standardErrorCodes[result.error.code];
Expand Down Expand Up @@ -485,7 +465,7 @@ export const conformanceApiPlugin: FastifyPluginAsync<ConformanceApiPluginOption
},
},
handler: async function (req, res) {
const request: IMixedRequest = {};
const request: Partial<IMixedRequest> = {};

const params = req.params as Record<string, string>;
if (typeof params['path'] === 'string') request.path = params['path'];
Expand All @@ -499,7 +479,7 @@ export const conformanceApiPlugin: FastifyPluginAsync<ConformanceApiPluginOption
const body = req.body as Record<string, never>;
request.normal = body.normal;

const result = await api.mixed(request);
const result = await getService(req).mixed(request as IMixedRequest);

if (result.error) {
const status = result.error.code && standardErrorCodes[result.error.code];
Expand Down Expand Up @@ -542,7 +522,7 @@ export const conformanceApiPlugin: FastifyPluginAsync<ConformanceApiPluginOption
},
},
handler: async function (req, res) {
const request: IRequiredRequest = {};
const request: Partial<IRequiredRequest> = {};

const query = req.query as Record<string, string>;
if (typeof query['query'] === 'string') request.query = query['query'];
Expand All @@ -558,7 +538,7 @@ export const conformanceApiPlugin: FastifyPluginAsync<ConformanceApiPluginOption
request.hasWidget = body.hasWidget;
request.point = body.point;

const result = await api.required(request);
const result = await getService(req).required(request as IRequiredRequest);

if (result.error) {
const status = result.error.code && standardErrorCodes[result.error.code];
Expand All @@ -584,14 +564,14 @@ export const conformanceApiPlugin: FastifyPluginAsync<ConformanceApiPluginOption
},
},
handler: async function (req, res) {
const request: IMirrorBytesRequest = {};
const request: Partial<IMirrorBytesRequest> = {};

const headers = req.headers as Record<string, string>;
if (typeof headers['content-type'] === 'string') request.type = headers['content-type'];

request.content = req.body as never;

const result = await api.mirrorBytes(request);
const result = await getService(req).mirrorBytes(request as IMirrorBytesRequest);

if (result.error) {
const status = result.error.code && standardErrorCodes[result.error.code];
Expand Down Expand Up @@ -621,14 +601,14 @@ export const conformanceApiPlugin: FastifyPluginAsync<ConformanceApiPluginOption
},
},
handler: async function (req, res) {
const request: IMirrorTextRequest = {};
const request: Partial<IMirrorTextRequest> = {};

const headers = req.headers as Record<string, string>;
if (typeof headers['content-type'] === 'string') request.type = headers['content-type'];

request.content = req.body as never;

const result = await api.mirrorText(request);
const result = await getService(req).mirrorText(request as IMirrorTextRequest);

if (result.error) {
const status = result.error.code && standardErrorCodes[result.error.code];
Expand Down Expand Up @@ -658,11 +638,11 @@ export const conformanceApiPlugin: FastifyPluginAsync<ConformanceApiPluginOption
},
},
handler: async function (req, res) {
const request: IBodyTypesRequest = {};
const request: Partial<IBodyTypesRequest> = {};

request.content = req.body as never;

const result = await api.bodyTypes(request);
const result = await getService(req).bodyTypes(request as IBodyTypesRequest);

if (result.error) {
const status = result.error.code && standardErrorCodes[result.error.code];
Expand Down Expand Up @@ -829,6 +809,34 @@ const jsonSchemas = [
} as const,
] as const;

const standardErrorCodes: { [code: string]: number } = {
'NotModified': 304,
'InvalidRequest': 400,
'NotAuthenticated': 401,
'NotAuthorized': 403,
'NotFound': 404,
'Conflict': 409,
'RequestTooLarge': 413,
'TooManyRequests': 429,
'InternalError': 500,
'ServiceUnavailable': 503,
'NotAdmin': 403,
'TooHappy': 500,
};

function parseBoolean(value: string | undefined) {
if (typeof value === 'string') {
const lowerValue = value.toLowerCase();
if (lowerValue === 'true') {
return true;
}
if (lowerValue === 'false') {
return false;
}
}
return undefined;
}

/** API for a Facility test server. */
export interface IConformanceApi {
/** Gets API information. */
Expand Down
Loading