refactor search API to return more info

This commit is contained in:
Wenkai Yin 2017-03-06 17:12:09 +08:00
parent fb77c1a7e3
commit d6a6f67596
7 changed files with 110 additions and 53 deletions

View File

@ -1483,26 +1483,12 @@ definitions:
description: Search results of the projects that matched the filter keywords.
type: array
items:
$ref: '#/definitions/SearchProject'
$ref: '#/definitions/Project'
repositories:
description: Search results of the repositories that matched the filter keywords.
type: array
items:
$ref: '#/definitions/SearchRepository'
SearchProject:
type: object
properties:
id:
type: integer
format: int64
description: The ID of project
name:
type: string
description: The name of the project
public:
type: integer
format: int
description: The flag to indicate the publicity of the project (1 is public, 0 is non-public)
SearchRepository:
type: object
properties:
@ -1518,6 +1504,12 @@ definitions:
repository_name:
type: string
description: The name of the repository
pull_count:
type: integer
description: The count how many times the repository is pulled
tags_count:
type: integer
description: The count of tags in the repository
ProjectReq:
type: object
properties:

View File

@ -168,10 +168,15 @@ func ToggleProjectPublicity(projectID int64, publicity int) error {
// 2. the prject is public or the user is a member of the project
func SearchProjects(userID int) ([]models.Project, error) {
o := GetOrmer()
sql := `select distinct p.project_id, p.name, p.public
sql :=
`select distinct p.project_id, p.name, p.public,
p.owner_id, p.creation_time, p.update_time, pm.role role
from project p
left join project_member pm on p.project_id = pm.project_id
where (pm.user_id = ? or p.public = 1) and p.deleted = 0`
left join project_member pm
on p.project_id = pm.project_id
where (pm.user_id = ? or p.public = 1)
and p.deleted = 0 `
var projects []models.Project

View File

@ -50,7 +50,8 @@ func GetRepositoryByName(name string) (*models.RepoRecord, error) {
func GetAllRepositories() ([]models.RepoRecord, error) {
o := GetOrmer()
var repos []models.RepoRecord
_, err := o.QueryTable("repository").All(&repos)
_, err := o.QueryTable("repository").
OrderBy("Name").All(&repos)
return repos, err
}

View File

@ -234,36 +234,46 @@ func (ra *RepositoryAPI) GetTags() {
}
}
rc, err := ra.initRepositoryClient(repoName)
client, err := ra.initRepositoryClient(repoName)
if err != nil {
log.Errorf("error occurred while initializing repository client for %s: %v", repoName, err)
ra.CustomAbort(http.StatusInternalServerError, "internal error")
}
tags := []string{}
ts, err := rc.ListTag()
tags, err := listTag(client)
if err != nil {
regErr, ok := err.(*registry_error.Error)
if !ok {
log.Errorf("error occurred while listing tags of %s: %v", repoName, err)
ra.CustomAbort(http.StatusInternalServerError, "internal error")
}
ra.CustomAbort(regErr.StatusCode, regErr.Detail)
}
ra.Data["json"] = tags
ra.ServeJSON()
}
func listTag(client *registry.Repository) ([]string, error) {
tags := []string{}
ts, err := client.ListTag()
if err != nil {
// TODO remove the logic if the bug of registry is fixed
// It's a workaround for a bug of registry: when listing tags of
// a repository which is being pushed, a "NAME_UNKNOWN" error will
// been returned, while the catalog API can list this repository.
if regErr.StatusCode != http.StatusNotFound {
ra.CustomAbort(regErr.StatusCode, regErr.Detail)
if regErr, ok := err.(*registry_error.Error); ok &&
regErr.StatusCode == http.StatusNotFound {
return tags, nil
}
}
tags = append(tags, ts...)
sort.Strings(tags)
ra.Data["json"] = tags
ra.ServeJSON()
return tags, nil
}
// GetManifests handles GET /api/repositories/manifests

View File

@ -20,12 +20,13 @@ import (
"sort"
"strings"
"github.com/vmware/harbor/src/common/api"
"github.com/vmware/harbor/src/common/dao"
"github.com/vmware/harbor/src/common/models"
"github.com/vmware/harbor/src/ui/service/cache"
"github.com/vmware/harbor/src/common/utils"
"github.com/vmware/harbor/src/common/utils/log"
"github.com/vmware/harbor/src/common/api"
"github.com/vmware/harbor/src/ui/config"
"github.com/vmware/harbor/src/ui/service/cache"
)
// SearchAPI handles requesst to /api/search
@ -34,7 +35,7 @@ type SearchAPI struct {
}
type searchResult struct {
Project []map[string]interface{} `json:"project"`
Project []models.Project `json:"project"`
Repository []map[string]interface{} `json:"repository"`
}
@ -71,58 +72,104 @@ func (s *SearchAPI) Get() {
projectSorter := &models.ProjectSorter{Projects: projects}
sort.Sort(projectSorter)
projectResult := []map[string]interface{}{}
projectResult := []models.Project{}
for _, p := range projects {
match := true
if len(keyword) > 0 && !strings.Contains(p.Name, keyword) {
match = false
}
if match {
entry := make(map[string]interface{})
entry["id"] = p.ProjectID
entry["name"] = p.Name
entry["public"] = p.Public
projectResult = append(projectResult, entry)
if userID != dao.NonExistUserID {
if isSysAdmin {
p.Role = models.PROJECTADMIN
}
if p.Role == models.PROJECTADMIN {
p.Togglable = true
}
}
repos, err := dao.GetRepositoryByProjectName(p.Name)
if err != nil {
log.Errorf("failed to get repositories of project %s: %v", p.Name, err)
s.CustomAbort(http.StatusInternalServerError, "")
}
p.RepoCount = len(repos)
projectResult = append(projectResult, p)
}
}
repositories, err := cache.GetRepoFromCache()
repositoryResult, err := filterRepositories(projects, keyword)
if err != nil {
log.Errorf("failed to list repositories: %v", err)
log.Errorf("failed to filter repositories: %v", err)
s.CustomAbort(http.StatusInternalServerError, "")
}
sort.Strings(repositories)
repositoryResult := filterRepositories(repositories, projects, keyword)
result := &searchResult{Project: projectResult, Repository: repositoryResult}
s.Data["json"] = result
s.ServeJSON()
}
func filterRepositories(repositories []string, projects []models.Project, keyword string) []map[string]interface{} {
func filterRepositories(projects []models.Project, keyword string) (
[]map[string]interface{}, error) {
repositories, err := dao.GetAllRepositories()
if err != nil {
return nil, err
}
i, j := 0, 0
result := []map[string]interface{}{}
for i < len(repositories) && j < len(projects) {
r := repositories[i]
p, _ := utils.ParseRepository(r)
p, _ := utils.ParseRepository(r.Name)
d := strings.Compare(p, projects[j].Name)
if d < 0 {
i++
continue
} else if d == 0 {
i++
if len(keyword) != 0 && !strings.Contains(r, keyword) {
if len(keyword) != 0 && !strings.Contains(r.Name, keyword) {
continue
}
entry := make(map[string]interface{})
entry["repository_name"] = r
entry["repository_name"] = r.Name
entry["project_name"] = projects[j].Name
entry["project_id"] = projects[j].ProjectID
entry["project_public"] = projects[j].Public
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++
}
}
return result
return result, nil
}
func getTags(repository string) ([]string, error) {
url, err := config.RegistryURL()
if err != nil {
return nil, err
}
client, err := cache.NewRepositoryClient(url, true,
"admin", repository, "repository", repository, "pull")
if err != nil {
return nil, err
}
tags, err := listTag(client)
if err != nil {
return nil, err
}
return tags, nil
}

View File

@ -22,9 +22,9 @@ func TestSearch(t *testing.T) {
t.Log(err)
} else {
assert.Equal(int(200), httpStatusCode, "httpStatusCode should be 200")
assert.Equal(int64(1), result.Projects[0].Id, "Project id should be equal")
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.Equal(int32(1), result.Projects[0].Public, "Project public status should be 1 (true)")
assert.Equal(1, result.Projects[0].Public, "Project public status should be 1 (true)")
}
//--------case 2 : Response Code = 200, sysAdmin and search repo--------//

View File

@ -1,10 +1,10 @@
/*
/*
* Harbor API
*
* These APIs provide services for manipulating Harbor project.
*
* OpenAPI spec version: 0.3.0
*
*
* Generated by: https://github.com/swagger-api/swagger-codegen.git
*
* Licensed under the Apache License, Version 2.0 (the "License");
@ -22,12 +22,14 @@
package apilib
import()
import (
"github.com/vmware/harbor/src/common/models"
)
type Search struct {
// Search results of the projects that matched the filter keywords.
Projects []SearchProject `json:"project,omitempty"`
Projects []models.Project `json:"project,omitempty"`
// Search results of the repositories that matched the filter keywords.
Repositories []SearchRepository `json:"repository,omitempty"`