Giter VIP home page Giter VIP logo

sdk-go's Introduction

Go SDK for CloudEvents

go-doc Go Report Card Releases LICENSE

Official CloudEvents SDK to integrate your application with CloudEvents.

This library will help you to:

Note: Supported CloudEvents specification: 0.3, 1.0

Note: Supported go version: 1.18+

Get started

Add the module as dependency using go mod:

go get github.com/cloudevents/sdk-go/[email protected]

And import the module in your code

import cloudevents "github.com/cloudevents/sdk-go/v2"

Send your first CloudEvent

To send a CloudEvent using HTTP:

func main() {
	c, err := cloudevents.NewClientHTTP()
	if err != nil {
		log.Fatalf("failed to create client, %v", err)
	}

	// Create an Event.
	event :=  cloudevents.NewEvent()
	event.SetSource("example/uri")
	event.SetType("example.type")
	event.SetData(cloudevents.ApplicationJSON, map[string]string{"hello": "world"})

	// Set a target.
	ctx := cloudevents.ContextWithTarget(context.Background(), "http://localhost:8080/")

	// Send that Event.
	if result := c.Send(ctx, event); cloudevents.IsUndelivered(result) {
		log.Fatalf("failed to send, %v", result)
	} else {
		log.Printf("sent: %v", event)
		log.Printf("result: %v", result)
	}
}

Receive your first CloudEvent

To start receiving CloudEvents using HTTP:

func receive(event cloudevents.Event) {
	// do something with event.
    fmt.Printf("%s", event)
}

func main() {
	// The default client is HTTP.
	c, err := cloudevents.NewClientHTTP()
	if err != nil {
		log.Fatalf("failed to create client, %v", err)
	}
	if err = c.StartReceiver(context.Background(), receive); err != nil {
		log.Fatalf("failed to start receiver: %v", err)
	}
}

Create a CloudEvent from an HTTP Request

func handler(w http.ResponseWriter, r *http.Request) {
	event, err := cloudevents.NewEventFromHTTPRequest(r)
	if err != nil {
		log.Printf("failed to parse CloudEvent from request: %v", err)
		http.Error(w, http.StatusText(http.StatusBadRequest), http.StatusBadRequest)
	}
	w.Write([]byte(event.String()))
	log.Println(event.String())
}

Serialize/Deserialize a CloudEvent

To marshal a CloudEvent into JSON:

event := cloudevents.NewEvent()
event.SetID("example-uuid-32943bac6fea")
event.SetSource("example/uri")
event.SetType("example.type")
event.SetData(cloudevents.ApplicationJSON, map[string]string{"hello": "world"})

bytes, err := json.Marshal(event)

To unmarshal JSON back into a CloudEvent:

event :=  cloudevents.NewEvent()

err := json.Unmarshal(bytes, &event)

Go further

Community

Each SDK may have its own unique processes, tooling and guidelines, common governance related material can be found in the CloudEvents community directory. In particular, in there you will find information concerning how SDK projects are managed, guidelines for how PR reviews and approval, and our Code of Conduct information.

If there is a security concern with one of the CloudEvents specifications, or with one of the project's SDKs, please send an email to [email protected].

Additional SDK Resources

sdk-go's People

Contributors

agrimmer avatar alanconway avatar antoineco avatar cali0707 avatar dan-j avatar dependabot[bot] avatar duglin avatar embano1 avatar grayside avatar ian-mi avatar inigohu avatar joaopgrassi avatar johejo avatar kars7e avatar kconwayinvision avatar lionelvillard avatar markpeek avatar markusthoemmes avatar matejvasek avatar matzew avatar n3wscott avatar nachocano avatar nbajaj90 avatar odacremolbap avatar oleg-kulyk avatar slinkydeveloper avatar tenczar avatar thinkerou avatar yanmxa avatar yolocs avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

sdk-go's Issues

FromRequest should take Event as an argument rather than return value

I think it's more convenient to explicitly pass in the type you expect to get in return (or create a new method for this):

var v2event v02.Event
err := cloudevents.FromRequest(req, &v2event)
...

No type assertions are required to use the v2event, and it follows golang conventions.

Add typed getters and setters

It would help to have getters (and setters?) for basic types such as GetInt, GetString so that users aren't forced to convert each returned value.

Development documentation

Should have documentation for Developers explaining how to set up the dev environment, and contribute

[500 Internal Server Error] in default-broker-ingress pod

