mirror of
https://github.com/kataras/iris.git
synced 2025-01-23 02:31:04 +01:00
add x/errors.OK, Create, NoContent and NoContentOrNotModified package-level generic functions as custom service helpers
This commit is contained in:
parent
56754ff5cc
commit
1ae4e71176
|
@ -23,6 +23,7 @@ Developers are not forced to upgrade if they don't really need it. Upgrade whene
|
||||||
|
|
||||||
Changes apply to `main` branch.
|
Changes apply to `main` branch.
|
||||||
|
|
||||||
|
- Add `x/errors.OK`, `Create`, `NoContent` and `NoContentOrNotModified` package-level generic functions as custom service method caller helpers. Example can be found [here](_examples/routing/http-wire-errors/service/main.go).
|
||||||
- Add `x/errors.ReadPayload`, `ReadQuery`, `ReadPaginationOptions`, `Handle`, `HandleCreate`, `HandleCreateResponse`, `HandleUpdate` and `HandleDelete` package-level functions as helpers for common actions.
|
- Add `x/errors.ReadPayload`, `ReadQuery`, `ReadPaginationOptions`, `Handle`, `HandleCreate`, `HandleCreateResponse`, `HandleUpdate` and `HandleDelete` package-level functions as helpers for common actions.
|
||||||
- Add `x/jsonx.GetSimpleDateRange(date, jsonx.WeekRange, time.Monday, time.Sunday)` which returns all dates between the given range and start/end weekday values for WeekRange.
|
- Add `x/jsonx.GetSimpleDateRange(date, jsonx.WeekRange, time.Monday, time.Sunday)` which returns all dates between the given range and start/end weekday values for WeekRange.
|
||||||
- Add `x/timex.GetMonthDays` and `x/timex.GetMonthEnd` functions.
|
- Add `x/timex.GetMonthDays` and `x/timex.GetMonthEnd` functions.
|
||||||
|
|
|
@ -55,6 +55,7 @@
|
||||||
* [Custom HTTP Errors](routing/http-errors/main.go)
|
* [Custom HTTP Errors](routing/http-errors/main.go)
|
||||||
* [HTTP Wire Errors](routing/http-wire-errors/main.go) **NEW**
|
* [HTTP Wire Errors](routing/http-wire-errors/main.go) **NEW**
|
||||||
* [Custom Validation Errors](routing/http-wire-errors/custom-validation-errors/main.go)
|
* [Custom Validation Errors](routing/http-wire-errors/custom-validation-errors/main.go)
|
||||||
|
* [Service](routing/http-wire-errors/service/main.go) **NEW**
|
||||||
* [Not Found - Intelligence](routing/intelligence/main.go)
|
* [Not Found - Intelligence](routing/intelligence/main.go)
|
||||||
* [Not Found - Suggest Closest Paths](routing/intelligence/manual/main.go)
|
* [Not Found - Suggest Closest Paths](routing/intelligence/manual/main.go)
|
||||||
* [Dynamic Path](routing/dynamic-path/main.go)
|
* [Dynamic Path](routing/dynamic-path/main.go)
|
||||||
|
|
|
@ -29,7 +29,7 @@ import (
|
||||||
// errors.Unavailable
|
// errors.Unavailable
|
||||||
// errors.DataLoss
|
// errors.DataLoss
|
||||||
var (
|
var (
|
||||||
Custom = errors.E("CUSTOM_CANONICAL_ERROR_NAME", iris.StatusBadRequest)
|
Custom = errors.Register("CUSTOM_CANONICAL_ERROR_NAME", iris.StatusBadRequest)
|
||||||
)
|
)
|
||||||
|
|
||||||
func main() {
|
func main() {
|
||||||
|
|
127
_examples/routing/http-wire-errors/service/main.go
Normal file
127
_examples/routing/http-wire-errors/service/main.go
Normal file
|
@ -0,0 +1,127 @@
|
||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
"github.com/kataras/iris/v12"
|
||||||
|
"github.com/kataras/iris/v12/x/errors"
|
||||||
|
)
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
app := iris.New()
|
||||||
|
|
||||||
|
service := new(myService)
|
||||||
|
app.Post("/", createHandler(service))
|
||||||
|
app.Get("/", listHandler(service))
|
||||||
|
app.Delete("/{id:string}", deleteHandler(service))
|
||||||
|
|
||||||
|
app.Listen(":8080")
|
||||||
|
}
|
||||||
|
|
||||||
|
func createHandler(service *myService) iris.Handler {
|
||||||
|
return func(ctx iris.Context) {
|
||||||
|
// What it does?
|
||||||
|
// 1. Reads the request body and binds it to the CreateRequest struct.
|
||||||
|
// 2. Calls the service.Create function with the given request body.
|
||||||
|
// 3. If the service.Create returns an error, it sends an appropriate error response to the client.
|
||||||
|
// 4. If the service.Create returns a response, it sets the status code to 201 (Created) and sends the response as a JSON payload to the client.
|
||||||
|
//
|
||||||
|
// Useful for create operations.
|
||||||
|
errors.Create(ctx, service.Create)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func listHandler(service *myService) iris.Handler {
|
||||||
|
return func(ctx iris.Context) {
|
||||||
|
// What it does?
|
||||||
|
// 1. If the 3rd variadic (optional) parameter is empty (not our case here), it reads the request body and binds it to the ListRequest struct,
|
||||||
|
// otherwise (our case) it calls the service.List function directly with the given input parameter (empty ListRequest struct value in our case).
|
||||||
|
// 2. Calls the service.List function with the ListRequest value.
|
||||||
|
// 3. If the service.List returns an error, it sends an appropriate error response to the client.
|
||||||
|
// 4. If the service.List returns a response, it sets the status code to 200 (OK) and sends the response as a JSON payload to the client.
|
||||||
|
//
|
||||||
|
// Useful for get single, fetch multiple and search operations.
|
||||||
|
errors.OK(ctx, service.List, ListRequest{})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func deleteHandler(service *myService) iris.Handler {
|
||||||
|
return func(ctx iris.Context) {
|
||||||
|
id := ctx.Params().Get("id")
|
||||||
|
// What it does?
|
||||||
|
// 1. Calls the service.Delete function with the given input parameter.
|
||||||
|
// 2. If the service.Delete returns an error, it sends an appropriate error response to the client.
|
||||||
|
// 3.If the service.Delete doesn't return an error then it sets the status code to 204 (No Content) and
|
||||||
|
// sends the response as a JSON payload to the client.
|
||||||
|
// errors.NoContent(ctx, service.Delete, id)
|
||||||
|
// OR:
|
||||||
|
// 1. Calls the service.DeleteWithFeedback function with the given input parameter.
|
||||||
|
// 2. If the service.DeleteWithFeedback returns an error, it sends an appropriate error response to the client.
|
||||||
|
// 3. If the service.DeleteWithFeedback returns true, it sets the status code to 204 (No Content).
|
||||||
|
// 4. If the service.DeleteWithFeedback returns false, it sets the status code to 304 (Not Modified).
|
||||||
|
//
|
||||||
|
// Useful for update and delete operations.
|
||||||
|
errors.NoContentOrNotModified(ctx, service.DeleteWithFeedback, id)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
type (
|
||||||
|
myService struct{}
|
||||||
|
|
||||||
|
CreateRequest struct {
|
||||||
|
Fullname string
|
||||||
|
}
|
||||||
|
|
||||||
|
CreateResponse struct {
|
||||||
|
ID string
|
||||||
|
Firstname string
|
||||||
|
Lastname string
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
func (s *myService) Create(ctx context.Context, in CreateRequest) (CreateResponse, error) {
|
||||||
|
arr := strings.Split(in.Fullname, " ")
|
||||||
|
firstname, lastname := arr[0], arr[1]
|
||||||
|
id := "test_id"
|
||||||
|
|
||||||
|
resp := CreateResponse{
|
||||||
|
ID: id,
|
||||||
|
Firstname: firstname,
|
||||||
|
Lastname: lastname,
|
||||||
|
}
|
||||||
|
return resp, nil // , errors.New("create: test error")
|
||||||
|
}
|
||||||
|
|
||||||
|
type ListRequest struct {
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *myService) List(ctx context.Context, in ListRequest) ([]CreateResponse, error) {
|
||||||
|
resp := []CreateResponse{
|
||||||
|
{
|
||||||
|
ID: "test-id-1",
|
||||||
|
Firstname: "test first name 1",
|
||||||
|
Lastname: "test last name 1",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ID: "test-id-2",
|
||||||
|
Firstname: "test first name 2",
|
||||||
|
Lastname: "test last name 2",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ID: "test-id-3",
|
||||||
|
Firstname: "test first name 3",
|
||||||
|
Lastname: "test last name 3",
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
return resp, nil //, errors.New("list: test error")
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *myService) Delete(ctx context.Context, id string) error {
|
||||||
|
return nil // errors.New("delete: test error")
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *myService) DeleteWithFeedback(ctx context.Context, id string) (bool, error) {
|
||||||
|
return true, nil // false, errors.New("delete: test error")
|
||||||
|
}
|
2
go.mod
2
go.mod
|
@ -34,7 +34,7 @@ require (
|
||||||
github.com/microcosm-cc/bluemonday v1.0.26
|
github.com/microcosm-cc/bluemonday v1.0.26
|
||||||
github.com/redis/go-redis/v9 v9.3.1
|
github.com/redis/go-redis/v9 v9.3.1
|
||||||
github.com/schollz/closestmatch v2.1.0+incompatible
|
github.com/schollz/closestmatch v2.1.0+incompatible
|
||||||
github.com/shirou/gopsutil/v3 v3.23.11
|
github.com/shirou/gopsutil/v3 v3.23.12
|
||||||
github.com/tdewolff/minify/v2 v2.20.10
|
github.com/tdewolff/minify/v2 v2.20.10
|
||||||
github.com/vmihailenco/msgpack/v5 v5.4.1
|
github.com/vmihailenco/msgpack/v5 v5.4.1
|
||||||
github.com/yosssi/ace v0.0.5
|
github.com/yosssi/ace v0.0.5
|
||||||
|
|
4
go.sum
generated
4
go.sum
generated
|
@ -196,8 +196,8 @@ github.com/schollz/closestmatch v2.1.0+incompatible h1:Uel2GXEpJqOWBrlyI+oY9LTiy
|
||||||
github.com/schollz/closestmatch v2.1.0+incompatible/go.mod h1:RtP1ddjLong6gTkbtmuhtR2uUrrJOpYzYRvbcPAid+g=
|
github.com/schollz/closestmatch v2.1.0+incompatible/go.mod h1:RtP1ddjLong6gTkbtmuhtR2uUrrJOpYzYRvbcPAid+g=
|
||||||
github.com/sergi/go-diff v1.0.0 h1:Kpca3qRNrduNnOQeazBd0ysaKrUJiIuISHxogkT9RPQ=
|
github.com/sergi/go-diff v1.0.0 h1:Kpca3qRNrduNnOQeazBd0ysaKrUJiIuISHxogkT9RPQ=
|
||||||
github.com/sergi/go-diff v1.0.0/go.mod h1:0CfEIISq7TuYL3j771MWULgwwjU+GofnZX9QAmXWZgo=
|
github.com/sergi/go-diff v1.0.0/go.mod h1:0CfEIISq7TuYL3j771MWULgwwjU+GofnZX9QAmXWZgo=
|
||||||
github.com/shirou/gopsutil/v3 v3.23.11 h1:i3jP9NjCPUz7FiZKxlMnODZkdSIp2gnzfrvsu9CuWEQ=
|
github.com/shirou/gopsutil/v3 v3.23.12 h1:z90NtUkp3bMtmICZKpC4+WaknU1eXtp5vtbQ11DgpE4=
|
||||||
github.com/shirou/gopsutil/v3 v3.23.11/go.mod h1:1FrWgea594Jp7qmjHUUPlJDTPgcsb9mGnXDxavtikzM=
|
github.com/shirou/gopsutil/v3 v3.23.12/go.mod h1:1FrWgea594Jp7qmjHUUPlJDTPgcsb9mGnXDxavtikzM=
|
||||||
github.com/shoenig/go-m1cpu v0.1.6 h1:nxdKQNcEB6vzgA2E2bvzKIYRuNj7XNJ4S/aRSwKzFtM=
|
github.com/shoenig/go-m1cpu v0.1.6 h1:nxdKQNcEB6vzgA2E2bvzKIYRuNj7XNJ4S/aRSwKzFtM=
|
||||||
github.com/shoenig/go-m1cpu v0.1.6/go.mod h1:1JJMcUBvfNwpq05QDQVAnx3gUHr9IYF7GNg9SUEw2VQ=
|
github.com/shoenig/go-m1cpu v0.1.6/go.mod h1:1JJMcUBvfNwpq05QDQVAnx3gUHr9IYF7GNg9SUEw2VQ=
|
||||||
github.com/shoenig/test v0.6.4 h1:kVTaSd7WLz5WZ2IaoM0RSzRsUD+m8wRR+5qvntpn4LU=
|
github.com/shoenig/test v0.6.4 h1:kVTaSd7WLz5WZ2IaoM0RSzRsUD+m8wRR+5qvntpn4LU=
|
||||||
|
|
|
@ -14,6 +14,8 @@ var (
|
||||||
New = errors.New
|
New = errors.New
|
||||||
// Unwrap is an alias of the standard errors.Unwrap function.
|
// Unwrap is an alias of the standard errors.Unwrap function.
|
||||||
Unwrap = errors.Unwrap
|
Unwrap = errors.Unwrap
|
||||||
|
// Join is an alias of the standard errors.Join function.
|
||||||
|
Join = errors.Join
|
||||||
)
|
)
|
||||||
|
|
||||||
func sprintf(format string, args ...interface{}) string {
|
func sprintf(format string, args ...interface{}) string {
|
||||||
|
|
|
@ -48,9 +48,12 @@ type (
|
||||||
// A read-only map of valid http error codes.
|
// A read-only map of valid http error codes.
|
||||||
var errorCodeMap = make(map[ErrorCodeName]ErrorCode)
|
var errorCodeMap = make(map[ErrorCodeName]ErrorCode)
|
||||||
|
|
||||||
// E registers a custom HTTP Error and returns its canonical name for future use.
|
// Deprecated: Use Register instead.
|
||||||
|
var E = Register
|
||||||
|
|
||||||
|
// Register registers a custom HTTP Error and returns its canonical name for future use.
|
||||||
// The method "New" is reserved and was kept as it is for compatibility
|
// The method "New" is reserved and was kept as it is for compatibility
|
||||||
// with the standard errors package, therefore the "E" name was chosen instead.
|
// with the standard errors package, therefore the "Register" name was chosen instead.
|
||||||
// The key stroke "e" is near and accessible while typing the "errors" word
|
// The key stroke "e" is near and accessible while typing the "errors" word
|
||||||
// so developers may find it easy to use.
|
// so developers may find it easy to use.
|
||||||
//
|
//
|
||||||
|
@ -59,14 +62,14 @@ var errorCodeMap = make(map[ErrorCodeName]ErrorCode)
|
||||||
// Example:
|
// Example:
|
||||||
//
|
//
|
||||||
// var (
|
// var (
|
||||||
// NotFound = errors.E("NOT_FOUND", http.StatusNotFound)
|
// NotFound = errors.Register("NOT_FOUND", http.StatusNotFound)
|
||||||
// )
|
// )
|
||||||
// ...
|
// ...
|
||||||
// NotFound.Details(ctx, "resource not found", "user with id: %q was not found", userID)
|
// NotFound.Details(ctx, "resource not found", "user with id: %q was not found", userID)
|
||||||
//
|
//
|
||||||
// This method MUST be called on initialization, before HTTP server starts as
|
// This method MUST be called on initialization, before HTTP server starts as
|
||||||
// the internal map is not protected by mutex.
|
// the internal map is not protected by mutex.
|
||||||
func E(httpErrorCanonicalName string, httpStatusCode int) ErrorCodeName {
|
func Register(httpErrorCanonicalName string, httpStatusCode int) ErrorCodeName {
|
||||||
canonicalName := ErrorCodeName(httpErrorCanonicalName)
|
canonicalName := ErrorCodeName(httpErrorCanonicalName)
|
||||||
RegisterErrorCode(canonicalName, httpStatusCode)
|
RegisterErrorCode(canonicalName, httpStatusCode)
|
||||||
return canonicalName
|
return canonicalName
|
||||||
|
@ -99,22 +102,22 @@ func RegisterErrorCodeMap(errorMap map[ErrorCodeName]int) {
|
||||||
|
|
||||||
// List of default error codes a server should follow and send back to the client.
|
// List of default error codes a server should follow and send back to the client.
|
||||||
var (
|
var (
|
||||||
Cancelled ErrorCodeName = E("CANCELLED", context.StatusTokenRequired)
|
Cancelled ErrorCodeName = Register("CANCELLED", context.StatusTokenRequired)
|
||||||
Unknown ErrorCodeName = E("UNKNOWN", http.StatusInternalServerError)
|
Unknown ErrorCodeName = Register("UNKNOWN", http.StatusInternalServerError)
|
||||||
InvalidArgument ErrorCodeName = E("INVALID_ARGUMENT", http.StatusBadRequest)
|
InvalidArgument ErrorCodeName = Register("INVALID_ARGUMENT", http.StatusBadRequest)
|
||||||
DeadlineExceeded ErrorCodeName = E("DEADLINE_EXCEEDED", http.StatusGatewayTimeout)
|
DeadlineExceeded ErrorCodeName = Register("DEADLINE_EXCEEDED", http.StatusGatewayTimeout)
|
||||||
NotFound ErrorCodeName = E("NOT_FOUND", http.StatusNotFound)
|
NotFound ErrorCodeName = Register("NOT_FOUND", http.StatusNotFound)
|
||||||
AlreadyExists ErrorCodeName = E("ALREADY_EXISTS", http.StatusConflict)
|
AlreadyExists ErrorCodeName = Register("ALREADY_EXISTS", http.StatusConflict)
|
||||||
PermissionDenied ErrorCodeName = E("PERMISSION_DENIED", http.StatusForbidden)
|
PermissionDenied ErrorCodeName = Register("PERMISSION_DENIED", http.StatusForbidden)
|
||||||
Unauthenticated ErrorCodeName = E("UNAUTHENTICATED", http.StatusUnauthorized)
|
Unauthenticated ErrorCodeName = Register("UNAUTHENTICATED", http.StatusUnauthorized)
|
||||||
ResourceExhausted ErrorCodeName = E("RESOURCE_EXHAUSTED", http.StatusTooManyRequests)
|
ResourceExhausted ErrorCodeName = Register("RESOURCE_EXHAUSTED", http.StatusTooManyRequests)
|
||||||
FailedPrecondition ErrorCodeName = E("FAILED_PRECONDITION", http.StatusBadRequest)
|
FailedPrecondition ErrorCodeName = Register("FAILED_PRECONDITION", http.StatusBadRequest)
|
||||||
Aborted ErrorCodeName = E("ABORTED", http.StatusConflict)
|
Aborted ErrorCodeName = Register("ABORTED", http.StatusConflict)
|
||||||
OutOfRange ErrorCodeName = E("OUT_OF_RANGE", http.StatusBadRequest)
|
OutOfRange ErrorCodeName = Register("OUT_OF_RANGE", http.StatusBadRequest)
|
||||||
Unimplemented ErrorCodeName = E("UNIMPLEMENTED", http.StatusNotImplemented)
|
Unimplemented ErrorCodeName = Register("UNIMPLEMENTED", http.StatusNotImplemented)
|
||||||
Internal ErrorCodeName = E("INTERNAL", http.StatusInternalServerError)
|
Internal ErrorCodeName = Register("INTERNAL", http.StatusInternalServerError)
|
||||||
Unavailable ErrorCodeName = E("UNAVAILABLE", http.StatusServiceUnavailable)
|
Unavailable ErrorCodeName = Register("UNAVAILABLE", http.StatusServiceUnavailable)
|
||||||
DataLoss ErrorCodeName = E("DATA_LOSS", http.StatusInternalServerError)
|
DataLoss ErrorCodeName = Register("DATA_LOSS", http.StatusInternalServerError)
|
||||||
)
|
)
|
||||||
|
|
||||||
// errorFuncCodeMap is a read-only map of error code names and their error functions.
|
// errorFuncCodeMap is a read-only map of error code names and their error functions.
|
||||||
|
@ -352,7 +355,7 @@ var (
|
||||||
// The server fails to send an error on two cases:
|
// The server fails to send an error on two cases:
|
||||||
// 1. when the provided error code name is not registered (the error value is the ErrUnexpectedErrorCode)
|
// 1. when the provided error code name is not registered (the error value is the ErrUnexpectedErrorCode)
|
||||||
// 2. when the error contains data but cannot be encoded to json (the value of the error is the result error of json.Marshal).
|
// 2. when the error contains data but cannot be encoded to json (the value of the error is the result error of json.Marshal).
|
||||||
ErrUnexpected = E("UNEXPECTED_ERROR", http.StatusInternalServerError)
|
ErrUnexpected = Register("UNEXPECTED_ERROR", http.StatusInternalServerError)
|
||||||
// ErrUnexpectedErrorCode is the error which logged
|
// ErrUnexpectedErrorCode is the error which logged
|
||||||
// when the given error code name is not registered.
|
// when the given error code name is not registered.
|
||||||
ErrUnexpectedErrorCode = New("unexpected error code name")
|
ErrUnexpectedErrorCode = New("unexpected error code name")
|
||||||
|
|
|
@ -1,12 +1,198 @@
|
||||||
package errors
|
package errors
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
stdContext "context"
|
||||||
"net/http"
|
"net/http"
|
||||||
|
|
||||||
"github.com/kataras/iris/v12/context"
|
"github.com/kataras/iris/v12/context"
|
||||||
"github.com/kataras/iris/v12/x/pagination"
|
"github.com/kataras/iris/v12/x/pagination"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// Handle handles a generic response and error from a service call and sends a JSON response to the context.
|
||||||
|
// It returns a boolean value indicating whether the handle was successful or not.
|
||||||
|
// If the error is not nil, it calls HandleError to send an appropriate error response to the client.
|
||||||
|
func Handle(ctx *context.Context, resp interface{}, err error) bool {
|
||||||
|
if HandleError(ctx, err) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
ctx.StatusCode(http.StatusOK)
|
||||||
|
|
||||||
|
if resp != nil {
|
||||||
|
if ctx.JSON(resp) != nil {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
// IDPayload is a simple struct which describes a json id value.
|
||||||
|
type IDPayload[T string | int] struct {
|
||||||
|
ID T `json:"id"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// HandleCreate handles a create operation and sends a JSON response with the created resource to the client.
|
||||||
|
// It returns a boolean value indicating whether the handle was successful or not.
|
||||||
|
//
|
||||||
|
// If the "respOrID" response is not nil, it sets the status code to 201 (Created) and sends the response as a JSON payload,
|
||||||
|
// however if the given "respOrID" is a string or an int, it sends the response as a JSON payload of {"id": resp}.
|
||||||
|
// If the "err" error is not nil, it calls HandleError to send an appropriate error response to the client.
|
||||||
|
// It sets the status code to 201 (Created) and sends any response as a JSON payload,
|
||||||
|
func HandleCreate(ctx *context.Context, respOrID any, err error) bool {
|
||||||
|
if HandleError(ctx, err) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
ctx.StatusCode(http.StatusCreated)
|
||||||
|
|
||||||
|
if respOrID != nil {
|
||||||
|
switch responseValue := respOrID.(type) {
|
||||||
|
case string:
|
||||||
|
if ctx.JSON(IDPayload[string]{ID: responseValue}) != nil {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
case int:
|
||||||
|
if ctx.JSON(IDPayload[int]{ID: responseValue}) != nil {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
if ctx.JSON(responseValue) != nil {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
// HandleUpdate handles an update operation and sends a status code to the client.
|
||||||
|
// It returns a boolean value indicating whether the handle was successful or not.
|
||||||
|
// If the error is not nil, it calls HandleError to send an appropriate error response to the client.
|
||||||
|
// If the updated value is true, it sets the status code to 204 (No Content).
|
||||||
|
// If the updated value is false, it sets the status code to 304 (Not Modified).
|
||||||
|
func HandleUpdate(ctx *context.Context, updated bool, err error) bool {
|
||||||
|
if HandleError(ctx, err) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
if updated {
|
||||||
|
ctx.StatusCode(http.StatusNoContent)
|
||||||
|
} else {
|
||||||
|
ctx.StatusCode(http.StatusNotModified)
|
||||||
|
}
|
||||||
|
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
// HandleDelete handles a delete operation and sends a status code to the client.
|
||||||
|
// If the error is not nil, it calls HandleError to send an appropriate error response to the client.
|
||||||
|
// If the deleted value is true, it sets the status code to 204 (No Content).
|
||||||
|
// If the deleted value is false, it sets the status code to 304 (Not Modified).
|
||||||
|
func HandleDelete(ctx *context.Context, deleted bool, err error) bool {
|
||||||
|
return HandleUpdate(ctx, deleted, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// HandleDelete handles a delete operation and sends a status code to the client.
|
||||||
|
// If the error is not nil, it calls HandleError to send an appropriate error response to the client.
|
||||||
|
// It sets the status code to 204 (No Content).
|
||||||
|
func HandleDeleteNoContent(ctx *context.Context, err error) bool {
|
||||||
|
return HandleUpdate(ctx, true, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// ResponseFunc is a function which takes a context and a generic type T and returns a generic type R and an error.
|
||||||
|
// It is used to bind a request payload to a generic type T and call a service function with it.
|
||||||
|
type ResponseFunc[T, R any] interface {
|
||||||
|
func(stdContext.Context, T) (R, error)
|
||||||
|
}
|
||||||
|
|
||||||
|
// ResponseOnlyErrorFunc is a function which takes a context and a generic type T and returns an error.
|
||||||
|
// It is used to bind a request payload to a generic type T and call a service function with it.
|
||||||
|
// It is used for functions which do not return a response.
|
||||||
|
type ResponseOnlyErrorFunc[T any] interface {
|
||||||
|
func(stdContext.Context, T) error
|
||||||
|
}
|
||||||
|
|
||||||
|
func bindResponse[T, R any, F ResponseFunc[T, R]](ctx *context.Context, fn F, fnInput ...T) (R, bool) {
|
||||||
|
var req T
|
||||||
|
switch len(fnInput) {
|
||||||
|
case 0:
|
||||||
|
err := ctx.ReadJSON(&req)
|
||||||
|
if err != nil {
|
||||||
|
var resp R
|
||||||
|
return resp, !HandleError(ctx, err)
|
||||||
|
}
|
||||||
|
case 1:
|
||||||
|
req = fnInput[0]
|
||||||
|
default:
|
||||||
|
panic("invalid number of arguments")
|
||||||
|
}
|
||||||
|
|
||||||
|
resp, err := fn(ctx, req)
|
||||||
|
return resp, !HandleError(ctx, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// OK handles a generic response and error from a service call and sends a JSON response to the context.
|
||||||
|
// It returns a boolean value indicating whether the handle was successful or not.
|
||||||
|
// If the error is not nil, it calls HandleError to send an appropriate error response to the client.
|
||||||
|
// It sets the status code to 200 (OK) and sends any response as a JSON payload.
|
||||||
|
//
|
||||||
|
// Useful for Get/List/Fetch operations.
|
||||||
|
func OK[T, R any, F ResponseFunc[T, R]](ctx *context.Context, fn F, fnInput ...T) bool { // or Fetch.
|
||||||
|
resp, ok := bindResponse(ctx, fn, fnInput...)
|
||||||
|
if !ok {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
return Handle(ctx, resp, nil)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Create handles a create operation and sends a JSON response with the created resource to the client.
|
||||||
|
// It returns a boolean value indicating whether the handle was successful or not.
|
||||||
|
// If the error is not nil, it calls HandleError to send an appropriate error response to the client.
|
||||||
|
// It sets the status code to 201 (Created) and sends any response as a JSON payload
|
||||||
|
// note that if the response is a string, then it sends an {"id": resp} JSON payload).
|
||||||
|
//
|
||||||
|
// Useful for Insert operations.
|
||||||
|
func Create[T, R any, F ResponseFunc[T, R]](ctx *context.Context, fn F, fnInput ...T) bool {
|
||||||
|
resp, ok := bindResponse(ctx, fn, fnInput...)
|
||||||
|
if !ok {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
return HandleCreate(ctx, resp, nil)
|
||||||
|
}
|
||||||
|
|
||||||
|
// NoContent handles a generic response and error from a service call and sends a JSON response to the context.
|
||||||
|
// It returns a boolean value indicating whether the handle was successful or not.
|
||||||
|
// If the error is not nil, it calls HandleError to send an appropriate error response to the client.
|
||||||
|
// It sets the status code to 204 (No Content).
|
||||||
|
//
|
||||||
|
// Useful for Update and Deletion operations.
|
||||||
|
func NoContent[T any, F ResponseOnlyErrorFunc[T]](ctx *context.Context, fn F, fnInput ...T) bool {
|
||||||
|
toFn := func(c stdContext.Context, req T) (bool, error) {
|
||||||
|
return true, fn(ctx, req)
|
||||||
|
}
|
||||||
|
|
||||||
|
return NoContentOrNotModified(ctx, toFn, fnInput...)
|
||||||
|
}
|
||||||
|
|
||||||
|
// NoContent handles a generic response and error from a service call and sends a JSON response to the context.
|
||||||
|
// It returns a boolean value indicating whether the handle was successful or not.
|
||||||
|
// If the error is not nil, it calls HandleError to send an appropriate error response to the client.
|
||||||
|
// If the response is true, it sets the status code to 204 (No Content).
|
||||||
|
// If the response is false, it sets the status code to 304 (Not Modified).
|
||||||
|
//
|
||||||
|
// Useful for Update and Deletion operations.
|
||||||
|
func NoContentOrNotModified[T any, F ResponseFunc[T, bool]](ctx *context.Context, fn F, fnInput ...T) bool {
|
||||||
|
resp, ok := bindResponse(ctx, fn, fnInput...)
|
||||||
|
if !ok {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
return HandleUpdate(ctx, bool(resp), nil)
|
||||||
|
}
|
||||||
|
|
||||||
// ReadPayload reads a JSON payload from the context and returns it as a generic type T.
|
// ReadPayload reads a JSON payload from the context and returns it as a generic type T.
|
||||||
// It also returns a boolean value indicating whether the read was successful or not.
|
// It also returns a boolean value indicating whether the read was successful or not.
|
||||||
// If the read fails, it sends an appropriate error response to the client.
|
// If the read fails, it sends an appropriate error response to the client.
|
||||||
|
@ -60,86 +246,3 @@ func ReadPaginationOptions[T /* T is FilterOptions */ any](ctx *context.Context)
|
||||||
|
|
||||||
return list, filter, true
|
return list, filter, true
|
||||||
}
|
}
|
||||||
|
|
||||||
// Handle handles a generic response and error from a service call and sends a JSON response to the context.
|
|
||||||
// It returns a boolean value indicating whether the handle was successful or not.
|
|
||||||
// If the error is not nil, it calls HandleError to send an appropriate error response to the client.
|
|
||||||
func Handle(ctx *context.Context, resp interface{}, err error) bool {
|
|
||||||
if HandleError(ctx, err) {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
return ctx.JSON(resp) == nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// IDPayload is a simple struct which describes a json id value.
|
|
||||||
type IDPayload struct {
|
|
||||||
ID string `json:"id"`
|
|
||||||
}
|
|
||||||
|
|
||||||
// HandleCreate handles a create operation and sends a JSON response with the created id to the client.
|
|
||||||
// It returns a boolean value indicating whether the handle was successful or not.
|
|
||||||
// If the error is not nil, it calls HandleError to send an appropriate error response to the client.
|
|
||||||
// If the id is not empty, it sets the status code to 201 (Created) and sends the id as a JSON payload.
|
|
||||||
func HandleCreate(ctx *context.Context, id string, err error) bool {
|
|
||||||
if HandleError(ctx, err) {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
ctx.StatusCode(http.StatusCreated)
|
|
||||||
|
|
||||||
if id != "" {
|
|
||||||
ctx.JSON(IDPayload{ID: id})
|
|
||||||
}
|
|
||||||
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
|
|
||||||
// HandleCreateResponse handles a create operation and sends a JSON response with the created resource to the client.
|
|
||||||
// It returns a boolean value indicating whether the handle was successful or not.
|
|
||||||
// If the error is not nil, it calls HandleError to send an appropriate error response to the client.
|
|
||||||
// If the response is not nil, it sets the status code to 201 (Created) and sends the response as a JSON payload.
|
|
||||||
func HandleCreateResponse(ctx *context.Context, resp interface{}, err error) bool {
|
|
||||||
if HandleError(ctx, err) {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
ctx.StatusCode(http.StatusCreated)
|
|
||||||
if resp != nil {
|
|
||||||
return ctx.JSON(resp) == nil
|
|
||||||
}
|
|
||||||
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
|
|
||||||
// HandleUpdate handles an update operation and sends a status code to the client.
|
|
||||||
// It returns a boolean value indicating whether the handle was successful or not.
|
|
||||||
// If the error is not nil, it calls HandleError to send an appropriate error response to the client.
|
|
||||||
// If the updated value is true, it sets the status code to 204 (No Content).
|
|
||||||
// If the updated value is false, it sets the status code to 304 (Not Modified).
|
|
||||||
func HandleUpdate(ctx *context.Context, updated bool, err error) bool {
|
|
||||||
if HandleError(ctx, err) {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
if updated {
|
|
||||||
ctx.StatusCode(http.StatusNoContent)
|
|
||||||
} else {
|
|
||||||
ctx.StatusCode(http.StatusNotModified)
|
|
||||||
}
|
|
||||||
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
|
|
||||||
// HandleDelete handles a delete operation and sends a status code to the client.
|
|
||||||
// If the error is not nil, it calls HandleError to send an appropriate error response to the client.
|
|
||||||
// It sets the status code to 204 (No Content).
|
|
||||||
func HandleDelete(ctx *context.Context, err error) bool {
|
|
||||||
if HandleError(ctx, err) {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
ctx.StatusCode(http.StatusNoContent)
|
|
||||||
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
|
|
Loading…
Reference in New Issue
Block a user