fix build test (#5251)

This commit is contained in:
Lionel Villard 2022-09-26 17:29:13 -04:00 committed by GitHub
parent 892540960d
commit 26a720ed86
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
286 changed files with 0 additions and 27237 deletions

View File

@ -1,201 +0,0 @@
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.

View File

@ -1,169 +0,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/observability"
"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
NewClientObserved = client.NewObserved
NewDefaultClient = client.NewDefault
NewHTTPReceiveHandler = client.NewHTTPReceiveHandler
// Client Options
WithEventDefaulter = client.WithEventDefaulter
WithUUIDs = client.WithUUIDs
WithTimeNow = client.WithTimeNow
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
// HTTP Results
NewHTTPResult = http.NewResult
NewHTTPRetriesResult = http.NewRetriesResult
// Message Creation
ToMessage = binding.ToMessage
// HTTP Messages
WriteHTTPRequest = http.WriteRequest
// Tracing
EnableTracing = observability.EnableTracing
// 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
)

View File

@ -1,47 +0,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
}

View File

@ -1,63 +0,0 @@
/*
Package binding defines interfaces for protocol bindings.
NOTE: Most applications that emit or consume events should use the ../client
package, which provides a simpler API to the underlying binding.
The interfaces in this package provide extra encoding and protocol information
to allow efficient forwarding and end-to-end reliable delivery between a
Receiver and a Sender belonging to different bindings. This is useful for
intermediary applications that route or forward events, but not necessary for
most "endpoint" applications that emit or consume events.
Protocol Bindings
A protocol binding 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

View File

@ -1,26 +0,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
)
// 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")

View File

@ -1,103 +0,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.NewReader(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)
}

View File

@ -1,37 +0,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}
}

View File

@ -1,7 +0,0 @@
/*
Package format formats structured events.
The "application/cloudevents+json" format is built-in and always
available. Other formats may be added.
*/
package format

View File

@ -1,75 +0,0 @@
package format
import (
"encoding/json"
"fmt"
"mime"
"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 {
mediaType, _, _ := mime.ParseMediaType(contentType)
return formats[mediaType]
}
func unknown(mediaType string) error {
return fmt.Errorf("unknown event format media-type %#v", mediaType)
}
// Add a new Format. It can be retrieved by Lookup(f.MediaType())
func Add(f Format) { formats[f.MediaType()] = f }
// Marshal an event to bytes using the mediaType event format.
func Marshal(mediaType string, e *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)
}

View File

@ -1,128 +0,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))
}
// 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
}

View File

@ -1,136 +0,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{})
}

View File

@ -1,8 +0,0 @@
/*
Package spec provides spec-version metadata.
For use by code that maps events using (prefixed) attribute name strings.
Supports handling multiple spec versions uniformly.
*/
package spec

View File

@ -1,76 +0,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
}

View File

@ -1,184 +0,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")
}

View File

@ -1,17 +0,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
}

View File

@ -1,126 +0,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 {
var buf bytes.Buffer
w, err := io.Copy(&buf, data)
if err != nil {
return err
}
if w != 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)
}

View File

@ -1,37 +0,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)

View File

@ -1,174 +0,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)
}

View File

@ -1,227 +0,0 @@
package client
import (
"context"
"errors"
"fmt"
"io"
"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{}
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
outboundContextDecorators []func(context.Context) context.Context
invoker Invoker
receiverMu sync.Mutex
eventDefaulterFns []EventDefaulter
}
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 {
if c.sender == nil {
return errors.New("sender not set")
}
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
}
return c.sender.Send(ctx, (*binding.EventMessage)(&e))
}
func (c *ceClient) Request(ctx context.Context, e event.Event) (*event.Event, protocol.Result) {
if c.requester == nil {
return nil, errors.New("requester not set")
}
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
}
// If provided a requester, use it to do request/response.
var resp *event.Event
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))
}
}()
}
// 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 err != nil {
err = fmt.Errorf("%w; failed to convert response into event: %s", err, rserr)
} else {
// 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 = fmt.Errorf("%w; failed to convert response into event: %s", protocol.ResultACK, rserr)
}
} else {
resp = rs
}
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 {
c.receiverMu.Lock()
defer c.receiverMu.Unlock()
if c.invoker != nil {
return fmt.Errorf("client already has a receiver")
}
invoker, err := newReceiveInvoker(fn, c.eventDefaulterFns...) // TODO: this will have to pick between a observed invoker or not.
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
defer func() {
c.invoker = nil
}()
// Start the opener, if set.
if c.opener != nil {
go func() {
// TODO: handle error correctly here.
if err := c.opener.OpenInbound(ctx); err != nil {
panic(err)
}
}()
}
var msg binding.Message
var respFn protocol.ResponseFn
// Start Polling.
for {
if c.responder != nil {
msg, respFn, err = c.responder.Respond(ctx)
} else if c.receiver != nil {
msg, err = c.receiver.Receive(ctx)
} else {
return errors.New("responder nor receiver set")
}
if err == io.EOF { // Normal close
return nil
}
if err != nil {
cecontext.LoggerFrom(ctx).Warn("Error while receiving a message: %s", err.Error())
continue
}
if err := c.invoker.Invoke(ctx, msg, respFn); err != nil {
return err
}
}
}

View File

@ -1,26 +0,0 @@
package client
import (
"github.com/cloudevents/sdk-go/v2/protocol/http"
)
// NewDefault provides the good defaults for the common case using an HTTP
// Protocol client. The http transport has had WithBinaryEncoding http
// transport option applied to it. The client will always send Binary
// encoding but will inspect the outbound event context and match the version.
// The WithTimeNow, and WithUUIDs client options are also applied to the
// client, all outbound events will have a time and id set if not already
// present.
func NewDefault() (Client, error) {
p, err := http.New()
if err != nil {
return nil, err
}
c, err := NewObserved(p, WithTimeNow(), WithUUIDs())
if err != nil {
return nil, err
}
return c, nil
}

View File

@ -1,102 +0,0 @@
package client
import (
"context"
"github.com/cloudevents/sdk-go/v2/event"
"github.com/cloudevents/sdk-go/v2/extensions"
"github.com/cloudevents/sdk-go/v2/observability"
"github.com/cloudevents/sdk-go/v2/protocol"
"go.opencensus.io/trace"
)
// NewObserved produces a new client with the provided transport object and applied
// client options.
func NewObserved(protocol interface{}, opts ...Option) (Client, error) {
client, err := New(protocol, opts...)
if err != nil {
return nil, err
}
c := &obsClient{client: client}
if err := c.applyOptions(opts...); err != nil {
return nil, err
}
return c, nil
}
type obsClient struct {
client Client
addTracing bool
}
func (c *obsClient) applyOptions(opts ...Option) error {
for _, fn := range opts {
if err := fn(c); err != nil {
return err
}
}
return nil
}
// Send transmits the provided event on a preconfigured Protocol. Send returns
// an error if there was an an issue validating the outbound event or the
// transport returns an error.
func (c *obsClient) Send(ctx context.Context, e event.Event) protocol.Result {
ctx, r := observability.NewReporter(ctx, reportSend)
ctx, span := trace.StartSpan(ctx, observability.ClientSpanName, trace.WithSpanKind(trace.SpanKindClient))
defer span.End()
if span.IsRecordingEvents() {
span.AddAttributes(EventTraceAttributes(&e)...)
}
if c.addTracing {
e.Context = e.Context.Clone()
extensions.FromSpanContext(span.SpanContext()).AddTracingAttributes(&e)
}
result := c.client.Send(ctx, e)
if protocol.IsACK(result) {
r.OK()
} else {
r.Error()
}
return result
}
func (c *obsClient) Request(ctx context.Context, e event.Event) (*event.Event, protocol.Result) {
ctx, r := observability.NewReporter(ctx, reportRequest)
ctx, span := trace.StartSpan(ctx, observability.ClientSpanName, trace.WithSpanKind(trace.SpanKindClient))
defer span.End()
if span.IsRecordingEvents() {
span.AddAttributes(EventTraceAttributes(&e)...)
}
resp, result := c.client.Request(ctx, e)
if protocol.IsACK(result) {
r.OK()
} else {
r.Error()
}
return resp, result
}
// StartReceiver sets up the given fn to handle Receive.
// See Client.StartReceiver for details. This is a blocking call.
func (c *obsClient) StartReceiver(ctx context.Context, fn interface{}) error {
ctx, r := observability.NewReporter(ctx, reportStartReceiver)
err := c.client.StartReceiver(ctx, fn)
if err != nil {
r.Error()
} else {
r.OK()
}
return err
}

View File

@ -1,52 +0,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
}
}

View File

@ -1,6 +0,0 @@
/*
Package client holds the recommended entry points for interacting with the CloudEvents Golang SDK. The client wraps
a selected transport. The client adds validation and defaulting for sending events, and flexible receiver method
registration. For full details, read the `client.Client` documentation.
*/
package client

View File

@ -1,39 +0,0 @@
package client
import (
"context"
"net/http"
thttp "github.com/cloudevents/sdk-go/v2/protocol/http"
)
func NewHTTPReceiveHandler(ctx context.Context, p *thttp.Protocol, fn interface{}) (*EventReceiver, error) {
invoker, err := newReceiveInvoker(fn)
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) {
go func() {
r.p.ServeHTTP(rw, req)
}()
ctx := context.Background()
msg, respFn, err := r.p.Respond(ctx)
if err != nil {
//lint:ignore SA9003 TODO: Branch left empty
} else if err := r.invoker.Invoke(ctx, msg, respFn); err != nil {
// TODO
}
}

View File

@ -1,94 +0,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/protocol"
)
type Invoker interface {
Invoke(context.Context, binding.Message, protocol.ResponseFn) error
IsReceiver() bool
IsResponder() bool
}
var _ Invoker = (*receiveInvoker)(nil)
func newReceiveInvoker(fn interface{}, fns ...EventDefaulter) (Invoker, error) {
r := &receiveInvoker{
eventDefaulterFns: fns,
}
if fn, err := receiver(fn); err != nil {
return nil, err
} else {
r.fn = fn
}
return r, nil
}
type receiveInvoker struct {
fn *receiverFn
eventDefaulterFns []EventDefaulter
}
func (r *receiveInvoker) Invoke(ctx context.Context, m binding.Message, respFn protocol.ResponseFn) (err error) {
var isFinished bool
defer func() {
if !isFinished {
if err2 := m.Finish(err); err2 == nil {
err = err2
}
}
}()
e, err := binding.ToEvent(ctx, m)
if err != nil {
return err
}
if e != nil && r.fn != nil {
resp, result := r.fn.invoke(ctx, *e)
// 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).Error(fmt.Errorf("cloudevent validation failed on response event: %v, %w", verr, err))
}
}
// protocol can manual ack by the result
if respFn == nil {
if !protocol.IsACK(result) {
err = m.Finish(result)
isFinished = true
}
return
}
var rm binding.Message
if resp != nil {
rm = (*binding.EventMessage)(resp)
}
return respFn(ctx, rm, result)
}
return nil
}
func (r *receiveInvoker) IsReceiver() bool {
return !r.fn.hasEventOut
}
func (r *receiveInvoker) IsResponder() bool {
return r.fn.hasEventOut
}

View File

@ -1,97 +0,0 @@
package client
import (
"context"
"github.com/cloudevents/sdk-go/v2/event"
"github.com/cloudevents/sdk-go/v2/extensions"
"github.com/cloudevents/sdk-go/v2/observability"
"go.opencensus.io/stats"
"go.opencensus.io/stats/view"
"go.opencensus.io/trace"
)
var (
// LatencyMs measures the latency in milliseconds for the CloudEvents
// client methods.
LatencyMs = stats.Float64("cloudevents.io/sdk-go/client/latency", "The latency in milliseconds for the CloudEvents client methods.", "ms")
)
var (
// LatencyView is an OpenCensus view that shows client method latency.
LatencyView = &view.View{
Name: "client/latency",
Measure: LatencyMs,
Description: "The distribution of latency inside of client for CloudEvents.",
Aggregation: view.Distribution(0, .01, .1, 1, 10, 100, 1000, 10000),
TagKeys: observability.LatencyTags(),
}
)
type observed int32
// Adheres to Observable
var _ observability.Observable = observed(0)
const (
specversionAttr = "cloudevents.specversion"
idAttr = "cloudevents.id"
typeAttr = "cloudevents.type"
sourceAttr = "cloudevents.source"
subjectAttr = "cloudevents.subject"
datacontenttypeAttr = "cloudevents.datacontenttype"
reportSend observed = iota
reportRequest
reportStartReceiver
)
// MethodName implements Observable.MethodName
func (o observed) MethodName() string {
switch o {
case reportSend:
return "send"
case reportRequest:
return "request"
case reportStartReceiver:
return "start_receiver"
default:
return "unknown"
}
}
// LatencyMs implements Observable.LatencyMs
func (o observed) LatencyMs() *stats.Float64Measure {
return LatencyMs
}
func EventTraceAttributes(e event.EventReader) []trace.Attribute {
as := []trace.Attribute{
trace.StringAttribute(specversionAttr, e.SpecVersion()),
trace.StringAttribute(idAttr, e.ID()),
trace.StringAttribute(typeAttr, e.Type()),
trace.StringAttribute(sourceAttr, e.Source()),
}
if sub := e.Subject(); sub != "" {
as = append(as, trace.StringAttribute(subjectAttr, sub))
}
if dct := e.DataContentType(); dct != "" {
as = append(as, trace.StringAttribute(datacontenttypeAttr, dct))
}
return as
}
// TraceSpan returns context and trace.Span based on event. Caller must call span.End()
func TraceSpan(ctx context.Context, e event.Event) (context.Context, *trace.Span) {
var span *trace.Span
if ext, ok := extensions.GetDistributedTracingExtension(e); ok {
ctx, span = ext.StartChildSpan(ctx, observability.ClientSpanName, trace.WithSpanKind(trace.SpanKindServer))
}
if span == nil {
ctx, span = trace.StartSpan(ctx, observability.ClientSpanName, trace.WithSpanKind(trace.SpanKindServer))
}
if span.IsRecordingEvents() {
span.AddAttributes(EventTraceAttributes(&e)...)
}
return ctx, span
}

View File

@ -1,74 +0,0 @@
package client
import (
"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.
func WithTracePropagation() Option {
return func(i interface{}) error {
if c, ok := i.(*obsClient); ok {
c.addTracing = true
}
return nil
}
}

View File

@ -1,189 +0,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() error
// * func(context.Context)
// * func(context.Context) transport.Result
// * func(event.Event)
// * func(event.Event) transport.Result
// * func(context.Context, event.Event)
// * func(context.Context, event.Event) transport.Result
// * func(event.Event) *event.Event
// * func(event.Event) (*event.Event, transport.Result)
// * func(context.Context, event.Event, *event.Event
// * func(context.Context, event.Event) (*event.Event, transport.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 !fnType.In(1).ConvertibleTo(eventType) {
return fmt.Errorf("%s; cannot convert parameter 2 from %s to event.Event", inParamUsage, fnType.In(1))
} else {
r.hasEventIn = true
}
fallthrough
case 1:
if !fnType.In(0).ConvertibleTo(contextType) {
if !fnType.In(0).ConvertibleTo(eventType) {
return fmt.Errorf("%s; cannot convert parameter 1 from %s to 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
}

View File

@ -1,105 +0,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
}

View File

@ -1,5 +0,0 @@
/*
Package context holds the last resort overrides and fyi objects that can be passed to clients and transports added to
context.Context objects.
*/
package context

View File

@ -1,43 +0,0 @@
package context
import (
"context"
"go.uber.org/zap"
)
// Opaque key type used to store logger
type loggerKeyType struct{}
var loggerKey = loggerKeyType{}
// fallbackLogger is the logger is used when there is no logger attached to the context.
var fallbackLogger *zap.SugaredLogger
func init() {
if logger, err := zap.NewProduction(); err != nil {
// We failed to create a fallback logger.
fallbackLogger = zap.NewNop().Sugar()
} else {
fallbackLogger = logger.Named("fallback").Sugar()
}
}
// WithLogger returns a new context with the logger injected into the given context.
func WithLogger(ctx context.Context, logger *zap.SugaredLogger) context.Context {
if logger == nil {
return context.WithValue(ctx, loggerKey, fallbackLogger)
}
return context.WithValue(ctx, loggerKey, logger)
}
// LoggerFrom returns the logger stored in context.
func LoggerFrom(ctx context.Context) *zap.SugaredLogger {
l := ctx.Value(loggerKey)
if l != nil {
if logger, ok := l.(*zap.SugaredLogger); ok {
return logger
}
}
return fallbackLogger
}

View File

@ -1,71 +0,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
}

View File

@ -1,42 +0,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
}

View File

@ -1,11 +0,0 @@
package event
const (
Base64 = "base64"
)
// StringOfBase64 returns a string pointer to "Base64"
func StringOfBase64() *string {
a := Base64
return &a
}

View File

@ -1,73 +0,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)
}

View File

@ -1,50 +0,0 @@
package datacodec
import (
"context"
"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"
"github.com/cloudevents/sdk-go/v2/observability"
)
func SetObservedCodecs() {
AddDecoder("", json.DecodeObserved)
AddDecoder("application/json", json.DecodeObserved)
AddDecoder("text/json", json.DecodeObserved)
AddDecoder("application/xml", xml.DecodeObserved)
AddDecoder("text/xml", xml.DecodeObserved)
AddDecoder("text/plain", text.DecodeObserved)
AddEncoder("", json.Encode)
AddEncoder("application/json", json.EncodeObserved)
AddEncoder("text/json", json.EncodeObserved)
AddEncoder("application/xml", xml.EncodeObserved)
AddEncoder("text/xml", xml.EncodeObserved)
AddEncoder("text/plain", text.EncodeObserved)
}
// DecodeObserved calls Decode and records the result.
func DecodeObserved(ctx context.Context, contentType string, in []byte, out interface{}) error {
_, r := observability.NewReporter(ctx, reportDecode)
err := Decode(ctx, contentType, in, out)
if err != nil {
r.Error()
} else {
r.OK()
}
return err
}
// EncodeObserved calls Encode and records the result.
func EncodeObserved(ctx context.Context, contentType string, in interface{}) ([]byte, error) {
_, r := observability.NewReporter(ctx, reportEncode)
b, err := Encode(ctx, contentType, in)
if err != nil {
r.Error()
} else {
r.OK()
}
return b, err
}

View File

@ -1,5 +0,0 @@
/*
Package datacodec holds the data codec registry and adds known encoders and decoders supporting media types such as
`application/json` and `application/xml`.
*/
package datacodec

View File

@ -1,51 +0,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)
}

View File

@ -1,31 +0,0 @@
package json
import (
"context"
"github.com/cloudevents/sdk-go/v2/observability"
)
// DecodeObserved calls Decode and records the results.
func DecodeObserved(ctx context.Context, in []byte, out interface{}) error {
_, r := observability.NewReporter(ctx, reportDecode)
err := Decode(ctx, in, out)
if err != nil {
r.Error()
} else {
r.OK()
}
return err
}
// EncodeObserved calls Encode and records the results.
func EncodeObserved(ctx context.Context, in interface{}) ([]byte, error) {
_, r := observability.NewReporter(ctx, reportEncode)
b, err := Encode(ctx, in)
if err != nil {
r.Error()
} else {
r.OK()
}
return b, err
}

View File

@ -1,4 +0,0 @@
/*
Package json holds the encoder/decoder implementation for `application/json`.
*/
package json

View File

@ -1,51 +0,0 @@
package json
import (
"github.com/cloudevents/sdk-go/v2/observability"
"go.opencensus.io/stats"
"go.opencensus.io/stats/view"
)
var (
// LatencyMs measures the latency in milliseconds for the CloudEvents json
// data codec methods.
LatencyMs = stats.Float64("cloudevents.io/sdk-go/datacodec/json/latency", "The latency in milliseconds for the CloudEvents json data codec methods.", "ms")
)
var (
// LatencyView is an OpenCensus view that shows data codec json method latency.
LatencyView = &view.View{
Name: "datacodec/json/latency",
Measure: LatencyMs,
Description: "The distribution of latency inside of the json data codec for CloudEvents.",
Aggregation: view.Distribution(0, .01, .1, 1, 10, 100, 1000, 10000),
TagKeys: observability.LatencyTags(),
}
)
type observed int32
// Adheres to Observable
var _ observability.Observable = observed(0)
const (
reportEncode observed = iota
reportDecode
)
// MethodName implements Observable.MethodName
func (o observed) MethodName() string {
switch o {
case reportEncode:
return "encode"
case reportDecode:
return "decode"
default:
return "unknown"
}
}
// LatencyMs implements Observable.LatencyMs
func (o observed) LatencyMs() *stats.Float64Measure {
return LatencyMs
}

