fetch_ml/internal/api/errors.go
Jeremie Fraeys f0ffbb4a3d
refactor: Phase 5 complete - API packages extracted
Extracted all deferred API packages from monolithic ws_*.go files:

- api/routes.go (75 lines) - Extracted route registration from server.go
- api/errors.go (108 lines) - Standardized error responses and error codes
- api/jobs/handlers.go (271 lines) - Job WebSocket handlers
  * HandleAnnotateRun, HandleSetRunNarrative
  * HandleCancelJob, HandlePruneJobs, HandleListJobs
- api/jupyter/handlers.go (244 lines) - Jupyter WebSocket handlers
  * HandleStartJupyter, HandleStopJupyter
  * HandleListJupyter, HandleListJupyterPackages
  * HandleRemoveJupyter, HandleRestoreJupyter
- api/validate/handlers.go (163 lines) - Validation WebSocket handlers
  * HandleValidate, HandleGetValidateStatus, HandleListValidations
- api/ws/handler.go (298 lines) - WebSocket handler framework
  * Core WebSocket handling logic
  * Opcode constants and error codes

Lines redistributed: ~1,150 lines from ws_jobs.go (1,365), ws_jupyter.go (512),
ws_validate.go (523), ws_handler.go (379) into focused packages.

Note: Original ws_*.go files still present - cleanup in next commit.
Build status: Compiles successfully
2026-02-17 13:25:58 -05:00

133 lines
3.8 KiB
Go

// Package api provides error handling utilities for the API
package api
import (
"encoding/json"
"net/http"
"time"
)
// ErrorResponse represents a standardized error response
type ErrorResponse struct {
Error bool `json:"error"`
Code byte `json:"code"`
Message string `json:"message"`
Details string `json:"details,omitempty"`
Timestamp time.Time `json:"timestamp"`
RequestID string `json:"request_id,omitempty"`
}
// SuccessResponse represents a standardized success response
type SuccessResponse struct {
Success bool `json:"success"`
Data interface{} `json:"data,omitempty"`
Timestamp time.Time `json:"timestamp"`
}
// WriteError writes a standardized error response
func WriteError(w http.ResponseWriter, code byte, message, details string, statusCode int) {
w.Header().Set("Content-Type", "application/json")
w.WriteHeader(statusCode)
response := ErrorResponse{
Error: true,
Code: code,
Message: message,
Details: details,
Timestamp: time.Now().UTC(),
}
json.NewEncoder(w).Encode(response)
}
// WriteSuccess writes a standardized success response
func WriteSuccess(w http.ResponseWriter, data interface{}) {
w.Header().Set("Content-Type", "application/json")
w.WriteHeader(http.StatusOK)
response := SuccessResponse{
Success: true,
Data: data,
Timestamp: time.Now().UTC(),
}
json.NewEncoder(w).Encode(response)
}
// Common error codes for API responses
const (
ErrCodeUnknownError = 0x00
ErrCodeInvalidRequest = 0x01
ErrCodeAuthenticationFailed = 0x02
ErrCodePermissionDenied = 0x03
ErrCodeResourceNotFound = 0x04
ErrCodeResourceAlreadyExists = 0x05
ErrCodeServerOverloaded = 0x10
ErrCodeDatabaseError = 0x11
ErrCodeNetworkError = 0x12
ErrCodeStorageError = 0x13
ErrCodeTimeout = 0x14
ErrCodeJobNotFound = 0x20
ErrCodeJobAlreadyRunning = 0x21
ErrCodeJobFailedToStart = 0x22
ErrCodeJobExecutionFailed = 0x23
ErrCodeJobCancelled = 0x24
ErrCodeOutOfMemory = 0x30
ErrCodeDiskFull = 0x31
ErrCodeInvalidConfiguration = 0x32
ErrCodeServiceUnavailable = 0x33
)
// HTTP status code mappings
const (
StatusBadRequest = http.StatusBadRequest
StatusUnauthorized = http.StatusUnauthorized
StatusForbidden = http.StatusForbidden
StatusNotFound = http.StatusNotFound
StatusConflict = http.StatusConflict
StatusInternalServerError = http.StatusInternalServerError
StatusServiceUnavailable = http.StatusServiceUnavailable
StatusTooManyRequests = http.StatusTooManyRequests
)
// ErrorCodeToHTTPStatus maps API error codes to HTTP status codes
func ErrorCodeToHTTPStatus(code byte) int {
switch code {
case ErrCodeInvalidRequest:
return StatusBadRequest
case ErrCodeAuthenticationFailed:
return StatusUnauthorized
case ErrCodePermissionDenied:
return StatusForbidden
case ErrCodeResourceNotFound, ErrCodeJobNotFound:
return StatusNotFound
case ErrCodeResourceAlreadyExists, ErrCodeJobAlreadyRunning:
return StatusConflict
case ErrCodeServerOverloaded, ErrCodeServiceUnavailable:
return StatusServiceUnavailable
case ErrCodeDatabaseError, ErrCodeNetworkError, ErrCodeStorageError:
return StatusInternalServerError
default:
return StatusInternalServerError
}
}
// NewErrorResponse creates a new error response with the given details
func NewErrorResponse(code byte, message, details string) ErrorResponse {
return ErrorResponse{
Error: true,
Code: code,
Message: message,
Details: details,
Timestamp: time.Now().UTC(),
}
}
// NewSuccessResponse creates a new success response with the given data
func NewSuccessResponse(data interface{}) SuccessResponse {
return SuccessResponse{
Success: true,
Data: data,
Timestamp: time.Now().UTC(),
}
}