nats-micro
Version:
NATS micro compatible extra-lightweight microservice library
183 lines • 8.99 kB
JavaScript
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
return new (P || (P = Promise))(function (resolve, reject) {
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
step((generator = generator.apply(thisArg, _arguments || [])).next());
});
};
import moment from 'moment';
import { isUndefined } from 'util';
import { z } from 'zod';
import { zodToJsonSchema } from 'zod-to-json-schema';
import { localConfig } from '../localConfig.js';
import { MicroserviceRegistrationSubject, } from '../types/index.js';
import { randomId, wrapMethod, attachThreadContext, } from '../utils/index.js';
const emptyMethodProfile = {
num_requests: 0,
num_errors: 0,
last_error: '',
processing_time: 0,
average_processing_time: 0,
};
export class Discovery {
constructor(broker, configOrGetter, options = {}) {
this.broker = broker;
this.configOrGetter = configOrGetter;
this.options = options;
this.methodStats = {};
this._isStarted = false;
this.startedAt = new Date();
this.id = randomId();
const wrap = (handler, name) => wrapMethod(this.broker, attachThreadContext(this.id, handler.bind(this)), { microservice: this.originalConfig.name, method: name });
this.handleSchemaWrap = wrap(this.handleSchema, 'handleSchema');
this.handleInfoWrap = wrap(this.handleInfo, 'handleInfo');
this.handlePingWrap = wrap(this.handlePing, 'handlePing');
this.handleStatsWrap = wrap(this.handleStats, 'handleStats');
}
get originalConfig() {
if (typeof (this.configOrGetter) === 'function')
return this.configOrGetter();
return this.configOrGetter;
}
get config() {
let config = this.originalConfig;
if (this.options.transformConfig)
config = this.options.transformConfig(config);
return config;
}
get isStarted() {
return this._isStarted;
}
start() {
return __awaiter(this, void 0, void 0, function* () {
this._isStarted = true;
this.broker.on('$SRV.SCHEMA', this.handleSchemaWrap);
this.broker.on(`$SRV.SCHEMA.${this.config.name}`, this.handleSchemaWrap);
this.broker.on(`$SRV.SCHEMA.${this.config.name}.${this.id}`, this.handleSchemaWrap);
this.broker.on('$SRV.INFO', this.handleInfoWrap);
this.broker.on(`$SRV.INFO.${this.config.name}`, this.handleInfoWrap);
this.broker.on(`$SRV.INFO.${this.config.name}.${this.id}`, this.handleInfoWrap);
this.broker.on('$SRV.PING', this.handlePingWrap);
this.broker.on(`$SRV.PING.${this.config.name}`, this.handlePingWrap);
this.broker.on(`$SRV.PING.${this.config.name}.${this.id}`, this.handlePingWrap);
this.broker.on('$SRV.STATS', this.handleStatsWrap);
this.broker.on(`$SRV.STATS.${this.config.name}`, this.handleStatsWrap);
this.broker.on(`$SRV.STATS.${this.config.name}.${this.id}`, this.handleStatsWrap);
yield this.publish();
return this;
});
}
stop() {
return __awaiter(this, void 0, void 0, function* () {
this._isStarted = false;
yield this.publishRegistration('down');
this.broker.off('$SRV.SCHEMA', this.handleSchemaWrap);
this.broker.off(`$SRV.SCHEMA.${this.config.name}`, this.handleSchemaWrap);
this.broker.off(`$SRV.SCHEMA.${this.config.name}.${this.id}`, this.handleSchemaWrap);
this.broker.off('$SRV.INFO', this.handleInfoWrap);
this.broker.off(`$SRV.INFO.${this.config.name}`, this.handleInfoWrap);
this.broker.off(`$SRV.INFO.${this.config.name}.${this.id}`, this.handleInfoWrap);
this.broker.off('$SRV.PING', this.handlePingWrap);
this.broker.off(`$SRV.PING.${this.config.name}`, this.handlePingWrap);
this.broker.off(`$SRV.PING.${this.config.name}.${this.id}`, this.handlePingWrap);
this.broker.off('$SRV.STATS', this.handleStatsWrap);
this.broker.off(`$SRV.STATS.${this.config.name}`, this.handleStatsWrap);
this.broker.off(`$SRV.STATS.${this.config.name}.${this.id}`, this.handleStatsWrap);
return this;
});
}
publish() {
return __awaiter(this, void 0, void 0, function* () {
yield this.publishRegistration('up');
});
}
publishRegistration(state) {
return __awaiter(this, void 0, void 0, function* () {
yield this.broker.send(MicroserviceRegistrationSubject, {
info: this.makeInfo(),
state,
});
});
}
profileMethod(name, error, time) {
if (!this.methodStats[name])
this.methodStats[name] = Object.assign({}, emptyMethodProfile);
const method = this.methodStats[name];
method.num_requests++;
if (!isUndefined(error)) {
method.num_errors++;
method.last_error = error;
}
method.processing_time += time;
method.average_processing_time =
Math.round(Number(method.processing_time) / method.num_requests);
}
makeMicroserviceData() {
return {
name: this.config.name,
id: this.id,
version: this.config.version,
metadata: Object.assign(Object.assign({ '_nats.client.created.library': localConfig.name, '_nats.client.created.version': localConfig.version, '_nats.client.id': String(this.broker.clientId) }, (!isUndefined(this.broker.name)
? { 'nats.micro.ext.v1.service.node': this.broker.name }
: {})), this.config.metadata),
};
}
makeMethodData(name, method) {
return {
name: name,
subject: this.getMethodSubject(name, method),
};
}
getMethodSubject(name, method) {
if (method.subject)
return method.subject;
if (method.local)
return `${this.config.name}.${this.id}.${name}`;
return `${this.config.name}.${name}`;
}
makeInfo() {
return Object.assign(Object.assign({}, this.makeMicroserviceData()), { description: this.config.description, type: 'io.nats.micro.v1.info_response', endpoints: Object.entries(this.config.methods)
.map(([n, m]) => {
const metadata = Object.assign({}, m.metadata);
if (m.unbalanced)
metadata['nats.micro.ext.v1.method.unbalanced'] = 'true';
if (m.local)
metadata['nats.micro.ext.v1.method.local'] = 'true';
return Object.assign(Object.assign({}, this.makeMethodData(n, m)), { metadata });
}) });
}
getMethodSchema(name, kind) {
var _a;
const method = this.config.methods[name];
return method ? ((_a = method[kind]) !== null && _a !== void 0 ? _a : z.void()) : undefined;
}
getMethodJsonSchema(name, kind) {
const schema = this.getMethodSchema(name, kind);
return schema ? zodToJsonSchema(schema) : undefined;
}
handleSchema(_req, res) {
res.send(Object.assign(Object.assign({}, this.makeMicroserviceData()), { type: 'io.nats.micro.v1.schema_response', endpoints: Object.entries(this.config.methods)
.map(([n, m]) => (Object.assign(Object.assign({}, this.makeMethodData(n, m)), { schema: {
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
request: this.getMethodJsonSchema(n, 'request'),
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
response: this.getMethodJsonSchema(n, 'response'),
} }))) }));
}
handleInfo(_req, res) {
res.send(this.makeInfo());
}
handleStats(_req, res) {
res.send(Object.assign(Object.assign({}, this.makeMicroserviceData()), { type: 'io.nats.micro.v1.stats_response', started: moment(this.startedAt).toISOString(), endpoints: Object.entries(this.config.methods)
.map(([n, m]) => {
var _a;
return (Object.assign(Object.assign({}, this.makeMethodData(n, m)), ((_a = this.methodStats[n]) !== null && _a !== void 0 ? _a : emptyMethodProfile)));
}) }));
}
handlePing(_req, res) {
res.send(Object.assign(Object.assign({}, this.makeMicroserviceData()), { type: 'io.nats.micro.v1.ping_response' }));
}
}
//# sourceMappingURL=discovery.js.map