First stage renames, rpc-{core,provider,rx} (#220)

* Docs renames (& removal for re-gen)

* Rename api, api-provider & api-rx

* Apoi -> Rpc, RxApi -> RpcRx (classes)

* Add .nodoc

* Bump version

* Link updates (summary)

* Cleanup deleted (master version)

* Deleted re-instated

* More... ;)

* Types...

* Revert .nodoc
This commit is contained in:
Jaco Greeff
2018-10-10 10:01:34 +02:00
committed by GitHub
parent cd7c17b265
commit 4344f33de8
102 changed files with 257 additions and 233 deletions
+15
View File
@@ -0,0 +1,15 @@
ISC License (ISC)
Copyright 2017-2018 @polkadot/rpc-rx authors & contributors
Permission to use, copy, modify, and/or distribute this software for any
purpose with or without fee is hereby granted, provided that the above
copyright notice and this permission notice appear in all copies.
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH
REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND
FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT,
INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR
OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
PERFORMANCE OF THIS SOFTWARE.
+13
View File
@@ -0,0 +1,13 @@
[![polkadotjs](https://img.shields.io/badge/polkadot-js-orange.svg?style=flat-square)](https://polkadot.js.org)
![isc](https://img.shields.io/badge/license-ISC-lightgrey.svg?style=flat-square)
[![style](https://img.shields.io/badge/code%20style-semistandard-lightgrey.svg?style=flat-square)](https://github.com/Flet/semistandard)
[![npm](https://img.shields.io/npm/v/@polkadot/rpc-rx.svg?style=flat-square)](https://www.npmjs.com/package/@polkadot/rpc-rx)
[![travis](https://img.shields.io/travis/polkadot-js/api.svg?style=flat-square)](https://travis-ci.org/polkadot-js/api)
[![maintainability](https://img.shields.io/codeclimate/maintainability/polkadot-js/api.svg?style=flat-square)](https://codeclimate.com/github/polkadot-js/api/maintainability)
[![coverage](https://img.shields.io/coveralls/polkadot-js/api.svg?style=flat-square)](https://coveralls.io/github/polkadot-js/api?branch=master)
[![dependency](https://david-dm.org/polkadot-js/api.svg?style=flat-square&path=packages/rpc-rx)](https://david-dm.org/polkadot-js/api?path=packages/rpc-rx)
[![devDependency](https://david-dm.org/polkadot-js/api/dev-status.svg?style=flat-square&path=packages/rpc-rx)](https://david-dm.org/polkadot-js/api?path=packages/rpc-rx#info=devDependencies)
# @polkadot/rpc-rx
An RxJs wrapper around the [@polkadot/api](../api).
+38
View File
@@ -0,0 +1,38 @@
{
"name": "@polkadot/rpc-rx",
"version": "0.31.0",
"description": "An RxJs wrapper around the Polkadot JS API",
"main": "index.js",
"keywords": [
"Polkadot",
"RxJs"
],
"author": "Jaco Greeff <jacogr@gmail.com>",
"maintainers": [
"Jaco Greeff <jacogr@gmail.com>"
],
"contributors": [],
"license": "ISC",
"engines": {
"node": ">=8.0"
},
"publishConfig": {
"access": "public",
"registry": "https://registry.npmjs.org"
},
"repository": {
"type": "git",
"url": "git+https://github.com/polkadot-js/api.git"
},
"bugs": {
"url": "https://github.com/polkadot-js/api/issues"
},
"homepage": "https://github.com/polkadot-js/api/tree/master/packages/rpc-rx#readme",
"dependencies": {
"@babel/runtime": "^7.0.0",
"@polkadot/rpc-core": "^0.31.0",
"@polkadot/rpc-provider": "^0.31.0",
"@types/rx": "^4.1.1",
"rxjs": "^6.3.2"
}
}
+91
View File
@@ -0,0 +1,91 @@
// Copyright 2017-2018 @polkadot/rpc-rx authors & contributors
// This software may be modified and distributed under the terms
// of the ISC license. See the LICENSE file for details.
jest.mock('@polkadot/rpc-provider/ws', () => class {
isConnected = () => true;
on = () => true;
send = () => true;
});
const Api = require('./index').default;
describe('createCachedObservable', () => {
let api;
let creator;
let section;
beforeEach(() => {
api = new Api();
});
beforeEach(() => {
const subMethod = jest.fn((name, ...params) => {
return Promise.resolve(12345);
});
subMethod.unsubscribe = jest.fn(() => {
return Promise.resolve(true);
});
const resMethod = jest.fn((name, ...params) => {
return Promise.resolve(12345);
});
section = {
resMethod,
subMethod
};
creator = api.createCachedObservable('test', 'subMethod', section);
});
it('creates a single observable', () => {
creator(123).subscribe((value) => {});
expect(
section.subMethod
).toHaveBeenCalledWith(123, expect.anything());
});
it('creates a single observable (multiple calls)', () => {
const observable1 = creator(123);
observable1.subscribe((value) => {});
const observable2 = creator(123);
observable2.subscribe((value) => {});
expect(
observable2
).toEqual(observable1);
});
it('creates multiple observers for different values', () => {
const observable1 = creator(123);
observable1.subscribe((value) => {});
const observable2 = creator(456);
observable2.subscribe((value) => {});
expect(
observable2
).not.toEqual(observable1);
});
it('functions as an subject', (done) => {
const subject = creator(123);
subject.subscribe((value) => {
if (value) {
expect(value).toEqual('test');
done();
}
});
subject.next('test');
});
});
+9
View File
@@ -0,0 +1,9 @@
// Copyright 2017-2018 @polkadot/rpc-rx authors & contributors
// This software may be modified and distributed under the terms
// of the ISC license. See the LICENSE file for details.
const WS_URL = 'ws://127.0.0.1:9944';
export default {
WS_URL
};
+39
View File
@@ -0,0 +1,39 @@
// Copyright 2017-2018 @polkadot/rpc-rx authors & contributors
// This software may be modified and distributed under the terms
// of the ISC license. See the LICENSE file for details.
import isFunction from '@polkadot/util/is/function';
jest.mock('@polkadot/rpc-provider/ws', () => class {
isConnected = () => true;
on = () => true;
send = () => true;
});
const Api = require('./index').default;
describe('Api', () => {
let api;
beforeEach(() => {
api = new Api();
});
it('creates an instance with all sections', () => {
expect(
Object
.keys(api)
.filter((key) =>
!key.startsWith('_')
)
).toEqual([
'author', 'chain', 'state', 'system'
]);
});
it('has isConnected', () => {
expect(
isFunction(api.isConnected)
).toBe(true);
});
});
+170
View File
@@ -0,0 +1,170 @@
// Copyright 2017-2018 @polkadot/rpc-rx authors & contributors
// This software may be modified and distributed under the terms
// of the ISC license. See the LICENSE file for details.
import { RpcInterface, RpcInterface$Section } from '@polkadot/rpc-core/types';
import { ProviderInterface } from '@polkadot/rpc-provider/types';
import { RxRpcInterface, RxRpcInterface$Section } from './types';
import { BehaviorSubject, Observable, Subscriber, from } from 'rxjs';
import Rpc from '@polkadot/rpc-core/index';
import Ws from '@polkadot/rpc-provider/ws';
import isFunction from '@polkadot/util/is/function';
import isUndefined from '@polkadot/util/is/undefined';
import defaults from './defaults';
type CachedMap = {
[index: string]: {
[index: string]: BehaviorSubject<any>
}
};
/**
* @name RpcRx
* @summary The RxJS API is a wrapper around the API.
* @description It allows wrapping API components with observables using RxJS.
*
* @example
* <BR>
*
* ```javascript
* import Rpc from '@polkadot/rpc-rx';
* import WsProvider from '@polkadot/rpc-provider/ws';
*
* const provider = new WsProvider('http://127.0.0.1:9944');
* const api = new Rpc(provider);
* ```
*/
export default class RpcRx implements RxRpcInterface {
private _api: RpcInterface;
private _cacheMap: CachedMap;
private _isConnected: BehaviorSubject<boolean>;
readonly author: RxRpcInterface$Section;
readonly chain: RxRpcInterface$Section;
readonly state: RxRpcInterface$Section;
readonly system: RxRpcInterface$Section;
/**
* @param {ProviderInterface} provider An API provider using HTTP or WebSocket
*/
constructor (provider: ProviderInterface = new Ws(defaults.WS_URL)) {
this._api = new Rpc(provider);
this._cacheMap = {};
this._isConnected = new BehaviorSubject(provider.isConnected());
provider.on('connected', () => this._isConnected.next(true));
provider.on('disconnected', () => this._isConnected.next(false));
this.author = this.createInterface('author', this._api.author);
this.chain = this.createInterface('chain', this._api.chain);
this.state = this.createInterface('state', this._api.state);
this.system = this.createInterface('system', this._api.system);
}
isConnected (): BehaviorSubject<boolean> {
return this._isConnected;
}
private createInterface (sectionName: string, section: RpcInterface$Section): RxRpcInterface$Section {
return Object
.keys(section)
.filter((name) => !['subscribe', 'unsubscribe'].includes(name))
.reduce((observables, name) => {
observables[name] = this.createObservable(`${sectionName}_${name}`, name, section);
return observables;
}, ({} as RxRpcInterface$Section));
}
private createObservable (subName: string, name: string, section: RpcInterface$Section): (...params: Array<any>) => Observable<any> | BehaviorSubject<any> {
if (isFunction(section[name].unsubscribe)) {
return this.createCachedObservable(subName, name, section);
}
return (...params: Array<any>): Observable<any> =>
from(
section[name]
.apply(null, params)
.catch((error: Error) => {
console.error(error);
})
);
}
private createCachedObservable (subName: string, name: string, section: RpcInterface$Section): (...params: Array<any>) => BehaviorSubject<any> {
if (!this._cacheMap[subName]) {
this._cacheMap[subName] = {};
}
return (...params: Array<any>): BehaviorSubject<any> => {
const paramStr = JSON.stringify(params);
if (!this._cacheMap[subName][paramStr]) {
this._cacheMap[subName][paramStr] = this.createSubject(name, params, section);
}
return this._cacheMap[subName][paramStr];
};
}
private createSubject (name: string, params: Array<any>, section: RpcInterface$Section, unsubCallback?: () => void): BehaviorSubject<any> {
const subject = new BehaviorSubject(undefined);
Observable
.create((observer: Subscriber<any>): Function => {
try {
const fn = section[name];
const subscribe = fn(...params, this.createSubjectCallback(observer));
return (): void => {
subscribe
.then((subscriptionId: number) =>
fn.unsubscribe(subscriptionId)
)
.then(() =>
isFunction(unsubCallback) && unsubCallback()
)
.catch((error) => {
console.error('Unsubscribe failed', error);
});
};
} catch (error) {
console.error(error);
return (): void => {
console.error('Unsubscribe called on previously failed subscription', error);
};
}
})
.subscribe(subject);
return subject;
}
private createSubjectCallback (observer: Subscriber<any>) {
let cachedResult: any;
return (error: Error | null, result: any) => {
if (error) {
console.error(error);
observer.next();
return;
}
if (isUndefined(cachedResult) || !Array.isArray(cachedResult)) {
cachedResult = result;
} else {
const resultArray = (result as Array<any>) || [];
cachedResult = cachedResult.map((cachedValue, index) =>
isUndefined(resultArray[index])
? cachedValue
: resultArray[index]
);
}
observer.next(cachedResult);
};
}
}
+84
View File
@@ -0,0 +1,84 @@
// Copyright 2017-2018 @polkadot/rpc-rx authors & contributors
// This software may be modified and distributed under the terms
// of the ISC license. See the LICENSE file for details.
jest.mock('@polkadot/rpc-provider/ws', () => class {
isConnected = () => true;
on = () => true;
send = () => true;
});
const Api = require('./index').default;
describe('subject', () => {
const params = [123, false];
let api;
let callback;
let section;
let subscription;
let observable;
beforeEach(() => {
api = new Api();
});
beforeEach(() => {
const subMethod = jest.fn((name, ...params) => {
callback = params.pop();
return Promise.resolve(12345);
});
subMethod.unsubscribe = jest.fn(() => {
return Promise.resolve(true);
});
section = {
subMethod
};
observable = api.createSubject('subMethod', params, section);
});
afterEach(() => {
if (subscription) {
subscription.unsubscribe();
}
});
it('subscribes via the api section', (done) => {
observable.subscribe((value) => {
if (value) {
expect(
section.subMethod
).toHaveBeenCalledWith(123, false, expect.anything());
done();
}
});
callback(null, 'test');
});
it('returns the observable value', (done) => {
subscription = observable.subscribe((value) => {
if (value) {
expect(value).toEqual('test');
done();
}
});
callback(null, 'test');
});
it('ignores errors, returns the observable value', (done) => {
subscription = observable.subscribe((value) => {
if (value) {
expect(value).toEqual('test');
done();
}
});
callback(new Error('error'));
callback(null, 'test');
});
});
+22
View File
@@ -0,0 +1,22 @@
// Copyright 2017-2018 @polkadot/rpc-rx authors & contributors
// This software may be modified and distributed under the terms
// of the ISC license. See the LICENSE file for details.
import { BehaviorSubject, Observable } from 'rxjs';
import interfaces from '@polkadot/jsonrpc/types';
export type RxRpcInterface$Method = (...params: Array<any>) => Observable<any> | BehaviorSubject<any>;
export type RxRpcInterface$Section = {
[index: string]: RxRpcInterface$Method
};
export type RxRpcInterface = {
readonly author: RxRpcInterface$Section;
readonly chain: RxRpcInterface$Section;
readonly state: RxRpcInterface$Section;
readonly system: RxRpcInterface$Section;
isConnected: () => BehaviorSubject<boolean>
}
@@ -0,0 +1,48 @@
// Copyright 2017-2018 @polkadot/rpc-rx authors & contributors
// This software may be modified and distributed under the terms
// of the ISC license. See the LICENSE file for details.
import Ws from '@polkadot/rpc-provider/ws';
import storage from '@polkadot/storage/static';
import RxApi from '../../src';
describe.skip('e2e subscriptions', () => {
let api;
beforeEach(() => {
jest.setTimeout(30000);
api = new RxApi(new Ws('ws://127.0.0.1:9944'));
});
it('retrieves current timestamp', (done) => {
let count = 0;
api.state
.storage([[storage.timestamp.now]])
.subscribe((data) => {
console.error('timestamp.now', data);
if (++count === 4) {
done();
}
});
});
it('retrieves multiple values', (done) => {
api.state
.storage([
[storage.timestamp.now],
[storage.session.sessionLength],
[storage.staking.sessionsPerEra]
])
.subscribe((data) => {
console.error('multiples', data);
if (data !== undefined && data.filter((item) => item).length === 3) {
done();
}
});
});
});