Skip to content

Commit

Permalink
Merge pull request #8 from tenczar/master
Browse files Browse the repository at this point in the history
update sdk to support spec version 0.2
  • Loading branch information
matzew authored Dec 10, 2018
2 parents 6dd7d1e + 39838d3 commit 701b84c
Show file tree
Hide file tree
Showing 13 changed files with 2,213 additions and 131 deletions.
22 changes: 14 additions & 8 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,33 +2,39 @@

**NOTE: This SDK is still considered work in progress, things might (and will) break with every update.**

## New for v0.2
For this release extensions have been moved to top level properties. Previously extensions were defined in an extensions map, which was itself a top level property. All CloudEvent properties can be accessed using the generic Get method, or the type checked versions, e.g. GetString, GetMap, etc., but only the well known properties allow for direct field access. The marshallers handle packing and unpacking the extensions into an internal map.

This release also makes significant changes to the CloudEvent property names. All property names on the wire are now lower case with no separator characters. This ensures that these names are recognized across transports, which have different standards for property names. This release also removes the redundant 'event' prefix on property names. So EventType becomes Type and EventID become ID, etc. One special case is CloudEventsVersion, which becomes SpecVersion.

## Working with CloudEvents
Package cloudevents provides primitives to work with CloudEvents specification: https://github.com/cloudevents/spec.

Parsing Event from HTTP Request:
```go
import "github.com/cloudevents/sdk-go"
marshaller := v01.NewDefaultHTTPMarshaller()
marshaller := v02.NewDefaultHTTPMarshaller()
// req is *http.Request
event, err := marshaller.FromRequest(req)
if err != nil {
panic("Unable to parse event from http Request: " + err.String())
}
fmt.Printf("eventType: %s", event.Get("eventType")
fmt.Printf("type: %s", event.Get("type")
```

Creating a minimal CloudEvent in version 0.1:
Creating a minimal CloudEvent in version 0.2:
```go
import "github.com/cloudevents/sdk-go/v01"
event := v01.Event{
EventType: "com.example.file.created",
import "github.com/cloudevents/sdk-go/v02"
event := v02.Event{
Type: "com.example.file.created",
Source: "/providers/Example.COM/storage/account#fileServices/default/{new-file}",
EventID: "ea35b24ede421",
ID: "ea35b24ede421",
}
```

