deployments.go 8.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294
  1. package handlers
  2. import (
  3. "fmt"
  4. "net/http"
  5. "strconv"
  6. "git.linuxforward.com/byop/byop-engine/dbstore"
  7. "git.linuxforward.com/byop/byop-engine/models"
  8. "github.com/gin-gonic/gin"
  9. )
  10. // DeploymentHandler handles deployment-related operations and contains integrated service logic
  11. type DeploymentHandler struct {
  12. store *dbstore.SQLiteStore
  13. }
  14. // NewDeploymentHandler creates a new DeploymentHandler
  15. func NewDeploymentHandler(store *dbstore.SQLiteStore) *DeploymentHandler {
  16. return &DeploymentHandler{
  17. store: store,
  18. }
  19. }
  20. // ListDeployments returns all deployments with optional filtering
  21. func (h *DeploymentHandler) ListDeployments(c *gin.Context) {
  22. filter := make(map[string]interface{})
  23. ctx := c.Request.Context()
  24. // Attempt to bind query parameters, but allow empty filters
  25. if err := c.ShouldBindQuery(&filter); err != nil && len(filter) > 0 {
  26. appErr := models.NewErrValidation("invalid_query_params", nil, err)
  27. models.RespondWithError(c, appErr)
  28. return
  29. }
  30. deployments, err := h.store.GetAllDeployments(ctx)
  31. if err != nil {
  32. appErr := models.NewErrInternalServer("failed_list_deployments", fmt.Errorf("Failed to list deployments: %w", err))
  33. models.RespondWithError(c, appErr)
  34. return
  35. }
  36. // If empty, return an empty list
  37. if len(deployments) == 0 {
  38. c.JSON(http.StatusOK, []models.Deployment{})
  39. return
  40. }
  41. c.JSON(http.StatusOK, deployments)
  42. }
  43. // CreateDeployment creates a new deployment
  44. func (h *DeploymentHandler) CreateDeployment(c *gin.Context) {
  45. var deployment models.Deployment
  46. ctx := c.Request.Context()
  47. if err := c.ShouldBindJSON(&deployment); err != nil {
  48. appErr := models.NewErrValidation("invalid_request_body", nil, err)
  49. models.RespondWithError(c, appErr)
  50. return
  51. }
  52. // Basic validation
  53. validationErrors := make(map[string]string)
  54. if deployment.AppId == 0 {
  55. validationErrors["app_id"] = "App ID is required"
  56. }
  57. if deployment.Environment == "" {
  58. validationErrors["environment"] = "Environment is required"
  59. }
  60. if len(validationErrors) > 0 {
  61. appErr := models.NewErrValidation("deployment_validation_failed", validationErrors, nil)
  62. models.RespondWithError(c, appErr)
  63. return
  64. }
  65. // Set default status
  66. if deployment.Status == "" {
  67. deployment.Status = "pending"
  68. }
  69. // TODO: Add complex deployment logic with cloud providers
  70. id, err := h.store.CreateDeployment(ctx, deployment)
  71. if err != nil {
  72. appErr := models.NewErrInternalServer("failed_create_deployment", fmt.Errorf("Failed to create deployment: %w", err))
  73. models.RespondWithError(c, appErr)
  74. return
  75. }
  76. // Set the generated ID
  77. deployment.ID = id
  78. c.JSON(http.StatusCreated, deployment)
  79. }
  80. // GetDeployment returns a specific deployment
  81. func (h *DeploymentHandler) GetDeployment(c *gin.Context) {
  82. idStr := c.Param("id")
  83. ctx := c.Request.Context()
  84. id, err := strconv.ParseInt(idStr, 10, 64)
  85. if err != nil {
  86. appErr := models.NewErrValidation("invalid_deployment_id_format", map[string]string{"id": "Invalid deployment ID format"}, err)
  87. models.RespondWithError(c, appErr)
  88. return
  89. }
  90. deployment, err := h.store.GetDeploymentByID(ctx, int(id))
  91. if err != nil {
  92. models.RespondWithError(c, err)
  93. return
  94. }
  95. c.JSON(http.StatusOK, deployment)
  96. }
  97. // UpdateDeployment updates a deployment
  98. func (h *DeploymentHandler) UpdateDeployment(c *gin.Context) {
  99. idStr := c.Param("id")
  100. ctx := c.Request.Context()
  101. id, err := strconv.ParseInt(idStr, 10, 64)
  102. if err != nil {
  103. appErr := models.NewErrValidation("invalid_deployment_id_format", map[string]string{"id": "Invalid deployment ID format"}, err)
  104. models.RespondWithError(c, appErr)
  105. return
  106. }
  107. var updatedDeployment models.Deployment
  108. if err := c.ShouldBindJSON(&updatedDeployment); err != nil {
  109. appErr := models.NewErrValidation("invalid_request_body", nil, err)
  110. models.RespondWithError(c, appErr)
  111. return
  112. }
  113. // Ensure the ID matches the URL parameter
  114. updatedDeployment.ID = int(id)
  115. // Basic validation for update
  116. validationErrors := make(map[string]string)
  117. if updatedDeployment.AppId == 0 {
  118. validationErrors["app_id"] = "App ID is required"
  119. }
  120. if updatedDeployment.Environment == "" {
  121. validationErrors["environment"] = "Environment is required"
  122. }
  123. if len(validationErrors) > 0 {
  124. appErr := models.NewErrValidation("deployment_update_validation_failed", validationErrors, nil)
  125. models.RespondWithError(c, appErr)
  126. return
  127. }
  128. if err := h.store.UpdateDeployment(ctx, &updatedDeployment); err != nil {
  129. models.RespondWithError(c, err)
  130. return
  131. }
  132. c.JSON(http.StatusOK, updatedDeployment)
  133. }
  134. // DeleteDeployment deletes a deployment
  135. func (h *DeploymentHandler) DeleteDeployment(c *gin.Context) {
  136. idStr := c.Param("id")
  137. ctx := c.Request.Context()
  138. id, err := strconv.ParseInt(idStr, 10, 64)
  139. if err != nil {
  140. appErr := models.NewErrValidation("invalid_deployment_id_format", map[string]string{"id": "Invalid deployment ID format"}, err)
  141. models.RespondWithError(c, appErr)
  142. return
  143. }
  144. if err := h.store.DeleteDeployment(ctx, int(id)); err != nil {
  145. models.RespondWithError(c, err)
  146. return
  147. }
  148. c.JSON(http.StatusOK, gin.H{"message": "Deployment deleted successfully"})
  149. }
  150. // UpdateDeploymentStatus updates the status of a deployment
  151. func (h *DeploymentHandler) UpdateDeploymentStatus(c *gin.Context) {
  152. idStr := c.Param("id")
  153. ctx := c.Request.Context()
  154. id, err := strconv.ParseInt(idStr, 10, 64)
  155. if err != nil {
  156. appErr := models.NewErrValidation("invalid_deployment_id_format", map[string]string{"id": "Invalid deployment ID format"}, err)
  157. models.RespondWithError(c, appErr)
  158. return
  159. }
  160. var statusUpdate struct {
  161. Status string `json:"status" binding:"required"`
  162. }
  163. if err := c.ShouldBindJSON(&statusUpdate); err != nil {
  164. appErr := models.NewErrValidation("invalid_status_update_body", nil, err)
  165. models.RespondWithError(c, appErr)
  166. return
  167. }
  168. // Get current deployment
  169. deployment, err := h.store.GetDeploymentByID(ctx, int(id))
  170. if err != nil {
  171. models.RespondWithError(c, err)
  172. return
  173. }
  174. // Update the status
  175. deployment.Status = statusUpdate.Status
  176. if err := h.store.UpdateDeployment(ctx, deployment); err != nil {
  177. if _, ok := err.(models.CustomError); !ok {
  178. err = models.NewErrInternalServer("failed_update_deployment_status", fmt.Errorf("Failed to update deployment status: %w", err))
  179. }
  180. models.RespondWithError(c, err)
  181. return
  182. }
  183. c.Status(http.StatusOK)
  184. }
  185. // GetDeploymentsByClient returns all deployments for a specific client
  186. func (h *DeploymentHandler) GetDeploymentsByClient(c *gin.Context) {
  187. clientIDStr := c.Param("clientId")
  188. ctx := c.Request.Context()
  189. clientID, err := strconv.ParseInt(clientIDStr, 10, 64)
  190. if err != nil {
  191. appErr := models.NewErrValidation("invalid_client_id_format", map[string]string{"clientId": "Invalid client ID format"}, err)
  192. models.RespondWithError(c, appErr)
  193. return
  194. }
  195. deployments, err := h.store.GetDeploymentsByClientID(ctx, int(clientID))
  196. if err != nil {
  197. if _, ok := err.(models.CustomError); !ok {
  198. err = models.NewErrInternalServer("failed_fetch_deployments_by_client", fmt.Errorf("Failed to fetch deployments for client %d: %w", clientID, err))
  199. }
  200. models.RespondWithError(c, err)
  201. return
  202. }
  203. c.JSON(http.StatusOK, deployments)
  204. }
  205. // GetDeploymentsByApp returns all deployments for a specific app
  206. func (h *DeploymentHandler) GetDeploymentsByApp(c *gin.Context) {
  207. appIDStr := c.Param("appId")
  208. ctx := c.Request.Context()
  209. appID, err := strconv.ParseInt(appIDStr, 10, 64)
  210. if err != nil {
  211. appErr := models.NewErrValidation("invalid_app_id_format", map[string]string{"appId": "Invalid app ID format"}, err)
  212. models.RespondWithError(c, appErr)
  213. return
  214. }
  215. deployments, err := h.store.GetDeploymentsByAppID(ctx, int(appID))
  216. if err != nil {
  217. if _, ok := err.(models.CustomError); !ok {
  218. err = models.NewErrInternalServer("failed_fetch_deployments_by_app", fmt.Errorf("Failed to fetch deployments for app %d: %w", appID, err))
  219. }
  220. models.RespondWithError(c, err)
  221. return
  222. }
  223. c.JSON(http.StatusOK, deployments)
  224. }
  225. // GetDeploymentsByUser returns all deployments created by a specific user
  226. func (h *DeploymentHandler) GetDeploymentsByUser(c *gin.Context) {
  227. userIDStr := c.Param("userId")
  228. ctx := c.Request.Context()
  229. userID, err := strconv.ParseInt(userIDStr, 10, 64)
  230. if err != nil {
  231. appErr := models.NewErrValidation("invalid_user_id_format", map[string]string{"userId": "Invalid user ID format"}, err)
  232. models.RespondWithError(c, appErr)
  233. return
  234. }
  235. deployments, err := h.store.GetDeploymentsByUserID(ctx, int(userID))
  236. if err != nil {
  237. if _, ok := err.(models.CustomError); !ok {
  238. err = models.NewErrInternalServer("failed_fetch_deployments_by_user", fmt.Errorf("Failed to fetch deployments for user %d: %w", userID, err))
  239. }
  240. models.RespondWithError(c, err)
  241. return
  242. }
  243. c.JSON(http.StatusOK, deployments)
  244. }