From e97d7dab8518d2ac7f6a6940784ba34b50fea9e1 Mon Sep 17 00:00:00 2001
From: Wenkai Yin <yinw@vmware.com>
Date: Fri, 9 Jun 2017 14:11:44 +0800
Subject: [PATCH 1/5] refactor GET /projects

---
 docs/swagger.yaml                        | 27 +++++++--
 src/common/dao/project.go                | 39 ++++++++++--
 src/common/models/project.go             |  8 +++
 src/common/security/rbac/context_test.go |  4 +-
 src/ui/api/harborapi_test.go             | 16 ++---
 src/ui/api/log_test.go                   | 38 ++++++------
 src/ui/api/project.go                    | 75 ++++++++++++++++--------
 src/ui/api/project_test.go               | 62 ++++++++++++--------
 src/ui/projectmanager/db/pm.go           |  8 +--
 src/ui/projectmanager/pm.go              |  4 +-
 tests/apitests/apilib/project.go         | 10 ++++
 11 files changed, 192 insertions(+), 99 deletions(-)

diff --git a/docs/swagger.yaml b/docs/swagger.yaml
index 825434fa7..0334b0555 100644
--- a/docs/swagger.yaml
+++ b/docs/swagger.yaml
@@ -43,21 +43,36 @@ paths:
           description: Unexpected internal errors.
   /projects:
     get:
-      summary: Return projects created by Harbor
+      summary: List projects
       description: |
         This endpoint returns all projects created by Harbor, and can be filtered by project name.
       parameters:
-        - name: project_name 
+        - name: name 
           in: query
-          description: Project name for filtering results.
+          description: The name of project.
           required: false
           type: string
-        - name: is_public
+        - name: public
           in: query
-          description: Public sign for filtering projects.
+          description: The project is public or private.
+          required: false
+          type: boolean
+          format: int32
+        - name: owner 
+          in: query
+          description: The name of project owner.
+          required: false
+          type: string
+        - name: member 
+          in: query
+          description: The name of project member.
+          required: false
+          type: string
+        - name: role 
+          in: query
+          description: The role of member, only take effect when member is provided.
           required: false
           type: integer
-          format: int32
         - name: page
           in: query
           type: integer
