Skip to content

Commit 73ee908

Browse files
Bug fixes for cloud event conversion (#85)
This commit fixes a few bugs in the code that converts legacy events to cloud events: - preserve nulls when serializing JSON - firebaseauth and firebasedatabase event types - firebaseauth subject is derived form user ID in event data - fix code to extract firebasedatabase subject from resourceName
1 parent e53e3b2 commit 73ee908

File tree

4 files changed

+64
-24
lines changed

4 files changed

+64
-24
lines changed

.github/workflows/conformance.yaml

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,5 @@ jobs:
4747
with:
4848
functionType: 'cloudevent'
4949
useBuildpacks: false
50-
validateMapping: false
5150
cmd: "'mvn -f invoker/conformance/pom.xml function:run -Drun.functionTarget=com.google.cloud.functions.conformance.CloudEventsConformanceFunction'"
5251
startDelay: 10

invoker/conformance/pom.xml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,7 @@
4242
<plugin>
4343
<groupId>com.google.cloud.functions</groupId>
4444
<artifactId>function-maven-plugin</artifactId>
45+
<version>0.9.8-SNAPSHOT</version>
4546
</plugin>
4647
</plugins>
4748
</pluginManagement>

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

Lines changed: 58 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@
2121
import com.google.cloud.functions.invoker.CloudFunctionsContext.Nullable;
2222
import com.google.cloud.functions.invoker.CloudFunctionsContext.Resource;
2323
import com.google.gson.Gson;
24+
import com.google.gson.GsonBuilder;
2425
import com.google.gson.JsonObject;
2526
import io.cloudevents.CloudEvent;
2627
import io.cloudevents.core.builder.CloudEventBuilder;
@@ -39,6 +40,8 @@
3940
*/
4041
class GcfEvents {
4142
private static final String FIREBASE_SERVICE = "firebase.googleapis.com";
43+
private static final String FIREBASE_AUTH_SERVICE = "firebaseauth.googleapis.com";
44+
private static final String FIREBASE_DB_SERVICE = "firebasedatabase.googleapis.com";
4245
private static final String FIRESTORE_SERVICE = "firestore.googleapis.com";
4346
private static final String PUB_SUB_SERVICE = "pubsub.googleapis.com";
4447
private static final String STORAGE_SERVICE = "storage.googleapis.com";
@@ -71,25 +74,25 @@ class GcfEvents {
7174
FIRESTORE_SERVICE)),
7275

7376
entry("providers/firebase.auth/eventTypes/user.create",
74-
new FirestoreFirebaseEventAdapter("google.firebase.auth.user.v1.created", FIREBASE_SERVICE)),
77+
new FirebaseAuthEventAdapter("google.firebase.auth.user.v1.created")),
7578
entry("providers/firebase.auth/eventTypes/user.delete",
76-
new FirestoreFirebaseEventAdapter("google.firebase.auth.user.v1.deleted", FIREBASE_SERVICE)),
79+
new FirebaseAuthEventAdapter("google.firebase.auth.user.v1.deleted")),
7780

7881
entry("providers/google.firebase.analytics/eventTypes/event.log",
7982
new FirestoreFirebaseEventAdapter("google.firebase.analytics.log.v1.written", FIREBASE_SERVICE)),
8083

8184
entry("providers/google.firebase.database/eventTypes/ref.create",
8285
new FirestoreFirebaseEventAdapter("google.firebase.database.document.v1.created",
83-
FIREBASE_SERVICE)),
86+
FIREBASE_DB_SERVICE)),
8487
entry("providers/google.firebase.database/eventTypes/ref.write",
8588
new FirestoreFirebaseEventAdapter("google.firebase.database.document.v1.written",
86-
FIREBASE_SERVICE)),
89+
FIREBASE_DB_SERVICE)),
8790
entry("providers/google.firebase.database/eventTypes/ref.update",
8891
new FirestoreFirebaseEventAdapter("google.firebase.database.document.v1.updated",
89-
FIREBASE_SERVICE)),
92+
FIREBASE_DB_SERVICE)),
9093
entry("providers/google.firebase.database/eventTypes/ref.delete",
9194
new FirestoreFirebaseEventAdapter("google.firebase.database.document.v1.deleted",
92-
FIREBASE_SERVICE)),
95+
FIREBASE_DB_SERVICE)),
9396

