penambahan All case Satu sehat
This commit is contained in:
+39
-15
@@ -13,17 +13,19 @@ import (
|
||||
|
||||
// HTTPErrorResponse represents standardized HTTP error response
|
||||
type HTTPErrorResponse struct {
|
||||
Error struct {
|
||||
Code string `json:"code"`
|
||||
Message string `json:"message"`
|
||||
Details map[string]interface{} `json:"details,omitempty"`
|
||||
RequestID string `json:"request_id,omitempty"`
|
||||
Timestamp string `json:"timestamp"`
|
||||
Retryable bool `json:"retryable,omitempty"`
|
||||
Status string `json:"status"`
|
||||
Error struct {
|
||||
Code string `json:"code"`
|
||||
Details map[string]interface{} `json:"details"`
|
||||
Message string `json:"message"`
|
||||
RequestID string `json:"request_id"`
|
||||
Retryable bool `json:"retryable"`
|
||||
StackTrace interface{} `json:"stack_trace"`
|
||||
Timestamp string `json:"timestamp"`
|
||||
} `json:"error"`
|
||||
Meta struct {
|
||||
HTTPStatus int `json:"http_status"`
|
||||
Category string `json:"category"`
|
||||
HTTPStatus int `json:"http_status"`
|
||||
} `json:"meta,omitempty"`
|
||||
}
|
||||
|
||||
@@ -50,18 +52,30 @@ func HandleHTTPError(c *gin.Context, err error) {
|
||||
|
||||
// Create error response
|
||||
response := &HTTPErrorResponse{}
|
||||
response.Status = "error"
|
||||
response.Error.Code = appErr.Code()
|
||||
response.Error.Message = appErr.GetLocalizedMessage(getLanguageFromContext(c))
|
||||
response.Error.Details = appErr.Metadata()
|
||||
if response.Error.Details == nil {
|
||||
response.Error.Details = make(map[string]interface{})
|
||||
}
|
||||
response.Error.Message = appErr.GetLocalizedMessage(getLanguageFromContext(c))
|
||||
response.Error.RequestID = c.GetString("request_id")
|
||||
response.Error.Timestamp = appErr.Metadata()["timestamp"].(string)
|
||||
response.Error.Retryable = IsRetryable(appErr.Code())
|
||||
response.Meta.HTTPStatus = appErr.HTTPStatus()
|
||||
response.Error.StackTrace = nil
|
||||
if ts, ok := appErr.Metadata()["timestamp"].(string); ok {
|
||||
response.Error.Timestamp = ts
|
||||
}
|
||||
response.Meta.Category = appErr.Category()
|
||||
response.Meta.HTTPStatus = appErr.HTTPStatus()
|
||||
|
||||
// Log error
|
||||
LogHTTPError(c, appErr)
|
||||
|
||||
// Cegah print ganda jika controller sudah print
|
||||
if c.Writer.Written() {
|
||||
return
|
||||
}
|
||||
|
||||
// Send response
|
||||
c.JSON(appErr.HTTPStatus(), response)
|
||||
}
|
||||
@@ -120,13 +134,16 @@ func ValidationErrorHandler(c *gin.Context, err error) {
|
||||
if appErr.Category() == CategoryValidation {
|
||||
if details, ok := appErr.Metadata()["validation_errors"]; ok {
|
||||
response := gin.H{
|
||||
"status": "error",
|
||||
"error": gin.H{
|
||||
"code": appErr.Code(),
|
||||
"message": appErr.GetLocalizedMessage(getLanguageFromContext(c)),
|
||||
"fields": details,
|
||||
},
|
||||
}
|
||||
c.JSON(http.StatusBadRequest, response)
|
||||
if !c.Writer.Written() {
|
||||
c.JSON(http.StatusBadRequest, response)
|
||||
}
|
||||
return
|
||||
}
|
||||
}
|
||||
@@ -158,13 +175,20 @@ func WriteErrorResponse(w http.ResponseWriter, err error) {
|
||||
}
|
||||
|
||||
response := &HTTPErrorResponse{}
|
||||
response.Status = "error"
|
||||
response.Error.Code = appErr.Code()
|
||||
response.Error.Message = appErr.GetLocalizedMessage("en")
|
||||
response.Error.Details = appErr.Metadata()
|
||||
response.Error.Timestamp = appErr.Metadata()["timestamp"].(string)
|
||||
if response.Error.Details == nil {
|
||||
response.Error.Details = make(map[string]interface{})
|
||||
}
|
||||
response.Error.Message = appErr.GetLocalizedMessage("en")
|
||||
response.Error.Retryable = IsRetryable(appErr.Code())
|
||||
response.Meta.HTTPStatus = appErr.HTTPStatus()
|
||||
response.Error.StackTrace = nil
|
||||
if ts, ok := appErr.Metadata()["timestamp"].(string); ok {
|
||||
response.Error.Timestamp = ts
|
||||
}
|
||||
response.Meta.Category = appErr.Category()
|
||||
response.Meta.HTTPStatus = appErr.HTTPStatus()
|
||||
|
||||
w.Header().Set("Content-Type", "application/json")
|
||||
w.WriteHeader(appErr.HTTPStatus())
|
||||
|
||||
@@ -247,7 +247,9 @@ func handleErrors(c *gin.Context, err error, cfg *ErrorMiddlewareConfig) {
|
||||
response := createErrorResponse(c, appErr, cfg)
|
||||
|
||||
// Send response
|
||||
c.JSON(appErr.HTTPStatus(), response)
|
||||
if !c.Writer.Written() {
|
||||
c.JSON(appErr.HTTPStatus(), response)
|
||||
}
|
||||
}
|
||||
|
||||
// createErrorResponse creates error response
|
||||
@@ -255,18 +257,30 @@ func createErrorResponse(c *gin.Context, err Error, cfg *ErrorMiddlewareConfig)
|
||||
lang := c.GetString("language")
|
||||
requestID := c.GetString("request_id")
|
||||
|
||||
details := err.Metadata()
|
||||
if details == nil {
|
||||
details = make(map[string]interface{})
|
||||
}
|
||||
|
||||
timestamp := time.Now().UTC().Format(time.RFC3339)
|
||||
if ts, ok := details["timestamp"].(string); ok {
|
||||
timestamp = ts
|
||||
}
|
||||
|
||||
response := gin.H{
|
||||
"status": "error",
|
||||
"error": gin.H{
|
||||
"code": err.Code(),
|
||||
"message": err.GetLocalizedMessage(lang),
|
||||
"details": err.Metadata(),
|
||||
"request_id": requestID,
|
||||
"timestamp": time.Now().UTC().Format(time.RFC3339),
|
||||
"retryable": IsRetryable(err.Code()),
|
||||
"code": err.Code(),
|
||||
"details": details,
|
||||
"message": err.GetLocalizedMessage(lang),
|
||||
"request_id": requestID,
|
||||
"retryable": IsRetryable(err.Code()),
|
||||
"stack_trace": nil,
|
||||
"timestamp": timestamp,
|
||||
},
|
||||
"meta": gin.H{
|
||||
"http_status": err.HTTPStatus(),
|
||||
"category": err.Category(),
|
||||
"http_status": err.HTTPStatus(),
|
||||
},
|
||||
}
|
||||
|
||||
@@ -302,13 +316,16 @@ func ValidationHandler(c *gin.Context, err error) {
|
||||
if appErr.Category() == CategoryValidation {
|
||||
if details, ok := appErr.Metadata()["validation_errors"]; ok {
|
||||
response := gin.H{
|
||||
"status": "error",
|
||||
"error": gin.H{
|
||||
"code": appErr.Code(),
|
||||
"message": appErr.GetLocalizedMessage(getLanguageFromContext(c)),
|
||||
"fields": details,
|
||||
},
|
||||
}
|
||||
c.JSON(http.StatusBadRequest, response)
|
||||
if !c.Writer.Written() {
|
||||
c.JSON(http.StatusBadRequest, response)
|
||||
}
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,43 @@
|
||||
package errors
|
||||
|
||||
import (
|
||||
"strings"
|
||||
)
|
||||
|
||||
// ParseSatuSehatError mengekstrak dan menerjemahkan balasan OperationOutcome
|
||||
// dari Kemenkes (SatuSehat) menjadi standard AppError aplikasi.
|
||||
func ParseSatuSehatError(result map[string]interface{}) Error {
|
||||
errMsg := "Gagal memproses data ke SatuSehat"
|
||||
var errMessages []string
|
||||
|
||||
// Ekstrak pesan dari array "issue"
|
||||
if issues, ok := result["issue"].([]interface{}); ok && len(issues) > 0 {
|
||||
for _, issueItem := range issues {
|
||||
if issue, ok := issueItem.(map[string]interface{}); ok {
|
||||
if diagnostics, ok := issue["diagnostics"].(string); ok {
|
||||
diagLower := strings.ToLower(diagnostics)
|
||||
|
||||
// Translasi pesan error spesifik SatuSehat
|
||||
if strings.Contains(diagLower, "reference target(s) not found") {
|
||||
errMessages = append(errMessages, "Data referensi (seperti ID) tidak ditemukan di SatuSehat")
|
||||
} else if strings.Contains(diagLower, "nik") {
|
||||
errMessages = append(errMessages, "NIK tidak valid atau tidak terdaftar di SatuSehat")
|
||||
} else {
|
||||
errMessages = append(errMessages, diagnostics) // Tampilkan pesan aslinya jika belum ada di kamus translasi
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Gabungkan semua error (jika > 1) dengan pemisah koma atau titik koma
|
||||
if len(errMessages) > 0 {
|
||||
errMsg = "Validasi SatuSehat: " + strings.Join(errMessages, "; ")
|
||||
}
|
||||
|
||||
// Gunakan NewValidationError agar status HTTP menjadi 400 (Bad Request)
|
||||
return NewValidationError().
|
||||
Message(errMsg).
|
||||
Metadata("operation_outcome", result).
|
||||
Build()
|
||||
}
|
||||
Reference in New Issue
Block a user