floret
Version:
Microservice Library for Node
1,260 lines (1,066 loc) • 39.9 kB
JavaScript
const logger = require('koa-logger');
const Router = require('koa-router');
const appRouter = new Router();
const Koa = require('koa');
const ip = require('ip');
const bodyParser = require('koa-bodyparser');
const apiDoc = require('swagger-jsdoc');
const fs = require('fs');
const uuidv4 = require('uuid/v4');
const send = require('koa-send');
// core floret services
const Config = require('./config/floret-config.js');
const Service = require('./service/floret-service.js');
const Channel = require('./channel/floret-channel.js');
const Subscriber = require('./subscriber/floret-subscriber.js');
const Subscription = require('./subscription/floret-subscription.js');
const Package = require('./package/floret-package.js');
let FloretClass;
{
// private
const _host = Symbol('host');
const _name = Symbol('name');
const _port = Symbol('port');
const _uri = Symbol('uri');
const _appRouter = Symbol('router');
const _gateway = Symbol('gateway');
const _listen = Symbol('listen');
const _subscribers = Symbol('subscribers');
const _subscriptions = Symbol('subscriptions');
const _channels = Symbol('channels');
const _service = Symbol('service');
const _baseURL = Symbol('baseURL');
const _config = Symbol('config');
const _docPaths = Symbol('docPaths');
const _disconnected = Symbol('disconnected');
const _registeredAPIs = Symbol('registeredAPIs');
const _environmentVars = Symbol('environmentVars');
const _modules = Symbol('modules');
const _logging = Symbol('_logging');
const _root = Symbol('_root');
// replaceable classes
const _gatewayClass = Symbol('gatewayClass');
const _subscriberClass = Symbol('subscriberClass');
const _subscriptionClass = Symbol('subscriptionClass');
const _channelClass = Symbol('channelClass');
const _serviceClass = Symbol('serviceClass');
const _packageClass = Symbol('packageClass');
const _configClass = Symbol('configClass');
const _send = Symbol('sender');
const _apiDoc = Symbol('apiDoc');
FloretClass = class Floret extends Koa {
constructor(floretConfig, ...[ConfigOverride, ChannelOverride, SubscriberOverride,
SubscriptionOverride, ServiceOverride, PackageOverride]) {
// koa
super();
if (floretConfig) {
this[_gatewayClass] = require(floretConfig.gatewayModulePath
|| floretConfig.gatewayModuleName);
this.configure(floretConfig, ...[ConfigOverride, ChannelOverride, SubscriberOverride,
SubscriptionOverride, ServiceOverride, PackageOverride]);
this.configureServices();
} else {
console.log('setting default gateway class: floret-gateway-kong');
this[_gatewayClass] = require('floret-gateway-kong');
}
this[_configClass] = Config;
this[_channelClass] = Channel;
this[_subscriberClass] = Subscriber;
this[_subscriptionClass] = Subscription;
this[_serviceClass] = Service;
this[_packageClass] = Package;
this[_appRouter] = appRouter;
this[_apiDoc] = apiDoc;
this[_modules] = {};
this[_send] = send;
}
configure(floretConfig, ...[ConfigOverride, ChannelOverride, SubscriberOverride,
SubscriptionOverride, ServiceOverride, PackageOverride]) {
this[_config] = floretConfig.params;
console.log('configuring with ', JSON.stringify(this[_config]));
const name = this[_config].gatewayModuleName ? `../../${this[_config].gatewayModuleName}` : '';
if (!floretConfig.disconnected) {
let modPath = this[_config].gatewayModulePath || name;
this[_gatewayClass] = modPath ? require(modPath) : require('../../floret-gateway-kong');
}
this[_host] = this[_config].host;
this[_name] = this[_config].name;
this[_port] = this[_config].port;
this[_root] = this[_config].root;
this[_uri] = this[_config].uri || `/${this[_name]}`;
this[_docPaths] = this[_config].documentationPaths || [];
console.log('this[_host], this[_name], this[_port], this[_root], this[_uri]', this[_host], this[_name], this[_port], this[_root], this[_uri])
this[_listen] = super.listen;
this[_disconnected] = this[_config].disconnected;
this[_logging] = this[_config].logging;
this[_registeredAPIs] = [];
if (!this[_disconnected]) {
this[_subscribers] = [];
this[_subscriptions] = [];
this[_channels] = {};
// overridess
this[_configClass] = ConfigOverride || Config;
this[_channelClass] = ChannelOverride || Channel;
this[_subscriberClass] = SubscriberOverride || Subscriber;
this[_subscriptionClass] = SubscriptionOverride || Subscription;
this[_serviceClass] = ServiceOverride || Service;
this[_packageClass] = PackageOverride || Package;
this[_gateway] = new this[_gatewayClass]('gw', this[_config].gatewayHost, this[_config].gatewayAdminPort, this[_config].gatewayProxyPort);
this[_service] = new this[_serviceClass](this[_name], this[_host], this[_port], this[_uri]);
this[_baseURL] = `${this[_gateway].ensureProtocol(this[_host])}:${this[_port]}${this[_uri]}`;
}
this.configureServices();
this.isInit = false;
console.log('Configuration complete.');
}
configureServices() {
if (this[_config].channels) {
this.configureChannels(this[_config].channels);
}
if (this[_config].subscriptions) {
this.configureSubscriptions(this[_config].subscriptions);
}
if (this[_config].apis) {
this.configureAPIs(this[_config].apis);
}
}
/**
*
* @param portOverride
*/
async listen(portOverride) {
if (!this.isInit) {
await this.init().then(() => {}).catch((e) => { throw e; });
console.log('...complete.');
}
const srv = await this[_listen](portOverride || this[_port]);
console.log(`Listening on port ${JSON.stringify(srv.address().port)}`);
return srv;
}
/**
*
* @returns {send}
*/
get serve() {
return this[_send];
}
/**
*
* @returns {boolean}
*/
async init() {
try {
console.log(`Initializing ${this[_name]}:`);
console.log(`...routes prefixed [${this[_uri]}]`);
this.router.prefix(this[_uri]);
if (!this[_disconnected]) {
let routes = [];
await this[_gateway].gatewayHealthCheck();
console.log('...gateway verified');
const service = await this.registerService();
this[_service].id = service.id;
console.log(`...service ${this[_name]} registered with gateway. id: ${this[_service].id}`);
console.log()
routes = routes.concat(await this.publishFloretConfig());
console.log(`...floret spec created: (${this[_uri]}/floret.json)`);
routes = routes.concat(await this.createHealthCheck());
console.log(`...healthcheck created: (${this[_uri]}/healthcheck)`);
await this.createSubscriberAPI();
console.log('...subscriber api created');
routes = routes.concat(await this.initSubscriptions());
await this.registerRoutes(routes);
console.log('...subscriptions initialized');
const channelService = await this.createChannelService(this[_name]);
routes = await this.initChannels(channelService);
console.log('...subscriber api created');
await this.registerRoutes(routes, `${this[_name]}-channel`);
console.log(`...channel service ${this[_name]}-channel initialized`);
await this.createSubscriberService(this[_name]);
console.log('subscriber service created');
routes = await this.initSubscribers(this[_channels]);
await this.registerRoutes(routes, `${this[_name]}-subscriber`);
routes = await this.registerAPIs();
console.log('...api endpoints created');
if (this[_config].publishDocs) {
await this.publishAPIDocumentation(this[_docPaths]);
console.log('...api documentation published');
}
await this.registerRoutes(routes);
console.log('...service routes registered with gateway');
}
this.use(bodyParser());
if (this[_logging]) {
this.use(logger());
}
this.use(async (ctx, next) => {
ctx.body = ctx.request.body;
await next();
});
this.use(this.router.routes());
this.use(this[_appRouter].allowedMethods());
this.isInit = true;
return true;
} catch (e) {
console.log('Error occurred during floret init routine.');
console.log(e);
throw e;
}
}
/**
*
* @returns {*}
*/
get name() {
return this[_name];
}
/**
*
* @returns {*}
*/
get host() {
return this[_host];
}
/**
*
* @returns {*}
*/
get port() {
return this[_port];
}
/**
*
* @returns {string}
*/
get url() {
return `${this[_host]}:${this[_port]}`;
}
/**
*
* @returns {*}
*/
get service() {
return this[_service];
}
get Service() {
return this[_serviceClass];
}
/**
*
* @returns {*}
*/
get router() {
return this[_appRouter];
}
/**
*
* @returns {*}
*/
get gateway() {
return this[_gateway];
}
get Gateway() {
return this[_gatewayClass];
}
/**
*
* @returns {*}
* @constructor
*/
get Package() {
return this[_packageClass];
}
/**
*
* @param pkg
* @constructor
*/
set Package(pkg) {
this[_packageClass] = pkg;
}
get config() {
return this[_config];
}
/**
*
* @returns {*}
* @constructor
*/
get Config() {
return this[_configClass];
}
/**
*
* @param cfg
* @constructor
*/
set Config(cfg) {
this[_configClass] = cfg;
}
/**
*
* @returns {*}
*/
get apiDoc() {
return this[_apiDoc];
}
/**
*
* @returns {*}
*/
get uri() {
return this[_uri];
}
/**
*
* @param u
*/
set uri(u) {
this[_uri] = u;
}
/**
*
* @returns {*}
*/
get baseURL() {
return this[_baseURL];
}
/**
*
* @returns {*}
*/
get baseURI() {
return this[_uri];
}
/**
*
*/
async createHealthCheck() {
this[_appRouter].get('/healthcheck/ping', (ctx) => {
ctx.set('Content-Type', 'text/plain');
ctx.body = 'OK';
});
this[_appRouter].get('/healthcheck/', async (ctx) => {
const gwHealth = await this[_gateway].gatewayHealthCheck();
ctx.body = {
ping: {
healthy: true,
},
'api-gateway': {
healthy: gwHealth.status === 'active',
message: `api gateway at ${this[_gateway].url}`,
},
};
});
const routes = [];
routes.push(
{
path: `${this[_uri]}/healthcheck`,
methods: ['GET', 'OPTIONS'],
},
{
path: `${this[_uri]}/healthcheck/ping`,
methods: ['GET', 'OPTIONS'],
},
);
return routes;
}
publishFloretConfig() {
this[_appRouter].get('/floret.json', (ctx) => {
ctx.body = this[_config];
});
return [{
path: `${this[_uri]}/floret.json`,
methods: ['GET', 'OPTIONS'],
}];
}
/** API ************************************************************************************ */
/**
*
* @param name
* @returns {*}
*/
async getAPI(name) {
try {
return await this.gateway.getAPI(name);
} catch (e) {
// throw e;
return {};
}
}
get registeredAPIs() {
return this[_registeredAPIs];
}
set registeredAPIs(arr) {
this[_registeredAPIs] = arr;
}
registerAPIs() {
console.log('...registering apis');
const routes = [];
this[_registeredAPIs].map((api) => {
routes.push({
path: api.uri,
methods: api.methods,
});
return api;
});
return routes;
}
async registerService() {
console.log('calling register service: ')
console.log('name' + this[_name],
'host' + this[_host],
'port' + this[_port],
'/');
return this[_gateway].addService({
name: this[_name],
host: this[_host],
port: this[_port],
path: '/'
});
}
async registerRoutes(routes = [], serviceName = this[_name], methods = ['GET', 'POST', 'PUT', 'PATCH', 'DELETE']) {
let paths = [];
let allMethods = [];
for (let i = 0; i < routes.length; i++) {
paths = paths.concat(routes[i].path);
allMethods = methods.concat(routes[i].methods);
}
await this[_gateway].addRoute({
serviceName,
allMethods,
paths,
});
}
/**
*
* @param name
* @param uri
* @param methods
*/
async registerAPI(name, uri, methods) {
await this[_gateway].addRoute({
serviceName: this[_name],
methods,
path: uri,
}).catch((e) => {
console.log(e.stack);
});
}
/**
*
* @param name
* @param uri
* @param methods
*/
async registerExternalAPI(name, uri, url, methods) {
await this[_gateway].addAPI(name, this[_uri] + uri, `${url}`, methods).then(() => {
}).catch((e) => {
if (e.statusCode !== 409) {
console.log(`api could not be registered. ${e.message}`);
throw e;
}
});
}
prependUri(uri) {
if (uri.indexOf(this[_uri]) < 0) {
return this[_uri] + uri;
}
return uri;
}
prependName(name) {
if (name.indexOf(this[_name]) < 0) {
return `${this[_name]}-${name}`;
}
return name;
}
configureAPIs(apis) {
const newApis = apis.map((api) => {
if (api.path) {
require(`${this[_root]}${api.path}`)(this);
}
api.uri = this.prependUri(api.uri);
return api;
});
this[_registeredAPIs] = this[_registeredAPIs] || [];
this[_registeredAPIs] = this[_registeredAPIs].concat(newApis);
}
/**
*
* @param name
* @returns {*}
*/
async deleteAPI(name) {
return this.gateway.deleteAPI(name);
}
/**
* @param uri
* @param method
* @param payload
* @param gateway
* @returns {*|any|XMLHttpRequest}
*/
async apiRequest(uri, method, payload, trackingId, gatewayOverride) {
const gw = gatewayOverride || this[_gateway];
trackingId = trackingId || uuidv4();
const options = {
method,
uri: gw.proxyURL + uri,
header: {
'Content-Type': 'application/json',
},
};
options.body = payload || {};
if (method.indexOf('post')) {
options.header['Content-Type'] = 'application/json';
}
options.body.trackingId = options.body.trackingId || trackingId;
const res = await gw.send(options);
res.trackingId = trackingId;
return res;
}
/** API Docs ******************************************************************************* */
/**
*
* @param newPaths
* @returns {Promise<void>}
*/
async publishAPIDocumentation(newPaths = []) {
console.log('...creating OpenAPI Spec');
this.buildAPISpec(newPaths);
const channel = await this.createAPISpecChannel();
// subscribe
channel.broadcast(this.name, { url: this.apiSpecURL });
// send a message to the document service
const options = {
uri: `${this.gateway.proxyURL}/api-doc/specs`,
method: 'GET',
};
console.log('about to pusblish')
// notify api-doc service to refresh spec list
await this.gateway.publishAPISpec(options).then(() => {
console.log('done publish')
}).catch((e) => {
console.log(`Default document service not found (${this.gateway.proxyURL}/api-doc/specs)`);
console.log(e.message);
});
const routes = [{
path: '/api-spec.json',
methods: ['GET', 'POST', 'DELETE'],
}];
console.log('done')
return routes;
}
/**
*
* @returns {Promise<*>}
*/
async createAPISpecChannel() {
const channelConfig = {
name: 'api-spec',
description: 'api spec',
uri: '/api-spec',
endpoint: `${this.url}${this.uri}/api-spec`,
};
const channel = new this[_channelClass](channelConfig);
await this.addChannel(channel);
return this.channels[channelConfig.name];
}
/**
*
* @returns {string}
*/
get apiSpecURL() {
return `${this.url}${this.uri}}/api-spec.json`;
}
/**
*
* @param newPaths
*/
buildAPISpec(documentedCodePaths = []) {
const swaggerDefinition = {
info: {
title: `${this.name}`,
version: '1.0.0',
description: 'A sample API',
},
host: this.gateway.proxyURL.split('http://')[1],
basePath: '/',
};
// Options for the swagger docs
const options = {
swaggerDefinition,
apis: documentedCodePaths,
};
const swaggerSpec = this[_apiDoc](options);
swaggerSpec.paths[`${this.uri}/subscribe`] = {
post: {
summary: 'subscribe to channel',
operationId: 'subscribe',
tags: ['floret'],
responses: [
{
200: {
name: 'floret subscription id',
},
201: {
description: 'Null response',
},
}],
},
delete: {
summary: 'unsubscribe to channel',
operationId: 'subscribe',
tags: ['floret'],
responses: [
{
200: {
name: 'floret subscription id',
},
201: {
description: 'Null response',
},
}],
},
};
swaggerSpec.paths[`${this.uri}/healthcheck`] = {
get: {
summary: 'returns an active health status when running',
operationId: 'healthcheck',
tags: ['floret'],
responses: [
{
200: {
status: 'active',
},
}],
},
};
swaggerSpec.paths[`${this.uri}/floret.json`] = {
get: {
summary: 'returns an floret configuration',
operationId: 'floret',
tags: ['floret'],
responses: [
{
200: {
},
}],
},
};
this.router.get('/api-spec.json', (ctx) => {
ctx.set('Content-Type', 'application/json');
ctx.body = swaggerSpec;
});
return swaggerSpec;
}
/**
*
* @returns {Promise<*>}
*/
async discoverAPISpecs() {
const apis = await this[_gateway].discoverAPISpecs();
return apis;
}
/** Channels ******************************************************************************* */
/**
*
*/
// list of channel routes ( path + methods )
async createChannelService(serviceName) {
// get the service
const service = await this[_gateway].getServiceByName(serviceName);
console.log('calling addService from createChannelService (floret)', 'name:'+ `${serviceName}-subscriber`, 'host:' + this[_host], 'port:' + this[_port], 'path:' + `/link/${serviceName}/${service.id}`)
return this[_gateway].addService({
name: `${serviceName}-channel`, host: this[_host], port: this[_port], path: `/link/${serviceName}/${service.id}`,
});
}
async createSubscriberService(serviceName) {
// get the service
const service = await this[_gateway].getServiceByName(serviceName);
console.log('calling addService from createSubscriberService (floret)', 'name:'+ `${serviceName}-subscriber`, 'host:' + this[_host], 'port:' + this[_port], 'path:' + `/link/${serviceName}/${service.id}`)
return this[_gateway].addService({
name: `${serviceName}-subscriber`, host: this[_host], port: this[_port], path: `/link/${serviceName}/${service.id}`
});
}
// async initSubscribers(channels) {
// }
async getChannelsFromGateway(serviceName) {
return this.discoverChannels(serviceName);
}
async initChannels() {
const gatewayChannels = await this.getChannelsFromGateway(this[_name]);
let keys = Object.keys(gatewayChannels);
for (let i = 0; i < keys.length; i++) {
if (this[_channels][keys[i]]) {
delete gatewayChannels[keys[i]];
} else {
this[_channels][keys[i]] = gatewayChannels[keys[i]];
}
}
this[_channels] = Object.assign(this[_channels], gatewayChannels);
const routes = [];
keys = Object.keys(this[_channels]);
for (let i = 0; i < keys.length; i++) {
routes.push({
path: `${this[_uri]}/channels/${this[_channels][keys[i]].name}`,
methods: ['GET', 'POST'],
});
}
return routes;
}
addChannels(channelArray) {
channelArray.map((chan) => {
this[_channels][chan.name] = chan;
});
}
configureChannels(chanConfigArr) {
// configuration-based channels
chanConfigArr.map((ch) => {
ch.endpoint = this.baseURL;
ch.serviceName = this[_name];
const channel = this.addChannel(new this.Channel(ch));
});
}
/**
*
* @param channel
*/
async initChannel(channelName) {
await this.discoverSubscribers(this[_name], channelName);
console.log(`......initialized channel ${channelName}`);
}
async initSubscribers(channels) {
for (const channelName in channels) {
const subscribers = await this.discoverSubscribers(channelName);
}
}
/**
*
* @returns {*}
* @constructor
*/
get Channel() {
return this[_channelClass];
}
/**
*
* @param channel
* @constructor
*/
set Channel(channel) {
this[_channelClass] = channel;
}
addChannel(channel) {
this[_channels][channel.name] = channel;
}
/**
*
* @returns {*}
*/
get channels() {
return this[_channels];
}
/**
*
* @returns {*|Observable.<R>|any}
*/
async discoverChannels(serviceName) {
const registeredChannels = await this[_gateway].discoverChannels(serviceName
|| this[_name]) || [];
// return channels in object format
const channelMap = registeredChannels.reduce((accObj, chan) => {
for (let i = 0; i < chan.paths.length; i++) {
const path = chan.paths[i];
if (path.indexOf('/channels/') > -1) {
const chanName = path.split(`/${this[_name]}/channels/`)[1];
const chanConfig = {
endpoint: `${this[_uri]}/channels/${chanName}`,
name: chanName,
description: chanName,
serviceName: this[_name],
uri: `${this[_uri]}/channels/${chanName}`,
};
accObj[chanName] = new this.Channel(chanConfig);
}
}
return accObj;
}, {});
return channelMap;
}
/**
*
* @param channel
*/
updateChannel(channel) {
const name = channel.name;
for (let i = 0; i < this[_channels].length; i++) {
if (this[_channels][i].config().name === name) {
this[_channels][i] = channel;
}
}
}
/**
*
* @param channel
*/
async deleteChannel(channel) {
const channelInfo = channel.config();
delete this[_channels][channel.name];
this[_gateway].deleteChannelAPI(channelInfo.apiName).then(() => {
for (let i = 0; i < this[_channels].length; i++) {
if (this[_channels][i].name === channel.name) {
this[_channels].splice(i, 1);
i++;
}
}
});
}
/** Subscribers **************************************************************************** */
/**
*
* @returns {*}
* @constructor
*/
get Subscriber() {
return this[_subscriberClass];
}
/**
*
* @param subscriber
* @constructor
*/
set Subscriber(subscriber) {
this[_subscriberClass] = subscriber;
}
/**
*
* @returns {*}
*/
async discoverSubscribers(channel) {
const subscribers = await this[_gateway].discoverSubscribers(this[_name], channel);
subscribers.map((sub) => {
if (sub.length > 0) {
for (let i = 0; i < sub.length; i++) {
const subscription = sub[i];
const channelName = subscription.channelName;
const url = `${this[_gateway].proxyURL}/${subscription.service}/subscriptions/${subscription.name}`;
const subObj = new this.Subscriber(subscription.name, subscription.service, `${this[_gateway].proxyURL}/${subscription.service}/subscriptions/${subscription.name}`);
if (!this.channels[channelName].subscribers[subscription.name]) {
this.attachSubscriber(this.channels[channelName], subObj);
}
}
}
return sub;
});
return subscribers;
}
attachSubscriber(channel, subscriber) {
if (channel && subscriber) {
if (!channel.subscribers[subscriber.name]) {
channel.subscribe(subscriber);
}
}
}
/**
*
*/
async createSubscriberAPI() {
// gateway endpoint for new subscriptions
console.log('adding route for subscriber for: ' + this[_name]);
await this[_gateway].addRoute({
serviceName: this[_name],
methods: ['GET', 'POST', 'OPTIONS', 'DELETE'],
paths: [`${this[_uri]}/subscribe`]
});
// subscribe api
this[_appRouter].post('/subscribe/', async (ctx, next) => {
const subRequest = ctx.request.body;
const subscriberName = subRequest.name;
const channelName = subRequest.channel;
const serviceName = subRequest.service;
const serviceURL = subRequest.url;
ctx.body = {
name: subRequest.name,
};
const newSubscriber = new this.Subscriber(subRequest.name,
subRequest.service,
subRequest.url);
const subURI = `/${this[_name]}/channels/${subRequest.channel}/subscriber/${subRequest.service}/subscriptions/${subRequest.name}`;
this.attachSubscriber(this.channels[subRequest.channel], newSubscriber);
// create the subscriber gateway endpoint
await this[_gateway].addRoute({
serviceName: `${this[_name]}-subscriber`,
methods: ['GET', 'POST', 'OPTIONS', 'DELETE'],
paths: [subURI],
});
}).delete('/subscribe/:channelName/:id', async (ctx) => {
console.log('delete inbound');
const channelName = ctx.params.channelName;
const subscriberName = ctx.params.id;
const subscriberId = ctx.params.id;
if (this[_channels][channelName]) {
this[_channels][channelName].unsubscribe(subscriberId);
}
// remove subscriber route path
await this[_gateway].deleteRoutePath(this[_name], channelName, subscriberName);
// should be empty
ctx.body = {
result: this[_channels][channelName].subscribers[subscriberName],
};
}).get('/subscribers/', (ctx, next) => {
ctx.body = this[_subscribers];
}).patch('/subscribers/:name', (ctx, next) => {
const event = ctx.body;
})
.put('/subscribers/:name', (ctx, next) => {
const event = ctx.body;
});
}
async subscribe({ serviceName, channelName, subscription }) {
const res = await this.gateway.subscribeTo({
targetService: serviceName,
targetChannel: channelName,
subscriberService: this[_name],
subscriberName: subscription.name,
subscriptionEndpoint: subscription.endpoint,
});
return res.name;
}
async unsubscribe(serviceName, channelName, subscriberName) {
await this.gateway.unsubscribe(serviceName, channelName, subscriberName);
await this.gateway.deleteAPI(subscriberName);
}
/**
*
* @param channelName
* @returns {Array|Array.<T>|*|Observable|any}
*/
subscribersByChannel(channelName) {
return this[_subscribers].filter(sub => sub.channel === channelName);
}
/**
*
* @param channelName
*/
removeChannelSubscribers(channelName) {
this[_subscribers] = this[_subscribers].filter(sub => sub.channel === channelName);
}
/** Subscriptions ************************************************************************** */
/**
*
*/
async initSubscriptions() {
const routes = [];
this[_subscriptions].map((sub) => {
sub.init();
console.log(`......initialized subscription ${sub.name}`);
routes.push({
path: `/${this[_name]}/subscriptions/${sub.name}`,
methods: ['GET', 'POST', 'OPTIONS'],
});
});
return routes;
}
addCustomAPI(api) {
this[_registeredAPIs].push(api);
}
addSubscription(subscription) {
if (this[_subscriptions].filter(sub => sub.name === subscription.name).length < 1) {
this[_subscriptions].push(subscription);
}
}
getSubscriptionByName(name) {
const sub = this[_subscriptions].filter(sub => sub.name === name);
return sub[0];
}
configureSubscriptions(subs) {
subs.map((sub) => {
const newSub = new this.Subscription(sub.name, this.service, this.router,
this.gateway.proxyURL);
const path = sub.path;
const handler = require(`${this[_root]}${path}`)(this);
newSub.observable.subscribe(handler.onEvent);
this.addSubscription(newSub);
this.subscribe({
serviceName: sub.service,
channelName: sub.channel,
subscription: newSub,
}).catch((e) => {
console.log(`Unabled to subscribe to ${sub.service}-${sub.channel} (${e.message})`);
});
});
}
get Subscription() {
return this[_subscriptionClass];
}
/**
*
* @returns {*}
*/
get subscriptions() {
return this[_subscriptions];
}
/**
*
* @param subArray
*/
set subscriptions(subArray) {
this[_subscriptions] = subArray;
}
createConfig(cfg, service, host, port, uri, gatewayHost, gatewayAdminPort,
gatewayProxyPort, disconnected) {
return new this[_configClass](cfg, service, host, port, uri, gatewayHost,
gatewayAdminPort, gatewayProxyPort, disconnected);
}
attachModule(name, fn) {
this[_modules][name] = fn;
}
getModule(name) {
return this[_modules][name];
}
get environmentVariables() {
return this[_environmentVars];
}
set environmentVariables(env) {
this[_environmentVars] = env;
}
createEnvConfig(envVars) {
let envStr = 'local'; let host; let port; let name; let uri; let gatewayHost;
let gatewayAdminPort; let gatewayProxyPort; let disconnected; let root; let config; let envObj;
this[_environmentVars] = envVars;
if (envVars) {
envStr = envVars.FLORET_ENV_KEY || 'local';
host = envVars.FLORET_HOST;
port = envVars.FLORET_PORT;
name = envVars.FLORET_NAME;
uri = envVars.FLORET_URI;
gatewayHost = envVars.FLORET_GATEWAY_HOST;
gatewayAdminPort = envVars.FLORET_GATEWAY_ADMIN_PORT;
gatewayProxyPort = envVars.FLORET_GATEWAY_PROXY_PORT;
disconnected = envVars.FLORET_IS_DISCONNECTED;
root = envVars.FLORET_ROOT;
}
root = envStr === 'local' ? process.cwd() : root;
config = JSON.parse(fs.readFileSync(`${root}/floret.json`, 'utf8'));
config.root = root;
if (!config.environments) {
config.environments = {
local: {
gatewayAdminPort: 8001,
gatewayProxyPort: 8000,
},
};
}
if ((envStr === 'local')) {
config.environments[envStr].gatewayHost = `${ip.address()}`;
config.environments[envStr].host = `${ip.address()}`;
config.root = root;
}
envObj = Object.assign({}, config.environments[envStr]);
delete config.environments;
return this.createConfig(Object.assign({}, config, envObj), name, host, port, uri,
gatewayHost, gatewayAdminPort, gatewayProxyPort, disconnected);
}
};
}
module.exports = FloretClass;