9497
entry("providers/cloud.pubsub/eventTypes/topic.publish",
9598
new PubSubEventAdapter(PUB_SUB_MESSAGE_PUBLISHED)),
@@ -98,7 +101,7 @@ class GcfEvents {
98101
new StorageEventAdapter("google.cloud.storage.object.v1.changed"))
99102
);
100103

101-
private static final Gson GSON = new Gson();
104+
private static final Gson GSON = new GsonBuilder().serializeNulls().create();
102105

103106
static CloudEvent convertToCloudEvent(Event legacyEvent) {
104107
String eventType = legacyEvent.getContext().eventType();
@@ -135,7 +138,7 @@ final CloudEvent convertToCloudEvent(Event legacyEvent) {
135138
Resource resource = Resource.from(legacyEvent.getContext().resource());
136139
String service = Optional.ofNullable(resource.service()).orElse(defaultService);
137140
String resourceName = resource.name();
138-
SourceAndSubject sourceAndSubject = convertResourceToSourceAndSubject(resourceName);
141+
SourceAndSubject sourceAndSubject = convertResourceToSourceAndSubject(resourceName, legacyEvent);
139142
URI source = URI.create("//" + service + "/" + sourceAndSubject.source());
140143
OffsetDateTime timestamp =
141144
Optional.ofNullable(legacyEvent.getContext().timestamp())
@@ -156,7 +159,7 @@ String maybeReshapeData(Event legacyEvent, String jsonData) {
156159
return jsonData;
157160
}
158161

159-
SourceAndSubject convertResourceToSourceAndSubject(String resourceName) {
162+
SourceAndSubject convertResourceToSourceAndSubject(String resourceName, Event legacyEvent) {
160163
return SourceAndSubject.of(resourceName, null);
161164
}
162165
}
@@ -184,32 +187,34 @@ private static class StorageEventAdapter extends EventAdapter {
184187
}
185188

186189
@Override
187-
SourceAndSubject convertResourceToSourceAndSubject(String resourceName) {
190+
SourceAndSubject convertResourceToSourceAndSubject(String resourceName, Event legacyEvent) {
188191
Matcher matcher = STORAGE_RESOURCE_PATTERN.matcher(resourceName);
189192
if (matcher.matches()) {
190193
String resource = matcher.group(1);
191194
String subject = matcher.group(2);
192195
return SourceAndSubject.of(resource, subject);
193196
}
194-
return super.convertResourceToSourceAndSubject(resourceName);
197+
return super.convertResourceToSourceAndSubject(resourceName, legacyEvent);
195198
}
196199
}
197200

198201
private static class FirestoreFirebaseEventAdapter extends EventAdapter {
202+
private static final Pattern FIRESTORE_RESOURCE_PATTERN =
203+
Pattern.compile("^(projects/.+)/((documents|refs)/.+)$");
204+
199205
FirestoreFirebaseEventAdapter(String cloudEventType, String defaultService) {
200206
super(cloudEventType, defaultService);
201207
}
202208

203209
@Override
204-
SourceAndSubject convertResourceToSourceAndSubject(String resourceName) {
205-
List<String> resourceSegments = Arrays.asList(resourceName.split("/"));
206-
int documentsIndex = resourceSegments.indexOf("documents");
207-
if (documentsIndex < 0) {
208-
return super.convertResourceToSourceAndSubject(resourceName);
210+
SourceAndSubject convertResourceToSourceAndSubject(String resourceName, Event legacyEvent) {
211+
Matcher matcher = FIRESTORE_RESOURCE_PATTERN.matcher(resourceName);
212+
if (matcher.matches()) {
213+
String resource = matcher.group(1);
214+
String subject = matcher.group(2);
215+
return SourceAndSubject.of(resource, subject);
209216
}
210-
String sourcePath = String.join("/", resourceSegments.subList(0, documentsIndex));
211-
String subject = String.join("/", resourceSegments.subList(documentsIndex, resourceSegments.size()));
212-
return SourceAndSubject.of(sourcePath, subject);
217+
return super.convertResourceToSourceAndSubject(resourceName, legacyEvent);
213218
}
214219

215220
@Override
@@ -226,4 +231,38 @@ String maybeReshapeData(Event legacyEvent, String jsonData) {
226231
return GSON.toJson(jsonObject);
227232
}
228233
}
234+
235+
private static class FirebaseAuthEventAdapter extends EventAdapter {
236+
FirebaseAuthEventAdapter(String cloudEventType) {
237+
super(cloudEventType, FIREBASE_AUTH_SERVICE);
238+
}
239+
240+
@Override
241+
SourceAndSubject convertResourceToSourceAndSubject(String resourceName, Event legacyEvent) {
242+
String subject = null;
243+
JsonObject data = legacyEvent.getData().getAsJsonObject();
244+
if (data.has("uid")) {
245+
subject = "users/" + data.get("uid").getAsString();
246+
}
247+
return SourceAndSubject.of(resourceName, subject);
248+
}
249+
250+
@Override
251+
String maybeReshapeData(Event legacyEvent, String jsonData) {
252+
JsonObject jsonObject = GSON.fromJson(jsonData, JsonObject.class);
253+
if (!jsonObject.has("metadata")) {
254+
return jsonData;
255+
}
256+
JsonObject metadata = jsonObject.getAsJsonObject("metadata");
257+
if (metadata.has("createdAt")) {
258+
metadata.add("createTime", metadata.get("createdAt"));
259+
metadata.remove("createdAt");
260+
}
261+
if (metadata.has("lastSignedInAt")) {
262+
metadata.add("lastSignInTime", metadata.get("lastSignedInAt"));
263+
metadata.remove("lastSignedInAt");
264+
}
265+
return GSON.toJson(jsonObject);
266+
}
267+
}
229268
}

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

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@
2929
import java.time.OffsetDateTime;
3030
import java.time.ZoneOffset;
3131
import java.util.Base64;
32+
import java.util.Collections;
3233
import java.util.List;
3334
import java.util.Map;
3435
import org.junit.Ignore;
@@ -51,11 +52,11 @@ public class GcfEventsTest {
5152
{"legacy_pubsub.json", "google.cloud.pubsub.topic.v1.messagePublished",
5253
"//pubsub.googleapis.com/projects/sample-project/topics/gcf-test", null},
5354
{"firebase-db1.json", "google.firebase.database.document.v1.written",
54-
"//firebase.googleapis.com/projects/_/instances/my-project-id/refs/gcf-test/xyz", null},
55+
"//firebasedatabase.googleapis.com/projects/_/instances/my-project-id", "refs/gcf-test/xyz"},
5556
{"firebase-auth1.json", "google.firebase.auth.user.v1.created",
56-
"//firebase.googleapis.com/projects/my-project-id", null},
57+
"//firebaseauth.googleapis.com/projects/my-project-id", "users/UUpby3s4spZre6kHsgVSPetzQ8l2"},
5758
{"firebase-auth2.json", "google.firebase.auth.user.v1.deleted",
58-
"//firebase.googleapis.com/projects/my-project-id", null},
59+
"//firebaseauth.googleapis.com/projects/my-project-id", "users/UUpby3s4spZre6kHsgVSPetzQ8l2"},
5960
};
6061

6162
@Test
@@ -152,7 +153,7 @@ public void firestoreComplexData() throws IOException {
152153
"geoPointValue", Map.of("geoPointValue", Map.of("latitude", 51.4543, "longitude", -0.9781)),
153154
"intValue", Map.of("integerValue", "50"),
154155
"doubleValue", Map.of("doubleValue", 5.5),
155-
"nullValue", Map.of(),
156+
"nullValue", Collections.singletonMap("nullValue", null),
156157
"referenceValue", Map.of("referenceValue",
157158
"projects/project-id/databases/(default)/documents/foo/bar/baz/qux"),
158159
"stringValue", Map.of("stringValue", "text"),

0 commit comments

Comments
 (0)