diff --git a/docker-compose.yml b/docker-compose.yml index 57cd8e4..76f4545 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -56,6 +56,11 @@ services: MONGODB_NAME: arch_auth MONGODB_HOST: 'mongo' MONGODB_PORT: '27017' + RABBITMQ_HOST: "rabbitmq" + RABBITMQ_PORT: "5672" + RABBITMQ_USER: "guest" + RABBITMQ_PASSWORD: "guest" + RABBITMQ_EXCHANGE: "arch_monorepo" JWT_TOKEN_JTI: mnb23vcsrt756yuiomnbvcx98ertyuiop JWT_EXPIRED: 60 APP_URL: "http://domain.com" diff --git a/env/auth/.env.example b/env/auth/.env.example index 5a99a91..8bd1704 100644 --- a/env/auth/.env.example +++ b/env/auth/.env.example @@ -12,5 +12,11 @@ MONGODB_NAME="arch_auth" MONGODB_HOST="localhost" MONGODB_PORT="27017" +RABBITMQ_HOST="localhost" +RABBITMQ_PORT="5672" +RABBITMQ_USER="guest" +RABBITMQ_PASSWORD="guest" +RABBITMQ_EXCHANGE="arch_monorepo" + JWT_TOKEN_JTI="" JWT_EXPIRED=60 \ No newline at end of file diff --git a/go.mod b/go.mod index e0a3e06..2981322 100644 --- a/go.mod +++ b/go.mod @@ -1,8 +1,6 @@ module github.com/febrihidayan/go-architecture-monorepo -go 1.22.7 - -toolchain go1.23.3 +go 1.23 require ( bou.ke/monkey v1.0.2 diff --git a/pkg/rabbitmq/rabbitmq.go b/pkg/rabbitmq/rabbitmq.go index 6434c2c..24a3b60 100644 --- a/pkg/rabbitmq/rabbitmq.go +++ b/pkg/rabbitmq/rabbitmq.go @@ -2,6 +2,7 @@ package rabbitmq import ( "fmt" + "log" "github.com/rabbitmq/amqp091-go" ) @@ -11,15 +12,17 @@ type RabbitMQ struct { Channel *amqp091.Channel } +// NewRabbitMQ initializes a RabbitMQ connection and channel func NewRabbitMQ(url string) (*RabbitMQ, error) { conn, err := amqp091.Dial(url) if err != nil { - return nil, err + return nil, fmt.Errorf("failed to connect to RabbitMQ: %w", err) } ch, err := conn.Channel() if err != nil { - return nil, err + conn.Close() + return nil, fmt.Errorf("failed to open a channel: %w", err) } return &RabbitMQ{ @@ -28,39 +31,54 @@ func NewRabbitMQ(url string) (*RabbitMQ, error) { }, nil } +// Close closes the RabbitMQ connection and channel func (x *RabbitMQ) Close() { - x.Channel.Close() - x.Conn.Close() + if x.Channel != nil { + if err := x.Channel.Close(); err != nil { + log.Printf("failed to close channel: %v", err) + } + } + if x.Conn != nil { + if err := x.Conn.Close(); err != nil { + log.Printf("failed to close connection: %v", err) + } + } } -// DeclareExchange for deklarasi exchange +// DeclareExchange declares an exchange if it doesn't exist func (x *RabbitMQ) DeclareExchange(exchangeName, exchangeType string) error { return x.Channel.ExchangeDeclare( - exchangeName, exchangeType, true, false, false, false, nil, + exchangeName, // Exchange name + exchangeType, // Exchange type (e.g., "direct", "topic", "fanout") + true, // Durable + false, // Auto-deleted + false, // Internal + false, // No-wait + nil, // Arguments ) } -// Publish for publish message +// Publish sends a message to the specified exchange and routing key func (x *RabbitMQ) Publish(exchange, routingKey string, body []byte) error { return x.Channel.Publish( - exchange, - routingKey, - false, - false, + exchange, // Exchange name + routingKey, // Routing key + false, // Mandatory + false, // Immediate amqp091.Publishing{ - ContentType: "application/protobuf", + ContentType: "application/protobuf", // You can change this if needed Body: body, }, ) } -// SetupQueue binds multiple routing keys to a single queue using a slice +// SetupQueue declares a queue and binds it to multiple routing keys func (x *RabbitMQ) SetupQueue(queueName, exchangeName string, routingKeys []string) error { - // Declare queue + // Declare the queue _, err := x.Channel.QueueDeclare( - queueName, // Nama queue - true, // Durable - false, // Delete when unused + queueName, // Queue name + true, // Durable + false, // Auto-deleted false, // Exclusive false, // No-wait nil, // Arguments @@ -69,24 +87,32 @@ func (x *RabbitMQ) SetupQueue(queueName, exchangeName string, routingKeys []stri return fmt.Errorf("failed to declare queue: %w", err) } - // Looping routing keys dan binding ke queue + // Bind each routing key to the queue for _, routingKey := range routingKeys { - err = x.Channel.QueueBind( - queueName, // Nama queue + err := x.Channel.QueueBind( + queueName, // Queue name routingKey, // Routing key - exchangeName, // Nama exchange + exchangeName, // Exchange name false, // No-wait nil, // Arguments ) if err != nil { - return fmt.Errorf("failed to bind queue: %w", err) + return fmt.Errorf("failed to bind queue to exchange: %w", err) } } return nil } -// Consume for start consumer +// Consume starts consuming messages from the specified queue func (x *RabbitMQ) Consume(queueName string) (<-chan amqp091.Delivery, error) { - return x.Channel.Consume(queueName, "", true, false, false, false, nil) + return x.Channel.Consume( + queueName, // Queue name + "", // Consumer tag + true, // Auto-acknowledge + false, // Exclusive + false, // No-local + false, // No-wait + nil, // Arguments + ) } diff --git a/proto/_generated/auth/auth.pb.go b/proto/_generated/auth/auth.pb.go index eb4d6fc..4c4a10d 100644 --- a/proto/_generated/auth/auth.pb.go +++ b/proto/_generated/auth/auth.pb.go @@ -249,6 +249,51 @@ func (x *CreateOrUpdateAuthResponse) GetResponse() *RequestResponse { return nil } +type FindByIdRequest struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Id string `protobuf:"bytes,1,opt,name=id,proto3" json:"id,omitempty"` +} + +func (x *FindByIdRequest) Reset() { + *x = FindByIdRequest{} + mi := &file_auth_auth_proto_msgTypes[4] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *FindByIdRequest) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*FindByIdRequest) ProtoMessage() {} + +func (x *FindByIdRequest) ProtoReflect() protoreflect.Message { + mi := &file_auth_auth_proto_msgTypes[4] + if x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use FindByIdRequest.ProtoReflect.Descriptor instead. +func (*FindByIdRequest) Descriptor() ([]byte, []int) { + return file_auth_auth_proto_rawDescGZIP(), []int{4} +} + +func (x *FindByIdRequest) GetId() string { + if x != nil { + return x.Id + } + return "" +} + var File_auth_auth_proto protoreflect.FileDescriptor var file_auth_auth_proto_rawDesc = []byte{ @@ -282,22 +327,25 @@ var file_auth_auth_proto_rawDesc = []byte{ 0x73, 0x65, 0x12, 0x31, 0x0a, 0x08, 0x72, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x15, 0x2e, 0x61, 0x75, 0x74, 0x68, 0x2e, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x52, 0x08, 0x72, 0x65, 0x73, - 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x32, 0x67, 0x0a, 0x0c, 0x41, 0x75, 0x74, 0x68, 0x53, 0x65, 0x72, - 0x76, 0x69, 0x63, 0x65, 0x73, 0x12, 0x57, 0x0a, 0x12, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x4f, - 0x72, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x41, 0x75, 0x74, 0x68, 0x12, 0x1f, 0x2e, 0x61, 0x75, - 0x74, 0x68, 0x2e, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x4f, 0x72, 0x55, 0x70, 0x64, 0x61, 0x74, - 0x65, 0x41, 0x75, 0x74, 0x68, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x20, 0x2e, 0x61, - 0x75, 0x74, 0x68, 0x2e, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x4f, 0x72, 0x55, 0x70, 0x64, 0x61, - 0x74, 0x65, 0x41, 0x75, 0x74, 0x68, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x42, 0x87, - 0x01, 0x0a, 0x08, 0x63, 0x6f, 0x6d, 0x2e, 0x61, 0x75, 0x74, 0x68, 0x42, 0x09, 0x41, 0x75, 0x74, - 0x68, 0x50, 0x72, 0x6f, 0x74, 0x6f, 0x50, 0x01, 0x5a, 0x40, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, - 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x66, 0x65, 0x62, 0x72, 0x69, 0x68, 0x69, 0x64, 0x61, 0x79, 0x61, - 0x6e, 0x2f, 0x67, 0x6f, 0x2d, 0x61, 0x72, 0x63, 0x68, 0x69, 0x74, 0x65, 0x63, 0x74, 0x75, 0x72, - 0x65, 0x2d, 0x6d, 0x6f, 0x6e, 0x6f, 0x72, 0x65, 0x70, 0x6f, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, - 0x2f, 0x61, 0x75, 0x74, 0x68, 0x3b, 0x61, 0x75, 0x74, 0x68, 0xa2, 0x02, 0x03, 0x41, 0x58, 0x58, - 0xaa, 0x02, 0x04, 0x41, 0x75, 0x74, 0x68, 0xca, 0x02, 0x04, 0x41, 0x75, 0x74, 0x68, 0xe2, 0x02, - 0x10, 0x41, 0x75, 0x74, 0x68, 0x5c, 0x47, 0x50, 0x42, 0x4d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, - 0x61, 0xea, 0x02, 0x04, 0x41, 0x75, 0x74, 0x68, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, + 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x21, 0x0a, 0x0f, 0x46, 0x69, 0x6e, 0x64, 0x42, 0x79, 0x49, + 0x64, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x0e, 0x0a, 0x02, 0x69, 0x64, 0x18, 0x01, + 0x20, 0x01, 0x28, 0x09, 0x52, 0x02, 0x69, 0x64, 0x32, 0x67, 0x0a, 0x0c, 0x41, 0x75, 0x74, 0x68, + 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x73, 0x12, 0x57, 0x0a, 0x12, 0x43, 0x72, 0x65, 0x61, + 0x74, 0x65, 0x4f, 0x72, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x41, 0x75, 0x74, 0x68, 0x12, 0x1f, + 0x2e, 0x61, 0x75, 0x74, 0x68, 0x2e, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x4f, 0x72, 0x55, 0x70, + 0x64, 0x61, 0x74, 0x65, 0x41, 0x75, 0x74, 0x68, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, + 0x20, 0x2e, 0x61, 0x75, 0x74, 0x68, 0x2e, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x4f, 0x72, 0x55, + 0x70, 0x64, 0x61, 0x74, 0x65, 0x41, 0x75, 0x74, 0x68, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, + 0x65, 0x42, 0x87, 0x01, 0x0a, 0x08, 0x63, 0x6f, 0x6d, 0x2e, 0x61, 0x75, 0x74, 0x68, 0x42, 0x09, + 0x41, 0x75, 0x74, 0x68, 0x50, 0x72, 0x6f, 0x74, 0x6f, 0x50, 0x01, 0x5a, 0x40, 0x67, 0x69, 0x74, + 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x66, 0x65, 0x62, 0x72, 0x69, 0x68, 0x69, 0x64, + 0x61, 0x79, 0x61, 0x6e, 0x2f, 0x67, 0x6f, 0x2d, 0x61, 0x72, 0x63, 0x68, 0x69, 0x74, 0x65, 0x63, + 0x74, 0x75, 0x72, 0x65, 0x2d, 0x6d, 0x6f, 0x6e, 0x6f, 0x72, 0x65, 0x70, 0x6f, 0x2f, 0x70, 0x72, + 0x6f, 0x74, 0x6f, 0x2f, 0x61, 0x75, 0x74, 0x68, 0x3b, 0x61, 0x75, 0x74, 0x68, 0xa2, 0x02, 0x03, + 0x41, 0x58, 0x58, 0xaa, 0x02, 0x04, 0x41, 0x75, 0x74, 0x68, 0xca, 0x02, 0x04, 0x41, 0x75, 0x74, + 0x68, 0xe2, 0x02, 0x10, 0x41, 0x75, 0x74, 0x68, 0x5c, 0x47, 0x50, 0x42, 0x4d, 0x65, 0x74, 0x61, + 0x64, 0x61, 0x74, 0x61, 0xea, 0x02, 0x04, 0x41, 0x75, 0x74, 0x68, 0x62, 0x06, 0x70, 0x72, 0x6f, + 0x74, 0x6f, 0x33, } var ( @@ -312,17 +360,18 @@ func file_auth_auth_proto_rawDescGZIP() []byte { return file_auth_auth_proto_rawDescData } -var file_auth_auth_proto_msgTypes = make([]protoimpl.MessageInfo, 4) +var file_auth_auth_proto_msgTypes = make([]protoimpl.MessageInfo, 5) var file_auth_auth_proto_goTypes = []any{ (*RequestResponse)(nil), // 0: auth.RequestResponse (*Auth)(nil), // 1: auth.Auth (*CreateOrUpdateAuthRequest)(nil), // 2: auth.CreateOrUpdateAuthRequest (*CreateOrUpdateAuthResponse)(nil), // 3: auth.CreateOrUpdateAuthResponse - (*timestamppb.Timestamp)(nil), // 4: google.protobuf.Timestamp + (*FindByIdRequest)(nil), // 4: auth.FindByIdRequest + (*timestamppb.Timestamp)(nil), // 5: google.protobuf.Timestamp } var file_auth_auth_proto_depIdxs = []int32{ - 4, // 0: auth.Auth.created_at:type_name -> google.protobuf.Timestamp - 4, // 1: auth.Auth.deleted_at:type_name -> google.protobuf.Timestamp + 5, // 0: auth.Auth.created_at:type_name -> google.protobuf.Timestamp + 5, // 1: auth.Auth.deleted_at:type_name -> google.protobuf.Timestamp 1, // 2: auth.CreateOrUpdateAuthRequest.data:type_name -> auth.Auth 0, // 3: auth.CreateOrUpdateAuthResponse.response:type_name -> auth.RequestResponse 2, // 4: auth.AuthServices.CreateOrUpdateAuth:input_type -> auth.CreateOrUpdateAuthRequest @@ -345,7 +394,7 @@ func file_auth_auth_proto_init() { GoPackagePath: reflect.TypeOf(x{}).PkgPath(), RawDescriptor: file_auth_auth_proto_rawDesc, NumEnums: 0, - NumMessages: 4, + NumMessages: 5, NumExtensions: 0, NumServices: 1, }, diff --git a/proto/auth/auth.proto b/proto/auth/auth.proto index e83d3f9..71dce84 100644 --- a/proto/auth/auth.proto +++ b/proto/auth/auth.proto @@ -30,4 +30,8 @@ message CreateOrUpdateAuthRequest { message CreateOrUpdateAuthResponse { RequestResponse response = 1; +} + +message FindByIdRequest { + string id = 1; } \ No newline at end of file diff --git a/services/auth/cmd/main.go b/services/auth/cmd/main.go index 9fb6acf..1408f9e 100644 --- a/services/auth/cmd/main.go +++ b/services/auth/cmd/main.go @@ -10,94 +10,81 @@ import ( "os/signal" "syscall" - "github.com/febrihidayan/go-architecture-monorepo/services/auth/internal/config" - "github.com/febrihidayan/go-architecture-monorepo/services/auth/internal/delivery/grpc_client" "github.com/febrihidayan/go-architecture-monorepo/services/auth/internal/delivery/grpc_server" acl_handler "github.com/febrihidayan/go-architecture-monorepo/services/auth/internal/delivery/http/delivery/acl" auth_handler "github.com/febrihidayan/go-architecture-monorepo/services/auth/internal/delivery/http/delivery/auth" permision_handler "github.com/febrihidayan/go-architecture-monorepo/services/auth/internal/delivery/http/delivery/permission" role_handler "github.com/febrihidayan/go-architecture-monorepo/services/auth/internal/delivery/http/delivery/role" + "github.com/febrihidayan/go-architecture-monorepo/services/auth/internal/delivery/rabbitmq_server" "github.com/febrihidayan/go-architecture-monorepo/services/auth/internal/repositories/factories" - repository_mongo "github.com/febrihidayan/go-architecture-monorepo/services/auth/internal/repositories/mongo" "github.com/gorilla/mux" "google.golang.org/grpc" ) var ( - cfg = config.Auth() - ctx, cancel = context.WithCancel(context.Background()) - db = config.InitDatabaseMongodb() - authRepo = repository_mongo.NewAuthRepository(db) - permissionRepo = repository_mongo.NewPermissionRepository(db) - roleRepo = repository_mongo.NewRoleRepository(db) - roleUserRepo = repository_mongo.NewRoleUserRepository(db) - mongoFactory = factories.NewMongoFactory(db) + ctx, cancel = context.WithCancel(context.Background()) ) func main() { - defer func() { - db.Client().Disconnect(ctx) - }() - - // run rpc client - grpcClient, errs := grpc_client.NewGrpcClient(&cfg.GrpcClient) - if len(errs) > 0 { - cancel() - log.Fatalf("did not connect grpc client: %v", errs) - } + defer cancel() - // run Grpc Server - go RunGrpcServer() - // end run Grpc Server - - router := mux.NewRouter() - initHandler(router, cfg, grpcClient) - http.Handle("/", router) + // Initialize dependencies + deps := factories.InitializeDependencies() + defer deps.Close() - log.Println("Http Run on", cfg.HttpPort) - err := http.ListenAndServe(cfg.HttpPort, router) - if err != nil { - log.Fatal(err) - } + // Run HTTP Server + go RunHTTPServer(deps) - quit := make(chan os.Signal, 1) - signal.Notify(quit, os.Interrupt, syscall.SIGTERM) + // Run gRPC Server + go RunGrpcServer(deps) - select { - case v := <-quit: - log.Fatal(fmt.Sprintf("signal.Notify: %v", v)) - case done := <-ctx.Done(): - log.Fatal(fmt.Sprintf("ctx.Done: %v", done)) - } + // Run RabbitMQ Worker + go RunRabbitMQWorker(deps) - log.Println("Server Exited Properly") + // Handle graceful shutdown + HandleGracefulShutdown() } -func RunGrpcServer() { - +func RunGrpcServer(deps *factories.Dependencies) { grpcServer := grpc.NewServer() - grpc_server.HandlerAuthServices(grpcServer, db, *cfg) + grpc_server.HandlerAuthServices(grpcServer, deps) - lis, err := net.Listen("tcp", cfg.RpcPort) + lis, err := net.Listen("tcp", deps.Config.RpcPort) if err != nil { log.Fatalln("Failed to listen:", err) } go func() { - log.Println(fmt.Sprintf("Grpc Server listen to: %v", cfg.RpcPort)) + log.Println(fmt.Sprintf("Grpc Server listen to: %v", deps.Config.RpcPort)) log.Fatal(grpcServer.Serve(lis)) }() } -func initHandler( - router *mux.Router, - cfg *config.AuthConfig, - grpcClient *grpc_client.ServerClient) { +func RunRabbitMQWorker(deps *factories.Dependencies) { + server := rabbitmq_server.HandlerRabbitMQServices(deps, deps.RabbitMQConn) + server.Worker() + log.Println("RabbitMQ Worker started") +} + +func RunHTTPServer(deps *factories.Dependencies) { + router := mux.NewRouter() + + auth_handler.NewAuthHttpHandler(router, deps) + permision_handler.NewPermissionHttpHandler(router, deps) + role_handler.NewRoleHttpHandler(router, deps) + acl_handler.NewAclHttpHandler(router, deps) - grpcClientFactory := factories.NewGrpcFactory(grpcClient) + log.Printf("HTTP Server running on %s", deps.Config.HttpPort) + if err := http.ListenAndServe(deps.Config.HttpPort, router); err != nil { + log.Fatalf("HTTP Server stopped: %v", err) + } +} + +func HandleGracefulShutdown() { + quit := make(chan os.Signal, 1) + signal.Notify(quit, os.Interrupt, syscall.SIGTERM) - auth_handler.NewAuthHttpHandler(router, cfg, mongoFactory, grpcClientFactory) - permision_handler.NewPermissionHttpHandler(router, cfg, mongoFactory) - role_handler.NewRoleHttpHandler(router, cfg, mongoFactory) - acl_handler.NewAclHttpHandler(router, cfg, mongoFactory) + <-quit + log.Println("Shutting down gracefully...") + cancel() // Cancel the context for all components } diff --git a/services/auth/domain/entities/rabbitmq.go b/services/auth/domain/entities/rabbitmq.go new file mode 100644 index 0000000..e207a1c --- /dev/null +++ b/services/auth/domain/entities/rabbitmq.go @@ -0,0 +1,5 @@ +package entities + +const ( + RABBITMQ_AUTH_AUTH_DELETE = "auth.auth.delete" +) diff --git a/services/auth/domain/repositories/auth.go b/services/auth/domain/repositories/auth.go index 0c30757..8364e19 100644 --- a/services/auth/domain/repositories/auth.go +++ b/services/auth/domain/repositories/auth.go @@ -12,4 +12,5 @@ type AuthRepository interface { FindByEmail(ctx context.Context, email string) (*entities.Auth, error) FindByUserId(ctx context.Context, userId string) (*entities.Auth, error) Update(ctx context.Context, payload *entities.Auth) error + DeleteByUserID(ctx context.Context, userId string) error } diff --git a/services/auth/domain/usecases/auth.go b/services/auth/domain/usecases/auth.go index 938e1bf..e56d1d6 100644 --- a/services/auth/domain/usecases/auth.go +++ b/services/auth/domain/usecases/auth.go @@ -15,4 +15,5 @@ type AuthUsecase interface { SendEmailVerified(ctx context.Context, email string) *exceptions.CustomError PasswordEmail(ctx context.Context, email string) *exceptions.CustomError PasswordReset(ctx context.Context, payload entities.PasswordReset) *exceptions.CustomError + DeleteByUserID(ctx context.Context, userId string) *exceptions.CustomError } diff --git a/services/auth/internal/config/server.go b/services/auth/internal/config/server.go index c747450..92783e9 100644 --- a/services/auth/internal/config/server.go +++ b/services/auth/internal/config/server.go @@ -16,6 +16,7 @@ type AuthConfig struct { AppSecretKey string Timeout time.Duration GrpcClient GrpcClient + RabbitMQ RabbitMQConfig } type GrpcClient struct { @@ -23,6 +24,14 @@ type GrpcClient struct { Notification string } +type RabbitMQConfig struct { + Host string + Port string + User string + Password string + Exchange string +} + func Auth() *AuthConfig { return &AuthConfig{ HttpPort: os.Getenv("HTTP_PORT"), @@ -36,5 +45,12 @@ func Auth() *AuthConfig { User: os.Getenv("RPC_USER"), Notification: os.Getenv("RPC_NOTIFICATION"), }, + RabbitMQ: RabbitMQConfig{ + Host: os.Getenv("RABBITMQ_HOST"), + Port: os.Getenv("RABBITMQ_PORT"), + User: os.Getenv("RABBITMQ_USER"), + Password: os.Getenv("RABBITMQ_PASSWORD"), + Exchange: os.Getenv("RABBITMQ_EXCHANGE"), + }, } } diff --git a/services/auth/internal/delivery/grpc_server/handler.go b/services/auth/internal/delivery/grpc_server/handler.go index 9973d8b..72629c0 100644 --- a/services/auth/internal/delivery/grpc_server/handler.go +++ b/services/auth/internal/delivery/grpc_server/handler.go @@ -3,21 +3,13 @@ package grpc_server import ( authPb "github.com/febrihidayan/go-architecture-monorepo/proto/_generated/auth" - "github.com/febrihidayan/go-architecture-monorepo/services/auth/internal/config" - "github.com/febrihidayan/go-architecture-monorepo/services/auth/internal/delivery/grpc_client" "github.com/febrihidayan/go-architecture-monorepo/services/auth/internal/repositories/factories" "github.com/febrihidayan/go-architecture-monorepo/services/auth/internal/usecases/auth" - "go.mongodb.org/mongo-driver/mongo" "google.golang.org/grpc" ) -func HandlerAuthServices(s *grpc.Server, db *mongo.Database, cfg config.AuthConfig) { - mongoFactory := factories.NewMongoFactory(db) - - grpcClient, _ := grpc_client.NewGrpcClient(&cfg.GrpcClient) - grpcClientFactory := factories.NewGrpcFactory(grpcClient) - +func HandlerAuthServices(s *grpc.Server, deps *factories.Dependencies) { authPb.RegisterAuthServicesServer(s, &server{ - authUsecase: auth.NewAuthInteractor(&cfg, mongoFactory, grpcClientFactory), + authUsecase: auth.NewAuthInteractor(deps), }) } diff --git a/services/auth/internal/delivery/http/delivery/acl/handler.go b/services/auth/internal/delivery/http/delivery/acl/handler.go index d18b1b4..ac69392 100644 --- a/services/auth/internal/delivery/http/delivery/acl/handler.go +++ b/services/auth/internal/delivery/http/delivery/acl/handler.go @@ -16,15 +16,11 @@ type AclHttpHandler struct { func NewAclHttpHandler( r *mux.Router, - config *config.AuthConfig, - mongoFactory *factories.MongoFactory, + deps *factories.Dependencies, ) { handler := &AclHttpHandler{ - Cfg: config, - AclUsecase: acl.NewAclInteractor( - config, - mongoFactory, - ), + Cfg: deps.Config, + AclUsecase: acl.NewAclInteractor(deps), } r.HandleFunc("/v1/auth/acl/roles", handler.GetAllRole).Methods("GET") diff --git a/services/auth/internal/delivery/http/delivery/auth/handler.go b/services/auth/internal/delivery/http/delivery/auth/handler.go index bb0b6ea..9f09395 100644 --- a/services/auth/internal/delivery/http/delivery/auth/handler.go +++ b/services/auth/internal/delivery/http/delivery/auth/handler.go @@ -16,17 +16,11 @@ type AuthHttpHandler struct { func NewAuthHttpHandler( r *mux.Router, - config *config.AuthConfig, - mongoFactory *factories.MongoFactory, - grpcClientFactory *factories.GrpcClientFactory, + deps *factories.Dependencies, ) { handler := &AuthHttpHandler{ - Cfg: config, - AuthUsecase: auth.NewAuthInteractor( - config, - mongoFactory, - grpcClientFactory, - ), + Cfg: deps.Config, + AuthUsecase: auth.NewAuthInteractor(deps), } r.HandleFunc("/v1/auth/login", handler.Login).Methods("POST") diff --git a/services/auth/internal/delivery/http/delivery/permission/handler.go b/services/auth/internal/delivery/http/delivery/permission/handler.go index b3d98e5..2783a64 100644 --- a/services/auth/internal/delivery/http/delivery/permission/handler.go +++ b/services/auth/internal/delivery/http/delivery/permission/handler.go @@ -16,15 +16,11 @@ type PermissionHttpHandler struct { func NewPermissionHttpHandler( r *mux.Router, - config *config.AuthConfig, - mongoFactory *factories.MongoFactory, + deps *factories.Dependencies, ) { handler := &PermissionHttpHandler{ - Cfg: config, - PermissionUsecase: permission.NewPermissionInteractor( - config, - mongoFactory, - ), + Cfg: deps.Config, + PermissionUsecase: permission.NewPermissionInteractor(deps), } r.HandleFunc("/v1/auth/permissions", handler.GetAll).Methods("GET") diff --git a/services/auth/internal/delivery/http/delivery/role/handler.go b/services/auth/internal/delivery/http/delivery/role/handler.go index 558b977..4e2aa65 100644 --- a/services/auth/internal/delivery/http/delivery/role/handler.go +++ b/services/auth/internal/delivery/http/delivery/role/handler.go @@ -16,15 +16,11 @@ type RoleHttpHandler struct { func NewRoleHttpHandler( r *mux.Router, - config *config.AuthConfig, - mongoFactory *factories.MongoFactory, + deps *factories.Dependencies, ) { handler := &RoleHttpHandler{ - Cfg: config, - RoleUsecase: role.NewRoleInteractor( - config, - mongoFactory, - ), + Cfg: deps.Config, + RoleUsecase: role.NewRoleInteractor(deps), } r.HandleFunc("/v1/auth/roles", handler.GetAll).Methods("GET") diff --git a/services/auth/internal/delivery/rabbitmq_server/consumer/auth.go b/services/auth/internal/delivery/rabbitmq_server/consumer/auth.go new file mode 100644 index 0000000..6650232 --- /dev/null +++ b/services/auth/internal/delivery/rabbitmq_server/consumer/auth.go @@ -0,0 +1,27 @@ +package customer + +import ( + "context" + "log" + + "github.com/febrihidayan/go-architecture-monorepo/pkg/exceptions" + "github.com/febrihidayan/go-architecture-monorepo/pkg/rabbitmq" + authPb "github.com/febrihidayan/go-architecture-monorepo/proto/_generated/auth" + "google.golang.org/grpc/codes" + "google.golang.org/grpc/status" +) + +func (x *CustomerRabbitMQ) AuthDelete(ctx context.Context, body []byte) error { + var req authPb.FindByIdRequest + + if err := rabbitmq.UnmarshalProto(body, &req); err != nil { + log.Printf("AuthDelete#Failed to unmarshal: %v", err) + return err + } + + if err := x.authUsecase.DeleteByUserID(ctx, req.GetId()); err != nil { + return status.Error(codes.Code(exceptions.MapToHttpStatusCode(err.Status)), err.Errors.Error()) + } + + return nil +} diff --git a/services/auth/internal/delivery/rabbitmq_server/consumer/handler.go b/services/auth/internal/delivery/rabbitmq_server/consumer/handler.go new file mode 100644 index 0000000..f569aea --- /dev/null +++ b/services/auth/internal/delivery/rabbitmq_server/consumer/handler.go @@ -0,0 +1,20 @@ +package customer + +import ( + "github.com/febrihidayan/go-architecture-monorepo/pkg/rabbitmq" + "github.com/febrihidayan/go-architecture-monorepo/services/auth/domain/usecases" + "github.com/febrihidayan/go-architecture-monorepo/services/auth/internal/repositories/factories" + "github.com/febrihidayan/go-architecture-monorepo/services/auth/internal/usecases/auth" +) + +type CustomerRabbitMQ struct { + rmq *rabbitmq.RabbitMQ + authUsecase usecases.AuthUsecase +} + +func NewCustomerRabbitMQ(deps *factories.Dependencies, rmq *rabbitmq.RabbitMQ) *CustomerRabbitMQ { + return &CustomerRabbitMQ{ + rmq: rmq, + authUsecase: auth.NewAuthInteractor(deps), + } +} diff --git a/services/auth/internal/delivery/rabbitmq_server/handler.go b/services/auth/internal/delivery/rabbitmq_server/handler.go new file mode 100644 index 0000000..5ea6deb --- /dev/null +++ b/services/auth/internal/delivery/rabbitmq_server/handler.go @@ -0,0 +1,73 @@ +package rabbitmq_server + +import ( + "context" + "log" + + "github.com/febrihidayan/go-architecture-monorepo/pkg/rabbitmq" + "github.com/febrihidayan/go-architecture-monorepo/services/auth/domain/entities" + "github.com/febrihidayan/go-architecture-monorepo/services/auth/internal/config" + customer "github.com/febrihidayan/go-architecture-monorepo/services/auth/internal/delivery/rabbitmq_server/consumer" + "github.com/febrihidayan/go-architecture-monorepo/services/auth/internal/repositories/factories" +) + +type RabbitMQServer struct { + ctx context.Context + cfg *config.AuthConfig + rmq *rabbitmq.RabbitMQ + customer *customer.CustomerRabbitMQ +} + +func HandlerRabbitMQServices(deps *factories.Dependencies, rmq *rabbitmq.RabbitMQ) *RabbitMQServer { + + return &RabbitMQServer{ + ctx: context.Background(), + cfg: deps.Config, + rmq: rmq, + customer: customer.NewCustomerRabbitMQ(deps, rmq), + } +} + +func (x *RabbitMQServer) Worker() { + x.CustomerListen() +} + +func (x *RabbitMQServer) CustomerListen() { + err := x.rmq.DeclareExchange(x.cfg.RabbitMQ.Exchange, "direct") + if err != nil { + log.Fatalf("Failed to declare exchange: %v", err) + } + + routingKeys := []string{ + entities.RABBITMQ_AUTH_AUTH_DELETE, + } + + if err := x.rmq.SetupQueue("auth.queue", x.cfg.RabbitMQ.Exchange, routingKeys); err != nil { + log.Fatalf("Failed to setup auth queue: %v", err) + } + + msgs, err := x.rmq.Consume("auth.queue") + if err != nil { + log.Fatal(err) + } + + forever := make(chan bool) + + go func() { + for msg := range msgs { + log.Println("CustomerListen:", msg.RoutingKey) + + switch msg.RoutingKey { + case entities.RABBITMQ_AUTH_AUTH_DELETE: + if err := x.customer.AuthDelete(x.ctx, msg.Body); err != nil { + log.Println("CustomerListen#AuthDelete", err) + } + default: + log.Printf("Unknown event: %s", msg.RoutingKey) + } + } + }() + + log.Printf("RabbitMQ#Waiting for messages") + <-forever +} diff --git a/services/auth/internal/repositories/factories/init.go b/services/auth/internal/repositories/factories/init.go new file mode 100644 index 0000000..d598fd7 --- /dev/null +++ b/services/auth/internal/repositories/factories/init.go @@ -0,0 +1,87 @@ +package factories + +import ( + "fmt" + "log" + "sync" + + "github.com/febrihidayan/go-architecture-monorepo/pkg/rabbitmq" + "github.com/febrihidayan/go-architecture-monorepo/services/auth/internal/config" + "github.com/febrihidayan/go-architecture-monorepo/services/auth/internal/delivery/grpc_client" + "go.mongodb.org/mongo-driver/mongo" +) + +type Dependencies struct { + Config *config.AuthConfig + MongoDB *mongo.Database + GrpcClient *grpc_client.ServerClient + RabbitMQConn *rabbitmq.RabbitMQ + MongoFactory *MongoFactory + GrpcClientFactory *GrpcClientFactory +} + +var ( + dependencies *Dependencies + once sync.Once +) + +func InitializeDependencies() *Dependencies { + cfg := config.Auth() + + once.Do(func() { + log.Println("Initializing Dependencies...") + + // MongoDB + db := config.InitDatabaseMongodb() + log.Println("MongoDB Initialized") + + // gRPC Client + grpcClient, errs := grpc_client.NewGrpcClient(&cfg.GrpcClient) + if len(errs) > 0 { + log.Fatalf("Failed to initialize gRPC client: %v", errs) + } + + // RabbitMQ + dns := fmt.Sprintf( + "amqp://%s:%s@%s:%s/", + cfg.RabbitMQ.User, + cfg.RabbitMQ.Password, + cfg.RabbitMQ.Host, + cfg.RabbitMQ.Port, + ) + rmq, err := rabbitmq.NewRabbitMQ(dns) + if err != nil { + log.Fatalf("Failed to connect to RabbitMQ: %v", err) + } + + dependencies = &Dependencies{ + Config: cfg, + MongoDB: db, + GrpcClient: grpcClient, + RabbitMQConn: rmq, + MongoFactory: NewMongoFactory(db), + GrpcClientFactory: NewGrpcFactory(grpcClient), + } + }) + + return dependencies +} + +func GetDependencies() *Dependencies { + if dependencies == nil { + log.Fatal("Dependencies not initialized") + } + return dependencies +} + +func (x *Dependencies) Close() { + log.Println("Closing dependencies...") + if err := x.MongoDB.Client().Disconnect(nil); err != nil { + log.Printf("Failed to disconnect MongoDB: %v", err) + } + + x.RabbitMQConn.Close() + + // No explicit close for gRPC Client + log.Println("Dependencies closed") +} diff --git a/services/auth/internal/repositories/mongo/auth_repository.go b/services/auth/internal/repositories/mongo/auth_repository.go index d35bf3c..855fb03 100644 --- a/services/auth/internal/repositories/mongo/auth_repository.go +++ b/services/auth/internal/repositories/mongo/auth_repository.go @@ -80,3 +80,12 @@ func (x *AuthRepository) Update(ctx context.Context, payload *entities.Auth) err return nil } + +func (x *AuthRepository) DeleteByUserID(ctx context.Context, userId string) error { + _, err := x.db.DeleteOne(ctx, bson.M{"user_id": userId}) + if err != nil { + return err + } + + return nil +} diff --git a/services/auth/internal/usecases/acl/init.go b/services/auth/internal/usecases/acl/init.go index ce3ac3b..56eb92d 100644 --- a/services/auth/internal/usecases/acl/init.go +++ b/services/auth/internal/usecases/acl/init.go @@ -16,18 +16,15 @@ type aclInteractor struct { permissionUserRepo repositories.PermissionUserRepository } -func NewAclInteractor( - config *config.AuthConfig, - mongoFactory *factories.MongoFactory, -) *aclInteractor { +func NewAclInteractor(deps *factories.Dependencies) *aclInteractor { return &aclInteractor{ - cfg: config, - authRepo: mongoFactory.AuthRepo, - roleRepo: mongoFactory.RoleRepo, - roleUserRepo: mongoFactory.RoleUserRepo, - permissionRepo: mongoFactory.PermissionRepo, - permissionRoleRepo: mongoFactory.PermissionRoleRepo, - permissionUserRepo: mongoFactory.PermissionUserRepo, + cfg: deps.Config, + authRepo: deps.MongoFactory.AuthRepo, + roleRepo: deps.MongoFactory.RoleRepo, + roleUserRepo: deps.MongoFactory.RoleUserRepo, + permissionRepo: deps.MongoFactory.PermissionRepo, + permissionRoleRepo: deps.MongoFactory.PermissionRoleRepo, + permissionUserRepo: deps.MongoFactory.PermissionUserRepo, } } diff --git a/services/auth/internal/usecases/auth/delete_by_user_id.go b/services/auth/internal/usecases/auth/delete_by_user_id.go new file mode 100644 index 0000000..06d4724 --- /dev/null +++ b/services/auth/internal/usecases/auth/delete_by_user_id.go @@ -0,0 +1,61 @@ +package auth + +import ( + "context" + "log" + + "github.com/febrihidayan/go-architecture-monorepo/pkg/exceptions" + + "github.com/hashicorp/go-multierror" +) + +func (x *authInteractor) DeleteByUserID(ctx context.Context, userId string) *exceptions.CustomError { + var ( + multilerr *multierror.Error + ) + + log.Println("DeleteByUserID::info#1", "start check userId already") + auth, err := x.authRepo.FindByUserId(ctx, userId) + if err != nil { + log.Println("DeleteByUserID::error#1", err) + multilerr = multierror.Append(multilerr, err) + return &exceptions.CustomError{ + Status: exceptions.ERRBUSSINESS, + Errors: multilerr, + } + } + + log.Println("DeleteByUserID::info#2", "start deleting an existing user") + if err := x.authRepo.DeleteByUserID(ctx, auth.UserId); err != nil { + log.Println("DeleteByUserID::error#2", err) + multilerr = multierror.Append(multilerr, err) + return &exceptions.CustomError{ + Status: exceptions.ERRBUSSINESS, + Errors: multilerr, + } + } + + log.Println("DeleteByUserID::info#3", "start deleting an existing user role") + if err := x.roleUserRepo.DeleteByUserId(ctx, auth.UserId); err != nil { + log.Println("DeleteByUserID::error#3", err) + multilerr = multierror.Append(multilerr, err) + return &exceptions.CustomError{ + Status: exceptions.ERRBUSSINESS, + Errors: multilerr, + } + } + + log.Println("DeleteByUserID::info#4", "start deleting an existing user permission") + if err := x.permissionUserRepo.DeleteByUserId(ctx, auth.UserId); err != nil { + log.Println("DeleteByUserID::error#4", err) + multilerr = multierror.Append(multilerr, err) + return &exceptions.CustomError{ + Status: exceptions.ERRBUSSINESS, + Errors: multilerr, + } + } + + log.Println("DeleteByUserID::success#1", "deleted successfully") + + return nil +} diff --git a/services/auth/internal/usecases/auth/init.go b/services/auth/internal/usecases/auth/init.go index e2a3870..f47dbef 100644 --- a/services/auth/internal/usecases/auth/init.go +++ b/services/auth/internal/usecases/auth/init.go @@ -12,21 +12,19 @@ type authInteractor struct { userRepo repositories.UserRepository roleUserRepo repositories.RoleUserRepository roleRepo repositories.RoleRepository + permissionUserRepo repositories.PermissionUserRepository notificationGrpcRepo repositories.NotificationRepository } -func NewAuthInteractor( - config *config.AuthConfig, - mongoFactory *factories.MongoFactory, - grpcClientFactory *factories.GrpcClientFactory, -) *authInteractor { +func NewAuthInteractor(deps *factories.Dependencies) *authInteractor { return &authInteractor{ - cfg: config, - authRepo: mongoFactory.AuthRepo, - roleUserRepo: mongoFactory.RoleUserRepo, - roleRepo: mongoFactory.RoleRepo, - userRepo: grpcClientFactory.UserRepo, - notificationGrpcRepo: grpcClientFactory.NotificationRepo, + cfg: deps.Config, + authRepo: deps.MongoFactory.AuthRepo, + roleUserRepo: deps.MongoFactory.RoleUserRepo, + roleRepo: deps.MongoFactory.RoleRepo, + permissionUserRepo: deps.MongoFactory.PermissionUserRepo, + userRepo: deps.GrpcClientFactory.UserRepo, + notificationGrpcRepo: deps.GrpcClientFactory.NotificationRepo, } } diff --git a/services/auth/internal/usecases/permission/init.go b/services/auth/internal/usecases/permission/init.go index af3922d..d09808e 100644 --- a/services/auth/internal/usecases/permission/init.go +++ b/services/auth/internal/usecases/permission/init.go @@ -11,13 +11,9 @@ type permissionInteractor struct { permissionRepo repositories.PermissionRepository } -func NewPermissionInteractor( - config *config.AuthConfig, - mongoFactory *factories.MongoFactory, -) *permissionInteractor { - +func NewPermissionInteractor(deps *factories.Dependencies) *permissionInteractor { return &permissionInteractor{ - cfg: config, - permissionRepo: mongoFactory.PermissionRepo, + cfg: deps.Config, + permissionRepo: deps.MongoFactory.PermissionRepo, } } diff --git a/services/auth/internal/usecases/role/init.go b/services/auth/internal/usecases/role/init.go index 59fb323..74f22c7 100644 --- a/services/auth/internal/usecases/role/init.go +++ b/services/auth/internal/usecases/role/init.go @@ -11,13 +11,9 @@ type roleInteractor struct { roleRepo repositories.RoleRepository } -func NewRoleInteractor( - config *config.AuthConfig, - mongoFactory *factories.MongoFactory, -) *roleInteractor { - +func NewRoleInteractor(deps *factories.Dependencies) *roleInteractor { return &roleInteractor{ - cfg: config, - roleRepo: mongoFactory.RoleRepo, + cfg: deps.Config, + roleRepo: deps.MongoFactory.RoleRepo, } } diff --git a/services/auth/tests/internal/usecases/acl/init_test.go b/services/auth/tests/internal/usecases/acl/init_test.go index 4ed57d4..357b478 100644 --- a/services/auth/tests/internal/usecases/acl/init_test.go +++ b/services/auth/tests/internal/usecases/acl/init_test.go @@ -21,6 +21,7 @@ import ( type AclUsecaseSuite struct { suite.Suite cfg *config.AuthConfig + deps *factories.Dependencies mongoFactory *factories.MongoFactory authRepo *mongo_repositories.AuthRepositoryMock permissionRepo *mongo_repositories.PermissionRepositoryMock @@ -58,7 +59,13 @@ func (x *AclUsecaseSuite) SetupTest() { RoleRepo: x.roleRepo, } - x.aclUsecase = acl.NewAclInteractor(x.cfg, x.mongoFactory) + // Initialize dependencies + x.deps = &factories.Dependencies{ + Config: x.cfg, + MongoFactory: x.mongoFactory, + } + + x.aclUsecase = acl.NewAclInteractor(x.deps) // fake time now for testing monkey.Patch(time.Now, func() time.Time { diff --git a/services/auth/tests/internal/usecases/auth/init_test.go b/services/auth/tests/internal/usecases/auth/init_test.go index ec36099..78e7da2 100644 --- a/services/auth/tests/internal/usecases/auth/init_test.go +++ b/services/auth/tests/internal/usecases/auth/init_test.go @@ -17,6 +17,7 @@ import ( type AuthUsecaseSuite struct { suite.Suite cfg *config.AuthConfig + deps *factories.Dependencies mongoFactory *factories.MongoFactory grpcClientFactory *factories.GrpcClientFactory authRepo *mongo_repositories.AuthRepositoryMock @@ -44,7 +45,14 @@ func (x *AuthUsecaseSuite) SetupTest() { UserRepo: x.userRepo, } - x.authUsecase = auth.NewAuthInteractor(x.cfg, x.mongoFactory, x.grpcClientFactory) + // Initialize dependencies + x.deps = &factories.Dependencies{ + Config: x.cfg, + MongoFactory: x.mongoFactory, + GrpcClientFactory: x.grpcClientFactory, + } + + x.authUsecase = auth.NewAuthInteractor(x.deps) // fake time now for testing monkey.Patch(time.Now, func() time.Time { diff --git a/services/auth/tests/internal/usecases/permission/init_test.go b/services/auth/tests/internal/usecases/permission/init_test.go index 361b2c3..1df3cab 100644 --- a/services/auth/tests/internal/usecases/permission/init_test.go +++ b/services/auth/tests/internal/usecases/permission/init_test.go @@ -16,6 +16,7 @@ import ( type PermissionUsecaseSuite struct { suite.Suite cfg *config.AuthConfig + deps *factories.Dependencies mongoFactory *factories.MongoFactory permissionRepo *mongo_repositories.PermissionRepositoryMock permissionUsecase usecases.PermissionUsecase @@ -30,7 +31,13 @@ func (x *PermissionUsecaseSuite) SetupTest() { PermissionRepo: x.permissionRepo, } - x.permissionUsecase = permission.NewPermissionInteractor(x.cfg, x.mongoFactory) + // Initialize dependencies + x.deps = &factories.Dependencies{ + Config: x.cfg, + MongoFactory: x.mongoFactory, + } + + x.permissionUsecase = permission.NewPermissionInteractor(x.deps) // fake time now for testing monkey.Patch(time.Now, func() time.Time { diff --git a/services/auth/tests/internal/usecases/role/init_test.go b/services/auth/tests/internal/usecases/role/init_test.go index 390c9c7..15a3b33 100644 --- a/services/auth/tests/internal/usecases/role/init_test.go +++ b/services/auth/tests/internal/usecases/role/init_test.go @@ -16,6 +16,7 @@ import ( type RoleUsecaseSuite struct { suite.Suite cfg *config.AuthConfig + deps *factories.Dependencies mongoFactory *factories.MongoFactory roleRepo *mongo_repositories.RoleRepositoryMock roleUsecase usecases.RoleUsecase @@ -30,7 +31,13 @@ func (x *RoleUsecaseSuite) SetupTest() { RoleRepo: x.roleRepo, } - x.roleUsecase = role.NewRoleInteractor(x.cfg, x.mongoFactory) + // Initialize dependencies + x.deps = &factories.Dependencies{ + Config: x.cfg, + MongoFactory: x.mongoFactory, + } + + x.roleUsecase = role.NewRoleInteractor(x.deps) // fake time now for testing monkey.Patch(time.Now, func() time.Time { diff --git a/services/auth/tests/mocks/repositories/mongo/auth_repository_mock.go b/services/auth/tests/mocks/repositories/mongo/auth_repository_mock.go index cdde30c..6e19fd9 100644 --- a/services/auth/tests/mocks/repositories/mongo/auth_repository_mock.go +++ b/services/auth/tests/mocks/repositories/mongo/auth_repository_mock.go @@ -72,3 +72,13 @@ func (x *AuthRepositoryMock) Update(ctx context.Context, payload *entities.Auth) return } + +func (x *AuthRepositoryMock) DeleteByUserID(ctx context.Context, userId string) (err error) { + args := x.Called(userId) + + if n, ok := args.Get(0).(error); ok { + err = n + } + + return +} diff --git a/services/auth/tests/mocks/usecases/auth_mock.go b/services/auth/tests/mocks/usecases/auth_mock.go index d449e3a..265efc6 100644 --- a/services/auth/tests/mocks/usecases/auth_mock.go +++ b/services/auth/tests/mocks/usecases/auth_mock.go @@ -93,3 +93,13 @@ func (x *AuthUsecaseMock) PasswordReset(ctx context.Context, payload entities.Pa return } + +func (x *AuthUsecaseMock) DeleteByUserID(ctx context.Context, userId string) (err *exceptions.CustomError) { + args := x.Called(userId) + + if n, ok := args.Get(0).(*exceptions.CustomError); ok { + err = n + } + + return +} diff --git a/services/user/cmd/main.go b/services/user/cmd/main.go index dd0437e..99708ad 100644 --- a/services/user/cmd/main.go +++ b/services/user/cmd/main.go @@ -115,5 +115,5 @@ func initHandler( grpcFactory := factories.NewGrpcFactory(grpcClient) profile_handler.NewProfileHttpHandler(router, cfg, mongoFactory, rabbitmq) - user_handler.NewUserHttpHandler(router, cfg, mongoFactory, grpcFactory) + user_handler.NewUserHttpHandler(router, cfg, mongoFactory, grpcFactory, rabbitmq) } diff --git a/services/user/domain/entities/rabbitmq.go b/services/user/domain/entities/rabbitmq.go index 216ed49..3827f62 100644 --- a/services/user/domain/entities/rabbitmq.go +++ b/services/user/domain/entities/rabbitmq.go @@ -3,4 +3,5 @@ package entities const ( RABBITMQ_STORAGE_CLOUD_UPDATE = "storage.cloud.update" RABBITMQ_STORAGE_CLOUD_DELETE = "storage.cloud.delete" + RABBITMQ_AUTH_AUTH_DELETE = "auth.auth.delete" ) diff --git a/services/user/domain/repositories/rabbitmq.go b/services/user/domain/repositories/rabbitmq.go index ed640c4..31d2aa7 100644 --- a/services/user/domain/repositories/rabbitmq.go +++ b/services/user/domain/repositories/rabbitmq.go @@ -2,4 +2,5 @@ package repositories type RabbitMQRepository interface { CloudApprove(url []string, _type string) error + AuthDelete(id string) error } diff --git a/services/user/internal/delivery/grpc_server/handler.go b/services/user/internal/delivery/grpc_server/handler.go index 6231878..6c889d8 100644 --- a/services/user/internal/delivery/grpc_server/handler.go +++ b/services/user/internal/delivery/grpc_server/handler.go @@ -20,7 +20,7 @@ func HandlerUserServices(s *grpc.Server, db *mongo.Database, cfg config.UserConf grpcClientFactory := factories.NewGrpcFactory(grpcClient) userPb.RegisterUserServicesServer(s, &server{ - userUsecase: user.NewUserInteractor(&cfg, mongoFactory, grpcClientFactory), + userUsecase: user.NewUserInteractor(&cfg, mongoFactory, grpcClientFactory, nil), profileUsecase: profile.NewProfileInteractor(&cfg, mongoFactory, nil), }) } diff --git a/services/user/internal/delivery/http/delivery/user/handler.go b/services/user/internal/delivery/http/delivery/user/handler.go index 43762f4..82b8535 100644 --- a/services/user/internal/delivery/http/delivery/user/handler.go +++ b/services/user/internal/delivery/http/delivery/user/handler.go @@ -3,6 +3,7 @@ package user_handler import ( "github.com/febrihidayan/go-architecture-monorepo/services/user/domain/usecases" "github.com/febrihidayan/go-architecture-monorepo/services/user/internal/config" + "github.com/febrihidayan/go-architecture-monorepo/services/user/internal/delivery/rabbitmq_server/publisher" "github.com/febrihidayan/go-architecture-monorepo/services/user/internal/repositories/factories" "github.com/febrihidayan/go-architecture-monorepo/services/user/internal/usecases/user" @@ -19,13 +20,14 @@ func NewUserHttpHandler( config *config.UserConfig, mongoFactory *factories.MongoFactory, grpcFactory *factories.GrpcClientFactory, -) { + rabbitmq *publisher.PublisherRabbitMQ) { handler := &UserHttpHandler{ Cfg: config, UserUsecase: user.NewUserInteractor( config, mongoFactory, grpcFactory, + rabbitmq, ), } diff --git a/services/user/internal/delivery/rabbitmq_server/publisher/auth.go b/services/user/internal/delivery/rabbitmq_server/publisher/auth.go new file mode 100644 index 0000000..8a90588 --- /dev/null +++ b/services/user/internal/delivery/rabbitmq_server/publisher/auth.go @@ -0,0 +1,26 @@ +package publisher + +import ( + "log" + + "github.com/febrihidayan/go-architecture-monorepo/pkg/rabbitmq" + authPb "github.com/febrihidayan/go-architecture-monorepo/proto/_generated/auth" + "github.com/febrihidayan/go-architecture-monorepo/services/user/domain/entities" +) + +func (x *PublisherRabbitMQ) AuthDelete(id string) error { + body, err := rabbitmq.MarshalProto(&authPb.FindByIdRequest{ + Id: id, + }) + if err != nil { + log.Fatalf("AuthDelete#1Failed to marshal message: %v", err) + return err + } + + err = x.rmq.Publish(x.cfg.RabbitMQ.Exchange, entities.RABBITMQ_AUTH_AUTH_DELETE, body) + if err != nil { + log.Fatalf("AuthDelete#2Failed to publish message: %v", err) + } + + return err +} diff --git a/services/user/internal/usecases/user/delete.go b/services/user/internal/usecases/user/delete.go index 9a5e494..c5d9f38 100644 --- a/services/user/internal/usecases/user/delete.go +++ b/services/user/internal/usecases/user/delete.go @@ -19,5 +19,13 @@ func (x *userInteractor) Delete(ctx context.Context, id string) *exceptions.Cust } } + if err := x.rabbitmqRepo.AuthDelete(id); err != nil { + multilerr = multierror.Append(multilerr, err) + return &exceptions.CustomError{ + Status: exceptions.ERRBUSSINESS, + Errors: multilerr, + } + } + return nil } diff --git a/services/user/internal/usecases/user/init.go b/services/user/internal/usecases/user/init.go index 61308c8..23c6d0d 100644 --- a/services/user/internal/usecases/user/init.go +++ b/services/user/internal/usecases/user/init.go @@ -3,6 +3,7 @@ package user import ( "github.com/febrihidayan/go-architecture-monorepo/services/user/domain/repositories" "github.com/febrihidayan/go-architecture-monorepo/services/user/internal/config" + "github.com/febrihidayan/go-architecture-monorepo/services/user/internal/delivery/rabbitmq_server/publisher" "github.com/febrihidayan/go-architecture-monorepo/services/user/internal/repositories/factories" ) @@ -11,12 +12,14 @@ type userInteractor struct { userRepo repositories.UserRepository authGrpcRepo repositories.AuthRepository storageGrpcRepo repositories.StorageRepository + rabbitmqRepo repositories.RabbitMQRepository } func NewUserInteractor( config *config.UserConfig, mongoFactory *factories.MongoFactory, grpcFactory *factories.GrpcClientFactory, + rabbitmq *publisher.PublisherRabbitMQ, ) *userInteractor { return &userInteractor{ @@ -24,5 +27,6 @@ func NewUserInteractor( userRepo: mongoFactory.UserRepo, authGrpcRepo: grpcFactory.AuthRepo, storageGrpcRepo: grpcFactory.StorageRepo, + rabbitmqRepo: rabbitmq, } } diff --git a/services/user/tests/internal/usecases/user/init_test.go b/services/user/tests/internal/usecases/user/init_test.go index c9c4126..6dac01f 100644 --- a/services/user/tests/internal/usecases/user/init_test.go +++ b/services/user/tests/internal/usecases/user/init_test.go @@ -41,7 +41,7 @@ func (x *UserUsecaseSuite) SetupTest() { StorageRepo: x.storageGrpcRepo, } - x.userUsecase = user.NewUserInteractor(x.cfg, x.mongoFactory, x.grpcFactory) + x.userUsecase = user.NewUserInteractor(x.cfg, x.mongoFactory, x.grpcFactory, nil) // fake time now for testing monkey.Patch(time.Now, func() time.Time {