Skip to content

Commit 176b4b7

Browse files
unblock local development with the pubsub emulator
Currently, the functions frameworks are dependent on some private dataplane event marshalling logic in order to correctly pass PubSub events to background functions. This commit implements the same marshalling logic in the FF in order to enable local development using the PubSub emulator. We have already made this change in other languages: GoogleCloudPlatform/functions-framework-nodejs#272 GoogleCloudPlatform/functions-framework-ruby#100 GoogleCloudPlatform/functions-framework-python#121 GoogleCloudPlatform/functions-framework-go#70
1 parent e96a3d9 commit 176b4b7

File tree

4 files changed

+106
-0
lines changed

4 files changed

+106
-0
lines changed

invoker/core/src/main/java/com/google/cloud/functions/invoker/Event.java

+35
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,8 @@
2121
import com.google.gson.JsonObject;
2222
import com.google.gson.JsonParseException;
2323
import java.lang.reflect.Type;
24+
import java.time.OffsetDateTime;
25+
import java.time.format.DateTimeFormatter;
2426

2527
/**
2628
* Represents an event that should be handled by a background function. This is an internal format
@@ -53,6 +55,31 @@ public Event deserialize(
5355
context =
5456
jsonDeserializationContext.deserialize(
5557
adjustContextResource(contextCopy), CloudFunctionsContext.class);
58+
} else if (isPubSubEmulatorPayload(root)) {
59+
JsonObject message = root.getAsJsonObject("message");
60+
61+
String timestampString = message.has("publishTime")
62+
? message.get("publishTime").getAsString()
63+
: DateTimeFormatter.ISO_INSTANT.format(OffsetDateTime.now());
64+
65+
context = CloudFunctionsContext.builder()
66+
.setEventType("google.pubsub.topic.publish")
67+
.setTimestamp(timestampString)
68+
.setEventId(message.get("messageId").getAsString())
69+
.setResource("{" +
70+
"\"name\":null," +
71+
"\"service\":\"pubsub.googleapis.com\"," +
72+
"\"type\":\"type.googleapis.com/google.pubsub.v1.PubsubMessage\""+
73+
"}")
74+
.build();
75+
76+
JsonObject marshalledData = new JsonObject();
77+
marshalledData.addProperty("@type", "type.googleapis.com/google.pubsub.v1.PubsubMessage");
78+
marshalledData.add("data", message.get("data"));
79+
if (message.has("attributes")) {
80+
marshalledData.add("attributes", message.get("attributes"));
81+
}
82+
data = marshalledData;
5683
} else {
5784
JsonObject rootCopy = root.deepCopy();
5885
rootCopy.remove("data");
@@ -63,6 +90,14 @@ public Event deserialize(
6390
return Event.of(data, context);
6491
}
6592

93+
private boolean isPubSubEmulatorPayload(JsonObject root) {
94+
if (root.has("subscription") && root.has("message") && root.get("message").isJsonObject()) {
95+
JsonObject message = root.getAsJsonObject("message");
96+
return message.has("data") && message.has("messageId");
97+
}
98+
return false;
99+
}
100+
66101
/**
67102
* Replaces 'resource' member from context JSON with its string equivalent. The original
68103
* 'resource' member can be a JSON object itself while {@link CloudFunctionsContext} requires it

invoker/core/src/test/java/com/google/cloud/functions/invoker/BackgroundFunctionExecutorTest.java

+42
Original file line numberDiff line numberDiff line change
@@ -2,14 +2,22 @@
22

33
import static com.google.cloud.functions.invoker.BackgroundFunctionExecutor.backgroundFunctionTypeArgument;
44
import static com.google.common.truth.Truth8.assertThat;
5+
import static org.junit.Assert.assertEquals;
6+
import static org.junit.Assert.assertNotNull;
57

68
import com.google.cloud.functions.BackgroundFunction;
79
import com.google.cloud.functions.Context;
10+
import com.google.gson.JsonObject;
11+
12+
import java.io.IOException;
13+
import java.io.InputStreamReader;
14+
import java.io.Reader;
815
import java.util.Map;
916
import org.junit.Test;
1017
import org.junit.runner.RunWith;
1118
import org.junit.runners.JUnit4;
1219

20+
1321
@RunWith(JUnit4.class)
1422
public class BackgroundFunctionExecutorTest {
1523
private static class PubSubMessage {
@@ -62,4 +70,38 @@ public void backgroundFunctionTypeArgument_raw() {
6270
(Class<? extends BackgroundFunction<?>>) (Class<?>) ForgotTypeParameter.class;
6371
assertThat(backgroundFunctionTypeArgument(c)).isEmpty();
6472
}
73+
74+
@Test
75+
public void parseLegacyEventPubSub() throws IOException {
76+
try (Reader reader = new InputStreamReader(getClass().getResourceAsStream("/pubsub_background.json"))) {
77+
Event event = BackgroundFunctionExecutor.parseLegacyEvent(reader);
78+
79+
Context context = event.getContext();
80+
assertEquals("google.pubsub.topic.publish", context.eventType());
81+
assertEquals("1", context.eventId());
82+
assertEquals("2021-06-28T05:46:32.390Z", context.timestamp());
83+
84+
JsonObject data = event.getData().getAsJsonObject();
85+
assertEquals(data.get("data").getAsString(), "eyJmb28iOiJiYXIifQ==");
86+
String attr = data.get("attributes").getAsJsonObject().get("test").getAsString();
87+
assertEquals(attr, "123");
88+
}
89+
}
90+
91+
@Test
92+
public void parseLegacyEventPubSubEmulator() throws IOException {
93+
try (Reader reader = new InputStreamReader(getClass().getResourceAsStream("/pubsub_emulator.json"))) {
94+
Event event = BackgroundFunctionExecutor.parseLegacyEvent(reader);
95+
96+
Context context = event.getContext();
97+
assertEquals("google.pubsub.topic.publish", context.eventType());
98+
assertEquals("1", context.eventId());
99+
assertNotNull(context.timestamp());
100+
101+
JsonObject data = event.getData().getAsJsonObject();
102+
assertEquals(data.get("data").getAsString(), "eyJmb28iOiJiYXIifQ==");
103+
String attr = data.get("attributes").getAsJsonObject().get("test").getAsString();
104+
assertEquals(attr, "123");
105+
}
106+
}
65107
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
{
2+
"data": {
3+
"@type": "type.googleapis.com/google.pubsub.v1.PubsubMessage",
4+
"data": "eyJmb28iOiJiYXIifQ==",
5+
"attributes": {
6+
"test": "123"
7+
}
8+
},
9+
"context": {
10+
"eventId": "1",
11+
"eventType": "google.pubsub.topic.publish",
12+
"resource": {
13+
"name": "projects/FOO/topics/BAR_TOPIC",
14+
"service": "pubsub.googleapis.com",
15+
"type": "type.googleapis.com/google.pubsub.v1.PubsubMessage"
16+
},
17+
"timestamp": "2021-06-28T05:46:32.390Z"
18+
}
19+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
{
2+
"subscription": "projects/FOO/subscriptions/BAR_SUB",
3+
"message": {
4+
"data": "eyJmb28iOiJiYXIifQ==",
5+
"messageId": "1",
6+
"attributes": {
7+
"test": "123"
8+
}
9+
}
10+
}

0 commit comments

Comments
 (0)