Classify Http & Ws providers (#120)
* Class-ify HttpProvider * Class-ify WsProvider (tests still WIP) * Rework tests * rx-api usage
This commit is contained in:
@@ -4,13 +4,25 @@
|
|||||||
|
|
||||||
import { TEST_HTTP_URL } from '../../test/mockHttp';
|
import { TEST_HTTP_URL } from '../../test/mockHttp';
|
||||||
|
|
||||||
import createHttp from './index';
|
import Http from './index';
|
||||||
|
|
||||||
describe('Http', () => {
|
describe('Http', () => {
|
||||||
let http;
|
let http;
|
||||||
|
|
||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
http = createHttp(TEST_HTTP_URL);
|
http = new Http(TEST_HTTP_URL);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('requires an http:// prefixed endpoint', () => {
|
||||||
|
expect(
|
||||||
|
() => new Http('ws://')
|
||||||
|
).toThrow(/with 'http/);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('allows https:// endpoints', () => {
|
||||||
|
expect(
|
||||||
|
() => new Http('https://')
|
||||||
|
).not.toThrow();
|
||||||
});
|
});
|
||||||
|
|
||||||
it('always returns isConnected true', () => {
|
it('always returns isConnected true', () => {
|
||||||
|
|||||||
@@ -2,29 +2,62 @@
|
|||||||
// This software may be modified and distributed under the terms
|
// This software may be modified and distributed under the terms
|
||||||
// of the ISC license. See the LICENSE file for details.
|
// of the ISC license. See the LICENSE file for details.
|
||||||
|
|
||||||
|
import { Logger } from '@polkadot/util/types';
|
||||||
|
import { RpcCoder } from '../coder/json/types';
|
||||||
import { ProviderInterface, ProviderInterface$Callback, ProviderInterface$Emitted, ProviderInterface$EmitCb } from '../types';
|
import { ProviderInterface, ProviderInterface$Callback, ProviderInterface$Emitted, ProviderInterface$EmitCb } from '../types';
|
||||||
|
|
||||||
import './polyfill';
|
import './polyfill';
|
||||||
|
|
||||||
import on from './on';
|
import assert from '@polkadot/util/assert';
|
||||||
import send from './send';
|
import logger from '@polkadot/util/logger';
|
||||||
import subscribe from './subscribe';
|
|
||||||
import state from './state';
|
|
||||||
import unsubscribe from './unsubscribe';
|
|
||||||
|
|
||||||
export default function httpProvider (endpoint: string): ProviderInterface {
|
import coder from '../coder/json';
|
||||||
const self = state(endpoint);
|
|
||||||
|
|
||||||
return {
|
export default class HttpProvider implements ProviderInterface {
|
||||||
isConnected: (): boolean =>
|
private coder: RpcCoder;
|
||||||
true,
|
private endpoint: string;
|
||||||
on: (type: ProviderInterface$Emitted, sub: ProviderInterface$EmitCb): void =>
|
private l: Logger;
|
||||||
on(self, type, sub),
|
|
||||||
send: (method: string, params: Array<any>): Promise<any> =>
|
constructor (endpoint: string) {
|
||||||
send(self, method, params),
|
assert(/^(https|http):\/\//.test(endpoint), `Endpoint should start with 'http://', received '${endpoint}'`);
|
||||||
subscribe: (method: string, params: Array<any>, cb: ProviderInterface$Callback): Promise<number> =>
|
|
||||||
subscribe(self, method, params, cb),
|
this.coder = coder();
|
||||||
unsubscribe: (method: string, id: number): Promise<boolean> =>
|
this.endpoint = endpoint;
|
||||||
unsubscribe(self, method, id)
|
this.l = logger('api-http');
|
||||||
};
|
}
|
||||||
|
|
||||||
|
isConnected (): boolean {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
on (type: ProviderInterface$Emitted, sub: ProviderInterface$EmitCb): void {
|
||||||
|
this.l.error(`Provider does not have 'on' emitters for type '${type}'`);
|
||||||
|
}
|
||||||
|
|
||||||
|
async send (method: string, params: Array<any>): Promise<any> {
|
||||||
|
const body = this.coder.encodeJson(method, params);
|
||||||
|
const response = await fetch(this.endpoint, {
|
||||||
|
body,
|
||||||
|
headers: {
|
||||||
|
'Accept': 'application/json',
|
||||||
|
'Content-Length': `${body.length}`,
|
||||||
|
'Content-Type': 'application/json'
|
||||||
|
},
|
||||||
|
method: 'POST'
|
||||||
|
});
|
||||||
|
|
||||||
|
assert(response.ok, `[${response.status}]: ${response.statusText}`);
|
||||||
|
|
||||||
|
const result = await response.json();
|
||||||
|
|
||||||
|
return this.coder.decodeResponse(result);
|
||||||
|
}
|
||||||
|
|
||||||
|
async subscribe (method: string, params: Array<any>, cb: ProviderInterface$Callback): Promise<number> {
|
||||||
|
throw new Error('Subscriptions has not been implemented');
|
||||||
|
}
|
||||||
|
|
||||||
|
async unsubscribe (method: string, id: number): Promise<boolean> {
|
||||||
|
throw new Error('Subscriptions has not been implemented');
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,10 +0,0 @@
|
|||||||
// Copyright 2017-2018 @polkadot/api-provider authors & contributors
|
|
||||||
// This software may be modified and distributed under the terms
|
|
||||||
// of the ISC license. See the LICENSE file for details.
|
|
||||||
|
|
||||||
import { ProviderInterface$Emitted, ProviderInterface$EmitCb } from '../types';
|
|
||||||
import { HttpState } from './types';
|
|
||||||
|
|
||||||
export default function on (self: HttpState, type: ProviderInterface$Emitted, sub: ProviderInterface$EmitCb): void {
|
|
||||||
self.l.error(`Provider does not have 'on' emitters for type '${type}'`);
|
|
||||||
}
|
|
||||||
@@ -4,14 +4,14 @@
|
|||||||
|
|
||||||
import { mockHttp, TEST_HTTP_URL } from '../../test/mockHttp';
|
import { mockHttp, TEST_HTTP_URL } from '../../test/mockHttp';
|
||||||
|
|
||||||
import createHttp from './index';
|
import Http from './index';
|
||||||
|
|
||||||
describe('send', () => {
|
describe('send', () => {
|
||||||
let http;
|
let http;
|
||||||
let mock;
|
let mock;
|
||||||
|
|
||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
http = createHttp(TEST_HTTP_URL);
|
http = new Http(TEST_HTTP_URL);
|
||||||
});
|
});
|
||||||
|
|
||||||
afterEach(() => {
|
afterEach(() => {
|
||||||
|
|||||||
@@ -1,26 +0,0 @@
|
|||||||
// Copyright 2017-2018 @polkadot/api-provider authors & contributors
|
|
||||||
// This software may be modified and distributed under the terms
|
|
||||||
// of the ISC license. See the LICENSE file for details.
|
|
||||||
|
|
||||||
import { HttpState } from './types';
|
|
||||||
|
|
||||||
import assert from '@polkadot/util/assert';
|
|
||||||
|
|
||||||
export default async function send ({ coder, endpoint }: HttpState, method: string, params: Array<any>): Promise<any> {
|
|
||||||
const body = coder.encodeJson(method, params);
|
|
||||||
const response = await fetch(endpoint, {
|
|
||||||
body,
|
|
||||||
headers: {
|
|
||||||
'Accept': 'application/json',
|
|
||||||
'Content-Length': `${body.length}`,
|
|
||||||
'Content-Type': 'application/json'
|
|
||||||
},
|
|
||||||
method: 'POST'
|
|
||||||
});
|
|
||||||
|
|
||||||
assert(response.ok, `[${response.status}]: ${response.statusText}`);
|
|
||||||
|
|
||||||
const result = await response.json();
|
|
||||||
|
|
||||||
return coder.decodeResponse(result);
|
|
||||||
}
|
|
||||||
@@ -1,19 +0,0 @@
|
|||||||
// Copyright 2017-2018 @polkadot/api-provider authors & contributors
|
|
||||||
// This software may be modified and distributed under the terms
|
|
||||||
// of the ISC license. See the LICENSE file for details.
|
|
||||||
|
|
||||||
import state from './state';
|
|
||||||
|
|
||||||
describe('state', () => {
|
|
||||||
it('requires an http:// prefixed endpoint', () => {
|
|
||||||
expect(
|
|
||||||
() => state('ws://')
|
|
||||||
).toThrow(/with 'http/);
|
|
||||||
});
|
|
||||||
|
|
||||||
it('allows https:// endpoints', () => {
|
|
||||||
expect(
|
|
||||||
state('https://')
|
|
||||||
).toBeDefined();
|
|
||||||
});
|
|
||||||
});
|
|
||||||
@@ -1,22 +0,0 @@
|
|||||||
// Copyright 2017-2018 @polkadot/api-provider authors & contributors
|
|
||||||
// This software may be modified and distributed under the terms
|
|
||||||
// of the ISC license. See the LICENSE file for details.
|
|
||||||
|
|
||||||
import { HttpState } from './types';
|
|
||||||
|
|
||||||
import assert from '@polkadot/util/assert';
|
|
||||||
import logger from '@polkadot/util/logger';
|
|
||||||
|
|
||||||
import coder from '../coder/json';
|
|
||||||
|
|
||||||
const l = logger('api-http');
|
|
||||||
|
|
||||||
export default function state (endpoint: string): HttpState {
|
|
||||||
assert(/^(https|http):\/\//.test(endpoint), `Endpoint should start with 'http://', received '${endpoint}'`);
|
|
||||||
|
|
||||||
return {
|
|
||||||
coder: coder(),
|
|
||||||
endpoint,
|
|
||||||
l
|
|
||||||
};
|
|
||||||
}
|
|
||||||
@@ -1,10 +0,0 @@
|
|||||||
// Copyright 2017-2018 @polkadot/api-provider authors & contributors
|
|
||||||
// This software may be modified and distributed under the terms
|
|
||||||
// of the ISC license. See the LICENSE file for details.
|
|
||||||
|
|
||||||
import { ProviderInterface$Callback } from '../types';
|
|
||||||
import { HttpState } from './types';
|
|
||||||
|
|
||||||
export default async function subscribe (self: HttpState, method: string, params: Array<any>, cb: ProviderInterface$Callback): Promise<number> {
|
|
||||||
throw new Error('Subscriptions has not been implemented');
|
|
||||||
}
|
|
||||||
@@ -1,9 +0,0 @@
|
|||||||
// Copyright 2017-2018 @polkadot/api-provider authors & contributors
|
|
||||||
// This software may be modified and distributed under the terms
|
|
||||||
// of the ISC license. See the LICENSE file for details.
|
|
||||||
|
|
||||||
import { HttpState } from './types';
|
|
||||||
|
|
||||||
export default async function unsubscribe (self: HttpState, method: string, id: number): Promise<boolean> {
|
|
||||||
throw new Error('Subscriptions has not been implemented');
|
|
||||||
}
|
|
||||||
Vendored
+1
-1
@@ -41,7 +41,7 @@ export type ProviderInterface$Emitted = 'connected' | 'disconnected';
|
|||||||
|
|
||||||
export type ProviderInterface$EmitCb = (value?: any) => any;
|
export type ProviderInterface$EmitCb = (value?: any) => any;
|
||||||
|
|
||||||
export type ProviderInterface = {
|
export interface ProviderInterface {
|
||||||
isConnected (): boolean,
|
isConnected (): boolean,
|
||||||
on (type: ProviderInterface$Emitted, sub: ProviderInterface$EmitCb): void,
|
on (type: ProviderInterface$Emitted, sub: ProviderInterface$EmitCb): void,
|
||||||
send (method: string, params: Array<any>): Promise<any>,
|
send (method: string, params: Array<any>): Promise<any>,
|
||||||
|
|||||||
@@ -4,10 +4,9 @@
|
|||||||
|
|
||||||
import { mockWs, TEST_WS_URL } from '../../test/mockWs';
|
import { mockWs, TEST_WS_URL } from '../../test/mockWs';
|
||||||
|
|
||||||
import createState from './state';
|
import Ws from './index';
|
||||||
import connect from './connect';
|
|
||||||
|
|
||||||
describe('onError', () => {
|
describe('onConnect', () => {
|
||||||
let mock;
|
let mock;
|
||||||
|
|
||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
@@ -22,13 +21,13 @@ describe('onError', () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
it('sets up the on* handlers', () => {
|
it('sets up the on* handlers', () => {
|
||||||
const state = createState(TEST_WS_URL, false);
|
const ws = new Ws(TEST_WS_URL, false);
|
||||||
|
|
||||||
connect(state);
|
ws.connect();
|
||||||
|
|
||||||
expect(state.websocket.onclose[0]).toBeDefined();
|
expect(ws.websocket.onclose[0]).toBeDefined();
|
||||||
expect(state.websocket.onerror[0]).toBeDefined();
|
expect(ws.websocket.onerror[0]).toBeDefined();
|
||||||
expect(state.websocket.onmessage[0]).toBeDefined();
|
expect(ws.websocket.onmessage[0]).toBeDefined();
|
||||||
expect(state.websocket.onopen[0]).toBeDefined();
|
expect(ws.websocket.onopen[0]).toBeDefined();
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -1,23 +0,0 @@
|
|||||||
// Copyright 2017-2018 @polkadot/api-provider authors & contributors
|
|
||||||
// This software may be modified and distributed under the terms
|
|
||||||
// of the ISC license. See the LICENSE file for details.
|
|
||||||
|
|
||||||
import { WsState } from './types';
|
|
||||||
|
|
||||||
import onClose from './onClose';
|
|
||||||
import onError from './onError';
|
|
||||||
import onMessage from './onMessage';
|
|
||||||
import onOpen from './onOpen';
|
|
||||||
|
|
||||||
export default function connect (self: WsState): void {
|
|
||||||
try {
|
|
||||||
self.websocket = new WebSocket(self.endpoint);
|
|
||||||
|
|
||||||
self.websocket.onclose = onClose(self);
|
|
||||||
self.websocket.onerror = onError(self);
|
|
||||||
self.websocket.onmessage = onMessage(self);
|
|
||||||
self.websocket.onopen = onOpen(self);
|
|
||||||
} catch (error) {
|
|
||||||
self.l.error(error);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -4,14 +4,14 @@
|
|||||||
|
|
||||||
import { mockWs, TEST_WS_URL } from '../../test/mockWs';
|
import { mockWs, TEST_WS_URL } from '../../test/mockWs';
|
||||||
|
|
||||||
import create from './index';
|
import Ws from './index';
|
||||||
|
|
||||||
let ws;
|
let ws;
|
||||||
let mock;
|
let mock;
|
||||||
|
|
||||||
function createWs (requests, autoConnect) {
|
function createWs (requests, autoConnect) {
|
||||||
mock = mockWs(requests);
|
mock = mockWs(requests);
|
||||||
ws = create(TEST_WS_URL, autoConnect);
|
ws = new Ws(TEST_WS_URL, autoConnect);
|
||||||
|
|
||||||
return ws;
|
return ws;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,40 +2,233 @@
|
|||||||
// This software may be modified and distributed under the terms
|
// This software may be modified and distributed under the terms
|
||||||
// of the ISC license. See the LICENSE file for details.
|
// of the ISC license. See the LICENSE file for details.
|
||||||
|
|
||||||
import { ProviderInterface, ProviderInterface$Callback, ProviderInterface$Emitted, ProviderInterface$EmitCb } from '../types';
|
import { Logger } from '@polkadot/util/types';
|
||||||
|
import { RpcCoder } from '../coder/json/types';
|
||||||
|
import { JsonRpcResponse, ProviderInterface, ProviderInterface$Callback, ProviderInterface$Emitted, ProviderInterface$EmitCb } from '../types';
|
||||||
|
|
||||||
import './polyfill';
|
import './polyfill';
|
||||||
|
|
||||||
import connect from './connect';
|
import E3 from 'eventemitter3';
|
||||||
import on from './on';
|
import assert from '@polkadot/util/assert';
|
||||||
import send from './send';
|
import isNull from '@polkadot/util/is/null';
|
||||||
import state from './state';
|
import isUndefined from '@polkadot/util/is/undefined';
|
||||||
import subscribe from './subscribe';
|
import logger from '@polkadot/util/logger';
|
||||||
import unsubscribe from './unsubscribe';
|
|
||||||
|
|
||||||
type WSProviderInterface = ProviderInterface & {
|
import coder from '../coder/json';
|
||||||
connect (): void
|
|
||||||
|
type WsState$Awaiting = {
|
||||||
|
callback: ProviderInterface$Callback,
|
||||||
|
subscription?: ProviderInterface$Callback
|
||||||
};
|
};
|
||||||
|
|
||||||
export default function wsProvider (endpoint: string, autoConnect: boolean = true): WSProviderInterface {
|
type WsState$Subscription = {
|
||||||
const self = state(endpoint, autoConnect);
|
callback: ProviderInterface$Callback
|
||||||
|
};
|
||||||
|
|
||||||
if (autoConnect) {
|
interface WSProviderInterface extends ProviderInterface {
|
||||||
connect(self);
|
connect (): void;
|
||||||
|
}
|
||||||
|
|
||||||
|
export default class WsProvider extends E3.EventEmitter implements WSProviderInterface {
|
||||||
|
private autoConnect: boolean;
|
||||||
|
private coder: RpcCoder;
|
||||||
|
private endpoint: string;
|
||||||
|
private handlers: {
|
||||||
|
[index: number]: WsState$Awaiting
|
||||||
|
};
|
||||||
|
private _isConnected: boolean;
|
||||||
|
private l: Logger;
|
||||||
|
private queued: {
|
||||||
|
[index: string]: string
|
||||||
|
};
|
||||||
|
private subscriptions: {
|
||||||
|
[index: number]: WsState$Subscription
|
||||||
|
};
|
||||||
|
private websocket: WebSocket | null;
|
||||||
|
|
||||||
|
constructor (endpoint: string, autoConnect: boolean = true) {
|
||||||
|
super();
|
||||||
|
|
||||||
|
assert(/^(wss|ws):\/\//.test(endpoint), `Endpoint should start with 'ws://', received '${endpoint}'`);
|
||||||
|
|
||||||
|
this.autoConnect = autoConnect;
|
||||||
|
this.coder = coder();
|
||||||
|
this.endpoint = endpoint;
|
||||||
|
this._isConnected = false;
|
||||||
|
this.handlers = {};
|
||||||
|
this.l = logger('api-ws');
|
||||||
|
this.queued = {};
|
||||||
|
this.subscriptions = {};
|
||||||
|
this.websocket = null;
|
||||||
|
|
||||||
|
if (autoConnect) {
|
||||||
|
this.connect();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return {
|
connect (): void {
|
||||||
connect: (): void =>
|
try {
|
||||||
connect(self),
|
this.websocket = new WebSocket(this.endpoint);
|
||||||
isConnected: (): boolean =>
|
|
||||||
self.isConnected,
|
this.websocket.onclose = this.onSocketClose;
|
||||||
on: (type: ProviderInterface$Emitted, sub: ProviderInterface$EmitCb): void =>
|
this.websocket.onerror = this.onSocketError;
|
||||||
on(self, type, sub),
|
this.websocket.onmessage = this.onSocketMessage;
|
||||||
send: (method: string, params: Array<any>): Promise<any> =>
|
this.websocket.onopen = this.onSocketOpen;
|
||||||
send(self, method, params),
|
} catch (error) {
|
||||||
subscribe: (method: string, params: Array<any>, cb: ProviderInterface$Callback): Promise<number> =>
|
this.l.error(error);
|
||||||
subscribe(self, method, params, cb),
|
}
|
||||||
unsubscribe: (method: string, id: number): Promise<boolean> =>
|
}
|
||||||
unsubscribe(self, method, id)
|
|
||||||
};
|
isConnected (): boolean {
|
||||||
|
return this._isConnected;
|
||||||
|
}
|
||||||
|
|
||||||
|
on (type: ProviderInterface$Emitted, sub: ProviderInterface$EmitCb): this {
|
||||||
|
return super.on(type, sub);
|
||||||
|
}
|
||||||
|
|
||||||
|
async send (method: string, params: Array<any>, subscription?: ProviderInterface$Callback): Promise<any> {
|
||||||
|
return new Promise((resolve, reject): void => {
|
||||||
|
try {
|
||||||
|
const json = this.coder.encodeJson(method, params);
|
||||||
|
const id = this.coder.getId();
|
||||||
|
const callback = (error: Error | null, result: any) => {
|
||||||
|
if (error) {
|
||||||
|
reject(error);
|
||||||
|
} else {
|
||||||
|
resolve(result);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
this.l.debug(() => ['calling', method, params, json, !!subscription]);
|
||||||
|
|
||||||
|
this.handlers[id] = {
|
||||||
|
callback,
|
||||||
|
subscription
|
||||||
|
};
|
||||||
|
|
||||||
|
if (this.isConnected && !isNull(this.websocket)) {
|
||||||
|
this.websocket.send(json);
|
||||||
|
} else {
|
||||||
|
this.queued[id] = json;
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
reject(error);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
async subscribe (method: string, params: Array<any>, subscription: ProviderInterface$Callback): Promise<number> {
|
||||||
|
const id = await this.send(method, params, subscription);
|
||||||
|
|
||||||
|
return id as number;
|
||||||
|
}
|
||||||
|
|
||||||
|
async unsubscribe (method: string, id: number): Promise<boolean> {
|
||||||
|
assert(!isUndefined(this.subscriptions[id]), `Unable to find active subscription=${id}`);
|
||||||
|
|
||||||
|
delete this.subscriptions[id];
|
||||||
|
|
||||||
|
const result = await this.send(method, [id]);
|
||||||
|
|
||||||
|
return result as boolean;
|
||||||
|
}
|
||||||
|
|
||||||
|
private onSocketClose = (): void => {
|
||||||
|
this.l.debug(() => ['disconnected from', this.endpoint]);
|
||||||
|
|
||||||
|
this._isConnected = false;
|
||||||
|
this.emit('disconnected');
|
||||||
|
|
||||||
|
if (this.autoConnect) {
|
||||||
|
setTimeout(() => {
|
||||||
|
this.connect();
|
||||||
|
}, 1000);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private onSocketError = (error: Event): void => {
|
||||||
|
this.l.error(error);
|
||||||
|
}
|
||||||
|
|
||||||
|
private onSocketMessage = (message: MessageEvent): void => {
|
||||||
|
this.l.debug(() => ['received', message.data]);
|
||||||
|
|
||||||
|
const response: JsonRpcResponse = JSON.parse(message.data as string);
|
||||||
|
|
||||||
|
return isUndefined(response.method)
|
||||||
|
? this.onSocketMessageResult(response)
|
||||||
|
: this.onSocketMessageSubscribe(response);
|
||||||
|
}
|
||||||
|
|
||||||
|
private onSocketMessageResult = (response: JsonRpcResponse): void => {
|
||||||
|
this.l.debug(() => ['handling: response =', response, 'id =', response.id]);
|
||||||
|
|
||||||
|
const handler = this.handlers[response.id];
|
||||||
|
|
||||||
|
if (!handler) {
|
||||||
|
this.l.error(`Unable to find handler for id=${response.id}`);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
const { subscription } = this.handlers[response.id];
|
||||||
|
const result = this.coder.decodeResponse(response);
|
||||||
|
|
||||||
|
if (subscription) {
|
||||||
|
this.subscriptions[result as number] = {
|
||||||
|
callback: subscription
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
handler.callback(null, result);
|
||||||
|
} catch (error) {
|
||||||
|
handler.callback(error, undefined);
|
||||||
|
}
|
||||||
|
|
||||||
|
delete this.handlers[response.id];
|
||||||
|
}
|
||||||
|
|
||||||
|
private onSocketMessageSubscribe = (response: JsonRpcResponse): void => {
|
||||||
|
this.l.debug(() => ['handling: response =', response, 'subscription =', response.params.subscription]);
|
||||||
|
|
||||||
|
const handler = this.subscriptions[response.params.subscription];
|
||||||
|
|
||||||
|
if (!handler) {
|
||||||
|
this.l.error(`Unable to find handler for subscription=${response.params.subscription}`);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
const result = this.coder.decodeResponse(response);
|
||||||
|
|
||||||
|
handler.callback(null, result);
|
||||||
|
} catch (error) {
|
||||||
|
handler.callback(error, undefined);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private onSocketOpen = (): boolean => {
|
||||||
|
assert(!isNull(this.websocket), 'WebSocket cannot be null in onOpen');
|
||||||
|
|
||||||
|
this.l.debug(() => ['connected to', this.endpoint]);
|
||||||
|
|
||||||
|
this._isConnected = true;
|
||||||
|
this.emit('connected');
|
||||||
|
|
||||||
|
Object.keys(this.queued).forEach((id) => {
|
||||||
|
try {
|
||||||
|
// @ts-ignore checked above
|
||||||
|
this.websocket.send(
|
||||||
|
this.queued[id]
|
||||||
|
);
|
||||||
|
|
||||||
|
delete this.queued[id];
|
||||||
|
} catch (error) {
|
||||||
|
this.l.error(error);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,10 +0,0 @@
|
|||||||
// Copyright 2017-2018 @polkadot/api-provider authors & contributors
|
|
||||||
// This software may be modified and distributed under the terms
|
|
||||||
// of the ISC license. See the LICENSE file for details.
|
|
||||||
|
|
||||||
import { ProviderInterface$Emitted, ProviderInterface$EmitCb } from '../types';
|
|
||||||
import { WsState } from './types';
|
|
||||||
|
|
||||||
export default function on (self: WsState, type: ProviderInterface$Emitted, sub: ProviderInterface$EmitCb): void {
|
|
||||||
self.emitter.on(type, sub);
|
|
||||||
}
|
|
||||||
@@ -1,42 +0,0 @@
|
|||||||
// Copyright 2017-2018 @polkadot/api-provider authors & contributors
|
|
||||||
// This software may be modified and distributed under the terms
|
|
||||||
// of the ISC license. See the LICENSE file for details.
|
|
||||||
|
|
||||||
import { TEST_WS_URL } from '../../test/mockWs';
|
|
||||||
|
|
||||||
import createState from './state';
|
|
||||||
|
|
||||||
describe('onClose', () => {
|
|
||||||
let mockConnect;
|
|
||||||
|
|
||||||
beforeEach(() => {
|
|
||||||
mockConnect = jest.fn();
|
|
||||||
|
|
||||||
jest.mock('./connect', () => mockConnect);
|
|
||||||
jest.useFakeTimers();
|
|
||||||
});
|
|
||||||
|
|
||||||
afterEach(() => {
|
|
||||||
jest.useRealTimers();
|
|
||||||
});
|
|
||||||
|
|
||||||
it('reconnects after delay', () => {
|
|
||||||
require('./onClose').default(createState(TEST_WS_URL, true))();
|
|
||||||
|
|
||||||
expect(mockConnect).not.toHaveBeenCalled();
|
|
||||||
|
|
||||||
jest.runAllTimers();
|
|
||||||
|
|
||||||
expect(mockConnect).toHaveBeenCalled();
|
|
||||||
});
|
|
||||||
|
|
||||||
it('does not reconnect when autoConnect false', () => {
|
|
||||||
require('./onClose').default(createState(TEST_WS_URL, false))();
|
|
||||||
|
|
||||||
expect(mockConnect).not.toHaveBeenCalled();
|
|
||||||
|
|
||||||
jest.runAllTimers();
|
|
||||||
|
|
||||||
expect(mockConnect).not.toHaveBeenCalled();
|
|
||||||
});
|
|
||||||
});
|
|
||||||
@@ -1,22 +0,0 @@
|
|||||||
// Copyright 2017-2018 @polkadot/api-provider authors & contributors
|
|
||||||
// This software may be modified and distributed under the terms
|
|
||||||
// of the ISC license. See the LICENSE file for details.
|
|
||||||
|
|
||||||
import { WsState } from './types';
|
|
||||||
|
|
||||||
import connect from './connect';
|
|
||||||
|
|
||||||
export default function onClose (self: WsState): () => void {
|
|
||||||
return (): void => {
|
|
||||||
self.l.debug(() => ['disconnected from', self.endpoint]);
|
|
||||||
|
|
||||||
self.isConnected = false;
|
|
||||||
self.emitter.emit('disconnected');
|
|
||||||
|
|
||||||
if (self.autoConnect) {
|
|
||||||
setTimeout(() => {
|
|
||||||
connect(self);
|
|
||||||
}, 1000);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
}
|
|
||||||
@@ -2,15 +2,14 @@
|
|||||||
// This software may be modified and distributed under the terms
|
// This software may be modified and distributed under the terms
|
||||||
// of the ISC license. See the LICENSE file for details.
|
// of the ISC license. See the LICENSE file for details.
|
||||||
|
|
||||||
import onError from './onError';
|
import Ws from './index';
|
||||||
import createState from './state';
|
|
||||||
|
|
||||||
describe('onError', () => {
|
describe('onError', () => {
|
||||||
let state;
|
let ws;
|
||||||
let errorSpy;
|
let errorSpy;
|
||||||
|
|
||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
state = createState('ws://127.0.0.1:1234', false);
|
ws = new Ws('ws://127.0.0.1:1234', false);
|
||||||
errorSpy = jest.spyOn(console, 'error');
|
errorSpy = jest.spyOn(console, 'error');
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -19,7 +18,7 @@ describe('onError', () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
it('logs the error', () => {
|
it('logs the error', () => {
|
||||||
onError(state)('test error');
|
ws.onSocketError('test error');
|
||||||
|
|
||||||
expect(errorSpy).toHaveBeenCalledWith(
|
expect(errorSpy).toHaveBeenCalledWith(
|
||||||
expect.anything(), expect.anything(), 'test error'
|
expect.anything(), expect.anything(), 'test error'
|
||||||
|
|||||||
@@ -1,11 +0,0 @@
|
|||||||
// Copyright 2017-2018 @polkadot/api-provider authors & contributors
|
|
||||||
// This software may be modified and distributed under the terms
|
|
||||||
// of the ISC license. See the LICENSE file for details.
|
|
||||||
|
|
||||||
import { WsState } from './types';
|
|
||||||
|
|
||||||
export default function onError (self: WsState): (event: Event) => void {
|
|
||||||
return (error: Event): void => {
|
|
||||||
self.l.error(error);
|
|
||||||
};
|
|
||||||
}
|
|
||||||
@@ -1,23 +0,0 @@
|
|||||||
// Copyright 2017-2018 @polkadot/api-provider authors & contributors
|
|
||||||
// This software may be modified and distributed under the terms
|
|
||||||
// of the ISC license. See the LICENSE file for details.
|
|
||||||
|
|
||||||
import { JsonRpcResponse } from '../types';
|
|
||||||
import { WsState } from './types';
|
|
||||||
|
|
||||||
import isUndefined from '@polkadot/util/is/undefined';
|
|
||||||
|
|
||||||
import onMessageResult from './onMessageResult';
|
|
||||||
import onMessageSubscribe from './onMessageSubscribe';
|
|
||||||
|
|
||||||
export default function onMessage (self: WsState): (event: MessageEvent) => void {
|
|
||||||
return (message: MessageEvent): void => {
|
|
||||||
self.l.debug(() => ['received', message.data]);
|
|
||||||
|
|
||||||
const response: JsonRpcResponse = JSON.parse(message.data as string);
|
|
||||||
|
|
||||||
return isUndefined(response.method)
|
|
||||||
? onMessageResult(self, response)
|
|
||||||
: onMessageSubscribe(self, response);
|
|
||||||
};
|
|
||||||
}
|
|
||||||
@@ -2,15 +2,14 @@
|
|||||||
// This software may be modified and distributed under the terms
|
// This software may be modified and distributed under the terms
|
||||||
// of the ISC license. See the LICENSE file for details.
|
// of the ISC license. See the LICENSE file for details.
|
||||||
|
|
||||||
import onMessage from './onMessage';
|
import Ws from './index';
|
||||||
import createState from './state';
|
|
||||||
|
|
||||||
describe('onMessageResult', () => {
|
describe('onMessageResult', () => {
|
||||||
let state;
|
let ws;
|
||||||
let errorSpy;
|
let errorSpy;
|
||||||
|
|
||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
state = createState('ws://127.0.0.1:1234', false);
|
ws = new Ws('ws://127.0.0.1:1234', false);
|
||||||
errorSpy = jest.spyOn(console, 'error');
|
errorSpy = jest.spyOn(console, 'error');
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -19,7 +18,7 @@ describe('onMessageResult', () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
it('fails with log when handler not found', () => {
|
it('fails with log when handler not found', () => {
|
||||||
onMessage(state)({ data: '{"id":2}' });
|
ws.onSocketMessage({ data: '{"id":2}' });
|
||||||
|
|
||||||
expect(errorSpy).toHaveBeenCalledWith(
|
expect(errorSpy).toHaveBeenCalledWith(
|
||||||
expect.anything(), expect.anything(), 'Unable to find handler for id=2'
|
expect.anything(), expect.anything(), 'Unable to find handler for id=2'
|
||||||
@@ -27,13 +26,13 @@ describe('onMessageResult', () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
it('calls the handler when found', (done) => {
|
it('calls the handler when found', (done) => {
|
||||||
state.handlers[5] = {
|
ws.handlers[5] = {
|
||||||
callback: (_, result) => {
|
callback: (_, result) => {
|
||||||
expect(result).toEqual('test');
|
expect(result).toEqual('test');
|
||||||
done();
|
done();
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
onMessage(state)({ data: '{"jsonrpc":"2.0","id":5,"result":"test"}' });
|
ws.onSocketMessage({ data: '{"jsonrpc":"2.0","id":5,"result":"test"}' });
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -1,34 +0,0 @@
|
|||||||
// Copyright 2017-2018 @polkadot/api-provider authors & contributors
|
|
||||||
// This software may be modified and distributed under the terms
|
|
||||||
// of the ISC license. See the LICENSE file for details.
|
|
||||||
|
|
||||||
import { JsonRpcResponse } from '../types';
|
|
||||||
import { WsState } from './types';
|
|
||||||
|
|
||||||
export default function onMessageResult (self: WsState, response: JsonRpcResponse): void {
|
|
||||||
self.l.debug(() => ['handling: response =', response, 'id =', response.id]);
|
|
||||||
|
|
||||||
const handler = self.handlers[response.id];
|
|
||||||
|
|
||||||
if (!handler) {
|
|
||||||
self.l.error(`Unable to find handler for id=${response.id}`);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
try {
|
|
||||||
const { subscription } = self.handlers[response.id];
|
|
||||||
const result = self.coder.decodeResponse(response);
|
|
||||||
|
|
||||||
if (subscription) {
|
|
||||||
self.subscriptions[result as number] = {
|
|
||||||
callback: subscription
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
handler.callback(null, result);
|
|
||||||
} catch (error) {
|
|
||||||
handler.callback(error, undefined);
|
|
||||||
}
|
|
||||||
|
|
||||||
delete self.handlers[response.id];
|
|
||||||
}
|
|
||||||
@@ -2,15 +2,14 @@
|
|||||||
// This software may be modified and distributed under the terms
|
// This software may be modified and distributed under the terms
|
||||||
// of the ISC license. See the LICENSE file for details.
|
// of the ISC license. See the LICENSE file for details.
|
||||||
|
|
||||||
import onMessage from './onMessage';
|
import Ws from './index';
|
||||||
import createState from './state';
|
|
||||||
|
|
||||||
describe('onMessageSubscribe', () => {
|
describe('onMessageSubscribe', () => {
|
||||||
let state;
|
let ws;
|
||||||
let errorSpy;
|
let errorSpy;
|
||||||
|
|
||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
state = createState('ws://127.0.0.1:1234', false);
|
ws = new Ws('ws://127.0.0.1:1234', false);
|
||||||
errorSpy = jest.spyOn(console, 'error');
|
errorSpy = jest.spyOn(console, 'error');
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -19,7 +18,7 @@ describe('onMessageSubscribe', () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
it('fails with log when handler not found', () => {
|
it('fails with log when handler not found', () => {
|
||||||
onMessage(state)({ data: '{"method":"test","params":{"subscription":2}}' });
|
ws.onSocketMessage({ data: '{"method":"test","params":{"subscription":2}}' });
|
||||||
|
|
||||||
expect(errorSpy).toHaveBeenCalledWith(
|
expect(errorSpy).toHaveBeenCalledWith(
|
||||||
expect.anything(), expect.anything(), 'Unable to find handler for subscription=2'
|
expect.anything(), expect.anything(), 'Unable to find handler for subscription=2'
|
||||||
@@ -27,7 +26,7 @@ describe('onMessageSubscribe', () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
it('calls the subscriber with data', (done) => {
|
it('calls the subscriber with data', (done) => {
|
||||||
state.handlers[11] = {
|
ws.handlers[11] = {
|
||||||
callback: (_, id) => {},
|
callback: (_, id) => {},
|
||||||
subscription: (_, result) => {
|
subscription: (_, result) => {
|
||||||
expect(result).toEqual('test');
|
expect(result).toEqual('test');
|
||||||
@@ -35,12 +34,12 @@ describe('onMessageSubscribe', () => {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
onMessage(state)({ data: '{"jsonrpc":"2.0","id":11,"result":22}' });
|
ws.onSocketMessage({ data: '{"jsonrpc":"2.0","id":11,"result":22}' });
|
||||||
onMessage(state)({ data: '{"jsonrpc":"2.0","method":"test","params":{"subscription":22,"result":"test"}}' });
|
ws.onSocketMessage({ data: '{"jsonrpc":"2.0","method":"test","params":{"subscription":22,"result":"test"}}' });
|
||||||
});
|
});
|
||||||
|
|
||||||
it('calls the subscriber with error', (done) => {
|
it('calls the subscriber with error', (done) => {
|
||||||
state.handlers[11] = {
|
ws.handlers[11] = {
|
||||||
callback: (_, id) => {},
|
callback: (_, id) => {},
|
||||||
subscription: (error) => {
|
subscription: (error) => {
|
||||||
expect(error.message).toMatch(/test/);
|
expect(error.message).toMatch(/test/);
|
||||||
@@ -48,7 +47,7 @@ describe('onMessageSubscribe', () => {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
onMessage(state)({ data: '{"jsonrpc":"2.0","id":11,"result":22}' });
|
ws.onSocketMessage({ data: '{"jsonrpc":"2.0","id":11,"result":22}' });
|
||||||
onMessage(state)({ data: '{"jsonrpc":"2.0","method":"test","params":{"subscription":22,"error":{"message":"test"}}}' });
|
ws.onSocketMessage({ data: '{"jsonrpc":"2.0","method":"test","params":{"subscription":22,"error":{"message":"test"}}}' });
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -1,25 +0,0 @@
|
|||||||
// Copyright 2017-2018 @polkadot/api-provider authors & contributors
|
|
||||||
// This software may be modified and distributed under the terms
|
|
||||||
// of the ISC license. See the LICENSE file for details.
|
|
||||||
|
|
||||||
import { JsonRpcResponse } from '../types';
|
|
||||||
import { WsState } from './types';
|
|
||||||
|
|
||||||
export default function onMessageSubscribe (self: WsState, response: JsonRpcResponse): void {
|
|
||||||
self.l.debug(() => ['handling: response =', response, 'subscription =', response.params.subscription]);
|
|
||||||
|
|
||||||
const handler = self.subscriptions[response.params.subscription];
|
|
||||||
|
|
||||||
if (!handler) {
|
|
||||||
self.l.error(`Unable to find handler for subscription=${response.params.subscription}`);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
try {
|
|
||||||
const result = self.coder.decodeResponse(response);
|
|
||||||
|
|
||||||
handler.callback(null, result);
|
|
||||||
} catch (error) {
|
|
||||||
handler.callback(error, undefined);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -4,14 +4,14 @@
|
|||||||
|
|
||||||
import { mockWs, TEST_WS_URL } from '../../test/mockWs';
|
import { mockWs, TEST_WS_URL } from '../../test/mockWs';
|
||||||
|
|
||||||
import create from './index';
|
import Ws from './index';
|
||||||
|
|
||||||
let ws;
|
let ws;
|
||||||
let mock;
|
let mock;
|
||||||
|
|
||||||
function createWs (requests, autoConnect) {
|
function createWs (requests, autoConnect) {
|
||||||
mock = mockWs(requests);
|
mock = mockWs(requests);
|
||||||
ws = create(TEST_WS_URL, autoConnect);
|
ws = new Ws(TEST_WS_URL, autoConnect);
|
||||||
|
|
||||||
return ws;
|
return ws;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,34 +0,0 @@
|
|||||||
// Copyright 2017-2018 @polkadot/api-provider authors & contributors
|
|
||||||
// This software may be modified and distributed under the terms
|
|
||||||
// of the ISC license. See the LICENSE file for details.
|
|
||||||
|
|
||||||
import { WsState } from './types';
|
|
||||||
|
|
||||||
import assert from '@polkadot/util/assert';
|
|
||||||
import isNull from '@polkadot/util/is/null';
|
|
||||||
|
|
||||||
export default function onOpen (self: WsState): () => boolean {
|
|
||||||
return (): boolean => {
|
|
||||||
assert(!isNull(self.websocket), 'WebSocket cannot be null in onOpen');
|
|
||||||
|
|
||||||
self.l.debug(() => ['connected to', self.endpoint]);
|
|
||||||
|
|
||||||
self.isConnected = true;
|
|
||||||
self.emitter.emit('connected');
|
|
||||||
|
|
||||||
Object.keys(self.queued).forEach((id) => {
|
|
||||||
try {
|
|
||||||
// @ts-ignore checked above
|
|
||||||
self.websocket.send(
|
|
||||||
self.queued[id]
|
|
||||||
);
|
|
||||||
|
|
||||||
delete self.queued[id];
|
|
||||||
} catch (error) {
|
|
||||||
self.l.error(error);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
return true;
|
|
||||||
};
|
|
||||||
}
|
|
||||||
@@ -4,7 +4,7 @@
|
|||||||
|
|
||||||
import { mockWs, TEST_WS_URL } from '../../test/mockWs';
|
import { mockWs, TEST_WS_URL } from '../../test/mockWs';
|
||||||
|
|
||||||
import index from './index';
|
import Ws from './index';
|
||||||
|
|
||||||
let ws;
|
let ws;
|
||||||
let mock;
|
let mock;
|
||||||
@@ -14,7 +14,7 @@ function createMock (requests) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function createWs (autoConnect) {
|
function createWs (autoConnect) {
|
||||||
ws = index(TEST_WS_URL, autoConnect);
|
ws = new Ws(TEST_WS_URL, autoConnect);
|
||||||
|
|
||||||
return ws;
|
return ws;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,39 +0,0 @@
|
|||||||
// Copyright 2017-2018 @polkadot/api-provider authors & contributors
|
|
||||||
// This software may be modified and distributed under the terms
|
|
||||||
// of the ISC license. See the LICENSE file for details.
|
|
||||||
|
|
||||||
import { ProviderInterface$Callback } from '../types';
|
|
||||||
import { WsState } from './types';
|
|
||||||
|
|
||||||
import isNull from '@polkadot/util/is/null';
|
|
||||||
|
|
||||||
export default async function send (self: WsState, method: string, params: Array<any>, subscription?: ProviderInterface$Callback): Promise<any> {
|
|
||||||
return new Promise((resolve, reject): void => {
|
|
||||||
try {
|
|
||||||
const json = self.coder.encodeJson(method, params);
|
|
||||||
const id = self.coder.getId();
|
|
||||||
const callback = (error: Error | null, result: any) => {
|
|
||||||
if (error) {
|
|
||||||
reject(error);
|
|
||||||
} else {
|
|
||||||
resolve(result);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
self.l.debug(() => ['calling', method, params, json, !!subscription]);
|
|
||||||
|
|
||||||
self.handlers[id] = {
|
|
||||||
callback,
|
|
||||||
subscription
|
|
||||||
};
|
|
||||||
|
|
||||||
if (self.isConnected && !isNull(self.websocket)) {
|
|
||||||
self.websocket.send(json);
|
|
||||||
} else {
|
|
||||||
self.queued[id] = json;
|
|
||||||
}
|
|
||||||
} catch (error) {
|
|
||||||
reject(error);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
@@ -2,18 +2,18 @@
|
|||||||
// This software may be modified and distributed under the terms
|
// This software may be modified and distributed under the terms
|
||||||
// of the ISC license. See the LICENSE file for details.
|
// of the ISC license. See the LICENSE file for details.
|
||||||
|
|
||||||
import state from './state';
|
import Ws from './index';
|
||||||
|
|
||||||
describe('state', () => {
|
describe('state', () => {
|
||||||
it('requires an ws:// prefixed endpoint', () => {
|
it('requires an ws:// prefixed endpoint', () => {
|
||||||
expect(
|
expect(
|
||||||
() => state('http://')
|
() => new Ws('http://', false)
|
||||||
).toThrow(/with 'ws/);
|
).toThrow(/with 'ws/);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('allows wss:// endpoints', () => {
|
it('allows wss:// endpoints', () => {
|
||||||
expect(
|
expect(
|
||||||
state('wss://')
|
() => new Ws('wss://', false)
|
||||||
).toBeDefined();
|
).not.toThrow();
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -1,30 +0,0 @@
|
|||||||
// Copyright 2017-2018 @polkadot/api-provider authors & contributors
|
|
||||||
// This software may be modified and distributed under the terms
|
|
||||||
// of the ISC license. See the LICENSE file for details.
|
|
||||||
|
|
||||||
import { WsState } from './types';
|
|
||||||
|
|
||||||
import E3 from 'eventemitter3';
|
|
||||||
import assert from '@polkadot/util/assert';
|
|
||||||
import logger from '@polkadot/util/logger';
|
|
||||||
|
|
||||||
import coder from '../coder/json';
|
|
||||||
|
|
||||||
const l = logger('api-ws');
|
|
||||||
|
|
||||||
export default function state (endpoint: string, autoConnect: boolean): WsState {
|
|
||||||
assert(/^(wss|ws):\/\//.test(endpoint), `Endpoint should start with 'ws://', received '${endpoint}'`);
|
|
||||||
|
|
||||||
return {
|
|
||||||
autoConnect,
|
|
||||||
coder: coder(),
|
|
||||||
emitter: new E3.EventEmitter(),
|
|
||||||
endpoint,
|
|
||||||
handlers: {},
|
|
||||||
isConnected: false,
|
|
||||||
l,
|
|
||||||
queued: {},
|
|
||||||
subscriptions: {},
|
|
||||||
websocket: null
|
|
||||||
};
|
|
||||||
}
|
|
||||||
@@ -4,7 +4,7 @@
|
|||||||
|
|
||||||
import { mockWs, TEST_WS_URL } from '../../test/mockWs';
|
import { mockWs, TEST_WS_URL } from '../../test/mockWs';
|
||||||
|
|
||||||
import index from './index';
|
import Ws from './index';
|
||||||
|
|
||||||
let ws;
|
let ws;
|
||||||
let mock;
|
let mock;
|
||||||
@@ -14,7 +14,7 @@ function createMock (requests) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function createWs (autoConnect) {
|
function createWs (autoConnect) {
|
||||||
ws = index(TEST_WS_URL, autoConnect);
|
ws = new Ws(TEST_WS_URL, autoConnect);
|
||||||
|
|
||||||
return ws;
|
return ws;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,14 +0,0 @@
|
|||||||
// Copyright 2017-2018 @polkadot/api-provider authors & contributors
|
|
||||||
// This software may be modified and distributed under the terms
|
|
||||||
// of the ISC license. See the LICENSE file for details.
|
|
||||||
|
|
||||||
import { ProviderInterface$Callback } from '../types';
|
|
||||||
import { WsState } from './types';
|
|
||||||
|
|
||||||
import send from './send';
|
|
||||||
|
|
||||||
export default async function subscribe (self: WsState, method: string, params: Array<any>, subscription: ProviderInterface$Callback): Promise<number> {
|
|
||||||
const id = await send(self, method, params, subscription);
|
|
||||||
|
|
||||||
return id as number;
|
|
||||||
}
|
|
||||||
@@ -4,7 +4,7 @@
|
|||||||
|
|
||||||
import { mockWs, TEST_WS_URL } from '../../test/mockWs';
|
import { mockWs, TEST_WS_URL } from '../../test/mockWs';
|
||||||
|
|
||||||
import index from './index';
|
import Ws from './index';
|
||||||
|
|
||||||
let ws;
|
let ws;
|
||||||
let mock;
|
let mock;
|
||||||
@@ -14,7 +14,7 @@ function createMock (requests) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function createWs (autoConnect) {
|
function createWs (autoConnect) {
|
||||||
ws = index(TEST_WS_URL, autoConnect);
|
ws = new Ws(TEST_WS_URL, autoConnect);
|
||||||
|
|
||||||
return ws;
|
return ws;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,20 +0,0 @@
|
|||||||
// Copyright 2017-2018 @polkadot/api-provider authors & contributors
|
|
||||||
// This software may be modified and distributed under the terms
|
|
||||||
// of the ISC license. See the LICENSE file for details.
|
|
||||||
|
|
||||||
import { WsState } from './types';
|
|
||||||
|
|
||||||
import assert from '@polkadot/util/assert';
|
|
||||||
import isUndefined from '@polkadot/util/is/undefined';
|
|
||||||
|
|
||||||
import send from './send';
|
|
||||||
|
|
||||||
export default async function unsubscribe (self: WsState, method: string, id: number): Promise<boolean> {
|
|
||||||
assert(!isUndefined(self.subscriptions[id]), `Unable to find active subscription=${id}`);
|
|
||||||
|
|
||||||
delete self.subscriptions[id];
|
|
||||||
|
|
||||||
const result = await send(self, method, [id]);
|
|
||||||
|
|
||||||
return result as boolean;
|
|
||||||
}
|
|
||||||
@@ -5,14 +5,14 @@
|
|||||||
import { ApiInterface } from '../../api/src/types';
|
import { ApiInterface } from '../../api/src/types';
|
||||||
|
|
||||||
import createApi from '../../api/src';
|
import createApi from '../../api/src';
|
||||||
import createProvider from '../src/ws';
|
import Ws from '../src/ws';
|
||||||
|
|
||||||
describe.skip('e2e', () => {
|
describe.skip('e2e', () => {
|
||||||
let api: ApiInterface;
|
let api: ApiInterface;
|
||||||
|
|
||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
jest.setTimeout(30000);
|
jest.setTimeout(30000);
|
||||||
api = createApi(createProvider('ws://127.0.0.1:9944'));
|
api = createApi(new Ws('ws://127.0.0.1:9944'));
|
||||||
});
|
});
|
||||||
|
|
||||||
it('subscribes to client_newHead', (done) => {
|
it('subscribes to client_newHead', (done) => {
|
||||||
|
|||||||
@@ -2,11 +2,11 @@
|
|||||||
// This software may be modified and distributed under the terms
|
// This software may be modified and distributed under the terms
|
||||||
// of the ISC license. See the LICENSE file for details.
|
// of the ISC license. See the LICENSE file for details.
|
||||||
|
|
||||||
jest.mock('@polkadot/api-provider/ws', () => () => ({
|
jest.mock('@polkadot/api-provider/ws', () => class {
|
||||||
isConnected: () => true,
|
isConnected = () => true;
|
||||||
on: () => true,
|
on = () => true;
|
||||||
send: () => true
|
send = () => true;
|
||||||
}));
|
});
|
||||||
jest.mock('./interface', () => (api, sectionName) => sectionName);
|
jest.mock('./interface', () => (api, sectionName) => sectionName);
|
||||||
|
|
||||||
const createApi = require('./index').default;
|
const createApi = require('./index').default;
|
||||||
|
|||||||
@@ -8,13 +8,13 @@ import { RxApiInterface } from './types';
|
|||||||
|
|
||||||
import createApi from '@polkadot/api/index';
|
import createApi from '@polkadot/api/index';
|
||||||
import interfaces from '@polkadot/jsonrpc';
|
import interfaces from '@polkadot/jsonrpc';
|
||||||
import createWs from '@polkadot/api-provider/ws';
|
import Ws from '@polkadot/api-provider/ws';
|
||||||
|
|
||||||
import createExposed from './api';
|
import createExposed from './api';
|
||||||
import defaults from './defaults';
|
import defaults from './defaults';
|
||||||
import createInterface from './interface';
|
import createInterface from './interface';
|
||||||
|
|
||||||
export default function rxApi (provider: ProviderInterface = createWs(defaults.WS_URL)): RxApiInterface {
|
export default function rxApi (provider: ProviderInterface = new Ws(defaults.WS_URL)): RxApiInterface {
|
||||||
const api = createApi(provider);
|
const api = createApi(provider);
|
||||||
|
|
||||||
return Object.keys(interfaces).reduce((result, type) => {
|
return Object.keys(interfaces).reduce((result, type) => {
|
||||||
|
|||||||
@@ -25,11 +25,11 @@ npm install --save @polkadot/api
|
|||||||
Initialisation -
|
Initialisation -
|
||||||
|
|
||||||
```js
|
```js
|
||||||
import Api from '@polkadot/api';
|
import createApi from '@polkadot/api';
|
||||||
import HttpProvider from '@polkadot/api-provider/http';
|
import HttpProvider from '@polkadot/api-provider/http';
|
||||||
|
|
||||||
const provider = new HttpProvider('http://127.0.0.1:9933');
|
const provider = new HttpProvider('http://127.0.0.1:9933');
|
||||||
const api = new Api(provider);
|
const api = createApi(provider);
|
||||||
```
|
```
|
||||||
|
|
||||||
Making calls -
|
Making calls -
|
||||||
|
|||||||
Reference in New Issue
Block a user