diff --git a/sam/repository/channel.go b/sam/repository/channel.go index ee5c7a7ad..d98415f76 100644 --- a/sam/repository/channel.go +++ b/sam/repository/channel.go @@ -1,9 +1,10 @@ package repository import ( + "time" + "github.com/crusttech/crust/sam/types" "github.com/titpetric/factory" - "time" ) type ( @@ -34,7 +35,7 @@ const ( sqlChannelDirect = `SELECT * FROM channels AS c - WHERE c.type = 'group' + WHERE c.type = ? AND c.id IN (SELECT rel_channel FROM channel_members GROUP BY rel_channel @@ -71,10 +72,12 @@ func (r *repository) FindDirectChannelByUserID(fromUserID, toUserID uint64) (*ty toUserID, fromUserID = fromUserID, toUserID } - return mod, isFound(r.db().Get(mod, sqlChannelDirect, fromUserID, toUserID), mod.ID > 0, ErrChannelNotFound) + return mod, isFound(r.db().Get(mod, sqlChannelDirect, types.ChannelTypeDirect, fromUserID, toUserID), mod.ID > 0, ErrChannelNotFound) } func (r *repository) FindChannels(filter *types.ChannelFilter) ([]*types.Channel, error) { + // @todo: actual searching (filter.Query) not just a full select + params := make([]interface{}, 0) rval := make([]*types.Channel, 0) diff --git a/sam/rest/channel.go b/sam/rest/channel.go index 542abf643..8181d6f9a 100644 --- a/sam/rest/channel.go +++ b/sam/rest/channel.go @@ -44,6 +44,7 @@ func (ctrl *Channel) Create(ctx context.Context, r *request.ChannelCreate) (inte func (ctrl *Channel) Edit(ctx context.Context, r *request.ChannelEdit) (interface{}, error) { channel := &types.Channel{ + ID: r.ChannelID, Name: r.Name, Topic: r.Topic, } diff --git a/sam/service/channel.go b/sam/service/channel.go index ff8337cec..cedfceed6 100644 --- a/sam/service/channel.go +++ b/sam/service/channel.go @@ -12,10 +12,7 @@ import ( type ( channel struct { rpo channelRepository - // - //sec struct { - // ch channelSecurity - //} + usr UserService } ChannelService interface { @@ -43,6 +40,7 @@ func Channel() *channel { var svc = &channel{} svc.rpo = repository.New() + svc.usr = User() //svc.sec.ch = ChannelSecurity(svc.rpo) return svc @@ -63,8 +61,11 @@ func (svc channel) FindByID(ctx context.Context, id uint64) (ch *types.Channel, func (svc channel) Find(ctx context.Context, filter *types.ChannelFilter) ([]*types.Channel, error) { // @todo: permission check to return only channels that channel has access to - // @todo: actual searching not just a full select - return svc.rpo.FindChannels(filter) + if cc, err := svc.rpo.FindChannels(filter); err != nil { + return nil, err + } else { + return cc, svc.usr.LoadFromChannels(ctx, cc) + } } // Returns all channels with membership info diff --git a/sam/service/message.go b/sam/service/message.go index a3afdb5cb..8816ed819 100644 --- a/sam/service/message.go +++ b/sam/service/message.go @@ -5,6 +5,7 @@ import ( "github.com/crusttech/crust/internal/auth" "github.com/crusttech/crust/sam/repository" "github.com/crusttech/crust/sam/types" + "github.com/davecgh/go-spew/spew" "github.com/pkg/errors" ) @@ -84,7 +85,7 @@ func (svc message) Direct(ctx context.Context, recipientID uint64, in *types.Mes dch, err := r.FindDirectChannelByUserID(currentUserID, recipientID) if err == repository.ErrChannelNotFound { dch, err = r.CreateChannel(&types.Channel{ - Type: types.ChannelTypeGroup, + Type: types.ChannelTypeDirect, }) if err != nil { @@ -94,10 +95,12 @@ func (svc message) Direct(ctx context.Context, recipientID uint64, in *types.Mes membership := &types.ChannelMember{ChannelID: dch.ID, Type: types.ChannelMembershipTypeOwner} membership.UserID = currentUserID + spew.Dump(membership) if _, err = r.AddChannelMember(membership); err != nil { return } + spew.Dump(membership) membership.UserID = recipientID if _, err = r.AddChannelMember(membership); err != nil { return @@ -110,6 +113,9 @@ func (svc message) Direct(ctx context.Context, recipientID uint64, in *types.Mes // Make sure our message is sent to the right channel in.ChannelID = dch.ID in.UserID = currentUserID + in.Type = types.MessageTypeSimpleMessage + + spew.Dump(in) // @todo send new msg to the event-loop out, err = r.CreateMessage(in) diff --git a/sam/service/user.go b/sam/service/user.go index 1b34a9127..b997c7407 100644 --- a/sam/service/user.go +++ b/sam/service/user.go @@ -18,6 +18,7 @@ type ( UserService interface { Find(ctx context.Context, filter *types.UserFilter) ([]*types.User, error) + LoadFromChannels(ctx context.Context, cc types.ChannelSet) (err error) } userRepository interface { @@ -52,6 +53,14 @@ func (svc user) FindByID(ctx context.Context, id uint64) (*types.User, error) { return svc.rpo.WithCtx(ctx).FindUserByID(id) } +func (svc user) LoadFromChannels(ctx context.Context, cc types.ChannelSet) (err error) { + return cc.Walk(func(c *types.Channel) error { + // @todo doing N selects (one per chan) for now, optimize! + c.Members, err = svc.rpo.FindUsers(&types.UserFilter{MembersOfChannel: c.ID}) + return err + }) +} + func (svc user) Find(ctx context.Context, filter *types.UserFilter) ([]*types.User, error) { return svc.rpo.FindUsers(filter) } diff --git a/sam/types/channel.go b/sam/types/channel.go index 446303dab..9a543b00b 100644 --- a/sam/types/channel.go +++ b/sam/types/channel.go @@ -23,7 +23,8 @@ type ( LastMessageID uint64 `json:",omitempty" db:"rel_last_message"` - Member *ChannelMember `json:"-" db:"-"` + Member *ChannelMember `json:"-" db:"-"` + Members []*User `json:"-" db:"-"` } ChannelMember struct { @@ -37,13 +38,26 @@ type ( } ChannelFilter struct { - Query string + Query string + IncludeMembers bool } ChannelMembershipType string ChannelType string + + ChannelSet []*Channel ) +func (cc ChannelSet) Walk(w func(*Channel) error) (err error) { + for i := range cc { + if err = w(cc[i]); err != nil { + return + } + } + + return +} + const ( ChannelMembershipTypeOwner ChannelMembershipType = "owner" ChannelMembershipTypeMember = "member" @@ -51,4 +65,5 @@ const ( ChannelTypePublic ChannelType = "public" ChannelTypePrivate = "private" ChannelTypeGroup = "group" + ChannelTypeDirect = "direct" ) diff --git a/sam/types/user.go b/sam/types/user.go index 872b11ba7..a33ea81af 100644 --- a/sam/types/user.go +++ b/sam/types/user.go @@ -23,8 +23,20 @@ type ( Query string MembersOfChannel uint64 } + + UserSet []*User ) +func (uu UserSet) Walk(w func(*User) error) (err error) { + for i := range uu { + if err = w(uu[i]); err != nil { + return + } + } + + return +} + func (u *User) Valid() bool { return u.ID > 0 && u.SuspendedAt == nil && u.DeletedAt == nil } diff --git a/sam/websocket/incoming/channel.go b/sam/websocket/incoming/channel.go index 449367c82..445af00f9 100644 --- a/sam/websocket/incoming/channel.go +++ b/sam/websocket/incoming/channel.go @@ -1,7 +1,7 @@ package incoming type ( - ChannelList struct{} + Channels struct{} ChannelJoin struct { ChannelID string `json:"id"` @@ -12,18 +12,16 @@ type ( } ChannelCreate struct { - Name string `json:"name"` - Topic string `json:"topic"` + Name *string `json:"name"` + Topic *string `json:"topic"` + Type *string `json:"type"` } - ChannelRename struct { - ChannelID string `json:"id"` - Name string `json:"name"` - } - - ChannelChangeTopic struct { - ChannelID string `json:"id"` - Topic string `json:"topic"` + ChannelUpdate struct { + ID string `json:"id"` + Name *string `json:"name"` + Topic *string `json:"topic"` + Type *string `json:"type"` } ChannelDelete struct { diff --git a/sam/websocket/incoming/messages.go b/sam/websocket/incoming/messages.go index 2f66f3c7d..da88e8cd5 100644 --- a/sam/websocket/incoming/messages.go +++ b/sam/websocket/incoming/messages.go @@ -2,23 +2,23 @@ package incoming type ( MessageCreate struct { - ChannelID string `json:"cid"` - Message string `json:"msg"` + ChannelID string `json:"channelId"` + Message string `json:"message"` } MessageUpdate struct { ID string `json:"id"` - Message string `json:"msg"` + Message string `json:"message"` } MessageDelete struct { - ChannelID string `json:"cid"` + ChannelID string `json:"channelId"` ID string `json:"id"` } - MessageHistory struct { - ChannelID string `json:"cid"` - FromID string `json:"fid,omitempty"` - UntilID string `json:"uid,omitempty"` + Messages struct { + ChannelID string `json:"channelId"` + FromID string `json:"fromId,omitempty"` + UntilID string `json:"untilId,omitempty"` } ) diff --git a/sam/websocket/incoming/payload.go b/sam/websocket/incoming/payload.go index 112d17e5d..eff7d3348 100644 --- a/sam/websocket/incoming/payload.go +++ b/sam/websocket/incoming/payload.go @@ -2,22 +2,21 @@ package incoming type Payload struct { // Channel actions - *ChannelList `json:"chlist"` - *ChannelJoin `json:"chjoin"` - *ChannelPart `json:"chpart"` + *Channels `json:"channels"` + *ChannelJoin `json:"joinChannel"` + *ChannelPart `json:"partChannel"` - *ChannelChangeTopic `json:"chct"` - *ChannelRename `json:"chrn"` - *ChannelCreate `json:"chcr"` - *ChannelDelete `json:"chdel"` + *ChannelCreate `json:"createChannel"` + *ChannelUpdate `json:"updateChannel"` + *ChannelDelete `json:"deleteChannel"` // Get channel message history - *MessageHistory `json:"chopen"` + *Messages `json:"messages"` // Message actions - *MessageCreate `json:"msgcre"` - *MessageUpdate `json:"msgupd"` - *MessageDelete `json:"msgdel"` + *MessageCreate `json:"createMessage"` + *MessageUpdate `json:"updateMessage"` + *MessageDelete `json:"deleteMessage"` - *UserList `json:"users"` + *Users `json:"getUsers"` } diff --git a/sam/websocket/incoming/user.go b/sam/websocket/incoming/user.go index 65b60202f..9f6aa64c7 100644 --- a/sam/websocket/incoming/user.go +++ b/sam/websocket/incoming/user.go @@ -1,5 +1,5 @@ package incoming type ( - UserList struct{} + Users struct{} ) diff --git a/sam/websocket/outgoing/channel.go b/sam/websocket/outgoing/channel.go index c026e5e9a..bf7323087 100644 --- a/sam/websocket/outgoing/channel.go +++ b/sam/websocket/outgoing/channel.go @@ -34,7 +34,9 @@ type ( ID string `json:"id"` Name string `json:"name"` Topic string `json:"topic"` + Type string `json:"type"` LastMessageID string `json:"lastMessageId"` + Members *Users `json:"members,omitempty"` } Channels []*Channel diff --git a/sam/websocket/payload.go b/sam/websocket/payload.go index dcb8e3b83..5d8df5272 100644 --- a/sam/websocket/payload.go +++ b/sam/websocket/payload.go @@ -36,6 +36,8 @@ func payloadFromChannel(ch *types.Channel) *outgoing.Channel { Name: ch.Name, LastMessageID: uint64toa(ch.LastMessageID), Topic: ch.Topic, + Type: string(ch.Type), + Members: payloadFromUsers(ch.Members), } } @@ -70,6 +72,7 @@ func payloadFromUsers(users []*types.User) *outgoing.Users { } retval := outgoing.Users(uu) + return &retval } diff --git a/sam/websocket/session_incoming.go b/sam/websocket/session_incoming.go index 5d733fc01..4236506f2 100644 --- a/sam/websocket/session_incoming.go +++ b/sam/websocket/session_incoming.go @@ -3,6 +3,7 @@ package websocket import ( "encoding/json" "github.com/crusttech/crust/sam/websocket/incoming" + "github.com/davecgh/go-spew/spew" "github.com/pkg/errors" ) @@ -12,6 +13,8 @@ func (s *Session) dispatch(raw []byte) (err error) { return errors.Wrap(err, "Session.incoming: payload malformed") } + spew.Dump(p, string(raw)) + ctx := s.Context() switch { @@ -22,27 +25,25 @@ func (s *Session) dispatch(raw []byte) (err error) { return s.messageUpdate(ctx, p.MessageUpdate) case p.MessageDelete != nil: return s.messageDelete(ctx, p.MessageDelete) - case p.MessageHistory != nil: - return s.messageHistory(ctx, p.MessageHistory) + case p.Messages != nil: + return s.messageHistory(ctx, p.Messages) // channel actions case p.ChannelJoin != nil: return s.channelJoin(ctx, p.ChannelJoin) case p.ChannelPart != nil: return s.channelPart(ctx, p.ChannelPart) - case p.ChannelList != nil: - return s.channelList(ctx, p.ChannelList) + case p.Channels != nil: + return s.channelList(ctx, p.Channels) case p.ChannelCreate != nil: return s.channelCreate(ctx, p.ChannelCreate) case p.ChannelDelete != nil: return s.channelDelete(ctx, p.ChannelDelete) - case p.ChannelRename != nil: - return s.channelRename(ctx, p.ChannelRename) - case p.ChannelChangeTopic != nil: - return s.channelChangeTopic(ctx, p.ChannelChangeTopic) + case p.ChannelUpdate != nil: + return s.channelUpdate(ctx, p.ChannelUpdate) - case p.UserList != nil: - return s.userList(ctx, p.UserList) + case p.Users != nil: + return s.userList(ctx, p.Users) } return nil diff --git a/sam/websocket/session_incoming_channel.go b/sam/websocket/session_incoming_channel.go index 647d44a21..df1927d80 100644 --- a/sam/websocket/session_incoming_channel.go +++ b/sam/websocket/session_incoming_channel.go @@ -43,8 +43,8 @@ func (s *Session) channelPart(ctx context.Context, p *incoming.ChannelPart) erro return nil } -func (s *Session) channelList(ctx context.Context, p *incoming.ChannelList) error { - channels, err := service.Channel().Find(ctx, nil) +func (s *Session) channelList(ctx context.Context, p *incoming.Channels) error { + channels, err := service.Channel().Find(ctx, &types.ChannelFilter{IncludeMembers: true}) if err != nil { return err } @@ -54,9 +54,19 @@ func (s *Session) channelList(ctx context.Context, p *incoming.ChannelList) erro func (s *Session) channelCreate(ctx context.Context, p *incoming.ChannelCreate) (err error) { ch := &types.Channel{ - Type: types.ChannelTypePublic, - Name: p.Name, - Topic: p.Topic, + Type: types.ChannelTypePublic, + } + + if p.Name != nil { + ch.Name = *p.Name + } + + if p.Topic != nil { + ch.Topic = *p.Topic + } + + if p.Type != nil { + ch.Type = types.ChannelType(*p.Type) } ch, err = service.Channel().Create(ctx, ch) @@ -91,44 +101,28 @@ func (s *Session) channelDelete(ctx context.Context, p *incoming.ChannelDelete) }, p.ChannelID) } -func (s *Session) channelRename(ctx context.Context, p *incoming.ChannelRename) error { - ch, err := service.Channel().FindByID(ctx, parseUInt64(p.ChannelID)) +func (s *Session) channelUpdate(ctx context.Context, p *incoming.ChannelUpdate) error { + ch, err := service.Channel().FindByID(ctx, parseUInt64(p.ID)) if err != nil { return err } - if ch.Name == p.Name { - // No changes, ignore - return nil + if p.Name != nil { + ch.Name = *p.Name } - ch.Name = p.Name + if p.Topic != nil { + ch.Topic = *p.Topic + } + + if p.Type != nil { + ch.Type = types.ChannelType(*p.Type) + } ch, err = service.Channel().Update(ctx, ch) if err != nil { return err } - return s.sendToAllSubscribers(payloadFromChannel(ch), p.ChannelID) -} - -func (s *Session) channelChangeTopic(ctx context.Context, p *incoming.ChannelChangeTopic) error { - ch, err := service.Channel().FindByID(ctx, parseUInt64(p.ChannelID)) - if err != nil { - return err - } - - if ch.Topic == p.Topic { - // No changes, ignore - return nil - } - - ch.Topic = p.Topic - - ch, err = service.Channel().Update(ctx, ch) - if err != nil { - return err - } - - return s.sendToAllSubscribers(payloadFromChannel(ch), p.ChannelID) + return s.sendToAllSubscribers(payloadFromChannel(ch), p.ID) } diff --git a/sam/websocket/session_incoming_message.go b/sam/websocket/session_incoming_message.go index 4442b430d..5cd67a3f9 100644 --- a/sam/websocket/session_incoming_message.go +++ b/sam/websocket/session_incoming_message.go @@ -69,7 +69,7 @@ func (s *Session) messageDelete(ctx context.Context, p *incoming.MessageDelete) return s.sendToAllSubscribers(&outgoing.MessageDelete{ID: p.ID}, p.ChannelID) } -func (s *Session) messageHistory(ctx context.Context, p *incoming.MessageHistory) error { +func (s *Session) messageHistory(ctx context.Context, p *incoming.Messages) error { var ( filter = &types.MessageFilter{ ChannelID: parseUInt64(p.ChannelID), diff --git a/sam/websocket/session_incoming_user.go b/sam/websocket/session_incoming_user.go index 4ac48ee4d..9ccfc13b9 100644 --- a/sam/websocket/session_incoming_user.go +++ b/sam/websocket/session_incoming_user.go @@ -6,7 +6,7 @@ import ( "github.com/crusttech/crust/sam/websocket/incoming" ) -func (s *Session) userList(ctx context.Context, p *incoming.UserList) error { +func (s *Session) userList(ctx context.Context, p *incoming.Users) error { users, err := service.User().Find(ctx, nil) if err != nil { return err