195 lines
5.8 KiB
Go
195 lines
5.8 KiB
Go
package response
|
|
|
|
import (
|
|
"fmt"
|
|
"net/http"
|
|
"service/pkg/errors"
|
|
|
|
"github.com/gin-gonic/gin"
|
|
)
|
|
|
|
// Response represents a standard API response
|
|
type Response struct {
|
|
Status string `json:"status"`
|
|
Message string `json:"message,omitempty"`
|
|
Data interface{} `json:"data,omitempty"`
|
|
Error interface{} `json:"error,omitempty"`
|
|
Meta interface{} `json:"meta,omitempty"`
|
|
}
|
|
|
|
// getErrorCodeAndCategory memetakan HTTP Status Code ke Error Code terpusat
|
|
func getErrorCodeAndCategory(statusCode int) (string, string) {
|
|
switch statusCode {
|
|
case http.StatusBadRequest:
|
|
return errors.ErrCodeInvalidInput, errors.CategoryValidation
|
|
case http.StatusUnauthorized:
|
|
return errors.ErrCodeUnauthorized, errors.CategoryUnauthorized
|
|
case http.StatusForbidden:
|
|
return errors.ErrCodeForbidden, errors.CategoryForbidden
|
|
case http.StatusNotFound:
|
|
return errors.ErrCodeNotFound, errors.CategoryNotFound
|
|
case http.StatusConflict:
|
|
return errors.ErrCodeConflict, errors.CategoryConflict
|
|
case http.StatusTooManyRequests:
|
|
return errors.ErrCodeRateLimitExceeded, errors.CategoryRateLimit
|
|
default:
|
|
return errors.ErrCodeInternalError, errors.CategoryInternal
|
|
}
|
|
}
|
|
|
|
// Success sends a success response
|
|
func Success(c *gin.Context, statusCode int, message string, data interface{}) {
|
|
c.JSON(statusCode, Response{
|
|
Status: "success",
|
|
Message: message,
|
|
Data: data,
|
|
})
|
|
}
|
|
|
|
// Error sends an error response
|
|
func Error(c *gin.Context, statusCode int, message string, err interface{}) {
|
|
var e error
|
|
if err != nil {
|
|
if errVal, ok := err.(error); ok {
|
|
e = errVal
|
|
} else {
|
|
e = fmt.Errorf("%v", err)
|
|
}
|
|
} else {
|
|
e = fmt.Errorf("%s", message)
|
|
}
|
|
|
|
// Jika error sudah berupa AppError dari pkg/errors, cetak langsung formatnya
|
|
if errors.IsAppError(e) {
|
|
errors.HandleHTTPError(c, e.(errors.Error))
|
|
return
|
|
}
|
|
|
|
// Jika berupa error standard/native biasa, konversi menjadi format terpusat AppError
|
|
code, category := getErrorCodeAndCategory(statusCode)
|
|
wrappedErr := errors.NewBuilder().
|
|
Code(code).
|
|
Category(category).
|
|
HTTPStatus(statusCode).
|
|
Message(message).
|
|
Cause(e).
|
|
Metadata("reason", e.Error()).
|
|
Build()
|
|
|
|
errors.HandleHTTPError(c, wrappedErr)
|
|
}
|
|
|
|
// ErrorWithLog mengirimkan respons error HTTP sekaligus menyisipkan error asli
|
|
// ke dalam context Gin agar bisa direkam oleh LoggingMiddleware.
|
|
func ErrorWithLog(c *gin.Context, err error, statusCode int, message string, details interface{}) {
|
|
if err != nil {
|
|
c.Error(err) // Meneruskan error asli ke Gin Context untuk dicatat logger
|
|
} else {
|
|
err = fmt.Errorf("%s", message)
|
|
}
|
|
|
|
// Sama seperti fungsi Error(), kita teruskan ke format sentral
|
|
if errors.IsAppError(err) {
|
|
errors.HandleHTTPError(c, err.(errors.Error))
|
|
return
|
|
}
|
|
|
|
code, category := getErrorCodeAndCategory(statusCode)
|
|
builder := errors.NewBuilder().
|
|
Code(code).
|
|
Category(category).
|
|
HTTPStatus(statusCode).
|
|
Message(message).
|
|
Cause(err).
|
|
Metadata("reason", err.Error())
|
|
|
|
// Sisipkan details tambahan ke dalam metadata error builder
|
|
if details != nil {
|
|
builder.Metadata("additional_details", details)
|
|
}
|
|
|
|
errors.HandleHTTPError(c, builder.Build())
|
|
}
|
|
|
|
// Meta contains pagination metadata
|
|
type Meta struct {
|
|
Page int `json:"page"`
|
|
Limit int `json:"limit"`
|
|
Total int `json:"total"`
|
|
TotalPages int `json:"total_pages"`
|
|
}
|
|
|
|
// Paginated sends a paginated response
|
|
func Paginated(c *gin.Context, statusCode int, message string, data interface{}, meta Meta) {
|
|
c.JSON(statusCode, Response{
|
|
Status: "success",
|
|
Message: message,
|
|
Data: data,
|
|
Meta: meta,
|
|
})
|
|
}
|
|
|
|
// =========================================================================
|
|
// BPJS FORMATTER
|
|
// =========================================================================
|
|
|
|
// BPJSMetaData merepresentasikan metadata standar dari API BPJS
|
|
type BPJSMetaData struct {
|
|
Code string `json:"code"`
|
|
Message string `json:"message"`
|
|
}
|
|
|
|
// BPJSResponse merepresentasikan standar response API BPJS
|
|
type BPJSResponse struct {
|
|
MetaData BPJSMetaData `json:"metaData"`
|
|
Response interface{} `json:"response,omitempty"`
|
|
}
|
|
|
|
// BPJS mengirimkan respons dengan format standar API BPJS
|
|
func BPJS(c *gin.Context, httpStatusCode int, bpjsCode string, message string, data interface{}) {
|
|
c.JSON(httpStatusCode, BPJSResponse{
|
|
MetaData: BPJSMetaData{
|
|
Code: bpjsCode, // Contoh: "200" (sukses), "201" (dibuat), atau kode error BPJS lainnya
|
|
Message: message,
|
|
},
|
|
Response: data,
|
|
})
|
|
}
|
|
|
|
// =========================================================================
|
|
// SATU SEHAT (HL7 FHIR) FORMATTER
|
|
// =========================================================================
|
|
|
|
// FHIROperationOutcome merepresentasikan struktur error standar HL7 FHIR
|
|
type FHIROperationOutcome struct {
|
|
ResourceType string `json:"resourceType"` // Harus selalu "OperationOutcome"
|
|
Issue []FHIRErrorIssue `json:"issue"`
|
|
}
|
|
|
|
// FHIRErrorIssue berisi detail issue untuk error Satu Sehat
|
|
type FHIRErrorIssue struct {
|
|
Severity string `json:"severity"` // fatal | error | warning | information
|
|
Code string `json:"code"` // invalid | security | exception | not-found | dll
|
|
Diagnostics string `json:"diagnostics"` // Pesan detail / human-readable
|
|
}
|
|
|
|
// FHIR mengirimkan respons untuk resource FHIR secara langsung (standar Satu Sehat)
|
|
// Satu Sehat tidak menggunakan wrapper "data" atau "status", melainkan me-return object Resource langsung
|
|
func FHIR(c *gin.Context, statusCode int, resource interface{}) {
|
|
c.JSON(statusCode, resource)
|
|
}
|
|
|
|
// FHIRError mengirimkan pesan error yang comply dengan format OperationOutcome FHIR
|
|
func FHIRError(c *gin.Context, statusCode int, severity, code, diagnostics string) {
|
|
c.JSON(statusCode, FHIROperationOutcome{
|
|
ResourceType: "OperationOutcome",
|
|
Issue: []FHIRErrorIssue{
|
|
{
|
|
Severity: severity,
|
|
Code: code,
|
|
Diagnostics: diagnostics,
|
|
},
|
|
},
|
|
})
|
|
}
|