98 lines
3.4 KiB
Go
98 lines
3.4 KiB
Go
package utils
|
|
|
|
import "github.com/gofiber/fiber/v2"
|
|
|
|
// Customer-app (doormile_cx) response envelope.
|
|
//
|
|
// Deliberately a separate helper set from OK/List/Fail rather than a reuse of
|
|
// them. The customer contract fixes three things the miler/console contract
|
|
// does not: `message` is always present on success (empty string, never
|
|
// omitted), the machine-readable code is nested under `error.code` rather than
|
|
// sitting at the top level, and `total`/`nextCursor` ride the list envelope.
|
|
// Serving one endpoint in one shape and its neighbour in another is exactly
|
|
// what cost the miler client a release on /miler/verify-pin — so the customer
|
|
// surface gets its own helpers and every /customer/* response goes through
|
|
// them, auth included.
|
|
|
|
// CxErr* are the error codes in the customer contract. The client branches on
|
|
// these; `message` is customer-safe English shown verbatim in the UI.
|
|
const (
|
|
CxErrInvalid = "invalid"
|
|
CxErrInvalidName = "invalid_name"
|
|
CxErrInvalidOtp = "invalid_otp"
|
|
CxErrUnauthorized = "unauthorized"
|
|
CxErrForbidden = "forbidden"
|
|
CxErrNotFound = "not_found"
|
|
CxErrConflict = "conflict"
|
|
CxErrUnserviceable = "unserviceable"
|
|
CxErrRateLimited = "rate_limited"
|
|
CxErrServer = "server_error"
|
|
)
|
|
|
|
// CxOK is the success envelope: {success, data, message}.
|
|
func CxOK(c *fiber.Ctx, data interface{}) error {
|
|
return c.JSON(fiber.Map{"success": true, "data": data, "message": ""})
|
|
}
|
|
|
|
// CxCreated is CxOK with a 201.
|
|
func CxCreated(c *fiber.Ctx, data interface{}) error {
|
|
return c.Status(fiber.StatusCreated).
|
|
JSON(fiber.Map{"success": true, "data": data, "message": ""})
|
|
}
|
|
|
|
// CxList is the list envelope. data is always an array — never null, because
|
|
// the client types it as a list and a null throws in the parser. nextCursor is
|
|
// null when the page is the last one.
|
|
func CxList(c *fiber.Ctx, data interface{}, total int, nextCursor *string) error {
|
|
body := fiber.Map{
|
|
"success": true,
|
|
"data": data,
|
|
"total": total,
|
|
"nextCursor": nil,
|
|
"message": "",
|
|
}
|
|
if nextCursor != nil && *nextCursor != "" {
|
|
body["nextCursor"] = *nextCursor
|
|
}
|
|
return c.JSON(body)
|
|
}
|
|
|
|
// CxFail is the error envelope: {success:false, message, error:{code}}. The
|
|
// app funnels every failure into one retryable error state and renders
|
|
// `message` verbatim, so callers must pass customer-safe English here — never
|
|
// an enum key, a driver error or a wrapped stack.
|
|
func CxFail(c *fiber.Ctx, status int, code, msg string) error {
|
|
return c.Status(status).JSON(fiber.Map{
|
|
"success": false,
|
|
"message": msg,
|
|
"error": fiber.Map{"code": code},
|
|
})
|
|
}
|
|
|
|
// Shorthands for the statuses the contract pins to a specific code.
|
|
func CxBadRequest(c *fiber.Ctx, msg string) error {
|
|
return CxFail(c, fiber.StatusBadRequest, CxErrInvalid, msg)
|
|
}
|
|
|
|
func CxUnauthorized(c *fiber.Ctx, msg string) error {
|
|
return CxFail(c, fiber.StatusUnauthorized, CxErrUnauthorized, msg)
|
|
}
|
|
|
|
func CxForbidden(c *fiber.Ctx, msg string) error {
|
|
return CxFail(c, fiber.StatusForbidden, CxErrForbidden, msg)
|
|
}
|
|
|
|
func CxNotFound(c *fiber.Ctx, msg string) error {
|
|
return CxFail(c, fiber.StatusNotFound, CxErrNotFound, msg)
|
|
}
|
|
|
|
func CxConflict(c *fiber.Ctx, msg string) error {
|
|
return CxFail(c, fiber.StatusConflict, CxErrConflict, msg)
|
|
}
|
|
|
|
// CxInternal never leaks the underlying error to the customer. Log the real
|
|
// one; the app shows this.
|
|
func CxInternal(c *fiber.Ctx) error {
|
|
return CxFail(c, fiber.StatusInternalServerError, CxErrServer, "Something went wrong")
|
|
}
|