2
0
Files
gitcaddy-server/modules/errors/api_error.go
logikonline 9094d8b503 feat(api): add v2 API with AI-friendly features (Phase 2)
This introduces a new v2 API at /api/v2/ with features designed for
AI agents and automation tools while maintaining full backward
compatibility with the existing v1 API.

New features:
- Structured error codes (70+ machine-readable codes) for precise
  error handling by automated tools
- Scalar API documentation at /api/v2/docs (modern replacement for
  Swagger UI)
- Batch operations for bulk file and repository fetching
- NDJSON streaming endpoints for files, commits, and issues
- AI context endpoints providing rich repository summaries,
  navigation hints, and issue context

Files added:
- modules/errors/codes.go - Error code definitions and catalog
- modules/errors/api_error.go - Rich API error response builder
- routers/api/v2/api.go - v2 router with auth middleware
- routers/api/v2/docs.go - Scalar docs and OpenAPI spec
- routers/api/v2/batch.go - Batch file/repo operations
- routers/api/v2/streaming.go - NDJSON streaming endpoints
- routers/api/v2/ai_context.go - AI context endpoints
- routers/api/v2/misc.go - Version and user endpoints

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-09 11:41:10 -05:00

130 lines
3.5 KiB
Go

// Copyright 2026 The Gitea Authors. All rights reserved.
// SPDX-License-Identifier: MIT
package errors
import (
"fmt"
"code.gitea.io/gitea/modules/setting"
)
// APIErrorResponse is the top-level error response wrapper
type APIErrorResponse struct {
Error *APIError `json:"error"`
}
// APIError represents a structured API error following RFC 7807 Problem Details
// with additional fields for AI-friendly error handling
type APIError struct {
// Machine-readable error code (e.g., "REPO_NOT_FOUND")
Code string `json:"code"`
// Human-readable error message
Message string `json:"message"`
// HTTP status code
Status int `json:"status"`
// Additional context about the error
Details map[string]any `json:"details,omitempty"`
// URL to documentation about this error
DocumentationURL string `json:"documentation_url,omitempty"`
// Unique request ID for tracing
RequestID string `json:"request_id,omitempty"`
// Suggested actions or alternatives
Suggestions []string `json:"suggestions,omitempty"`
// RFC 7807 Problem Details fields
Type string `json:"type,omitempty"` // URI reference identifying the problem type
Title string `json:"title,omitempty"` // Short summary of the problem type
Instance string `json:"instance,omitempty"` // URI reference for this specific occurrence
}
// NewAPIError creates a new structured API error
func NewAPIError(code ErrorCode, requestID string) *APIError {
docURL := fmt.Sprintf("%s/api/errors#%s", setting.AppURL, code)
return &APIError{
Code: code.String(),
Message: code.Message(),
Status: code.HTTPStatus(),
DocumentationURL: docURL,
RequestID: requestID,
Type: "about:blank",
Title: code.Message(),
Instance: requestID,
}
}
// WithDetails adds details to the error
func (e *APIError) WithDetails(details map[string]any) *APIError {
e.Details = details
return e
}
// WithDetail adds a single detail to the error
func (e *APIError) WithDetail(key string, value any) *APIError {
if e.Details == nil {
e.Details = make(map[string]any)
}
e.Details[key] = value
return e
}
// WithMessage overrides the default message
func (e *APIError) WithMessage(message string) *APIError {
e.Message = message
e.Title = message
return e
}
// WithSuggestions adds suggested actions
func (e *APIError) WithSuggestions(suggestions ...string) *APIError {
e.Suggestions = append(e.Suggestions, suggestions...)
return e
}
// Response wraps the error in an APIErrorResponse
func (e *APIError) Response() *APIErrorResponse {
return &APIErrorResponse{Error: e}
}
// ValidationError represents a field-level validation error
type ValidationError struct {
Field string `json:"field"`
Message string `json:"message"`
Code string `json:"code,omitempty"`
}
// APIValidationError represents a validation error with field-level details
type APIValidationError struct {
*APIError
Errors []ValidationError `json:"errors,omitempty"`
}
// NewValidationError creates a new validation error
func NewValidationError(requestID string, errors ...ValidationError) *APIValidationError {
baseErr := NewAPIError(ValInvalidInput, requestID)
return &APIValidationError{
APIError: baseErr,
Errors: errors,
}
}
// AddFieldError adds a field validation error
func (e *APIValidationError) AddFieldError(field, message string, code ...string) *APIValidationError {
ve := ValidationError{
Field: field,
Message: message,
}
if len(code) > 0 {
ve.Code = code[0]
}
e.Errors = append(e.Errors, ve)
return e
}