forked from hyperledger-cacti/cacti
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
refactor(core,core-api): use fastify, drop express
Primary Changes --------------- 1. Migrated files of (cactus-core and core-api) using express to fastify Fixes: hyperledger-cacti#3598 Signed-off-by: aldousalvarez <aldousss.alvarez@gmail.com>
- Loading branch information
1 parent
ca70682
commit bc8800f
Showing
14 changed files
with
830 additions
and
10 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
6 changes: 6 additions & 0 deletions
6
packages/cactus-core-api/src/main/typescript/plugin/web-service/i-fastify-request-handler.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,6 @@ | ||
import type { FastifyRequest, FastifyReply } from "fastify"; | ||
|
||
export type IFastifyRequestHandler = ( | ||
req: FastifyRequest, | ||
reply: FastifyReply, | ||
) => Promise<void>; |
31 changes: 31 additions & 0 deletions
31
...es/cactus-core-api/src/main/typescript/plugin/web-service/i-plugin-web-service-fastify.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,31 @@ | ||
import type { FastifyInstance } from "fastify"; // Import Fastify type | ||
import { IWebServiceEndpointFastify } from "./i-web-service-endpoint-fastify"; | ||
import { ICactusPlugin } from "../i-cactus-plugin"; | ||
import type { Server as SocketIoServer } from "socket.io"; | ||
|
||
export interface IPluginWebServiceFastify extends ICactusPlugin { | ||
getOrCreateWebServices(): Promise<IWebServiceEndpointFastify[]>; | ||
|
||
registerWebServices( | ||
fastifyApp: FastifyInstance, // Updated to FastifyInstance | ||
wsApi: SocketIoServer, | ||
): Promise<IWebServiceEndpointFastify[]>; | ||
|
||
shutdown(): Promise<void>; | ||
getOpenApiSpec(): unknown; | ||
} | ||
|
||
export function isIPluginWebServiceFastify( | ||
x: unknown, | ||
): x is IPluginWebServiceFastify { | ||
return ( | ||
!!x && | ||
typeof (x as IPluginWebServiceFastify).registerWebServices === "function" && | ||
typeof (x as IPluginWebServiceFastify).getOrCreateWebServices === | ||
"function" && | ||
typeof (x as IPluginWebServiceFastify).getPackageName === "function" && | ||
typeof (x as IPluginWebServiceFastify).getInstanceId === "function" && | ||
typeof (x as IPluginWebServiceFastify).shutdown === "function" && | ||
typeof (x as IPluginWebServiceFastify).getOpenApiSpec === "function" | ||
); | ||
} |
69 changes: 69 additions & 0 deletions
69
.../cactus-core-api/src/main/typescript/plugin/web-service/i-web-service-endpoint-fastify.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,69 @@ | ||
import type { FastifyInstance, FastifyReply, FastifyRequest } from "fastify"; | ||
import { IAsyncProvider } from "@hyperledger/cactus-common"; | ||
import { IEndpointAuthzOptions } from "./i-endpoint-authz-options"; | ||
|
||
/** | ||
* Interface for implementing API endpoints that can be dynamically registered | ||
* at runtime in a Fastify web application. | ||
* | ||
* This interface outlines the necessary methods for a Cactus API endpoint, | ||
* which is typically part of a plugin. It facilitates dynamic routing and | ||
* ensures consistent endpoint registration. | ||
*/ | ||
export interface IWebServiceEndpointFastify { | ||
/** | ||
* Registers this endpoint with a Fastify application. | ||
* | ||
* @param fastifyApp - The Fastify application instance to register the endpoint with. | ||
* @returns A promise that resolves to the endpoint instance after registration. | ||
*/ | ||
registerFastify( | ||
fastifyApp: FastifyInstance, | ||
): Promise<IWebServiceEndpointFastify>; | ||
|
||
/** | ||
* Gets the HTTP verb for this endpoint in lowercase. | ||
* | ||
* @example "get", "post", "put", "delete" | ||
* @returns The lowercase HTTP verb. | ||
*/ | ||
getVerbLowerCase(): string; | ||
|
||
/** | ||
* Gets the HTTP path for this endpoint. | ||
* | ||
* @returns The path string where the endpoint will be accessible. | ||
*/ | ||
getPath(): string; | ||
|
||
/** | ||
* Provides the Fastify-compatible request handler for this endpoint. | ||
* | ||
* The handler can be directly registered using Fastify's route configuration methods. | ||
* | ||
* @returns The request handler function. | ||
*/ | ||
getFastifyHandler(): ( | ||
request: FastifyRequest, | ||
reply: FastifyReply, | ||
) => Promise<void>; | ||
|
||
/** | ||
* Provides an asynchronous provider for authorization options. | ||
* | ||
* This allows dynamic determination of authorization configurations, | ||
* such as role-based access control (RBAC) or token requirements. | ||
* | ||
* @returns An async provider for authorization options. | ||
*/ | ||
getAuthorizationOptionsProvider(): IAsyncProvider<IEndpointAuthzOptions>; | ||
|
||
/** | ||
* Optionally provides a reference to the Fastify reply object. | ||
* | ||
* This can be used for scenarios requiring custom reply handling | ||
* after the handler function completes. | ||
* | ||
* @returns The Fastify reply object, if applicable. | ||
*/ | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
49 changes: 49 additions & 0 deletions
49
packages/cactus-core/src/main/typescript/web-services/configure-fastify-app-base.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,49 @@ | ||
import { FastifyInstance } from "fastify"; | ||
import { | ||
Checks, | ||
LogLevelDesc, | ||
LoggerProvider, | ||
} from "@hyperledger/cactus-common"; | ||
import { stringifyBigIntReplacer } from "./stringify-big-int-replacer"; | ||
|
||
export const CACTI_CORE_CONFIGURE_FASTIFY_APP_BASE_MARKER = | ||
"CACTI_CORE_CONFIGURE_FASTIFY_APP_BASE_MARKER"; | ||
|
||
export interface IConfigureFastifyAppContext { | ||
readonly logLevel?: LogLevelDesc; | ||
readonly app: FastifyInstance; | ||
} | ||
|
||
export async function configureFastifyAppBase( | ||
ctx: IConfigureFastifyAppContext, | ||
): Promise<void> { | ||
const fn = "configureFastifyAppBase()"; | ||
Checks.truthy(ctx, `${fn} arg1 ctx`); | ||
Checks.truthy(ctx.app, `${fn} arg1 ctx.app`); | ||
|
||
const logLevel: LogLevelDesc = ctx.logLevel || "WARN"; | ||
const log = LoggerProvider.getOrCreate({ level: logLevel, label: fn }); | ||
|
||
log.debug("ENTRY"); | ||
|
||
if (ctx.app.hasDecorator(CACTI_CORE_CONFIGURE_FASTIFY_APP_BASE_MARKER)) { | ||
throw new Error("Fastify instance has already been configured."); | ||
} | ||
|
||
log.debug("Fastify JSON parsing enabled by default"); | ||
|
||
ctx.app.addHook("onSend", async (request, reply, payload) => { | ||
if (typeof payload === "string") { | ||
try { | ||
return JSON.stringify(JSON.parse(payload), stringifyBigIntReplacer); | ||
} catch (err) { | ||
return payload; | ||
} | ||
} | ||
return payload; | ||
}); | ||
|
||
ctx.app.decorate(CACTI_CORE_CONFIGURE_FASTIFY_APP_BASE_MARKER, true); | ||
|
||
log.debug("EXIT"); | ||
} |
85 changes: 85 additions & 0 deletions
85
...actus-core/src/main/typescript/web-services/get-open-api-spec-v1-endpoint-base-fastify.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,85 @@ | ||
import { | ||
FastifyReply, | ||
FastifyRequest, | ||
FastifyInstance, | ||
RouteOptions, | ||
} from "fastify"; | ||
import { | ||
Logger, | ||
LoggerProvider, | ||
IAsyncProvider, | ||
} from "@hyperledger/cactus-common"; | ||
|
||
import { | ||
IWebServiceEndpointFastify, | ||
IEndpointAuthzOptions, | ||
} from "@hyperledger/cactus-core-api"; | ||
|
||
export class GetOpenApiSpecV1EndpointBase | ||
implements IWebServiceEndpointFastify | ||
{ | ||
public static readonly CLASS_NAME = "GetOpenApiSpecV1EndpointBase<S, P>"; | ||
|
||
protected readonly log: Logger; | ||
|
||
public get className(): string { | ||
return GetOpenApiSpecV1EndpointBase.CLASS_NAME; | ||
} | ||
|
||
constructor(public readonly opts: { path: string; verbLowerCase: string }) { | ||
const level = "INFO"; | ||
const label = this.className; | ||
this.log = LoggerProvider.getOrCreate({ level, label }); | ||
} | ||
|
||
public getPath(): string { | ||
return this.opts.path; | ||
} | ||
|
||
public getVerbLowerCase(): string { | ||
return this.opts.verbLowerCase; | ||
} | ||
|
||
public async registerFastify( | ||
fastify: FastifyInstance, | ||
): Promise<IWebServiceEndpointFastify> { | ||
fastify.route({ | ||
method: this.getVerbLowerCase().toUpperCase() as RouteOptions["method"], | ||
url: this.getPath(), | ||
handler: this.getFastifyHandler(), | ||
}); | ||
return this; | ||
} | ||
|
||
getAuthorizationOptionsProvider(): IAsyncProvider<IEndpointAuthzOptions> { | ||
return { | ||
get: async () => ({ | ||
isProtected: true, | ||
requiredRoles: [], | ||
}), | ||
}; | ||
} | ||
|
||
public getFastifyHandler(): ( | ||
request: FastifyRequest, | ||
reply: FastifyReply, | ||
) => Promise<void> { | ||
return async ( | ||
request: FastifyRequest, | ||
reply: FastifyReply, | ||
): Promise<void> => { | ||
await this.handleRequest(request, reply); | ||
}; | ||
} | ||
|
||
private async handleRequest( | ||
request: FastifyRequest, | ||
reply: FastifyReply, | ||
): Promise<void> { | ||
try { | ||
reply.status(200).send({ success: true }); | ||
} catch (error) { | ||
reply.status(500).send({ error: "Internal Server Error" }); | ||
} | ||
} | ||
} |
80 changes: 80 additions & 0 deletions
80
...es/cactus-core/src/main/typescript/web-services/handle-rest-endpoint-exception-fastify.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,80 @@ | ||
import type { FastifyReply } from "fastify"; | ||
import createHttpError from "http-errors"; | ||
|
||
import { | ||
identifierByCodes, | ||
INTERNAL_SERVER_ERROR, | ||
} from "http-errors-enhanced-cjs"; | ||
|
||
import { | ||
Logger, | ||
createRuntimeErrorWithCause, | ||
safeStringifyException, | ||
} from "@hyperledger/cactus-common"; | ||
|
||
/** | ||
* An interface describing the object containing the contextual information needed by the | ||
* `#handleRestEndpointException()` method to perform its duties. | ||
* | ||
* @param ctx - An object containing options for handling the REST endpoint exception. | ||
* @param ctx.errorMsg - The error message to log (if there will be error logging e.g. HTTP 500) | ||
* @param ctx.log - The logger instance used for logging errors and/or debug messages. | ||
* @param ctx.error - The error object representing the exception that is being handled. | ||
* @param ctx.reply - The Fastify reply object to send the HTTP response. | ||
*/ | ||
export interface IHandleRestEndpointExceptionOptions { | ||
readonly errorMsg: string; | ||
readonly log: Logger; | ||
readonly error: unknown; | ||
readonly reply: FastifyReply; | ||
} | ||
|
||
/** | ||
* Handles exceptions thrown during REST endpoint processing and sends an appropriate HTTP response. | ||
* | ||
* If the exception is an instance of `HttpError` from the `http-errors` library, | ||
* it logs the error at the debug level and sends a JSON response with the error details | ||
* and the corresponding HTTP status code. | ||
* | ||
* If the exception is not an instance of `HttpError`, it logs the error at the error level, | ||
* creates a runtime error with the original error as the cause, and sends a JSON response | ||
* with a generic "Internal Server Error" message and a 500 HTTP status code. | ||
* | ||
* @param ctx - An object containing options for handling the REST endpoint exception. | ||
*/ | ||
export async function handleRestEndpointException( | ||
ctx: Readonly<IHandleRestEndpointExceptionOptions>, | ||
): Promise<void> { | ||
const errorAsSanitizedJson = safeStringifyException(ctx.error); | ||
|
||
if (createHttpError.isHttpError(ctx.error)) { | ||
ctx.reply.status(ctx.error.statusCode); | ||
|
||
if (ctx.error.statusCode >= INTERNAL_SERVER_ERROR) { | ||
ctx.log.debug(ctx.errorMsg, errorAsSanitizedJson); | ||
} else { | ||
ctx.log.error(ctx.errorMsg, errorAsSanitizedJson); | ||
} | ||
|
||
if (ctx.error.expose) { | ||
ctx.reply.send({ | ||
message: identifierByCodes[ctx.error.statusCode], | ||
error: errorAsSanitizedJson, | ||
}); | ||
} else { | ||
ctx.reply.send({ | ||
message: identifierByCodes[ctx.error.statusCode], | ||
}); | ||
} | ||
} else { | ||
ctx.log.error(ctx.errorMsg, errorAsSanitizedJson); | ||
|
||
const rex = createRuntimeErrorWithCause(ctx.errorMsg, ctx.error); | ||
const sanitizedJsonRex = safeStringifyException(rex); | ||
|
||
ctx.reply.status(INTERNAL_SERVER_ERROR).send({ | ||
message: identifierByCodes[INTERNAL_SERVER_ERROR], | ||
error: sanitizedJsonRex, | ||
}); | ||
} | ||
} |
Oops, something went wrong.