Files
insights-go/message.go
T

93 lines
3.1 KiB
Go

package insights
import (
"time"
json "github.com/goccy/go-json"
"github.com/google/uuid"
)
// Values implementing this interface are used by insights clients to notify
// the application when a message send succeeded or failed.
//
// Callback methods are called by a client's internal goroutines. There are no
// guarantees about which goroutine triggers the callbacks, whether calls are
// made sequentially or in parallel, or whether callback order matches enqueue order.
//
// Callback methods must return quickly and avoid long blocking operations so
// they do not interfere with the client's internal workflow.
type Callback interface {
// Success is called for every message that was successfully sent to the API.
Success(APIMessage)
// Failure is called for every message that failed to be sent to the API and
// will be discarded by the client. The error describes the send or serialization failure.
Failure(APIMessage, error)
}
// This interface is used to represent insights objects that can be sent via
// a client.
//
// Types like insights.Capture, insights.Alias, etc... implement this interface
// and therefore can be passed to the insights.Client.Enqueue method.
type Message interface {
// Validate checks the internal structure of the message. It returns nil when
// the message is valid or an error describing the invalid field.
Validate() error
// APIfy converts the message into its PostHog batch API representation.
APIfy() APIMessage
// internal prevents external packages from satisfying Message. Calling it panics.
internal()
}
// Returns the time value passed as first argument, unless it's the zero-value,
// in that case the default value passed as second argument is returned.
func makeTimestamp(t time.Time, def time.Time) time.Time {
if t == (time.Time{}) {
return def
}
return t
}
// makeUUID returns the UUID passed as first argument if non-empty,
// otherwise generates and returns a new random UUID (v4).
func makeUUID(u string) string {
if u != "" {
return u
}
return uuid.New().String()
}
// batch represents objects sent to the /batch/ endpoint with pre-serialized messages.
// Messages are pre-serialized as json.RawMessage for efficient batch building -
// json.Marshal embeds them directly without re-encoding.
type batch struct {
ApiKey string `json:"api_key"`
HistoricalMigration bool `json:"historical_migration,omitempty"`
Messages []json.RawMessage `json:"batch"`
}
// APIMessage is a wire-format message produced by Message.APIfy and passed to callbacks.
type APIMessage interface{}
// prepareForSend creates the API message and serializes it to JSON.
// Returns pre-serialized JSON for efficient batch building, the original
// APIMessage for callbacks, and any serialization error.
// Size is derived from len(json.RawMessage) when needed - O(1) operation.
func prepareForSend(msg Message) (json.RawMessage, APIMessage, error) {
apiMsg := msg.APIfy()
data, err := json.Marshal(apiMsg)
if err != nil {
return nil, apiMsg, err
}
return json.RawMessage(data), apiMsg, nil
}
const (
maxBatchBytes = 500000
maxMessageBytes = 500000
)