// Imports import {IncomingMessage} from 'http'; import * as https from 'https'; // 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(protected repoSlug: string, githubToken?: string) { if (!repoSlug) { throw new Error('Missing required parameter \'repoSlug\'!'); } if (!githubToken) { console.warn('No GitHub access-token specified. Requests will be unauthenticated.'); } this.requestHeaders = {'User-Agent': `Node/${process.versions.node}`}; if (githubToken) { this.requestHeaders['Authorization'] = `token ${githubToken}`; } } // Methods - Public public get(pathname: string, params?: RequestParamsOrNull): Promise { const path = this.buildPath(pathname, params); return this.request('get', path); } public post(pathname: string, params?: RequestParamsOrNull, data?: any): Promise { const path = this.buildPath(pathname, params); return this.request('post', path, data); } // Methods - Protected protected buildPath(pathname: string, params?: RequestParamsOrNull): string { if (params == null) { return pathname; } const search = (params === null) ? '' : this.serializeSearchParams(params); const joiner = search && '?'; return `${pathname}${joiner}${search}`; } protected request(method: string, path: string, data: any = null): Promise { return new Promise((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(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): string { return Object.keys(params). filter(key => params[key] != null). map(key => `${key}=${encodeURIComponent(String(params[key]))}`). join('&'); } }