Merge pull request #5194 from ywk253100/180626_search

Fix bug in search API
This commit is contained in:
Daniel Jiang 2018-06-29 21:13:09 +08:00 committed by GitHub
commit 1afb09b6cf
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
3 changed files with 169 additions and 89 deletions

View File

@ -105,26 +105,6 @@ func isTrue(value string) bool {
strings.ToLower(value) == "1"
}
// ProjectSorter holds an array of projects
type ProjectSorter struct {
Projects []*Project
}
// Len returns the length of array in ProjectSorter
func (ps *ProjectSorter) Len() int {
return len(ps.Projects)
}
// Less defines the comparison rules of project
func (ps *ProjectSorter) Less(i, j int) bool {
return ps.Projects[i].Name < ps.Projects[j].Name
}
// Swap swaps the position of i and j
func (ps *ProjectSorter) Swap(i, j int) {
ps.Projects[i], ps.Projects[j] = ps.Projects[j], ps.Projects[i]
}
// ProjectQueryParam can be used to set query parameters when listing projects.
// The query condition will be set in the query if its corresponding field
// is not nil. Leave it empty if you don't want to apply this condition.

View File

@ -17,7 +17,6 @@ package api
import (
"fmt"
"net/http"
"sort"
"strings"
"github.com/vmware/harbor/src/common"
@ -80,8 +79,6 @@ func (s *SearchAPI) Get() {
}
}
projectSorter := &models.ProjectSorter{Projects: projects}
sort.Sort(projectSorter)
projectResult := []*models.Project{}
for _, p := range projects {
if len(keyword) > 0 && !strings.Contains(p.Name, keyword) {
@ -125,43 +122,46 @@ func (s *SearchAPI) Get() {
func filterRepositories(projects []*models.Project, keyword string) (
[]map[string]interface{}, error) {
result := []map[string]interface{}{}
if len(projects) == 0 {
return result, nil
}
repositories, err := dao.GetRepositories()
repositories, err := dao.GetRepositories(&models.RepositoryQuery{
Name: keyword,
})
if err != nil {
return nil, err
}
if len(repositories) == 0 {
return result, nil
}
i, j := 0, 0
result := []map[string]interface{}{}
for i < len(repositories) && j < len(projects) {
r := repositories[i]
p, _ := utils.ParseRepository(r.Name)
d := strings.Compare(p, projects[j].Name)
if d < 0 {
i++
projectMap := map[string]*models.Project{}
for _, project := range projects {
projectMap[project.Name] = project
}
for _, repository := range repositories {
projectName, _ := utils.ParseRepository(repository.Name)
project, exist := projectMap[projectName]
if !exist {
continue
} else if d == 0 {
i++
if len(keyword) != 0 && !strings.Contains(r.Name, keyword) {
continue
}
entry := make(map[string]interface{})
entry["repository_name"] = r.Name
entry["project_name"] = projects[j].Name
entry["project_id"] = projects[j].ProjectID
entry["project_public"] = projects[j].IsPublic()
entry["pull_count"] = r.PullCount
tags, err := getTags(r.Name)
if err != nil {
return nil, err
}
entry["tags_count"] = len(tags)
result = append(result, entry)
} else {
j++
}
entry := make(map[string]interface{})
entry["repository_name"] = repository.Name
entry["project_name"] = project.Name
entry["project_id"] = project.ProjectID
entry["project_public"] = project.IsPublic()
entry["pull_count"] = repository.PullCount
tags, err := getTags(repository.Name)
if err != nil {
return nil, err
}
entry["tags_count"] = len(tags)
result = append(result, entry)
}
return result, nil
}

View File

@ -15,53 +15,153 @@ package api
import (
"fmt"
"net/http"
"testing"
"github.com/vmware/harbor/src/common"
"github.com/vmware/harbor/src/common/models"
"github.com/stretchr/testify/assert"
"github.com/vmware/harbor/tests/apitests/apilib"
"github.com/stretchr/testify/require"
"github.com/vmware/harbor/src/common/dao"
member "github.com/vmware/harbor/src/common/dao/project"
)
func TestSearch(t *testing.T) {
fmt.Println("Testing Search(SearchGet) API")
assert := assert.New(t)
// create a public project named "search"
projectID1, err := dao.AddProject(models.Project{
Name: "search",
OwnerID: int(nonSysAdminID),
})
require.Nil(t, err)
defer dao.DeleteProject(projectID1)
apiTest := newHarborAPI()
var result apilib.Search
err = dao.AddProjectMetadata(&models.ProjectMetadata{
ProjectID: projectID1,
Name: "public",
Value: "true",
})
require.Nil(t, err)
//-------------case 1 : Response Code = 200, Not sysAdmin --------------//
httpStatusCode, result, err := apiTest.SearchGet("library")
if err != nil {
t.Error("Error while search project or repository", err.Error())
t.Log(err)
} else {
assert.Equal(int(200), httpStatusCode, "httpStatusCode should be 200")
assert.Equal(int64(1), result.Projects[0].ProjectID, "Project id should be equal")
assert.Equal("library", result.Projects[0].Name, "Project name should be library")
assert.True(result.Projects[0].IsPublic(), "Project public status should be 1 (true)")
memberID1, err := member.AddProjectMember(models.Member{
ProjectID: projectID1,
EntityID: int(nonSysAdminID),
EntityType: common.UserMember,
Role: models.GUEST,
})
require.Nil(t, err)
defer member.DeleteProjectMemberByID(memberID1)
// create a private project named "search-2", the "-" is necessary
// in the project name to test some corner cases
projectID2, err := dao.AddProject(models.Project{
Name: "search-2",
OwnerID: int(nonSysAdminID),
})
require.Nil(t, err)
defer dao.DeleteProject(projectID2)
memberID2, err := member.AddProjectMember(models.Member{
ProjectID: projectID2,
EntityID: int(nonSysAdminID),
EntityType: common.UserMember,
Role: models.GUEST,
})
require.Nil(t, err)
defer member.DeleteProjectMemberByID(memberID2)
// add a repository in project "search"
err = dao.AddRepository(models.RepoRecord{
ProjectID: projectID1,
Name: "search/hello-world",
})
require.Nil(t, err)
// add a repository in project "search-2"
err = dao.AddRepository(models.RepoRecord{
ProjectID: projectID2,
Name: "search-2/hello-world",
})
require.Nil(t, err)
// search without login
result := &searchResult{}
err = handleAndParse(&testingRequest{
method: http.MethodGet,
url: "/api/search",
queryStruct: struct {
Keyword string `url:"q"`
}{
Keyword: "search",
},
}, result)
require.Nil(t, err)
require.Equal(t, 1, len(result.Project))
require.Equal(t, 1, len(result.Repository))
assert.Equal(t, "search", result.Project[0].Name)
assert.Equal(t, "search/hello-world", result.Repository[0]["repository_name"].(string))
// search with user who is the member of the project
err = handleAndParse(&testingRequest{
method: http.MethodGet,
url: "/api/search",
queryStruct: struct {
Keyword string `url:"q"`
}{
Keyword: "search",
},
credential: nonSysAdmin,
}, result)
require.Nil(t, err)
require.Equal(t, 2, len(result.Project))
require.Equal(t, 2, len(result.Repository))
projects := map[string]struct{}{}
repositories := map[string]struct{}{}
for _, project := range result.Project {
projects[project.Name] = struct{}{}
}
for _, repository := range result.Repository {
repositories[repository["repository_name"].(string)] = struct{}{}
}
//--------case 2 : Response Code = 200, sysAdmin and search repo--------//
httpStatusCode, result, err = apiTest.SearchGet("library", *admin)
if err != nil {
t.Error("Error while search project or repository", err.Error())
t.Log(err)
} else {
assert.Equal(int(200), httpStatusCode, "httpStatusCode should be 200")
assert.Equal("library", result.Repositories[0].ProjectName, "Project name should be library")
assert.Equal("library/busybox", result.Repositories[0].RepositoryName, "Repository name should be library/busybox")
assert.True(result.Repositories[0].ProjectPublic, "Project public status should be 1 (true)")
}
_, exist := projects["search"]
assert.True(t, exist)
_, exist = projects["search-2"]
assert.True(t, exist)
_, exist = repositories["search/hello-world"]
assert.True(t, exist)
_, exist = repositories["search-2/hello-world"]
assert.True(t, exist)
//--------case 3 : Response Code = 200, normal user and search repo--------//
httpStatusCode, result, err = apiTest.SearchGet("library", *testUser)
if err != nil {
t.Error("Error while search project or repository", err.Error())
t.Log(err)
} else {
assert.Equal(int(200), httpStatusCode, "httpStatusCode should be 200")
assert.Equal("library", result.Repositories[0].ProjectName, "Project name should be library")
assert.Equal("library/busybox", result.Repositories[0].RepositoryName, "Repository name should be library/busybox")
assert.True(result.Repositories[0].ProjectPublic, "Project public status should be 1 (true)")
// search with system admin
err = handleAndParse(&testingRequest{
method: http.MethodGet,
url: "/api/search",
queryStruct: struct {
Keyword string `url:"q"`
}{
Keyword: "search",
},
credential: sysAdmin,
}, result)
require.Nil(t, err)
require.Equal(t, 2, len(result.Project))
require.Equal(t, 2, len(result.Repository))
projects = map[string]struct{}{}
repositories = map[string]struct{}{}
for _, project := range result.Project {
projects[project.Name] = struct{}{}
}
for _, repository := range result.Repository {
repositories[repository["repository_name"].(string)] = struct{}{}
}
_, exist = projects["search"]
assert.True(t, exist)
_, exist = projects["search-2"]
assert.True(t, exist)
_, exist = repositories["search/hello-world"]
assert.True(t, exist)
_, exist = repositories["search-2/hello-world"]
assert.True(t, exist)
}