108 lines
3.0 KiB
Go
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",
|
|
})
|
|
}
|
|
}
|