draincloud-core/internal/processor/gin_processor.go
2025-01-02 15:34:47 -08:00

108 lines
3.0 KiB
Go

package processor
import (
"context"
"errors"
"fmt"
"net/http"
"git.optclblast.xyz/draincloud/draincloud-core/internal/common"
"git.optclblast.xyz/draincloud/draincloud-core/internal/domain"
"git.optclblast.xyz/draincloud/draincloud-core/internal/errs"
"git.optclblast.xyz/draincloud/draincloud-core/internal/handler"
"git.optclblast.xyz/draincloud/draincloud-core/internal/logger"
resolvedispatcher "git.optclblast.xyz/draincloud/draincloud-core/internal/resolve_dispatcher"
"git.optclblast.xyz/draincloud/draincloud-core/internal/storage"
"github.com/gin-gonic/gin"
"golang.org/x/sync/errgroup"
)
type GinProcessor struct {
rp *common.RequestPool
authStorage storage.AuthStorage
resolveDispatcher *resolvedispatcher.ResolveDispatcher
}
func NewGinProcessor(
authStorage storage.AuthStorage,
resolveDispatcher *resolvedispatcher.ResolveDispatcher,
) *GinProcessor {
return &GinProcessor{
rp: common.NewRequestPool(),
authStorage: authStorage,
resolveDispatcher: resolveDispatcher,
}
}
func (p *GinProcessor) Process(handler handler.Handler) gin.HandlerFunc {
return func(ctx *gin.Context) {
req := common.NewRequestFromHttp(p.rp, ctx.Request)
ctx.Request = ctx.Request.WithContext(context.WithValue(ctx.Request.Context(), "__request_id", req.ID))
// 1. Resolve the resolvers, collect all data required
// 2. Try process oprional resolvers
err := p.resolve(ctx, handler, req)
if err != nil {
p.writeError(ctx, err)
return
}
// 3. Call preprocessing fn's, middlewares etc.
if err = handler.GetPreprocessFn()(ctx, req, wrapGin(ctx)); err != nil {
p.writeError(ctx, err)
return
}
// 4. Call handler.ProcessFn
if err = handler.GetProcessFn()(ctx, req, wrapGin(ctx)); err != nil {
p.writeError(ctx, err)
return
}
}
}
func (p *GinProcessor) resolve(ctx context.Context, h handler.Handler, req *common.Request) error {
eg, ctx := errgroup.WithContext(ctx)
for _, r := range h.GetRequiredResolveParams() {
resolver, err := p.resolveDispatcher.GetResolver(r)
if err != nil {
return fmt.Errorf("failed to resolve '%s' param: no resolver provided: %w", r, err)
}
resolveValueName := r
eg.Go(func() error {
if resolveErr := resolver.Resolve(ctx, req); resolveErr != nil {
return fmt.Errorf("failed to resolve '%s' value: %w", resolveValueName, resolveErr)
}
return nil
})
}
if err := eg.Wait(); err != nil {
return err
}
return nil
}
func (p *GinProcessor) writeError(ctx *gin.Context, err error) {
logger.Error(ctx, "error process request", logger.Err(err))
switch {
case errors.Is(err, errs.ErrorAccessDenied):
ctx.JSON(http.StatusInternalServerError, domain.ErrorJson{
Code: http.StatusForbidden,
Message: err.Error(),
})
case errors.Is(err, errs.ErrorSessionExpired):
ctx.JSON(http.StatusInternalServerError, domain.ErrorJson{
Code: http.StatusForbidden,
Message: err.Error(),
})
default:
ctx.JSON(http.StatusInternalServerError, domain.ErrorJson{
Code: http.StatusInternalServerError,
Message: "Internal Error",
})
}
}