feat(api): Don't enforce JSON content type for GET and DELETE requests and enhance cross-module code sharing with a commons module
All checks were successful
/ build (push) Successful in 4m52s

This commit is contained in:
Varakh 2024-06-11 23:47:30 +02:00
parent 20588b44bf
commit f37ea4fbbb
19 changed files with 61 additions and 56 deletions

View file

@ -4,7 +4,7 @@ Changes adhere to [semantic versioning](https://semver.org).
## [3.0.2] - UNRELEASED
* ...
* Don't enforce JSON content type for GET and DELETE requests
## [3.0.1] - 2024/06/10

View file

@ -109,8 +109,7 @@ platform, uploads produced artifacts to that release and publishes docker images
**Before** doing so, please ensure that the **commit on `master`** has the **correct version settings** and has been
built successfully:
* Adapt `constants_app.go` and change `Version` to the correct version number
* Adapt `app.go` and change `version` to the correct version number (CLI)
* Adapt `commons/constants.go` and change `Version` to the correct version number
* Adapt `CHANGELOG.md` to reflect changes and ensure a date is properly set in the header, also add a reference link
in footer (link to scm git tag source)
* Adapt `api.yaml`: `version` attribute must reflect the to be released version
@ -118,8 +117,7 @@ built successfully:
After the release has been created, ensure to change the following settings for the _next development cycle_:
* Adapt `constants_app.go` and change `Version` to the _next_ version number
* Adapt `app.go` and change `version` to the _next_ version number (CLI)
* Adapt `commons/constants.go` and change `Version` to the _next_ version number
* Adapt `CHANGELOG.md` and add an _UNRELEASED_ section
* Adapt `api.yaml`: `version` attribute must reflect the _next_ version number
* Adapt `env: VERSION_*` in `.forgejo/workflows/release.yaml` to _next_ version number

View file

@ -1,5 +1,15 @@
package api
const (
HeaderAppName = "X-App-Name"
HeaderAppVersion = "X-App-Version"
HeaderWebhookToken = "X-Webhook-Token"
HeaderContentType = "Content-Type"
HeaderContentTypeApplicationJson = "application/json"
)
// UpdateState state of an update
type UpdateState string

5
commons/constants.go Normal file
View file

@ -0,0 +1,5 @@
package commons
const (
Version = "3.0.2"
)

View file

@ -225,6 +225,6 @@ func (h *actionHandler) delete(c *gin.Context) {
return
}
c.Header(headerContentType, headerContentTypeApplicationJson)
c.Header(api.HeaderContentType, api.HeaderContentTypeApplicationJson)
c.Status(http.StatusNoContent)
}

View file

@ -97,6 +97,6 @@ func (h *actionInvocationHandler) delete(c *gin.Context) {
return
}
c.Header(headerContentType, headerContentTypeApplicationJson)
c.Header(api.HeaderContentType, api.HeaderContentTypeApplicationJson)
c.Status(http.StatusNoContent)
}

View file

@ -1,6 +1,7 @@
package server
import (
"git.myservermanager.com/varakh/upda/api"
"github.com/gin-gonic/gin"
"net/http"
)
@ -13,6 +14,6 @@ func newAuthHandler() *authHandler {
}
func (h *authHandler) login(c *gin.Context) {
c.Header(headerContentType, headerContentTypeApplicationJson)
c.Header(api.HeaderContentType, api.HeaderContentTypeApplicationJson)
c.Status(http.StatusNoContent)
}

View file

@ -3,6 +3,7 @@ package server
import (
"errors"
"fmt"
"git.myservermanager.com/varakh/upda/api"
"git.myservermanager.com/varakh/upda/util"
"github.com/gin-gonic/gin"
"github.com/go-playground/validator/v10"
@ -20,7 +21,7 @@ func errAbortWithValidatorPayload(c *gin.Context, err error) {
}
resErr := newServiceError(illegalArgument, fmt.Errorf("validation error: %v (%w)", util.ValuesString(errorMap), err))
c.Header(headerContentType, headerContentTypeApplicationJson)
c.Header(api.HeaderContentType, api.HeaderContentTypeApplicationJson)
_ = c.AbortWithError(http.StatusBadRequest, resErr)
return
}

