GH-750 Add support for pluggable protobufs
This initial support adds plugin extension to support CloudEvent proto as well as the example Additional plugins could be provided in the same ay as CloudEvent plugin extension Resolves #750
This commit is contained in:
@@ -0,0 +1,47 @@
|
||||
/*
|
||||
* Copyright 2021-2021 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.cloud.function.grpc.ce;
|
||||
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
|
||||
import org.springframework.cloud.function.grpc.MessageHandlingHelper;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
|
||||
import io.grpc.BindableService;
|
||||
|
||||
|
||||
/**
|
||||
*
|
||||
* @author Oleg Zhurakousky
|
||||
*
|
||||
*/
|
||||
@Configuration(proxyBeanMethods = false)
|
||||
@ConditionalOnProperty(name = "spring.cloud.function.grpc.server", havingValue = "true", matchIfMissing = true)
|
||||
public class CloudEventGrpcAutoConfiguration {
|
||||
|
||||
@Bean
|
||||
public BindableService cloudEventMessageHandler(MessageHandlingHelper helper) {
|
||||
return new CloudEventHandler(helper);
|
||||
}
|
||||
|
||||
@Bean
|
||||
@ConditionalOnMissingBean
|
||||
public CloudEventMessageConverter cloudEventMessageConverter() {
|
||||
return new CloudEventMessageConverter();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,88 @@
|
||||
/*
|
||||
* Copyright 2021-2021 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
/*
|
||||
* Copyright 2021-2021 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
/*
|
||||
* Copyright 2021-2021 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.cloud.function.grpc.ce;
|
||||
|
||||
import org.apache.commons.logging.Log;
|
||||
import org.apache.commons.logging.LogFactory;
|
||||
import org.springframework.cloud.function.grpc.MessageHandlingHelper;
|
||||
|
||||
import io.cloudevents.v1.CloudEventServiceGrpc.CloudEventServiceImplBase;
|
||||
import io.cloudevents.v1.proto.CloudEvent;
|
||||
import io.grpc.stub.StreamObserver;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author Oleg Zhurakousky
|
||||
* @since 3.2
|
||||
*
|
||||
*/
|
||||
@SuppressWarnings("rawtypes")
|
||||
class CloudEventHandler extends CloudEventServiceImplBase {
|
||||
|
||||
private Log logger = LogFactory.getLog(CloudEventHandler.class);
|
||||
|
||||
|
||||
|
||||
private final MessageHandlingHelper helper;
|
||||
|
||||
|
||||
|
||||
public CloudEventHandler(MessageHandlingHelper helper) {
|
||||
this.helper = helper;
|
||||
}
|
||||
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
@Override
|
||||
public void requestReply(CloudEvent request, StreamObserver<CloudEvent> responseObserver) {
|
||||
this.helper.requestReply(request, responseObserver);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,102 @@
|
||||
/*
|
||||
* Copyright 2021-2021 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.cloud.function.grpc.ce;
|
||||
|
||||
import java.util.Map.Entry;
|
||||
|
||||
import org.springframework.cloud.function.cloudevent.CloudEventMessageUtils;
|
||||
import org.springframework.cloud.function.grpc.AbstractGrpcMessageConverter;
|
||||
import org.springframework.messaging.Message;
|
||||
import org.springframework.messaging.support.MessageBuilder;
|
||||
|
||||
import com.google.protobuf.ByteString;
|
||||
import com.google.protobuf.GeneratedMessageV3;
|
||||
|
||||
import io.cloudevents.v1.proto.CloudEvent;
|
||||
import io.cloudevents.v1.proto.CloudEvent.Builder;
|
||||
import io.cloudevents.v1.proto.CloudEvent.CloudEventAttributeValue;
|
||||
import io.cloudevents.v1.proto.CloudEvent.CloudEventAttributeValue.AttrCase;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author Oleg Zhurakousky
|
||||
*
|
||||
*/
|
||||
public class CloudEventMessageConverter extends AbstractGrpcMessageConverter<CloudEvent> {
|
||||
|
||||
@SuppressWarnings({ "rawtypes", "unchecked" })
|
||||
@Override
|
||||
protected Message<byte[]> doToSpringMessage(CloudEvent cloudEvent) {
|
||||
MessageBuilder builder = MessageBuilder.withPayload(cloudEvent.getTextData());
|
||||
builder.setHeader(CloudEventMessageUtils.TYPE, cloudEvent.getType());
|
||||
builder.setHeader(CloudEventMessageUtils.SOURCE, cloudEvent.getSource());
|
||||
builder.setHeader(CloudEventMessageUtils.ID, cloudEvent.getId());
|
||||
builder.setHeader(CloudEventMessageUtils.SPECVERSION, cloudEvent.getId());
|
||||
|
||||
for (Entry<String, CloudEventAttributeValue> attributeEntry : cloudEvent.getAttributesMap().entrySet()) {
|
||||
AttrCase attrCase = attributeEntry.getValue().getAttrCase();
|
||||
if (attrCase.equals(AttrCase.CE_BOOLEAN)) {
|
||||
builder.setHeader(attributeEntry.getKey(), attributeEntry.getValue().getCeBoolean());
|
||||
}
|
||||
else if (attrCase.equals(AttrCase.CE_BYTES)) {
|
||||
builder.setHeader(attributeEntry.getKey(), attributeEntry.getValue().getCeBytes());
|
||||
}
|
||||
else if (attrCase.equals(AttrCase.CE_INTEGER)) {
|
||||
builder.setHeader(attributeEntry.getKey(), attributeEntry.getValue().getCeInteger());
|
||||
}
|
||||
else if (attrCase.equals(AttrCase.CE_STRING)) {
|
||||
builder.setHeader(attributeEntry.getKey(), attributeEntry.getValue().getCeString());
|
||||
}
|
||||
else if (attrCase.equals(AttrCase.CE_TIMESTAMP)) {
|
||||
builder.setHeader(attributeEntry.getKey(), attributeEntry.getValue().getCeTimestamp());
|
||||
}
|
||||
else if (attrCase.equals(AttrCase.CE_URI)) {
|
||||
builder.setHeader(attributeEntry.getKey(), attributeEntry.getValue().getCeUri());
|
||||
}
|
||||
else if (attrCase.equals(AttrCase.CE_URI_REF)) {
|
||||
builder.setHeader(attributeEntry.getKey(), attributeEntry.getValue().getCeUriRef());
|
||||
}
|
||||
else {
|
||||
throw new IllegalStateException("Unknown type for attribute " + attributeEntry.getKey());
|
||||
}
|
||||
|
||||
}
|
||||
return builder.build();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected CloudEvent doFromSpringMessage(Message<byte[]> springMessage) {
|
||||
Builder builder = CloudEvent.newBuilder()
|
||||
.setTextDataBytes(ByteString.copyFrom(springMessage.getPayload()))
|
||||
.setType(CloudEventMessageUtils.getType(springMessage))
|
||||
.setSource(CloudEventMessageUtils.getSource(springMessage).toString())
|
||||
.setId(CloudEventMessageUtils.getId(springMessage))
|
||||
.setSpecVersion(CloudEventMessageUtils.getSpecVersion(springMessage));
|
||||
|
||||
|
||||
for (Entry<String, Object> entry : springMessage.getHeaders().entrySet()) {
|
||||
builder.putAttributes(entry.getKey(), CloudEventAttributeValue.newBuilder().setCeString(entry.getValue().toString()).build());
|
||||
}
|
||||
return builder.build();
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean supports(Class<? extends GeneratedMessageV3> grpcClass) {
|
||||
return grpcClass.isAssignableFrom(CloudEvent.class);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,49 @@
|
||||
syntax = "proto3";
|
||||
|
||||
package io.cloudevents.v1;
|
||||
|
||||
import "google/protobuf/any.proto";
|
||||
import "google/protobuf/timestamp.proto";
|
||||
|
||||
option go_package = "cloudevents.io/genproto/v1";
|
||||
option java_package = "io.cloudevents.v1.proto";
|
||||
option java_multiple_files = true;
|
||||
|
||||
message CloudEvent {
|
||||
|
||||
// -- CloudEvent Context Attributes
|
||||
|
||||
// Required Attributes
|
||||
string id = 1;
|
||||
string source = 2; // URI-reference
|
||||
string spec_version = 3;
|
||||
string type = 4;
|
||||
|
||||
// Optional & Extension Attributes
|
||||
map<string, CloudEventAttributeValue> attributes = 5;
|
||||
|
||||
// -- CloudEvent Data (Bytes, Text, or Proto)
|
||||
oneof data {
|
||||
bytes binary_data = 6;
|
||||
string text_data = 7;
|
||||
google.protobuf.Any proto_data = 8;
|
||||
}
|
||||
|
||||
/**
|
||||
* The CloudEvent specification defines
|
||||
* seven attribute value types...
|
||||
*/
|
||||
|
||||
message CloudEventAttributeValue {
|
||||
|
||||
oneof attr {
|
||||
bool ce_boolean = 1;
|
||||
int32 ce_integer = 2;
|
||||
string ce_string = 3;
|
||||
bytes ce_bytes = 4;
|
||||
string ce_uri = 5;
|
||||
string ce_uri_ref = 6;
|
||||
google.protobuf.Timestamp ce_timestamp = 7;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,17 @@
|
||||
syntax = "proto3";
|
||||
|
||||
package io.cloudevents.v1;
|
||||
|
||||
import "google/protobuf/any.proto";
|
||||
import "google/protobuf/timestamp.proto";
|
||||
import "CloudEvent.proto";
|
||||
|
||||
service CloudEventService {
|
||||
rpc biStream(stream io.cloudevents.v1.CloudEvent) returns (stream io.cloudevents.v1.CloudEvent);
|
||||
|
||||
rpc clientStream(stream io.cloudevents.v1.CloudEvent) returns (io.cloudevents.v1.CloudEvent);
|
||||
|
||||
rpc serverStream(io.cloudevents.v1.CloudEvent) returns (stream io.cloudevents.v1.CloudEvent);
|
||||
|
||||
rpc requestReply(io.cloudevents.v1.CloudEvent) returns (io.cloudevents.v1.CloudEvent);
|
||||
}
|
||||
@@ -0,0 +1,2 @@
|
||||
org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
|
||||
org.springframework.cloud.function.grpc.ce.CloudEventGrpcAutoConfiguration
|
||||
@@ -0,0 +1 @@
|
||||
|
||||
@@ -0,0 +1,13 @@
|
||||
package org.springframework.cloud.grpc.ce;
|
||||
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.springframework.boot.test.context.SpringBootTest;
|
||||
|
||||
@SpringBootTest
|
||||
class SpringCloudFunctionGrpcCloudeventApplicationTests {
|
||||
|
||||
// @Test
|
||||
// void contextLoads() {
|
||||
// }
|
||||
|
||||
}
|
||||
Reference in New Issue
Block a user