diff --git a/package.json b/package.json index 6f36aba..ac6a774 100644 --- a/package.json +++ b/package.json @@ -1,5 +1,5 @@ { - "name": "del-osx", + "name": "del-sdk", "version": "1.0.0", "description": "JS SDK for links e-mail and wallet addresses", "repository": "https://github.com/bosagora/del-sdk", diff --git a/packages/client/package.json b/packages/client/package.json index 3f44e58..3a8e314 100644 --- a/packages/client/package.json +++ b/packages/client/package.json @@ -75,7 +75,8 @@ "@ethersproject/wallet": "^5.7.0", "del-osx-lib": "^1.0.6", "del-sdk-common": "^1.0.0", - "dotenv": "^16.3.1" + "dotenv": "^16.3.1", + "unfetch": "^5.0.0" }, "jest": { "testEnvironment": "../../test-environment.js", diff --git a/packages/client/src/client-common/interfaces/common.ts b/packages/client/src/client-common/interfaces/common.ts index 8fa7b58..1760361 100644 --- a/packages/client/src/client-common/interfaces/common.ts +++ b/packages/client/src/client-common/interfaces/common.ts @@ -3,3 +3,12 @@ export type SupportedNetworks = typeof SupportedNetworksArray[number]; export type NetworkDeployment = { LinkCollection: string; }; + +export type GenericRecord = Record; + +export interface IHttpConfig { + /** IPFS Cluster URL */ + url: URL; + /** Additional headers to be included with requests */ + headers?: Record; +} diff --git a/packages/client/src/client-common/interfaces/network.ts b/packages/client/src/client-common/interfaces/network.ts new file mode 100644 index 0000000..1f5a8be --- /dev/null +++ b/packages/client/src/client-common/interfaces/network.ts @@ -0,0 +1,45 @@ +import fetch, { UnfetchResponse } from "unfetch"; +import { GenericRecord, IHttpConfig } from "./common"; +import { InvalidResponseError } from "../../utils/errors"; + +export namespace Network { + /** + * Performs a request and returns a JSON object with the response + */ + + export async function get(config: IHttpConfig, path: string, data?: GenericRecord) { + const { url, headers } = config; + const endpoint: URL = new URL(path, url); + for (const [key, value] of Object.entries(data ?? {})) { + if (value != null) { + endpoint.searchParams.set(key, String(value)); + } + } + const response: UnfetchResponse = await fetch(endpoint.href, { + method: "GET", + headers + }); + if (!response.ok) { + throw new InvalidResponseError(response); + } + return response.json(); + } + + export async function post(config: IHttpConfig, path: string, data?: any) { + const { url, headers } = config; + const endpoint: URL = new URL(path, url); + const response: UnfetchResponse = await fetch(endpoint.href, { + method: "POST", + headers: { + "Content-Type": "application/json", + ...headers + }, + body: JSON.stringify(data) + }); + + if (!response.ok) { + throw new InvalidResponseError(response); + } + return response.json(); + } +} diff --git a/packages/client/src/interfaces.ts b/packages/client/src/interfaces.ts index dab63c7..97b0f4a 100644 --- a/packages/client/src/interfaces.ts +++ b/packages/client/src/interfaces.ts @@ -7,6 +7,7 @@ export interface IClientMethods extends IClientCore { toAddress: (email: string) => Promise; toEmail: (wallet: string) => Promise; nonceOf: (wallet: string) => Promise; + getValidators: () => Promise; } export interface IClient { @@ -29,6 +30,13 @@ export type NonceOfParams = { wallet: string; }; +export type ValidatorInfoValue = { + address: string; + index: number; + endpoint: string; + status: number; +}; + export type AddRequestValue = | { key: AddRequestSteps.ADDING; txHash: string } | { diff --git a/packages/client/src/internal/client/methods.ts b/packages/client/src/internal/client/methods.ts index 1a7e1a8..5662681 100644 --- a/packages/client/src/internal/client/methods.ts +++ b/packages/client/src/internal/client/methods.ts @@ -1,7 +1,7 @@ import { LinkCollection__factory } from "del-osx-lib"; import { NoProviderError, NoSignerError } from "del-sdk-common"; -import { AddRequestSteps, AddRequestValue, IClientMethods } from "../../interfaces"; +import { AddRequestSteps, AddRequestValue, IClientMethods, ValidatorInfoValue } from "../../interfaces"; import { ClientCore, Context } from "../../client-common"; import { ContractUtils } from "../../utils/ContractUtils"; @@ -120,4 +120,24 @@ export class ClientMethods extends ClientCore implements IClientMethods { if (await contract.isAvailable(id)) return id; } } + + public async getValidators(): Promise { + const signer = this.web3.getConnectedSigner(); + if (!signer) { + throw new NoSignerError(); + } else if (!signer.provider) { + throw new NoProviderError(); + } + + const contract = LinkCollection__factory.connect(this.web3.getLinkCollectionAddress(), signer); + const validators = await contract.getValidators() + return validators.map(m => { + return { + address: m.validator, + index: m.index.toNumber(), + endpoint: m.endpoint, + status: m.status + } + }) + } } diff --git a/packages/client/src/utils/errors.ts b/packages/client/src/utils/errors.ts new file mode 100644 index 0000000..c65c17f --- /dev/null +++ b/packages/client/src/utils/errors.ts @@ -0,0 +1,57 @@ +import { UnfetchResponse } from "unfetch"; + +export class InvalidEmailParamError extends Error { + constructor() { + super("The param does not email"); + } +} + +export class MismatchApproveAddressError extends Error { + constructor() { + super("Customer and approver mismatch"); + } +} + +export class UnregisteredEmailError extends Error { + constructor() { + super("Unregistered email error"); + } +} +export class InsufficientBalanceError extends Error { + constructor() { + super("Insufficient balance error"); + } +} + +export class NoHttpModuleError extends Error { + constructor() { + super("A Http Module is needed"); + } +} +export class ClientError extends Error { + public response: UnfetchResponse; + constructor(res: UnfetchResponse) { + super(res.statusText); + this.name = "ClientError"; + this.response = res; + } +} + +export class InvalidResponseError extends ClientError { + constructor(res: UnfetchResponse) { + super(res); + this.message = "Invalid response"; + } +} +export class MissingBodyeError extends ClientError { + constructor(res: UnfetchResponse) { + super(res); + this.message = "Missing response body"; + } +} +export class BodyParseError extends ClientError { + constructor(res: UnfetchResponse) { + super(res); + this.message = "Error parsing body"; + } +} diff --git a/packages/client/src/utils/network/ErrorTypes.ts b/packages/client/src/utils/network/ErrorTypes.ts new file mode 100644 index 0000000..fefa94f --- /dev/null +++ b/packages/client/src/utils/network/ErrorTypes.ts @@ -0,0 +1,111 @@ +/** + * Contains definition for error types + * + * Copyright: + * Copyright (c) 2022 BOSAGORA Foundation All rights reserved. + * + * License: + * MIT License. See LICENSE for details. + */ + +/** + * The class used when a network error occurs + */ +export class NetworkError extends Error { + /** + * The status code + */ + public status: number; + + /** + * The status text + */ + public statusText: string; + + /** + * The message of response + */ + public statusMessage: string; + + /** + * Constructor + * @param status The status code + * @param statusText The status text + * @param statusMessage The message of response + */ + constructor(status: number, statusText: string, statusMessage: string) { + super(statusText); + this.name = "NetworkError"; + this.status = status; + this.statusText = statusText; + this.statusMessage = statusMessage; + } +} + +/** + * When status code is 404 + */ +export class NotFoundError extends NetworkError { + /** + * Constructor + * @param status The status code + * @param statusText The status text + * @param statusMessage The message of response + */ + constructor(status: number, statusText: string, statusMessage: string) { + super(status, statusText, statusMessage); + this.name = "NotFoundError"; + } +} + +/** + * When status code is 400 + */ +export class BadRequestError extends NetworkError { + /** + * Constructor + * @param status The status code + * @param statusText The status text + * @param statusMessage The message of response + */ + constructor(status: number, statusText: string, statusMessage: string) { + super(status, statusText, statusMessage); + this.name = "BadRequestError"; + } +} + +/** + * It is a function that handles errors that occur during communication + * with a server for easy use. + * @param error This is why the error occurred + * @returns The instance of Error + */ +export function handleNetworkError(error: any): Error { + if ( + error.response !== undefined && + error.response.status !== undefined && + error.response.statusText !== undefined + ) { + let statusMessage: string; + if (error.response.data !== undefined) { + if (typeof error.response.data === "string") statusMessage = error.response.data; + else if (typeof error.response.data === "object" && error.response.data.statusMessage !== undefined) + statusMessage = error.response.data.statusMessage; + else if (typeof error.response.data === "object" && error.response.data.errorMessage !== undefined) + statusMessage = error.response.data.errorMessage; + else statusMessage = error.response.data.toString(); + } else statusMessage = ""; + + switch (error.response.status) { + case 400: + return new BadRequestError(error.response.status, error.response.statusText, statusMessage); + case 404: + return new NotFoundError(error.response.status, error.response.statusText, statusMessage); + default: + return new NetworkError(error.response.status, error.response.statusText, statusMessage); + } + } else { + if (error.message !== undefined) return new Error(error.message); + else return new Error("An unknown error has occurred."); + } +} diff --git a/packages/client/test/methods.test.ts b/packages/client/test/methods.test.ts index b23d662..9e7022c 100644 --- a/packages/client/test/methods.test.ts +++ b/packages/client/test/methods.test.ts @@ -76,6 +76,11 @@ describe("SDK Client", () => { await expect(await client.methods.toAddress(emailHash)).toEqual(address); await expect(await client.methods.toEmail(address)).toEqual(emailHash); }); + + it("getValidators", async () => { + const infos = await client.methods.getValidators(); + console.log(infos); + }); }); }); }); diff --git a/yarn.lock b/yarn.lock index 14ceb53..fea968f 100644 --- a/yarn.lock +++ b/yarn.lock @@ -9038,6 +9038,11 @@ unbox-primitive@^1.0.2: has-symbols "^1.0.3" which-boxed-primitive "^1.0.2" +unfetch@^5.0.0: + version "5.0.0" + resolved "https://registry.yarnpkg.com/unfetch/-/unfetch-5.0.0.tgz#8a5b6e5779ebe4dde0049f7d7a81d4a1af99d142" + integrity sha512-3xM2c89siXg0nHvlmYsQ2zkLASvVMBisZm5lF3gFDqfF2xonNStDJyMpvaOBe0a1Edxmqrf2E0HBdmy9QyZaeg== + unicode-canonical-property-names-ecmascript@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/unicode-canonical-property-names-ecmascript/-/unicode-canonical-property-names-ecmascript-2.0.0.tgz#301acdc525631670d39f6146e0e77ff6bbdebddc"