fix: rest typings (#835)

* fix: rest typings

* fix: more typings
This commit is contained in:
Skillz4Killz
2021-04-12 11:18:13 -04:00
committed by GitHub
parent af3dbf1564
commit 78a894a4ab
9 changed files with 66 additions and 78 deletions

View File

@@ -16,7 +16,7 @@ export async function sendDirectMessage(
recipient_id: memberId,
}) as DMChannelCreatePayload;
const discordenoChannel = await structures.createDiscordenoChannel(
dmChannelData as unknown as ChannelCreatePayload,
dmChannelData as unknown as DiscordChannel,
);
// Recreate the channel and add it undert he users id
await cacheHandlers.set("channels", memberId, discordenoChannel);

View File

@@ -1,8 +1,12 @@
import { FileContent } from "../types/misc/file_content.ts";
import { USER_AGENT } from "../util/constants.ts";
import { rest } from "./rest.ts";
import { rest, RestPayload, RestRequest } from "./rest.ts";
/** Creates the request body and headers that are necessary to send a request. Will handle different types of methods and everything necessary for discord. */
export function createRequestBody(queuedRequest: QueuedRequest) {
export function createRequestBody(queuedRequest: {
request: RestRequest;
payload: RestPayload;
}) {
const headers: { [key: string]: string } = {
Authorization: rest.token,
"User-Agent": USER_AGENT,
@@ -16,7 +20,7 @@ export function createRequestBody(queuedRequest: QueuedRequest) {
// IF A REASON IS PROVIDED ENCODE IT IN HEADERS
if (queuedRequest.payload.body?.reason) {
headers["X-Audit-Log-Reason"] = encodeURIComponent(
queuedRequest.payload.body.reason,
queuedRequest.payload.body.reason as string
);
}
@@ -25,12 +29,12 @@ export function createRequestBody(queuedRequest: QueuedRequest) {
const form = new FormData();
form.append(
"file",
queuedRequest.payload.body.file.blob,
queuedRequest.payload.body.file.name,
(queuedRequest.payload.body.file as FileContent).blob,
(queuedRequest.payload.body.file as FileContent).name
);
form.append(
"payload_json",
JSON.stringify({ ...queuedRequest.payload.body, file: undefined }),
JSON.stringify({ ...queuedRequest.payload.body, file: undefined })
);
queuedRequest.payload.body.file = form;
} else if (
@@ -42,8 +46,9 @@ export function createRequestBody(queuedRequest: QueuedRequest) {
return {
headers,
body: queuedRequest.payload.body?.file ||
JSON.stringify(queuedRequest.payload.body),
body:
(queuedRequest.payload.body?.file ||
JSON.stringify(queuedRequest.payload.body)) as FormData | string,
method: queuedRequest.request.method.toUpperCase(),
};
}

View File

@@ -1,34 +0,0 @@
// SERVERLESS REST CLIENT THAT CAN WORK ACROSS SHARDS/WORKERS TO COMMUNICATE GLOBAL RATE LIMITS EASILY
import { rest } from "./rest.ts";
/** Handler function for every request. Converts to json, verified authorization & requirements and begins processing the request */
export async function handlePayload(
request: Request,
) {
// INSTANTLY IGNORE ANY REQUESTS THAT DON'T HAVE THE SECRET AUTHORIZATION KEY
const authorization = request.headers.get("authorization");
if (authorization !== rest.authorization) return;
// READ BUFFER AFTER AUTH CHECK
const buffer = await Deno.readAll(request.body);
try {
// CONVERT THE BODY TO JSON
const data = JSON.parse(new TextDecoder().decode(buffer));
if (
!["GET", "POST", "PUT", "PATCH", "HEAD", "DELETE"].includes(
request.method,
)
) {
return request.respond(
{
status: 400,
body: JSON.stringify({ error: "Invalid METHOD." }),
},
);
}
// PROCESS THE REQUEST
await rest.processRequest(request, { body: data, retryCount: 0 });
} catch (error) {
rest.eventHandlers.error("serverRequest", error);
}
}

View File

@@ -1,7 +1,6 @@
export * from "./check_rate_limits.ts";
export * from "./cleanup_queues.ts";
export * from "./create_request_body.ts";
export * from "./handle_payload.ts";
export * from "./process_queue.ts";
export * from "./process_rate_limited_paths.ts";
export * from "./process_request.ts";

View File

@@ -30,7 +30,6 @@ export async function processQueue(id: string) {
// IF THIS DOESNT HAVE ANY ITEMS JUST CANCEL, THE CLEANER WILL REMOVE IT.
if (!queuedRequest) return;
const basicURL = rest.simplifyUrl(
queuedRequest.request.url,
queuedRequest.request.method.toUpperCase()
@@ -42,8 +41,9 @@ export async function processQueue(id: string) {
// PAUSE FOR THIS SPECIFC REQUEST
await delay(urlResetIn);
continue;
} // IF A BUCKET EXISTS, CHECK THE BUCKET'S RATE LIMITS
}
// IF A BUCKET EXISTS, CHECK THE BUCKET'S RATE LIMITS
const bucketResetIn = queuedRequest.payload.bucketId
? rest.checkRateLimits(queuedRequest.payload.bucketId)
: false;
@@ -172,7 +172,7 @@ export async function processQueue(id: string) {
} catch (error) {
// SOMETHING WENT WRONG, LOG AND RESPOND WITH ERROR
rest.eventHandlers.fetchFailed(queuedRequest.payload, error);
queuedRequest.request.reject(error);
queuedRequest.request.reject?.(error);
queuedRequest.request.respond({
status: 404,
body: JSON.stringify({ error }),

View File

@@ -1,10 +1,10 @@
import { BASE_URL } from "../util/constants.ts";
import { rest } from "./rest.ts";
import { rest, RestPayload, RestRequest } from "./rest.ts";
/** Processes a request and assigns it to a queue or creates a queue if none exists for it. */
export async function processRequest(
request: ServerRequest,
payload: RunMethodOptions
request: RestRequest,
payload: RestPayload
) {
const route = request.url.substring(request.url.indexOf("api/"));
const parts = route.split("/");

View File

@@ -9,7 +9,8 @@ export function processRequestHeaders(url: string, headers: Headers) {
const retryAfter = headers.get("x-ratelimit-reset-after");
const reset = Date.now() + Number(retryAfter) * 1000;
const global = headers.get("x-ratelimit-global");
const bucketId = headers.get("x-ratelimit-bucket");
// undefined override null needed for typings
const bucketId = headers.get("x-ratelimit-bucket") || undefined;
// IF THERE IS NO REMAINING RATE LIMIT, MARK IT AS RATE LIMITED
if (remaining === "0") {
@@ -55,7 +56,7 @@ export function processRequestHeaders(url: string, headers: Headers) {
}
}
if (ratelimited && !rest.processingRateLimitedPaths) {
if (ratelimited && rest.processingRateLimitedPaths) {
rest.processRateLimitedPaths();
}
return ratelimited ? bucketId : undefined;

View File

@@ -1,7 +1,6 @@
import { checkRateLimits } from "./check_rate_limits.ts";
import { cleanupQueues } from "./cleanup_queues.ts";
import { createRequestBody } from "./create_request_body.ts";
import { handlePayload } from "./handle_payload.ts";
import { processQueue } from "./process_queue.ts";
import { processRateLimitedPaths } from "./process_rate_limited_paths.ts";
import { processRequest } from "./process_request.ts";
@@ -17,25 +16,30 @@ export const rest = {
apiVersion: "8",
/** The secret authorization key to confirm that this was a request made by you and not a DDOS attack. */
authorization: "discordeno_best_lib_ever",
pathQueues: new Map(),
pathQueues: new Map<
string,
{
request: RestRequest;
payload: RestPayload;
}[]
>(),
processingQueue: false,
processingRateLimitedPaths: false,
globallyRateLimited: false,
ratelimitedPaths: new Map(),
ratelimitedPaths: new Map<string, RestRateLimitedPath>(),
eventHandlers: {
// BY DEFAULT WE WILL LOG ALL ERRORS TO CONSOLE. USER CAN CHOOSE TO OVERRIDE
error: function (...args: unknown[]) {},
// PLACEHOLDERS TO ALLOW USERS TO CUSTOMIZE
debug: function (type: string, error: string | Record<string, unknown>) {},
fetching(payload: Record<string, unknown>) {},
fetched(payload: Record<string, unknown>) {},
fetchSuccess(payload: Record<string, unknown>) {},
fetchFailed(payload: Record<string, unknown>, error: any) {},
fetching(payload: RestPayload) {},
fetched(payload: RestPayload) {},
fetchSuccess(payload: RestPayload) {},
fetchFailed(payload: RestPayload, error: unknown) {},
globallyRateLimited(url: string, resetsAt: number) {},
retriesMaxed(payload: Record<string, unknown>) {},
retriesMaxed(payload: RestPayload) {},
},
/** Handler function for every request. Converts to json, verified authorization & requirements and begins processing the request */
handlePayload,
checkRateLimits,
cleanupQueues,
processQueue,
@@ -46,3 +50,22 @@ export const rest = {
runMethod,
simplifyUrl,
};
export interface RestRequest {
url: string;
method: string;
respond: (payload: { status: number; body?: string }) => unknown;
reject?: (error: unknown) => unknown;
}
export interface RestPayload {
bucketId?: string;
body?: Record<string, unknown>;
retryCount: number;
}
export interface RestRateLimitedPath {
url: string;
resetTimestamp: number;
bucketId?: string;
}

View File

@@ -1,13 +1,13 @@
import { API_VERSION, BASE_URL, IMAGE_BASE_URL } from "../util/constants.ts";
import { rest } from "./rest.ts";
export function runMethod<T = any>(
export async function runMethod(
method: "get" | "post" | "put" | "delete" | "patch",
url: string,
body?: unknown,
retryCount = 0,
bucketId?: string | null,
): Promise<T | undefined> {
bucketId?: string
) {
rest.eventHandlers.debug?.("requestCreate", {
method,
url,
@@ -24,22 +24,18 @@ export function runMethod<T = any>(
!url.startsWith(`${BASE_URL}/v${API_VERSION}`) &&
!url.startsWith(IMAGE_BASE_URL)
) {
return fetch(url, {
const result = await fetch(url, {
body: JSON.stringify(body || {}),
headers: {
authorization: rest.authorization,
},
method: method.toUpperCase(),
})
.then((res) => {
if (res.status === 204) return undefined;
}).catch((error) => {
console.error(error);
throw errorStack;
});
return (res.json() as unknown) as T;
})
.catch((error) => {
console.error(error);
throw errorStack;
});
return result.status !== 204 ? await result.json() : undefined;
}
// No proxy so we need to handle all rate limiting and such
@@ -54,11 +50,9 @@ export function runMethod<T = any>(
},
{
bucketId,
url,
method,
body,
body: body as Record<string, unknown> | undefined,
retryCount,
},
}
);
});
}