@restorecommerce/chassis-srv
Version:
Restore Commerce microservice chassis
147 lines • 5.06 kB
JavaScript
import { createLogger } from '@restorecommerce/logger';
import { isNullish, isString, keys } from 'remeda';
import { EventEmitter } from 'events';
import { grpcServer } from './transport/provider/grpc/index.js';
const transports = {};
/**
* register transport provider
*
* @param {string} name transport provider identifier
* @param {constructor} provider transport provider constructor function
*/
export const registerTransport = (name, provider) => {
transports[name] = provider;
};
// register included providers
registerTransport('grpc', grpcServer);
/**
* Initializes all configured transports.
* @param {object} config Configuration
* @param {object} logger
* @return {object} Transport
*/
const setupTransport = (config, logger) => {
const transport = {};
logger.debug('available transport providers', Object.keys(transports).join(','));
for (let i = 0; i < config.length; i += 1) {
const transportCfg = config[i];
const providerName = transportCfg.provider;
if (isNullish(providerName)) {
throw new Error('transport configuration without a provider');
}
const transportName = transportCfg.name;
if (isNullish(providerName)) {
throw new Error('transport configuration without a name');
}
const TransportProvider = transports[providerName];
if (isNullish(TransportProvider)) {
throw new Error(`transport provider ${providerName} does not exist`);
}
const provider = new TransportProvider(transportCfg, logger);
transport[transportName] = provider;
}
logger.debug('using transports', Object.keys(transport).join(','));
return transport;
};
/**
* Server is a microservice server chassis.
* It enables business logic to be accessed over transports and listen to events.
* Default event providers: 'kafka'
* Default transports: 'grpc'
* @class
*/
export class Server extends EventEmitter {
config;
logger;
middleware;
transport;
/**
* @constructor
* @param {object} config Server config.
* @param {Logger} logger
*/
constructor(config, logger) {
super();
if (isNullish(config)) {
throw new Error('mising argument config');
}
this.config = config;
// logger
if (isNullish(logger)) {
if (isNullish(this.config.logger)) {
this.logger = createLogger();
}
else {
const loggerCfg = this.config.logger;
this.logger = createLogger(loggerCfg);
}
}
else {
this.logger = logger;
}
// services
this.logger.debug('setting up service endpoints');
// transports
this.logger.debug('setting up transports');
try {
this.transport = setupTransport(this.config.transports, this.logger);
}
catch (error) {
this.logger.error('setupTransports', { code: error.code, message: error.message, stack: error.stack });
throw error;
}
}
/**
* bind connects the service to configured transports.
*
* @param {string} name Service name.
* @param {BindConfig} bindConfig A business logic service.
*/
async bind(name, bindConfig) {
if (isNullish(name)) {
throw new Error('missing argument name');
}
if (!isString(name)) {
throw new Error('argument name is not of type string');
}
if (isNullish(bindConfig)) {
throw new Error('missing argument bindConfig');
}
this.logger.debug('binding endpoints to transports');
const transportNames = Object.keys(this.transport);
const transport = this.transport;
for (let i = 0; i < transportNames.length; i += 1) {
const transportName = transportNames[i];
const provider = transport[transportName];
await provider.bind(bindConfig);
this.emit('bound', name, bindConfig, provider);
}
}
/**
* start launches the server by starting transports and listening to events.
*/
async start() {
const transportNames = Object.keys(this.transport);
for (let i = 0; i < transportNames.length; i += 1) {
const name = transportNames[i];
const provider = this.transport[name];
await provider.start();
this.logger.info(`transport ${name} started`);
}
this.emit('serving', this.transport);
}
/**
* Shuts down all transport provider servers.
*/
async stop() {
const transportNames = keys(this.transport);
for (let i = 0; i < transportNames.length; i += 1) {
const name = transportNames[i];
if (this.transport[name].end) {
await this.transport[name].end();
}
}
this.emit('stopped', this.transport);
}
}
//# sourceMappingURL=server.js.map