package handlers import ( "fmt" "net/http" "strconv" "git.linuxforward.com/byop/byop-engine/dbstore" "git.linuxforward.com/byop/byop-engine/models" "github.com/gin-gonic/gin" ) // DeploymentHandler handles deployment-related operations and contains integrated service logic type DeploymentHandler struct { store *dbstore.SQLiteStore } // NewDeploymentHandler creates a new DeploymentHandler func NewDeploymentHandler(store *dbstore.SQLiteStore) *DeploymentHandler { return &DeploymentHandler{ store: store, } } // ListDeployments returns all deployments with optional filtering func (h *DeploymentHandler) ListDeployments(c *gin.Context) { filter := make(map[string]interface{}) ctx := c.Request.Context() // Attempt to bind query parameters, but allow empty filters if err := c.ShouldBindQuery(&filter); err != nil && len(filter) > 0 { appErr := models.NewErrValidation("invalid_query_params", nil, err) models.RespondWithError(c, appErr) return } deployments, err := h.store.GetAllDeployments(ctx) if err != nil { appErr := models.NewErrInternalServer("failed_list_deployments", fmt.Errorf("Failed to list deployments: %w", err)) models.RespondWithError(c, appErr) return } // If empty, return an empty list if len(deployments) == 0 { c.JSON(http.StatusOK, []models.Deployment{}) return } c.JSON(http.StatusOK, deployments) } // CreateDeployment creates a new deployment func (h *DeploymentHandler) CreateDeployment(c *gin.Context) { var deployment models.Deployment ctx := c.Request.Context() if err := c.ShouldBindJSON(&deployment); err != nil { appErr := models.NewErrValidation("invalid_request_body", nil, err) models.RespondWithError(c, appErr) return } // Basic validation validationErrors := make(map[string]string) if deployment.AppId == 0 { validationErrors["app_id"] = "App ID is required" } if deployment.Environment == "" { validationErrors["environment"] = "Environment is required" } if len(validationErrors) > 0 { appErr := models.NewErrValidation("deployment_validation_failed", validationErrors, nil) models.RespondWithError(c, appErr) return } // Set default status if deployment.Status == "" { deployment.Status = "pending" } // TODO: Add complex deployment logic with cloud providers id, err := h.store.CreateDeployment(ctx, deployment) if err != nil { appErr := models.NewErrInternalServer("failed_create_deployment", fmt.Errorf("Failed to create deployment: %w", err)) models.RespondWithError(c, appErr) return } // Set the generated ID deployment.ID = id c.JSON(http.StatusCreated, deployment) } // GetDeployment returns a specific deployment func (h *DeploymentHandler) GetDeployment(c *gin.Context) { idStr := c.Param("id") ctx := c.Request.Context() id, err := strconv.ParseInt(idStr, 10, 64) if err != nil { appErr := models.NewErrValidation("invalid_deployment_id_format", map[string]string{"id": "Invalid deployment ID format"}, err) models.RespondWithError(c, appErr) return } deployment, err := h.store.GetDeploymentByID(ctx, int(id)) if err != nil { models.RespondWithError(c, err) return } c.JSON(http.StatusOK, deployment) } // UpdateDeployment updates a deployment func (h *DeploymentHandler) UpdateDeployment(c *gin.Context) { idStr := c.Param("id") ctx := c.Request.Context() id, err := strconv.ParseInt(idStr, 10, 64) if err != nil { appErr := models.NewErrValidation("invalid_deployment_id_format", map[string]string{"id": "Invalid deployment ID format"}, err) models.RespondWithError(c, appErr) return } var updatedDeployment models.Deployment if err := c.ShouldBindJSON(&updatedDeployment); err != nil { appErr := models.NewErrValidation("invalid_request_body", nil, err) models.RespondWithError(c, appErr) return } // Ensure the ID matches the URL parameter updatedDeployment.ID = int(id) // Basic validation for update validationErrors := make(map[string]string) if updatedDeployment.AppId == 0 { validationErrors["app_id"] = "App ID is required" } if updatedDeployment.Environment == "" { validationErrors["environment"] = "Environment is required" } if len(validationErrors) > 0 { appErr := models.NewErrValidation("deployment_update_validation_failed", validationErrors, nil) models.RespondWithError(c, appErr) return } if err := h.store.UpdateDeployment(ctx, &updatedDeployment); err != nil { models.RespondWithError(c, err) return } c.JSON(http.StatusOK, updatedDeployment) } // DeleteDeployment deletes a deployment func (h *DeploymentHandler) DeleteDeployment(c *gin.Context) { idStr := c.Param("id") ctx := c.Request.Context() id, err := strconv.ParseInt(idStr, 10, 64) if err != nil { appErr := models.NewErrValidation("invalid_deployment_id_format", map[string]string{"id": "Invalid deployment ID format"}, err) models.RespondWithError(c, appErr) return } if err := h.store.DeleteDeployment(ctx, int(id)); err != nil { models.RespondWithError(c, err) return } c.JSON(http.StatusOK, gin.H{"message": "Deployment deleted successfully"}) } // UpdateDeploymentStatus updates the status of a deployment func (h *DeploymentHandler) UpdateDeploymentStatus(c *gin.Context) { idStr := c.Param("id") ctx := c.Request.Context() id, err := strconv.ParseInt(idStr, 10, 64) if err != nil { appErr := models.NewErrValidation("invalid_deployment_id_format", map[string]string{"id": "Invalid deployment ID format"}, err) models.RespondWithError(c, appErr) return } var statusUpdate struct { Status string `json:"status" binding:"required"` } if err := c.ShouldBindJSON(&statusUpdate); err != nil { appErr := models.NewErrValidation("invalid_status_update_body", nil, err) models.RespondWithError(c, appErr) return } // Get current deployment deployment, err := h.store.GetDeploymentByID(ctx, int(id)) if err != nil { models.RespondWithError(c, err) return } // Update the status deployment.Status = statusUpdate.Status if err := h.store.UpdateDeployment(ctx, deployment); err != nil { if _, ok := err.(models.CustomError); !ok { err = models.NewErrInternalServer("failed_update_deployment_status", fmt.Errorf("Failed to update deployment status: %w", err)) } models.RespondWithError(c, err) return } c.Status(http.StatusOK) } // GetDeploymentsByClient returns all deployments for a specific client func (h *DeploymentHandler) GetDeploymentsByClient(c *gin.Context) { clientIDStr := c.Param("clientId") ctx := c.Request.Context() clientID, err := strconv.ParseInt(clientIDStr, 10, 64) if err != nil { appErr := models.NewErrValidation("invalid_client_id_format", map[string]string{"clientId": "Invalid client ID format"}, err) models.RespondWithError(c, appErr) return } deployments, err := h.store.GetDeploymentsByClientID(ctx, int(clientID)) if err != nil { if _, ok := err.(models.CustomError); !ok { err = models.NewErrInternalServer("failed_fetch_deployments_by_client", fmt.Errorf("Failed to fetch deployments for client %d: %w", clientID, err)) } models.RespondWithError(c, err) return } c.JSON(http.StatusOK, deployments) } // GetDeploymentsByApp returns all deployments for a specific app func (h *DeploymentHandler) GetDeploymentsByApp(c *gin.Context) { appIDStr := c.Param("appId") ctx := c.Request.Context() appID, err := strconv.ParseInt(appIDStr, 10, 64) if err != nil { appErr := models.NewErrValidation("invalid_app_id_format", map[string]string{"appId": "Invalid app ID format"}, err) models.RespondWithError(c, appErr) return } deployments, err := h.store.GetDeploymentsByAppID(ctx, int(appID)) if err != nil { if _, ok := err.(models.CustomError); !ok { err = models.NewErrInternalServer("failed_fetch_deployments_by_app", fmt.Errorf("Failed to fetch deployments for app %d: %w", appID, err)) } models.RespondWithError(c, err) return } c.JSON(http.StatusOK, deployments) } // GetDeploymentsByUser returns all deployments created by a specific user func (h *DeploymentHandler) GetDeploymentsByUser(c *gin.Context) { userIDStr := c.Param("userId") ctx := c.Request.Context() userID, err := strconv.ParseInt(userIDStr, 10, 64) if err != nil { appErr := models.NewErrValidation("invalid_user_id_format", map[string]string{"userId": "Invalid user ID format"}, err) models.RespondWithError(c, appErr) return } deployments, err := h.store.GetDeploymentsByUserID(ctx, int(userID)) if err != nil { if _, ok := err.(models.CustomError); !ok { err = models.NewErrInternalServer("failed_fetch_deployments_by_user", fmt.Errorf("Failed to fetch deployments for user %d: %w", userID, err)) } models.RespondWithError(c, err) return } c.JSON(http.StatusOK, deployments) }