Skip to content

Commit 16e9b15

Browse files
Add Context.attributes(). (#24)
* Add `Context.attributes()`. The main purpose is to reflect attributes defined by the CloudEvents spec when the event source conforms to that spec.
1 parent 894c80e commit 16e9b15

File tree

8 files changed

+70
-18
lines changed

8 files changed

+70
-18
lines changed

functions-framework-api/pom.xml

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@
2424

2525
<groupId>com.google.cloud.functions</groupId>
2626
<artifactId>functions-framework-api</artifactId>
27-
<version>1.0.0-alpha-2-rc3-SNAPSHOT</version>
27+
<version>1.0.0-alpha-2-rc4-SNAPSHOT</version>
2828

2929
<properties>
3030
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
@@ -65,6 +65,7 @@
6565
<plugin>
6666
<groupId>org.apache.maven.plugins</groupId>
6767
<artifactId>maven-source-plugin</artifactId>
68+
<version>3.2.1</version>
6869
<executions>
6970
<execution>
7071
<id>attach-sources</id>
@@ -139,7 +140,7 @@
139140
<plugin>
140141
<groupId>org.apache.maven.plugins</groupId>
141142
<artifactId>maven-source-plugin</artifactId>
142-
<version>3.0.1</version>
143+
<version>3.2.1</version>
143144
<executions>
144145
<execution>
145146
<id>attach-sources</id>

functions-framework-api/src/main/java/com/google/cloud/functions/Context.java

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,9 @@
1414

1515
package com.google.cloud.functions;
1616

17+
import java.util.Collections;
18+
import java.util.Map;
19+
1720
/** An interface for event function context. */
1821
public interface Context {
1922
/** Returns event ID. */
@@ -27,4 +30,20 @@ public interface Context {
2730

2831
/** Returns event resource. */
2932
String resource();
33+
34+
/**
35+
* Returns additional attributes from this event. For CloudEvents, the entries in this map will
36+
* include the
37+
* <a href="https://github.com/cloudevents/spec/blob/v1.0/spec.md#required-attributes">required
38+
* attributes</a> and may include
39+
* <a href="https://github.com/cloudevents/spec/blob/v1.0/spec.md#required-attributes">optional
40+
* attributes</a> and
41+
* <a href="https://github.com/cloudevents/spec/blob/v1.0/spec.md#extension-context-attributes">
42+
* extension attributes</a>.
43+
*
44+
* <p>The map returned by this method may be empty but is never null.</p>
45+
*/
46+
default Map<String, String> attributes() {
47+
return Collections.emptyMap();
48+
}
3049
}

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

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,8 @@
2020
import com.google.gson.TypeAdapter;
2121
import java.lang.annotation.Retention;
2222
import java.lang.annotation.RetentionPolicy;
23+
import java.util.Collections;
24+
import java.util.Map;
2325

2426
/** Event context (metadata) for events handled by Cloud Functions. */
2527
@AutoValue
@@ -28,24 +30,32 @@ abstract class CloudFunctionsContext implements Context {
2830
@Retention(RetentionPolicy.SOURCE)
2931
@interface Nullable {}
3032

33+
@Override
3134
@Nullable
3235
public abstract String eventId();
3336

37+
@Override
3438
@Nullable
3539
public abstract String timestamp();
3640

41+
@Override
3742
@Nullable
3843
public abstract String eventType();
3944

45+
@Override
4046
@Nullable
4147
public abstract String resource();
4248

49+
@Override
50+
public abstract Map<String, String> attributes();
51+
4352
public static TypeAdapter<CloudFunctionsContext> typeAdapter(Gson gson) {
4453
return new AutoValue_CloudFunctionsContext.GsonTypeAdapter(gson);
4554
}
4655

4756
static Builder builder() {
48-
return new AutoValue_CloudFunctionsContext.Builder();
57+
return new AutoValue_CloudFunctionsContext.Builder()
58+
.setAttributes(Collections.emptyMap());
4959
}
5060

5161
@AutoValue.Builder
@@ -54,6 +64,8 @@ abstract static class Builder {
5464
abstract Builder setTimestamp(String x);
5565
abstract Builder setEventType(String x);
5666
abstract Builder setResource(String x);
67+
abstract Builder setAttributes(Map<String, String> value);
68+
5769
abstract CloudFunctionsContext build();
5870
}
5971
}

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

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -133,11 +133,13 @@ private static Context contextFromCloudEvent(CloudEvent<AttributesImpl, ?> cloud
133133
// We don't have an obvious replacement for the Context.resource field, which with legacy events
134134
// corresponded to a value present for some proprietary Google event types.
135135
String resource = "{}";
136+
Map<String, String> attributesMap = AttributesImpl.marshal(attributes);
136137
return CloudFunctionsContext.builder()
137138
.setEventId(attributes.getId())
138139
.setEventType(attributes.getType())
139140
.setResource(resource)
140141
.setTimestamp(timestampString)
142+
.setAttributes(attributesMap)
141143
.build();
142144
}
143145

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

Lines changed: 30 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -115,6 +115,18 @@ private static CloudEventImpl<Map<String, Object>> sampleCloudEvent(File snoopFi
115115
.build();
116116
}
117117

118+
private static JsonObject expectedCloudEventAttributes() {
119+
JsonObject attributes = new JsonObject();
120+
attributes.addProperty("datacontenttype", "application/json");
121+
attributes.addProperty("specversion", "1.0");
122+
attributes.addProperty("id", "B234-1234-1234");
123+
attributes.addProperty("source", "/source");
124+
attributes.addProperty("time", "2018-04-05T17:31:00Z");
125+
attributes.addProperty("type", "com.example.someevent.new");
126+
attributes.addProperty("dataschema", "/schema");
127+
return attributes;
128+
}
129+
118130
private static int serverPort;
119131

120132
/**
@@ -144,7 +156,7 @@ abstract static class TestCase {
144156

145157
abstract Optional<String> expectedResponseText();
146158

147-
abstract Optional<String> expectedJsonString();
159+
abstract Optional<JsonObject> expectedJson();
148160

149161
abstract Optional<String> expectedContentType();
150162

@@ -183,7 +195,7 @@ abstract static class Builder {
183195

184196
abstract Builder setExpectedOutput(String x);
185197

186-
abstract Builder setExpectedJsonString(String x);
198+
abstract Builder setExpectedJson(JsonObject x);
187199

188200
abstract Builder setHttpContentType(String x);
189201

@@ -319,7 +331,7 @@ public void background() throws Exception {
319331
TestCase testCase = TestCase.builder()
320332
.setRequestText(requestText)
321333
.setSnoopFile(snoopFile)
322-
.setExpectedJsonString(requestText)
334+
.setExpectedJson(new Gson().fromJson(requestText, JsonObject.class))
323335
.build();
324336
backgroundTest(fullTarget("BackgroundSnoop.snoop"), ImmutableList.of(testCase));
325337
}
@@ -337,24 +349,28 @@ public void newTypedBackground() throws Exception {
337349
private void newBackgroundTest(String target) throws Exception {
338350
File snoopFile = snoopFile();
339351
String gcfRequestText = sampleLegacyEvent(snoopFile);
352+
JsonObject expectedJson = new Gson().fromJson(gcfRequestText, JsonObject.class);
353+
// We don't currently put anything in the attributes() map for legacy events.
354+
expectedJson.add("attributes", new JsonObject());
340355
TestCase gcfTestCase = TestCase.builder()
341356
.setRequestText(gcfRequestText)
342357
.setSnoopFile(snoopFile)
343-
.setExpectedJsonString(gcfRequestText)
358+
.setExpectedJson(expectedJson)
344359
.build();
345360

346361
// A CloudEvent using the "structured content mode", where both the metadata and the payload
347362
// are in the body of the HTTP request.
348363
String cloudEventRequestText = Json.encode(sampleCloudEvent(snoopFile));
349364
// For CloudEvents, we don't currently populate Context#getResource with anything interesting,
350365
// so we excise that from the expected text we would have with legacy events.
351-
String cloudEventExpectedJsonString =
352-
gcfRequestText.replaceAll("\"resource\":\\s*\\{[^}]*\\}", "\"resource\":{}");
366+
JsonObject cloudEventExpectedJson = new Gson().fromJson(gcfRequestText, JsonObject.class);
367+
cloudEventExpectedJson.getAsJsonObject("context").add("resource", new JsonObject());
368+
cloudEventExpectedJson.add("attributes", expectedCloudEventAttributes());
353369
TestCase cloudEventsStructuredTestCase = TestCase.builder()
354370
.setSnoopFile(snoopFile)
355371
.setRequestText(cloudEventRequestText)
356372
.setHttpContentType("application/cloudevents+json; charset=utf-8")
357-
.setExpectedJsonString(cloudEventExpectedJsonString)
373+
.setExpectedJson(cloudEventExpectedJson)
358374
.build();
359375

360376
// A CloudEvent using the "binary content mode", where the metadata is in HTTP headers and the
@@ -367,7 +383,7 @@ private void newBackgroundTest(String target) throws Exception {
367383
.setRequestText(wire.getPayload().get())
368384
.setHttpContentType("application/json")
369385
.setHttpHeaders(ImmutableMap.copyOf(wire.getHeaders()))
370-
.setExpectedJsonString(cloudEventExpectedJsonString)
386+
.setExpectedJson(cloudEventExpectedJson)
371387
.build();
372388
// TODO(emcmanus): Update the Content-Type to "application/json; charset=utf-8" when
373389
// https://github.com/cloudevents/sdk-java/issues/89 has been fixed.
@@ -458,19 +474,19 @@ private void backgroundTest(String functionTarget, List<TestCase> testCases) thr
458474
String snooped = new String(Files.readAllBytes(snoopFile.toPath()), StandardCharsets.UTF_8);
459475
Gson gson = new Gson();
460476
JsonObject snoopedJson = gson.fromJson(snooped, JsonObject.class);
461-
String expectedJsonString = testCase.expectedJsonString().get();
462-
JsonObject expectedJson = gson.fromJson(expectedJsonString, JsonObject.class);
477+
JsonObject expectedJson = testCase.expectedJson().get();
463478
expect.withMessage("Testing %s with %s", functionTarget, testCase)
464479
.that(snoopedJson).isEqualTo(expectedJson);
465480
}
466481
}
467482

468-
private void checkSnoopFile(File snoopFile, String expectedJsonString) throws IOException {
483+
private void checkSnoopFile(TestCase testCase) throws IOException {
484+
File snoopFile = testCase.snoopFile().get();
485+
JsonObject expectedJson = testCase.expectedJson().get();
469486
String snooped = new String(Files.readAllBytes(snoopFile.toPath()), StandardCharsets.UTF_8);
470487
Gson gson = new Gson();
471488
JsonObject snoopedJson = gson.fromJson(snooped, JsonObject.class);
472-
JsonObject expectedJson = gson.fromJson(expectedJsonString, JsonObject.class);
473-
expect.that(snoopedJson).isEqualTo(expectedJson);
489+
expect.withMessage("Testing with %s", testCase).that(snoopedJson).isEqualTo(expectedJson);
474490
}
475491

476492
private void testHttpFunction(String target, List<TestCase> testCases) throws Exception {
@@ -518,7 +534,7 @@ private void testFunction(
518534
testCase.expectedContentType()
519535
.ifPresent(type -> expect.that(response.getMediaType()).isEqualTo(type));
520536
if (testCase.snoopFile().isPresent()) {
521-
checkSnoopFile(testCase.snoopFile().get(), testCase.expectedJsonString().get());
537+
checkSnoopFile(testCase);
522538
}
523539
testCase.expectedOutput()
524540
.ifPresent(output -> expect.that(serverProcess.output().toString()).contains(output));

invoker/core/src/test/java/com/google/cloud/functions/invoker/testfunctions/NewBackgroundSnoop.java

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,7 @@ public void accept(String json, Context context) {
3434
JsonObject contextAndPayloadJson = new JsonObject();
3535
contextAndPayloadJson.add("data", jsonObject);
3636
contextAndPayloadJson.add("context", contextJson);
37+
contextAndPayloadJson.add("attributes", gson.toJsonTree(context.attributes()));
3738
try (FileWriter fileWriter = new FileWriter(targetFile);
3839
PrintWriter writer = new PrintWriter(fileWriter)) {
3940
writer.println(contextAndPayloadJson);

invoker/core/src/test/java/com/google/cloud/functions/invoker/testfunctions/NewTypedBackgroundSnoop.java

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,7 @@ public void accept(Payload payload, Context context) {
4040
JsonObject contextAndPayloadJson = new JsonObject();
4141
contextAndPayloadJson.add("data", gson.toJsonTree(payload));
4242
contextAndPayloadJson.add("context", contextJson);
43+
contextAndPayloadJson.add("attributes", gson.toJsonTree(context.attributes()));
4344
try (FileWriter fileWriter = new FileWriter(targetFile);
4445
PrintWriter writer = new PrintWriter(fileWriter)) {
4546
writer.println(contextAndPayloadJson);

invoker/pom.xml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -42,7 +42,7 @@
4242
<dependency>
4343
<groupId>com.google.cloud.functions</groupId>
4444
<artifactId>functions-framework-api</artifactId>
45-
<version>1.0.0-alpha-2-rc3</version>
45+
<version>1.0.0-alpha-2-rc4-SNAPSHOT</version>
4646
</dependency>
4747
</dependencies>
4848
</dependencyManagement>

0 commit comments

Comments
 (0)