Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: add ability to augment the user agent #76

Merged
merged 1 commit into from Jan 21, 2019
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
2 changes: 2 additions & 0 deletions package.json
Expand Up @@ -31,6 +31,7 @@
"license": "Apache-2.0",
"devDependencies": {
"@compodoc/compodoc": "^1.1.7",
"@types/chai": "^4.1.7",
"@types/execa": "^0.9.0",
"@types/mocha": "^5.2.5",
"@types/mv": "^2.1.0",
Expand All @@ -41,6 +42,7 @@
"@types/tmp": "0.0.33",
"@types/url-template": "^2.0.28",
"@types/uuid": "^3.4.3",
"chai": "^4.2.0",
"codecov": "^3.0.4",
"gts": "^0.9.0",
"ink-docstrap": "^1.3.2",
Expand Down
18 changes: 17 additions & 1 deletion src/api.ts
Expand Up @@ -18,7 +18,7 @@ import {Endpoint} from './endpoint';

// tslint:disable-next-line no-any
export interface APIRequestParams<T = any> {
options: GaxiosOptions;
options: MethodOptions;
params: T;
requiredParams: string[];
pathParams: string[];
Expand All @@ -45,7 +45,23 @@ export interface GlobalOptions extends GaxiosOptions {

export interface MethodOptions extends GaxiosOptions {
rootUrl?: string;
userAgentDirectives?: UserAgentDirective[];
}

/**
* An additional directive to add to the user agent header.
* Directives come in the form of:
* User-Agent: <product> / <product-version> <comment>
*
* For more information, see:
* https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/User-Agent
*/
export interface UserAgentDirective {
product: string;
version: string;
comment?: string;
}

export interface ServiceOptions extends GlobalOptions {
version?: string;
}
Expand Down
28 changes: 21 additions & 7 deletions src/apirequest.ts
Expand Up @@ -24,7 +24,6 @@ import {SchemaParameters} from './schema';

// tslint:disable-next-line no-var-requires
const pkg = require('../../package.json');
const USER_AGENT = `google-api-nodejs-client/${pkg.version} (gzip)`;

function isReadableStream(obj: stream.Readable|string) {
return obj instanceof stream.Readable && typeof obj._read === 'function';
Expand Down Expand Up @@ -145,7 +144,7 @@ async function createAPIRequestAsync<T>(parameters: APIRequestParams) {
// values are serialized like this:
// myParams: ['one', 'two'] ---> 'myParams=one&myParams=two'
// This serializer also encodes spaces in the querystring as `%20`,
// whereas the default serializer in axios encodes to a `+`.
// whereas the default serializer in gaxios encodes to a `+`.
options.paramsSerializer = (params) => {
return qs.stringify(params, {arrayFormat: 'repeat'});
};
Expand All @@ -165,7 +164,7 @@ async function createAPIRequestAsync<T>(parameters: APIRequestParams) {
if (parameters.mediaUrl && media.body) {
options.url = parameters.mediaUrl;
if (resource) {
// Axios doesn't support multipart/related uploads, so it has to
// gaxios doesn't support multipart/related uploads, so it has to
// be implemented here.
params.uploadType = 'multipart';
const multipart = [
Expand All @@ -189,7 +188,7 @@ async function createAPIRequestAsync<T>(parameters: APIRequestParams) {
rStream.push(part.body);
rStream.push('\r\n');
} else {
// Axios does not natively support onUploadProgress in node.js.
// Gaxios does not natively support onUploadProgress in node.js.
// Pipe through the pStream first to read the number of bytes read
// for the purpose of tracking progress.
pStream.on('progress', bytesRead => {
Expand Down Expand Up @@ -223,10 +222,25 @@ async function createAPIRequestAsync<T>(parameters: APIRequestParams) {
options.params = params;
if (!isBrowser()) {
options.headers!['Accept-Encoding'] = 'gzip';
options.headers!['User-Agent'] = USER_AGENT;
const directives = options.userAgentDirectives || [];
directives.push({
product: 'google-api-nodejs-client',
version: pkg.version,
comment: 'gzip'
});
const userAgent = directives
.map(d => {
let line = `${d.product}/${d.version}`;
if (d.comment) {
line += ` (${d.comment})`;
}
return line;
})
.join(' ');
options.headers!['User-Agent'] = userAgent;
}

// By default Axios treats any 2xx as valid, and all non 2xx status
// By default gaxios treats any 2xx as valid, and all non 2xx status
// codes as errors. This is a problem for HTTP 304s when used along
// with an eTag.
if (!options.validateStatus) {
Expand All @@ -235,7 +249,7 @@ async function createAPIRequestAsync<T>(parameters: APIRequestParams) {
};
}

// Combine the AxiosRequestConfig options passed with this specific
// Combine the GaxiosOptions options passed with this specific
// API call witht the global options configured at the API Context
// level, or at the global level.
const mergedOptions = Object.assign(
Expand Down
24 changes: 22 additions & 2 deletions test/test.apirequest.ts
Expand Up @@ -11,7 +11,7 @@
// See the License for the specific language governing permissions and
// limitations under the License.

import * as assert from 'assert';
import {assert} from 'chai';
import * as nock from 'nock';
import {createAPIRequest} from '../src/apirequest';

Expand Down Expand Up @@ -49,6 +49,26 @@ it('should create a valid API request', async () => {
context: fakeContext
});
scope.done();
assert.strictEqual(result.data, fakeResponse);
assert.strictEqual(result.data, fakeResponse as {});
assert(result);
});

it('should include directives in the user agent', async () => {
const scope = nock(url).get('/').reply(200);
const res = await createAPIRequest<FakeParams>({
options: {
url,
userAgentDirectives:
[{product: 'frog', version: '1.0', comment: 'jumps'}]
},
params: {},
requiredParams: [],
pathParams: [],
context: fakeContext
});
scope.done();
// frog/1.0 (jumps) google-api-nodejs-client/0.6.0 (gzip)
const userAgent = res.config.headers!['User-Agent'];
assert.match(userAgent, /frog\/1.0 \(jumps\)/);
assert.match(userAgent, /google-api-nodejs-client\/.* \(gzip\)/);
});