View file

@ -66,6 +66,6 @@ func (h *eventHandler) delete(c *gin.Context) {
return
}
c.Header(headerContentType, headerContentTypeApplicationJson)
c.Header(api.HeaderContentType, api.HeaderContentTypeApplicationJson)
c.Status(http.StatusNoContent)
}

View file

@ -2,6 +2,7 @@ package server
import (
"git.myservermanager.com/varakh/upda/api"
"git.myservermanager.com/varakh/upda/commons"
"github.com/gin-gonic/gin"
"net/http"
)
@ -17,7 +18,7 @@ func newInfoHandler(a *appConfig) *infoHandler {
func (h *infoHandler) show(c *gin.Context) {
c.JSON(http.StatusOK, api.DataResponse{Data: gin.H{
"name": name,
"version": version,
"Version": commons.Version,
"timeZone": h.appConfig.timeZone,
}})
}

View file

@ -92,6 +92,6 @@ func (h *secretHandler) delete(c *gin.Context) {
return
}
c.Header(headerContentType, headerContentTypeApplicationJson)
c.Header(api.HeaderContentType, api.HeaderContentTypeApplicationJson)
c.Status(http.StatusNoContent)
}

View file

@ -101,6 +101,6 @@ func (h *updateHandler) delete(c *gin.Context) {
return
}
c.Header(headerContentType, headerContentTypeApplicationJson)
c.Header(api.HeaderContentType, api.HeaderContentTypeApplicationJson)
c.Status(http.StatusNoContent)
}

View file

@ -125,6 +125,6 @@ func (h *webhookHandler) delete(c *gin.Context) {
return
}
c.Header(headerContentType, headerContentTypeApplicationJson)
c.Header(api.HeaderContentType, api.HeaderContentTypeApplicationJson)
c.Status(http.StatusNoContent)
}

View file

@ -17,7 +17,7 @@ func newWebhookInvocationHandler(i *webhookInvocationService, w *webhookService)
}
func (h *webhookInvocationHandler) execute(c *gin.Context) {
tokenHeader := c.GetHeader(headerWebhookToken)
tokenHeader := c.GetHeader(api.HeaderWebhookToken)
webhookId := c.Param("id")
var w *Webhook
@ -57,6 +57,6 @@ func (h *webhookInvocationHandler) execute(c *gin.Context) {
return
}
c.Header(headerContentType, headerContentTypeApplicationJson)
c.Header(api.HeaderContentType, api.HeaderContentTypeApplicationJson)
c.Status(http.StatusNoContent)
}

View file