diff --git a/src/common/dao/project.go b/src/common/dao/project.go
index d0cb990e4..5f0e73fad 100644
--- a/src/common/dao/project.go
+++ b/src/common/dao/project.go
@@ -190,7 +190,7 @@ func GetHasReadPermProjects(username string) ([]*models.Project, error) {
 
 // GetTotalOfProjects returns the total count of projects
 // according to the query conditions
-func GetTotalOfProjects(query *models.ProjectQueryParam) (int64, error) {
+func GetTotalOfProjects(query *models.ProjectQueryParam, base ...*models.BaseProjectCollection) (int64, error) {
 
 	var (
 		owner  string
@@ -210,7 +210,7 @@ func GetTotalOfProjects(query *models.ProjectQueryParam) (int64, error) {
 		}
 	}
 
-	sql, params := projectQueryConditions(owner, name, public, member, role)
+	sql, params := projectQueryConditions(owner, name, public, member, role, base...)
 
 	sql = `select count(*) ` + sql
 
@@ -220,7 +220,7 @@ func GetTotalOfProjects(query *models.ProjectQueryParam) (int64, error) {
 }
 
 // GetProjects returns a project list according to the query conditions
-func GetProjects(query *models.ProjectQueryParam) ([]*models.Project, error) {
+func GetProjects(query *models.ProjectQueryParam, base ...*models.BaseProjectCollection) ([]*models.Project, error) {
 
 	var (
 		owner  string
@@ -246,7 +246,7 @@ func GetProjects(query *models.ProjectQueryParam) ([]*models.Project, error) {
 		}
 	}
 
-	sql, params := projectQueryConditions(owner, name, public, member, role)
+	sql, params := projectQueryConditions(owner, name, public, member, role, base...)
 
 	sql = `select distinct p.project_id, p.name, p.public, p.owner_id, 
 				p.creation_time, p.update_time ` + sql
@@ -266,10 +266,37 @@ func GetProjects(query *models.ProjectQueryParam) ([]*models.Project, error) {
 }
 
 func projectQueryConditions(owner, name string, public *bool, member string,
-	role int) (string, []interface{}) {
+	role int, base ...*models.BaseProjectCollection) (string, []interface{}) {
 	params := []interface{}{}
 
-	sql := ` from project p`
+	// the base project collections:
+	// 1. all projects
+	// 2. public projects
+	// 3. public projects and projects which the user is a member of
+	// 4. projects which the user is a member of
+	collection := `project `
+	if len(base) != 0 && base[0] != nil {
+		if len(base[0].Member) > 0 {
+			collection = `(select pr.project_id, pr.owner_id, pr.name, pr.
+						creation_time, pr.update_time, pr.deleted, pr.public 
+					from project pr
+					join project_member prm
+						on pr.project_id = prm.project_id
+					join user ur
+						on prm.user_id=ur.user_id
+					where ur.username=? `
+			if base[0].Public {
+				collection += ` or pr.public=1 `
+			}
+			collection += `) `
+			params = append(params, base[0].Member)
+		} else if base[0].Public {
+			collection = `(select * from project pr
+					where pr.public=1) `
+		}
+	}
+
+	sql := ` from ` + collection + ` as p`
 
 	if len(owner) != 0 {
 		sql += ` join user u1
diff --git a/src/common/models/project.go b/src/common/models/project.go
index db7f62d5f..fc01f81f8 100644
--- a/src/common/models/project.go
+++ b/src/common/models/project.go
@@ -88,3 +88,11 @@ type Pagination struct {
 	Page int64
 	Size int64
 }
+
+// BaseProjectCollection contains the query conditions which can be used
+// to get a project collection. The collection can be used as the base to
+// do other filter
+type BaseProjectCollection struct {
+	Public bool
+	Member string
+}
diff --git a/src/common/security/rbac/context_test.go b/src/common/security/rbac/context_test.go
index d72d66426..f23958c58 100644
--- a/src/common/security/rbac/context_test.go
+++ b/src/common/security/rbac/context_test.go
@@ -106,7 +106,7 @@ func (f *fakePM) Update(projectIDOrName interface{}, project *models.Project) er
 }
 
 // nil implement
-func (f *fakePM) GetAll(*models.ProjectQueryParam) ([]*models.Project, error) {
+func (f *fakePM) GetAll(*models.ProjectQueryParam, ...*models.BaseProjectCollection) ([]*models.Project, error) {
 	return []*models.Project{}, nil
 }
 
@@ -116,7 +116,7 @@ func (f *fakePM) GetHasReadPerm(username ...string) ([]*models.Project, error) {
 }
 
 // nil implement
-func (f *fakePM) GetTotal(*models.ProjectQueryParam) (int64, error) {
+func (f *fakePM) GetTotal(*models.ProjectQueryParam, ...*models.BaseProjectCollection) (int64, error) {
 	return 0, nil
 }
 
diff --git a/src/ui/api/harborapi_test.go b/src/ui/api/harborapi_test.go
index 135225287..580b61477 100644
--- a/src/ui/api/harborapi_test.go
+++ b/src/ui/api/harborapi_test.go
@@ -329,17 +329,10 @@ func (a testapi) ProjectsGetByPID(projectID string) (int, apilib.Project, error)
 }
 
 //Search projects by projectName and isPublic
-func (a testapi) ProjectsGet(projectName string, isPublic int32, authInfo ...usrInfo) (int, []apilib.Project, error) {
-	_sling := sling.New().Get(a.basePath)
-
-	//create api path
-	path := "api/projects"
-	_sling = _sling.Path(path)
-	type QueryParams struct {
-		ProjectName string `url:"project_name,omitempty"`
-		IsPubilc    int32  `url:"is_public,omitempty"`
-	}
-	_sling = _sling.QueryStruct(&QueryParams{ProjectName: projectName, IsPubilc: isPublic})
+func (a testapi) ProjectsGet(query *apilib.ProjectQuery, authInfo ...usrInfo) (int, []apilib.Project, error) {
+	_sling := sling.New().Get(a.basePath).
+		Path("api/projects").
+		QueryStruct(query)
 
 	var successPayload []apilib.Project
 
@@ -349,6 +342,7 @@ func (a testapi) ProjectsGet(projectName string, isPublic int32, authInfo ...usr
 	if len(authInfo) > 0 {
 		httpStatusCode, body, err = request(_sling, jsonAcceptHeader, authInfo[0])
 	} else {
+		log.Println("enter no auth $$$$$$$$$$$$$$$$$$$")
 		httpStatusCode, body, err = request(_sling, jsonAcceptHeader)
 	}
 
diff --git a/src/ui/api/log_test.go b/src/ui/api/log_test.go
index e86367bd9..f9c83d962 100644
--- a/src/ui/api/log_test.go
+++ b/src/ui/api/log_test.go
@@ -23,28 +23,24 @@ import (
 )
 
 func TestLogGet(t *testing.T) {
-
 	fmt.Println("Testing Log API")
-	assert := assert.New(t)
 	apiTest := newHarborAPI()
+	assert := assert.New(t)
 
-	//prepare for test
 	CommonAddUser()
-	var project apilib.ProjectReq
-	project.ProjectName = "my_project"
-	project.Public = 1
-	statusCode, result, err := apiTest.LogGet(*testUser)
-	if err != nil {
-		t.Error("Error while get log information", err.Error())
-		t.Log(err)
-	} else {
-		assert.Equal(int(200), statusCode, "Log get should return 200")
 
-	}
+	statusCode, result, err := apiTest.LogGet(*testUser)
+	assert.Nil(err)
+	assert.Equal(200, statusCode)
+
 	logNum := len(result)
-	fmt.Println("result", result)
-	//add the project first.
+
 	fmt.Println("add the project first.")
+	project := apilib.ProjectReq{
+		ProjectName: "project_for_test_log",
+		Public:      1,
+	}
+
 	reply, err := apiTest.ProjectsPost(*testUser, project)
 	if err != nil {
 		t.Error("Error while creat project", err.Error())
@@ -63,7 +59,7 @@ func TestLogGet(t *testing.T) {
 		if num != 1 {
 			assert.Equal(1, num, "add my_project log number should be 1")
 		} else {
-			assert.Equal("my_project/", result[index].RepoName, "RepoName should be equal")
+			assert.Equal("project_for_test_log/", result[index].RepoName)
 			assert.Equal("N/A", result[index].RepoTag, "RepoTag should be equal")
 			assert.Equal("create", result[index].Operation, "Operation should be equal")
 		}
@@ -73,7 +69,12 @@ func TestLogGet(t *testing.T) {
 	//get the project
 	var projects []apilib.Project
 	var addProjectID int32
-	httpStatusCode, projects, err := apiTest.ProjectsGet(project.ProjectName, 1)
+	httpStatusCode, projects, err := apiTest.ProjectsGet(
+		&apilib.ProjectQuery{
+			Name:   project.ProjectName,
+			Owner:  testUser.Name,
+			Public: true,
+		})
 	if err != nil {
 		t.Error("Error while search project by proName and isPublic", err.Error())
 		t.Log(err)
@@ -81,6 +82,7 @@ func TestLogGet(t *testing.T) {
 		assert.Equal(int(200), httpStatusCode, "httpStatusCode should be 200")
 		addProjectID = projects[0].ProjectId
 	}
+	t.Logf("%%%%%%%%%%%%% %v", projects)
 
 	//delete the project
 	projectID := strconv.Itoa(int(addProjectID))
@@ -99,7 +101,7 @@ func TestLogGet(t *testing.T) {
 func getLog(result []apilib.AccessLog) (int, int) {
 	var num, index int
 	for i := 0; i < len(result); i++ {
-		if result[i].RepoName == "my_project/" {
+		if result[i].RepoName == "project_for_test_log/" {
 			num++
 			index = i
 		}
diff --git a/src/ui/api/project.go b/src/ui/api/project.go
index 36596c2cb..caff5749c 100644
--- a/src/ui/api/project.go
+++ b/src/ui/api/project.go
@@ -256,58 +256,81 @@ func projectContainsPolicy(id int64) (bool, error) {
 }
 
 // List ...
-// TODO refacter pattern to:
-// /api/repositories?owner=xxx&name=xxx&public=true&member=xxx&role=1&page=1&size=3
 func (p *ProjectAPI) List() {
-	query := &models.ProjectQueryParam{}
+	// query strings
+	page, size := p.GetPaginationParams()
+	query := &models.ProjectQueryParam{
+		Name:  p.GetString("name"),
+		Owner: p.GetString("owner"),
+		Pagination: &models.Pagination{
+			Page: page,
+			Size: size,
+		},
+	}
 
-	query.Name = p.GetString("project_name")
-	public := p.GetString("is_public")
-	if len(public) != 0 {
-		if public != "0" && public != "1" {
-			p.HandleBadRequest("is_public should be 0 or 1")
+	public := p.GetString("public")
+	if len(public) > 0 {
+		pub, err := strconv.ParseBool(public)
+		if err != nil {
+			p.HandleBadRequest(fmt.Sprintf("invalid public: %s", public))
 			return
 		}
-		if public == "1" {
-			t := true
-			query.Public = &t
+		query.Public = &pub
+	}
+
+	member := p.GetString("member")
+	if len(member) > 0 {
+		query.Member = &models.Member{
+			Name: member,
+		}
+
+		role := p.GetString("role")
+		if len(role) > 0 {
+			r, err := strconv.Atoi(role)
+			if err != nil {
+				if err != nil {
+					p.HandleBadRequest(fmt.Sprintf("invalid role: %s", role))
+					return
+				}
+			}
+			query.Member.Role = r
 		}
 	}
 
-	if query.Public == nil || *query.Public == false {
-		//if the request is not for public projects, user must login or provide credential
-		if !p.SecurityCtx.IsAuthenticated() {
+	// base project collection from which filter is done
+	base := &models.BaseProjectCollection{}
+	if !p.SecurityCtx.IsAuthenticated() {
+		if query.Member != nil && len(query.Member.Name) > 0 {
+			// must login if query member
 			p.HandleUnauthorized()
 			return
 		}
-
+		base.Public = true
+	} else {
 		if !p.SecurityCtx.IsSysAdmin() {
-			query.Member = &models.Member{
-				Name: p.SecurityCtx.GetUsername(),
+			base.Member = p.SecurityCtx.GetUsername()
+			if query.Member != nil && len(query.Member.Name) > 0 {
+				base.Public = false
+			} else {
+				base.Public = true
 			}
 		}
 	}
 
-	total, err := p.ProjectMgr.GetTotal(query)
+	total, err := p.ProjectMgr.GetTotal(query, base)
 	if err != nil {
 		p.HandleInternalServerError(fmt.Sprintf("failed to get total of projects: %v", err))
 		return
 	}
 
-	page, size := p.GetPaginationParams()
-	query.Pagination = &models.Pagination{
-		Page: page,
-		Size: size,
-	}
-
-	projects, err := p.ProjectMgr.GetAll(query)
+	projects, err := p.ProjectMgr.GetAll(query, base)
 	if err != nil {
 		p.HandleInternalServerError(fmt.Sprintf("failed to get projects: %v", err))
 		return
 	}
 
 	for _, project := range projects {
-		if query.Public == nil || *query.Public == false {
+		if p.SecurityCtx.IsAuthenticated() {
 			roles, err := p.ProjectMgr.GetRoles(p.SecurityCtx.GetUsername(), project.ProjectID)
 			if err != nil {
 				p.HandleInternalServerError(fmt.Sprintf("failed to get roles of user %s to project %d: %v",
diff --git a/src/ui/api/project_test.go b/src/ui/api/project_test.go
index 3f725af6a..edbc23826 100644
--- a/src/ui/api/project_test.go
+++ b/src/ui/api/project_test.go
@@ -90,8 +90,7 @@ func TestAddProject(t *testing.T) {
 
 }
 
-//Get project by proName
-func TestProGetByName(t *testing.T) {
+func TestListProjects(t *testing.T) {
 	fmt.Println("\nTest for Project GET API by project name")
 	assert := assert.New(t)
 
@@ -100,29 +99,39 @@ func TestProGetByName(t *testing.T) {
 
 	//----------------------------case 1 : Response Code=200----------------------------//
 	fmt.Println("case 1: respose code:200")
-	httpStatusCode, result, err := apiTest.ProjectsGet(addProject.ProjectName, 1)
-	if err != nil {
-		t.Error("Error while search project by proName and isPublic", err.Error())
-		t.Log(err)
-	} else {
-		assert.Equal(int(200), httpStatusCode, "httpStatusCode should be 200")
-		assert.Equal(addProject.ProjectName, result[0].ProjectName, "Project name is wrong")
-		assert.Equal(int32(1), result[0].Public, "Public is wrong")
-		//find add projectID
-		addPID = int(result[0].ProjectId)
-	}
-	//----------------------------case 2 : Response Code=401:is_public=0----------------------------//
-	fmt.Println("case 2: respose code:401,isPublic = 0")
-	httpStatusCode, result, err = apiTest.ProjectsGet("library", 0)
-	if err != nil {
-		t.Error("Error while search project by proName and isPublic", err.Error())
-		t.Log(err)
-	} else {
-		assert.Equal(int(401), httpStatusCode, "httpStatusCode should be 200")
-	}
+	httpStatusCode, result, err := apiTest.ProjectsGet(
+		&apilib.ProjectQuery{
+			Name:   addProject.ProjectName,
+			Owner:  admin.Name,
+			Public: true,
+		})
+	assert.Nil(err)
+	assert.Equal(int(200), httpStatusCode, "httpStatusCode should be 200")
+	assert.Equal(addProject.ProjectName, result[0].ProjectName, "Project name is wrong")
+	assert.Equal(int32(1), result[0].Public, "Public is wrong")
+
+	//find add projectID
+	addPID = int(result[0].ProjectId)
+
+	//----------------------------case 2 : Response Code=401----------------------------//
+	fmt.Println("case 2: respose code:401")
+	httpStatusCode, result, err = apiTest.ProjectsGet(
+		&apilib.ProjectQuery{
+			Member: admin.Name,
+		})
+	t.Logf("$$$$$$$$$$$$$$$$$$$%v", result)
+	assert.Nil(err)
+	assert.Equal(int(401), httpStatusCode)
 
 	//-------------------case 3 :  check admin project role------------------------//
-	httpStatusCode, result, err = apiTest.ProjectsGet(addProject.ProjectName, 0, *admin)
+	httpStatusCode, result, err = apiTest.ProjectsGet(
+		&apilib.ProjectQuery{
+			Name:   addProject.ProjectName,
+			Owner:  admin.Name,
+			Public: true,
+			Member: admin.Name,
+			Role:   1,
+		}, *admin)
 	if err != nil {
 		t.Error("Error while search project by proName and isPublic", err.Error())
 		t.Log(err)
@@ -144,7 +153,12 @@ func TestProGetByName(t *testing.T) {
 	} else {
 		assert.Equal(int(200), httpStatusCode, "httpStatusCode should be 200")
 	}
-	httpStatusCode, result, err = apiTest.ProjectsGet(addProject.ProjectName, 0, *testUser)
+	httpStatusCode, result, err = apiTest.ProjectsGet(
+		&apilib.ProjectQuery{
+			Name:   addProject.ProjectName,
+			Member: TestUserName,
+			Role:   2,
+		}, *testUser)
 	if err != nil {
 		t.Error("Error while search project by proName and isPublic", err.Error())
 		t.Log(err)
diff --git a/src/ui/projectmanager/db/pm.go b/src/ui/projectmanager/db/pm.go
index dd8819d3e..a44f6a1ba 100644
--- a/src/ui/projectmanager/db/pm.go
+++ b/src/ui/projectmanager/db/pm.go
@@ -190,15 +190,15 @@ func (p *ProjectManager) Update(projectIDOrName interface{},
 }
 
 // GetAll returns a project list according to the query parameters
-func (p *ProjectManager) GetAll(query *models.ProjectQueryParam) (
+func (p *ProjectManager) GetAll(query *models.ProjectQueryParam, base ...*models.BaseProjectCollection) (
 	[]*models.Project, error) {
-	return dao.GetProjects(query)
+	return dao.GetProjects(query, base...)
 }
 
 // GetTotal returns the total count according to the query parameters
-func (p *ProjectManager) GetTotal(query *models.ProjectQueryParam) (
+func (p *ProjectManager) GetTotal(query *models.ProjectQueryParam, base ...*models.BaseProjectCollection) (
 	int64, error) {
-	return dao.GetTotalOfProjects(query)
+	return dao.GetTotalOfProjects(query, base...)
 }
 
 // GetHasReadPerm returns projects which are public or the user is a member of
diff --git a/src/ui/projectmanager/pm.go b/src/ui/projectmanager/pm.go
index 3e497a737..676dfe9c3 100644
--- a/src/ui/projectmanager/pm.go
+++ b/src/ui/projectmanager/pm.go
@@ -33,9 +33,9 @@ type ProjectManager interface {
 	Delete(projectIDOrName interface{}) error
 	Update(projectIDOrName interface{}, project *models.Project) error
 	// GetAll returns a project list according to the query parameters
-	GetAll(query *models.ProjectQueryParam) ([]*models.Project, error)
+	GetAll(query *models.ProjectQueryParam, base ...*models.BaseProjectCollection) ([]*models.Project, error)
 	// GetTotal returns the total count according to the query parameters
-	GetTotal(query *models.ProjectQueryParam) (int64, error)
+	GetTotal(query *models.ProjectQueryParam, base ...*models.BaseProjectCollection) (int64, error)
 	// GetHasReadPerm returns a project list which the user has read
 	// permission of. The list should contains all public projects and
 	// projects which the user is a member of if the username is not nil
diff --git a/tests/apitests/apilib/project.go b/tests/apitests/apilib/project.go
index a7bde6f6e..abf1c7480 100644
--- a/tests/apitests/apilib/project.go
+++ b/tests/apitests/apilib/project.go
@@ -57,3 +57,13 @@ type Project struct {
 	// The number of the repositories under this project.
 	RepoCount int32 `json:"repo_count,omitempty"`
 }
+
+type ProjectQuery struct {
+	Name     string `json:"name"`
+	Owner    string `json:"owner"`
+	Public   bool   `json:"public"`
+	Member   string `json:"member"`
+	Role     int    `json:"role"`
+	Page     int64  `json:"page"`
+	PageSize int64  `json:"page_size"`
+}

From 6b4ae098db8bdd4fe87d12bc51693967edd35e9c Mon Sep 17 00:00:00 2001
From: Wenkai Yin <yinw@vmware.com>
Date: Fri, 9 Jun 2017 17:01:56 +0800
Subject: [PATCH 2/5] update

---
 src/ui/api/harborapi_test.go               |  3 ++-
 src/ui/api/log_test.go                     |  1 -
 tests/apitests/apilib/access_log_filter.go | 16 ++++++++--------
 tests/apitests/apilib/project.go           | 14 +++++++-------
 4 files changed, 17 insertions(+), 17 deletions(-)

diff --git a/src/ui/api/harborapi_test.go b/src/ui/api/harborapi_test.go
index fa5f85fed..426a45e35 100644
--- a/src/ui/api/harborapi_test.go
+++ b/src/ui/api/harborapi_test.go
@@ -342,12 +342,13 @@ func (a testapi) ProjectsGet(query *apilib.ProjectQuery, authInfo ...usrInfo) (i
 	if len(authInfo) > 0 {
 		httpStatusCode, body, err = request(_sling, jsonAcceptHeader, authInfo[0])
 	} else {
-		log.Println("enter no auth $$$$$$$$$$$$$$$$$$$")
 		httpStatusCode, body, err = request(_sling, jsonAcceptHeader)
 	}
 
 	if err == nil && httpStatusCode == 200 {
 		err = json.Unmarshal(body, &successPayload)
+	} else {
+		log.Println(string(body))
 	}
 
 	return httpStatusCode, successPayload, err
diff --git a/src/ui/api/log_test.go b/src/ui/api/log_test.go
index f9c83d962..cfef52579 100644
--- a/src/ui/api/log_test.go
+++ b/src/ui/api/log_test.go
@@ -82,7 +82,6 @@ func TestLogGet(t *testing.T) {
 		assert.Equal(int(200), httpStatusCode, "httpStatusCode should be 200")
 		addProjectID = projects[0].ProjectId
 	}
-	t.Logf("%%%%%%%%%%%%% %v", projects)
 
 	//delete the project
 	projectID := strconv.Itoa(int(addProjectID))
diff --git a/tests/apitests/apilib/access_log_filter.go b/tests/apitests/apilib/access_log_filter.go
index 0b6b63d68..9a3a38ad7 100644
--- a/tests/apitests/apilib/access_log_filter.go
+++ b/tests/apitests/apilib/access_log_filter.go
@@ -23,12 +23,12 @@
 package apilib
 
 type LogQuery struct {
-	Username       string   `json:"username"`
-	Repository     string   `json:"repository"`
-	Tag            string   `json:"tag"`
-	Operation      []string `json:"operation"`
-	BeginTimestamp int64    `json:"begin_timestamp"`
-	EndTimestamp   int64    `json:"end_timestamp"`
-	Page           int64    `json:"page"`
-	PageSize       int64    `json:"page_size"`
+	Username       string   `url:"username,omitempty"`
+	Repository     string   `url:"repository,omitempty"`
+	Tag            string   `url:"tag,omitempty"`
+	Operation      []string `url:"operation,omitempty"`
+	BeginTimestamp int64    `url:"begin_timestamp,omitempty"`
+	EndTimestamp   int64    `url:"end_timestamp,omitempty"`
+	Page           int64    `url:"page,omitempty"`
+	PageSize       int64    `url:"page_size,omitempty"`
 }
diff --git a/tests/apitests/apilib/project.go b/tests/apitests/apilib/project.go
index abf1c7480..7aae3714d 100644
--- a/tests/apitests/apilib/project.go
+++ b/tests/apitests/apilib/project.go
@@ -59,11 +59,11 @@ type Project struct {
 }
 
 type ProjectQuery struct {
-	Name     string `json:"name"`
-	Owner    string `json:"owner"`
-	Public   bool   `json:"public"`
-	Member   string `json:"member"`
-	Role     int    `json:"role"`
-	Page     int64  `json:"page"`
-	PageSize int64  `json:"page_size"`
+	Name     string `url:"name,omitempty"`
+	Owner    string `url:"owner,omitempty"`
+	Public   bool   `url:"public,omitempty"`
+	Member   string `url:"member,omitempty"`
+	Role     int    `url:"role,omitempty"`
+	Page     int64  `url:"page,omitempty"`
+	PageSize int64  `url:"page_size,omitempty"`
 }

From 802a2fbcce500247e49f480c1b8af565cb0a7003 Mon Sep 17 00:00:00 2001
From: Wenkai Yin <yinw@vmware.com>
Date: Wed, 14 Jun 2017 20:23:29 +0800
Subject: [PATCH 3/5] update

---
 docs/swagger.yaml          | 10 ----------
 src/common/dao/project.go  |  1 -
 src/ui/api/project.go      | 33 ++++-----------------------------
 src/ui/api/project_test.go | 16 +---------------
 4 files changed, 5 insertions(+), 55 deletions(-)

diff --git a/docs/swagger.yaml b/docs/swagger.yaml
index a90592b92..ad9579df8 100644
--- a/docs/swagger.yaml
+++ b/docs/swagger.yaml
@@ -63,16 +63,6 @@ paths:
           description: The name of project owner.
           required: false
           type: string
-        - name: member 
-          in: query
-          description: The name of project member.
-          required: false
-          type: string
-        - name: role 
-          in: query
-          description: The role of member, only take effect when member is provided.
-          required: false
-          type: integer
         - name: page
           in: query
           type: integer
diff --git a/src/common/dao/project.go b/src/common/dao/project.go
index 5f0e73fad..d3cb4ae34 100644
--- a/src/common/dao/project.go
+++ b/src/common/dao/project.go
@@ -273,7 +273,6 @@ func projectQueryConditions(owner, name string, public *bool, member string,
 	// 1. all projects
 	// 2. public projects
 	// 3. public projects and projects which the user is a member of
-	// 4. projects which the user is a member of
 	collection := `project `
 	if len(base) != 0 && base[0] != nil {
 		if len(base[0].Member) > 0 {
diff --git a/src/ui/api/project.go b/src/ui/api/project.go
index 4c6ff1965..a06be9e69 100644
--- a/src/ui/api/project.go
+++ b/src/ui/api/project.go
@@ -278,42 +278,17 @@ func (p *ProjectAPI) List() {
 		query.Public = &pub
 	}
 
-	member := p.GetString("member")
-	if len(member) > 0 {
-		query.Member = &models.Member{
-			Name: member,
-		}
-
-		role := p.GetString("role")
-		if len(role) > 0 {
-			r, err := strconv.Atoi(role)
-			if err != nil {
-				if err != nil {
-					p.HandleBadRequest(fmt.Sprintf("invalid role: %s", role))
-					return
-				}
-			}
-			query.Member.Role = r
-		}
-	}
-
 	// base project collection from which filter is done
 	base := &models.BaseProjectCollection{}
 	if !p.SecurityCtx.IsAuthenticated() {
-		if query.Member != nil && len(query.Member.Name) > 0 {
-			// must login if query member
-			p.HandleUnauthorized()
-			return
-		}
+		// not login, only get public projects
 		base.Public = true
 	} else {
 		if !p.SecurityCtx.IsSysAdmin() {
+			// login, but not system admin, get public projects and
+			// projects that the user is member of
 			base.Member = p.SecurityCtx.GetUsername()
-			if query.Member != nil && len(query.Member.Name) > 0 {
-				base.Public = false
-			} else {
-				base.Public = true
-			}
+			base.Public = true
 		}
 	}
 
diff --git a/src/ui/api/project_test.go b/src/ui/api/project_test.go
index 8b4d0571f..0bc143d80 100644
--- a/src/ui/api/project_test.go
+++ b/src/ui/api/project_test.go
@@ -113,24 +113,12 @@ func TestListProjects(t *testing.T) {
 	//find add projectID
 	addPID = int(result[0].ProjectId)
 
-	//----------------------------case 2 : Response Code=401----------------------------//
-	fmt.Println("case 2: respose code:401")
-	httpStatusCode, result, err = apiTest.ProjectsGet(
-		&apilib.ProjectQuery{
-			Member: admin.Name,
-		})
-	t.Logf("$$$$$$$$$$$$$$$$$$$%v", result)
-	assert.Nil(err)
-	assert.Equal(int(401), httpStatusCode)
-
 	//-------------------case 3 :  check admin project role------------------------//
 	httpStatusCode, result, err = apiTest.ProjectsGet(
 		&apilib.ProjectQuery{
 			Name:   addProject.ProjectName,
 			Owner:  admin.Name,
 			Public: true,
-			Member: admin.Name,
-			Role:   1,
 		}, *admin)
 	if err != nil {
 		t.Error("Error while search project by proName and isPublic", err.Error())
@@ -155,9 +143,7 @@ func TestListProjects(t *testing.T) {
 	}
 	httpStatusCode, result, err = apiTest.ProjectsGet(
 		&apilib.ProjectQuery{
-			Name:   addProject.ProjectName,
-			Member: TestUserName,
-			Role:   2,
+			Name: addProject.ProjectName,
 		}, *testUser)
 	if err != nil {
 		t.Error("Error while search project by proName and isPublic", err.Error())

From 421e9b114e9923ccc9ea066ac29d905c1e30c3a1 Mon Sep 17 00:00:00 2001
From: Wenkai Yin <yinw@vmware.com>
Date: Wed, 14 Jun 2017 20:35:51 +0800
Subject: [PATCH 4/5] update

---
 src/common/dao/project.go | 15 ++++++---------
 1 file changed, 6 insertions(+), 9 deletions(-)

diff --git a/src/common/dao/project.go b/src/common/dao/project.go
index d3cb4ae34..aa51cb353 100644
--- a/src/common/dao/project.go
+++ b/src/common/dao/project.go
@@ -275,7 +275,11 @@ func projectQueryConditions(owner, name string, public *bool, member string,
 	// 3. public projects and projects which the user is a member of
 	collection := `project `
 	if len(base) != 0 && base[0] != nil {
-		if len(base[0].Member) > 0 {
+		if len(base[0].Member) == 0 && base[0].Public {
+			collection = `(select * from project pr
+					where pr.public=1) `
+		}
+		if len(base[0].Member) > 0 && base[0].Public {
 			collection = `(select pr.project_id, pr.owner_id, pr.name, pr.
 						creation_time, pr.update_time, pr.deleted, pr.public 
 					from project pr
@@ -283,15 +287,8 @@ func projectQueryConditions(owner, name string, public *bool, member string,
 						on pr.project_id = prm.project_id
 					join user ur
 						on prm.user_id=ur.user_id
-					where ur.username=? `
-			if base[0].Public {
-				collection += ` or pr.public=1 `
-			}
-			collection += `) `
+					where ur.username=?  or pr.public=1 )`
 			params = append(params, base[0].Member)
-		} else if base[0].Public {
-			collection = `(select * from project pr
-					where pr.public=1) `
 		}
 	}
 

From fa0dec6c9ce40ac879dc84e7bd2c03b9ec0d0834 Mon Sep 17 00:00:00 2001
From: Wenkai Yin <yinw@vmware.com>
Date: Wed, 14 Jun 2017 20:48:43 +0800
Subject: [PATCH 5/5] update

---
 src/ui/projectmanager/pms/pm.go | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/src/ui/projectmanager/pms/pm.go b/src/ui/projectmanager/pms/pm.go
index d5db15496..16b6692fb 100644
--- a/src/ui/projectmanager/pms/pm.go
+++ b/src/ui/projectmanager/pms/pm.go
@@ -371,12 +371,12 @@ func (p *ProjectManager) Update(projectIDOrName interface{}, project *models.Pro
 }
 
 // GetAll ...
-func (p *ProjectManager) GetAll(query *models.ProjectQueryParam) ([]*models.Project, error) {
+func (p *ProjectManager) GetAll(query *models.ProjectQueryParam, base ...*models.BaseProjectCollection) ([]*models.Project, error) {
 	return nil, errors.New("get all projects is unsupported")
 }
 
 // GetTotal ...
-func (p *ProjectManager) GetTotal(query *models.ProjectQueryParam) (int64, error) {
+func (p *ProjectManager) GetTotal(query *models.ProjectQueryParam, base ...*models.BaseProjectCollection) (int64, error) {
 	return 0, errors.New("get total of projects is unsupported")
 }