courier

package module
v1.2.160 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Jun 11, 2019 License: AGPL-3.0 Imports: 30 Imported by: 86

README

Courier Build Status codecov Go Report Card

About

Courier is a messaging gateway for text-based messaging channels. It abstracts out various different texting mediums and providers, allowing applications to focus on the creation and processing of those messages.

Current courier supports over 36 different channel types, ranging for SMS aggregators like Twilio to IP channels like Facebook and Telegram messenger. The goal is for Courier to support every popular messaging channels and aggregator and we are happy to accept pull requests to help accomplish that.

Courier is currently used to power RapidPro and TextIt but the backend is pluggable, so you can add your own backend to read and write messages.

Deploying

As courier is a go application, it compiles to a binary and that binary along with the config file is all you need to run it on your server. You can find bundles for each platform in the releases directory. We recommend running Courier behind a reverse proxy such as nginx or Elastic Load Balancer that provides HTTPs encryption.

Configuration

Courier uses a tiered configuration system, each option takes precendence over the ones above it:

  1. The configuration file
  2. Environment variables starting with COURIER_
  3. Command line parameters

We recommend running courier with no changes to the configuration and no parameters, using only environment variables to configure it. You can use % courier --help to see a list of the environment variables and parameters and for more details on each option.

RapidPro Configuration

