diff --git a/.eslintrc.js b/.eslintrc.js index f0269b070..c8127eef4 100644 --- a/.eslintrc.js +++ b/.eslintrc.js @@ -6,7 +6,7 @@ module.exports = { node: true, }, root: true, - ignorePatterns: ["docs", "lib", "src/typechain"], + ignorePatterns: ["docs", "lib", "coverage", "src/typechain"], reportUnusedDisableDirectives: true, parser: "@typescript-eslint/parser", plugins: ["@typescript-eslint", "import", "prettier"], diff --git a/.prettierignore b/.prettierignore index b884e148c..981af69c9 100644 --- a/.prettierignore +++ b/.prettierignore @@ -1,4 +1,5 @@ lib docs .nyc_output +coverage src/typechain diff --git a/package.json b/package.json index 18c8c3566..a525520d9 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "opensea-js", - "version": "6.0.2", + "version": "6.0.3", "description": "JavaScript SDK for the OpenSea marketplace helps developers build new experiences using NFTs and our marketplace data!", "license": "MIT", "author": "OpenSea Developers", @@ -38,8 +38,7 @@ "types": "lib/index.d.ts", "dependencies": { "@opensea/seaport-js": "^2.0.0", - "ethers": "^5.7.2", - "isomorphic-unfetch": "^4.0.2" + "ethers": "^5.7.2" }, "devDependencies": { "@typechain/ethers-v5": "^11.0.0", diff --git a/src/api.ts b/src/api.ts index 990ded11c..f93d6cbd9 100644 --- a/src/api.ts +++ b/src/api.ts @@ -1,4 +1,4 @@ -import "isomorphic-unfetch"; +import { ethers } from "ethers"; import { API_BASE_MAINNET, API_BASE_TESTNET, API_PATH } from "./constants"; import { BuildOfferResponse, @@ -382,36 +382,27 @@ export class OpenSeaAPI { */ public async get(apiPath: string, query: object = {}): Promise { const qs = this.objectToSearchParams(query); - const url = `${apiPath}?${qs}`; - - const response = await this._fetch(url); - return response.json(); + const url = `${this.apiBaseUrl}${apiPath}?${qs}`; + return await this._fetch({ url }); } /** * POST JSON data to API, sending auth token in headers * @param apiPath Path to URL endpoint under API * @param body Data to send. Will be JSON.stringified - * @param opts RequestInit opts, similar to Fetch API. If it contains - * a body, it won't be stringified. + * @param opts ethers ConnectionInfo, similar to Fetch API. */ public async post( apiPath: string, body?: object, - opts: RequestInit = {} + opts?: ethers.utils.ConnectionInfo ): Promise { - const fetchOpts = { - method: "POST", - body: body ? JSON.stringify(body) : undefined, - headers: { - Accept: "application/json", - "Content-Type": "application/json", - }, + const options = { + url: `${this.apiBaseUrl}${apiPath}`, ...opts, }; - const response = await this._fetch(apiPath, fetchOpts); - return response.json(); + return await this._fetch(options, body); } private objectToSearchParams(params: object = {}) { @@ -430,86 +421,29 @@ export class OpenSeaAPI { /** * Get from an API Endpoint, sending auth token in headers - * @param apiPath Path to URL endpoint under API - * @param opts RequestInit opts, similar to Fetch API + * @param opts ethers ConnectionInfo, similar to Fetch API + * @param body Optional body to send. If set, will POST, otherwise GET */ - private async _fetch(apiPath: string, opts: RequestInit = {}) { - const apiBase = this.apiBaseUrl; - const apiKey = this.apiKey; - const finalUrl = apiBase + apiPath; - const finalOpts = { + private async _fetch(opts: ethers.utils.ConnectionInfo, body?: object) { + const headers = { + "x-app-id": "opensea-js", + ...(this.apiKey ? { "X-API-KEY": this.apiKey } : {}), + ...opts.headers, + }; + const req = { ...opts, - headers: { - ...(apiKey ? { "X-API-KEY": apiKey } : {}), - "x-app-id": "opensea-js", - ...(opts.headers ?? {}), - }, + headers, }; this.logger( - `Sending request: ${finalUrl} ${JSON.stringify(finalOpts).substr( - 0, - 100 - )}...` + `Sending request: ${opts.url} ${JSON.stringify(req).slice(0, 200)}...` ); - return fetch(finalUrl, finalOpts).then(async (res) => - this._handleApiResponse(res) + return await ethers.utils.fetchJson( + req, + body ? JSON.stringify(body) : undefined ); } - - private async _handleApiResponse(response: Response) { - if (response.ok) { - this.logger(`Got success: ${response.status}`); - return response; - } - - let result; - let errorMessage; - try { - result = await response.text(); - result = JSON.parse(result); - } catch { - // Result will be undefined or text - } - - this.logger(`Got error ${response.status}: ${JSON.stringify(result)}`); - - switch (response.status) { - case 400: - errorMessage = - result && result.errors - ? result.errors.join(", ") - : `Invalid request: ${JSON.stringify(result)}`; - break; - case 401: - case 403: - errorMessage = `Unauthorized. Full message was '${JSON.stringify( - result - )}'`; - break; - case 404: - errorMessage = `Not found. Full message was '${JSON.stringify( - result - )}'`; - break; - case 500: - errorMessage = `Internal server error. OpenSea has been alerted, but if the problem persists please contact us via Discord: https://discord.gg/opensea - full message was ${JSON.stringify( - result - )}`; - break; - case 503: - errorMessage = `Service unavailable. Please try again in a few minutes. If the problem persists please contact us via Discord: https://discord.gg/opensea - full message was ${JSON.stringify( - result - )}`; - break; - default: - errorMessage = `Message: ${JSON.stringify(result)}`; - break; - } - - throw new Error(`API Error ${response.status}: ${errorMessage}`); - } } function _throwOrContinue(error: unknown, retries: number) { diff --git a/test/api/api.spec.ts b/test/api/api.spec.ts index fd4863372..862dbb634 100644 --- a/test/api/api.spec.ts +++ b/test/api/api.spec.ts @@ -58,7 +58,7 @@ suite("API", () => { try { await mainAPI.get(`/asset/${BAYC_CONTRACT_ADDRESS}/202020202020`); } catch (error) { - assert.include((error as Error).message, "Not found"); + assert.include((error as Error).message, "status=404"); } }); });