UNPKG

@iota-pico/api

Version:
316 lines 25.9 kB
Object.defineProperty(exports, "__esModule", { value: true }); const arrayHelper_1 = require("@iota-pico/core/dist/helpers/arrayHelper"); const numberHelper_1 = require("@iota-pico/core/dist/helpers/numberHelper"); const objectHelper_1 = require("@iota-pico/core/dist/helpers/objectHelper"); const stringHelper_1 = require("@iota-pico/core/dist/helpers/stringHelper"); const nullLogger_1 = require("@iota-pico/core/dist/loggers/nullLogger"); const apiError_1 = require("../error/apiError"); /** * Default implementation of an api client. */ class ApiClient { /** * Create an instance of ApiClient. * @param networkClient The network client to communicate through. * @param apiVersion The API version to send with the requests * @param additionalHeaders Extra headers to send with the requests. * @param logger Logger to send communication info to. */ constructor(networkClient, apiVersion = "1", additionalHeaders, logger) { if (objectHelper_1.ObjectHelper.isEmpty(networkClient)) { throw new apiError_1.ApiError("The networkClient must be defined"); } if (stringHelper_1.StringHelper.isEmpty(apiVersion)) { throw new apiError_1.ApiError("The apiVersion must not be empty"); } this._networkClient = networkClient; this._apiVersion = apiVersion; this._additionalHeaders = additionalHeaders; this._logger = logger || new nullLogger_1.NullLogger(); } /** * Returns information about your node. * @returns Promise which resolves to the getNodeInfo response object or rejects with error. */ async getNodeInfo() { return this.sendCommand("getNodeInfo", {}); } /** * Returns the set of neighbors you are connected with, as well as their activity count. * The activity counter is reset after restarting IRI. * @returns Promise which resolves to the getNeighbors response object or rejects with error. */ async getNeighbors() { return this.sendCommand("getNeighbors", {}); } /** * Add a list of neighbors to your node. It should be noted that this is only temporary, * and the added neighbors will be removed from your set of neighbors after you relaunch IRI. * @param request The add neighbours request object. * @returns Promise which resolves to the addNeighbors response object or rejects with error. */ async addNeighbors(request) { if (objectHelper_1.ObjectHelper.isEmpty(request)) { throw new apiError_1.ApiError("The request must be defined"); } if (arrayHelper_1.ArrayHelper.isEmpty(request.uris)) { throw new apiError_1.ApiError("The request.uris must not be empty"); } return this.sendCommand("addNeighbors", request); } /** * Removes a list of neighbors from your node. This is only temporary, and if you have your * neighbors added via the command line, they will be retained after you restart your node. * @param request The remove neighbours request object. * @returns Promise which resolves to the removeNeighbors response object or rejects with error. */ async removeNeighbors(request) { if (objectHelper_1.ObjectHelper.isEmpty(request)) { throw new apiError_1.ApiError("The request must be defined"); } if (arrayHelper_1.ArrayHelper.isEmpty(request.uris)) { throw new apiError_1.ApiError("The request.uris must not be empty"); } return this.sendCommand("removeNeighbors", request); } /** * Returns the list of tips. * @returns Promise which resolves to the getTips response object or rejects with error. */ async getTips() { return this.sendCommand("getTips", {}); } /** * Find the transactions which match the specified input and return. All input values are lists, * for which a list of return values (transaction hashes), in the same order, is returned for all * individual elements. The input fields can either be bundles, addresses, tags or approvees. * Using multiple of these input fields returns the intersection of the values. * @param request The find transactions request object. * @returns Promise which resolves to the findTransactions response object or rejects with error. */ async findTransactions(request) { if (objectHelper_1.ObjectHelper.isEmpty(request)) { throw new apiError_1.ApiError("The request must be defined"); } const bundlesEmpty = arrayHelper_1.ArrayHelper.isEmpty(request.bundles); const addressesEmpty = arrayHelper_1.ArrayHelper.isEmpty(request.addresses); const tagsEmpty = arrayHelper_1.ArrayHelper.isEmpty(request.tags); const approveesEmpty = arrayHelper_1.ArrayHelper.isEmpty(request.approvees); if (bundlesEmpty && addressesEmpty && tagsEmpty && approveesEmpty) { throw new apiError_1.ApiError("One of the bundle, addresses, tags or approvees must not be empty"); } return this.sendCommand("findTransactions", request); } /** * Returns the raw transaction data (trytes) of a specific transaction. * These trytes can then be easily converted into the actual transaction object. * @param request The get trytes request object. * @returns Promise which resolves to the findTransactions response object or rejects with error. */ async getTrytes(request) { if (objectHelper_1.ObjectHelper.isEmpty(request)) { throw new apiError_1.ApiError("The request must be defined"); } if (arrayHelper_1.ArrayHelper.isEmpty(request.hashes)) { throw new apiError_1.ApiError("The request.hashes must not be empty"); } return this.sendCommand("getTrytes", request); } /** * Get the inclusion states of a set of transactions. This is for determining if a transaction * was accepted and confirmed by the network or not. You can search for multiple tips (and thus, * milestones) to get past inclusion states of transactions. * @param request The get inclusion states request object. * @returns Promise which resolves to the getInclusionStates response object or rejects with error. */ async getInclusionStates(request) { if (objectHelper_1.ObjectHelper.isEmpty(request)) { throw new apiError_1.ApiError("The request must be defined"); } if (arrayHelper_1.ArrayHelper.isEmpty(request.transactions)) { throw new apiError_1.ApiError("The request.transactions must not be empty"); } if (arrayHelper_1.ArrayHelper.isEmpty(request.tips)) { throw new apiError_1.ApiError("The request.tips must not be empty"); } return this.sendCommand("getInclusionStates", request); } /** * Returns the confirmed balance which a list of addresses have at the latest confirmed milestone. * In addition to the balances, it also returns the milestone as well as the index with which the * confirmed balance was determined. The balances is returned as a list in the same order as the * addresses were provided as input. * @param request The getBalances request object. * @returns Promise which resolves to the getBalances response object or rejects with error. */ async getBalances(request) { if (objectHelper_1.ObjectHelper.isEmpty(request)) { throw new apiError_1.ApiError("The request must be defined"); } if (arrayHelper_1.ArrayHelper.isEmpty(request.addresses)) { throw new apiError_1.ApiError("The request.addresses must not be empty"); } if (!numberHelper_1.NumberHelper.isInteger(request.threshold)) { throw new apiError_1.ApiError("The request.threshold must be a valid number"); } return this.sendCommand("getBalances", request); } /** * Tip selection which returns trunkTransaction and branchTransaction. The input value is depth, * which basically determines how many bundles to go back to for finding the transactions to approve. * The higher your depth value, the more "babysitting" you do for the network (as you have to confirm more transactions). * @param request The getTransactionsToApprove request object. * @returns Promise which resolves to the getTransactionsToApprove response object or rejects with error. */ async getTransactionsToApprove(request) { if (objectHelper_1.ObjectHelper.isEmpty(request)) { throw new apiError_1.ApiError("The request must be defined"); } if (!numberHelper_1.NumberHelper.isInteger(request.depth)) { throw new apiError_1.ApiError("The request.depth must be a valid number"); } if (!objectHelper_1.ObjectHelper.isEmpty(request.reference) && stringHelper_1.StringHelper.isEmpty(request.reference)) { throw new apiError_1.ApiError("The request.reference must be a non empty string"); } if (!objectHelper_1.ObjectHelper.isEmpty(request.numWalks) && !numberHelper_1.NumberHelper.isInteger(request.numWalks)) { throw new apiError_1.ApiError("The request.numWalks must be a valid number"); } return this.sendCommand("getTransactionsToApprove", request); } /** * Attaches the specified transactions (trytes) to the Tangle by doing Proof of Work. You need to supply * branchTransaction as well as trunkTransaction (basically the tips which you're going to validate and * reference with this transaction) - both of which you'll get through the getTransactionsToApprove API call. * @param request The attachToTangle request object. * @returns Promise which resolves to the attachToTangle response object or rejects with error. */ async attachToTangle(request) { if (objectHelper_1.ObjectHelper.isEmpty(request)) { throw new apiError_1.ApiError("The request must be defined"); } if (stringHelper_1.StringHelper.isEmpty(request.trunkTransaction)) { throw new apiError_1.ApiError("The request.trunkTransaction must not be empty"); } if (stringHelper_1.StringHelper.isEmpty(request.branchTransaction)) { throw new apiError_1.ApiError("The request.branchTransaction must not be empty"); } if (!numberHelper_1.NumberHelper.isInteger(request.minWeightMagnitude)) { throw new apiError_1.ApiError("The request.minWeightMagnitude must be a valid number"); } if (arrayHelper_1.ArrayHelper.isEmpty(request.trytes)) { throw new apiError_1.ApiError("The request.trytes must not be empty"); } return this.sendCommand("attachToTangle", request); } /** * Interrupts and completely aborts the attachToTangle process * @returns Promise which resolves with empty response object or rejects with error. */ async interruptAttachingToTangle() { return this.sendCommand("interruptAttachingToTangle", {}); } /** * Broadcast a list of transactions to all neighbors. The input trytes for this call are provided by attachToTangle. * @param request The broadcastTransactions request object. * @returns Promise which resolves with empty response object or rejects with error. */ async broadcastTransactions(request) { if (objectHelper_1.ObjectHelper.isEmpty(request)) { throw new apiError_1.ApiError("The request must be defined"); } if (arrayHelper_1.ArrayHelper.isEmpty(request.trytes)) { throw new apiError_1.ApiError("The request.trytes must not be empty"); } return this.sendCommand("broadcastTransactions", request); } /** * Store transactions into the local storage. The trytes to be used for this call are returned by attachToTangle. * @param request The storeTransactions request object. * @returns Promise which resolves with empty response object or rejects with error. */ async storeTransactions(request) { if (objectHelper_1.ObjectHelper.isEmpty(request)) { throw new apiError_1.ApiError("The request must be defined"); } if (arrayHelper_1.ArrayHelper.isEmpty(request.trytes)) { throw new apiError_1.ApiError("The request.trytes must not be empty"); } return this.sendCommand("storeTransactions", request); } /** * Get transactions with missing references. * @param request The getMissingTransactions request object. * @returns Promise which resolves to the getMissingTransactions response object or rejects with error. */ async getMissingTransactions() { return this.sendCommand("getMissingTransactions", {}); } /** * Check the consistency of tail hashes. * @param request The checkConsistency request object. * @returns Promise which resolves to the checkConsistency response object or rejects with error. */ async checkConsistency(request) { if (objectHelper_1.ObjectHelper.isEmpty(request)) { throw new apiError_1.ApiError("The request must be defined"); } if (arrayHelper_1.ArrayHelper.isEmpty(request.tails)) { throw new apiError_1.ApiError("The request.tails must not be empty"); } return this.sendCommand("checkConsistency", request); } /** * Have the requested addresses been spent from already. * @param request The wereAddressesSpentFrom request object. * @returns Promise which resolves to the wereAddressesSpentFrom response object or rejects with error. */ async wereAddressesSpentFrom(request) { if (objectHelper_1.ObjectHelper.isEmpty(request)) { throw new apiError_1.ApiError("The request must be defined"); } if (arrayHelper_1.ArrayHelper.isEmpty(request.addresses)) { throw new apiError_1.ApiError("The request.addresses must not be empty"); } return this.sendCommand("wereAddressesSpentFrom", request); } /* @internal */ async sendCommand(command, request) { this._logger.info(`===> ApiClient::${command}`, request); Object.defineProperty(request, "command", { value: command, enumerable: true }); return this._networkClient.postJson(request, undefined, this.createHeaders()) .then((response) => { this._logger.info(`===> ${command}`, response); return response; }) .catch((err) => { this._logger.error(`===> ${command} Error`, err); if (err.additional && err.additional.errorResponse) { try { const apiError = JSON.parse(err.additional.errorResponse); if (apiError.error) { delete err.additional.errorResponse; err.additional.apiError = apiError.error; } else if (apiError.exception) { delete err.additional.errorResponse; err.additional.apiError = apiError.exception; } } catch (e) { } } throw err; }); } /* @internal */ createHeaders() { const headers = this._additionalHeaders || {}; headers["X-IOTA-API-Version"] = this._apiVersion; return headers; } } exports.ApiClient = ApiClient; //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiYXBpQ2xpZW50LmpzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXMiOlsiLi4vLi4vc3JjL2NsaWVudC9hcGlDbGllbnQudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6IjtBQUFBLDBFQUF1RTtBQUN2RSw0RUFBeUU7QUFDekUsNEVBQXlFO0FBQ3pFLDRFQUF5RTtBQUd6RSx3RUFBcUU7QUFDckUsZ0RBQTZDO0FBOEI3Qzs7R0FFRztBQUNILE1BQWEsU0FBUztJQVVsQjs7Ozs7O09BTUc7SUFDSCxZQUFZLGFBQTZCLEVBQUUsYUFBcUIsR0FBRyxFQUFFLGlCQUFnRCxFQUFFLE1BQWdCO1FBQ25JLElBQUksMkJBQVksQ0FBQyxPQUFPLENBQUMsYUFBYSxDQUFDLEVBQUU7WUFDckMsTUFBTSxJQUFJLG1CQUFRLENBQUMsbUNBQW1DLENBQUMsQ0FBQztTQUMzRDtRQUNELElBQUksMkJBQVksQ0FBQyxPQUFPLENBQUMsVUFBVSxDQUFDLEVBQUU7WUFDbEMsTUFBTSxJQUFJLG1CQUFRLENBQUMsa0NBQWtDLENBQUMsQ0FBQztTQUMxRDtRQUNELElBQUksQ0FBQyxjQUFjLEdBQUcsYUFBYSxDQUFDO1FBQ3BDLElBQUksQ0FBQyxXQUFXLEdBQUcsVUFBVSxDQUFDO1FBQzlCLElBQUksQ0FBQyxrQkFBa0IsR0FBRyxpQkFBaUIsQ0FBQztRQUM1QyxJQUFJLENBQUMsT0FBTyxHQUFHLE1BQU0sSUFBSSxJQUFJLHVCQUFVLEVBQUUsQ0FBQztJQUM5QyxDQUFDO0lBRUQ7OztPQUdHO0lBQ0ksS0FBSyxDQUFDLFdBQVc7UUFDcEIsT0FBTyxJQUFJLENBQUMsV0FBVyxDQUEyQixhQUFhLEVBQUUsRUFBRSxDQUFDLENBQUM7SUFDekUsQ0FBQztJQUVEOzs7O09BSUc7SUFDSSxLQUFLLENBQUMsWUFBWTtRQUNyQixPQUFPLElBQUksQ0FBQyxXQUFXLENBQTRCLGNBQWMsRUFBRSxFQUFFLENBQUMsQ0FBQztJQUMzRSxDQUFDO0lBRUQ7Ozs7O09BS0c7SUFDSSxLQUFLLENBQUMsWUFB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