refactor(rest): switch api to fetch-like and provide strategies (#9416)

BREAKING CHANGE: NodeJS v18+ is required when using node due to the use of global `fetch`
BREAKING CHANGE: The raw method of REST now returns a web compatible `Respone` object.
BREAKING CHANGE: The `parseResponse` utility method has been updated to operate on a web compatible `Response` object.
BREAKING CHANGE: Many underlying internals have changed, some of which were exported.
BREAKING CHANGE: `DefaultRestOptions` used to contain a default `agent`, which is now set to `null` instead.
This commit is contained in:
ckohen
2023-05-06 12:09:19 -07:00
committed by GitHub
parent fc5b9c523b
commit cdaa0a36f5
28 changed files with 317 additions and 203 deletions

View File

@@ -3,10 +3,10 @@ import { URLSearchParams } from 'node:url';
import { DiscordSnowflake } from '@sapphire/snowflake';
import type { Snowflake } from 'discord-api-types/v10';
import { Routes } from 'discord-api-types/v10';
import type { FormData } from 'undici';
import { type FormData, fetch } from 'undici';
import { File as UndiciFile, MockAgent, setGlobalDispatcher } from 'undici';
import type { Interceptable, MockInterceptor } from 'undici/types/mock-interceptor.js';
import { beforeEach, afterEach, test, expect } from 'vitest';
import { beforeEach, afterEach, test, expect, vitest } from 'vitest';
import { REST } from '../src/index.js';
import { genPath } from './util.js';
@@ -16,6 +16,10 @@ const newSnowflake: Snowflake = DiscordSnowflake.generate().toString();
const api = new REST().setToken('A-Very-Fake-Token');
const makeRequestMock = vitest.fn(fetch);
const fetchApi = new REST({ makeRequest: makeRequestMock }).setToken('A-Very-Fake-Token');
// @discordjs/rest uses the `content-type` header to detect whether to parse
// the response as JSON or as an ArrayBuffer.
const responseOptions: MockInterceptor.MockResponseOptions = {
@@ -114,6 +118,22 @@ test('simple POST', async () => {
expect(await api.post('/simplePost')).toStrictEqual({ test: true });
});
test('simple POST with fetch', async () => {
mockPool
.intercept({
path: genPath('/fetchSimplePost'),
method: 'POST',
})
.reply(() => ({
data: { test: true },
statusCode: 200,
responseOptions,
}));
expect(await fetchApi.post('/fetchSimplePost')).toStrictEqual({ test: true });
expect(makeRequestMock).toHaveBeenCalledTimes(1);
});
test('simple PUT 2', async () => {
mockPool
.intercept({
@@ -159,11 +179,11 @@ test('getAuth', async () => {
path: genPath('/getAuth'),
method: 'GET',
})
.reply((from) => ({
data: { auth: (from.headers as unknown as Record<string, string | undefined>).Authorization ?? null },
statusCode: 200,
.reply(
200,
(from) => ({ auth: (from.headers as unknown as Record<string, string | undefined>).Authorization ?? null }),
responseOptions,
}))
)
.times(3);
// default
@@ -190,11 +210,13 @@ test('getReason', async () => {
path: genPath('/getReason'),
method: 'GET',
})
.reply((from) => ({
data: { reason: (from.headers as unknown as Record<string, string | undefined>)['X-Audit-Log-Reason'] ?? null },
statusCode: 200,
.reply(
200,
(from) => ({
reason: (from.headers as unknown as Record<string, string | undefined>)['X-Audit-Log-Reason'] ?? null,
}),
responseOptions,
}))
)
.times(3);
// default