Skip to content

Commit

Permalink
Merge branch 'prod'
Browse files Browse the repository at this point in the history
  • Loading branch information
gdethier committed Jun 14, 2024
2 parents d316cc8 + a56e6e6 commit 2f78ec1
Show file tree
Hide file tree
Showing 7 changed files with 154 additions and 8 deletions.
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
{
"version": "0.47.0",
"version": "0.48.1",
"private": true,
"repository": {
"type": "git",
Expand Down
10 changes: 9 additions & 1 deletion packages/client/src/DirectoryClient.ts
Original file line number Diff line number Diff line change
Expand Up @@ -96,10 +96,18 @@ export class DirectoryClient {

private getHost(address: string, data: Record<string, PalletLoAuthorityListLegalOfficerData>): PalletLoAuthorityListLegalOfficerData {
const hostOrGuest = data[address];
if(!hostOrGuest) {
throw new Error(`No data for address ${ address }`);
}
if(hostOrGuest.isHost) {
return hostOrGuest;
} else {
return data[hostOrGuest.asGuest.toString()];
const hostAddress = hostOrGuest.asGuest.hostId.toString();
const host = data[hostAddress];
if(!host) {
throw new Error(`No host with address ${ hostAddress }`);
}
return host;
}
}

Expand Down
109 changes: 109 additions & 0 deletions packages/client/test/DirectoryClient.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,109 @@
import { LogionNodeApiClass, Region, ValidAccountId } from "@logion/node-api";
import { AxiosInstance, AxiosResponse } from "axios";
import { Mock } from "moq.ts";
import { AccountId32 } from "@polkadot/types/interfaces/types.js";
import type { Bytes } from '@polkadot/types-codec';
import { PalletLoAuthorityListLegalOfficerData, PalletLoAuthorityListHostData, PalletLoAuthorityListGuestData, LogionRuntimeRegion } from "@polkadot/types/lookup";
import { AxiosFactory, DirectoryClient, DirectoryLegalOfficer } from "../src/index.js";
import { EMPTY_POSTAL_ADDRESS, EMPTY_USER_IDENTITY, mockCodecWithToString, mockCodecWithToUtf8, mockOption, mockStorageKey } from "./Utils.js";

describe("DirectoryClient", () => {

it("handles guest LLOs", async () => {
const api = mockApi();
const axiosFactory = mockAxiosFactory();
const client = new DirectoryClient(api, ENDPOINT, axiosFactory);

const legalOfficers = await client.getLegalOfficers();

expect(legalOfficers.length).toBe(2);

expect(legalOfficers[0].node).toBe(BASE_URL);
expect(legalOfficers[0].account).toEqual(ValidAccountId.polkadot(HOST_ADDRESS));
expect(legalOfficers[0].region).toBe(REGION_TYPE);
expect(legalOfficers[0].nodeId).toBe(PEER_ID);

expect(legalOfficers[1].node).toBe(BASE_URL);
expect(legalOfficers[1].account).toEqual(ValidAccountId.polkadot(GUEST_ADDRESS));
expect(legalOfficers[1].region).toBe(REGION_TYPE);
expect(legalOfficers[1].nodeId).toBe(PEER_ID);
});
});

const ENDPOINT = "https://test-directory.logion.network";

function mockApi(): LogionNodeApiClass {
const api = new Mock<LogionNodeApiClass>();
api.setup(instance => instance.polkadot.query.loAuthorityList.legalOfficerSet.entries()).returnsAsync([
[
mockStorageKey([mockCodecWithToString<AccountId32>(HOST_ADDRESS)]),
mockOption<PalletLoAuthorityListLegalOfficerData>(mockHost()),
],
[
mockStorageKey([mockCodecWithToString<AccountId32>(GUEST_ADDRESS)]),
mockOption<PalletLoAuthorityListLegalOfficerData>(mockGuest()),
]
]);
api.setup(instance => instance.adapters.fromLogionRuntimeRegion(REGION)).returns("Europe");
return api.object();
}

const HOST_ADDRESS = "vQvWaxNDdzuX5N3qSvGMtjdHcQdw1TAcPNgx4S1Utd3MTxYeN";
const GUEST_ADDRESS = "vQvZF2YMgKuQhzfF7T3xDjHjuEmcPSUVEoUDPy1mzuSXzFgca";
const REGION_TYPE: Region = "Europe";
const REGION = mockCodecWithToString<LogionRuntimeRegion>(REGION_TYPE);

function mockAxiosFactory(): AxiosFactory {
const axios = mockAxios();
const factory = new Mock<AxiosFactory>();
factory.setup(instance => instance.buildAxiosInstance(ENDPOINT, undefined)).returns(axios);
return factory.object();
}

function mockAxios(): AxiosInstance {
const axios = new Mock<AxiosInstance>();
const response = new Mock<AxiosResponse>();
const legalOfficers: DirectoryLegalOfficer[] = [
{
address: HOST_ADDRESS,
additionalDetails: "",
postalAddress: EMPTY_POSTAL_ADDRESS,
userIdentity: EMPTY_USER_IDENTITY,
},
{
address: GUEST_ADDRESS,
additionalDetails: "",
postalAddress: EMPTY_POSTAL_ADDRESS,
userIdentity: EMPTY_USER_IDENTITY,
},
];
response.setup(instance => instance.data.legalOfficers).returns(legalOfficers);
axios.setup(instance => instance.get("/api/legal-officer")).returnsAsync(response.object());
return axios.object();
}

function mockHost(): PalletLoAuthorityListLegalOfficerData {
const host = new Mock<PalletLoAuthorityListLegalOfficerData>();
host.setup(instance => instance.isHost).returns(true);
const hostData = new Mock<PalletLoAuthorityListHostData>();
hostData.setup(instance => instance.baseUrl)
.returns(mockOption<Bytes>(mockCodecWithToUtf8<Bytes>(BASE_URL)));
hostData.setup(instance => instance.nodeId)
.returns(mockOption<Bytes>(mockCodecWithToString<Bytes>(PEER_ID)));
hostData.setup(instance => instance.region)
.returns(REGION);
host.setup(instance => instance.asHost).returns(hostData.object());
return host.object();
}

const BASE_URL = "https://test-node.logion.network";
const PEER_ID = "12D3KooWJvyP3VJYymTqG7eH4PM5rN4T2agk5cdNCfNymAqwqcvZ";

function mockGuest(): PalletLoAuthorityListLegalOfficerData {
const guest = new Mock<PalletLoAuthorityListLegalOfficerData>();
guest.setup(instance => instance.isHost).returns(false);
const guestData = new Mock<PalletLoAuthorityListGuestData>();
guestData.setup(instance => instance.hostId).returns(mockCodecWithToString(HOST_ADDRESS));
guest.setup(instance => instance.asGuest).returns(guestData.object());
return guest.object();
}
25 changes: 24 additions & 1 deletion packages/client/test/Utils.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,9 @@
import { DateTime } from "luxon";
import { ApiPromise } from "@polkadot/api";
import type { StorageKey } from '@polkadot/types';
import { SubmittableExtrinsic } from '@polkadot/api/promise/types';
import { Option, Vec, bool } from "@polkadot/types-codec";
import type { Codec } from '@polkadot/types-codec/types';
import type { Codec, AnyTuple } from '@polkadot/types-codec/types';

import {
AccountTokens,
Expand Down Expand Up @@ -285,3 +286,25 @@ export class MockFile extends File {
}

export const MOCK_FILE = new MockFile();

export function mockStorageKey<T extends AnyTuple = AnyTuple>(args: T): StorageKey<T> {
const key = new Mock<StorageKey<T>>();
key.setup(instance => instance.args).returns(args);
return key.object();
}

export const EMPTY_POSTAL_ADDRESS: LegalOfficerPostalAddress = {
city: "",
company: "",
country: "",
line1: "",
line2: "",
postalCode: "",
};

export const EMPTY_USER_IDENTITY: UserIdentity = {
email: "",
firstName: "",
lastName: "",
phoneNumber: "",
};
12 changes: 9 additions & 3 deletions packages/node-api/integration/LoAuthorityList.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,18 +3,24 @@ import { setup, signAndSend } from "./Util.js";

export async function addGuestLegalOfficer() {
const { alice, api } = await setup();
const dave = api.adapters.getValidPolkadotAccountId("5DAAnrj7VHTznn2AWBemMuyBwZWs6FNFjdyVXUeYum3PTXFy").address;
const dave = api.adapters.getValidPolkadotAccountId("5DAAnrj7VHTznn2AWBemMuyBwZWs6FNFjdyVXUeYum3PTXFy");

const extrinsic = api.polkadot.tx.loAuthorityList.addLegalOfficer(dave, {
const extrinsic = api.polkadot.tx.loAuthorityList.addLegalOfficer(dave.address, {
Guest: alice.address,
});
const sudoExtrinsic = api.polkadot.tx.sudo.sudo(extrinsic);
const result = await signAndSend(alice, sudoExtrinsic);

expect(result.dispatchError).not.toBeDefined();
const entry = await api.polkadot.query.loAuthorityList.legalOfficerSet(dave);
const entry = await api.polkadot.query.loAuthorityList.legalOfficerSet(dave.address);
expect(entry.isSome).toBe(true);
const host = entry.unwrap().asGuest;
expect(host.hostId.toString()).toBe(alice.address);

const legalOfficerData = await api.queries.getLegalOfficerData(dave);
expect(legalOfficerData.isHost).toBe(false);
expect(legalOfficerData.hostData).toBeDefined();
expect(legalOfficerData.hostAccount).toEqual(ValidAccountId.polkadot(alice.address));
}

export async function updateHostLegalOfficer() {
Expand Down
2 changes: 1 addition & 1 deletion packages/node-api/package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@logion/node-api",
"version": "0.31.0",
"version": "0.31.1",
"description": "logion API",
"main": "dist/cjs/index.js",
"module": "dist/esm/index.js",
Expand Down
2 changes: 1 addition & 1 deletion packages/node-api/src/Queries.ts
Original file line number Diff line number Diff line change
Expand Up @@ -157,7 +157,7 @@ export class Queries {
guests: await this.getGuestsOf(accountId),
};
} else {
const hostAddress = someLegalOfficerData.asGuest.toString();
const hostAddress = someLegalOfficerData.asGuest.hostId.toString();
const hostLegalOfficerData = await this.api.query.loAuthorityList.legalOfficerSet(hostAddress);
const hostData = this.adapters.toHostData(hostLegalOfficerData.unwrap());
onchainSettings = {
Expand Down

0 comments on commit 2f78ec1

Please sign in to comment.