import { HTTPResponseCodes } from '../../api-types/shared.ts'; import { BASE_URL } from '../util/constants.ts'; import { RequestMethod } from './rest.ts'; import { RestManager } from './restManager.ts'; export interface RestSendRequestOptions { url: string; method: RequestMethod; bucketId?: string; reject?: Function; respond?: Function; retryCount?: number; payload?: { headers: Record; body: string | FormData; }; } export async function sendRequest(rest: RestManager, options: RestSendRequestOptions): Promise { try { // CUSTOM HANDLER FOR USER TO LOG OR WHATEVER WHENEVER A FETCH IS MADE rest.debug(`[REST - fetching] URL: ${options.url} | ${JSON.stringify(options)}`); const newURL = options.url.startsWith(BASE_URL) ? options.url : `${BASE_URL}/v${rest.version}/${options.url}`; rest.debug(`[REST - url data] URL: ${newURL}`); const response = await fetch( new Request(newURL, { method: options.method, headers: options.payload?.headers, body: options.payload?.body, }), ); rest.debug(`[REST - fetched] URL: ${options.url} | ${JSON.stringify(options)}`); const bucketIdFromHeaders = rest.processRequestHeaders( rest, rest.simplifyUrl(options.url, options.method), response.headers, ); // SET THE BUCKET Id IF IT WAS PRESENT if (bucketIdFromHeaders) { options.bucketId = bucketIdFromHeaders; } if (response.status < 200 || response.status >= 400) { rest.debug( `[REST - httpError] Payload: ${JSON.stringify(options)} | Response: ${JSON.stringify(response)}`, ); let error = 'REQUEST_UNKNOWN_ERROR'; switch (response.status) { case HTTPResponseCodes.BadRequest: error = 'The options was improperly formatted, or the server couldn\'t understand it.'; break; case HTTPResponseCodes.Unauthorized: error = 'The Authorization header was missing or invalid.'; break; case HTTPResponseCodes.Forbidden: error = 'The Authorization token you passed did not have permission to the resource.'; break; case HTTPResponseCodes.NotFound: error = 'The resource at the location specified doesn\'t exist.'; break; case HTTPResponseCodes.MethodNotAllowed: error = 'The HTTP method used is not valid for the location specified.'; break; case HTTPResponseCodes.GatewayUnavailable: error = 'There was not a gateway available to process your options. Wait a bit and retry.'; break; } if ( rest.invalidRequestErrorStatuses.includes(response.status) && !(response.status === 429 && response.headers.get('X-RateLimit-Scope')) ) { // INCREMENT CURRENT INVALID REQUESTS ++rest.invalidRequests; if (!rest.invalidRequestsTimeoutId) { rest.invalidRequestsTimeoutId = setTimeout(() => { rest.debug(`[REST - processGlobalQueue] Resetting invalid optionss counter in setTimeout.`); rest.invalidRequests = 0; rest.invalidRequestsTimeoutId = 0; }, rest.invalidRequestsInterval); } } // If NOT rate limited remove from queue if (response.status !== 429) { options.reject?.({ ok: false, status: response.status, error, body: response.type ? JSON.stringify(await response.json()) : undefined, }); throw new Error( JSON.stringify({ ok: false, status: response.status, error, body: response.type ? JSON.stringify(await response.json()) : undefined, }), ); } else { if (options.retryCount && options.retryCount++ >= rest.maxRetryCount) { rest.debug(`[REST - RetriesMaxed] ${JSON.stringify(options)}`); // REMOVE ITEM FROM QUEUE TO PREVENT RETRY options.reject?.({ ok: false, status: response.status, error: 'The options was rate limited and it maxed out the retries limit.', }); // @ts-ignore Code should never reach here return; } } } // SOMETIMES DISCORD RETURNS AN EMPTY 204 RESPONSE THAT CAN'T BE MADE TO JSON if (response.status === 204) { rest.debug(`[REST - FetchSuccess] URL: ${options.url} | ${JSON.stringify(options)}`); options.respond?.({ ok: true, status: 204, }); // @ts-ignore 204 will be void return; } else { // CONVERT THE RESPONSE TO JSON const json = JSON.stringify(await response.json()); rest.debug(`[REST - fetchSuccess] ${JSON.stringify(options)}`); options.respond?.({ ok: true, status: 200, body: json, }); return JSON.parse(json); } } catch (error) { // SOMETHING WENT WRONG, LOG AND RESPOND WITH ERROR rest.debug(`[REST - fetchFailed] Payload: ${JSON.stringify(options)} | Error: ${error}`); options.reject?.({ ok: false, status: 599, error: 'Internal Proxy Error', }); throw new Error('Something went wrong in sendRequest', { cause: error, }); } }