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", }) } }