diff --git a/app/controller/user_controller.go b/app/controller/user_controller.go index 2ccdf43..9169710 100644 --- a/app/controller/user_controller.go +++ b/app/controller/user_controller.go @@ -1,22 +1,25 @@ package controller import ( + "fmt" "net/http" "github.com/gin-gonic/gin" dto "github.com/kavkaco/Kavka-Core/app/dto" "github.com/kavkaco/Kavka-Core/app/presenters" + "github.com/kavkaco/Kavka-Core/internal/domain/chat" "github.com/kavkaco/Kavka-Core/internal/domain/user" "github.com/kavkaco/Kavka-Core/pkg/session" "github.com/kavkaco/Kavka-Core/utils/bearer" ) type UserController struct { - userService user.UserService + userService user.Service + chatService chat.Service } -func NewUserController(userService user.UserService) *UserController { - return &UserController{userService} +func NewUserController(userService user.Service, chatService chat.Service) *UserController { + return &UserController{userService, chatService} } func (ctrl *UserController) HandleLogin(ctx *gin.Context) { @@ -82,12 +85,23 @@ func (ctrl *UserController) HandleAuthenticate(ctx *gin.Context) { accessToken, bearerOk := bearer.AccessToken(ctx) if bearerOk { + // get the user info userInfo, err := ctrl.userService.Authenticate(accessToken) if err != nil { - presenters.ResponseError(ctx, err) + presenters.AccessDenied(ctx) return } - presenters.ResponseUserInfo(ctx, userInfo) + // gathering user chats + userChats, err := ctrl.chatService.GetUserChats(userInfo.StaticID) + if err != nil { + presenters.ResponseInternalServerError(ctx) + return + } + + fmt.Println(userChats) + + // FIXME - fix the presenters + // presenters.ResponseUserInfo(ctx, userInfo, userChats) } } diff --git a/app/presenters/chat_presenters.go b/app/presenters/chat_presenters.go index 6b2825d..3d9edc3 100644 --- a/app/presenters/chat_presenters.go +++ b/app/presenters/chat_presenters.go @@ -9,10 +9,27 @@ type ChatDto struct { Chat *chat.Chat `json:"chat"` } -func ChatAsJSON(_ string, obj *chat.Chat) interface{} { - if obj.ChatType == chat.TypeDirect { - obj.ChatDetail = nil - } +// FIXME +func ChatAsJSON(obj chat.Chat) (interface{}, error) { + // if obj.ChatType == chat.TypeDirect { + // obj.ChatDetail = nil + // } else { + // // Determine the specific ChatDetail type based on chatType + // var chatDetail interface{} + // var convertErr error - return obj + // switch obj.ChatType { + // case chat.TypeChannel: + // chatDetail, convertErr = utils.TypeConverter[chat.ChannelChatDetail](obj.ChatDetail) + // case chat.TypeGroup: + // chatDetail, convertErr = utils.TypeConverter[chat.GroupChatDetail](obj.ChatDetail) + // } + + // if convertErr != nil { + // return nil, convertErr + // } + // obj.ChatDetail = chatDetail + // } + + return obj, nil } diff --git a/app/presenters/user_presenter.go b/app/presenters/user_presenter.go index fb782f2..c601ed3 100644 --- a/app/presenters/user_presenter.go +++ b/app/presenters/user_presenter.go @@ -5,14 +5,28 @@ import ( "net/http" "github.com/gin-gonic/gin" + "github.com/kavkaco/Kavka-Core/internal/domain/chat" "github.com/kavkaco/Kavka-Core/internal/domain/user" "github.com/kavkaco/Kavka-Core/pkg/session" ) +// the data and information that would be send to user after authentication; +// the messages of chats must not be sent! + type UserInfoDto struct { - Message string `json:"message"` - Code int `json:"code"` - UserInfo *user.User `json:"user"` + Message string `json:"message"` + Code int `json:"code"` + UserInfo *user.User `json:"user"` + UserChats []chat.Chat `json:"chats"` +} + +func AccessDenied(ctx *gin.Context) { + code := http.StatusForbidden + + ctx.JSON(code, SimpleMessageDto{ + Code: code, + Message: "Forbidden", + }) } func SendTokensHeader(ctx *gin.Context, tokens session.LoginTokens) { @@ -20,10 +34,11 @@ func SendTokensHeader(ctx *gin.Context, tokens session.LoginTokens) { ctx.Header("authorization", fmt.Sprintf("Bearer %s", tokens.AccessToken)) } -func ResponseUserInfo(ctx *gin.Context, userInfo *user.User) { +func ResponseUserInfo(ctx *gin.Context, userInfo *user.User, userChats []chat.Chat) { ctx.JSON(http.StatusOK, UserInfoDto{ - Message: "Success", - Code: 200, - UserInfo: userInfo, + Message: "Success", + Code: 200, + UserInfo: userInfo, + UserChats: userChats, }) } diff --git a/app/router/user_routes.go b/app/router/user_routes.go index 723e5bf..c5d2d5c 100644 --- a/app/router/user_routes.go +++ b/app/router/user_routes.go @@ -3,22 +3,24 @@ package router import ( "github.com/gin-gonic/gin" "github.com/kavkaco/Kavka-Core/app/controller" + "github.com/kavkaco/Kavka-Core/internal/domain/chat" "github.com/kavkaco/Kavka-Core/internal/domain/user" ) type UserRouter struct { - service user.UserService - ctrl *controller.UserController - router *gin.RouterGroup + userService user.Service + chatService chat.Service + ctrl *controller.UserController + router *gin.RouterGroup } -func NewUserRouter(router *gin.RouterGroup, service user.UserService) *UserRouter { - ctrl := controller.NewUserController(service) +func NewUserRouter(router *gin.RouterGroup, userService user.Service, chatService chat.Service) *UserRouter { + ctrl := controller.NewUserController(userService, chatService) router.POST("/login", ctrl.HandleLogin) router.POST("/verify_otp", ctrl.HandleVerifyOTP) router.POST("/refresh_token", ctrl.HandleRefreshToken) router.POST("/authenticate", ctrl.HandleAuthenticate) - return &UserRouter{service, ctrl, router} + return &UserRouter{userService, chatService, ctrl, router} } diff --git a/app/socket/chats_handler.go b/app/socket/chats_handler.go index cff1e27..d4df342 100644 --- a/app/socket/chats_handler.go +++ b/app/socket/chats_handler.go @@ -3,7 +3,6 @@ package socket import ( "log" - "github.com/kavkaco/Kavka-Core/app/presenters" "go.mongodb.org/mongo-driver/bson/primitive" ) @@ -32,12 +31,13 @@ func CreateDirect(event string, args MessageHandlerArgs) bool { return false } - chat, err := args.socketService.chatService.CreateDirect(args.staticID, staticID.(primitive.ObjectID)) + _, err := args.socketService.chatService.CreateDirect(args.staticID, staticID.(primitive.ObjectID)) if err != nil { return false } - err = args.conn.WriteJSON(presenters.ChatAsJSON(event, chat)) + // FIXME + // err = args.conn.WriteJSON(presenters.ChatAsJSON(event, chat)) return err == nil } @@ -50,13 +50,14 @@ func GetChat(event string, args MessageHandlerArgs) bool { return false } - chat, err := args.socketService.chatService.GetChat(staticID.(primitive.ObjectID)) + _, err := args.socketService.chatService.GetChat(staticID.(primitive.ObjectID)) if err != nil { log.Println("find chat error in socket:", err) return false } - err = args.conn.WriteJSON(presenters.ChatAsJSON(event, chat)) + // FIXME + // err = args.conn.WriteJSON(presenters.ChatAsJSON(event, chat)) return err == nil } @@ -67,14 +68,15 @@ func CreateGroup(event string, args MessageHandlerArgs) bool { description := args.message.Data["description"] if title != nil && username != nil && description != nil { - chat, createErr := args.socketService.chatService.CreateGroup(args.staticID, title.(string), username.(string), description.(string)) + _, createErr := args.socketService.chatService.CreateGroup(args.staticID, title.(string), username.(string), description.(string)) if createErr != nil { return false } - err := args.conn.WriteJSON(presenters.ChatAsJSON(event, chat)) + // FIXME + // err := args.conn.WriteJSON(presenters.ChatAsJSON(event, chat)) - return err == nil + return true } return false @@ -86,14 +88,16 @@ func CreateChannel(event string, args MessageHandlerArgs) bool { description := args.message.Data["description"] if title != nil && username != nil && description != nil { - chat, createErr := args.socketService.chatService.CreateChannel(args.staticID, title.(string), username.(string), description.(string)) + _, createErr := args.socketService.chatService.CreateChannel(args.staticID, title.(string), username.(string), description.(string)) if createErr != nil { return false } - err := args.conn.WriteJSON(presenters.ChatAsJSON(event, chat)) + // FIXME + // err := args.conn.WriteJSON(presenters.ChatAsJSON(event, chat)) - return err == nil + // return err == nil + return true } return false diff --git a/app/socket/socket_handler.go b/app/socket/socket_handler.go index 2766f15..b589f91 100644 --- a/app/socket/socket_handler.go +++ b/app/socket/socket_handler.go @@ -21,7 +21,7 @@ var ( ) type Service struct { - userService user.UserService + userService user.Service chatService chat.Service msgService message.Service } @@ -40,7 +40,7 @@ type MessageHandlerArgs struct { var upgrader = websocket.Upgrader{} -func NewSocketService(app *gin.Engine, userService user.UserService, chatService chat.Service, messageService message.Service) *Service { +func NewSocketService(app *gin.Engine, userService user.Service, chatService chat.Service, messageService message.Service) *Service { socketService := &Service{userService, chatService, messageService} app.GET("/ws", socketService.handleWebsocket) diff --git a/cmd/server/server.go b/cmd/server/server.go index 1eba176..df95643 100644 --- a/cmd/server/server.go +++ b/cmd/server/server.go @@ -4,8 +4,11 @@ import ( "github.com/gin-contrib/cors" "github.com/gin-gonic/gin" "github.com/kavkaco/Kavka-Core/app/router" + "github.com/kavkaco/Kavka-Core/app/socket" "github.com/kavkaco/Kavka-Core/config" "github.com/kavkaco/Kavka-Core/database" + chatRepository "github.com/kavkaco/Kavka-Core/internal/repository/chat" + messageRepository "github.com/kavkaco/Kavka-Core/internal/repository/message" userRepository "github.com/kavkaco/Kavka-Core/internal/repository/user" "github.com/kavkaco/Kavka-Core/internal/service" "github.com/kavkaco/Kavka-Core/pkg/session" @@ -45,16 +48,18 @@ func main() { userRepo := userRepository.NewUserRepository(mongoDB) userService := service.NewUserService(userRepo, session, smsService) - router.NewUserRouter(app.Group("/users"), userService) - //chatRepo := chatRepository.NewRepository(mongoDB) - //chatService := service.NewChatService(chatRepo, userRepo) + chatRepo := chatRepository.NewRepository(mongoDB) + chatService := service.NewChatService(chatRepo, userRepo) - //messageRepo := messageRepository.NewMessageRepository(mongoDB) - //messageRepository := service.NewMessageService(messageRepo, chatRepo) + messageRepo := messageRepository.NewMessageRepository(mongoDB) + messageRepository := service.NewMessageService(messageRepo, chatRepo) + + // Init routes + router.NewUserRouter(app.Group("/users"), userService, chatService) // Init Socket Server - //socket.NewSocketService(app, userService, chatService, messageRepository) + socket.NewSocketService(app, userService, chatService, messageRepository) // Everything almost done! err := app.Run(configs.App.HTTP.Address) diff --git a/database/mongo.go b/database/mongo.go index 35cb3bb..711c2b8 100644 --- a/database/mongo.go +++ b/database/mongo.go @@ -43,6 +43,12 @@ func GetMongoDBInstance(mongoConfigs config.Mongo) (*mongo.Database, error) { return nil, err } + // Send a ping to confirm a successful connection + var result bson.M + if err := client.Database("test").RunCommand(context.TODO(), bson.D{{Key: "ping", Value: 1}}).Decode(&result); err != nil { + return nil, err + } + mongoInstance = client.Database(mongoConfigs.DBName) collectionsConfigurations(mongoInstance) diff --git a/go.mod b/go.mod index 31eee57..0abd027 100644 --- a/go.mod +++ b/go.mod @@ -25,6 +25,7 @@ require ( github.com/go-playground/validator/v10 v10.15.3 // indirect github.com/goccy/go-json v0.10.2 // indirect github.com/google/uuid v1.3.1 // indirect + github.com/graph-gophers/graphql-go v1.5.0 // indirect github.com/json-iterator/go v1.1.12 // indirect github.com/klauspost/cpuid/v2 v2.2.5 // indirect github.com/leodido/go-urn v1.2.4 // indirect diff --git a/go.sum b/go.sum index 0ff7c54..5dfda3c 100644 --- a/go.sum +++ b/go.sum @@ -10,7 +10,6 @@ github.com/chenzhuoyu/base64x v0.0.0-20230717121745-296ad89f973d h1:77cEq6EriyTZ github.com/chenzhuoyu/base64x v0.0.0-20230717121745-296ad89f973d/go.mod h1:8EPpVsBuRksnlj1mLy4AWzRNQYxauNi62uWcE3to6eA= github.com/chenzhuoyu/iasm v0.9.0 h1:9fhXjVzq5hUy2gkhhgHl95zG2cEAhw9OSGs8toWWAwo= github.com/chenzhuoyu/iasm v0.9.0/go.mod h1:Xjy2NpN3h7aUqeqM+woSuuvxmIe6+DDsiNLIrkAmYog= -github.com/creack/pty v1.1.9 h1:uDmaGzcdjhF4i/plgjmEsriH11Y0o7RKapEf/LDaM3w= github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= @@ -32,6 +31,9 @@ github.com/gin-contrib/sse v0.1.0/go.mod h1:RHrZQHXnP2xjPF+u1gW/2HnVO7nvIa9PG3Gm github.com/gin-gonic/gin v1.8.1/go.mod h1:ji8BvRH1azfM+SYow9zQ6SZMvR8qOMZHmsCuWR9tTTk= github.com/gin-gonic/gin v1.9.1 h1:4idEAncQnU5cB7BeOkPtxjfCSye0AAm1R0RVIqJ+Jmg= github.com/gin-gonic/gin v1.9.1/go.mod h1:hPrL7YrpYKXt5YId3A/Tnip5kqbEAP+KLuI3SUcPTeU= +github.com/go-logr/logr v1.2.2/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A= +github.com/go-logr/logr v1.2.3/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A= +github.com/go-logr/stdr v1.2.2/go.mod h1:mMo/vtBO5dYbehREoey6XUKy/eSumjCCveDpRre4VKE= github.com/go-playground/assert/v2 v2.0.1/go.mod h1:VDjEfimB/XKnb+ZQfWdccd7VUvScMdVu0Titje2rxJ4= github.com/go-playground/assert/v2 v2.2.0 h1:JvknZsQTYeFEAhQwI4qEt9cyV5ONwRHC+lYKSsYSR8s= github.com/go-playground/assert/v2 v2.2.0/go.mod h1:VDjEfimB/XKnb+ZQfWdccd7VUvScMdVu0Titje2rxJ4= @@ -53,20 +55,21 @@ github.com/goccy/go-json v0.10.2 h1:CrxCmQqYDkv1z7lO7Wbh2HN93uovUHgrECaO5ZrCXAU= github.com/goccy/go-json v0.10.2/go.mod h1:6MelG93GURQebXPDq3khkgXZkazVtN9CRI+MGFi0w8I= github.com/golang-jwt/jwt/v4 v4.4.2 h1:rcc4lwaZgFMCZ5jxF9ABolDcIHdBytAFgqFPbSJQAYs= github.com/golang-jwt/jwt/v4 v4.4.2/go.mod h1:m21LjoU+eqJr34lmDMbreY2eSTRJ1cv77w39/MY0Ch0= -github.com/golang/protobuf v1.5.0 h1:LUVKkCeviFUMKqHa4tXIIij/lbhnMbP7Fn5wKdKkRh4= github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk= github.com/golang/snappy v0.0.1 h1:Qgr9rKW7uDUkrbSmQeiDsGa8SjGyCOGtuasMWwvp2P4= github.com/golang/snappy v0.0.1/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= github.com/google/go-cmp v0.5.2/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/go-cmp v0.5.7/go.mod h1:n+brtR0CgQNWTVd5ZUFpTBC8YFBDLK/h/bpaJ8/DtOE= github.com/google/go-cmp v0.5.8 h1:e6P7q2lk1O+qJJb4BtCQXlK8vWEO8V1ZeuEdJNOqZyg= github.com/google/go-cmp v0.5.8/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= -github.com/google/gofuzz v1.0.0 h1:A8PeW59pxE9IoFRqBp37U+mSNaQoZ46F1f0f863XSXw= github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= github.com/google/uuid v1.3.1 h1:KjJaJ9iWZ3jOFZIf1Lqf4laDRCasjl0BCmnEGxkdLb4= github.com/google/uuid v1.3.1/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/gorilla/websocket v1.5.0 h1:PPwGk2jz7EePpoHN/+ClbZu8SPxiqlu12wZP/3sWmnc= github.com/gorilla/websocket v1.5.0/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE= +github.com/graph-gophers/graphql-go v1.5.0 h1:fDqblo50TEpD0LY7RXk/LFVYEVqo3+tXMNMPSVXA1yc= +github.com/graph-gophers/graphql-go v1.5.0/go.mod h1:YtmJZDLbF1YYNrlNAuiO5zAStUWc3XZT07iGsVqe1Os= github.com/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnrnM= github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo= github.com/klauspost/compress v1.13.6/go.mod h1:/3/Vjq9QcHkK5uEr5lBEmyoZ1iFhe47etQ6QUkpK6sk= @@ -76,13 +79,11 @@ github.com/klauspost/cpuid/v2 v2.0.1/go.mod h1:FInQzS24/EEf25PyTYn52gqo7WaD8xa02 github.com/klauspost/cpuid/v2 v2.0.9/go.mod h1:FInQzS24/EEf25PyTYn52gqo7WaD8xa0213Md/qVLRg= github.com/klauspost/cpuid/v2 v2.2.5 h1:0E5MSMDEoAulmXNFquVs//DdoomxaoTY1kUhbc/qbZg= github.com/klauspost/cpuid/v2 v2.2.5/go.mod h1:Lcz8mBdAVJIBVzewtcLocK12l3Y+JytZYpaMropDUws= -github.com/knz/go-libedit v1.10.1 h1:0pHpWtx9vcvC0xGZqEQlQdfSQs7WRlAjuPvk3fOZDCo= github.com/knz/go-libedit v1.10.1/go.mod h1:MZTVkCWyz0oBc7JOWP3wNAzd002ZbM/5hgShxwh4x8M= github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= github.com/kr/pretty v0.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI= github.com/kr/pretty v0.3.0 h1:WgNl7dwNpEZ6jJ9k1snq4pZsg7DOEN8hP9Xw0Tsjwk0= github.com/kr/pretty v0.3.0/go.mod h1:640gp4NfQd8pI5XOwp5fnNeVWj67G7CFk/SaSQn7NBk= -github.com/kr/pty v1.1.1 h1:VkoXIwSboBpnk99O/KFauAEILuNHv5DVFKZMBN/gUgw= github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= @@ -112,10 +113,10 @@ github.com/onsi/ginkgo v1.16.5 h1:8xi0RTUf59SOSfEtZMvwTvXYMzG4gV23XVHOZiXNtnE= github.com/onsi/ginkgo v1.16.5/go.mod h1:+E8gABHa3K6zRBolWtd+ROzc/U5bkGt0FwiG042wbpU= github.com/onsi/gomega v1.18.1 h1:M1GfJqGRrBrrGGsbxzV5dqM2U2ApXefZCQpkukxYRLE= github.com/onsi/gomega v1.18.1/go.mod h1:0q+aL8jAiMXy9hbwj2mr5GziHiwhAIQpFmmtT5hitRs= +github.com/opentracing/opentracing-go v1.2.0/go.mod h1:GxEUsuufX4nBwe+T+Wl9TAgYrxe9dPLANfrWvHYVTgc= github.com/pelletier/go-toml/v2 v2.0.1/go.mod h1:r9LEWfGN8R5k0VXJ+0BkIe7MYkRdwZOjgMj2KwnJFUo= github.com/pelletier/go-toml/v2 v2.1.0 h1:FnwAJ4oYMvbT/34k9zzHuZNrhlz48GB3/s6at6/MHO4= github.com/pelletier/go-toml/v2 v2.1.0/go.mod h1:tJU2Z3ZkXwnxa4DPO899bsyIoywizdUvyaeZurnPPDc= -github.com/pkg/diff v0.0.0-20210226163009-20ebb0f2a09e h1:aoZm08cpOy4WuID//EZDgcC4zIxODThtZNPirFr42+A= github.com/pkg/diff v0.0.0-20210226163009-20ebb0f2a09e/go.mod h1:pJLUxLENpZxwdsKMEsNbx1VGcRFpLqf3715MtcvvzbA= github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= @@ -128,7 +129,6 @@ github.com/sirupsen/logrus v1.9.3 h1:dueUQJ1C2q9oE3F7wvmSGAaVtTmUizReu6fjN8uqzbQ github.com/sirupsen/logrus v1.9.3/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw= -github.com/stretchr/objx v0.5.0 h1:1zr/of2m5FGMsad5YfcqgdqdWrIhu+EBEJRhR1U7z/c= github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo= github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= @@ -141,7 +141,6 @@ github.com/stretchr/testify v1.8.4 h1:CcVxjf3Q8PM0mHUKJCdn+eZZtm5yQwehR5yeSVQQcU github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo= github.com/twitchyliquid64/golang-asm v0.15.1 h1:SU5vSMR7hnwNxj24w34ZyCi/FmDZTkS4MhqMhdFk5YI= github.com/twitchyliquid64/golang-asm v0.15.1/go.mod h1:a1lVb/DtPvCB8fslRZhAngC2+aY1QWCk3Cedj/Gdt08= -github.com/ugorji/go v1.2.7 h1:qYhyWUUd6WbiM+C6JZAUkIJt/1WrjzNHY9+KCIjVqTo= github.com/ugorji/go v1.2.7/go.mod h1:nF9osbDWLy6bDVv/Rtoh6QgnvNDpmCalQV5urGCCS6M= github.com/ugorji/go/codec v1.2.7/go.mod h1:WGN1fab3R1fzQlVQTkfxVtIBhWDRqOviHU95kRgeqEY= github.com/ugorji/go/codec v1.2.11 h1:BMaWp1Bb6fHwEtbplGBGJ498wD+LKlNSl25MjdZY4dU= @@ -154,10 +153,11 @@ github.com/xdg-go/stringprep v1.0.4 h1:XLI/Ng3O1Atzq0oBs3TWm+5ZVgkq2aqdlvP9JtoZ6 github.com/xdg-go/stringprep v1.0.4/go.mod h1:mPGuuIYwz7CmR2bT9j4GbQqutWS1zV24gijq1dTyGkM= github.com/youmark/pkcs8 v0.0.0-20181117223130-1be2e3e5546d h1:splanxYIlg+5LfHAM6xpdFEAYOk8iySO56hMFq6uLyA= github.com/youmark/pkcs8 v0.0.0-20181117223130-1be2e3e5546d/go.mod h1:rHwXgn7JulP+udvsHwJoVG1YGAP6VLg4y9I5dyZdqmA= -github.com/yuin/goldmark v1.4.13 h1:fVcFKWvrslecOb/tg+Cc05dkeYx540o0FuFt3nUVDoE= github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY= go.mongodb.org/mongo-driver v1.12.1 h1:nLkghSU8fQNaK7oUmDhQFsnrtcoNy7Z6LVFKsEecqgE= go.mongodb.org/mongo-driver v1.12.1/go.mod h1:/rGBTebI3XYboVmgz+Wv3Bcbl3aD0QF9zl6kDDw18rQ= +go.opentelemetry.io/otel v1.6.3/go.mod h1:7BgNga5fNlF/iZjG06hM3yofffp0ofKCDwSXx1GC4dI= +go.opentelemetry.io/otel/trace v1.6.3/go.mod h1:GNJQusJlUgZl9/TQBPKU/Y/ty+0iVB5fjhKeJGZPGFs= golang.org/x/arch v0.0.0-20210923205945-b76863e36670/go.mod h1:5om86z9Hs0C8fWVUuoMHwpExlXzs5Tkyp9hOrfG7pp8= golang.org/x/arch v0.5.0 h1:jpGode6huXQxcskEIpOCvrU+tzo81b6+oFLUYXWtH/Y= golang.org/x/arch v0.5.0/go.mod h1:5om86z9Hs0C8fWVUuoMHwpExlXzs5Tkyp9hOrfG7pp8= @@ -170,8 +170,6 @@ golang.org/x/crypto v0.13.0/go.mod h1:y6Z2r+Rw4iayiXXAIxJIDAJ1zMW4yaTpebo8fPOliY golang.org/x/exp v0.0.0-20230817173708-d852ddb80c63 h1:m64FZMko/V45gv0bNmrNYoDEq8U5YUhetc9cBWKS1TQ= golang.org/x/exp v0.0.0-20230817173708-d852ddb80c63/go.mod h1:0v4NqG35kSWCMzLaMeX+IQrlSnVE/bqGSyC2cz/9Le8= golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4= -golang.org/x/mod v0.12.0 h1:rmsUpXtvNzj340zd98LZ4KntptpfRHwpFOHG188oHXc= -golang.org/x/mod v0.12.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= @@ -196,8 +194,6 @@ golang.org/x/sys v0.12.0 h1:CM0HF96J0hcLAwsHPJZjfdNzs0gftsLfgKt57wWHJ0o= golang.org/x/sys v0.12.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= -golang.org/x/term v0.12.0 h1:/ZfYdc3zq+q02Rv9vGqTeSItdzZTSNDmfTi0mBAuidU= -golang.org/x/term v0.12.0/go.mod h1:owVbMEjm3cBLCHdkQu9b1opXd4ETQWc3BhuQGKgXgvU= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= @@ -209,10 +205,7 @@ golang.org/x/text v0.13.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc= -golang.org/x/tools v0.12.1-0.20230815132531-74c255bcf846 h1:Vve/L0v7CXXuxUmaMGIEK/dEeq7uiqb5qBgQrZzIE7E= -golang.org/x/tools v0.12.1-0.20230815132531-74c255bcf846/go.mod h1:Sc0INKfu04TlqNoRA1hgpFZbhYXHPr4V5DzpSBTPqQM= golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= -golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543 h1:E7g+9GITq07hpfrRu66IVDexMakfv52eLZ2CXBWiKr4= golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw= google.golang.org/protobuf v1.28.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I= @@ -222,7 +215,6 @@ gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8 gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk= gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q= -gopkg.in/errgo.v2 v2.1.0 h1:0vLT13EuvQ0hNvakwLuFZ/jYrLp5F3kcWHXdRggjCE8= gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI= gopkg.in/go-playground/assert.v1 v1.2.1 h1:xoYuJVE7KT85PYWrN730RguIQO0ePzVRfFMXadIrXTM= gopkg.in/go-playground/assert.v1 v1.2.1/go.mod h1:9RXL0bg/zibRAgZUYszZSwO/z8Y/a8bDuhia5mkpMnE= @@ -236,7 +228,5 @@ gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= -nullprogram.com/x/optparse v1.0.0 h1:xGFgVi5ZaWOnYdac2foDT3vg0ZZC9ErXFV57mr4OHrI= nullprogram.com/x/optparse v1.0.0/go.mod h1:KdyPE+Igbe0jQUrVfMqDMeJQIJZEuyV7pjYmp6pbG50= -rsc.io/pdf v0.1.1 h1:k1MczvYDUvJBe93bYd7wrZLLUEcLZAuF824/I4e5Xr4= rsc.io/pdf v0.1.1/go.mod h1:n8OzWcQ6Sp37PL01nO98y4iUCRdTGarVfzxY20ICaU4= diff --git a/internal/domain/chat/chat_entity.go b/internal/domain/chat/chat_entity.go index 6b9e614..4b9d380 100644 --- a/internal/domain/chat/chat_entity.go +++ b/internal/domain/chat/chat_entity.go @@ -13,10 +13,9 @@ const ( TypeDirect = "direct" ) -type StaticID = primitive.ObjectID - +// type StaticID = primitive.ObjectID type Chat struct { - ChatID primitive.ObjectID `bson:"id" json:"chatId"` + ChatID primitive.ObjectID `bson:"chat_id" json:"chatId"` ChatType string `bson:"chat_type" json:"chatType"` ChatDetail interface{} `bson:"chat_detail" json:"chatDetail"` Messages []*message.Message `bson:"messages" json:"messages"` @@ -99,6 +98,7 @@ type Repository interface { Create(newChat Chat) (*Chat, error) Where(filter bson.M) ([]Chat, error) Destroy(chatID primitive.ObjectID) error + GetUserChats(userStaticID primitive.ObjectID) ([]Chat, error) FindByID(staticID primitive.ObjectID) (*Chat, error) FindChatOrSidesByStaticID(staticID primitive.ObjectID) (*Chat, error) FindBySides(sides [2]primitive.ObjectID) (*Chat, error) @@ -106,14 +106,8 @@ type Repository interface { type Service interface { GetChat(staticID primitive.ObjectID) (*Chat, error) + GetUserChats(userStaticID primitive.ObjectID) ([]Chat, error) CreateDirect(userStaticID primitive.ObjectID, targetStaticID primitive.ObjectID) (*Chat, error) CreateGroup(userStaticID primitive.ObjectID, title string, username string, description string) (*Chat, error) CreateChannel(userStaticID primitive.ObjectID, title string, username string, description string) (*Chat, error) } - -type Service interface { - GetChat(staticID primitive.ObjectID) (Chat, error) - CreateDirect(userStaticID primitive.ObjectID, targetStaticID primitive.ObjectID) (Chat, error) - CreateGroup(userStaticID primitive.ObjectID, title string, username string, description string) (Chat, error) - CreateChannel(userStaticID primitive.ObjectID, title string, username string, description string) (Chat, error) -} diff --git a/internal/domain/user/user_entity.go b/internal/domain/user/user_entity.go index 0efcd94..a6ece5e 100644 --- a/internal/domain/user/user_entity.go +++ b/internal/domain/user/user_entity.go @@ -58,7 +58,7 @@ type UserRepository interface { FindByPhone(phone string) (*User, error) } -type UserService interface { +type Service interface { Login(phone string) error VerifyOTP(phone string, otp int) (*session.LoginTokens, error) RefreshToken(refreshToken string, accessToken string) (string, error) diff --git a/internal/repository/chat/chat_repository.go b/internal/repository/chat/chat_repository.go index c191ad8..0375740 100644 --- a/internal/repository/chat/chat_repository.go +++ b/internal/repository/chat/chat_repository.go @@ -25,18 +25,16 @@ func NewRepository(db *mongo.Database) chat.Repository { } func (repo *repository) Create(newChat chat.Chat) (*chat.Chat, error) { - result, err := repo.chatsCollection.InsertOne(context.Background(), newChat) + _, err := repo.chatsCollection.InsertOne(context.Background(), newChat) if err != nil { return nil, err } - newChat.ChatID = result.InsertedID.(primitive.ObjectID) - return &newChat, nil } func (repo *repository) Destroy(chatID primitive.ObjectID) error { - filter := bson.M{"id": chatID} + filter := bson.M{"chat_id": chatID} _, err := repo.chatsCollection.DeleteOne(context.TODO(), filter) if err != nil { @@ -77,8 +75,23 @@ func (repo *repository) findBy(filter bson.M) (*chat.Chat, error) { return nil, ErrChatNotFound } +func (repo *repository) GetUserChats(userStaticID primitive.ObjectID) ([]chat.Chat, error) { + filter := bson.M{ + "chat_detail.members": bson.M{ + "$in": bson.A{userStaticID}, + }, + } + + chats, err := repo.Where(filter) + if err != nil { + return nil, err + } + + return chats, nil +} + func (repo *repository) FindByID(staticID primitive.ObjectID) (*chat.Chat, error) { - filter := bson.M{"id": staticID} + filter := bson.M{"chat_id": staticID} return repo.findBy(filter) } @@ -86,7 +99,7 @@ func (repo *repository) FindChatOrSidesByStaticID(staticID primitive.ObjectID) ( filter := bson.M{ "$or": []interface{}{ bson.M{"chat_detail.sides": bson.M{"$in": []primitive.ObjectID{staticID}}}, - bson.M{"id": staticID}, + bson.M{"chat_id": staticID}, }, } diff --git a/internal/repository/chat/chat_repository_mock.go b/internal/repository/chat/chat_repository_mock.go deleted file mode 100644 index 93a3660..0000000 --- a/internal/repository/chat/chat_repository_mock.go +++ /dev/null @@ -1,221 +0,0 @@ -package repository - -import ( - "errors" - "reflect" - - "github.com/fatih/structs" - "github.com/kavkaco/Kavka-Core/internal/domain/chat" - "github.com/kavkaco/Kavka-Core/utils" - "go.mongodb.org/mongo-driver/bson" - "go.mongodb.org/mongo-driver/bson/primitive" - "reflect" -) - -type MockRepository struct { - chats []chat.Chat -} - -func NewMockRepository() chat.Repository { - return &MockRepository{} -} - -func (repo *MockRepository) Create(newChat chat.Chat) (*chat.Chat, error) { - repo.chats = append(repo.chats, newChat) - - return &newChat, nil -} - -func (repo *MockRepository) Where(filter bson.M) ([]chat.Chat, error) { - var filterKey string - var filterValue interface{} - - for k, v := range filter { - filterKey = k - filterValue = v - } - - var result []chat.Chat - - if len(repo.chats) == 0 { - return result, nil - } - - for _, row := range repo.chats { - // Check filter - fields := structs.Fields(row) - - for _, field := range fields { - tag := field.Tag("bson") - fieldValue := field.Value() - fieldValueType := reflect.TypeOf(fieldValue).Name() - - if filterKey == tag { - if (fieldValue == filterValue) || (fieldValueType == "ObjectID" && fieldValue.(primitive.ObjectID).Hex() == filterValue.(primitive.ObjectID).Hex()) { - result = append(result, row) - } - } - } - } - - return result, nil -} - -func (repo *MockRepository) Destroy(chatID primitive.ObjectID) error { - for index, row := range repo.chats { - if row.ChatID == chatID { - repo.chats = append(repo.chats[:index], repo.chats[index+1:]...) - break - } - } - - return ErrChatNotFound -} - -func (repo *MockRepository) findBy(filter bson.M) (*chat.Chat, error) { - result, err := repo.Where(filter) - if err != nil { - return nil, err - } - - if len(result) > 0 { - row := result[len(result)-1] - - return &row, nil - } - - return nil, ErrChatNotFound -} - -func (repo *MockRepository) FindByID(staticID primitive.ObjectID) (*chat.Chat, error) { - filter := bson.M{"id": staticID} - return repo.findBy(filter) -} - -func (repo *MockRepository) FindChatOrSidesByStaticID(staticID primitive.ObjectID) (*chat.Chat, error) { - // Check as a group or channel with StaticID - resultByID, err := repo.FindByID(staticID) - if errors.Is(err, ErrChatNotFound) { - // This condition means it is not a channel or group - // So we must look up in the Sides of direct-chats. - for _, c := range repo.chats { - if c.ChatType == chat.TypeDirect { - chatDetail, err := utils.TypeConverter[chat.DirectChatDetail](c.ChatDetail) - if err != nil { - return nil, err - } - - if chatDetail.HasSide(staticID) { - return &c, nil - } - } - } - } else { - return resultByID, nil - } - - return nil, ErrChatNotFound -} - -func (repo *MockRepository) FindBySides(sides [2]primitive.ObjectID) (*chat.Chat, error) { - for _, c := range repo.chats { - if c.ChatType == chat.TypeDirect { - chatDetail, _ := utils.TypeConverter[chat.DirectChatDetail](c.ChatDetail) - chatSides := chatDetail.Sides - - if chatSides[0].Hex() == sides[0].Hex() && chatSides[1].Hex() == sides[1].Hex() { - return &c, nil - } - } -} - -func (repo *MockRepository) Where(filter bson.M) ([]chat.Chat, error) { - var filterKey string - var filterValue interface{} - - for k, v := range filter { - filterKey = k - filterValue = v - } - - var result []chat.Chat - - if len(repo.chats) == 0 { - return result, nil - } - - for _, row := range repo.chats { - // Check filter - fields := structs.Fields(row) - - for _, field := range fields { - tag := field.Tag("bson") - fieldValue := field.Value() - fieldValueType := reflect.TypeOf(fieldValue).Name() - - if filterKey == tag { - if (fieldValue == filterValue) || (fieldValueType == "ObjectID" && fieldValue.(primitive.ObjectID).Hex() == filterValue.(primitive.ObjectID).Hex()) { - result = append(result, row) - } - } - } - } - - return result, nil -} - -func (repo *MockRepository) Destroy(chatID primitive.ObjectID) error { - for index, row := range repo.chats { - if row.ChatID == chatID { - repo.chats = append(repo.chats[:index], repo.chats[index+1:]...) - break - } - } - - return ErrChatNotFound -} - -func (repo *MockRepository) findBy(filter bson.M) (*chat.Chat, error) { - result, err := repo.Where(filter) - if err != nil { - return nil, err - } - - if len(result) > 0 { - row := result[len(result)-1] - - return &row, nil - } - - return nil, ErrChatNotFound -} - -func (repo *MockRepository) FindByID(staticID primitive.ObjectID) (*chat.Chat, error) { - filter := bson.M{"id": staticID} - return repo.findBy(filter) -} - -func (repo *MockRepository) FindChatOrSidesByStaticID(staticID primitive.ObjectID) (*chat.Chat, error) { - // Check as a group or channel with StaticID - resultByID, err := repo.FindByID(staticID) - if errors.Is(err, ErrChatNotFound) { - // This condition means it is not a channel or group - // So we must look up in the Sides of direct-chats. - for _, c := range repo.chats { - if c.ChatType == chat.TypeDirect { - chatDetail, err := utils.TypeConverter[chat.DirectChatDetail](c.ChatDetail) - if err != nil { - return nil, err - } - - if chatDetail.HasSide(staticID) { - return &c, nil - } - } - } - } else { - return resultByID, nil - } - - return nil, ErrChatNotFound -} diff --git a/internal/repository/chat/chat_repository_test.go b/internal/repository/chat/chat_repository_test.go index a5eac84..df2555b 100644 --- a/internal/repository/chat/chat_repository_test.go +++ b/internal/repository/chat/chat_repository_test.go @@ -1,32 +1,44 @@ package repository import ( + "context" "testing" + "github.com/kavkaco/Kavka-Core/config" + "github.com/kavkaco/Kavka-Core/database" "github.com/kavkaco/Kavka-Core/internal/domain/chat" "github.com/kavkaco/Kavka-Core/utils" "github.com/stretchr/testify/assert" - "github.com/kavkaco/Kavka-Core/utils" - "github.com/stretchr/testify/assert" - "testing" - - "github.com/kavkaco/Kavka-Core/internal/domain/chat" "github.com/stretchr/testify/suite" "go.mongodb.org/mongo-driver/bson/primitive" + "go.mongodb.org/mongo-driver/mongo" ) const SampleChatUsername = "sample_chat" type MyTestSuite struct { suite.Suite + db *mongo.Database chatRepo chat.Repository - sampleChannelChat chat.Chat - sampleDirectChat chat.Chat + sampleChannelChatID primitive.ObjectID + sampleDirectChatID primitive.ObjectID sampleDirectChatSides [2]primitive.ObjectID creatorID primitive.ObjectID } func (s *MyTestSuite) SetupSuite() { + // Connecting to test database! + cfg := config.Read() + cfg.Mongo.DBName = "test" + db, connErr := database.GetMongoDBInstance(cfg.Mongo) + assert.NoError(s.T(), connErr) + s.db = db + + // Drop test db + err := s.db.Drop(context.TODO()) + assert.NoError(s.T(), err) + + // Set a new object-id to sample creator s.creatorID = primitive.NewObjectID() // Create the clients who going to chat with each other in direct-chat. @@ -34,7 +46,7 @@ func (s *MyTestSuite) SetupSuite() { user2StaticID := primitive.NewObjectID() s.sampleDirectChatSides = [2]primitive.ObjectID{user1StaticID, user2StaticID} - s.chatRepo = NewMockRepository() + s.chatRepo = NewRepository(db) } func (s *MyTestSuite) TestA_Create() { @@ -55,7 +67,9 @@ func (s *MyTestSuite) TestA_Create() { assert.True(s.T(), newChannelChat.IsMember(s.creatorID)) assert.True(s.T(), newChannelChat.IsAdmin(s.creatorID)) - s.sampleChannelChat = *newChannelChat + s.sampleChannelChatID = newChannelChat.ChatID + + s.T().Log(newChannelChat.ChatID) // Create a direct chat newDirectChat := chat.NewChat(chat.TypeDirect, &chat.DirectChatDetail{Sides: s.sampleDirectChatSides}) @@ -67,7 +81,7 @@ func (s *MyTestSuite) TestA_Create() { assert.NoError(s.T(), err) - s.sampleDirectChat = *newDirectChat + s.sampleDirectChatID = newDirectChat.ChatID } func (s *MyTestSuite) TestB_FindByID() { @@ -83,7 +97,7 @@ func (s *MyTestSuite) TestB_FindByID() { }, { name: "Should be found", - staticID: s.sampleChannelChat.ChatID, + staticID: s.sampleChannelChatID, success: true, }, } @@ -115,12 +129,12 @@ func (s *MyTestSuite) TestC_FindChatOrSidesByStaticID() { }, { name: "Find the direct chat by staticID", - staticID: s.sampleDirectChat.ChatID, + staticID: s.sampleDirectChatID, success: true, }, - { + { name: "Find the channel chat by staticID", - staticID: s.sampleChannelChat.ChatID, + staticID: s.sampleChannelChatID, success: true, }, } diff --git a/internal/repository/message/message_repository.go b/internal/repository/message/message_repository.go index 5a9b953..d7a052b 100644 --- a/internal/repository/message/message_repository.go +++ b/internal/repository/message/message_repository.go @@ -3,6 +3,7 @@ package repository import ( "context" "errors" + "github.com/kavkaco/Kavka-Core/database" "github.com/kavkaco/Kavka-Core/internal/domain/message" "go.mongodb.org/mongo-driver/bson" @@ -25,7 +26,7 @@ func NewMessageRepository(db *mongo.Database) message.Repository { } func (repo *messageRepository) Insert(chatID primitive.ObjectID, msg *message.Message) (*message.Message, error) { - filter := bson.M{"id": chatID} + filter := bson.M{"chat_id": chatID} update := bson.M{"$push": bson.M{"messages": msg}} _, err := repo.chatsCollection.UpdateOne(context.TODO(), filter, update) @@ -40,7 +41,7 @@ func (repo *messageRepository) Insert(chatID primitive.ObjectID, msg *message.Me } func (repo *messageRepository) Update(chatID primitive.ObjectID, messageID primitive.ObjectID, fieldsToUpdate bson.M) error { - filter := bson.M{"id": chatID, "messages.id": messageID} + filter := bson.M{"chat_id": chatID, "messages.message_id": messageID} update := bson.M{"$set": fieldsToUpdate} _, err := repo.chatsCollection.UpdateOne(context.TODO(), filter, update) @@ -56,8 +57,8 @@ func (repo *messageRepository) Update(chatID primitive.ObjectID, messageID primi } func (repo *messageRepository) Delete(chatID primitive.ObjectID, messageID primitive.ObjectID) error { - filter := bson.M{"id": chatID} - update := bson.M{"$pull": bson.M{"messages": bson.M{"id": messageID}}} + filter := bson.M{"chat_id": chatID} + update := bson.M{"$pull": bson.M{"messages": bson.M{"message_id": messageID}}} _, err := repo.chatsCollection.UpdateOne(context.TODO(), filter, update) if err != nil { diff --git a/internal/repository/message/message_repository_mock.go b/internal/repository/message/message_repository_mock.go deleted file mode 100644 index 8d7661c..0000000 --- a/internal/repository/message/message_repository_mock.go +++ /dev/null @@ -1,74 +0,0 @@ -package repository - -import ( - "github.com/kavkaco/Kavka-Core/internal/domain/chat" - "github.com/kavkaco/Kavka-Core/internal/domain/message" - "github.com/kavkaco/Kavka-Core/utils/structs" - "go.mongodb.org/mongo-driver/bson" - "go.mongodb.org/mongo-driver/bson/primitive" -) - -type MockRepository struct { - chats []*chat.Chat -} - -func (repo MockRepository) Insert(chatID primitive.ObjectID, msg *message.Message) (*message.Message, error) { - for i, c := range repo.chats { - if c.ChatID.Hex() == chatID.Hex() { - repo.chats[i].Messages = append(c.Messages, msg) - return msg, nil - } - } - - return nil, ErrChatNotFound -} - -func (repo MockRepository) Update(chatID primitive.ObjectID, messageID primitive.ObjectID, fieldsToUpdate bson.M) error { - for i, c := range repo.chats { - if c.ChatID.Hex() == chatID.Hex() { - chatMessages := c.Messages - - for j, m := range chatMessages { - if m.MessageID.Hex() == messageID.Hex() { - // Change value in the message - for key, value := range fieldsToUpdate { - msg := repo.chats[i].Messages[j] - - setErr := structs.SetFieldByBSON(msg, key, value) - if setErr != nil { - return setErr - } - } - - return nil - } - } - } - } - - return ErrChatNotFound -} - -func (repo MockRepository) Delete(chatID primitive.ObjectID, messageID primitive.ObjectID) error { - for i, c := range repo.chats { - if c.ChatID.Hex() == chatID.Hex() { // Chat found! - for j, m := range c.Messages { - if m.MessageID.Hex() == messageID.Hex() { - messagesList := repo.chats[i].Messages - repo.chats[i].Messages = append(messagesList[:j], messagesList[j+1:]...) - return nil - } - } - - return ErrMsgNotFound - } - } - - return ErrChatNotFound -} - -// NewMockRepository takes the @existingChats to mock repo because -// we do not want to focus on creating a chat here in this mock-repository. -func NewMockRepository(existingChats []*chat.Chat) message.Repository { - return &MockRepository{existingChats} -} diff --git a/internal/repository/message/message_repository_test.go b/internal/repository/message/message_repository_test.go index 70dedc0..e067a2d 100644 --- a/internal/repository/message/message_repository_test.go +++ b/internal/repository/message/message_repository_test.go @@ -1,83 +1,116 @@ package repository import ( + "context" "testing" + "github.com/kavkaco/Kavka-Core/config" + "github.com/kavkaco/Kavka-Core/database" "github.com/kavkaco/Kavka-Core/internal/domain/chat" "github.com/kavkaco/Kavka-Core/internal/domain/message" + repository "github.com/kavkaco/Kavka-Core/internal/repository/chat" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/suite" "go.mongodb.org/mongo-driver/bson" "go.mongodb.org/mongo-driver/bson/primitive" + "go.mongodb.org/mongo-driver/mongo" ) type MyTestSuite struct { suite.Suite - messageRepo message.Repository - chats []*chat.Chat - senderStaticID primitive.ObjectID + db *mongo.Database + messageRepo message.Repository + chatRepo chat.Repository + senderStaticID primitive.ObjectID + channelChatID primitive.ObjectID + sampleMessageID primitive.ObjectID } func (s *MyTestSuite) SetupSuite() { + // Connecting to test database! + cfg := config.Read() + cfg.Mongo.DBName = "test" + db, connErr := database.GetMongoDBInstance(cfg.Mongo) + assert.NoError(s.T(), connErr) + s.db = db + + // Drop test db + err := s.db.Drop(context.TODO()) + assert.NoError(s.T(), err) + + s.messageRepo = NewMessageRepository(db) + s.chatRepo = repository.NewRepository(db) + s.senderStaticID = primitive.NewObjectID() - s.chats = []*chat.Chat{ - chat.NewChat(chat.TypeChannel, chat.ChannelChatDetail{}), - } + newChannelChat := chat.NewChat(chat.TypeChannel, &chat.ChannelChatDetail{ + Title: "New Channel", + Username: "sample_channel", + Description: "This is a new channel created from unit-test.", + Members: []primitive.ObjectID{s.senderStaticID}, + Admins: []primitive.ObjectID{s.senderStaticID}, + }) + + newChannelChat, createErr := s.chatRepo.Create(*newChannelChat) + assert.NoError(s.T(), createErr) - s.messageRepo = NewMockRepository(s.chats) + s.channelChatID = newChannelChat.ChatID } func (s *MyTestSuite) TestA_InsertTextMessage() { - // Inserting a text-message into the first chat of the list. - selectedChatIndex := 0 - chatID := s.chats[selectedChatIndex].ChatID + chatID := s.channelChatID messageContent := "Hello World" newMessage := message.NewMessage(s.senderStaticID, message.TypeTextMessage, message.TextMessage{Message: messageContent}) - _, err := s.messageRepo.Insert(chatID, newMessage) - + createdMessage, err := s.messageRepo.Insert(chatID, newMessage) assert.NoError(s.T(), err) // Reading the memory for the recently updated store. - chatMessages := s.chats[selectedChatIndex].Messages + foundChat, findErr := s.chatRepo.FindByID(chatID) + assert.NoError(s.T(), findErr) + chatMessages := foundChat.Messages + assert.Equal(s.T(), len(chatMessages), 1) // Get the created message from the store. - createdMessage := chatMessages[0] - assert.Equal(s.T(), createdMessage.Type, message.TypeTextMessage) assert.Equal(s.T(), createdMessage.Content.(message.TextMessage).Message, messageContent) assert.NotEmpty(s.T(), createdMessage.MessageID) + + s.sampleMessageID = createdMessage.MessageID } func (s *MyTestSuite) TestB_UpdateMessage() { // Update the fields of a text-message - c := s.chats[0] - messageID := s.chats[0].Messages[0].MessageID - - update := bson.M{"seen": true} + update := bson.M{ + "messages.$.seen": true, + } - err := s.messageRepo.Update(c.ChatID, messageID, update) + err := s.messageRepo.Update(s.channelChatID, s.sampleMessageID, update) assert.NoError(s.T(), err) // Reading the memory for the recently updated store. - msg := s.chats[0].Messages[0] - assert.Equal(s.T(), msg.Seen, true) + foundChat, findErr := s.chatRepo.FindByID(s.channelChatID) + assert.NoError(s.T(), findErr) + chatMessages := foundChat.Messages + + createdMessage := chatMessages[0] + + assert.Equal(s.T(), createdMessage.Seen, true) } func (s *MyTestSuite) TestC_DeleteMessage() { // Delete a text-message from the first chat of the list. - chatID := s.chats[0].ChatID - messageID := s.chats[0].Messages[0].MessageID - - err := s.messageRepo.Delete(chatID, messageID) + err := s.messageRepo.Delete(s.channelChatID, s.sampleMessageID) assert.NoError(s.T(), err) // Reading the memory for the recently updated store. - chatMessages := s.chats[0].Messages + foundChat, findErr := s.chatRepo.FindByID(s.channelChatID) + assert.NoError(s.T(), findErr) + chatMessages := foundChat.Messages + assert.Equal(s.T(), len(chatMessages), 0) } diff --git a/internal/repository/user/user_repository_mock.go b/internal/repository/user/user_repository_mock.go deleted file mode 100644 index 1b3d05c..0000000 --- a/internal/repository/user/user_repository_mock.go +++ /dev/null @@ -1,93 +0,0 @@ -package repository - -import ( - "github.com/fatih/structs" - "github.com/kavkaco/Kavka-Core/internal/domain/user" - "go.mongodb.org/mongo-driver/bson" - "go.mongodb.org/mongo-driver/bson/primitive" -) - -type MockUserRepository struct { - users []*user.User -} - -func NewMockUserRepository() user.UserRepository { - return &MockUserRepository{} -} - -func (repo *MockUserRepository) Create(user *user.User) (*user.User, error) { - repo.users = append(repo.users, user) - - return user, nil -} - -func (repo *MockUserRepository) Where(filter bson.M) ([]*user.User, error) { - var filterKey string - var filterValue interface{} - - for k, v := range filter { - filterKey = k - filterValue = v - } - - var result []*user.User - - if len(repo.users) == 0 { - return result, nil - } - - for _, row := range repo.users { - // Check filter for user - fields := structs.Fields(row) - - for _, field := range fields { - tagValue := field.Tag("bson") - fieldValue := field.Value() - - if filterKey == tagValue { - switch filterValue := filterValue.(type) { - case primitive.ObjectID: - if fieldValue.(primitive.ObjectID).Hex() == filterValue.Hex() { - result = append(result, row) - } - case any: - if fieldValue == filterValue { - result = append(result, row) - } - } - } - } - } - - return result, nil -} - -func (repo *MockUserRepository) findBy(filter bson.M) (*user.User, error) { - users, err := repo.Where(filter) - if err != nil { - return nil, err - } - - if len(users) > 0 { - result := users[len(users)-1] - - return result, nil - } - - return nil, ErrUserNotFound -} - -func (repo *MockUserRepository) FindByID(staticID primitive.ObjectID) (*user.User, error) { - filter := bson.M{"id": staticID} - return repo.findBy(filter) -} - -func (repo *MockUserRepository) FindByUsername(username string) (*user.User, error) { - filter := bson.M{"username": username} - return repo.findBy(filter) -} - -func (repo *MockUserRepository) FindByPhone(phone string) (*user.User, error) { - filter := bson.M{"phone": phone} - return repo.findBy(filter) -} diff --git a/internal/repository/user/user_repository_test.go b/internal/repository/user/user_repository_test.go index bc82644..d79c56d 100644 --- a/internal/repository/user/user_repository_test.go +++ b/internal/repository/user/user_repository_test.go @@ -1,25 +1,41 @@ package repository import ( + "context" "testing" + "github.com/kavkaco/Kavka-Core/config" + "github.com/kavkaco/Kavka-Core/database" "github.com/kavkaco/Kavka-Core/internal/domain/user" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/suite" "go.mongodb.org/mongo-driver/bson" "go.mongodb.org/mongo-driver/bson/primitive" + "go.mongodb.org/mongo-driver/mongo" ) const Phone = "user-phone" type MyTestSuite struct { suite.Suite + db *mongo.Database userRepo user.UserRepository sampleUser user.User } func (s *MyTestSuite) SetupSuite() { - s.userRepo = NewMockUserRepository() + // Connecting to test database! + cfg := config.Read() + cfg.Mongo.DBName = "test" + db, connErr := database.GetMongoDBInstance(cfg.Mongo) + assert.NoError(s.T(), connErr) + s.db = db + + // Drop test db + err := s.db.Drop(context.TODO()) + assert.NoError(s.T(), err) + + s.userRepo = NewUserRepository(db) } func (s *MyTestSuite) TestA_Create() { diff --git a/internal/service/chat_service.go b/internal/service/chat_service.go index 0163f9c..007ab25 100644 --- a/internal/service/chat_service.go +++ b/internal/service/chat_service.go @@ -25,6 +25,10 @@ func (s *chatService) GetChat(staticID primitive.ObjectID) (*chat.Chat, error) { return foundChat, nil } +func (s *chatService) GetUserChats(userStaticID primitive.ObjectID) ([]chat.Chat, error) { + return s.chatRepo.GetUserChats(userStaticID) +} + func (s *chatService) CreateDirect(userStaticID primitive.ObjectID, targetStaticID primitive.ObjectID) (*chat.Chat, error) { sides := [2]primitive.ObjectID{ userStaticID, diff --git a/internal/service/message_service.go b/internal/service/message_service.go index 1b34c9d..38d841b 100644 --- a/internal/service/message_service.go +++ b/internal/service/message_service.go @@ -3,6 +3,7 @@ package service import ( "log" "reflect" + "slices" "github.com/kavkaco/Kavka-Core/internal/domain/chat" "github.com/kavkaco/Kavka-Core/internal/domain/message" @@ -20,21 +21,20 @@ func NewMessageService(msgRepo message.Repository, chatRepo chat.Repository) mes } // This function is used to check that user can send message or not. -func (s *messageService) hasAccessToSendMessage(chatID primitive.ObjectID, _ primitive.ObjectID) (bool, error) { +func (s *messageService) hasAccessToSendMessage(chatID primitive.ObjectID, staticID primitive.ObjectID) (bool, error) { foundChat, chatErr := s.chatRepo.FindByID(chatID) if chatErr != nil { return false, chatErr } - // if foundChat.ChatType == chat.ChatTypeDirect { - // hasSide := foundChat.ChatDetail.(*chat.DirectChatDetail).HasSide(staticID) - // return hasSide, nil - // } else if foundChat.ChatType == chat.ChatTypeChannel { - // admins := foundChat.ChatDetail.(*chat.ChannelChatDetail).Admins - // isAdmin := slices.Contains(admins, &staticID) - // return isAdmin, nil - // } else - if foundChat.ChatType == chat.TypeGroup { + if foundChat.ChatType == chat.TypeDirect { + hasSide := foundChat.ChatDetail.(*chat.DirectChatDetail).HasSide(staticID) + return hasSide, nil + } else if foundChat.ChatType == chat.TypeChannel { + admins := foundChat.ChatDetail.(*chat.ChannelChatDetail).Admins + isAdmin := slices.Contains(admins, staticID) + return isAdmin, nil + } else if foundChat.ChatType == chat.TypeGroup { log.Println(reflect.TypeOf(foundChat.ChatDetail).Name()) // members := foundChat.ChatDetail.(*chat.GroupChatDetail).Members // isMember := slices.Contains(members, &staticID) diff --git a/internal/service/user_service.go b/internal/service/user_service.go index e10aa58..a15f1b5 100644 --- a/internal/service/user_service.go +++ b/internal/service/user_service.go @@ -3,6 +3,7 @@ package service import ( "errors" "fmt" + "github.com/kavkaco/Kavka-Core/internal/domain/user" repository "github.com/kavkaco/Kavka-Core/internal/repository/user" "github.com/kavkaco/Kavka-Core/pkg/jwt_manager" @@ -16,7 +17,7 @@ type userService struct { SmsOtp *sms_service.SmsService } -func NewUserService(userRepo user.UserRepository, session *session.Session, smsOtp *sms_service.SmsService) user.UserService { +func NewUserService(userRepo user.UserRepository, session *session.Session, smsOtp *sms_service.SmsService) user.Service { return &userService{userRepo, session, smsOtp} }