Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,12 @@ jobs:

- name: Verify dependencies
run: go mod verify

- name: Validate Go packages
run: go run ./deployment/ci/validation_pkg/main.go

- name: Check mocks
run: make check-mocks

- name: Generate mocks
run: make generate-mocks
Expand Down
118 changes: 33 additions & 85 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -19,9 +19,20 @@ NC=\033[0m # No Color
all: help

# Executar todos os checks do CI localmente
ci: lint security test build clean
ci: validate-pkg check-mocks test build lint security clean
@echo "$(GREEN)✅ Todos os checks do CI passaram com sucesso!$(NC)"

# Gera mocks
generate-mocks: install-mockgen clean-mocks
go run deployment/ci/gen_mocks/main.go
@echo "$(GREEN)Mocks gerados com sucesso!$(NC)"

# Valida pacotes Go
validate-pkg:
@echo "$(GREEN)Validando pacotes Go...$(NC)"
$(GO) run ./deployment/ci/validation_pkg/main.go
@echo "$(GREEN)Validação dos pacotes Go finalizada!$(NC)"

# Construir o aplicativo
build:
@echo "$(GREEN)Construindo aplicação...$(NC)"
Expand All @@ -32,20 +43,25 @@ load-test:
@echo "$(YELLOW)Executando teste de carga...$(NC)"
@$(GO) run ./cmd/loadtest/loadtest.go

# Iniciar worker
run-worker:
@echo "$(BLUE)Iniciando serviço worker...$(NC)"
@$(GO) run ./cmd/api/main.go --service=worker
# Iniciar pubsub (API)
run-pubsub:
@echo "$(BLUE)Iniciando serviço pubsub (API)...$(NC)"
@$(GO) run ./cmd/api/main.go --scope=pubsub

# Iniciar task (API)
run-task:
@echo "$(BLUE)Iniciando serviço task (API)...$(NC)"
@$(GO) run ./cmd/api/main.go --scope=task

# Iniciar webhook (API)
run-webhook:
@echo "$(BLUE)Iniciando serviço webhook (API)...$(NC)"
@$(GO) run ./cmd/api/main.go --service=webhook
# Iniciar backoffice (API)
run-backoffice:
@echo "$(BLUE)Iniciando serviço backoffice (API)...$(NC)"
@$(GO) run ./cmd/api/main.go --scope=backoffice

# Iniciar ambos serviços
run-all:
@echo "$(BLUE)Iniciando todos os serviços (worker e webhook)...$(NC)"
@$(GO) run ./cmd/api/main.go --service=all
@$(GO) run ./cmd/api/main.go --scope=all

# Limpar binários gerados
clean:
Expand Down Expand Up @@ -201,90 +217,22 @@ install-mockgen:
@$(GO) install go.uber.org/mock/mockgen@latest

generate-mocks: install-mockgen clean-mocks
@echo "$(GREEN)Gerando mocks...$(NC)"
@echo "$(BLUE)Gerando mock para DeadLetter...$(NC)"
@$(MOCKGEN) -source=internal/wtrhandler/deadletter_asynq_handle.go -destination=internal/wtrhandler/deadletter_mock.go -package=wtrhandler DeadLetterStore
@echo "$(BLUE)Gerando mock para Fetcher...$(NC)"
@$(MOCKGEN) -source=internal/wtrhandler/request_handle_asynq.go -destination=internal/wtrhandler/fetcher_mock.go -package=wtrhandler Fetcher
@echo "$(BLUE)Gerando mock para Publisher...$(NC)"
@$(MOCKGEN) -source=pkg/pubadapter/adapter.go -destination=pkg/publisher/publisher_task_mock.go -package=publisher
@echo "$(BLUE)Gerando mock para Publisher em pubadapter...$(NC)"
@$(MOCKGEN) -source=pkg/pubadapter/adapter.go -destination=pkg/pubadapter/publisher_task_mock.go -package=pubadapter
@echo "$(BLUE)Gerando mock para PublisherInsights...$(NC)"
# TODO: está sendo criado junto com outro por estar no mesmo arquivo, separar interfaces em arquivos diferentes
# @$(MOCKGEN) -source=tmp/publisher_insights.go -destination=internal/wtrhandler/mock_publisher_insights.go -package=wtrhandler
@echo "$(BLUE)Gerando mock para ConsumerInsights...$(NC)"
# TODO: está sendo criado junto com outro por estar no mesmo arquivo, separar interfaces em arquivos diferentes
# @$(MOCKGEN) -source=tmp/consumer_insights.go -destination=internal/wtrhandler/mock_consumer_insights.go -package=wtrhandler
@go run deployment/ci/gen_mocks/main.go
@echo "$(GREEN)Mocks gerados com sucesso!$(NC)"

update-mocks: generate-mocks
@$(MAKE) clean-mocks
@$(MAKE) generate-mocks
@echo "$(GREEN)Mocks atualizados!$(NC)"

check-mocks:
@echo "$(GREEN)Verificando se os mocks existem...$(NC)"
@if [ ! -f "internal/wtrhandler/repository_mock.go" ]; then \
echo "$(YELLOW)⚠️ Repository mock não encontrado!$(NC)"; \
echo "Execute 'make generate-mocks' para gerar os mocks"; \
exit 1; \
fi
@if [ ! -f "internal/wtrhandler/deadletter_mock.go" ]; then \
echo "$(YELLOW)⚠️ DeadLetter mock não encontrado!$(NC)"; \
echo "Execute 'make generate-mocks' para gerar os mocks"; \
exit 1; \
fi
@if [ ! -f "internal/wtrhandler/fetcher_mock.go" ]; then \
echo "$(YELLOW)⚠️ Fetcher mock não encontrado!$(NC)"; \
echo "Execute 'make generate-mocks' para gerar os mocks"; \
exit 1; \
fi
@if [ ! -f "pkg/cachemanager/cache_mock.go" ]; then \
echo "$(YELLOW)⚠️ Cache mock não encontrado!$(NC)"; \
echo "Execute 'make generate-mocks' para gerar os mocks"; \
exit 1; \
fi
@if [ ! -f "pkg/publisher/publisher_task_mock.go" ]; then \
echo "$(YELLOW)⚠️ Publisher mock não encontrado!$(NC)"; \
echo "Execute 'make generate-mocks' para gerar os mocks"; \
exit 1; \
fi
@if [ ! -f "pkg/pubadapter/publisher_task_mock.go" ]; then \
echo "$(YELLOW)⚠️ Publisher mock em pubadapter não encontrado!$(NC)"; \
echo "Execute 'make generate-mocks' para gerar os mocks"; \
exit 1; \
fi
# TODO: Descomentar quando os mocks de insights forem implementados
# @if [ ! -f "internal/wtrhandler/mock_publisher_insights.go" ]; then \
# echo "$(YELLOW)⚠️ PublisherInsights mock não encontrado!$(NC)"; \
# echo "Execute 'make generate-mocks' para gerar os mocks"; \
# exit 1; \
# fi
# @if [ ! -f "internal/wtrhandler/mock_consumer_insights.go" ]; then \
# echo "$(YELLOW)⚠️ ConsumerInsights mock não encontrado!$(NC)"; \
# echo "Execute 'make generate-mocks' para gerar os mocks"; \
# exit 1; \
# fi
@echo "$(GREEN)✅ Todos os mocks existem!$(NC)"
@echo "$(GREEN)Verificando se todas as interfaces possuem mocks...$(NC)"
@sh ./deployment/ci/validate_mock.sh
@echo "$(BLUE)💡 Para regenerar todos os mocks, execute: make update-mocks$(NC)"

clean-mocks:
@echo "$(GREEN)Removendo mocks...$(NC)"
@echo "$(BLUE)Removendo Repository mock...$(NC)"
@rm -f internal/wtrhandler/repository_mock.go
@echo "$(BLUE)Removendo DeadLetter mock...$(NC)"
@rm -f internal/wtrhandler/deadletter_mock.go
@echo "$(BLUE)Removendo Fetcher mock...$(NC)"
@rm -f internal/wtrhandler/fetcher_mock.go
@echo "$(BLUE)Removendo Cache mock...$(NC)"
@rm -f pkg/cachemanager/cache_mock.go
@echo "$(BLUE)Removendo Publisher mock...$(NC)"
@rm -f pkg/publisher/publisher_task_mock.go
@echo "$(BLUE)Removendo Publisher mock em pubadapter...$(NC)"
@rm -f pkg/pubadapter/publisher_task_mock.go
@echo "$(BLUE)Removendo PublisherInsights mock...$(NC)"
@rm -f internal/wtrhandler/mock_publisher_insights.go
@echo "$(BLUE)Removendo ConsumerInsights mock...$(NC)"
@rm -f internal/wtrhandler/mock_consumer_insights.go
@find . -type f -name '*_mock.go' -exec rm -f {} \;
@rm -rf ./mocks/**
@echo "$(GREEN)Mocks removidos com sucesso!$(NC)"
@echo "$(BLUE)💡 Para gerar novos mocks, execute: make generate-mocks$(NC)"

Expand Down
160 changes: 57 additions & 103 deletions cmd/api/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,25 +10,18 @@ import (
"syscall"
"time"

"cloud.google.com/go/pubsub"
vkit "cloud.google.com/go/pubsub/apiv1"
"github.com/IsaacDSC/gqueue/internal/cfg"
"github.com/IsaacDSC/gqueue/internal/domain"
"github.com/IsaacDSC/gqueue/internal/storests"
"github.com/googleapis/gax-go/v2"
"google.golang.org/grpc/codes"

"github.com/hibiken/asynq"

"github.com/IsaacDSC/gqueue/cmd/setup"
"github.com/IsaacDSC/gqueue/cmd/setup/api"
"github.com/IsaacDSC/gqueue/cmd/setup/backoffice"
"github.com/IsaacDSC/gqueue/cmd/setup/pubsub"
"github.com/IsaacDSC/gqueue/cmd/setup/task"
"github.com/IsaacDSC/gqueue/internal/cfg"
"github.com/IsaacDSC/gqueue/internal/fetcher"
"github.com/IsaacDSC/gqueue/internal/interstore"
"github.com/IsaacDSC/gqueue/internal/storests"
"github.com/redis/go-redis/v9"
)

// waitForShutdown waits for SIGINT/SIGTERM and gracefully shuts down the provided servers.
func waitForShutdown(apiServer, backofficeServer *http.Server) {
func waitForShutdown(server *http.Server) {
quit := make(chan os.Signal, 1)
signal.Notify(quit, syscall.SIGINT, syscall.SIGTERM)
<-quit
Expand All @@ -38,125 +31,86 @@ func waitForShutdown(apiServer, backofficeServer *http.Server) {
shutdownCtx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
defer cancel()

if apiServer != nil {
if err := apiServer.Shutdown(shutdownCtx); err != nil {
log.Printf("API server shutdown error: %v", err)
}
}

if backofficeServer != nil {
if err := backofficeServer.Shutdown(shutdownCtx); err != nil {
if server != nil {
if err := server.Shutdown(shutdownCtx); err != nil {
log.Printf("Backoffice server shutdown error: %v", err)
}
}

log.Println("Server shutdown complete")
}

// TODO: rename to --scope=...
// go run . --service=all
// go run . --service=backoffice
// go run . --service=api
// go run . --service=archived-notification
// go run . --scope=all
// go run . --scope=backoffice
// go run . --scope=pubsub
// go run . --scope=task
func main() {
conf := cfg.Get()
ctx := context.Background()

asynqClient := asynq.NewClient(asynq.RedisClientOpt{Addr: conf.Cache.CacheAddr})
defer asynqClient.Close()

// var highPerformancePublisher pubadapter.GenericPublisher
var pubsubClient *pubsub.Client

if conf.WQ == cfg.WQGooglePubSub {
config := &pubsub.ClientConfig{
PublisherCallOptions: &vkit.PublisherCallOptions{
Publish: []gax.CallOption{
gax.WithRetry(func() gax.Retryer {
return gax.OnCodes([]codes.Code{
codes.Aborted,
codes.Canceled,
codes.Internal,
codes.ResourceExhausted,
codes.Unknown,
codes.Unavailable,
codes.DeadlineExceeded,
}, gax.Backoff{
Initial: 250 * time.Millisecond, // default 100 milliseconds
Max: 5 * time.Second, // default 60 seconds
Multiplier: 1.45, // default 1.3
})
}),
},
},
}

clientPubsub, err := pubsub.NewClientWithConfig(ctx, domain.ProjectID, config)
if err != nil {
log.Fatalf("Erro ao criar cliente: %v", err)
}

pubsubClient = clientPubsub

defer clientPubsub.Close()
}
scope := flag.String("scope", "all", "service to run")
flag.Parse()

cacheClient := redis.NewClient(&redis.Options{Addr: conf.Cache.CacheAddr})
if err := cacheClient.Ping(ctx).Err(); err != nil {
redisClient := redis.NewClient(&redis.Options{Addr: conf.Cache.CacheAddr})
if err := redisClient.Ping(ctx).Err(); err != nil {
panic(err)
}

storeInsights := storests.NewStore(cacheClient)
storeInsights := storests.NewStore(redisClient)

store, err := interstore.NewPostgresStoreFromDSN(conf.ConfigDatabase.DbConn)
if err != nil {
panic(err)
}

service := flag.String("service", "all", "service to run")
flag.Parse()

if *service == "api" {
apiServer := api.Start(
ctx,
var servers []*http.Server
if scopeOrAll(*scope, "backoffice") {
backofficeServer := backoffice.Start(
redisClient,
store,
asynqClient,
pubsubClient,
storeInsights,
)
waitForShutdown(apiServer, nil)
return
servers = append(servers, backofficeServer)
}

if *service == "backoffice" {
backofficeServer := backoffice.Start(
cacheClient,
store,
storeInsights,
var memStore *interstore.MemStore
var fetch *fetcher.Notification
// task and pubsub share some dependencies, so we initialize them here and pass to both services
if *scope == "pubsub" || *scope == "task" || *scope == "all" {
memStore = interstore.NewMemStore(store)
fetch = fetcher.NewNotification()
}

if scopeOrAll(*scope, "pubsub") {
s := pubsub.New(
store, memStore, fetch, storeInsights,
)
waitForShutdown(nil, backofficeServer)
return

s.Start(ctx, conf)

defer s.Close()

servers = append(servers, s.Server())
}

// TODO: adicionar graceful shutdown
if *service == "archived-notification" {
setup.StartArchivedNotify(ctx, store, cacheClient)
return
if scopeOrAll(*scope, "task") {
s := task.New(
store, memStore, fetch, storeInsights,
)

s.Start(ctx, conf)

defer s.Close()

servers = append(servers, s.Server())
}

backofficeServer := backoffice.Start(
cacheClient,
store,
storeInsights,
)

apiServer := api.Start(
ctx,
store,
asynqClient,
pubsubClient,
storeInsights,
)

waitForShutdown(apiServer, backofficeServer)
for _, server := range servers {
waitForShutdown(server)
}

}

func scopeOrAll(scope, expected string) bool {
return scope == "all" || scope == expected
}
Loading