From f37ea4fbbbcedeb8d55647baadcb3327b441eefc Mon Sep 17 00:00:00 2001 From: Varakh Date: Tue, 11 Jun 2024 23:47:30 +0200 Subject: [PATCH] feat(api): Don't enforce JSON content type for GET and DELETE requests and enhance cross-module code sharing with a commons module --- CHANGELOG.md | 2 +- README.md | 6 ++--- api/constants.go | 10 +++++++ commons/constants.go | 5 ++++ server/api_handler_action.go | 2 +- server/api_handler_action_invocation.go | 2 +- server/api_handler_auth.go | 3 ++- server/api_handler_error.go | 3 ++- server/api_handler_event.go | 2 +- server/api_handler_info.go | 3 ++- server/api_handler_secret.go | 2 +- server/api_handler_update.go | 2 +- server/api_handler_webhook.go | 2 +- server/api_handler_webhook_invocation.go | 4 +-- server/api_middleware.go | 11 ++++---- server/app.go | 33 ++++++++++++------------ server/constants_api.go | 11 -------- server/constants_app.go | 3 +-- terminal/app.go | 11 ++++---- 19 files changed, 61 insertions(+), 56 deletions(-) create mode 100644 commons/constants.go delete mode 100644 server/constants_api.go diff --git a/CHANGELOG.md b/CHANGELOG.md index dd64852..4deb1ac 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -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 diff --git a/README.md b/README.md index 2d12942..b2f4c00 100644 --- a/README.md +++ b/README.md @@ -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 diff --git a/api/constants.go b/api/constants.go index baec016..1b774cb 100644 --- a/api/constants.go +++ b/api/constants.go @@ -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 diff --git a/commons/constants.go b/commons/constants.go new file mode 100644 index 0000000..18d0e4e --- /dev/null +++ b/commons/constants.go @@ -0,0 +1,5 @@ +package commons + +const ( + Version = "3.0.2" +) diff --git a/server/api_handler_action.go b/server/api_handler_action.go index 7f5824a..ca8850f 100644 --- a/server/api_handler_action.go +++ b/server/api_handler_action.go @@ -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) } diff --git a/server/api_handler_action_invocation.go b/server/api_handler_action_invocation.go index fd96c83..d52d826 100644 --- a/server/api_handler_action_invocation.go +++ b/server/api_handler_action_invocation.go @@ -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) } diff --git a/server/api_handler_auth.go b/server/api_handler_auth.go index dfd2018..3d5a2c7 100644 --- a/server/api_handler_auth.go +++ b/server/api_handler_auth.go @@ -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) } diff --git a/server/api_handler_error.go b/server/api_handler_error.go index 62bc368..c521af7 100644 --- a/server/api_handler_error.go +++ b/server/api_handler_error.go @@ -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 } diff --git a/server/api_handler_event.go b/server/api_handler_event.go index 43aa472..893575d 100644 --- a/server/api_handler_event.go +++ b/server/api_handler_event.go @@ -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) } diff --git a/server/api_handler_info.go b/server/api_handler_info.go index 35a849a..1efd673 100644 --- a/server/api_handler_info.go +++ b/server/api_handler_info.go @@ -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, }}) } diff --git a/server/api_handler_secret.go b/server/api_handler_secret.go index c8525fa..e3b477f 100644 --- a/server/api_handler_secret.go +++ b/server/api_handler_secret.go @@ -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) } diff --git a/server/api_handler_update.go b/server/api_handler_update.go index e444878..de2e517 100644 --- a/server/api_handler_update.go +++ b/server/api_handler_update.go @@ -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) } diff --git a/server/api_handler_webhook.go b/server/api_handler_webhook.go index 316829a..1f11f6e 100644 --- a/server/api_handler_webhook.go +++ b/server/api_handler_webhook.go @@ -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) } diff --git a/server/api_handler_webhook_invocation.go b/server/api_handler_webhook_invocation.go index 60de86c..f87af4f 100644 --- a/server/api_handler_webhook_invocation.go +++ b/server/api_handler_webhook_invocation.go @@ -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) } diff --git a/server/api_middleware.go b/server/api_middleware.go index b70e6d3..9a30040 100644 --- a/server/api_middleware.go +++ b/server/api_middleware.go @@ -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 } diff --git a/server/app.go b/server/app.go index cfe119b..beee3dc 100644 --- a/server/app.go +++ b/server/app.go @@ -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) diff --git a/server/constants_api.go b/server/constants_api.go deleted file mode 100644 index 7c16be8..0000000 --- a/server/constants_api.go +++ /dev/null @@ -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" -) diff --git a/server/constants_app.go b/server/constants_app.go index 334b307..9c1f6ff 100644 --- a/server/constants_app.go +++ b/server/constants_app.go @@ -1,6 +1,5 @@ package server const ( - name = "upda" - version = "3.0.2" + name = "upda" ) diff --git a/terminal/app.go b/terminal/app.go index 20849f0..d984f7e 100644 --- a/terminal/app.go +++ b/terminal/app.go @@ -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)