Skip to content

Commit

Permalink
feat: add alternative initializations of context class
Browse files Browse the repository at this point in the history
  • Loading branch information
shuffledex committed Feb 27, 2020
1 parent adf9a12 commit 506787f
Show file tree
Hide file tree
Showing 4 changed files with 151 additions and 39 deletions.
50 changes: 38 additions & 12 deletions src/Polymesh.ts
@@ -1,14 +1,9 @@
import { ApiPromise, WsProvider } from '@polymathnetwork/polkadot/api';
import { ApiPromise, Keyring, WsProvider } from '@polymathnetwork/polkadot/api';
import { BigNumber } from 'bignumber.js';

import { Context, PolymeshError } from '~/base';
import { ErrorCode } from '~/types';

interface ConnectParams {
nodeUrl: string;
accountSeed?: string;
}

/**
* Main entry point of the Polymesh SDK
*/
Expand All @@ -22,22 +17,53 @@ export class Polymesh {
this.context = context;
}

static async connect(params: { nodeUrl: string; accountSeed: string }): Promise<Polymesh>;

static async connect(params: { nodeUrl: string; keyring: Keyring }): Promise<Polymesh>;

static async connect(params: { nodeUrl: string; accountUri: string }): Promise<Polymesh>;

static async connect(params: { nodeUrl: string }): Promise<Polymesh>;