View File

@ -1,51 +0,0 @@
package datacodec
import (
"github.com/cloudevents/sdk-go/v2/observability"
"go.opencensus.io/stats"
"go.opencensus.io/stats/view"
)
var (
// LatencyMs measures the latency in milliseconds for the CloudEvents generic
// codec data methods.
LatencyMs = stats.Float64("cloudevents.io/sdk-go/datacodec/latency", "The latency in milliseconds for the CloudEvents generic data codec methods.", "ms")
)
var (
// LatencyView is an OpenCensus view that shows data codec method latency.
LatencyView = &view.View{
Name: "datacodec/latency",
Measure: LatencyMs,
Description: "The distribution of latency inside of the generic data codec for CloudEvents.",
Aggregation: view.Distribution(0, .01, .1, 1, 10, 100, 1000, 10000),
TagKeys: observability.LatencyTags(),
}
)
type observed int32
// Adheres to Observable
var _ observability.Observable = observed(0)
const (
reportEncode observed = iota
reportDecode
)
// MethodName implements Observable.MethodName
func (o observed) MethodName() string {
switch o {
case reportEncode:
return "encode"
case reportDecode:
return "decode"
default:
return "unknown"
}
}
// LatencyMs implements Observable.LatencyMs
func (o observed) LatencyMs() *stats.Float64Measure {
return LatencyMs
}

View File

@ -1,25 +0,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
}

View File

@ -1,31 +0,0 @@
package text
import (
"context"
"github.com/cloudevents/sdk-go/v2/observability"
)
// DecodeObserved calls Decode and records the results.
func DecodeObserved(ctx context.Context, in []byte, out interface{}) error {
_, r := observability.NewReporter(ctx, reportDecode)
err := Decode(ctx, in, out)
if err != nil {
r.Error()
} else {
r.OK()
}
return err
}
// EncodeObserved calls Encode and records the results.
func EncodeObserved(ctx context.Context, in interface{}) ([]byte, error) {
_, r := observability.NewReporter(ctx, reportEncode)
b, err := Encode(ctx, in)
if err != nil {
r.Error()
} else {
r.OK()
}
return b, err
}

View File

@ -1,4 +0,0 @@
/*
Package text holds the encoder/decoder implementation for `text/plain`.
*/
package text

View File

@ -1,51 +0,0 @@
package text
import (
"github.com/cloudevents/sdk-go/v2/observability"
"go.opencensus.io/stats"
"go.opencensus.io/stats/view"
)
var (
// LatencyMs measures the latency in milliseconds for the CloudEvents xml data
// codec methods.
LatencyMs = stats.Float64("cloudevents.io/sdk-go/datacodec/text/latency", "The latency in milliseconds for the CloudEvents text data codec methods.", "ms")
)
var (
// LatencyView is an OpenCensus view that shows data codec xml method latency.
LatencyView = &view.View{
Name: "datacodec/text/latency",
Measure: LatencyMs,
Description: "The distribution of latency inside of the text data codec for CloudEvents.",
Aggregation: view.Distribution(0, .01, .1, 1, 10, 100, 1000, 10000),
TagKeys: observability.LatencyTags(),
}
)
type observed int32
// Adheres to Observable
var _ observability.Observable = observed(0)
const (
reportEncode observed = iota
reportDecode
)
// MethodName implements Observable.MethodName
func (o observed) MethodName() string {
switch o {
case reportEncode:
return "encode"
case reportDecode:
return "decode"
default:
return "unknown"
}
}
// LatencyMs implements Observable.LatencyMs
func (o observed) LatencyMs() *stats.Float64Measure {
return LatencyMs
}

View File

@ -1,35 +0,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)
}

View File

@ -1,31 +0,0 @@
package xml
import (
"context"
"github.com/cloudevents/sdk-go/v2/observability"
)
// DecodeObserved calls Decode and records the result.
func DecodeObserved(ctx context.Context, in []byte, out interface{}) error {
_, r := observability.NewReporter(ctx, reportDecode)
err := Decode(ctx, in, out)
if err != nil {
r.Error()
} else {
r.OK()
}
return err
}
// EncodeObserved calls Encode and records the result.
func EncodeObserved(ctx context.Context, in interface{}) ([]byte, error) {
_, r := observability.NewReporter(ctx, reportEncode)
b, err := Encode(ctx, in)
if err != nil {
r.Error()
} else {
r.OK()
}
return b, err
}

View File

@ -1,4 +0,0 @@
/*
Package xml holds the encoder/decoder implementation for `application/xml`.
*/
package xml

View File

@ -1,51 +0,0 @@
package xml
import (
"github.com/cloudevents/sdk-go/v2/observability"
"go.opencensus.io/stats"
"go.opencensus.io/stats/view"
)
var (
// LatencyMs measures the latency in milliseconds for the CloudEvents xml data
// codec methods.
LatencyMs = stats.Float64("cloudevents.io/sdk-go/datacodec/xml/latency", "The latency in milliseconds for the CloudEvents xml data codec methods.", "ms")
)
var (
// LatencyView is an OpenCensus view that shows data codec xml method latency.
LatencyView = &view.View{
Name: "datacodec/xml/latency",
Measure: LatencyMs,
Description: "The distribution of latency inside of the xml data codec for CloudEvents.",
Aggregation: view.Distribution(0, .01, .1, 1, 10, 100, 1000, 10000),
TagKeys: observability.LatencyTags(),
}
)
type observed int32
// Adheres to Observable
var _ observability.Observable = observed(0)
const (
reportEncode observed = iota
reportDecode
)
// MethodName implements Observable.MethodName
func (o observed) MethodName() string {
switch o {
case reportEncode:
return "encode"
case reportDecode:
return "decode"
default:
return "unknown"
}
}
// LatencyMs implements Observable.LatencyMs
func (o observed) LatencyMs() *stats.Float64Measure {
return LatencyMs
}

View File

@ -1,4 +0,0 @@
/*
Package event provides primitives to work with CloudEvents specification: https://github.com/cloudevents/spec.
*/
package event

View File

@ -1,158 +0,0 @@
package event
import (
"bytes"
"encoding/json"
"fmt"
"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)
}
// Validate performs a spec based validation on this event.
// Validation is dependent on the spec version specified in the event context.
func (e Event) Validate() error {
if e.Context == nil {
return fmt.Errorf("every event conforming to the CloudEvents specification MUST include a context")
}
if e.FieldErrors != nil {
errs := make([]string, 0)
for f, e := range e.FieldErrors {
errs = append(errs, fmt.Sprintf("%q: %s,", f, e))
}
if len(errs) > 0 {
return fmt.Errorf("previous field errors: [%s]", strings.Join(errs, "\n"))
}
}
if err := e.Context.Validate(); err != nil {
return err
}
return nil
}
// String returns a pretty-printed representation of the Event.
func (e Event) String() string {
b := strings.Builder{}
b.WriteString("Validation: ")
valid := e.Validate()
if valid == nil {
b.WriteString("valid\n")
} else {
b.WriteString("invalid\n")
}
if valid != nil {
b.WriteString(fmt.Sprintf("Validation Error: \n%s\n", valid.Error()))
}
b.WriteString(e.Context.String())
if e.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
}

View File

@ -1,113 +0,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.
// data 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
}

View File

@ -1,97 +0,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
}

View File

@ -1,327 +0,0 @@
package event
import (
"context"
"encoding/json"
"errors"
"fmt"
"strings"
errors2 "github.com/pkg/errors"
"github.com/cloudevents/sdk-go/v2/observability"
)
// MarshalJSON implements a custom json marshal method used when this type is
// marshaled using json.Marshal.
func (e Event) MarshalJSON() ([]byte, error) {
_, r := observability.NewReporter(context.Background(), eventJSONObserved{o: reportMarshal, v: e.SpecVersion()})
if err := e.Validate(); err != nil {
r.Error()
return nil, err
}
var b []byte
var err error
switch e.SpecVersion() {
case CloudEventsVersionV03:
b, err = JsonEncodeLegacy(e)
case CloudEventsVersionV1:
b, err = JsonEncode(e)
default:
return nil, fmt.Errorf("unknown spec version: %q", e.SpecVersion())
}
// Report the observable
if err != nil {
r.Error()
return nil, err
} else {
r.OK()
}
return b, nil
}
// UnmarshalJSON implements the json unmarshal method used when this type is
// unmarshaled using json.Unmarshal.
func (e *Event) UnmarshalJSON(b []byte) error {
raw := make(map[string]json.RawMessage)
if err := json.Unmarshal(b, &raw); err != nil {
return err
}
version := versionFromRawMessage(raw)
_, r := observability.NewReporter(context.Background(), eventJSONObserved{o: reportUnmarshal, v: version})
var err error
switch version {
case CloudEventsVersionV03:
err = e.JsonDecodeV03(b, raw)
case CloudEventsVersionV1:
err = e.JsonDecodeV1(b, raw)
default:
return fmt.Errorf("unknown spec version: %q", version)
}
// Report the observable
if err != nil {
r.Error()
return err
} else {
r.OK()
}
return nil
}
func versionFromRawMessage(raw map[string]json.RawMessage) string {
// v0.2 and after
if v, ok := raw["specversion"]; ok {
var version string
if err := json.Unmarshal(v, &version); err != nil {
return ""
}
return version
}
return ""
}
// JsonEncode encodes an event to JSON
func JsonEncode(e Event) ([]byte, error) {
return jsonEncode(e.Context, e.DataEncoded, e.DataBase64)
}
// JsonEncodeLegacy performs legacy JSON encoding
func JsonEncodeLegacy(e Event) ([]byte, error) {
isBase64 := e.Context.DeprecatedGetDataContentEncoding() == Base64
return jsonEncode(e.Context, e.DataEncoded, isBase64)
}
func jsonEncode(ctx EventContextReader, data []byte, shouldEncodeToBase64 bool) ([]byte, error) {
var b map[string]json.RawMessage
var err error
b, err = marshalEvent(ctx, ctx.GetExtensions())
if err != nil {
return nil, err
}
if data != nil {
// data here is a serialized version of whatever payload.
// If we need to write the payload as base64, shouldEncodeToBase64 is true.
mediaType, err := ctx.GetDataMediaType()
if err != nil {
return nil, err
}
isJson := mediaType == "" || mediaType == ApplicationJSON || mediaType == TextJSON
// If isJson and no encoding to base64, we don't need to perform additional steps
if isJson && !shouldEncodeToBase64 {
b["data"] = data
} else {
var dataKey = "data"
if ctx.GetSpecVersion() == CloudEventsVersionV1 && shouldEncodeToBase64 {
dataKey = "data_base64"
}
var dataPointer []byte
if shouldEncodeToBase64 {
dataPointer, err = json.Marshal(data)
} else {
dataPointer, err = json.Marshal(string(data))
}
if err != nil {
return nil, err
}
b[dataKey] = dataPointer
}
}
body, err := json.Marshal(b)
if err != nil {
return nil, err
}
return body, nil
}
// JsonDecodeV03 takes in the byte representation of a version 0.3 structured json CloudEvent and returns a
// cloudevent.Event or an error if there are parsing errors.
func (e *Event) JsonDecodeV03(body []byte, raw map[string]json.RawMessage) error {
ec := EventContextV03{}
if err := json.Unmarshal(body, &ec); err != nil {
return err
}
delete(raw, "specversion")
delete(raw, "type")
delete(raw, "source")
delete(raw, "subject")
delete(raw, "id")
delete(raw, "time")
delete(raw, "schemaurl")
delete(raw, "datacontenttype")
delete(raw, "datacontentencoding")
var data []byte
if d, ok := raw["data"]; ok {
data = d
// Decode the Base64 if we have a base64 payload
if ec.DeprecatedGetDataContentEncoding() == Base64 {
var tmp []byte
if err := json.Unmarshal(d, &tmp); err != nil {
return err
}
e.DataBase64 = true
e.DataEncoded = tmp
} else {
if ec.DataContentType != nil {
ct := *ec.DataContentType
if ct != ApplicationJSON && ct != TextJSON {
var dataStr string
err := json.Unmarshal(d, &dataStr)
if err != nil {
return err
}
data = []byte(dataStr)
}
}
e.DataEncoded = data
e.DataBase64 = false
}
}
delete(raw, "data")
if len(raw) > 0 {
extensions := make(map[string]interface{}, len(raw))
ec.Extensions = extensions
for k, v := range raw {
k = strings.ToLower(k)
var tmp interface{}
if err := json.Unmarshal(v, &tmp); err != nil {
return err
}
if err := ec.SetExtension(k, tmp); err != nil {
return errors2.Wrap(err, "Cannot set extension with key "+k)
}
}
}
e.Context = &ec
return nil
}
// JsonDecodeV1 takes in the byte representation of a version 1.0 structured json CloudEvent and returns a
// cloudevent.Event or an error if there are parsing errors.
func (e *Event) JsonDecodeV1(body []byte, raw map[string]json.RawMessage) error {
ec := EventContextV1{}
if err := json.Unmarshal(body, &ec); err != nil {
return err
}
delete(raw, "specversion")
delete(raw, "type")
delete(raw, "source")
delete(raw, "subject")
delete(raw, "id")
delete(raw, "time")
delete(raw, "dataschema")
delete(raw, "datacontenttype")
var data []byte
if d, ok := raw["data"]; ok {
data = d
if ec.DataContentType != nil {
ct := *ec.DataContentType
if ct != ApplicationJSON && ct != TextJSON {
var dataStr string
err := json.Unmarshal(d, &dataStr)
if err != nil {
return err
}
data = []byte(dataStr)
}
}
}
delete(raw, "data")
var dataBase64 []byte
if d, ok := raw["data_base64"]; ok {
var tmp []byte
if err := json.Unmarshal(d, &tmp); err != nil {
return err
}
dataBase64 = tmp
}
delete(raw, "data_base64")
if data != nil && dataBase64 != nil {
return errors.New("parsing error: JSON decoder found both 'data', and 'data_base64' in JSON payload")
}
if data != nil {
e.DataEncoded = data
e.DataBase64 = false
} else if dataBase64 != nil {
e.DataEncoded = dataBase64
e.DataBase64 = true
}
if len(raw) > 0 {
extensions := make(map[string]interface{}, len(raw))
ec.Extensions = extensions
for k, v := range raw {
k = strings.ToLower(k)
var tmp interface{}
if err := json.Unmarshal(v, &tmp); err != nil {
return err
}
if err := ec.SetExtension(k, tmp); err != nil {
return errors2.Wrap(err, "Cannot set extension with key "+k)
}
}
}
e.Context = &ec
return nil
}
func marshalEvent(eventCtx EventContextReader, extensions map[string]interface{}) (map[string]json.RawMessage, error) {
b, err := json.Marshal(eventCtx)
if err != nil {
return nil, err
}
brm := map[string]json.RawMessage{}
if err := json.Unmarshal(b, &brm); err != nil {
return nil, err
}
sv, err := json.Marshal(eventCtx.GetSpecVersion())
if err != nil {
return nil, err
}
brm["specversion"] = sv
for k, v := range extensions {
k = strings.ToLower(k)
vb, err := json.Marshal(v)
if err != nil {
return nil, err
}
// Don't overwrite spec keys.
if _, ok := brm[k]; !ok {
brm[k] = vb
}
}
return brm, nil
}

View File

@ -1,77 +0,0 @@
package event
import (
"fmt"
"github.com/cloudevents/sdk-go/v2/observability"
"go.opencensus.io/stats"
"go.opencensus.io/stats/view"
)
var (
// EventMarshalLatencyMs measures the latency in milliseconds for the
// CloudEvents.Event marshal/unmarshalJSON methods.
EventMarshalLatencyMs = stats.Float64(
"cloudevents.io/sdk-go/event/json/latency",
"The latency in milliseconds of (un)marshalJSON methods for CloudEvents.Event.",
"ms")
)
var (
// LatencyView is an OpenCensus view that shows CloudEvents.Event (un)marshalJSON method latency.
EventMarshalLatencyView = &view.View{
Name: "event/json/latency",
Measure: EventMarshalLatencyMs,
Description: "The distribution of latency inside of (un)marshalJSON methods for CloudEvents.Event.",
Aggregation: view.Distribution(0, .01, .1, 1, 10, 100, 1000, 10000),
TagKeys: observability.LatencyTags(),
}
)
type observed int32
// Adheres to Observable
var _ observability.Observable = observed(0)
const (
reportMarshal observed = iota
reportUnmarshal
)
// MethodName implements Observable.MethodName
func (o observed) MethodName() string {
switch o {
case reportMarshal:
return "marshaljson"
case reportUnmarshal:
return "unmarshaljson"
default:
return "unknown"
}
}
// LatencyMs implements Observable.LatencyMs
func (o observed) LatencyMs() *stats.Float64Measure {
return EventMarshalLatencyMs
}
// eventJSONObserved is a wrapper to append version to observed.
type eventJSONObserved struct {
// Method
o observed
// Version
v string
}
// Adheres to Observable
var _ observability.Observable = (*eventJSONObserved)(nil)
// MethodName implements Observable.MethodName
func (c eventJSONObserved) MethodName() string {
return fmt.Sprintf("%s/%s", c.o.MethodName(), c.v)
}
// LatencyMs implements Observable.LatencyMs
func (c eventJSONObserved) LatencyMs() *stats.Float64Measure {
return c.o.LatencyMs()
}

View File

@ -1,98 +0,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)
}

View File

@ -1,112 +0,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)
}
}

View File

@ -1,120 +0,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() error
// Clone clones the event context.
Clone() EventContext
// String returns a pretty-printed representation of the EventContext.
String() string
}

View File

@ -1,307 +0,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"
)
// 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.
func (ec *EventContextV03) SetExtension(name string, value interface{}) error {
if ec.Extensions == nil {
ec.Extensions = make(map[string]interface{})
}
if value == nil {
delete(ec.Extensions, name)
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() error {
errors := []string(nil)
// type
// Type: String
// Constraints:
// REQUIRED
// MUST be a non-empty string
// SHOULD be prefixed with a reverse-DNS name. The prefixed domain dictates the organization which defines the semantics of this event type.
eventType := strings.TrimSpace(ec.Type)
if eventType == "" {
errors = append(errors, "type: MUST be a non-empty string")
}
// source
// Type: URI-reference
// Constraints:
// REQUIRED
source := strings.TrimSpace(ec.Source.String())
if source == "" {
errors = append(errors, "source: REQUIRED")
}
// subject
// Type: String
// Constraints:
// OPTIONAL
// MUST be a non-empty string
if ec.Subject != nil {
subject := strings.TrimSpace(*ec.Subject)
if subject == "" {
errors = append(errors, "subject: if present, MUST be a non-empty string")
}
}
// id
// Type: String
// Constraints:
// REQUIRED
// MUST be a non-empty string
// MUST be unique within the scope of the producer
id := strings.TrimSpace(ec.ID)
if id == "" {
errors = append(errors, "id: MUST be a non-empty string")
// no way to test "MUST be unique within the scope of the producer"
}
// time
// Type: Timestamp
// Constraints:
// OPTIONAL
// If present, MUST adhere to the format specified in RFC 3339
// --> no need to test this, no way to set the time without it being valid.
// schemaurl
// Type: URI
// Constraints:
// OPTIONAL
// If present, MUST adhere to the format specified in RFC 3986
if ec.SchemaURL != nil {
schemaURL := strings.TrimSpace(ec.SchemaURL.String())
// empty string is not RFC 3986 compatible.
if schemaURL == "" {
errors = append(errors, "schemaurl: if present, MUST adhere to the format specified in RFC 3986")
}
}
// datacontenttype
// Type: String per RFC 2046
// Constraints:
// OPTIONAL
// If present, MUST adhere to the format specified in RFC 2046
if ec.DataContentType != nil {
dataContentType := strings.TrimSpace(*ec.DataContentType)
if dataContentType == "" {
errors = append(errors, "datacontenttype: if present, MUST adhere to the format specified in RFC 2046")
} else {
_, _, err := mime.ParseMediaType(dataContentType)
if err != nil {
errors = append(errors, fmt.Sprintf("datacontenttype: failed to parse RFC 2046 media type, %s", err.Error()))
}
}
}
// 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 = append(errors, "datacontentencoding: if present, MUST adhere to RFC 2045 Section 6.1")
}
}
if len(errors) > 0 {
return fmt.Errorf(strings.Join(errors, "\n"))
}
return nil
}
// String returns a pretty-printed representation of the EventContext.
func (ec EventContextV03) String() string {
b := strings.Builder{}
b.WriteString("Context Attributes,\n")
b.WriteString(" specversion: " + 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()
}

View File

@ -1,93 +0,0 @@
package event
import (
"fmt"
"mime"
"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 {
mediaType, _, err := mime.ParseMediaType(*ec.DataContentType)
if err != nil {
return "", err
}
return mediaType, nil
}
return "", nil
}
// GetType implements EventContextReader.GetType
func (ec EventContextV03) GetType() string {
return ec.Type
}
// GetSource implements EventContextReader.GetSource
func (ec EventContextV03) GetSource() string {
return ec.Source.String()
}
// GetSubject implements EventContextReader.GetSubject
func (ec EventContextV03) GetSubject() string {
if ec.Subject != nil {
return *ec.Subject
}
return ""
}
// GetTime implements EventContextReader.GetTime
func (ec EventContextV03) GetTime() time.Time {
if ec.Time != nil {
return ec.Time.Time
}
return time.Time{}
}
// GetID implements EventContextReader.GetID
func (ec EventContextV03) GetID() string {
return ec.ID
}
// GetDataSchema implements EventContextReader.GetDataSchema
func (ec EventContextV03) GetDataSchema() string {
if ec.SchemaURL != nil {
return ec.SchemaURL.String()
}
return ""
}
// DeprecatedGetDataContentEncoding implements EventContextReader.DeprecatedGetDataContentEncoding
func (ec EventContextV03) DeprecatedGetDataContentEncoding() string {
if ec.DataContentEncoding != nil {
return *ec.DataContentEncoding
}
return ""
}
// GetExtensions implements EventContextReader.GetExtensions
func (ec EventContextV03) GetExtensions() map[string]interface{} {
return ec.Extensions
}
// GetExtension implements EventContextReader.GetExtension
func (ec EventContextV03) GetExtension(key string) (interface{}, error) {
v, ok := caseInsensitiveSearch(key, ec.Extensions)
if !ok {
return "", fmt.Errorf("%q not found", key)
}
return v, nil
}

