diff --git a/docs/swagger.yaml b/docs/swagger.yaml index 7db7d4602..638ce5cbc 100644 --- a/docs/swagger.yaml +++ b/docs/swagger.yaml @@ -3031,6 +3031,9 @@ definitions: repo_count: type: integer description: The number of the repositories under this project. + chart_count: + type: integer + description: The total number of charts under this project. metadata: description: The metadata of the project. $ref: '#/definitions/ProjectMetadata' diff --git a/src/chartserver/handler_interface.go b/src/chartserver/handler_interface.go index 602a68467..18ca16fe9 100644 --- a/src/chartserver/handler_interface.go +++ b/src/chartserver/handler_interface.go @@ -77,6 +77,14 @@ type ServiceHandler interface { // If succeed, a nil error will be returned; // otherwise, a non-nil error will be got. DeleteChart(namespace, chartName string) error + + // GetCountOfCharts calculates and returns the total count of charts under the specified namespaces. + // + // namespaces []string : the namespaces to count charts + // + // If succeed, a unsigned integer with nil error will be returned; + // otherwise, a non-nil error will be got. + GetCountOfCharts(namespaces []string) (uint64, error) } // ProxyTrafficHandler defines the handler methods to handle the proxy traffic. diff --git a/src/chartserver/handler_utility.go b/src/chartserver/handler_utility.go index 1342f614a..1fa1b89a2 100644 --- a/src/chartserver/handler_utility.go +++ b/src/chartserver/handler_utility.go @@ -15,6 +15,21 @@ const ( maxDeletionThreads = 10 ) +// GetCountOfCharts calculates and returns the total count of charts under the specified namespaces. +// See @ServiceHandler.GetCountOfCharts +func (c *Controller) GetCountOfCharts(namespaces []string) (uint64, error) { + if namespaces == nil || len(namespaces) == 0 { + return 0, nil // Directly return 0 instead of non-nil error + } + + indexFile, err := c.getIndexYaml(namespaces) + if err != nil { + return 0, err + } + + return (uint64)(len(indexFile.Entries)), nil +} + // DeleteChart deletes all the chart versions of the specified chart under the namespace. // See @ServiceHandler.DeleteChart func (c *Controller) DeleteChart(namespace, chartName string) error { diff --git a/src/chartserver/handler_utility_test.go b/src/chartserver/handler_utility_test.go index 800ab3420..636352947 100644 --- a/src/chartserver/handler_utility_test.go +++ b/src/chartserver/handler_utility_test.go @@ -4,6 +4,38 @@ import ( "testing" ) +// Test the function GetCountOfCharts +func TestGetCountOfCharts(t *testing.T) { + s, c, err := createMockObjects() + if err != nil { + t.Fatal(err) + } + defer s.Close() + + count, err := c.GetCountOfCharts([]string{}) + if err != nil { + t.Fatalf("expect nil error but got %s", err) + } + if count != 0 { + t.Fatalf("expect 0 but got %d", count) + } + + namespaces := []string{"repo1", "repo2"} + count, err = c.GetCountOfCharts(namespaces) + if err != nil { + t.Fatalf("expect nil error but got %s", err) + } + + if count != 5 { + t.Fatalf("expect 5 but got %d", count) + } + + _, err = c.GetCountOfCharts([]string{"not-existing-ns"}) + if err == nil { + t.Fatal("expect non-nil error but got nil one") + } +} + // Test the function DeleteChart func TestDeleteChart(t *testing.T) { s, c, err := createMockObjects() diff --git a/src/common/models/project.go b/src/common/models/project.go index 8bf2e4018..bebadcdd1 100644 --- a/src/common/models/project.go +++ b/src/common/models/project.go @@ -34,6 +34,7 @@ type Project struct { Togglable bool `orm:"-" json:"togglable"` Role int `orm:"-" json:"current_user_role_id"` RepoCount int64 `orm:"-" json:"repo_count"` + ChartCount uint64 `orm:"-" json:"chart_count"` Metadata map[string]string `orm:"-" json:"metadata"` } diff --git a/src/core/api/project.go b/src/core/api/project.go index 2f28d4bae..6c49cb899 100644 --- a/src/core/api/project.go +++ b/src/core/api/project.go @@ -418,6 +418,15 @@ func (p *ProjectAPI) populateProperties(project *models.Project) { } project.RepoCount = total + + // Populate chart count property + count, err := chartController.GetCountOfCharts([]string{project.Name}) + if err != nil { + log.Errorf("Failed to get total of charts under project %s: %v", project.Name, err) + p.CustomAbort(http.StatusInternalServerError, "") + } + + project.ChartCount = count } // Put ... diff --git a/src/core/api/project_test.go b/src/core/api/project_test.go index 1a0e714ec..2ec07cc1d 100644 --- a/src/core/api/project_test.go +++ b/src/core/api/project_test.go @@ -101,6 +101,15 @@ func TestListProjects(t *testing.T) { apiTest := newHarborAPI() var result []apilib.Project + cMockServer, oldCtrl, err := mockChartController() + if err != nil { + t.Fatal(err) + } + defer func() { + cMockServer.Close() + chartController = oldCtrl + }() + // ----------------------------case 1 : Response Code=200----------------------------// fmt.Println("case 1: respose code:200") httpStatusCode, result, err := apiTest.ProjectsGet( @@ -183,6 +192,15 @@ func TestProGetByID(t *testing.T) { var result apilib.Project projectID := strconv.Itoa(addPID) + cMockServer, oldCtrl, err := mockChartController() + if err != nil { + t.Fatal(err) + } + defer func() { + cMockServer.Close() + chartController = oldCtrl + }() + // ----------------------------case 1 : Response Code=200----------------------------// fmt.Println("case 1: respose code:200") httpStatusCode, result, err := apiTest.ProjectsGetByPID(projectID) diff --git a/src/testing/chart_utility.go b/src/testing/chart_utility.go index 570c7aa9b..82dd6fca5 100644 --- a/src/testing/chart_utility.go +++ b/src/testing/chart_utility.go @@ -35,6 +35,11 @@ var MockChartRepoHandler = http.HandlerFunc(func(w http.ResponseWriter, r *http. w.Write([]byte(repo2IndexYaml)) return } + case "/not-existing-ns/index.yaml": + if r.Method == http.MethodGet { + w.WriteHeader(http.StatusNotFound) + return + } case "/repo1/charts/harbor-0.2.0.tgz", "/library/charts/harbor-0.2.0.tgz": if r.Method == http.MethodGet {