Skip to content

Commit

Permalink
chore: standardize User-Agent string: format and include more details (
Browse files Browse the repository at this point in the history
…twilio#140)

* User Agent Upgrade

Changes to make standardised format for User Agent

* Added <extensions> tag to User Agent

The standardised format for user-agent:
<core-api-lib>/<core-api-lib-version> (<os-name> <os-arch>) <extensions>

* Test case coverage and formatting changes

* Prettier format changes

Updated format acc to prettier warning

Review comment changes

Added testcases, prettier format changes

TestCases update + Prettier
  • Loading branch information
shrutiburman committed Aug 11, 2021
1 parent 5423df6 commit 2a42604
Show file tree
Hide file tree
Showing 3 changed files with 59 additions and 9 deletions.
3 changes: 2 additions & 1 deletion src/base-commands/twilio-client-command.js
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ class TwilioClientCommand extends BaseCommand {

this.currentProfile = this.userConfig.getProfileById(this.flags.profile);
let keytarFlag = false;
const pluginName = (this.config.userAgent || ' ').split(' ')[0];

const reportUnconfigured = (verb, message = '') => {
const profileParam = this.flags.profile ? ` --profile "${this.flags.profile}"` : '';
Expand All @@ -51,7 +52,7 @@ class TwilioClientCommand extends BaseCommand {
keytarFlag = true;
}

this.httpClient = new CliRequestClient(this.id, this.logger, undefined, keytarFlag);
this.httpClient = new CliRequestClient(this.id, this.logger, undefined, keytarFlag, pluginName);
}

async catch(error) {
Expand Down
18 changes: 11 additions & 7 deletions src/services/cli-http-client.js
Original file line number Diff line number Diff line change
Expand Up @@ -14,9 +14,10 @@ const NETWORK_ERROR_CODES = new Set(['ETIMEDOUT', 'ESOCKETTIMEDOUT', 'ECONNABORT
const STANDARD_HEADERS = ['user-agent', 'accept-charset', 'connection', 'authorization', 'accept', 'content-type'];

class CliRequestClient {
constructor(commandName, logger, http, keytarFlag = false) {
constructor(commandName, logger, http, keytarFlag = false, extensions = ' ') {
this.commandName = commandName;
this.logger = logger;
this.pluginName = extensions;
this.http = http || require('axios');
if (process.env.HTTP_PROXY) {
/*
Expand Down Expand Up @@ -64,12 +65,15 @@ class CliRequestClient {
const b64Auth = Buffer.from(`${opts.username}:${opts.password}`).toString('base64');
headers.Authorization = `Basic ${b64Auth}`;
}

const componentInfo = (headers['User-Agent'] || '').replace(' (', '|').replace(')', '').split('|');
componentInfo.push(`${os.platform()} ${os.release()} ${os.arch()}`);
componentInfo.push(this.commandName);
componentInfo.push(this.keytarWord);
headers['User-Agent'] = `${pkg.name}/${pkg.version} (${componentInfo.filter(Boolean).join(', ')})`;
// User-Agent will have these info : <plugin/version> <core-api-lib>/<core-api-lib-version> (<os-name> <os-arch>) <extensions>
const componentInfo = [];
componentInfo.push(`(${os.platform()} ${os.arch()})`); // (<os-name> <os-arch>)
const userAgentArr = (headers['User-Agent'] || ' ').split(' '); // contains twilio-node/version (darwin x64) node/v16.4.2
componentInfo.push(userAgentArr[0]); // Api client version
componentInfo.push(userAgentArr[3]); // nodejs version
componentInfo.push(this.commandName); // cli-command
componentInfo.push(this.keytarWord); // keytar flag
headers['User-Agent'] = `${this.pluginName} ${pkg.name}/${pkg.version} ${componentInfo.filter(Boolean).join(' ')}`;

const options = {
timeout: opts.timeout || 30000,
Expand Down
47 changes: 46 additions & 1 deletion test/services/cli-http-client.test.js
Original file line number Diff line number Diff line change
@@ -1,8 +1,11 @@
const os = require('os');

const { expect, test } = require('@twilio/cli-test');

const CliRequestClient = require('../../src/services/cli-http-client');
const { TwilioCliError } = require('../../src/services/error');
const { Logger, LoggingLevel } = require('../../src/services/messaging/logging');
const pkg = require('../../package.json');

describe('services', () => {
describe('cli-http-client', () => {
Expand All @@ -19,9 +22,11 @@ describe('services', () => {
return { status: 200, data: 'foo', headers: {} };
},
true,
'blah',
);
expect(client.commandName).to.equal('blah');
expect(client.keytarWord).to.equal('keytar');
expect(client.pluginName).to.equal('blah');
const response = await client.request({
method: 'POST',
uri: 'https://foo.com/bar',
Expand All @@ -38,9 +43,10 @@ describe('services', () => {

test.it('should add the correct http agent for proxy', async () => {
process.env.HTTP_PROXY = 'http://someproxy.com:8080';
const client = new CliRequestClient('blah', logger, { defaults: {} }, false);
const client = new CliRequestClient('blah', logger, { defaults: {} }, false, 'blah');
const httpAgent = client.http.defaults.httpsAgent;
expect(client.keytarWord).to.equal('');
expect(client.pluginName).to.equal('blah');
expect(httpAgent.proxy.host).to.equal('someproxy.com');
expect(httpAgent.proxy.port).to.equal(8080);
});
Expand All @@ -65,6 +71,45 @@ describe('services', () => {
await expect(request).to.be.rejectedWith(TwilioCliError);
});

test
.nock('https://foo.com', (api) => {
api.get('/bar').reply(200, '', {
'User-Agent': `twilio-cli/2.27.1 ${pkg.name}\/${
pkg.version
} \(${os.platform()} ${os.arch()}\) dummyCommand keytar`,
});
})
.it('correctly sets user-agent', async () => {
const client = new CliRequestClient('dummyCommand', logger, '', true, 'twilio-cli/2.27.1');
const response = await client.request({
method: 'GET',
uri: 'https://foo.com/bar',
});
expect(client.keytarWord).to.equal('keytar');
expect(client.lastRequest.headers['User-Agent']).to.equal(
`twilio-cli/2.27.1 ${pkg.name}\/${pkg.version} \(${os.platform()} ${os.arch()}\) dummyCommand keytar`,
);
expect(response.statusCode).to.equal(200);
});

test
.nock('https://foo.com', (api) => {
api.get('/bar').reply(200, '', {
'User-Agent': ` ${pkg.name}\/${pkg.version} \(${os.platform()} ${os.arch()}\) dummyCommand keytar`,
});
})
.it('correctly sets user-agent with empty plugin value', async () => {
const client = new CliRequestClient('dummyCommand', logger, '', true, '');
const response = await client.request({
method: 'GET',
uri: 'https://foo.com/bar',
});
expect(client.lastRequest.headers['User-Agent']).to.equal(
` ${pkg.name}\/${pkg.version} \(${os.platform()} ${os.arch()}\) dummyCommand keytar`,
);
expect(response.statusCode).to.equal(200);
});

test
.nock('https://foo.com', (api) => {
api.get('/bar?foo=bar&foo=baz').reply(200);
Expand Down

0 comments on commit 2a42604

Please sign in to comment.