diff --git a/packages/api/src/Base.ts b/packages/api/src/Base.ts index 69d03b535a..1a659dc122 100644 --- a/packages/api/src/Base.ts +++ b/packages/api/src/Base.ts @@ -10,10 +10,12 @@ import Rpc from '@polkadot/rpc-core/index'; import extrinsicsFromMeta from '@polkadot/extrinsics/fromMetadata'; import { Storage } from '@polkadot/storage/types'; import storageFromMeta from '@polkadot/storage/fromMetadata'; +import TypeRegistry from '@polkadot/types/codec/TypeRegistry'; import { Hash, Method, RuntimeVersion } from '@polkadot/types/index'; import Event from '@polkadot/types/Event'; import RuntimeMetadata from '@polkadot/types/Metadata'; import { Extrinsics } from '@polkadot/types/Method'; +import { Constructor } from '@polkadot/types/types'; import { assert, isUndefined, logger } from '@polkadot/util'; type MetaDecoration = { @@ -28,6 +30,18 @@ const l = logger('api'); const INIT_ERROR = `Api needs to be initialised before using, listen on 'ready'`; +export interface ApiOptions { + /** + * WebSocket provider from rpc-provider/ws. If not specified, it will default to connecting to the + * localhost with the default port, i.e. `ws://127.0.0.1:9944` + */ + wsProvider?: WsProvider; + /** + * Additional types used by runtime modules. This is nessusary if the runtime modules uses non-buildin types. + */ + additionalTypes?: {[name: string]: Constructor}; +} + export default abstract class ApiBase implements ApiBaseInterface { private _eventemitter: EventEmitter; protected _extrinsics?: E; @@ -41,7 +55,7 @@ export default abstract class ApiBase implements ApiBaseInterface @@ -56,11 +70,15 @@ export default abstract class ApiBase implements ApiBaseInterface @@ -123,14 +123,14 @@ export default class ApiPromise extends ApiBase { - return new ApiPromise(wsProvider).isReady; + static create (options: ApiOptions | WsProvider = {}): Promise { + return new ApiPromise(options).isReady; } /** * @description Creates an instance of the ApiPromise class * - * @param wsProvider WebSocket provider from rpc-provider/ws. If not specified, it will default to connecting to the localhost with the default port, i.e. `ws://127.0.0.1:9944` + * @param options Options to create an instance. Can be either [[ApiOptions]] or [[WsProvider]] * * @example *
@@ -145,8 +145,13 @@ export default class ApiPromise extends ApiBase super.on('ready', () => diff --git a/packages/api/src/rx/index.ts b/packages/api/src/rx/index.ts index 9c0e896f80..571bd57473 100644 --- a/packages/api/src/rx/index.ts +++ b/packages/api/src/rx/index.ts @@ -6,8 +6,8 @@ import { ApiRxInterface, QueryableStorageFunction, QueryableModuleStorage, Query import { EMPTY, Observable, from } from 'rxjs'; import { defaultIfEmpty, map } from 'rxjs/operators'; -import WsProvider from '@polkadot/rpc-provider/ws'; import Rpc from '@polkadot/rpc-core/index'; +import WsProvider from '@polkadot/rpc-provider/ws'; import RpcRx from '@polkadot/rpc-rx/index'; import { Storage } from '@polkadot/storage/types'; import { Codec } from '@polkadot/types/types'; @@ -15,7 +15,7 @@ import { Extrinsics, ExtrinsicFunction } from '@polkadot/types/Method'; import { StorageFunction } from '@polkadot/types/StorageKey'; import { logger } from '@polkadot/util'; -import ApiBase from '../Base'; +import ApiBase, { ApiOptions } from '../Base'; import SubmittableExtrinsic from './SubmittableExtrinsic'; const l = logger('api-rx'); @@ -123,7 +123,7 @@ export default class ApiRx extends ApiBase @@ -138,14 +138,14 @@ export default class ApiRx extends ApiBase { - return new ApiRx(wsProvider).isReady; + static create (options: ApiOptions | WsProvider = {}): Observable { + return new ApiRx(options).isReady; } /** * @description Create an instance of the ApiRx class * - * @param wsProvider A WebSocket provider from rpc-provider/ws. If not specified, it will default to connecting to the localhost with the default port, i.e. `ws://127.0.0.1:9944` + * @param options Options to create an instance. Can be either [[ApiOptions]] or [[WsProvider]] * * @example *
@@ -160,8 +160,13 @@ export default class ApiRx extends ApiBase { + let registry: TypeRegistry; + + beforeEach(() => { + registry = new TypeRegistry(); + }); + + it('Handles non exist type', () => { + expect(registry.get('non-exist')).toBeUndefined(); + }); + + it('Able to register type', () => { + registry.register(Text); + expect(registry.get('Text')).toBe(Text); + }); + + it('Able to register type with a different name', () => { + registry.register('TextRenamed', Text); + expect(registry.get('TextRenamed')).toBe(Text); + }); + + it('Able to register multiple types', () => { + registry.register({ + Text, + 'U32Renamed': U32 + }); + expect(registry.get('Text')).toBe(Text); + expect(registry.get('U32Renamed')).toBe(U32); + }); +}); diff --git a/packages/types/src/codec/TypeRegistry.ts b/packages/types/src/codec/TypeRegistry.ts new file mode 100644 index 0000000000..52258dd157 --- /dev/null +++ b/packages/types/src/codec/TypeRegistry.ts @@ -0,0 +1,40 @@ +// Copyright 2017-2018 @polkadot/types authors & contributors +// This software may be modified and distributed under the terms +// of the Apache-2.0 license. See the LICENSE file for details. + +import { Constructor } from '../types'; +import * as allTypes from '../index'; + +export default class TypeRegistry { + static readonly defaultRegistry: TypeRegistry = new TypeRegistry(); + + private _registry: Map = new Map(); + + register (type: Constructor | {[name: string]: Constructor}): void; + register (name: string, type: Constructor): void; + register (arg1: string | Constructor | {[name: string]: Constructor}, arg2?: Constructor): void { + if (typeof arg1 === 'string') { + const name = arg1; + const type = arg2!; + + this._registry.set(name, type); + } else if (typeof arg1 === 'function') { + const name = arg1.name; + const type = arg1; + + this._registry.set(name, type); + } else { + const dict = arg1; + + for (const [name, type] of Object.entries(dict)) { + this._registry.set(name, type); + } + } + } + + get (name: string) { + return this._registry.get(name); + } +} + +TypeRegistry.defaultRegistry.register(allTypes as any); diff --git a/packages/types/src/codec/createType.ts b/packages/types/src/codec/createType.ts index bd77f6079c..16d1d7b90e 100644 --- a/packages/types/src/codec/createType.ts +++ b/packages/types/src/codec/createType.ts @@ -8,6 +8,7 @@ import { Codec, Constructor } from '../types'; import Text from '../Text'; import Compact from './Compact'; import Tuple from './Tuple'; +import TypeRegistry from './TypeRegistry'; import UInt from './UInt'; import Vector from './Vector'; @@ -145,13 +146,12 @@ export function getTypeClass (value: TypeDef): Constructor { ); } - // We are dynamically loading as to avoid circular dependencies - const Types = require('../index'); - const Type = Types[value.type]; + const registry = TypeRegistry.defaultRegistry; + const Type = registry.get(value.type); assert(Type, `Unable to determine type from '${value.type}'`); - return Type; + return Type!; } export default function createType (type: Text | string, value?: any): Codec {