Skip to content

Commit b9f417a

Browse files
committed
Make CloudEventAttributesProvider a FunctionalInterface
Related to spring-cloud#422 and spring-cloud#606
1 parent 1d67b80 commit b9f417a

File tree

7 files changed

+112
-93
lines changed

7 files changed

+112
-93
lines changed

spring-cloud-function-context/src/main/java/org/springframework/cloud/function/cloudevent/CloudEventAttributesProvider.java

Lines changed: 1 addition & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -19,48 +19,14 @@
1919
import java.util.Map;
2020

2121
import org.springframework.messaging.Message;
22-
import org.springframework.messaging.MessageHeaders;
2322

2423
/**
2524
*
2625
* @author Oleg Zhurakousky
2726
* @since 3.1
2827
*/
28+
@FunctionalInterface
2929
public interface CloudEventAttributesProvider {
30-
31-
/**
32-
* Will construct instance of {@link CloudEventAttributes} setting its required attributes.
33-
*
34-
* @param ce_id value for Cloud Event 'id' attribute
35-
* @param ce_specversion value for Cloud Event 'specversion' attribute
36-
* @param ce_source value for Cloud Event 'source' attribute
37-
* @param ce_type value for Cloud Event 'type' attribute
38-
* @return instance of {@link CloudEventAttributes}
39-
*/
40-
CloudEventAttributes get(String ce_id, String ce_specversion, String ce_source, String ce_type);
41-
42-
/**
43-
* Will construct instance of {@link CloudEventAttributes}
44-
* Should default/generate cloud event ID and SPECVERSION.
45-
*
46-
* @param ce_source value for Cloud Event 'source' attribute
47-
* @param ce_type value for Cloud Event 'type' attribute
48-
* @return instance of {@link CloudEventAttributes}
49-
*/
50-
CloudEventAttributes get(String ce_source, String ce_type);
51-
52-
53-
/**
54-
* Will construct instance of {@link CloudEventAttributes} from {@link MessageHeaders}.
55-
*
56-
* Should copy Cloud Event related headers into an instance of {@link CloudEventAttributes}
57-
* NOTE: Certain headers must not be copied.
58-
*
59-
* @param headers instance of {@link MessageHeaders}
60-
* @return modifiable instance of {@link CloudEventAttributes}
61-
*/
62-
RequiredAttributeAccessor get(MessageHeaders headers);
63-
6430
/**
6531
*
6632
* @param inputMessage input message used to invoke user functionality (e.g., function)

spring-cloud-function-context/src/main/java/org/springframework/cloud/function/cloudevent/CloudEventMessageUtils.java

Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,9 +16,13 @@
1616

1717
package org.springframework.cloud.function.cloudevent;
1818

19+
import java.util.HashMap;
1920
import java.util.Map;
21+
import java.util.UUID;
2022

2123
import org.springframework.messaging.Message;
24+
import org.springframework.messaging.MessageHeaders;
25+
import org.springframework.util.Assert;
2226
import org.springframework.util.MimeType;
2327
import org.springframework.util.MimeTypeUtils;
2428

@@ -155,4 +159,52 @@ public static boolean isBinary(Map<String, Object> headers) {
155159
&& headers.containsKey(CE_SPECVERSION)
156160
&& headers.containsKey(CE_TYPE));
157161
}
162+
163+
164+
/**
165+
* Will construct instance of {@link CloudEventAttributes} setting its required attributes.
166+
*
167+
* @param ce_id value for Cloud Event 'id' attribute
168+
* @param ce_specversion value for Cloud Event 'specversion' attribute
169+
* @param ce_source value for Cloud Event 'source' attribute
170+
* @param ce_type value for Cloud Event 'type' attribute
171+
* @return instance of {@link CloudEventAttributes}
172+
*/
173+
public static CloudEventAttributes get(String ce_id, String ce_specversion, String ce_source, String ce_type) {
174+
Assert.hasText(ce_id, "'ce_id' must not be null or empty");
175+
Assert.hasText(ce_specversion, "'ce_specversion' must not be null or empty");
176+
Assert.hasText(ce_source, "'ce_source' must not be null or empty");
177+
Assert.hasText(ce_type, "'ce_type' must not be null or empty");
178+
Map<String, Object> requiredAttributes = new HashMap<>();
179+
requiredAttributes.put(CloudEventMessageUtils.CE_ID, ce_id);
180+
requiredAttributes.put(CloudEventMessageUtils.CE_SPECVERSION, ce_specversion);
181+
requiredAttributes.put(CloudEventMessageUtils.CE_SOURCE, ce_source);
182+
requiredAttributes.put(CloudEventMessageUtils.CE_TYPE, ce_type);
183+
return new CloudEventAttributes(requiredAttributes);
184+
}
185+
186+
/**
187+
* Will construct instance of {@link CloudEventAttributes}
188+
* Should default/generate cloud event ID and SPECVERSION.
189+
*
190+
* @param ce_source value for Cloud Event 'source' attribute
191+
* @param ce_type value for Cloud Event 'type' attribute
192+
* @return instance of {@link CloudEventAttributes}
193+
*/
194+
public static CloudEventAttributes get(String ce_source, String ce_type) {
195+
return get(UUID.randomUUID().toString(), "1.0", ce_source, ce_type);
196+
}
197+
198+
/**
199+
* Will construct instance of {@link CloudEventAttributes} from {@link MessageHeaders}.
200+
*
201+
* Should copy Cloud Event related headers into an instance of {@link CloudEventAttributes}
202+
* NOTE: Certain headers must not be copied.
203+
*
204+
* @param headers instance of {@link MessageHeaders}
205+
* @return modifiable instance of {@link CloudEventAttributes}
206+
*/
207+
public static RequiredAttributeAccessor get(MessageHeaders headers) {
208+
return new RequiredAttributeAccessor(headers);
209+
}
158210
}

