Skip to content

Commit fecf108

Browse files
stephenpluspluscallmehiphop
authored andcommitted
pubsub: allow simple publishing (#1662)
1 parent fc7ebb8 commit fecf108

File tree

5 files changed

+161
-55
lines changed

5 files changed

+161
-55
lines changed

README.md

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -425,9 +425,7 @@ var pubsubClient = pubsub({
425425
var topic = pubsubClient.topic('my-topic');
426426

427427
// Publish a message to the topic.
428-
topic.publish({
429-
data: 'New message!'
430-
}, function(err) {});
428+
topic.publish('New message!', function(err) {});
431429

432430
// Subscribe to the topic.
433431
var options = {

packages/pubsub/README.md

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -20,9 +20,7 @@ var pubsub = require('@google-cloud/pubsub')({
2020
var topic = pubsub.topic('my-topic');
2121

2222
// Publish a message to the topic.
23-
topic.publish({
24-
data: 'New message!'
25-
}, function(err) {});
23+
topic.publish('New message!', function(err) {});
2624

2725
// Subscribe to the topic.
2826
var options = {

packages/pubsub/src/topic.js

Lines changed: 52 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -22,8 +22,8 @@
2222

2323
var arrify = require('arrify');
2424
var common = require('@google-cloud/common');
25+
var extend = require('extend');
2526
var is = require('is');
26-
var prop = require('propprop');
2727
var util = require('util');
2828

2929
/**
@@ -282,32 +282,27 @@ Topic.prototype.getSubscriptions = function(options, callback) {
282282
* @resource [Topics: publish API Documentation]{@link https://cloud.google.com/pubsub/reference/rest/v1/projects.topics/publish}
283283
*
284284
* @throws {Error} If no message is provided.
285-
* @throws {Error} If a message is missing a data property.
286285
*
287-
* @param {object|object[]} message - The message(s) to publish.
288-
* @param {*} message.data - The contents of the message.
289-
* @param {array=} message.attributes - Key/value pair of attributes to apply to
290-
* the message. All values must be strings.
286+
* @param {*|*[]} message - The message(s) to publish. If you need to
287+
* provide attributes for the message, you must enable `options.raw`, then
288+
* box your message in to an object with a `data` and `attributes` property.
289+
* `data` will be the raw message value you want to publish, and
290+
* `attributes` is a key/value pair of attributes to apply to the message.
291+
* @param {object=} options - Configuration object.
292+
* @param {boolean} options.raw - Enable if you require setting attributes on
293+
* your messages.
291294
* @param {function=} callback - The callback function.
292295
*
293296
* @example
294-
* topic.publish({
295-
* data: 'Hello, world!'
296-
* }, function(err, messageIds, apiResponse) {});
297+
* topic.publish('Hello, world!', function(err, messageIds, apiResponse) {});
297298
*
298299
* //-
299-
* // The data property can be a JSON object as well.
300+
* // You can also publish a JSON object.
300301
* //-
301302
* var registerMessage = {
302-
* data: {
303-
* userId: 3,
304-
* name: 'Stephen',
305-
* event: 'new user'
306-
* },
307-
* attributes: {
308-
* key: 'value',
309-
* hello: 'world'
310-
* }
303+
* userId: 3,
304+
* name: 'Stephen',
305+
* event: 'new user'
311306
* };
312307
*
313308
* topic.publish(registerMessage, function(err, messageIds, apiResponse) {});
@@ -327,28 +322,58 @@ Topic.prototype.getSubscriptions = function(options, callback) {
327322
* registerMessage,
328323
* purchaseMessage
329324
* ], function(err, messageIds, apiResponse) {});
325+
*
326+
* //-
327+
* // Set attributes with your message.
328+
* //-
329+
* var message = {
330+
* data: {
331+
* userId: 3,
332+
* product: 'book',
333+
* event: 'rent'
334+
* },
335+
* attributes: {
336+
* key: 'value',
337+
* hello: 'world'
338+
* }
339+
* };
340+
*
341+
* var options = {
342+
* raw: true
343+
* };
344+
*
345+
* topic.publish(message, options, function(err, messageIds, apiResponse) {});
330346
*/
331-
Topic.prototype.publish = function(messages, callback) {
347+
Topic.prototype.publish = function(messages, options, callback) {
332348
messages = arrify(messages);
333349

334-
if (messages.length === 0) {
335-
throw new Error('Cannot publish without a message.');
336-
}
337-
338-
if (!messages.every(prop('data'))) {
339-
throw new Error('Cannot publish message without a `data` property.');
350+
if (is.fn(options)) {
351+
callback = options;
352+
options = {};
340353
}
341354

355+
options = options || {};
342356
callback = callback || common.util.noop;
343357

358+
if (messages.length === 0) {
359+
throw new Error('Cannot publish without a message.');
360+
}
361+
344362
var protoOpts = {
345363
service: 'Publisher',
346364
method: 'publish',
347365
};
348366

349367
var reqOpts = {
350368
topic: this.name,
351-
messages: messages.map(Topic.formatMessage_)
369+
messages: messages
370+
.map(function(message) {
371+
if (is.object(message)) {
372+
message = extend(true, {}, message);
373+
}
374+
return options.raw ? message : { data: message };
375+
})
376+
.map(Topic.formatMessage_)
352377
};
353378

354379
this.request(protoOpts, reqOpts, function(err, result) {

packages/pubsub/system-test/pubsub.js

Lines changed: 68 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -24,14 +24,6 @@ var uuid = require('node-uuid');
2424
var env = require('../../../system-test/env.js');
2525
var pubsub = require('../')(env);
2626

27-
function generateSubName() {
28-
return 'test-subscription-' + uuid.v4();
29-
}
30-
31-
function generateTopicName() {
32-
return 'test-topic-' + uuid.v4();
33-
}
34-
3527
describe('pubsub', function() {
3628
var TOPIC_NAMES = [
3729
generateTopicName(),
@@ -51,6 +43,53 @@ describe('pubsub', function() {
5143
TOPICS[2].name
5244
];
5345

46+
function generateSubName() {
47+
return 'test-subscription-' + uuid.v4();
48+
}
49+
50+
function generateTopicName() {
51+
return 'test-topic-' + uuid.v4();
52+
}
53+
54+
function publishPop(message, options, callback) {
55+
if (!callback) {
56+
callback = options;
57+
options = {};
58+
}
59+
60+
options = options || {};
61+
62+
var topic = pubsub.topic(generateTopicName());
63+
var subscription = topic.subscription(generateSubName());
64+
65+
async.series([
66+
topic.create.bind(topic),
67+
subscription.create.bind(subscription),
68+
function(callback) {
69+
async.times(6, function(_, callback) {
70+
topic.publish(message, options, callback);
71+
}, callback);
72+
}
73+
], function(err) {
74+
if (err) {
75+
callback(err);
76+
return;
77+
}
78+
79+
subscription.pull({
80+
returnImmediately: true,
81+
maxResults: 1
82+
}, function(err, messages) {
83+
if (err) {
84+
callback(err);
85+
return;
86+
}
87+
88+
callback(null, messages.pop());
89+
});
90+
});
91+
}
92+
5493
before(function(done) {
5594
// create all needed topics
5695
async.each(TOPICS, function(topic, cb) {
@@ -120,9 +159,27 @@ describe('pubsub', function() {
120159

121160
it('should publish a message', function(done) {
122161
var topic = pubsub.topic(TOPIC_NAMES[0]);
123-
topic.publish({ data: 'message from me' }, function(err, messageIds) {
162+
topic.publish('message from me', function(err, messageIds) {
124163
assert.ifError(err);
125-
assert.equal(messageIds.length, 1);
164+
assert.strictEqual(messageIds.length, 1);
165+
done();
166+
});
167+
});
168+
169+
it('should publish a message with attributes', function(done) {
170+
var rawMessage = {
171+
data: 'raw message data',
172+
attributes: {
173+
customAttribute: 'value'
174+
}
175+
};
176+
177+
publishPop(rawMessage, { raw: true }, function(err, message) {
178+
assert.ifError(err);
179+
180+
assert.strictEqual(message.data, rawMessage.data);
181+
assert.deepEqual(message.attributes, rawMessage.attributes);
182+
126183
done();
127184
});
128185
});
@@ -166,7 +223,7 @@ describe('pubsub', function() {
166223
}
167224

168225
async.times(10, function(_, next) {
169-
topic.publish({ data: 'hello' }, next);
226+
topic.publish('hello', next);
170227
}, function(err) {
171228
if (err) {
172229
done(err);

packages/pubsub/test/topic.js

Lines changed: 39 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -180,7 +180,9 @@ describe('Topic', function() {
180180

181181
describe('publish', function() {
182182
var message = 'howdy';
183-
var messageObject = { data: message };
183+
var attributes = {
184+
key: 'value'
185+
};
184186

185187
it('should throw if no message is provided', function() {
186188
assert.throws(function() {
@@ -192,12 +194,6 @@ describe('Topic', function() {
192194
}, /Cannot publish without a message\./);
193195
});
194196

195-
it('should throw if a message has no data', function() {
196-
assert.throws(function() {
197-
topic.publish({});
198-
}, /Cannot publish message without a `data` property\./);
199-
});
200-
201197
it('should send correct api request', function(done) {
202198
topic.request = function(protoOpts, reqOpts) {
203199
assert.strictEqual(protoOpts.service, 'Publisher');
@@ -211,15 +207,47 @@ describe('Topic', function() {
211207
done();
212208
};
213209

214-
topic.publish(messageObject, assert.ifError);
210+
topic.publish(message, assert.ifError);
211+
});
212+
213+
it('should send correct api request for raw message', function(done) {
214+
topic.request = function(protoOpts, reqOpts) {
215+
assert.deepEqual(reqOpts.messages, [
216+
{
217+
data: new Buffer(JSON.stringify(message)).toString('base64'),
218+
attributes: attributes
219+
}
220+
]);
221+
222+
done();
223+
};
224+
225+
topic.publish({
226+
data: message,
227+
attributes: attributes
228+
}, { raw: true }, assert.ifError);
229+
});
230+
231+
it('should clone the provided message', function(done) {
232+
var message = {
233+
data: 'data'
234+
};
235+
var originalMessage = extend({}, message);
236+
237+
topic.request = function() {
238+
assert.deepEqual(message, originalMessage);
239+
done();
240+
};
241+
242+
topic.publish(message, { raw: true }, assert.ifError);
215243
});
216244

217245
it('should execute callback', function(done) {
218246
topic.request = function(protoOpts, reqOpts, callback) {
219247
callback(null, {});
220248
};
221249

222-
topic.publish(messageObject, done);
250+
topic.publish(message, done);
223251
});
224252

225253
it('should execute callback with error', function(done) {
@@ -230,7 +258,7 @@ describe('Topic', function() {
230258
callback(error, apiResponse);
231259
};
232260

233-
topic.publish(messageObject, function(err, ackIds, apiResponse_) {
261+
topic.publish(message, function(err, ackIds, apiResponse_) {
234262
assert.strictEqual(err, error);
235263
assert.strictEqual(ackIds, null);
236264
assert.strictEqual(apiResponse_, apiResponse);
@@ -246,7 +274,7 @@ describe('Topic', function() {
246274
callback(null, resp);
247275
};
248276

249-
topic.publish(messageObject, function(err, ackIds, apiResponse) {
277+
topic.publish(message, function(err, ackIds, apiResponse) {
250278
assert.deepEqual(resp, apiResponse);
251279
done();
252280
});

0 commit comments

Comments
 (0)