diff --git a/docs/swagger.yaml b/docs/swagger.yaml index 8336b174d4..51a4b464f0 100644 --- a/docs/swagger.yaml +++ b/docs/swagger.yaml @@ -906,6 +906,11 @@ paths: type: string required: false description: Repo name for filtering results. + - name: label_id + in: query + type: integer + required: false + description: The ID of label used to filter the result. - name: page in: query type: integer @@ -1144,6 +1149,11 @@ paths: type: string required: true description: Relevant repository name. + - name: label_ids + in: query + type: string + required: false + description: A list of comma separated label IDs. tags: - Products responses: diff --git a/make/photon/db/registry.sql b/make/photon/db/registry.sql index 02e96d6637..111ca6232d 100644 --- a/make/photon/db/registry.sql +++ b/make/photon/db/registry.sql @@ -277,8 +277,9 @@ create table harbor_resource_label ( label_id int NOT NULL, # the resource_id is the ID of project when the resource_type is p # the resource_id is the ID of repository when the resource_type is r -# the resource_id is the name of image when the resource_type is i - resource_id varchar(256) NOT NULL, + resource_id int, +# the resource_name is the name of image when the resource_type is i + resource_name varchar(256), # 'p' for project # 'r' for repository # 'i' for image diff --git a/make/photon/db/registry_sqlite.sql b/make/photon/db/registry_sqlite.sql index 5d045edb08..82d0be64c3 100644 --- a/make/photon/db/registry_sqlite.sql +++ b/make/photon/db/registry_sqlite.sql @@ -267,9 +267,12 @@ create table harbor_resource_label ( /* the resource_id is the ID of project when the resource_type is p the resource_id is the ID of repository when the resource_type is r - the resource_id is the name of image when the resource_type is i */ - resource_id varchar(256) NOT NULL, + resource_id int, +/* + the resource_name is the name of image when the resource_type is i +*/ + resource_name varchar(256), /* 'p' for project 'r' for repository diff --git a/src/common/dao/repository.go b/src/common/dao/repository.go index ee95334170..1fca529797 100644 --- a/src/common/dao/repository.go +++ b/src/common/dao/repository.go @@ -47,15 +47,6 @@ func GetRepositoryByName(name string) (*models.RepoRecord, error) { return &r, err } -// GetAllRepositories ... -func GetAllRepositories() ([]*models.RepoRecord, error) { - o := GetOrmer() - var repos []*models.RepoRecord - _, err := o.QueryTable("repository").Limit(-1). - OrderBy("Name").All(&repos) - return repos, err -} - // DeleteRepository ... func DeleteRepository(name string) error { o := GetOrmer() @@ -94,18 +85,6 @@ func RepositoryExists(name string) bool { return o.QueryTable("repository").Filter("name", name).Exist() } -// GetRepositoryByProjectName ... -func GetRepositoryByProjectName(name string) ([]*models.RepoRecord, error) { - sql := `select * from repository - where project_id = ( - select project_id from project - where name = ? - )` - repos := []*models.RepoRecord{} - _, err := GetOrmer().Raw(sql, name).QueryRows(&repos) - return repos, err -} - //GetTopRepos returns the most popular repositories whose project ID is // in projectIDs func GetTopRepos(projectIDs []int64, n int) ([]*models.RepoRecord, error) { @@ -124,46 +103,78 @@ func GetTopRepos(projectIDs []int64, n int) ([]*models.RepoRecord, error) { } // GetTotalOfRepositories ... -func GetTotalOfRepositories(name string) (int64, error) { - qs := GetOrmer().QueryTable(&models.RepoRecord{}) - if len(name) != 0 { - qs = qs.Filter("Name__contains", name) +func GetTotalOfRepositories(query ...*models.RepositoryQuery) (int64, error) { + sql, params := repositoryQueryConditions(query...) + sql = `select count(*) ` + sql + var total int64 + if err := GetOrmer().Raw(sql, params).QueryRow(&total); err != nil { + return 0, err } - return qs.Count() + return total, nil } -// GetTotalOfRepositoriesByProject ... -func GetTotalOfRepositoriesByProject(projectIDs []int64, name string) (int64, error) { - if len(projectIDs) == 0 { - return 0, nil - } - - qs := GetOrmer().QueryTable(&models.RepoRecord{}). - Filter("project_id__in", projectIDs) - - if len(name) != 0 { - qs = qs.Filter("Name__contains", name) - } - - return qs.Count() -} - -// GetRepositoriesByProject ... -func GetRepositoriesByProject(projectID int64, name string, - limit, offset int64) ([]*models.RepoRecord, error) { - +// GetRepositories ... +func GetRepositories(query ...*models.RepositoryQuery) ([]*models.RepoRecord, error) { repositories := []*models.RepoRecord{} - qs := GetOrmer().QueryTable(&models.RepoRecord{}). - Filter("ProjectID", projectID) - - if len(name) != 0 { - qs = qs.Filter("Name__contains", name) + sql, params := repositoryQueryConditions(query...) + sql = `select r.repository_id, r.name, r.project_id, r.description, r.pull_count, + r.star_count, r.creation_time, r.update_time ` + sql + `order by r.name ` + if len(query) > 0 && query[0] != nil { + page, size := query[0].Page, query[0].Size + if size > 0 { + sql += `limit ? ` + params = append(params, size) + if page > 0 { + sql += `offset ? ` + params = append(params, size*(page-1)) + } + } } - if limit > 0 { - qs = qs.Limit(limit).Offset(offset) - } - _, err := qs.All(&repositories) - return repositories, err + if _, err := GetOrmer().Raw(sql, params).QueryRows(&repositories); err != nil { + return nil, err + } + return repositories, nil +} + +func repositoryQueryConditions(query ...*models.RepositoryQuery) (string, []interface{}) { + params := []interface{}{} + sql := `from repository r ` + if len(query) == 0 || query[0] == nil { + return sql, params + } + q := query[0] + if len(q.ProjectName) > 0 { + sql += `join project p on r.project_id = p.project_id ` + } + + if q.LabelID > 0 { + sql += `join harbor_resource_label rl on r.repository_id = rl.resource_id + and rl.resource_type = 'r' ` + } + sql += `where 1=1 ` + + if len(q.Name) > 0 { + sql += `and r.name = ? ` + params = append(params, q.Name) + } + + if len(q.ProjectIDs) > 0 { + sql += fmt.Sprintf(`and r.project_id in ( %s ) `, + paramPlaceholder(len(q.ProjectIDs))) + params = append(params, q.ProjectIDs) + } + + if len(q.ProjectName) > 0 { + sql += `and p.name = ? ` + params = append(params, q.ProjectName) + } + + if q.LabelID > 0 { + sql += `and rl.label_id = ? ` + params = append(params, q.LabelID) + } + + return sql, params } diff --git a/src/common/dao/repository_test.go b/src/common/dao/repository_test.go index 78379d85e2..ac293d6f8c 100644 --- a/src/common/dao/repository_test.go +++ b/src/common/dao/repository_test.go @@ -16,10 +16,11 @@ package dao import ( "fmt" - "strconv" "testing" + "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" + "github.com/vmware/harbor/src/common" "github.com/vmware/harbor/src/common/models" ) @@ -32,61 +33,93 @@ var ( } ) -func TestGetRepositoryByProjectName(t *testing.T) { - if err := addRepository(repository); err != nil { - t.Fatalf("failed to add repository %s: %v", name, err) - } - defer func() { - if err := deleteRepository(name); err != nil { - t.Fatalf("failed to delete repository %s: %v", name, err) - } - }() +func TestGetTotalOfRepositories(t *testing.T) { + total, err := GetTotalOfRepositories() + require.Nil(t, err) - repositories, err := GetRepositoryByProjectName(project) - if err != nil { - t.Fatalf("failed to get repositories of project %s: %v", - project, err) - } + err = addRepository(repository) + require.Nil(t, err) + defer deleteRepository(name) - if len(repositories) == 0 { - t.Fatal("unexpected length of repositories: 0, at least 1") - } + n, err := GetTotalOfRepositories() + require.Nil(t, err) + assert.Equal(t, total+1, n) +} - exist := false - for _, repo := range repositories { - if repo.Name == name { - exist = true +func TestGetRepositories(t *testing.T) { + // no query + repositories, err := GetRepositories() + require.Nil(t, err) + n := len(repositories) + + err = addRepository(repository) + require.Nil(t, err) + defer deleteRepository(name) + + repositories, err = GetRepositories() + require.Nil(t, err) + assert.Equal(t, n+1, len(repositories)) + + // query by name + repositories, err = GetRepositories(&models.RepositoryQuery{ + Name: name, + }) + require.Nil(t, err) + require.Equal(t, 1, len(repositories)) + assert.Equal(t, name, repositories[0].Name) + + // query by project name + repositories, err = GetRepositories(&models.RepositoryQuery{ + ProjectName: project, + }) + require.Nil(t, err) + found := false + for _, repository := range repositories { + if repository.Name == name { + found = true break } } - if !exist { - t.Errorf("there is no repository whose name is %s", name) - } -} + assert.True(t, found) -func TestGetTotalOfRepositories(t *testing.T) { - total, err := GetTotalOfRepositories("") - if err != nil { - t.Fatalf("failed to get total of repositoreis: %v", err) - } - - if err := addRepository(repository); err != nil { - t.Fatalf("failed to add repository %s: %v", name, err) - } - defer func() { - if err := deleteRepository(name); err != nil { - t.Fatalf("failed to delete repository %s: %v", name, err) + // query by project ID + repositories, err = GetRepositories(&models.RepositoryQuery{ + ProjectIDs: []int64{1}, + }) + require.Nil(t, err) + found = false + for _, repository := range repositories { + if repository.Name == name { + found = true + break } - }() - - n, err := GetTotalOfRepositories("") - if err != nil { - t.Fatalf("failed to get total of repositoreis: %v", err) } + assert.True(t, found) - if n != total+1 { - t.Errorf("unexpected total: %d != %d", n, total+1) - } + // query by label ID + labelID, err := AddLabel(&models.Label{ + Name: "label_for_test", + }) + require.Nil(t, err) + defer DeleteLabel(labelID) + + r, err := GetRepositoryByName(name) + require.Nil(t, err) + + rlID, err := AddResourceLabel(&models.ResourceLabel{ + LabelID: labelID, + ResourceID: r.RepositoryID, + ResourceType: common.ResourceTypeRepository, + }) + require.Nil(t, err) + defer DeleteResourceLabel(rlID) + + repositories, err = GetRepositories(&models.RepositoryQuery{ + LabelID: labelID, + }) + require.Nil(t, err) + require.Equal(t, 1, len(repositories)) + assert.Equal(t, name, repositories[0].Name) } func TestGetTopRepos(t *testing.T) { @@ -149,112 +182,6 @@ func TestGetTopRepos(t *testing.T) { require.Equal(topRepos[0].Name, repository3.Name) } -func TestGetTotalOfRepositoriesByProject(t *testing.T) { - var projectID int64 = 1 - repoName := "library/total_count" - - total, err := GetTotalOfRepositoriesByProject([]int64{projectID}, repoName) - if err != nil { - t.Errorf("failed to get total of repositoreis of project %d: %v", projectID, err) - return - } - - if err := addRepository(&models.RepoRecord{ - Name: repoName, - ProjectID: projectID, - }); err != nil { - t.Errorf("failed to add repository %s: %v", repoName, err) - return - } - defer func() { - if err := deleteRepository(repoName); err != nil { - t.Errorf("failed to delete repository %s: %v", name, err) - return - } - }() - - n, err := GetTotalOfRepositoriesByProject([]int64{projectID}, repoName) - if err != nil { - t.Errorf("failed to get total of repositoreis of project %d: %v", projectID, err) - return - } - - if n != total+1 { - t.Errorf("unexpected total: %d != %d", n, total+1) - } -} - -func TestGetRepositoriesByProject(t *testing.T) { - var projectID int64 = 1 - repoName := "library/repository" - - if err := addRepository(&models.RepoRecord{ - Name: repoName, - ProjectID: projectID, - }); err != nil { - t.Errorf("failed to add repository %s: %v", repoName, err) - return - } - defer func() { - if err := deleteRepository(repoName); err != nil { - t.Errorf("failed to delete repository %s: %v", name, err) - return - } - }() - - repositories, err := GetRepositoriesByProject(projectID, repoName, 10, 0) - if err != nil { - t.Errorf("failed to get repositoreis of project %d: %v", projectID, err) - return - } - - t.Log(repositories) - - for _, repository := range repositories { - if repository.Name == repoName { - return - } - } - - t.Errorf("repository %s not found", repoName) -} - -func TestGetAllRepositories(t *testing.T) { - require := require.New(t) - - var repos []*models.RepoRecord - repos, err := GetAllRepositories() - require.NoError(err) - allBefore := len(repos) - - project1 := models.Project{ - OwnerID: 1, - Name: "projectRepo", - } - var err2 error - project1.ProjectID, err2 = AddProject(project1) - require.NoError(err2) - - for i := 0; i < 1200; i++ { - end := strconv.Itoa(i) - repoRecord := models.RepoRecord{ - Name: "test" + end, - ProjectID: project1.ProjectID, - } - err := AddRepository(repoRecord) - require.NoError(err) - } - - repos, err = GetAllRepositories() - require.NoError(err) - allAfter := len(repos) - - require.Equal(allAfter, allBefore+1200) - - err = clearRepositoryData() - require.NoError(err) -} - func addRepository(repository *models.RepoRecord) error { return AddRepository(*repository) } diff --git a/src/common/dao/resource_label.go b/src/common/dao/resource_label.go index f89bb27a56..56b17916b7 100644 --- a/src/common/dao/resource_label.go +++ b/src/common/dao/resource_label.go @@ -29,14 +29,26 @@ func AddResourceLabel(rl *models.ResourceLabel) (int64, error) { return GetOrmer().Insert(rl) } -// GetResourceLabel specified by ID -func GetResourceLabel(rType, rID string, labelID int64) (*models.ResourceLabel, error) { +// GetResourceLabel specified by resource ID or name +// Get the ResourceLabel by ResourceID if rIDOrName is int +// Get the ResourceLabel by ResourceName if rIDOrName is string +func GetResourceLabel(rType string, rIDOrName interface{}, labelID int64) (*models.ResourceLabel, error) { rl := &models.ResourceLabel{ ResourceType: rType, - ResourceID: rID, LabelID: labelID, } - if err := GetOrmer().Read(rl, "ResourceType", "ResourceID", "LabelID"); err != nil { + + var err error + id, ok := rIDOrName.(int64) + if ok { + rl.ResourceID = id + err = GetOrmer().Read(rl, "ResourceType", "ResourceID", "LabelID") + } else { + rl.ResourceName = rIDOrName.(string) + err = GetOrmer().Read(rl, "ResourceType", "ResourceName", "LabelID") + } + + if err != nil { if err == orm.ErrNoRows { return nil, nil } @@ -47,13 +59,20 @@ func GetResourceLabel(rType, rID string, labelID int64) (*models.ResourceLabel, } // GetLabelsOfResource returns the label list of the resource -func GetLabelsOfResource(rType, rID string) ([]*models.Label, error) { +// Get the labels by ResourceID if rIDOrName is int, or get the labels by ResourceName +func GetLabelsOfResource(rType string, rIDOrName interface{}) ([]*models.Label, error) { sql := `select l.id, l.name, l.description, l.color, l.scope, l.project_id, l.creation_time, l.update_time from harbor_resource_label rl join harbor_label l on rl.label_id=l.id - where rl.resource_type = ? and rl.resource_id = ?` + where rl.resource_type = ? and` + if _, ok := rIDOrName.(int64); ok { + sql += ` rl.resource_id = ?` + } else { + sql += ` rl.resource_name = ?` + } + labels := []*models.Label{} - _, err := GetOrmer().Raw(sql, rType, rID).QueryRows(&labels) + _, err := GetOrmer().Raw(sql, rType, rIDOrName).QueryRows(&labels) return labels, err } @@ -65,10 +84,39 @@ func DeleteResourceLabel(id int64) error { return err } -// DeleteLabelsOfResource removes all labels of resource specified by rType and rID -func DeleteLabelsOfResource(rType, rID string) error { - _, err := GetOrmer().QueryTable(&models.ResourceLabel{}). - Filter("ResourceType", rType). - Filter("ResourceID", rID).Delete() +// DeleteLabelsOfResource removes all labels of the resource +func DeleteLabelsOfResource(rType string, rIDOrName interface{}) error { + qs := GetOrmer().QueryTable(&models.ResourceLabel{}). + Filter("ResourceType", rType) + if _, ok := rIDOrName.(int64); ok { + qs = qs.Filter("ResourceID", rIDOrName) + } else { + qs = qs.Filter("ResourceName", rIDOrName) + } + _, err := qs.Delete() return err } + +// ListResourceLabels lists ResourceLabel according to the query conditions +func ListResourceLabels(query ...*models.ResourceLabelQuery) ([]*models.ResourceLabel, error) { + qs := GetOrmer().QueryTable(&models.ResourceLabel{}) + if len(query) > 0 { + q := query[0] + if q.LabelID > 0 { + qs = qs.Filter("LabelID", q.LabelID) + } + if len(q.ResourceType) > 0 { + qs = qs.Filter("ResourceType", q.ResourceType) + } + if q.ResourceID > 0 { + qs = qs.Filter("ResourceID", q.ResourceID) + } + if len(q.ResourceName) > 0 { + qs = qs.Filter("ResourceName", q.ResourceName) + } + } + + rls := []*models.ResourceLabel{} + _, err := qs.All(&rls) + return rls, err +} diff --git a/src/common/dao/resource_label_test.go b/src/common/dao/resource_label_test.go index 33259e1fd5..572bec8b5d 100644 --- a/src/common/dao/resource_label_test.go +++ b/src/common/dao/resource_label_test.go @@ -32,7 +32,7 @@ func TestMethodsOfResourceLabel(t *testing.T) { require.Nil(t, err) defer DeleteLabel(labelID) - resourceID := "1" + var resourceID int64 = 1 resourceType := common.ResourceTypeRepository // add @@ -56,6 +56,16 @@ func TestMethodsOfResourceLabel(t *testing.T) { require.Equal(t, 1, len(labels)) assert.Equal(t, id, r.ID) + // list + rls, err := ListResourceLabels(&models.ResourceLabelQuery{ + LabelID: labelID, + ResourceType: resourceType, + ResourceID: resourceID, + }) + require.Nil(t, err) + require.Equal(t, 1, len(rls)) + assert.Equal(t, id, rls[0].ID) + // delete err = DeleteResourceLabel(id) require.Nil(t, err) diff --git a/src/common/models/label.go b/src/common/models/label.go index 6d949b81ce..f725848321 100644 --- a/src/common/models/label.go +++ b/src/common/models/label.go @@ -69,7 +69,8 @@ func (l *Label) Valid(v *validation.Validation) { type ResourceLabel struct { ID int64 `orm:"pk;auto;column(id)"` LabelID int64 `orm:"column(label_id)"` - ResourceID string `orm:"column(resource_id)"` + ResourceID int64 `orm:"column(resource_id)"` + ResourceName string `orm:"column(resource_name)"` ResourceType string `orm:"column(resource_type)"` CreationTime time.Time `orm:"column(creation_time)"` UpdateTime time.Time `orm:"column(update_time)"` @@ -79,3 +80,11 @@ type ResourceLabel struct { func (r *ResourceLabel) TableName() string { return "harbor_resource_label" } + +// ResourceLabelQuery : query parameters for the mapping relationships of resource and label +type ResourceLabelQuery struct { + LabelID int64 + ResourceID int64 + ResourceName string + ResourceType string +} diff --git a/src/common/models/project.go b/src/common/models/project.go index f916f3c623..5b8fd4f6a4 100644 --- a/src/common/models/project.go +++ b/src/common/models/project.go @@ -33,7 +33,7 @@ type Project struct { OwnerName string `orm:"-" json:"owner_name"` Togglable bool `orm:"-" json:"togglable"` Role int `orm:"-" json:"current_user_role_id"` - RepoCount int `orm:"-" json:"repo_count"` + RepoCount int64 `orm:"-" json:"repo_count"` Metadata map[string]string `orm:"-" json:"metadata"` } diff --git a/src/common/models/repo.go b/src/common/models/repo.go index 5d5984ae76..9d1e36aebe 100644 --- a/src/common/models/repo.go +++ b/src/common/models/repo.go @@ -37,3 +37,12 @@ type RepoRecord struct { func (rp *RepoRecord) TableName() string { return RepoTable } + +// RepositoryQuery : query parameters for repository +type RepositoryQuery struct { + Name string + ProjectIDs []int64 + ProjectName string + LabelID int64 + Pagination +} diff --git a/src/replication/registry/harbor_adaptor.go b/src/replication/registry/harbor_adaptor.go index e28622f933..184689ca71 100644 --- a/src/replication/registry/harbor_adaptor.go +++ b/src/replication/registry/harbor_adaptor.go @@ -2,6 +2,7 @@ package registry import ( "github.com/vmware/harbor/src/common/dao" + common_models "github.com/vmware/harbor/src/common/models" "github.com/vmware/harbor/src/common/utils/log" "github.com/vmware/harbor/src/replication" "github.com/vmware/harbor/src/replication/models" @@ -30,7 +31,9 @@ func (ha *HarborAdaptor) GetNamespace(name string) models.Namespace { //GetRepositories is used to get all the repositories under the specified namespace func (ha *HarborAdaptor) GetRepositories(namespace string) []models.Repository { - repos, err := dao.GetRepositoryByProjectName(namespace) + repos, err := dao.GetRepositories(&common_models.RepositoryQuery{ + ProjectName: namespace, + }) if err != nil { log.Errorf("failed to get repositories under namespace %s: %v", namespace, err) return nil diff --git a/src/ui/api/project.go b/src/ui/api/project.go index 3c34bb2c21..a292ca9fec 100644 --- a/src/ui/api/project.go +++ b/src/ui/api/project.go @@ -270,7 +270,9 @@ func (p *ProjectAPI) Deletable() { } func deletable(projectID int64) (*deletableResp, error) { - count, err := dao.GetTotalOfRepositoriesByProject([]int64{projectID}, "") + count, err := dao.GetTotalOfRepositories(&models.RepositoryQuery{ + ProjectIDs: []int64{projectID}, + }) if err != nil { return nil, err } @@ -395,13 +397,15 @@ func (p *ProjectAPI) populateProperties(project *models.Project) { } } - repos, err := dao.GetRepositoryByProjectName(project.Name) + total, err := dao.GetTotalOfRepositories(&models.RepositoryQuery{ + ProjectIDs: []int64{project.ProjectID}, + }) if err != nil { - log.Errorf("failed to get repositories of project %s: %v", project.Name, err) + log.Errorf("failed to get total of repositories of project %d: %v", project.ProjectID, err) p.CustomAbort(http.StatusInternalServerError, "") } - project.RepoCount = len(repos) + project.RepoCount = total } // Put ... diff --git a/src/ui/api/repository.go b/src/ui/api/repository.go index b57786ff65..cff3f94448 100644 --- a/src/ui/api/repository.go +++ b/src/ui/api/repository.go @@ -96,6 +96,12 @@ func (ra *RepositoryAPI) Get() { return } + labelID, err := ra.GetInt64("label_id", 0) + if err != nil { + ra.HandleBadRequest(fmt.Sprintf("invalid label_id: %s", ra.GetString("label_id"))) + return + } + exist, err := ra.ProjectMgr.Exists(projectID) if err != nil { ra.ParseAndHandleError(fmt.Sprintf("failed to check the existence of project %d", @@ -117,33 +123,33 @@ func (ra *RepositoryAPI) Get() { return } - keyword := ra.GetString("q") + query := &models.RepositoryQuery{ + ProjectIDs: []int64{projectID}, + Name: ra.GetString("q"), + LabelID: labelID, + } + query.Page, query.Size = ra.GetPaginationParams() - total, err := dao.GetTotalOfRepositoriesByProject( - []int64{projectID}, keyword) + total, err := dao.GetTotalOfRepositories(query) if err != nil { ra.HandleInternalServerError(fmt.Sprintf("failed to get total of repositories of project %d: %v", projectID, err)) return } - page, pageSize := ra.GetPaginationParams() - - repositories, err := getRepositories(projectID, - keyword, pageSize, pageSize*(page-1)) + repositories, err := getRepositories(query) if err != nil { ra.HandleInternalServerError(fmt.Sprintf("failed to get repository: %v", err)) return } - ra.SetPaginationHeader(total, page, pageSize) + ra.SetPaginationHeader(total, query.Page, query.Size) ra.Data["json"] = repositories ra.ServeJSON() } -func getRepositories(projectID int64, keyword string, - limit, offset int64) ([]*repoResp, error) { - repositories, err := dao.GetRepositoriesByProject(projectID, keyword, limit, offset) +func getRepositories(query *models.RepositoryQuery) ([]*repoResp, error) { + repositories, err := dao.GetRepositories(query) if err != nil { return nil, err } @@ -171,8 +177,7 @@ func assembleRepos(repositories []*models.RepoRecord) ([]*repoResp, error) { } repo.TagsCount = int64(len(tags)) - labels, err := dao.GetLabelsOfResource(common.ResourceTypeRepository, - strconv.FormatInt(repository.RepositoryID, 10)) + labels, err := dao.GetLabelsOfResource(common.ResourceTypeRepository, repository.RepositoryID) if err != nil { log.Errorf("failed to get labels of repository %s: %v", repository.Name, err) } else { @@ -385,6 +390,11 @@ func (ra *RepositoryAPI) GetTag() { // GetTags returns tags of a repository func (ra *RepositoryAPI) GetTags() { repoName := ra.GetString(":splat") + labelID, err := ra.GetInt64("label_id", 0) + if err != nil { + ra.HandleBadRequest(fmt.Sprintf("invalid label_id: %s", ra.GetString("label_id"))) + return + } projectName, _ := utils.ParseRepository(repoName) exist, err := ra.ProjectMgr.Exists(projectName) @@ -420,7 +430,31 @@ func (ra *RepositoryAPI) GetTags() { return } - ra.Data["json"] = assembleTags(client, repoName, tags, ra.SecurityCtx.GetUsername()) + // filter tags by label ID + if labelID > 0 { + rls, err := dao.ListResourceLabels(&models.ResourceLabelQuery{ + LabelID: labelID, + ResourceType: common.ResourceTypeImage, + }) + if err != nil { + ra.HandleInternalServerError(fmt.Sprintf("failed to list resource labels: %v", err)) + return + } + labeledTags := map[string]struct{}{} + for _, rl := range rls { + labeledTags[strings.Split(rl.ResourceName, ":")[1]] = struct{}{} + } + ts := []string{} + for _, tag := range tags { + if _, ok := labeledTags[tag]; ok { + ts = append(ts, tag) + } + } + tags = ts + } + + ra.Data["json"] = assembleTags(client, repoName, tags, + ra.SecurityCtx.GetUsername()) ra.ServeJSON() } @@ -443,6 +477,15 @@ func assembleTags(client *registry.Repository, repository string, for _, t := range tags { item := &tagResp{} + // labels + image := fmt.Sprintf("%s:%s", repository, t) + labels, err := dao.GetLabelsOfResource(common.ResourceTypeImage, image) + if err != nil { + log.Errorf("failed to get labels of image %s: %v", image, err) + } else { + item.Labels = labels + } + // the detail information of tag tagDetail, err := getTagDetail(client, t) if err != nil { @@ -468,15 +511,6 @@ func assembleTags(client *registry.Repository, repository string, } } - // labels - image := fmt.Sprintf("%s:%s", repository, t) - labels, err := dao.GetLabelsOfResource(common.ResourceTypeImage, image) - if err != nil { - log.Errorf("failed to get labels of image %s: %v", image, err) - } else { - item.Labels = labels - } - result = append(result, item) } diff --git a/src/ui/api/repository_label.go b/src/ui/api/repository_label.go index 95e92cdfc3..756c78d9db 100644 --- a/src/ui/api/repository_label.go +++ b/src/ui/api/repository_label.go @@ -144,24 +144,20 @@ func (r *RepositoryLabelAPI) AddToImage() { rl := &models.ResourceLabel{ LabelID: r.label.ID, ResourceType: common.ResourceTypeImage, - ResourceID: fmt.Sprintf("%s:%s", r.repository.Name, r.tag), + ResourceName: fmt.Sprintf("%s:%s", r.repository.Name, r.tag), } r.addLabel(rl) } // RemoveFromImage removes the label from an image func (r *RepositoryLabelAPI) RemoveFromImage() { - rl := &models.ResourceLabel{ - LabelID: r.label.ID, - ResourceType: common.ResourceTypeImage, - ResourceID: fmt.Sprintf("%s:%s", r.repository.Name, r.tag), - } - r.removeLabel(rl) + r.removeLabel(common.ResourceTypeImage, + fmt.Sprintf("%s:%s", r.repository.Name, r.tag), r.label.ID) } // GetOfRepository returns labels of a repository func (r *RepositoryLabelAPI) GetOfRepository() { - r.getLabels(common.ResourceTypeRepository, strconv.FormatInt(r.repository.RepositoryID, 10)) + r.getLabels(common.ResourceTypeRepository, r.repository.RepositoryID) } // AddToRepository adds the label to a repository @@ -169,26 +165,21 @@ func (r *RepositoryLabelAPI) AddToRepository() { rl := &models.ResourceLabel{ LabelID: r.label.ID, ResourceType: common.ResourceTypeRepository, - ResourceID: strconv.FormatInt(r.repository.RepositoryID, 10), + ResourceID: r.repository.RepositoryID, } r.addLabel(rl) } // RemoveFromRepository removes the label from a repository func (r *RepositoryLabelAPI) RemoveFromRepository() { - rl := &models.ResourceLabel{ - LabelID: r.label.ID, - ResourceType: common.ResourceTypeRepository, - ResourceID: strconv.FormatInt(r.repository.RepositoryID, 10), - } - r.removeLabel(rl) + r.removeLabel(common.ResourceTypeRepository, r.repository.RepositoryID, r.label.ID) } -func (r *RepositoryLabelAPI) getLabels(rType, rID string) { - labels, err := dao.GetLabelsOfResource(rType, rID) +func (r *RepositoryLabelAPI) getLabels(rType string, rIDOrName interface{}) { + labels, err := dao.GetLabelsOfResource(rType, rIDOrName) if err != nil { - r.HandleInternalServerError(fmt.Sprintf("failed to get labels of resource %s %s: %v", - rType, rID, err)) + r.HandleInternalServerError(fmt.Sprintf("failed to get labels of resource %s %v: %v", + rType, rIDOrName, err)) return } r.Data["json"] = labels @@ -196,10 +187,16 @@ func (r *RepositoryLabelAPI) getLabels(rType, rID string) { } func (r *RepositoryLabelAPI) addLabel(rl *models.ResourceLabel) { - rlabel, err := dao.GetResourceLabel(rl.ResourceType, rl.ResourceID, rl.LabelID) + var rIDOrName interface{} + if rl.ResourceID != 0 { + rIDOrName = rl.ResourceID + } else { + rIDOrName = rl.ResourceName + } + rlabel, err := dao.GetResourceLabel(rl.ResourceType, rIDOrName, rl.LabelID) if err != nil { - r.HandleInternalServerError(fmt.Sprintf("failed to check the existence of label %d for resource %s %s: %v", - rl.LabelID, rl.ResourceType, rl.ResourceID, err)) + r.HandleInternalServerError(fmt.Sprintf("failed to check the existence of label %d for resource %s %v: %v", + rl.LabelID, rl.ResourceType, rIDOrName, err)) return } @@ -208,8 +205,8 @@ func (r *RepositoryLabelAPI) addLabel(rl *models.ResourceLabel) { return } if _, err := dao.AddResourceLabel(rl); err != nil { - r.HandleInternalServerError(fmt.Sprintf("failed to add label %d to resource %s %s: %v", - rl.LabelID, rl.ResourceType, rl.ResourceID, err)) + r.HandleInternalServerError(fmt.Sprintf("failed to add label %d to resource %s %v: %v", + rl.LabelID, rl.ResourceType, rIDOrName, err)) return } @@ -217,22 +214,22 @@ func (r *RepositoryLabelAPI) addLabel(rl *models.ResourceLabel) { r.Redirect(http.StatusOK, strconv.FormatInt(rl.LabelID, 10)) } -func (r *RepositoryLabelAPI) removeLabel(rl *models.ResourceLabel) { - rlabel, err := dao.GetResourceLabel(rl.ResourceType, rl.ResourceID, rl.LabelID) +func (r *RepositoryLabelAPI) removeLabel(rType string, rIDOrName interface{}, labelID int64) { + rl, err := dao.GetResourceLabel(rType, rIDOrName, labelID) if err != nil { - r.HandleInternalServerError(fmt.Sprintf("failed to check the existence of label %d for resource %s %s: %v", - rl.LabelID, rl.ResourceType, rl.ResourceID, err)) + r.HandleInternalServerError(fmt.Sprintf("failed to check the existence of label %d for resource %s %v: %v", + labelID, rType, rIDOrName, err)) return } - if rlabel == nil { + if rl == nil { r.HandleNotFound(fmt.Sprintf("label %d of resource %s %s not found", - rl.LabelID, rl.ResourceType, rl.ResourceID)) + labelID, rType, rIDOrName)) return } - if err = dao.DeleteResourceLabel(rlabel.ID); err != nil { + if err = dao.DeleteResourceLabel(rl.ID); err != nil { r.HandleInternalServerError(fmt.Sprintf("failed to delete resource label record %d: %v", - rlabel.ID, err)) + rl.ID, err)) return } } diff --git a/src/ui/api/repository_test.go b/src/ui/api/repository_test.go index 45d11db979..c0efc2c6cf 100644 --- a/src/ui/api/repository_test.go +++ b/src/ui/api/repository_test.go @@ -27,7 +27,7 @@ func TestGetRepos(t *testing.T) { assert := assert.New(t) apiTest := newHarborAPI() projectID := "1" - keyword := "hello-world" + keyword := "library/hello-world" fmt.Println("Testing Repos Get API") //-------------------case 1 : response code = 200------------------------// diff --git a/src/ui/api/search.go b/src/ui/api/search.go index ded814f1c6..468e481aac 100644 --- a/src/ui/api/search.go +++ b/src/ui/api/search.go @@ -99,13 +99,15 @@ func (s *SearchAPI) Get() { } } - repos, err := dao.GetRepositoryByProjectName(p.Name) + total, err := dao.GetTotalOfRepositories(&models.RepositoryQuery{ + ProjectIDs: []int64{p.ProjectID}, + }) if err != nil { - log.Errorf("failed to get repositories of project %s: %v", p.Name, err) + log.Errorf("failed to get total of repositories of project %d: %v", p.ProjectID, err) s.CustomAbort(http.StatusInternalServerError, "") } - p.RepoCount = len(repos) + p.RepoCount = total projectResult = append(projectResult, p) } @@ -124,7 +126,7 @@ func (s *SearchAPI) Get() { func filterRepositories(projects []*models.Project, keyword string) ( []map[string]interface{}, error) { - repositories, err := dao.GetAllRepositories() + repositories, err := dao.GetRepositories() if err != nil { return nil, err } diff --git a/src/ui/api/statistic.go b/src/ui/api/statistic.go index f2ab3283e4..71d4ce77e7 100644 --- a/src/ui/api/statistic.go +++ b/src/ui/api/statistic.go @@ -64,17 +64,22 @@ func (s *StatisticAPI) Get() { } statistic[PubPC] = (int64)(len(pubProjs)) - - ids := []int64{} - for _, p := range pubProjs { - ids = append(ids, p.ProjectID) + if len(pubProjs) == 0 { + statistic[PubRC] = 0 + } else { + ids := []int64{} + for _, p := range pubProjs { + ids = append(ids, p.ProjectID) + } + n, err := dao.GetTotalOfRepositories(&models.RepositoryQuery{ + ProjectIDs: ids, + }) + if err != nil { + log.Errorf("failed to get total of public repositories: %v", err) + s.CustomAbort(http.StatusInternalServerError, "") + } + statistic[PubRC] = n } - n, err := dao.GetTotalOfRepositoriesByProject(ids, "") - if err != nil { - log.Errorf("failed to get total of public repositories: %v", err) - s.CustomAbort(http.StatusInternalServerError, "") - } - statistic[PubRC] = n if s.SecurityCtx.IsSysAdmin() { result, err := s.ProjectMgr.List(nil) @@ -85,7 +90,7 @@ func (s *StatisticAPI) Get() { statistic[TPC] = result.Total statistic[PriPC] = result.Total - statistic[PubPC] - n, err := dao.GetTotalOfRepositories("") + n, err := dao.GetTotalOfRepositories() if err != nil { log.Errorf("failed to get total of repositories: %v", err) s.CustomAbort(http.StatusInternalServerError, "") @@ -107,20 +112,25 @@ func (s *StatisticAPI) Get() { } statistic[PriPC] = result.Total + if result.Total == 0 { + statistic[PriRC] = 0 + } else { + ids := []int64{} + for _, p := range result.Projects { + ids = append(ids, p.ProjectID) + } - ids := []int64{} - for _, p := range result.Projects { - ids = append(ids, p.ProjectID) + n, err := dao.GetTotalOfRepositories(&models.RepositoryQuery{ + ProjectIDs: ids, + }) + if err != nil { + s.HandleInternalServerError(fmt.Sprintf( + "failed to get total of repositories for user %s: %v", + s.username, err)) + return + } + statistic[PriRC] = n } - - n, err = dao.GetTotalOfRepositoriesByProject(ids, "") - if err != nil { - s.HandleInternalServerError(fmt.Sprintf( - "failed to get total of repositories for user %s: %v", - s.username, err)) - return - } - statistic[PriRC] = n } s.Data["json"] = statistic diff --git a/src/ui/api/utils.go b/src/ui/api/utils.go index 0f1c2acac3..26cae35642 100644 --- a/src/ui/api/utils.go +++ b/src/ui/api/utils.go @@ -86,7 +86,7 @@ func SyncRegistry(pm promgr.ProjectManager) error { } var repoRecordsInDB []*models.RepoRecord - repoRecordsInDB, err = dao.GetAllRepositories() + repoRecordsInDB, err = dao.GetRepositories() if err != nil { log.Errorf("error occurred while getting all registories. %v", err) return err diff --git a/src/ui/utils/utils.go b/src/ui/utils/utils.go index 6118551c57..503d2b2f7b 100644 --- a/src/ui/utils/utils.go +++ b/src/ui/utils/utils.go @@ -33,7 +33,7 @@ import ( // ScanAllImages scans all images of Harbor by submiting jobs to jobservice, the whole process will move on if failed to submit any job of a single image. func ScanAllImages() error { - repos, err := dao.GetAllRepositories() + repos, err := dao.GetRepositories() if err != nil { log.Errorf("Failed to list all repositories, error: %v", err) return err @@ -46,7 +46,9 @@ func ScanAllImages() error { // ScanImagesByProjectID scans all images under a projet, the whole process will move on if failed to submit any job of a single image. func ScanImagesByProjectID(id int64) error { - repos, err := dao.GetRepositoriesByProject(id, "", 0, 0) + repos, err := dao.GetRepositories(&models.RepositoryQuery{ + ProjectIDs: []int64{id}, + }) if err != nil { log.Errorf("Failed list repositories in project %d, error: %v", id, err) return err