diff --git a/docs/swagger.yaml b/docs/swagger.yaml index b25bde29e..41ff42cf8 100644 --- a/docs/swagger.yaml +++ b/docs/swagger.yaml @@ -658,6 +658,11 @@ paths: format: int32 required: true description: Relevant project ID. + - name: detail + in: query + type: boolean + required: false + description: Get detail info or not. - name: q in: query type: string diff --git a/src/common/dao/repository.go b/src/common/dao/repository.go index e7cab6459..21ad94c73 100644 --- a/src/common/dao/repository.go +++ b/src/common/dao/repository.go @@ -184,3 +184,34 @@ func GetTotalOfUserRelevantRepositories(userID int, name string) (int64, error) err := GetOrmer().Raw(sql, params).QueryRow(&total) return total, err } + +// GetTotalOfRepositoriesByProject ... +func GetTotalOfRepositoriesByProject(projectID int64, name string) (int64, error) { + qs := GetOrmer().QueryTable(&models.RepoRecord{}). + Filter("ProjectID", projectID) + + 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) { + + repositories := []*models.RepoRecord{} + + qs := GetOrmer().QueryTable(&models.RepoRecord{}). + Filter("ProjectID", projectID) + + if len(name) != 0 { + qs = qs.Filter("Name__contains", name) + } + + _, err := qs.Limit(limit). + Offset(offset).All(&repositories) + + return repositories, err +} diff --git a/src/common/dao/repository_test.go b/src/common/dao/repository_test.go index 563c3013e..2eb1c6d9f 100644 --- a/src/common/dao/repository_test.go +++ b/src/common/dao/repository_test.go @@ -340,6 +340,78 @@ func TestGetTopRepos(t *testing.T) { }) } +func TestGetTotalOfRepositoriesByProject(t *testing.T) { + var projectID int64 = 1 + repoName := "library/total_count" + + total, err := GetTotalOfRepositoriesByProject(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, + OwnerName: "admin", + ProjectName: "library", + }); 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(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, + OwnerName: "admin", + ProjectName: "library", + }); 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 addRepository(repository *models.RepoRecord) error { return AddRepository(*repository) } diff --git a/src/ui/api/harborapi_test.go b/src/ui/api/harborapi_test.go index a5536249a..57cc8ea11 100644 --- a/src/ui/api/harborapi_test.go +++ b/src/ui/api/harborapi_test.go @@ -453,7 +453,7 @@ func (a testapi) PutProjectMember(authInfo usrInfo, projectID string, userID str //-------------------------Repositories Test---------------------------------------// //Return relevant repos of projectID -func (a testapi) GetRepos(authInfo usrInfo, projectID string) (int, error) { +func (a testapi) GetRepos(authInfo usrInfo, projectID, detail string) (int, error) { _sling := sling.New().Get(a.basePath) path := "/api/repositories/" @@ -462,9 +462,13 @@ func (a testapi) GetRepos(authInfo usrInfo, projectID string) (int, error) { type QueryParams struct { ProjectID string `url:"project_id"` + Detail string `url:"detail"` } - _sling = _sling.QueryStruct(&QueryParams{ProjectID: projectID}) + _sling = _sling.QueryStruct(&QueryParams{ + ProjectID: projectID, + Detail: detail, + }) httpStatusCode, _, err := request(_sling, jsonAcceptHeader, authInfo) return httpStatusCode, err } diff --git a/src/ui/api/repository.go b/src/ui/api/repository.go index e0d6a15bc..2a82ebe77 100644 --- a/src/ui/api/repository.go +++ b/src/ui/api/repository.go @@ -22,6 +22,7 @@ import ( "path" "sort" "strings" + "time" "github.com/docker/distribution/manifest/schema1" "github.com/docker/distribution/manifest/schema2" @@ -44,6 +45,19 @@ type RepositoryAPI struct { api.BaseAPI } +type repoResp struct { + ID string `json:"id"` + Name string `json:"name"` + OwnerID int64 `json:"owner_id"` + ProjectID int64 `json:"project_id"` + Description string `json:"description"` + PullCount int64 `json:"pull_count"` + StarCount int64 `json:"star_count"` + TagsCount int64 `json:"tags_count"` + CreationTime time.Time `json:"creation_time"` + UpdateTime time.Time `json:"update_time"` +} + // Get ... func (ra *RepositoryAPI) Get() { projectID, err := ra.GetInt64("project_id") @@ -51,8 +65,6 @@ func (ra *RepositoryAPI) Get() { ra.CustomAbort(http.StatusBadRequest, "invalid project_id") } - page, pageSize := ra.GetPaginationParams() - project, err := dao.GetProjectByID(projectID) if err != nil { log.Errorf("failed to get project %d: %v", projectID, err) @@ -77,30 +89,71 @@ func (ra *RepositoryAPI) Get() { } } - repositories, err := getReposByProject(project.Name, ra.GetString("q")) + keyword := ra.GetString("q") + + total, err := dao.GetTotalOfRepositoriesByProject(projectID, keyword) + if err != nil { + log.Errorf("failed to get total of repositories of project %d: %v", projectID, err) + ra.CustomAbort(http.StatusInternalServerError, "") + } + + page, pageSize := ra.GetPaginationParams() + + detail := ra.GetString("detail") == "1" || ra.GetString("detail") == "true" + + repositories, err := getRepositories(projectID, + keyword, pageSize, pageSize*(page-1), detail) if err != nil { log.Errorf("failed to get repository: %v", err) ra.CustomAbort(http.StatusInternalServerError, "") } - total := int64(len(repositories)) - - if (page-1)*pageSize > total { - repositories = []string{} - } else { - repositories = repositories[(page-1)*pageSize:] - } - - if page*pageSize <= total { - repositories = repositories[:pageSize] - } - ra.SetPaginationHeader(total, page, pageSize) - ra.Data["json"] = repositories ra.ServeJSON() } +func getRepositories(projectID int64, keyword string, + limit, offset int64, detail bool) (interface{}, error) { + repositories, err := dao.GetRepositoriesByProject(projectID, keyword, limit, offset) + if err != nil { + return nil, err + } + + //keep compatibility with old API + if !detail { + result := []string{} + for _, repository := range repositories { + result = append(result, repository.Name) + } + return result, nil + } + + result := []*repoResp{} + for _, repository := range repositories { + repo := &repoResp{ + ID: repository.RepositoryID, + Name: repository.Name, + OwnerID: repository.OwnerID, + ProjectID: repository.ProjectID, + Description: repository.Description, + PullCount: repository.PullCount, + StarCount: repository.StarCount, + CreationTime: repository.CreationTime, + UpdateTime: repository.UpdateTime, + } + + tags, err := getTags(repository.Name) + if err != nil { + return nil, err + } + repo.TagsCount = int64(len(tags)) + result = append(result, repo) + } + + return result, nil +} + // Delete ... func (ra *RepositoryAPI) Delete() { repoName := ra.GetString("repo_name") @@ -119,11 +172,9 @@ func (ra *RepositoryAPI) Delete() { ra.CustomAbort(http.StatusNotFound, fmt.Sprintf("project %s not found", projectName)) } - if project.Public == 0 { - userID := ra.ValidateUser() - if !hasProjectAdminRole(userID, project.ProjectID) { - ra.CustomAbort(http.StatusForbidden, "") - } + userID := ra.ValidateUser() + if !hasProjectAdminRole(userID, project.ProjectID) { + ra.CustomAbort(http.StatusForbidden, "") } rc, err := ra.initRepositoryClient(repoName) diff --git a/src/ui/api/repository_test.go b/src/ui/api/repository_test.go index bfd94c8a8..a473bdadb 100644 --- a/src/ui/api/repository_test.go +++ b/src/ui/api/repository_test.go @@ -20,7 +20,7 @@ func TestGetRepos(t *testing.T) { fmt.Println("Testing Repos Get API") //-------------------case 1 : response code = 200------------------------// fmt.Println("case 1 : response code = 200") - httpStatusCode, err = apiTest.GetRepos(*admin, projectID) + httpStatusCode, err = apiTest.GetRepos(*admin, projectID, "true") if err != nil { t.Error("Error whihle get repos by projectID", err.Error()) t.Log(err) @@ -30,7 +30,7 @@ func TestGetRepos(t *testing.T) { //-------------------case 2 : response code = 400------------------------// fmt.Println("case 2 : response code = 409,invalid project_id") projectID = "ccc" - httpStatusCode, err = apiTest.GetRepos(*admin, projectID) + httpStatusCode, err = apiTest.GetRepos(*admin, projectID, "0") if err != nil { t.Error("Error whihle get repos by projectID", err.Error()) t.Log(err) @@ -40,7 +40,7 @@ func TestGetRepos(t *testing.T) { //-------------------case 3 : response code = 404------------------------// fmt.Println("case 3 : response code = 404:project not found") projectID = "111" - httpStatusCode, err = apiTest.GetRepos(*admin, projectID) + httpStatusCode, err = apiTest.GetRepos(*admin, projectID, "0") if err != nil { t.Error("Error whihle get repos by projectID", err.Error()) t.Log(err)