From 0b829d61946c11f0b292176be51f301971adc5c7 Mon Sep 17 00:00:00 2001 From: Mayur Deshmukh Date: Tue, 8 Sep 2020 17:04:32 +0530 Subject: [PATCH 01/14] Added user registration API --- db/db.go | 2 + db/user.go | 54 ++++++++++++- go.mod | 1 + go.sum | 1 + migrations/1587381324_create_users.down.sql | 1 - migrations/1587381324_create_users.up.sql | 4 - service/router.go | 1 + service/user_http.go | 85 +++++++++++++++++++++ 8 files changed, 142 insertions(+), 7 deletions(-) delete mode 100644 migrations/1587381324_create_users.down.sql delete mode 100644 migrations/1587381324_create_users.up.sql diff --git a/db/db.go b/db/db.go index 2d1556a..dd77210 100644 --- a/db/db.go +++ b/db/db.go @@ -6,6 +6,8 @@ import ( type Storer interface { ListUsers(context.Context) ([]User, error) + CreateNewUser(context.Context, User) error + CheckUserByEmail(context.Context, string) (bool, error) //Create(context.Context, User) error //GetUser(context.Context) (User, error) //Delete(context.Context, string) error diff --git a/db/user.go b/db/user.go index 17715e9..2f3a4d1 100644 --- a/db/user.go +++ b/db/user.go @@ -2,13 +2,30 @@ package db import ( "context" + "database/sql" logger "github.com/sirupsen/logrus" ) +const ( + insertUserQuery = `INSERT INTO users (first_name, last_name, email, password, mobile_number, country, state, city, address) + VALUES (:first_name, :last_name, :email, :password, :mobile_number, :country, :state, :city, :address)` + + getUserByEmailQuery = `SELECT * FROM users WHERE email=$1 LIMIT 1` +) + +// User - struct representing a user type User struct { - Name string `db:"name" json:"full_name"` - Age int `db:"age" json:"age"` + UserID int `db:"userid" json:"user_id"` + FirstName string `db:"first_name" json:"first_name"` + LastName string `db:"last_name" json:"last_name"` + Email string `db:"email" json:"email"` + Password string `db:"password" json:"password"` + MobileNumber string `db:"mobile_number" json:"mobile_number"` + Country string `db:"country" json:"country"` + State string `db:"state" json:"state"` + City string `db:"city" json:"city"` + Address string `db:"address" json:"address"` } func (s *pgStore) ListUsers(ctx context.Context) (users []User, err error) { @@ -20,3 +37,36 @@ func (s *pgStore) ListUsers(ctx context.Context) (users []User, err error) { return } + +// CreateNewUser = creates a new user in database +func (s *pgStore) CreateNewUser(ctx context.Context, u User) (err error) { + tx, err := s.db.Beginx() + if err != nil { + logger.WithField("err", err.Error()).Error("Error in beginning user insert transaction") + return + } + _, err = tx.NamedExec(insertUserQuery, u) + if err != nil { + logger.WithField("err", err.Error()).Error("Error while inserting user into database") + return + } + err = tx.Commit() + if err != nil { + logger.WithField("err", err.Error()).Error("Error while commiting transaction inserting user") + return + } + return +} + +func (s *pgStore) CheckUserByEmail(ctx context.Context, email string) (check bool, err error) { + user := User{} + err = s.db.Get(&user, getUserByEmailQuery, email) + if err != nil { + if err == sql.ErrNoRows { + return false, err + } + logger.WithField("err", err.Error()).Error("Error while selecting user from database by email" + email) + return + } + return true, err +} diff --git a/go.mod b/go.mod index bd1ae4f..d0a5248 100644 --- a/go.mod +++ b/go.mod @@ -12,4 +12,5 @@ require ( github.com/stretchr/testify v1.6.1 github.com/urfave/cli v1.22.4 github.com/urfave/negroni v1.0.0 + golang.org/x/crypto v0.0.0-20190605123033-f99c8df09eb5 ) diff --git a/go.sum b/go.sum index 0b155a0..81c9326 100644 --- a/go.sum +++ b/go.sum @@ -203,6 +203,7 @@ golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnf golang.org/x/crypto v0.0.0-20181029021203-45a5f77698d3/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= golang.org/x/crypto v0.0.0-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= +golang.org/x/crypto v0.0.0-20190605123033-f99c8df09eb5 h1:58fnuSXlxZmFdJyvtTFVmVhcMLU6v5fEb/ok4wyqtNU= golang.org/x/crypto v0.0.0-20190605123033-f99c8df09eb5/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= diff --git a/migrations/1587381324_create_users.down.sql b/migrations/1587381324_create_users.down.sql deleted file mode 100644 index cc1f647..0000000 --- a/migrations/1587381324_create_users.down.sql +++ /dev/null @@ -1 +0,0 @@ -DROP TABLE users; diff --git a/migrations/1587381324_create_users.up.sql b/migrations/1587381324_create_users.up.sql deleted file mode 100644 index f893282..0000000 --- a/migrations/1587381324_create_users.up.sql +++ /dev/null @@ -1,4 +0,0 @@ -CREATE TABLE users ( - name text, - age integer -); diff --git a/service/router.go b/service/router.go index 121ad64..15b257c 100644 --- a/service/router.go +++ b/service/router.go @@ -24,5 +24,6 @@ func InitRouter(deps Dependencies) (router *mux.Router) { v1 := fmt.Sprintf("application/vnd.%s.v1", config.AppName()) router.HandleFunc("/users", listUsersHandler(deps)).Methods(http.MethodGet).Headers(versionHeader, v1) + router.HandleFunc("/register", registerUserHandler(deps)).Methods(http.MethodPost).Headers(versionHeader, v1) return } diff --git a/service/user_http.go b/service/user_http.go index c544bcd..fa84bf0 100644 --- a/service/user_http.go +++ b/service/user_http.go @@ -1,12 +1,23 @@ package service import ( + "database/sql" "encoding/json" + "io/ioutil" + "joshsoftware/go-e-commerce/db" "net/http" logger "github.com/sirupsen/logrus" + "golang.org/x/crypto/bcrypt" ) +type errorResponse struct { + Error string `json:"error"` +} +type successResponse struct { + Error string `json:"error"` +} + // @Title listUsers // @Description list all User // @Router /users [get] @@ -33,3 +44,77 @@ func listUsersHandler(deps Dependencies) http.HandlerFunc { rw.Write(respBytes) }) } + +// @Title registerUser +// @Description registers new user +// @Router /register [post] +// @Accept json +// @Success 200 {object} +// @Failure 400 {object} +func registerUserHandler(deps Dependencies) http.HandlerFunc { + return http.HandlerFunc(func(rw http.ResponseWriter, req *http.Request) { + + // reading data from body + reqBody, err := ioutil.ReadAll(req.Body) + if err != nil { + logger.WithField("err", err.Error()).Error("Error in reading request body") + rw.WriteHeader(http.StatusInternalServerError) + return + } + user := db.User{} + err = json.Unmarshal(reqBody, &user) + if err != nil { + logger.WithField("err", err.Error()).Error("Error while Unmarshalling request json") + rw.WriteHeader(http.StatusInternalServerError) + return + } + + // For checking if user already registered + check, err := deps.Store.CheckUserByEmail(req.Context(), user.Email) + + // If check true then user is already registered + if check { + e := errorResponse{ + Error: "user already registered", + } + respBytes, err := json.Marshal(e) + if err != nil { + logger.WithField("err", err.Error()).Error("Error while marshalling error ") + rw.WriteHeader(http.StatusInternalServerError) + return + } + rw.Header().Add("Content-Type", "application/json") + rw.WriteHeader(http.StatusBadRequest) + rw.Write(respBytes) + return + } + + // For checking error occured while looking already registered user + if err != nil && err != sql.ErrNoRows { + logger.WithField("err", err.Error()).Error("Error while looking existing user") + rw.WriteHeader(http.StatusInternalServerError) + return + } + + // creating hash of the password + hashedPassword, err := bcrypt.GenerateFromPassword([]byte(user.Password), 8) + if err != nil { + logger.WithField("err", err.Error()).Error("Error while creating hash of the password") + rw.WriteHeader(http.StatusInternalServerError) + return + } + user.Password = string(hashedPassword) + + // Storing new user's data in database + err = deps.Store.CreateNewUser(req.Context(), user) + if err != nil { + logger.WithField("err", err.Error()).Error("Error in inserting user in database") + rw.WriteHeader(http.StatusInternalServerError) + return + } + + rw.WriteHeader(http.StatusOK) + return + + }) +} From 9c090387f3ae906b0a5790ca3186aa243486f872 Mon Sep 17 00:00:00 2001 From: Mayur Deshmukh Date: Tue, 8 Sep 2020 17:07:05 +0530 Subject: [PATCH 02/14] Added migration files for registration api --- migrations/1599540715_create_users.down.sql | 1 + migrations/1599540715_create_users.up.sql | 12 ++++++++++++ 2 files changed, 13 insertions(+) create mode 100644 migrations/1599540715_create_users.down.sql create mode 100644 migrations/1599540715_create_users.up.sql diff --git a/migrations/1599540715_create_users.down.sql b/migrations/1599540715_create_users.down.sql new file mode 100644 index 0000000..441087a --- /dev/null +++ b/migrations/1599540715_create_users.down.sql @@ -0,0 +1 @@ +DROP TABLE users; \ No newline at end of file diff --git a/migrations/1599540715_create_users.up.sql b/migrations/1599540715_create_users.up.sql new file mode 100644 index 0000000..45e601e --- /dev/null +++ b/migrations/1599540715_create_users.up.sql @@ -0,0 +1,12 @@ +CREATE TABLE IF NOT EXISTS users ( + userid SERIAL PRIMARY KEY, + first_name VARCHAR(255) NOT NULL, + last_name VARCHAR(255), + email VARCHAR(255) NOT NULL, + password VARCHAR(255) NOT NULL, + mobile_number VARCHAR(50) NOT NULL, + country VARCHAR(100), + state VARCHAR(100), + city VARCHAR(100), + address VARCHAR(255) +); \ No newline at end of file From ed16e1dd5101cccf779dfa2fffab3d3a3e990623 Mon Sep 17 00:00:00 2001 From: Mayur Deshmukh Date: Wed, 9 Sep 2020 11:04:52 +0530 Subject: [PATCH 03/14] Added test cases for user registration API --- db/mock.go | 12 +++++ go.mod | 1 + go.sum | 5 +++ service/user_http_test.go | 95 +++++++++++++++++++++++---------------- 4 files changed, 74 insertions(+), 39 deletions(-) diff --git a/db/mock.go b/db/mock.go index a8047e7..b199cef 100644 --- a/db/mock.go +++ b/db/mock.go @@ -14,3 +14,15 @@ func (m *DBMockStore) ListUsers(ctx context.Context) (users []User, err error) { args := m.Called(ctx) return args.Get(0).([]User), args.Error(1) } + +// CreateNewUser - test mock +func (m *DBMockStore) CreateNewUser(ctx context.Context, u User) (err error) { + args := m.Called(ctx, u) + return args.Error(0) +} + +// CheckUserByEmail - test mock +func (m *DBMockStore) CheckUserByEmail(ctx context.Context, email string) (check bool, err error) { + args := m.Called(ctx, email) + return args.Get(0).(bool), args.Get(1).(error) +} diff --git a/go.mod b/go.mod index d0a5248..62bf44d 100644 --- a/go.mod +++ b/go.mod @@ -3,6 +3,7 @@ module joshsoftware/go-e-commerce go 1.14 require ( + github.com/bxcodec/faker/v3 v3.5.0 github.com/gorilla/mux v1.8.0 github.com/jmoiron/sqlx v1.2.0 github.com/lib/pq v1.8.0 diff --git a/go.sum b/go.sum index 81c9326..237d0c1 100644 --- a/go.sum +++ b/go.sum @@ -23,6 +23,10 @@ github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24 github.com/beorn7/perks v1.0.0/go.mod h1:KWe93zE9D1o94FZ5RNwFwVgaQK1VOXiVxmqh+CedLV8= github.com/bgentry/speakeasy v0.1.0/go.mod h1:+zsyZBPWlz7T6j88CTgSN5bM796AkVf0kBD4zp0CCIs= github.com/bketelsen/crypt v0.0.3-0.20200106085610-5cbc8cc4026c/go.mod h1:MKsuJmJgSg28kpZDP6UIiPt0e0Oz0kqKNGyRaWEPv84= +github.com/bxcodec/faker v1.5.0 h1:RIWOeAcM3ZHye1i8bQtHU2LfNOaLmHuRiCo60mNMOcQ= +github.com/bxcodec/faker v2.0.1+incompatible h1:P0KUpUw5w6WJXwrPfv35oc91i4d8nf40Nwln+M/+faA= +github.com/bxcodec/faker/v3 v3.5.0 h1:Rahy6dwbd6up0wbwbV7dFyQb+jmdC51kpATuUdnzfMg= +github.com/bxcodec/faker/v3 v3.5.0/go.mod h1:gF31YgnMSMKgkvl+fyEo1xuSMbEuieyqfeslGYFjneM= github.com/cespare/xxhash v1.1.0/go.mod h1:XrSqR1VqqWfGrhpAt58auRo0WTKS1nRRg3ghfAqPWnc= github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw= github.com/coreos/bbolt v1.3.2/go.mod h1:iRUV2dpdMOn7Bo10OQBFzIJO9kkE559Wcmn+qkEiiKk= @@ -205,6 +209,7 @@ golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACk golang.org/x/crypto v0.0.0-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20190605123033-f99c8df09eb5 h1:58fnuSXlxZmFdJyvtTFVmVhcMLU6v5fEb/ok4wyqtNU= golang.org/x/crypto v0.0.0-20190605123033-f99c8df09eb5/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= +golang.org/x/crypto v0.0.0-20200820211705-5c72a883971a h1:vclmkQCjlDX5OydZ9wv8rBCcS0QyQY66Mpf/7BZbInM= golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/exp v0.0.0-20190510132918-efd6b22b2522/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8= diff --git a/service/user_http_test.go b/service/user_http_test.go index 4e2b367..74ac383 100644 --- a/service/user_http_test.go +++ b/service/user_http_test.go @@ -1,16 +1,17 @@ package service import ( - "errors" + "database/sql" "joshsoftware/go-e-commerce/db" "net/http" "net/http/httptest" "strings" "testing" + "github.com/stretchr/testify/mock" + "github.com/gorilla/mux" "github.com/stretchr/testify/assert" - "github.com/stretchr/testify/mock" "github.com/stretchr/testify/suite" ) @@ -30,43 +31,6 @@ func TestExampleTestSuite(t *testing.T) { suite.Run(t, new(UsersHandlerTestSuite)) } -func (suite *UsersHandlerTestSuite) TestListUsersSuccess() { - suite.dbMock.On("ListUsers", mock.Anything).Return( - []db.User{ - db.User{Name: "test-user", Age: 18}, - }, - nil, - ) - - recorder := makeHTTPCall( - http.MethodGet, - "/users", - "", - listUsersHandler(Dependencies{Store: suite.dbMock}), - ) - - assert.Equal(suite.T(), http.StatusOK, recorder.Code) - assert.Equal(suite.T(), `[{"full_name":"test-user","age":18}]`, recorder.Body.String()) - suite.dbMock.AssertExpectations(suite.T()) -} - -func (suite *UsersHandlerTestSuite) TestListUsersWhenDBFailure() { - suite.dbMock.On("ListUsers", mock.Anything).Return( - []db.User{}, - errors.New("error fetching user records"), - ) - - recorder := makeHTTPCall( - http.MethodGet, - "/users", - "", - listUsersHandler(Dependencies{Store: suite.dbMock}), - ) - - assert.Equal(suite.T(), http.StatusInternalServerError, recorder.Code) - suite.dbMock.AssertExpectations(suite.T()) -} - func makeHTTPCall(method, path, body string, handlerFunc http.HandlerFunc) (recorder *httptest.ResponseRecorder) { // create a http request using the given parameters req, _ := http.NewRequest(method, path, strings.NewReader(body)) @@ -82,3 +46,56 @@ func makeHTTPCall(method, path, body string, handlerFunc http.HandlerFunc) (reco router.ServeHTTP(recorder, req) return } + +func (suite *UsersHandlerTestSuite) TestRegisterUserSuccess() { + suite.dbMock.On("CreateNewUser", mock.Anything, mock.Anything).Return(nil) + suite.dbMock.On("CheckUserByEmail", mock.Anything, mock.Anything).Return(false, sql.ErrNoRows) + body := + `{ + "first_name" : "test1", + "last_name" : "test2", + "email" : "test@gmail.com", + "password": "password", + "mobile_number": "8421987845", + "country": "India", + "state": "Maharashtra", + "city": "Nashik", + "address": "abc" + }` + recorder := makeHTTPCall(http.MethodPost, + "/register", + body, + registerUserHandler(Dependencies{Store: suite.dbMock}), + ) + assert.Equal(suite.T(), http.StatusOK, recorder.Code) + suite.dbMock.AssertExpectations(suite.T()) + +} + +func (suite *UsersHandlerTestSuite) TestRegisterUserFailure() { + suite.dbMock.On("CreateNewUser", mock.Anything, mock.Anything).Return(nil) + suite.dbMock.On("CheckUserByEmail", mock.Anything, mock.Anything).Return(true, sql.ErrNoRows) + body := + `{ + "first_name" : "test1", + "last_name" : "test2", + "email" : "test@gmail.com", + "password": "password", + "mobile_number": "8421987845", + "country": "India", + "state": "Maharashtra", + "city": "Nashik", + "address": "abc" + }` + recorder := makeHTTPCall(http.MethodPost, + "/register", + body, + registerUserHandler(Dependencies{Store: suite.dbMock}), + ) + + assert.Equal(suite.T(), `{"error":"user already registered"}`, recorder.Body.String()) + assert.Equal(suite.T(), http.StatusBadRequest, recorder.Code) + suite.dbMock.AssertNotCalled(suite.T(), "CreateNewUser", mock.Anything, mock.Anything) + suite.dbMock.AssertCalled(suite.T(), "CheckUserByEmail", mock.Anything, mock.Anything) + +} From 56eb4dc1703bdf999138698cf15573750f828c2f Mon Sep 17 00:00:00 2001 From: Mayur Deshmukh Date: Wed, 9 Sep 2020 18:10:50 +0530 Subject: [PATCH 04/14] Changed users table --- migrations/1599540715_create_users.up.sql | 21 +++++++++++---------- 1 file changed, 11 insertions(+), 10 deletions(-) diff --git a/migrations/1599540715_create_users.up.sql b/migrations/1599540715_create_users.up.sql index 45e601e..65f44a2 100644 --- a/migrations/1599540715_create_users.up.sql +++ b/migrations/1599540715_create_users.up.sql @@ -1,12 +1,13 @@ CREATE TABLE IF NOT EXISTS users ( - userid SERIAL PRIMARY KEY, - first_name VARCHAR(255) NOT NULL, - last_name VARCHAR(255), - email VARCHAR(255) NOT NULL, - password VARCHAR(255) NOT NULL, - mobile_number VARCHAR(50) NOT NULL, - country VARCHAR(100), - state VARCHAR(100), - city VARCHAR(100), - address VARCHAR(255) + id SERIAL NOT NULL PRIMARY KEY, + first_name VARCHAR(255) NOT NULL, + last_name VARCHAR(255) NOT NULL, + email VARCHAR(255) NOT NULL UNIQUE, + mobile VARCHAR(20) NOT NULL UNIQUE, + country VARCHAR(100), + state VARCHAR(100), + city VARCHAR(100), + address TEXT, + password TEXT, + created_at TIMESTAMP DEFAULT (NOW() AT TIME ZONE 'UTC') ); \ No newline at end of file From bac3c11d6b39241c4f03f56531e8c382248bb8c8 Mon Sep 17 00:00:00 2001 From: Mayur Deshmukh Date: Wed, 9 Sep 2020 18:12:51 +0530 Subject: [PATCH 05/14] Changed response status code and checked for whether mobile number is unique or not while registering --- db/db.go | 2 +- db/mock.go | 4 ++-- db/user.go | 31 ++++++++++++++++--------------- service/user_http.go | 23 +++++++++++++++++------ service/user_http_test.go | 36 ++++++++++++++++++------------------ 5 files changed, 54 insertions(+), 42 deletions(-) diff --git a/db/db.go b/db/db.go index dd77210..ad406e7 100644 --- a/db/db.go +++ b/db/db.go @@ -7,7 +7,7 @@ import ( type Storer interface { ListUsers(context.Context) ([]User, error) CreateNewUser(context.Context, User) error - CheckUserByEmail(context.Context, string) (bool, error) + CheckUserByEmail(context.Context, string, string) (bool, error) //Create(context.Context, User) error //GetUser(context.Context) (User, error) //Delete(context.Context, string) error diff --git a/db/mock.go b/db/mock.go index b199cef..7ddae71 100644 --- a/db/mock.go +++ b/db/mock.go @@ -22,7 +22,7 @@ func (m *DBMockStore) CreateNewUser(ctx context.Context, u User) (err error) { } // CheckUserByEmail - test mock -func (m *DBMockStore) CheckUserByEmail(ctx context.Context, email string) (check bool, err error) { - args := m.Called(ctx, email) +func (m *DBMockStore) CheckUserByEmail(ctx context.Context, email string, mobile string) (check bool, err error) { + args := m.Called(ctx, email, mobile) return args.Get(0).(bool), args.Get(1).(error) } diff --git a/db/user.go b/db/user.go index 2f3a4d1..f523b41 100644 --- a/db/user.go +++ b/db/user.go @@ -8,24 +8,25 @@ import ( ) const ( - insertUserQuery = `INSERT INTO users (first_name, last_name, email, password, mobile_number, country, state, city, address) - VALUES (:first_name, :last_name, :email, :password, :mobile_number, :country, :state, :city, :address)` + insertUserQuery = `INSERT INTO users (first_name, last_name, email, mobile, country, state, city, address, password) + VALUES (:first_name, :last_name, :email, :mobile, :country, :state, :city, :address, :password)` - getUserByEmailQuery = `SELECT * FROM users WHERE email=$1 LIMIT 1` + getUserByEmailQuery = `SELECT * FROM users WHERE email=$1 OR mobile=$2 LIMIT 1` ) // User - struct representing a user type User struct { - UserID int `db:"userid" json:"user_id"` - FirstName string `db:"first_name" json:"first_name"` - LastName string `db:"last_name" json:"last_name"` - Email string `db:"email" json:"email"` - Password string `db:"password" json:"password"` - MobileNumber string `db:"mobile_number" json:"mobile_number"` - Country string `db:"country" json:"country"` - State string `db:"state" json:"state"` - City string `db:"city" json:"city"` - Address string `db:"address" json:"address"` + ID int `db:"id" json:"id"` + FirstName string `db:"first_name" json:"first_name"` + LastName string `db:"last_name" json:"last_name"` + Email string `db:"email" json:"email"` + Mobile string `db:"mobile" json:"mobile"` + Country string `db:"country" json:"country"` + State string `db:"state" json:"state"` + City string `db:"city" json:"city"` + Address string `db:"address" json:"address"` + Password string `db:"password" json:"password"` + CreatedAt string `db:"created_at" json:"created_at"` } func (s *pgStore) ListUsers(ctx context.Context) (users []User, err error) { @@ -58,9 +59,9 @@ func (s *pgStore) CreateNewUser(ctx context.Context, u User) (err error) { return } -func (s *pgStore) CheckUserByEmail(ctx context.Context, email string) (check bool, err error) { +func (s *pgStore) CheckUserByEmail(ctx context.Context, email string, mobile string) (check bool, err error) { user := User{} - err = s.db.Get(&user, getUserByEmailQuery, email) + err = s.db.Get(&user, getUserByEmailQuery, email, mobile) if err != nil { if err == sql.ErrNoRows { return false, err diff --git a/service/user_http.go b/service/user_http.go index fa84bf0..bba9a18 100644 --- a/service/user_http.go +++ b/service/user_http.go @@ -15,7 +15,7 @@ type errorResponse struct { Error string `json:"error"` } type successResponse struct { - Error string `json:"error"` + Message string `json:"message"` } // @Title listUsers @@ -49,7 +49,7 @@ func listUsersHandler(deps Dependencies) http.HandlerFunc { // @Description registers new user // @Router /register [post] // @Accept json -// @Success 200 {object} +// @Success 201 {object} // @Failure 400 {object} func registerUserHandler(deps Dependencies) http.HandlerFunc { return http.HandlerFunc(func(rw http.ResponseWriter, req *http.Request) { @@ -70,16 +70,16 @@ func registerUserHandler(deps Dependencies) http.HandlerFunc { } // For checking if user already registered - check, err := deps.Store.CheckUserByEmail(req.Context(), user.Email) + check, err := deps.Store.CheckUserByEmail(req.Context(), user.Email, user.Mobile) // If check true then user is already registered if check { e := errorResponse{ - Error: "user already registered", + Error: "user's email or mobile already registered", } respBytes, err := json.Marshal(e) if err != nil { - logger.WithField("err", err.Error()).Error("Error while marshalling error ") + logger.WithField("err", err.Error()).Error("Error while marshalling error msg ") rw.WriteHeader(http.StatusInternalServerError) return } @@ -113,7 +113,18 @@ func registerUserHandler(deps Dependencies) http.HandlerFunc { return } - rw.WriteHeader(http.StatusOK) + msg := successResponse{ + Message: "user successfully registered", + } + respBytes, err := json.Marshal(msg) + if err != nil { + logger.WithField("err", err.Error()).Error("Error while marshalling success msg ") + rw.WriteHeader(http.StatusInternalServerError) + return + } + rw.Header().Add("Content-Type", "application/json") + rw.WriteHeader(http.StatusCreated) + rw.Write(respBytes) return }) diff --git a/service/user_http_test.go b/service/user_http_test.go index 74ac383..8842173 100644 --- a/service/user_http_test.go +++ b/service/user_http_test.go @@ -49,43 +49,43 @@ func makeHTTPCall(method, path, body string, handlerFunc http.HandlerFunc) (reco func (suite *UsersHandlerTestSuite) TestRegisterUserSuccess() { suite.dbMock.On("CreateNewUser", mock.Anything, mock.Anything).Return(nil) - suite.dbMock.On("CheckUserByEmail", mock.Anything, mock.Anything).Return(false, sql.ErrNoRows) + suite.dbMock.On("CheckUserByEmail", mock.Anything, mock.Anything, mock.Anything).Return(false, sql.ErrNoRows) body := `{ - "first_name" : "test1", - "last_name" : "test2", - "email" : "test@gmail.com", - "password": "password", - "mobile_number": "8421987845", - "country": "India", - "state": "Maharashtra", - "city": "Nashik", - "address": "abc" - }` + "first_name" : "test1", + "last_name" : "test2", + "email" : "test@gmail.com", + "mobile": "8421987856", + "country": "India", + "state": "Maharashtra", + "city": "Nashik", + "address": "abc", + "password": "password" + }` recorder := makeHTTPCall(http.MethodPost, "/register", body, registerUserHandler(Dependencies{Store: suite.dbMock}), ) - assert.Equal(suite.T(), http.StatusOK, recorder.Code) + assert.Equal(suite.T(), http.StatusCreated, recorder.Code) suite.dbMock.AssertExpectations(suite.T()) } func (suite *UsersHandlerTestSuite) TestRegisterUserFailure() { suite.dbMock.On("CreateNewUser", mock.Anything, mock.Anything).Return(nil) - suite.dbMock.On("CheckUserByEmail", mock.Anything, mock.Anything).Return(true, sql.ErrNoRows) + suite.dbMock.On("CheckUserByEmail", mock.Anything, mock.Anything, mock.Anything).Return(true, sql.ErrNoRows) body := `{ "first_name" : "test1", "last_name" : "test2", "email" : "test@gmail.com", - "password": "password", - "mobile_number": "8421987845", + "mobile": "8421987856", "country": "India", "state": "Maharashtra", "city": "Nashik", - "address": "abc" + "address": "abc", + "password": "password" }` recorder := makeHTTPCall(http.MethodPost, "/register", @@ -93,9 +93,9 @@ func (suite *UsersHandlerTestSuite) TestRegisterUserFailure() { registerUserHandler(Dependencies{Store: suite.dbMock}), ) - assert.Equal(suite.T(), `{"error":"user already registered"}`, recorder.Body.String()) + assert.Equal(suite.T(), `{"error":"user's email or mobile already registered"}`, recorder.Body.String()) assert.Equal(suite.T(), http.StatusBadRequest, recorder.Code) suite.dbMock.AssertNotCalled(suite.T(), "CreateNewUser", mock.Anything, mock.Anything) - suite.dbMock.AssertCalled(suite.T(), "CheckUserByEmail", mock.Anything, mock.Anything) + suite.dbMock.AssertCalled(suite.T(), "CheckUserByEmail", mock.Anything, mock.Anything, mock.Anything) } From a5b58ef8dc112d1b2cb8d5c93ba97e41ca5f48d8 Mon Sep 17 00:00:00 2001 From: Mayur Deshmukh Date: Thu, 10 Sep 2020 15:01:30 +0530 Subject: [PATCH 06/14] Changed user registration as per google oauth --- db/db.go | 4 ++-- db/mock.go | 10 +++++----- db/user.go | 18 +++++++++++------- migrations/1599540715_create_users.up.sql | 4 ++-- service/user_http.go | 6 +++--- service/user_http_test.go | 12 ++++++------ 6 files changed, 29 insertions(+), 25 deletions(-) diff --git a/db/db.go b/db/db.go index ad406e7..b9f7c0b 100644 --- a/db/db.go +++ b/db/db.go @@ -6,8 +6,8 @@ import ( type Storer interface { ListUsers(context.Context) ([]User, error) - CreateNewUser(context.Context, User) error - CheckUserByEmail(context.Context, string, string) (bool, error) + CreateNewUser(context.Context, User) (User, error) + CheckUserByEmail(context.Context, string) (bool, User, error) //Create(context.Context, User) error //GetUser(context.Context) (User, error) //Delete(context.Context, string) error diff --git a/db/mock.go b/db/mock.go index 7ddae71..9363ed7 100644 --- a/db/mock.go +++ b/db/mock.go @@ -16,13 +16,13 @@ func (m *DBMockStore) ListUsers(ctx context.Context) (users []User, err error) { } // CreateNewUser - test mock -func (m *DBMockStore) CreateNewUser(ctx context.Context, u User) (err error) { +func (m *DBMockStore) CreateNewUser(ctx context.Context, u User) (user User, err error) { args := m.Called(ctx, u) - return args.Error(0) + return args.Get(0).(User), args.Error(1) } // CheckUserByEmail - test mock -func (m *DBMockStore) CheckUserByEmail(ctx context.Context, email string, mobile string) (check bool, err error) { - args := m.Called(ctx, email, mobile) - return args.Get(0).(bool), args.Get(1).(error) +func (m *DBMockStore) CheckUserByEmail(ctx context.Context, email string) (check bool, user User, err error) { + args := m.Called(ctx, email) + return args.Get(0).(bool), args.Get(1).(User), args.Get(2).(error) } diff --git a/db/user.go b/db/user.go index f523b41..2e8c78c 100644 --- a/db/user.go +++ b/db/user.go @@ -11,7 +11,7 @@ const ( insertUserQuery = `INSERT INTO users (first_name, last_name, email, mobile, country, state, city, address, password) VALUES (:first_name, :last_name, :email, :mobile, :country, :state, :city, :address, :password)` - getUserByEmailQuery = `SELECT * FROM users WHERE email=$1 OR mobile=$2 LIMIT 1` + getUserByEmailQuery = `SELECT * FROM users WHERE email=$1 LIMIT 1` ) // User - struct representing a user @@ -40,7 +40,7 @@ func (s *pgStore) ListUsers(ctx context.Context) (users []User, err error) { } // CreateNewUser = creates a new user in database -func (s *pgStore) CreateNewUser(ctx context.Context, u User) (err error) { +func (s *pgStore) CreateNewUser(ctx context.Context, u User) (newUser User, err error) { tx, err := s.db.Beginx() if err != nil { logger.WithField("err", err.Error()).Error("Error in beginning user insert transaction") @@ -56,18 +56,22 @@ func (s *pgStore) CreateNewUser(ctx context.Context, u User) (err error) { logger.WithField("err", err.Error()).Error("Error while commiting transaction inserting user") return } + _, newUser, err = s.CheckUserByEmail(ctx, u.Email) + if err != nil { + logger.WithField("err", err.Error()).Error("Error selecting user from database with email: " + u.Email) + return + } return } -func (s *pgStore) CheckUserByEmail(ctx context.Context, email string, mobile string) (check bool, err error) { - user := User{} - err = s.db.Get(&user, getUserByEmailQuery, email, mobile) +func (s *pgStore) CheckUserByEmail(ctx context.Context, email string) (check bool, user User, err error) { + err = s.db.Get(&user, getUserByEmailQuery, email) if err != nil { if err == sql.ErrNoRows { - return false, err + return false, user, err } logger.WithField("err", err.Error()).Error("Error while selecting user from database by email" + email) return } - return true, err + return true, user, err } diff --git a/migrations/1599540715_create_users.up.sql b/migrations/1599540715_create_users.up.sql index 65f44a2..75c0b9a 100644 --- a/migrations/1599540715_create_users.up.sql +++ b/migrations/1599540715_create_users.up.sql @@ -1,9 +1,9 @@ CREATE TABLE IF NOT EXISTS users ( id SERIAL NOT NULL PRIMARY KEY, first_name VARCHAR(255) NOT NULL, - last_name VARCHAR(255) NOT NULL, + last_name VARCHAR(255), email VARCHAR(255) NOT NULL UNIQUE, - mobile VARCHAR(20) NOT NULL UNIQUE, + mobile VARCHAR(20), country VARCHAR(100), state VARCHAR(100), city VARCHAR(100), diff --git a/service/user_http.go b/service/user_http.go index bba9a18..c05a7f0 100644 --- a/service/user_http.go +++ b/service/user_http.go @@ -70,12 +70,12 @@ func registerUserHandler(deps Dependencies) http.HandlerFunc { } // For checking if user already registered - check, err := deps.Store.CheckUserByEmail(req.Context(), user.Email, user.Mobile) + check, _, err := deps.Store.CheckUserByEmail(req.Context(), user.Email) // If check true then user is already registered if check { e := errorResponse{ - Error: "user's email or mobile already registered", + Error: "user already registered", } respBytes, err := json.Marshal(e) if err != nil { @@ -106,7 +106,7 @@ func registerUserHandler(deps Dependencies) http.HandlerFunc { user.Password = string(hashedPassword) // Storing new user's data in database - err = deps.Store.CreateNewUser(req.Context(), user) + _, err = deps.Store.CreateNewUser(req.Context(), user) if err != nil { logger.WithField("err", err.Error()).Error("Error in inserting user in database") rw.WriteHeader(http.StatusInternalServerError) diff --git a/service/user_http_test.go b/service/user_http_test.go index 8842173..191361f 100644 --- a/service/user_http_test.go +++ b/service/user_http_test.go @@ -48,8 +48,8 @@ func makeHTTPCall(method, path, body string, handlerFunc http.HandlerFunc) (reco } func (suite *UsersHandlerTestSuite) TestRegisterUserSuccess() { - suite.dbMock.On("CreateNewUser", mock.Anything, mock.Anything).Return(nil) - suite.dbMock.On("CheckUserByEmail", mock.Anything, mock.Anything, mock.Anything).Return(false, sql.ErrNoRows) + suite.dbMock.On("CreateNewUser", mock.Anything, mock.Anything).Return(db.User{}, nil) + suite.dbMock.On("CheckUserByEmail", mock.Anything, mock.Anything).Return(false, db.User{}, sql.ErrNoRows) body := `{ "first_name" : "test1", @@ -73,8 +73,8 @@ func (suite *UsersHandlerTestSuite) TestRegisterUserSuccess() { } func (suite *UsersHandlerTestSuite) TestRegisterUserFailure() { - suite.dbMock.On("CreateNewUser", mock.Anything, mock.Anything).Return(nil) - suite.dbMock.On("CheckUserByEmail", mock.Anything, mock.Anything, mock.Anything).Return(true, sql.ErrNoRows) + suite.dbMock.On("CreateNewUser", mock.Anything, mock.Anything).Return(db.User{}, nil) + suite.dbMock.On("CheckUserByEmail", mock.Anything, mock.Anything).Return(true, db.User{}, sql.ErrNoRows) body := `{ "first_name" : "test1", @@ -93,9 +93,9 @@ func (suite *UsersHandlerTestSuite) TestRegisterUserFailure() { registerUserHandler(Dependencies{Store: suite.dbMock}), ) - assert.Equal(suite.T(), `{"error":"user's email or mobile already registered"}`, recorder.Body.String()) + assert.Equal(suite.T(), `{"error":"user already registered"}`, recorder.Body.String()) assert.Equal(suite.T(), http.StatusBadRequest, recorder.Code) suite.dbMock.AssertNotCalled(suite.T(), "CreateNewUser", mock.Anything, mock.Anything) - suite.dbMock.AssertCalled(suite.T(), "CheckUserByEmail", mock.Anything, mock.Anything, mock.Anything) + suite.dbMock.AssertCalled(suite.T(), "CheckUserByEmail", mock.Anything, mock.Anything) } From 232e8e4f3d430f51c202a186baa5e5cfc5246981 Mon Sep 17 00:00:00 2001 From: Mayur Deshmukh Date: Thu, 10 Sep 2020 21:34:22 +0530 Subject: [PATCH 07/14] Added cors middleware for handling cors --- go.mod | 1 + go.sum | 2 ++ main.go | 9 +++++++++ 3 files changed, 12 insertions(+) diff --git a/go.mod b/go.mod index 62bf44d..bb9b135 100644 --- a/go.mod +++ b/go.mod @@ -8,6 +8,7 @@ require ( github.com/jmoiron/sqlx v1.2.0 github.com/lib/pq v1.8.0 github.com/mattes/migrate v3.0.1+incompatible + github.com/rs/cors v1.7.0 github.com/sirupsen/logrus v1.6.0 github.com/spf13/viper v1.7.1 github.com/stretchr/testify v1.6.1 diff --git a/go.sum b/go.sum index 237d0c1..a3ed01a 100644 --- a/go.sum +++ b/go.sum @@ -158,6 +158,8 @@ github.com/prometheus/procfs v0.0.0-20190507164030-5867b95ac084/go.mod h1:TjEm7z github.com/prometheus/tsdb v0.7.1/go.mod h1:qhTCs0VvXwvX/y3TZrWD7rabWM+ijKTux40TwIPHuXU= github.com/rogpeppe/fastuuid v0.0.0-20150106093220-6724a57986af/go.mod h1:XWv6SoW27p1b0cqNHllgS5HIMJraePCO15w5zCzIWYg= github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4= +github.com/rs/cors v1.7.0 h1:+88SsELBHx5r+hZ8TCkggzSstaWNbDvThkVK8H6f9ik= +github.com/rs/cors v1.7.0/go.mod h1:gFx+x8UowdsKA9AchylcLynDq+nNFfI8FkUZdN/jGCU= github.com/russross/blackfriday/v2 v2.0.1 h1:lPqVAte+HuHNfhJ/0LC98ESWRz8afy9tM/0RK8m9o+Q= github.com/russross/blackfriday/v2 v2.0.1/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= github.com/ryanuber/columnize v0.0.0-20160712163229-9b3edd62028f/go.mod h1:sm1tb6uqfes/u+d4ooFouqFdy9/2g9QGwK3SQygK0Ts= diff --git a/main.go b/main.go index e0ad81d..5d7618d 100644 --- a/main.go +++ b/main.go @@ -11,6 +11,7 @@ import ( "os" "strconv" + "github.com/rs/cors" logger "github.com/sirupsen/logrus" "github.com/urfave/cli" "github.com/urfave/negroni" @@ -70,6 +71,13 @@ func startApp() (err error) { return } + c := cors.New(cors.Options{ + AllowedOrigins: []string{"*"}, + AllowedMethods: []string{"POST", "GET", "DELETE", "PUT", "PATCH", "OPTIONS"}, + AllowCredentials: true, + Debug: true, + }) + deps := service.Dependencies{ Store: store, } @@ -79,6 +87,7 @@ func startApp() (err error) { // init web server server := negroni.Classic() + server.Use(c) server.UseHandler(router) port := config.AppPort() // This can be changed to the service port number via environment variable. From 9e91b8285e37832f07f5d6e06aab4cf8550d10ef Mon Sep 17 00:00:00 2001 From: Mayur Deshmukh Date: Tue, 15 Sep 2020 13:56:41 +0530 Subject: [PATCH 08/14] Changed signature of GetUserByEmail and CreateUser --- db/db.go | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/db/db.go b/db/db.go index b9f7c0b..0cc0f91 100644 --- a/db/db.go +++ b/db/db.go @@ -6,8 +6,9 @@ import ( type Storer interface { ListUsers(context.Context) ([]User, error) - CreateNewUser(context.Context, User) (User, error) - CheckUserByEmail(context.Context, string) (bool, User, error) + CreateUser(context.Context, User) (User, error) + // CheckUserByEmail(context.Context, string) (bool, User, error) + GetUserByEmail(context.Context, string) (User, error) //Create(context.Context, User) error //GetUser(context.Context) (User, error) //Delete(context.Context, string) error From b238b19ff6990651c809dab49fc2bbaf974b5aac Mon Sep 17 00:00:00 2001 From: Mayur Deshmukh Date: Tue, 15 Sep 2020 13:58:14 +0530 Subject: [PATCH 09/14] Removed transaction from CreateUser and changed GetUserByEmail --- db/mock.go | 10 +++++----- db/user.go | 24 ++++++++++-------------- 2 files changed, 15 insertions(+), 19 deletions(-) diff --git a/db/mock.go b/db/mock.go index 9363ed7..0b34d39 100644 --- a/db/mock.go +++ b/db/mock.go @@ -15,14 +15,14 @@ func (m *DBMockStore) ListUsers(ctx context.Context) (users []User, err error) { return args.Get(0).([]User), args.Error(1) } -// CreateNewUser - test mock -func (m *DBMockStore) CreateNewUser(ctx context.Context, u User) (user User, err error) { +// CreateUser - test mock +func (m *DBMockStore) CreateUser(ctx context.Context, u User) (user User, err error) { args := m.Called(ctx, u) return args.Get(0).(User), args.Error(1) } -// CheckUserByEmail - test mock -func (m *DBMockStore) CheckUserByEmail(ctx context.Context, email string) (check bool, user User, err error) { +// GetUserByEmail - test mock +func (m *DBMockStore) GetUserByEmail(ctx context.Context, email string) (user User, err error) { args := m.Called(ctx, email) - return args.Get(0).(bool), args.Get(1).(User), args.Get(2).(error) + return args.Get(0).(User), args.Error(1) } diff --git a/db/user.go b/db/user.go index 2e8c78c..be2d92e 100644 --- a/db/user.go +++ b/db/user.go @@ -40,23 +40,18 @@ func (s *pgStore) ListUsers(ctx context.Context) (users []User, err error) { } // CreateNewUser = creates a new user in database -func (s *pgStore) CreateNewUser(ctx context.Context, u User) (newUser User, err error) { - tx, err := s.db.Beginx() +func (s *pgStore) CreateUser(ctx context.Context, u User) (newUser User, err error) { + stmt, err := s.db.PrepareNamed(insertUserQuery) if err != nil { - logger.WithField("err", err.Error()).Error("Error in beginning user insert transaction") + logger.WithField("err", err.Error()).Error("Error while preparing user insert query") return } - _, err = tx.NamedExec(insertUserQuery, u) + _, err = stmt.Exec(u) if err != nil { logger.WithField("err", err.Error()).Error("Error while inserting user into database") return } - err = tx.Commit() - if err != nil { - logger.WithField("err", err.Error()).Error("Error while commiting transaction inserting user") - return - } - _, newUser, err = s.CheckUserByEmail(ctx, u.Email) + newUser, err = s.GetUserByEmail(ctx, u.Email) if err != nil { logger.WithField("err", err.Error()).Error("Error selecting user from database with email: " + u.Email) return @@ -64,14 +59,15 @@ func (s *pgStore) CreateNewUser(ctx context.Context, u User) (newUser User, err return } -func (s *pgStore) CheckUserByEmail(ctx context.Context, email string) (check bool, user User, err error) { +// GetUserByEmail - Checks if user is present in DB and if then return user +func (s *pgStore) GetUserByEmail(ctx context.Context, email string) (user User, err error) { err = s.db.Get(&user, getUserByEmailQuery, email) if err != nil { if err == sql.ErrNoRows { - return false, user, err + return } - logger.WithField("err", err.Error()).Error("Error while selecting user from database by email" + email) + logger.WithField("err", err.Error()).Error("Error while selecting user from database by email " + email) return } - return true, user, err + return } From 605e3bf870edfd69c4d4ca708deac27d656b4ba0 Mon Sep 17 00:00:00 2001 From: Mayur Deshmukh Date: Tue, 15 Sep 2020 14:00:59 +0530 Subject: [PATCH 10/14] Changed createUser db call --- service/user_http.go | 6 +++--- service/user_http_test.go | 10 +++++----- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/service/user_http.go b/service/user_http.go index c05a7f0..7d4e334 100644 --- a/service/user_http.go +++ b/service/user_http.go @@ -70,10 +70,10 @@ func registerUserHandler(deps Dependencies) http.HandlerFunc { } // For checking if user already registered - check, _, err := deps.Store.CheckUserByEmail(req.Context(), user.Email) + _, err = deps.Store.GetUserByEmail(req.Context(), user.Email) // If check true then user is already registered - if check { + if err != sql.ErrNoRows && err == nil { e := errorResponse{ Error: "user already registered", } @@ -106,7 +106,7 @@ func registerUserHandler(deps Dependencies) http.HandlerFunc { user.Password = string(hashedPassword) // Storing new user's data in database - _, err = deps.Store.CreateNewUser(req.Context(), user) + _, err = deps.Store.CreateUser(req.Context(), user) if err != nil { logger.WithField("err", err.Error()).Error("Error in inserting user in database") rw.WriteHeader(http.StatusInternalServerError) diff --git a/service/user_http_test.go b/service/user_http_test.go index 191361f..1f02c13 100644 --- a/service/user_http_test.go +++ b/service/user_http_test.go @@ -48,8 +48,8 @@ func makeHTTPCall(method, path, body string, handlerFunc http.HandlerFunc) (reco } func (suite *UsersHandlerTestSuite) TestRegisterUserSuccess() { - suite.dbMock.On("CreateNewUser", mock.Anything, mock.Anything).Return(db.User{}, nil) - suite.dbMock.On("CheckUserByEmail", mock.Anything, mock.Anything).Return(false, db.User{}, sql.ErrNoRows) + suite.dbMock.On("CreateUser", mock.Anything, mock.Anything).Return(db.User{}, nil) + suite.dbMock.On("GetUserByEmail", mock.Anything, mock.Anything).Return(db.User{}, sql.ErrNoRows) body := `{ "first_name" : "test1", @@ -73,8 +73,8 @@ func (suite *UsersHandlerTestSuite) TestRegisterUserSuccess() { } func (suite *UsersHandlerTestSuite) TestRegisterUserFailure() { - suite.dbMock.On("CreateNewUser", mock.Anything, mock.Anything).Return(db.User{}, nil) - suite.dbMock.On("CheckUserByEmail", mock.Anything, mock.Anything).Return(true, db.User{}, sql.ErrNoRows) + suite.dbMock.On("CreateUser", mock.Anything, mock.Anything).Return(db.User{}, nil) + suite.dbMock.On("GetUserByEmail", mock.Anything, mock.Anything).Return(db.User{}, nil) body := `{ "first_name" : "test1", @@ -96,6 +96,6 @@ func (suite *UsersHandlerTestSuite) TestRegisterUserFailure() { assert.Equal(suite.T(), `{"error":"user already registered"}`, recorder.Body.String()) assert.Equal(suite.T(), http.StatusBadRequest, recorder.Code) suite.dbMock.AssertNotCalled(suite.T(), "CreateNewUser", mock.Anything, mock.Anything) - suite.dbMock.AssertCalled(suite.T(), "CheckUserByEmail", mock.Anything, mock.Anything) + suite.dbMock.AssertCalled(suite.T(), "GetUserByEmail", mock.Anything, mock.Anything) } From f75c1356155d7d7e261d85dd9895b7d1a391802f Mon Sep 17 00:00:00 2001 From: Mayur Deshmukh Date: Thu, 17 Sep 2020 11:18:21 +0530 Subject: [PATCH 11/14] Removed commented code --- db/db.go | 4 ---- 1 file changed, 4 deletions(-) diff --git a/db/db.go b/db/db.go index 0cc0f91..0647eb5 100644 --- a/db/db.go +++ b/db/db.go @@ -7,9 +7,5 @@ import ( type Storer interface { ListUsers(context.Context) ([]User, error) CreateUser(context.Context, User) (User, error) - // CheckUserByEmail(context.Context, string) (bool, User, error) GetUserByEmail(context.Context, string) (User, error) - //Create(context.Context, User) error - //GetUser(context.Context) (User, error) - //Delete(context.Context, string) error } From 395aa91f3ac2e8de385076348bacfeece495fa33 Mon Sep 17 00:00:00 2001 From: Mayur Deshmukh Date: Thu, 17 Sep 2020 11:19:17 +0530 Subject: [PATCH 12/14] Added password hashing code --- db/user.go | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/db/user.go b/db/user.go index be2d92e..e8f13ad 100644 --- a/db/user.go +++ b/db/user.go @@ -5,6 +5,7 @@ import ( "database/sql" logger "github.com/sirupsen/logrus" + "golang.org/x/crypto/bcrypt" ) const ( @@ -41,6 +42,14 @@ func (s *pgStore) ListUsers(ctx context.Context) (users []User, err error) { // CreateNewUser = creates a new user in database func (s *pgStore) CreateUser(ctx context.Context, u User) (newUser User, err error) { + // creating hash of the password + hashedPassword, err := bcrypt.GenerateFromPassword([]byte(u.Password), 8) + if err != nil { + logger.WithField("err", err.Error()).Error("Error while creating hash of the password") + return + } + u.Password = string(hashedPassword) + stmt, err := s.db.PrepareNamed(insertUserQuery) if err != nil { logger.WithField("err", err.Error()).Error("Error while preparing user insert query") From 2ce23309c078606f3a8e2149cec8ab025eba1112 Mon Sep 17 00:00:00 2001 From: Mayur Deshmukh Date: Thu, 17 Sep 2020 11:19:43 +0530 Subject: [PATCH 13/14] Removed password hashing code --- service/user_http.go | 16 +++------------- 1 file changed, 3 insertions(+), 13 deletions(-) diff --git a/service/user_http.go b/service/user_http.go index 7d4e334..04befdc 100644 --- a/service/user_http.go +++ b/service/user_http.go @@ -8,7 +8,6 @@ import ( "net/http" logger "github.com/sirupsen/logrus" - "golang.org/x/crypto/bcrypt" ) type errorResponse struct { @@ -69,11 +68,11 @@ func registerUserHandler(deps Dependencies) http.HandlerFunc { return } - // For checking if user already registered + // Getting user by email to check if user is already present in db _, err = deps.Store.GetUserByEmail(req.Context(), user.Email) - // If check true then user is already registered - if err != sql.ErrNoRows && err == nil { + // If error is nil then user is already registered + if err == nil { e := errorResponse{ Error: "user already registered", } @@ -96,15 +95,6 @@ func registerUserHandler(deps Dependencies) http.HandlerFunc { return } - // creating hash of the password - hashedPassword, err := bcrypt.GenerateFromPassword([]byte(user.Password), 8) - if err != nil { - logger.WithField("err", err.Error()).Error("Error while creating hash of the password") - rw.WriteHeader(http.StatusInternalServerError) - return - } - user.Password = string(hashedPassword) - // Storing new user's data in database _, err = deps.Store.CreateUser(req.Context(), user) if err != nil { From b66b7484498bc6434b0fde19b83849b4dc71e75b Mon Sep 17 00:00:00 2001 From: Mayur Deshmukh Date: Thu, 17 Sep 2020 11:20:32 +0530 Subject: [PATCH 14/14] Removed mock.Anything and replaced it with user object --- service/user_http_test.go | 35 +++++++++++++++++++++++++++++------ 1 file changed, 29 insertions(+), 6 deletions(-) diff --git a/service/user_http_test.go b/service/user_http_test.go index 1f02c13..66de7b8 100644 --- a/service/user_http_test.go +++ b/service/user_http_test.go @@ -48,8 +48,19 @@ func makeHTTPCall(method, path, body string, handlerFunc http.HandlerFunc) (reco } func (suite *UsersHandlerTestSuite) TestRegisterUserSuccess() { - suite.dbMock.On("CreateUser", mock.Anything, mock.Anything).Return(db.User{}, nil) - suite.dbMock.On("GetUserByEmail", mock.Anything, mock.Anything).Return(db.User{}, sql.ErrNoRows) + user := db.User{} + user.Email = "test@gmail.com" + user.FirstName = "test1" + user.LastName = "test2" + user.Mobile = "8421987856" + user.Address = "abc" + user.State = "Maharashtra" + user.City = "Nashik" + user.Password = "password" + user.Country = "India" + + suite.dbMock.On("CreateUser", mock.Anything, user).Return(user, nil) + suite.dbMock.On("GetUserByEmail", mock.Anything, mock.Anything).Return(user, sql.ErrNoRows) body := `{ "first_name" : "test1", @@ -67,14 +78,26 @@ func (suite *UsersHandlerTestSuite) TestRegisterUserSuccess() { body, registerUserHandler(Dependencies{Store: suite.dbMock}), ) + assert.Equal(suite.T(), http.StatusCreated, recorder.Code) suite.dbMock.AssertExpectations(suite.T()) } func (suite *UsersHandlerTestSuite) TestRegisterUserFailure() { - suite.dbMock.On("CreateUser", mock.Anything, mock.Anything).Return(db.User{}, nil) - suite.dbMock.On("GetUserByEmail", mock.Anything, mock.Anything).Return(db.User{}, nil) + user := db.User{} + user.Email = "test@gmail.com" + user.FirstName = "test1" + user.LastName = "test2" + user.Mobile = "8421987856" + user.Address = "abc" + user.State = "Maharashtra" + user.City = "Nashik" + user.Password = "password" + user.Country = "India" + + suite.dbMock.On("CreateUser", mock.Anything, user).Return(user, nil) + suite.dbMock.On("GetUserByEmail", mock.Anything, user.Email).Return(user, nil) body := `{ "first_name" : "test1", @@ -95,7 +118,7 @@ func (suite *UsersHandlerTestSuite) TestRegisterUserFailure() { assert.Equal(suite.T(), `{"error":"user already registered"}`, recorder.Body.String()) assert.Equal(suite.T(), http.StatusBadRequest, recorder.Code) - suite.dbMock.AssertNotCalled(suite.T(), "CreateNewUser", mock.Anything, mock.Anything) - suite.dbMock.AssertCalled(suite.T(), "GetUserByEmail", mock.Anything, mock.Anything) + suite.dbMock.AssertNotCalled(suite.T(), "CreateUser", mock.Anything, user) + suite.dbMock.AssertCalled(suite.T(), "GetUserByEmail", mock.Anything, user.Email) }