177 lines
3.8 KiB
TypeScript
177 lines
3.8 KiB
TypeScript
import { timer } from '@gitroom/helpers/utils/timer';
|
|
import { Integration } from '@prisma/client';
|
|
import { ApplicationFailure } from '@temporalio/activity';
|
|
|
|
export class RefreshToken extends ApplicationFailure {
|
|
constructor(identifier: string, json: string, body: BodyInit, message = '') {
|
|
super(message, 'refresh_token', true, [
|
|
{
|
|
identifier,
|
|
json,
|
|
body,
|
|
},
|
|
]);
|
|
}
|
|
}
|
|
|
|
export class BadBody extends ApplicationFailure {
|
|
constructor(identifier: string, json: string, body: BodyInit, message = '') {
|
|
super(message, 'bad_body', true, [
|
|
{
|
|
identifier,
|
|
json,
|
|
body,
|
|
},
|
|
]);
|
|
}
|
|
}
|
|
|
|
export class NotEnoughScopes {
|
|
constructor(public message = 'Not enough scopes') {}
|
|
}
|
|
|
|
export abstract class SocialAbstract {
|
|
abstract identifier: string;
|
|
maxConcurrentJob = 1;
|
|
|
|
public handleErrors(
|
|
body: string
|
|
):
|
|
| { type: 'refresh-token' | 'bad-body' | 'retry'; value: string }
|
|
| undefined {
|
|
return undefined;
|
|
}
|
|
|
|
public async mention(
|
|
token: string,
|
|
d: { query: string },
|
|
id: string,
|
|
integration: Integration
|
|
): Promise<
|
|
| { id: string; label: string; image: string; doNotCache?: boolean }[]
|
|
| { none: true }
|
|
> {
|
|
return { none: true };
|
|
}
|
|
|
|
async runInConcurrent<T>(
|
|
func: (...args: any[]) => Promise<T>,
|
|
ignoreConcurrency?: boolean
|
|
) {
|
|
let value: any;
|
|
try {
|
|
value = await func();
|
|
} catch (err) {
|
|
const handle = this.handleErrors(JSON.stringify(err));
|
|
value = { err: true, value: 'Unknown Error', ...(handle || {}) };
|
|
}
|
|
|
|
if (value && value?.err && value?.value) {
|
|
if (value.type === 'refresh-token') {
|
|
throw new RefreshToken(
|
|
'',
|
|
JSON.stringify({}),
|
|
{} as any,
|
|
value.value || ''
|
|
);
|
|
}
|
|
throw new BadBody('', JSON.stringify({}), {} as any, value.value || '');
|
|
}
|
|
|
|
return value;
|
|
}
|
|
|
|
async fetch(
|
|
url: string,
|
|
options: RequestInit = {},
|
|
identifier = '',
|
|
totalRetries = 0,
|
|
ignoreConcurrency = false
|
|
): Promise<Response> {
|
|
const request = await fetch(url, options);
|
|
|
|
if (request.status === 200 || request.status === 201) {
|
|
return request;
|
|
}
|
|
|
|
if (totalRetries > 2) {
|
|
throw new BadBody(identifier, '{}', options.body || '{}');
|
|
}
|
|
|
|
let json = '{}';
|
|
try {
|
|
json = await request.text();
|
|
} catch (err) {
|
|
json = '{}';
|
|
}
|
|
|
|
if (
|
|
request.status === 429 ||
|
|
request.status === 500 ||
|
|
json.includes('rate_limit_exceeded') ||
|
|
json.includes('Rate limit')
|
|
) {
|
|
await timer(5000);
|
|
return this.fetch(
|
|
url,
|
|
options,
|
|
identifier,
|
|
totalRetries + 1,
|
|
ignoreConcurrency
|
|
);
|
|
}
|
|
|
|
const handleError = this.handleErrors(json || '{}');
|
|
|
|
if (handleError?.type === 'retry') {
|
|
await timer(5000);
|
|
return this.fetch(
|
|
url,
|
|
options,
|
|
identifier,
|
|
totalRetries + 1,
|
|
ignoreConcurrency
|
|
);
|
|
}
|
|
|
|
if (
|
|
request.status === 401 &&
|
|
(handleError?.type === 'refresh-token' || !handleError)
|
|
) {
|
|
throw new RefreshToken(
|
|
identifier,
|
|
json,
|
|
options.body!,
|
|
handleError?.value
|
|
);
|
|
}
|
|
|
|
throw new BadBody(
|
|
identifier,
|
|
json,
|
|
options.body!,
|
|
handleError?.value || ''
|
|
);
|
|
}
|
|
|
|
checkScopes(required: string[], got: string | string[]) {
|
|
if (Array.isArray(got)) {
|
|
if (!required.every((scope) => got.includes(scope))) {
|
|
throw new NotEnoughScopes();
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
const newGot = decodeURIComponent(got);
|
|
|
|
const splitType = newGot.indexOf(',') > -1 ? ',' : ' ';
|
|
const gotArray = newGot.split(splitType);
|
|
if (!required.every((scope) => gotArray.includes(scope))) {
|
|
throw new NotEnoughScopes();
|
|
}
|
|
|
|
return true;
|
|
}
|
|
}
|