@ -3,6 +3,7 @@ package server
import (
"fmt"
"git.myservermanager.com/varakh/upda/api"
"git.myservermanager.com/varakh/upda/commons"
"github.com/gin-gonic/gin"
"net/http"
"strings"
@ -10,7 +11,7 @@ import (
func middlewareAppName() gin.HandlerFunc {
return func(c *gin.Context) {
c.Header(headerAppName, name)
c.Header(api.HeaderAppName, name)
c.Next()
}
}
@ -31,7 +32,7 @@ func middlewareGlobalMethodNotAllowed() gin.HandlerFunc {
func middlewareEnforceJsonContentType() gin.HandlerFunc {
return func(c *gin.Context) {
if c.Request.Method != http.MethodOptions && !strings.HasPrefix(c.GetHeader(headerContentType), headerContentTypeApplicationJson) {
if c.Request.Method != http.MethodOptions && !strings.HasPrefix(c.GetHeader(api.HeaderContentType), api.HeaderContentTypeApplicationJson) {
c.AbortWithStatusJSON(http.StatusBadRequest, api.NewErrorResponseWithStatusAndMessage(string(illegalArgument), "content-type must be application/json"))
return
}
@ -41,14 +42,14 @@ func middlewareEnforceJsonContentType() gin.HandlerFunc {
func middlewareAppVersion() gin.HandlerFunc {
return func(c *gin.Context) {
c.Header(headerAppVersion, version)
c.Header(api.HeaderAppVersion, commons.Version)
c.Next()
}
}
func middlewareAppContentType() gin.HandlerFunc {
return func(c *gin.Context) {
c.Header(headerContentType, headerContentTypeApplicationJson)
c.Header(api.HeaderContentType, api.HeaderContentTypeApplicationJson)
c.Next()
}
}
@ -61,7 +62,7 @@ func middlewareErrorHandler() gin.HandlerFunc {
if len(c.Errors) > 0 {
// status -1 doesn't overwrite existing status code
c.Header(headerContentType, headerContentTypeApplicationJson)
c.Header(api.HeaderContentType, api.HeaderContentTypeApplicationJson)
c.JSON(-1, api.NewErrorResponseWithStatusAndMessage(errCodeToStr(c.Errors.Last()), c.Errors.Last().Error()))
return
}

View file

@ -100,7 +100,6 @@ func Start() {
hh := newHealthHandler()
authH := newAuthHandler()
router.Use(middlewareEnforceJsonContentType())
router.Use(middlewareAppName())
router.Use(middlewareAppVersion())
router.Use(middlewareAppContentType())
@ -121,7 +120,7 @@ func Start() {
apiPublicGroup.GET("/health", hh.show)
apiPublicGroup.GET("/info", ih.show)
apiPublicGroup.POST("/webhooks/:id", wih.execute)
apiPublicGroup.POST("/webhooks/:id", middlewareEnforceJsonContentType(), wih.execute)
var authMethodHandler gin.HandlerFunc
@ -141,14 +140,14 @@ func Start() {
apiAuthGroup.GET("/updates", uh.paginate)
apiAuthGroup.GET("/updates/:id", uh.get)
apiAuthGroup.PATCH("/updates/:id/state", uh.updateState)
apiAuthGroup.PATCH("/updates/:id/state", middlewareEnforceJsonContentType(), uh.updateState)
apiAuthGroup.DELETE("/updates/:id", uh.delete)
apiAuthGroup.GET("/webhooks", wh.paginate)
apiAuthGroup.POST("/webhooks", wh.create)
apiAuthGroup.POST("/webhooks", middlewareEnforceJsonContentType(), wh.create)
apiAuthGroup.GET("/webhooks/:id", wh.get)
apiAuthGroup.PATCH("/webhooks/:id/label", wh.updateLabel)
apiAuthGroup.PATCH("/webhooks/:id/ignore-host", wh.updateIgnoreHost)
apiAuthGroup.PATCH("/webhooks/:id/label", middlewareEnforceJsonContentType(), wh.updateLabel)
apiAuthGroup.PATCH("/webhooks/:id/ignore-host", middlewareEnforceJsonContentType(), wh.updateIgnoreHost)
apiAuthGroup.DELETE("/webhooks/:id", wh.delete)
apiAuthGroup.GET("/events", eh.window)
@ -157,22 +156,22 @@ func Start() {
apiAuthGroup.GET("/secrets", sh.getAll)
apiAuthGroup.GET("/secrets/:id", sh.get)
apiAuthGroup.POST("/secrets", sh.create)
apiAuthGroup.PATCH("/secrets/:id/value", sh.updateValue)
apiAuthGroup.POST("/secrets", middlewareEnforceJsonContentType(), sh.create)
apiAuthGroup.PATCH("/secrets/:id/value", middlewareEnforceJsonContentType(), sh.updateValue)
apiAuthGroup.DELETE("/secrets/:id", sh.delete)
apiAuthGroup.GET("/actions", ah.paginate)
apiAuthGroup.POST("/actions", ah.create)
apiAuthGroup.POST("/actions", middlewareEnforceJsonContentType(), ah.create)
apiAuthGroup.GET("/actions/:id", ah.get)
apiAuthGroup.PATCH("/actions/:id/label", ah.updateLabel)
apiAuthGroup.PATCH("/actions/:id/match-event", ah.updateMatchEvent)
apiAuthGroup.PATCH("/actions/:id/match-host", ah.updateMatchHost)
apiAuthGroup.PATCH("/actions/:id/match-application", ah.updateMatchApplication)
apiAuthGroup.PATCH("/actions/:id/match-provider", ah.updateMatchProvider)
apiAuthGroup.PATCH("/actions/:id/payload", ah.updatePayload)
apiAuthGroup.PATCH("/actions/:id/enabled", ah.updateEnabled)
apiAuthGroup.PATCH("/actions/:id/label", middlewareEnforceJsonContentType(), ah.updateLabel)
apiAuthGroup.PATCH("/actions/:id/match-event", middlewareEnforceJsonContentType(), ah.updateMatchEvent)
apiAuthGroup.PATCH("/actions/:id/match-host", middlewareEnforceJsonContentType(), ah.updateMatchHost)
apiAuthGroup.PATCH("/actions/:id/match-application", middlewareEnforceJsonContentType(), ah.updateMatchApplication)
apiAuthGroup.PATCH("/actions/:id/match-provider", middlewareEnforceJsonContentType(), ah.updateMatchProvider)
apiAuthGroup.PATCH("/actions/:id/payload", middlewareEnforceJsonContentType(), ah.updatePayload)
apiAuthGroup.PATCH("/actions/:id/enabled", middlewareEnforceJsonContentType(), ah.updateEnabled)
apiAuthGroup.DELETE("/actions/:id", ah.delete)
apiAuthGroup.POST("/actions/:id/test", aih.test)
apiAuthGroup.POST("/actions/:id/test", middlewareEnforceJsonContentType(), aih.test)
apiAuthGroup.GET("/action-invocations", aih.paginate)
apiAuthGroup.GET("/action-invocations/:id", aih.get)

View file

@ -1,11 +0,0 @@
package server
const (
headerAppName = "X-App-Name"
headerAppVersion = "X-App-Version"
headerWebhookToken = "X-Webhook-Token"
headerContentType = "Content-Type"
headerContentTypeApplicationJson = "application/json"
)

View file

@ -1,6 +1,5 @@
package server
const (
name = "upda"
version = "3.0.2"
name = "upda"
)

View file

@ -5,6 +5,7 @@ import (
"errors"
"fmt"
"git.myservermanager.com/varakh/upda/api"
"git.myservermanager.com/varakh/upda/commons"
"git.myservermanager.com/varakh/upda/util"
"github.com/go-resty/resty/v2"
"github.com/urfave/cli/v2"
@ -16,7 +17,7 @@ import (
const (
name = "upda-cli"
desc = "a commandline helper for upda"
version = "3.0.2"
version = commons.Version
envServerUrl = "UPDA_SERVER_URL"
envUser = "UPDA_USER"
@ -214,7 +215,7 @@ func webhookCreate(cCtx *cli.Context) error {
client.SetDisableWarn(true)
res, err := client.R().
SetBasicAuth(cCtx.String(flagUser), cCtx.String(flagPass)).
SetHeader("Content-Type", "application/json").
SetHeader(api.HeaderContentType, api.HeaderContentTypeApplicationJson).
SetBody(&payload).
SetResult(&successRes).
SetError(&errorRes).
@ -262,8 +263,8 @@ func webhookSend(cCtx *cli.Context) error {
client := resty.New()
client.SetDisableWarn(true)
res, err := client.R().
SetHeader("Content-Type", "application/json").
SetHeader("X-Webhook-Token", cCtx.String(flagWebhookToken)).
SetHeader(api.HeaderContentType, api.HeaderContentTypeApplicationJson).
SetHeader(api.HeaderWebhookToken, cCtx.String(flagWebhookToken)).
SetBody(payloadArg).
SetError(&errorRes).
Post(url)
@ -290,7 +291,7 @@ func updateShow(cCtx *cli.Context) error {
client.SetDisableWarn(true)
res, err := client.R().
SetBasicAuth(cCtx.String(flagUser), cCtx.String(flagPass)).
SetHeader("Content-Type", "application/json").
SetHeader(api.HeaderContentType, api.HeaderContentTypeApplicationJson).
SetResult(&successRes).
SetError(&errorRes).
Get(url)