goext/ginext/response.go

413 lines
9.5 KiB
Go
Raw Permalink Normal View History

2023-07-18 14:40:10 +02:00
package ginext
import (
"fmt"
"github.com/gin-gonic/gin"
2023-07-24 11:11:15 +02:00
"gogs.mikescher.com/BlackForestBytes/goext/exerr"
2023-07-18 14:40:10 +02:00
json "gogs.mikescher.com/BlackForestBytes/goext/gojson"
2023-12-28 01:36:21 +01:00
"gogs.mikescher.com/BlackForestBytes/goext/langext"
"os"
2023-07-18 14:40:10 +02:00
)
2023-07-24 14:16:02 +02:00
type headerval struct {
Key string
Val string
}
2023-07-18 14:40:10 +02:00
type HTTPResponse interface {
Write(g *gin.Context)
2023-07-24 14:16:02 +02:00
WithHeader(k string, v string) HTTPResponse
2023-12-02 13:15:19 +01:00
IsSuccess() bool
2023-07-18 14:40:10 +02:00
}
2023-12-28 01:36:21 +01:00
type InspectableHTTPResponse interface {
HTTPResponse
Statuscode() int
BodyString(g *gin.Context) *string
ContentType() string
Headers() []string
}
2023-07-18 14:40:10 +02:00
type jsonHTTPResponse struct {
statusCode int
data any
2023-07-24 14:16:02 +02:00
headers []headerval
2023-07-18 14:40:10 +02:00
}
2023-12-28 01:36:21 +01:00
func (j jsonHTTPResponse) jsonRenderer(g *gin.Context) json.GoJsonRender {
var f *string
if jsonfilter := g.GetString("goext.jsonfilter"); jsonfilter != "" {
f = &jsonfilter
}
2023-12-28 01:36:21 +01:00
return json.GoJsonRender{Data: j.data, NilSafeSlices: true, NilSafeMaps: true, Filter: f}
}
func (j jsonHTTPResponse) Write(g *gin.Context) {
for _, v := range j.headers {
g.Header(v.Key, v.Val)
}
g.Render(j.statusCode, j.jsonRenderer(g))
2023-07-18 14:40:10 +02:00
}
2023-07-24 14:16:02 +02:00
func (j jsonHTTPResponse) WithHeader(k string, v string) HTTPResponse {
j.headers = append(j.headers, headerval{k, v})
return j
}
2023-12-02 13:15:19 +01:00
func (j jsonHTTPResponse) IsSuccess() bool {
return j.statusCode >= 200 && j.statusCode <= 399
}
2023-12-28 01:36:21 +01:00
func (j jsonHTTPResponse) Statuscode() int {
return j.statusCode
}
func (j jsonHTTPResponse) BodyString(g *gin.Context) *string {
if str, err := j.jsonRenderer(g).RenderString(); err == nil {
return &str
} else {
return nil
}
}
func (j jsonHTTPResponse) ContentType() string {
return "application/json"
}
func (j jsonHTTPResponse) Headers() []string {
return langext.ArrMap(j.headers, func(v headerval) string { return v.Key + "=" + v.Val })
}
2023-07-18 14:40:10 +02:00
type emptyHTTPResponse struct {
statusCode int
2023-07-24 14:16:02 +02:00
headers []headerval
2023-07-18 14:40:10 +02:00
}
func (j emptyHTTPResponse) Write(g *gin.Context) {
2023-07-24 14:16:02 +02:00
for _, v := range j.headers {
g.Header(v.Key, v.Val)
}
2023-07-18 14:40:10 +02:00
g.Status(j.statusCode)
}
2023-07-24 14:16:02 +02:00
func (j emptyHTTPResponse) WithHeader(k string, v string) HTTPResponse {
j.headers = append(j.headers, headerval{k, v})
return j
}
2023-12-02 13:15:19 +01:00
func (j emptyHTTPResponse) IsSuccess() bool {
return j.statusCode >= 200 && j.statusCode <= 399
}
2023-12-28 01:36:21 +01:00
func (j emptyHTTPResponse) Statuscode() int {
return j.statusCode
}
func (j emptyHTTPResponse) BodyString(*gin.Context) *string {
return nil
}
func (j emptyHTTPResponse) ContentType() string {
return ""
}
func (j emptyHTTPResponse) Headers() []string {
return langext.ArrMap(j.headers, func(v headerval) string { return v.Key + "=" + v.Val })
}
2023-07-18 14:40:10 +02:00
type textHTTPResponse struct {
statusCode int
data string
2023-07-24 14:16:02 +02:00
headers []headerval
2023-07-18 14:40:10 +02:00
}
func (j textHTTPResponse) Write(g *gin.Context) {
2023-07-24 14:16:02 +02:00
for _, v := range j.headers {
g.Header(v.Key, v.Val)
}
2023-07-18 14:40:10 +02:00
g.String(j.statusCode, "%s", j.data)
}
2023-07-24 14:16:02 +02:00
func (j textHTTPResponse) WithHeader(k string, v string) HTTPResponse {
j.headers = append(j.headers, headerval{k, v})
return j
}
2023-12-02 13:15:19 +01:00
func (j textHTTPResponse) IsSuccess() bool {
return j.statusCode >= 200 && j.statusCode <= 399
}
2023-12-28 01:36:21 +01:00
func (j textHTTPResponse) Statuscode() int {
return j.statusCode
}
func (j textHTTPResponse) BodyString(*gin.Context) *string {
return langext.Ptr(j.data)
}
func (j textHTTPResponse) ContentType() string {
return "text/plain"
}
func (j textHTTPResponse) Headers() []string {
return langext.ArrMap(j.headers, func(v headerval) string { return v.Key + "=" + v.Val })
}
2023-07-18 14:40:10 +02:00
type dataHTTPResponse struct {
statusCode int
data []byte
contentType string
2023-07-24 14:16:02 +02:00
headers []headerval
2023-07-18 14:40:10 +02:00
}
func (j dataHTTPResponse) Write(g *gin.Context) {
2023-07-24 14:16:02 +02:00
for _, v := range j.headers {
g.Header(v.Key, v.Val)
}
2023-07-18 14:40:10 +02:00
g.Data(j.statusCode, j.contentType, j.data)
}
2023-07-24 14:16:02 +02:00
func (j dataHTTPResponse) WithHeader(k string, v string) HTTPResponse {
j.headers = append(j.headers, headerval{k, v})
return j
}
2023-12-02 13:15:19 +01:00
func (j dataHTTPResponse) IsSuccess() bool {
return j.statusCode >= 200 && j.statusCode <= 399
}
2023-12-28 01:36:21 +01:00
func (j dataHTTPResponse) Statuscode() int {
return j.statusCode
}
func (j dataHTTPResponse) BodyString(*gin.Context) *string {
return langext.Ptr(string(j.data))
}
func (j dataHTTPResponse) ContentType() string {
return j.contentType
}
func (j dataHTTPResponse) Headers() []string {
return langext.ArrMap(j.headers, func(v headerval) string { return v.Key + "=" + v.Val })
}
2023-07-18 14:40:10 +02:00
type fileHTTPResponse struct {
mimetype string
filepath string
filename *string
2023-07-24 14:16:02 +02:00
headers []headerval
2023-07-18 14:40:10 +02:00
}
func (j fileHTTPResponse) Write(g *gin.Context) {
g.Header("Content-Type", j.mimetype) // if we don't set it here gin does weird file-sniffing later...
if j.filename != nil {
g.Header("Content-Disposition", fmt.Sprintf("attachment; filename=\"%s\"", *j.filename))
}
2023-07-24 14:16:02 +02:00
for _, v := range j.headers {
g.Header(v.Key, v.Val)
}
2023-07-18 14:40:10 +02:00
g.File(j.filepath)
}
2023-07-24 14:16:02 +02:00
func (j fileHTTPResponse) WithHeader(k string, v string) HTTPResponse {
j.headers = append(j.headers, headerval{k, v})
return j
}
2023-12-02 13:15:19 +01:00
func (j fileHTTPResponse) IsSuccess() bool {
return true
}
2023-12-28 01:36:21 +01:00
func (j fileHTTPResponse) Statuscode() int {
return 200
}
func (j fileHTTPResponse) BodyString(*gin.Context) *string {
data, err := os.ReadFile(j.filepath)
if err != nil {
return nil
}
return langext.Ptr(string(data))
}
func (j fileHTTPResponse) ContentType() string {
return j.mimetype
}
func (j fileHTTPResponse) Headers() []string {
return langext.ArrMap(j.headers, func(v headerval) string { return v.Key + "=" + v.Val })
}
2023-08-06 19:10:31 +02:00
type downloadDataHTTPResponse struct {
statusCode int
mimetype string
data []byte
filename *string
headers []headerval
}
func (j downloadDataHTTPResponse) Write(g *gin.Context) {
g.Header("Content-Type", j.mimetype) // if we don't set it here gin does weird file-sniffing later...
if j.filename != nil {
g.Header("Content-Disposition", fmt.Sprintf("attachment; filename=\"%s\"", *j.filename))
}
for _, v := range j.headers {
g.Header(v.Key, v.Val)
}
g.Data(j.statusCode, j.mimetype, j.data)
}
func (j downloadDataHTTPResponse) WithHeader(k string, v string) HTTPResponse {
j.headers = append(j.headers, headerval{k, v})
return j
}
2023-12-02 13:15:19 +01:00
func (j downloadDataHTTPResponse) IsSuccess() bool {
return j.statusCode >= 200 && j.statusCode <= 399
}
2023-12-28 01:36:21 +01:00
func (j downloadDataHTTPResponse) Statuscode() int {
return j.statusCode
}
func (j downloadDataHTTPResponse) BodyString(*gin.Context) *string {
return langext.Ptr(string(j.data))
}
func (j downloadDataHTTPResponse) ContentType() string {
return j.mimetype
}
func (j downloadDataHTTPResponse) Headers() []string {
return langext.ArrMap(j.headers, func(v headerval) string { return v.Key + "=" + v.Val })
}
2023-07-18 15:23:32 +02:00
type redirectHTTPResponse struct {
statusCode int
url string
2023-07-24 14:16:02 +02:00
headers []headerval
2023-07-18 15:23:32 +02:00
}
func (j redirectHTTPResponse) Write(g *gin.Context) {
g.Redirect(j.statusCode, j.url)
}
2023-07-24 14:16:02 +02:00
func (j redirectHTTPResponse) WithHeader(k string, v string) HTTPResponse {
j.headers = append(j.headers, headerval{k, v})
return j
}
2023-12-02 13:15:19 +01:00
func (j redirectHTTPResponse) IsSuccess() bool {
return j.statusCode >= 200 && j.statusCode <= 399
}
2023-12-28 01:36:21 +01:00
func (j redirectHTTPResponse) Statuscode() int {
return j.statusCode
}
func (j redirectHTTPResponse) BodyString(*gin.Context) *string {
return nil
}
func (j redirectHTTPResponse) ContentType() string {
return ""
}
func (j redirectHTTPResponse) Headers() []string {
return langext.ArrMap(j.headers, func(v headerval) string { return v.Key + "=" + v.Val })
}
2023-07-24 11:11:15 +02:00
type jsonAPIErrResponse struct {
2023-07-24 14:16:02 +02:00
err *exerr.ExErr
headers []headerval
2023-07-24 11:11:15 +02:00
}
func (j jsonAPIErrResponse) Write(g *gin.Context) {
j.err.Output(g)
}
2023-07-24 14:16:02 +02:00
func (j jsonAPIErrResponse) WithHeader(k string, v string) HTTPResponse {
j.headers = append(j.headers, headerval{k, v})
return j
}
2023-12-02 13:15:19 +01:00
func (j jsonAPIErrResponse) IsSuccess() bool {
return false
}
2023-12-28 01:36:21 +01:00
func (j jsonAPIErrResponse) Statuscode() int {
return langext.Coalesce(j.err.RecursiveStatuscode(), 0)
}
func (j jsonAPIErrResponse) BodyString(*gin.Context) *string {
if str, err := j.err.ToDefaultAPIJson(); err == nil {
return &str
} else {
return nil
}
}
func (j jsonAPIErrResponse) ContentType() string {
return "application/json"
}
func (j jsonAPIErrResponse) Headers() []string {
return langext.ArrMap(j.headers, func(v headerval) string { return v.Key + "=" + v.Val })
}
2024-01-05 07:21:43 +01:00
func (j jsonAPIErrResponse) Unwrap() error {
return j.err
}
2023-07-18 14:40:10 +02:00
func Status(sc int) HTTPResponse {
return &emptyHTTPResponse{statusCode: sc}
}
func JSON(sc int, data any) HTTPResponse {
return &jsonHTTPResponse{statusCode: sc, data: data}
}
func Data(sc int, contentType string, data []byte) HTTPResponse {
return &dataHTTPResponse{statusCode: sc, contentType: contentType, data: data}
}
func Text(sc int, data string) HTTPResponse {
return &textHTTPResponse{statusCode: sc, data: data}
}
func File(mimetype string, filepath string) HTTPResponse {
return &fileHTTPResponse{mimetype: mimetype, filepath: filepath}
}
func Download(mimetype string, filepath string, filename string) HTTPResponse {
return &fileHTTPResponse{mimetype: mimetype, filepath: filepath, filename: &filename}
}
2023-08-06 19:11:59 +02:00
func DownloadData(status int, mimetype string, filename string, data []byte) HTTPResponse {
return &downloadDataHTTPResponse{statusCode: status, mimetype: mimetype, data: data, filename: &filename}
2023-08-06 19:10:31 +02:00
}
2023-07-18 15:23:32 +02:00
func Redirect(sc int, newURL string) HTTPResponse {
return &redirectHTTPResponse{statusCode: sc, url: newURL}
}
func Error(e error) HTTPResponse {
2023-07-24 11:11:15 +02:00
return &jsonAPIErrResponse{
err: exerr.FromError(e),
}
2023-07-18 14:40:10 +02:00
}
func ErrWrap(e error, errorType exerr.ErrorType, msg string) HTTPResponse {
return &jsonAPIErrResponse{
2023-08-22 10:36:35 +02:00
err: exerr.FromError(exerr.Wrap(e, msg).WithType(errorType).Build()),
}
}
func NotImplemented() HTTPResponse {
return Error(exerr.New(exerr.TypeNotImplemented, "").Build())
2023-07-18 14:40:10 +02:00
}