UNPKG

nationstates.js

Version:

A wrapper to interact with the NationStates API.

278 lines 10.6 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.RequestBuilder = void 0; const client_1 = require("../client"); const xml_parser_1 = require("../xml_parser"); const fetch = require('node-fetch'); /** * Build a request to your specifications! Usage: * - (1) Define the architecture of a https request before it sent to the client. * - (2) Access and modify the response of a request. * @example const request = await new RequestBuilder(api).addNation('testlandia').execute(); * console.log(request.body); * @param {client} api - The client instance to use. Used to enforce the rate limit and user agent. */ class RequestBuilder { constructor(client) { this._urlObj = new URL('https://www.nationstates.net/cgi-bin/api.cgi'); this._shards = []; /** * client is deprecated. Use Client instead. * TODO: Remove this in the future. */ if (client instanceof client_1.API) { this.client = new client_1.Client(client.userAgent, client.rateLimit); } else if (client instanceof client_1.Client) { this.client = client; } else { throw new Error('Invalid client. Must be an instance of client or Client. client is deprecated!'); } } /** * Returns full node-fetch request and other meta-data created by the client wrapper. * Typical usage would to analyze the request for any errors. * @example console.log(request.fetchResponse); */ get responseData() { // Verify if response is undefined. if (!this.response) throw new Error('No response found. Send a request first using execute()!'); return this.response; } /** * Returns the response status code and status boolean from the node-fetch response as an object. * @example console.log(request.responseStatus.statusCode); */ get responseStatus() { // Verify if response is undefined. if (!this.response) throw new Error('No response found. Send a request first using execute()!'); return { code: this.response.statusCode, bool: this.response.statusBool }; } /** * Returns the current body of the last node-fetch request associated with this instance. * @example console.log(request.body); */ get body() { // Verifies if a response has been received. if (!this.response) throw new Error('No body found. Have you sent and awaited your request via execute()?'); // If the body is a number, convert the string to a number and return it, else return the body as is. return !isNaN(this.response.body) ? parseInt(this.response.body) : this.response.body; } /** * Returns the current JS object of the last node-fetch request associated with this instance. * You must convert the body to a JS object before using this method via convertBodyToJSON(). * @example request.convertToJSON(); * console.log(request.js); */ get js() { // Verify if the response has been converted to js. if (!this.response.js) throw new Error('No JSON found. Try toJS() first and make sure a request has been sent.'); return this.response.js; } /** * Returns the current shards of a RequestBuilder object as a single string or as an array of strings. * @example console.log(request.shards); */ get shards() { // Verifies if shards have been added. if (!this._shards) throw new Error('No shards have been added.'); // If there is only a single shard, return it. if (this._shards.length === 1) return this._shards[0]; // Returns the array of shards. return this._shards; } /** * Builds and then returns the URL for which the request will be sent. * Serves the purpose of ensuring proper URL encoding. */ get href() { let url = this._urlObj.origin + this._urlObj.pathname + '?'; // https://www.nationstates.net/cgi-bin/api.cgi?. let params = []; this._urlObj.searchParams.forEach((value, key) => { if (key === 'q') params.push(`${key}=${decodeURIComponent(value)}`); else params.push(`${key}=${encodeURIComponent(value)}`); }); return url + params.join('&'); } /** * Adds the nation to the url parameters. * @example .addNation('Testlandia') adds 'nation=Testlandia' to the url. * @param name - The name of the nation from which data is retrieved. */ addNation(name) { if (name.length < 3 || !name.match(/^[\w\-\s]+$/) || // Must be alphanumeric, or only alpha, or only numeric name.slice(-1) === ' ' || typeof (name) !== 'string') { throw new Error(`You submitted an invalid nation name: ${name}`); } this._urlObj.searchParams.append('nation', name); return this; } /** * Adds the region to the url parameters. * @example .addRegion('The South Pacific') adds 'region=The%20South%20Pacific' to the url. * @param name */ addRegion(name) { this._urlObj.searchParams.append('region', name); return this; } /** * Adds a council ID to the url parameters. * @example .addCouncil(1) adds 'wa=1' to the url. * @param id */ addCouncilID(id) { if (typeof (id) !== 'number') throw new Error(`You submitted an invalid council ID: ${id}. Must be a number.`); if (id > 2 || id < 0) throw new Error('Invalid ID. 1 = GA, 2 = SC.'); this._urlObj.searchParams.append('wa', id.toString()); return this; } /** * Adds a resolution ID to the url parameters. * @example .addResolutionID(22) adds 'id=22' to the url parameters. * @param id */ addResolutionID(id) { if (typeof (id) !== 'number') throw new Error(`You submitted an invalid resolution ID: ${id}. Must be a number.`); this._urlObj.searchParams.append('id', id.toString()); return this; } /** * Add shards to the url parameters after the 'q=' parameter. * @example .addShards('flag') adds 'q=Testlandia' to the url. * @example .addShards([ 'flag', 'population' ]) adds 'q=flag+population' to the url. * @param shards */ addShards(shards) { switch (typeof (shards)) { case "string": this._shards.push(shards); break; case "object": shards.forEach(shard => this._shards.push(shard)); break; default: throw new Error("Invalid type of _shards. Must be a string or an array of strings."); } if (this._urlObj.searchParams.has('q')) this._urlObj.searchParams.delete('q'); this._urlObj.searchParams.append('q', this._shards.join('+')); return this; } /** * Appends the given parameters to the url with the defined key and value. * @example .addCustomParam('key', 'value') adds 'key=value' to the url. * @param key * @param value */ addCustomParam(key, value) { this._urlObj.searchParams.append(key.toString(), value.toString()); return this; } /** * Removes all shards from the RequestBuilder object and its associated URL. * @example new RequestBuilder(api).addShards('numnations').removeShards() */ deleteAllShards() { this._urlObj.searchParams.delete('q'); this._shards.length = 0; } /** * Enforces the rate-limit by calculating time-to-wait and then waiting for the specified amount of time. */ async execRateLimit() { const difference = Date.now() - this.client.lastRequestMs; if (this.client.rateLimit > difference) { const timeToWait = this.client.rateLimit - difference; // Forcefully stop JavaScript execution. await new Promise(resolve => setTimeout(resolve, timeToWait)); } } /** * Executes the request and saves the response to the RequestBuilder object. * Retrieve after awaiting it via .response, .body, or convert it to a JS object with convertToJSON(); * @example const req = await new RequestBuilder(api).addNation('Testlandia').execute() */ async execute() { await this.execRateLimit(); try { const res = await fetch(this.href, { headers: { 'User-Agent': this.client.userAgent } }); await this.logRequest(res); } catch (err) { throw new Error(`Error sending request: ${err}`); } return this; } /** * ⚠️ Deprecated! Use execute() instead. */ async sendRequestAsync() { console.log('WARNING: sendRequestAsync() is deprecated. Use execute() instead.'); await this.execute(); } /** * Saves the node-fetch response to the response object within the instance. * @param res * @protected */ async logRequest(res) { this.client.lastRequestMs = Date.now(); this.response = { fetchResponse: res, unixTime: Date.now(), statusCode: res.status, statusBool: res.ok, body: await res.text() }; } async toJS() { if (!this.response.body) throw new Error("No response body could be found. You can examine the response body by doing: "); try { this.response.js = await (0, xml_parser_1.parseXml)(this.response.body); } catch (err) { throw new Error(err); } return this; } /** * ⚠️ Deprecated! Use execute() instead. */ async convertToJSAsync() { console.log('WARNING: convertToJSAsync() is deprecated. Use execute() instead.'); await this.toJS(); } /** * Resets the url and shards to the default. Protected to allow extending into the NSMethods class. * End-users wishing to reset their URL should simply create a new RequestBuilder object instead. * @protected */ resetURL() { this._urlObj = new URL('https://www.nationstates.net/cgi-bin/api.cgi'); this._shards = []; return this; } } exports.RequestBuilder = RequestBuilder; //# sourceMappingURL=request_builder.js.map