support pagination in GET recent logs API

This commit is contained in:
Wenkai Yin 2017-05-26 14:58:00 +08:00
parent 565110d9f1
commit 1b83c0b076
17 changed files with 194 additions and 326 deletions

View File

@ -861,24 +861,18 @@ paths:
description: | description: |
This endpoint let user see the recent operation logs of the projects which he is member of This endpoint let user see the recent operation logs of the projects which he is member of
parameters: parameters:
- name: lines - name: page
in: query in: query
type: integer type: integer
format: int32 format: int32
required: false required: false
description: The number of logs to be shown, default is 10 if lines, start_time, end_time are not provided. description: The page nubmer, default is 1.
- name: start_time - name: page_size
in: query in: query
type: integer type: integer
format: int64 format: int32
required: false required: false
description: The start time of logs to be shown in unix timestap description: The size of per page, default is 10, maximum is 100.
- name: end_time
in: query
type: integer
format: int64
required: false
description: The end time of logs to be shown in unix timestap
tags: tags:
- Products - Products
responses: responses:
@ -889,7 +883,7 @@ paths:
items: items:
$ref: '#/definitions/AccessLog' $ref: '#/definitions/AccessLog'
400: 400:
description: Bad request because of invalid parameter of lines or start_time or end_time. description: Bad request because of invalid parameters.
401: 401:
description: User need to login first. description: User need to login first.
500: 500:

View File

@ -15,8 +15,7 @@
package dao package dao
import ( import (
"strings" "github.com/astaxie/beego/orm"
"github.com/vmware/harbor/src/common/models" "github.com/vmware/harbor/src/common/models"
"github.com/vmware/harbor/src/common/utils/log" "github.com/vmware/harbor/src/common/utils/log"
) )
@ -29,155 +28,61 @@ func AddAccessLog(accessLog models.AccessLog) error {
} }
// GetTotalOfAccessLogs ... // GetTotalOfAccessLogs ...
func GetTotalOfAccessLogs(query models.AccessLog) (int64, error) { func GetTotalOfAccessLogs(query *models.LogQueryParam) (int64, error) {
o := GetOrmer() return logQueryConditions(query).Count()
queryParam := []interface{}{}
sql := `select count(*) from access_log al
where al.project_id = ?`
queryParam = append(queryParam, query.ProjectID)
sql += genFilterClauses(query, &queryParam)
var total int64
if err := o.Raw(sql, queryParam).QueryRow(&total); err != nil {
return 0, err
}
return total, nil
} }
//GetAccessLogs gets access logs according to different conditions //GetAccessLogs gets access logs according to different conditions
func GetAccessLogs(query models.AccessLog, limit, offset int64) ([]models.AccessLog, error) { func GetAccessLogs(query *models.LogQueryParam) ([]models.AccessLog, error) {
o := GetOrmer() qs := logQueryConditions(query).OrderBy("-op_time")
queryParam := []interface{}{} if query != nil && query.Pagination != nil {
sql := `select al.log_id, al.username, al.repo_name, size := query.Pagination.Size
al.repo_tag, al.operation, al.op_time if size > 0 {
from access_log al qs = qs.Limit(size)
where al.project_id = ? `
queryParam = append(queryParam, query.ProjectID)
sql += genFilterClauses(query, &queryParam) page := query.Pagination.Page
if page > 0 {
sql += ` order by al.op_time desc ` qs = qs.Offset((page - 1) * size)
}
sql = paginateForRawSQL(sql, limit, offset) }
}
logs := []models.AccessLog{} logs := []models.AccessLog{}
_, err := o.Raw(sql, queryParam).QueryRows(&logs) _, err := qs.All(&logs)
if err != nil {
return logs, err return logs, err
} }
return logs, nil func logQueryConditions(query *models.LogQueryParam) orm.QuerySeter {
qs := GetOrmer().QueryTable(&models.AccessLog{})
if query == nil {
return qs
} }
func genFilterClauses(query models.AccessLog, queryParam *[]interface{}) string { if len(query.ProjectIDs) > 0 {
sql := "" qs = qs.Filter("project_id__in", query.ProjectIDs)
}
if query.Username != "" { if len(query.Username) != 0 {
sql += ` and al.username like ? ` qs = qs.Filter("username__contains", query.Username)
*queryParam = append(*queryParam, "%"+escape(query.Username)+"%") }
if len(query.Repository) != 0 {
qs = qs.Filter("repo_name", query.Repository)
}
if len(query.Tag) != 0 {
qs = qs.Filter("repo_tag", query.Tag)
}
if len(query.Operations) > 0 {
qs = qs.Filter("operation__in", query.Operations)
}
if query.BeginTime != nil {
qs = qs.Filter("op_time__gte", query.BeginTime)
}
if query.EndTime != nil {
qs = qs.Filter("op_time__lte", query.EndTime)
} }
if query.Operation != "" { return qs
sql += ` and al.operation = ? `
*queryParam = append(*queryParam, query.Operation)
}
if query.RepoName != "" {
sql += ` and al.repo_name = ? `
*queryParam = append(*queryParam, query.RepoName)
}
if query.RepoTag != "" {
sql += ` and al.repo_tag = ? `
*queryParam = append(*queryParam, query.RepoTag)
}
if query.Keywords != "" {
sql += ` and al.operation in ( `
keywordList := strings.Split(query.Keywords, "/")
num := len(keywordList)
for i := 0; i < num; i++ {
if keywordList[i] != "" {
if i == num-1 {
sql += `?)`
} else {
sql += `?,`
}
*queryParam = append(*queryParam, keywordList[i])
}
}
}
if query.BeginTimestamp > 0 {
sql += ` and al.op_time >= ? `
*queryParam = append(*queryParam, query.BeginTime)
}
if query.EndTimestamp > 0 {
sql += ` and al.op_time <= ? `
*queryParam = append(*queryParam, query.EndTime)
}
return sql
}
//GetRecentLogs returns recent logs according to parameters
func GetRecentLogs(username string, linesNum int, startTime, endTime string) ([]models.AccessLog, error) {
logs := []models.AccessLog{}
isAdmin, err := IsAdminRole(username)
if err != nil {
return logs, err
}
queryParam := []interface{}{}
sql := `select log_id, username, project_id, repo_name, repo_tag, GUID, operation, op_time
from access_log `
hasWhere := false
if !isAdmin {
sql += ` where project_id in
(select distinct project_id
from project_member pm
join user u
on pm.user_id = u.user_id
where u.username = ?) `
queryParam = append(queryParam, username)
hasWhere = true
}
if startTime != "" {
if hasWhere {
sql += " and op_time >= ?"
} else {
sql += " where op_time >= ?"
hasWhere = true
}
queryParam = append(queryParam, startTime)
}
if endTime != "" {
if hasWhere {
sql += " and op_time <= ?"
} else {
sql += " where op_time <= ?"
hasWhere = true
}
queryParam = append(queryParam, endTime)
}
sql += " order by op_time desc"
if linesNum != 0 {
sql += " limit ?"
queryParam = append(queryParam, linesNum)
}
_, err = GetOrmer().Raw(sql, queryParam).QueryRows(&logs)
if err != nil {
return logs, err
}
return logs, nil
} }
// CountPull ... // CountPull ...

