diff --git a/.circleci/config.yml b/.circleci/config.yml
index 25122d772..5039115d0 100644
--- a/.circleci/config.yml
+++ b/.circleci/config.yml
@@ -55,7 +55,7 @@ jobs:
- run: go get honnef.co/go/tools/cmd/staticcheck
- run:
name: "Run staticcheck"
- command: pushd v2; staticcheck ./...; popd
+ command: pushd v2; staticcheck -tags conformance ./...; popd
- run:
name: Run unit tests
command: |
diff --git a/hack/conformance-test.sh b/hack/conformance-test.sh
index cdb024f1d..445dd4c4f 100755
--- a/hack/conformance-test.sh
+++ b/hack/conformance-test.sh
@@ -7,7 +7,7 @@ set -o pipefail
# v2 only
pushd ./v2/test/conformance/
-go test -v -timeout 15s
+go test --tags=conformance -v -timeout 15s
# Remove test only deps.
go mod tidy
diff --git a/hack/unit-test.sh b/hack/unit-test.sh
index f8a3b8a95..f952803dc 100755
--- a/hack/unit-test.sh
+++ b/hack/unit-test.sh
@@ -4,21 +4,6 @@ set -o errexit
set -o nounset
set -o pipefail
-# v1
-pushd ./v1
-touch ./coverage.tmp
-echo 'mode: atomic' > ./coverage.txt
-COVERPKG=$(go list ./... | grep -v /vendor | tr "\n" ",")
-for gomodule in $(go list ./... | grep -v /cmd | grep -v /vendor)
-do
- go test -v -timeout 15s -covermode=atomic -coverprofile=coverage.tmp -coverpkg "$COVERPKG" "$gomodule" 2>&1 | sed 's/ of statements in.*//; /warning: no packages being tested depend on matches for pattern /d'
- tail -n +2 coverage.tmp >> ./coverage.txt
-done
-rm coverage.tmp
-# Remove test only deps.
-go mod tidy
-popd
-
# v2
pushd ./v2
touch ./coverage.tmp
diff --git a/v1/README.md b/v1/README.md
deleted file mode 100644
index 93d28c03d..000000000
--- a/v1/README.md
+++ /dev/null
@@ -1,76 +0,0 @@
-# NOTE: The v1 directory will be removed for v2.0.0.
-
-We will make a final migration release that will match the v2.0.0 release with
-the addition of this legacy directory for migration support.
-
-# Migration Guide
-
-To enable an easier migration, this directory holds a copy of the code in the
-branch `release-1.y.z`.
-
-Switch your imports from `github.com/cloudevents/sdk-go` to
-`github.com/cloudevents/sdk-go/legacy` and your code should compile again,
-letting you get on with the task of migrating to v2.
-
-## Background
-
-In the migration from v1 to v2 of the SDK, there are a lot of API breaking
-changes. It is shorter to define what is not a breaking change:
-
-- The `Event` object marshaling results in the same json.
-- cloudevents.NewDefault should get an http server working out of the box.
-- Most of alias.go file remains with some exceptions.
-- Most of the original demos remain in the cmd dir to see how the new
- integrations should be.
-
-Large breaking changes have to do with a strategy change inside the SDK to shift
-the control to the integrator, allowing more direct access to the knobs required
-to make choices over plumbing those knobs through the SDK down to the original
-transports that implement the features integrators are really trying to control.
-
-If you implemented a custom transport, the migration to how protocol bindings
-work is covered in the document [TBD](TODO).
-
-## Architectural Changes
-
-New Architectural Layout:
-
-```
-Client <-- Operates on event.Event
- |
- v
-Protocol <-- Operates on binding.Message
- |
- v
-3rd Party <-- Operates out of our control
-```
-
-Some Architectural changes that are worth noting:
-
-- Client still exists but it has a new API.
- - client.Request allows for responses from outbound events.
- - client.StartReceiver has a mode that will test for underlying support if the
- receiver function is allowed to produce responses to inbound events.
-- Client interface is event.Event focused.
-- Protocol layer operates on `binding.Message`
- - This is a change from v1, `transport.Transport` mixed up `event.Event`
- objects into the interface. With the thinking that codecs were specific to a
- transport. But as we implemented bindings, it became clear that there are
- many cases where the cost to convert a 3rd Party message into a
- `event.Event` is too high and it is better to stay in the intermediate state
- of a `binding.Message` (similar to a `transport.Message` but
- `transport.Message` was never exposed in the v1 architecture).
-- Setting a transport to emit a specific version of cloudevents is an
- anti-pattern. If a version is required, the burden should be on the integrator
- to implement what they need. The edge cases the SDK had to handle made that
- code unrulely. It is simpler if the SDK does simple things. So outbound event
- encoding is based on the `event.Event` that is passed in.
-
-## Moves and renames
-
-Note these are based on internal packages unless noted as from alias.
-
-- `cloudevents.Event` --> `event.Event`
-- `transport.Codec` --> Deleted, the binding concept replaced it.
-- `transport.Transport` --> Deleted, the
- protocol.Sender/Receiver/Requester/Responder interfaces replaced it.
diff --git a/v1/alias.go b/v1/alias.go
deleted file mode 100644
index 885c37797..000000000
--- a/v1/alias.go
+++ /dev/null
@@ -1,241 +0,0 @@
-package v1
-
-// Package cloudevents alias' common functions and types to improve discoverability and reduce
-// the number of imports for simple HTTP clients.
-
-import (
- "github.com/cloudevents/sdk-go/v1/cloudevents"
- "github.com/cloudevents/sdk-go/v1/cloudevents/client"
- "github.com/cloudevents/sdk-go/v1/cloudevents/context"
- "github.com/cloudevents/sdk-go/v1/cloudevents/observability"
- "github.com/cloudevents/sdk-go/v1/cloudevents/transport/http"
- "github.com/cloudevents/sdk-go/v1/cloudevents/types"
-)
-
-// Client
-
-// Deprecated: legacy.
-type ClientOption client.Option
-
-// Deprecated: legacy.
-type Client = client.Client
-
-// Deprecated: legacy.
-type ConvertFn = client.ConvertFn
-
-// Event
-
-// Deprecated: legacy.
-type Event = cloudevents.Event
-
-// Deprecated: legacy.
-type EventResponse = cloudevents.EventResponse
-
-// Context
-
-// Deprecated: legacy.
-type EventContext = cloudevents.EventContext
-
-// Deprecated: legacy.
-type EventContextV1 = cloudevents.EventContextV1
-
-// Deprecated: legacy.
-type EventContextV01 = cloudevents.EventContextV01
-
-// Deprecated: legacy.
-type EventContextV02 = cloudevents.EventContextV02
-
-// Deprecated: legacy.
-type EventContextV03 = cloudevents.EventContextV03
-
-// Custom Types
-
-// Deprecated: legacy.
-type Timestamp = types.Timestamp
-
-// Deprecated: legacy.
-type URLRef = types.URLRef
-
-// HTTP Transport
-
-// Deprecated: legacy.
-type HTTPOption http.Option
-
-// Deprecated: legacy.
-type HTTPTransport = http.Transport
-
-// Deprecated: legacy.
-type HTTPTransportContext = http.TransportContext
-
-// Deprecated: legacy.
-type HTTPTransportResponseContext = http.TransportResponseContext
-
-// Deprecated: legacy.
-type HTTPEncoding = http.Encoding
-
-const (
- // Encoding
- // Deprecated: legacy.
- ApplicationXML = cloudevents.ApplicationXML
- // Deprecated: legacy.
- ApplicationJSON = cloudevents.ApplicationJSON
- // Deprecated: legacy.
- ApplicationCloudEventsJSON = cloudevents.ApplicationCloudEventsJSON
- // Deprecated: legacy.
- ApplicationCloudEventsBatchJSON = cloudevents.ApplicationCloudEventsBatchJSON
- // Deprecated: legacy.
- Base64 = cloudevents.Base64
-
- // Event Versions
-
- // Deprecated: legacy.
- VersionV1 = cloudevents.CloudEventsVersionV1
- // Deprecated: legacy.
- VersionV01 = cloudevents.CloudEventsVersionV01
- // Deprecated: legacy.
- VersionV02 = cloudevents.CloudEventsVersionV02
- // Deprecated: legacy.
- VersionV03 = cloudevents.CloudEventsVersionV03
-
- // HTTP Transport Encodings
-
- // Deprecated: legacy.
- HTTPBinaryV1 = http.BinaryV1
- // Deprecated: legacy.
- HTTPStructuredV1 = http.StructuredV1
- // Deprecated: legacy.
- HTTPBatchedV1 = http.BatchedV1
- // Deprecated: legacy.
- HTTPBinaryV01 = http.BinaryV01
- // Deprecated: legacy.
- HTTPStructuredV01 = http.StructuredV01
- // Deprecated: legacy.
- HTTPBinaryV02 = http.BinaryV02
- // Deprecated: legacy.
- HTTPStructuredV02 = http.StructuredV02
- // Deprecated: legacy.
- HTTPBinaryV03 = http.BinaryV03
- // Deprecated: legacy.
- HTTPStructuredV03 = http.StructuredV03
- // Deprecated: legacy.
- HTTPBatchedV03 = http.BatchedV03
-
- // Context HTTP Transport Encodings
-
- Binary = http.Binary
- Structured = http.Structured
-)
-
-var (
- // ContentType Helpers
-
- // Deprecated: legacy.
- StringOfApplicationJSON = cloudevents.StringOfApplicationJSON
- // Deprecated: legacy.
- StringOfApplicationXML = cloudevents.StringOfApplicationXML
- // Deprecated: legacy.
- StringOfApplicationCloudEventsJSON = cloudevents.StringOfApplicationCloudEventsJSON
- // Deprecated: legacy.
- StringOfApplicationCloudEventsBatchJSON = cloudevents.StringOfApplicationCloudEventsBatchJSON
- // Deprecated: legacy.
- StringOfBase64 = cloudevents.StringOfBase64
-
- // Client Creation
-
- // Deprecated: legacy.
- NewClient = client.New
- // Deprecated: legacy.
- NewDefaultClient = client.NewDefault
-
- // Client Options
-
- // Deprecated: legacy.
- WithEventDefaulter = client.WithEventDefaulter
- // Deprecated: legacy.
- WithUUIDs = client.WithUUIDs
- // Deprecated: legacy.
- WithTimeNow = client.WithTimeNow
- // Deprecated: legacy.
- WithConverterFn = client.WithConverterFn
- // Deprecated: legacy.
- WithDataContentType = client.WithDataContentType
- // Deprecated: legacy.
- WithoutTracePropagation = client.WithoutTracePropagation
-
- // Event Creation
-
- // Deprecated: legacy.
- NewEvent = cloudevents.New
-
- // Tracing
-
- // Deprecated: legacy.
- EnableTracing = observability.EnableTracing
-
- // Context
-
- // Deprecated: legacy.
- ContextWithTarget = context.WithTarget
- // Deprecated: legacy.
- TargetFromContext = context.TargetFrom
- // Deprecated: legacy.
- ContextWithEncoding = context.WithEncoding
- // Deprecated: legacy.
- EncodingFromContext = context.EncodingFrom
-
- // Custom Types
-
- // Deprecated: legacy.
- ParseTimestamp = types.ParseTimestamp
- // Deprecated: legacy.
- ParseURLRef = types.ParseURLRef
- // Deprecated: legacy.
- ParseURIRef = types.ParseURIRef
- // Deprecated: legacy.
- ParseURI = types.ParseURI
-
- // HTTP Transport
-
- // Deprecated: legacy.
- NewHTTPTransport = http.New
-
- // HTTP Transport Options
-
- // Deprecated: legacy.
- WithTarget = http.WithTarget
- // Deprecated: legacy.
- WithMethod = http.WithMethod
- // Deprecated: legacy.
- WitHHeader = http.WithHeader
- // Deprecated: legacy.
- WithShutdownTimeout = http.WithShutdownTimeout
- // Deprecated: legacy.
- WithEncoding = http.WithEncoding
- // Deprecated: legacy.
- WithContextBasedEncoding = http.WithContextBasedEncoding
- // Deprecated: legacy.
- WithBinaryEncoding = http.WithBinaryEncoding
- // Deprecated: legacy.
- WithStructuredEncoding = http.WithStructuredEncoding
- // Deprecated: legacy.
- WithPort = http.WithPort
- // Deprecated: legacy.
- WithPath = http.WithPath
- // Deprecated: legacy.
- WithMiddleware = http.WithMiddleware
- // Deprecated: legacy.
- WithLongPollTarget = http.WithLongPollTarget
- // Deprecated: legacy.
- WithListener = http.WithListener
- // Deprecated: legacy.
- WithHTTPTransport = http.WithHTTPTransport
-
- // HTTP Context
-
- // Deprecated: legacy.
- HTTPTransportContextFrom = http.TransportContextFrom
- // Deprecated: legacy.
- ContextWithHeader = http.ContextWithHeader
- // Deprecated: legacy.
- SetContextHeaders = http.SetContextHeaders
-)
diff --git a/v1/binding/buffering/acks_before_finish_message.go b/v1/binding/buffering/acks_before_finish_message.go
deleted file mode 100644
index 47983e2f9..000000000
--- a/v1/binding/buffering/acks_before_finish_message.go
+++ /dev/null
@@ -1,33 +0,0 @@
-package buffering
-
-import (
- "sync/atomic"
-
- "github.com/cloudevents/sdk-go/v1/binding"
-)
-
-type acksMessage struct {
- binding.Message
- requiredAcks int32
-}
-
-func (m *acksMessage) GetWrappedMessage() binding.Message {
- return m.Message
-}
-
-func (m *acksMessage) Finish(err error) error {
- remainingAcks := atomic.AddInt32(&m.requiredAcks, -1)
- if remainingAcks == 0 {
- return m.Message.Finish(err)
- }
- return nil
-}
-
-var _ binding.MessageWrapper = (*acksMessage)(nil)
-
-// WithAcksBeforeFinish returns a wrapper for m that calls m.Finish()
-// only after the specified number of acks are received.
-// Use it when you need to route a Message to more Sender instances
-func WithAcksBeforeFinish(m binding.Message, requiredAcks int) binding.Message {
- return &acksMessage{Message: m, requiredAcks: int32(requiredAcks)}
-}
diff --git a/v1/binding/buffering/acks_before_finish_message_test.go b/v1/binding/buffering/acks_before_finish_message_test.go
deleted file mode 100644
index 31a9baeed..000000000
--- a/v1/binding/buffering/acks_before_finish_message_test.go
+++ /dev/null
@@ -1,83 +0,0 @@
-package buffering
-
-import (
- "context"
- "net/url"
- "sync"
- "testing"
-
- "github.com/stretchr/testify/assert"
-
- cloudevents "github.com/cloudevents/sdk-go/v1"
- "github.com/cloudevents/sdk-go/v1/binding"
- "github.com/cloudevents/sdk-go/v1/cloudevents/types"
-)
-
-func TestWithAcksBeforeFinish(t *testing.T) {
- var testEvent = cloudevents.Event{
- Data: []byte(`"data"`),
- DataEncoded: true,
- Context: cloudevents.EventContextV1{
- DataContentType: cloudevents.StringOfApplicationJSON(),
- Source: types.URIRef{URL: url.URL{Path: "source"}},
- ID: "id",
- Type: "type"}.AsV1(),
- }
-
- finishCalled := false
- finishMessage := binding.WithFinish(binding.EventMessage(testEvent), func(err error) {
- finishCalled = true
- })
-
- wg := sync.WaitGroup{}
-
- messageToTest := WithAcksBeforeFinish(finishMessage, 1000)
- for i := 0; i < 1000; i++ {
- wg.Add(1)
- go func(m binding.Message) {
- ch := make(chan binding.Message, 1)
- assert.NoError(t, binding.ChanSender(ch).Send(context.Background(), m))
- <-ch
- wg.Done()
- }(messageToTest)
- }
-
- wg.Wait()
- assert.True(t, finishCalled)
-}
-
-func TestCopyAndWithAcksBeforeFinish(t *testing.T) {
- var testEvent = cloudevents.Event{
- Data: []byte(`"data"`),
- DataEncoded: true,
- Context: cloudevents.EventContextV1{
- DataContentType: cloudevents.StringOfApplicationJSON(),
- Source: types.URIRef{URL: url.URL{Path: "source"}},
- ID: "id",
- Type: "type"}.AsV1(),
- }
-
- finishCalled := false
- finishMessage := binding.WithFinish(binding.EventMessage(testEvent), func(err error) {
- finishCalled = true
- })
-
- copiedMessage, err := BufferMessage(context.Background(), finishMessage)
- assert.NoError(t, err)
-
- wg := sync.WaitGroup{}
-
- messageToTest := WithAcksBeforeFinish(copiedMessage, 1000)
- for i := 0; i < 1000; i++ {
- wg.Add(1)
- go func(m binding.Message) {
- ch := make(chan binding.Message, 1)
- assert.NoError(t, binding.ChanSender(ch).Send(context.Background(), m))
- <-ch
- wg.Done()
- }(messageToTest)
- }
-
- wg.Wait()
- assert.True(t, finishCalled)
-}
diff --git a/v1/binding/buffering/binary_buffer_message.go b/v1/binding/buffering/binary_buffer_message.go
deleted file mode 100644
index 12e95ecaf..000000000
--- a/v1/binding/buffering/binary_buffer_message.go
+++ /dev/null
@@ -1,106 +0,0 @@
-package buffering
-
-import (
- "bytes"
- "context"
- "io"
-
- "github.com/valyala/bytebufferpool"
-
- "github.com/cloudevents/sdk-go/v1/binding"
- "github.com/cloudevents/sdk-go/v1/binding/spec"
-)
-
-var binaryMessagePool bytebufferpool.Pool
-
-// binaryBufferedMessage implements a binary-mode message as a simple struct.
-// This message implementation is used by CopyMessage and BufferMessage
-type binaryBufferedMessage struct {
- metadata map[spec.Attribute]interface{}
- extensions map[string]interface{}
- body *bytebufferpool.ByteBuffer
-}
-
-func (m *binaryBufferedMessage) Start(ctx context.Context) error {
- m.metadata = make(map[spec.Attribute]interface{}, 4)
- m.extensions = make(map[string]interface{})
- return nil
-}
-
-func (m *binaryBufferedMessage) End() error {
- return nil
-}
-
-func (m *binaryBufferedMessage) GetParent() binding.Message {
- return nil
-}
-
-func (m *binaryBufferedMessage) Encoding() binding.Encoding {
- return binding.EncodingBinary
-}
-
-func (m *binaryBufferedMessage) Structured(context.Context, binding.StructuredEncoder) error {
- return binding.ErrNotStructured
-}
-
-func (m *binaryBufferedMessage) Binary(ctx context.Context, b binding.BinaryEncoder) (err error) {
- err = b.Start(ctx)
- if err != nil {
- return
- }
- for k, v := range m.metadata {
- err = b.SetAttribute(k, v)
- if err != nil {
- return
- }
- }
- for k, v := range m.extensions {
- err = b.SetExtension(k, v)
- if err != nil {
- return
- }
- }
- if m.body != nil {
- err = b.SetData(bytes.NewReader(m.body.Bytes()))
- if err != nil {
- return
- }
- }
- return b.End()
-}
-
-func (b *binaryBufferedMessage) Finish(error) error {
- if b.body != nil {
- binaryMessagePool.Put(b.body)
- }
- return nil
-}
-
-// Binary Encoder
-func (b *binaryBufferedMessage) SetData(data io.Reader) error {
- buf := binaryMessagePool.Get()
- w, err := io.Copy(buf, data)
- if err != nil {
- return err
- }
- if w == 0 {
- binaryMessagePool.Put(buf)
- return nil
- }
- b.body = buf
- return nil
-}
-
-func (b *binaryBufferedMessage) SetAttribute(attribute spec.Attribute, value interface{}) error {
- // If spec version we need to change to right context struct
- b.metadata[attribute] = value
- return nil
-}
-
-func (b *binaryBufferedMessage) SetExtension(name string, value interface{}) error {
- b.extensions[name] = value
- return nil
-}
-
-var _ binding.Message = (*binaryBufferedMessage)(nil) // Test it conforms to the interface
-var _ binding.BinaryEncoder = (*binaryBufferedMessage)(nil)
diff --git a/v1/binding/buffering/copy_message.go b/v1/binding/buffering/copy_message.go
deleted file mode 100644
index d683267af..000000000
--- a/v1/binding/buffering/copy_message.go
+++ /dev/null
@@ -1,51 +0,0 @@
-package buffering
-
-import (
- "context"
-
- "github.com/cloudevents/sdk-go/v1/binding"
-)
-
-// BufferMessage does the same than CopyMessage and it also bounds the original Message
-// lifecycle to the newly created message: calling Finish() on the returned message calls m.Finish()
-func BufferMessage(ctx context.Context, m binding.Message, transformers ...binding.TransformerFactory) (binding.Message, error) {
- result, err := CopyMessage(ctx, m, transformers...)
- if err != nil {
- return nil, err
- }
- return binding.WithFinish(result, func(err error) { _ = m.Finish(err) }), nil
-}
-
-// CopyMessage reads m once and creates an in-memory copy depending on the encoding of m.
-// The returned copy is not dependent on any transport and can be read many times.
-// When the copy can be forgot, the copied message must be finished with Finish() message to release the memory
-func CopyMessage(ctx context.Context, m binding.Message, transformers ...binding.TransformerFactory) (binding.Message, error) {
- originalMessageEncoding := m.Encoding()
-
- if originalMessageEncoding == binding.EncodingUnknown {
- return nil, binding.ErrUnknownEncoding
- }
- if originalMessageEncoding == binding.EncodingEvent {
- e, _, err := binding.ToEvent(ctx, m, transformers...)
- if err != nil {
- return nil, err
- }
- return binding.EventMessage(e), nil
- }
-
- sm := structBufferedMessage{}
- bm := binaryBufferedMessage{}
-
- encoding, err := binding.RunDirectEncoding(ctx, m, &sm, &bm, transformers)
- if encoding == binding.EncodingStructured {
- return &sm, err
- } else if encoding == binding.EncodingBinary {
- return &bm, err
- } else {
- e, _, err := binding.ToEvent(ctx, m, transformers...)
- if err != nil {
- return nil, err
- }
- return binding.EventMessage(e), nil
- }
-}
diff --git a/v1/binding/buffering/copy_message_benchmark_test.go b/v1/binding/buffering/copy_message_benchmark_test.go
deleted file mode 100644
index e9c4bf9e5..000000000
--- a/v1/binding/buffering/copy_message_benchmark_test.go
+++ /dev/null
@@ -1,30 +0,0 @@
-package buffering
-
-import (
- "context"
- "testing"
-
- "github.com/cloudevents/sdk-go/v1/binding/test"
-)
-
-var err error
-
-func BenchmarkBufferMessageFromStructured(b *testing.B) {
- e := test.FullEvent()
- input := test.NewMockStructuredMessage(e)
- ctx := context.Background()
- for i := 0; i < b.N; i++ {
- outputMessage, _ := BufferMessage(ctx, input)
- err = outputMessage.Finish(nil)
- }
-}
-
-func BenchmarkBufferMessageFromBinary(b *testing.B) {
- e := test.FullEvent()
- input := test.NewMockBinaryMessage(e)
- ctx := context.Background()
- for i := 0; i < b.N; i++ {
- outputMessage, _ := BufferMessage(ctx, input)
- err = outputMessage.Finish(nil)
- }
-}
diff --git a/v1/binding/buffering/copy_message_test.go b/v1/binding/buffering/copy_message_test.go
deleted file mode 100644
index 58612d5e6..000000000
--- a/v1/binding/buffering/copy_message_test.go
+++ /dev/null
@@ -1,103 +0,0 @@
-package buffering
-
-import (
- "context"
- "fmt"
- "testing"
-
- "github.com/stretchr/testify/assert"
- "github.com/stretchr/testify/require"
-
- "github.com/cloudevents/sdk-go/v1/binding"
- "github.com/cloudevents/sdk-go/v1/binding/test"
- "github.com/cloudevents/sdk-go/v1/cloudevents"
-)
-
-type copyMessageTestCase struct {
- name string
- encoding binding.Encoding
- message binding.Message
- want cloudevents.Event
-}
-
-func TestCopyMessage(t *testing.T) {
- tests := []copyMessageTestCase{}
-
- for _, v := range test.Events() {
- tests = append(tests, []copyMessageTestCase{
- {
- name: "From structured with payload/" + test.NameOf(v),
- encoding: binding.EncodingStructured,
- message: test.NewMockStructuredMessage(v),
- want: v,
- },
- {
- name: "From structured without payload/" + test.NameOf(v),
- encoding: binding.EncodingStructured,
- message: test.NewMockStructuredMessage(v),
- want: v,
- },
- {
- name: "From binary with payload/" + test.NameOf(v),
- encoding: binding.EncodingBinary,
- message: test.NewMockBinaryMessage(v),
- want: v,
- },
- {
- name: "From binary without payload/" + test.NameOf(v),
- encoding: binding.EncodingBinary,
- message: test.NewMockBinaryMessage(v),
- want: v,
- },
- {
- name: "From event with payload/" + test.NameOf(v),
- encoding: binding.EncodingEvent,
- message: binding.EventMessage(v),
- want: v,
- },
- {
- name: "From event without payload/" + test.NameOf(v),
- encoding: binding.EncodingEvent,
- message: binding.EventMessage(v),
- want: v,
- },
- }...)
- }
- for _, tt := range tests {
- tt := tt // Don't use range variable in Run() scope
- t.Run(fmt.Sprintf("CopyMessage: %s", tt.name), func(t *testing.T) {
- finished := false
- message := binding.WithFinish(tt.message, func(err error) {
- finished = true
- })
- cpy, err := CopyMessage(context.Background(), message)
- require.NoError(t, err)
- // The copy can be read any number of times
- for i := 0; i < 3; i++ {
- got, encoding, err := binding.ToEvent(context.Background(), cpy)
- assert.NoError(t, err)
- require.Equal(t, tt.encoding, encoding)
- test.AssertEventEquals(t, test.ExToStr(t, tt.want), test.ExToStr(t, got))
- }
- require.NoError(t, cpy.Finish(nil))
- require.Equal(t, false, finished)
- })
- t.Run(fmt.Sprintf("BufferMessage: %s", tt.name), func(t *testing.T) {
- finished := false
- message := binding.WithFinish(tt.message, func(err error) {
- finished = true
- })
- cpy, err := BufferMessage(context.Background(), message)
- require.NoError(t, err)
- // The copy can be read any number of times
- for i := 0; i < 3; i++ {
- got, encoding, err := binding.ToEvent(context.Background(), cpy)
- assert.NoError(t, err)
- require.Equal(t, tt.encoding, encoding)
- test.AssertEventEquals(t, test.ExToStr(t, tt.want), test.ExToStr(t, got))
- }
- require.NoError(t, cpy.Finish(nil))
- require.Equal(t, true, finished)
- })
- }
-}
diff --git a/v1/binding/buffering/struct_buffer_message.go b/v1/binding/buffering/struct_buffer_message.go
deleted file mode 100644
index 8058f9acf..000000000
--- a/v1/binding/buffering/struct_buffer_message.go
+++ /dev/null
@@ -1,57 +0,0 @@
-package buffering
-
-import (
- "bytes"
- "context"
- "io"
-
- "github.com/valyala/bytebufferpool"
-
- "github.com/cloudevents/sdk-go/v1/binding"
- "github.com/cloudevents/sdk-go/v1/binding/format"
-)
-
-var structMessagePool bytebufferpool.Pool
-
-// structBufferedMessage implements a structured-mode message as a simple struct.
-// This message implementation is used by CopyMessage and BufferMessage
-type structBufferedMessage struct {
- Format format.Format
- Bytes *bytebufferpool.ByteBuffer
-}
-
-func (m *structBufferedMessage) GetParent() binding.Message {
- return nil
-}
-
-func (m *structBufferedMessage) Encoding() binding.Encoding {
- return binding.EncodingStructured
-}
-
-// Structured copies structured data to a StructuredEncoder
-func (m *structBufferedMessage) Structured(ctx context.Context, enc binding.StructuredEncoder) error {
- return enc.SetStructuredEvent(ctx, m.Format, bytes.NewReader(m.Bytes.B))
-}
-
-// Binary returns ErrNotBinary
-func (m structBufferedMessage) Binary(context.Context, binding.BinaryEncoder) error {
- return binding.ErrNotBinary
-}
-
-func (m *structBufferedMessage) Finish(error) error {
- structMessagePool.Put(m.Bytes)
- return nil
-}
-
-func (m *structBufferedMessage) SetStructuredEvent(ctx context.Context, format format.Format, event io.Reader) error {
- m.Bytes = structMessagePool.Get()
- _, err := io.Copy(m.Bytes, event)
- if err != nil {
- return err
- }
- m.Format = format
- return nil
-}
-
-var _ binding.Message = (*structBufferedMessage)(nil) // Test it conforms to the interface
-var _ binding.StructuredEncoder = (*structBufferedMessage)(nil) // Test it conforms to the interface
diff --git a/v1/binding/chan.go b/v1/binding/chan.go
deleted file mode 100644
index fdda2d69a..000000000
--- a/v1/binding/chan.go
+++ /dev/null
@@ -1,52 +0,0 @@
-package binding
-
-import (
- "context"
- "errors"
- "io"
-)
-
-// ChanSender implements Sender by sending on a channel.
-type ChanSender chan<- Message
-
-// ChanReceiver implements Receiver by receiving from a channel.
-type ChanReceiver <-chan Message
-
-func (s ChanSender) Send(ctx context.Context, m Message) (err error) {
- defer func() {
- err2 := m.Finish(err)
- if err == nil {
- err = err2
- }
- }()
- select {
- case <-ctx.Done():
- return ctx.Err()
- case s <- m:
- return nil
- }
-}
-
-func (s ChanSender) Close(ctx context.Context) (err error) {
- defer func() {
- if recover() != nil {
- err = errors.New("send on closed channel")
- }
- }()
- close(s)
- return nil
-}
-
-func (r ChanReceiver) Receive(ctx context.Context) (Message, error) {
- select {
- case <-ctx.Done():
- return nil, ctx.Err()
- case m, ok := <-r:
- if !ok {
- return nil, io.EOF
- }
- return m, nil
- }
-}
-
-func (r ChanReceiver) Close(ctx context.Context) error { return nil }
diff --git a/v1/binding/doc.go b/v1/binding/doc.go
deleted file mode 100644
index 6038a3ecf..000000000
--- a/v1/binding/doc.go
+++ /dev/null
@@ -1,43 +0,0 @@
-/*
-
-Package binding defines interfaces for protocol bindings.
-
-NOTE: Most applications that emit or consume events should use the ../client
-package, which provides a simpler API to the underlying binding.
-
-The interfaces in this package provide extra encoding and protocol information
-to allow efficient forwarding and end-to-end reliable delivery between a
-Receiver and a Sender belonging to different bindings. This is useful for
-intermediary applications that route or forward events, but not necessary for
-most "endpoint" applications that emit or consume events.
-
-Protocol Bindings
-
-A protocol binding implements at least Message, Sender and Receiver, and usually
-Encoder.
-
-Receiver: receives protocol messages and wraps them to implement the Message interface.
-
-Message: interface that defines the visitors for an encoded event in structured mode,
-binary mode or event mode. A method is provided to read the Encoding of the message
-
-Sender: converts arbitrary Message implementations to a protocol-specific form
-and sends them. A protocol Sender should preserve the spec-version and
-structured/binary mode of sent messages as far as possible. This package
-provides generic Sender wrappers to pre-process messages into a specific
-spec-version or structured/binary mode when the user requires that.
-
-Message and ExactlyOnceMessage provide methods to allow acknowledgments to
-propagate when a reliable messages is forwarded from a Receiver to a Sender.
-QoS 0 (unreliable), 1 (at-least-once) and 2 (exactly-once) are supported.
-
-
-Intermediaries
-
-Intermediaries can forward Messages from a Receiver to a Sender without
-knowledge of the underlying protocols. The Message interface allows structured
-messages to be forwarded without decoding and re-encoding. It also allows any
-Message to be fully decoded and examined as needed.
-
-*/
-package binding
diff --git a/v1/binding/event_message.go b/v1/binding/event_message.go
deleted file mode 100644
index e4118ec29..000000000
--- a/v1/binding/event_message.go
+++ /dev/null
@@ -1,91 +0,0 @@
-package binding
-
-import (
- "bytes"
- "context"
-
- cloudevents "github.com/cloudevents/sdk-go/v1"
- "github.com/cloudevents/sdk-go/v1/binding/format"
- "github.com/cloudevents/sdk-go/v1/binding/spec"
- ce "github.com/cloudevents/sdk-go/v1/cloudevents"
-)
-
-// EventMessage type-converts a cloudevents.Event object to implement Message.
-// This allows local cloudevents.Event objects to be sent directly via Sender.Send()
-// s.Send(ctx, binding.EventMessage(e))
-type EventMessage ce.Event
-
-func (m EventMessage) GetParent() Message {
- return nil
-}
-
-func (m EventMessage) Encoding() Encoding {
- return EncodingEvent
-}
-
-func (m EventMessage) Structured(ctx context.Context, builder StructuredEncoder) error {
- // TODO here only json is supported, should we support other message encodings?
- b, err := format.JSON.Marshal(ce.Event(m))
- if err != nil {
- return err
- }
- return builder.SetStructuredEvent(ctx, format.JSON, bytes.NewReader(b))
-}
-
-func (m EventMessage) Binary(ctx context.Context, b BinaryEncoder) (err error) {
- err = b.Start(ctx)
- if err != nil {
- return err
- }
- err = EventContextToBinaryEncoder(m.Context, b)
- if err != nil {
- return err
- }
- // Pass the body
- body, err := (*ce.Event)(&m).DataBytes()
- if err != nil {
- return err
- }
- if len(body) > 0 {
- err = b.SetData(bytes.NewReader(body))
- if err != nil {
- return err
- }
- }
- return b.End()
-}
-
-func (EventMessage) Finish(error) error { return nil }
-
-func (m *EventMessage) SetEvent(e ce.Event) error {
- *m = EventMessage(e)
- return nil
-}
-
-var _ Message = (*EventMessage)(nil) // Test it conforms to the interface
-
-func EventContextToBinaryEncoder(c cloudevents.EventContext, b BinaryEncoder) (err error) {
- // Pass all attributes
- var sv spec.Version
- sv, err = spec.VS.Version(c.GetSpecVersion())
- if err != nil {
- return err
- }
- for _, a := range sv.Attributes() {
- value := a.Get(c)
- if value != nil {
- err = b.SetAttribute(a, value)
- }
- if err != nil {
- return err
- }
- }
- // Pass all extensions
- for k, v := range c.GetExtensions() {
- err = b.SetExtension(k, v)
- if err != nil {
- return err
- }
- }
- return nil
-}
diff --git a/v1/binding/example_implementing_test.go b/v1/binding/example_implementing_test.go
deleted file mode 100644
index f4b79cc7f..000000000
--- a/v1/binding/example_implementing_test.go
+++ /dev/null
@@ -1,114 +0,0 @@
-package binding_test
-
-import (
- "bytes"
- "context"
- "encoding/json"
- "io"
- "io/ioutil"
-
- "github.com/cloudevents/sdk-go/v1/binding"
- "github.com/cloudevents/sdk-go/v1/binding/format"
- "github.com/cloudevents/sdk-go/v1/cloudevents/transport"
-)
-
-// ExMessage is a json.RawMessage, a byte slice containing a JSON encoded event.
-// It implements binding.MockStructuredMessage
-//
-// Note: a good binding implementation should provide an easy way to convert
-// between the Message implementation and the "native" message format.
-// In this case it's as simple as:
-//
-// native = ExMessage(impl)
-// impl = json.RawMessage(native)
-//
-// For example in a HTTP binding it should be easy to convert between
-// the HTTP binding.Message implementation and net/http.Request and
-// Response types. There are no interfaces for this conversion as it
-// requires the use of unknown types.
-type ExMessage json.RawMessage
-
-func (m ExMessage) GetParent() binding.Message {
- return nil
-}
-
-func (m ExMessage) Encoding() binding.Encoding {
- return binding.EncodingStructured
-}
-
-func (m ExMessage) Structured(ctx context.Context, b binding.StructuredEncoder) error {
- return b.SetStructuredEvent(ctx, format.JSON, bytes.NewReader(m))
-}
-
-func (m ExMessage) Binary(context.Context, binding.BinaryEncoder) error {
- return binding.ErrNotBinary
-}
-
-func (m ExMessage) Finish(error) error { return nil }
-
-var _ binding.Message = (*ExMessage)(nil)
-
-// ExSender sends by writing JSON encoded events to an io.Writer
-// ExSender supports transcoding
-// ExSender implements directly StructuredEncoder & EventEncoder
-type ExSender struct {
- encoder *json.Encoder
- transformers binding.TransformerFactories
-}
-
-func NewExSender(w io.Writer, factories ...binding.TransformerFactory) binding.Sender {
- return &ExSender{encoder: json.NewEncoder(w), transformers: factories}
-}
-
-func (s *ExSender) Send(ctx context.Context, m binding.Message) error {
- // Encode tries the various encodings, starting with provided root encoder factories.
- // If a sender doesn't support a specific encoding, a null root encoder factory could be provided.
- _, err := binding.Encode(
- ctx,
- m,
- s,
- nil,
- s.transformers,
- )
-
- return err
-}
-
-func (s *ExSender) SetStructuredEvent(ctx context.Context, f format.Format, event io.Reader) error {
- if f == format.JSON {
- b, err := ioutil.ReadAll(event)
- if err != nil {
- return err
- }
- return s.encoder.Encode(json.RawMessage(b))
- } else {
- return binding.ErrNotStructured
- }
-}
-
-func (s *ExSender) Close(context.Context) error { return nil }
-
-var _ binding.Sender = (*ExSender)(nil)
-var _ binding.StructuredEncoder = (*ExSender)(nil)
-
-// ExReceiver receives by reading JSON encoded events from an io.Reader
-type ExReceiver struct{ decoder *json.Decoder }
-
-func NewExReceiver(r io.Reader) binding.Receiver { return &ExReceiver{json.NewDecoder(r)} }
-
-func (r *ExReceiver) Receive(context.Context) (binding.Message, error) {
- var rm json.RawMessage
- err := r.decoder.Decode(&rm) // This is just a byte copy.
- return ExMessage(rm), err
-}
-func (r *ExReceiver) Close(context.Context) error { return nil }
-
-// NewExTransport returns a transport.Transport which is implemented by
-// an ExSender and an ExReceiver
-func NewExTransport(r io.Reader, w io.Writer) transport.Transport {
- return binding.NewTransportAdapter(NewExSender(w), NewExReceiver(r), []func(ctx context.Context) context.Context{})
-}
-
-// Example of implementing a transport including a simple message type,
-// and a transport sender and receiver.
-func Example_implementing() {}
diff --git a/v1/binding/example_using_test.go b/v1/binding/example_using_test.go
deleted file mode 100644
index 24281386b..000000000
--- a/v1/binding/example_using_test.go
+++ /dev/null
@@ -1,126 +0,0 @@
-package binding_test
-
-import (
- "context"
- "fmt"
- "io"
- "strconv"
-
- "github.com/cloudevents/sdk-go/v1/cloudevents"
- "github.com/cloudevents/sdk-go/v1/cloudevents/client"
-)
-
-const count = 3 // Example ends after this many events.
-
-// The sender uses the cloudevents.Client API, not the transport APIs directly.
-func runSender(w io.Writer) error {
- c, err := client.New(NewExTransport(nil, w), client.WithoutTracePropagation())
- if err != nil {
- return err
- }
- for i := 0; i < count; i++ {
- e := cloudevents.New()
- e.SetType("example.com/event")
- e.SetSource("example.com/source")
- e.SetID(strconv.Itoa(i))
- if err := e.SetData(fmt.Sprintf("hello %d", i)); err != nil {
- return err
- }
- if _, _, err := c.Send(context.TODO(), e); err != nil {
- return err
- }
- }
- return nil
-}
-
-// The receiver uses the cloudevents.Client API, not the transport APIs directly.
-func runReceiver(r io.Reader) error {
- i := 0
- process := func(e cloudevents.Event) error {
- fmt.Printf("%s\n", e)
- i++
- if i == count {
- return io.EOF
- }
- return nil
- }
- c, err := client.New(NewExTransport(r, nil), client.WithoutTracePropagation())
- if err != nil {
- return err
- }
- return c.StartReceiver(context.TODO(), process)
-}
-
-// The intermediary receives events and forwards them to another
-// process using ExReceiver and ExSender directly.
-//
-// By forwarding a transport.Message instead of a cloudevents.Event,
-// it allows the transports to avoid un-necessary decoding of
-// structured events, and to exchange delivery status between reliable
-// transports. Even transports using different protocols can ensure
-// reliable delivery.
-//
-func runIntermediary(r io.Reader, w io.WriteCloser) error {
- defer w.Close()
- for {
- receiver := NewExReceiver(r)
- sender := NewExSender(w)
- for i := 0; i < count; i++ {
- if m, err := receiver.Receive(context.TODO()); err != nil {
- return err
- } else if err := sender.Send(context.TODO(), m); err != nil {
- return err
- }
- }
- }
-}
-
-// This example shows how to use a transport in sender, receiver,
-// and intermediary processes.
-//
-// The sender and receiver use the client.Client API to send and
-// receive messages. the transport. Only the intermediary example
-// actually uses the transport APIs for efficiency and reliability in
-// forwarding events.
-func Example_using() {
- r1, w1 := io.Pipe() // The sender-to-intermediary pipe
- r2, w2 := io.Pipe() // The intermediary-to-receiver pipe
-
- done := make(chan error)
- go func() { done <- runReceiver(r2) }()
- go func() { done <- runIntermediary(r1, w2) }()
- go func() { done <- runSender(w1) }()
- for i := 0; i < 2; i++ {
- if err := <-done; err != nil && err != io.EOF {
- fmt.Println(err)
- }
- }
-
- // Output:
- // Validation: valid
- // Context Attributes,
- // specversion: 1.0
- // type: example.com/event
- // source: example.com/source
- // id: 0
- // Data,
- // "hello 0"
- //
- // Validation: valid
- // Context Attributes,
- // specversion: 1.0
- // type: example.com/event
- // source: example.com/source
- // id: 1
- // Data,
- // "hello 1"
- //
- // Validation: valid
- // Context Attributes,
- // specversion: 1.0
- // type: example.com/event
- // source: example.com/source
- // id: 2
- // Data,
- // "hello 2"
-}
diff --git a/v1/binding/finish_message.go b/v1/binding/finish_message.go
deleted file mode 100644
index 3c4efc5c0..000000000
--- a/v1/binding/finish_message.go
+++ /dev/null
@@ -1,27 +0,0 @@
-package binding
-
-type finishMessage struct {
- Message
- finish func(error)
-}
-
-func (m *finishMessage) GetWrappedMessage() Message {
- return m.Message
-}
-
-func (m *finishMessage) Finish(err error) error {
- err2 := m.Message.Finish(err) // Finish original message first
- if m.finish != nil {
- m.finish(err) // Notify callback
- }
- return err2
-}
-
-var _ MessageWrapper = (*finishMessage)(nil)
-
-// WithFinish returns a wrapper for m that calls finish() and
-// m.Finish() in its Finish().
-// Allows code to be notified when a message is Finished.
-func WithFinish(m Message, finish func(error)) Message {
- return &finishMessage{Message: m, finish: finish}
-}
diff --git a/v1/binding/finish_message_test.go b/v1/binding/finish_message_test.go
deleted file mode 100644
index df294282a..000000000
--- a/v1/binding/finish_message_test.go
+++ /dev/null
@@ -1,38 +0,0 @@
-package binding_test
-
-import (
- "context"
- "net/url"
- "testing"
-
- "github.com/stretchr/testify/assert"
-
- cloudevents "github.com/cloudevents/sdk-go/v1"
- "github.com/cloudevents/sdk-go/v1/binding"
- "github.com/cloudevents/sdk-go/v1/cloudevents/types"
-)
-
-func TestWithFinish(t *testing.T) {
- var testEvent = cloudevents.Event{
- Data: []byte(`"data"`),
- DataEncoded: true,
- Context: cloudevents.EventContextV1{
- DataContentType: cloudevents.StringOfApplicationJSON(),
- Source: types.URIRef{URL: url.URL{Path: "source"}},
- ID: "id",
- Type: "type"}.AsV1(),
- }
-
- done := make(chan error, 1)
- m := binding.WithFinish(binding.EventMessage(testEvent), func(err error) {
- done <- err
- })
- select {
- case <-done:
- assert.Fail(t, "done early")
- default:
- }
- ch := make(chan binding.Message, 1)
- assert.NoError(t, binding.ChanSender(ch).Send(context.Background(), m))
- assert.NoError(t, <-done)
-}
diff --git a/v1/binding/format/format.go b/v1/binding/format/format.go
deleted file mode 100644
index 4070acdcc..000000000
--- a/v1/binding/format/format.go
+++ /dev/null
@@ -1,103 +0,0 @@
-/*
-Package format formats structured events.
-
-The "application/cloudevents+json" format is built-in and always
-available. Other formats may be added.
-*/
-package format
-
-import (
- "encoding/json"
- "fmt"
- "strings"
-
- ce "github.com/cloudevents/sdk-go/v1/cloudevents"
-)
-
-// Format marshals and unmarshals structured events to bytes.
-type Format interface {
- // MediaType identifies the format
- MediaType() string
- // Marshal event to bytes
- Marshal(ce.Event) ([]byte, error)
- // Unmarshal bytes to event
- Unmarshal([]byte, *ce.Event) error
-}
-
-// UnknownFormat allows an event with an unknown format string to be forwarded,
-// but Marshal() and Unmarshal will always fail.
-type UnknownFormat string
-
-func (uf UnknownFormat) MediaType() string { return string(uf) }
-func (uf UnknownFormat) Marshal(ce.Event) ([]byte, error) { return nil, unknown(uf.MediaType()) }
-func (uf UnknownFormat) Unmarshal([]byte, *ce.Event) error { return unknown(uf.MediaType()) }
-
-// Prefix for event-format media types.
-const Prefix = "application/cloudevents"
-
-// IsFormat returns true if mediaType begins with "application/cloudevents"
-func IsFormat(mediaType string) bool { return strings.HasPrefix(mediaType, Prefix) }
-
-// JSON is the built-in "application/cloudevents+json" format.
-var JSON = jsonFmt{}
-
-type jsonFmt struct{}
-
-func (jsonFmt) MediaType() string { return ce.ApplicationCloudEventsJSON }
-
-func (jsonFmt) Marshal(e ce.Event) ([]byte, error) { return json.Marshal(e) }
-func (jsonFmt) Unmarshal(b []byte, e *ce.Event) error {
- err := json.Unmarshal(b, e)
- if err != nil {
- return err
- }
-
- // Extensions to go types when unparsed
- for k, v := range e.Extensions() {
- var vParsed interface{}
- switch v.(type) {
- case json.RawMessage:
- err = json.Unmarshal(v.(json.RawMessage), &vParsed)
- if err != nil {
- return err
- }
- e.SetExtension(k, vParsed)
- }
- }
-
- return nil
-}
-
-// built-in formats
-var formats map[string]Format
-
-func init() {
- formats = map[string]Format{}
- Add(JSON)
-}
-
-// Lookup returns the format for mediaType, or nil if not found.
-func Lookup(mediaType string) Format { return formats[mediaType] }
-
-func unknown(mediaType string) error {
- return fmt.Errorf("unknown event format media-type %#v", mediaType)
-}
-
-// Add a new Format. It can be retrieved by Lookup(f.MediaType())
-func Add(f Format) { formats[f.MediaType()] = f }
-
-// Marshal an event to bytes using the mediaType event format.
-func Marshal(mediaType string, e ce.Event) ([]byte, error) {
- if f := formats[mediaType]; f != nil {
- return f.Marshal(e)
- }
- return nil, unknown(mediaType)
-}
-
-// Unmarshal bytes to an event using the mediaType event format.
-func Unmarshal(mediaType string, b []byte, e *ce.Event) error {
- if f := formats[mediaType]; f != nil {
- return f.Unmarshal(b, e)
- }
- return unknown(mediaType)
-}
diff --git a/v1/binding/format/format_test.go b/v1/binding/format/format_test.go
deleted file mode 100644
index fb4632e80..000000000
--- a/v1/binding/format/format_test.go
+++ /dev/null
@@ -1,84 +0,0 @@
-package format_test
-
-import (
- "testing"
-
- "github.com/stretchr/testify/assert"
-
- "github.com/cloudevents/sdk-go/v1/binding/format"
- ce "github.com/cloudevents/sdk-go/v1/cloudevents"
- "github.com/cloudevents/sdk-go/v1/cloudevents/types"
-)
-
-func TestJSON(t *testing.T) {
- assert := assert.New(t)
- e := ce.Event{
- Context: ce.EventContextV03{
- Type: "type",
- ID: "id",
- Source: *types.ParseURLRef("source"),
- }.AsV03(),
- }
- e.SetExtension("ex", "val")
- assert.NoError(e.SetData("foo"))
- b, err := format.JSON.Marshal(e)
- assert.NoError(err)
- assert.Equal(`{"data":"foo","ex":"val","id":"id","source":"source","specversion":"0.3","type":"type"}`, string(b))
-
- var e2 ce.Event
- assert.NoError(format.JSON.Unmarshal(b, &e2))
- assert.Equal(e, e2)
-}
-
-func TestLookup(t *testing.T) {
- assert := assert.New(t)
- assert.Nil(format.Lookup("nosuch"))
-
- f := format.Lookup(ce.ApplicationCloudEventsJSON)
- assert.Equal(f.MediaType(), ce.ApplicationCloudEventsJSON)
- assert.Equal(format.JSON, f)
-}
-
-func TestMarshalUnmarshal(t *testing.T) {
- assert := assert.New(t)
- e := ce.Event{
- Context: ce.EventContextV03{
- Type: "type",
- ID: "id",
- Source: *types.ParseURLRef("source"),
- }.AsV03(),
- }
- assert.NoError(e.SetData("foo"))
- b, err := format.Marshal(format.JSON.MediaType(), e)
- assert.NoError(err)
- assert.Equal(`{"data":"foo","id":"id","source":"source","specversion":"0.3","type":"type"}`, string(b))
-
- var e2 ce.Event
- assert.NoError(format.Unmarshal(format.JSON.MediaType(), b, &e2))
- assert.Equal(e, e2)
-
- _, err = format.Marshal("nosuchformat", e)
- assert.EqualError(err, "unknown event format media-type \"nosuchformat\"")
- err = format.Unmarshal("nosuchformat", nil, &e)
- assert.EqualError(err, "unknown event format media-type \"nosuchformat\"")
-}
-
-type dummyFormat struct{}
-
-func (dummyFormat) MediaType() string { return "dummy" }
-func (dummyFormat) Marshal(ce.Event) ([]byte, error) { return []byte("dummy!"), nil }
-func (dummyFormat) Unmarshal(b []byte, e *ce.Event) error { e.Data = "undummy!"; return nil }
-
-func TestAdd(t *testing.T) {
- assert := assert.New(t)
- format.Add(dummyFormat{})
- assert.Equal(dummyFormat{}, format.Lookup("dummy"))
-
- e := ce.Event{}
- b, err := format.Marshal("dummy", e)
- assert.NoError(err)
- assert.Equal("dummy!", string(b))
- err = format.Unmarshal("dummy", b, &e)
- assert.NoError(err)
- assert.Equal("undummy!", e.Data)
-}
diff --git a/v1/binding/interfaces.go b/v1/binding/interfaces.go
deleted file mode 100644
index 5b9f4088c..000000000
--- a/v1/binding/interfaces.go
+++ /dev/null
@@ -1,214 +0,0 @@
-package binding
-
-import (
- "context"
- "errors"
- "io"
-
- "github.com/cloudevents/sdk-go/v1/binding/format"
- "github.com/cloudevents/sdk-go/v1/binding/spec"
-)
-
-// Encoding enum specifies the type of encodings supported by binding interfaces
-type Encoding int
-
-const (
- // Binary encoding as specified in https://github.com/cloudevents/spec/blob/master/spec.md#message
- EncodingBinary Encoding = iota
- // Structured encoding as specified in https://github.com/cloudevents/spec/blob/master/spec.md#message
- EncodingStructured
- // Message is an instance of EventMessage or it contains it nested (through MessageWrapper)
- EncodingEvent
- // When the encoding is unknown (which means that the message is a non-event)
- EncodingUnknown
-)
-
-// Error to specify that or the Message is not an event or it is encoded with an unknown encoding
-var ErrUnknownEncoding = errors.New("unknown Message encoding")
-
-// Message is the interface to a binding-specific message containing an event.
-//
-// Reliable Delivery
-//
-// There are 3 reliable qualities of service for messages:
-//
-// 0/at-most-once/unreliable: messages can be dropped silently.
-//
-// 1/at-least-once: messages are not dropped without signaling an error
-// to the sender, but they may be duplicated in the event of a re-send.
-//
-// 2/exactly-once: messages are never dropped (without error) or
-// duplicated, as long as both sending and receiving ends maintain
-// some binding-specific delivery state. Whether this is persisted
-// depends on the configuration of the binding implementations.
-//
-// The Message interface supports QoS 0 and 1, the ExactlyOnceMessage interface
-// supports QoS 2
-//
-// The Structured and Binary methods provide optional optimized transfer of an event
-// to a Sender, they may not be implemented by all Message instances. A Sender should
-// try each method of interest and fall back to ToEvent() if none are supported.
-//
-type Message interface {
- // Return the type of the message Encoding.
- // The encoding should be preferably computed when the message is constructed.
- Encoding() Encoding
-
- // Structured transfers a structured-mode event to a StructuredEncoder.
- // Returns ErrNotStructured if message is not in structured mode.
- //
- // Returns a different err if something wrong happened while trying to read the structured event
- // In this case, the caller must Finish the message with appropriate error
- //
- // This allows Senders to avoid re-encoding messages that are
- // already in suitable structured form.
- Structured(context.Context, StructuredEncoder) error
-
- // Binary transfers a binary-mode event to an BinaryEncoder.
- // Returns ErrNotBinary if message is not in binary mode.
- //
- // Returns a different err if something wrong happened while trying to read the binary event
- // In this case, the caller must Finish the message with appropriate error
- //
- // Allows Senders to forward a binary message without allocating an
- // intermediate Event.
- Binary(context.Context, BinaryEncoder) error
-
- // Finish *must* be called when message from a Receiver can be forgotten by
- // the receiver. Sender.Send() calls Finish() when the message is sent. A QoS
- // 1 sender should not call Finish() until it gets an acknowledgment of
- // receipt on the underlying transport. For QoS 2 see ExactlyOnceMessage.
- //
- // Passing a non-nil err indicates sending or processing failed.
- // A non-nil return indicates that the message was not accepted
- // by the receivers peer.
- Finish(error) error
-}
-
-// ErrNotStructured returned by Message.Structured for non-structured messages.
-var ErrNotStructured = errors.New("message is not in structured mode")
-
-// ErrNotBinary returned by Message.Binary for non-binary messages.
-var ErrNotBinary = errors.New("message is not in binary mode")
-
-// StructuredEncoder is used to visit a structured Message and generate a new representation.
-//
-// Protocols that supports structured encoding should implement this interface to implement direct
-// structured -> structured transfer and event -> binary.
-type StructuredEncoder interface {
- // Event receives an io.Reader for the whole event.
- SetStructuredEvent(ctx context.Context, format format.Format, event io.Reader) error
-}
-
-// BinaryEncoder is used to visit a binary Message and generate a new representation.
-//
-// Protocols that supports binary encoding should implement this interface to implement direct
-// binary -> binary transfer and event -> binary.
-//
-// Start() and End() methods are invoked every time this BinaryEncoder implementation is used to visit a Message
-type BinaryEncoder interface {
- // Method invoked at the beginning of the visit. Useful to perform initial memory allocations
- Start(ctx context.Context) error
-
- // Set a standard attribute.
- //
- // The value can either be the correct golang type for the attribute, or a canonical
- // string encoding. See package cloudevents/types
- SetAttribute(attribute spec.Attribute, value interface{}) error
-
- // Set an extension attribute.
- //
- // The value can either be the correct golang type for the attribute, or a canonical
- // string encoding. See package cloudevents/types
- SetExtension(name string, value interface{}) error
-
- // SetData receives an io.Reader for the data attribute.
- // io.Reader could be empty, meaning that message payload is empty
- SetData(data io.Reader) error
-
- // End method is invoked only after the whole encoding process ends successfully.
- // If it fails, it's never invoked. It can be used to finalize the message.
- End() error
-}
-
-// ExactlyOnceMessage is implemented by received Messages
-// that support QoS 2. Only transports that support QoS 2 need to
-// implement or use this interface.
-type ExactlyOnceMessage interface {
- Message
-
- // Received is called by a forwarding QoS2 Sender when it gets
- // acknowledgment of receipt (e.g. AMQP 'accept' or MQTT PUBREC)
- //
- // The receiver must call settle(nil) when it get's the ack-of-ack
- // (e.g. AMQP 'settle' or MQTT PUBCOMP) or settle(err) if the
- // transfer fails.
- //
- // Finally the Sender calls Finish() to indicate the message can be
- // discarded.
- //
- // If sending fails, or if the sender does not support QoS 2, then
- // Finish() may be called without any call to Received()
- Received(settle func(error))
-}
-
-// Message Wrapper interface is used to walk through a decorated Message and unwrap it.
-type MessageWrapper interface {
- Message
-
- // Method to get the wrapped message
- GetWrappedMessage() Message
-}
-
-// Receiver receives messages.
-type Receiver interface {
- // Receive blocks till a message is received or ctx expires.
- //
- // A non-nil error means the receiver is closed.
- // io.EOF means it closed cleanly, any other value indicates an error.
- Receive(ctx context.Context) (Message, error)
-}
-
-// Sender sends messages.
-type Sender interface {
- // Send a message.
- //
- // Send returns when the "outbound" message has been sent. The Sender may
- // still be expecting acknowledgment or holding other state for the message.
- //
- // m.Finish() is called when sending is finished: expected acknowledgments (or
- // errors) have been received, the Sender is no longer holding any state for
- // the message. m.Finish() may be called during or after Send().
- //
- // To support optimized forwading of structured-mode messages, Send()
- // should use the encoding returned by m.Structured() if there is one.
- // Otherwise m.Event() can be encoded as per the binding's rules.
- Send(ctx context.Context, m Message) error
-}
-
-// Requester sends a message and receives a response
-//
-// Optional interface that may be implemented by protocols that support
-// request/response correlation.
-type Requester interface {
- // Request sends m like Sender.Send() but also arranges to receive a response.
- // The returned Receiver is used to receive the response.
- Request(ctx context.Context, m Message) (Receiver, error)
-}
-
-// Closer is the common interface for things that can be closed
-type Closer interface {
- Close(ctx context.Context) error
-}
-
-// ReceiveCloser is a Receiver that can be closed.
-type ReceiveCloser interface {
- Receiver
- Closer
-}
-
-// SendCloser is a Sender that can be closed.
-type SendCloser interface {
- Sender
- Closer
-}
diff --git a/v1/binding/spec/attributes.go b/v1/binding/spec/attributes.go
deleted file mode 100644
index 511a131af..000000000
--- a/v1/binding/spec/attributes.go
+++ /dev/null
@@ -1,133 +0,0 @@
-package spec
-
-import (
- "fmt"
- "time"
-
- ce "github.com/cloudevents/sdk-go/v1/cloudevents"
- "github.com/cloudevents/sdk-go/v1/cloudevents/types"
-)
-
-// Kind is a version-independent identifier for a CloudEvent context attribute.
-type Kind uint8
-
-const (
- // Required cloudevents attributes
- ID Kind = iota
- Source
- SpecVersion
- Type
- // Optional cloudevents attributes
- DataContentType
- DataSchema
- Subject
- Time
-)
-const nAttrs = int(Time) + 1
-
-var kindNames = [nAttrs]string{
- "id",
- "source",
- "specversion",
- "type",
- "datacontenttype",
- "dataschema",
- "subject",
- "time",
-}
-
-// String is a human-readable string, for a valid attribute name use Attribute.Name
-func (k Kind) String() string { return kindNames[k] }
-
-// IsRequired returns true for attributes defined as "required" by the CE spec.
-func (k Kind) IsRequired() bool { return k < DataContentType }
-
-// Attribute is a named attribute accessor.
-// The attribute name is specific to a Version.
-type Attribute interface {
- Kind() Kind
- // Name of the attribute with respect to the current spec Version()
- Name() string
- // Version of the spec that this attribute belongs to
- Version() Version
- // Get the value of this attribute from an event context
- Get(ce.EventContextReader) interface{}
- // Set the value of this attribute on an event context
- Set(ce.EventContextWriter, interface{}) error
- // Delete this attribute from and event context, when possible
- Delete(ce.EventContextWriter) error
-}
-
-// accessor provides Kind, Get, Set.
-type accessor interface {
- Kind() Kind
- Get(ce.EventContextReader) interface{}
- Set(ce.EventContextWriter, interface{}) error
- Delete(ce.EventContextWriter) error
-}
-
-var acc = [nAttrs]accessor{
- &aStr{aKind(ID), ce.EventContextReader.GetID, ce.EventContextWriter.SetID},
- &aStr{aKind(Source), ce.EventContextReader.GetSource, ce.EventContextWriter.SetSource},
- &aStr{aKind(SpecVersion), ce.EventContextReader.GetSpecVersion, ce.EventContextWriter.SetSpecVersion},
- &aStr{aKind(Type), ce.EventContextReader.GetType, ce.EventContextWriter.SetType},
- &aStr{aKind(DataContentType), ce.EventContextReader.GetDataContentType, ce.EventContextWriter.SetDataContentType},
- &aStr{aKind(DataSchema), ce.EventContextReader.GetDataSchema, ce.EventContextWriter.SetDataSchema},
- &aStr{aKind(Subject), ce.EventContextReader.GetSubject, ce.EventContextWriter.SetSubject},
- &aTime{aKind(Time), ce.EventContextReader.GetTime, ce.EventContextWriter.SetTime},
-}
-
-// aKind implements Kind()
-type aKind Kind
-
-func (kind aKind) Kind() Kind { return Kind(kind) }
-
-type aStr struct {
- aKind
- get func(ce.EventContextReader) string
- set func(ce.EventContextWriter, string) error
-}
-
-func (a *aStr) Get(c ce.EventContextReader) interface{} {
- if s := a.get(c); s != "" {
- return s
- }
- return nil // Treat blank as missing
-}
-
-func (a *aStr) Set(c ce.EventContextWriter, v interface{}) error {
- s, err := types.ToString(v)
- if err != nil {
- return fmt.Errorf("invalid value for %s: %#v", a.Kind(), v)
- }
- return a.set(c, s)
-}
-
-func (a *aStr) Delete(c ce.EventContextWriter) error {
- return a.set(c, "")
-}
-
-type aTime struct {
- aKind
- get func(ce.EventContextReader) time.Time
- set func(ce.EventContextWriter, time.Time) error
-}
-
-func (a *aTime) Get(c ce.EventContextReader) interface{} {
- if v := a.get(c); !v.IsZero() {
- return v
- }
- return nil // Treat zero time as missing.
-}
-
-func (a *aTime) Set(c ce.EventContextWriter, v interface{}) error {
- t, err := types.ToTime(v)
- if err != nil {
- return fmt.Errorf("invalid value for %s: %#v", a.Kind(), v)
- }
- return a.set(c, t)
-}
-
-func (a *aTime) Delete(c ce.EventContextWriter) error {
- return a.set(c, time.Time{})
-}
diff --git a/v1/binding/spec/attributes_test.go b/v1/binding/spec/attributes_test.go
deleted file mode 100644
index 05d79b369..000000000
--- a/v1/binding/spec/attributes_test.go
+++ /dev/null
@@ -1,93 +0,0 @@
-package spec_test
-
-import (
- "testing"
- "time"
-
- "github.com/cloudevents/sdk-go/v1/binding/spec"
- "github.com/google/go-cmp/cmp"
- "github.com/stretchr/testify/assert"
-)
-
-func TestAttributes03(t *testing.T) {
- assert := assert.New(t)
- v, err := spec.WithPrefix("x:").Version("0.3")
- assert.NoError(err)
- subject := v.Attribute("x:subject")
- assert.Equal(spec.Subject, subject.Kind())
- assert.Equal("x:subject", subject.Name())
-
- c := v.NewContext()
- assert.Equal("0.3", c.GetSpecVersion())
- assert.NoError(subject.Set(c, "foobar"))
- got := subject.Get(c)
- assert.Equal("foobar", got)
- assert.Equal("foobar", c.GetSubject())
-
- now := time.Now()
- atime := v.Attribute("x:time")
- assert.Equal(spec.Time, atime.Kind())
- assert.NoError(atime.Set(c, now))
- tm := atime.Get(c)
- assert.Empty(cmp.Diff(now, tm))
- assert.Empty(cmp.Diff(now, c.GetTime()))
-
- nosuch := v.Attribute("nosuch")
- assert.Nil(nosuch)
-
- err = subject.Set(c, 1)
- assert.EqualError(err, "invalid value for subject: 1")
- err = atime.Set(c, "foo")
- assert.EqualError(err, `invalid value for time: "foo"`)
-}
-
-func TestAttributes02(t *testing.T) {
- assert := assert.New(t)
- v, err := spec.WithPrefix("x:").Version("0.2")
- assert.NoError(err)
- c := v.NewContext()
- id := v.Attribute("x:id")
- assert.NoError(id.Set(c, "foobar"))
- s := id.Get(c)
- assert.Equal("foobar", s)
- assert.Equal("foobar", c.GetID())
-
- now := time.Now()
- atime := v.Attribute("x:time")
- assert.Equal(spec.Time, atime.Kind())
- assert.NoError(atime.Set(c, now))
- tm := atime.Get(c)
- assert.Empty(cmp.Diff(now, tm))
- assert.Empty(cmp.Diff(now, c.GetTime()))
-
- nosuch := v.Attribute("nosuch")
- assert.Nil(nosuch)
-
- err = id.Set(c, 1)
- assert.EqualError(err, "invalid value for id: 1")
- err = atime.Set(c, "foo")
- assert.EqualError(err, "invalid value for time: \"foo\"")
-}
-
-func TestAttributes01(t *testing.T) {
- assert := assert.New(t)
- v, err := spec.WithPrefix("x:").Version("0.1")
- assert.NoError(err)
- c := v.NewContext()
- contentType := v.Attribute("x:contentType")
- assert.Equal(spec.DataContentType, contentType.Kind())
- assert.NoError(contentType.Set(c, "foobar"))
- s := contentType.Get(c)
- assert.Equal("foobar", s)
- assert.Equal("foobar", c.GetDataContentType())
-
- nosuch := v.Attribute("x:subject")
- assert.Nil(nosuch)
-}
-
-func TestAttributesBadVersions(t *testing.T) {
- assert := assert.New(t)
- v, err := spec.WithPrefix("x:").Version("0.x")
- assert.Nil(v)
- assert.EqualError(err, `invalid spec version "0.x"`)
-}
diff --git a/v1/binding/spec/doc.go b/v1/binding/spec/doc.go
deleted file mode 100644
index 38d6fddf9..000000000
--- a/v1/binding/spec/doc.go
+++ /dev/null
@@ -1,8 +0,0 @@
-/*
-Package spec provides spec-version metadata.
-
-For use by code that maps events using (prefixed) attribute name strings.
-Supports handling multiple spec versions uniformly.
-
-*/
-package spec
diff --git a/v1/binding/spec/spec.go b/v1/binding/spec/spec.go
deleted file mode 100644
index 53663af03..000000000
--- a/v1/binding/spec/spec.go
+++ /dev/null
@@ -1,233 +0,0 @@
-package spec
-
-import (
- "fmt"
- "strings"
-
- ce "github.com/cloudevents/sdk-go/v1/cloudevents"
- "github.com/cloudevents/sdk-go/v1/cloudevents/types"
-)
-
-// Version provides meta-data for a single spec-version.
-type Version interface {
- // String name of the version, e.g. "1.0"
- String() string
- // Prefix for attribute names.
- Prefix() string
- // Attribute looks up a prefixed attribute name (case insensitive).
- // Returns nil if not found.
- Attribute(name string) Attribute
- // Attributes returns all the context attributes for this version.
- Attributes() []Attribute
- // NewContext returns a new context for this version.
- NewContext() ce.EventContext
- // Convert translates a context to this version.
- Convert(ce.EventContextConverter) ce.EventContext
- // SetAttribute sets named attribute to value.
- //
- // Name is case insensitive.
- // Does nothing if name does not start with prefix.
- SetAttribute(context ce.EventContextWriter, name string, value interface{}) error
- // Attribute looks up the attribute from kind.
- // Returns nil if not found.
- AttributeFromKind(kind Kind) Attribute
-}
-
-// Versions contains all known versions with the same attribute prefix.
-type Versions struct {
- prefix string
- all []Version
- m map[string]Version
- svnames []string
-}
-
-// Versions returns the list of all known versions, most recent first.
-func (vs *Versions) Versions() []Version { return vs.all }
-
-// Version returns the named version.
-func (vs *Versions) Version(name string) (Version, error) {
- if v := vs.m[name]; v != nil {
- return v, nil
- }
- return nil, fmt.Errorf("invalid spec version %#v", name)
-}
-
-// Latest returns the latest Version
-func (vs *Versions) Latest() Version { return vs.all[0] }
-
-// SpecVersionNames returns distinct names of the specversion
-// attribute used in all versions, newest first.
-// Names are prefixed.
-func (vs *Versions) SpecVersionNames() []string { return vs.svnames }
-
-// Prefix is the lowercase attribute name prefix.
-func (vs *Versions) Prefix() string { return vs.prefix }
-
-// FindVersion calls getAttr with known (prefixed) spec-version attribute names
-// till it finds a valid version.
-func (vs *Versions) FindVersion(getAttr func(string) string) (Version, error) {
- for _, sv := range vs.svnames {
- if v, err := vs.Version(getAttr(sv)); err == nil {
- return v, nil
- }
- }
- return nil, fmt.Errorf("CloudEvents spec-version not found")
-}
-
-type attribute struct {
- accessor
- name string
- version Version
-}
-
-func (a *attribute) Name() string { return a.name }
-func (a *attribute) Version() Version { return a.version }
-
-type version struct {
- prefix string
- context ce.EventContext
- convert func(ce.EventContextConverter) ce.EventContext
- attrMap map[string]Attribute
- attrs []Attribute
-}
-
-func (v *version) Attribute(name string) Attribute { return v.attrMap[strings.ToLower(name)] }
-func (v *version) Attributes() []Attribute { return v.attrs }
-func (v *version) String() string { return v.context.GetSpecVersion() }
-func (v *version) Prefix() string { return v.prefix }
-func (v *version) NewContext() ce.EventContext { return v.context.Clone() }
-
-// HasPrefix is a case-insensitive prefix check.
-func (v *version) HasPrefix(name string) bool {
- return strings.HasPrefix(strings.ToLower(name), v.prefix)
-}
-
-func (v *version) Convert(c ce.EventContextConverter) ce.EventContext { return v.convert(c) }
-
-func (v *version) SetAttribute(c ce.EventContextWriter, name string, value interface{}) error {
- if a := v.Attribute(name); a != nil { // Standard attribute
- return a.Set(c, value)
- }
- name = strings.ToLower(name)
- var err error
- if strings.HasPrefix(name, v.prefix) { // Extension attribute
- value, err = types.Validate(value)
- if err == nil {
- err = c.SetExtension(strings.TrimPrefix(name, v.prefix), value)
- }
- }
- return err
-}
-
-func (v *version) AttributeFromKind(kind Kind) Attribute {
- for _, a := range v.Attributes() {
- if a.Kind() == kind {
- return a
- }
- }
- return nil
-}
-
-func newVersion(
- prefix string,
- context ce.EventContext,
- convert func(ce.EventContextConverter) ce.EventContext,
- attrs ...*attribute,
-) *version {
- v := &version{
- prefix: strings.ToLower(prefix),
- context: context,
- convert: convert,
- attrMap: map[string]Attribute{},
- attrs: make([]Attribute, len(attrs)),
- }
- for i, a := range attrs {
- a.name = prefix + a.name
- a.version = v
- v.attrs[i] = a
- v.attrMap[strings.ToLower(a.name)] = a
- }
- return v
-}
-
-// WithPrefix returns a set of versions with prefix added to all attribute names.
-func WithPrefix(prefix string) *Versions {
- attr := func(name string, kind Kind) *attribute {
- return &attribute{accessor: acc[kind], name: name}
- }
- vs := &Versions{
- m: map[string]Version{},
- svnames: []string{
- prefix + "specversion",
- prefix + "cloudEventsVersion",
- },
- all: []Version{
- newVersion(prefix, ce.EventContextV1{}.AsV1(),
- func(c ce.EventContextConverter) ce.EventContext { return c.AsV1() },
- attr("id", ID),
- attr("source", Source),
- attr("specversion", SpecVersion),
- attr("type", Type),
- attr("datacontenttype", DataContentType),
- attr("dataschema", DataSchema),
- attr("subject", Subject),
- attr("time", Time),
- ),
- newVersion(prefix, ce.EventContextV03{}.AsV03(),
- func(c ce.EventContextConverter) ce.EventContext { return c.AsV03() },
- attr("specversion", SpecVersion),
- attr("type", Type),
- attr("source", Source),
- attr("schemaurl", DataSchema),
- attr("subject", Subject),
- attr("id", ID),
- attr("time", Time),
- attr("datacontenttype", DataContentType),
- ),
- newVersion(prefix, ce.EventContextV02{}.AsV02(),
- func(c ce.EventContextConverter) ce.EventContext { return c.AsV02() },
- attr("specversion", SpecVersion),
- attr("type", Type),
- attr("source", Source),
- attr("schemaurl", DataSchema),
- attr("id", ID),
- attr("time", Time),
- attr("contenttype", DataContentType),
- ),
- newVersion(prefix, ce.EventContextV01{}.AsV01(),
- func(c ce.EventContextConverter) ce.EventContext { return c.AsV01() },
- attr("cloudEventsVersion", SpecVersion),
- attr("eventType", Type),
- attr("source", Source),
- attr("schemaURL", DataSchema),
- attr("eventID", ID),
- attr("eventTime", Time),
- attr("contentType", DataContentType),
- ),
- },
- }
- for _, v := range vs.all {
- vs.m[v.String()] = v
- }
- return vs
-}
-
-// New returns a set of versions
-func New() *Versions { return WithPrefix("") }
-
-// Built-in un-prefixed versions.
-var (
- VS *Versions
- V01 Version
- V02 Version
- V03 Version
- V1 Version
-)
-
-func init() {
- VS = New()
- V01, _ = VS.Version("0.1")
- V02, _ = VS.Version("0.2")
- V03, _ = VS.Version("0.3")
- V1, _ = VS.Version("1.0")
-}
diff --git a/v1/binding/spec/spec_test.go b/v1/binding/spec/spec_test.go
deleted file mode 100644
index 84f53f93a..000000000
--- a/v1/binding/spec/spec_test.go
+++ /dev/null
@@ -1,26 +0,0 @@
-package spec_test
-
-import (
- "testing"
-
- "github.com/cloudevents/sdk-go/v1/binding/spec"
- ce "github.com/cloudevents/sdk-go/v1/cloudevents"
- "github.com/stretchr/testify/assert"
-)
-
-func TestVersions(t *testing.T) {
- assert := assert.New(t)
- versions := spec.New()
- assert.Equal([]string{"specversion", "cloudEventsVersion"}, versions.SpecVersionNames())
-
- want := []string{"1.0", "0.3", "0.2", "0.1"}
- all := versions.Versions()
- assert.Equal(len(want), len(all))
- for i, s := range want {
- assert.Equal(s, all[i].String())
- assert.Equal(s, all[i].NewContext().GetSpecVersion())
- converted := all[i].Convert(ce.EventContextV01{}.AsV01())
- assert.Equal(s, converted.GetSpecVersion(), "%v %v %v", i, s, converted)
- }
- assert.Equal(want[0], versions.Latest().NewContext().GetSpecVersion())
-}
diff --git a/v1/binding/test/benchmark.go b/v1/binding/test/benchmark.go
deleted file mode 100644
index 0f358390e..000000000
--- a/v1/binding/test/benchmark.go
+++ /dev/null
@@ -1,44 +0,0 @@
-package test
-
-import (
- "context"
- "testing"
-
- "github.com/stretchr/testify/assert"
- "golang.org/x/sync/errgroup"
-
- "github.com/cloudevents/sdk-go/v1/binding"
-)
-
-// Simple send/receive benchmark.
-// Requires a sender and receiver that are connected to each other.
-func BenchmarkSendReceive(b *testing.B, s binding.Sender, r binding.Receiver) {
- m := binding.EventMessage(FullEvent())
- ctx := context.Background()
- b.ResetTimer() // Don't count setup.
- for i := 0; i < b.N; i++ {
- n := 10 // Messages to send async.
- g := errgroup.Group{}
- g.Go(func() error {
- for j := 0; j < n; j++ {
- if err := s.Send(ctx, m); err != nil {
- return err
- }
- }
- return nil
- })
- g.Go(func() error {
- for j := 0; j < n; j++ {
- m, err := r.Receive(ctx)
- if err != nil {
- return err
- }
- if err := m.Finish(nil); err != nil {
- return err
- }
- }
- return nil
- })
- assert.NoError(b, g.Wait())
- }
-}
diff --git a/v1/binding/test/events.go b/v1/binding/test/events.go
deleted file mode 100644
index b826b0ad2..000000000
--- a/v1/binding/test/events.go
+++ /dev/null
@@ -1,97 +0,0 @@
-// Package test contains test data and generic tests for testing bindings.
-package test
-
-import (
- "fmt"
- "net/url"
- "reflect"
- "time"
-
- "github.com/cloudevents/sdk-go/v1/binding/spec"
- ce "github.com/cloudevents/sdk-go/v1/cloudevents"
- "github.com/cloudevents/sdk-go/v1/cloudevents/types"
-)
-
-func strptr(s string) *string { return &s }
-
-var (
- Source = types.URIRef{URL: url.URL{Scheme: "http", Host: "example.com", Path: "/source"}}
- Timestamp = types.Timestamp{Time: time.Date(2020, 03, 21, 12, 34, 56, 780000000, time.UTC)}
- Schema = types.URI{URL: url.URL{Scheme: "http", Host: "example.com", Path: "/schema"}}
-)
-
-// FullEvent has all context attributes set and JSON string data.
-func FullEvent() ce.Event {
- e := ce.Event{
- Context: ce.EventContextV1{
- Type: "com.example.FullEvent",
- Source: Source,
- ID: "full-event",
- Time: &Timestamp,
- DataSchema: &Schema,
- DataContentType: strptr("text/json"),
- Subject: strptr("topic"),
- }.AsV1(),
- }
-
- e.SetExtension("exbool", true)
- e.SetExtension("exint", 42)
- e.SetExtension("exstring", "exstring")
- e.SetExtension("exbinary", []byte{0, 1, 2, 3})
- e.SetExtension("exurl", Source)
- e.SetExtension("extime", Timestamp)
-
- if err := e.SetData("hello"); err != nil {
- panic(err)
- }
- return e
-}
-
-// MinEvent has only required attributes set.
-func MinEvent() ce.Event {
- return ce.Event{
- Context: ce.EventContextV1{
- Type: "com.example.MinEvent",
- Source: Source,
- ID: "min-event",
- }.AsV1(),
- }
-}
-
-// AllVersions returns all versions of each event in events.
-// ID gets a -number suffix so IDs are unique.
-func AllVersions(events []ce.Event) []ce.Event {
- versions := spec.New()
- all := versions.Versions()
- result := make([]ce.Event, len(events)*len(all))
- i := 0
- for _, e := range events {
- for _, v := range all {
- result[i] = e
- result[i].Context = v.Convert(e.Context)
- result[i].SetID(fmt.Sprintf("%v-%v", e.ID(), i)) // Unique IDs
- i++
- }
- }
- return result
-}
-
-// Events is a set of test events that should be handled correctly by
-// all event-processing code.
-func Events() []ce.Event {
- return AllVersions([]ce.Event{FullEvent(), MinEvent()})
-}
-
-// NoExtensions returns a copy of events with no Extensions.
-// Use for testing where extensions are not supported.
-func NoExtensions(events []ce.Event) []ce.Event {
- result := make([]ce.Event, len(events))
- for i, e := range events {
- result[i] = e
- result[i].Context = e.Context.Clone()
- ctx := reflect.ValueOf(result[i].Context).Elem()
- ext := ctx.FieldByName("Extensions")
- ext.Set(reflect.Zero(ext.Type()))
- }
- return result
-}
diff --git a/v1/binding/test/mock_binary_message.go b/v1/binding/test/mock_binary_message.go
deleted file mode 100644
index 465e2ad3c..000000000
--- a/v1/binding/test/mock_binary_message.go
+++ /dev/null
@@ -1,119 +0,0 @@
-package test
-
-import (
- "bytes"
- "context"
- "io"
- "io/ioutil"
-
- cloudevents "github.com/cloudevents/sdk-go/v1"
- "github.com/cloudevents/sdk-go/v1/binding"
- "github.com/cloudevents/sdk-go/v1/binding/spec"
-)
-
-// MockBinaryMessage implements a binary-mode message as a simple struct.
-type MockBinaryMessage struct {
- Metadata map[spec.Attribute]interface{}
- Extensions map[string]interface{}
- Body []byte
-}
-
-func (bm *MockBinaryMessage) Start(ctx context.Context) error {
- bm.Metadata = make(map[spec.Attribute]interface{})
- bm.Extensions = make(map[string]interface{})
- return nil
-}
-
-func (bm *MockBinaryMessage) SetAttribute(attribute spec.Attribute, value interface{}) error {
- bm.Metadata[attribute] = value
- return nil
-}
-
-func (bm *MockBinaryMessage) SetExtension(name string, value interface{}) error {
- bm.Extensions[name] = value
- return nil
-}
-
-func (bm *MockBinaryMessage) SetData(data io.Reader) (err error) {
- bm.Body, err = ioutil.ReadAll(data)
- return err
-}
-
-func (bm *MockBinaryMessage) End() error {
- return nil
-}
-
-var versions = spec.New()
-
-func NewMockBinaryMessage(e cloudevents.Event) binding.Message {
- version, err := versions.Version(e.SpecVersion())
- if err != nil {
- panic(err)
- }
-
- m := MockBinaryMessage{
- Metadata: make(map[spec.Attribute]interface{}),
- Extensions: make(map[string]interface{}),
- }
-
- for _, attribute := range version.Attributes() {
- val := attribute.Get(e.Context)
- if val != nil {
- m.Metadata[attribute] = val
- }
- }
-
- for k, v := range e.Extensions() {
- m.Extensions[k] = v
- }
-
- m.Body, err = e.DataBytes()
- if err != nil {
- panic(err)
- }
-
- return &m
-}
-
-func (bm *MockBinaryMessage) GetParent() binding.Message {
- return nil
-}
-
-func (bm *MockBinaryMessage) Structured(context.Context, binding.StructuredEncoder) error {
- return binding.ErrNotStructured
-}
-
-func (bm *MockBinaryMessage) Binary(ctx context.Context, b binding.BinaryEncoder) error {
- err := b.Start(ctx)
- if err != nil {
- return err
- }
- for k, v := range bm.Metadata {
- err = b.SetAttribute(k, v)
- if err != nil {
- return err
- }
- }
- for k, v := range bm.Extensions {
- err = b.SetExtension(k, v)
- if err != nil {
- return err
- }
- }
- if len(bm.Body) != 0 {
- err = b.SetData(bytes.NewReader(bm.Body))
- if err != nil {
- return err
- }
- }
- return b.End()
-}
-
-func (bm *MockBinaryMessage) Encoding() binding.Encoding {
- return binding.EncodingBinary
-}
-
-func (bm *MockBinaryMessage) Finish(error) error { return nil }
-
-var _ binding.Message = (*MockBinaryMessage)(nil) // Test it conforms to the interface
-var _ binding.BinaryEncoder = (*MockBinaryMessage)(nil)
diff --git a/v1/binding/test/mock_structured_message.go b/v1/binding/test/mock_structured_message.go
deleted file mode 100644
index 8fcc082ed..000000000
--- a/v1/binding/test/mock_structured_message.go
+++ /dev/null
@@ -1,56 +0,0 @@
-package test
-
-import (
- "bytes"
- "context"
- "io"
- "io/ioutil"
-
- cloudevents "github.com/cloudevents/sdk-go/v1"
- "github.com/cloudevents/sdk-go/v1/binding"
- "github.com/cloudevents/sdk-go/v1/binding/format"
-)
-
-// MockStructuredMessage implements a structured-mode message as a simple struct.
-type MockStructuredMessage struct {
- Format format.Format
- Bytes []byte
-}
-
-func (s *MockStructuredMessage) SetStructuredEvent(ctx context.Context, format format.Format, event io.Reader) (err error) {
- s.Format = format
- s.Bytes, err = ioutil.ReadAll(event)
- if err != nil {
- return
- }
-
- return nil
-}
-
-func NewMockStructuredMessage(e cloudevents.Event) binding.Message {
- testEventSerialized, err := format.JSON.Marshal(e)
- if err != nil {
- panic(err)
- }
- return &MockStructuredMessage{
- Bytes: testEventSerialized,
- Format: format.JSON,
- }
-}
-
-func (s *MockStructuredMessage) Structured(ctx context.Context, b binding.StructuredEncoder) error {
- return b.SetStructuredEvent(ctx, s.Format, bytes.NewReader(s.Bytes))
-}
-
-func (s *MockStructuredMessage) Binary(context.Context, binding.BinaryEncoder) error {
- return binding.ErrNotBinary
-}
-
-func (bm *MockStructuredMessage) Encoding() binding.Encoding {
- return binding.EncodingStructured
-}
-
-func (s *MockStructuredMessage) Finish(error) error { return nil }
-
-var _ binding.Message = (*MockStructuredMessage)(nil) // Test it conforms to the interface
-var _ binding.StructuredEncoder = (*MockStructuredMessage)(nil)
diff --git a/v1/binding/test/test.go b/v1/binding/test/test.go
deleted file mode 100644
index 6700af08f..000000000
--- a/v1/binding/test/test.go
+++ /dev/null
@@ -1,153 +0,0 @@
-// Package test provides re-usable functions for binding tests.
-package test
-
-import (
- "context"
- "encoding/json"
- "fmt"
- "reflect"
- "sync"
- "testing"
-
- "github.com/stretchr/testify/assert"
- "github.com/stretchr/testify/require"
-
- cloudevents "github.com/cloudevents/sdk-go/v1"
- "github.com/cloudevents/sdk-go/v1/binding"
- "github.com/cloudevents/sdk-go/v1/binding/format"
- "github.com/cloudevents/sdk-go/v1/binding/spec"
- ce "github.com/cloudevents/sdk-go/v1/cloudevents"
- "github.com/cloudevents/sdk-go/v1/cloudevents/types"
-)
-
-// NameOf generates a string test name from x, esp. for ce.Event and ce.Message.
-func NameOf(x interface{}) string {
- switch x := x.(type) {
- case ce.Event:
- b, err := json.Marshal(x)
- if err == nil {
- return fmt.Sprintf("Event%s", b)
- }
- case binding.Message:
- return fmt.Sprintf("Message{%s}", reflect.TypeOf(x).String())
- }
- return fmt.Sprintf("%T(%#v)", x, x)
-}
-
-// Run f as a test for each event in events
-func EachEvent(t *testing.T, events []ce.Event, f func(*testing.T, ce.Event)) {
- for _, e := range events {
- in := e
- t.Run(NameOf(in), func(t *testing.T) { f(t, in) })
- }
-}
-
-// Run f as a test for each message in messages
-func EachMessage(t *testing.T, messages []binding.Message, f func(*testing.T, binding.Message)) {
- for _, m := range messages {
- in := m
- t.Run(NameOf(in), func(t *testing.T) { f(t, in) })
- }
-}
-
-// Canonical converts all attributes to canonical string form for comparisons.
-func Canonical(t *testing.T, c ce.EventContext) {
- t.Helper()
- for k, v := range c.GetExtensions() {
- s, err := types.Format(v)
- require.NoError(t, err, "extension[%q]=%#v: %v", k, v, err)
- assert.NoError(t, c.SetExtension(k, s))
- }
-}
-
-// SendReceive does, s.Send(in) and returns r.Receive().
-// Halt test on error.
-func SendReceive(t *testing.T, ctx context.Context, in binding.Message, s binding.Sender, r binding.Receiver, outAssert func(binding.Message)) {
- t.Helper()
- wg := sync.WaitGroup{}
- wg.Add(2)
-
- go func() {
- defer wg.Done()
- out, recvErr := r.Receive(ctx)
- require.NoError(t, recvErr)
- outAssert(out)
- finishErr := out.Finish(nil)
- require.NoError(t, finishErr)
- }()
-
- go func() {
- defer wg.Done()
- err := s.Send(ctx, in)
- require.NoError(t, err)
- }()
-
- wg.Wait()
-}
-
-func AssertEventContextEquals(t *testing.T, want cloudevents.EventContext, have cloudevents.EventContext) {
- wantVersion, err := spec.VS.Version(want.GetSpecVersion())
- require.NoError(t, err)
- haveVersion, err := spec.VS.Version(have.GetSpecVersion())
- require.NoError(t, err)
- require.Equal(t, wantVersion, haveVersion)
-
- for _, a := range wantVersion.Attributes() {
- require.Equal(t, a.Get(want), a.Get(have), "Attribute %s does not match: %v != %v", a.Name(), a.Get(want), a.Get(have))
- }
-
- require.Equal(t, want.GetExtensions(), have.GetExtensions(), "Extensions")
-}
-
-func AssertEventEquals(t *testing.T, want cloudevents.Event, have cloudevents.Event) {
- AssertEventContextEquals(t, want.Context, have.Context)
- wantPayload, err := want.DataBytes()
- assert.NoError(t, err)
- havePayload, err := have.DataBytes()
- assert.NoError(t, err)
- assert.Equal(t, wantPayload, havePayload)
-}
-
-func ExToStr(t *testing.T, e ce.Event) ce.Event {
- for k, v := range e.Extensions() {
- var vParsed interface{}
- var err error
-
- switch v.(type) {
- case json.RawMessage:
- err = json.Unmarshal(v.(json.RawMessage), &vParsed)
- assert.NoError(t, err)
- default:
- vParsed, err = types.Format(v)
- require.NoError(t, err)
- }
- e.SetExtension(k, vParsed)
- }
- return e
-}
-
-func MustJSON(e ce.Event) []byte {
- b, err := format.JSON.Marshal(e)
- if err != nil {
- panic(err)
- }
- return b
-}
-
-func MustToEvent(ctx context.Context, m binding.Message) (e ce.Event, encoding binding.Encoding) {
- var err error
- e, encoding, err = binding.ToEvent(ctx, m)
- if err != nil {
- panic(err)
- }
- return
-}
-
-func CopyEventContext(e ce.Event) ce.Event {
- newE := ce.Event{}
- newE.Context = e.Context.Clone()
- newE.DataEncoded = e.DataEncoded
- newE.Data = e.Data
- newE.DataBinary = e.DataBinary
- return newE
-}
diff --git a/v1/binding/test/test_test.go b/v1/binding/test/test_test.go
deleted file mode 100644
index 7baa80fc0..000000000
--- a/v1/binding/test/test_test.go
+++ /dev/null
@@ -1,52 +0,0 @@
-package test_test
-
-import (
- "context"
- "testing"
-
- "github.com/stretchr/testify/assert"
-
- "github.com/cloudevents/sdk-go/v1/binding"
- "github.com/cloudevents/sdk-go/v1/binding/test"
-)
-
-func TestEvent(t *testing.T) {
- assert := assert.New(t)
-
- e := test.FullEvent()
- assert.Equal("1.0", e.SpecVersion())
- assert.Equal("com.example.FullEvent", e.Type())
- assert.Equal(true, e.DataEncoded)
- var s string
- err := e.DataAs(&s)
- assert.NoError(err)
- assert.Equal("hello", s)
-
- e = test.MinEvent()
- assert.Equal("1.0", e.SpecVersion())
- assert.Equal("com.example.MinEvent", e.Type())
- assert.Nil(e.Data)
- assert.Empty(e.DataContentType())
-}
-
-type dummySR chan binding.Message
-
-func (d dummySR) Send(ctx context.Context, m binding.Message) (err error) { d <- m; return nil }
-func (d dummySR) Receive(ctx context.Context) (binding.Message, error) { return <-d, nil }
-
-func TestSendReceive(t *testing.T) {
- sr := make(dummySR)
- allIn := []binding.Message{}
- for _, e := range test.Events() {
- allIn = append(allIn, binding.EventMessage(e))
- }
-
- var allOut []binding.Message
- test.EachMessage(t, allIn, func(t *testing.T, in binding.Message) {
- test.SendReceive(t, context.Background(), in, sr, sr, func(out binding.Message) {
- assert.Equal(t, in, out)
- allOut = append(allOut, out)
- })
- })
- assert.Equal(t, allIn, allOut)
-}
diff --git a/v1/binding/test/transcoder.go b/v1/binding/test/transcoder.go
deleted file mode 100644
index 9e21a7d37..000000000
--- a/v1/binding/test/transcoder.go
+++ /dev/null
@@ -1,45 +0,0 @@
-package test
-
-import (
- "context"
- "testing"
-
- "github.com/stretchr/testify/require"
-
- cloudevents "github.com/cloudevents/sdk-go/v1"
- "github.com/cloudevents/sdk-go/v1/binding"
-)
-
-type TranscoderTestArgs struct {
- Name string
- InputMessage binding.Message
- WantEvent cloudevents.Event
- Transformers []binding.TransformerFactory
-}
-
-func RunTranscoderTests(t *testing.T, ctx context.Context, tests []TranscoderTestArgs) {
- for _, tt := range tests {
- tt := tt // Don't use range variable inside scope
- t.Run(tt.Name, func(t *testing.T) {
-
- mockStructured := MockStructuredMessage{}
- mockBinary := MockBinaryMessage{}
-
- enc, err := binding.Encode(ctx, tt.InputMessage, &mockStructured, &mockBinary, tt.Transformers)
- require.NoError(t, err)
-
- var e cloudevents.Event
- if enc == binding.EncodingStructured {
- e, _, err = binding.ToEvent(ctx, &mockStructured)
- require.NoError(t, err)
- } else if enc == binding.EncodingBinary {
- e, _, err = binding.ToEvent(ctx, &mockBinary)
- require.NoError(t, err)
- } else {
- t.Fatalf("Unexpected encoding %v", enc)
- }
- require.NoError(t, err)
- AssertEventEquals(t, tt.WantEvent, e)
- })
- }
-}
diff --git a/v1/binding/to_event.go b/v1/binding/to_event.go
deleted file mode 100644
index ff28a7ed7..000000000
--- a/v1/binding/to_event.go
+++ /dev/null
@@ -1,123 +0,0 @@
-package binding
-
-import (
- "bytes"
- "context"
- "errors"
- "io"
-
- cloudevents "github.com/cloudevents/sdk-go/v1"
- "github.com/cloudevents/sdk-go/v1/binding/format"
- "github.com/cloudevents/sdk-go/v1/binding/spec"
- ce "github.com/cloudevents/sdk-go/v1/cloudevents"
- "github.com/cloudevents/sdk-go/v1/cloudevents/types"
-)
-
-var ErrCannotConvertToEvent = errors.New("cannot convert message to event")
-
-// Translates a Message with a valid Structured or Binary representation to an Event
-// The TransformerFactories **aren't invoked** during the transformation to event,
-// but after the event instance is generated
-func ToEvent(ctx context.Context, message Message, transformers ...TransformerFactory) (e ce.Event, encoding Encoding, err error) {
- e = cloudevents.NewEvent()
-
- messageEncoding := message.Encoding()
- if messageEncoding == EncodingEvent {
- for m := message; m != nil; {
- if em, ok := m.(EventMessage); ok {
- e = ce.Event(em)
- encoding = EncodingEvent
- err = TransformerFactories(transformers).EventTransformer()(&e)
- return
- }
- if mw, ok := m.(MessageWrapper); ok {
- m = mw.GetWrappedMessage()
- } else {
- break
- }
- }
- err = ErrCannotConvertToEvent
- return
- }
-
- encoder := &messageToEventBuilder{event: &e}
- encoding, err = RunDirectEncoding(
- context.TODO(),
- message,
- encoder,
- encoder,
- []TransformerFactory{},
- )
- if err != nil {
- return e, encoding, err
- }
- err = TransformerFactories(transformers).EventTransformer()(&e)
- return
-}
-
-type messageToEventBuilder struct {
- event *ce.Event
-}
-
-var _ StructuredEncoder = (*messageToEventBuilder)(nil)
-var _ BinaryEncoder = (*messageToEventBuilder)(nil)
-
-func (b *messageToEventBuilder) SetStructuredEvent(ctx context.Context, format format.Format, event io.Reader) error {
- var buf bytes.Buffer
- _, err := io.Copy(&buf, event)
- if err != nil {
- return err
- }
- return format.Unmarshal(buf.Bytes(), b.event)
-}
-
-func (b *messageToEventBuilder) Start(ctx context.Context) error {
- return nil
-}
-
-func (b *messageToEventBuilder) End() error {
- return nil
-}
-
-func (b *messageToEventBuilder) SetData(data io.Reader) error {
- var buf bytes.Buffer
- w, err := io.Copy(&buf, data)
- if err != nil {
- return err
- }
- if w != 0 {
- return b.event.SetData(buf.Bytes())
- }
- return nil
-}
-
-func (b *messageToEventBuilder) SetAttribute(attribute spec.Attribute, value interface{}) error {
- // If spec version we need to change to right context struct
- if attribute.Kind() == spec.SpecVersion {
- str, err := types.ToString(value)
- if err != nil {
- return err
- }
- switch str {
- case cloudevents.VersionV01:
- b.event.Context = b.event.Context.AsV01()
- case cloudevents.VersionV02:
- b.event.Context = b.event.Context.AsV02()
- case cloudevents.VersionV03:
- b.event.Context = b.event.Context.AsV03()
- case cloudevents.VersionV1:
- b.event.Context = b.event.Context.AsV1()
- }
- return nil
- }
- return attribute.Set(b.event.Context, value)
-}
-
-func (b *messageToEventBuilder) SetExtension(name string, value interface{}) error {
- value, err := types.Validate(value)
- if err != nil {
- return err
- }
- b.event.SetExtension(name, value)
- return nil
-}
diff --git a/v1/binding/to_event_test.go b/v1/binding/to_event_test.go
deleted file mode 100644
index 7bb583fb0..000000000
--- a/v1/binding/to_event_test.go
+++ /dev/null
@@ -1,73 +0,0 @@
-package binding_test
-
-import (
- "context"
- "testing"
-
- "github.com/stretchr/testify/require"
-
- "github.com/cloudevents/sdk-go/v1/binding"
- "github.com/cloudevents/sdk-go/v1/binding/test"
- "github.com/cloudevents/sdk-go/v1/cloudevents"
-)
-
-type toEventTestCase struct {
- name string
- encoding binding.Encoding
- message binding.Message
- want cloudevents.Event
-}
-
-func TestToEvent(t *testing.T) {
- tests := []toEventTestCase{}
-
- for _, v := range test.Events() {
- tests = append(tests, []toEventTestCase{
- {
- name: "From mock structured with payload/" + test.NameOf(v),
- encoding: binding.EncodingStructured,
- message: test.NewMockStructuredMessage(v),
- want: v,
- },
- {
- name: "From mock structured without payload/" + test.NameOf(v),
- encoding: binding.EncodingStructured,
- message: test.NewMockStructuredMessage(v),
- want: v,
- },
- {
- name: "From mock binary with payload/" + test.NameOf(v),
- encoding: binding.EncodingBinary,
- message: test.NewMockBinaryMessage(v),
- want: v,
- },
- {
- name: "From mock binary without payload/" + test.NameOf(v),
- encoding: binding.EncodingBinary,
- message: test.NewMockBinaryMessage(v),
- want: v,
- },
- {
- name: "From event with payload/" + test.NameOf(v),
- encoding: binding.EncodingEvent,
- message: binding.EventMessage(v),
- want: v,
- },
- {
- name: "From event without payload/" + test.NameOf(v),
- encoding: binding.EncodingEvent,
- message: binding.EventMessage(v),
- want: v,
- },
- }...)
- }
- for _, tt := range tests {
- tt := tt // Don't use range variable in Run() scope
- t.Run(tt.name, func(t *testing.T) {
- got, encoding, err := binding.ToEvent(context.Background(), tt.message)
- require.NoError(t, err)
- require.Equal(t, tt.encoding, encoding)
- test.AssertEventEquals(t, test.ExToStr(t, tt.want), test.ExToStr(t, got))
- })
- }
-}
diff --git a/v1/binding/transcoder.go b/v1/binding/transcoder.go
deleted file mode 100644
index 9ff08f0fb..000000000
--- a/v1/binding/transcoder.go
+++ /dev/null
@@ -1,78 +0,0 @@
-package binding
-
-import ce "github.com/cloudevents/sdk-go/v1"
-
-// Implements a transformation process while transferring the event from the Message implementation
-// to the provided encoder
-//
-// A transformer could optionally not provide an implementation for binary and/or structured encodings,
-// returning nil to the respective factory method.
-type TransformerFactory interface {
- // Can return nil if the transformation doesn't support structured encoding directly
- StructuredTransformer(encoder StructuredEncoder) StructuredEncoder
-
- // Can return nil if the transformation doesn't support binary encoding directly
- BinaryTransformer(encoder BinaryEncoder) BinaryEncoder
-
- // Can return nil if the transformation doesn't support events
- EventTransformer() EventTransformer
-}
-
-// Utility type alias to manage multiple TransformerFactory
-type TransformerFactories []TransformerFactory
-
-func (t TransformerFactories) StructuredTransformer(encoder StructuredEncoder) StructuredEncoder {
- if encoder == nil {
- return nil
- }
- res := encoder
- for _, b := range t {
- if b == nil {
- continue
- }
- if r := b.StructuredTransformer(res); r != nil {
- res = r
- } else {
- return nil // Structured not supported!
- }
- }
- return res
-}
-
-func (t TransformerFactories) BinaryTransformer(encoder BinaryEncoder) BinaryEncoder {
- if encoder == nil {
- return nil
- }
- res := encoder
- for _, b := range t {
- if b == nil {
- continue
- }
- if r := b.BinaryTransformer(res); r != nil {
- res = r
- } else {
- return nil // Binary not supported!
- }
- }
- return res
-}
-
-func (t TransformerFactories) EventTransformer() EventTransformer {
- return func(e *ce.Event) error {
- for _, factory := range t {
- if factory == nil {
- continue
- }
- f := factory.EventTransformer()
- if f != nil {
- err := f(e)
- if err != nil {
- return err
- }
- }
- }
- return nil
- }
-}
-
-type EventTransformer func(*ce.Event) error
diff --git a/v1/binding/transcoder/add_metadata.go b/v1/binding/transcoder/add_metadata.go
deleted file mode 100644
index dc08a3db9..000000000
--- a/v1/binding/transcoder/add_metadata.go
+++ /dev/null
@@ -1,119 +0,0 @@
-package transcoder
-
-import (
- cloudevents "github.com/cloudevents/sdk-go/v1"
- "github.com/cloudevents/sdk-go/v1/binding"
- "github.com/cloudevents/sdk-go/v1/binding/spec"
-)
-
-// Add cloudevents attribute (if missing) during the encoding process
-func AddAttribute(attributeKind spec.Kind, value interface{}) binding.TransformerFactory {
- return setAttributeTranscoderFactory{attributeKind: attributeKind, value: value}
-}
-
-// Add cloudevents extension (if missing) during the encoding process
-func AddExtension(name string, value interface{}) binding.TransformerFactory {
- return setExtensionTranscoderFactory{name: name, value: value}
-}
-
-type setAttributeTranscoderFactory struct {
- attributeKind spec.Kind
- value interface{}
-}
-
-func (a setAttributeTranscoderFactory) StructuredTransformer(binding.StructuredEncoder) binding.StructuredEncoder {
- return nil
-}
-
-func (a setAttributeTranscoderFactory) BinaryTransformer(encoder binding.BinaryEncoder) binding.BinaryEncoder {
- return &setAttributeTransformer{
- BinaryEncoder: encoder,
- attributeKind: a.attributeKind,
- value: a.value,
- found: false,
- }
-}
-
-func (a setAttributeTranscoderFactory) EventTransformer() binding.EventTransformer {
- return func(event *cloudevents.Event) error {
- v, err := spec.VS.Version(event.SpecVersion())
- if err != nil {
- return err
- }
- if v.AttributeFromKind(a.attributeKind).Get(event.Context) == nil {
- return v.AttributeFromKind(a.attributeKind).Set(event.Context, a.value)
- }
- return nil
- }
-}
-
-type setExtensionTranscoderFactory struct {
- name string
- value interface{}
-}
-
-func (a setExtensionTranscoderFactory) StructuredTransformer(binding.StructuredEncoder) binding.StructuredEncoder {
- return nil
-}
-
-func (a setExtensionTranscoderFactory) BinaryTransformer(encoder binding.BinaryEncoder) binding.BinaryEncoder {
- return &setExtensionTransformer{
- BinaryEncoder: encoder,
- name: a.name,
- value: a.value,
- found: false,
- }
-}
-
-func (a setExtensionTranscoderFactory) EventTransformer() binding.EventTransformer {
- return func(event *cloudevents.Event) error {
- if _, ok := event.Extensions()[a.name]; !ok {
- return event.Context.SetExtension(a.name, a.value)
- }
- return nil
- }
-}
-
-type setAttributeTransformer struct {
- binding.BinaryEncoder
- attributeKind spec.Kind
- value interface{}
- version spec.Version
- found bool
-}
-
-func (b *setAttributeTransformer) SetAttribute(attribute spec.Attribute, value interface{}) error {
- if attribute.Kind() == b.attributeKind {
- b.found = true
- }
- b.version = attribute.Version()
- return b.BinaryEncoder.SetAttribute(attribute, value)
-}
-
-func (b *setAttributeTransformer) End() error {
- if !b.found {
- return b.BinaryEncoder.SetAttribute(b.version.AttributeFromKind(b.attributeKind), b.value)
- }
- return nil
-}
-
-type setExtensionTransformer struct {
- binding.BinaryEncoder
- name string
- value interface{}
- found bool
-}
-
-func (b *setExtensionTransformer) SetExtension(name string, value interface{}) error {
- if name == b.name {
- b.found = true
- }
- return b.BinaryEncoder.SetExtension(name, value)
-}
-
-func (b *setExtensionTransformer) End() error {
- if !b.found {
- return b.BinaryEncoder.SetExtension(b.name, b.value)
- }
- return nil
-}
diff --git a/v1/binding/transcoder/add_metadata_test.go b/v1/binding/transcoder/add_metadata_test.go
deleted file mode 100644
index 92d8c3e22..000000000
--- a/v1/binding/transcoder/add_metadata_test.go
+++ /dev/null
@@ -1,134 +0,0 @@
-package transcoder
-
-import (
- "context"
- "testing"
- "time"
-
- "github.com/stretchr/testify/require"
-
- "github.com/cloudevents/sdk-go/v1/binding"
- "github.com/cloudevents/sdk-go/v1/binding/spec"
- "github.com/cloudevents/sdk-go/v1/binding/test"
- "github.com/cloudevents/sdk-go/v1/cloudevents/types"
-)
-
-func TestAddAttribute(t *testing.T) {
- e := test.MinEvent()
- e.Context = e.Context.AsV1()
-
- subject := "aaa"
- expectedEventWithSubject := test.CopyEventContext(e)
- require.NoError(t, expectedEventWithSubject.Context.SetSubject(subject))
-
- timestamp, err := types.ToTime(time.Now())
- require.NoError(t, err)
- expectedEventWithTime := test.CopyEventContext(e)
- require.NoError(t, expectedEventWithTime.Context.SetTime(timestamp))
-
- test.RunTranscoderTests(t, context.Background(), []test.TranscoderTestArgs{
- {
- Name: "No change to id to Mock Structured message",
- InputMessage: test.NewMockStructuredMessage(test.CopyEventContext(e)),
- WantEvent: test.CopyEventContext(e),
- Transformers: binding.TransformerFactories{AddAttribute(spec.ID, "new-id")},
- },
- {
- Name: "No change to id to Mock Binary message",
- InputMessage: test.NewMockBinaryMessage(test.CopyEventContext(e)),
- WantEvent: test.CopyEventContext(e),
- Transformers: binding.TransformerFactories{AddAttribute(spec.ID, "new-id")},
- },
- {
- Name: "No change to id to Event message",
- InputMessage: binding.EventMessage(test.CopyEventContext(e)),
- WantEvent: test.CopyEventContext(e),
- Transformers: binding.TransformerFactories{AddAttribute(spec.ID, "new-id")},
- },
- {
- Name: "Add subject to Mock Structured message",
- InputMessage: test.NewMockStructuredMessage(test.CopyEventContext(e)),
- WantEvent: expectedEventWithSubject,
- Transformers: binding.TransformerFactories{AddAttribute(spec.Subject, subject)},
- },
- {
- Name: "Add subject to Mock Binary message",
- InputMessage: test.NewMockBinaryMessage(test.CopyEventContext(e)),
- WantEvent: expectedEventWithSubject,
- Transformers: binding.TransformerFactories{AddAttribute(spec.Subject, subject)},
- },
- {
- Name: "Add subject to Event message",
- InputMessage: binding.EventMessage(test.CopyEventContext(e)),
- WantEvent: expectedEventWithSubject,
- Transformers: binding.TransformerFactories{AddAttribute(spec.Subject, subject)},
- },
- {
- Name: "Add time to Mock Structured message",
- InputMessage: test.NewMockStructuredMessage(test.CopyEventContext(e)),
- WantEvent: expectedEventWithTime,
- Transformers: binding.TransformerFactories{AddAttribute(spec.Time, timestamp)},
- },
- {
- Name: "Add time to Mock Binary message",
- InputMessage: test.NewMockBinaryMessage(test.CopyEventContext(e)),
- WantEvent: expectedEventWithTime,
- Transformers: binding.TransformerFactories{AddAttribute(spec.Time, timestamp)},
- },
- {
- Name: "Add time to Event message",
- InputMessage: binding.EventMessage(test.CopyEventContext(e)),
- WantEvent: expectedEventWithTime,
- Transformers: binding.TransformerFactories{AddAttribute(spec.Time, timestamp)},
- },
- })
-}
-
-func TestAddExtension(t *testing.T) {
- e := test.MinEvent()
- e.Context = e.Context.AsV1()
-
- extName := "aaa"
- extValue := "bbb"
- expectedEventWithExtension := test.CopyEventContext(e)
- require.NoError(t, expectedEventWithExtension.Context.SetExtension(extName, extValue))
-
- test.RunTranscoderTests(t, context.Background(), []test.TranscoderTestArgs{
- {
- Name: "No change to extension 'aaa' to Mock Structured message",
- InputMessage: test.NewMockStructuredMessage(test.CopyEventContext(expectedEventWithExtension)),
- WantEvent: test.CopyEventContext(expectedEventWithExtension),
- Transformers: binding.TransformerFactories{AddExtension(extName, extValue)},
- },
- {
- Name: "No change to extension 'aaa' to Mock Binary message",
- InputMessage: test.NewMockBinaryMessage(test.CopyEventContext(expectedEventWithExtension)),
- WantEvent: test.CopyEventContext(expectedEventWithExtension),
- Transformers: binding.TransformerFactories{AddExtension(extName, extValue)},
- },
- {
- Name: "No change to extension 'aaa' to Event message",
- InputMessage: binding.EventMessage(test.CopyEventContext(expectedEventWithExtension)),
- WantEvent: test.CopyEventContext(expectedEventWithExtension),
- Transformers: binding.TransformerFactories{AddExtension(extName, extValue)},
- },
- {
- Name: "Add extension 'aaa' to Mock Structured message",
- InputMessage: test.NewMockStructuredMessage(test.CopyEventContext(e)),
- WantEvent: test.CopyEventContext(expectedEventWithExtension),
- Transformers: binding.TransformerFactories{AddExtension(extName, extValue)},
- },
- {
- Name: "Add extension 'aaa' to Mock Binary message",
- InputMessage: test.NewMockBinaryMessage(test.CopyEventContext(e)),
- WantEvent: test.CopyEventContext(expectedEventWithExtension),
- Transformers: binding.TransformerFactories{AddExtension(extName, extValue)},
- },
- {
- Name: "Add extension 'aaa' to Event message",
- InputMessage: binding.EventMessage(test.CopyEventContext(e)),
- WantEvent: test.CopyEventContext(expectedEventWithExtension),
- Transformers: binding.TransformerFactories{AddExtension(extName, extValue)},
- },
- })
-}
diff --git a/v1/binding/transcoder/delete_metadata.go b/v1/binding/transcoder/delete_metadata.go
deleted file mode 100644
index d87b3be56..000000000
--- a/v1/binding/transcoder/delete_metadata.go
+++ /dev/null
@@ -1,90 +0,0 @@
-package transcoder
-
-import (
- cloudevents "github.com/cloudevents/sdk-go/v1"
- "github.com/cloudevents/sdk-go/v1/binding"
- "github.com/cloudevents/sdk-go/v1/binding/spec"
-)
-
-// Delete cloudevents attribute during the encoding process
-func DeleteAttribute(attributeKind spec.Kind) binding.TransformerFactory {
- return deleteAttributeTranscoderFactory{attributeKind: attributeKind}
-}
-
-// Delete cloudevents extension during the encoding process
-func DeleteExtension(name string) binding.TransformerFactory {
- return deleteExtensionTranscoderFactory{name: name}
-}
-
-type deleteAttributeTranscoderFactory struct {
- attributeKind spec.Kind
-}
-
-func (a deleteAttributeTranscoderFactory) StructuredTransformer(binding.StructuredEncoder) binding.StructuredEncoder {
- return nil
-}
-
-func (a deleteAttributeTranscoderFactory) BinaryTransformer(encoder binding.BinaryEncoder) binding.BinaryEncoder {
- return &deleteAttributeTransformer{
- BinaryEncoder: encoder,
- attributeKind: a.attributeKind,
- }
-}
-
-func (a deleteAttributeTranscoderFactory) EventTransformer() binding.EventTransformer {
- return func(event *cloudevents.Event) error {
- v, err := spec.VS.Version(event.SpecVersion())
- if err != nil {
- return err
- }
- if v.AttributeFromKind(a.attributeKind).Get(event.Context) != nil {
- return v.AttributeFromKind(a.attributeKind).Delete(event.Context)
- }
- return nil
- }
-}
-
-type deleteExtensionTranscoderFactory struct {
- name string
-}
-
-func (a deleteExtensionTranscoderFactory) StructuredTransformer(binding.StructuredEncoder) binding.StructuredEncoder {
- return nil
-}
-
-func (a deleteExtensionTranscoderFactory) BinaryTransformer(encoder binding.BinaryEncoder) binding.BinaryEncoder {
- return &deleteExtensionTransformer{
- BinaryEncoder: encoder,
- name: a.name,
- }
-}
-
-func (a deleteExtensionTranscoderFactory) EventTransformer() binding.EventTransformer {
- return func(event *cloudevents.Event) error {
- return event.Context.SetExtension(a.name, nil)
- }
-}
-
-type deleteAttributeTransformer struct {
- binding.BinaryEncoder
- attributeKind spec.Kind
-}
-
-func (b *deleteAttributeTransformer) SetAttribute(attribute spec.Attribute, value interface{}) error {
- if attribute.Kind() == b.attributeKind {
- return nil
- }
- return b.BinaryEncoder.SetAttribute(attribute, value)
-}
-
-type deleteExtensionTransformer struct {
- binding.BinaryEncoder
- name string
-}
-
-func (b *deleteExtensionTransformer) SetExtension(name string, value interface{}) error {
- if b.name == name {
- return nil
- }
- return b.BinaryEncoder.SetExtension(name, value)
-}
diff --git a/v1/binding/transcoder/delete_metadata_test.go b/v1/binding/transcoder/delete_metadata_test.go
deleted file mode 100644
index f69e4c8da..000000000
--- a/v1/binding/transcoder/delete_metadata_test.go
+++ /dev/null
@@ -1,131 +0,0 @@
-package transcoder
-
-import (
- "context"
- "testing"
- "time"
-
- "github.com/stretchr/testify/require"
-
- "github.com/cloudevents/sdk-go/v1/binding"
- "github.com/cloudevents/sdk-go/v1/binding/spec"
- "github.com/cloudevents/sdk-go/v1/binding/test"
-)
-
-func TestDeleteAttribute(t *testing.T) {
- withSubjectEvent := test.MinEvent()
- withSubjectEvent.Context = withSubjectEvent.Context.AsV1()
- require.NoError(t, withSubjectEvent.Context.SetSubject("aaa"))
-
- withTimeEvent := test.CopyEventContext(withSubjectEvent)
- require.NoError(t, withTimeEvent.Context.SetTime(time.Now()))
-
- noSubjectEvent := test.CopyEventContext(withSubjectEvent)
- require.NoError(t, noSubjectEvent.Context.SetSubject(""))
-
- test.RunTranscoderTests(t, context.Background(), []test.TranscoderTestArgs{
- {
- Name: "Remove subject from Mock Structured message",
- InputMessage: test.NewMockStructuredMessage(test.CopyEventContext(withSubjectEvent)),
- WantEvent: noSubjectEvent,
- Transformers: binding.TransformerFactories{DeleteAttribute(spec.Subject)},
- },
- {
- Name: "Remove subject from Mock Binary message",
- InputMessage: test.NewMockBinaryMessage(test.CopyEventContext(withSubjectEvent)),
- WantEvent: noSubjectEvent,
- Transformers: binding.TransformerFactories{DeleteAttribute(spec.Subject)},
- },
- {
- Name: "Remove subject from Event message",
- InputMessage: binding.EventMessage(test.CopyEventContext(withSubjectEvent)),
- WantEvent: noSubjectEvent,
- Transformers: binding.TransformerFactories{DeleteAttribute(spec.Subject)},
- },
- {
- Name: "Remove time from Mock Structured message",
- InputMessage: test.NewMockStructuredMessage(test.CopyEventContext(withTimeEvent)),
- WantEvent: test.CopyEventContext(withSubjectEvent),
- Transformers: binding.TransformerFactories{DeleteAttribute(spec.Time)},
- },
- {
- Name: "Remove time from Mock Binary message",
- InputMessage: test.NewMockBinaryMessage(test.CopyEventContext(withTimeEvent)),
- WantEvent: test.CopyEventContext(withSubjectEvent),
- Transformers: binding.TransformerFactories{DeleteAttribute(spec.Time)},
- },
- {
- Name: "Remove time from Event message",
- InputMessage: binding.EventMessage(test.CopyEventContext(withTimeEvent)),
- WantEvent: test.CopyEventContext(withSubjectEvent),
- Transformers: binding.TransformerFactories{DeleteAttribute(spec.Time)},
- },
- {
- Name: "Do nothing with Mock Structured message",
- InputMessage: test.NewMockStructuredMessage(test.CopyEventContext(withSubjectEvent)),
- WantEvent: test.CopyEventContext(withSubjectEvent),
- Transformers: binding.TransformerFactories{DeleteAttribute(spec.Time)},
- },
- {
- Name: "Do nothing with Mock Binary message",
- InputMessage: test.NewMockBinaryMessage(test.CopyEventContext(withSubjectEvent)),
- WantEvent: test.CopyEventContext(withSubjectEvent),
- Transformers: binding.TransformerFactories{DeleteAttribute(spec.Time)},
- },
- {
- Name: "Do nothing with Event message",
- InputMessage: binding.EventMessage(test.CopyEventContext(withSubjectEvent)),
- WantEvent: test.CopyEventContext(withSubjectEvent),
- Transformers: binding.TransformerFactories{DeleteAttribute(spec.Time)},
- },
- })
-}
-
-func TestDeleteExtension(t *testing.T) {
- e := test.MinEvent()
- e.Context = e.Context.AsV1()
-
- extName := "aaa"
- extValue := "bbb"
- expectedEventWithExtension := test.CopyEventContext(e)
- require.NoError(t, expectedEventWithExtension.Context.SetExtension(extName, extValue))
-
- test.RunTranscoderTests(t, context.Background(), []test.TranscoderTestArgs{
- {
- Name: "No change to Mock Structured message",
- InputMessage: test.NewMockStructuredMessage(test.CopyEventContext(expectedEventWithExtension)),
- WantEvent: test.CopyEventContext(expectedEventWithExtension),
- Transformers: binding.TransformerFactories{DeleteExtension("ccc")},
- },
- {
- Name: "No change to Mock Binary message",
- InputMessage: test.NewMockBinaryMessage(test.CopyEventContext(expectedEventWithExtension)),
- WantEvent: test.CopyEventContext(expectedEventWithExtension),
- Transformers: binding.TransformerFactories{DeleteExtension("ccc")},
- },
- {
- Name: "No change to Event message",
- InputMessage: binding.EventMessage(test.CopyEventContext(expectedEventWithExtension)),
- WantEvent: test.CopyEventContext(expectedEventWithExtension),
- Transformers: binding.TransformerFactories{DeleteExtension("ccc")},
- },
- {
- Name: "Delete extension 'aaa' from Mock Structured message",
- InputMessage: test.NewMockStructuredMessage(test.CopyEventContext(expectedEventWithExtension)),
- WantEvent: test.CopyEventContext(e),
- Transformers: binding.TransformerFactories{DeleteExtension(extName)},
- },
- {
- Name: "Delete extension 'aaa' from Mock Binary message",
- InputMessage: test.NewMockBinaryMessage(test.CopyEventContext(expectedEventWithExtension)),
- WantEvent: test.CopyEventContext(e),
- Transformers: binding.TransformerFactories{DeleteExtension(extName)},
- },
- {
- Name: "Delete extension 'aaa' from Event message",
- InputMessage: binding.EventMessage(test.CopyEventContext(expectedEventWithExtension)),
- WantEvent: test.CopyEventContext(e),
- Transformers: binding.TransformerFactories{DeleteExtension(extName)},
- },
- })
-}
diff --git a/v1/binding/transcoder/update_metadata.go b/v1/binding/transcoder/update_metadata.go
deleted file mode 100644
index d93c9d0a5..000000000
--- a/v1/binding/transcoder/update_metadata.go
+++ /dev/null
@@ -1,125 +0,0 @@
-package transcoder
-
-import (
- cloudevents "github.com/cloudevents/sdk-go/v1"
- "github.com/cloudevents/sdk-go/v1/binding"
- "github.com/cloudevents/sdk-go/v1/binding/spec"
-)
-
-// Update cloudevents attribute (if present) using the provided function during the encoding process
-func UpdateAttribute(attributeKind spec.Kind, updater func(interface{}) (interface{}, error)) binding.TransformerFactory {
- return updateAttributeTranscoderFactory{attributeKind: attributeKind, updater: updater}
-}
-
-// Update cloudevents extension (if present) using the provided function during the encoding process
-func UpdateExtension(name string, updater func(interface{}) (interface{}, error)) binding.TransformerFactory {
- return updateExtensionTranscoderFactory{name: name, updater: updater}
-}
-
-type updateAttributeTranscoderFactory struct {
- attributeKind spec.Kind
- updater func(interface{}) (interface{}, error)
-}
-
-func (a updateAttributeTranscoderFactory) StructuredTransformer(binding.StructuredEncoder) binding.StructuredEncoder {
- return nil
-}
-
-func (a updateAttributeTranscoderFactory) BinaryTransformer(encoder binding.BinaryEncoder) binding.BinaryEncoder {
- return &updateAttributeTransformer{
- BinaryEncoder: encoder,
- attributeKind: a.attributeKind,
- updater: a.updater,
- }
-}
-
-func (a updateAttributeTranscoderFactory) EventTransformer() binding.EventTransformer {
- return func(event *cloudevents.Event) error {
- v, err := spec.VS.Version(event.SpecVersion())
- if err != nil {
- return err
- }
- if val := v.AttributeFromKind(a.attributeKind).Get(event.Context); val != nil {
- newVal, err := a.updater(val)
- if err != nil {
- return err
- }
- if newVal == nil {
- return v.AttributeFromKind(a.attributeKind).Delete(event.Context)
- } else {
- return v.AttributeFromKind(a.attributeKind).Set(event.Context, newVal)
- }
- }
- return nil
- }
-}
-
-type updateExtensionTranscoderFactory struct {
- name string
- updater func(interface{}) (interface{}, error)
-}
-
-func (a updateExtensionTranscoderFactory) StructuredTransformer(binding.StructuredEncoder) binding.StructuredEncoder {
- return nil
-}
-
-func (a updateExtensionTranscoderFactory) BinaryTransformer(encoder binding.BinaryEncoder) binding.BinaryEncoder {
- return &updateExtensionTransformer{
- BinaryEncoder: encoder,
- name: a.name,
- updater: a.updater,
- }
-}
-
-func (a updateExtensionTranscoderFactory) EventTransformer() binding.EventTransformer {
- return func(event *cloudevents.Event) error {
- if val, ok := event.Extensions()[a.name]; ok {
- newVal, err := a.updater(val)
- if err != nil {
- return err
- }
- return event.Context.SetExtension(a.name, newVal)
- }
- return nil
- }
-}
-
-type updateAttributeTransformer struct {
- binding.BinaryEncoder
- attributeKind spec.Kind
- updater func(interface{}) (interface{}, error)
-}
-
-func (b *updateAttributeTransformer) SetAttribute(attribute spec.Attribute, value interface{}) error {
- if attribute.Kind() == b.attributeKind {
- newVal, err := b.updater(value)
- if err != nil {
- return err
- }
- if newVal != nil {
- return b.BinaryEncoder.SetAttribute(attribute, newVal)
- }
- return nil
- }
- return b.BinaryEncoder.SetAttribute(attribute, value)
-}
-
-type updateExtensionTransformer struct {
- binding.BinaryEncoder
- name string
- updater func(interface{}) (interface{}, error)
-}
-
-func (b *updateExtensionTransformer) SetExtension(name string, value interface{}) error {
- if name == b.name {
- newVal, err := b.updater(value)
- if err != nil {
- return err
- }
- if newVal != nil {
- return b.BinaryEncoder.SetExtension(name, newVal)
- }
- return nil
- }
- return b.BinaryEncoder.SetExtension(name, value)
-}
diff --git a/v1/binding/transcoder/update_metadata_test.go b/v1/binding/transcoder/update_metadata_test.go
deleted file mode 100644
index b362de14b..000000000
--- a/v1/binding/transcoder/update_metadata_test.go
+++ /dev/null
@@ -1,152 +0,0 @@
-package transcoder
-
-import (
- "context"
- "strings"
- "testing"
- "time"
-
- "github.com/stretchr/testify/require"
-
- "github.com/cloudevents/sdk-go/v1/binding"
- "github.com/cloudevents/sdk-go/v1/binding/spec"
- "github.com/cloudevents/sdk-go/v1/binding/test"
-)
-
-func TestUpdateAttribute(t *testing.T) {
- withSubjectEvent := test.MinEvent()
- withSubjectEvent.Context = withSubjectEvent.Context.AsV1()
- require.NoError(t, withSubjectEvent.Context.SetSubject("abc"))
-
- subjectUpdateFunc := func(v interface{}) (interface{}, error) {
- return strings.ToUpper(v.(string)), nil
- }
- updatedSubjectEvent := test.CopyEventContext(withSubjectEvent)
- require.NoError(t, updatedSubjectEvent.Context.SetSubject(strings.ToUpper("abc")))
-
- location, err := time.LoadLocation("UTC")
- require.NoError(t, err)
- timestamp := time.Now().In(location)
- withTimeEvent := test.CopyEventContext(withSubjectEvent)
- require.NoError(t, withTimeEvent.Context.SetTime(timestamp))
-
- timeUpdateFunc := func(v interface{}) (interface{}, error) {
- return v.(time.Time).Add(3 * time.Hour), nil
- }
- updatedTimeEvent := test.CopyEventContext(withTimeEvent)
- require.NoError(t, updatedTimeEvent.Context.SetTime(timestamp.Add(3*time.Hour)))
-
- test.RunTranscoderTests(t, context.Background(), []test.TranscoderTestArgs{
- {
- Name: "Update subject in Mock Structured message",
- InputMessage: test.NewMockStructuredMessage(test.CopyEventContext(withSubjectEvent)),
- WantEvent: test.CopyEventContext(updatedSubjectEvent),
- Transformers: binding.TransformerFactories{UpdateAttribute(spec.Subject, subjectUpdateFunc)},
- },
- {
- Name: "Update subject in Mock Binary message",
- InputMessage: test.NewMockBinaryMessage(test.CopyEventContext(withSubjectEvent)),
- WantEvent: test.CopyEventContext(updatedSubjectEvent),
- Transformers: binding.TransformerFactories{UpdateAttribute(spec.Subject, subjectUpdateFunc)},
- },
- {
- Name: "Update subject in Event message",
- InputMessage: binding.EventMessage(test.CopyEventContext(withSubjectEvent)),
- WantEvent: test.CopyEventContext(updatedSubjectEvent),
- Transformers: binding.TransformerFactories{UpdateAttribute(spec.Subject, subjectUpdateFunc)},
- },
- {
- Name: "Update time in Mock Structured message",
- InputMessage: test.NewMockStructuredMessage(test.CopyEventContext(withTimeEvent)),
- WantEvent: test.CopyEventContext(updatedTimeEvent),
- Transformers: binding.TransformerFactories{UpdateAttribute(spec.Time, timeUpdateFunc)},
- },
- {
- Name: "Update time in Mock Binary message",
- InputMessage: test.NewMockBinaryMessage(test.CopyEventContext(withTimeEvent)),
- WantEvent: test.CopyEventContext(updatedTimeEvent),
- Transformers: binding.TransformerFactories{UpdateAttribute(spec.Time, timeUpdateFunc)},
- },
- {
- Name: "Update time in Event message",
- InputMessage: binding.EventMessage(test.CopyEventContext(withTimeEvent)),
- WantEvent: test.CopyEventContext(updatedTimeEvent),
- Transformers: binding.TransformerFactories{UpdateAttribute(spec.Time, timeUpdateFunc)},
- },
- {
- Name: "Do nothing with Mock Structured message",
- InputMessage: test.NewMockStructuredMessage(test.CopyEventContext(withSubjectEvent)),
- WantEvent: test.CopyEventContext(withSubjectEvent),
- Transformers: binding.TransformerFactories{UpdateAttribute(spec.DataContentType, func(i interface{}) (interface{}, error) {
- return "text/plain", nil
- })},
- },
- {
- Name: "Do nothing with Mock Binary message",
- InputMessage: test.NewMockBinaryMessage(test.CopyEventContext(withSubjectEvent)),
- WantEvent: test.CopyEventContext(withSubjectEvent),
- Transformers: binding.TransformerFactories{UpdateAttribute(spec.DataContentType, func(i interface{}) (interface{}, error) {
- return "text/plain", nil
- })},
- },
- {
- Name: "Do nothing with Event message",
- InputMessage: binding.EventMessage(test.CopyEventContext(withSubjectEvent)),
- WantEvent: test.CopyEventContext(withSubjectEvent),
- Transformers: binding.TransformerFactories{UpdateAttribute(spec.DataContentType, func(i interface{}) (interface{}, error) {
- return "text/plain", nil
- })},
- },
- })
-}
-
-func TestUpdateExtension(t *testing.T) {
- e := test.MinEvent()
- e.Context = e.Context.AsV1()
- require.NoError(t, e.Context.SetExtension("aaa", "bbb"))
-
- updateFunc := func(v interface{}) (interface{}, error) {
- return strings.ToUpper(v.(string)), nil
- }
- updatedExtensionEvent := test.CopyEventContext(e)
- require.NoError(t, updatedExtensionEvent.Context.SetExtension("aaa", strings.ToUpper("bbb")))
-
- test.RunTranscoderTests(t, context.Background(), []test.TranscoderTestArgs{
- {
- Name: "No change in Mock Structured message",
- InputMessage: test.NewMockStructuredMessage(test.CopyEventContext(e)),
- WantEvent: test.CopyEventContext(e),
- Transformers: binding.TransformerFactories{UpdateExtension("ccc", updateFunc)},
- },
- {
- Name: "No change in Mock Binary message",
- InputMessage: test.NewMockBinaryMessage(test.CopyEventContext(e)),
- WantEvent: test.CopyEventContext(e),
- Transformers: binding.TransformerFactories{UpdateExtension("ccc", updateFunc)},
- },
- {
- Name: "No change in Event message",
- InputMessage: binding.EventMessage(test.CopyEventContext(e)),
- WantEvent: test.CopyEventContext(e),
- Transformers: binding.TransformerFactories{UpdateExtension("ccc", updateFunc)},
- },
- {
- Name: "Update extension 'aaa' in Mock Structured message",
- InputMessage: test.NewMockStructuredMessage(test.CopyEventContext(e)),
- WantEvent: test.CopyEventContext(updatedExtensionEvent),
- Transformers: binding.TransformerFactories{UpdateExtension("aaa", updateFunc)},
- },
- {
- Name: "Update extension 'aaa' in Mock Binary message",
- InputMessage: test.NewMockBinaryMessage(test.CopyEventContext(e)),
- WantEvent: test.CopyEventContext(updatedExtensionEvent),
- Transformers: binding.TransformerFactories{UpdateExtension("aaa", updateFunc)},
- },
- {
- Name: "Update extension 'aaa' in Event message",
- InputMessage: binding.EventMessage(test.CopyEventContext(e)),
- WantEvent: test.CopyEventContext(updatedExtensionEvent),
- Transformers: binding.TransformerFactories{UpdateExtension("aaa", updateFunc)},
- },
- })
-}
diff --git a/v1/binding/transcoder/version.go b/v1/binding/transcoder/version.go
deleted file mode 100644
index 7690c8483..000000000
--- a/v1/binding/transcoder/version.go
+++ /dev/null
@@ -1,44 +0,0 @@
-package transcoder
-
-import (
- "github.com/cloudevents/sdk-go/v1/binding"
- "github.com/cloudevents/sdk-go/v1/binding/spec"
- ce "github.com/cloudevents/sdk-go/v1/cloudevents"
-)
-
-// Returns a TransformerFactory that converts the event context version to the specified one.
-func Version(version spec.Version) binding.TransformerFactory {
- return versionTranscoderFactory{version: version}
-}
-
-type versionTranscoderFactory struct {
- version spec.Version
-}
-
-func (v versionTranscoderFactory) StructuredTransformer(binding.StructuredEncoder) binding.StructuredEncoder {
- return nil // Not supported, must fallback to EventTransformer!
-}
-
-func (v versionTranscoderFactory) BinaryTransformer(encoder binding.BinaryEncoder) binding.BinaryEncoder {
- return binaryVersionTransformer{BinaryEncoder: encoder, version: v.version}
-}
-
-func (v versionTranscoderFactory) EventTransformer() binding.EventTransformer {
- return func(e *ce.Event) error {
- e.Context = v.version.Convert(e.Context)
- return nil
- }
-}
-
-type binaryVersionTransformer struct {
- binding.BinaryEncoder
- version spec.Version
-}
-
-func (b binaryVersionTransformer) SetAttribute(attribute spec.Attribute, value interface{}) error {
- if attribute.Kind() == spec.SpecVersion {
- return b.BinaryEncoder.SetAttribute(b.version.AttributeFromKind(spec.SpecVersion), b.version.String())
- }
- attributeInDifferentVersion := b.version.AttributeFromKind(attribute.Kind())
- return b.BinaryEncoder.SetAttribute(attributeInDifferentVersion, value)
-}
diff --git a/v1/binding/transcoder/version_test.go b/v1/binding/transcoder/version_test.go
deleted file mode 100644
index 518fcafbf..000000000
--- a/v1/binding/transcoder/version_test.go
+++ /dev/null
@@ -1,56 +0,0 @@
-package transcoder
-
-import (
- "context"
- "net/url"
- "testing"
-
- "github.com/stretchr/testify/require"
-
- cloudevents "github.com/cloudevents/sdk-go/v1"
- "github.com/cloudevents/sdk-go/v1/binding"
- "github.com/cloudevents/sdk-go/v1/binding/spec"
- "github.com/cloudevents/sdk-go/v1/binding/test"
- "github.com/cloudevents/sdk-go/v1/cloudevents/types"
-)
-
-func TestVersionTranscoder(t *testing.T) {
- var testEventV02 = cloudevents.Event{
- Context: cloudevents.EventContextV02{
- Source: types.URLRef{URL: url.URL{Path: "source"}},
- ContentType: cloudevents.StringOfApplicationJSON(),
- ID: "id",
- Type: "type",
- }.AsV02(),
- }
-
- var testEventV1 = testEventV02
- testEventV1.Context = testEventV02.Context.AsV1()
-
- data := []byte("\"data\"")
- err := testEventV02.SetData(data)
- require.NoError(t, err)
- err = testEventV1.SetData(data)
- require.NoError(t, err)
-
- test.RunTranscoderTests(t, context.Background(), []test.TranscoderTestArgs{
- {
- Name: "V02 -> V1 with Mock Structured message",
- InputMessage: test.NewMockStructuredMessage(test.CopyEventContext(testEventV02)),
- WantEvent: test.CopyEventContext(testEventV1),
- Transformers: binding.TransformerFactories{Version(spec.V1)},
- },
- {
- Name: "V02 -> V1 with Mock Binary message",
- InputMessage: test.NewMockBinaryMessage(test.CopyEventContext(testEventV02)),
- WantEvent: test.CopyEventContext(testEventV1),
- Transformers: binding.TransformerFactories{Version(spec.V1)},
- },
- {
- Name: "V02 -> V1 with Event message",
- InputMessage: binding.EventMessage(test.CopyEventContext(testEventV02)),
- WantEvent: test.CopyEventContext(testEventV1),
- Transformers: binding.TransformerFactories{Version(spec.V1)},
- },
- })
-}
diff --git a/v1/binding/translate.go b/v1/binding/translate.go
deleted file mode 100644
index 7cc30d994..000000000
--- a/v1/binding/translate.go
+++ /dev/null
@@ -1,148 +0,0 @@
-package binding
-
-import (
- "context"
- "errors"
-
- ce "github.com/cloudevents/sdk-go/v1"
-)
-
-const (
- SKIP_DIRECT_STRUCTURED_ENCODING = "SKIP_DIRECT_STRUCTURED_ENCODING"
- SKIP_DIRECT_BINARY_ENCODING = "SKIP_DIRECT_BINARY_ENCODING"
- PREFERRED_EVENT_ENCODING = "PREFERRED_EVENT_ENCODING"
-)
-
-// Invokes the encoders. createRootStructuredEncoder and createRootBinaryEncoder could be null if the protocol doesn't support it
-//
-// Returns:
-// * EncodingStructured, nil if message was structured and correctly translated to Event
-// * EncodingBinary, nil if message was binary and correctly translated to Event
-// * EncodingStructured, err if message was structured but error happened during translation
-// * BinaryEncoding, err if message was binary but error happened during translation
-// * EncodingUnknown, ErrUnknownEncoding if message is not recognized
-func RunDirectEncoding(
- ctx context.Context,
- message Message,
- structuredEncoder StructuredEncoder,
- binaryEncoder BinaryEncoder,
- factories TransformerFactories,
-) (Encoding, error) {
- if structuredEncoder != nil && !GetOrDefaultFromCtx(ctx, SKIP_DIRECT_STRUCTURED_ENCODING, false).(bool) {
- // Wrap the transformers in the structured builder
- structuredEncoder = factories.StructuredTransformer(structuredEncoder)
-
- // StructuredTransformer could return nil if one of transcoders doesn't support
- // direct structured transcoding
- if structuredEncoder != nil {
- if err := message.Structured(ctx, structuredEncoder); err == nil {
- return EncodingStructured, nil
- } else if err != ErrNotStructured {
- return EncodingStructured, err
- }
- }
- }
-
- if binaryEncoder != nil && !GetOrDefaultFromCtx(ctx, SKIP_DIRECT_BINARY_ENCODING, false).(bool) {
- binaryEncoder = factories.BinaryTransformer(binaryEncoder)
- if binaryEncoder != nil {
- if err := message.Binary(ctx, binaryEncoder); err == nil {
- return EncodingBinary, nil
- } else if err != ErrNotBinary {
- return EncodingBinary, err
- }
- }
- }
-
- return EncodingUnknown, ErrUnknownEncoding
-}
-
-// This is the full algorithm to encode a Message using transformers:
-// 1. It first tries direct encoding using RunEncoders
-// 2. If no direct encoding is possible, it goes through ToEvent to generate an event representation
-// 3. Using the encoders previously defined
-// You can tweak the encoding process using the context decorators WithForceStructured, WithForceStructured, etc.
-// This function guarantees that transformers are invoked only one time during the encoding process.
-// Returns:
-// * EncodingStructured, nil if message was structured and correctly translated to Event
-// * EncodingBinary, nil if message was binary and correctly translated to Event
-// * EncodingStructured, err if message was structured but error happened during translation
-// * BinaryEncoding, err if message was binary but error happened during translation
-// * EncodingUnknown, ErrUnknownEncoding if message is not recognized
-func Encode(
- ctx context.Context,
- message Message,
- structuredEncoder StructuredEncoder,
- binaryEncoder BinaryEncoder,
- transformers TransformerFactories,
-) (Encoding, error) {
- enc := message.Encoding()
- var err error
- // Skip direct encoding if the event is an event message
- if enc != EncodingEvent {
- enc, err = RunDirectEncoding(ctx, message, structuredEncoder, binaryEncoder, transformers)
- if enc != EncodingUnknown {
- // Message directly encoded, nothing else to do here
- return enc, err
- }
- }
-
- var e ce.Event
- e, enc, err = ToEvent(ctx, message, transformers)
- if err != nil {
- return enc, err
- }
-
- message = EventMessage(e)
-
- if GetOrDefaultFromCtx(ctx, PREFERRED_EVENT_ENCODING, EncodingBinary).(Encoding) == EncodingStructured {
- if structuredEncoder != nil {
- return EncodingStructured, message.Structured(ctx, structuredEncoder)
- }
- if binaryEncoder != nil {
- return EncodingBinary, message.Binary(ctx, binaryEncoder)
- }
- } else {
- if binaryEncoder != nil {
- return EncodingBinary, message.Binary(ctx, binaryEncoder)
- }
- if structuredEncoder != nil {
- return EncodingStructured, message.Structured(ctx, structuredEncoder)
- }
- }
-
- return enc, errors.New("cannot find a suitable encoder to use from EventMessage")
-}
-
-// Skip direct structured to structured encoding during the encoding process
-func WithSkipDirectStructuredEncoding(ctx context.Context, skip bool) context.Context {
- return context.WithValue(ctx, SKIP_DIRECT_STRUCTURED_ENCODING, skip)
-}
-
-// Skip direct binary to binary encoding during the encoding process
-func WithSkipDirectBinaryEncoding(ctx context.Context, skip bool) context.Context {
- return context.WithValue(ctx, SKIP_DIRECT_BINARY_ENCODING, skip)
-}
-
-// Define the preferred encoding from event to message during the encoding process
-func WithPreferredEventEncoding(ctx context.Context, enc Encoding) context.Context {
- return context.WithValue(ctx, PREFERRED_EVENT_ENCODING, enc)
-}
-
-// Force structured encoding during the encoding process
-func WithForceStructured(ctx context.Context) context.Context {
- return context.WithValue(context.WithValue(ctx, PREFERRED_EVENT_ENCODING, EncodingStructured), SKIP_DIRECT_BINARY_ENCODING, true)
-}
-
-// Force binary encoding during the encoding process
-func WithForceBinary(ctx context.Context) context.Context {
- return context.WithValue(context.WithValue(ctx, PREFERRED_EVENT_ENCODING, EncodingBinary), SKIP_DIRECT_STRUCTURED_ENCODING, true)
-}
-
-func GetOrDefaultFromCtx(ctx context.Context, key string, def interface{}) interface{} {
- if val := ctx.Value(key); val != nil {
- return val
- } else {
- return def
- }
-}
diff --git a/v1/binding/transport.go b/v1/binding/transport.go
deleted file mode 100644
index cc17123a7..000000000
--- a/v1/binding/transport.go
+++ /dev/null
@@ -1,66 +0,0 @@
-package binding
-
-import (
- "context"
- "io"
-
- ce "github.com/cloudevents/sdk-go/v1/cloudevents"
- "github.com/cloudevents/sdk-go/v1/cloudevents/transport"
-)
-
-// BindingTransport implements transport.Transport using a Sender and Receiver.
-type BindingTransport struct {
- Sender Sender
- Receiver Receiver
- SenderContextDecorators []func(context.Context) context.Context
- handler transport.Receiver
-}
-
-var _ transport.Transport = (*BindingTransport)(nil) // Conforms to the interface
-
-func NewTransportAdapter(s Sender, r Receiver, senderContextDecorators []func(context.Context) context.Context) *BindingTransport {
- if senderContextDecorators == nil {
- senderContextDecorators = []func(ctx context.Context) context.Context{}
- }
- return &BindingTransport{Sender: s, Receiver: r, SenderContextDecorators: senderContextDecorators}
-}
-
-func (t *BindingTransport) Send(ctx context.Context, e ce.Event) (context.Context, *ce.Event, error) {
- for _, f := range t.SenderContextDecorators {
- ctx = f(ctx)
- }
- return ctx, nil, t.Sender.Send(ctx, EventMessage(e))
-}
-
-func (t *BindingTransport) SetReceiver(r transport.Receiver) { t.handler = r }
-
-func (t *BindingTransport) StartReceiver(ctx context.Context) error {
- for {
- m, err := t.Receiver.Receive(ctx)
- if err == io.EOF { // Normal close
- return nil
- } else if err != nil {
- return err
- }
- if err := t.handle(ctx, m); err != nil {
- return err
- }
- }
-}
-
-func (t *BindingTransport) handle(ctx context.Context, m Message) (err error) {
- defer func() {
- if err2 := m.Finish(err); err == nil {
- err = err2
- }
- }()
- e, _, err := ToEvent(ctx, m)
- if err != nil {
- return err
- }
- return t.handler.Receive(ctx, e, nil)
-}
-
-func (t *BindingTransport) SetConverter(transport.Converter) {}
-func (t *BindingTransport) HasConverter() bool { return false }
-func (t *BindingTransport) HasTracePropagation() bool { return false }
diff --git a/v1/binding/transport_test.go b/v1/binding/transport_test.go
deleted file mode 100644
index 40b7fa30b..000000000
--- a/v1/binding/transport_test.go
+++ /dev/null
@@ -1,51 +0,0 @@
-package binding_test
-
-import (
- "context"
- "testing"
-
- "github.com/stretchr/testify/require"
-
- cloudevents "github.com/cloudevents/sdk-go/v1"
- "github.com/cloudevents/sdk-go/v1/binding"
- "github.com/cloudevents/sdk-go/v1/binding/test"
-)
-
-func TestTransportSend(t *testing.T) {
- messageChannel := make(chan binding.Message, 1)
- transport := binding.NewTransportAdapter(binding.ChanSender(messageChannel), binding.ChanReceiver(messageChannel), nil)
- ev := test.MinEvent()
-
- client, err := cloudevents.NewClient(transport, cloudevents.WithoutTracePropagation())
- require.NoError(t, err)
-
- _, _, err = client.Send(context.Background(), ev)
- require.NoError(t, err)
-
- result := <-messageChannel
-
- test.AssertEventEquals(t, ev, cloudevents.Event(result.(binding.EventMessage)))
-}
-
-func TestTransportReceive(t *testing.T) {
- messageChannel := make(chan binding.Message, 1)
- eventReceivedChannel := make(chan cloudevents.Event, 1)
- transport := binding.NewTransportAdapter(binding.ChanSender(messageChannel), binding.ChanReceiver(messageChannel), nil)
- ev := test.MinEvent()
-
- client, err := cloudevents.NewClient(transport)
- require.NoError(t, err)
-
- messageChannel <- binding.EventMessage(ev)
-
- go func() {
- err = client.StartReceiver(context.Background(), func(event cloudevents.Event) {
- eventReceivedChannel <- event
- })
- require.NoError(t, err)
- }()
-
- result := <-eventReceivedChannel
-
- test.AssertEventEquals(t, ev, result)
-}
diff --git a/v1/bindings/amqp/amqp_test.go b/v1/bindings/amqp/amqp_test.go
deleted file mode 100644
index 9fb90d3d2..000000000
--- a/v1/bindings/amqp/amqp_test.go
+++ /dev/null
@@ -1,115 +0,0 @@
-// +build amqp
-
-package amqp
-
-import (
- "context"
- "io"
- "net/url"
- "os"
- "testing"
-
- "github.com/stretchr/testify/assert"
- "github.com/stretchr/testify/require"
- "pack.ag/amqp"
-
- "github.com/cloudevents/sdk-go/v1/binding"
- "github.com/cloudevents/sdk-go/v1/binding/test"
- ce "github.com/cloudevents/sdk-go/v1/cloudevents"
-)
-
-func TestSendSkipBinary(t *testing.T) {
- c, s, r := testSenderReceiver(t)
- defer c.Close()
- test.EachEvent(t, test.Events(), func(t *testing.T, e ce.Event) {
- eventIn := test.ExToStr(t, e)
- in := test.NewMockBinaryMessage(eventIn)
- test.SendReceive(t, binding.WithSkipDirectBinaryEncoding(binding.WithPreferredEventEncoding(context.Background(), binding.EncodingStructured), true), in, s, r, func(out binding.Message) {
- eventOut, encoding := test.MustToEvent(context.TODO(), out)
- assert.Equal(t, encoding, binding.EncodingStructured)
- test.AssertEventEquals(t, eventIn, test.ExToStr(t, eventOut))
- })
- })
-}
-
-func TestSendSkipStructured(t *testing.T) {
- c, s, r := testSenderReceiver(t)
- defer c.Close()
- test.EachEvent(t, test.Events(), func(t *testing.T, e ce.Event) {
- eventIn := test.ExToStr(t, e)
- in := test.NewMockStructuredMessage(eventIn)
- test.SendReceive(t, binding.WithSkipDirectStructuredEncoding(context.Background(), true), in, s, r, func(out binding.Message) {
- eventOut, encoding := test.MustToEvent(context.Background(), out)
- assert.Equal(t, encoding, binding.EncodingBinary)
- test.AssertEventEquals(t, eventIn, test.ExToStr(t, eventOut))
- })
- })
-}
-
-func TestSendEventReceiveStruct(t *testing.T) {
- c, s, r := testSenderReceiver(t)
- defer c.Close()
- test.EachEvent(t, test.Events(), func(t *testing.T, e ce.Event) {
- eventIn := test.ExToStr(t, e)
- in := binding.EventMessage(eventIn)
- test.SendReceive(t, binding.WithPreferredEventEncoding(context.TODO(), binding.EncodingStructured), in, s, r, func(out binding.Message) {
- eventOut, encoding := test.MustToEvent(context.Background(), out)
- assert.Equal(t, encoding, binding.EncodingStructured)
- test.AssertEventEquals(t, eventIn, test.ExToStr(t, eventOut))
- })
- })
-}
-
-func TestSendEventReceiveBinary(t *testing.T) {
- c, s, r := testSenderReceiver(t)
- defer c.Close()
- test.EachEvent(t, test.Events(), func(t *testing.T, e ce.Event) {
- eventIn := test.ExToStr(t, e)
- in := binding.EventMessage(eventIn)
- test.SendReceive(t, context.Background(), in, s, r, func(out binding.Message) {
- eventOut, encoding := test.MustToEvent(context.Background(), out)
- assert.Equal(t, encoding, binding.EncodingBinary)
- test.AssertEventEquals(t, eventIn, test.ExToStr(t, eventOut))
- })
- })
-}
-
-// Ideally add AMQP server support to the binding.
-
-// Some test require an AMQP broker or router. If the connection fails
-// the tests are skipped. The env variable TEST_AMQP_URL can be set to the
-// test URL, otherwise the default is "/test"
-//
-// On option is http://qpid.apache.org/components/dispatch-router/indexthtml.
-// It can be installed from source or from RPMs, see https://qpid.apache.org/packages.html
-// Run `qdrouterd` and the tests will work with no further config.
-func testClient(t testing.TB) (client *amqp.Client, session *amqp.Session, addr string) {
- t.Helper()
- addr = "test"
- s := os.Getenv("TEST_AMQP_URL")
- if u, err := url.Parse(s); err == nil && u.Path != "" {
- addr = u.Path
- }
- client, err := amqp.Dial(s)
- if err != nil {
- t.Skipf("ampq.Dial(%#v): %v", s, err)
- }
- session, err = client.NewSession()
- require.NoError(t, err)
- return client, session, addr
-}
-
-func testSenderReceiver(t testing.TB, senderOptions ...SenderOptionFunc) (io.Closer, binding.Sender, binding.Receiver) {
- c, ss, a := testClient(t)
- r, err := ss.NewReceiver(amqp.LinkSourceAddress(a))
- require.NoError(t, err)
- s, err := ss.NewSender(amqp.LinkTargetAddress(a))
- require.NoError(t, err)
- return c, NewSender(s, senderOptions...), &Receiver{r}
-}
-
-func BenchmarkSendReceive(b *testing.B) {
- c, s, r := testSenderReceiver(b)
- defer func() { require.NoError(b, c.Close()) }()
- test.BenchmarkSendReceive(b, s, r)
-}
diff --git a/v1/bindings/amqp/doc.go b/v1/bindings/amqp/doc.go
deleted file mode 100644
index e2062e4e8..000000000
--- a/v1/bindings/amqp/doc.go
+++ /dev/null
@@ -1,6 +0,0 @@
-/*
-Package amqp implements an AMQP binding.
-*/
-package amqp
-
-// TODO(slinkydeveloper)
diff --git a/v1/bindings/amqp/encoder.go b/v1/bindings/amqp/encoder.go
deleted file mode 100644
index 016bf2047..000000000
--- a/v1/bindings/amqp/encoder.go
+++ /dev/null
@@ -1,90 +0,0 @@
-package amqp
-
-import (
- "context"
- "io"
- "io/ioutil"
-
- "pack.ag/amqp"
-
- "github.com/cloudevents/sdk-go/v1/binding"
- "github.com/cloudevents/sdk-go/v1/binding/format"
- "github.com/cloudevents/sdk-go/v1/binding/spec"
- "github.com/cloudevents/sdk-go/v1/cloudevents/types"
-)
-
-// Fill the provided amqpMessage with the message m.
-// Using context you can tweak the encoding processing (more details on binding.Translate documentation).
-func EncodeAMQPMessage(ctx context.Context, m binding.Message, amqpMessage *amqp.Message, transformerFactories ...binding.TransformerFactory) error {
- structuredEncoder := (*amqpMessageEncoder)(amqpMessage)
- binaryEncoder := (*amqpMessageEncoder)(amqpMessage)
-
- _, err := binding.Encode(
- ctx,
- m,
- structuredEncoder,
- binaryEncoder,
- transformerFactories,
- )
- return err
-}
-
-type amqpMessageEncoder amqp.Message
-
-func (b *amqpMessageEncoder) SetStructuredEvent(ctx context.Context, format format.Format, event io.Reader) error {
- val, err := ioutil.ReadAll(event)
- if err != nil {
- return err
- }
- b.Data = [][]byte{val}
- b.Properties = &amqp.MessageProperties{ContentType: format.MediaType()}
- return nil
-}
-
-func (b *amqpMessageEncoder) Start(ctx context.Context) error {
- b.Properties = &amqp.MessageProperties{}
- b.ApplicationProperties = make(map[string]interface{})
- return nil
-}
-
-func (b *amqpMessageEncoder) End() error {
- return nil
-}
-
-func (b *amqpMessageEncoder) SetData(reader io.Reader) error {
- data, err := ioutil.ReadAll(reader)
- if err != nil {
- return err
- }
- b.Data = [][]byte{data}
- return nil
-}
-
-func (b *amqpMessageEncoder) SetAttribute(attribute spec.Attribute, value interface{}) error {
- if attribute.Kind() == spec.DataContentType {
- s, err := types.Format(value)
- if err != nil {
- return err
- }
- b.Properties.ContentType = s
- } else {
- v, err := safeAMQPPropertiesUnwrap(value)
- if err != nil {
- return err
- }
- b.ApplicationProperties[prefix+attribute.Name()] = v
- }
- return nil
-}
-
-func (b *amqpMessageEncoder) SetExtension(name string, value interface{}) error {
- v, err := safeAMQPPropertiesUnwrap(value)
- if err != nil {
- return err
- }
- b.ApplicationProperties[prefix+name] = v
- return nil
-}
-
-var _ binding.BinaryEncoder = (*amqpMessageEncoder)(nil) // Test it conforms to the interface
-var _ binding.StructuredEncoder = (*amqpMessageEncoder)(nil) // Test it conforms to the interface
diff --git a/v1/bindings/amqp/message.go b/v1/bindings/amqp/message.go
deleted file mode 100644
index 897f4a3b5..000000000
--- a/v1/bindings/amqp/message.go
+++ /dev/null
@@ -1,114 +0,0 @@
-package amqp
-
-import (
- "bytes"
- "context"
- "errors"
- "reflect"
- "strings"
-
- "pack.ag/amqp"
-
- "github.com/cloudevents/sdk-go/v1/binding"
- "github.com/cloudevents/sdk-go/v1/binding/format"
- "github.com/cloudevents/sdk-go/v1/binding/spec"
-)
-
-const prefix = "cloudEvents:" // Name prefix for AMQP properties that hold CE attributes.
-
-var (
- // Use the package path as AMQP error condition name
- condition = amqp.ErrorCondition(reflect.TypeOf(Message{}).PkgPath())
- specs = spec.WithPrefix(prefix)
-)
-
-// Message implements binding.Message by wrapping an *amqp.Message.
-type Message struct {
- AMQP *amqp.Message
- encoding binding.Encoding
-}
-
-func NewMessage(message *amqp.Message) *Message {
- if message.Properties != nil && format.IsFormat(message.Properties.ContentType) {
- return &Message{AMQP: message, encoding: binding.EncodingStructured}
- } else if _, err := specs.FindVersion(func(k string) string {
- s, _ := message.ApplicationProperties[k].(string)
- return s
- }); err == nil {
- return &Message{AMQP: message, encoding: binding.EncodingBinary}
- } else {
- return &Message{AMQP: message, encoding: binding.EncodingUnknown}
- }
-}
-
-// Check if amqp.Message implements binding.Message
-var _ binding.Message = (*Message)(nil)
-
-func (m *Message) Encoding() binding.Encoding {
- return m.encoding
-}
-
-func (m *Message) Structured(ctx context.Context, encoder binding.StructuredEncoder) error {
- if m.AMQP.Properties != nil && format.IsFormat(m.AMQP.Properties.ContentType) {
- return encoder.SetStructuredEvent(ctx, format.Lookup(m.AMQP.Properties.ContentType), bytes.NewReader(m.AMQP.GetData()))
- }
- return binding.ErrNotStructured
-}
-
-func (m *Message) Binary(ctx context.Context, encoder binding.BinaryEncoder) error {
- if len(m.AMQP.ApplicationProperties) == 0 {
- return errors.New("AMQP CloudEvents message has no application properties")
- }
- version, err := specs.FindVersion(func(k string) string {
- s, _ := m.AMQP.ApplicationProperties[k].(string)
- return s
- })
- if err != nil {
- return err
- }
-
- err = encoder.Start(ctx)
- if err != nil {
- return err
- }
-
- if m.AMQP.Properties != nil && m.AMQP.Properties.ContentType != "" {
- err = encoder.SetAttribute(version.AttributeFromKind(spec.DataContentType), m.AMQP.Properties.ContentType)
- if err != nil {
- return err
- }
- }
-
- for k, v := range m.AMQP.ApplicationProperties {
- if strings.HasPrefix(k, prefix) {
- attr := version.Attribute(k)
- if attr != nil {
- err = encoder.SetAttribute(attr, v)
- } else {
- err = encoder.SetExtension(strings.ToLower(strings.TrimPrefix(k, prefix)), v)
- }
- }
- if err != nil {
- return err
- }
- }
-
- data := m.AMQP.GetData()
- if len(data) != 0 { // Some data
- err = encoder.SetData(bytes.NewReader(data))
- if err != nil {
- return err
- }
- }
- return encoder.End()
-}
-
-func (m *Message) Finish(err error) error {
- if err != nil {
- return m.AMQP.Reject(&amqp.Error{
- Condition: condition,
- Description: err.Error(),
- })
- }
- return m.AMQP.Accept()
-}
diff --git a/v1/bindings/amqp/option.go b/v1/bindings/amqp/option.go
deleted file mode 100644
index 448b8323d..000000000
--- a/v1/bindings/amqp/option.go
+++ /dev/null
@@ -1,11 +0,0 @@
-package amqp
-
-import "github.com/cloudevents/sdk-go/v1/binding"
-
-type SenderOptionFunc func(sender *Sender)
-
-func WithTranscoder(factory binding.TransformerFactory) SenderOptionFunc {
- return func(sender *Sender) {
- sender.transformerFactories = append(sender.transformerFactories, factory)
- }
-}
diff --git a/v1/bindings/amqp/receiver.go b/v1/bindings/amqp/receiver.go
deleted file mode 100644
index 287dbb667..000000000
--- a/v1/bindings/amqp/receiver.go
+++ /dev/null
@@ -1,22 +0,0 @@
-package amqp
-
-import (
- "context"
-
- "github.com/cloudevents/sdk-go/v1/binding"
- "pack.ag/amqp"
-)
-
-// Receiver wraps an amqp.Receiver as a binding.Receiver
-type Receiver struct{ AMQP *amqp.Receiver }
-
-func (r *Receiver) Receive(ctx context.Context) (binding.Message, error) {
- m, err := r.AMQP.Receive(ctx)
- if err != nil {
- return nil, err
- }
-
- return NewMessage(m), nil
-}
-
-func (r *Receiver) Close(ctx context.Context) error { return r.AMQP.Close(ctx) }
diff --git a/v1/bindings/amqp/sender.go b/v1/bindings/amqp/sender.go
deleted file mode 100644
index b3eaaaa58..000000000
--- a/v1/bindings/amqp/sender.go
+++ /dev/null
@@ -1,42 +0,0 @@
-package amqp
-
-import (
- "context"
-
- "pack.ag/amqp"
-
- "github.com/cloudevents/sdk-go/v1/binding"
-)
-
-// Sender wraps an amqp.Sender as a binding.Sender
-type Sender struct {
- AMQP *amqp.Sender
-
- transformerFactories binding.TransformerFactories
-}
-
-func (s *Sender) Send(ctx context.Context, in binding.Message) error {
- var err error
- defer func() { _ = in.Finish(err) }()
- if m, ok := in.(*Message); ok { // Already an AMQP message.
- return s.AMQP.Send(ctx, m.AMQP)
- }
-
- var amqpMessage amqp.Message
- err = EncodeAMQPMessage(ctx, in, &amqpMessage, s.transformerFactories)
- if err != nil {
- return err
- }
-
- return s.AMQP.Send(ctx, &amqpMessage)
-}
-
-func (s *Sender) Close(ctx context.Context) error { return s.AMQP.Close(ctx) }
-
-func NewSender(amqpClient *amqp.Sender, options ...SenderOptionFunc) binding.Sender {
- s := &Sender{AMQP: amqpClient, transformerFactories: make(binding.TransformerFactories, 0)}
- for _, o := range options {
- o(s)
- }
- return s
-}
diff --git a/v1/bindings/amqp/types.go b/v1/bindings/amqp/types.go
deleted file mode 100644
index 02174cd10..000000000
--- a/v1/bindings/amqp/types.go
+++ /dev/null
@@ -1,24 +0,0 @@
-package amqp
-
-import "github.com/cloudevents/sdk-go/v1/cloudevents/types"
-
-func safeAMQPPropertiesUnwrap(val interface{}) (interface{}, error) {
- v, err := types.Validate(val)
- if err != nil {
- return nil, err
- }
- switch t := v.(type) {
- case types.URI: // Use string form of URLs.
- v = t.String()
- case types.URIRef: // Use string form of URLs.
- v = t.String()
- case types.URLRef: // Use string form of URLs.
- v = t.String()
- case types.Timestamp: // Use string form of URLs.
- v = t.Time
- case int32: // Use AMQP long for Integer as per CE spec.
- v = int64(t)
- }
-
- return v, nil
-}
diff --git a/v1/bindings/doc.go b/v1/bindings/doc.go
deleted file mode 100644
index b0a91eefc..000000000
--- a/v1/bindings/doc.go
+++ /dev/null
@@ -1,5 +0,0 @@
-// Package bindings contains packages that implement different protocol bindings.
-//
-// Package ../binding provides interfaces and helper functions for implementing
-// and using protocol bindings in a uniform way.
-package bindings
diff --git a/v1/bindings/http/doc.go b/v1/bindings/http/doc.go
deleted file mode 100644
index 312fc21d4..000000000
--- a/v1/bindings/http/doc.go
+++ /dev/null
@@ -1,6 +0,0 @@
-// Package http implements an HTTP CloudEvents binding.
-package http
-
-// TODO(alanconway, slinkydeveloper)
-// - different kinds of sender/receiver: long poll, event as response, etc.
-// - review blocking, error handling, closing.
diff --git a/v1/bindings/http/http_test.go b/v1/bindings/http/http_test.go
deleted file mode 100644
index 402d55446..000000000
--- a/v1/bindings/http/http_test.go
+++ /dev/null
@@ -1,102 +0,0 @@
-package http_test
-
-import (
- "context"
- nethttp "net/http"
- "net/http/httptest"
- "net/url"
- "testing"
-
- "github.com/stretchr/testify/assert"
- "github.com/stretchr/testify/require"
-
- "github.com/cloudevents/sdk-go/v1/binding"
- "github.com/cloudevents/sdk-go/v1/binding/test"
- "github.com/cloudevents/sdk-go/v1/bindings/http"
- ce "github.com/cloudevents/sdk-go/v1/cloudevents"
-)
-
-func TestSendSkipBinary(t *testing.T) {
- close, s, r := testSenderReceiver(t)
- defer close()
- test.EachEvent(t, test.Events(), func(t *testing.T, eventIn ce.Event) {
- eventIn = test.ExToStr(t, eventIn)
- in := test.NewMockBinaryMessage(eventIn)
- test.SendReceive(t, binding.WithSkipDirectBinaryEncoding(binding.WithPreferredEventEncoding(context.Background(), binding.EncodingStructured), true), in, s, r, func(out binding.Message) {
- eventOut, encoding := test.MustToEvent(context.Background(), out)
- assert.Equal(t, encoding, binding.EncodingStructured)
- test.AssertEventEquals(t, eventIn, test.ExToStr(t, eventOut))
- })
- })
-}
-
-func TestSendSkipStructured(t *testing.T) {
- close, s, r := testSenderReceiver(t)
- defer close()
- test.EachEvent(t, test.Events(), func(t *testing.T, eventIn ce.Event) {
- eventIn = test.ExToStr(t, eventIn)
- in := test.NewMockStructuredMessage(eventIn)
- test.SendReceive(t, binding.WithSkipDirectStructuredEncoding(context.Background(), true), in, s, r, func(out binding.Message) {
- eventOut, encoding := test.MustToEvent(context.Background(), out)
- assert.Equal(t, encoding, binding.EncodingBinary)
- test.AssertEventEquals(t, eventIn, test.ExToStr(t, eventOut))
- })
- })
-}
-
-func TestSendBinaryReceiveBinary(t *testing.T) {
- close, s, r := testSenderReceiver(t)
- defer close()
- test.EachEvent(t, test.Events(), func(t *testing.T, eventIn ce.Event) {
- eventIn = test.ExToStr(t, eventIn)
- in := test.NewMockBinaryMessage(eventIn)
- test.SendReceive(t, context.Background(), in, s, r, func(out binding.Message) {
- eventOut, encoding := test.MustToEvent(context.Background(), out)
- assert.Equal(t, encoding, binding.EncodingBinary)
- test.AssertEventEquals(t, eventIn, test.ExToStr(t, eventOut))
- })
- })
-}
-
-func TestSendStructReceiveStruct(t *testing.T) {
- close, s, r := testSenderReceiver(t)
- defer close()
- test.EachEvent(t, test.Events(), func(t *testing.T, eventIn ce.Event) {
- eventIn = test.ExToStr(t, eventIn)
- in := test.NewMockStructuredMessage(eventIn)
- test.SendReceive(t, context.Background(), in, s, r, func(out binding.Message) {
- eventOut, encoding := test.MustToEvent(context.Background(), out)
- assert.Equal(t, encoding, binding.EncodingStructured)
- test.AssertEventEquals(t, eventIn, test.ExToStr(t, eventOut))
- })
- })
-}
-
-func TestSendEventReceiveBinary(t *testing.T) {
- close, s, r := testSenderReceiver(t)
- defer close()
- test.EachEvent(t, test.Events(), func(t *testing.T, eventIn ce.Event) {
- eventIn = test.ExToStr(t, eventIn)
- in := binding.EventMessage(eventIn)
- test.SendReceive(t, context.Background(), in, s, r, func(out binding.Message) {
- eventOut, encoding := test.MustToEvent(context.Background(), out)
- assert.Equal(t, encoding, binding.EncodingBinary)
- test.AssertEventEquals(t, eventIn, test.ExToStr(t, eventOut))
- })
- })
-}
-
-func testSenderReceiver(t testing.TB, options ...http.SenderOptionFunc) (func(), binding.Sender, binding.Receiver) {
- r := http.NewReceiver() // Parameters? Capacity, sync.
- srv := httptest.NewServer(r)
- u, err := url.Parse(srv.URL)
- require.NoError(t, err)
- s := http.NewSender(&nethttp.Client{}, u, options...) // Capacity, sync etc.
- return func() { srv.Close() }, s, r
-}
-
-func BenchmarkSendReceive(b *testing.B) {
- c, s, r := testSenderReceiver(b)
- defer c() // Cleanup
- test.BenchmarkSendReceive(b, s, r)
-}
diff --git a/v1/bindings/http/message.go b/v1/bindings/http/message.go
deleted file mode 100644
index 943c3864e..000000000
--- a/v1/bindings/http/message.go
+++ /dev/null
@@ -1,105 +0,0 @@
-package http
-
-import (
- "context"
- "io"
- nethttp "net/http"
- "strings"
-
- "github.com/cloudevents/sdk-go/v1/binding"
- "github.com/cloudevents/sdk-go/v1/binding/format"
- "github.com/cloudevents/sdk-go/v1/binding/spec"
-)
-
-const prefix = "Ce-"
-
-var specs = spec.WithPrefix(prefix)
-
-const ContentType = "Content-Type"
-
-// Message holds the Header and Body of a HTTP Request or Response.
-type Message struct {
- Header nethttp.Header
- BodyReader io.ReadCloser
- encoding binding.Encoding
- OnFinish func(error) error
-}
-
-// Check if http.Message implements binding.Message
-var _ binding.Message = (*Message)(nil)
-
-// NewMessage returns a Message with header and data from body.
-// Reads and closes body.
-func NewMessage(header nethttp.Header, body io.ReadCloser) (*Message, error) {
- m := Message{Header: header}
- if body != nil {
- m.BodyReader = body
- }
- if ft := format.Lookup(header.Get(ContentType)); ft == nil {
- m.encoding = binding.EncodingStructured
- } else if _, err := specs.FindVersion(m.Header.Get); err != nil {
- m.encoding = binding.EncodingBinary
- } else {
- m.encoding = binding.EncodingUnknown
- }
- return &m, nil
-}
-
-func (m *Message) Encoding() binding.Encoding {
- return m.encoding
-}
-
-func (m *Message) Structured(ctx context.Context, encoder binding.StructuredEncoder) error {
- if ft := format.Lookup(m.Header.Get(ContentType)); ft == nil {
- return binding.ErrNotStructured
- } else {
- return encoder.SetStructuredEvent(ctx, ft, m.BodyReader)
- }
-}
-
-func (m *Message) Binary(ctx context.Context, encoder binding.BinaryEncoder) error {
- version, err := specs.FindVersion(m.Header.Get)
- if err != nil {
- return binding.ErrNotBinary
- }
-
- err = encoder.Start(ctx)
- if err != nil {
- return err
- }
-
- for k, v := range m.Header {
- if strings.HasPrefix(k, prefix) {
- attr := version.Attribute(k)
- if attr != nil {
- err = encoder.SetAttribute(attr, v[0])
- } else {
- err = encoder.SetExtension(strings.ToLower(strings.TrimPrefix(k, prefix)), v[0])
- }
- } else if k == ContentType {
- err = encoder.SetAttribute(version.AttributeFromKind(spec.DataContentType), v[0])
- }
- if err != nil {
- return err
- }
- }
-
- if m.BodyReader != nil {
- err = encoder.SetData(m.BodyReader)
- if err != nil {
- return err
- }
- }
-
- return encoder.End()
-}
-
-func (m *Message) Finish(err error) error {
- if m.BodyReader != nil {
- _ = m.BodyReader.Close()
- }
- if m.OnFinish != nil {
- return m.OnFinish(err)
- }
- return nil
-}
diff --git a/v1/bindings/http/option.go b/v1/bindings/http/option.go
deleted file mode 100644
index 032562513..000000000
--- a/v1/bindings/http/option.go
+++ /dev/null
@@ -1,11 +0,0 @@
-package http
-
-import "github.com/cloudevents/sdk-go/v1/binding"
-
-type SenderOptionFunc func(sender *Sender)
-
-func WithTranscoder(factory binding.TransformerFactory) SenderOptionFunc {
- return func(sender *Sender) {
- sender.transformerFactories = append(sender.transformerFactories, factory)
- }
-}
diff --git a/v1/bindings/http/receiver.go b/v1/bindings/http/receiver.go
deleted file mode 100644
index 3531173ee..000000000
--- a/v1/bindings/http/receiver.go
+++ /dev/null
@@ -1,53 +0,0 @@
-package http
-
-import (
- "context"
- "fmt"
- "io"
- "net/http"
- nethttp "net/http"
-
- "github.com/cloudevents/sdk-go/v1/binding"
-)
-
-type msgErr struct {
- msg *Message
- err error
-}
-
-// Receiver for CloudEvents as HTTP requests.
-// Implements http.Handler, To receive messages, associate it with a http.Server.
-type Receiver struct {
- incoming chan msgErr
-}
-
-// ServeHTTP implements http.Handler.
-// Blocks until Message.Finish is called.
-func (r *Receiver) ServeHTTP(rw http.ResponseWriter, req *http.Request) {
- m, err := NewMessage(req.Header, req.Body)
- if err != nil {
- r.incoming <- msgErr{nil, err}
- }
- done := make(chan error)
- m.OnFinish = func(err error) error { done <- err; return nil }
- r.incoming <- msgErr{m, err} // Send to Receive()
- if err = <-done; err != nil {
- nethttp.Error(rw, fmt.Sprintf("cannot forward CloudEvent: %v", err), http.StatusInternalServerError)
- }
-}
-
-// NewReceiver creates a receiver
-func NewReceiver() *Receiver {
- return &Receiver{incoming: make(chan msgErr)}
-}
-
-// Receive the next incoming HTTP request as a CloudEvent.
-// Returns non-nil error if the incoming HTTP request fails to parse as a CloudEvent
-// Returns io.EOF if the receiver is closed.
-func (r *Receiver) Receive(ctx context.Context) (binding.Message, error) {
- msgErr, ok := <-r.incoming
- if !ok {
- return nil, io.EOF
- }
- return msgErr.msg, msgErr.err
-}
diff --git a/v1/bindings/http/request_encoder.go b/v1/bindings/http/request_encoder.go
deleted file mode 100644
index 1124aac14..000000000
--- a/v1/bindings/http/request_encoder.go
+++ /dev/null
@@ -1,78 +0,0 @@
-package http
-
-import (
- "context"
- "io"
- "io/ioutil"
- "net/http"
-
- "github.com/cloudevents/sdk-go/v1/binding"
- "github.com/cloudevents/sdk-go/v1/binding/format"
- "github.com/cloudevents/sdk-go/v1/binding/spec"
- "github.com/cloudevents/sdk-go/v1/cloudevents/types"
-)
-
-// Fill the provided req with the message m.
-// Using context you can tweak the encoding processing (more details on binding.Translate documentation).
-func EncodeHttpRequest(ctx context.Context, m binding.Message, req *http.Request, transformerFactories binding.TransformerFactories) error {
- structuredEncoder := (*httpRequestEncoder)(req)
- binaryEncoder := (*httpRequestEncoder)(req)
-
- _, err := binding.Encode(
- ctx,
- m,
- structuredEncoder,
- binaryEncoder,
- transformerFactories,
- )
- return err
-}
-
-type httpRequestEncoder http.Request
-
-func (b *httpRequestEncoder) SetStructuredEvent(ctx context.Context, format format.Format, event io.Reader) error {
- b.Header.Set(ContentType, format.MediaType())
- b.Body = ioutil.NopCloser(event)
- return nil
-}
-
-func (b *httpRequestEncoder) Start(ctx context.Context) error {
- return nil
-}
-
-func (b *httpRequestEncoder) End() error {
- return nil
-}
-
-func (b *httpRequestEncoder) SetData(reader io.Reader) error {
- b.Body = ioutil.NopCloser(reader)
- return nil
-}
-
-func (b *httpRequestEncoder) SetAttribute(attribute spec.Attribute, value interface{}) error {
- // Http headers, everything is a string!
- s, err := types.Format(value)
- if err != nil {
- return err
- }
-
- if attribute.Kind() == spec.DataContentType {
- b.Header.Add(ContentType, s)
- } else {
- b.Header.Add(prefix+attribute.Name(), s)
- }
- return nil
-}
-
-func (b *httpRequestEncoder) SetExtension(name string, value interface{}) error {
- // Http headers, everything is a string!
- s, err := types.Format(value)
- if err != nil {
- return err
- }
- b.Header.Add(prefix+name, s)
- return nil
-}
-
-var _ binding.StructuredEncoder = (*httpRequestEncoder)(nil) // Test it conforms to the interface
-var _ binding.BinaryEncoder = (*httpRequestEncoder)(nil) // Test it conforms to the interface
diff --git a/v1/bindings/http/response_encoder.go b/v1/bindings/http/response_encoder.go
deleted file mode 100644
index 47165ef99..000000000
--- a/v1/bindings/http/response_encoder.go
+++ /dev/null
@@ -1,78 +0,0 @@
-package http
-
-import (
- "context"
- "io"
- "io/ioutil"
- "net/http"
-
- "github.com/cloudevents/sdk-go/v1/binding"
- "github.com/cloudevents/sdk-go/v1/binding/format"
- "github.com/cloudevents/sdk-go/v1/binding/spec"
- "github.com/cloudevents/sdk-go/v1/cloudevents/types"
-)
-
-// Fill the provided res with the message m.
-// Using context you can tweak the encoding processing (more details on binding.Translate documentation).
-func EncodeHttpResponse(ctx context.Context, m binding.Message, res *http.Response, transformerFactories binding.TransformerFactories) error {
- structuredEncoder := (*httpResponseEncoder)(res)
- binaryEncoder := (*httpResponseEncoder)(res)
-
- _, err := binding.Encode(
- ctx,
- m,
- structuredEncoder,
- binaryEncoder,
- transformerFactories,
- )
- return err
-}
-
-type httpResponseEncoder http.Response
-
-func (b *httpResponseEncoder) SetStructuredEvent(ctx context.Context, format format.Format, event io.Reader) error {
- b.Header.Set(ContentType, format.MediaType())
- b.Body = ioutil.NopCloser(event)
- return nil
-}
-
-func (b *httpResponseEncoder) Start(ctx context.Context) error {
- return nil
-}
-
-func (b *httpResponseEncoder) End() error {
- return nil
-}
-
-func (b *httpResponseEncoder) SetData(reader io.Reader) error {
- b.Body = ioutil.NopCloser(reader)
- return nil
-}
-
-func (b *httpResponseEncoder) SetAttribute(attribute spec.Attribute, value interface{}) error {
- // Http headers, everything is a string!
- s, err := types.Format(value)
- if err != nil {
- return err
- }
-
- if attribute.Kind() == spec.DataContentType {
- b.Header.Add(ContentType, s)
- } else {
- b.Header.Add(prefix+attribute.Name(), s)
- }
- return nil
-}
-
-func (b *httpResponseEncoder) SetExtension(name string, value interface{}) error {
- // Http headers, everything is a string!
- s, err := types.Format(value)
- if err != nil {
- return err
- }
- b.Header.Add(prefix+name, s)
- return nil
-}
-
-var _ binding.StructuredEncoder = (*httpResponseEncoder)(nil) // Test it conforms to the interface
-var _ binding.BinaryEncoder = (*httpResponseEncoder)(nil) // Test it conforms to the interface
diff --git a/v1/bindings/http/response_encoder_test.go b/v1/bindings/http/response_encoder_test.go
deleted file mode 100644
index 6015069ac..000000000
--- a/v1/bindings/http/response_encoder_test.go
+++ /dev/null
@@ -1,72 +0,0 @@
-package http
-
-import (
- "context"
- "testing"
-
- "github.com/stretchr/testify/require"
-
- "net/http"
-
- cloudevents "github.com/cloudevents/sdk-go/v1"
- "github.com/cloudevents/sdk-go/v1/binding"
- "github.com/cloudevents/sdk-go/v1/binding/test"
- ce "github.com/cloudevents/sdk-go/v1/cloudevents"
-)
-
-func TestEncodeHttpResponse(t *testing.T) {
- tests := []struct {
- name string
- context context.Context
- messageFactory func(e cloudevents.Event) binding.Message
- expectedEncoding binding.Encoding
- }{
- {
- name: "Structured to Structured",
- context: context.TODO(),
- messageFactory: test.NewMockStructuredMessage,
- expectedEncoding: binding.EncodingStructured,
- },
- {
- name: "Binary to Binary",
- context: context.TODO(),
- messageFactory: test.NewMockBinaryMessage,
- expectedEncoding: binding.EncodingBinary,
- },
- {
- name: "Event to Structured",
- context: binding.WithPreferredEventEncoding(context.TODO(), binding.EncodingStructured),
- messageFactory: func(e cloudevents.Event) binding.Message { return binding.EventMessage(e) },
- expectedEncoding: binding.EncodingStructured,
- },
- {
- name: "Event to Binary",
- context: binding.WithPreferredEventEncoding(context.TODO(), binding.EncodingBinary),
- messageFactory: func(e cloudevents.Event) binding.Message { return binding.EventMessage(e) },
- expectedEncoding: binding.EncodingBinary,
- },
- }
- for _, tt := range tests {
- test.EachEvent(t, test.Events(), func(t *testing.T, eventIn ce.Event) {
- t.Run(tt.name, func(t *testing.T) {
- res := &http.Response{
- Header: make(http.Header),
- }
-
- eventIn = test.ExToStr(t, eventIn)
- messageIn := tt.messageFactory(eventIn)
-
- err := EncodeHttpResponse(tt.context, messageIn, res, binding.TransformerFactories{})
- require.NoError(t, err)
-
- //Little hack to go back to Message
- messageOut, err := NewMessage(res.Header, res.Body)
- require.NoError(t, err)
-
- eventOut, encoding, err := binding.ToEvent(context.TODO(), messageOut)
- require.Equal(t, encoding, tt.expectedEncoding)
- test.AssertEventEquals(t, eventIn, eventOut)
- })
- })
- }
-}
diff --git a/v1/bindings/http/sender.go b/v1/bindings/http/sender.go
deleted file mode 100644
index 4c638d954..000000000
--- a/v1/bindings/http/sender.go
+++ /dev/null
@@ -1,54 +0,0 @@
-package http
-
-import (
- "context"
- "fmt"
- "net/http"
- nethttp "net/http"
- "net/url"
-
- "github.com/cloudevents/sdk-go/v1/binding"
-)
-
-type Sender struct {
- // Client is the HTTP client used to send events as HTTP requests
- Client *http.Client
- // Target is the URL to send event requests to.
- Target *url.URL
-
- transformerFactories binding.TransformerFactories
-}
-
-func (s *Sender) Send(ctx context.Context, m binding.Message) (err error) {
- defer func() { _ = m.Finish(err) }()
- if s.Client == nil || s.Target == nil {
- return fmt.Errorf("not initialized: %#v", s)
- }
-
- var req *http.Request
- req, err = http.NewRequest("POST", s.Target.String(), nil)
- if err != nil {
- return
- }
- req = req.WithContext(ctx)
-
- if err = EncodeHttpRequest(ctx, m, req, s.transformerFactories); err != nil {
- return
- }
- resp, err := s.Client.Do(req)
- if err != nil {
- return
- }
- if resp.StatusCode/100 != 2 {
- return fmt.Errorf("%d %s", resp.StatusCode, nethttp.StatusText(resp.StatusCode))
- }
- return
-}
-
-func NewSender(client *http.Client, target *url.URL, options ...SenderOptionFunc) binding.Sender {
- s := &Sender{Client: client, Target: target, transformerFactories: make(binding.TransformerFactories, 0)}
- for _, o := range options {
- o(s)
- }
- return s
-}
diff --git a/v1/bindings/kafka_sarama/doc.go b/v1/bindings/kafka_sarama/doc.go
deleted file mode 100644
index 96f40f4f5..000000000
--- a/v1/bindings/kafka_sarama/doc.go
+++ /dev/null
@@ -1,4 +0,0 @@
-// Package kafka implements the Kafka CloudEvents binding.
-package kafka_sarama
-
-// TODO(slinkydeveloper)
diff --git a/v1/bindings/kafka_sarama/kafka_test.go b/v1/bindings/kafka_sarama/kafka_test.go
deleted file mode 100644
index a7e8f3692..000000000
--- a/v1/bindings/kafka_sarama/kafka_test.go
+++ /dev/null
@@ -1,188 +0,0 @@
-// +build kafka
-
-package kafka_sarama
-
-import (
- "context"
- "os"
- "strings"
- "testing"
-
- "github.com/google/uuid"
- "github.com/stretchr/testify/assert"
- "github.com/stretchr/testify/require"
-
- "github.com/Shopify/sarama"
-
- cloudevents "github.com/cloudevents/sdk-go/v1"
- "github.com/cloudevents/sdk-go/v1/binding"
- "github.com/cloudevents/sdk-go/v1/binding/test"
-)
-
-const (
- TEST_GROUP_ID = "test_group_id"
-)
-
-var (
- e = test.FullEvent()
- StructuredConsumerMessageWithoutKey = &sarama.ConsumerMessage{
- Value: test.MustJSON(e),
- Headers: []*sarama.RecordHeader{{
- Key: []byte(ContentType),
- Value: []byte(cloudevents.ApplicationCloudEventsJSON),
- }},
- }
- StructuredConsumerMessageWithKey = &sarama.ConsumerMessage{
- Key: []byte("aaa"),
- Value: test.MustJSON(e),
- Headers: []*sarama.RecordHeader{{
- Key: []byte(ContentType),
- Value: []byte(cloudevents.ApplicationCloudEventsJSON),
- }},
- }
- BinaryConsumerMessageWithoutKey = &sarama.ConsumerMessage{
- Value: []byte("hello world!"),
- Headers: mustToSaramaConsumerHeaders(map[string]string{
- "ce_type": e.Type(),
- "ce_source": e.Source(),
- "ce_id": e.ID(),
- "ce_time": test.Timestamp.String(),
- "ce_specversion": "1.0",
- "ce_dataschema": test.Schema.String(),
- "ce_datacontenttype": "text/json",
- "ce_subject": "topic",
- "ce_exta": "someext",
- }),
- }
- BinaryConsumerMessageWithKey = &sarama.ConsumerMessage{
- Key: []byte("akey"),
- Value: []byte("hello world!"),
- Headers: mustToSaramaConsumerHeaders(map[string]string{
- "ce_type": e.Type(),
- "ce_source": e.Source(),
- "ce_id": e.ID(),
- "ce_time": test.Timestamp.String(),
- "ce_specversion": "1.0",
- "ce_dataschema": test.Schema.String(),
- "ce_datacontenttype": "text/json",
- "ce_subject": "topic",
- "ce_exta": "someext",
- }),
- }
-)
-
-func TestSendStructuredMessageToStructuredWithKey(t *testing.T) {
- close, s, r := testSenderReceiver(t)
- defer close()
- test.EachEvent(t, test.Events(), func(t *testing.T, eventIn cloudevents.Event) {
- eventIn = test.ExToStr(t, eventIn)
- require.NoError(t, eventIn.Context.SetExtension("key", "aaa"))
-
- in := test.NewMockStructuredMessage(eventIn)
- test.SendReceive(t, binding.WithPreferredEventEncoding(context.TODO(), binding.EncodingStructured), in, s, r, func(out binding.Message) {
- eventOut, encoding := test.MustToEvent(context.Background(), out)
- assert.Equal(t, binding.EncodingEvent, encoding)
- test.AssertEventEquals(t, eventIn, test.ExToStr(t, eventOut))
- })
- })
-}
-
-func TestSendStructuredMessageToStructuredWithoutKey(t *testing.T) {
- close, s, r := testSenderReceiver(t)
- defer close()
- test.EachEvent(t, test.Events(), func(t *testing.T, eventIn cloudevents.Event) {
- eventIn = test.ExToStr(t, eventIn)
-
- in := test.NewMockStructuredMessage(eventIn)
- test.SendReceive(t, binding.WithPreferredEventEncoding(context.TODO(), binding.EncodingStructured), in, s, r, func(out binding.Message) {
- eventOut, encoding := test.MustToEvent(context.Background(), out)
- assert.Equal(t, binding.EncodingStructured, encoding)
- test.AssertEventEquals(t, eventIn, test.ExToStr(t, eventOut))
- })
- })
-}
-
-func TestSendBinaryMessageToBinaryWithKey(t *testing.T) {
- close, s, r := testSenderReceiver(t)
- defer close()
- test.EachEvent(t, test.Events(), func(t *testing.T, eventIn cloudevents.Event) {
- eventIn = test.ExToStr(t, eventIn)
- require.NoError(t, eventIn.Context.SetExtension("key", "aaa"))
-
- in := test.NewMockBinaryMessage(eventIn)
- test.SendReceive(t, binding.WithPreferredEventEncoding(context.TODO(), binding.EncodingBinary), in, s, r, func(out binding.Message) {
- eventOut, encoding := test.MustToEvent(context.Background(), out)
- assert.Equal(t, binding.EncodingBinary, encoding)
- test.AssertEventEquals(t, eventIn, test.ExToStr(t, eventOut))
- })
- })
-}
-
-func TestSendBinaryMessageToBinaryWithoutKey(t *testing.T) {
- close, s, r := testSenderReceiver(t)
- defer close()
- test.EachEvent(t, test.Events(), func(t *testing.T, eventIn cloudevents.Event) {
- eventIn = test.ExToStr(t, eventIn)
-
- in := test.NewMockBinaryMessage(eventIn)
- test.SendReceive(t, binding.WithPreferredEventEncoding(context.TODO(), binding.EncodingBinary), in, s, r, func(out binding.Message) {
- eventOut, encoding := test.MustToEvent(context.Background(), out)
- assert.Equal(t, binding.EncodingBinary, encoding)
- test.AssertEventEquals(t, eventIn, test.ExToStr(t, eventOut))
- })
- })
-}
-
-func testClient(t testing.TB) sarama.Client {
- t.Helper()
- s := os.Getenv("TEST_KAFKA_BOOTSTRAP_SERVER")
- if s == "" {
- s = "localhost:9092"
- }
-
- config := sarama.NewConfig()
- config.Version = sarama.V2_0_0_0
- config.Producer.Return.Successes = true
- config.Producer.Return.Errors = true
- config.Consumer.Offsets.Initial = sarama.OffsetOldest
- client, err := sarama.NewClient(strings.Split(s, ","), config)
- if err != nil {
- t.Skipf("Cannot create sarama client to servers [%s]: %v", s, err)
- }
-
- return client
-}
-
-func testSenderReceiver(t testing.TB, options ...SenderOptionFunc) (func(), binding.Sender, binding.Receiver) {
- client := testClient(t)
-
- topicName := "test-ce-client-" + uuid.New().String()
- r := NewReceiver(client, TEST_GROUP_ID, topicName)
- s, err := NewSender(client, topicName, options...)
- require.NoError(t, err)
-
- return func() {
- err = r.Close(context.TODO())
- require.NoError(t, err)
- err = s.Close(context.TODO())
- require.NoError(t, err)
- err = client.Close()
- require.NoError(t, err)
- }, s, r
-}
-
-func BenchmarkSendReceive(b *testing.B) {
- c, s, r := testSenderReceiver(b)
- defer c() // Cleanup
- test.BenchmarkSendReceive(b, s, r)
-}
-
-func mustToSaramaConsumerHeaders(m map[string]string) []*sarama.RecordHeader {
- res := make([]*sarama.RecordHeader, len(m))
- i := 0
- for k, v := range m {
- res[i] = &sarama.RecordHeader{Key: []byte(k), Value: []byte(v)}
- i++
- }
- return res
-}
diff --git a/v1/bindings/kafka_sarama/message.go b/v1/bindings/kafka_sarama/message.go
deleted file mode 100644
index a36241c50..000000000
--- a/v1/bindings/kafka_sarama/message.go
+++ /dev/null
@@ -1,153 +0,0 @@
-package kafka_sarama
-
-import (
- "bytes"
- "context"
- "strings"
-
- ce "github.com/cloudevents/sdk-go/v1"
- "github.com/cloudevents/sdk-go/v1/binding"
- "github.com/cloudevents/sdk-go/v1/binding/format"
- "github.com/cloudevents/sdk-go/v1/binding/spec"
-
- "github.com/Shopify/sarama"
-)
-
-const prefix = "ce_"
-
-var specs = spec.WithPrefix(prefix)
-
-const ContentType = "content-type"
-
-// Message holds a sarama ConsumerMessage.
-type Message struct {
- Key, Value []byte
- Headers map[string][]byte
- ContentType string
- format format.Format
- version spec.Version
-}
-
-// Check if http.Message implements binding.Message
-var _ binding.Message = (*Message)(nil)
-
-// NewMessage returns a Message with data from body.
-// Reads and closes body.
-func NewMessage(cm *sarama.ConsumerMessage) (binding.Message, error) {
- var contentType string
- headers := make(map[string][]byte, len(cm.Headers))
- for _, r := range cm.Headers {
- k := strings.ToLower(string(r.Key))
- if k == ContentType {
- contentType = string(r.Value)
- }
- headers[strings.ToLower(string(r.Key))] = r.Value
- }
- return NewMessageFromRaw(cm.Key, cm.Value, contentType, headers)
-}
-
-func NewMessageFromRaw(key []byte, value []byte, contentType string, headers map[string][]byte) (binding.Message, error) {
- if ft := format.Lookup(contentType); ft != nil {
- // if the message is structured and has a key,
- // then it's cheaper to go through event message
- // because we need to add the key as extension
- if key != nil {
- event := ce.Event{}
- err := ft.Unmarshal(value, &event)
- if err != nil {
- return nil, err
- }
- event.SetExtension("key", string(key))
- return binding.EventMessage(event), nil
- } else {
- return &Message{
- Key: key,
- Value: value,
- ContentType: contentType,
- Headers: headers,
- format: ft,
- }, nil
- }
- } else if v, err := specs.FindVersion(func(s string) string {
- return string(headers[strings.ToLower(s)])
- }); err == nil {
- return &Message{
- Key: key,
- Value: value,
- ContentType: contentType,
- Headers: headers,
- version: v,
- }, nil
- }
-
- return &Message{
- Key: key,
- Value: value,
- ContentType: contentType,
- Headers: headers,
- }, nil
-}
-
-func (m *Message) Encoding() binding.Encoding {
- if m.version != nil {
- return binding.EncodingBinary
- }
- if m.format != nil {
- return binding.EncodingStructured
- }
- return binding.EncodingUnknown
-}
-
-func (m *Message) Structured(ctx context.Context, encoder binding.StructuredEncoder) error {
- if m.format != nil {
- return encoder.SetStructuredEvent(ctx, m.format, bytes.NewReader(m.Value))
- }
- return binding.ErrNotStructured
-}
-
-func (m *Message) Binary(ctx context.Context, encoder binding.BinaryEncoder) error {
- if m.version == nil {
- return binding.ErrNotBinary
- }
-
- err := encoder.Start(ctx)
- if err != nil {
- return err
- }
-
- for k, v := range m.Headers {
- if strings.HasPrefix(k, prefix) {
- attr := m.version.Attribute(k)
- if attr != nil {
- err = encoder.SetAttribute(attr, string(v))
- } else {
- err = encoder.SetExtension(strings.ToLower(strings.TrimPrefix(k, prefix)), string(v))
- }
- } else if k == ContentType {
- err = encoder.SetAttribute(m.version.AttributeFromKind(spec.DataContentType), string(v))
- }
- if err != nil {
- return err
- }
- }
-
- if m.Key != nil {
- err = encoder.SetExtension("key", string(m.Key))
- if err != nil {
- return err
- }
- }
-
- if m.Value != nil {
- err = encoder.SetData(bytes.NewReader(m.Value))
- if err != nil {
- return err
- }
- }
-
- return encoder.End()
-}
-
-func (m *Message) Finish(error) error {
- return nil
-}
diff --git a/v1/bindings/kafka_sarama/message_benchmark_test.go b/v1/bindings/kafka_sarama/message_benchmark_test.go
deleted file mode 100644
index 6fe6bd2a3..000000000
--- a/v1/bindings/kafka_sarama/message_benchmark_test.go
+++ /dev/null
@@ -1,69 +0,0 @@
-// +build kafka
-
-package kafka_sarama_test
-
-import (
- "context"
- "testing"
-
- cloudevents "github.com/cloudevents/sdk-go/v1"
- "github.com/cloudevents/sdk-go/v1/binding"
- "github.com/cloudevents/sdk-go/v1/bindings/kafka_sarama"
-)
-
-// Avoid DCE
-var M binding.Message
-var Event cloudevents.Event
-var Err error
-
-func BenchmarkNewStructuredMessageWithoutKey(b *testing.B) {
- for i := 0; i < b.N; i++ {
- M, Err = kafka_sarama.NewMessage(kafka_sarama.StructuredConsumerMessageWithoutKey)
- }
-}
-
-func BenchmarkNewStructuredMessageWithKey(b *testing.B) {
- for i := 0; i < b.N; i++ {
- M, Err = kafka_sarama.NewMessage(kafka_sarama.StructuredConsumerMessageWithKey)
- }
-}
-
-func BenchmarkNewBinaryMessageWithoutKey(b *testing.B) {
- for i := 0; i < b.N; i++ {
- M, Err = kafka_sarama.NewMessage(kafka_sarama.BinaryConsumerMessageWithoutKey)
- }
-}
-
-func BenchmarkNewBinaryMessageWithKey(b *testing.B) {
- for i := 0; i < b.N; i++ {
- M, Err = kafka_sarama.NewMessage(kafka_sarama.BinaryConsumerMessageWithKey)
- }
-}
-
-func BenchmarkNewStructuredMessageWithoutKeyToEvent(b *testing.B) {
- for i := 0; i < b.N; i++ {
- M, Err = kafka_sarama.NewMessage(kafka_sarama.StructuredConsumerMessageWithoutKey)
- Event, _, Err = binding.ToEvent(context.TODO(), M)
- }
-}
-
-func BenchmarkNewStructuredMessageWithKeyToEvent(b *testing.B) {
- for i := 0; i < b.N; i++ {
- M, Err = kafka_sarama.NewMessage(kafka_sarama.StructuredConsumerMessageWithKey)
- Event, _, Err = binding.ToEvent(context.TODO(), M)
- }
-}
-
-func BenchmarkNewBinaryMessageWithoutKeyToEvent(b *testing.B) {
- for i := 0; i < b.N; i++ {
- M, Err = kafka_sarama.NewMessage(kafka_sarama.BinaryConsumerMessageWithoutKey)
- Event, _, Err = binding.ToEvent(context.TODO(), M)
- }
-}
-
-func BenchmarkNewBinaryMessageWithKeyToEvent(b *testing.B) {
- for i := 0; i < b.N; i++ {
- M, Err = kafka_sarama.NewMessage(kafka_sarama.BinaryConsumerMessageWithKey)
- Event, _, Err = binding.ToEvent(context.TODO(), M)
- }
-}
diff --git a/v1/bindings/kafka_sarama/option.go b/v1/bindings/kafka_sarama/option.go
deleted file mode 100644
index 076b1d057..000000000
--- a/v1/bindings/kafka_sarama/option.go
+++ /dev/null
@@ -1,11 +0,0 @@
-package kafka_sarama
-
-import "github.com/cloudevents/sdk-go/v1/binding"
-
-type SenderOptionFunc func(sender *Sender)
-
-func WithTranscoder(factory binding.TransformerFactory) SenderOptionFunc {
- return func(sender *Sender) {
- sender.transformerFactories = append(sender.transformerFactories, factory)
- }
-}
diff --git a/v1/bindings/kafka_sarama/producer_message_encoder.go b/v1/bindings/kafka_sarama/producer_message_encoder.go
deleted file mode 100644
index edc12fb54..000000000
--- a/v1/bindings/kafka_sarama/producer_message_encoder.go
+++ /dev/null
@@ -1,170 +0,0 @@
-package kafka_sarama
-
-import (
- "bytes"
- "context"
- "io"
-
- "github.com/Shopify/sarama"
-
- ce "github.com/cloudevents/sdk-go/v1"
- "github.com/cloudevents/sdk-go/v1/binding"
- "github.com/cloudevents/sdk-go/v1/binding/format"
- "github.com/cloudevents/sdk-go/v1/binding/spec"
- "github.com/cloudevents/sdk-go/v1/cloudevents/types"
-)
-
-const (
- SKIP_KEY_EXTENSION = "SKIP_KEY_EXTENSION"
-)
-
-// Fill the provided producerMessage with the message m.
-// Using context you can tweak the encoding processing (more details on binding.Translate documentation).
-func EncodeKafkaProducerMessage(ctx context.Context, m binding.Message, producerMessage *sarama.ProducerMessage, transformerFactories binding.TransformerFactories) error {
- skipKey := binding.GetOrDefaultFromCtx(ctx, SKIP_KEY_EXTENSION, false).(bool)
-
- if skipKey {
- enc := &kafkaProducerMessageEncoder{
- producerMessage,
- skipKey,
- }
-
- _, err := binding.Encode(
- ctx,
- m,
- enc,
- enc,
- transformerFactories,
- )
- return err
- }
-
- enc := m.Encoding()
- var err error
- // Skip direct encoding if the event is an event message
- if enc == binding.EncodingBinary {
- encoder := &kafkaProducerMessageEncoder{
- producerMessage,
- skipKey,
- }
- enc, err = binding.RunDirectEncoding(ctx, m, nil, encoder, transformerFactories)
- if enc != binding.EncodingUnknown {
- // Message directly encoded binary -> binary, nothing else to do here
- return err
- }
- }
-
- var e ce.Event
- e, _, err = binding.ToEvent(ctx, m, transformerFactories)
- if err != nil {
- return err
- }
-
- if val, ok := e.Extensions()["key"]; ok {
- s, err := types.Format(val)
- if err != nil {
- return err
- }
-
- producerMessage.Key = sarama.StringEncoder(s)
- }
-
- eventMessage := binding.EventMessage(e)
-
- encoder := &kafkaProducerMessageEncoder{
- producerMessage,
- skipKey,
- }
-
- if binding.GetOrDefaultFromCtx(ctx, binding.PREFERRED_EVENT_ENCODING, binding.EncodingBinary).(binding.Encoding) == binding.EncodingStructured {
- return eventMessage.Structured(ctx, encoder)
- } else {
- return eventMessage.Binary(ctx, encoder)
- }
-}
-
-type kafkaProducerMessageEncoder struct {
- *sarama.ProducerMessage
- skipKey bool
-}
-
-func (b *kafkaProducerMessageEncoder) SetStructuredEvent(ctx context.Context, format format.Format, event io.Reader) error {
- b.Headers = []sarama.RecordHeader{{
- Key: []byte(ContentType),
- Value: []byte(format.MediaType()),
- }}
-
- var buf bytes.Buffer
- _, err := io.Copy(&buf, event)
- if err != nil {
- return err
- }
-
- b.Value = sarama.ByteEncoder(buf.Bytes())
- return nil
-}
-
-func (b *kafkaProducerMessageEncoder) Start(ctx context.Context) error {
- b.Headers = []sarama.RecordHeader{}
- return nil
-}
-
-func (b *kafkaProducerMessageEncoder) End() error {
- return nil
-}
-
-func (b *kafkaProducerMessageEncoder) SetData(reader io.Reader) error {
- var buf bytes.Buffer
- _, err := io.Copy(&buf, reader)
- if err != nil {
- return err
- }
-
- b.Value = sarama.ByteEncoder(buf.Bytes())
- return nil
-}
-
-func (b *kafkaProducerMessageEncoder) SetAttribute(attribute spec.Attribute, value interface{}) error {
- // Everything is a string here
- s, err := types.Format(value)
- if err != nil {
- return err
- }
-
- if attribute.Kind() == spec.DataContentType {
- b.Headers = append(b.Headers, sarama.RecordHeader{Key: []byte(ContentType), Value: []byte(s)})
- } else {
- b.Headers = append(b.Headers, sarama.RecordHeader{Key: []byte(prefix + attribute.Name()), Value: []byte(s)})
- }
- return nil
-}
-
-func (b *kafkaProducerMessageEncoder) SetExtension(name string, value interface{}) error {
- if !b.skipKey && name == "key" {
- if v, ok := value.([]byte); ok {
- b.Key = sarama.ByteEncoder(v)
- } else {
- s, err := types.Format(value)
- if err != nil {
- return err
- }
- b.Key = sarama.ByteEncoder(s)
- }
- return nil
- }
-
- // Http headers, everything is a string!
- s, err := types.Format(value)
- if err != nil {
- return err
- }
- b.Headers = append(b.Headers, sarama.RecordHeader{Key: []byte(prefix + name), Value: []byte(s)})
- return nil
-}
-
-var _ binding.StructuredEncoder = (*kafkaProducerMessageEncoder)(nil) // Test it conforms to the interface
-var _ binding.BinaryEncoder = (*kafkaProducerMessageEncoder)(nil) // Test it conforms to the interface
-
-func WithSkipKeyExtension(ctx context.Context) context.Context {
- return context.WithValue(ctx, SKIP_KEY_EXTENSION, true)
-}
diff --git a/v1/bindings/kafka_sarama/producer_message_encoder_benchmark_test.go b/v1/bindings/kafka_sarama/producer_message_encoder_benchmark_test.go
deleted file mode 100644
index 4cb847fe7..000000000
--- a/v1/bindings/kafka_sarama/producer_message_encoder_benchmark_test.go
+++ /dev/null
@@ -1,71 +0,0 @@
-// +build kafka
-
-package kafka_sarama_test
-
-import (
- "context"
- "testing"
-
- "github.com/Shopify/sarama"
-
- cloudevents "github.com/cloudevents/sdk-go/v1"
- "github.com/cloudevents/sdk-go/v1/binding"
- "github.com/cloudevents/sdk-go/v1/binding/test"
- "github.com/cloudevents/sdk-go/v1/bindings/kafka_sarama"
-)
-
-// Avoid DCE
-var ProducerMessage *sarama.ProducerMessage
-
-var (
- ctxSkipKey context.Context
- ctx context.Context
- eventWithoutKey cloudevents.Event
- eventWithKey cloudevents.Event
- structuredMessageWithoutKey binding.Message
- structuredMessageWithKey binding.Message
- binaryMessageWithoutKey binding.Message
- binaryMessageWithKey binding.Message
-)
-
-func init() {
- ctxSkipKey = kafka_sarama.WithSkipKeyExtension(context.TODO())
- ctx = context.TODO()
-
- eventWithoutKey = test.FullEvent()
- eventWithKey = test.FullEvent()
- eventWithKey.SetExtension("key", "aaaaaa")
-
- structuredMessageWithoutKey = test.NewMockStructuredMessage(eventWithoutKey)
- structuredMessageWithKey = test.NewMockStructuredMessage(eventWithKey)
- binaryMessageWithoutKey = test.NewMockBinaryMessage(eventWithoutKey)
- binaryMessageWithKey = test.NewMockBinaryMessage(eventWithKey)
-}
-
-func BenchmarkEncodeStructuredMessageSkipKey(b *testing.B) {
- for i := 0; i < b.N; i++ {
- ProducerMessage = &sarama.ProducerMessage{}
- Err = kafka_sarama.EncodeKafkaProducerMessage(ctxSkipKey, structuredMessageWithoutKey, ProducerMessage, binding.TransformerFactories{})
- }
-}
-
-func BenchmarkEncodeStructuredMessage(b *testing.B) {
- for i := 0; i < b.N; i++ {
- ProducerMessage = &sarama.ProducerMessage{}
- Err = kafka_sarama.EncodeKafkaProducerMessage(ctx, structuredMessageWithKey, ProducerMessage, binding.TransformerFactories{})
- }
-}
-
-func BenchmarkEncodeBinaryMessageSkipKey(b *testing.B) {
- for i := 0; i < b.N; i++ {
- ProducerMessage = &sarama.ProducerMessage{}
- Err = kafka_sarama.EncodeKafkaProducerMessage(ctxSkipKey, binaryMessageWithoutKey, ProducerMessage, binding.TransformerFactories{})
- }
-}
-
-func BenchmarkEncodeBinaryMessage(b *testing.B) {
- for i := 0; i < b.N; i++ {
- ProducerMessage = &sarama.ProducerMessage{}
- Err = kafka_sarama.EncodeKafkaProducerMessage(ctx, binaryMessageWithKey, ProducerMessage, binding.TransformerFactories{})
- }
-}
diff --git a/v1/bindings/kafka_sarama/producer_message_encoder_test.go b/v1/bindings/kafka_sarama/producer_message_encoder_test.go
deleted file mode 100644
index 3cb89ea68..000000000
--- a/v1/bindings/kafka_sarama/producer_message_encoder_test.go
+++ /dev/null
@@ -1,137 +0,0 @@
-// +build kafka
-
-package kafka_sarama
-
-import (
- "context"
- "strings"
- "testing"
-
- "github.com/Shopify/sarama"
- "github.com/stretchr/testify/require"
-
- cloudevents "github.com/cloudevents/sdk-go/v1"
- "github.com/cloudevents/sdk-go/v1/binding"
- "github.com/cloudevents/sdk-go/v1/binding/test"
- ce "github.com/cloudevents/sdk-go/v1/cloudevents"
-)
-
-func TestEncodeKafkaProducerMessage(t *testing.T) {
- tests := []struct {
- name string
- context context.Context
- messageFactory func(e cloudevents.Event) binding.Message
- expectedEncoding binding.Encoding
- skipKey bool
- }{
- {
- name: "Structured to Structured with Skip key",
- context: context.TODO(),
- messageFactory: func(e cloudevents.Event) binding.Message { return test.NewMockStructuredMessage(e) },
- expectedEncoding: binding.EncodingStructured,
- skipKey: true,
- },
- {
- name: "Binary to Binary with Skip key",
- context: context.TODO(),
- messageFactory: func(e cloudevents.Event) binding.Message { return test.NewMockBinaryMessage(e) },
- expectedEncoding: binding.EncodingBinary,
- skipKey: true,
- },
- {
- name: "Event to Structured with Skip key",
- context: binding.WithPreferredEventEncoding(context.TODO(), binding.EncodingStructured),
- messageFactory: func(e cloudevents.Event) binding.Message { return binding.EventMessage(e) },
- expectedEncoding: binding.EncodingStructured,
- skipKey: true,
- },
- {
- name: "Event to Binary with Skip key",
- context: binding.WithPreferredEventEncoding(context.TODO(), binding.EncodingBinary),
- messageFactory: func(e cloudevents.Event) binding.Message { return binding.EventMessage(e) },
- expectedEncoding: binding.EncodingBinary,
- skipKey: true,
- },
- {
- name: "Structured to Structured",
- context: binding.WithPreferredEventEncoding(context.TODO(), binding.EncodingStructured),
- messageFactory: func(e cloudevents.Event) binding.Message { return test.NewMockStructuredMessage(e) },
- expectedEncoding: binding.EncodingEvent,
- skipKey: false,
- },
- {
- name: "Binary to Binary",
- context: context.TODO(),
- messageFactory: func(e cloudevents.Event) binding.Message { return test.NewMockBinaryMessage(e) },
- expectedEncoding: binding.EncodingBinary,
- skipKey: false,
- },
- {
- name: "Event to Structured",
- context: binding.WithPreferredEventEncoding(context.TODO(), binding.EncodingStructured),
- messageFactory: func(e cloudevents.Event) binding.Message { return binding.EventMessage(e) },
- expectedEncoding: binding.EncodingEvent,
- skipKey: false,
- },
- {
- name: "Event to Binary",
- context: binding.WithPreferredEventEncoding(context.TODO(), binding.EncodingBinary),
- messageFactory: func(e cloudevents.Event) binding.Message { return binding.EventMessage(e) },
- expectedEncoding: binding.EncodingBinary,
- skipKey: false,
- },
- }
- for _, tt := range tests {
- test.EachEvent(t, test.Events(), func(t *testing.T, eventIn ce.Event) {
- t.Run(tt.name, func(t *testing.T) {
- ctx := tt.context
-
- if tt.skipKey {
- ctx = WithSkipKeyExtension(ctx)
- } else {
- eventIn.SetExtension("key", "bla")
- }
-
- kafkaMessage := &sarama.ProducerMessage{
- Topic: "aaa",
- }
-
- eventIn = test.ExToStr(t, eventIn)
- messageIn := tt.messageFactory(eventIn)
-
- err := EncodeKafkaProducerMessage(ctx, messageIn, kafkaMessage, binding.TransformerFactories{})
- require.NoError(t, err)
-
- //Little hack to go back to Message
- headers := make(map[string][]byte)
- for _, h := range kafkaMessage.Headers {
- headers[strings.ToLower(string(h.Key))] = h.Value
- }
-
- var key []byte
- if kafkaMessage.Key != nil {
- key, err = kafkaMessage.Key.Encode()
- require.NoError(t, err)
- }
-
- var value []byte
- if kafkaMessage.Value != nil {
- value, err = kafkaMessage.Value.Encode()
- require.NoError(t, err)
- }
-
- if !tt.skipKey {
- require.Equal(t, []byte("bla"), key)
- }
-
- messageOut, err := NewMessageFromRaw(key, value, string(headers[ContentType]), headers)
- require.NoError(t, err)
-
- eventOut, encoding, err := binding.ToEvent(context.TODO(), messageOut)
- require.NoError(t, err)
- require.Equal(t, tt.expectedEncoding, encoding)
- test.AssertEventEquals(t, eventIn, eventOut)
- })
- })
- }
-}
diff --git a/v1/bindings/kafka_sarama/receiver.go b/v1/bindings/kafka_sarama/receiver.go
deleted file mode 100644
index 95587f747..000000000
--- a/v1/bindings/kafka_sarama/receiver.go
+++ /dev/null
@@ -1,87 +0,0 @@
-package kafka_sarama
-
-import (
- "context"
- "io"
-
- "github.com/Shopify/sarama"
-
- "github.com/cloudevents/sdk-go/v1/binding"
-)
-
-type msgErr struct {
- msg binding.Message
- err error
-}
-
-type Receiver struct {
- incoming chan msgErr
-
- client sarama.Client
- topic string
- groupId string
- saramaConsumerGroup sarama.ConsumerGroup
-}
-
-func (r *Receiver) Setup(sess sarama.ConsumerGroupSession) error {
- return nil
-}
-
-func (r *Receiver) Cleanup(sarama.ConsumerGroupSession) error {
- return nil
-}
-
-func (r *Receiver) ConsumeClaim(session sarama.ConsumerGroupSession, claim sarama.ConsumerGroupClaim) error {
- for message := range claim.Messages() {
- m, err := NewMessage(message)
-
- if err != nil {
- r.incoming <- msgErr{err: err}
- } else {
- r.incoming <- msgErr{
- msg: binding.WithFinish(m, func(err error) { session.MarkMessage(message, "") }),
- }
- }
-
- }
- return nil
-}
-
-func NewReceiver(client sarama.Client, groupId string, topic string) *Receiver {
- return &Receiver{
- incoming: make(chan msgErr),
- client: client,
- groupId: groupId,
- topic: topic,
- }
-}
-
-func (r *Receiver) Receive(ctx context.Context) (binding.Message, error) {
- // Consumer Group not started!
- if r.saramaConsumerGroup == nil {
- cg, err := sarama.NewConsumerGroupFromClient(r.groupId, r.client)
- if err != nil {
- return nil, err
- }
- r.saramaConsumerGroup = cg
-
- go func() {
- if err := cg.Consume(ctx, []string{r.topic}, r); err != nil {
- r.incoming <- msgErr{err: err}
- }
- }()
- }
-
- msgErr, ok := <-r.incoming
- if !ok {
- return nil, io.EOF
- }
- return msgErr.msg, msgErr.err
-}
-
-func (r *Receiver) Close(ctx context.Context) error {
- if r.saramaConsumerGroup != nil {
- return r.saramaConsumerGroup.Close()
- }
- return nil
-}
diff --git a/v1/bindings/kafka_sarama/sender.go b/v1/bindings/kafka_sarama/sender.go
deleted file mode 100644
index 58eda4b52..000000000
--- a/v1/bindings/kafka_sarama/sender.go
+++ /dev/null
@@ -1,48 +0,0 @@
-package kafka_sarama
-
-import (
- "context"
-
- "github.com/Shopify/sarama"
-
- "github.com/cloudevents/sdk-go/v1/binding"
-)
-
-type Sender struct {
- topic string
- syncProducer sarama.SyncProducer
-
- transformerFactories binding.TransformerFactories
-}
-
-func (s *Sender) Send(ctx context.Context, m binding.Message) error {
- kafkaMessage := sarama.ProducerMessage{Topic: s.topic}
-
- if err := EncodeKafkaProducerMessage(ctx, m, &kafkaMessage, s.transformerFactories); err != nil {
- return err
- }
-
- _, _, err := s.syncProducer.SendMessage(&kafkaMessage)
- return err
-}
-
-func (s *Sender) Close(ctx context.Context) error {
- return s.syncProducer.Close()
-}
-
-func NewSender(client sarama.Client, topic string, options ...SenderOptionFunc) (*Sender, error) {
- producer, err := sarama.NewSyncProducerFromClient(client)
- if err != nil {
- return nil, err
- }
-
- s := &Sender{
- topic: topic,
- syncProducer: producer,
- transformerFactories: make(binding.TransformerFactories, 0),
- }
- for _, o := range options {
- o(s)
- }
- return s, nil
-}
diff --git a/v1/cloudevents/client/client.go b/v1/cloudevents/client/client.go
deleted file mode 100644
index 55721a024..000000000
--- a/v1/cloudevents/client/client.go
+++ /dev/null
@@ -1,226 +0,0 @@
-package client
-
-import (
- "context"
- "fmt"
- "sync"
-
- "github.com/cloudevents/sdk-go/v1/cloudevents"
- "github.com/cloudevents/sdk-go/v1/cloudevents/extensions"
- "github.com/cloudevents/sdk-go/v1/cloudevents/observability"
- "github.com/cloudevents/sdk-go/v1/cloudevents/transport"
- "github.com/cloudevents/sdk-go/v1/cloudevents/transport/http"
- "go.opencensus.io/trace"
-)
-
-// Client interface defines the runtime contract the CloudEvents client supports.
-type Client interface {
- // Send will transmit the given event over the client's configured transport.
- Send(ctx context.Context, event cloudevents.Event) (context.Context, *cloudevents.Event, error)
-
- // StartReceiver will register the provided function for callback on receipt
- // of a cloudevent. It will also start the underlying transport as it has
- // been configured.
- // This call is blocking.
- // Valid fn signatures are:
- // * func()
- // * func() error
- // * func(context.Context)
- // * func(context.Context) error
- // * func(cloudevents.Event)
- // * func(cloudevents.Event) error
- // * func(context.Context, cloudevents.Event)
- // * func(context.Context, cloudevents.Event) error
- // * func(cloudevents.Event, *cloudevents.EventResponse)
- // * func(cloudevents.Event, *cloudevents.EventResponse) error
- // * func(context.Context, cloudevents.Event, *cloudevents.EventResponse)
- // * func(context.Context, cloudevents.Event, *cloudevents.EventResponse) error
- // Note: if fn returns an error, it is treated as a critical and
- // EventResponse will not be processed.
- StartReceiver(ctx context.Context, fn interface{}) error
-}
-
-// New produces a new client with the provided transport object and applied
-// client options.
-func New(t transport.Transport, opts ...Option) (Client, error) {
- c := &ceClient{
- transport: t,
- }
- if err := c.applyOptions(opts...); err != nil {
- return nil, err
- }
- t.SetReceiver(c)
- return c, nil
-}
-
-// NewDefault provides the good defaults for the common case using an HTTP
-// Transport client. The http transport has had WithBinaryEncoding http
-// transport option applied to it. The client will always send Binary
-// encoding but will inspect the outbound event context and match the version.
-// The WithTimeNow, WithUUIDs and WithDataContentType("application/json")
-// client options are also applied to the client, all outbound events will have
-// a time and id set if not already present.
-func NewDefault() (Client, error) {
- t, err := http.New(http.WithBinaryEncoding())
- if err != nil {
- return nil, err
- }
- c, err := New(t, WithTimeNow(), WithUUIDs(), WithDataContentType(cloudevents.ApplicationJSON))
- if err != nil {
- return nil, err
- }
- return c, nil
-}
-
-type ceClient struct {
- transport transport.Transport
- fn *receiverFn
-
- convertFn ConvertFn
-
- receiverMu sync.Mutex
- eventDefaulterFns []EventDefaulter
-
- disableTracePropagation bool
-}
-
-// Send transmits the provided event on a preconfigured Transport.
-// Send returns a response event if there is a response or an error if there
-// was an an issue validating the outbound event or the transport returns an
-// error.
-func (c *ceClient) Send(ctx context.Context, event cloudevents.Event) (context.Context, *cloudevents.Event, error) {
- ctx, r := observability.NewReporter(ctx, reportSend)
-
- ctx, span := trace.StartSpan(ctx, clientSpanName, trace.WithSpanKind(trace.SpanKindClient))
- defer span.End()
- if span.IsRecordingEvents() {
- span.AddAttributes(eventTraceAttributes(event.Context)...)
- }
-
- rctx, resp, err := c.obsSend(ctx, event)
- if err != nil {
- r.Error()
- } else {
- r.OK()
- }
- return rctx, resp, err
-}
-
-func (c *ceClient) obsSend(ctx context.Context, event cloudevents.Event) (context.Context, *cloudevents.Event, error) {
- // Confirm we have a transport set.
- if c.transport == nil {
- return ctx, nil, fmt.Errorf("client not ready, transport not initialized")
- }
- // Apply the defaulter chain to the incoming event.
- if len(c.eventDefaulterFns) > 0 {
- for _, fn := range c.eventDefaulterFns {
- event = fn(ctx, event)
- }
- }
-
- // Set distributed tracing extension.
- if !c.disableTracePropagation {
- if span := trace.FromContext(ctx); span != nil {
- event.Context = event.Context.Clone()
- if err := extensions.FromSpanContext(span.SpanContext()).AddTracingAttributes(event.Context); err != nil {
- return ctx, nil, fmt.Errorf("error setting distributed tracing extension: %w", err)
- }
- }
- }
-
- // Validate the event conforms to the CloudEvents Spec.
- if err := event.Validate(); err != nil {
- return ctx, nil, err
- }
- // Send the event over the transport.
- return c.transport.Send(ctx, event)
-}
-
-// Receive is called from from the transport on event delivery.
-func (c *ceClient) Receive(ctx context.Context, event cloudevents.Event, resp *cloudevents.EventResponse) error {
- ctx, r := observability.NewReporter(ctx, reportReceive)
-
- var span *trace.Span
- if !c.transport.HasTracePropagation() {
- if ext, ok := extensions.GetDistributedTracingExtension(event); ok {
- ctx, span = ext.StartChildSpan(ctx, clientSpanName, trace.WithSpanKind(trace.SpanKindServer))
- }
- }
- if span == nil {
- ctx, span = trace.StartSpan(ctx, clientSpanName, trace.WithSpanKind(trace.SpanKindServer))
- }
- defer span.End()
- if span.IsRecordingEvents() {
- span.AddAttributes(eventTraceAttributes(event.Context)...)
- }
-
- err := c.obsReceive(ctx, event, resp)
- if err != nil {
- r.Error()
- } else {
- r.OK()
- }
- return err
-}
-
-func (c *ceClient) obsReceive(ctx context.Context, event cloudevents.Event, resp *cloudevents.EventResponse) error {
- if c.fn != nil {
- err := c.fn.invoke(ctx, event, resp)
-
- // Apply the defaulter chain to the outgoing event.
- if err == nil && resp != nil && resp.Event != nil && len(c.eventDefaulterFns) > 0 {
- for _, fn := range c.eventDefaulterFns {
- *resp.Event = fn(ctx, *resp.Event)
- }
- // Validate the event conforms to the CloudEvents Spec.
- if err := resp.Event.Validate(); err != nil {
- return fmt.Errorf("cloudevent validation failed on response event: %v", err)
- }
- }
- return err
- }
- return nil
-}
-
-// StartReceiver sets up the given fn to handle Receive.
-// See Client.StartReceiver for details. This is a blocking call.
-func (c *ceClient) StartReceiver(ctx context.Context, fn interface{}) error {
- c.receiverMu.Lock()
- defer c.receiverMu.Unlock()
-
- if c.transport == nil {
- return fmt.Errorf("client not ready, transport not initialized")
- }
- if c.fn != nil {
- return fmt.Errorf("client already has a receiver")
- }
-
- if fn, err := receiver(fn); err != nil {
- return err
- } else {
- c.fn = fn
- }
-
- defer func() {
- c.fn = nil
- }()
-
- return c.transport.StartReceiver(ctx)
-}
-
-func (c *ceClient) applyOptions(opts ...Option) error {
- for _, fn := range opts {
- if err := fn(c); err != nil {
- return err
- }
- }
- return nil
-}
-
-// Convert implements transport Converter.Convert.
-func (c *ceClient) Convert(ctx context.Context, m transport.Message, err error) (*cloudevents.Event, error) {
- if c.convertFn != nil {
- return c.convertFn(ctx, m, err)
- }
- return nil, err
-}
diff --git a/v1/cloudevents/client/client_test.go b/v1/cloudevents/client/client_test.go
deleted file mode 100644
index bb09baade..000000000
--- a/v1/cloudevents/client/client_test.go
+++ /dev/null
@@ -1,873 +0,0 @@
-package client_test
-
-import (
- "bytes"
- "context"
- "fmt"
- "io/ioutil"
- "net/http"
- "net/http/httptest"
- "net/url"
- "strings"
- "testing"
- "time"
-
- "github.com/cloudevents/sdk-go/v1/cloudevents"
- "github.com/cloudevents/sdk-go/v1/cloudevents/client"
- cehttp "github.com/cloudevents/sdk-go/v1/cloudevents/transport/http"
- "github.com/cloudevents/sdk-go/v1/cloudevents/types"
- "github.com/lightstep/tracecontext.go/traceparent"
- "go.opencensus.io/trace"
-
- "github.com/google/go-cmp/cmp"
-)
-
-var (
- // Headers that are added to the response, but we don't want to check in our assertions.
- unimportantHeaders = []string{
- "accept-encoding",
- "content-length",
- "user-agent",
- "connection",
- "traceparent",
- "tracestate",
- }
-)
-
-func simpleBinaryClient(target string) client.Client {
- t, err := cehttp.New(
- cehttp.WithTarget(target),
- cehttp.WithBinaryEncoding(),
- )
- if err != nil {
- return nil
- }
-
- c, err := client.New(t, client.WithoutTracePropagation())
- if err != nil {
- return nil
- }
- return c
-}
-
-func simpleTracingBinaryClient(target string) client.Client {
- t, err := cehttp.New(
- cehttp.WithTarget(target),
- cehttp.WithBinaryEncoding(),
- )
- if err != nil {
- return nil
- }
-
- c, err := client.New(t)
- if err != nil {
- return nil
- }
- return c
-}
-
-func simpleStructuredClient(target string) client.Client {
- t, err := cehttp.New(
- cehttp.WithTarget(target),
- cehttp.WithStructuredEncoding(),
- )
- if err != nil {
- return nil
- }
-
- c, err := client.New(t, client.WithoutTracePropagation())
- if err != nil {
- return nil
- }
- return c
-}
-
-func TestClientSend(t *testing.T) {
- now := time.Now()
-
- testCases := map[string]struct {
- c func(target string) client.Client
- event cloudevents.Event
- resp *http.Response
- want *requestValidation
- wantErr string
- }{
- "binary simple v0.1": {
- c: simpleBinaryClient,
- event: cloudevents.Event{
- Context: cloudevents.EventContextV01{
- EventType: "unit.test.client",
- Source: *types.ParseURLRef("/unit/test/client"),
- EventTime: &types.Timestamp{Time: now},
- EventID: "AABBCCDDEE",
- }.AsV01(),
- Data: &map[string]interface{}{
- "sq": 42,
- "msg": "hello",
- },
- },
- resp: &http.Response{
- StatusCode: http.StatusAccepted,
- },
- want: &requestValidation{
- Headers: map[string][]string{
- "ce-cloudeventsversion": {"0.1"},
- "ce-eventid": {"AABBCCDDEE"},
- "ce-eventtime": {now.UTC().Format(time.RFC3339Nano)},
- "ce-eventtype": {"unit.test.client"},
- "ce-source": {"/unit/test/client"},
- },
- Body: `{"msg":"hello","sq":42}`,
- },
- },
- "binary simple v0.2": {
- c: simpleBinaryClient,
- event: cloudevents.Event{
- Context: cloudevents.EventContextV02{
- Type: "unit.test.client",
- Source: *types.ParseURLRef("/unit/test/client"),
- Time: &types.Timestamp{Time: now},
- ID: "AABBCCDDEE",
- }.AsV02(),
- Data: &map[string]interface{}{
- "sq": 42,
- "msg": "hello",
- },
- },
- resp: &http.Response{
- StatusCode: http.StatusAccepted,
- },
- want: &requestValidation{
- Headers: map[string][]string{
- "ce-specversion": {"0.2"},
- "ce-id": {"AABBCCDDEE"},
- "ce-time": {now.UTC().Format(time.RFC3339Nano)},
- "ce-type": {"unit.test.client"},
- "ce-source": {"/unit/test/client"},
- },
- Body: `{"msg":"hello","sq":42}`,
- },
- },
- "binary simple v0.3": {
- c: simpleBinaryClient,
- event: cloudevents.Event{
- Context: cloudevents.EventContextV03{
- Type: "unit.test.client",
- Source: *types.ParseURLRef("/unit/test/client"),
- Time: &types.Timestamp{Time: now},
- ID: "AABBCCDDEE",
- }.AsV03(),
- Data: &map[string]interface{}{
- "sq": 42,
- "msg": "hello",
- },
- },
- resp: &http.Response{
- StatusCode: http.StatusAccepted,
- },
- want: &requestValidation{
- Headers: map[string][]string{
- "ce-specversion": {"0.3"},
- "ce-id": {"AABBCCDDEE"},
- "ce-time": {now.UTC().Format(time.RFC3339Nano)},
- "ce-type": {"unit.test.client"},
- "ce-source": {"/unit/test/client"},
- },
- Body: `{"msg":"hello","sq":42}`,
- },
- },
- "structured simple v0.1": {
- c: simpleStructuredClient,
- event: cloudevents.Event{
- Context: cloudevents.EventContextV01{
- EventType: "unit.test.client",
- Source: *types.ParseURLRef("/unit/test/client"),
- EventTime: &types.Timestamp{Time: now},
- EventID: "AABBCCDDEE",
- }.AsV01(),
- Data: &map[string]interface{}{
- "sq": 42,
- "msg": "hello",
- },
- },
- resp: &http.Response{
- StatusCode: http.StatusAccepted,
- },
- want: &requestValidation{
- Headers: map[string][]string{
- "content-type": {"application/cloudevents+json"},
- },
- Body: fmt.Sprintf(`{"cloudEventsVersion":"0.1","data":{"msg":"hello","sq":42},"eventID":"AABBCCDDEE","eventTime":%q,"eventType":"unit.test.client","source":"/unit/test/client"}`,
- now.UTC().Format(time.RFC3339Nano),
- ),
- },
- },
- "structured simple v0.2": {
- c: simpleStructuredClient,
- event: cloudevents.Event{
- Context: cloudevents.EventContextV02{
- Type: "unit.test.client",
- Source: *types.ParseURLRef("/unit/test/client"),
- Time: &types.Timestamp{Time: now},
- ID: "AABBCCDDEE",
- }.AsV02(),
- Data: &map[string]interface{}{
- "sq": 42,
- "msg": "hello",
- },
- },
- resp: &http.Response{
- StatusCode: http.StatusAccepted,
- },
- want: &requestValidation{
- Headers: map[string][]string{
- "content-type": {"application/cloudevents+json"},
- },
- Body: fmt.Sprintf(`{"data":{"msg":"hello","sq":42},"id":"AABBCCDDEE","source":"/unit/test/client","specversion":"0.2","time":%q,"type":"unit.test.client"}`,
- now.UTC().Format(time.RFC3339Nano),
- ),
- },
- },
- "structured simple v0.3": {
- c: simpleStructuredClient,
- event: cloudevents.Event{
- Context: cloudevents.EventContextV03{
- Type: "unit.test.client",
- Source: *types.ParseURLRef("/unit/test/client"),
- Time: &types.Timestamp{Time: now},
- ID: "AABBCCDDEE",
- }.AsV03(),
- Data: &map[string]interface{}{
- "sq": 42,
- "msg": "hello",
- },
- },
- resp: &http.Response{
- StatusCode: http.StatusAccepted,
- },
- want: &requestValidation{
- Headers: map[string][]string{
- "content-type": {"application/cloudevents+json"},
- },
- Body: fmt.Sprintf(`{"data":{"msg":"hello","sq":42},"id":"AABBCCDDEE","source":"/unit/test/client","specversion":"0.3","time":%q,"type":"unit.test.client"}`,
- now.UTC().Format(time.RFC3339Nano),
- ),
- },
- },
- }
- for n, tc := range testCases {
- t.Run(n, func(t *testing.T) {
- handler := &fakeHandler{
- t: t,
- response: tc.resp,
- requests: make([]requestValidation, 0),
- }
- server := httptest.NewServer(handler)
- defer server.Close()
-
- c := tc.c(server.URL)
-
- _, _, err := c.Send(context.TODO(), tc.event) // TODO: update test with new returned event and returned context
- if tc.wantErr != "" {
- if err == nil {
- t.Fatalf("failed to return expected error, got nil")
- }
- want := tc.wantErr
- got := err.Error()
- if !strings.Contains(got, want) {
- t.Fatalf("failed to return expected error, got %q, want %q", err, want)
- }
- return
- } else {
- if err != nil {
- t.Fatalf("failed to send event: %s", err)
- }
- }
-
- rv := handler.popRequest(t)
-
- assertEquality(t, server.URL, *tc.want, rv)
- })
- }
-}
-
-func TestTracingClientSend(t *testing.T) {
- now := time.Now()
-
- testCases := map[string]struct {
- c func(target string) client.Client
- event cloudevents.Event
- resp *http.Response
- tpHeader string
- sample bool
- }{
- "send unsampled": {
- c: simpleTracingBinaryClient,
- event: cloudevents.Event{
- Context: cloudevents.EventContextV01{
- EventType: "unit.test.client",
- Source: *types.ParseURLRef("/unit/test/client"),
- EventTime: &types.Timestamp{Time: now},
- EventID: "AABBCCDDEE",
- }.AsV1(),
- Data: &map[string]interface{}{
- "sq": 42,
- "msg": "hello",
- },
- },
- resp: &http.Response{
- StatusCode: http.StatusAccepted,
- },
- tpHeader: "ce-traceparent",
- },
- "send sampled": {
- c: simpleTracingBinaryClient,
- event: cloudevents.Event{
- Context: cloudevents.EventContextV01{
- EventType: "unit.test.client",
- Source: *types.ParseURLRef("/unit/test/client"),
- EventTime: &types.Timestamp{Time: now},
- EventID: "AABBCCDDEE",
- }.AsV1(),
- Data: &map[string]interface{}{
- "sq": 42,
- "msg": "hello",
- },
- },
- resp: &http.Response{
- StatusCode: http.StatusAccepted,
- },
- sample: true,
- tpHeader: "ce-traceparent",
- },
- }
- for n, tc := range testCases {
- t.Run(n, func(t *testing.T) {
- handler := &fakeHandler{
- t: t,
- response: tc.resp,
- requests: make([]requestValidation, 0),
- }
- server := httptest.NewServer(handler)
- defer server.Close()
-
- c := tc.c(server.URL)
-
- var sampler trace.Sampler
- if tc.sample {
- sampler = trace.AlwaysSample()
- } else {
- sampler = trace.NeverSample()
- }
- ctx, span := trace.StartSpan(context.TODO(), "test-span", trace.WithSampler(sampler))
- sc := span.SpanContext()
-
- _, _, err := c.Send(ctx, tc.event)
- span.End()
-
- if err != nil {
- t.Fatalf("failed to send event: %s", err)
- }
-
- rv := handler.popRequest(t)
-
- var got traceparent.TraceParent
- if tp := rv.Headers.Get(tc.tpHeader); tp == "" {
- t.Fatal("missing traceparent header")
- } else {
- got, err = traceparent.ParseString(tp)
- if err != nil {
- t.Fatalf("invalid traceparent: %s", err)
- }
- }
- if got.TraceID != sc.TraceID {
- t.Errorf("unexpected trace id: want %s got %s", sc.TraceID, got.TraceID)
- }
- if got.Flags.Recorded != tc.sample {
- t.Errorf("unexpected recorded flag: want %t got %t", tc.sample, got.Flags.Recorded)
- }
- })
- }
-}
-
-func simpleBinaryOptions(port int, path string) []cehttp.Option {
- opts := []cehttp.Option{
- cehttp.WithPort(port),
- cehttp.WithBinaryEncoding(),
- }
- if len(path) > 0 {
- opts = append(opts, cehttp.WithPath(path))
- }
- return opts
-}
-
-func simpleStructuredOptions(port int, path string) []cehttp.Option {
- opts := []cehttp.Option{
- cehttp.WithPort(port),
- cehttp.WithStructuredEncoding(),
- }
- if len(path) > 0 {
- opts = append(opts, cehttp.WithPath(path))
- }
- return opts
-}
-
-func TestClientReceive(t *testing.T) {
- now := time.Now()
-
- testCases := map[string]struct {
- optsFn func(port int, path string) []cehttp.Option
- req *requestValidation
- want cloudevents.Event
- wantErr string
- }{
- "binary simple v0.1": {
- optsFn: simpleBinaryOptions,
- req: &requestValidation{
- Headers: map[string][]string{
- "ce-cloudeventsversion": {"0.1"},
- "ce-eventid": {"AABBCCDDEE"},
- "ce-eventtime": {now.UTC().Format(time.RFC3339Nano)},
- "ce-eventtype": {"unit.test.client"},
- "ce-source": {"/unit/test/client"},
- "content-type": {"application/json"},
- },
- Body: `{"msg":"hello","sq":"42"}`,
- },
- want: cloudevents.Event{
- Context: cloudevents.EventContextV01{
- EventType: "unit.test.client",
- ContentType: cloudevents.StringOfApplicationJSON(),
- Source: *types.ParseURLRef("/unit/test/client"),
- EventTime: &types.Timestamp{Time: now},
- EventID: "AABBCCDDEE",
- }.AsV01(),
- Data: &map[string]string{
- "sq": "42",
- "msg": "hello",
- },
- },
- },
- "binary simple v0.2": {
- optsFn: simpleBinaryOptions,
- req: &requestValidation{
- Headers: map[string][]string{
- "ce-specversion": {"0.2"},
- "ce-id": {"AABBCCDDEE"},
- "ce-time": {now.UTC().Format(time.RFC3339Nano)},
- "ce-type": {"unit.test.client"},
- "ce-source": {"/unit/test/client"},
- "content-type": {"application/json"},
- },
- Body: `{"msg":"hello","sq":"42"}`,
- },
- want: cloudevents.Event{
- Context: cloudevents.EventContextV02{
- Type: "unit.test.client",
- ContentType: cloudevents.StringOfApplicationJSON(),
- Source: *types.ParseURLRef("/unit/test/client"),
- Time: &types.Timestamp{Time: now},
- ID: "AABBCCDDEE",
- }.AsV02(),
- Data: &map[string]string{
- "sq": "42",
- "msg": "hello",
- },
- },
- },
- "binary simple v0.3": {
- optsFn: simpleBinaryOptions,
- req: &requestValidation{
- Headers: map[string][]string{
- "ce-specversion": {"0.3"},
- "ce-id": {"AABBCCDDEE"},
- "ce-time": {now.UTC().Format(time.RFC3339Nano)},
- "ce-type": {"unit.test.client"},
- "ce-source": {"/unit/test/client"},
- "content-type": {"application/json"},
- },
- Body: `{"msg":"hello","sq":"42"}`,
- },
- want: cloudevents.Event{
- Context: cloudevents.EventContextV03{
- Type: "unit.test.client",
- DataContentType: cloudevents.StringOfApplicationJSON(),
- Source: *types.ParseURLRef("/unit/test/client"),
- Time: &types.Timestamp{Time: now},
- ID: "AABBCCDDEE",
- }.AsV03(),
- Data: &map[string]string{
- "sq": "42",
- "msg": "hello",
- },
- },
- },
- "structured simple v0.1": {
- optsFn: simpleStructuredOptions,
- req: &requestValidation{
- Headers: map[string][]string{
- "content-type": {"application/cloudevents+json"},
- },
- Body: fmt.Sprintf(`{"cloudEventsVersion":"0.1","contentType":"application/json","data":{"msg":"hello","sq":"42"},"eventID":"AABBCCDDEE","eventTime":%q,"eventType":"unit.test.client","source":"/unit/test/client"}`,
- now.UTC().Format(time.RFC3339Nano),
- ),
- },
- want: cloudevents.Event{
- Context: cloudevents.EventContextV01{
- EventType: "unit.test.client",
- ContentType: cloudevents.StringOfApplicationJSON(),
- Source: *types.ParseURLRef("/unit/test/client"),
- EventTime: &types.Timestamp{Time: now},
- EventID: "AABBCCDDEE",
- }.AsV01(),
- Data: &map[string]string{
- "sq": "42",
- "msg": "hello",
- },
- },
- },
- "structured simple v0.2": {
- optsFn: simpleStructuredOptions,
- req: &requestValidation{
- Headers: map[string][]string{
- "content-type": {"application/cloudevents+json"},
- },
- Body: fmt.Sprintf(`{"contenttype":"application/json","data":{"msg":"hello","sq":"42"},"id":"AABBCCDDEE","source":"/unit/test/client","specversion":"0.2","time":%q,"type":"unit.test.client"}`,
- now.UTC().Format(time.RFC3339Nano),
- ),
- },
- want: cloudevents.Event{
- Context: cloudevents.EventContextV02{
- Type: "unit.test.client",
- ContentType: cloudevents.StringOfApplicationJSON(),
- Source: *types.ParseURLRef("/unit/test/client"),
- Time: &types.Timestamp{Time: now},
- ID: "AABBCCDDEE",
- }.AsV02(),
- Data: &map[string]string{
- "sq": "42",
- "msg": "hello",
- },
- },
- },
- "structured simple v0.3": {
- optsFn: simpleStructuredOptions,
- req: &requestValidation{
- Headers: map[string][]string{
- "content-type": {"application/cloudevents+json"},
- },
- Body: fmt.Sprintf(`{"data":{"msg":"hello","sq":"42"},"datacontenttype":"application/json","id":"AABBCCDDEE","source":"/unit/test/client","specversion":"0.3","time":%q,"type":"unit.test.client"}`,
- now.UTC().Format(time.RFC3339Nano),
- ),
- },
- want: cloudevents.Event{
- Context: cloudevents.EventContextV03{
- Type: "unit.test.client",
- DataContentType: cloudevents.StringOfApplicationJSON(),
- Source: *types.ParseURLRef("/unit/test/client"),
- Time: &types.Timestamp{Time: now},
- ID: "AABBCCDDEE",
- }.AsV03(),
- Data: &map[string]string{
- "sq": "42",
- "msg": "hello",
- },
- },
- },
- }
- for n, tc := range testCases {
- for _, path := range []string{"", "/", "/unittest/"} {
- t.Run(n+" at path "+path, func(t *testing.T) {
-
- events := make(chan cloudevents.Event)
-
- tp, err := cehttp.New(tc.optsFn(0, path)...)
- if err != nil {
- t.Errorf("failed to make http transport %s", err.Error())
- }
-
- c, err := client.New(tp)
- if err != nil {
- t.Errorf("failed to make client %s", err.Error())
- }
-
- ctx, cancel := context.WithCancel(context.TODO())
- go func() {
- err = c.StartReceiver(ctx, func(ctx context.Context, event cloudevents.Event, resp *cloudevents.EventResponse) error {
- go func() {
- events <- event
- }()
- return nil
- })
- if err != nil {
- t.Errorf("failed to start receiver %s", err.Error())
- }
- }()
- time.Sleep(5 * time.Millisecond) // let the server start
-
- target, _ := url.Parse(fmt.Sprintf("http://localhost:%d%s", tp.GetPort(), tp.GetPath()))
-
- if tc.wantErr != "" {
- if err == nil {
- t.Fatalf("failed to return expected error, got nil")
- }
- want := tc.wantErr
- got := err.Error()
- if !strings.Contains(got, want) {
- t.Fatalf("failed to return expected error, got %q, want %q", err, want)
- }
- cancel()
- return
- } else {
- if err != nil {
- t.Fatalf("failed to send event %s", err)
- }
- }
-
- req := &http.Request{
- Method: "POST",
- URL: target,
- Header: tc.req.Headers,
- Body: ioutil.NopCloser(strings.NewReader(tc.req.Body)),
- ContentLength: int64(len([]byte(tc.req.Body))),
- }
-
- _, err = http.DefaultClient.Do(req)
-
- //// Make a copy of the request.
- //body, err := ioutil.ReadAll(resp.Body)
- //if err != nil {
- // t.Error("failed to read the request body")
- //}
- //gotResp := requestValidation{
- // Headers: resp.Header,
- // Body: string(body),
- //}
- //
- //_ = gotResp // TODO: check response
-
- got := <-events
-
- if diff := cmp.Diff(tc.want.Context, got.Context); diff != "" {
- t.Errorf("unexpected events.Context (-want, +got) = %v", diff)
- }
-
- data := &map[string]string{}
- err = got.DataAs(data)
- if err != nil {
- t.Fatalf("returned unexpected error, got %s", err.Error())
- }
-
- if diff := cmp.Diff(tc.want.Data, data); diff != "" {
- t.Errorf("unexpected events.Data (-want, +got) = %v", diff)
- }
-
- // Now stop the client
- cancel()
-
- // try the request again, expecting an error:
-
- if _, err = http.DefaultClient.Do(req); err == nil {
- t.Fatalf("expected error to when sending request to stopped client")
- }
- })
- }
- }
-}
-
-func TestTracedClientReceive(t *testing.T) {
- now := time.Now()
-
- testCases := map[string]struct {
- optsFn func(port int, path string) []cehttp.Option
- event cloudevents.Event
- }{
- "simple binary v1.0": {
- optsFn: simpleBinaryOptions,
- event: cloudevents.Event{
- Context: cloudevents.EventContextV01{
- EventType: "unit.test.client",
- ContentType: cloudevents.StringOfApplicationJSON(),
- Source: *types.ParseURLRef("/unit/test/client"),
- EventTime: &types.Timestamp{Time: now},
- EventID: "AABBCCDDEE",
- }.AsV1(),
- Data: &map[string]string{
- "sq": "42",
- "msg": "hello",
- },
- },
- },
- "simple binary v0.2": {
- optsFn: simpleBinaryOptions,
- event: cloudevents.Event{
- Context: cloudevents.EventContextV02{
- Type: "unit.test.client",
- ContentType: cloudevents.StringOfApplicationJSON(),
- Source: *types.ParseURLRef("/unit/test/client"),
- Time: &types.Timestamp{Time: now},
- ID: "AABBCCDDEE",
- }.AsV02(),
- Data: &map[string]string{
- "sq": "42",
- "msg": "hello",
- },
- },
- },
- "simple binary v0.3": {
- optsFn: simpleBinaryOptions,
- event: cloudevents.Event{
- Context: cloudevents.EventContextV03{
- Type: "unit.test.client",
- DataContentType: cloudevents.StringOfApplicationJSON(),
- Source: *types.ParseURLRef("/unit/test/client"),
- Time: &types.Timestamp{Time: now},
- ID: "AABBCCDDEE",
- }.AsV03(),
- Data: &map[string]string{
- "sq": "42",
- "msg": "hello",
- },
- },
- },
- }
- for n, tc := range testCases {
- t.Run(n, func(t *testing.T) {
- spanContexts := make(chan trace.SpanContext)
-
- tp, err := cehttp.New(tc.optsFn(0, "")...)
- if err != nil {
- t.Errorf("failed to make http transport %s", err.Error())
- }
-
- c, err := client.New(tp)
- if err != nil {
- t.Errorf("failed to make client %s", err.Error())
- }
-
- ctx, cancel := context.WithCancel(context.TODO())
- go func() {
- err = c.StartReceiver(ctx, func(ctx context.Context, event cloudevents.Event, resp *cloudevents.EventResponse) error {
- go func() {
- spanContexts <- trace.FromContext(ctx).SpanContext()
- }()
- return nil
- })
- if err != nil {
- t.Errorf("failed to start receiver %s", err.Error())
- }
- }()
- time.Sleep(5 * time.Millisecond) // let the server start
-
- target := fmt.Sprintf("http://localhost:%d", tp.GetPort())
- client := simpleBinaryClient(target)
-
- ctx, span := trace.StartSpan(context.TODO(), "test-span")
- _, _, err = client.Send(ctx, tc.event)
- span.End()
-
- if err != nil {
- t.Fatalf("failed to send event %s", err)
- }
-
- got := <-spanContexts
-
- if span.SpanContext().TraceID != got.TraceID {
- t.Errorf("unexpected traceID. want: %s, got %s", span.SpanContext().TraceID, got.TraceID)
- }
-
- // Now stop the client
- cancel()
- })
- }
-}
-
-type requestValidation struct {
- Host string
- Headers http.Header
- Body string
-}
-
-type fakeHandler struct {
- t *testing.T
- response *http.Response
- requests []requestValidation
-}
-
-func (f *fakeHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
- defer r.Body.Close()
-
- // Make a copy of the request.
- body, err := ioutil.ReadAll(r.Body)
- if err != nil {
- f.t.Error("failed to read the request body")
- }
- f.requests = append(f.requests, requestValidation{
- Host: r.Host,
- Headers: r.Header,
- Body: string(body),
- })
-
- // Write the response.
- if f.response != nil {
- for h, vs := range f.response.Header {
- for _, v := range vs {
- w.Header().Add(h, v)
- }
- }
- w.WriteHeader(f.response.StatusCode)
- var buf bytes.Buffer
- if f.response.ContentLength > 0 {
- _, _ = buf.ReadFrom(f.response.Body)
- _, _ = w.Write(buf.Bytes())
- }
- } else {
- w.WriteHeader(http.StatusOK)
- _, _ = w.Write([]byte(""))
- }
-}
-
-func (f *fakeHandler) popRequest(t *testing.T) requestValidation {
- if len(f.requests) == 0 {
- t.Error("Unable to pop request")
- }
- rv := f.requests[0]
- f.requests = f.requests[1:]
- return rv
-}
-
-func assertEquality(t *testing.T, replacementURL string, expected, actual requestValidation) {
- server, err := url.Parse(replacementURL)
- if err != nil {
- t.Errorf("Bad replacement URL: %q", replacementURL)
- }
- expected.Host = server.Host
- canonicalizeHeaders(expected, actual)
- if diff := cmp.Diff(expected, actual); diff != "" {
- t.Errorf("Unexpected difference (-want, +got): %v", diff)
- }
-}
-
-func canonicalizeHeaders(rvs ...requestValidation) {
- // HTTP header names are case-insensitive, so normalize them to lower case for comparison.
- for _, rv := range rvs {
- headers := rv.Headers
- for n, v := range headers {
- delete(headers, n)
- ln := strings.ToLower(n)
-
- if isImportantHeader(ln) {
- headers[ln] = v
- }
- }
- }
-}
-
-func isImportantHeader(h string) bool {
- for _, v := range unimportantHeaders {
- if v == h {
- return false
- }
- }
- return true
-}
diff --git a/v1/cloudevents/client/defaulters.go b/v1/cloudevents/client/defaulters.go
deleted file mode 100644
index 8ddd8bf28..000000000
--- a/v1/cloudevents/client/defaulters.go
+++ /dev/null
@@ -1,51 +0,0 @@
-package client
-
-import (
- "context"
- "time"
-
- "github.com/cloudevents/sdk-go/v1/cloudevents"
- "github.com/google/uuid"
-)
-
-// EventDefaulter is the function signature for extensions that are able
-// to perform event defaulting.
-type EventDefaulter func(ctx context.Context, event cloudevents.Event) cloudevents.Event
-
-// DefaultIDToUUIDIfNotSet will inspect the provided event and assign a UUID to
-// context.ID if it is found to be empty.
-func DefaultIDToUUIDIfNotSet(ctx context.Context, event cloudevents.Event) cloudevents.Event {
- if event.Context != nil {
- if event.ID() == "" {
- event.Context = event.Context.Clone()
- event.SetID(uuid.New().String())
- }
- }
- return event
-}
-
-// DefaultTimeToNowIfNotSet will inspect the provided event and assign a new
-// Timestamp to context.Time if it is found to be nil or zero.
-func DefaultTimeToNowIfNotSet(ctx context.Context, event cloudevents.Event) cloudevents.Event {
- if event.Context != nil {
- if event.Time().IsZero() {
- event.Context = event.Context.Clone()
- event.SetTime(time.Now())
- }
- }
- return event
-}
-
-// NewDefaultDataContentTypeIfNotSet returns a defaulter that will inspect the
-// provided event and set the provided content type if content type is found
-// to be empty.
-func NewDefaultDataContentTypeIfNotSet(contentType string) EventDefaulter {
- return func(ctx context.Context, event cloudevents.Event) cloudevents.Event {
- if event.Context != nil {
- if event.DataContentType() == "" {
- event.SetDataContentType(contentType)
- }
- }
- return event
- }
-}
diff --git a/v1/cloudevents/client/defaulters_test.go b/v1/cloudevents/client/defaulters_test.go
deleted file mode 100644
index 30ff3404b..000000000
--- a/v1/cloudevents/client/defaulters_test.go
+++ /dev/null
@@ -1,159 +0,0 @@
-package client
-
-import (
- "context"
- "testing"
- "time"
-
- "github.com/cloudevents/sdk-go/v1/cloudevents"
- "github.com/google/go-cmp/cmp"
-)
-
-var versions = []string{"0.1", "0.2", "0.3", "1.0"}
-
-func TestDefaultIDToUUIDIfNotSet_empty(t *testing.T) {
- for _, tc := range versions {
- t.Run(tc, func(t *testing.T) {
- got := DefaultIDToUUIDIfNotSet(context.TODO(), cloudevents.New(tc))
-
- if got.Context != nil && got.ID() == "" {
- t.Errorf("failed to generate an id for event")
- }
- })
- }
-}
-
-func TestDefaultIDToUUIDIfNotSet_set(t *testing.T) {
- for _, tc := range versions {
- t.Run(tc, func(t *testing.T) {
- event := cloudevents.New(tc)
- event.SetID("abc-123")
-
- got := DefaultIDToUUIDIfNotSet(context.TODO(), event)
-
- if got.ID() != "abc-123" {
- t.Errorf("id was defaulted when already set")
- }
- })
- }
-}
-
-func TestDefaultIDToUUIDIfNotSet_nil(t *testing.T) {
- got := DefaultIDToUUIDIfNotSet(context.TODO(), cloudevents.Event{})
-
- if got.Context != nil && got.ID() == "" {
- t.Errorf("failed to generate time for nil context event")
- }
-}
-
-func TestDefaultIDToUUIDIfNotSetImmutable(t *testing.T) {
- event := cloudevents.Event{
- Context: &cloudevents.EventContextV01{},
- }
-
- got := DefaultIDToUUIDIfNotSet(context.TODO(), event)
-
- want := "0.1"
-
- if diff := cmp.Diff(want, got.SpecVersion()); diff != "" {
- t.Errorf("unexpected (-want, +got) = %v", diff)
- }
-
- if event.Context.AsV01().EventID != "" {
- t.Errorf("modified the original event")
- }
-
- if got.Context.AsV01().EventID == "" {
- t.Errorf("failed to generate an id for event")
- }
-}
-
-func TestDefaultTimeToNowIfNotSet_empty(t *testing.T) {
- for _, tc := range versions {
- t.Run(tc, func(t *testing.T) {
- got := DefaultTimeToNowIfNotSet(context.TODO(), cloudevents.New(tc))
-
- if got.Time().IsZero() {
- t.Errorf("failed to generate time for event")
- }
- })
- }
-}
-
-func TestDefaultTimeToNowIfNotSet_set(t *testing.T) {
- for _, tc := range versions {
- t.Run(tc, func(t *testing.T) {
- event := cloudevents.New(tc)
- now := time.Now()
-
- event.SetTime(now)
-
- got := DefaultTimeToNowIfNotSet(context.TODO(), event)
-
- if !got.Time().Equal(now) {
- t.Errorf("time was defaulted when already set")
- }
- })
- }
-}
-
-func TestDefaultTimeToNowIfNotSet_nil(t *testing.T) {
- got := DefaultTimeToNowIfNotSet(context.TODO(), cloudevents.Event{})
-
- if got.Context != nil && got.Time().IsZero() {
- t.Errorf("failed to generate time for nil context event")
- }
-}
-
-func TestDefaultTimeToNowIfNotSetImmutable(t *testing.T) {
- event := cloudevents.Event{
- Context: &cloudevents.EventContextV01{},
- }
-
- got := DefaultTimeToNowIfNotSet(context.TODO(), event)
-
- want := "0.1"
-
- if diff := cmp.Diff(want, got.SpecVersion()); diff != "" {
- t.Errorf("unexpected (-want, +got) = %v", diff)
- }
-
- if event.Context.AsV01().EventTime != nil {
- t.Errorf("modified the original event")
- }
-
- if got.Context.AsV01().EventTime.IsZero() {
- t.Errorf("failed to generate a time for event")
- }
-}
-
-func TestNewDefaultDataContentTypeIfNotSet_empty(t *testing.T) {
- ct := "a/b"
- for _, tc := range versions {
- t.Run(tc, func(t *testing.T) {
- fn := NewDefaultDataContentTypeIfNotSet(ct)
- got := fn(context.TODO(), cloudevents.New(tc))
-
- if got.DataContentType() != ct {
- t.Errorf("failed to default data content type for event")
- }
- })
- }
-}
-
-func TestNewDefaultDataContentTypeIfNotSet_set(t *testing.T) {
- ct := "a/b"
- for _, tc := range versions {
- t.Run(tc, func(t *testing.T) {
- event := cloudevents.New(tc)
- event.SetDataContentType(ct)
-
- fn := NewDefaultDataContentTypeIfNotSet("b/c")
- got := fn(context.TODO(), event)
-
- if got.DataContentType() != ct {
- t.Errorf("failed to preserve data content type for event")
- }
- })
- }
-}
diff --git a/v1/cloudevents/client/doc.go b/v1/cloudevents/client/doc.go
deleted file mode 100644
index a6a602bb4..000000000
--- a/v1/cloudevents/client/doc.go
+++ /dev/null
@@ -1,6 +0,0 @@
-/*
-Package client holds the recommended entry points for interacting with the CloudEvents Golang SDK. The client wraps
-a selected transport. The client adds validation and defaulting for sending events, and flexible receiver method
-registration. For full details, read the `client.Client` documentation.
-*/
-package client
diff --git a/v1/cloudevents/client/observability.go b/v1/cloudevents/client/observability.go
deleted file mode 100644
index 110c78d22..000000000
--- a/v1/cloudevents/client/observability.go
+++ /dev/null
@@ -1,76 +0,0 @@
-package client
-
-import (
- "github.com/cloudevents/sdk-go/v1/cloudevents"
- "github.com/cloudevents/sdk-go/v1/cloudevents/observability"
- "go.opencensus.io/stats"
- "go.opencensus.io/stats/view"
- "go.opencensus.io/trace"
-)
-
-var (
- // LatencyMs measures the latency in milliseconds for the CloudEvents
- // client methods.
- LatencyMs = stats.Float64("cloudevents.io/sdk-go/client/latency", "The latency in milliseconds for the CloudEvents client methods.", "ms")
-)
-
-var (
- // LatencyView is an OpenCensus view that shows client method latency.
- LatencyView = &view.View{
- Name: "client/latency",
- Measure: LatencyMs,
- Description: "The distribution of latency inside of client for CloudEvents.",
- Aggregation: view.Distribution(0, .01, .1, 1, 10, 100, 1000, 10000),
- TagKeys: observability.LatencyTags(),
- }
-)
-
-type observed int32
-
-// Adheres to Observable
-var _ observability.Observable = observed(0)
-
-const (
- clientSpanName = "cloudevents.client"
-
- specversionAttr = "cloudevents.specversion"
- typeAttr = "cloudevents.type"
- sourceAttr = "cloudevents.source"
- subjectAttr = "cloudevents.subject"
- datacontenttypeAttr = "cloudevents.datacontenttype"
-
- reportSend observed = iota
- reportReceive
-)
-
-// MethodName implements Observable.MethodName
-func (o observed) MethodName() string {
- switch o {
- case reportSend:
- return "send"
- case reportReceive:
- return "receive"
- default:
- return "unknown"
- }
-}
-
-// LatencyMs implements Observable.LatencyMs
-func (o observed) LatencyMs() *stats.Float64Measure {
- return LatencyMs
-}
-
-func eventTraceAttributes(e cloudevents.EventContextReader) []trace.Attribute {
- as := []trace.Attribute{
- trace.StringAttribute(specversionAttr, e.GetSpecVersion()),
- trace.StringAttribute(typeAttr, e.GetType()),
- trace.StringAttribute(sourceAttr, e.GetSource()),
- }
- if sub := e.GetSubject(); sub != "" {
- as = append(as, trace.StringAttribute(subjectAttr, sub))
- }
- if dct := e.GetDataContentType(); dct != "" {
- as = append(as, trace.StringAttribute(datacontenttypeAttr, dct))
- }
- return as
-}
diff --git a/v1/cloudevents/client/options.go b/v1/cloudevents/client/options.go
deleted file mode 100644
index a9364f6ad..000000000
--- a/v1/cloudevents/client/options.go
+++ /dev/null
@@ -1,72 +0,0 @@
-package client
-
-import (
- "fmt"
-)
-
-// Option is the function signature required to be considered an client.Option.
-type Option func(*ceClient) error
-
-// WithEventDefaulter adds an event defaulter to the end of the defaulter chain.
-func WithEventDefaulter(fn EventDefaulter) Option {
- return func(c *ceClient) error {
- if fn == nil {
- return fmt.Errorf("client option was given an nil event defaulter")
- }
- c.eventDefaulterFns = append(c.eventDefaulterFns, fn)
- return nil
- }
-}
-
-// WithUUIDs adds DefaultIDToUUIDIfNotSet event defaulter to the end of the
-// defaulter chain.
-func WithUUIDs() Option {
- return func(c *ceClient) error {
- c.eventDefaulterFns = append(c.eventDefaulterFns, DefaultIDToUUIDIfNotSet)
- return nil
- }
-}
-
-// WithDataContentType adds the resulting defaulter from
-// NewDefaultDataContentTypeIfNotSet event defaulter to the end of the
-// defaulter chain.
-func WithDataContentType(contentType string) Option {
- return func(c *ceClient) error {
- c.eventDefaulterFns = append(c.eventDefaulterFns, NewDefaultDataContentTypeIfNotSet(contentType))
- return nil
- }
-}
-
-// WithTimeNow adds DefaultTimeToNowIfNotSet event defaulter to the end of the
-// defaulter chain.
-func WithTimeNow() Option {
- return func(c *ceClient) error {
- c.eventDefaulterFns = append(c.eventDefaulterFns, DefaultTimeToNowIfNotSet)
- return nil
- }
-}
-
-// WithConverterFn defines the function the transport will use to delegate
-// conversion of non-decodable messages.
-func WithConverterFn(fn ConvertFn) Option {
- return func(c *ceClient) error {
- if fn == nil {
- return fmt.Errorf("client option was given an nil message converter")
- }
- if c.transport.HasConverter() {
- return fmt.Errorf("transport converter already set")
- }
- c.convertFn = fn
- c.transport.SetConverter(c)
- return nil
- }
-}
-
-// WithoutTracePropagation disables automatic trace propagation via
-// the distributed tracing extension.
-func WithoutTracePropagation() Option {
- return func(c *ceClient) error {
- c.disableTracePropagation = true
- return nil
- }
-}
diff --git a/v1/cloudevents/client/options_test.go b/v1/cloudevents/client/options_test.go
deleted file mode 100644
index 7488ca102..000000000
--- a/v1/cloudevents/client/options_test.go
+++ /dev/null
@@ -1,137 +0,0 @@
-package client
-
-import (
- "context"
- "testing"
-
- "github.com/cloudevents/sdk-go/v1/cloudevents"
- "github.com/google/go-cmp/cmp"
-)
-
-func TestWithEventDefaulter(t *testing.T) {
-
- v1 := func(ctx context.Context, event cloudevents.Event) cloudevents.Event {
- event.Context = event.Context.AsV01()
- return event
- }
-
- v2 := func(ctx context.Context, event cloudevents.Event) cloudevents.Event {
- event.Context = event.Context.AsV02()
- return event
- }
-
- v3 := func(ctx context.Context, event cloudevents.Event) cloudevents.Event {
- event.Context = event.Context.AsV03()
- return event
- }
-
- testCases := map[string]struct {
- c *ceClient
- fns []EventDefaulter
- want int // number of defaulters
- wantErr string
- }{
- "none": {
- c: &ceClient{},
- want: 0,
- },
- "one": {
- c: &ceClient{},
- fns: []EventDefaulter{v1},
- want: 1,
- },
- "three": {
- c: &ceClient{},
- fns: []EventDefaulter{v1, v2, v3},
- want: 3,
- },
- "nil fn": {
- c: &ceClient{},
- fns: []EventDefaulter{nil},
- wantErr: "client option was given an nil event defaulter",
- },
- }
- for n, tc := range testCases {
- t.Run(n, func(t *testing.T) {
- var err error
- for _, fn := range tc.fns {
- err = tc.c.applyOptions(WithEventDefaulter(fn))
- if err != nil {
- break
- }
- }
-
- if tc.wantErr != "" || err != nil {
- var gotErr string
- if err != nil {
- gotErr = err.Error()
- }
- if diff := cmp.Diff(tc.wantErr, gotErr); diff != "" {
- t.Errorf("unexpected error (-want, +got) = %v", diff)
- }
- return
- }
-
- got := len(tc.c.eventDefaulterFns)
-
- if diff := cmp.Diff(tc.want, got); diff != "" {
- t.Errorf("unexpected (-want, +got) = %v", diff)
- }
- })
- }
-}
-
-func TestWith_Defaulters(t *testing.T) {
-
- testCases := map[string]struct {
- c *ceClient
- opts []Option
- want int // number of defaulters
- wantErr string
- }{
- "none": {
- c: &ceClient{},
- want: 0,
- },
- "uuid": {
- c: &ceClient{},
- opts: []Option{WithUUIDs()},
- want: 1,
- },
- "time": {
- c: &ceClient{},
- opts: []Option{WithTimeNow()},
- want: 1,
- },
- "uuid and time": {
- c: &ceClient{},
- opts: []Option{WithUUIDs(), WithTimeNow()},
- want: 2,
- },
- }
- for n, tc := range testCases {
- t.Run(n, func(t *testing.T) {
- var err error
- if len(tc.opts) > 0 {
- err = tc.c.applyOptions(tc.opts...)
- }
-
- if tc.wantErr != "" || err != nil {
- var gotErr string
- if err != nil {
- gotErr = err.Error()
- }
- if diff := cmp.Diff(tc.wantErr, gotErr); diff != "" {
- t.Errorf("unexpected error (-want, +got) = %v", diff)
- }
- return
- }
-
- got := len(tc.c.eventDefaulterFns)
-
- if diff := cmp.Diff(tc.want, got); diff != "" {
- t.Errorf("unexpected (-want, +got) = %v", diff)
- }
- })
- }
-}
diff --git a/v1/cloudevents/client/receiver.go b/v1/cloudevents/client/receiver.go
deleted file mode 100644
index d63ec13a3..000000000
--- a/v1/cloudevents/client/receiver.go
+++ /dev/null
@@ -1,193 +0,0 @@
-package client
-
-import (
- "context"
- "errors"
- "fmt"
- "reflect"
-
- "github.com/cloudevents/sdk-go/v1/cloudevents"
- "github.com/cloudevents/sdk-go/v1/cloudevents/transport"
-)
-
-// Receive is the signature of a fn to be invoked for incoming cloudevents.
-// If fn returns an error, EventResponse will not be considered by the client or
-// or transport.
-// This is just an FYI:
-type ReceiveFull func(context.Context, cloudevents.Event, *cloudevents.EventResponse) error
-
-type receiverFn struct {
- numIn int
- fnValue reflect.Value
-
- hasContextIn bool
- hasEventIn bool
- hasEventResponseIn bool
-
- hasErrorOut bool
-}
-
-// ConvertFn defines the signature the client expects to enable conversion
-// delegation.
-type ConvertFn func(context.Context, transport.Message, error) (*cloudevents.Event, error)
-
-const (
- inParamUsage = "expected a function taking either no parameters, one or more of (context.Context, cloudevents.Event, *cloudevents.EventResponse) ordered"
- outParamUsage = "expected a function returning either nothing or an error"
-)
-
-var (
- contextType = reflect.TypeOf((*context.Context)(nil)).Elem()
- eventType = reflect.TypeOf((*cloudevents.Event)(nil)).Elem()
- eventResponseType = reflect.TypeOf((*cloudevents.EventResponse)(nil)) // want the ptr type
- errorType = reflect.TypeOf((*error)(nil)).Elem()
-)
-
-// receiver creates a receiverFn wrapper class that is used by the client to
-// validate and invoke the provided function.
-// Valid fn signatures are:
-// * func()
-// * func() error
-// * func(context.Context)
-// * func(context.Context) error
-// * func(cloudevents.Event)
-// * func(cloudevents.Event) error
-// * func(context.Context, cloudevents.Event)
-// * func(context.Context, cloudevents.Event) error
-// * func(cloudevents.Event, *cloudevents.EventResponse)
-// * func(cloudevents.Event, *cloudevents.EventResponse) error
-// * func(context.Context, cloudevents.Event, *cloudevents.EventResponse)
-// * func(context.Context, cloudevents.Event, *cloudevents.EventResponse) error
-//
-func receiver(fn interface{}) (*receiverFn, error) {
- fnType := reflect.TypeOf(fn)
- if fnType.Kind() != reflect.Func {
- return nil, errors.New("must pass a function to handle events")
- }
-
- r := &receiverFn{
- fnValue: reflect.ValueOf(fn),
- numIn: fnType.NumIn(),
- }
- if err := r.validate(fnType); err != nil {
- return nil, err
- }
-
- return r, nil
-}
-
-func (r *receiverFn) invoke(ctx context.Context, event cloudevents.Event, resp *cloudevents.EventResponse) error {
- args := make([]reflect.Value, 0, r.numIn)
-
- if r.numIn > 0 {
- if r.hasContextIn {
- args = append(args, reflect.ValueOf(ctx))
- }
- if r.hasEventIn {
- args = append(args, reflect.ValueOf(event))
- }
- if r.hasEventResponseIn {
- args = append(args, reflect.ValueOf(resp))
- }
- }
- v := r.fnValue.Call(args)
- if r.hasErrorOut && len(v) >= 1 {
- if err, ok := v[0].Interface().(error); ok {
- return err
- }
- }
- return nil
-}
-
-// Verifies that the inputs to a function have a valid signature
-// Valid input is to be [0, all] of
-// context.Context, cloudevents.Event, *cloudevents.EventResponse in this order.
-func (r *receiverFn) validateInParamSignature(fnType reflect.Type) error {
- r.hasContextIn = false
- r.hasEventIn = false
- r.hasEventResponseIn = false
-
- switch fnType.NumIn() {
- case 3:
- // has to be cloudevents.Event, *cloudevents.EventResponse
- if !fnType.In(2).ConvertibleTo(eventResponseType) {
- return fmt.Errorf("%s; cannot convert parameter 2 from %s to *cloudevents.EventResponse", inParamUsage, fnType.In(2))
- } else {
- r.hasEventResponseIn = true
- }
- fallthrough
- case 2:
- // can be cloudevents.Event or *cloudevents.EventResponse
- if !fnType.In(1).ConvertibleTo(eventResponseType) {
- if !fnType.In(1).ConvertibleTo(eventType) {
- return fmt.Errorf("%s; cannot convert parameter 1 from %s to cloudevents.Event or *cloudevents.EventResponse", inParamUsage, fnType.In(1))
- } else {
- r.hasEventIn = true
- }
- } else if r.hasEventResponseIn {
- return fmt.Errorf("%s; duplicate parameter of type *cloudevents.EventResponse", inParamUsage)
- } else {
- r.hasEventResponseIn = true
- }
- fallthrough
- case 1:
- if !fnType.In(0).ConvertibleTo(contextType) {
- if !fnType.In(0).ConvertibleTo(eventResponseType) {
- if !fnType.In(0).ConvertibleTo(eventType) {
- return fmt.Errorf("%s; cannot convert parameter 0 from %s to context.Context, cloudevents.Event or *cloudevents.EventResponse", inParamUsage, fnType.In(0))
- } else if r.hasEventIn {
- return fmt.Errorf("%s; duplicate parameter of type cloudevents.Event", inParamUsage)
- } else {
- r.hasEventIn = true
- }
- } else if r.hasEventResponseIn {
- return fmt.Errorf("%s; duplicate parameter of type *cloudevents.EventResponse", inParamUsage)
- } else if r.hasEventIn {
- return fmt.Errorf("%s; out of order parameter 0 for %s", inParamUsage, fnType.In(1))
- } else {
- r.hasEventResponseIn = true
- }
- } else {
- r.hasContextIn = true
- }
- fallthrough
- case 0:
- return nil
- default:
- return fmt.Errorf("%s; function has too many parameters (%d)", inParamUsage, fnType.NumIn())
- }
-}
-
-// Verifies that the outputs of a function have a valid signature
-// Valid output signatures:
-// (), (error)
-func (r *receiverFn) validateOutParamSignature(fnType reflect.Type) error {
- r.hasErrorOut = false
- switch fnType.NumOut() {
- case 1:
- paramNo := fnType.NumOut() - 1
- paramType := fnType.Out(paramNo)
- if !paramType.ConvertibleTo(errorType) {
- return fmt.Errorf("%s; cannot convert return type %d from %s to error", outParamUsage, paramNo, paramType)
- } else {
- r.hasErrorOut = true
- }
- fallthrough
- case 0:
- return nil
- default:
- return fmt.Errorf("%s; function has too many return types (%d)", outParamUsage, fnType.NumOut())
- }
-}
-
-// validateReceiverFn validates that a function has the right number of in and
-// out params and that they are of allowed types.
-func (r *receiverFn) validate(fnType reflect.Type) error {
- if err := r.validateInParamSignature(fnType); err != nil {
- return err
- }
- if err := r.validateOutParamSignature(fnType); err != nil {
- return err
- }
- return nil
-}
diff --git a/v1/cloudevents/client/receiver_test.go b/v1/cloudevents/client/receiver_test.go
deleted file mode 100644
index 3beb1f764..000000000
--- a/v1/cloudevents/client/receiver_test.go
+++ /dev/null
@@ -1,237 +0,0 @@
-package client
-
-import (
- "context"
- "errors"
- "testing"
-
- "github.com/cloudevents/sdk-go/v1/cloudevents"
- "github.com/google/go-cmp/cmp"
-)
-
-func TestReceiverFnValidTypes(t *testing.T) {
- for name, fn := range map[string]interface{}{
- "no in, no out": func() {},
- "no in, error out": func() error { return nil },
- "ctx in, no out": func(context.Context) {},
- "ctx, Event in, no out": func(context.Context, cloudevents.Event) {},
- "ctx, EventResponse in, no out": func(context.Context, *cloudevents.EventResponse) {},
- "ctx, Event, EventResponse in, no out": func(context.Context, cloudevents.Event, *cloudevents.EventResponse) {},
- "ctx in, error out": func(context.Context) error { return nil },
- "ctx, Event in, error out": func(context.Context, cloudevents.Event) error { return nil },
- "ctx, EventResponse in, error out": func(context.Context, *cloudevents.EventResponse) error { return nil },
- "ctx, Event, EventResponse in, error out": func(context.Context, cloudevents.Event, *cloudevents.EventResponse) error { return nil },
- "Event in, no out": func(cloudevents.Event) {},
- "EventResponse in, no out": func(*cloudevents.EventResponse) {},
- "Event, EventResponse in, no out": func(cloudevents.Event, *cloudevents.EventResponse) {},
- "Event in, error out": func(cloudevents.Event) error { return nil },
- "EventResponse in, error out": func(*cloudevents.EventResponse) error { return nil },
- "Event, EventResponse in, error out": func(cloudevents.Event, *cloudevents.EventResponse) error { return nil },
- } {
- t.Run(name, func(t *testing.T) {
- if _, err := receiver(fn); err != nil {
- t.Errorf("%q failed: %v", name, err)
- }
- })
- }
-}
-
-func TestReceiverFnInvalidTypes(t *testing.T) {
- for name, fn := range map[string]interface{}{
- "wrong type in": func(string) {},
- "wrong type out": func() string { return "" },
- "extra in": func(context.Context, cloudevents.Event, *cloudevents.EventResponse, map[string]string) {},
- "extra out": func(context.Context, *cloudevents.EventResponse) (error, int) { return nil, 0 },
- "context dup EventResponse in": func(context.Context, *cloudevents.EventResponse, *cloudevents.EventResponse) {},
- "dup EventResponse in": func(*cloudevents.EventResponse, *cloudevents.EventResponse) {},
- "context dup Event in": func(context.Context, cloudevents.Event, cloudevents.Event) {},
- "dup Event in": func(cloudevents.Event, cloudevents.Event) {},
- "wrong order, context3 in": func(*cloudevents.EventResponse, *cloudevents.EventResponse, context.Context) {},
- "wrong order, event in": func(context.Context, *cloudevents.EventResponse, cloudevents.Event) {},
- "wrong order, resp in": func(*cloudevents.EventResponse, cloudevents.Event) {},
- "wrong order, context2 in": func(*cloudevents.EventResponse, context.Context) {},
- "Event as ptr in": func(*cloudevents.Event) {},
- "EventResponse as non-ptr in": func(cloudevents.EventResponse) {},
- "extra Event in": func(cloudevents.Event, *cloudevents.EventResponse, cloudevents.Event) {},
- "not a function": map[string]string(nil),
- } {
- t.Run(name, func(t *testing.T) {
- if _, err := receiver(fn); err == nil {
- t.Errorf("%q failed to catch the issue", name)
- }
- })
- }
-}
-
-func TestReceiverFnInvoke_1(t *testing.T) {
- wantErr := errors.New("UNIT TEST")
- key := struct{}{}
- wantCtx := context.WithValue(context.TODO(), key, "UNIT TEST")
- wantEvent := cloudevents.Event{
- Context: &cloudevents.EventContextV02{
- ID: "UNIT TEST",
- },
- }
- wantResp := &cloudevents.EventResponse{Reason: "UNIT TEST"}
-
- fn, err := receiver(func(ctx context.Context, event cloudevents.Event, resp *cloudevents.EventResponse) error {
- if diff := cmp.Diff(wantCtx.Value(key), ctx.Value(key)); diff != "" {
- t.Errorf("unexpected context (-want, +got) = %v", diff)
- }
-
- if diff := cmp.Diff(wantEvent, event); diff != "" {
- t.Errorf("unexpected event (-want, +got) = %v", diff)
- }
-
- if diff := cmp.Diff(wantResp, resp); diff != "" {
- t.Errorf("unexpected response (-want, +got) = %v", diff)
- }
- return wantErr
- })
- if err != nil {
- t.Errorf("unexpected error, wanted nil got = %v", err)
- }
-
- err = fn.invoke(wantCtx, wantEvent, wantResp)
-
- if diff := cmp.Diff(wantErr.Error(), err.Error()); diff != "" {
- t.Errorf("unexpected error (-want, +got) = %v", diff)
- }
-}
-
-func TestReceiverFnInvoke_2(t *testing.T) {
- wantErr := errors.New("UNIT TEST")
- key := struct{}{}
- ctx := context.WithValue(context.TODO(), key, "UNIT TEST")
- wantEvent := cloudevents.Event{
- Context: &cloudevents.EventContextV02{
- ID: "UNIT TEST",
- },
- }
- wantResp := &cloudevents.EventResponse{Reason: "UNIT TEST"}
-
- fn, err := receiver(func(event cloudevents.Event, resp *cloudevents.EventResponse) error {
- if diff := cmp.Diff(wantEvent, event); diff != "" {
- t.Errorf("unexpected event (-want, +got) = %v", diff)
- }
-
- if diff := cmp.Diff(wantResp, resp); diff != "" {
- t.Errorf("unexpected response (-want, +got) = %v", diff)
- }
- return wantErr
- })
- if err != nil {
- t.Errorf("unexpected error, wanted nil got = %v", err)
- }
-
- err = fn.invoke(ctx, wantEvent, wantResp)
-
- if diff := cmp.Diff(wantErr.Error(), err.Error()); diff != "" {
- t.Errorf("unexpected error (-want, +got) = %v", diff)
- }
-}
-
-func TestReceiverFnInvoke_3(t *testing.T) {
- key := struct{}{}
- ctx := context.WithValue(context.TODO(), key, "UNIT TEST")
- wantEvent := cloudevents.Event{
- Context: &cloudevents.EventContextV02{
- ID: "UNIT TEST",
- },
- }
- wantResp := &cloudevents.EventResponse{Reason: "UNIT TEST"}
-
- fn, err := receiver(func(event cloudevents.Event, resp *cloudevents.EventResponse) {
- if diff := cmp.Diff(wantEvent, event); diff != "" {
- t.Errorf("unexpected event (-want, +got) = %v", diff)
- }
-
- if diff := cmp.Diff(wantResp, resp); diff != "" {
- t.Errorf("unexpected response (-want, +got) = %v", diff)
- }
- })
- if err != nil {
- t.Errorf("unexpected error, wanted nil got = %v", err)
- }
-
- err = fn.invoke(ctx, wantEvent, wantResp)
-
- if err != nil {
- t.Errorf("unexpected error, want nil got got = %v", err.Error())
- }
-}
-
-func TestReceiverFnInvoke_4(t *testing.T) {
- wantErr := errors.New("UNIT TEST")
- key := struct{}{}
- ctx := context.WithValue(context.TODO(), key, "UNIT TEST")
- event := cloudevents.Event{
- Context: &cloudevents.EventContextV02{
- ID: "UNIT TEST",
- },
- }
- wantResp := &cloudevents.EventResponse{Reason: "UNIT TEST"}
-
- fn, err := receiver(func(resp *cloudevents.EventResponse) error {
- if diff := cmp.Diff(wantResp, resp); diff != "" {
- t.Errorf("unexpected response (-want, +got) = %v", diff)
- }
- return wantErr
- })
- if err != nil {
- t.Errorf("unexpected error, wanted nil got = %v", err)
- }
-
- err = fn.invoke(ctx, event, wantResp)
-
- if diff := cmp.Diff(wantErr.Error(), err.Error()); diff != "" {
- t.Errorf("unexpected error (-want, +got) = %v", diff)
- }
-}
-
-func TestReceiverFnInvoke_5(t *testing.T) {
- wantErr := errors.New("UNIT TEST")
- key := struct{}{}
- ctx := context.WithValue(context.TODO(), key, "UNIT TEST")
- event := cloudevents.Event{
- Context: &cloudevents.EventContextV02{
- ID: "UNIT TEST",
- },
- }
- resp := &cloudevents.EventResponse{Reason: "UNIT TEST"}
-
- fn, err := receiver(func() error {
- return wantErr
- })
- if err != nil {
- t.Errorf("unexpected error, wanted nil got = %v", err)
- }
-
- err = fn.invoke(ctx, event, resp)
-
- if diff := cmp.Diff(wantErr.Error(), err.Error()); diff != "" {
- t.Errorf("unexpected error (-want, +got) = %v", diff)
- }
-}
-
-func TestReceiverFnInvoke_6(t *testing.T) {
- key := struct{}{}
- ctx := context.WithValue(context.TODO(), key, "UNIT TEST")
- event := cloudevents.Event{
- Context: &cloudevents.EventContextV02{
- ID: "UNIT TEST",
- },
- }
- resp := &cloudevents.EventResponse{Reason: "UNIT TEST"}
-
- fn, err := receiver(func() {})
- if err != nil {
- t.Errorf("unexpected error, wanted nil got = %v", err)
- }
-
- err = fn.invoke(ctx, event, resp)
-
- if err != nil {
- t.Errorf("unexpected error, want nil got got = %v", err.Error())
- }
-}
diff --git a/v1/cloudevents/content_type.go b/v1/cloudevents/content_type.go
deleted file mode 100644
index e4e0e17f2..000000000
--- a/v1/cloudevents/content_type.go
+++ /dev/null
@@ -1,35 +0,0 @@
-package cloudevents
-
-const (
- TextJSON = "text/json"
- ApplicationJSON = "application/json"
- ApplicationXML = "application/xml"
- ApplicationCloudEventsJSON = "application/cloudevents+json"
- ApplicationCloudEventsBatchJSON = "application/cloudevents-batch+json"
-)
-
-// StringOfApplicationJSON returns a string pointer to "application/json"
-func StringOfApplicationJSON() *string {
- a := ApplicationJSON
- return &a
-}
-
-// StringOfApplicationXML returns a string pointer to "application/xml"
-func StringOfApplicationXML() *string {
- a := ApplicationXML
- return &a
-}
-
-// StringOfApplicationCloudEventsJSON returns a string pointer to
-// "application/cloudevents+json"
-func StringOfApplicationCloudEventsJSON() *string {
- a := ApplicationCloudEventsJSON
- return &a
-}
-
-// StringOfApplicationCloudEventsBatchJSON returns a string pointer to
-// "application/cloudevents-batch+json"
-func StringOfApplicationCloudEventsBatchJSON() *string {
- a := ApplicationCloudEventsBatchJSON
- return &a
-}
diff --git a/v1/cloudevents/content_type_test.go b/v1/cloudevents/content_type_test.go
deleted file mode 100644
index 2f2f10d7f..000000000
--- a/v1/cloudevents/content_type_test.go
+++ /dev/null
@@ -1,44 +0,0 @@
-package cloudevents_test
-
-import (
- "testing"
-
- ce "github.com/cloudevents/sdk-go/v1/cloudevents"
- "github.com/google/go-cmp/cmp"
-)
-
-func TestStringOfApplicationJSON(t *testing.T) {
- want := strptr("application/json")
- got := ce.StringOfApplicationJSON()
-
- if diff := cmp.Diff(want, got); diff != "" {
- t.Errorf("unexpected string (-want, +got) = %v", diff)
- }
-}
-
-func TestStringOfApplicationXML(t *testing.T) {
- want := strptr("application/xml")
- got := ce.StringOfApplicationXML()
-
- if diff := cmp.Diff(want, got); diff != "" {
- t.Errorf("unexpected string (-want, +got) = %v", diff)
- }
-}
-
-func TestStringOfApplicationCloudEventsJSON(t *testing.T) {
- want := strptr("application/cloudevents+json")
- got := ce.StringOfApplicationCloudEventsJSON()
-
- if diff := cmp.Diff(want, got); diff != "" {
- t.Errorf("unexpected string (-want, +got) = %v", diff)
- }
-}
-
-func TestStringOfApplicationCloudEventsBatchJSON(t *testing.T) {
- want := strptr("application/cloudevents-batch+json")
- got := ce.StringOfApplicationCloudEventsBatchJSON()
-
- if diff := cmp.Diff(want, got); diff != "" {
- t.Errorf("unexpected string (-want, +got) = %v", diff)
- }
-}
diff --git a/v1/cloudevents/context/context.go b/v1/cloudevents/context/context.go
deleted file mode 100644
index e580360f1..000000000
--- a/v1/cloudevents/context/context.go
+++ /dev/null
@@ -1,76 +0,0 @@
-package context
-
-import (
- "context"
- "net/url"
- "strings"
-)
-
-// Opaque key type used to store target
-type targetKeyType struct{}
-
-var targetKey = targetKeyType{}
-
-// WithTarget returns back a new context with the given target. Target is intended to be transport dependent.
-// For http transport, `target` should be a full URL and will be injected into the outbound http request.
-func WithTarget(ctx context.Context, target string) context.Context {
- return context.WithValue(ctx, targetKey, target)
-}
-
-// TargetFrom looks in the given context and returns `target` as a parsed url if found and valid, otherwise nil.
-func TargetFrom(ctx context.Context) *url.URL {
- c := ctx.Value(targetKey)
- if c != nil {
- if s, ok := c.(string); ok && s != "" {
- if target, err := url.Parse(s); err == nil {
- return target
- }
- }
- }
- return nil
-}
-
-// Opaque key type used to store topic
-type topicKeyType struct{}
-
-var topicKey = topicKeyType{}
-
-// WithTopic returns back a new context with the given topic. Topic is intended to be transport dependent.
-// For pubsub transport, `topic` should be a Pub/Sub Topic ID.
-func WithTopic(ctx context.Context, topic string) context.Context {
- return context.WithValue(ctx, topicKey, topic)
-}
-
-// TopicFrom looks in the given context and returns `topic` as a string if found and valid, otherwise "".
-func TopicFrom(ctx context.Context) string {
- c := ctx.Value(topicKey)
- if c != nil {
- if s, ok := c.(string); ok {
- return s
- }
- }
- return ""
-}
-
-// Opaque key type used to store encoding
-type encodingKeyType struct{}
-
-var encodingKey = encodingKeyType{}
-
-// WithEncoding returns back a new context with the given encoding. Encoding is intended to be transport dependent.
-// For http transport, `encoding` should be one of [binary, structured] and will be used to override the outbound
-// codec encoding setting. If the transport does not understand the encoding, it will be ignored.
-func WithEncoding(ctx context.Context, encoding string) context.Context {
- return context.WithValue(ctx, encodingKey, strings.ToLower(encoding))
-}
-
-// EncodingFrom looks in the given context and returns `target` as a parsed url if found and valid, otherwise nil.
-func EncodingFrom(ctx context.Context) string {
- c := ctx.Value(encodingKey)
- if c != nil {
- if s, ok := c.(string); ok && s != "" {
- return s
- }
- }
- return ""
-}
diff --git a/v1/cloudevents/context/context_test.go b/v1/cloudevents/context/context_test.go
deleted file mode 100644
index b12a5eccf..000000000
--- a/v1/cloudevents/context/context_test.go
+++ /dev/null
@@ -1,88 +0,0 @@
-package context_test
-
-import (
- "context"
- "net/url"
- "testing"
-
- cecontext "github.com/cloudevents/sdk-go/v1/cloudevents/context"
- "github.com/google/go-cmp/cmp"
-)
-
-func TestTargetContext(t *testing.T) {
- exampleDotCom, _ := url.Parse("http://example.com")
-
- testCases := map[string]struct {
- target string
- ctx context.Context
- want *url.URL
- }{
- "nil context": {},
- "nil context, set url": {
- target: "http://example.com",
- want: exampleDotCom,
- },
- "todo context, set url": {
- ctx: context.TODO(),
- target: "http://example.com",
- want: exampleDotCom,
- },
- "bad url": {
- ctx: context.TODO(),
- target: "%",
- },
- "already set target": {
- ctx: cecontext.WithTarget(context.TODO(), "http://example2.com"),
- target: "http://example.com",
- want: exampleDotCom,
- },
- }
- for n, tc := range testCases {
- t.Run(n, func(t *testing.T) {
-
- ctx := cecontext.WithTarget(tc.ctx, tc.target)
-
- got := cecontext.TargetFrom(ctx)
-
- if diff := cmp.Diff(tc.want, got); diff != "" {
- t.Errorf("unexpected (-want, +got) = %v", diff)
- }
- })
- }
-}
-
-func TestEncodingContext(t *testing.T) {
- testCases := map[string]struct {
- encoding string
- ctx context.Context
- want string
- }{
- "nil context": {},
- "nil context, set encoding": {
- encoding: "foo",
- want: "foo",
- },
- "todo context, set encoding": {
- ctx: context.TODO(),
- encoding: "foo",
- want: "foo",
- },
- "already set encoding": {
- ctx: cecontext.WithTarget(context.TODO(), "foo"),
- encoding: "bar",
- want: "bar",
- },
- }
- for n, tc := range testCases {
- t.Run(n, func(t *testing.T) {
-
- ctx := cecontext.WithEncoding(tc.ctx, tc.encoding)
-
- got := cecontext.EncodingFrom(ctx)
-
- if diff := cmp.Diff(tc.want, got); diff != "" {
- t.Errorf("unexpected (-want, +got) = %v", diff)
- }
- })
- }
-}
diff --git a/v1/cloudevents/context/doc.go b/v1/cloudevents/context/doc.go
deleted file mode 100644
index 377cab850..000000000
--- a/v1/cloudevents/context/doc.go
+++ /dev/null
@@ -1,5 +0,0 @@
-/*
-Package context holds the last resort overrides and fyi objects that can be passed to clients and transports added to
-context.Context objects.
-*/
-package context
diff --git a/v1/cloudevents/context/logger.go b/v1/cloudevents/context/logger.go
deleted file mode 100644
index 996f72057..000000000
--- a/v1/cloudevents/context/logger.go
+++ /dev/null
@@ -1,43 +0,0 @@
-package context
-
-import (
- "context"
-
- "go.uber.org/zap"
-)
-
-// Opaque key type used to store logger
-type loggerKeyType struct{}
-
-var loggerKey = loggerKeyType{}
-
-// fallbackLogger is the logger is used when there is no logger attached to the context.
-var fallbackLogger *zap.SugaredLogger
-
-func init() {
- if logger, err := zap.NewProduction(); err != nil {
- // We failed to create a fallback logger.
- fallbackLogger = zap.NewNop().Sugar()
- } else {
- fallbackLogger = logger.Named("fallback").Sugar()
- }
-}
-
-// WithLogger returns a new context with the logger injected into the given context.
-func WithLogger(ctx context.Context, logger *zap.SugaredLogger) context.Context {
- if logger == nil {
- return context.WithValue(ctx, loggerKey, fallbackLogger)
- }
- return context.WithValue(ctx, loggerKey, logger)
-}
-
-// LoggerFrom returns the logger stored in context.
-func LoggerFrom(ctx context.Context) *zap.SugaredLogger {
- l := ctx.Value(loggerKey)
- if l != nil {
- if logger, ok := l.(*zap.SugaredLogger); ok {
- return logger
- }
- }
- return fallbackLogger
-}
diff --git a/v1/cloudevents/context/logger_test.go b/v1/cloudevents/context/logger_test.go
deleted file mode 100644
index ee369cfae..000000000
--- a/v1/cloudevents/context/logger_test.go
+++ /dev/null
@@ -1,56 +0,0 @@
-package context
-
-import (
- "context"
- "testing"
-
- "github.com/google/go-cmp/cmp"
- "github.com/google/go-cmp/cmp/cmpopts"
- "go.uber.org/zap"
-)
-
-func TestLoggerContext(t *testing.T) {
- var namedLogger *zap.SugaredLogger
- if logger, err := zap.NewProduction(); err != nil {
- t.Fatal(err)
- } else {
- namedLogger = logger.Named("unittest").Sugar()
- }
-
- nopLogger := zap.NewNop().Sugar()
-
- testCases := map[string]struct {
- logger *zap.SugaredLogger
- ctx context.Context
- want *zap.SugaredLogger
- }{
- "nil context": {
- want: fallbackLogger,
- },
- "nil context, set nop logger": {
- logger: nopLogger,
- want: nopLogger,
- },
- "todo context, set logger": {
- ctx: context.TODO(),
- logger: namedLogger,
- want: namedLogger,
- },
- "already set logger": {
- ctx: WithLogger(context.TODO(), nopLogger),
- logger: namedLogger,
- want: namedLogger,
- },
- }
- for n, tc := range testCases {
- t.Run(n, func(t *testing.T) {
-
- ctx := WithLogger(tc.ctx, tc.logger)
- got := LoggerFrom(ctx)
-
- if diff := cmp.Diff(tc.want, got, cmpopts.IgnoreUnexported(zap.SugaredLogger{})); diff != "" {
- t.Errorf("unexpected (-want, +got) = %v", diff)
- }
- })
- }
-}
diff --git a/v1/cloudevents/data_content_encoding.go b/v1/cloudevents/data_content_encoding.go
deleted file mode 100644
index 180102ee3..000000000
--- a/v1/cloudevents/data_content_encoding.go
+++ /dev/null
@@ -1,11 +0,0 @@
-package cloudevents
-
-const (
- Base64 = "base64"
-)
-
-// StringOfBase64 returns a string pointer to "Base64"
-func StringOfBase64() *string {
- a := Base64
- return &a
-}
diff --git a/v1/cloudevents/data_content_encoding_test.go b/v1/cloudevents/data_content_encoding_test.go
deleted file mode 100644
index ab62ba8a6..000000000
--- a/v1/cloudevents/data_content_encoding_test.go
+++ /dev/null
@@ -1,17 +0,0 @@
-package cloudevents_test
-
-import (
- "testing"
-
- ce "github.com/cloudevents/sdk-go/v1/cloudevents"
- "github.com/google/go-cmp/cmp"
-)
-
-func TestStringOfBase64(t *testing.T) {
- want := strptr("base64")
- got := ce.StringOfBase64()
-
- if diff := cmp.Diff(want, got); diff != "" {
- t.Errorf("unexpected string (-want, +got) = %v", diff)
- }
-}
diff --git a/v1/cloudevents/datacodec/codec.go b/v1/cloudevents/datacodec/codec.go
deleted file mode 100644
index 12243addb..000000000
--- a/v1/cloudevents/datacodec/codec.go
+++ /dev/null
@@ -1,96 +0,0 @@
-package datacodec
-
-import (
- "context"
- "fmt"
-
- "github.com/cloudevents/sdk-go/v1/cloudevents/datacodec/json"
- "github.com/cloudevents/sdk-go/v1/cloudevents/datacodec/text"
- "github.com/cloudevents/sdk-go/v1/cloudevents/datacodec/xml"
- "github.com/cloudevents/sdk-go/v1/cloudevents/observability"
-)
-
-// Decoder is the expected function signature for decoding `in` to `out`. What
-// `in` is could be decoder dependent. For example, `in` could be bytes, or a
-// base64 string.
-type Decoder func(ctx context.Context, in, out interface{}) error
-
-// Encoder is the expected function signature for encoding `in` to bytes.
-// Returns an error if the encoder has an issue encoding `in`.
-type Encoder func(ctx context.Context, in interface{}) ([]byte, error)
-
-var decoder map[string]Decoder
-var encoder map[string]Encoder
-
-func init() {
- decoder = make(map[string]Decoder, 10)
- encoder = make(map[string]Encoder, 10)
-
- AddDecoder("", json.Decode)
- AddDecoder("application/json", json.Decode)
- AddDecoder("text/json", json.Decode)
- AddDecoder("application/xml", xml.Decode)
- AddDecoder("text/xml", xml.Decode)
- AddDecoder("text/plain", text.Decode)
-
- AddEncoder("", json.Encode)
- AddEncoder("application/json", json.Encode)
- AddEncoder("text/json", json.Encode)
- AddEncoder("application/xml", xml.Encode)
- AddEncoder("text/xml", xml.Encode)
- AddEncoder("text/plain", text.Encode)
-}
-
-// AddDecoder registers a decoder for a given content type. The codecs will use
-// these to decode the data payload from a cloudevent.Event object.
-func AddDecoder(contentType string, fn Decoder) {
- decoder[contentType] = fn
-}
-
-// AddEncoder registers an encoder for a given content type. The codecs will
-// use these to encode the data payload for a cloudevent.Event object.
-func AddEncoder(contentType string, fn Encoder) {
- encoder[contentType] = fn
-}
-
-// Decode looks up and invokes the decoder registered for the given content
-// type. An error is returned if no decoder is registered for the given
-// content type.
-func Decode(ctx context.Context, contentType string, in, out interface{}) error {
- _, r := observability.NewReporter(ctx, reportDecode)
- err := obsDecode(ctx, contentType, in, out)
- if err != nil {
- r.Error()
- } else {
- r.OK()
- }
- return err
-}
-
-func obsDecode(ctx context.Context, contentType string, in, out interface{}) error {
- if fn, ok := decoder[contentType]; ok {
- return fn(ctx, in, out)
- }
- return fmt.Errorf("[decode] unsupported content type: %q", contentType)
-}
-
-// Encode looks up and invokes the encoder registered for the given content
-// type. An error is returned if no encoder is registered for the given
-// content type.
-func Encode(ctx context.Context, contentType string, in interface{}) ([]byte, error) {
- _, r := observability.NewReporter(ctx, reportEncode)
- b, err := obsEncode(ctx, contentType, in)
- if err != nil {
- r.Error()
- } else {
- r.OK()
- }
- return b, err
-}
-
-func obsEncode(ctx context.Context, contentType string, in interface{}) ([]byte, error) {
- if fn, ok := encoder[contentType]; ok {
- return fn(ctx, in)
- }
- return nil, fmt.Errorf("[encode] unsupported content type: %q", contentType)
-}
diff --git a/v1/cloudevents/datacodec/codec_test.go b/v1/cloudevents/datacodec/codec_test.go
deleted file mode 100644
index 831041e77..000000000
--- a/v1/cloudevents/datacodec/codec_test.go
+++ /dev/null
@@ -1,239 +0,0 @@
-package datacodec_test
-
-import (
- "context"
- "fmt"
- "strings"
- "testing"
-
- "github.com/cloudevents/sdk-go/v1/cloudevents/datacodec"
- "github.com/cloudevents/sdk-go/v1/cloudevents/types"
- "github.com/google/go-cmp/cmp"
-)
-
-func strptr(s string) *string { return &s }
-
-type Example struct {
- Sequence int `json:"id"`
- Message string `json:"message"`
-}
-
-func TestCodecDecode(t *testing.T) {
- testCases := map[string]struct {
- contentType string
- decoder datacodec.Decoder
- in interface{}
- want interface{}
- wantErr string
- }{
- "empty": {},
- "invalid content type": {
- contentType: "unit/testing-invalid",
- wantErr: `[decode] unsupported content type: "unit/testing-invalid"`,
- },
-
- "text/plain": {
- contentType: "text/plain",
- in: "hello😀",
- want: strptr("hello😀"), // Test unicode outiside UTF-8
- },
- "application/json": {
- contentType: "application/json",
- in: []byte(`{"a":"apple","b":"banana"}`),
- want: &map[string]string{
- "a": "apple",
- "b": "banana",
- },
- },
- "application/xml": {
- contentType: "application/xml",
- in: []byte(`7Hello, Structured Encoding v0.2!`),
- want: &Example{Sequence: 7, Message: "Hello, Structured Encoding v0.2!"},
- },
-
- "custom content type": {
- contentType: "unit/testing",
- in: []byte("Hello, Testing"),
- decoder: func(ctx context.Context, in, out interface{}) error {
- if b, ok := in.([]byte); ok {
- if s, k := out.(*map[string]string); k {
- if (*s) == nil {
- (*s) = make(map[string]string)
- }
- (*s)["upper"] = strings.ToUpper(string(b))
- (*s)["lower"] = strings.ToLower(string(b))
- }
- }
- return nil
- },
- want: &map[string]string{
- "upper": "HELLO, TESTING",
- "lower": "hello, testing",
- },
- },
- "custom content type error": {
- contentType: "unit/testing",
- in: []byte("Hello, Testing"),
- decoder: func(ctx context.Context, in, out interface{}) error {
- return fmt.Errorf("expecting unit test error")
- },
- wantErr: "expecting unit test error",
- },
- }
- for n, tc := range testCases {
- t.Run(n, func(t *testing.T) {
-
- if tc.decoder != nil {
- datacodec.AddDecoder(tc.contentType, tc.decoder)
- }
-
- got, _ := types.Allocate(tc.want)
-
- err := datacodec.Decode(context.TODO(), tc.contentType, tc.in, got)
-
- if tc.wantErr != "" || err != nil {
- if diff := cmp.Diff(tc.wantErr, err.Error()); diff != "" {
- t.Errorf("unexpected error (-want, +got) = %v", diff)
- }
- return
- }
-
- if tc.want != nil {
- if diff := cmp.Diff(tc.want, got); diff != "" {
- t.Errorf("unexpected data (-want, +got) = %v", diff)
- }
- }
- })
- }
-}
-
-func TestCodecEncode(t *testing.T) {
- testCases := map[string]struct {
- contentType string
- encoder datacodec.Encoder
- in interface{}
- want []byte
- wantErr string
- }{
- "empty": {},
- "invalid content type": {
- contentType: "unit/testing-invalid",
- wantErr: `[encode] unsupported content type: "unit/testing-invalid"`,
- },
- "blank": {
- contentType: "",
- in: map[string]string{
- "a": "apple",
- "b": "banana",
- },
- want: []byte(`{"a":"apple","b":"banana"}`),
- },
- "application/json": {
- contentType: "application/json",
- in: map[string]string{
- "a": "apple",
- "b": "banana",
- },
- want: []byte(`{"a":"apple","b":"banana"}`),
- },
- "application/xml": {
- contentType: "application/xml",
- in: &Example{Sequence: 7, Message: "Hello, Structured Encoding v0.2!"},
- want: []byte(`7Hello, Structured Encoding v0.2!`),
- },
-
- "custom content type": {
- contentType: "unit/testing",
- in: []string{
- "Hello,",
- "Testing",
- },
- encoder: func(ctx context.Context, in interface{}) ([]byte, error) {
- if s, ok := in.([]string); ok {
- sb := strings.Builder{}
- for _, v := range s {
- if sb.Len() > 0 {
- sb.WriteString(" ")
- }
- sb.WriteString(v)
- }
- return []byte(sb.String()), nil
- }
- return nil, fmt.Errorf("don't get here")
- },
- want: []byte("Hello, Testing"),
- },
- "custom content type error": {
- contentType: "unit/testing",
- in: []byte("Hello, Testing"),
- encoder: func(ctx context.Context, in interface{}) ([]byte, error) {
- return nil, fmt.Errorf("expecting unit test error")
- },
- wantErr: "expecting unit test error",
- },
- }
- for n, tc := range testCases {
- t.Run(n, func(t *testing.T) {
-
- if tc.encoder != nil {
- datacodec.AddEncoder(tc.contentType, tc.encoder)
- }
-
- got, err := datacodec.Encode(context.TODO(), tc.contentType, tc.in)
-
- if tc.wantErr != "" || err != nil {
- if diff := cmp.Diff(tc.wantErr, err.Error()); diff != "" {
- t.Errorf("unexpected error (-want, +got) = %v", diff)
- }
- return
- }
-
- if tc.want != nil {
- if diff := cmp.Diff(tc.want, got); diff != "" {
- t.Errorf("unexpected data (-want, +got) = %v", diff)
- }
- }
- })
- }
-}
-
-//
-//func TestCodecRoundTrip(t *testing.T) {
-// testCases := map[string]struct {
-// contentType string
-// decoder datacodec.Decoder
-// encoder datacodec.Encoder
-// in interface{}
-// want interface{}
-// wantErr string
-// }{
-// "empty": {},
-// }
-// for n, tc := range testCases {
-// t.Run(n, func(t *testing.T) {
-//
-// if tc.decoder != nil {
-// datacodec.AddDecoder(tc.contentType, tc.decoder)
-// }
-//
-//
-// // TODO
-// got, _ := types.Allocate(tc.want)
-//
-// err := datacodec.Decode(tc.contentType, tc.in, got)
-//
-// if tc.wantErr != "" || err != nil {
-// if diff := cmp.Diff(tc.wantErr, err.Error()); diff != "" {
-// t.Errorf("unexpected error (-want, +got) = %v", diff)
-// }
-// return
-// }
-//
-// if tc.want != nil {
-// if diff := cmp.Diff(tc.want, got); diff != "" {
-// t.Errorf("unexpected data (-want, +got) = %v", diff)
-// }
-// }
-// })
-// }
-//}
diff --git a/v1/cloudevents/datacodec/doc.go b/v1/cloudevents/datacodec/doc.go
deleted file mode 100644
index 9e401534e..000000000
--- a/v1/cloudevents/datacodec/doc.go
+++ /dev/null
@@ -1,5 +0,0 @@
-/*
-Package datacodec holds the data codec registry and adds known encoders and decoders supporting media types such as
-`application/json` and `application/xml`.
-*/
-package datacodec
diff --git a/v1/cloudevents/datacodec/json/data.go b/v1/cloudevents/datacodec/json/data.go
deleted file mode 100644
index 31ef508b1..000000000
--- a/v1/cloudevents/datacodec/json/data.go
+++ /dev/null
@@ -1,97 +0,0 @@
-package json
-
-import (
- "context"
- "encoding/json"
- "fmt"
- "reflect"
- "strconv"
-
- "github.com/cloudevents/sdk-go/v1/cloudevents/observability"
-)
-
-// Decode takes `in` as []byte, or base64 string, normalizes in to unquoted and
-// base64 decoded []byte if required, and then attempts to use json.Unmarshal
-// to convert those bytes to `out`. Returns and error if this process fails.
-func Decode(ctx context.Context, in, out interface{}) error {
- _, r := observability.NewReporter(ctx, reportDecode)
- err := obsDecode(ctx, in, out)
- if err != nil {
- r.Error()
- } else {
- r.OK()
- }
- return err
-}
-
-func obsDecode(ctx context.Context, in, out interface{}) error {
- if in == nil {
- return nil
- }
- if out == nil {
- return fmt.Errorf("out is nil")
- }
-
- b, ok := in.([]byte) // TODO: I think there is fancy marshaling happening here. Fix with reflection?
- if !ok {
- var err error
- b, err = json.Marshal(in)
- if err != nil {
- return fmt.Errorf("[json] failed to marshal in: %s", err.Error())
- }
- }
-
- // TODO: the spec says json could be just data... At the moment we expect wrapped.
- if len(b) > 1 && (b[0] == byte('"') || (b[0] == byte('\\') && b[1] == byte('"'))) {
- s, err := strconv.Unquote(string(b))
- if err != nil {
- return fmt.Errorf("[json] failed to unquote in: %s", err.Error())
- }
- if len(s) > 0 && (s[0] == '{' || s[0] == '[') {
- // looks like json, use it
- b = []byte(s)
- }
- }
-
- if err := json.Unmarshal(b, out); err != nil {
- return fmt.Errorf("[json] found bytes \"%s\", but failed to unmarshal: %s", string(b), err.Error())
- }
- return nil
-}
-
-// Encode attempts to json.Marshal `in` into bytes. Encode will inspect `in`
-// and returns `in` unmodified if it is detected that `in` is already a []byte;
-// Or json.Marshal errors.
-func Encode(ctx context.Context, in interface{}) ([]byte, error) {
- _, r := observability.NewReporter(ctx, reportEncode)
- b, err := obsEncode(ctx, in)
- if err != nil {
- r.Error()
- } else {
- r.OK()
- }
- return b, err
-}
-
-func obsEncode(ctx context.Context, in interface{}) ([]byte, error) {
- if in == nil {
- return nil, nil
- }
-
- it := reflect.TypeOf(in)
- switch it.Kind() {
- case reflect.Slice:
- if it.Elem().Kind() == reflect.Uint8 {
-
- if b, ok := in.([]byte); ok && len(b) > 0 {
- // check to see if it is a pre-encoded byte string.
- if b[0] == byte('"') || b[0] == byte('{') || b[0] == byte('[') {
- return b, nil
- }
- }
-
- }
- }
-
- return json.Marshal(in)
-}
diff --git a/v1/cloudevents/datacodec/json/data_test.go b/v1/cloudevents/datacodec/json/data_test.go
deleted file mode 100644
index 956814973..000000000
--- a/v1/cloudevents/datacodec/json/data_test.go
+++ /dev/null
@@ -1,252 +0,0 @@
-package json_test
-
-import (
- "context"
- "encoding/json"
- "fmt"
- "testing"
- "time"
-
- cej "github.com/cloudevents/sdk-go/v1/cloudevents/datacodec/json"
- "github.com/cloudevents/sdk-go/v1/cloudevents/types"
- "github.com/google/go-cmp/cmp"
-)
-
-type DataExample struct {
- AnInt int `json:"a,omitempty"`
- AString string `json:"b,omitempty"`
- AnArray []string `json:"c,omitempty"`
- AMap map[string]map[string]int `json:"d,omitempty"`
- ATime *time.Time `json:"e,omitempty"`
-}
-
-type BadMarshal struct{}
-
-func (b BadMarshal) MarshalJSON() ([]byte, error) {
- return nil, fmt.Errorf("BadMashal Error")
-}
-
-func TestCodecDecode(t *testing.T) {
- now := time.Now()
-
- testCases := map[string]struct {
- in interface{}
- want interface{}
- wantErr string
- }{
- "empty": {},
- "out nil": {
- in: "not nil",
- wantErr: "out is nil",
- },
- "not a []byte": {
- in: "something that is not a map",
- want: &map[string]string{
- "an": "error",
- },
- wantErr: `[json] found bytes ""something that is not a map"", but failed to unmarshal: json: cannot unmarshal string into Go value of type map[string]string`,
- },
- "BadMarshal": {
- in: BadMarshal{},
- want: &BadMarshal{},
- wantErr: "[json] failed to marshal in: json: error calling MarshalJSON for type json_test.BadMarshal: BadMashal Error",
- },
- "Bad Quotes": {
- in: []byte{'\\', '"'},
- want: &map[string]string{
- "an": "error",
- },
- wantErr: "[json] failed to unquote in: invalid syntax",
- },
- "simple": {
- in: []byte(`{"a":"apple","b":"banana"}`),
- want: &map[string]string{
- "a": "apple",
- "b": "banana",
- },
- },
- "complex empty": {
- in: []byte(`{}`),
- want: &DataExample{},
- },
- "simple array": {
- in: []byte(`["apple","banana"]`),
- want: &[]string{
- "apple",
- "banana",
- },
- },
- "simple quoted array": {
- in: []byte(`"[\"apple\",\"banana\"]"`),
- want: &[]string{
- "apple",
- "banana",
- },
- },
- "complex filled": {
- in: func() []byte {
- data := &DataExample{
- AnInt: 42,
- AMap: map[string]map[string]int{
- "a": {"1": 1, "2": 2, "3": 3},
- "z": {"3": 3, "2": 2, "1": 1},
- },
- AString: "Hello, World!",
- ATime: &now,
- AnArray: []string{"Anne", "Bob", "Chad"},
- }
-
- j, err := json.Marshal(data)
- if err != nil {
- t.Errorf("failed to marshal test data: %s", err.Error())
- }
- return j
- }(),
- want: &DataExample{
- AnInt: 42,
- AMap: map[string]map[string]int{
- "a": {"1": 1, "2": 2, "3": 3},
- "z": {"3": 3, "2": 2, "1": 1},
- },
- AString: "Hello, World!",
- ATime: &now,
- AnArray: []string{"Anne", "Bob", "Chad"},
- },
- },
- "object in": {
- in: &DataExample{
- AnInt: 42,
- },
- want: &DataExample{
- AnInt: 42,
- },
- },
- }
- for n, tc := range testCases {
- t.Run(n, func(t *testing.T) {
- got, _ := types.Allocate(tc.want)
-
- err := cej.Decode(context.TODO(), tc.in, got)
- if tc.wantErr != "" || err != nil {
- var gotErr string
- if err != nil {
- gotErr = err.Error()
- }
- if diff := cmp.Diff(tc.wantErr, gotErr); diff != "" {
- t.Errorf("unexpected error (-want, +got) = %v", diff)
- }
- return
- }
-
- if tc.want != nil {
- if diff := cmp.Diff(tc.want, got); diff != "" {
- t.Errorf("unexpected data (-want, +got) = %v", diff)
- }
- }
- })
- }
-}
-
-// TODO: test for bad []byte input?
-func TestCodecEncode(t *testing.T) {
- now := time.Now()
-
- testCases := map[string]struct {
- in interface{}
- want []byte
- wantErr string
- }{
- "empty": {},
- "BadMarshal": {
- in: BadMarshal{},
- wantErr: "json: error calling MarshalJSON for type json_test.BadMarshal: BadMashal Error",
- },
- "already encoded object": {
- in: []byte(`{"a":"apple","b":"banana"}`),
- want: []byte(`{"a":"apple","b":"banana"}`),
- },
- "already encoded quote": {
- in: []byte(`"{"a":"apple","b":"banana"}"`),
- want: []byte(`"{"a":"apple","b":"banana"}"`),
- },
- "already encoded slice": {
- in: []byte(`["apple","banana"]`),
- want: []byte(`["apple","banana"]`),
- },
- "simple": {
- in: map[string]string{
- "a": "apple",
- "b": "banana",
- },
- want: []byte(`{"a":"apple","b":"banana"}`),
- },
- "complex empty": {
- in: DataExample{},
- want: []byte(`{}`),
- },
- "simple array": {
- in: &[]string{
- "apple",
- "banana",
- },
- want: []byte(`["apple","banana"]`),
- },
- "complex filled": {
- in: &DataExample{
- AnInt: 42,
- AMap: map[string]map[string]int{
- "a": {"1": 1, "2": 2, "3": 3},
- "z": {"3": 3, "2": 2, "1": 1},
- },
- AString: "Hello, World!",
- ATime: &now,
- AnArray: []string{"Anne", "Bob", "Chad"},
- },
- want: func() []byte {
- data := &DataExample{
- AnInt: 42,
- AMap: map[string]map[string]int{
- "a": {"1": 1, "2": 2, "3": 3},
- "z": {"3": 3, "2": 2, "1": 1},
- },
- AString: "Hello, World!",
- ATime: &now,
- AnArray: []string{"Anne", "Bob", "Chad"},
- }
-
- j, err := json.Marshal(data)
- if err != nil {
- t.Errorf("failed to marshal test data: %s", err.Error())
- }
- return j
- }(),
- },
- "object in": {
- in: &DataExample{
- AnInt: 42,
- },
- want: []byte(`{"a":42}`),
- },
- }
- for n, tc := range testCases {
- t.Run(n, func(t *testing.T) {
- got, err := cej.Encode(context.TODO(), tc.in)
- if tc.wantErr != "" || err != nil {
- var gotErr string
- if err != nil {
- gotErr = err.Error()
- }
- if diff := cmp.Diff(tc.wantErr, gotErr); diff != "" {
- t.Errorf("unexpected error (-want, +got) = %v", diff)
- }
- return
- }
-
- if tc.want != nil {
- if diff := cmp.Diff(tc.want, got); diff != "" {
- t.Errorf("unexpected data (-want, +got) = %v", diff)
- }
- }
- })
- }
-}
diff --git a/v1/cloudevents/datacodec/json/doc.go b/v1/cloudevents/datacodec/json/doc.go
deleted file mode 100644
index 86772c2e3..000000000
--- a/v1/cloudevents/datacodec/json/doc.go
+++ /dev/null
@@ -1,4 +0,0 @@
-/*
-Package json holds the encoder/decoder implementation for `application/json`.
-*/
-package json
diff --git a/v1/cloudevents/datacodec/json/observability.go b/v1/cloudevents/datacodec/json/observability.go
deleted file mode 100644
index 3e88f4907..000000000
--- a/v1/cloudevents/datacodec/json/observability.go
+++ /dev/null
@@ -1,51 +0,0 @@
-package json
-
-import (
- "github.com/cloudevents/sdk-go/v1/cloudevents/observability"
- "go.opencensus.io/stats"
- "go.opencensus.io/stats/view"
-)
-
-var (
- // LatencyMs measures the latency in milliseconds for the CloudEvents json
- // data codec methods.
- LatencyMs = stats.Float64("cloudevents.io/sdk-go/datacodec/json/latency", "The latency in milliseconds for the CloudEvents json data codec methods.", "ms")
-)
-
-var (
- // LatencyView is an OpenCensus view that shows data codec json method latency.
- LatencyView = &view.View{
- Name: "datacodec/json/latency",
- Measure: LatencyMs,
- Description: "The distribution of latency inside of the json data codec for CloudEvents.",
- Aggregation: view.Distribution(0, .01, .1, 1, 10, 100, 1000, 10000),
- TagKeys: observability.LatencyTags(),
- }
-)
-
-type observed int32
-
-// Adheres to Observable
-var _ observability.Observable = observed(0)
-
-const (
- reportEncode observed = iota
- reportDecode
-)
-
-// MethodName implements Observable.MethodName
-func (o observed) MethodName() string {
- switch o {
- case reportEncode:
- return "encode"
- case reportDecode:
- return "decode"
- default:
- return "unknown"
- }
-}
-
-// LatencyMs implements Observable.LatencyMs
-func (o observed) LatencyMs() *stats.Float64Measure {
- return LatencyMs
-}
diff --git a/v1/cloudevents/datacodec/observability.go b/v1/cloudevents/datacodec/observability.go
deleted file mode 100644
index a0ad7deb3..000000000
--- a/v1/cloudevents/datacodec/observability.go
+++ /dev/null
@@ -1,51 +0,0 @@
-package datacodec
-
-import (
- "github.com/cloudevents/sdk-go/v1/cloudevents/observability"
- "go.opencensus.io/stats"
- "go.opencensus.io/stats/view"
-)
-
-var (
- // LatencyMs measures the latency in milliseconds for the CloudEvents generic
- // codec data methods.
- LatencyMs = stats.Float64("cloudevents.io/sdk-go/datacodec/latency", "The latency in milliseconds for the CloudEvents generic data codec methods.", "ms")
-)
-
-var (
- // LatencyView is an OpenCensus view that shows data codec method latency.
- LatencyView = &view.View{
- Name: "datacodec/latency",
- Measure: LatencyMs,
- Description: "The distribution of latency inside of the generic data codec for CloudEvents.",
- Aggregation: view.Distribution(0, .01, .1, 1, 10, 100, 1000, 10000),
- TagKeys: observability.LatencyTags(),
- }
-)
-
-type observed int32
-
-// Adheres to Observable
-var _ observability.Observable = observed(0)
-
-const (
- reportEncode observed = iota
- reportDecode
-)
-
-// MethodName implements Observable.MethodName
-func (o observed) MethodName() string {
- switch o {
- case reportEncode:
- return "encode"
- case reportDecode:
- return "decode"
- default:
- return "unknown"
- }
-}
-
-// LatencyMs implements Observable.LatencyMs
-func (o observed) LatencyMs() *stats.Float64Measure {
- return LatencyMs
-}
diff --git a/v1/cloudevents/datacodec/text/text.go b/v1/cloudevents/datacodec/text/text.go
deleted file mode 100644
index 3c37c5b13..000000000
--- a/v1/cloudevents/datacodec/text/text.go
+++ /dev/null
@@ -1,33 +0,0 @@
-// Text codec converts []byte or string to string and vice-versa.
-package text
-
-import (
- "context"
- "fmt"
-)
-
-func Decode(_ context.Context, in, out interface{}) error {
- p, _ := out.(*string)
- if p == nil {
- return fmt.Errorf("text.Decode out: want *string, got %T", out)
- }
- switch s := in.(type) {
- case string:
- *p = s
- case []byte:
- *p = string(s)
- case nil: // treat nil like []byte{}
- *p = ""
- default:
- return fmt.Errorf("text.Decode in: want []byte or string, got %T", in)
- }
- return nil
-}
-
-func Encode(_ context.Context, in interface{}) ([]byte, error) {
- s, ok := in.(string)
- if !ok {
- return nil, fmt.Errorf("text.Encode in: want string, got %T", in)
- }
- return []byte(s), nil
-}
diff --git a/v1/cloudevents/datacodec/text/text_test.go b/v1/cloudevents/datacodec/text/text_test.go
deleted file mode 100644
index ba51dc30f..000000000
--- a/v1/cloudevents/datacodec/text/text_test.go
+++ /dev/null
@@ -1,46 +0,0 @@
-package text_test
-
-import (
- "context"
- "testing"
-
- "github.com/cloudevents/sdk-go/v1/cloudevents/datacodec/text"
- "github.com/stretchr/testify/assert"
-)
-
-var ctx = context.Background()
-
-func TestEncode(t *testing.T) {
- assert := assert.New(t)
-
- b, err := text.Encode(ctx, "")
- assert.NoError(err)
- assert.Empty(b)
-
- b, err = text.Encode(ctx, "hello😀")
- assert.NoError(err)
- assert.Equal("hello😀", string(b))
-
- _, err = text.Encode(ctx, []byte("x"))
- assert.EqualError(err, "text.Encode in: want string, got []uint8")
- _, err = text.Encode(ctx, nil)
- assert.EqualError(err, "text.Encode in: want string, got ")
-}
-
-func TestDecode(t *testing.T) {
- assert := assert.New(t)
- var s string
- assert.NoError(text.Decode(ctx, "hello", &s))
- assert.Equal("hello", s)
- assert.NoError(text.Decode(ctx, []byte("bye"), &s))
- assert.Equal("bye", s)
- assert.NoError(text.Decode(ctx, []byte{}, &s))
- assert.Equal("", s)
- s = "xxx"
- assert.NoError(text.Decode(ctx, nil, &s))
- assert.Equal("", s)
-
- assert.EqualError(text.Decode(ctx, 123, &s), "text.Decode in: want []byte or string, got int")
- assert.EqualError(text.Decode(ctx, "", nil), "text.Decode out: want *string, got ")
- assert.EqualError(text.Decode(ctx, "", 1), "text.Decode out: want *string, got int")
-}
diff --git a/v1/cloudevents/datacodec/xml/data.go b/v1/cloudevents/datacodec/xml/data.go
deleted file mode 100644
index 9f42b7fa6..000000000
--- a/v1/cloudevents/datacodec/xml/data.go
+++ /dev/null
@@ -1,90 +0,0 @@
-package xml
-
-import (
- "context"
- "encoding/base64"
- "encoding/xml"
- "fmt"
- "strconv"
-
- "github.com/cloudevents/sdk-go/v1/cloudevents/observability"
-)
-
-// Decode takes `in` as []byte, or base64 string, normalizes in to unquoted and
-// base64 decoded []byte if required, and then attempts to use xml.Unmarshal
-// to convert those bytes to `out`. Returns and error if this process fails.
-func Decode(ctx context.Context, in, out interface{}) error {
- _, r := observability.NewReporter(ctx, reportDecode)
- err := obsDecode(ctx, in, out)
- if err != nil {
- r.Error()
- } else {
- r.OK()
- }
- return err
-}
-
-func obsDecode(ctx context.Context, in, out interface{}) error {
- if in == nil {
- return nil
- }
-
- b, ok := in.([]byte)
- if !ok {
- var err error
- b, err = xml.Marshal(in)
- if err != nil {
- return fmt.Errorf("[xml] failed to marshal in: %s", err.Error())
- }
- }
-
- // If the message is encoded as a base64 block as a string, we need to
- // decode that first before trying to unmarshal the bytes
- if len(b) > 1 && (b[0] == byte('"') || (b[0] == byte('\\') && b[1] == byte('"'))) {
- s, err := strconv.Unquote(string(b))
- if err != nil {
- return fmt.Errorf("[xml] failed to unquote quoted data: %s", err.Error())
- }
- if len(s) > 0 && s[0] == '<' {
- // looks like xml, use it
- b = []byte(s)
- } else if len(s) > 0 {
- // looks like base64, decode
- bs, err := base64.StdEncoding.DecodeString(s)
- if err != nil {
- return fmt.Errorf("[xml] failed to decode base64 encoded string: %s", err.Error())
- }
- b = bs
- }
- }
-
- if err := xml.Unmarshal(b, out); err != nil {
- return fmt.Errorf("[xml] found bytes, but failed to unmarshal: %s %s", err.Error(), string(b))
- }
- return nil
-}
-
-// Encode attempts to xml.Marshal `in` into bytes. Encode will inspect `in`
-// and returns `in` unmodified if it is detected that `in` is already a []byte;
-// Or xml.Marshal errors.
-func Encode(ctx context.Context, in interface{}) ([]byte, error) {
- _, r := observability.NewReporter(ctx, reportEncode)
- b, err := obsEncode(ctx, in)
- if err != nil {
- r.Error()
- } else {
- r.OK()
- }
- return b, err
-}
-
-func obsEncode(ctx context.Context, in interface{}) ([]byte, error) {
- if b, ok := in.([]byte); ok {
- // check to see if it is a pre-encoded byte string.
- if len(b) > 0 && b[0] == byte('"') {
- return b, nil
- }
- }
-
- return xml.Marshal(in)
-}
diff --git a/v1/cloudevents/datacodec/xml/data_test.go b/v1/cloudevents/datacodec/xml/data_test.go
deleted file mode 100644
index 751cf2b09..000000000
--- a/v1/cloudevents/datacodec/xml/data_test.go
+++ /dev/null
@@ -1,128 +0,0 @@
-package xml_test
-
-import (
- "context"
- "encoding/xml"
- "fmt"
- "strings"
- "testing"
- "time"
-
- cex "github.com/cloudevents/sdk-go/v1/cloudevents/datacodec/xml"
- "github.com/cloudevents/sdk-go/v1/cloudevents/types"
- "github.com/google/go-cmp/cmp"
-)
-
-type DataExample struct {
- AnInt int `xml:"a,omitempty"`
- AString string `xml:"b,omitempty"`
- AnArray []string `xml:"c,omitempty"`
- ATime *time.Time `xml:"e,omitempty"`
-}
-
-type BadDataExample struct {
- AnInt int `xml:"a,omitempty"`
-}
-
-func (b BadDataExample) MarshalXML(e *xml.Encoder, start xml.StartElement) error {
- return fmt.Errorf("unit test")
-}
-
-// Basic data struct.
-type Example struct {
- Sequence int `json:"id"`
- Message string `json:"message"`
-}
-
-func TestCodecDecode(t *testing.T) {
- now := time.Now()
-
- testCases := map[string]struct {
- in interface{}
- want interface{}
- wantErr string
- }{
- "empty": {},
- "not bytes": {
- in: &BadDataExample{},
- wantErr: "[xml] failed to marshal in",
- },
- "structured type encoding, escaped": {
- in: []byte(`"7Hello, Structured Encoding v0.2!"`),
- want: &Example{Sequence: 7, Message: "Hello, Structured Encoding v0.2!"},
- },
- "structured type encoding, escaped error": {
- in: []byte(`"7Hello, Structured Encoding v0.2!"`),
- wantErr: "[xml] found bytes, but failed to unmarshal",
- },
- "structured type encoding, base64": {
- in: []byte(`"PEV4YW1wbGU+PFNlcXVlbmNlPjc8L1NlcXVlbmNlPjxNZXNzYWdlPkhlbGxvLCBTdHJ1Y3R1cmVkIEVuY29kaW5nIHYwLjIhPC9NZXNzYWdlPjwvRXhhbXBsZT4="`),
- want: &Example{Sequence: 7, Message: "Hello, Structured Encoding v0.2!"},
- },
- "structured type encoding, bad quote base64": {
- in: []byte(`"PEV4YW1wbGU+PFNlcXVlbmNlPjc8L1NlcXVlbmNlPjxNZXNzYWdlPkhlbGxvLCBTdHJ1Y3R1cmVkIEVuY29kaW5nIHYwLjIhPC9NZXNzYWdlPjwvRXhhbXBsZT4=`),
- wantErr: "[xml] failed to unquote quoted data",
- },
- "structured type encoding, bad base64": {
- in: []byte(`"?EV4YW1wbGU+PFNlcXVlbmNlPjc8L1NlcXVlbmNlPjxNZXNzYWdlPkhlbGxvLCBTdHJ1Y3R1cmVkIEVuY29kaW5nIHYwLjIhPC9NZXNzYWdlPjwvRXhhbXBsZT4="`),
- wantErr: "[xml] failed to decode base64 encoded string",
- },
- "complex filled": {
- in: func() []byte {
- data := &DataExample{
- AnInt: 42,
- AString: "Hello, World!",
- ATime: &now,
- AnArray: []string{"Anne", "Bob", "Chad"},
- }
-
- j, err := xml.Marshal(data)
-
- if err != nil {
- t.Errorf("failed to marshal test data: %s", err.Error())
- }
- return j
- }(),
- want: &DataExample{
- AnInt: 42,
- AString: "Hello, World!",
- ATime: &now,
- AnArray: []string{"Anne", "Bob", "Chad"},
- },
- },
- "object in": {
- in: &DataExample{
- AnInt: 42,
- },
- want: &DataExample{
- AnInt: 42,
- },
- },
- }
- for n, tc := range testCases {
- t.Run(n, func(t *testing.T) {
-
- got, _ := types.Allocate(tc.want)
-
- err := cex.Decode(context.TODO(), tc.in, got)
-
- if tc.wantErr != "" {
- if err != nil {
- gotErr := err.Error()
- if !strings.Contains(gotErr, tc.wantErr) {
- t.Errorf("unexpected error, expected to contain %q, got: %q", tc.wantErr, gotErr)
- }
- } else {
- t.Errorf("expected error to contain %q, got: nil", tc.wantErr)
- }
- return
- }
-
- if tc.want != nil {
- if diff := cmp.Diff(tc.want, got); diff != "" {
- t.Errorf("unexpected data (-want, +got) = %v", diff)
- }
- }
- })
- }
-}
diff --git a/v1/cloudevents/datacodec/xml/doc.go b/v1/cloudevents/datacodec/xml/doc.go
deleted file mode 100644
index d90b7c444..000000000
--- a/v1/cloudevents/datacodec/xml/doc.go
+++ /dev/null
@@ -1,4 +0,0 @@
-/*
-Package xml holds the encoder/decoder implementation for `application/xml`.
-*/
-package xml
diff --git a/v1/cloudevents/datacodec/xml/observability.go b/v1/cloudevents/datacodec/xml/observability.go
deleted file mode 100644
index 92102555c..000000000
--- a/v1/cloudevents/datacodec/xml/observability.go
+++ /dev/null
@@ -1,51 +0,0 @@
-package xml
-
-import (
- "github.com/cloudevents/sdk-go/v1/cloudevents/observability"
- "go.opencensus.io/stats"
- "go.opencensus.io/stats/view"
-)
-
-var (
- // LatencyMs measures the latency in milliseconds for the CloudEvents xml data
- // codec methods.
- LatencyMs = stats.Float64("cloudevents.io/sdk-go/datacodec/xml/latency", "The latency in milliseconds for the CloudEvents xml data codec methods.", "ms")
-)
-
-var (
- // LatencyView is an OpenCensus view that shows data codec xml method latency.
- LatencyView = &view.View{
- Name: "datacodec/xml/latency",
- Measure: LatencyMs,
- Description: "The distribution of latency inside of the xml data codec for CloudEvents.",
- Aggregation: view.Distribution(0, .01, .1, 1, 10, 100, 1000, 10000),
- TagKeys: observability.LatencyTags(),
- }
-)
-
-type observed int32
-
-// Adheres to Observable
-var _ observability.Observable = observed(0)
-
-const (
- reportEncode observed = iota
- reportDecode
-)
-
-// MethodName implements Observable.MethodName
-func (o observed) MethodName() string {
- switch o {
- case reportEncode:
- return "encode"
- case reportDecode:
- return "decode"
- default:
- return "unknown"
- }
-}
-
-// LatencyMs implements Observable.LatencyMs
-func (o observed) LatencyMs() *stats.Float64Measure {
- return LatencyMs
-}
diff --git a/v1/cloudevents/doc.go b/v1/cloudevents/doc.go
deleted file mode 100644
index cc2201da9..000000000
--- a/v1/cloudevents/doc.go
+++ /dev/null
@@ -1,4 +0,0 @@
-/*
-Package cloudevents provides primitives to work with CloudEvents specification: https://github.com/cloudevents/spec.
-*/
-package cloudevents
diff --git a/v1/cloudevents/event.go b/v1/cloudevents/event.go
deleted file mode 100644
index 2f605fd3c..000000000
--- a/v1/cloudevents/event.go
+++ /dev/null
@@ -1,132 +0,0 @@
-package cloudevents
-
-import (
- "bytes"
- "encoding/json"
- "fmt"
- "strings"
-)
-
-// Event represents the canonical representation of a CloudEvent.
-type Event struct {
- Context EventContext
- Data interface{}
- DataEncoded bool
- DataBinary bool
- FieldErrors map[string]error
-}
-
-const (
- defaultEventVersion = CloudEventsVersionV1
-)
-
-func (e *Event) fieldError(field string, err error) {
- if e.FieldErrors == nil {
- e.FieldErrors = make(map[string]error, 0)
- }
- e.FieldErrors[field] = err
-}
-
-func (e *Event) fieldOK(field string) {
- if e.FieldErrors != nil {
- delete(e.FieldErrors, field)
- }
-}
-
-// New returns a new Event, an optional version can be passed to change the
-// default spec version from 1.0 to the provided version.
-func New(version ...string) Event {
- specVersion := defaultEventVersion // TODO: should there be a default? or set a default?
- if len(version) >= 1 {
- specVersion = version[0]
- }
- e := &Event{}
- e.SetSpecVersion(specVersion)
- return *e
-}
-
-// DEPRECATED: Access extensions directly via the e.Extensions() map.
-// Use functions in the types package to convert extension values.
-// For example replace this:
-//
-// var i int
-// err := e.ExtensionAs("foo", &i)
-//
-// With this:
-//
-// i, err := types.ToInteger(e.Extensions["foo"])
-//
-func (e Event) ExtensionAs(name string, obj interface{}) error {
- return e.Context.ExtensionAs(name, obj)
-}
-
-// Validate performs a spec based validation on this event.
-// Validation is dependent on the spec version specified in the event context.
-func (e Event) Validate() error {
- if e.Context == nil {
- return fmt.Errorf("every event conforming to the CloudEvents specification MUST include a context")
- }
-
- if e.FieldErrors != nil {
- errs := make([]string, 0)
- for f, e := range e.FieldErrors {
- errs = append(errs, fmt.Sprintf("%q: %s,", f, e))
- }
- if len(errs) > 0 {
- return fmt.Errorf("previous field errors: [%s]", strings.Join(errs, "\n"))
- }
- }
-
- if err := e.Context.Validate(); err != nil {
- return err
- }
-
- // TODO: validate data.
-
- return nil
-}
-
-// String returns a pretty-printed representation of the Event.
-func (e Event) String() string {
- b := strings.Builder{}
-
- b.WriteString("Validation: ")
-
- valid := e.Validate()
- if valid == nil {
- b.WriteString("valid\n")
- } else {
- b.WriteString("invalid\n")
- }
- if valid != nil {
- b.WriteString(fmt.Sprintf("Validation Error: \n%s\n", valid.Error()))
- }
-
- b.WriteString(e.Context.String())
-
- if e.Data != nil {
- b.WriteString("Data,\n ")
- if strings.HasPrefix(e.DataContentType(), ApplicationJSON) {
- var prettyJSON bytes.Buffer
-
- data, ok := e.Data.([]byte)
- if !ok {
- var err error
- data, err = json.Marshal(e.Data)
- if err != nil {
- data = []byte(err.Error())
- }
- }
- err := json.Indent(&prettyJSON, data, " ", " ")
- if err != nil {
- b.Write(e.Data.([]byte))
- } else {
- b.Write(prettyJSON.Bytes())
- }
- } else {
- b.Write(e.Data.([]byte))
- }
- b.WriteString("\n")
- }
- return b.String()
-}
diff --git a/v1/cloudevents/event_data.go b/v1/cloudevents/event_data.go
deleted file mode 100644
index 63cc2d567..000000000
--- a/v1/cloudevents/event_data.go
+++ /dev/null
@@ -1,135 +0,0 @@
-package cloudevents
-
-import (
- "context"
- "encoding/base64"
- "errors"
- "fmt"
- "strconv"
-
- "github.com/cloudevents/sdk-go/v1/cloudevents/datacodec"
-)
-
-// Data is special. Break it out into it's own file.
-
-// SetData implements EventWriter.SetData
-func (e *Event) SetData(obj interface{}) error {
- if e.SpecVersion() != CloudEventsVersionV1 {
- return e.legacySetData(obj)
- }
-
- // Version 1.0 and above.
-
- // TODO: we will have to be smarter about how data relates to media type.
- // but the issue is we can not just encode data anymore without understanding
- // what the encoding will be on the outbound event. Structured will use
- // data_base64, binary will not (if the transport supports binary mode).
-
- // TODO: look at content encoding too.
-
- switch obj.(type) {
- case []byte:
- e.Data = obj
- e.DataEncoded = true
- e.DataBinary = true
- default:
- data, err := datacodec.Encode(context.Background(), e.DataMediaType(), obj)
- if err != nil {
- return err
- }
- e.Data = data
- e.DataEncoded = true
- e.DataBinary = false
- }
-
- return nil
-}
-
-func (e *Event) legacySetData(obj interface{}) error {
- data, err := datacodec.Encode(context.Background(), e.DataMediaType(), obj)
- if err != nil {
- return err
- }
- if e.DeprecatedDataContentEncoding() == Base64 {
- buf := make([]byte, base64.StdEncoding.EncodedLen(len(data)))
- base64.StdEncoding.Encode(buf, data)
- e.Data = string(buf)
- } else {
- e.Data = data
- }
- e.DataEncoded = true
- return nil
-}
-
-func (e *Event) DataBytes() ([]byte, error) {
- if !e.DataEncoded {
- if err := e.SetData(e.Data); err != nil {
- return nil, err
- }
- }
-
- b, ok := e.Data.([]byte)
- if !ok {
- if s, ok := e.Data.(string); ok {
- b = []byte(s)
- } else {
- // No data.
- return []byte(nil), nil
- }
- }
- return b, nil
-}
-
-const (
- quotes = `"'`
-)
-
-// DataAs attempts to populate the provided data object with the event payload.
-// data should be a pointer type.
-func (e Event) DataAs(data interface{}) error { // TODO: Clean this function up
- if e.Data == nil {
- return nil
- }
- obj, ok := e.Data.([]byte)
- if !ok {
- if s, ok := e.Data.(string); ok {
- obj = []byte(s)
- } else {
- return errors.New("data was not a byte slice or string")
- }
- }
- if len(obj) == 0 {
- // No data.
- return nil
- }
- if e.Context.DeprecatedGetDataContentEncoding() == Base64 {
- var bs []byte
- // test to see if we need to unquote the data.
- if obj[0] == quotes[0] || obj[0] == quotes[1] {
- str, err := strconv.Unquote(string(obj))
- if err != nil {
- return err
- }
- bs = []byte(str)
- } else {
- bs = obj
- }
-
- buf := make([]byte, base64.StdEncoding.DecodedLen(len(bs)))
- n, err := base64.StdEncoding.Decode(buf, bs)
- if err != nil {
- return fmt.Errorf("failed to decode data from base64: %s", err.Error())
- }
- obj = buf[:n]
- }
-
- mediaType := ""
- if e.Context.GetDataContentType() != "" {
- var err error
- mediaType, err = e.Context.GetDataMediaType()
- if err != nil {
- return err
- }
- }
- return datacodec.Decode(context.Background(), mediaType, obj, data)
-}
diff --git a/v1/cloudevents/event_data_test.go b/v1/cloudevents/event_data_test.go
deleted file mode 100644
index 613c10855..000000000
--- a/v1/cloudevents/event_data_test.go
+++ /dev/null
@@ -1,199 +0,0 @@
-package cloudevents_test
-
-import (
- "strings"
- "testing"
-
- ce "github.com/cloudevents/sdk-go/v1/cloudevents"
- "github.com/cloudevents/sdk-go/v1/cloudevents/types"
- "github.com/google/go-cmp/cmp"
-)
-
-type DataTest struct {
- event func(string) ce.Event
- set interface{}
- want interface{}
- wantErr string
-}
-
-func TestEventSetData_Json(t *testing.T) {
- // All version should be the same, so run through them all.
-
- versions := []string{ce.CloudEventsVersionV01, ce.CloudEventsVersionV02, ce.CloudEventsVersionV03}
-
- testCases := map[string]DataTest{
- "empty": {
- event: func(version string) ce.Event {
- return ce.New(version)
- },
- want: nil,
- },
- "defaults": {
- event: func(version string) ce.Event {
- return ce.New(version)
- },
- set: map[string]interface{}{
- "hello": "unittest",
- },
- want: []byte(`{"hello":"unittest"}`),
- },
- "text/json": {
- event: func(version string) ce.Event {
- e := ce.New(version)
- e.SetDataContentType("text/json")
- return e
- },
- set: map[string]interface{}{
- "hello": "unittest",
- },
- want: []byte(`{"hello":"unittest"}`),
- },
- "application/json": {
- event: func(version string) ce.Event {
- e := ce.New(version)
- e.SetDataContentType("application/json")
- return e
- },
- set: map[string]interface{}{
- "hello": "unittest",
- },
- want: []byte(`{"hello":"unittest"}`),
- },
- "application/json+base64": {
- event: func(version string) ce.Event {
- e := ce.New(version)
- e.SetDataContentType("application/json")
- e.SetDataContentEncoding(ce.Base64)
- return e
- },
- set: map[string]interface{}{
- "hello": "unittest",
- },
- want: `eyJoZWxsbyI6InVuaXR0ZXN0In0=`,
- },
- }
- for n, tc := range testCases {
- for _, version := range versions {
- t.Run(n+":"+version, func(t *testing.T) {
- // Make a versioned event.
- event := tc.event(version)
-
- if tc.set != nil {
- if err := event.SetData(tc.set); err != nil {
- t.Errorf("unexpected error, %v", err)
- }
- }
- got := event.Data
-
- as, _ := types.Allocate(tc.set)
-
- err := event.DataAs(&as)
- validateData(t, tc, got, as, err)
- })
- }
- }
-}
-
-type XmlExample struct {
- AnInt int `xml:"a,omitempty"`
- AString string `xml:"b,omitempty"`
- AnArray []string `xml:"c,omitempty"`
-}
-
-func TestEventSetData_xml(t *testing.T) {
- // All version should be the same, so run through them all.
-
- versions := []string{ce.CloudEventsVersionV01, ce.CloudEventsVersionV02, ce.CloudEventsVersionV03}
-
- testCases := map[string]DataTest{
- "empty": {
- event: func(version string) ce.Event {
- e := ce.New(version)
- e.SetDataContentType("application/xml")
- return e
- },
- want: nil,
- },
- "text/xml": {
- event: func(version string) ce.Event {
- e := ce.New(version)
- e.SetDataContentType("text/xml")
- return e
- },
- set: &XmlExample{
- AnInt: 42,
- AString: "true fact",
- AnArray: versions,
- },
- want: []byte(`42true fact0.10.20.3`),
- },
- "application/xml": {
- event: func(version string) ce.Event {
- e := ce.New(version)
- e.SetDataContentType("application/xml")
- return e
- },
- set: &XmlExample{
- AnInt: 42,
- AString: "true fact",
- AnArray: versions,
- },
- want: []byte(`42true fact0.10.20.3`),
- },
- "application/xml+base64": {
- event: func(version string) ce.Event {
- e := ce.New(version)
- e.SetDataContentType("application/xml")
- e.SetDataContentEncoding(ce.Base64)
- return e
- },
- set: &XmlExample{
- AnInt: 42,
- AString: "true fact",
- AnArray: versions,
- },
- want: `PFhtbEV4YW1wbGU+PGE+NDI8L2E+PGI+dHJ1ZSBmYWN0PC9iPjxjPjAuMTwvYz48Yz4wLjI8L2M+PGM+MC4zPC9jPjwvWG1sRXhhbXBsZT4=`,
- },
- }
- for n, tc := range testCases {
- for _, version := range versions {
- t.Run(n+":"+version, func(t *testing.T) {
- // Make a versioned event.
- event := tc.event(version)
-
- if tc.set != nil {
- if err := event.SetData(tc.set); err != nil {
- t.Errorf("unexpected error, %v", err)
- }
- }
- got := event.Data
-
- as, _ := types.Allocate(tc.set)
-
- err := event.DataAs(&as)
- validateData(t, tc, got, as, err)
- })
- }
- }
-}
-
-func validateData(t *testing.T, tc DataTest, got, as interface{}, err error) {
- var gotErr string
- if err != nil {
- gotErr = err.Error()
- if tc.wantErr == "" {
- t.Errorf("unexpected no error, got %q", gotErr)
- }
- }
- if tc.wantErr != "" {
- if !strings.Contains(gotErr, tc.wantErr) {
- t.Errorf("unexpected error, expected to contain %q, got: %q ", tc.wantErr, gotErr)
- }
- }
- if diff := cmp.Diff(tc.want, got); diff != "" {
- t.Errorf("unexpected data (-want, +got) = %v", diff)
- }
- if diff := cmp.Diff(tc.set, as); diff != "" {
- t.Errorf("unexpected as (-want, +got) = %v", diff)
- }
-}
diff --git a/v1/cloudevents/event_interface.go b/v1/cloudevents/event_interface.go
deleted file mode 100644
index 37bb82ab5..000000000
--- a/v1/cloudevents/event_interface.go
+++ /dev/null
@@ -1,79 +0,0 @@
-package cloudevents
-
-import (
- "time"
-)
-
-// EventWriter is the interface for reading through an event from attributes.
-type EventReader interface {
- // SpecVersion returns event.Context.GetSpecVersion().
- SpecVersion() string
- // Type returns event.Context.GetType().
- Type() string
- // Source returns event.Context.GetSource().
- Source() string
- // Subject returns event.Context.GetSubject().
- Subject() string
- // ID returns event.Context.GetID().
- ID() string
- // Time returns event.Context.GetTime().
- Time() time.Time
- // DataSchema returns event.Context.GetDataSchema().
- DataSchema() string
- // DataContentType returns event.Context.GetDataContentType().
- DataContentType() string
- // DataMediaType returns event.Context.GetDataMediaType().
- DataMediaType() string
- // DeprecatedDataContentEncoding returns event.Context.DeprecatedGetDataContentEncoding().
- DeprecatedDataContentEncoding() string
-
- // Extension Attributes
-
- // Extensions returns the event.Context.GetExtensions().
- // Extensions use the CloudEvents type system, details in package cloudevents/types.
- Extensions() map[string]interface{}
-
- // DEPRECATED: see event.Context.ExtensionAs
- // ExtensionAs returns event.Context.ExtensionAs(name, obj).
- ExtensionAs(string, interface{}) error
-
- // Data Attribute
-
- // DataAs attempts to populate the provided data object with the event payload.
- // data should be a pointer type.
- DataAs(interface{}) error
-}
-
-// EventWriter is the interface for writing through an event onto attributes.
-// If an error is thrown by a sub-component, EventWriter caches the error
-// internally and exposes errors with a call to event.Validate().
-type EventWriter interface {
- // Context Attributes
-
- // SetSpecVersion performs event.Context.SetSpecVersion.
- SetSpecVersion(string)
- // SetType performs event.Context.SetType.
- SetType(string)
- // SetSource performs event.Context.SetSource.
- SetSource(string)
- // SetSubject( performs event.Context.SetSubject.
- SetSubject(string)
- // SetID performs event.Context.SetID.
- SetID(string)
- // SetTime performs event.Context.SetTime.
- SetTime(time.Time)
- // SetDataSchema performs event.Context.SetDataSchema.
- SetDataSchema(string)
- // SetDataContentType performs event.Context.SetDataContentType.
- SetDataContentType(string)
- // DeprecatedSetDataContentEncoding performs event.Context.DeprecatedSetDataContentEncoding.
- SetDataContentEncoding(string)
-
- // Extension Attributes
-
- // SetExtension performs event.Context.SetExtension.
- SetExtension(string, interface{})
-
- // SetData encodes the given payload with the current encoding settings.
- SetData(interface{}) error
-}
diff --git a/v1/cloudevents/event_marshal.go b/v1/cloudevents/event_marshal.go
deleted file mode 100644
index a1f10e5c4..000000000
--- a/v1/cloudevents/event_marshal.go
+++ /dev/null
@@ -1,394 +0,0 @@
-package cloudevents
-
-import (
- "context"
- "encoding/base64"
- "encoding/json"
- "errors"
- "fmt"
- "strconv"
- "strings"
-
- errors2 "github.com/pkg/errors"
-
- "github.com/cloudevents/sdk-go/v1/cloudevents/observability"
-)
-
-// MarshalJSON implements a custom json marshal method used when this type is
-// marshaled using json.Marshal.
-func (e Event) MarshalJSON() ([]byte, error) {
- _, r := observability.NewReporter(context.Background(), eventJSONObserved{o: reportMarshal, v: e.SpecVersion()})
-
- if err := e.Validate(); err != nil {
- r.Error()
- return nil, err
- }
-
- var b []byte
- var err error
-
- switch e.SpecVersion() {
- case CloudEventsVersionV01, CloudEventsVersionV02, CloudEventsVersionV03:
- b, err = JsonEncodeLegacy(e)
- case CloudEventsVersionV1:
- b, err = JsonEncode(e)
- default:
- return nil, fmt.Errorf("unnknown spec version: %q", e.SpecVersion())
- }
-
- // Report the observable
- if err != nil {
- r.Error()
- return nil, err
- } else {
- r.OK()
- }
-
- return b, nil
-}
-
-// UnmarshalJSON implements the json unmarshal method used when this type is
-// unmarshaled using json.Unmarshal.
-func (e *Event) UnmarshalJSON(b []byte) error {
- raw := make(map[string]json.RawMessage)
- if err := json.Unmarshal(b, &raw); err != nil {
- return err
- }
-
- version := versionFromRawMessage(raw)
-
- _, r := observability.NewReporter(context.Background(), eventJSONObserved{o: reportUnmarshal, v: version})
-
- var err error
- switch version {
- case CloudEventsVersionV01:
- err = e.JsonDecodeV01(b, raw)
- case CloudEventsVersionV02:
- err = e.JsonDecodeV02(b, raw)
- case CloudEventsVersionV03:
- err = e.JsonDecodeV03(b, raw)
- case CloudEventsVersionV1:
- err = e.JsonDecodeV1(b, raw)
- default:
- return fmt.Errorf("unnknown spec version: %q", version)
- }
-
- // Report the observable
- if err != nil {
- r.Error()
- return err
- } else {
- r.OK()
- }
- return nil
-}
-
-func versionFromRawMessage(raw map[string]json.RawMessage) string {
- // v0.1
- if v, ok := raw["cloudEventsVersion"]; ok {
- var version string
- if err := json.Unmarshal(v, &version); err != nil {
- return ""
- }
- return version
- }
-
- // v0.2 and after
- if v, ok := raw["specversion"]; ok {
- var version string
- if err := json.Unmarshal(v, &version); err != nil {
- return ""
- }
- return version
- }
- return ""
-}
-
-// JsonEncode
-func JsonEncode(e Event) ([]byte, error) {
- data, err := e.DataBytes()
- if err != nil {
- return nil, err
- }
- return jsonEncode(e.Context, data, e.DataBinary)
-}
-
-// JsonEncodeLegacy
-func JsonEncodeLegacy(e Event) ([]byte, error) {
- var data []byte
- isBase64 := e.Context.DeprecatedGetDataContentEncoding() == Base64
- var err error
- data, err = e.DataBytes()
- if err != nil {
- return nil, err
- }
- return jsonEncode(e.Context, data, isBase64)
-}
-
-func jsonEncode(ctx EventContextReader, data []byte, isBase64 bool) ([]byte, error) {
- var b map[string]json.RawMessage
- var err error
-
- if ctx.GetSpecVersion() == CloudEventsVersionV01 {
- b, err = marshalEventLegacy(ctx)
- } else {
- b, err = marshalEvent(ctx, ctx.GetExtensions())
- }
- if err != nil {
- return nil, err
- }
-
- if data != nil {
- // data is passed in as an encoded []byte. That slice might be any
- // number of things but for json encoding of the envelope all we care
- // is if the payload is either a string or a json object. If it is a
- // json object, it can be inserted into the body without modification.
- // Otherwise we need to quote it if not already quoted.
- mediaType, err := ctx.GetDataMediaType()
- if err != nil {
- return nil, err
- }
- isJson := mediaType == "" || mediaType == ApplicationJSON || mediaType == TextJSON
- // TODO(#60): we do not support json values at the moment, only objects and lists.
- if isJson && !isBase64 {
- b["data"] = data
- } else {
- var dataKey string
- if ctx.GetSpecVersion() == CloudEventsVersionV1 {
- dataKey = "data_base64"
- buf := make([]byte, base64.StdEncoding.EncodedLen(len(data)))
- base64.StdEncoding.Encode(buf, data)
- data = buf
- } else {
- dataKey = "data"
- }
- if data[0] != byte('"') {
- b[dataKey] = []byte(strconv.QuoteToASCII(string(data)))
- } else {
- // already quoted
- b[dataKey] = data
- }
- }
- }
-
- body, err := json.Marshal(b)
- if err != nil {
- return nil, err
- }
-
- return body, nil
-}
-
-// JsonDecodeV01 takes in the byte representation of a version 0.1 structured json CloudEvent and returns a
-// cloudevent.Event or an error if there are parsing errors.
-func (e *Event) JsonDecodeV01(body []byte, raw map[string]json.RawMessage) error {
- ec := EventContextV01{}
- if err := json.Unmarshal(body, &ec); err != nil {
- return err
- }
-
- var data interface{}
- if d, ok := raw["data"]; ok {
- data = []byte(d)
- }
-
- e.Context = &ec
- e.Data = data
- e.DataEncoded = data != nil
-
- return nil
-}
-
-// JsonDecodeV02 takes in the byte representation of a version 0.2 structured json CloudEvent and returns a
-// cloudevent.Event or an error if there are parsing errors.
-func (e *Event) JsonDecodeV02(body []byte, raw map[string]json.RawMessage) error {
- ec := EventContextV02{}
- if err := json.Unmarshal(body, &ec); err != nil {
- return err
- }
-
- // TODO: could use reflection to get these.
- delete(raw, "specversion")
- delete(raw, "type")
- delete(raw, "source")
- delete(raw, "id")
- delete(raw, "time")
- delete(raw, "schemaurl")
- delete(raw, "contenttype")
-
- var data interface{}
- if d, ok := raw["data"]; ok {
- data = []byte(d)
- }
- delete(raw, "data")
-
- if len(raw) > 0 {
- extensions := make(map[string]interface{}, len(raw))
- ec.Extensions = extensions
- for k, v := range raw {
- k = strings.ToLower(k)
- var tmp interface{}
- if err := json.Unmarshal(v, &tmp); err != nil {
- return err
- }
- if err := ec.SetExtension(k, tmp); err != nil {
- return errors2.Wrap(err, "Cannot set extension with key "+k)
- }
- }
- }
-
- e.Context = &ec
- e.Data = data
- e.DataEncoded = data != nil
-
- return nil
-}
-
-// JsonDecodeV03 takes in the byte representation of a version 0.3 structured json CloudEvent and returns a
-// cloudevent.Event or an error if there are parsing errors.
-func (e *Event) JsonDecodeV03(body []byte, raw map[string]json.RawMessage) error {
- ec := EventContextV03{}
- if err := json.Unmarshal(body, &ec); err != nil {
- return err
- }
-
- // TODO: could use reflection to get these.
- delete(raw, "specversion")
- delete(raw, "type")
- delete(raw, "source")
- delete(raw, "subject")
- delete(raw, "id")
- delete(raw, "time")
- delete(raw, "schemaurl")
- delete(raw, "datacontenttype")
- delete(raw, "datacontentencoding")
-
- var data interface{}
- if d, ok := raw["data"]; ok {
- data = []byte(d)
- }
- delete(raw, "data")
-
- if len(raw) > 0 {
- extensions := make(map[string]interface{}, len(raw))
- ec.Extensions = extensions
- for k, v := range raw {
- k = strings.ToLower(k)
- var tmp interface{}
- if err := json.Unmarshal(v, &tmp); err != nil {
- return err
- }
- if err := ec.SetExtension(k, tmp); err != nil {
- return errors2.Wrap(err, "Cannot set extension with key "+k)
- }
- }
- }
-
- e.Context = &ec
- e.Data = data
- e.DataEncoded = data != nil
-
- return nil
-}
-
-// JsonDecodeV1 takes in the byte representation of a version 1.0 structured json CloudEvent and returns a
-// cloudevent.Event or an error if there are parsing errors.
-func (e *Event) JsonDecodeV1(body []byte, raw map[string]json.RawMessage) error {
- ec := EventContextV1{}
- if err := json.Unmarshal(body, &ec); err != nil {
- return err
- }
-
- delete(raw, "specversion")
- delete(raw, "type")
- delete(raw, "source")
- delete(raw, "subject")
- delete(raw, "id")
- delete(raw, "time")
- delete(raw, "dataschema")
- delete(raw, "datacontenttype")
-
- var data interface{}
- if d, ok := raw["data"]; ok {
- data = []byte(d)
- }
- delete(raw, "data")
-
- var dataBase64 []byte
- if d, ok := raw["data_base64"]; ok {
- var tmp []byte
- if err := json.Unmarshal(d, &tmp); err != nil {
- return err
- }
- dataBase64 = tmp
- }
- delete(raw, "data_base64")
-
- if len(raw) > 0 {
- extensions := make(map[string]interface{}, len(raw))
- ec.Extensions = extensions
- for k, v := range raw {
- k = strings.ToLower(k)
- var tmp interface{}
- if err := json.Unmarshal(v, &tmp); err != nil {
- return err
- }
- if err := ec.SetExtension(k, tmp); err != nil {
- return errors2.Wrap(err, "Cannot set extension with key "+k)
- }
- }
- }
-
- e.Context = &ec
- if data != nil && dataBase64 != nil {
- return errors.New("parsing error: JSON decoder found both 'data', and 'data_base64' in JSON payload")
- }
- if data != nil {
- e.Data = data
- } else if dataBase64 != nil {
- e.Data = dataBase64
- }
- e.DataEncoded = data != nil
-
- return nil
-}
-
-func marshalEventLegacy(event interface{}) (map[string]json.RawMessage, error) {
- b, err := json.Marshal(event)
- if err != nil {
- return nil, err
- }
-
- brm := map[string]json.RawMessage{}
- if err := json.Unmarshal(b, &brm); err != nil {
- return nil, err
- }
-
- return brm, nil
-}
-
-func marshalEvent(event interface{}, extensions map[string]interface{}) (map[string]json.RawMessage, error) {
- b, err := json.Marshal(event)
- if err != nil {
- return nil, err
- }
-
- brm := map[string]json.RawMessage{}
- if err := json.Unmarshal(b, &brm); err != nil {
- return nil, err
- }
-
- for k, v := range extensions {
- k = strings.ToLower(k)
- vb, err := json.Marshal(v)
- if err != nil {
- return nil, err
- }
- // Don't overwrite spec keys.
- if _, ok := brm[k]; !ok {
- brm[k] = vb
- }
- }
-
- return brm, nil
-}
diff --git a/v1/cloudevents/event_marshal_test.go b/v1/cloudevents/event_marshal_test.go
deleted file mode 100644
index 26288408c..000000000
--- a/v1/cloudevents/event_marshal_test.go
+++ /dev/null
@@ -1,812 +0,0 @@
-package cloudevents_test
-
-import (
- "encoding/json"
- "fmt"
- "net/url"
- "testing"
- "time"
-
- "github.com/google/go-cmp/cmp"
-
- "github.com/cloudevents/sdk-go/v1/cloudevents"
- "github.com/cloudevents/sdk-go/v1/cloudevents/types"
-)
-
-//type DataExample struct {
-// AnInt int `json:"a,omitempty"`
-// AString string `json:"b,omitempty"`
-// AnArray []string `json:"c,omitempty"`
-// ATime *time.Time `json:"e,omitempty"`
-//}
-
-func TestMarshal(t *testing.T) {
- now := types.Timestamp{Time: time.Now().UTC()}
- sourceUrl, _ := url.Parse("http://example.com/source")
- source := &types.URLRef{URL: *sourceUrl}
- sourceV1 := &types.URIRef{URL: *sourceUrl}
-
- schemaUrl, _ := url.Parse("http://example.com/schema")
- schema := &types.URLRef{URL: *schemaUrl}
- schemaV1 := &types.URI{URL: *schemaUrl}
-
- testCases := map[string]struct {
- event cloudevents.Event
- eventExtensions map[string]interface{}
- want []byte
- wantErr *string
- }{
- "empty struct": {
- event: cloudevents.Event{},
- wantErr: strptr("json: error calling MarshalJSON for type cloudevents.Event: every event conforming to the CloudEvents specification MUST include a context"),
- },
- "struct data v0.1": {
- event: cloudevents.Event{
- Context: cloudevents.EventContextV01{
- EventType: "com.example.test",
- Source: *source,
- SchemaURL: schema,
- EventTypeVersion: strptr("version1"),
- EventID: "ABC-123",
- EventTime: &now,
- ContentType: cloudevents.StringOfApplicationJSON(),
- }.AsV01(),
- Data: DataExample{
- AnInt: 42,
- AString: "testing",
- },
- },
- eventExtensions: map[string]interface{}{
- "exbool": true,
- "exint": int32(42),
- "exstring": "exstring",
- "exbinary": []byte{0, 1, 2, 3},
- "exurl": source,
- "extime": &now,
- },
- want: toBytes(map[string]interface{}{
- "cloudEventsVersion": "0.1",
- "contentType": "application/json",
- "data": map[string]interface{}{
- "a": 42,
- "b": "testing",
- },
- "eventID": "ABC-123",
- "eventTime": now.Format(time.RFC3339Nano),
- "eventType": "com.example.test",
- "eventTypeVersion": "version1",
- "extensions": map[string]interface{}{
- "exbool": true,
- "exint": 42,
- "exstring": "exstring",
- "exbinary": "AAECAw==",
- "exurl": "http://example.com/source",
- "extime": now.Format(time.RFC3339Nano),
- },
- "schemaURL": "http://example.com/schema",
- "source": "http://example.com/source",
- }),
- },
- "struct data v0.2": {
- event: cloudevents.Event{
- Context: cloudevents.EventContextV02{
- Type: "com.example.test",
- Source: *source,
- SchemaURL: schema,
- ID: "ABC-123",
- Time: &now,
- ContentType: cloudevents.StringOfApplicationJSON(),
- }.AsV02(),
- Data: DataExample{
- AnInt: 42,
- AString: "testing",
- },
- },
- eventExtensions: map[string]interface{}{
- "exbool": true,
- "exint": int32(42),
- "exstring": "exstring",
- "exbinary": []byte{0, 1, 2, 3},
- "exurl": source,
- "extime": &now,
- },
- want: toBytes(map[string]interface{}{
- "specversion": "0.2",
- "contenttype": "application/json",
- "data": map[string]interface{}{
- "a": 42,
- "b": "testing",
- },
- "id": "ABC-123",
- "time": now.Format(time.RFC3339Nano),
- "type": "com.example.test",
- "exbool": true,
- "exint": 42,
- "exstring": "exstring",
- "exbinary": "AAECAw==",
- "exurl": "http://example.com/source",
- "extime": now.Format(time.RFC3339Nano),
- "schemaurl": "http://example.com/schema",
- "source": "http://example.com/source",
- }),
- },
- "v0.2 cased extensions": {
- event: cloudevents.Event{
- Context: cloudevents.EventContextV02{
- Type: "com.example.test",
- Source: *source,
- SchemaURL: schema,
- ID: "ABC-123",
- Time: &now,
- ContentType: cloudevents.StringOfApplicationJSON(),
- }.AsV02(),
- Data: DataExample{
- AnInt: 42,
- AString: "testing",
- },
- },
- eventExtensions: map[string]interface{}{
- "exBool": true,
- "Exint": int32(42),
- "EXSTRING": "exstring",
- "exbinary": []byte{0, 1, 2, 3},
- "exurl": source,
- "extime": &now,
- },
- want: toBytes(map[string]interface{}{
- "specversion": "0.2",
- "contenttype": "application/json",
- "data": map[string]interface{}{
- "a": 42,
- "b": "testing",
- },
- "id": "ABC-123",
- "time": now.Format(time.RFC3339Nano),
- "type": "com.example.test",
- "exbool": true,
- "exint": 42,
- "exstring": "exstring",
- "exbinary": "AAECAw==",
- "exurl": "http://example.com/source",
- "extime": now.Format(time.RFC3339Nano),
- "schemaurl": "http://example.com/schema",
- "source": "http://example.com/source",
- }),
- },
- "struct data v0.3": {
- event: cloudevents.Event{
- Context: cloudevents.EventContextV03{
- Type: "com.example.test",
- Source: *source,
- SchemaURL: schema,
- ID: "ABC-123",
- Time: &now,
- DataContentType: cloudevents.StringOfApplicationJSON(),
- }.AsV03(),
- Data: DataExample{
- AnInt: 42,
- AString: "testing",
- },
- },
- eventExtensions: map[string]interface{}{
- "exbool": true,
- "exint": int32(42),
- "exstring": "exstring",
- "exbinary": []byte{0, 1, 2, 3},
- "exurl": source,
- "extime": &now,
- },
- want: toBytes(map[string]interface{}{
- "specversion": "0.3",
- "datacontenttype": "application/json",
- "data": map[string]interface{}{
- "a": 42,
- "b": "testing",
- },
- "id": "ABC-123",
- "time": now.Format(time.RFC3339Nano),
- "type": "com.example.test",
- "exbool": true,
- "exint": 42,
- "exstring": "exstring",
- "exbinary": "AAECAw==",
- "exurl": "http://example.com/source",
- "extime": now.Format(time.RFC3339Nano),
- "schemaurl": "http://example.com/schema",
- "source": "http://example.com/source",
- }),
- },
- "nil data v0.3": {
- event: cloudevents.Event{
- Context: cloudevents.EventContextV03{
- Type: "com.example.test",
- Source: *source,
- SchemaURL: schema,
- ID: "ABC-123",
- Time: &now,
- DataContentType: cloudevents.StringOfApplicationJSON(),
- }.AsV03(),
- },
- eventExtensions: map[string]interface{}{
- "exbool": true,
- "exint": int32(42),
- "exstring": "exstring",
- "exbinary": []byte{0, 1, 2, 3},
- "exurl": source,
- "extime": &now,
- },
- want: toBytes(map[string]interface{}{
- "specversion": "0.3",
- "datacontenttype": "application/json",
- "id": "ABC-123",
- "time": now.Format(time.RFC3339Nano),
- "type": "com.example.test",
- "exbool": true,
- "exint": 42,
- "exstring": "exstring",
- "exbinary": "AAECAw==",
- "exurl": "http://example.com/source",
- "extime": now.Format(time.RFC3339Nano),
- "schemaurl": "http://example.com/schema",
- "source": "http://example.com/source",
- }),
- },
- "string data v0.3": {
- event: cloudevents.Event{
- Context: cloudevents.EventContextV03{
- Type: "com.example.test",
- Source: *source,
- SchemaURL: schema,
- ID: "ABC-123",
- Time: &now,
- DataContentType: cloudevents.StringOfApplicationJSON(),
- }.AsV03(),
- Data: "This is a string.",
- },
- eventExtensions: map[string]interface{}{
- "exbool": true,
- "exint": int32(42),
- "exstring": "exstring",
- "exbinary": []byte{0, 1, 2, 3},
- "exurl": source,
- "extime": &now,
- },
- want: toBytes(map[string]interface{}{
- "specversion": "0.3",
- "datacontenttype": "application/json",
- "data": "This is a string.",
- "id": "ABC-123",
- "time": now.Format(time.RFC3339Nano),
- "type": "com.example.test",
- "exbool": true,
- "exint": 42,
- "exstring": "exstring",
- "exbinary": "AAECAw==",
- "exurl": "http://example.com/source",
- "extime": now.Format(time.RFC3339Nano),
- "schemaurl": "http://example.com/schema",
- "source": "http://example.com/source",
- }),
- },
- "struct data v1.0": {
- event: cloudevents.Event{
- Context: cloudevents.EventContextV1{
- Type: "com.example.test",
- Source: *sourceV1,
- DataSchema: schemaV1,
- ID: "ABC-123",
- Time: &now,
- DataContentType: cloudevents.StringOfApplicationJSON(),
- }.AsV1(),
- Data: DataExample{
- AnInt: 42,
- AString: "testing",
- },
- },
- eventExtensions: map[string]interface{}{
- "exbool": true,
- "exint": int32(42),
- "exstring": "exstring",
- "exbinary": []byte{0, 1, 2, 3},
- "exurl": sourceV1,
- "extime": &now,
- },
- want: toBytes(map[string]interface{}{
- "specversion": "1.0",
- "datacontenttype": "application/json",
- "data": map[string]interface{}{
- "a": 42,
- "b": "testing",
- },
- "id": "ABC-123",
- "time": now.Format(time.RFC3339Nano),
- "type": "com.example.test",
- "exbool": true,
- "exint": 42,
- "exstring": "exstring",
- "exbinary": "AAECAw==",
- "exurl": "http://example.com/source",
- "extime": now.Format(time.RFC3339Nano),
- "dataschema": "http://example.com/schema",
- "source": "http://example.com/source",
- }),
- },
- "nil data v1.0": {
- event: cloudevents.Event{
- Context: cloudevents.EventContextV1{
- Type: "com.example.test",
- Source: *sourceV1,
- DataSchema: schemaV1,
- ID: "ABC-123",
- Time: &now,
- DataContentType: cloudevents.StringOfApplicationJSON(),
- }.AsV1(),
- },
- eventExtensions: map[string]interface{}{
- "exbool": true,
- "exint": int32(42),
- "exstring": "exstring",
- "exbinary": []byte{0, 1, 2, 3},
- "exurl": sourceV1,
- "extime": &now,
- },
- want: toBytes(map[string]interface{}{
- "specversion": "1.0",
- "datacontenttype": "application/json",
- "id": "ABC-123",
- "time": now.Format(time.RFC3339Nano),
- "type": "com.example.test",
- "exbool": true,
- "exint": 42,
- "exstring": "exstring",
- "exbinary": "AAECAw==",
- "exurl": "http://example.com/source",
- "extime": now.Format(time.RFC3339Nano),
- "dataschema": "http://example.com/schema",
- "source": "http://example.com/source",
- }),
- },
- "string data v1.0": {
- event: cloudevents.Event{
- Context: cloudevents.EventContextV1{
- Type: "com.example.test",
- Source: *sourceV1,
- DataSchema: schemaV1,
- ID: "ABC-123",
- Time: &now,
- DataContentType: cloudevents.StringOfApplicationJSON(),
- }.AsV1(),
- Data: "This is a string.",
- },
- eventExtensions: map[string]interface{}{
- "exbool": true,
- "exint": int32(42),
- "exstring": "exstring",
- "exbinary": []byte{0, 1, 2, 3},
- "exurl": sourceV1,
- "extime": &now,
- },
- want: toBytes(map[string]interface{}{
- "specversion": "1.0",
- "datacontenttype": "application/json",
- "data": "This is a string.",
- "id": "ABC-123",
- "time": now.Format(time.RFC3339Nano),
- "type": "com.example.test",
- "exbool": true,
- "exint": 42,
- "exstring": "exstring",
- "exbinary": "AAECAw==",
- "exurl": "http://example.com/source",
- "extime": now.Format(time.RFC3339Nano),
- "dataschema": "http://example.com/schema",
- "source": "http://example.com/source",
- }),
- },
- }
- for n, tc := range testCases {
- t.Run(n, func(t *testing.T) {
-
- event := tc.event
-
- for k, v := range tc.eventExtensions {
- event.SetExtension(k, v)
- }
-
- gotBytes, err := json.Marshal(event)
-
- if tc.wantErr != nil || err != nil {
- if diff := cmp.Diff(*tc.wantErr, err.Error()); diff != "" {
- t.Errorf("unexpected error (-want, +got) = %v", diff)
- }
- return
- }
-
- // so we can understand the diff, turn bytes to strings
- want := string(tc.want)
- got := string(gotBytes)
-
- if diff := cmp.Diff(want, got); diff != "" {
- t.Errorf("unexpected event (-want, +got) = %v", diff)
- }
- })
- }
-}
-
-func TestUnmarshal(t *testing.T) {
- now := types.Timestamp{Time: time.Now().UTC()}
- sourceUrl, _ := url.Parse("http://example.com/source")
- source := &types.URLRef{URL: *sourceUrl}
- sourceV1 := &types.URIRef{URL: *sourceUrl}
-
- schemaUrl, _ := url.Parse("http://example.com/schema")
- schema := &types.URLRef{URL: *schemaUrl}
- schemaV1 := &types.URI{URL: *schemaUrl}
-
- testCases := map[string]struct {
- body []byte
- want *cloudevents.Event
- wantErr error
- }{
- "struct data v0.1": {
- body: toBytes(map[string]interface{}{
- "cloudEventsVersion": "0.1",
- "contentType": "application/json",
- "data": map[string]interface{}{
- "a": 42,
- "b": "testing",
- },
- "eventID": "ABC-123",
- "eventTime": now.Format(time.RFC3339Nano),
- "eventType": "com.example.test",
- "eventTypeVersion": "version1",
- "extensions": map[string]interface{}{
- "exbool": true,
- "exint": 42,
- "exstring": "exstring",
- "exbinary": "AAECAw==",
- "exurl": "http://example.com/source",
- "extime": now.Format(time.RFC3339Nano),
- },
- "schemaURL": "http://example.com/schema",
- "source": "http://example.com/source",
- }),
- want: &cloudevents.Event{
- Context: cloudevents.EventContextV01{
- EventType: "com.example.test",
- Source: *source,
- SchemaURL: schema,
- EventTypeVersion: strptr("version1"),
- EventID: "ABC-123",
- EventTime: &now,
- ContentType: cloudevents.StringOfApplicationJSON(),
- Extensions: map[string]interface{}{
- "exbool": true, // Boolean should be preserved
- "exint": float64(42),
- "exstring": "exstring",
- // Since byte, url and time are encoded as string, the unmarshal should just convert them to string
- "exbinary": "AAECAw==",
- "exurl": "http://example.com/source",
- "extime": now.Format(time.RFC3339Nano),
- },
- }.AsV01(),
- Data: toBytes(DataExample{
- AnInt: 42,
- AString: "testing",
- }),
- DataEncoded: true,
- },
- },
- "struct data v0.2": {
- body: toBytes(map[string]interface{}{
- "specversion": "0.2",
- "contenttype": "application/json",
- "data": map[string]interface{}{
- "a": 42,
- "b": "testing",
- },
- "id": "ABC-123",
- "time": now.Format(time.RFC3339Nano),
- "type": "com.example.test",
- "exbool": true,
- "exint": 42,
- "exstring": "exstring",
- "exbinary": "AAECAw==",
- "exurl": "http://example.com/source",
- "extime": now.Format(time.RFC3339Nano),
- "schemaurl": "http://example.com/schema",
- "source": "http://example.com/source",
- }),
- want: &cloudevents.Event{
- Context: cloudevents.EventContextV02{
- Type: "com.example.test",
- Source: *source,
- SchemaURL: schema,
- ID: "ABC-123",
- Time: &now,
- ContentType: cloudevents.StringOfApplicationJSON(),
- Extensions: map[string]interface{}{
- "exbool": true, // Boolean should be preserved
- "exint": float64(42),
- "exstring": "exstring",
- // Since byte, url and time are encoded as string, the unmarshal should just convert them to string
- "exbinary": "AAECAw==",
- "exurl": "http://example.com/source",
- "extime": now.Format(time.RFC3339Nano),
- },
- }.AsV02(),
- Data: toBytes(DataExample{
- AnInt: 42,
- AString: "testing",
- }),
- DataEncoded: true,
- },
- },
- "struct data v0.3": {
- body: toBytes(map[string]interface{}{
- "specversion": "0.3",
- "datacontenttype": "application/json",
- "data": map[string]interface{}{
- "a": 42,
- "b": "testing",
- },
- "id": "ABC-123",
- "time": now.Format(time.RFC3339Nano),
- "type": "com.example.test",
- "exbool": true,
- "exint": 42,
- "exstring": "exstring",
- "exbinary": "AAECAw==",
- "exurl": "http://example.com/source",
- "extime": now.Format(time.RFC3339Nano),
- "schemaurl": "http://example.com/schema",
- "source": "http://example.com/source",
- }),
- want: &cloudevents.Event{
- Context: cloudevents.EventContextV03{
- Type: "com.example.test",
- Source: *source,
- SchemaURL: schema,
- ID: "ABC-123",
- Time: &now,
- DataContentType: cloudevents.StringOfApplicationJSON(),
- Extensions: map[string]interface{}{
- "exbool": true, // Boolean should be preserved
- "exint": int32(42),
- "exstring": "exstring",
- // Since byte, url and time are encoded as string, the unmarshal should just convert them to string
- "exbinary": "AAECAw==",
- "exurl": "http://example.com/source",
- "extime": now.Format(time.RFC3339Nano),
- },
- }.AsV03(),
- Data: toBytes(DataExample{
- AnInt: 42,
- AString: "testing",
- }),
- DataEncoded: true,
- },
- },
- "string data v0.3": {
- body: toBytes(map[string]interface{}{
- "specversion": "0.3",
- "datacontenttype": "application/json",
- "data": "This is a string.",
- "id": "ABC-123",
- "time": now.Format(time.RFC3339Nano),
- "type": "com.example.test",
- "exbool": true,
- "exint": 42,
- "exstring": "exstring",
- "exbinary": "AAECAw==",
- "exurl": "http://example.com/source",
- "extime": now.Format(time.RFC3339Nano),
- "schemaurl": "http://example.com/schema",
- "source": "http://example.com/source",
- }),
- want: &cloudevents.Event{
- Context: cloudevents.EventContextV03{
- Type: "com.example.test",
- Source: *source,
- SchemaURL: schema,
- ID: "ABC-123",
- Time: &now,
- DataContentType: cloudevents.StringOfApplicationJSON(),
- Extensions: map[string]interface{}{
- "exbool": true, // Boolean should be preserved
- "exint": int32(42),
- "exstring": "exstring",
- // Since byte, url and time are encoded as string, the unmarshal should just convert them to string
- "exbinary": "AAECAw==",
- "exurl": "http://example.com/source",
- "extime": now.Format(time.RFC3339Nano),
- },
- }.AsV03(),
- Data: toBytes("This is a string."),
- DataEncoded: true,
- },
- },
- "nil data v0.3": {
- body: toBytes(map[string]interface{}{
- "specversion": "0.3",
- "datacontenttype": "application/json",
- "id": "ABC-123",
- "time": now.Format(time.RFC3339Nano),
- "type": "com.example.test",
- "exbool": true,
- "exint": 42,
- "exstring": "exstring",
- "exbinary": "AAECAw==",
- "exurl": "http://example.com/source",
- "extime": now.Format(time.RFC3339Nano),
- "schemaurl": "http://example.com/schema",
- "source": "http://example.com/source",
- }),
- want: &cloudevents.Event{
- Context: cloudevents.EventContextV03{
- Type: "com.example.test",
- Source: *source,
- SchemaURL: schema,
- ID: "ABC-123",
- Time: &now,
- DataContentType: cloudevents.StringOfApplicationJSON(),
- Extensions: map[string]interface{}{
- "exbool": true, // Boolean should be preserved
- "exint": int32(42),
- "exstring": "exstring",
- // Since byte, url and time are encoded as string, the unmarshal should just convert them to string
- "exbinary": "AAECAw==",
- "exurl": "http://example.com/source",
- "extime": now.Format(time.RFC3339Nano),
- },
- }.AsV03(),
- },
- },
- "struct data v1.0": {
- body: toBytes(map[string]interface{}{
- "specversion": "1.0",
- "datacontenttype": "application/json",
- "data": map[string]interface{}{
- "a": 42,
- "b": "testing",
- },
- "id": "ABC-123",
- "time": now.Format(time.RFC3339Nano),
- "type": "com.example.test",
- "exbool": true,
- "exint": 42,
- "exstring": "exstring",
- "exbinary": "AAECAw==",
- "exurl": "http://example.com/source",
- "extime": now.Format(time.RFC3339Nano),
- "dataschema": "http://example.com/schema",
- "source": "http://example.com/source",
- }),
- want: &cloudevents.Event{
- Context: cloudevents.EventContextV1{
- Type: "com.example.test",
- Source: *sourceV1,
- DataSchema: schemaV1,
- ID: "ABC-123",
- Time: &now,
- DataContentType: cloudevents.StringOfApplicationJSON(),
- Extensions: map[string]interface{}{
- "exbool": true, // Boolean should be preserved
- "exint": int32(42),
- "exstring": "exstring",
- // Since byte, url and time are encoded as string, the unmarshal should just convert them to string
- "exbinary": "AAECAw==",
- "exurl": "http://example.com/source",
- "extime": now.Format(time.RFC3339Nano),
- },
- }.AsV1(),
- Data: toBytes(DataExample{
- AnInt: 42,
- AString: "testing",
- }),
- DataEncoded: true,
- },
- },
- "string data v1.0": {
- body: toBytes(map[string]interface{}{
- "specversion": "1.0",
- "datacontenttype": "application/json",
- "data": "This is a string.",
- "id": "ABC-123",
- "time": now.Format(time.RFC3339Nano),
- "type": "com.example.test",
- "exbool": true,
- "exint": 42,
- "exstring": "exstring",
- "exbinary": "AAECAw==",
- "exurl": "http://example.com/source",
- "extime": now.Format(time.RFC3339Nano),
- "dataschema": "http://example.com/schema",
- "source": "http://example.com/source",
- }),
- want: &cloudevents.Event{
- Context: cloudevents.EventContextV1{
- Type: "com.example.test",
- Source: *sourceV1,
- DataSchema: schemaV1,
- ID: "ABC-123",
- Time: &now,
- DataContentType: cloudevents.StringOfApplicationJSON(),
- Extensions: map[string]interface{}{
- "exbool": true, // Boolean should be preserved
- "exint": int32(42),
- "exstring": "exstring",
- // Since byte, url and time are encoded as string, the unmarshal should just convert them to string
- "exbinary": "AAECAw==",
- "exurl": "http://example.com/source",
- "extime": now.Format(time.RFC3339Nano),
- },
- }.AsV1(),
- Data: toBytes("This is a string."),
- DataEncoded: true,
- },
- },
- "nil data v1.0": {
- body: toBytes(map[string]interface{}{
- "specversion": "1.0",
- "datacontenttype": "application/json",
- "id": "ABC-123",
- "time": now.Format(time.RFC3339Nano),
- "type": "com.example.test",
- "exbool": true,
- "exint": 42,
- "exstring": "exstring",
- "exbinary": "AAECAw==",
- "exurl": "http://example.com/source",
- "extime": now.Format(time.RFC3339Nano),
- "dataschema": "http://example.com/schema",
- "source": "http://example.com/source",
- }),
- want: &cloudevents.Event{
- Context: cloudevents.EventContextV1{
- Type: "com.example.test",
- Source: *sourceV1,
- DataSchema: schemaV1,
- ID: "ABC-123",
- Time: &now,
- DataContentType: cloudevents.StringOfApplicationJSON(),
- Extensions: map[string]interface{}{
- "exbool": true, // Boolean should be preserved
- "exint": int32(42),
- "exstring": "exstring",
- // Since byte, url and time are encoded as string, the unmarshal should just convert them to string
- "exbinary": "AAECAw==",
- "exurl": "http://example.com/source",
- "extime": now.Format(time.RFC3339Nano),
- },
- }.AsV1(),
- },
- },
- }
- for n, tc := range testCases {
- t.Run(n, func(t *testing.T) {
-
- got := &cloudevents.Event{}
- err := json.Unmarshal(tc.body, got)
-
- if tc.wantErr != nil || err != nil {
- if diff := cmp.Diff(tc.wantErr, err); diff != "" {
- t.Errorf("unexpected error (-want, +got) = %v", diff)
- }
- return
- }
-
- if diff := cmp.Diff(tc.want, got); diff != "" {
- t.Errorf("unexpected event (-want, +got) = %v", diff)
- }
- })
- }
-}
-
-func toBytes(body interface{}) []byte {
- b, err := json.Marshal(body)
- if err != nil {
- return []byte(fmt.Sprintf(`{"error":%q}`, err.Error()))
- }
- return b
-}
diff --git a/v1/cloudevents/event_observability.go b/v1/cloudevents/event_observability.go
deleted file mode 100644
index ef03abfe5..000000000
--- a/v1/cloudevents/event_observability.go
+++ /dev/null
@@ -1,77 +0,0 @@
-package cloudevents
-
-import (
- "fmt"
-
- "github.com/cloudevents/sdk-go/v1/cloudevents/observability"
- "go.opencensus.io/stats"
- "go.opencensus.io/stats/view"
-)
-
-var (
- // EventMarshalLatencyMs measures the latency in milliseconds for the
- // CloudEvents.Event marshal/unmarshalJSON methods.
- EventMarshalLatencyMs = stats.Float64(
- "cloudevents.io/sdk-go/event/json/latency",
- "The latency in milliseconds of (un)marshalJSON methods for CloudEvents.Event.",
- "ms")
-)
-
-var (
- // LatencyView is an OpenCensus view that shows CloudEvents.Event (un)marshalJSON method latency.
- EventMarshalLatencyView = &view.View{
- Name: "event/json/latency",
- Measure: EventMarshalLatencyMs,
- Description: "The distribution of latency inside of (un)marshalJSON methods for CloudEvents.Event.",
- Aggregation: view.Distribution(0, .01, .1, 1, 10, 100, 1000, 10000),
- TagKeys: observability.LatencyTags(),
- }
-)
-
-type observed int32
-
-// Adheres to Observable
-var _ observability.Observable = observed(0)
-
-const (
- reportMarshal observed = iota
- reportUnmarshal
-)
-
-// MethodName implements Observable.MethodName
-func (o observed) MethodName() string {
- switch o {
- case reportMarshal:
- return "marshaljson"
- case reportUnmarshal:
- return "unmarshaljson"
- default:
- return "unknown"
- }
-}
-
-// LatencyMs implements Observable.LatencyMs
-func (o observed) LatencyMs() *stats.Float64Measure {
- return EventMarshalLatencyMs
-}
-
-// eventJSONObserved is a wrapper to append version to observed.
-type eventJSONObserved struct {
- // Method
- o observed
- // Version
- v string
-}
-
-// Adheres to Observable
-var _ observability.Observable = (*eventJSONObserved)(nil)
-
-// MethodName implements Observable.MethodName
-func (c eventJSONObserved) MethodName() string {
- return fmt.Sprintf("%s/%s", c.o.MethodName(), c.v)
-}
-
-// LatencyMs implements Observable.LatencyMs
-func (c eventJSONObserved) LatencyMs() *stats.Float64Measure {
- return c.o.LatencyMs()
-}
diff --git a/v1/cloudevents/event_reader.go b/v1/cloudevents/event_reader.go
deleted file mode 100644
index fe49e8424..000000000
--- a/v1/cloudevents/event_reader.go
+++ /dev/null
@@ -1,98 +0,0 @@
-package cloudevents
-
-import (
- "time"
-)
-
-var _ EventReader = (*Event)(nil)
-
-// SpecVersion implements EventReader.SpecVersion
-func (e Event) SpecVersion() string {
- if e.Context != nil {
- return e.Context.GetSpecVersion()
- }
- return ""
-}
-
-// Type implements EventReader.Type
-func (e Event) Type() string {
- if e.Context != nil {
- return e.Context.GetType()
- }
- return ""
-}
-
-// Source implements EventReader.Source
-func (e Event) Source() string {
- if e.Context != nil {
- return e.Context.GetSource()
- }
- return ""
-}
-
-// Subject implements EventReader.Subject
-func (e Event) Subject() string {
- if e.Context != nil {
- return e.Context.GetSubject()
- }
- return ""
-}
-
-// ID implements EventReader.ID
-func (e Event) ID() string {
- if e.Context != nil {
- return e.Context.GetID()
- }
- return ""
-}
-
-// Time implements EventReader.Time
-func (e Event) Time() time.Time {
- if e.Context != nil {
- return e.Context.GetTime()
- }
- return time.Time{}
-}
-
-// DataSchema implements EventReader.DataSchema
-func (e Event) DataSchema() string {
- if e.Context != nil {
- return e.Context.GetDataSchema()
- }
- return ""
-}
-
-// DataContentType implements EventReader.DataContentType
-func (e Event) DataContentType() string {
- if e.Context != nil {
- return e.Context.GetDataContentType()
- }
- return ""
-}
-
-// DataMediaType returns the parsed DataMediaType of the event. If parsing
-// fails, the empty string is returned. To retrieve the parsing error, use
-// `Context.GetDataMediaType` instead.
-func (e Event) DataMediaType() string {
- if e.Context != nil {
- mediaType, _ := e.Context.GetDataMediaType()
- return mediaType
- }
- return ""
-}
-
-// DeprecatedDataContentEncoding implements EventReader.DeprecatedDataContentEncoding
-func (e Event) DeprecatedDataContentEncoding() string {
- if e.Context != nil {
- return e.Context.DeprecatedGetDataContentEncoding()
- }
- return ""
-}
-
-// Extensions implements EventReader.Extensions
-func (e Event) Extensions() map[string]interface{} {
- if e.Context != nil {
- return e.Context.GetExtensions()
- }
- return map[string]interface{}(nil)
-}
diff --git a/v1/cloudevents/event_reader_writer_test.go b/v1/cloudevents/event_reader_writer_test.go
deleted file mode 100644
index 1fa421b8a..000000000
--- a/v1/cloudevents/event_reader_writer_test.go
+++ /dev/null
@@ -1,681 +0,0 @@
-package cloudevents_test
-
-import (
- "strings"
- "testing"
- "time"
-
- ce "github.com/cloudevents/sdk-go/v1/cloudevents"
- "github.com/google/go-cmp/cmp"
-)
-
-type ReadWriteTest struct {
- event ce.Event
- set string
- want interface{}
- corrected interface{} // used in corrected tests.
- wantErr string
-}
-
-func TestEventRW_SpecVersion(t *testing.T) {
- testCases := map[string]ReadWriteTest{
- "empty v01": {
- event: ce.New(),
- want: "1.0",
- set: "0.1",
- wantErr: "invalid version",
- },
- "empty v02": {
- event: ce.New(),
- want: "1.0",
- set: "0.2",
- wantErr: "invalid version",
- },
- "empty v03": {
- event: ce.New(),
- want: "1.0",
- set: "0.3",
- wantErr: "invalid version",
- },
- "empty v1": {
- event: ce.New(),
- set: "1.0",
- want: "1.0",
- },
- "v01": {
- event: ce.New("0.1"),
- set: "0.1",
- want: "0.1",
- },
- "v02": {
- event: ce.New("0.2"),
- set: "0.2",
- want: "0.2",
- },
- "v03": {
- event: ce.New("0.3"),
- set: "0.3",
- want: "0.3",
- },
- "v1": {
- event: ce.New("1.0"),
- set: "1.0",
- want: "1.0",
- },
- "invalid v01": {
- event: ce.New("0.1"),
- want: "0.1",
- set: "1.1",
- wantErr: "invalid version",
- },
- "invalid v02": {
- event: ce.New("0.2"),
- want: "0.2",
- set: "1.2",
- wantErr: "invalid version",
- },
- "invalid v03": {
- event: ce.New("0.3"),
- want: "0.3",
- set: "1.3",
- wantErr: "invalid version",
- },
- "invalid v1": {
- event: ce.New("1.0"),
- want: "1.0",
- set: "1.3",
- wantErr: "invalid version",
- },
- }
- for n, tc := range testCases {
- t.Run(n, func(t *testing.T) {
- var got interface{}
-
- tc.event.SetSpecVersion(tc.set)
- got = tc.event.SpecVersion()
-
- err := tc.event.Validate()
- validateReaderWriter(t, tc, got, err)
- })
- }
-}
-
-func TestEventRW_Type(t *testing.T) {
- testCases := map[string]ReadWriteTest{
- "v01": {
- event: ce.New("0.1"),
- set: "type.0.1",
- want: "type.0.1",
- },
- "v02": {
- event: ce.New("0.2"),
- set: "type.0.2",
- want: "type.0.2",
- },
- "v03": {
- event: ce.New("0.3"),
- set: "type.0.3",
- want: "type.0.3",
- },
- "spaced v01": {
- event: ce.New("0.1"),
- set: " type.0.1 ",
- want: "type.0.1",
- },
- "spaced v02": {
- event: ce.New("0.2"),
- set: " type.0.2 ",
- want: "type.0.2",
- },
- "spaced v03": {
- event: ce.New("0.3"),
- set: " type.0.3 ",
- want: "type.0.3",
- },
- }
- for n, tc := range testCases {
- t.Run(n, func(t *testing.T) {
- var got interface{}
-
- tc.event.SetType(tc.set)
- got = tc.event.Type()
-
- err := tc.event.Validate()
- validateReaderWriter(t, tc, got, err)
- })
- }
-}
-
-func TestEventRW_ID(t *testing.T) {
- testCases := map[string]ReadWriteTest{
- "v01": {
- event: ce.New("0.1"),
- set: "id.0.1",
- want: "id.0.1",
- },
- "v02": {
- event: ce.New("0.2"),
- set: "id.0.2",
- want: "id.0.2",
- },
- "v03": {
- event: ce.New("0.3"),
- set: "id.0.3",
- want: "id.0.3",
- },
- "spaced v01": {
- event: ce.New("0.1"),
- set: " id.0.1 ",
- want: "id.0.1",
- },
- "spaced v02": {
- event: ce.New("0.2"),
- set: " id.0.2 ",
- want: "id.0.2",
- },
- "spaced v03": {
- event: ce.New("0.3"),
- set: " id.0.3 ",
- want: "id.0.3",
- },
- }
- for n, tc := range testCases {
- t.Run(n, func(t *testing.T) {
- var got interface{}
-
- tc.event.SetID(tc.set)
- got = tc.event.ID()
-
- err := tc.event.Validate()
- validateReaderWriter(t, tc, got, err)
- })
- }
-}
-
-func TestEventRW_Source(t *testing.T) {
- testCases := map[string]ReadWriteTest{
- "v01": {
- event: ce.New("0.1"),
- set: "http://example/",
- want: "http://example/",
- },
- "v02": {
- event: ce.New("0.2"),
- set: "http://example/",
- want: "http://example/",
- },
- "v03": {
- event: ce.New("0.3"),
- set: "http://example/",
- want: "http://example/",
- },
- "invalid v01": {
- event: ce.New("0.1"),
- set: "%",
- want: "",
- wantErr: "invalid URL escape",
- },
- "invalid v02": {
- event: ce.New("0.2"),
- set: "%",
- want: "",
- wantErr: "invalid URL escape",
- },
- "invalid v03": {
- event: ce.New("0.3"),
- set: "%",
- want: "",
- wantErr: "invalid URL escape",
- },
- }
- for n, tc := range testCases {
- t.Run(n, func(t *testing.T) {
- var got interface{}
-
- tc.event.SetSource(tc.set)
- got = tc.event.Source()
-
- err := tc.event.Validate()
- validateReaderWriter(t, tc, got, err)
- })
- }
-}
-
-// Set will be split on pipe, set1|set2
-func TestEventRW_Corrected_Source(t *testing.T) {
- testCases := map[string]ReadWriteTest{
- "corrected v01": {
- event: ce.New("0.1"),
- set: "%|http://good",
- want: "",
- corrected: "http://good",
- wantErr: "invalid URL escape",
- },
- "corrected v02": {
- event: ce.New("0.2"),
- set: "%|http://good",
- want: "",
- corrected: "http://good",
- wantErr: "invalid URL escape",
- },
- "corrected v03": {
- event: ce.New("0.3"),
- set: "%|http://good",
- want: "",
- corrected: "http://good",
- wantErr: "invalid URL escape",
- },
- }
- for n, tc := range testCases {
- t.Run(n, func(t *testing.T) {
- var got interface{}
- var err error
-
- // Split set on pipe.
- set := strings.Split(tc.set, "|")
-
- // Set
-
- tc.event.SetSource(set[0])
- got = tc.event.Source()
- err = tc.event.Validate()
- validateReaderWriter(t, tc, got, err)
-
- // Correct
-
- tc.event.SetSource(set[1])
- got = tc.event.Source()
- err = tc.event.Validate()
- validateReaderWriterCorrected(t, tc, got, err)
- })
- }
-}
-
-func TestEventRW_Subject(t *testing.T) {
- testCases := map[string]ReadWriteTest{
- "v01": {
- event: ce.New("0.1"),
- set: "subject.0.1",
- want: "subject.0.1",
- },
- "v02": {
- event: ce.New("0.2"),
- set: "subject.0.2",
- want: "subject.0.2",
- },
- "v03": {
- event: ce.New("0.3"),
- set: "subject.0.3",
- want: "subject.0.3",
- },
- "spaced v01": {
- event: ce.New("0.1"),
- set: " subject.0.1 ",
- want: "subject.0.1",
- },
- "spaced v02": {
- event: ce.New("0.2"),
- set: " subject.0.2 ",
- want: "subject.0.2",
- },
- "spaced v03": {
- event: ce.New("0.3"),
- set: " subject.0.3 ",
- want: "subject.0.3",
- },
- "nilled v01": {
- event: func() ce.Event {
- e := ce.New("0.1")
- e.SetSource("should nil")
- return e
- }(),
- want: "",
- },
- "nilled v02": {
- event: func() ce.Event {
- e := ce.New("0.2")
- e.SetSource("should nil")
- return e
- }(),
- want: "",
- },
- "nilled v03": {
- event: func() ce.Event {
- e := ce.New("0.3")
- e.SetSource("should nil")
- return e
- }(),
- want: "",
- },
- }
- for n, tc := range testCases {
- t.Run(n, func(t *testing.T) {
- var got interface{}
-
- tc.event.SetSubject(tc.set)
- got = tc.event.Subject()
-
- err := tc.event.Validate()
- validateReaderWriter(t, tc, got, err)
- })
- }
-}
-
-func TestEventRW_Time(t *testing.T) {
- now := time.Now()
-
- testCases := map[string]ReadWriteTest{
- "v01": {
- event: ce.New("0.1"),
- set: "now", // hack
- want: now,
- },
- "v02": {
- event: ce.New("0.2"),
- set: "now", // hack
- want: now,
- },
- "v03": {
- event: ce.New("0.3"),
- set: "now", // hack
- want: now,
- },
- "nilled v01": {
- event: func() ce.Event {
- e := ce.New("0.1")
- e.SetTime(now)
- return e
- }(),
- want: time.Time{},
- },
- "nilled v02": {
- event: func() ce.Event {
- e := ce.New("0.2")
- e.SetTime(now)
- return e
- }(),
- want: time.Time{},
- },
- "nilled v03": {
- event: func() ce.Event {
- e := ce.New("0.3")
- e.SetTime(now)
- return e
- }(),
- want: time.Time{},
- },
- }
- for n, tc := range testCases {
- t.Run(n, func(t *testing.T) {
- var got interface{}
-
- if tc.set == "now" {
- tc.event.SetTime(now) // pull now from outer test.
- } else {
- tc.event.SetTime(time.Time{}) // pull now from outer test.
- }
- got = tc.event.Time()
-
- err := tc.event.Validate()
- validateReaderWriter(t, tc, got, err)
- })
- }
-}
-
-func TestEventRW_SchemaURL(t *testing.T) {
- testCases := map[string]ReadWriteTest{
- "v01": {
- event: ce.New("0.1"),
- set: "http://example/",
- want: "http://example/",
- },
- "v02": {
- event: ce.New("0.2"),
- set: "http://example/",
- want: "http://example/",
- },
- "v03": {
- event: ce.New("0.3"),
- set: "http://example/",
- want: "http://example/",
- },
- "invalid v01": {
- event: ce.New("0.1"),
- set: "%",
- want: "",
- wantErr: "invalid URL escape",
- },
- "invalid v02": {
- event: ce.New("0.2"),
- set: "%",
- want: "",
- wantErr: "invalid URL escape",
- },
- "invalid v03": {
- event: ce.New("0.3"),
- set: "%",
- want: "",
- wantErr: "invalid URL escape",
- },
- "nilled v01": {
- event: func() ce.Event {
- e := ce.New("0.1")
- e.SetDataSchema("should nil")
- return e
- }(),
- want: "",
- },
- "nilled v02": {
- event: func() ce.Event {
- e := ce.New("0.2")
- e.SetDataSchema("should nil")
- return e
- }(),
- want: "",
- },
- "nilled v03": {
- event: func() ce.Event {
- e := ce.New("0.3")
- e.SetDataSchema("should nil")
- return e
- }(),
- want: "",
- },
- }
- for n, tc := range testCases {
- t.Run(n, func(t *testing.T) {
- var got interface{}
-
- tc.event.SetDataSchema(tc.set)
- got = tc.event.DataSchema()
-
- err := tc.event.Validate()
- validateReaderWriter(t, tc, got, err)
- })
- }
-}
-
-func TestEventRW_DataContentType(t *testing.T) {
- testCases := map[string]ReadWriteTest{
- "v01": {
- event: ce.New("0.1"),
- set: "application/json",
- want: "application/json",
- },
- "v02": {
- event: ce.New("0.2"),
- set: "application/json",
- want: "application/json",
- },
- "v03": {
- event: ce.New("0.3"),
- set: "application/json",
- want: "application/json",
- },
- "spaced v01": {
- event: ce.New("0.1"),
- set: " application/json ",
- want: "application/json",
- },
- "spaced v02": {
- event: ce.New("0.2"),
- set: " application/json ",
- want: "application/json",
- },
- "spaced v03": {
- event: ce.New("0.3"),
- set: " application/json ",
- want: "application/json",
- },
- "nilled v01": {
- event: func() ce.Event {
- e := ce.New("0.1")
- e.SetDataContentType("application/json")
- return e
- }(),
- want: "",
- },
- "nilled v02": {
- event: func() ce.Event {
- e := ce.New("0.2")
- e.SetDataContentType("application/json")
- return e
- }(),
- want: "",
- },
- "nilled v03": {
- event: func() ce.Event {
- e := ce.New("0.3")
- e.SetDataContentType("application/json")
- return e
- }(),
- want: "",
- },
- }
- for n, tc := range testCases {
- t.Run(n, func(t *testing.T) {
- var got interface{}
-
- tc.event.SetDataContentType(tc.set)
- got = tc.event.DataContentType()
-
- err := tc.event.Validate()
- validateReaderWriter(t, tc, got, err)
- })
- }
-}
-
-func TestEventRW_DataContentEncoding(t *testing.T) {
- testCases := map[string]ReadWriteTest{
- "v01": {
- event: ce.New("0.1"),
- set: "base64",
- want: "base64",
- },
- "v02": {
- event: ce.New("0.2"),
- set: "base64",
- want: "base64",
- },
- "v03": {
- event: ce.New("0.3"),
- set: "base64",
- want: "base64",
- },
- "spaced v01": {
- event: ce.New("0.1"),
- set: " base64 ",
- want: "base64",
- },
- "spaced v02": {
- event: ce.New("0.2"),
- set: " base64 ",
- want: "base64",
- },
- "spaced v03": {
- event: ce.New("0.3"),
- set: " base64 ",
- want: "base64",
- },
- "cased v01": {
- event: ce.New("0.1"),
- set: " BaSe64 ",
- want: "base64",
- },
- "cased v02": {
- event: ce.New("0.2"),
- set: " BaSe64 ",
- want: "base64",
- },
- "cased v03": {
- event: ce.New("0.3"),
- set: " BaSe64 ",
- want: "base64",
- },
- "nilled v01": {
- event: func() ce.Event {
- e := ce.New("0.1")
- e.SetDataContentEncoding("base64")
- return e
- }(),
- want: "",
- },
- "nilled v02": {
- event: func() ce.Event {
- e := ce.New("0.2")
- e.SetDataContentEncoding("base64")
- return e
- }(),
- want: "",
- },
- "nilled v03": {
- event: func() ce.Event {
- e := ce.New("0.3")
- e.SetDataContentEncoding("base64")
- return e
- }(),
- want: "",
- },
- }
- for n, tc := range testCases {
- t.Run(n, func(t *testing.T) {
- var got interface{}
-
- tc.event.SetDataContentEncoding(tc.set)
- got = tc.event.DeprecatedDataContentEncoding()
-
- err := tc.event.Validate()
- validateReaderWriter(t, tc, got, err)
- })
- }
-}
-
-func validateReaderWriter(t *testing.T, tc ReadWriteTest, got interface{}, err error) {
- var gotErr string
- if err != nil {
- gotErr = err.Error()
- }
- if tc.wantErr != "" {
- if !strings.Contains(gotErr, tc.wantErr) {
- t.Errorf("unexpected error, expected to contain %q, got: %q ", tc.wantErr, gotErr)
- }
- }
- if diff := cmp.Diff(tc.want, got); diff != "" {
- t.Errorf("unexpected (-want, +got) = %v", diff)
- }
-}
-
-func validateReaderWriterCorrected(t *testing.T, tc ReadWriteTest, got interface{}, err error) {
- var gotErr string
- if err != nil {
- gotErr = err.Error()
- }
- if tc.wantErr != "" {
- if strings.Contains(gotErr, tc.wantErr) {
- t.Errorf("unexpected error, expected to NOT contain %q, got: %q ", tc.wantErr, gotErr)
- }
- }
- if diff := cmp.Diff(tc.corrected, got); diff != "" {
- t.Errorf("unexpected (-want, +got) = %v", diff)
- }
-}
diff --git a/v1/cloudevents/event_response.go b/v1/cloudevents/event_response.go
deleted file mode 100644
index 0e5f7ce75..000000000
--- a/v1/cloudevents/event_response.go
+++ /dev/null
@@ -1,37 +0,0 @@
-package cloudevents
-
-// EventResponse represents the canonical representation of a Response to a
-// CloudEvent from a receiver. Response implementation is Transport dependent.
-type EventResponse struct {
- Status int
- Event *Event
- Reason string
- // Context is transport specific struct to allow for controlling transport
- // response details.
- // For example, see http.TransportResponseContext.
- Context interface{}
-}
-
-// RespondWith sets up the instance of EventResponse to be set with status and
-// an event. Response implementation is Transport dependent.
-func (e *EventResponse) RespondWith(status int, event *Event) {
- if e == nil {
- // if nil, response not supported
- return
- }
- e.Status = status
- if event != nil {
- e.Event = event
- }
-}
-
-// Error sets the instance of EventResponse to be set with an error code and
-// reason string. Response implementation is Transport dependent.
-func (e *EventResponse) Error(status int, reason string) {
- if e == nil {
- // if nil, response not supported
- return
- }
- e.Status = status
- e.Reason = reason
-}
diff --git a/v1/cloudevents/event_response_test.go b/v1/cloudevents/event_response_test.go
deleted file mode 100644
index 4ea040db4..000000000
--- a/v1/cloudevents/event_response_test.go
+++ /dev/null
@@ -1,71 +0,0 @@
-package cloudevents
-
-import (
- "testing"
-
- "github.com/google/go-cmp/cmp"
-)
-
-func TestEventResponse_RespondWith(t *testing.T) {
- testCases := map[string]struct {
- t *EventResponse
- e *Event
- status int
- want *EventResponse
- }{
- "nil": {},
- "valid": {
- t: &EventResponse{},
- e: &Event{Data: "unit test"},
- status: 200,
- want: &EventResponse{
- Status: 200,
- Event: &Event{Data: "unit test"},
- },
- },
- }
- for n, tc := range testCases {
- t.Run(n, func(t *testing.T) {
-
- tc.t.RespondWith(tc.status, tc.e)
-
- got := tc.t
-
- if diff := cmp.Diff(tc.want, got); diff != "" {
- t.Errorf("unexpected (-want, +got) = %v", diff)
- }
- })
- }
-}
-
-func TestEventResponse_Error(t *testing.T) {
- testCases := map[string]struct {
- t *EventResponse
- msg string
- status int
- want *EventResponse
- }{
- "nil": {},
- "valid": {
- t: &EventResponse{},
- msg: "unit test",
- status: 400,
- want: &EventResponse{
- Status: 400,
- Reason: "unit test",
- },
- },
- }
- for n, tc := range testCases {
- t.Run(n, func(t *testing.T) {
-
- tc.t.Error(tc.status, tc.msg)
-
- got := tc.t
-
- if diff := cmp.Diff(tc.want, got); diff != "" {
- t.Errorf("unexpected (-want, +got) = %v", diff)
- }
- })
- }
-}
diff --git a/v1/cloudevents/event_test.go b/v1/cloudevents/event_test.go
deleted file mode 100644
index 10b38a3ca..000000000
--- a/v1/cloudevents/event_test.go
+++ /dev/null
@@ -1,996 +0,0 @@
-package cloudevents_test
-
-import (
- "encoding/base64"
- "encoding/json"
- "fmt"
- "net/url"
- "strings"
- "testing"
- "time"
-
- "github.com/google/go-cmp/cmp"
-
- ce "github.com/cloudevents/sdk-go/v1/cloudevents"
- "github.com/cloudevents/sdk-go/v1/cloudevents/types"
-)
-
-func TestGetDataContentType(t *testing.T) {
- now := types.Timestamp{Time: time.Now()}
-
- testCases := map[string]struct {
- event ce.Event
- want string
- }{
- "min v01, blank": {
- event: ce.Event{
- Context: MinEventContextV01(),
- },
- want: "",
- },
- "full v01, json": {
- event: ce.Event{
- Context: FullEventContextV01(now),
- },
- want: "application/json",
- },
- "min v02, blank": {
- event: ce.Event{
- Context: MinEventContextV02(),
- },
- want: "",
- },
- "full v02, json": {
- event: ce.Event{
- Context: FullEventContextV02(now),
- },
- want: "application/json",
- },
- "min v03, blank": {
- event: ce.Event{
- Context: MinEventContextV03(),
- },
- want: "",
- },
- "full v03, json": {
- event: ce.Event{
- Context: FullEventContextV03(now),
- },
- want: "application/json",
- },
- "min v1, blank": {
- event: ce.Event{
- Context: MinEventContextV1(),
- },
- want: "",
- },
- "full v1, json": {
- event: ce.Event{
- Context: FullEventContextV1(now),
- },
- want: "application/json",
- },
- }
- for n, tc := range testCases {
- t.Run(n, func(t *testing.T) {
-
- got, _ := tc.event.Context.GetDataMediaType()
-
- if diff := cmp.Diff(tc.want, got); diff != "" {
- t.Errorf("unexpected (-want, +got) = %v", diff)
- }
- })
- }
-}
-
-func TestSource(t *testing.T) {
- now := types.Timestamp{Time: time.Now()}
-
- source := "http://example.com/source"
-
- testCases := map[string]struct {
- event ce.Event
- want string
- }{
- "min v01": {
- event: ce.Event{
- Context: MinEventContextV01(),
- },
- want: source,
- },
- "full v01": {
- event: ce.Event{
- Context: FullEventContextV01(now),
- },
- want: source,
- },
- "min v02": {
- event: ce.Event{
- Context: MinEventContextV02(),
- },
- want: source,
- },
- "full v02": {
- event: ce.Event{
- Context: FullEventContextV02(now),
- },
- want: source,
- },
- "min v03": {
- event: ce.Event{
- Context: MinEventContextV03(),
- },
- want: source,
- },
- "full v03": {
- event: ce.Event{
- Context: FullEventContextV03(now),
- },
- want: source,
- },
- "min v1": {
- event: ce.Event{
- Context: MinEventContextV1(),
- },
- want: source,
- },
- "full v1": {
- event: ce.Event{
- Context: FullEventContextV1(now),
- },
- want: source,
- },
- }
- for n, tc := range testCases {
- t.Run(n, func(t *testing.T) {
-
- got := tc.event.Source()
-
- if diff := cmp.Diff(tc.want, got); diff != "" {
- t.Errorf("unexpected (-want, +got) = %v", diff)
- }
- })
- }
-}
-
-func TestSchemaURL(t *testing.T) {
- now := types.Timestamp{Time: time.Now()}
-
- schema := "http://example.com/schema"
-
- testCases := map[string]struct {
- event ce.Event
- want string
- }{
- "min v01, empty schema": {
- event: ce.Event{
- Context: MinEventContextV01(),
- },
- want: "",
- },
- "full v01, schema": {
- event: ce.Event{
- Context: FullEventContextV01(now),
- },
- want: schema,
- },
- "min v02, empty schema": {
- event: ce.Event{
- Context: MinEventContextV02(),
- },
- want: "",
- },
- "full v02, schema": {
- event: ce.Event{
- Context: FullEventContextV02(now),
- },
- want: schema,
- },
- "min v03, empty schema": {
- event: ce.Event{
- Context: MinEventContextV03(),
- },
- want: "",
- },
- "full v03, schema": {
- event: ce.Event{
- Context: FullEventContextV03(now),
- },
- want: schema,
- },
- "min v1, empty schema": {
- event: ce.Event{
- Context: MinEventContextV1(),
- },
- want: "",
- },
- "full v1, schema": {
- event: ce.Event{
- Context: FullEventContextV1(now),
- },
- want: schema,
- },
- }
- for n, tc := range testCases {
- t.Run(n, func(t *testing.T) {
-
- got := tc.event.DataSchema()
-
- if diff := cmp.Diff(tc.want, got); diff != "" {
- t.Errorf("unexpected (-want, +got) = %v", diff)
- }
- })
- }
-}
-
-type DataExample struct {
- AnInt int `json:"a,omitempty"`
- AString string `json:"b,omitempty"`
- AnArray []string `json:"c,omitempty"`
- AMap map[string]map[string]int `json:"d,omitempty"`
- ATime *time.Time `json:"e,omitempty"`
-}
-
-func TestDataAs(t *testing.T) {
- now := types.Timestamp{Time: time.Now()}
-
- testCases := map[string]struct {
- event ce.Event
- want interface{}
- wantErr error
- }{
- "empty": {
- event: ce.Event{
- Context: MinEventContextV01(),
- },
- want: nil,
- },
- "json simple": {
- event: ce.Event{
- Context: FullEventContextV01(now),
- Data: []byte(`eyJhIjoiYXBwbGUiLCJiIjoiYmFuYW5hIn0K`),
- DataEncoded: true,
- },
- want: &map[string]string{
- "a": "apple",
- "b": "banana",
- },
- },
- "json complex empty": {
- event: ce.Event{
- Context: FullEventContextV01(now),
- Data: []byte(`e30K`),
- DataEncoded: true,
- },
- want: &DataExample{},
- },
- "json complex filled": {
- event: ce.Event{
- Context: FullEventContextV01(now),
- Data: func() []byte {
- data := &DataExample{
- AnInt: 42,
- AMap: map[string]map[string]int{
- "a": {"1": 1, "2": 2, "3": 3},
- "z": {"3": 3, "2": 2, "1": 1},
- },
- AString: "Hello, World!",
- ATime: &now.Time,
- AnArray: []string{"Anne", "Bob", "Chad"},
- }
- j, err := json.Marshal(data)
- if err != nil {
- t.Errorf("failed to marshal test data: %s", err.Error())
- }
- buf := make([]byte, base64.StdEncoding.EncodedLen(len(j)))
- base64.StdEncoding.Encode(buf, j)
- return buf
- }(),
- DataEncoded: true,
- },
- want: &DataExample{
- AnInt: 42,
- AMap: map[string]map[string]int{
- "a": {"1": 1, "2": 2, "3": 3},
- "z": {"3": 3, "2": 2, "1": 1},
- },
- AString: "Hello, World!",
- ATime: &now.Time,
- AnArray: []string{"Anne", "Bob", "Chad"},
- },
- },
- }
- for n, tc := range testCases {
- t.Run(n, func(t *testing.T) {
-
- got, _ := types.Allocate(tc.want)
- err := tc.event.DataAs(got)
-
- if tc.wantErr != nil || err != nil {
- if diff := cmp.Diff(tc.wantErr, err); diff != "" {
- t.Errorf("unexpected error (-want, +got) = %v", diff)
- }
- return
- }
-
- if tc.want != nil {
- if diff := cmp.Diff(tc.want, got); diff != "" {
- t.Errorf("unexpected data (-want, +got) = %v", diff)
- }
- }
- })
- }
-}
-
-func TestValidate(t *testing.T) {
- now := types.Timestamp{Time: time.Now()}
-
- testCases := map[string]struct {
- event ce.Event
- want []string
- }{
- "min valid v0.1": {
- event: ce.Event{
- Context: MinEventContextV01(),
- },
- },
- "min valid v0.2": {
- event: ce.Event{
- Context: MinEventContextV02(),
- },
- },
- "min valid v0.3": {
- event: ce.Event{
- Context: MinEventContextV03(),
- },
- },
- "min valid v1.0": {
- event: ce.Event{
- Context: MinEventContextV1(),
- },
- },
- "json valid, v0.1": {
- event: ce.Event{
- Context: FullEventContextV01(now),
- Data: []byte(`{"a":"apple","b":"banana"}`),
- },
- },
- "json valid, v0.2": {
- event: ce.Event{
- Context: FullEventContextV02(now),
- Data: []byte(`{"a":"apple","b":"banana"}`),
- },
- },
- "json valid, v0.3": {
- event: ce.Event{
- Context: FullEventContextV03(now),
- Data: []byte(`{"a":"apple","b":"banana"}`),
- },
- },
- "json valid, v1.0": {
- event: ce.Event{
- Context: FullEventContextV1(now),
- Data: []byte(`{"a":"apple","b":"banana"}`),
- },
- },
- }
- for n, tc := range testCases {
- t.Run(n, func(t *testing.T) {
-
- got := tc.event.Validate()
- var gotErr string
- if got != nil {
- gotErr = got.Error()
-
- if len(tc.want) == 0 {
- t.Errorf("unexpected no error, got %q", gotErr)
- }
- }
-
- for _, want := range tc.want {
- if !strings.Contains(gotErr, want) {
- t.Errorf("unexpected error, expected to contain %q, got: %q ", want, gotErr)
- }
- }
- })
- }
-}
-
-func TestString(t *testing.T) {
- now := types.Timestamp{Time: time.Now()}
-
- testCases := map[string]struct {
- event ce.Event
- want string
- }{
- "empty v0.1": {
- event: ce.Event{
- Context: &ce.EventContextV01{},
- },
- want: `Validation: invalid
-Validation Error:
-eventType: MUST be a non-empty string
-cloudEventsVersion: MUST be a non-empty string
-source: REQUIRED
-eventID: MUST be a non-empty string
-Context Attributes,
- cloudEventsVersion:
- eventType:
- source:
- eventID:
-`,
- },
- "empty v0.2": {
- event: ce.Event{
- Context: &ce.EventContextV02{},
- },
- want: `Validation: invalid
-Validation Error:
-type: MUST be a non-empty string
-specversion: MUST be a non-empty string
-source: REQUIRED
-id: MUST be a non-empty string
-Context Attributes,
- specversion:
- type:
- source:
- id:
-`,
- },
- "empty v0.3": {
- event: ce.Event{
- Context: &ce.EventContextV03{},
- },
- want: `Validation: invalid
-Validation Error:
-type: MUST be a non-empty string
-specversion: MUST be a non-empty string
-source: REQUIRED
-id: MUST be a non-empty string
-Context Attributes,
- specversion:
- type:
- source:
- id:
-`,
- },
- "empty v1.0": {
- event: ce.Event{
- Context: &ce.EventContextV1{},
- },
- want: `Validation: invalid
-Validation Error:
-id: MUST be a non-empty string
-source: REQUIRED
-specversion: MUST be a non-empty string
-type: MUST be a non-empty string
-Context Attributes,
- specversion:
- type:
- source:
- id:
-`,
- },
- "min v0.1": {
- event: ce.Event{
- Context: MinEventContextV01(),
- },
- want: `Validation: valid
-Context Attributes,
- cloudEventsVersion: 0.1
- eventType: com.example.simple
- source: http://example.com/source
- eventID: ABC-123
-`,
- },
- "min v0.2": {
- event: ce.Event{
- Context: MinEventContextV02(),
- },
- want: `Validation: valid
-Context Attributes,
- specversion: 0.2
- type: com.example.simple
- source: http://example.com/source
- id: ABC-123
-`,
- },
- "min v0.3": {
- event: ce.Event{
- Context: MinEventContextV03(),
- },
- want: `Validation: valid
-Context Attributes,
- specversion: 0.3
- type: com.example.simple
- source: http://example.com/source
- id: ABC-123
-`,
- },
- "min v1.0": {
- event: ce.Event{
- Context: MinEventContextV1(),
- },
- want: `Validation: valid
-Context Attributes,
- specversion: 1.0
- type: com.example.simple
- source: http://example.com/source
- id: ABC-123
-`,
- },
- "json simple, v0.1": {
- event: ce.Event{
- Context: FullEventContextV01(now),
- Data: []byte(`{"a":"apple","b":"banana"}`),
- },
- want: fmt.Sprintf(`Validation: valid
-Context Attributes,
- cloudEventsVersion: 0.1
- eventType: com.example.simple
- eventTypeVersion: v1alpha1
- source: http://example.com/source
- eventID: ABC-123
- eventTime: %s
- schemaURL: http://example.com/schema
- contentType: application/json
-Extensions,
- anothertest: 1
- datacontentencoding: base64
- subject: topic
- test: extended
-Data,
- {
- "a": "apple",
- "b": "banana"
- }
-`, now.String()),
- },
- "json simple, v0.2": {
- event: ce.Event{
- Context: FullEventContextV02(now),
- Data: []byte(`{"a":"apple","b":"banana"}`),
- },
- want: fmt.Sprintf(`Validation: valid
-Context Attributes,
- specversion: 0.2
- type: com.example.simple
- source: http://example.com/source
- id: ABC-123
- time: %s
- schemaurl: http://example.com/schema
- contenttype: application/json
-Extensions,
- anothertest: 1
- datacontentencoding: base64
- eventtypeversion: v1alpha1
- subject: topic
- test: extended
-Data,
- {
- "a": "apple",
- "b": "banana"
- }
-`, now.String()),
- },
- "json simple, v0.3": {
- event: ce.Event{
- Context: FullEventContextV03(now),
- Data: []byte(`{"a":"apple","b":"banana"}`),
- },
- want: fmt.Sprintf(`Validation: valid
-Context Attributes,
- specversion: 0.3
- type: com.example.simple
- source: http://example.com/source
- subject: topic
- id: ABC-123
- time: %s
- schemaurl: http://example.com/schema
- datacontenttype: application/json
- datacontentencoding: base64
-Extensions,
- anothertest: 1
- eventtypeversion: v1alpha1
- test: extended
-Data,
- {
- "a": "apple",
- "b": "banana"
- }
-`, now.String()),
- },
- "json simple, v1.0": {
- event: ce.Event{
- Context: FullEventContextV1(now),
- Data: []byte(`{"a":"apple","b":"banana"}`),
- },
- want: fmt.Sprintf(`Validation: valid
-Context Attributes,
- specversion: 1.0
- type: com.example.simple
- source: http://example.com/source
- subject: topic
- id: ABC-123
- time: %s
- dataschema: http://example.com/schema
- datacontenttype: application/json
-Extensions,
- anothertest: 1
- datacontentencoding: base64
- eventtypeversion: v1alpha1
- test: extended
-Data,
- {
- "a": "apple",
- "b": "banana"
- }
-`, now.String()),
- },
- }
- for n, tc := range testCases {
- t.Run(n, func(t *testing.T) {
-
- got := tc.event.String()
-
- if diff := cmp.Diff(tc.want, got); diff != "" {
- t.Log(got)
- t.Errorf("unexpected string (-want, +got) = %v", diff)
- }
- })
- }
-}
-
-// ExtensionAs is deprecated, replacement is TestExtensions below
-func TestExtensionAs(t *testing.T) {
- now := types.Timestamp{Time: time.Now()}
-
- testCases := map[string]struct {
- event ce.Event
- extension string
- want string
- wantError bool
- wantErrorMsg string
- }{
- "min v01, no extension": {
- event: ce.Event{
- Context: MinEventContextV01(),
- },
- extension: "test",
- wantError: true,
- wantErrorMsg: `extension "test" does not exist`,
- },
- "full v01, test extension": {
- event: ce.Event{
- Context: FullEventContextV01(now),
- },
- extension: "test",
- want: "extended",
- },
- "full v01, anothertest extension invalid type": {
- event: ce.Event{
- Context: FullEventContextV01(now),
- },
- extension: "anothertest",
- wantError: true,
- wantErrorMsg: `invalid type for extension "anothertest"`,
- },
- "min v02, no extension": {
- event: ce.Event{
- Context: MinEventContextV02(),
- },
- extension: "test",
- wantError: true,
- wantErrorMsg: `extension "test" does not exist`,
- },
- "full v02, test extension": {
- event: ce.Event{
- Context: FullEventContextV02(now),
- },
- extension: "test",
- want: "extended",
- },
- "full v02, anothertest extension invalid type": {
- event: ce.Event{
- Context: FullEventContextV02(now),
- },
- extension: "anothertest",
- wantError: true,
- wantErrorMsg: `invalid type for extension "anothertest"`,
- },
- "min v03, no extension": {
- event: ce.Event{
- Context: MinEventContextV03(),
- },
- extension: "test",
- wantError: true,
- wantErrorMsg: `extension "test" does not exist`,
- },
- "full v03, test extension": {
- event: ce.Event{
- Context: FullEventContextV03(now),
- },
- extension: "test",
- want: "extended",
- },
- "full v03, anothertest extension invalid type": {
- event: ce.Event{
- Context: FullEventContextV03(now),
- },
- extension: "anothertest",
- wantError: true,
- wantErrorMsg: `invalid type for extension "anothertest"`,
- },
- "full v1, test extension": {
- event: ce.Event{
- Context: FullEventContextV1(now),
- },
- extension: "test",
- want: "extended",
- },
- "full v1, anothertest extension invalid type": {
- event: ce.Event{
- Context: FullEventContextV1(now),
- },
- extension: "anothertest",
- wantError: true,
- wantErrorMsg: `unknown extension type *string`,
- },
- }
- for n, tc := range testCases {
- t.Run(n, func(t *testing.T) {
-
- var got string
- err := tc.event.Context.ExtensionAs(tc.extension, &got)
-
- if tc.wantError {
- if err == nil {
- t.Errorf("expected error %q, got nil", tc.wantErrorMsg)
- } else {
- if diff := cmp.Diff(tc.wantErrorMsg, err.Error()); diff != "" {
- t.Errorf("unexpected (-want, +got) = %v", diff)
- }
- }
- } else {
- if diff := cmp.Diff(tc.want, got); diff != "" {
- t.Errorf("unexpected (-want, +got) = %v", diff)
- }
- }
- })
- }
-}
-
-func TestExtensions(t *testing.T) {
- now := types.Timestamp{Time: time.Now()}
-
- testCases := map[string]struct {
- event ce.Event
- extension string
- want string
- wantError bool
- wantErrorMsg string
- }{
- "full v01, test extension": {
- event: ce.Event{
- Context: FullEventContextV01(now),
- },
- extension: "test",
- want: "extended",
- },
- "full v01, anothertest extension invalid type": {
- event: ce.Event{
- Context: FullEventContextV01(now),
- },
- extension: "anothertest",
- wantError: true,
- wantErrorMsg: "cannot convert 1 to string",
- },
- "full v02, test extension": {
- event: ce.Event{
- Context: FullEventContextV02(now),
- },
- extension: "test",
- want: "extended",
- },
- "full v02, anothertest extension invalid type": {
- event: ce.Event{
- Context: FullEventContextV02(now),
- },
- extension: "anothertest",
- wantError: true,
- wantErrorMsg: "cannot convert 1 to string",
- },
- "full v03, test extension": {
- event: ce.Event{
- Context: FullEventContextV03(now),
- },
- extension: "test",
- want: "extended",
- },
- "full v03, anothertest extension invalid type": {
- event: ce.Event{
- Context: FullEventContextV03(now),
- },
- extension: "anothertest",
- wantError: true,
- wantErrorMsg: "cannot convert 1 to string",
- },
- "full v1, test extension": {
- event: ce.Event{
- Context: FullEventContextV1(now),
- },
- extension: "test",
- want: "extended",
- },
- "full v1, anothertest extension invalid type": {
- event: ce.Event{
- Context: FullEventContextV1(now),
- },
- extension: "anothertest",
- wantError: true,
- wantErrorMsg: "cannot convert 1 to string",
- },
- }
- for n, tc := range testCases {
- t.Run(n, func(t *testing.T) {
- var got string
- got, err := types.ToString(tc.event.Context.GetExtensions()[tc.extension])
- if tc.wantError {
- if err == nil {
- t.Errorf("expected error %q, got nil", tc.wantErrorMsg)
- } else {
- if diff := cmp.Diff(tc.wantErrorMsg, err.Error()); diff != "" {
- t.Errorf("unexpected (-want, +got) = %v", diff)
- }
- }
- } else {
- if diff := cmp.Diff(tc.want, got); diff != "" {
- t.Errorf("unexpected (-want, +got) = %v", diff)
- }
- }
- })
- }
-}
-
-func strptr(s string) *string {
- return &s
-}
-
-func MinEventContextV01() *ce.EventContextV01 {
- sourceUrl, _ := url.Parse("http://example.com/source")
- source := &types.URLRef{URL: *sourceUrl}
-
- return ce.EventContextV01{
- EventType: "com.example.simple",
- Source: *source,
- EventID: "ABC-123",
- }.AsV01()
-}
-
-func MinEventContextV02() *ce.EventContextV02 {
- sourceUrl, _ := url.Parse("http://example.com/source")
- source := &types.URLRef{URL: *sourceUrl}
-
- return ce.EventContextV02{
- Type: "com.example.simple",
- Source: *source,
- ID: "ABC-123",
- }.AsV02()
-}
-
-func MinEventContextV03() *ce.EventContextV03 {
- sourceUrl, _ := url.Parse("http://example.com/source")
- source := &types.URLRef{URL: *sourceUrl}
-
- return ce.EventContextV03{
- Type: "com.example.simple",
- Source: *source,
- ID: "ABC-123",
- }.AsV03()
-}
-
-func MinEventContextV1() *ce.EventContextV1 {
- sourceUrl, _ := url.Parse("http://example.com/source")
- source := &types.URIRef{URL: *sourceUrl}
-
- return ce.EventContextV1{
- Type: "com.example.simple",
- Source: *source,
- ID: "ABC-123",
- }.AsV1()
-}
-
-func FullEventContextV01(now types.Timestamp) *ce.EventContextV01 {
- sourceUrl, _ := url.Parse("http://example.com/source")
- source := &types.URLRef{URL: *sourceUrl}
-
- schemaUrl, _ := url.Parse("http://example.com/schema")
- schema := &types.URLRef{URL: *schemaUrl}
-
- eventContextV01 := ce.EventContextV01{
- EventID: "ABC-123",
- EventTime: &now,
- EventType: "com.example.simple",
- EventTypeVersion: strptr("v1alpha1"),
- SchemaURL: schema,
- ContentType: ce.StringOfApplicationJSON(),
- Source: *source,
- }
- _ = eventContextV01.SetExtension(ce.SubjectKey, "topic")
- _ = eventContextV01.SetExtension(ce.DataContentEncodingKey, ce.Base64)
- _ = eventContextV01.SetExtension("test", "extended")
- _ = eventContextV01.SetExtension("anothertest", int32(1))
- return eventContextV01.AsV01()
-}
-
-func FullEventContextV02(now types.Timestamp) *ce.EventContextV02 {
- sourceUrl, _ := url.Parse("http://example.com/source")
- source := &types.URLRef{URL: *sourceUrl}
-
- schemaUrl, _ := url.Parse("http://example.com/schema")
- schema := &types.URLRef{URL: *schemaUrl}
-
- extensions := make(map[string]interface{})
- extensions["test"] = "extended"
- extensions["anothertest"] = int32(1)
-
- eventContextV02 := ce.EventContextV02{
- ID: "ABC-123",
- Time: &now,
- Type: "com.example.simple",
- SchemaURL: schema,
- ContentType: ce.StringOfApplicationJSON(),
- Source: *source,
- Extensions: extensions,
- }
- _ = eventContextV02.SetExtension(ce.SubjectKey, "topic")
- _ = eventContextV02.SetExtension(ce.DataContentEncodingKey, ce.Base64)
- _ = eventContextV02.SetExtension(ce.EventTypeVersionKey, "v1alpha1")
- return eventContextV02.AsV02()
-}
-
-func FullEventContextV03(now types.Timestamp) *ce.EventContextV03 {
- sourceUrl, _ := url.Parse("http://example.com/source")
- source := &types.URLRef{URL: *sourceUrl}
-
- schemaUrl, _ := url.Parse("http://example.com/schema")
- schema := &types.URLRef{URL: *schemaUrl}
-
- eventContextV03 := ce.EventContextV03{
- ID: "ABC-123",
- Time: &now,
- Type: "com.example.simple",
- SchemaURL: schema,
- DataContentType: ce.StringOfApplicationJSON(),
- DataContentEncoding: ce.StringOfBase64(),
- Source: *source,
- Subject: strptr("topic"),
- }
- _ = eventContextV03.SetExtension("test", "extended")
- _ = eventContextV03.SetExtension("anothertest", int32(1))
- _ = eventContextV03.SetExtension(ce.EventTypeVersionKey, "v1alpha1")
- return eventContextV03.AsV03()
-}
-
-func FullEventContextV1(now types.Timestamp) *ce.EventContextV1 {
- sourceUrl, _ := url.Parse("http://example.com/source")
- source := &types.URIRef{URL: *sourceUrl}
-
- schemaUrl, _ := url.Parse("http://example.com/schema")
- schema := &types.URI{URL: *schemaUrl}
-
- eventContextV1 := ce.EventContextV1{
- ID: "ABC-123",
- Time: &now,
- Type: "com.example.simple",
- DataSchema: schema,
- DataContentType: ce.StringOfApplicationJSON(),
- Source: *source,
- Subject: strptr("topic"),
- }
- _ = eventContextV1.SetExtension("test", "extended")
- _ = eventContextV1.SetExtension("anothertest", 1)
- _ = eventContextV1.SetExtension(ce.EventTypeVersionKey, "v1alpha1")
- _ = eventContextV1.SetExtension(ce.DataContentEncodingKey, ce.Base64)
- return eventContextV1.AsV1()
-}
diff --git a/v1/cloudevents/event_writer.go b/v1/cloudevents/event_writer.go
deleted file mode 100644
index 19982ca5b..000000000
--- a/v1/cloudevents/event_writer.go
+++ /dev/null
@@ -1,116 +0,0 @@
-package cloudevents
-
-import (
- "fmt"
- "time"
-)
-
-var _ EventWriter = (*Event)(nil)
-
-// SetSpecVersion implements EventWriter.SetSpecVersion
-func (e *Event) SetSpecVersion(v string) {
- if e.Context == nil {
- switch v {
- case CloudEventsVersionV01:
- e.Context = EventContextV01{}.AsV01()
- case CloudEventsVersionV02:
- e.Context = EventContextV02{}.AsV02()
- case CloudEventsVersionV03:
- e.Context = EventContextV03{}.AsV03()
- case CloudEventsVersionV1:
- e.Context = EventContextV1{}.AsV1()
- default:
- e.fieldError("specversion", fmt.Errorf("a valid spec version is required: [%s, %s, %s, %s]",
- CloudEventsVersionV01, CloudEventsVersionV02, CloudEventsVersionV03, CloudEventsVersionV1))
- return
- }
- e.fieldOK("specversion")
- return
- }
- if err := e.Context.SetSpecVersion(v); err != nil {
- e.fieldError("specversion", err)
- } else {
- e.fieldOK("specversion")
- }
-}
-
-// SetType implements EventWriter.SetType
-func (e *Event) SetType(t string) {
- if err := e.Context.SetType(t); err != nil {
- e.fieldError("type", err)
- } else {
- e.fieldOK("type")
- }
-}
-
-// SetSource implements EventWriter.SetSource
-func (e *Event) SetSource(s string) {
- if err := e.Context.SetSource(s); err != nil {
- e.fieldError("source", err)
- } else {
- e.fieldOK("source")
- }
-}
-
-// SetSubject implements EventWriter.SetSubject
-func (e *Event) SetSubject(s string) {
- if err := e.Context.SetSubject(s); err != nil {
- e.fieldError("subject", err)
- } else {
- e.fieldOK("subject")
- }
-}
-
-// SetID implements EventWriter.SetID
-func (e *Event) SetID(id string) {
- if err := e.Context.SetID(id); err != nil {
- e.fieldError("id", err)
- } else {
- e.fieldOK("id")
- }
-}
-
-// SetTime implements EventWriter.SetTime
-func (e *Event) SetTime(t time.Time) {
- if err := e.Context.SetTime(t); err != nil {
- e.fieldError("time", err)
- } else {
- e.fieldOK("time")
- }
-}
-
-// SetDataSchema implements EventWriter.SetDataSchema
-func (e *Event) SetDataSchema(s string) {
- if err := e.Context.SetDataSchema(s); err != nil {
- e.fieldError("dataschema", err)
- } else {
- e.fieldOK("dataschema")
- }
-}
-
-// SetDataContentType implements EventWriter.SetDataContentType
-func (e *Event) SetDataContentType(ct string) {
- if err := e.Context.SetDataContentType(ct); err != nil {
- e.fieldError("datacontenttype", err)
- } else {
- e.fieldOK("datacontenttype")
- }
-}
-
-// DeprecatedSetDataContentEncoding implements EventWriter.DeprecatedSetDataContentEncoding
-func (e *Event) SetDataContentEncoding(enc string) {
- if err := e.Context.DeprecatedSetDataContentEncoding(enc); err != nil {
- e.fieldError("datacontentencoding", err)
- } else {
- e.fieldOK("datacontentencoding")
- }
-}
-
-// SetExtension implements EventWriter.SetExtension
-func (e *Event) SetExtension(name string, obj interface{}) {
- if err := e.Context.SetExtension(name, obj); err != nil {
- e.fieldError("extension:"+name, err)
- } else {
- e.fieldOK("extension:" + name)
- }
-}
diff --git a/v1/cloudevents/eventcontext.go b/v1/cloudevents/eventcontext.go
deleted file mode 100644
index 5683a8292..000000000
--- a/v1/cloudevents/eventcontext.go
+++ /dev/null
@@ -1,132 +0,0 @@
-package cloudevents
-
-import "time"
-
-// EventContextReader are the methods required to be a reader of context
-// attributes.
-type EventContextReader interface {
- // GetSpecVersion returns the native CloudEvents Spec version of the event
- // context.
- GetSpecVersion() string
- // GetType returns the CloudEvents type from the context.
- GetType() string
- // GetSource returns the CloudEvents source from the context.
- GetSource() string
- // GetSubject returns the CloudEvents subject from the context.
- GetSubject() string
- // GetID returns the CloudEvents ID from the context.
- GetID() string
- // GetTime returns the CloudEvents creation time from the context.
- GetTime() time.Time
- // GetDataSchema returns the CloudEvents schema URL (if any) from the
- // context.
- GetDataSchema() string
- // GetDataContentType returns content type on the context.
- GetDataContentType() string
- // DeprecatedGetDataContentEncoding returns content encoding on the context.
- DeprecatedGetDataContentEncoding() string
-
- // GetDataMediaType returns the MIME media type for encoded data, which is
- // needed by both encoding and decoding. This is a processed form of
- // GetDataContentType and it may return an error.
- GetDataMediaType() (string, error)
-
- // DEPRECATED: Access extensions directly via the GetExtensions()
- // For example replace this:
- //
- // var i int
- // err := ec.ExtensionAs("foo", &i)
- //
- // With this:
- //
- // i, err := types.ToInteger(ec.GetExtensions["foo"])
- //
- ExtensionAs(string, interface{}) error
-
- // GetExtensions returns the full extensions map.
- //
- // Extensions use the CloudEvents type system, details in package cloudevents/types.
- GetExtensions() map[string]interface{}
-
- // GetExtension returns the extension associated with with the given key.
- // The given key is case insensitive. If the extension can not be found,
- // an error will be returned.
- GetExtension(string) (interface{}, error)
-}
-
-// EventContextWriter are the methods required to be a writer of context
-// attributes.
-type EventContextWriter interface {
- // SetSpecVersion sets the spec version of the context.
- SetSpecVersion(string) error
- // SetType sets the type of the context.
- SetType(string) error
- // SetSource sets the source of the context.
- SetSource(string) error
- // SetSubject sets the subject of the context.
- SetSubject(string) error
- // SetID sets the ID of the context.
- SetID(string) error
- // SetTime sets the time of the context.
- SetTime(time time.Time) error
- // SetDataSchema sets the schema url of the context.
- SetDataSchema(string) error
- // SetDataContentType sets the data content type of the context.
- SetDataContentType(string) error
- // DeprecatedSetDataContentEncoding sets the data context encoding of the context.
- DeprecatedSetDataContentEncoding(string) error
-
- // SetExtension sets the given interface onto the extension attributes
- // determined by the provided name.
- //
- // This function fails in V1 if the name doesn't respect the regex ^[a-zA-Z0-9]+$
- //
- // Package ./types documents the types that are allowed as extension values.
- SetExtension(string, interface{}) error
-}
-
-// EventContextConverter are the methods that allow for event version
-// conversion.
-type EventContextConverter interface {
- // AsV01 provides a translation from whatever the "native" encoding of the
- // CloudEvent was to the equivalent in v0.1 field names, moving fields to or
- // from extensions as necessary.
- AsV01() *EventContextV01
-
- // AsV02 provides a translation from whatever the "native" encoding of the
- // CloudEvent was to the equivalent in v0.2 field names, moving fields to or
- // from extensions as necessary.
- AsV02() *EventContextV02
-
- // AsV03 provides a translation from whatever the "native" encoding of the
- // CloudEvent was to the equivalent in v0.3 field names, moving fields to or
- // from extensions as necessary.
- AsV03() *EventContextV03
-
- // AsV1 provides a translation from whatever the "native" encoding of the
- // CloudEvent was to the equivalent in v1.0 field names, moving fields to or
- // from extensions as necessary.
- AsV1() *EventContextV1
-}
-
-// EventContext is conical interface for a CloudEvents Context.
-type EventContext interface {
- // EventContextConverter allows for conversion between versions.
- EventContextConverter
-
- // EventContextReader adds methods for reading context.
- EventContextReader
-
- // EventContextWriter adds methods for writing to context.
- EventContextWriter
-
- // Validate the event based on the specifics of the CloudEvents spec version
- // represented by this event context.
- Validate() error
-
- // Clone clones the event context.
- Clone() EventContext
-
- // String returns a pretty-printed representation of the EventContext.
- String() string
-}
diff --git a/v1/cloudevents/eventcontext_test.go b/v1/cloudevents/eventcontext_test.go
deleted file mode 100644
index 0b20a4730..000000000
--- a/v1/cloudevents/eventcontext_test.go
+++ /dev/null
@@ -1,353 +0,0 @@
-package cloudevents_test
-
-import (
- "testing"
- "time"
-
- ce "github.com/cloudevents/sdk-go/v1/cloudevents"
- "github.com/cloudevents/sdk-go/v1/cloudevents/types"
- "github.com/google/go-cmp/cmp"
- "github.com/stretchr/testify/require"
-)
-
-func TestContextAsV01(t *testing.T) {
- now := types.Timestamp{Time: time.Now()}
-
- testCases := map[string]struct {
- event ce.Event
- want *ce.EventContextV01
- }{
- "empty, no conversion": {
- event: ce.Event{
- Context: &ce.EventContextV01{},
- },
- want: &ce.EventContextV01{
- CloudEventsVersion: "0.1",
- },
- },
- "min v01, no conversion": {
- event: ce.Event{
- Context: MinEventContextV01(),
- },
- want: MinEventContextV01(),
- },
- "full v01, no conversion": {
- event: ce.Event{
- Context: FullEventContextV01(now),
- },
- want: FullEventContextV01(now),
- },
- "min v02 -> v01": {
- event: ce.Event{
- Context: MinEventContextV02(),
- },
- want: MinEventContextV01(),
- },
- "full v02 -> v01": {
- event: ce.Event{
- Context: FullEventContextV02(now),
- },
- want: FullEventContextV01(now),
- },
- "min v03 -> v01": {
- event: ce.Event{
- Context: MinEventContextV03(),
- },
- want: MinEventContextV01(),
- },
- "full v03 -> v01": {
- event: ce.Event{
- Context: FullEventContextV03(now),
- },
- want: FullEventContextV01(now),
- },
- "min v1 -> v01": {
- event: ce.Event{
- Context: MinEventContextV1(),
- },
- want: MinEventContextV01(),
- },
- "full v1 -> v01": {
- event: ce.Event{
- Context: FullEventContextV1(now),
- },
- want: FullEventContextV01(now),
- },
- }
- for n, tc := range testCases {
- t.Run(n, func(t *testing.T) {
-
- got := tc.event.Context.AsV01()
-
- if diff := cmp.Diff(tc.want, got); diff != "" {
- t.Errorf("unexpected (-want, +got) = %v", diff)
- }
- })
- }
-}
-
-func TestContextAsV02(t *testing.T) {
- now := types.Timestamp{Time: time.Now()}
-
- testCases := map[string]struct {
- event ce.Event
- want *ce.EventContextV02
- }{
- "empty, no conversion": {
- event: ce.Event{
- Context: &ce.EventContextV02{},
- },
- want: &ce.EventContextV02{
- SpecVersion: "0.2",
- },
- },
- "min v01 -> v02": {
- event: ce.Event{
- Context: MinEventContextV01(),
- },
- want: MinEventContextV02(),
- },
- "full v01 -> v02": {
- event: ce.Event{
- Context: FullEventContextV01(now),
- },
- want: FullEventContextV02(now),
- },
- "min v02, no conversion": {
- event: ce.Event{
- Context: MinEventContextV02(),
- },
- want: MinEventContextV02(),
- },
- "full v02, no conversion": {
- event: ce.Event{
- Context: FullEventContextV02(now),
- },
- want: FullEventContextV02(now),
- },
- "min v03 -> v02": {
- event: ce.Event{
- Context: MinEventContextV03(),
- },
- want: MinEventContextV02(),
- },
- "full v03 -> v02": {
- event: ce.Event{
- Context: FullEventContextV03(now),
- },
- want: FullEventContextV02(now),
- },
-
- "min v1 -> v02": {
- event: ce.Event{
- Context: MinEventContextV1(),
- },
- want: MinEventContextV02(),
- },
- "full v1 -> v02": {
- event: ce.Event{
- Context: FullEventContextV1(now),
- },
- want: FullEventContextV02(now),
- },
- }
- for n, tc := range testCases {
- t.Run(n, func(t *testing.T) {
-
- got := tc.event.Context.AsV02()
-
- if diff := cmp.Diff(tc.want, got); diff != "" {
- t.Errorf("unexpected (-want, +got) = %v", diff)
- }
- })
- }
-}
-
-func TestContextAsV03(t *testing.T) {
- now := types.Timestamp{Time: time.Now()}
-
- testCases := map[string]struct {
- event ce.Event
- want *ce.EventContextV03
- }{
- "empty, no conversion": {
- event: ce.Event{
- Context: &ce.EventContextV03{},
- },
- want: &ce.EventContextV03{
- SpecVersion: "0.3",
- },
- },
- "min v01 -> v03": {
- event: ce.Event{
- Context: MinEventContextV01(),
- },
- want: MinEventContextV03(),
- },
- "full v01 -> v03": {
- event: ce.Event{
- Context: FullEventContextV01(now),
- },
- want: FullEventContextV03(now),
- },
- "min v02 -> v03": {
- event: ce.Event{
- Context: MinEventContextV02(),
- },
- want: MinEventContextV03(),
- },
- "full v02 -> v03": {
- event: ce.Event{
- Context: FullEventContextV02(now),
- },
- want: FullEventContextV03(now),
- },
- "min v03, no conversion": {
- event: ce.Event{
- Context: MinEventContextV03(),
- },
- want: MinEventContextV03(),
- },
- "full v03, no conversion": {
- event: ce.Event{
- Context: FullEventContextV03(now),
- },
- want: FullEventContextV03(now),
- },
- "min v1 -> v03": {
- event: ce.Event{
- Context: MinEventContextV1(),
- },
- want: MinEventContextV03(),
- },
- "full v1 -> v03": {
- event: ce.Event{
- Context: FullEventContextV1(now),
- },
- want: FullEventContextV03(now),
- },
- }
- for n, tc := range testCases {
- t.Run(n, func(t *testing.T) {
-
- got := tc.event.Context.AsV03()
-
- if diff := cmp.Diff(tc.want, got); diff != "" {
- t.Errorf("unexpected (-want, +got) = %v", diff)
- }
- })
- }
-}
-
-func TestContextAsV1(t *testing.T) {
- now := types.Timestamp{Time: time.Now()}
-
- testCases := map[string]struct {
- event ce.Event
- want *ce.EventContextV1
- }{
- "empty, no conversion": {
- event: ce.Event{
- Context: &ce.EventContextV1{},
- },
- want: &ce.EventContextV1{
- SpecVersion: "1.0",
- },
- },
- "min v01 -> v1": {
- event: ce.Event{
- Context: MinEventContextV01(),
- },
- want: MinEventContextV1(),
- },
- "full v01 -> v1": {
- event: ce.Event{
- Context: FullEventContextV01(now),
- },
- want: FullEventContextV1(now),
- },
- "min v02 -> v1": {
- event: ce.Event{
- Context: MinEventContextV02(),
- },
- want: MinEventContextV1(),
- },
- "full v02 -> v1": {
- event: ce.Event{
- Context: FullEventContextV02(now),
- },
- want: FullEventContextV1(now),
- },
- "min v03 -> v1": {
- event: ce.Event{
- Context: MinEventContextV03(),
- },
- want: MinEventContextV1(),
- },
- "full v03 -> v1": {
- event: ce.Event{
- Context: FullEventContextV03(now),
- },
- want: FullEventContextV1(now),
- },
- "min v1, no conversion": {
- event: ce.Event{
- Context: MinEventContextV1(),
- },
- want: MinEventContextV1(),
- },
- "full v1, no conversion": {
- event: ce.Event{
- Context: FullEventContextV1(now),
- },
- want: FullEventContextV1(now),
- },
- }
- for n, tc := range testCases {
- t.Run(n, func(t *testing.T) {
-
- got := tc.event.Context.AsV1()
-
- if diff := cmp.Diff(tc.want, got); diff != "" {
- t.Errorf("unexpected (-want, +got) = %v", diff)
- }
- })
- }
-}
-
-func TestEventContextClone(t *testing.T) {
- tests := []struct {
- name string
- context ce.EventContext
- }{
- {
- name: "v0.1",
- context: FullEventContextV01(types.Timestamp{Time: time.Now()}),
- },
- {
- name: "v0.2",
- context: FullEventContextV02(types.Timestamp{Time: time.Now()}),
- },
- {
- name: "v0.3",
- context: FullEventContextV03(types.Timestamp{Time: time.Now()}),
- },
- {
- name: "v1.0",
- context: FullEventContextV1(types.Timestamp{Time: time.Now()}),
- },
- }
- for _, test := range tests {
- t.Run(test.name, func(t *testing.T) {
- initial := test.context
- require.NoError(t, initial.SetExtension("aaa", "bbb"))
-
- clone := initial.Clone()
- require.NoError(t, clone.SetExtension("aaa", "ccc"))
-
- val, err := initial.GetExtension("aaa")
- require.NoError(t, err)
- require.Equal(t, "bbb", val)
- })
- }
-}
diff --git a/v1/cloudevents/eventcontext_v01.go b/v1/cloudevents/eventcontext_v01.go
deleted file mode 100644
index 0ecacf68f..000000000
--- a/v1/cloudevents/eventcontext_v01.go
+++ /dev/null
@@ -1,286 +0,0 @@
-package cloudevents
-
-import (
- "fmt"
- "sort"
- "strings"
-
- "github.com/cloudevents/sdk-go/v1/cloudevents/types"
-)
-
-const (
- // CloudEventsVersionV01 represents the version 0.1 of the CloudEvents spec.
- CloudEventsVersionV01 = "0.1"
-)
-
-// EventContextV01 holds standard metadata about an event. See
-// https://github.com/cloudevents/spec/blob/v0.1/spec.md#context-attributes for
-// details on these fields.
-type EventContextV01 struct {
- // The version of the CloudEvents specification used by the event.
- CloudEventsVersion string `json:"cloudEventsVersion,omitempty"`
- // ID of the event; must be non-empty and unique within the scope of the producer.
- EventID string `json:"eventID"`
- // Timestamp when the event happened.
- EventTime *types.Timestamp `json:"eventTime,omitempty"`
- // Type of occurrence which has happened.
- EventType string `json:"eventType"`
- // The version of the `eventType`; this is producer-specific.
- EventTypeVersion *string `json:"eventTypeVersion,omitempty"`
- // A link to the schema that the `data` attribute adheres to.
- SchemaURL *types.URLRef `json:"schemaURL,omitempty"`
- // A MIME (RFC 2046) string describing the media type of `data`.
- // TODO: Should an empty string assume `application/json`, or auto-detect the content?
- ContentType *string `json:"contentType,omitempty"`
- // A URI describing the event producer.
- Source types.URLRef `json:"source"`
- // Additional metadata without a well-defined structure.
- Extensions map[string]interface{} `json:"extensions,omitempty"`
-}
-
-// Adhere to EventContext
-var _ EventContext = (*EventContextV01)(nil)
-
-// ExtensionAs implements EventContextReader.ExtensionAs
-func (ec EventContextV01) ExtensionAs(name string, obj interface{}) error {
- value, ok := ec.Extensions[name]
- if !ok {
- return fmt.Errorf("extension %q does not exist", name)
- }
- // Only support *string for now.
- switch v := obj.(type) {
- case *string:
- if valueAsString, ok := value.(string); ok {
- *v = valueAsString
- return nil
- } else {
- return fmt.Errorf("invalid type for extension %q", name)
- }
- default:
- return fmt.Errorf("unknown extension type %T", obj)
- }
-}
-
-// SetExtension adds the extension 'name' with value 'value' to the CloudEvents context.
-func (ec *EventContextV01) SetExtension(name string, value interface{}) error {
- if ec.Extensions == nil {
- ec.Extensions = make(map[string]interface{})
- }
- if value == nil {
- delete(ec.Extensions, name)
- } else {
- ec.Extensions[name] = value
- }
- return nil
-}
-
-// Clone implements EventContextConverter.Clone
-func (ec EventContextV01) Clone() EventContext {
- ev01 := ec.AsV01()
- ev01.Extensions = ev01.cloneExtensions()
- return ev01
-}
-
-func (ec *EventContextV01) cloneExtensions() map[string]interface{} {
- old := ec.Extensions
- if old == nil {
- return nil
- }
- new := make(map[string]interface{}, len(ec.Extensions))
- for k, v := range old {
- new[k] = v
- }
- return new
-}
-
-// AsV01 implements EventContextConverter.AsV01
-func (ec EventContextV01) AsV01() *EventContextV01 {
- ec.CloudEventsVersion = CloudEventsVersionV01
- return &ec
-}
-
-// AsV02 implements EventContextConverter.AsV02
-func (ec EventContextV01) AsV02() *EventContextV02 {
- ret := EventContextV02{
- SpecVersion: CloudEventsVersionV02,
- Type: ec.EventType,
- Source: ec.Source,
- ID: ec.EventID,
- Time: ec.EventTime,
- SchemaURL: ec.SchemaURL,
- ContentType: ec.ContentType,
- Extensions: make(map[string]interface{}),
- }
-
- // eventTypeVersion was retired in v0.2, so put it in an extension.
- if ec.EventTypeVersion != nil {
- _ = ret.SetExtension(EventTypeVersionKey, *ec.EventTypeVersion)
- }
- if ec.Extensions != nil {
- for k, v := range ec.Extensions {
- ret.Extensions[k] = v
- }
- }
- if len(ret.Extensions) == 0 {
- ret.Extensions = nil
- }
- return &ret
-}
-
-// AsV03 implements EventContextConverter.AsV03
-func (ec EventContextV01) AsV03() *EventContextV03 {
- return ec.AsV02().AsV03()
-}
-
-// AsV1 implements EventContextConverter.AsV1
-func (ec EventContextV01) AsV1() *EventContextV1 {
- return ec.AsV02().AsV03().AsV1()
-}
-
-// Validate returns errors based on requirements from the CloudEvents spec.
-// For more details, see https://github.com/cloudevents/spec/blob/v0.1/spec.md
-func (ec EventContextV01) Validate() error {
- errors := []string(nil)
-
- // eventType
- // Type: String
- // Constraints:
- // REQUIRED
- // MUST be a non-empty string
- // SHOULD be prefixed with a reverse-DNS name. The prefixed domain dictates the organization which defines the semantics of this event type.
- eventType := strings.TrimSpace(ec.EventType)
- if eventType == "" {
- errors = append(errors, "eventType: MUST be a non-empty string")
- }
-
- // eventTypeVersion
- // Type: String
- // Constraints:
- // OPTIONAL
- // If present, MUST be a non-empty string
- if ec.EventTypeVersion != nil {
- eventTypeVersion := strings.TrimSpace(*ec.EventTypeVersion)
- if eventTypeVersion == "" {
- errors = append(errors, "eventTypeVersion: if present, MUST be a non-empty string")
- }
- }
-
- // cloudEventsVersion
- // Type: String
- // Constraints:
- // REQUIRED
- // MUST be a non-empty string
- cloudEventsVersion := strings.TrimSpace(ec.CloudEventsVersion)
- if cloudEventsVersion == "" {
- errors = append(errors, "cloudEventsVersion: MUST be a non-empty string")
- }
-
- // source
- // Type: URI
- // Constraints:
- // REQUIRED
- source := strings.TrimSpace(ec.Source.String())
- if source == "" {
- errors = append(errors, "source: REQUIRED")
- }
-
- // eventID
- // Type: String
- // Constraints:
- // REQUIRED
- // MUST be a non-empty string
- // MUST be unique within the scope of the producer
- eventID := strings.TrimSpace(ec.EventID)
- if eventID == "" {
- errors = append(errors, "eventID: MUST be a non-empty string")
-
- // no way to test "MUST be unique within the scope of the producer"
- }
-
- // eventTime
- // Type: Timestamp
- // Constraints:
- // OPTIONAL
- // If present, MUST adhere to the format specified in RFC 3339
- // --> no need to test this, no way to set the eventTime without it being valid.
-
- // schemaURL
- // Type: URI
- // Constraints:
- // OPTIONAL
- // If present, MUST adhere to the format specified in RFC 3986
- if ec.SchemaURL != nil {
- schemaURL := strings.TrimSpace(ec.SchemaURL.String())
- // empty string is not RFC 3986 compatible.
- if schemaURL == "" {
- errors = append(errors, "schemaURL: if present, MUST adhere to the format specified in RFC 3986")
- }
- }
-
- // contentType
- // Type: String per RFC 2046
- // Constraints:
- // OPTIONAL
- // If present, MUST adhere to the format specified in RFC 2046
- if ec.ContentType != nil {
- contentType := strings.TrimSpace(*ec.ContentType)
- if contentType == "" {
- // TODO: need to test for RFC 2046
- errors = append(errors, "contentType: if present, MUST adhere to the format specified in RFC 2046")
- }
- }
-
- // extensions
- // Type: Map
- // Constraints:
- // OPTIONAL
- // If present, MUST contain at least one entry
- if ec.Extensions != nil {
- if len(ec.Extensions) == 0 {
- errors = append(errors, "extensions: if present, MUST contain at least one entry")
- }
- }
-
- if len(errors) > 0 {
- return fmt.Errorf(strings.Join(errors, "\n"))
- }
- return nil
-}
-
-// String returns a pretty-printed representation of the EventContext.
-func (ec EventContextV01) String() string {
- b := strings.Builder{}
-
- b.WriteString("Context Attributes,\n")
-
- b.WriteString(" cloudEventsVersion: " + ec.CloudEventsVersion + "\n")
- b.WriteString(" eventType: " + ec.EventType + "\n")
- if ec.EventTypeVersion != nil {
- b.WriteString(" eventTypeVersion: " + *ec.EventTypeVersion + "\n")
- }
- b.WriteString(" source: " + ec.Source.String() + "\n")
- b.WriteString(" eventID: " + ec.EventID + "\n")
- if ec.EventTime != nil {
- b.WriteString(" eventTime: " + ec.EventTime.String() + "\n")
- }
- if ec.SchemaURL != nil {
- b.WriteString(" schemaURL: " + ec.SchemaURL.String() + "\n")
- }
- if ec.ContentType != nil {
- b.WriteString(" contentType: " + *ec.ContentType + "\n")
- }
-
- if ec.Extensions != nil && len(ec.Extensions) > 0 {
- b.WriteString("Extensions,\n")
- keys := make([]string, 0, len(ec.Extensions))
- for k := range ec.Extensions {
- keys = append(keys, k)
- }
- sort.Strings(keys)
- for _, key := range keys {
- b.WriteString(fmt.Sprintf(" %s: %v\n", key, ec.Extensions[key]))
- }
- }
-
- return b.String()
-}
diff --git a/v1/cloudevents/eventcontext_v01_reader.go b/v1/cloudevents/eventcontext_v01_reader.go
deleted file mode 100644
index 8d75ea70c..000000000
--- a/v1/cloudevents/eventcontext_v01_reader.go
+++ /dev/null
@@ -1,101 +0,0 @@
-package cloudevents
-
-import (
- "fmt"
- "mime"
- "time"
-)
-
-// Adhere to EventContextReader
-var _ EventContextReader = (*EventContextV01)(nil)
-
-// GetSpecVersion implements EventContextReader.GetSpecVersion
-func (ec EventContextV01) GetSpecVersion() string {
- if ec.CloudEventsVersion != "" {
- return ec.CloudEventsVersion
- }
- return CloudEventsVersionV01
-}
-
-// GetDataContentType implements EventContextReader.GetDataContentType
-func (ec EventContextV01) GetDataContentType() string {
- if ec.ContentType != nil {
- return *ec.ContentType
- }
- return ""
-}
-
-// GetDataMediaType implements EventContextReader.GetDataMediaType
-func (ec EventContextV01) GetDataMediaType() (string, error) {
- if ec.ContentType != nil {
- mediaType, _, err := mime.ParseMediaType(*ec.ContentType)
- if err != nil {
- return "", err
- }
- return mediaType, nil
- }
- return "", nil
-}
-
-// GetType implements EventContextReader.GetType
-func (ec EventContextV01) GetType() string {
- return ec.EventType
-}
-
-// GetSource implements EventContextReader.GetSource
-func (ec EventContextV01) GetSource() string {
- return ec.Source.String()
-}
-
-// GetSubject implements EventContextReader.GetSubject
-func (ec EventContextV01) GetSubject() string {
- var sub string
- if err := ec.ExtensionAs(SubjectKey, &sub); err != nil {
- return ""
- }
- return sub
-}
-
-// GetID implements EventContextReader.GetID
-func (ec EventContextV01) GetID() string {
- return ec.EventID
-}
-
-// GetTime implements EventContextReader.GetTime
-func (ec EventContextV01) GetTime() time.Time {
- if ec.EventTime != nil {
- return ec.EventTime.Time
- }
- return time.Time{}
-}
-
-// GetDataSchema implements EventContextReader.GetDataSchema
-func (ec EventContextV01) GetDataSchema() string {
- if ec.SchemaURL != nil {
- return ec.SchemaURL.String()
- }
- return ""
-}
-
-// DeprecatedGetDataContentEncoding implements EventContextReader.DeprecatedGetDataContentEncoding
-func (ec EventContextV01) DeprecatedGetDataContentEncoding() string {
- var enc string
- if err := ec.ExtensionAs(DataContentEncodingKey, &enc); err != nil {
- return ""
- }
- return enc
-}
-
-// GetExtensions implements EventContextReader.GetExtensions
-func (ec EventContextV01) GetExtensions() map[string]interface{} {
- return ec.Extensions
-}
-
-// GetExtension implements EventContextReader.GetExtension
-func (ec EventContextV01) GetExtension(key string) (interface{}, error) {
- v, ok := caseInsensitiveSearch(key, ec.Extensions)
- if !ok {
- return "", fmt.Errorf("%q not found", key)
- }
- return v, nil
-}
diff --git a/v1/cloudevents/eventcontext_v01_test.go b/v1/cloudevents/eventcontext_v01_test.go
deleted file mode 100644
index e0127b309..000000000
--- a/v1/cloudevents/eventcontext_v01_test.go
+++ /dev/null
@@ -1,205 +0,0 @@
-package cloudevents_test
-
-import (
- "net/url"
- "strings"
- "testing"
- "time"
-
- ce "github.com/cloudevents/sdk-go/v1/cloudevents"
- "github.com/cloudevents/sdk-go/v1/cloudevents/types"
- "github.com/google/go-cmp/cmp"
-)
-
-func TestValidateV01(t *testing.T) {
- now := types.Timestamp{Time: time.Now()}
-
- sourceUrl, _ := url.Parse("http://example.com/source")
- source := &types.URLRef{URL: *sourceUrl}
-
- schemaUrl, _ := url.Parse("http://example.com/schema")
- schema := &types.URLRef{URL: *schemaUrl}
-
- extensions := make(map[string]interface{})
- extensions["test"] = "extended"
-
- testCases := map[string]struct {
- ctx ce.EventContextV01
- want []string
- }{
- "min valid": {
- ctx: ce.EventContextV01{
- CloudEventsVersion: ce.CloudEventsVersionV01,
- EventID: "ABC-123",
- EventType: "com.example.simple",
- Source: *source,
- },
- },
- "full valid": {
- ctx: ce.EventContextV01{
- CloudEventsVersion: ce.CloudEventsVersionV01,
- EventID: "ABC-123",
- EventTime: &now,
- EventType: "com.example.simple",
- EventTypeVersion: strptr("v1alpha1"),
- SchemaURL: schema,
- ContentType: ce.StringOfApplicationJSON(),
- Source: *source,
- Extensions: extensions,
- },
- },
- "no eventType": {
- ctx: ce.EventContextV01{
- CloudEventsVersion: ce.CloudEventsVersionV01,
- EventID: "ABC-123",
- Source: *source,
- },
- want: []string{"eventType:"},
- },
- "non-empty cloudEventsVersion": {
- ctx: ce.EventContextV01{
- CloudEventsVersion: "",
- EventID: "ABC-123",
- EventType: "com.example.simple",
- EventTypeVersion: strptr("v1alpha1"),
- Source: *source,
- },
- want: []string{"cloudEventsVersion:"},
- },
- "non-empty eventTypeVersion": {
- ctx: ce.EventContextV01{
- CloudEventsVersion: ce.CloudEventsVersionV01,
- EventID: "ABC-123",
- EventType: "com.example.simple",
- EventTypeVersion: strptr(""),
- Source: *source,
- },
- want: []string{"eventTypeVersion:"},
- },
- "missing source": {
- ctx: ce.EventContextV01{
- CloudEventsVersion: ce.CloudEventsVersionV01,
- EventID: "ABC-123",
- EventType: "com.example.simple",
- },
- want: []string{"source:"},
- },
- "non-empty eventID": {
- ctx: ce.EventContextV01{
- CloudEventsVersion: ce.CloudEventsVersionV01,
- EventID: "",
- EventType: "com.example.simple",
- Source: *source,
- },
- want: []string{"eventID:"},
- },
- "empty schemaURL": {
- ctx: ce.EventContextV01{
- CloudEventsVersion: ce.CloudEventsVersionV01,
- EventID: "ABC-123",
- EventType: "com.example.simple",
- SchemaURL: &types.URLRef{},
- Source: *source,
- },
- want: []string{"schemaURL:"},
- },
- "non-empty contentType": {
- ctx: ce.EventContextV01{
- CloudEventsVersion: ce.CloudEventsVersionV01,
- EventID: "ABC-123",
- EventType: "com.example.simple",
- Source: *source,
- ContentType: strptr(""),
- },
- want: []string{"contentType:"},
- },
- "empty extensions": {
- ctx: ce.EventContextV01{
- CloudEventsVersion: ce.CloudEventsVersionV01,
- EventID: "ABC-123",
- EventType: "com.example.simple",
- Source: *source,
- Extensions: make(map[string]interface{}),
- },
- want: []string{"extensions:"},
- },
- "all errors": {
- ctx: ce.EventContextV01{
- CloudEventsVersion: "",
- EventID: "",
- SchemaURL: &types.URLRef{},
- ContentType: strptr(""),
- Extensions: make(map[string]interface{}),
- },
- want: []string{
- "eventType:",
- "eventID:",
- "extensions:",
- "cloudEventsVersion:",
- "source:",
- "contentType:",
- },
- },
- }
- for n, tc := range testCases {
- t.Run(n, func(t *testing.T) {
-
- got := tc.ctx.Validate()
- var gotErr string
- if got != nil {
- gotErr = got.Error()
-
- if len(tc.want) == 0 {
- t.Errorf("unexpected no error, got %q", gotErr)
- }
- }
-
- for _, want := range tc.want {
- if !strings.Contains(gotErr, want) {
- t.Errorf("unexpected error, expected to contain %q, got: %q ", want, gotErr)
- }
- }
- })
- }
-}
-
-func TestGetMediaTypeV01(t *testing.T) {
- testCases := map[string]struct {
- t string
- want string
- }{
- "nil": {
- want: "",
- },
- "just encoding": {
- t: "charset=utf-8",
- want: "",
- },
- "text/html with encoding": {
- t: "text/html; charset=utf-8",
- want: "text/html",
- },
- "application/json with encoding": {
- t: "application/json; charset=utf-8",
- want: "application/json",
- },
- "application/json": {
- t: "application/json",
- want: "application/json",
- },
- }
- for n, tc := range testCases {
- t.Run(n, func(t *testing.T) {
-
- ec := ce.EventContextV01{}
- if tc.t != "" {
- ec.ContentType = &tc.t
- }
- got, _ := ec.GetDataMediaType()
-
- if diff := cmp.Diff(tc.want, got); diff != "" {
- t.Errorf("unexpected (-want, +got) = %v", diff)
- }
- })
- }
-}
diff --git a/v1/cloudevents/eventcontext_v01_writer.go b/v1/cloudevents/eventcontext_v01_writer.go
deleted file mode 100644
index f8e5888ab..000000000
--- a/v1/cloudevents/eventcontext_v01_writer.go
+++ /dev/null
@@ -1,104 +0,0 @@
-package cloudevents
-
-import (
- "errors"
- "fmt"
- "net/url"
- "strings"
- "time"
-
- "github.com/cloudevents/sdk-go/v1/cloudevents/types"
-)
-
-// Adhere to EventContextWriter
-var _ EventContextWriter = (*EventContextV01)(nil)
-
-// SetSpecVersion implements EventContextWriter.SetSpecVersion
-func (ec *EventContextV01) SetSpecVersion(v string) error {
- if v != CloudEventsVersionV01 {
- return fmt.Errorf("invalid version %q, expecting %q", v, CloudEventsVersionV01)
- }
- ec.CloudEventsVersion = CloudEventsVersionV01
- return nil
-}
-
-// SetDataContentType implements EventContextWriter.SetDataContentType
-func (ec *EventContextV01) SetDataContentType(ct string) error {
- ct = strings.TrimSpace(ct)
- if ct == "" {
- ec.ContentType = nil
- } else {
- ec.ContentType = &ct
- }
- return nil
-}
-
-// SetType implements EventContextWriter.SetType
-func (ec *EventContextV01) SetType(t string) error {
- t = strings.TrimSpace(t)
- ec.EventType = t
- return nil
-}
-
-// SetSource implements EventContextWriter.SetSource
-func (ec *EventContextV01) SetSource(u string) error {
- pu, err := url.Parse(u)
- if err != nil {
- return err
- }
- ec.Source = types.URLRef{URL: *pu}
- return nil
-}
-
-// SetSubject implements EventContextWriter.SetSubject
-func (ec *EventContextV01) SetSubject(s string) error {
- s = strings.TrimSpace(s)
- if s == "" {
- return ec.SetExtension(SubjectKey, nil)
- }
- return ec.SetExtension(SubjectKey, s)
-}
-
-// SetID implements EventContextWriter.SetID
-func (ec *EventContextV01) SetID(id string) error {
- id = strings.TrimSpace(id)
- if id == "" {
- return errors.New("event id is required to be a non-empty string")
- }
- ec.EventID = id
- return nil
-}
-
-// SetTime implements EventContextWriter.SetTime
-func (ec *EventContextV01) SetTime(t time.Time) error {
- if t.IsZero() {
- ec.EventTime = nil
- } else {
- ec.EventTime = &types.Timestamp{Time: t}
- }
- return nil
-}
-
-// SetDataSchema implements EventContextWriter.SetDataSchema
-func (ec *EventContextV01) SetDataSchema(u string) error {
- u = strings.TrimSpace(u)
- if u == "" {
- ec.SchemaURL = nil
- return nil
- }
- pu, err := url.Parse(u)
- if err != nil {
- return err
- }
- ec.SchemaURL = &types.URLRef{URL: *pu}
- return nil
-}
-
-// DeprecatedSetDataContentEncoding implements EventContextWriter.DeprecatedSetDataContentEncoding
-func (ec *EventContextV01) DeprecatedSetDataContentEncoding(e string) error {
- e = strings.ToLower(strings.TrimSpace(e))
- if e == "" {
- return ec.SetExtension(DataContentEncodingKey, nil)
- }
- return ec.SetExtension(DataContentEncodingKey, e)
-}
diff --git a/v1/cloudevents/eventcontext_v02.go b/v1/cloudevents/eventcontext_v02.go
deleted file mode 100644
index f4401047f..000000000
--- a/v1/cloudevents/eventcontext_v02.go
+++ /dev/null
@@ -1,305 +0,0 @@
-package cloudevents
-
-import (
- "encoding/json"
- "fmt"
- "sort"
- "strings"
-
- "github.com/cloudevents/sdk-go/v1/cloudevents/types"
-)
-
-const (
- // CloudEventsVersionV02 represents the version 0.2 of the CloudEvents spec.
- CloudEventsVersionV02 = "0.2"
-)
-
-// EventContextV02 represents the non-data attributes of a CloudEvents v0.2
-// event.
-type EventContextV02 struct {
- // The version of the CloudEvents specification used by the event.
- SpecVersion string `json:"specversion"`
- // The type of the occurrence which has happened.
- Type string `json:"type"`
- // 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"`
- // Timestamp when the event happened.
- Time *types.Timestamp `json:"time,omitempty"`
- // A link to the schema that the `data` attribute adheres to.
- SchemaURL *types.URLRef `json:"schemaurl,omitempty"`
- // 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?
- ContentType *string `json:"contenttype,omitempty"`
- // Additional extension metadata beyond the base spec.
- Extensions map[string]interface{} `json:"-"`
-}
-
-// Adhere to EventContext
-var _ EventContext = (*EventContextV02)(nil)
-
-// ExtensionAs implements EventContext.ExtensionAs
-func (ec EventContextV02) ExtensionAs(name string, obj interface{}) error {
- value, ok := ec.Extensions[name]
- if !ok {
- return fmt.Errorf("extension %q does not exist", name)
- }
-
- // Try to unmarshal extension if we find it as a RawMessage.
- switch v := value.(type) {
- case json.RawMessage:
- if err := json.Unmarshal(v, obj); err == nil {
- // if that worked, return with obj set.
- return nil
- }
- }
- // else try as a string ptr.
-
- // Only support *string for now.
- switch v := obj.(type) {
- case *string:
- if valueAsString, ok := value.(string); ok {
- *v = valueAsString
- return nil
- } else {
- return fmt.Errorf("invalid type for extension %q", name)
- }
- default:
- return fmt.Errorf("unknown extension type %T", obj)
- }
-}
-
-// SetExtension adds the extension 'name' with value 'value' to the CloudEvents context.
-func (ec *EventContextV02) SetExtension(name string, value interface{}) error {
- if ec.Extensions == nil {
- ec.Extensions = make(map[string]interface{})
- }
- if value == nil {
- delete(ec.Extensions, name)
- } else {
- ec.Extensions[name] = value
- }
- return nil
-}
-
-// Clone implements EventContextConverter.Clone
-func (ec EventContextV02) Clone() EventContext {
- ec02 := ec.AsV02()
- ec02.Extensions = ec02.cloneExtensions()
- return ec02
-}
-
-func (ec *EventContextV02) cloneExtensions() map[string]interface{} {
- old := ec.Extensions
- if old == nil {
- return nil
- }
- new := make(map[string]interface{}, len(ec.Extensions))
- for k, v := range old {
- new[k] = v
- }
- return new
-}
-
-// AsV01 implements EventContextConverter.AsV01
-func (ec EventContextV02) AsV01() *EventContextV01 {
- ret := EventContextV01{
- CloudEventsVersion: CloudEventsVersionV01,
- EventID: ec.ID,
- EventTime: ec.Time,
- EventType: ec.Type,
- SchemaURL: ec.SchemaURL,
- Source: ec.Source,
- ContentType: ec.ContentType,
- Extensions: make(map[string]interface{}),
- }
-
- for k, v := range ec.Extensions {
- // eventTypeVersion was retired in v0.2
- if strings.EqualFold(k, EventTypeVersionKey) {
- etv, ok := v.(string)
- if ok && etv != "" {
- ret.EventTypeVersion = &etv
- }
- continue
- }
- ret.Extensions[k] = v
- }
- if len(ret.Extensions) == 0 {
- ret.Extensions = nil
- }
- return &ret
-}
-
-// AsV02 implements EventContextConverter.AsV02
-func (ec EventContextV02) AsV02() *EventContextV02 {
- ec.SpecVersion = CloudEventsVersionV02
- return &ec
-}
-
-// AsV03 implements EventContextConverter.AsV03
-func (ec EventContextV02) AsV03() *EventContextV03 {
- ret := EventContextV03{
- SpecVersion: CloudEventsVersionV03,
- ID: ec.ID,
- Time: ec.Time,
- Type: ec.Type,
- SchemaURL: ec.SchemaURL,
- DataContentType: ec.ContentType,
- Source: ec.Source,
- Extensions: make(map[string]interface{}),
- }
-
- for k, v := range ec.Extensions {
- // Subject was introduced in 0.3
- if strings.EqualFold(k, SubjectKey) {
- sub, ok := v.(string)
- if ok && sub != "" {
- ret.Subject = &sub
- }
- continue
- }
- // DeprecatedDataContentEncoding was introduced in 0.3
- if strings.EqualFold(k, DataContentEncodingKey) {
- etv, ok := v.(string)
- if ok && etv != "" {
- ret.DataContentEncoding = &etv
- }
- continue
- }
- ret.Extensions[k] = v
- }
- if len(ret.Extensions) == 0 {
- ret.Extensions = nil
- }
-
- return &ret
-}
-
-// AsV1 implements EventContextConverter.AsV1
-func (ec EventContextV02) AsV1() *EventContextV1 {
- return ec.AsV03().AsV1()
-}
-
-// Validate returns errors based on requirements from the CloudEvents spec.
-// For more details, see https://github.com/cloudevents/spec/blob/v0.2/spec.md
-func (ec EventContextV02) Validate() error {
- errors := []string(nil)
-
- // type
- // Type: String
- // Constraints:
- // REQUIRED
- // MUST be a non-empty string
- // SHOULD be prefixed with a reverse-DNS name. The prefixed domain dictates the organization which defines the semantics of this event type.
- eventType := strings.TrimSpace(ec.Type)
- if eventType == "" {
- errors = append(errors, "type: MUST be a non-empty string")
- }
-
- // specversion
- // Type: String
- // Constraints:
- // REQUIRED
- // MUST be a non-empty string
- specVersion := strings.TrimSpace(ec.SpecVersion)
- if specVersion == "" {
- errors = append(errors, "specversion: MUST be a non-empty string")
- }
-
- // source
- // Type: URI-reference
- // Constraints:
- // REQUIRED
- source := strings.TrimSpace(ec.Source.String())
- if source == "" {
- errors = append(errors, "source: REQUIRED")
- }
-
- // id
- // Type: String
- // Constraints:
- // REQUIRED
- // MUST be a non-empty string
- // MUST be unique within the scope of the producer
- id := strings.TrimSpace(ec.ID)
- if id == "" {
- errors = append(errors, "id: MUST be a non-empty string")
-
- // no way to test "MUST be unique within the scope of the producer"
- }
-
- // time
- // Type: Timestamp
- // Constraints:
- // OPTIONAL
- // If present, MUST adhere to the format specified in RFC 3339
- // --> no need to test this, no way to set the time without it being valid.
-
- // schemaurl
- // Type: URI
- // Constraints:
- // OPTIONAL
- // If present, MUST adhere to the format specified in RFC 3986
- if ec.SchemaURL != nil {
- schemaURL := strings.TrimSpace(ec.SchemaURL.String())
- // empty string is not RFC 3986 compatible.
- if schemaURL == "" {
- errors = append(errors, "schemaurl: if present, MUST adhere to the format specified in RFC 3986")
- }
- }
-
- // contenttype
- // Type: String per RFC 2046
- // Constraints:
- // OPTIONAL
- // If present, MUST adhere to the format specified in RFC 2046
- if ec.ContentType != nil {
- contentType := strings.TrimSpace(*ec.ContentType)
- if contentType == "" {
- // TODO: need to test for RFC 2046
- errors = append(errors, "contenttype: if present, MUST adhere to the format specified in RFC 2046")
- }
- }
-
- if len(errors) > 0 {
- return fmt.Errorf(strings.Join(errors, "\n"))
- }
- return nil
-}
-
-// String returns a pretty-printed representation of the EventContext.
-func (ec EventContextV02) String() string {
- b := strings.Builder{}
-
- b.WriteString("Context Attributes,\n")
-
- b.WriteString(" specversion: " + ec.SpecVersion + "\n")
- b.WriteString(" type: " + ec.Type + "\n")
- b.WriteString(" source: " + ec.Source.String() + "\n")
- b.WriteString(" id: " + ec.ID + "\n")
- if ec.Time != nil {
- b.WriteString(" time: " + ec.Time.String() + "\n")
- }
- if ec.SchemaURL != nil {
- b.WriteString(" schemaurl: " + ec.SchemaURL.String() + "\n")
- }
- if ec.ContentType != nil {
- b.WriteString(" contenttype: " + *ec.ContentType + "\n")
- }
-
- if ec.Extensions != nil && len(ec.Extensions) > 0 {
- b.WriteString("Extensions,\n")
- keys := make([]string, 0, len(ec.Extensions))
- for k := range ec.Extensions {
- keys = append(keys, k)
- }
- sort.Strings(keys)
- for _, key := range keys {
- b.WriteString(fmt.Sprintf(" %s: %v\n", key, ec.Extensions[key]))
- }
- }
-
- return b.String()
-}
diff --git a/v1/cloudevents/eventcontext_v02_reader.go b/v1/cloudevents/eventcontext_v02_reader.go
deleted file mode 100644
index 120cdb87e..000000000
--- a/v1/cloudevents/eventcontext_v02_reader.go
+++ /dev/null
@@ -1,101 +0,0 @@
-package cloudevents
-
-import (
- "fmt"
- "mime"
- "time"
-)
-
-// Adhere to EventContextReader
-var _ EventContextReader = (*EventContextV02)(nil)
-
-// GetSpecVersion implements EventContextReader.GetSpecVersion
-func (ec EventContextV02) GetSpecVersion() string {
- if ec.SpecVersion != "" {
- return ec.SpecVersion
- }
- return CloudEventsVersionV02
-}
-
-// GetType implements EventContextReader.GetType
-func (ec EventContextV02) GetType() string {
- return ec.Type
-}
-
-// GetSource implements EventContextReader.GetSource
-func (ec EventContextV02) GetSource() string {
- return ec.Source.String()
-}
-
-// GetSubject implements EventContextReader.GetSubject
-func (ec EventContextV02) GetSubject() string {
- var sub string
- if err := ec.ExtensionAs(SubjectKey, &sub); err != nil {
- return ""
- }
- return sub
-}
-
-// GetID implements EventContextReader.GetID
-func (ec EventContextV02) GetID() string {
- return ec.ID
-}
-
-// GetTime implements EventContextReader.GetTime
-func (ec EventContextV02) GetTime() time.Time {
- if ec.Time != nil {
- return ec.Time.Time
- }
- return time.Time{}
-}
-
-// GetDataSchema implements EventContextReader.GetDataSchema
-func (ec EventContextV02) GetDataSchema() string {
- if ec.SchemaURL != nil {
- return ec.SchemaURL.String()
- }
- return ""
-}
-
-// GetDataContentType implements EventContextReader.GetDataContentType
-func (ec EventContextV02) GetDataContentType() string {
- if ec.ContentType != nil {
- return *ec.ContentType
- }
- return ""
-}
-
-// GetDataMediaType implements EventContextReader.GetDataMediaType
-func (ec EventContextV02) GetDataMediaType() (string, error) {
- if ec.ContentType != nil {
- mediaType, _, err := mime.ParseMediaType(*ec.ContentType)
- if err != nil {
- return "", err
- }
- return mediaType, nil
- }
- return "", nil
-}
-
-// DeprecatedGetDataContentEncoding implements EventContextReader.DeprecatedGetDataContentEncoding
-func (ec EventContextV02) DeprecatedGetDataContentEncoding() string {
- var enc string
- if err := ec.ExtensionAs(DataContentEncodingKey, &enc); err != nil {
- return ""
- }
- return enc
-}
-
-// GetExtensions implements EventContextReader.GetExtensions
-func (ec EventContextV02) GetExtensions() map[string]interface{} {
- return ec.Extensions
-}
-
-// GetExtension implements EventContextReader.GetExtension
-func (ec EventContextV02) GetExtension(key string) (interface{}, error) {
- v, ok := caseInsensitiveSearch(key, ec.Extensions)
- if !ok {
- return "", fmt.Errorf("%q not found", key)
- }
- return v, nil
-}
diff --git a/v1/cloudevents/eventcontext_v02_test.go b/v1/cloudevents/eventcontext_v02_test.go
deleted file mode 100644
index 78aff7be6..000000000
--- a/v1/cloudevents/eventcontext_v02_test.go
+++ /dev/null
@@ -1,192 +0,0 @@
-package cloudevents_test
-
-import (
- "net/url"
- "strings"
- "testing"
- "time"
-
- ce "github.com/cloudevents/sdk-go/v1/cloudevents"
- "github.com/cloudevents/sdk-go/v1/cloudevents/types"
- "github.com/google/go-cmp/cmp"
-)
-
-func TestValidateV02(t *testing.T) {
- now := types.Timestamp{Time: time.Now()}
-
- sourceUrl, _ := url.Parse("http://example.com/source")
- source := &types.URLRef{URL: *sourceUrl}
-
- schemaUrl, _ := url.Parse("http://example.com/schema")
- schema := &types.URLRef{URL: *schemaUrl}
-
- extensions := make(map[string]interface{})
- extensions["test"] = "extended"
-
- testCases := map[string]struct {
- ctx ce.EventContextV02
- want []string
- }{
- "min valid": {
- ctx: ce.EventContextV02{
- SpecVersion: ce.CloudEventsVersionV02,
- ID: "ABC-123",
- Type: "com.example.simple",
- Source: *source,
- },
- },
- "full valid": {
- ctx: ce.EventContextV02{
- SpecVersion: ce.CloudEventsVersionV02,
- ID: "ABC-123",
- Time: &now,
- Type: "com.example.simple",
- SchemaURL: schema,
- ContentType: ce.StringOfApplicationJSON(),
- Source: *source,
- Extensions: extensions,
- },
- },
- "no Type": {
- ctx: ce.EventContextV02{
- SpecVersion: ce.CloudEventsVersionV02,
- ID: "ABC-123",
- Source: *source,
- },
- want: []string{"type:"},
- },
- "non-empty SpecVersion": {
- ctx: ce.EventContextV02{
- SpecVersion: "",
- ID: "ABC-123",
- Type: "com.example.simple",
- Source: *source,
- },
- want: []string{"specversion:"},
- },
- "missing source": {
- ctx: ce.EventContextV02{
- SpecVersion: ce.CloudEventsVersionV02,
- ID: "ABC-123",
- Type: "com.example.simple",
- },
- want: []string{"source:"},
- },
- "non-empty ID": {
- ctx: ce.EventContextV02{
- SpecVersion: ce.CloudEventsVersionV02,
- ID: "",
- Type: "com.example.simple",
- Source: *source,
- },
- want: []string{"id:"},
- },
- "empty schemaURL": {
- ctx: ce.EventContextV02{
- SpecVersion: ce.CloudEventsVersionV02,
- ID: "ABC-123",
- Type: "com.example.simple",
- SchemaURL: &types.URLRef{},
- Source: *source,
- },
- want: []string{"schemaurl:"},
- },
- "non-empty contentType": {
- ctx: ce.EventContextV02{
- SpecVersion: ce.CloudEventsVersionV02,
- ID: "ABC-123",
- Type: "com.example.simple",
- Source: *source,
- ContentType: strptr(""),
- },
- want: []string{"contenttype:"},
- },
- //"empty extensions": {
- // ctx: ce.EventContextV02{
- // SpecVersion: ce.CloudEventsVersionV02,
- // ID: "ABC-123",
- // Type: "com.example.simple",
- // Source: *source,
- // Extensions: make(map[string]interface{}),
- // },
- // want: []string{"extensions:"},
- //},
- "all errors": {
- ctx: ce.EventContextV02{
- SpecVersion: "",
- ID: "",
- SchemaURL: &types.URLRef{},
- ContentType: strptr(""),
- Extensions: make(map[string]interface{}),
- },
- want: []string{
- "type:",
- "id:",
- "specversion:",
- "source:",
- "contenttype:",
- },
- },
- }
- for n, tc := range testCases {
- t.Run(n, func(t *testing.T) {
-
- got := tc.ctx.Validate()
- var gotErr string
- if got != nil {
- gotErr = got.Error()
-
- if len(tc.want) == 0 {
- t.Errorf("unexpected no error, got %q", gotErr)
- }
- }
-
- for _, want := range tc.want {
- if !strings.Contains(gotErr, want) {
- t.Errorf("unexpected error, expected to contain %q, got: %q ", want, gotErr)
- }
- }
- })
- }
-}
-
-func TestGetMediaTypeV02(t *testing.T) {
- testCases := map[string]struct {
- t string
- want string
- }{
- "nil": {
- want: "",
- },
- "just encoding": {
- t: "charset=utf-8",
- want: "",
- },
- "text/html with encoding": {
- t: "text/html; charset=utf-8",
- want: "text/html",
- },
- "application/json with encoding": {
- t: "application/json; charset=utf-8",
- want: "application/json",
- },
- "application/json": {
- t: "application/json",
- want: "application/json",
- },
- }
- for n, tc := range testCases {
- t.Run(n, func(t *testing.T) {
-
- ec := ce.EventContextV02{}
- if tc.t != "" {
- ec.ContentType = &tc.t
- }
- got, _ := ec.GetDataMediaType()
-
- if diff := cmp.Diff(tc.want, got); diff != "" {
- t.Errorf("unexpected (-want, +got) = %v", diff)
- }
- })
- }
-}
diff --git a/v1/cloudevents/eventcontext_v02_writer.go b/v1/cloudevents/eventcontext_v02_writer.go
deleted file mode 100644
index 733680aa2..000000000
--- a/v1/cloudevents/eventcontext_v02_writer.go
+++ /dev/null
@@ -1,104 +0,0 @@
-package cloudevents
-
-import (
- "errors"
- "fmt"
- "net/url"
- "strings"
- "time"
-
- "github.com/cloudevents/sdk-go/v1/cloudevents/types"
-)
-
-// Adhere to EventContextWriter
-var _ EventContextWriter = (*EventContextV02)(nil)
-
-// SetSpecVersion implements EventContextWriter.SetSpecVersion
-func (ec *EventContextV02) SetSpecVersion(v string) error {
- if v != CloudEventsVersionV02 {
- return fmt.Errorf("invalid version %q, expecting %q", v, CloudEventsVersionV02)
- }
- ec.SpecVersion = CloudEventsVersionV02
- return nil
-}
-
-// SetDataContentType implements EventContextWriter.SetDataContentType
-func (ec *EventContextV02) SetDataContentType(ct string) error {
- ct = strings.TrimSpace(ct)
- if ct == "" {
- ec.ContentType = nil
- } else {
- ec.ContentType = &ct
- }
- return nil
-}
-
-// SetType implements EventContextWriter.SetType
-func (ec *EventContextV02) SetType(t string) error {
- t = strings.TrimSpace(t)
- ec.Type = t
- return nil
-}
-
-// SetSource implements EventContextWriter.SetSource
-func (ec *EventContextV02) SetSource(u string) error {
- pu, err := url.Parse(u)
- if err != nil {
- return err
- }
- ec.Source = types.URLRef{URL: *pu}
- return nil
-}
-
-// SetSubject implements EventContextWriter.SetSubject
-func (ec *EventContextV02) SetSubject(s string) error {
- s = strings.TrimSpace(s)
- if s == "" {
- return ec.SetExtension(SubjectKey, nil)
- }
- return ec.SetExtension(SubjectKey, s)
-}
-
-// SetID implements EventContextWriter.SetID
-func (ec *EventContextV02) SetID(id string) error {
- id = strings.TrimSpace(id)
- if id == "" {
- return errors.New("id is required to be a non-empty string")
- }
- ec.ID = id
- return nil
-}
-
-// SetTime implements EventContextWriter.SetTime
-func (ec *EventContextV02) SetTime(t time.Time) error {
- if t.IsZero() {
- ec.Time = nil
- } else {
- ec.Time = &types.Timestamp{Time: t}
- }
- return nil
-}
-
-// SetDataSchema implements EventContextWriter.SetDataSchema
-func (ec *EventContextV02) SetDataSchema(u string) error {
- u = strings.TrimSpace(u)
- if u == "" {
- ec.SchemaURL = nil
- return nil
- }
- pu, err := url.Parse(u)
- if err != nil {
- return err
- }
- ec.SchemaURL = &types.URLRef{URL: *pu}
- return nil
-}
-
-// DeprecatedSetDataContentEncoding implements EventContextWriter.DeprecatedSetDataContentEncoding
-func (ec *EventContextV02) DeprecatedSetDataContentEncoding(e string) error {
- e = strings.ToLower(strings.TrimSpace(e))
- if e == "" {
- return ec.SetExtension(DataContentEncodingKey, nil)
- }
- return ec.SetExtension(DataContentEncodingKey, e)
-}
diff --git a/v1/cloudevents/eventcontext_v03.go b/v1/cloudevents/eventcontext_v03.go
deleted file mode 100644
index 0b855ff71..000000000
--- a/v1/cloudevents/eventcontext_v03.go
+++ /dev/null
@@ -1,345 +0,0 @@
-package cloudevents
-
-import (
- "encoding/json"
- "fmt"
- "sort"
- "strings"
-
- "github.com/cloudevents/sdk-go/v1/cloudevents/types"
-)
-
-const (
- // CloudEventsVersionV03 represents the version 0.3 of the CloudEvents spec.
- CloudEventsVersionV03 = "0.3"
-)
-
-// EventContextV03 represents the non-data attributes of a CloudEvents v0.3
-// event.
-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"`
- // Subject - The subject of the event in the context of the event producer
- // (identified by `source`).
- Subject *string `json:"subject,omitempty"`
- // 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"`
- // DataSchema - 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"`
- // DeprecatedDataContentEncoding describes the content encoding for the `data` attribute. Valid: nil, `Base64`.
- DataContentEncoding *string `json:"datacontentencoding,omitempty"`
- // Extensions - Additional extension metadata beyond the base spec.
- Extensions map[string]interface{} `json:"-"`
-}
-
-// Adhere to EventContext
-var _ EventContext = (*EventContextV03)(nil)
-
-// ExtensionAs implements EventContext.ExtensionAs
-func (ec EventContextV03) ExtensionAs(name string, obj interface{}) error {
- value, ok := ec.Extensions[name]
- if !ok {
- return fmt.Errorf("extension %q does not exist", name)
- }
-
- // Try to unmarshal extension if we find it as a RawMessage.
- switch v := value.(type) {
- case json.RawMessage:
- if err := json.Unmarshal(v, obj); err == nil {
- // if that worked, return with obj set.
- return nil
- }
- }
- // else try as a string ptr.
-
- // Only support *string for now.
- switch v := obj.(type) {
- case *string:
- if valueAsString, ok := value.(string); ok {
- *v = valueAsString
- return nil
- } else {
- return fmt.Errorf("invalid type for extension %q", name)
- }
- default:
- return fmt.Errorf("unknown extension type %T", obj)
- }
-}
-
-// SetExtension adds the extension 'name' with value 'value' to the CloudEvents context.
-func (ec *EventContextV03) SetExtension(name string, value interface{}) error {
- if ec.Extensions == nil {
- ec.Extensions = make(map[string]interface{})
- }
- if value == nil {
- delete(ec.Extensions, name)
- } else {
- v, err := types.Validate(value)
- if err == nil {
- ec.Extensions[name] = v
- }
- return err
- }
- return nil
-}
-
-// Clone implements EventContextConverter.Clone
-func (ec EventContextV03) Clone() EventContext {
- ec03 := ec.AsV03()
- ec03.Extensions = ec03.cloneExtensions()
- return ec03
-}
-
-func (ec *EventContextV03) cloneExtensions() map[string]interface{} {
- old := ec.Extensions
- if old == nil {
- return nil
- }
- new := make(map[string]interface{}, len(ec.Extensions))
- for k, v := range old {
- new[k] = v
- }
- return new
-}
-
-// AsV01 implements EventContextConverter.AsV01
-func (ec EventContextV03) AsV01() *EventContextV01 {
- ecv2 := ec.AsV02()
- return ecv2.AsV01()
-}
-
-// AsV02 implements EventContextConverter.AsV02
-func (ec EventContextV03) AsV02() *EventContextV02 {
- ret := EventContextV02{
- SpecVersion: CloudEventsVersionV02,
- ID: ec.ID,
- Time: ec.Time,
- Type: ec.Type,
- SchemaURL: ec.SchemaURL,
- ContentType: ec.DataContentType,
- Source: ec.Source,
- Extensions: make(map[string]interface{}),
- }
- // Subject was introduced in 0.3, so put it in an extension for 0.2.
- if ec.Subject != nil {
- _ = ret.SetExtension(SubjectKey, *ec.Subject)
- }
- // DeprecatedDataContentEncoding was introduced in 0.3, so put it in an extension for 0.2.
- if ec.DataContentEncoding != nil {
- _ = ret.SetExtension(DataContentEncodingKey, *ec.DataContentEncoding)
- }
- if ec.Extensions != nil {
- for k, v := range ec.Extensions {
- ret.Extensions[k] = v
- }
- }
- if len(ret.Extensions) == 0 {
- ret.Extensions = nil
- }
- return &ret
-}
-
-// AsV03 implements EventContextConverter.AsV03
-func (ec EventContextV03) AsV03() *EventContextV03 {
- ec.SpecVersion = CloudEventsVersionV03
- return &ec
-}
-
-// AsV04 implements EventContextConverter.AsV04
-func (ec EventContextV03) AsV1() *EventContextV1 {
- ret := EventContextV1{
- SpecVersion: CloudEventsVersionV1,
- ID: ec.ID,
- Time: ec.Time,
- Type: ec.Type,
- DataContentType: ec.DataContentType,
- Source: types.URIRef{URL: ec.Source.URL},
- Subject: ec.Subject,
- Extensions: make(map[string]interface{}),
- }
- if ec.SchemaURL != nil {
- ret.DataSchema = &types.URI{URL: ec.SchemaURL.URL}
- }
-
- // DataContentEncoding was removed in 1.0, so put it in an extension for 1.0.
- if ec.DataContentEncoding != nil {
- _ = ret.SetExtension(DataContentEncodingKey, *ec.DataContentEncoding)
- }
-
- if ec.Extensions != nil {
- for k, v := range ec.Extensions {
- k = strings.ToLower(k)
- ret.Extensions[k] = v
- }
- }
- if len(ret.Extensions) == 0 {
- ret.Extensions = nil
- }
- return &ret
-}
-
-// Validate returns errors based on requirements from the CloudEvents spec.
-// For more details, see https://github.com/cloudevents/spec/blob/master/spec.md
-// As of Feb 26, 2019, commit 17c32ea26baf7714ad027d9917d03d2fff79fc7e
-// + https://github.com/cloudevents/spec/pull/387 -> datacontentencoding
-// + https://github.com/cloudevents/spec/pull/406 -> subject
-func (ec EventContextV03) Validate() error {
- errors := []string(nil)
-
- // type
- // Type: String
- // Constraints:
- // REQUIRED
- // MUST be a non-empty string
- // SHOULD be prefixed with a reverse-DNS name. The prefixed domain dictates the organization which defines the semantics of this event type.
- eventType := strings.TrimSpace(ec.Type)
- if eventType == "" {
- errors = append(errors, "type: MUST be a non-empty string")
- }
-
- // specversion
- // Type: String
- // Constraints:
- // REQUIRED
- // MUST be a non-empty string
- specVersion := strings.TrimSpace(ec.SpecVersion)
- if specVersion == "" {
- errors = append(errors, "specversion: MUST be a non-empty string")
- }
-
- // source
- // Type: URI-reference
- // Constraints:
- // REQUIRED
- source := strings.TrimSpace(ec.Source.String())
- if source == "" {
- errors = append(errors, "source: REQUIRED")
- }
-
- // subject
- // Type: String
- // Constraints:
- // OPTIONAL
- // MUST be a non-empty string
- if ec.Subject != nil {
- subject := strings.TrimSpace(*ec.Subject)
- if subject == "" {
- errors = append(errors, "subject: if present, MUST be a non-empty string")
- }
- }
-
- // id
- // Type: String
- // Constraints:
- // REQUIRED
- // MUST be a non-empty string
- // MUST be unique within the scope of the producer
- id := strings.TrimSpace(ec.ID)
- if id == "" {
- errors = append(errors, "id: MUST be a non-empty string")
-
- // no way to test "MUST be unique within the scope of the producer"
- }
-
- // time
- // Type: Timestamp
- // Constraints:
- // OPTIONAL
- // If present, MUST adhere to the format specified in RFC 3339
- // --> no need to test this, no way to set the time without it being valid.
-
- // schemaurl
- // Type: URI
- // Constraints:
- // OPTIONAL
- // If present, MUST adhere to the format specified in RFC 3986
- if ec.SchemaURL != nil {
- schemaURL := strings.TrimSpace(ec.SchemaURL.String())
- // empty string is not RFC 3986 compatible.
- if schemaURL == "" {
- errors = append(errors, "schemaurl: if present, MUST adhere to the format specified in RFC 3986")
- }
- }
-
- // datacontenttype
- // Type: String per RFC 2046
- // Constraints:
- // OPTIONAL
- // If present, MUST adhere to the format specified in RFC 2046
- if ec.DataContentType != nil {
- dataContentType := strings.TrimSpace(*ec.DataContentType)
- if dataContentType == "" {
- // TODO: need to test for RFC 2046
- errors = append(errors, "datacontenttype: if present, MUST adhere to the format specified in RFC 2046")
- }
- }
-
- // datacontentencoding
- // Type: String per RFC 2045 Section 6.1
- // Constraints:
- // The attribute MUST be set if the data attribute contains string-encoded binary data.
- // Otherwise the attribute MUST NOT be set.
- // If present, MUST adhere to RFC 2045 Section 6.1
- if ec.DataContentEncoding != nil {
- dataContentEncoding := strings.ToLower(strings.TrimSpace(*ec.DataContentEncoding))
- if dataContentEncoding != Base64 {
- // TODO: need to test for RFC 2046
- errors = append(errors, "datacontentencoding: if present, MUST adhere to RFC 2045 Section 6.1")
- }
- }
-
- if len(errors) > 0 {
- return fmt.Errorf(strings.Join(errors, "\n"))
- }
- return nil
-}
-
-// String returns a pretty-printed representation of the EventContext.
-func (ec EventContextV03) String() string {
- b := strings.Builder{}
-
- b.WriteString("Context Attributes,\n")
-
- b.WriteString(" specversion: " + ec.SpecVersion + "\n")
- b.WriteString(" type: " + ec.Type + "\n")
- b.WriteString(" source: " + ec.Source.String() + "\n")
- if ec.Subject != nil {
- b.WriteString(" subject: " + *ec.Subject + "\n")
- }
- b.WriteString(" id: " + ec.ID + "\n")
- if ec.Time != nil {
- b.WriteString(" time: " + ec.Time.String() + "\n")
- }
- if ec.SchemaURL != nil {
- b.WriteString(" schemaurl: " + ec.SchemaURL.String() + "\n")
- }
- if ec.DataContentType != nil {
- b.WriteString(" datacontenttype: " + *ec.DataContentType + "\n")
- }
- if ec.DataContentEncoding != nil {
- b.WriteString(" datacontentencoding: " + *ec.DataContentEncoding + "\n")
- }
-
- if ec.Extensions != nil && len(ec.Extensions) > 0 {
- b.WriteString("Extensions,\n")
- keys := make([]string, 0, len(ec.Extensions))
- for k := range ec.Extensions {
- keys = append(keys, k)
- }
- sort.Strings(keys)
- for _, key := range keys {
- b.WriteString(fmt.Sprintf(" %s: %v\n", key, ec.Extensions[key]))
- }
- }
-
- return b.String()
-}
diff --git a/v1/cloudevents/eventcontext_v03_reader.go b/v1/cloudevents/eventcontext_v03_reader.go
deleted file mode 100644
index 2b3cc207f..000000000
--- a/v1/cloudevents/eventcontext_v03_reader.go
+++ /dev/null
@@ -1,96 +0,0 @@
-package cloudevents
-
-import (
- "fmt"
- "mime"
- "time"
-)
-
-// GetSpecVersion implements EventContextReader.GetSpecVersion
-func (ec EventContextV03) GetSpecVersion() string {
- if ec.SpecVersion != "" {
- return ec.SpecVersion
- }
- return CloudEventsVersionV03
-}
-
-// GetDataContentType implements EventContextReader.GetDataContentType
-func (ec EventContextV03) GetDataContentType() string {
- if ec.DataContentType != nil {
- return *ec.DataContentType
- }
- return ""
-}
-
-// GetDataMediaType implements EventContextReader.GetDataMediaType
-func (ec EventContextV03) GetDataMediaType() (string, error) {
- if ec.DataContentType != nil {
- mediaType, _, err := mime.ParseMediaType(*ec.DataContentType)
- if err != nil {
- return "", err
- }
- return mediaType, nil
- }
- return "", nil
-}
-
-// GetType implements EventContextReader.GetType
-func (ec EventContextV03) GetType() string {
- return ec.Type
-}
-
-// GetSource implements EventContextReader.GetSource
-func (ec EventContextV03) GetSource() string {
- return ec.Source.String()
-}
-
-// GetSubject implements EventContextReader.GetSubject
-func (ec EventContextV03) GetSubject() string {
- if ec.Subject != nil {
- return *ec.Subject
- }
- return ""
-}
-
-// GetTime implements EventContextReader.GetTime
-func (ec EventContextV03) GetTime() time.Time {
- if ec.Time != nil {
- return ec.Time.Time
- }
- return time.Time{}
-}
-
-// GetID implements EventContextReader.GetID
-func (ec EventContextV03) GetID() string {
- return ec.ID
-}
-
-// GetDataSchema implements EventContextReader.GetDataSchema
-func (ec EventContextV03) GetDataSchema() string {
- if ec.SchemaURL != nil {
- return ec.SchemaURL.String()
- }
- return ""
-}
-
-// DeprecatedGetDataContentEncoding implements EventContextReader.DeprecatedGetDataContentEncoding
-func (ec EventContextV03) DeprecatedGetDataContentEncoding() string {
- if ec.DataContentEncoding != nil {
- return *ec.DataContentEncoding
- }
- return ""
-}
-
-// GetExtensions implements EventContextReader.GetExtensions
-func (ec EventContextV03) GetExtensions() map[string]interface{} {
- return ec.Extensions
-}
-
-// GetExtension implements EventContextReader.GetExtension
-func (ec EventContextV03) GetExtension(key string) (interface{}, error) {
- v, ok := caseInsensitiveSearch(key, ec.Extensions)
- if !ok {
- return "", fmt.Errorf("%q not found", key)
- }
- return v, nil
-}
diff --git a/v1/cloudevents/eventcontext_v03_test.go b/v1/cloudevents/eventcontext_v03_test.go
deleted file mode 100644
index 344b82f5f..000000000
--- a/v1/cloudevents/eventcontext_v03_test.go
+++ /dev/null
@@ -1,227 +0,0 @@
-package cloudevents_test
-
-import (
- "net/url"
- "strings"
- "testing"
- "time"
-
- ce "github.com/cloudevents/sdk-go/v1/cloudevents"
- "github.com/cloudevents/sdk-go/v1/cloudevents/types"
- "github.com/google/go-cmp/cmp"
-)
-
-func TestValidateV03(t *testing.T) {
- now := types.Timestamp{Time: time.Now()}
-
- sourceUrl, _ := url.Parse("http://example.com/source")
- source := &types.URLRef{URL: *sourceUrl}
-
- subject := "a subject"
-
- schemaUrl, _ := url.Parse("http://example.com/schema")
- schema := &types.URLRef{URL: *schemaUrl}
-
- extensions := make(map[string]interface{})
- extensions["test"] = "extended"
-
- testCases := map[string]struct {
- ctx ce.EventContextV03
- want []string
- }{
- "min valid": {
- ctx: ce.EventContextV03{
- SpecVersion: ce.CloudEventsVersionV03,
- ID: "ABC-123",
- Type: "com.example.simple",
- Source: *source,
- },
- },
- "full valid": {
- ctx: ce.EventContextV03{
- SpecVersion: ce.CloudEventsVersionV03,
- ID: "ABC-123",
- Time: &now,
- Type: "com.example.simple",
- SchemaURL: schema,
- DataContentType: ce.StringOfApplicationJSON(),
- DataContentEncoding: ce.StringOfBase64(),
- Source: *source,
- Subject: &subject,
- Extensions: extensions,
- },
- },
- "no Type": {
- ctx: ce.EventContextV03{
- SpecVersion: ce.CloudEventsVersionV03,
- ID: "ABC-123",
- Source: *source,
- },
- want: []string{"type:"},
- },
- "non-empty SpecVersion": {
- ctx: ce.EventContextV03{
- SpecVersion: "",
- ID: "ABC-123",
- Type: "com.example.simple",
- Source: *source,
- },
- want: []string{"specversion:"},
- },
- "missing source": {
- ctx: ce.EventContextV03{
- SpecVersion: ce.CloudEventsVersionV03,
- ID: "ABC-123",
- Type: "com.example.simple",
- },
- want: []string{"source:"},
- },
- "non-empty subject": {
- ctx: ce.EventContextV03{
- SpecVersion: ce.CloudEventsVersionV03,
- ID: "",
- Type: "com.example.simple",
- Source: *source,
- Subject: strptr(" "),
- },
- want: []string{"subject:"},
- },
- "non-empty ID": {
- ctx: ce.EventContextV03{
- SpecVersion: ce.CloudEventsVersionV03,
- ID: "",
- Type: "com.example.simple",
- Source: *source,
- },
- want: []string{"id:"},
- },
- "empty schemaURL": {
- ctx: ce.EventContextV03{
- SpecVersion: ce.CloudEventsVersionV03,
- ID: "ABC-123",
- Type: "com.example.simple",
- SchemaURL: &types.URLRef{},
- Source: *source,
- },
- want: []string{"schemaurl:"},
- },
- "non-empty contentType": {
- ctx: ce.EventContextV03{
- SpecVersion: ce.CloudEventsVersionV03,
- ID: "ABC-123",
- Type: "com.example.simple",
- Source: *source,
- DataContentType: strptr(""),
- },
- want: []string{"datacontenttype:"},
- },
- "non-empty dataContentEncoding": {
- ctx: ce.EventContextV03{
- SpecVersion: ce.CloudEventsVersionV03,
- ID: "ABC-123",
- Type: "com.example.simple",
- Source: *source,
- DataContentEncoding: strptr(""),
- },
- want: []string{"datacontentencoding:"},
- },
- "invalid dataContentEncoding": {
- ctx: ce.EventContextV03{
- SpecVersion: ce.CloudEventsVersionV03,
- ID: "ABC-123",
- Type: "com.example.simple",
- Source: *source,
- DataContentEncoding: strptr("binary"),
- },
- want: []string{"datacontentencoding:"},
- },
-
- //"empty extensions": {
- // ctx: ce.EventContextV03{
- // SpecVersion: ce.CloudEventsVersionV03,
- // ID: "ABC-123",
- // Type: "com.example.simple",
- // Source: *source,
- // Extensions: make(map[string]interface{}),
- // },
- // want: []string{"extensions:"},
- //},
- "all errors": {
- ctx: ce.EventContextV03{
- SpecVersion: "",
- ID: "",
- SchemaURL: &types.URLRef{},
- DataContentType: strptr(""),
- Extensions: make(map[string]interface{}),
- },
- want: []string{
- "type:",
- "id:",
- "specversion:",
- "source:",
- "contenttype:",
- },
- },
- }
- for n, tc := range testCases {
- t.Run(n, func(t *testing.T) {
-
- got := tc.ctx.Validate()
- var gotErr string
- if got != nil {
- gotErr = got.Error()
-
- if len(tc.want) == 0 {
- t.Errorf("unexpected no error, got %q", gotErr)
- }
- }
-
- for _, want := range tc.want {
- if !strings.Contains(gotErr, want) {
- t.Errorf("unexpected error, expected to contain %q, got: %q ", want, gotErr)
- }
- }
- })
- }
-}
-
-func TestGetMediaTypeV03(t *testing.T) {
- testCases := map[string]struct {
- t string
- want string
- }{
- "nil": {
- want: "",
- },
- "just encoding": {
- t: "charset=utf-8",
- want: "",
- },
- "text/html with encoding": {
- t: "text/html; charset=utf-8",
- want: "text/html",
- },
- "application/json with encoding": {
- t: "application/json; charset=utf-8",
- want: "application/json",
- },
- "application/json": {
- t: "application/json",
- want: "application/json",
- },
- }
- for n, tc := range testCases {
- t.Run(n, func(t *testing.T) {
-
- ec := ce.EventContextV03{}
- if tc.t != "" {
- ec.DataContentType = &tc.t
- }
- got, _ := ec.GetDataMediaType()
-
- if diff := cmp.Diff(tc.want, got); diff != "" {
- t.Errorf("unexpected (-want, +got) = %v", diff)
- }
- })
- }
-}
diff --git a/v1/cloudevents/eventcontext_v03_writer.go b/v1/cloudevents/eventcontext_v03_writer.go
deleted file mode 100644
index 479bc0729..000000000
--- a/v1/cloudevents/eventcontext_v03_writer.go
+++ /dev/null
@@ -1,108 +0,0 @@
-package cloudevents
-
-import (
- "errors"
- "fmt"
- "net/url"
- "strings"
- "time"
-
- "github.com/cloudevents/sdk-go/v1/cloudevents/types"
-)
-
-// Adhere to EventContextWriter
-var _ EventContextWriter = (*EventContextV03)(nil)
-
-// SetSpecVersion implements EventContextWriter.SetSpecVersion
-func (ec *EventContextV03) SetSpecVersion(v string) error {
- if v != CloudEventsVersionV03 {
- return fmt.Errorf("invalid version %q, expecting %q", v, CloudEventsVersionV03)
- }
- ec.SpecVersion = CloudEventsVersionV03
- return nil
-}
-
-// SetDataContentType implements EventContextWriter.SetDataContentType
-func (ec *EventContextV03) SetDataContentType(ct string) error {
- ct = strings.TrimSpace(ct)
- if ct == "" {
- ec.DataContentType = nil
- } else {
- ec.DataContentType = &ct
- }
- return nil
-}
-
-// SetType implements EventContextWriter.SetType
-func (ec *EventContextV03) SetType(t string) error {
- t = strings.TrimSpace(t)
- ec.Type = t
- return nil
-}
-
-// SetSource implements EventContextWriter.SetSource
-func (ec *EventContextV03) SetSource(u string) error {
- pu, err := url.Parse(u)
- if err != nil {
- return err
- }
- ec.Source = types.URLRef{URL: *pu}
- return nil
-}
-
-// SetSubject implements EventContextWriter.SetSubject
-func (ec *EventContextV03) SetSubject(s string) error {
- s = strings.TrimSpace(s)
- if s == "" {
- ec.Subject = nil
- } else {
- ec.Subject = &s
- }
- return nil
-}
-
-// SetID implements EventContextWriter.SetID
-func (ec *EventContextV03) SetID(id string) error {
- id = strings.TrimSpace(id)
- if id == "" {
- return errors.New("id is required to be a non-empty string")
- }
- ec.ID = id
- return nil
-}
-
-// SetTime implements EventContextWriter.SetTime
-func (ec *EventContextV03) SetTime(t time.Time) error {
- if t.IsZero() {
- ec.Time = nil
- } else {
- ec.Time = &types.Timestamp{Time: t}
- }
- return nil
-}
-
-// SetDataSchema implements EventContextWriter.SetDataSchema
-func (ec *EventContextV03) SetDataSchema(u string) error {
- u = strings.TrimSpace(u)
- if u == "" {
- ec.SchemaURL = nil
- return nil
- }
- pu, err := url.Parse(u)
- if err != nil {
- return err
- }
- ec.SchemaURL = &types.URLRef{URL: *pu}
- return nil
-}
-
-// DeprecatedSetDataContentEncoding implements EventContextWriter.DeprecatedSetDataContentEncoding
-func (ec *EventContextV03) DeprecatedSetDataContentEncoding(e string) error {
- e = strings.ToLower(strings.TrimSpace(e))
- if e == "" {
- ec.DataContentEncoding = nil
- } else {
- ec.DataContentEncoding = &e
- }
- return nil
-}
diff --git a/v1/cloudevents/eventcontext_v1.go b/v1/cloudevents/eventcontext_v1.go
deleted file mode 100644
index c3d055776..000000000
--- a/v1/cloudevents/eventcontext_v1.go
+++ /dev/null
@@ -1,315 +0,0 @@
-package cloudevents
-
-import (
- "errors"
- "fmt"
- "mime"
- "sort"
- "strings"
-
- "github.com/cloudevents/sdk-go/v1/cloudevents/types"
-)
-
-// WIP: AS OF SEP 20, 2019
-
-const (
- // CloudEventsVersionV1 represents the version 1.0 of the CloudEvents spec.
- CloudEventsVersionV1 = "1.0"
-)
-
-// EventContextV1 represents the non-data attributes of a CloudEvents v1.0
-// event.
-type EventContextV1 struct {
- // ID of the event; must be non-empty and unique within the scope of the producer.
- // +required
- ID string `json:"id"`
- // Source - A URI describing the event producer.
- // +required
- Source types.URIRef `json:"source"`
- // SpecVersion - The version of the CloudEvents specification used by the event.
- // +required
- SpecVersion string `json:"specversion"`
- // Type - The type of the occurrence which has happened.
- // +required
- Type string `json:"type"`
-
- // DataContentType - A MIME (RFC2046) string describing the media type of `data`.
- // +optional
- DataContentType *string `json:"datacontenttype,omitempty"`
- // Subject - The subject of the event in the context of the event producer
- // (identified by `source`).
- // +optional
- Subject *string `json:"subject,omitempty"`
- // Time - A Timestamp when the event happened.
- // +optional
- Time *types.Timestamp `json:"time,omitempty"`
- // DataSchema - A link to the schema that the `data` attribute adheres to.
- // +optional
- DataSchema *types.URI `json:"dataschema,omitempty"`
-
- // Extensions - Additional extension metadata beyond the base spec.
- // +optional
- Extensions map[string]interface{} `json:"-"`
-}
-
-// Adhere to EventContext
-var _ EventContext = (*EventContextV1)(nil)
-
-// ExtensionAs implements EventContext.ExtensionAs
-func (ec EventContextV1) ExtensionAs(name string, obj interface{}) error {
- name = strings.ToLower(name)
- value, ok := ec.Extensions[name]
- if !ok {
- return fmt.Errorf("extension %q does not exist", name)
- }
-
- // Only support *string for now.
- if v, ok := obj.(*string); ok {
- if *v, ok = value.(string); ok {
- return nil
- }
- }
- return fmt.Errorf("unknown extension type %T", obj)
-}
-
-// SetExtension adds the extension 'name' with value 'value' to the CloudEvents context.
-// This function fails if the name doesn't respect the regex ^[a-zA-Z0-9]+$
-func (ec *EventContextV1) SetExtension(name string, value interface{}) error {
- if !IsAlphaNumeric(name) {
- return errors.New("bad key, CloudEvents attribute names MUST consist of lower-case letters ('a' to 'z') or digits ('0' to '9') from the ASCII character set")
- }
-
- name = strings.ToLower(name)
- if ec.Extensions == nil {
- ec.Extensions = make(map[string]interface{})
- }
- if value == nil {
- delete(ec.Extensions, name)
- return nil
- } else {
- v, err := types.Validate(value) // Ensure it's a legal CE attribute value
- if err == nil {
- ec.Extensions[name] = v
- }
- return err
- }
-}
-
-// Clone implements EventContextConverter.Clone
-func (ec EventContextV1) Clone() EventContext {
- ec1 := ec.AsV1()
- ec1.Extensions = ec1.cloneExtensions()
- return ec1
-}
-
-func (ec *EventContextV1) cloneExtensions() map[string]interface{} {
- old := ec.Extensions
- if old == nil {
- return nil
- }
- new := make(map[string]interface{}, len(ec.Extensions))
- for k, v := range old {
- new[k] = v
- }
- return new
-}
-
-// AsV01 implements EventContextConverter.AsV01
-func (ec EventContextV1) AsV01() *EventContextV01 {
- ecv2 := ec.AsV02()
- return ecv2.AsV01()
-}
-
-// AsV02 implements EventContextConverter.AsV02
-func (ec EventContextV1) AsV02() *EventContextV02 {
- ecv3 := ec.AsV03()
- return ecv3.AsV02()
-}
-
-// AsV03 implements EventContextConverter.AsV03
-func (ec EventContextV1) AsV03() *EventContextV03 {
- ret := EventContextV03{
- SpecVersion: CloudEventsVersionV03,
- ID: ec.ID,
- Time: ec.Time,
- Type: ec.Type,
- DataContentType: ec.DataContentType,
- Source: types.URLRef{URL: ec.Source.URL},
- Subject: ec.Subject,
- Extensions: make(map[string]interface{}),
- }
-
- if ec.DataSchema != nil {
- ret.SchemaURL = &types.URLRef{URL: ec.DataSchema.URL}
- }
-
- // TODO: DeprecatedDataContentEncoding needs to be moved to extensions.
- if ec.Extensions != nil {
- for k, v := range ec.Extensions {
- k = strings.ToLower(k)
- // DeprecatedDataContentEncoding was introduced in 0.3, removed in 1.0
- if strings.EqualFold(k, DataContentEncodingKey) {
- etv, ok := v.(string)
- if ok && etv != "" {
- ret.DataContentEncoding = &etv
- }
- continue
- }
- ret.Extensions[k] = v
- }
- }
- if len(ret.Extensions) == 0 {
- ret.Extensions = nil
- }
- return &ret
-}
-
-// AsV04 implements EventContextConverter.AsV04
-func (ec EventContextV1) AsV1() *EventContextV1 {
- ec.SpecVersion = CloudEventsVersionV1
- return &ec
-}
-
-// Validate returns errors based on requirements from the CloudEvents spec.
-// For more details, see https://github.com/cloudevents/spec/blob/v1.0-rc1/spec.md.
-func (ec EventContextV1) Validate() error {
- errors := []string(nil)
-
- // id
- // Type: String
- // Constraints:
- // REQUIRED
- // MUST be a non-empty string
- // MUST be unique within the scope of the producer
- id := strings.TrimSpace(ec.ID)
- if id == "" {
- errors = append(errors, "id: MUST be a non-empty string")
- // no way to test "MUST be unique within the scope of the producer"
- }
-
- // source
- // Type: URI-reference
- // Constraints:
- // REQUIRED
- // MUST be a non-empty URI-reference
- // An absolute URI is RECOMMENDED
- source := strings.TrimSpace(ec.Source.String())
- if source == "" {
- errors = append(errors, "source: REQUIRED")
- }
-
- // specversion
- // Type: String
- // Constraints:
- // REQUIRED
- // MUST be a non-empty string
- specVersion := strings.TrimSpace(ec.SpecVersion)
- if specVersion == "" {
- errors = append(errors, "specversion: MUST be a non-empty string")
- }
-
- // type
- // Type: String
- // Constraints:
- // REQUIRED
- // MUST be a non-empty string
- // SHOULD be prefixed with a reverse-DNS name. The prefixed domain dictates the organization which defines the semantics of this event type.
- eventType := strings.TrimSpace(ec.Type)
- if eventType == "" {
- errors = append(errors, "type: MUST be a non-empty string")
- }
-
- // The following attributes are optional but still have validation.
-
- // datacontenttype
- // Type: String per RFC 2046
- // Constraints:
- // OPTIONAL
- // If present, MUST adhere to the format specified in RFC 2046
- if ec.DataContentType != nil {
- dataContentType := strings.TrimSpace(*ec.DataContentType)
- if dataContentType == "" {
- errors = append(errors, "datacontenttype: if present, MUST adhere to the format specified in RFC 2046")
- } else {
- _, _, err := mime.ParseMediaType(dataContentType)
- if err != nil {
- errors = append(errors, fmt.Sprintf("datacontenttype: failed to parse media type, %s", err.Error()))
- }
- }
- }
-
- // dataschema
- // Type: URI
- // Constraints:
- // OPTIONAL
- // If present, MUST adhere to the format specified in RFC 3986
- if ec.DataSchema != nil {
- dataSchema := strings.TrimSpace(ec.DataSchema.String())
- // empty string is not RFC 3986 compatible.
- if dataSchema == "" {
- errors = append(errors, "dataschema: if present, MUST adhere to the format specified in RFC 3986")
- }
- }
-
- // subject
- // Type: String
- // Constraints:
- // OPTIONAL
- // MUST be a non-empty string
- if ec.Subject != nil {
- subject := strings.TrimSpace(*ec.Subject)
- if subject == "" {
- errors = append(errors, "subject: if present, MUST be a non-empty string")
- }
- }
-
- // time
- // Type: Timestamp
- // Constraints:
- // OPTIONAL
- // If present, MUST adhere to the format specified in RFC 3339
- // --> no need to test this, no way to set the time without it being valid.
-
- if len(errors) > 0 {
- return fmt.Errorf(strings.Join(errors, "\n"))
- }
- return nil
-}
-
-// String returns a pretty-printed representation of the EventContext.
-func (ec EventContextV1) String() string {
- b := strings.Builder{}
-
- b.WriteString("Context Attributes,\n")
-
- b.WriteString(" specversion: " + ec.SpecVersion + "\n")
- b.WriteString(" type: " + ec.Type + "\n")
- b.WriteString(" source: " + ec.Source.String() + "\n")
- if ec.Subject != nil {
- b.WriteString(" subject: " + *ec.Subject + "\n")
- }
- b.WriteString(" id: " + ec.ID + "\n")
- if ec.Time != nil {
- b.WriteString(" time: " + ec.Time.String() + "\n")
- }
- if ec.DataSchema != nil {
- b.WriteString(" dataschema: " + ec.DataSchema.String() + "\n")
- }
- if ec.DataContentType != nil {
- b.WriteString(" datacontenttype: " + *ec.DataContentType + "\n")
- }
-
- if ec.Extensions != nil && len(ec.Extensions) > 0 {
- b.WriteString("Extensions,\n")
- keys := make([]string, 0, len(ec.Extensions))
- for k := range ec.Extensions {
- keys = append(keys, k)
- }
- sort.Strings(keys)
- for _, key := range keys {
- b.WriteString(fmt.Sprintf(" %s: %v\n", key, ec.Extensions[key]))
- }
- }
-
- return b.String()
-}
diff --git a/v1/cloudevents/eventcontext_v1_reader.go b/v1/cloudevents/eventcontext_v1_reader.go
deleted file mode 100644
index e3f329d31..000000000
--- a/v1/cloudevents/eventcontext_v1_reader.go
+++ /dev/null
@@ -1,98 +0,0 @@
-package cloudevents
-
-import (
- "fmt"
- "mime"
- "time"
-)
-
-// GetSpecVersion implements EventContextReader.GetSpecVersion
-func (ec EventContextV1) GetSpecVersion() string {
- if ec.SpecVersion != "" {
- return ec.SpecVersion
- }
- return CloudEventsVersionV03
-}
-
-// GetDataContentType implements EventContextReader.GetDataContentType
-func (ec EventContextV1) GetDataContentType() string {
- if ec.DataContentType != nil {
- return *ec.DataContentType
- }
- return ""
-}
-
-// GetDataMediaType implements EventContextReader.GetDataMediaType
-func (ec EventContextV1) GetDataMediaType() (string, error) {
- if ec.DataContentType != nil {
- mediaType, _, err := mime.ParseMediaType(*ec.DataContentType)
- if err != nil {
- return "", err
- }
- return mediaType, nil
- }
- return "", nil
-}
-
-// GetType implements EventContextReader.GetType
-func (ec EventContextV1) GetType() string {
- return ec.Type
-}
-
-// GetSource implements EventContextReader.GetSource
-func (ec EventContextV1) GetSource() string {
- return ec.Source.String()
-}
-
-// GetSubject implements EventContextReader.GetSubject
-func (ec EventContextV1) GetSubject() string {
- if ec.Subject != nil {
- return *ec.Subject
- }
- return ""
-}
-
-// GetTime implements EventContextReader.GetTime
-func (ec EventContextV1) GetTime() time.Time {
- if ec.Time != nil {
- return ec.Time.Time
- }
- return time.Time{}
-}
-
-// GetID implements EventContextReader.GetID
-func (ec EventContextV1) GetID() string {
- return ec.ID
-}
-
-// GetDataSchema implements EventContextReader.GetDataSchema
-func (ec EventContextV1) GetDataSchema() string {
- if ec.DataSchema != nil {
- return ec.DataSchema.String()
- }
- return ""
-}
-
-// DeprecatedGetDataContentEncoding implements EventContextReader.DeprecatedGetDataContentEncoding
-func (ec EventContextV1) DeprecatedGetDataContentEncoding() string {
- return ""
-}
-
-// GetExtensions implements EventContextReader.GetExtensions
-func (ec EventContextV1) GetExtensions() map[string]interface{} {
- // For now, convert the extensions of v1.0 to the pre-v1.0 style.
- ext := make(map[string]interface{}, len(ec.Extensions))
- for k, v := range ec.Extensions {
- ext[k] = v
- }
- return ext
-}
-
-// GetExtension implements EventContextReader.GetExtension
-func (ec EventContextV1) GetExtension(key string) (interface{}, error) {
- v, ok := caseInsensitiveSearch(key, ec.Extensions)
- if !ok {
- return "", fmt.Errorf("%q not found", key)
- }
- return v, nil
-}
diff --git a/v1/cloudevents/eventcontext_v1_test.go b/v1/cloudevents/eventcontext_v1_test.go
deleted file mode 100644
index 97501633a..000000000
--- a/v1/cloudevents/eventcontext_v1_test.go
+++ /dev/null
@@ -1,206 +0,0 @@
-package cloudevents_test
-
-import (
- "net/url"
- "strings"
- "testing"
- "time"
-
- ce "github.com/cloudevents/sdk-go/v1/cloudevents"
- "github.com/cloudevents/sdk-go/v1/cloudevents/types"
- "github.com/google/go-cmp/cmp"
-)
-
-func TestValidateV1(t *testing.T) {
- now := types.Timestamp{Time: time.Now()}
-
- sourceUrl, _ := url.Parse("http://example.com/source")
- source := &types.URIRef{URL: *sourceUrl}
-
- subject := "a subject"
-
- DataSchema, _ := url.Parse("http://example.com/schema")
- schema := &types.URI{URL: *DataSchema}
-
- extensions := make(map[string]interface{})
- extensions["test"] = "extended"
-
- testCases := map[string]struct {
- ctx ce.EventContextV1
- want []string
- }{
- "min valid": {
- ctx: ce.EventContextV1{
- SpecVersion: ce.CloudEventsVersionV03,
- ID: "ABC-123",
- Type: "com.example.simple",
- Source: *source,
- },
- },
- "full valid": {
- ctx: ce.EventContextV1{
- SpecVersion: ce.CloudEventsVersionV03,
- ID: "ABC-123",
- Time: &now,
- Type: "com.example.simple",
- DataSchema: schema,
- DataContentType: ce.StringOfApplicationJSON(),
- Source: *source,
- Subject: &subject,
- Extensions: extensions,
- },
- },
- "no Type": {
- ctx: ce.EventContextV1{
- SpecVersion: ce.CloudEventsVersionV03,
- ID: "ABC-123",
- Source: *source,
- },
- want: []string{"type:"},
- },
- "non-empty SpecVersion": {
- ctx: ce.EventContextV1{
- SpecVersion: "",
- ID: "ABC-123",
- Type: "com.example.simple",
- Source: *source,
- },
- want: []string{"specversion:"},
- },
- "missing source": {
- ctx: ce.EventContextV1{
- SpecVersion: ce.CloudEventsVersionV03,
- ID: "ABC-123",
- Type: "com.example.simple",
- },
- want: []string{"source:"},
- },
- "non-empty subject": {
- ctx: ce.EventContextV1{
- SpecVersion: ce.CloudEventsVersionV03,
- ID: "",
- Type: "com.example.simple",
- Source: *source,
- Subject: strptr(" "),
- },
- want: []string{"subject:"},
- },
- "non-empty ID": {
- ctx: ce.EventContextV1{
- SpecVersion: ce.CloudEventsVersionV03,
- ID: "",
- Type: "com.example.simple",
- Source: *source,
- },
- want: []string{"id:"},
- },
- "empty DataSchema": {
- ctx: ce.EventContextV1{
- SpecVersion: ce.CloudEventsVersionV03,
- ID: "ABC-123",
- Type: "com.example.simple",
- DataSchema: &types.URI{},
- Source: *source,
- },
- want: []string{"dataschema:"},
- },
- "non-empty contentType": {
- ctx: ce.EventContextV1{
- SpecVersion: ce.CloudEventsVersionV03,
- ID: "ABC-123",
- Type: "com.example.simple",
- Source: *source,
- DataContentType: strptr(""),
- },
- want: []string{"datacontenttype:"},
- },
- "invalid contentType": {
- ctx: ce.EventContextV1{
- SpecVersion: ce.CloudEventsVersionV03,
- ID: "ABC-123",
- Type: "com.example.simple",
- Source: *source,
- DataContentType: strptr("bogus ;========="),
- },
- want: []string{"datacontenttype:"},
- },
-
- "all errors": {
- ctx: ce.EventContextV1{
- SpecVersion: "",
- ID: "",
- DataSchema: &types.URI{},
- DataContentType: strptr(""),
- Extensions: make(map[string]interface{}),
- },
- want: []string{
- "type:",
- "id:",
- "specversion:",
- "source:",
- "contenttype:",
- },
- },
- }
- for n, tc := range testCases {
- t.Run(n, func(t *testing.T) {
-
- got := tc.ctx.Validate()
- var gotErr string
- if got != nil {
- gotErr = got.Error()
-
- if len(tc.want) == 0 {
- t.Errorf("unexpected no error, got %q", gotErr)
- }
- }
-
- for _, want := range tc.want {
- if !strings.Contains(gotErr, want) {
- t.Errorf("unexpected error, expected to contain %q, got: %q ", want, gotErr)
- }
- }
- })
- }
-}
-
-func TestGetMediaTypeV1(t *testing.T) {
- testCases := map[string]struct {
- t string
- want string
- }{
- "nil": {
- want: "",
- },
- "just encoding": {
- t: "charset=utf-8",
- want: "",
- },
- "text/html with encoding": {
- t: "text/html; charset=utf-8",
- want: "text/html",
- },
- "application/json with encoding": {
- t: "application/json; charset=utf-8",
- want: "application/json",
- },
- "application/json": {
- t: "application/json",
- want: "application/json",
- },
- }
- for n, tc := range testCases {
- t.Run(n, func(t *testing.T) {
-
- ec := ce.EventContextV1{}
- if tc.t != "" {
- ec.DataContentType = &tc.t
- }
- got, _ := ec.GetDataMediaType()
-
- if diff := cmp.Diff(tc.want, got); diff != "" {
- t.Errorf("unexpected (-want, +got) = %v", diff)
- }
- })
- }
-}
diff --git a/v1/cloudevents/eventcontext_v1_writer.go b/v1/cloudevents/eventcontext_v1_writer.go
deleted file mode 100644
index 5765fdd25..000000000
--- a/v1/cloudevents/eventcontext_v1_writer.go
+++ /dev/null
@@ -1,102 +0,0 @@
-package cloudevents
-
-import (
- "errors"
- "fmt"
- "net/url"
- "strings"
- "time"
-
- "github.com/cloudevents/sdk-go/v1/cloudevents/types"
-)
-
-// Adhere to EventContextWriter
-var _ EventContextWriter = (*EventContextV1)(nil)
-
-// SetSpecVersion implements EventContextWriter.SetSpecVersion
-func (ec *EventContextV1) SetSpecVersion(v string) error {
- if v != CloudEventsVersionV1 {
- return fmt.Errorf("invalid version %q, expecting %q", v, CloudEventsVersionV1)
- }
- ec.SpecVersion = CloudEventsVersionV1
- return nil
-}
-
-// SetDataContentType implements EventContextWriter.SetDataContentType
-func (ec *EventContextV1) SetDataContentType(ct string) error {
- ct = strings.TrimSpace(ct)
- if ct == "" {
- ec.DataContentType = nil
- } else {
- ec.DataContentType = &ct
- }
- return nil
-}
-
-// SetType implements EventContextWriter.SetType
-func (ec *EventContextV1) SetType(t string) error {
- t = strings.TrimSpace(t)
- ec.Type = t
- return nil
-}
-
-// SetSource implements EventContextWriter.SetSource
-func (ec *EventContextV1) SetSource(u string) error {
- pu, err := url.Parse(u)
- if err != nil {
- return err
- }
- ec.Source = types.URIRef{URL: *pu}
- return nil
-}
-
-// SetSubject implements EventContextWriter.SetSubject
-func (ec *EventContextV1) SetSubject(s string) error {
- s = strings.TrimSpace(s)
- if s == "" {
- ec.Subject = nil
- } else {
- ec.Subject = &s
- }
- return nil
-}
-
-// SetID implements EventContextWriter.SetID
-func (ec *EventContextV1) SetID(id string) error {
- id = strings.TrimSpace(id)
- if id == "" {
- return errors.New("id is required to be a non-empty string")
- }
- ec.ID = id
- return nil
-}
-
-// SetTime implements EventContextWriter.SetTime
-func (ec *EventContextV1) SetTime(t time.Time) error {
- if t.IsZero() {
- ec.Time = nil
- } else {
- ec.Time = &types.Timestamp{Time: t}
- }
- return nil
-}
-
-// SetDataSchema implements EventContextWriter.SetDataSchema
-func (ec *EventContextV1) SetDataSchema(u string) error {
- u = strings.TrimSpace(u)
- if u == "" {
- ec.DataSchema = nil
- return nil
- }
- pu, err := url.Parse(u)
- if err != nil {
- return err
- }
- ec.DataSchema = &types.URI{URL: *pu}
- return nil
-}
-
-// DeprecatedSetDataContentEncoding implements EventContextWriter.DeprecatedSetDataContentEncoding
-func (ec *EventContextV1) DeprecatedSetDataContentEncoding(e string) error {
- return errors.New("deprecated: SetDataContentEncoding is not supported in v1.0 of CloudEvents")
-}
diff --git a/v1/cloudevents/extensions.go b/v1/cloudevents/extensions.go
deleted file mode 100644
index e6a7d5325..000000000
--- a/v1/cloudevents/extensions.go
+++ /dev/null
@@ -1,30 +0,0 @@
-package cloudevents
-
-import (
- "regexp"
- "strings"
-)
-
-const (
- // DataContentEncodingKey is the key to DeprecatedDataContentEncoding for versions that do not support data content encoding
- // directly.
- DataContentEncodingKey = "datacontentencoding"
-
- // EventTypeVersionKey is the key to EventTypeVersion for versions that do not support event type version directly.
- EventTypeVersionKey = "eventtypeversion"
-
- // SubjectKey is the key to Subject for versions that do not support subject directly.
- SubjectKey = "subject"
-)
-
-func caseInsensitiveSearch(key string, space map[string]interface{}) (interface{}, bool) {
- lkey := strings.ToLower(key)
- for k, v := range space {
- if strings.EqualFold(lkey, strings.ToLower(k)) {
- return v, true
- }
- }
- return nil, false
-}
-
-var IsAlphaNumeric = regexp.MustCompile(`^[a-zA-Z0-9]+$`).MatchString
diff --git a/v1/cloudevents/extensions/distributed_tracing_extension.go b/v1/cloudevents/extensions/distributed_tracing_extension.go
deleted file mode 100644
index c6aa241e2..000000000
--- a/v1/cloudevents/extensions/distributed_tracing_extension.go
+++ /dev/null
@@ -1,135 +0,0 @@
-package extensions
-
-import (
- "context"
- "reflect"
- "strings"
-
- "github.com/cloudevents/sdk-go/v1/cloudevents"
- "github.com/cloudevents/sdk-go/v1/cloudevents/types"
- "github.com/lightstep/tracecontext.go/traceparent"
- "github.com/lightstep/tracecontext.go/tracestate"
- "go.opencensus.io/trace"
- octs "go.opencensus.io/trace/tracestate"
-)
-
-const (
- TraceParentExtension = "traceparent"
- TraceStateExtension = "tracestate"
-)
-
-// EventTracer interface allows setting extension for cloudevents context.
-type EventTracer interface {
- SetExtension(k string, v interface{}) error
-}
-
-// DistributedTracingExtension represents the extension for cloudevents context
-type DistributedTracingExtension struct {
- TraceParent string `json:"traceparent"`
- TraceState string `json:"tracestate"`
-}
-
-// AddTracingAttributes adds the tracing attributes traceparent and tracestate to the cloudevents context
-func (d DistributedTracingExtension) AddTracingAttributes(ec EventTracer) error {
- if d.TraceParent != "" {
- value := reflect.ValueOf(d)
- typeOf := value.Type()
-
- for i := 0; i < value.NumField(); i++ {
- k := strings.ToLower(typeOf.Field(i).Name)
- v := value.Field(i).Interface()
- if k == TraceStateExtension && v == "" {
- continue
- }
- if err := ec.SetExtension(k, v); err != nil {
- return err
- }
- }
- }
- return nil
-}
-
-func GetDistributedTracingExtension(event cloudevents.Event) (DistributedTracingExtension, bool) {
- if tp, ok := event.Extensions()[TraceParentExtension]; ok {
- if tpStr, err := types.ToString(tp); err == nil {
- var tsStr string
- if ts, ok := event.Extensions()[TraceStateExtension]; ok {
- tsStr, _ = types.ToString(ts)
- }
- return DistributedTracingExtension{TraceParent: tpStr, TraceState: tsStr}, true
- }
- }
- return DistributedTracingExtension{}, false
-}
-
-// FromSpanContext populates DistributedTracingExtension from a SpanContext.
-func FromSpanContext(sc trace.SpanContext) DistributedTracingExtension {
- tp := traceparent.TraceParent{
- TraceID: sc.TraceID,
- SpanID: sc.SpanID,
- Flags: traceparent.Flags{
- Recorded: sc.IsSampled(),
- },
- }
-
- entries := make([]string, 0, len(sc.Tracestate.Entries()))
- for _, entry := range sc.Tracestate.Entries() {
- entries = append(entries, strings.Join([]string{entry.Key, entry.Value}, "="))
- }
-
- return DistributedTracingExtension{
- TraceParent: tp.String(),
- TraceState: strings.Join(entries, ","),
- }
-}
-
-// ToSpanContext creates a SpanContext from a DistributedTracingExtension instance.
-func (d DistributedTracingExtension) ToSpanContext() (trace.SpanContext, error) {
- tp, err := traceparent.ParseString(d.TraceParent)
- if err != nil {
- return trace.SpanContext{}, err
- }
- sc := trace.SpanContext{
- TraceID: tp.TraceID,
- SpanID: tp.SpanID,
- }
- if tp.Flags.Recorded {
- sc.TraceOptions |= 1
- }
-
- if ts, err := tracestate.ParseString(d.TraceState); err == nil {
- entries := make([]octs.Entry, 0, len(ts))
- for _, member := range ts {
- var key string
- if member.Tenant != "" {
- // Due to github.com/lightstep/tracecontext.go/issues/6,
- // the meaning of Vendor and Tenant are swapped here.
- key = member.Vendor + "@" + member.Tenant
- } else {
- key = member.Vendor
- }
- entries = append(entries, octs.Entry{Key: key, Value: member.Value})
- }
- sc.Tracestate, _ = octs.New(nil, entries...)
- }
-
- return sc, nil
-}
-
-func (d DistributedTracingExtension) StartChildSpan(ctx context.Context, name string, opts ...trace.StartOption) (context.Context, *trace.Span) {
- if sc, err := d.ToSpanContext(); err == nil {
- tSpan := trace.FromContext(ctx)
- ctx, span := trace.StartSpanWithRemoteParent(ctx, name, sc, opts...)
- if tSpan != nil {
- // Add link to the previous in-process trace.
- tsc := tSpan.SpanContext()
- span.AddLink(trace.Link{
- TraceID: tsc.TraceID,
- SpanID: tsc.SpanID,
- Type: trace.LinkTypeParent,
- })
- }
- return ctx, span
- }
- return ctx, nil
-}
diff --git a/v1/cloudevents/extensions/distributed_tracing_extension_test.go b/v1/cloudevents/extensions/distributed_tracing_extension_test.go
deleted file mode 100644
index 7054cf74e..000000000
--- a/v1/cloudevents/extensions/distributed_tracing_extension_test.go
+++ /dev/null
@@ -1,340 +0,0 @@
-package extensions_test
-
-import (
- "encoding/hex"
- "net/url"
- "testing"
- "time"
-
- "github.com/cloudevents/sdk-go/v1/cloudevents"
- "github.com/cloudevents/sdk-go/v1/cloudevents/extensions"
- "github.com/cloudevents/sdk-go/v1/cloudevents/types"
- "github.com/google/go-cmp/cmp"
- "go.opencensus.io/trace"
- "go.opencensus.io/trace/tracestate"
-)
-
-type Data struct {
- Message string
-}
-
-var now = types.Timestamp{Time: time.Now().UTC()}
-
-var sourceUrl, _ = url.Parse("http://example.com/source")
-var source = &types.URLRef{URL: *sourceUrl}
-
-var schemaUrl, _ = url.Parse("http://example.com/schema")
-var schema = &types.URLRef{URL: *schemaUrl}
-
-type values struct {
- context interface{}
- want map[string]interface{}
-}
-
-func TestAddTracingAttributes_Scenario1(t *testing.T) {
- var st = extensions.DistributedTracingExtension{
- TraceParent: "00-4bf92f3577b34da6a3ce929d0e0e4736-00f067aa0ba902b7-01",
- TraceState: "rojo=00-4bf92f3577b34da6a3ce929d0e0e4736-00f067aa0ba902b7-01,congo=lZWRzIHRoNhcm5hbCBwbGVhc3VyZS4=",
- }
-
- var eventContextVersions = map[string]values{
- "EventContextV01": {
- context: cloudevents.EventContextV01{
- EventID: "ABC-123",
- EventTime: &now,
- EventType: "com.example.test",
- SchemaURL: schema,
- ContentType: cloudevents.StringOfApplicationJSON(),
- Source: *source,
- },
- want: map[string]interface{}{"traceparent": st.TraceParent, "tracestate": st.TraceState},
- },
- "EventContextV02": {
- context: cloudevents.EventContextV02{
- ID: "ABC-123",
- Time: &now,
- Type: "com.example.test",
- SchemaURL: schema,
- ContentType: cloudevents.StringOfApplicationJSON(),
- Source: *source,
- },
- want: map[string]interface{}{"traceparent": st.TraceParent, "tracestate": st.TraceState},
- },
- "EventContextV03": {
- context: cloudevents.EventContextV03{
- ID: "ABC-123",
- Time: &now,
- Type: "com.example.test",
- SchemaURL: schema,
- DataContentType: cloudevents.StringOfApplicationJSON(),
- Source: *source,
- },
- want: map[string]interface{}{"traceparent": st.TraceParent, "tracestate": st.TraceState},
- },
- }
-
- for k, ecv := range eventContextVersions {
- testAddTracingAttributesFunc(t, st, ecv, k)
- }
-}
-
-func TestAddTracingAttributes_Scenario2(t *testing.T) {
- var st = extensions.DistributedTracingExtension{
- TraceParent: "00-4bf92f3577b34da6a3ce929d0e0e4736-00f067aa0ba902b7-01",
- }
-
- var eventContextVersions = map[string]values{
- "EventContextV01": {
- context: cloudevents.EventContextV01{
- EventID: "ABC-123",
- EventTime: &now,
- EventType: "com.example.test",
- SchemaURL: schema,
- ContentType: cloudevents.StringOfApplicationJSON(),
- Source: *source,
- },
- want: map[string]interface{}{"traceparent": st.TraceParent},
- },
- "EventContextV02": {
- context: cloudevents.EventContextV02{
- ID: "ABC-123",
- Time: &now,
- Type: "com.example.test",
- SchemaURL: schema,
- ContentType: cloudevents.StringOfApplicationJSON(),
- Source: *source,
- },
- want: map[string]interface{}{"traceparent": st.TraceParent},
- },
- "EventContextV03": {
- context: cloudevents.EventContextV03{
- ID: "ABC-123",
- Time: &now,
- Type: "com.example.test",
- SchemaURL: schema,
- DataContentType: cloudevents.StringOfApplicationJSON(),
- Source: *source,
- },
- want: map[string]interface{}{"traceparent": st.TraceParent},
- },
- }
-
- for k, ecv := range eventContextVersions {
- testAddTracingAttributesFunc(t, st, ecv, k)
- }
-}
-
-func TestAddTracingAttributes_Scenario3(t *testing.T) {
- var st = extensions.DistributedTracingExtension{}
-
- var eventContextVersions = map[string]values{
- "EventContextV01": {
- context: cloudevents.EventContextV01{
- EventID: "ABC-123",
- EventTime: &now,
- EventType: "com.example.test",
- SchemaURL: schema,
- ContentType: cloudevents.StringOfApplicationJSON(),
- Source: *source,
- },
- want: map[string]interface{}(nil),
- },
- "EventContextV02": {
- context: cloudevents.EventContextV02{
- ID: "ABC-123",
- Time: &now,
- Type: "com.example.test",
- SchemaURL: schema,
- ContentType: cloudevents.StringOfApplicationJSON(),
- Source: *source,
- },
- want: map[string]interface{}(nil),
- },
- "EventContextV03": {
- context: cloudevents.EventContextV03{
- ID: "ABC-123",
- Time: &now,
- Type: "com.example.test",
- SchemaURL: schema,
- DataContentType: cloudevents.StringOfApplicationJSON(),
- Source: *source,
- },
- want: map[string]interface{}(nil),
- },
- }
-
- for k, ecv := range eventContextVersions {
- testAddTracingAttributesFunc(t, st, ecv, k)
- }
-}
-
-func TestAddTracingAttributes_Scenario4(t *testing.T) {
- var st = extensions.DistributedTracingExtension{
- TraceState: "rojo=00-4bf92f3577b34da6a3ce929d0e0e4736-00f067aa0ba902b7-01,congo=lZWRzIHRoNhcm5hbCBwbGVhc3VyZS4=",
- }
-
- var eventContextVersions = map[string]values{
- "EventContextV01": {
- context: cloudevents.EventContextV01{
- EventID: "ABC-123",
- EventTime: &now,
- EventType: "com.example.test",
- SchemaURL: schema,
- ContentType: cloudevents.StringOfApplicationJSON(),
- Source: *source,
- },
- want: map[string]interface{}(nil),
- },
- "EventContextV02": {
- context: cloudevents.EventContextV02{
- ID: "ABC-123",
- Time: &now,
- Type: "com.example.test",
- SchemaURL: schema,
- ContentType: cloudevents.StringOfApplicationJSON(),
- Source: *source,
- },
- want: map[string]interface{}(nil),
- },
- "EventContextV03": {
- context: cloudevents.EventContextV03{
- ID: "ABC-123",
- Time: &now,
- Type: "com.example.test",
- SchemaURL: schema,
- DataContentType: cloudevents.StringOfApplicationJSON(),
- Source: *source,
- },
- want: map[string]interface{}(nil),
- },
- }
-
- for k, ecv := range eventContextVersions {
- testAddTracingAttributesFunc(t, st, ecv, k)
- }
-}
-
-func testAddTracingAttributesFunc(t *testing.T, st extensions.DistributedTracingExtension, ecv values, ces string) {
- var event cloudevents.Event
- switch ces {
- case "EventContextV01":
- ectx := ecv.context.(cloudevents.EventContextV01).AsV01()
- st.AddTracingAttributes(ectx)
- event = cloudevents.Event{Context: ectx, Data: &Data{Message: "Hello world"}}
- case "EventContextV02":
- ectx := ecv.context.(cloudevents.EventContextV02).AsV02()
- st.AddTracingAttributes(ectx)
- event = cloudevents.Event{Context: ectx, Data: &Data{Message: "Hello world"}}
- case "EventContextV03":
- ectx := ecv.context.(cloudevents.EventContextV03).AsV03()
- st.AddTracingAttributes(ectx)
- event = cloudevents.Event{Context: ectx, Data: &Data{Message: "Hello world"}}
- }
- got := event.Extensions()
-
- if diff := cmp.Diff(ecv.want, got); diff != "" {
- t.Errorf("\nunexpected (-want, +got) = %v", diff)
- }
-}
-
-func decodeTID(s string) (tid [16]byte, err error) {
- buf, err := hex.DecodeString(s)
- copy(tid[:], buf)
- return
-}
-
-func decodeSID(s string) (sid [8]byte, err error) {
- buf, err := hex.DecodeString(s)
- copy(sid[:], buf)
- return
-}
-
-func TestConvertSpanContext(t *testing.T) {
- tid, err := decodeTID("4bf92f3577b34da6a3ce929d0e0e4736")
- if err != nil {
- t.Fatalf("failed to decode traceID: %v", err)
- }
- sid, err := decodeSID("00f067aa0ba902b7")
- if err != nil {
- t.Fatalf("failed to decode spanID: %v", err)
- }
- ts, err := tracestate.New(nil,
- tracestate.Entry{
- Key: "rojo",
- Value: "00-4bf92f3577b34da6a3ce929d0e0e4736-00f067aa0ba902b7-01",
- },
- tracestate.Entry{
- Key: "tenant@congo",
- Value: "lZWRzIHRoNhcm5hbCBwbGVhc3VyZS4",
- },
- )
- if err != nil {
- t.Fatalf("failed to make tracestate: %v", err)
- }
- tests := []struct {
- name string
- sc trace.SpanContext
- ext extensions.DistributedTracingExtension
- }{{
- name: "with tracestate",
- sc: trace.SpanContext{
- TraceID: trace.TraceID(tid),
- SpanID: sid,
- TraceOptions: 1,
- Tracestate: ts,
- },
- ext: extensions.DistributedTracingExtension{
- TraceParent: "00-4bf92f3577b34da6a3ce929d0e0e4736-00f067aa0ba902b7-01",
- TraceState: "rojo=00-4bf92f3577b34da6a3ce929d0e0e4736-00f067aa0ba902b7-01,tenant@congo=lZWRzIHRoNhcm5hbCBwbGVhc3VyZS4",
- },
- }, {
- name: "without tracestate",
- sc: trace.SpanContext{
- TraceID: trace.TraceID(tid),
- SpanID: sid,
- TraceOptions: 1,
- },
- ext: extensions.DistributedTracingExtension{
- TraceParent: "00-4bf92f3577b34da6a3ce929d0e0e4736-00f067aa0ba902b7-01",
- },
- }, {
- name: "unsampled",
- sc: trace.SpanContext{
- TraceID: trace.TraceID(tid),
- SpanID: sid,
- TraceOptions: 0,
- },
- ext: extensions.DistributedTracingExtension{
- TraceParent: "00-4bf92f3577b34da6a3ce929d0e0e4736-00f067aa0ba902b7-00",
- },
- }}
-
- for _, tt := range tests {
- tt := tt
- t.Run("FromSpanContext: "+tt.name, func(t *testing.T) {
- t.Parallel()
- got := extensions.FromSpanContext(tt.sc)
- if diff := cmp.Diff(tt.ext, got); diff != "" {
- t.Errorf("\nunexpected (-want, +got) = %v", diff)
- }
- })
- t.Run("ToSpanContext: "+tt.name, func(t *testing.T) {
- t.Parallel()
- got, err := tt.ext.ToSpanContext()
- if err != nil {
- t.Error(err)
- }
- if diff := cmp.Diff(
- tt.sc, got,
- cmp.Transformer(
- "entries",
- func(ts tracestate.Tracestate) []tracestate.Entry {
- return ts.Entries()
- },
- ),
- ); diff != "" {
- t.Errorf("\nunexpected (-want, +got) = %v", diff)
- }
- })
- }
-}
diff --git a/v1/cloudevents/observability/doc.go b/v1/cloudevents/observability/doc.go
deleted file mode 100644
index 3067ebe7e..000000000
--- a/v1/cloudevents/observability/doc.go
+++ /dev/null
@@ -1,4 +0,0 @@
-/*
-Package observability holds metrics and tracing recording implementations.
-*/
-package observability
diff --git a/v1/cloudevents/observability/keys.go b/v1/cloudevents/observability/keys.go
deleted file mode 100644
index f032b10ec..000000000
--- a/v1/cloudevents/observability/keys.go
+++ /dev/null
@@ -1,19 +0,0 @@
-package observability
-
-import (
- "go.opencensus.io/tag"
-)
-
-var (
- // KeyMethod is the tag used for marking method on a metric.
- KeyMethod, _ = tag.NewKey("method")
- // KeyResult is the tag used for marking result on a metric.
- KeyResult, _ = tag.NewKey("result")
-)
-
-const (
- // ResultError is a shared result tag value for error.
- ResultError = "error"
- // ResultOK is a shared result tag value for success.
- ResultOK = "success"
-)
diff --git a/v1/cloudevents/observability/observer.go b/v1/cloudevents/observability/observer.go
deleted file mode 100644
index b27ffa973..000000000
--- a/v1/cloudevents/observability/observer.go
+++ /dev/null
@@ -1,87 +0,0 @@
-package observability
-
-import (
- "context"
- "sync"
- "time"
-
- "go.opencensus.io/stats"
- "go.opencensus.io/tag"
-)
-
-// Observable represents the the customization used by the Reporter for a given
-// measurement and trace for a single method.
-type Observable interface {
- MethodName() string
- LatencyMs() *stats.Float64Measure
-}
-
-// Reporter represents a running latency counter. When Error or OK are
-// called, the latency is calculated. Error or OK are only allowed to
-// be called once.
-type Reporter interface {
- Error()
- OK()
-}
-
-type reporter struct {
- ctx context.Context
- on Observable
- start time.Time
- once sync.Once
-}
-
-// All tags used for Latency measurements.
-func LatencyTags() []tag.Key {
- return []tag.Key{KeyMethod, KeyResult}
-}
-
-// Deprecated. Tracing is always enabled.
-func EnableTracing(enabled bool) {}
-
-// NewReporter creates and returns a reporter wrapping the provided Observable.
-func NewReporter(ctx context.Context, on Observable) (context.Context, Reporter) {
- r := &reporter{
- ctx: ctx,
- on: on,
- start: time.Now(),
- }
- r.tagMethod()
- return ctx, r
-}
-
-func (r *reporter) tagMethod() {
- var err error
- r.ctx, err = tag.New(r.ctx, tag.Insert(KeyMethod, r.on.MethodName()))
- if err != nil {
- panic(err) // or ignore?
- }
-}
-
-func (r *reporter) record() {
- ms := float64(time.Since(r.start) / time.Millisecond)
- stats.Record(r.ctx, r.on.LatencyMs().M(ms))
-}
-
-// Error records the result as an error.
-func (r *reporter) Error() {
- r.once.Do(func() {
- r.result(ResultError)
- })
-}
-
-// OK records the result as a success.
-func (r *reporter) OK() {
- r.once.Do(func() {
- r.result(ResultOK)
- })
-}
-
-func (r *reporter) result(v string) {
- var err error
- r.ctx, err = tag.New(r.ctx, tag.Insert(KeyResult, v))
- if err != nil {
- panic(err) // or ignore?
- }
- r.record()
-}
diff --git a/v1/cloudevents/transport/amqp/doc.go b/v1/cloudevents/transport/amqp/doc.go
deleted file mode 100644
index 42465d779..000000000
--- a/v1/cloudevents/transport/amqp/doc.go
+++ /dev/null
@@ -1,6 +0,0 @@
-/*
-Package amqp implements the CloudEvent transport implementation using amqp.
-*/
-package amqp
-
-// TODO(alanconway) deprecated, use bindings/amqp directly
diff --git a/v1/cloudevents/transport/amqp/encoding.go b/v1/cloudevents/transport/amqp/encoding.go
deleted file mode 100644
index 8b13d2fdd..000000000
--- a/v1/cloudevents/transport/amqp/encoding.go
+++ /dev/null
@@ -1,66 +0,0 @@
-package amqp
-
-// Encoding to use for amqp transport.
-type Encoding int32
-
-const (
- // Default allows amqp transport implementation to pick.
- Default Encoding = iota
- // BinaryV02 is Binary CloudEvents spec v0.2.
- BinaryV02
- // StructuredV02 is Structured CloudEvents spec v0.2.
- StructuredV02
- // BinaryV03 is Binary CloudEvents spec v0.3.
- BinaryV03
- // StructuredV03 is Structured CloudEvents spec v0.3.
- StructuredV03
- // BinaryV1 is Binary CloudEvents spec v1.0.
- BinaryV1
- // StructuredV1 is Structured CloudEvents spec v1.0.
- StructuredV1
- // Unknown is unknown.
- Unknown
-)
-
-// String pretty-prints the encoding as a string.
-func (e Encoding) String() string {
- switch e {
- case Default:
- return "Default Encoding " + e.Version()
-
- // Binary
- case BinaryV02, BinaryV03, BinaryV1:
- return "Binary Encoding " + e.Version()
-
- // Structured
- case StructuredV02, StructuredV03, StructuredV1:
- return "Structured Encoding " + e.Version()
-
- default:
- return "Unknown Encoding"
- }
-}
-
-// Version pretty-prints the encoding version as a string.
-func (e Encoding) Version() string {
- switch e {
-
- // Version 0.2
- case Default: // <-- Move when a new default is wanted.
- fallthrough
- case StructuredV02:
- return "v0.2"
-
- // Version 0.3
- case StructuredV03:
- return "v0.3"
-
- // Version 1.0
- case StructuredV1:
- return "v1.0"
-
- // Unknown
- default:
- return "Unknown"
- }
-}
diff --git a/v1/cloudevents/transport/amqp/message.go b/v1/cloudevents/transport/amqp/message.go
deleted file mode 100644
index 510eabacd..000000000
--- a/v1/cloudevents/transport/amqp/message.go
+++ /dev/null
@@ -1,46 +0,0 @@
-package amqp
-
-import (
- "encoding/json"
-
- "github.com/cloudevents/sdk-go/v1/cloudevents/transport"
-)
-
-// type check that this transport message impl matches the contract
-var _ transport.Message = (*Message)(nil)
-
-type Message struct {
- ContentType string
- ApplicationProperties map[string]interface{}
- Body []byte
-}
-
-// TODO: update this to work with AMQP
-func (m Message) CloudEventsVersion() string {
- // Check as Binary encoding first.
- if m.ApplicationProperties != nil {
- // Binary v0.2, v0.3:
- if v := m.ApplicationProperties["cloudEvents:specversion"]; v != nil {
- if s, ok := v.(string); ok {
- return s
- }
- }
- }
-
- // Now check as Structured encoding.
- raw := make(map[string]json.RawMessage)
- if err := json.Unmarshal(m.Body, &raw); err != nil {
- return ""
- }
-
- // structured v0.2, v0.3, v1.0
- if v, ok := raw["specversion"]; ok {
- var version string
- if err := json.Unmarshal(v, &version); err != nil {
- return ""
- }
- return version
- }
-
- return ""
-}
diff --git a/v1/cloudevents/transport/amqp/options.go b/v1/cloudevents/transport/amqp/options.go
deleted file mode 100644
index 21b9b741f..000000000
--- a/v1/cloudevents/transport/amqp/options.go
+++ /dev/null
@@ -1,51 +0,0 @@
-package amqp
-
-import "pack.ag/amqp"
-
-// Option is the function signature required to be considered an amqp.Option.
-type Option func(*Transport) error
-
-// WithEncoding sets the encoding for amqp transport.
-func WithEncoding(encoding Encoding) Option {
- return func(t *Transport) error {
- t.Encoding = encoding
- return nil
- }
-}
-
-// WithConnOpt sets a connection option for amqp
-func WithConnOpt(opt amqp.ConnOption) Option {
- return func(t *Transport) error {
- t.connOpts = append(t.connOpts, opt)
- return nil
- }
-}
-
-// WithConnSASLPlain sets SASLPlain connection option for amqp
-func WithConnSASLPlain(username, password string) Option {
- return WithConnOpt(amqp.ConnSASLPlain(username, password))
-}
-
-// WithSessionOpt sets a session option for amqp
-func WithSessionOpt(opt amqp.SessionOption) Option {
- return func(t *Transport) error {
- t.sessionOpts = append(t.sessionOpts, opt)
- return nil
- }
-}
-
-// WithSenderLinkOption sets a link option for amqp
-func WithSenderLinkOption(opt amqp.LinkOption) Option {
- return func(t *Transport) error {
- t.senderLinkOpts = append(t.senderLinkOpts, opt)
- return nil
- }
-}
-
-// WithReceiverLinkOption sets a link option for amqp
-func WithReceiverLinkOption(opt amqp.LinkOption) Option {
- return func(t *Transport) error {
- t.receiverLinkOpts = append(t.receiverLinkOpts, opt)
- return nil
- }
-}
diff --git a/v1/cloudevents/transport/amqp/transport.go b/v1/cloudevents/transport/amqp/transport.go
deleted file mode 100644
index cd263a31b..000000000
--- a/v1/cloudevents/transport/amqp/transport.go
+++ /dev/null
@@ -1,165 +0,0 @@
-package amqp
-
-import (
- "context"
-
- "pack.ag/amqp"
-
- "github.com/cloudevents/sdk-go/v1/binding"
- "github.com/cloudevents/sdk-go/v1/binding/spec"
- "github.com/cloudevents/sdk-go/v1/binding/transcoder"
- bindings_amqp "github.com/cloudevents/sdk-go/v1/bindings/amqp"
- cecontext "github.com/cloudevents/sdk-go/v1/cloudevents/context"
- "github.com/cloudevents/sdk-go/v1/cloudevents/transport"
-)
-
-// Transport adheres to transport.Transport.
-var _ transport.Transport = (*Transport)(nil)
-
-const (
- // TransportName is the name of this transport.
- TransportName = "AMQP"
-)
-
-type Transport struct {
- binding.BindingTransport
- connOpts []amqp.ConnOption
- sessionOpts []amqp.SessionOption
- senderLinkOpts []amqp.LinkOption
- receiverLinkOpts []amqp.LinkOption
-
- // Encoding
- Encoding Encoding
-
- // AMQP
- Client *amqp.Client
- Session *amqp.Session
- Sender *amqp.Sender
- Node string
-
- // Receiver
- Receiver transport.Receiver
- // Converter is invoked if the incoming transport receives an undecodable
- // message.
- Converter transport.Converter
-}
-
-// New creates a new amqp transport.
-func New(server, queue string, opts ...Option) (*Transport, error) {
- t := &Transport{
- Node: queue,
- connOpts: []amqp.ConnOption(nil),
- sessionOpts: []amqp.SessionOption(nil),
- senderLinkOpts: []amqp.LinkOption(nil),
- receiverLinkOpts: []amqp.LinkOption(nil),
- }
- if err := t.applyOptions(opts...); err != nil {
- return nil, err
- }
-
- client, err := amqp.Dial(server, t.connOpts...)
- if err != nil {
- return nil, err
- }
- t.Client = client
-
- // Open a session
- session, err := client.NewSession(t.sessionOpts...)
- if err != nil {
- _ = client.Close()
- return nil, err
- }
- t.Session = session
-
- t.senderLinkOpts = append(t.senderLinkOpts, amqp.LinkTargetAddress(queue))
-
- // Create a sender
- sender, err := session.NewSender(t.senderLinkOpts...)
- if err != nil {
- _ = client.Close()
- _ = session.Close(context.Background())
- return nil, err
- }
- // TODO: in the future we might have more than one sender.
- t.BindingTransport.Sender, t.BindingTransport.SenderContextDecorators = t.applyEncoding(sender)
- return t, nil
-}
-
-func (t *Transport) applyEncoding(amqpSender *amqp.Sender) (binding.Sender, []func(context.Context) context.Context) {
- switch t.Encoding {
- case BinaryV02:
- return bindings_amqp.NewSender(
- amqpSender,
- bindings_amqp.WithTranscoder(transcoder.Version(spec.V02)),
- ), []func(context.Context) context.Context{binding.WithForceBinary}
- case BinaryV03:
- return bindings_amqp.NewSender(
- amqpSender,
- bindings_amqp.WithTranscoder(transcoder.Version(spec.V03)),
- ), []func(context.Context) context.Context{binding.WithForceBinary}
- case BinaryV1:
- return bindings_amqp.NewSender(
- amqpSender,
- bindings_amqp.WithTranscoder(transcoder.Version(spec.V1)),
- ), []func(context.Context) context.Context{binding.WithForceBinary}
- case StructuredV02:
- return bindings_amqp.NewSender(
- amqpSender,
- bindings_amqp.WithTranscoder(transcoder.Version(spec.V02)),
- ), []func(context.Context) context.Context{binding.WithForceStructured}
- case StructuredV03:
- return bindings_amqp.NewSender(
- amqpSender,
- bindings_amqp.WithTranscoder(transcoder.Version(spec.V03)),
- ), []func(context.Context) context.Context{binding.WithForceStructured}
- case StructuredV1:
- return bindings_amqp.NewSender(
- amqpSender,
- bindings_amqp.WithTranscoder(transcoder.Version(spec.V1)),
- ), []func(context.Context) context.Context{binding.WithForceStructured}
- }
- return bindings_amqp.NewSender(amqpSender), []func(context.Context) context.Context{}
-}
-
-func (t *Transport) applyOptions(opts ...Option) error {
- for _, fn := range opts {
- if err := fn(t); err != nil {
- return err
- }
- }
- return nil
-}
-
-// SetConverter implements Transport.SetConverter
-func (t *Transport) SetConverter(c transport.Converter) {
- t.Converter = c
-}
-
-// HasConverter implements Transport.HasConverter
-func (t *Transport) HasConverter() bool {
- return t.Converter != nil
-}
-
-// StartReceiver implements Transport.StartReceiver
-// NOTE: This is a blocking call.
-func (t *Transport) StartReceiver(ctx context.Context) error {
- logger := cecontext.LoggerFrom(ctx)
- logger.Info("StartReceiver on ", t.Node)
-
- t.receiverLinkOpts = append(t.receiverLinkOpts, amqp.LinkSourceAddress(t.Node))
- receiver, err := t.Session.NewReceiver(t.receiverLinkOpts...)
- if err != nil {
- return err
- }
- t.BindingTransport.Receiver = &bindings_amqp.Receiver{AMQP: receiver}
- return t.BindingTransport.StartReceiver(ctx)
-}
-
-// HasTracePropagation implements Transport.HasTracePropagation
-func (t *Transport) HasTracePropagation() bool {
- return false
-}
-
-func (t *Transport) Close() error {
- return t.Client.Close()
-}
diff --git a/v1/cloudevents/transport/amqp/transport_test.go b/v1/cloudevents/transport/amqp/transport_test.go
deleted file mode 100644
index 62d21ddcc..000000000
--- a/v1/cloudevents/transport/amqp/transport_test.go
+++ /dev/null
@@ -1,109 +0,0 @@
-// +build amqp
-
-package amqp_test
-
-import (
- "context"
- "net/url"
- "os"
- "testing"
-
- "github.com/stretchr/testify/require"
-
- "github.com/cloudevents/sdk-go/v1/binding/spec"
- "github.com/cloudevents/sdk-go/v1/binding/test"
- ce "github.com/cloudevents/sdk-go/v1/cloudevents"
- "github.com/cloudevents/sdk-go/v1/cloudevents/transport"
- "github.com/cloudevents/sdk-go/v1/cloudevents/transport/amqp"
- "github.com/cloudevents/sdk-go/v1/cloudevents/types"
-)
-
-// Requires an external AMQP broker or router, skip if not available.
-// The env variable TEST_AMQP_URL provides the URL, default is "/test"
-//
-// One option is http://qpid.apache.org/components/dispatch-router/indext.html.
-// It can be installed from source or from RPMs, see https://qpid.apache.org/packages.html
-// Run `qdrouterd` and the tests will work with no further config.
-func testTransport(t *testing.T, opts ...amqp.Option) *amqp.Transport {
- t.Helper()
- addr := "test"
- s := os.Getenv("TEST_AMQP_URL")
- if u, err := url.Parse(s); err == nil && u.Path != "" {
- addr = u.Path
- }
- transport, err := amqp.New(s, addr, opts...)
- if err != nil {
- t.Skipf("ampq.New(%#v): %v", s, err)
- }
- return transport
-}
-
-type tester struct {
- s, r transport.Transport
- got chan interface{} // ce.Event or error
-}
-
-func (t *tester) Receive(_ context.Context, e ce.Event, _ *ce.EventResponse) error {
- t.got <- e
- return nil
-}
-
-func (t *tester) Close() {
- _ = t.s.(*amqp.Transport).Close()
- _ = t.r.(*amqp.Transport).Close()
-}
-
-func newTester(t *testing.T, sendOpts, recvOpts []amqp.Option) *tester {
- t.Helper()
- tester := &tester{
- s: testTransport(t, sendOpts...),
- r: testTransport(t, recvOpts...),
- got: make(chan interface{}),
- }
- got := make(chan interface{}, 100)
- go func() {
- defer func() { close(got) }()
- tester.r.SetReceiver(tester)
- err := tester.r.StartReceiver(context.Background())
- if err != nil {
- got <- err
- }
- }()
- return tester
-}
-
-func exurl(e ce.Event) ce.Event {
- // Flatten exurl to string, AMQP doesn't preserve the URL type.
- // It should preserve other attribute types.
- if s, _ := types.Format(e.Extensions()["exurl"]); s != "" {
- e.SetExtension("exurl", s)
- }
- return e
-}
-
-func TestSendReceive(t *testing.T) {
- ctx := context.Background()
- tester := newTester(t, nil, nil)
- defer tester.Close()
- test.EachEvent(t, test.Events(), func(t *testing.T, e ce.Event) {
- _, _, err := tester.s.Send(ctx, e)
- require.NoError(t, err)
- got := <-tester.got
- test.AssertEventEquals(t, exurl(e), got.(ce.Event))
- })
-}
-
-func TestWithEncoding(t *testing.T) {
- ctx := context.Background()
- tester := newTester(t, []amqp.Option{amqp.WithEncoding(amqp.StructuredV03)}, nil)
- defer tester.Close()
- // FIXME(alanconway) problem with JSON round-tripping extensions
- events := test.NoExtensions(test.Events())
- test.EachEvent(t, events, func(t *testing.T, e ce.Event) {
- _, _, err := tester.s.Send(ctx, e)
- require.NoError(t, err)
- got := <-tester.got
- e.Context = spec.V03.Convert(e.Context)
- test.AssertEventEquals(t, exurl(e), got.(ce.Event))
- })
-}
diff --git a/v1/cloudevents/transport/codec.go b/v1/cloudevents/transport/codec.go
deleted file mode 100644
index 97d83701b..000000000
--- a/v1/cloudevents/transport/codec.go
+++ /dev/null
@@ -1,35 +0,0 @@
-package transport
-
-import (
- "context"
- "fmt"
-
- "github.com/cloudevents/sdk-go/v1/cloudevents"
-)
-
-// Codec is the interface for transport codecs to convert between transport
-// specific payloads and the Message interface.
-type Codec interface {
- Encode(context.Context, cloudevents.Event) (Message, error)
- Decode(context.Context, Message) (*cloudevents.Event, error)
-}
-
-// ErrMessageEncodingUnknown is an error produced when the encoding for an incoming
-// message can not be understood.
-type ErrMessageEncodingUnknown struct {
- codec string
- transport string
-}
-
-// NewErrMessageEncodingUnknown makes a new ErrMessageEncodingUnknown.
-func NewErrMessageEncodingUnknown(codec, transport string) *ErrMessageEncodingUnknown {
- return &ErrMessageEncodingUnknown{
- codec: codec,
- transport: transport,
- }
-}
-
-// Error implements error.Error
-func (e *ErrMessageEncodingUnknown) Error() string {
- return fmt.Sprintf("message encoding unknown for %s codec on %s transport", e.codec, e.transport)
-}
diff --git a/v1/cloudevents/transport/codec_test.go b/v1/cloudevents/transport/codec_test.go
deleted file mode 100644
index fe81d83d3..000000000
--- a/v1/cloudevents/transport/codec_test.go
+++ /dev/null
@@ -1,9 +0,0 @@
-package transport_test
-
-import (
- "testing"
-)
-
-func TestCodec(t *testing.T) {
- // TODO: add a test. This makes coverage count this dir.
-}
diff --git a/v1/cloudevents/transport/doc.go b/v1/cloudevents/transport/doc.go
deleted file mode 100644
index c2cbadde0..000000000
--- a/v1/cloudevents/transport/doc.go
+++ /dev/null
@@ -1,12 +0,0 @@
-/*
-
-Package transport defines interfaces to decouple the client package
-from transport implementations.
-
-Most event sender and receiver applications should not use this
-package, they should use the client package. This package is for
-infrastructure developers implementing new transports, or intermediary
-components like importers, channels or brokers.
-
-*/
-package transport
diff --git a/v1/cloudevents/transport/error.go b/v1/cloudevents/transport/error.go
deleted file mode 100644
index bb4e8ec9f..000000000
--- a/v1/cloudevents/transport/error.go
+++ /dev/null
@@ -1,37 +0,0 @@
-package transport
-
-import "fmt"
-
-// ErrTransportMessageConversion is an error produced when the transport
-// message can not be converted.
-type ErrTransportMessageConversion struct {
- fatal bool
- handled bool
- transport string
- message string
-}
-
-// NewErrMessageEncodingUnknown makes a new ErrMessageEncodingUnknown.
-func NewErrTransportMessageConversion(transport, message string, handled, fatal bool) *ErrTransportMessageConversion {
- return &ErrTransportMessageConversion{
- transport: transport,
- message: message,
- handled: handled,
- fatal: fatal,
- }
-}
-
-// IsFatal reports if this error should be considered fatal.
-func (e *ErrTransportMessageConversion) IsFatal() bool {
- return e.fatal
-}
-
-// Handled reports if this error should be considered accepted and no further action.
-func (e *ErrTransportMessageConversion) Handled() bool {
- return e.handled
-}
-
-// Error implements error.Error
-func (e *ErrTransportMessageConversion) Error() string {
- return fmt.Sprintf("transport %s failed to convert message: %s", e.transport, e.message)
-}
diff --git a/v1/cloudevents/transport/http/codec.go b/v1/cloudevents/transport/http/codec.go
deleted file mode 100644
index f7eca8cce..000000000
--- a/v1/cloudevents/transport/http/codec.go
+++ /dev/null
@@ -1,152 +0,0 @@
-package http
-
-import (
- "context"
- "errors"
- "fmt"
- "sync"
-
- "github.com/cloudevents/sdk-go/v1/cloudevents"
- cecontext "github.com/cloudevents/sdk-go/v1/cloudevents/context"
- "github.com/cloudevents/sdk-go/v1/cloudevents/transport"
-)
-
-// Codec is the wrapper for all versions of codecs supported by the http
-// transport.
-type Codec struct {
- // Encoding is the setting to inform the DefaultEncodingSelectionFn for
- // selecting a codec.
- Encoding Encoding
-
- // DefaultEncodingSelectionFn allows for encoding selection strategies to be injected.
- DefaultEncodingSelectionFn EncodingSelector
-
- v01 *CodecV01
- v02 *CodecV02
- v03 *CodecV03
- v1 *CodecV1
-
- _v01 sync.Once
- _v02 sync.Once
- _v03 sync.Once
- _v1 sync.Once
-}
-
-// Adheres to Codec
-var _ transport.Codec = (*Codec)(nil)
-
-func (c *Codec) loadCodec(encoding Encoding) (transport.Codec, error) {
- switch encoding {
- case BinaryV01, StructuredV01:
- c._v01.Do(func() {
- c.v01 = &CodecV01{DefaultEncoding: c.Encoding}
- })
- return c.v01, nil
- case BinaryV02, StructuredV02:
- c._v02.Do(func() {
- c.v02 = &CodecV02{DefaultEncoding: c.Encoding}
- })
- return c.v02, nil
- case BinaryV03, StructuredV03, BatchedV03:
- c._v03.Do(func() {
- c.v03 = &CodecV03{DefaultEncoding: c.Encoding}
- })
- return c.v03, nil
- case BinaryV1, StructuredV1, BatchedV1, Default:
- c._v1.Do(func() {
- c.v1 = &CodecV1{DefaultEncoding: c.Encoding}
- })
- return c.v1, nil
- }
- return nil, fmt.Errorf("unknown encoding: %s", encoding)
-}
-
-// Encode encodes the provided event into a transport message.
-func (c *Codec) Encode(ctx context.Context, e cloudevents.Event) (transport.Message, error) {
- encoding := c.Encoding
- if encoding == Default && c.DefaultEncodingSelectionFn != nil {
- encoding = c.DefaultEncodingSelectionFn(ctx, e)
- }
- codec, err := c.loadCodec(encoding)
- if err != nil {
- return nil, err
- }
- ctx = cecontext.WithEncoding(ctx, encoding.Name())
- return codec.Encode(ctx, e)
-}
-
-// Decode converts a provided transport message into an Event, or error.
-func (c *Codec) Decode(ctx context.Context, msg transport.Message) (*cloudevents.Event, error) {
- codec, err := c.loadCodec(c.inspectEncoding(ctx, msg))
- if err != nil {
- return nil, err
- }
- event, err := codec.Decode(ctx, msg)
- if err != nil {
- return nil, err
- }
- return c.convertEvent(event)
-}
-
-// Give the context back as the user expects
-func (c *Codec) convertEvent(event *cloudevents.Event) (*cloudevents.Event, error) {
- if event == nil {
- return nil, errors.New("event is nil, can not convert")
- }
-
- switch c.Encoding {
- case Default:
- return event, nil
- case BinaryV01, StructuredV01:
- ca := event.Context.AsV01()
- event.Context = ca
- return event, nil
- case BinaryV02, StructuredV02:
- ca := event.Context.AsV02()
- event.Context = ca
- return event, nil
- case BinaryV03, StructuredV03, BatchedV03:
- ca := event.Context.AsV03()
- event.Context = ca
- return event, nil
- case BinaryV1, StructuredV1, BatchedV1:
- ca := event.Context.AsV1()
- event.Context = ca
- return event, nil
- default:
- return nil, fmt.Errorf("unknown encoding: %s", c.Encoding)
- }
-}
-
-func (c *Codec) inspectEncoding(ctx context.Context, msg transport.Message) Encoding {
- // Try v1.0.
- _, _ = c.loadCodec(BinaryV1)
- encoding := c.v1.inspectEncoding(ctx, msg)
- if encoding != Unknown {
- return encoding
- }
-
- // Try v0.3.
- _, _ = c.loadCodec(BinaryV03)
- encoding = c.v03.inspectEncoding(ctx, msg)
- if encoding != Unknown {
- return encoding
- }
-
- // Try v0.2.
- _, _ = c.loadCodec(BinaryV02)
- encoding = c.v02.inspectEncoding(ctx, msg)
- if encoding != Unknown {
- return encoding
- }
-
- // Try v0.1 first.
- _, _ = c.loadCodec(BinaryV01)
- encoding = c.v01.inspectEncoding(ctx, msg)
- if encoding != Unknown {
- return encoding
- }
-
- // We do not understand the message encoding.
- return Unknown
-}
diff --git a/v1/cloudevents/transport/http/codec_structured.go b/v1/cloudevents/transport/http/codec_structured.go
deleted file mode 100644
index 097e9361e..000000000
--- a/v1/cloudevents/transport/http/codec_structured.go
+++ /dev/null
@@ -1,44 +0,0 @@
-package http
-
-import (
- "context"
- "encoding/json"
- "fmt"
- "net/http"
-
- "github.com/cloudevents/sdk-go/v1/cloudevents"
- "github.com/cloudevents/sdk-go/v1/cloudevents/transport"
-)
-
-// CodecStructured represents an structured http transport codec for all versions.
-// Intended to be used as a base class.
-type CodecStructured struct {
- DefaultEncoding Encoding
-}
-
-func (v CodecStructured) encodeStructured(ctx context.Context, e cloudevents.Event) (transport.Message, error) {
- header := http.Header{}
- header.Set("Content-Type", cloudevents.ApplicationCloudEventsJSON)
-
- body, err := json.Marshal(e)
- if err != nil {
- return nil, err
- }
-
- msg := &Message{
- Header: header,
- Body: body,
- }
-
- return msg, nil
-}
-
-func (v CodecStructured) decodeStructured(ctx context.Context, version string, msg transport.Message) (*cloudevents.Event, error) {
- m, ok := msg.(*Message)
- if !ok {
- return nil, fmt.Errorf("failed to convert transport.Message to http.Message")
- }
- event := cloudevents.New(version)
- err := json.Unmarshal(m.Body, &event)
- return &event, err
-}
diff --git a/v1/cloudevents/transport/http/codec_test.go b/v1/cloudevents/transport/http/codec_test.go
deleted file mode 100644
index 86dca3c54..000000000
--- a/v1/cloudevents/transport/http/codec_test.go
+++ /dev/null
@@ -1,976 +0,0 @@
-package http_test
-
-import (
- "context"
- "encoding/json"
- "fmt"
- nethttp "net/http"
- "net/url"
- "testing"
- "time"
-
- "github.com/cloudevents/sdk-go/v1/cloudevents"
- "github.com/cloudevents/sdk-go/v1/cloudevents/transport/http"
- "github.com/cloudevents/sdk-go/v1/cloudevents/types"
- "github.com/google/go-cmp/cmp"
-)
-
-func strptr(s string) *string {
- return &s
-}
-
-func TestDefaultBinaryEncodingSelectionStrategy(t *testing.T) {
- testCases := map[string]struct {
- event cloudevents.Event
- want http.Encoding
- }{
- "default, unknown version": {
- event: cloudevents.Event{
- Context: &cloudevents.EventContextV01{
- CloudEventsVersion: "unknown",
- },
- },
- want: http.Default,
- },
- "v0.1": {
- event: cloudevents.Event{
- Context: cloudevents.EventContextV01{}.AsV01(),
- },
- want: http.BinaryV01,
- },
- "v0.2": {
- event: cloudevents.Event{
- Context: cloudevents.EventContextV02{}.AsV02(),
- },
- want: http.BinaryV02,
- },
- "v0.3": {
- event: cloudevents.Event{
- Context: cloudevents.EventContextV03{}.AsV03(),
- },
- want: http.BinaryV03,
- },
- }
- for n, tc := range testCases {
- t.Run(n, func(t *testing.T) {
-
- got := http.DefaultBinaryEncodingSelectionStrategy(context.TODO(), tc.event)
-
- if got != tc.want {
- t.Errorf("unexpected selection want: %s, got: %s", tc.want, got)
- }
- })
- }
-}
-
-func TestDefaultStructuredEncodingSelectionStrategy(t *testing.T) {
- testCases := map[string]struct {
- event cloudevents.Event
- want http.Encoding
- }{
- "default, unknown version": {
- event: cloudevents.Event{
- Context: &cloudevents.EventContextV01{
- CloudEventsVersion: "unknown",
- },
- },
- want: http.Default,
- },
- "v0.1": {
- event: cloudevents.Event{
- Context: cloudevents.EventContextV01{}.AsV01(),
- },
- want: http.StructuredV01,
- },
- "v0.2": {
- event: cloudevents.Event{
- Context: cloudevents.EventContextV02{}.AsV02(),
- },
- want: http.StructuredV02,
- },
- "v0.3": {
- event: cloudevents.Event{
- Context: cloudevents.EventContextV03{}.AsV03(),
- },
- want: http.StructuredV03,
- },
- }
- for n, tc := range testCases {
- t.Run(n, func(t *testing.T) {
-
- got := http.DefaultStructuredEncodingSelectionStrategy(context.TODO(), tc.event)
-
- if got != tc.want {
- t.Errorf("unexpected selection want: %s, got: %s", tc.want, got)
- }
- })
- }
-}
-
-func TestCodecEncode(t *testing.T) {
- sourceUrl, _ := url.Parse("http://example.com/source")
- source := &types.URLRef{URL: *sourceUrl}
-
- testCases := map[string]struct {
- codec *http.Codec
- event cloudevents.Event
- want *http.Message
- wantErr error
- }{
- "default v0.1 binary": {
- codec: &http.Codec{
- DefaultEncodingSelectionFn: http.DefaultBinaryEncodingSelectionStrategy,
- },
- event: cloudevents.Event{
- Context: cloudevents.EventContextV01{
- EventType: "com.example.test",
- Source: *source,
- EventID: "ABC-123",
- }.AsV01(),
- },
- want: &http.Message{
- Header: map[string][]string{
- "CE-CloudEventsVersion": {"0.1"},
- "CE-EventID": {"ABC-123"},
- "CE-EventType": {"com.example.test"},
- "CE-Source": {"http://example.com/source"},
- },
- },
- },
- "default v0.1 structured": {
- codec: &http.Codec{
- DefaultEncodingSelectionFn: http.DefaultStructuredEncodingSelectionStrategy,
- },
- event: cloudevents.Event{
- Context: cloudevents.EventContextV01{
- EventType: "com.example.test",
- Source: *source,
- EventID: "ABC-123",
- }.AsV01(),
- },
- want: &http.Message{
- Header: map[string][]string{
- "Content-Type": {"application/cloudevents+json"},
- },
- Body: func() []byte {
- body := map[string]interface{}{
- "cloudEventsVersion": "0.1",
- "eventID": "ABC-123",
- "eventType": "com.example.test",
- "source": "http://example.com/source",
- }
- return toBytes(body)
- }(),
- },
- },
- "default v0.2 binary": {
- codec: &http.Codec{
- DefaultEncodingSelectionFn: http.DefaultBinaryEncodingSelectionStrategy,
- },
- event: cloudevents.Event{
- Context: cloudevents.EventContextV02{
- Type: "com.example.test",
- Source: *source,
- ID: "ABC-123",
- }.AsV02(),
- },
- want: &http.Message{
- Header: map[string][]string{
- "Ce-Specversion": {"0.2"},
- "Ce-Id": {"ABC-123"},
- "Ce-Type": {"com.example.test"},
- "Ce-Source": {"http://example.com/source"},
- },
- },
- },
- "default v0.2 structured": {
- codec: &http.Codec{
- DefaultEncodingSelectionFn: http.DefaultStructuredEncodingSelectionStrategy,
- },
- event: cloudevents.Event{
- Context: cloudevents.EventContextV02{
- Type: "com.example.test",
- Source: *source,
- ID: "ABC-123",
- }.AsV02(),
- },
- want: &http.Message{
- Header: map[string][]string{
- "Content-Type": {"application/cloudevents+json"},
- },
- Body: func() []byte {
- body := map[string]interface{}{
- "specversion": "0.2",
- "id": "ABC-123",
- "type": "com.example.test",
- "source": "http://example.com/source",
- }
- return toBytes(body)
- }(),
- },
- },
- "default v0.3 binary": {
- codec: &http.Codec{
- DefaultEncodingSelectionFn: http.DefaultBinaryEncodingSelectionStrategy,
- },
- event: cloudevents.Event{
- Context: cloudevents.EventContextV03{
- Type: "com.example.test",
- Source: *source,
- ID: "ABC-123",
- }.AsV03(),
- },
- want: &http.Message{
- Header: map[string][]string{
- "Ce-Specversion": {"0.3"},
- "Ce-Id": {"ABC-123"},
- "Ce-Type": {"com.example.test"},
- "Ce-Source": {"http://example.com/source"},
- },
- },
- },
- "default v0.3 structured": {
- codec: &http.Codec{
- DefaultEncodingSelectionFn: http.DefaultStructuredEncodingSelectionStrategy,
- },
- event: cloudevents.Event{
- Context: cloudevents.EventContextV03{
- Type: "com.example.test",
- Source: *source,
- ID: "ABC-123",
- }.AsV03(),
- },
- want: &http.Message{
- Header: map[string][]string{
- "Content-Type": {"application/cloudevents+json"},
- },
- Body: func() []byte {
- body := map[string]interface{}{
- "specversion": "0.3",
- "id": "ABC-123",
- "type": "com.example.test",
- "source": "http://example.com/source",
- }
- return toBytes(body)
- }(),
- },
- },
- "simple v0.1 binary": {
- codec: &http.Codec{Encoding: http.BinaryV01},
- event: cloudevents.Event{
- Context: &cloudevents.EventContextV01{
- EventType: "com.example.test",
- Source: *source,
- EventID: "ABC-123",
- },
- },
- want: &http.Message{
- Header: map[string][]string{
- "CE-CloudEventsVersion": {"0.1"},
- "CE-EventID": {"ABC-123"},
- "CE-EventType": {"com.example.test"},
- "CE-Source": {"http://example.com/source"},
- },
- },
- },
- "simple v0.1 structured": {
- codec: &http.Codec{Encoding: http.StructuredV01},
- event: cloudevents.Event{
- Context: cloudevents.EventContextV01{
- EventType: "com.example.test",
- Source: *source,
- EventID: "ABC-123",
- }.AsV01(),
- },
- want: &http.Message{
- Header: map[string][]string{
- "Content-Type": {"application/cloudevents+json"},
- },
- Body: func() []byte {
- body := map[string]interface{}{
- "cloudEventsVersion": "0.1",
- "eventID": "ABC-123",
- "eventType": "com.example.test",
- "source": "http://example.com/source",
- }
- return toBytes(body)
- }(),
- },
- },
- "simple v0.2 binary": {
- codec: &http.Codec{Encoding: http.BinaryV02},
- event: cloudevents.Event{
- Context: cloudevents.EventContextV02{
- Type: "com.example.test",
- Source: *source,
- ID: "ABC-123",
- }.AsV02(),
- },
- want: &http.Message{
- Header: map[string][]string{
- "Ce-Specversion": {"0.2"},
- "Ce-Id": {"ABC-123"},
- "Ce-Type": {"com.example.test"},
- "Ce-Source": {"http://example.com/source"},
- },
- },
- },
- "simple v0.2 structured": {
- codec: &http.Codec{Encoding: http.StructuredV02},
- event: cloudevents.Event{
- Context: cloudevents.EventContextV02{
- Type: "com.example.test",
- Source: *source,
- ID: "ABC-123",
- }.AsV02(),
- },
- want: &http.Message{
- Header: map[string][]string{
- "Content-Type": {"application/cloudevents+json"},
- },
- Body: func() []byte {
- body := map[string]interface{}{
- "specversion": "0.2",
- "id": "ABC-123",
- "type": "com.example.test",
- "source": "http://example.com/source",
- }
- return toBytes(body)
- }(),
- },
- },
- "simple v0.3 binary": {
- codec: &http.Codec{Encoding: http.BinaryV03},
- event: cloudevents.Event{
- Context: cloudevents.EventContextV03{
- Type: "com.example.test",
- Source: *source,
- ID: "ABC-123",
- }.AsV03(),
- },
- want: &http.Message{
- Header: map[string][]string{
- "Ce-Specversion": {"0.3"},
- "Ce-Id": {"ABC-123"},
- "Ce-Type": {"com.example.test"},
- "Ce-Source": {"http://example.com/source"},
- },
- },
- },
- "simple v0.3 structured": {
- codec: &http.Codec{Encoding: http.StructuredV03},
- event: cloudevents.Event{
- Context: cloudevents.EventContextV03{
- Type: "com.example.test",
- Source: *source,
- ID: "ABC-123",
- }.AsV03(),
- },
- want: &http.Message{
- Header: map[string][]string{
- "Content-Type": {"application/cloudevents+json"},
- },
- Body: func() []byte {
- body := map[string]interface{}{
- "specversion": "0.3",
- "id": "ABC-123",
- "type": "com.example.test",
- "source": "http://example.com/source",
- }
- return toBytes(body)
- }(),
- },
- },
- }
- for n, tc := range testCases {
- t.Run(n, func(t *testing.T) {
-
- got, err := tc.codec.Encode(context.TODO(), tc.event)
-
- if tc.wantErr != nil || err != nil {
- if diff := cmp.Diff(tc.wantErr, err); diff != "" {
- t.Errorf("unexpected error (-want, +got) = %v", diff)
- }
- return
- }
-
- if diff := cmp.Diff(tc.want, got); diff != "" {
-
- if msg, ok := got.(*http.Message); ok {
- // It is hard to read the byte dump
- want := string(tc.want.Body)
- got := string(msg.Body)
- if diff := cmp.Diff(want, got); diff != "" {
- t.Errorf("unexpected (-want, +got) = %v", diff)
- return
- }
- }
- t.Errorf("unexpected message (-want, +got) = %v", diff)
- }
- })
- }
-}
-
-// A cmp.Transformer to normalize case of http.Header map keys.
-var normalizeHeaders = cmp.Transformer("NormalizeHeaders",
- func(in nethttp.Header) nethttp.Header {
- out := nethttp.Header{}
- for k, v := range in {
- out[nethttp.CanonicalHeaderKey(k)] = v
- }
- return out
- })
-
-func TestCodecDecode(t *testing.T) {
- sourceUrl, _ := url.Parse("http://example.com/source")
- source := &types.URLRef{URL: *sourceUrl}
-
- testCases := map[string]struct {
- codec *http.Codec
- msg *http.Message
- want *cloudevents.Event
- wantErr error
- }{
- "simple v0.1 binary": {
- codec: &http.Codec{Encoding: http.BinaryV01},
- msg: &http.Message{
- Header: map[string][]string{
- "CE-CloudEventsVersion": {"0.1"},
- "CE-EventID": {"ABC-123"},
- "CE-EventType": {"com.example.test"},
- "CE-Source": {"http://example.com/source"},
- "Content-Type": {"application/json"},
- },
- },
- want: &cloudevents.Event{
- Context: &cloudevents.EventContextV01{
- CloudEventsVersion: cloudevents.CloudEventsVersionV01,
- EventType: "com.example.test",
- Source: *source,
- EventID: "ABC-123",
- ContentType: cloudevents.StringOfApplicationJSON(),
- },
- },
- },
- "simple v0.1 structured": {
- codec: &http.Codec{Encoding: http.StructuredV01},
- msg: &http.Message{
- Header: map[string][]string{
- "Content-Type": {"application/cloudevents+json"},
- },
- Body: func() []byte {
- body := map[string]interface{}{
- "cloudEventsVersion": "0.1",
- "eventID": "ABC-123",
- "eventType": "com.example.test",
- "source": "http://example.com/source",
- }
- return toBytes(body)
- }(),
- },
- want: &cloudevents.Event{
- Context: &cloudevents.EventContextV01{
- CloudEventsVersion: cloudevents.CloudEventsVersionV01,
- EventType: "com.example.test",
- Source: *source,
- EventID: "ABC-123",
- },
- },
- },
- "simple v0.2 binary": {
- codec: &http.Codec{Encoding: http.BinaryV02},
- msg: &http.Message{
- Header: map[string][]string{
- "Ce-Specversion": {"0.2"},
- "Ce-Id": {"ABC-123"},
- "Ce-Type": {"com.example.test"},
- "Ce-Source": {"http://example.com/source"},
- "Content-Type": {"application/json"},
- },
- },
- want: &cloudevents.Event{
- Context: &cloudevents.EventContextV02{
- SpecVersion: cloudevents.CloudEventsVersionV02,
- Type: "com.example.test",
- Source: *source,
- ID: "ABC-123",
- ContentType: cloudevents.StringOfApplicationJSON(),
- },
- },
- },
- "simple v0.2 structured": {
- codec: &http.Codec{Encoding: http.StructuredV02},
- msg: &http.Message{
- Header: map[string][]string{
- "Content-Type": {"application/cloudevents+json"},
- },
- Body: func() []byte {
- body := map[string]interface{}{
- "specversion": "0.2",
- "id": "ABC-123",
- "type": "com.example.test",
- "source": "http://example.com/source",
- }
- return toBytes(body)
- }(),
- },
- want: &cloudevents.Event{
- Context: &cloudevents.EventContextV02{
- SpecVersion: cloudevents.CloudEventsVersionV02,
- Type: "com.example.test",
- Source: *source,
- ID: "ABC-123",
- },
- },
- },
-
- "simple v0.3 binary": {
- codec: &http.Codec{Encoding: http.BinaryV03},
- msg: &http.Message{
- Header: map[string][]string{
- "Ce-Specversion": {"0.3"},
- "Ce-Id": {"ABC-123"},
- "Ce-Type": {"com.example.test"},
- "Ce-Source": {"http://example.com/source"},
- "Content-Type": {"application/json"},
- },
- },
- want: &cloudevents.Event{
- Context: &cloudevents.EventContextV03{
- SpecVersion: cloudevents.CloudEventsVersionV03,
- Type: "com.example.test",
- Source: *source,
- ID: "ABC-123",
- DataContentType: cloudevents.StringOfApplicationJSON(),
- },
- },
- },
- "simple v0.3 structured": {
- codec: &http.Codec{Encoding: http.StructuredV03},
- msg: &http.Message{
- Header: map[string][]string{
- "Content-Type": {"application/cloudevents+json"},
- },
- Body: func() []byte {
- body := map[string]interface{}{
- "specversion": "0.3",
- "id": "ABC-123",
- "type": "com.example.test",
- "source": "http://example.com/source",
- }
- return toBytes(body)
- }(),
- },
- want: &cloudevents.Event{
- Context: &cloudevents.EventContextV03{
- SpecVersion: cloudevents.CloudEventsVersionV03,
- Type: "com.example.test",
- Source: *source,
- ID: "ABC-123",
- },
- },
- },
-
- // Conversion tests.
-
- "simple v0.1 binary -> v0.2 binary": {
- codec: &http.Codec{Encoding: http.BinaryV02},
- msg: &http.Message{
- Header: map[string][]string{
- "CE-CloudEventsVersion": {"0.1"},
- "CE-EventID": {"ABC-123"},
- "CE-EventType": {"com.example.test"},
- "CE-Source": {"http://example.com/source"},
- "Content-Type": {"application/json"},
- },
- },
- want: &cloudevents.Event{
- Context: &cloudevents.EventContextV02{
- SpecVersion: cloudevents.CloudEventsVersionV02,
- Type: "com.example.test",
- Source: *source,
- ID: "ABC-123",
- ContentType: cloudevents.StringOfApplicationJSON(),
- },
- },
- },
- "simple v0.1 structured -> v0.2 structured": {
- codec: &http.Codec{Encoding: http.StructuredV02},
- msg: &http.Message{
- Header: map[string][]string{
- "Content-Type": {"application/cloudevents+json"},
- },
- Body: func() []byte {
- body := map[string]interface{}{
- "cloudEventsVersion": "0.1",
- "eventID": "ABC-123",
- "eventType": "com.example.test",
- "source": "http://example.com/source",
- }
- return toBytes(body)
- }(),
- },
- want: &cloudevents.Event{
- Context: &cloudevents.EventContextV02{
- SpecVersion: cloudevents.CloudEventsVersionV02,
- Type: "com.example.test",
- Source: *source,
- ID: "ABC-123",
- },
- },
- },
- "simple v0.2 binary -> v0.1 binary": {
- codec: &http.Codec{Encoding: http.BinaryV01},
- msg: &http.Message{
- Header: map[string][]string{
- "Ce-Specversion": {"0.2"},
- "Ce-Id": {"ABC-123"},
- "Ce-Type": {"com.example.test"},
- "Ce-Source": {"http://example.com/source"},
- "Content-Type": {"application/json"},
- },
- },
- want: &cloudevents.Event{
- Context: &cloudevents.EventContextV01{
- CloudEventsVersion: cloudevents.CloudEventsVersionV01,
- EventType: "com.example.test",
- Source: *source,
- EventID: "ABC-123",
- ContentType: cloudevents.StringOfApplicationJSON(),
- },
- },
- },
- "simple v0.2 structured -> v0.1 structured": {
- codec: &http.Codec{Encoding: http.StructuredV01},
- msg: &http.Message{
- Header: map[string][]string{
- "Content-Type": {"application/cloudevents+json"},
- },
- Body: func() []byte {
- body := map[string]interface{}{
- "specversion": "0.2",
- "id": "ABC-123",
- "type": "com.example.test",
- "source": "http://example.com/source",
- }
- return toBytes(body)
- }(),
- },
- want: &cloudevents.Event{
- Context: &cloudevents.EventContextV01{
- CloudEventsVersion: cloudevents.CloudEventsVersionV01,
- EventType: "com.example.test",
- Source: *source,
- EventID: "ABC-123",
- },
- },
- },
- // TODO:: add the v0.3 conversion tests. Might want to think of a new way to do this.
- // The tests are getting very long...
- }
- for n, tc := range testCases {
- t.Run(n, func(t *testing.T) {
-
- got, err := tc.codec.Decode(context.TODO(), tc.msg)
-
- if tc.wantErr != nil || err != nil {
- if diff := cmp.Diff(tc.wantErr, err); diff != "" {
- t.Errorf("unexpected error (-want, +got) = %v", diff)
- }
- return
- }
-
- if diff := cmp.Diff(tc.want, got); diff != "" {
- t.Errorf("unexpected event (-want, +got) = %v", diff)
- }
- // Round trip thru a http.Request
- var req nethttp.Request
- tc.msg.ToRequest(&req)
- gotm, err := http.NewMessage(req.Header, req.Body)
- if err != nil {
- t.Error(err)
- }
- if diff := cmp.Diff(tc.msg, gotm, normalizeHeaders); diff != "" {
- t.Errorf("unexpected message (-want, +got) = %v", diff)
- }
- })
- }
-}
-
-type DataExample struct {
- AnInt int `json:"a,omitempty"`
- AString string `json:"b,omitempty"`
- AnArray []string `json:"c,omitempty"`
- ATime *time.Time `json:"e,omitempty"`
-}
-
-func TestCodecRoundTrip(t *testing.T) {
- sourceUrl, _ := url.Parse("http://example.com/source")
- source := &types.URLRef{URL: *sourceUrl}
-
- for _, encoding := range []http.Encoding{http.BinaryV01, http.BinaryV02, http.StructuredV01, http.StructuredV02} {
-
- testCases := map[string]struct {
- codec *http.Codec
- event cloudevents.Event
- want cloudevents.Event
- wantErr error
- }{
- "simple data v0.1": {
- codec: &http.Codec{Encoding: encoding},
- event: cloudevents.Event{
- Context: cloudevents.EventContextV01{
- EventType: "com.example.test",
- Source: *source,
- EventID: "ABC-123",
- }.AsV01(),
- Data: map[string]string{
- "a": "apple",
- "b": "banana",
- },
- },
- want: cloudevents.Event{
- Context: cloudevents.EventContextV01{
- CloudEventsVersion: cloudevents.CloudEventsVersionV01,
- EventType: "com.example.test",
- Source: *source,
- EventID: "ABC-123",
- }.AsV01(),
- Data: map[string]interface{}{
- "a": "apple",
- "b": "banana",
- },
- DataEncoded: true,
- },
- },
- "struct data v0.1": {
- codec: &http.Codec{Encoding: encoding},
- event: cloudevents.Event{
- Context: cloudevents.EventContextV01{
- EventType: "com.example.test",
- Source: *source,
- EventID: "ABC-123",
- }.AsV01(),
- Data: DataExample{
- AnInt: 42,
- AString: "testing",
- },
- },
- want: cloudevents.Event{
- Context: cloudevents.EventContextV01{
- CloudEventsVersion: cloudevents.CloudEventsVersionV01,
- EventType: "com.example.test",
- Source: *source,
- EventID: "ABC-123",
- }.AsV01(),
- Data: &DataExample{
- AnInt: 42,
- AString: "testing",
- },
- DataEncoded: true,
- },
- },
- // TODO: add tests for other versions. (note not really needed because these is tested internally too)
- }
- for n, tc := range testCases {
- n = fmt.Sprintf("%s, %s", encoding, n)
- t.Run(n, func(t *testing.T) {
-
- msg, err := tc.codec.Encode(context.TODO(), tc.event)
- if err != nil {
- if diff := cmp.Diff(tc.wantErr, err); diff != "" {
- t.Errorf("unexpected error (-want, +got) = %v", diff)
- }
- return
- }
-
- got, err := tc.codec.Decode(context.TODO(), msg)
- if err != nil {
- if diff := cmp.Diff(tc.wantErr, err); diff != "" {
- t.Errorf("unexpected error (-want, +got) = %v", diff)
- }
- return
- }
-
- if tc.event.Data != nil {
- // We have to be pretty tricky in the test to get the correct type.
- data, _ := types.Allocate(tc.want.Data)
- err := got.DataAs(&data)
- if err != nil {
- if diff := cmp.Diff(tc.wantErr, err); diff != "" {
- t.Errorf("unexpected error (-want, +got) = %v", diff)
- }
- return
- }
- got.Data = data
- }
-
- if tc.wantErr != nil {
- if diff := cmp.Diff(tc.wantErr, err); diff != "" {
- t.Errorf("unexpected error (-want, +got) = %v", diff)
- }
- return
- }
-
- // fix the context back to v1 to test.
- ctxv1 := got.Context.AsV01()
- got.Context = ctxv1
-
- if diff := cmp.Diff(tc.want, *got); diff != "" {
- t.Errorf("unexpected event (-want, +got) = %v", diff)
- }
- })
- }
-
- }
-}
-
-// Tests v0.1 -> X -> v0.1
-func TestCodecAsMiddleware(t *testing.T) {
- sourceUrl, _ := url.Parse("http://example.com/source")
- source := &types.URLRef{URL: *sourceUrl}
-
- for _, contentType := range []string{"application/json", "application/xml"} {
- for _, encoding := range []http.Encoding{http.BinaryV01, http.BinaryV02, http.BinaryV03, http.StructuredV01, http.StructuredV02, http.StructuredV03} {
-
- testCases := map[string]struct {
- codec *http.Codec
- event cloudevents.Event
- want cloudevents.Event
- wantErr error
- }{
- // TODO: this is commented out because xml does not support maps.
- //"simple data": {
- // codec: http.Codec{Encoding: encoding},
- // event: cloudevents.Event{
- // Context: &cloudevents.EventContextV01{
- // EventType: "com.example.test",
- // Source: *source,
- // EventID: "ABC-123",
- // ContentType: contentType,
- // },
- // Data: map[string]string{
- // "a": "apple",
- // "b": "banana",
- // },
- // },
- // want: cloudevents.Event{
- // Context: &cloudevents.EventContextV01{
- // CloudEventsVersion: cloudevents.CloudEventsVersionV01,
- // EventType: "com.example.test",
- // Source: *source,
- // EventID: "ABC-123",
- // ContentType: contentType,
- // },
- // Data: map[string]interface{}{
- // "a": "apple",
- // "b": "banana",
- // },
- // },
- //},
- "struct data": {
- codec: &http.Codec{Encoding: encoding},
- event: cloudevents.Event{
- Context: cloudevents.EventContextV01{
- EventType: "com.example.test",
- Source: *source,
- EventID: "ABC-123",
- ContentType: strptr(contentType),
- }.AsV01(),
- Data: DataExample{
- AnInt: 42,
- AString: "testing",
- },
- },
- want: cloudevents.Event{
- Context: &cloudevents.EventContextV01{
- CloudEventsVersion: cloudevents.CloudEventsVersionV01,
- EventType: "com.example.test",
- Source: *source,
- EventID: "ABC-123",
- ContentType: strptr(contentType),
- },
- Data: &DataExample{
- AnInt: 42,
- AString: "testing",
- },
- DataEncoded: true,
- },
- },
- }
- for n, tc := range testCases {
- n = fmt.Sprintf("%s[%s],%s", encoding, contentType, n)
- t.Run(n, func(t *testing.T) {
-
- msg1, err := tc.codec.Encode(context.TODO(), tc.event)
- if err != nil {
- if diff := cmp.Diff(tc.wantErr, err); diff != "" {
- t.Errorf("unexpected error (-want, +got) = %v", diff)
- }
- return
- }
-
- midEvent, err := tc.codec.Decode(context.TODO(), msg1)
- if err != nil {
- if diff := cmp.Diff(tc.wantErr, err); diff != "" {
- t.Errorf("unexpected error (-want, +got) = %v", diff)
- }
- return
- }
-
- msg2, err := tc.codec.Encode(context.TODO(), *midEvent)
- if err != nil {
- if diff := cmp.Diff(tc.wantErr, err); diff != "" {
- t.Errorf("unexpected error (-want, +got) = %v", diff)
- }
- return
- }
-
- got, err := tc.codec.Decode(context.TODO(), msg2)
- if err != nil {
- if diff := cmp.Diff(tc.wantErr, err); diff != "" {
- t.Errorf("unexpected error (-want, +got) = %v", diff)
- }
- return
- }
-
- if tc.event.Data != nil {
- // We have to be pretty tricky in the test to get the correct type.
- data, _ := types.Allocate(tc.want.Data)
- err := got.DataAs(&data)
- if err != nil {
- if diff := cmp.Diff(tc.wantErr, err); diff != "" {
- t.Errorf("unexpected error (-want, +got) = %v", diff)
- }
- return
- }
- got.Data = data
- }
-
- if tc.wantErr != nil {
- if diff := cmp.Diff(tc.wantErr, err); diff != "" {
- t.Errorf("unexpected error (-want, +got) = %v", diff)
- }
- return
- }
-
- // fix the context back to v1 to test.
- ctxv1 := got.Context.AsV01()
- got.Context = ctxv1
-
- if diff := cmp.Diff(tc.want, *got); diff != "" {
- t.Errorf("unexpected event (-want, +got) = %v", diff)
- }
- })
- }
- }
- }
-}
-
-func toBytes(body map[string]interface{}) []byte {
- b, err := json.Marshal(body)
- if err != nil {
- return []byte(fmt.Sprintf(`{"error":%q}`, err.Error()))
- }
- return b
-}
diff --git a/v1/cloudevents/transport/http/codec_v01.go b/v1/cloudevents/transport/http/codec_v01.go
deleted file mode 100644
index 78da7b2fb..000000000
--- a/v1/cloudevents/transport/http/codec_v01.go
+++ /dev/null
@@ -1,232 +0,0 @@
-package http
-
-import (
- "context"
- "encoding/json"
- "fmt"
- "net/http"
- "net/textproto"
- "strings"
-
- "github.com/cloudevents/sdk-go/v1/cloudevents"
- cecontext "github.com/cloudevents/sdk-go/v1/cloudevents/context"
- "github.com/cloudevents/sdk-go/v1/cloudevents/observability"
- "github.com/cloudevents/sdk-go/v1/cloudevents/transport"
- "github.com/cloudevents/sdk-go/v1/cloudevents/types"
-)
-
-// CodecV01 represents a http transport codec that uses CloudEvents spec v0.1
-type CodecV01 struct {
- CodecStructured
-
- DefaultEncoding Encoding
-}
-
-// Adheres to Codec
-var _ transport.Codec = (*CodecV01)(nil)
-
-// Encode implements Codec.Encode
-func (v CodecV01) Encode(ctx context.Context, e cloudevents.Event) (transport.Message, error) {
- encoding := v.DefaultEncoding
- strEnc := cecontext.EncodingFrom(ctx)
- if strEnc != "" {
- switch strEnc {
- case Binary:
- encoding = BinaryV01
- case Structured:
- encoding = StructuredV01
- }
- }
-
- _, r := observability.NewReporter(context.Background(), CodecObserved{o: reportEncode, c: encoding.Codec()})
- m, err := v.obsEncode(ctx, e, encoding)
- if err != nil {
- r.Error()
- } else {
- r.OK()
- }
- return m, err
-}
-
-func (v CodecV01) obsEncode(ctx context.Context, e cloudevents.Event, encoding Encoding) (transport.Message, error) {
- switch encoding {
- case Default:
- fallthrough
- case BinaryV01:
- return v.encodeBinary(ctx, e)
- case StructuredV01:
- return v.encodeStructured(ctx, e)
- default:
- return nil, fmt.Errorf("unknown encoding: %d", encoding)
- }
-}
-
-// Decode implements Codec.Decode
-func (v CodecV01) Decode(ctx context.Context, msg transport.Message) (*cloudevents.Event, error) {
- _, r := observability.NewReporter(ctx, CodecObserved{o: reportDecode, c: v.inspectEncoding(ctx, msg).Codec()}) // TODO: inspectEncoding is not free.
- e, err := v.obsDecode(ctx, msg)
- if err != nil {
- r.Error()
- } else {
- r.OK()
- }
- return e, err
-}
-
-func (v CodecV01) obsDecode(ctx context.Context, msg transport.Message) (*cloudevents.Event, error) {
- switch v.inspectEncoding(ctx, msg) {
- case BinaryV01:
- return v.decodeBinary(ctx, msg)
- case StructuredV01:
- return v.decodeStructured(ctx, cloudevents.CloudEventsVersionV01, msg)
- default:
- return nil, transport.NewErrMessageEncodingUnknown("v01", TransportName)
- }
-}
-
-func (v CodecV01) encodeBinary(ctx context.Context, e cloudevents.Event) (transport.Message, error) {
- header, err := v.toHeaders(e.Context.AsV01())
- if err != nil {
- return nil, err
- }
-
- body, err := e.DataBytes()
- if err != nil {
- panic("encode")
- }
-
- msg := &Message{
- Header: header,
- Body: body,
- }
-
- return msg, nil
-}
-
-func (v CodecV01) toHeaders(ec *cloudevents.EventContextV01) (http.Header, error) {
- // Preserve case in v0.1, even though HTTP headers are case-insensitive.
- h := http.Header{}
- h["CE-CloudEventsVersion"] = []string{ec.CloudEventsVersion}
- h["CE-EventID"] = []string{ec.EventID}
- h["CE-EventType"] = []string{ec.EventType}
- h["CE-Source"] = []string{ec.Source.String()}
- if ec.EventTime != nil && !ec.EventTime.IsZero() {
- h["CE-EventTime"] = []string{ec.EventTime.String()}
- }
- if ec.EventTypeVersion != nil {
- h["CE-EventTypeVersion"] = []string{*ec.EventTypeVersion}
- }
- if ec.SchemaURL != nil {
- h["CE-DataSchema"] = []string{ec.SchemaURL.String()}
- }
- if ec.ContentType != nil && *ec.ContentType != "" {
- h.Set("Content-Type", *ec.ContentType)
- }
-
- // Regarding Extensions, v0.1 Spec says the following:
- // * Each map entry name MUST be prefixed with "CE-X-"
- // * Each map entry name's first character MUST be capitalized
- for k, v := range ec.Extensions {
- encoded, err := json.Marshal(v)
- if err != nil {
- return nil, err
- }
- h["CE-X-"+strings.Title(k)] = []string{string(encoded)}
- }
- return h, nil
-}
-
-func (v CodecV01) decodeBinary(ctx context.Context, msg transport.Message) (*cloudevents.Event, error) {
- m, ok := msg.(*Message)
- if !ok {
- return nil, fmt.Errorf("failed to convert transport.Message to http.Message")
- }
- ca, err := v.fromHeaders(m.Header)
- if err != nil {
- return nil, err
- }
- var body interface{}
- if len(m.Body) > 0 {
- body = m.Body
- }
- return &cloudevents.Event{
- Context: &ca,
- Data: body,
- DataEncoded: body != nil,
- }, nil
-}
-
-func (v CodecV01) fromHeaders(h http.Header) (cloudevents.EventContextV01, error) {
- // Normalize headers.
- for k, v := range h {
- ck := textproto.CanonicalMIMEHeaderKey(k)
- if k != ck {
- h[ck] = v
- }
- }
-
- ec := cloudevents.EventContextV01{}
- ec.CloudEventsVersion = h.Get("CE-CloudEventsVersion")
- h.Del("CE-CloudEventsVersion")
- ec.EventID = h.Get("CE-EventID")
- h.Del("CE-EventID")
- ec.EventType = h.Get("CE-EventType")
- h.Del("CE-EventType")
- source := types.ParseURLRef(h.Get("CE-Source"))
- h.Del("CE-Source")
- if source != nil {
- ec.Source = *source
- }
- var err error
- ec.EventTime, err = types.ParseTimestamp(h.Get("CE-EventTime"))
- if err != nil {
- return ec, err
- }
- h.Del("CE-EventTime")
- etv := h.Get("CE-EventTypeVersion")
- h.Del("CE-EventTypeVersion")
- if etv != "" {
- ec.EventTypeVersion = &etv
- }
- ec.SchemaURL = types.ParseURLRef(h.Get("CE-DataSchema"))
- h.Del("CE-DataSchema")
- et := h.Get("Content-Type")
- if et != "" {
- ec.ContentType = &et
- }
-
- extensions := make(map[string]interface{})
- for k, v := range h {
- if len(k) > len("CE-X-") && strings.EqualFold(k[:len("CE-X-")], "CE-X-") {
- key := k[len("CE-X-"):]
- var tmp interface{}
- if err := json.Unmarshal([]byte(v[0]), &tmp); err == nil {
- extensions[key] = tmp
- } else {
- // If we can't unmarshal the data, treat it as a string.
- extensions[key] = v[0]
- }
- h.Del(k)
- }
- }
- if len(extensions) > 0 {
- ec.Extensions = extensions
- }
- return ec, nil
-}
-
-func (v CodecV01) inspectEncoding(ctx context.Context, msg transport.Message) Encoding {
- version := msg.CloudEventsVersion()
- if version != cloudevents.CloudEventsVersionV01 {
- return Unknown
- }
- m, ok := msg.(*Message)
- if !ok {
- return Unknown
- }
- contentType := m.Header.Get("Content-Type")
- if contentType == cloudevents.ApplicationCloudEventsJSON {
- return StructuredV01
- }
- return BinaryV01
-}
diff --git a/v1/cloudevents/transport/http/codec_v01_test.go b/v1/cloudevents/transport/http/codec_v01_test.go
deleted file mode 100644
index aac2d5283..000000000
--- a/v1/cloudevents/transport/http/codec_v01_test.go
+++ /dev/null
@@ -1,410 +0,0 @@
-package http_test
-
-import (
- "context"
- "net/url"
- "testing"
- "time"
-
- "github.com/cloudevents/sdk-go/v1/cloudevents"
- "github.com/cloudevents/sdk-go/v1/cloudevents/transport/http"
- "github.com/cloudevents/sdk-go/v1/cloudevents/types"
- "github.com/google/go-cmp/cmp"
-)
-
-func TestCodecV01_Encode(t *testing.T) {
- now := types.Timestamp{Time: time.Now().UTC()}
- sourceUrl, _ := url.Parse("http://example.com/source")
- source := &types.URLRef{URL: *sourceUrl}
-
- schemaUrl, _ := url.Parse("http://example.com/schema")
- schema := &types.URLRef{URL: *schemaUrl}
-
- testCases := map[string]struct {
- codec http.CodecV01
- event cloudevents.Event
- want *http.Message
- wantErr error
- }{
- "simple v0.1 default": {
- codec: http.CodecV01{},
- event: cloudevents.Event{
- Context: cloudevents.EventContextV01{
- CloudEventsVersion: "TestIfDefaulted",
- EventType: "com.example.test",
- Source: *source,
- EventID: "ABC-123",
- }.AsV01(),
- },
- want: &http.Message{
- Header: map[string][]string{
- "CE-CloudEventsVersion": {"0.1"},
- "CE-EventID": {"ABC-123"},
- "CE-EventType": {"com.example.test"},
- "CE-Source": {"http://example.com/source"},
- },
- },
- },
- "full v0.1 default": {
- codec: http.CodecV01{},
- event: cloudevents.Event{
- Context: cloudevents.EventContextV01{
- EventID: "ABC-123",
- EventTime: &now,
- EventType: "com.example.full",
- EventTypeVersion: strptr("v1alpha1"),
- SchemaURL: schema,
- ContentType: cloudevents.StringOfApplicationJSON(),
- Source: *source,
- Extensions: map[string]interface{}{
- "test": "extended",
- },
- }.AsV01(),
- Data: map[string]interface{}{
- "hello": "world",
- },
- },
- want: &http.Message{
- Header: map[string][]string{
- "CE-CloudEventsVersion": {"0.1"},
- "CE-EventID": {"ABC-123"},
- "CE-EventTime": {now.UTC().Format(time.RFC3339Nano)},
- "CE-EventType": {"com.example.full"},
- "CE-EventTypeVersion": {"v1alpha1"},
- "CE-Source": {"http://example.com/source"},
- "CE-DataSchema": {"http://example.com/schema"},
- "Content-Type": {"application/json"},
- "CE-X-Test": {`"extended"`},
- },
- Body: []byte(`{"hello":"world"}`),
- },
- },
- "simple v0.1 binary": {
- codec: http.CodecV01{DefaultEncoding: http.BinaryV01},
- event: cloudevents.Event{
- Context: &cloudevents.EventContextV01{
- EventType: "com.example.test",
- Source: *source,
- EventID: "ABC-123",
- },
- },
- want: &http.Message{
- Header: map[string][]string{
- "CE-CloudEventsVersion": {"0.1"},
- "CE-EventID": {"ABC-123"},
- "CE-EventType": {"com.example.test"},
- "CE-Source": {"http://example.com/source"},
- },
- },
- },
- "full v0.1 binary": {
- codec: http.CodecV01{DefaultEncoding: http.BinaryV01},
- event: cloudevents.Event{
- Context: cloudevents.EventContextV01{
- EventID: "ABC-123",
- EventTime: &now,
- EventType: "com.example.full",
- EventTypeVersion: strptr("v1alpha1"),
- SchemaURL: schema,
- ContentType: cloudevents.StringOfApplicationJSON(),
- Source: *source,
- Extensions: map[string]interface{}{
- "test": "extended",
- },
- }.AsV01(),
- Data: map[string]interface{}{
- "hello": "world",
- },
- },
- want: &http.Message{
- Header: map[string][]string{
- "CE-CloudEventsVersion": {"0.1"},
- "CE-EventID": {"ABC-123"},
- "CE-EventTime": {now.UTC().Format(time.RFC3339Nano)},
- "CE-EventType": {"com.example.full"},
- "CE-EventTypeVersion": {"v1alpha1"},
- "CE-Source": {"http://example.com/source"},
- "CE-DataSchema": {"http://example.com/schema"},
- "Content-Type": {"application/json"},
- "CE-X-Test": {`"extended"`},
- },
- Body: []byte(`{"hello":"world"}`),
- },
- },
- "simple v0.1 structured": {
- codec: http.CodecV01{DefaultEncoding: http.StructuredV01},
- event: cloudevents.Event{
- Context: cloudevents.EventContextV01{
- EventType: "com.example.test",
- Source: *source,
- EventID: "ABC-123",
- }.AsV01(),
- },
- want: &http.Message{
- Header: map[string][]string{
- "Content-Type": {"application/cloudevents+json"},
- },
- Body: func() []byte {
- body := map[string]interface{}{
- "cloudEventsVersion": "0.1",
- "eventID": "ABC-123",
- "eventType": "com.example.test",
- "source": "http://example.com/source",
- }
- return toBytes(body)
- }(),
- },
- },
- "full v0.1 structured": {
- codec: http.CodecV01{DefaultEncoding: http.StructuredV01},
- event: cloudevents.Event{
- Context: cloudevents.EventContextV01{
- EventID: "ABC-123",
- EventTime: &now,
- EventType: "com.example.full",
- EventTypeVersion: strptr("v1alpha1"),
- SchemaURL: schema,
- ContentType: cloudevents.StringOfApplicationJSON(),
- Source: *source,
- Extensions: map[string]interface{}{
- "test": "extended",
- },
- }.AsV01(),
- Data: map[string]interface{}{
- "hello": "world",
- },
- },
- want: &http.Message{
- Header: map[string][]string{
- "Content-Type": {"application/cloudevents+json"},
- },
- Body: func() []byte {
- body := map[string]interface{}{
- "cloudEventsVersion": "0.1",
- "contentType": "application/json",
- "data": map[string]interface{}{
- "hello": "world",
- },
- "eventID": "ABC-123",
- "eventTime": now,
- "eventType": "com.example.full",
- "eventTypeVersion": "v1alpha1",
- "extensions": map[string]interface{}{
- "test": "extended",
- },
- "schemaURL": "http://example.com/schema",
- "source": "http://example.com/source",
- }
- return toBytes(body)
- }(),
- },
- },
- }
- for n, tc := range testCases {
- t.Run(n, func(t *testing.T) {
-
- got, err := tc.codec.Encode(context.TODO(), tc.event)
-
- if tc.wantErr != nil || err != nil {
- if diff := cmp.Diff(tc.wantErr, err); diff != "" {
- t.Errorf("unexpected error (-want, +got) = %v", diff)
- }
- return
- }
-
- if diff := cmp.Diff(tc.want, got); diff != "" {
-
- if msg, ok := got.(*http.Message); ok {
- // It is hard to read the byte dump
- want := string(tc.want.Body)
- got := string(msg.Body)
- if diff := cmp.Diff(want, got); diff != "" {
- t.Errorf("unexpected message body (-want, +got) = %v", diff)
- return
- }
- }
- t.Errorf("unexpected message (-want, +got) = %v", diff)
- }
- })
- }
-}
-
-func TestCodecV01_Decode(t *testing.T) {
- now := types.Timestamp{Time: time.Now()}
- sourceUrl, _ := url.Parse("http://example.com/source")
- source := &types.URLRef{URL: *sourceUrl}
-
- schemaUrl, _ := url.Parse("http://example.com/schema")
- schema := &types.URLRef{URL: *schemaUrl}
-
- testCases := map[string]struct {
- codec http.CodecV01
- msg *http.Message
- want *cloudevents.Event
- wantErr error
- }{
- "simple v0.1 binary": {
- codec: http.CodecV01{},
- msg: &http.Message{
- Header: map[string][]string{
- "CE-CloudEventsVersion": {"0.1"},
- "CE-EventID": {"ABC-123"},
- "CE-EventType": {"com.example.test"},
- "CE-Source": {"http://example.com/source"},
- "Content-Type": {"application/json"},
- },
- },
- want: &cloudevents.Event{
- Context: &cloudevents.EventContextV01{
- CloudEventsVersion: cloudevents.CloudEventsVersionV01,
- EventType: "com.example.test",
- Source: *source,
- EventID: "ABC-123",
- ContentType: cloudevents.StringOfApplicationJSON(),
- },
- },
- },
- "full v0.1 binary": {
- codec: http.CodecV01{},
- msg: &http.Message{
- Header: map[string][]string{
- "CE-CloudEventsVersion": {"0.1"},
- "CE-EventID": {"ABC-123"},
- "CE-EventTime": {now.UTC().Format(time.RFC3339Nano)},
- "CE-EventType": {"com.example.full"},
- "CE-EventTypeVersion": {"v1alpha1"},
- "CE-Source": {"http://example.com/source"},
- "CE-DataSchema": {"http://example.com/schema"},
- "Content-Type": {"application/json"},
- "CE-X-Test": {`"extended"`},
- },
- Body: toBytes(map[string]interface{}{
- "hello": "world",
- }),
- },
- want: &cloudevents.Event{
- Context: &cloudevents.EventContextV01{
- CloudEventsVersion: cloudevents.CloudEventsVersionV01,
- EventID: "ABC-123",
- EventTime: &now,
- EventType: "com.example.full",
- EventTypeVersion: strptr("v1alpha1"),
- SchemaURL: schema,
- ContentType: cloudevents.StringOfApplicationJSON(),
- Source: *source,
- Extensions: map[string]interface{}{
- "Test": "extended",
- },
- },
- Data: toBytes(map[string]interface{}{
- "hello": "world",
- }),
- DataEncoded: true,
- },
- },
- "simple v0.1 structured": {
- codec: http.CodecV01{},
- msg: &http.Message{
- Header: map[string][]string{
- "Content-Type": {"application/cloudevents+json"},
- },
- Body: toBytes(map[string]interface{}{
- "cloudEventsVersion": "0.1",
- "eventID": "ABC-123",
- "eventType": "com.example.test",
- "source": "http://example.com/source",
- }),
- },
- want: &cloudevents.Event{
- Context: &cloudevents.EventContextV01{
- CloudEventsVersion: cloudevents.CloudEventsVersionV01,
- EventType: "com.example.test",
- Source: *source,
- EventID: "ABC-123",
- },
- },
- },
- "full v0.1 structured": {
- codec: http.CodecV01{},
- msg: &http.Message{
- Header: map[string][]string{
- "Content-Type": {"application/cloudevents+json"},
- },
- Body: toBytes(map[string]interface{}{
- "cloudEventsVersion": "0.1",
- "contentType": "application/json",
- "data": map[string]interface{}{
- "hello": "world",
- },
- "eventID": "ABC-123",
- "eventTime": now,
- "eventType": "com.example.full",
- "eventTypeVersion": "v1alpha1",
- "extensions": map[string]interface{}{
- "test": "extended",
- },
- "schemaURL": "http://example.com/schema",
- "source": "http://example.com/source",
- }),
- },
- want: &cloudevents.Event{
- Context: &cloudevents.EventContextV01{
- CloudEventsVersion: cloudevents.CloudEventsVersionV01,
- EventID: "ABC-123",
- EventTime: &now,
- EventType: "com.example.full",
- EventTypeVersion: strptr("v1alpha1"),
- SchemaURL: schema,
- ContentType: cloudevents.StringOfApplicationJSON(),
- Source: *source,
- Extensions: map[string]interface{}{
- "test": "extended",
- },
- },
- Data: toBytes(map[string]interface{}{
- "hello": "world",
- }),
- DataEncoded: true,
- },
- },
- "simple v0.1 binary with short header": {
- codec: http.CodecV01{},
- msg: &http.Message{
- Header: map[string][]string{
- "CE-CloudEventsVersion": {"0.1"},
- "CE-EventID": {"ABC-123"},
- "CE-EventType": {"com.example.test"},
- "CE-Source": {"http://example.com/source"},
- "Content-Type": {"application/json"},
- "X": {"Notice how short the header's name is"},
- },
- },
- want: &cloudevents.Event{
- Context: &cloudevents.EventContextV01{
- CloudEventsVersion: cloudevents.CloudEventsVersionV01,
- EventType: "com.example.test",
- Source: *source,
- EventID: "ABC-123",
- ContentType: cloudevents.StringOfApplicationJSON(),
- },
- },
- },
- }
- for n, tc := range testCases {
- t.Run(n, func(t *testing.T) {
-
- got, err := tc.codec.Decode(context.TODO(), tc.msg)
-
- if tc.wantErr != nil || err != nil {
- if diff := cmp.Diff(tc.wantErr, err); diff != "" {
- t.Errorf("unexpected error (-want, +got) = %v", diff)
- }
- return
- }
-
- if diff := cmp.Diff(tc.want, got); diff != "" {
- t.Errorf("unexpected event (-want, +got) = %v", diff)
- }
- })
- }
-}
diff --git a/v1/cloudevents/transport/http/codec_v02.go b/v1/cloudevents/transport/http/codec_v02.go
deleted file mode 100644
index 8f4328a76..000000000
--- a/v1/cloudevents/transport/http/codec_v02.go
+++ /dev/null
@@ -1,261 +0,0 @@
-package http
-
-import (
- "context"
- "encoding/json"
- "fmt"
- "net/http"
- "net/textproto"
- "strings"
-
- "github.com/cloudevents/sdk-go/v1/cloudevents"
- cecontext "github.com/cloudevents/sdk-go/v1/cloudevents/context"
- "github.com/cloudevents/sdk-go/v1/cloudevents/observability"
- "github.com/cloudevents/sdk-go/v1/cloudevents/transport"
- "github.com/cloudevents/sdk-go/v1/cloudevents/types"
-)
-
-// CodecV02 represents a http transport codec that uses CloudEvents spec v0.2
-type CodecV02 struct {
- CodecStructured
-
- DefaultEncoding Encoding
-}
-
-// Adheres to Codec
-var _ transport.Codec = (*CodecV02)(nil)
-
-// Encode implements Codec.Encode
-func (v CodecV02) Encode(ctx context.Context, e cloudevents.Event) (transport.Message, error) {
- encoding := v.DefaultEncoding
- strEnc := cecontext.EncodingFrom(ctx)
- if strEnc != "" {
- switch strEnc {
- case Binary:
- encoding = BinaryV02
- case Structured:
- encoding = StructuredV02
- }
- }
-
- _, r := observability.NewReporter(ctx, CodecObserved{o: reportEncode, c: encoding.Codec()})
- m, err := v.obsEncode(ctx, e, encoding)
- if err != nil {
- r.Error()
- } else {
- r.OK()
- }
- return m, err
-}
-
-func (v CodecV02) obsEncode(ctx context.Context, e cloudevents.Event, encoding Encoding) (transport.Message, error) {
- switch encoding {
- case Default:
- fallthrough
- case BinaryV02:
- return v.encodeBinary(ctx, e)
- case StructuredV02:
- return v.encodeStructured(ctx, e)
- default:
- return nil, fmt.Errorf("unknown encoding: %d", encoding)
- }
-}
-
-// Decode implements Codec.Decode
-func (v CodecV02) Decode(ctx context.Context, msg transport.Message) (*cloudevents.Event, error) {
- _, r := observability.NewReporter(ctx, CodecObserved{o: reportDecode, c: v.inspectEncoding(ctx, msg).Codec()}) // TODO: inspectEncoding is not free.
- e, err := v.obsDecode(ctx, msg)
- if err != nil {
- r.Error()
- } else {
- r.OK()
- }
- return e, err
-}
-
-func (v CodecV02) obsDecode(ctx context.Context, msg transport.Message) (*cloudevents.Event, error) {
- switch v.inspectEncoding(ctx, msg) {
- case BinaryV02:
- return v.decodeBinary(ctx, msg)
- case StructuredV02:
- return v.decodeStructured(ctx, cloudevents.CloudEventsVersionV02, msg)
- default:
- return nil, transport.NewErrMessageEncodingUnknown("v02", TransportName)
- }
-}
-
-func (v CodecV02) encodeBinary(ctx context.Context, e cloudevents.Event) (transport.Message, error) {
- header, err := v.toHeaders(e.Context.AsV02())
- if err != nil {
- return nil, err
- }
- body, err := e.DataBytes()
- if err != nil {
- return nil, err
- }
-
- msg := &Message{
- Header: header,
- Body: body,
- }
-
- return msg, nil
-}
-
-func (v CodecV02) toHeaders(ec *cloudevents.EventContextV02) (http.Header, error) {
- h := http.Header{}
- h.Set("ce-specversion", ec.SpecVersion)
- h.Set("ce-type", ec.Type)
- h.Set("ce-source", ec.Source.String())
- h.Set("ce-id", ec.ID)
- if ec.Time != nil && !ec.Time.IsZero() {
- h.Set("ce-time", ec.Time.String())
- }
- if ec.SchemaURL != nil {
- h.Set("ce-schemaurl", ec.SchemaURL.String())
- }
- if ec.ContentType != nil && *ec.ContentType != "" {
- h.Set("Content-Type", *ec.ContentType)
- }
- for k, v := range ec.Extensions {
- // Per spec, map-valued extensions are converted to a list of headers as:
- // CE-attrib-key
- if mapVal, ok := v.(map[string]interface{}); ok {
- for subkey, subval := range mapVal {
- encoded, err := json.Marshal(subval)
- if err != nil {
- return nil, err
- }
- h.Set("ce-"+k+"-"+subkey, string(encoded))
- }
- continue
- }
- encoded, err := json.Marshal(v)
- if err != nil {
- return nil, err
- }
- h.Set("ce-"+k, string(encoded))
- }
-
- return h, nil
-}
-
-func (v CodecV02) decodeBinary(ctx context.Context, msg transport.Message) (*cloudevents.Event, error) {
- m, ok := msg.(*Message)
- if !ok {
- return nil, fmt.Errorf("failed to convert transport.Message to http.Message")
- }
- ca, err := v.fromHeaders(m.Header)
- if err != nil {
- return nil, err
- }
- var body interface{}
- if len(m.Body) > 0 {
- body = m.Body
- }
- return &cloudevents.Event{
- Context: &ca,
- Data: body,
- DataEncoded: body != nil,
- }, nil
-}
-
-func (v CodecV02) fromHeaders(h http.Header) (cloudevents.EventContextV02, error) {
- // Normalize headers.
- for k, v := range h {
- ck := textproto.CanonicalMIMEHeaderKey(k)
- if k != ck {
- delete(h, k)
- h[ck] = v
- }
- }
-
- ec := cloudevents.EventContextV02{}
-
- ec.SpecVersion = h.Get("ce-specversion")
- h.Del("ce-specversion")
-
- ec.ID = h.Get("ce-id")
- h.Del("ce-id")
-
- ec.Type = h.Get("ce-type")
- h.Del("ce-type")
-
- source := types.ParseURLRef(h.Get("ce-source"))
- if source != nil {
- ec.Source = *source
- }
- h.Del("ce-source")
-
- var err error
- ec.Time, err = types.ParseTimestamp(h.Get("ce-time"))
- if err != nil {
- return ec, err
- }
- h.Del("ce-time")
-
- ec.SchemaURL = types.ParseURLRef(h.Get("ce-schemaurl"))
- h.Del("ce-schemaurl")
-
- contentType := h.Get("Content-Type")
- if contentType != "" {
- ec.ContentType = &contentType
- }
- h.Del("Content-Type")
-
- // At this point, we have deleted all the known headers.
- // Everything left is assumed to be an extension.
-
- extensions := make(map[string]interface{})
- for k, v := range h {
- if len(k) > len("ce-") && strings.EqualFold(k[:len("ce-")], "ce-") {
- ak := strings.ToLower(k[len("ce-"):])
- if i := strings.Index(ak, "-"); i > 0 {
- // attrib-key
- attrib := ak[:i]
- key := ak[(i + 1):]
- if xv, ok := extensions[attrib]; ok {
- if m, ok := xv.(map[string]interface{}); ok {
- m[key] = v
- continue
- }
- // TODO: revisit how we want to bubble errors up.
- return ec, fmt.Errorf("failed to process map type extension")
- } else {
- m := make(map[string]interface{})
- m[key] = v
- extensions[attrib] = m
- }
- } else {
- // key
- var tmp interface{}
- if err := json.Unmarshal([]byte(v[0]), &tmp); err == nil {
- extensions[ak] = tmp
- } else {
- // If we can't unmarshal the data, treat it as a string.
- extensions[ak] = v[0]
- }
- }
- }
- }
- if len(extensions) > 0 {
- ec.Extensions = extensions
- }
- return ec, nil
-}
-
-func (v CodecV02) inspectEncoding(ctx context.Context, msg transport.Message) Encoding {
- version := msg.CloudEventsVersion()
- if version != cloudevents.CloudEventsVersionV02 {
- return Unknown
- }
- m, ok := msg.(*Message)
- if !ok {
- return Unknown
- }
- contentType := m.Header.Get("Content-Type")
- if contentType == cloudevents.ApplicationCloudEventsJSON {
- return StructuredV02
- }
- return BinaryV02
-}
diff --git a/v1/cloudevents/transport/http/codec_v02_test.go b/v1/cloudevents/transport/http/codec_v02_test.go
deleted file mode 100644
index 56f1eff3e..000000000
--- a/v1/cloudevents/transport/http/codec_v02_test.go
+++ /dev/null
@@ -1,417 +0,0 @@
-package http_test
-
-import (
- "context"
- "net/url"
- "testing"
- "time"
-
- "github.com/cloudevents/sdk-go/v1/cloudevents"
- "github.com/cloudevents/sdk-go/v1/cloudevents/transport/http"
- "github.com/cloudevents/sdk-go/v1/cloudevents/types"
- "github.com/google/go-cmp/cmp"
-)
-
-func TestCodecV02_Encode(t *testing.T) {
- now := types.Timestamp{Time: time.Now().UTC()}
- sourceUrl, _ := url.Parse("http://example.com/source")
- source := &types.URLRef{URL: *sourceUrl}
-
- schemaUrl, _ := url.Parse("http://example.com/schema")
- schema := &types.URLRef{URL: *schemaUrl}
-
- testCases := map[string]struct {
- codec http.CodecV02
- event cloudevents.Event
- want *http.Message
- wantErr error
- }{
- "simple v0.2 default": {
- codec: http.CodecV02{},
- event: cloudevents.Event{
- Context: &cloudevents.EventContextV02{
- Type: "com.example.test",
- Source: *source,
- ID: "ABC-123",
- },
- },
- want: &http.Message{
- Header: map[string][]string{
- "Ce-Specversion": {"0.2"},
- "Ce-Id": {"ABC-123"},
- "Ce-Type": {"com.example.test"},
- "Ce-Source": {"http://example.com/source"},
- },
- },
- },
- "full v0.2 default": {
- codec: http.CodecV02{},
- event: cloudevents.Event{
- Context: &cloudevents.EventContextV02{
- ID: "ABC-123",
- Time: &now,
- Type: "com.example.test",
- SchemaURL: schema,
- ContentType: cloudevents.StringOfApplicationJSON(),
- Source: *source,
- Extensions: map[string]interface{}{
- "test": "extended",
- },
- },
- Data: map[string]interface{}{
- "hello": "world",
- },
- },
- want: &http.Message{
- Header: map[string][]string{
- "Ce-Specversion": {"0.2"},
- "Ce-Id": {"ABC-123"},
- "Ce-Time": {now.Format(time.RFC3339Nano)},
- "Ce-Type": {"com.example.test"},
- "Ce-Source": {"http://example.com/source"},
- "Ce-Schemaurl": {"http://example.com/schema"},
- "Ce-Test": {`"extended"`},
- "Content-Type": {"application/json"},
- },
- Body: []byte(`{"hello":"world"}`),
- },
- },
- "simple v0.2 binary": {
- codec: http.CodecV02{DefaultEncoding: http.BinaryV02},
- event: cloudevents.Event{
- Context: &cloudevents.EventContextV02{
- Type: "com.example.test",
- Source: *source,
- ID: "ABC-123",
- },
- },
- want: &http.Message{
- Header: map[string][]string{
- "Ce-Specversion": {"0.2"},
- "Ce-Id": {"ABC-123"},
- "Ce-Type": {"com.example.test"},
- "Ce-Source": {"http://example.com/source"},
- },
- },
- },
- "full v0.2 binary": {
- codec: http.CodecV02{DefaultEncoding: http.BinaryV02},
- event: cloudevents.Event{
- Context: &cloudevents.EventContextV02{
- ID: "ABC-123",
- Time: &now,
- Type: "com.example.test",
- SchemaURL: schema,
- ContentType: cloudevents.StringOfApplicationJSON(),
- Source: *source,
- Extensions: map[string]interface{}{
- "test": "extended",
- "asmap": map[string]interface{}{
- "a": "apple",
- "b": "banana",
- "c": map[string]interface{}{
- "d": "dog",
- "e": "eel",
- },
- },
- },
- },
- Data: map[string]interface{}{
- "hello": "world",
- },
- },
- want: &http.Message{
- Header: map[string][]string{
- "Ce-Specversion": {"0.2"},
- "Ce-Id": {"ABC-123"},
- "Ce-Time": {now.Format(time.RFC3339Nano)},
- "Ce-Type": {"com.example.test"},
- "Ce-Source": {"http://example.com/source"},
- "Ce-Schemaurl": {"http://example.com/schema"},
- "Ce-Test": {`"extended"`},
- "Ce-Asmap-A": {`"apple"`},
- "Ce-Asmap-B": {`"banana"`},
- "Ce-Asmap-C": {`{"d":"dog","e":"eel"}`},
- "Content-Type": {"application/json"},
- },
- Body: []byte(`{"hello":"world"}`),
- },
- },
- "simple v0.2 structured": {
- codec: http.CodecV02{DefaultEncoding: http.StructuredV02},
- event: cloudevents.Event{
- Context: cloudevents.EventContextV02{
- Type: "com.example.test",
- Source: *source,
- ID: "ABC-123",
- }.AsV02(),
- },
- want: &http.Message{
- Header: map[string][]string{
- "Content-Type": {"application/cloudevents+json"},
- },
- Body: func() []byte {
- body := map[string]interface{}{
- "specversion": "0.2",
- "id": "ABC-123",
- "type": "com.example.test",
- "source": "http://example.com/source",
- }
- return toBytes(body)
- }(),
- },
- },
- "full v0.2 structured": {
- codec: http.CodecV02{DefaultEncoding: http.StructuredV02},
- event: cloudevents.Event{
- Context: cloudevents.EventContextV02{
- ID: "ABC-123",
- Time: &now,
- Type: "com.example.test",
- SchemaURL: schema,
- ContentType: cloudevents.StringOfApplicationJSON(),
- Source: *source,
- Extensions: map[string]interface{}{
- "test": "extended",
- },
- }.AsV02(),
- Data: map[string]interface{}{
- "hello": "world",
- },
- },
- want: &http.Message{
- Header: map[string][]string{
- "Content-Type": {"application/cloudevents+json"},
- },
- Body: func() []byte {
- body := map[string]interface{}{
- "specversion": "0.2",
- "contenttype": "application/json",
- "data": map[string]interface{}{
- "hello": "world",
- },
- "id": "ABC-123",
- "time": now,
- "type": "com.example.test",
- "test": "extended",
- "schemaurl": "http://example.com/schema",
- "source": "http://example.com/source",
- }
- return toBytes(body)
- }(),
- },
- },
- }
- for n, tc := range testCases {
- t.Run(n, func(t *testing.T) {
-
- got, err := tc.codec.Encode(context.TODO(), tc.event)
-
- if tc.wantErr != nil || err != nil {
- if diff := cmp.Diff(tc.wantErr, err); diff != "" {
- t.Errorf("unexpected error (-want, +got) = %v", diff)
- }
- return
- }
-
- if diff := cmp.Diff(tc.want, got); diff != "" {
-
- if msg, ok := got.(*http.Message); ok {
- // It is hard to read the byte dump
- want := string(tc.want.Body)
- got := string(msg.Body)
- if diff := cmp.Diff(want, got); diff != "" {
- t.Errorf("unexpected message body (-want, +got) = %v", diff)
- return
- }
- }
-
- t.Errorf("unexpected message (-want, +got) = %v", diff)
- }
- })
- }
-}
-
-// TODO: figure out extensions for v0.2
-
-func TestCodecV02_Decode(t *testing.T) {
- now := types.Timestamp{Time: time.Now()}
- sourceUrl, _ := url.Parse("http://example.com/source")
- source := &types.URLRef{URL: *sourceUrl}
-
- schemaUrl, _ := url.Parse("http://example.com/schema")
- schema := &types.URLRef{URL: *schemaUrl}
-
- testCases := map[string]struct {
- codec http.CodecV02
- msg *http.Message
- want *cloudevents.Event
- wantErr error
- }{
- "simple v0.2 binary": {
- codec: http.CodecV02{},
- msg: &http.Message{
- Header: map[string][]string{
- "ce-specversion": {"0.2"},
- "ce-id": {"ABC-123"},
- "ce-type": {"com.example.test"},
- "ce-source": {"http://example.com/source"},
- "Content-Type": {"application/json"},
- },
- },
- want: &cloudevents.Event{
- Context: &cloudevents.EventContextV02{
- SpecVersion: cloudevents.CloudEventsVersionV02,
- ContentType: cloudevents.StringOfApplicationJSON(),
- Type: "com.example.test",
- Source: *source,
- ID: "ABC-123",
- },
- },
- },
- "full v0.2 binary": {
- codec: http.CodecV02{},
- msg: &http.Message{
- Header: map[string][]string{
- "ce-specversion": {"0.2"},
- "ce-id": {"ABC-123"},
- "ce-time": {now.Format(time.RFC3339Nano)},
- "ce-type": {"com.example.test"},
- "ce-source": {"http://example.com/source"},
- "ce-schemaurl": {"http://example.com/schema"},
- "ce-test": {`"extended"`},
- "ce-asmap-a": {`"apple"`},
- "ce-asmap-b": {`"banana"`},
- "ce-asmap-c": {`{"d":"dog","e":"eel"}`},
- "Content-Type": {"application/json"},
- },
- Body: toBytes(map[string]interface{}{
- "hello": "world",
- }),
- },
- want: &cloudevents.Event{
- Context: &cloudevents.EventContextV02{
- SpecVersion: cloudevents.CloudEventsVersionV02,
- ID: "ABC-123",
- Time: &now,
- Type: "com.example.test",
- SchemaURL: schema,
- ContentType: cloudevents.StringOfApplicationJSON(),
- Source: *source,
- Extensions: map[string]interface{}{
- "test": "extended",
- "asmap": map[string]interface{}{
- "a": []string{`"apple"`},
- "b": []string{`"banana"`},
- "c": []string{`{"d":"dog","e":"eel"}`},
- },
- },
- },
- Data: toBytes(map[string]interface{}{
- "hello": "world",
- }),
- DataEncoded: true,
- },
- },
- "simple v0.2 structured": {
- codec: http.CodecV02{},
- msg: &http.Message{
- Header: map[string][]string{
- "Content-Type": {"application/cloudevents+json"},
- },
- Body: toBytes(map[string]interface{}{
- "specversion": "0.2",
- "id": "ABC-123",
- "type": "com.example.test",
- "source": "http://example.com/source",
- }),
- },
- want: &cloudevents.Event{
- Context: &cloudevents.EventContextV02{
- SpecVersion: cloudevents.CloudEventsVersionV02,
- Type: "com.example.test",
- Source: *source,
- ID: "ABC-123",
- },
- },
- },
- "full v0.2 structured": {
- codec: http.CodecV02{},
- msg: &http.Message{
- Header: map[string][]string{
- "Content-Type": {"application/cloudevents+json"},
- },
- Body: toBytes(map[string]interface{}{
- "specversion": "0.2",
- "contenttype": "application/json",
- "data": map[string]interface{}{
- "hello": "world",
- },
- "id": "ABC-123",
- "time": now,
- "type": "com.example.test",
- "test": "extended",
- "schemaurl": "http://example.com/schema",
- "source": "http://example.com/source",
- }),
- },
- want: &cloudevents.Event{
- Context: &cloudevents.EventContextV02{
- SpecVersion: cloudevents.CloudEventsVersionV02,
- ID: "ABC-123",
- Time: &now,
- Type: "com.example.test",
- SchemaURL: schema,
- ContentType: cloudevents.StringOfApplicationJSON(),
- Source: *source,
- Extensions: map[string]interface{}{
- "test": "extended",
- },
- },
- Data: toBytes(map[string]interface{}{
- "hello": "world",
- }),
- DataEncoded: true,
- },
- },
- "simple v0.2 binary with short header": {
- codec: http.CodecV02{},
- msg: &http.Message{
- Header: map[string][]string{
- "ce-specversion": {"0.2"},
- "ce-id": {"ABC-123"},
- "ce-type": {"com.example.test"},
- "ce-source": {"http://example.com/source"},
- "Content-Type": {"application/json"},
- "X": {"Notice how short the header's name is"},
- },
- },
- want: &cloudevents.Event{
- Context: &cloudevents.EventContextV02{
- SpecVersion: cloudevents.CloudEventsVersionV02,
- ContentType: cloudevents.StringOfApplicationJSON(),
- Type: "com.example.test",
- Source: *source,
- ID: "ABC-123",
- },
- },
- },
- }
- for n, tc := range testCases {
- t.Run(n, func(t *testing.T) {
-
- got, err := tc.codec.Decode(context.TODO(), tc.msg)
-
- if tc.wantErr != nil || err != nil {
- if diff := cmp.Diff(tc.wantErr, err); diff != "" {
- t.Errorf("unexpected error (-want, +got) = %v", diff)
- }
- return
- }
-
- if diff := cmp.Diff(tc.want, got); diff != "" {
- t.Errorf("unexpected event (-want, +got) = %v", diff)
- }
- })
- }
-}
diff --git a/v1/cloudevents/transport/http/codec_v03.go b/v1/cloudevents/transport/http/codec_v03.go
deleted file mode 100644
index 02ba1fb2a..000000000
--- a/v1/cloudevents/transport/http/codec_v03.go
+++ /dev/null
@@ -1,302 +0,0 @@
-package http
-
-import (
- "context"
- "encoding/json"
- "fmt"
- "net/http"
- "net/textproto"
- "strings"
-
- "github.com/cloudevents/sdk-go/v1/cloudevents"
- cecontext "github.com/cloudevents/sdk-go/v1/cloudevents/context"
- "github.com/cloudevents/sdk-go/v1/cloudevents/observability"
- "github.com/cloudevents/sdk-go/v1/cloudevents/transport"
- "github.com/cloudevents/sdk-go/v1/cloudevents/types"
-)
-
-// CodecV03 represents a http transport codec that uses CloudEvents spec v0.3
-type CodecV03 struct {
- CodecStructured
-
- DefaultEncoding Encoding
-}
-
-// Adheres to Codec
-var _ transport.Codec = (*CodecV03)(nil)
-
-// Encode implements Codec.Encode
-func (v CodecV03) Encode(ctx context.Context, e cloudevents.Event) (transport.Message, error) {
- encoding := v.DefaultEncoding
- strEnc := cecontext.EncodingFrom(ctx)
- if strEnc != "" {
- switch strEnc {
- case Binary:
- encoding = BinaryV03
- case Structured:
- encoding = StructuredV03
- }
- }
-
- _, r := observability.NewReporter(ctx, CodecObserved{o: reportEncode, c: encoding.Codec()})
- m, err := v.obsEncode(ctx, e, encoding)
- if err != nil {
- r.Error()
- } else {
- r.OK()
- }
- return m, err
-}
-
-func (v CodecV03) obsEncode(ctx context.Context, e cloudevents.Event, encoding Encoding) (transport.Message, error) {
- switch encoding {
- case Default:
- fallthrough
- case BinaryV03:
- return v.encodeBinary(ctx, e)
- case StructuredV03:
- return v.encodeStructured(ctx, e)
- case BatchedV03:
- return nil, fmt.Errorf("not implemented")
- default:
- return nil, fmt.Errorf("unknown encoding: %d", encoding)
- }
-}
-
-// Decode implements Codec.Decode
-func (v CodecV03) Decode(ctx context.Context, msg transport.Message) (*cloudevents.Event, error) {
- _, r := observability.NewReporter(ctx, CodecObserved{o: reportDecode, c: v.inspectEncoding(ctx, msg).Codec()}) // TODO: inspectEncoding is not free.
- e, err := v.obsDecode(ctx, msg)
- if err != nil {
- r.Error()
- } else {
- r.OK()
- }
- return e, err
-}
-
-func (v CodecV03) obsDecode(ctx context.Context, msg transport.Message) (*cloudevents.Event, error) {
- switch v.inspectEncoding(ctx, msg) {
- case BinaryV03:
- return v.decodeBinary(ctx, msg)
- case StructuredV03:
- return v.decodeStructured(ctx, cloudevents.CloudEventsVersionV03, msg)
- case BatchedV03:
- return nil, fmt.Errorf("not implemented")
- default:
- return nil, transport.NewErrMessageEncodingUnknown("v03", TransportName)
- }
-}
-
-func (v CodecV03) encodeBinary(ctx context.Context, e cloudevents.Event) (transport.Message, error) {
- header, err := v.toHeaders(e.Context.AsV03())
- if err != nil {
- return nil, err
- }
-
- body, err := e.DataBytes()
- if err != nil {
- return nil, err
- }
-
- msg := &Message{
- Header: header,
- Body: body,
- }
-
- return msg, nil
-}
-
-func (v CodecV03) toHeaders(ec *cloudevents.EventContextV03) (http.Header, error) {
- h := http.Header{}
- h.Set("ce-specversion", ec.SpecVersion)
- h.Set("ce-type", ec.Type)
- h.Set("ce-source", ec.Source.String())
- if ec.Subject != nil {
- h.Set("ce-subject", *ec.Subject)
- }
- h.Set("ce-id", ec.ID)
- if ec.Time != nil && !ec.Time.IsZero() {
- h.Set("ce-time", ec.Time.String())
- }
- if ec.SchemaURL != nil {
- h.Set("ce-schemaurl", ec.SchemaURL.String())
- }
- if ec.DataContentType != nil && *ec.DataContentType != "" {
- h.Set("Content-Type", *ec.DataContentType)
- }
- if ec.DataContentEncoding != nil {
- h.Set("ce-datacontentencoding", *ec.DataContentEncoding)
- }
-
- for k, v := range ec.Extensions {
- k = strings.ToLower(k)
- // Per spec, map-valued extensions are converted to a list of headers as:
- // CE-attrib-key
- switch v.(type) {
- case string:
- h.Set("ce-"+k, v.(string))
-
- case map[string]interface{}:
- mapVal := v.(map[string]interface{})
-
- for subkey, subval := range mapVal {
- if subvalstr, ok := v.(string); ok {
- h.Set("ce-"+k+"-"+subkey, subvalstr)
- continue
- }
-
- encoded, err := json.Marshal(subval)
- if err != nil {
- return nil, err
- }
- h.Set("ce-"+k+"-"+subkey, string(encoded))
- }
-
- default:
- encoded, err := json.Marshal(v)
- if err != nil {
- return nil, err
- }
- h.Set("ce-"+k, string(encoded))
- }
- }
-
- return h, nil
-}
-
-func (v CodecV03) decodeBinary(ctx context.Context, msg transport.Message) (*cloudevents.Event, error) {
- m, ok := msg.(*Message)
- if !ok {
- return nil, fmt.Errorf("failed to convert transport.Message to http.Message")
- }
- ca, err := v.fromHeaders(m.Header)
- if err != nil {
- return nil, err
- }
- var body interface{}
- if len(m.Body) > 0 {
- body = m.Body
- }
- return &cloudevents.Event{
- Context: &ca,
- Data: body,
- DataEncoded: body != nil,
- }, nil
-}
-
-func (v CodecV03) fromHeaders(h http.Header) (cloudevents.EventContextV03, error) {
- // Normalize headers.
- for k, v := range h {
- ck := textproto.CanonicalMIMEHeaderKey(k)
- if k != ck {
- delete(h, k)
- h[ck] = v
- }
- }
-
- ec := cloudevents.EventContextV03{}
-
- ec.SpecVersion = h.Get("ce-specversion")
- h.Del("ce-specversion")
-
- ec.ID = h.Get("ce-id")
- h.Del("ce-id")
-
- ec.Type = h.Get("ce-type")
- h.Del("ce-type")
-
- source := types.ParseURLRef(h.Get("ce-source"))
- if source != nil {
- ec.Source = *source
- }
- h.Del("ce-source")
-
- subject := h.Get("ce-subject")
- if subject != "" {
- ec.Subject = &subject
- }
- h.Del("ce-subject")
-
- var err error
- ec.Time, err = types.ParseTimestamp(h.Get("ce-time"))
- if err != nil {
- return ec, err
- }
- h.Del("ce-time")
-
- ec.SchemaURL = types.ParseURLRef(h.Get("ce-schemaurl"))
- h.Del("ce-schemaurl")
-
- contentType := h.Get("Content-Type")
- if contentType != "" {
- ec.DataContentType = &contentType
- }
- h.Del("Content-Type")
-
- dataContentEncoding := h.Get("ce-datacontentencoding")
- if dataContentEncoding != "" {
- ec.DataContentEncoding = &dataContentEncoding
- }
- h.Del("ce-datacontentencoding")
-
- // At this point, we have deleted all the known headers.
- // Everything left is assumed to be an extension.
-
- extensions := make(map[string]interface{})
- for k, v := range h {
- k = strings.ToLower(k)
- if len(k) > len("ce-") && strings.EqualFold(k[:len("ce-")], "ce-") {
- ak := strings.ToLower(k[len("ce-"):])
- if i := strings.Index(ak, "-"); i > 0 {
- // attrib-key
- attrib := ak[:i]
- key := ak[(i + 1):]
- if xv, ok := extensions[attrib]; ok {
- if m, ok := xv.(map[string]interface{}); ok {
- m[key] = v
- continue
- }
- // TODO: revisit how we want to bubble errors up.
- return ec, fmt.Errorf("failed to process map type extension")
- } else {
- m := make(map[string]interface{})
- m[key] = v
- extensions[attrib] = m
- }
- } else {
- // key
- var tmp interface{}
- if err := json.Unmarshal([]byte(v[0]), &tmp); err == nil {
- extensions[ak] = tmp
- } else {
- // If we can't unmarshal the data, treat it as a string.
- extensions[ak] = v[0]
- }
- }
- }
- }
- if len(extensions) > 0 {
- ec.Extensions = extensions
- }
- return ec, nil
-}
-
-func (v CodecV03) inspectEncoding(ctx context.Context, msg transport.Message) Encoding {
- version := msg.CloudEventsVersion()
- if version != cloudevents.CloudEventsVersionV03 {
- return Unknown
- }
- m, ok := msg.(*Message)
- if !ok {
- return Unknown
- }
- contentType := m.Header.Get("Content-Type")
- if contentType == cloudevents.ApplicationCloudEventsJSON {
- return StructuredV03
- }
- if contentType == cloudevents.ApplicationCloudEventsBatchJSON {
- return BatchedV03
- }
- return BinaryV03
-}
diff --git a/v1/cloudevents/transport/http/codec_v03_test.go b/v1/cloudevents/transport/http/codec_v03_test.go
deleted file mode 100644
index 87f054c78..000000000
--- a/v1/cloudevents/transport/http/codec_v03_test.go
+++ /dev/null
@@ -1,603 +0,0 @@
-package http_test
-
-import (
- "context"
- "net/url"
- "testing"
- "time"
-
- "github.com/cloudevents/sdk-go/v1/cloudevents"
- "github.com/cloudevents/sdk-go/v1/cloudevents/transport/http"
- "github.com/cloudevents/sdk-go/v1/cloudevents/types"
- "github.com/google/go-cmp/cmp"
-)
-
-func TestCodecV03_Encode(t *testing.T) {
- now := types.Timestamp{Time: time.Now().UTC()}
- sourceUrl, _ := url.Parse("http://example.com/source")
- source := &types.URLRef{URL: *sourceUrl}
-
- subject := "resource"
-
- schemaUrl, _ := url.Parse("http://example.com/schema")
- schema := &types.URLRef{URL: *schemaUrl}
-
- testCases := map[string]struct {
- codec http.CodecV03
- event cloudevents.Event
- want *http.Message
- wantErr error
- }{
- "simple v0.3 default": {
- codec: http.CodecV03{},
- event: cloudevents.Event{
- Context: &cloudevents.EventContextV03{
- Type: "com.example.test",
- Source: *source,
- ID: "ABC-123",
- },
- },
- want: &http.Message{
- Header: map[string][]string{
- "Ce-Specversion": {"0.3"},
- "Ce-Id": {"ABC-123"},
- "Ce-Type": {"com.example.test"},
- "Ce-Source": {"http://example.com/source"},
- },
- },
- },
- "full v0.3 default": {
- codec: http.CodecV03{},
- event: cloudevents.Event{
- Context: &cloudevents.EventContextV03{
- ID: "ABC-123",
- Time: &now,
- Type: "com.example.test",
- SchemaURL: schema,
- DataContentType: cloudevents.StringOfApplicationJSON(),
- Source: *source,
- Subject: &subject,
- Extensions: map[string]interface{}{
- "test": "extended",
- },
- },
- Data: map[string]interface{}{
- "hello": "world",
- },
- },
- want: &http.Message{
- Header: map[string][]string{
- "Ce-Specversion": {"0.3"},
- "Ce-Id": {"ABC-123"},
- "Ce-Time": {now.Format(time.RFC3339Nano)},
- "Ce-Type": {"com.example.test"},
- "Ce-Source": {"http://example.com/source"},
- "Ce-Subject": {"resource"},
- "Ce-Schemaurl": {"http://example.com/schema"},
- "Ce-Test": {"extended"},
- "Content-Type": {"application/json"},
- },
- Body: []byte(`{"hello":"world"}`),
- },
- },
- "simple v0.3 binary": {
- codec: http.CodecV03{DefaultEncoding: http.BinaryV03},
- event: cloudevents.Event{
- Context: cloudevents.EventContextV03{
- Type: "com.example.test",
- Source: *source,
- ID: "ABC-123",
- }.AsV03(),
- },
- want: &http.Message{
- Header: map[string][]string{
- "Ce-Specversion": {"0.3"},
- "Ce-Id": {"ABC-123"},
- "Ce-Type": {"com.example.test"},
- "Ce-Source": {"http://example.com/source"},
- },
- },
- },
- "full v0.3 binary": {
- codec: http.CodecV03{DefaultEncoding: http.BinaryV03},
- event: cloudevents.Event{
- Context: &cloudevents.EventContextV03{
- ID: "ABC-123",
- Time: &now,
- Type: "com.example.test",
- SchemaURL: schema,
- DataContentType: cloudevents.StringOfApplicationJSON(),
- Source: *source,
- Subject: &subject,
- Extensions: map[string]interface{}{
- "test": "extended",
- "asmap": map[string]interface{}{
- "a": "apple",
- "b": "banana",
- "c": map[string]interface{}{
- "d": "dog",
- "e": "eel",
- },
- },
- },
- },
- Data: map[string]interface{}{
- "hello": "world",
- },
- },
- want: &http.Message{
- Header: map[string][]string{
- "Ce-Specversion": {"0.3"},
- "Ce-Id": {"ABC-123"},
- "Ce-Time": {now.Format(time.RFC3339Nano)},
- "Ce-Type": {"com.example.test"},
- "Ce-Source": {"http://example.com/source"},
- "Ce-Subject": {"resource"},
- "Ce-Schemaurl": {"http://example.com/schema"},
- "Ce-Test": {"extended"},
- "Ce-Asmap-A": {`"apple"`},
- "Ce-Asmap-B": {`"banana"`},
- "Ce-Asmap-C": {`{"d":"dog","e":"eel"}`},
- "Content-Type": {"application/json"},
- },
- Body: []byte(`{"hello":"world"}`),
- },
- },
- "full v0.3 binary base64": {
- codec: http.CodecV03{DefaultEncoding: http.BinaryV03},
- event: cloudevents.Event{
- Context: cloudevents.EventContextV03{
- ID: "ABC-123",
- Time: &now,
- Type: "com.example.test",
- SchemaURL: schema,
- DataContentType: cloudevents.StringOfApplicationJSON(),
- DataContentEncoding: cloudevents.StringOfBase64(),
- Source: *source,
- Subject: &subject,
- Extensions: map[string]interface{}{
- "test": "extended",
- "asmap": map[string]interface{}{
- "a": "apple",
- "b": "banana",
- "c": map[string]interface{}{
- "d": "dog",
- "e": "eel",
- },
- },
- },
- }.AsV03(),
- Data: map[string]interface{}{
- "hello": "world",
- },
- },
- want: &http.Message{
- Header: map[string][]string{
- "Ce-Specversion": {"0.3"},
- "Ce-Id": {"ABC-123"},
- "Ce-Time": {now.Format(time.RFC3339Nano)},
- "Ce-Type": {"com.example.test"},
- "Ce-Source": {"http://example.com/source"},
- "Ce-Subject": {"resource"},
- "Ce-Schemaurl": {"http://example.com/schema"},
- "Ce-Test": {"extended"},
- "Ce-Asmap-A": {`"apple"`},
- "Ce-Asmap-B": {`"banana"`},
- "Ce-Asmap-C": {`{"d":"dog","e":"eel"}`},
- "Content-Type": {"application/json"},
- "Ce-Datacontentencoding": {"base64"},
- },
- Body: []byte("eyJoZWxsbyI6IndvcmxkIn0="),
- },
- },
- "simple v0.3 structured": {
- codec: http.CodecV03{DefaultEncoding: http.StructuredV03},
- event: cloudevents.Event{
- Context: cloudevents.EventContextV03{
- Type: "com.example.test",
- Source: *source,
- ID: "ABC-123",
- }.AsV03(),
- },
- want: &http.Message{
- Header: map[string][]string{
- "Content-Type": {"application/cloudevents+json"},
- },
- Body: func() []byte {
- body := map[string]interface{}{
- "specversion": "0.3",
- "id": "ABC-123",
- "type": "com.example.test",
- "source": "http://example.com/source",
- }
- return toBytes(body)
- }(),
- },
- },
- "full v0.3 structured": {
- codec: http.CodecV03{DefaultEncoding: http.StructuredV03},
- event: cloudevents.Event{
- Context: cloudevents.EventContextV03{
- ID: "ABC-123",
- Time: &now,
- Type: "com.example.test",
- SchemaURL: schema,
- DataContentType: cloudevents.StringOfApplicationJSON(),
- Source: *source,
- Subject: &subject,
- Extensions: map[string]interface{}{
- "test": "extended",
- },
- }.AsV03(),
- Data: map[string]interface{}{
- "hello": "world",
- },
- },
- want: &http.Message{
- Header: map[string][]string{
- "Content-Type": {"application/cloudevents+json"},
- },
- Body: func() []byte {
- body := map[string]interface{}{
- "specversion": "0.3",
- "datacontenttype": "application/json",
- "data": map[string]interface{}{
- "hello": "world",
- },
- "id": "ABC-123",
- "time": now,
- "type": "com.example.test",
- "test": "extended",
- "schemaurl": "http://example.com/schema",
- "source": "http://example.com/source",
- "subject": "resource",
- }
- return toBytes(body)
- }(),
- },
- },
- "full v0.3 structured base64": {
- codec: http.CodecV03{DefaultEncoding: http.StructuredV03},
- event: cloudevents.Event{
- Context: cloudevents.EventContextV03{
- ID: "ABC-123",
- Time: &now,
- Type: "com.example.test",
- SchemaURL: schema,
- DataContentType: cloudevents.StringOfApplicationJSON(),
- DataContentEncoding: cloudevents.StringOfBase64(),
- Source: *source,
- Subject: &subject,
- Extensions: map[string]interface{}{
- "test": "extended",
- },
- }.AsV03(),
- Data: map[string]interface{}{
- "hello": "world",
- },
- },
- want: &http.Message{
- Header: map[string][]string{
- "Content-Type": {"application/cloudevents+json"},
- },
- Body: func() []byte {
- body := map[string]interface{}{
- "specversion": "0.3",
- "datacontentencoding": "base64",
- "datacontenttype": "application/json",
- "data": "eyJoZWxsbyI6IndvcmxkIn0=",
- "id": "ABC-123",
- "time": now,
- "type": "com.example.test",
- "test": "extended",
- "schemaurl": "http://example.com/schema",
- "source": "http://example.com/source",
- "subject": "resource",
- }
- return toBytes(body)
- }(),
- },
- },
- }
- for n, tc := range testCases {
- t.Run(n, func(t *testing.T) {
-
- got, err := tc.codec.Encode(context.TODO(), tc.event)
-
- if tc.wantErr != nil || err != nil {
- if diff := cmp.Diff(tc.wantErr, err); diff != "" {
- t.Errorf("unexpected error (-want, +got) = %v", diff)
- }
- return
- }
-
- if diff := cmp.Diff(tc.want, got); diff != "" {
-
- if msg, ok := got.(*http.Message); ok {
- // It is hard to read the byte dump
- want := string(tc.want.Body)
- got := string(msg.Body)
- if diff := cmp.Diff(want, got); diff != "" {
- t.Errorf("unexpected message body (-want, +got) = %v", diff)
- return
- }
- }
-
- t.Errorf("unexpected message (-want, +got) = %v", diff)
- }
- })
- }
-}
-
-// TODO: figure out extensions for v0.3
-
-func TestCodecV03_Decode(t *testing.T) {
- now := types.Timestamp{Time: time.Now()}
- sourceUrl, _ := url.Parse("http://example.com/source")
- source := &types.URLRef{URL: *sourceUrl}
-
- subject := "resource"
-
- schemaUrl, _ := url.Parse("http://example.com/schema")
- schema := &types.URLRef{URL: *schemaUrl}
-
- testCases := map[string]struct {
- codec http.CodecV03
- msg *http.Message
- want *cloudevents.Event
- wantErr error
- }{
- "simple v0.3 binary": {
- codec: http.CodecV03{},
- msg: &http.Message{
- Header: map[string][]string{
- "ce-specversion": {"0.3"},
- "ce-id": {"ABC-123"},
- "ce-type": {"com.example.test"},
- "ce-source": {"http://example.com/source"},
- "Content-Type": {"application/json"},
- },
- },
- want: &cloudevents.Event{
- Context: &cloudevents.EventContextV03{
- SpecVersion: cloudevents.CloudEventsVersionV03,
- DataContentType: cloudevents.StringOfApplicationJSON(),
- Type: "com.example.test",
- Source: *source,
- ID: "ABC-123",
- },
- },
- },
- "full v0.3 binary": {
- codec: http.CodecV03{},
- msg: &http.Message{
- Header: map[string][]string{
- "ce-specversion": {"0.3"},
- "ce-id": {"ABC-123"},
- "ce-time": {now.Format(time.RFC3339Nano)},
- "ce-type": {"com.example.test"},
- "ce-source": {"http://example.com/source"},
- "ce-subject": {"resource"},
- "ce-schemaurl": {"http://example.com/schema"},
- "ce-test": {`"extended"`},
- "ce-asmap-a": {`"apple"`},
- "ce-asmap-b": {`"banana"`},
- "ce-asmap-c": {`{"d":"dog","e":"eel"}`},
- "Content-Type": {"application/json"},
- },
- Body: toBytes(map[string]interface{}{
- "hello": "world",
- }),
- },
- want: &cloudevents.Event{
- Context: &cloudevents.EventContextV03{
- SpecVersion: cloudevents.CloudEventsVersionV03,
- ID: "ABC-123",
- Time: &now,
- Type: "com.example.test",
- SchemaURL: schema,
- DataContentType: cloudevents.StringOfApplicationJSON(),
- Source: *source,
- Subject: &subject,
- Extensions: map[string]interface{}{
- "test": "extended",
- "asmap": map[string]interface{}{
- "a": []string{`"apple"`},
- "b": []string{`"banana"`},
- "c": []string{`{"d":"dog","e":"eel"}`},
- },
- },
- },
- Data: toBytes(map[string]interface{}{
- "hello": "world",
- }),
- DataEncoded: true,
- },
- },
- "full v0.3 binary base64": {
- codec: http.CodecV03{},
- msg: &http.Message{
- Header: map[string][]string{
- "ce-specversion": {"0.3"},
- "ce-id": {"ABC-123"},
- "ce-time": {now.Format(time.RFC3339Nano)},
- "ce-type": {"com.example.test"},
- "ce-source": {"http://example.com/source"},
- "ce-subject": {"resource"},
- "ce-schemaurl": {"http://example.com/schema"},
- "ce-test": {`"extended"`},
- "ce-asmap-a": {`"apple"`},
- "ce-asmap-b": {`"banana"`},
- "ce-asmap-c": {`{"d":"dog","e":"eel"}`},
- "Content-Type": {"application/json"},
- "ce-datacontentencoding": {"base64"},
- },
- Body: []byte("eyJoZWxsbyI6IndvcmxkIn0="),
- },
- want: &cloudevents.Event{
- Context: &cloudevents.EventContextV03{
- SpecVersion: cloudevents.CloudEventsVersionV03,
- ID: "ABC-123",
- Time: &now,
- Type: "com.example.test",
- SchemaURL: schema,
- DataContentType: cloudevents.StringOfApplicationJSON(),
- Source: *source,
- Subject: &subject,
- DataContentEncoding: cloudevents.StringOfBase64(),
- Extensions: map[string]interface{}{
- "test": "extended",
- "asmap": map[string]interface{}{
- "a": []string{`"apple"`},
- "b": []string{`"banana"`},
- "c": []string{`{"d":"dog","e":"eel"}`},
- },
- },
- },
- Data: []byte(`eyJoZWxsbyI6IndvcmxkIn0=`),
- DataEncoded: true,
- },
- },
- "simple v0.3 structured": {
- codec: http.CodecV03{},
- msg: &http.Message{
- Header: map[string][]string{
- "Content-Type": {"application/cloudevents+json"},
- },
- Body: toBytes(map[string]interface{}{
- "specversion": "0.3",
- "id": "ABC-123",
- "type": "com.example.test",
- "source": "http://example.com/source",
- }),
- },
- want: &cloudevents.Event{
- Context: &cloudevents.EventContextV03{
- SpecVersion: cloudevents.CloudEventsVersionV03,
- Type: "com.example.test",
- Source: *source,
- ID: "ABC-123",
- },
- },
- },
- "full v0.3 structured": {
- codec: http.CodecV03{},
- msg: &http.Message{
- Header: map[string][]string{
- "Content-Type": {"application/cloudevents+json"},
- },
- Body: toBytes(map[string]interface{}{
- "specversion": "0.3",
- "datacontenttype": "application/json",
- "data": map[string]interface{}{
- "hello": "world",
- },
- "id": "ABC-123",
- "time": now,
- "type": "com.example.test",
- "test": "extended",
- "schemaurl": "http://example.com/schema",
- "source": "http://example.com/source",
- "subject": "resource",
- }),
- },
- want: &cloudevents.Event{
- Context: cloudevents.EventContextV03{
- SpecVersion: cloudevents.CloudEventsVersionV03,
- ID: "ABC-123",
- Time: &now,
- Type: "com.example.test",
- SchemaURL: schema,
- DataContentType: cloudevents.StringOfApplicationJSON(),
- Source: *source,
- Subject: &subject,
- Extensions: map[string]interface{}{
- "test": "extended",
- },
- }.AsV03(),
- Data: toBytes(map[string]interface{}{
- "hello": "world",
- }),
- DataEncoded: true,
- },
- },
- "full v0.3 structured base64": {
- codec: http.CodecV03{},
- msg: &http.Message{
- Header: map[string][]string{
- "Content-Type": {"application/cloudevents+json"},
- },
- Body: toBytes(map[string]interface{}{
- "specversion": "0.3",
- "datacontentencoding": "base64",
- "datacontenttype": "application/json",
- "data": "eyJoZWxsbyI6IndvcmxkIn0=",
- "id": "ABC-123",
- "time": now,
- "type": "com.example.test",
- "test": "extended",
- "schemaurl": "http://example.com/schema",
- "source": "http://example.com/source",
- "subject": "resource",
- }),
- },
- want: &cloudevents.Event{
- Context: cloudevents.EventContextV03{
- SpecVersion: cloudevents.CloudEventsVersionV03,
- ID: "ABC-123",
- Time: &now,
- Type: "com.example.test",
- SchemaURL: schema,
- DataContentType: cloudevents.StringOfApplicationJSON(),
- DataContentEncoding: cloudevents.StringOfBase64(),
- Source: *source,
- Subject: &subject,
- Extensions: map[string]interface{}{
- "test": "extended",
- },
- }.AsV03(),
- Data: []byte(`"eyJoZWxsbyI6IndvcmxkIn0="`), // TODO: structured comes in quoted. Unquote?
- DataEncoded: true,
- },
- },
- "simple v0.3 binary with short header": {
- codec: http.CodecV03{},
- msg: &http.Message{
- Header: map[string][]string{
- "ce-specversion": {"0.3"},
- "ce-id": {"ABC-123"},
- "ce-type": {"com.example.test"},
- "ce-source": {"http://example.com/source"},
- "Content-Type": {"application/json"},
- "X": {"Notice how short the header's name is"},
- },
- },
- want: &cloudevents.Event{
- Context: &cloudevents.EventContextV03{
- SpecVersion: cloudevents.CloudEventsVersionV03,
- DataContentType: cloudevents.StringOfApplicationJSON(),
- Type: "com.example.test",
- Source: *source,
- ID: "ABC-123",
- },
- },
- },
- }
- for n, tc := range testCases {
- t.Run(n, func(t *testing.T) {
-
- got, err := tc.codec.Decode(context.TODO(), tc.msg)
-
- if tc.wantErr != nil || err != nil {
- if diff := cmp.Diff(tc.wantErr, err); diff != "" {
- t.Errorf("unexpected error (-want, +got) = %v", diff)
- }
- return
- }
-
- if diff := cmp.Diff(tc.want, got); diff != "" {
- t.Errorf("unexpected event (-want, +got) = %v", diff)
- }
- })
- }
-}
diff --git a/v1/cloudevents/transport/http/codec_v1.go b/v1/cloudevents/transport/http/codec_v1.go
deleted file mode 100644
index 4a9c0b8e5..000000000
--- a/v1/cloudevents/transport/http/codec_v1.go
+++ /dev/null
@@ -1,245 +0,0 @@
-package http
-
-import (
- "context"
- "fmt"
- "net/http"
- "net/textproto"
- "strings"
-
- "github.com/cloudevents/sdk-go/v1/cloudevents"
- cecontext "github.com/cloudevents/sdk-go/v1/cloudevents/context"
- "github.com/cloudevents/sdk-go/v1/cloudevents/observability"
- "github.com/cloudevents/sdk-go/v1/cloudevents/transport"
- "github.com/cloudevents/sdk-go/v1/cloudevents/types"
-)
-
-// CodecV1 represents a http transport codec that uses CloudEvents spec v1.0
-type CodecV1 struct {
- CodecStructured
-
- DefaultEncoding Encoding
-}
-
-// Adheres to Codec
-var _ transport.Codec = (*CodecV1)(nil)
-
-// Encode implements Codec.Encode
-func (v CodecV1) Encode(ctx context.Context, e cloudevents.Event) (transport.Message, error) {
- encoding := v.DefaultEncoding
- strEnc := cecontext.EncodingFrom(ctx)
- if strEnc != "" {
- switch strEnc {
- case Binary:
- encoding = BinaryV1
- case Structured:
- encoding = StructuredV1
- }
- }
-
- _, r := observability.NewReporter(ctx, CodecObserved{o: reportEncode, c: encoding.Codec()})
- m, err := v.obsEncode(ctx, e, encoding)
- if err != nil {
- r.Error()
- } else {
- r.OK()
- }
- return m, err
-}
-
-func (v CodecV1) obsEncode(ctx context.Context, e cloudevents.Event, encoding Encoding) (transport.Message, error) {
- switch encoding {
- case Default:
- fallthrough
- case BinaryV1:
- return v.encodeBinary(ctx, e)
- case StructuredV1:
- return v.encodeStructured(ctx, e)
- case BatchedV1:
- return nil, fmt.Errorf("not implemented")
- default:
- return nil, fmt.Errorf("unknown encoding: %d", encoding)
- }
-}
-
-// Decode implements Codec.Decode
-func (v CodecV1) Decode(ctx context.Context, msg transport.Message) (*cloudevents.Event, error) {
- _, r := observability.NewReporter(ctx, CodecObserved{o: reportDecode, c: v.inspectEncoding(ctx, msg).Codec()}) // TODO: inspectEncoding is not free.
- e, err := v.obsDecode(ctx, msg)
- if err != nil {
- r.Error()
- } else {
- r.OK()
- }
- return e, err
-}
-
-func (v CodecV1) obsDecode(ctx context.Context, msg transport.Message) (*cloudevents.Event, error) {
- switch v.inspectEncoding(ctx, msg) {
- case BinaryV1:
- return v.decodeBinary(ctx, msg)
- case StructuredV1:
- return v.decodeStructured(ctx, cloudevents.CloudEventsVersionV1, msg)
- case BatchedV1:
- return nil, fmt.Errorf("not implemented")
- default:
- return nil, transport.NewErrMessageEncodingUnknown("V1", TransportName)
- }
-}
-
-func (v CodecV1) encodeBinary(ctx context.Context, e cloudevents.Event) (transport.Message, error) {
- header, err := v.toHeaders(e.Context.AsV1())
- if err != nil {
- return nil, err
- }
-
- body, err := e.DataBytes()
- if err != nil {
- return nil, err
- }
-
- msg := &Message{
- Header: header,
- Body: body,
- }
-
- return msg, nil
-}
-
-func (v CodecV1) toHeaders(ec *cloudevents.EventContextV1) (http.Header, error) {
- h := http.Header{}
- h.Set("ce-specversion", ec.SpecVersion)
- h.Set("ce-type", ec.Type)
- h.Set("ce-source", ec.Source.String())
- if ec.Subject != nil {
- h.Set("ce-subject", *ec.Subject)
- }
- h.Set("ce-id", ec.ID)
- if ec.Time != nil && !ec.Time.IsZero() {
- h.Set("ce-time", ec.Time.String())
- }
- if ec.DataSchema != nil {
- h.Set("ce-dataschema", ec.DataSchema.String())
- }
- if ec.DataContentType != nil && *ec.DataContentType != "" {
- h.Set("Content-Type", *ec.DataContentType)
- }
-
- for k, v := range ec.Extensions {
- k = strings.ToLower(k)
- // Per spec, extensions are strings and converted to a list of headers as:
- // ce-key: value
- cstr, err := types.Format(v)
- if err != nil {
- return h, err
- }
- h.Set("ce-"+k, cstr)
- }
-
- return h, nil
-}
-
-func (v CodecV1) decodeBinary(ctx context.Context, msg transport.Message) (*cloudevents.Event, error) {
- m, ok := msg.(*Message)
- if !ok {
- return nil, fmt.Errorf("failed to convert transport.Message to http.Message")
- }
- ca, err := v.fromHeaders(m.Header)
- if err != nil {
- return nil, err
- }
- var body interface{}
- if len(m.Body) > 0 {
- body = m.Body
- }
- return &cloudevents.Event{
- Context: &ca,
- Data: body,
- DataEncoded: body != nil,
- }, nil
-}
-
-func (v CodecV1) fromHeaders(h http.Header) (cloudevents.EventContextV1, error) {
- // Normalize headers.
- for k, v := range h {
- ck := textproto.CanonicalMIMEHeaderKey(k)
- if k != ck {
- delete(h, k)
- h[ck] = v
- }
- }
-
- ec := cloudevents.EventContextV1{}
-
- ec.SpecVersion = h.Get("ce-specversion")
- h.Del("ce-specversion")
-
- ec.ID = h.Get("ce-id")
- h.Del("ce-id")
-
- ec.Type = h.Get("ce-type")
- h.Del("ce-type")
-
- source := types.ParseURIRef(h.Get("ce-source"))
- if source != nil {
- ec.Source = *source
- }
- h.Del("ce-source")
-
- subject := h.Get("ce-subject")
- if subject != "" {
- ec.Subject = &subject
- }
- h.Del("ce-subject")
-
- var err error
- ec.Time, err = types.ParseTimestamp(h.Get("ce-time"))
- if err != nil {
- return ec, err
- }
- h.Del("ce-time")
-
- ec.DataSchema = types.ParseURI(h.Get("ce-dataschema"))
- h.Del("ce-dataschema")
-
- contentType := h.Get("Content-Type")
- if contentType != "" {
- ec.DataContentType = &contentType
- }
- h.Del("Content-Type")
-
- // At this point, we have deleted all the known headers.
- // Everything left is assumed to be an extension.
-
- extensions := make(map[string]interface{})
- for k := range h {
- k = strings.ToLower(k)
- if len(k) > len("ce-") && strings.EqualFold(k[:len("ce-")], "ce-") {
- ak := strings.ToLower(k[len("ce-"):])
- extensions[ak] = h.Get(k)
- }
- }
- if len(extensions) > 0 {
- ec.Extensions = extensions
- }
- return ec, nil
-}
-
-func (v CodecV1) inspectEncoding(ctx context.Context, msg transport.Message) Encoding {
- version := msg.CloudEventsVersion()
- if version != cloudevents.CloudEventsVersionV1 {
- return Unknown
- }
- m, ok := msg.(*Message)
- if !ok {
- return Unknown
- }
- contentType := m.Header.Get("Content-Type")
- if contentType == cloudevents.ApplicationCloudEventsJSON {
- return StructuredV1
- }
- if contentType == cloudevents.ApplicationCloudEventsBatchJSON {
- return BatchedV1
- }
- return BinaryV1
-}
diff --git a/v1/cloudevents/transport/http/codec_v1_test.go b/v1/cloudevents/transport/http/codec_v1_test.go
deleted file mode 100644
index dd719a31d..000000000
--- a/v1/cloudevents/transport/http/codec_v1_test.go
+++ /dev/null
@@ -1,487 +0,0 @@
-package http_test
-
-import (
- "context"
- "net/url"
- "testing"
- "time"
-
- "github.com/google/go-cmp/cmp"
-
- "github.com/cloudevents/sdk-go/v1/cloudevents"
- "github.com/cloudevents/sdk-go/v1/cloudevents/transport/http"
- "github.com/cloudevents/sdk-go/v1/cloudevents/types"
-)
-
-func TestCodecV1_Encode(t *testing.T) {
- now := types.Timestamp{Time: time.Now().UTC()}
- sourceUrl, _ := url.Parse("http://example.com/source")
- source := &types.URIRef{URL: *sourceUrl}
-
- subject := "resource"
-
- DataSchema, _ := url.Parse("http://example.com/schema")
- schema := &types.URI{URL: *DataSchema}
-
- testCases := map[string]struct {
- codec http.CodecV1
- event cloudevents.Event
- want *http.Message
- wantErr error
- }{
- "simple v1.0 default": {
- codec: http.CodecV1{},
- event: cloudevents.Event{
- Context: &cloudevents.EventContextV1{
- Type: "com.example.test",
- Source: *source,
- ID: "ABC-123",
- },
- },
- want: &http.Message{
- Header: map[string][]string{
- "Ce-Specversion": {"1.0"},
- "Ce-Id": {"ABC-123"},
- "Ce-Type": {"com.example.test"},
- "Ce-Source": {"http://example.com/source"},
- },
- },
- },
- "full v1.0 default": {
- codec: http.CodecV1{},
- event: cloudevents.Event{
- Context: &cloudevents.EventContextV1{
- ID: "ABC-123",
- Time: &now,
- Type: "com.example.test",
- DataSchema: schema,
- DataContentType: cloudevents.StringOfApplicationJSON(),
- Source: *source,
- Subject: &subject,
- Extensions: map[string]interface{}{
- "test": "extended",
- },
- },
- Data: map[string]interface{}{
- "hello": "world",
- },
- },
- want: &http.Message{
- Header: map[string][]string{
- "Ce-Specversion": {"1.0"},
- "Ce-Id": {"ABC-123"},
- "Ce-Time": {now.Format(time.RFC3339Nano)},
- "Ce-Type": {"com.example.test"},
- "Ce-Source": {"http://example.com/source"},
- "Ce-Subject": {"resource"},
- "Ce-Dataschema": {"http://example.com/schema"},
- "Ce-Test": {"extended"},
- "Content-Type": {"application/json"},
- },
- Body: []byte(`{"hello":"world"}`),
- },
- },
- "simple v1.0 binary": {
- codec: http.CodecV1{DefaultEncoding: http.BinaryV1},
- event: cloudevents.Event{
- Context: cloudevents.EventContextV1{
- Type: "com.example.test",
- Source: *source,
- ID: "ABC-123",
- }.AsV1(),
- },
- want: &http.Message{
- Header: map[string][]string{
- "Ce-Specversion": {"1.0"},
- "Ce-Id": {"ABC-123"},
- "Ce-Type": {"com.example.test"},
- "Ce-Source": {"http://example.com/source"},
- },
- },
- },
- "full v1.0 binary": {
- codec: http.CodecV1{DefaultEncoding: http.BinaryV1},
- event: cloudevents.Event{
- Context: &cloudevents.EventContextV1{
- ID: "ABC-123",
- Time: &now,
- Type: "com.example.test",
- DataSchema: schema,
- DataContentType: cloudevents.StringOfApplicationJSON(),
- Source: *source,
- Subject: &subject,
- Extensions: map[string]interface{}{
- "test": "extended",
- },
- },
- Data: map[string]interface{}{
- "hello": "world",
- },
- },
- want: &http.Message{
- Header: map[string][]string{
- "Ce-Specversion": {"1.0"},
- "Ce-Id": {"ABC-123"},
- "Ce-Time": {now.Format(time.RFC3339Nano)},
- "Ce-Type": {"com.example.test"},
- "Ce-Source": {"http://example.com/source"},
- "Ce-Subject": {"resource"},
- "Ce-Dataschema": {"http://example.com/schema"},
- "Ce-Test": {"extended"},
- "Content-Type": {"application/json"},
- },
- Body: []byte(`{"hello":"world"}`),
- },
- },
- "simple v1.0 structured": {
- codec: http.CodecV1{DefaultEncoding: http.StructuredV1},
- event: cloudevents.Event{
- Context: cloudevents.EventContextV1{
- Type: "com.example.test",
- Source: *source,
- ID: "ABC-123",
- }.AsV1(),
- },
- want: &http.Message{
- Header: map[string][]string{
- "Content-Type": {"application/cloudevents+json"},
- },
- Body: func() []byte {
- body := map[string]interface{}{
- "specversion": "1.0",
- "id": "ABC-123",
- "type": "com.example.test",
- "source": "http://example.com/source",
- }
- return toBytes(body)
- }(),
- },
- },
- "full v1.0 structured": {
- codec: http.CodecV1{DefaultEncoding: http.StructuredV1},
- event: cloudevents.Event{
- Context: cloudevents.EventContextV1{
- ID: "ABC-123",
- Time: &now,
- Type: "com.example.test",
- DataSchema: schema,
- DataContentType: cloudevents.StringOfApplicationJSON(),
- Source: *source,
- Subject: &subject,
- Extensions: map[string]interface{}{
- "test": "extended",
- },
- }.AsV1(),
- Data: map[string]interface{}{
- "hello": "world",
- },
- },
- want: &http.Message{
- Header: map[string][]string{
- "Content-Type": {"application/cloudevents+json"},
- },
- Body: func() []byte {
- body := map[string]interface{}{
- "specversion": "1.0",
- "datacontenttype": "application/json",
- "data": map[string]interface{}{
- "hello": "world",
- },
- "id": "ABC-123",
- "time": now,
- "type": "com.example.test",
- "test": "extended",
- "dataschema": "http://example.com/schema",
- "source": "http://example.com/source",
- "subject": "resource",
- }
- return toBytes(body)
- }(),
- },
- },
- "full v1.0 structured base64": {
- codec: http.CodecV1{DefaultEncoding: http.StructuredV1},
- event: cloudevents.Event{
- Context: cloudevents.EventContextV1{
- ID: "ABC-123",
- Time: &now,
- Type: "com.example.test",
- DataSchema: schema,
- DataContentType: cloudevents.StringOfApplicationJSON(),
- Source: *source,
- Subject: &subject,
- Extensions: map[string]interface{}{
- "test": "extended",
- },
- }.AsV1(),
- Data: []byte(`{"hello":"world"}`),
- DataBinary: true,
- DataEncoded: true,
- },
- want: &http.Message{
- Header: map[string][]string{
- "Content-Type": {"application/cloudevents+json"},
- },
- Body: func() []byte {
- body := map[string]interface{}{
- "specversion": "1.0",
- "datacontenttype": "application/json",
- "data_base64": "eyJoZWxsbyI6IndvcmxkIn0=",
- "id": "ABC-123",
- "time": now,
- "type": "com.example.test",
- "test": "extended",
- "dataschema": "http://example.com/schema",
- "source": "http://example.com/source",
- "subject": "resource",
- }
- return toBytes(body)
- }(),
- },
- },
- }
- for n, tc := range testCases {
- t.Run(n, func(t *testing.T) {
-
- got, err := tc.codec.Encode(context.TODO(), tc.event)
-
- if tc.wantErr != nil || err != nil {
- if diff := cmp.Diff(tc.wantErr, err); diff != "" {
- t.Errorf("unexpected error (-want, +got) = %v", diff)
- }
- return
- }
-
- if diff := cmp.Diff(tc.want, got); diff != "" {
-
- if msg, ok := got.(*http.Message); ok {
- // It is hard to read the byte dump
- want := string(tc.want.Body)
- got := string(msg.Body)
- if diff := cmp.Diff(want, got); diff != "" {
- t.Errorf("unexpected message body (-want, +got) = %v", diff)
- return
- }
- }
-
- t.Errorf("unexpected message (-want, +got) = %v", diff)
- }
- })
- }
-}
-
-func TestCodecV1_Decode(t *testing.T) {
- now := types.Timestamp{Time: time.Now()}
- sourceUrl, _ := url.Parse("http://example.com/source")
- source := &types.URIRef{URL: *sourceUrl}
-
- subject := "resource"
-
- DataSchema, _ := url.Parse("http://example.com/schema")
- schema := &types.URI{URL: *DataSchema}
-
- testCases := map[string]struct {
- codec http.CodecV1
- msg *http.Message
- want *cloudevents.Event
- wantErr error
- }{
- "simple v1.0 binary": {
- codec: http.CodecV1{},
- msg: &http.Message{
- Header: map[string][]string{
- "ce-specversion": {"1.0"},
- "ce-id": {"ABC-123"},
- "ce-type": {"com.example.test"},
- "ce-source": {"http://example.com/source"},
- "Content-Type": {"application/json"},
- },
- },
- want: &cloudevents.Event{
- Context: &cloudevents.EventContextV1{
- SpecVersion: cloudevents.CloudEventsVersionV1,
- DataContentType: cloudevents.StringOfApplicationJSON(),
- Type: "com.example.test",
- Source: *source,
- ID: "ABC-123",
- },
- },
- },
- "full v1.0 binary": {
- codec: http.CodecV1{},
- msg: &http.Message{
- Header: map[string][]string{
- "ce-specversion": {"1.0"},
- "ce-id": {"ABC-123"},
- "ce-time": {now.Format(time.RFC3339Nano)},
- "ce-type": {"com.example.test"},
- "ce-source": {"http://example.com/source"},
- "ce-subject": {"resource"},
- "ce-dataschema": {"http://example.com/schema"},
- "ce-test": {"extended binary"},
- "Content-Type": {"application/json"},
- },
- Body: toBytes(map[string]interface{}{
- "hello": "world",
- }),
- },
- want: &cloudevents.Event{
- Context: &cloudevents.EventContextV1{
- SpecVersion: cloudevents.CloudEventsVersionV1,
- ID: "ABC-123",
- Time: &now,
- Type: "com.example.test",
- DataSchema: schema,
- DataContentType: cloudevents.StringOfApplicationJSON(),
- Source: *source,
- Subject: &subject,
- Extensions: map[string]interface{}{
- "test": "extended binary",
- },
- },
- Data: toBytes(map[string]interface{}{
- "hello": "world",
- }),
- DataEncoded: true,
- },
- },
- "simple v1.0 structured": {
- codec: http.CodecV1{},
- msg: &http.Message{
- Header: map[string][]string{
- "Content-Type": {"application/cloudevents+json"},
- },
- Body: toBytes(map[string]interface{}{
- "specversion": "1.0",
- "id": "ABC-123",
- "type": "com.example.test",
- "source": "http://example.com/source",
- }),
- },
- want: &cloudevents.Event{
- Context: &cloudevents.EventContextV1{
- SpecVersion: cloudevents.CloudEventsVersionV1,
- Type: "com.example.test",
- Source: *source,
- ID: "ABC-123",
- },
- },
- },
- "full v1.0 structured": {
- codec: http.CodecV1{},
- msg: &http.Message{
- Header: map[string][]string{
- "Content-Type": {"application/cloudevents+json"},
- },
- Body: toBytes(map[string]interface{}{
- "specversion": "1.0",
- "datacontenttype": "application/json",
- "data": map[string]interface{}{
- "hello": "world",
- },
- "id": "ABC-123",
- "time": now,
- "type": "com.example.test",
- "test": "extended",
- "dataschema": "http://example.com/schema",
- "source": "http://example.com/source",
- "subject": "resource",
- }),
- },
- want: &cloudevents.Event{
- Context: cloudevents.EventContextV1{
- SpecVersion: cloudevents.CloudEventsVersionV1,
- ID: "ABC-123",
- Time: &now,
- Type: "com.example.test",
- DataSchema: schema,
- DataContentType: cloudevents.StringOfApplicationJSON(),
- Source: *source,
- Subject: &subject,
- Extensions: map[string]interface{}{
- "test": "extended",
- },
- }.AsV1(),
- Data: toBytes(map[string]interface{}{
- "hello": "world",
- }),
- DataEncoded: true,
- },
- },
- "full v1.0 structured base64": {
- codec: http.CodecV1{},
- msg: &http.Message{
- Header: map[string][]string{
- "Content-Type": {"application/cloudevents+json"},
- },
- Body: toBytes(map[string]interface{}{
- "specversion": "1.0",
- "datacontenttype": "application/json",
- "data_base64": "eyJoZWxsbyI6IndvcmxkIn0=",
- "id": "ABC-123",
- "time": now,
- "type": "com.example.test",
- "test": "extended",
- "dataschema": "http://example.com/schema",
- "source": "http://example.com/source",
- "subject": "resource",
- }),
- },
- want: &cloudevents.Event{
- Context: cloudevents.EventContextV1{
- SpecVersion: cloudevents.CloudEventsVersionV1,
- ID: "ABC-123",
- Time: &now,
- Type: "com.example.test",
- DataSchema: schema,
- DataContentType: cloudevents.StringOfApplicationJSON(),
- Source: *source,
- Subject: &subject,
- Extensions: map[string]interface{}{
- "test": "extended",
- },
- }.AsV1(),
- Data: []byte(`{"hello":"world"}`),
- },
- },
- "simple v1.0 binary with short header": {
- codec: http.CodecV1{},
- msg: &http.Message{
- Header: map[string][]string{
- "ce-specversion": {"1.0"},
- "ce-id": {"ABC-123"},
- "ce-type": {"com.example.test"},
- "ce-source": {"http://example.com/source"},
- "Content-Type": {"application/json"},
- "X": {"Notice how short the header's name is"},
- },
- },
- want: &cloudevents.Event{
- Context: &cloudevents.EventContextV1{
- SpecVersion: cloudevents.CloudEventsVersionV1,
- DataContentType: cloudevents.StringOfApplicationJSON(),
- Type: "com.example.test",
- Source: *source,
- ID: "ABC-123",
- },
- },
- },
- }
- for n, tc := range testCases {
- t.Run(n, func(t *testing.T) {
-
- got, err := tc.codec.Decode(context.TODO(), tc.msg)
-
- if tc.wantErr != nil || err != nil {
- if diff := cmp.Diff(tc.wantErr, err); diff != "" {
- t.Errorf("unexpected error (-want, +got) = %v", diff)
- }
- return
- }
-
- if diff := cmp.Diff(tc.want, got); diff != "" {
- t.Errorf("unexpected event (-want, +got) = %v", diff)
- }
- })
- }
-}
diff --git a/v1/cloudevents/transport/http/context.go b/v1/cloudevents/transport/http/context.go
deleted file mode 100644
index 8e3c7b44e..000000000
--- a/v1/cloudevents/transport/http/context.go
+++ /dev/null
@@ -1,212 +0,0 @@
-package http
-
-import (
- "context"
- "fmt"
- "net/http"
- "net/url"
- "strconv"
- "strings"
-)
-
-// TransportContext allows a Receiver to understand the context of a request.
-type TransportContext struct {
- URI string
- Host string
- Method string
- Header http.Header
- StatusCode int
-
- // IgnoreHeaderPrefixes controls what comes back from AttendToHeaders.
- // AttendToHeaders controls what is output for .String()
- IgnoreHeaderPrefixes []string
-}
-
-// NewTransportContext creates a new TransportContext from a http.Request.
-func NewTransportContext(req *http.Request) TransportContext {
- var tx *TransportContext
- if req != nil {
- tx = &TransportContext{
- URI: req.RequestURI,
- Host: req.Host,
- Method: req.Method,
- Header: req.Header,
- }
- } else {
- tx = &TransportContext{}
- }
- tx.AddIgnoreHeaderPrefix("accept-encoding", "user-agent", "connection", "content-type")
- return *tx
-}
-
-// NewTransportContextFromResponse creates a new TransportContext from a http.Response.
-// If `res` is nil, it returns a context with a http.StatusInternalServerError status code.
-func NewTransportContextFromResponse(res *http.Response) TransportContext {
- var tx *TransportContext
- if res != nil {
- tx = &TransportContext{
- Header: res.Header,
- StatusCode: res.StatusCode,
- }
- } else {
- tx = &TransportContext{StatusCode: http.StatusInternalServerError}
- }
- tx.AddIgnoreHeaderPrefix("accept-encoding", "user-agent", "connection", "content-type")
- return *tx
-}
-
-// TransportResponseContext allows a Receiver response with http transport specific fields.
-type TransportResponseContext struct {
- // Header will be merged with the response headers.
- Header http.Header
-}
-
-// AttendToHeaders returns the list of headers that exist in the TransportContext that are not currently in
-// tx.IgnoreHeaderPrefix.
-func (tx TransportContext) AttendToHeaders() []string {
- a := []string(nil)
- if tx.Header != nil && len(tx.Header) > 0 {
- for k := range tx.Header {
- if tx.shouldIgnoreHeader(k) {
- continue
- }
- a = append(a, k)
- }
- }
- return a
-}
-
-func (tx TransportContext) shouldIgnoreHeader(h string) bool {
- for _, v := range tx.IgnoreHeaderPrefixes {
- if strings.HasPrefix(strings.ToLower(h), strings.ToLower(v)) {
- return true
- }
- }
- return false
-}
-
-// String generates a pretty-printed version of the resource as a string.
-func (tx TransportContext) String() string {
- b := strings.Builder{}
-
- b.WriteString("Transport Context,\n")
-
- empty := b.Len()
-
- if tx.URI != "" {
- b.WriteString(" URI: " + tx.URI + "\n")
- }
- if tx.Host != "" {
- b.WriteString(" Host: " + tx.Host + "\n")
- }
-
- if tx.Method != "" {
- b.WriteString(" Method: " + tx.Method + "\n")
- }
-
- if tx.StatusCode != 0 {
- b.WriteString(" StatusCode: " + strconv.Itoa(tx.StatusCode) + "\n")
- }
-
- if tx.Header != nil && len(tx.Header) > 0 {
- b.WriteString(" Header:\n")
- for _, k := range tx.AttendToHeaders() {
- b.WriteString(fmt.Sprintf(" %s: %s\n", k, tx.Header.Get(k)))
- }
- }
-
- if b.Len() == empty {
- b.WriteString(" nil\n")
- }
-
- return b.String()
-}
-
-// AddIgnoreHeaderPrefix controls what header key is to be attended to and/or printed.
-func (tx *TransportContext) AddIgnoreHeaderPrefix(prefix ...string) {
- if tx.IgnoreHeaderPrefixes == nil {
- tx.IgnoreHeaderPrefixes = []string(nil)
- }
- tx.IgnoreHeaderPrefixes = append(tx.IgnoreHeaderPrefixes, prefix...)
-}
-
-// Opaque key type used to store TransportContext
-type transportContextKeyType struct{}
-
-var transportContextKey = transportContextKeyType{}
-
-// WithTransportContext return a context with the given TransportContext into the provided context object.
-func WithTransportContext(ctx context.Context, tcxt TransportContext) context.Context {
- return context.WithValue(ctx, transportContextKey, tcxt)
-}
-
-// TransportContextFrom pulls a TransportContext out of a context. Always
-// returns a non-nil object.
-func TransportContextFrom(ctx context.Context) TransportContext {
- tctx := ctx.Value(transportContextKey)
- if tctx != nil {
- if tx, ok := tctx.(TransportContext); ok {
- return tx
- }
- if tx, ok := tctx.(*TransportContext); ok {
- return *tx
- }
- }
- return TransportContext{}
-}
-
-// Opaque key type used to store Headers
-type headerKeyType struct{}
-
-var headerKey = headerKeyType{}
-
-// ContextWithHeader returns a context with a header added to the given context.
-// Can be called multiple times to set multiple header key/value pairs.
-func ContextWithHeader(ctx context.Context, key, value string) context.Context {
- header := HeaderFrom(ctx)
- header.Add(key, value)
- return context.WithValue(ctx, headerKey, header)
-}
-
-// HeaderFrom extracts the header object in the given context. Always returns a non-nil Header.
-func HeaderFrom(ctx context.Context) http.Header {
- ch := http.Header{}
- header := ctx.Value(headerKey)
- if header != nil {
- if h, ok := header.(http.Header); ok {
- copyHeaders(h, ch)
- }
- }
- return ch
-}
-
-// SetContextHeader sets the context's headers replacing any headers currently in context.
-func SetContextHeaders(ctx context.Context, headers http.Header) context.Context {
- return context.WithValue(ctx, headerKey, headers)
-}
-
-// Opaque key type used to store long poll target.
-type longPollTargetKeyType struct{}
-
-var longPollTargetKey = longPollTargetKeyType{}
-
-// WithLongPollTarget returns a new context with the given long poll target.
-// `target` should be a full URL and will be injected into the long polling
-// http request within StartReceiver.
-func ContextWithLongPollTarget(ctx context.Context, target string) context.Context {
- return context.WithValue(ctx, longPollTargetKey, target)
-}
-
-// LongPollTargetFrom looks in the given context and returns `target` as a
-// parsed url if found and valid, otherwise nil.
-func LongPollTargetFrom(ctx context.Context) *url.URL {
- c := ctx.Value(longPollTargetKey)
- if c != nil {
- if s, ok := c.(string); ok && s != "" {
- if target, err := url.Parse(s); err == nil {
- return target
- }
- }
- }
- return nil
-}
diff --git a/v1/cloudevents/transport/http/context_test.go b/v1/cloudevents/transport/http/context_test.go
deleted file mode 100644
index c7efe7144..000000000
--- a/v1/cloudevents/transport/http/context_test.go
+++ /dev/null
@@ -1,315 +0,0 @@
-package http_test
-
-import (
- "context"
- nethttp "net/http"
- "sort"
- "testing"
-
- "github.com/cloudevents/sdk-go/v1/cloudevents/transport/http"
- "github.com/google/go-cmp/cmp"
- "github.com/google/go-cmp/cmp/cmpopts"
-)
-
-func TestTransportContext(t *testing.T) {
- testCases := map[string]struct {
- t http.TransportContext
- ctx context.Context
- want http.TransportContext
- }{
- "nil context": {},
- "nil context, set transport context": {
- t: http.TransportContext{
- Host: "unit test",
- Method: "unit test",
- },
- want: http.TransportContext{
- Host: "unit test",
- Method: "unit test",
- },
- },
- "todo context, set transport context": {
- ctx: context.TODO(),
- t: http.TransportContext{
- Host: "unit test",
- Method: "unit test",
- },
- want: http.TransportContext{
- Host: "unit test",
- Method: "unit test",
- },
- },
- "bad transport context": {
- ctx: context.TODO(),
- },
- "already set transport context": {
- ctx: http.WithTransportContext(context.TODO(),
- http.TransportContext{
- Host: "existing test",
- Method: "exiting test",
- }),
- t: http.TransportContext{
- Host: "unit test",
- Method: "unit test",
- },
- want: http.TransportContext{
- Host: "unit test",
- Method: "unit test",
- },
- },
- }
- for n, tc := range testCases {
- t.Run(n, func(t *testing.T) {
-
- ctx := http.WithTransportContext(tc.ctx, tc.t)
-
- got := http.TransportContextFrom(ctx)
-
- if diff := cmp.Diff(tc.want, got); diff != "" {
- t.Errorf("unexpected (-want, +got) = %v", diff)
- }
- })
- }
-}
-
-func TestNewTransportContext(t *testing.T) {
- testCases := map[string]struct {
- r *nethttp.Request
- want http.TransportContext
- wantStr string
- }{
- "nil request": {
- want: http.TransportContext{},
- wantStr: `Transport Context,
- nil
-`,
- },
- "full request": {
- r: &nethttp.Request{
- Host: "unit test host",
- Method: "unit test method",
- RequestURI: "unit test uri",
- Header: func() nethttp.Header {
- h := nethttp.Header{}
- h.Set("unit", "test header")
- return h
- }(),
- },
- want: http.TransportContext{
- Host: "unit test host",
- Method: "unit test method",
- URI: "unit test uri",
- Header: func() nethttp.Header {
- h := nethttp.Header{}
- h.Set("unit", "test header")
- return h
- }(),
- },
- wantStr: `Transport Context,
- URI: unit test uri
- Host: unit test host
- Method: unit test method
- Header:
- Unit: test header
-`,
- },
- "no headers request": {
- r: &nethttp.Request{
- Host: "unit test host",
- Method: "unit test method",
- RequestURI: "unit test uri",
- },
- want: http.TransportContext{
- Host: "unit test host",
- Method: "unit test method",
- URI: "unit test uri",
- },
- wantStr: `Transport Context,
- URI: unit test uri
- Host: unit test host
- Method: unit test method
-`,
- },
- }
- for n, tc := range testCases {
- t.Run(n, func(t *testing.T) {
-
- got := http.NewTransportContext(tc.r)
-
- if diff := cmp.Diff(tc.want, got, cmpopts.IgnoreFields(http.TransportContext{}, "IgnoreHeaderPrefixes")); diff != "" {
- t.Errorf("unexpected (-want, +got) = %v", diff)
- }
-
- if tc.wantStr != "" {
- gotStr := got.String()
-
- if diff := cmp.Diff(tc.wantStr, gotStr); diff != "" {
- t.Errorf("unexpected (-want, +got) = %v", diff)
- }
- }
- })
- }
-}
-
-func TestNewTransportContextFromResponse(t *testing.T) {
- testCases := map[string]struct {
- r *nethttp.Response
- want http.TransportContext
- wantStr string
- }{
- "nil response": {
- want: http.TransportContext{
- StatusCode: nethttp.StatusInternalServerError,
- },
- wantStr: `Transport Context,
- StatusCode: 500
-`,
- },
- "full response": {
- r: &nethttp.Response{
- Header: func() nethttp.Header {
- h := nethttp.Header{}
- h.Set("unit", "test header")
- return h
- }(),
- StatusCode: nethttp.StatusOK,
- },
- want: http.TransportContext{
- Header: func() nethttp.Header {
- h := nethttp.Header{}
- h.Set("unit", "test header")
- return h
- }(),
- StatusCode: nethttp.StatusOK,
- },
- wantStr: `Transport Context,
- StatusCode: 200
- Header:
- Unit: test header
-`,
- },
- "no headers response": {
- r: &nethttp.Response{
- StatusCode: nethttp.StatusOK,
- },
- want: http.TransportContext{
- StatusCode: nethttp.StatusOK,
- },
- wantStr: `Transport Context,
- StatusCode: 200
-`,
- },
- }
- for n, tc := range testCases {
- t.Run(n, func(t *testing.T) {
-
- got := http.NewTransportContextFromResponse(tc.r)
-
- if diff := cmp.Diff(tc.want, got, cmpopts.IgnoreFields(http.TransportContext{}, "IgnoreHeaderPrefixes")); diff != "" {
- t.Errorf("unexpected (-want, +got) = %v", diff)
- }
-
- if tc.wantStr != "" {
- gotStr := got.String()
-
- if diff := cmp.Diff(tc.wantStr, gotStr); diff != "" {
- t.Errorf("unexpected (-want, +got) = %v", diff)
- }
- }
- })
- }
-}
-
-func TestAttendToHeader(t *testing.T) {
- testCases := map[string]struct {
- header nethttp.Header
- ignore []string
- want []string
- }{
- "nil": {},
- "no new ignore": {
- header: func() nethttp.Header {
- h := nethttp.Header{}
- h.Set("unit", "test header")
- h.Set("testing", "header unit")
- return h
- }(),
- want: []string{"Unit", "Testing"},
- },
- "with ignore": {
- header: func() nethttp.Header {
- h := nethttp.Header{}
- h.Set("unit", "test header")
- h.Set("testing", "header unit")
- return h
- }(),
- ignore: []string{"test"},
- want: []string{"Unit"},
- },
- }
- for n, tc := range testCases {
- t.Run(n, func(t *testing.T) {
-
- tx := http.NewTransportContext(&nethttp.Request{
- Header: tc.header,
- })
-
- tx.AddIgnoreHeaderPrefix(tc.ignore...)
-
- got := tx.AttendToHeaders()
-
- // Sort to make the test work.
- sort.Strings(got)
- sort.Strings(tc.want)
-
- if diff := cmp.Diff(tc.want, got); diff != "" {
- t.Errorf("unexpected (-want, +got) = %v", diff)
- }
- })
- }
-}
-
-func TestSetHeaders(t *testing.T) {
- testCases := map[string]struct {
- initial nethttp.Header
- want nethttp.Header
- }{
- "nil": {
- want: nethttp.Header{},
- },
- "no initial": {
- want: func() nethttp.Header {
- h := nethttp.Header{}
- h.Set("unit", "test header")
- h.Set("testing", "header unit")
- return h
- }(),
- },
- "with initial": {
- initial: func() nethttp.Header {
- h := nethttp.Header{}
- h.Set("unit", "test header")
- h.Set("testing", "header unit")
- return h
- }(),
- want: func() nethttp.Header {
- h := nethttp.Header{}
- h.Set("new", "test header")
- h.Set("testing", "header unit")
- return h
- }(),
- },
- }
- for n, tc := range testCases {
- t.Run(n, func(t *testing.T) {
- ctx := context.TODO()
- ctx = http.SetContextHeaders(ctx, tc.initial)
- ctx = http.SetContextHeaders(ctx, tc.want)
- got := http.HeaderFrom(ctx)
-
- if diff := cmp.Diff(tc.want, got); diff != "" {
- t.Errorf("unexpected (-want, +got) = %v", diff)
- }
- })
- }
-}
diff --git a/v1/cloudevents/transport/http/doc.go b/v1/cloudevents/transport/http/doc.go
deleted file mode 100644
index 1a171e46e..000000000
--- a/v1/cloudevents/transport/http/doc.go
+++ /dev/null
@@ -1,4 +0,0 @@
-/*
-Package http implements the CloudEvent transport implementation using HTTP.
-*/
-package http
diff --git a/v1/cloudevents/transport/http/encoding.go b/v1/cloudevents/transport/http/encoding.go
deleted file mode 100644
index 70ea6989f..000000000
--- a/v1/cloudevents/transport/http/encoding.go
+++ /dev/null
@@ -1,205 +0,0 @@
-package http
-
-import (
- "context"
-
- "github.com/cloudevents/sdk-go/v1/cloudevents"
- cecontext "github.com/cloudevents/sdk-go/v1/cloudevents/context"
-)
-
-// Encoding to use for HTTP transport.
-type Encoding int32
-
-type EncodingSelector func(context.Context, cloudevents.Event) Encoding
-
-const (
- // Default
- Default Encoding = iota
- // BinaryV01 is Binary CloudEvents spec v0.1.
- BinaryV01
- // StructuredV01 is Structured CloudEvents spec v0.1.
- StructuredV01
- // BinaryV02 is Binary CloudEvents spec v0.2.
- BinaryV02
- // StructuredV02 is Structured CloudEvents spec v0.2.
- StructuredV02
- // BinaryV03 is Binary CloudEvents spec v0.3.
- BinaryV03
- // StructuredV03 is Structured CloudEvents spec v0.3.
- StructuredV03
- // BatchedV03 is Batched CloudEvents spec v0.3.
- BatchedV03
- // BinaryV1 is Binary CloudEvents spec v1.0.
- BinaryV1
- // StructuredV03 is Structured CloudEvents spec v1.0.
- StructuredV1
- // BatchedV1 is Batched CloudEvents spec v1.0.
- BatchedV1
-
- // Unknown is unknown.
- Unknown
-
- // Binary is used for Context Based Encoding Selections to use the
- // DefaultBinaryEncodingSelectionStrategy
- Binary = "binary"
-
- // Structured is used for Context Based Encoding Selections to use the
- // DefaultStructuredEncodingSelectionStrategy
- Structured = "structured"
-
- // Batched is used for Context Based Encoding Selections to use the
- // DefaultStructuredEncodingSelectionStrategy
- Batched = "batched"
-)
-
-func ContextBasedEncodingSelectionStrategy(ctx context.Context, e cloudevents.Event) Encoding {
- encoding := cecontext.EncodingFrom(ctx)
- switch encoding {
- case "", Binary:
- return DefaultBinaryEncodingSelectionStrategy(ctx, e)
- case Structured:
- return DefaultStructuredEncodingSelectionStrategy(ctx, e)
- }
- return Default
-}
-
-// DefaultBinaryEncodingSelectionStrategy implements a selection process for
-// which binary encoding to use based on spec version of the event.
-func DefaultBinaryEncodingSelectionStrategy(ctx context.Context, e cloudevents.Event) Encoding {
- switch e.SpecVersion() {
- case cloudevents.CloudEventsVersionV01:
- return BinaryV01
- case cloudevents.CloudEventsVersionV02:
- return BinaryV02
- case cloudevents.CloudEventsVersionV03:
- return BinaryV03
- case cloudevents.CloudEventsVersionV1:
- return BinaryV1
- }
- // Unknown version, return Default.
- return Default
-}
-
-// DefaultStructuredEncodingSelectionStrategy implements a selection process
-// for which structured encoding to use based on spec version of the event.
-func DefaultStructuredEncodingSelectionStrategy(ctx context.Context, e cloudevents.Event) Encoding {
- switch e.SpecVersion() {
- case cloudevents.CloudEventsVersionV01:
- return StructuredV01
- case cloudevents.CloudEventsVersionV02:
- return StructuredV02
- case cloudevents.CloudEventsVersionV03:
- return StructuredV03
- case cloudevents.CloudEventsVersionV1:
- return StructuredV1
- }
- // Unknown version, return Default.
- return Default
-}
-
-// String pretty-prints the encoding as a string.
-func (e Encoding) String() string {
- switch e {
- case Default:
- return "Default Encoding " + e.Version()
-
- // Binary
- case BinaryV01, BinaryV02, BinaryV03, BinaryV1:
- return "Binary Encoding " + e.Version()
-
- // Structured
- case StructuredV01, StructuredV02, StructuredV03, StructuredV1:
- return "Structured Encoding " + e.Version()
-
- // Batched
- case BatchedV03, BatchedV1:
- return "Batched Encoding " + e.Version()
-
- default:
- return "Unknown Encoding"
- }
-}
-
-// Version pretty-prints the encoding version as a string.
-func (e Encoding) Version() string {
- switch e {
- case Default:
- return "Default"
-
- // Version 0.1
- case BinaryV01, StructuredV01:
- return "v0.1"
-
- // Version 0.2
- case BinaryV02, StructuredV02:
- return "v0.2"
-
- // Version 0.3
- case BinaryV03, StructuredV03, BatchedV03:
- return "v0.3"
-
- // Version 1.0
- case BinaryV1, StructuredV1, BatchedV1:
- return "v1.0"
-
- // Unknown
- default:
- return "Unknown"
- }
-}
-
-// Codec creates a structured string to represent the the codec version.
-func (e Encoding) Codec() string {
- switch e {
- case Default:
- return "default"
-
- // Version 0.1
- case BinaryV01:
- return "binary/v0.1"
- case StructuredV01:
- return "structured/v0.1"
-
- // Version 0.2
- case BinaryV02:
- return "binary/v0.2"
- case StructuredV02:
- return "structured/v0.2"
-
- // Version 0.3
- case BinaryV03:
- return "binary/v0.3"
- case StructuredV03:
- return "structured/v0.3"
- case BatchedV03:
- return "batched/v0.3"
-
- // Version 1.0
- case BinaryV1:
- return "binary/v1.0"
- case StructuredV1:
- return "structured/v1.0"
- case BatchedV1:
- return "batched/v1.0"
-
- // Unknown
- default:
- return "unknown"
- }
-}
-
-// Name creates a string to represent the the codec name.
-func (e Encoding) Name() string {
- switch e {
- case Default:
- return Binary
- case BinaryV01, BinaryV02, BinaryV03, BinaryV1:
- return Binary
- case StructuredV01, StructuredV02, StructuredV03, StructuredV1:
- return Structured
- case BatchedV03, BatchedV1:
- return Batched
- default:
- return Binary
- }
-}
diff --git a/v1/cloudevents/transport/http/message.go b/v1/cloudevents/transport/http/message.go
deleted file mode 100644
index 5693c6eab..000000000
--- a/v1/cloudevents/transport/http/message.go
+++ /dev/null
@@ -1,148 +0,0 @@
-package http
-
-import (
- "bytes"
- "encoding/json"
-
- "io"
- "io/ioutil"
- "net/http"
-
- "github.com/cloudevents/sdk-go/v1/cloudevents/transport"
-)
-
-// type check that this transport message impl matches the contract
-var _ transport.Message = (*Message)(nil)
-
-// Message is an http transport message.
-type Message struct {
- Header http.Header
- Body []byte
-}
-
-// Response is an http transport response.
-type Response struct {
- StatusCode int
- Message
-}
-
-// CloudEventsVersion inspects a message and tries to discover and return the
-// CloudEvents spec version.
-func (m Message) CloudEventsVersion() string {
-
- // TODO: the impl of this method needs to move into the codec.
-
- if m.Header != nil {
- // Try headers first.
- // v0.1, cased from the spec
- // Note: don't pass literal string direct to m.Header[] so that
- // go vet won't complain about non-canonical case.
- name := "CE-CloudEventsVersion"
- if v := m.Header[name]; len(v) == 1 {
- return v[0]
- }
- // v0.2, canonical casing
- if ver := m.Header.Get("CE-CloudEventsVersion"); ver != "" {
- return ver
- }
-
- // v0.2, cased from the spec
- name = "ce-specversion"
- if v := m.Header[name]; len(v) == 1 {
- return v[0]
- }
- // v0.2, canonical casing
- name = "ce-specversion"
- if ver := m.Header.Get(name); ver != "" {
- return ver
- }
- }
-
- // Then try the data body.
- // TODO: we need to use the correct decoding based on content type.
-
- raw := make(map[string]json.RawMessage)
- if err := json.Unmarshal(m.Body, &raw); err != nil {
- return ""
- }
-
- // v0.1
- if v, ok := raw["cloudEventsVersion"]; ok {
- var version string
- if err := json.Unmarshal(v, &version); err != nil {
- return ""
- }
- return version
- }
-
- // v0.2
- if v, ok := raw["specversion"]; ok {
- var version string
- if err := json.Unmarshal(v, &version); err != nil {
- return ""
- }
- return version
- }
-
- return ""
-}
-
-func readAllClose(r io.ReadCloser) ([]byte, error) {
- if r != nil {
- defer r.Close()
- return ioutil.ReadAll(r)
- }
- return nil, nil
-}
-
-// NewMessage creates a new message from the Header and Body of
-// an http.Request or http.Response
-func NewMessage(header http.Header, body io.ReadCloser) (*Message, error) {
- var m Message
- err := m.Init(header, body)
- return &m, err
-}
-
-// NewResponse creates a new response from the Header and Body of
-// an http.Request or http.Response
-func NewResponse(header http.Header, body io.ReadCloser, statusCode int) (*Response, error) {
- resp := Response{StatusCode: statusCode}
- err := resp.Init(header, body)
- return &resp, err
-}
-
-// Copy copies a new Body and Header into a message, replacing any previous data.
-func (m *Message) Init(header http.Header, body io.ReadCloser) error {
- m.Header = make(http.Header, len(header))
- copyHeadersEnsure(header, &m.Header)
- var err error
- m.Body, err = readAllClose(body)
- return err
-}
-
-func (m *Message) copyOut(header *http.Header, body *io.ReadCloser) {
- copyHeadersEnsure(m.Header, header)
- *body = nil
- if m.Body != nil {
- copy := append([]byte(nil), m.Body...)
- *body = ioutil.NopCloser(bytes.NewBuffer(copy))
- }
-}
-
-// ToRequest updates a http.Request from a Message.
-// Replaces Body, ContentLength and Method, updates Headers.
-// Panic if req is nil
-func (m *Message) ToRequest(req *http.Request) {
- m.copyOut(&req.Header, &req.Body)
- req.ContentLength = int64(len(m.Body))
- req.Method = http.MethodPost
-}
-
-// ToResponse updates a http.Response from a Response.
-// Replaces Body, updates Headers.
-// Panic if resp is nil
-func (m *Response) ToResponse(resp *http.Response) {
- m.copyOut(&resp.Header, &resp.Body)
- resp.ContentLength = int64(len(m.Body))
- resp.StatusCode = m.StatusCode
-}
diff --git a/v1/cloudevents/transport/http/message_test.go b/v1/cloudevents/transport/http/message_test.go
deleted file mode 100644
index 093add598..000000000
--- a/v1/cloudevents/transport/http/message_test.go
+++ /dev/null
@@ -1,99 +0,0 @@
-package http_test
-
-import (
- "bytes"
- "io/ioutil"
- "net/http"
- "testing"
-
- cehttp "github.com/cloudevents/sdk-go/v1/cloudevents/transport/http"
- "github.com/google/go-cmp/cmp"
-)
-
-func TestNewMessage(t *testing.T) {
- h := http.Header{"A": []string{"b"}, "X": []string{"y"}}
- b := ioutil.NopCloser(bytes.NewBuffer([]byte("hello")))
- m, err := cehttp.NewMessage(h, b)
- if err != nil {
- t.Error(err)
- }
- if s := cmp.Diff(h, m.Header); s != "" {
- t.Error(s)
- }
- if s := cmp.Diff("hello", string(m.Body)); s != "" {
- t.Error(s)
- }
- // Make sure the Message map is an independent copy
- h.Set("a", "A")
- if s := cmp.Diff(m.Header.Get("a"), "b"); s != "" {
- t.Error(s)
- }
-}
-
-func TestNewResponse(t *testing.T) {
- h := http.Header{"A": []string{"b"}, "X": []string{"y"}}
- b := ioutil.NopCloser(bytes.NewBuffer([]byte("hello")))
- m, err := cehttp.NewResponse(h, b, 42)
- if err != nil {
- t.Error(err)
- }
- if s := cmp.Diff(42, m.StatusCode); s != "" {
- if s := cmp.Diff(h, m.Header); s != "" {
- t.Error(s)
- }
- if s := cmp.Diff("hello", string(m.Body)); s != "" {
- t.Error(s)
- }
- }
-}
-
-func TestToRequest(t *testing.T) {
- h := http.Header{"a": []string{"b"}, "x": []string{"y"}}
- b := ioutil.NopCloser(bytes.NewBuffer([]byte("hello")))
- m, err := cehttp.NewMessage(h, b)
- if err != nil {
- t.Error(err)
- }
- var req http.Request
- m.ToRequest(&req)
- if s := cmp.Diff(m.Header, req.Header); s != "" {
- t.Error(s)
- }
- data, err := ioutil.ReadAll(req.Body)
- if err != nil {
- t.Error(err)
- }
- if s := cmp.Diff("hello", string(data)); s != "" {
- t.Error(s)
- }
- if s := cmp.Diff(len(data), int(req.ContentLength)); s != "" {
- t.Error(s)
- }
- if s := cmp.Diff("POST", req.Method); s != "" {
- t.Error(s)
- }
-}
-
-func TestToResponse(t *testing.T) {
- h := http.Header{"a": []string{"b"}, "x": []string{"y"}}
- b := ioutil.NopCloser(bytes.NewBuffer([]byte("hello")))
- m, err := cehttp.NewResponse(h, b, 42)
- if err != nil {
- t.Error(err)
- }
- var resp http.Response
- m.ToResponse(&resp)
- if s := cmp.Diff(m.Header, resp.Header); s != "" {
- t.Error(s)
- }
- data, err := ioutil.ReadAll(resp.Body)
- if err != nil {
- t.Error(err)
- }
- if s := cmp.Diff("hello", string(data)); s != "" {
- t.Error(s)
- }
- if s := cmp.Diff(len(data), int(resp.ContentLength)); s != "" {
- t.Error(s)
- }
-}
diff --git a/v1/cloudevents/transport/http/observability.go b/v1/cloudevents/transport/http/observability.go
deleted file mode 100644
index d60a2ab61..000000000
--- a/v1/cloudevents/transport/http/observability.go
+++ /dev/null
@@ -1,86 +0,0 @@
-package http
-
-import (
- "fmt"
-
- "github.com/cloudevents/sdk-go/v1/cloudevents/observability"
- "go.opencensus.io/stats"
- "go.opencensus.io/stats/view"
-)
-
-var (
- // LatencyMs measures the latency in milliseconds for the http transport
- // methods for CloudEvents.
- LatencyMs = stats.Float64(
- "cloudevents.io/sdk-go/transport/http/latency",
- "The latency in milliseconds for the http transport methods for CloudEvents.",
- "ms")
-)
-
-var (
- // LatencyView is an OpenCensus view that shows http transport method latency.
- LatencyView = &view.View{
- Name: "transport/http/latency",
- Measure: LatencyMs,
- Description: "The distribution of latency inside of http transport for CloudEvents.",
- Aggregation: view.Distribution(0, .01, .1, 1, 10, 100, 1000, 10000),
- TagKeys: observability.LatencyTags(),
- }
-)
-
-type observed int32
-
-// Adheres to Observable
-var _ observability.Observable = observed(0)
-
-const (
- reportSend observed = iota
- reportReceive
- reportServeHTTP
- reportEncode
- reportDecode
-)
-
-// MethodName implements Observable.MethodName
-func (o observed) MethodName() string {
- switch o {
- case reportSend:
- return "send"
- case reportReceive:
- return "receive"
- case reportServeHTTP:
- return "servehttp"
- case reportEncode:
- return "encode"
- case reportDecode:
- return "decode"
- default:
- return "unknown"
- }
-}
-
-// LatencyMs implements Observable.LatencyMs
-func (o observed) LatencyMs() *stats.Float64Measure {
- return LatencyMs
-}
-
-// CodecObserved is a wrapper to append version to observed.
-type CodecObserved struct {
- // Method
- o observed
- // Codec
- c string
-}
-
-// Adheres to Observable
-var _ observability.Observable = (*CodecObserved)(nil)
-
-// MethodName implements Observable.MethodName
-func (c CodecObserved) MethodName() string {
- return fmt.Sprintf("%s/%s", c.o.MethodName(), c.c)
-}
-
-// LatencyMs implements Observable.LatencyMs
-func (c CodecObserved) LatencyMs() *stats.Float64Measure {
- return c.o.LatencyMs()
-}
diff --git a/v1/cloudevents/transport/http/options.go b/v1/cloudevents/transport/http/options.go
deleted file mode 100644
index fde7598b0..000000000
--- a/v1/cloudevents/transport/http/options.go
+++ /dev/null
@@ -1,274 +0,0 @@
-package http
-
-import (
- "fmt"
- "net"
- nethttp "net/http"
- "net/url"
- "strings"
- "time"
-)
-
-// Option is the function signature required to be considered an http.Option.
-type Option func(*Transport) error
-
-// WithTarget sets the outbound recipient of cloudevents when using an HTTP
-// request.
-func WithTarget(targetUrl string) Option {
- return func(t *Transport) error {
- if t == nil {
- return fmt.Errorf("http target option can not set nil transport")
- }
- targetUrl = strings.TrimSpace(targetUrl)
- if targetUrl != "" {
- var err error
- var target *url.URL
- target, err = url.Parse(targetUrl)
- if err != nil {
- return fmt.Errorf("http target option failed to parse target url: %s", err.Error())
- }
-
- if t.Req == nil {
- t.Req = &nethttp.Request{
- Method: nethttp.MethodPost,
- }
- }
- t.Req.URL = target
- return nil
- }
- return fmt.Errorf("http target option was empty string")
- }
-}
-
-// WithMethod sets the HTTP verb (GET, POST, PUT, etc.) to use
-// when using an HTTP request.
-func WithMethod(method string) Option {
- return func(t *Transport) error {
- if t == nil {
- return fmt.Errorf("http method option can not set nil transport")
- }
- method = strings.TrimSpace(method)
- if method != "" {
- if t.Req == nil {
- t.Req = &nethttp.Request{}
- }
- t.Req.Method = method
- return nil
- }
- return fmt.Errorf("http method option was empty string")
- }
-}
-
-// WithHeader sets an additional default outbound header for all cloudevents
-// when using an HTTP request.
-func WithHeader(key, value string) Option {
- return func(t *Transport) error {
- if t == nil {
- return fmt.Errorf("http header option can not set nil transport")
- }
- key = strings.TrimSpace(key)
- if key != "" {
- if t.Req == nil {
- t.Req = &nethttp.Request{}
- }
- if t.Req.Header == nil {
- t.Req.Header = nethttp.Header{}
- }
- t.Req.Header.Add(key, value)
- return nil
- }
- return fmt.Errorf("http header option was empty string")
- }
-}
-
-// WithShutdownTimeout sets the shutdown timeout when the http server is being shutdown.
-func WithShutdownTimeout(timeout time.Duration) Option {
- return func(t *Transport) error {
- if t == nil {
- return fmt.Errorf("http shutdown timeout option can not set nil transport")
- }
- t.ShutdownTimeout = &timeout
- return nil
- }
-}
-
-// WithEncoding sets the encoding for clients with HTTP transports.
-func WithEncoding(encoding Encoding) Option {
- return func(t *Transport) error {
- if t == nil {
- return fmt.Errorf("http encoding option can not set nil transport")
- }
- t.Encoding = encoding
- return nil
- }
-}
-
-// WithDefaultEncodingSelector sets the encoding selection strategy for
-// default encoding selections based on Event.
-func WithDefaultEncodingSelector(fn EncodingSelector) Option {
- return func(t *Transport) error {
- if t == nil {
- return fmt.Errorf("http default encoding selector option can not set nil transport")
- }
- if fn != nil {
- t.DefaultEncodingSelectionFn = fn
- return nil
- }
- return fmt.Errorf("http fn for DefaultEncodingSelector was nil")
- }
-}
-
-// WithContextBasedEncoding sets the encoding selection strategy for
-// default encoding selections based context and then on Event, the encoded
-// event will be the given version in the encoding specified by the given
-// context, or Binary if not set.
-func WithContextBasedEncoding() Option {
- return func(t *Transport) error {
- if t == nil {
- return fmt.Errorf("http context based encoding option can not set nil transport")
- }
-
- t.DefaultEncodingSelectionFn = ContextBasedEncodingSelectionStrategy
- return nil
- }
-}
-
-// WithBinaryEncoding sets the encoding selection strategy for
-// default encoding selections based on Event, the encoded event will be the
-// given version in Binary form.
-func WithBinaryEncoding() Option {
- return func(t *Transport) error {
- if t == nil {
- return fmt.Errorf("http binary encoding option can not set nil transport")
- }
-
- t.DefaultEncodingSelectionFn = DefaultBinaryEncodingSelectionStrategy
- return nil
- }
-}
-
-// WithStructuredEncoding sets the encoding selection strategy for
-// default encoding selections based on Event, the encoded event will be the
-// given version in Structured form.
-func WithStructuredEncoding() Option {
- return func(t *Transport) error {
- if t == nil {
- return fmt.Errorf("http structured encoding option can not set nil transport")
- }
-
- t.DefaultEncodingSelectionFn = DefaultStructuredEncodingSelectionStrategy
- return nil
- }
-}
-
-func checkListen(t *Transport, prefix string) error {
- switch {
- case t.Port != nil:
- return fmt.Errorf("%v port already set", prefix)
- case t.listener != nil:
- return fmt.Errorf("%v listener already set", prefix)
- }
- return nil
-}
-
-// WithPort sets the listening port for StartReceiver.
-// Only one of WithListener or WithPort is allowed.
-func WithPort(port int) Option {
- return func(t *Transport) error {
- if t == nil {
- return fmt.Errorf("http port option can not set nil transport")
- }
- if port < 0 || port > 65535 {
- return fmt.Errorf("http port option was given an invalid port: %d", port)
- }
- if err := checkListen(t, "http port option"); err != nil {
- return err
- }
- t.setPort(port)
- return nil
- }
-}
-
-// WithListener sets the listener for StartReceiver.
-// Only one of WithListener or WithPort is allowed.
-func WithListener(l net.Listener) Option {
- return func(t *Transport) error {
- if t == nil {
- return fmt.Errorf("http listener option can not set nil transport")
- }
- if err := checkListen(t, "http port option"); err != nil {
- return err
- }
- t.listener = l
- _, err := t.listen()
- return err
- }
-}
-
-// WithPath sets the path to receive cloudevents on for HTTP transports.
-func WithPath(path string) Option {
- return func(t *Transport) error {
- if t == nil {
- return fmt.Errorf("http path option can not set nil transport")
- }
- path = strings.TrimSpace(path)
- if len(path) == 0 {
- return fmt.Errorf("http path option was given an invalid path: %q", path)
- }
- t.Path = path
- return nil
- }
-}
-
-// Middleware is a function that takes an existing http.Handler and wraps it in middleware,
-// returning the wrapped http.Handler.
-type Middleware func(next nethttp.Handler) nethttp.Handler
-
-// WithMiddleware adds an HTTP middleware to the transport. It may be specified multiple times.
-// Middleware is applied to everything before it. For example
-// `NewClient(WithMiddleware(foo), WithMiddleware(bar))` would result in `bar(foo(original))`.
-func WithMiddleware(middleware Middleware) Option {
- return func(t *Transport) error {
- if t == nil {
- return fmt.Errorf("http middleware option can not set nil transport")
- }
- t.middleware = append(t.middleware, middleware)
- return nil
- }
-}
-
-// WithLongPollTarget sets the receivers URL to perform long polling after
-// StartReceiver is called.
-func WithLongPollTarget(targetUrl string) Option {
- return func(t *Transport) error {
- if t == nil {
- return fmt.Errorf("http long poll target option can not set nil transport")
- }
- targetUrl = strings.TrimSpace(targetUrl)
- if targetUrl != "" {
- var err error
- var target *url.URL
- target, err = url.Parse(targetUrl)
- if err != nil {
- return fmt.Errorf("http long poll target option failed to parse target url: %s", err.Error())
- }
-
- if t.LongPollReq == nil {
- t.LongPollReq = &nethttp.Request{
- Method: nethttp.MethodGet,
- }
- }
- t.LongPollReq.URL = target
- return nil
- }
- return fmt.Errorf("http long poll target option was empty string")
- }
-}
-
-// WithHTTPTransport sets the HTTP client transport.
-func WithHTTPTransport(httpTransport nethttp.RoundTripper) Option {
- return func(t *Transport) error {
- t.transport = httpTransport
- return nil
- }
-}
diff --git a/v1/cloudevents/transport/http/options_test.go b/v1/cloudevents/transport/http/options_test.go
deleted file mode 100644
index d917e52b3..000000000
--- a/v1/cloudevents/transport/http/options_test.go
+++ /dev/null
@@ -1,803 +0,0 @@
-package http
-
-import (
- "context"
- "fmt"
- "net"
- "net/http"
- "net/url"
- "testing"
- "time"
-
- "github.com/cloudevents/sdk-go/v1/cloudevents"
- "github.com/google/go-cmp/cmp"
- "github.com/google/go-cmp/cmp/cmpopts"
-)
-
-func TestWithTarget(t *testing.T) {
- t.Skip("Fails on Golang 1.14")
- testCases := map[string]struct {
- t *Transport
- target string
- want *Transport
- wantErr string
- }{
- "valid url": {
- t: &Transport{
- Req: &http.Request{},
- },
- target: "http://localhost:8080/",
- want: &Transport{
- Req: &http.Request{
- URL: func() *url.URL {
- u, _ := url.Parse("http://localhost:8080/")
- return u
- }(),
- },
- },
- },
- "valid url, unset req": {
- t: &Transport{},
- target: "http://localhost:8080/",
- want: &Transport{
- Req: &http.Request{
- Method: http.MethodPost,
- URL: func() *url.URL {
- u, _ := url.Parse("http://localhost:8080/")
- return u
- }(),
- },
- },
- },
- "invalid url": {
- t: &Transport{
- Req: &http.Request{},
- },
- target: "%",
- wantErr: `http target option failed to parse target url: parse %: invalid URL escape "%"`,
- },
- "empty target": {
- t: &Transport{
- Req: &http.Request{},
- },
- target: "",
- wantErr: `http target option was empty string`,
- },
- "whitespace target": {
- t: &Transport{
- Req: &http.Request{},
- },
- target: " \t\n",
- wantErr: `http target option was empty string`,
- },
- "nil transport": {
- wantErr: `http target option can not set nil transport`,
- },
- }
- for n, tc := range testCases {
- t.Run(n, func(t *testing.T) {
-
- err := tc.t.applyOptions(WithTarget(tc.target))
-
- if tc.wantErr != "" || err != nil {
- var gotErr string
- if err != nil {
- gotErr = err.Error()
- }
- if diff := cmp.Diff(tc.wantErr, gotErr); diff != "" {
- t.Errorf("unexpected error (-want, +got) = %v", diff)
- }
- return
- }
-
- got := tc.t
-
- if diff := cmp.Diff(tc.want, got,
- cmpopts.IgnoreUnexported(Transport{}), cmpopts.IgnoreUnexported(http.Request{})); diff != "" {
- t.Errorf("unexpected (-want, +got) = %v", diff)
- }
- })
- }
-}
-
-func TestWithMethod(t *testing.T) {
- testCases := map[string]struct {
- t *Transport
- method string
- want *Transport
- wantErr string
- }{
- "valid method": {
- t: &Transport{
- Req: &http.Request{},
- },
- method: "GET",
- want: &Transport{
- Req: &http.Request{
- Method: http.MethodGet,
- },
- },
- },
- "valid method, unset req": {
- t: &Transport{},
- method: "PUT",
- want: &Transport{
- Req: &http.Request{
- Method: http.MethodPut,
- },
- },
- },
- "empty method": {
- t: &Transport{
- Req: &http.Request{},
- },
- method: "",
- wantErr: `http method option was empty string`,
- },
- "whitespace method": {
- t: &Transport{
- Req: &http.Request{},
- },
- method: " \t\n",
- wantErr: `http method option was empty string`,
- },
- "nil transport": {
- wantErr: `http method option can not set nil transport`,
- },
- }
- for n, tc := range testCases {
- t.Run(n, func(t *testing.T) {
-
- err := tc.t.applyOptions(WithMethod(tc.method))
-
- if tc.wantErr != "" || err != nil {
- var gotErr string
- if err != nil {
- gotErr = err.Error()
- }
- if diff := cmp.Diff(tc.wantErr, gotErr); diff != "" {
- t.Errorf("unexpected error (-want, +got) = %v", diff)
- }
- return
- }
-
- got := tc.t
-
- if diff := cmp.Diff(tc.want, got,
- cmpopts.IgnoreUnexported(Transport{}), cmpopts.IgnoreUnexported(http.Request{})); diff != "" {
- t.Errorf("unexpected (-want, +got) = %v", diff)
- }
- })
- }
-}
-
-func TestWithHeader(t *testing.T) {
- testCases := map[string]struct {
- t *Transport
- key string
- value string
- want *Transport
- wantErr string
- }{
- "valid header": {
- t: &Transport{
- Req: &http.Request{},
- },
- key: "unit",
- value: "test",
- want: &Transport{
- Req: &http.Request{
- Header: http.Header{
- "Unit": {
- "test",
- },
- },
- },
- },
- },
- "valid header, unset req": {
- t: &Transport{},
- key: "unit",
- value: "test",
- want: &Transport{
- Req: &http.Request{
- Header: http.Header{
- "Unit": {
- "test",
- },
- },
- },
- },
- },
- "empty header key": {
- t: &Transport{
- Req: &http.Request{},
- },
- value: "test",
- wantErr: `http header option was empty string`,
- },
- "whitespace key": {
- t: &Transport{
- Req: &http.Request{},
- },
- key: " \t\n",
- value: "test",
- wantErr: `http header option was empty string`,
- },
- "nil transport": {
- wantErr: `http header option can not set nil transport`,
- },
- }
- for n, tc := range testCases {
- t.Run(n, func(t *testing.T) {
-
- err := tc.t.applyOptions(WithHeader(tc.key, tc.value))
-
- if tc.wantErr != "" || err != nil {
- var gotErr string
- if err != nil {
- gotErr = err.Error()
- }
- if diff := cmp.Diff(tc.wantErr, gotErr); diff != "" {
- t.Errorf("unexpected error (-want, +got) = %v", diff)
- }
- return
- }
-
- got := tc.t
-
- if diff := cmp.Diff(tc.want, got,
- cmpopts.IgnoreUnexported(Transport{}), cmpopts.IgnoreUnexported(http.Request{})); diff != "" {
- t.Errorf("unexpected (-want, +got) = %v", diff)
- }
- })
- }
-}
-
-func TestWithShutdownTimeout(t *testing.T) {
- testCases := map[string]struct {
- t *Transport
- timeout time.Duration
- want *Transport
- wantErr string
- }{
- "valid timeout": {
- t: &Transport{},
- timeout: time.Minute * 4,
- want: &Transport{
- ShutdownTimeout: durationptr(time.Minute * 4),
- },
- },
- "nil transport": {
- wantErr: `http shutdown timeout option can not set nil transport`,
- },
- }
- for n, tc := range testCases {
- t.Run(n, func(t *testing.T) {
-
- err := tc.t.applyOptions(WithShutdownTimeout(tc.timeout))
-
- if tc.wantErr != "" || err != nil {
- var gotErr string
- if err != nil {
- gotErr = err.Error()
- }
- if diff := cmp.Diff(tc.wantErr, gotErr); diff != "" {
- t.Errorf("unexpected error (-want, +got) = %v", diff)
- }
- return
- }
-
- got := tc.t
-
- if diff := cmp.Diff(tc.want, got,
- cmpopts.IgnoreUnexported(Transport{})); diff != "" {
- t.Errorf("unexpected (-want, +got) = %v", diff)
- }
- })
- }
-}
-
-func durationptr(duration time.Duration) *time.Duration {
- return &duration
-}
-
-func intptr(i int) *int {
- return &i
-}
-
-func TestWithPort(t *testing.T) {
- testCases := map[string]struct {
- t *Transport
- port int
- want *Transport
- wantErr string
- }{
- "valid port": {
- t: &Transport{},
- port: 8181,
- want: &Transport{
- Port: intptr(8181),
- },
- },
- "invalid port, low": {
- t: &Transport{},
- port: -1,
- wantErr: `http port option was given an invalid port: -1`,
- },
- "invalid port, high": {
- t: &Transport{},
- port: 65536,
- wantErr: `http port option was given an invalid port: 65536`,
- },
- "nil transport": {
- wantErr: `http port option can not set nil transport`,
- },
- }
- for n, tc := range testCases {
- t.Run(n, func(t *testing.T) {
-
- err := tc.t.applyOptions(WithPort(tc.port))
-
- if tc.wantErr != "" || err != nil {
- var gotErr string
- if err != nil {
- gotErr = err.Error()
- }
- if diff := cmp.Diff(tc.wantErr, gotErr); diff != "" {
- t.Errorf("unexpected error (-want, +got) = %v", diff)
- }
- return
- }
-
- got := tc.t
-
- if diff := cmp.Diff(tc.want, got,
- cmpopts.IgnoreUnexported(Transport{}), cmpopts.IgnoreUnexported(http.Request{})); diff != "" {
- t.Errorf("unexpected (-want, +got) = %v", diff)
- }
- })
- }
-}
-
-// Force a transport to close its server/listener by cancelling StartReceiver
-func forceClose(tr *Transport) {
- ctx, cancel := context.WithCancel(context.Background())
- go func() { _ = tr.StartReceiver(ctx) }()
- cancel()
-}
-
-func TestWithPort0(t *testing.T) {
- testCases := map[string]func() (*Transport, error){
- "WithPort0": func() (*Transport, error) { return New(WithPort(0)) },
- "SetPort0": func() (*Transport, error) { return &Transport{Port: new(int)}, nil },
- }
- for name, f := range testCases {
- t.Run(name, func(t *testing.T) {
- tr, err := f()
- if err != nil {
- t.Fatal(err)
- }
- defer func() { forceClose(tr) }()
- port := tr.GetPort()
- if port <= 0 {
- t.Error("no dynamic port")
- }
- if d := cmp.Diff(port, *tr.Port); d != "" {
- t.Error(d)
- }
- })
- }
-}
-
-func TestWithListener(t *testing.T) {
- l, err := net.Listen("tcp", ":0")
- if err != nil {
- t.Fatal(err)
- }
- tr, err := New(WithListener(l))
- defer func() { forceClose(tr) }()
- if err != nil {
- t.Fatal(err)
- }
- port := tr.GetPort()
- if port <= 0 {
- t.Error("no dynamic port")
- }
- if d := cmp.Diff(port, l.Addr().(*net.TCPAddr).Port); d != "" {
- t.Error(d)
- }
-}
-
-func TestWithPath(t *testing.T) {
- testCases := map[string]struct {
- t *Transport
- path string
- want *Transport
- wantErr string
- }{
- "valid path": {
- t: &Transport{},
- path: "/test",
- want: &Transport{
- Path: "/test",
- },
- },
- "invalid path": {
- t: &Transport{},
- path: "",
- wantErr: `http path option was given an invalid path: ""`,
- },
- "nil transport": {
- wantErr: `http path option can not set nil transport`,
- },
- }
- for n, tc := range testCases {
- t.Run(n, func(t *testing.T) {
-
- err := tc.t.applyOptions(WithPath(tc.path))
-
- if tc.wantErr != "" || err != nil {
- var gotErr string
- if err != nil {
- gotErr = err.Error()
- }
- if diff := cmp.Diff(tc.wantErr, gotErr); diff != "" {
- t.Errorf("unexpected error (-want, +got) = %v", diff)
- }
- return
- }
-
- got := tc.t
-
- if diff := cmp.Diff(tc.want, got,
- cmpopts.IgnoreUnexported(Transport{}), cmpopts.IgnoreUnexported(http.Request{})); diff != "" {
- t.Errorf("unexpected (-want, +got) = %v", diff)
- }
- })
- }
-}
-
-func TestWithEncoding(t *testing.T) {
- testCases := map[string]struct {
- t *Transport
- encoding Encoding
- want *Transport
- wantErr string
- }{
- "valid encoding": {
- t: &Transport{},
- encoding: StructuredV03,
- want: &Transport{
- Encoding: StructuredV03,
- },
- },
- "nil transport": {
- wantErr: `http encoding option can not set nil transport`,
- },
- }
- for n, tc := range testCases {
- t.Run(n, func(t *testing.T) {
-
- err := tc.t.applyOptions(WithEncoding(tc.encoding))
-
- if tc.wantErr != "" || err != nil {
- var gotErr string
- if err != nil {
- gotErr = err.Error()
- }
- if diff := cmp.Diff(tc.wantErr, gotErr); diff != "" {
- t.Errorf("unexpected error (-want, +got) = %v", diff)
- }
- return
- }
-
- got := tc.t
-
- if diff := cmp.Diff(tc.want, got,
- cmpopts.IgnoreUnexported(Transport{})); diff != "" {
- t.Errorf("unexpected (-want, +got) = %v", diff)
- }
- })
- }
-}
-
-func TestWithDefaultEncodingSelector(t *testing.T) {
-
- fn := func(ctx context.Context, e cloudevents.Event) Encoding {
- return Default
- }
-
- testCases := map[string]struct {
- t *Transport
- fn EncodingSelector
- want *Transport
- wantErr string
- }{
- "valid fn": {
- t: &Transport{},
- fn: fn,
- want: &Transport{
- DefaultEncodingSelectionFn: fn,
- },
- },
- "invalid fn": {
- t: &Transport{},
- fn: nil,
- wantErr: "http fn for DefaultEncodingSelector was nil",
- },
- "nil transport": {
- wantErr: `http default encoding selector option can not set nil transport`,
- },
- }
- for n, tc := range testCases {
- t.Run(n, func(t *testing.T) {
-
- err := tc.t.applyOptions(WithDefaultEncodingSelector(tc.fn))
-
- if tc.wantErr != "" || err != nil {
- var gotErr string
- if err != nil {
- gotErr = err.Error()
- }
- if diff := cmp.Diff(tc.wantErr, gotErr); diff != "" {
- t.Errorf("unexpected error (-want, +got) = %v", diff)
- }
- return
- }
-
- got := tc.t
-
- if diff := cmp.Diff(tc.want, got,
- cmpopts.IgnoreUnexported(Transport{}),
- cmpopts.IgnoreFields(Transport{}, "DefaultEncodingSelectionFn")); diff != "" {
- t.Errorf("unexpected (-want, +got) = %v", diff)
- }
- if tc.fn == nil {
- if got.DefaultEncodingSelectionFn != nil {
- t.Errorf("expected nil DefaultEncodingSelectionFn")
- }
- } else {
- want := fmt.Sprintf("%v", tc.fn)
- got := fmt.Sprintf("%v", got.DefaultEncodingSelectionFn)
- if got != want {
- t.Errorf("unexpected DefaultEncodingSelectionFn; want: %v; got: %v", want, got)
- }
-
- }
- })
- }
-}
-
-func TestWithBinaryEncoding(t *testing.T) {
-
- fn := DefaultBinaryEncodingSelectionStrategy
-
- testCases := map[string]struct {
- t *Transport
- fn EncodingSelector
- want *Transport
- wantErr string
- }{
- "valid": {
- t: &Transport{},
- fn: fn,
- want: &Transport{
- DefaultEncodingSelectionFn: fn,
- },
- },
- "nil transport": {
- wantErr: `http binary encoding option can not set nil transport`,
- },
- }
- for n, tc := range testCases {
- t.Run(n, func(t *testing.T) {
-
- err := tc.t.applyOptions(WithBinaryEncoding())
-
- if tc.wantErr != "" || err != nil {
- var gotErr string
- if err != nil {
- gotErr = err.Error()
- }
- if diff := cmp.Diff(tc.wantErr, gotErr); diff != "" {
- t.Errorf("unexpected error (-want, +got) = %v", diff)
- }
- return
- }
-
- got := tc.t
-
- if diff := cmp.Diff(tc.want, got,
- cmpopts.IgnoreUnexported(Transport{}),
- cmpopts.IgnoreFields(Transport{}, "DefaultEncodingSelectionFn")); diff != "" {
- t.Errorf("unexpected (-want, +got) = %v", diff)
- }
-
- if tc.fn == nil {
- if got.DefaultEncodingSelectionFn != nil {
- t.Errorf("expected nil DefaultEncodingSelectionFn")
- }
- } else {
- want := fmt.Sprintf("%v", tc.fn)
- got := fmt.Sprintf("%v", got.DefaultEncodingSelectionFn)
- if got != want {
- t.Errorf("unexpected DefaultEncodingSelectionFn; want: %v; got: %v", want, got)
- }
- }
- })
- }
-}
-
-func TestWithStructuredEncoding(t *testing.T) {
-
- fn := DefaultStructuredEncodingSelectionStrategy
-
- testCases := map[string]struct {
- t *Transport
- fn EncodingSelector
- want *Transport
- wantErr string
- }{
- "valid": {
- t: &Transport{},
- fn: fn,
- want: &Transport{
- DefaultEncodingSelectionFn: fn,
- },
- },
- "nil transport": {
- wantErr: `http structured encoding option can not set nil transport`,
- },
- }
- for n, tc := range testCases {
- t.Run(n, func(t *testing.T) {
-
- err := tc.t.applyOptions(WithStructuredEncoding())
-
- if tc.wantErr != "" || err != nil {
- var gotErr string
- if err != nil {
- gotErr = err.Error()
- }
- if diff := cmp.Diff(tc.wantErr, gotErr); diff != "" {
- t.Errorf("unexpected error (-want, +got) = %v", diff)
- }
- return
- }
-
- got := tc.t
-
- if diff := cmp.Diff(tc.want, got,
- cmpopts.IgnoreUnexported(Transport{}),
- cmpopts.IgnoreFields(Transport{}, "DefaultEncodingSelectionFn")); diff != "" {
- t.Errorf("unexpected (-want, +got) = %v", diff)
- }
- if tc.fn == nil {
- if got.DefaultEncodingSelectionFn != nil {
- t.Errorf("expected nil DefaultEncodingSelectionFn")
- }
- } else {
- want := fmt.Sprintf("%v", tc.fn)
- got := fmt.Sprintf("%v", got.DefaultEncodingSelectionFn)
- if got != want {
- t.Errorf("unexpected DefaultEncodingSelectionFn; want: %v; got: %v", want, got)
- }
- }
- })
- }
-}
-
-func TestWithMiddleware(t *testing.T) {
- testCases := map[string]struct {
- t *Transport
- wantErr string
- }{
- "nil transport": {
- wantErr: "http middleware option can not set nil transport",
- },
- "non-nil transport": {
- t: &Transport{},
- },
- }
- for n, tc := range testCases {
- t.Run(n, func(t *testing.T) {
- err := tc.t.applyOptions(WithMiddleware(func(next http.Handler) http.Handler {
- return next
- }))
- if tc.wantErr != "" {
- if err == nil || err.Error() != tc.wantErr {
- t.Fatalf("Expected error '%s'. Actual '%v'", tc.wantErr, err)
- }
- } else if err != nil {
- t.Fatalf("Unexpected error: %v", err)
- }
- })
- }
-}
-
-func TestWithLongPollTarget(t *testing.T) {
- t.Skip("Fails on Golang 1.14")
- testCases := map[string]struct {
- t *Transport
- target string
- want *Transport
- wantErr string
- }{
- "valid url": {
- t: &Transport{
- LongPollReq: &http.Request{},
- },
- target: "http://localhost:8080/",
- want: &Transport{
- LongPollReq: &http.Request{
- URL: func() *url.URL {
- u, _ := url.Parse("http://localhost:8080/")
- return u
- }(),
- },
- },
- },
- "valid url, unset req": {
- t: &Transport{},
- target: "http://localhost:8080/",
- want: &Transport{
- LongPollReq: &http.Request{
- Method: http.MethodGet,
- URL: func() *url.URL {
- u, _ := url.Parse("http://localhost:8080/")
- return u
- }(),
- },
- },
- },
- "invalid url": {
- t: &Transport{
- LongPollReq: &http.Request{},
- },
- target: "%",
- wantErr: `http long poll target option failed to parse target url: parse %: invalid URL escape "%"`,
- },
- "empty target": {
- t: &Transport{
- LongPollReq: &http.Request{},
- },
- target: "",
- wantErr: `http long poll target option was empty string`,
- },
- "whitespace target": {
- t: &Transport{
- LongPollReq: &http.Request{},
- },
- target: " \t\n",
- wantErr: `http long poll target option was empty string`,
- },
- "nil transport": {
- wantErr: `http long poll target option can not set nil transport`,
- },
- }
- for n, tc := range testCases {
- t.Run(n, func(t *testing.T) {
-
- err := tc.t.applyOptions(WithLongPollTarget(tc.target))
-
- if tc.wantErr != "" || err != nil {
- var gotErr string
- if err != nil {
- gotErr = err.Error()
- }
- if diff := cmp.Diff(tc.wantErr, gotErr); diff != "" {
- t.Errorf("unexpected error (-want, +got) = %v", diff)
- }
- return
- }
-
- got := tc.t
-
- if diff := cmp.Diff(tc.want, got,
- cmpopts.IgnoreUnexported(Transport{}), cmpopts.IgnoreUnexported(http.Request{})); diff != "" {
- t.Errorf("unexpected (-want, +got) = %v", diff)
- }
- })
- }
-}
diff --git a/v1/cloudevents/transport/http/transport.go b/v1/cloudevents/transport/http/transport.go
deleted file mode 100644
index 87c0d25c5..000000000
--- a/v1/cloudevents/transport/http/transport.go
+++ /dev/null
@@ -1,711 +0,0 @@
-package http
-
-import (
- "context"
- "errors"
- "fmt"
- "io/ioutil"
- "net"
- "net/http"
- "net/url"
- "strconv"
- "strings"
- "sync"
- "time"
-
- "go.opencensus.io/plugin/ochttp"
- "go.opencensus.io/plugin/ochttp/propagation/tracecontext"
- "go.uber.org/zap"
-
- "github.com/cloudevents/sdk-go/v1/cloudevents"
- cecontext "github.com/cloudevents/sdk-go/v1/cloudevents/context"
- "github.com/cloudevents/sdk-go/v1/cloudevents/observability"
- "github.com/cloudevents/sdk-go/v1/cloudevents/transport"
-)
-
-// Transport adheres to transport.Transport.
-var _ transport.Transport = (*Transport)(nil)
-
-const (
- // DefaultShutdownTimeout defines the default timeout given to the http.Server when calling Shutdown.
- DefaultShutdownTimeout = time.Minute * 1
-
- // TransportName is the name of this transport.
- TransportName = "HTTP"
-)
-
-// Transport acts as both a http client and a http handler.
-type Transport struct {
- // The encoding used to select the codec for outbound events.
- Encoding Encoding
-
- // DefaultEncodingSelectionFn allows for other encoding selection strategies to be injected.
- DefaultEncodingSelectionFn EncodingSelector
-
- // ShutdownTimeout defines the timeout given to the http.Server when calling Shutdown.
- // If nil, DefaultShutdownTimeout is used.
- ShutdownTimeout *time.Duration
-
- // Sending
-
- // Deprecated - setting http client will override use of the
- // HTTP transport set with WithHTTPTransport.
- Client *http.Client
-
- // Req is the base http request that is used for http.Do.
- // Only .Method, .URL, .Close, and .Header is considered.
- // If not set, Req.Method defaults to POST.
- // Req.URL or context.WithTarget(url) are required for sending.
- Req *http.Request
-
- // Receiving
-
- // Receiver is invoked target for incoming events.
- Receiver transport.Receiver
- // Converter is invoked if the incoming transport receives an undecodable
- // message.
- Converter transport.Converter
- // Port is the port to bind the receiver to. Defaults to 8080.
- Port *int
- // Path is the path to bind the receiver to. Defaults to "/".
- Path string
- // Handler is the handler the http Server will use. Use this to reuse the
- // http server. If nil, the Transport will create a one.
- Handler *http.ServeMux
-
- // LongPollClient is the http client that will be used to long poll.
- // If nil and LongPollReq is set, the Transport will create a one.
- LongPollClient *http.Client
- // LongPollReq is the base http request that is used for long poll.
- // Only .Method, .URL, .Close, and .Header is considered.
- // If not set, LongPollReq.Method defaults to GET.
- // LongPollReq.URL or context.WithLongPollTarget(url) are required to long
- // poll on StartReceiver.
- LongPollReq *http.Request
-
- listener net.Listener
- server *http.Server
- handlerRegistered bool
- codec transport.Codec
- // Create Mutex
- crMu sync.Mutex
- // Receive Mutex
- reMu sync.Mutex
-
- middleware []Middleware
-
- // transport is the http client transport that will be used to send requests.
- // If nil, the default transport will be used.
- transport http.RoundTripper
-}
-
-func New(opts ...Option) (*Transport, error) {
- t := &Transport{
- Req: &http.Request{
- Method: http.MethodPost,
- },
- }
- if err := t.applyOptions(opts...); err != nil {
- return nil, err
- }
- t.transport = &ochttp.Transport{
- Base: t.transport,
- Propagation: &tracecontext.HTTPFormat{},
- NewClientTrace: ochttp.NewSpanAnnotatingClientTrace,
- FormatSpanName: formatSpanName,
- }
- return t, nil
-}
-
-func (t *Transport) applyOptions(opts ...Option) error {
- for _, fn := range opts {
- if err := fn(t); err != nil {
- return err
- }
- }
- return nil
-}
-
-func (t *Transport) loadCodec(ctx context.Context) bool {
- if t.codec == nil {
- t.crMu.Lock()
- if t.DefaultEncodingSelectionFn != nil && t.Encoding != Default {
- logger := cecontext.LoggerFrom(ctx)
- logger.Warn("transport has a DefaultEncodingSelectionFn set but Encoding is not Default. DefaultEncodingSelectionFn will be ignored.")
-
- t.codec = &Codec{
- Encoding: t.Encoding,
- }
- } else {
- t.codec = &Codec{
- Encoding: t.Encoding,
- DefaultEncodingSelectionFn: t.DefaultEncodingSelectionFn,
- }
- }
- t.crMu.Unlock()
- }
- return true
-}
-
-func copyHeaders(from, to http.Header) {
- if from == nil || to == nil {
- return
- }
- for header, values := range from {
- for _, value := range values {
- to.Add(header, value)
- }
- }
-}
-
-// Ensure to is a non-nil map before copying
-func copyHeadersEnsure(from http.Header, to *http.Header) {
- if len(from) > 0 {
- if *to == nil {
- *to = http.Header{}
- }
- copyHeaders(from, *to)
- }
-}
-
-// Send implements Transport.Send
-func (t *Transport) Send(ctx context.Context, event cloudevents.Event) (context.Context, *cloudevents.Event, error) {
- ctx, r := observability.NewReporter(ctx, reportSend)
- rctx, resp, err := t.obsSend(ctx, event)
- if err != nil {
- r.Error()
- } else {
- r.OK()
- }
- return rctx, resp, err
-}
-
-func (t *Transport) obsSend(ctx context.Context, event cloudevents.Event) (context.Context, *cloudevents.Event, error) {
- req := http.Request{
- Header: HeaderFrom(ctx),
- }
- if t.Req != nil {
- req.Method = t.Req.Method
- req.URL = t.Req.URL
- req.Close = t.Req.Close
- req.Host = t.Req.Host
- copyHeadersEnsure(t.Req.Header, &req.Header)
- }
-
- // Override the default request with target from context.
- if target := cecontext.TargetFrom(ctx); target != nil {
- req.URL = target
- }
-
- if ok := t.loadCodec(ctx); !ok {
- return WithTransportContext(ctx, NewTransportContextFromResponse(nil)), nil, fmt.Errorf("unknown encoding set on transport: %d", t.Encoding)
- }
-
- msg, err := t.codec.Encode(ctx, event)
- if err != nil {
- return WithTransportContext(ctx, NewTransportContextFromResponse(nil)), nil, err
- }
-
- if m, ok := msg.(*Message); ok {
- m.ToRequest(&req)
- client := t.Client
- if client == nil {
- client = &http.Client{Transport: t.transport}
- }
- return httpDo(ctx, client, &req, func(resp *http.Response, err error) (context.Context, *cloudevents.Event, error) {
- rctx := WithTransportContext(ctx, NewTransportContextFromResponse(resp))
- if err != nil {
- return rctx, nil, err
- }
- defer resp.Body.Close()
-
- body, _ := ioutil.ReadAll(resp.Body)
- respEvent, err := t.MessageToEvent(ctx, &Message{
- Header: resp.Header,
- Body: body,
- })
- if err != nil {
- isErr := true
- handled := false
- if txerr, ok := err.(*transport.ErrTransportMessageConversion); ok {
- if !txerr.IsFatal() {
- isErr = false
- }
- if txerr.Handled() {
- handled = true
- }
- }
- if isErr {
- return rctx, nil, err
- }
- if handled {
- return rctx, nil, nil
- }
- }
- if accepted(resp) {
- return rctx, respEvent, nil
- }
- return rctx, respEvent, fmt.Errorf("error sending cloudevent: %s", resp.Status)
- })
- }
- return WithTransportContext(ctx, NewTransportContextFromResponse(nil)), nil, fmt.Errorf("failed to encode Event into a Message")
-}
-
-func (t *Transport) MessageToEvent(ctx context.Context, msg *Message) (*cloudevents.Event, error) {
- logger := cecontext.LoggerFrom(ctx)
- var event *cloudevents.Event
- var err error
-
- if msg.CloudEventsVersion() != "" {
- // This is likely a cloudevents encoded message, try to decode it.
- if ok := t.loadCodec(ctx); !ok {
- err = transport.NewErrTransportMessageConversion("http", fmt.Sprintf("unknown encoding set on transport: %d", t.Encoding), false, true)
- logger.Error("failed to load codec", zap.Error(err))
- } else {
- event, err = t.codec.Decode(ctx, msg)
- }
- } else {
- err = transport.NewErrTransportMessageConversion("http", "cloudevents version unknown", false, false)
- }
-
- // If codec returns and error, or could not load the correct codec, try
- // with the converter if it is set.
- if err != nil && t.HasConverter() {
- event, err = t.Converter.Convert(ctx, msg, err)
- }
-
- // If err is still set, it means that there was no converter, or the
- // converter failed to convert.
- if err != nil {
- logger.Debug("failed to decode message", zap.Error(err))
- }
-
- // If event and error are both nil, then there is nothing to do with this event, it was handled.
- if err == nil && event == nil {
- logger.Debug("convert function returned (nil, nil)")
- err = transport.NewErrTransportMessageConversion("http", "convert function handled request", true, false)
- }
-
- return event, err
-}
-
-// SetReceiver implements Transport.SetReceiver
-func (t *Transport) SetReceiver(r transport.Receiver) {
- t.Receiver = r
-}
-
-// SetConverter implements Transport.SetConverter
-func (t *Transport) SetConverter(c transport.Converter) {
- t.Converter = c
-}
-
-// HasConverter implements Transport.HasConverter
-func (t *Transport) HasConverter() bool {
- return t.Converter != nil
-}
-
-// StartReceiver implements Transport.StartReceiver
-// NOTE: This is a blocking call.
-func (t *Transport) StartReceiver(ctx context.Context) error {
- t.reMu.Lock()
- defer t.reMu.Unlock()
-
- if t.LongPollReq != nil {
- go func() { _ = t.longPollStart(ctx) }()
- }
-
- if t.Handler == nil {
- t.Handler = http.NewServeMux()
- }
- if !t.handlerRegistered {
- // handler.Handle might panic if the user tries to use the same path as the sdk.
- t.Handler.Handle(t.GetPath(), t)
- t.handlerRegistered = true
- }
-
- addr, err := t.listen()
- if err != nil {
- return err
- }
-
- t.server = &http.Server{
- Addr: addr.String(),
- Handler: &ochttp.Handler{
- Propagation: &tracecontext.HTTPFormat{},
- Handler: attachMiddleware(t.Handler, t.middleware),
- FormatSpanName: formatSpanName,
- },
- }
-
- // Shutdown
- defer func() {
- t.server.Close()
- t.server = nil
- }()
-
- errChan := make(chan error, 1)
- go func() {
- errChan <- t.server.Serve(t.listener)
- }()
-
- // wait for the server to return or ctx.Done().
- select {
- case <-ctx.Done():
- // Try a gracefully shutdown.
- timeout := DefaultShutdownTimeout
- if t.ShutdownTimeout != nil {
- timeout = *t.ShutdownTimeout
- }
- ctx, cancel := context.WithTimeout(context.Background(), timeout)
- defer cancel()
- err := t.server.Shutdown(ctx)
- <-errChan // Wait for server goroutine to exit
- return err
- case err := <-errChan:
- return err
- }
-}
-
-// HasTracePropagation implements Transport.HasTracePropagation
-func (t *Transport) HasTracePropagation() bool {
- return true
-}
-
-func (t *Transport) longPollStart(ctx context.Context) error {
- logger := cecontext.LoggerFrom(ctx)
- logger.Info("starting long poll receiver")
-
- if t.LongPollClient == nil {
- t.crMu.Lock()
- t.LongPollClient = &http.Client{}
- t.crMu.Unlock()
- }
- req := &http.Request{
- // TODO: decide if it is ok to use HeaderFrom context here.
- Header: HeaderFrom(ctx),
- }
- if t.LongPollReq != nil {
- req.Method = t.LongPollReq.Method
- req.URL = t.LongPollReq.URL
- req.Close = t.LongPollReq.Close
- copyHeaders(t.LongPollReq.Header, req.Header)
- }
-
- // Override the default request with target from context.
- if target := LongPollTargetFrom(ctx); target != nil {
- req.URL = target
- }
-
- if req.URL == nil {
- return errors.New("no long poll target found")
- }
-
- req = req.WithContext(ctx)
- msgCh := make(chan Message)
- defer close(msgCh)
- isClosed := false
-
- go func(ch chan<- Message) {
- for {
- if isClosed {
- return
- }
-
- if resp, err := t.LongPollClient.Do(req); err != nil {
- logger.Errorw("long poll request returned error", err)
- uErr := err.(*url.Error)
- if uErr.Temporary() || uErr.Timeout() {
- continue
- }
- // TODO: if the transport is throwing errors, we might want to try again. Maybe with a back-off sleep.
- // But this error also might be that there was a done on the context.
- } else if resp.StatusCode == http.StatusNotModified {
- // Keep polling.
- continue
- } else if resp.StatusCode == http.StatusOK {
- body, _ := ioutil.ReadAll(resp.Body)
- if err := resp.Body.Close(); err != nil {
- logger.Warnw("error closing long poll response body", zap.Error(err))
- }
- msg := Message{
- Header: resp.Header,
- Body: body,
- }
- msgCh <- msg
- } else {
- // TODO: not sure what to do with upstream errors yet.
- logger.Errorw("unhandled long poll response", zap.Any("resp", resp))
- }
- }
- }(msgCh)
-
- // Attach the long poll request context to the context.
- ctx = WithTransportContext(ctx, TransportContext{
- URI: req.URL.RequestURI(),
- Host: req.URL.Host,
- Method: req.Method,
- })
-
- for {
- select {
- case <-ctx.Done():
- isClosed = true
- return nil
- case msg := <-msgCh:
- logger.Debug("got a message", zap.Any("msg", msg))
- if event, err := t.MessageToEvent(ctx, &msg); err != nil {
- logger.Errorw("could not convert http message to event", zap.Error(err))
- } else {
- logger.Debugw("got an event", zap.Any("event", event))
- // TODO: deliver event.
- if _, err := t.invokeReceiver(ctx, *event); err != nil {
- logger.Errorw("could not invoke receiver event", zap.Error(err))
- }
- }
- }
- }
-}
-
-// attachMiddleware attaches the HTTP middleware to the specified handler.
-func attachMiddleware(h http.Handler, middleware []Middleware) http.Handler {
- for _, m := range middleware {
- h = m(h)
- }
- return h
-}
-
-func formatSpanName(r *http.Request) string {
- return "cloudevents.http." + r.URL.Path
-}
-
-type eventError struct {
- ctx context.Context
- event *cloudevents.Event
- err error
-}
-
-func httpDo(ctx context.Context, client *http.Client, req *http.Request, fn func(*http.Response, error) (context.Context, *cloudevents.Event, error)) (context.Context, *cloudevents.Event, error) {
- // Run the HTTP request in a goroutine and pass the response to fn.
- c := make(chan eventError, 1)
- req = req.WithContext(ctx)
- go func() {
- rctx, event, err := fn(client.Do(req))
- c <- eventError{ctx: rctx, event: event, err: err}
- }()
- select {
- case <-ctx.Done():
- return ctx, nil, ctx.Err()
- case ee := <-c:
- return ee.ctx, ee.event, ee.err
- }
-}
-
-// accepted is a helper method to understand if the response from the target
-// accepted the CloudEvent.
-func accepted(resp *http.Response) bool {
- if resp.StatusCode >= 200 && resp.StatusCode < 300 {
- return true
- }
- return false
-}
-
-func (t *Transport) invokeReceiver(ctx context.Context, event cloudevents.Event) (*Response, error) {
- ctx, r := observability.NewReporter(ctx, reportReceive)
- resp, err := t.obsInvokeReceiver(ctx, event)
- if err != nil {
- r.Error()
- } else {
- r.OK()
- }
- return resp, err
-}
-
-func (t *Transport) obsInvokeReceiver(ctx context.Context, event cloudevents.Event) (*Response, error) {
- logger := cecontext.LoggerFrom(ctx)
- if t.Receiver != nil {
- // Note: http does not use eventResp.Reason
- eventResp := cloudevents.EventResponse{}
- resp := Response{}
-
- err := t.Receiver.Receive(ctx, event, &eventResp)
- if err != nil {
- logger.Warnw("got an error from receiver fn", zap.Error(err))
- resp.StatusCode = http.StatusInternalServerError
- return &resp, err
- }
-
- if eventResp.Event != nil {
- if t.loadCodec(ctx) {
- if m, err := t.codec.Encode(ctx, *eventResp.Event); err != nil {
- logger.Errorw("failed to encode response from receiver fn", zap.Error(err))
- } else if msg, ok := m.(*Message); ok {
- resp.Message = *msg
- }
- } else {
- logger.Error("failed to load codec")
- resp.StatusCode = http.StatusInternalServerError
- return &resp, err
- }
- // Look for a transport response context
- var trx *TransportResponseContext
- if ptrTrx, ok := eventResp.Context.(*TransportResponseContext); ok {
- // found a *TransportResponseContext, use it.
- trx = ptrTrx
- } else if realTrx, ok := eventResp.Context.(TransportResponseContext); ok {
- // found a TransportResponseContext, make it a pointer.
- trx = &realTrx
- }
- // If we found a TransportResponseContext, use it.
- if trx != nil && trx.Header != nil && len(trx.Header) > 0 {
- copyHeadersEnsure(trx.Header, &resp.Message.Header)
- }
- }
-
- if eventResp.Status != 0 {
- resp.StatusCode = eventResp.Status
- } else {
- resp.StatusCode = http.StatusAccepted // default is 202 - Accepted
- }
- return &resp, err
- }
- return nil, nil
-}
-
-// ServeHTTP implements http.Handler
-func (t *Transport) ServeHTTP(w http.ResponseWriter, req *http.Request) {
- ctx, r := observability.NewReporter(req.Context(), reportServeHTTP)
- // Add the transport context to ctx.
- ctx = WithTransportContext(ctx, NewTransportContext(req))
- logger := cecontext.LoggerFrom(ctx)
-
- body, err := ioutil.ReadAll(req.Body)
- if err != nil {
- logger.Errorw("failed to handle request", zap.Error(err))
- w.WriteHeader(http.StatusBadRequest)
- _, _ = w.Write([]byte(`{"error":"Invalid request"}`))
- r.Error()
- return
- }
-
- event, err := t.MessageToEvent(ctx, &Message{
- Header: req.Header,
- Body: body,
- })
- if err != nil {
- isFatal := true
- handled := false
- if txerr, ok := err.(*transport.ErrTransportMessageConversion); ok {
- isFatal = txerr.IsFatal()
- handled = txerr.Handled()
- }
- if isFatal {
- logger.Errorw("failed to convert http message to event", zap.Error(err))
- w.WriteHeader(http.StatusBadRequest)
- _, _ = w.Write([]byte(fmt.Sprintf(`{"error":%q}`, err.Error())))
- r.Error()
- return
- }
- // if handled, do not pass to receiver.
- if handled {
- w.WriteHeader(http.StatusNoContent)
- r.OK()
- return
- }
- }
- if event == nil {
- logger.Error("failed to get non-nil event from MessageToEvent")
- w.WriteHeader(http.StatusBadRequest)
- r.Error()
- return
- }
-
- resp, err := t.invokeReceiver(ctx, *event)
- if err != nil {
- logger.Warnw("error returned from invokeReceiver", zap.Error(err))
- w.WriteHeader(http.StatusBadRequest)
- _, _ = w.Write([]byte(fmt.Sprintf(`{"error":%q}`, err.Error())))
- r.Error()
- return
- }
-
- if resp != nil {
- if t.Req != nil {
- copyHeaders(t.Req.Header, w.Header())
- }
- if len(resp.Message.Header) > 0 {
- copyHeaders(resp.Message.Header, w.Header())
- }
-
- status := http.StatusAccepted
- if resp.StatusCode >= 200 && resp.StatusCode < 600 {
- status = resp.StatusCode
- }
- w.Header().Add("Content-Length", strconv.Itoa(len(resp.Message.Body)))
- w.WriteHeader(status)
-
- if len(resp.Message.Body) > 0 {
- if _, err := w.Write(resp.Message.Body); err != nil {
- r.Error()
- return
- }
- }
-
- r.OK()
- return
- }
-
- w.WriteHeader(http.StatusNoContent)
- r.OK()
-}
-
-// GetPort returns the listening port.
-// Returns -1 if there is a listening error.
-// Note this will call net.Listen() if the listener is not already started.
-func (t *Transport) GetPort() int {
- // Ensure we have a listener and therefore a port.
- if _, err := t.listen(); err == nil || t.Port != nil {
- return *t.Port
- }
- return -1
-}
-
-func (t *Transport) setPort(port int) {
- if t.Port == nil {
- t.Port = new(int)
- }
- *t.Port = port
-}
-
-// listen if not already listening, update t.Port
-func (t *Transport) listen() (net.Addr, error) {
- if t.listener == nil {
- port := 8080
- if t.Port != nil {
- port = *t.Port
- if port < 0 || port > 65535 {
- return nil, fmt.Errorf("invalid port %d", port)
- }
- }
- var err error
- if t.listener, err = net.Listen("tcp", fmt.Sprintf(":%d", port)); err != nil {
- return nil, err
- }
- }
- addr := t.listener.Addr()
- if tcpAddr, ok := addr.(*net.TCPAddr); ok {
- t.setPort(tcpAddr.Port)
- }
- return addr, nil
-}
-
-// GetPath returns the path the transport is hosted on. If the path is '/',
-// the transport will handle requests on any URI. To discover the true path
-// a request was received on, inspect the context from Receive(cxt, ...) with
-// TransportContextFrom(ctx).
-func (t *Transport) GetPath() string {
- path := strings.TrimSpace(t.Path)
- if len(path) > 0 {
- return path
- }
- return "/" // default
-}
diff --git a/v1/cloudevents/transport/http/transport_test.go b/v1/cloudevents/transport/http/transport_test.go
deleted file mode 100644
index 79839b071..000000000
--- a/v1/cloudevents/transport/http/transport_test.go
+++ /dev/null
@@ -1,268 +0,0 @@
-package http_test
-
-import (
- "bytes"
- "context"
- "fmt"
- "io/ioutil"
- "net"
- "net/http"
- "net/http/httptest"
- "strings"
- "sync/atomic"
- "testing"
- "time"
-
- "github.com/cloudevents/sdk-go/v1/cloudevents"
- cehttp "github.com/cloudevents/sdk-go/v1/cloudevents/transport/http"
- "github.com/cloudevents/sdk-go/v1/cloudevents/types"
- "golang.org/x/sync/errgroup"
-)
-
-type task func() error
-
-// We can't use net/http/httptest.Server here because it's connection
-// tracking logic interferes with the connection lifecycle under test
-func startTestServer(handler http.Handler) (*http.Server, error) {
- listener, err := net.Listen("tcp", ":0")
- if err != nil {
- return nil, err
- }
- server := &http.Server{
- Addr: listener.Addr().String(),
- Handler: handler,
- }
- go func() { _ = server.Serve(listener) }()
- return server, nil
-}
-
-func doConcurrently(concurrency int, duration time.Duration, fn task) error {
- var group errgroup.Group
- for i := 0; i < concurrency; i++ {
- group.Go(func() error {
- done := time.After(duration)
- for {
- select {
- case <-done:
- return nil
- default:
- if err := fn(); err != nil {
- return err
- }
- }
- }
- })
- }
-
- if err := group.Wait(); err != nil {
- return err
- }
- return nil
-}
-
-// An example of how to make a stable client under sustained
-// concurrency sending to a single host
-func makeStableClient(addr string) (*cehttp.Transport, error) {
- netHTTPTransport := &http.Transport{
- MaxIdleConnsPerHost: 1000,
- MaxConnsPerHost: 5000,
- }
- ceClient, err := cehttp.New(
- cehttp.WithTarget(addr),
- cehttp.WithHTTPTransport(netHTTPTransport),
- )
- if err != nil {
- return nil, err
- }
- return ceClient, nil
-}
-
-func TestStableConnectionsToSingleHost(t *testing.T) {
- // Start a dummy HTTP server
- handler := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
- time.Sleep(50 * time.Millisecond)
- w.WriteHeader(http.StatusAccepted)
- })
- sinkServer, err := startTestServer(handler)
- if err != nil {
- t.Fatalf("unexpected error starting test http server %v", err.Error())
- }
- defer sinkServer.Close()
-
- // Keep track of all new connections to that dummy HTTP server
- var newConnectionCount uint64
- sinkServer.ConnState = func(connection net.Conn, state http.ConnState) {
- if state == http.StateNew {
- atomic.AddUint64(&newConnectionCount, 1)
- }
- }
-
- ceClient, err := makeStableClient("http://" + sinkServer.Addr)
- if err != nil {
- t.Fatalf("unexpected error creating CloudEvents client %v", err.Error())
- }
- event := cloudevents.Event{
- Context: &cloudevents.EventContextV02{
- SpecVersion: cloudevents.CloudEventsVersionV02,
- Type: "test.event",
- ID: "abc-123",
- Source: *types.ParseURLRef("test"),
- },
- }
-
- ctx := context.TODO()
- concurrency := 64
- duration := 1 * time.Second
- var sent uint64
- err = doConcurrently(concurrency, duration, func() error {
- rctx, _, err := ceClient.Send(ctx, event)
- if err != nil {
- return fmt.Errorf("unexpected error sending CloudEvent: %v", err.Error())
- }
- trctx := cehttp.TransportContextFrom(rctx)
- if trctx.StatusCode != http.StatusAccepted {
- return fmt.Errorf("unexpected status code: want %d, got %d", http.StatusAccepted, trctx.StatusCode)
- }
- atomic.AddUint64(&sent, 1)
- return nil
- })
- if err != nil {
- t.Errorf("error sending concurrent CloudEvents: %v", err)
- }
-
- // newConnectionCount usually equals concurrency, but give some
- // leeway. When this fails, it fails by a lot
- if newConnectionCount > uint64(concurrency*2) {
- t.Errorf("too many new connections opened: expected %d, got %d", concurrency, newConnectionCount)
- }
-}
-
-func TestMiddleware(t *testing.T) {
- testCases := map[string]struct {
- middleware []string
- }{
- "none": {},
- "one": {
- middleware: []string{"Foo"},
- },
- "nested": {
- middleware: []string{"Foo", "Bar", "Qux"},
- },
- }
- for n, tc := range testCases {
- t.Run(n, func(t *testing.T) {
- m := make([]cehttp.Option, 0, len(tc.middleware)+2)
- m = append(m, cehttp.WithPort(0), cehttp.WithShutdownTimeout(time.Nanosecond))
- for _, ms := range tc.middleware {
- ms := ms
- m = append(m, cehttp.WithMiddleware(func(next http.Handler) http.Handler {
- return &namedHandler{
- name: ms,
- next: next,
- }
- }))
- }
- tr, err := cehttp.New(m...)
- if err != nil {
- t.Fatalf("Unable to create transport, %v", err)
- }
- innermostResponse := "Original"
- origResponse := makeRequestToServer(t, tr, innermostResponse)
-
- // Verify that the response is all the middlewares run in the correct order (as a stack).
- response := string(origResponse)
- for i := len(tc.middleware) - 1; i >= 0; i-- {
- expected := tc.middleware[i]
- if !strings.HasPrefix(response, expected) {
- t.Fatalf("Incorrect prefix at offset %d. Expected %s. Actual %s", i, tc.middleware[i], string(origResponse))
- }
- response = strings.TrimPrefix(response, expected)
- }
- if response != innermostResponse {
- t.Fatalf("Incorrect prefix at last offset. Expected '%s'. Actual %s", innermostResponse, string(origResponse))
- }
- })
- }
-}
-
-// makeRequestToServer starts the transport and makes a request to it, pointing at a custom path that will return
-// responseText.
-func makeRequestToServer(t *testing.T, tr *cehttp.Transport, responseText string) string {
- // Create a custom path that will be used to respond with responseText.
- tr.Handler = http.NewServeMux()
- path := "/123"
- tr.Handler.HandleFunc(path, func(w http.ResponseWriter, _ *http.Request) {
- _, _ = w.Write([]byte(responseText))
- })
-
- // Start the server.
- ctx, cancel := context.WithCancel(context.Background())
- defer cancel()
- go func() { _ = tr.StartReceiver(ctx) }()
-
- // Give some time for the receiver to start. One second was chosen arbitrarily.
- time.Sleep(time.Second)
-
- // Make the request.
- port := tr.GetPort()
- r, err := http.Post(fmt.Sprintf("http://localhost:%d%s", port, path), "text", &bytes.Buffer{})
- if err != nil {
- t.Fatalf("Error posting: %v", err)
- }
- rb, err := ioutil.ReadAll(r.Body)
- if err != nil {
- t.Fatalf("Error reading: %v", err)
- }
- return string(rb)
-}
-
-type namedHandler struct {
- name string
- next http.Handler
-}
-
-func (h *namedHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
- _, _ = w.Write([]byte(h.name))
- h.next.ServeHTTP(w, r)
-}
-
-func TestServeHTTPSpecVersion(t *testing.T) {
- tests := []struct {
- specVersion string
- expectedStatus int
- }{
- {
- // Missing specVersion -> HTTP 400
- specVersion: "",
- expectedStatus: http.StatusBadRequest,
- },
- {
- // Valid request
- specVersion: "0.3",
- expectedStatus: http.StatusNoContent,
- },
- {
- // Future specVersion -> HTTP 400
- specVersion: "999999.99",
- expectedStatus: http.StatusBadRequest,
- },
- }
-
- for _, test := range tests {
- t.Run(test.specVersion, func(t *testing.T) {
- transport := &cehttp.Transport{}
- req := httptest.NewRequest("GET", "/", nil)
- req.Header.Set("ce-specversion", test.specVersion)
- w := httptest.NewRecorder()
-
- transport.ServeHTTP(w, req)
-
- actualStatus := w.Result().StatusCode
- if actualStatus != test.expectedStatus {
- t.Errorf("actual status (%d) != expected status (%d)", actualStatus, test.expectedStatus)
- t.Errorf("response body: %s", w.Body.String())
- }
- })
- }
-
-}
diff --git a/v1/cloudevents/transport/message.go b/v1/cloudevents/transport/message.go
deleted file mode 100644
index e2ed55c97..000000000
--- a/v1/cloudevents/transport/message.go
+++ /dev/null
@@ -1,9 +0,0 @@
-package transport
-
-// Message is the abstract transport message wrapper.
-type Message interface {
- // CloudEventsVersion returns the version of the CloudEvent.
- CloudEventsVersion() string
-
- // TODO maybe get encoding
-}
diff --git a/v1/cloudevents/transport/nats/codec.go b/v1/cloudevents/transport/nats/codec.go
deleted file mode 100644
index e87188909..000000000
--- a/v1/cloudevents/transport/nats/codec.go
+++ /dev/null
@@ -1,100 +0,0 @@
-package nats
-
-import (
- "context"
- "fmt"
-
- "github.com/cloudevents/sdk-go/v1/cloudevents"
- "github.com/cloudevents/sdk-go/v1/cloudevents/transport"
-)
-
-type Codec struct {
- Encoding Encoding
-
- v02 *CodecV02
- v03 *CodecV03
- v1 *CodecV1
-}
-
-var _ transport.Codec = (*Codec)(nil)
-
-func (c *Codec) Encode(ctx context.Context, e cloudevents.Event) (transport.Message, error) {
- switch c.Encoding {
- case Default:
- fallthrough
- case StructuredV02:
- if c.v02 == nil {
- c.v02 = &CodecV02{Encoding: c.Encoding}
- }
- return c.v02.Encode(ctx, e)
- case StructuredV03:
- if c.v03 == nil {
- c.v03 = &CodecV03{Encoding: c.Encoding}
- }
- return c.v03.Encode(ctx, e)
- case StructuredV1:
- if c.v1 == nil {
- c.v1 = &CodecV1{Encoding: c.Encoding}
- }
- return c.v1.Encode(ctx, e)
- default:
- return nil, fmt.Errorf("unknown encoding: %d", c.Encoding)
- }
-}
-
-func (c *Codec) Decode(ctx context.Context, msg transport.Message) (*cloudevents.Event, error) {
- switch c.inspectEncoding(ctx, msg) {
- case Default:
- fallthrough
- case StructuredV02:
- if c.v02 == nil {
- c.v02 = &CodecV02{Encoding: c.Encoding}
- }
- return c.v02.Decode(ctx, msg)
- case StructuredV03:
- if c.v03 == nil {
- c.v03 = &CodecV03{Encoding: c.Encoding}
- }
- return c.v03.Decode(ctx, msg)
- case StructuredV1:
- if c.v1 == nil {
- c.v1 = &CodecV1{Encoding: c.Encoding}
- }
- return c.v1.Decode(ctx, msg)
- default:
- return nil, transport.NewErrMessageEncodingUnknown("wrapper", TransportName)
- }
-}
-
-func (c *Codec) inspectEncoding(ctx context.Context, msg transport.Message) Encoding {
-
- if c.v1 == nil {
- c.v1 = &CodecV1{Encoding: c.Encoding}
- }
- // Try v1.0.
- encoding := c.v1.inspectEncoding(ctx, msg)
- if encoding != Unknown {
- return encoding
- }
-
- if c.v03 == nil {
- c.v03 = &CodecV03{Encoding: c.Encoding}
- }
- // Try v0.3.
- encoding = c.v03.inspectEncoding(ctx, msg)
- if encoding != Unknown {
- return encoding
- }
-
- if c.v02 == nil {
- c.v02 = &CodecV02{Encoding: c.Encoding}
- }
- // Try v0.2.
- encoding = c.v02.inspectEncoding(ctx, msg)
- if encoding != Unknown {
- return encoding
- }
-
- // We do not understand the message encoding.
- return Unknown
-}
diff --git a/v1/cloudevents/transport/nats/codec_structured.go b/v1/cloudevents/transport/nats/codec_structured.go
deleted file mode 100644
index cc4f8a147..000000000
--- a/v1/cloudevents/transport/nats/codec_structured.go
+++ /dev/null
@@ -1,39 +0,0 @@
-package nats
-
-import (
- "context"
- "encoding/json"
- "fmt"
-
- "github.com/cloudevents/sdk-go/v1/cloudevents"
- "github.com/cloudevents/sdk-go/v1/cloudevents/transport"
-)
-
-// CodecStructured represents an structured http transport codec for all versions.
-// Intended to be used as a base class.
-type CodecStructured struct {
- Encoding Encoding
-}
-
-func (v CodecStructured) encodeStructured(ctx context.Context, e cloudevents.Event) (transport.Message, error) {
- body, err := json.Marshal(e)
- if err != nil {
- return nil, err
- }
-
- msg := &Message{
- Body: body,
- }
-
- return msg, nil
-}
-
-func (v CodecStructured) decodeStructured(ctx context.Context, version string, msg transport.Message) (*cloudevents.Event, error) {
- m, ok := msg.(*Message)
- if !ok {
- return nil, fmt.Errorf("failed to convert transport.Message to nats.Message")
- }
- event := cloudevents.New(version)
- err := json.Unmarshal(m.Body, &event)
- return &event, err
-}
diff --git a/v1/cloudevents/transport/nats/codec_test.go b/v1/cloudevents/transport/nats/codec_test.go
deleted file mode 100644
index f5f96d381..000000000
--- a/v1/cloudevents/transport/nats/codec_test.go
+++ /dev/null
@@ -1,399 +0,0 @@
-package nats_test
-
-import (
- "context"
- "encoding/json"
- "fmt"
- "net/url"
- "testing"
- "time"
-
- "github.com/cloudevents/sdk-go/v1/cloudevents"
- "github.com/cloudevents/sdk-go/v1/cloudevents/transport/nats"
- "github.com/cloudevents/sdk-go/v1/cloudevents/types"
- "github.com/google/go-cmp/cmp"
-)
-
-func TestCodecEncode(t *testing.T) {
- sourceUrl, _ := url.Parse("http://example.com/source")
- source := &types.URLRef{URL: *sourceUrl}
-
- testCases := map[string]struct {
- codec nats.Codec
- event cloudevents.Event
- want *nats.Message
- wantErr error
- }{
- "simple v02 structured binary": {
- codec: nats.Codec{Encoding: nats.StructuredV02},
- event: cloudevents.Event{
- Context: cloudevents.EventContextV02{
- Type: "com.example.test",
- Source: *source,
- ID: "ABC-123",
- }.AsV02(),
- },
- want: &nats.Message{
- Body: func() []byte {
- body := map[string]interface{}{
- "specversion": "0.2",
- "id": "ABC-123",
- "type": "com.example.test",
- "source": "http://example.com/source",
- }
- return toBytes(body)
- }(),
- },
- },
- }
- for n, tc := range testCases {
- t.Run(n, func(t *testing.T) {
-
- got, err := tc.codec.Encode(context.TODO(), tc.event)
-
- if tc.wantErr != nil || err != nil {
- if diff := cmp.Diff(tc.wantErr, err); diff != "" {
- t.Errorf("unexpected error (-want, +got) = %v", diff)
- }
- return
- }
-
- if diff := cmp.Diff(tc.want, got); diff != "" {
- if msg, ok := got.(*nats.Message); ok {
- // It is hard to read the byte dump
- want := string(tc.want.Body)
- got := string(msg.Body)
- if diff := cmp.Diff(want, got); diff != "" {
- t.Errorf("unexpected (-want, +got) = %v", diff)
- return
- }
- }
- t.Errorf("unexpected message (-want, +got) = %v", diff)
- }
- })
- }
-}
-
-func TestCodecDecode(t *testing.T) {
- sourceUrl, _ := url.Parse("http://example.com/source")
- source := &types.URLRef{URL: *sourceUrl}
-
- testCases := map[string]struct {
- codec nats.Codec
- msg *nats.Message
- want *cloudevents.Event
- wantErr error
- }{
- "simple v2 structured": {
- codec: nats.Codec{Encoding: nats.StructuredV02},
- msg: &nats.Message{
- Body: func() []byte {
- body := map[string]interface{}{
- "specversion": "0.2",
- "id": "ABC-123",
- "type": "com.example.test",
- "source": "http://example.com/source",
- }
- return toBytes(body)
- }(),
- },
- want: &cloudevents.Event{
- Context: &cloudevents.EventContextV02{
- SpecVersion: cloudevents.CloudEventsVersionV02,
- Type: "com.example.test",
- Source: *source,
- ID: "ABC-123",
- },
- },
- },
- }
- for n, tc := range testCases {
- t.Run(n, func(t *testing.T) {
-
- got, err := tc.codec.Decode(context.TODO(), tc.msg)
-
- if tc.wantErr != nil || err != nil {
- if diff := cmp.Diff(tc.wantErr, err); diff != "" {
- t.Errorf("unexpected error (-want, +got) = %v", diff)
- }
- return
- }
-
- if diff := cmp.Diff(tc.want, got); diff != "" {
- t.Errorf("unexpected event (-want, +got) = %v", diff)
- }
- })
- }
-}
-
-type DataExample struct {
- AnInt int `json:"a,omitempty" xml:"a,omitempty"`
- AString string `json:"b,omitempty" xml:"b,omitempty"`
- AnArray []string `json:"c,omitempty" xml:"c,omitempty"`
- AMap map[string]map[string]int `json:"d,omitempty" xml:"d,omitempty"`
- ATime *time.Time `json:"e,omitempty" xml:"e,omitempty"`
-}
-
-func TestCodecRoundTrip(t *testing.T) {
- sourceUrl, _ := url.Parse("http://example.com/source")
- source := &types.URLRef{URL: *sourceUrl}
-
- for _, encoding := range []nats.Encoding{nats.StructuredV02} {
-
- testCases := map[string]struct {
- codec nats.Codec
- event cloudevents.Event
- want cloudevents.Event
- wantErr error
- }{
- "simple data": {
- codec: nats.Codec{Encoding: encoding},
- event: cloudevents.Event{
- Context: cloudevents.EventContextV01{
- EventType: "com.example.test",
- Source: *source,
- EventID: "ABC-123",
- }.AsV02(),
- Data: map[string]string{
- "a": "apple",
- "b": "banana",
- },
- },
- want: cloudevents.Event{
- Context: &cloudevents.EventContextV01{
- CloudEventsVersion: cloudevents.CloudEventsVersionV01,
- EventType: "com.example.test",
- Source: *source,
- EventID: "ABC-123",
- },
- Data: map[string]interface{}{
- "a": "apple",
- "b": "banana",
- },
- DataEncoded: true,
- },
- },
- "struct data": {
- codec: nats.Codec{Encoding: encoding},
- event: cloudevents.Event{
- Context: cloudevents.EventContextV01{
- EventType: "com.example.test",
- Source: *source,
- EventID: "ABC-123",
- }.AsV02(),
- Data: DataExample{
- AnInt: 42,
- AString: "testing",
- },
- },
- want: cloudevents.Event{
- Context: &cloudevents.EventContextV01{
- CloudEventsVersion: cloudevents.CloudEventsVersionV01,
- EventType: "com.example.test",
- Source: *source,
- EventID: "ABC-123",
- },
- Data: &DataExample{
- AnInt: 42,
- AString: "testing",
- },
- DataEncoded: true,
- },
- },
- }
- for n, tc := range testCases {
- n = fmt.Sprintf("%s, %s", encoding, n)
- t.Run(n, func(t *testing.T) {
-
- msg, err := tc.codec.Encode(context.TODO(), tc.event)
- if err != nil {
- if diff := cmp.Diff(tc.wantErr, err); diff != "" {
- t.Errorf("unexpected error (-want, +got) = %v", diff)
- }
- return
- }
-
- got, err := tc.codec.Decode(context.TODO(), msg)
- if err != nil {
- if diff := cmp.Diff(tc.wantErr, err); diff != "" {
- t.Errorf("unexpected error (-want, +got) = %v", diff)
- }
- return
- }
-
- if tc.event.Data != nil {
- // We have to be pretty tricky in the test to get the correct type.
- data, _ := types.Allocate(tc.want.Data)
- err := got.DataAs(&data)
- if err != nil {
- if diff := cmp.Diff(tc.wantErr, err); diff != "" {
- t.Errorf("unexpected error (-want, +got) = %v", diff)
- }
- return
- }
- got.Data = data
- }
-
- if tc.wantErr != nil {
- if diff := cmp.Diff(tc.wantErr, err); diff != "" {
- t.Errorf("unexpected error (-want, +got) = %v", diff)
- }
- return
- }
-
- // fix the context back to v01 to test.
- ctxv1 := got.Context.AsV01()
- got.Context = ctxv1
-
- if diff := cmp.Diff(tc.want, *got); diff != "" {
- t.Errorf("unexpected event (-want, +got) = %v", diff)
- }
- })
- }
-
- }
-}
-
-func TestCodecAsMiddleware(t *testing.T) {
- sourceUrl, _ := url.Parse("http://example.com/source")
- source := &types.URLRef{URL: *sourceUrl}
-
- for _, encoding := range []nats.Encoding{nats.StructuredV02} {
-
- testCases := map[string]struct {
- codec nats.Codec
- event cloudevents.Event
- want cloudevents.Event
- wantErr error
- }{
- "simple data": {
- codec: nats.Codec{Encoding: encoding},
- event: cloudevents.Event{
- Context: cloudevents.EventContextV01{
- EventType: "com.example.test",
- Source: *source,
- EventID: "ABC-123",
- }.AsV02(),
- Data: map[string]string{
- "a": "apple",
- "b": "banana",
- },
- },
- want: cloudevents.Event{
- Context: &cloudevents.EventContextV01{
- CloudEventsVersion: cloudevents.CloudEventsVersionV01,
- EventType: "com.example.test",
- Source: *source,
- EventID: "ABC-123",
- },
- Data: map[string]interface{}{
- "a": "apple",
- "b": "banana",
- },
- DataEncoded: true,
- },
- },
- "struct data": {
- codec: nats.Codec{Encoding: encoding},
- event: cloudevents.Event{
- Context: cloudevents.EventContextV01{
- EventType: "com.example.test",
- Source: *source,
- EventID: "ABC-123",
- }.AsV02(),
- Data: DataExample{
- AnInt: 42,
- AString: "testing",
- },
- },
- want: cloudevents.Event{
- Context: &cloudevents.EventContextV01{
- CloudEventsVersion: cloudevents.CloudEventsVersionV01,
- EventType: "com.example.test",
- Source: *source,
- EventID: "ABC-123",
- },
- Data: &DataExample{
- AnInt: 42,
- AString: "testing",
- },
- DataEncoded: true,
- },
- },
- }
- for n, tc := range testCases {
- n = fmt.Sprintf("%s, %s", encoding, n)
- t.Run(n, func(t *testing.T) {
-
- msg1, err := tc.codec.Encode(context.TODO(), tc.event)
- if err != nil {
- if diff := cmp.Diff(tc.wantErr, err); diff != "" {
- t.Errorf("unexpected error (-want, +got) = %v", diff)
- }
- return
- }
-
- midEvent, err := tc.codec.Decode(context.TODO(), msg1)
- if err != nil {
- if diff := cmp.Diff(tc.wantErr, err); diff != "" {
- t.Errorf("unexpected error (-want, +got) = %v", diff)
- }
- return
- }
-
- msg2, err := tc.codec.Encode(context.TODO(), *midEvent)
- if err != nil {
- if diff := cmp.Diff(tc.wantErr, err); diff != "" {
- t.Errorf("unexpected error (-want, +got) = %v", diff)
- }
- return
- }
-
- got, err := tc.codec.Decode(context.TODO(), msg2)
- if err != nil {
- if diff := cmp.Diff(tc.wantErr, err); diff != "" {
- t.Errorf("unexpected error (-want, +got) = %v", diff)
- }
- return
- }
-
- if tc.event.Data != nil {
- // We have to be pretty tricky in the test to get the correct type.
- data, _ := types.Allocate(tc.want.Data)
- err := got.DataAs(&data)
- if err != nil {
- if diff := cmp.Diff(tc.wantErr, err); diff != "" {
- t.Errorf("unexpected error (-want, +got) = %v", diff)
- }
- return
- }
- got.Data = data
- }
-
- if tc.wantErr != nil {
- if diff := cmp.Diff(tc.wantErr, err); diff != "" {
- t.Errorf("unexpected error (-want, +got) = %v", diff)
- }
- return
- }
-
- // fix the context back to v1 to test.
- ctxv1 := got.Context.AsV01()
- got.Context = ctxv1
-
- if diff := cmp.Diff(tc.want, *got); diff != "" {
- t.Errorf("unexpected event (-want, +got) = %v", diff)
- }
- })
- }
-
- }
-}
-
-func toBytes(body map[string]interface{}) []byte {
- b, err := json.Marshal(body)
- if err != nil {
- return []byte(fmt.Sprintf(`{"error":%q}`, err.Error()))
- }
- return b
-}
diff --git a/v1/cloudevents/transport/nats/codec_v02.go b/v1/cloudevents/transport/nats/codec_v02.go
deleted file mode 100644
index 550375c39..000000000
--- a/v1/cloudevents/transport/nats/codec_v02.go
+++ /dev/null
@@ -1,50 +0,0 @@
-package nats
-
-import (
- "context"
- "fmt"
-
- "github.com/cloudevents/sdk-go/v1/cloudevents"
- "github.com/cloudevents/sdk-go/v1/cloudevents/transport"
-)
-
-type CodecV02 struct {
- CodecStructured
-
- Encoding Encoding
-}
-
-var _ transport.Codec = (*CodecV02)(nil)
-
-func (v CodecV02) Encode(ctx context.Context, e cloudevents.Event) (transport.Message, error) {
- switch v.Encoding {
- case Default:
- fallthrough
- case StructuredV02:
- return v.encodeStructured(ctx, e)
- default:
- return nil, fmt.Errorf("unknown encoding: %d", v.Encoding)
- }
-}
-
-func (v CodecV02) Decode(ctx context.Context, msg transport.Message) (*cloudevents.Event, error) {
- // only structured is supported as of v0.2
- switch v.inspectEncoding(ctx, msg) {
- case StructuredV02:
- return v.decodeStructured(ctx, cloudevents.CloudEventsVersionV02, msg)
- default:
- return nil, transport.NewErrMessageEncodingUnknown("v02", TransportName)
- }
-}
-
-func (v CodecV02) inspectEncoding(ctx context.Context, msg transport.Message) Encoding {
- version := msg.CloudEventsVersion()
- if version != cloudevents.CloudEventsVersionV02 {
- return Unknown
- }
- _, ok := msg.(*Message)
- if !ok {
- return Unknown
- }
- return StructuredV02
-}
diff --git a/v1/cloudevents/transport/nats/codec_v02_test.go b/v1/cloudevents/transport/nats/codec_v02_test.go
deleted file mode 100644
index 5ec4bd6f9..000000000
--- a/v1/cloudevents/transport/nats/codec_v02_test.go
+++ /dev/null
@@ -1,265 +0,0 @@
-package nats_test
-
-import (
- "context"
- "net/url"
- "testing"
- "time"
-
- "github.com/cloudevents/sdk-go/v1/cloudevents"
- "github.com/cloudevents/sdk-go/v1/cloudevents/transport/nats"
- "github.com/cloudevents/sdk-go/v1/cloudevents/types"
- "github.com/google/go-cmp/cmp"
-)
-
-func TestCodecV02_Encode(t *testing.T) {
- now := types.Timestamp{Time: time.Now().UTC()}
- sourceUrl, _ := url.Parse("http://example.com/source")
- source := &types.URLRef{URL: *sourceUrl}
-
- schemaUrl, _ := url.Parse("http://example.com/schema")
- schema := &types.URLRef{URL: *schemaUrl}
-
- testCases := map[string]struct {
- codec nats.CodecV02
- event cloudevents.Event
- want *nats.Message
- wantErr error
- }{
- "simple v2 default": {
- codec: nats.CodecV02{},
- event: cloudevents.Event{
- Context: cloudevents.EventContextV02{
- Type: "com.example.test",
- Source: *source,
- ID: "ABC-123",
- }.AsV02(),
- },
- want: &nats.Message{
- Body: func() []byte {
- body := map[string]interface{}{
- "specversion": "0.2",
- "id": "ABC-123",
- "type": "com.example.test",
- "source": "http://example.com/source",
- }
- return toBytes(body)
- }(),
- },
- },
- "full v2 default": {
- codec: nats.CodecV02{},
- event: cloudevents.Event{
- Context: cloudevents.EventContextV02{
- ID: "ABC-123",
- Time: &now,
- Type: "com.example.test",
- SchemaURL: schema,
- ContentType: cloudevents.StringOfApplicationJSON(),
- Source: *source,
- Extensions: map[string]interface{}{
- "test": "extended",
- },
- }.AsV02(),
- Data: map[string]interface{}{
- "hello": "world",
- },
- },
- want: &nats.Message{
- Body: func() []byte {
- body := map[string]interface{}{
- "specversion": "0.2",
- "contenttype": "application/json",
- "data": map[string]interface{}{
- "hello": "world",
- },
- "id": "ABC-123",
- "time": now,
- "type": "com.example.test",
- "test": "extended",
- "schemaurl": "http://example.com/schema",
- "source": "http://example.com/source",
- }
- return toBytes(body)
- }(),
- },
- },
- "simple v2 structured": {
- codec: nats.CodecV02{Encoding: nats.StructuredV02},
- event: cloudevents.Event{
- Context: cloudevents.EventContextV02{
- Type: "com.example.test",
- Source: *source,
- ID: "ABC-123",
- }.AsV02(),
- },
- want: &nats.Message{
- Body: func() []byte {
- body := map[string]interface{}{
- "specversion": "0.2",
- "id": "ABC-123",
- "type": "com.example.test",
- "source": "http://example.com/source",
- }
- return toBytes(body)
- }(),
- },
- },
- "full v2 structured": {
- codec: nats.CodecV02{Encoding: nats.StructuredV02},
- event: cloudevents.Event{
- Context: cloudevents.EventContextV02{
- ID: "ABC-123",
- Time: &now,
- Type: "com.example.test",
- SchemaURL: schema,
- ContentType: cloudevents.StringOfApplicationJSON(),
- Source: *source,
- Extensions: map[string]interface{}{
- "test": "extended",
- },
- }.AsV02(),
- Data: map[string]interface{}{
- "hello": "world",
- },
- },
- want: &nats.Message{
- Body: func() []byte {
- body := map[string]interface{}{
- "specversion": "0.2",
- "contenttype": "application/json",
- "data": map[string]interface{}{
- "hello": "world",
- },
- "id": "ABC-123",
- "time": now,
- "type": "com.example.test",
- "test": "extended",
- "schemaurl": "http://example.com/schema",
- "source": "http://example.com/source",
- }
- return toBytes(body)
- }(),
- },
- },
- }
- for n, tc := range testCases {
- t.Run(n, func(t *testing.T) {
-
- got, err := tc.codec.Encode(context.TODO(), tc.event)
-
- if tc.wantErr != nil || err != nil {
- if diff := cmp.Diff(tc.wantErr, err); diff != "" {
- t.Errorf("unexpected error (-want, +got) = %v", diff)
- }
- return
- }
-
- if diff := cmp.Diff(tc.want, got); diff != "" {
-
- if msg, ok := got.(*nats.Message); ok {
- // It is hard to read the byte dump
- want := string(tc.want.Body)
- got := string(msg.Body)
- if diff := cmp.Diff(want, got); diff != "" {
- t.Errorf("unexpected message body (-want, +got) = %v", diff)
- return
- }
- }
-
- t.Errorf("unexpected message (-want, +got) = %v", diff)
- }
- })
- }
-}
-
-// TODO: figure out extensions for v2
-
-func TestCodecV02_Decode(t *testing.T) {
- now := types.Timestamp{Time: time.Now()}
- sourceUrl, _ := url.Parse("http://example.com/source")
- source := &types.URLRef{URL: *sourceUrl}
-
- schemaUrl, _ := url.Parse("http://example.com/schema")
- schema := &types.URLRef{URL: *schemaUrl}
-
- testCases := map[string]struct {
- codec nats.CodecV02
- msg *nats.Message
- want *cloudevents.Event
- wantErr error
- }{
- "simple v2 structured": {
- codec: nats.CodecV02{},
- msg: &nats.Message{
- Body: toBytes(map[string]interface{}{
- "specversion": "0.2",
- "id": "ABC-123",
- "type": "com.example.test",
- "source": "http://example.com/source",
- }),
- },
- want: &cloudevents.Event{
- Context: &cloudevents.EventContextV02{
- SpecVersion: cloudevents.CloudEventsVersionV02,
- Type: "com.example.test",
- Source: *source,
- ID: "ABC-123",
- },
- },
- },
- "full v2 structured": {
- codec: nats.CodecV02{},
- msg: &nats.Message{
- Body: toBytes(map[string]interface{}{
- "specversion": "0.2",
- "contenttype": "application/json",
- "data": map[string]interface{}{
- "hello": "world",
- },
- "id": "ABC-123",
- "time": now,
- "type": "com.example.test",
- "test": "extended",
- "schemaurl": "http://example.com/schema",
- "source": "http://example.com/source",
- }),
- },
- want: &cloudevents.Event{
- Context: &cloudevents.EventContextV02{
- SpecVersion: cloudevents.CloudEventsVersionV02,
- ID: "ABC-123",
- Time: &now,
- Type: "com.example.test",
- SchemaURL: schema,
- ContentType: cloudevents.StringOfApplicationJSON(),
- Source: *source,
- Extensions: map[string]interface{}{
- "test": "extended",
- },
- },
- Data: toBytes(map[string]interface{}{
- "hello": "world",
- }),
- DataEncoded: true,
- },
- },
- }
- for n, tc := range testCases {
- t.Run(n, func(t *testing.T) {
-
- got, err := tc.codec.Decode(context.TODO(), tc.msg)
-
- if tc.wantErr != nil || err != nil {
- if diff := cmp.Diff(tc.wantErr, err); diff != "" {
- t.Errorf("unexpected error (-want, +got) = %v", diff)
- }
- return
- }
-
- if diff := cmp.Diff(tc.want, got); diff != "" {
- t.Errorf("unexpected event (-want, +got) = %v", diff)
- }
- })
- }
-}
diff --git a/v1/cloudevents/transport/nats/codec_v03.go b/v1/cloudevents/transport/nats/codec_v03.go
deleted file mode 100644
index fa8bfe3f8..000000000
--- a/v1/cloudevents/transport/nats/codec_v03.go
+++ /dev/null
@@ -1,50 +0,0 @@
-package nats
-
-import (
- "context"
- "fmt"
-
- "github.com/cloudevents/sdk-go/v1/cloudevents"
- "github.com/cloudevents/sdk-go/v1/cloudevents/transport"
-)
-
-type CodecV03 struct {
- CodecStructured
-
- Encoding Encoding
-}
-
-var _ transport.Codec = (*CodecV03)(nil)
-
-func (v CodecV03) Encode(ctx context.Context, e cloudevents.Event) (transport.Message, error) {
- switch v.Encoding {
- case Default:
- fallthrough
- case StructuredV03:
- return v.encodeStructured(ctx, e)
- default:
- return nil, fmt.Errorf("unknown encoding: %d", v.Encoding)
- }
-}
-
-func (v CodecV03) Decode(ctx context.Context, msg transport.Message) (*cloudevents.Event, error) {
- // only structured is supported as of v0.3
- switch v.inspectEncoding(ctx, msg) {
- case StructuredV03:
- return v.decodeStructured(ctx, cloudevents.CloudEventsVersionV03, msg)
- default:
- return nil, transport.NewErrMessageEncodingUnknown("v03", TransportName)
- }
-}
-
-func (v CodecV03) inspectEncoding(ctx context.Context, msg transport.Message) Encoding {
- version := msg.CloudEventsVersion()
- if version != cloudevents.CloudEventsVersionV03 {
- return Unknown
- }
- _, ok := msg.(*Message)
- if !ok {
- return Unknown
- }
- return StructuredV03
-}
diff --git a/v1/cloudevents/transport/nats/codec_v03_test.go b/v1/cloudevents/transport/nats/codec_v03_test.go
deleted file mode 100644
index 5fe64ebaf..000000000
--- a/v1/cloudevents/transport/nats/codec_v03_test.go
+++ /dev/null
@@ -1,266 +0,0 @@
-package nats_test
-
-import (
- "context"
- "net/url"
- "testing"
- "time"
-
- "github.com/google/go-cmp/cmp"
-
- "github.com/cloudevents/sdk-go/v1/cloudevents"
- "github.com/cloudevents/sdk-go/v1/cloudevents/transport/nats"
- "github.com/cloudevents/sdk-go/v1/cloudevents/types"
-)
-
-func TestCodecV03_Encode(t *testing.T) {
- now := types.Timestamp{Time: time.Now().UTC()}
- sourceUrl, _ := url.Parse("http://example.com/source")
- source := &types.URLRef{URL: *sourceUrl}
-
- schemaUrl, _ := url.Parse("http://example.com/schema")
- schema := &types.URLRef{URL: *schemaUrl}
-
- testCases := map[string]struct {
- codec nats.CodecV03
- event cloudevents.Event
- want *nats.Message
- wantErr error
- }{
- "simple v0.3 default": {
- codec: nats.CodecV03{},
- event: cloudevents.Event{
- Context: cloudevents.EventContextV03{
- Type: "com.example.test",
- Source: *source,
- ID: "ABC-123",
- }.AsV03(),
- },
- want: &nats.Message{
- Body: func() []byte {
- body := map[string]interface{}{
- "specversion": "0.3",
- "id": "ABC-123",
- "type": "com.example.test",
- "source": "http://example.com/source",
- }
- return toBytes(body)
- }(),
- },
- },
- "full v0.3 default": {
- codec: nats.CodecV03{},
- event: cloudevents.Event{
- Context: cloudevents.EventContextV03{
- ID: "ABC-123",
- Time: &now,
- Type: "com.example.test",
- SchemaURL: schema,
- DataContentType: cloudevents.StringOfApplicationJSON(),
- Source: *source,
- Extensions: map[string]interface{}{
- "test": "extended",
- },
- }.AsV03(),
- Data: map[string]interface{}{
- "hello": "world",
- },
- },
- want: &nats.Message{
- Body: func() []byte {
- body := map[string]interface{}{
- "specversion": "0.3",
- "datacontenttype": "application/json",
- "data": map[string]interface{}{
- "hello": "world",
- },
- "id": "ABC-123",
- "time": now,
- "type": "com.example.test",
- "test": "extended",
- "schemaurl": "http://example.com/schema",
- "source": "http://example.com/source",
- }
- return toBytes(body)
- }(),
- },
- },
- "simple v0.3 structured": {
- codec: nats.CodecV03{Encoding: nats.StructuredV03},
- event: cloudevents.Event{
- Context: cloudevents.EventContextV03{
- Type: "com.example.test",
- Source: *source,
- ID: "ABC-123",
- }.AsV03(),
- },
- want: &nats.Message{
- Body: func() []byte {
- body := map[string]interface{}{
- "specversion": "0.3",
- "id": "ABC-123",
- "type": "com.example.test",
- "source": "http://example.com/source",
- }
- return toBytes(body)
- }(),
- },
- },
- "full v0.3 structured": {
- codec: nats.CodecV03{Encoding: nats.StructuredV03},
- event: cloudevents.Event{
- Context: cloudevents.EventContextV03{
- ID: "ABC-123",
- Time: &now,
- Type: "com.example.test",
- SchemaURL: schema,
- DataContentType: cloudevents.StringOfApplicationJSON(),
- Source: *source,
- Extensions: map[string]interface{}{
- "test": "extended",
- },
- }.AsV03(),
- Data: map[string]interface{}{
- "hello": "world",
- },
- },
- want: &nats.Message{
- Body: func() []byte {
- body := map[string]interface{}{
- "specversion": "0.3",
- "datacontenttype": "application/json",
- "data": map[string]interface{}{
- "hello": "world",
- },
- "id": "ABC-123",
- "time": now,
- "type": "com.example.test",
- "test": "extended",
- "schemaurl": "http://example.com/schema",
- "source": "http://example.com/source",
- }
- return toBytes(body)
- }(),
- },
- },
- }
- for n, tc := range testCases {
- t.Run(n, func(t *testing.T) {
-
- got, err := tc.codec.Encode(context.TODO(), tc.event)
-
- if tc.wantErr != nil || err != nil {
- if diff := cmp.Diff(tc.wantErr, err); diff != "" {
- t.Errorf("unexpected error (-want, +got) = %v", diff)
- }
- return
- }
-
- if diff := cmp.Diff(tc.want, got); diff != "" {
-
- if msg, ok := got.(*nats.Message); ok {
- // It is hard to read the byte dump
- want := string(tc.want.Body)
- got := string(msg.Body)
- if diff := cmp.Diff(want, got); diff != "" {
- t.Errorf("unexpected message body (-want, +got) = %v", diff)
- return
- }
- }
-
- t.Errorf("unexpected message (-want, +got) = %v", diff)
- }
- })
- }
-}
-
-// TODO: figure out extensions for v0.3
-
-func TestCodecV03_Decode(t *testing.T) {
- now := types.Timestamp{Time: time.Now()}
- sourceUrl, _ := url.Parse("http://example.com/source")
- source := &types.URLRef{URL: *sourceUrl}
-
- schemaUrl, _ := url.Parse("http://example.com/schema")
- schema := &types.URLRef{URL: *schemaUrl}
-
- testCases := map[string]struct {
- codec nats.CodecV03
- msg *nats.Message
- want *cloudevents.Event
- wantErr error
- }{
- "simple v0.3 structured": {
- codec: nats.CodecV03{},
- msg: &nats.Message{
- Body: toBytes(map[string]interface{}{
- "specversion": "0.3",
- "id": "ABC-123",
- "type": "com.example.test",
- "source": "http://example.com/source",
- }),
- },
- want: &cloudevents.Event{
- Context: &cloudevents.EventContextV03{
- SpecVersion: cloudevents.CloudEventsVersionV03,
- Type: "com.example.test",
- Source: *source,
- ID: "ABC-123",
- },
- },
- },
- "full v0.3 structured": {
- codec: nats.CodecV03{},
- msg: &nats.Message{
- Body: toBytes(map[string]interface{}{
- "specversion": "0.3",
- "datacontenttype": "application/json",
- "data": map[string]interface{}{
- "hello": "world",
- },
- "id": "ABC-123",
- "time": now,
- "type": "com.example.test",
- "test": "extended",
- "schemaurl": "http://example.com/schema",
- "source": "http://example.com/source",
- }),
- },
- want: &cloudevents.Event{
- Context: &cloudevents.EventContextV03{
- SpecVersion: cloudevents.CloudEventsVersionV03,
- ID: "ABC-123",
- Time: &now,
- Type: "com.example.test",
- SchemaURL: schema,
- DataContentType: cloudevents.StringOfApplicationJSON(),
- Source: *source,
- Extensions: map[string]interface{}{
- "test": "extended",
- },
- },
- Data: toBytes(map[string]interface{}{
- "hello": "world",
- }),
- DataEncoded: true,
- },
- },
- }
- for n, tc := range testCases {
- t.Run(n, func(t *testing.T) {
-
- got, err := tc.codec.Decode(context.TODO(), tc.msg)
-
- if tc.wantErr != nil || err != nil {
- if diff := cmp.Diff(tc.wantErr, err); diff != "" {
- t.Errorf("unexpected error (-want, +got) = %v", diff)
- }
- return
- }
-
- if diff := cmp.Diff(tc.want, got); diff != "" {
- t.Errorf("unexpected event (-want, +got) = %v", diff)
- }
- })
- }
-}
diff --git a/v1/cloudevents/transport/nats/codec_v1.go b/v1/cloudevents/transport/nats/codec_v1.go
deleted file mode 100644
index 5f2a6a762..000000000
--- a/v1/cloudevents/transport/nats/codec_v1.go
+++ /dev/null
@@ -1,50 +0,0 @@
-package nats
-
-import (
- "context"
- "fmt"
-
- "github.com/cloudevents/sdk-go/v1/cloudevents"
- "github.com/cloudevents/sdk-go/v1/cloudevents/transport"
-)
-
-type CodecV1 struct {
- CodecStructured
-
- Encoding Encoding
-}
-
-var _ transport.Codec = (*CodecV1)(nil)
-
-func (v CodecV1) Encode(ctx context.Context, e cloudevents.Event) (transport.Message, error) {
- switch v.Encoding {
- case Default:
- fallthrough
- case StructuredV1:
- return v.encodeStructured(ctx, e)
- default:
- return nil, fmt.Errorf("unknown encoding: %d", v.Encoding)
- }
-}
-
-func (v CodecV1) Decode(ctx context.Context, msg transport.Message) (*cloudevents.Event, error) {
- // only structured is supported as of v0.3
- switch v.inspectEncoding(ctx, msg) {
- case StructuredV1:
- return v.decodeStructured(ctx, cloudevents.CloudEventsVersionV1, msg)
- default:
- return nil, transport.NewErrMessageEncodingUnknown("V1", TransportName)
- }
-}
-
-func (v CodecV1) inspectEncoding(ctx context.Context, msg transport.Message) Encoding {
- version := msg.CloudEventsVersion()
- if version != cloudevents.CloudEventsVersionV1 {
- return Unknown
- }
- _, ok := msg.(*Message)
- if !ok {
- return Unknown
- }
- return StructuredV1
-}
diff --git a/v1/cloudevents/transport/nats/codec_v1_test.go b/v1/cloudevents/transport/nats/codec_v1_test.go
deleted file mode 100644
index ba05403b3..000000000
--- a/v1/cloudevents/transport/nats/codec_v1_test.go
+++ /dev/null
@@ -1,264 +0,0 @@
-package nats_test
-
-import (
- "context"
- "net/url"
- "testing"
- "time"
-
- "github.com/google/go-cmp/cmp"
-
- "github.com/cloudevents/sdk-go/v1/cloudevents"
- "github.com/cloudevents/sdk-go/v1/cloudevents/transport/nats"
- "github.com/cloudevents/sdk-go/v1/cloudevents/types"
-)
-
-func TestCodecV1_Encode(t *testing.T) {
- now := types.Timestamp{Time: time.Now().UTC()}
- sourceUrl, _ := url.Parse("http://example.com/source")
- source := &types.URIRef{URL: *sourceUrl}
-
- schemaUrl, _ := url.Parse("http://example.com/schema")
- schema := &types.URI{URL: *schemaUrl}
-
- testCases := map[string]struct {
- codec nats.CodecV1
- event cloudevents.Event
- want *nats.Message
- wantErr error
- }{
- "simple v1.0 default": {
- codec: nats.CodecV1{},
- event: cloudevents.Event{
- Context: cloudevents.EventContextV1{
- Type: "com.example.test",
- Source: *source,
- ID: "ABC-123",
- }.AsV1(),
- },
- want: &nats.Message{
- Body: func() []byte {
- body := map[string]interface{}{
- "specversion": "1.0",
- "id": "ABC-123",
- "type": "com.example.test",
- "source": "http://example.com/source",
- }
- return toBytes(body)
- }(),
- },
- },
- "full v1.0 default": {
- codec: nats.CodecV1{},
- event: cloudevents.Event{
- Context: cloudevents.EventContextV1{
- ID: "ABC-123",
- Time: &now,
- Type: "com.example.test",
- DataSchema: schema,
- DataContentType: cloudevents.StringOfApplicationJSON(),
- Source: *source,
- Extensions: map[string]interface{}{
- "test": "extended",
- },
- }.AsV1(),
- Data: map[string]interface{}{
- "hello": "world",
- },
- },
- want: &nats.Message{
- Body: func() []byte {
- body := map[string]interface{}{
- "specversion": "1.0",
- "datacontenttype": "application/json",
- "data": map[string]interface{}{
- "hello": "world",
- },
- "id": "ABC-123",
- "time": now,
- "type": "com.example.test",
- "test": "extended",
- "dataschema": "http://example.com/schema",
- "source": "http://example.com/source",
- }
- return toBytes(body)
- }(),
- },
- },
- "simple v1.0 structured": {
- codec: nats.CodecV1{Encoding: nats.StructuredV1},
- event: cloudevents.Event{
- Context: cloudevents.EventContextV1{
- Type: "com.example.test",
- Source: *source,
- ID: "ABC-123",
- }.AsV1(),
- },
- want: &nats.Message{
- Body: func() []byte {
- body := map[string]interface{}{
- "specversion": "1.0",
- "id": "ABC-123",
- "type": "com.example.test",
- "source": "http://example.com/source",
- }
- return toBytes(body)
- }(),
- },
- },
- "full v1.0 structured": {
- codec: nats.CodecV1{Encoding: nats.StructuredV1},
- event: cloudevents.Event{
- Context: cloudevents.EventContextV1{
- ID: "ABC-123",
- Time: &now,
- Type: "com.example.test",
- DataSchema: schema,
- DataContentType: cloudevents.StringOfApplicationJSON(),
- Source: *source,
- Extensions: map[string]interface{}{
- "test": "extended",
- },
- }.AsV1(),
- Data: map[string]interface{}{
- "hello": "world",
- },
- },
- want: &nats.Message{
- Body: func() []byte {
- body := map[string]interface{}{
- "specversion": "1.0",
- "datacontenttype": "application/json",
- "data": map[string]interface{}{
- "hello": "world",
- },
- "id": "ABC-123",
- "time": now,
- "type": "com.example.test",
- "test": "extended",
- "dataschema": "http://example.com/schema",
- "source": "http://example.com/source",
- }
- return toBytes(body)
- }(),
- },
- },
- }
- for n, tc := range testCases {
- t.Run(n, func(t *testing.T) {
-
- got, err := tc.codec.Encode(context.TODO(), tc.event)
-
- if tc.wantErr != nil || err != nil {
- if diff := cmp.Diff(tc.wantErr, err); diff != "" {
- t.Errorf("unexpected error (-want, +got) = %v", diff)
- }
- return
- }
-
- if diff := cmp.Diff(tc.want, got); diff != "" {
-
- if msg, ok := got.(*nats.Message); ok {
- // It is hard to read the byte dump
- want := string(tc.want.Body)
- got := string(msg.Body)
- if diff := cmp.Diff(want, got); diff != "" {
- t.Errorf("unexpected message body (-want, +got) = %v", diff)
- return
- }
- }
-
- t.Errorf("unexpected message (-want, +got) = %v", diff)
- }
- })
- }
-}
-
-func TestCodecV1_Decode(t *testing.T) {
- now := types.Timestamp{Time: time.Now()}
- sourceUrl, _ := url.Parse("http://example.com/source")
- source := &types.URIRef{URL: *sourceUrl}
-
- schemaUrl, _ := url.Parse("http://example.com/schema")
- schema := &types.URI{URL: *schemaUrl}
-
- testCases := map[string]struct {
- codec nats.CodecV1
- msg *nats.Message
- want *cloudevents.Event
- wantErr error
- }{
- "simple v1.0 structured": {
- codec: nats.CodecV1{},
- msg: &nats.Message{
- Body: toBytes(map[string]interface{}{
- "specversion": "1.0",
- "id": "ABC-123",
- "type": "com.example.test",
- "source": "http://example.com/source",
- }),
- },
- want: &cloudevents.Event{
- Context: &cloudevents.EventContextV1{
- SpecVersion: cloudevents.CloudEventsVersionV1,
- Type: "com.example.test",
- Source: *source,
- ID: "ABC-123",
- },
- },
- },
- "full v1.0 structured": {
- codec: nats.CodecV1{},
- msg: &nats.Message{
- Body: toBytes(map[string]interface{}{
- "specversion": "1.0",
- "datacontenttype": "application/json",
- "data": map[string]interface{}{
- "hello": "world",
- },
- "id": "ABC-123",
- "time": now,
- "type": "com.example.test",
- "test": "extended",
- "dataschema": "http://example.com/schema",
- "source": "http://example.com/source",
- }),
- },
- want: &cloudevents.Event{
- Context: &cloudevents.EventContextV1{
- SpecVersion: cloudevents.CloudEventsVersionV1,
- ID: "ABC-123",
- Time: &now,
- Type: "com.example.test",
- DataSchema: schema,
- DataContentType: cloudevents.StringOfApplicationJSON(),
- Source: *source,
- Extensions: map[string]interface{}{
- "test": "extended",
- },
- },
- Data: toBytes(map[string]interface{}{
- "hello": "world",
- }),
- DataEncoded: true,
- },
- },
- }
- for n, tc := range testCases {
- t.Run(n, func(t *testing.T) {
-
- got, err := tc.codec.Decode(context.TODO(), tc.msg)
-
- if tc.wantErr != nil || err != nil {
- if diff := cmp.Diff(tc.wantErr, err); diff != "" {
- t.Errorf("unexpected error (-want, +got) = %v", diff)
- }
- return
- }
-
- if diff := cmp.Diff(tc.want, got); diff != "" {
- t.Errorf("unexpected event (-want, +got) = %v", diff)
- }
- })
- }
-}
diff --git a/v1/cloudevents/transport/nats/doc.go b/v1/cloudevents/transport/nats/doc.go
deleted file mode 100644
index a621ae0c1..000000000
--- a/v1/cloudevents/transport/nats/doc.go
+++ /dev/null
@@ -1,4 +0,0 @@
-/*
-Package nats implements the CloudEvent transport implementation using NATS.
-*/
-package nats
diff --git a/v1/cloudevents/transport/nats/encoding.go b/v1/cloudevents/transport/nats/encoding.go
deleted file mode 100644
index 2bbdca7c1..000000000
--- a/v1/cloudevents/transport/nats/encoding.go
+++ /dev/null
@@ -1,56 +0,0 @@
-package nats
-
-// Encoding to use for NATS transport.
-type Encoding int32
-
-const (
- // Default allows NATS transport implementation to pick.
- Default Encoding = iota
- // StructuredV02 is Structured CloudEvents spec v0.2.
- StructuredV02
- // StructuredV03 is Structured CloudEvents spec v0.3.
- StructuredV03
- // StructuredV1 is Structured CloudEvents spec v1.0.
- StructuredV1
- // Unknown is unknown.
- Unknown
-)
-
-// String pretty-prints the encoding as a string.
-func (e Encoding) String() string {
- switch e {
- case Default:
- return "Default Encoding " + e.Version()
-
- // Structured
- case StructuredV02, StructuredV03, StructuredV1:
- return "Structured Encoding " + e.Version()
-
- default:
- return "Unknown Encoding"
- }
-}
-
-// Version pretty-prints the encoding version as a string.
-func (e Encoding) Version() string {
- switch e {
-
- // Version 0.2
- case Default: // <-- Move when a new default is wanted.
- fallthrough
- case StructuredV02:
- return "v0.2"
-
- // Version 0.3
- case StructuredV03:
- return "v0.3"
-
- // Version 1.0
- case StructuredV1:
- return "v1.0"
-
- // Unknown
- default:
- return "Unknown"
- }
-}
diff --git a/v1/cloudevents/transport/nats/message.go b/v1/cloudevents/transport/nats/message.go
deleted file mode 100644
index 49c4ec84c..000000000
--- a/v1/cloudevents/transport/nats/message.go
+++ /dev/null
@@ -1,32 +0,0 @@
-package nats
-
-import (
- "encoding/json"
-
- "github.com/cloudevents/sdk-go/v1/cloudevents/transport"
-)
-
-// type check that this transport message impl matches the contract
-var _ transport.Message = (*Message)(nil)
-
-type Message struct {
- Body []byte
-}
-
-func (m Message) CloudEventsVersion() string {
- raw := make(map[string]json.RawMessage)
- if err := json.Unmarshal(m.Body, &raw); err != nil {
- return ""
- }
-
- // v0.2
- if v, ok := raw["specversion"]; ok {
- var version string
- if err := json.Unmarshal(v, &version); err != nil {
- return ""
- }
- return version
- }
-
- return ""
-}
diff --git a/v1/cloudevents/transport/nats/options.go b/v1/cloudevents/transport/nats/options.go
deleted file mode 100644
index 0010768dd..000000000
--- a/v1/cloudevents/transport/nats/options.go
+++ /dev/null
@@ -1,28 +0,0 @@
-package nats
-
-import (
- "github.com/nats-io/nats.go"
-)
-
-// Option is the function signature required to be considered an nats.Option.
-type Option func(*Transport) error
-
-// WithEncoding sets the encoding for NATS transport.
-func WithEncoding(encoding Encoding) Option {
- return func(t *Transport) error {
- t.Encoding = encoding
- return nil
- }
-}
-
-// WithConnOptions supplies NATS connection options that will be used when setting
-// up the internal NATS connection
-func WithConnOptions(opts ...nats.Option) Option {
- return func(t *Transport) error {
- for _, o := range opts {
- t.ConnOptions = append(t.ConnOptions, o)
- }
-
- return nil
- }
-}
diff --git a/v1/cloudevents/transport/nats/options_test.go b/v1/cloudevents/transport/nats/options_test.go
deleted file mode 100644
index d102fc714..000000000
--- a/v1/cloudevents/transport/nats/options_test.go
+++ /dev/null
@@ -1,83 +0,0 @@
-package nats
-
-import (
- "testing"
- "time"
-
- "github.com/google/go-cmp/cmp"
- "github.com/google/go-cmp/cmp/cmpopts"
- natsd "github.com/nats-io/nats-server/v2/server"
- "github.com/nats-io/nats.go"
-)
-
-func TestWithEncoding(t *testing.T) {
- testCases := map[string]struct {
- t *Transport
- encoding Encoding
- want *Transport
- wantErr string
- }{
- "valid encoding": {
- t: &Transport{},
- encoding: StructuredV03,
- want: &Transport{
- Encoding: StructuredV03,
- },
- },
- }
- for n, tc := range testCases {
- t.Run(n, func(t *testing.T) {
-
- err := tc.t.applyOptions(WithEncoding(tc.encoding))
-
- if tc.wantErr != "" || err != nil {
- var gotErr string
- if err != nil {
- gotErr = err.Error()
- }
- if diff := cmp.Diff(tc.wantErr, gotErr); diff != "" {
- t.Errorf("unexpected error (-want, +got) = %v", diff)
- }
- return
- }
-
- got := tc.t
-
- if diff := cmp.Diff(tc.want, got,
- cmpopts.IgnoreUnexported(Transport{})); diff != "" {
- t.Errorf("unexpected (-want, +got) = %v", diff)
- }
- })
- }
-}
-
-func TestWithConnOptions(t *testing.T) {
- opts := []nats.Option{
- nats.DontRandomize(),
- nats.NoEcho(),
- }
-
- srv, err := natsd.NewServer(&natsd.Options{Port: -1})
- if err != nil {
- t.Errorf("could not start nats server: %s", err)
- }
- go srv.Start()
- defer srv.Shutdown()
-
- if !srv.ReadyForConnections(10 * time.Second) {
- t.Errorf("nats server did not start")
- }
-
- tr, err := New(srv.Addr().String(), "testing", WithConnOptions(opts...))
- if err != nil {
- t.Errorf("connection failed: %s", err)
- }
-
- if !tr.Conn.Opts.NoRandomize {
- t.Errorf("NoRandomize option was not set")
- }
-
- if !tr.Conn.Opts.NoEcho {
- t.Errorf("NoEcho option was not set")
- }
-}
diff --git a/v1/cloudevents/transport/nats/transport.go b/v1/cloudevents/transport/nats/transport.go
deleted file mode 100644
index 218e2183d..000000000
--- a/v1/cloudevents/transport/nats/transport.go
+++ /dev/null
@@ -1,187 +0,0 @@
-package nats
-
-import (
- "context"
- "fmt"
-
- "github.com/cloudevents/sdk-go/v1/cloudevents"
- context2 "github.com/cloudevents/sdk-go/v1/cloudevents/context"
- "github.com/cloudevents/sdk-go/v1/cloudevents/transport"
- "github.com/nats-io/nats.go"
- "go.uber.org/zap"
-)
-
-// Transport adheres to transport.Transport.
-var _ transport.Transport = (*Transport)(nil)
-
-const (
- // TransportName is the name of this transport.
- TransportName = "NATS"
-)
-
-// Transport acts as both a NATS client and a NATS handler.
-type Transport struct {
- Encoding Encoding
- Conn *nats.Conn
- ConnOptions []nats.Option
- NatsURL string
- Subject string
-
- sub *nats.Subscription
-
- Receiver transport.Receiver
- // Converter is invoked if the incoming transport receives an undecodable
- // message.
- Converter transport.Converter
-
- codec transport.Codec
-}
-
-// New creates a new NATS transport.
-func New(natsURL, subject string, opts ...Option) (*Transport, error) {
- t := &Transport{
- Subject: subject,
- NatsURL: natsURL,
- ConnOptions: []nats.Option{},
- }
-
- err := t.applyOptions(opts...)
- if err != nil {
- return nil, err
- }
-
- err = t.connect()
- if err != nil {
- return nil, err
- }
-
- return t, nil
-}
-
-func (t *Transport) connect() error {
- var err error
-
- t.Conn, err = nats.Connect(t.NatsURL, t.ConnOptions...)
-
- return err
-}
-
-func (t *Transport) applyOptions(opts ...Option) error {
- for _, fn := range opts {
- if err := fn(t); err != nil {
- return err
- }
- }
- return nil
-}
-
-func (t *Transport) loadCodec() bool {
- if t.codec == nil {
- switch t.Encoding {
- case Default:
- t.codec = &Codec{}
- case StructuredV02:
- t.codec = &CodecV02{Encoding: t.Encoding}
- case StructuredV03:
- t.codec = &CodecV03{Encoding: t.Encoding}
- case StructuredV1:
- t.codec = &CodecV1{Encoding: t.Encoding}
- default:
- return false
- }
- }
- return true
-}
-
-// Send implements Transport.Send
-func (t *Transport) Send(ctx context.Context, event cloudevents.Event) (context.Context, *cloudevents.Event, error) {
- // TODO populate response context properly.
- if ok := t.loadCodec(); !ok {
- return ctx, nil, fmt.Errorf("unknown encoding set on transport: %d", t.Encoding)
- }
-
- msg, err := t.codec.Encode(ctx, event)
- if err != nil {
- return ctx, nil, err
- }
-
- if m, ok := msg.(*Message); ok {
- return ctx, nil, t.Conn.Publish(t.Subject, m.Body)
- }
-
- return ctx, nil, fmt.Errorf("failed to encode Event into a Message")
-}
-
-// SetReceiver implements Transport.SetReceiver
-func (t *Transport) SetReceiver(r transport.Receiver) {
- t.Receiver = r
-}
-
-// SetConverter implements Transport.SetConverter
-func (t *Transport) SetConverter(c transport.Converter) {
- t.Converter = c
-}
-
-// HasConverter implements Transport.HasConverter
-func (t *Transport) HasConverter() bool {
- return t.Converter != nil
-}
-
-// StartReceiver implements Transport.StartReceiver
-// NOTE: This is a blocking call.
-func (t *Transport) StartReceiver(ctx context.Context) (err error) {
- if t.Conn == nil {
- return fmt.Errorf("no active nats connection")
- }
- if t.sub != nil {
- return fmt.Errorf("already subscribed")
- }
- if ok := t.loadCodec(); !ok {
- return fmt.Errorf("unknown encoding set on transport: %d", t.Encoding)
- }
-
- // TODO: there could be more than one subscription. Might have to do a map
- // of subject to subscription.
-
- if t.Subject == "" {
- return fmt.Errorf("subject required for nats listen")
- }
-
- // Simple Async Subscriber
- t.sub, err = t.Conn.Subscribe(t.Subject, func(m *nats.Msg) {
- logger := context2.LoggerFrom(ctx)
- msg := &Message{
- Body: m.Data,
- }
- event, err := t.codec.Decode(ctx, msg)
- // If codec returns and error, try with the converter if it is set.
- if err != nil && t.HasConverter() {
- event, err = t.Converter.Convert(ctx, msg, err)
- }
- if err != nil {
- logger.Errorw("failed to decode message", zap.Error(err)) // TODO: create an error channel to pass this up
- return
- }
- // TODO: I do not know enough about NATS to implement reply.
- // For now, NATS does not support reply.
- if err := t.Receiver.Receive(context.TODO(), *event, nil); err != nil {
- logger.Warnw("nats receiver return err", zap.Error(err))
- }
- })
- defer func() {
- if t.sub != nil {
- err2 := t.sub.Unsubscribe()
- if err != nil {
- err = err2 // Set the returned error if not already set.
- }
- t.sub = nil
- }
- }()
- <-ctx.Done()
- return err
-}
-
-// HasTracePropagation implements Transport.HasTracePropagation
-func (t *Transport) HasTracePropagation() bool {
- return false
-}
diff --git a/v1/cloudevents/transport/pubsub/codec.go b/v1/cloudevents/transport/pubsub/codec.go
deleted file mode 100644
index b36864e92..000000000
--- a/v1/cloudevents/transport/pubsub/codec.go
+++ /dev/null
@@ -1,116 +0,0 @@
-package pubsub
-
-import (
- "context"
- "errors"
- "fmt"
- "sync"
-
- "github.com/cloudevents/sdk-go/v1/cloudevents"
- cecontext "github.com/cloudevents/sdk-go/v1/cloudevents/context"
- "github.com/cloudevents/sdk-go/v1/cloudevents/transport"
-)
-
-type Codec struct {
- Encoding Encoding
-
- // DefaultEncodingSelectionFn allows for encoding selection strategies to be injected.
- DefaultEncodingSelectionFn EncodingSelector
-
- v03 *CodecV03
- v1 *CodecV1
-
- _v03 sync.Once
- _v1 sync.Once
-}
-
-const (
- prefix = "ce-"
-)
-
-var _ transport.Codec = (*Codec)(nil)
-
-func (c *Codec) loadCodec(encoding Encoding) (transport.Codec, error) {
- switch encoding {
- case Default:
- fallthrough
- case BinaryV1, StructuredV1:
- c._v1.Do(func() {
- c.v1 = &CodecV1{DefaultEncoding: c.Encoding}
- })
- return c.v1, nil
- case BinaryV03, StructuredV03:
- c._v03.Do(func() {
- c.v03 = &CodecV03{DefaultEncoding: c.Encoding}
- })
- return c.v03, nil
- }
-
- return nil, fmt.Errorf("unknown encoding: %s", encoding)
-}
-
-func (c *Codec) Encode(ctx context.Context, e cloudevents.Event) (transport.Message, error) {
- encoding := c.Encoding
- if encoding == Default && c.DefaultEncodingSelectionFn != nil {
- encoding = c.DefaultEncodingSelectionFn(ctx, e)
- }
- codec, err := c.loadCodec(encoding)
- if err != nil {
- return nil, err
- }
- ctx = cecontext.WithEncoding(ctx, encoding.Name())
- return codec.Encode(ctx, e)
-}
-
-func (c *Codec) Decode(ctx context.Context, msg transport.Message) (*cloudevents.Event, error) {
- codec, err := c.loadCodec(c.inspectEncoding(ctx, msg))
- if err != nil {
- return nil, err
- }
- event, err := codec.Decode(ctx, msg)
- if err != nil {
- return nil, err
- }
- return c.convertEvent(event)
-}
-
-// Give the context back as the user expects
-func (c *Codec) convertEvent(event *cloudevents.Event) (*cloudevents.Event, error) {
- if event == nil {
- return nil, errors.New("event is nil, can not convert")
- }
-
- switch c.Encoding {
- case Default:
- return event, nil
- case BinaryV03, StructuredV03:
- ca := event.Context.AsV03()
- event.Context = ca
- return event, nil
- case BinaryV1, StructuredV1:
- ca := event.Context.AsV1()
- event.Context = ca
- return event, nil
- default:
- return nil, fmt.Errorf("unknown encoding: %s", c.Encoding)
- }
-}
-
-func (c *Codec) inspectEncoding(ctx context.Context, msg transport.Message) Encoding {
- // Try v1.0.
- _, _ = c.loadCodec(BinaryV1)
- encoding := c.v1.inspectEncoding(ctx, msg)
- if encoding != Unknown {
- return encoding
- }
-
- // Try v0.3.
- _, _ = c.loadCodec(BinaryV03)
- encoding = c.v03.inspectEncoding(ctx, msg)
- if encoding != Unknown {
- return encoding
- }
-
- // We do not understand the message encoding.
- return Unknown
-}
diff --git a/v1/cloudevents/transport/pubsub/codec_structured.go b/v1/cloudevents/transport/pubsub/codec_structured.go
deleted file mode 100644
index 0e62e5d9a..000000000
--- a/v1/cloudevents/transport/pubsub/codec_structured.go
+++ /dev/null
@@ -1,40 +0,0 @@
-package pubsub
-
-import (
- "context"
- "encoding/json"
- "fmt"
-
- "github.com/cloudevents/sdk-go/v1/cloudevents"
- "github.com/cloudevents/sdk-go/v1/cloudevents/transport"
-)
-
-// CodecStructured represents an structured http transport codec for all versions.
-// Intended to be used as a base class.
-type CodecStructured struct {
- Encoding Encoding
-}
-
-func (v CodecStructured) encodeStructured(ctx context.Context, e cloudevents.Event) (transport.Message, error) {
- data, err := json.Marshal(e)
- if err != nil {
- return nil, err
- }
-
- msg := &Message{
- Attributes: map[string]string{StructuredContentType: cloudevents.ApplicationCloudEventsJSON},
- Data: data,
- }
-
- return msg, nil
-}
-
-func (v CodecStructured) decodeStructured(ctx context.Context, version string, msg transport.Message) (*cloudevents.Event, error) {
- m, ok := msg.(*Message)
- if !ok {
- return nil, fmt.Errorf("failed to convert transport.Message to pubsub.Message")
- }
- event := cloudevents.New(version)
- err := json.Unmarshal(m.Data, &event)
- return &event, err
-}
diff --git a/v1/cloudevents/transport/pubsub/codec_test.go b/v1/cloudevents/transport/pubsub/codec_test.go
deleted file mode 100644
index fd8050dd0..000000000
--- a/v1/cloudevents/transport/pubsub/codec_test.go
+++ /dev/null
@@ -1,309 +0,0 @@
-package pubsub_test
-
-import (
- "context"
- "encoding/json"
- "fmt"
- "net/url"
- "testing"
- "time"
-
- "github.com/cloudevents/sdk-go/v1/cloudevents"
- "github.com/cloudevents/sdk-go/v1/cloudevents/transport/pubsub"
- "github.com/cloudevents/sdk-go/v1/cloudevents/types"
- "github.com/google/go-cmp/cmp"
-)
-
-const (
- prefix = "ce-"
-)
-
-func strptr(s string) *string {
- return &s
-}
-
-func TestCodecEncode(t *testing.T) {
- sourceUrl, _ := url.Parse("http://example.com/source")
- source := &types.URLRef{URL: *sourceUrl}
-
- testCases := map[string]struct {
- codec *pubsub.Codec
- event *cloudevents.Event
- want *pubsub.Message
- wantErr error
- }{
- "simple v03 structured": {
- codec: &pubsub.Codec{Encoding: pubsub.StructuredV03},
- event: &cloudevents.Event{
- Context: cloudevents.EventContextV03{
- Type: "com.example.test",
- Source: *source,
- ID: "ABC-123",
- Subject: strptr("a-subject"),
- }.AsV03(),
- },
- want: &pubsub.Message{
- Attributes: map[string]string{
- "Content-Type": cloudevents.ApplicationCloudEventsJSON,
- },
- Data: func() []byte {
- body := map[string]interface{}{
- "specversion": "0.3",
- "id": "ABC-123",
- "type": "com.example.test",
- "source": "http://example.com/source",
- "subject": "a-subject",
- }
- return toBytes(body)
- }(),
- },
- },
- "simple v03 binary": {
- codec: &pubsub.Codec{Encoding: pubsub.BinaryV03},
- event: &cloudevents.Event{
- Context: &cloudevents.EventContextV03{
- Type: "com.example.test",
- Source: *source,
- ID: "ABC-123",
- Subject: strptr("a-subject"),
- },
- },
- want: &pubsub.Message{
- Attributes: map[string]string{
- "ce-specversion": "0.3",
- "ce-id": "ABC-123",
- "ce-type": "com.example.test",
- "ce-source": "http://example.com/source",
- "ce-subject": "a-subject",
- "ce-datacontenttype": "application/json",
- },
- },
- },
- }
- for n, tc := range testCases {
- t.Run(n, func(t *testing.T) {
-
- got, err := tc.codec.Encode(context.TODO(), *tc.event)
-
- if tc.wantErr != nil || err != nil {
- if diff := cmp.Diff(tc.wantErr, err); diff != "" {
- t.Errorf("unexpected error (-want, +got) = %v", diff)
- }
- return
- }
-
- if diff := cmp.Diff(tc.want, got); diff != "" {
- if msg, ok := got.(*pubsub.Message); ok {
- // It is hard to read the byte dump
- want := string(tc.want.Data)
- got := string(msg.Data)
- if diff := cmp.Diff(want, got); diff != "" {
- t.Errorf("unexpected (-want, +got) = %v", diff)
- return
- }
- }
- t.Errorf("unexpected message (-want, +got) = %v", diff)
- }
- })
- }
-}
-
-func TestCodecDecode(t *testing.T) {
- sourceUrl, _ := url.Parse("http://example.com/source")
- source := &types.URLRef{URL: *sourceUrl}
-
- testCases := map[string]struct {
- codec *pubsub.Codec
- msg *pubsub.Message
- want *cloudevents.Event
- wantErr error
- }{
- "simple v3 structured": {
- codec: &pubsub.Codec{Encoding: pubsub.StructuredV03},
- msg: &pubsub.Message{
- Attributes: map[string]string{
- "Content-Type": cloudevents.ApplicationCloudEventsJSON,
- },
- Data: func() []byte {
- body := map[string]interface{}{
- "specversion": "0.3",
- "id": "ABC-123",
- "type": "com.example.test",
- "source": "http://example.com/source",
- "subject": "a-subject",
- }
- return toBytes(body)
- }(),
- },
- want: &cloudevents.Event{
- Context: &cloudevents.EventContextV03{
- SpecVersion: cloudevents.CloudEventsVersionV03,
- Type: "com.example.test",
- Source: *source,
- ID: "ABC-123",
- Subject: strptr("a-subject"),
- },
- },
- },
- }
- for n, tc := range testCases {
- t.Run(n, func(t *testing.T) {
-
- got, err := tc.codec.Decode(context.TODO(), tc.msg)
-
- if tc.wantErr != nil || err != nil {
- if diff := cmp.Diff(tc.wantErr, err); diff != "" {
- t.Errorf("unexpected error (-want, +got) = %v", diff)
- }
- return
- }
-
- if diff := cmp.Diff(tc.want, got); diff != "" {
- t.Errorf("unexpected event (-want, +got) = %v", diff)
- }
- })
- }
-}
-
-type DataExample struct {
- AnInt int `json:"a,omitempty" xml:"a,omitempty"`
- AString string `json:"b,omitempty" xml:"b,omitempty"`
- AnArray []string `json:"c,omitempty" xml:"c,omitempty"`
- AMap map[string]map[string]int `json:"d,omitempty" xml:"d,omitempty"`
- ATime *time.Time `json:"e,omitempty" xml:"e,omitempty"`
-}
-
-func TestCodecAsMiddleware(t *testing.T) {
- sourceUrl, _ := url.Parse("http://example.com/source")
- source := &types.URLRef{URL: *sourceUrl}
-
- for _, encoding := range []pubsub.Encoding{pubsub.StructuredV03} {
-
- testCases := map[string]struct {
- codec *pubsub.Codec
- event *cloudevents.Event
- want *cloudevents.Event
- wantErr error
- }{
- "simple data": {
- codec: &pubsub.Codec{Encoding: encoding},
- event: &cloudevents.Event{
- Context: cloudevents.EventContextV03{
- Type: "com.example.test",
- Source: *source,
- ID: "ABC-123",
- }.AsV03(),
- Data: map[string]string{
- "a": "apple",
- "b": "banana",
- },
- },
- want: &cloudevents.Event{
- Context: &cloudevents.EventContextV03{
- SpecVersion: cloudevents.CloudEventsVersionV03,
- Type: "com.example.test",
- Source: *source,
- ID: "ABC-123",
- },
- Data: map[string]interface{}{
- "a": "apple",
- "b": "banana",
- },
- DataEncoded: true,
- },
- },
- "struct data": {
- codec: &pubsub.Codec{Encoding: encoding},
- event: &cloudevents.Event{
- Context: cloudevents.EventContextV03{
- Type: "com.example.test",
- Source: *source,
- ID: "ABC-123",
- }.AsV03(),
- Data: DataExample{
- AnInt: 42,
- AString: "testing",
- },
- },
- want: &cloudevents.Event{
- Context: &cloudevents.EventContextV03{
- SpecVersion: cloudevents.CloudEventsVersionV03,
- Type: "com.example.test",
- Source: *source,
- ID: "ABC-123",
- },
- Data: &DataExample{
- AnInt: 42,
- AString: "testing",
- },
- DataEncoded: true,
- },
- },
- }
- for n, tc := range testCases {
- n = fmt.Sprintf("%s, %s", encoding, n)
- t.Run(n, func(t *testing.T) {
-
- msg1, err := tc.codec.Encode(context.TODO(), *tc.event)
- if err != nil {
- if diff := cmp.Diff(tc.wantErr, err); diff != "" {
- t.Errorf("unexpected error (-want, +got) = %v", diff)
- }
- return
- }
-
- midEvent, err := tc.codec.Decode(context.TODO(), msg1)
- if err != nil {
- if diff := cmp.Diff(tc.wantErr, err); diff != "" {
- t.Errorf("unexpected error (-want, +got) = %v", diff)
- }
- return
- }
-
- msg2, err := tc.codec.Encode(context.TODO(), *midEvent)
- if err != nil {
- if diff := cmp.Diff(tc.wantErr, err); diff != "" {
- t.Errorf("unexpected error (-want, +got) = %v", diff)
- }
- return
- }
-
- got, err := tc.codec.Decode(context.TODO(), msg2)
- if err != nil {
- if diff := cmp.Diff(tc.wantErr, err); diff != "" {
- t.Errorf("unexpected error (-want, +got) = %v", diff)
- }
- return
- }
-
- if tc.event.Data != nil {
- // We have to be pretty tricky in the test to get the correct type.
- data, _ := types.Allocate(tc.want.Data)
- err := got.DataAs(&data)
- if err != nil {
- if diff := cmp.Diff(tc.wantErr, err); diff != "" {
- t.Errorf("unexpected error (-want, +got) = %v", diff)
- }
- return
- }
- got.Data = data
- }
-
- ctxv3 := got.Context.AsV03()
- got.Context = ctxv3
-
- if diff := cmp.Diff(tc.want, got); diff != "" {
- t.Errorf("unexpected event (-want, +got) = %v", diff)
- }
- })
- }
- }
-}
-
-func toBytes(body map[string]interface{}) []byte {
- b, err := json.Marshal(body)
- if err != nil {
- return []byte(fmt.Sprintf(`{"error":%q}`, err.Error()))
- }
- return b
-}
diff --git a/v1/cloudevents/transport/pubsub/codec_v03.go b/v1/cloudevents/transport/pubsub/codec_v03.go
deleted file mode 100644
index 8d41fb12a..000000000
--- a/v1/cloudevents/transport/pubsub/codec_v03.go
+++ /dev/null
@@ -1,286 +0,0 @@
-package pubsub
-
-import (
- "context"
- "encoding/json"
- "fmt"
- "strings"
-
- "github.com/cloudevents/sdk-go/v1/cloudevents"
- cecontext "github.com/cloudevents/sdk-go/v1/cloudevents/context"
- "github.com/cloudevents/sdk-go/v1/cloudevents/transport"
- "github.com/cloudevents/sdk-go/v1/cloudevents/types"
-)
-
-const (
- StructuredContentType = "Content-Type"
-)
-
-type CodecV03 struct {
- CodecStructured
-
- DefaultEncoding Encoding
-}
-
-var _ transport.Codec = (*CodecV03)(nil)
-
-func (v CodecV03) Encode(ctx context.Context, e cloudevents.Event) (transport.Message, error) {
- encoding := v.DefaultEncoding
- strEnc := cecontext.EncodingFrom(ctx)
- if strEnc != "" {
- switch strEnc {
- case Binary:
- encoding = BinaryV03
- case Structured:
- encoding = StructuredV03
- }
- }
- switch encoding {
- case Default:
- fallthrough
- case StructuredV03:
- return v.encodeStructured(ctx, e)
- case BinaryV03:
- return v.encodeBinary(ctx, e)
- default:
- return nil, fmt.Errorf("unknown encoding: %d", encoding)
- }
-}
-
-func (v CodecV03) Decode(ctx context.Context, msg transport.Message) (*cloudevents.Event, error) {
- // only structured is supported as of v0.3
- switch v.inspectEncoding(ctx, msg) {
- case StructuredV03:
- return v.decodeStructured(ctx, cloudevents.CloudEventsVersionV03, msg)
- case BinaryV03:
- event := cloudevents.New(cloudevents.CloudEventsVersionV03)
- return v.decodeBinary(ctx, msg, &event)
- default:
- return nil, transport.NewErrMessageEncodingUnknown("v03", TransportName)
- }
-}
-
-func (v CodecV03) inspectEncoding(ctx context.Context, msg transport.Message) Encoding {
- version := msg.CloudEventsVersion()
- if version != cloudevents.CloudEventsVersionV03 {
- return Unknown
- }
- m, ok := msg.(*Message)
- if !ok {
- return Unknown
- }
- if m.Attributes[StructuredContentType] == cloudevents.ApplicationCloudEventsJSON {
- return StructuredV03
- }
- return BinaryV03
-}
-
-func (v CodecV03) encodeBinary(ctx context.Context, e cloudevents.Event) (transport.Message, error) {
- attributes, err := v.toAttributes(e)
- if err != nil {
- return nil, err
- }
- data, err := e.DataBytes()
- if err != nil {
- return nil, err
- }
-
- msg := &Message{
- Attributes: attributes,
- Data: data,
- }
-
- return msg, nil
-}
-
-func (v CodecV03) toAttributes(e cloudevents.Event) (map[string]string, error) {
- a := make(map[string]string)
- a[prefix+"specversion"] = e.SpecVersion()
- a[prefix+"type"] = e.Type()
- a[prefix+"source"] = e.Source()
- a[prefix+"id"] = e.ID()
- if !e.Time().IsZero() {
- t := types.Timestamp{Time: e.Time()} // TODO: change e.Time() to return string so I don't have to do this.
- a[prefix+"time"] = t.String()
- }
- if e.DataSchema() != "" {
- a[prefix+"schemaurl"] = e.DataSchema()
- }
-
- if e.DataContentType() != "" {
- a[prefix+"datacontenttype"] = e.DataContentType()
- } else {
- a[prefix+"datacontenttype"] = cloudevents.ApplicationJSON
- }
-
- if e.Subject() != "" {
- a[prefix+"subject"] = e.Subject()
- }
-
- if e.DeprecatedDataContentEncoding() != "" {
- a[prefix+"datacontentencoding"] = e.DeprecatedDataContentEncoding()
- }
-
- for k, v := range e.Extensions() {
- if mapVal, ok := v.(map[string]interface{}); ok {
- for subkey, subval := range mapVal {
- encoded, err := json.Marshal(subval)
- if err != nil {
- return nil, err
- }
- a[prefix+k+"-"+subkey] = string(encoded)
- }
- continue
- }
- if s, ok := v.(string); ok {
- a[prefix+k] = s
- continue
- }
- encoded, err := json.Marshal(v)
- if err != nil {
- return nil, err
- }
- a[prefix+k] = string(encoded)
- }
-
- return a, nil
-}
-
-func (v CodecV03) decodeBinary(ctx context.Context, msg transport.Message, event *cloudevents.Event) (*cloudevents.Event, error) {
- m, ok := msg.(*Message)
- if !ok {
- return nil, fmt.Errorf("failed to convert transport.Message to pubsub.Message")
- }
- err := v.fromAttributes(m.Attributes, event)
- if err != nil {
- return nil, err
- }
- var data interface{}
- if len(m.Data) > 0 {
- data = m.Data
- }
- event.Data = data
- event.DataEncoded = true
- return event, nil
-}
-
-func (v CodecV03) fromAttributes(a map[string]string, event *cloudevents.Event) error {
- // Normalize attributes.
- for k, v := range a {
- ck := strings.ToLower(k)
- if k != ck {
- delete(a, k)
- a[ck] = v
- }
- }
-
- ec := event.Context
-
- if sv := a[prefix+"specversion"]; sv != "" {
- if err := ec.SetSpecVersion(sv); err != nil {
- return err
- }
- }
- delete(a, prefix+"specversion")
-
- if id := a[prefix+"id"]; id != "" {
- if err := ec.SetID(id); err != nil {
- return err
- }
- }
- delete(a, prefix+"id")
-
- if t := a[prefix+"type"]; t != "" {
- if err := ec.SetType(t); err != nil {
- return err
- }
- }
- delete(a, prefix+"type")
-
- if s := a[prefix+"source"]; s != "" {
- if err := ec.SetSource(s); err != nil {
- return err
- }
- }
- delete(a, prefix+"source")
-
- if t := a[prefix+"time"]; t != "" {
- if timestamp, err := types.ParseTimestamp(t); err != nil {
- return err
- } else if err := ec.SetTime(timestamp.Time); err != nil {
- return err
- }
- }
- delete(a, prefix+"time")
-
- if s := a[prefix+"schemaurl"]; s != "" {
- if err := ec.SetDataSchema(s); err != nil {
- return err
- }
- }
- delete(a, prefix+"schemaurl")
-
- if s := a[prefix+"subject"]; s != "" {
- if err := ec.SetSubject(s); err != nil {
- return err
- }
- }
- delete(a, prefix+"subject")
-
- if s := a[prefix+"datacontenttype"]; s != "" {
- if err := ec.SetDataContentType(s); err != nil {
- return err
- }
- }
- delete(a, prefix+"datacontenttype")
-
- if s := a[prefix+"datacontentencoding"]; s != "" {
- if err := ec.DeprecatedSetDataContentEncoding(s); err != nil {
- return err
- }
- }
- delete(a, prefix+"datacontentencoding")
-
- // At this point, we have deleted all the known headers.
- // Everything left is assumed to be an extension.
-
- extensions := make(map[string]interface{})
- for k, v := range a {
- if len(k) > len(prefix) && strings.EqualFold(k[:len(prefix)], prefix) {
- ak := strings.ToLower(k[len(prefix):])
- if i := strings.Index(ak, "-"); i > 0 {
- // attrib-key
- attrib := ak[:i]
- key := ak[(i + 1):]
- if xv, ok := extensions[attrib]; ok {
- if m, ok := xv.(map[string]interface{}); ok {
- m[key] = v
- continue
- }
- // TODO: revisit how we want to bubble errors up.
- return fmt.Errorf("failed to process map type extension")
- } else {
- m := make(map[string]interface{})
- m[key] = v
- extensions[attrib] = m
- }
- } else {
- // key
- var tmp interface{}
- if err := json.Unmarshal([]byte(v), &tmp); err == nil {
- extensions[ak] = tmp
- } else {
- // If we can't unmarshal the data, treat it as a string.
- extensions[ak] = v
- }
- }
- }
- }
- event.Context = ec
- if len(extensions) > 0 {
- for k, v := range extensions {
- event.SetExtension(k, v)
- }
- }
- return nil
-}
diff --git a/v1/cloudevents/transport/pubsub/codec_v03_test.go b/v1/cloudevents/transport/pubsub/codec_v03_test.go
deleted file mode 100644
index 6a6ce026e..000000000
--- a/v1/cloudevents/transport/pubsub/codec_v03_test.go
+++ /dev/null
@@ -1,283 +0,0 @@
-package pubsub_test
-
-import (
- "context"
- "net/url"
- "testing"
- "time"
-
- "github.com/cloudevents/sdk-go/v1/cloudevents"
- "github.com/cloudevents/sdk-go/v1/cloudevents/transport/pubsub"
- "github.com/cloudevents/sdk-go/v1/cloudevents/types"
- "github.com/google/go-cmp/cmp"
-)
-
-func TestCodecV03_Encode(t *testing.T) {
- now := types.Timestamp{Time: time.Now().UTC()}
- sourceUrl, _ := url.Parse("http://example.com/source")
- source := &types.URLRef{URL: *sourceUrl}
-
- schemaUrl, _ := url.Parse("http://example.com/schema")
- schema := &types.URLRef{URL: *schemaUrl}
-
- testCases := map[string]struct {
- codec pubsub.CodecV03
- event cloudevents.Event
- want *pubsub.Message
- wantErr error
- }{
- "simple v0.3 default": {
- codec: pubsub.CodecV03{},
- event: cloudevents.Event{
- Context: cloudevents.EventContextV03{
- Type: "com.example.test",
- Source: *source,
- ID: "ABC-123",
- }.AsV03(),
- },
- want: &pubsub.Message{
- Attributes: map[string]string{
- "Content-Type": cloudevents.ApplicationCloudEventsJSON,
- },
- Data: func() []byte {
- body := map[string]interface{}{
- "specversion": "0.3",
- "id": "ABC-123",
- "type": "com.example.test",
- "source": "http://example.com/source",
- }
- return toBytes(body)
- }(),
- },
- },
- "full v0.3 default": {
- codec: pubsub.CodecV03{},
- event: cloudevents.Event{
- Context: cloudevents.EventContextV03{
- ID: "ABC-123",
- Time: &now,
- Type: "com.example.test",
- SchemaURL: schema,
- DataContentType: cloudevents.StringOfApplicationJSON(),
- Source: *source,
- Extensions: map[string]interface{}{
- "test": "extended",
- },
- }.AsV03(),
- Data: map[string]interface{}{
- "hello": "world",
- },
- },
- want: &pubsub.Message{
- Attributes: map[string]string{
- "Content-Type": cloudevents.ApplicationCloudEventsJSON,
- },
- Data: func() []byte {
- body := map[string]interface{}{
- "specversion": "0.3",
- "datacontenttype": "application/json",
- "data": map[string]interface{}{
- "hello": "world",
- },
- "id": "ABC-123",
- "time": now,
- "type": "com.example.test",
- "test": "extended",
- "schemaurl": "http://example.com/schema",
- "source": "http://example.com/source",
- }
- return toBytes(body)
- }(),
- },
- },
- "simple v0.3 structured": {
- codec: pubsub.CodecV03{DefaultEncoding: pubsub.StructuredV03},
- event: cloudevents.Event{
- Context: cloudevents.EventContextV03{
- Type: "com.example.test",
- Source: *source,
- ID: "ABC-123",
- }.AsV03(),
- },
- want: &pubsub.Message{
- Attributes: map[string]string{
- "Content-Type": cloudevents.ApplicationCloudEventsJSON,
- },
- Data: func() []byte {
- body := map[string]interface{}{
- "specversion": "0.3",
- "id": "ABC-123",
- "type": "com.example.test",
- "source": "http://example.com/source",
- }
- return toBytes(body)
- }(),
- },
- },
- "full v0.3 structured": {
- codec: pubsub.CodecV03{DefaultEncoding: pubsub.StructuredV03},
- event: cloudevents.Event{
- Context: cloudevents.EventContextV03{
- ID: "ABC-123",
- Time: &now,
- Type: "com.example.test",
- SchemaURL: schema,
- DataContentType: cloudevents.StringOfApplicationJSON(),
- Source: *source,
- Extensions: map[string]interface{}{
- "test": "extended",
- },
- }.AsV03(),
- Data: map[string]interface{}{
- "hello": "world",
- },
- },
- want: &pubsub.Message{
- Attributes: map[string]string{
- "Content-Type": cloudevents.ApplicationCloudEventsJSON,
- },
- Data: func() []byte {
- body := map[string]interface{}{
- "specversion": "0.3",
- "datacontenttype": "application/json",
- "data": map[string]interface{}{
- "hello": "world",
- },
- "id": "ABC-123",
- "time": now,
- "type": "com.example.test",
- "test": "extended",
- "schemaurl": "http://example.com/schema",
- "source": "http://example.com/source",
- }
- return toBytes(body)
- }(),
- },
- },
- }
- for n, tc := range testCases {
- t.Run(n, func(t *testing.T) {
-
- got, err := tc.codec.Encode(context.TODO(), tc.event)
-
- if tc.wantErr != nil || err != nil {
- if diff := cmp.Diff(tc.wantErr, err); diff != "" {
- t.Errorf("unexpected error (-want, +got) = %v", diff)
- }
- return
- }
-
- if diff := cmp.Diff(tc.want, got); diff != "" {
-
- if msg, ok := got.(*pubsub.Message); ok {
- // It is hard to read the byte dump
- want := string(tc.want.Data)
- got := string(msg.Data)
- if diff := cmp.Diff(want, got); diff != "" {
- t.Errorf("unexpected message body (-want, +got) = %v", diff)
- return
- }
- }
-
- t.Errorf("unexpected message (-want, +got) = %v", diff)
- }
- })
- }
-}
-
-// TODO: figure out extensions for v0.3
-
-func TestCodecV03_Decode(t *testing.T) {
- now := types.Timestamp{Time: time.Now()}
- sourceUrl, _ := url.Parse("http://example.com/source")
- source := &types.URLRef{URL: *sourceUrl}
-
- schemaUrl, _ := url.Parse("http://example.com/schema")
- schema := &types.URLRef{URL: *schemaUrl}
-
- testCases := map[string]struct {
- codec pubsub.CodecV03
- msg *pubsub.Message
- want *cloudevents.Event
- wantErr error
- }{
- "simple v0.3 structured": {
- codec: pubsub.CodecV03{},
- msg: &pubsub.Message{
- Attributes: map[string]string{
- "Content-Type": cloudevents.ApplicationCloudEventsJSON,
- },
- Data: toBytes(map[string]interface{}{
- "specversion": "0.3",
- "id": "ABC-123",
- "type": "com.example.test",
- "source": "http://example.com/source",
- }),
- },
- want: &cloudevents.Event{
- Context: &cloudevents.EventContextV03{
- SpecVersion: cloudevents.CloudEventsVersionV03,
- Type: "com.example.test",
- Source: *source,
- ID: "ABC-123",
- },
- },
- },
- "full v0.3 structured": {
- codec: pubsub.CodecV03{},
- msg: &pubsub.Message{
- Attributes: map[string]string{
- "Content-Type": cloudevents.ApplicationCloudEventsJSON,
- },
- Data: toBytes(map[string]interface{}{
- "specversion": "0.3",
- "datacontenttype": "application/json",
- "data": map[string]interface{}{
- "hello": "world",
- },
- "id": "ABC-123",
- "time": now,
- "type": "com.example.test",
- "test": "extended",
- "schemaurl": "http://example.com/schema",
- "source": "http://example.com/source",
- }),
- },
- want: &cloudevents.Event{
- Context: &cloudevents.EventContextV03{
- SpecVersion: cloudevents.CloudEventsVersionV03,
- ID: "ABC-123",
- Time: &now,
- Type: "com.example.test",
- SchemaURL: schema,
- DataContentType: cloudevents.StringOfApplicationJSON(),
- Source: *source,
- Extensions: map[string]interface{}{
- "test": "extended",
- },
- },
- Data: toBytes(map[string]interface{}{
- "hello": "world",
- }),
- DataEncoded: true,
- },
- },
- }
- for n, tc := range testCases {
- t.Run(n, func(t *testing.T) {
-
- got, err := tc.codec.Decode(context.TODO(), tc.msg)
-
- if tc.wantErr != nil || err != nil {
- if diff := cmp.Diff(tc.wantErr, err); diff != "" {
- t.Errorf("unexpected error (-want, +got) = %v", diff)
- }
- return
- }
-
- if diff := cmp.Diff(tc.want, got); diff != "" {
- t.Errorf("unexpected event (-want, +got) = %v", diff)
- }
- })
- }
-}
diff --git a/v1/cloudevents/transport/pubsub/codec_v1.go b/v1/cloudevents/transport/pubsub/codec_v1.go
deleted file mode 100644
index 092bf6d6d..000000000
--- a/v1/cloudevents/transport/pubsub/codec_v1.go
+++ /dev/null
@@ -1,242 +0,0 @@
-package pubsub
-
-import (
- "context"
- "fmt"
- "strings"
-
- "github.com/cloudevents/sdk-go/v1/cloudevents"
- cecontext "github.com/cloudevents/sdk-go/v1/cloudevents/context"
- "github.com/cloudevents/sdk-go/v1/cloudevents/transport"
- "github.com/cloudevents/sdk-go/v1/cloudevents/types"
-)
-
-type CodecV1 struct {
- CodecStructured
-
- DefaultEncoding Encoding
-}
-
-var _ transport.Codec = (*CodecV1)(nil)
-
-func (v CodecV1) Encode(ctx context.Context, e cloudevents.Event) (transport.Message, error) {
- encoding := v.DefaultEncoding
- strEnc := cecontext.EncodingFrom(ctx)
- if strEnc != "" {
- switch strEnc {
- case Binary:
- encoding = BinaryV1
- case Structured:
- encoding = StructuredV1
- }
- }
- switch encoding {
- case Default:
- fallthrough
- case StructuredV1:
- return v.encodeStructured(ctx, e)
- case BinaryV1:
- return v.encodeBinary(ctx, e)
- default:
- return nil, fmt.Errorf("unknown encoding: %d", v.Encoding)
- }
-}
-
-func (v CodecV1) Decode(ctx context.Context, msg transport.Message) (*cloudevents.Event, error) {
- // only structured is supported as of v0.3
- switch v.inspectEncoding(ctx, msg) {
- case StructuredV1:
- return v.decodeStructured(ctx, cloudevents.CloudEventsVersionV1, msg)
- case BinaryV1:
- event := cloudevents.New(cloudevents.CloudEventsVersionV1)
- return v.decodeBinary(ctx, msg, &event)
- default:
- return nil, transport.NewErrMessageEncodingUnknown("V1", TransportName)
- }
-}
-
-func (v CodecV1) inspectEncoding(ctx context.Context, msg transport.Message) Encoding {
- version := msg.CloudEventsVersion()
- if version != cloudevents.CloudEventsVersionV1 {
- return Unknown
- }
- m, ok := msg.(*Message)
- if !ok {
- return Unknown
- }
- if m.Attributes[StructuredContentType] == cloudevents.ApplicationCloudEventsJSON {
- return StructuredV1
- }
- return BinaryV1
-}
-
-func (v CodecV1) encodeBinary(ctx context.Context, e cloudevents.Event) (transport.Message, error) {
- attributes, err := v.toAttributes(e)
- if err != nil {
- return nil, err
- }
- data, err := e.DataBytes()
- if err != nil {
- return nil, err
- }
-
- msg := &Message{
- Attributes: attributes,
- Data: data,
- }
-
- return msg, nil
-}
-
-func (v CodecV1) toAttributes(e cloudevents.Event) (map[string]string, error) {
- a := make(map[string]string)
- a[prefix+"specversion"] = e.SpecVersion()
- a[prefix+"type"] = e.Type()
- a[prefix+"source"] = e.Source()
- a[prefix+"id"] = e.ID()
- if !e.Time().IsZero() {
- t := types.Timestamp{Time: e.Time()} // TODO: change e.Time() to return string so I don't have to do this.
- a[prefix+"time"] = t.String()
- }
- if e.DataSchema() != "" {
- a[prefix+"dataschema"] = e.DataSchema()
- }
-
- if e.DataContentType() != "" {
- a[prefix+"datacontenttype"] = e.DataContentType()
- } else {
- a[prefix+"datacontenttype"] = cloudevents.ApplicationJSON
- }
-
- if e.Subject() != "" {
- a[prefix+"subject"] = e.Subject()
- }
-
- if e.DeprecatedDataContentEncoding() != "" {
- a[prefix+"datacontentencoding"] = e.DeprecatedDataContentEncoding()
- }
-
- for k, v := range e.Extensions() {
- k = strings.ToLower(k)
- cstr, err := types.Format(v)
- if err != nil {
- return a, err
- }
- a[prefix+k] = cstr
- }
- return a, nil
-}
-
-func (v CodecV1) decodeBinary(ctx context.Context, msg transport.Message, event *cloudevents.Event) (*cloudevents.Event, error) {
- m, ok := msg.(*Message)
- if !ok {
- return nil, fmt.Errorf("failed to convert transport.Message to pubsub.Message")
- }
- err := v.fromAttributes(m.Attributes, event)
- if err != nil {
- return nil, err
- }
- var data interface{}
- if len(m.Data) > 0 {
- data = m.Data
- }
- event.Data = data
- event.DataEncoded = true
- return event, nil
-}
-
-func (v CodecV1) fromAttributes(a map[string]string, event *cloudevents.Event) error {
- // Normalize attributes.
- for k, v := range a {
- ck := strings.ToLower(k)
- if k != ck {
- delete(a, k)
- a[ck] = v
- }
- }
-
- ec := event.Context
-
- if sv := a[prefix+"specversion"]; sv != "" {
- if err := ec.SetSpecVersion(sv); err != nil {
- return err
- }
- }
- delete(a, prefix+"specversion")
-
- if id := a[prefix+"id"]; id != "" {
- if err := ec.SetID(id); err != nil {
- return err
- }
- }
- delete(a, prefix+"id")
-
- if t := a[prefix+"type"]; t != "" {
- if err := ec.SetType(t); err != nil {
- return err
- }
- }
- delete(a, prefix+"type")
-
- if s := a[prefix+"source"]; s != "" {
- if err := ec.SetSource(s); err != nil {
- return err
- }
- }
- delete(a, prefix+"source")
-
- if t := a[prefix+"time"]; t != "" {
- if timestamp, err := types.ParseTimestamp(t); err != nil {
- return err
- } else if err := ec.SetTime(timestamp.Time); err != nil {
- return err
- }
- }
- delete(a, prefix+"time")
-
- if s := a[prefix+"dataschema"]; s != "" {
- if err := ec.SetDataSchema(s); err != nil {
- return err
- }
- }
- delete(a, prefix+"dataschema")
-
- if s := a[prefix+"subject"]; s != "" {
- if err := ec.SetSubject(s); err != nil {
- return err
- }
- }
- delete(a, prefix+"subject")
-
- if s := a[prefix+"datacontenttype"]; s != "" {
- if err := ec.SetDataContentType(s); err != nil {
- return err
- }
- }
- delete(a, prefix+"datacontenttype")
-
- if s := a[prefix+"datacontentencoding"]; s != "" {
- if err := ec.DeprecatedSetDataContentEncoding(s); err != nil {
- return err
- }
- }
- delete(a, prefix+"datacontentencoding")
-
- // At this point, we have deleted all the known headers.
- // Everything left is assumed to be an extension.
-
- extensions := make(map[string]interface{})
- for k, v := range a {
- if len(k) > len(prefix) && strings.EqualFold(k[:len(prefix)], prefix) {
- ak := strings.ToLower(k[len(prefix):])
- extensions[ak] = v
- }
- }
- event.Context = ec
- if len(extensions) > 0 {
- for k, v := range extensions {
- event.SetExtension(k, v)
- }
- }
- return nil
-}
diff --git a/v1/cloudevents/transport/pubsub/codec_v1_test.go b/v1/cloudevents/transport/pubsub/codec_v1_test.go
deleted file mode 100644
index b43b31496..000000000
--- a/v1/cloudevents/transport/pubsub/codec_v1_test.go
+++ /dev/null
@@ -1,362 +0,0 @@
-package pubsub_test
-
-import (
- "context"
- "net/url"
- "testing"
- "time"
-
- "github.com/google/go-cmp/cmp"
-
- "github.com/cloudevents/sdk-go/v1/cloudevents"
- "github.com/cloudevents/sdk-go/v1/cloudevents/transport/pubsub"
- "github.com/cloudevents/sdk-go/v1/cloudevents/types"
-)
-
-func TestCodecV1_Encode(t *testing.T) {
- now := types.Timestamp{Time: time.Now().UTC()}
- sourceUrl, _ := url.Parse("http://example.com/source")
- source := &types.URIRef{URL: *sourceUrl}
-
- schemaUrl, _ := url.Parse("http://example.com/schema")
- schema := &types.URI{URL: *schemaUrl}
-
- testCases := map[string]struct {
- codec pubsub.CodecV1
- event cloudevents.Event
- want *pubsub.Message
- wantErr error
- }{
- "simple v1.0 default": {
- codec: pubsub.CodecV1{},
- event: cloudevents.Event{
- Context: cloudevents.EventContextV1{
- Type: "com.example.test",
- Source: *source,
- ID: "ABC-123",
- }.AsV1(),
- },
- want: &pubsub.Message{
- Attributes: map[string]string{
- "Content-Type": cloudevents.ApplicationCloudEventsJSON,
- },
- Data: func() []byte {
- body := map[string]interface{}{
- "specversion": "1.0",
- "id": "ABC-123",
- "type": "com.example.test",
- "source": "http://example.com/source",
- }
- return toBytes(body)
- }(),
- },
- },
- "full v1.0 default": {
- codec: pubsub.CodecV1{},
- event: cloudevents.Event{
- Context: cloudevents.EventContextV1{
- ID: "ABC-123",
- Time: &now,
- Type: "com.example.test",
- DataSchema: schema,
- DataContentType: cloudevents.StringOfApplicationJSON(),
- Source: *source,
- Extensions: map[string]interface{}{
- "test": "extended",
- "generation": "1579743478182200",
- },
- }.AsV1(),
- Data: map[string]interface{}{
- "hello": "world",
- },
- },
- want: &pubsub.Message{
- Attributes: map[string]string{
- "Content-Type": cloudevents.ApplicationCloudEventsJSON,
- },
- Data: func() []byte {
- body := map[string]interface{}{
- "specversion": "1.0",
- "datacontenttype": "application/json",
- "data": map[string]interface{}{
- "hello": "world",
- },
- "id": "ABC-123",
- "time": now,
- "type": "com.example.test",
- "test": "extended",
- "generation": "1579743478182200",
- "dataschema": "http://example.com/schema",
- "source": "http://example.com/source",
- }
- return toBytes(body)
- }(),
- },
- },
- "full v1.0 binary": {
- codec: pubsub.CodecV1{
- DefaultEncoding: pubsub.BinaryV1,
- },
- event: cloudevents.Event{
- Context: cloudevents.EventContextV1{
- ID: "ABC-123",
- Time: &now,
- Type: "com.example.test",
- DataSchema: schema,
- Source: *source,
- Extensions: map[string]interface{}{
- "test": "extended",
- "generation": "1579743478182200",
- },
- }.AsV1(),
- Data: map[string]interface{}{
- "hello": "world",
- },
- },
- want: &pubsub.Message{
- Attributes: map[string]string{
- prefix + "specversion": "1.0",
- prefix + "id": "ABC-123",
- prefix + "datacontenttype": cloudevents.ApplicationJSON,
- prefix + "time": now.String(),
- prefix + "type": "com.example.test",
- prefix + "dataschema": "http://example.com/schema",
- prefix + "source": "http://example.com/source",
- prefix + "test": "extended",
- prefix + "generation": "1579743478182200",
- },
- Data: func() []byte {
- data := map[string]interface{}{
- "hello": "world",
- }
- return toBytes(data)
- }(),
- },
- },
- "simple v1.0 structured": {
- codec: pubsub.CodecV1{DefaultEncoding: pubsub.StructuredV1},
- event: cloudevents.Event{
- Context: cloudevents.EventContextV1{
- Type: "com.example.test",
- Source: *source,
- ID: "ABC-123",
- }.AsV1(),
- },
- want: &pubsub.Message{
- Attributes: map[string]string{
- "Content-Type": cloudevents.ApplicationCloudEventsJSON,
- },
- Data: func() []byte {
- body := map[string]interface{}{
- "specversion": "1.0",
- "id": "ABC-123",
- "type": "com.example.test",
- "source": "http://example.com/source",
- }
- return toBytes(body)
- }(),
- },
- },
- "full v1.0 structured": {
- codec: pubsub.CodecV1{DefaultEncoding: pubsub.StructuredV1},
- event: cloudevents.Event{
- Context: cloudevents.EventContextV1{
- ID: "ABC-123",
- Time: &now,
- Type: "com.example.test",
- DataSchema: schema,
- DataContentType: cloudevents.StringOfApplicationJSON(),
- Source: *source,
- Extensions: map[string]interface{}{
- "test": "extended",
- },
- }.AsV1(),
- Data: map[string]interface{}{
- "hello": "world",
- },
- },
- want: &pubsub.Message{
- Attributes: map[string]string{
- "Content-Type": cloudevents.ApplicationCloudEventsJSON,
- },
- Data: func() []byte {
- body := map[string]interface{}{
- "specversion": "1.0",
- "datacontenttype": "application/json",
- "data": map[string]interface{}{
- "hello": "world",
- },
- "id": "ABC-123",
- "time": now,
- "type": "com.example.test",
- "test": "extended",
- "dataschema": "http://example.com/schema",
- "source": "http://example.com/source",
- }
- return toBytes(body)
- }(),
- },
- },
- }
- for n, tc := range testCases {
- t.Run(n, func(t *testing.T) {
-
- got, err := tc.codec.Encode(context.TODO(), tc.event)
-
- if tc.wantErr != nil || err != nil {
- if diff := cmp.Diff(tc.wantErr, err); diff != "" {
- t.Errorf("unexpected error (-want, +got) = %v", diff)
- }
- return
- }
-
- if diff := cmp.Diff(tc.want, got); diff != "" {
-
- if msg, ok := got.(*pubsub.Message); ok {
- // It is hard to read the byte dump
- want := string(tc.want.Data)
- got := string(msg.Data)
- if diff := cmp.Diff(want, got); diff != "" {
- t.Errorf("unexpected message body (-want, +got) = %v", diff)
- return
- }
- }
-
- t.Errorf("unexpected message (-want, +got) = %v", diff)
- }
- })
- }
-}
-
-func TestCodecV1_Decode(t *testing.T) {
- now := types.Timestamp{Time: time.Now()}
- sourceUrl, _ := url.Parse("http://example.com/source")
- source := &types.URIRef{URL: *sourceUrl}
-
- schemaUrl, _ := url.Parse("http://example.com/schema")
- schema := &types.URI{URL: *schemaUrl}
-
- testCases := map[string]struct {
- codec pubsub.CodecV1
- msg *pubsub.Message
- want *cloudevents.Event
- wantErr error
- }{
- "simple v1.0 structured": {
- codec: pubsub.CodecV1{},
- msg: &pubsub.Message{
- Attributes: map[string]string{
- "Content-Type": cloudevents.ApplicationCloudEventsJSON,
- },
- Data: toBytes(map[string]interface{}{
- "specversion": "1.0",
- "id": "ABC-123",
- "type": "com.example.test",
- "source": "http://example.com/source",
- }),
- },
- want: &cloudevents.Event{
- Context: &cloudevents.EventContextV1{
- SpecVersion: cloudevents.CloudEventsVersionV1,
- Type: "com.example.test",
- Source: *source,
- ID: "ABC-123",
- },
- },
- },
- "full v1.0 structured": {
- codec: pubsub.CodecV1{},
- msg: &pubsub.Message{
- Attributes: map[string]string{
- "Content-Type": cloudevents.ApplicationCloudEventsJSON,
- },
- Data: toBytes(map[string]interface{}{
- "specversion": "1.0",
- "datacontenttype": "application/json",
- "data": map[string]interface{}{
- "hello": "world",
- },
- "id": "ABC-123",
- "time": now,
- "type": "com.example.test",
- "test": "extended",
- "generation": "1579743478182200",
- "dataschema": "http://example.com/schema",
- "source": "http://example.com/source",
- }),
- },
- want: &cloudevents.Event{
- Context: &cloudevents.EventContextV1{
- SpecVersion: cloudevents.CloudEventsVersionV1,
- ID: "ABC-123",
- Time: &now,
- Type: "com.example.test",
- DataSchema: schema,
- DataContentType: cloudevents.StringOfApplicationJSON(),
- Source: *source,
- Extensions: map[string]interface{}{
- "test": "extended",
- "generation": "1579743478182200",
- },
- },
- Data: toBytes(map[string]interface{}{
- "hello": "world",
- }),
- DataEncoded: true,
- },
- },
- "full v1.0 binary": {
- codec: pubsub.CodecV1{},
- msg: &pubsub.Message{
- Attributes: map[string]string{
- prefix + "specversion": "1.0",
- prefix + "id": "ABC-123",
- prefix + "time": now.String(),
- prefix + "type": "com.example.test",
- prefix + "dataschema": "http://example.com/schema",
- prefix + "source": "http://example.com/source",
- prefix + "test": "extended",
- prefix + "generation": "1579743478182200",
- },
- Data: toBytes(map[string]interface{}{
- "hello": "world",
- }),
- },
- want: &cloudevents.Event{
- Context: &cloudevents.EventContextV1{
- SpecVersion: cloudevents.CloudEventsVersionV1,
- ID: "ABC-123",
- Time: &now,
- Type: "com.example.test",
- DataSchema: schema,
- Source: *source,
- Extensions: map[string]interface{}{
- "test": "extended",
- "generation": "1579743478182200",
- },
- },
- Data: toBytes(map[string]interface{}{
- "hello": "world",
- }),
- DataEncoded: true,
- },
- },
- }
- for n, tc := range testCases {
- t.Run(n, func(t *testing.T) {
-
- got, err := tc.codec.Decode(context.TODO(), tc.msg)
-
- if tc.wantErr != nil || err != nil {
- if diff := cmp.Diff(tc.wantErr, err); diff != "" {
- t.Errorf("unexpected error (-want, +got) = %v", diff)
- }
- return
- }
-
- if diff := cmp.Diff(tc.want, got); diff != "" {
- t.Errorf("unexpected event (-want, +got) = %v", diff)
- }
- })
- }
-}
diff --git a/v1/cloudevents/transport/pubsub/context/context.go b/v1/cloudevents/transport/pubsub/context/context.go
deleted file mode 100644
index e64a5ee71..000000000
--- a/v1/cloudevents/transport/pubsub/context/context.go
+++ /dev/null
@@ -1,94 +0,0 @@
-package context
-
-import (
- "context"
- "strings"
- "time"
-
- "cloud.google.com/go/pubsub"
-)
-
-// TransportContext allows a Receiver to understand the context of a request.
-type TransportContext struct {
- ID string
- PublishTime time.Time
- Project string
- Topic string
- Subscription string
- Method string // push or pull
-}
-
-// NewTransportContext creates a new TransportContext from a pubsub.Message.
-func NewTransportContext(project, topic, subscription, method string, msg *pubsub.Message) TransportContext {
- var tx *TransportContext
- if msg != nil {
- tx = &TransportContext{
- ID: msg.ID,
- PublishTime: msg.PublishTime,
- Project: project,
- Topic: topic,
- Subscription: subscription,
- Method: method,
- }
- } else {
- tx = &TransportContext{}
- }
- return *tx
-}
-
-// String generates a pretty-printed version of the resource as a string.
-func (tx TransportContext) String() string {
- b := strings.Builder{}
-
- b.WriteString("Transport Context,\n")
-
- if tx.ID != "" {
- b.WriteString(" ID: " + tx.ID + "\n")
- }
- if !tx.PublishTime.IsZero() {
- b.WriteString(" PublishTime: " + tx.PublishTime.String() + "\n")
- }
-
- if tx.Project != "" {
- b.WriteString(" Project: " + tx.Project + "\n")
- }
-
- if tx.Topic != "" {
- b.WriteString(" Topic: " + tx.Topic + "\n")
- }
-
- if tx.Subscription != "" {
- b.WriteString(" Subscription: " + tx.Subscription + "\n")
- }
-
- if tx.Method != "" {
- b.WriteString(" Method: " + tx.Method + "\n")
- }
-
- return b.String()
-}
-
-// Opaque key type used to store TransportContext
-type transportContextKeyType struct{}
-
-var transportContextKey = transportContextKeyType{}
-
-// WithTransportContext return a context with the given TransportContext into the provided context object.
-func WithTransportContext(ctx context.Context, tcxt TransportContext) context.Context {
- return context.WithValue(ctx, transportContextKey, tcxt)
-}
-
-// TransportContextFrom pulls a TransportContext out of a context. Always
-// returns a non-nil object.
-func TransportContextFrom(ctx context.Context) TransportContext {
- tctx := ctx.Value(transportContextKey)
- if tctx != nil {
- if tx, ok := tctx.(TransportContext); ok {
- return tx
- }
- if tx, ok := tctx.(*TransportContext); ok {
- return *tx
- }
- }
- return TransportContext{}
-}
diff --git a/v1/cloudevents/transport/pubsub/context/context_test.go b/v1/cloudevents/transport/pubsub/context/context_test.go
deleted file mode 100644
index 960b2b7c5..000000000
--- a/v1/cloudevents/transport/pubsub/context/context_test.go
+++ /dev/null
@@ -1 +0,0 @@
-package context_test
diff --git a/v1/cloudevents/transport/pubsub/doc.go b/v1/cloudevents/transport/pubsub/doc.go
deleted file mode 100644
index b45504609..000000000
--- a/v1/cloudevents/transport/pubsub/doc.go
+++ /dev/null
@@ -1,4 +0,0 @@
-/*
-Package pubsub implements the CloudEvent transport implementation using pubsub.
-*/
-package pubsub
diff --git a/v1/cloudevents/transport/pubsub/encoding.go b/v1/cloudevents/transport/pubsub/encoding.go
deleted file mode 100644
index 1edea5aed..000000000
--- a/v1/cloudevents/transport/pubsub/encoding.go
+++ /dev/null
@@ -1,114 +0,0 @@
-package pubsub
-
-import (
- "context"
-
- "github.com/cloudevents/sdk-go/v1/cloudevents"
-)
-
-// Encoding to use for pubsub transport.
-type Encoding int32
-
-type EncodingSelector func(context.Context, cloudevents.Event) Encoding
-
-const (
- // Default allows pubsub transport implementation to pick.
- Default Encoding = iota
- // BinaryV03 is Binary CloudEvents spec v0.3.
- BinaryV03
- // BinaryV1 is Binary CloudEvents spec v1.0.
- BinaryV1
- // StructuredV03 is Structured CloudEvents spec v0.3.
- StructuredV03
- // StructuredV1 is Structured CloudEvents spec v1.0.
- StructuredV1
-
- // Unknown is unknown.
- Unknown
-
- // Binary is used for Context Based Encoding Selections to use the
- // DefaultBinaryEncodingSelectionStrategy
- Binary = "binary"
-
- // Structured is used for Context Based Encoding Selections to use the
- // DefaultStructuredEncodingSelectionStrategy
- Structured = "structured"
-)
-
-// DefaultBinaryEncodingSelectionStrategy implements a selection process for
-// which binary encoding to use based on spec version of the event.
-func DefaultBinaryEncodingSelectionStrategy(ctx context.Context, e cloudevents.Event) Encoding {
- switch e.SpecVersion() {
- case cloudevents.CloudEventsVersionV01, cloudevents.CloudEventsVersionV02, cloudevents.CloudEventsVersionV03:
- return BinaryV03
- case cloudevents.CloudEventsVersionV1:
- return BinaryV1
- }
- // Unknown version, return Default.
- return Default
-}
-
-// DefaultStructuredEncodingSelectionStrategy implements a selection process
-// for which structured encoding to use based on spec version of the event.
-func DefaultStructuredEncodingSelectionStrategy(ctx context.Context, e cloudevents.Event) Encoding {
- switch e.SpecVersion() {
- case cloudevents.CloudEventsVersionV01, cloudevents.CloudEventsVersionV02, cloudevents.CloudEventsVersionV03:
- return StructuredV03
- case cloudevents.CloudEventsVersionV1:
- return StructuredV1
- }
- // Unknown version, return Default.
- return Default
-}
-
-// String pretty-prints the encoding as a string.
-func (e Encoding) String() string {
- switch e {
- case Default:
- return "Default Encoding " + e.Version()
-
- // Binary
- case BinaryV03, BinaryV1:
- return "Binary Encoding " + e.Version()
-
- // Structured
- case StructuredV03, StructuredV1:
- return "Structured Encoding " + e.Version()
-
- default:
- return "Unknown Encoding"
- }
-}
-
-// Version pretty-prints the encoding version as a string.
-func (e Encoding) Version() string {
- switch e {
-
- // Version 0.2
- // Version 0.3
- case Default, BinaryV03, StructuredV03:
- return "v0.3"
-
- // Version 1.0
- case BinaryV1, StructuredV1:
- return "v1.0"
-
- // Unknown
- default:
- return "Unknown"
- }
-}
-
-// Name creates a string to represent the the codec name.
-func (e Encoding) Name() string {
- switch e {
- case Default:
- return Binary
- case BinaryV03, BinaryV1:
- return Binary
- case StructuredV03, StructuredV1:
- return Structured
- default:
- return Binary
- }
-}
diff --git a/v1/cloudevents/transport/pubsub/internal/connection.go b/v1/cloudevents/transport/pubsub/internal/connection.go
deleted file mode 100644
index af1237e12..000000000
--- a/v1/cloudevents/transport/pubsub/internal/connection.go
+++ /dev/null
@@ -1,310 +0,0 @@
-package internal
-
-import (
- "context"
- "errors"
- "fmt"
- "sync"
- "time"
-
- "cloud.google.com/go/pubsub"
- "github.com/cloudevents/sdk-go/v1/cloudevents"
- pscontext "github.com/cloudevents/sdk-go/v1/cloudevents/transport/pubsub/context"
-)
-
-type topicInfo struct {
- topic *pubsub.Topic
- wasCreated bool
- once sync.Once
- err error
-}
-
-type subInfo struct {
- sub *pubsub.Subscription
- wasCreated bool
- once sync.Once
- err error
-}
-
-// Connection acts as either a pubsub topic or a pubsub subscription .
-type Connection struct {
- // AllowCreateTopic controls if the transport can create a topic if it does
- // not exist.
- AllowCreateTopic bool
-
- // AllowCreateSubscription controls if the transport can create a
- // subscription if it does not exist.
- AllowCreateSubscription bool
-
- ProjectID string
-
- Client *pubsub.Client
-
- TopicID string
- topicInfo *topicInfo
-
- SubscriptionID string
- subInfo *subInfo
-
- // Held when reading or writing topicInfo and subInfo. This is only
- // held while reading the pointer, the structure internally manage
- // their own internal concurrency. This also controls
- // the update of AckDeadline and RetentionDuration if those are
- // nil on start.
- initLock sync.Mutex
-
- // ReceiveSettings is used to configure Pubsub pull subscription.
- ReceiveSettings *pubsub.ReceiveSettings
-
- // AckDeadline is Pub/Sub AckDeadline.
- // Default is 30 seconds.
- // This can only be set prior to first call of any function.
- AckDeadline *time.Duration
- // RetentionDuration is Pub/Sub RetentionDuration.
- // Default is 25 hours.
- // This can only be set prior to first call of any function.
- RetentionDuration *time.Duration
-}
-
-const (
- DefaultAckDeadline = 30 * time.Second
- DefaultRetentionDuration = 25 * time.Hour
-)
-
-var DefaultReceiveSettings = pubsub.ReceiveSettings{
- // Pubsub default receive settings will fill in other values.
- // https://godoc.org/cloud.google.com/go/pubsub#Client.Subscription
-
- // Override the default number of goroutines.
- // This is a magical number now. This has shown throughput improvements empirically
- // by at least 10x (compared to the default value).
- NumGoroutines: 1000,
- Synchronous: false,
-}
-
-func (c *Connection) getOrCreateTopicInfo(ctx context.Context, getAlreadyOpenOnly bool) (*topicInfo, error) {
- // See if a topic has already been created or is in the process of being created.
- // If not, start creating one.
- c.initLock.Lock()
- ti := c.topicInfo
- if ti == nil && !getAlreadyOpenOnly {
- c.topicInfo = &topicInfo{}
- ti = c.topicInfo
- }
- c.initLock.Unlock()
- if ti == nil {
- return nil, fmt.Errorf("no already open topic")
- }
-
- // Make sure the topic structure is initialized at most once.
- ti.once.Do(func() {
- var ok bool
- // Load the topic.
- topic := c.Client.Topic(c.TopicID)
- ok, ti.err = topic.Exists(ctx)
- if ti.err != nil {
- return
- }
- // If the topic does not exist, create a new topic with the given name.
- if !ok {
- if !c.AllowCreateTopic {
- ti.err = fmt.Errorf("transport not allowed to create topic %q", c.TopicID)
- return
- }
- topic, ti.err = c.Client.CreateTopic(ctx, c.TopicID)
- if ti.err != nil {
- return
- }
- ti.wasCreated = true
- }
- // Success.
- ti.topic = topic
- })
- if ti.topic == nil {
- // Initialization failed, remove this attempt so that future callers
- // will try to initialize again.
- c.initLock.Lock()
- if c.topicInfo == ti {
- c.topicInfo = nil
- }
- c.initLock.Unlock()
-
- return nil, fmt.Errorf("unable to get or create topic %q, %v", c.TopicID, ti.err)
- }
- return ti, nil
-}
-
-func (c *Connection) getOrCreateTopic(ctx context.Context, getAlreadyOpenOnly bool) (*pubsub.Topic, error) {
- ti, err := c.getOrCreateTopicInfo(ctx, getAlreadyOpenOnly)
- if ti != nil {
- return ti.topic, nil
- } else {
- return nil, err
- }
-}
-
-// DeleteTopic
-func (c *Connection) DeleteTopic(ctx context.Context) error {
- ti, err := c.getOrCreateTopicInfo(ctx, true)
-
- if err != nil {
- return errors.New("topic not open")
- }
- if !ti.wasCreated {
- return errors.New("topic was not created by pubsub transport")
- }
- if err := ti.topic.Delete(ctx); err != nil {
- return err
- }
-
- ti.topic.Stop()
-
- c.initLock.Lock()
- if ti == c.topicInfo {
- c.topicInfo = nil
- }
- c.initLock.Unlock()
-
- return nil
-}
-
-func (c *Connection) getOrCreateSubscriptionInfo(ctx context.Context, getAlreadyOpenOnly bool) (*subInfo, error) {
- c.initLock.Lock()
- // Default the ack deadline and retention duration config.
- // We only do this once.
- if c.AckDeadline == nil {
- ackDeadline := DefaultAckDeadline
- c.AckDeadline = &(ackDeadline)
- }
- if c.RetentionDuration == nil {
- retentionDuration := DefaultRetentionDuration
- c.RetentionDuration = &retentionDuration
- }
- // See if a subscription has already been created or is in the process of being created.
- // If not, start creating one.
- si := c.subInfo
- if si == nil && !getAlreadyOpenOnly {
- c.subInfo = &subInfo{}
- si = c.subInfo
- }
- c.initLock.Unlock()
- if si == nil {
- return nil, fmt.Errorf("no already open subscription")
- }
-
- // Make sure the subscription structure is initialized at most once.
- si.once.Do(func() {
- // Load the subscription.
- var ok bool
- sub := c.Client.Subscription(c.SubscriptionID)
- ok, si.err = sub.Exists(ctx)
- if si.err != nil {
- return
- }
- // If subscription doesn't exist, create it.
- if !ok {
- if !c.AllowCreateSubscription {
- si.err = fmt.Errorf("transport not allowed to create subscription %q", c.SubscriptionID)
- return
- }
-
- // Load the topic.
- var topic *pubsub.Topic
- topic, si.err = c.getOrCreateTopic(ctx, false)
- if si.err != nil {
- return
- }
-
- // Create a new subscription to the previously created topic
- // with the given name.
- // TODO: allow to use push config + allow setting the SubscriptionConfig.
- sub, si.err = c.Client.CreateSubscription(ctx, c.SubscriptionID, pubsub.SubscriptionConfig{
- Topic: topic,
- AckDeadline: *c.AckDeadline,
- RetentionDuration: *c.RetentionDuration,
- })
- if si.err != nil {
- return
- }
-
- si.wasCreated = true
- }
- if c.ReceiveSettings == nil {
- sub.ReceiveSettings = DefaultReceiveSettings
- } else {
- sub.ReceiveSettings = *c.ReceiveSettings
- }
- // Success.
- si.sub = sub
- })
- if si.sub == nil {
- // Initialization failed, remove this attempt so that future callers
- // will try to initialize again.
- c.initLock.Lock()
- if c.subInfo == si {
- c.subInfo = nil
- }
- c.initLock.Unlock()
- return nil, fmt.Errorf("unable to create subscription %q, %v", c.SubscriptionID, si.err)
- }
- return si, nil
-}
-
-func (c *Connection) getOrCreateSubscription(ctx context.Context, getAlreadyOpenOnly bool) (*pubsub.Subscription, error) {
- si, err := c.getOrCreateSubscriptionInfo(ctx, getAlreadyOpenOnly)
- if si != nil {
- return si.sub, nil
- } else {
- return nil, err
- }
-}
-
-// DeleteSubscription
-func (c *Connection) DeleteSubscription(ctx context.Context) error {
- si, err := c.getOrCreateSubscriptionInfo(ctx, true)
-
- if err != nil {
- return errors.New("subscription not open")
- }
-
- if !si.wasCreated {
- return errors.New("subscription was not created by pubsub transport")
- }
- if err := si.sub.Delete(ctx); err != nil {
- return err
- }
-
- c.initLock.Lock()
- if si == c.subInfo {
- c.subInfo = nil
- }
- c.initLock.Unlock()
-
- return nil
-}
-
-// Publish
-func (c *Connection) Publish(ctx context.Context, msg *pubsub.Message) (*cloudevents.Event, error) {
- topic, err := c.getOrCreateTopic(ctx, false)
- if err != nil {
- return nil, err
- }
-
- r := topic.Publish(ctx, msg)
- _, err = r.Get(ctx)
- return nil, err
-}
-
-// Start
-// NOTE: This is a blocking call.
-func (c *Connection) Receive(ctx context.Context, fn func(context.Context, *pubsub.Message)) error {
- sub, err := c.getOrCreateSubscription(ctx, false)
- if err != nil {
- return err
- }
- // Ok, ready to start pulling.
- return sub.Receive(ctx, func(ctx context.Context, m *pubsub.Message) {
- ctx = pscontext.WithTransportContext(ctx, pscontext.NewTransportContext(c.ProjectID, c.TopicID, c.SubscriptionID, "pull", m))
- fn(ctx, m)
- })
-}
diff --git a/v1/cloudevents/transport/pubsub/internal/connection_test.go b/v1/cloudevents/transport/pubsub/internal/connection_test.go
deleted file mode 100644
index b524a6890..000000000
--- a/v1/cloudevents/transport/pubsub/internal/connection_test.go
+++ /dev/null
@@ -1,730 +0,0 @@
-package internal
-
-import (
- "context"
- "fmt"
- "sync"
- "testing"
- "time"
-
- "cloud.google.com/go/pubsub"
- "cloud.google.com/go/pubsub/pstest"
- "github.com/google/go-cmp/cmp"
- "google.golang.org/api/option"
- "google.golang.org/grpc"
-)
-
-type testPubsubClient struct {
- srv *pstest.Server
- conn *grpc.ClientConn
-}
-
-type failPattern struct {
- // Error to return. nil for no injected error
- ErrReturn error
- // Times to return this error (or non-error). 0 for infinite.
- Count int
- // Duration to block prior to making the call
- Delay time.Duration
-}
-
-// Create a pubsub client. If failureMap is provided, it gives a set of failures to induce in specific methods.
-// failureMap is modified by the event processor and should not be read or modified after calling New()
-func (pc *testPubsubClient) New(ctx context.Context, projectID string, failureMap map[string][]failPattern) (*pubsub.Client, error) {
- pc.srv = pstest.NewServer()
- var err error
- var conn *grpc.ClientConn
- if len(failureMap) == 0 {
- conn, err = grpc.Dial(pc.srv.Addr, grpc.WithInsecure())
- } else {
- conn, err = grpc.Dial(pc.srv.Addr, grpc.WithInsecure(), grpc.WithUnaryInterceptor(makeFailureIntercept(failureMap)))
-
- }
- if err != nil {
- return nil, err
- }
- pc.conn = conn
- return pubsub.NewClient(ctx, projectID, option.WithGRPCConn(conn))
-}
-
-func (pc *testPubsubClient) Close() {
- pc.srv.Close()
- pc.conn.Close()
-}
-
-// Make a grpc failure injector that failes the specified methods with the
-// specified rates.
-func makeFailureIntercept(failureMap map[string][]failPattern) grpc.UnaryClientInterceptor {
- var lock sync.Mutex
- return func(ctx context.Context, method string, req, reply interface{}, cc *grpc.ClientConn, invoker grpc.UnaryInvoker, opts ...grpc.CallOption) error {
- var injectedErr error
- var delay time.Duration
-
- lock.Lock()
- if failureMap != nil {
- fpArr := failureMap[method]
- if len(fpArr) != 0 {
- injectedErr = fpArr[0].ErrReturn
- delay = fpArr[0].Delay
- if fpArr[0].Count != 0 {
- fpArr[0].Count--
- if fpArr[0].Count == 0 {
- failureMap[method] = fpArr[1:]
- }
- }
- }
- }
- lock.Unlock()
- if delay != 0 {
- time.Sleep(delay)
- }
- if injectedErr != nil {
- return injectedErr
- } else {
- return invoker(ctx, method, req, reply, cc, opts...)
- }
- }
-
-}
-
-// Verify that the topic exists prior to the call, deleting it via psconn succeeds, and
-// the topic does not exist after the call.
-func verifyTopicDeleteWorks(t *testing.T, client *pubsub.Client, psconn *Connection, topicID string) {
- ctx := context.Background()
-
- if ok, err := client.Topic(topicID).Exists(ctx); err != nil || !ok {
- t.Errorf("topic id=%s got exists=%v want=true, err=%v", topicID, ok, err)
- }
-
- if err := psconn.DeleteTopic(ctx); err != nil {
- t.Errorf("delete topic failed: %v", err)
- }
-
- if ok, err := client.Topic(topicID).Exists(ctx); err != nil || ok {
- t.Errorf("topic id=%s got exists=%v want=false, err=%v", topicID, ok, err)
- }
-}
-
-// Verify that the topic exists before and after the call, and that deleting it via psconn fails
-func verifyTopicDeleteFails(t *testing.T, client *pubsub.Client, psconn *Connection, topicID string) {
- ctx := context.Background()
-
- if ok, err := client.Topic(topicID).Exists(ctx); err != nil || !ok {
- t.Errorf("topic id=%s got exists=%v want=true, err=%v", topicID, ok, err)
- }
-
- if err := psconn.DeleteTopic(ctx); err == nil {
- t.Errorf("delete topic succeeded unexpectedly")
- }
-
- if ok, err := client.Topic(topicID).Exists(ctx); err != nil || !ok {
- t.Errorf("topic id=%s after delete got exists=%v want=true, err=%v", topicID, ok, err)
- }
-}
-
-// Test that publishing creates a topic
-func TestPublishCreateTopic(t *testing.T) {
- ctx := context.Background()
- pc := &testPubsubClient{}
- defer pc.Close()
-
- projectID, topicID, subID := "test-project", "test-topic", "test-sub"
-
- client, err := pc.New(ctx, projectID, nil)
- if err != nil {
- t.Fatalf("failed to create pubsub client: %v", err)
- }
- defer client.Close()
-
- psconn := &Connection{
- AllowCreateSubscription: true,
- AllowCreateTopic: true,
- Client: client,
- ProjectID: projectID,
- TopicID: topicID,
- SubscriptionID: subID,
- }
-
- msg := &pubsub.Message{
- ID: "msg-id-1",
- Data: []byte("msg-data-1"),
- }
- if _, err := psconn.Publish(ctx, msg); err != nil {
- t.Errorf("failed to publish message: %v", err)
- }
-
- verifyTopicDeleteWorks(t, client, psconn, topicID)
-}
-
-// Test that publishing to an already created topic works and doesn't allow topic deletion
-func TestPublishExistingTopic(t *testing.T) {
- for _, allowCreate := range []bool{true, false} {
- t.Run(fmt.Sprintf("allowCreate_%v", allowCreate), func(t *testing.T) {
- ctx := context.Background()
- pc := &testPubsubClient{}
- defer pc.Close()
-
- projectID, topicID, subID := "test-project", "test-topic", "test-sub"
-
- client, err := pc.New(ctx, projectID, nil)
- if err != nil {
- t.Fatalf("failed to create pubsub client: %v", err)
- }
- defer client.Close()
-
- psconn := &Connection{
- AllowCreateSubscription: true,
- AllowCreateTopic: allowCreate,
- Client: client,
- ProjectID: projectID,
- TopicID: topicID,
- SubscriptionID: subID,
- }
-
- topic, err := client.CreateTopic(ctx, topicID)
- if err != nil {
- t.Fatalf("failed to pre-create topic: %v", err)
- }
- topic.Stop()
-
- msg := &pubsub.Message{
- ID: "msg-id-1",
- Data: []byte("msg-data-1"),
- }
- if _, err := psconn.Publish(ctx, msg); err != nil {
- t.Errorf("failed to publish message: %v", err)
- }
-
- verifyTopicDeleteFails(t, client, psconn, topicID)
- })
- }
-}
-
-// Make sure that Publishing works if the original publish failed due to an
-// error in one of the pubsub calls.
-func TestPublishAfterPublishFailure(t *testing.T) {
- for _, failureMethod := range []string{
- "/google.pubsub.v1.Publisher/GetTopic",
- "/google.pubsub.v1.Publisher/CreateTopic",
- "/google.pubsub.v1.Publisher/Publish"} {
- t.Run(failureMethod, func(t *testing.T) {
- ctx := context.Background()
- pc := &testPubsubClient{}
- defer pc.Close()
-
- projectID, topicID, subID := "test-project", "test-topic", "test-sub"
-
- failureMap := make(map[string][]failPattern)
- failureMap[failureMethod] = []failPattern{{
- ErrReturn: fmt.Errorf("Injected error"),
- Count: 1,
- Delay: 0}}
- client, err := pc.New(ctx, projectID, failureMap)
- if err != nil {
- t.Fatalf("failed to create pubsub client: %v", err)
- }
- defer client.Close()
-
- psconn := &Connection{
- AllowCreateSubscription: true,
- AllowCreateTopic: true,
- Client: client,
- ProjectID: projectID,
- TopicID: topicID,
- SubscriptionID: subID,
- }
-
- msg := &pubsub.Message{
- ID: "msg-id-1",
- Data: []byte("msg-data-1"),
- }
- // Fails due to injected failure
- if _, err := psconn.Publish(ctx, msg); err == nil {
- t.Errorf("Expected publish failure, but didn't see it: %v", err)
- }
- // Succeeds
- if _, err := psconn.Publish(ctx, msg); err != nil {
- t.Errorf("failed to publish message: %v", err)
- }
- verifyTopicDeleteWorks(t, client, psconn, topicID)
- })
- }
-}
-
-// Test Publishing after Deleting a first version of a topic
-func TestPublishCreateTopicAfterDelete(t *testing.T) {
- ctx := context.Background()
- pc := &testPubsubClient{}
- defer pc.Close()
-
- projectID, topicID, subID := "test-project", "test-topic", "test-sub"
-
- client, err := pc.New(ctx, projectID, nil)
- if err != nil {
- t.Fatalf("failed to create pubsub client: %v", err)
- }
- defer client.Close()
-
- psconn := &Connection{
- AllowCreateSubscription: true,
- AllowCreateTopic: true,
- Client: client,
- ProjectID: projectID,
- TopicID: topicID,
- SubscriptionID: subID,
- }
-
- msg := &pubsub.Message{
- ID: "msg-id-1",
- Data: []byte("msg-data-1"),
- }
- if _, err := psconn.Publish(ctx, msg); err != nil {
- t.Errorf("failed to publish message: %v", err)
- }
-
- verifyTopicDeleteWorks(t, client, psconn, topicID)
-
- if _, err := psconn.Publish(ctx, msg); err != nil {
- t.Errorf("failed to publish message: %v", err)
- }
-
- verifyTopicDeleteWorks(t, client, psconn, topicID)
-}
-
-// Test that publishing fails if a topic doesn't exist and topic creation isn't allowed
-func TestPublishCreateTopicNotAllowedFails(t *testing.T) {
- ctx := context.Background()
- pc := &testPubsubClient{}
- defer pc.Close()
-
- projectID, topicID, subID := "test-project", "test-topic", "test-sub"
-
- client, err := pc.New(ctx, projectID, nil)
- if err != nil {
- t.Fatalf("failed to create pubsub client: %v", err)
- }
- defer client.Close()
-
- psconn := &Connection{
- AllowCreateSubscription: true,
- AllowCreateTopic: false,
- Client: client,
- ProjectID: projectID,
- TopicID: topicID,
- SubscriptionID: subID,
- }
-
- msg := &pubsub.Message{
- ID: "msg-id-1",
- Data: []byte("msg-data-1"),
- }
- if _, err := psconn.Publish(ctx, msg); err == nil {
- t.Errorf("publish succeeded unexpectedly")
- }
-
- if ok, err := client.Topic(topicID).Exists(ctx); err == nil && ok {
- t.Errorf("topic id=%s got exists=%v want=false, err=%v", topicID, ok, err)
- }
-}
-
-// Test that failures of racing topic opens are reported out
-func TestPublishParallelFailure(t *testing.T) {
- // This test is racy since it relies on a delay on one goroutine to
- // ensure a second hits a sync.Once while the other is still processing
- // it. Optimistically try with a short delay, but retry with longer
- // ones so a failure is almost certainly a real failure, not a race.
- var overallError error
- for _, delay := range []time.Duration{time.Second / 4, 2 * time.Second, 10 * time.Second, 40 * time.Second} {
- overallError = func() error {
- failureMethod := "/google.pubsub.v1.Publisher/GetTopic"
- ctx := context.Background()
- pc := &testPubsubClient{}
- defer pc.Close()
-
- projectID, topicID, subID := "test-project", "test-topic", "test-sub"
-
- // Inject a failure, but also add a delay to the call sees the error
- failureMap := make(map[string][]failPattern)
- failureMap[failureMethod] = []failPattern{{
- ErrReturn: fmt.Errorf("Injected error"),
- Count: 1,
- Delay: delay}}
- client, err := pc.New(ctx, projectID, failureMap)
- if err != nil {
- t.Fatalf("failed to create pubsub client: %v", err)
- }
- defer client.Close()
-
- psconn := &Connection{
- AllowCreateSubscription: true,
- AllowCreateTopic: true,
- Client: client,
- ProjectID: projectID,
- TopicID: topicID,
- SubscriptionID: subID,
- }
-
- msg := &pubsub.Message{
- ID: "msg-id-1",
- Data: []byte("msg-data-1"),
- }
- resChan := make(chan error)
- // Try a publish. We want this to be the first to try to create the channel
- go func() {
- _, err := psconn.Publish(ctx, msg)
- resChan <- err
- }()
-
- // Try a second publish. We hope the above has hit it's critical section before
- // this starts so that this reports out the error returned above.
- _, errPub1 := psconn.Publish(ctx, msg)
- errPub2 := <-resChan
- if errPub1 == nil || errPub2 == nil {
- return fmt.Errorf("expected dual expected failure, saw (%v) (%v) last run", errPub1, errPub2)
- } else if errPub1 == nil && errPub2 == nil {
- t.Fatalf("Dual success when expecting at least one failure, delay %v", delay)
- }
- return nil
- }()
- // Saw a successfull run, no retry needed.
- if overallError == nil {
- break
- }
- // Failure. The loop will bump the delay and try again(unless we've hit the max reasonable delay)
- }
- if overallError != nil {
- t.Errorf(overallError.Error())
- }
-}
-
-// Test that creating a subscription also creates the topic and subscription
-func TestReceiveCreateTopicAndSubscription(t *testing.T) {
- ctx := context.Background()
- pc := &testPubsubClient{}
- defer pc.Close()
-
- projectID, topicID, subID := "test-project", "test-topic", "test-sub"
- client, err := pc.New(ctx, projectID, nil)
- if err != nil {
- t.Fatalf("failed to create pubsub client: %v", err)
- }
- defer client.Close()
-
- psconn := &Connection{
- AllowCreateSubscription: true,
- AllowCreateTopic: true,
- Client: client,
- ProjectID: projectID,
- TopicID: topicID,
- SubscriptionID: subID,
- }
-
- ctx2, cancel := context.WithCancel(ctx)
- go psconn.Receive(ctx2, func(_ context.Context, msg *pubsub.Message) {
- msg.Ack()
- })
- // Sleep waiting for the goroutine to create the topic and subscription
- // If it takes over a minute, run the test anyway to get failure logging
- for _, delay := range []time.Duration{time.Second / 4, time.Second, 20 * time.Second, 40 * time.Second} {
- time.Sleep(delay)
- ok, err := client.Subscription(subID).Exists(ctx)
- if ok == true && err == nil {
- break
- }
- }
-
- if ok, err := client.Topic(topicID).Exists(ctx); err != nil || !ok {
- t.Errorf("topic id=%s got exists=%v want=true, err=%v", topicID, ok, err)
- }
-
- if ok, err := client.Subscription(subID).Exists(ctx); err != nil || !ok {
- t.Errorf("subscription id=%s got exists=%v want=true, err=%v", subID, ok, err)
- }
-
- si, err := psconn.getOrCreateSubscriptionInfo(context.Background(), true)
- if err != nil {
- t.Errorf("error getting subscription info %v", err)
- }
- if si.sub.ReceiveSettings.NumGoroutines != DefaultReceiveSettings.NumGoroutines {
- t.Errorf("subscription receive settings have NumGoroutines=%d, want %d",
- si.sub.ReceiveSettings.NumGoroutines, DefaultReceiveSettings.NumGoroutines)
- }
-
- cancel()
-
- if err := psconn.DeleteSubscription(ctx); err != nil {
- t.Errorf("delete subscription failed: %v", err)
- }
-
- if ok, err := client.Subscription(subID).Exists(ctx); err != nil || ok {
- t.Errorf("subscription id=%s got exists=%v want=false, err=%v", subID, ok, err)
- }
-
- verifyTopicDeleteWorks(t, client, psconn, topicID)
-}
-
-// Test receive on an existing topic and subscription also works.
-func TestReceiveExistingTopic(t *testing.T) {
- for _, allow := range [](struct{ Sub, Topic bool }){{true, true}, {true, false}, {false, true}, {false, false}} {
- t.Run(fmt.Sprintf("sub_%v__topic_%v", allow.Sub, allow.Topic), func(t *testing.T) {
-
- ctx := context.Background()
- pc := &testPubsubClient{}
- defer pc.Close()
-
- projectID, topicID, subID := "test-project", "test-topic", "test-sub"
- client, err := pc.New(ctx, projectID, nil)
- if err != nil {
- t.Fatalf("failed to create pubsub client: %v", err)
- }
- defer client.Close()
-
- psconn := &Connection{
- AllowCreateSubscription: allow.Sub,
- AllowCreateTopic: allow.Topic,
- Client: client,
- ProjectID: projectID,
- TopicID: topicID,
- SubscriptionID: subID,
- }
-
- topic, err := client.CreateTopic(ctx, topicID)
- if err != nil {
- pc.Close()
- t.Fatalf("failed to pre-create topic: %v", err)
- }
-
- _, err = client.CreateSubscription(ctx, subID, pubsub.SubscriptionConfig{
- Topic: topic,
- AckDeadline: DefaultAckDeadline,
- RetentionDuration: DefaultRetentionDuration,
- })
- topic.Stop()
- if err != nil {
- pc.Close()
- t.Fatalf("failed to pre-createsubscription: %v", err)
- }
-
- ctx2, cancel := context.WithCancel(ctx)
- go psconn.Receive(ctx2, func(_ context.Context, msg *pubsub.Message) {
- msg.Ack()
- })
- // Block waiting for receive to succeed
- si, err := psconn.getOrCreateSubscriptionInfo(context.Background(), false)
- if err != nil {
- t.Errorf("error getting subscription info %v", err)
- }
- if si.sub.ReceiveSettings.NumGoroutines != DefaultReceiveSettings.NumGoroutines {
- t.Errorf("subscription receive settings have NumGoroutines=%d, want %d",
- si.sub.ReceiveSettings.NumGoroutines, DefaultReceiveSettings.NumGoroutines)
- }
-
- cancel()
-
- if err := psconn.DeleteSubscription(ctx); err == nil {
- t.Errorf("delete subscription unexpectedly succeeded")
- }
-
- if ok, err := client.Subscription(subID).Exists(ctx); err != nil || !ok {
- t.Errorf("subscription id=%s got exists=%v want=true, err=%v", subID, ok, err)
- }
-
- verifyTopicDeleteFails(t, client, psconn, topicID)
- })
- }
-}
-
-// Test that creating a subscription after a failed attempt to create a subsciption works
-func TestReceiveCreateSubscriptionAfterFailure(t *testing.T) {
- for _, failureMethod := range []string{
- "/google.pubsub.v1.Publisher/GetTopic",
- "/google.pubsub.v1.Publisher/CreateTopic",
- "/google.pubsub.v1.Subscriber/GetSubscription",
- "/google.pubsub.v1.Subscriber/CreateSubscription"} {
- t.Run(failureMethod, func(t *testing.T) {
-
- ctx := context.Background()
- pc := &testPubsubClient{}
- defer pc.Close()
-
- projectID, topicID, subID := "test-project", "test-topic", "test-sub"
- failureMap := make(map[string][]failPattern)
- failureMap[failureMethod] = []failPattern{{
- ErrReturn: fmt.Errorf("Injected error"),
- Count: 1,
- Delay: 0}}
- client, err := pc.New(ctx, projectID, failureMap)
- if err != nil {
- t.Fatalf("failed to create pubsub client: %v", err)
- }
- defer client.Close()
-
- psconn := &Connection{
- AllowCreateSubscription: true,
- AllowCreateTopic: true,
- Client: client,
- ProjectID: projectID,
- TopicID: topicID,
- SubscriptionID: subID,
- }
-
- // We expect this receive to fail due to the injected error
- ctx2, cancel := context.WithCancel(ctx)
- errRet := make(chan error)
- go func() {
- errRet <- psconn.Receive(ctx2, func(_ context.Context, msg *pubsub.Message) {
- msg.Ack()
- })
- }()
-
- select {
- case err := <-errRet:
- if err == nil {
- t.Fatalf("unexpected nil error from Receive")
- }
- case <-time.After(time.Minute):
- cancel()
- t.Fatalf("timeout waiting for receive error")
- }
-
- // We expect this receive to succeed
- errRet2 := make(chan error)
- ctx2, cancel = context.WithCancel(context.Background())
- go func() {
- errRet2 <- psconn.Receive(ctx2, func(_ context.Context, msg *pubsub.Message) {
- msg.Ack()
- })
- }()
- // Sleep waiting for the goroutine to create the topic and subscription
- // If it takes over a minute, run the test anyway to get failure logging
- for _, delay := range []time.Duration{time.Second / 4, time.Second, 20 * time.Second, 40 * time.Second} {
- time.Sleep(delay)
- ok, err := client.Subscription(subID).Exists(ctx)
- if ok == true && err == nil {
- break
- }
- }
- select {
- case err := <-errRet2:
- t.Errorf("unexpected error from Receive: %v", err)
- default:
- }
- if ok, err := client.Topic(topicID).Exists(ctx); err != nil || !ok {
- t.Errorf("topic id=%s got exists=%v want=true, err=%v", topicID, ok, err)
- }
-
- if ok, err := client.Subscription(subID).Exists(ctx); err != nil || !ok {
- t.Errorf("subscription id=%s got exists=%v want=true, err=%v", subID, ok, err)
- }
-
- cancel()
- })
- }
-}
-
-// Test that lack of create privileges for topic or subscription causes a receive to fail for
-// a non-existing subscription and topic
-func TestReceiveCreateDisallowedFail(t *testing.T) {
- ctx := context.Background()
- pc := &testPubsubClient{}
- defer pc.Close()
-
- for _, allow := range [](struct{ Sub, Topic bool }){{false, true}, {true, false}, {false, false}} {
- t.Run(fmt.Sprintf("sub_%v__topic_%v", allow.Sub, allow.Topic), func(t *testing.T) {
-
- projectID, topicID, subID := "test-project", "test-topic", "test-sub"
- client, err := pc.New(ctx, projectID, nil)
- if err != nil {
- t.Fatalf("failed to create pubsub client: %v", err)
- }
- defer client.Close()
-
- psconn := &Connection{
- AllowCreateSubscription: allow.Sub,
- AllowCreateTopic: allow.Topic,
- Client: client,
- ProjectID: projectID,
- TopicID: topicID,
- SubscriptionID: subID,
- }
-
- ctx2, cancel := context.WithCancel(ctx)
- errRet := make(chan error)
- go func() {
- errRet <- psconn.Receive(ctx2, func(_ context.Context, msg *pubsub.Message) {
- msg.Ack()
- })
- }()
-
- select {
- case err := <-errRet:
- if err == nil {
- t.Fatalf("unexpected nil error from Receive")
- }
- case <-time.After(time.Minute):
- cancel()
- t.Fatalf("timeout waiting for receive error")
- }
- cancel()
- })
- }
-}
-
-// Test a full round trip of a message
-func TestPublishReceiveRoundtrip(t *testing.T) {
- ctx := context.Background()
- pc := &testPubsubClient{}
- defer pc.Close()
-
- projectID, topicID, subID := "test-project", "test-topic", "test-sub"
- client, err := pc.New(ctx, projectID, nil)
- if err != nil {
- t.Fatalf("failed to create pubsub client: %v", err)
- }
- defer client.Close()
-
- psconn := &Connection{
- AllowCreateSubscription: true,
- AllowCreateTopic: true,
- Client: client,
- ProjectID: projectID,
- TopicID: topicID,
- SubscriptionID: subID,
- }
-
- wantMsgs := make(map[string]string)
- gotMsgs := make(map[string]string)
- wg := &sync.WaitGroup{}
-
- ctx2, cancel := context.WithCancel(ctx)
- mux := &sync.Mutex{}
- // Pubsub will drop all messages if there is no subscription.
- // Call Receive first so that subscription can be created before
- // we publish any message.
- go psconn.Receive(ctx2, func(_ context.Context, msg *pubsub.Message) {
- mux.Lock()
- defer mux.Unlock()
- gotMsgs[string(msg.Data)] = string(msg.Data)
- msg.Ack()
- wg.Done()
- })
- // Wait a little bit for the subscription creation to complete.
- time.Sleep(time.Second)
-
- for i := 0; i < 10; i++ {
- data := fmt.Sprintf("data-%d", i)
- wantMsgs[data] = data
-
- if _, err := psconn.Publish(ctx, &pubsub.Message{Data: []byte(data)}); err != nil {
- t.Errorf("failed to publish message: %v", err)
- }
- wg.Add(1)
- }
-
- wg.Wait()
- cancel()
-
- if diff := cmp.Diff(gotMsgs, wantMsgs); diff != "" {
- t.Errorf("received unexpected messages (-want +got):\n%s", diff)
- }
-}
diff --git a/v1/cloudevents/transport/pubsub/message.go b/v1/cloudevents/transport/pubsub/message.go
deleted file mode 100644
index 5597e20b5..000000000
--- a/v1/cloudevents/transport/pubsub/message.go
+++ /dev/null
@@ -1,47 +0,0 @@
-package pubsub
-
-import (
- "encoding/json"
-
- "github.com/cloudevents/sdk-go/v1/cloudevents/transport"
-)
-
-// type check that this transport message impl matches the contract
-var _ transport.Message = (*Message)(nil)
-
-// Message represents a Pub/Sub message.
-type Message struct {
- // Data is the actual data in the message.
- Data []byte
-
- // Attributes represents the key-value pairs the current message
- // is labelled with.
- Attributes map[string]string
-}
-
-func (m Message) CloudEventsVersion() string {
- // Check as Binary encoding first.
- if m.Attributes != nil {
- // Binary v0.3:
- if s := m.Attributes[prefix+"specversion"]; s != "" {
- return s
- }
- }
-
- // Now check as Structured encoding.
- raw := make(map[string]json.RawMessage)
- if err := json.Unmarshal(m.Data, &raw); err != nil {
- return ""
- }
-
- // structured v0.3
- if v, ok := raw["specversion"]; ok {
- var version string
- if err := json.Unmarshal(v, &version); err != nil {
- return ""
- }
- return version
- }
-
- return ""
-}
diff --git a/v1/cloudevents/transport/pubsub/options.go b/v1/cloudevents/transport/pubsub/options.go
deleted file mode 100644
index a77adb246..000000000
--- a/v1/cloudevents/transport/pubsub/options.go
+++ /dev/null
@@ -1,207 +0,0 @@
-package pubsub
-
-import (
- "fmt"
- "os"
-
- "cloud.google.com/go/pubsub"
-)
-
-// Option is the function signature required to be considered an pubsub.Option.
-type Option func(*Transport) error
-
-const (
- DefaultProjectEnvKey = "GOOGLE_CLOUD_PROJECT"
- DefaultTopicEnvKey = "PUBSUB_TOPIC"
- DefaultSubscriptionEnvKey = "PUBSUB_SUBSCRIPTION"
-)
-
-// WithEncoding sets the encoding for pubsub transport.
-func WithEncoding(encoding Encoding) Option {
- return func(t *Transport) error {
- t.Encoding = encoding
- return nil
- }
-}
-
-// WithDefaultEncodingSelector sets the encoding selection strategy for
-// default encoding selections based on Event.
-func WithDefaultEncodingSelector(fn EncodingSelector) Option {
- return func(t *Transport) error {
- if t == nil {
- return fmt.Errorf("http default encoding selector option can not set nil transport")
- }
- if fn != nil {
- t.DefaultEncodingSelectionFn = fn
- return nil
- }
- return fmt.Errorf("pubsub fn for DefaultEncodingSelector was nil")
- }
-}
-
-// WithBinaryEncoding sets the encoding selection strategy for
-// default encoding selections based on Event, the encoded event will be the
-// given version in Binary form.
-func WithBinaryEncoding() Option {
- return func(t *Transport) error {
- if t == nil {
- return fmt.Errorf("pubsub binary encoding option can not set nil transport")
- }
-
- t.DefaultEncodingSelectionFn = DefaultBinaryEncodingSelectionStrategy
- return nil
- }
-}
-
-// WithStructuredEncoding sets the encoding selection strategy for
-// default encoding selections based on Event, the encoded event will be the
-// given version in Structured form.
-func WithStructuredEncoding() Option {
- return func(t *Transport) error {
- if t == nil {
- return fmt.Errorf("pubsub structured encoding option can not set nil transport")
- }
-
- t.DefaultEncodingSelectionFn = DefaultStructuredEncodingSelectionStrategy
- return nil
- }
-}
-
-// WithClient sets the pubsub client for pubsub transport. Use this for explicit
-// auth setup. Otherwise the env var 'GOOGLE_APPLICATION_CREDENTIALS' is used.
-// See https://cloud.google.com/docs/authentication/production for more details.
-func WithClient(client *pubsub.Client) Option {
- return func(t *Transport) error {
- t.client = client
- return nil
- }
-}
-
-// WithProjectID sets the project ID for pubsub transport.
-func WithProjectID(projectID string) Option {
- return func(t *Transport) error {
- t.projectID = projectID
- return nil
- }
-}
-
-// WithProjectIDFromEnv sets the project ID for pubsub transport from a
-// given environment variable name.
-func WithProjectIDFromEnv(key string) Option {
- return func(t *Transport) error {
- v := os.Getenv(key)
- if v == "" {
- return fmt.Errorf("unable to load project id, %q environment variable not set", key)
- }
- t.projectID = v
- return nil
- }
-}
-
-// WithProjectIDFromDefaultEnv sets the project ID for pubsub transport from
-// the environment variable named 'GOOGLE_CLOUD_PROJECT'.
-func WithProjectIDFromDefaultEnv() Option {
- return WithProjectIDFromEnv(DefaultProjectEnvKey)
-}
-
-// WithTopicID sets the topic ID for pubsub transport.
-func WithTopicID(topicID string) Option {
- return func(t *Transport) error {
- t.topicID = topicID
- return nil
- }
-}
-
-// WithTopicIDFromEnv sets the topic ID for pubsub transport from a given
-// environment variable name.
-func WithTopicIDFromEnv(key string) Option {
- return func(t *Transport) error {
- v := os.Getenv(key)
- if v == "" {
- return fmt.Errorf("unable to load topic id, %q environment variable not set", key)
- }
- t.topicID = v
- return nil
- }
-}
-
-// WithTopicIDFromDefaultEnv sets the topic ID for pubsub transport from the
-// environment variable named 'PUBSUB_TOPIC'.
-func WithTopicIDFromDefaultEnv() Option {
- return WithTopicIDFromEnv(DefaultTopicEnvKey)
-}
-
-// WithSubscriptionID sets the subscription ID for pubsub transport.
-// This option can be used multiple times.
-func WithSubscriptionID(subscriptionID string) Option {
- return func(t *Transport) error {
- if t.subscriptions == nil {
- t.subscriptions = make([]subscriptionWithTopic, 0)
- }
- t.subscriptions = append(t.subscriptions, subscriptionWithTopic{
- subscriptionID: subscriptionID,
- })
- return nil
- }
-}
-
-// WithSubscriptionAndTopicID sets the subscription and topic IDs for pubsub transport.
-// This option can be used multiple times.
-func WithSubscriptionAndTopicID(subscriptionID, topicID string) Option {
- return func(t *Transport) error {
- if t.subscriptions == nil {
- t.subscriptions = make([]subscriptionWithTopic, 0)
- }
- t.subscriptions = append(t.subscriptions, subscriptionWithTopic{
- subscriptionID: subscriptionID,
- topicID: topicID,
- })
- return nil
- }
-}
-
-// WithSubscriptionIDFromEnv sets the subscription ID for pubsub transport from
-// a given environment variable name.
-func WithSubscriptionIDFromEnv(key string) Option {
- return func(t *Transport) error {
- v := os.Getenv(key)
- if v == "" {
- return fmt.Errorf("unable to load subscription id, %q environment variable not set", key)
- }
-
- opt := WithSubscriptionID(v)
- return opt(t)
- }
-}
-
-// WithSubscriptionIDFromDefaultEnv sets the subscription ID for pubsub
-// transport from the environment variable named 'PUBSUB_SUBSCRIPTION'.
-func WithSubscriptionIDFromDefaultEnv() Option {
- return WithSubscriptionIDFromEnv(DefaultSubscriptionEnvKey)
-}
-
-// AllowCreateTopic sets if the transport can create a topic if it does not
-// exist.
-func AllowCreateTopic(allow bool) Option {
- return func(t *Transport) error {
- t.AllowCreateTopic = allow
- return nil
- }
-}
-
-// AllowCreateSubscription sets if the transport can create a subscription if
-// it does not exist.
-func AllowCreateSubscription(allow bool) Option {
- return func(t *Transport) error {
- t.AllowCreateSubscription = allow
- return nil
- }
-}
-
-// WithReceiveSettings sets the Pubsub ReceiveSettings for pull subscriptions.
-func WithReceiveSettings(rs *pubsub.ReceiveSettings) Option {
- return func(t *Transport) error {
- t.ReceiveSettings = rs
- return nil
- }
-}
diff --git a/v1/cloudevents/transport/pubsub/options_test.go b/v1/cloudevents/transport/pubsub/options_test.go
deleted file mode 100644
index 40fa1c782..000000000
--- a/v1/cloudevents/transport/pubsub/options_test.go
+++ /dev/null
@@ -1,49 +0,0 @@
-package pubsub
-
-import (
- "testing"
-
- "github.com/google/go-cmp/cmp"
- "github.com/google/go-cmp/cmp/cmpopts"
-)
-
-func TestWithEncoding(t *testing.T) {
- testCases := map[string]struct {
- t *Transport
- encoding Encoding
- want *Transport
- wantErr string
- }{
- "valid encoding": {
- t: &Transport{},
- encoding: StructuredV03,
- want: &Transport{
- Encoding: StructuredV03,
- },
- },
- }
- for n, tc := range testCases {
- t.Run(n, func(t *testing.T) {
-
- err := tc.t.applyOptions(WithEncoding(tc.encoding))
-
- if tc.wantErr != "" || err != nil {
- var gotErr string
- if err != nil {
- gotErr = err.Error()
- }
- if diff := cmp.Diff(tc.wantErr, gotErr); diff != "" {
- t.Errorf("unexpected error (-want, +got) = %v", diff)
- }
- return
- }
-
- got := tc.t
-
- if diff := cmp.Diff(tc.want, got,
- cmpopts.IgnoreUnexported(Transport{})); diff != "" {
- t.Errorf("unexpected (-want, +got) = %v", diff)
- }
- })
- }
-}
diff --git a/v1/cloudevents/transport/pubsub/transport.go b/v1/cloudevents/transport/pubsub/transport.go
deleted file mode 100644
index 36be548be..000000000
--- a/v1/cloudevents/transport/pubsub/transport.go
+++ /dev/null
@@ -1,317 +0,0 @@
-package pubsub
-
-import (
- "context"
- "errors"
- "fmt"
- "strings"
- "sync"
-
- "go.uber.org/zap"
-
- "cloud.google.com/go/pubsub"
-
- "github.com/cloudevents/sdk-go/v1/cloudevents"
- cecontext "github.com/cloudevents/sdk-go/v1/cloudevents/context"
- "github.com/cloudevents/sdk-go/v1/cloudevents/transport"
- "github.com/cloudevents/sdk-go/v1/cloudevents/transport/pubsub/internal"
-)
-
-// Transport adheres to transport.Transport.
-var _ transport.Transport = (*Transport)(nil)
-
-const (
- TransportName = "Pub/Sub"
-)
-
-type subscriptionWithTopic struct {
- topicID string
- subscriptionID string
-}
-
-// Transport acts as both a pubsub topic and a pubsub subscription .
-type Transport struct {
- // Encoding
- Encoding Encoding
-
- // DefaultEncodingSelectionFn allows for other encoding selection strategies to be injected.
- DefaultEncodingSelectionFn EncodingSelector
-
- codec transport.Codec
- // Codec Mutex
- coMu sync.Mutex
-
- // PubSub
-
- // ReceiveSettings is used to configure Pubsub pull subscription.
- ReceiveSettings *pubsub.ReceiveSettings
-
- // AllowCreateTopic controls if the transport can create a topic if it does
- // not exist.
- AllowCreateTopic bool
-
- // AllowCreateSubscription controls if the transport can create a
- // subscription if it does not exist.
- AllowCreateSubscription bool
-
- projectID string
- topicID string
- subscriptionID string
-
- gccMux sync.Mutex
-
- subscriptions []subscriptionWithTopic
- client *pubsub.Client
-
- connectionsBySubscription map[string]*internal.Connection
- connectionsByTopic map[string]*internal.Connection
-
- // Receiver
- Receiver transport.Receiver
-
- // Converter is invoked if the incoming transport receives an undecodable
- // message.
- Converter transport.Converter
-}
-
-// New creates a new pubsub transport.
-func New(ctx context.Context, opts ...Option) (*Transport, error) {
- t := &Transport{}
- if err := t.applyOptions(opts...); err != nil {
- return nil, err
- }
-
- if t.client == nil {
- // Auth to pubsub.
- client, err := pubsub.NewClient(ctx, t.projectID)
- if err != nil {
- return nil, err
- }
- // Success.
- t.client = client
- }
-
- if t.connectionsBySubscription == nil {
- t.connectionsBySubscription = make(map[string]*internal.Connection, 0)
- }
-
- if t.connectionsByTopic == nil {
- t.connectionsByTopic = make(map[string]*internal.Connection, 0)
- }
- return t, nil
-}
-
-func (t *Transport) applyOptions(opts ...Option) error {
- for _, fn := range opts {
- if err := fn(t); err != nil {
- return err
- }
- }
- return nil
-}
-
-func (t *Transport) loadCodec(ctx context.Context) bool {
- if t.codec == nil {
- t.coMu.Lock()
- if t.DefaultEncodingSelectionFn != nil && t.Encoding != Default {
- logger := cecontext.LoggerFrom(ctx)
- logger.Warn("transport has a DefaultEncodingSelectionFn set but Encoding is not Default. DefaultEncodingSelectionFn will be ignored.")
-
- t.codec = &Codec{
- Encoding: t.Encoding,
- }
- } else {
- t.codec = &Codec{
- Encoding: t.Encoding,
- DefaultEncodingSelectionFn: t.DefaultEncodingSelectionFn,
- }
- }
- t.coMu.Unlock()
- }
- return true
-}
-
-func (t *Transport) getConnection(ctx context.Context, topic, subscription string) *internal.Connection {
- if subscription != "" {
- if conn, ok := t.connectionsBySubscription[subscription]; ok {
- return conn
- }
- }
- if topic != "" {
- if conn, ok := t.connectionsByTopic[topic]; ok {
- return conn
- }
- }
-
- return nil
-}
-
-func (t *Transport) getOrCreateConnection(ctx context.Context, topic, subscription string) *internal.Connection {
- t.gccMux.Lock()
- defer t.gccMux.Unlock()
-
- // Get.
- if conn := t.getConnection(ctx, topic, subscription); conn != nil {
- return conn
- }
- // Create.
- conn := &internal.Connection{
- AllowCreateSubscription: t.AllowCreateSubscription,
- AllowCreateTopic: t.AllowCreateTopic,
- ReceiveSettings: t.ReceiveSettings,
- Client: t.client,
- ProjectID: t.projectID,
- TopicID: topic,
- SubscriptionID: subscription,
- }
- // Save for later.
- if subscription != "" {
- t.connectionsBySubscription[subscription] = conn
- }
- if topic != "" {
- t.connectionsByTopic[topic] = conn
- }
-
- return conn
-}
-
-// Send implements Transport.Send
-func (t *Transport) Send(ctx context.Context, event cloudevents.Event) (context.Context, *cloudevents.Event, error) {
- // TODO populate response context properly.
- if ok := t.loadCodec(ctx); !ok {
- return ctx, nil, fmt.Errorf("unknown encoding set on transport: %d", t.Encoding)
- }
-
- topic := cecontext.TopicFrom(ctx)
- if topic == "" {
- topic = t.topicID
- }
-
- conn := t.getOrCreateConnection(ctx, topic, "")
-
- msg, err := t.codec.Encode(ctx, event)
- if err != nil {
- return ctx, nil, err
- }
-
- if m, ok := msg.(*Message); ok {
- respEvent, err := conn.Publish(ctx, &pubsub.Message{
- Attributes: m.Attributes,
- Data: m.Data,
- })
- return ctx, respEvent, err
- }
-
- return ctx, nil, fmt.Errorf("failed to encode Event into a Message")
-}
-
-// SetReceiver implements Transport.SetReceiver
-func (t *Transport) SetReceiver(r transport.Receiver) {
- t.Receiver = r
-}
-
-// SetConverter implements Transport.SetConverter
-func (t *Transport) SetConverter(c transport.Converter) {
- t.Converter = c
-}
-
-// HasConverter implements Transport.HasConverter
-func (t *Transport) HasConverter() bool {
- return t.Converter != nil
-}
-
-func (t *Transport) startSubscriber(ctx context.Context, sub subscriptionWithTopic, done func(error)) {
- logger := cecontext.LoggerFrom(ctx)
- logger.Infof("starting subscriber for Topic %q, Subscription %q", sub.topicID, sub.subscriptionID)
- conn := t.getOrCreateConnection(ctx, sub.topicID, sub.subscriptionID)
-
- logger.Info("conn is", conn)
- if conn == nil {
- err := fmt.Errorf("failed to find connection for Topic: %q, Subscription: %q", sub.topicID, sub.subscriptionID)
- done(err)
- return
- }
- // Ok, ready to start pulling.
- err := conn.Receive(ctx, func(ctx context.Context, m *pubsub.Message) {
- msg := &Message{
- Attributes: m.Attributes,
- Data: m.Data,
- }
- event, err := t.codec.Decode(ctx, msg)
- // If codec returns and error, try with the converter if it is set.
- if err != nil && t.HasConverter() {
- event, err = t.Converter.Convert(ctx, msg, err)
- }
- if err != nil {
- logger.Errorw("failed to decode message", zap.Error(err))
- m.Nack()
- return
- }
-
- if err := t.Receiver.Receive(ctx, *event, nil); err != nil {
- logger.Warnw("pubsub receiver return err", zap.Error(err))
- m.Nack()
- return
- }
- m.Ack()
- })
- done(err)
-}
-
-// StartReceiver implements Transport.StartReceiver
-// NOTE: This is a blocking call.
-func (t *Transport) StartReceiver(ctx context.Context) error {
- // Load the codec.
- if ok := t.loadCodec(ctx); !ok {
- return fmt.Errorf("unknown encoding set on transport: %d", t.Encoding)
- }
-
- cctx, cancel := context.WithCancel(ctx)
- defer cancel()
- n := len(t.subscriptions)
-
- // Make the channels for quit and errors.
- quit := make(chan struct{}, n)
- errc := make(chan error, n)
-
- // Start up each subscription.
- for _, sub := range t.subscriptions {
- go t.startSubscriber(cctx, sub, func(err error) {
- if err != nil {
- errc <- err
- } else {
- quit <- struct{}{}
- }
- })
- }
-
- // Collect errors and done calls until we have n of them.
- errs := []string(nil)
- for success := 0; success < n; success++ {
- var err error
- select {
- case <-ctx.Done(): // Block for parent context to finish.
- success--
- case err = <-errc: // Collect errors
- case <-quit:
- }
- if cancel != nil {
- // Stop all other subscriptions.
- cancel()
- cancel = nil
- }
- if err != nil {
- errs = append(errs, err.Error())
- }
- }
-
- close(quit)
- close(errc)
-
- return errors.New(strings.Join(errs, "\n"))
-}
-
-// HasTracePropagation implements Transport.HasTracePropagation
-func (t *Transport) HasTracePropagation() bool {
- return false
-}
diff --git a/v1/cloudevents/transport/transport.go b/v1/cloudevents/transport/transport.go
deleted file mode 100644
index c9a574a3e..000000000
--- a/v1/cloudevents/transport/transport.go
+++ /dev/null
@@ -1,49 +0,0 @@
-package transport
-
-import (
- "context"
-
- "github.com/cloudevents/sdk-go/v1/cloudevents"
-)
-
-// Transport is the interface for transport sender to send the converted Message
-// over the underlying transport.
-type Transport interface {
- Send(context.Context, cloudevents.Event) (context.Context, *cloudevents.Event, error)
-
- SetReceiver(Receiver)
- StartReceiver(context.Context) error
-
- // SetConverter sets the delegate to use for converting messages that have
- // failed to be decoded from known codecs for this transport.
- SetConverter(Converter)
- // HasConverter is true when a non-nil converter has been set.
- HasConverter() bool
- // HasTracePropagation is true when the transport implements
- // in-band trace propagation. When false, the client receiver
- // will propagate trace context from distributed tracing
- // extension attributes when available.
- HasTracePropagation() bool
-}
-
-// Receiver is an interface to define how a transport will invoke a listener
-// of incoming events.
-type Receiver interface {
- Receive(context.Context, cloudevents.Event, *cloudevents.EventResponse) error
-}
-
-// ReceiveFunc wraps a function as a Receiver object.
-type ReceiveFunc func(ctx context.Context, e cloudevents.Event, er *cloudevents.EventResponse) error
-
-// Receive implements Receiver.Receive
-func (f ReceiveFunc) Receive(ctx context.Context, e cloudevents.Event, er *cloudevents.EventResponse) error {
- return f(ctx, e, er)
-}
-
-// Converter is an interface to define how a transport delegate to convert an
-// non-understood transport message from the internal codecs. Providing a
-// Converter allows incoming requests to be bridged to CloudEvents format if
-// they have not been sent as an event in CloudEvents format.
-type Converter interface {
- Convert(context.Context, Message, error) (*cloudevents.Event, error)
-}
diff --git a/v1/cloudevents/types/allocate.go b/v1/cloudevents/types/allocate.go
deleted file mode 100644
index c38f71177..000000000
--- a/v1/cloudevents/types/allocate.go
+++ /dev/null
@@ -1,36 +0,0 @@
-package types
-
-import "reflect"
-
-// Allocate allocates a new instance of type t and returns:
-// asPtr is of type t if t is a pointer type and of type &t otherwise
-// asValue is a Value of type t pointing to the same data as asPtr
-func Allocate(obj interface{}) (asPtr interface{}, asValue reflect.Value) {
- if obj == nil {
- return nil, reflect.Value{}
- }
-
- switch t := reflect.TypeOf(obj); t.Kind() {
- case reflect.Ptr:
- reflectPtr := reflect.New(t.Elem())
- asPtr = reflectPtr.Interface()
- asValue = reflectPtr
- case reflect.Map:
- reflectPtr := reflect.MakeMap(t)
- asPtr = reflectPtr.Interface()
- asValue = reflectPtr
- case reflect.String:
- reflectPtr := reflect.New(t)
- asPtr = ""
- asValue = reflectPtr.Elem()
- case reflect.Slice:
- reflectPtr := reflect.MakeSlice(t, 0, 0)
- asPtr = reflectPtr.Interface()
- asValue = reflectPtr
- default:
- reflectPtr := reflect.New(t)
- asPtr = reflectPtr.Interface()
- asValue = reflectPtr.Elem()
- }
- return
-}
diff --git a/v1/cloudevents/types/allocate_test.go b/v1/cloudevents/types/allocate_test.go
deleted file mode 100644
index a092d10be..000000000
--- a/v1/cloudevents/types/allocate_test.go
+++ /dev/null
@@ -1,75 +0,0 @@
-package types_test
-
-import (
- "testing"
- "time"
-
- "github.com/cloudevents/sdk-go/v1/cloudevents/types"
- "github.com/google/go-cmp/cmp"
-)
-
-type DataExample struct {
- AnInt int `json:"a,omitempty"`
- AString string `json:"b,omitempty"`
- AnArray []string `json:"c,omitempty"`
- AMap map[string]map[string]int `json:"d,omitempty"`
- ATime *time.Time `json:"e,omitempty"`
-}
-
-func TestAllocate(t *testing.T) {
-
- emptyString := ""
- exampleString := "howdy"
-
- testCases := map[string]struct {
- obj interface{}
- want interface{}
- }{
- "nil": {
- obj: nil,
- want: nil,
- },
- "map": {
- obj: map[string]string{
- "test": "case",
- },
- want: map[string]string{},
- },
- "slice": {
- obj: []string{
- "test",
- "case",
- },
- want: []string{},
- },
- "string": {
- obj: "hello",
- want: "",
- },
- "string ptr": {
- obj: &exampleString,
- want: &emptyString,
- },
- "struct": {
- obj: DataExample{
- AnInt: 42,
- },
- want: &DataExample{},
- },
- "pointer": {
- obj: &DataExample{
- AnInt: 42,
- },
- want: &DataExample{},
- },
- }
- for n, tc := range testCases {
- tc := tc // Don't use range variable in func literal.
- t.Run(n, func(t *testing.T) {
- got, _ := types.Allocate(tc.obj)
- if diff := cmp.Diff(tc.want, got); diff != "" {
- t.Errorf("unexpected object (-want, +got) = %v", diff)
- }
- })
- }
-}
diff --git a/v1/cloudevents/types/doc.go b/v1/cloudevents/types/doc.go
deleted file mode 100644
index b1d9c29da..000000000
--- a/v1/cloudevents/types/doc.go
+++ /dev/null
@@ -1,41 +0,0 @@
-/*
-Package types implements the CloudEvents type system.
-
-CloudEvents defines a set of abstract types for event context attributes. Each
-type has a corresponding native Go type and a canonical string encoding. The
-native Go types used to represent the CloudEvents types are:
-bool, int32, string, []byte, *url.URL, time.Time
-
- +----------------+----------------+-----------------------------------+
- |CloudEvents Type|Native Type |Convertible From |
- +================+================+===================================+
- |Bool |bool |bool |
- +----------------+----------------+-----------------------------------+
- |Integer |int32 |Any numeric type with value in |
- | | |range of int32 |
- +----------------+----------------+-----------------------------------+
- |String |string |string |
- +----------------+----------------+-----------------------------------+
- |Binary |[]byte |[]byte |
- +----------------+----------------+-----------------------------------+
- |URI-Reference |*url.URL |url.URL, types.URIRef, types.URI |
- +----------------+----------------+-----------------------------------+
- |URI |*url.URL |url.URL, types.URIRef, types.URI |
- | | |Must be an absolute URI. |
- +----------------+----------------+-----------------------------------+
- |Timestamp |time.Time |time.Time, types.Timestamp |
- +----------------+----------------+-----------------------------------+
-
-Extension attributes may be stored as a native type or a canonical string. The
-To functions will convert to the desired from any convertible type
-or from the canonical string form.
-
-The Parse and Format functions convert native types to/from
-canonical strings.
-
-Note are no Parse or Format functions for URL or string. For URL use the
-standard url.Parse() and url.URL.String(). The canonical string format of a
-string is the string itself.
-
-*/
-package types
diff --git a/v1/cloudevents/types/timestamp.go b/v1/cloudevents/types/timestamp.go
deleted file mode 100644
index 3ae1c7def..000000000
--- a/v1/cloudevents/types/timestamp.go
+++ /dev/null
@@ -1,70 +0,0 @@
-package types
-
-import (
- "encoding/json"
- "encoding/xml"
- "fmt"
- "time"
-)
-
-// Timestamp wraps time.Time to normalize the time layout to RFC3339. It is
-// intended to enforce compliance with the CloudEvents spec for their
-// definition of Timestamp. Custom marshal methods are implemented to ensure
-// the outbound Timestamp is a string in the RFC3339 layout.
-type Timestamp struct {
- time.Time
-}
-
-// ParseTimestamp attempts to parse the given time assuming RFC3339 layout
-func ParseTimestamp(s string) (*Timestamp, error) {
- if s == "" {
- return nil, nil
- }
- tt, err := ParseTime(s)
- return &Timestamp{Time: tt}, err
-}
-
-// MarshalJSON implements a custom json marshal method used when this type is
-// marshaled using json.Marshal.
-func (t *Timestamp) MarshalJSON() ([]byte, error) {
- if t == nil || t.IsZero() {
- return []byte(`""`), nil
- }
- return []byte(fmt.Sprintf("%q", t)), nil
-}
-
-// UnmarshalJSON implements the json unmarshal method used when this type is
-// unmarshaled using json.Unmarshal.
-func (t *Timestamp) UnmarshalJSON(b []byte) error {
- var timestamp string
- if err := json.Unmarshal(b, ×tamp); err != nil {
- return err
- }
- var err error
- t.Time, err = ParseTime(timestamp)
- return err
-}
-
-// MarshalXML implements a custom xml marshal method used when this type is
-// marshaled using xml.Marshal.
-func (t *Timestamp) MarshalXML(e *xml.Encoder, start xml.StartElement) error {
- if t == nil || t.IsZero() {
- return e.EncodeElement(nil, start)
- }
- return e.EncodeElement(t.String(), start)
-}
-
-// UnmarshalXML implements the xml unmarshal method used when this type is
-// unmarshaled using xml.Unmarshal.
-func (t *Timestamp) UnmarshalXML(d *xml.Decoder, start xml.StartElement) error {
- var timestamp string
- if err := d.DecodeElement(×tamp, &start); err != nil {
- return err
- }
- var err error
- t.Time, err = ParseTime(timestamp)
- return err
-}
-
-// String outputs the time using RFC3339 format.
-func (t Timestamp) String() string { return FormatTime(t.Time) }
diff --git a/v1/cloudevents/types/timestamp_test.go b/v1/cloudevents/types/timestamp_test.go
deleted file mode 100644
index b6706c98a..000000000
--- a/v1/cloudevents/types/timestamp_test.go
+++ /dev/null
@@ -1,82 +0,0 @@
-package types_test
-
-import (
- "encoding/json"
- "encoding/xml"
- "fmt"
- "testing"
- "time"
-
- "github.com/cloudevents/sdk-go/v1/cloudevents/types"
- "github.com/stretchr/testify/assert"
-)
-
-func TestTimestampParseString(t *testing.T) {
- ok := func(s string, want time.Time) {
- t.Helper()
- got, err := types.ParseTimestamp(s)
- assert.NoError(t, err)
- assert.Equal(t, want, got.Time)
- assert.Equal(t, s, got.String())
- }
- ok("1984-02-28T15:04:05Z", time.Date(1984, 02, 28, 15, 04, 05, 0, time.UTC))
- ok("1984-02-28T15:04:05.999999999Z", time.Date(1984, 02, 28, 15, 04, 05, 999999999, time.UTC))
-
- bad := func(s, wanterr string) {
- t.Helper()
- _, err := types.ParseTime(s)
- assert.EqualError(t, err, wanterr)
- }
- bad("", "cannot convert \"\" to time.Time: not in RFC3339 format")
- bad("2019-02-28", "cannot convert \"2019-02-28\" to time.Time: not in RFC3339 format")
-}
-
-func TestJsonMarshalUnmarshalTimestamp(t *testing.T) {
- ok := func(ts string) {
- t.Helper()
- tt, err := types.ParseTime(ts)
- assert.NoError(t, err)
- got, err := json.Marshal(types.Timestamp{Time: tt})
- assert.NoError(t, err)
- assert.Equal(t, fmt.Sprintf(`"%s"`, ts), string(got))
- }
- ok("1984-02-28T15:04:05Z")
- ok("1984-02-28T15:04:05.999999999Z")
-
- bad := func(s, wanterr string) {
- t.Helper()
- var ts types.Timestamp
- err := json.Unmarshal([]byte(s), &ts)
- assert.EqualError(t, err, wanterr)
- }
- bad("", "unexpected end of JSON input")
- bad("2019-02-28", "invalid character '-' after top-level value")
-}
-
-func TestXMLMarshalUnmarshalTimestamp(t *testing.T) {
- ok := func(tstr string) {
- t.Helper()
- tt, err := types.ParseTime(tstr)
- assert.NoError(t, err)
- got, err := xml.Marshal(types.Timestamp{Time: tt})
- assert.NoError(t, err)
- assert.Equal(t, fmt.Sprintf("%s", tstr), string(got))
- var ts types.Timestamp
- err = xml.Unmarshal(got, &ts)
- assert.NoError(t, err)
- assert.Equal(t, tt, ts.Time)
- }
- ok("1984-02-28T15:04:05Z")
- ok("1984-02-28T15:04:05.999999999Z")
-
- bad := func(s, wanterr string) {
- t.Helper()
- var ts types.Timestamp
- err := xml.Unmarshal([]byte(s), &ts)
- assert.EqualError(t, err, wanterr)
- }
- bad("", "EOF")
- bad("2019-02-28", "EOF")
- bad("2019-02-28", "cannot convert \"2019-02-28\" to time.Time: not in RFC3339 format")
- bad("", "cannot convert \"\" to time.Time: not in RFC3339 format")
-}
diff --git a/v1/cloudevents/types/uri.go b/v1/cloudevents/types/uri.go
deleted file mode 100644
index 97248a24d..000000000
--- a/v1/cloudevents/types/uri.go
+++ /dev/null
@@ -1,77 +0,0 @@
-package types
-
-import (
- "encoding/json"
- "encoding/xml"
- "fmt"
- "net/url"
-)
-
-// URI is a wrapper to url.URL. It is intended to enforce compliance with
-// the CloudEvents spec for their definition of URI. Custom
-// marshal methods are implemented to ensure the outbound URI object
-// is a flat string.
-type URI struct {
- url.URL
-}
-
-// ParseURI attempts to parse the given string as a URI.
-func ParseURI(u string) *URI {
- if u == "" {
- return nil
- }
- pu, err := url.Parse(u)
- if err != nil {
- return nil
- }
- return &URI{URL: *pu}
-}
-
-// MarshalJSON implements a custom json marshal method used when this type is
-// marshaled using json.Marshal.
-func (u URI) MarshalJSON() ([]byte, error) {
- b := fmt.Sprintf("%q", u.String())
- return []byte(b), nil
-}
-
-// UnmarshalJSON implements the json unmarshal method used when this type is
-// unmarshaled using json.Unmarshal.
-func (u *URI) UnmarshalJSON(b []byte) error {
- var ref string
- if err := json.Unmarshal(b, &ref); err != nil {
- return err
- }
- r := ParseURI(ref)
- if r != nil {
- *u = *r
- }
- return nil
-}
-
-// MarshalXML implements a custom xml marshal method used when this type is
-// marshaled using xml.Marshal.
-func (u URI) MarshalXML(e *xml.Encoder, start xml.StartElement) error {
- return e.EncodeElement(u.String(), start)
-}
-
-// UnmarshalXML implements the xml unmarshal method used when this type is
-// unmarshaled using xml.Unmarshal.
-func (u *URI) UnmarshalXML(d *xml.Decoder, start xml.StartElement) error {
- var ref string
- if err := d.DecodeElement(&ref, &start); err != nil {
- return err
- }
- r := ParseURI(ref)
- if r != nil {
- *u = *r
- }
- return nil
-}
-
-// String returns the full string representation of the URI-Reference.
-func (u *URI) String() string {
- if u == nil {
- return ""
- }
- return u.URL.String()
-}
diff --git a/v1/cloudevents/types/uri_test.go b/v1/cloudevents/types/uri_test.go
deleted file mode 100644
index 9b9725848..000000000
--- a/v1/cloudevents/types/uri_test.go
+++ /dev/null
@@ -1,292 +0,0 @@
-package types_test
-
-import (
- "encoding/xml"
- "net/url"
- "testing"
-
- "github.com/cloudevents/sdk-go/v1/cloudevents/types"
- "github.com/google/go-cmp/cmp"
-)
-
-func TestParseURL(t *testing.T) {
- testCases := map[string]struct {
- t string
- want *types.URI
- }{
- "empty": {
- want: nil,
- },
- "empty string": {
- t: "",
- want: nil,
- },
- "invalid format": {
- t: "💩://error",
- want: nil,
- },
- "relative": {
- t: "/path/to/something",
- want: func() *types.URI {
- u, _ := url.Parse("/path/to/something")
- return &types.URI{URL: *u}
- }(),
- },
- "url": {
- t: "http://path/to/something",
- want: func() *types.URI {
- u, _ := url.Parse("http://path/to/something")
- return &types.URI{URL: *u}
- }(),
- },
- "mailto": {
- t: "mailto:cncf-wg-serverless@lists.cncf.io",
- want: func() *types.URI {
- u, _ := url.Parse("mailto:cncf-wg-serverless@lists.cncf.io")
- return &types.URI{URL: *u}
- }(),
- },
- "urn": {
- t: "urn:uuid:6e8bc430-9c3a-11d9-9669-0800200c9a66",
- want: func() *types.URI {
- u, _ := url.Parse("urn:uuid:6e8bc430-9c3a-11d9-9669-0800200c9a66")
- return &types.URI{URL: *u}
- }(),
- },
- "id3": {
- t: "1-555-123-4567",
- want: func() *types.URI {
- u, _ := url.Parse("1-555-123-4567")
- return &types.URI{URL: *u}
- }(),
- },
- }
- for n, tc := range testCases {
- tc := tc // Don't use range variable in func literal.
- t.Run(n, func(t *testing.T) {
-
- got := types.ParseURI(tc.t)
-
- if diff := cmp.Diff(tc.want, got); diff != "" {
- t.Errorf("unexpected object (-want, +got) = %v", diff)
- }
- })
- }
-}
-
-func TestJsonMarshalURL(t *testing.T) {
- testCases := map[string]struct {
- t string
- want []byte
- }{
- "empty": {},
- "empty string": {
- t: "",
- },
- "invalid url": {
- t: "not a url",
- want: []byte(`"not%20a%20url"`),
- },
- "relative format": {
- t: "/path/to/something",
- want: []byte(`"/path/to/something"`),
- },
- }
- for n, tc := range testCases {
- tc := tc // Don't use range variable in func literal.
- t.Run(n, func(t *testing.T) {
-
- var got []byte
- tt := types.ParseURI(tc.t)
- if tt != nil {
- got, _ = tt.MarshalJSON()
- }
-
- if diff := cmp.Diff(tc.want, got); diff != "" {
- t.Logf("got: %s", string(got))
- t.Errorf("unexpected object (-want, +got) = %v", diff)
- }
- })
- }
-}
-
-func TestXMLMarshalURI(t *testing.T) {
- testCases := map[string]struct {
- t string
- want []byte
- }{
- "empty": {},
- "empty string": {
- t: "",
- },
- "invalid url": {
- t: "not a url",
- want: []byte(`not%20a%20url`),
- },
- "relative format": {
- t: "/path/to/something",
- want: []byte(`/path/to/something`),
- },
- }
- for n, tc := range testCases {
- tc := tc // Don't use range variable in func literal.
- t.Run(n, func(t *testing.T) {
-
- var got []byte
- tt := types.ParseURI(tc.t)
- if tt != nil {
- got, _ = xml.Marshal(tt)
- }
-
- if diff := cmp.Diff(tc.want, got); diff != "" {
- t.Logf("got: %s", string(got))
- t.Errorf("unexpected object (-want, +got) = %v", diff)
- }
- })
- }
-}
-
-func TestJsonUnmarshalURI(t *testing.T) {
- testCases := map[string]struct {
- b []byte
- want *types.URI
- wantErr string
- }{
- "empty": {
- wantErr: "unexpected end of JSON input",
- },
- "invalid format": {
- b: []byte("%"),
- wantErr: "invalid character '%' looking for beginning of value",
- },
- "relative": {
- b: []byte(`"/path/to/something"`),
- want: func() *types.URI {
- u, _ := url.Parse("/path/to/something")
- return &types.URI{URL: *u}
- }(),
- },
- "url": {
- b: []byte(`"http://path/to/something"`),
- want: func() *types.URI {
- u, _ := url.Parse("http://path/to/something")
- return &types.URI{URL: *u}
- }(),
- },
- }
- for n, tc := range testCases {
- tc := tc // Don't use range variable in func literal.
- t.Run(n, func(t *testing.T) {
-
- got := &types.URI{}
- err := got.UnmarshalJSON(tc.b)
-
- if tc.wantErr != "" || err != nil {
- var gotErr string
- if err != nil {
- gotErr = err.Error()
- }
- if diff := cmp.Diff(tc.wantErr, gotErr); diff != "" {
- t.Errorf("unexpected error (-want, +got) = %v", diff)
- }
- return
- }
-
- if diff := cmp.Diff(tc.want, got); diff != "" {
- t.Errorf("unexpected object (-want, +got) = %v", diff)
- }
- })
- }
-}
-
-func TestXMLUnmarshalURI(t *testing.T) {
- testCases := map[string]struct {
- b []byte
- want *types.URI
- wantErr string
- }{
- "empty": {
- wantErr: "EOF",
- },
- "invalid format": {
- b: []byte(`%`),
- want: &types.URI{},
- },
- "bad xml": {
- b: []byte(`%`),
- wantErr: "XML syntax error on line 1: element closed by ",
- },
- "relative": {
- b: []byte(`/path/to/something`),
- want: func() *types.URI {
- u, _ := url.Parse("/path/to/something")
- return &types.URI{URL: *u}
- }(),
- },
- "url": {
- b: []byte(`http://path/to/something`),
- want: func() *types.URI {
- u, _ := url.Parse("http://path/to/something")
- return &types.URI{URL: *u}
- }(),
- },
- }
- for n, tc := range testCases {
- tc := tc // Don't use range variable in func literal.
- t.Run(n, func(t *testing.T) {
-
- got := &types.URI{}
-
- err := xml.Unmarshal(tc.b, got)
-
- if tc.wantErr != "" || err != nil {
- var gotErr string
- if err != nil {
- gotErr = err.Error()
- }
- if diff := cmp.Diff(tc.wantErr, gotErr); diff != "" {
- t.Errorf("unexpected error (-want, +got) = %v", diff)
- }
- return
- }
-
- if diff := cmp.Diff(tc.want, got); diff != "" {
- t.Errorf("unexpected object (-want, +got) = %v", diff)
- }
- })
- }
-}
-
-func TestURIString(t *testing.T) {
- testCases := map[string]struct {
- t string
- want string
- }{
- "empty": {
- want: "",
- },
- "relative": {
- t: "/path/to/something",
- want: "/path/to/something",
- },
- "url": {
- t: "http://path/to/something",
- want: "http://path/to/something",
- },
- }
- for n, tc := range testCases {
- tc := tc // Don't use range variable in func literal.
- t.Run(n, func(t *testing.T) {
-
- tt := types.ParseURI(tc.t)
- got := tt.String()
-
- if diff := cmp.Diff(tc.want, got); diff != "" {
- t.Logf("got: %s", got)
- t.Errorf("unexpected string (-want, +got) = %v", diff)
- }
- })
- }
-}
-
-// TODO: Test xml: MarshalXML(e *xml.Encoder, start xml.StartElement) error {
diff --git a/v1/cloudevents/types/uriref.go b/v1/cloudevents/types/uriref.go
deleted file mode 100644
index e19a1dbb7..000000000
--- a/v1/cloudevents/types/uriref.go
+++ /dev/null
@@ -1,77 +0,0 @@
-package types
-
-import (
- "encoding/json"
- "encoding/xml"
- "fmt"
- "net/url"
-)
-
-// URIRef is a wrapper to url.URL. It is intended to enforce compliance with
-// the CloudEvents spec for their definition of URI-Reference. Custom
-// marshal methods are implemented to ensure the outbound URIRef object is
-// is a flat string.
-type URIRef struct {
- url.URL
-}
-
-// ParseURIRef attempts to parse the given string as a URI-Reference.
-func ParseURIRef(u string) *URIRef {
- if u == "" {
- return nil
- }
- pu, err := url.Parse(u)
- if err != nil {
- return nil
- }
- return &URIRef{URL: *pu}
-}
-
-// MarshalJSON implements a custom json marshal method used when this type is
-// marshaled using json.Marshal.
-func (u URIRef) MarshalJSON() ([]byte, error) {
- b := fmt.Sprintf("%q", u.String())
- return []byte(b), nil
-}
-
-// UnmarshalJSON implements the json unmarshal method used when this type is
-// unmarshaled using json.Unmarshal.
-func (u *URIRef) UnmarshalJSON(b []byte) error {
- var ref string
- if err := json.Unmarshal(b, &ref); err != nil {
- return err
- }
- r := ParseURIRef(ref)
- if r != nil {
- *u = *r
- }
- return nil
-}
-
-// MarshalXML implements a custom xml marshal method used when this type is
-// marshaled using xml.Marshal.
-func (u URIRef) MarshalXML(e *xml.Encoder, start xml.StartElement) error {
- return e.EncodeElement(u.String(), start)
-}
-
-// UnmarshalXML implements the xml unmarshal method used when this type is
-// unmarshaled using xml.Unmarshal.
-func (u *URIRef) UnmarshalXML(d *xml.Decoder, start xml.StartElement) error {
- var ref string
- if err := d.DecodeElement(&ref, &start); err != nil {
- return err
- }
- r := ParseURIRef(ref)
- if r != nil {
- *u = *r
- }
- return nil
-}
-
-// String returns the full string representation of the URI-Reference.
-func (u *URIRef) String() string {
- if u == nil {
- return ""
- }
- return u.URL.String()
-}
diff --git a/v1/cloudevents/types/uriref_test.go b/v1/cloudevents/types/uriref_test.go
deleted file mode 100644
index a2ce66769..000000000
--- a/v1/cloudevents/types/uriref_test.go
+++ /dev/null
@@ -1,271 +0,0 @@
-package types_test
-
-import (
- "encoding/xml"
- "net/url"
- "testing"
-
- "github.com/cloudevents/sdk-go/v1/cloudevents/types"
- "github.com/google/go-cmp/cmp"
-)
-
-func TestParseURIRef(t *testing.T) {
- testCases := map[string]struct {
- t string
- want *types.URIRef
- }{
- "empty": {
- want: nil,
- },
- "empty string": {
- t: "",
- want: nil,
- },
- "invalid format": {
- t: "💩://error",
- want: nil,
- },
- "relative": {
- t: "/path/to/something",
- want: func() *types.URIRef {
- u, _ := url.Parse("/path/to/something")
- return &types.URIRef{URL: *u}
- }(),
- },
- "url": {
- t: "http://path/to/something",
- want: func() *types.URIRef {
- u, _ := url.Parse("http://path/to/something")
- return &types.URIRef{URL: *u}
- }(),
- },
- }
- for n, tc := range testCases {
- tc := tc // Don't use range variable in func literal.
- t.Run(n, func(t *testing.T) {
-
- got := types.ParseURIRef(tc.t)
-
- if diff := cmp.Diff(tc.want, got); diff != "" {
- t.Errorf("unexpected object (-want, +got) = %v", diff)
- }
- })
- }
-}
-
-func TestJsonMarshalURIRef(t *testing.T) {
- testCases := map[string]struct {
- t string
- want []byte
- }{
- "empty": {},
- "empty string": {
- t: "",
- },
- "invalid url": {
- t: "not a url",
- want: []byte(`"not%20a%20url"`),
- },
- "relative format": {
- t: "/path/to/something",
- want: []byte(`"/path/to/something"`),
- },
- }
- for n, tc := range testCases {
- tc := tc // Don't use range variable in func literal.
- t.Run(n, func(t *testing.T) {
-
- var got []byte
- tt := types.ParseURIRef(tc.t)
- if tt != nil {
- got, _ = tt.MarshalJSON()
- }
-
- if diff := cmp.Diff(tc.want, got); diff != "" {
- t.Logf("got: %s", string(got))
- t.Errorf("unexpected object (-want, +got) = %v", diff)
- }
- })
- }
-}
-
-func TestXMLMarshalURIRef(t *testing.T) {
- testCases := map[string]struct {
- t string
- want []byte
- }{
- "empty": {},
- "empty string": {
- t: "",
- },
- "invalid url": {
- t: "not a url",
- want: []byte(`not%20a%20url`),
- },
- "relative format": {
- t: "/path/to/something",
- want: []byte(`/path/to/something`),
- },
- }
- for n, tc := range testCases {
- tc := tc // Don't use range variable in func literal.
- t.Run(n, func(t *testing.T) {
-
- var got []byte
- tt := types.ParseURIRef(tc.t)
- if tt != nil {
- got, _ = xml.Marshal(tt)
- }
-
- if diff := cmp.Diff(tc.want, got); diff != "" {
- t.Logf("got: %s", string(got))
- t.Errorf("unexpected object (-want, +got) = %v", diff)
- }
- })
- }
-}
-
-func TestJsonUnmarshalURIRef(t *testing.T) {
- testCases := map[string]struct {
- b []byte
- want *types.URIRef
- wantErr string
- }{
- "empty": {
- wantErr: "unexpected end of JSON input",
- },
- "invalid format": {
- b: []byte("%"),
- wantErr: "invalid character '%' looking for beginning of value",
- },
- "relative": {
- b: []byte(`"/path/to/something"`),
- want: func() *types.URIRef {
- u, _ := url.Parse("/path/to/something")
- return &types.URIRef{URL: *u}
- }(),
- },
- "url": {
- b: []byte(`"http://path/to/something"`),
- want: func() *types.URIRef {
- u, _ := url.Parse("http://path/to/something")
- return &types.URIRef{URL: *u}
- }(),
- },
- }
- for n, tc := range testCases {
- tc := tc // Don't use range variable in func literal.
- t.Run(n, func(t *testing.T) {
-
- got := &types.URIRef{}
- err := got.UnmarshalJSON(tc.b)
-
- if tc.wantErr != "" || err != nil {
- var gotErr string
- if err != nil {
- gotErr = err.Error()
- }
- if diff := cmp.Diff(tc.wantErr, gotErr); diff != "" {
- t.Errorf("unexpected error (-want, +got) = %v", diff)
- }
- return
- }
-
- if diff := cmp.Diff(tc.want, got); diff != "" {
- t.Errorf("unexpected object (-want, +got) = %v", diff)
- }
- })
- }
-}
-
-func TestXMLUnmarshalURIRef(t *testing.T) {
- testCases := map[string]struct {
- b []byte
- want *types.URIRef
- wantErr string
- }{
- "empty": {
- wantErr: "EOF",
- },
- "invalid format": {
- b: []byte(`%`),
- want: &types.URIRef{},
- },
- "bad xml": {
- b: []byte(`%`),
- wantErr: "XML syntax error on line 1: element closed by ",
- },
- "relative": {
- b: []byte(`/path/to/something`),
- want: func() *types.URIRef {
- u, _ := url.Parse("/path/to/something")
- return &types.URIRef{URL: *u}
- }(),
- },
- "url": {
- b: []byte(`http://path/to/something`),
- want: func() *types.URIRef {
- u, _ := url.Parse("http://path/to/something")
- return &types.URIRef{URL: *u}
- }(),
- },
- }
- for n, tc := range testCases {
- tc := tc // Don't use range variable in func literal.
- t.Run(n, func(t *testing.T) {
-
- got := &types.URIRef{}
-
- err := xml.Unmarshal(tc.b, got)
-
- if tc.wantErr != "" || err != nil {
- var gotErr string
- if err != nil {
- gotErr = err.Error()
- }
- if diff := cmp.Diff(tc.wantErr, gotErr); diff != "" {
- t.Errorf("unexpected error (-want, +got) = %v", diff)
- }
- return
- }
-
- if diff := cmp.Diff(tc.want, got); diff != "" {
- t.Errorf("unexpected object (-want, +got) = %v", diff)
- }
- })
- }
-}
-
-func TestURIRefString(t *testing.T) {
- testCases := map[string]struct {
- t string
- want string
- }{
- "empty": {
- want: "",
- },
- "relative": {
- t: "/path/to/something",
- want: "/path/to/something",
- },
- "url": {
- t: "http://path/to/something",
- want: "http://path/to/something",
- },
- }
- for n, tc := range testCases {
- tc := tc // Don't use range variable in func literal.
- t.Run(n, func(t *testing.T) {
-
- tt := types.ParseURIRef(tc.t)
- got := tt.String()
-
- if diff := cmp.Diff(tc.want, got); diff != "" {
- t.Logf("got: %s", got)
- t.Errorf("unexpected string (-want, +got) = %v", diff)
- }
- })
- }
-}
-
-// TODO: Test xml: MarshalXML(e *xml.Encoder, start xml.StartElement) error {
diff --git a/v1/cloudevents/types/urlref.go b/v1/cloudevents/types/urlref.go
deleted file mode 100644
index 2578801cd..000000000
--- a/v1/cloudevents/types/urlref.go
+++ /dev/null
@@ -1,79 +0,0 @@
-package types
-
-import (
- "encoding/json"
- "encoding/xml"
- "fmt"
- "net/url"
-)
-
-// URLRef is a wrapper to url.URL. It is intended to enforce compliance with
-// the CloudEvents spec for their definition of URI-Reference. Custom
-// marshal methods are implemented to ensure the outbound URLRef object is
-// is a flat string.
-//
-// deprecated: use URIRef.
-type URLRef struct {
- url.URL
-}
-
-// ParseURLRef attempts to parse the given string as a URI-Reference.
-func ParseURLRef(u string) *URLRef {
- if u == "" {
- return nil
- }
- pu, err := url.Parse(u)
- if err != nil {
- return nil
- }
- return &URLRef{URL: *pu}
-}
-
-// MarshalJSON implements a custom json marshal method used when this type is
-// marshaled using json.Marshal.
-func (u URLRef) MarshalJSON() ([]byte, error) {
- b := fmt.Sprintf("%q", u.String())
- return []byte(b), nil
-}
-
-// UnmarshalJSON implements the json unmarshal method used when this type is
-// unmarshaled using json.Unmarshal.
-func (u *URLRef) UnmarshalJSON(b []byte) error {
- var ref string
- if err := json.Unmarshal(b, &ref); err != nil {
- return err
- }
- r := ParseURLRef(ref)
- if r != nil {
- *u = *r
- }
- return nil
-}
-
-// MarshalXML implements a custom xml marshal method used when this type is
-// marshaled using xml.Marshal.
-func (u URLRef) MarshalXML(e *xml.Encoder, start xml.StartElement) error {
- return e.EncodeElement(u.String(), start)
-}
-
-// UnmarshalXML implements the xml unmarshal method used when this type is
-// unmarshaled using xml.Unmarshal.
-func (u *URLRef) UnmarshalXML(d *xml.Decoder, start xml.StartElement) error {
- var ref string
- if err := d.DecodeElement(&ref, &start); err != nil {
- return err
- }
- r := ParseURLRef(ref)
- if r != nil {
- *u = *r
- }
- return nil
-}
-
-// String returns the full string representation of the URI-Reference.
-func (u *URLRef) String() string {
- if u == nil {
- return ""
- }
- return u.URL.String()
-}
diff --git a/v1/cloudevents/types/urlref_test.go b/v1/cloudevents/types/urlref_test.go
deleted file mode 100644
index 76450ae3b..000000000
--- a/v1/cloudevents/types/urlref_test.go
+++ /dev/null
@@ -1,271 +0,0 @@
-package types_test
-
-import (
- "encoding/xml"
- "net/url"
- "testing"
-
- "github.com/cloudevents/sdk-go/v1/cloudevents/types"
- "github.com/google/go-cmp/cmp"
-)
-
-func TestParseURLRef(t *testing.T) {
- testCases := map[string]struct {
- t string
- want *types.URLRef
- }{
- "empty": {
- want: nil,
- },
- "empty string": {
- t: "",
- want: nil,
- },
- "invalid format": {
- t: "💩://error",
- want: nil,
- },
- "relative": {
- t: "/path/to/something",
- want: func() *types.URLRef {
- u, _ := url.Parse("/path/to/something")
- return &types.URLRef{URL: *u}
- }(),
- },
- "url": {
- t: "http://path/to/something",
- want: func() *types.URLRef {
- u, _ := url.Parse("http://path/to/something")
- return &types.URLRef{URL: *u}
- }(),
- },
- }
- for n, tc := range testCases {
- tc := tc // Don't use range variable in func literal.
- t.Run(n, func(t *testing.T) {
-
- got := types.ParseURLRef(tc.t)
-
- if diff := cmp.Diff(tc.want, got); diff != "" {
- t.Errorf("unexpected object (-want, +got) = %v", diff)
- }
- })
- }
-}
-
-func TestJsonMarshalURLRef(t *testing.T) {
- testCases := map[string]struct {
- t string
- want []byte
- }{
- "empty": {},
- "empty string": {
- t: "",
- },
- "invalid url": {
- t: "not a url",
- want: []byte(`"not%20a%20url"`),
- },
- "relative format": {
- t: "/path/to/something",
- want: []byte(`"/path/to/something"`),
- },
- }
- for n, tc := range testCases {
- tc := tc // Don't use range variable in func literal.
- t.Run(n, func(t *testing.T) {
-
- var got []byte
- tt := types.ParseURLRef(tc.t)
- if tt != nil {
- got, _ = tt.MarshalJSON()
- }
-
- if diff := cmp.Diff(tc.want, got); diff != "" {
- t.Logf("got: %s", string(got))
- t.Errorf("unexpected object (-want, +got) = %v", diff)
- }
- })
- }
-}
-
-func TestXMLMarshalURLRef(t *testing.T) {
- testCases := map[string]struct {
- t string
- want []byte
- }{
- "empty": {},
- "empty string": {
- t: "",
- },
- "invalid url": {
- t: "not a url",
- want: []byte(`not%20a%20url`),
- },
- "relative format": {
- t: "/path/to/something",
- want: []byte(`/path/to/something`),
- },
- }
- for n, tc := range testCases {
- tc := tc // Don't use range variable in func literal.
- t.Run(n, func(t *testing.T) {
-
- var got []byte
- tt := types.ParseURLRef(tc.t)
- if tt != nil {
- got, _ = xml.Marshal(tt)
- }
-
- if diff := cmp.Diff(tc.want, got); diff != "" {
- t.Logf("got: %s", string(got))
- t.Errorf("unexpected object (-want, +got) = %v", diff)
- }
- })
- }
-}
-
-func TestJsonUnmarshalURLRef(t *testing.T) {
- testCases := map[string]struct {
- b []byte
- want *types.URLRef
- wantErr string
- }{
- "empty": {
- wantErr: "unexpected end of JSON input",
- },
- "invalid format": {
- b: []byte("%"),
- wantErr: "invalid character '%' looking for beginning of value",
- },
- "relative": {
- b: []byte(`"/path/to/something"`),
- want: func() *types.URLRef {
- u, _ := url.Parse("/path/to/something")
- return &types.URLRef{URL: *u}
- }(),
- },
- "url": {
- b: []byte(`"http://path/to/something"`),
- want: func() *types.URLRef {
- u, _ := url.Parse("http://path/to/something")
- return &types.URLRef{URL: *u}
- }(),
- },
- }
- for n, tc := range testCases {
- tc := tc // Don't use range variable in func literal.
- t.Run(n, func(t *testing.T) {
-
- got := &types.URLRef{}
- err := got.UnmarshalJSON(tc.b)
-
- if tc.wantErr != "" || err != nil {
- var gotErr string
- if err != nil {
- gotErr = err.Error()
- }
- if diff := cmp.Diff(tc.wantErr, gotErr); diff != "" {
- t.Errorf("unexpected error (-want, +got) = %v", diff)
- }
- return
- }
-
- if diff := cmp.Diff(tc.want, got); diff != "" {
- t.Errorf("unexpected object (-want, +got) = %v", diff)
- }
- })
- }
-}
-
-func TestXMLUnmarshalURLRef(t *testing.T) {
- testCases := map[string]struct {
- b []byte
- want *types.URLRef
- wantErr string
- }{
- "empty": {
- wantErr: "EOF",
- },
- "invalid format": {
- b: []byte(`%`),
- want: &types.URLRef{},
- },
- "bad xml": {
- b: []byte(`%`),
- wantErr: "XML syntax error on line 1: element closed by ",
- },
- "relative": {
- b: []byte(`/path/to/something`),
- want: func() *types.URLRef {
- u, _ := url.Parse("/path/to/something")
- return &types.URLRef{URL: *u}
- }(),
- },
- "url": {
- b: []byte(`http://path/to/something`),
- want: func() *types.URLRef {
- u, _ := url.Parse("http://path/to/something")
- return &types.URLRef{URL: *u}
- }(),
- },
- }
- for n, tc := range testCases {
- tc := tc // Don't use range variable in func literal.
- t.Run(n, func(t *testing.T) {
-
- got := &types.URLRef{}
-
- err := xml.Unmarshal(tc.b, got)
-
- if tc.wantErr != "" || err != nil {
- var gotErr string
- if err != nil {
- gotErr = err.Error()
- }
- if diff := cmp.Diff(tc.wantErr, gotErr); diff != "" {
- t.Errorf("unexpected error (-want, +got) = %v", diff)
- }
- return
- }
-
- if diff := cmp.Diff(tc.want, got); diff != "" {
- t.Errorf("unexpected object (-want, +got) = %v", diff)
- }
- })
- }
-}
-
-func TestURLRefString(t *testing.T) {
- testCases := map[string]struct {
- t string
- want string
- }{
- "empty": {
- want: "",
- },
- "relative": {
- t: "/path/to/something",
- want: "/path/to/something",
- },
- "url": {
- t: "http://path/to/something",
- want: "http://path/to/something",
- },
- }
- for n, tc := range testCases {
- tc := tc // Don't use range variable in func literal.
- t.Run(n, func(t *testing.T) {
-
- tt := types.ParseURLRef(tc.t)
- got := tt.String()
-
- if diff := cmp.Diff(tc.want, got); diff != "" {
- t.Logf("got: %s", got)
- t.Errorf("unexpected string (-want, +got) = %v", diff)
- }
- })
- }
-}
-
-// TODO: Test xml: MarshalXML(e *xml.Encoder, start xml.StartElement) error {
diff --git a/v1/cloudevents/types/value.go b/v1/cloudevents/types/value.go
deleted file mode 100644
index 4086808e6..000000000
--- a/v1/cloudevents/types/value.go
+++ /dev/null
@@ -1,269 +0,0 @@
-package types
-
-import (
- "encoding/base64"
- "fmt"
- "math"
- "net/url"
- "reflect"
- "strconv"
- "time"
-)
-
-// FormatBool returns canonical string format: "true" or "false"
-func FormatBool(v bool) string { return strconv.FormatBool(v) }
-
-// FormatInteger returns canonical string format: decimal notation.
-func FormatInteger(v int32) string { return strconv.Itoa(int(v)) }
-
-// FormatBinary returns canonical string format: standard base64 encoding
-func FormatBinary(v []byte) string { return base64.StdEncoding.EncodeToString(v) }
-
-// FormatTime returns canonical string format: RFC3339 with nanoseconds
-func FormatTime(v time.Time) string { return v.UTC().Format(time.RFC3339Nano) }
-
-// ParseBool parse canonical string format: "true" or "false"
-func ParseBool(v string) (bool, error) { return strconv.ParseBool(v) }
-
-// ParseInteger parse canonical string format: decimal notation.
-func ParseInteger(v string) (int32, error) {
- // Accept floating-point but truncate to int32 as per CE spec.
- f, err := strconv.ParseFloat(v, 64)
- if err != nil {
- return 0, err
- }
- if f > math.MaxInt32 || f < math.MinInt32 {
- return 0, rangeErr(v)
- }
- return int32(f), nil
-}
-
-// ParseBinary parse canonical string format: standard base64 encoding
-func ParseBinary(v string) ([]byte, error) { return base64.StdEncoding.DecodeString(v) }
-
-// ParseTime parse canonical string format: RFC3339 with nanoseconds
-func ParseTime(v string) (time.Time, error) {
- t, err := time.Parse(time.RFC3339Nano, v)
- if err != nil {
- err := convertErr(time.Time{}, v)
- err.extra = ": not in RFC3339 format"
- return time.Time{}, err
- }
- return t, nil
-}
-
-// Format returns the canonical string format of v, where v can be
-// any type that is convertible to a CloudEvents type.
-func Format(v interface{}) (string, error) {
- v, err := Validate(v)
- if err != nil {
- return "", err
- }
- switch v := v.(type) {
- case bool:
- return FormatBool(v), nil
- case int32:
- return FormatInteger(v), nil
- case string:
- return v, nil
- case []byte:
- return FormatBinary(v), nil
- case URI:
- return v.String(), nil
- case URIRef:
- // url.URL is often passed by pointer so allow both
- return v.String(), nil
- case Timestamp:
- return FormatTime(v.Time), nil
- default:
- return "", fmt.Errorf("%T is not a CloudEvents type", v)
- }
-}
-
-// Validate v is a valid CloudEvents attribute value, convert it to one of:
-// bool, int32, string, []byte, types.URI, types.URIRef, types.Timestamp
-func Validate(v interface{}) (interface{}, error) {
- switch v := v.(type) {
- case bool, int32, string, []byte:
- return v, nil // Already a CloudEvents type, no validation needed.
-
- case uint, uintptr, uint8, uint16, uint32, uint64:
- u := reflect.ValueOf(v).Uint()
- if u > math.MaxInt32 {
- return nil, rangeErr(v)
- }
- return int32(u), nil
- case int, int8, int16, int64:
- i := reflect.ValueOf(v).Int()
- if i > math.MaxInt32 || i < math.MinInt32 {
- return nil, rangeErr(v)
- }
- return int32(i), nil
- case float32, float64:
- f := reflect.ValueOf(v).Float()
- if f > math.MaxInt32 || f < math.MinInt32 {
- return nil, rangeErr(v)
- }
- return int32(f), nil
-
- case *url.URL:
- if v == nil {
- break
- }
- return URI{*v}, nil
- case url.URL:
- return URI{v}, nil
- case URIRef:
- return v, nil
- case URI:
- return v, nil
- case URLRef:
- // Convert old type to new one
- return URIRef{v.URL}, nil
- case time.Time:
- return Timestamp{v}, nil
- case *time.Time:
- if v == nil {
- break
- }
- return Timestamp{*v}, nil
- case Timestamp:
- return v, nil
- }
- rx := reflect.ValueOf(v)
- if rx.Kind() == reflect.Ptr && !rx.IsNil() {
- // Allow pointers-to convertible types
- return Validate(rx.Elem().Interface())
- }
- return nil, fmt.Errorf("invalid CloudEvents value: %#v", v)
-}
-
-// ToBool accepts a bool value or canonical "true"/"false" string.
-func ToBool(v interface{}) (bool, error) {
- v, err := Validate(v)
- if err != nil {
- return false, err
- }
- switch v := v.(type) {
- case bool:
- return v, nil
- case string:
- return ParseBool(v)
- default:
- return false, convertErr(true, v)
- }
-}
-
-// ToInteger accepts any numeric value in int32 range, or canonical string.
-func ToInteger(v interface{}) (int32, error) {
- v, err := Validate(v)
- if err != nil {
- return 0, err
- }
- switch v := v.(type) {
- case int32:
- return v, nil
- case string:
- return ParseInteger(v)
- default:
- return 0, convertErr(int32(0), v)
- }
-}
-
-// ToString returns a string value unaltered.
-//
-// This function does not perform canonical string encoding, use one of the
-// Format functions for that.
-func ToString(v interface{}) (string, error) {
- v, err := Validate(v)
- if err != nil {
- return "", err
- }
- switch v := v.(type) {
- case string:
- return v, nil
- default:
- return "", convertErr("", v)
- }
-}
-
-// ToBinary returns a []byte value, decoding from base64 string if necessary.
-func ToBinary(v interface{}) ([]byte, error) {
- v, err := Validate(v)
- if err != nil {
- return nil, err
- }
- switch v := v.(type) {
- case []byte:
- return v, nil
- case string:
- return base64.StdEncoding.DecodeString(v)
- default:
- return nil, convertErr([]byte(nil), v)
- }
-}
-
-// ToURL returns a *url.URL value, parsing from string if necessary.
-func ToURL(v interface{}) (*url.URL, error) {
- v, err := Validate(v)
- if err != nil {
- return nil, err
- }
- switch v := v.(type) {
- case URI:
- return &v.URL, nil
- case URIRef:
- return &v.URL, nil
- case string:
- u, err := url.Parse(v)
- if err != nil {
- return nil, err
- }
- return u, nil
- default:
- return nil, convertErr((*url.URL)(nil), v)
- }
-}
-
-// ToTime returns a time.Time value, parsing from RFC3339 string if necessary.
-func ToTime(v interface{}) (time.Time, error) {
- v, err := Validate(v)
- if err != nil {
- return time.Time{}, err
- }
- switch v := v.(type) {
- case Timestamp:
- return v.Time, nil
- case string:
- ts, err := time.Parse(time.RFC3339Nano, v)
- if err != nil {
- return time.Time{}, err
- }
- return ts, nil
- default:
- return time.Time{}, convertErr(time.Time{}, v)
- }
-}
-
-type ConvertErr struct {
- // Value being converted
- Value interface{}
- // Type of attempted conversion
- Type reflect.Type
-
- extra string
-}
-
-func (e *ConvertErr) Error() string {
- return fmt.Sprintf("cannot convert %#v to %s%s", e.Value, e.Type, e.extra)
-}
-
-func convertErr(target, v interface{}) *ConvertErr {
- return &ConvertErr{Value: v, Type: reflect.TypeOf(target)}
-}
-
-func rangeErr(v interface{}) error {
- e := convertErr(int32(0), v)
- e.extra = ": out of range"
- return e
-}
diff --git a/v1/cloudevents/types/value_test.go b/v1/cloudevents/types/value_test.go
deleted file mode 100644
index 75cb64e41..000000000
--- a/v1/cloudevents/types/value_test.go
+++ /dev/null
@@ -1,247 +0,0 @@
-package types_test
-
-import (
- "fmt"
- "math"
- "net/url"
- "reflect"
- "strconv"
- "testing"
- "time"
-
- "github.com/cloudevents/sdk-go/v1/cloudevents/types"
- "github.com/stretchr/testify/assert"
- "github.com/stretchr/testify/require"
-)
-
-func Example() {
- // Handle a time value that may be in native or canonical string form.
- printTime := func(v interface{}) {
- t, err := types.ToTime(v)
- fmt.Printf("%v %v\n", t, err)
- }
- printTime(time.Date(1969, 3, 21, 12, 24, 0, 0, time.UTC))
- printTime("2020-03-21T12:34:56.78Z")
-
- // Convert numeric values to common 32-bit integer form
- printInt := func(v interface{}) {
- i, err := types.ToInteger(v)
- fmt.Printf("%v %v\n", i, err)
- }
- printInt(123.456)
- printInt("456")
- printInt(int64(99999))
- // But not illegal or out-of-range values
- printInt(math.MaxInt32 + 1)
- printInt("not an int")
-
- // OUTPUT:
- // 1969-03-21 12:24:00 +0000 UTC
- // 2020-03-21 12:34:56.78 +0000 UTC
- // 123
- // 456
- // 99999
- // 0 cannot convert 2147483648 to int32: out of range
- // 0 strconv.ParseFloat: parsing "not an int": invalid syntax
-}
-
-var (
- testURL = &url.URL{Scheme: "http", Host: "example.com", Path: "/foo"}
- testURLstr = "http://example.com/foo"
- timeStr = "2020-03-21T12:34:56.78Z"
- someTime = time.Date(2020, 3, 21, 12, 34, 56, 780000000, time.UTC)
-)
-
-type valueTester struct {
- testing.TB
- convertFn interface{}
-}
-
-// Call types.To... function, use reflection since return types differ.
-func (t valueTester) convert(v interface{}) (interface{}, error) {
- rf := reflect.ValueOf(t.convertFn)
- args := []reflect.Value{reflect.ValueOf(v)}
- if v == nil {
- args[0] = reflect.Zero(rf.Type().In(0)) // Avoid the zero argument reflection trap.
- }
- result := rf.Call(args)
- err, _ := result[1].Interface().(error)
- return result[0].Interface(), err
-}
-
-// Verify round trip: convertible -> wrapped -> string -> wrapped
-func (t *valueTester) ok(in, want interface{}, wantStr string) {
- t.Helper()
- got, err := types.Validate(in)
- require.NoError(t, err)
- assert.Equal(t, want, got)
-
- gotStr, err := types.Format(in)
- require.NoError(t, err)
- assert.Equal(t, wantStr, gotStr)
-
- x, err := t.convert(gotStr)
- assert.NoError(t, err)
- x2, err := types.Validate(x)
- assert.NoError(t, err)
- assert.Equal(t, want, x2)
-}
-
-// Verify round trip with exception: convertible -> wrapped -> string -> different wrapped
-func (t *valueTester) okWithDifferentFromString(in, want interface{}, wantStr string, wantAfterStr interface{}) {
- t.Helper()
- got, err := types.Validate(in)
- require.NoError(t, err)
- assert.Equal(t, want, got)
-
- gotStr, err := types.Format(in)
- require.NoError(t, err)
- assert.Equal(t, wantStr, gotStr)
-
- x, err := t.convert(gotStr)
- assert.NoError(t, err)
- x2, err := types.Validate(x)
- assert.NoError(t, err)
- assert.Equal(t, wantAfterStr, x2)
-}
-
-// Verify expected error.
-func (t *valueTester) err(in interface{}, wantErr string) {
- t.Helper()
- _, err := t.convert(in)
- assert.EqualError(t, err, wantErr)
-}
-
-// Verify string->value conversion.
-func (t *valueTester) str(str string, want interface{}) {
- t.Helper()
- got, err := t.convert(str)
- assert.NoError(t, err)
- assert.Equal(t, want, got)
-}
-
-func TestBool(t *testing.T) {
- x := valueTester{t, types.ToBool}
- x.ok(true, true, "true")
- x.ok(false, false, "false")
-
- x.err("notabool", "strconv.ParseBool: parsing \"notabool\": invalid syntax")
- x.err(0, "cannot convert 0 to bool")
- x.err(nil, "invalid CloudEvents value: ")
-}
-
-func TestInteger(t *testing.T) {
- x := valueTester{t, types.ToInteger}
- x.ok(42, int32(42), "42")
- x.ok(int8(-8), int32(-8), "-8")
- x.ok(int16(-16), int32(-16), "-16")
- x.ok(int32(-32), int32(-32), "-32")
- x.ok(int64(-64), int32(-64), "-64")
- x.ok(uint(1), int32(1), "1")
- x.ok(uint8(8), int32(8), "8")
- x.ok(uint16(16), int32(16), "16")
- x.ok(uint32(32), int32(32), "32")
- x.ok(uint64(64), int32(64), "64")
- x.ok(float32(123.4), int32(123), "123")
- x.ok(float64(-567.8), int32(-567), "-567")
- i := new(uint16)
- *i = 24 // non-nil pointers allowed
- x.ok(i, int32(24), "24")
-
- x.ok(math.MaxInt32, int32(math.MaxInt32), strconv.Itoa(math.MaxInt32))
- x.ok(math.MinInt32, int32(math.MinInt32), strconv.Itoa(math.MinInt32))
- x.ok(int64(math.MinInt32), int32(math.MinInt32), strconv.Itoa(math.MinInt32))
- x.ok(uint32(math.MaxInt32), int32(math.MaxInt32), strconv.Itoa(math.MaxInt32))
- x.ok(uint64(math.MaxInt32), int32(math.MaxInt32), strconv.Itoa(math.MaxInt32))
- x.ok(float64(math.MaxInt32), int32(math.MaxInt32), strconv.Itoa(math.MaxInt32))
- x.ok(float64(math.MinInt32), int32(math.MinInt32), strconv.Itoa(math.MinInt32))
-
- x.str("123.456", int32(123))
- x.str("-123.456", int32(-123))
- x.str(".9", int32(0))
- x.str("-.9", int32(0))
-
- x.err(math.MaxInt32+1, "cannot convert 2147483648 to int32: out of range")
- x.err(uint32(math.MaxInt32+1), "cannot convert 0x80000000 to int32: out of range")
- x.err(int64(math.MaxInt32+1), "cannot convert 2147483648 to int32: out of range")
- x.err(int64(math.MinInt32-1), "cannot convert -2147483649 to int32: out of range")
- x.err(float64(math.MinInt32-1), "cannot convert -2.147483649e+09 to int32: out of range")
- x.err(float64(math.MaxInt32+1), "cannot convert 2.147483648e+09 to int32: out of range")
- // Float32 doesn't keep all the bits of an int32 so we need to exaggerate fof range error.
- x.err(float64(2*math.MinInt32), "cannot convert -4.294967296e+09 to int32: out of range")
- x.err(float64(-2*math.MaxInt32), "cannot convert -4.294967294e+09 to int32: out of range")
-
- x.err("X", "strconv.ParseFloat: parsing \"X\": invalid syntax")
- x.err(true, "cannot convert true to int32")
- x.err(nil, "invalid CloudEvents value: ")
-}
-
-func TestString(t *testing.T) {
- x := valueTester{t, types.ToString}
- x.ok("hello", "hello", "hello")
- s := new(string)
- *s = "foo" // non-nil pointers allowed
- x.ok(s, "foo", "foo")
-}
-
-func TestBinary(t *testing.T) {
- x := valueTester{t, types.ToBinary}
- x.ok([]byte("hello"), []byte("hello"), "aGVsbG8=")
- x.ok([]byte{}, []byte{}, "")
- // Asymmetic case: ToBinary([]byte(nil)) returns []byte(nil),
- // but ToBinary("") returns []byte{}
- // Logically equivalent but not assert.Equal().
- x.str("", []byte{})
-
- x.err("XXX", "illegal base64 data at input byte 0")
- x.err(nil, "invalid CloudEvents value: ")
-}
-
-func TestURL(t *testing.T) {
- t.Skip("Fails on Golang 1.14")
-
- x := valueTester{t, types.ToURL}
- x.ok(testURL, types.URI{*testURL}, testURLstr)
- x.ok(*testURL, types.URI{*testURL}, testURLstr)
- x.okWithDifferentFromString(types.URLRef{URL: *testURL}, types.URIRef{*testURL}, testURLstr, types.URI{*testURL})
- x.okWithDifferentFromString(&types.URLRef{URL: *testURL}, types.URIRef{*testURL}, testURLstr, types.URI{*testURL})
- x.okWithDifferentFromString(types.URIRef{URL: *testURL}, types.URIRef{*testURL}, testURLstr, types.URI{*testURL})
- x.okWithDifferentFromString(&types.URIRef{URL: *testURL}, types.URIRef{*testURL}, testURLstr, types.URI{*testURL})
- x.ok(types.URI{URL: *testURL}, types.URI{*testURL}, testURLstr)
- x.ok(&types.URI{URL: *testURL}, types.URI{*testURL}, testURLstr)
-
- x.str("http://hello/world", &url.URL{Scheme: "http", Host: "hello", Path: "/world"})
- x.str("/world", &url.URL{Path: "/world"})
- x.str("world", &url.URL{Path: "world"})
-
- x.err("%bad %url", "parse %bad %url: invalid URL escape \"%ur\"")
- x.err(nil, "invalid CloudEvents value: ")
- x.err((*url.URL)(nil), "invalid CloudEvents value: (*url.URL)(nil)")
- x.err((*types.URIRef)(nil), "invalid CloudEvents value: (*types.URIRef)(nil)")
-}
-
-func TestTime(t *testing.T) {
- x := valueTester{t, types.ToTime}
- x.ok(someTime, types.Timestamp{someTime}, timeStr)
- x.ok(&someTime, types.Timestamp{someTime}, timeStr)
- x.ok(types.Timestamp{someTime}, types.Timestamp{someTime}, timeStr)
- x.ok(&types.Timestamp{someTime}, types.Timestamp{someTime}, timeStr)
-
- x.str(timeStr, someTime)
-
- x.err(nil, "invalid CloudEvents value: ")
- x.err(5, "cannot convert 5 to time.Time")
- x.err((*time.Time)(nil), "invalid CloudEvents value: (*time.Time)(nil)")
- x.err((*types.Timestamp)(nil), "invalid CloudEvents value: (*types.Timestamp)(nil)")
- x.err("not a time", "parsing time \"not a time\" as \"2006-01-02T15:04:05.999999999Z07:00\": cannot parse \"not a time\" as \"2006\"")
-}
-
-func TestIncompatible(t *testing.T) {
- // Values that won't convert at all.
- x := valueTester{t, types.Validate}
- x.err(nil, "invalid CloudEvents value: ")
- x.err(complex(0, 0), "invalid CloudEvents value: (0+0i)")
- x.err(map[string]interface{}{}, "invalid CloudEvents value: map[string]interface {}{}")
- x.err(struct{ i int }{i: 9}, "invalid CloudEvents value: struct { i int }{i:9}")
- x.err((*int32)(nil), "invalid CloudEvents value: (*int32)(nil)")
-}
diff --git a/v1/go.mod b/v1/go.mod
deleted file mode 100644
index ca81f80c9..000000000
--- a/v1/go.mod
+++ /dev/null
@@ -1,34 +0,0 @@
-module github.com/cloudevents/sdk-go/v1
-
-require (
- cloud.google.com/go v0.40.0
- github.com/Azure/azure-sdk-for-go v30.1.0+incompatible // indirect
- github.com/Azure/go-autorest/autorest v0.2.0 // indirect
- github.com/Azure/go-autorest/autorest/to v0.2.0 // indirect
- github.com/Azure/go-autorest/autorest/validation v0.1.0 // indirect
- github.com/Shopify/sarama v1.19.0
- github.com/fortytw2/leaktest v1.3.0 // indirect
- github.com/google/go-cmp v0.4.0
- github.com/google/uuid v1.1.1
- github.com/kr/pretty v0.2.0 // indirect
- github.com/lightstep/tracecontext.go v0.0.0-20181129014701-1757c391b1ac
- github.com/nats-io/nats-server/v2 v2.1.2
- github.com/nats-io/nats.go v1.9.1
- github.com/pkg/errors v0.8.1
- github.com/stretchr/testify v1.5.1
- github.com/valyala/bytebufferpool v1.0.0
- go.opencensus.io v0.22.0
- go.uber.org/atomic v1.4.0 // indirect
- go.uber.org/multierr v1.1.0 // indirect
- go.uber.org/zap v1.10.0
- golang.org/x/crypto v0.0.0-20200204104054-c9f3fb736b72 // indirect
- golang.org/x/net v0.0.0-20200202094626-16171245cfb2 // indirect
- golang.org/x/sync v0.0.0-20190423024810-112230192c58
- google.golang.org/api v0.15.0
- google.golang.org/grpc v1.26.0
- gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15 // indirect
- gopkg.in/yaml.v2 v2.2.8 // indirect
- pack.ag/amqp v0.11.0
-)
-
-go 1.13
diff --git a/v1/go.sum b/v1/go.sum
deleted file mode 100644
index aba7f5878..000000000
--- a/v1/go.sum
+++ /dev/null
@@ -1,296 +0,0 @@
-cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
-cloud.google.com/go v0.34.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
-cloud.google.com/go v0.38.0 h1:ROfEUZz+Gh5pa62DJWXSaonyu3StP6EA6lPEXPI6mCo=
-cloud.google.com/go v0.38.0/go.mod h1:990N+gfupTy94rShfmMCWGDn0LpTmnzTp2qbd1dvSRU=
-cloud.google.com/go v0.40.0 h1:FjSY7bOj+WzJe6TZRVtXI2b9kAYvtNg4lMbcH2+MUkk=
-cloud.google.com/go v0.40.0/go.mod h1:Tk58MuI9rbLMKlAjeO/bDnteAx7tX2gJIXw4T5Jwlro=
-contrib.go.opencensus.io/exporter/ocagent v0.4.12 h1:jGFvw3l57ViIVEPKKEUXPcLYIXJmQxLUh6ey1eJhwyc=
-contrib.go.opencensus.io/exporter/ocagent v0.4.12/go.mod h1:450APlNTSR6FrvC3CTRqYosuDstRB9un7SOx2k/9ckA=
-github.com/Azure/azure-sdk-for-go v30.1.0+incompatible h1:HyYPft8wXpxMd0kfLtXo6etWcO+XuPbLkcgx9g2cqxU=
-github.com/Azure/azure-sdk-for-go v30.1.0+incompatible/go.mod h1:9XXNKU+eRnpl9moKnB4QOLf1HestfXbmab5FXxiDBjc=
-github.com/Azure/go-autorest/autorest v0.2.0 h1:zBtSTOQTtjzHVRe+mhkiHvHwRTKHhjBEyo1m6DfI3So=
-github.com/Azure/go-autorest/autorest v0.2.0/go.mod h1:AKyIcETwSUFxIcs/Wnq/C+kwCtlEYGUVd7FPNb2slmg=
-github.com/Azure/go-autorest/autorest/adal v0.1.0 h1:RSw/7EAullliqwkZvgIGDYZWQm1PGKXI8c4aY/87yuU=
-github.com/Azure/go-autorest/autorest/adal v0.1.0/go.mod h1:MeS4XhScH55IST095THyTxElntu7WqB7pNbZo8Q5G3E=
-github.com/Azure/go-autorest/autorest/date v0.1.0 h1:YGrhWfrgtFs84+h0o46rJrlmsZtyZRg470CqAXTZaGM=
-github.com/Azure/go-autorest/autorest/date v0.1.0/go.mod h1:plvfp3oPSKwf2DNjlBjWF/7vwR+cUD/ELuzDCXwHUVA=
-github.com/Azure/go-autorest/autorest/mocks v0.1.0 h1:Kx+AUU2Te+A3JIyYn6Dfs+cFgx5XorQKuIXrZGoq/SI=
-github.com/Azure/go-autorest/autorest/mocks v0.1.0/go.mod h1:OTyCOPRA2IgIlWxVYxBee2F5Gr4kF2zd2J5cFRaIDN0=
-github.com/Azure/go-autorest/autorest/to v0.2.0 h1:nQOZzFCudTh+TvquAtCRjM01VEYx85e9qbwt5ncW4L8=
-github.com/Azure/go-autorest/autorest/to v0.2.0/go.mod h1:GunWKJp1AEqgMaGLV+iocmRAJWqST1wQYhyyjXJ3SJc=
-github.com/Azure/go-autorest/autorest/validation v0.1.0 h1:ISSNzGUh+ZSzizJWOWzs8bwpXIePbGLW4z/AmUFGH5A=
-github.com/Azure/go-autorest/autorest/validation v0.1.0/go.mod h1:Ha3z/SqBeaalWQvokg3NZAlQTalVMtOIAs1aGK7G6u8=
-github.com/Azure/go-autorest/logger v0.1.0 h1:ruG4BSDXONFRrZZJ2GUXDiUyVpayPmb1GnWeHDdaNKY=
-github.com/Azure/go-autorest/logger v0.1.0/go.mod h1:oExouG+K6PryycPJfVSxi/koC6LSNgds39diKLz7Vrc=
-github.com/Azure/go-autorest/tracing v0.1.0 h1:TRBxC5Pj/fIuh4Qob0ZpkggbfT8RC0SubHbpV3p4/Vc=
-github.com/Azure/go-autorest/tracing v0.1.0/go.mod h1:ROEEAFwXycQw7Sn3DXNtEedEvdeRAgDr0izn4z5Ij88=
-github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
-github.com/Shopify/sarama v1.19.0 h1:9oksLxC6uxVPHPVYUmq6xhr1BOF/hHobWH2UzO67z1s=
-github.com/Shopify/sarama v1.19.0/go.mod h1:FVkBWblsNy7DGZRfXLU0O9RCGt5g3g3yEuWXgklEdEo=
-github.com/Shopify/toxiproxy v2.1.4+incompatible h1:TKdv8HiTLgE5wdJuEML90aBgNWsokNbMijUGhmcoBJc=
-github.com/Shopify/toxiproxy v2.1.4+incompatible/go.mod h1:OXgGpZ6Cli1/URJOF1DMxUHB2q5Ap20/P/eIdh4G0pI=
-github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc=
-github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0=
-github.com/apache/thrift v0.12.0/go.mod h1:cp2SuWMxlEZw2r+iP2GNCdIi4C1qmUzdZFSVb+bacwQ=
-github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973 h1:xJ4a3vCFaGF/jqvzLMYoU8P317H5OQ+Via4RmuPwCS0=
-github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q=
-github.com/census-instrumentation/opencensus-proto v0.2.0 h1:LzQXZOgg4CQfE6bFvXGM30YZL1WW/M337pXml+GrcZ4=
-github.com/census-instrumentation/opencensus-proto v0.2.0/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU=
-github.com/census-instrumentation/opencensus-proto v0.2.1 h1:glEXhBS5PSLLv4IXzLA5yPRVX4bilULVyxxbrfOtDAk=
-github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU=
-github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw=
-github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
-github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
-github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
-github.com/dgrijalva/jwt-go v3.2.0+incompatible h1:7qlOGliEKZXTDg6OTjfoBKDXWrumCAMpl/TFQ4/5kLM=
-github.com/dgrijalva/jwt-go v3.2.0+incompatible/go.mod h1:E3ru+11k8xSBh+hMPgOLZmtrrCbhqsmaPHjLKYnJCaQ=
-github.com/eapache/go-resiliency v1.1.0 h1:1NtRmCAqadE2FN4ZcN6g90TP3uk8cg9rn9eNK2197aU=
-github.com/eapache/go-resiliency v1.1.0/go.mod h1:kFI+JgMyC7bLPUVY133qvEBtVayf5mFgVsvEsIPBvNs=
-github.com/eapache/go-xerial-snappy v0.0.0-20180814174437-776d5712da21 h1:YEetp8/yCZMuEPMUDHG0CW/brkkEp8mzqk2+ODEitlw=
-github.com/eapache/go-xerial-snappy v0.0.0-20180814174437-776d5712da21/go.mod h1:+020luEh2TKB4/GOp8oxxtq0Daoen/Cii55CzbTV6DU=
-github.com/eapache/queue v1.1.0 h1:YOEu7KNc61ntiQlcEeUIoDTJ2o8mQznoNvUhiigpIqc=
-github.com/eapache/queue v1.1.0/go.mod h1:6eCeP0CKFpHLu8blIFXhExK/dRa7WDZfr6jVFPTqq+I=
-github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4=
-github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c=
-github.com/fortytw2/leaktest v1.3.0 h1:u8491cBMTQ8ft8aeV+adlcytMZylmA5nnwwkRZjI8vw=
-github.com/fortytw2/leaktest v1.3.0/go.mod h1:jDsjWgpAGjm2CA7WthBh/CdZYEPF31XHquHwclZch5g=
-github.com/fsnotify/fsnotify v1.4.7 h1:IXs+QLmnXW2CcXuY+8Mzv/fWEsPGWxqefPtCP5CnV9I=
-github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo=
-github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04=
-github.com/go-kit/kit v0.8.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as=
-github.com/go-logfmt/logfmt v0.3.0/go.mod h1:Qt1PoO58o5twSAckw1HlFXLmHsOX5/0LbT9GBnD5lWE=
-github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY=
-github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ=
-github.com/gogo/protobuf v1.2.0/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ=
-github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b h1:VKtxabqXZkF25pY9ekfRL6a582T4P37/31XEstQ5p58=
-github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q=
-github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A=
-github.com/golang/mock v1.2.0/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A=
-github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
-github.com/golang/protobuf v1.3.1 h1:YF8+flBXS5eO826T4nzqPrxfhQThhXl0YzfuUPu4SBg=
-github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
-github.com/golang/protobuf v1.3.2 h1:6nsPYzhq5kReh6QImI3k5qWzO4PEbvbIW2cwSfR/6xs=
-github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
-github.com/golang/snappy v0.0.0-20180518054509-2e65f85255db h1:woRePGFeVFfLKN/pOkfl+p/TAqKOfFu+7KPlMVpok/w=
-github.com/golang/snappy v0.0.0-20180518054509-2e65f85255db/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q=
-github.com/google/btree v0.0.0-20180813153112-4030bb1f1f0c/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ=
-github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M=
-github.com/google/go-cmp v0.3.0 h1:crn/baboCvb5fXaQ0IJ1SGTsTVrWpDsCWC8EGETZijY=
-github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
-github.com/google/go-cmp v0.4.0 h1:xsAVV57WRhGj6kEIi8ReJzQlHHqcBYCElAvkovg3B/4=
-github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
-github.com/google/martian v2.1.0+incompatible/go.mod h1:9I4somxYTbIHy5NJKHRl3wXiIaQGbYVAs8BPL6v8lEs=
-github.com/google/pprof v0.0.0-20181206194817-3ea8567a2e57/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc=
-github.com/google/uuid v1.1.1 h1:Gkbcsh/GbpXz7lPftLA3P6TYMwjCLYm83jiFQZF/3gY=
-github.com/google/uuid v1.1.1/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
-github.com/googleapis/gax-go/v2 v2.0.4 h1:hU4mGcQI4DaAYW+IbTun+2qEZVFxK0ySjQLTbS0VQKc=
-github.com/googleapis/gax-go/v2 v2.0.4/go.mod h1:0Wqv26UfaUD9n4G6kQubkQ+KchISgw+vpHVxEJEs9eg=
-github.com/googleapis/gax-go/v2 v2.0.5 h1:sjZBwGj9Jlw33ImPtvFviGYvseOtDM7hkSKB7+Tv3SM=
-github.com/googleapis/gax-go/v2 v2.0.5/go.mod h1:DWXyrwAJ9X0FpwwEdw+IPEYBICEFu5mhpdKc/us6bOk=
-github.com/gorilla/context v1.1.1 h1:AWwleXJkX/nhcU9bZSnZoi3h/qGYqQAGhq6zZe/aQW8=
-github.com/gorilla/context v1.1.1/go.mod h1:kBGZzfjB9CEq2AlWe17Uuf7NDRt0dE0s8S51q0aT7Yg=
-github.com/gorilla/mux v1.6.2 h1:Pgr17XVTNXAk3q/r4CpKzC5xBM/qW1uVLV+IhRZpIIk=
-github.com/gorilla/mux v1.6.2/go.mod h1:1lud6UwP+6orDFRuTfBEV8e9/aOM/c4fVVCaMa2zaAs=
-github.com/grpc-ecosystem/grpc-gateway v1.8.5 h1:2+KSC78XiO6Qy0hIjfc1OD9H+hsaJdJlb8Kqsd41CTE=
-github.com/grpc-ecosystem/grpc-gateway v1.8.5/go.mod h1:vNeuVxBJEsws4ogUvrchl83t/GYV9WGTSLVdBhOQFDY=
-github.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8=
-github.com/hashicorp/golang-lru v0.5.1 h1:0hERBMJE1eitiLkihrMvRVBYAkpHzc/J3QdDN+dAcgU=
-github.com/hashicorp/golang-lru v0.5.1/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8=
-github.com/hpcloud/tail v1.0.0 h1:nfCOvKYfkgYP8hkirhJocXT2+zOD8yUNjXaWfTlyFKI=
-github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU=
-github.com/jstemmer/go-junit-report v0.0.0-20190106144839-af01ea7f8024/go.mod h1:6v2b51hI/fHJwM22ozAgKL4VKDeJcHhJFhtBdhmNjmU=
-github.com/julienschmidt/httprouter v1.2.0/go.mod h1:SYymIcj16QtmaHHD7aYtjjsJG7VTCxuUUipMqKk8s4w=
-github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck=
-github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
-github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515/go.mod h1:+0opPa2QZZtGFBFZlji/RkVcI2GknAs/DXo4wKdlNEc=
-github.com/kr/pretty v0.1.0 h1:L/CwN0zerZDmRFUapSPitk6f+Q3+0za1rQkzVuMiMFI=
-github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
-github.com/kr/pretty v0.2.0 h1:s5hAObm+yFO5uHYt5dYjxi2rXrsnmRpJx4OYvIWUaQs=
-github.com/kr/pretty v0.2.0/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI=
-github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
-github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE=
-github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
-github.com/lightstep/tracecontext.go v0.0.0-20181129014701-1757c391b1ac h1:+2b6iGRJe3hvV/yVXrd41yVEjxuFHxasJqDhkIjS4gk=
-github.com/lightstep/tracecontext.go v0.0.0-20181129014701-1757c391b1ac/go.mod h1:Frd2bnT3w5FB5q49ENTfVlztJES+1k/7lyWX2+9gq/M=
-github.com/matttproud/golang_protobuf_extensions v1.0.1 h1:4hp9jkHxhMHkqkrB3Ix0jegS5sx/RkqARlsWZ6pIwiU=
-github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0=
-github.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U=
-github.com/nats-io/jwt v0.3.0/go.mod h1:fRYCDE99xlTsqUzISS1Bi75UBJ6ljOJQOAAu5VglpSg=
-github.com/nats-io/jwt v0.3.2 h1:+RB5hMpXUUA2dfxuhBTEkMOrYmM+gKIZYS1KjSostMI=
-github.com/nats-io/jwt v0.3.2/go.mod h1:/euKqTS1ZD+zzjYrY7pseZrTtWQSjujC7xjPc8wL6eU=
-github.com/nats-io/nats-server/v2 v2.1.2 h1:i2Ly0B+1+rzNZHHWtD4ZwKi+OU5l+uQo1iDHZ2PmiIc=
-github.com/nats-io/nats-server/v2 v2.1.2/go.mod h1:Afk+wRZqkMQs/p45uXdrVLuab3gwv3Z8C4HTBu8GD/k=
-github.com/nats-io/nats.go v1.9.1 h1:ik3HbLhZ0YABLto7iX80pZLPw/6dx3T+++MZJwLnMrQ=
-github.com/nats-io/nats.go v1.9.1/go.mod h1:ZjDU1L/7fJ09jvUSRVBR2e7+RnLiiIQyqyzEE/Zbp4w=
-github.com/nats-io/nkeys v0.1.0/go.mod h1:xpnFELMwJABBLVhffcfd1MZx6VsNRFpEugbxziKVo7w=
-github.com/nats-io/nkeys v0.1.3 h1:6JrEfig+HzTH85yxzhSVbjHRJv9cn0p6n3IngIcM5/k=
-github.com/nats-io/nkeys v0.1.3/go.mod h1:xpnFELMwJABBLVhffcfd1MZx6VsNRFpEugbxziKVo7w=
-github.com/nats-io/nuid v1.0.1 h1:5iA8DT8V7q8WK2EScv2padNa/rTESc1KdnPw4TC2paw=
-github.com/nats-io/nuid v1.0.1/go.mod h1:19wcPz3Ph3q0Jbyiqsd0kePYG7A95tJPxeL+1OSON2c=
-github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
-github.com/onsi/ginkgo v1.7.0 h1:WSHQ+IS43OoUrWtD1/bbclrwK8TTH5hzp+umCiuxHgs=
-github.com/onsi/ginkgo v1.7.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
-github.com/onsi/gomega v1.4.3 h1:RE1xgDvH7imwFD45h+u2SgIfERHlS2yNG4DObb5BSKU=
-github.com/onsi/gomega v1.4.3/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY=
-github.com/openzipkin/zipkin-go v0.1.6/go.mod h1:QgAqvLzwWbR/WpD4A3cGpPtJrZXNIiJc5AZX7/PBEpw=
-github.com/pierrec/lz4 v2.0.5+incompatible h1:2xWsjqPFWcplujydGg4WmhC/6fZqK42wMM8aXeqhl0I=
-github.com/pierrec/lz4 v2.0.5+incompatible/go.mod h1:pdkljMzZIN41W+lC3N2tnIh5sFi+IEE17M5jbnwPHcY=
-github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
-github.com/pkg/errors v0.8.1 h1:iURUrRGxPUNPdy5/HRSm+Yj6okJ6UtLINN0Q9M4+h3I=
-github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
-github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
-github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
-github.com/prometheus/client_golang v0.9.1/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw=
-github.com/prometheus/client_golang v0.9.3-0.20190127221311-3c4408c8b829 h1:D+CiwcpGTW6pL6bv6KI3KbyEyCKyS+1JWS2h8PNDnGA=
-github.com/prometheus/client_golang v0.9.3-0.20190127221311-3c4408c8b829/go.mod h1:p2iRAGwDERtqlqzRXnrOVns+ignqQo//hLXqYxZYVNs=
-github.com/prometheus/client_model v0.0.0-20180712105110-5c3871d89910 h1:idejC8f05m9MGOsuEi1ATq9shN03HrxNkD/luQvxCv8=
-github.com/prometheus/client_model v0.0.0-20180712105110-5c3871d89910/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo=
-github.com/prometheus/client_model v0.0.0-20190115171406-56726106282f h1:BVwpUVJDADN2ufcGik7W992pyps0wZ888b/y9GXcLTU=
-github.com/prometheus/client_model v0.0.0-20190115171406-56726106282f/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo=
-github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4 h1:gQz4mCbXsO+nc9n1hCxHcGA3Zx3Eo+UHZoInFGUIXNM=
-github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
-github.com/prometheus/common v0.2.0 h1:kUZDBDTdBVBYBj5Tmh2NZLlF60mfjA27rM34b+cVwNU=
-github.com/prometheus/common v0.2.0/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4=
-github.com/prometheus/procfs v0.0.0-20181005140218-185b4288413d/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk=
-github.com/prometheus/procfs v0.0.0-20190117184657-bf6a532e95b1 h1:/K3IL0Z1quvmJ7X0A1AwNEK7CRkVK3YwfOU/QAL4WGg=
-github.com/prometheus/procfs v0.0.0-20190117184657-bf6a532e95b1/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk=
-github.com/rcrowley/go-metrics v0.0.0-20181016184325-3113b8401b8a h1:9ZKAASQSHhDYGoxY8uLVpewe1GDZ2vu2Tr/vTdVAkFQ=
-github.com/rcrowley/go-metrics v0.0.0-20181016184325-3113b8401b8a/go.mod h1:bCqnVzQkZxMG4s8nGwiZ5l3QUCyqpo9Y+/ZMZ9VjZe4=
-github.com/rogpeppe/fastuuid v0.0.0-20150106093220-6724a57986af/go.mod h1:XWv6SoW27p1b0cqNHllgS5HIMJraePCO15w5zCzIWYg=
-github.com/sirupsen/logrus v1.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo=
-github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
-github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
-github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
-github.com/stretchr/testify v1.3.0 h1:TivCn/peBQ7UY8ooIcPgZFpTNSz0Q2U6UrFlUfqbe0Q=
-github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
-github.com/stretchr/testify v1.5.1 h1:nOGnQDM7FYENwehXlg/kFVnos3rEvtKTjRvOWSzb6H4=
-github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA=
-github.com/valyala/bytebufferpool v1.0.0 h1:GqA5TC/0021Y/b9FG4Oi9Mr3q7XYx6KllzawFIhcdPw=
-github.com/valyala/bytebufferpool v1.0.0/go.mod h1:6bBcMArwyJ5K/AmCkWv1jt77kVWyCJ6HpOuEn7z0Csc=
-go.opencensus.io v0.20.1/go.mod h1:6WKK9ahsWS3RSO+PY9ZHZUfv2irvY6gN279GOPZjmmk=
-go.opencensus.io v0.20.2/go.mod h1:6WKK9ahsWS3RSO+PY9ZHZUfv2irvY6gN279GOPZjmmk=
-go.opencensus.io v0.21.0/go.mod h1:mSImk1erAIZhrmZN+AvHh14ztQfjbGwt4TtuofqLduU=
-go.opencensus.io v0.22.0 h1:C9hSCOW830chIVkdja34wa6Ky+IzWllkUinR+BtRZd4=
-go.opencensus.io v0.22.0/go.mod h1:+kGneAE2xo2IficOXnaByMWTGM9T73dGwxeWcUqIpI8=
-go.uber.org/atomic v1.4.0 h1:cxzIVoETapQEqDhQu3QfnvXAV4AlzcvUCxkVUFw3+EU=
-go.uber.org/atomic v1.4.0/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE=
-go.uber.org/multierr v1.1.0 h1:HoEmRHQPVSqub6w2z2d2EOVs2fjyFRGyofhKuyDq0QI=
-go.uber.org/multierr v1.1.0/go.mod h1:wR5kodmAFQ0UK8QlbwjlSNy0Z68gJhDJUG5sjR94q/0=
-go.uber.org/zap v1.10.0 h1:ORx85nbTijNz8ljznvCMR1ZBIPKFn3jQrag10X2AsuM=
-go.uber.org/zap v1.10.0/go.mod h1:vwi/ZaCAaUcBkycHslxD9B2zi4UTXhF60s6SWpuDF0Q=
-golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
-golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2 h1:VklqNMn3ovrHsnt90PveolxSbWFaJdECFbxSq0Mqo2M=
-golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
-golang.org/x/crypto v0.0.0-20190701094942-4def268fd1a4 h1:HuIa8hRrWRSrqYzx1qI49NNxhdi2PrY7gxVSq1JjLDc=
-golang.org/x/crypto v0.0.0-20190701094942-4def268fd1a4/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
-golang.org/x/crypto v0.0.0-20200204104054-c9f3fb736b72 h1:+ELyKg6m8UBf0nPFSqD0mi7zUfwPyXo23HNjMnXPz7w=
-golang.org/x/crypto v0.0.0-20200204104054-c9f3fb736b72/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
-golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
-golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
-golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU=
-golang.org/x/lint v0.0.0-20190301231843-5614ed5bae6f/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
-golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
-golang.org/x/lint v0.0.0-20190409202823-959b441ac422/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
-golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
-golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
-golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
-golang.org/x/net v0.0.0-20181114220301-adae6a3d119a/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
-golang.org/x/net v0.0.0-20181220203305-927f97764cc3/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
-golang.org/x/net v0.0.0-20190108225652-1e06a53dbb7e/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
-golang.org/x/net v0.0.0-20190125091013-d26f9f9a57f3/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
-golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
-golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
-golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
-golang.org/x/net v0.0.0-20190501004415-9ce7a6920f09/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
-golang.org/x/net v0.0.0-20190503192946-f4e77d36d62c h1:uOCk1iQW6Vc18bnC13MfzScl+wdKBmM9Y9kU7Z83/lw=
-golang.org/x/net v0.0.0-20190503192946-f4e77d36d62c/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
-golang.org/x/net v0.0.0-20200202094626-16171245cfb2 h1:CCH4IOTTfewWjGOlSp+zGcjutRKlBEZQ6wTn8ozI/nI=
-golang.org/x/net v0.0.0-20200202094626-16171245cfb2/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
-golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
-golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
-golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45 h1:SVwTIAaPC2U/AvvLNZ2a7OVsmBpC8L5BlwK1whH3hm0=
-golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
-golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
-golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
-golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
-golang.org/x/sync v0.0.0-20190227155943-e225da77a7e6/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
-golang.org/x/sync v0.0.0-20190423024810-112230192c58 h1:8gQV6CLnAEikrhgkHFbMAEhagSSnXWGV915qUMm9mrU=
-golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
-golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
-golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
-golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
-golang.org/x/sys v0.0.0-20181107165924-66b7b1311ac8/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
-golang.org/x/sys v0.0.0-20181116152217-5ac8a444bdc5/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
-golang.org/x/sys v0.0.0-20181122145206-62eef0e2fa9b/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
-golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
-golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
-golang.org/x/sys v0.0.0-20190502145724-3ef323f4f1fd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
-golang.org/x/sys v0.0.0-20190507160741-ecd444e8653b h1:ag/x1USPSsqHud38I9BAC88qdNLDHHtQ4mlgQIZPPNA=
-golang.org/x/sys v0.0.0-20190507160741-ecd444e8653b/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
-golang.org/x/sys v0.0.0-20190726091711-fc99dfbffb4e h1:D5TXcfTk7xF7hvieo4QErS3qqCB4teTffacDWr7CI+0=
-golang.org/x/sys v0.0.0-20190726091711-fc99dfbffb4e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
-golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
-golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
-golang.org/x/text v0.3.2 h1:tW2bmiBqwgJj/UpqtC8EpXEZVYOwU0yG4iWbprSVAcs=
-golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk=
-golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
-golang.org/x/tools v0.0.0-20180828015842-6cd1fcedba52/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
-golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
-golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
-golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY=
-golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
-golang.org/x/tools v0.0.0-20190312170243-e65039ee4138/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
-golang.org/x/tools v0.0.0-20190506145303-2d16b83fe98c/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q=
-golang.org/x/tools v0.0.0-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q=
-golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543 h1:E7g+9GITq07hpfrRu66IVDexMakfv52eLZ2CXBWiKr4=
-golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
-google.golang.org/api v0.3.1/go.mod h1:6wY9I6uQWHQ8EM57III9mq/AjF+i8G65rmVagqKMtkk=
-google.golang.org/api v0.4.0/go.mod h1:8k5glujaEP+g9n7WNsDg8QP6cUVNI86fCNMcbazEtwE=
-google.golang.org/api v0.6.0/go.mod h1:btoxGiFvQNVUZQ8W08zLtrVS08CNpINPEfxXxgJL1Q4=
-google.golang.org/api v0.15.0 h1:yzlyyDW/J0w8yNFJIhiAJy4kq74S+1DOLdawELNxFMA=
-google.golang.org/api v0.15.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI=
-google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM=
-google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
-google.golang.org/appengine v1.5.0 h1:KxkO13IPW4Lslp2bz+KHP2E3gtFlrIGNThxkZQ3g+4c=
-google.golang.org/appengine v1.5.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
-google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc=
-google.golang.org/genproto v0.0.0-20190307195333-5fe7a883aa19/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE=
-google.golang.org/genproto v0.0.0-20190418145605-e7d98fc518a7/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE=
-google.golang.org/genproto v0.0.0-20190425155659-357c62f0e4bb/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE=
-google.golang.org/genproto v0.0.0-20190502173448-54afdca5d873/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE=
-google.golang.org/genproto v0.0.0-20190530194941-fb225487d101/go.mod h1:z3L6/3dTEVtUr6QSP8miRzeRqwQOioJ9I66odjN4I7s=
-google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55 h1:gSJIx1SDwno+2ElGhA4+qG2zF97qiUzTM+rQ0klBOcE=
-google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc=
-google.golang.org/grpc v1.17.0/go.mod h1:6QZJwpn2B+Zp71q/5VxRsJ6NXXVCE5NRUHRo+f3cWCs=
-google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c=
-google.golang.org/grpc v1.19.1/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c=
-google.golang.org/grpc v1.20.1 h1:Hz2g2wirWK7H0qIIhGIqRGTuMwTE8HEKFnDZZ7lm9NU=
-google.golang.org/grpc v1.20.1/go.mod h1:10oTOabMzJvdu6/UiuZezV6QK5dSlG84ov/aaiqXj38=
-google.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg=
-google.golang.org/grpc v1.26.0 h1:2dTRdpdFEEhJYQD8EMLB61nnrzSCTbG38PhqdhvOltg=
-google.golang.org/grpc v1.26.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk=
-gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw=
-gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
-gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127 h1:qIbj1fsPNlZgppZ+VLlY7N33q108Sa+fhmuc+sWQYwY=
-gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
-gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15 h1:YR8cESwS4TdDjEe65xsg0ogRM/Nc3DYOhEAlW+xobZo=
-gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
-gopkg.in/fsnotify.v1 v1.4.7 h1:xOHLXZwVvI9hhs+cLKq5+I5onOuwQLhQwiu63xxlHs4=
-gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys=
-gopkg.in/resty.v1 v1.12.0/go.mod h1:mDo4pnntr5jdWRML875a/NmxYqAlA73dVijT2AXvQQo=
-gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 h1:uRGJdciOHaEIrze2W8Q3AKkepLTh2hOroT7a+7czfdQ=
-gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw=
-gopkg.in/yaml.v2 v2.0.0-20170812160011-eb3733d160e7/go.mod h1:JAlM8MvJe8wmxCU4Bli9HhUf9+ttbYbLASfIpnQbh74=
-gopkg.in/yaml.v2 v2.2.1 h1:mUhvW9EsL+naU5Q3cakzfE91YhliOondGd6ZrsDBHQE=
-gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
-gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
-gopkg.in/yaml.v2 v2.2.8 h1:obN1ZagJSUGI0Ek/LBmuj4SNLPfIny3KsKFopxRdj10=
-gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
-honnef.co/go/tools v0.0.0-20180728063816-88497007e858/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
-honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
-honnef.co/go/tools v0.0.0-20190106161140-3f1c8253044a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
-honnef.co/go/tools v0.0.0-20190418001031-e561f6794a2a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
-honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
-pack.ag/amqp v0.11.0 h1:ot/IA0enDkt4/c8xfbCO7AZzjM4bHys/UffnFmnHUnU=
-pack.ag/amqp v0.11.0/go.mod h1:4/cbmt4EJXSKlG6LCfWHoqmN0uFdy5i/+YFz+fTfhV4=
-rsc.io/binaryregexp v0.2.0/go.mod h1:qTv7/COck+e2FymRvadv62gMdZztPaShugOCi3I+8D8=
diff --git a/v2/test/conformance/run_test.go b/v2/test/conformance/run_test.go
index 37714511f..5e12ced90 100644
--- a/v2/test/conformance/run_test.go
+++ b/v2/test/conformance/run_test.go
@@ -1,3 +1,5 @@
+// +build conformance
+
package conformance
import (