diff --git a/api/emails/create/main.go b/api/emails/create/main.go index 54145f38..550f874d 100644 --- a/api/emails/create/main.go +++ b/api/emails/create/main.go @@ -83,6 +83,11 @@ func handler(ctx context.Context, req events.APIGatewayV2HTTPRequest) (apiutil.R if err == email.ErrInvalidInput { return apiutil.NewErrorResponse(http.StatusBadRequest, "invalid input"), nil } + if err == email.ErrTooManyRequests { + fmt.Println("too many requests") + return apiutil.NewErrorResponse(http.StatusTooManyRequests, "too many requests"), nil + } + fmt.Printf("email create failed: %v\n", err) return apiutil.NewErrorResponse(http.StatusInternalServerError, "internal error"), nil } diff --git a/api/emails/delete/delete.go b/api/emails/delete/delete.go index bf679d51..275059bd 100644 --- a/api/emails/delete/delete.go +++ b/api/emails/delete/delete.go @@ -58,6 +58,10 @@ func handler(ctx context.Context, req events.APIGatewayV2HTTPRequest) (apiutil.R fmt.Printf("dynamodb delete failed: %v\n", err) return apiutil.NewErrorResponse(http.StatusBadRequest, "email not trashed"), nil } + if err == email.ErrTooManyRequests { + fmt.Println("too many requests") + return apiutil.NewErrorResponse(http.StatusTooManyRequests, "too many requests"), nil + } fmt.Printf("dynamodb delete failed: %v\n", err) return apiutil.NewErrorResponse(http.StatusInternalServerError, "internal error"), nil diff --git a/api/emails/get/main.go b/api/emails/get/main.go index b1a58901..16d6c964 100644 --- a/api/emails/get/main.go +++ b/api/emails/get/main.go @@ -44,6 +44,10 @@ func handler(ctx context.Context, req events.APIGatewayV2HTTPRequest) (apiutil.R fmt.Println("email not found") return apiutil.NewErrorResponse(http.StatusNotFound, "email not found"), nil } + if err == email.ErrTooManyRequests { + fmt.Println("too many requests") + return apiutil.NewErrorResponse(http.StatusTooManyRequests, "too many requests"), nil + } fmt.Printf("dynamodb get failed: %v\n", err) return apiutil.NewErrorResponse(http.StatusInternalServerError, "internal error"), nil } diff --git a/api/emails/list/main.go b/api/emails/list/main.go index 29c67f6e..e6ba6af2 100644 --- a/api/emails/list/main.go +++ b/api/emails/list/main.go @@ -57,6 +57,10 @@ func handler(ctx context.Context, req events.APIGatewayV2HTTPRequest) (apiutil.R if err == email.ErrInvalidInput { return apiutil.NewErrorResponse(http.StatusBadRequest, "invalid input"), nil } + if err == email.ErrTooManyRequests { + fmt.Println("too many requests") + return apiutil.NewErrorResponse(http.StatusTooManyRequests, "too many requests"), nil + } fmt.Printf("email list failed: %v\n", err) return apiutil.NewErrorResponse(http.StatusInternalServerError, "internal error"), nil } diff --git a/api/emails/save/main.go b/api/emails/save/main.go index 0228b71f..a19a06a6 100644 --- a/api/emails/save/main.go +++ b/api/emails/save/main.go @@ -87,6 +87,11 @@ func handler(ctx context.Context, req events.APIGatewayV2HTTPRequest) (apiutil.R if err == email.ErrInvalidInput { return apiutil.NewErrorResponse(http.StatusBadRequest, "invalid input"), nil } + if err == email.ErrTooManyRequests { + fmt.Println("too many requests") + return apiutil.NewErrorResponse(http.StatusTooManyRequests, "too many requests"), nil + } + fmt.Printf("email save failed: %v\n", err) return apiutil.NewErrorResponse(http.StatusInternalServerError, "internal error"), nil } diff --git a/api/emails/send/main.go b/api/emails/send/main.go index 7043fbb7..1bd811b4 100644 --- a/api/emails/send/main.go +++ b/api/emails/send/main.go @@ -58,6 +58,11 @@ func handler(ctx context.Context, req events.APIGatewayV2HTTPRequest) (apiutil.R client := sendClient{cfg: cfg} result, err := email.Send(ctx, client, messageID) if err != nil { + if err == email.ErrTooManyRequests { + fmt.Println("too many requests") + return apiutil.NewErrorResponse(http.StatusTooManyRequests, "too many requests"), nil + } + fmt.Printf("email send failed: %v\n", err) return apiutil.NewErrorResponse(http.StatusInternalServerError, "internal error"), nil } diff --git a/api/emails/trash/trash.go b/api/emails/trash/trash.go index a52b8aa2..9cb33d55 100644 --- a/api/emails/trash/trash.go +++ b/api/emails/trash/trash.go @@ -43,6 +43,10 @@ func handler(ctx context.Context, req events.APIGatewayV2HTTPRequest) (apiutil.R fmt.Printf("dynamodb trash failed: %v\n", err) return apiutil.NewErrorResponse(http.StatusBadRequest, "email is already trashed"), nil } + if err == email.ErrTooManyRequests { + fmt.Println("too many requests") + return apiutil.NewErrorResponse(http.StatusTooManyRequests, "too many requests"), nil + } fmt.Printf("dynamodb trash failed: %v\n", err) return apiutil.NewErrorResponse(http.StatusInternalServerError, "internal error"), nil diff --git a/api/emails/untrash/untrash.go b/api/emails/untrash/untrash.go index 56a2ea32..82b330e2 100644 --- a/api/emails/untrash/untrash.go +++ b/api/emails/untrash/untrash.go @@ -43,6 +43,10 @@ func handler(ctx context.Context, req events.APIGatewayV2HTTPRequest) (apiutil.R fmt.Printf("dynamodb untrash failed: %v\n", err) return apiutil.NewErrorResponse(http.StatusBadRequest, "email already not trashed"), nil } + if err == email.ErrTooManyRequests { + fmt.Println("too many requests") + return apiutil.NewErrorResponse(http.StatusTooManyRequests, "too many requests"), nil + } fmt.Printf("dynamodb untrash failed: %v\n", err) return apiutil.NewErrorResponse(http.StatusInternalServerError, "internal error"), nil diff --git a/internal/email/create.go b/internal/email/create.go index ab7df2d3..526d785c 100644 --- a/internal/email/create.go +++ b/internal/email/create.go @@ -2,12 +2,14 @@ package email import ( "context" + "errors" "fmt" "strings" "time" "github.com/aws/aws-sdk-go-v2/aws" "github.com/aws/aws-sdk-go-v2/service/dynamodb" + "github.com/aws/aws-sdk-go-v2/service/dynamodb/types" "github.com/google/uuid" "github.com/harryzcy/mailbox/internal/util/htmlutil" ) @@ -64,6 +66,9 @@ func Create(ctx context.Context, api SaveAndSendEmailAPI, input CreateInput) (*C Item: item, }) if err != nil { + if apiErr := new(types.ProvisionedThroughputExceededException); errors.As(err, &apiErr) { + return nil, ErrTooManyRequests + } return nil, err } diff --git a/internal/email/delete.go b/internal/email/delete.go index c7b95a71..605ed23e 100644 --- a/internal/email/delete.go +++ b/internal/email/delete.go @@ -35,6 +35,9 @@ func Delete(ctx context.Context, api DeleteItemAPI, messageID string) error { err = storage.S3.DeleteEmail(ctx, api, messageID) if err != nil { + if apiErr := new(types.ProvisionedThroughputExceededException); errors.As(err, &apiErr) { + return ErrTooManyRequests + } return err } diff --git a/internal/email/err.go b/internal/email/err.go index 3c717586..64cc7b83 100644 --- a/internal/email/err.go +++ b/internal/email/err.go @@ -4,6 +4,8 @@ import "errors" // Errors var ( + ErrTooManyRequests = errors.New("too many requests") + ErrNotFound = errors.New("email not found") ErrInvalidInput = errors.New("invalid input") ErrQueryNotMatch = errors.New("query does not match with next cursor") diff --git a/internal/email/get.go b/internal/email/get.go index 6c0fe857..d53633e2 100644 --- a/internal/email/get.go +++ b/internal/email/get.go @@ -2,6 +2,7 @@ package email import ( "context" + "errors" "fmt" "github.com/aws/aws-sdk-go-v2/aws" @@ -43,6 +44,9 @@ func Get(ctx context.Context, api GetItemAPI, messageID string) (*GetResult, err }, }) if err != nil { + if apiErr := new(types.ProvisionedThroughputExceededException); errors.As(err, &apiErr) { + return nil, ErrTooManyRequests + } return nil, err } if len(resp.Item) == 0 { diff --git a/internal/email/list_query.go b/internal/email/list_query.go index eebf7270..76dc19f1 100644 --- a/internal/email/list_query.go +++ b/internal/email/list_query.go @@ -2,6 +2,7 @@ package email import ( "context" + "errors" "fmt" "github.com/aws/aws-sdk-go-v2/aws" @@ -50,6 +51,10 @@ func listByYearMonth(ctx context.Context, api QueryAPI, input listQueryInput) (l ScanIndexForward: aws.Bool(false), // reverse order }) if err != nil { + if apiErr := new(types.ProvisionedThroughputExceededException); errors.As(err, &apiErr) { + return listQueryResult{}, ErrTooManyRequests + } + return listQueryResult{}, err } diff --git a/internal/email/save.go b/internal/email/save.go index 586c22d8..080cf18e 100644 --- a/internal/email/save.go +++ b/internal/email/save.go @@ -65,10 +65,13 @@ func Save(ctx context.Context, api SaveAndSendEmailAPI, input SaveInput) (*SaveR }, }) if err != nil { - var condFailedErr *types.ConditionalCheckFailedException - if errors.As(err, &condFailedErr) { + if apiErr := new(types.ConditionalCheckFailedException); errors.As(err, &apiErr) { return nil, ErrNotFound } + if apiErr := new(types.ProvisionedThroughputExceededException); errors.As(err, &apiErr) { + return nil, ErrTooManyRequests + } + return nil, err } diff --git a/internal/email/send.go b/internal/email/send.go index 113be16f..95d41189 100644 --- a/internal/email/send.go +++ b/internal/email/send.go @@ -2,6 +2,7 @@ package email import ( "context" + "errors" "fmt" "strings" @@ -119,6 +120,9 @@ func markEmailAsSent(ctx context.Context, api SendEmailAPI, oldMessageID string, }) if err != nil { + if apiErr := new(dynamodbtypes.ProvisionedThroughputExceededException); errors.As(err, &apiErr) { + return ErrTooManyRequests + } return err } return nil diff --git a/internal/email/trash.go b/internal/email/trash.go index 60169fcf..764ced9a 100644 --- a/internal/email/trash.go +++ b/internal/email/trash.go @@ -26,10 +26,13 @@ func Trash(ctx context.Context, api UpdateItemAPI, messageID string) error { }, }) if err != nil { - var condFailedErr *types.ConditionalCheckFailedException - if errors.As(err, &condFailedErr) { + if apiErr := new(types.ConditionalCheckFailedException); errors.As(err, &apiErr) { return ErrAlreadyTrashed } + if apiErr := new(types.ProvisionedThroughputExceededException); errors.As(err, &apiErr) { + return ErrTooManyRequests + } + return err } diff --git a/internal/email/untrash.go b/internal/email/untrash.go index 1bbd383c..604135aa 100644 --- a/internal/email/untrash.go +++ b/internal/email/untrash.go @@ -24,10 +24,14 @@ func Untrash(ctx context.Context, api UpdateItemAPI, messageID string) error { }, }) if err != nil { - var condFailedErr *types.ConditionalCheckFailedException - if errors.As(err, &condFailedErr) { + if apiErr := new(types.ConditionalCheckFailedException); errors.As(err, &apiErr) { return ErrNotTrashed } + + if apiErr := new(types.ProvisionedThroughputExceededException); errors.As(err, &apiErr) { + return ErrTooManyRequests + } + return err }