The event watcher and the broker ingress and filter are running.
(kubectl get pods)
NAME READY STATUS RESTARTS AGE
default-broker-filter-694464b4f4-j4kg7 2/2 Running 1 17h
default-broker-ingress-55bbb8555b-ck9lw 2/2 Running 1 17h
glusterfs-8przp 1/1 Running 0 17h
glusterfs-ddj9h 1/1 Running 5 13d
glusterfs-jpv2p 1/1 Running 0 8d
heketi-576bb8967c-vgm94 0/1 CrashLoopBackOff 0 8d
testevents-c2m8m-xzd8s-75fd6bcf48-zbjzn 2/2 Running 0 17h

The broker.yaml which I applied is -
apiVersion: eventing.knative.dev/v1alpha1
kind: Broker
metadata:
name: default
namespace: default
spec:
channelTemplate:
provisioner:
apiVersion: eventing.knative.dev/v1alpha1
kind: ClusterChannelProvisioner
name: in-memory-channel

The logs of the ingress pod gives the following error -
(kubectl logs default-broker-ingress-55bbb8555b-ck9lw -c ingress)
"got an error from receiver fn: error sending cloudevent: Status[500 Internal Server Error] "

The logs of the testevents pod is flusshing this error -
(kubectl logs testevents-c2m8m-xzd8s-75fd6bcf48-zbjzn -c source)
"{"level":"info","ts":1557311356.807349,"logger":"fallback","caller":"kubernetesevents/adapter.go:113","msg":"Event delivery failed: error sending cloudevent: Status[400 Bad Request] ","eventID":"21c4c723-717c-11e9-91cf-14feb50fb61e","sink":"http://default-broker.default.svc.cluster.local/"} "

Regards
Rashi

Support response headers from resp := client.Send(e)?

The response from send is not passed back a context, and thus does not have access to any transport specific fields that might be valuable to them, such as http headers that are sent in response to a Send.

Consider solving them with a function similar to receive for response.

Originally posted by @n3wscott in #85

Use of cloudevent HTTP transport as codec only

There are 2 use cases for cloudevents transport:

  1. As message codec only: to translate Event to/from the native transport libraries message type (e.g. for HTTP this would be http.Request and http.Response) This is for applications that want full access to features of the underlying transport library (e.g. net/http) and decisions about dispatching, use of goroutines, memory management etc.

  2. As a generic API for transport-neutral applications that want to allow replaceable transports. This is for applications that care most about isolating themselves from transport details. It is ok (when necessary) to limit access to the underlying transport and make implementation choices - the goal is encapsulation rather than total freedom.

The HTTP code provides 2. and is close to 1. it is missing some convenience methods, e.g.

func (m *Message) toRequest(r *http.Request)

This issue serves for HTTP, other issues referencing this can be raised for other transports.

Need a way to tell when a Transport is ready

I can't see any way to tell when a transport is "ready", which is critical if you need to query the transport state. For example in transport.http there is this comment:

// GetPort returns the port the transport is active on.
// .Port can be set to 0, which means the transport selects a port, GetPort
// allows the transport to report back the selected port.

That is actually not possible: while one goroutine is blocked in StartReceiver(), there's no way for another goroutine to tell when it can call GetPort() - it has to happen after the net.Listen() call.

