mirror of
https://github.com/sablierapp/sablier.git
synced 2025-12-21 13:23:03 +01:00
This features adds rfc7807 Problem detail responses when an error happens processing a request. This will greatly improve the common issues with "blank pages" and "404 pages" issues which should now properly tell the user what input was wrong (group that does not exist, container name that does not exist, etc.)
133 lines
3.9 KiB
Go
133 lines
3.9 KiB
Go
package api
|
|
|
|
import (
|
|
"bufio"
|
|
"bytes"
|
|
"errors"
|
|
"fmt"
|
|
"github.com/gin-gonic/gin"
|
|
"github.com/sablierapp/sablier/app/http/routes"
|
|
"github.com/sablierapp/sablier/app/http/routes/models"
|
|
"github.com/sablierapp/sablier/app/instance"
|
|
"github.com/sablierapp/sablier/app/sessions"
|
|
"github.com/sablierapp/sablier/app/theme"
|
|
log "github.com/sirupsen/logrus"
|
|
"sort"
|
|
"strconv"
|
|
"strings"
|
|
)
|
|
|
|
func StartDynamic(router *gin.RouterGroup, s *routes.ServeStrategy) {
|
|
router.GET("/strategies/dynamic", func(c *gin.Context) {
|
|
request := models.DynamicRequest{
|
|
Theme: s.StrategyConfig.Dynamic.DefaultTheme,
|
|
ShowDetails: s.StrategyConfig.Dynamic.ShowDetailsByDefault,
|
|
RefreshFrequency: s.StrategyConfig.Dynamic.DefaultRefreshFrequency,
|
|
SessionDuration: s.SessionsConfig.DefaultDuration,
|
|
}
|
|
|
|
if err := c.ShouldBind(&request); err != nil {
|
|
AbortWithProblemDetail(c, ProblemValidation(err))
|
|
return
|
|
}
|
|
|
|
if len(request.Names) == 0 && request.Group == "" {
|
|
AbortWithProblemDetail(c, ProblemValidation(errors.New("'names' or 'group' query parameter must be set")))
|
|
return
|
|
}
|
|
|
|
if len(request.Names) > 0 && request.Group != "" {
|
|
AbortWithProblemDetail(c, ProblemValidation(errors.New("'names' and 'group' query parameters are both set, only one must be set")))
|
|
return
|
|
}
|
|
|
|
var sessionState *sessions.SessionState
|
|
var err error
|
|
if len(request.Names) > 0 {
|
|
sessionState, err = s.SessionsManager.RequestSession(request.Names, request.SessionDuration)
|
|
} else {
|
|
sessionState, err = s.SessionsManager.RequestSessionGroup(request.Group, request.SessionDuration)
|
|
var groupNotFoundError sessions.ErrGroupNotFound
|
|
if errors.As(err, &groupNotFoundError) {
|
|
AbortWithProblemDetail(c, ProblemGroupNotFound(groupNotFoundError))
|
|
return
|
|
}
|
|
}
|
|
|
|
if err != nil {
|
|
AbortWithProblemDetail(c, ProblemError(err))
|
|
return
|
|
}
|
|
|
|
if sessionState == nil {
|
|
AbortWithProblemDetail(c, ProblemError(errors.New("session could not be created, please check logs for more details")))
|
|
return
|
|
}
|
|
|
|
AddSablierHeader(c, sessionState)
|
|
|
|
renderOptions := theme.Options{
|
|
DisplayName: request.DisplayName,
|
|
ShowDetails: request.ShowDetails,
|
|
SessionDuration: request.SessionDuration,
|
|
RefreshFrequency: request.RefreshFrequency,
|
|
InstanceStates: sessionStateToRenderOptionsInstanceState(sessionState),
|
|
}
|
|
|
|
buf := new(bytes.Buffer)
|
|
writer := bufio.NewWriter(buf)
|
|
err = s.Theme.Render(request.Theme, renderOptions, writer)
|
|
var themeNotFound theme.ErrThemeNotFound
|
|
if errors.As(err, &themeNotFound) {
|
|
AbortWithProblemDetail(c, ProblemThemeNotFound(themeNotFound))
|
|
return
|
|
}
|
|
writer.Flush()
|
|
|
|
c.Header("Cache-Control", "no-cache")
|
|
c.Header("Content-Type", "text/html")
|
|
c.Header("Content-Length", strconv.Itoa(buf.Len()))
|
|
c.Writer.Write(buf.Bytes())
|
|
})
|
|
}
|
|
|
|
func sessionStateToRenderOptionsInstanceState(sessionState *sessions.SessionState) (instances []theme.Instance) {
|
|
if sessionState == nil {
|
|
log.Warnf("sessionStateToRenderOptionsInstanceState: sessionState is nil")
|
|
return
|
|
}
|
|
sessionState.Instances.Range(func(key, value any) bool {
|
|
if value != nil {
|
|
instances = append(instances, instanceStateToRenderOptionsRequestState(value.(sessions.InstanceState).Instance))
|
|
} else {
|
|
log.Warnf("sessionStateToRenderOptionsInstanceState: sessionState instance is nil, key: %v", key)
|
|
}
|
|
|
|
return true
|
|
})
|
|
|
|
sort.SliceStable(instances, func(i, j int) bool {
|
|
return strings.Compare(instances[i].Name, instances[j].Name) == -1
|
|
})
|
|
|
|
return
|
|
}
|
|
|
|
func instanceStateToRenderOptionsRequestState(instanceState *instance.State) theme.Instance {
|
|
|
|
var err error
|
|
if instanceState.Message == "" {
|
|
err = nil
|
|
} else {
|
|
err = fmt.Errorf(instanceState.Message)
|
|
}
|
|
|
|
return theme.Instance{
|
|
Name: instanceState.Name,
|
|
Status: instanceState.Status,
|
|
CurrentReplicas: instanceState.CurrentReplicas,
|
|
DesiredReplicas: instanceState.DesiredReplicas,
|
|
Error: err,
|
|
}
|
|
}
|