@hms-networks/kolibri-js-client
Version:
Kolibri Consumer API client for building kolibri based applications
331 lines • 13.5 kB
JavaScript
"use strict";
/*
* Copyright 2021 HMS Industrial Networks AB
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http: //www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.BaseClient = void 0;
const url_parse_1 = __importDefault(require("url-parse"));
const subscription_1 = require("./subscription");
const kolibri_connection_1 = require("./kolibri_connection");
const kolibri_js_core_1 = require("@hms-networks/kolibri-js-core");
const https_proxy_agent_1 = require("https-proxy-agent");
const kolibri_request_error_1 = require("../error/kolibri_request_error");
class BaseClient {
constructor(config) {
this.config = config;
this.rpcId = 1;
this.tId = 1;
this.subscription = new subscription_1.Subscription();
this.writeTransactions = new Map();
this.consumerRpcs = new Map();
this.kolibriRpcs = new Map();
const urlStr = `${config.host}/${config.project}${config.path}`;
const brokerUrl = new url_parse_1.default(urlStr);
if (config.port) {
brokerUrl.set('port', config.port.toString());
}
const connectionOptions = {
url: brokerUrl,
protocol: this.getKolibriProtocol(),
tlsOptions: config.tls,
reconnectOptions: config.reconnect,
clientMessageListener: (jsonrpc) => this.clientEventDispatcher(jsonrpc)
};
if (config.proxy) {
const agent = new https_proxy_agent_1.HttpsProxyAgent(config.proxy);
connectionOptions.requestOptions = {
agent: agent
};
}
this.connection = new kolibri_connection_1.KolibriConnection(connectionOptions);
this.initBuildInConsumerRpcs();
this.registerKolibriRpc(kolibri_js_core_1.KolibriRequestMethods.GetRpcInfoRequestMethod, this.handleGetRpcInfoRequest.bind(this));
if (this.config.auth) {
this.loginParams = this.config.auth;
}
if (this.config.reconnect) {
this.initReconnectHandler();
}
this.connection.addOnDisconnectListener(async (event) => { var _a; await ((_a = this.onDisconnectListener) === null || _a === void 0 ? void 0 : _a.call(this, event)); });
}
getNextRpcId() {
return this.rpcId++;
}
getNextTid() {
return this.tId++;
}
storeLoginData(params, result) {
this.loginParams = params;
this.loginResult = result;
}
storeSubscribeData(params, result) {
this.subscribeParams = params;
this.subscribeResult = result;
}
clearSubscribeData() {
this.subscribeParams = undefined;
this.subscribeResult = undefined;
}
storeUserSubscribeData(params, result) {
this.userSubscribeParams = params;
this.userSubscribeResult = result;
}
clearUserSubscribeData() {
this.userSubscribeParams = undefined;
this.userSubscribeResult = undefined;
}
addOnUnsubscribed(listener) {
this.subscription.onUnsubscribed = listener;
}
addOnWriteListener(listener) {
this.subscription.onWrite = listener;
}
addOnUserNotifyListener(listener) {
this.subscription.onUserNotify = listener;
}
addOnErrorListener(listener) {
this.subscription.onError = listener;
}
addOnReconnectListener(listener) {
this.onReconnectListener = listener;
}
addOnDisconnectListener(listener) {
this.onDisconnectListener = listener;
}
async connect() {
return this.connection.connect();
}
async disconnect() {
this.rpcId = 1;
this.tId = 1;
this.connection.disableReconnect();
await this.connection.close();
}
async sendKolibriRpcRequest(request) {
const response = await this.connection.sendRequest(request);
if ((0, kolibri_js_core_1.isKolibriRpcSuccess)(response)) {
return response;
}
else if ((0, kolibri_js_core_1.isKolibriRpcError)(response)) {
throw (0, kolibri_request_error_1.getRequestError)(response.error.code, response.error.data);
}
else {
throw new Error('Unknown response type!');
}
}
async sendKolibriRequest(kolibriRequest) {
const response = await this.connection.sendRequest(kolibriRequest);
if ((0, kolibri_js_core_1.isJsonRpcSuccess)(response)) {
return response;
}
else if ((0, kolibri_js_core_1.isJsonRpcFailure)(response)) {
throw (0, kolibri_request_error_1.getRequestError)(response.error.code, response.error.data);
}
else {
throw new Error('Unknown response type!');
}
}
registerKolibriRpc(method, handler) {
this.kolibriRpcs.set(method, handler.bind(this));
}
async handleGetRpcInfoRequest(_args) {
return Array.from(this.kolibriRpcs.keys());
}
async handleWriteRequest(request) {
var _a, _b, _c, _d;
const writeRequest = request;
const combinedWriteResult = writeRequest.params.nodes
.map((node) => {
const nodeProperties = this.subscription.fetchSubscribedNode(node.path);
if (!nodeProperties)
return;
return { ...nodeProperties, ...node };
})
.filter((elem) => elem !== undefined);
try {
if (writeRequest.params.tid) {
this.writeTransactions.set(writeRequest.params.tid, combinedWriteResult);
}
else if (combinedWriteResult.length !== 0) {
(_b = (_a = this.subscription).onWrite) === null || _b === void 0 ? void 0 : _b.call(_a, combinedWriteResult);
}
const response = new kolibri_js_core_1.DefaultKolibriResponse(writeRequest.id, 0);
await this.connection.sendResponse(response);
}
catch (e) {
(_d = (_c = this.subscription).onError) === null || _d === void 0 ? void 0 : _d.call(_c, e);
}
}
async handleUnsubscribedRequest(request) {
var _a, _b, _c, _d;
const unsubscribedRequest = request;
this.subscription.deleteSubscribedNodes(unsubscribedRequest.params);
try {
const response = new kolibri_js_core_1.DefaultKolibriResponse(request.id, 0);
await this.connection.sendResponse(response);
(_b = (_a = this.subscription).onUnsubscribed) === null || _b === void 0 ? void 0 : _b.call(_a, unsubscribedRequest.params);
}
catch (e) {
(_d = (_c = this.subscription).onError) === null || _d === void 0 ? void 0 : _d.call(_c, e);
}
}
async handleUserNotifyRequest(request) {
var _a, _b, _c, _d;
const userNotifyRequest = request;
try {
const response = new kolibri_js_core_1.DefaultKolibriResponse(request.id, 0);
await this.connection.sendResponse(response);
(_b = (_a = this.subscription).onUserNotify) === null || _b === void 0 ? void 0 : _b.call(_a, userNotifyRequest.params);
}
catch (e) {
(_d = (_c = this.subscription).onError) === null || _d === void 0 ? void 0 : _d.call(_c, e);
}
}
async handleCommitRequest(request) {
var _a, _b, _c, _d;
const commitRequest = request;
const pendingWriteResult = this.writeTransactions
.get(commitRequest.params.tid);
if (pendingWriteResult) {
(_b = (_a = this.subscription).onWrite) === null || _b === void 0 ? void 0 : _b.call(_a, pendingWriteResult);
}
else {
// could not match commit with pending transaction. Respond to broker anyway.
}
try {
const response = new kolibri_js_core_1.DefaultKolibriResponse(commitRequest.id, 0);
await this.connection.sendResponse(response);
}
catch (e) {
(_d = (_c = this.subscription).onError) === null || _d === void 0 ? void 0 : _d.call(_c, e);
}
finally {
this.writeTransactions.delete(commitRequest.params.tid);
}
}
async clientEventDispatcher(jsonrpc) {
if (!(0, kolibri_js_core_1.isJsonRpcRequest)(jsonrpc)) {
return;
}
if ((0, kolibri_js_core_1.isKolibriRpcRequest)(jsonrpc)) {
await this.handleKolibriRpcRequest(jsonrpc);
}
else {
await this.handleConsumerRpcRequest(jsonrpc);
}
}
async handleConsumerRpcRequest(jsonrpc) {
const handlerFunction = this.consumerRpcs.get(jsonrpc.method);
if (!handlerFunction) {
const error = (0, kolibri_js_core_1.errorFromCode)(kolibri_js_core_1.errorcode.METHOD_NOT_FOUND.code);
error.data = jsonrpc.method;
const response = new class extends kolibri_js_core_1.KolibriErrorResponse {
}(jsonrpc.id, error);
await this.connection.sendResponse(response);
return;
}
await handlerFunction(jsonrpc);
}
async handleKolibriRpcRequest(jsonrpc) {
const handlerFunction = this.kolibriRpcs.get(jsonrpc.method);
let response;
if (!handlerFunction) {
const error = (0, kolibri_js_core_1.errorFromCode)(kolibri_js_core_1.errorcode.METHOD_NOT_FOUND.code);
error.data = jsonrpc.method;
response = new kolibri_js_core_1.KolibriRpcErrorResponse(jsonrpc.id, jsonrpc._server, error);
await this.connection.sendResponse(response);
return;
}
let result;
try {
result = await handlerFunction(jsonrpc.params);
}
catch (e) {
try {
if ((0, kolibri_js_core_1.isJsonRpcError)(e)) {
response = new kolibri_js_core_1.KolibriRpcErrorResponse(jsonrpc.id, jsonrpc._server, e);
}
else {
response = new kolibri_js_core_1.KolibriRpcErrorResponse(jsonrpc.id, jsonrpc._server, (0, kolibri_js_core_1.errorFromCode)(kolibri_js_core_1.errorcode.KOLIBRI_RPC_ERROR.code));
}
await this.connection.sendResponse(response);
return;
}
catch (ex) {
throw new Error('Failed to send error!');
}
}
try {
response = new kolibri_js_core_1.KolibriRpcSuccessResponse(jsonrpc.id, jsonrpc._server, result);
await this.connection.sendResponse(response);
}
catch (e) {
throw new Error('Failed to send result!');
}
}
initBuildInConsumerRpcs() {
this.consumerRpcs.set(kolibri_js_core_1.KolibriRequestMethods.WriteRequestMethod, this.handleWriteRequest.bind(this));
this.consumerRpcs.set(kolibri_js_core_1.KolibriRequestMethods.UnsubscribedRequestMethod, this.handleUnsubscribedRequest.bind(this));
this.consumerRpcs.set(kolibri_js_core_1.KolibriRequestMethods.UserNotifyRequestMethod, this.handleUserNotifyRequest.bind(this));
this.consumerRpcs.set(kolibri_js_core_1.KolibriRequestMethods.CommitRequestMethod, this.handleCommitRequest.bind(this));
}
initReconnectHandler() {
this.connection.addOnReconnectListener(async () => {
var _a, _b;
// Reconnected
try {
await this.restoreLogin();
if ((_a = this.config.reconnect) === null || _a === void 0 ? void 0 : _a.resumeSubscriptions) {
// Restoring Subscriptions
await this.restoreSubscriptions();
}
}
catch (e) {
throw new Error('Restoring client state failed!');
}
try {
await ((_b = this.onReconnectListener) === null || _b === void 0 ? void 0 : _b.call(this));
}
catch (e) {
// ignore errors on the listener
}
});
}
async restoreLogin() {
if (this.loginParams && this.loginResult) {
this.loginParams.client = this.loginResult.client;
await this.login(this.loginParams);
}
else if (this.loginParams) {
await this.login(this.loginParams);
}
else {
throw new Error('Unknown client state!');
}
}
async restoreSubscriptions() {
if (this.subscribeParams && this.subscribeResult) {
await this.subscribe(this.subscribeParams);
}
if (this.userSubscribeParams && this.userSubscribeResult) {
await this.userSubscribe(this.userSubscribeParams);
}
}
}
exports.BaseClient = BaseClient;
//# sourceMappingURL=base_client.js.map