View File

@ -570,11 +570,11 @@ func TestGetAccessLog(t *testing.T) {
t.Errorf("failed to add access log: %v", err) t.Errorf("failed to add access log: %v", err)
} }
queryAccessLog := models.AccessLog{ query := &models.LogQueryParam{
Username: currentUser.Username, Username: currentUser.Username,
ProjectID: currentProject.ProjectID, ProjectIDs: []int64{currentProject.ProjectID},
} }
accessLogs, err := GetAccessLogs(queryAccessLog, 1000, 0) accessLogs, err := GetAccessLogs(query)
if err != nil { if err != nil {
t.Errorf("Error occurred in GetAccessLog: %v", err) t.Errorf("Error occurred in GetAccessLog: %v", err)
} }
@ -587,11 +587,11 @@ func TestGetAccessLog(t *testing.T) {
} }
func TestGetTotalOfAccessLogs(t *testing.T) { func TestGetTotalOfAccessLogs(t *testing.T) {
queryAccessLog := models.AccessLog{ query := &models.LogQueryParam{
Username: currentUser.Username, Username: currentUser.Username,
ProjectID: currentProject.ProjectID, ProjectIDs: []int64{currentProject.ProjectID},
} }
total, err := GetTotalOfAccessLogs(queryAccessLog) total, err := GetTotalOfAccessLogs(query)
if err != nil { if err != nil {
t.Fatalf("failed to get total of access log: %v", err) t.Fatalf("failed to get total of access log: %v", err)
} }
@ -617,7 +617,15 @@ func TestAddAccessLog(t *testing.T) {
if err != nil { if err != nil {
t.Errorf("Error occurred in AddAccessLog: %v", err) t.Errorf("Error occurred in AddAccessLog: %v", err)
} }
accessLogList, err = GetAccessLogs(accessLog, 1000, 0)
query := &models.LogQueryParam{
Username: accessLog.Username,
ProjectIDs: []int64{accessLog.ProjectID},
Repository: accessLog.RepoName,
Tag: accessLog.RepoTag,
Operations: []string{accessLog.Operation},
}
accessLogList, err = GetAccessLogs(query)
if err != nil { if err != nil {
t.Errorf("Error occurred in GetAccessLog: %v", err) t.Errorf("Error occurred in GetAccessLog: %v", err)
} }
@ -819,7 +827,7 @@ func TestGetProjects(t *testing.T) {
func TestGetPublicProjects(t *testing.T) { func TestGetPublicProjects(t *testing.T) {
value := true value := true
projects, err := GetProjects(&models.QueryParam{ projects, err := GetProjects(&models.ProjectQueryParam{
Public: &value, Public: &value,
}) })
if err != nil { if err != nil {
@ -953,16 +961,6 @@ func TestChangeUserProfile(t *testing.T) {
} }
} }
func TestGetRecentLogs(t *testing.T) {
logs, err := GetRecentLogs(currentUser.Username, 10, "2016-05-13 00:00:00", time.Now().String())
if err != nil {
t.Errorf("error occured in getting recent logs, error: %v", err)
}
if len(logs) <= 0 {
t.Errorf("get logs error, expected: %d, actual: %d", 1, len(logs))
}
}
var targetID, policyID, policyID2, policyID3, jobID, jobID2, jobID3 int64 var targetID, policyID, policyID2, policyID3, jobID, jobID2, jobID3 int64
func TestAddRepTarget(t *testing.T) { func TestAddRepTarget(t *testing.T) {

View File

@ -183,7 +183,7 @@ func SearchProjects(userID int) ([]*models.Project, error) {
// GetTotalOfProjects returns the total count of projects // GetTotalOfProjects returns the total count of projects
// according to the query conditions // according to the query conditions
func GetTotalOfProjects(query *models.QueryParam) (int64, error) { func GetTotalOfProjects(query *models.ProjectQueryParam) (int64, error) {
var ( var (
owner string owner string
@ -203,7 +203,7 @@ func GetTotalOfProjects(query *models.QueryParam) (int64, error) {
} }
} }
sql, params := queryConditions(owner, name, public, member, role) sql, params := projectQueryConditions(owner, name, public, member, role)
sql = `select count(*) ` + sql sql = `select count(*) ` + sql
@ -213,7 +213,7 @@ func GetTotalOfProjects(query *models.QueryParam) (int64, error) {
} }
// GetProjects returns a project list according to the query conditions // GetProjects returns a project list according to the query conditions
func GetProjects(query *models.QueryParam) ([]*models.Project, error) { func GetProjects(query *models.ProjectQueryParam) ([]*models.Project, error) {
var ( var (
owner string owner string
@ -239,7 +239,7 @@ func GetProjects(query *models.QueryParam) ([]*models.Project, error) {
} }
} }
sql, params := queryConditions(owner, name, public, member, role) sql, params := projectQueryConditions(owner, name, public, member, role)
sql = `select distinct p.project_id, p.name, p.public, p.owner_id, sql = `select distinct p.project_id, p.name, p.public, p.owner_id,
p.creation_time, p.update_time ` + sql p.creation_time, p.update_time ` + sql
@ -258,7 +258,7 @@ func GetProjects(query *models.QueryParam) ([]*models.Project, error) {
return projects, err return projects, err
} }
func queryConditions(owner, name string, public *bool, member string, func projectQueryConditions(owner, name string, public *bool, member string,
role int) (string, []interface{}) { role int) (string, []interface{}) {
params := []interface{}{} params := []interface{}{}

View File

@ -30,8 +30,19 @@ type AccessLog struct {
Operation string `orm:"column(operation)" json:"operation"` Operation string `orm:"column(operation)" json:"operation"`
OpTime time.Time `orm:"column(op_time)" json:"op_time"` OpTime time.Time `orm:"column(op_time)" json:"op_time"`
Keywords string `orm:"-" json:"keywords"` Keywords string `orm:"-" json:"keywords"`
BeginTime time.Time `orm:"-"`
BeginTimestamp int64 `orm:"-" json:"begin_timestamp"` BeginTimestamp int64 `orm:"-" json:"begin_timestamp"`
EndTime time.Time `orm:"-"`
EndTimestamp int64 `orm:"-" json:"end_timestamp"` EndTimestamp int64 `orm:"-" json:"end_timestamp"`
} }
// LogQueryParam is used to set query conditions when listing
// access logs.
type LogQueryParam struct {
ProjectIDs []int64 // the IDs of projects to which the operation is done
Username string // the operator's username of the log
Repository string // repository name
Tag string // tag name
Operations []string // operations
BeginTime *time.Time // the time after which the operation is done
EndTime *time.Time // the time before which the operation is doen
Pagination *Pagination // pagination information
}

View File

@ -58,7 +58,7 @@ func (ps *ProjectSorter) Swap(i, j int) {
ps.Projects[i], ps.Projects[j] = ps.Projects[j], ps.Projects[i] ps.Projects[i], ps.Projects[j] = ps.Projects[j], ps.Projects[i]
} }
// QueryParam can be used to set query parameters when listing projects. // ProjectQueryParam can be used to set query parameters when listing projects.
// The query condition will be set in the query if its corresponding field // The query condition will be set in the query if its corresponding field
// is not nil. Leave it empty if you don't want to apply this condition. // is not nil. Leave it empty if you don't want to apply this condition.
// //
@ -69,7 +69,7 @@ func (ps *ProjectSorter) Swap(i, j int) {
// List all public projects the owner of which is user1: query := &QueryParam{Owner:"user1",Public:true} // List all public projects the owner of which is user1: query := &QueryParam{Owner:"user1",Public:true}
// List projects which user1 is member of: query := &QueryParam{Member:&Member{Name:"user1"}} // List projects which user1 is member of: query := &QueryParam{Member:&Member{Name:"user1"}}
// List projects which user1 is the project admin : query := &QueryParam{Memeber:&Member{Name:"user1",Role:1}} // List projects which user1 is the project admin : query := &QueryParam{Memeber:&Member{Name:"user1",Role:1}}
type QueryParam struct { type ProjectQueryParam struct {
Name string // the name of project Name string // the name of project
Owner string // the username of project owner Owner string // the username of project owner
Public *bool // the project is public or not, can be ture, false and nil Public *bool // the project is public or not, can be ture, false and nil

View File

@ -106,12 +106,12 @@ func (f *fakePM) Update(projectIDOrName interface{}, project *models.Project) er
} }
// nil implement // nil implement
func (f *fakePM) GetAll(*models.QueryParam) ([]*models.Project, error) { func (f *fakePM) GetAll(*models.ProjectQueryParam) ([]*models.Project, error) {
return []*models.Project{}, nil return []*models.Project{}, nil
} }
// nil implement // nil implement
func (f *fakePM) GetTotal(*models.QueryParam) (int64, error) { func (f *fakePM) GetTotal(*models.ProjectQueryParam) (int64, error) {
return 0, nil return 0, nil
} }

View File

@ -226,20 +226,14 @@ func (a testapi) StatisticGet(user usrInfo) (int, apilib.StatisticMap, error) {
return httpStatusCode, successPayload, err return httpStatusCode, successPayload, err
} }
func (a testapi) LogGet(user usrInfo, startTime, endTime, lines string) (int, []apilib.AccessLog, error) { func (a testapi) LogGet(user usrInfo) (int, []apilib.AccessLog, error) {
_sling := sling.New().Get(a.basePath) _sling := sling.New().Get(a.basePath)
// create path and map variables // create path and map variables
path := "/api/logs/" path := "/api/logs/"
fmt.Printf("logs path: %s\n", path) fmt.Printf("logs path: %s\n", path)
_sling = _sling.Path(path) _sling = _sling.Path(path)
type QueryParams struct {
StartTime string `url:"start_time,omitempty"`
EndTime string `url:"end_time,omitempty"`
Lines string `url:"lines,omitempty"`
}
_sling = _sling.QueryStruct(&QueryParams{StartTime: startTime, EndTime: endTime, Lines: lines})
var successPayload []apilib.AccessLog var successPayload []apilib.AccessLog
code, body, err := request(_sling, jsonAcceptHeader, user) code, body, err := request(_sling, jsonAcceptHeader, user)
if 200 == code && nil == err { if 200 == code && nil == err {

View File

@ -15,18 +15,17 @@
package api package api
import ( import (
"net/http" "fmt"
"strconv"
"time"
"github.com/vmware/harbor/src/common/dao" "github.com/vmware/harbor/src/common/dao"
"github.com/vmware/harbor/src/common/models" "github.com/vmware/harbor/src/common/models"
"github.com/vmware/harbor/src/common/utils/log"
) )
//LogAPI handles request api/logs //LogAPI handles request api/logs
type LogAPI struct { type LogAPI struct {
BaseController BaseController
username string
isSysAdmin bool
} }
//Prepare validates the URL and the user //Prepare validates the URL and the user
@ -36,53 +35,58 @@ func (l *LogAPI) Prepare() {
l.HandleUnauthorized() l.HandleUnauthorized()
return return
} }
l.username = l.SecurityCtx.GetUsername()
l.isSysAdmin = l.SecurityCtx.IsSysAdmin()
} }
//Get returns the recent logs according to parameters //Get returns the recent logs according to parameters
func (l *LogAPI) Get() { func (l *LogAPI) Get() {
var err error page, size := l.GetPaginationParams()
startTime := l.GetString("start_time") query := &models.LogQueryParam{
if len(startTime) != 0 { Pagination: &models.Pagination{
i, err := strconv.ParseInt(startTime, 10, 64) Page: page,
if err != nil { Size: size,
log.Errorf("Parse startTime to int error, err: %v", err) },
l.CustomAbort(http.StatusBadRequest, "startTime is not a valid integer")
}
startTime = time.Unix(i, 0).String()
} }
endTime := l.GetString("end_time") if !l.isSysAdmin {
if len(endTime) != 0 { projects, err := l.ProjectMgr.GetByMember(l.username)
j, err := strconv.ParseInt(endTime, 10, 64)
if err != nil { if err != nil {
log.Errorf("Parse endTime to int error, err: %v", err) l.HandleInternalServerError(fmt.Sprintf(
l.CustomAbort(http.StatusBadRequest, "endTime is not a valid integer") "failed to get projects of user %s: %v", l.username, err))
} return
endTime = time.Unix(j, 0).String()
} }
var linesNum int if len(projects) == 0 {
lines := l.GetString("lines") l.SetPaginationHeader(0, page, size)
if len(lines) != 0 { l.Data["json"] = nil
linesNum, err = strconv.Atoi(lines) l.ServeJSON()
if err != nil { return
log.Errorf("Get parameters error--lines, err: %v", err)
l.CustomAbort(http.StatusBadRequest, "bad request of lines")
}
if linesNum <= 0 {
log.Warning("lines must be a positive integer")
l.CustomAbort(http.StatusBadRequest, "lines is 0 or negative")
}
} else if len(startTime) == 0 && len(endTime) == 0 {
linesNum = 10
} }
var logList []models.AccessLog ids := []int64{}
logList, err = dao.GetRecentLogs(l.SecurityCtx.GetUsername(), linesNum, startTime, endTime) for _, project := range projects {
if err != nil { ids = append(ids, project.ProjectID)
log.Errorf("Get recent logs error, err: %v", err)
l.CustomAbort(http.StatusInternalServerError, "Internal error")
} }
l.Data["json"] = logList query.ProjectIDs = ids
}
total, err := dao.GetTotalOfAccessLogs(query)
if err != nil {
l.HandleInternalServerError(fmt.Sprintf(
"failed to get total of access logs: %v", err))
return
}
logs, err := dao.GetAccessLogs(query)
if err != nil {
l.HandleInternalServerError(fmt.Sprintf(
"failed to get access logs: %v", err))
return
}
l.SetPaginationHeader(total, page, size)
l.Data["json"] = logs
l.ServeJSON() l.ServeJSON()
} }

View File

@ -15,11 +15,11 @@ package api
import ( import (
"fmt" "fmt"
"github.com/stretchr/testify/assert"
"github.com/vmware/harbor/tests/apitests/apilib"
"strconv" "strconv"
"testing" "testing"
"time"
"github.com/stretchr/testify/assert"
"github.com/vmware/harbor/tests/apitests/apilib"
) )
func TestLogGet(t *testing.T) { func TestLogGet(t *testing.T) {
@ -29,12 +29,11 @@ func TestLogGet(t *testing.T) {
apiTest := newHarborAPI() apiTest := newHarborAPI()
//prepare for test //prepare for test
CommonAddUser()
var project apilib.ProjectReq var project apilib.ProjectReq
project.ProjectName = "my_project" project.ProjectName = "my_project"
project.Public = 1 project.Public = 1
now := fmt.Sprintf("%v", time.Now().Unix()) statusCode, result, err := apiTest.LogGet(*testUser)
statusCode, result, err := apiTest.LogGet(*admin, "0", now, "1000")
if err != nil { if err != nil {
t.Error("Error while get log information", err.Error()) t.Error("Error while get log information", err.Error())
t.Log(err) t.Log(err)
@ -46,7 +45,7 @@ func TestLogGet(t *testing.T) {
fmt.Println("result", result) fmt.Println("result", result)
//add the project first. //add the project first.
fmt.Println("add the project first.") fmt.Println("add the project first.")
reply, err := apiTest.ProjectsPost(*admin, project) reply, err := apiTest.ProjectsPost(*testUser, project)
if err != nil { if err != nil {
t.Error("Error while creat project", err.Error()) t.Error("Error while creat project", err.Error())
t.Log(err) t.Log(err)
@ -54,8 +53,7 @@ func TestLogGet(t *testing.T) {
assert.Equal(int(201), reply, "Case 2: Project creation status should be 201") assert.Equal(int(201), reply, "Case 2: Project creation status should be 201")
} }
//case 1: right parameters, expect the right output //case 1: right parameters, expect the right output
now = fmt.Sprintf("%v", time.Now().Unix()) statusCode, result, err = apiTest.LogGet(*testUser)
statusCode, result, err = apiTest.LogGet(*admin, "0", now, "1000")
if err != nil { if err != nil {
t.Error("Error while get log information", err.Error()) t.Error("Error while get log information", err.Error())
t.Log(err) t.Log(err)
@ -71,64 +69,6 @@ func TestLogGet(t *testing.T) {
} }
} }
fmt.Println("log ", result) fmt.Println("log ", result)
//case 2: wrong format of start_time parameter, expect the wrong output
statusCode, result, err = apiTest.LogGet(*admin, "ss", now, "3")
if err != nil {
t.Error("Error occured while get log information since the format of start_time parameter is not right.", err.Error())
t.Log(err)
} else {
assert.Equal(int(400), statusCode, "Http status code should be 400")
}
//case 3: wrong format of end_time parameter, expect the wrong output
statusCode, result, err = apiTest.LogGet(*admin, "0", "cc", "3")
if err != nil {
t.Error("Error occured while get log information since the format of end_time parameter is not right.", err.Error())
t.Log(err)
} else {
assert.Equal(int(400), statusCode, "Http status code should be 400")
}
//case 4: wrong format of lines parameter, expect the wrong output
statusCode, result, err = apiTest.LogGet(*admin, "0", now, "s")
if err != nil {
t.Error("Error occured while get log information since the format of lines parameter is not right.", err.Error())
t.Log(err)
} else {
assert.Equal(int(400), statusCode, "Http status code should be 400")
}
//case 5: wrong format of lines parameter, expect the wrong output
statusCode, result, err = apiTest.LogGet(*admin, "0", now, "-5")
if err != nil {
t.Error("Error occured while get log information since the format of lines parameter is not right.", err.Error())
t.Log(err)
} else {
assert.Equal(int(400), statusCode, "Http status code should be 400")
}
//case 6: all parameters are null, expect the right output
statusCode, result, err = apiTest.LogGet(*admin, "", "", "")
if err != nil {
t.Error("Error while get log information", err.Error())
t.Log(err)
} else {
//default get 10 logs
if logNum+1 >= 10 {
logNum = 10
} else {
logNum++
}
assert.Equal(logNum, len(result), "lines of logs should be equal")
num, index := getLog(result)
if num != 1 {
assert.Equal(1, num, "add my_project log number should be 1")
} else {
assert.Equal("my_project/", result[index].RepoName, "RepoName should be equal")
assert.Equal("N/A", result[index].RepoTag, "RepoTag should be equal")
assert.Equal("create", result[index].Operation, "Operation should be equal")
}
}
//get the project //get the project
var projects []apilib.Project var projects []apilib.Project
@ -144,7 +84,7 @@ func TestLogGet(t *testing.T) {
//delete the project //delete the project
projectID := strconv.Itoa(int(addProjectID)) projectID := strconv.Itoa(int(addProjectID))
httpStatusCode, err = apiTest.ProjectsDelete(*admin, projectID) httpStatusCode, err = apiTest.ProjectsDelete(*testUser, projectID)
if err != nil { if err != nil {
t.Error("Error while delete project", err.Error()) t.Error("Error while delete project", err.Error())
t.Log(err) t.Log(err)
@ -152,7 +92,7 @@ func TestLogGet(t *testing.T) {
assert.Equal(int(200), httpStatusCode, "Case 1: Project creation status should be 200") assert.Equal(int(200), httpStatusCode, "Case 1: Project creation status should be 200")
//t.Log(result) //t.Log(result)
} }
CommonDelUser()
fmt.Printf("\n") fmt.Printf("\n")
} }

View File

@ -18,6 +18,7 @@ import (
"fmt" "fmt"
"net/http" "net/http"
"regexp" "regexp"
"strings"
"github.com/vmware/harbor/src/common" "github.com/vmware/harbor/src/common"
"github.com/vmware/harbor/src/common/dao" "github.com/vmware/harbor/src/common/dao"
@ -258,7 +259,7 @@ func projectContainsPolicy(id int64) (bool, error) {
// TODO refacter pattern to: // TODO refacter pattern to:
// /api/repositories?owner=xxx&name=xxx&public=true&member=xxx&role=1&page=1&size=3 // /api/repositories?owner=xxx&name=xxx&public=true&member=xxx&role=1&page=1&size=3
func (p *ProjectAPI) List() { func (p *ProjectAPI) List() {
query := &models.QueryParam{} query := &models.ProjectQueryParam{}
query.Name = p.GetString("project_name") query.Name = p.GetString("project_name")
public := p.GetString("is_public") public := p.GetString("is_public")
@ -382,28 +383,49 @@ func (p *ProjectAPI) FilterAccessLog() {
var query models.AccessLog var query models.AccessLog
p.DecodeJSONReq(&query) p.DecodeJSONReq(&query)
query.ProjectID = p.project.ProjectID queryParm := &models.LogQueryParam{
query.BeginTime = time.Unix(query.BeginTimestamp, 0) ProjectIDs: []int64{p.project.ProjectID},
query.EndTime = time.Unix(query.EndTimestamp, 0) Username: query.Username,
Repository: query.RepoName,
page, pageSize := p.GetPaginationParams() Tag: query.RepoTag,
total, err := dao.GetTotalOfAccessLogs(query)
if err != nil {
log.Errorf("failed to get total of access log: %v", err)
p.CustomAbort(http.StatusInternalServerError, "")
} }
logs, err := dao.GetAccessLogs(query, pageSize, pageSize*(page-1)) if len(query.Keywords) > 0 {
queryParm.Operations = strings.Split(query.Keywords, "/")
}
if query.BeginTimestamp > 0 {
beginTime := time.Unix(query.BeginTimestamp, 0)
queryParm.BeginTime = &beginTime
}
if query.EndTimestamp > 0 {
endTime := time.Unix(query.EndTimestamp, 0)
queryParm.EndTime = &endTime
}
page, pageSize := p.GetPaginationParams()
queryParm.Pagination = &models.Pagination{
Page: page,
Size: pageSize,
}
total, err := dao.GetTotalOfAccessLogs(queryParm)
if err != nil { if err != nil {
log.Errorf("failed to get access log: %v", err) p.HandleInternalServerError(fmt.Sprintf(
p.CustomAbort(http.StatusInternalServerError, "") "failed to get total of access log: %v", err))
return
}
logs, err := dao.GetAccessLogs(queryParm)
if err != nil {
p.HandleInternalServerError(fmt.Sprintf(
"failed to get access log: %v", err))
return
} }
p.SetPaginationHeader(total, page, pageSize) p.SetPaginationHeader(total, page, pageSize)
p.Data["json"] = logs p.Data["json"] = logs
p.ServeJSON() p.ServeJSON()
} }

View File

@ -53,7 +53,7 @@ func (s *StatisticAPI) Prepare() {
func (s *StatisticAPI) Get() { func (s *StatisticAPI) Get() {
statistic := map[string]int64{} statistic := map[string]int64{}
t := true t := true
n, err := dao.GetTotalOfProjects(&models.QueryParam{ n, err := dao.GetTotalOfProjects(&models.ProjectQueryParam{
Public: &t, Public: &t,
}) })
if err != nil { if err != nil {
@ -99,7 +99,7 @@ func (s *StatisticAPI) Get() {
log.Errorf("failed to get user %d: %v", s.userID, err) log.Errorf("failed to get user %d: %v", s.userID, err)
s.CustomAbort(http.StatusInternalServerError, "") s.CustomAbort(http.StatusInternalServerError, "")
} }
n, err := dao.GetTotalOfProjects(&models.QueryParam{ n, err := dao.GetTotalOfProjects(&models.ProjectQueryParam{
Member: &models.Member{ Member: &models.Member{
Name: user.Username, Name: user.Username,
}, },

View File

@ -107,7 +107,7 @@ func (p *ProjectManager) GetRoles(username string, projectIDOrName interface{})
// GetPublic returns all public projects // GetPublic returns all public projects
func (p *ProjectManager) GetPublic() ([]*models.Project, error) { func (p *ProjectManager) GetPublic() ([]*models.Project, error) {
t := true t := true
return p.GetAll(&models.QueryParam{ return p.GetAll(&models.ProjectQueryParam{
Public: &t, Public: &t,
}) })
} }
@ -115,7 +115,7 @@ func (p *ProjectManager) GetPublic() ([]*models.Project, error) {
// GetByMember returns all projects which the user is a member of // GetByMember returns all projects which the user is a member of
func (p *ProjectManager) GetByMember(username string) ( func (p *ProjectManager) GetByMember(username string) (
[]*models.Project, error) { []*models.Project, error) {
return p.GetAll(&models.QueryParam{ return p.GetAll(&models.ProjectQueryParam{
Member: &models.Member{ Member: &models.Member{
Name: username, Name: username,
}, },
@ -190,13 +190,13 @@ func (p *ProjectManager) Update(projectIDOrName interface{},
} }
// GetAll returns a project list according to the query parameters // GetAll returns a project list according to the query parameters
func (p *ProjectManager) GetAll(query *models.QueryParam) ( func (p *ProjectManager) GetAll(query *models.ProjectQueryParam) (
[]*models.Project, error) { []*models.Project, error) {
return dao.GetProjects(query) return dao.GetProjects(query)
} }
// GetTotal returns the total count according to the query parameters // GetTotal returns the total count according to the query parameters
func (p *ProjectManager) GetTotal(query *models.QueryParam) ( func (p *ProjectManager) GetTotal(query *models.ProjectQueryParam) (
int64, error) { int64, error) {
return dao.GetTotalOfProjects(query) return dao.GetTotalOfProjects(query)
} }

View File

@ -229,14 +229,14 @@ func TestGetTotal(t *testing.T) {
defer pm.Delete(id) defer pm.Delete(id)
// get by name // get by name
total, err := pm.GetTotal(&models.QueryParam{ total, err := pm.GetTotal(&models.ProjectQueryParam{
Name: "get_total_test", Name: "get_total_test",
}) })
assert.Nil(t, err) assert.Nil(t, err)
assert.Equal(t, int64(1), total) assert.Equal(t, int64(1), total)
// get by owner // get by owner
total, err = pm.GetTotal(&models.QueryParam{ total, err = pm.GetTotal(&models.ProjectQueryParam{
Owner: "admin", Owner: "admin",
}) })
assert.Nil(t, err) assert.Nil(t, err)
@ -244,7 +244,7 @@ func TestGetTotal(t *testing.T) {
// get by public // get by public
value := true value := true
total, err = pm.GetTotal(&models.QueryParam{ total, err = pm.GetTotal(&models.ProjectQueryParam{
Public: &value, Public: &value,
}) })
assert.Nil(t, err) assert.Nil(t, err)
@ -263,14 +263,14 @@ func TestGetAll(t *testing.T) {
defer pm.Delete(id) defer pm.Delete(id)
// get by name // get by name
projects, err := pm.GetAll(&models.QueryParam{ projects, err := pm.GetAll(&models.ProjectQueryParam{
Name: "get_all_test", Name: "get_all_test",
}) })
assert.Nil(t, err) assert.Nil(t, err)
assert.Equal(t, id, projects[0].ProjectID) assert.Equal(t, id, projects[0].ProjectID)
// get by owner // get by owner
projects, err = pm.GetAll(&models.QueryParam{ projects, err = pm.GetAll(&models.ProjectQueryParam{
Owner: "admin", Owner: "admin",
}) })
assert.Nil(t, err) assert.Nil(t, err)
@ -285,7 +285,7 @@ func TestGetAll(t *testing.T) {
// get by public // get by public
value := true value := true
projects, err = pm.GetAll(&models.QueryParam{ projects, err = pm.GetAll(&models.ProjectQueryParam{
Public: &value, Public: &value,
}) })
assert.Nil(t, err) assert.Nil(t, err)

View File

@ -33,7 +33,7 @@ type ProjectManager interface {
Delete(projectIDOrName interface{}) error Delete(projectIDOrName interface{}) error
Update(projectIDOrName interface{}, project *models.Project) error Update(projectIDOrName interface{}, project *models.Project) error
// GetAll returns a project list according to the query parameters // GetAll returns a project list according to the query parameters
GetAll(query *models.QueryParam) ([]*models.Project, error) GetAll(query *models.ProjectQueryParam) ([]*models.Project, error)
// GetTotal returns the total count according to the query parameters // GetTotal returns the total count according to the query parameters
GetTotal(query *models.QueryParam) (int64, error) GetTotal(query *models.ProjectQueryParam) (int64, error)
} }

View File

@ -67,7 +67,7 @@ export class AccessLogDefaultService extends AccessLogService {
url = '/api/logs'; url = '/api/logs';
} }
return this.http.get(url+`?lines=${lines}`, HTTP_JSON_OPTIONS).toPromise() return this.http.get(url+`?page_size=${lines}`, HTTP_JSON_OPTIONS).toPromise()
.then(response => response.json() as AccessLog[]) .then(response => response.json() as AccessLog[])
.catch(error => Promise.reject(error)); .catch(error => Promise.reject(error));
} }

View File

@ -49,7 +49,7 @@ export class AuditLogService {
} }
getRecentLogs(lines: number): Observable<AuditLog[]> { getRecentLogs(lines: number): Observable<AuditLog[]> {
return this.http.get(logEndpoint + "?lines=" + lines, this.httpOptions) return this.http.get(logEndpoint + "?page_size=" + lines, this.httpOptions)
.map(response => response.json() as AuditLog[]) .map(response => response.json() as AuditLog[])
.catch(error => Observable.throw(error)); .catch(error => Observable.throw(error));
} }