courier

package module
v0.3.9 Latest Latest
Warning

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

Go to latest
Published: Aug 20, 2017 License: AGPL-3.0 Imports: 28 Imported by: 87

README

Courier Build Status Coverage Status Go Report Card

Install Courier 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 $(go list ./... | grep -v /vendor/) -cover -bench=.

Documentation

Index

Constants

View Source
const (
	// ConfigAuthToken is a constant key for channel configs
	ConfigAuthToken = "auth_token"

	// ConfigUsername is a constant key for channel configs
	ConfigUsername = "username"

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

	// ConfigAPIKey is a constant key for channel configs
	ConfigAPIKey = "api_key"

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

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

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

	// ConfigContentType is a constant key for channel configs
	ConfigContentType = "content_type"
)
View Source
const (
	// FacebookScheme is the scheme used for Facebook identifiers
	FacebookScheme string = "facebook"

	// TelegramScheme is the scheme used for telegram identifier
	TelegramScheme string = "telegram"

	// TelScheme is the scheme used for telephone numbers
	TelScheme string = "tel"

	// TwitterScheme is the scheme used for Twitter identifiers
	TwitterScheme string = "twitter"
)
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 NilChannelID = ChannelID{null.NewInt(0, false)}

NilChannelID is our nil value for ChannelIDs

View Source
var NilChannelUUID = ChannelUUID{uuid.Nil}

NilChannelUUID is our nil value for channel UUIDs

View Source
var NilMsgID = MsgID{null.NewInt(0, false)}

NilMsgID is our nil value for MsgID

View Source
var NilMsgUUID = MsgUUID{uuid.Nil}

NilMsgUUID is a "zero value" message UUID

View Source
var NilURN = URN("")

NilURN is our constant for nil URNs

Functions

func EnsureSpoolDirPresent

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

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

func GetTextAndAttachments added in v0.2.0

func GetTextAndAttachments(m Msg) string

GetTextAndAttachments returns both the text of our message as well as any attachments, newline delimited

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 SplitAttachment added in v0.2.0

func SplitAttachment(attachment string) (string, string)

SplitAttachment takes an attachment string and returns the media type and URL for the attachment

func WriteError

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

WriteError writes a JSON response for the passed in error

func WriteIgnored

func WriteIgnored(w http.ResponseWriter, r *http.Request, message string) error

WriteIgnored writes a JSON response for the passed in message

func WriteReceiveSuccess

func WriteReceiveSuccess(w http.ResponseWriter, r *http.Request, msg Msg) error

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

func WriteStatusSuccess

