1
0
mirror of https://github.com/jhillyerd/inbucket.git synced 2026-01-24 12:05:57 +00:00

Refactor datastore into it's own package for #67

This commit is contained in:
James Hillyerd
2017-12-26 14:54:49 -08:00
parent cc47895d71
commit 3a4fd3f093
17 changed files with 93 additions and 84 deletions

View File

@@ -1,52 +0,0 @@
package smtpd
import (
"errors"
"io"
"net/mail"
"time"
"github.com/jhillyerd/enmime"
)
var (
// ErrNotExist indicates the requested message does not exist
ErrNotExist = errors.New("Message does not exist")
// ErrNotWritable indicates the message is closed; no longer writable
ErrNotWritable = errors.New("Message not writable")
)
// DataStore is an interface to get Mailboxes stored in Inbucket
type DataStore interface {
MailboxFor(emailAddress string) (Mailbox, error)
AllMailboxes() ([]Mailbox, error)
}
// Mailbox is an interface to get and manipulate messages in a DataStore
type Mailbox interface {
GetMessages() ([]Message, error)
GetMessage(id string) (Message, error)
Purge() error
NewMessage() (Message, error)
Name() string
String() string
}
// Message is an interface for a single message in a Mailbox
type Message interface {
ID() string
From() string
To() []string
Date() time.Time
Subject() string
RawReader() (reader io.ReadCloser, err error)
ReadHeader() (msg *mail.Message, err error)
ReadBody() (body *enmime.Envelope, err error)
ReadRaw() (raw *string, err error)
Append(data []byte) error
Close() error
Delete() error
String() string
Size() int64
}

View File

@@ -11,6 +11,7 @@ import (
"time"
"github.com/jhillyerd/enmime"
"github.com/jhillyerd/inbucket/datastore"
"github.com/jhillyerd/inbucket/log"
)
@@ -33,7 +34,7 @@ type FileMessage struct {
// NewMessage creates a new FileMessage object and sets the Date and Id fields.
// It will also delete messages over messageCap if configured.
func (mb *FileMailbox) NewMessage() (Message, error) {
func (mb *FileMailbox) NewMessage() (datastore.Message, error) {
// Load index
if !mb.indexLoaded {
if err := mb.readIndex(); err != nil {
@@ -165,7 +166,7 @@ func (m *FileMessage) ReadRaw() (raw *string, err error) {
func (m *FileMessage) Append(data []byte) error {
// Prevent Appending to a pre-existing Message
if !m.writable {
return ErrNotWritable
return datastore.ErrNotWritable
}
// Open file for writing if we haven't yet
if m.writer == nil {

View File

@@ -12,6 +12,7 @@ import (
"time"
"github.com/jhillyerd/inbucket/config"
"github.com/jhillyerd/inbucket/datastore"
"github.com/jhillyerd/inbucket/log"
)
@@ -55,7 +56,7 @@ type FileDataStore struct {
}
// NewFileDataStore creates a new DataStore object using the specified path
func NewFileDataStore(cfg config.DataStoreConfig) DataStore {
func NewFileDataStore(cfg config.DataStoreConfig) datastore.DataStore {
path := cfg.Path
if path == "" {
log.Errorf("No value configured for datastore path")
@@ -73,14 +74,14 @@ func NewFileDataStore(cfg config.DataStoreConfig) DataStore {
// DefaultFileDataStore creates a new DataStore object. It uses the inbucket.Config object to
// construct it's path.
func DefaultFileDataStore() DataStore {
func DefaultFileDataStore() datastore.DataStore {
cfg := config.GetDataStoreConfig()
return NewFileDataStore(cfg)
}
// MailboxFor retrieves the Mailbox object for a specified email address, if the mailbox
// does not exist, it will attempt to create it.
func (ds *FileDataStore) MailboxFor(emailAddress string) (Mailbox, error) {
func (ds *FileDataStore) MailboxFor(emailAddress string) (datastore.Mailbox, error) {
name, err := ParseMailboxName(emailAddress)
if err != nil {
return nil, err
@@ -96,8 +97,8 @@ func (ds *FileDataStore) MailboxFor(emailAddress string) (Mailbox, error) {
}
// AllMailboxes returns a slice with all Mailboxes
func (ds *FileDataStore) AllMailboxes() ([]Mailbox, error) {
mailboxes := make([]Mailbox, 0, 100)
func (ds *FileDataStore) AllMailboxes() ([]datastore.Mailbox, error) {
mailboxes := make([]datastore.Mailbox, 0, 100)
infos1, err := ioutil.ReadDir(ds.mailPath)
if err != nil {
return nil, err
@@ -159,14 +160,14 @@ func (mb *FileMailbox) String() string {
// GetMessages scans the mailbox directory for .gob files and decodes them into
// a slice of Message objects.
func (mb *FileMailbox) GetMessages() ([]Message, error) {
func (mb *FileMailbox) GetMessages() ([]datastore.Message, error) {
if !mb.indexLoaded {
if err := mb.readIndex(); err != nil {
return nil, err
}
}
messages := make([]Message, len(mb.messages))
messages := make([]datastore.Message, len(mb.messages))
for i, m := range mb.messages {
messages[i] = m
}
@@ -174,7 +175,7 @@ func (mb *FileMailbox) GetMessages() ([]Message, error) {
}
// GetMessage decodes a single message by Id and returns a Message object
func (mb *FileMailbox) GetMessage(id string) (Message, error) {
func (mb *FileMailbox) GetMessage(id string) (datastore.Message, error) {
if !mb.indexLoaded {
if err := mb.readIndex(); err != nil {
return nil, err
@@ -191,7 +192,7 @@ func (mb *FileMailbox) GetMessage(id string) (Message, error) {
}
}
return nil, ErrNotExist
return nil, datastore.ErrNotExist
}
// Purge deletes all messages in this mailbox

View File

@@ -12,6 +12,7 @@ import (
"strings"
"time"
"github.com/jhillyerd/inbucket/datastore"
"github.com/jhillyerd/inbucket/log"
"github.com/jhillyerd/inbucket/msghub"
)
@@ -71,7 +72,7 @@ var commands = map[string]bool{
// recipientDetails for message delivery
type recipientDetails struct {
address, localPart, domainPart string
mailbox Mailbox
mailbox datastore.Mailbox
}
// Session holds the state of an SMTP session

View File

@@ -14,6 +14,7 @@ import (
"time"
"github.com/jhillyerd/inbucket/config"
"github.com/jhillyerd/inbucket/datastore"
"github.com/jhillyerd/inbucket/msghub"
)
@@ -376,7 +377,7 @@ func (m *mockConn) SetDeadline(t time.Time) error { return nil }
func (m *mockConn) SetReadDeadline(t time.Time) error { return nil }
func (m *mockConn) SetWriteDeadline(t time.Time) error { return nil }
func setupSMTPServer(ds DataStore) (s *Server, buf *bytes.Buffer, teardown func()) {
func setupSMTPServer(ds datastore.DataStore) (s *Server, buf *bytes.Buffer, teardown func()) {
// Test Server Config
cfg := config.SMTPConfig{
IP4address: net.IPv4(127, 0, 0, 1),

View File

@@ -11,6 +11,7 @@ import (
"time"
"github.com/jhillyerd/inbucket/config"
"github.com/jhillyerd/inbucket/datastore"
"github.com/jhillyerd/inbucket/log"
"github.com/jhillyerd/inbucket/msghub"
)
@@ -27,10 +28,10 @@ type Server struct {
storeMessages bool
// Dependencies
dataStore DataStore // Mailbox/message store
globalShutdown chan bool // Shuts down Inbucket
msgHub *msghub.Hub // Pub/sub for message info
retentionScanner *RetentionScanner // Deletes expired messages
dataStore datastore.DataStore // Mailbox/message store
globalShutdown chan bool // Shuts down Inbucket
msgHub *msghub.Hub // Pub/sub for message info
retentionScanner *RetentionScanner // Deletes expired messages
// State
listener net.Listener // Incoming network connections
@@ -62,7 +63,7 @@ var (
func NewServer(
cfg config.SMTPConfig,
globalShutdown chan bool,
ds DataStore,
ds datastore.DataStore,
msgHub *msghub.Hub) *Server {
return &Server{
host: fmt.Sprintf("%v:%v", cfg.IP4address, cfg.IP4port),

View File

@@ -7,6 +7,7 @@ import (
"time"
"github.com/jhillyerd/inbucket/config"
"github.com/jhillyerd/inbucket/datastore"
"github.com/jhillyerd/inbucket/log"
)
@@ -42,14 +43,14 @@ func init() {
type RetentionScanner struct {
globalShutdown chan bool // Closes when Inbucket needs to shut down
retentionShutdown chan bool // Closed after the scanner has shut down
ds DataStore
ds datastore.DataStore
retentionPeriod time.Duration
retentionSleep time.Duration
}
// NewRetentionScanner launches a go-routine that scans for expired
// messages, following the configured interval
func NewRetentionScanner(ds DataStore, shutdownChannel chan bool) *RetentionScanner {
func NewRetentionScanner(ds datastore.DataStore, shutdownChannel chan bool) *RetentionScanner {
cfg := config.GetDataStoreConfig()
rs := &RetentionScanner{
globalShutdown: shutdownChannel,

View File

@@ -8,6 +8,7 @@ import (
"time"
"github.com/jhillyerd/enmime"
"github.com/jhillyerd/inbucket/datastore"
"github.com/stretchr/testify/mock"
)
@@ -28,12 +29,12 @@ func TestDoRetentionScan(t *testing.T) {
old3 := mockMessage(24)
// First it should ask for all mailboxes
mds.On("AllMailboxes").Return([]Mailbox{mb1, mb2, mb3}, nil)
mds.On("AllMailboxes").Return([]datastore.Mailbox{mb1, mb2, mb3}, nil)
// Then for all messages on each box
mb1.On("GetMessages").Return([]Message{new1, old1, old2}, nil)
mb2.On("GetMessages").Return([]Message{old3, new2}, nil)
mb3.On("GetMessages").Return([]Message{new3}, nil)
mb1.On("GetMessages").Return([]datastore.Message{new1, old1, old2}, nil)
mb2.On("GetMessages").Return([]datastore.Message{old3, new2}, nil)
mb3.On("GetMessages").Return([]datastore.Message{new3}, nil)
// Test 4 hour retention
rs := &RetentionScanner{
@@ -76,14 +77,14 @@ type MockDataStore struct {
mock.Mock
}
func (m *MockDataStore) MailboxFor(name string) (Mailbox, error) {
func (m *MockDataStore) MailboxFor(name string) (datastore.Mailbox, error) {
args := m.Called()
return args.Get(0).(Mailbox), args.Error(1)
return args.Get(0).(datastore.Mailbox), args.Error(1)
}
func (m *MockDataStore) AllMailboxes() ([]Mailbox, error) {
func (m *MockDataStore) AllMailboxes() ([]datastore.Mailbox, error) {
args := m.Called()
return args.Get(0).([]Mailbox), args.Error(1)
return args.Get(0).([]datastore.Mailbox), args.Error(1)
}
// Mock Mailbox object
@@ -91,14 +92,14 @@ type MockMailbox struct {
mock.Mock
}
func (m *MockMailbox) GetMessages() ([]Message, error) {
func (m *MockMailbox) GetMessages() ([]datastore.Message, error) {
args := m.Called()
return args.Get(0).([]Message), args.Error(1)
return args.Get(0).([]datastore.Message), args.Error(1)
}
func (m *MockMailbox) GetMessage(id string) (Message, error) {
func (m *MockMailbox) GetMessage(id string) (datastore.Message, error) {
args := m.Called(id)
return args.Get(0).(Message), args.Error(1)
return args.Get(0).(datastore.Message), args.Error(1)
}
func (m *MockMailbox) Purge() error {
@@ -106,9 +107,9 @@ func (m *MockMailbox) Purge() error {
return args.Error(0)
}
func (m *MockMailbox) NewMessage() (Message, error) {
func (m *MockMailbox) NewMessage() (datastore.Message, error) {
args := m.Called()
return args.Get(0).(Message), args.Error(1)
return args.Get(0).(datastore.Message), args.Error(1)
}
func (m *MockMailbox) Name() string {