Previously, Travis pushed the build artitfacts to the preview server. This required us to use JWT to secure the POST request from Travis, to ensure we couldn't receive malicious builds. JWT has been deprecated and we are moving our builds to CircleCI. This commit rewrites the TypeScript part of the preview server that handles converting build artifact into hosted previews of the docs.
111 lines
3.2 KiB
TypeScript
111 lines
3.2 KiB
TypeScript
// Imports
|
|
import {IncomingMessage} from 'http';
|
|
import * as https from 'https';
|
|
import {assertNotMissingOrEmpty} from './utils';
|
|
|
|
// Constants
|
|
const GITHUB_HOSTNAME = 'api.github.com';
|
|
|
|
// Interfaces - Types
|
|
interface RequestParams {
|
|
[key: string]: string | number;
|
|
}
|
|
|
|
type RequestParamsOrNull = RequestParams | null;
|
|
|
|
// Classes
|
|
export class GithubApi {
|
|
protected requestHeaders: {[key: string]: string};
|
|
|
|
// Constructor
|
|
constructor(githubToken: string) {
|
|
assertNotMissingOrEmpty('githubToken', githubToken);
|
|
|
|
this.requestHeaders = {
|
|
'Authorization': `token ${githubToken}`,
|
|
'User-Agent': `Node/${process.versions.node}`,
|
|
};
|
|
}
|
|
|
|
// Methods - Public
|
|
public get<T = any>(pathname: string, params?: RequestParamsOrNull): Promise<T> {
|
|
const path = this.buildPath(pathname, params);
|
|
return this.request<T>('get', path);
|
|
}
|
|
|
|
public post<T = any>(pathname: string, params?: RequestParamsOrNull, data?: any): Promise<T> {
|
|
const path = this.buildPath(pathname, params);
|
|
return this.request<T>('post', path, data);
|
|
}
|
|
|
|
public getPaginated<T>(pathname: string, baseParams: RequestParams = {}, currentPage: number = 0): Promise<T[]> {
|
|
const perPage = 100;
|
|
const params = {
|
|
...baseParams,
|
|
page: currentPage,
|
|
per_page: perPage,
|
|
};
|
|
|
|
return this.get<T[]>(pathname, params).then(items => {
|
|
if (items.length < perPage) {
|
|
return items;
|
|
}
|
|
|
|
return this.getPaginated<T>(pathname, baseParams, currentPage + 1).then(moreItems => [...items, ...moreItems]);
|
|
});
|
|
}
|
|
|
|
// Methods - Protected
|
|
protected buildPath(pathname: string, params?: RequestParamsOrNull) {
|
|
if (params == null) {
|
|
return pathname;
|
|
}
|
|
|
|
const search = (params === null) ? '' : this.serializeSearchParams(params);
|
|
const joiner = search && '?';
|
|
|
|
return `${pathname}${joiner}${search}`;
|
|
}
|
|
|
|
protected request<T>(method: string, path: string, data: any = null) {
|
|
return new Promise<T>((resolve, reject) => {
|
|
const options = {
|
|
headers: {...this.requestHeaders},
|
|
host: GITHUB_HOSTNAME,
|
|
method,
|
|
path,
|
|
};
|
|
|
|
const onError = (statusCode: number, responseText: string) => {
|
|
const url = `https://${GITHUB_HOSTNAME}${path}`;
|
|
reject(`Request to '${url}' failed (status: ${statusCode}): ${responseText}`);
|
|
};
|
|
const onSuccess = (responseText: string) => {
|
|
try { resolve(responseText && JSON.parse(responseText)); } catch (err) { reject(err); }
|
|
};
|
|
const onResponse = (res: IncomingMessage) => {
|
|
const statusCode = res.statusCode || -1;
|
|
const isSuccess = (200 <= statusCode) && (statusCode < 400);
|
|
let responseText = '';
|
|
|
|
res.
|
|
on('data', d => responseText += d).
|
|
on('end', () => isSuccess ? onSuccess(responseText) : onError(statusCode, responseText)).
|
|
on('error', reject);
|
|
};
|
|
|
|
https.
|
|
request(options, onResponse).
|
|
on('error', reject).
|
|
end(data && JSON.stringify(data));
|
|
});
|
|
}
|
|
|
|
protected serializeSearchParams(params: RequestParams) {
|
|
return Object.keys(params).
|
|
filter(key => params[key] != null).
|
|
map(key => `${key}=${encodeURIComponent(String(params[key]))}`).
|
|
join('&');
|
|
}
|
|
}
|