From 75b0215e568f13894da52dcce86f34cc38899136 Mon Sep 17 00:00:00 2001 From: Stephen Sawchuk Date: Fri, 9 Sep 2016 13:51:58 -0400 Subject: [PATCH] common: add user agent setters --- packages/common/src/grpc-service.js | 10 ++++- packages/common/src/service.js | 5 +++ packages/common/src/util.js | 27 +++++++----- packages/common/test/grpc-service.js | 40 +++++++++++++---- packages/common/test/service.js | 28 +++++++++++- packages/common/test/util.js | 65 +++++----------------------- 6 files changed, 100 insertions(+), 75 deletions(-) diff --git a/packages/common/src/grpc-service.js b/packages/common/src/grpc-service.js index 7919b640ae1..dde6a2401af 100644 --- a/packages/common/src/grpc-service.js +++ b/packages/common/src/grpc-service.js @@ -36,6 +36,12 @@ var through = require('through2'); */ var Service = require('./service.js'); +/** + * @type {module:common/util} + * @private + */ +var util = require('./util.js'); + /** * @const {object} - A map of protobuf codes to HTTP status codes. * @private @@ -170,6 +176,7 @@ function GrpcService(config, options) { } this.maxRetries = options.maxRetries; + this.userAgent = util.getUserAgentFromPackageJson(config.packageJson); var apiVersion = config.apiVersion; var service = this.service = config.service; @@ -732,7 +739,8 @@ GrpcService.prototype.getService_ = function(protoOpts) { if (!service) { service = new proto[protoOpts.service]( proto.baseUrl || this.baseUrl, - this.grpcCredentials + this.grpcCredentials, + { 'grpc.primary_user_agent': this.userAgent } ); this.activeServiceMap_.set(protoOpts.service, service); diff --git a/packages/common/src/service.js b/packages/common/src/service.js index 75f738f948a..54aea3bd33e 100644 --- a/packages/common/src/service.js +++ b/packages/common/src/service.js @@ -57,6 +57,7 @@ function Service(config, options) { this.getCredentials = this.makeAuthenticatedRequest.getCredentials; this.globalInterceptors = arrify(options.interceptors_); this.interceptors = []; + this.packageJson = config.packageJson; this.projectId = options.projectId; this.projectIdRequired = config.projectIdRequired !== false; } @@ -114,6 +115,10 @@ Service.prototype.request = function(reqOpts, callback) { delete reqOpts.interceptors_; + reqOpts.headers = extend({}, reqOpts.headers, { + 'User-Agent': util.getUserAgentFromPackageJson(this.packageJson) + }); + return this.makeAuthenticatedRequest(reqOpts, callback); }; diff --git a/packages/common/src/util.js b/packages/common/src/util.js index f279531aff7..ac7ac524475 100644 --- a/packages/common/src/util.js +++ b/packages/common/src/util.js @@ -38,12 +38,6 @@ var streamEvents = require('stream-events'); var through = require('through2'); var uniq = require('array-uniq'); -/** @const {object} @google-cloud/common's package.json file. */ -var PKG = require('../package.json'); - -/** @const {string} User agent. */ -var USER_AGENT = PKG.name + '/' + PKG.version; - var util = module.exports; var errorMessage = format([ @@ -466,11 +460,6 @@ util.makeRequest = makeRequest; */ function decorateRequest(reqOpts, config) { config = config || {}; - reqOpts.headers = reqOpts.headers || {}; - - var headers = reqOpts.headers; - var userAgent = headers['User-Agent'] || headers['user-agent']; - headers['User-Agent'] = userAgent || config.userAgent || USER_AGENT; if (is.object(reqOpts.qs)) { delete reqOpts.qs.autoPaginate; @@ -625,3 +614,19 @@ function isCustomType(unknown, module) { } util.isCustomType = isCustomType; + +/** + * Create a properly-formatted User-Agent string from a package.json file. + * + * @param {object} packageJson - A module's package.json file. + * @return {string} userAgent - The formatted User-Agent string. + */ +function getUserAgentFromPackageJson(packageJson) { + var hyphenatedPackageName = packageJson.name + .replace('@google-cloud', 'gcloud-node') // For legacy purposes. + .replace('/', '-'); // For UA spec-compliance purposes. + + return hyphenatedPackageName + '/' + packageJson.version; +} + +util.getUserAgentFromPackageJson = getUserAgentFromPackageJson; diff --git a/packages/common/test/grpc-service.js b/packages/common/test/grpc-service.js index 05392a49ea7..f5c57a89f78 100644 --- a/packages/common/test/grpc-service.js +++ b/packages/common/test/grpc-service.js @@ -28,6 +28,7 @@ var sinon = require('sinon').sandbox.create(); var through = require('through2'); var util = require('../src/util.js'); +var fakeUtil = extend({}, util); function FakeService() { this.calledWith_ = arguments; @@ -92,6 +93,10 @@ describe('GrpcService', function() { proto: {}, service: 'Service', apiVersion: 'v1', + packageJson: { + name: '@google-cloud/service', + version: '0.2.0' + }, grpcMetadata: { property: 'value' } @@ -115,7 +120,8 @@ describe('GrpcService', function() { 'google-proto-files': fakeGoogleProtoFiles, 'retry-request': fakeRetryRequest, grpc: fakeGrpc, - './service.js': FakeService + './service.js': FakeService, + './util.js': fakeUtil }); GrpcServiceCached = extend(true, {}, GrpcService); }); @@ -243,6 +249,12 @@ describe('GrpcService', function() { assert.strictEqual(calledWith[1], OPTIONS); }); + it('should set insecure credentials if using customEndpoint', function() { + var config = extend({}, CONFIG, { customEndpoint: true }); + var grpcService = new GrpcService(config, OPTIONS); + assert.strictEqual(grpcService.grpcCredentials.name, 'createInsecure'); + }); + it('should default grpcMetadata to empty metadata', function() { var fakeGrpcMetadata = {}; @@ -277,6 +289,20 @@ describe('GrpcService', function() { assert.strictEqual(grpcService.maxRetries, OPTIONS.maxRetries); }); + it('should set the correct user-agent', function() { + var userAgent = 'user-agent/0.0.0'; + + var getUserAgentFn = fakeUtil.getUserAgentFromPackageJson; + fakeUtil.getUserAgentFromPackageJson = function(packageJson) { + fakeUtil.getUserAgentFromPackageJson = getUserAgentFn; + assert.strictEqual(packageJson, CONFIG.packageJson); + return userAgent; + }; + + var grpcService = new GrpcService(CONFIG, OPTIONS); + assert.strictEqual(grpcService.userAgent, userAgent); + }); + it('should get the root directory for the proto files', function(done) { googleProtoFilesOverride = function(path) { assert.strictEqual(path, '..'); @@ -287,12 +313,6 @@ describe('GrpcService', function() { new GrpcService(CONFIG, OPTIONS); }); - it('should set insecure credentials if using customEndpoint', function() { - var config = extend({}, CONFIG, { customEndpoint: true }); - var grpcService = new GrpcService(config, OPTIONS); - assert.strictEqual(grpcService.grpcCredentials.name, 'createInsecure'); - }); - it('should localize the service', function() { assert.strictEqual(grpcService.service, CONFIG.service); }); @@ -1498,9 +1518,13 @@ describe('GrpcService', function() { grpcService.protos = { Service: { - Service: function(baseUrl, grpcCredentials) { + Service: function(baseUrl, grpcCredentials, userAgent) { assert.strictEqual(baseUrl, grpcService.baseUrl); assert.strictEqual(grpcCredentials, grpcService.grpcCredentials); + assert.deepEqual(userAgent, { + 'grpc.primary_user_agent': grpcService.userAgent + }); + return fakeService; } } diff --git a/packages/common/test/service.js b/packages/common/test/service.js index b6202987aa5..f7ade0d0aba 100644 --- a/packages/common/test/service.js +++ b/packages/common/test/service.js @@ -39,7 +39,11 @@ describe('Service', function() { var CONFIG = { scopes: [], baseUrl: 'base-url', - projectIdRequired: false + projectIdRequired: false, + packageJson: { + name: '@google-cloud/service', + version: '0.2.0' + } }; var OPTIONS = { @@ -129,6 +133,10 @@ describe('Service', function() { assert.deepEqual(service.interceptors, []); }); + it('should localize package.json', function() { + assert.strictEqual(service.packageJson, CONFIG.packageJson); + }); + it('should localize the projectId', function() { assert.strictEqual(service.projectId, OPTIONS.projectId); }); @@ -212,6 +220,24 @@ describe('Service', function() { service.request(reqOpts, assert.ifError); }); + it('should add the User Agent', function(done) { + var userAgent = 'user-agent/0.0.0'; + + var getUserAgentFn = util.getUserAgentFromPackageJson; + util.getUserAgentFromPackageJson = function(packageJson) { + util.getUserAgentFromPackageJson = getUserAgentFn; + assert.strictEqual(packageJson, service.packageJson); + return userAgent; + }; + + service.makeAuthenticatedRequest = function(reqOpts) { + assert.strictEqual(reqOpts.headers['User-Agent'], userAgent); + done(); + }; + + service.request(reqOpts, assert.ifError); + }); + describe('projectIdRequired', function() { describe('false', function() { it('should include the projectId', function(done) { diff --git a/packages/common/test/util.js b/packages/common/test/util.js index 83c49f424ec..8ba26fef3dc 100644 --- a/packages/common/test/util.js +++ b/packages/common/test/util.js @@ -1153,60 +1153,6 @@ describe('common/util', function() { }); describe('decorateRequest', function() { - it('should keep user agent', function() { - var USER_AGENT = 'test/v1'; - - var reqOpts = { - a: 'b', - c: 'd', - headers: { - 'User-Agent': USER_AGENT - } - }; - - var expectedReqOpts = extend({}, reqOpts, { - headers: { - 'User-Agent': USER_AGENT - } - }); - - var decoratedReqOpts = util.decorateRequest(reqOpts); - assert.deepEqual(decoratedReqOpts, expectedReqOpts); - }); - - it('should add the user agent', function() { - var USER_AGENT = 'test/v2'; - - var reqOpts = { a: 'b', c: 'd' }; - - var expectedReqOpts = extend({}, reqOpts, { - headers: { - 'User-Agent': USER_AGENT - } - }); - - var decoratedReqOpts = util.decorateRequest(reqOpts, { - userAgent: USER_AGENT - }); - assert.deepEqual(decoratedReqOpts, expectedReqOpts); - }); - - it('should add the default user agent', function() { - var PKG = require('../package.json'); - var USER_AGENT = PKG.name + '/' + PKG.version; - - var reqOpts = { a: 'b', c: 'd' }; - - var expectedReqOpts = extend({}, reqOpts, { - headers: { - 'User-Agent': USER_AGENT - } - }); - - var decoratedReqOpts = util.decorateRequest(reqOpts); - assert.deepEqual(decoratedReqOpts, expectedReqOpts); - }); - it('should delete qs.autoPaginate', function() { var decoratedReqOpts = util.decorateRequest({ qs: { @@ -1417,4 +1363,15 @@ describe('common/util', function() { }); }); }); + + describe('getUserAgentFromPackageJson', function() { + it('should format a User Agent string from a package.json', function() { + var userAgent = util.getUserAgentFromPackageJson({ + name: '@google-cloud/storage', + version: '0.1.0' + }); + + assert.strictEqual(userAgent, 'gcloud-node-storage/0.1.0'); + }); + }); });