Skip to content

Commit

Permalink
feat: add a health check endpoint
Browse files Browse the repository at this point in the history
  • Loading branch information
allisson committed Jan 3, 2024
1 parent d33d289 commit 241e495
Show file tree
Hide file tree
Showing 15 changed files with 423 additions and 3 deletions.
5 changes: 4 additions & 1 deletion cmd/psqlqueue/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -47,21 +47,24 @@ func main() {
messageRepository := repository.NewMessage(pool)
topicRepository := repository.NewTopic(pool)
subscriptionRepository := repository.NewSubscription(pool)
healthCheckRepository := repository.NewHealthCheck(pool)

// services
queueService := service.NewQueue(queueRepository)
messageService := service.NewMessage(messageRepository, queueRepository)
topicService := service.NewTopic(topicRepository, subscriptionRepository, queueRepository, messageRepository)
subscriptionService := service.NewSubscription(subscriptionRepository)
healthCheckService := service.NewHealthCheck(healthCheckRepository)

// http handlers
queueHandler := http.NewQueueHandler(queueService)
messageHandler := http.NewMessageHandler(messageService)
topicHandler := http.NewTopicHandler(topicService)
subscriptionHandler := http.NewSubscriptionHandler(subscriptionService)
healthCheckHandler := http.NewHealthCheckHandler(healthCheckService)

// run http server
http.RunServer(c.Context, cfg, http.SetupRouter(logger, queueHandler, messageHandler, topicHandler, subscriptionHandler))
http.RunServer(c.Context, cfg, http.SetupRouter(logger, queueHandler, messageHandler, topicHandler, subscriptionHandler, healthCheckHandler))

return nil
},
Expand Down
36 changes: 36 additions & 0 deletions docs/docs.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,34 @@ const docTemplate = `{
"host": "{{.Host}}",
"basePath": "{{.BasePath}}",
"paths": {
"/healthz": {
"get": {
"consumes": [
"application/json"
],
"produces": [
"application/json"
],
"tags": [
"health-check"
],
"summary": "Execute a health check",
"responses": {
"200": {
"description": "OK",
"schema": {
"$ref": "#/definitions/HealthCheckResponse"
}
},
"500": {
"description": "Internal Server Error",
"schema": {
"$ref": "#/definitions/ErrorResponse"
}
}
}
}
},
"/queue/{queue_id}/messages": {
"post": {
"consumes": [
Expand Down Expand Up @@ -987,6 +1015,14 @@ const docTemplate = `{
"subscriptionNotFound"
]
},
"HealthCheckResponse": {
"type": "object",
"properties": {
"success": {
"type": "boolean"
}
}
},
"MessageListResponse": {
"type": "object",
"properties": {
Expand Down
36 changes: 36 additions & 0 deletions docs/swagger.json
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,34 @@
"contact": {}
},
"paths": {
"/healthz": {
"get": {
"consumes": [
"application/json"
],
"produces": [
"application/json"
],
"tags": [
"health-check"
],
"summary": "Execute a health check",
"responses": {
"200": {
"description": "OK",
"schema": {
"$ref": "#/definitions/HealthCheckResponse"
}
},
"500": {
"description": "Internal Server Error",
"schema": {
"$ref": "#/definitions/ErrorResponse"
}
}
}
}
},
"/queue/{queue_id}/messages": {
"post": {
"consumes": [
Expand Down Expand Up @@ -976,6 +1004,14 @@
"subscriptionNotFound"
]
},
"HealthCheckResponse": {
"type": "object",
"properties": {
"success": {
"type": "boolean"
}
}
},
"MessageListResponse": {
"type": "object",
"properties": {
Expand Down
23 changes: 23 additions & 0 deletions docs/swagger.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,11 @@ definitions:
- topicNotFound
- subscriptionAlreadyExists
- subscriptionNotFound
HealthCheckResponse:
properties:
success:
type: boolean
type: object
MessageListResponse:
properties:
data:
Expand Down Expand Up @@ -240,6 +245,24 @@ definitions:
info:
contact: {}
paths:
/healthz:
get:
consumes:
- application/json
produces:
- application/json
responses:
"200":
description: OK
schema:
$ref: '#/definitions/HealthCheckResponse'
"500":
description: Internal Server Error
schema:
$ref: '#/definitions/ErrorResponse'
summary: Execute a health check
tags:
- health-check
/queue/{queue_id}/messages:
post:
consumes:
Expand Down
18 changes: 18 additions & 0 deletions domain/health_check.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
package domain

import "context"

// HealthCheck entity.
type HealthCheck struct {
Success bool `json:"success"`
}

// HealthCheckRepository is the repository interface for the HealthCheck entity.
type HealthCheckRepository interface {
Check(ctx context.Context) (*HealthCheck, error)
}

// HealthCheckService is the service interface for the HealthCheck entity.
type HealthCheckService interface {
Check(ctx context.Context) (*HealthCheck, error)
}
44 changes: 44 additions & 0 deletions http/health_check.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
package http

import (
"net/http"

"github.com/gin-gonic/gin"

"github.com/allisson/psqlqueue/domain"
)

// nolint:unused
type healthCheckResponse struct {
Success bool `json:"success"`
} //@name HealthCheckResponse

// HealthCheckHandler exposes a REST API for domain.HealthCheckService.
type HealthCheckHandler struct {
healthCheckService domain.HealthCheckService
}

// Execute a health check.
//
// @Summary Execute a health check
// @Tags health-check
// @Accept json
// @Produce json
// @Success 200 {object} healthCheckResponse
// @Failure 500 {object} errorResponse
// @Router /healthz [get]
func (h *HealthCheckHandler) Check(c *gin.Context) {
healthCheck, err := h.healthCheckService.Check(c.Request.Context())
if err != nil {
er := parseServiceError("healthCheckService", "Check", err)
c.JSON(er.StatusCode, &er)
return
}

c.JSON(http.StatusOK, &healthCheck)
}

// NewHealthCheckHandler returns a new HealthCheckHandler.
func NewHealthCheckHandler(healthCheckService domain.HealthCheckService) *HealthCheckHandler {
return &HealthCheckHandler{healthCheckService: healthCheckService}
}
27 changes: 27 additions & 0 deletions http/health_check_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
package http

import (
"net/http"
"net/http/httptest"
"testing"

"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/mock"

"github.com/allisson/psqlqueue/domain"
)

func TestHealthCheckHandler(t *testing.T) {
t.Run("Check", func(t *testing.T) {
expectedPayload := `{"success":true}`
tc := makeTestContext(t)
reqRec := httptest.NewRecorder()
req, _ := http.NewRequest("GET", "/v1/healthz", nil)

tc.healthCheckService.On("Check", mock.Anything).Return(&domain.HealthCheck{Success: true}, nil)
tc.router.ServeHTTP(reqRec, req)

assert.Equal(t, http.StatusOK, reqRec.Code)
assert.Equal(t, expectedPayload, reqRec.Body.String())
})
}
8 changes: 7 additions & 1 deletion http/queue_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,8 @@ type testContext struct {
topicHandler *TopicHandler
subscriptionService *mocks.SubscriptionService
subscriptionHandler *SubscriptionHandler
healthCheckService *mocks.HealthCheckService
healthCheckHandler *HealthCheckHandler
router *gin.Engine
}

Expand All @@ -37,7 +39,9 @@ func makeTestContext(t *testing.T) *testContext {
topicHandler := NewTopicHandler(topicService)
subscriptionService := mocks.NewSubscriptionService(t)
subscriptionHandler := NewSubscriptionHandler(subscriptionService)
router := SetupRouter(logger, queueHandler, messageHandler, topicHandler, subscriptionHandler)
healthCheckService := mocks.NewHealthCheckService(t)
healthCheckHandler := NewHealthCheckHandler(healthCheckService)
router := SetupRouter(logger, queueHandler, messageHandler, topicHandler, subscriptionHandler, healthCheckHandler)
return &testContext{
queueService: queueService,
queueHandler: queueHandler,
Expand All @@ -47,6 +51,8 @@ func makeTestContext(t *testing.T) *testContext {
topicHandler: topicHandler,
subscriptionService: subscriptionService,
subscriptionHandler: subscriptionHandler,
healthCheckService: healthCheckService,
healthCheckHandler: healthCheckHandler,
router: router,
}
}
Expand Down
5 changes: 4 additions & 1 deletion http/setup.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ import (
"github.com/allisson/psqlqueue/domain"
)

func SetupRouter(logger *slog.Logger, queueHandler *QueueHandler, messageHandler *MessageHandler, topicHandler *TopicHandler, subscriptionHandler *SubscriptionHandler) *gin.Engine {
func SetupRouter(logger *slog.Logger, queueHandler *QueueHandler, messageHandler *MessageHandler, topicHandler *TopicHandler, subscriptionHandler *SubscriptionHandler, healthCheckHandler *HealthCheckHandler) *gin.Engine {
// router setup
gin.SetMode(gin.ReleaseMode)
router := gin.New()
Expand Down Expand Up @@ -64,6 +64,9 @@ func SetupRouter(logger *slog.Logger, queueHandler *QueueHandler, messageHandler
v1.GET("/subscriptions", subscriptionHandler.List)
v1.DELETE("/subscriptions/:subscription_id", subscriptionHandler.Delete)

// health check handler
v1.GET("/healthz", healthCheckHandler.Check)

return router
}

Expand Down
59 changes: 59 additions & 0 deletions mocks/HealthCheckRepository.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Loading

0 comments on commit 241e495

Please sign in to comment.