Source: client.js

/* eslint-disable max-len */
/* eslint-disable no-underscore-dangle */
const grpc = require('@grpc/grpc-js');
const validate = require('validate.js');
const protoLoader = require('@grpc/proto-loader');

const utils = require('./utils');

const { log } = utils;

/**
 * Instantiate an elarian client. You have to call connect() on then client to start using it
 * @class
 * @param {ClientConfig} config
 */
function Client(config) {
    const conf = {
        ...config,
    };
    const constraints = {
        appId: {
            type: 'string',
            presence: true,
        },
        token: {
            type: 'string',
            presence: true,
        },
        options: {
            type: 'object',
        },
    };

    const error = validate(conf, constraints);
    if (error) {
        throw error;
    }

    this.config = {
        ...conf,
        options: conf.options || {
            uris: {
                social: 'id.elarian.com:443',
                state: 'api.elarian.com:443',
            },
        },
    };
    this._socialService = null;
    this._appStateService = null;

    this.eventListeners = {
        // Connection
        error: null,
        closed: null,
        pending: null,
        connected: null,
        connecting: null,
    };

    /**
     * Connecto to elarian servers
     * @returns {Elarian} this instance
     */
    this.connect = function connect() {
        // TODO: Connect to notification service

        if (this.eventListeners.connected) {
            this.eventListeners.connected();
        }
        return this;
    };

    /**
     * Check if client is connected
     * @returns {boolean}
     */
    this.isConnected = function isConnected() {
        return this._isConnected;
    };

    /**
     * Disconnect from the elarian server
     */
    this.disconnect = function disconnect() {
        // TODO: Disconnect from notification service

        if (this.eventListeners.closed) {
            this.eventListeners.closed();
        }
    };

    const cleanup = (code) => {
        log.warn(`Disconnecting from API server(${code})`);
        this.disconnect();
        process.exit(code);
    };

    process.on('SIGINT', cleanup.bind(null));
    process.on('SIGQUIT', cleanup.bind(null));
    process.on('SIGTERM', cleanup.bind(null));
}

// eslint-disable-next-line no-underscore-dangle
Client.prototype._getAppStateService = function _getAppStateService() {
    if (this._appStateService) {
        return this._appStateService;
    }

    const { options } = this.config;
    const uri = options.uris.state;
    const def = protoLoader.loadSync(
        utils.SERVICE_PROTO.APP_STATE,
        {
            keepCase: true,
            longs: String,
            enums: String,
            defaults: true,
            oneofs: true,
        },
    );
    const pd = grpc.loadPackageDefinition(def);
    const credentials = options.dev ? grpc.credentials.createInsecure() : grpc.credentials.createSsl();
    this._appStateService = new pd.com.elarian.hera.proto.AppStateService(uri, credentials);
    return this._appStateService;
};

// eslint-disable-next-line no-underscore-dangle
Client.prototype._getSocialService = function _getSocialService() {
    if (this._socialService) {
        return this._socialService;
    }

    const { options } = this.config;
    const uri = options.uris.social;
    const def = protoLoader.loadSync(
        utils.SERVICE_PROTO.SOCIAL,
        {
            keepCase: true,
            longs: String,
            enums: String,
            defaults: true,
            oneofs: true,
        },
    );
    const pd = grpc.loadPackageDefinition(def);
    const credentials = options.dev ? grpc.credentials.createInsecure() : grpc.credentials.createSsl();
    this._socialService = new pd.com.elarian.hera.proto.SocialService(uri, credentials);
    return this._socialService;
};

// eslint-disable-next-line no-underscore-dangle
Client.prototype._notificationHandler = () => (payload) => {
    log.debug(`TODO: Handle incoming ${payload}`);
};

/**
 * Register a listener to watch out for events. Can also be called with <code>client.on(event,listener)</code>
 * @param {Event} event The event whose listener to register
 * @param {NotificationHandler} handler A function that reacts to events
 * @returns {Client} this instance
 */
Client.prototype.registerNotificationHandler = function registerNotificationHandler(event, handler) {
    const events = Object.keys(this.eventListeners);
    if (!events.includes(event)) {
        throw new Error(`Unexpected event ${event}. Must be one of ${events}`);
    }
    this.eventListeners[event] = handler;
    return this;
};

/**
 * Register a listener to watch out for events. Can also be called with <code>client.registerListerner(event,listener)</code>
 * @param {Event} event The event whose listener to register
 * @param {NotificationHandler} handler A function that reacts to events
 * @returns {Client} this instance
 */
Client.prototype.on = function on(event, handler) { return this.registerNotificationHandler(event, handler); };

module.exports = Client;