From 3fa9ccf50824889e65d0add25ca059aaed7aae0e Mon Sep 17 00:00:00 2001 From: Jon Wayne Parrott Date: Wed, 25 Mar 2015 12:13:11 -0700 Subject: [PATCH 1/6] Adding autoCreate option to Topic constructor and reuseExisting option to subscribe. --- lib/pubsub/index.js | 10 ++++++++-- lib/pubsub/topic.js | 42 +++++++++++++++++++++++++++++++++++++++++- test/pubsub/topic.js | 32 ++++++++++++++++++++++++++++++++ 3 files changed, 81 insertions(+), 3 deletions(-) diff --git a/lib/pubsub/index.js b/lib/pubsub/index.js index c4b0ef160f3..23bf7edf0ba 100644 --- a/lib/pubsub/index.js +++ b/lib/pubsub/index.js @@ -196,18 +196,24 @@ PubSub.prototype.createTopic = function(name, callback) { * @throws {Error} If a name is not provided. * * @param {string} name - The name of the topic. + * @param {object=} options - Configuration object. + * @param {bool=} options.autoCreate - Automatically create topic if + * it doesn't exist. Note that messages published to a topic with + * no subscribers will not be delivered. * @return {module:pubsub/topic} * * @example * var topic = pubsub.topic('my-existing-topic'); * topic.publish('New message!'); */ -PubSub.prototype.topic = function(name) { +PubSub.prototype.topic = function(name, options) { if (!name) { throw new Error('A name must be specified for a new topic.'); } + options = options || {}; return new Topic(this, { - name: name + name: name, + autoCreate: options.autoCreate }); }; diff --git a/lib/pubsub/topic.js b/lib/pubsub/topic.js index 7c339e63f86..8f824e8f7e4 100644 --- a/lib/pubsub/topic.js +++ b/lib/pubsub/topic.js @@ -60,6 +60,12 @@ function Topic(pubsub, options) { this.name = Topic.formatName_(pubsub.projectId, options.name); this.projectId = pubsub.projectId; this.pubsub = pubsub; + + if (options.autoCreate) { + this.unformattedName = options.name; + this.origMakeReq_ = this.makeReq_; + this.makeReq_ = this.autoCreateWrapper_; + } } /** @@ -95,6 +101,37 @@ Topic.formatName_ = function(projectId, name) { return 'projects/' + projectId + '/topics/' + name; }; +/** + * Wrapper for makeReq_ that automatically attempts to create a topic if it + * does not yet exist. + * + * @private + */ +Topic.prototype.autoCreateWrapper_ = function(method, path, q, body, callback){ + var self = this; + + var createAndRetry = function(){ + self.pubsub.createTopic(self.unformattedName, function(err) { + if (err) { + callback(err); + return; + } + self.origMakeReq_(method, path, q, body, callback); + }); + }; + + this.origMakeReq_(method, path, q, body, function(err, res){ + if (method !== 'DELETE' && err && err.code === 404){ + createAndRetry(); + return; + } + else { + callback(err, res); + return; + } + }); +}; + /** * Publish the provided message or array of messages. A message can be of any * type. On success, an array of messageIds is returned in the response. @@ -276,7 +313,10 @@ Topic.prototype.subscribe = function(name, options, callback) { var path = Subscription.formatName_(this.projectId, name); this.makeReq_('PUT', path, null, body, function(err) { - if (err) { + if (options.reuseExisting && err && err.code === 409) { + callback(null, self.subscription(name, options)); + } + else if (err) { callback(err); return; } diff --git a/test/pubsub/topic.js b/test/pubsub/topic.js index f01044b133c..d5733035f9d 100644 --- a/test/pubsub/topic.js +++ b/test/pubsub/topic.js @@ -161,6 +161,38 @@ describe('Topic', function() { }); }); + describe('publish to non-existing topic', function(){ + var messageObject = { data: 'howdy' }; + + it('should execute callback with 404 error without autoCreate', function(done) { + topic.makeReq_ = function(method, path, query, body, callback) { + callback({code: 404}); + }; + + topic.publish(messageObject, function(err){ + assert.equal(err.code, 404); + done(); + }); + }); + + it('should publish successfully with autoCreate', function(done) { + var acTopic = new Topic(pubsubMock, { name: TOPIC_NAME, autoCreate: true }); + var created = false; + + acTopic.origMakeReq_ = function(method, path, query, body, callback) { + if (!created) callback({code: 404}); + else callback(null); + }; + + pubsubMock.createTopic = function(name, callback) { + created = true; + callback(); + } + + acTopic.publish(messageObject, done); + }); + }); + describe('delete', function() { it('should delete a topic', function(done) { topic.makeReq_ = function(method, path) { From 7603d700f13cfc380343585a320c726c6c5b5a2a Mon Sep 17 00:00:00 2001 From: Jon Wayne Parrott Date: Wed, 25 Mar 2015 12:19:21 -0700 Subject: [PATCH 2/6] Formatting fixes --- lib/pubsub/topic.js | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/lib/pubsub/topic.js b/lib/pubsub/topic.js index 8f824e8f7e4..b57de89b8ba 100644 --- a/lib/pubsub/topic.js +++ b/lib/pubsub/topic.js @@ -107,7 +107,7 @@ Topic.formatName_ = function(projectId, name) { * * @private */ -Topic.prototype.autoCreateWrapper_ = function(method, path, q, body, callback){ +Topic.prototype.autoCreateWrapper_ = function(method, path, q, body, callback) { var self = this; var createAndRetry = function(){ @@ -120,8 +120,8 @@ Topic.prototype.autoCreateWrapper_ = function(method, path, q, body, callback){ }); }; - this.origMakeReq_(method, path, q, body, function(err, res){ - if (method !== 'DELETE' && err && err.code === 404){ + this.origMakeReq_(method, path, q, body, function(err, res) { + if (method !== 'DELETE' && err && err.code === 404) { createAndRetry(); return; } From 5ccf7f7a0bee84105426a85ab950c726e3a80fb7 Mon Sep 17 00:00:00 2001 From: Jon Wayne Parrott Date: Wed, 25 Mar 2015 15:24:14 -0700 Subject: [PATCH 3/6] * Fixing lint issues. * Adding simple example of using autoCreate. * Putting string comparison last. --- lib/pubsub/index.js | 1 + lib/pubsub/topic.js | 2 +- test/pubsub/topic.js | 16 +++++++++++----- 3 files changed, 13 insertions(+), 6 deletions(-) diff --git a/lib/pubsub/index.js b/lib/pubsub/index.js index 23bf7edf0ba..4e9b12059fb 100644 --- a/lib/pubsub/index.js +++ b/lib/pubsub/index.js @@ -204,6 +204,7 @@ PubSub.prototype.createTopic = function(name, callback) { * * @example * var topic = pubsub.topic('my-existing-topic'); + * var topic = pubsub.topic('topic-that-maybe-exists', {autoCreate: true}); * topic.publish('New message!'); */ PubSub.prototype.topic = function(name, options) { diff --git a/lib/pubsub/topic.js b/lib/pubsub/topic.js index b57de89b8ba..ca18feac65b 100644 --- a/lib/pubsub/topic.js +++ b/lib/pubsub/topic.js @@ -121,7 +121,7 @@ Topic.prototype.autoCreateWrapper_ = function(method, path, q, body, callback) { }; this.origMakeReq_(method, path, q, body, function(err, res) { - if (method !== 'DELETE' && err && err.code === 404) { + if (err && err.code === 404 && method !== 'DELETE') { createAndRetry(); return; } diff --git a/test/pubsub/topic.js b/test/pubsub/topic.js index d5733035f9d..eeb4bd7ead1 100644 --- a/test/pubsub/topic.js +++ b/test/pubsub/topic.js @@ -164,7 +164,7 @@ describe('Topic', function() { describe('publish to non-existing topic', function(){ var messageObject = { data: 'howdy' }; - it('should execute callback with 404 error without autoCreate', function(done) { + it('should generate 404 error without autoCreate', function(done) { topic.makeReq_ = function(method, path, query, body, callback) { callback({code: 404}); }; @@ -176,18 +176,24 @@ describe('Topic', function() { }); it('should publish successfully with autoCreate', function(done) { - var acTopic = new Topic(pubsubMock, { name: TOPIC_NAME, autoCreate: true }); + var acTopic = new Topic(pubsubMock, { + name: TOPIC_NAME, autoCreate: true + }); var created = false; acTopic.origMakeReq_ = function(method, path, query, body, callback) { - if (!created) callback({code: 404}); - else callback(null); + if (!created) { + callback({code: 404}); + } + else { + callback(null); + } }; pubsubMock.createTopic = function(name, callback) { created = true; callback(); - } + }; acTopic.publish(messageObject, done); }); From 576f7a8786d4bd51a20144b3d751795f52a9f4b4 Mon Sep 17 00:00:00 2001 From: Jon Wayne Parrott Date: Thu, 26 Mar 2015 13:03:50 -0700 Subject: [PATCH 4/6] Documenting the reuseExisting option --- lib/pubsub/topic.js | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/lib/pubsub/topic.js b/lib/pubsub/topic.js index ca18feac65b..039a84fa10f 100644 --- a/lib/pubsub/topic.js +++ b/lib/pubsub/topic.js @@ -280,6 +280,10 @@ Topic.prototype.getSubscriptions = function(query, callback) { * once it's pulled. (default: false) * @param {number=} options.interval - Interval in milliseconds to check for new * messages. (default: 10) + * @param {boolean=} options.reuseExisting - If the subscription already exists, + * reuse it. The options of the existing subscription are not changed. If + * If false, attempting to create the same subscription twice will fail. + * (default: false) * @param {function} callback - The callback function. * * @example From 11f7286f3d411f9a97663fec117efea0e85ffe97 Mon Sep 17 00:00:00 2001 From: Jon Wayne Parrott Date: Thu, 26 Mar 2015 22:38:33 -0700 Subject: [PATCH 5/6] Adding test for reuseExisting and fixing duplicate callback error. --- lib/pubsub/topic.js | 1 + test/pubsub/topic.js | 16 ++++++++++++++++ 2 files changed, 17 insertions(+) diff --git a/lib/pubsub/topic.js b/lib/pubsub/topic.js index 039a84fa10f..708f9450dfd 100644 --- a/lib/pubsub/topic.js +++ b/lib/pubsub/topic.js @@ -319,6 +319,7 @@ Topic.prototype.subscribe = function(name, options, callback) { this.makeReq_('PUT', path, null, body, function(err) { if (options.reuseExisting && err && err.code === 409) { callback(null, self.subscription(name, options)); + return; } else if (err) { callback(err); diff --git a/test/pubsub/topic.js b/test/pubsub/topic.js index eeb4bd7ead1..0b413819ab8 100644 --- a/test/pubsub/topic.js +++ b/test/pubsub/topic.js @@ -314,6 +314,22 @@ describe('Topic', function() { }; topic.subscribe(SUB_NAME, CONFIG, assert.ifError); }); + + it('should re-use existing subscription if specified', function(done) { + topic.subscription = function() { + done(); + }; + + topic.makeReq_ = function(method, path, qs, body, callback){ + callback({code: 409}); + }; + + topic.subscribe(SUB_NAME, function(err) { + assert.equal(err.code, 409); + }); + + topic.subscribe(SUB_NAME, { reuseExisting: true }, assert.ifError); + }); }); describe('subscription', function() { From 1aaec176ed9cfba533a4550c263473a1b77129a3 Mon Sep 17 00:00:00 2001 From: Jon Wayne Parrott Date: Fri, 27 Mar 2015 10:23:38 -0700 Subject: [PATCH 6/6] Fixing some small styling --- test/pubsub/topic.js | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/test/pubsub/topic.js b/test/pubsub/topic.js index 0b413819ab8..1e82e7d0f97 100644 --- a/test/pubsub/topic.js +++ b/test/pubsub/topic.js @@ -166,7 +166,7 @@ describe('Topic', function() { it('should generate 404 error without autoCreate', function(done) { topic.makeReq_ = function(method, path, query, body, callback) { - callback({code: 404}); + callback({ code: 404 }); }; topic.publish(messageObject, function(err){ @@ -183,7 +183,7 @@ describe('Topic', function() { acTopic.origMakeReq_ = function(method, path, query, body, callback) { if (!created) { - callback({code: 404}); + callback({ code: 404 }); } else { callback(null); @@ -321,7 +321,7 @@ describe('Topic', function() { }; topic.makeReq_ = function(method, path, qs, body, callback){ - callback({code: 409}); + callback({ code: 409 }); }; topic.subscribe(SUB_NAME, function(err) {