/**
* Create the instance and connect to the Polymesh node
*/
static async connect(params: ConnectParams): Promise<Polymesh> {
const { nodeUrl, accountSeed } = params;
static async connect(params: {
nodeUrl: string;
accountSeed?: string;
keyring?: Keyring;
accountUri?: string;
}): Promise<Polymesh> {
const { nodeUrl, accountSeed, keyring, accountUri } = params;
let polymeshApi: ApiPromise;

try {
polymeshApi = await ApiPromise.create({
provider: new WsProvider(nodeUrl),
});

const context = await Context.create({
polymeshApi,
accountSeed,
});
let context: Context = {} as Context;

if (accountSeed) {
context = await Context.create({
polymeshApi,
seed: accountSeed,
});
} else if (keyring) {
context = await Context.create({
polymeshApi,
keyring: keyring,
});
} else if (accountUri) {
context = await Context.create({
polymeshApi,
uri: accountUri,
});
} else {
context = await Context.create({
polymeshApi,
});
}

return new Polymesh(context);
} catch (e) {
Expand Down
27 changes: 27 additions & 0 deletions src/__tests__/Polymesh.ts
Expand Up @@ -33,6 +33,33 @@ describe('Polymesh Class', () => {
sinon.assert.match(polymesh instanceof Polymesh, true);
});

test('should instantiate ApiPromise with a seed and return a Polymesh instance', async () => {
const polymesh = await Polymesh.connect({
nodeUrl: '',
accountSeed: 'Alice'.padEnd(32, ' '),
});

sinon.assert.match(polymesh instanceof Polymesh, true);
});

test('should instantiate ApiPromise with a keyring and return a Polymesh instance', async () => {
const polymesh = await Polymesh.connect({
nodeUrl: '',
keyring: {} as polkadotModule.Keyring,
});

sinon.assert.match(polymesh instanceof Polymesh, true);
});

test('should instantiate ApiPromise with a uri and return a Polymesh instance', async () => {
const polymesh = await Polymesh.connect({
nodeUrl: '',
accountUri: '//uri',
});

sinon.assert.match(polymesh instanceof Polymesh, true);
});

test('should throw if ApiPromise fails in the connection process', async () => {
polkadotMockFactory.throwOnApiCreation();
const polymeshApiPromise = Polymesh.connect({
Expand Down
66 changes: 44 additions & 22 deletions src/base/Context.ts
Expand Up @@ -7,11 +7,6 @@ import { Identity } from '~/api/entities';
import { PolymeshError } from '~/base';
import { ErrorCode } from '~/types';

interface BuildParams {
polymeshApi: ApiPromise;
accountSeed?: string | undefined;
}

interface SignerData {
currentPair: IKeyringPair;
did: IdentityId;
Expand Down Expand Up @@ -59,26 +54,53 @@ export class Context {
}
}

static async create(params: { polymeshApi: ApiPromise; seed: string }): Promise<Context>;

static async create(params: { polymeshApi: ApiPromise; keyring: Keyring }): Promise<Context>;

static async create(params: { polymeshApi: ApiPromise; uri: string }): Promise<Context>;

static async create(params: { polymeshApi: ApiPromise }): Promise<Context>;

/**
* Create the Context instance
*/
static async create(params: BuildParams): Promise<Context> {
const { polymeshApi, accountSeed } = params;

const keyring = new Keyring({ type: 'sr25519' });

if (accountSeed) {
if (accountSeed.length !== 32) {
throw new PolymeshError({
code: ErrorCode.ValidationError,
message: 'Seed must be 32 characters in length',
});
static async create(params: {
polymeshApi: ApiPromise;
seed?: string;
keyring?: Keyring;
uri?: string;
}): Promise<Context> {
const { polymeshApi, seed, keyring, uri } = params;

let keyringManagement = new Keyring({ type: 'sr25519' });
let currentPair = {} as IKeyringPair;
let keyToIdentityIds;

if (seed || keyring || uri) {
if (keyring) {
keyringManagement = keyring;

keyToIdentityIds = await polymeshApi.query.identity.keyToIdentityIds(
keyring.getPairs()[0].publicKey
);
} else {
if (seed) {
if (seed.length !== 32) {
throw new PolymeshError({
code: ErrorCode.ValidationError,
message: 'Seed must be 32 characters in length',
});
}

currentPair = keyringManagement.addFromSeed(stringToU8a(seed));
} else {
currentPair = keyringManagement.addFromUri(uri as string);
}

keyToIdentityIds = await polymeshApi.query.identity.keyToIdentityIds(currentPair.publicKey);
}

const currentPair = keyring.addFromSeed(stringToU8a(accountSeed));
const keyToIdentityIds = await polymeshApi.query.identity.keyToIdentityIds(
currentPair.publicKey
);
let did: IdentityId;
try {
did = keyToIdentityIds.unwrap().asUnique;
Expand All @@ -89,9 +111,9 @@ export class Context {
});
}

return new Context({ polymeshApi, keyring, pair: { currentPair, did } });
return new Context({ polymeshApi, keyring: keyringManagement, pair: { currentPair, did } });
}
return new Context({ polymeshApi, keyring });
return new Context({ polymeshApi, keyring: keyringManagement });
}

/**
Expand Down
47 changes: 42 additions & 5 deletions src/base/__tests__/Context.ts
Expand Up @@ -26,16 +26,16 @@ describe('Context class', () => {
});

describe('method: create', () => {
test('should throw if accountSeed parameter is not a 32 length string', async () => {
test('should throw if seed parameter is not a 32 length string', async () => {
const context = Context.create({
polymeshApi: polkadotMockFactory.getApiInstance(),
accountSeed: 'abc',
seed: 'abc',
});

await expect(context).rejects.toThrow(new Error('Seed must be 32 characters in length'));
});

test('should create a Context class with Pair and Identity attached', async () => {
test('should create a Context class from a seed with Pair and Identity attached', async () => {
const keyToIdentityIdsStub = polkadotMockFactory.createQueryStub(
'identity',
'keyToIdentityIds',
Expand All @@ -45,7 +45,7 @@ describe('Context class', () => {

const context = await Context.create({
polymeshApi: polkadotMockFactory.getApiInstance(),
accountSeed: 'Alice'.padEnd(32, ' '),
seed: 'Alice'.padEnd(32, ' '),
});

sinon.assert.calledOnce(keyringAddFromSeedStub);
Expand All @@ -54,6 +54,43 @@ describe('Context class', () => {
sinon.assert.match(context.currentIdentity instanceof identityModule.Identity, true);
});

test('should create a Context class from a keyring with Pair and Identity attached', async () => {
const keyToIdentityIdsStub = polkadotMockFactory.createQueryStub(
'identity',
'keyToIdentityIds',
{ unwrap: () => ({ asUnique: '012abc' }) }
);
const keyringGetPairsStub = mockKeyring.mock('getPairs', [{ publicKey: 'address' }]);

const context = await Context.create({
polymeshApi: polkadotMockFactory.getApiInstance(),
keyring: mockKeyring.getMockInstance(),
});

sinon.assert.calledOnce(keyringGetPairsStub);
sinon.assert.calledOnce(keyToIdentityIdsStub);
sinon.assert.match(context.currentIdentity instanceof identityModule.Identity, true);
});

test('should create a Context class from a uri with Pair and Identity attached', async () => {
const keyToIdentityIdsStub = polkadotMockFactory.createQueryStub(
'identity',
'keyToIdentityIds',
{ unwrap: () => ({ asUnique: '012abc' }) }
);
const keyringAddFromUriStub = mockKeyring.mock('addFromUri', 'currentPair');

const context = await Context.create({
polymeshApi: polkadotMockFactory.getApiInstance(),
uri: '//Alice',
});

sinon.assert.calledOnce(keyringAddFromUriStub);
sinon.assert.calledOnce(keyToIdentityIdsStub);
expect(context.currentPair).toEqual('currentPair');
sinon.assert.match(context.currentIdentity instanceof identityModule.Identity, true);
});

test('should create a Context class without Pair and Identity attached', async () => {
const keyToIdentityIdsStub = polkadotMockFactory.createQueryStub(
'identity',
Expand All @@ -78,7 +115,7 @@ describe('Context class', () => {

const context = Context.create({
polymeshApi: polkadotMockFactory.getApiInstance(),
accountSeed: 'Alice'.padEnd(32, ' '),
seed: 'Alice'.padEnd(32, ' '),
});

await expect(context).rejects.toThrow(new Error('Identity ID does not exist'));
Expand Down

0 comments on commit 506787f

Please sign in to comment.