Skip to content

Commit 2a42604

Browse files
authored
chore: standardize User-Agent string: format and include more details (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
1 parent 5423df6 commit 2a42604

File tree

3 files changed

+59
-9
lines changed

3 files changed

+59
-9
lines changed

src/base-commands/twilio-client-command.js

+2-1
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@ class TwilioClientCommand extends BaseCommand {
2626

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

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

54-
this.httpClient = new CliRequestClient(this.id, this.logger, undefined, keytarFlag);
55+
this.httpClient = new CliRequestClient(this.id, this.logger, undefined, keytarFlag, pluginName);
5556
}
5657

5758
async catch(error) {

src/services/cli-http-client.js

+11-7
Original file line numberDiff line numberDiff line change
@@ -14,9 +14,10 @@ const NETWORK_ERROR_CODES = new Set(['ETIMEDOUT', 'ESOCKETTIMEDOUT', 'ECONNABORT
1414
const STANDARD_HEADERS = ['user-agent', 'accept-charset', 'connection', 'authorization', 'accept', 'content-type'];
1515

1616
class CliRequestClient {
17-
constructor(commandName, logger, http, keytarFlag = false) {
17+
constructor(commandName, logger, http, keytarFlag = false, extensions = ' ') {
1818
this.commandName = commandName;
1919
this.logger = logger;
20+
this.pluginName = extensions;
2021
this.http = http || require('axios');
2122
if (process.env.HTTP_PROXY) {
2223
/*
@@ -64,12 +65,15 @@ class CliRequestClient {
6465
const b64Auth = Buffer.from(`${opts.username}:${opts.password}`).toString('base64');
6566
headers.Authorization = `Basic ${b64Auth}`;
6667
}
67-
68-
const componentInfo = (headers['User-Agent'] || '').replace(' (', '|').replace(')', '').split('|');
69-
componentInfo.push(`${os.platform()} ${os.release()} ${os.arch()}`);
70-
componentInfo.push(this.commandName);
71-
componentInfo.push(this.keytarWord);
72-
headers['User-Agent'] = `${pkg.name}/${pkg.version} (${componentInfo.filter(Boolean).join(', ')})`;
68+
// User-Agent will have these info : <plugin/version> <core-api-lib>/<core-api-lib-version> (<os-name> <os-arch>) <extensions>
69+
const componentInfo = [];
70+
componentInfo.push(`(${os.platform()} ${os.arch()})`); // (<os-name> <os-arch>)
71+
const userAgentArr = (headers['User-Agent'] || ' ').split(' '); // contains twilio-node/version (darwin x64) node/v16.4.2
72+
componentInfo.push(userAgentArr[0]); // Api client version
73+
componentInfo.push(userAgentArr[3]); // nodejs version
74+
componentInfo.push(this.commandName); // cli-command
75+
componentInfo.push(this.keytarWord); // keytar flag
76+
headers['User-Agent'] = `${this.pluginName} ${pkg.name}/${pkg.version} ${componentInfo.filter(Boolean).join(' ')}`;
7377

7478
const options = {
7579
timeout: opts.timeout || 30000,

test/services/cli-http-client.test.js

+46-1
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,11 @@
1+
const os = require('os');
2+
13
const { expect, test } = require('@twilio/cli-test');
24

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

710
describe('services', () => {
811
describe('cli-http-client', () => {
@@ -19,9 +22,11 @@ describe('services', () => {
1922
return { status: 200, data: 'foo', headers: {} };
2023
},
2124
true,
25+
'blah',
2226
);
2327
expect(client.commandName).to.equal('blah');
2428
expect(client.keytarWord).to.equal('keytar');
29+
expect(client.pluginName).to.equal('blah');
2530
const response = await client.request({
2631
method: 'POST',
2732
uri: 'https://foo.com/bar',
@@ -38,9 +43,10 @@ describe('services', () => {
3843

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

74+
test
75+
.nock('https://foo.com', (api) => {
76+
api.get('/bar').reply(200, '', {
77+
'User-Agent': `twilio-cli/2.27.1 ${pkg.name}\/${
78+
pkg.version
79+
} \(${os.platform()} ${os.arch()}\) dummyCommand keytar`,
80+
});
81+
})
82+
.it('correctly sets user-agent', async () => {
83+
const client = new CliRequestClient('dummyCommand', logger, '', true, 'twilio-cli/2.27.1');
84+
const response = await client.request({
85+
method: 'GET',
86+
uri: 'https://foo.com/bar',
87+
});
88+
expect(client.keytarWord).to.equal('keytar');
89+
expect(client.lastRequest.headers['User-Agent']).to.equal(
90+
`twilio-cli/2.27.1 ${pkg.name}\/${pkg.version} \(${os.platform()} ${os.arch()}\) dummyCommand keytar`,
91+
);
92+
expect(response.statusCode).to.equal(200);
93+
});
94+
95+
test
96+
.nock('https://foo.com', (api) => {
97+
api.get('/bar').reply(200, '', {
98+
'User-Agent': ` ${pkg.name}\/${pkg.version} \(${os.platform()} ${os.arch()}\) dummyCommand keytar`,
99+
});
100+
})
101+
.it('correctly sets user-agent with empty plugin value', async () => {
102+
const client = new CliRequestClient('dummyCommand', logger, '', true, '');
103+
const response = await client.request({
104+
method: 'GET',
105+
uri: 'https://foo.com/bar',
106+
});
107+
expect(client.lastRequest.headers['User-Agent']).to.equal(
108+
` ${pkg.name}\/${pkg.version} \(${os.platform()} ${os.arch()}\) dummyCommand keytar`,
109+
);
110+
expect(response.statusCode).to.equal(200);
111+
});
112+
68113
test
69114
.nock('https://foo.com', (api) => {
70115
api.get('/bar?foo=bar&foo=baz').reply(200);

0 commit comments

Comments
 (0)