Return the total count of charts under the project in project API

- add new interface method to get total count of charts under namespaces by calling get index
- add new field 'chart_count' in project model
- append chart count to the project model in project API

Signed-off-by: Steven Zou <szou@vmware.com>
This commit is contained in:
Steven Zou 2018-09-25 14:30:29 +08:00
parent 7a4c0191f6
commit 8b538cbc0a
8 changed files with 91 additions and 0 deletions

View File

@ -3031,6 +3031,9 @@ definitions:
repo_count: repo_count:
type: integer type: integer
description: The number of the repositories under this project. description: The number of the repositories under this project.
chart_count:
type: integer
description: The total number of charts under this project.
metadata: metadata:
description: The metadata of the project. description: The metadata of the project.
$ref: '#/definitions/ProjectMetadata' $ref: '#/definitions/ProjectMetadata'

View File

@ -77,6 +77,14 @@ type ServiceHandler interface {
// If succeed, a nil error will be returned; // If succeed, a nil error will be returned;
// otherwise, a non-nil error will be got. // otherwise, a non-nil error will be got.
DeleteChart(namespace, chartName string) error 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. // ProxyTrafficHandler defines the handler methods to handle the proxy traffic.

View File

@ -15,6 +15,21 @@ const (
maxDeletionThreads = 10 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. // DeleteChart deletes all the chart versions of the specified chart under the namespace.
// See @ServiceHandler.DeleteChart // See @ServiceHandler.DeleteChart
func (c *Controller) DeleteChart(namespace, chartName string) error { func (c *Controller) DeleteChart(namespace, chartName string) error {

View File

@ -4,6 +4,38 @@ import (
"testing" "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 // Test the function DeleteChart
func TestDeleteChart(t *testing.T) { func TestDeleteChart(t *testing.T) {
s, c, err := createMockObjects() s, c, err := createMockObjects()

View File

@ -34,6 +34,7 @@ type Project struct {
Togglable bool `orm:"-" json:"togglable"` Togglable bool `orm:"-" json:"togglable"`
Role int `orm:"-" json:"current_user_role_id"` Role int `orm:"-" json:"current_user_role_id"`
RepoCount int64 `orm:"-" json:"repo_count"` RepoCount int64 `orm:"-" json:"repo_count"`
ChartCount uint64 `orm:"-" json:"chart_count"`
Metadata map[string]string `orm:"-" json:"metadata"` Metadata map[string]string `orm:"-" json:"metadata"`
} }

View File

@ -418,6 +418,15 @@ func (p *ProjectAPI) populateProperties(project *models.Project) {
} }
project.RepoCount = total 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 ... // Put ...

View File

@ -101,6 +101,15 @@ func TestListProjects(t *testing.T) {
apiTest := newHarborAPI() apiTest := newHarborAPI()
var result []apilib.Project 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----------------------------// // ----------------------------case 1 : Response Code=200----------------------------//
fmt.Println("case 1: respose code:200") fmt.Println("case 1: respose code:200")
httpStatusCode, result, err := apiTest.ProjectsGet( httpStatusCode, result, err := apiTest.ProjectsGet(
@ -183,6 +192,15 @@ func TestProGetByID(t *testing.T) {
var result apilib.Project var result apilib.Project
projectID := strconv.Itoa(addPID) 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----------------------------// // ----------------------------case 1 : Response Code=200----------------------------//
fmt.Println("case 1: respose code:200") fmt.Println("case 1: respose code:200")
httpStatusCode, result, err := apiTest.ProjectsGetByPID(projectID) httpStatusCode, result, err := apiTest.ProjectsGetByPID(projectID)

View File

@ -35,6 +35,11 @@ var MockChartRepoHandler = http.HandlerFunc(func(w http.ResponseWriter, r *http.
w.Write([]byte(repo2IndexYaml)) w.Write([]byte(repo2IndexYaml))
return 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", case "/repo1/charts/harbor-0.2.0.tgz",
"/library/charts/harbor-0.2.0.tgz": "/library/charts/harbor-0.2.0.tgz":
if r.Method == http.MethodGet { if r.Method == http.MethodGet {