diff --git a/common/mmongo/mongo.go b/common/mmongo/mongo.go index 8e994a6f..5132ce25 100644 --- a/common/mmongo/mongo.go +++ b/common/mmongo/mongo.go @@ -17,7 +17,7 @@ type MongoConnection struct { Logger mlog.Logger } -// Connect keeps a singleton connection with postgres. +// Connect keeps a singleton connection with mongodb. func (mc *MongoConnection) Connect(ctx context.Context) error { mc.Logger.Info("Connecting to mongodb...") diff --git a/common/mredis/redis.go b/common/mredis/redis.go new file mode 100644 index 00000000..d9274801 --- /dev/null +++ b/common/mredis/redis.go @@ -0,0 +1,64 @@ +package mredis + +import ( + "context" + "go.uber.org/zap" + + "github.com/LerianStudio/midaz/common/mlog" + "github.com/redis/go-redis/v9" +) + +const RedisTTL = 300 + +// RedisConnection is a hub which deal with redis connections. +type RedisConnection struct { + Addr string + User string + Password string + DB int + Protocol int + Client *redis.Client + Connected bool + Logger mlog.Logger +} + +// Connect keeps a singleton connection with redis. +func (rc *RedisConnection) Connect(ctx context.Context) error { + rc.Logger.Info("Connecting to redis...") + + rdb := redis.NewClient(&redis.Options{ + Addr: rc.Addr, + Password: rc.Password, + DB: rc.DB, + Protocol: rc.Protocol, + }) + + _, err := rdb.Ping(ctx).Result() + if err != nil { + rc.Logger.Infof("RedisConnection.Ping %v", + zap.Error(err)) + + return err + } + + rc.Logger.Info("Connected to redis ✅ \n") + + rc.Connected = true + + rc.Client = rdb + + return nil +} + +// GetClient returns a pointer to the redis connection, initializing it if necessary. +func (rc *RedisConnection) GetClient(ctx context.Context) (*redis.Client, error) { + if rc.Client == nil { + err := rc.Connect(ctx) + if err != nil { + rc.Logger.Infof("ERRCONECT %s", err) + return nil, err + } + } + + return rc.Client, nil +} diff --git a/components/infra/.env.example b/components/infra/.env.example index 550061d1..0c1fa3dd 100644 --- a/components/infra/.env.example +++ b/components/infra/.env.example @@ -22,7 +22,10 @@ MONGO_PASSWORD=lerian MONGO_PORT=5703 # REDIS +REDIS_HOST=redis REDIS_PORT=5704 +REDIS_USER=midaz +REDIS_PASSWORD=lerian # RABBITMQ RABBITMQ_PORT_HOST=5672 diff --git a/components/infra/docker-compose.yml b/components/infra/docker-compose.yml index 43292b46..87121173 100644 --- a/components/infra/docker-compose.yml +++ b/components/infra/docker-compose.yml @@ -47,8 +47,15 @@ services: redis: <<: *redis-common container_name: midaz-redis + environment: + - REDIS_USER=${REDIS_USER} + - REDIS_PASSWORD=${REDIS_PASSWORD} + command: ["redis-server", "--requirepass", "${REDIS_PASSWORD}", "--user", "${REDIS_USER}", "--port ${REDIS_PORT}"] ports: - ${REDIS_PORT}:${REDIS_PORT} + volumes: + - redis-data:/data + restart: always primary-ledger: <<: *postgres-ledger-common @@ -133,6 +140,7 @@ services: volumes: mongodb_data_container: + redis-data: networks: infra_network: diff --git a/components/ledger/.env.example b/components/ledger/.env.example index 200fd799..ebcf1476 100644 --- a/components/ledger/.env.example +++ b/components/ledger/.env.example @@ -32,7 +32,10 @@ MONGO_PASSWORD=lerian MONGO_PORT=5703 # REDIS +REDIS_HOST=redis REDIS_PORT=5704 +REDIS_USER=midaz +REDIS_PASSWORD=lerian # LOG LEVEL LOG_LEVEL=debug diff --git a/components/ledger/internal/adapters/database/redis/consumer.redis.go b/components/ledger/internal/adapters/database/redis/consumer.redis.go new file mode 100644 index 00000000..8433d4d3 --- /dev/null +++ b/components/ledger/internal/adapters/database/redis/consumer.redis.go @@ -0,0 +1,65 @@ +package redis + +import ( + "context" + "time" + + "github.com/LerianStudio/midaz/common" + "github.com/LerianStudio/midaz/common/mopentelemetry" + "github.com/LerianStudio/midaz/common/mredis" +) + +// RedisConsumerRepository is a Redis implementation of the Redis consumer. +type RedisConsumerRepository struct { + conn *mredis.RedisConnection +} + +// NewConsumerRedis returns a new instance of RedisRepository using the given Redis connection. +func NewConsumerRedis(rc *mredis.RedisConnection) *RedisConsumerRepository { + r := &RedisConsumerRepository{ + conn: rc, + } + if _, err := r.conn.GetClient(context.Background()); err != nil { + panic("Failed to connect on redis") + } + + return r +} + +func (rr *RedisConsumerRepository) Set(ctx context.Context, key, value string, ttl time.Duration) error { + logger := common.NewLoggerFromContext(ctx) + tracer := common.NewTracerFromContext(ctx) + + ctx, span := tracer.Start(ctx, "redis.set") + defer span.End() + + rds, err := rr.conn.GetClient(ctx) + if err != nil { + mopentelemetry.HandleSpanError(&span, "Failed to get redis", err) + + return err + } + + if ttl <= 0 { + ttl = mredis.RedisTTL + } + + logger.Infof("value of ttl: %v", ttl) + + statusCMD := rds.Set(ctx, key, value, ttl) + if statusCMD.Err() != nil { + mopentelemetry.HandleSpanError(&span, "Failed to set on redis", statusCMD.Err()) + + return statusCMD.Err() + } + + return nil +} + +func (rr *RedisConsumerRepository) Get(ctx context.Context, key string) error { + return nil +} + +func (rr *RedisConsumerRepository) Del(ctx context.Context, key string) error { + return nil +} diff --git a/components/ledger/internal/app/command/command.go b/components/ledger/internal/app/command/command.go index fa14cd83..837000b8 100644 --- a/components/ledger/internal/app/command/command.go +++ b/components/ledger/internal/app/command/command.go @@ -9,6 +9,7 @@ import ( p "github.com/LerianStudio/midaz/components/ledger/internal/domain/portfolio/portfolio" r "github.com/LerianStudio/midaz/components/ledger/internal/domain/portfolio/product" rmq "github.com/LerianStudio/midaz/components/ledger/internal/domain/rabbitmq" + rds "github.com/LerianStudio/midaz/components/ledger/internal/domain/redis" ) // UseCase is a struct that aggregates various repositories for simplified access in use case implementations. @@ -36,4 +37,7 @@ type UseCase struct { // RabbitMQRepo provides an abstraction on top of the producer rabbitmq. RabbitMQRepo rmq.ProducerRepository + + // RedisRepo provides an abstraction on top of the redis consumer. + RedisRepo rds.RedisRepository } diff --git a/components/ledger/internal/app/query/query.go b/components/ledger/internal/app/query/query.go index 3f21aaac..8532651c 100644 --- a/components/ledger/internal/app/query/query.go +++ b/components/ledger/internal/app/query/query.go @@ -9,6 +9,7 @@ import ( p "github.com/LerianStudio/midaz/components/ledger/internal/domain/portfolio/portfolio" r "github.com/LerianStudio/midaz/components/ledger/internal/domain/portfolio/product" rmq "github.com/LerianStudio/midaz/components/ledger/internal/domain/rabbitmq" + rds "github.com/LerianStudio/midaz/components/ledger/internal/domain/redis" ) // UseCase is a struct that aggregates various repositories for simplified access in use case implementations. @@ -36,4 +37,7 @@ type UseCase struct { // RabbitMQRepo provides an abstraction on top of the consumer rabbitmq. RabbitMQRepo rmq.ConsumerRepository + + // RedisRepo provides an abstraction on top of the redis consumer. + RedisRepo rds.RedisRepository } diff --git a/components/ledger/internal/domain/redis/redis_repository.go b/components/ledger/internal/domain/redis/redis_repository.go new file mode 100644 index 00000000..858006e8 --- /dev/null +++ b/components/ledger/internal/domain/redis/redis_repository.go @@ -0,0 +1,15 @@ +package redis + +import ( + "context" + "time" +) + +// RedisRepository provides an interface for redis. +// +//go:generate mockgen --destination=../../gen/mock/redis/redis_repository_mock.go --package=mock . RedisRepository +type RedisRepository interface { + Set(ctx context.Context, key, value string, ttl time.Duration) error + Get(ctx context.Context, key string) error + Del(ctx context.Context, key string) error +} diff --git a/components/ledger/internal/gen/inject.go b/components/ledger/internal/gen/inject.go index fc37298c..d44f7137 100644 --- a/components/ledger/internal/gen/inject.go +++ b/components/ledger/internal/gen/inject.go @@ -14,9 +14,11 @@ import ( "github.com/LerianStudio/midaz/common/mopentelemetry" "github.com/LerianStudio/midaz/common/mpostgres" "github.com/LerianStudio/midaz/common/mrabbitmq" + "github.com/LerianStudio/midaz/common/mredis" "github.com/LerianStudio/midaz/common/mzap" "github.com/LerianStudio/midaz/components/ledger/internal/adapters/database/mongodb" "github.com/LerianStudio/midaz/components/ledger/internal/adapters/database/postgres" + "github.com/LerianStudio/midaz/components/ledger/internal/adapters/database/redis" rabbitmq "github.com/LerianStudio/midaz/components/ledger/internal/adapters/rabbitmq" "github.com/LerianStudio/midaz/components/ledger/internal/app/command" "github.com/LerianStudio/midaz/components/ledger/internal/app/query" @@ -28,6 +30,7 @@ import ( "github.com/LerianStudio/midaz/components/ledger/internal/domain/portfolio/portfolio" "github.com/LerianStudio/midaz/components/ledger/internal/domain/portfolio/product" r "github.com/LerianStudio/midaz/components/ledger/internal/domain/rabbitmq" + rds "github.com/LerianStudio/midaz/components/ledger/internal/domain/redis" portsGRPC "github.com/LerianStudio/midaz/components/ledger/internal/ports/grpc" portsHTTP "github.com/LerianStudio/midaz/components/ledger/internal/ports/http" "github.com/LerianStudio/midaz/components/ledger/internal/service" @@ -110,6 +113,19 @@ func setupTelemetryProviders(cfg *service.Config) *mopentelemetry.Telemetry { return t } +func setupRedisConnection(cfg *service.Config, log mlog.Logger) *mredis.RedisConnection { + connStrSource := fmt.Sprintf("%s:%s", cfg.RedisHost, cfg.RedisPort) + + return &mredis.RedisConnection{ + Addr: connStrSource, + User: cfg.RedisUser, + Password: cfg.RedisPassword, + DB: 0, + Protocol: 3, + Logger: log, + } +} + var ( serviceSet = wire.NewSet( common.InitLocalEnvConfig, @@ -119,6 +135,7 @@ var ( setupMongoDBConnection, setupCasdoorConnection, setupRabbitMQConnection, + setupRedisConnection, portsGRPC.NewRouterGRPC, service.NewServerGRPC, portsHTTP.NewRouter, @@ -133,6 +150,7 @@ var ( mongodb.NewMetadataMongoDBRepository, rabbitmq.NewProducerRabbitMQ, rabbitmq.NewConsumerRabbitMQ, + redis.NewConsumerRedis, wire.Struct(new(portsHTTP.OrganizationHandler), "*"), wire.Struct(new(portsHTTP.LedgerHandler), "*"), wire.Struct(new(portsHTTP.AssetHandler), "*"), @@ -150,6 +168,7 @@ var ( wire.Bind(new(metadata.Repository), new(*mongodb.MetadataMongoDBRepository)), wire.Bind(new(r.ConsumerRepository), new(*rabbitmq.ConsumerRabbitMQRepository)), wire.Bind(new(r.ProducerRepository), new(*rabbitmq.ProducerRabbitMQRepository)), + wire.Bind(new(rds.RedisRepository), new(*redis.RedisConsumerRepository)), ) svcSet = wire.NewSet( diff --git a/components/ledger/internal/gen/mock/redis/redis_repository_mock.go b/components/ledger/internal/gen/mock/redis/redis_repository_mock.go new file mode 100644 index 00000000..b4b86dbb --- /dev/null +++ b/components/ledger/internal/gen/mock/redis/redis_repository_mock.go @@ -0,0 +1,83 @@ +// Code generated by MockGen. DO NOT EDIT. +// Source: github.com/LerianStudio/midaz/components/ledger/internal/domain/redis (interfaces: RedisRepository) +// +// Generated by this command: +// +// mockgen --destination=../../gen/mock/redis/redis_repository_mock.go --package=mock . RedisRepository +// + +// Package mock is a generated GoMock package. +package mock + +import ( + context "context" + reflect "reflect" + time "time" + + gomock "go.uber.org/mock/gomock" +) + +// MockRedisRepository is a mock of RedisRepository interface. +type MockRedisRepository struct { + ctrl *gomock.Controller + recorder *MockRedisRepositoryMockRecorder +} + +// MockRedisRepositoryMockRecorder is the mock recorder for MockRedisRepository. +type MockRedisRepositoryMockRecorder struct { + mock *MockRedisRepository +} + +// NewMockRedisRepository creates a new mock instance. +func NewMockRedisRepository(ctrl *gomock.Controller) *MockRedisRepository { + mock := &MockRedisRepository{ctrl: ctrl} + mock.recorder = &MockRedisRepositoryMockRecorder{mock} + return mock +} + +// EXPECT returns an object that allows the caller to indicate expected use. +func (m *MockRedisRepository) EXPECT() *MockRedisRepositoryMockRecorder { + return m.recorder +} + +// Del mocks base method. +func (m *MockRedisRepository) Del(arg0 context.Context, arg1 string) error { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "Del", arg0, arg1) + ret0, _ := ret[0].(error) + return ret0 +} + +// Del indicates an expected call of Del. +func (mr *MockRedisRepositoryMockRecorder) Del(arg0, arg1 any) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Del", reflect.TypeOf((*MockRedisRepository)(nil).Del), arg0, arg1) +} + +// Get mocks base method. +func (m *MockRedisRepository) Get(arg0 context.Context, arg1 string) error { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "Get", arg0, arg1) + ret0, _ := ret[0].(error) + return ret0 +} + +// Get indicates an expected call of Get. +func (mr *MockRedisRepositoryMockRecorder) Get(arg0, arg1 any) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Get", reflect.TypeOf((*MockRedisRepository)(nil).Get), arg0, arg1) +} + +// Set mocks base method. +func (m *MockRedisRepository) Set(arg0 context.Context, arg1, arg2 string, arg3 time.Duration) error { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "Set", arg0, arg1, arg2, arg3) + ret0, _ := ret[0].(error) + return ret0 +} + +// Set indicates an expected call of Set. +func (mr *MockRedisRepositoryMockRecorder) Set(arg0, arg1, arg2, arg3 any) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Set", reflect.TypeOf((*MockRedisRepository)(nil).Set), arg0, arg1, arg2, arg3) +} diff --git a/components/ledger/internal/gen/wire_gen.go b/components/ledger/internal/gen/wire_gen.go index bf5381fe..5219c0a7 100644 --- a/components/ledger/internal/gen/wire_gen.go +++ b/components/ledger/internal/gen/wire_gen.go @@ -15,9 +15,11 @@ import ( "github.com/LerianStudio/midaz/common/mopentelemetry" "github.com/LerianStudio/midaz/common/mpostgres" mrabbitmq2 "github.com/LerianStudio/midaz/common/mrabbitmq" + "github.com/LerianStudio/midaz/common/mredis" "github.com/LerianStudio/midaz/common/mzap" "github.com/LerianStudio/midaz/components/ledger/internal/adapters/database/mongodb" "github.com/LerianStudio/midaz/components/ledger/internal/adapters/database/postgres" + "github.com/LerianStudio/midaz/components/ledger/internal/adapters/database/redis" "github.com/LerianStudio/midaz/components/ledger/internal/adapters/rabbitmq" "github.com/LerianStudio/midaz/components/ledger/internal/app/command" "github.com/LerianStudio/midaz/components/ledger/internal/app/query" @@ -29,6 +31,7 @@ import ( "github.com/LerianStudio/midaz/components/ledger/internal/domain/portfolio/portfolio" "github.com/LerianStudio/midaz/components/ledger/internal/domain/portfolio/product" "github.com/LerianStudio/midaz/components/ledger/internal/domain/rabbitmq" + redis2 "github.com/LerianStudio/midaz/components/ledger/internal/domain/redis" "github.com/LerianStudio/midaz/components/ledger/internal/ports/grpc" "github.com/LerianStudio/midaz/components/ledger/internal/ports/http" "github.com/LerianStudio/midaz/components/ledger/internal/service" @@ -55,6 +58,8 @@ func InitializeService() *service.Service { metadataMongoDBRepository := mongodb.NewMetadataMongoDBRepository(mongoConnection) rabbitMQConnection := setupRabbitMQConnection(config, logger) producerRabbitMQRepository := mrabbitmq.NewProducerRabbitMQ(rabbitMQConnection) + redisConnection := setupRedisConnection(config, logger) + redisConsumerRepository := redis.NewConsumerRedis(redisConnection) useCase := &command.UseCase{ OrganizationRepo: organizationPostgreSQLRepository, LedgerRepo: ledgerPostgreSQLRepository, @@ -64,6 +69,7 @@ func InitializeService() *service.Service { AssetRepo: assetPostgreSQLRepository, MetadataRepo: metadataMongoDBRepository, RabbitMQRepo: producerRabbitMQRepository, + RedisRepo: redisConsumerRepository, } consumerRabbitMQRepository := mrabbitmq.NewConsumerRabbitMQ(rabbitMQConnection) queryUseCase := &query.UseCase{ @@ -75,6 +81,7 @@ func InitializeService() *service.Service { AssetRepo: assetPostgreSQLRepository, MetadataRepo: metadataMongoDBRepository, RabbitMQRepo: consumerRabbitMQRepository, + RedisRepo: redisConsumerRepository, } accountHandler := &http.AccountHandler{ Command: useCase, @@ -190,11 +197,25 @@ func setupTelemetryProviders(cfg *service.Config) *mopentelemetry.Telemetry { return t } +func setupRedisConnection(cfg *service.Config, log mlog.Logger) *mredis.RedisConnection { + connStrSource := fmt.Sprintf("%s:%s", cfg.RedisHost, cfg.RedisPort) + + return &mredis.RedisConnection{ + Addr: connStrSource, + User: cfg.RedisUser, + Password: cfg.RedisPassword, + DB: 0, + Protocol: 3, + Logger: log, + } +} + var ( serviceSet = wire.NewSet(common.InitLocalEnvConfig, setupTelemetryProviders, mzap.InitializeLogger, setupPostgreSQLConnection, setupMongoDBConnection, setupCasdoorConnection, - setupRabbitMQConnection, grpc.NewRouterGRPC, service.NewServerGRPC, http.NewRouter, service.NewConfig, service.NewServer, postgres.NewOrganizationPostgreSQLRepository, postgres.NewLedgerPostgreSQLRepository, postgres.NewAssetPostgreSQLRepository, postgres.NewPortfolioPostgreSQLRepository, postgres.NewProductPostgreSQLRepository, postgres.NewAccountPostgreSQLRepository, mongodb.NewMetadataMongoDBRepository, mrabbitmq.NewProducerRabbitMQ, mrabbitmq.NewConsumerRabbitMQ, wire.Struct(new(http.OrganizationHandler), "*"), wire.Struct(new(http.LedgerHandler), "*"), wire.Struct(new(http.AssetHandler), "*"), wire.Struct(new(http.PortfolioHandler), "*"), wire.Struct(new(http.ProductHandler), "*"), wire.Struct(new(http.AccountHandler), "*"), wire.Struct(new(command.UseCase), "*"), wire.Struct(new(query.UseCase), "*"), wire.Bind(new(organization.Repository), new(*postgres.OrganizationPostgreSQLRepository)), wire.Bind(new(ledger.Repository), new(*postgres.LedgerPostgreSQLRepository)), wire.Bind(new(asset.Repository), new(*postgres.AssetPostgreSQLRepository)), wire.Bind(new(portfolio.Repository), new(*postgres.PortfolioPostgreSQLRepository)), wire.Bind(new(product.Repository), new(*postgres.ProductPostgreSQLRepository)), wire.Bind(new(account.Repository), new(*postgres.AccountPostgreSQLRepository)), wire.Bind(new(metadata.Repository), new(*mongodb.MetadataMongoDBRepository)), wire.Bind(new(rabbitmq.ConsumerRepository), new(*mrabbitmq.ConsumerRabbitMQRepository)), wire.Bind(new(rabbitmq.ProducerRepository), new(*mrabbitmq.ProducerRabbitMQRepository)), + setupRabbitMQConnection, + setupRedisConnection, grpc.NewRouterGRPC, service.NewServerGRPC, http.NewRouter, service.NewConfig, service.NewServer, postgres.NewOrganizationPostgreSQLRepository, postgres.NewLedgerPostgreSQLRepository, postgres.NewAssetPostgreSQLRepository, postgres.NewPortfolioPostgreSQLRepository, postgres.NewProductPostgreSQLRepository, postgres.NewAccountPostgreSQLRepository, mongodb.NewMetadataMongoDBRepository, mrabbitmq.NewProducerRabbitMQ, mrabbitmq.NewConsumerRabbitMQ, redis.NewConsumerRedis, wire.Struct(new(http.OrganizationHandler), "*"), wire.Struct(new(http.LedgerHandler), "*"), wire.Struct(new(http.AssetHandler), "*"), wire.Struct(new(http.PortfolioHandler), "*"), wire.Struct(new(http.ProductHandler), "*"), wire.Struct(new(http.AccountHandler), "*"), wire.Struct(new(command.UseCase), "*"), wire.Struct(new(query.UseCase), "*"), wire.Bind(new(organization.Repository), new(*postgres.OrganizationPostgreSQLRepository)), wire.Bind(new(ledger.Repository), new(*postgres.LedgerPostgreSQLRepository)), wire.Bind(new(asset.Repository), new(*postgres.AssetPostgreSQLRepository)), wire.Bind(new(portfolio.Repository), new(*postgres.PortfolioPostgreSQLRepository)), wire.Bind(new(product.Repository), new(*postgres.ProductPostgreSQLRepository)), wire.Bind(new(account.Repository), new(*postgres.AccountPostgreSQLRepository)), wire.Bind(new(metadata.Repository), new(*mongodb.MetadataMongoDBRepository)), wire.Bind(new(rabbitmq.ConsumerRepository), new(*mrabbitmq.ConsumerRabbitMQRepository)), wire.Bind(new(rabbitmq.ProducerRepository), new(*mrabbitmq.ProducerRabbitMQRepository)), wire.Bind(new(redis2.RedisRepository), new(*redis.RedisConsumerRepository)), ) svcSet = wire.NewSet(wire.Struct(new(service.Service), "Server", "ServerGRPC", "Logger")) diff --git a/components/ledger/internal/service/config.go b/components/ledger/internal/service/config.go index 8f4f4298..54966021 100644 --- a/components/ledger/internal/service/config.go +++ b/components/ledger/internal/service/config.go @@ -44,6 +44,10 @@ type Config struct { OtelServiceVersion string `env:"OTEL_RESOURCE_SERVICE_VERSION"` OtelDeploymentEnv string `env:"OTEL_RESOURCE_DEPLOYMENT_ENVIRONMENT"` OtelColExporterEndpoint string `env:"OTEL_EXPORTER_OTLP_ENDPOINT"` + RedisHost string `env:"REDIS_HOST"` + RedisPort string `env:"REDIS_PORT"` + RedisUser string `env:"REDIS_USER"` + RedisPassword string `env:"REDIS_PASSWORD"` } // NewConfig creates an instance of Config. diff --git a/components/transaction/.env.example b/components/transaction/.env.example index adeac118..903a02d2 100644 --- a/components/transaction/.env.example +++ b/components/transaction/.env.example @@ -28,7 +28,10 @@ MONGO_PASSWORD=lerian MONGO_PORT=5703 # REDIS +REDIS_HOST=redis REDIS_PORT=5704 +REDIS_USER=midaz +REDIS_PASSWORD=lerian # LOG LEVEL LOG_LEVEL=debug diff --git a/components/transaction/internal/adapters/database/redis/consumer.redis.go b/components/transaction/internal/adapters/database/redis/consumer.redis.go new file mode 100644 index 00000000..8433d4d3 --- /dev/null +++ b/components/transaction/internal/adapters/database/redis/consumer.redis.go @@ -0,0 +1,65 @@ +package redis + +import ( + "context" + "time" + + "github.com/LerianStudio/midaz/common" + "github.com/LerianStudio/midaz/common/mopentelemetry" + "github.com/LerianStudio/midaz/common/mredis" +) + +// RedisConsumerRepository is a Redis implementation of the Redis consumer. +type RedisConsumerRepository struct { + conn *mredis.RedisConnection +} + +// NewConsumerRedis returns a new instance of RedisRepository using the given Redis connection. +func NewConsumerRedis(rc *mredis.RedisConnection) *RedisConsumerRepository { + r := &RedisConsumerRepository{ + conn: rc, + } + if _, err := r.conn.GetClient(context.Background()); err != nil { + panic("Failed to connect on redis") + } + + return r +} + +func (rr *RedisConsumerRepository) Set(ctx context.Context, key, value string, ttl time.Duration) error { + logger := common.NewLoggerFromContext(ctx) + tracer := common.NewTracerFromContext(ctx) + + ctx, span := tracer.Start(ctx, "redis.set") + defer span.End() + + rds, err := rr.conn.GetClient(ctx) + if err != nil { + mopentelemetry.HandleSpanError(&span, "Failed to get redis", err) + + return err + } + + if ttl <= 0 { + ttl = mredis.RedisTTL + } + + logger.Infof("value of ttl: %v", ttl) + + statusCMD := rds.Set(ctx, key, value, ttl) + if statusCMD.Err() != nil { + mopentelemetry.HandleSpanError(&span, "Failed to set on redis", statusCMD.Err()) + + return statusCMD.Err() + } + + return nil +} + +func (rr *RedisConsumerRepository) Get(ctx context.Context, key string) error { + return nil +} + +func (rr *RedisConsumerRepository) Del(ctx context.Context, key string) error { + return nil +} diff --git a/components/transaction/internal/app/command/command.go b/components/transaction/internal/app/command/command.go index a79d47d7..5fda27d7 100644 --- a/components/transaction/internal/app/command/command.go +++ b/components/transaction/internal/app/command/command.go @@ -6,6 +6,7 @@ import ( m "github.com/LerianStudio/midaz/components/transaction/internal/domain/metadata" o "github.com/LerianStudio/midaz/components/transaction/internal/domain/operation" rmq "github.com/LerianStudio/midaz/components/transaction/internal/domain/rabbitmq" + rds "github.com/LerianStudio/midaz/components/transaction/internal/domain/redis" t "github.com/LerianStudio/midaz/components/transaction/internal/domain/transaction" ) @@ -28,4 +29,7 @@ type UseCase struct { // RabbitMQRepo provides an abstraction on top of the producer rabbitmq. RabbitMQRepo rmq.ProducerRepository + + // RedisRepo provides an abstraction on top of the redis consumer. + RedisRepo rds.RedisRepository } diff --git a/components/transaction/internal/app/query/query.go b/components/transaction/internal/app/query/query.go index 6c5ee1ba..b23ae51a 100644 --- a/components/transaction/internal/app/query/query.go +++ b/components/transaction/internal/app/query/query.go @@ -6,6 +6,7 @@ import ( m "github.com/LerianStudio/midaz/components/transaction/internal/domain/metadata" o "github.com/LerianStudio/midaz/components/transaction/internal/domain/operation" rmq "github.com/LerianStudio/midaz/components/transaction/internal/domain/rabbitmq" + rds "github.com/LerianStudio/midaz/components/transaction/internal/domain/redis" t "github.com/LerianStudio/midaz/components/transaction/internal/domain/transaction" ) @@ -28,4 +29,7 @@ type UseCase struct { // RabbitMQRepo provides an abstraction on top of the consumer rabbitmq. RabbitMQRepo rmq.ConsumerRepository + + // RedisRepo provides an abstraction on top of the redis consumer. + RedisRepo rds.RedisRepository } diff --git a/components/transaction/internal/domain/redis/redis_repository.go b/components/transaction/internal/domain/redis/redis_repository.go new file mode 100644 index 00000000..858006e8 --- /dev/null +++ b/components/transaction/internal/domain/redis/redis_repository.go @@ -0,0 +1,15 @@ +package redis + +import ( + "context" + "time" +) + +// RedisRepository provides an interface for redis. +// +//go:generate mockgen --destination=../../gen/mock/redis/redis_repository_mock.go --package=mock . RedisRepository +type RedisRepository interface { + Set(ctx context.Context, key, value string, ttl time.Duration) error + Get(ctx context.Context, key string) error + Del(ctx context.Context, key string) error +} diff --git a/components/transaction/internal/gen/inject.go b/components/transaction/internal/gen/inject.go index 82459011..f262278c 100644 --- a/components/transaction/internal/gen/inject.go +++ b/components/transaction/internal/gen/inject.go @@ -15,9 +15,11 @@ import ( "github.com/LerianStudio/midaz/common/mopentelemetry" "github.com/LerianStudio/midaz/common/mpostgres" "github.com/LerianStudio/midaz/common/mrabbitmq" + "github.com/LerianStudio/midaz/common/mredis" "github.com/LerianStudio/midaz/common/mzap" "github.com/LerianStudio/midaz/components/transaction/internal/adapters/database/mongodb" "github.com/LerianStudio/midaz/components/transaction/internal/adapters/database/postgres" + "github.com/LerianStudio/midaz/components/transaction/internal/adapters/database/redis" grpc "github.com/LerianStudio/midaz/components/transaction/internal/adapters/grpc" rabbitmq "github.com/LerianStudio/midaz/components/transaction/internal/adapters/rabbitmq" "github.com/LerianStudio/midaz/components/transaction/internal/app/command" @@ -27,6 +29,7 @@ import ( m "github.com/LerianStudio/midaz/components/transaction/internal/domain/metadata" o "github.com/LerianStudio/midaz/components/transaction/internal/domain/operation" r "github.com/LerianStudio/midaz/components/transaction/internal/domain/rabbitmq" + rds "github.com/LerianStudio/midaz/components/transaction/internal/domain/redis" t "github.com/LerianStudio/midaz/components/transaction/internal/domain/transaction" httpHandler "github.com/LerianStudio/midaz/components/transaction/internal/ports/http" "github.com/LerianStudio/midaz/components/transaction/internal/service" @@ -118,6 +121,19 @@ func setupTelemetryProviders(cfg *service.Config) *mopentelemetry.Telemetry { return t } +func setupRedisConnection(cfg *service.Config, log mlog.Logger) *mredis.RedisConnection { + connStrSource := fmt.Sprintf("%s:%s", cfg.RedisHost, cfg.RedisPort) + + return &mredis.RedisConnection{ + Addr: connStrSource, + User: cfg.RedisUser, + Password: cfg.RedisPassword, + DB: 0, + Protocol: 3, + Logger: log, + } +} + var ( serviceSet = wire.NewSet( common.InitLocalEnvConfig, @@ -128,6 +144,7 @@ var ( setupCasdoorConnection, setupGRPCConnection, setupRabbitMQConnection, + setupRedisConnection, service.NewConfig, httpHandler.NewRouter, service.NewServer, @@ -138,6 +155,7 @@ var ( grpc.NewAccountGRPC, rabbitmq.NewProducerRabbitMQ, rabbitmq.NewConsumerRabbitMQ, + redis.NewConsumerRedis, wire.Struct(new(httpHandler.TransactionHandler), "*"), wire.Struct(new(httpHandler.OperationHandler), "*"), wire.Struct(new(httpHandler.AssetRateHandler), "*"), @@ -150,6 +168,7 @@ var ( wire.Bind(new(a.Repository), new(*grpc.AccountGRPCRepository)), wire.Bind(new(r.ConsumerRepository), new(*rabbitmq.ConsumerRabbitMQRepository)), wire.Bind(new(r.ProducerRepository), new(*rabbitmq.ProducerRabbitMQRepository)), + wire.Bind(new(rds.RedisRepository), new(*redis.RedisConsumerRepository)), ) svcSet = wire.NewSet( diff --git a/components/transaction/internal/gen/mock/redis/redis_repository_mock.go b/components/transaction/internal/gen/mock/redis/redis_repository_mock.go new file mode 100644 index 00000000..5a9e6f16 --- /dev/null +++ b/components/transaction/internal/gen/mock/redis/redis_repository_mock.go @@ -0,0 +1,83 @@ +// Code generated by MockGen. DO NOT EDIT. +// Source: github.com/LerianStudio/midaz/components/transaction/internal/domain/redis (interfaces: RedisRepository) +// +// Generated by this command: +// +// mockgen --destination=../../gen/mock/redis/redis_repository_mock.go --package=mock . RedisRepository +// + +// Package mock is a generated GoMock package. +package mock + +import ( + context "context" + reflect "reflect" + time "time" + + gomock "go.uber.org/mock/gomock" +) + +// MockRedisRepository is a mock of RedisRepository interface. +type MockRedisRepository struct { + ctrl *gomock.Controller + recorder *MockRedisRepositoryMockRecorder +} + +// MockRedisRepositoryMockRecorder is the mock recorder for MockRedisRepository. +type MockRedisRepositoryMockRecorder struct { + mock *MockRedisRepository +} + +// NewMockRedisRepository creates a new mock instance. +func NewMockRedisRepository(ctrl *gomock.Controller) *MockRedisRepository { + mock := &MockRedisRepository{ctrl: ctrl} + mock.recorder = &MockRedisRepositoryMockRecorder{mock} + return mock +} + +// EXPECT returns an object that allows the caller to indicate expected use. +func (m *MockRedisRepository) EXPECT() *MockRedisRepositoryMockRecorder { + return m.recorder +} + +// Del mocks base method. +func (m *MockRedisRepository) Del(arg0 context.Context, arg1 string) error { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "Del", arg0, arg1) + ret0, _ := ret[0].(error) + return ret0 +} + +// Del indicates an expected call of Del. +func (mr *MockRedisRepositoryMockRecorder) Del(arg0, arg1 any) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Del", reflect.TypeOf((*MockRedisRepository)(nil).Del), arg0, arg1) +} + +// Get mocks base method. +func (m *MockRedisRepository) Get(arg0 context.Context, arg1 string) error { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "Get", arg0, arg1) + ret0, _ := ret[0].(error) + return ret0 +} + +// Get indicates an expected call of Get. +func (mr *MockRedisRepositoryMockRecorder) Get(arg0, arg1 any) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Get", reflect.TypeOf((*MockRedisRepository)(nil).Get), arg0, arg1) +} + +// Set mocks base method. +func (m *MockRedisRepository) Set(arg0 context.Context, arg1, arg2 string, arg3 time.Duration) error { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "Set", arg0, arg1, arg2, arg3) + ret0, _ := ret[0].(error) + return ret0 +} + +// Set indicates an expected call of Set. +func (mr *MockRedisRepositoryMockRecorder) Set(arg0, arg1, arg2, arg3 any) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Set", reflect.TypeOf((*MockRedisRepository)(nil).Set), arg0, arg1, arg2, arg3) +} diff --git a/components/transaction/internal/gen/wire_gen.go b/components/transaction/internal/gen/wire_gen.go index 08950e2d..5ab21705 100644 --- a/components/transaction/internal/gen/wire_gen.go +++ b/components/transaction/internal/gen/wire_gen.go @@ -16,9 +16,11 @@ import ( "github.com/LerianStudio/midaz/common/mopentelemetry" "github.com/LerianStudio/midaz/common/mpostgres" mrabbitmq2 "github.com/LerianStudio/midaz/common/mrabbitmq" + "github.com/LerianStudio/midaz/common/mredis" "github.com/LerianStudio/midaz/common/mzap" "github.com/LerianStudio/midaz/components/transaction/internal/adapters/database/mongodb" "github.com/LerianStudio/midaz/components/transaction/internal/adapters/database/postgres" + "github.com/LerianStudio/midaz/components/transaction/internal/adapters/database/redis" "github.com/LerianStudio/midaz/components/transaction/internal/adapters/grpc" "github.com/LerianStudio/midaz/components/transaction/internal/adapters/rabbitmq" "github.com/LerianStudio/midaz/components/transaction/internal/app/command" @@ -28,6 +30,7 @@ import ( "github.com/LerianStudio/midaz/components/transaction/internal/domain/metadata" "github.com/LerianStudio/midaz/components/transaction/internal/domain/operation" "github.com/LerianStudio/midaz/components/transaction/internal/domain/rabbitmq" + redis2 "github.com/LerianStudio/midaz/components/transaction/internal/domain/redis" "github.com/LerianStudio/midaz/components/transaction/internal/domain/transaction" "github.com/LerianStudio/midaz/components/transaction/internal/ports/http" "github.com/LerianStudio/midaz/components/transaction/internal/service" @@ -53,6 +56,8 @@ func InitializeService() *service.Service { metadataMongoDBRepository := mongodb.NewMetadataMongoDBRepository(mongoConnection) rabbitMQConnection := setupRabbitMQConnection(config, logger) producerRabbitMQRepository := mrabbitmq.NewProducerRabbitMQ(rabbitMQConnection) + redisConnection := setupRedisConnection(config, logger) + redisConsumerRepository := redis.NewConsumerRedis(redisConnection) useCase := &command.UseCase{ TransactionRepo: transactionPostgreSQLRepository, AccountGRPCRepo: accountGRPCRepository, @@ -60,6 +65,7 @@ func InitializeService() *service.Service { AssetRateRepo: assetRatePostgreSQLRepository, MetadataRepo: metadataMongoDBRepository, RabbitMQRepo: producerRabbitMQRepository, + RedisRepo: redisConsumerRepository, } consumerRabbitMQRepository := mrabbitmq.NewConsumerRabbitMQ(rabbitMQConnection) queryUseCase := &query.UseCase{ @@ -69,6 +75,7 @@ func InitializeService() *service.Service { AssetRateRepo: assetRatePostgreSQLRepository, MetadataRepo: metadataMongoDBRepository, RabbitMQRepo: consumerRabbitMQRepository, + RedisRepo: redisConsumerRepository, } transactionHandler := &http.TransactionHandler{ Command: useCase, @@ -178,12 +185,26 @@ func setupTelemetryProviders(cfg *service.Config) *mopentelemetry.Telemetry { return t } +func setupRedisConnection(cfg *service.Config, log mlog.Logger) *mredis.RedisConnection { + connStrSource := fmt.Sprintf("%s:%s", cfg.RedisHost, cfg.RedisPort) + + return &mredis.RedisConnection{ + Addr: connStrSource, + User: cfg.RedisUser, + Password: cfg.RedisPassword, + DB: 0, + Protocol: 3, + Logger: log, + } +} + var ( serviceSet = wire.NewSet(common.InitLocalEnvConfig, setupTelemetryProviders, mzap.InitializeLogger, setupPostgreSQLConnection, setupMongoDBConnection, setupCasdoorConnection, setupGRPCConnection, - setupRabbitMQConnection, service.NewConfig, http.NewRouter, service.NewServer, postgres.NewTransactionPostgreSQLRepository, postgres.NewOperationPostgreSQLRepository, postgres.NewAssetRatePostgreSQLRepository, mongodb.NewMetadataMongoDBRepository, grpc.NewAccountGRPC, mrabbitmq.NewProducerRabbitMQ, mrabbitmq.NewConsumerRabbitMQ, wire.Struct(new(http.TransactionHandler), "*"), wire.Struct(new(http.OperationHandler), "*"), wire.Struct(new(http.AssetRateHandler), "*"), wire.Struct(new(command.UseCase), "*"), wire.Struct(new(query.UseCase), "*"), wire.Bind(new(transaction.Repository), new(*postgres.TransactionPostgreSQLRepository)), wire.Bind(new(operation.Repository), new(*postgres.OperationPostgreSQLRepository)), wire.Bind(new(assetrate.Repository), new(*postgres.AssetRatePostgreSQLRepository)), wire.Bind(new(metadata.Repository), new(*mongodb.MetadataMongoDBRepository)), wire.Bind(new(account.Repository), new(*grpc.AccountGRPCRepository)), wire.Bind(new(rabbitmq.ConsumerRepository), new(*mrabbitmq.ConsumerRabbitMQRepository)), wire.Bind(new(rabbitmq.ProducerRepository), new(*mrabbitmq.ProducerRabbitMQRepository)), + setupRabbitMQConnection, + setupRedisConnection, service.NewConfig, http.NewRouter, service.NewServer, postgres.NewTransactionPostgreSQLRepository, postgres.NewOperationPostgreSQLRepository, postgres.NewAssetRatePostgreSQLRepository, mongodb.NewMetadataMongoDBRepository, grpc.NewAccountGRPC, mrabbitmq.NewProducerRabbitMQ, mrabbitmq.NewConsumerRabbitMQ, redis.NewConsumerRedis, wire.Struct(new(http.TransactionHandler), "*"), wire.Struct(new(http.OperationHandler), "*"), wire.Struct(new(http.AssetRateHandler), "*"), wire.Struct(new(command.UseCase), "*"), wire.Struct(new(query.UseCase), "*"), wire.Bind(new(transaction.Repository), new(*postgres.TransactionPostgreSQLRepository)), wire.Bind(new(operation.Repository), new(*postgres.OperationPostgreSQLRepository)), wire.Bind(new(assetrate.Repository), new(*postgres.AssetRatePostgreSQLRepository)), wire.Bind(new(metadata.Repository), new(*mongodb.MetadataMongoDBRepository)), wire.Bind(new(account.Repository), new(*grpc.AccountGRPCRepository)), wire.Bind(new(rabbitmq.ConsumerRepository), new(*mrabbitmq.ConsumerRabbitMQRepository)), wire.Bind(new(rabbitmq.ProducerRepository), new(*mrabbitmq.ProducerRabbitMQRepository)), wire.Bind(new(redis2.RedisRepository), new(*redis.RedisConsumerRepository)), ) svcSet = wire.NewSet(wire.Struct(new(service.Service), "Server", "Logger")) diff --git a/components/transaction/internal/service/config.go b/components/transaction/internal/service/config.go index 5e7950a0..a3877b42 100644 --- a/components/transaction/internal/service/config.go +++ b/components/transaction/internal/service/config.go @@ -45,6 +45,10 @@ type Config struct { OtelServiceVersion string `env:"OTEL_RESOURCE_SERVICE_VERSION"` OtelDeploymentEnv string `env:"OTEL_RESOURCE_DEPLOYMENT_ENVIRONMENT"` OtelColExporterEndpoint string `env:"OTEL_EXPORTER_OTLP_ENDPOINT"` + RedisHost string `env:"REDIS_HOST"` + RedisPort string `env:"REDIS_PORT"` + RedisUser string `env:"REDIS_USER"` + RedisPassword string `env:"REDIS_PASSWORD"` } // NewConfig creates an instance of Config. diff --git a/go.mod b/go.mod index 3d96e447..a5f6d05c 100644 --- a/go.mod +++ b/go.mod @@ -47,9 +47,11 @@ require ( github.com/atotto/clipboard v0.1.4 // indirect github.com/aymanbagabas/go-osc52/v2 v2.0.1 // indirect github.com/cenkalti/backoff/v4 v4.3.0 // indirect + github.com/cespare/xxhash/v2 v2.3.0 // indirect github.com/charmbracelet/lipgloss v1.0.0 // indirect github.com/charmbracelet/x/ansi v0.4.5 // indirect github.com/charmbracelet/x/term v0.2.0 // indirect + github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f // indirect github.com/erikgeiser/coninput v0.0.0-20211004153227-1c3628e74d0f // indirect github.com/go-logr/logr v1.4.2 // indirect github.com/go-logr/stdr v1.2.2 // indirect @@ -70,6 +72,7 @@ require ( github.com/muesli/ansi v0.0.0-20230316100256-276c6243b2f6 // indirect github.com/muesli/cancelreader v0.2.2 // indirect github.com/muesli/termenv v0.15.2 // indirect + github.com/redis/go-redis/v9 v9.7.0 // indirect github.com/swaggo/files/v2 v2.0.0 // indirect github.com/swaggo/swag v1.16.3 // indirect go.opentelemetry.io/proto/otlp v1.3.1 // indirect diff --git a/go.sum b/go.sum index ee9c7e8c..65055b0c 100644 --- a/go.sum +++ b/go.sum @@ -22,6 +22,8 @@ github.com/casdoor/casdoor-go-sdk v1.3.0 h1:iUZKsrNUkhtAoyitFIFw3e6TchctAdoxmVgL github.com/casdoor/casdoor-go-sdk v1.3.0/go.mod h1:cMnkCQJgMYpgAlgEx8reSt1AVaDIQLcJ1zk5pzBaz+4= github.com/cenkalti/backoff/v4 v4.3.0 h1:MyRJ/UdXutAwSAT+s3wNd7MfTIcy71VQueUuFK343L8= github.com/cenkalti/backoff/v4 v4.3.0/go.mod h1:Y3VNntkOUPxTVeUxJ/G5vcM//AlwfmyYozVcomhLiZE= +github.com/cespare/xxhash/v2 v2.3.0 h1:UL815xU9SqsFlibzuggzjXhog7bL6oX9BbNZnL2UFvs= +github.com/cespare/xxhash/v2 v2.3.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= github.com/charmbracelet/bubbles v0.20.0 h1:jSZu6qD8cRQ6k9OMfR1WlM+ruM8fkPWkHvQWD9LIutE= github.com/charmbracelet/bubbles v0.20.0/go.mod h1:39slydyswPy+uVOHZ5x/GjwVAFkCsV8IIVy+4MhzwwU= github.com/charmbracelet/bubbletea v1.2.1 h1:J041h57zculJKEKf/O2pS4edXGIz+V0YvojvfGXePIk= @@ -39,6 +41,8 @@ github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc h1:U9qPSI2PIWSS1 github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/decred/dcrd/dcrec/secp256k1/v4 v4.3.0 h1:rpfIENRNNilwHwZeG5+P150SMrnNEcHYvcCuK6dPZSg= github.com/decred/dcrd/dcrec/secp256k1/v4 v4.3.0/go.mod h1:v57UDF4pDQJcEfFUCRop3lJL149eHGSe9Jvczhzjo/0= +github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f h1:lO4WD4F/rVNCu3HqELle0jiPLLBs70cWOduZpkS1E78= +github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f/go.mod h1:cuUVRXasLTGF7a8hSLbxyZXjz+1KgoB3wDUb6vlszIc= github.com/dhui/dktest v0.4.1 h1:/w+IWuDXVymg3IrRJCHHOkMK10m9aNVMOyD0X12YVTg= github.com/dhui/dktest v0.4.1/go.mod h1:DdOqcUpL7vgyP4GlF3X3w7HbSlz8cEQzwewPveYEQbA= github.com/docker/distribution v2.8.2+incompatible h1:T3de5rq0dB1j30rp0sA2rER+m322EBzniBPB6ZIzuh8= @@ -191,6 +195,8 @@ github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 h1:Jamvg5psRI github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/rabbitmq/amqp091-go v1.10.0 h1:STpn5XsHlHGcecLmMFCtg7mqq0RnD+zFr4uzukfVhBw= github.com/rabbitmq/amqp091-go v1.10.0/go.mod h1:Hy4jKW5kQART1u+JkDTF9YYOQUHXqMuhrgxOEeS7G4o= +github.com/redis/go-redis/v9 v9.7.0 h1:HhLSs+B6O021gwzl+locl0zEDnyNkxMtf/Z3NNBMa9E= +github.com/redis/go-redis/v9 v9.7.0/go.mod h1:f6zhXITC7JUJIlPEiBOTXxJgPLdZcA93GewI7inzyWw= github.com/rivo/uniseg v0.2.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc= github.com/rivo/uniseg v0.4.7 h1:WUdvkW8uEhrYfLC4ZzdpI2ztxP1I582+49Oc5Mq64VQ= github.com/rivo/uniseg v0.4.7/go.mod h1:FN3SvrM+Zdj16jyLfmOkMNblXMcoc8DfTHruCPUcx88=