-
Notifications
You must be signed in to change notification settings - Fork 226
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #1 from kars7e/master
Initial import of SDK code
- Loading branch information
Showing
12 changed files
with
553 additions
and
2 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -10,3 +10,7 @@ | |
|
||
# Output of the go coverage tool, specifically when used with LiteIDE | ||
*.out | ||
|
||
# Others | ||
## IDE-specific | ||
*.iml |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,2 +1,45 @@ | ||
# cloudevents-go-sdk | ||
Go SDK for CloudEvents (https://github.com/cloudevents/spec) | ||
# Go SDK for [CloudEvents](https://github.com/cloudevents/spec) | ||
|
||
**NOTE: This SDK is still considered work in progress, things might (and will) break with every update.** | ||
|
||
Package cloudevents provides primitives to work with CloudEvents specification: https://github.com/cloudevents/spec. | ||
|
||
Parsing Event from HTTP Request: | ||
```go | ||
// req is *http.Request | ||
event, err := cloudEvents.FromHTTPRequest(req) | ||
if err != nil { | ||
panic("Unable to parse event from http Request: " + err.String()) | ||
} | ||
fmt.Printf("eventType: %s", event.EventType) | ||
``` | ||
|
||
Creating a minimal CloudEvent in version 0.1: | ||
```go | ||
import "github.com/dispatchframework/cloudevents-go-sdk/v01" | ||
event := v01.Event{ | ||
EventType: "com.example.file.created", | ||
Source: "/providers/Example.COM/storage/account#fileServices/default/{new-file}", | ||
EventID: "ea35b24ede421", | ||
} | ||
``` | ||
|
||
The goal of this package is to provide support for all released versions of CloudEvents, ideally while maintaining | ||
the same API. It will use semantic versioning with following rules: | ||
* MAJOR version increments when backwards incompatible changes is introduced. | ||
* MINOR version increments when backwards compatible feature is introduced INCLUDING support for new CloudEvents version. | ||
* PATCH version increments when a backwards compatible bug fix is introduced. | ||
|
||
|
||
## TODO list | ||
|
||
- [ ] Add encoders registry, where SDK user can register their custom content-type encoders/decoders | ||
- [ ] Add more tests for edge cases | ||
|
||
## Existing Go for CloudEvents | ||
|
||
Existing projects that added support for CloudEvents in Go are listed below. It's our goal to identify existing patterns | ||
of using CloudEvents in Go-based project and design the SDK to support these patterns (where it makes sense). | ||
- https://github.com/google/cloudevents-demo/tree/master/pkg/event | ||
- https://github.com/vmware/dispatch/blob/master/pkg/events/cloudevent.go | ||
- https://github.com/serverless/event-gateway/tree/master/event |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,28 @@ | ||
/* | ||
Package cloudevents provides primitives to work with CloudEvents specification: https://github.com/cloudevents/spec. | ||
Parsing Event from HTTP Request: | ||
// req is *http.Request | ||
event, err := cloudEvents.FromHTTPRequest(req) | ||
if err != nil { | ||
panic("Unable to parse event from http Request: " + err.String()) | ||
} | ||
Creating a minimal CloudEvent in version 0.1: | ||
import "github.com/dispatchframework/cloudevents-go-sdk/v01" | ||
event := v01.Event{ | ||
EventType: "com.example.file.created", | ||
Source: "/providers/Example.COM/storage/account#fileServices/default/{new-file}", | ||
EventID: "ea35b24ede421", | ||
} | ||
The goal of this package is to provide support for all released versions of CloudEvents, ideally while maintaining | ||
the same API. It will use semantic versioning with following rules: | ||
* MAJOR version increments when backwards incompatible changes is introduced. | ||
* MINOR version increments when backwards compatible feature is introduced INCLUDING support for new CloudEvents version. | ||
* PATCH version increments when a backwards compatible bug fix is introduced. | ||
*/ | ||
package cloudevents |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,30 @@ | ||
package cloudevents | ||
|
||
// RequiredPropertyError is return when a property of an event that is required by specification is not set | ||
type RequiredPropertyError string | ||
|
||
func (e RequiredPropertyError) Error() string { | ||
return "missing required property " + string(e) | ||
} | ||
|
||
// VersionMismatchError is returned when expected CloudEvent version does not match the actual one, e.g. | ||
// when using GetEventV01 or when using transport bindings. | ||
type VersionMismatchError string | ||
|
||
func (e VersionMismatchError) Error() string { | ||
return "provided event is not CloudEvent or does not implement expected version: " + string(e) | ||
} | ||
|
||
// VersionNotSupportedError is returned when provided version is not supported by this library. | ||
type VersionNotSupportedError string | ||
|
||
func (e VersionNotSupportedError) Error() string { | ||
return "provided version " + string(e) + " is not supported" | ||
} | ||
|
||
// ContentTypeNotSupportedError is returned when povided event's content type is not supported by this library. | ||
type ContentTypeNotSupportedError string | ||
|
||
func (e ContentTypeNotSupportedError) Error() string { | ||
return "provided content type " + string(e) + " is not supported" | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,18 @@ | ||
package cloudevents | ||
|
||
// 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" | ||
|
||
// Event interface is a generic abstraction over all possible versions and implementations of CloudEvents. | ||
type Event interface { | ||
// CloudEventVersion returns the version of Event specification followed by the underlying implementation. | ||
CloudEventVersion() string | ||
// 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) | ||
// 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 | ||
Properties() map[string]bool | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,16 @@ | ||
package cloudevents | ||
|
||
import ( | ||
"net/http" | ||
|
||
"github.com/dispatchframework/cloudevents-go-sdk/v01" | ||
) | ||
|
||
// FromHTTPRequest parses a CloudEvent from any known encoding. | ||
func FromHTTPRequest(req *http.Request) (Event, error) { | ||
// TODO: this should check the version of incoming CloudVersion header and create an appropriate event structure. | ||
e := &v01.Event{} | ||
err := e.FromHTTPRequest(req) | ||
return e, err | ||
|
||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,8 @@ | ||
package webhooks | ||
|
||
import "github.com/dispatchframework/cloudevents-go-sdk" | ||
|
||
// Deliver delivers the event to the endpoint | ||
func Deliver(event cloudevents.Event) (string, error) { | ||
|
||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,2 @@ | ||
// Package webhooks implements the HTTP webhooks spec as defined in https://github.com/cloudevents/spec/blob/86e443ca8575374e281048779a39c6734d3cd58e/http-webhook.md. | ||
package webhooks |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,12 @@ | ||
package webhooks | ||
|
||
import "net/http" | ||
|
||
// WebHook is an HTTP middleware handling webhook requests according to HTTP Webhook spec | ||
type WebHook struct { | ||
} | ||
|
||
// ServeHTTP implements http.Handler interface | ||
func (w *WebHook) ServeHTTP(rw http.ResponseWriter, req *http.Request) { | ||
panic("not implemented") | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,131 @@ | ||
package v01 | ||
|
||
import ( | ||
"encoding/json" | ||
"reflect" | ||
"strings" | ||
"time" | ||
|
||
"github.com/dispatchframework/cloudevents-go-sdk" | ||
) | ||
|
||
const ( | ||
eventTypeKey = "eventType" | ||
eventTypeVersionKey = "eventTypeVersion" | ||
sourceKey = "sourceKey" | ||
eventIDKey = "eventID" | ||
eventTimeKey = "eventTime" | ||
schemaURLKey = "schemaURL" | ||
contentTypeKey = "contentType" | ||
extensionsKey = "extensions" | ||
dataKey = "data" | ||
) | ||
|
||
// Event implements the the CloudEvents specification version 0.1 | ||
// https://github.com/cloudevents/spec/blob/v0.1/spec.md | ||
type Event struct { | ||
// EventType is a mandatory property | ||
// https://github.com/cloudevents/spec/blob/v0.1/spec.md#eventtype | ||
EventType string `json:"eventType"` | ||
// EventTypeVersion is an optional property | ||
// https://github.com/cloudevents/spec/blob/v0.1/spec.md#eventtypeversion | ||
EventTypeVersion string `json:"eventTypeVersion,omitempty"` | ||
// Source is a mandatory property | ||
// TODO: ensure URI parsing | ||
// https://github.com/cloudevents/spec/blob/v0.1/spec.md#source | ||
Source string `json:"source"` | ||
// EventID is a mandatory property | ||
// https://github.com/cloudevents/spec/blob/v0.1/spec.md#eventid | ||
EventID string `json:"eventID"` | ||
// EventTime is an optional property | ||
// https://github.com/cloudevents/spec/blob/v0.1/spec.md#eventtime | ||
EventTime *time.Time `json:"eventTime,omitempty"` | ||
// SchemaURL is an optional property | ||
// https://github.com/cloudevents/spec/blob/v0.1/spec.md#schemaurl | ||
SchemaURL string `json:"schemaURL,omitempty"` | ||
// ContentType is an optional property | ||
// https://github.com/cloudevents/spec/blob/v0.1/spec.md#contenttype | ||
ContentType string `json:"contentType,omitempty"` | ||
// Extensions is an optional property | ||
// https://github.com/cloudevents/spec/blob/v0.1/spec.md#extensions | ||
Extensions map[string]interface{} `json:"extensions,omitempty"` | ||
// Data is an optional property | ||
// https://github.com/cloudevents/spec/blob/v0.1/spec.md#data-1 | ||
Data interface{} `json:"data,omitempty"` | ||
} | ||
|
||
// CloudEventVersion returns the CloudEvents specification version supported by this implementation | ||
func (Event) CloudEventVersion() (version string) { | ||
return cloudevents.Version01 | ||
} | ||
|
||
// Properties returns the map of all supported properties in version 0.1. | ||
// The map value says whether particular property is required. | ||
func (Event) Properties() map[string]bool { | ||
return map[string]bool{ | ||
eventTypeKey: true, | ||
sourceKey: true, | ||
eventIDKey: true, | ||
eventTypeVersionKey: false, | ||
eventTimeKey: false, | ||
schemaURLKey: false, | ||
contentTypeKey: false, | ||
extensionsKey: false, | ||
dataKey: false, | ||
} | ||
} | ||
|
||
// Get implements a generic getter method | ||
func (e *Event) Get(property string) (interface{}, bool) { | ||
field := reflect.ValueOf(e).Elem().FieldByName(strings.Title(property)) | ||
if field.IsValid() { | ||
return field.Interface(), true | ||
} | ||
if e.Extensions == nil { | ||
return nil, false | ||
} | ||
if value, ok := e.Extensions[property]; ok { | ||
return value, ok | ||
} | ||
return nil, false | ||
} | ||
|
||
// Set sets the arbitrary property of event. | ||
func (e *Event) Set(property string, value interface{}) { | ||
field := reflect.ValueOf(e).Elem().FieldByName(strings.Title(property)) | ||
if field.IsValid() { | ||
field.Set(reflect.ValueOf(value)) | ||
return | ||
} | ||
|
||
if e.Extensions == nil { | ||
e.Extensions = make(map[string]interface{}) | ||
} | ||
e.Extensions[property] = value | ||
} | ||
|
||
// Validate returns an error if the event is not correct according to the spec. | ||
func (e *Event) Validate() error { | ||
if e.EventType == "" { | ||
return cloudevents.RequiredPropertyError(eventTypeKey) | ||
} | ||
if e.EventID == "" { | ||
return cloudevents.RequiredPropertyError(eventIDKey) | ||
} | ||
if e.Source == "" { | ||
return cloudevents.RequiredPropertyError(sourceKey) | ||
} | ||
} | ||
|
||
// MarshalJSON implements the JSON Marshaler interface. | ||
func (e *Event) MarshalJSON() ([]byte, error) { | ||
type tmp Event | ||
eventWithVersion := struct { | ||
CloudEventVersion string `json:"cloudEventVersion"` | ||
*tmp | ||
}{ | ||
CloudEventVersion: cloudevents.Version01, | ||
tmp: (*tmp)(e), | ||
} | ||
return json.Marshal(&eventWithVersion) | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,79 @@ | ||
package v01_test | ||
|
||
import ( | ||
"encoding/json" | ||
"reflect" | ||
"testing" | ||
|
||
"github.com/dispatchframework/cloudevents-go-sdk/v01" | ||
) | ||
|
||
func TestNewEvent(t *testing.T) { | ||
event := &v01.Event{ | ||
EventType: "testType", | ||
EventTypeVersion: "testVersion", | ||
Source: "version", | ||
EventID: "12345", | ||
EventTime: nil, | ||
SchemaURL: "http://example.com/schema", | ||
ContentType: "application/json", | ||
Extensions: nil, | ||
Data: map[string]interface{}{"key": "value"}, | ||
} | ||
data, err := json.Marshal(event) | ||
if err != nil { | ||
t.Errorf("JSON Error received: %v", err) | ||
} | ||
|
||
eventUnmarshaled := &v01.Event{} | ||
json.Unmarshal(data, eventUnmarshaled) | ||
if !reflect.DeepEqual(event, eventUnmarshaled) { | ||
t.Errorf("source event %#v and unmarshaled event %#v are not equal", event, eventUnmarshaled) | ||
} | ||
} | ||
|
||
func TestGetSet(t *testing.T) { | ||
event := &v01.Event{ | ||
EventType: "testType", | ||
EventTypeVersion: "testVersion", | ||
Source: "version", | ||
EventID: "12345", | ||
EventTime: nil, | ||
SchemaURL: "http://example.com/schema", | ||
ContentType: "application/json", | ||
Extensions: nil, | ||
Data: map[string]interface{}{"key": "value"}, | ||
} | ||
|
||
value, ok := event.Get("nonexistent") | ||
if ok { | ||
t.Error("Get ok for nonexistent key shoud be false, but isn't") | ||
} | ||
if value != nil { | ||
t.Error("Get value for nonexistent key should be nil, but isn't") | ||
} | ||
|
||
value, ok = event.Get("contentType") | ||
if !ok { | ||
t.Error("Get ok for existing key shoud be true, but isn't") | ||
|
||
} | ||
if value != "application/json" { | ||
t.Errorf("Get value for contentType should be application/json, but is %s", value) | ||
} | ||
|
||
event.Set("eventType", "newType") | ||
if event.EventType != "newType" { | ||
t.Errorf("expected eventType to be newType, got %s", event.EventType) | ||
} | ||
|
||
event.Set("ext", "somevalue") | ||
value, ok = event.Get("ext") | ||
if !ok { | ||
t.Error("Get ok for ext key shoud be false, but isn't") | ||
} | ||
if value != "somevalue" { | ||
t.Errorf("Get value for ext key should be somevalue, but is %s", value) | ||
} | ||
|
||
} |
Oops, something went wrong.