From 05242e7729c990b0247638c2d82a902684fa466d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Krzysztof=20Dry=C5=9B?= Date: Wed, 29 Nov 2023 13:13:38 +0100 Subject: [PATCH] fix: error handling for icons (#17) fix: error handling for icons Second batch of error handling in icon handlers --- .../icons/adapters/icons_gorm_repository.go | 12 +- .../icons_requests_gorm_repository.go | 15 +- .../mobile_web_services_gorm_repository.go | 14 +- .../api/icons/app/command/icons_requests.go | 145 +++++++++--------- internal/api/icons/app/queries/web_service.go | 2 +- internal/api/icons/ports/http.go | 18 ++- internal/common/aws/s3.go | 24 +-- 7 files changed, 115 insertions(+), 115 deletions(-) diff --git a/internal/api/icons/adapters/icons_gorm_repository.go b/internal/api/icons/adapters/icons_gorm_repository.go index 04b71ba..961401e 100644 --- a/internal/api/icons/adapters/icons_gorm_repository.go +++ b/internal/api/icons/adapters/icons_gorm_repository.go @@ -3,9 +3,12 @@ package adapters import ( "errors" "fmt" + "github.com/google/uuid" - "github.com/twofas/2fas-server/internal/api/icons/domain" "gorm.io/gorm" + + "github.com/twofas/2fas-server/internal/api/icons/domain" + "github.com/twofas/2fas-server/internal/common/db" ) type IconCouldNotBeFound struct { @@ -53,8 +56,11 @@ func (r *IconMysqlRepository) FindById(id uuid.UUID) (*domain.Icon, error) { result := r.db.First(&Icon, "id = ?", id.String()) - if errors.Is(result.Error, gorm.ErrRecordNotFound) { - return nil, IconCouldNotBeFound{IconId: id.String()} + if err := result.Error; err != nil { + if errors.Is(result.Error, gorm.ErrRecordNotFound) { + return nil, IconCouldNotBeFound{IconId: id.String()} + } + return nil, db.WrapError(err) } return Icon, nil diff --git a/internal/api/icons/adapters/icons_requests_gorm_repository.go b/internal/api/icons/adapters/icons_requests_gorm_repository.go index f1b396b..87912ab 100644 --- a/internal/api/icons/adapters/icons_requests_gorm_repository.go +++ b/internal/api/icons/adapters/icons_requests_gorm_repository.go @@ -3,9 +3,12 @@ package adapters import ( "errors" "fmt" + "github.com/google/uuid" - "github.com/twofas/2fas-server/internal/api/icons/domain" "gorm.io/gorm" + + "github.com/twofas/2fas-server/internal/api/icons/domain" + "github.com/twofas/2fas-server/internal/common/db" ) type IconRequestCouldNotBeFound struct { @@ -42,7 +45,7 @@ func (r *IconRequestMysqlRepository) Update(iconRequest *domain.IconRequest) err func (r *IconRequestMysqlRepository) Delete(iconRequest *domain.IconRequest) error { if err := r.db.Delete(iconRequest).Error; err != nil { - return err + return db.WrapError(err) } return nil @@ -52,9 +55,11 @@ func (r *IconRequestMysqlRepository) FindById(id uuid.UUID) (*domain.IconRequest iconRequest := &domain.IconRequest{} result := r.db.First(&iconRequest, "id = ?", id.String()) - - if errors.Is(result.Error, gorm.ErrRecordNotFound) { - return nil, IconRequestCouldNotBeFound{IconRequestId: id.String()} + if err := result.Error; err != nil { + if errors.Is(result.Error, gorm.ErrRecordNotFound) { + return nil, IconRequestCouldNotBeFound{IconRequestId: id.String()} + } + return nil, db.WrapError(err) } return iconRequest, nil diff --git a/internal/api/icons/adapters/mobile_web_services_gorm_repository.go b/internal/api/icons/adapters/mobile_web_services_gorm_repository.go index b84f8b9..55a1493 100644 --- a/internal/api/icons/adapters/mobile_web_services_gorm_repository.go +++ b/internal/api/icons/adapters/mobile_web_services_gorm_repository.go @@ -12,11 +12,11 @@ import ( ) type WebServiceCouldNotBeFound struct { - WebServiceId string + Identifier string } func (e WebServiceCouldNotBeFound) Error() string { - return fmt.Sprintf("Web service could not be found: %s", e.WebServiceId) + return fmt.Sprintf("Web service could not be found: %s", e.Identifier) } type WebServiceMysqlRepository struct { @@ -29,7 +29,7 @@ func NewWebServiceMysqlRepository(db *gorm.DB) *WebServiceMysqlRepository { func (r *WebServiceMysqlRepository) Save(webService *domain.WebService) error { if err := r.db.Create(webService).Error; err != nil { - return err + return db.WrapError(err) } return nil @@ -37,7 +37,7 @@ func (r *WebServiceMysqlRepository) Save(webService *domain.WebService) error { func (r *WebServiceMysqlRepository) Update(webService *domain.WebService) error { if err := r.db.Updates(webService).Error; err != nil { - return err + return db.WrapError(err) } return nil @@ -45,7 +45,7 @@ func (r *WebServiceMysqlRepository) Update(webService *domain.WebService) error func (r *WebServiceMysqlRepository) Delete(webService *domain.WebService) error { if err := r.db.Delete(webService).Error; err != nil { - return err + return db.WrapError(err) } return nil @@ -58,7 +58,7 @@ func (r *WebServiceMysqlRepository) FindById(id uuid.UUID) (*domain.WebService, if err := result.Error; err != nil { if errors.Is(err, gorm.ErrRecordNotFound) { - return nil, WebServiceCouldNotBeFound{WebServiceId: id.String()} + return nil, WebServiceCouldNotBeFound{Identifier: id.String()} } return nil, db.WrapError(err) } @@ -73,7 +73,7 @@ func (r *WebServiceMysqlRepository) FindByName(name string) (*domain.WebService, if err := result.Error; err != nil { if errors.Is(err, gorm.ErrRecordNotFound) { - return nil, errors.New("web service could not be found") + return nil, WebServiceCouldNotBeFound{Identifier: name} } return nil, db.WrapError(err) } diff --git a/internal/api/icons/app/command/icons_requests.go b/internal/api/icons/app/command/icons_requests.go index 8c05cbe..6218b07 100644 --- a/internal/api/icons/app/command/icons_requests.go +++ b/internal/api/icons/app/command/icons_requests.go @@ -2,15 +2,22 @@ package command import ( "encoding/json" - "github.com/doug-martin/goqu/v9" - "github.com/google/uuid" - "github.com/twofas/2fas-server/internal/api/icons/domain" - "github.com/twofas/2fas-server/internal/common/logging" - "github.com/twofas/2fas-server/internal/common/storage" - "gorm.io/datatypes" - "gorm.io/gorm" + "fmt" "image/png" "path/filepath" + + "github.com/pkg/errors" + "github.com/twofas/2fas-server/internal/api/icons/adapters" + + "github.com/doug-martin/goqu/v9" + "github.com/google/uuid" + "gorm.io/datatypes" + "gorm.io/gorm" + + "github.com/twofas/2fas-server/internal/api/icons/domain" + "github.com/twofas/2fas-server/internal/common/db" + "github.com/twofas/2fas-server/internal/common/logging" + "github.com/twofas/2fas-server/internal/common/storage" ) type CreateIconRequest struct { @@ -125,25 +132,21 @@ type UpdateWebServiceFromIconRequestHandler struct { func (h *UpdateWebServiceFromIconRequestHandler) Handle(cmd *UpdateWebServiceFromIconRequest) error { webServiceId, err := uuid.Parse(cmd.WebServiceId) - if err != nil { return err } iconRequestId, err := uuid.Parse(cmd.IconRequestId) - if err != nil { return err } iconRequest, err := h.IconsRequestsRepository.FindById(iconRequestId) - if err != nil { return err } webService, err := h.WebServiceRepository.FindById(webServiceId) - if err != nil { return err } @@ -153,23 +156,20 @@ func (h *UpdateWebServiceFromIconRequestHandler) Handle(cmd *UpdateWebServiceFro lightIconStoragePath := filepath.Join(iconsStoragePath, filepath.Base(iconRequest.LightIconUrl)) lightIconImg, err := h.IconsStorage.Get(lightIconStoragePath) - if err != nil { - return err + return fmt.Errorf("failed to get the icon from the storage: %w", err) } lightIconPng, err := png.Decode(lightIconImg) - if err != nil { - return err + return fmt.Errorf("failed to decode the icon as pgn: %w", err) } lightIconId := uuid.New() lightIconNewPath := filepath.Join(iconsStoragePath, lightIconId.String()+".png") newLightIconLocation, err := h.IconsStorage.Move(lightIconStoragePath, lightIconNewPath) - if err != nil { - return err + return fmt.Errorf("failed to move icons storage: %w", err) } lightIcon := &domain.Icon{ @@ -182,9 +182,8 @@ func (h *UpdateWebServiceFromIconRequestHandler) Handle(cmd *UpdateWebServiceFro } err = h.IconsRepository.Save(lightIcon) - if err != nil { - return err + return fmt.Errorf("failed to save light icon: %w", err) } iconsIds := []string{ @@ -195,23 +194,20 @@ func (h *UpdateWebServiceFromIconRequestHandler) Handle(cmd *UpdateWebServiceFro darkIconStoragePath := filepath.Join(iconsStoragePath, filepath.Base(iconRequest.DarkIconUrl)) darkIconImg, err := h.IconsStorage.Get(darkIconStoragePath) - if err != nil { - return err + return fmt.Errorf("failed to get dark icon: %w", err) } darkIconPng, err := png.Decode(darkIconImg) - if err != nil { - return err + return fmt.Errorf("failed to decode dark icon: %w", err) } darkIconId := uuid.New() darkIconNewPath := filepath.Join(iconsStoragePath, darkIconId.String()+".png") newDarkIconLocation, err := h.IconsStorage.Move(darkIconStoragePath, darkIconNewPath) - if err != nil { - return err + return fmt.Errorf("failed to move dark icon: %w", err) } darkIcon := &domain.Icon{ @@ -224,15 +220,17 @@ func (h *UpdateWebServiceFromIconRequestHandler) Handle(cmd *UpdateWebServiceFro } err = h.IconsRepository.Save(darkIcon) - if err != nil { - return err + return fmt.Errorf("failed to save dark icon: %w", err) } iconsIds = append(iconsIds, darkIconId.String()) } iconsJson, err := json.Marshal(iconsIds) + if err != nil { + return fmt.Errorf("failed to marshal icon ids: %w", err) + } iconsCollection := &domain.IconsCollection{ Id: iconsCollectionId, @@ -241,64 +239,65 @@ func (h *UpdateWebServiceFromIconRequestHandler) Handle(cmd *UpdateWebServiceFro } err = h.IconsCollectionsRepository.Save(iconsCollection) - if err != nil { - return err + return fmt.Errorf("failed to save icons collection: %w", err) } var webServiceIconsCollectionsIds []string err = json.Unmarshal(webService.IconsCollections, &webServiceIconsCollectionsIds) - if err != nil { - return err + return fmt.Errorf("failed to decode icons collection from web service: %w", err) } for _, outdatedIconsCollectionId := range webServiceIconsCollectionsIds { - id, _ := uuid.Parse(outdatedIconsCollectionId) + id, err := uuid.Parse(outdatedIconsCollectionId) + if err != nil { + return fmt.Errorf("failed to parse 'outdatedIconsCollectionId' %q: %w", outdatedIconsCollectionId, err) + } outDatedIconsCollection, err := h.IconsCollectionsRepository.FindById(id) - if err != nil { logging. WithField("icon_collection_id", outdatedIconsCollectionId). Error("Out of date icons collection cannot be found") - } - - err = h.IconsCollectionsRepository.Delete(outDatedIconsCollection) - - if err != nil { - logging. - WithField("icon_collection_id", outdatedIconsCollectionId). - Error("Cannot delete out of date icons collection") + } else { + err = h.IconsCollectionsRepository.Delete(outDatedIconsCollection) + if err != nil { + logging. + WithField("icon_collection_id", outdatedIconsCollectionId). + Error("Cannot delete out of date icons collection") + } } var outdatedCollectionIcons []string - err = json.Unmarshal(outDatedIconsCollection.Icons, &outdatedCollectionIcons) - if err != nil { - return err + return fmt.Errorf("failed to decode 'outdatedCollectionIcons': %w", err) } for _, outdatedIconId := range webServiceIconsCollectionsIds { iconId, _ := uuid.Parse(outdatedIconId) iconToDelete, err := h.IconsRepository.FindById(iconId) - if err == nil { h.IconsRepository.Delete(iconToDelete) + } else if db.IsDBError(err) { + logging. + WithField("icon_id", iconId). + Error("Failed to delete icon by id") } } } webService.IconsCollections = datatypes.JSON(`["` + iconsCollection.Id.String() + `"]`) - h.WebServiceRepository.Update(webService) + if err := h.WebServiceRepository.Update(webService); err != nil { + return fmt.Errorf("failed to update web service %q: %w", webService.Id.String(), err) + } err = h.IconsRequestsRepository.Delete(iconRequest) - if err != nil { - return err + return fmt.Errorf("failed to delete icon request %q: %w", iconRequest.Id.String(), err) } return nil @@ -319,21 +318,23 @@ type TransformIconRequestToWebServiceHandler struct { func (h *TransformIconRequestToWebServiceHandler) Handle(cmd *TransformIconRequestToWebService) error { iconRequestId, err := uuid.Parse(cmd.IconRequestId) - if err != nil { - return err + return fmt.Errorf("invalid 'iconRequestId': %w", err) } iconRequest, err := h.IconsRequestsRepository.FindById(iconRequestId) - if err != nil { return err } - conflict, err := h.WebServiceRepository.FindByName(iconRequest.ServiceName) - - if conflict != nil { + _, err = h.WebServiceRepository.FindByName(iconRequest.ServiceName) + if err == nil { return domain.WebServiceAlreadyExistsError{Name: iconRequest.ServiceName} + } else { + var notFound adapters.WebServiceCouldNotBeFound + if !errors.Is(err, ¬Found) { + return fmt.Errorf("failed to find web service by name: %w", err) + } } iconsCollectionId := uuid.New() @@ -341,23 +342,20 @@ func (h *TransformIconRequestToWebServiceHandler) Handle(cmd *TransformIconReque lightIconStoragePath := filepath.Join(iconsStoragePath, filepath.Base(iconRequest.LightIconUrl)) lightIconImg, err := h.IconsStorage.Get(lightIconStoragePath) - if err != nil { - return err + return fmt.Errorf("failed to get light icon: %w", err) } lightIconPng, err := png.Decode(lightIconImg) - if err != nil { - return err + return fmt.Errorf("failed to decode light icon: %w", err) } lightIconId := uuid.New() lightIconNewPath := filepath.Join(iconsStoragePath, lightIconId.String()+".png") newLightIconLocation, err := h.IconsStorage.Move(lightIconStoragePath, lightIconNewPath) - if err != nil { - return err + return fmt.Errorf("failed to move light icon: %w", err) } lightIcon := &domain.Icon{ @@ -370,9 +368,8 @@ func (h *TransformIconRequestToWebServiceHandler) Handle(cmd *TransformIconReque } err = h.IconsRepository.Save(lightIcon) - if err != nil { - return err + return fmt.Errorf("failed to save light icon: %w", err) } iconsIds := []string{ @@ -383,23 +380,20 @@ func (h *TransformIconRequestToWebServiceHandler) Handle(cmd *TransformIconReque darkIconStoragePath := filepath.Join(iconsStoragePath, filepath.Base(iconRequest.DarkIconUrl)) darkIconImg, err := h.IconsStorage.Get(darkIconStoragePath) - if err != nil { - return err + return fmt.Errorf("failed to get dark icon: %w", err) } darkIconPng, err := png.Decode(darkIconImg) - if err != nil { - return err + return fmt.Errorf("failed to decode dark icon: %w", err) } darkIconId := uuid.New() darkIconNewPath := filepath.Join(iconsStoragePath, darkIconId.String()+".png") newDarkIconLocation, err := h.IconsStorage.Move(darkIconStoragePath, darkIconNewPath) - if err != nil { - return err + return fmt.Errorf("failed to move dark icon: %w", err) } darkIcon := &domain.Icon{ @@ -412,15 +406,17 @@ func (h *TransformIconRequestToWebServiceHandler) Handle(cmd *TransformIconReque } err = h.IconsRepository.Save(darkIcon) - if err != nil { - return err + return fmt.Errorf("failed to save dark icon: %w", err) } iconsIds = append(iconsIds, darkIconId.String()) } iconsJson, err := json.Marshal(iconsIds) + if err != nil { + return fmt.Errorf("failed to encode icon ids: %w", err) + } iconsCollection := &domain.IconsCollection{ Id: iconsCollectionId, @@ -429,9 +425,8 @@ func (h *TransformIconRequestToWebServiceHandler) Handle(cmd *TransformIconReque } err = h.IconsCollectionsRepository.Save(iconsCollection) - if err != nil { - return err + return fmt.Errorf("failed to save icons collection: %w", err) } webService := &domain.WebService{ @@ -444,15 +439,13 @@ func (h *TransformIconRequestToWebServiceHandler) Handle(cmd *TransformIconReque } err = h.WebServiceRepository.Save(webService) - if err != nil { - return err + return fmt.Errorf("failed to save web service: %w", err) } err = h.IconsRequestsRepository.Delete(iconRequest) - if err != nil { - return err + return fmt.Errorf("failed to delete icon request: %w", err) } return nil diff --git a/internal/api/icons/app/queries/web_service.go b/internal/api/icons/app/queries/web_service.go index 7b8b37e..6aab3bb 100644 --- a/internal/api/icons/app/queries/web_service.go +++ b/internal/api/icons/app/queries/web_service.go @@ -53,7 +53,7 @@ func (h *WebServiceQueryHandler) FindOne(query *WebServiceQuery) (*WebServicePre if errors.Is(result.Error, gorm.ErrRecordNotFound) { return nil, db.WrapError(err) } - return nil, adapters.WebServiceCouldNotBeFound{WebServiceId: query.Id} + return nil, adapters.WebServiceCouldNotBeFound{Identifier: query.Id} } return presenter, nil diff --git a/internal/api/icons/ports/http.go b/internal/api/icons/ports/http.go index 1faa5e4..61e9f9f 100644 --- a/internal/api/icons/ports/http.go +++ b/internal/api/icons/ports/http.go @@ -91,7 +91,6 @@ func (r *RoutesHandler) UpdateWebService(c *gin.Context) { c.ShouldBindJSON(cmd) err := r.validator.Struct(cmd) - if err != nil { var notFoundErr adapters.WebServiceCouldNotBeFound @@ -537,7 +536,6 @@ func (r *RoutesHandler) UpdateWebServiceFromIconRequest(c *gin.Context) { c.BindJSON(cmd) err := r.validator.Struct(cmd) - if err != nil { validationErrors := err.(validator.ValidationErrors) c.JSON(400, api.NewBadRequestError(validationErrors)) @@ -548,10 +546,15 @@ func (r *RoutesHandler) UpdateWebServiceFromIconRequest(c *gin.Context) { logging.LogCommand(cmd) err = r.cqrs.Commands.UpdateWebServiceFromIconRequest.Handle(cmd) - if err != nil { - c.JSON(400, api.NewBadRequestError(err)) logging.LogCommandFailed(cmd, err) + + if db.IsDBError(err) { + c.JSON(500, api.NewInternalServerError(err)) + return + } + + c.JSON(400, api.NewBadRequestError(err)) return } @@ -576,10 +579,12 @@ func (r *RoutesHandler) TransformToWebService(c *gin.Context) { WebServiceId: webServiceId, } - c.BindUri(cmd) + if err := c.BindUri(cmd); err != nil { + // c.BindUri already returned 400 and error. + return + } err := r.validator.Struct(cmd) - if err != nil { validationErrors := err.(validator.ValidationErrors) c.JSON(400, api.NewBadRequestError(validationErrors)) @@ -589,7 +594,6 @@ func (r *RoutesHandler) TransformToWebService(c *gin.Context) { logging.LogCommand(cmd) err = r.cqrs.Commands.TransformIconRequestToWebService.Handle(cmd) - if err != nil { var conflictErr domain.WebServiceAlreadyExistsError diff --git a/internal/common/aws/s3.go b/internal/common/aws/s3.go index 8035e50..c277487 100644 --- a/internal/common/aws/s3.go +++ b/internal/common/aws/s3.go @@ -1,15 +1,18 @@ package aws import ( + "fmt" + "io" + "os" + "path/filepath" + "github.com/aws/aws-sdk-go/aws" "github.com/aws/aws-sdk-go/aws/credentials" "github.com/aws/aws-sdk-go/aws/session" "github.com/aws/aws-sdk-go/service/s3" "github.com/aws/aws-sdk-go/service/s3/s3manager" + "github.com/twofas/2fas-server/internal/common/logging" - "io" - "os" - "path/filepath" ) type AwsS3 struct { @@ -44,27 +47,16 @@ func (s *AwsS3) Get(path string) (file *os.File, err error) { downloader := s3manager.NewDownloader(sess) f, err := os.Create(name) - if err != nil { - logging.WithFields(logging.Fields{ - "error": err.Error(), - "file": f.Name(), - }).Error("Cannot create file") + return nil, fmt.Errorf("failed to create file: %w", err) } _, err = downloader.Download(f, &s3.GetObjectInput{ Bucket: aws.String(directory), Key: aws.String(name), }) - if err != nil { - logging.WithFields(logging.Fields{ - "error": err.Error(), - "bucket": directory, - "filename": name, - }).Error("Cannot download file") - - return nil, err + return nil, fmt.Errorf("failed to download the object from s3: %w", err) } return f, nil