diff --git a/.circleci/config.yml b/.circleci/config.yml index b9d947020..52f0e10ff 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -2,7 +2,7 @@ version: 2 jobs: build: docker: - - image: circleci/golang:1.11.5 + - image: circleci/golang:1.13.4 environment: TEST_RESULTS: /tmp/test-results diff --git a/.gitignore b/.gitignore index 606ce3e90..fe4744be7 100644 --- a/.gitignore +++ b/.gitignore @@ -24,3 +24,5 @@ bazel-* # Others ## IDE-specific *.iml + +tmp/ diff --git a/alias.go b/alias.go index 92f5b40c7..7bc4e54d3 100644 --- a/alias.go +++ b/alias.go @@ -95,10 +95,11 @@ var ( // Client Options - WithEventDefaulter = client.WithEventDefaulter - WithUUIDs = client.WithUUIDs - WithTimeNow = client.WithTimeNow - WithConverterFn = client.WithConverterFn + WithEventDefaulter = client.WithEventDefaulter + WithUUIDs = client.WithUUIDs + WithTimeNow = client.WithTimeNow + WithConverterFn = client.WithConverterFn + WithDataContentType = client.WithDataContentType // Event Creation diff --git a/cmd/samples/simple/http/sender/main.go b/cmd/samples/simple/http/sender/main.go index afd36b9a7..571c8a636 100644 --- a/cmd/samples/simple/http/sender/main.go +++ b/cmd/samples/simple/http/sender/main.go @@ -31,13 +31,15 @@ func main() { } var version string - switch i % 3 { + switch i % 4 { case 0: version = cloudevents.VersionV01 case 1: version = cloudevents.VersionV02 case 2: version = cloudevents.VersionV03 + case 3: + version = cloudevents.VersionV1 } event := cloudevents.NewEvent(version) diff --git a/go.mod b/go.mod index 5893debd7..1ebd9d7fc 100644 --- a/go.mod +++ b/go.mod @@ -14,7 +14,6 @@ require ( github.com/nats-io/nats-server/v2 v2.0.0 // indirect github.com/nats-io/nats.go v1.8.1 github.com/pkg/errors v0.8.1 // indirect - github.com/prometheus/client_golang v0.9.3-0.20190127221311-3c4408c8b829 // indirect github.com/stretchr/testify v1.3.0 go.opencensus.io v0.22.0 go.uber.org/atomic v1.4.0 // indirect @@ -23,3 +22,5 @@ require ( golang.org/x/sync v0.0.0-20190423024810-112230192c58 pack.ag/amqp v0.11.0 ) + +go 1.13 diff --git a/pkg/cloudevents/client/client.go b/pkg/cloudevents/client/client.go index a36fe4718..a5e5c27a1 100644 --- a/pkg/cloudevents/client/client.go +++ b/pkg/cloudevents/client/client.go @@ -55,14 +55,15 @@ func New(t transport.Transport, opts ...Option) (Client, error) { // 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 and WithUUIDs client options are also applied to the client, -// all outbound events will have a time and id set if not already present. +// 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()) + c, err := New(t, WithTimeNow(), WithUUIDs(), WithDataContentType(cloudevents.ApplicationJSON)) if err != nil { return nil, err } diff --git a/pkg/cloudevents/client/defaulters.go b/pkg/cloudevents/client/defaulters.go index 40bd85a9c..e827dec23 100644 --- a/pkg/cloudevents/client/defaulters.go +++ b/pkg/cloudevents/client/defaulters.go @@ -35,3 +35,17 @@ func DefaultTimeToNowIfNotSet(ctx context.Context, event cloudevents.Event) clou } 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/pkg/cloudevents/client/defaulters_test.go b/pkg/cloudevents/client/defaulters_test.go index ea9f75989..d83f748f2 100644 --- a/pkg/cloudevents/client/defaulters_test.go +++ b/pkg/cloudevents/client/defaulters_test.go @@ -6,60 +6,46 @@ import ( "time" "github.com/cloudevents/sdk-go/pkg/cloudevents" - "github.com/cloudevents/sdk-go/pkg/cloudevents/types" "github.com/google/go-cmp/cmp" ) -func TestDefaultIDToUUIDIfNotSet(t *testing.T) { - testCases := map[string]struct { - event cloudevents.Event - }{ - "nil context": { - event: cloudevents.Event{}, - }, - "v0.1 empty": { - event: cloudevents.Event{ - Context: &cloudevents.EventContextV01{}, - }, - }, - "v0.2 empty": { - event: cloudevents.Event{ - Context: &cloudevents.EventContextV02{}, - }, - }, - "v0.3 empty": { - event: cloudevents.Event{ - Context: &cloudevents.EventContextV03{}, - }, - }, - "v0.1 no change": { - event: cloudevents.Event{ - Context: cloudevents.EventContextV01{EventID: "abc"}.AsV01(), - }, - }, - "v0.2 no change": { - event: cloudevents.Event{ - Context: cloudevents.EventContextV02{ID: "abc"}.AsV02(), - }, - }, - "v0.3 no change": { - event: cloudevents.Event{ - Context: cloudevents.EventContextV03{ID: "abc"}.AsV03(), - }, - }, - } - for n, tc := range testCases { - t.Run(n, func(t *testing.T) { - - got := DefaultIDToUUIDIfNotSet(context.TODO(), tc.event) - - if got.Context != nil && got.Context.AsV02().ID == "" { +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{}, @@ -82,56 +68,43 @@ func TestDefaultIDToUUIDIfNotSetImmutable(t *testing.T) { } } -func TestDefaultTimeToNowIfNotSet(t *testing.T) { - testCases := map[string]struct { - event cloudevents.Event - }{ - "nil context": { - event: cloudevents.Event{}, - }, - "v0.1 empty": { - event: cloudevents.Event{ - Context: &cloudevents.EventContextV01{}, - }, - }, - "v0.2 empty": { - event: cloudevents.Event{ - Context: &cloudevents.EventContextV02{}, - }, - }, - "v0.3 empty": { - event: cloudevents.Event{ - Context: &cloudevents.EventContextV03{}, - }, - }, - "v0.1 no change": { - event: cloudevents.Event{ - Context: cloudevents.EventContextV01{EventTime: &types.Timestamp{Time: time.Now()}}.AsV01(), - }, - }, - "v0.2 no change": { - event: cloudevents.Event{ - Context: cloudevents.EventContextV02{Time: &types.Timestamp{Time: time.Now()}}.AsV02(), - }, - }, - "v0.3 no change": { - event: cloudevents.Event{ - Context: cloudevents.EventContextV03{Time: &types.Timestamp{Time: time.Now()}}.AsV03(), - }, - }, - } - for n, tc := range testCases { - t.Run(n, func(t *testing.T) { - - got := DefaultTimeToNowIfNotSet(context.TODO(), tc.event) - - if got.Context != nil && got.Context.AsV02().Time.IsZero() { +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{}, @@ -153,3 +126,34 @@ func TestDefaultTimeToNowIfNotSetImmutable(t *testing.T) { 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/pkg/cloudevents/client/options.go b/pkg/cloudevents/client/options.go index 6e5051c3e..bb2eff9cc 100644 --- a/pkg/cloudevents/client/options.go +++ b/pkg/cloudevents/client/options.go @@ -27,6 +27,16 @@ func WithUUIDs() Option { } } +// 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 {