Proposed solution (I'll write a PR if this seems acceptable), 2 versions the clean and compatible:

// clean but incompatible, adding a method to Transport interface invalidates existing transports:
interface Transport {
...
// Ready returns a channel that closes when StartReceiver() has completed any
// set up (listening, connecting etc.) and is waiting to receive events. It always
// closes before StartReceiver() returns, including in the event of an error.
Ready() <-chan struct{}
}

// backwards compatible version, separate interface. Transports are strongly encouraged to
// implement it but existing transports won't be broken.
interface Readyer {
Ready() <-chan struct{}
}

error on json marshal for binary mode body arrays

If the sender sends an array type, decode attempts to unmarshal the data before checking for headers via the various message.CloudEventsVersion() methods.

Clean this up, check for binary mode first and then the body for structured.

Allow tuning net/http MaxIdleConnsPerHost for high throughput

While load-testing some parts of Knative, I ran into an issue where the cloudevents client with HTTP transport can end up with a lot of sockets in TIME_WAIT when sending events to a single host with a high throughput.

golang/go#16012 (comment) describes both the issue and the solution for the issue I hit, which is to allow tuning the MaxIdleConnsPerHost value of the underlying net/http transport. While I can workaround this as shown in that comment, I opened this issue to discuss surfacing this in cloudevents/transport/http.

XML double encodes inside of nats payloads on partial message decode for v0.3

The json codec does not see quoted xml as data that can be left alone in v0.3 for some reason.

Running the complex example: complex sender -> httptonats -> nats receiver

I get the following error from the nats client:

Got Event Context: {SpecVersion:0.3 Type:com.cloudevents.sample.nats.sent Source:{URL:{Scheme:https Opaque: User: Host:github.com Path:/cloudevents/sdk-go/cmd/samples/sender RawPath: ForceQuery:false RawQuery: Fragment:}} ID:32e4cc1e-dec9-4fcf-abe7-5ce1d84b7295 Time:2019-02-22T09:45:53.090594-08:00 SchemaURL: ContentType: Extensions:map[]}
Got Data Error: [json] found bytes ""\u003cExample\u003e\u003cSequence\u003e19\u003c/Sequence\u003e\u003cMessage\u003eHello, Structured Encoding v0.3 using application/xml!\u003c/Message\u003e\u003c/Example\u003e"", but failed to unmarshal: json: cannot unmarshal string into Go value of type main.Example
Got Data: &{Sequence:0 Message:}

Incorrect usage of message.contentType in v0.1

https://github.com/cloudevents/spec/blob/v0.1/spec.md#contenttype

In binary mode there should be a Ce-ContentType AND Content-Type header if the message content type is used.

This sdk couples the inner and the outer content type.

How do I ask for a request with structured encoding without setting the Ce-ContentType to

For example, I do not see a way to encode the following cloudevent:

{
    "cloudEventsVersion" : "0.1",
    "eventType" : "com.example.someevent",
    "eventTypeVersion" : "1.0",
    "source" : "/mycontext",
    "eventID" : "A234-1234-1234",
    "eventTime" : "2018-04-05T17:31:00Z",
    "extensions" : {
      "comExampleExtension" : "value"
    },
    "contentType" : "text/xml",
    "data" : "<much wow=\"xml\"/>"
}

The Header "Content-Type" will be "application/cloudevents+json" but the data content type is "text/xml".

Double quote for extensions attributes in binary encoding (http and amqp)

When forwarding a CloudEvent, that was received in structured mode, with http or amqp binary mode, the values of extension attributes are double quoted like
cause: ""d282c453-8ccd-4f29-b754-3344cb878dc5""
I think it might be due to the code lines in function toHeaders:

		if s, ok := v.(string); ok {
			h[prefix+k] = s
			continue
		}

In jsoncodec.go extensions are copied from the map raw. Therefore they are of type []byte. When converting the event to binary, the type assertion v.(string) fails.

Auto-generate ID when it is empty.

It would be nice to do this:

		event := cloudevents.Event{
			Context: cloudevents.EventContextV02{
				Type:   "com.cloudevents.readme.sent",
				Source: source,
			},
			Data: data,
		}

Rather than this:

		event := cloudevents.Event{
			Context: cloudevents.EventContextV02{
				ID:     uuid.New().String(),
				Type:   "com.cloudevents.readme.sent",
				Source: source,
			},
			Data: data,
		}

Document of receiving a cloudevents.Event is out of date

In README.md, this part is out of date now.

Receiving a cloudevents.Event via the HTTP Transport:

// import "github.com/cloudevents/sdk-go/pkg/cloudevents/client/http"

func Receive(event cloudevents.Event) {
	// do something with event.Context and event.Data (via event.DataAs(foo)
}

func main() {
	ctx := context.Background()
	
	c, err := http.New(
		http.WithTarget("http://localhost:8080/"),
		http.WithEncoding(cloudeventshttp.BinaryV02),
		)
		if err != nil {
			panic("unable to create cloudevent client: " + err.Error())
		}
	
	if err := c.StartReceiver(ctx, Receive); err != nil {
		panic("unable to start the cloudevent receiver: " + err.Error())
	}
	<-ctx.Done()
}

EventContext Extensions should be exported to JSON

It would be great if extensions will be marshaled to json with valid key (BTW it's marshaled now with "-" key) - usage of - should be without omitempty

example marshaled event with - instead of extensions:

{  
   "context":{  
      "specversion":"",
      "type":"basket",
      "source":"http://events.kinuin.io/types/eventType",
      "id":"ce45f584-21dd-41be-a846-8a79606dad05",
      "time":"2019-03-29T08:14:49.35340161Z",
      "datacontenttype":"application/json",
      "-":{  
         "request-headers":{  
            "X-Request-Id":"35434715274"
         }
      }
   },
   "data":{  
      "payload":"Time: 2019-03-29 09:14:49.352101145 +0100 CET m=+314.000752244"
   }
}
type EventContextV03 struct {
	// SpecVersion - The version of the CloudEvents specification used by the event.
	SpecVersion string `json:"specversion"`
	// Type - The type of the occurrence which has happened.
	Type string `json:"type"`
	// Source - A URI describing the event producer.
	Source types.URLRef `json:"source"`
	// ID of the event; must be non-empty and unique within the scope of the producer.
	ID string `json:"id"`
	// Time - A Timestamp when the event happened.
	Time *types.Timestamp `json:"time,omitempty"`
	// SchemaURL - A link to the schema that the `data` attribute adheres to.
	SchemaURL *types.URLRef `json:"schemaurl,omitempty"`
	// GetDataMediaType - A MIME (RFC2046) string describing the media type of `data`.
	// TODO: Should an empty string assume `application/json`, `application/octet-stream`, or auto-detect the content?
	DataContentType *string `json:"datacontenttype,omitempty"`
	// Extensions - Additional extension metadata beyond the base spec.
	Extensions map[string]interface{} `json:"-,omitempty"` // TODO: decide how we want extensions to be inserted
}

Redundant use of string contants

Within the transport codecs there are quite a few string constants with some redundant use. Should these be put into string constants? And if so, should any of them be made part of the public API?
Two quick examples:

$ grep -r '"ce-specversion"' .
./pkg/cloudevents/client/client_test.go:					"ce-specversion": {"0.2"},
./pkg/cloudevents/client/client_test.go:					"ce-specversion": {"0.3"},
./pkg/cloudevents/transport/http/codec_v02.go:	h.Set("ce-specversion", ec.SpecVersion)
./pkg/cloudevents/transport/http/codec_v02.go:	ec.SpecVersion = h.Get("ce-specversion")
./pkg/cloudevents/transport/http/codec_v02.go:	h.Del("ce-specversion")
./pkg/cloudevents/transport/http/codec_v02_test.go:					"ce-specversion": {"0.2"},
./pkg/cloudevents/transport/http/codec_v02_test.go:					"ce-specversion": {"0.2"},
./pkg/cloudevents/transport/http/codec_v02_test.go:					"ce-specversion": {"0.2"},
./pkg/cloudevents/transport/http/codec_v03.go:	h.Set("ce-specversion", ec.SpecVersion)
./pkg/cloudevents/transport/http/codec_v03.go:	ec.SpecVersion = h.Get("ce-specversion")
./pkg/cloudevents/transport/http/codec_v03.go:	h.Del("ce-specversion")
./pkg/cloudevents/transport/http/codec_v03_test.go:					"ce-specversion": {"0.3"},
./pkg/cloudevents/transport/http/codec_v03_test.go:					"ce-specversion": {"0.3"},
./pkg/cloudevents/transport/http/codec_v03_test.go:					"ce-specversion": {"0.3"},
./pkg/cloudevents/transport/http/message.go:		if v := m.Header["ce-specversion"]; len(v) == 1 {
./pkg/cloudevents/transport/http/message.go:		if ver := m.Header.Get("ce-specversion"); ver != "" {


$ grep -r '"ce-"' .
./pkg/cloudevents/transport/http/codec_v02.go:				h.Set("ce-"+k+"-"+subkey, string(encoded))
./pkg/cloudevents/transport/http/codec_v02.go:		h.Set("ce-"+k, string(encoded))
./pkg/cloudevents/transport/http/codec_v02.go:		if len(k) > len("ce-") && strings.EqualFold(k[:len("ce-")], "ce-") {
./pkg/cloudevents/transport/http/codec_v02.go:			ak := strings.ToLower(k[len("ce-"):])
./pkg/cloudevents/transport/http/codec_v03.go:				h.Set("ce-"+k+"-"+subkey, string(encoded))
./pkg/cloudevents/transport/http/codec_v03.go:		h.Set("ce-"+k, string(encoded))
./pkg/cloudevents/transport/http/codec_v03.go:		if len(k) > len("ce-") && strings.EqualFold(k[:len("ce-")], "ce-") {
./pkg/cloudevents/transport/http/codec_v03.go:			ak := strings.ToLower(k[len("ce-"):])

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.