ci(docs-infra): change AIO preview server stuff to pull builds from CircleCI (#25554)

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.

PR Close #25554
This commit is contained in:
Pete Bacon Darwin
2018-05-10 13:56:07 +01:00
committed by Jason Aden
parent 1e0f455855
commit faf184ad63
42 changed files with 3089 additions and 2060 deletions

View File

@ -1,7 +1,5 @@
// Imports
import {EventEmitter} from 'events';
import {ClientRequest, IncomingMessage} from 'http';
import * as https from 'https';
import * as nock from 'nock';
import {GithubApi} from '../../lib/common/github-api';
// Tests
@ -110,39 +108,6 @@ describe('GithubApi', () => {
});
// Protected methods
describe('buildPath()', () => {
it('should return the pathname if no params', () => {
expect((api as any).buildPath('/foo')).toBe('/foo');
expect((api as any).buildPath('/foo', undefined)).toBe('/foo');
expect((api as any).buildPath('/foo', null)).toBe('/foo');
});
it('should append the params to the pathname', () => {
expect((api as any).buildPath('/foo', {bar: 'baz'})).toBe('/foo?bar=baz');
});
it('should join the params with \'&\'', () => {
expect((api as any).buildPath('/foo', {bar: 1, baz: 2})).toBe('/foo?bar=1&baz=2');
});
it('should ignore undefined/null params', () => {
expect((api as any).buildPath('/foo', {bar: undefined, baz: null})).toBe('/foo');
});
it('should encode param values as URI components', () => {
expect((api as any).buildPath('/foo', {bar: 'b a&z'})).toBe('/foo?bar=b%20a%26z');
});
});
describe('getPaginated()', () => {
let deferreds: {resolve: (v: any) => void, reject: (v: any) => void}[];
@ -218,191 +183,162 @@ describe('GithubApi', () => {
});
describe('request()', () => {
let httpsRequestSpy: jasmine.Spy;
let latestRequest: ClientRequest;
// Protected methods
beforeEach(() => {
const originalRequest = https.request;
describe('buildPath()', () => {
httpsRequestSpy = spyOn(https, 'request').and.callFake((...args: any[]) => {
latestRequest = originalRequest.apply(https, args);
spyOn(latestRequest, 'on').and.callThrough();
spyOn(latestRequest, 'end');
return latestRequest;
});
it('should return the pathname if no params', () => {
expect((api as any).buildPath('/foo')).toBe('/foo');
expect((api as any).buildPath('/foo', undefined)).toBe('/foo');
expect((api as any).buildPath('/foo', null)).toBe('/foo');
});
it('should append the params to the pathname', () => {
expect((api as any).buildPath('/foo', {bar: 'baz'})).toBe('/foo?bar=baz');
});
it('should join the params with \'&\'', () => {
expect((api as any).buildPath('/foo', {bar: 1, baz: 2})).toBe('/foo?bar=1&baz=2');
});
it('should ignore undefined/null params', () => {
expect((api as any).buildPath('/foo', {bar: undefined, baz: null})).toBe('/foo');
});
it('should encode param values as URI components', () => {
expect((api as any).buildPath('/foo', {bar: 'b a&z'})).toBe('/foo?bar=b%20a%26z');
});
});
describe('request()', () => {
it('should return a promise', () => {
nock('https://api.github.com').get('').reply(200);
expect((api as any).request()).toEqual(jasmine.any(Promise));
});
it('should call \'https.request()\' with the correct options', () => {
(api as any).request('method', 'path');
const requestHandler = nock('https://api.github.com')
.intercept('/path', 'method')
.reply(200);
expect(httpsRequestSpy).toHaveBeenCalled();
expect(httpsRequestSpy.calls.argsFor(0)[0]).toEqual(jasmine.objectContaining({
headers: jasmine.objectContaining({
'User-Agent': `Node/${process.versions.node}`,
}),
host: 'api.github.com',
method: 'method',
path: 'path',
}));
(api as any).request('method', '/path');
requestHandler.done();
});
it('should call specify an \'Authorization\' header if \'githubToken\' is present', () => {
(api as any).request('method', 'path');
expect(httpsRequestSpy).toHaveBeenCalled();
expect(httpsRequestSpy.calls.argsFor(0)[0].headers).toEqual(jasmine.objectContaining({
Authorization: 'token 12345',
}));
it('should add the \'Authorization\' header containing the \'githubToken\'', () => {
const requestHandler = nock('https://api.github.com')
.intercept('/path', 'method', undefined, {
reqheaders: {Authorization: 'token 12345'},
})
.reply(200);
(api as any).request('method', '/path');
requestHandler.done();
});
it('should reject on request error', done => {
(api as any).request('method', 'path').catch((err: any) => {
expect(err).toBe('Test');
done();
});
latestRequest.emit('error', 'Test');
});
it('should send the request (i.e. call \'end()\')', () => {
(api as any).request('method', 'path');
expect(latestRequest.end).toHaveBeenCalled();
it('should reject on request error', async () => {
nock('https://api.github.com')
.intercept('/path', 'method')
.replyWithError('Test');
let message = 'Failed to reject error';
await (api as any).request('method', '/path').catch((err: any) => message = err.message);
expect(message).toEqual('Test');
});
it('should \'JSON.stringify\' and send the data along with the request', () => {
(api as any).request('method', 'path');
expect(latestRequest.end).toHaveBeenCalledWith(null);
(api as any).request('method', 'path', {key: 'value'});
expect(latestRequest.end).toHaveBeenCalledWith('{"key":"value"}');
const data = {key: 'value'};
const requestHandler = nock('https://api.github.com')
.intercept('/path', 'method', JSON.stringify(data))
.reply(200);
(api as any).request('method', '/path', data);
requestHandler.done();
});
describe('onResponse', () => {
let promise: Promise<object>;
let respond: (statusCode: number) => IncomingMessage;
it('should reject if response statusCode is <200', done => {
const requestHandler = nock('https://api.github.com')
.intercept('/path', 'method')
.reply(199);
beforeEach(() => {
promise = (api as any).request('method', 'path');
respond = (statusCode: number) => {
const mockResponse = new EventEmitter() as IncomingMessage;
mockResponse.statusCode = statusCode;
const onResponse = httpsRequestSpy.calls.argsFor(0)[1];
onResponse(mockResponse);
return mockResponse;
};
});
it('should reject on response error', done => {
promise.catch(err => {
expect(err).toBe('Test');
done();
});
const res = respond(200);
res.emit('error', 'Test');
});
it('should reject if returned statusCode is <200', done => {
promise.catch(err => {
(api as any).request('method', '/path')
.catch((err: string) => {
expect(err).toContain('failed');
expect(err).toContain('status: 199');
done();
});
const res = respond(199);
res.emit('end');
});
requestHandler.done();
});
it('should reject if returned statusCode is >=400', done => {
promise.catch(err => {
it('should reject if response statusCode is >=400', done => {
const requestHandler = nock('https://api.github.com')
.intercept('/path', 'method')
.reply(400);
(api as any).request('method', '/path')
.catch((err: string) => {
expect(err).toContain('failed');
expect(err).toContain('status: 400');
done();
});
const res = respond(400);
res.emit('end');
});
requestHandler.done();
});
it('should include the response text in the rejection message', done => {
promise.catch(err => {
it('should include the response text in the rejection message', done => {
const requestHandler = nock('https://api.github.com')
.intercept('/path', 'method')
.reply(500, 'Test');
(api as any).request('method', '/path')
.catch((err: string) => {
expect(err).toContain('Test');
done();
});
requestHandler.done();
});
const res = respond(500);
res.emit('data', 'Test');
res.emit('end');
it('should resolve if returned statusCode is >=200 and <400', done => {
const requestHandler = nock('https://api.github.com')
.intercept('/path', 'method')
.reply(200);
(api as any).request('method', '/path').then(done);
requestHandler.done();
});
it('should parse the response body into an object using \'JSON.parse\'', done => {
const requestHandler = nock('https://api.github.com')
.intercept('/path', 'method')
.reply(300, '{"foo": "bar"}');
(api as any).request('method', '/path').then((data: any) => {
expect(data).toEqual({foo: 'bar'});
done();
});
requestHandler.done();
});
it('should reject if the response text is malformed JSON', done => {
const requestHandler = nock('https://api.github.com')
.intercept('/path', 'method')
.reply(300, '}');
it('should resolve if returned statusCode is <=200 <400', done => {
promise.then(done);
const res = respond(200);
res.emit('data', '{}');
res.emit('end');
(api as any).request('method', '/path').catch((err: any) => {
expect(err).toEqual(jasmine.any(SyntaxError));
done();
});
it('should resolve with the response text \'JSON.parsed\'', done => {
promise.then(data => {
expect(data).toEqual({foo: 'bar'});
done();
});
const res = respond(300);
res.emit('data', '{"foo":"bar"}');
res.emit('end');
});
it('should collect and concatenate the whole response text', done => {
promise.then(data => {
expect(data).toEqual({foo: 'bar', baz: 'qux'});
done();
});
const res = respond(300);
res.emit('data', '{"foo":');
res.emit('data', '"bar","baz"');
res.emit('data', ':"qux"}');
res.emit('end');
});
it('should reject if the response text is malformed JSON', done => {
promise.catch(err => {
expect(err).toEqual(jasmine.any(SyntaxError));
done();
});
const res = respond(300);
res.emit('data', '}');
res.emit('end');
});
requestHandler.done();
});
});