spring-cloud-function-context/src/main/java/org/springframework/cloud/function/cloudevent/DefaultCloudEventAttributesProvider.java

Lines changed: 2 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2019-2019 the original author or authors.
2+
* Copyright 2020-2020 the original author or authors.
33
*
44
* Licensed under the Apache License, Version 2.0 (the "License");
55
* you may not use this file except in compliance with the License.
@@ -17,7 +17,6 @@
1717
package org.springframework.cloud.function.cloudevent;
1818

1919
import java.util.Collections;
20-
import java.util.HashMap;
2120
import java.util.Map;
2221
import java.util.UUID;
2322

@@ -27,8 +26,6 @@
2726
import org.springframework.context.ConfigurableApplicationContext;
2827
import org.springframework.core.env.ConfigurableEnvironment;
2928
import org.springframework.messaging.Message;
30-
import org.springframework.messaging.MessageHeaders;
31-
import org.springframework.util.Assert;
3229
import org.springframework.util.StringUtils;
3330

3431
/**
@@ -41,38 +38,12 @@ public class DefaultCloudEventAttributesProvider implements CloudEventAttributes
4138

4239
private ConfigurableApplicationContext applicationContext;
4340

44-
@Override
45-
public CloudEventAttributes get(String ce_id, String ce_specversion, String ce_source, String ce_type) {
46-
Assert.hasText(ce_id, "'ce_id' must not be null or empty");
47-
Assert.hasText(ce_specversion, "'ce_specversion' must not be null or empty");
48-
Assert.hasText(ce_source, "'ce_source' must not be null or empty");
49-
Assert.hasText(ce_type, "'ce_type' must not be null or empty");
50-
Map<String, Object> requiredAttributes = new HashMap<>();
51-
requiredAttributes.put(CloudEventMessageUtils.CE_ID, ce_id);
52-
requiredAttributes.put(CloudEventMessageUtils.CE_SPECVERSION, ce_specversion);
53-
requiredAttributes.put(CloudEventMessageUtils.CE_SOURCE, ce_source);
54-
requiredAttributes.put(CloudEventMessageUtils.CE_TYPE, ce_type);
55-
return new CloudEventAttributes(requiredAttributes);
56-
}
57-
58-
@Override
59-
public CloudEventAttributes get(String ce_source, String ce_type) {
60-
return this.get(UUID.randomUUID().toString(), "1.0", ce_source, ce_type);
61-
}
62-
63-
/**
64-
* By default it will copy all the headers while exposing accessor to allow user to modify any of them.
65-
*/
66-
@Override
67-
public RequiredAttributeAccessor get(MessageHeaders headers) {
68-
return new RequiredAttributeAccessor(headers);
69-
}
7041

