UNPKG

rosetta-sdk-typescript

Version:

Typescript SDK to create and interact with Rosetta API implementations.

114 lines (104 loc) 4.14 kB
import express, { Express } from 'express'; import * as OpenApiValidator from 'express-openapi-validator'; import { readFileSync } from 'fs'; import { Server } from 'net'; import { join } from 'path'; import * as swagger from 'swagger-ui-express'; import { fileExistsSync } from 'tsconfig-paths/lib/filesystem'; import { ApiControllerFactoryImpl, ApiServiceFactory, OpenApiRoute, OpenApiRouterResolver, ServerUtils } from './'; /** * Params that allow you running the Rosetta server. The only require param is apiServiceFactory. */ export interface RosettaServerParams { apiServiceFactory: ApiServiceFactory; openApiValidateRequests: boolean; openApiValidateResponses: boolean; openApiServeDocs: boolean; openApiServeSpec: boolean; welcomeMessage: string; ignorePaths: string[]; appSetupCallback: (app: Express) => void; } /** * Default roseeta server params. */ const defaultServerParams: Partial<RosettaServerParams> = { openApiValidateRequests: true, openApiValidateResponses: true, openApiServeDocs: true, openApiServeSpec: true, welcomeMessage: 'Welcome to the Rosetta Server', ignorePaths: ['/robots.txt'], }; /** * Main class of this SDK. A Rosetta implementation should initialize this object and start the server. * * */ const apiSpec = join(__dirname, '../../api.json'); if (!fileExistsSync(apiSpec)) { throw new Error(`${apiSpec} does not exist!`); } const apiContent = readFileSync(apiSpec, 'utf-8'); const api = JSON.parse(apiContent); export class RosettaServer { // The Rosetta api this SDK supports. public static ROSETTA_API_VERSION = api.info.version; private openApiRouterResolver: OpenApiRouterResolver; private app: Express; private server: Server | undefined; constructor(private readonly params: { apiServiceFactory: ApiServiceFactory } & Partial<RosettaServerParams>) { const resolvedParams = { ...defaultServerParams, ...params }; if (!resolvedParams.apiServiceFactory) { throw new Error('apiServiceFactory must be provided!!'); } this.openApiRouterResolver = new OpenApiRouterResolver(new ApiControllerFactoryImpl(resolvedParams.apiServiceFactory)); const app = express(); resolvedParams.appSetupCallback?.(app); app.use(express.json()); app.use(express.urlencoded({ extended: false })); app.get('/', (req, res) => res.send(resolvedParams.welcomeMessage)); if (resolvedParams.openApiServeSpec) app.get('/openapi', (req, res) => res.json(api)); if (resolvedParams.openApiServeDocs) app.use('/api-docs', swagger.serve, swagger.setup(api)); app.use(ServerUtils.errorHandler()); app.use( OpenApiValidator.middleware({ operationHandlers: { basePath: __dirname, resolver: (modulePath, route: OpenApiRoute): express.RequestHandler => { return this.openApiRouterResolver.resolver(route); }, }, ignorePaths: (path) => { return resolvedParams.ignorePaths.includes(path); }, apiSpec: apiSpec, validateRequests: resolvedParams.openApiValidateRequests, validateResponses: resolvedParams.openApiValidateResponses, }), ); this.app = app; } start(port: number): Promise<void> { if (this.server) { throw new Error('Server has already started!!!'); } return new Promise<void>((resolve) => { this.server = this.app.listen({ port: port }, () => { console.log(`🚀 Rosetta server ready and listening at ==> http://localhost:${port}`); resolve(); }); }); } stop(): Promise<void> { if (!this.server) { return Promise.resolve(); } return new Promise<void>((resolve) => { this.server.close(() => { this.server = undefined; resolve(); }); }); } }