Skip to content

Commit 7ef48bf

Browse files
committed
fix: marshal attribute-only Pub/Sub messages
1 parent f39924d commit 7ef48bf

3 files changed

Lines changed: 75 additions & 4 deletions

File tree

src/pubsub_middleware.ts

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -46,7 +46,7 @@ export interface RawPubSubBody {
4646
* Base64 encoded message data. If this field is empty, the message must contain at least one
4747
* attribute.
4848
*/
49-
data: string;
49+
data?: string;
5050
/**
5151
* ID of this message, assigned by the server when the message is published. Guaranteed to be
5252
* unique within the topic.
@@ -82,7 +82,7 @@ export interface MarshalledPubSubBody {
8282
};
8383
data: {
8484
'@type': typeof PUBSUB_MESSAGE_TYPE;
85-
data: string;
85+
data?: string;
8686
attributes: {[key: string]: string};
8787
};
8888
}
@@ -99,7 +99,9 @@ const isRawPubSubRequestBody = (body: any): body is RawPubSubBody => {
9999
!body.context &&
100100
body.subscription &&
101101
body.message &&
102-
body.message.data &&
102+
(body.message.data ||
103+
(body.message.attributes &&
104+
Object.keys(body.message.attributes).length > 0)) &&
103105
body.message.messageId
104106
);
105107
};
@@ -145,7 +147,7 @@ const marshalPubSubRequestBody = (
145147
},
146148
data: {
147149
'@type': PUBSUB_MESSAGE_TYPE,
148-
data: body.message.data,
150+
...(body.message.data !== undefined && {data: body.message.data}),
149151
attributes: body.message.attributes || {},
150152
},
151153
});

test/integration/cloud_event.ts

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -291,6 +291,38 @@ describe('CloudEvent Function', () => {
291291
});
292292
});
293293

294+
it('marshals attribute-only Pub/Sub emulator requests', async () => {
295+
const server = getTestServer('testCloudEventFunction');
296+
await supertest(server)
297+
.post('/projects/FOO/topics/BAR_TOPIC')
298+
.send({
299+
subscription: 'projects/FOO/subscriptions/BAR_SUB',
300+
message: {
301+
messageId: 'attribute-only-message',
302+
publishTime: '2026-07-22T00:00:00.000Z',
303+
attributes: {attribute1: 'value1'},
304+
},
305+
})
306+
.expect(204);
307+
308+
assert.deepStrictEqual(receivedCloudEvent, {
309+
specversion: '1.0',
310+
type: 'google.cloud.pubsub.topic.v1.messagePublished',
311+
source: '//pubsub.googleapis.com/projects/FOO/topics/BAR_TOPIC',
312+
id: 'attribute-only-message',
313+
time: '2026-07-22T00:00:00.000Z',
314+
datacontenttype: 'application/json',
315+
data: {
316+
message: {
317+
'@type': 'type.googleapis.com/google.pubsub.v1.PubsubMessage',
318+
attributes: {attribute1: 'value1'},
319+
messageId: 'attribute-only-message',
320+
publishTime: '2026-07-22T00:00:00.000Z',
321+
},
322+
},
323+
});
324+
});
325+
294326
it('allows customers to provide a type parameter for the data payload', async () => {
295327
const testPayload = 'a test string';
296328

test/integration/legacy_event.ts

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -211,6 +211,43 @@ describe('Event Function', () => {
211211
});
212212
});
213213

214+
it('marshals attribute-only Pub/Sub emulator requests', async () => {
215+
let receivedData: {} | null = null;
216+
let receivedContext: functions.CloudFunctionsContext | null = null;
217+
const server = getServer((data: {}, context: functions.Context) => {
218+
receivedData = data;
219+
receivedContext = context as functions.CloudFunctionsContext;
220+
}, testOptions);
221+
222+
await supertest(server)
223+
.post('/projects/FOO/topics/BAR_TOPIC')
224+
.send({
225+
subscription: 'projects/FOO/subscriptions/BAR_SUB',
226+
message: {
227+
messageId: 'attribute-only-message',
228+
publishTime: '2026-07-22T00:00:00.000Z',
229+
attributes: {attribute1: 'value1'},
230+
},
231+
})
232+
.set({'Content-Type': 'application/json'})
233+
.expect(204);
234+
235+
assert.deepStrictEqual(receivedData, {
236+
'@type': 'type.googleapis.com/google.pubsub.v1.PubsubMessage',
237+
attributes: {attribute1: 'value1'},
238+
});
239+
assert.deepStrictEqual(receivedContext, {
240+
eventId: 'attribute-only-message',
241+
timestamp: '2026-07-22T00:00:00.000Z',
242+
eventType: 'google.pubsub.topic.publish',
243+
resource: {
244+
service: 'pubsub.googleapis.com',
245+
type: 'type.googleapis.com/google.pubsub.v1.PubsubMessage',
246+
name: 'projects/FOO/topics/BAR_TOPIC',
247+
},
248+
});
249+
});
250+
214251
it('returns a 500 if the function throws an exception', async () => {
215252
const server = getServer(() => {
216253
throw 'I crashed';

0 commit comments

Comments
 (0)