LCOV - code coverage report
Current view: top level - adapter/http/handlerutil - error.go Coverage Total Hit
Test: coverage.lcov Lines: 94.7 % 38 36
Test Date: 2026-04-14 06:42:22 Functions: - 0 0

            Line data    Source code
       1              : // Package handlerutil は、Gin ベースの HTTP ハンドラで共通的に利用される
       2              : // エラーレスポンス整形や Request ID 解決などのユーティリティを提供します。
       3              : //
       4              : // 主な機能:
       5              : //   - WriteError: アプリケーション層のエラー(apperr.Error など)を
       6              : //     統一された JSON レスポンス形式に変換して返します。
       7              : //
       8              : // これにより、ハンドラごとの重複実装を避けつつ、
       9              : // 一貫したエラーレスポンス構造とトレーサビリティを確保できます。
      10              : package handlerutil
      11              : 
      12              : import (
      13              :         "errors"
      14              :         "fmt"
      15              :         "net/http"
      16              : 
      17              :         "github.com/gin-gonic/gin"
      18              :         v10 "github.com/go-playground/validator/v10"
      19              : 
      20              :         verrs "resume/internal/adapter/validation/errors" // ToDetails(err) を置いた所
      21              :         "resume/internal/shared/apperr"
      22              :         "resume/internal/shared/requestid"
      23              : )
      24              : 
      25              : // レスポンス形(error をトップにネストしている前提)
      26              : type errorBody struct {
      27              :         Code      string      `json:"code"`
      28              :         Message   string      `json:"message"`
      29              :         RequestID string      `json:"requestId,omitempty"`
      30              :         Details   interface{} `json:"details,omitempty"`
      31              : }
      32              : type errorEnvelope struct {
      33              :         Error errorBody `json:"error"`
      34              : }
      35              : 
      36              : // apperr.Code → HTTP ステータス(あなたの環境に HTTPStatusOf があるならそちらを使ってOK)
      37            3 : func httpStatusOf(code apperr.Code) int {
      38            3 :         return apperr.HTTPStatusOf(code)
      39            3 : }
      40              : 
      41              : // apperr.Code を安全に文字列化(String 実装があればそれが使われる)
      42            3 : func codeToString(code apperr.Code) string { return fmt.Sprint(code) }
      43              : 
      44              : // WriteError ハンドラ側は必ず `return` で呼び出しを終了してください(二重書き込み防止)
      45            3 : func WriteError(c *gin.Context, err error) {
      46            3 :         // デフォルト(予期せぬエラー)
      47            3 :         status := http.StatusInternalServerError
      48            3 :         body := errorEnvelope{
      49            3 :                 Error: errorBody{
      50            3 :                         Code:    "internal_error",
      51            3 :                         Message: "internal server error",
      52            3 :                         Details: nil,
      53            3 :                 },
      54            3 :         }
      55            3 : 
      56            3 :         // エラーの種類を判定して Details などだけ差し替える
      57            3 :         var ae *apperr.Error
      58            3 :         var verr v10.ValidationErrors
      59            3 : 
      60            3 :         switch {
      61            3 :         case errors.As(err, &ae):
      62            3 :                 status = httpStatusOf(ae.Code)
      63            3 :                 body.Error.Code = codeToString(ae.Code)
      64            3 :                 body.Error.Message = ae.Message
      65            3 :                 body.Error.Details = ae.Details
      66              : 
      67            1 :         case errors.As(err, &verr):
      68            1 :                 status = http.StatusUnprocessableEntity
      69            1 :                 body.Error.Code = "UNPROCESSABLE"
      70            1 :                 body.Error.Message = "The request contains semantically invalid data."
      71            1 :                 body.Error.Details = verrs.ToDetails(err)
      72              : 
      73            1 :         default:
      74              :                 // 必要なら err.Error() を details に入れてもOK(内部情報を出したくないなら nil のまま)
      75              :                 // body.Error.Details = map[string]any{"error": err.Error()}
      76              :         }
      77              : 
      78              :         // requestId を最終段で埋める(あなたの既存コードに合わせて body.Error.RequestID にセット)
      79            3 :         rid := requestid.Get(c)
      80            3 :         body.Error.RequestID = rid
      81            3 :         // 返却ヘッダにも反映したい場合(任意)
      82            3 :         if rid != "" && c.Writer.Header().Get("X-Request-Id") == "" {
      83            0 :                 c.Writer.Header().Set("X-Request-Id", rid)
      84            0 :         }
      85              : 
      86              :         // ★ 最後は必ず c.JSON で統一(Abort は使わない)
      87            3 :         c.JSON(status, body)
      88              : }
        

Generated by: LCOV version 2.3.1-1