For use with RapidPro, you will want to configure these settings:

  • COURIER_DOMAIN: The root domain which courier is exposed as (ex textit.in)
  • COURIER_SPOOL_DIR: A local path where courier can spool files if the database is down, should be writable. (ex: /home/courier/spool)
  • COURIER_DB: Details parameters used to connect to the Postgres RapidPro database (ex: postgres://textit:fooman@rds.courier.io/5432/textit)
  • COURIER_REDIS: Details parameters to use to connect to Redis RapidPro database (ex: redis://redis-internal.courier.io:6379/13)

For writing of message attachments, Courier needs access to an S3 bucket, you can configure access to your bucket via:

  • COURIER_S3_REGION: The region for your S3 bucket (ex: ew-west-1)
  • COURIER_S3_MEDIA_BUCKET: The name of your S3 bucket (ex: dl-courier)
  • COURIER_S3_MEDIA_PREFIX: The prefix to use for filenames of attachments added to your bucket (ex: attachments)
  • COURIER_AWS_ACCESS_KEY_ID: The AWS access key id used to authenticate to AWS
  • COURIER_AWS_SECRET_ACCESS_KEY The AWS secret access key used to authenticate to AWS

Recommended settings for error and performance monitoring:

  • COURIER_LIBRATO_USERNAME: The username to use for logging of events to Librato
  • COURIER_LIBRATO_TOKEN: The token to use for logging of events to Librato
  • COURIER_SENTRY_DSN: The DSN to use when logging errors to Sentry

Development

Install Courier source in your workspace with:

go get github.com/nyaruka/courier

Build Courier with:

go install github.com/nyaruka/courier/cmd/...

This will create a new executable in $GOPATH/bin called courier.

To run the tests you need to create the test database:

$ createdb courier_test
$ createuser -P -E courier
$ psql -d courier_test -f backends/rapidpro/schema.sql
$ psql -d courier_test -c "GRANT ALL PRIVILEGES ON ALL TABLES IN SCHEMA public TO courier;"
$ psql -d courier_test -c "GRANT ALL PRIVILEGES ON ALL SEQUENCES IN SCHEMA public TO courier;"

To run all of the tests including benchmarks:

go test github.com/nyaruka/courier/... -p=1 -bench=.

Documentation

Index

Constants

View Source
const (
	// ConfigAPIKey is a constant key for channel configs
	ConfigAPIKey = "api_key"

	// ConfigAuthToken is a constant key for channel configs
	ConfigAuthToken = "auth_token"

	// ConfigBaseURL is a constant key for channel configs
	ConfigBaseURL = "base_url"

	// ConfigCallbackDomain is the domain that should be used for this channel when registering callbacks
	ConfigCallbackDomain = "callback_domain"

	// ConfigContentType is a constant key for channel configs
	ConfigContentType = "content_type"

	// ConfigMaxLength is the maximum size of a message in characters
	ConfigMaxLength = "max_length"

	// ConfigPassword is a constant key for channel configs
	ConfigPassword = "password"

	// ConfigSecret is the secret used for signing commands by the channel
	ConfigSecret = "secret"

	// ConfigSendAuthorization is a constant key for channel configs
	ConfigSendAuthorization = "send_authorization"

	// ConfigSendBody is a constant key for channel configs
	ConfigSendBody = "body"

	// ConfigSendMethod is a constant key for channel configs
	ConfigSendMethod = "method"

	// ConfigSendURL is a constant key for channel configs
	ConfigSendURL = "send_url"

	// ConfigUsername is a constant key for channel configs
	ConfigUsername = "username"
)
View Source
const NilChannelID = ChannelID(0)

NilChannelID represents a nil channel id

View Source
const NilStatusCode int = 417

NilStatusCode is used when we have an error before even sending anything

Variables

View Source
var AnyChannelType = ChannelType("")

AnyChannelType is our empty channel type used when doing lookups without channel type assertions

View Source
var ErrChannelExpired = errors.New("channel expired")

ErrChannelExpired is returned when our cached channel has outlived it's TTL

View Source
var ErrChannelNotFound = errors.New("channel not found")

ErrChannelNotFound is returned when we fail to find a channel in the db

View Source
var ErrChannelWrongType = errors.New("channel type wrong")

ErrChannelWrongType is returned when we find a channel with the set UUID but with a different type

View Source
var ErrMsgNotFound = errors.New("message not found")

ErrMsgNotFound is returned when trying to queue the status for a Msg that doesn't exit

View Source
var ErrWrongIncomingMsgStatus = errors.New("Incoming messages can only be PENDING or HANDLED")

ErrWrongIncomingMsgStatus use do ignore the status update if the DB raise this

View Source
var NilChannelUUID = ChannelUUID{uuid.Nil}

NilChannelUUID is our nil value for channel UUIDs

View Source
var NilContactUUID = ContactUUID{uuid.Nil}

NilContactUUID is our nil value for contact UUIDs

View Source
var NilMsgID = MsgID(0)

NilMsgID is our nil value for MsgID

View Source
var NilMsgUUID = MsgUUID{uuid.Nil}

NilMsgUUID is a "zero value" message UUID

Functions

func EnsureSpoolDirPresent

func EnsureSpoolDirPresent(spoolDir string, subdir string) (err error)

EnsureSpoolDirPresent checks that the passed in spool directory is present and writable

func LogChannelEventReceived added in v0.9.2

func LogChannelEventReceived(r *http.Request, event ChannelEvent)

LogChannelEventReceived logs that we received the passed in channel event

func LogMsgReceived added in v0.9.2

func LogMsgReceived(r *http.Request, msg Msg)

LogMsgReceived logs that we received the passed in message

func LogMsgStatusReceived added in v0.9.2

func LogMsgStatusReceived(r *http.Request, status MsgStatus)

LogMsgStatusReceived logs our that we received a new MsgStatus

func LogRequestError added in v1.1.3

func LogRequestError(r *http.Request, channel Channel, err error)

LogRequestError logs that errored during parsing (this is logged as an info as it isn't an error on our side)

func LogRequestHandled added in v1.2.34

func LogRequestHandled(r *http.Request, channel Channel, details string)

LogRequestHandled logs that we handled the passed in request but didn't create any events

func LogRequestIgnored added in v0.9.2

func LogRequestIgnored(r *http.Request, channel Channel, details string)

LogRequestIgnored logs that we ignored the passed in request

func RegisterBackend

func RegisterBackend(backendType string, constructorFunc BackendConstructorFunc)

RegisterBackend adds a new backend, called by individual backends in their init() func

func RegisterFlusher

func RegisterFlusher(directory string, flusherFunc FlusherFunc)

RegisterFlusher creates a new walker which we will use to flush files from the passed in directory

func RegisterHandler

func RegisterHandler(handler ChannelHandler)

RegisterHandler adds a new handler for a channel type, this is called by individual handlers when they are initialized

func WriteAndLogUnauthorized added in v1.2.48

func WriteAndLogUnauthorized(ctx context.Context, w http.ResponseWriter, r *http.Request, c Channel, err error) error

WriteAndLogUnauthorized writes a JSON response for the passed in message and logs an info message

func WriteChannelEventSuccess added in v0.8.0

func WriteChannelEventSuccess(ctx context.Context, w http.ResponseWriter, r *http.Request, event ChannelEvent) error

WriteChannelEventSuccess writes a JSON response for the passed in event indicating we handled it

func WriteDataResponse added in v1.1.3

func WriteDataResponse(ctx context.Context, w http.ResponseWriter, statusCode int, message string, data []interface{}) error

WriteDataResponse writes a JSON formatted response with the passed in status code, message and data

func WriteError

func WriteError(ctx context.Context, w http.ResponseWriter, r *http.Request, err error) error

WriteError writes a JSON response for the passed in error

func WriteIgnored

func WriteIgnored(ctx context.Context, w http.ResponseWriter, r *http.Request, details string) error

WriteIgnored writes a JSON response indicating that we ignored the request

func WriteMsgSuccess added in v0.8.0

func WriteMsgSuccess(ctx context.Context, w http.ResponseWriter, r *http.Request, msgs []Msg) error

WriteMsgSuccess writes a JSON response for the passed in msg indicating we handled it

func WriteStatusSuccess

func WriteStatusSuccess(ctx context.Context, w http.ResponseWriter, r *http.Request, statuses []MsgStatus) error

WriteStatusSuccess writes a JSON response for the passed in status update indicating we handled it

func WriteToSpool

func WriteToSpool(spoolDir string, subdir string, contents interface{}) error

WriteToSpool writes the passed in object to the passed in subdir

Types

type Backend

type Backend interface {
	// Start starts the backend and opens any db connections it needs
	Start() error

	// Stop stops any backend processes
	Stop() error

	// Cleanup closes any active connections to databases
	Cleanup() error

	// GetChannel returns the channel with the passed in type and UUID
	GetChannel(context.Context, ChannelType, ChannelUUID) (Channel, error)

	// GetContact returns (or creates) the contact for the passed in channel and URN
	GetContact(context context.Context, channel Channel, urn urns.URN, auth string, name string) (Contact, error)

	// AddURNtoContact adds a URN to the passed in contact
	AddURNtoContact(context context.Context, channel Channel, contact Contact, urn urns.URN) (urns.URN, error)

	// RemoveURNFromcontact removes a URN from the passed in contact
	RemoveURNfromContact(context context.Context, channel Channel, contact Contact, urn urns.URN) (urns.URN, error)

	// NewIncomingMsg creates a new message from the given params
	NewIncomingMsg(channel Channel, urn urns.URN, text string) Msg

	// WriteMsg writes the passed in message to our backend
	WriteMsg(context.Context, Msg) error

	// NewMsgStatusForID creates a new Status object for the given message id
	NewMsgStatusForID(Channel, MsgID, MsgStatusValue) MsgStatus

	// NewMsgStatusForExternalID creates a new Status object for the given external id
	NewMsgStatusForExternalID(Channel, string, MsgStatusValue) MsgStatus

	// WriteMsgStatus writes the passed in status update to our backend
	WriteMsgStatus(context.Context, MsgStatus) error

	// NewChannelEvent creates a new channel event for the given channel and event type
	NewChannelEvent(Channel, ChannelEventType, urns.URN) ChannelEvent

	// WriteChannelEvent writes the passed in channel even returning any error
	WriteChannelEvent(context.Context, ChannelEvent) error

	// WriteChannelLogs writes the passed in channel logs to our backend
	WriteChannelLogs(context.Context, []*ChannelLog) error

	// PopNextOutgoingMsg returns the next message that needs to be sent, callers should call MarkOutgoingMsgComplete with the
	// returned message when they have dealt with the message (regardless of whether it was sent or not)
	PopNextOutgoingMsg(context.Context) (Msg, error)

	// WasMsgSent returns whether the backend thinks the passed in message was already sent. This can be used in cases where
	// a backend wants to implement a failsafe against double sending messages (say if they were double queued)
	WasMsgSent(context.Context, Msg) (bool, error)

	// MarkOutgoingMsgComplete marks the passed in message as having been processed. Note this should be called even in the case
	// of errors during sending as it will manage the number of active workers per channel. The optional status parameter can be
	// used to determine any sort of deduping of msg sends
	MarkOutgoingMsgComplete(context.Context, Msg, MsgStatus)

	// Check if external ID has been seen in a period
	CheckExternalIDSeen(Msg) Msg

	// Mark a external ID as seen for a period
	WriteExternalIDSeen(Msg)

	// Health returns a string describing any health problems the backend has, or empty string if all is well
	Health() string

	// Status returns a string describing the current status, this can detail queue sizes or other attributes
	Status() string

	// Heartbeat is called every minute, it can be used by backends to log status to a dashboard such as librato
	Heartbeat() error

	// RedisPool returns the redisPool for this backend
	RedisPool() *redis.Pool
}

Backend represents the part of Courier that deals with looking up and writing channels and results

func NewBackend

func NewBackend(config *Config) (Backend, error)

NewBackend creates the type of backend passed in

type BackendConstructorFunc

type BackendConstructorFunc func(*Config) Backend

BackendConstructorFunc defines a function to create a particular backend type

type Channel

type Channel interface {
	UUID() ChannelUUID
	Name() string
	ChannelType() ChannelType
	Schemes() []string
	Country() string
	Address() string

	// CallbackDomain returns the domain that should be used for any callbacks the channel registers
	CallbackDomain(fallbackDomain string) string

	ConfigForKey(key string, defaultValue interface{}) interface{}
	StringConfigForKey(key string, defaultValue string) string
	BoolConfigForKey(key string, defaultValue bool) bool
	IntConfigForKey(key string, defaultValue int) int
	OrgConfigForKey(key string, defaultValue interface{}) interface{}
}

Channel defines the general interface backend Channel implementations must adhere to

func NewMockChannel

func NewMockChannel(uuid string, channelType string, address string, country string, config map[string]interface{}) Channel

NewMockChannel creates a new mock channel for the passed in type, address, country and config

type ChannelEvent added in v0.8.0

type ChannelEvent interface {
	ChannelUUID() ChannelUUID
	URN() urns.URN
	EventType() ChannelEventType
	Extra() map[string]interface{}
	CreatedOn() time.Time
	OccurredOn() time.Time

	Logs() []*ChannelLog
	AddLog(log *ChannelLog)

	WithContactName(name string) ChannelEvent
	WithExtra(extra map[string]interface{}) ChannelEvent
	WithOccurredOn(time.Time) ChannelEvent

	EventID() int64
}

ChannelEvent represents an event on a channel, such as a follow, new conversation or referral

type ChannelEventType added in v0.8.0

type ChannelEventType string

ChannelEventType is the type of channel event this is

const (
	NewConversation ChannelEventType = "new_conversation"
	Referral        ChannelEventType = "referral"
	StopContact     ChannelEventType = "stop_contact"
	WelcomeMessage  ChannelEventType = "welcome_message"
)

Possible values for ChannelEventTypes

type ChannelHandleFunc added in v1.0.28

type ChannelHandleFunc func(context.Context, Channel, http.ResponseWriter, *http.Request) ([]Event, error)

ChannelHandleFunc is the interface ChannelHandlers must satisfy to handle incoming requests. The Server will take care of looking up the channel by UUID before passing it to this function. Errors in format of the request or by the caller should be handled and logged internally. Errors in execution or in courier itself should be passed back.

type ChannelHandler

type ChannelHandler interface {
	Initialize(Server) error
	ChannelType() ChannelType
	ChannelName() string
	SendMsg(context.Context, Msg) (MsgStatus, error)
}

ChannelHandler is the interface all handlers must satisfy

func GetHandler added in v1.1.3

func GetHandler(ct ChannelType) ChannelHandler

GetHandler returns the handler for the passed in channel type, or nil if not found

type ChannelID added in v0.2.0

type ChannelID null.Int

ChannelID is our SQL type for a channel's id

func NewChannelID added in v0.2.0

func NewChannelID(id int64) ChannelID

NewChannelID creates a new ChannelID for the passed in int64

func (ChannelID) MarshalJSON added in v0.2.0

func (i ChannelID) MarshalJSON() ([]byte, error)

MarshalJSON marshals into JSON. 0 values will become null

func (*ChannelID) Scan added in v1.2.151

func (i *ChannelID) Scan(value interface{}) error

Scan scans from the db value. null values become 0

func (*ChannelID) UnmarshalJSON added in v0.2.0

func (i *ChannelID) UnmarshalJSON(b []byte) error

UnmarshalJSON unmarshals from JSON. null values become 0

func (ChannelID) Value added in v1.2.151

func (i ChannelID) Value() (driver.Value, error)

Value returns the db value, null is returned for 0

type ChannelLog added in v0.2.0

type ChannelLog struct {
	Description string
	Channel     Channel
	MsgID       MsgID
	Method      string
	URL         string
	StatusCode  int
	Error       string
	Request     string
	Response    string
	Elapsed     time.Duration
	CreatedOn   time.Time
}

ChannelLog represents the log for a msg being received, sent or having its status updated. It includes the HTTP request and response for the action as well as the channel it was performed on and an option ID of the msg (for some error cases we may log without a msg id)

func NewChannelLog added in v0.2.0

func NewChannelLog(description string, channel Channel, msgID MsgID, method string, url string, statusCode int,
	request string, response string, elapsed time.Duration, err error) *ChannelLog

NewChannelLog creates a new channel log for the passed in channel, id, and request and response info

func NewChannelLogFromError added in v1.2.40

func NewChannelLogFromError(description string, channel Channel, msgID MsgID, elapsed time.Duration, err error) *ChannelLog

NewChannelLogFromError creates a new channel log for the passed in channel, msg id and error

func NewChannelLogFromRR added in v0.2.0

func NewChannelLogFromRR(description string, channel Channel, msgID MsgID, rr *utils.RequestResponse) *ChannelLog

NewChannelLogFromRR creates a new channel log for the passed in channel, id, and request/response log

func (*ChannelLog) String added in v0.2.0

func (l *ChannelLog) String() string

func (*ChannelLog) WithError added in v0.6.3

func (l *ChannelLog) WithError(description string, err error) *ChannelLog

WithError augments the passed in ChannelLog with the passed in description and error if error is not nil

type ChannelType

type ChannelType string

ChannelType is our typing of the two char channel types

func (ChannelType) String added in v0.4.2

func (ct ChannelType) String() string

type ChannelUUID

type ChannelUUID struct {
	uuid.UUID
}

ChannelUUID is our typing of a channel's UUID

func NewChannelUUID

func NewChannelUUID(u string) (ChannelUUID, error)

NewChannelUUID creates a new ChannelUUID for the passed in string

type Config added in v1.2.98

type Config struct {
	Backend            string `help:"the backend that will be used by courier (currently only rapidpro is supported)"`
	SentryDSN          string `help:"the DSN used for logging errors to Sentry"`
	Domain             string `help:"the domain courier is exposed on"`
	Address            string `help:"the network interface address courier will bind to"`
	Port               int    `help:"the port courier will listen on"`
	DB                 string `help:"URL describing how to connect to the RapidPro database"`
	Redis              string `help:"URL describing how to connect to Redis"`
	SpoolDir           string `help:"the local directory where courier will write statuses or msgs that need to be retried (needs to be writable)"`
	S3Endpoint         string `help:"the S3 endpoint we will write attachments to"`
	S3Region           string `help:"the S3 region we will write attachments to"`
	S3MediaBucket      string `help:"the S3 bucket we will write attachments to"`
	S3MediaPrefix      string `help:"the prefix that will be added to attachment filenames"`
	S3DisableSSL       bool   `` /* 158-byte string literal not displayed */
	S3ForcePathStyle   bool   `` /* 127-byte string literal not displayed */
	AWSAccessKeyID     string `help:"the access key id to use when authenticating S3"`
	AWSSecretAccessKey string `help:"the secret access key id to use when authenticating S3"`
	MaxWorkers         int    `help:"the maximum number of go routines that will be used for sending (set to 0 to disable sending)"`
	LibratoUsername    string `help:"the username that will be used to authenticate to Librato"`
	LibratoToken       string `help:"the token that will be used to authenticate to Librato"`
	StatusUsername     string `help:"the username that is needed to authenticate against the /status endpoint"`
	StatusPassword     string `help:"the password that is needed to authenticate against the /status endpoint"`
	LogLevel           string `help:"the logging level courier should use"`
	Version            string `help:"the version that will be used in request and response headers"`

	// IncludeChannels is the list of channels to enable, empty means include all
	IncludeChannels []string

	// ExcludeChannels is the list of channels to exclude, empty means exclude none
	ExcludeChannels []string
}

Config is our top level configuration object

func LoadConfig added in v1.2.98

func LoadConfig(filename string) *Config

LoadConfig loads our configuration from the passed in filename

func NewConfig added in v1.2.98

func NewConfig() *Config

NewConfig returns a new default configuration object

type Contact added in v1.2.45

type Contact interface {
	UUID() ContactUUID
}

Contact defines the attributes on a contact, for our purposes that is just a contact UUID

type ContactUUID added in v1.2.45

type ContactUUID struct {
	uuid.UUID
}

ContactUUID is our typing of a contact's UUID

func NewContactUUID added in v1.2.45

func NewContactUUID(u string) (ContactUUID, error)

NewContactUUID creates a new ContactUUID for the passed in string

type ErrorData added in v1.1.3

type ErrorData struct {
	Type  string `json:"type"`
	Error string `json:"error"`
}

ErrorData is our response payload for an error

func NewErrorData added in v1.1.3

func NewErrorData(err string) ErrorData

NewErrorData creates a new data segment for the passed in error string

type Event added in v1.0.28

type Event interface {
	EventID() int64
}

Event is our interface for the types of things a ChannelHandleFunc can return.

type EventReceiveData added in v1.1.3

type EventReceiveData struct {
	Type        string                 `json:"type"`
	ChannelUUID ChannelUUID            `json:"channel_uuid"`
	EventType   ChannelEventType       `json:"event_type"`
	URN         urns.URN               `json:"urn"`
	ReceivedOn  time.Time              `json:"received_on"`
	Extra       map[string]interface{} `json:"extra,omitempty"`
}

EventReceiveData is our response payload for a channel event

func NewEventReceiveData added in v1.1.3

func NewEventReceiveData(event ChannelEvent) EventReceiveData

NewEventReceiveData creates a new receive data for the passed in event

type FlusherFunc

type FlusherFunc func(filename string, contents []byte) error

FlusherFunc defines our interface for flushers, they are handed a filename and byte blob and are expected to try to flush that to the db, returning an error if the db is still down

type Foreman added in v0.2.0

type Foreman struct {
	// contains filtered or unexported fields
}

Foreman takes care of managing our set of sending workers and assigns msgs for each to send

func NewForeman added in v0.2.0

func NewForeman(server Server, maxSenders int) *Foreman

NewForeman creates a new Foreman for the passed in server with the number of max senders

func (*Foreman) Assign added in v0.2.0

func (f *Foreman) Assign()

Assign is our main loop for the Foreman, it takes care of popping the next outgoing messages from our backend and assigning them to workers

func (*Foreman) Start added in v0.2.0

func (f *Foreman) Start()

Start starts the foreman and all its senders, assigning jobs while there are some

func (*Foreman) Stop added in v0.2.0

func (f *Foreman) Stop()

Stop stops the foreman and all its senders, the wait group of the server can be used to track progress

type InfoData added in v1.1.3

type InfoData struct {
	Type string `json:"type"`
	Info string `json:"info"`
}

InfoData is our response payload for an informational message

func NewInfoData added in v1.1.3

func NewInfoData(info string) InfoData

NewInfoData creates a new data segment for the passed in info string

type MediaDownloadRequestBuilder added in v1.2.32

type MediaDownloadRequestBuilder interface {
	BuildDownloadMediaRequest(context.Context, Backend, Channel, string) (*http.Request, error)
}

MediaDownloadRequestBuilder is the interface handlers which can allow a custom way to download attachment media for messages should satisfy

type MockBackend

type MockBackend struct {
	// contains filtered or unexported fields
}

MockBackend is a mocked version of a backend which doesn't require a real database or cache

func NewMockBackend

func NewMockBackend() *MockBackend

NewMockBackend returns a new mock backend suitable for testing

func (*MockBackend) AddChannel

func (mb *MockBackend) AddChannel(channel Channel)

AddChannel adds a test channel to the test server

func (*MockBackend) AddURNtoContact added in v1.2.118

func (mb *MockBackend) AddURNtoContact(context context.Context, channel Channel, contact Contact, urn urns.URN) (urns.URN, error)

AddURNtoContact adds a URN to the passed in contact

func (*MockBackend) CheckExternalIDSeen added in v1.2.143

func (mb *MockBackend) CheckExternalIDSeen(msg Msg) Msg

CheckExternalIDSeen checks if external ID has been seen in a period

func (*MockBackend) Cleanup added in v0.5.3

func (mb *MockBackend) Cleanup() error

Cleanup cleans up any connections that are open

func (*MockBackend) ClearChannels

func (mb *MockBackend) ClearChannels()

ClearChannels is a utility function on our mock server to clear all added channels

func (*MockBackend) ClearQueueMsgs

func (mb *MockBackend) ClearQueueMsgs()

ClearQueueMsgs clears our mock msg queue

func (*MockBackend) ClearSeenExternalIDs added in v1.2.143

func (mb *MockBackend) ClearSeenExternalIDs()

ClearSeenExternalIDs clears our mock seen external ids

func (*MockBackend) GetChannel

func (mb *MockBackend) GetChannel(ctx context.Context, cType ChannelType, uuid ChannelUUID) (Channel, error)

GetChannel returns the channel with the passed in type and channel uuid

func (*MockBackend) GetContact added in v1.2.45

func (mb *MockBackend) GetContact(ctx context.Context, channel Channel, urn urns.URN, auth string, name string) (Contact, error)

GetContact creates a new contact with the passed in channel and URN

func (*MockBackend) GetLastChannelEvent added in v0.8.0

func (mb *MockBackend) GetLastChannelEvent() (ChannelEvent, error)

GetLastChannelEvent returns the last event written to the server

func (*MockBackend) GetLastContactName added in v0.8.0

func (mb *MockBackend) GetLastContactName() string

GetLastContactName returns the contact name set on the last msg or channel event written

func (*MockBackend) GetLastMsgStatus added in v0.9.0

func (mb *MockBackend) GetLastMsgStatus() (MsgStatus, error)

GetLastMsgStatus returns the last status written to the server

func (*MockBackend) GetLastQueueMsg

func (mb *MockBackend) GetLastQueueMsg() (Msg, error)

GetLastQueueMsg returns the last message queued to the server

func (*MockBackend) Health

func (mb *MockBackend) Health() string

Health gives a string representing our health, empty for our mock

func (*MockBackend) Heartbeat added in v1.2.137

func (mb *MockBackend) Heartbeat() error

Heartbeat is a noop for our mock backend

func (*MockBackend) LenQueuedMsgs added in v1.2.98

func (mb *MockBackend) LenQueuedMsgs() int

LenQueuedMsgs Get the length of queued msgs

func (*MockBackend) MarkOutgoingMsgComplete added in v0.2.0

func (mb *MockBackend) MarkOutgoingMsgComplete(ctx context.Context, msg Msg, s MsgStatus)

MarkOutgoingMsgComplete marks the passed msg as having been dealt with

func (*MockBackend) NewChannelEvent added in v0.8.0

func (mb *MockBackend) NewChannelEvent(channel Channel, eventType ChannelEventType, urn urns.URN) ChannelEvent

NewChannelEvent creates a new channel event with the passed in parameters

func (*MockBackend) NewIncomingMsg added in v0.2.0

func (mb *MockBackend) NewIncomingMsg(channel Channel, urn urns.URN, text string) Msg

NewIncomingMsg creates a new message from the given params

func (*MockBackend) NewMsgStatusForExternalID added in v0.2.0

func (mb *MockBackend) NewMsgStatusForExternalID(channel Channel, externalID string, status MsgStatusValue) MsgStatus

NewMsgStatusForExternalID creates a new Status object for the given external id

func (*MockBackend) NewMsgStatusForID added in v0.2.0

func (mb *MockBackend) NewMsgStatusForID(channel Channel, id MsgID, status MsgStatusValue) MsgStatus

NewMsgStatusForID creates a new Status object for the given message id

func (*MockBackend) NewOutgoingMsg added in v0.2.0

func (mb *MockBackend) NewOutgoingMsg(channel Channel, id MsgID, urn urns.URN, text string, highPriority bool, replies []string, responseToID int64, responseToExternalID string) Msg

NewOutgoingMsg creates a new outgoing message from the given params

func (*MockBackend) PopNextOutgoingMsg added in v0.2.0

func (mb *MockBackend) PopNextOutgoingMsg(ctx context.Context) (Msg, error)

PopNextOutgoingMsg returns the next message that should be sent, or nil if there are none to send

func (*MockBackend) PushOutgoingMsg added in v0.2.0

func (mb *MockBackend) PushOutgoingMsg(msg Msg)

PushOutgoingMsg is a test method to add a message to our queue of messages to send

func (*MockBackend) RedisPool added in v1.2.32

func (mb *MockBackend) RedisPool() *redis.Pool

RedisPool returns the redisPool for this backend

func (*MockBackend) RemoveURNfromContact added in v1.2.118

func (mb *MockBackend) RemoveURNfromContact(context context.Context, channel Channel, contact Contact, urn urns.URN) (urns.URN, error)

RemoveURNFromcontact removes a URN from the passed in contact

func (*MockBackend) SetErrorOnQueue

func (mb *MockBackend) SetErrorOnQueue(shouldError bool)

SetErrorOnQueue is a mock method which makes the QueueMsg call throw the passed in error on next call

func (*MockBackend) Start

func (mb *MockBackend) Start() error

Start starts our mock backend

func (*MockBackend) Status added in v0.4.6

func (mb *MockBackend) Status() string

Status returns a string describing the status of the service, queue size etc..

func (*MockBackend) Stop

func (mb *MockBackend) Stop() error

Stop stops our mock backend

func (*MockBackend) WasMsgSent added in v0.2.0

func (mb *MockBackend) WasMsgSent(ctx context.Context, msg Msg) (bool, error)

WasMsgSent returns whether the passed in msg was already sent

func (*MockBackend) WriteChannelEvent added in v0.8.0

func (mb *MockBackend) WriteChannelEvent(ctx context.Context, event ChannelEvent) error

WriteChannelEvent writes the channel event passed in

func (*MockBackend) WriteChannelLogs added in v0.2.0

func (mb *MockBackend) WriteChannelLogs(ctx context.Context, logs []*ChannelLog) error

WriteChannelLogs writes the passed in channel logs to the DB

func (*MockBackend) WriteExternalIDSeen added in v1.2.143

func (mb *MockBackend) WriteExternalIDSeen(msg Msg)

WriteExternalIDSeen marks a external ID as seen for a period

func (*MockBackend) WriteMsg

func (mb *MockBackend) WriteMsg(ctx context.Context, m Msg) error

WriteMsg queues the passed in message internally

func (*MockBackend) WriteMsgStatus

func (mb *MockBackend) WriteMsgStatus(ctx context.Context, status MsgStatus) error

WriteMsgStatus writes the status update to our queue

type MockChannel added in v0.2.0

type MockChannel struct {
	// contains filtered or unexported fields
}

MockChannel implements the Channel interface and is used in our tests

func (*MockChannel) Address added in v0.2.0

func (c *MockChannel) Address() string

Address returns the address of this channel

func (*MockChannel) BoolConfigForKey added in v1.2.159

func (c *MockChannel) BoolConfigForKey(key string, defaultValue bool) bool

BoolConfigForKey returns the config value for the passed in key

func (*MockChannel) CallbackDomain added in v1.0.21

func (c *MockChannel) CallbackDomain(fallbackDomain string) string

CallbackDomain returns the callback domain to use for this channel

func (*MockChannel) ChannelType added in v0.2.0

func (c *MockChannel) ChannelType() ChannelType

ChannelType returns the type of this channel

func (*MockChannel) ConfigForKey added in v0.2.0

func (c *MockChannel) ConfigForKey(key string, defaultValue interface{}) interface{}

ConfigForKey returns the config value for the passed in key

func (*MockChannel) Country added in v0.2.0

func (c *MockChannel) Country() string

Country returns the country this channel is for (if any)

func (*MockChannel) IntConfigForKey added in v1.2.98

func (c *MockChannel) IntConfigForKey(key string, defaultValue int) int

IntConfigForKey returns the config value for the passed in key

func (*MockChannel) Name added in v1.0.9

func (c *MockChannel) Name() string

Name returns the name of this channel, we just return our UUID for our mock instances

func (*MockChannel) OrgConfigForKey added in v1.0.9

func (c *MockChannel) OrgConfigForKey(key string, defaultValue interface{}) interface{}

OrgConfigForKey returns the org config value for the passed in key

func (*MockChannel) Schemes added in v0.3.0

func (c *MockChannel) Schemes() []string

Schemes returns the schemes for this channel

func (*MockChannel) SetConfig added in v0.2.0

func (c *MockChannel) SetConfig(key string, value interface{})

SetConfig sets the passed in config parameter

func (*MockChannel) StringConfigForKey added in v0.2.0

func (c *MockChannel) StringConfigForKey(key string, defaultValue string) string

StringConfigForKey returns the config value for the passed in key

func (*MockChannel) UUID added in v0.2.0

func (c *MockChannel) UUID() ChannelUUID

UUID returns the uuid for this channel

type Msg

type Msg interface {
	ID() MsgID
	UUID() MsgUUID
	Text() string
	Attachments() []string
	ExternalID() string
	URN() urns.URN
	URNAuth() string
	ContactName() string
	QuickReplies() []string
	Metadata() json.RawMessage
	ResponseToID() MsgID
	ResponseToExternalID() string

	Channel() Channel

	ReceivedOn() *time.Time
	SentOn() *time.Time

	HighPriority() bool

	WithContactName(name string) Msg
	WithReceivedOn(date time.Time) Msg
	WithExternalID(id string) Msg
	WithID(id MsgID) Msg
	WithUUID(uuid MsgUUID) Msg
	WithAttachment(url string) Msg
	WithURNAuth(auth string) Msg
	WithMetadata(metadata json.RawMessage) Msg

	EventID() int64
}

Msg is our interface to represent an incoming or outgoing message

type MsgID

type MsgID null.Int

MsgID is our typing of the db int type

func NewMsgID

func NewMsgID(id int64) MsgID

NewMsgID creates a new MsgID for the passed in int64

func (MsgID) MarshalJSON added in v1.2.151

func (i MsgID) MarshalJSON() ([]byte, error)

MarshalJSON marshals into JSON. 0 values will become null

func (*MsgID) Scan added in v1.2.151

func (i *MsgID) Scan(value interface{}) error

Scan scans from the db value. null values become 0

func (MsgID) String

func (i MsgID) String() string

String satisfies the Stringer interface

func (*MsgID) UnmarshalJSON

func (i *MsgID) UnmarshalJSON(b []byte) error

UnmarshalJSON unmarshals from JSON. null values become 0

func (MsgID) Value added in v1.2.151

func (i MsgID) Value() (driver.Value, error)

Value returns the db value, null is returned for 0

type MsgReceiveData added in v1.1.3

type MsgReceiveData struct {
	Type        string      `json:"type"`
	ChannelUUID ChannelUUID `json:"channel_uuid"`
	MsgUUID     MsgUUID     `json:"msg_uuid"`
	Text        string      `json:"text"`
	URN         urns.URN    `json:"urn"`
	Attachments []string    `json:"attachments,omitempty"`
	ExternalID  string      `json:"external_id,omitempty"`
	ReceivedOn  *time.Time  `json:"received_on,omitempty"`
}

MsgReceiveData is our response payload for a received message

func NewMsgReceiveData added in v1.1.3

func NewMsgReceiveData(msg Msg) MsgReceiveData

NewMsgReceiveData creates a new data response for the passed in msg parameters

type MsgStatus

type MsgStatus interface {
	EventID() int64

	ChannelUUID() ChannelUUID
	ID() MsgID

	ExternalID() string
	SetExternalID(string)

	Status() MsgStatusValue
	SetStatus(MsgStatusValue)

	Logs() []*ChannelLog
	AddLog(log *ChannelLog)
}

MsgStatus represents a status update on a message

type MsgStatusValue added in v0.2.0

type MsgStatusValue string

MsgStatusValue is the status of a message

const (
	MsgPending   MsgStatusValue = "P"
	MsgQueued    MsgStatusValue = "Q"
	MsgSent      MsgStatusValue = "S"
	MsgWired     MsgStatusValue = "W"
	MsgErrored   MsgStatusValue = "E"
	MsgDelivered MsgStatusValue = "D"
	MsgFailed    MsgStatusValue = "F"
	NilMsgStatus MsgStatusValue = ""
)

Possible values for MsgStatus

type MsgUUID

type MsgUUID struct {
	uuid.UUID
}

MsgUUID is the UUID of a message which has been received

func NewMsgUUID

func NewMsgUUID() MsgUUID

NewMsgUUID creates a new unique message UUID

func NewMsgUUIDFromString added in v0.2.0

func NewMsgUUIDFromString(uuidString string) MsgUUID

NewMsgUUIDFromString creates a new message UUID for the passed in string

type Sender added in v0.2.0

type Sender struct {
	// contains filtered or unexported fields
}

Sender is our type for a single goroutine that is sending messages

func NewSender added in v0.2.0

func NewSender(foreman *Foreman, id int) *Sender

NewSender creates a new sender responsible for sending messages

func (*Sender) Start added in v0.2.0

func (w *Sender) Start()

Start starts our Sender's goroutine and has it start waiting for tasks from the foreman

func (*Sender) Stop added in v0.2.0

func (w *Sender) Stop()

Stop stops our senders, callers can use the server's wait group to track progress

type Server

type Server interface {
	Config() *Config

	AddHandlerRoute(handler ChannelHandler, method string, action string, handlerFunc ChannelHandleFunc)

	SendMsg(context.Context, Msg) (MsgStatus, error)

	Backend() Backend

	WaitGroup() *sync.WaitGroup
	StopChan() chan bool
	Stopped() bool

	Router() chi.Router

	Start() error
	Stop() error
}

Server is the main interface ChannelHandlers use to interact with backends. It provides an abstraction that makes mocking easier for isolated unit tests

func NewServer

func NewServer(config *Config, backend Backend) Server

NewServer creates a new Server for the passed in configuration. The server will have to be started afterwards, which is when configuration options are checked.

func NewServerWithLogger added in v0.2.0

func NewServerWithLogger(config *Config, backend Backend, logger *logrus.Logger) Server

NewServerWithLogger creates a new Server for the passed in configuration. The server will have to be started afterwards, which is when configuration options are checked.

type StatusData added in v1.1.3

type StatusData struct {
	Type        string         `json:"type"`
	ChannelUUID ChannelUUID    `json:"channel_uuid"`
	Status      MsgStatusValue `json:"status"`
	MsgID       MsgID          `json:"msg_id,omitempty"`
	ExternalID  string         `json:"external_id,omitempty"`
}

StatusData is our response payload for a status update

func NewStatusData added in v1.1.3

func NewStatusData(status MsgStatus) StatusData

NewStatusData creates a new status data object for the passed in status

type URNDescriber added in v1.1.3

type URNDescriber interface {
	DescribeURN(context.Context, Channel, urns.URN) (map[string]string, error)
}

URNDescriber is the interface handlers which can look up URN metadata for new contacts should satisfy.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL