diff --git a/sam/repository/attachment.go b/sam/repository/attachment.go index e9b080baf..816da1a60 100644 --- a/sam/repository/attachment.go +++ b/sam/repository/attachment.go @@ -11,8 +11,8 @@ import ( ) type ( - Attachment interface { - With(ctx context.Context) Attachment + AttachmentRepository interface { + With(ctx context.Context, db *factory.DB) AttachmentRepository FindAttachmentByID(id uint64) (*types.Attachment, error) FindAttachmentByMessageID(IDs ...uint64) (types.MessageAttachmentSet, error) @@ -32,13 +32,13 @@ const ( ErrAttachmentNotFound = repositoryError("AttachmentNotFound") ) -func NewAttachment(ctx context.Context) Attachment { - return (&attachment{}).With(ctx) +func Attachment(ctx context.Context, db *factory.DB) AttachmentRepository { + return (&attachment{}).With(ctx, db) } -func (r *attachment) With(ctx context.Context) Attachment { +func (r *attachment) With(ctx context.Context, db *factory.DB) AttachmentRepository { return &attachment{ - repository: r.repository.With(ctx), + repository: r.repository.With(ctx, db), } } diff --git a/sam/repository/channel.go b/sam/repository/channel.go index 7dc58ab88..c5f39f629 100644 --- a/sam/repository/channel.go +++ b/sam/repository/channel.go @@ -10,8 +10,8 @@ import ( ) type ( - Channel interface { - With(ctx context.Context) Channel + ChannelRepository interface { + With(ctx context.Context, db *factory.DB) ChannelRepository FindChannelByID(id uint64) (*types.Channel, error) FindDirectChannelByUserID(fromUserID, toUserID uint64) (*types.Channel, error) @@ -58,13 +58,13 @@ const ( ErrChannelNotFound = repositoryError("ChannelNotFound") ) -func NewChannel(ctx context.Context) Channel { - return (&channel{}).With(ctx) +func Channel(ctx context.Context, db *factory.DB) ChannelRepository { + return (&channel{}).With(ctx, db) } -func (r *channel) With(ctx context.Context) Channel { +func (r *channel) With(ctx context.Context, db *factory.DB) ChannelRepository { return &channel{ - repository: r.repository.With(ctx), + repository: r.repository.With(ctx, db), } } diff --git a/sam/repository/events.go b/sam/repository/events.go index ecac31d07..906f5821c 100644 --- a/sam/repository/events.go +++ b/sam/repository/events.go @@ -33,7 +33,7 @@ The reading of the event queue table is triggered by pubsub. type ( Events interface { - With(ctx context.Context) Events + With(ctx context.Context, db *factory.DB) Events Pull(origin uint64) ([]*types.EventQueueItem, error) Push(eqi *types.EventQueueItem) error @@ -45,13 +45,13 @@ type ( } ) -func NewEvents(ctx context.Context) Events { - return (&events{}).With(ctx) +func NewEvents(ctx context.Context, db *factory.DB) Events { + return (&events{}).With(ctx, db) } -func (r *events) With(ctx context.Context) Events { +func (r *events) With(ctx context.Context, db *factory.DB) Events { return &events{ - repository: r.repository.With(ctx), + repository: r.repository.With(ctx, db), } } diff --git a/sam/repository/message.go b/sam/repository/message.go index 35256af76..e17920bc9 100644 --- a/sam/repository/message.go +++ b/sam/repository/message.go @@ -10,8 +10,8 @@ import ( ) type ( - Message interface { - With(ctx context.Context) Message + MessageRepository interface { + With(ctx context.Context, db *factory.DB) MessageRepository FindMessageByID(id uint64) (*types.Message, error) FindMessages(filter *types.MessageFilter) (types.MessageSet, error) @@ -43,13 +43,13 @@ const ( ErrMessageNotFound = repositoryError("MessageNotFound") ) -func NewMessage(ctx context.Context) Message { - return (&message{}).With(ctx) +func Message(ctx context.Context, db *factory.DB) MessageRepository { + return (&message{}).With(ctx, db) } -func (r *message) With(ctx context.Context) Message { +func (r *message) With(ctx context.Context, db *factory.DB) MessageRepository { return &message{ - repository: r.repository.With(ctx), + repository: r.repository.With(ctx, db), } } diff --git a/sam/repository/organisation.go b/sam/repository/organisation.go index 0c1313d1b..d7f2a0af8 100644 --- a/sam/repository/organisation.go +++ b/sam/repository/organisation.go @@ -10,8 +10,8 @@ import ( ) type ( - Organisation interface { - With(ctx context.Context) Organisation + OrganisationRepository interface { + With(ctx context.Context, db *factory.DB) OrganisationRepository FindOrganisationByID(id uint64) (*types.Organisation, error) FindOrganisations(filter *types.OrganisationFilter) ([]*types.Organisation, error) @@ -33,13 +33,13 @@ const ( ErrOrganisationNotFound = repositoryError("OrganisationNotFound") ) -func NewOrganisation(ctx context.Context) Organisation { - return (&organisation{}).With(ctx) +func Organisation(ctx context.Context, db *factory.DB) OrganisationRepository { + return (&organisation{}).With(ctx, db) } -func (r *organisation) With(ctx context.Context) Organisation { +func (r *organisation) With(ctx context.Context, db *factory.DB) OrganisationRepository { return &organisation{ - repository: r.repository.With(ctx), + repository: r.repository.With(ctx, db), } } diff --git a/sam/repository/reaction.go b/sam/repository/reaction.go index 89b67c3e4..be2933957 100644 --- a/sam/repository/reaction.go +++ b/sam/repository/reaction.go @@ -10,8 +10,8 @@ import ( ) type ( - Reaction interface { - With(ctx context.Context) Reaction + ReactionRepository interface { + With(ctx context.Context, db *factory.DB) ReactionRepository FindReactionByID(id uint64) (*types.Reaction, error) FindReactionsByRange(channelID, fromReactionID, toReactionID uint64) ([]*types.Reaction, error) @@ -28,13 +28,13 @@ const ( ErrReactionNotFound = repositoryError("ReactionNotFound") ) -func NewReaction(ctx context.Context) Reaction { - return (&reaction{}).With(ctx) +func Reaction(ctx context.Context, db *factory.DB) ReactionRepository { + return (&reaction{}).With(ctx, db) } -func (r *reaction) With(ctx context.Context) Reaction { +func (r *reaction) With(ctx context.Context, db *factory.DB) ReactionRepository { return &reaction{ - repository: r.repository.With(ctx), + repository: r.repository.With(ctx, db), } } diff --git a/sam/repository/repository.go b/sam/repository/repository.go index 82b20e4ef..236880d57 100644 --- a/sam/repository/repository.go +++ b/sam/repository/repository.go @@ -11,44 +11,24 @@ import ( type ( repository struct { ctx context.Context - - // Get database handle - dbh func(ctxs ...context.Context) *factory.DB + dbh *factory.DB } ) -var ( - _db *factory.DB - _ctx context.Context -) - -// DB returns a repository-wide singleton DB handle -func DB(ctxs ...context.Context) *factory.DB { - if _db == nil { - _db = factory.Database.MustGet() - } - for _, ctx := range ctxs { - _db = _db.With(ctx) - _ctx = ctx - break - } - return _db +// DB produces a contextual DB handle +func DB(ctx context.Context) *factory.DB { + return factory.Database.MustGet().With(ctx) } func Identity(ctx context.Context) uint64 { return auth.GetIdentityFromContext(ctx).Identity() } -// With updates repository and database contexts -func (r *repository) With(ctx context.Context) *repository { - res := &repository{ +func (r *repository) With(ctx context.Context, db *factory.DB) *repository { + return &repository{ ctx: ctx, - dbh: DB, + dbh: db, } - if r != nil { - res.dbh = r.dbh - } - return res } // Context returns current active repository context @@ -58,5 +38,8 @@ func (r *repository) Context() context.Context { // db returns context-aware db handle func (r *repository) db() *factory.DB { - return r.dbh(r.ctx) + if r.dbh != nil { + return r.dbh + } + return DB(r.ctx) } diff --git a/sam/repository/team.go b/sam/repository/team.go index e933128c8..d36c45f63 100644 --- a/sam/repository/team.go +++ b/sam/repository/team.go @@ -10,8 +10,8 @@ import ( ) type ( - Team interface { - With(ctx context.Context) Team + TeamRepository interface { + With(ctx context.Context, db *factory.DB) TeamRepository FindTeamByID(id uint64) (*types.Team, error) FindTeams(filter *types.TeamFilter) ([]*types.Team, error) @@ -35,13 +35,13 @@ const ( ErrTeamNotFound = repositoryError("TeamNotFound") ) -func NewTeam(ctx context.Context) Team { - return (&team{}).With(ctx) +func Team(ctx context.Context, db *factory.DB) TeamRepository { + return (&team{}).With(ctx, db) } -func (r *team) With(ctx context.Context) Team { +func (r *team) With(ctx context.Context, db *factory.DB) TeamRepository { return &team{ - repository: r.repository.With(ctx), + repository: r.repository.With(ctx, db), } } diff --git a/sam/rest/attachment.go b/sam/rest/attachment.go index 081f32131..ea833832e 100644 --- a/sam/rest/attachment.go +++ b/sam/rest/attachment.go @@ -35,7 +35,6 @@ func (Attachment) New() *Attachment { func (ctrl *Attachment) Original(ctx context.Context, r *request.AttachmentOriginal) (interface{}, error) { return ctrl.get(r.AttachmentID, false, r.Download) - } func (ctrl *Attachment) Preview(ctx context.Context, r *request.AttachmentPreview) (interface{}, error) { diff --git a/sam/service/attachment.go b/sam/service/attachment.go index 994976eed..098e5028f 100644 --- a/sam/service/attachment.go +++ b/sam/service/attachment.go @@ -20,16 +20,12 @@ import ( type ( attachment struct { + db *factory.DB ctx context.Context - attachment repository.Attachment - message repository.Message + attachment repository.AttachmentRepository + message repository.MessageRepository store store.Store - - config struct { - url string - previewUrl string - } } AttachmentService interface { @@ -43,25 +39,26 @@ type ( } ) +const ( + attachmentURL = "/attachment/%d/%s" + attachmentPreviewURL = "/attachment/%d/%s/preview" +) + func Attachment(store store.Store) *attachment { - svc := &attachment{ - ctx: context.Background(), - attachment: repository.NewAttachment(context.Background()), - message: repository.NewMessage(context.Background()), - store: store, - } - svc.config.url = "/attachment/%d/%s" - svc.config.previewUrl = "/attachment/%d/%s/preview" + svc := (&attachment{ + store: store, + }).With(context.Background()).(*attachment) return svc } func (svc *attachment) With(ctx context.Context) AttachmentService { + db := repository.DB(ctx) return &attachment{ + db: db, ctx: ctx, - attachment: svc.attachment.With(ctx), - message: svc.message.With(ctx), + attachment: repository.Attachment(ctx, db), + message: repository.Message(ctx, db), store: svc.store, - config: svc.config, } } @@ -70,11 +67,11 @@ func (svc *attachment) FindByID(id uint64) (*types.Attachment, error) { } func (svc *attachment) OpenOriginal(att *types.Attachment) (io.ReadSeeker, error) { - return svc.store.Open(att.Url) + return svc.store.Open(attachmentURL) } func (svc *attachment) OpenPreview(att *types.Attachment) (io.ReadSeeker, error) { - return svc.store.Open(att.PreviewUrl) + return svc.store.Open(attachmentPreviewURL) } @@ -141,7 +138,7 @@ func (svc *attachment) Create(channelId uint64, name string, size int64, fh io.R log.Printf("File %s stored as %s", att.Name, att.Url) - return att, repository.DB().Transaction(func() (err error) { + return att, svc.db.Transaction(func() (err error) { if att, err = svc.attachment.CreateAttachment(att); err != nil { return @@ -176,12 +173,12 @@ func (svc *attachment) Create(channelId uint64, name string, size int64, fh io.R // Generates URL to a location func (svc *attachment) url(att *types.Attachment) string { - return fmt.Sprintf(svc.config.url, att.ID, url.PathEscape(att.Name)) + return fmt.Sprintf(attachmentURL, att.ID, url.PathEscape(att.Name)) } // Generates URL to a location func (svc *attachment) previewUrl(att *types.Attachment) string { - return fmt.Sprintf(svc.config.previewUrl, att.ID, url.PathEscape(att.Name)) + return fmt.Sprintf(attachmentPreviewURL, att.ID, url.PathEscape(att.Name)) } func (svc *attachment) extractMeta(att *types.Attachment, file io.ReadSeeker) (err error) { diff --git a/sam/service/channel.go b/sam/service/channel.go index 2715401d7..17a926067 100644 --- a/sam/service/channel.go +++ b/sam/service/channel.go @@ -5,6 +5,7 @@ import ( "fmt" "github.com/pkg/errors" + "github.com/titpetric/factory" "github.com/crusttech/crust/sam/repository" "github.com/crusttech/crust/sam/types" @@ -12,10 +13,11 @@ import ( type ( channel struct { + db *factory.DB ctx context.Context - channel repository.Channel - message repository.Message + channel repository.ChannelRepository + message repository.MessageRepository } ChannelService interface { @@ -38,20 +40,18 @@ type ( ) func Channel() *channel { - var svc = &channel{ - ctx: context.Background(), - channel: repository.NewChannel(context.Background()), - message: repository.NewMessage(context.Background()), - } + svc := (&channel{}).With(context.Background()).(*channel) //svc.sec.ch = ChannelSecurity(svc.channel) return svc } func (svc *channel) With(ctx context.Context) ChannelService { + db := repository.DB(ctx) return &channel{ + db: db, ctx: ctx, - channel: svc.channel.With(ctx), - message: svc.message.With(ctx), + channel: repository.Channel(ctx, db), + message: repository.Message(ctx, db), } } @@ -84,7 +84,7 @@ func (svc *channel) preloadMembers(set types.ChannelSet) error { // Returns all channels with membership info func (svc *channel) FindByMembership() (rval []*types.Channel, err error) { - return rval, repository.DB().Transaction(func() error { + return rval, svc.db.Transaction(func() error { var chMemberId = repository.Identity(svc.ctx) var mm []*types.ChannelMember @@ -111,7 +111,7 @@ func (svc *channel) FindByMembership() (rval []*types.Channel, err error) { func (svc *channel) Create(in *types.Channel) (out *types.Channel, err error) { // @todo: [SECURITY] permission check if user can add channel - return out, repository.DB().Transaction(func() (err error) { + return out, svc.db.Transaction(func() (err error) { var msg *types.Message // @todo get organisation from somewhere @@ -188,7 +188,7 @@ func (svc *channel) Create(in *types.Channel) (out *types.Channel, err error) { } func (svc *channel) Update(in *types.Channel) (out *types.Channel, err error) { - return out, repository.DB().Transaction(func() (err error) { + return out, svc.db.Transaction(func() (err error) { var msgs types.MessageSet // @todo [SECURITY] can user access this channel? @@ -271,7 +271,7 @@ func (svc *channel) Update(in *types.Channel) (out *types.Channel, err error) { } func (svc *channel) Delete(id uint64) error { - return repository.DB().Transaction(func() (err error) { + return svc.db.Transaction(func() (err error) { var userID = repository.Identity(svc.ctx) var ch *types.Channel @@ -293,7 +293,7 @@ func (svc *channel) Delete(id uint64) error { } func (svc *channel) Recover(id uint64) error { - return repository.DB().Transaction(func() (err error) { + return svc.db.Transaction(func() (err error) { var userID = repository.Identity(svc.ctx) var ch *types.Channel @@ -315,7 +315,7 @@ func (svc *channel) Recover(id uint64) error { } func (svc *channel) Archive(id uint64) error { - return repository.DB().Transaction(func() (err error) { + return svc.db.Transaction(func() (err error) { var userID = repository.Identity(svc.ctx) var ch *types.Channel @@ -337,7 +337,7 @@ func (svc *channel) Archive(id uint64) error { } func (svc *channel) Unarchive(id uint64) error { - return repository.DB().Transaction(func() (err error) { + return svc.db.Transaction(func() (err error) { var userID = repository.Identity(svc.ctx) var ch *types.Channel diff --git a/sam/service/message.go b/sam/service/message.go index 905863685..ad69deae5 100644 --- a/sam/service/message.go +++ b/sam/service/message.go @@ -4,6 +4,7 @@ import ( "context" "github.com/pkg/errors" + "github.com/titpetric/factory" "github.com/crusttech/crust/sam/repository" "github.com/crusttech/crust/sam/types" @@ -11,11 +12,12 @@ import ( type ( message struct { + db *factory.DB ctx context.Context - channel repository.Channel - message repository.Message - reaction repository.Reaction + channel repository.ChannelRepository + message repository.MessageRepository + reaction repository.ReactionRepository att AttachmentService } @@ -44,23 +46,21 @@ type ( ) func Message(attSvc AttachmentService) *message { - m := &message{ - ctx: context.Background(), - att: attSvc, - channel: repository.NewChannel(context.Background()), - message: repository.NewMessage(context.Background()), - reaction: repository.NewReaction(context.Background()), - } + m := (&message{ + att: attSvc, + }).With(context.Background()).(*message) return m } func (svc *message) With(ctx context.Context) MessageService { + db := repository.DB(ctx) return &message{ + db: db, ctx: ctx, att: svc.att, - channel: svc.channel.With(ctx), - message: svc.message.With(ctx), - reaction: svc.reaction.With(ctx), + channel: repository.Channel(ctx, db), + message: repository.Message(ctx, db), + reaction: repository.Reaction(ctx, db), } } @@ -81,7 +81,7 @@ func (svc *message) Find(filter *types.MessageFilter) (mm types.MessageSet, err } func (svc *message) Direct(recipientID uint64, in *types.Message) (out *types.Message, err error) { - return out, repository.DB().Transaction(func() (err error) { + return out, svc.db.Transaction(func() (err error) { var currentUserID = repository.Identity(svc.ctx) // @todo [SECURITY] verify if current user can send direct messages to anyone? diff --git a/sam/service/organisation.go b/sam/service/organisation.go index 104f8735a..a0686ae39 100644 --- a/sam/service/organisation.go +++ b/sam/service/organisation.go @@ -3,14 +3,18 @@ package service import ( "context" + "github.com/titpetric/factory" + "github.com/crusttech/crust/sam/repository" "github.com/crusttech/crust/sam/types" ) type ( organisation struct { + db *factory.DB ctx context.Context - rpo repository.Organisation + + rpo repository.OrganisationRepository } OrganisationService interface { @@ -28,16 +32,15 @@ type ( ) func Organisation() *organisation { - return &organisation{ - ctx: context.Background(), - rpo: repository.NewOrganisation(context.Background()), - } + return (&organisation{}).With(context.Background()).(*organisation) } func (svc *organisation) With(ctx context.Context) OrganisationService { + db := repository.DB(ctx) return &organisation{ + db: db, ctx: ctx, - rpo: svc.rpo.With(ctx), + rpo: repository.Organisation(ctx, db), } } diff --git a/sam/service/team.go b/sam/service/team.go index e853daaca..76f71dae4 100644 --- a/sam/service/team.go +++ b/sam/service/team.go @@ -2,14 +2,19 @@ package service import ( "context" + + "github.com/titpetric/factory" + "github.com/crusttech/crust/sam/repository" "github.com/crusttech/crust/sam/types" ) type ( team struct { - ctx context.Context - team repository.Team + db *factory.DB + ctx context.Context + + team repository.TeamRepository } TeamService interface { @@ -29,16 +34,15 @@ type ( ) func Team() *team { - return &team{ - ctx: context.Background(), - team: repository.NewTeam(context.Background()), - } + return (&team{}).With(context.Background()).(*team) } func (svc *team) With(ctx context.Context) TeamService { + db := repository.DB(ctx) return &team{ + db: db, ctx: ctx, - team: svc.team.With(ctx), + team: repository.Team(ctx, db), } } diff --git a/sam/websocket/router.go b/sam/websocket/router.go index f44f3dbb7..f4cdba344 100644 --- a/sam/websocket/router.go +++ b/sam/websocket/router.go @@ -11,7 +11,7 @@ import ( func MountRoutes(ctx context.Context, config *repository.Flags) func(chi.Router) { return func(r chi.Router) { - events := repository.NewEvents(ctx) + events := repository.NewEvents(ctx, nil) go func() { for {