Skip to content

Commit

Permalink
feat: add GitHub-specific request headers (#443)
Browse files Browse the repository at this point in the history
* refactor: simplify header append logic

This felt a bit redundant, and this pattern will make it easier for us to add more headers more easily

* refactor: add GHA check into separate function

We'll be using this in our fetch wrapper so let's extract it!

* feat: move source header to all requests

Also the header is now different depending on if it's in a GHA environment or not

Refactoring all fetch tests to check for multiple headers

* feat: add additional GitHub metadata
  • Loading branch information
kanadgupta authored Feb 17, 2022
1 parent 9f86fa0 commit bf20f2d
Show file tree
Hide file tree
Showing 3 changed files with 65 additions and 21 deletions.
52 changes: 41 additions & 11 deletions __tests__/lib/fetch.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,63 +6,93 @@ const pkg = require('../../package.json');

describe('#fetch()', () => {
describe('GitHub Actions environment', () => {
// List of all GitHub Actions env variables:
// https://docs.github.com/en/actions/learn-github-actions/environment-variables#default-environment-variables
beforeEach(() => {
process.env.GITHUB_ACTIONS = 'true';
process.env.GITHUB_REPOSITORY = 'octocat/Hello-World';
process.env.GITHUB_RUN_ATTEMPT = '3';
process.env.GITHUB_RUN_ID = '1658821493';
process.env.GITHUB_RUN_NUMBER = '3';
process.env.GITHUB_SHA = 'ffac537e6cbbf934b08745a378932722df287a53';
});

afterEach(() => {
delete process.env.GITHUB_ACTIONS;
delete process.env.GITHUB_REPOSITORY;
delete process.env.GITHUB_RUN_ATTEMPT;
delete process.env.GITHUB_RUN_ID;
delete process.env.GITHUB_RUN_NUMBER;
delete process.env.GITHUB_SHA;
});

it('should use correct user-agent for requests in GitHub Action env', async () => {
it('should have correct headers for requests in GitHub Action env', async () => {
const key = 'API_KEY';

const mock = getApiNock()
.get('/api/v1')
.basicAuth({ user: key })
.reply(200, function () {
return this.req.headers['user-agent'];
return this.req.headers;
});

const userAgent = await fetch(`${config.get('host')}/api/v1`, {
const headers = await fetch(`${config.get('host')}/api/v1`, {
method: 'get',
headers: cleanHeaders(key),
}).then(handleRes);

expect(userAgent.shift()).toBe(`rdme-github/${pkg.version}`);
expect(headers['user-agent'].shift()).toBe(`rdme-github/${pkg.version}`);
expect(headers['x-readme-source'].shift()).toBe('cli-gh');
expect(headers['x-github-repository'].shift()).toBe('octocat/Hello-World');
expect(headers['x-github-run-attempt'].shift()).toBe('3');
expect(headers['x-github-run-id'].shift()).toBe('1658821493');
expect(headers['x-github-run-number'].shift()).toBe('3');
expect(headers['x-github-sha'].shift()).toBe('ffac537e6cbbf934b08745a378932722df287a53');
mock.done();
});
});

it('should wrap all requests with a rdme User-Agent', async () => {
it('should wrap all requests with standard user-agent and source headers', async () => {
const key = 'API_KEY';

const mock = getApiNock()
.get('/api/v1')
.basicAuth({ user: key })
.reply(200, function () {
return this.req.headers['user-agent'];
return this.req.headers;
});

const userAgent = await fetch(`${config.get('host')}/api/v1`, {
const headers = await fetch(`${config.get('host')}/api/v1`, {
method: 'get',
headers: cleanHeaders(key),
}).then(handleRes);

expect(userAgent.shift()).toBe(`rdme/${pkg.version}`);
expect(headers['user-agent'].shift()).toBe(`rdme/${pkg.version}`);
expect(headers['x-readme-source'].shift()).toBe('cli');
expect(headers['x-github-repository']).toBeUndefined();
expect(headers['x-github-run-attempt']).toBeUndefined();
expect(headers['x-github-run-id']).toBeUndefined();
expect(headers['x-github-run-number']).toBeUndefined();
expect(headers['x-github-sha']).toBeUndefined();
mock.done();
});

it('should support if we dont supply any other options with the request', async () => {
const mock = getApiNock()
.get('/api/v1/doesnt-need-auth')
.reply(200, function () {
return this.req.headers['user-agent'];
return this.req.headers;
});

const userAgent = await fetch(`${config.get('host')}/api/v1/doesnt-need-auth`).then(handleRes);
const headers = await fetch(`${config.get('host')}/api/v1/doesnt-need-auth`).then(handleRes);

expect(userAgent.shift()).toBe(`rdme/${pkg.version}`);
expect(headers['user-agent'].shift()).toBe(`rdme/${pkg.version}`);
expect(headers['x-readme-source'].shift()).toBe('cli');
expect(headers['x-github-repository']).toBeUndefined();
expect(headers['x-github-run-attempt']).toBeUndefined();
expect(headers['x-github-run-id']).toBeUndefined();
expect(headers['x-github-run-number']).toBeUndefined();
expect(headers['x-github-sha']).toBeUndefined();
mock.done();
});
});
Expand Down
1 change: 0 additions & 1 deletion src/cmds/openapi.js
Original file line number Diff line number Diff line change
Expand Up @@ -138,7 +138,6 @@ module.exports = class OpenAPICommand {
const options = {
headers: cleanHeaders(key, {
'x-readme-version': versionCleaned,
'x-readme-source': 'cli',
Accept: 'application/json',
}),
body: formData,
Expand Down
33 changes: 24 additions & 9 deletions src/lib/fetch.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,18 +4,33 @@ const pkg = require('../../package.json');
const APIError = require('./apiError');

/**
* Wrapper for the `fetch` API so we can add an rdme user agent to all API requests.
* Small env check to determine if we're in a GitHub Actions environment
* @link https://docs.github.com/en/actions/learn-github-actions/environment-variables#default-environment-variables
*/
function isGHA() {
return process.env.GITHUB_ACTIONS === 'true';
}

/**
* Wrapper for the `fetch` API so we can add rdme-specific headers to all API requests.
*
*/
module.exports = (url, options = {}) => {
if (!options.headers) {
options.headers = {
'User-Agent': module.exports.getUserAgent(),
};
} else {
options.headers['User-Agent'] = module.exports.getUserAgent();
module.exports = (url, options = { headers: {} }) => {
let source = 'cli';

options.headers['User-Agent'] = module.exports.getUserAgent();

if (isGHA()) {
source = 'cli-gh';
options.headers['x-github-repository'] = process.env.GITHUB_REPOSITORY;
options.headers['x-github-run-attempt'] = process.env.GITHUB_RUN_ATTEMPT;
options.headers['x-github-run-id'] = process.env.GITHUB_RUN_ID;
options.headers['x-github-run-number'] = process.env.GITHUB_RUN_NUMBER;
options.headers['x-github-sha'] = process.env.GITHUB_SHA;
}

options.headers['x-readme-source'] = source;

return fetch(url, options);
};

Expand All @@ -25,7 +40,7 @@ module.exports = (url, options = {}) => {
*
*/
module.exports.getUserAgent = function getUserAgent() {
const gh = process.env.GITHUB_ACTIONS === 'true' ? '-github' : '';
const gh = isGHA() ? '-github' : '';
return `rdme${gh}/${pkg.version}`;
};

Expand Down

0 comments on commit bf20f2d

Please sign in to comment.