diff --git a/integrations/api_user_email_test.go b/integrations/api_user_email_test.go new file mode 100644 index 0000000000000..8d0a0cdf1b274 --- /dev/null +++ b/integrations/api_user_email_test.go @@ -0,0 +1,105 @@ +// Copyright 2021 The Gitea Authors. All rights reserved. +// Use of this source code is governed by a MIT-style +// license that can be found in the LICENSE file. + +package integrations + +import ( + "net/http" + "testing" + + api "code.gitea.io/gitea/modules/structs" + + "github.com/stretchr/testify/assert" +) + +func TestAPIListEmails(t *testing.T) { + defer prepareTestEnv(t)() + + normalUsername := "user2" + session := loginUser(t, normalUsername) + token := getTokenForLoggedInUser(t, session) + + req := NewRequest(t, "GET", "/api/v1/user/emails?token="+token) + resp := session.MakeRequest(t, req, http.StatusOK) + + var emails []*api.Email + DecodeJSON(t, resp, &emails) + + assert.EqualValues(t, []*api.Email{ + { + Email: "user2@example.com", + Verified: true, + Primary: true, + }, + { + Email: "user21@example.com", + Verified: false, + Primary: false, + }, + }, emails) +} + +func TestAPIAddEmail(t *testing.T) { + defer prepareTestEnv(t)() + + normalUsername := "user2" + session := loginUser(t, normalUsername) + token := getTokenForLoggedInUser(t, session) + + opts := api.CreateEmailOption{ + Emails: []string{"user101@example.com"}, + } + + req := NewRequestWithJSON(t, "POST", "/api/v1/user/emails?token="+token, &opts) + session.MakeRequest(t, req, http.StatusUnprocessableEntity) + + opts = api.CreateEmailOption{ + Emails: []string{"user22@example.com"}, + } + req = NewRequestWithJSON(t, "POST", "/api/v1/user/emails?token="+token, &opts) + resp := session.MakeRequest(t, req, http.StatusCreated) + + var emails []*api.Email + DecodeJSON(t, resp, &emails) + assert.EqualValues(t, []*api.Email{ + { + Email: "user22@example.com", + Verified: true, + Primary: false, + }, + }, emails) +} + +func TestAPIDeleteEmail(t *testing.T) { + defer prepareTestEnv(t)() + + normalUsername := "user2" + session := loginUser(t, normalUsername) + token := getTokenForLoggedInUser(t, session) + + opts := api.DeleteEmailOption{ + Emails: []string{"user22@example.com"}, + } + req := NewRequestWithJSON(t, "DELETE", "/api/v1/user/emails?token="+token, &opts) + session.MakeRequest(t, req, http.StatusNotFound) + + opts = api.DeleteEmailOption{ + Emails: []string{"user21@example.com"}, + } + req = NewRequestWithJSON(t, "DELETE", "/api/v1/user/emails?token="+token, &opts) + session.MakeRequest(t, req, http.StatusNoContent) + + req = NewRequest(t, "GET", "/api/v1/user/emails?token="+token) + resp := session.MakeRequest(t, req, http.StatusOK) + + var emails []*api.Email + DecodeJSON(t, resp, &emails) + assert.EqualValues(t, []*api.Email{ + { + Email: "user2@example.com", + Verified: true, + Primary: true, + }, + }, emails) +} diff --git a/models/error.go b/models/error.go index 6e110f94d748f..48cba57a8135c 100644 --- a/models/error.go +++ b/models/error.go @@ -222,6 +222,21 @@ func (err ErrEmailInvalid) Error() string { return fmt.Sprintf("e-mail invalid [email: %s]", err.Email) } +// ErrEmailAddressNotExist email address not exist +type ErrEmailAddressNotExist struct { + Email string +} + +// IsErrEmailAddressNotExist checks if an error is an ErrEmailAddressNotExist +func IsErrEmailAddressNotExist(err error) bool { + _, ok := err.(ErrEmailAddressNotExist) + return ok +} + +func (err ErrEmailAddressNotExist) Error() string { + return fmt.Sprintf("Email address does not exist [email: %s]", err.Email) +} + // ErrOpenIDAlreadyUsed represents a "OpenIDAlreadyUsed" kind of error. type ErrOpenIDAlreadyUsed struct { OpenID string diff --git a/models/user_mail.go b/models/user_mail.go index f3e4fe984f426..1bdd6a423cc35 100644 --- a/models/user_mail.go +++ b/models/user_mail.go @@ -6,7 +6,6 @@ package models import ( - "errors" "fmt" "net/mail" "strings" @@ -18,9 +17,6 @@ import ( "xorm.io/builder" ) -// ErrEmailAddressNotExist email address not exist -var ErrEmailAddressNotExist = errors.New("Email address does not exist") - // EmailAddress is the list of all email addresses of a user. Can contain the // primary email address, but is not obligatory. type EmailAddress struct { @@ -243,7 +239,7 @@ func DeleteEmailAddress(email *EmailAddress) (err error) { if err != nil { return err } else if deleted != 1 { - return ErrEmailAddressNotExist + return ErrEmailAddressNotExist{Email: email.Email} } return nil } diff --git a/routers/api/v1/user/email.go b/routers/api/v1/user/email.go index bc8b2fa87bec2..9dd35f91b62b1 100644 --- a/routers/api/v1/user/email.go +++ b/routers/api/v1/user/email.go @@ -111,6 +111,8 @@ func DeleteEmail(ctx *context.APIContext) { // responses: // "204": // "$ref": "#/responses/empty" + // "404": + // "$ref": "#/responses/notFound" form := web.GetForm(ctx).(*api.DeleteEmailOption) if len(form.Emails) == 0 { ctx.Status(http.StatusNoContent) @@ -126,6 +128,10 @@ func DeleteEmail(ctx *context.APIContext) { } if err := models.DeleteEmailAddresses(emails); err != nil { + if models.IsErrEmailAddressNotExist(err) { + ctx.Error(http.StatusNotFound, "DeleteEmailAddresses", err) + return + } ctx.Error(http.StatusInternalServerError, "DeleteEmailAddresses", err) return } diff --git a/templates/swagger/v1_json.tmpl b/templates/swagger/v1_json.tmpl index 99c3373cef3fa..4be2718ef16cd 100644 --- a/templates/swagger/v1_json.tmpl +++ b/templates/swagger/v1_json.tmpl @@ -10221,6 +10221,9 @@ "responses": { "204": { "$ref": "#/responses/empty" + }, + "404": { + "$ref": "#/responses/notFound" } } }