View File

@ -1,98 +0,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
}

View File

@ -1,297 +0,0 @@
package event
import (
"errors"
"fmt"
"mime"
"sort"
"strings"
"github.com/cloudevents/sdk-go/v2/types"
)
// WIP: AS OF SEP 20, 2019
const (
// CloudEventsVersionV1 represents the version 1.0 of the CloudEvents spec.
CloudEventsVersionV1 = "1.0"
)
// EventContextV1 represents the non-data attributes of a CloudEvents v1.0
// event.
type EventContextV1 struct {
// ID of the event; must be non-empty and unique within the scope of the producer.
// +required
ID string `json:"id"`
// Source - A URI describing the event producer.
// +required
Source types.URIRef `json:"source"`
// Type - The type of the occurrence which has happened.
// +required
Type string `json:"type"`
// DataContentType - A MIME (RFC2046) string describing the media type of `data`.
// +optional
DataContentType *string `json:"datacontenttype,omitempty"`
// Subject - The subject of the event in the context of the event producer
// (identified by `source`).
// +optional
Subject *string `json:"subject,omitempty"`
// Time - A Timestamp when the event happened.
// +optional
Time *types.Timestamp `json:"time,omitempty"`
// DataSchema - A link to the schema that the `data` attribute adheres to.
// +optional
DataSchema *types.URI `json:"dataschema,omitempty"`
// Extensions - Additional extension metadata beyond the base spec.
// +optional
Extensions map[string]interface{} `json:"-"`
}
// Adhere to EventContext
var _ EventContext = (*EventContextV1)(nil)
// ExtensionAs implements EventContext.ExtensionAs
func (ec EventContextV1) ExtensionAs(name string, obj interface{}) error {
name = strings.ToLower(name)
value, ok := ec.Extensions[name]
if !ok {
return fmt.Errorf("extension %q does not exist", name)
}
// Only support *string for now.
if v, ok := obj.(*string); ok {
if *v, ok = value.(string); ok {
return nil
}
}
return fmt.Errorf("unknown extension type %T", obj)
}
// SetExtension adds the extension 'name' with value 'value' to the CloudEvents context.
// This function fails if the name doesn't respect the regex ^[a-zA-Z0-9]+$
func (ec *EventContextV1) SetExtension(name string, value interface{}) error {
if !IsAlphaNumeric(name) {
return errors.New("bad key, CloudEvents attribute names MUST consist of lower-case letters ('a' to 'z') or digits ('0' to '9') from the ASCII character set")
}
name = strings.ToLower(name)
if ec.Extensions == nil {
ec.Extensions = make(map[string]interface{})
}
if value == nil {
delete(ec.Extensions, name)
if len(ec.Extensions) == 0 {
ec.Extensions = nil
}
return nil
} else {
v, err := types.Validate(value) // Ensure it's a legal CE attribute value
if err == nil {
ec.Extensions[name] = v
}
return err
}
}
// Clone implements EventContextConverter.Clone
func (ec EventContextV1) Clone() EventContext {
ec1 := ec.AsV1()
ec1.Source = types.Clone(ec.Source).(types.URIRef)
if ec.Time != nil {
ec1.Time = types.Clone(ec.Time).(*types.Timestamp)
}
if ec.DataSchema != nil {
ec1.DataSchema = types.Clone(ec.DataSchema).(*types.URI)
}
ec1.Extensions = ec.cloneExtensions()
return ec1
}
func (ec *EventContextV1) cloneExtensions() map[string]interface{} {
old := ec.Extensions
if old == nil {
return nil
}
new := make(map[string]interface{}, len(ec.Extensions))
for k, v := range old {
new[k] = types.Clone(v)
}
return new
}
// AsV03 implements EventContextConverter.AsV03
func (ec EventContextV1) AsV03() *EventContextV03 {
ret := EventContextV03{
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.DataSchema != nil {
ret.SchemaURL = &types.URIRef{URL: ec.DataSchema.URL}
}
if ec.Extensions != nil {
for k, v := range ec.Extensions {
k = strings.ToLower(k)
// DeprecatedDataContentEncoding was introduced in 0.3, removed in 1.0
if strings.EqualFold(k, DataContentEncodingKey) {
etv, ok := v.(string)
if ok && etv != "" {
ret.DataContentEncoding = &etv
}
continue
}
ret.Extensions[k] = v
}
}
if len(ret.Extensions) == 0 {
ret.Extensions = nil
}
return &ret
}
// AsV1 implements EventContextConverter.AsV1
func (ec EventContextV1) AsV1() *EventContextV1 {
return &ec
}
// Validate returns errors based on requirements from the CloudEvents spec.
// For more details, see https://github.com/cloudevents/spec/blob/v1.0-rc1/spec.md.
func (ec EventContextV1) Validate() error {
errors := []string(nil)
// id
// Type: String
// Constraints:
// REQUIRED
// MUST be a non-empty string
// MUST be unique within the scope of the producer
id := strings.TrimSpace(ec.ID)
if id == "" {
errors = append(errors, "id: MUST be a non-empty string")
// no way to test "MUST be unique within the scope of the producer"
}
// source
// Type: URI-reference
// Constraints:
// REQUIRED
// MUST be a non-empty URI-reference
// An absolute URI is RECOMMENDED
source := strings.TrimSpace(ec.Source.String())
if source == "" {
errors = append(errors, "source: REQUIRED")
}
// type
// Type: String
// Constraints:
// REQUIRED
// MUST be a non-empty string
// SHOULD be prefixed with a reverse-DNS name. The prefixed domain dictates the organization which defines the semantics of this event type.
eventType := strings.TrimSpace(ec.Type)
if eventType == "" {
errors = append(errors, "type: MUST be a non-empty string")
}
// The following attributes are optional but still have validation.
// datacontenttype
// Type: String per RFC 2046
// Constraints:
// OPTIONAL
// If present, MUST adhere to the format specified in RFC 2046
if ec.DataContentType != nil {
dataContentType := strings.TrimSpace(*ec.DataContentType)
if dataContentType == "" {
errors = append(errors, "datacontenttype: if present, MUST adhere to the format specified in RFC 2046")
} else {
_, _, err := mime.ParseMediaType(dataContentType)
if err != nil {
errors = append(errors, fmt.Sprintf("datacontenttype: failed to parse RFC 2046 media type, %s", err.Error()))
}
}
}
// dataschema
// Type: URI
// Constraints:
// OPTIONAL
// If present, MUST adhere to the format specified in RFC 3986
if ec.DataSchema != nil {
dataSchema := strings.TrimSpace(ec.DataSchema.String())
// empty string is not RFC 3986 compatible.
if dataSchema == "" {
errors = append(errors, "dataschema: if present, MUST adhere to the format specified in RFC 3986")
}
}
// subject
// Type: String
// Constraints:
// OPTIONAL
// MUST be a non-empty string
if ec.Subject != nil {
subject := strings.TrimSpace(*ec.Subject)
if subject == "" {
errors = append(errors, "subject: if present, MUST be a non-empty string")
}
}
// time
// Type: Timestamp
// Constraints:
// OPTIONAL
// If present, MUST adhere to the format specified in RFC 3339
// --> no need to test this, no way to set the time without it being valid.
if len(errors) > 0 {
return fmt.Errorf(strings.Join(errors, "\n"))
}
return nil
}
// String returns a pretty-printed representation of the EventContext.
func (ec EventContextV1) String() string {
b := strings.Builder{}
b.WriteString("Context Attributes,\n")
b.WriteString(" specversion: " + CloudEventsVersionV1 + "\n")
b.WriteString(" type: " + ec.Type + "\n")
b.WriteString(" source: " + ec.Source.String() + "\n")
if ec.Subject != nil {
b.WriteString(" subject: " + *ec.Subject + "\n")
}
b.WriteString(" id: " + ec.ID + "\n")
if ec.Time != nil {
b.WriteString(" time: " + ec.Time.String() + "\n")
}
if ec.DataSchema != nil {
b.WriteString(" dataschema: " + ec.DataSchema.String() + "\n")
}
if ec.DataContentType != nil {
b.WriteString(" datacontenttype: " + *ec.DataContentType + "\n")
}
if ec.Extensions != nil && len(ec.Extensions) > 0 {
b.WriteString("Extensions,\n")
keys := make([]string, 0, len(ec.Extensions))
for k := range ec.Extensions {
keys = append(keys, k)
}
sort.Strings(keys)
for _, key := range keys {
b.WriteString(fmt.Sprintf(" %s: %v\n", key, ec.Extensions[key]))
}
}
return b.String()
}

View File

@ -1,98 +0,0 @@
package event
import (
"fmt"
"mime"
"time"
)
// GetSpecVersion implements EventContextReader.GetSpecVersion
func (ec EventContextV1) GetSpecVersion() string {
return CloudEventsVersionV1
}
// GetDataContentType implements EventContextReader.GetDataContentType
func (ec EventContextV1) GetDataContentType() string {
if ec.DataContentType != nil {
return *ec.DataContentType
}
return ""
}
// GetDataMediaType implements EventContextReader.GetDataMediaType
func (ec EventContextV1) GetDataMediaType() (string, error) {
if ec.DataContentType != nil {
mediaType, _, err := mime.ParseMediaType(*ec.DataContentType)
if err != nil {
return "", err
}
return mediaType, nil
}
return "", nil
}
// GetType implements EventContextReader.GetType
func (ec EventContextV1) GetType() string {
return ec.Type
}
// GetSource implements EventContextReader.GetSource
func (ec EventContextV1) GetSource() string {
return ec.Source.String()
}
// GetSubject implements EventContextReader.GetSubject
func (ec EventContextV1) GetSubject() string {
if ec.Subject != nil {
return *ec.Subject
}
return ""
}
// GetTime implements EventContextReader.GetTime
func (ec EventContextV1) GetTime() time.Time {
if ec.Time != nil {
return ec.Time.Time
}
return time.Time{}
}
// GetID implements EventContextReader.GetID
func (ec EventContextV1) GetID() string {
return ec.ID
}
// GetDataSchema implements EventContextReader.GetDataSchema
func (ec EventContextV1) GetDataSchema() string {
if ec.DataSchema != nil {
return ec.DataSchema.String()
}
return ""
}
// DeprecatedGetDataContentEncoding implements EventContextReader.DeprecatedGetDataContentEncoding
func (ec EventContextV1) DeprecatedGetDataContentEncoding() string {
return ""
}
// GetExtensions implements EventContextReader.GetExtensions
func (ec EventContextV1) GetExtensions() map[string]interface{} {
if len(ec.Extensions) == 0 {
return nil
}
// For now, convert the extensions of v1.0 to the pre-v1.0 style.
ext := make(map[string]interface{}, len(ec.Extensions))
for k, v := range ec.Extensions {
ext[k] = v
}
return ext
}
// GetExtension implements EventContextReader.GetExtension
func (ec EventContextV1) GetExtension(key string) (interface{}, error) {
v, ok := caseInsensitiveSearch(key, ec.Extensions)
if !ok {
return "", fmt.Errorf("%q not found", key)
}
return v, nil
}

View File

@ -1,92 +0,0 @@
package event
import (
"errors"
"net/url"
"strings"
"time"
"github.com/cloudevents/sdk-go/v2/types"
)
// Adhere to EventContextWriter
var _ EventContextWriter = (*EventContextV1)(nil)
// SetDataContentType implements EventContextWriter.SetDataContentType
func (ec *EventContextV1) SetDataContentType(ct string) error {
ct = strings.TrimSpace(ct)
if ct == "" {
ec.DataContentType = nil
} else {
ec.DataContentType = &ct
}
return nil
}
// SetType implements EventContextWriter.SetType
func (ec *EventContextV1) SetType(t string) error {
t = strings.TrimSpace(t)
ec.Type = t
return nil
}
// SetSource implements EventContextWriter.SetSource
func (ec *EventContextV1) SetSource(u string) error {
pu, err := url.Parse(u)
if err != nil {
return err
}
ec.Source = types.URIRef{URL: *pu}
return nil
}
// SetSubject implements EventContextWriter.SetSubject
func (ec *EventContextV1) SetSubject(s string) error {
s = strings.TrimSpace(s)
if s == "" {
ec.Subject = nil
} else {
ec.Subject = &s
}
return nil
}
// SetID implements EventContextWriter.SetID
func (ec *EventContextV1) SetID(id string) error {
id = strings.TrimSpace(id)
if id == "" {
return errors.New("id is required to be a non-empty string")
}
ec.ID = id
return nil
}
// SetTime implements EventContextWriter.SetTime
func (ec *EventContextV1) SetTime(t time.Time) error {
if t.IsZero() {
ec.Time = nil
} else {
ec.Time = &types.Timestamp{Time: t}
}
return nil
}
// SetDataSchema implements EventContextWriter.SetDataSchema
func (ec *EventContextV1) SetDataSchema(u string) error {
u = strings.TrimSpace(u)
if u == "" {
ec.DataSchema = nil
return nil
}
pu, err := url.Parse(u)
if err != nil {
return err
}
ec.DataSchema = &types.URI{URL: *pu}
return nil
}
// DeprecatedSetDataContentEncoding implements EventContextWriter.DeprecatedSetDataContentEncoding
func (ec *EventContextV1) DeprecatedSetDataContentEncoding(e string) error {
return errors.New("deprecated: SetDataContentEncoding is not supported in v1.0 of CloudEvents")
}

View File

@ -1,24 +0,0 @@
package event
import (
"regexp"
"strings"
)
const (
// DataContentEncodingKey is the key to DeprecatedDataContentEncoding for versions that do not support data content encoding
// directly.
DataContentEncodingKey = "datacontentencoding"
)
func caseInsensitiveSearch(key string, space map[string]interface{}) (interface{}, bool) {
lkey := strings.ToLower(key)
for k, v := range space {
if strings.EqualFold(lkey, strings.ToLower(k)) {
return v, true
}
}
return nil, false
}
var IsAlphaNumeric = regexp.MustCompile(`^[a-zA-Z0-9]+$`).MatchString

View File

@ -1,161 +0,0 @@
package extensions
import (
"context"
"reflect"
"strings"
"github.com/cloudevents/sdk-go/v2/binding"
"github.com/cloudevents/sdk-go/v2/event"
"github.com/cloudevents/sdk-go/v2/types"
"github.com/lightstep/tracecontext.go/traceparent"
"github.com/lightstep/tracecontext.go/tracestate"
"go.opencensus.io/trace"
octs "go.opencensus.io/trace/tracestate"
)
const (
TraceParentExtension = "traceparent"
TraceStateExtension = "tracestate"
)
// DistributedTracingExtension represents the extension for cloudevents context
type DistributedTracingExtension struct {
TraceParent string `json:"traceparent"`
TraceState string `json:"tracestate"`
}
// AddTracingAttributes adds the tracing attributes traceparent and tracestate to the cloudevents context
func (d DistributedTracingExtension) AddTracingAttributes(e event.EventWriter) {
if d.TraceParent != "" {
value := reflect.ValueOf(d)
typeOf := value.Type()
for i := 0; i < value.NumField(); i++ {
k := strings.ToLower(typeOf.Field(i).Name)
v := value.Field(i).Interface()
if k == TraceStateExtension && v == "" {
continue
}
e.SetExtension(k, v)
}
}
}
func GetDistributedTracingExtension(event event.Event) (DistributedTracingExtension, bool) {
if tp, ok := event.Extensions()[TraceParentExtension]; ok {
if tpStr, err := types.ToString(tp); err == nil {
var tsStr string
if ts, ok := event.Extensions()[TraceStateExtension]; ok {
tsStr, _ = types.ToString(ts)
}
return DistributedTracingExtension{TraceParent: tpStr, TraceState: tsStr}, true
}
}
return DistributedTracingExtension{}, false
}
func (d *DistributedTracingExtension) ReadTransformer() binding.TransformerFunc {
return func(reader binding.MessageMetadataReader, writer binding.MessageMetadataWriter) error {
tp := reader.GetExtension(TraceParentExtension)
if tp != nil {
tpFormatted, err := types.Format(tp)
if err != nil {
return err
}
d.TraceParent = tpFormatted
}
ts := reader.GetExtension(TraceStateExtension)
if ts != nil {
tsFormatted, err := types.Format(ts)
if err != nil {
return err
}
d.TraceState = tsFormatted
}
return nil
}
}
func (d *DistributedTracingExtension) WriteTransformer() binding.TransformerFunc {
return func(reader binding.MessageMetadataReader, writer binding.MessageMetadataWriter) error {
err := writer.SetExtension(TraceParentExtension, d.TraceParent)
if err != nil {
return nil
}
return writer.SetExtension(TraceStateExtension, d.TraceState)
}
}
// FromSpanContext populates DistributedTracingExtension from a SpanContext.
func FromSpanContext(sc trace.SpanContext) DistributedTracingExtension {
tp := traceparent.TraceParent{
TraceID: sc.TraceID,
SpanID: sc.SpanID,
Flags: traceparent.Flags{
Recorded: sc.IsSampled(),
},
}
entries := make([]string, 0, len(sc.Tracestate.Entries()))
for _, entry := range sc.Tracestate.Entries() {
entries = append(entries, strings.Join([]string{entry.Key, entry.Value}, "="))
}
return DistributedTracingExtension{
TraceParent: tp.String(),
TraceState: strings.Join(entries, ","),
}
}
// ToSpanContext creates a SpanContext from a DistributedTracingExtension instance.
func (d DistributedTracingExtension) ToSpanContext() (trace.SpanContext, error) {
tp, err := traceparent.ParseString(d.TraceParent)
if err != nil {
return trace.SpanContext{}, err
}
sc := trace.SpanContext{
TraceID: tp.TraceID,
SpanID: tp.SpanID,
}
if tp.Flags.Recorded {
sc.TraceOptions |= 1
}
if ts, err := tracestate.ParseString(d.TraceState); err == nil {
entries := make([]octs.Entry, 0, len(ts))
for _, member := range ts {
var key string
if member.Tenant != "" {
// Due to github.com/lightstep/tracecontext.go/issues/6,
// the meaning of Vendor and Tenant are swapped here.
key = member.Vendor + "@" + member.Tenant
} else {
key = member.Vendor
}
entries = append(entries, octs.Entry{Key: key, Value: member.Value})
}
sc.Tracestate, _ = octs.New(nil, entries...)
}
return sc, nil
}
func (d DistributedTracingExtension) StartChildSpan(ctx context.Context, name string, opts ...trace.StartOption) (context.Context, *trace.Span) {
if sc, err := d.ToSpanContext(); err == nil {
tSpan := trace.FromContext(ctx)
ctx, span := trace.StartSpanWithRemoteParent(ctx, name, sc, opts...)
if tSpan != nil {
// Add link to the previous in-process trace.
tsc := tSpan.SpanContext()
span.AddLink(trace.Link{
TraceID: tsc.TraceID,
SpanID: tsc.SpanID,
Type: trace.LinkTypeParent,
})
}
return ctx, span
}
return ctx, nil
}

View File

@ -1,2 +0,0 @@
// Package extensions provides implementations of common event extensions.
package extensions

View File

@ -1,29 +0,0 @@
module github.com/cloudevents/sdk-go/v2
require (
cloud.google.com/go v0.40.0
contrib.go.opencensus.io/exporter/prometheus v0.1.0
github.com/Azure/go-amqp v0.12.7
github.com/Shopify/sarama v1.19.0
github.com/cloudevents/sdk-go v1.1.2
github.com/cucumber/godog v0.9.0
github.com/cucumber/messages-go/v10 v10.0.3
github.com/google/go-cmp v0.4.0
github.com/google/uuid v1.1.1
github.com/gorilla/mux v1.6.2
github.com/kelseyhightower/envconfig v1.4.0
github.com/lightstep/tracecontext.go v0.0.0-20181129014701-1757c391b1ac
github.com/nats-io/nats-streaming-server v0.17.0 // indirect
github.com/nats-io/nats.go v1.9.1
github.com/nats-io/stan.go v0.6.0
github.com/pkg/errors v0.9.1
github.com/stretchr/testify v1.5.1
github.com/valyala/bytebufferpool v1.0.0
go.opencensus.io v0.22.0
go.uber.org/zap v1.10.0
golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e
google.golang.org/api v0.15.0
google.golang.org/grpc v1.26.0
)
go 1.13

View File

@ -1,385 +0,0 @@
cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
cloud.google.com/go v0.34.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
cloud.google.com/go v0.38.0 h1:ROfEUZz+Gh5pa62DJWXSaonyu3StP6EA6lPEXPI6mCo=
cloud.google.com/go v0.38.0/go.mod h1:990N+gfupTy94rShfmMCWGDn0LpTmnzTp2qbd1dvSRU=
cloud.google.com/go v0.40.0 h1:FjSY7bOj+WzJe6TZRVtXI2b9kAYvtNg4lMbcH2+MUkk=
cloud.google.com/go v0.40.0/go.mod h1:Tk58MuI9rbLMKlAjeO/bDnteAx7tX2gJIXw4T5Jwlro=
contrib.go.opencensus.io/exporter/ocagent v0.4.12 h1:jGFvw3l57ViIVEPKKEUXPcLYIXJmQxLUh6ey1eJhwyc=
contrib.go.opencensus.io/exporter/ocagent v0.4.12/go.mod h1:450APlNTSR6FrvC3CTRqYosuDstRB9un7SOx2k/9ckA=
contrib.go.opencensus.io/exporter/prometheus v0.1.0 h1:SByaIoWwNgMdPSgl5sMqM2KDE5H/ukPWBRo314xiDvg=
contrib.go.opencensus.io/exporter/prometheus v0.1.0/go.mod h1:cGFniUXGZlKRjzOyuZJ6mgB+PgBcCIa79kEKR8YCW+A=
github.com/Azure/azure-sdk-for-go v30.1.0+incompatible h1:HyYPft8wXpxMd0kfLtXo6etWcO+XuPbLkcgx9g2cqxU=
github.com/Azure/azure-sdk-for-go v30.1.0+incompatible/go.mod h1:9XXNKU+eRnpl9moKnB4QOLf1HestfXbmab5FXxiDBjc=
github.com/Azure/go-amqp v0.12.7 h1:/Uyqh30J5JrDFAOERQtEqP0qPWkrNXxr94vRnSa54Ac=
github.com/Azure/go-amqp v0.12.7/go.mod h1:qApuH6OFTSKZFmCOxccvAv5rLizBQf4v8pRmG138DPo=
github.com/Azure/go-autorest/autorest v0.2.0 h1:zBtSTOQTtjzHVRe+mhkiHvHwRTKHhjBEyo1m6DfI3So=
github.com/Azure/go-autorest/autorest v0.2.0/go.mod h1:AKyIcETwSUFxIcs/Wnq/C+kwCtlEYGUVd7FPNb2slmg=
github.com/Azure/go-autorest/autorest/adal v0.1.0 h1:RSw/7EAullliqwkZvgIGDYZWQm1PGKXI8c4aY/87yuU=
github.com/Azure/go-autorest/autorest/adal v0.1.0/go.mod h1:MeS4XhScH55IST095THyTxElntu7WqB7pNbZo8Q5G3E=
github.com/Azure/go-autorest/autorest/date v0.1.0 h1:YGrhWfrgtFs84+h0o46rJrlmsZtyZRg470CqAXTZaGM=
github.com/Azure/go-autorest/autorest/date v0.1.0/go.mod h1:plvfp3oPSKwf2DNjlBjWF/7vwR+cUD/ELuzDCXwHUVA=
github.com/Azure/go-autorest/autorest/mocks v0.1.0 h1:Kx+AUU2Te+A3JIyYn6Dfs+cFgx5XorQKuIXrZGoq/SI=
github.com/Azure/go-autorest/autorest/mocks v0.1.0/go.mod h1:OTyCOPRA2IgIlWxVYxBee2F5Gr4kF2zd2J5cFRaIDN0=
github.com/Azure/go-autorest/autorest/to v0.2.0 h1:nQOZzFCudTh+TvquAtCRjM01VEYx85e9qbwt5ncW4L8=
github.com/Azure/go-autorest/autorest/to v0.2.0/go.mod h1:GunWKJp1AEqgMaGLV+iocmRAJWqST1wQYhyyjXJ3SJc=
github.com/Azure/go-autorest/autorest/validation v0.1.0 h1:ISSNzGUh+ZSzizJWOWzs8bwpXIePbGLW4z/AmUFGH5A=
github.com/Azure/go-autorest/autorest/validation v0.1.0/go.mod h1:Ha3z/SqBeaalWQvokg3NZAlQTalVMtOIAs1aGK7G6u8=
github.com/Azure/go-autorest/logger v0.1.0 h1:ruG4BSDXONFRrZZJ2GUXDiUyVpayPmb1GnWeHDdaNKY=
github.com/Azure/go-autorest/logger v0.1.0/go.mod h1:oExouG+K6PryycPJfVSxi/koC6LSNgds39diKLz7Vrc=
github.com/Azure/go-autorest/tracing v0.1.0 h1:TRBxC5Pj/fIuh4Qob0ZpkggbfT8RC0SubHbpV3p4/Vc=
github.com/Azure/go-autorest/tracing v0.1.0/go.mod h1:ROEEAFwXycQw7Sn3DXNtEedEvdeRAgDr0izn4z5Ij88=
github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
github.com/DataDog/datadog-go v2.2.0+incompatible/go.mod h1:LButxg5PwREeZtORoXG3tL4fMGNddJ+vMq1mwgfaqoQ=
github.com/Shopify/sarama v1.19.0 h1:9oksLxC6uxVPHPVYUmq6xhr1BOF/hHobWH2UzO67z1s=
github.com/Shopify/sarama v1.19.0/go.mod h1:FVkBWblsNy7DGZRfXLU0O9RCGt5g3g3yEuWXgklEdEo=
github.com/Shopify/toxiproxy v2.1.4+incompatible h1:TKdv8HiTLgE5wdJuEML90aBgNWsokNbMijUGhmcoBJc=
github.com/Shopify/toxiproxy v2.1.4+incompatible/go.mod h1:OXgGpZ6Cli1/URJOF1DMxUHB2q5Ap20/P/eIdh4G0pI=
github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc=
github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0=
github.com/apache/thrift v0.12.0/go.mod h1:cp2SuWMxlEZw2r+iP2GNCdIi4C1qmUzdZFSVb+bacwQ=
github.com/armon/go-metrics v0.0.0-20190430140413-ec5e00d3c878 h1:EFSB7Zo9Eg91v7MJPVsifUysc/wPdN+NOnVe6bWbdBM=
github.com/armon/go-metrics v0.0.0-20190430140413-ec5e00d3c878/go.mod h1:3AMJUQhVx52RsWOnlkpikZr01T/yAVN2gn0861vByNg=
github.com/aslakhellesoy/gox v1.0.100/go.mod h1:AJl542QsKKG96COVsv0N74HHzVQgDIQPceVUh1aeU2M=
github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973 h1:xJ4a3vCFaGF/jqvzLMYoU8P317H5OQ+Via4RmuPwCS0=
github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q=
github.com/boltdb/bolt v1.3.1/go.mod h1:clJnj/oiGkjum5o1McbSZDSLxVThjynRyGBgiAx27Ps=
github.com/census-instrumentation/opencensus-proto v0.2.0 h1:LzQXZOgg4CQfE6bFvXGM30YZL1WW/M337pXml+GrcZ4=
github.com/census-instrumentation/opencensus-proto v0.2.0/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU=
github.com/census-instrumentation/opencensus-proto v0.2.1 h1:glEXhBS5PSLLv4IXzLA5yPRVX4bilULVyxxbrfOtDAk=
github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU=
github.com/circonus-labs/circonus-gometrics v2.3.1+incompatible/go.mod h1:nmEj6Dob7S7YxXgwXpfOuvO54S+tGdZdw9fuRZt25Ag=
github.com/circonus-labs/circonusllhist v0.1.3/go.mod h1:kMXHVDlOchFAehlya5ePtbp5jckzBHf4XRpQvBOLI+I=
github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw=
github.com/cloudevents/sdk-go v1.1.2 h1:mg/7d+BzubBPrPpH1bdeF85BQZYV85j7Ljqat3+m+qE=
github.com/cloudevents/sdk-go v1.1.2/go.mod h1:ss+jWJ88wypiewnPEzChSBzTYXGpdcILoN9YHk8uhTQ=
github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E=
github.com/cucumber/gherkin-go/v11 v11.0.0 h1:cwVwN1Qn2VRSfHZNLEh5x00tPBmZcjATBWDpxsR5Xug=
github.com/cucumber/gherkin-go/v11 v11.0.0/go.mod h1:CX33k2XU2qog4e+TFjOValoq6mIUq0DmVccZs238R9w=
github.com/cucumber/godog v0.9.0 h1:QOb8wyC7f+FVFXzY3RdgowwJUb4WeJfqbnQqaH4jp+A=
github.com/cucumber/godog v0.9.0/go.mod h1:roWCHkpeK6UTOyIRRl7IR+fgfBeZ4vZR7OSq2J/NbM4=
github.com/cucumber/messages-go/v10 v10.0.1/go.mod h1:kA5T38CBlBbYLU12TIrJ4fk4wSkVVOgyh7Enyy8WnSg=
github.com/cucumber/messages-go/v10 v10.0.3 h1:m/9SD/K/A15WP7i1aemIv7cwvUw+viS51Ui5HBw1cdE=
github.com/cucumber/messages-go/v10 v10.0.3/go.mod h1:9jMZ2Y8ZxjLY6TG2+x344nt5rXstVVDYSdS5ySfI1WY=
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/dgrijalva/jwt-go v3.2.0+incompatible h1:7qlOGliEKZXTDg6OTjfoBKDXWrumCAMpl/TFQ4/5kLM=
github.com/dgrijalva/jwt-go v3.2.0+incompatible/go.mod h1:E3ru+11k8xSBh+hMPgOLZmtrrCbhqsmaPHjLKYnJCaQ=
github.com/eapache/go-resiliency v1.1.0 h1:1NtRmCAqadE2FN4ZcN6g90TP3uk8cg9rn9eNK2197aU=
github.com/eapache/go-resiliency v1.1.0/go.mod h1:kFI+JgMyC7bLPUVY133qvEBtVayf5mFgVsvEsIPBvNs=
github.com/eapache/go-xerial-snappy v0.0.0-20180814174437-776d5712da21 h1:YEetp8/yCZMuEPMUDHG0CW/brkkEp8mzqk2+ODEitlw=
github.com/eapache/go-xerial-snappy v0.0.0-20180814174437-776d5712da21/go.mod h1:+020luEh2TKB4/GOp8oxxtq0Daoen/Cii55CzbTV6DU=
github.com/eapache/queue v1.1.0 h1:YOEu7KNc61ntiQlcEeUIoDTJ2o8mQznoNvUhiigpIqc=
github.com/eapache/queue v1.1.0/go.mod h1:6eCeP0CKFpHLu8blIFXhExK/dRa7WDZfr6jVFPTqq+I=
github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4=
github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c=
github.com/fortytw2/leaktest v1.3.0 h1:u8491cBMTQ8ft8aeV+adlcytMZylmA5nnwwkRZjI8vw=
github.com/fortytw2/leaktest v1.3.0/go.mod h1:jDsjWgpAGjm2CA7WthBh/CdZYEPF31XHquHwclZch5g=
github.com/fsnotify/fsnotify v1.4.7 h1:IXs+QLmnXW2CcXuY+8Mzv/fWEsPGWxqefPtCP5CnV9I=
github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo=
github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04=
github.com/go-kit/kit v0.8.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as=
github.com/go-logfmt/logfmt v0.3.0/go.mod h1:Qt1PoO58o5twSAckw1HlFXLmHsOX5/0LbT9GBnD5lWE=
github.com/go-sql-driver/mysql v1.5.0 h1:ozyZYNQW3x3HtqT1jira07DN2PArx2v7/mN66gGcHOs=
github.com/go-sql-driver/mysql v1.5.0/go.mod h1:DCzpHaOWr8IXmIStZouvnhqoel9Qv2LBy8hT2VhHyBg=
github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY=
github.com/gofrs/uuid v3.2.0+incompatible h1:y12jRkkFxsd7GpqdSZ+/KCs/fJbqpEXSGd4+jfEaewE=
github.com/gofrs/uuid v3.2.0+incompatible/go.mod h1:b2aQJv3Z4Fp6yNu3cdSllBxTCLRxnplIgP/c0N/04lM=
github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ=
github.com/gogo/protobuf v1.2.0/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ=
github.com/gogo/protobuf v1.2.1 h1:/s5zKNz0uPFCZ5hddgPdo2TK2TVrUNMn0OOX8/aZMTE=
github.com/gogo/protobuf v1.2.1/go.mod h1:hp+jE20tsWTFYpLwKvXlhS1hjn+gTNwPg2I6zVXpSg4=
github.com/gogo/protobuf v1.3.1 h1:DqDEcV5aeaTmdFBePNpYsp3FlcVH/2ISVVM9Qf8PSls=
github.com/gogo/protobuf v1.3.1/go.mod h1:SlYgWuQ5SjCEi6WLHjHCa1yvBfUnHcTbrrZtXPKa29o=
github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b h1:VKtxabqXZkF25pY9ekfRL6a582T4P37/31XEstQ5p58=
github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q=
github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A=
github.com/golang/mock v1.2.0/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A=
github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
github.com/golang/protobuf v1.3.1 h1:YF8+flBXS5eO826T4nzqPrxfhQThhXl0YzfuUPu4SBg=
github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
github.com/golang/protobuf v1.3.2 h1:6nsPYzhq5kReh6QImI3k5qWzO4PEbvbIW2cwSfR/6xs=
github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
github.com/golang/snappy v0.0.0-20180518054509-2e65f85255db h1:woRePGFeVFfLKN/pOkfl+p/TAqKOfFu+7KPlMVpok/w=
github.com/golang/snappy v0.0.0-20180518054509-2e65f85255db/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q=
github.com/google/btree v0.0.0-20180813153112-4030bb1f1f0c/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ=
github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M=
github.com/google/go-cmp v0.3.0 h1:crn/baboCvb5fXaQ0IJ1SGTsTVrWpDsCWC8EGETZijY=
github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
github.com/google/go-cmp v0.4.0 h1:xsAVV57WRhGj6kEIi8ReJzQlHHqcBYCElAvkovg3B/4=
github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/google/martian v2.1.0+incompatible/go.mod h1:9I4somxYTbIHy5NJKHRl3wXiIaQGbYVAs8BPL6v8lEs=
github.com/google/pprof v0.0.0-20181206194817-3ea8567a2e57/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc=
github.com/google/uuid v1.1.1 h1:Gkbcsh/GbpXz7lPftLA3P6TYMwjCLYm83jiFQZF/3gY=
github.com/google/uuid v1.1.1/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
github.com/googleapis/gax-go/v2 v2.0.4 h1:hU4mGcQI4DaAYW+IbTun+2qEZVFxK0ySjQLTbS0VQKc=
github.com/googleapis/gax-go/v2 v2.0.4/go.mod h1:0Wqv26UfaUD9n4G6kQubkQ+KchISgw+vpHVxEJEs9eg=
github.com/googleapis/gax-go/v2 v2.0.5 h1:sjZBwGj9Jlw33ImPtvFviGYvseOtDM7hkSKB7+Tv3SM=
github.com/googleapis/gax-go/v2 v2.0.5/go.mod h1:DWXyrwAJ9X0FpwwEdw+IPEYBICEFu5mhpdKc/us6bOk=
github.com/gorilla/context v1.1.1 h1:AWwleXJkX/nhcU9bZSnZoi3h/qGYqQAGhq6zZe/aQW8=
github.com/gorilla/context v1.1.1/go.mod h1:kBGZzfjB9CEq2AlWe17Uuf7NDRt0dE0s8S51q0aT7Yg=
github.com/gorilla/mux v1.6.2 h1:Pgr17XVTNXAk3q/r4CpKzC5xBM/qW1uVLV+IhRZpIIk=
github.com/gorilla/mux v1.6.2/go.mod h1:1lud6UwP+6orDFRuTfBEV8e9/aOM/c4fVVCaMa2zaAs=
github.com/grpc-ecosystem/grpc-gateway v1.8.5 h1:2+KSC78XiO6Qy0hIjfc1OD9H+hsaJdJlb8Kqsd41CTE=
github.com/grpc-ecosystem/grpc-gateway v1.8.5/go.mod h1:vNeuVxBJEsws4ogUvrchl83t/GYV9WGTSLVdBhOQFDY=
github.com/hashicorp/go-cleanhttp v0.5.0/go.mod h1:JpRdi6/HCYpAwUzNwuwqhbovhLtngrth3wmdIIUrZ80=
github.com/hashicorp/go-hclog v0.9.1 h1:9PZfAcVEvez4yhLH2TBU64/h/z4xlFI80cWXRrxuKuM=
github.com/hashicorp/go-hclog v0.9.1/go.mod h1:5CU+agLiy3J7N7QjHK5d05KxGsuXiQLrjA0H7acj2lQ=
github.com/hashicorp/go-immutable-radix v1.0.0 h1:AKDB1HM5PWEA7i4nhcpwOrO2byshxBjXVn/J/3+z5/0=
github.com/hashicorp/go-immutable-radix v1.0.0/go.mod h1:0y9vanUI8NX6FsYoO3zeMjhV/C5i9g4Q3DwcSNZ4P60=
github.com/hashicorp/go-msgpack v0.5.5 h1:i9R9JSrqIz0QVLz3sz+i3YJdT7TTSLcfLLzJi9aZTuI=
github.com/hashicorp/go-msgpack v0.5.5/go.mod h1:ahLV/dePpqEmjfWmKiqvPkv/twdG7iPBM1vqhUKIvfM=
github.com/hashicorp/go-retryablehttp v0.5.3/go.mod h1:9B5zBasrRhHXnJnui7y6sL7es7NDiJgTc6Er0maI1Xs=
github.com/hashicorp/go-uuid v1.0.0 h1:RS8zrF7PhGwyNPOtxSClXXj9HA8feRnJzgnI1RJCSnM=
github.com/hashicorp/go-uuid v1.0.0/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro=
github.com/hashicorp/go-version v1.0.0/go.mod h1:fltr4n8CU8Ke44wwGCBoEymUuxUHl09ZGVZPK5anwXA=
github.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8=
github.com/hashicorp/golang-lru v0.5.1 h1:0hERBMJE1eitiLkihrMvRVBYAkpHzc/J3QdDN+dAcgU=
github.com/hashicorp/golang-lru v0.5.1/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8=
github.com/hashicorp/raft v1.1.1 h1:HJr7UE1x/JrJSc9Oy6aDBHtNHUUBHjcQjTgvUVihoZs=
github.com/hashicorp/raft v1.1.1/go.mod h1:vPAJM8Asw6u8LxC3eJCUZmRP/E4QmUGE1R7g7k8sG/8=
github.com/hashicorp/raft-boltdb v0.0.0-20171010151810-6e5ba93211ea/go.mod h1:pNv7Wc3ycL6F5oOWn+tPGo2gWD4a5X+yp/ntwdKLjRk=
github.com/hpcloud/tail v1.0.0 h1:nfCOvKYfkgYP8hkirhJocXT2+zOD8yUNjXaWfTlyFKI=
github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU=
github.com/jstemmer/go-junit-report v0.0.0-20190106144839-af01ea7f8024/go.mod h1:6v2b51hI/fHJwM22ozAgKL4VKDeJcHhJFhtBdhmNjmU=
github.com/julienschmidt/httprouter v1.2.0/go.mod h1:SYymIcj16QtmaHHD7aYtjjsJG7VTCxuUUipMqKk8s4w=
github.com/kelseyhightower/envconfig v1.4.0 h1:Im6hONhd3pLkfDFsbRgu68RDNkGF1r3dvMUtDTo2cv8=
github.com/kelseyhightower/envconfig v1.4.0/go.mod h1:cccZRl6mQpaq41TPp5QxidR+Sa3axMbJDNb//FQX6Gg=
github.com/kisielk/errcheck v1.1.0/go.mod h1:EZBBE59ingxPouuu3KfxchcWSUPOHkagtvWXihfKN4Q=
github.com/kisielk/errcheck v1.2.0/go.mod h1:/BMXB+zMLi60iA8Vv6Ksmxu/1UDYcXs4uQLJ+jE2L00=
github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck=
github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515/go.mod h1:+0opPa2QZZtGFBFZlji/RkVcI2GknAs/DXo4wKdlNEc=
github.com/kr/pretty v0.1.0 h1:L/CwN0zerZDmRFUapSPitk6f+Q3+0za1rQkzVuMiMFI=
github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
github.com/kr/pretty v0.2.0 h1:s5hAObm+yFO5uHYt5dYjxi2rXrsnmRpJx4OYvIWUaQs=
github.com/kr/pretty v0.2.0/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI=
github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE=
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
github.com/lib/pq v1.3.0 h1:/qkRGz8zljWiDcFvgpwUpwIAPu3r07TDvs3Rws+o/pU=
github.com/lib/pq v1.3.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo=
github.com/lightstep/tracecontext.go v0.0.0-20181129014701-1757c391b1ac h1:+2b6iGRJe3hvV/yVXrd41yVEjxuFHxasJqDhkIjS4gk=
github.com/lightstep/tracecontext.go v0.0.0-20181129014701-1757c391b1ac/go.mod h1:Frd2bnT3w5FB5q49ENTfVlztJES+1k/7lyWX2+9gq/M=
github.com/matttproud/golang_protobuf_extensions v1.0.1 h1:4hp9jkHxhMHkqkrB3Ix0jegS5sx/RkqARlsWZ6pIwiU=
github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0=
github.com/mitchellh/iochan v1.0.0/go.mod h1:JwYml1nuB7xOzsp52dPpHFffvOCDupsG0QubkSMEySY=
github.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U=
github.com/nats-io/jwt v0.3.0/go.mod h1:fRYCDE99xlTsqUzISS1Bi75UBJ6ljOJQOAAu5VglpSg=
github.com/nats-io/jwt v0.3.2 h1:+RB5hMpXUUA2dfxuhBTEkMOrYmM+gKIZYS1KjSostMI=
github.com/nats-io/jwt v0.3.2/go.mod h1:/euKqTS1ZD+zzjYrY7pseZrTtWQSjujC7xjPc8wL6eU=
github.com/nats-io/nats-server/v2 v2.1.2 h1:i2Ly0B+1+rzNZHHWtD4ZwKi+OU5l+uQo1iDHZ2PmiIc=
github.com/nats-io/nats-server/v2 v2.1.2/go.mod h1:Afk+wRZqkMQs/p45uXdrVLuab3gwv3Z8C4HTBu8GD/k=
github.com/nats-io/nats-server/v2 v2.1.4 h1:BILRnsJ2Yb/fefiFbBWADpViGF69uh4sxe8poVDQ06g=
github.com/nats-io/nats-server/v2 v2.1.4/go.mod h1:Jw1Z28soD/QasIA2uWjXyM9El1jly3YwyFOuR8tH1rg=
github.com/nats-io/nats-streaming-server v0.17.0 h1:eYhSmjRmRsCYNsoUshmZ+RgKbhq6B+7FvMHXo3M5yMs=
github.com/nats-io/nats-streaming-server v0.17.0/go.mod h1:ewPBEsmp62Znl3dcRsYtlcfwudxHEdYMtYqUQSt4fE0=
github.com/nats-io/nats.go v1.9.1 h1:ik3HbLhZ0YABLto7iX80pZLPw/6dx3T+++MZJwLnMrQ=
github.com/nats-io/nats.go v1.9.1/go.mod h1:ZjDU1L/7fJ09jvUSRVBR2e7+RnLiiIQyqyzEE/Zbp4w=
github.com/nats-io/nkeys v0.1.0/go.mod h1:xpnFELMwJABBLVhffcfd1MZx6VsNRFpEugbxziKVo7w=
github.com/nats-io/nkeys v0.1.3 h1:6JrEfig+HzTH85yxzhSVbjHRJv9cn0p6n3IngIcM5/k=
github.com/nats-io/nkeys v0.1.3/go.mod h1:xpnFELMwJABBLVhffcfd1MZx6VsNRFpEugbxziKVo7w=
github.com/nats-io/nuid v1.0.1 h1:5iA8DT8V7q8WK2EScv2padNa/rTESc1KdnPw4TC2paw=
github.com/nats-io/nuid v1.0.1/go.mod h1:19wcPz3Ph3q0Jbyiqsd0kePYG7A95tJPxeL+1OSON2c=
github.com/nats-io/stan.go v0.6.0 h1:26IJPeykh88d8KVLT4jJCIxCyUBOC5/IQup8oWD/QYY=
github.com/nats-io/stan.go v0.6.0/go.mod h1:eIcD5bi3pqbHT/xIIvXMwvzXYElgouBvaVRftaE+eac=
github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e h1:fD57ERR4JtEqsWbfPhv4DMiApHyliiK5xCTNVSPiaAs=
github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e/go.mod h1:zD1mROLANZcx1PVRCS0qkT7pwLkGfwJo4zjcN/Tysno=
github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
github.com/onsi/ginkgo v1.7.0 h1:WSHQ+IS43OoUrWtD1/bbclrwK8TTH5hzp+umCiuxHgs=
github.com/onsi/ginkgo v1.7.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
github.com/onsi/gomega v1.4.3 h1:RE1xgDvH7imwFD45h+u2SgIfERHlS2yNG4DObb5BSKU=
github.com/onsi/gomega v1.4.3/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY=
github.com/openzipkin/zipkin-go v0.1.6/go.mod h1:QgAqvLzwWbR/WpD4A3cGpPtJrZXNIiJc5AZX7/PBEpw=
github.com/pascaldekloe/goe v0.1.0 h1:cBOtyMzM9HTpWjXfbbunk26uA6nG3a8n06Wieeh0MwY=
github.com/pascaldekloe/goe v0.1.0/go.mod h1:lzWF7FIEvWOWxwDKqyGYQf6ZUaNfKdP144TG7ZOy1lc=
github.com/pierrec/lz4 v2.0.5+incompatible h1:2xWsjqPFWcplujydGg4WmhC/6fZqK42wMM8aXeqhl0I=
github.com/pierrec/lz4 v2.0.5+incompatible/go.mod h1:pdkljMzZIN41W+lC3N2tnIh5sFi+IEE17M5jbnwPHcY=
github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
github.com/pkg/errors v0.8.1 h1:iURUrRGxPUNPdy5/HRSm+Yj6okJ6UtLINN0Q9M4+h3I=
github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/prometheus/client_golang v0.9.1/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw=
github.com/prometheus/client_golang v0.9.2 h1:awm861/B8OKDd2I/6o1dy3ra4BamzKhYOiGItCeZ740=
github.com/prometheus/client_golang v0.9.2/go.mod h1:OsXs2jCmiKlQ1lTBmv21f2mNfw4xf/QclQDMrYNZzcM=
github.com/prometheus/client_golang v0.9.3-0.20190127221311-3c4408c8b829 h1:D+CiwcpGTW6pL6bv6KI3KbyEyCKyS+1JWS2h8PNDnGA=
github.com/prometheus/client_golang v0.9.3-0.20190127221311-3c4408c8b829/go.mod h1:p2iRAGwDERtqlqzRXnrOVns+ignqQo//hLXqYxZYVNs=
github.com/prometheus/client_model v0.0.0-20180712105110-5c3871d89910 h1:idejC8f05m9MGOsuEi1ATq9shN03HrxNkD/luQvxCv8=
github.com/prometheus/client_model v0.0.0-20180712105110-5c3871d89910/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo=
github.com/prometheus/client_model v0.0.0-20190115171406-56726106282f h1:BVwpUVJDADN2ufcGik7W992pyps0wZ888b/y9GXcLTU=
github.com/prometheus/client_model v0.0.0-20190115171406-56726106282f/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo=
github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4 h1:gQz4mCbXsO+nc9n1hCxHcGA3Zx3Eo+UHZoInFGUIXNM=
github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
github.com/prometheus/common v0.0.0-20181126121408-4724e9255275 h1:PnBWHBf+6L0jOqq0gIVUe6Yk0/QMZ640k6NvkxcBf+8=
github.com/prometheus/common v0.0.0-20181126121408-4724e9255275/go.mod h1:daVV7qP5qjZbuso7PdcryaAu0sAZbrN9i7WWcTMWvro=
github.com/prometheus/common v0.2.0 h1:kUZDBDTdBVBYBj5Tmh2NZLlF60mfjA27rM34b+cVwNU=
github.com/prometheus/common v0.2.0/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4=
github.com/prometheus/procfs v0.0.0-20181005140218-185b4288413d/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk=
github.com/prometheus/procfs v0.0.0-20181204211112-1dc9a6cbc91a h1:9a8MnZMP0X2nLJdBg+pBmGgkJlSaKC2KaQmTCk1XDtE=
github.com/prometheus/procfs v0.0.0-20181204211112-1dc9a6cbc91a/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk=
github.com/prometheus/procfs v0.0.0-20190117184657-bf6a532e95b1 h1:/K3IL0Z1quvmJ7X0A1AwNEK7CRkVK3YwfOU/QAL4WGg=
github.com/prometheus/procfs v0.0.0-20190117184657-bf6a532e95b1/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk=
github.com/prometheus/procfs v0.0.8 h1:+fpWZdT24pJBiqJdAwYBjPSk+5YmQzYNPYzQsdzLkt8=
github.com/prometheus/procfs v0.0.8/go.mod h1:7Qr8sr6344vo1JqZ6HhLceV9o3AJ1Ff+GxbHq6oeK9A=
github.com/rcrowley/go-metrics v0.0.0-20181016184325-3113b8401b8a h1:9ZKAASQSHhDYGoxY8uLVpewe1GDZ2vu2Tr/vTdVAkFQ=
github.com/rcrowley/go-metrics v0.0.0-20181016184325-3113b8401b8a/go.mod h1:bCqnVzQkZxMG4s8nGwiZ5l3QUCyqpo9Y+/ZMZ9VjZe4=
github.com/rogpeppe/fastuuid v0.0.0-20150106093220-6724a57986af/go.mod h1:XWv6SoW27p1b0cqNHllgS5HIMJraePCO15w5zCzIWYg=
github.com/sirupsen/logrus v1.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo=
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
github.com/stretchr/testify v1.3.0 h1:TivCn/peBQ7UY8ooIcPgZFpTNSz0Q2U6UrFlUfqbe0Q=
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
github.com/stretchr/testify v1.5.1 h1:nOGnQDM7FYENwehXlg/kFVnos3rEvtKTjRvOWSzb6H4=
github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA=
github.com/tv42/httpunix v0.0.0-20150427012821-b75d8614f926/go.mod h1:9ESjWnEqriFuLhtthL60Sar/7RFoluCcXsuvEwTV5KM=
github.com/valyala/bytebufferpool v1.0.0 h1:GqA5TC/0021Y/b9FG4Oi9Mr3q7XYx6KllzawFIhcdPw=
github.com/valyala/bytebufferpool v1.0.0/go.mod h1:6bBcMArwyJ5K/AmCkWv1jt77kVWyCJ6HpOuEn7z0Csc=
go.etcd.io/bbolt v1.3.3 h1:MUGmc65QhB3pIlaQ5bB4LwqSj6GIonVJXpZiaKNyaKk=
go.etcd.io/bbolt v1.3.3/go.mod h1:IbVyRI1SCnLcuJnV2u8VeU0CEYM7e686BmAb1XKL+uU=
go.opencensus.io v0.20.1/go.mod h1:6WKK9ahsWS3RSO+PY9ZHZUfv2irvY6gN279GOPZjmmk=
go.opencensus.io v0.20.2/go.mod h1:6WKK9ahsWS3RSO+PY9ZHZUfv2irvY6gN279GOPZjmmk=
go.opencensus.io v0.21.0/go.mod h1:mSImk1erAIZhrmZN+AvHh14ztQfjbGwt4TtuofqLduU=
go.opencensus.io v0.22.0 h1:C9hSCOW830chIVkdja34wa6Ky+IzWllkUinR+BtRZd4=
go.opencensus.io v0.22.0/go.mod h1:+kGneAE2xo2IficOXnaByMWTGM9T73dGwxeWcUqIpI8=
go.uber.org/atomic v1.4.0 h1:cxzIVoETapQEqDhQu3QfnvXAV4AlzcvUCxkVUFw3+EU=
go.uber.org/atomic v1.4.0/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE=
go.uber.org/multierr v1.1.0 h1:HoEmRHQPVSqub6w2z2d2EOVs2fjyFRGyofhKuyDq0QI=
go.uber.org/multierr v1.1.0/go.mod h1:wR5kodmAFQ0UK8QlbwjlSNy0Z68gJhDJUG5sjR94q/0=
go.uber.org/zap v1.10.0 h1:ORx85nbTijNz8ljznvCMR1ZBIPKFn3jQrag10X2AsuM=
go.uber.org/zap v1.10.0/go.mod h1:vwi/ZaCAaUcBkycHslxD9B2zi4UTXhF60s6SWpuDF0Q=
golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2 h1:VklqNMn3ovrHsnt90PveolxSbWFaJdECFbxSq0Mqo2M=
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
golang.org/x/crypto v0.0.0-20190701094942-4def268fd1a4 h1:HuIa8hRrWRSrqYzx1qI49NNxhdi2PrY7gxVSq1JjLDc=
golang.org/x/crypto v0.0.0-20190701094942-4def268fd1a4/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
golang.org/x/crypto v0.0.0-20200204104054-c9f3fb736b72 h1:+ELyKg6m8UBf0nPFSqD0mi7zUfwPyXo23HNjMnXPz7w=
golang.org/x/crypto v0.0.0-20200204104054-c9f3fb736b72/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
golang.org/x/crypto v0.0.0-20200206161412-a0c6ece9d31a h1:aczoJ0HPNE92XKa7DrIzkNN6esOKO2TBwiiYoKcINhA=
golang.org/x/crypto v0.0.0-20200206161412-a0c6ece9d31a/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU=
golang.org/x/lint v0.0.0-20190301231843-5614ed5bae6f/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
golang.org/x/lint v0.0.0-20190409202823-959b441ac422/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20181114220301-adae6a3d119a/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20181201002055-351d144fa1fc/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20181220203305-927f97764cc3/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20190108225652-1e06a53dbb7e/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20190125091013-d26f9f9a57f3/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
golang.org/x/net v0.0.0-20190501004415-9ce7a6920f09/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
golang.org/x/net v0.0.0-20190503192946-f4e77d36d62c h1:uOCk1iQW6Vc18bnC13MfzScl+wdKBmM9Y9kU7Z83/lw=
golang.org/x/net v0.0.0-20190503192946-f4e77d36d62c/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
golang.org/x/net v0.0.0-20200202094626-16171245cfb2 h1:CCH4IOTTfewWjGOlSp+zGcjutRKlBEZQ6wTn8ozI/nI=
golang.org/x/net v0.0.0-20200202094626-16171245cfb2/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45 h1:SVwTIAaPC2U/AvvLNZ2a7OVsmBpC8L5BlwK1whH3hm0=
golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20190227155943-e225da77a7e6/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20190423024810-112230192c58 h1:8gQV6CLnAEikrhgkHFbMAEhagSSnXWGV915qUMm9mrU=
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e h1:vcxGaoTs7kV8m5Np9uUNQin4BrLOthgV7252N8V+FwY=
golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20181107165924-66b7b1311ac8/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20181116152217-5ac8a444bdc5/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20181122145206-62eef0e2fa9b/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190502145724-3ef323f4f1fd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190507160741-ecd444e8653b h1:ag/x1USPSsqHud38I9BAC88qdNLDHHtQ4mlgQIZPPNA=
golang.org/x/sys v0.0.0-20190507160741-ecd444e8653b/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190523142557-0e01d883c5c5/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190726091711-fc99dfbffb4e h1:D5TXcfTk7xF7hvieo4QErS3qqCB4teTffacDWr7CI+0=
golang.org/x/sys v0.0.0-20190726091711-fc99dfbffb4e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200202164722-d101bd2416d5 h1:LfCXLvNmTYH9kEmVgqbnsWfruoXZIrh4YBgqVHtDvw0=
golang.org/x/sys v0.0.0-20200202164722-d101bd2416d5/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.3.2 h1:tW2bmiBqwgJj/UpqtC8EpXEZVYOwU0yG4iWbprSVAcs=
golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk=
golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
golang.org/x/tools v0.0.0-20180221164845-07fd8470d635/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.0.0-20180828015842-6cd1fcedba52/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.0.0-20181030221726-6c7e314b6563/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY=
golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
golang.org/x/tools v0.0.0-20190312170243-e65039ee4138/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
golang.org/x/tools v0.0.0-20190506145303-2d16b83fe98c/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q=
golang.org/x/tools v0.0.0-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q=
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543 h1:E7g+9GITq07hpfrRu66IVDexMakfv52eLZ2CXBWiKr4=
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
google.golang.org/api v0.3.1/go.mod h1:6wY9I6uQWHQ8EM57III9mq/AjF+i8G65rmVagqKMtkk=
google.golang.org/api v0.4.0/go.mod h1:8k5glujaEP+g9n7WNsDg8QP6cUVNI86fCNMcbazEtwE=
google.golang.org/api v0.6.0/go.mod h1:btoxGiFvQNVUZQ8W08zLtrVS08CNpINPEfxXxgJL1Q4=
google.golang.org/api v0.15.0 h1:yzlyyDW/J0w8yNFJIhiAJy4kq74S+1DOLdawELNxFMA=
google.golang.org/api v0.15.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI=
google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM=
google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
google.golang.org/appengine v1.5.0 h1:KxkO13IPW4Lslp2bz+KHP2E3gtFlrIGNThxkZQ3g+4c=
google.golang.org/appengine v1.5.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc=
google.golang.org/genproto v0.0.0-20190307195333-5fe7a883aa19/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE=
google.golang.org/genproto v0.0.0-20190418145605-e7d98fc518a7/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE=
google.golang.org/genproto v0.0.0-20190425155659-357c62f0e4bb/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE=
google.golang.org/genproto v0.0.0-20190502173448-54afdca5d873/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE=
google.golang.org/genproto v0.0.0-20190530194941-fb225487d101/go.mod h1:z3L6/3dTEVtUr6QSP8miRzeRqwQOioJ9I66odjN4I7s=
google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55 h1:gSJIx1SDwno+2ElGhA4+qG2zF97qiUzTM+rQ0klBOcE=
google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc=
google.golang.org/grpc v1.17.0/go.mod h1:6QZJwpn2B+Zp71q/5VxRsJ6NXXVCE5NRUHRo+f3cWCs=
google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c=
google.golang.org/grpc v1.19.1/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c=
google.golang.org/grpc v1.20.1 h1:Hz2g2wirWK7H0qIIhGIqRGTuMwTE8HEKFnDZZ7lm9NU=
google.golang.org/grpc v1.20.1/go.mod h1:10oTOabMzJvdu6/UiuZezV6QK5dSlG84ov/aaiqXj38=
google.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg=
google.golang.org/grpc v1.26.0 h1:2dTRdpdFEEhJYQD8EMLB61nnrzSCTbG38PhqdhvOltg=
google.golang.org/grpc v1.26.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk=
gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127 h1:qIbj1fsPNlZgppZ+VLlY7N33q108Sa+fhmuc+sWQYwY=
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15 h1:YR8cESwS4TdDjEe65xsg0ogRM/Nc3DYOhEAlW+xobZo=
gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f h1:BLraFXnmrev5lT+xlilqcH8XK9/i0At2xKjWk4p6zsU=
gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/fsnotify.v1 v1.4.7 h1:xOHLXZwVvI9hhs+cLKq5+I5onOuwQLhQwiu63xxlHs4=
gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys=
gopkg.in/resty.v1 v1.12.0/go.mod h1:mDo4pnntr5jdWRML875a/NmxYqAlA73dVijT2AXvQQo=
gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 h1:uRGJdciOHaEIrze2W8Q3AKkepLTh2hOroT7a+7czfdQ=
gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw=
gopkg.in/yaml.v2 v2.0.0-20170812160011-eb3733d160e7/go.mod h1:JAlM8MvJe8wmxCU4Bli9HhUf9+ttbYbLASfIpnQbh74=
gopkg.in/yaml.v2 v2.2.1 h1:mUhvW9EsL+naU5Q3cakzfE91YhliOondGd6ZrsDBHQE=
gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v2 v2.2.8 h1:obN1ZagJSUGI0Ek/LBmuj4SNLPfIny3KsKFopxRdj10=
gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
honnef.co/go/tools v0.0.0-20180728063816-88497007e858/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
honnef.co/go/tools v0.0.0-20190106161140-3f1c8253044a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
honnef.co/go/tools v0.0.0-20190418001031-e561f6794a2a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
pack.ag/amqp v0.11.0 h1:ot/IA0enDkt4/c8xfbCO7AZzjM4bHys/UffnFmnHUnU=
pack.ag/amqp v0.11.0/go.mod h1:4/cbmt4EJXSKlG6LCfWHoqmN0uFdy5i/+YFz+fTfhV4=
rsc.io/binaryregexp v0.2.0/go.mod h1:qTv7/COck+e2FymRvadv62gMdZztPaShugOCi3I+8D8=

View File

@ -1,4 +0,0 @@
/*
Package observability holds metrics and tracing recording implementations.
*/
package observability

View File

@ -1,22 +0,0 @@
package observability
import (
"go.opencensus.io/tag"
)
var (
// KeyMethod is the tag used for marking method on a metric.
KeyMethod, _ = tag.NewKey("method")
// KeyResult is the tag used for marking result on a metric.
KeyResult, _ = tag.NewKey("result")
)
const (
// ClientSpanName is the key used to start spans from the client.
ClientSpanName = "cloudevents.client"
// ResultError is a shared result tag value for error.
ResultError = "error"
// ResultOK is a shared result tag value for success.
ResultOK = "success"
)

View File

@ -1,87 +0,0 @@
package observability
import (
"context"
"sync"
"time"
"go.opencensus.io/stats"
"go.opencensus.io/tag"
)
// Observable represents the the customization used by the Reporter for a given
// measurement and trace for a single method.
type Observable interface {
MethodName() string
LatencyMs() *stats.Float64Measure
}
// Reporter represents a running latency counter. When Error or OK are
// called, the latency is calculated. Error or OK are only allowed to
// be called once.
type Reporter interface {
Error()
OK()
}
type reporter struct {
ctx context.Context
on Observable
start time.Time
once sync.Once
}
// LatencyTags returns all tags used for Latency measurements.
func LatencyTags() []tag.Key {
return []tag.Key{KeyMethod, KeyResult}
}
// EnableTracing is deprecated. Tracing is always enabled.
func EnableTracing(enabled bool) {}
// NewReporter creates and returns a reporter wrapping the provided Observable.
func NewReporter(ctx context.Context, on Observable) (context.Context, Reporter) {
r := &reporter{
ctx: ctx,
on: on,
start: time.Now(),
}
r.tagMethod()
return ctx, r
}
func (r *reporter) tagMethod() {
var err error
r.ctx, err = tag.New(r.ctx, tag.Insert(KeyMethod, r.on.MethodName()))
if err != nil {
panic(err) // or ignore?
}
}
func (r *reporter) record() {
ms := float64(time.Since(r.start) / time.Millisecond)
stats.Record(r.ctx, r.on.LatencyMs().M(ms))
}
// Error records the result as an error.
func (r *reporter) Error() {
r.once.Do(func() {
r.result(ResultError)
})
}
// OK records the result as a success.
func (r *reporter) OK() {
r.once.Do(func() {
r.result(ResultOK)
})
}
func (r *reporter) result(v string) {
var err error
r.ctx, err = tag.New(r.ctx, tag.Insert(KeyResult, v))
if err != nil {
panic(err) // or ignore?
}
r.record()
}

View File

@ -1,21 +0,0 @@
/*
Package protocol defines interfaces to decouple the client package
from protocol implementations.
Most event sender and receiver applications should not use this
package, they should use the client package. This package is for
infrastructure developers implementing new transports, or intermediary
components like importers, channels or brokers.
Available protocols:
* HTTP (using net/http)
* Kafka (using github.com/Shopify/sarama)
* AMQP (using pack.ag/amqp)
* Go Channels
* Nats
* Nats Streaming (stan)
* Google PubSub
*/
package protocol

View File

@ -1,37 +0,0 @@
package protocol
import "fmt"
// ErrTransportMessageConversion is an error produced when the transport
// message can not be converted.
type ErrTransportMessageConversion struct {
fatal bool
handled bool
transport string
message string
}
// NewErrTransportMessageConversion makes a new ErrTransportMessageConversion.
func NewErrTransportMessageConversion(transport, message string, handled, fatal bool) *ErrTransportMessageConversion {
return &ErrTransportMessageConversion{
transport: transport,
message: message,
handled: handled,
fatal: fatal,
}
}
// IsFatal reports if this error should be considered fatal.
func (e *ErrTransportMessageConversion) IsFatal() bool {
return e.fatal
}
// Handled reports if this error should be considered accepted and no further action.
func (e *ErrTransportMessageConversion) Handled() bool {
return e.handled
}
// Error implements error.Error
func (e *ErrTransportMessageConversion) Error() string {
return fmt.Sprintf("transport %s failed to convert message: %s", e.transport, e.message)
}

View File

@ -1,4 +0,0 @@
/*
Package http implements an HTTP binding using net/http module
*/
package http

View File

@ -1,33 +0,0 @@
package http
import (
"net/textproto"
"strings"
"unicode"
"github.com/cloudevents/sdk-go/v2/binding/spec"
)
var attributeHeadersMapping map[string]string
func init() {
attributeHeadersMapping = make(map[string]string)
for _, v := range specs.Versions() {
for _, a := range v.Attributes() {
if a.Kind() == spec.DataContentType {
attributeHeadersMapping[a.Name()] = ContentType
} else {
attributeHeadersMapping[a.Name()] = textproto.CanonicalMIMEHeaderKey(prefix + a.Name())
}
}
}
}
func extNameToHeaderName(name string) string {
var b strings.Builder
b.Grow(len(name) + len(prefix))
b.WriteString(prefix)
b.WriteRune(unicode.ToUpper(rune(name[0])))
b.WriteString(name[1:])
return b.String()
}

View File

@ -1,158 +0,0 @@
package http
import (
"context"
"io"
nethttp "net/http"
"net/textproto"
"strings"
"unicode"
"github.com/cloudevents/sdk-go/v2/binding"
"github.com/cloudevents/sdk-go/v2/binding/format"
"github.com/cloudevents/sdk-go/v2/binding/spec"
)
const prefix = "Ce-"
var specs = spec.WithPrefixMatchExact(
func(s string) string {
if s == "datacontenttype" {
return "Content-Type"
} else {
return textproto.CanonicalMIMEHeaderKey("Ce-" + s)
}
},
"Ce-",
)
const ContentType = "Content-Type"
const ContentLength = "Content-Length"
// Message holds the Header and Body of a HTTP Request or Response.
// The Message instance *must* be constructed from NewMessage function.
// This message *cannot* be read several times. In order to read it more times, buffer it using binding/buffering methods
type Message struct {
Header nethttp.Header
BodyReader io.ReadCloser
OnFinish func(error) error
format format.Format
version spec.Version
}
// Check if http.Message implements binding.Message
var _ binding.Message = (*Message)(nil)
var _ binding.MessageMetadataReader = (*Message)(nil)
// NewMessage returns a binding.Message with header and data.
// The returned binding.Message *cannot* be read several times. In order to read it more times, buffer it using binding/buffering methods
func NewMessage(header nethttp.Header, body io.ReadCloser) *Message {
m := Message{Header: header}
if body != nil {
m.BodyReader = body
}
if m.format = format.Lookup(header.Get(ContentType)); m.format == nil {
m.version = specs.Version(m.Header.Get(specs.PrefixedSpecVersionName()))
}
return &m
}
// NewMessageFromHttpRequest returns a binding.Message with header and data.
// The returned binding.Message *cannot* be read several times. In order to read it more times, buffer it using binding/buffering methods
func NewMessageFromHttpRequest(req *nethttp.Request) *Message {
if req == nil {
return nil
}
return NewMessage(req.Header, req.Body)
}
// NewMessageFromHttpResponse returns a binding.Message with header and data.
// The returned binding.Message *cannot* be read several times. In order to read it more times, buffer it using binding/buffering methods
func NewMessageFromHttpResponse(resp *nethttp.Response) *Message {
if resp == nil {
return nil
}
msg := NewMessage(resp.Header, resp.Body)
return msg
}
func (m *Message) ReadEncoding() binding.Encoding {
if m.version != nil {
return binding.EncodingBinary
}
if m.format != nil {
return binding.EncodingStructured
}
return binding.EncodingUnknown
}
func (m *Message) ReadStructured(ctx context.Context, encoder binding.StructuredWriter) error {
if m.format == nil {
return binding.ErrNotStructured
} else {
return encoder.SetStructuredEvent(ctx, m.format, m.BodyReader)
}
}
func (m *Message) ReadBinary(ctx context.Context, encoder binding.BinaryWriter) (err error) {
if m.version == nil {
return binding.ErrNotBinary
}
for k, v := range m.Header {
attr := m.version.Attribute(k)
if attr != nil {
err = encoder.SetAttribute(attr, v[0])
} else if strings.HasPrefix(k, prefix) {
// Trim Prefix + To lower
var b strings.Builder
b.Grow(len(k) - len(prefix))
b.WriteRune(unicode.ToLower(rune(k[len(prefix)])))
b.WriteString(k[len(prefix)+1:])
err = encoder.SetExtension(b.String(), v[0])
}
if err != nil {
return err
}
}
if m.BodyReader != nil {
err = encoder.SetData(m.BodyReader)
if err != nil {
return err
}
}
return
}
func (m *Message) GetAttribute(k spec.Kind) (spec.Attribute, interface{}) {
attr := m.version.AttributeFromKind(k)
if attr != nil {
h := m.Header[attributeHeadersMapping[attr.Name()]]
if h != nil {
return attr, h[0]
}
return attr, nil
}
return nil, nil
}
func (m *Message) GetExtension(name string) interface{} {
h := m.Header[extNameToHeaderName(name)]
if h != nil {
return h[0]
}
return nil
}
func (m *Message) Finish(err error) error {
if m.BodyReader != nil {
_ = m.BodyReader.Close()
}
if m.OnFinish != nil {
return m.OnFinish(err)
}
return nil
}

View File

@ -1,197 +0,0 @@
package http
import (
"fmt"
"net"
nethttp "net/http"
"net/url"
"strings"
"time"
)
// Option is the function signature required to be considered an http.Option.
type Option func(*Protocol) error
// WithTarget sets the outbound recipient of cloudevents when using an HTTP
// request.
func WithTarget(targetUrl string) Option {
return func(p *Protocol) error {
if p == nil {
return fmt.Errorf("http target option can not set nil protocol")
}
targetUrl = strings.TrimSpace(targetUrl)
if targetUrl != "" {
var err error
var target *url.URL
target, err = url.Parse(targetUrl)
if err != nil {
return fmt.Errorf("http target option failed to parse target url: %s", err.Error())
}
p.Target = target
if p.RequestTemplate == nil {
p.RequestTemplate = &nethttp.Request{
Method: nethttp.MethodPost,
}
}
p.RequestTemplate.URL = target
return nil
}
return fmt.Errorf("http target option was empty string")
}
}
// WithHeader sets an additional default outbound header for all cloudevents
// when using an HTTP request.
func WithHeader(key, value string) Option {
return func(p *Protocol) error {
if p == nil {
return fmt.Errorf("http header option can not set nil protocol")
}
key = strings.TrimSpace(key)
if key != "" {
if p.RequestTemplate == nil {
p.RequestTemplate = &nethttp.Request{
Method: nethttp.MethodPost,
}
}
if p.RequestTemplate.Header == nil {
p.RequestTemplate.Header = nethttp.Header{}
}
p.RequestTemplate.Header.Add(key, value)
return nil
}
return fmt.Errorf("http header option was empty string")
}
}
// WithShutdownTimeout sets the shutdown timeout when the http server is being shutdown.
func WithShutdownTimeout(timeout time.Duration) Option {
return func(t *Protocol) error {
if t == nil {
return fmt.Errorf("http shutdown timeout option can not set nil protocol")
}
t.ShutdownTimeout = timeout
return nil
}
}
func checkListen(t *Protocol, prefix string) error {
switch {
case t.Port != nil:
return fmt.Errorf("%v port already set", prefix)
case t.listener != nil:
return fmt.Errorf("%v listener already set", prefix)
}
return nil
}
// WithPort sets the listening port for StartReceiver.
// Only one of WithListener or WithPort is allowed.
func WithPort(port int) Option {
return func(t *Protocol) error {
if t == nil {
return fmt.Errorf("http port option can not set nil protocol")
}
if port < 0 || port > 65535 {
return fmt.Errorf("http port option was given an invalid port: %d", port)
}
if err := checkListen(t, "http port option"); err != nil {
return err
}
t.setPort(port)
return nil
}
}
// WithListener sets the listener for StartReceiver.
// Only one of WithListener or WithPort is allowed.
func WithListener(l net.Listener) Option {
return func(t *Protocol) error {
if t == nil {
return fmt.Errorf("http listener option can not set nil protocol")
}
if err := checkListen(t, "http port option"); err != nil {
return err
}
t.listener = l
_, err := t.listen()
return err
}
}
// WithPath sets the path to receive cloudevents on for HTTP transports.
func WithPath(path string) Option {
return func(t *Protocol) error {
if t == nil {
return fmt.Errorf("http path option can not set nil protocol")
}
path = strings.TrimSpace(path)
if len(path) == 0 {
return fmt.Errorf("http path option was given an invalid path: %q", path)
}
t.Path = path
return nil
}
}
// WithMethod sets the HTTP verb (GET, POST, PUT, etc.) to use
// when using an HTTP request.
func WithMethod(method string) Option {
return func(p *Protocol) error {
if p == nil {
return fmt.Errorf("http method option can not set nil protocol")
}
method = strings.TrimSpace(method)
if method != "" {
if p.RequestTemplate == nil {
p.RequestTemplate = &nethttp.Request{}
}
p.RequestTemplate.Method = method
return nil
}
return fmt.Errorf("http method option was empty string")
}
}
//
// Middleware is a function that takes an existing http.Handler and wraps it in middleware,
// returning the wrapped http.Handler.
type Middleware func(next nethttp.Handler) nethttp.Handler
// WithMiddleware adds an HTTP middleware to the transport. It may be specified multiple times.
// Middleware is applied to everything before it. For example
// `NewClient(WithMiddleware(foo), WithMiddleware(bar))` would result in `bar(foo(original))`.
func WithMiddleware(middleware Middleware) Option {
return func(t *Protocol) error {
if t == nil {
return fmt.Errorf("http middleware option can not set nil protocol")
}
t.middleware = append(t.middleware, middleware)
return nil
}
}
// WithRoundTripper sets the HTTP RoundTripper.
func WithRoundTripper(roundTripper nethttp.RoundTripper) Option {
return func(t *Protocol) error {
if t == nil {
return fmt.Errorf("http round tripper option can not set nil protocol")
}
t.roundTripper = roundTripper
return nil
}
}
// WithClient sets the protocol client
func WithClient(client nethttp.Client) Option {
return func(p *Protocol) error {
if p == nil {
return fmt.Errorf("client option can not set nil protocol")
}
p.Client = &client
return nil
}
}

View File

@ -1,261 +0,0 @@
package http
import (
"context"
"fmt"
"io"
"net"
"net/http"
"net/url"
"sync"
"time"
"github.com/cloudevents/sdk-go/v2/binding"
cecontext "github.com/cloudevents/sdk-go/v2/context"
"github.com/cloudevents/sdk-go/v2/protocol"
)
const (
// DefaultShutdownTimeout defines the default timeout given to the http.Server when calling Shutdown.
DefaultShutdownTimeout = time.Minute * 1
)
// Protocol acts as both a http client and a http handler.
type Protocol struct {
Target *url.URL
RequestTemplate *http.Request
Client *http.Client
incoming chan msgErr
// To support Opener:
// ShutdownTimeout defines the timeout given to the http.Server when calling Shutdown.
// If nil, DefaultShutdownTimeout is used.
ShutdownTimeout time.Duration
// Port is the port to bind the receiver to. Defaults to 8080.
Port *int
// Path is the path to bind the receiver to. Defaults to "/".
Path string
// Receive Mutex
reMu sync.Mutex
// Handler is the handler the http Server will use. Use this to reuse the
// http server. If nil, the Protocol will create a one.
Handler *http.ServeMux
listener net.Listener
roundTripper http.RoundTripper
server *http.Server
handlerRegistered bool
middleware []Middleware
}
func New(opts ...Option) (*Protocol, error) {
p := &Protocol{
incoming: make(chan msgErr),
}
if err := p.applyOptions(opts...); err != nil {
return nil, err
}
if p.Client == nil {
p.Client = http.DefaultClient
}
if p.roundTripper != nil {
p.Client.Transport = p.roundTripper
}
if p.ShutdownTimeout == 0 {
p.ShutdownTimeout = DefaultShutdownTimeout
}
return p, nil
}
func (p *Protocol) applyOptions(opts ...Option) error {
for _, fn := range opts {
if err := fn(p); err != nil {
return err
}
}
return nil
}
// Send implements binding.Sender
func (p *Protocol) Send(ctx context.Context, m binding.Message, transformers ...binding.Transformer) error {
if ctx == nil {
return fmt.Errorf("nil Context")
} else if m == nil {
return fmt.Errorf("nil Message")
}
_, err := p.Request(ctx, m, transformers...)
return err
}
// Request implements binding.Requester
func (p *Protocol) Request(ctx context.Context, m binding.Message, transformers ...binding.Transformer) (binding.Message, error) {
if ctx == nil {
return nil, fmt.Errorf("nil Context")
} else if m == nil {
return nil, fmt.Errorf("nil Message")
}
var err error
defer func() { _ = m.Finish(err) }()
req := p.makeRequest(ctx)
if p.Client == nil || req == nil || req.URL == nil {
return nil, fmt.Errorf("not initialized: %#v", p)
}
if err = WriteRequest(ctx, m, req, transformers...); err != nil {
return nil, err
}
return p.do(ctx, req)
}
func (p *Protocol) makeRequest(ctx context.Context) *http.Request {
// TODO: support custom headers from context?
req := &http.Request{
Method: http.MethodPost,
Header: make(http.Header),
// TODO: HeaderFrom(ctx),
}
if p.RequestTemplate != nil {
req.Method = p.RequestTemplate.Method
req.URL = p.RequestTemplate.URL
req.Close = p.RequestTemplate.Close
req.Host = p.RequestTemplate.Host
copyHeadersEnsure(p.RequestTemplate.Header, &req.Header)
}
if p.Target != nil {
req.URL = p.Target
}
// Override the default request with target from context.
if target := cecontext.TargetFrom(ctx); target != nil {
req.URL = target
}
return req.WithContext(ctx)
}
// Ensure to is a non-nil map before copying
func copyHeadersEnsure(from http.Header, to *http.Header) {
if len(from) > 0 {
if *to == nil {
*to = http.Header{}
}
copyHeaders(from, *to)
}
}
func copyHeaders(from, to http.Header) {
if from == nil || to == nil {
return
}
for header, values := range from {
for _, value := range values {
to.Add(header, value)
}
}
}
// Receive the next incoming HTTP request as a CloudEvent.
// Returns non-nil error if the incoming HTTP request fails to parse as a CloudEvent
// Returns io.EOF if the receiver is closed.
func (p *Protocol) Receive(ctx context.Context) (binding.Message, error) {
if ctx == nil {
return nil, fmt.Errorf("nil Context")
}
msg, fn, err := p.Respond(ctx)
// No-op the response when finish is invoked.
if msg != nil {
return binding.WithFinish(msg, func(err error) {
if fn != nil {
_ = fn(ctx, nil, nil)
}
}), err
} else {
return nil, err
}
}
// Respond receives the next incoming HTTP request as a CloudEvent and waits
// for the response callback to invoked before continuing.
// Returns non-nil error if the incoming HTTP request fails to parse as a CloudEvent
// Returns io.EOF if the receiver is closed.
func (p *Protocol) Respond(ctx context.Context) (binding.Message, protocol.ResponseFn, error) {
if ctx == nil {
return nil, nil, fmt.Errorf("nil Context")
}
select {
case in, ok := <-p.incoming:
if !ok {
return nil, nil, io.EOF
}
return in.msg, in.respFn, in.err
case <-ctx.Done():
return nil, nil, io.EOF
}
}
type msgErr struct {
msg *Message
respFn protocol.ResponseFn
err error
}
// ServeHTTP implements http.Handler.
// Blocks until ResponseFn is invoked.
func (p *Protocol) ServeHTTP(rw http.ResponseWriter, req *http.Request) {
m := NewMessageFromHttpRequest(req)
if m == nil || m.ReadEncoding() == binding.EncodingUnknown {
p.incoming <- msgErr{msg: nil, err: binding.ErrUnknownEncoding}
return // if there was no message, return.
}
done := make(chan struct{})
var finishErr error
m.OnFinish = func(err error) error {
finishErr = err
return nil
}
var fn protocol.ResponseFn = func(ctx context.Context, resp binding.Message, er protocol.Result, transformers ...binding.Transformer) error {
// Unblock the ServeHTTP after the reply is written
defer func() {
done <- struct{}{}
}()
status := http.StatusOK
if finishErr != nil {
http.Error(rw, fmt.Sprintf("cannot forward CloudEvent: %v", finishErr), http.StatusInternalServerError)
}
if er != nil {
var result *Result
if protocol.ResultAs(er, &result) {
if result.StatusCode > 100 && result.StatusCode < 600 {
status = result.StatusCode
}
}
}
if resp != nil {
err := WriteResponseWriter(ctx, resp, status, rw, transformers...)
return resp.Finish(err)
}
rw.WriteHeader(status)
return nil
}
p.incoming <- msgErr{msg: m, respFn: fn} // Send to Request
// Block until ResponseFn is invoked
<-done
}

View File

@ -1,132 +0,0 @@
package http
import (
"context"
"fmt"
"net"
"net/http"
"strings"
"github.com/cloudevents/sdk-go/v2/protocol"
"go.opencensus.io/plugin/ochttp"
"go.opencensus.io/plugin/ochttp/propagation/tracecontext"
)
var _ protocol.Opener = (*Protocol)(nil)
func (p *Protocol) OpenInbound(ctx context.Context) error {
p.reMu.Lock()
defer p.reMu.Unlock()
if p.Handler == nil {
p.Handler = http.NewServeMux()
}
if !p.handlerRegistered {
// handler.Handle might panic if the user tries to use the same path as the sdk.
p.Handler.Handle(p.GetPath(), p)
p.handlerRegistered = true
}
addr, err := p.listen()
if err != nil {
return err
}
p.server = &http.Server{
Addr: addr.String(),
Handler: &ochttp.Handler{
Propagation: &tracecontext.HTTPFormat{},
Handler: attachMiddleware(p.Handler, p.middleware),
FormatSpanName: formatSpanName,
},
}
// Shutdown
defer func() {
_ = p.server.Close()
p.server = nil
}()
errChan := make(chan error, 1)
go func() {
errChan <- p.server.Serve(p.listener)
}()
// wait for the server to return or ctx.Done().
select {
case <-ctx.Done():
// Try a gracefully shutdown.
ctx, cancel := context.WithTimeout(context.Background(), p.ShutdownTimeout)
defer cancel()
err := p.server.Shutdown(ctx)
<-errChan // Wait for server goroutine to exit
return err
case err := <-errChan:
return err
}
}
// GetPort returns the listening port.
// Returns -1 if there is a listening error.
// Note this will call net.Listen() if the listener is not already started.
func (p *Protocol) GetPort() int {
// Ensure we have a listener and therefore a port.
if _, err := p.listen(); err == nil || p.Port != nil {
return *p.Port
}
return -1
}
func formatSpanName(r *http.Request) string {
return "cloudevents.http." + r.URL.Path
}
func (p *Protocol) setPort(port int) {
if p.Port == nil {
p.Port = new(int)
}
*p.Port = port
}
// listen if not already listening, update t.Port
func (p *Protocol) listen() (net.Addr, error) {
if p.listener == nil {
port := 8080
if p.Port != nil {
port = *p.Port
if port < 0 || port > 65535 {
return nil, fmt.Errorf("invalid port %d", port)
}
}
var err error
if p.listener, err = net.Listen("tcp", fmt.Sprintf(":%d", port)); err != nil {
return nil, err
}
}
addr := p.listener.Addr()
if tcpAddr, ok := addr.(*net.TCPAddr); ok {
p.setPort(tcpAddr.Port)
}
return addr, nil
}
// GetPath returns the path the transport is hosted on. If the path is '/',
// the transport will handle requests on any URI. To discover the true path
// a request was received on, inspect the context from Receive(cxt, ...) with
// TransportContextFrom(ctx).
func (p *Protocol) GetPath() string {
path := strings.TrimSpace(p.Path)
if len(path) > 0 {
return path
}
return "/" // default
}
// attachMiddleware attaches the HTTP middleware to the specified handler.
func attachMiddleware(h http.Handler, middleware []Middleware) http.Handler {
for _, m := range middleware {
h = m(h)
}
return h
}

View File

@ -1,108 +0,0 @@
package http
import (
"context"
"errors"
"net/http"
"net/url"
"time"
"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/protocol"
)
func (p *Protocol) do(ctx context.Context, req *http.Request) (binding.Message, error) {
params := cecontext.RetriesFrom(ctx)
switch params.Strategy {
case cecontext.BackoffStrategyConstant, cecontext.BackoffStrategyLinear, cecontext.BackoffStrategyExponential:
return p.doWithRetry(ctx, params, req)
case cecontext.BackoffStrategyNone:
fallthrough
default:
return p.doOnce(req)
}
}
func (p *Protocol) doOnce(req *http.Request) (binding.Message, protocol.Result) {
resp, err := p.Client.Do(req)
if err != nil {
return nil, protocol.NewReceipt(false, "%w", err)
}
var result protocol.Result
if resp.StatusCode/100 == 2 {
result = protocol.ResultACK
} else {
result = protocol.ResultNACK
}
return NewMessage(resp.Header, resp.Body), NewResult(resp.StatusCode, "%w", result)
}
func (p *Protocol) doWithRetry(ctx context.Context, params *cecontext.RetryParams, req *http.Request) (binding.Message, error) {
then := time.Now()
retry := 0
results := make([]protocol.Result, 0)
for {
msg, result := p.doOnce(req)
// Fast track common case.
if protocol.IsACK(result) {
return msg, NewRetriesResult(result, retry, then, results)
}
// Try again?
//
// Make sure the error was something we should retry.
{
var uErr *url.Error
if errors.As(result, &uErr) {
goto DoBackoff
}
}
{
var httpResult *Result
if errors.As(result, &httpResult) {
// Potentially retry when:
// - 404 Not Found
// - 413 Payload Too Large with Retry-After (NOT SUPPORTED)
// - 425 Too Early
// - 429 Too Many Requests
// - 503 Service Unavailable (with or without Retry-After) (IGNORE Retry-After)
// - 504 Gateway Timeout
sc := httpResult.StatusCode
if sc == 404 || sc == 425 || sc == 429 || sc == 503 || sc == 504 {
// retry!
goto DoBackoff
} else {
// Permanent error
cecontext.LoggerFrom(ctx).Debugw("status code not retryable, will not try again",
zap.Error(httpResult),
zap.Int("statusCode", sc))
return msg, NewRetriesResult(result, retry, then, results)
}
}
}
DoBackoff:
// Wait for the correct amount of backoff time.
// total tries = retry + 1
if err := params.Backoff(ctx, retry+1); err != nil {
// do not try again.
cecontext.LoggerFrom(ctx).Debugw("backoff error, will not try again", zap.Error(err))
return msg, NewRetriesResult(result, retry, then, results)
}
retry++
results = append(results, result)
}
}

View File

@ -1,44 +0,0 @@
package http
import (
"errors"
"fmt"
"github.com/cloudevents/sdk-go/v2/protocol"
)
// NewResult returns a fully populated http Result that should be used as
// a transport.Result.
func NewResult(statusCode int, messageFmt string, args ...interface{}) protocol.Result {
return &Result{
StatusCode: statusCode,
Format: messageFmt,
Args: args,
}
}
// Result wraps the fields required to make adjustments for http Responses.
type Result struct {
StatusCode int
Format string
Args []interface{}
}
// make sure Result implements error.
var _ error = (*Result)(nil)
// Is returns if the target error is a Result type checking target.
func (e *Result) Is(target error) bool {
if o, ok := target.(*Result); ok {
return e.StatusCode == o.StatusCode
}
// Allow for wrapped errors.
err := fmt.Errorf(e.Format, e.Args...)
return errors.Is(err, target)
}
// Error returns the string that is formed by using the format string with the
// provided args.
func (e *Result) Error() string {
return fmt.Sprintf("%d: %s", e.StatusCode, fmt.Sprintf(e.Format, e.Args...))
}

View File

@ -1,54 +0,0 @@
package http
import (
"fmt"
"time"
"github.com/cloudevents/sdk-go/v2/protocol"
)
// NewRetriesResult returns a http RetriesResult that should be used as
// a transport.Result without retries
func NewRetriesResult(result protocol.Result, retries int, startTime time.Time, attempts []protocol.Result) protocol.Result {
rr := &RetriesResult{
Result: result,
Retries: retries,
Duration: time.Since(startTime),
}
if len(attempts) > 0 {
rr.Attempts = attempts
}
return rr
}
// RetriesResult wraps the fields required to make adjustments for http Responses.
type RetriesResult struct {
// The last result
protocol.Result
// Retries is the number of times the request was tried
Retries int
// Duration records the time spent retrying. Exclude the successful request (if any)
Duration time.Duration
// Attempts of all failed requests. Exclude last result.
Attempts []protocol.Result
}
// make sure RetriesResult implements error.
var _ error = (*RetriesResult)(nil)
// Is returns if the target error is a RetriesResult type checking target.
func (e *RetriesResult) Is(target error) bool {
return protocol.ResultIs(e.Result, target)
}
// Error returns the string that is formed by using the format string with the
// provided args.
func (e *RetriesResult) Error() string {
if e.Retries == 0 {
return e.Result.Error()
}
return fmt.Sprintf("%s (%dx)", e.Result.Error(), e.Retries)
}

View File

@ -1,134 +0,0 @@
package http
import (
"bytes"
"context"
"io"
"io/ioutil"
"net/http"
"strings"
"github.com/cloudevents/sdk-go/v2/binding"
"github.com/cloudevents/sdk-go/v2/binding/format"
"github.com/cloudevents/sdk-go/v2/binding/spec"
"github.com/cloudevents/sdk-go/v2/types"
)
// WriteRequest fills the provided httpRequest with the message m.
// Using context you can tweak the encoding processing (more details on binding.Write documentation).
func WriteRequest(ctx context.Context, m binding.Message, httpRequest *http.Request, transformers ...binding.Transformer) error {
structuredWriter := (*httpRequestWriter)(httpRequest)
binaryWriter := (*httpRequestWriter)(httpRequest)
_, err := binding.Write(
ctx,
m,
structuredWriter,
binaryWriter,
transformers...,
)
return err
}
type httpRequestWriter http.Request
func (b *httpRequestWriter) SetStructuredEvent(ctx context.Context, format format.Format, event io.Reader) error {
b.Header.Set(ContentType, format.MediaType())
return b.setBody(event)
}
func (b *httpRequestWriter) Start(ctx context.Context) error {
return nil
}
func (b *httpRequestWriter) End(ctx context.Context) error {
return nil
}
func (b *httpRequestWriter) SetData(data io.Reader) error {
return b.setBody(data)
}
// setBody is a cherry-pick of the implementation in http.NewRequestWithContext
func (b *httpRequestWriter) setBody(body io.Reader) error {
rc, ok := body.(io.ReadCloser)
if !ok && body != nil {
rc = ioutil.NopCloser(body)
}
b.Body = rc
if body != nil {
switch v := body.(type) {
case *bytes.Buffer:
b.ContentLength = int64(v.Len())
buf := v.Bytes()
b.GetBody = func() (io.ReadCloser, error) {
r := bytes.NewReader(buf)
return ioutil.NopCloser(r), nil
}
case *bytes.Reader:
b.ContentLength = int64(v.Len())
snapshot := *v
b.GetBody = func() (io.ReadCloser, error) {
r := snapshot
return ioutil.NopCloser(&r), nil
}
case *strings.Reader:
b.ContentLength = int64(v.Len())
snapshot := *v
b.GetBody = func() (io.ReadCloser, error) {
r := snapshot
return ioutil.NopCloser(&r), nil
}
default:
// This is where we'd set it to -1 (at least
// if body != NoBody) to mean unknown, but
// that broke people during the Go 1.8 testing
// period. People depend on it being 0 I
// guess. Maybe retry later. See Issue 18117.
}
// For client requests, Request.ContentLength of 0
// means either actually 0, or unknown. The only way
// to explicitly say that the ContentLength is zero is
// to set the Body to nil. But turns out too much code
// depends on NewRequest returning a non-nil Body,
// so we use a well-known ReadCloser variable instead
// and have the http package also treat that sentinel
// variable to mean explicitly zero.
if b.GetBody != nil && b.ContentLength == 0 {
b.Body = http.NoBody
b.GetBody = func() (io.ReadCloser, error) { return http.NoBody, nil }
}
}
return nil
}
func (b *httpRequestWriter) SetAttribute(attribute spec.Attribute, value interface{}) error {
mapping := attributeHeadersMapping[attribute.Name()]
if value == nil {
delete(b.Header, mapping)
}
// Http headers, everything is a string!
s, err := types.Format(value)
if err != nil {
return err
}
b.Header[mapping] = append(b.Header[mapping], s)
return nil
}
func (b *httpRequestWriter) SetExtension(name string, value interface{}) error {
if value == nil {
delete(b.Header, extNameToHeaderName(name))
}
// Http headers, everything is a string!
s, err := types.Format(value)
if err != nil {
return err
}
b.Header[extNameToHeaderName(name)] = []string{s}
return nil
}
var _ binding.StructuredWriter = (*httpRequestWriter)(nil) // Test it conforms to the interface
var _ binding.BinaryWriter = (*httpRequestWriter)(nil) // Test it conforms to the interface

View File

@ -1,121 +0,0 @@
package http
import (
"bytes"
"context"
"io"
"net/http"
"strconv"
"strings"
"github.com/cloudevents/sdk-go/v2/binding"
"github.com/cloudevents/sdk-go/v2/binding/format"
"github.com/cloudevents/sdk-go/v2/binding/spec"
"github.com/cloudevents/sdk-go/v2/types"
)
// WriteResponseWriter writes out to the the provided httpResponseWriter with the message m.
// Using context you can tweak the encoding processing (more details on binding.Write documentation).
func WriteResponseWriter(ctx context.Context, m binding.Message, status int, rw http.ResponseWriter, transformers ...binding.Transformer) error {
if status < 200 || status >= 600 {
status = http.StatusOK
}
writer := &httpResponseWriter{rw: rw, status: status}
_, err := binding.Write(
ctx,
m,
writer,
writer,
transformers...,
)
return err
}
type httpResponseWriter struct {
rw http.ResponseWriter
status int
body io.Reader
}
func (b *httpResponseWriter) SetStructuredEvent(ctx context.Context, format format.Format, event io.Reader) error {
b.rw.Header().Set(ContentType, format.MediaType())
b.body = event
return b.finalizeWriter()
}
func (b *httpResponseWriter) Start(ctx context.Context) error {
return nil
}
func (b *httpResponseWriter) SetAttribute(attribute spec.Attribute, value interface{}) error {
mapping := attributeHeadersMapping[attribute.Name()]
if value == nil {
delete(b.rw.Header(), mapping)
}
// Http headers, everything is a string!
s, err := types.Format(value)
if err != nil {
return err
}
b.rw.Header()[mapping] = append(b.rw.Header()[mapping], s)
return nil
}
func (b *httpResponseWriter) SetExtension(name string, value interface{}) error {
if value == nil {
delete(b.rw.Header(), extNameToHeaderName(name))
}
// Http headers, everything is a string!
s, err := types.Format(value)
if err != nil {
return err
}
b.rw.Header()[extNameToHeaderName(name)] = []string{s}
return nil
}
func (b *httpResponseWriter) SetData(reader io.Reader) error {
b.body = reader
return nil
}
func (b *httpResponseWriter) finalizeWriter() error {
if b.body != nil {
// Try to figure it out if we have a content-length
contentLength := -1
switch v := b.body.(type) {
case *bytes.Buffer:
contentLength = v.Len()
case *bytes.Reader:
contentLength = v.Len()
case *strings.Reader:
contentLength = v.Len()
}
if contentLength != -1 {
b.rw.Header().Add("Content-length", strconv.Itoa(contentLength))
}
// Finalize the headers.
b.rw.WriteHeader(b.status)
// Write body.
_, err := io.Copy(b.rw, b.body)
if err != nil {
return err
}
} else {
// Finalize the headers.
b.rw.WriteHeader(b.status)
}
return nil
}
func (b *httpResponseWriter) End(ctx context.Context) error {
return b.finalizeWriter()
}
var _ binding.StructuredWriter = (*httpResponseWriter)(nil) // Test it conforms to the interface
var _ binding.BinaryWriter = (*httpResponseWriter)(nil) // Test it conforms to the interface

View File

@ -1,47 +0,0 @@
package protocol
import (
"context"
"github.com/cloudevents/sdk-go/v2/binding"
)
// Receiver receives messages.
type Receiver interface {
// Receive blocks till a message is received or ctx expires.
//
// A non-nil error means the receiver is closed.
// io.EOF means it closed cleanly, any other value indicates an error.
// The caller is responsible for `Finish()` the returned message
Receive(ctx context.Context) (binding.Message, error)
}
// ReceiveCloser is a Receiver that can be closed.
type ReceiveCloser interface {
Receiver
Closer
}
// ResponseFn is the function callback provided from Responder.Respond to allow
// for a receiver to "reply" to a message it receives.
// transformers are applied when the message is written on the wire.
type ResponseFn func(ctx context.Context, m binding.Message, r Result, transformers ...binding.Transformer) error
// Responder receives messages and is given a callback to respond.
type Responder interface {
// Receive blocks till a message is received or ctx expires.
//
// A non-nil error means the receiver is closed.
// io.EOF means it closed cleanly, any other value indicates an error.
// The caller is responsible for `Finish()` the returned message,
// while the protocol implementation is responsible for `Finish()` the response message.
// The caller MUST invoke ResponseFn, in order to avoid leaks.
// The correct flow for the caller is to finish the received message and then invoke the ResponseFn
Respond(ctx context.Context) (binding.Message, ResponseFn, error)
}
// ResponderCloser is a Responder that can be closed.
type ResponderCloser interface {
Responder
Closer
}

View File

@ -1,18 +0,0 @@
package protocol
import (
"context"
)
// Opener is the common interface for things that need to be opened.
type Opener interface {
// OpenInbound is a blocking call and ctx is used to stop the Inbound message Receiver/Responder.
// Closing the context won't close the Receiver/Responder, aka it won't invoke Close(ctx).
OpenInbound(ctx context.Context) error
}
// Closer is the common interface for things that can be closed.
// After invoking Close(ctx), you cannot reuse the object you closed.
type Closer interface {
Close(ctx context.Context) error
}

View File

@ -1,44 +0,0 @@
package protocol
import (
"context"
"github.com/cloudevents/sdk-go/v2/binding"
)
// Sender sends messages.
type Sender interface {
// Send a message.
//
// Send returns when the "outbound" message has been sent. The Sender may
// still be expecting acknowledgment or holding other state for the message.
//
// m.Finish() is called when sending is finished (both succeeded or failed):
// expected acknowledgments (or errors) have been received, the Sender is
// no longer holding any state for the message.
// m.Finish() may be called during or after Send().
//
// transformers are applied when the message is written on the wire.
Send(ctx context.Context, m binding.Message, transformers ...binding.Transformer) error
}
// SendCloser is a Sender that can be closed.
type SendCloser interface {
Sender
Closer
}
// Requester sends a message and receives a response
//
// Optional interface that may be implemented by protocols that support
// request/response correlation.
type Requester interface {
// Request sends m like Sender.Send() but also arranges to receive a response.
Request(ctx context.Context, m binding.Message, transformers ...binding.Transformer) (binding.Message, error)
}
// RequesterCloser is a Requester that can be closed.
type RequesterCloser interface {
Requester
Closer
}

View File

@ -1,99 +0,0 @@
package protocol
import (
"errors"
"fmt"
)
// Result leverages go's 1.13 error wrapping.
type Result error
// ResultIs reports whether any error in err's chain matches target.
//
// The chain consists of err itself followed by the sequence of errors obtained by
// repeatedly calling Unwrap.
//
// An error is considered to match a target if it is equal to that target or if
// it implements a method Is(error) bool such that Is(target) returns true.
// (text from errors/wrap.go)
var ResultIs = errors.Is
// ResultAs finds the first error in err's chain that matches target, and if so, sets
// target to that error value and returns true.
//
// The chain consists of err itself followed by the sequence of errors obtained by
// repeatedly calling Unwrap.
//
// An error matches target if the error's concrete value is assignable to the value
// pointed to by target, or if the error has a method As(interface{}) bool such that
// As(target) returns true. In the latter case, the As method is responsible for
// setting target.
//
// As will panic if target is not a non-nil pointer to either a type that implements
// error, or to any interface type. As returns false if err is nil.
// (text from errors/wrap.go)
var ResultAs = errors.As
func NewResult(messageFmt string, args ...interface{}) Result {
return fmt.Errorf(messageFmt, args...) // TODO: look at adding ACK/Nak support.
}
func IsACK(target Result) bool {
// special case, nil target also means ACK.
if target == nil {
return true
}
return ResultIs(target, ResultACK)
}
func IsNACK(target Result) bool {
return ResultIs(target, ResultNACK)
}
var (
ResultACK = NewReceipt(true, "")
ResultNACK = NewReceipt(false, "")
)
// NewReceipt returns a fully populated protocol Receipt that should be used as
// a transport.Result. This type holds the base ACK/NACK results.
func NewReceipt(ack bool, messageFmt string, args ...interface{}) Result {
return &Receipt{
ACK: ack,
Format: messageFmt,
Args: args,
}
}
// Receipt wraps the fields required to understand if a protocol event is acknowledged.
type Receipt struct {
ACK bool
Format string
Args []interface{}
}
// make sure Result implements error.
var _ error = (*Receipt)(nil)
// Is returns if the target error is a Result type checking target.
func (e *Receipt) Is(target error) bool {
if o, ok := target.(*Receipt); ok {
return e.ACK == o.ACK
}
// Allow for wrapped errors.
err := fmt.Errorf(e.Format, e.Args...)
return errors.Is(err, target)
}
// Error returns the string that is formed by using the format string with the
// provided args.
func (e *Receipt) Error() string {
return fmt.Sprintf(e.Format, e.Args...)
}
// Unwrap returns the wrapped error if exist or nil
func (e *Receipt) Unwrap() error {
err := fmt.Errorf(e.Format, e.Args...)
return errors.Unwrap(err)
}

View File

@ -1,3 +0,0 @@
checks = [
"all", "-ST1003",
]

View File

@ -1,36 +0,0 @@
package types
import "reflect"
// Allocate allocates a new instance of type t and returns:
// asPtr is of type t if t is a pointer type and of type &t otherwise
// asValue is a Value of type t pointing to the same data as asPtr
func Allocate(obj interface{}) (asPtr interface{}, asValue reflect.Value) {
if obj == nil {
return nil, reflect.Value{}
}
switch t := reflect.TypeOf(obj); t.Kind() {
case reflect.Ptr:
reflectPtr := reflect.New(t.Elem())
asPtr = reflectPtr.Interface()
asValue = reflectPtr
case reflect.Map:
reflectPtr := reflect.MakeMap(t)
asPtr = reflectPtr.Interface()
asValue = reflectPtr
case reflect.String:
reflectPtr := reflect.New(t)
asPtr = ""
asValue = reflectPtr.Elem()
case reflect.Slice:
reflectPtr := reflect.MakeSlice(t, 0, 0)
asPtr = reflectPtr.Interface()
asValue = reflectPtr
default:
reflectPtr := reflect.New(t)
asPtr = reflectPtr.Interface()
asValue = reflectPtr.Elem()
}
return
}

View File

@ -1,41 +0,0 @@
/*
Package types implements the CloudEvents type system.
CloudEvents defines a set of abstract types for event context attributes. Each
type has a corresponding native Go type and a canonical string encoding. The
native Go types used to represent the CloudEvents types are:
bool, int32, string, []byte, *url.URL, time.Time
+----------------+----------------+-----------------------------------+
|CloudEvents Type|Native Type |Convertible From |
+================+================+===================================+
|Bool |bool |bool |
+----------------+----------------+-----------------------------------+
|Integer |int32 |Any numeric type with value in |
| | |range of int32 |
+----------------+----------------+-----------------------------------+
|String |string |string |
+----------------+----------------+-----------------------------------+
|Binary |[]byte |[]byte |
+----------------+----------------+-----------------------------------+
|URI-Reference |*url.URL |url.URL, types.URIRef, types.URI |
+----------------+----------------+-----------------------------------+
|URI |*url.URL |url.URL, types.URIRef, types.URI |
| | |Must be an absolute URI. |
+----------------+----------------+-----------------------------------+
|Timestamp |time.Time |time.Time, types.Timestamp |
+----------------+----------------+-----------------------------------+
Extension attributes may be stored as a native type or a canonical string. The
To<Type> functions will convert to the desired <Type> from any convertible type
or from the canonical string form.
The Parse<Type> and Format<Type> functions convert native types to/from
canonical strings.
Note are no Parse or Format functions for URL or string. For URL use the
standard url.Parse() and url.URL.String(). The canonical string format of a
string is the string itself.
*/
package types

View File

@ -1,70 +0,0 @@
package types
import (
"encoding/json"
"encoding/xml"
"fmt"
"time"
)
// Timestamp wraps time.Time to normalize the time layout to RFC3339. It is
// intended to enforce compliance with the CloudEvents spec for their
// definition of Timestamp. Custom marshal methods are implemented to ensure
// the outbound Timestamp is a string in the RFC3339 layout.
type Timestamp struct {
time.Time
}
// ParseTimestamp attempts to parse the given time assuming RFC3339 layout
func ParseTimestamp(s string) (*Timestamp, error) {
if s == "" {
return nil, nil
}
tt, err := ParseTime(s)
return &Timestamp{Time: tt}, err
}
// MarshalJSON implements a custom json marshal method used when this type is
// marshaled using json.Marshal.
func (t *Timestamp) MarshalJSON() ([]byte, error) {
if t == nil || t.IsZero() {
return []byte(`""`), nil
}
return []byte(fmt.Sprintf("%q", t)), nil
}
// UnmarshalJSON implements the json unmarshal method used when this type is
// unmarshaled using json.Unmarshal.
func (t *Timestamp) UnmarshalJSON(b []byte) error {
var timestamp string
if err := json.Unmarshal(b, &timestamp); err != nil {
return err
}
var err error
t.Time, err = ParseTime(timestamp)
return err
}
// MarshalXML implements a custom xml marshal method used when this type is
// marshaled using xml.Marshal.
func (t *Timestamp) MarshalXML(e *xml.Encoder, start xml.StartElement) error {
if t == nil || t.IsZero() {
return e.EncodeElement(nil, start)
}
return e.EncodeElement(t.String(), start)
}
// UnmarshalXML implements the xml unmarshal method used when this type is
// unmarshaled using xml.Unmarshal.
func (t *Timestamp) UnmarshalXML(d *xml.Decoder, start xml.StartElement) error {
var timestamp string
if err := d.DecodeElement(&timestamp, &start); err != nil {
return err
}
var err error
t.Time, err = ParseTime(timestamp)
return err
}
// String outputs the time using RFC3339 format.
func (t Timestamp) String() string { return FormatTime(t.Time) }

View File

@ -1,77 +0,0 @@
package types
import (
"encoding/json"
"encoding/xml"
"fmt"
"net/url"
)
// URI is a wrapper to url.URL. It is intended to enforce compliance with
// the CloudEvents spec for their definition of URI. Custom
// marshal methods are implemented to ensure the outbound URI object
// is a flat string.
type URI struct {
url.URL
}
// ParseURI attempts to parse the given string as a URI.
func ParseURI(u string) *URI {
if u == "" {
return nil
}
pu, err := url.Parse(u)
if err != nil {
return nil
}
return &URI{URL: *pu}
}
// MarshalJSON implements a custom json marshal method used when this type is
// marshaled using json.Marshal.
func (u URI) MarshalJSON() ([]byte, error) {
b := fmt.Sprintf("%q", u.String())
return []byte(b), nil
}
// UnmarshalJSON implements the json unmarshal method used when this type is
// unmarshaled using json.Unmarshal.
func (u *URI) UnmarshalJSON(b []byte) error {
var ref string
if err := json.Unmarshal(b, &ref); err != nil {
return err
}
r := ParseURI(ref)
if r != nil {
*u = *r
}
return nil
}
// MarshalXML implements a custom xml marshal method used when this type is
// marshaled using xml.Marshal.
func (u URI) MarshalXML(e *xml.Encoder, start xml.StartElement) error {
return e.EncodeElement(u.String(), start)
}
// UnmarshalXML implements the xml unmarshal method used when this type is
// unmarshaled using xml.Unmarshal.
func (u *URI) UnmarshalXML(d *xml.Decoder, start xml.StartElement) error {
var ref string
if err := d.DecodeElement(&ref, &start); err != nil {
return err
}
r := ParseURI(ref)
if r != nil {
*u = *r
}
return nil
}
// String returns the full string representation of the URI-Reference.
func (u *URI) String() string {
if u == nil {
return ""
}
return u.URL.String()
}

View File

@ -1,77 +0,0 @@
package types
import (
"encoding/json"
"encoding/xml"
"fmt"
"net/url"
)
// URIRef is a wrapper to url.URL. It is intended to enforce compliance with
// the CloudEvents spec for their definition of URI-Reference. Custom
// marshal methods are implemented to ensure the outbound URIRef object is
// is a flat string.
type URIRef struct {
url.URL
}
// ParseURIRef attempts to parse the given string as a URI-Reference.
func ParseURIRef(u string) *URIRef {
if u == "" {
return nil
}
pu, err := url.Parse(u)
if err != nil {
return nil
}
return &URIRef{URL: *pu}
}
// MarshalJSON implements a custom json marshal method used when this type is
// marshaled using json.Marshal.
func (u URIRef) MarshalJSON() ([]byte, error) {
b := fmt.Sprintf("%q", u.String())
return []byte(b), nil
}
// UnmarshalJSON implements the json unmarshal method used when this type is
// unmarshaled using json.Unmarshal.
func (u *URIRef) UnmarshalJSON(b []byte) error {
var ref string
if err := json.Unmarshal(b, &ref); err != nil {
return err
}
r := ParseURIRef(ref)
if r != nil {
*u = *r
}
return nil
}
// MarshalXML implements a custom xml marshal method used when this type is
// marshaled using xml.Marshal.
func (u URIRef) MarshalXML(e *xml.Encoder, start xml.StartElement) error {
return e.EncodeElement(u.String(), start)
}
// UnmarshalXML implements the xml unmarshal method used when this type is
// unmarshaled using xml.Unmarshal.
func (u *URIRef) UnmarshalXML(d *xml.Decoder, start xml.StartElement) error {
var ref string
if err := d.DecodeElement(&ref, &start); err != nil {
return err
}
r := ParseURIRef(ref)
if r != nil {
*u = *r
}
return nil
}
// String returns the full string representation of the URI-Reference.
func (u *URIRef) String() string {
if u == nil {
return ""
}
return u.URL.String()
}

View File

@ -1,330 +0,0 @@
package types
import (
"encoding/base64"
"fmt"
"math"
"net/url"
"reflect"
"strconv"
"time"
)
// FormatBool returns canonical string format: "true" or "false"
func FormatBool(v bool) string { return strconv.FormatBool(v) }
// FormatInteger returns canonical string format: decimal notation.
func FormatInteger(v int32) string { return strconv.Itoa(int(v)) }
// FormatBinary returns canonical string format: standard base64 encoding
func FormatBinary(v []byte) string { return base64.StdEncoding.EncodeToString(v) }
// FormatTime returns canonical string format: RFC3339 with nanoseconds
func FormatTime(v time.Time) string { return v.UTC().Format(time.RFC3339Nano) }
// ParseBool parse canonical string format: "true" or "false"
func ParseBool(v string) (bool, error) { return strconv.ParseBool(v) }
// ParseInteger parse canonical string format: decimal notation.
func ParseInteger(v string) (int32, error) {
// Accept floating-point but truncate to int32 as per CE spec.
f, err := strconv.ParseFloat(v, 64)
if err != nil {
return 0, err
}
if f > math.MaxInt32 || f < math.MinInt32 {
return 0, rangeErr(v)
}
return int32(f), nil
}
// ParseBinary parse canonical string format: standard base64 encoding
func ParseBinary(v string) ([]byte, error) { return base64.StdEncoding.DecodeString(v) }
// ParseTime parse canonical string format: RFC3339 with nanoseconds
func ParseTime(v string) (time.Time, error) {
t, err := time.Parse(time.RFC3339Nano, v)
if err != nil {
err := convertErr(time.Time{}, v)
err.extra = ": not in RFC3339 format"
return time.Time{}, err
}
return t, nil
}
// Format returns the canonical string format of v, where v can be
// any type that is convertible to a CloudEvents type.
func Format(v interface{}) (string, error) {
v, err := Validate(v)
if err != nil {
return "", err
}
switch v := v.(type) {
case bool:
return FormatBool(v), nil
case int32:
return FormatInteger(v), nil
case string:
return v, nil
case []byte:
return FormatBinary(v), nil
case URI:
return v.String(), nil
case URIRef:
// url.URL is often passed by pointer so allow both
return v.String(), nil
case Timestamp:
return FormatTime(v.Time), nil
default:
return "", fmt.Errorf("%T is not a CloudEvents type", v)
}
}
// Validate v is a valid CloudEvents attribute value, convert it to one of:
// bool, int32, string, []byte, types.URI, types.URIRef, types.Timestamp
func Validate(v interface{}) (interface{}, error) {
switch v := v.(type) {
case bool, int32, string, []byte:
return v, nil // Already a CloudEvents type, no validation needed.
case uint, uintptr, uint8, uint16, uint32, uint64:
u := reflect.ValueOf(v).Uint()
if u > math.MaxInt32 {
return nil, rangeErr(v)
}
return int32(u), nil
case int, int8, int16, int64:
i := reflect.ValueOf(v).Int()
if i > math.MaxInt32 || i < math.MinInt32 {
return nil, rangeErr(v)
}
return int32(i), nil
case float32, float64:
f := reflect.ValueOf(v).Float()
if f > math.MaxInt32 || f < math.MinInt32 {
return nil, rangeErr(v)
}
return int32(f), nil
case *url.URL:
if v == nil {
break
}
return URI{URL: *v}, nil
case url.URL:
return URI{URL: v}, nil
case *URIRef:
if v != nil {
return *v, nil
}
return nil, nil
case URIRef:
return v, nil
case *URI:
if v != nil {
return *v, nil
}
return nil, nil
case URI:
return v, nil
case time.Time:
return Timestamp{Time: v}, nil
case *time.Time:
if v == nil {
break
}
return Timestamp{Time: *v}, nil
case Timestamp:
return v, nil
}
rx := reflect.ValueOf(v)
if rx.Kind() == reflect.Ptr && !rx.IsNil() {
// Allow pointers-to convertible types
return Validate(rx.Elem().Interface())
}
return nil, fmt.Errorf("invalid CloudEvents value: %#v", v)
}
// Clone v clones a CloudEvents attribute value, which is one of the valid types:
// bool, int32, string, []byte, types.URI, types.URIRef, types.Timestamp
// Returns the same type
// Panics if the type is not valid
func Clone(v interface{}) interface{} {
if v == nil {
return nil
}
switch v := v.(type) {
case bool, int32, string, nil:
return v // Already a CloudEvents type, no validation needed.
case []byte:
clone := make([]byte, len(v))
copy(clone, v)
return v
case url.URL:
return URI{v}
case *url.URL:
return &URI{*v}
case URIRef:
return v
case *URIRef:
return &URIRef{v.URL}
case URI:
return v
case *URI:
return &URI{v.URL}
case time.Time:
return Timestamp{v}
case *time.Time:
return &Timestamp{*v}
case Timestamp:
return v
case *Timestamp:
return &Timestamp{v.Time}
}
panic(fmt.Errorf("invalid CloudEvents value: %#v", v))
}
// ToBool accepts a bool value or canonical "true"/"false" string.
func ToBool(v interface{}) (bool, error) {
v, err := Validate(v)
if err != nil {
return false, err
}
switch v := v.(type) {
case bool:
return v, nil
case string:
return ParseBool(v)
default:
return false, convertErr(true, v)
}
}
// ToInteger accepts any numeric value in int32 range, or canonical string.
func ToInteger(v interface{}) (int32, error) {
v, err := Validate(v)
if err != nil {
return 0, err
}
switch v := v.(type) {
case int32:
return v, nil
case string:
return ParseInteger(v)
default:
return 0, convertErr(int32(0), v)
}
}
// ToString returns a string value unaltered.
//
// This function does not perform canonical string encoding, use one of the
// Format functions for that.
func ToString(v interface{}) (string, error) {
v, err := Validate(v)
if err != nil {
return "", err
}
switch v := v.(type) {
case string:
return v, nil
default:
return "", convertErr("", v)
}
}
// ToBinary returns a []byte value, decoding from base64 string if necessary.
func ToBinary(v interface{}) ([]byte, error) {
v, err := Validate(v)
if err != nil {
return nil, err
}
switch v := v.(type) {
case []byte:
return v, nil
case string:
return base64.StdEncoding.DecodeString(v)
default:
return nil, convertErr([]byte(nil), v)
}
}
// ToURL returns a *url.URL value, parsing from string if necessary.
func ToURL(v interface{}) (*url.URL, error) {
v, err := Validate(v)
if err != nil {
return nil, err
}
switch v := v.(type) {
case *URI:
return &v.URL, nil
case URI:
return &v.URL, nil
case *URIRef:
return &v.URL, nil
case URIRef:
return &v.URL, nil
case string:
u, err := url.Parse(v)
if err != nil {
return nil, err
}
return u, nil
default:
return nil, convertErr((*url.URL)(nil), v)
}
}
// ToTime returns a time.Time value, parsing from RFC3339 string if necessary.
func ToTime(v interface{}) (time.Time, error) {
v, err := Validate(v)
if err != nil {
return time.Time{}, err
}
switch v := v.(type) {
case Timestamp:
return v.Time, nil
case string:
ts, err := time.Parse(time.RFC3339Nano, v)
if err != nil {
return time.Time{}, err
}
return ts, nil
default:
return time.Time{}, convertErr(time.Time{}, v)
}
}
func IsZero(v interface{}) bool {
// Fast path
if v == nil {
return true
}
if s, ok := v.(string); ok && s == "" {
return true
}
return reflect.ValueOf(v).IsZero()
}
type ConvertErr struct {
// Value being converted
Value interface{}
// Type of attempted conversion
Type reflect.Type
extra string
}
func (e *ConvertErr) Error() string {
return fmt.Sprintf("cannot convert %#v to %s%s", e.Value, e.Type, e.extra)
}
func convertErr(target, v interface{}) *ConvertErr {
return &ConvertErr{Value: v, Type: reflect.TypeOf(target)}
}
func rangeErr(v interface{}) error {
e := convertErr(int32(0), v)
e.extra = ": out of range"
return e
}

View File

@ -1,9 +0,0 @@
language: go
go:
- 1.4.3
- 1.5.3
- tip
script:
- go test -v ./...

View File

@ -1,10 +0,0 @@
# How to contribute
We definitely welcome patches and contribution to this project!
### Legal requirements
In order to protect both you and ourselves, you will need to sign the
[Contributor License Agreement](https://cla.developers.google.com/clas).
You may have already signed it for other Google projects.

View File

@ -1,9 +0,0 @@
Paul Borman <borman@google.com>
bmatsuo
shawnps
theory
jboverfelt
dsymonds
cd1
wallclockbuilder
dansouza

Some files were not shown because too many files have changed in this diff Show More