Files
blitzkit/src/core/blitz/fetchBlitz.ts
T

92 lines
2.6 KiB
TypeScript
Raw Normal View History

2024-04-15 13:19:25 -05:00
import { Region } from '../../constants/regions';
import { WARGAMING_APPLICATION_ID } from '../../constants/wargamingApplicationID';
2024-06-06 13:37:50 -05:00
import { EventManager } from '../blitzkit/eventManager';
2024-04-23 09:57:59 -05:00
import { patientFetch } from '../blitzkit/patientFetch';
2024-03-19 18:21:56 -05:00
2024-06-08 19:04:54 -05:00
const RETRY_ERRORS = ['REQUEST_LIMIT_EXCEEDED', 'SOURCE_NOT_AVAILABLE'];
export const retryAbleBlitzFetchEvent = new EventManager();
2024-06-26 21:22:03 -05:00
export const blitzFetchQueueAvailableEvent = new EventManager();
2024-06-06 13:37:50 -05:00
2024-03-12 23:36:56 -05:00
type BlitzResponse<Data extends object> =
2023-10-20 10:50:54 -05:00
| {
status: 'error';
error: { field: string; message: string; code: 402; value: null };
}
| {
status: 'ok';
data: Data;
};
2024-04-13 22:13:57 -05:00
const MAX_CALLS_PER_SECOND = 10;
2023-10-20 10:50:54 -05:00
const queue: { url: string; resolve: (data: object) => void }[] = [];
let inProgress = 0;
2024-06-26 21:22:03 -05:00
function decrement() {
inProgress--;
blitzFetchQueueAvailableEvent.emit(undefined);
manageQueue();
}
2023-10-20 10:50:54 -05:00
async function manageQueue() {
if (queue.length > 0 && inProgress < MAX_CALLS_PER_SECOND) {
inProgress++;
2024-06-26 21:22:03 -05:00
const timeout = setTimeout(() => decrement, 1000);
2024-04-13 22:13:57 -05:00
const request = queue.shift()!;
const data = (await patientFetch(request.url)
2023-10-20 10:50:54 -05:00
.then((response) => response.json())
.catch((error) => ({ status: 'error', error }))) as BlitzResponse<
2024-04-13 22:13:57 -05:00
typeof request.resolve
2023-10-20 10:50:54 -05:00
>;
if (data.status === 'ok') {
2024-04-13 22:13:57 -05:00
request.resolve(data.data);
2023-10-20 10:50:54 -05:00
} else {
2024-06-08 19:04:54 -05:00
if (RETRY_ERRORS.includes(data.error.message)) {
2024-04-13 22:13:57 -05:00
console.log(
2024-06-08 19:04:54 -05:00
`Encountered retry-able error ${data.error}, putting "${request.url}" back in queue...`,
2024-04-13 22:13:57 -05:00
);
2023-10-31 15:32:35 -05:00
2024-06-08 19:04:54 -05:00
retryAbleBlitzFetchEvent.emit(undefined);
2024-04-13 22:13:57 -05:00
clearTimeout(timeout);
setTimeout(() => {
queue.push(request);
2024-06-26 21:22:03 -05:00
decrement();
2024-04-13 22:13:57 -05:00
}, 1000 / MAX_CALLS_PER_SECOND);
} else {
clearTimeout(timeout);
2024-06-26 21:22:03 -05:00
decrement();
2024-04-13 22:13:57 -05:00
throw new Error(`Wargaming response error status: "${data.status}"`, {
cause: `Message: "${data.error.message}"\nURL: "${request.url}"`,
});
}
2023-10-20 10:50:54 -05:00
}
}
}
2024-04-28 19:16:08 -05:00
export type FetchBlitzParams = Record<string, string | number | undefined>;
2024-04-15 13:19:25 -05:00
export default function fetchBlitz<Data extends object>(
region: Region,
path: string,
2024-04-28 19:16:08 -05:00
params: FetchBlitzParams = {},
2024-04-15 13:19:25 -05:00
) {
2023-10-20 11:08:22 -05:00
return new Promise<Data>((resolve) => {
2023-10-20 10:50:54 -05:00
queue.push({
2024-04-15 13:19:25 -05:00
url: `https://api.wotblitz.${region}/wotb/${path}/?${Object.entries({
application_id: WARGAMING_APPLICATION_ID,
...params,
})
.filter(([_, value]) => value !== undefined)
.map(([key, value]) => `${key}=${encodeURIComponent(value!)}`)
.join('&')}`,
2023-10-20 10:50:54 -05:00
resolve(data) {
2023-10-20 11:08:22 -05:00
resolve(data as Data);
2023-10-20 10:50:54 -05:00
},
});
manageQueue();
});
}