mirror of
https://github.com/jdalrymple/gitbeaker.git
synced 2026-01-18 15:55:30 +00:00
121 lines
3.3 KiB
TypeScript
121 lines
3.3 KiB
TypeScript
import { decamelizeKeys } from 'xcase';
|
|
import FormData from 'form-data';
|
|
import { stringify } from 'query-string';
|
|
|
|
// Types
|
|
export interface Constructable<T = any> {
|
|
new (...args: any[]): T;
|
|
}
|
|
|
|
export interface RequesterType {
|
|
get(endpoint: string, options?: Record<string, unknown>): Promise<any>;
|
|
post(endpoint: string, options?: Record<string, unknown>): Promise<any>;
|
|
put(endpoint: string, options?: Record<string, unknown>): Promise<any>;
|
|
delete(endpoint: string, options?: Record<string, unknown>): Promise<any>;
|
|
stream?(endpoint: string, options?: Record<string, unknown>): NodeJS.ReadableStream;
|
|
}
|
|
|
|
export type DefaultServiceOptions = {
|
|
headers: { [header: string]: string };
|
|
requestTimeout: number;
|
|
url: string;
|
|
rejectUnauthorized: boolean;
|
|
};
|
|
|
|
export type DefaultRequestOptions = {
|
|
body?: FormData | Record<string, unknown>;
|
|
query?: Record<string, unknown>;
|
|
sudo?: string;
|
|
method?: string;
|
|
};
|
|
|
|
export type DefaultRequestReturn = {
|
|
headers: Record<string, string> | Headers;
|
|
timeout?: number;
|
|
method: string;
|
|
searchParams?: string;
|
|
prefixUrl?: string;
|
|
body?: string | FormData;
|
|
};
|
|
|
|
// Utility methods
|
|
export function formatQuery(params: Record<string, unknown> = {}) {
|
|
const decamelized = decamelizeKeys(params);
|
|
|
|
if (decamelized.not) decamelized.not = JSON.stringify(decamelized.not);
|
|
|
|
return stringify(decamelized, { arrayFormat: 'bracket' });
|
|
}
|
|
|
|
export function defaultOptionsHandler(
|
|
serviceOptions: DefaultServiceOptions,
|
|
{ body, query, sudo, method = 'get' }: DefaultRequestOptions = {},
|
|
): DefaultRequestReturn {
|
|
const { headers, requestTimeout, url } = serviceOptions;
|
|
let bod: FormData | string;
|
|
|
|
if (sudo) headers.sudo = sudo;
|
|
|
|
// FIXME: Not the best comparison, but...it will have to do for now.
|
|
if (typeof body === 'object' && body.constructor.name !== 'FormData') {
|
|
bod = JSON.stringify(decamelizeKeys(body));
|
|
headers['content-type'] = 'application/json';
|
|
} else {
|
|
bod = body as FormData;
|
|
}
|
|
|
|
return {
|
|
headers,
|
|
timeout: requestTimeout,
|
|
method,
|
|
searchParams: formatQuery(query),
|
|
prefixUrl: url,
|
|
body: bod,
|
|
};
|
|
}
|
|
|
|
export function createRequesterFn(
|
|
optionsHandler,
|
|
requestHandler,
|
|
): (serviceOptions: DefaultServiceOptions) => RequesterType {
|
|
const methods = ['get', 'post', 'put', 'delete', 'stream'];
|
|
|
|
return (serviceOptions) => {
|
|
const requester: RequesterType = {} as RequesterType;
|
|
|
|
methods.forEach((m) => {
|
|
requester[m] = (endpoint: string, options: Record<string, unknown>) => {
|
|
const requestOptions = optionsHandler(serviceOptions, { ...options, method: m });
|
|
|
|
return requestHandler(endpoint, requestOptions);
|
|
};
|
|
});
|
|
|
|
return requester;
|
|
};
|
|
}
|
|
|
|
function extendClass<T extends Constructable>(Base: T, customConfig: Record<string, unknown>): T {
|
|
return class extends Base {
|
|
constructor(...options: any[]) {
|
|
const [config, ...opts] = options;
|
|
|
|
super({ ...customConfig, ...config }, ...opts);
|
|
}
|
|
};
|
|
}
|
|
|
|
export function modifyServices<T>(services: T, customConfig: Record<string, unknown> = {}) {
|
|
const updated = {};
|
|
|
|
Object.entries(services).forEach(([k, s]) => {
|
|
updated[k] = extendClass(s, customConfig);
|
|
});
|
|
|
|
return updated as T;
|
|
}
|
|
|
|
export async function wait(ms: number): Promise<void> {
|
|
return new Promise((resolve) => setTimeout(resolve, ms));
|
|
}
|