func WriteStatusSuccess(w http.ResponseWriter, r *http.Request, status 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 the backend closing any db connections it has open
	Stop() error

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

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

	// NewOutgoingMsg creates a new outgoing message from the given params
	NewOutgoingMsg(channel Channel, urn URN, text string) Msg

	// WriteMsg writes the passed in message to our backend
	WriteMsg(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(MsgStatus) error

	// WriteChannelLogs writes the passed in channel logs to our backend
	WriteChannelLogs([]*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() (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(msg 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(Msg, MsgStatus)

	// StopMsgContact marks the contact for the passed in msg as stopped
	StopMsgContact(Msg)

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

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

func NewBackend

func NewBackend(config *config.Courier) (Backend, error)

NewBackend creates the type of backend passed in

type BackendConstructorFunc

type BackendConstructorFunc func(*config.Courier) Backend

BackendConstructorFunc defines a function to create a particular backend type

type Channel

type Channel interface {
	UUID() ChannelUUID
	ChannelType() ChannelType
	Schemes() []string
	Country() string
	Address() string
	ConfigForKey(key string, defaultValue interface{}) interface{}
	StringConfigForKey(key string, defaultValue string) string
}

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 ChannelActionHandlerFunc

type ChannelActionHandlerFunc func(Channel, http.ResponseWriter, *http.Request) error

ChannelActionHandlerFunc is the interface ChannelHandler functions must satisfy to handle other types of requests. These generic handlers should only be used if they are not dealing with receiving messages or status updates.

The Server will take care of looking up the channel by UUID before passing it to this function.

type ChannelHandler

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

ChannelHandler is the interface all handlers must satisfy

type ChannelID added in v0.2.0

type ChannelID struct {
	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

type ChannelLog added in v0.2.0

type ChannelLog struct {
	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(channel Channel, msgID MsgID, method string, url string, statusCode int, err error,
	request string, response string, elapsed time.Duration, createdOn time.Time) *ChannelLog

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

func NewChannelLogFromRR added in v0.2.0

func NewChannelLogFromRR(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

type ChannelReceiveMsgFunc added in v0.2.0

type ChannelReceiveMsgFunc func(Channel, http.ResponseWriter, *http.Request) ([]Msg, error)

ChannelReceiveMsgFunc is the interface ChannelHandler functions must satisfy to handle incoming msgs The Server will take care of looking up the channel by UUID before passing it to this function.

type ChannelType

type ChannelType string

ChannelType is our typing of the two char channel types

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 ChannelUpdateStatusFunc added in v0.2.0

type ChannelUpdateStatusFunc func(Channel, http.ResponseWriter, *http.Request) ([]MsgStatus, error)

ChannelUpdateStatusFunc is the interface ChannelHandler functions must satisfy to handle incoming status requests. The Server will take care of looking up the channel by UUID before passing it to this function.

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 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) 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) GetChannel

func (mb *MockBackend) GetChannel(cType ChannelType, uuid ChannelUUID) (Channel, error)

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

func (*MockBackend) GetLastQueueMsg

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

GetLastQueueMsg returns the last message queued to the server

func (*MockBackend) GetLastStoppedMsgContact added in v0.2.0

func (mb *MockBackend) GetLastStoppedMsgContact() Msg

GetLastStoppedMsgContact returns the last msg contact

func (*MockBackend) Health

func (mb *MockBackend) Health() string

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

func (*MockBackend) MarkOutgoingMsgComplete added in v0.2.0

func (mb *MockBackend) MarkOutgoingMsgComplete(msg Msg, s MsgStatus)

MarkOutgoingMsgComplete marks the passed msg as having been dealt with

func (*MockBackend) NewIncomingMsg added in v0.2.0

func (mb *MockBackend) NewIncomingMsg(channel Channel, urn 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, urn URN, text string) Msg

NewOutgoingMsg creates a new outgoing message from the given params

func (*MockBackend) PopNextOutgoingMsg added in v0.2.0

func (mb *MockBackend) PopNextOutgoingMsg() (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) 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) Stop

func (mb *MockBackend) Stop() error

Stop stops our mock backend

func (*MockBackend) StopMsgContact added in v0.2.0

func (mb *MockBackend) StopMsgContact(msg Msg)

StopMsgContact stops the contact for the passed in msg

func (*MockBackend) WasMsgSent added in v0.2.0

func (mb *MockBackend) WasMsgSent(msg Msg) (bool, error)

WasMsgSent returns whether the passed in msg was already sent

func (*MockBackend) WriteChannelLogs added in v0.2.0

func (mb *MockBackend) WriteChannelLogs(logs []*ChannelLog) error

WriteChannelLogs writes the passed in channel logs to the DB

func (*MockBackend) WriteMsg

func (mb *MockBackend) WriteMsg(m Msg) error

WriteMsg queues the passed in message internally

func (*MockBackend) WriteMsgStatus

func (mb *MockBackend) WriteMsgStatus(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) 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) 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 {
	Channel() Channel
	ID() MsgID
	UUID() MsgUUID
	Text() string
	Attachments() []string
	ExternalID() string
	URN() URN
	ContactName() string

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

	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
}

Msg is our interface to represent an incoming or outgoing message

type MsgID

type MsgID struct {
	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) String

func (i MsgID) String() string

String satisfies the Stringer interface

type MsgStatus

type MsgStatus interface {
	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) Send added in v0.2.0

func (w *Sender) Send()

Send is our main work loop for our worker. The Worker marks itself as available for work to the foreman, then waits for the next job

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.Courier

	AddChannelRoute(handler ChannelHandler, method string, action string, handlerFunc ChannelActionHandlerFunc) error
	AddReceiveMsgRoute(handler ChannelHandler, method string, action string, handlerFunc ChannelReceiveMsgFunc) error
	AddUpdateStatusRoute(handler ChannelHandler, method string, action string, handlerFunc ChannelUpdateStatusFunc) error

	SendMsg(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.Courier, 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.Courier, 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 URN

type URN string

URN represents a Universal Resource Name, we use this for contact identifiers like phone numbers etc..

func NewTelURNForChannel

func NewTelURNForChannel(number string, channel Channel) URN

NewTelURNForChannel returns a URN for the passed in telephone number and channel

func NewTelURNForCountry

func NewTelURNForCountry(number string, country string) URN

NewTelURNForCountry returns a URN for the passed in telephone number and country code ("US")

func NewTelegramURN

func NewTelegramURN(identifier int64, display string) URN

NewTelegramURN returns a URN for the passed in telegram identifier

func NewURNFromParts

func NewURNFromParts(scheme string, path string, display string) (URN, error)

NewURNFromParts returns a new URN for the given scheme, path and display

func (URN) Display added in v0.3.0

func (u URN) Display() null.String

Display returns the display portion for the URN (if any)

func (URN) Identity added in v0.3.0

func (u URN) Identity() string

Identity returns the URN with any display attributes stripped

func (URN) Path added in v0.2.0

func (u URN) Path() string

Path returns the path portion for the URN

func (URN) Scheme added in v0.2.0

func (u URN) Scheme() string

Scheme returns the scheme portion for the URN

func (URN) String added in v0.2.0

func (u URN) String() string

String returns a string representation of our URN

Jump to

Keyboard shortcuts

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