Add monitoring service and database schema
- Implement monitoring service with heartbeat, lease tasks, resume tasks, and task result handling. - Create monitoring time utilities for business date calculations. - Add unit tests for date window resolution and business day handling. - Define database schema for monitoring-related tables including quotas, daily reports, and task management. - Establish migration scripts for creating and dropping monitoring tables.
This commit is contained in:
@@ -0,0 +1,162 @@
|
||||
package transport
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"io"
|
||||
"strconv"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
|
||||
"github.com/geo-platform/tenant-api/internal/bootstrap"
|
||||
"github.com/geo-platform/tenant-api/internal/shared/response"
|
||||
"github.com/geo-platform/tenant-api/internal/tenant/app"
|
||||
)
|
||||
|
||||
type MonitoringCallbackHandler struct {
|
||||
svc *app.MonitoringCallbackService
|
||||
}
|
||||
|
||||
func NewMonitoringCallbackHandler(a *bootstrap.App) *MonitoringCallbackHandler {
|
||||
return &MonitoringCallbackHandler{
|
||||
svc: app.NewMonitoringCallbackService(a.DB, a.MonitoringDB, a.RabbitMQ, a.LLM, a.Logger),
|
||||
}
|
||||
}
|
||||
|
||||
func (h *MonitoringCallbackHandler) Heartbeat(c *gin.Context) {
|
||||
installationID, installationToken, ok := parseMonitoringInstallationHeaders(c)
|
||||
if !ok {
|
||||
return
|
||||
}
|
||||
|
||||
var req app.MonitoringHeartbeatRequest
|
||||
if err := bindOptionalJSON(c, &req); err != nil {
|
||||
response.Error(c, response.ErrBadRequest(40041, "invalid_params", err.Error()))
|
||||
return
|
||||
}
|
||||
|
||||
data, svcErr := h.svc.Heartbeat(c.Request.Context(), installationID, installationToken, req)
|
||||
if svcErr != nil {
|
||||
response.Error(c, svcErr)
|
||||
return
|
||||
}
|
||||
response.Success(c, data)
|
||||
}
|
||||
|
||||
func (h *MonitoringCallbackHandler) LeaseTasks(c *gin.Context) {
|
||||
installationID, installationToken, ok := parseMonitoringInstallationHeaders(c)
|
||||
if !ok {
|
||||
return
|
||||
}
|
||||
|
||||
var req app.MonitoringLeaseTasksRequest
|
||||
if err := bindOptionalJSON(c, &req); err != nil {
|
||||
response.Error(c, response.ErrBadRequest(40041, "invalid_params", err.Error()))
|
||||
return
|
||||
}
|
||||
|
||||
data, svcErr := h.svc.LeaseTasks(c.Request.Context(), installationID, installationToken, req)
|
||||
if svcErr != nil {
|
||||
response.Error(c, svcErr)
|
||||
return
|
||||
}
|
||||
response.Success(c, data)
|
||||
}
|
||||
|
||||
func (h *MonitoringCallbackHandler) ResumeTasks(c *gin.Context) {
|
||||
installationID, installationToken, ok := parseMonitoringInstallationHeaders(c)
|
||||
if !ok {
|
||||
return
|
||||
}
|
||||
|
||||
var req app.MonitoringResumeTasksRequest
|
||||
if err := bindOptionalJSON(c, &req); err != nil {
|
||||
response.Error(c, response.ErrBadRequest(40041, "invalid_params", err.Error()))
|
||||
return
|
||||
}
|
||||
|
||||
data, svcErr := h.svc.ResumeTasks(c.Request.Context(), installationID, installationToken, req)
|
||||
if svcErr != nil {
|
||||
response.Error(c, svcErr)
|
||||
return
|
||||
}
|
||||
response.Success(c, data)
|
||||
}
|
||||
|
||||
func (h *MonitoringCallbackHandler) TaskResult(c *gin.Context) {
|
||||
taskID, err := strconv.ParseInt(c.Param("id"), 10, 64)
|
||||
if err != nil {
|
||||
response.Error(c, response.ErrBadRequest(40041, "invalid_task_id", "task id must be a number"))
|
||||
return
|
||||
}
|
||||
|
||||
installationID, installationToken, ok := parseMonitoringInstallationHeaders(c)
|
||||
if !ok {
|
||||
return
|
||||
}
|
||||
|
||||
var req app.MonitoringTaskResultRequest
|
||||
if err := c.ShouldBindJSON(&req); err != nil {
|
||||
response.Error(c, response.ErrBadRequest(40041, "invalid_params", err.Error()))
|
||||
return
|
||||
}
|
||||
|
||||
data, svcErr := h.svc.HandleTaskResult(c.Request.Context(), installationID, installationToken, taskID, req)
|
||||
if svcErr != nil {
|
||||
response.Error(c, svcErr)
|
||||
return
|
||||
}
|
||||
response.Success(c, data)
|
||||
}
|
||||
|
||||
func (h *MonitoringCallbackHandler) SkipTask(c *gin.Context) {
|
||||
taskID, err := strconv.ParseInt(c.Param("id"), 10, 64)
|
||||
if err != nil {
|
||||
response.Error(c, response.ErrBadRequest(40041, "invalid_task_id", "task id must be a number"))
|
||||
return
|
||||
}
|
||||
|
||||
installationID, installationToken, ok := parseMonitoringInstallationHeaders(c)
|
||||
if !ok {
|
||||
return
|
||||
}
|
||||
|
||||
var req app.MonitoringSkipTaskRequest
|
||||
if err := c.ShouldBindJSON(&req); err != nil {
|
||||
response.Error(c, response.ErrBadRequest(40041, "invalid_params", err.Error()))
|
||||
return
|
||||
}
|
||||
|
||||
data, svcErr := h.svc.SkipTask(c.Request.Context(), installationID, installationToken, taskID, req)
|
||||
if svcErr != nil {
|
||||
response.Error(c, svcErr)
|
||||
return
|
||||
}
|
||||
response.Success(c, data)
|
||||
}
|
||||
|
||||
func parseMonitoringInstallationHeaders(c *gin.Context) (int64, string, bool) {
|
||||
installationID, err := strconv.ParseInt(c.GetHeader("X-Geo-Installation-Id"), 10, 64)
|
||||
if err != nil {
|
||||
response.Error(c, response.ErrUnauthorized(40141, "invalid_installation_id", "installation id header is invalid"))
|
||||
return 0, "", false
|
||||
}
|
||||
|
||||
installationToken := c.GetHeader("X-Geo-Installation-Token")
|
||||
if installationToken == "" {
|
||||
response.Error(c, response.ErrUnauthorized(40141, "missing_installation_token", "installation token header is required"))
|
||||
return 0, "", false
|
||||
}
|
||||
|
||||
return installationID, installationToken, true
|
||||
}
|
||||
|
||||
func bindOptionalJSON(c *gin.Context, target interface{}) error {
|
||||
err := c.ShouldBindJSON(target)
|
||||
if err == nil {
|
||||
return nil
|
||||
}
|
||||
if errors.Is(err, io.EOF) {
|
||||
return nil
|
||||
}
|
||||
return err
|
||||
}
|
||||
Reference in New Issue
Block a user