refactor: http interfaces (#114)

* implement custom http handler interface

* implement trace_id

* normalize http method spacing for consistent logs

* fix failing test

* fix linter errors

* cleanup old dead code

* more route cleanup

* cleanup some inconsistent errors

* update and generate code

* make taskfile more consistent

* update task calls

* run tidy

* drop `@` tag for version

* use relative paths

* tidy

* fix auto-setting variables

* update build paths

* add contributing guide

* tidy
This commit is contained in:
Hayden 2022-10-29 18:15:35 -08:00 committed by GitHub
parent e2d93f8523
commit 6529549289
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
40 changed files with 984 additions and 808 deletions

View file

@ -5,11 +5,10 @@ import (
"fmt"
"net/http"
"github.com/go-chi/chi/v5"
"github.com/google/uuid"
"github.com/hay-kot/homebox/backend/ent/attachment"
"github.com/hay-kot/homebox/backend/internal/repo"
"github.com/hay-kot/homebox/backend/internal/services"
"github.com/hay-kot/homebox/backend/internal/sys/validate"
"github.com/hay-kot/homebox/backend/pkgs/server"
"github.com/rs/zerolog/log"
)
@ -29,19 +28,19 @@ type (
// @Param type formData string true "Type of file"
// @Param name formData string true "name of the file including extension"
// @Success 200 {object} repo.ItemOut
// @Failure 422 {object} []server.ValidationError
// @Failure 422 {object} server.ErrorResponse
// @Router /v1/items/{id}/attachments [POST]
// @Security Bearer
func (ctrl *V1Controller) HandleItemAttachmentCreate() http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
func (ctrl *V1Controller) HandleItemAttachmentCreate() server.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) error {
err := r.ParseMultipartForm(ctrl.maxUploadSize << 20)
if err != nil {
log.Err(err).Msg("failed to parse multipart form")
server.RespondError(w, http.StatusBadRequest, errors.New("failed to parse multipart form"))
return
return validate.NewRequestError(errors.New("failed to parse multipart form"), http.StatusBadRequest)
}
errs := make(server.ValidationErrors, 0)
errs := validate.NewFieldErrors()
file, _, err := r.FormFile("file")
if err != nil {
@ -51,8 +50,7 @@ func (ctrl *V1Controller) HandleItemAttachmentCreate() http.HandlerFunc {
errs = errs.Append("file", "file is required")
default:
log.Err(err).Msg("failed to get file from form")
server.RespondServerError(w)
return
return validate.NewRequestError(err, http.StatusInternalServerError)
}
}
@ -62,9 +60,8 @@ func (ctrl *V1Controller) HandleItemAttachmentCreate() http.HandlerFunc {
errs = errs.Append("name", "name is required")
}
if errs.HasErrors() {
server.Respond(w, http.StatusUnprocessableEntity, errs)
return
if !errs.Nil() {
return server.Respond(w, http.StatusUnprocessableEntity, errs)
}
attachmentType := r.FormValue("type")
@ -72,9 +69,9 @@ func (ctrl *V1Controller) HandleItemAttachmentCreate() http.HandlerFunc {
attachmentType = attachment.TypeAttachment.String()
}
id, err := ctrl.routeID(w, r)
id, err := ctrl.routeID(r)
if err != nil {
return
return err
}
ctx := services.NewContext(r.Context())
@ -89,11 +86,10 @@ func (ctrl *V1Controller) HandleItemAttachmentCreate() http.HandlerFunc {
if err != nil {
log.Err(err).Msg("failed to add attachment")
server.RespondServerError(w)
return
return validate.NewRequestError(err, http.StatusInternalServerError)
}
server.Respond(w, http.StatusCreated, item)
return server.Respond(w, http.StatusCreated, item)
}
}
@ -106,21 +102,21 @@ func (ctrl *V1Controller) HandleItemAttachmentCreate() http.HandlerFunc {
// @Success 200
// @Router /v1/items/{id}/attachments/download [GET]
// @Security Bearer
func (ctrl *V1Controller) HandleItemAttachmentDownload() http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
func (ctrl *V1Controller) HandleItemAttachmentDownload() server.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) error {
token := server.GetParam(r, "token", "")
doc, err := ctrl.svc.Items.AttachmentPath(r.Context(), token)
if err != nil {
log.Err(err).Msg("failed to get attachment")
server.RespondServerError(w)
return
return validate.NewRequestError(err, http.StatusInternalServerError)
}
w.Header().Set("Content-Disposition", fmt.Sprintf("attachment; filename=\"%s\"", doc.Title))
w.Header().Set("Content-Type", "application/octet-stream")
http.ServeFile(w, r, doc.Path)
return nil
}
}
@ -133,7 +129,7 @@ func (ctrl *V1Controller) HandleItemAttachmentDownload() http.HandlerFunc {
// @Success 200 {object} ItemAttachmentToken
// @Router /v1/items/{id}/attachments/{attachment_id} [GET]
// @Security Bearer
func (ctrl *V1Controller) HandleItemAttachmentToken() http.HandlerFunc {
func (ctrl *V1Controller) HandleItemAttachmentToken() server.HandlerFunc {
return ctrl.handleItemAttachmentsHandler
}
@ -145,7 +141,7 @@ func (ctrl *V1Controller) HandleItemAttachmentToken() http.HandlerFunc {
// @Success 204
// @Router /v1/items/{id}/attachments/{attachment_id} [DELETE]
// @Security Bearer
func (ctrl *V1Controller) HandleItemAttachmentDelete() http.HandlerFunc {
func (ctrl *V1Controller) HandleItemAttachmentDelete() server.HandlerFunc {
return ctrl.handleItemAttachmentsHandler
}
@ -158,66 +154,60 @@ func (ctrl *V1Controller) HandleItemAttachmentDelete() http.HandlerFunc {
// @Success 200 {object} repo.ItemOut
// @Router /v1/items/{id}/attachments/{attachment_id} [PUT]
// @Security Bearer
func (ctrl *V1Controller) HandleItemAttachmentUpdate() http.HandlerFunc {
func (ctrl *V1Controller) HandleItemAttachmentUpdate() server.HandlerFunc {
return ctrl.handleItemAttachmentsHandler
}
func (ctrl *V1Controller) handleItemAttachmentsHandler(w http.ResponseWriter, r *http.Request) {
ID, err := ctrl.routeID(w, r)
func (ctrl *V1Controller) handleItemAttachmentsHandler(w http.ResponseWriter, r *http.Request) error {
ID, err := ctrl.routeID(r)
if err != nil {
return
return err
}
attachmentId, err := uuid.Parse(chi.URLParam(r, "attachment_id"))
attachmentID, err := ctrl.routeUUID(r, "attachment_id")
if err != nil {
log.Err(err).Msg("failed to parse attachment_id param")
server.RespondError(w, http.StatusBadRequest, err)
return
return err
}
ctx := services.NewContext(r.Context())
switch r.Method {
// Token Handler
case http.MethodGet:
token, err := ctrl.svc.Items.AttachmentToken(ctx, ID, attachmentId)
token, err := ctrl.svc.Items.AttachmentToken(ctx, ID, attachmentID)
if err != nil {
switch err {
case services.ErrNotFound:
log.Err(err).
Str("id", attachmentId.String()).
Str("id", attachmentID.String()).
Msg("failed to find attachment with id")
server.RespondError(w, http.StatusNotFound, err)
return validate.NewRequestError(err, http.StatusNotFound)
case services.ErrFileNotFound:
log.Err(err).
Str("id", attachmentId.String()).
Str("id", attachmentID.String()).
Msg("failed to find file path for attachment with id")
log.Warn().Msg("attachment with no file path removed from database")
server.RespondError(w, http.StatusNotFound, err)
return validate.NewRequestError(err, http.StatusNotFound)
default:
log.Err(err).Msg("failed to get attachment")
server.RespondServerError(w)
return
return validate.NewRequestError(err, http.StatusInternalServerError)
}
}
server.Respond(w, http.StatusOK, ItemAttachmentToken{Token: token})
return server.Respond(w, http.StatusOK, ItemAttachmentToken{Token: token})
// Delete Attachment Handler
case http.MethodDelete:
err = ctrl.svc.Items.AttachmentDelete(r.Context(), ctx.GID, ID, attachmentId)
err = ctrl.svc.Items.AttachmentDelete(r.Context(), ctx.GID, ID, attachmentID)
if err != nil {
log.Err(err).Msg("failed to delete attachment")
server.RespondServerError(w)
return
return validate.NewRequestError(err, http.StatusInternalServerError)
}
server.Respond(w, http.StatusNoContent, nil)
return server.Respond(w, http.StatusNoContent, nil)
// Update Attachment Handler
case http.MethodPut:
@ -225,18 +215,18 @@ func (ctrl *V1Controller) handleItemAttachmentsHandler(w http.ResponseWriter, r
err = server.Decode(r, &attachment)
if err != nil {
log.Err(err).Msg("failed to decode attachment")
server.RespondError(w, http.StatusBadRequest, err)
return
return validate.NewRequestError(err, http.StatusBadRequest)
}
attachment.ID = attachmentId
attachment.ID = attachmentID
val, err := ctrl.svc.Items.AttachmentUpdate(ctx, ID, &attachment)
if err != nil {
log.Err(err).Msg("failed to delete attachment")
server.RespondServerError(w)
return
return validate.NewRequestError(err, http.StatusInternalServerError)
}
server.Respond(w, http.StatusOK, val)
return server.Respond(w, http.StatusOK, val)
}
return nil
}