diff --git a/packages/logging/package.json b/packages/logging/package.json index 1b40920c474..f453993e762 100644 --- a/packages/logging/package.json +++ b/packages/logging/package.json @@ -58,10 +58,14 @@ "async": "^2.1.4", "eventid": "^0.1.0", "extend": "^3.0.0", - "google-gax": "^0.12.2", + "google-auto-auth": "^0.5.4", + "google-gax": "^0.13.0", "google-proto-files": "^0.10.0", "is": "^3.0.1", - "string-format-obj": "^1.0.0" + "pumpify": "^1.3.5", + "stream-events": "^1.0.1", + "string-format-obj": "^1.0.0", + "through2": "^2.0.3" }, "devDependencies": { "@google-cloud/bigquery": "*", diff --git a/packages/logging/src/index.js b/packages/logging/src/index.js index 8e669785c29..46bc05d79c6 100644 --- a/packages/logging/src/index.js +++ b/packages/logging/src/index.js @@ -22,12 +22,15 @@ var arrify = require('arrify'); var common = require('@google-cloud/common'); -var commonGrpc = require('@google-cloud/common-grpc'); var extend = require('extend'); var format = require('string-format-obj'); -var googleProtoFiles = require('google-proto-files'); +var googleAuth = require('google-auto-auth'); var is = require('is'); -var util = require('util'); +var pumpify = require('pumpify'); +var streamEvents = require('stream-events'); +var through = require('through2'); + +var v2 = require('./v2'); /** * @type {module:logging/entry} @@ -66,26 +69,16 @@ function Logging(options) { return new Logging(options); } - var config = { - baseUrl: 'logging.googleapis.com', - service: 'logging', - apiVersion: 'v2', - protoServices: { - ConfigServiceV2: - googleProtoFiles('logging', 'v2', 'logging_config.proto'), - LoggingServiceV2: googleProtoFiles.logging.v2 - }, - scopes: [ - 'https://www.googleapis.com/auth/cloud-platform' - ], - packageJson: require('../package.json') - }; + var options_ = extend({ + scopes: v2.ALL_SCOPES + }, options); - commonGrpc.Service.call(this, config, options); + this.api = {}; + this.auth = googleAuth(options); + this.options = options_; + this.projectId = options.projectId || '{{projectId}}'; } -util.inherits(Logging, commonGrpc.Service); - // jscs:disable maximumLineLength /** * Create a sink. @@ -100,6 +93,8 @@ util.inherits(Logging, commonGrpc.Service); * @param {string} name - Name of the sink. * @param {object} config - See a * [Sink resource](https://cloud.google.com/logging/docs/reference/v2/rest/v2/projects.sinks#LogSink). + * @param {object} config.gaxOptions - Request configuration options, outlined + * here: https://googleapis.github.io/gax-nodejs/global.html#CallOptions. * @param {module:storage/bucket|module:bigquery/dataset|module:pubsub/topic} config.destination - * The destination. The proper ACL scopes will be granted to the provided * destination. @@ -161,17 +156,19 @@ Logging.prototype.createSink = function(name, config, callback) { return; } - var protoOpts = { - service: 'ConfigServiceV2', - method: 'createSink' - }; - var reqOpts = { parent: 'projects/' + this.projectId, sink: extend({}, config, { name: name }) }; - this.request(protoOpts, reqOpts, function(err, resp) { + delete reqOpts.sink.gaxOptions; + + this.request({ + client: 'configServiceV2Client', + method: 'createSink', + reqOpts: reqOpts, + gaxOpts: config.gaxOptions + }, function(err, resp) { if (err) { callback(err, null, resp); return; @@ -241,8 +238,8 @@ Logging.prototype.entry = function(resource, data) { * @param {string} options.filter - An * [advanced logs filter](https://cloud.google.com/logging/docs/view/advanced_filters). * An empty filter matches all log entries. - * @param {number} options.maxApiCalls - Maximum number of API calls to make. - * @param {number} options.maxResults - Maximum number of results to return. + * @param {object} options.gaxOptions - Request configuration options, outlined + * here: https://googleapis.github.io/gax-nodejs/global.html#CallOptions. * @param {string} options.orderBy - How the results should be sorted, * `timestamp` (oldest first) and `timestamp desc` (newest first, * **default**). @@ -288,34 +285,33 @@ Logging.prototype.getEntries = function(options, callback) { options = {}; } - var protoOpts = { - service: 'LoggingServiceV2', - method: 'listLogEntries' - }; - var reqOpts = extend({ orderBy: 'timestamp desc' }, options); - reqOpts.projectIds = arrify(reqOpts.projectIds); - reqOpts.projectIds.push(this.projectId); - this.request(protoOpts, reqOpts, function(err, resp) { - if (err) { - callback(err, null, null, resp); - return; - } + reqOpts.resourceNames = arrify(reqOpts.resourceNames); + reqOpts.resourceNames.push('projects/' + this.projectId); - var nextQuery = null; + delete reqOpts.autoPaginate; + delete reqOpts.gaxOptions; - if (resp.nextPageToken) { - nextQuery = extend({}, reqOpts, { - pageToken: resp.nextPageToken - }); - } + var gaxOptions = extend({ + autoPaginate: options.autoPaginate + }, options.gaxOptions); + + this.request({ + client: 'loggingServiceV2Client', + method: 'listLogEntries', + reqOpts: reqOpts, + gaxOpts: gaxOptions + }, function() { + var entries = arguments[1]; - var entries = arrify(resp.entries).map(Entry.fromApiResponse_); + if (entries) { + arguments[1] = entries.map(Entry.fromApiResponse_); + } - callback(null, entries, nextQuery, resp); + callback.apply(null, arguments); }); }; @@ -347,7 +343,49 @@ Logging.prototype.getEntries = function(options, callback) { * this.end(); * }); */ -Logging.prototype.getEntriesStream = common.paginator.streamify('getEntries'); +Logging.prototype.getEntriesStream = function(options) { + var self = this; + + var requestStream; + + var userStream = streamEvents(pumpify.obj()); + + userStream.abort = function() { + if (requestStream) { + requestStream.abort(); + } + }; + + var toEntryStream = through.obj(function(entry, _, next) { + next(null, Entry.fromApiResponse_(entry)); + }); + + userStream.once('reading', function() { + var reqOpts = extend({ + orderBy: 'timestamp desc' + }, options); + reqOpts.resourceNames = arrify(reqOpts.resourceNames); + reqOpts.resourceNames.push('projects/' + self.projectId); + + delete reqOpts.autoPaginate; + delete reqOpts.gaxOptions; + + var gaxOptions = extend({ + autoPaginate: options.autoPaginate + }, options.gaxOptions); + + requestStream = self.request({ + client: 'loggingServiceV2Client', + method: 'listLogEntriesStream', + reqOpts: reqOpts, + gaxOpts: gaxOptions + }); + + userStream.setPipeline(requestStream, toEntryStream); + }); + + return userStream; +}; /** * Get the sinks associated with this project. @@ -357,8 +395,8 @@ Logging.prototype.getEntriesStream = common.paginator.streamify('getEntries'); * @param {object=} options - Configuration object. * @param {boolean} options.autoPaginate - Have pagination handled * automatically. Default: true. - * @param {number} options.maxApiCalls - Maximum number of API calls to make. - * @param {number} options.maxResults - Maximum number of results to return. + * @param {object} options.gaxOptions - Request configuration options, outlined + * here: https://googleapis.github.io/gax-nodejs/global.html#CallOptions. * @param {function} callback - The callback function. * @param {?error} callback.err - An error returned while making this request. * @param {module:logging/sink[]} callback.sinks - Sink objects. @@ -384,36 +422,34 @@ Logging.prototype.getSinks = function(options, callback) { options = {}; } - var protoOpts = { - service: 'ConfigServiceV2', - method: 'listSinks' - }; - var reqOpts = extend({}, options, { parent: 'projects/' + this.projectId }); - this.request(protoOpts, reqOpts, function(err, resp) { - if (err) { - callback(err, null, null, resp); - return; - } - - var nextQuery = null; - - if (resp.nextPageToken) { - nextQuery = extend({}, options, { - pageToken: resp.nextPageToken + delete reqOpts.autoPaginate; + delete reqOpts.gaxOptions; + + var gaxOptions = extend({ + autoPaginate: options.autoPaginate + }, options.gaxOptions); + + this.request({ + client: 'configServiceV2Client', + method: 'listSinks', + reqOpts: reqOpts, + gaxOpts: gaxOptions + }, function() { + var sinks = arguments[1]; + + if (sinks) { + arguments[1] = sinks.map(function(sink) { + var sinkInstance = self.sink(sink.name); + sinkInstance.metadata = sink; + return sinkInstance; }); } - var sinks = arrify(resp.sinks).map(function(sink) { - var sinkInstance = self.sink(sink.name); - sinkInstance.metadata = sink; - return sinkInstance; - }); - - callback(null, sinks, nextQuery, resp); + callback.apply(null, arguments); }); }; @@ -444,7 +480,49 @@ Logging.prototype.getSinks = function(options, callback) { * this.end(); * }); */ -Logging.prototype.getSinksStream = common.paginator.streamify('getSinks'); +Logging.prototype.getSinksStream = function(options) { + var self = this; + + options = options || {}; + + var requestStream; + var userStream = streamEvents(pumpify.obj()); + + userStream.abort = function() { + if (requestStream) { + requestStream.abort(); + } + }; + + var toSinkStream = through.obj(function(sink, _, next) { + var sinkInstance = self.sink(sink.name); + sinkInstance.metadata = sink; + next(null, sinkInstance); + }); + + userStream.once('reading', function() { + var reqOpts = extend({}, options, { + parent: 'projects/' + self.projectId + }); + + delete reqOpts.gaxOptions; + + var gaxOptions = extend({ + autoPaginate: options.autoPaginate + }, options.gaxOptions); + + requestStream = self.request({ + client: 'configServiceV2Client', + method: 'listSinksStream', + reqOpts: reqOpts, + gaxOpts: gaxOptions + }); + + userStream.setPipeline(requestStream, toSinkStream); + }); + + return userStream; +}; /** * Get a reference to a Stackdriver Logging log. @@ -479,6 +557,95 @@ Logging.prototype.sink = function(name) { return new Sink(this, name); }; +/** + * Funnel all API requests through this method, to be sure we have a project ID. + * + * @param {object} config - Configuration object. + * @param {object} config.gaxOpts - GAX options. + * @param {function} config.method - The gax method to call. + * @param {object} config.reqOpts - Request options. + * @param {function=} callback - The callback function. + */ +Logging.prototype.request = function(config, callback) { + var self = this; + var isStreamMode = !callback; + + var gaxStream; + var stream; + + if (isStreamMode) { + stream = streamEvents(through.obj()); + + stream.abort = function() { + if (gaxStream && gaxStream.cancel) { + gaxStream.cancel(); + } + }; + + stream.once('reading', makeRequestStream); + } else { + makeRequestCallback(); + } + + function prepareGaxRequest(callback) { + self.auth.getProjectId(function(err, projectId) { + if (err) { + callback(err); + return; + } + + var gaxClient = self.api[config.client]; + + if (!gaxClient) { + // Lazily instantiate client. + gaxClient = v2(self.options)[config.client](self.options); + self.api[config.client] = gaxClient; + } + + var reqOpts = extend(true, {}, config.reqOpts); + reqOpts = common.util.replaceProjectIdToken(reqOpts, projectId); + + var requestFn = gaxClient[config.method].bind( + gaxClient, + reqOpts, + config.gaxOpts + ); + + callback(null, requestFn); + }); + } + + function makeRequestCallback() { + prepareGaxRequest(function(err, requestFn) { + if (err) { + callback(err); + return; + } + + requestFn(callback); + }); + } + + function makeRequestStream() { + prepareGaxRequest(function(err, requestFn) { + if (err) { + stream.destroy(err); + return; + } + + gaxStream = requestFn(); + + gaxStream + .on('error', function(err) { + stream.destroy(err); + }) + .pipe(stream); + }); + } + + return stream; +}; + /** * This method is called when creating a sink with a Bucket destination. The * bucket must first grant proper ACL access to the Stackdriver Logging account. @@ -603,7 +770,12 @@ common.paginator.extend(Logging, ['getEntries', 'getSinks']); * that a callback is omitted. */ common.util.promisifyAll(Logging, { - exclude: ['entry', 'log', 'sink'] + exclude: [ + 'entry', + 'log', + 'request', + 'sink' + ] }); Logging.Entry = Entry; @@ -612,4 +784,4 @@ Logging.Logging = Logging; Logging.Sink = Sink; module.exports = Logging; -module.exports.v2 = require('./v2'); +module.exports.v2 = v2; diff --git a/packages/logging/src/log.js b/packages/logging/src/log.js index aab982658f9..9c4855f60ad 100644 --- a/packages/logging/src/log.js +++ b/packages/logging/src/log.js @@ -23,10 +23,8 @@ var arrify = require('arrify'); var async = require('async'); var common = require('@google-cloud/common'); -var commonGrpc = require('@google-cloud/common-grpc'); var extend = require('extend'); var is = require('is'); -var util = require('util'); /** * @type {module:logging/entry} @@ -68,56 +66,13 @@ function Log(logging, name, options) { options = options || {}; this.formattedName_ = Log.formatName_(logging.projectId, name); - this.name = this.formattedName_.split('/').pop(); - this.removeCircular_ = !!options.removeCircular; - + this.removeCircular_ = options.removeCircular === true; this.metadata_ = new Metadata(logging); - var methods = { - /** - * Delete the log. - * - * @resource [projects.logs.delete API Documentation]{@link https://cloud.google.com/logging/docs/reference/v2/rest/v2/projects.logs/delete} - * - * @param {function=} callback - The callback function. - * @param {?error} callback.err - An error returned while making this - * request. - * @param {object} callback.apiResponse - The full API response. - * - * @example - * log.delete(function(err, apiResponse) { - * if (!err) { - * // The log was deleted. - * } - * }); - * - * //- - * // If the callback is omitted, we'll return a Promise. - * //- - * log.delete().then(function(data) { - * var apiResponse = data[0]; - * }); - */ - delete: { - protoOpts: { - service: 'LoggingServiceV2', - method: 'deleteLog' - }, - reqOpts: { - logName: this.formattedName_ - } - } - }; - - commonGrpc.ServiceObject.call(this, { - parent: logging, - id: this.name, - methods: methods - }); + this.logging = logging; + this.name = this.formattedName_.split('/').pop(); } -util.inherits(Log, commonGrpc.ServiceObject); - /** * Return an array of log entries with the desired severity assigned. * @@ -231,6 +186,50 @@ Log.prototype.debug = function(entry, options, callback) { this.write(Log.assignSeverityToEntries_(entry, 'DEBUG'), options, callback); }; +/** + * Delete the log. + * + * @resource [projects.logs.delete API Documentation]{@link https://cloud.google.com/logging/docs/reference/v2/rest/v2/projects.logs/delete} + * + * @param {object=} gaxOptions - Request configuration options, outlined + * here: https://googleapis.github.io/gax-nodejs/global.html#CallOptions. + * @param {function=} callback - The callback function. + * @param {?error} callback.err - An error returned while making this + * request. + * @param {object} callback.apiResponse - The full API response. + * + * @example + * log.delete(function(err, apiResponse) { + * if (!err) { + * // The log was deleted. + * } + * }); + * + * //- + * // If the callback is omitted, we'll return a Promise. + * //- + * log.delete().then(function(data) { + * var apiResponse = data[0]; + * }); + */ +Log.prototype.delete = function(gaxOptions, callback) { + if (is.fn(gaxOptions)) { + callback = gaxOptions; + gaxOptions = {}; + } + + var reqOpts = { + logName: this.formattedName_ + }; + + this.logging.request({ + client: 'loggingServiceV2Client', + method: 'deleteLog', + reqOpts: reqOpts, + gaxOpts: gaxOptions + }, callback); +}; + /** * Write a log entry with a severity of "EMERGENCY". * @@ -311,7 +310,7 @@ Log.prototype.entry = function(metadata, data) { logName: this.formattedName_ }); - return this.parent.entry(metadata, data); + return this.logging.entry(metadata, data); }; /** @@ -401,7 +400,7 @@ Log.prototype.getEntries = function(options, callback) { filter: 'logName="' + this.formattedName_ + '"' }, options); - return this.parent.getEntries(options, callback); + return this.logging.getEntries(options, callback); }; /** @@ -437,7 +436,7 @@ Log.prototype.getEntriesStream = function(options) { filter: 'logName="' + this.formattedName_ + '"' }, options); - return this.parent.getEntriesStream(options); + return this.logging.getEntriesStream(options); }; /** @@ -523,6 +522,8 @@ Log.prototype.warning = function(entry, options, callback) { * @param {module:logging/entry|module:logging/entry[]} entry - A log entry, or * array of entries, to write. * @param {object=} options - Configuration object. + * @param {object} options.gaxOptions - Request configuration options, outlined + * here: https://googleapis.github.io/gax-nodejs/global.html#CallOptions. * @param {object[]} options.labels - Labels to set on the log. * @param {object} options.resource - A default monitored resource for entries * where one isn't specified. @@ -592,25 +593,22 @@ Log.prototype.write = function(entry, options, callback) { options = {}; } - var protoOpts = { - service: 'LoggingServiceV2', - method: 'writeLogEntries' - }; - - var reqOpts = extend({ - logName: this.formattedName_ - }, options); - - var entries = arrify(entry); - - this.decorateEntries_(entries, function(err, decoratedEntries) { + this.decorateEntries_(arrify(entry), function(err, decoratedEntries) { // Ignore errors (the API will speak up if it has an issue). - reqOpts.entries = decoratedEntries; + var reqOpts = extend({ + logName: self.formattedName_, + entries: decoratedEntries + }, options); - self.request(protoOpts, reqOpts, function(err, resp) { - callback(err, resp); - }); + delete reqOpts.gaxOptions; + + self.logging.request({ + client: 'loggingServiceV2Client', + method: 'writeLogEntries', + reqOpts: reqOpts, + gaxOpts: options.gaxOptions + }, callback); }); }; diff --git a/packages/logging/src/metadata.js b/packages/logging/src/metadata.js index cda6ae2a084..589981b4c04 100644 --- a/packages/logging/src/metadata.js +++ b/packages/logging/src/metadata.js @@ -35,7 +35,7 @@ * @param {module:logging} logging - The parent Logging instance. */ function Metadata(logging) { - this.logging_ = logging; + this.logging = logging; } /** @@ -157,13 +157,13 @@ Metadata.prototype.assignDefaultResource = function(entryJson, callback) { Metadata.prototype.getDefaultResource = function(callback) { var self = this; - this.getProjectId(function(err, projectId) { + this.logging.auth.getProjectId(function(err, projectId) { if (err) { callback(err); return; } - self.logging_.authClient.getEnvironment(function(err, env) { + self.logging.auth.getEnvironment(function(err, env) { var defaultResource; if (env.IS_APP_ENGINE) { @@ -183,26 +183,4 @@ Metadata.prototype.getDefaultResource = function(callback) { }); }; -/** - * Attempt to retrieve the project ID from the auth client. - * - * @param {function} callback - The callback function. - */ -Metadata.prototype.getProjectId = function(callback) { - if (global.GCLOUD_SANDBOX_ENV) { - return; - } - - var self = this; - - if (this.logging_.projectId) { - setImmediate(function() { - callback(null, self.logging_.projectId); - }); - return; - } - - this.logging_.authClient.getProjectId(callback); -}; - module.exports = Metadata; diff --git a/packages/logging/src/sink.js b/packages/logging/src/sink.js index 25ace550247..9396cc68fee 100644 --- a/packages/logging/src/sink.js +++ b/packages/logging/src/sink.js @@ -21,9 +21,8 @@ 'use strict'; var common = require('@google-cloud/common'); -var commonGrpc = require('@google-cloud/common-grpc'); var extend = require('extend'); -var util = require('util'); +var is = require('is'); /*! Developer Documentation * @@ -47,116 +46,135 @@ var util = require('util'); * var sink = logging.sink('my-sink'); */ function Sink(logging, name) { + this.logging = logging; this.name = name; this.formattedName_ = 'projects/' + logging.projectId + '/sinks/' + name; +} - var methods = { - /** - * Create a sink. - * - * @param {object} config - See {module:logging#createSink}. - * - * @example - * var config = { - * destination: { - * // ... - * } - * }; - * - * sink.create(config, function(err, sink, apiResponse) { - * if (!err) { - * // The sink was created successfully. - * } - * }); - * - * //- - * // If the callback is omitted, we'll return a Promise. - * //- - * sink.create(config).then(function(data) { - * var sink = data[0]; - * var apiResponse = data[1]; - * }); - */ - create: true, +/** + * Create a sink. + * + * @param {object} config - See {module:logging#createSink}. + * + * @example + * var config = { + * destination: { + * // ... + * } + * }; + * + * sink.create(config, function(err, sink, apiResponse) { + * if (!err) { + * // The sink was created successfully. + * } + * }); + * + * //- + * // If the callback is omitted, we'll return a Promise. + * //- + * sink.create(config).then(function(data) { + * var sink = data[0]; + * var apiResponse = data[1]; + * }); + */ +Sink.prototype.create = function(config, callback) { + this.logging.createSink(this.name, config, callback); +}; - /** - * Delete the sink. - * - * @resource [projects.sink.delete API Documentation]{@link https://cloud.google.com/logging/docs/reference/v2/rest/v2/projects.sinks/delete} - * - * @param {function=} callback - The callback function. - * @param {?error} callback.err - An error returned while making this - * request. - * @param {object} callback.apiResponse - The full API response. - * - * @example - * sink.delete(function(err, apiResponse) { - * if (!err) { - * // The log was deleted. - * } - * }); - * - * //- - * // If the callback is omitted, we'll return a Promise. - * //- - * sink.delete().then(function(data) { - * var apiResponse = data[0]; - * }); - */ - delete: { - protoOpts: { - service: 'ConfigServiceV2', - method: 'deleteSink' - }, - reqOpts: { - sinkName: this.formattedName_ - } - }, +/** + * Delete the sink. + * + * @resource [projects.sink.delete API Documentation]{@link https://cloud.google.com/logging/docs/reference/v2/rest/v2/projects.sinks/delete} + * + * @param {object=} gaxOptions - Request configuration options, outlined + * here: https://googleapis.github.io/gax-nodejs/global.html#CallOptions. + * @param {function=} callback - The callback function. + * @param {?error} callback.err - An error returned while making this + * request. + * @param {object} callback.apiResponse - The full API response. + * + * @example + * sink.delete(function(err, apiResponse) { + * if (!err) { + * // The log was deleted. + * } + * }); + * + * //- + * // If the callback is omitted, we'll return a Promise. + * //- + * sink.delete().then(function(data) { + * var apiResponse = data[0]; + * }); + */ +Sink.prototype.delete = function(gaxOptions, callback) { + if (is.fn(gaxOptions)) { + callback = gaxOptions; + gaxOptions = {}; + } - /** - * Get the sink's metadata. - * - * @resource [Sink Resource]{@link https://cloud.google.com/logging/docs/reference/v2/rest/v2/projects.sinks#LogSink} - * @resource [projects.sink.get API Documentation]{@link https://cloud.google.com/logging/docs/reference/v2/rest/v2/projects.sinks/get} - * - * @param {function=} callback - The callback function. - * @param {?error} callback.err - An error returned while making this - * request. - * @param {object} callback.metadata - The sink's metadata. - * @param {object} callback.apiResponse - The full API response. - * - * @example - * sink.getMetadata(function(err, metadata, apiResponse) {}); - * - * //- - * // If the callback is omitted, we'll return a Promise. - * //- - * sink.getMetadata().then(function(data) { - * var metadata = data[0]; - * var apiResponse = data[1]; - * }); - */ - getMetadata: { - protoOpts: { - service: 'ConfigServiceV2', - method: 'getSink' - }, - reqOpts: { - sinkName: this.formattedName_ - } - } + var reqOpts = { + sinkName: this.formattedName_ }; - commonGrpc.ServiceObject.call(this, { - parent: logging, - baseUrl: '/sinks', - id: name, - createMethod: logging.createSink.bind(logging), - methods: methods - }); -} + this.logging.request({ + client: 'configServiceV2Client', + method: 'deleteSink', + reqOpts: reqOpts, + gaxOpts: gaxOptions + }, callback); +}; + +/** + * Get the sink's metadata. + * + * @resource [Sink Resource]{@link https://cloud.google.com/logging/docs/reference/v2/rest/v2/projects.sinks#LogSink} + * @resource [projects.sink.get API Documentation]{@link https://cloud.google.com/logging/docs/reference/v2/rest/v2/projects.sinks/get} + * + * @param {object=} gaxOptions - Request configuration options, outlined + * here: https://googleapis.github.io/gax-nodejs/global.html#CallOptions. + * @param {function=} callback - The callback function. + * @param {?error} callback.err - An error returned while making this + * request. + * @param {object} callback.metadata - The sink's metadata. + * @param {object} callback.apiResponse - The full API response. + * + * @example + * sink.getMetadata(function(err, metadata, apiResponse) {}); + * + * //- + * // If the callback is omitted, we'll return a Promise. + * //- + * sink.getMetadata().then(function(data) { + * var metadata = data[0]; + * var apiResponse = data[1]; + * }); + */ +Sink.prototype.getMetadata = function(gaxOptions, callback) { + var self = this; + + if (is.fn(gaxOptions)) { + callback = gaxOptions; + gaxOptions = {}; + } -util.inherits(Sink, commonGrpc.ServiceObject); + var reqOpts = { + sinkName: this.formattedName_ + }; + + this.logging.request({ + client: 'configServiceV2Client', + method: 'getSink', + reqOpts: reqOpts, + gaxOpts: gaxOptions + }, function() { + if (arguments[1]) { + self.metadata = arguments[1]; + } + + callback.apply(null, arguments); + }); +}; /** * Set the sink's filter. @@ -197,6 +215,8 @@ Sink.prototype.setFilter = function(filter, callback) { * * @param {object} metadata - See a * [Sink resource](https://cloud.google.com/logging/docs/reference/v2/rest/v2/projects.sinks#LogSink). + * @param {object=} metadata.gaxOptions - Request configuration options, + * outlined here: https://googleapis.github.io/gax-nodejs/global.html#CallOptions. * @param {function=} callback - The callback function. * @param {?error} callback.err - An error returned while making this * request. @@ -227,25 +247,24 @@ Sink.prototype.setMetadata = function(metadata, callback) { return; } - var protoOpts = { - service: 'ConfigServiceV2', - method: 'updateSink' - }; - var reqOpts = { sinkName: self.formattedName_, sink: extend({}, currentMetadata, metadata) }; - self.request(protoOpts, reqOpts, function(err, apiResponse) { - if (err) { - callback(err, apiResponse); - return; - } + delete reqOpts.sink.gaxOptions; - self.metadata = apiResponse; + self.logging.request({ + client: 'configServiceV2Client', + method: 'updateSink', + reqOpts: reqOpts, + gaxOpts: metadata.gaxOptions + }, function() { + if (arguments[1]) { + self.metadata = arguments[1]; + } - callback(null, apiResponse); + callback.apply(null, arguments); }); }); }; diff --git a/packages/logging/system-test/logging.js b/packages/logging/system-test/logging.js index c748430d5e7..cc6b8a4aaae 100644 --- a/packages/logging/system-test/logging.js +++ b/packages/logging/system-test/logging.js @@ -18,13 +18,13 @@ var assert = require('assert'); var async = require('async'); -var BigQuery = require('@google-cloud/bigquery'); +var bigqueryLibrary = require('@google-cloud/bigquery'); var exec = require('methmeth'); var extend = require('extend'); var is = require('is'); var prop = require('propprop'); -var PubSub = require('@google-cloud/pubsub'); -var Storage = require('@google-cloud/storage'); +var pubsubLibrary = require('@google-cloud/pubsub'); +var storageLibrary = require('@google-cloud/storage'); var uuid = require('uuid'); var env = require('../../../system-test/env.js'); @@ -34,10 +34,11 @@ describe('Logging', function() { var TESTS_PREFIX = 'gcloud-logging-test'; var WRITE_CONSISTENCY_DELAY_MS = 90000; + var bigQuery = bigqueryLibrary(env); + var pubsub = pubsubLibrary(env); + var storage = storageLibrary(env); + var logging = new Logging(env); - var bigQuery = new BigQuery(env); - var pubsub = new PubSub(env); - var storage = new Storage(env); // Create the possible destinations for sinks that we will create. var bucket = storage.bucket(generateName()); @@ -104,7 +105,7 @@ describe('Logging', function() { } objects = objects.filter(function(object) { - return object.id.indexOf(TESTS_PREFIX) === 0; + return (object.name || object.id).indexOf(TESTS_PREFIX) === 0; }); async.each(objects, exec('delete'), callback); @@ -277,7 +278,10 @@ describe('Logging', function() { }; it('should list log entries', function(done) { - logging.getEntries({ pageSize: 1 }, function(err, entries) { + logging.getEntries({ + autoPaginate: false, + pageSize: 1 + }, function(err, entries) { assert.ifError(err); assert.strictEqual(entries.length, 1); done(); @@ -285,12 +289,15 @@ describe('Logging', function() { }); it('should list log entries as a stream', function(done) { - logging.getEntriesStream({ pageSize: 1 }) + logging.getEntriesStream({ + autoPaginate: false, + pageSize: 1 + }) .on('error', done) .once('data', function() { this.end(); - done(); - }); + }) + .on('end', done); }); describe('log-specific entries', function() { @@ -299,7 +306,10 @@ describe('Logging', function() { }); it('should list log entries', function(done) { - log.getEntries({ pageSize: 1 }, function(err, entries) { + log.getEntries({ + autoPaginate: false, + pageSize: 1 + }, function(err, entries) { assert.ifError(err); assert.strictEqual(entries.length, 1); done(); @@ -307,7 +317,10 @@ describe('Logging', function() { }); it('should list log entries as a stream', function(done) { - log.getEntriesStream({ pageSize: 1 }) + log.getEntriesStream({ + autoPaginate: false, + pageSize: 1 + }) .on('error', done) .once('data', function() { this.end(); @@ -326,6 +339,7 @@ describe('Logging', function() { setTimeout(function() { log.getEntries({ + autoPaginate: false, pageSize: logEntries.length }, function(err, entries) { assert.ifError(err); @@ -365,7 +379,10 @@ describe('Logging', function() { assert.ifError(err); setTimeout(function() { - log.getEntries({ pageSize: 3 }, function(err, entries) { + log.getEntries({ + autoPaginate: false, + pageSize: 3 + }, function(err, entries) { assert.ifError(err); assert.deepEqual(entries.map(prop('data')), [ '3', '2', '1' ]); done(); @@ -383,7 +400,10 @@ describe('Logging', function() { }); setTimeout(function() { - log.getEntries({ pageSize: messages.length }, function(err, entries) { + log.getEntries({ + autoPaginate: false, + pageSize: messages.length + }, function(err, entries) { assert.ifError(err); assert.deepEqual(entries.reverse().map(prop('data')), messages); done(); @@ -403,7 +423,10 @@ describe('Logging', function() { assert.ifError(err); setTimeout(function() { - log.getEntries({ pageSize: 1 }, function(err, entries) { + log.getEntries({ + autoPaginate: false, + pageSize: 1 + }, function(err, entries) { assert.ifError(err); var entry = entries[0]; @@ -435,7 +458,10 @@ describe('Logging', function() { assert.ifError(err); setTimeout(function() { - log.getEntries({ pageSize: 1 }, function(err, entries) { + log.getEntries({ + autoPaginate: false, + pageSize: 1 + }, function(err, entries) { assert.ifError(err); var entry = entries[0]; @@ -457,7 +483,10 @@ describe('Logging', function() { assert.ifError(err); setTimeout(function() { - log.getEntries({ pageSize: 1 }, function(err, entries) { + log.getEntries({ + autoPaginate: false, + pageSize: 1 + }, function(err, entries) { assert.ifError(err); var entry = entries[0]; diff --git a/packages/logging/test/index.js b/packages/logging/test/index.js index 10428e02ae3..39427c5f83c 100644 --- a/packages/logging/test/index.js +++ b/packages/logging/test/index.js @@ -19,10 +19,12 @@ var arrify = require('arrify'); var assert = require('assert'); var extend = require('extend'); -var googleProtoFiles = require('google-proto-files'); var proxyquire = require('proxyquire'); +var through = require('through2'); var util = require('@google-cloud/common').util; +var v2 = require('../src/v2/index.js'); + var extended = false; var fakePaginator = { extend: function(Class, methods) { @@ -42,8 +44,14 @@ var fakePaginator = { } }; +var googleAutoAuthOverride; +function fakeGoogleAutoAuth() { + return (googleAutoAuthOverride || util.noop).apply(null, arguments); +} + var isCustomTypeOverride; var promisifed = false; +var replaceProjectIdTokenOverride; var fakeUtil = extend({}, util, { isCustomType: function() { if (isCustomTypeOverride) { @@ -58,10 +66,27 @@ var fakeUtil = extend({}, util, { } promisifed = true; - assert.deepEqual(options.exclude, ['entry', 'log', 'sink']); + assert.deepEqual(options.exclude, [ + 'entry', + 'log', + 'request', + 'sink' + ]); + }, + replaceProjectIdToken: function(reqOpts) { + if (replaceProjectIdTokenOverride) { + return replaceProjectIdTokenOverride.apply(null, arguments); + } + + return reqOpts; } }); +var v2Override; +function fakeV2() { + return (v2Override || util.noop).apply(null, arguments); +} + function FakeEntry() { this.calledWith_ = arguments; } @@ -78,10 +103,6 @@ function FakeSink() { this.calledWith_ = arguments; } -function FakeGrpcService() { - this.calledWith_ = arguments; -} - describe('Logging', function() { var Logging; var logging; @@ -94,23 +115,23 @@ describe('Logging', function() { paginator: fakePaginator, util: fakeUtil }, - '@google-cloud/common-grpc': { - Service: FakeGrpcService - }, + 'google-auto-auth': fakeGoogleAutoAuth, './log.js': FakeLog, './entry.js': FakeEntry, - './sink.js': FakeSink + './sink.js': FakeSink, + './v2': fakeV2 }); }); beforeEach(function() { + googleAutoAuthOverride = null; isCustomTypeOverride = null; + replaceProjectIdTokenOverride = null; + v2Override = null; logging = new Logging({ projectId: PROJECT_ID }); - logging.projectId = PROJECT_ID; - logging.request = util.noop; }); describe('instantiation', function() { @@ -122,11 +143,6 @@ describe('Logging', function() { assert(promisifed); }); - it('should streamify the correct methods', function() { - assert.strictEqual(logging.getEntriesStream, 'getEntries'); - assert.strictEqual(logging.getSinksStream, 'getSinks'); - }); - it('should normalize the arguments', function() { var options = { projectId: PROJECT_ID, @@ -152,23 +168,43 @@ describe('Logging', function() { fakeUtil.normalizeArguments = normalizeArguments; }); - it('should inherit from GrpcService', function() { - assert(logging instanceof FakeGrpcService); + it('should initialize the API object', function() { + assert.deepEqual(logging.api, {}); + }); - var calledWith = logging.calledWith_[0]; + it('should cache a local google-auto-auth instance', function() { + var fakeGoogleAutoAuthInstance = {}; - assert.strictEqual(calledWith.baseUrl, 'logging.googleapis.com'); - assert.strictEqual(calledWith.service, 'logging'); - assert.strictEqual(calledWith.apiVersion, 'v2'); - assert.deepEqual(calledWith.protoServices, { - ConfigServiceV2: - googleProtoFiles('logging', 'v2', 'logging_config.proto'), - LoggingServiceV2: googleProtoFiles.logging.v2 - }); - assert.deepEqual(calledWith.scopes, [ - 'https://www.googleapis.com/auth/cloud-platform' - ]); - assert.deepEqual(calledWith.packageJson, require('../package.json')); + googleAutoAuthOverride = function() { + return fakeGoogleAutoAuthInstance; + }; + + var logging = new Logging({}); + assert.strictEqual(logging.auth, fakeGoogleAutoAuthInstance); + }); + + it('should localize the options', function() { + var options = { + a: 'b', + c: 'd' + }; + + var logging = new Logging(options); + + assert.notStrictEqual(logging.options, options); + + assert.deepEqual(logging.options, extend({ + scopes: v2.ALL_SCOPES + }, options)); + }); + + it('should set the projectId', function() { + assert.strictEqual(logging.projectId, PROJECT_ID); + }); + + it('should default the projectId to the token', function() { + var logging = new Logging({}); + assert.strictEqual(logging.projectId, '{{projectId}}'); }); }); @@ -250,7 +286,6 @@ describe('Logging', function() { logging.createSink(SINK_NAME, CONFIG, done); }); - describe('API request', function() { it('should make the correct API request', function(done) { var config = { @@ -262,13 +297,15 @@ describe('Logging', function() { name: SINK_NAME }); - logging.request = function(protoOpts, reqOpts) { - assert.strictEqual(protoOpts.service, 'ConfigServiceV2'); - assert.strictEqual(protoOpts.method, 'createSink'); + logging.request = function(config) { + assert.strictEqual(config.client, 'configServiceV2Client'); + assert.strictEqual(config.method, 'createSink'); var expectedParent = 'projects/' + logging.projectId; - assert.strictEqual(reqOpts.parent, expectedParent); - assert.deepEqual(reqOpts.sink, expectedConfig); + assert.strictEqual(config.reqOpts.parent, expectedParent); + assert.deepEqual(config.reqOpts.sink, expectedConfig); + + assert.strictEqual(config.gaxOpts, undefined); done(); }; @@ -276,12 +313,28 @@ describe('Logging', function() { logging.createSink(SINK_NAME, config, assert.ifError); }); + it('should accept GAX options', function(done) { + var config = { + a: 'b', + c: 'd', + gaxOptions: {} + }; + + logging.request = function(config_) { + assert.strictEqual(config_.reqOpts.sink.gaxOptions, undefined); + assert.strictEqual(config_.gaxOpts, config.gaxOptions); + done(); + }; + + logging.createSink(SINK_NAME, config, assert.ifError); + }); + describe('error', function() { var error = new Error('Error.'); var apiResponse = {}; beforeEach(function() { - logging.request = function(protoOpts, reqOpts, callback) { + logging.request = function(config, callback) { callback(error, apiResponse); }; }); @@ -303,7 +356,7 @@ describe('Logging', function() { }; beforeEach(function() { - logging.request = function(protoOpts, reqOpts, callback) { + logging.request = function(config, callback) { callback(null, apiResponse); }; }); @@ -344,10 +397,10 @@ describe('Logging', function() { describe('getEntries', function() { it('should accept only a callback', function(done) { - logging.request = function(protoOpts, reqOpts) { - assert.deepEqual(reqOpts, { + logging.request = function(config) { + assert.deepEqual(config.reqOpts, { orderBy: 'timestamp desc', - projectIds: [logging.projectId] + resourceNames: ['projects/' + logging.projectId] }); done(); }; @@ -358,15 +411,19 @@ describe('Logging', function() { it('should make the correct API request', function(done) { var options = {}; - logging.request = function(protoOpts, reqOpts) { - assert.strictEqual(protoOpts.service, 'LoggingServiceV2'); - assert.strictEqual(protoOpts.method, 'listLogEntries'); + logging.request = function(config) { + assert.strictEqual(config.client, 'loggingServiceV2Client'); + assert.strictEqual(config.method, 'listLogEntries'); - assert.deepEqual(reqOpts, extend(options, { + assert.deepEqual(config.reqOpts, extend(options, { orderBy: 'timestamp desc', - projectIds: [logging.projectId] + resourceNames: ['projects/' + logging.projectId] })); + assert.deepStrictEqual(config.gaxOpts, { + autoPaginate: undefined + }); + done(); }; @@ -378,8 +435,26 @@ describe('Logging', function() { orderBy: 'timestamp asc' }; - logging.request = function(protoOpts, reqOpts) { - assert.deepEqual(reqOpts.orderBy, options.orderBy); + logging.request = function(config) { + assert.deepEqual(config.reqOpts.orderBy, options.orderBy); + done(); + }; + + logging.getEntries(options, assert.ifError); + }); + + it('should accept GAX options', function(done) { + var options = { + a: 'b', + c: 'd', + gaxOptions: { + autoPaginate: true + } + }; + + logging.request = function(config) { + assert.strictEqual(config.reqOpts.gaxOptions, undefined); + assert.deepStrictEqual(config.gaxOpts, options.gaxOptions); done(); }; @@ -387,85 +462,147 @@ describe('Logging', function() { }); describe('error', function() { - var error = new Error('Error.'); - var apiResponse = {}; + var ARGS = [ + new Error('Error.'), + [], + {} + ]; beforeEach(function() { - logging.request = function(protoOpts, reqOpts, callback) { - callback(error, apiResponse); + logging.request = function(config, callback) { + callback.apply(null, ARGS); }; }); it('should execute callback with error & API response', function(done) { - logging.getEntries({}, function(err, entries, nextQuery, apiResponse_) { - assert.strictEqual(err, error); - assert.strictEqual(entries, null); - assert.strictEqual(nextQuery, null); - assert.strictEqual(apiResponse_, apiResponse); - + logging.getEntries({}, function() { + var args = [].slice.call(arguments); + assert.deepStrictEqual(args, ARGS); done(); }); }); }); describe('success', function() { - var apiResponse = { - entries: [ + var ARGS = [ + null, + [ { logName: 'syslog' } ] - }; + ]; beforeEach(function() { - logging.request = function(protoOpts, reqOpts, callback) { - callback(null, apiResponse); + logging.request = function(config, callback) { + callback.apply(null, ARGS); }; }); - it('should build a nextQuery if necessary', function(done) { - var nextPageToken = 'next-page-token'; - var apiResponseWithNextPageToken = extend({}, apiResponse, { - nextPageToken: nextPageToken - }); - var expectedNextQuery = { - orderBy: 'timestamp desc', - projectIds: [logging.projectId], - pageToken: nextPageToken - }; - - logging.request = function(protoOpts, reqOpts, callback) { - callback(null, apiResponseWithNextPageToken); - }; - - logging.getEntries({}, function(err, entries, nextQuery) { + it('should execute callback with entries & API resp', function(done) { + logging.getEntries({}, function(err, entries) { assert.ifError(err); - assert.deepEqual(nextQuery, expectedNextQuery); + var argsPassedToFromApiResponse_ = entries[0]; + assert.strictEqual( + argsPassedToFromApiResponse_[0], + ARGS[1][0] + ); done(); }); }); + }); + }); - it('should execute callback with entries & API resp', function(done) { - logging.getEntries({}, function(err, entries, nextQuery, apiResponse_) { - assert.ifError(err); + describe('getEntriesStream', function() { + var OPTIONS = { + a: 'b', + c: 'd', + gaxOptions: { + a: 'b', + c: 'd' + } + }; - var argsPassedToFromApiResponse_ = entries[0]; - assert.strictEqual( - argsPassedToFromApiResponse_[0], - apiResponse.entries[0] - ); + var REQUEST_STREAM; + var RESULT = {}; - assert.strictEqual(apiResponse_, apiResponse); + beforeEach(function() { + REQUEST_STREAM = through.obj(); + REQUEST_STREAM.push(RESULT); - done(); + logging.request = function() { + return REQUEST_STREAM; + }; + }); + + it('should make request once reading', function(done) { + logging.request = function(config) { + assert.strictEqual(config.client, 'loggingServiceV2Client'); + assert.strictEqual(config.method, 'listLogEntriesStream'); + + assert.deepEqual(config.reqOpts, { + resourceNames: [ + 'projects/' + logging.projectId + ], + orderBy: 'timestamp desc', + a: 'b', + c: 'd' }); + + assert.deepEqual(config.gaxOpts, { + autoPaginate: undefined, + a: 'b', + c: 'd' + }); + + setImmediate(done); + + return REQUEST_STREAM; + }; + + var stream = logging.getEntriesStream(OPTIONS); + stream.emit('reading'); + }); + + it('should convert results from request to Entry', function(done) { + var stream = logging.getEntriesStream(OPTIONS); + + stream.on('data', function(entry) { + var argsPassedToFromApiResponse_ = entry[0]; + assert.strictEqual( + argsPassedToFromApiResponse_, + RESULT + ); + + done(); }); + + stream.emit('reading'); + }); + + it('should expose abort function', function(done) { + REQUEST_STREAM.abort = done; + + var stream = logging.getEntriesStream(OPTIONS); + + stream.emit('reading'); + + stream.abort(); }); }); describe('getSinks', function() { + var OPTIONS = { + a: 'b', + c: 'd', + gaxOptions: { + a: 'b', + c: 'd' + } + }; + it('should accept only a callback', function(done) { logging.request = function() { done(); @@ -475,97 +612,164 @@ describe('Logging', function() { }); it('should make the correct API request', function(done) { - logging.request = function(protoOpts, reqOpts) { - assert.strictEqual(protoOpts.service, 'ConfigServiceV2'); - assert.strictEqual(protoOpts.method, 'listSinks'); + logging.request = function(config) { + assert.strictEqual(config.client, 'configServiceV2Client'); + assert.strictEqual(config.method, 'listSinks'); - var expectedParent = 'projects/' + logging.projectId; - assert.strictEqual(reqOpts.parent, expectedParent); + assert.deepEqual(config.reqOpts, { + parent: 'projects/' + logging.projectId, + a: 'b', + c: 'd' + }); + + assert.deepEqual(config.gaxOpts, { + autoPaginate: undefined, + a: 'b', + c: 'd' + }); done(); }; - logging.getSinks({}, assert.ifError); + logging.getSinks(OPTIONS, assert.ifError); }); describe('error', function() { - var error = new Error('Error.'); - var apiResponse = {}; + var ARGS = [ + new Error('Error.'), + [], + {} + ]; beforeEach(function() { - logging.request = function(protoOpts, reqOpts, callback) { - callback(error, apiResponse); + logging.request = function(config, callback) { + callback.apply(null, ARGS); }; }); it('should execute callback with error & API response', function(done) { - logging.getSinks({}, function(err, sinks, nextQuery, apiResponse_) { - assert.strictEqual(err, error); - assert.strictEqual(sinks, null); - assert.strictEqual(nextQuery, null); - assert.strictEqual(apiResponse_, apiResponse); - + logging.getEntries(OPTIONS, function() { + var args = [].slice.call(arguments); + assert.deepStrictEqual(args, ARGS); done(); }); }); }); describe('success', function() { - var apiResponse = { - sinks: [ + var ARGS = [ + null, + [ { name: 'sink-name' } - ] - }; + ], + {} + ]; beforeEach(function() { - logging.request = function(protoOpts, reqOpts, callback) { - callback(null, apiResponse); + logging.request = function(config, callback) { + callback.apply(null, ARGS); }; }); - it('should build a nextQuery if necessary', function(done) { - var nextPageToken = 'next-page-token'; - var apiResponseWithNextPageToken = extend({}, apiResponse, { - nextPageToken: nextPageToken - }); - var expectedNextQuery = { - pageToken: nextPageToken - }; + it('should execute callback with Logs & API resp', function(done) { + var sinkInstance = {}; - logging.request = function(protoOpts, reqOpts, callback) { - callback(null, apiResponseWithNextPageToken); + logging.sink = function(name) { + assert.strictEqual(name, ARGS[1][0].name); + return sinkInstance; }; - logging.getSinks({}, function(err, sinks, nextQuery) { + logging.getSinks(OPTIONS, function(err, sinks) { assert.ifError(err); - assert.deepEqual(nextQuery, expectedNextQuery); + assert.strictEqual(sinks[0], sinkInstance); + assert.strictEqual(sinks[0].metadata, ARGS[1][0]); done(); }); }); + }); + }); - it('should execute callback with Logs & API resp', function(done) { - var log = {}; + describe('getSinksStream', function() { + var OPTIONS = { + a: 'b', + c: 'd', + gaxOptions: { + a: 'b', + c: 'd' + } + }; + + var REQUEST_STREAM; + var RESULT = { + name: 'sink-name' + }; - logging.sink = function(name) { - assert.strictEqual(name, apiResponse.sinks[0].name); - return log; - }; + beforeEach(function() { + REQUEST_STREAM = through.obj(); + REQUEST_STREAM.push(RESULT); - logging.getSinks({}, function(err, sinks, nextQuery, apiResponse_) { - assert.ifError(err); + logging.request = function() { + return REQUEST_STREAM; + }; + }); - assert.strictEqual(sinks[0], log); - assert.strictEqual(sinks[0].metadata, apiResponse.sinks[0]); + it('should make request once reading', function(done) { + logging.request = function(config) { + assert.strictEqual(config.client, 'configServiceV2Client'); + assert.strictEqual(config.method, 'listSinksStream'); - assert.strictEqual(apiResponse_, apiResponse); + assert.deepEqual(config.reqOpts, { + parent: 'projects/' + logging.projectId, + a: 'b', + c: 'd' + }); - done(); + assert.deepEqual(config.gaxOpts, { + autoPaginate: undefined, + a: 'b', + c: 'd' }); + + setImmediate(done); + + return REQUEST_STREAM; + }; + + var stream = logging.getSinksStream(OPTIONS); + stream.emit('reading'); + }); + + it('should convert results from request to Sink', function(done) { + var stream = logging.getSinksStream(OPTIONS); + + var sinkInstance = {}; + + logging.sink = function(name) { + assert.strictEqual(name, RESULT.name); + return sinkInstance; + }; + + stream.on('data', function(sink) { + assert.strictEqual(sink, sinkInstance); + assert.strictEqual(sink.metadata, RESULT); + done(); }); + + stream.emit('reading'); + }); + + it('should expose abort function', function(done) { + REQUEST_STREAM.abort = done; + + var stream = logging.getSinksStream(OPTIONS); + + stream.emit('reading'); + + stream.abort(); }); }); @@ -580,6 +784,225 @@ describe('Logging', function() { }); }); + describe('request', function() { + var CONFIG = { + client: 'client', + method: 'method', + reqOpts: { + a: 'b', + c: 'd' + }, + gaxOpts: {} + }; + + var PROJECT_ID = 'project-id'; + + beforeEach(function() { + logging.auth = { + getProjectId: function(callback) { + callback(null, PROJECT_ID); + } + }; + + logging.api[CONFIG.client] = { + [CONFIG.method]: util.noop + }; + }); + + describe('prepareGaxRequest', function() { + it('should get the project ID', function(done) { + logging.auth.getProjectId = function() { + done(); + }; + + logging.request(CONFIG, assert.ifError); + }); + + it('should return error if getting project ID failed', function(done) { + var error = new Error('Error.'); + + logging.auth.getProjectId = function(callback) { + callback(error); + }; + + logging.request(CONFIG, function(err) { + assert.strictEqual(err, error); + done(); + }); + }); + + it('should initiate and cache the client', function() { + var fakeClient = { + [CONFIG.method]: util.noop + }; + + v2Override = function(options) { + assert.strictEqual(options, logging.options); + + return { + [CONFIG.client]: function(options) { + assert.strictEqual(options, logging.options); + return fakeClient; + } + }; + }; + + logging.api = {}; + + logging.request(CONFIG, assert.ifError); + + assert.strictEqual(logging.api[CONFIG.client], fakeClient); + }); + + it('should use the cached client', function(done) { + v2Override = function() { + done(new Error('Should not re-instantiate a GAX client.')); + }; + + logging.request(CONFIG); + done(); + }); + + it('should replace the project ID token', function(done) { + var replacedReqOpts = {}; + + replaceProjectIdTokenOverride = function(reqOpts, projectId) { + assert.notStrictEqual(reqOpts, CONFIG.reqOpts); + assert.deepEqual(reqOpts, CONFIG.reqOpts); + assert.strictEqual(projectId, PROJECT_ID); + + return replacedReqOpts; + }; + + logging.api[CONFIG.client][CONFIG.method] = { + bind: function(gaxClient, reqOpts) { + assert.strictEqual(reqOpts, replacedReqOpts); + + setImmediate(done); + + return util.noop; + } + }; + + logging.request(CONFIG, assert.ifError); + }); + }); + + describe('makeRequestCallback', function() { + it('should prepare the request', function(done) { + logging.api[CONFIG.client][CONFIG.method] = { + bind: function(gaxClient, reqOpts, gaxOpts) { + assert.strictEqual(gaxClient, logging.api[CONFIG.client]); + assert.deepEqual(reqOpts, CONFIG.reqOpts); + assert.strictEqual(gaxOpts, CONFIG.gaxOpts); + + setImmediate(done); + + return util.noop; + } + }; + + logging.request(CONFIG, assert.ifError); + }); + + it('should execute callback with error', function(done) { + var error = new Error('Error.'); + + logging.api[CONFIG.client][CONFIG.method] = function() { + var callback = [].slice.call(arguments).pop(); + callback(error); + }; + + logging.request(CONFIG, function(err) { + assert.strictEqual(err, error); + done(); + }); + }); + + it('should execute the request function', function() { + logging.api[CONFIG.client][CONFIG.method] = function(done) { + var callback = [].slice.call(arguments).pop(); + callback(null, done); // so it ends the test + }; + + logging.request(CONFIG, assert.ifError); + }); + }); + + describe('makeRequestStream', function() { + var GAX_STREAM; + + beforeEach(function() { + GAX_STREAM = through(); + + logging.api[CONFIG.client][CONFIG.method] = { + bind: function() { + return function() { + return GAX_STREAM; + }; + } + }; + }); + + it('should expose an abort function', function(done) { + GAX_STREAM.cancel = done; + + var requestStream = logging.request(CONFIG); + requestStream.emit('reading'); + requestStream.abort(); + }); + + it('should prepare the request once reading', function(done) { + logging.api[CONFIG.client][CONFIG.method] = { + bind: function(gaxClient, reqOpts, gaxOpts) { + assert.strictEqual(gaxClient, logging.api[CONFIG.client]); + assert.deepEqual(reqOpts, CONFIG.reqOpts); + assert.strictEqual(gaxOpts, CONFIG.gaxOpts); + + setImmediate(done); + + return function() { + return GAX_STREAM; + }; + } + }; + + var requestStream = logging.request(CONFIG); + requestStream.emit('reading'); + }); + + it('should destroy the stream with prepare error', function(done) { + var error = new Error('Error.'); + + logging.auth.getProjectId = function(callback) { + callback(error); + }; + + var requestStream = logging.request(CONFIG); + requestStream.emit('reading'); + + requestStream.on('error', function(err) { + assert.strictEqual(err, error); + done(); + }); + }); + + it('should destroy the stream with GAX error', function(done) { + var error = new Error('Error.'); + + var requestStream = logging.request(CONFIG); + requestStream.emit('reading'); + + requestStream.on('error', function(err) { + assert.strictEqual(err, error); + done(); + }); + + GAX_STREAM.emit('error', error); + }); + }); + }); + describe('sink', function() { var NAME = 'sink-name'; diff --git a/packages/logging/test/log.js b/packages/logging/test/log.js index 3d7841777c2..36d2f73d6d2 100644 --- a/packages/logging/test/log.js +++ b/packages/logging/test/log.js @@ -18,7 +18,6 @@ var assert = require('assert'); var extend = require('extend'); -var GrpcServiceObject = require('@google-cloud/common').GrpcServiceObject; var prop = require('propprop'); var proxyquire = require('proxyquire'); var util = require('@google-cloud/common').util; @@ -41,11 +40,6 @@ function FakeMetadata() { this.calledWith_ = arguments; } -function FakeGrpcServiceObject() { - this.calledWith_ = arguments; - this.parent = {}; -} - describe('Log', function() { var Log; var log; @@ -60,11 +54,7 @@ describe('Log', function() { LOG_NAME_ENCODED ].join('/'); - var LOGGING = { - projectId: PROJECT_ID, - entry: util.noop, - request: util.noop - }; + var LOGGING; var assignSeverityToEntriesOverride = null; @@ -73,9 +63,6 @@ describe('Log', function() { '@google-cloud/common': { util: fakeUtil }, - '@google-cloud/common-grpc': { - ServiceObject: FakeGrpcServiceObject, - }, './entry.js': Entry, './metadata.js': FakeMetadata }); @@ -88,7 +75,13 @@ describe('Log', function() { beforeEach(function() { assignSeverityToEntriesOverride = null; - extend(FakeGrpcServiceObject, GrpcServiceObject); + + LOGGING = { + projectId: PROJECT_ID, + entry: util.noop, + request: util.noop + }; + log = new Log(LOGGING, LOG_NAME_FORMATTED); }); @@ -124,31 +117,19 @@ describe('Log', function() { assert.strictEqual(log.metadata_.calledWith_[0], LOGGING); }); - it('should inherit from GrpcServiceObject', function() { - assert(log instanceof FakeGrpcServiceObject); - - var calledWith = log.calledWith_[0]; - - assert.strictEqual(calledWith.parent, LOGGING); - assert.strictEqual(calledWith.id, LOG_NAME_ENCODED); - assert.deepEqual(calledWith.methods, { - delete: { - protoOpts: { - service: 'LoggingServiceV2', - method: 'deleteLog' - }, - reqOpts: { - logName: log.formattedName_ - } - } - }); - }); - it('should accept and localize options.removeCircular', function() { var options = { removeCircular: true }; var log = new Log(LOGGING, LOG_NAME_FORMATTED, options); assert.strictEqual(log.removeCircular_, true); }); + + it('should localize the Logging instance', function() { + assert.strictEqual(log.logging, LOGGING); + }); + + it('should localize the name', function() { + assert.strictEqual(log.name, LOG_NAME_FORMATTED.split('/').pop()); + }); }); describe('assignSeverityToEntries_', function() { @@ -219,6 +200,36 @@ describe('Log', function() { }); }); + describe('delete', function() { + it('should accept gaxOptions', function(done) { + log.logging.request = function(config, callback) { + assert.strictEqual(config.client, 'loggingServiceV2Client'); + assert.strictEqual(config.method, 'deleteLog'); + + assert.deepEqual(config.reqOpts, { + logName: log.formattedName_ + }); + + assert.deepEqual(config.gaxOpts, {}); + + callback(); // done() + }; + + log.delete(done); + }); + + it('should accept gaxOptions', function(done) { + var gaxOptions = {}; + + log.logging.request = function(config) { + assert.strictEqual(config.gaxOpts, gaxOptions); + done(); + }; + + log.delete(gaxOptions, assert.ifError); + }); + }); + describe('entry', function() { it('should return an entry from Logging', function() { var metadata = { @@ -228,7 +239,7 @@ describe('Log', function() { var entryObject = {}; - log.parent.entry = function(metadata_, data_) { + log.logging.entry = function(metadata_, data_) { assert.deepEqual(metadata_, extend({}, metadata, { logName: log.formattedName_ })); @@ -241,7 +252,7 @@ describe('Log', function() { }); it('should attach the log name to the entry', function(done) { - log.parent.entry = function(metadata) { + log.logging.entry = function(metadata) { assert.strictEqual(metadata.logName, log.formattedName_); done(); }; @@ -252,7 +263,7 @@ describe('Log', function() { it('should assume one argument means data', function(done) { var data = {}; - log.parent.entry = function(metadata, data_) { + log.logging.entry = function(metadata, data_) { assert.strictEqual(data_, data); done(); }; @@ -267,7 +278,7 @@ describe('Log', function() { }; it('should call Logging getEntries with defaults', function(done) { - log.parent.getEntries = function(options, callback) { + log.logging.getEntries = function(options, callback) { assert.deepEqual(options, EXPECTED_OPTIONS); callback(); // done() }; @@ -281,7 +292,7 @@ describe('Log', function() { filter: 'custom filter' }; - log.parent.getEntries = function(options_, callback) { + log.logging.getEntries = function(options_, callback) { assert.deepEqual(options_, extend({}, EXPECTED_OPTIONS, options)); callback(); // done() }; @@ -297,7 +308,7 @@ describe('Log', function() { }; it('should call Logging getEntriesStream with defaults', function(done) { - log.parent.getEntriesStream = function(options) { + log.logging.getEntriesStream = function(options) { assert.deepEqual(options, EXPECTED_OPTIONS); setImmediate(done); return fakeStream; @@ -313,7 +324,7 @@ describe('Log', function() { filter: 'custom filter' }; - log.parent.getEntriesStream = function(options_) { + log.logging.getEntriesStream = function(options_) { assert.deepEqual(options_, extend({}, EXPECTED_OPTIONS, options)); setImmediate(done); return fakeStream; @@ -337,17 +348,22 @@ describe('Log', function() { }); it('should make the correct API request', function(done) { - log.request = function(protoOpts, reqOpts) { - assert.strictEqual(protoOpts.service, 'LoggingServiceV2'); - assert.strictEqual(protoOpts.method, 'writeLogEntries'); + log.logging.request = function(config, callback) { + assert.strictEqual(config.client, 'loggingServiceV2Client'); + assert.strictEqual(config.method, 'writeLogEntries'); - assert.strictEqual(reqOpts.logName, log.formattedName_); - assert.strictEqual(reqOpts.entries[0], ENTRY); + assert.deepEqual(config.reqOpts, { + logName: log.formattedName_, + entries: [ENTRY], + resource: {} + }); - done(); + assert.strictEqual(config.gaxOpts, undefined); + + callback(); }; - log.write(ENTRY, OPTIONS, assert.ifError); + log.write(ENTRY, OPTIONS, done); }); it('should arrify & decorate the entries', function(done) { @@ -358,33 +374,16 @@ describe('Log', function() { callback(null, decoratedEntries); }; - log.request = function(protoOpts, reqOpts) { - assert.strictEqual(reqOpts.entries, decoratedEntries); + log.logging.request = function(config) { + assert.strictEqual(config.reqOpts.entries, decoratedEntries); done(); }; log.write(ENTRY, OPTIONS, assert.ifError); }); - it('should exec callback with only error and API response', function(done) { - var args = [1, 2, 3, 4]; - - log.request = function(protoOpts, reqOpts, callback) { - callback.apply(null, args); - }; - - log.write(ENTRY, OPTIONS, function() { - assert.strictEqual(arguments.length, 2); - - assert.strictEqual(arguments[0], args[0]); - assert.strictEqual(arguments[1], args[1]); - - done(); - }); - }); - it('should not require options', function(done) { - log.request = function(protoOpts, reqOpts, callback) { + log.logging.request = function(config, callback) { callback(); // done() }; diff --git a/packages/logging/test/metadata.js b/packages/logging/test/metadata.js index 1174e17782b..d357a68856b 100644 --- a/packages/logging/test/metadata.js +++ b/packages/logging/test/metadata.js @@ -35,7 +35,9 @@ describe('metadata', function() { }); beforeEach(function() { - LOGGING = {}; + LOGGING = { + auth: {} + }; extend(Metadata, MetadataCached); metadata = new Metadata(LOGGING); }); @@ -46,7 +48,7 @@ describe('metadata', function() { describe('instantiation', function() { it('should localize Logging instance', function() { - assert.strictEqual(metadata.logging_, LOGGING); + assert.strictEqual(metadata.logging, LOGGING); }); }); @@ -187,13 +189,13 @@ describe('metadata', function() { var RETURNED_PROJECT_ID = 'project-id'; beforeEach(function() { - metadata.getProjectId = function(callback) { + metadata.logging.auth.getProjectId = function(callback) { callback(null, RETURNED_PROJECT_ID); }; }); it('should get the project ID', function(done) { - metadata.getProjectId = function() { + metadata.logging.auth.getProjectId = function() { done(); }; @@ -203,7 +205,7 @@ describe('metadata', function() { it('should return error from getProjectId', function(done) { var error = new Error('Error.'); - metadata.getProjectId = function(callback) { + metadata.logging.auth.getProjectId = function(callback) { callback(error); }; @@ -214,10 +216,8 @@ describe('metadata', function() { }); it('should get the environment from auth client', function(done) { - metadata.logging_.authClient = { - getEnvironment: function() { - done(); - } + metadata.logging.auth.getEnvironment = function() { + done(); }; metadata.getDefaultResource(assert.ifError); @@ -233,12 +233,10 @@ describe('metadata', function() { return DESCRIPTOR; }; - metadata.logging_.authClient = { - getEnvironment: function(callback) { - callback(null, { - IS_APP_ENGINE: true - }); - } + metadata.logging.auth.getEnvironment = function(callback) { + callback(null, { + IS_APP_ENGINE: true + }); }; metadata.getDefaultResource(function(err, defaultResource) { @@ -258,12 +256,10 @@ describe('metadata', function() { return DESCRIPTOR; }; - metadata.logging_.authClient = { - getEnvironment: function(callback) { - callback(null, { - IS_CLOUD_FUNCTION: true - }); - } + metadata.logging.auth.getEnvironment = function(callback) { + callback(null, { + IS_CLOUD_FUNCTION: true + }); }; metadata.getDefaultResource(function(err, defaultResource) { @@ -283,12 +279,10 @@ describe('metadata', function() { return DESCRIPTOR; }; - metadata.logging_.authClient = { - getEnvironment: function(callback) { - callback(null, { - IS_COMPUTE_ENGINE: true - }); - } + metadata.logging.auth.getEnvironment = function(callback) { + callback(null, { + IS_COMPUTE_ENGINE: true + }); }; metadata.getDefaultResource(function(err, defaultResource) { @@ -308,12 +302,10 @@ describe('metadata', function() { return DESCRIPTOR; }; - metadata.logging_.authClient = { - getEnvironment: function(callback) { - callback(null, { - IS_CONTAINER_ENGINE: true - }); - } + metadata.logging.auth.getEnvironment = function(callback) { + callback(null, { + IS_CONTAINER_ENGINE: true + }); }; metadata.getDefaultResource(function(err, defaultResource) { @@ -333,15 +325,13 @@ describe('metadata', function() { return DESCRIPTOR; }; - metadata.logging_.authClient = { - getEnvironment: function(callback) { - callback(null, { - IS_APP_ENGINE: false, - IS_CLOUD_FUNCTION: false, - IS_COMPUTE_ENGINE: false, - IS_CONTAINER_ENGINE: false - }); - } + metadata.logging.auth.getEnvironment = function(callback) { + callback(null, { + IS_APP_ENGINE: false, + IS_CLOUD_FUNCTION: false, + IS_COMPUTE_ENGINE: false, + IS_CONTAINER_ENGINE: false + }); }; metadata.getDefaultResource(function(err, defaultResource) { @@ -353,34 +343,4 @@ describe('metadata', function() { }); }); }); - - describe('getProjectId', function() { - var CACHED_PROJECT_ID = 'cached-project-id'; - - it('should exit early if in the sandbox environment', function() { - global.GCLOUD_SANDBOX_ENV = true; - assert.strictEqual(metadata.getProjectId(), undefined); - global.GCLOUD_SANDBOX_ENV = false; - }); - - it('should return cached projectId from Logging instance', function(done) { - metadata.logging_.projectId = CACHED_PROJECT_ID; - - metadata.getProjectId(function(err, projectId) { - assert.ifError(err); - assert.strictEqual(projectId, CACHED_PROJECT_ID); - done(); - }); - }); - - it('should get project ID from auth client', function(done) { - metadata.logging_.authClient = { - getProjectId: function(callback) { - callback(); // done() - } - }; - - metadata.getProjectId(done); - }); - }); }); diff --git a/packages/logging/test/sink.js b/packages/logging/test/sink.js index b9f2305d830..8821192aee6 100644 --- a/packages/logging/test/sink.js +++ b/packages/logging/test/sink.js @@ -30,10 +30,6 @@ var fakeUtil = extend({}, util, { } }); -function FakeGrpcServiceObject() { - this.calledWith_ = arguments; -} - describe('Sink', function() { var Sink; var sink; @@ -48,9 +44,6 @@ describe('Sink', function() { Sink = proxyquire('../src/sink.js', { '@google-cloud/common': { util: fakeUtil - }, - '@google-cloud/common-grpc': { - ServiceObject: FakeGrpcServiceObject, } }); }); @@ -60,50 +53,14 @@ describe('Sink', function() { }); describe('instantiation', function() { - it('should inherit from GrpcServiceObject', function() { - var loggingInstance = extend({}, LOGGING, { - createSink: { - bind: function(context) { - assert.strictEqual(context, loggingInstance); - } - } - }); - - var sink = new Sink(loggingInstance, SINK_NAME); - assert(sink instanceof FakeGrpcServiceObject); - - var calledWith = sink.calledWith_[0]; - - assert.strictEqual(calledWith.parent, loggingInstance); - assert.strictEqual(calledWith.baseUrl, '/sinks'); - assert.strictEqual(calledWith.id, SINK_NAME); - assert.deepEqual(calledWith.methods, { - create: true, - delete: { - protoOpts: { - service: 'ConfigServiceV2', - method: 'deleteSink' - }, - reqOpts: { - sinkName: sink.formattedName_ - } - }, - getMetadata: { - protoOpts: { - service: 'ConfigServiceV2', - method: 'getSink' - }, - reqOpts: { - sinkName: sink.formattedName_ - } - } - }); - }); - it('should promisify all the things', function() { assert(promisifed); }); + it('should localize Logging instance', function() { + assert.strictEqual(sink.logging, LOGGING); + }); + it('should localize the name', function() { assert.strictEqual(sink.name, SINK_NAME); }); @@ -116,6 +73,106 @@ describe('Sink', function() { }); }); + describe('create', function() { + it('should call parent createSink', function(done) { + var config = {}; + + sink.logging.createSink = function(name, config_, callback) { + assert.strictEqual(name, sink.name); + assert.strictEqual(config_, config); + callback(); // done() + }; + + sink.create(config, done); + }); + }); + + describe('delete', function() { + it('should accept gaxOptions', function(done) { + sink.logging.request = function(config, callback) { + assert.strictEqual(config.client, 'configServiceV2Client'); + assert.strictEqual(config.method, 'deleteSink'); + + assert.deepEqual(config.reqOpts, { + sinkName: sink.formattedName_ + }); + + assert.deepEqual(config.gaxOpts, {}); + + callback(); // done() + }; + + sink.delete(done); + }); + + it('should accept gaxOptions', function(done) { + var gaxOptions = {}; + + sink.logging.request = function(config) { + assert.strictEqual(config.gaxOpts, gaxOptions); + done(); + }; + + sink.delete(gaxOptions, assert.ifError); + }); + }); + + describe('getMetadata', function() { + it('should make correct request', function(done) { + sink.logging.request = function(config) { + assert.strictEqual(config.client, 'configServiceV2Client'); + assert.strictEqual(config.method, 'getSink'); + + assert.deepEqual(config.reqOpts, { + sinkName: sink.formattedName_ + }); + + assert.deepEqual(config.gaxOpts, {}); + + done(); + }; + + sink.getMetadata(assert.ifError); + }); + + it('should accept gaxOptions', function(done) { + var gaxOptions = {}; + + sink.logging.request = function(config) { + assert.strictEqual(config.gaxOpts, gaxOptions); + done(); + }; + + sink.delete(gaxOptions, assert.ifError); + }); + + it('should update metadata', function(done) { + var metadata = {}; + + sink.logging.request = function(config, callback) { + callback(null, metadata); + }; + + sink.getMetadata(function() { + assert.strictEqual(sink.metadata, metadata); + done(); + }); + }); + + it('should execute callback with original arguments', function(done) { + var args = [{}, {}, {}]; + + sink.logging.request = function(config, callback) { + callback.apply(null, args); + }; + + sink.getMetadata(function() { + assert.deepStrictEqual([].slice.call(arguments), args); + done(); + }); + }); + }); + describe('setFilter', function() { var FILTER = 'filter'; @@ -132,6 +189,12 @@ describe('Sink', function() { describe('setMetadata', function() { var METADATA = { a: 'b', c: 'd' }; + beforeEach(function() { + sink.getMetadata = function(callback) { + callback(null, METADATA); + }; + }); + it('should refresh the metadata', function(done) { sink.getMetadata = function() { done(); @@ -162,14 +225,16 @@ describe('Sink', function() { callback(null, currentMetadata); }; - sink.request = function(protoOpts, reqOpts) { - assert.strictEqual(protoOpts.service, 'ConfigServiceV2'); - assert.strictEqual(protoOpts.method, 'updateSink'); + sink.logging.request = function(config) { + assert.strictEqual(config.client, 'configServiceV2Client'); + assert.strictEqual(config.method, 'updateSink'); - assert.strictEqual(reqOpts.sinkName, sink.formattedName_); + assert.deepEqual(config.reqOpts, { + sinkName: sink.formattedName_, + sink: extend({}, currentMetadata, METADATA) + }); - var expectedMetadata = extend({}, currentMetadata, METADATA); - assert.deepEqual(reqOpts.sink, expectedMetadata); + assert.strictEqual(config.gaxOpts, undefined); done(); }; @@ -177,50 +242,43 @@ describe('Sink', function() { sink.setMetadata(METADATA, assert.ifError); }); - describe('error', function() { - var error = new Error('Error.'); - var apiResponse = {}; - - beforeEach(function() { - sink.getMetadata = function(callback) { - callback(); - }; - - sink.request = function(protoOpts, reqOpts, callback) { - callback(error, apiResponse); - }; + it('should accept gaxOptions', function(done) { + var metadata = extend({}, METADATA, { + gaxOptions: {} }); - it('should execute callback with error & API response', function(done) { - sink.setMetadata(METADATA, function(err, apiResponse_) { - assert.strictEqual(err, error); - assert.strictEqual(apiResponse_, apiResponse); + sink.logging.request = function(config) { + assert.strictEqual(config.reqOpts.sink.gaxOptions, undefined); + assert.strictEqual(config.gaxOpts, metadata.gaxOptions); + done(); + }; - done(); - }); - }); + sink.setMetadata(metadata, assert.ifError); }); - describe('success', function() { - var apiResponse = {}; + it('should update metadata', function(done) { + var metadata = {}; - beforeEach(function() { - sink.getMetadata = function(callback) { - callback(); - }; + sink.logging.request = function(config, callback) { + callback(null, metadata); + }; - sink.request = function(protoOpts, reqOpts, callback) { - callback(null, apiResponse); - }; + sink.setMetadata(metadata, function() { + assert.strictEqual(sink.metadata, metadata); + done(); }); + }); - it('should execute callback with API resp', function(done) { - sink.setMetadata(METADATA, function(err, apiResponse_) { - assert.ifError(err); - assert.strictEqual(apiResponse_, apiResponse); + it('should execute callback with original arguments', function(done) { + var args = [{}, {}, {}]; - done(); - }); + sink.logging.request = function(config, callback) { + callback.apply(null, args); + }; + + sink.setMetadata(METADATA, function() { + assert.deepStrictEqual([].slice.call(arguments), args); + done(); }); }); });