mirror of
https://github.com/goharbor/harbor.git
synced 2025-02-26 16:51:47 +01:00
feat: integrate CloudEvents to webhook (#18322)
Integrate CloudEvents as payload format for webhook. Closes: #17748 Signed-off-by: chlins <chenyuzh@vmware.com>
This commit is contained in:
parent
125daf9cdb
commit
65e675d2e6
@ -8522,7 +8522,7 @@ definitions:
|
||||
PayloadFormatType:
|
||||
type: string
|
||||
description: The type of webhook paylod format.
|
||||
example: 'cloudevent'
|
||||
example: 'CloudEvents'
|
||||
PayloadFormat:
|
||||
type: object
|
||||
description: Webhook supported payload format type collections.
|
||||
|
@ -567,8 +567,8 @@ func (c *controller) AddLabel(ctx context.Context, artifactID int64, labelID int
|
||||
LabelID: labelID,
|
||||
Ctx: ctx,
|
||||
}
|
||||
if err := e.Build(metaData); err == nil {
|
||||
if err := e.Publish(); err != nil {
|
||||
if err := e.Build(ctx, metaData); err == nil {
|
||||
if err := e.Publish(ctx); err != nil {
|
||||
log.Error(errors.Wrap(err, "mark label to resource handler: event publish"))
|
||||
}
|
||||
} else {
|
||||
|
@ -81,7 +81,7 @@ func (suite *AuditLogHandlerTestSuite) TestSubscribeTagEvent() {
|
||||
|
||||
notifier.Subscribe(event.TopicCreateProject, suite.auditLogHandler)
|
||||
// event data should implement the interface TopicEvent
|
||||
ne.BuildAndPublish(&metadata.CreateProjectEventMetadata{
|
||||
ne.BuildAndPublish(context.TODO(), &metadata.CreateProjectEventMetadata{
|
||||
ProjectID: 1,
|
||||
Project: "test",
|
||||
Operator: "admin",
|
||||
|
@ -1,13 +1,13 @@
|
||||
package util
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"fmt"
|
||||
"strings"
|
||||
|
||||
"github.com/goharbor/harbor/src/lib/config"
|
||||
"github.com/goharbor/harbor/src/lib/log"
|
||||
"github.com/goharbor/harbor/src/lib/orm"
|
||||
"github.com/goharbor/harbor/src/pkg/distribution"
|
||||
policy_model "github.com/goharbor/harbor/src/pkg/notification/policy/model"
|
||||
"github.com/goharbor/harbor/src/pkg/notifier/event"
|
||||
@ -15,9 +15,9 @@ import (
|
||||
)
|
||||
|
||||
// SendHookWithPolicies send hook by publishing topic of specified target type(notify type)
|
||||
func SendHookWithPolicies(policies []*policy_model.Policy, payload *notifyModel.Payload, eventType string) error {
|
||||
func SendHookWithPolicies(ctx context.Context, policies []*policy_model.Policy, payload *notifyModel.Payload, eventType string) error {
|
||||
// if global notification configured disabled, return directly
|
||||
if !config.NotificationEnable(orm.Context()) {
|
||||
if !config.NotificationEnable(ctx) {
|
||||
log.Debug("notification feature is not enabled")
|
||||
return nil
|
||||
}
|
||||
@ -28,14 +28,15 @@ func SendHookWithPolicies(policies []*policy_model.Policy, payload *notifyModel.
|
||||
for _, target := range targets {
|
||||
evt := &event.Event{}
|
||||
hookMetadata := &event.HookMetaData{
|
||||
ProjectID: ply.ProjectID,
|
||||
EventType: eventType,
|
||||
PolicyID: ply.ID,
|
||||
Payload: payload,
|
||||
Target: &target,
|
||||
}
|
||||
// It should never affect evaluating other policies when one is failed, but error should return
|
||||
if err := evt.Build(hookMetadata); err == nil {
|
||||
if err := evt.Publish(); err != nil {
|
||||
if err := evt.Build(ctx, hookMetadata); err == nil {
|
||||
if err := evt.Publish(ctx); err != nil {
|
||||
errRet = true
|
||||
log.Errorf("failed to publish hook notify event: %v", err)
|
||||
}
|
||||
|
@ -88,7 +88,7 @@ func (a *Handler) handle(ctx context.Context, event *event.ArtifactEvent) error
|
||||
return err
|
||||
}
|
||||
|
||||
err = util.SendHookWithPolicies(policies, payload, event.EventType)
|
||||
err = util.SendHookWithPolicies(ctx, policies, payload, event.EventType)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
@ -14,7 +14,6 @@ import (
|
||||
"github.com/goharbor/harbor/src/jobservice/job"
|
||||
"github.com/goharbor/harbor/src/lib/config"
|
||||
"github.com/goharbor/harbor/src/lib/log"
|
||||
"github.com/goharbor/harbor/src/lib/orm"
|
||||
"github.com/goharbor/harbor/src/pkg/notification"
|
||||
"github.com/goharbor/harbor/src/pkg/notifier/model"
|
||||
proModels "github.com/goharbor/harbor/src/pkg/project/models"
|
||||
@ -46,12 +45,12 @@ func (r *ReplicationHandler) Handle(ctx context.Context, value interface{}) erro
|
||||
return fmt.Errorf("nil replication event")
|
||||
}
|
||||
|
||||
payload, project, err := constructReplicationPayload(rpEvent)
|
||||
payload, project, err := constructReplicationPayload(ctx, rpEvent)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
policies, err := notification.PolicyMgr.GetRelatedPolices(orm.Context(), project.ProjectID, rpEvent.EventType)
|
||||
policies, err := notification.PolicyMgr.GetRelatedPolices(ctx, project.ProjectID, rpEvent.EventType)
|
||||
if err != nil {
|
||||
log.Errorf("failed to find policy for %s event: %v", rpEvent.EventType, err)
|
||||
return err
|
||||
@ -60,7 +59,7 @@ func (r *ReplicationHandler) Handle(ctx context.Context, value interface{}) erro
|
||||
log.Debugf("cannot find policy for %s event: %v", rpEvent.EventType, rpEvent)
|
||||
return nil
|
||||
}
|
||||
err = util.SendHookWithPolicies(policies, payload, rpEvent.EventType)
|
||||
err = util.SendHookWithPolicies(ctx, policies, payload, rpEvent.EventType)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@ -72,8 +71,7 @@ func (r *ReplicationHandler) IsStateful() bool {
|
||||
return false
|
||||
}
|
||||
|
||||
func constructReplicationPayload(event *event.ReplicationEvent) (*model.Payload, *proModels.Project, error) {
|
||||
ctx := orm.Context()
|
||||
func constructReplicationPayload(ctx context.Context, event *event.ReplicationEvent) (*model.Payload, *proModels.Project, error) {
|
||||
task, err := replication.Ctl.GetTask(ctx, event.ReplicationTaskID)
|
||||
if err != nil {
|
||||
log.Errorf("failed to get replication task %d: error: %v", event.ReplicationTaskID, err)
|
||||
@ -191,7 +189,7 @@ func constructReplicationPayload(event *event.ReplicationEvent) (*model.Payload,
|
||||
payload.EventData.Replication.FailedArtifact = []*ctlModel.ArtifactInfo{failedArtifact}
|
||||
}
|
||||
|
||||
prj, err := project.Ctl.GetByName(orm.Context(), prjName, project.Metadata(true))
|
||||
prj, err := project.Ctl.GetByName(ctx, prjName, project.Metadata(true))
|
||||
if err != nil {
|
||||
log.Errorf("failed to get project %s, error: %v", prjName, err)
|
||||
return nil, nil, err
|
||||
|
@ -15,7 +15,6 @@
|
||||
package artifact
|
||||
|
||||
import (
|
||||
"context"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
@ -28,6 +27,7 @@ import (
|
||||
repctl "github.com/goharbor/harbor/src/controller/replication"
|
||||
repctlmodel "github.com/goharbor/harbor/src/controller/replication/model"
|
||||
"github.com/goharbor/harbor/src/lib/config"
|
||||
"github.com/goharbor/harbor/src/lib/orm"
|
||||
_ "github.com/goharbor/harbor/src/pkg/config/db"
|
||||
_ "github.com/goharbor/harbor/src/pkg/config/inmemory"
|
||||
"github.com/goharbor/harbor/src/pkg/notification"
|
||||
@ -109,7 +109,7 @@ func TestReplicationHandler_Handle(t *testing.T) {
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
err := handler.Handle(context.TODO(), tt.args.data)
|
||||
err := handler.Handle(orm.Context(), tt.args.data)
|
||||
if tt.wantErr {
|
||||
require.NotNil(t, err, "Error: %s", err)
|
||||
return
|
||||
|
@ -12,7 +12,6 @@ import (
|
||||
"github.com/goharbor/harbor/src/lib/config"
|
||||
"github.com/goharbor/harbor/src/lib/errors"
|
||||
"github.com/goharbor/harbor/src/lib/log"
|
||||
"github.com/goharbor/harbor/src/lib/orm"
|
||||
"github.com/goharbor/harbor/src/pkg/notification"
|
||||
"github.com/goharbor/harbor/src/pkg/notifier/model"
|
||||
)
|
||||
@ -45,7 +44,7 @@ func (r *RetentionHandler) Handle(ctx context.Context, value interface{}) error
|
||||
return nil
|
||||
}
|
||||
|
||||
payload, dryRun, project, err := r.constructRetentionPayload(trEvent)
|
||||
payload, dryRun, project, err := r.constructRetentionPayload(ctx, trEvent)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@ -64,7 +63,7 @@ func (r *RetentionHandler) Handle(ctx context.Context, value interface{}) error
|
||||
log.Debugf("cannot find policy for %s event: %v", trEvent.EventType, trEvent)
|
||||
return nil
|
||||
}
|
||||
err = util.SendHookWithPolicies(policies, payload, trEvent.EventType)
|
||||
err = util.SendHookWithPolicies(ctx, policies, payload, trEvent.EventType)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@ -76,8 +75,7 @@ func (r *RetentionHandler) IsStateful() bool {
|
||||
return false
|
||||
}
|
||||
|
||||
func (r *RetentionHandler) constructRetentionPayload(event *event.RetentionEvent) (*model.Payload, bool, int64, error) {
|
||||
ctx := orm.Context()
|
||||
func (r *RetentionHandler) constructRetentionPayload(ctx context.Context, event *event.RetentionEvent) (*model.Payload, bool, int64, error) {
|
||||
task, err := retention.Ctl.GetRetentionExecTask(ctx, event.TaskID)
|
||||
if err != nil {
|
||||
log.Errorf("failed to get retention task %d: error: %v", event.TaskID, err)
|
||||
|
@ -1,7 +1,6 @@
|
||||
package artifact
|
||||
|
||||
import (
|
||||
"context"
|
||||
"os"
|
||||
"testing"
|
||||
"time"
|
||||
@ -14,6 +13,7 @@ import (
|
||||
"github.com/goharbor/harbor/src/controller/event"
|
||||
"github.com/goharbor/harbor/src/controller/retention"
|
||||
"github.com/goharbor/harbor/src/lib/config"
|
||||
"github.com/goharbor/harbor/src/lib/orm"
|
||||
"github.com/goharbor/harbor/src/lib/selector"
|
||||
"github.com/goharbor/harbor/src/pkg/notification"
|
||||
policy_model "github.com/goharbor/harbor/src/pkg/notification/policy/model"
|
||||
@ -97,7 +97,7 @@ func TestRetentionHandler_Handle(t *testing.T) {
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
err := handler.Handle(context.TODO(), tt.args.data)
|
||||
err := handler.Handle(orm.Context(), tt.args.data)
|
||||
if tt.wantErr {
|
||||
require.NotNil(t, err, "Error: %s", err)
|
||||
return
|
||||
|
@ -23,7 +23,6 @@ import (
|
||||
"github.com/goharbor/harbor/src/controller/event/handler/util"
|
||||
"github.com/goharbor/harbor/src/controller/project"
|
||||
"github.com/goharbor/harbor/src/lib/log"
|
||||
"github.com/goharbor/harbor/src/lib/orm"
|
||||
"github.com/goharbor/harbor/src/pkg/notification"
|
||||
notifyModel "github.com/goharbor/harbor/src/pkg/notifier/model"
|
||||
proModels "github.com/goharbor/harbor/src/pkg/project/models"
|
||||
@ -48,7 +47,7 @@ func (qp *Handler) Handle(ctx context.Context, value interface{}) error {
|
||||
return fmt.Errorf("nil quota event")
|
||||
}
|
||||
|
||||
prj, err := project.Ctl.GetByName(orm.Context(), quotaEvent.Project.Name, project.Metadata(true))
|
||||
prj, err := project.Ctl.GetByName(ctx, quotaEvent.Project.Name, project.Metadata(true))
|
||||
if err != nil {
|
||||
log.Errorf("failed to get project:%s, error: %v", quotaEvent.Project.Name, err)
|
||||
return err
|
||||
@ -69,7 +68,7 @@ func (qp *Handler) Handle(ctx context.Context, value interface{}) error {
|
||||
return err
|
||||
}
|
||||
|
||||
err = util.SendHookWithPolicies(policies, payload, quotaEvent.EventType)
|
||||
err = util.SendHookWithPolicies(ctx, policies, payload, quotaEvent.EventType)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
@ -26,6 +26,7 @@ import (
|
||||
common_dao "github.com/goharbor/harbor/src/common/dao"
|
||||
"github.com/goharbor/harbor/src/controller/event"
|
||||
"github.com/goharbor/harbor/src/lib/config"
|
||||
"github.com/goharbor/harbor/src/lib/orm"
|
||||
_ "github.com/goharbor/harbor/src/pkg/config/inmemory"
|
||||
"github.com/goharbor/harbor/src/pkg/notification"
|
||||
"github.com/goharbor/harbor/src/pkg/notification/policy"
|
||||
@ -96,7 +97,7 @@ func (suite *QuotaPreprocessHandlerSuite) TearDownSuite() {
|
||||
// TestHandle ...
|
||||
func (suite *QuotaPreprocessHandlerSuite) TestHandle() {
|
||||
handler := &Handler{}
|
||||
err := handler.Handle(context.TODO(), suite.evt)
|
||||
err := handler.Handle(orm.Context(), suite.evt)
|
||||
suite.NoError(err)
|
||||
}
|
||||
|
||||
|
@ -18,8 +18,6 @@ import (
|
||||
"context"
|
||||
"time"
|
||||
|
||||
o "github.com/beego/beego/v2/client/orm"
|
||||
|
||||
"github.com/goharbor/harbor/src/controller/artifact"
|
||||
"github.com/goharbor/harbor/src/controller/event"
|
||||
"github.com/goharbor/harbor/src/controller/event/handler/util"
|
||||
@ -27,7 +25,6 @@ import (
|
||||
"github.com/goharbor/harbor/src/controller/scan"
|
||||
"github.com/goharbor/harbor/src/lib/errors"
|
||||
"github.com/goharbor/harbor/src/lib/log"
|
||||
"github.com/goharbor/harbor/src/lib/orm"
|
||||
"github.com/goharbor/harbor/src/pkg/notification"
|
||||
"github.com/goharbor/harbor/src/pkg/notifier/model"
|
||||
proModels "github.com/goharbor/harbor/src/pkg/project/models"
|
||||
@ -66,17 +63,17 @@ func (si *Handler) Handle(ctx context.Context, value interface{}) error {
|
||||
}
|
||||
|
||||
// Get project
|
||||
prj, err := project.Ctl.Get(orm.Context(), e.Artifact.NamespaceID, project.Metadata(true))
|
||||
prj, err := project.Ctl.Get(ctx, e.Artifact.NamespaceID, project.Metadata(true))
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "scan preprocess handler")
|
||||
}
|
||||
|
||||
payload, err := constructScanImagePayload(e, prj)
|
||||
payload, err := constructScanImagePayload(ctx, e, prj)
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "scan preprocess handler")
|
||||
}
|
||||
|
||||
err = util.SendHookWithPolicies(policies, payload, e.EventType)
|
||||
err = util.SendHookWithPolicies(ctx, policies, payload, e.EventType)
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "scan preprocess handler")
|
||||
}
|
||||
@ -89,7 +86,7 @@ func (si *Handler) IsStateful() bool {
|
||||
return false
|
||||
}
|
||||
|
||||
func constructScanImagePayload(event *event.ScanImageEvent, project *proModels.Project) (*model.Payload, error) {
|
||||
func constructScanImagePayload(ctx context.Context, event *event.ScanImageEvent, project *proModels.Project) (*model.Payload, error) {
|
||||
repoType := proModels.ProjectPrivate
|
||||
if project.IsPublic() {
|
||||
repoType = proModels.ProjectPublic
|
||||
@ -121,8 +118,6 @@ func constructScanImagePayload(event *event.ScanImageEvent, project *proModels.P
|
||||
return nil, errors.Wrap(err, "construct scan payload")
|
||||
}
|
||||
|
||||
ctx := orm.NewContext(context.TODO(), o.NewOrm())
|
||||
|
||||
art, err := artifact.Ctl.GetByReference(ctx, event.Artifact.Repository, event.Artifact.Digest, nil)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
|
@ -28,6 +28,7 @@ import (
|
||||
"github.com/goharbor/harbor/src/controller/event"
|
||||
sc "github.com/goharbor/harbor/src/controller/scan"
|
||||
"github.com/goharbor/harbor/src/lib/config"
|
||||
"github.com/goharbor/harbor/src/lib/orm"
|
||||
_ "github.com/goharbor/harbor/src/pkg/config/inmemory"
|
||||
"github.com/goharbor/harbor/src/pkg/notification"
|
||||
"github.com/goharbor/harbor/src/pkg/notification/policy"
|
||||
@ -137,7 +138,7 @@ func (suite *ScanImagePreprocessHandlerSuite) TearDownSuite() {
|
||||
func (suite *ScanImagePreprocessHandlerSuite) TestHandle() {
|
||||
handler := &Handler{}
|
||||
|
||||
err := handler.Handle(context.TODO(), suite.evt)
|
||||
err := handler.Handle(orm.Context(), suite.evt)
|
||||
suite.NoError(err)
|
||||
}
|
||||
|
||||
|
@ -239,7 +239,7 @@ func (c *controller) ProxyManifest(ctx context.Context, art lib.ArtifactInfo, re
|
||||
}
|
||||
}
|
||||
if a != nil {
|
||||
SendPullEvent(a, art.Tag, operator)
|
||||
SendPullEvent(bCtx, a, art.Tag, operator)
|
||||
}
|
||||
}(operator.FromContext(ctx))
|
||||
|
||||
|
@ -152,11 +152,11 @@ func (l *localHelper) CheckDependencies(ctx context.Context, repo string, man di
|
||||
}
|
||||
|
||||
// SendPullEvent send a pull image event
|
||||
func SendPullEvent(a *artifact.Artifact, tag, operator string) {
|
||||
func SendPullEvent(ctx context.Context, a *artifact.Artifact, tag, operator string) {
|
||||
e := &metadata.PullArtifactEventMetadata{
|
||||
Artifact: &a.Artifact,
|
||||
Tag: tag,
|
||||
Operator: operator,
|
||||
}
|
||||
event.BuildAndPublish(e)
|
||||
event.BuildAndPublish(ctx, e)
|
||||
}
|
||||
|
@ -177,9 +177,9 @@ func buildTaskQuery(execID int64, query *q.Query) *q.Query {
|
||||
|
||||
func (c *controller) GetLastTriggerTime(ctx context.Context, eventType string, policyID int64) (time.Time, error) {
|
||||
query := q.New(q.KeyWords{
|
||||
"vendor_type": webhookJobVendors,
|
||||
"vendor_id": policyID,
|
||||
"ExtraAttrs.type": eventType,
|
||||
"vendor_type": webhookJobVendors,
|
||||
"vendor_id": policyID,
|
||||
"ExtraAttrs.event_type": eventType,
|
||||
})
|
||||
// fetch the latest execution sort by start_time
|
||||
execs, err := c.execMgr.List(ctx, query.First(q.NewSort("start_time", true)))
|
||||
|
@ -12,6 +12,7 @@ require (
|
||||
github.com/bmatcuk/doublestar v1.1.1
|
||||
github.com/casbin/casbin v1.9.1
|
||||
github.com/cenkalti/backoff/v4 v4.1.2
|
||||
github.com/cloudevents/sdk-go/v2 v2.13.0
|
||||
github.com/coreos/go-oidc/v3 v3.0.0
|
||||
github.com/dghubble/sling v1.1.0
|
||||
github.com/docker/distribution v2.8.1+incompatible
|
||||
@ -172,6 +173,8 @@ require (
|
||||
go.opentelemetry.io/otel/metric v0.22.0 // indirect
|
||||
go.opentelemetry.io/proto/otlp v0.11.0 // indirect
|
||||
go.uber.org/atomic v1.7.0 // indirect
|
||||
go.uber.org/multierr v1.6.0 // indirect
|
||||
go.uber.org/zap v1.19.0 // indirect
|
||||
golang.org/x/sys v0.2.0 // indirect
|
||||
golang.org/x/term v0.2.0 // indirect
|
||||
google.golang.org/api v0.61.0 // indirect
|
||||
|
14
src/go.sum
14
src/go.sum
@ -191,6 +191,8 @@ github.com/beego/beego/v2 v2.0.6 h1:21Aqz3+RzUE1yP9a5xdU6LK54n9Z7NLEJtR4PE7NrPQ=
|
||||
github.com/beego/beego/v2 v2.0.6/go.mod h1:CH2/JIaB4ceGYVQlYqTAFft4pVk/ol1ZkakUrUvAyns=
|
||||
github.com/beego/i18n v0.0.0-20140604031826-e87155e8f0c0 h1:fQaDnUQvBXHHQdGBu9hz8nPznB4BeiPQokvmQVjmNEw=
|
||||
github.com/beego/i18n v0.0.0-20140604031826-e87155e8f0c0/go.mod h1:KLeFCpAMq2+50NkXC8iiJxLLiiTfTqrGtKEVm+2fk7s=
|
||||
github.com/benbjohnson/clock v1.1.0 h1:Q92kusRqC1XV2MjkWETPvjJVqKetz1OzxZB7mHJLju8=
|
||||
github.com/benbjohnson/clock v1.1.0/go.mod h1:J11/hYXuz8f4ySSvYwY0FKfm+ezbsZBKZxNJlLklBHA=
|
||||
github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q=
|
||||
github.com/beorn7/perks v1.0.0/go.mod h1:KWe93zE9D1o94FZ5RNwFwVgaQK1VOXiVxmqh+CedLV8=
|
||||
github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM=
|
||||
@ -236,6 +238,8 @@ github.com/cilium/ebpf v0.4.0/go.mod h1:4tRaxcgiL706VnOzHOdBlY8IEAIdxINsQBcU4xJJ
|
||||
github.com/cilium/ebpf v0.6.2/go.mod h1:4tRaxcgiL706VnOzHOdBlY8IEAIdxINsQBcU4xJJXRs=
|
||||
github.com/clbanning/x2j v0.0.0-20191024224557-825249438eec/go.mod h1:jMjuTZXRI4dUb/I5gc9Hdhagfvm9+RyrPryS/auMzxE=
|
||||
github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw=
|
||||
github.com/cloudevents/sdk-go/v2 v2.13.0 h1:2zxDS8RyY1/wVPULGGbdgniGXSzLaRJVl136fLXGsYw=
|
||||
github.com/cloudevents/sdk-go/v2 v2.13.0/go.mod h1:xDmKfzNjM8gBvjaF8ijFjM1VYOVUEeUfapHMUX1T5To=
|
||||
github.com/cloudflare/cfssl v0.0.0-20190510060611-9c027c93ba9e h1:ZtyhUG4s94BMUCdgvRZySr/AXYL5CDcjxhIV/83xJog=
|
||||
github.com/cloudflare/cfssl v0.0.0-20190510060611-9c027c93ba9e/go.mod h1:yMWuSON2oQp+43nFtAV/uvKQIFpSPerB57DCt9t8sSA=
|
||||
github.com/cloudflare/golz4 v0.0.0-20150217214814-ef862a3cdc58/go.mod h1:EOBUe0h4xcZ5GoxqC5SDxFQ8gwyZPKQoEzownBlhI80=
|
||||
@ -409,6 +413,7 @@ github.com/edsrzf/mmap-go v1.0.0/go.mod h1:YO35OhQPt3KJa3ryjFM5Bs14WD66h8eGKpfaB
|
||||
github.com/elazarl/go-bindata-assetfs v1.0.1 h1:m0kkaHRKEu7tUIUFVwhGGGYClXvyl4RE03qmvRTNfbw=
|
||||
github.com/elazarl/goproxy v0.0.0-20180725130230-947c36da3153/go.mod h1:/Zj4wYkgs4iZTTu3o/KG3Itv/qCCa8VVMlb3i9OVuzc=
|
||||
github.com/emicklei/go-restful v0.0.0-20170410110728-ff4f55a20633/go.mod h1:otzb+WCGbkyDHkqmQmT5YD2WR4BBwUdeQoFo8l/7tVs=
|
||||
github.com/emicklei/go-restful v2.9.5+incompatible h1:spTtZBk5DYEvbxMVutUuTyh1Ao2r4iyvLdACqsl/Ljk=
|
||||
github.com/emicklei/go-restful v2.9.5+incompatible/go.mod h1:otzb+WCGbkyDHkqmQmT5YD2WR4BBwUdeQoFo8l/7tVs=
|
||||
github.com/emicklei/go-restful/v3 v3.8.0 h1:eCZ8ulSerjdAiaNpF7GxXIE7ZCMo1moN1qX+S609eVw=
|
||||
github.com/envoyproxy/go-control-plane v0.6.9/go.mod h1:SBwIajubJHhxtWwsL9s8ss4safvEdbitLhGGK48rN6g=
|
||||
@ -1263,6 +1268,7 @@ github.com/urfave/cli v0.0.0-20171014202726-7bc6a0acffa5/go.mod h1:70zkFmudgCuE/
|
||||
github.com/urfave/cli v1.20.0/go.mod h1:70zkFmudgCuE/ngEzBv17Jvp/497gISqfk5gWijbERA=
|
||||
github.com/urfave/cli v1.22.1/go.mod h1:Gos4lmkARVdJ6EkW0WaNv/tZAAMe9V7XWyB60NtXRu0=
|
||||
github.com/urfave/cli v1.22.2/go.mod h1:Gos4lmkARVdJ6EkW0WaNv/tZAAMe9V7XWyB60NtXRu0=
|
||||
github.com/valyala/bytebufferpool v1.0.0 h1:GqA5TC/0021Y/b9FG4Oi9Mr3q7XYx6KllzawFIhcdPw=
|
||||
github.com/vishvananda/netlink v0.0.0-20181108222139-023a6dafdcdf/go.mod h1:+SR5DhBJrl6ZM7CoCKvpw5BKroDKQ+PJqOg65H/2ktk=
|
||||
github.com/vishvananda/netlink v1.1.0/go.mod h1:cTgwzPIzzgDAYoQrMm0EdrjRUBkTqKYppBueQtXaqoE=
|
||||
github.com/vishvananda/netlink v1.1.1-0.20201029203352-d40f9887b852/go.mod h1:twkDnbuQxJYemMlGd4JFIcuhgX83tXhKS2B/PRMpOho=
|
||||
@ -1364,9 +1370,12 @@ go.uber.org/atomic v1.5.0/go.mod h1:sABNBOSYdrvTF6hTgEIbc7YasKWGhgEQZyfxyTvoXHQ=
|
||||
go.uber.org/atomic v1.6.0/go.mod h1:sABNBOSYdrvTF6hTgEIbc7YasKWGhgEQZyfxyTvoXHQ=
|
||||
go.uber.org/atomic v1.7.0 h1:ADUqmZGgLDDfbSL9ZmPxKTybcoEYHgpYfELNoN+7hsw=
|
||||
go.uber.org/atomic v1.7.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc=
|
||||
go.uber.org/goleak v1.1.10 h1:z+mqJhf6ss6BSfSM671tgKyZBFPTTJM+HLxnhPC3wu0=
|
||||
go.uber.org/goleak v1.1.10/go.mod h1:8a7PlsEVH3e/a/GLqe5IIrQx6GzcnRmZEufDUTk4A7A=
|
||||
go.uber.org/multierr v1.1.0/go.mod h1:wR5kodmAFQ0UK8QlbwjlSNy0Z68gJhDJUG5sjR94q/0=
|
||||
go.uber.org/multierr v1.3.0/go.mod h1:VgVr7evmIr6uPjLBxg28wmKNXyqE9akIJ5XnfpiKl+4=
|
||||
go.uber.org/multierr v1.5.0/go.mod h1:FeouvMocqHpRaaGuG9EjoKcStLC43Zu/fmqdUMPcKYU=
|
||||
go.uber.org/multierr v1.6.0 h1:y6IPFStTAIT5Ytl7/XYmHvzXQ7S3g/IeZW9hyZ5thw4=
|
||||
go.uber.org/multierr v1.6.0/go.mod h1:cdWPpRnG4AhwMwsgIHip0KRBQjJy5kYEpYjJxpXp9iU=
|
||||
go.uber.org/ratelimit v0.2.0 h1:UQE2Bgi7p2B85uP5dC2bbRtig0C+OeNRnNEafLjsLPA=
|
||||
go.uber.org/ratelimit v0.2.0/go.mod h1:YYBV4e4naJvhpitQrWJu1vCpgB7CboMe0qhltKt6mUg=
|
||||
@ -1375,6 +1384,8 @@ go.uber.org/zap v1.9.1/go.mod h1:vwi/ZaCAaUcBkycHslxD9B2zi4UTXhF60s6SWpuDF0Q=
|
||||
go.uber.org/zap v1.10.0/go.mod h1:vwi/ZaCAaUcBkycHslxD9B2zi4UTXhF60s6SWpuDF0Q=
|
||||
go.uber.org/zap v1.13.0/go.mod h1:zwrFLgMcdUuIBviXEYEH1YKNaOBnKXsx2IPda5bBwHM=
|
||||
go.uber.org/zap v1.17.0/go.mod h1:MXVU+bhUf/A7Xi2HNOnopQOrmycQ5Ih87HtOu4q5SSo=
|
||||
go.uber.org/zap v1.19.0 h1:mZQZefskPPCMIBCSEH0v2/iUqqLrYtaeqwD6FUGUnFE=
|
||||
go.uber.org/zap v1.19.0/go.mod h1:xg/QME4nWcxGxrpdeYfq7UvYrLh66cuVKdrbD1XF/NI=
|
||||
golang.org/x/crypto v0.0.0-20171113213409-9f005a07e0d3/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
|
||||
golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
|
||||
golang.org/x/crypto v0.0.0-20181009213950-7c1a557ab941/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
|
||||
@ -1440,6 +1451,7 @@ golang.org/x/lint v0.0.0-20190930215403-16217165b5de/go.mod h1:6SW0HCj/g11FgYtHl
|
||||
golang.org/x/lint v0.0.0-20191125180803-fdd1cda4f05f/go.mod h1:5qLYkcX4OjUUV8bRuDixDT3tpyyb+LUpUlRWLxfhWrs=
|
||||
golang.org/x/lint v0.0.0-20200130185559-910be7a94367/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY=
|
||||
golang.org/x/lint v0.0.0-20200302205851-738671d3881b/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY=
|
||||
golang.org/x/lint v0.0.0-20210508222113-6edffad5e616 h1:VLliZ0d+/avPrXXH+OakdXhpJuEoBZuwh1m2j7U6Iug=
|
||||
golang.org/x/lint v0.0.0-20210508222113-6edffad5e616/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY=
|
||||
golang.org/x/mobile v0.0.0-20190312151609-d3739f865fa6/go.mod h1:z+o9i4GpDbdi3rU15maQ/Ox0txvL9dWGYEHz965HBQE=
|
||||
golang.org/x/mobile v0.0.0-20190719004257-d2bd2a29d028/go.mod h1:E/iHnbuqvinMTCcRqshq8CkpyQDoeVncDDYHnLhea+o=
|
||||
@ -1692,6 +1704,7 @@ golang.org/x/tools v0.0.0-20190927191325-030b2cf1153e/go.mod h1:b+2E5dAYhXwXZwtn
|
||||
golang.org/x/tools v0.0.0-20191012152004-8de300cfc20a/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
||||
golang.org/x/tools v0.0.0-20191029041327-9cc4af7d6b2c/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
||||
golang.org/x/tools v0.0.0-20191029190741-b9c20aec41a5/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
||||
golang.org/x/tools v0.0.0-20191108193012-7d206e10da11/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
||||
golang.org/x/tools v0.0.0-20191112195655-aa38f8e97acc/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
||||
golang.org/x/tools v0.0.0-20191113191852-77e3bb0ad9e7/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
||||
golang.org/x/tools v0.0.0-20191115202509-3a792d9c32b2/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
||||
@ -1725,6 +1738,7 @@ golang.org/x/tools v0.1.1/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk=
|
||||
golang.org/x/tools v0.1.2/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk=
|
||||
golang.org/x/tools v0.1.4/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk=
|
||||
golang.org/x/tools v0.1.5/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk=
|
||||
golang.org/x/tools v0.1.12 h1:VveCTK38A2rkS8ZqFY25HIDFscX5X9OoEhJd3quQmXU=
|
||||
golang.org/x/xerrors v0.0.0-20190410155217-1f06c39b4373/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
golang.org/x/xerrors v0.0.0-20190513163551-3ee3066db522/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
|
@ -2,7 +2,7 @@ package notification
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"io"
|
||||
"net/http"
|
||||
"os"
|
||||
"reflect"
|
||||
@ -79,11 +79,14 @@ func (sj *SlackJob) Run(ctx job.Context, params job.Parameters) error {
|
||||
return err
|
||||
}
|
||||
|
||||
sj.logger.Info("start to run slack job")
|
||||
|
||||
err := sj.execute(params)
|
||||
if err != nil {
|
||||
sj.logger.Error(err)
|
||||
sj.logger.Errorf("exit slack job, error: %s", err)
|
||||
} else {
|
||||
sj.logger.Info("success to run slack job")
|
||||
}
|
||||
|
||||
// Wait a second for slack rate limit, refer to https://api.slack.com/docs/rate-limits
|
||||
time.Sleep(time.Second)
|
||||
return err
|
||||
@ -111,17 +114,25 @@ func (sj *SlackJob) execute(params map[string]interface{}) error {
|
||||
|
||||
req, err := http.NewRequest(http.MethodPost, address, bytes.NewReader([]byte(payload)))
|
||||
if err != nil {
|
||||
return err
|
||||
return errors.Wrap(err, "error to generate request")
|
||||
}
|
||||
req.Header.Set("Content-Type", "application/json")
|
||||
|
||||
sj.logger.Infof("send request to remote endpoint, body: %s", payload)
|
||||
|
||||
resp, err := sj.client.Do(req)
|
||||
if err != nil {
|
||||
return err
|
||||
return errors.Wrap(err, "error to send request")
|
||||
}
|
||||
|
||||
defer resp.Body.Close()
|
||||
if resp.StatusCode < 200 || resp.StatusCode >= 300 {
|
||||
return fmt.Errorf("slack job(target: %s) response code is %d", address, resp.StatusCode)
|
||||
body, err := io.ReadAll(resp.Body)
|
||||
if err != nil {
|
||||
sj.logger.Errorf("error to read response body, error: %s", err)
|
||||
}
|
||||
|
||||
return errors.Errorf("abnormal response code: %d, body: %s", resp.StatusCode, string(body))
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
@ -2,13 +2,15 @@ package notification
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"encoding/json"
|
||||
"io"
|
||||
"net/http"
|
||||
"os"
|
||||
"strconv"
|
||||
|
||||
"github.com/goharbor/harbor/src/jobservice/job"
|
||||
"github.com/goharbor/harbor/src/jobservice/logger"
|
||||
"github.com/goharbor/harbor/src/lib/errors"
|
||||
)
|
||||
|
||||
// Max retry has the same meaning as max fails.
|
||||
@ -58,11 +60,14 @@ func (wj *WebhookJob) Run(ctx job.Context, params job.Parameters) error {
|
||||
return err
|
||||
}
|
||||
|
||||
wj.logger.Info("start to run webhook job")
|
||||
|
||||
if err := wj.execute(ctx, params); err != nil {
|
||||
wj.logger.Error(err)
|
||||
wj.logger.Errorf("exit webhook job, error: %s", err)
|
||||
return err
|
||||
}
|
||||
|
||||
wj.logger.Info("success to run webhook job")
|
||||
return nil
|
||||
}
|
||||
|
||||
@ -89,20 +94,32 @@ func (wj *WebhookJob) execute(ctx job.Context, params map[string]interface{}) er
|
||||
|
||||
req, err := http.NewRequest(http.MethodPost, address, bytes.NewReader([]byte(payload)))
|
||||
if err != nil {
|
||||
return err
|
||||
return errors.Wrap(err, "error to generate request")
|
||||
}
|
||||
if v, ok := params["auth_header"]; ok && len(v.(string)) > 0 {
|
||||
req.Header.Set("Authorization", v.(string))
|
||||
|
||||
if h, ok := params["header"].(string); ok {
|
||||
header := make(http.Header)
|
||||
if err = json.Unmarshal([]byte(h), &header); err != nil {
|
||||
return errors.Wrap(err, "error to unmarshal header")
|
||||
}
|
||||
req.Header = header
|
||||
}
|
||||
req.Header.Set("Content-Type", "application/json")
|
||||
|
||||
wj.logger.Infof("send request to remote endpoint, body: %s", payload)
|
||||
|
||||
resp, err := wj.client.Do(req)
|
||||
if err != nil {
|
||||
return err
|
||||
return errors.Wrap(err, "error to send request")
|
||||
}
|
||||
|
||||
defer resp.Body.Close()
|
||||
if resp.StatusCode < 200 || resp.StatusCode >= 300 {
|
||||
return fmt.Errorf("webhook job(target: %s) response code is %d", address, resp.StatusCode)
|
||||
body, err := io.ReadAll(resp.Body)
|
||||
if err != nil {
|
||||
wj.logger.Errorf("error to read response body, error: %s", err)
|
||||
}
|
||||
|
||||
return errors.Errorf("abnormal response code: %d, body: %s", resp.StatusCode, string(body))
|
||||
}
|
||||
|
||||
return nil
|
||||
|
@ -63,7 +63,7 @@ func TestRun(t *testing.T) {
|
||||
"skip_cert_verify": true,
|
||||
"payload": `{"key": "value"}`,
|
||||
"address": ts.URL,
|
||||
"auth_header": "auth_test",
|
||||
"header": `{"Authorization": ["auth_test"]}`,
|
||||
}
|
||||
// test correct webhook response
|
||||
assert.Nil(t, rep.Run(ctx, params))
|
||||
@ -77,7 +77,7 @@ func TestRun(t *testing.T) {
|
||||
"skip_cert_verify": true,
|
||||
"payload": `{"key": "value"}`,
|
||||
"address": tsWrong.URL,
|
||||
"auth_header": "auth_test",
|
||||
"header": `{"Authorization": ["auth_test"]}`,
|
||||
}
|
||||
// test incorrect webhook response
|
||||
assert.NotNil(t, rep.Run(ctx, paramsWrong))
|
||||
|
@ -26,6 +26,7 @@ const (
|
||||
contextKeyArtifactInfo contextKey = "artifactInfo"
|
||||
contextKeyAuthMode contextKey = "authMode"
|
||||
contextKeyCarrySession contextKey = "carrySession"
|
||||
contextKeyRequestID contextKey = "X-Request-ID"
|
||||
)
|
||||
|
||||
// ArtifactInfo wraps the artifact info extracted from the request to "/v2/"
|
||||
@ -112,3 +113,18 @@ func GetCarrySession(ctx context.Context) bool {
|
||||
}
|
||||
return carrySession
|
||||
}
|
||||
|
||||
// WithXRequestID returns a context with XRequestID set
|
||||
func WithXRequestID(ctx context.Context, version string) context.Context {
|
||||
return setToContext(ctx, contextKeyRequestID, version)
|
||||
}
|
||||
|
||||
// GetXRequestID gets the XRequestID from the context
|
||||
func GetXRequestID(ctx context.Context) string {
|
||||
id := ""
|
||||
value := getFromContext(ctx, contextKeyRequestID)
|
||||
if value != nil {
|
||||
id, _ = value.(string)
|
||||
}
|
||||
return id
|
||||
}
|
||||
|
@ -18,6 +18,7 @@ import (
|
||||
"context"
|
||||
"testing"
|
||||
|
||||
"github.com/google/uuid"
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
@ -40,3 +41,24 @@ func TestGetAPIVersion(t *testing.T) {
|
||||
version = GetAPIVersion(ctx)
|
||||
assert.Equal(t, "1.0", version)
|
||||
}
|
||||
|
||||
func TestSetXRequestID(t *testing.T) {
|
||||
ctx := WithXRequestID(context.Background(), uuid.NewString())
|
||||
assert.NotNil(t, ctx)
|
||||
}
|
||||
|
||||
func TestGetXRequestID(t *testing.T) {
|
||||
// nil context
|
||||
id := GetXRequestID(nil)
|
||||
assert.Empty(t, id)
|
||||
|
||||
// no request id set in context
|
||||
id = GetXRequestID(context.Background())
|
||||
assert.Empty(t, id)
|
||||
|
||||
// request id set in context
|
||||
mockID := uuid.NewString()
|
||||
ctx := WithXRequestID(context.Background(), mockID)
|
||||
id = GetXRequestID(ctx)
|
||||
assert.Equal(t, mockID, id)
|
||||
}
|
||||
|
@ -2,8 +2,6 @@ package hook
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
|
||||
"github.com/goharbor/harbor/src/common/job/models"
|
||||
"github.com/goharbor/harbor/src/jobservice/job"
|
||||
@ -34,16 +32,6 @@ func NewHookManager() *DefaultManager {
|
||||
|
||||
// StartHook create a webhook job record in database, and submit it to jobservice
|
||||
func (hm *DefaultManager) StartHook(ctx context.Context, event *model.HookEvent, data *models.JobData) error {
|
||||
payload, err := json.Marshal(event.Payload)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
extraAttrs := make(map[string]interface{})
|
||||
if err = json.Unmarshal(payload, &extraAttrs); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
var vendorType string
|
||||
switch event.Target.Type {
|
||||
case model.NotifyTypeHTTP:
|
||||
@ -56,11 +44,14 @@ func (hm *DefaultManager) StartHook(ctx context.Context, event *model.HookEvent,
|
||||
return errors.Errorf("invalid event target type: %s", event.Target.Type)
|
||||
}
|
||||
|
||||
extraAttrs := map[string]interface{}{
|
||||
"event_type": event.EventType,
|
||||
"payload": data.Parameters["payload"],
|
||||
}
|
||||
// create execution firstly, then create task.
|
||||
execID, err := hm.execMgr.Create(ctx, vendorType, event.PolicyID, task.ExecutionTriggerEvent, extraAttrs)
|
||||
if err != nil {
|
||||
log.Errorf("failed to create execution for webhook based on policy %d: %v", event.PolicyID, err)
|
||||
return nil
|
||||
return errors.Errorf("failed to create execution for webhook based on policy %d: %v", event.PolicyID, err)
|
||||
}
|
||||
|
||||
taskID, err := hm.taskMgr.Create(ctx, execID, &task.Job{
|
||||
@ -69,12 +60,12 @@ func (hm *DefaultManager) StartHook(ctx context.Context, event *model.HookEvent,
|
||||
JobKind: data.Metadata.JobKind,
|
||||
},
|
||||
Parameters: map[string]interface{}(data.Parameters),
|
||||
}, extraAttrs)
|
||||
})
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to create the task for webhook based on policy %d: %v", event.PolicyID, err)
|
||||
return errors.Errorf("failed to create task for webhook based on policy %d: %v", event.PolicyID, err)
|
||||
}
|
||||
|
||||
log.Debugf("created a webhook job %d for the policy %d", taskID, event.PolicyID)
|
||||
log.Debugf("created webhook task %d for the policy %d", taskID, event.PolicyID)
|
||||
|
||||
return nil
|
||||
}
|
||||
|
@ -9,6 +9,7 @@ import (
|
||||
"github.com/goharbor/harbor/src/pkg/notification/hook"
|
||||
"github.com/goharbor/harbor/src/pkg/notification/policy"
|
||||
n_event "github.com/goharbor/harbor/src/pkg/notifier/event"
|
||||
"github.com/goharbor/harbor/src/pkg/notifier/formats"
|
||||
notifier_model "github.com/goharbor/harbor/src/pkg/notifier/model"
|
||||
)
|
||||
|
||||
@ -17,6 +18,8 @@ type (
|
||||
EventType string
|
||||
// NotifyType is the type of notify
|
||||
NotifyType string
|
||||
// PayloadFormatType is the type of payload format
|
||||
PayloadFormatType string
|
||||
)
|
||||
|
||||
func (e EventType) String() string {
|
||||
@ -27,6 +30,10 @@ func (n NotifyType) String() string {
|
||||
return string(n)
|
||||
}
|
||||
|
||||
func (p PayloadFormatType) String() string {
|
||||
return string(p)
|
||||
}
|
||||
|
||||
var (
|
||||
// PolicyMgr is a global notification policy manager
|
||||
PolicyMgr policy.Manager
|
||||
@ -39,6 +46,9 @@ var (
|
||||
|
||||
// supportedNotifyTypes is a slice to store notification type, eg. HTTP, Email etc
|
||||
supportedNotifyTypes []NotifyType
|
||||
|
||||
// supportedPayloadFormatTypes is a slice to store the supported payload formats. eg. Default, CloudEvents etc
|
||||
supportedPayloadFormatTypes []PayloadFormatType
|
||||
)
|
||||
|
||||
// Init ...
|
||||
@ -77,6 +87,11 @@ func initSupportedNotifyType() {
|
||||
for _, notifyType := range notifyTypes {
|
||||
supportedNotifyTypes = append(supportedNotifyTypes, NotifyType(notifyType))
|
||||
}
|
||||
|
||||
payloadFormats := []string{formats.DefaultFormat, formats.CloudEventsFormat}
|
||||
for _, payloadFormat := range payloadFormats {
|
||||
supportedPayloadFormatTypes = append(supportedPayloadFormatTypes, PayloadFormatType(payloadFormat))
|
||||
}
|
||||
}
|
||||
|
||||
type eventKey struct{}
|
||||
@ -127,3 +142,7 @@ func GetSupportedEventTypes() []EventType {
|
||||
func GetSupportedNotifyTypes() []NotifyType {
|
||||
return supportedNotifyTypes
|
||||
}
|
||||
|
||||
func GetSupportedPayloadFormats() []PayloadFormatType {
|
||||
return supportedPayloadFormatTypes
|
||||
}
|
||||
|
@ -81,4 +81,5 @@ type EventTarget struct {
|
||||
Address string `json:"address"`
|
||||
AuthHeader string `json:"auth_header,omitempty"`
|
||||
SkipCertVerify bool `json:"skip_cert_verify"`
|
||||
PayloadFormat string `json:"payload_format,omitempty"`
|
||||
}
|
||||
|
@ -1,6 +1,8 @@
|
||||
package event
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"github.com/goharbor/harbor/src/lib/errors"
|
||||
"github.com/goharbor/harbor/src/lib/log"
|
||||
policy_model "github.com/goharbor/harbor/src/pkg/notification/policy/model"
|
||||
@ -38,6 +40,7 @@ type Metadata interface {
|
||||
|
||||
// HookMetaData defines hook notification related event data
|
||||
type HookMetaData struct {
|
||||
ProjectID int64
|
||||
PolicyID int64
|
||||
EventType string
|
||||
Target *policy_model.EventTarget
|
||||
@ -47,6 +50,7 @@ type HookMetaData struct {
|
||||
// Resolve hook metadata into hook event
|
||||
func (h *HookMetaData) Resolve(evt *Event) error {
|
||||
data := &model.HookEvent{
|
||||
ProjectID: h.ProjectID,
|
||||
PolicyID: h.PolicyID,
|
||||
EventType: h.EventType,
|
||||
Target: h.Target,
|
||||
@ -59,7 +63,7 @@ func (h *HookMetaData) Resolve(evt *Event) error {
|
||||
}
|
||||
|
||||
// Build an event by metadata
|
||||
func (e *Event) Build(metadata ...Metadata) error {
|
||||
func (e *Event) Build(ctx context.Context, metadata ...Metadata) error {
|
||||
for _, md := range metadata {
|
||||
if err := md.Resolve(e); err != nil {
|
||||
log.Debugf("failed to resolve event metadata: %v", md)
|
||||
@ -70,8 +74,8 @@ func (e *Event) Build(metadata ...Metadata) error {
|
||||
}
|
||||
|
||||
// Publish an event
|
||||
func (e *Event) Publish() error {
|
||||
if err := notifier.Publish(e.Topic, e.Data); err != nil {
|
||||
func (e *Event) Publish(ctx context.Context) error {
|
||||
if err := notifier.Publish(ctx, e.Topic, e.Data); err != nil {
|
||||
log.Debugf("failed to publish topic %s with event: %v", e.Topic, e.Data)
|
||||
return errors.Wrap(err, "failed to publish event")
|
||||
}
|
||||
@ -80,14 +84,14 @@ func (e *Event) Publish() error {
|
||||
|
||||
// BuildAndPublish builds the event according to the metadata and publish the event
|
||||
// The process is done in a separated goroutine
|
||||
func BuildAndPublish(metadata ...Metadata) {
|
||||
func BuildAndPublish(ctx context.Context, metadata ...Metadata) {
|
||||
go func() {
|
||||
event := &Event{}
|
||||
if err := event.Build(metadata...); err != nil {
|
||||
if err := event.Build(ctx, metadata...); err != nil {
|
||||
log.Errorf("failed to build the event from metadata: %v", err)
|
||||
return
|
||||
}
|
||||
if err := event.Publish(); err != nil {
|
||||
if err := event.Publish(ctx); err != nil {
|
||||
log.Errorf("failed to publish the event %s: %v", event.Topic, err)
|
||||
return
|
||||
}
|
||||
|
@ -1,6 +1,7 @@
|
||||
package event
|
||||
|
||||
import (
|
||||
"context"
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
@ -43,7 +44,7 @@ func TestHookEvent_Build(t *testing.T) {
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
event := &Event{}
|
||||
err := event.Build(tt.args.hookMetadata)
|
||||
err := event.Build(context.TODO(), tt.args.hookMetadata)
|
||||
if tt.wantErr {
|
||||
require.NotNil(t, err, "Error: %s", err)
|
||||
return
|
||||
@ -76,7 +77,7 @@ func TestEvent_Publish(t *testing.T) {
|
||||
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
err := tt.args.event.Publish()
|
||||
err := tt.args.event.Publish(context.TODO())
|
||||
if tt.wantErr {
|
||||
require.NotNil(t, err, "Error: %s", err)
|
||||
return
|
||||
|
147
src/pkg/notifier/formats/cloudevents.go
Normal file
147
src/pkg/notifier/formats/cloudevents.go
Normal file
@ -0,0 +1,147 @@
|
||||
// Copyright Project Harbor Authors
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package formats
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"net/http"
|
||||
"time"
|
||||
|
||||
cloudevents "github.com/cloudevents/sdk-go/v2"
|
||||
"github.com/google/uuid"
|
||||
|
||||
"github.com/goharbor/harbor/src/controller/event"
|
||||
"github.com/goharbor/harbor/src/lib"
|
||||
"github.com/goharbor/harbor/src/lib/errors"
|
||||
"github.com/goharbor/harbor/src/lib/log"
|
||||
"github.com/goharbor/harbor/src/pkg/notifier/model"
|
||||
)
|
||||
|
||||
var (
|
||||
// cloudEventsFormatter is the global single formatter for CloudEvents.
|
||||
cloudEventsFormatter Formatter = &CloudEvents{}
|
||||
)
|
||||
|
||||
func init() {
|
||||
registerFormats(CloudEventsFormat, cloudEventsFormatter)
|
||||
}
|
||||
|
||||
const (
|
||||
// CloudEventsFormat is the type for CloudEvents format.
|
||||
CloudEventsFormat = "CloudEvents"
|
||||
|
||||
// extOperator is the key for the operator in the CloudEvents.
|
||||
extOperator = "operator"
|
||||
)
|
||||
|
||||
var (
|
||||
// eventTypeMapping defines the mapping of harbor event type and CloudEvents type.
|
||||
eventTypeMapping = map[string]string{
|
||||
event.TopicDeleteArtifact: eventType("artifact.deleted"),
|
||||
event.TopicPullArtifact: eventType("artifact.pulled"),
|
||||
event.TopicPushArtifact: eventType("artifact.pushed"),
|
||||
event.TopicQuotaExceed: eventType("quota.exceeded"),
|
||||
event.TopicQuotaWarning: eventType("quota.warned"),
|
||||
event.TopicReplication: eventType("replication.status.changed"),
|
||||
event.TopicScanningFailed: eventType("scan.failed"),
|
||||
event.TopicScanningCompleted: eventType("scan.completed"),
|
||||
event.TopicScanningStopped: eventType("scan.stopped"),
|
||||
event.TopicTagRetention: eventType("tag_retention.finished"),
|
||||
}
|
||||
)
|
||||
|
||||
// eventType returns the constructed event type.
|
||||
func eventType(t string) string {
|
||||
// defines the prefix for event type, organization name or FQDN or more extended possibility,
|
||||
// use harbor by default.
|
||||
prefix := "harbor"
|
||||
return fmt.Sprintf("%s.%s", prefix, t)
|
||||
}
|
||||
|
||||
// CloudEvents is the instance for the CloudEvents format.
|
||||
type CloudEvents struct{}
|
||||
|
||||
// Format implements the interface Formatter.
|
||||
/*
|
||||
{
|
||||
"specversion":"1.0",
|
||||
"id":"4b2f89a6-548d-4c12-9993-a1f5790b97d2",
|
||||
"source":"/projects/1/webhook/policies/3",
|
||||
"type":"harbor.artifact.pulled",
|
||||
"datacontenttype":"application/json",
|
||||
"time":"2023-03-06T06:08:43Z",
|
||||
"data":{
|
||||
"resources":[
|
||||
{
|
||||
"digest":"sha256:dde8e930c7b6a490f728e66292bc9bce42efc9bbb5278bae40e4f30f6e00fe8c",
|
||||
"tag":"sha256:dde8e930c7b6a490f728e66292bc9bce42efc9bbb5278bae40e4f30f6e00fe8c",
|
||||
"resource_url":"harbor.dev/library/busybox@sha256:dde8e930c7b6a490f728e66292bc9bce42efc9bbb5278bae40e4f30f6e00fe8c"
|
||||
}
|
||||
],
|
||||
"repository":{
|
||||
"date_created":1677053165,
|
||||
"name":"busybox",
|
||||
"namespace":"library",
|
||||
"repo_full_name":"library/busybox",
|
||||
"repo_type":"public"
|
||||
}
|
||||
},
|
||||
"operator":"robot$library+scanner-Trivy-51fe4548-bbe5-11ed-9217-0242ac14000d"
|
||||
}
|
||||
*/
|
||||
func (ce *CloudEvents) Format(ctx context.Context, he *model.HookEvent) (http.Header, []byte, error) {
|
||||
if he == nil {
|
||||
return nil, nil, errors.Errorf("HookEvent should not be nil")
|
||||
}
|
||||
|
||||
eventType, ok := eventTypeMapping[he.EventType]
|
||||
if !ok {
|
||||
return nil, nil, errors.Errorf("unknown event type: %s", he.EventType)
|
||||
}
|
||||
|
||||
event := cloudevents.NewEvent()
|
||||
// retrieve request id from context as id, set to uuid if not found
|
||||
id := lib.GetXRequestID(ctx)
|
||||
if len(id) == 0 {
|
||||
id = uuid.NewString()
|
||||
log.Warningf("cannot extract request id from context, use UUID %s instead", id)
|
||||
}
|
||||
event.SetID(id)
|
||||
event.SetSource(source(he.ProjectID, he.PolicyID))
|
||||
event.SetType(eventType)
|
||||
event.SetTime(time.Unix(he.Payload.OccurAt, 0))
|
||||
event.SetExtension(extOperator, he.Payload.Operator)
|
||||
|
||||
if err := event.SetData(cloudevents.ApplicationJSON, he.Payload.EventData); err != nil {
|
||||
return nil, nil, errors.Wrap(err, "error to set data in CloudEvents")
|
||||
}
|
||||
|
||||
data, err := json.Marshal(event)
|
||||
if err != nil {
|
||||
return nil, nil, errors.Wrap(err, "error to marshal CloudEvents")
|
||||
}
|
||||
|
||||
header := http.Header{
|
||||
"Content-Type": []string{cloudevents.ApplicationCloudEventsJSON},
|
||||
}
|
||||
return header, data, nil
|
||||
}
|
||||
|
||||
// source builds the source for CloudEvents.
|
||||
func source(projectID, policyID int64) string {
|
||||
return fmt.Sprintf("/projects/%d/webhook/policies/%d", projectID, policyID)
|
||||
}
|
86
src/pkg/notifier/formats/cloudevents_test.go
Normal file
86
src/pkg/notifier/formats/cloudevents_test.go
Normal file
@ -0,0 +1,86 @@
|
||||
// Copyright Project Harbor Authors
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package formats
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"net/http"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
cloudevents "github.com/cloudevents/sdk-go/v2"
|
||||
|
||||
"github.com/goharbor/harbor/src/lib"
|
||||
"github.com/goharbor/harbor/src/pkg/notifier/model"
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
func TestCloudEvents_Format(t *testing.T) {
|
||||
ce := &CloudEvents{}
|
||||
// invalid case
|
||||
{
|
||||
header, data, err := ce.Format(nil, nil)
|
||||
assert.Error(t, err)
|
||||
assert.Nil(t, header)
|
||||
assert.Nil(t, data)
|
||||
}
|
||||
// normal case
|
||||
{
|
||||
he := &model.HookEvent{
|
||||
ProjectID: 1,
|
||||
PolicyID: 3,
|
||||
EventType: "PULL_ARTIFACT",
|
||||
Payload: &model.Payload{
|
||||
Type: "PULL_ARTIFACT",
|
||||
OccurAt: 1678082923,
|
||||
Operator: "admin",
|
||||
EventData: &model.EventData{
|
||||
Resources: []*model.Resource{
|
||||
{Digest: "sha256:dde8e930c7b6a490f728e66292bc9bce42efc9bbb5278bae40e4f30f6e00fe8c",
|
||||
Tag: "sha256:dde8e930c7b6a490f728e66292bc9bce42efc9bbb5278bae40e4f30f6e00fe8c",
|
||||
ResourceURL: "harbor.dev/library/busybox@sha256:dde8e930c7b6a490f728e66292bc9bce42efc9bbb5278bae40e4f30f6e00fe8c",
|
||||
},
|
||||
},
|
||||
Repository: &model.Repository{
|
||||
DateCreated: 1677053165,
|
||||
Name: "busybox",
|
||||
Namespace: "library",
|
||||
RepoFullName: "library/busybox",
|
||||
RepoType: "public",
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
ctx := context.TODO()
|
||||
requestID := "mock-request-id"
|
||||
header, data, err := ce.Format(lib.WithXRequestID(ctx, requestID), he)
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, http.Header{"Content-Type": []string{"application/cloudevents+json"}}, header)
|
||||
// validate data format
|
||||
event := cloudevents.NewEvent()
|
||||
err = json.Unmarshal(data, &event)
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, "1.0", event.SpecVersion())
|
||||
assert.Equal(t, requestID, event.ID())
|
||||
assert.Equal(t, "/projects/1/webhook/policies/3", event.Source())
|
||||
assert.Equal(t, "harbor.artifact.pulled", event.Type())
|
||||
assert.Equal(t, "application/json", event.DataContentType())
|
||||
assert.Equal(t, "2023-03-06T06:08:43Z", event.Time().Format(time.RFC3339))
|
||||
assert.Equal(t, "admin", event.Extensions()["operator"])
|
||||
assert.Equal(t, `{"resources":[{"digest":"sha256:dde8e930c7b6a490f728e66292bc9bce42efc9bbb5278bae40e4f30f6e00fe8c","tag":"sha256:dde8e930c7b6a490f728e66292bc9bce42efc9bbb5278bae40e4f30f6e00fe8c","resource_url":"harbor.dev/library/busybox@sha256:dde8e930c7b6a490f728e66292bc9bce42efc9bbb5278bae40e4f30f6e00fe8c"}],"repository":{"date_created":1677053165,"name":"busybox","namespace":"library","repo_full_name":"library/busybox","repo_type":"public"}}`, string(event.Data()))
|
||||
}
|
||||
}
|
83
src/pkg/notifier/formats/default.go
Normal file
83
src/pkg/notifier/formats/default.go
Normal file
@ -0,0 +1,83 @@
|
||||
// Copyright Project Harbor Authors
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package formats
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"net/http"
|
||||
|
||||
"github.com/goharbor/harbor/src/lib/errors"
|
||||
"github.com/goharbor/harbor/src/pkg/notifier/model"
|
||||
)
|
||||
|
||||
var (
|
||||
// defaultFormatter is the global single formatter for Default.
|
||||
defaultFormatter Formatter = &Default{}
|
||||
)
|
||||
|
||||
func init() {
|
||||
// for forward compatibility, empty is also the default.
|
||||
registerFormats("", defaultFormatter)
|
||||
registerFormats(DefaultFormat, defaultFormatter)
|
||||
}
|
||||
|
||||
const (
|
||||
// DefaultFormat is the type for default format.
|
||||
DefaultFormat = "Default"
|
||||
)
|
||||
|
||||
// Default is the instance for default format(original format in previous versions).
|
||||
type Default struct{}
|
||||
|
||||
// Format implements the interface Formatter.
|
||||
/*
|
||||
{
|
||||
"type":"PULL_ARTIFACT",
|
||||
"occur_at":1678082303,
|
||||
"operator":"admin",
|
||||
"event_data":{
|
||||
"resources":[
|
||||
{
|
||||
"digest":"sha256:dde8e930c7b6a490f728e66292bc9bce42efc9bbb5278bae40e4f30f6e00fe8c",
|
||||
"tag":"sha256:dde8e930c7b6a490f728e66292bc9bce42efc9bbb5278bae40e4f30f6e00fe8c",
|
||||
"resource_url":"harbor.dev/library/busybox@sha256:dde8e930c7b6a490f728e66292bc9bce42efc9bbb5278bae40e4f30f6e00fe8c"
|
||||
}
|
||||
],
|
||||
"repository":{
|
||||
"date_created":1677053165,
|
||||
"name":"busybox",
|
||||
"namespace":"library",
|
||||
"repo_full_name":"library/busybox",
|
||||
"repo_type":"public"
|
||||
}
|
||||
}
|
||||
}
|
||||
*/
|
||||
func (d *Default) Format(ctx context.Context, he *model.HookEvent) (http.Header, []byte, error) {
|
||||
if he == nil {
|
||||
return nil, nil, errors.Errorf("HookEvent should not be nil")
|
||||
}
|
||||
|
||||
payload, err := json.Marshal(he.Payload)
|
||||
if err != nil {
|
||||
return nil, nil, errors.Wrap(err, "error to marshal payload")
|
||||
}
|
||||
|
||||
header := http.Header{
|
||||
"Content-Type": []string{"application/json"},
|
||||
}
|
||||
return header, payload, nil
|
||||
}
|
92
src/pkg/notifier/formats/default_test.go
Normal file
92
src/pkg/notifier/formats/default_test.go
Normal file
@ -0,0 +1,92 @@
|
||||
// Copyright Project Harbor Authors
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package formats
|
||||
|
||||
import (
|
||||
"context"
|
||||
"net/http"
|
||||
"reflect"
|
||||
"testing"
|
||||
|
||||
"github.com/goharbor/harbor/src/pkg/notifier/model"
|
||||
)
|
||||
|
||||
func TestDefault_Format(t *testing.T) {
|
||||
type args struct {
|
||||
he *model.HookEvent
|
||||
}
|
||||
tests := []struct {
|
||||
name string
|
||||
d *Default
|
||||
args args
|
||||
want http.Header
|
||||
want1 []byte
|
||||
wantErr bool
|
||||
}{
|
||||
{
|
||||
name: "invalid case",
|
||||
d: &Default{},
|
||||
args: args{he: nil},
|
||||
want: nil,
|
||||
want1: nil,
|
||||
wantErr: true,
|
||||
},
|
||||
{
|
||||
name: "normal case",
|
||||
d: &Default{},
|
||||
args: args{he: &model.HookEvent{
|
||||
Payload: &model.Payload{
|
||||
Type: "PULL_ARTIFACT",
|
||||
OccurAt: 1678082303,
|
||||
Operator: "admin",
|
||||
EventData: &model.EventData{
|
||||
Resources: []*model.Resource{
|
||||
{Digest: "sha256:dde8e930c7b6a490f728e66292bc9bce42efc9bbb5278bae40e4f30f6e00fe8c",
|
||||
Tag: "sha256:dde8e930c7b6a490f728e66292bc9bce42efc9bbb5278bae40e4f30f6e00fe8c",
|
||||
ResourceURL: "harbor.dev/library/busybox@sha256:dde8e930c7b6a490f728e66292bc9bce42efc9bbb5278bae40e4f30f6e00fe8c",
|
||||
},
|
||||
},
|
||||
Repository: &model.Repository{
|
||||
DateCreated: 1677053165,
|
||||
Name: "busybox",
|
||||
Namespace: "library",
|
||||
RepoFullName: "library/busybox",
|
||||
RepoType: "public",
|
||||
},
|
||||
},
|
||||
},
|
||||
}},
|
||||
want: http.Header{"Content-Type": []string{"application/json"}},
|
||||
want1: []byte(`{"type":"PULL_ARTIFACT","occur_at":1678082303,"operator":"admin","event_data":{"resources":[{"digest":"sha256:dde8e930c7b6a490f728e66292bc9bce42efc9bbb5278bae40e4f30f6e00fe8c","tag":"sha256:dde8e930c7b6a490f728e66292bc9bce42efc9bbb5278bae40e4f30f6e00fe8c","resource_url":"harbor.dev/library/busybox@sha256:dde8e930c7b6a490f728e66292bc9bce42efc9bbb5278bae40e4f30f6e00fe8c"}],"repository":{"date_created":1677053165,"name":"busybox","namespace":"library","repo_full_name":"library/busybox","repo_type":"public"}}}`),
|
||||
wantErr: false,
|
||||
},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
d := &Default{}
|
||||
got, got1, err := d.Format(context.TODO(), tt.args.he)
|
||||
if (err != nil) != tt.wantErr {
|
||||
t.Errorf("Default.Format() error = %v, wantErr %v", err, tt.wantErr)
|
||||
return
|
||||
}
|
||||
if !reflect.DeepEqual(got, tt.want) {
|
||||
t.Errorf("Default.Format() got = %v, want %v", got, tt.want)
|
||||
}
|
||||
if !reflect.DeepEqual(got1, tt.want1) {
|
||||
t.Errorf("Default.Format() got1 = %v, want %v", got1, tt.want1)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
51
src/pkg/notifier/formats/formatter.go
Normal file
51
src/pkg/notifier/formats/formatter.go
Normal file
@ -0,0 +1,51 @@
|
||||
// Copyright Project Harbor Authors
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package formats
|
||||
|
||||
import (
|
||||
"context"
|
||||
"net/http"
|
||||
|
||||
"github.com/goharbor/harbor/src/lib/errors"
|
||||
"github.com/goharbor/harbor/src/pkg/notifier/model"
|
||||
)
|
||||
|
||||
// formatsRegistry is the service registry for formats.
|
||||
var formatsRegistry map[string]Formatter
|
||||
|
||||
// registerFormats registers the format to formatsRegistry.
|
||||
func registerFormats(formatType string, formatter Formatter) {
|
||||
if formatsRegistry == nil {
|
||||
formatsRegistry = make(map[string]Formatter)
|
||||
}
|
||||
|
||||
formatsRegistry[formatType] = formatter
|
||||
}
|
||||
|
||||
// Formatter is the interface for event which for implementing different drivers to
|
||||
// organize their customize data format.
|
||||
type Formatter interface {
|
||||
// Format formats the data to expected format and return request headers and encoded payload
|
||||
Format(context.Context, *model.HookEvent) (http.Header, []byte, error)
|
||||
}
|
||||
|
||||
// GetFormatter returns corresponding formatter from format type.
|
||||
func GetFormatter(formatType string) (Formatter, error) {
|
||||
if formatter, ok := formatsRegistry[formatType]; ok {
|
||||
return formatter, nil
|
||||
}
|
||||
|
||||
return nil, errors.Errorf("unknown format type: %s", formatType)
|
||||
}
|
@ -3,12 +3,12 @@ package notification
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
|
||||
"github.com/goharbor/harbor/src/common/job/models"
|
||||
"github.com/goharbor/harbor/src/jobservice/job"
|
||||
"github.com/goharbor/harbor/src/lib/errors"
|
||||
"github.com/goharbor/harbor/src/pkg/notification"
|
||||
"github.com/goharbor/harbor/src/pkg/notifier/formats"
|
||||
"github.com/goharbor/harbor/src/pkg/notifier/model"
|
||||
)
|
||||
|
||||
@ -47,17 +47,33 @@ func (h *HTTPHandler) process(ctx context.Context, event *model.HookEvent) error
|
||||
}
|
||||
j.Name = job.WebhookJobVendorType
|
||||
|
||||
payload, err := json.Marshal(event.Payload)
|
||||
if event == nil || event.Payload == nil || event.Target == nil {
|
||||
return errors.Errorf("invalid event: %+v", event)
|
||||
}
|
||||
|
||||
formatter, err := formats.GetFormatter(event.Target.PayloadFormat)
|
||||
if err != nil {
|
||||
return fmt.Errorf("marshal from payload %v failed: %v", event.Payload, err)
|
||||
return errors.Wrap(err, "error to get formatter")
|
||||
}
|
||||
|
||||
header, payload, err := formatter.Format(ctx, event)
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "error to format event")
|
||||
}
|
||||
|
||||
if len(event.Target.AuthHeader) > 0 {
|
||||
header.Set("Authorization", event.Target.AuthHeader)
|
||||
}
|
||||
|
||||
headerBytes, err := json.Marshal(header)
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "error to marshal header")
|
||||
}
|
||||
|
||||
j.Parameters = map[string]interface{}{
|
||||
"payload": string(payload),
|
||||
"address": event.Target.Address,
|
||||
// Users can define a auth header in http statement in notification(webhook) policy.
|
||||
// So it will be sent in header in http request.
|
||||
"auth_header": event.Target.AuthHeader,
|
||||
"payload": string(payload),
|
||||
"address": event.Target.Address,
|
||||
"header": string(headerBytes),
|
||||
"skip_cert_verify": event.Target.SkipCertVerify,
|
||||
}
|
||||
return notification.HookManager.StartHook(ctx, event, j)
|
||||
|
@ -8,7 +8,6 @@ import (
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
|
||||
"github.com/goharbor/harbor/src/common/dao"
|
||||
"github.com/goharbor/harbor/src/pkg/notification"
|
||||
policy_model "github.com/goharbor/harbor/src/pkg/notification/policy/model"
|
||||
"github.com/goharbor/harbor/src/pkg/notifier/event"
|
||||
@ -16,7 +15,6 @@ import (
|
||||
)
|
||||
|
||||
func TestSlackHandler_Handle(t *testing.T) {
|
||||
dao.PrepareTestForPostgresSQL()
|
||||
hookMgr := notification.HookManager
|
||||
defer func() {
|
||||
notification.HookManager = hookMgr
|
||||
|
@ -7,6 +7,7 @@ import (
|
||||
|
||||
// HookEvent is hook related event data to publish
|
||||
type HookEvent struct {
|
||||
ProjectID int64
|
||||
PolicyID int64
|
||||
EventType string
|
||||
Target *policy_model.EventTarget
|
||||
|
@ -1,6 +1,7 @@
|
||||
package notifier
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"fmt"
|
||||
"reflect"
|
||||
@ -160,7 +161,7 @@ func (nw *NotificationWatcher) UnHandle(topic string, handler string) error {
|
||||
}
|
||||
|
||||
// Notify that notification is coming.
|
||||
func (nw *NotificationWatcher) Notify(notification Notification) error {
|
||||
func (nw *NotificationWatcher) Notify(ctx context.Context, notification Notification) error {
|
||||
if strings.TrimSpace(notification.Topic) == "" {
|
||||
return errors.New("empty topic can not be notified")
|
||||
}
|
||||
@ -198,7 +199,7 @@ func (nw *NotificationWatcher) Notify(notification Notification) error {
|
||||
<-ch
|
||||
}
|
||||
}()
|
||||
if err := hd.Handle(orm.Context(), notification.Value); err != nil {
|
||||
if err := hd.Handle(orm.Copy(ctx), notification.Value); err != nil {
|
||||
// Currently, we just log the error
|
||||
log.Errorf("Error occurred when triggering handler %s of topic %s: %s\n", reflect.TypeOf(hd).String(), notification.Topic, err.Error())
|
||||
} else {
|
||||
@ -222,8 +223,8 @@ func UnSubscribe(topic string, handler string) error {
|
||||
}
|
||||
|
||||
// Publish is a wrapper utility method for NotificationWatcher.notify()
|
||||
func Publish(topic string, value interface{}) error {
|
||||
return notificationWatcher.Notify(Notification{
|
||||
func Publish(ctx context.Context, topic string, value interface{}) error {
|
||||
return notificationWatcher.Notify(ctx, Notification{
|
||||
Topic: topic,
|
||||
Value: value,
|
||||
})
|
||||
|
@ -135,8 +135,8 @@ func TestPublish(t *testing.T) {
|
||||
t.Fail()
|
||||
}
|
||||
|
||||
Publish("topic1", 100)
|
||||
Publish("topic2", 50)
|
||||
Publish(context.TODO(), "topic1", 100)
|
||||
Publish(context.TODO(), "topic2", 50)
|
||||
|
||||
// Waiting for async is done
|
||||
<-time.After(1 * time.Second)
|
||||
@ -174,7 +174,7 @@ func TestConcurrentPublish(t *testing.T) {
|
||||
|
||||
// Publish in a short interval.
|
||||
for i := 0; i < 10; i++ {
|
||||
Publish("topic1", 100)
|
||||
Publish(context.TODO(), "topic1", 100)
|
||||
}
|
||||
|
||||
// Waiting for async is done
|
||||
|
@ -58,8 +58,8 @@ func retentionTaskCheckInProcessor(ctx context.Context, t *task.Task, sc *job.St
|
||||
TaskID: taskID,
|
||||
}
|
||||
|
||||
if err := e.Build(metaData); err == nil {
|
||||
if err := e.Publish(); err != nil {
|
||||
if err := e.Build(ctx, metaData); err == nil {
|
||||
if err := e.Publish(ctx); err != nil {
|
||||
log.G(ctx).WithField("error", err).Errorf("tag retention job hook handler: event publish")
|
||||
}
|
||||
} else {
|
||||
|
@ -31,7 +31,7 @@ func Middleware(skippers ...middleware.Skipper) func(http.Handler) http.Handler
|
||||
next.ServeHTTP(res, r.WithContext(notification.NewContext(r.Context(), evc)))
|
||||
if res.Success() || evc.MustNotify {
|
||||
for e := evc.Events.Front(); e != nil; e = e.Next() {
|
||||
event.BuildAndPublish(e.Value.(event.Metadata))
|
||||
event.BuildAndPublish(r.Context(), e.Value.(event.Metadata))
|
||||
}
|
||||
}
|
||||
}, skippers...)
|
||||
|
@ -21,6 +21,7 @@ import (
|
||||
"go.opentelemetry.io/otel/attribute"
|
||||
oteltrace "go.opentelemetry.io/otel/trace"
|
||||
|
||||
"github.com/goharbor/harbor/src/lib"
|
||||
tracelib "github.com/goharbor/harbor/src/lib/trace"
|
||||
"github.com/goharbor/harbor/src/server/middleware"
|
||||
)
|
||||
@ -40,6 +41,8 @@ func Middleware(skippers ...middleware.Skipper) func(http.Handler) http.Handler
|
||||
if tracelib.Enabled() {
|
||||
oteltrace.SpanFromContext(r.Context()).SetAttributes(attribute.Key(HeaderXRequestID).String(rid))
|
||||
}
|
||||
next.ServeHTTP(w, r)
|
||||
// also set the request id to context
|
||||
ctx := lib.WithXRequestID(r.Context(), rid)
|
||||
next.ServeHTTP(w, r.WithContext(ctx))
|
||||
}, skippers...)
|
||||
}
|
||||
|
@ -1,12 +1,9 @@
|
||||
package model
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
|
||||
"github.com/go-openapi/strfmt"
|
||||
|
||||
"github.com/goharbor/harbor/src/jobservice/job"
|
||||
"github.com/goharbor/harbor/src/lib/log"
|
||||
"github.com/goharbor/harbor/src/pkg/task"
|
||||
"github.com/goharbor/harbor/src/server/v2.0/models"
|
||||
)
|
||||
@ -36,14 +33,12 @@ func (n *WebhookJob) ToSwagger() *models.WebhookJob {
|
||||
webhookJob.NotifyType = notifyType
|
||||
|
||||
if n.ExtraAttrs != nil {
|
||||
if eventType, ok := n.ExtraAttrs["type"].(string); ok {
|
||||
if eventType, ok := n.ExtraAttrs["event_type"].(string); ok {
|
||||
webhookJob.EventType = eventType
|
||||
}
|
||||
detail, err := json.Marshal(n.ExtraAttrs)
|
||||
if err == nil {
|
||||
webhookJob.JobDetail = string(detail)
|
||||
} else {
|
||||
log.Errorf("failed to marshal exec.ExtraAttrs, error: %v", err)
|
||||
|
||||
if payload, ok := n.ExtraAttrs["payload"].(string); ok {
|
||||
webhookJob.JobDetail = payload
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -37,6 +37,7 @@ func (n *WebhookPolicy) ToTargets() []*models.WebhookTargetObject {
|
||||
Address: t.Address,
|
||||
AuthHeader: t.AuthHeader,
|
||||
SkipCertVerify: t.SkipCertVerify,
|
||||
PayloadFormat: models.PayloadFormatType(t.PayloadFormat),
|
||||
})
|
||||
}
|
||||
return results
|
||||
|
@ -392,6 +392,12 @@ func (n *webhookAPI) GetSupportedEventTypes(ctx context.Context, params webhook.
|
||||
for _, eventType := range notification.GetSupportedEventTypes() {
|
||||
notificationTypes.EventType = append(notificationTypes.EventType, models.EventType(eventType))
|
||||
}
|
||||
// currently only http type support payload format
|
||||
httpPayloadFormats := &models.PayloadFormat{NotifyType: models.NotifyType("http")}
|
||||
for _, formatType := range notification.GetSupportedPayloadFormats() {
|
||||
httpPayloadFormats.Formats = append(httpPayloadFormats.Formats, models.PayloadFormatType(formatType))
|
||||
}
|
||||
notificationTypes.PayloadFormats = []*models.PayloadFormat{httpPayloadFormats}
|
||||
|
||||
return webhook.NewGetSupportedEventTypesOK().WithPayload(notificationTypes)
|
||||
}
|
||||
@ -411,6 +417,15 @@ func (n *webhookAPI) validateTargets(policy *policy_model.Policy) (bool, error)
|
||||
if !isNotifyTypeSupported(target.Type) {
|
||||
return false, errors.New(nil).WithMessage("unsupported target type %s with policy %s", target.Type, policy.Name).WithCode(errors.BadRequestCode)
|
||||
}
|
||||
// don't allow set the payload format for slack type
|
||||
// slack should be migrated as a kind of payload in the future
|
||||
if len(target.PayloadFormat) > 0 && target.Type == "slack" {
|
||||
return false, errors.New(nil).WithMessage("set payload format is not allowed for slack").WithCode(errors.BadRequestCode)
|
||||
}
|
||||
|
||||
if len(target.PayloadFormat) > 0 && !isPayloadFormatSupported(target.PayloadFormat) {
|
||||
return false, errors.New(nil).WithMessage("unsupported payload format type: %s", target.PayloadFormat).WithCode(errors.BadRequestCode)
|
||||
}
|
||||
}
|
||||
return true, nil
|
||||
}
|
||||
@ -475,3 +490,13 @@ func isNotifyTypeSupported(notifyType string) bool {
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
func isPayloadFormatSupported(payloadFormat string) bool {
|
||||
for _, t := range notification.GetSupportedPayloadFormats() {
|
||||
if t.String() == payloadFormat {
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
|
201
src/vendor/github.com/cloudevents/sdk-go/v2/LICENSE
generated
vendored
Normal file
201
src/vendor/github.com/cloudevents/sdk-go/v2/LICENSE
generated
vendored
Normal file
@ -0,0 +1,201 @@
|
||||
Apache License
|
||||
Version 2.0, January 2004
|
||||
http://www.apache.org/licenses/
|
||||
|
||||
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
|
||||
|
||||
1. Definitions.
|
||||
|
||||
"License" shall mean the terms and conditions for use, reproduction,
|
||||
and distribution as defined by Sections 1 through 9 of this document.
|
||||
|
||||
"Licensor" shall mean the copyright owner or entity authorized by
|
||||
the copyright owner that is granting the License.
|
||||
|
||||
"Legal Entity" shall mean the union of the acting entity and all
|
||||
other entities that control, are controlled by, or are under common
|
||||
control with that entity. For the purposes of this definition,
|
||||
"control" means (i) the power, direct or indirect, to cause the
|
||||
direction or management of such entity, whether by contract or
|
||||
otherwise, or (ii) ownership of fifty percent (50%) or more of the
|
||||
outstanding shares, or (iii) beneficial ownership of such entity.
|
||||
|
||||
"You" (or "Your") shall mean an individual or Legal Entity
|
||||
exercising permissions granted by this License.
|
||||
|
||||
"Source" form shall mean the preferred form for making modifications,
|
||||
including but not limited to software source code, documentation
|
||||
source, and configuration files.
|
||||
|
||||
"Object" form shall mean any form resulting from mechanical
|
||||
transformation or translation of a Source form, including but
|
||||
not limited to compiled object code, generated documentation,
|
||||
and conversions to other media types.
|
||||
|
||||
"Work" shall mean the work of authorship, whether in Source or
|
||||
Object form, made available under the License, as indicated by a
|
||||
copyright notice that is included in or attached to the work
|
||||
(an example is provided in the Appendix below).
|
||||
|
||||
"Derivative Works" shall mean any work, whether in Source or Object
|
||||
form, that is based on (or derived from) the Work and for which the
|
||||
editorial revisions, annotations, elaborations, or other modifications
|
||||
represent, as a whole, an original work of authorship. For the purposes
|
||||
of this License, Derivative Works shall not include works that remain
|
||||
separable from, or merely link (or bind by name) to the interfaces of,
|
||||
the Work and Derivative Works thereof.
|
||||
|
||||
"Contribution" shall mean any work of authorship, including
|
||||
the original version of the Work and any modifications or additions
|
||||
to that Work or Derivative Works thereof, that is intentionally
|
||||
submitted to Licensor for inclusion in the Work by the copyright owner
|
||||
or by an individual or Legal Entity authorized to submit on behalf of
|
||||
the copyright owner. For the purposes of this definition, "submitted"
|
||||
means any form of electronic, verbal, or written communication sent
|
||||
to the Licensor or its representatives, including but not limited to
|
||||
communication on electronic mailing lists, source code control systems,
|
||||
and issue tracking systems that are managed by, or on behalf of, the
|
||||
Licensor for the purpose of discussing and improving the Work, but
|
||||
excluding communication that is conspicuously marked or otherwise
|
||||
designated in writing by the copyright owner as "Not a Contribution."
|
||||
|
||||
"Contributor" shall mean Licensor and any individual or Legal Entity
|
||||
on behalf of whom a Contribution has been received by Licensor and
|
||||
subsequently incorporated within the Work.
|
||||
|
||||
2. Grant of Copyright License. Subject to the terms and conditions of
|
||||
this License, each Contributor hereby grants to You a perpetual,
|
||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||
copyright license to reproduce, prepare Derivative Works of,
|
||||
publicly display, publicly perform, sublicense, and distribute the
|
||||
Work and such Derivative Works in Source or Object form.
|
||||
|
||||
3. Grant of Patent License. Subject to the terms and conditions of
|
||||
this License, each Contributor hereby grants to You a perpetual,
|
||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||
(except as stated in this section) patent license to make, have made,
|
||||
use, offer to sell, sell, import, and otherwise transfer the Work,
|
||||
where such license applies only to those patent claims licensable
|
||||
by such Contributor that are necessarily infringed by their
|
||||
Contribution(s) alone or by combination of their Contribution(s)
|
||||
with the Work to which such Contribution(s) was submitted. If You
|
||||
institute patent litigation against any entity (including a
|
||||
cross-claim or counterclaim in a lawsuit) alleging that the Work
|
||||
or a Contribution incorporated within the Work constitutes direct
|
||||
or contributory patent infringement, then any patent licenses
|
||||
granted to You under this License for that Work shall terminate
|
||||
as of the date such litigation is filed.
|
||||
|
||||
4. Redistribution. You may reproduce and distribute copies of the
|
||||
Work or Derivative Works thereof in any medium, with or without
|
||||
modifications, and in Source or Object form, provided that You
|
||||
meet the following conditions:
|
||||
|
||||
(a) You must give any other recipients of the Work or
|
||||
Derivative Works a copy of this License; and
|
||||
|
||||
(b) You must cause any modified files to carry prominent notices
|
||||
stating that You changed the files; and
|
||||
|
||||
(c) You must retain, in the Source form of any Derivative Works
|
||||
that You distribute, all copyright, patent, trademark, and
|
||||
attribution notices from the Source form of the Work,
|
||||
excluding those notices that do not pertain to any part of
|
||||
the Derivative Works; and
|
||||
|
||||
(d) If the Work includes a "NOTICE" text file as part of its
|
||||
distribution, then any Derivative Works that You distribute must
|
||||
include a readable copy of the attribution notices contained
|
||||
within such NOTICE file, excluding those notices that do not
|
||||
pertain to any part of the Derivative Works, in at least one
|
||||
of the following places: within a NOTICE text file distributed
|
||||
as part of the Derivative Works; within the Source form or
|
||||
documentation, if provided along with the Derivative Works; or,
|
||||
within a display generated by the Derivative Works, if and
|
||||
wherever such third-party notices normally appear. The contents
|
||||
of the NOTICE file are for informational purposes only and
|
||||
do not modify the License. You may add Your own attribution
|
||||
notices within Derivative Works that You distribute, alongside
|
||||
or as an addendum to the NOTICE text from the Work, provided
|
||||
that such additional attribution notices cannot be construed
|
||||
as modifying the License.
|
||||
|
||||
You may add Your own copyright statement to Your modifications and
|
||||
may provide additional or different license terms and conditions
|
||||
for use, reproduction, or distribution of Your modifications, or
|
||||
for any such Derivative Works as a whole, provided Your use,
|
||||
reproduction, and distribution of the Work otherwise complies with
|
||||
the conditions stated in this License.
|
||||
|
||||
5. Submission of Contributions. Unless You explicitly state otherwise,
|
||||
any Contribution intentionally submitted for inclusion in the Work
|
||||
by You to the Licensor shall be under the terms and conditions of
|
||||
this License, without any additional terms or conditions.
|
||||
Notwithstanding the above, nothing herein shall supersede or modify
|
||||
the terms of any separate license agreement you may have executed
|
||||
with Licensor regarding such Contributions.
|
||||
|
||||
6. Trademarks. This License does not grant permission to use the trade
|
||||
names, trademarks, service marks, or product names of the Licensor,
|
||||
except as required for reasonable and customary use in describing the
|
||||
origin of the Work and reproducing the content of the NOTICE file.
|
||||
|
||||
7. Disclaimer of Warranty. Unless required by applicable law or
|
||||
agreed to in writing, Licensor provides the Work (and each
|
||||
Contributor provides its Contributions) on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
|
||||
implied, including, without limitation, any warranties or conditions
|
||||
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
|
||||
PARTICULAR PURPOSE. You are solely responsible for determining the
|
||||
appropriateness of using or redistributing the Work and assume any
|
||||
risks associated with Your exercise of permissions under this License.
|
||||
|
||||
8. Limitation of Liability. In no event and under no legal theory,
|
||||
whether in tort (including negligence), contract, or otherwise,
|
||||
unless required by applicable law (such as deliberate and grossly
|
||||
negligent acts) or agreed to in writing, shall any Contributor be
|
||||
liable to You for damages, including any direct, indirect, special,
|
||||
incidental, or consequential damages of any character arising as a
|
||||
result of this License or out of the use or inability to use the
|
||||
Work (including but not limited to damages for loss of goodwill,
|
||||
work stoppage, computer failure or malfunction, or any and all
|
||||
other commercial damages or losses), even if such Contributor
|
||||
has been advised of the possibility of such damages.
|
||||
|
||||
9. Accepting Warranty or Additional Liability. While redistributing
|
||||
the Work or Derivative Works thereof, You may choose to offer,
|
||||
and charge a fee for, acceptance of support, warranty, indemnity,
|
||||
or other liability obligations and/or rights consistent with this
|
||||
License. However, in accepting such obligations, You may act only
|
||||
on Your own behalf and on Your sole responsibility, not on behalf
|
||||
of any other Contributor, and only if You agree to indemnify,
|
||||
defend, and hold each Contributor harmless for any liability
|
||||
incurred by, or claims asserted against, such Contributor by reason
|
||||
of your accepting any such warranty or additional liability.
|
||||
|
||||
END OF TERMS AND CONDITIONS
|
||||
|
||||
APPENDIX: How to apply the Apache License to your work.
|
||||
|
||||
To apply the Apache License to your work, attach the following
|
||||
boilerplate notice, with the fields enclosed by brackets "[]"
|
||||
replaced with your own identifying information. (Don't include
|
||||
the brackets!) The text should be enclosed in the appropriate
|
||||
comment syntax for the file format. We also recommend that a
|
||||
file or class name and description of purpose be included on the
|
||||
same "printed page" as the copyright notice for easier
|
||||
identification within third-party archives.
|
||||
|
||||
Copyright [yyyy] [name of copyright owner]
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
181
src/vendor/github.com/cloudevents/sdk-go/v2/alias.go
generated
vendored
Normal file
181
src/vendor/github.com/cloudevents/sdk-go/v2/alias.go
generated
vendored
Normal file
@ -0,0 +1,181 @@
|
||||
/*
|
||||
Copyright 2021 The CloudEvents Authors
|
||||
SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
// Package v2 reexports a subset of the SDK v2 API.
|
||||
package v2
|
||||
|
||||
// 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/v2/binding"
|
||||
"github.com/cloudevents/sdk-go/v2/client"
|
||||
"github.com/cloudevents/sdk-go/v2/context"
|
||||
"github.com/cloudevents/sdk-go/v2/event"
|
||||
"github.com/cloudevents/sdk-go/v2/protocol"
|
||||
"github.com/cloudevents/sdk-go/v2/protocol/http"
|
||||
"github.com/cloudevents/sdk-go/v2/types"
|
||||
)
|
||||
|
||||
// Client
|
||||
|
||||
type ClientOption = client.Option
|
||||
type Client = client.Client
|
||||
|
||||
// Event
|
||||
|
||||
type Event = event.Event
|
||||
type Result = protocol.Result
|
||||
|
||||
// Context
|
||||
|
||||
type EventContext = event.EventContext
|
||||
type EventContextV1 = event.EventContextV1
|
||||
type EventContextV03 = event.EventContextV03
|
||||
|
||||
// Custom Types
|
||||
|
||||
type Timestamp = types.Timestamp
|
||||
type URIRef = types.URIRef
|
||||
|
||||
// HTTP Protocol
|
||||
|
||||
type HTTPOption = http.Option
|
||||
|
||||
type HTTPProtocol = http.Protocol
|
||||
|
||||
// Encoding
|
||||
|
||||
type Encoding = binding.Encoding
|
||||
|
||||
// Message
|
||||
|
||||
type Message = binding.Message
|
||||
|
||||
const (
|
||||
// ReadEncoding
|
||||
|
||||
ApplicationXML = event.ApplicationXML
|
||||
ApplicationJSON = event.ApplicationJSON
|
||||
TextPlain = event.TextPlain
|
||||
ApplicationCloudEventsJSON = event.ApplicationCloudEventsJSON
|
||||
ApplicationCloudEventsBatchJSON = event.ApplicationCloudEventsBatchJSON
|
||||
Base64 = event.Base64
|
||||
|
||||
// Event Versions
|
||||
|
||||
VersionV1 = event.CloudEventsVersionV1
|
||||
VersionV03 = event.CloudEventsVersionV03
|
||||
|
||||
// Encoding
|
||||
|
||||
EncodingBinary = binding.EncodingBinary
|
||||
EncodingStructured = binding.EncodingStructured
|
||||
)
|
||||
|
||||
var (
|
||||
|
||||
// ContentType Helpers
|
||||
|
||||
StringOfApplicationJSON = event.StringOfApplicationJSON
|
||||
StringOfApplicationXML = event.StringOfApplicationXML
|
||||
StringOfTextPlain = event.StringOfTextPlain
|
||||
StringOfApplicationCloudEventsJSON = event.StringOfApplicationCloudEventsJSON
|
||||
StringOfApplicationCloudEventsBatchJSON = event.StringOfApplicationCloudEventsBatchJSON
|
||||
StringOfBase64 = event.StringOfBase64
|
||||
|
||||
// Client Creation
|
||||
|
||||
NewClient = client.New
|
||||
NewClientHTTP = client.NewHTTP
|
||||
// Deprecated: please use New with the observability options.
|
||||
NewClientObserved = client.NewObserved
|
||||
// Deprecated: Please use NewClientHTTP with the observability options.
|
||||
NewDefaultClient = client.NewDefault
|
||||
NewHTTPReceiveHandler = client.NewHTTPReceiveHandler
|
||||
|
||||
// Client Options
|
||||
|
||||
WithEventDefaulter = client.WithEventDefaulter
|
||||
WithUUIDs = client.WithUUIDs
|
||||
WithTimeNow = client.WithTimeNow
|
||||
// Deprecated: this is now noop and will be removed in future releases.
|
||||
WithTracePropagation = client.WithTracePropagation()
|
||||
|
||||
// Event Creation
|
||||
|
||||
NewEvent = event.New
|
||||
|
||||
// Results
|
||||
|
||||
NewResult = protocol.NewResult
|
||||
ResultIs = protocol.ResultIs
|
||||
ResultAs = protocol.ResultAs
|
||||
|
||||
// Receipt helpers
|
||||
|
||||
NewReceipt = protocol.NewReceipt
|
||||
|
||||
ResultACK = protocol.ResultACK
|
||||
ResultNACK = protocol.ResultNACK
|
||||
|
||||
IsACK = protocol.IsACK
|
||||
IsNACK = protocol.IsNACK
|
||||
IsUndelivered = protocol.IsUndelivered
|
||||
|
||||
// HTTP Results
|
||||
|
||||
NewHTTPResult = http.NewResult
|
||||
NewHTTPRetriesResult = http.NewRetriesResult
|
||||
|
||||
// Message Creation
|
||||
|
||||
ToMessage = binding.ToMessage
|
||||
|
||||
// Event Creation
|
||||
NewEventFromHTTPRequest = http.NewEventFromHTTPRequest
|
||||
NewEventFromHTTPResponse = http.NewEventFromHTTPResponse
|
||||
|
||||
// HTTP Messages
|
||||
|
||||
WriteHTTPRequest = http.WriteRequest
|
||||
|
||||
// Context
|
||||
|
||||
ContextWithTarget = context.WithTarget
|
||||
TargetFromContext = context.TargetFrom
|
||||
ContextWithRetriesConstantBackoff = context.WithRetriesConstantBackoff
|
||||
ContextWithRetriesLinearBackoff = context.WithRetriesLinearBackoff
|
||||
ContextWithRetriesExponentialBackoff = context.WithRetriesExponentialBackoff
|
||||
|
||||
WithEncodingBinary = binding.WithForceBinary
|
||||
WithEncodingStructured = binding.WithForceStructured
|
||||
|
||||
// Custom Types
|
||||
|
||||
ParseTimestamp = types.ParseTimestamp
|
||||
ParseURIRef = types.ParseURIRef
|
||||
ParseURI = types.ParseURI
|
||||
|
||||
// HTTP Protocol
|
||||
|
||||
NewHTTP = http.New
|
||||
|
||||
// HTTP Protocol Options
|
||||
|
||||
WithTarget = http.WithTarget
|
||||
WithHeader = http.WithHeader
|
||||
WithShutdownTimeout = http.WithShutdownTimeout
|
||||
//WithEncoding = http.WithEncoding
|
||||
//WithStructuredEncoding = http.WithStructuredEncoding // TODO: expose new way
|
||||
WithPort = http.WithPort
|
||||
WithPath = http.WithPath
|
||||
WithMiddleware = http.WithMiddleware
|
||||
WithListener = http.WithListener
|
||||
WithRoundTripper = http.WithRoundTripper
|
||||
WithGetHandlerFunc = http.WithGetHandlerFunc
|
||||
WithOptionsHandlerFunc = http.WithOptionsHandlerFunc
|
||||
WithDefaultOptionsHandlerFunc = http.WithDefaultOptionsHandlerFunc
|
||||
)
|
52
src/vendor/github.com/cloudevents/sdk-go/v2/binding/binary_writer.go
generated
vendored
Normal file
52
src/vendor/github.com/cloudevents/sdk-go/v2/binding/binary_writer.go
generated
vendored
Normal file
@ -0,0 +1,52 @@
|
||||
/*
|
||||
Copyright 2021 The CloudEvents Authors
|
||||
SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
package binding
|
||||
|
||||
import (
|
||||
"context"
|
||||
"io"
|
||||
|
||||
"github.com/cloudevents/sdk-go/v2/binding/spec"
|
||||
)
|
||||
|
||||
// MessageMetadataWriter is used to set metadata when a binary Message is visited.
|
||||
type MessageMetadataWriter interface {
|
||||
// Set a standard attribute.
|
||||
//
|
||||
// The value can either be the correct golang type for the attribute, or a canonical
|
||||
// string encoding, or nil. If value is nil, then the attribute should be deleted.
|
||||
// See package types to perform the needed conversions.
|
||||
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, or nil. If value is nil, then the extension should be deleted.
|
||||
// See package types to perform the needed conversions.
|
||||
SetExtension(name string, value interface{}) error
|
||||
}
|
||||
|
||||
// BinaryWriter 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 to binary encoding and event to binary encoding.
|
||||
//
|
||||
// Start() and End() methods must be invoked by the caller of Message.ReadBinary() every time
|
||||
// the BinaryWriter implementation is used to visit a Message.
|
||||
type BinaryWriter interface {
|
||||
MessageMetadataWriter
|
||||
|
||||
// Method invoked at the beginning of the visit. Useful to perform initial memory allocations
|
||||
Start(ctx context.Context) error
|
||||
|
||||
// SetData receives an io.Reader for the data attribute.
|
||||
// io.Reader is not invoked when the data attribute 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(ctx context.Context) error
|
||||
}
|
68
src/vendor/github.com/cloudevents/sdk-go/v2/binding/doc.go
generated
vendored
Normal file
68
src/vendor/github.com/cloudevents/sdk-go/v2/binding/doc.go
generated
vendored
Normal file
@ -0,0 +1,68 @@
|
||||
/*
|
||||
Copyright 2021 The CloudEvents Authors
|
||||
SPDX-License-Identifier: Apache-2.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 usually implements a Message, a Sender and Receiver, a StructuredWriter and a BinaryWriter (depending on the supported encodings of the protocol) and an Write[ProtocolMessage] method.
|
||||
|
||||
Read and write events
|
||||
|
||||
The core of this package is the binding.Message interface.
|
||||
Through binding.MessageReader It defines how to read a protocol specific message for an
|
||||
encoded event in structured mode or binary mode.
|
||||
The entity who receives a protocol specific data structure representing a message
|
||||
(e.g. an HttpRequest) encapsulates it in a binding.Message implementation using a NewMessage method (e.g. http.NewMessage).
|
||||
Then the entity that wants to send the binding.Message back on the wire,
|
||||
translates it back to the protocol specific data structure (e.g. a Kafka ConsumerMessage), using
|
||||
the writers BinaryWriter and StructuredWriter specific to that protocol.
|
||||
Binding implementations exposes their writers
|
||||
through a specific Write[ProtocolMessage] function (e.g. kafka.EncodeProducerMessage),
|
||||
in order to simplify the encoding process.
|
||||
|
||||
The encoding process can be customized in order to mutate the final result with binding.TransformerFactory.
|
||||
A bunch of these are provided directly by the binding/transformer module.
|
||||
|
||||
Usually binding.Message implementations can be encoded only one time, because the encoding process drain the message itself.
|
||||
In order to consume a message several times, the binding/buffering package provides several APIs to buffer the Message.
|
||||
|
||||
A message can be converted to an event.Event using binding.ToEvent() method.
|
||||
An event.Event can be used as Message casting it to binding.EventMessage.
|
||||
|
||||
In order to simplify the encoding process for each protocol, this package provide several utility methods like binding.Write and binding.DirectWrite.
|
||||
The binding.Write method tries to preserve the structured/binary encoding, in order to be as much efficient as possible.
|
||||
|
||||
Messages can be eventually wrapped to change their behaviours and binding their lifecycle, like the binding.FinishMessage.
|
||||
Every Message wrapper implements the MessageWrapper interface
|
||||
|
||||
Sender and Receiver
|
||||
|
||||
A Receiver receives protocol specific messages and wraps them to into binding.Message implementations.
|
||||
|
||||
A Sender converts arbitrary Message implementations to a protocol-specific form using the protocol specific Write method
|
||||
and sends them.
|
||||
|
||||
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.
|
||||
|
||||
Transport
|
||||
|
||||
A binding implementation providing Sender and Receiver implementations can be used as a Transport through the BindingTransport adapter.
|
||||
|
||||
*/
|
||||
package binding
|
45
src/vendor/github.com/cloudevents/sdk-go/v2/binding/encoding.go
generated
vendored
Normal file
45
src/vendor/github.com/cloudevents/sdk-go/v2/binding/encoding.go
generated
vendored
Normal file
@ -0,0 +1,45 @@
|
||||
/*
|
||||
Copyright 2021 The CloudEvents Authors
|
||||
SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
package binding
|
||||
|
||||
import "errors"
|
||||
|
||||
// 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 EventMessage nested (through MessageWrapper)
|
||||
EncodingEvent
|
||||
// When the encoding is unknown (which means that the message is a non-event)
|
||||
EncodingUnknown
|
||||
)
|
||||
|
||||
func (e Encoding) String() string {
|
||||
switch e {
|
||||
case EncodingBinary:
|
||||
return "binary"
|
||||
case EncodingStructured:
|
||||
return "structured"
|
||||
case EncodingEvent:
|
||||
return "event"
|
||||
case EncodingUnknown:
|
||||
return "unknown"
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
// ErrUnknownEncoding specifies that the Message is not an event or it is encoded with an unknown encoding
|
||||
var ErrUnknownEncoding = errors.New("unknown Message encoding")
|
||||
|
||||
// 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")
|
108
src/vendor/github.com/cloudevents/sdk-go/v2/binding/event_message.go
generated
vendored
Normal file
108
src/vendor/github.com/cloudevents/sdk-go/v2/binding/event_message.go
generated
vendored
Normal file
@ -0,0 +1,108 @@
|
||||
/*
|
||||
Copyright 2021 The CloudEvents Authors
|
||||
SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
package binding
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
|
||||
"github.com/cloudevents/sdk-go/v2/binding/format"
|
||||
"github.com/cloudevents/sdk-go/v2/binding/spec"
|
||||
"github.com/cloudevents/sdk-go/v2/event"
|
||||
)
|
||||
|
||||
type eventFormatKey int
|
||||
|
||||
const (
|
||||
formatEventStructured eventFormatKey = iota
|
||||
)
|
||||
|
||||
// EventMessage type-converts a event.Event object to implement Message.
|
||||
// This allows local event.Event objects to be sent directly via Sender.Send()
|
||||
// s.Send(ctx, binding.EventMessage(e))
|
||||
// When an event is wrapped into a EventMessage, the original event could be
|
||||
// potentially mutated. If you need to use the Event again, after wrapping it into
|
||||
// an Event message, you should copy it before
|
||||
type EventMessage event.Event
|
||||
|
||||
func ToMessage(e *event.Event) Message {
|
||||
return (*EventMessage)(e)
|
||||
}
|
||||
|
||||
func (m *EventMessage) ReadEncoding() Encoding {
|
||||
return EncodingEvent
|
||||
}
|
||||
|
||||
func (m *EventMessage) ReadStructured(ctx context.Context, builder StructuredWriter) error {
|
||||
f := GetOrDefaultFromCtx(ctx, formatEventStructured, format.JSON).(format.Format)
|
||||
b, err := f.Marshal((*event.Event)(m))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return builder.SetStructuredEvent(ctx, f, bytes.NewReader(b))
|
||||
}
|
||||
|
||||
func (m *EventMessage) ReadBinary(ctx context.Context, b BinaryWriter) (err error) {
|
||||
err = eventContextToBinaryWriter(m.Context, b)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
// Pass the body
|
||||
body := (*event.Event)(m).Data()
|
||||
if len(body) > 0 {
|
||||
err = b.SetData(bytes.NewBuffer(body))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (m *EventMessage) GetAttribute(k spec.Kind) (spec.Attribute, interface{}) {
|
||||
sv := spec.VS.Version(m.Context.GetSpecVersion())
|
||||
a := sv.AttributeFromKind(k)
|
||||
if a != nil {
|
||||
return a, a.Get(m.Context)
|
||||
}
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
func (m *EventMessage) GetExtension(name string) interface{} {
|
||||
ext, _ := m.Context.GetExtension(name)
|
||||
return ext
|
||||
}
|
||||
|
||||
func eventContextToBinaryWriter(c event.EventContext, b BinaryWriter) (err error) {
|
||||
// Pass all attributes
|
||||
sv := spec.VS.Version(c.GetSpecVersion())
|
||||
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
|
||||
}
|
||||
|
||||
func (*EventMessage) Finish(error) error { return nil }
|
||||
|
||||
var _ Message = (*EventMessage)(nil) // Test it conforms to the interface
|
||||
var _ MessageMetadataReader = (*EventMessage)(nil) // Test it conforms to the interface
|
||||
|
||||
// UseFormatForEvent configures which format to use when marshalling the event to structured mode
|
||||
func UseFormatForEvent(ctx context.Context, f format.Format) context.Context {
|
||||
return context.WithValue(ctx, formatEventStructured, f)
|
||||
}
|
42
src/vendor/github.com/cloudevents/sdk-go/v2/binding/finish_message.go
generated
vendored
Normal file
42
src/vendor/github.com/cloudevents/sdk-go/v2/binding/finish_message.go
generated
vendored
Normal file
@ -0,0 +1,42 @@
|
||||
/*
|
||||
Copyright 2021 The CloudEvents Authors
|
||||
SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
package binding
|
||||
|
||||
import "github.com/cloudevents/sdk-go/v2/binding/spec"
|
||||
|
||||
type finishMessage struct {
|
||||
Message
|
||||
finish func(error)
|
||||
}
|
||||
|
||||
func (m *finishMessage) GetAttribute(k spec.Kind) (spec.Attribute, interface{}) {
|
||||
return m.Message.(MessageMetadataReader).GetAttribute(k)
|
||||
}
|
||||
|
||||
func (m *finishMessage) GetExtension(s string) interface{} {
|
||||
return m.Message.(MessageMetadataReader).GetExtension(s)
|
||||
}
|
||||
|
||||
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}
|
||||
}
|
12
src/vendor/github.com/cloudevents/sdk-go/v2/binding/format/doc.go
generated
vendored
Normal file
12
src/vendor/github.com/cloudevents/sdk-go/v2/binding/format/doc.go
generated
vendored
Normal file
@ -0,0 +1,12 @@
|
||||
/*
|
||||
Copyright 2021 The CloudEvents Authors
|
||||
SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
/*
|
||||
Package format formats structured events.
|
||||
|
||||
The "application/cloudevents+json" format is built-in and always
|
||||
available. Other formats may be added.
|
||||
*/
|
||||
package format
|
83
src/vendor/github.com/cloudevents/sdk-go/v2/binding/format/format.go
generated
vendored
Normal file
83
src/vendor/github.com/cloudevents/sdk-go/v2/binding/format/format.go
generated
vendored
Normal file
@ -0,0 +1,83 @@
|
||||
/*
|
||||
Copyright 2021 The CloudEvents Authors
|
||||
SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
package format
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"strings"
|
||||
|
||||
"github.com/cloudevents/sdk-go/v2/event"
|
||||
)
|
||||
|
||||
// Format marshals and unmarshals structured events to bytes.
|
||||
type Format interface {
|
||||
// MediaType identifies the format
|
||||
MediaType() string
|
||||
// Marshal event to bytes
|
||||
Marshal(*event.Event) ([]byte, error)
|
||||
// Unmarshal bytes to event
|
||||
Unmarshal([]byte, *event.Event) error
|
||||
}
|
||||
|
||||
// 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 event.ApplicationCloudEventsJSON }
|
||||
|
||||
func (jsonFmt) Marshal(e *event.Event) ([]byte, error) { return json.Marshal(e) }
|
||||
func (jsonFmt) Unmarshal(b []byte, e *event.Event) error {
|
||||
return json.Unmarshal(b, e)
|
||||
}
|
||||
|
||||
// built-in formats
|
||||
var formats map[string]Format
|
||||
|
||||
func init() {
|
||||
formats = map[string]Format{}
|
||||
Add(JSON)
|
||||
}
|
||||
|
||||
// Lookup returns the format for contentType, or nil if not found.
|
||||
func Lookup(contentType string) Format {
|
||||
i := strings.IndexRune(contentType, ';')
|
||||
if i == -1 {
|
||||
i = len(contentType)
|
||||
}
|
||||
contentType = strings.TrimSpace(strings.ToLower(contentType[0:i]))
|
||||
return formats[contentType]
|
||||
}
|
||||
|
||||
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 *event.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 *event.Event) error {
|
||||
if f := formats[mediaType]; f != nil {
|
||||
return f.Unmarshal(b, e)
|
||||
}
|
||||
return unknown(mediaType)
|
||||
}
|
153
src/vendor/github.com/cloudevents/sdk-go/v2/binding/message.go
generated
vendored
Normal file
153
src/vendor/github.com/cloudevents/sdk-go/v2/binding/message.go
generated
vendored
Normal file
@ -0,0 +1,153 @@
|
||||
/*
|
||||
Copyright 2021 The CloudEvents Authors
|
||||
SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
package binding
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"github.com/cloudevents/sdk-go/v2/binding/spec"
|
||||
)
|
||||
|
||||
// MessageReader defines the read-related portion of the Message interface.
|
||||
//
|
||||
// The ReadStructured and ReadBinary methods allows to perform an optimized encoding of a Message to a specific data structure.
|
||||
//
|
||||
// If MessageReader.ReadEncoding() can be equal to EncodingBinary, then the implementation of MessageReader
|
||||
// MUST also implement MessageMetadataReader.
|
||||
//
|
||||
// A Sender should try each method of interest and fall back to binding.ToEvent() if none are supported.
|
||||
// An out of the box algorithm is provided for writing a message: binding.Write().
|
||||
type MessageReader interface {
|
||||
// Return the type of the message Encoding.
|
||||
// The encoding should be preferably computed when the message is constructed.
|
||||
ReadEncoding() Encoding
|
||||
|
||||
// ReadStructured transfers a structured-mode event to a StructuredWriter.
|
||||
// It must return 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.
|
||||
ReadStructured(context.Context, StructuredWriter) error
|
||||
|
||||
// ReadBinary transfers a binary-mode event to an BinaryWriter.
|
||||
// It must return ErrNotBinary if message is not in binary mode.
|
||||
//
|
||||
// The implementation of ReadBinary must not control the lifecycle with BinaryWriter.Start() and BinaryWriter.End(),
|
||||
// because the caller must control the lifecycle.
|
||||
//
|
||||
// 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
|
||||
//
|
||||
// This allows Senders to avoid re-encoding messages that are
|
||||
// already in suitable binary form.
|
||||
ReadBinary(context.Context, BinaryWriter) error
|
||||
}
|
||||
|
||||
// MessageMetadataReader defines how to read metadata from a binary/event message
|
||||
//
|
||||
// If a message implementing MessageReader is encoded as binary (MessageReader.ReadEncoding() == EncodingBinary)
|
||||
// or it's an EventMessage, then it's safe to assume that it also implements this interface
|
||||
type MessageMetadataReader interface {
|
||||
// GetAttribute returns:
|
||||
//
|
||||
// * attribute, value: if the message contains an attribute of that attribute kind
|
||||
// * attribute, nil: if the message spec version supports the attribute kind, but doesn't have any value
|
||||
// * nil, nil: if the message spec version doesn't support the attribute kind
|
||||
GetAttribute(attributeKind spec.Kind) (spec.Attribute, interface{})
|
||||
// GetExtension returns the value of that extension, if any.
|
||||
GetExtension(name string) interface{}
|
||||
}
|
||||
|
||||
// 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
|
||||
//
|
||||
// Message includes the MessageReader interface to read messages. Every binding.Message implementation *must* specify if the message can be accessed one or more times.
|
||||
//
|
||||
// When a Message can be forgotten by the entity who produced the message, Message.Finish() *must* be invoked.
|
||||
type Message interface {
|
||||
MessageReader
|
||||
|
||||
// Finish *must* be called when message from a Receiver can be forgotten by
|
||||
// the receiver. A QoS 1 sender should not call Finish() until it gets an acknowledgment of
|
||||
// receipt on the underlying transport. For QoS 2 see ExactlyOnceMessage.
|
||||
//
|
||||
// Note that, depending on the Message implementation, forgetting to Finish the message
|
||||
// could produce memory/resources leaks!
|
||||
//
|
||||
// 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
|
||||
}
|
||||
|
||||
// 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))
|
||||
}
|
||||
|
||||
// MessageContext interface exposes the internal context that a message might contain
|
||||
// Only some Message implementations implement this interface.
|
||||
type MessageContext interface {
|
||||
// Get the context associated with this message
|
||||
Context() context.Context
|
||||
}
|
||||
|
||||
// MessageWrapper interface is used to walk through a decorated Message and unwrap it.
|
||||
type MessageWrapper interface {
|
||||
Message
|
||||
MessageMetadataReader
|
||||
|
||||
// Method to get the wrapped message
|
||||
GetWrappedMessage() Message
|
||||
}
|
||||
|
||||
func UnwrapMessage(message Message) Message {
|
||||
m := message
|
||||
for m != nil {
|
||||
switch mt := m.(type) {
|
||||
case MessageWrapper:
|
||||
m = mt.GetWrappedMessage()
|
||||
default:
|
||||
return m
|
||||
}
|
||||
}
|
||||
return m
|
||||
}
|
141
src/vendor/github.com/cloudevents/sdk-go/v2/binding/spec/attributes.go
generated
vendored
Normal file
141
src/vendor/github.com/cloudevents/sdk-go/v2/binding/spec/attributes.go
generated
vendored
Normal file
@ -0,0 +1,141 @@
|
||||
/*
|
||||
Copyright 2021 The CloudEvents Authors
|
||||
SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
package spec
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"time"
|
||||
|
||||
"github.com/cloudevents/sdk-go/v2/event"
|
||||
|
||||
"github.com/cloudevents/sdk-go/v2/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() with prefix
|
||||
PrefixedName() string
|
||||
// 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(event.EventContextReader) interface{}
|
||||
// Set the value of this attribute on an event context
|
||||
Set(event.EventContextWriter, interface{}) error
|
||||
// Delete this attribute from and event context, when possible
|
||||
Delete(event.EventContextWriter) error
|
||||
}
|
||||
|
||||
// accessor provides Kind, Get, Set.
|
||||
type accessor interface {
|
||||
Kind() Kind
|
||||
Get(event.EventContextReader) interface{}
|
||||
Set(event.EventContextWriter, interface{}) error
|
||||
Delete(event.EventContextWriter) error
|
||||
}
|
||||
|
||||
var acc = [nAttrs]accessor{
|
||||
&aStr{aKind(ID), event.EventContextReader.GetID, event.EventContextWriter.SetID},
|
||||
&aStr{aKind(Source), event.EventContextReader.GetSource, event.EventContextWriter.SetSource},
|
||||
&aStr{aKind(SpecVersion), event.EventContextReader.GetSpecVersion, func(writer event.EventContextWriter, s string) error { return nil }},
|
||||
&aStr{aKind(Type), event.EventContextReader.GetType, event.EventContextWriter.SetType},
|
||||
&aStr{aKind(DataContentType), event.EventContextReader.GetDataContentType, event.EventContextWriter.SetDataContentType},
|
||||
&aStr{aKind(DataSchema), event.EventContextReader.GetDataSchema, event.EventContextWriter.SetDataSchema},
|
||||
&aStr{aKind(Subject), event.EventContextReader.GetSubject, event.EventContextWriter.SetSubject},
|
||||
&aTime{aKind(Time), event.EventContextReader.GetTime, event.EventContextWriter.SetTime},
|
||||
}
|
||||
|
||||
// aKind implements Kind()
|
||||
type aKind Kind
|
||||
|
||||
func (kind aKind) Kind() Kind { return Kind(kind) }
|
||||
|
||||
type aStr struct {
|
||||
aKind
|
||||
get func(event.EventContextReader) string
|
||||
set func(event.EventContextWriter, string) error
|
||||
}
|
||||
|
||||
func (a *aStr) Get(c event.EventContextReader) interface{} {
|
||||
if s := a.get(c); s != "" {
|
||||
return s
|
||||
}
|
||||
return nil // Treat blank as missing
|
||||
}
|
||||
|
||||
func (a *aStr) Set(c event.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 event.EventContextWriter) error {
|
||||
return a.set(c, "")
|
||||
}
|
||||
|
||||
type aTime struct {
|
||||
aKind
|
||||
get func(event.EventContextReader) time.Time
|
||||
set func(event.EventContextWriter, time.Time) error
|
||||
}
|
||||
|
||||
func (a *aTime) Get(c event.EventContextReader) interface{} {
|
||||
if v := a.get(c); !v.IsZero() {
|
||||
return v
|
||||
}
|
||||
return nil // Treat zero time as missing.
|
||||
}
|
||||
|
||||
func (a *aTime) Set(c event.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 event.EventContextWriter) error {
|
||||
return a.set(c, time.Time{})
|
||||
}
|
13
src/vendor/github.com/cloudevents/sdk-go/v2/binding/spec/doc.go
generated
vendored
Normal file
13
src/vendor/github.com/cloudevents/sdk-go/v2/binding/spec/doc.go
generated
vendored
Normal file
@ -0,0 +1,13 @@
|
||||
/*
|
||||
Copyright 2021 The CloudEvents Authors
|
||||
SPDX-License-Identifier: Apache-2.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
|
81
src/vendor/github.com/cloudevents/sdk-go/v2/binding/spec/match_exact_version.go
generated
vendored
Normal file
81
src/vendor/github.com/cloudevents/sdk-go/v2/binding/spec/match_exact_version.go
generated
vendored
Normal file
@ -0,0 +1,81 @@
|
||||
/*
|
||||
Copyright 2021 The CloudEvents Authors
|
||||
SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
package spec
|
||||
|
||||
import (
|
||||
"github.com/cloudevents/sdk-go/v2/event"
|
||||
)
|
||||
|
||||
type matchExactVersion struct {
|
||||
version
|
||||
}
|
||||
|
||||
func (v *matchExactVersion) Attribute(name string) Attribute { return v.attrMap[name] }
|
||||
|
||||
var _ Version = (*matchExactVersion)(nil)
|
||||
|
||||
func newMatchExactVersionVersion(
|
||||
prefix string,
|
||||
attributeNameMatchMapper func(string) string,
|
||||
context event.EventContext,
|
||||
convert func(event.EventContextConverter) event.EventContext,
|
||||
attrs ...*attribute,
|
||||
) *matchExactVersion {
|
||||
v := &matchExactVersion{
|
||||
version: version{
|
||||
prefix: prefix,
|
||||
context: context,
|
||||
convert: convert,
|
||||
attrMap: map[string]Attribute{},
|
||||
attrs: make([]Attribute, len(attrs)),
|
||||
},
|
||||
}
|
||||
for i, a := range attrs {
|
||||
a.version = v
|
||||
v.attrs[i] = a
|
||||
v.attrMap[attributeNameMatchMapper(a.name)] = a
|
||||
}
|
||||
return v
|
||||
}
|
||||
|
||||
// WithPrefixMatchExact returns a set of versions with prefix added to all attribute names.
|
||||
func WithPrefixMatchExact(attributeNameMatchMapper func(string) string, prefix string) *Versions {
|
||||
attr := func(name string, kind Kind) *attribute {
|
||||
return &attribute{accessor: acc[kind], name: name}
|
||||
}
|
||||
vs := &Versions{
|
||||
m: map[string]Version{},
|
||||
prefix: prefix,
|
||||
all: []Version{
|
||||
newMatchExactVersionVersion(prefix, attributeNameMatchMapper, event.EventContextV1{}.AsV1(),
|
||||
func(c event.EventContextConverter) event.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),
|
||||
),
|
||||
newMatchExactVersionVersion(prefix, attributeNameMatchMapper, event.EventContextV03{}.AsV03(),
|
||||
func(c event.EventContextConverter) event.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),
|
||||
),
|
||||
},
|
||||
}
|
||||
for _, v := range vs.all {
|
||||
vs.m[v.String()] = v
|
||||
}
|
||||
return vs
|
||||
}
|
189
src/vendor/github.com/cloudevents/sdk-go/v2/binding/spec/spec.go
generated
vendored
Normal file
189
src/vendor/github.com/cloudevents/sdk-go/v2/binding/spec/spec.go
generated
vendored
Normal file
@ -0,0 +1,189 @@
|
||||
/*
|
||||
Copyright 2021 The CloudEvents Authors
|
||||
SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
package spec
|
||||
|
||||
import (
|
||||
"strings"
|
||||
|
||||
"github.com/cloudevents/sdk-go/v2/event"
|
||||
)
|
||||
|
||||
// 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(prefixedName string) Attribute
|
||||
// Attribute looks up the attribute from kind.
|
||||
// Returns nil if not found.
|
||||
AttributeFromKind(kind Kind) Attribute
|
||||
// Attributes returns all the context attributes for this version.
|
||||
Attributes() []Attribute
|
||||
// Convert translates a context to this version.
|
||||
Convert(event.EventContextConverter) event.EventContext
|
||||
// NewContext returns a new context for this version.
|
||||
NewContext() event.EventContext
|
||||
// SetAttribute sets named attribute to value.
|
||||
//
|
||||
// Name is case insensitive.
|
||||
// Does nothing if name does not start with prefix.
|
||||
SetAttribute(context event.EventContextWriter, name string, value interface{}) error
|
||||
}
|
||||
|
||||
// Versions contains all known versions with the same attribute prefix.
|
||||
type Versions struct {
|
||||
prefix string
|
||||
all []Version
|
||||
m map[string]Version
|
||||
}
|
||||
|
||||
// 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 {
|
||||
return vs.m[name]
|
||||
}
|
||||
|
||||
// Latest returns the latest Version
|
||||
func (vs *Versions) Latest() Version { return vs.all[0] }
|
||||
|
||||
// PrefixedSpecVersionName returns the specversion attribute PrefixedName
|
||||
func (vs *Versions) PrefixedSpecVersionName() string { return vs.prefix + "specversion" }
|
||||
|
||||
// Prefix is the lowercase attribute name prefix.
|
||||
func (vs *Versions) Prefix() string { return vs.prefix }
|
||||
|
||||
type attribute struct {
|
||||
accessor
|
||||
name string
|
||||
version Version
|
||||
}
|
||||
|
||||
func (a *attribute) PrefixedName() string { return a.version.Prefix() + a.name }
|
||||
func (a *attribute) Name() string { return a.name }
|
||||
func (a *attribute) Version() Version { return a.version }
|
||||
|
||||
type version struct {
|
||||
prefix string
|
||||
context event.EventContext
|
||||
convert func(event.EventContextConverter) event.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() event.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 event.EventContextConverter) event.EventContext { return v.convert(c) }
|
||||
|
||||
func (v *version) SetAttribute(c event.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 v.HasPrefix(name) { // Extension attribute
|
||||
return 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 event.EventContext,
|
||||
convert func(event.EventContextConverter) event.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.version = v
|
||||
v.attrs[i] = a
|
||||
v.attrMap[strings.ToLower(a.PrefixedName())] = 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{},
|
||||
prefix: prefix,
|
||||
all: []Version{
|
||||
newVersion(prefix, event.EventContextV1{}.AsV1(),
|
||||
func(c event.EventContextConverter) event.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, event.EventContextV03{}.AsV03(),
|
||||
func(c event.EventContextConverter) event.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),
|
||||
),
|
||||
},
|
||||
}
|
||||
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
|
||||
V03 Version
|
||||
V1 Version
|
||||
)
|
||||
|
||||
func init() {
|
||||
VS = New()
|
||||
V03 = VS.Version("0.3")
|
||||
V1 = VS.Version("1.0")
|
||||
}
|
22
src/vendor/github.com/cloudevents/sdk-go/v2/binding/structured_writer.go
generated
vendored
Normal file
22
src/vendor/github.com/cloudevents/sdk-go/v2/binding/structured_writer.go
generated
vendored
Normal file
@ -0,0 +1,22 @@
|
||||
/*
|
||||
Copyright 2021 The CloudEvents Authors
|
||||
SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
package binding
|
||||
|
||||
import (
|
||||
"context"
|
||||
"io"
|
||||
|
||||
"github.com/cloudevents/sdk-go/v2/binding/format"
|
||||
)
|
||||
|
||||
// StructuredWriter 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 to structured encoding and event to structured encoding.
|
||||
type StructuredWriter interface {
|
||||
// Event receives an io.Reader for the whole event.
|
||||
SetStructuredEvent(ctx context.Context, format format.Format, event io.Reader) error
|
||||
}
|
134
src/vendor/github.com/cloudevents/sdk-go/v2/binding/to_event.go
generated
vendored
Normal file
134
src/vendor/github.com/cloudevents/sdk-go/v2/binding/to_event.go
generated
vendored
Normal file
@ -0,0 +1,134 @@
|
||||
/*
|
||||
Copyright 2021 The CloudEvents Authors
|
||||
SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
package binding
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
|
||||
"github.com/cloudevents/sdk-go/v2/binding/format"
|
||||
"github.com/cloudevents/sdk-go/v2/binding/spec"
|
||||
"github.com/cloudevents/sdk-go/v2/event"
|
||||
"github.com/cloudevents/sdk-go/v2/types"
|
||||
)
|
||||
|
||||
// ErrCannotConvertToEvent is a generic error when a conversion of a Message to an Event fails
|
||||
var ErrCannotConvertToEvent = errors.New("cannot convert message to event")
|
||||
|
||||
// ToEvent translates a Message with a valid Structured or Binary representation to an Event.
|
||||
// This function returns the Event generated from the Message and the original encoding of the message or
|
||||
// an error that points the conversion error.
|
||||
// transformers can be nil and this function guarantees that they are invoked only once during the encoding process.
|
||||
func ToEvent(ctx context.Context, message MessageReader, transformers ...Transformer) (*event.Event, error) {
|
||||
if message == nil {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
messageEncoding := message.ReadEncoding()
|
||||
if messageEncoding == EncodingEvent {
|
||||
m := message
|
||||
for m != nil {
|
||||
switch mt := m.(type) {
|
||||
case *EventMessage:
|
||||
e := (*event.Event)(mt)
|
||||
return e, Transformers(transformers).Transform(mt, (*messageToEventBuilder)(e))
|
||||
case MessageWrapper:
|
||||
m = mt.GetWrappedMessage()
|
||||
default:
|
||||
break
|
||||
}
|
||||
}
|
||||
return nil, ErrCannotConvertToEvent
|
||||
}
|
||||
|
||||
e := event.New()
|
||||
encoder := (*messageToEventBuilder)(&e)
|
||||
_, err := DirectWrite(
|
||||
context.Background(),
|
||||
message,
|
||||
encoder,
|
||||
encoder,
|
||||
)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &e, Transformers(transformers).Transform((*EventMessage)(&e), encoder)
|
||||
}
|
||||
|
||||
type messageToEventBuilder event.Event
|
||||
|
||||
var _ StructuredWriter = (*messageToEventBuilder)(nil)
|
||||
var _ BinaryWriter = (*messageToEventBuilder)(nil)
|
||||
|
||||
func (b *messageToEventBuilder) SetStructuredEvent(ctx context.Context, format format.Format, ev io.Reader) error {
|
||||
var buf bytes.Buffer
|
||||
_, err := io.Copy(&buf, ev)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return format.Unmarshal(buf.Bytes(), (*event.Event)(b))
|
||||
}
|
||||
|
||||
func (b *messageToEventBuilder) Start(ctx context.Context) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (b *messageToEventBuilder) End(ctx context.Context) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (b *messageToEventBuilder) SetData(data io.Reader) error {
|
||||
buf, ok := data.(*bytes.Buffer)
|
||||
if !ok {
|
||||
buf = new(bytes.Buffer)
|
||||
_, err := io.Copy(buf, data)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
if buf.Len() > 0 {
|
||||
b.DataEncoded = buf.Bytes()
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (b *messageToEventBuilder) SetAttribute(attribute spec.Attribute, value interface{}) error {
|
||||
if value == nil {
|
||||
_ = attribute.Delete(b.Context)
|
||||
return nil
|
||||
}
|
||||
// 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 event.CloudEventsVersionV03:
|
||||
b.Context = b.Context.AsV03()
|
||||
case event.CloudEventsVersionV1:
|
||||
b.Context = b.Context.AsV1()
|
||||
default:
|
||||
return fmt.Errorf("unrecognized event version %s", str)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
return attribute.Set(b.Context, value)
|
||||
}
|
||||
|
||||
func (b *messageToEventBuilder) SetExtension(name string, value interface{}) error {
|
||||
if value == nil {
|
||||
return b.Context.SetExtension(name, nil)
|
||||
}
|
||||
value, err := types.Validate(value)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return b.Context.SetExtension(name, value)
|
||||
}
|
42
src/vendor/github.com/cloudevents/sdk-go/v2/binding/transformer.go
generated
vendored
Normal file
42
src/vendor/github.com/cloudevents/sdk-go/v2/binding/transformer.go
generated
vendored
Normal file
@ -0,0 +1,42 @@
|
||||
/*
|
||||
Copyright 2021 The CloudEvents Authors
|
||||
SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
package binding
|
||||
|
||||
// Transformer is an interface that implements a transformation
|
||||
// process while transferring the event from the Message
|
||||
// implementation to the provided encoder
|
||||
//
|
||||
// When a write function (binding.Write, binding.ToEvent, buffering.CopyMessage, etc.)
|
||||
// takes Transformer(s) as parameter, it eventually converts the message to a form
|
||||
// which correctly implements MessageMetadataReader, in order to guarantee that transformation
|
||||
// is applied
|
||||
type Transformer interface {
|
||||
Transform(MessageMetadataReader, MessageMetadataWriter) error
|
||||
}
|
||||
|
||||
// TransformerFunc is a type alias to implement a Transformer through a function pointer
|
||||
type TransformerFunc func(MessageMetadataReader, MessageMetadataWriter) error
|
||||
|
||||
func (t TransformerFunc) Transform(r MessageMetadataReader, w MessageMetadataWriter) error {
|
||||
return t(r, w)
|
||||
}
|
||||
|
||||
var _ Transformer = (TransformerFunc)(nil)
|
||||
|
||||
// Transformers is a utility alias to run several Transformer
|
||||
type Transformers []Transformer
|
||||
|
||||
func (t Transformers) Transform(r MessageMetadataReader, w MessageMetadataWriter) error {
|
||||
for _, transformer := range t {
|
||||
err := transformer.Transform(r, w)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
var _ Transformer = (Transformers)(nil)
|
179
src/vendor/github.com/cloudevents/sdk-go/v2/binding/write.go
generated
vendored
Normal file
179
src/vendor/github.com/cloudevents/sdk-go/v2/binding/write.go
generated
vendored
Normal file
@ -0,0 +1,179 @@
|
||||
/*
|
||||
Copyright 2021 The CloudEvents Authors
|
||||
SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
package binding
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"github.com/cloudevents/sdk-go/v2/event"
|
||||
)
|
||||
|
||||
type eventEncodingKey int
|
||||
|
||||
const (
|
||||
skipDirectStructuredEncoding eventEncodingKey = iota
|
||||
skipDirectBinaryEncoding
|
||||
preferredEventEncoding
|
||||
)
|
||||
|
||||
// DirectWrite invokes the encoders. structuredWriter and binaryWriter could be nil if the protocol doesn't support it.
|
||||
// transformers can be nil and this function guarantees that they are invoked only once during the encoding process.
|
||||
// This function MUST be invoked only if message.ReadEncoding() == EncodingBinary or message.ReadEncoding() == EncodingStructured
|
||||
//
|
||||
// Returns:
|
||||
// * EncodingStructured, nil if message is correctly encoded in structured encoding
|
||||
// * EncodingBinary, nil if message is correctly encoded in binary encoding
|
||||
// * EncodingStructured, err if message was structured but error happened during the encoding
|
||||
// * EncodingBinary, err if message was binary but error happened during the encoding
|
||||
// * EncodingUnknown, ErrUnknownEncoding if message is not a structured or a binary Message
|
||||
func DirectWrite(
|
||||
ctx context.Context,
|
||||
message MessageReader,
|
||||
structuredWriter StructuredWriter,
|
||||
binaryWriter BinaryWriter,
|
||||
transformers ...Transformer,
|
||||
) (Encoding, error) {
|
||||
if structuredWriter != nil && len(transformers) == 0 && !GetOrDefaultFromCtx(ctx, skipDirectStructuredEncoding, false).(bool) {
|
||||
if err := message.ReadStructured(ctx, structuredWriter); err == nil {
|
||||
return EncodingStructured, nil
|
||||
} else if err != ErrNotStructured {
|
||||
return EncodingStructured, err
|
||||
}
|
||||
}
|
||||
|
||||
if binaryWriter != nil && !GetOrDefaultFromCtx(ctx, skipDirectBinaryEncoding, false).(bool) && message.ReadEncoding() == EncodingBinary {
|
||||
return EncodingBinary, writeBinaryWithTransformer(ctx, message, binaryWriter, transformers)
|
||||
}
|
||||
|
||||
return EncodingUnknown, ErrUnknownEncoding
|
||||
}
|
||||
|
||||
// Write executes the full algorithm to encode a Message using transformers:
|
||||
// 1. It first tries direct encoding using DirectWrite
|
||||
// 2. If no direct encoding is possible, it uses ToEvent to generate an Event representation
|
||||
// 3. From the Event, the message is encoded back to the provided structured or binary encoders
|
||||
// You can tweak the encoding process using the context decorators WithForceStructured, WithForceStructured, etc.
|
||||
// transformers can be nil and this function guarantees that they are invoked only once during the encoding process.
|
||||
// Returns:
|
||||
// * EncodingStructured, nil if message is correctly encoded in structured encoding
|
||||
// * EncodingBinary, nil if message is correctly encoded in binary encoding
|
||||
// * EncodingUnknown, ErrUnknownEncoding if message.ReadEncoding() == EncodingUnknown
|
||||
// * _, err if error happened during the encoding
|
||||
func Write(
|
||||
ctx context.Context,
|
||||
message MessageReader,
|
||||
structuredWriter StructuredWriter,
|
||||
binaryWriter BinaryWriter,
|
||||
transformers ...Transformer,
|
||||
) (Encoding, error) {
|
||||
enc := message.ReadEncoding()
|
||||
var err error
|
||||
// Skip direct encoding if the event is an event message
|
||||
if enc != EncodingEvent {
|
||||
enc, err = DirectWrite(ctx, message, structuredWriter, binaryWriter, transformers...)
|
||||
if enc != EncodingUnknown {
|
||||
// Message directly encoded, nothing else to do here
|
||||
return enc, err
|
||||
}
|
||||
}
|
||||
|
||||
var e *event.Event
|
||||
e, err = ToEvent(ctx, message, transformers...)
|
||||
if err != nil {
|
||||
return enc, err
|
||||
}
|
||||
|
||||
message = (*EventMessage)(e)
|
||||
|
||||
if GetOrDefaultFromCtx(ctx, preferredEventEncoding, EncodingBinary).(Encoding) == EncodingStructured {
|
||||
if structuredWriter != nil {
|
||||
return EncodingStructured, message.ReadStructured(ctx, structuredWriter)
|
||||
}
|
||||
if binaryWriter != nil {
|
||||
return EncodingBinary, writeBinary(ctx, message, binaryWriter)
|
||||
}
|
||||
} else {
|
||||
if binaryWriter != nil {
|
||||
return EncodingBinary, writeBinary(ctx, message, binaryWriter)
|
||||
}
|
||||
if structuredWriter != nil {
|
||||
return EncodingStructured, message.ReadStructured(ctx, structuredWriter)
|
||||
}
|
||||
}
|
||||
|
||||
return EncodingUnknown, ErrUnknownEncoding
|
||||
}
|
||||
|
||||
// WithSkipDirectStructuredEncoding skips direct structured to structured encoding during the encoding process
|
||||
func WithSkipDirectStructuredEncoding(ctx context.Context, skip bool) context.Context {
|
||||
return context.WithValue(ctx, skipDirectStructuredEncoding, skip)
|
||||
}
|
||||
|
||||
// WithSkipDirectBinaryEncoding skips direct binary to binary encoding during the encoding process
|
||||
func WithSkipDirectBinaryEncoding(ctx context.Context, skip bool) context.Context {
|
||||
return context.WithValue(ctx, skipDirectBinaryEncoding, skip)
|
||||
}
|
||||
|
||||
// WithPreferredEventEncoding defines the preferred encoding from event to message during the encoding process
|
||||
func WithPreferredEventEncoding(ctx context.Context, enc Encoding) context.Context {
|
||||
return context.WithValue(ctx, preferredEventEncoding, enc)
|
||||
}
|
||||
|
||||
// WithForceStructured forces structured encoding during the encoding process
|
||||
func WithForceStructured(ctx context.Context) context.Context {
|
||||
return context.WithValue(context.WithValue(ctx, preferredEventEncoding, EncodingStructured), skipDirectBinaryEncoding, true)
|
||||
}
|
||||
|
||||
// WithForceBinary forces binary encoding during the encoding process
|
||||
func WithForceBinary(ctx context.Context) context.Context {
|
||||
return context.WithValue(context.WithValue(ctx, preferredEventEncoding, EncodingBinary), skipDirectStructuredEncoding, true)
|
||||
}
|
||||
|
||||
// GetOrDefaultFromCtx gets a configuration value from the provided context
|
||||
func GetOrDefaultFromCtx(ctx context.Context, key interface{}, def interface{}) interface{} {
|
||||
if val := ctx.Value(key); val != nil {
|
||||
return val
|
||||
} else {
|
||||
return def
|
||||
}
|
||||
}
|
||||
|
||||
func writeBinaryWithTransformer(
|
||||
ctx context.Context,
|
||||
message MessageReader,
|
||||
binaryWriter BinaryWriter,
|
||||
transformers Transformers,
|
||||
) error {
|
||||
err := binaryWriter.Start(ctx)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
err = message.ReadBinary(ctx, binaryWriter)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
err = transformers.Transform(message.(MessageMetadataReader), binaryWriter)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return binaryWriter.End(ctx)
|
||||
}
|
||||
|
||||
func writeBinary(
|
||||
ctx context.Context,
|
||||
message MessageReader,
|
||||
binaryWriter BinaryWriter,
|
||||
) error {
|
||||
err := binaryWriter.Start(ctx)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
err = message.ReadBinary(ctx, binaryWriter)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return binaryWriter.End(ctx)
|
||||
}
|
288
src/vendor/github.com/cloudevents/sdk-go/v2/client/client.go
generated
vendored
Normal file
288
src/vendor/github.com/cloudevents/sdk-go/v2/client/client.go
generated
vendored
Normal file
@ -0,0 +1,288 @@
|
||||
/*
|
||||
Copyright 2021 The CloudEvents Authors
|
||||
SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
package client
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
"runtime"
|
||||
"sync"
|
||||
|
||||
"go.uber.org/zap"
|
||||
|
||||
"github.com/cloudevents/sdk-go/v2/binding"
|
||||
cecontext "github.com/cloudevents/sdk-go/v2/context"
|
||||
"github.com/cloudevents/sdk-go/v2/event"
|
||||
"github.com/cloudevents/sdk-go/v2/protocol"
|
||||
)
|
||||
|
||||
// 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 event.Event) protocol.Result
|
||||
|
||||
// Request will transmit the given event over the client's configured
|
||||
// transport and return any response event.
|
||||
Request(ctx context.Context, event event.Event) (*event.Event, protocol.Result)
|
||||
|
||||
// StartReceiver will register the provided function for callback on receipt
|
||||
// of a cloudevent. It will also start the underlying protocol as it has
|
||||
// been configured.
|
||||
// This call is blocking.
|
||||
// Valid fn signatures are:
|
||||
// * func()
|
||||
// * func() error
|
||||
// * func(context.Context)
|
||||
// * func(context.Context) protocol.Result
|
||||
// * func(event.Event)
|
||||
// * func(event.Event) protocol.Result
|
||||
// * func(context.Context, event.Event)
|
||||
// * func(context.Context, event.Event) protocol.Result
|
||||
// * func(event.Event) *event.Event
|
||||
// * func(event.Event) (*event.Event, protocol.Result)
|
||||
// * func(context.Context, event.Event) *event.Event
|
||||
// * func(context.Context, event.Event) (*event.Event, protocol.Result)
|
||||
StartReceiver(ctx context.Context, fn interface{}) error
|
||||
}
|
||||
|
||||
// New produces a new client with the provided transport object and applied
|
||||
// client options.
|
||||
func New(obj interface{}, opts ...Option) (Client, error) {
|
||||
c := &ceClient{
|
||||
// Running runtime.GOMAXPROCS(0) doesn't update the value, just returns the current one
|
||||
pollGoroutines: runtime.GOMAXPROCS(0),
|
||||
observabilityService: noopObservabilityService{},
|
||||
}
|
||||
|
||||
if p, ok := obj.(protocol.Sender); ok {
|
||||
c.sender = p
|
||||
}
|
||||
if p, ok := obj.(protocol.Requester); ok {
|
||||
c.requester = p
|
||||
}
|
||||
if p, ok := obj.(protocol.Responder); ok {
|
||||
c.responder = p
|
||||
}
|
||||
if p, ok := obj.(protocol.Receiver); ok {
|
||||
c.receiver = p
|
||||
}
|
||||
if p, ok := obj.(protocol.Opener); ok {
|
||||
c.opener = p
|
||||
}
|
||||
|
||||
if err := c.applyOptions(opts...); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return c, nil
|
||||
}
|
||||
|
||||
type ceClient struct {
|
||||
sender protocol.Sender
|
||||
requester protocol.Requester
|
||||
receiver protocol.Receiver
|
||||
responder protocol.Responder
|
||||
// Optional.
|
||||
opener protocol.Opener
|
||||
|
||||
observabilityService ObservabilityService
|
||||
|
||||
inboundContextDecorators []func(context.Context, binding.Message) context.Context
|
||||
outboundContextDecorators []func(context.Context) context.Context
|
||||
invoker Invoker
|
||||
receiverMu sync.Mutex
|
||||
eventDefaulterFns []EventDefaulter
|
||||
pollGoroutines int
|
||||
blockingCallback bool
|
||||
}
|
||||
|
||||
func (c *ceClient) applyOptions(opts ...Option) error {
|
||||
for _, fn := range opts {
|
||||
if err := fn(c); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (c *ceClient) Send(ctx context.Context, e event.Event) protocol.Result {
|
||||
var err error
|
||||
if c.sender == nil {
|
||||
err = errors.New("sender not set")
|
||||
return err
|
||||
}
|
||||
|
||||
for _, f := range c.outboundContextDecorators {
|
||||
ctx = f(ctx)
|
||||
}
|
||||
|
||||
if len(c.eventDefaulterFns) > 0 {
|
||||
for _, fn := range c.eventDefaulterFns {
|
||||
e = fn(ctx, e)
|
||||
}
|
||||
}
|
||||
if err = e.Validate(); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Event has been defaulted and validated, record we are going to perform send.
|
||||
ctx, cb := c.observabilityService.RecordSendingEvent(ctx, e)
|
||||
err = c.sender.Send(ctx, (*binding.EventMessage)(&e))
|
||||
defer cb(err)
|
||||
return err
|
||||
}
|
||||
|
||||
func (c *ceClient) Request(ctx context.Context, e event.Event) (*event.Event, protocol.Result) {
|
||||
var resp *event.Event
|
||||
var err error
|
||||
|
||||
if c.requester == nil {
|
||||
err = errors.New("requester not set")
|
||||
return nil, err
|
||||
}
|
||||
for _, f := range c.outboundContextDecorators {
|
||||
ctx = f(ctx)
|
||||
}
|
||||
|
||||
if len(c.eventDefaulterFns) > 0 {
|
||||
for _, fn := range c.eventDefaulterFns {
|
||||
e = fn(ctx, e)
|
||||
}
|
||||
}
|
||||
|
||||
if err = e.Validate(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// Event has been defaulted and validated, record we are going to perform request.
|
||||
ctx, cb := c.observabilityService.RecordRequestEvent(ctx, e)
|
||||
|
||||
// If provided a requester, use it to do request/response.
|
||||
var msg binding.Message
|
||||
msg, err = c.requester.Request(ctx, (*binding.EventMessage)(&e))
|
||||
if msg != nil {
|
||||
defer func() {
|
||||
if err := msg.Finish(err); err != nil {
|
||||
cecontext.LoggerFrom(ctx).Warnw("failed calling message.Finish", zap.Error(err))
|
||||
}
|
||||
}()
|
||||
}
|
||||
if protocol.IsUndelivered(err) {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// try to turn msg into an event, it might not work and that is ok.
|
||||
if rs, rserr := binding.ToEvent(ctx, msg); rserr != nil {
|
||||
cecontext.LoggerFrom(ctx).Debugw("response: failed calling ToEvent", zap.Error(rserr), zap.Any("resp", msg))
|
||||
// If the protocol returns no error, it is an ACK on the request, but we had
|
||||
// issues turning the response into an event, so make an ACK Result and pass
|
||||
// down the ToEvent error as well.
|
||||
err = protocol.NewReceipt(true, "failed to convert response into event: %v\n%w", rserr, err)
|
||||
} else {
|
||||
resp = rs
|
||||
}
|
||||
defer cb(err, resp)
|
||||
return resp, err
|
||||
}
|
||||
|
||||
// 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 {
|
||||
ctx, cancel := context.WithCancel(ctx)
|
||||
defer cancel()
|
||||
|
||||
c.receiverMu.Lock()
|
||||
defer c.receiverMu.Unlock()
|
||||
|
||||
if c.invoker != nil {
|
||||
return fmt.Errorf("client already has a receiver")
|
||||
}
|
||||
|
||||
invoker, err := newReceiveInvoker(fn, c.observabilityService, c.inboundContextDecorators, c.eventDefaulterFns...)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if invoker.IsReceiver() && c.receiver == nil {
|
||||
return fmt.Errorf("mismatched receiver callback without protocol.Receiver supported by protocol")
|
||||
}
|
||||
if invoker.IsResponder() && c.responder == nil {
|
||||
return fmt.Errorf("mismatched receiver callback without protocol.Responder supported by protocol")
|
||||
}
|
||||
c.invoker = invoker
|
||||
|
||||
if c.responder == nil && c.receiver == nil {
|
||||
return errors.New("responder nor receiver set")
|
||||
}
|
||||
|
||||
defer func() {
|
||||
c.invoker = nil
|
||||
}()
|
||||
|
||||
// Start Polling.
|
||||
wg := sync.WaitGroup{}
|
||||
for i := 0; i < c.pollGoroutines; i++ {
|
||||
wg.Add(1)
|
||||
go func() {
|
||||
defer wg.Done()
|
||||
for {
|
||||
var msg binding.Message
|
||||
var respFn protocol.ResponseFn
|
||||
var err error
|
||||
|
||||
if c.responder != nil {
|
||||
msg, respFn, err = c.responder.Respond(ctx)
|
||||
} else if c.receiver != nil {
|
||||
msg, err = c.receiver.Receive(ctx)
|
||||
respFn = noRespFn
|
||||
}
|
||||
|
||||
if err == io.EOF { // Normal close
|
||||
return
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
cecontext.LoggerFrom(ctx).Warn("Error while receiving a message: ", err)
|
||||
continue
|
||||
}
|
||||
|
||||
callback := func() {
|
||||
if err := c.invoker.Invoke(ctx, msg, respFn); err != nil {
|
||||
cecontext.LoggerFrom(ctx).Warn("Error while handling a message: ", err)
|
||||
}
|
||||
}
|
||||
|
||||
if c.blockingCallback {
|
||||
callback()
|
||||
} else {
|
||||
// Do not block on the invoker.
|
||||
wg.Add(1)
|
||||
go func() {
|
||||
defer wg.Done()
|
||||
callback()
|
||||
}()
|
||||
}
|
||||
}
|
||||
}()
|
||||
}
|
||||
|
||||
// Start the opener, if set.
|
||||
if c.opener != nil {
|
||||
if err = c.opener.OpenInbound(ctx); err != nil {
|
||||
err = fmt.Errorf("error while opening the inbound connection: %w", err)
|
||||
cancel()
|
||||
}
|
||||
}
|
||||
|
||||
wg.Wait()
|
||||
|
||||
return err
|
||||
}
|
||||
|
||||
// noRespFn is used to simply forward the protocol.Result for receivers that aren't responders
|
||||
func noRespFn(_ context.Context, _ binding.Message, r protocol.Result, _ ...binding.Transformer) error {
|
||||
return r
|
||||
}
|
35
src/vendor/github.com/cloudevents/sdk-go/v2/client/client_http.go
generated
vendored
Normal file
35
src/vendor/github.com/cloudevents/sdk-go/v2/client/client_http.go
generated
vendored
Normal file
@ -0,0 +1,35 @@
|
||||
/*
|
||||
Copyright 2021 The CloudEvents Authors
|
||||
SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
package client
|
||||
|
||||
import (
|
||||
"github.com/cloudevents/sdk-go/v2/protocol/http"
|
||||
)
|
||||
|
||||
// NewHTTP provides the good defaults for the common case using an HTTP
|
||||
// Protocol client.
|
||||
// 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.
|
||||
func NewHTTP(opts ...http.Option) (Client, error) {
|
||||
p, err := http.New(opts...)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
c, err := New(p, WithTimeNow(), WithUUIDs())
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return c, nil
|
||||
}
|
||||
|
||||
// NewDefault has been replaced by NewHTTP
|
||||
// Deprecated. To get the same as NewDefault provided, please use NewHTTP with
|
||||
// the observability service passed as an option, or client.NewClientHTTP from
|
||||
// package github.com/cloudevents/sdk-go/observability/opencensus/v2/client
|
||||
var NewDefault = NewHTTP
|
12
src/vendor/github.com/cloudevents/sdk-go/v2/client/client_observed.go
generated
vendored
Normal file
12
src/vendor/github.com/cloudevents/sdk-go/v2/client/client_observed.go
generated
vendored
Normal file
@ -0,0 +1,12 @@
|
||||
/*
|
||||
Copyright 2021 The CloudEvents Authors
|
||||
SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
package client
|
||||
|
||||
// NewObserved produces a new client with the provided transport object and applied
|
||||
// client options.
|
||||
// Deprecated: This now has the same behaviour of New, and will be removed in future releases.
|
||||
// As New, you must provide the observability service to use.
|
||||
var NewObserved = New
|
57
src/vendor/github.com/cloudevents/sdk-go/v2/client/defaulters.go
generated
vendored
Normal file
57
src/vendor/github.com/cloudevents/sdk-go/v2/client/defaulters.go
generated
vendored
Normal file
@ -0,0 +1,57 @@
|
||||
/*
|
||||
Copyright 2021 The CloudEvents Authors
|
||||
SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
package client
|
||||
|
||||
import (
|
||||
"context"
|
||||
"time"
|
||||
|
||||
"github.com/cloudevents/sdk-go/v2/event"
|
||||
|
||||
"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 event.Event) event.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 event.Event) event.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 event.Event) event.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 event.Event) event.Event {
|
||||
if event.Context != nil {
|
||||
if event.DataContentType() == "" {
|
||||
event.SetDataContentType(contentType)
|
||||
}
|
||||
}
|
||||
return event
|
||||
}
|
||||
}
|
11
src/vendor/github.com/cloudevents/sdk-go/v2/client/doc.go
generated
vendored
Normal file
11
src/vendor/github.com/cloudevents/sdk-go/v2/client/doc.go
generated
vendored
Normal file
@ -0,0 +1,11 @@
|
||||
/*
|
||||
Copyright 2021 The CloudEvents Authors
|
||||
SPDX-License-Identifier: Apache-2.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
|
45
src/vendor/github.com/cloudevents/sdk-go/v2/client/http_receiver.go
generated
vendored
Normal file
45
src/vendor/github.com/cloudevents/sdk-go/v2/client/http_receiver.go
generated
vendored
Normal file
@ -0,0 +1,45 @@
|
||||
/*
|
||||
Copyright 2021 The CloudEvents Authors
|
||||
SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
package client
|
||||
|
||||
import (
|
||||
"context"
|
||||
cecontext "github.com/cloudevents/sdk-go/v2/context"
|
||||
thttp "github.com/cloudevents/sdk-go/v2/protocol/http"
|
||||
"go.uber.org/zap"
|
||||
"net/http"
|
||||
)
|
||||
|
||||
func NewHTTPReceiveHandler(ctx context.Context, p *thttp.Protocol, fn interface{}) (*EventReceiver, error) {
|
||||
invoker, err := newReceiveInvoker(fn, noopObservabilityService{}, nil) //TODO(slinkydeveloper) maybe not nil?
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &EventReceiver{
|
||||
p: p,
|
||||
invoker: invoker,
|
||||
}, nil
|
||||
}
|
||||
|
||||
type EventReceiver struct {
|
||||
p *thttp.Protocol
|
||||
invoker Invoker
|
||||
}
|
||||
|
||||
func (r *EventReceiver) ServeHTTP(rw http.ResponseWriter, req *http.Request) {
|
||||
// Prepare to handle the message if there's one (context cancellation will ensure this closes)
|
||||
go func() {
|
||||
ctx := req.Context()
|
||||
msg, respFn, err := r.p.Respond(ctx)
|
||||
if err != nil {
|
||||
cecontext.LoggerFrom(context.TODO()).Debugw("failed to call Respond", zap.Error(err))
|
||||
} else if err := r.invoker.Invoke(ctx, msg, respFn); err != nil {
|
||||
cecontext.LoggerFrom(context.TODO()).Debugw("failed to call Invoke", zap.Error(err))
|
||||
}
|
||||
}()
|
||||
r.p.ServeHTTP(rw, req)
|
||||
}
|
137
src/vendor/github.com/cloudevents/sdk-go/v2/client/invoker.go
generated
vendored
Normal file
137
src/vendor/github.com/cloudevents/sdk-go/v2/client/invoker.go
generated
vendored
Normal file
@ -0,0 +1,137 @@
|
||||
/*
|
||||
Copyright 2021 The CloudEvents Authors
|
||||
SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
package client
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
|
||||
"github.com/cloudevents/sdk-go/v2/binding"
|
||||
cecontext "github.com/cloudevents/sdk-go/v2/context"
|
||||
"github.com/cloudevents/sdk-go/v2/event"
|
||||
"github.com/cloudevents/sdk-go/v2/protocol"
|
||||
)
|
||||
|
||||
type Invoker interface {
|
||||
Invoke(context.Context, binding.Message, protocol.ResponseFn) error
|
||||
IsReceiver() bool
|
||||
IsResponder() bool
|
||||
}
|
||||
|
||||
var _ Invoker = (*receiveInvoker)(nil)
|
||||
|
||||
func newReceiveInvoker(fn interface{}, observabilityService ObservabilityService, inboundContextDecorators []func(context.Context, binding.Message) context.Context, fns ...EventDefaulter) (Invoker, error) {
|
||||
r := &receiveInvoker{
|
||||
eventDefaulterFns: fns,
|
||||
observabilityService: observabilityService,
|
||||
inboundContextDecorators: inboundContextDecorators,
|
||||
}
|
||||
|
||||
if fn, err := receiver(fn); err != nil {
|
||||
return nil, err
|
||||
} else {
|
||||
r.fn = fn
|
||||
}
|
||||
|
||||
return r, nil
|
||||
}
|
||||
|
||||
type receiveInvoker struct {
|
||||
fn *receiverFn
|
||||
observabilityService ObservabilityService
|
||||
eventDefaulterFns []EventDefaulter
|
||||
inboundContextDecorators []func(context.Context, binding.Message) context.Context
|
||||
}
|
||||
|
||||
func (r *receiveInvoker) Invoke(ctx context.Context, m binding.Message, respFn protocol.ResponseFn) (err error) {
|
||||
defer func() {
|
||||
err = m.Finish(err)
|
||||
}()
|
||||
|
||||
var respMsg binding.Message
|
||||
var result protocol.Result
|
||||
|
||||
e, eventErr := binding.ToEvent(ctx, m)
|
||||
switch {
|
||||
case eventErr != nil && r.fn.hasEventIn:
|
||||
r.observabilityService.RecordReceivedMalformedEvent(ctx, eventErr)
|
||||
return respFn(ctx, nil, protocol.NewReceipt(false, "failed to convert Message to Event: %w", eventErr))
|
||||
case r.fn != nil:
|
||||
// Check if event is valid before invoking the receiver function
|
||||
if e != nil {
|
||||
if validationErr := e.Validate(); validationErr != nil {
|
||||
r.observabilityService.RecordReceivedMalformedEvent(ctx, validationErr)
|
||||
return respFn(ctx, nil, protocol.NewReceipt(false, "validation error in incoming event: %w", validationErr))
|
||||
}
|
||||
}
|
||||
|
||||
// Let's invoke the receiver fn
|
||||
var resp *event.Event
|
||||
resp, result = func() (resp *event.Event, result protocol.Result) {
|
||||
defer func() {
|
||||
if r := recover(); r != nil {
|
||||
result = fmt.Errorf("call to Invoker.Invoke(...) has panicked: %v", r)
|
||||
cecontext.LoggerFrom(ctx).Error(result)
|
||||
}
|
||||
}()
|
||||
ctx = computeInboundContext(m, ctx, r.inboundContextDecorators)
|
||||
|
||||
var cb func(error)
|
||||
ctx, cb = r.observabilityService.RecordCallingInvoker(ctx, e)
|
||||
|
||||
resp, result = r.fn.invoke(ctx, e)
|
||||
defer cb(result)
|
||||
return
|
||||
}()
|
||||
|
||||
if respFn == nil {
|
||||
break
|
||||
}
|
||||
|
||||
// Apply the defaulter chain to the outgoing event.
|
||||
if resp != nil && len(r.eventDefaulterFns) > 0 {
|
||||
for _, fn := range r.eventDefaulterFns {
|
||||
*resp = fn(ctx, *resp)
|
||||
}
|
||||
// Validate the event conforms to the CloudEvents Spec.
|
||||
if vErr := resp.Validate(); vErr != nil {
|
||||
cecontext.LoggerFrom(ctx).Errorf("cloudevent validation failed on response event: %v", vErr)
|
||||
}
|
||||
}
|
||||
|
||||
// because binding.Message is an interface, casting a nil resp
|
||||
// here would make future comparisons to nil false
|
||||
if resp != nil {
|
||||
respMsg = (*binding.EventMessage)(resp)
|
||||
}
|
||||
}
|
||||
|
||||
if respFn == nil {
|
||||
// let the protocol ACK based on the result
|
||||
return result
|
||||
}
|
||||
|
||||
return respFn(ctx, respMsg, result)
|
||||
}
|
||||
|
||||
func (r *receiveInvoker) IsReceiver() bool {
|
||||
return !r.fn.hasEventOut
|
||||
}
|
||||
|
||||
func (r *receiveInvoker) IsResponder() bool {
|
||||
return r.fn.hasEventOut
|
||||
}
|
||||
|
||||
func computeInboundContext(message binding.Message, fallback context.Context, inboundContextDecorators []func(context.Context, binding.Message) context.Context) context.Context {
|
||||
result := fallback
|
||||
if mctx, ok := message.(binding.MessageContext); ok {
|
||||
result = cecontext.ValuesDelegating(mctx.Context(), fallback)
|
||||
}
|
||||
for _, f := range inboundContextDecorators {
|
||||
result = f(result, message)
|
||||
}
|
||||
return result
|
||||
}
|
54
src/vendor/github.com/cloudevents/sdk-go/v2/client/observability.go
generated
vendored
Normal file
54
src/vendor/github.com/cloudevents/sdk-go/v2/client/observability.go
generated
vendored
Normal file
@ -0,0 +1,54 @@
|
||||
/*
|
||||
Copyright 2021 The CloudEvents Authors
|
||||
SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
package client
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"github.com/cloudevents/sdk-go/v2/binding"
|
||||
"github.com/cloudevents/sdk-go/v2/event"
|
||||
)
|
||||
|
||||
// ObservabilityService is an interface users can implement to record metrics, create tracing spans, and plug other observability tools in the Client
|
||||
type ObservabilityService interface {
|
||||
// InboundContextDecorators is a method that returns the InboundContextDecorators that must be mounted in the Client to properly propagate some tracing informations.
|
||||
InboundContextDecorators() []func(context.Context, binding.Message) context.Context
|
||||
|
||||
// RecordReceivedMalformedEvent is invoked when an event was received but it's malformed or invalid.
|
||||
RecordReceivedMalformedEvent(ctx context.Context, err error)
|
||||
// RecordCallingInvoker is invoked before the user function is invoked.
|
||||
// The returned callback will be invoked after the user finishes to process the event with the eventual processing error
|
||||
// The error provided to the callback could be both a processing error, or a result
|
||||
RecordCallingInvoker(ctx context.Context, event *event.Event) (context.Context, func(errOrResult error))
|
||||
// RecordSendingEvent is invoked before the event is sent.
|
||||
// The returned callback will be invoked when the response is received
|
||||
// The error provided to the callback could be both a processing error, or a result
|
||||
RecordSendingEvent(ctx context.Context, event event.Event) (context.Context, func(errOrResult error))
|
||||
|
||||
// RecordRequestEvent is invoked before the event is requested.
|
||||
// The returned callback will be invoked when the response is received
|
||||
RecordRequestEvent(ctx context.Context, event event.Event) (context.Context, func(errOrResult error, event *event.Event))
|
||||
}
|
||||
|
||||
type noopObservabilityService struct{}
|
||||
|
||||
func (n noopObservabilityService) InboundContextDecorators() []func(context.Context, binding.Message) context.Context {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (n noopObservabilityService) RecordReceivedMalformedEvent(ctx context.Context, err error) {}
|
||||
|
||||
func (n noopObservabilityService) RecordCallingInvoker(ctx context.Context, event *event.Event) (context.Context, func(errOrResult error)) {
|
||||
return ctx, func(errOrResult error) {}
|
||||
}
|
||||
|
||||
func (n noopObservabilityService) RecordSendingEvent(ctx context.Context, event event.Event) (context.Context, func(errOrResult error)) {
|
||||
return ctx, func(errOrResult error) {}
|
||||
}
|
||||
|
||||
func (n noopObservabilityService) RecordRequestEvent(ctx context.Context, e event.Event) (context.Context, func(errOrResult error, event *event.Event)) {
|
||||
return ctx, func(errOrResult error, event *event.Event) {}
|
||||
}
|
128
src/vendor/github.com/cloudevents/sdk-go/v2/client/options.go
generated
vendored
Normal file
128
src/vendor/github.com/cloudevents/sdk-go/v2/client/options.go
generated
vendored
Normal file
@ -0,0 +1,128 @@
|
||||
/*
|
||||
Copyright 2021 The CloudEvents Authors
|
||||
SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
package client
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
|
||||
"github.com/cloudevents/sdk-go/v2/binding"
|
||||
)
|
||||
|
||||
// Option is the function signature required to be considered an client.Option.
|
||||
type Option func(interface{}) error
|
||||
|
||||
// WithEventDefaulter adds an event defaulter to the end of the defaulter chain.
|
||||
func WithEventDefaulter(fn EventDefaulter) Option {
|
||||
return func(i interface{}) error {
|
||||
if c, ok := i.(*ceClient); ok {
|
||||
if fn == nil {
|
||||
return fmt.Errorf("client option was given an nil event defaulter")
|
||||
}
|
||||
c.eventDefaulterFns = append(c.eventDefaulterFns, fn)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
func WithForceBinary() Option {
|
||||
return func(i interface{}) error {
|
||||
if c, ok := i.(*ceClient); ok {
|
||||
c.outboundContextDecorators = append(c.outboundContextDecorators, binding.WithForceBinary)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
func WithForceStructured() Option {
|
||||
return func(i interface{}) error {
|
||||
if c, ok := i.(*ceClient); ok {
|
||||
c.outboundContextDecorators = append(c.outboundContextDecorators, binding.WithForceStructured)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
// WithUUIDs adds DefaultIDToUUIDIfNotSet event defaulter to the end of the
|
||||
// defaulter chain.
|
||||
func WithUUIDs() Option {
|
||||
return func(i interface{}) error {
|
||||
if c, ok := i.(*ceClient); ok {
|
||||
c.eventDefaulterFns = append(c.eventDefaulterFns, DefaultIDToUUIDIfNotSet)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
// WithTimeNow adds DefaultTimeToNowIfNotSet event defaulter to the end of the
|
||||
// defaulter chain.
|
||||
func WithTimeNow() Option {
|
||||
return func(i interface{}) error {
|
||||
if c, ok := i.(*ceClient); ok {
|
||||
c.eventDefaulterFns = append(c.eventDefaulterFns, DefaultTimeToNowIfNotSet)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
// WithTracePropagation enables trace propagation via the distributed tracing
|
||||
// extension.
|
||||
// Deprecated: this is now noop and will be removed in future releases.
|
||||
// Don't use distributed tracing extension to propagate traces:
|
||||
// https://github.com/cloudevents/spec/blob/v1.0.1/extensions/distributed-tracing.md#using-the-distributed-tracing-extension
|
||||
func WithTracePropagation() Option {
|
||||
return func(i interface{}) error {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
// WithPollGoroutines configures how much goroutines should be used to
|
||||
// poll the Receiver/Responder/Protocol implementations.
|
||||
// Default value is GOMAXPROCS
|
||||
func WithPollGoroutines(pollGoroutines int) Option {
|
||||
return func(i interface{}) error {
|
||||
if c, ok := i.(*ceClient); ok {
|
||||
c.pollGoroutines = pollGoroutines
|
||||
}
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
// WithObservabilityService configures the observability service to use
|
||||
// to record traces and metrics
|
||||
func WithObservabilityService(service ObservabilityService) Option {
|
||||
return func(i interface{}) error {
|
||||
if c, ok := i.(*ceClient); ok {
|
||||
c.observabilityService = service
|
||||
c.inboundContextDecorators = append(c.inboundContextDecorators, service.InboundContextDecorators()...)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
// WithInboundContextDecorator configures a new inbound context decorator.
|
||||
// Inbound context decorators are invoked to wrap additional informations from the binding.Message
|
||||
// and propagate these informations in the context passed to the event receiver.
|
||||
func WithInboundContextDecorator(dec func(context.Context, binding.Message) context.Context) Option {
|
||||
return func(i interface{}) error {
|
||||
if c, ok := i.(*ceClient); ok {
|
||||
c.inboundContextDecorators = append(c.inboundContextDecorators, dec)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
// WithBlockingCallback makes the callback passed into StartReceiver is executed as a blocking call,
|
||||
// i.e. in each poll go routine, the next event will not be received until the callback on current event completes.
|
||||
// To make event processing serialized (no concurrency), use this option along with WithPollGoroutines(1)
|
||||
func WithBlockingCallback() Option {
|
||||
return func(i interface{}) error {
|
||||
if c, ok := i.(*ceClient); ok {
|
||||
c.blockingCallback = true
|
||||
}
|
||||
return nil
|
||||
}
|
||||
}
|
194
src/vendor/github.com/cloudevents/sdk-go/v2/client/receiver.go
generated
vendored
Normal file
194
src/vendor/github.com/cloudevents/sdk-go/v2/client/receiver.go
generated
vendored
Normal file
@ -0,0 +1,194 @@
|
||||
/*
|
||||
Copyright 2021 The CloudEvents Authors
|
||||
SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
package client
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"fmt"
|
||||
"reflect"
|
||||
|
||||
"github.com/cloudevents/sdk-go/v2/event"
|
||||
"github.com/cloudevents/sdk-go/v2/protocol"
|
||||
)
|
||||
|
||||
// ReceiveFull is the signature of a fn to be invoked for incoming cloudevents.
|
||||
type ReceiveFull func(context.Context, event.Event) protocol.Result
|
||||
|
||||
type receiverFn struct {
|
||||
numIn int
|
||||
numOut int
|
||||
fnValue reflect.Value
|
||||
|
||||
hasContextIn bool
|
||||
hasEventIn bool
|
||||
|
||||
hasEventOut bool
|
||||
hasResultOut bool
|
||||
}
|
||||
|
||||
const (
|
||||
inParamUsage = "expected a function taking either no parameters, one or more of (context.Context, event.Event) ordered"
|
||||
outParamUsage = "expected a function returning one or mode of (*event.Event, protocol.Result) ordered"
|
||||
)
|
||||
|
||||
var (
|
||||
contextType = reflect.TypeOf((*context.Context)(nil)).Elem()
|
||||
eventType = reflect.TypeOf((*event.Event)(nil)).Elem()
|
||||
eventPtrType = reflect.TypeOf((*event.Event)(nil)) // want the ptr type
|
||||
resultType = reflect.TypeOf((*protocol.Result)(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() protocol.Result
|
||||
// * func(context.Context)
|
||||
// * func(context.Context) protocol.Result
|
||||
// * func(event.Event)
|
||||
// * func(event.Event) transport.Result
|
||||
// * func(context.Context, event.Event)
|
||||
// * func(context.Context, event.Event) protocol.Result
|
||||
// * func(event.Event) *event.Event
|
||||
// * func(event.Event) (*event.Event, protocol.Result)
|
||||
// * func(context.Context, event.Event) *event.Event
|
||||
// * func(context.Context, event.Event) (*event.Event, protocol.Result)
|
||||
//
|
||||
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(),
|
||||
numOut: fnType.NumOut(),
|
||||
}
|
||||
|
||||
if err := r.validate(fnType); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return r, nil
|
||||
}
|
||||
|
||||
func (r *receiverFn) invoke(ctx context.Context, e *event.Event) (*event.Event, protocol.Result) {
|
||||
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(*e))
|
||||
}
|
||||
}
|
||||
v := r.fnValue.Call(args)
|
||||
var respOut protocol.Result
|
||||
var eOut *event.Event
|
||||
if r.numOut > 0 {
|
||||
i := 0
|
||||
if r.hasEventOut {
|
||||
if eo, ok := v[i].Interface().(*event.Event); ok {
|
||||
eOut = eo
|
||||
}
|
||||
i++ // <-- note, need to inc i.
|
||||
}
|
||||
if r.hasResultOut {
|
||||
if resp, ok := v[i].Interface().(protocol.Result); ok {
|
||||
respOut = resp
|
||||
}
|
||||
}
|
||||
}
|
||||
return eOut, respOut
|
||||
}
|
||||
|
||||
// Verifies that the inputs to a function have a valid signature
|
||||
// Valid input is to be [0, all] of
|
||||
// context.Context, event.Event in this order.
|
||||
func (r *receiverFn) validateInParamSignature(fnType reflect.Type) error {
|
||||
r.hasContextIn = false
|
||||
r.hasEventIn = false
|
||||
|
||||
switch fnType.NumIn() {
|
||||
case 2:
|
||||
// has to be (context.Context, event.Event)
|
||||
if !eventType.ConvertibleTo(fnType.In(1)) {
|
||||
return fmt.Errorf("%s; cannot convert parameter 2 to %s from event.Event", inParamUsage, fnType.In(1))
|
||||
} else {
|
||||
r.hasEventIn = true
|
||||
}
|
||||
fallthrough
|
||||
case 1:
|
||||
if !contextType.ConvertibleTo(fnType.In(0)) {
|
||||
if !eventType.ConvertibleTo(fnType.In(0)) {
|
||||
return fmt.Errorf("%s; cannot convert parameter 1 to %s from context.Context or event.Event", inParamUsage, fnType.In(0))
|
||||
} else if r.hasEventIn {
|
||||
return fmt.Errorf("%s; duplicate parameter of type event.Event", inParamUsage)
|
||||
} else {
|
||||
r.hasEventIn = 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 to be [0, all] of
|
||||
// *event.Event, transport.Result in this order
|
||||
func (r *receiverFn) validateOutParamSignature(fnType reflect.Type) error {
|
||||
r.hasEventOut = false
|
||||
r.hasResultOut = false
|
||||
|
||||
switch fnType.NumOut() {
|
||||
case 2:
|
||||
// has to be (*event.Event, transport.Result)
|
||||
if !fnType.Out(1).ConvertibleTo(resultType) {
|
||||
return fmt.Errorf("%s; cannot convert parameter 2 from %s to event.Response", outParamUsage, fnType.Out(1))
|
||||
} else {
|
||||
r.hasResultOut = true
|
||||
}
|
||||
fallthrough
|
||||
case 1:
|
||||
if !fnType.Out(0).ConvertibleTo(resultType) {
|
||||
if !fnType.Out(0).ConvertibleTo(eventPtrType) {
|
||||
return fmt.Errorf("%s; cannot convert parameter 1 from %s to *event.Event or transport.Result", outParamUsage, fnType.Out(0))
|
||||
} else {
|
||||
r.hasEventOut = true
|
||||
}
|
||||
} else if r.hasResultOut {
|
||||
return fmt.Errorf("%s; duplicate parameter of type event.Response", outParamUsage)
|
||||
} else {
|
||||
r.hasResultOut = 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
|
||||
}
|
110
src/vendor/github.com/cloudevents/sdk-go/v2/context/context.go
generated
vendored
Normal file
110
src/vendor/github.com/cloudevents/sdk-go/v2/context/context.go
generated
vendored
Normal file
@ -0,0 +1,110 @@
|
||||
/*
|
||||
Copyright 2021 The CloudEvents Authors
|
||||
SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
package context
|
||||
|
||||
import (
|
||||
"context"
|
||||
"net/url"
|
||||
"time"
|
||||
)
|
||||
|
||||
// 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 retry parameters
|
||||
type retriesKeyType struct{}
|
||||
|
||||
var retriesKey = retriesKeyType{}
|
||||
|
||||
// WithRetriesConstantBackoff returns back a new context with retries parameters using constant backoff strategy.
|
||||
// MaxTries is the maximum number for retries and delay is the time interval between retries
|
||||
func WithRetriesConstantBackoff(ctx context.Context, delay time.Duration, maxTries int) context.Context {
|
||||
return WithRetryParams(ctx, &RetryParams{
|
||||
Strategy: BackoffStrategyConstant,
|
||||
Period: delay,
|
||||
MaxTries: maxTries,
|
||||
})
|
||||
}
|
||||
|
||||
// WithRetriesLinearBackoff returns back a new context with retries parameters using linear backoff strategy.
|
||||
// MaxTries is the maximum number for retries and delay*tries is the time interval between retries
|
||||
func WithRetriesLinearBackoff(ctx context.Context, delay time.Duration, maxTries int) context.Context {
|
||||
return WithRetryParams(ctx, &RetryParams{
|
||||
Strategy: BackoffStrategyLinear,
|
||||
Period: delay,
|
||||
MaxTries: maxTries,
|
||||
})
|
||||
}
|
||||
|
||||
// WithRetriesExponentialBackoff returns back a new context with retries parameters using exponential backoff strategy.
|
||||
// MaxTries is the maximum number for retries and period is the amount of time to wait, used as `period * 2^retries`.
|
||||
func WithRetriesExponentialBackoff(ctx context.Context, period time.Duration, maxTries int) context.Context {
|
||||
return WithRetryParams(ctx, &RetryParams{
|
||||
Strategy: BackoffStrategyExponential,
|
||||
Period: period,
|
||||
MaxTries: maxTries,
|
||||
})
|
||||
}
|
||||
|
||||
// WithRetryParams returns back a new context with retries parameters.
|
||||
func WithRetryParams(ctx context.Context, rp *RetryParams) context.Context {
|
||||
return context.WithValue(ctx, retriesKey, rp)
|
||||
}
|
||||
|
||||
// RetriesFrom looks in the given context and returns the retries parameters if found.
|
||||
// Otherwise returns the default retries configuration (ie. no retries).
|
||||
func RetriesFrom(ctx context.Context) *RetryParams {
|
||||
c := ctx.Value(retriesKey)
|
||||
if c != nil {
|
||||
if s, ok := c.(*RetryParams); ok {
|
||||
return s
|
||||
}
|
||||
}
|
||||
return &DefaultRetryParams
|
||||
}
|
25
src/vendor/github.com/cloudevents/sdk-go/v2/context/delegating.go
generated
vendored
Normal file
25
src/vendor/github.com/cloudevents/sdk-go/v2/context/delegating.go
generated
vendored
Normal file
@ -0,0 +1,25 @@
|
||||
package context
|
||||
|
||||
import "context"
|
||||
|
||||
type valuesDelegating struct {
|
||||
context.Context
|
||||
parent context.Context
|
||||
}
|
||||
|
||||
// ValuesDelegating wraps a child and parent context. It will perform Value()
|
||||
// lookups first on the child, and then fall back to the child. All other calls
|
||||
// go solely to the child context.
|
||||
func ValuesDelegating(child, parent context.Context) context.Context {
|
||||
return &valuesDelegating{
|
||||
Context: child,
|
||||
parent: parent,
|
||||
}
|
||||
}
|
||||
|
||||
func (c *valuesDelegating) Value(key interface{}) interface{} {
|
||||
if val := c.Context.Value(key); val != nil {
|
||||
return val
|
||||
}
|
||||
return c.parent.Value(key)
|
||||
}
|
10
src/vendor/github.com/cloudevents/sdk-go/v2/context/doc.go
generated
vendored
Normal file
10
src/vendor/github.com/cloudevents/sdk-go/v2/context/doc.go
generated
vendored
Normal file
@ -0,0 +1,10 @@
|
||||
/*
|
||||
Copyright 2021 The CloudEvents Authors
|
||||
SPDX-License-Identifier: Apache-2.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
|
48
src/vendor/github.com/cloudevents/sdk-go/v2/context/logger.go
generated
vendored
Normal file
48
src/vendor/github.com/cloudevents/sdk-go/v2/context/logger.go
generated
vendored
Normal file
@ -0,0 +1,48 @@
|
||||
/*
|
||||
Copyright 2021 The CloudEvents Authors
|
||||
SPDX-License-Identifier: Apache-2.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
|
||||
}
|
76
src/vendor/github.com/cloudevents/sdk-go/v2/context/retry.go
generated
vendored
Normal file
76
src/vendor/github.com/cloudevents/sdk-go/v2/context/retry.go
generated
vendored
Normal file
@ -0,0 +1,76 @@
|
||||
/*
|
||||
Copyright 2021 The CloudEvents Authors
|
||||
SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
package context
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"math"
|
||||
"time"
|
||||
)
|
||||
|
||||
type BackoffStrategy string
|
||||
|
||||
const (
|
||||
BackoffStrategyNone = "none"
|
||||
BackoffStrategyConstant = "constant"
|
||||
BackoffStrategyLinear = "linear"
|
||||
BackoffStrategyExponential = "exponential"
|
||||
)
|
||||
|
||||
var DefaultRetryParams = RetryParams{Strategy: BackoffStrategyNone}
|
||||
|
||||
// RetryParams holds parameters applied to retries
|
||||
type RetryParams struct {
|
||||
// Strategy is the backoff strategy to applies between retries
|
||||
Strategy BackoffStrategy
|
||||
|
||||
// MaxTries is the maximum number of times to retry request before giving up
|
||||
MaxTries int
|
||||
|
||||
// Period is
|
||||
// - for none strategy: no delay
|
||||
// - for constant strategy: the delay interval between retries
|
||||
// - for linear strategy: interval between retries = Period * retries
|
||||
// - for exponential strategy: interval between retries = Period * retries^2
|
||||
Period time.Duration
|
||||
}
|
||||
|
||||
// BackoffFor tries will return the time duration that should be used for this
|
||||
// current try count.
|
||||
// `tries` is assumed to be the number of times the caller has already retried.
|
||||
func (r *RetryParams) BackoffFor(tries int) time.Duration {
|
||||
switch r.Strategy {
|
||||
case BackoffStrategyConstant:
|
||||
return r.Period
|
||||
case BackoffStrategyLinear:
|
||||
return r.Period * time.Duration(tries)
|
||||
case BackoffStrategyExponential:
|
||||
exp := math.Exp2(float64(tries))
|
||||
return r.Period * time.Duration(exp)
|
||||
case BackoffStrategyNone:
|
||||
fallthrough // default
|
||||
default:
|
||||
return r.Period
|
||||
}
|
||||
}
|
||||
|
||||
// Backoff is a blocking call to wait for the correct amount of time for the retry.
|
||||
// `tries` is assumed to be the number of times the caller has already retried.
|
||||
func (r *RetryParams) Backoff(ctx context.Context, tries int) error {
|
||||
if tries > r.MaxTries {
|
||||
return errors.New("too many retries")
|
||||
}
|
||||
ticker := time.NewTicker(r.BackoffFor(tries))
|
||||
select {
|
||||
case <-ctx.Done():
|
||||
ticker.Stop()
|
||||
return errors.New("context has been cancelled")
|
||||
case <-ticker.C:
|
||||
ticker.Stop()
|
||||
}
|
||||
return nil
|
||||
}
|
47
src/vendor/github.com/cloudevents/sdk-go/v2/event/content_type.go
generated
vendored
Normal file
47
src/vendor/github.com/cloudevents/sdk-go/v2/event/content_type.go
generated
vendored
Normal file
@ -0,0 +1,47 @@
|
||||
/*
|
||||
Copyright 2021 The CloudEvents Authors
|
||||
SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
package event
|
||||
|
||||
const (
|
||||
TextPlain = "text/plain"
|
||||
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
|
||||
}
|
||||
|
||||
// StringOfTextPlain returns a string pointer to "text/plain"
|
||||
func StringOfTextPlain() *string {
|
||||
a := TextPlain
|
||||
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
|
||||
}
|
16
src/vendor/github.com/cloudevents/sdk-go/v2/event/data_content_encoding.go
generated
vendored
Normal file
16
src/vendor/github.com/cloudevents/sdk-go/v2/event/data_content_encoding.go
generated
vendored
Normal file
@ -0,0 +1,16 @@
|
||||
/*
|
||||
Copyright 2021 The CloudEvents Authors
|
||||
SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
package event
|
||||
|
||||
const (
|
||||
Base64 = "base64"
|
||||
)
|
||||
|
||||
// StringOfBase64 returns a string pointer to "Base64"
|
||||
func StringOfBase64() *string {
|
||||
a := Base64
|
||||
return &a
|
||||
}
|
78
src/vendor/github.com/cloudevents/sdk-go/v2/event/datacodec/codec.go
generated
vendored
Normal file
78
src/vendor/github.com/cloudevents/sdk-go/v2/event/datacodec/codec.go
generated
vendored
Normal file
@ -0,0 +1,78 @@
|
||||
/*
|
||||
Copyright 2021 The CloudEvents Authors
|
||||
SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
package datacodec
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
|
||||
"github.com/cloudevents/sdk-go/v2/event/datacodec/json"
|
||||
"github.com/cloudevents/sdk-go/v2/event/datacodec/text"
|
||||
"github.com/cloudevents/sdk-go/v2/event/datacodec/xml"
|
||||
)
|
||||
|
||||
// Decoder is the expected function signature for decoding `in` to `out`.
|
||||
// If Event sent the payload as base64, Decoder assumes that `in` is the
|
||||
// decoded base64 byte array.
|
||||
type Decoder func(ctx context.Context, in []byte, 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 []byte, 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) {
|
||||
if fn, ok := encoder[contentType]; ok {
|
||||
return fn(ctx, in)
|
||||
}
|
||||
return nil, fmt.Errorf("[encode] unsupported content type: %q", contentType)
|
||||
}
|
10
src/vendor/github.com/cloudevents/sdk-go/v2/event/datacodec/doc.go
generated
vendored
Normal file
10
src/vendor/github.com/cloudevents/sdk-go/v2/event/datacodec/doc.go
generated
vendored
Normal file
@ -0,0 +1,10 @@
|
||||
/*
|
||||
Copyright 2021 The CloudEvents Authors
|
||||
SPDX-License-Identifier: Apache-2.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
|
56
src/vendor/github.com/cloudevents/sdk-go/v2/event/datacodec/json/data.go
generated
vendored
Normal file
56
src/vendor/github.com/cloudevents/sdk-go/v2/event/datacodec/json/data.go
generated
vendored
Normal file
@ -0,0 +1,56 @@
|
||||
/*
|
||||
Copyright 2021 The CloudEvents Authors
|
||||
SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
package json
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"reflect"
|
||||
)
|
||||
|
||||
// Decode takes `in` as []byte.
|
||||
// If Event sent the payload as base64, Decoder assumes that `in` is the
|
||||
// decoded base64 byte array.
|
||||
func Decode(ctx context.Context, in []byte, out interface{}) error {
|
||||
if in == nil {
|
||||
return nil
|
||||
}
|
||||
if out == nil {
|
||||
return fmt.Errorf("out is nil")
|
||||
}
|
||||
|
||||
if err := json.Unmarshal(in, out); err != nil {
|
||||
return fmt.Errorf("[json] found bytes \"%s\", but failed to unmarshal: %s", string(in), 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) {
|
||||
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)
|
||||
}
|
9
src/vendor/github.com/cloudevents/sdk-go/v2/event/datacodec/json/doc.go
generated
vendored
Normal file
9
src/vendor/github.com/cloudevents/sdk-go/v2/event/datacodec/json/doc.go
generated
vendored
Normal file
@ -0,0 +1,9 @@
|
||||
/*
|
||||
Copyright 2021 The CloudEvents Authors
|
||||
SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
/*
|
||||
Package json holds the encoder/decoder implementation for `application/json`.
|
||||
*/
|
||||
package json
|
30
src/vendor/github.com/cloudevents/sdk-go/v2/event/datacodec/text/data.go
generated
vendored
Normal file
30
src/vendor/github.com/cloudevents/sdk-go/v2/event/datacodec/text/data.go
generated
vendored
Normal file
@ -0,0 +1,30 @@
|
||||
/*
|
||||
Copyright 2021 The CloudEvents Authors
|
||||
SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
package text
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
)
|
||||
|
||||
// Text codec converts []byte or string to string and vice-versa.
|
||||
|
||||
func Decode(_ context.Context, in []byte, out interface{}) error {
|
||||
p, _ := out.(*string)
|
||||
if p == nil {
|
||||
return fmt.Errorf("text.Decode out: want *string, got %T", out)
|
||||
}
|
||||
*p = string(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
|
||||
}
|
9
src/vendor/github.com/cloudevents/sdk-go/v2/event/datacodec/text/doc.go
generated
vendored
Normal file
9
src/vendor/github.com/cloudevents/sdk-go/v2/event/datacodec/text/doc.go
generated
vendored
Normal file
@ -0,0 +1,9 @@
|
||||
/*
|
||||
Copyright 2021 The CloudEvents Authors
|
||||
SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
/*
|
||||
Package text holds the encoder/decoder implementation for `text/plain`.
|
||||
*/
|
||||
package text
|
40
src/vendor/github.com/cloudevents/sdk-go/v2/event/datacodec/xml/data.go
generated
vendored
Normal file
40
src/vendor/github.com/cloudevents/sdk-go/v2/event/datacodec/xml/data.go
generated
vendored
Normal file
@ -0,0 +1,40 @@
|
||||
/*
|
||||
Copyright 2021 The CloudEvents Authors
|
||||
SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
package xml
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/xml"
|
||||
"fmt"
|
||||
)
|
||||
|
||||
// Decode takes `in` as []byte.
|
||||
// If Event sent the payload as base64, Decoder assumes that `in` is the
|
||||
// decoded base64 byte array.
|
||||
func Decode(ctx context.Context, in []byte, out interface{}) error {
|
||||
if in == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
if err := xml.Unmarshal(in, out); err != nil {
|
||||
return fmt.Errorf("[xml] found bytes, but failed to unmarshal: %s %s", err.Error(), string(in))
|
||||
}
|
||||
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) {
|
||||
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)
|
||||
}
|
9
src/vendor/github.com/cloudevents/sdk-go/v2/event/datacodec/xml/doc.go
generated
vendored
Normal file
9
src/vendor/github.com/cloudevents/sdk-go/v2/event/datacodec/xml/doc.go
generated
vendored
Normal file
@ -0,0 +1,9 @@
|
||||
/*
|
||||
Copyright 2021 The CloudEvents Authors
|
||||
SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
/*
|
||||
Package xml holds the encoder/decoder implementation for `application/xml`.
|
||||
*/
|
||||
package xml
|
9
src/vendor/github.com/cloudevents/sdk-go/v2/event/doc.go
generated
vendored
Normal file
9
src/vendor/github.com/cloudevents/sdk-go/v2/event/doc.go
generated
vendored
Normal file
@ -0,0 +1,9 @@
|
||||
/*
|
||||
Copyright 2021 The CloudEvents Authors
|
||||
SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
/*
|
||||
Package event provides primitives to work with CloudEvents specification: https://github.com/cloudevents/spec.
|
||||
*/
|
||||
package event
|
126
src/vendor/github.com/cloudevents/sdk-go/v2/event/event.go
generated
vendored
Normal file
126
src/vendor/github.com/cloudevents/sdk-go/v2/event/event.go
generated
vendored
Normal file
@ -0,0 +1,126 @@
|
||||
/*
|
||||
Copyright 2021 The CloudEvents Authors
|
||||
SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
package event
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/json"
|
||||
"strings"
|
||||
)
|
||||
|
||||
// Event represents the canonical representation of a CloudEvent.
|
||||
type Event struct {
|
||||
Context EventContext
|
||||
DataEncoded []byte
|
||||
// DataBase64 indicates if the event, when serialized, represents
|
||||
// the data field using the base64 encoding.
|
||||
// In v0.3, this field is superseded by DataContentEncoding
|
||||
DataBase64 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)
|
||||
}
|
||||
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
|
||||
if len(version) >= 1 {
|
||||
specVersion = version[0]
|
||||
}
|
||||
e := &Event{}
|
||||
e.SetSpecVersion(specVersion)
|
||||
return *e
|
||||
}
|
||||
|
||||
// ExtensionAs is 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)
|
||||
}
|
||||
|
||||
// String returns a pretty-printed representation of the Event.
|
||||
func (e Event) String() string {
|
||||
b := strings.Builder{}
|
||||
|
||||
b.WriteString(e.Context.String())
|
||||
|
||||
if e.DataEncoded != nil {
|
||||
if e.DataBase64 {
|
||||
b.WriteString("Data (binary),\n ")
|
||||
} else {
|
||||
b.WriteString("Data,\n ")
|
||||
}
|
||||
switch e.DataMediaType() {
|
||||
case ApplicationJSON:
|
||||
var prettyJSON bytes.Buffer
|
||||
err := json.Indent(&prettyJSON, e.DataEncoded, " ", " ")
|
||||
if err != nil {
|
||||
b.Write(e.DataEncoded)
|
||||
} else {
|
||||
b.Write(prettyJSON.Bytes())
|
||||
}
|
||||
default:
|
||||
b.Write(e.DataEncoded)
|
||||
}
|
||||
b.WriteString("\n")
|
||||
}
|
||||
|
||||
return b.String()
|
||||
}
|
||||
|
||||
func (e Event) Clone() Event {
|
||||
out := Event{}
|
||||
out.Context = e.Context.Clone()
|
||||
out.DataEncoded = cloneBytes(e.DataEncoded)
|
||||
out.DataBase64 = e.DataBase64
|
||||
out.FieldErrors = e.cloneFieldErrors()
|
||||
return out
|
||||
}
|
||||
|
||||
func cloneBytes(in []byte) []byte {
|
||||
if in == nil {
|
||||
return nil
|
||||
}
|
||||
out := make([]byte, len(in))
|
||||
copy(out, in)
|
||||
return out
|
||||
}
|
||||
|
||||
func (e Event) cloneFieldErrors() map[string]error {
|
||||
if e.FieldErrors == nil {
|
||||
return nil
|
||||
}
|
||||
newFE := make(map[string]error, len(e.FieldErrors))
|
||||
for k, v := range e.FieldErrors {
|
||||
newFE[k] = v
|
||||
}
|
||||
return newFE
|
||||
}
|
118
src/vendor/github.com/cloudevents/sdk-go/v2/event/event_data.go
generated
vendored
Normal file
118
src/vendor/github.com/cloudevents/sdk-go/v2/event/event_data.go
generated
vendored
Normal file
@ -0,0 +1,118 @@
|
||||
/*
|
||||
Copyright 2021 The CloudEvents Authors
|
||||
SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
package event
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/base64"
|
||||
"fmt"
|
||||
"strconv"
|
||||
|
||||
"github.com/cloudevents/sdk-go/v2/event/datacodec"
|
||||
)
|
||||
|
||||
// SetData encodes the given payload with the given content type.
|
||||
// If the provided payload is a byte array, when marshalled to json it will be encoded as base64.
|
||||
// If the provided payload is different from byte array, datacodec.Encode is invoked to attempt a
|
||||
// marshalling to byte array.
|
||||
func (e *Event) SetData(contentType string, obj interface{}) error {
|
||||
e.SetDataContentType(contentType)
|
||||
|
||||
if e.SpecVersion() != CloudEventsVersionV1 {
|
||||
return e.legacySetData(obj)
|
||||
}
|
||||
|
||||
// Version 1.0 and above.
|
||||
switch obj := obj.(type) {
|
||||
case []byte:
|
||||
e.DataEncoded = obj
|
||||
e.DataBase64 = true
|
||||
default:
|
||||
data, err := datacodec.Encode(context.Background(), e.DataMediaType(), obj)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
e.DataEncoded = data
|
||||
e.DataBase64 = false
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// Deprecated: Delete when we do not have to support Spec v0.3.
|
||||
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.DataEncoded = buf
|
||||
e.DataBase64 = false
|
||||
} else {
|
||||
data, err := datacodec.Encode(context.Background(), e.DataMediaType(), obj)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
e.DataEncoded = data
|
||||
e.DataBase64 = false
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
const (
|
||||
quotes = `"'`
|
||||
)
|
||||
|
||||
func (e Event) Data() []byte {
|
||||
return e.DataEncoded
|
||||
}
|
||||
|
||||
// DataAs attempts to populate the provided data object with the event payload.
|
||||
// obj should be a pointer type.
|
||||
func (e Event) DataAs(obj interface{}) error {
|
||||
data := e.Data()
|
||||
|
||||
if len(data) == 0 {
|
||||
// No data.
|
||||
return nil
|
||||
}
|
||||
|
||||
if e.SpecVersion() != CloudEventsVersionV1 {
|
||||
var err error
|
||||
if data, err = e.legacyConvertData(data); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
return datacodec.Decode(context.Background(), e.DataMediaType(), data, obj)
|
||||
}
|
||||
|
||||
func (e Event) legacyConvertData(data []byte) ([]byte, error) {
|
||||
if e.Context.DeprecatedGetDataContentEncoding() == Base64 {
|
||||
var bs []byte
|
||||
// test to see if we need to unquote the data.
|
||||
if data[0] == quotes[0] || data[0] == quotes[1] {
|
||||
str, err := strconv.Unquote(string(data))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
bs = []byte(str)
|
||||
} else {
|
||||
bs = data
|
||||
}
|
||||
|
||||
buf := make([]byte, base64.StdEncoding.DecodedLen(len(bs)))
|
||||
n, err := base64.StdEncoding.Decode(buf, bs)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to decode data from base64: %s", err.Error())
|
||||
}
|
||||
data = buf[:n]
|
||||
}
|
||||
|
||||
return data, nil
|
||||
}
|
102
src/vendor/github.com/cloudevents/sdk-go/v2/event/event_interface.go
generated
vendored
Normal file
102
src/vendor/github.com/cloudevents/sdk-go/v2/event/event_interface.go
generated
vendored
Normal file
@ -0,0 +1,102 @@
|
||||
/*
|
||||
Copyright 2021 The CloudEvents Authors
|
||||
SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
package event
|
||||
|
||||
import (
|
||||
"time"
|
||||
)
|
||||
|
||||
// EventReader 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{}
|
||||
|
||||
// ExtensionAs returns event.Context.ExtensionAs(name, obj).
|
||||
//
|
||||
// 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"])
|
||||
//
|
||||
ExtensionAs(string, interface{}) error
|
||||
|
||||
// Data Attribute
|
||||
|
||||
// Data returns the raw data buffer
|
||||
// If the event was encoded with base64 encoding, Data returns the already decoded
|
||||
// byte array
|
||||
Data() []byte
|
||||
|
||||
// DataAs attempts to populate the provided data object with the event payload.
|
||||
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 given content type.
|
||||
// If the provided payload is a byte array, when marshalled to json it will be encoded as base64.
|
||||
// If the provided payload is different from byte array, datacodec.Encode is invoked to attempt a
|
||||
// marshalling to byte array.
|
||||
SetData(string, interface{}) error
|
||||
}
|
203
src/vendor/github.com/cloudevents/sdk-go/v2/event/event_marshal.go
generated
vendored
Normal file
203
src/vendor/github.com/cloudevents/sdk-go/v2/event/event_marshal.go
generated
vendored
Normal file
@ -0,0 +1,203 @@
|
||||
/*
|
||||
Copyright 2021 The CloudEvents Authors
|
||||
SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
package event
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/base64"
|
||||
"fmt"
|
||||
"io"
|
||||
"strings"
|
||||
|
||||
jsoniter "github.com/json-iterator/go"
|
||||
)
|
||||
|
||||
// WriteJson writes the in event in the provided writer.
|
||||
// Note: this function assumes the input event is valid.
|
||||
func WriteJson(in *Event, writer io.Writer) error {
|
||||
stream := jsoniter.ConfigFastest.BorrowStream(writer)
|
||||
defer jsoniter.ConfigFastest.ReturnStream(stream)
|
||||
stream.WriteObjectStart()
|
||||
|
||||
var ext map[string]interface{}
|
||||
var dct *string
|
||||
var isBase64 bool
|
||||
|
||||
// Write the context (without the extensions)
|
||||
switch eventContext := in.Context.(type) {
|
||||
case *EventContextV03:
|
||||
// Set a bunch of variables we need later
|
||||
ext = eventContext.Extensions
|
||||
dct = eventContext.DataContentType
|
||||
|
||||
stream.WriteObjectField("specversion")
|
||||
stream.WriteString(CloudEventsVersionV03)
|
||||
stream.WriteMore()
|
||||
|
||||
stream.WriteObjectField("id")
|
||||
stream.WriteString(eventContext.ID)
|
||||
stream.WriteMore()
|
||||
|
||||
stream.WriteObjectField("source")
|
||||
stream.WriteString(eventContext.Source.String())
|
||||
stream.WriteMore()
|
||||
|
||||
stream.WriteObjectField("type")
|
||||
stream.WriteString(eventContext.Type)
|
||||
|
||||
if eventContext.Subject != nil {
|
||||
stream.WriteMore()
|
||||
stream.WriteObjectField("subject")
|
||||
stream.WriteString(*eventContext.Subject)
|
||||
}
|
||||
|
||||
if eventContext.DataContentEncoding != nil {
|
||||
isBase64 = true
|
||||
stream.WriteMore()
|
||||
stream.WriteObjectField("datacontentencoding")
|
||||
stream.WriteString(*eventContext.DataContentEncoding)
|
||||
}
|
||||
|
||||
if eventContext.DataContentType != nil {
|
||||
stream.WriteMore()
|
||||
stream.WriteObjectField("datacontenttype")
|
||||
stream.WriteString(*eventContext.DataContentType)
|
||||
}
|
||||
|
||||
if eventContext.SchemaURL != nil {
|
||||
stream.WriteMore()
|
||||
stream.WriteObjectField("schemaurl")
|
||||
stream.WriteString(eventContext.SchemaURL.String())
|
||||
}
|
||||
|
||||
if eventContext.Time != nil {
|
||||
stream.WriteMore()
|
||||
stream.WriteObjectField("time")
|
||||
stream.WriteString(eventContext.Time.String())
|
||||
}
|
||||
case *EventContextV1:
|
||||
// Set a bunch of variables we need later
|
||||
ext = eventContext.Extensions
|
||||
dct = eventContext.DataContentType
|
||||
isBase64 = in.DataBase64
|
||||
|
||||
stream.WriteObjectField("specversion")
|
||||
stream.WriteString(CloudEventsVersionV1)
|
||||
stream.WriteMore()
|
||||
|
||||
stream.WriteObjectField("id")
|
||||
stream.WriteString(eventContext.ID)
|
||||
stream.WriteMore()
|
||||
|
||||
stream.WriteObjectField("source")
|
||||
stream.WriteString(eventContext.Source.String())
|
||||
stream.WriteMore()
|
||||
|
||||
stream.WriteObjectField("type")
|
||||
stream.WriteString(eventContext.Type)
|
||||
|
||||
if eventContext.Subject != nil {
|
||||
stream.WriteMore()
|
||||
stream.WriteObjectField("subject")
|
||||
stream.WriteString(*eventContext.Subject)
|
||||
}
|
||||
|
||||
if eventContext.DataContentType != nil {
|
||||
stream.WriteMore()
|
||||
stream.WriteObjectField("datacontenttype")
|
||||
stream.WriteString(*eventContext.DataContentType)
|
||||
}
|
||||
|
||||
if eventContext.DataSchema != nil {
|
||||
stream.WriteMore()
|
||||
stream.WriteObjectField("dataschema")
|
||||
stream.WriteString(eventContext.DataSchema.String())
|
||||
}
|
||||
|
||||
if eventContext.Time != nil {
|
||||
stream.WriteMore()
|
||||
stream.WriteObjectField("time")
|
||||
stream.WriteString(eventContext.Time.String())
|
||||
}
|
||||
default:
|
||||
return fmt.Errorf("missing event context")
|
||||
}
|
||||
|
||||
// Let's do a check on the error
|
||||
if stream.Error != nil {
|
||||
return fmt.Errorf("error while writing the event attributes: %w", stream.Error)
|
||||
}
|
||||
|
||||
// Let's write the body
|
||||
if in.DataEncoded != nil {
|
||||
stream.WriteMore()
|
||||
|
||||
// We need to figure out the media type first
|
||||
var mediaType string
|
||||
if dct == nil {
|
||||
mediaType = ApplicationJSON
|
||||
} else {
|
||||
// This code is required to extract the media type from the full content type string (which might contain encoding and stuff)
|
||||
contentType := *dct
|
||||
i := strings.IndexRune(contentType, ';')
|
||||
if i == -1 {
|
||||
i = len(contentType)
|
||||
}
|
||||
mediaType = strings.TrimSpace(strings.ToLower(contentType[0:i]))
|
||||
}
|
||||
|
||||
isJson := mediaType == "" || mediaType == ApplicationJSON || mediaType == TextJSON
|
||||
|
||||
// If isJson and no encoding to base64, we don't need to perform additional steps
|
||||
if isJson && !isBase64 {
|
||||
stream.WriteObjectField("data")
|
||||
_, err := stream.Write(in.DataEncoded)
|
||||
if err != nil {
|
||||
return fmt.Errorf("error while writing data: %w", err)
|
||||
}
|
||||
} else {
|
||||
if in.Context.GetSpecVersion() == CloudEventsVersionV1 && isBase64 {
|
||||
stream.WriteObjectField("data_base64")
|
||||
} else {
|
||||
stream.WriteObjectField("data")
|
||||
}
|
||||
// At this point of we need to write to base 64 string, or we just need to write the plain string
|
||||
if isBase64 {
|
||||
stream.WriteString(base64.StdEncoding.EncodeToString(in.DataEncoded))
|
||||
} else {
|
||||
stream.WriteString(string(in.DataEncoded))
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// Let's do a check on the error
|
||||
if stream.Error != nil {
|
||||
return fmt.Errorf("error while writing the event data: %w", stream.Error)
|
||||
}
|
||||
|
||||
for k, v := range ext {
|
||||
stream.WriteMore()
|
||||
stream.WriteObjectField(k)
|
||||
stream.WriteVal(v)
|
||||
}
|
||||
|
||||
stream.WriteObjectEnd()
|
||||
|
||||
// Let's do a check on the error
|
||||
if stream.Error != nil {
|
||||
return fmt.Errorf("error while writing the event extensions: %w", stream.Error)
|
||||
}
|
||||
return stream.Flush()
|
||||
}
|
||||
|
||||
// MarshalJSON implements a custom json marshal method used when this type is
|
||||
// marshaled using json.Marshal.
|
||||
func (e Event) MarshalJSON() ([]byte, error) {
|
||||
var buf bytes.Buffer
|
||||
err := WriteJson(&e, &buf)
|
||||
return buf.Bytes(), err
|
||||
}
|
103
src/vendor/github.com/cloudevents/sdk-go/v2/event/event_reader.go
generated
vendored
Normal file
103
src/vendor/github.com/cloudevents/sdk-go/v2/event/event_reader.go
generated
vendored
Normal file
@ -0,0 +1,103 @@
|
||||
/*
|
||||
Copyright 2021 The CloudEvents Authors
|
||||
SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
package event
|
||||
|
||||
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)
|
||||
}
|
480
src/vendor/github.com/cloudevents/sdk-go/v2/event/event_unmarshal.go
generated
vendored
Normal file
480
src/vendor/github.com/cloudevents/sdk-go/v2/event/event_unmarshal.go
generated
vendored
Normal file
@ -0,0 +1,480 @@
|
||||
/*
|
||||
Copyright 2021 The CloudEvents Authors
|
||||
SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
package event
|
||||
|
||||
import (
|
||||
"encoding/base64"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
"sync"
|
||||
|
||||
jsoniter "github.com/json-iterator/go"
|
||||
|
||||
"github.com/cloudevents/sdk-go/v2/types"
|
||||
)
|
||||
|
||||
const specVersionV03Flag uint8 = 1 << 4
|
||||
const specVersionV1Flag uint8 = 1 << 5
|
||||
const dataBase64Flag uint8 = 1 << 6
|
||||
const dataContentTypeFlag uint8 = 1 << 7
|
||||
|
||||
func checkFlag(state uint8, flag uint8) bool {
|
||||
return state&flag != 0
|
||||
}
|
||||
|
||||
func appendFlag(state *uint8, flag uint8) {
|
||||
*state = (*state) | flag
|
||||
}
|
||||
|
||||
var iterPool = sync.Pool{
|
||||
New: func() interface{} {
|
||||
return jsoniter.Parse(jsoniter.ConfigFastest, nil, 1024)
|
||||
},
|
||||
}
|
||||
|
||||
func borrowIterator(reader io.Reader) *jsoniter.Iterator {
|
||||
iter := iterPool.Get().(*jsoniter.Iterator)
|
||||
iter.Reset(reader)
|
||||
return iter
|
||||
}
|
||||
|
||||
func returnIterator(iter *jsoniter.Iterator) {
|
||||
iter.Error = nil
|
||||
iter.Attachment = nil
|
||||
iterPool.Put(iter)
|
||||
}
|
||||
|
||||
func ReadJson(out *Event, reader io.Reader) error {
|
||||
iterator := borrowIterator(reader)
|
||||
defer returnIterator(iterator)
|
||||
|
||||
return readJsonFromIterator(out, iterator)
|
||||
}
|
||||
|
||||
// ReadJson allows you to read the bytes reader as an event
|
||||
func readJsonFromIterator(out *Event, iterator *jsoniter.Iterator) error {
|
||||
// Parsing dependency graph:
|
||||
// SpecVersion
|
||||
// ^ ^
|
||||
// | +--------------+
|
||||
// + +
|
||||
// All Attributes datacontenttype (and datacontentencoding for v0.3)
|
||||
// (except datacontenttype) ^
|
||||
// |
|
||||
// |
|
||||
// +
|
||||
// Data
|
||||
|
||||
var state uint8 = 0
|
||||
var cachedData []byte
|
||||
|
||||
var (
|
||||
// Universally parseable fields.
|
||||
id string
|
||||
typ string
|
||||
source types.URIRef
|
||||
subject *string
|
||||
time *types.Timestamp
|
||||
datacontenttype *string
|
||||
extensions = make(map[string]interface{})
|
||||
|
||||
// These fields require knowledge about the specversion to be parsed.
|
||||
schemaurl jsoniter.Any
|
||||
datacontentencoding jsoniter.Any
|
||||
dataschema jsoniter.Any
|
||||
dataBase64 jsoniter.Any
|
||||
)
|
||||
|
||||
for key := iterator.ReadObject(); key != ""; key = iterator.ReadObject() {
|
||||
// Check if we have some error in our error cache
|
||||
if iterator.Error != nil {
|
||||
return iterator.Error
|
||||
}
|
||||
|
||||
// We have a key, now we need to figure out what to do
|
||||
// depending on the parsing state
|
||||
|
||||
// If it's a specversion, trigger state change
|
||||
if key == "specversion" {
|
||||
if checkFlag(state, specVersionV1Flag|specVersionV03Flag) {
|
||||
return fmt.Errorf("specversion was already provided")
|
||||
}
|
||||
sv := iterator.ReadString()
|
||||
|
||||
// Check proper specversion
|
||||
switch sv {
|
||||
case CloudEventsVersionV1:
|
||||
con := &EventContextV1{
|
||||
ID: id,
|
||||
Type: typ,
|
||||
Source: source,
|
||||
Subject: subject,
|
||||
Time: time,
|
||||
DataContentType: datacontenttype,
|
||||
}
|
||||
|
||||
// Add the fields relevant for the version ...
|
||||
if dataschema != nil {
|
||||
var err error
|
||||
con.DataSchema, err = toUriPtr(dataschema)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
if dataBase64 != nil {
|
||||
stream := jsoniter.ConfigFastest.BorrowStream(nil)
|
||||
defer jsoniter.ConfigFastest.ReturnStream(stream)
|
||||
dataBase64.WriteTo(stream)
|
||||
cachedData = stream.Buffer()
|
||||
if stream.Error != nil {
|
||||
return stream.Error
|
||||
}
|
||||
appendFlag(&state, dataBase64Flag)
|
||||
}
|
||||
|
||||
// ... add all remaining fields as extensions.
|
||||
if schemaurl != nil {
|
||||
extensions["schemaurl"] = schemaurl.GetInterface()
|
||||
}
|
||||
if datacontentencoding != nil {
|
||||
extensions["datacontentencoding"] = datacontentencoding.GetInterface()
|
||||
}
|
||||
|
||||
out.Context = con
|
||||
appendFlag(&state, specVersionV1Flag)
|
||||
case CloudEventsVersionV03:
|
||||
con := &EventContextV03{
|
||||
ID: id,
|
||||
Type: typ,
|
||||
Source: source,
|
||||
Subject: subject,
|
||||
Time: time,
|
||||
DataContentType: datacontenttype,
|
||||
}
|
||||
var err error
|
||||
// Add the fields relevant for the version ...
|
||||
if schemaurl != nil {
|
||||
con.SchemaURL, err = toUriRefPtr(schemaurl)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
if datacontentencoding != nil {
|
||||
con.DataContentEncoding, err = toStrPtr(datacontentencoding)
|
||||
if *con.DataContentEncoding != Base64 {
|
||||
err = ValidationError{"datacontentencoding": errors.New("invalid datacontentencoding value, the only allowed value is 'base64'")}
|
||||
}
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
appendFlag(&state, dataBase64Flag)
|
||||
}
|
||||
|
||||
// ... add all remaining fields as extensions.
|
||||
if dataschema != nil {
|
||||
extensions["dataschema"] = dataschema.GetInterface()
|
||||
}
|
||||
if dataBase64 != nil {
|
||||
extensions["data_base64"] = dataBase64.GetInterface()
|
||||
}
|
||||
|
||||
out.Context = con
|
||||
appendFlag(&state, specVersionV03Flag)
|
||||
default:
|
||||
return ValidationError{"specversion": errors.New("unknown value: " + sv)}
|
||||
}
|
||||
|
||||
// Apply all extensions to the context object.
|
||||
for key, val := range extensions {
|
||||
if err := out.Context.SetExtension(key, val); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
continue
|
||||
}
|
||||
|
||||
// If no specversion ...
|
||||
if !checkFlag(state, specVersionV03Flag|specVersionV1Flag) {
|
||||
switch key {
|
||||
case "id":
|
||||
id = iterator.ReadString()
|
||||
case "type":
|
||||
typ = iterator.ReadString()
|
||||
case "source":
|
||||
source = readUriRef(iterator)
|
||||
case "subject":
|
||||
subject = readStrPtr(iterator)
|
||||
case "time":
|
||||
time = readTimestamp(iterator)
|
||||
case "datacontenttype":
|
||||
datacontenttype = readStrPtr(iterator)
|
||||
appendFlag(&state, dataContentTypeFlag)
|
||||
case "data":
|
||||
cachedData = iterator.SkipAndReturnBytes()
|
||||
case "data_base64":
|
||||
dataBase64 = iterator.ReadAny()
|
||||
case "dataschema":
|
||||
dataschema = iterator.ReadAny()
|
||||
case "schemaurl":
|
||||
schemaurl = iterator.ReadAny()
|
||||
case "datacontentencoding":
|
||||
datacontentencoding = iterator.ReadAny()
|
||||
default:
|
||||
extensions[key] = iterator.Read()
|
||||
}
|
||||
continue
|
||||
}
|
||||
|
||||
// From this point downward -> we can assume the event has a context pointer non nil
|
||||
|
||||
// If it's a datacontenttype, trigger state change
|
||||
if key == "datacontenttype" {
|
||||
if checkFlag(state, dataContentTypeFlag) {
|
||||
return fmt.Errorf("datacontenttype was already provided")
|
||||
}
|
||||
|
||||
dct := iterator.ReadString()
|
||||
|
||||
switch ctx := out.Context.(type) {
|
||||
case *EventContextV03:
|
||||
ctx.DataContentType = &dct
|
||||
case *EventContextV1:
|
||||
ctx.DataContentType = &dct
|
||||
}
|
||||
appendFlag(&state, dataContentTypeFlag)
|
||||
continue
|
||||
}
|
||||
|
||||
// If it's a datacontentencoding and it's v0.3, trigger state change
|
||||
if checkFlag(state, specVersionV03Flag) && key == "datacontentencoding" {
|
||||
if checkFlag(state, dataBase64Flag) {
|
||||
return ValidationError{"datacontentencoding": errors.New("datacontentencoding was specified twice")}
|
||||
}
|
||||
|
||||
dce := iterator.ReadString()
|
||||
|
||||
if dce != Base64 {
|
||||
return ValidationError{"datacontentencoding": errors.New("invalid datacontentencoding value, the only allowed value is 'base64'")}
|
||||
}
|
||||
|
||||
out.Context.(*EventContextV03).DataContentEncoding = &dce
|
||||
appendFlag(&state, dataBase64Flag)
|
||||
continue
|
||||
}
|
||||
|
||||
// We can parse all attributes, except data.
|
||||
// If it's data or data_base64 and we don't have the attributes to process it, then we cache it
|
||||
// The expanded form of this condition is:
|
||||
// (checkFlag(state, specVersionV1Flag) && !checkFlag(state, dataContentTypeFlag) && (key == "data" || key == "data_base64")) ||
|
||||
// (checkFlag(state, specVersionV03Flag) && !(checkFlag(state, dataContentTypeFlag) && checkFlag(state, dataBase64Flag)) && key == "data")
|
||||
if (state&(specVersionV1Flag|dataContentTypeFlag) == specVersionV1Flag && (key == "data" || key == "data_base64")) ||
|
||||
((state&specVersionV03Flag == specVersionV03Flag) && (state&(dataContentTypeFlag|dataBase64Flag) != (dataContentTypeFlag | dataBase64Flag)) && key == "data") {
|
||||
if key == "data_base64" {
|
||||
appendFlag(&state, dataBase64Flag)
|
||||
}
|
||||
cachedData = iterator.SkipAndReturnBytes()
|
||||
continue
|
||||
}
|
||||
|
||||
// At this point or this value is an attribute (excluding datacontenttype and datacontentencoding), or this value is data and this condition is valid:
|
||||
// (specVersionV1Flag & dataContentTypeFlag) || (specVersionV03Flag & dataContentTypeFlag & dataBase64Flag)
|
||||
switch eventContext := out.Context.(type) {
|
||||
case *EventContextV03:
|
||||
switch key {
|
||||
case "id":
|
||||
eventContext.ID = iterator.ReadString()
|
||||
case "type":
|
||||
eventContext.Type = iterator.ReadString()
|
||||
case "source":
|
||||
eventContext.Source = readUriRef(iterator)
|
||||
case "subject":
|
||||
eventContext.Subject = readStrPtr(iterator)
|
||||
case "time":
|
||||
eventContext.Time = readTimestamp(iterator)
|
||||
case "schemaurl":
|
||||
eventContext.SchemaURL = readUriRefPtr(iterator)
|
||||
case "data":
|
||||
iterator.Error = consumeData(out, checkFlag(state, dataBase64Flag), iterator)
|
||||
default:
|
||||
if eventContext.Extensions == nil {
|
||||
eventContext.Extensions = make(map[string]interface{}, 1)
|
||||
}
|
||||
iterator.Error = eventContext.SetExtension(key, iterator.Read())
|
||||
}
|
||||
case *EventContextV1:
|
||||
switch key {
|
||||
case "id":
|
||||
eventContext.ID = iterator.ReadString()
|
||||
case "type":
|
||||
eventContext.Type = iterator.ReadString()
|
||||
case "source":
|
||||
eventContext.Source = readUriRef(iterator)
|
||||
case "subject":
|
||||
eventContext.Subject = readStrPtr(iterator)
|
||||
case "time":
|
||||
eventContext.Time = readTimestamp(iterator)
|
||||
case "dataschema":
|
||||
eventContext.DataSchema = readUriPtr(iterator)
|
||||
case "data":
|
||||
iterator.Error = consumeData(out, false, iterator)
|
||||
case "data_base64":
|
||||
iterator.Error = consumeData(out, true, iterator)
|
||||
default:
|
||||
if eventContext.Extensions == nil {
|
||||
eventContext.Extensions = make(map[string]interface{}, 1)
|
||||
}
|
||||
iterator.Error = eventContext.SetExtension(key, iterator.Read())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if state&(specVersionV03Flag|specVersionV1Flag) == 0 {
|
||||
return ValidationError{"specversion": errors.New("no specversion")}
|
||||
}
|
||||
|
||||
if iterator.Error != nil {
|
||||
return iterator.Error
|
||||
}
|
||||
|
||||
// If there is a dataToken cached, we always defer at the end the processing
|
||||
// because nor datacontenttype or datacontentencoding are mandatory.
|
||||
if cachedData != nil {
|
||||
return consumeDataAsBytes(out, checkFlag(state, dataBase64Flag), cachedData)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func consumeDataAsBytes(e *Event, isBase64 bool, b []byte) error {
|
||||
if isBase64 {
|
||||
e.DataBase64 = true
|
||||
|
||||
// Allocate payload byte buffer
|
||||
base64Encoded := b[1 : len(b)-1] // remove quotes
|
||||
e.DataEncoded = make([]byte, base64.StdEncoding.DecodedLen(len(base64Encoded)))
|
||||
length, err := base64.StdEncoding.Decode(e.DataEncoded, base64Encoded)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
e.DataEncoded = e.DataEncoded[0:length]
|
||||
return nil
|
||||
}
|
||||
|
||||
mt, _ := e.Context.GetDataMediaType()
|
||||
// Empty content type assumes json
|
||||
if mt != "" && mt != ApplicationJSON && mt != TextJSON {
|
||||
// If not json, then data is encoded as string
|
||||
iter := jsoniter.ParseBytes(jsoniter.ConfigFastest, b)
|
||||
src := iter.ReadString() // handles escaping
|
||||
e.DataEncoded = []byte(src)
|
||||
if iter.Error != nil {
|
||||
return fmt.Errorf("unexpected data payload for media type %q, expected a string: %w", mt, iter.Error)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
e.DataEncoded = b
|
||||
return nil
|
||||
}
|
||||
|
||||
func consumeData(e *Event, isBase64 bool, iter *jsoniter.Iterator) error {
|
||||
if isBase64 {
|
||||
e.DataBase64 = true
|
||||
|
||||
// Allocate payload byte buffer
|
||||
base64Encoded := iter.ReadStringAsSlice()
|
||||
e.DataEncoded = make([]byte, base64.StdEncoding.DecodedLen(len(base64Encoded)))
|
||||
length, err := base64.StdEncoding.Decode(e.DataEncoded, base64Encoded)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
e.DataEncoded = e.DataEncoded[0:length]
|
||||
return nil
|
||||
}
|
||||
|
||||
mt, _ := e.Context.GetDataMediaType()
|
||||
if mt != ApplicationJSON && mt != TextJSON {
|
||||
// If not json, then data is encoded as string
|
||||
src := iter.ReadString() // handles escaping
|
||||
e.DataEncoded = []byte(src)
|
||||
if iter.Error != nil {
|
||||
return fmt.Errorf("unexpected data payload for media type %q, expected a string: %w", mt, iter.Error)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
e.DataEncoded = iter.SkipAndReturnBytes()
|
||||
return nil
|
||||
}
|
||||
|
||||
func readUriRef(iter *jsoniter.Iterator) types.URIRef {
|
||||
str := iter.ReadString()
|
||||
uriRef := types.ParseURIRef(str)
|
||||
if uriRef == nil {
|
||||
iter.Error = fmt.Errorf("cannot parse uri ref: %v", str)
|
||||
return types.URIRef{}
|
||||
}
|
||||
return *uriRef
|
||||
}
|
||||
|
||||
func readStrPtr(iter *jsoniter.Iterator) *string {
|
||||
str := iter.ReadString()
|
||||
if str == "" {
|
||||
return nil
|
||||
}
|
||||
return &str
|
||||
}
|
||||
|
||||
func readUriRefPtr(iter *jsoniter.Iterator) *types.URIRef {
|
||||
return types.ParseURIRef(iter.ReadString())
|
||||
}
|
||||
|
||||
func readUriPtr(iter *jsoniter.Iterator) *types.URI {
|
||||
return types.ParseURI(iter.ReadString())
|
||||
}
|
||||
|
||||
func readTimestamp(iter *jsoniter.Iterator) *types.Timestamp {
|
||||
t, err := types.ParseTimestamp(iter.ReadString())
|
||||
if err != nil {
|
||||
iter.Error = err
|
||||
}
|
||||
return t
|
||||
}
|
||||
|
||||
func toStrPtr(val jsoniter.Any) (*string, error) {
|
||||
str := val.ToString()
|
||||
if val.LastError() != nil {
|
||||
return nil, val.LastError()
|
||||
}
|
||||
if str == "" {
|
||||
return nil, nil
|
||||
}
|
||||
return &str, nil
|
||||
}
|
||||
|
||||
func toUriRefPtr(val jsoniter.Any) (*types.URIRef, error) {
|
||||
str := val.ToString()
|
||||
if val.LastError() != nil {
|
||||
return nil, val.LastError()
|
||||
}
|
||||
return types.ParseURIRef(str), nil
|
||||
}
|
||||
|
||||
func toUriPtr(val jsoniter.Any) (*types.URI, error) {
|
||||
str := val.ToString()
|
||||
if val.LastError() != nil {
|
||||
return nil, val.LastError()
|
||||
}
|
||||
return types.ParseURI(str), nil
|
||||
}
|
||||
|
||||
// UnmarshalJSON implements the json unmarshal method used when this type is
|
||||
// unmarshaled using json.Unmarshal.
|
||||
func (e *Event) UnmarshalJSON(b []byte) error {
|
||||
iterator := jsoniter.ConfigFastest.BorrowIterator(b)
|
||||
defer jsoniter.ConfigFastest.ReturnIterator(iterator)
|
||||
return readJsonFromIterator(e, iterator)
|
||||
}
|
50
src/vendor/github.com/cloudevents/sdk-go/v2/event/event_validation.go
generated
vendored
Normal file
50
src/vendor/github.com/cloudevents/sdk-go/v2/event/event_validation.go
generated
vendored
Normal file
@ -0,0 +1,50 @@
|
||||
/*
|
||||
Copyright 2021 The CloudEvents Authors
|
||||
SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
package event
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strings"
|
||||
)
|
||||
|
||||
type ValidationError map[string]error
|
||||
|
||||
func (e ValidationError) Error() string {
|
||||
b := strings.Builder{}
|
||||
for k, v := range e {
|
||||
b.WriteString(k)
|
||||
b.WriteString(": ")
|
||||
b.WriteString(v.Error())
|
||||
b.WriteRune('\n')
|
||||
}
|
||||
return b.String()
|
||||
}
|
||||
|
||||
// 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 ValidationError{"specversion": fmt.Errorf("missing Event.Context")}
|
||||
}
|
||||
|
||||
errs := map[string]error{}
|
||||
if e.FieldErrors != nil {
|
||||
for k, v := range e.FieldErrors {
|
||||
errs[k] = v
|
||||
}
|
||||
}
|
||||
|
||||
if fieldErrors := e.Context.Validate(); fieldErrors != nil {
|
||||
for k, v := range fieldErrors {
|
||||
errs[k] = v
|
||||
}
|
||||
}
|
||||
|
||||
if len(errs) > 0 {
|
||||
return ValidationError(errs)
|
||||
}
|
||||
return nil
|
||||
}
|
117
src/vendor/github.com/cloudevents/sdk-go/v2/event/event_writer.go
generated
vendored
Normal file
117
src/vendor/github.com/cloudevents/sdk-go/v2/event/event_writer.go
generated
vendored
Normal file
@ -0,0 +1,117 @@
|
||||
/*
|
||||
Copyright 2021 The CloudEvents Authors
|
||||
SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
package event
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"time"
|
||||
)
|
||||
|
||||
var _ EventWriter = (*Event)(nil)
|
||||
|
||||
// SetSpecVersion implements EventWriter.SetSpecVersion
|
||||
func (e *Event) SetSpecVersion(v string) {
|
||||
switch v {
|
||||
case CloudEventsVersionV03:
|
||||
if e.Context == nil {
|
||||
e.Context = &EventContextV03{}
|
||||
} else {
|
||||
e.Context = e.Context.AsV03()
|
||||
}
|
||||
case CloudEventsVersionV1:
|
||||
if e.Context == nil {
|
||||
e.Context = &EventContextV1{}
|
||||
} else {
|
||||
e.Context = e.Context.AsV1()
|
||||
}
|
||||
default:
|
||||
e.fieldError("specversion", fmt.Errorf("a valid spec version is required: [%s, %s]",
|
||||
CloudEventsVersionV03, CloudEventsVersionV1))
|
||||
return
|
||||
}
|
||||
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")
|
||||
}
|
||||
}
|
||||
|
||||
// SetDataContentEncoding is deprecated. Implements EventWriter.SetDataContentEncoding.
|
||||
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)
|
||||
}
|
||||
}
|
125
src/vendor/github.com/cloudevents/sdk-go/v2/event/eventcontext.go
generated
vendored
Normal file
125
src/vendor/github.com/cloudevents/sdk-go/v2/event/eventcontext.go
generated
vendored
Normal file
@ -0,0 +1,125 @@
|
||||
/*
|
||||
Copyright 2021 The CloudEvents Authors
|
||||
SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
package event
|
||||
|
||||
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 {
|
||||
// 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 {
|
||||
// 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() ValidationError
|
||||
|
||||
// Clone clones the event context.
|
||||
Clone() EventContext
|
||||
|
||||
// String returns a pretty-printed representation of the EventContext.
|
||||
String() string
|
||||
}
|
329
src/vendor/github.com/cloudevents/sdk-go/v2/event/eventcontext_v03.go
generated
vendored
Normal file
329
src/vendor/github.com/cloudevents/sdk-go/v2/event/eventcontext_v03.go
generated
vendored
Normal file
@ -0,0 +1,329 @@
|
||||
/*
|
||||
Copyright 2021 The CloudEvents Authors
|
||||
SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
package event
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"mime"
|
||||
"sort"
|
||||
"strings"
|
||||
|
||||
"github.com/cloudevents/sdk-go/v2/types"
|
||||
)
|
||||
|
||||
const (
|
||||
// CloudEventsVersionV03 represents the version 0.3 of the CloudEvents spec.
|
||||
CloudEventsVersionV03 = "0.3"
|
||||
)
|
||||
|
||||
var specV03Attributes = map[string]struct{}{
|
||||
"type": {},
|
||||
"source": {},
|
||||
"subject": {},
|
||||
"id": {},
|
||||
"time": {},
|
||||
"schemaurl": {},
|
||||
"datacontenttype": {},
|
||||
"datacontentencoding": {},
|
||||
}
|
||||
|
||||
// EventContextV03 represents the non-data attributes of a CloudEvents v0.3
|
||||
// event.
|
||||
type EventContextV03 struct {
|
||||
// Type - The type of the occurrence which has happened.
|
||||
Type string `json:"type"`
|
||||
// Source - A URI describing the event producer.
|
||||
Source types.URIRef `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.URIRef `json:"schemaurl,omitempty"`
|
||||
// GetDataMediaType - A MIME (RFC2046) string describing the media type of `data`.
|
||||
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. This function fails if the name uses a reserved event context key.
|
||||
func (ec *EventContextV03) SetExtension(name string, value interface{}) error {
|
||||
if ec.Extensions == nil {
|
||||
ec.Extensions = make(map[string]interface{})
|
||||
}
|
||||
|
||||
if _, ok := specV03Attributes[strings.ToLower(name)]; ok {
|
||||
return fmt.Errorf("bad key %q: CloudEvents spec attribute MUST NOT be overwritten by extension", name)
|
||||
}
|
||||
|
||||
if value == nil {
|
||||
delete(ec.Extensions, name)
|
||||
if len(ec.Extensions) == 0 {
|
||||
ec.Extensions = nil
|
||||
}
|
||||
return nil
|
||||
} else {
|
||||
v, err := types.Validate(value)
|
||||
if err == nil {
|
||||
ec.Extensions[name] = v
|
||||
}
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
// Clone implements EventContextConverter.Clone
|
||||
func (ec EventContextV03) Clone() EventContext {
|
||||
ec03 := ec.AsV03()
|
||||
ec03.Source = types.Clone(ec.Source).(types.URIRef)
|
||||
if ec.Time != nil {
|
||||
ec03.Time = types.Clone(ec.Time).(*types.Timestamp)
|
||||
}
|
||||
if ec.SchemaURL != nil {
|
||||
ec03.SchemaURL = types.Clone(ec.SchemaURL).(*types.URIRef)
|
||||
}
|
||||
ec03.Extensions = ec.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] = types.Clone(v)
|
||||
}
|
||||
return new
|
||||
}
|
||||
|
||||
// AsV03 implements EventContextConverter.AsV03
|
||||
func (ec EventContextV03) AsV03() *EventContextV03 {
|
||||
return &ec
|
||||
}
|
||||
|
||||
// AsV1 implements EventContextConverter.AsV1
|
||||
func (ec EventContextV03) AsV1() *EventContextV1 {
|
||||
ret := EventContextV1{
|
||||
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() ValidationError {
|
||||
errors := map[string]error{}
|
||||
|
||||
// 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["type"] = fmt.Errorf("MUST be a non-empty string")
|
||||
}
|
||||
|
||||
// source
|
||||
// Type: URI-reference
|
||||
// Constraints:
|
||||
// REQUIRED
|
||||
source := strings.TrimSpace(ec.Source.String())
|
||||
if source == "" {
|
||||
errors["source"] = fmt.Errorf("REQUIRED")
|
||||
}
|
||||
|
||||
// subject
|
||||
// Type: String
|
||||
// Constraints:
|
||||
// OPTIONAL
|
||||
// MUST be a non-empty string
|
||||
if ec.Subject != nil {
|
||||
subject := strings.TrimSpace(*ec.Subject)
|
||||
if subject == "" {
|
||||
errors["subject"] = fmt.Errorf("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["id"] = fmt.Errorf("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["schemaurl"] = fmt.Errorf("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 == "" {
|
||||
errors["datacontenttype"] = fmt.Errorf("if present, MUST adhere to the format specified in RFC 2046")
|
||||
} else {
|
||||
_, _, err := mime.ParseMediaType(dataContentType)
|
||||
if err != nil {
|
||||
errors["datacontenttype"] = fmt.Errorf("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 {
|
||||
errors["datacontentencoding"] = fmt.Errorf("if present, MUST adhere to RFC 2045 Section 6.1")
|
||||
}
|
||||
}
|
||||
|
||||
if len(errors) > 0 {
|
||||
return errors
|
||||
}
|
||||
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: " + CloudEventsVersionV03 + "\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()
|
||||
}
|
99
src/vendor/github.com/cloudevents/sdk-go/v2/event/eventcontext_v03_reader.go
generated
vendored
Normal file
99
src/vendor/github.com/cloudevents/sdk-go/v2/event/eventcontext_v03_reader.go
generated
vendored
Normal file
@ -0,0 +1,99 @@
|
||||
/*
|
||||
Copyright 2021 The CloudEvents Authors
|
||||
SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
package event
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strings"
|
||||
"time"
|
||||
)
|
||||
|
||||
// GetSpecVersion implements EventContextReader.GetSpecVersion
|
||||
func (ec EventContextV03) GetSpecVersion() string {
|
||||
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 {
|
||||
dct := *ec.DataContentType
|
||||
i := strings.IndexRune(dct, ';')
|
||||
if i == -1 {
|
||||
return dct, nil
|
||||
}
|
||||
return strings.TrimSpace(dct[0:i]), 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
|
||||
}
|
103
src/vendor/github.com/cloudevents/sdk-go/v2/event/eventcontext_v03_writer.go
generated
vendored
Normal file
103
src/vendor/github.com/cloudevents/sdk-go/v2/event/eventcontext_v03_writer.go
generated
vendored
Normal file
@ -0,0 +1,103 @@
|
||||
/*
|
||||
Copyright 2021 The CloudEvents Authors
|
||||
SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
package event
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"net/url"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/cloudevents/sdk-go/v2/types"
|
||||
)
|
||||
|
||||
// Adhere to EventContextWriter
|
||||
var _ EventContextWriter = (*EventContextV03)(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.URIRef{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.URIRef{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
|
||||
}
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user