Skip to content

Commit

Permalink
add routes
Browse files Browse the repository at this point in the history
  • Loading branch information
John-Lin committed Jul 25, 2018
1 parent ef60c33 commit d8fad9e
Show file tree
Hide file tree
Showing 4 changed files with 312 additions and 8 deletions.
4 changes: 2 additions & 2 deletions src/entity/user.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,9 +15,9 @@ const (
// User is the structure for user info
type User struct {
ID bson.ObjectId `bson:"_id,omitempty" json:"id" validate:"-"`
UUID uuid.UUID `bson:"uuid" json:"uuid" validate:"required,uuid4"`
UUID uuid.UUID `bson:"uuid" json:"uuid" validate:"required"`
JWT string `bson:"jwt" json:"jwt" validate:"-"`
UserName string `bson:"username" json:"username" validate:"required"`
Username string `bson:"username" json:"username" validate:"required"`
Email string `bson:"email" json:"email" validate:"required,email"`
Password string `bson:"password" json:"password" validate:"required"`
FirstName string `bson:"firstname" json:"firstName" validate:"required"`
Expand Down
22 changes: 16 additions & 6 deletions src/server/handler_user.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package server

import (
"fmt"
"math"
"strconv"

Expand All @@ -24,11 +25,11 @@ func createUserHandler(ctx *web.Context) {
return
}

uuid4, _ := uuid.NewV4()
user.UUID = uuid4
user.UUID = uuid.Must(uuid.NewV4())

encryptedPassword, err := hashPassword(user.Password)
if err != nil {
response.InternalServerError(req.Request, resp.ResponseWriter, err)
response.BadRequest(req.Request, resp.ResponseWriter, err)
return
}
user.Password = encryptedPassword
Expand All @@ -39,17 +40,26 @@ func createUserHandler(ctx *web.Context) {
}

session := sp.Mongo.NewSession()
// make email to be a unique key
session.C(entity.UserCollectionName).EnsureIndex(mgo.Index{
Key: []string{"email"},
Unique: true,
})
defer session.Close()

user.ID = bson.NewObjectId()
user.CreatedAt = timeutils.Now()

if err := session.Insert(entity.UserCollectionName, &user); err != nil {
response.InternalServerError(req.Request, resp.ResponseWriter, err)
if mgo.IsDup(err) {
response.Conflict(req.Request, resp.ResponseWriter, fmt.Errorf("Email: %s already existed", user.Email))
} else {
response.InternalServerError(req.Request, resp.ResponseWriter, err)
}
return
}

resp.WriteEntity(ActionResponse{
resp.WriteEntity(response.ActionResponse{
Error: false,
Message: "User Created Success",
})
Expand Down Expand Up @@ -84,7 +94,7 @@ func deleteUserHandler(ctx *web.Context) {
}
}

resp.WriteEntity(ActionResponse{
resp.WriteEntity(response.ActionResponse{
Error: false,
Message: "User Deleted Success",
})
Expand Down
283 changes: 283 additions & 0 deletions src/server/handler_user_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,283 @@
package server

import (
"encoding/json"
"math/rand"
"net/http"
"net/http/httptest"
"strings"
"testing"
"time"

restful "github.com/emicklei/go-restful"
"github.com/linkernetworks/mongo"
"github.com/linkernetworks/vortex/src/config"
"github.com/linkernetworks/vortex/src/entity"
"github.com/linkernetworks/vortex/src/serviceprovider"
"github.com/moby/moby/pkg/namesgenerator"
"github.com/stretchr/testify/suite"
"gopkg.in/mgo.v2/bson"
)

func init() {
rand.Seed(time.Now().UnixNano())
}

type UserTestSuite struct {
suite.Suite
sp *serviceprovider.Container
wc *restful.Container
session *mongo.Session
}

func (suite *UserTestSuite) SetupSuite() {
cf := config.MustRead("../../config/testing.json")
sp := serviceprovider.NewForTesting(cf)

suite.sp = sp
//init session
suite.session = sp.Mongo.NewSession()
//init restful container
suite.wc = restful.NewContainer()
user := newUserService(suite.sp)
suite.wc.Add(user)
}

func (suite *UserTestSuite) TearDownSuite() {}

func TestUserSuite(t *testing.T) {
suite.Run(t, new(UserTestSuite))
}

func (suite *UserTestSuite) TestCreateUser() {
mailAccount := namesgenerator.GetRandomName(0)
user := entity.User{
ID: bson.NewObjectId(),
Username: "John Doe",
Email: mailAccount + "@linkernetworks.com",
Password: "p@ssw0rd",
FirstName: "John",
LastName: "Doe",
PhoneNumber: "0911123456",
}

bodyBytes, err := json.MarshalIndent(user, "", " ")
suite.NoError(err)

bodyReader := strings.NewReader(string(bodyBytes))
httpRequest, err := http.NewRequest("POST", "http://localhost:7890/v1/users", bodyReader)
suite.NoError(err)

httpRequest.Header.Add("Content-Type", "application/json")
httpWriter := httptest.NewRecorder()
suite.wc.Dispatch(httpWriter, httpRequest)
assertResponseCode(suite.T(), http.StatusOK, httpWriter)
defer suite.session.Remove(entity.UserCollectionName, "email", user.Email)

// load data to check
retUser := entity.User{}
err = suite.session.FindOne(entity.UserCollectionName, bson.M{"_id": user.ID}, &retUser)
suite.NoError(err)
suite.NotEqual("", retUser.ID)
suite.Equal(user.Username, retUser.Username)
suite.Equal(user.Email, retUser.Email)

// We use the new write but empty input which will cause the readEntity Error
httpWriter = httptest.NewRecorder()
suite.wc.Dispatch(httpWriter, httpRequest)
assertResponseCode(suite.T(), http.StatusBadRequest, httpWriter)
// Create again and it should fail since the name exist
bodyReader = strings.NewReader(string(bodyBytes))
httpRequest, err = http.NewRequest("POST", "http://localhost:7890/v1/users", bodyReader)
suite.NoError(err)
httpRequest.Header.Add("Content-Type", "application/json")
httpWriter = httptest.NewRecorder()
suite.wc.Dispatch(httpWriter, httpRequest)
assertResponseCode(suite.T(), http.StatusInternalServerError, httpWriter)
}

func (suite *UserTestSuite) TestCreateUserFail() {
user := entity.User{
ID: bson.NewObjectId(),
Username: "John Doe",
FirstName: "John",
LastName: "Doe",
PhoneNumber: "0911123456",
}

bodyBytes, err := json.MarshalIndent(user, "", " ")
suite.NoError(err)

bodyReader := strings.NewReader(string(bodyBytes))
httpRequest, err := http.NewRequest("POST", "http://localhost:7890/v1/users", bodyReader)
suite.NoError(err)

httpRequest.Header.Add("Content-Type", "application/json")
httpWriter := httptest.NewRecorder()
suite.wc.Dispatch(httpWriter, httpRequest)
assertResponseCode(suite.T(), http.StatusBadRequest, httpWriter)
}

func (suite *UserTestSuite) TestDeleteUser() {
user := entity.User{
ID: bson.NewObjectId(),
Username: "John Doe",
Email: "[email protected]",
Password: "p@ssw0rd",
FirstName: "John",
LastName: "Doe",
PhoneNumber: "0911123456",
}

err := suite.session.Insert(entity.UserCollectionName, &user)
suite.NoError(err)

bodyBytes, err := json.MarshalIndent(user, "", " ")
suite.NoError(err)

bodyReader := strings.NewReader(string(bodyBytes))
httpRequest, err := http.NewRequest("DELETE", "http://localhost:7890/v1/users/"+user.ID.Hex(), bodyReader)
suite.NoError(err)

httpRequest.Header.Add("Content-Type", "application/json")
httpWriter := httptest.NewRecorder()
suite.wc.Dispatch(httpWriter, httpRequest)
assertResponseCode(suite.T(), http.StatusOK, httpWriter)

n, err := suite.session.Count(entity.UserCollectionName, bson.M{"_id": user.ID})
suite.NoError(err)
suite.Equal(0, n)
}

func (suite *UserTestSuite) TestDeleteUserWithInvalidID() {
httpRequest, err := http.NewRequest("DELETE", "http://localhost:7890/v1/users/"+bson.NewObjectId().Hex(), nil)
suite.NoError(err)

httpRequest.Header.Add("Content-Type", "application/json")
httpWriter := httptest.NewRecorder()
suite.wc.Dispatch(httpWriter, httpRequest)
assertResponseCode(suite.T(), http.StatusBadRequest, httpWriter)
}

// For Get/List, we only return mongo document
func (suite *UserTestSuite) TestGetUser() {
mailAccount := namesgenerator.GetRandomName(0)
user := entity.User{
ID: bson.NewObjectId(),
Username: "John Doe",
Email: mailAccount + "@linkernetworks.com",
Password: "p@ssw0rd",
FirstName: "John",
LastName: "Doe",
PhoneNumber: "0911123456",
}
// Create data into mongo manually
suite.session.C(entity.UserCollectionName).Insert(user)
defer suite.session.Remove(entity.UserCollectionName, "email", user.Email)

httpRequest, err := http.NewRequest("GET", "http://localhost:7890/v1/users/"+user.ID.Hex(), nil)
suite.NoError(err)

httpWriter := httptest.NewRecorder()
suite.wc.Dispatch(httpWriter, httpRequest)
assertResponseCode(suite.T(), http.StatusOK, httpWriter)

retUser := entity.User{}
err = json.Unmarshal(httpWriter.Body.Bytes(), &retUser)
suite.NoError(err)
suite.Equal(user.Username, retUser.Username)
suite.Equal(user.Email, retUser.Email)
}

func (suite *UserTestSuite) TestGetUserWithInvalidID() {
// Get data with non-exits ID
httpRequest, err := http.NewRequest("GET", "http://localhost:7890/v1/users/"+bson.NewObjectId().Hex(), nil)
suite.NoError(err)

httpWriter := httptest.NewRecorder()
suite.wc.Dispatch(httpWriter, httpRequest)
assertResponseCode(suite.T(), http.StatusNotFound, httpWriter)
}

func (suite *UserTestSuite) TestListUser() {
users := []entity.User{}
count := 3
for i := 0; i < count; i++ {
mailAccount := namesgenerator.GetRandomName(i)
users = append(users, entity.User{
ID: bson.NewObjectId(),
Username: "John Doe",
Email: mailAccount + "@linkernetworks.com",
Password: "p@ssw0rd",
FirstName: "John",
LastName: "Doe",
PhoneNumber: "0911123456",
})
}

for _, u := range users {
suite.session.C(entity.UserCollectionName).Insert(u)
defer suite.session.Remove(entity.UserCollectionName, "email", u.Email)
}

testCases := []struct {
page string
pageSize string
expectSize int
}{
{"", "", count},
{"1", "1", count},
{"1", "3", count},
}

for _, tc := range testCases {
caseName := "page:pageSize" + tc.page + ":" + tc.pageSize
suite.T().Run(caseName, func(t *testing.T) {
// list data by default page and page_size
url := "http://localhost:7890/v1/users/"
if tc.page != "" || tc.pageSize != "" {
url = "http://localhost:7890/v1/users?"
url += "page=" + tc.page + "%" + "page_size" + tc.pageSize
}
httpRequest, err := http.NewRequest("GET", url, nil)
suite.NoError(err)

httpWriter := httptest.NewRecorder()
suite.wc.Dispatch(httpWriter, httpRequest)
assertResponseCode(suite.T(), http.StatusOK, httpWriter)

retUsers := []entity.User{}
err = json.Unmarshal(httpWriter.Body.Bytes(), &retUsers)
suite.NoError(err)
suite.Equal(tc.expectSize, len(retUsers))
for i, u := range retUsers {
suite.Equal(users[i].Username, u.Username)
suite.Equal(users[i].Email, u.Email)
}
})
}
}

func (suite *UserTestSuite) TestListUserWithInvalidPage() {
// Get data with non-exits ID
httpRequest, err := http.NewRequest("GET", "http://localhost:7890/v1/users?page=asdd", nil)
suite.NoError(err)

httpWriter := httptest.NewRecorder()
suite.wc.Dispatch(httpWriter, httpRequest)
assertResponseCode(suite.T(), http.StatusBadRequest, httpWriter)

httpRequest, err = http.NewRequest("GET", "http://localhost:7890/v1/users?page_size=asdd", nil)
suite.NoError(err)

httpWriter = httptest.NewRecorder()
suite.wc.Dispatch(httpWriter, httpRequest)
assertResponseCode(suite.T(), http.StatusBadRequest, httpWriter)

httpRequest, err = http.NewRequest("GET", "http://localhost:7890/v1/users?page=-1", nil)
suite.NoError(err)

httpWriter = httptest.NewRecorder()
suite.wc.Dispatch(httpWriter, httpRequest)
assertResponseCode(suite.T(), http.StatusInternalServerError, httpWriter)
}
11 changes: 11 additions & 0 deletions src/server/route.go
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ func (a *App) AppRoute() *mux.Router {
container.Filter(globalLogging)

container.Add(newVersionService(a.ServiceProvider))
container.Add(newUserService(a.ServiceProvider))
container.Add(newNetworkService(a.ServiceProvider))
container.Add(newStorageService(a.ServiceProvider))
container.Add(newVolumeService(a.ServiceProvider))
Expand All @@ -34,6 +35,16 @@ func newVersionService(sp *serviceprovider.Container) *restful.WebService {
return webService
}

func newUserService(sp *serviceprovider.Container) *restful.WebService {
webService := new(restful.WebService)
webService.Path("/v1/users").Consumes(restful.MIME_JSON, restful.MIME_JSON).Produces(restful.MIME_JSON, restful.MIME_JSON)
webService.Route(webService.POST("/").To(handler.RESTfulServiceHandler(sp, createUserHandler)))
webService.Route(webService.DELETE("/{id}").To(handler.RESTfulServiceHandler(sp, deleteUserHandler)))
webService.Route(webService.GET("/").To(handler.RESTfulServiceHandler(sp, listUserHandler)))
webService.Route(webService.GET("/{id}").To(handler.RESTfulServiceHandler(sp, getUserHandler)))
return webService
}

func newNetworkService(sp *serviceprovider.Container) *restful.WebService {
webService := new(restful.WebService)
webService.Path("/v1/networks").Consumes(restful.MIME_JSON, restful.MIME_JSON).Produces(restful.MIME_JSON, restful.MIME_JSON)
Expand Down

0 comments on commit d8fad9e

Please sign in to comment.