refactor: Refactor audit logs count to support filtering (#4113)

This commit is contained in:
Bruno Quaresma
2022-09-19 14:08:25 -03:00
committed by GitHub
parent 6f82ad09c8
commit adad347902
9 changed files with 164 additions and 13 deletions

View File

@ -65,7 +65,20 @@ func (api *API) auditLogCount(rw http.ResponseWriter, r *http.Request) {
return
}
count, err := api.Database.GetAuditLogCount(ctx)
queryStr := r.URL.Query().Get("q")
filter, errs := auditSearchQuery(queryStr)
if len(errs) > 0 {
httpapi.Write(rw, http.StatusBadRequest, codersdk.Response{
Message: "Invalid audit search query.",
Validations: errs,
})
return
}
count, err := api.Database.GetAuditLogCount(ctx, database.GetAuditLogCountParams{
ResourceType: filter.ResourceType,
Action: filter.Action,
})
if err != nil {
httpapi.InternalServerError(rw, err)
return

View File

@ -23,7 +23,7 @@ func TestAuditLogs(t *testing.T) {
err := client.CreateTestAuditLog(ctx, codersdk.CreateTestAuditLogRequest{})
require.NoError(t, err)
count, err := client.AuditLogCount(ctx)
count, err := client.AuditLogCount(ctx, codersdk.AuditLogCountRequest{})
require.NoError(t, err)
alogs, err := client.AuditLogs(ctx, codersdk.AuditLogsRequest{
@ -41,7 +41,7 @@ func TestAuditLogs(t *testing.T) {
func TestAuditLogsFilter(t *testing.T) {
t.Parallel()
t.Run("FilterByResourceType", func(t *testing.T) {
t.Run("Filter", func(t *testing.T) {
t.Parallel()
ctx := context.Background()
@ -110,3 +110,73 @@ func TestAuditLogsFilter(t *testing.T) {
}
})
}
func TestAuditLogCountFilter(t *testing.T) {
t.Parallel()
t.Run("Filter", func(t *testing.T) {
t.Parallel()
ctx := context.Background()
client := coderdtest.New(t, nil)
_ = coderdtest.CreateFirstUser(t, client)
// Create two logs with "Create"
err := client.CreateTestAuditLog(ctx, codersdk.CreateTestAuditLogRequest{
Action: codersdk.AuditActionCreate,
ResourceType: codersdk.ResourceTypeTemplate,
})
require.NoError(t, err)
err = client.CreateTestAuditLog(ctx, codersdk.CreateTestAuditLogRequest{
Action: codersdk.AuditActionCreate,
ResourceType: codersdk.ResourceTypeUser,
})
require.NoError(t, err)
// Create one log with "Delete"
err = client.CreateTestAuditLog(ctx, codersdk.CreateTestAuditLogRequest{
Action: codersdk.AuditActionDelete,
ResourceType: codersdk.ResourceTypeUser,
})
require.NoError(t, err)
// Test cases
testCases := []struct {
Name string
SearchQuery string
ExpectedResult int64
}{
{
Name: "FilterByCreateAction",
SearchQuery: "action:create",
ExpectedResult: 2,
},
{
Name: "FilterByDeleteAction",
SearchQuery: "action:delete",
ExpectedResult: 1,
},
{
Name: "FilterByUserResourceType",
SearchQuery: "resource_type:user",
ExpectedResult: 2,
},
{
Name: "FilterByTemplateResourceType",
SearchQuery: "resource_type:template",
ExpectedResult: 1,
},
}
for _, testCase := range testCases {
t.Run(testCase.Name, func(t *testing.T) {
t.Parallel()
response, err := client.AuditLogCount(ctx, codersdk.AuditLogCountRequest{
SearchQuery: testCase.SearchQuery,
})
require.NoError(t, err, "fetch audit logs count")
require.Equal(t, response.Count, testCase.ExpectedResult, "expected audit logs count returned")
})
}
})
}

View File

@ -2402,11 +2402,25 @@ func (q *fakeQuerier) GetAuditLogsOffset(ctx context.Context, arg database.GetAu
return logs, nil
}
func (q *fakeQuerier) GetAuditLogCount(_ context.Context) (int64, error) {
func (q *fakeQuerier) GetAuditLogCount(_ context.Context, arg database.GetAuditLogCountParams) (int64, error) {
q.mutex.RLock()
defer q.mutex.RUnlock()
return int64(len(q.auditLogs)), nil
logs := make([]database.AuditLog, 0)
for _, alog := range q.auditLogs {
if arg.Action != "" && !strings.Contains(string(alog.Action), arg.Action) {
continue
}
if arg.ResourceType != "" && !strings.Contains(string(alog.ResourceType), arg.ResourceType) {
continue
}
logs = append(logs, alog)
}
return int64(len(logs)), nil
}
func (q *fakeQuerier) InsertAuditLog(_ context.Context, arg database.InsertAuditLogParams) (database.AuditLog, error) {

View File

@ -27,7 +27,7 @@ type querier interface {
GetAPIKeyByID(ctx context.Context, id string) (APIKey, error)
GetAPIKeysLastUsedAfter(ctx context.Context, lastUsed time.Time) ([]APIKey, error)
GetActiveUserCount(ctx context.Context) (int64, error)
GetAuditLogCount(ctx context.Context) (int64, error)
GetAuditLogCount(ctx context.Context, arg GetAuditLogCountParams) (int64, error)
// GetAuditLogsBefore retrieves `row_limit` number of audit logs before the provided
// ID.
GetAuditLogsOffset(ctx context.Context, arg GetAuditLogsOffsetParams) ([]GetAuditLogsOffsetRow, error)

View File

@ -292,10 +292,28 @@ SELECT
COUNT(*) as count
FROM
audit_logs
WHERE
-- Filter resource_type
CASE
WHEN $1 :: text != '' THEN
resource_type = $1 :: resource_type
ELSE true
END
-- Filter action
AND CASE
WHEN $2 :: text != '' THEN
action = $2 :: audit_action
ELSE true
END
`
func (q *sqlQuerier) GetAuditLogCount(ctx context.Context) (int64, error) {
row := q.db.QueryRowContext(ctx, getAuditLogCount)
type GetAuditLogCountParams struct {
ResourceType string `db:"resource_type" json:"resource_type"`
Action string `db:"action" json:"action"`
}
func (q *sqlQuerier) GetAuditLogCount(ctx context.Context, arg GetAuditLogCountParams) (int64, error) {
row := q.db.QueryRowContext(ctx, getAuditLogCount, arg.ResourceType, arg.Action)
var count int64
err := row.Scan(&count)
return count, err

View File

@ -37,7 +37,20 @@ OFFSET
SELECT
COUNT(*) as count
FROM
audit_logs;
audit_logs
WHERE
-- Filter resource_type
CASE
WHEN @resource_type :: text != '' THEN
resource_type = @resource_type :: resource_type
ELSE true
END
-- Filter action
AND CASE
WHEN @action :: text != '' THEN
action = @action :: audit_action
ELSE true
END;
-- name: InsertAuditLog :one
INSERT INTO