Creating HTTP request from CloudEvent:
```
marshaller := v01.NewDefaultHTTPMarshaller()
marshaller := v02.NewDefaultHTTPMarshaller()
var req *http.Request
err := event.ToRequest(req)
if err != nil {
Expand Down
24 changes: 24 additions & 0 deletions event.go
Original file line number Diff line number Diff line change
@@ -1,8 +1,14 @@
package cloudevents

import (
"net/url"
"time"
)

// Version01 holds a version string for CloudEvents specification version 0.1. See also EventV01 interface
// https://github.com/cloudevents/spec/blob/v0.1/spec.md
const Version01 = "0.1"
const Version02 = "0.2"

// Event interface is a generic abstraction over all possible versions and implementations of CloudEvents.
type Event interface {
Expand All @@ -11,6 +17,24 @@ type Event interface {
// Get takes a property name and, if it exists, returns the value of that property. The ok return value can
// be used to verify if the property exists.
Get(property string) (value interface{}, ok bool)
// GetInt is a convenience method that wraps Get to provide a type checked return value. Ok will be false
// if the property does not exist or the value cannot be converted to an int32.
GetInt(property string) (value int32, ok bool)
// GetString is a convenience method that wraps Get to provide a type checked return value. Ok will be false
// if the property does not exist or the value cannot be converted to a string.
GetString(property string) (value string, ok bool)
// GetBinary is a convenience method that wraps Get to provide a type checked return value. Ok will be false
// if the property does not exist or the value cannot be converted to a binary array.
GetBinary(property string) (value []byte, ok bool)
// GetMap is a convenience method that wraps Get to provide a type checked return value. Ok will be false
// if the property does not exist or the value cannot be converted to a map.
GetMap(propery string) (value map[string]interface{}, ok bool)
// GetTime is a convenience method that wraps Get to provide a type checked return value. Ok will be false
// if the property does not exist or the value cannot be converted or parsed into a time.Time.
GetTime(property string) (value *time.Time, ok bool)
// GetURL is a convenience method that wraps Get to provide a type checked return value. Ok will be false
// if the property does not exist or the value cannot be converted or parsed into a url.URL.
GetURL(property string) (value url.URL, ok bool)
// Set sets the property value
Set(property string, value interface{})
// Properties returns a map of all event properties as keys and their mandatory status as values
Expand Down
97 changes: 57 additions & 40 deletions v01/converter_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import (
"io/ioutil"
"net/http"
"net/http/httptest"
"net/url"
"reflect"
"testing"
"time"
Expand Down Expand Up @@ -100,9 +101,8 @@ func TestFromRequestConverterError(t *testing.T) {

func TestFromRequestSuccess(t *testing.T) {
expected := &v01.Event{
EventType: "com.example.someevent",
EventID: "00001",
EventTypeVersion: "0.1",
EventType: "com.example.someevent",
EventID: "00001",
}

jsonConverter := &mocks.HTTPCloudEventConverter{}
Expand Down Expand Up @@ -265,7 +265,7 @@ func TestJSONConverterReadError(t *testing.T) {
func TestJSONConverterReadSuccess(t *testing.T) {
principal := v01.NewJSONHTTPCloudEventConverter()

body := bytes.NewBufferString("{\"eventType\":\"com.example.someevent\"}")
body := bytes.NewBufferString("{\"eventtype\":\"com.example.someevent\"}")
req := httptest.NewRequest("GET", "localhost:8080", body)

actual, err := principal.Read(reflect.TypeOf(v01.Event{}), req)
Expand Down Expand Up @@ -300,11 +300,17 @@ func TestFromRequestJSONSuccess(t *testing.T) {
event := v01.Event{
CloudEventsVersion: "0.1",
EventType: "com.example.someevent",
EventTypeVersion: "1.1",
EventID: "1234-1234-1234",
Source: "/mycontext/subcontext",
SchemaURL: "http://example.com",
EventTime: &myDate,
Source: url.URL{
Scheme: "http",
Host: "example.com",
Path: "/mycontext/subcontext",
},
SchemaURL: url.URL{
Scheme: "http",
Host: "example.com",
},
EventTime: &myDate,
}

var buffer bytes.Buffer
Expand All @@ -319,11 +325,17 @@ func TestFromRequestJSONSuccess(t *testing.T) {
expected := &v01.Event{
CloudEventsVersion: "0.1",
EventType: "com.example.someevent",
EventTypeVersion: "1.1",
EventID: "1234-1234-1234",
Source: "/mycontext/subcontext",
SchemaURL: "http://example.com",
EventTime: &myDate,
Source: url.URL{
Scheme: "http",
Host: "example.com",
Path: "/mycontext/subcontext",
},
SchemaURL: url.URL{
Scheme: "http",
Host: "example.com",
},
EventTime: &myDate,
}

assert.EqualValues(t, expected, actual)
Expand All @@ -333,10 +345,11 @@ func TestHTTPMarshallerToRequestJSONSuccess(t *testing.T) {
factory := v01.NewDefaultHTTPMarshaller()

event := v01.Event{
EventType: "com.example.someevent",
EventTypeVersion: "1.1",
EventID: "1234-1234-1234",
Source: "/mycontext/subcontext",
EventType: "com.example.someevent",
EventID: "1234-1234-1234",
Source: url.URL{
Path: "/mycontext/subcontext",
},
}
event.Set("myint", 100)
event.Set("myfloat", 100e+3)
Expand All @@ -363,13 +376,13 @@ func TestHTTPMarshallerFromRequestBinarySuccess(t *testing.T) {
factory := v01.NewDefaultHTTPMarshaller()

header := http.Header{}
header.Set("Content-Type", "application/json")
header.Set("CE-EventType", "com.example.someevent")
header.Set("CE-Source", "/mycontext/subcontext")
header.Set("CE-EventID", "1234-1234-1234")
header.Set("CE-MyExtension", "myvalue")
header.Set("CE-AnotherExtension", "anothervalue")
header.Set("CE-EventTime", "2018-04-05T03:56:24Z")
header.Set("content-type", "application/json")
header.Set("ce-eventtype", "com.example.someevent")
header.Set("ce-source", "/mycontext/subcontext")
header.Set("ce-eventid", "1234-1234-1234")
header.Set("ce-myextension", "myvalue")
header.Set("ce-anotherextension", "anothervalue")
header.Set("ce-eventtime", "2018-04-05T03:56:24Z")

body := bytes.NewBufferString("{\"key1\":\"value1\", \"key2\":\"value2\"}")
req := httptest.NewRequest("GET", "localhost:8080", ioutil.NopCloser(body))
Expand All @@ -382,9 +395,11 @@ func TestHTTPMarshallerFromRequestBinarySuccess(t *testing.T) {
expected := &v01.Event{
ContentType: "application/json",
EventType: "com.example.someevent",
Source: "/mycontext/subcontext",
EventID: "1234-1234-1234",
EventTime: &timestamp,
Source: url.URL{
Path: "/mycontext/subcontext",
},
EventID: "1234-1234-1234",
EventTime: &timestamp,
Data: map[string]interface{}{
"key1": "value1",
"key2": "value2",
Expand All @@ -401,11 +416,14 @@ func TestHTTPMarshallerToRequestBinarySuccess(t *testing.T) {
factory := v01.NewDefaultHTTPMarshaller()

event := v01.Event{
EventType: "com.example.someevent",
EventTypeVersion: "1.1",
EventID: "1234-1234-1234",
Source: "/mycontext/subcontext",
ContentType: "application/json",
EventType: "com.example.someevent",
EventID: "1234-1234-1234",
Source: url.URL{
Scheme: "http",
Host: "example.com",
Path: "/mycontext/subcontext",
},
ContentType: "application/json",
Data: map[string]interface{}{
"key1": "value1",
"key2": "value2",
Expand All @@ -427,15 +445,14 @@ func TestHTTPMarshallerToRequestBinarySuccess(t *testing.T) {
"key2": "value2",
})
expected, _ := http.NewRequest("GET", "localhost:8080", &buffer)
expected.Header.Set("CE-EventID", "1234-1234-1234")
expected.Header.Set("CE-EventType", "com.example.someevent")
expected.Header.Set("CE-EventTypeVersion", "1.1")
expected.Header.Set("CE-Source", "/mycontext/subcontext")
expected.Header.Set("CE-Myfloat", "100000")
expected.Header.Set("CE-Myint", "100")
expected.Header.Set("CE-Mybool", "true")
expected.Header.Set("CE-Mystring", "string")
expected.Header.Set("Content-Type", "application/json")
expected.Header.Set("ce-eventid", "1234-1234-1234")
expected.Header.Set("ce-eventtype", "com.example.someevent")
expected.Header.Set("ce-source", "http://example.com/mycontext/subcontext")
expected.Header.Set("ce-myfloat", "100000")
expected.Header.Set("ce-myint", "100")
expected.Header.Set("ce-mybool", "true")
expected.Header.Set("ce-mystring", "string")
expected.Header.Set("content-type", "application/json")

// Can't test function equality
expected.GetBody = nil
Expand Down
Loading

0 comments on commit 701b84c

Please sign in to comment.