Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Simplify nodejs client and wallet #1119

Closed
wants to merge 21 commits into from
Closed
Show file tree
Hide file tree
Changes from 17 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions bindings/nodejs-old/src/message_handler.rs
Original file line number Diff line number Diff line change
Expand Up @@ -138,7 +138,7 @@ pub fn send_message(mut cx: FunctionContext) -> JsResult<JsUndefined> {
Ok(())
});
} else {
panic!("Message handler got destroyed")
panic!("Message handler was destroyed")
}
});

Expand Down Expand Up @@ -169,7 +169,7 @@ pub fn listen(mut cx: FunctionContext) -> JsResult<JsUndefined> {
})
.await;
} else {
panic!("Message handler got destroyed")
panic!("Message handler was destroyed")
}
});

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -43,11 +43,11 @@ async function run() {
// Get Alias address
const aliasAddress = Utils.aliasIdToBech32(
aliasId,
await (await wallet.getClient()).getBech32Hrp(),
await wallet.getClient().getBech32Hrp(),
);
const faucetResponse = await (
await wallet.getClient()
).requestFundsFromFaucet(faucetUrl, aliasAddress);
const faucetResponse = await wallet
.getClient()
.requestFundsFromFaucet(faucetUrl, aliasAddress);
console.log(faucetResponse);

await new Promise((resolve) => setTimeout(resolve, 10000));
Expand Down
7 changes: 3 additions & 4 deletions bindings/nodejs/examples/how_tos/alias_wallet/transaction.ts
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,7 @@ async function run() {
// Get Alias address
const aliasAddress = Utils.aliasIdToBech32(
aliasId,
await (await wallet.getClient()).getBech32Hrp(),
await wallet.getClient().getBech32Hrp(),
);

// Find first output unlockable by the alias address
Expand All @@ -59,9 +59,8 @@ async function run() {
address: aliasAddress,
},
];
const input = (
await (await wallet.getClient()).basicOutputIds(queryParameters)
).items[0];
const input = (await wallet.getClient().basicOutputIds(queryParameters))
.items[0];

const params = [
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@ async function run() {
// Get the id we generated with `00_mint_issuer_nft`
const issuerNftId: NftId = process.argv[2];

const bech32Hrp = await (await wallet.getClient()).getBech32Hrp();
const bech32Hrp = await wallet.getClient().getBech32Hrp();
const issuer = Utils.nftIdToBech32(issuerNftId, bech32Hrp);

const nftMintParams = [];
Expand Down
2 changes: 1 addition & 1 deletion bindings/nodejs/examples/how_tos/nfts/mint_nft.ts
Original file line number Diff line number Diff line change
Expand Up @@ -74,7 +74,7 @@ async function run() {
console.log('Minted NFT 1');

// Build an NFT manually by using the `NftOutputBuilder`
const client = await wallet.getClient();
const client = wallet.getClient();

const hexAddress = Utils.bech32ToHex(senderAddress);
const output = await client.buildNftOutput({
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -29,9 +29,9 @@ async function run() {
const address = (await account.addresses())[0].address;
console.log(address);

const faucetResponse = await (
await wallet.getClient()
).requestFundsFromFaucet(faucetUrl, address);
const faucetResponse = await wallet
.getClient()
.requestFundsFromFaucet(faucetUrl, address);
console.log(faucetResponse);
} catch (error) {
console.error('Error: ', error);
Expand Down
32 changes: 19 additions & 13 deletions bindings/nodejs/lib/bindings.ts
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ const callClientMethodAsync = (
handler: ClientMethodHandler,
): Promise<string> =>
new Promise((resolve, reject) => {
callClientMethod(method, handler, (error: Error, result: string) => {
callClientMethod(method, handler, (error: any, result: string) => {
if (error) {
reject(error);
} else {
Expand All @@ -51,7 +51,7 @@ const callSecretManagerMethodAsync = (
callSecretManagerMethod(
method,
handler,
(error: Error, result: string) => {
(error: any, result: string) => {
if (error) {
reject(error);
} else {
Expand All @@ -75,23 +75,29 @@ const listenWalletAsync = (
callback: (error: Error, event: Event) => void,
handler: WalletMethodHandler,
): Promise<void> => {
listenWallet(
eventTypes,
function (err: any, data: string) {
const parsed = JSON.parse(data);
callback(err, new Event(parsed.accountIndex, parsed.event));
},
handler,
);
return Promise.resolve();
return new Promise((resolve) => {
listenWallet(
eventTypes,
function (err: any, data: string) {
const parsed = JSON.parse(data);
callback(
// Send back raw error instead of parsing
err,
new Event(parsed.accountIndex, parsed.event),
);
},
handler,
);
resolve();
});
};

const callWalletMethodAsync = (
method: string,
handler: WalletMethodHandler,
): Promise<string> =>
new Promise((resolve, reject) => {
callWalletMethod(method, handler, (error: Error, result: string) => {
callWalletMethod(method, handler, (error: any, result: string) => {
if (error) {
reject(error);
} else {
Expand All @@ -110,8 +116,8 @@ export {
callSecretManagerMethodAsync,
callUtilsMethod,
callWalletMethodAsync,
destroyWallet,
listenWalletAsync,
destroyWallet,
getClientFromWallet,
getSecretManagerFromWallet,
listenMqtt,
Expand Down
48 changes: 35 additions & 13 deletions bindings/nodejs/lib/client/client-method-handler.ts
Original file line number Diff line number Diff line change
@@ -1,10 +1,11 @@
// Copyright 2023 IOTA Stiftung
// SPDX-License-Identifier: Apache-2.0

import { errorHandle } from '..';
import {
callClientMethodAsync,
createClient,
listenMqtt,
listenMqtt as listenMqttRust,
destroyClient,
} from '../bindings';
import type { IClientOptions, __ClientMethods__ } from '../types/client';
Expand All @@ -19,16 +20,24 @@ export class ClientMethodHandler {
* @param options client options or a client method handler.
*/
constructor(options: IClientOptions | ClientMethodHandler) {
// The rust client object is not extensible
if (Object.isExtensible(options)) {
this.methodHandler = createClient(JSON.stringify(options));
} else {
this.methodHandler = options as ClientMethodHandler;
try {
// The rust client object is not extensible
if (Object.isExtensible(options)) {
this.methodHandler = createClient(JSON.stringify(options));
} else {
this.methodHandler = options as ClientMethodHandler;
}
} catch (error: any) {
throw errorHandle(error);
}
}

async destroy() {
return destroyClient(this.methodHandler);
destroy(): void {
try {
destroyClient(this.methodHandler);
} catch (error: any) {
throw errorHandle(error);
}
}

/**
Expand All @@ -39,9 +48,18 @@ export class ClientMethodHandler {
*/
async callMethod(method: __ClientMethods__): Promise<string> {
return callClientMethodAsync(
JSON.stringify(method),
// mapToObject is required to convert maps to array since they otherwise get serialized as `[{}]` even if not empty
JSON.stringify(method, function mapToObject(_key, value) {
if (value instanceof Map) {
return Object.fromEntries(value);
} else {
return value;
}
}),
this.methodHandler,
);
).catch((error: any) => {
throw errorHandle(error);
});
}

/**
Expand All @@ -50,10 +68,14 @@ export class ClientMethodHandler {
* @param topics The topics to listen to.
* @param callback The callback to be called when an MQTT event is received.
*/
async listen(
listenMqtt(
topics: string[],
callback: (error: Error, result: string) => void,
): Promise<void> {
return listenMqtt(topics, callback, this.methodHandler);
): void {
try {
listenMqttRust(topics, callback, this.methodHandler);
} catch (error: any) {
throw errorHandle(error);
}
}
}
10 changes: 5 additions & 5 deletions bindings/nodejs/lib/client/client.ts
Original file line number Diff line number Diff line change
Expand Up @@ -68,8 +68,8 @@ export class Client {
this.methodHandler = new ClientMethodHandler(options);
}

async destroy() {
return this.methodHandler.destroy();
destroy() {
this.methodHandler.destroy();
}

/**
Expand Down Expand Up @@ -1146,11 +1146,11 @@ export class Client {
*
* @param topics An array of MQTT topics to listen to.
*/
async listenMqtt(
listenMqtt(
topics: string[],
callback: (error: Error, result: string) => void,
): Promise<void> {
return this.methodHandler.listen(topics, callback);
): void {
return this.methodHandler.listenMqtt(topics, callback);
}

/**
Expand Down
41 changes: 41 additions & 0 deletions bindings/nodejs/lib/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -39,3 +39,44 @@ export * from './types';
export * from './utils';
export * from './wallet';
export * from './logger';

// For future reference to see what we return from rust as a serialized string
export type Result = {
// "error" | "panic" or other binding method response name
type: string;
payload: {
// All method names from types/bridge/__name__.name
// Or all variants of rust sdk Error type
type: string;
// If "ok", json payload
payload?: string;
// If !"ok", error
error?: string;
};
};

function errorHandle(error: any): Error {
if (error instanceof TypeError) {
// neon or other bindings lib related error
throw error;
} else if (error instanceof Error) {
try {
const err: Result = JSON.parse(error.message);
if (err.type == 'panic') {
return Error(err.payload.toString());
} else {
return Error(err.payload.error);
}
} catch (err: any) {
// json error, SyntaxError, we must have send a bad error
return error;
}
} else {
// Something bad happened! Make sure we dont double parse
//Possible scenarios:
// - "json to string error: x"
return TypeError(error);
}
}

export { errorHandle };
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
// Copyright 2021-2023 IOTA Stiftung
// SPDX-License-Identifier: Apache-2.0

import { errorHandle } from '..';
import {
callSecretManagerMethodAsync,
createSecretManager,
Expand All @@ -19,11 +20,17 @@ export class SecretManagerMethodHandler {
* @param options A secret manager type or a secret manager method handler.
*/
constructor(options: SecretManagerType | SecretManagerMethodHandler) {
// The rust secret manager object is not extensible
if (Object.isExtensible(options)) {
this.methodHandler = createSecretManager(JSON.stringify(options));
} else {
this.methodHandler = options as SecretManagerMethodHandler;
try {
// The rust secret manager object is not extensible
if (Object.isExtensible(options)) {
this.methodHandler = createSecretManager(
JSON.stringify(options),
);
} else {
this.methodHandler = options as SecretManagerMethodHandler;
}
} catch (error: any) {
throw errorHandle(error);
}
}

Expand All @@ -35,9 +42,18 @@ export class SecretManagerMethodHandler {
*/
async callMethod(method: __SecretManagerMethods__): Promise<string> {
return callSecretManagerMethodAsync(
JSON.stringify(method),
// mapToObject is required to convert maps to array since they otherwise get serialized as `[{}]` even if not empty
JSON.stringify(method, function mapToObject(_key, value) {
if (value instanceof Map) {
return Object.fromEntries(value);
} else {
return value;
}
}),
this.methodHandler,
);
).catch((error: any) => {
throw errorHandle(error);
});
}
}

Expand Down
Loading