7142
@Override
7243
public Map<String, Object> generateDefaultCloudEventHeaders(Message<?> inputMessage, Object result) {
7344
if (inputMessage.getHeaders().containsKey(CloudEventMessageUtils.CE_ID)) { // input is a cloud event
7445
String applicationName = this.getApplicationName();
75-
return this.get(inputMessage.getHeaders())
46+
return CloudEventMessageUtils.get(inputMessage.getHeaders())
7647
.setId(UUID.randomUUID().toString())
7748
.setType(result.getClass().getName())
7849
.setSource(applicationName);

spring-cloud-function-context/src/test/java/org/springframework/cloud/function/cloudevent/CloudEventTypeConversionTests.java

Lines changed: 6 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -45,8 +45,7 @@ public class CloudEventTypeConversionTests {
4545
@Test
4646
public void testFromMessageBinaryPayloadMatchesType() {
4747
SmartCompositeMessageConverter messageConverter = this.configure(DummyConfiguration.class);
48-
CloudEventAttributesProvider ceAttrProvider = new DefaultCloudEventAttributesProvider();
49-
CloudEventAttributes ceAttributes = ceAttrProvider
48+
CloudEventAttributes ceAttributes = CloudEventMessageUtils
5049
.get(UUID.randomUUID().toString(), "1.0", "https://spring.io/", "org.springframework");
5150
ceAttributes.setDataContentType("text/plain");
5251
Message<String> message = MessageBuilder.withPayload("Hello Ricky").copyHeaders(ceAttributes).build();
@@ -58,8 +57,7 @@ public void testFromMessageBinaryPayloadMatchesType() {
5857
@Test
5958
public void testFromMessageBinaryPayloadDoesNotMatchType() {
6059
SmartCompositeMessageConverter messageConverter = this.configure(DummyConfiguration.class);
61-
CloudEventAttributesProvider ceAttrProvider = new DefaultCloudEventAttributesProvider();
62-
CloudEventAttributes ceAttributes = ceAttrProvider
60+
CloudEventAttributes ceAttributes = CloudEventMessageUtils
6361
.get(UUID.randomUUID().toString(), "1.0", "https://spring.io/", "org.springframework");
6462
Message<byte[]> message = MessageBuilder.withPayload("Hello Ricky".getBytes())
6563
.copyHeaders(ceAttributes)
@@ -70,12 +68,10 @@ public void testFromMessageBinaryPayloadDoesNotMatchType() {
7068
assertThat(converted).isEqualTo("Hello Ricky");
7169
}
7270

73-
@Test // JsonMessageConverter does some special things between byte[] and String so
74-
// this works
71+
@Test // JsonMessageConverter does some special things between byte[] and String so this works
7572
public void testFromMessageBinaryPayloadNoDataContentTypeToString() {
7673
SmartCompositeMessageConverter messageConverter = this.configure(DummyConfiguration.class);
77-
CloudEventAttributesProvider ceAttrProvider = new DefaultCloudEventAttributesProvider();
78-
CloudEventAttributes ceAttributes = ceAttrProvider
74+
CloudEventAttributes ceAttributes = CloudEventMessageUtils
7975
.get(UUID.randomUUID().toString(), "1.0", "https://spring.io/", "org.springframework");
8076
Message<byte[]> message = MessageBuilder.withPayload("Hello Ricky".getBytes())
8177
.copyHeaders(ceAttributes)
@@ -89,8 +85,7 @@ public void testFromMessageBinaryPayloadNoDataContentTypeToString() {
8985
@Test // Unlike the previous test the type here is POJO so no special treatement
9086
public void testFromMessageBinaryPayloadNoDataContentTypeToPOJO() {
9187
SmartCompositeMessageConverter messageConverter = this.configure(DummyConfiguration.class);
92-
CloudEventAttributesProvider ceAttrProvider = new DefaultCloudEventAttributesProvider();
93-
CloudEventAttributes ceAttributes = ceAttrProvider.get("https://spring.io/", "org.springframework");
88+
CloudEventAttributes ceAttributes = CloudEventMessageUtils.get("https://spring.io/", "org.springframework");
9489
Message<byte[]> message = MessageBuilder.withPayload("Hello Ricky".getBytes())
9590
.copyHeaders(ceAttributes)
9691
.setHeader(MessageHeaders.CONTENT_TYPE,
@@ -103,8 +98,7 @@ public void testFromMessageBinaryPayloadNoDataContentTypeToPOJO() {
10398
@Test // will fall on default CT which is json
10499
public void testFromMessageBinaryPayloadNoDataContentTypeToPOJOThatWorks() {
105100
SmartCompositeMessageConverter messageConverter = this.configure(DummyConfiguration.class);
106-
CloudEventAttributesProvider ceAttrProvider = new DefaultCloudEventAttributesProvider();
107-
CloudEventAttributes ceAttributes = ceAttrProvider.get("https://spring.io/", "org.springframework");
101+
CloudEventAttributes ceAttributes = CloudEventMessageUtils.get("https://spring.io/", "org.springframework");
108102
Message<byte[]> message = MessageBuilder.withPayload("{\"name\":\"Ricky\"}".getBytes())
109103
.copyHeaders(ceAttributes)
110104
.setHeader(MessageHeaders.CONTENT_TYPE,

spring-cloud-function-samples/function-sample-cloudevent/src/main/java/io/spring/cloudevent/CloudeventDemoApplication.java

Lines changed: 1 addition & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -16,20 +16,14 @@
1616

1717
package io.spring.cloudevent;
1818

19-
import java.text.ParseException;
20-
import java.text.SimpleDateFormat;
21-
import java.util.Collections;
2219
import java.util.Map;
23-
import java.util.UUID;
2420
import java.util.function.Function;
2521

2622
import org.springframework.boot.SpringApplication;
2723
import org.springframework.boot.autoconfigure.SpringBootApplication;
2824
import org.springframework.cloud.function.cloudevent.CloudEventAttributes;
2925
import org.springframework.cloud.function.cloudevent.CloudEventAttributesProvider;
3026
import org.springframework.cloud.function.cloudevent.CloudEventMessageUtils;
31-
import org.springframework.cloud.function.cloudevent.DefaultCloudEventAttributesProvider;
32-
import org.springframework.context.ConfigurableApplicationContext;
3327
import org.springframework.context.annotation.Bean;
3428
import org.springframework.messaging.Message;
3529
import org.springframework.messaging.support.MessageBuilder;
@@ -96,7 +90,7 @@ public Function<Message<SpringReleaseEvent>, Message<SpringReleaseEvent>> consum
9690
data.setVersion("2.0");
9791
data.setReleaseDateAsString("01-10-2006");
9892

99-
CloudEventAttributes ceAttributes = ceAttrProvider.get(ceMessage.getHeaders())
93+
CloudEventAttributes ceAttributes = CloudEventMessageUtils.get(ceMessage.getHeaders())
10094
.setSource("https://interface21.com/")
10195
.setType("com.interface21");
10296

spring-cloud-function-samples/function-sample-cloudevent/src/test/java/io/spring/cloudevent/CloudeventDemoApplicationFunctionTests.java

Lines changed: 4 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -20,8 +20,7 @@
2020

2121
import org.junit.jupiter.api.Test;
2222
import org.springframework.boot.SpringApplication;
23-
import org.springframework.cloud.function.cloudevent.CloudEventAttributesProvider;
24-
import org.springframework.cloud.function.cloudevent.DefaultCloudEventAttributesProvider;
23+
import org.springframework.cloud.function.cloudevent.CloudEventMessageUtils;
2524
import org.springframework.cloud.function.context.FunctionCatalog;
2625
import org.springframework.context.ConfigurableApplicationContext;
2726
import org.springframework.messaging.Message;
@@ -39,10 +38,9 @@ public void demoPureFunctionInvocation() {
3938

4039
try(ConfigurableApplicationContext context = SpringApplication.run(CloudeventDemoApplication.class)) {
4140
FunctionCatalog catalog = context.getBean(FunctionCatalog.class);
42-
CloudEventAttributesProvider ceAttrProvider = new DefaultCloudEventAttributesProvider();
4341
Message<String> binaryCloudEventMessage = MessageBuilder
4442
.withPayload("{\"releaseDate\":\"24-03-2004\", \"releaseName\":\"Spring Framework\", \"version\":\"1.0\"}")
45-
.copyHeaders(ceAttrProvider.get("spring.io/spring-event", "com.example.springevent"))
43+
.copyHeaders(CloudEventMessageUtils.get("spring.io/spring-event", "com.example.springevent"))
4644
.build();
4745

4846
/*
@@ -68,10 +66,9 @@ public void demoPureFunctionInvocation() {
6866
public void demoPureFunctionProduceConsumeCloudEvent() {
6967
try(ConfigurableApplicationContext context = SpringApplication.run(CloudeventDemoApplication.class)) {
7068
FunctionCatalog catalog = context.getBean(FunctionCatalog.class);
71-
CloudEventAttributesProvider ceAttrProvider = new DefaultCloudEventAttributesProvider();
7269
Message<String> binaryCloudEventMessage = MessageBuilder
7370
.withPayload("{\"releaseDate\":\"24-03-2004\", \"releaseName\":\"Spring Framework\", \"version\":\"1.0\"}")
74-
.copyHeaders(ceAttrProvider.get("spring.io/spring-event", "com.example.springevent"))
71+
.copyHeaders(CloudEventMessageUtils.get("spring.io/spring-event", "com.example.springevent"))
7572
.build();
7673

7774
/*
@@ -88,10 +85,9 @@ public void demoPureFunctionProduceConsumeCloudEvent() {
8885
public void demoPureFunctionProduceConsumeCloudEventAsPojo() {
8986
try(ConfigurableApplicationContext context = SpringApplication.run(CloudeventDemoApplication.class)) {
9087
FunctionCatalog catalog = context.getBean(FunctionCatalog.class);
91-
CloudEventAttributesProvider ceAttrProvider = new DefaultCloudEventAttributesProvider();
9288
Message<String> binaryCloudEventMessage = MessageBuilder
9389
.withPayload("{\"releaseDate\":\"24-03-2004\", \"releaseName\":\"Spring Framework\", \"version\":\"1.0\"}")
94-
.copyHeaders(ceAttrProvider.get("spring.io/spring-event", "com.example.springevent"))
90+
.copyHeaders(CloudEventMessageUtils.get("spring.io/spring-event", "com.example.springevent"))
9591
.build();
9692

9793
/*

spring-cloud-function-samples/function-sample-cloudevent/src/test/java/io/spring/cloudevent/CloudeventDemoApplicationRESTTests.java

Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,7 @@
3535
import org.springframework.cloud.function.cloudevent.CloudEventMessageUtils;
3636
import org.springframework.cloud.function.cloudevent.DefaultCloudEventAttributesProvider;
3737
import org.springframework.cloud.function.json.JsonMapper;
38+
import org.springframework.context.ApplicationContext;
3839
import org.springframework.context.annotation.Bean;
3940
import org.springframework.context.annotation.Configuration;
4041
import org.springframework.http.HttpHeaders;
@@ -241,6 +242,51 @@ public void testAsBinaryPojoToPojo() throws Exception {
241242
.isEqualTo(Collections.singletonList(SpringReleaseEvent.class.getName()));
242243
}
243244

245+
@Test
246+
public void testAsStructuralPojoToPojo() throws Exception {
247+
ApplicationContext context = SpringApplication.run(CloudeventDemoApplication.class);
248+
JsonMapper mapper = context.getBean(JsonMapper.class);
249+
250+
String payload = "{\n" +
251+
" \"specversion\" : \"1.0\",\n" +
252+
" \"type\" : \"org.springframework\",\n" +
253+
" \"source\" : \"https://spring.io/\",\n" +
254+
" \"id\" : \"A234-1234-1234\",\n" +
255+
// " \"ce-datacontenttype\" : \"application/json\",\n" +
256+
" \"data\" : {\n" +
257+
" \"version\" : \"1.0\",\n" +
258+
" \"releaseName\" : \"Spring Framework\",\n" +
259+
" \"releaseDate\" : \"24-03-2004\"\n" +
260+
" }\n" +
261+
"}";
262+
263+
System.out.println(payload);
264+
HttpHeaders headers = new HttpHeaders();
265+
headers.setContentType(MediaType.valueOf("application/cloudevents+json;charset=utf-8"));
266+
267+
RequestEntity<String> re = new RequestEntity<>(payload, headers, HttpMethod.POST, this.constructURI("/consumeAndProduceCloudEventAsPojoToPojo"));
268+
ResponseEntity<String> response = testRestTemplate.exchange(re, String.class);
269+
270+
SpringReleaseEvent springReleaseEvent = mapper.fromJson(response.getBody(), SpringReleaseEvent.class);
271+
272+
assertThat(springReleaseEvent.getReleaseName()).isEqualTo("Spring Framework");
273+
assertThat(springReleaseEvent.getVersion()).isEqualTo("2.0");
274+
275+
re = new RequestEntity<>(payload, headers, HttpMethod.POST, this.constructURI("/consumeAndProduceCloudEventAsMapToMap"));
276+
response = testRestTemplate.exchange(re, String.class);
277+
278+
springReleaseEvent = mapper.fromJson(response.getBody(), SpringReleaseEvent.class);
279+
280+
assertThat(springReleaseEvent.getReleaseName()).isEqualTo("Spring Framework");
281+
assertThat(springReleaseEvent.getVersion()).isEqualTo("10.0");
282+
283+
284+
// assertThat(response.getHeaders().get(CloudEventMessageUtils.CE_SOURCE))
285+
// .isEqualTo(Collections.singletonList("http://spring.io/application-application"));
286+
// assertThat(response.getHeaders().get(CloudEventMessageUtils.CE_TYPE))
287+
// .isEqualTo(Collections.singletonList(SpringReleaseEvent.class.getName()));
288+
}
289+
244290
private URI constructURI(String path) throws Exception {
245291
return new URI("http://localhost:" + System.getProperty("server.port") + path);
246292
}

0 commit comments

Comments
 (0)