Files
RenewCMS/internal/infrastructure/useCases/activityQuery.go
T

145 lines
3.4 KiB
Go

package useCases
import (
"RenewCMS/internal/domain/audit"
"time"
)
const (
activityRangeToday = "today"
activityRange7Days = "7d"
activityRange30Days = "30d"
activityRangeCustom = "custom"
activityDateLayout = "2006-01-02"
activityDefaultSize = 25
outcomeAll = "all"
securityOnly = "true"
)
var activityPageSizes = []int{25, 50, 100}
type ActivityQuery struct {
Q string
Actor string
Category string
Outcome string
Security string
Range string
From string
To string
Page int
Size int
}
type activityWindow struct {
from time.Time
to time.Time
explicit bool
}
func resolveActivityWindow(query ActivityQuery, now time.Time) (activityWindow, error) {
utc := now.UTC()
end := utc.Add(time.Second)
switch query.Range {
case "", activityRange7Days:
return activityWindow{from: end.AddDate(0, 0, -7), to: end}, nil
case activityRange30Days:
return activityWindow{from: end.AddDate(0, 0, -30), to: end}, nil
case activityRangeToday:
return activityWindow{from: time.Date(utc.Year(), utc.Month(), utc.Day(), 0, 0, 0, 0, time.UTC), to: end}, nil
case activityRangeCustom:
return resolveCustomWindow(query)
default:
return activityWindow{}, audit.ErrInvalidFilter
}
}
func resolveCustomWindow(query ActivityQuery) (activityWindow, error) {
from, err := time.Parse(activityDateLayout, query.From)
if err != nil {
return activityWindow{}, audit.ErrInvalidFilter
}
last, err := time.Parse(activityDateLayout, query.To)
if err != nil || last.Before(from) {
return activityWindow{}, audit.ErrInvalidFilter
}
return activityWindow{from: from, to: last.AddDate(0, 0, 1), explicit: true}, nil
}
func buildActivityFilter(query ActivityQuery, now time.Time) (audit.Filter, error) {
window, err := resolveActivityWindow(query, now)
if err != nil {
return audit.Filter{}, err
}
filter := audit.Filter{Query: query.Q, From: &window.from}
if window.explicit {
filter.To = &window.to
}
if filter.Outcome, err = parseActivityOutcome(query.Outcome); err != nil {
return audit.Filter{}, err
}
if filter.Security, err = parseActivitySecurity(query.Security); err != nil {
return audit.Filter{}, err
}
if query.Category != "" {
if filter.Category, err = audit.ParseCategory(query.Category); err != nil {
return audit.Filter{}, err
}
}
if query.Actor != "" {
actor, err := audit.ParseActor(query.Actor)
if err != nil {
return audit.Filter{}, err
}
filter.Actor = &actor
}
return filter, nil
}
func parseActivityOutcome(raw string) (audit.Outcome, error) {
switch raw {
case "", outcomeAll:
return "", nil
case string(audit.OutcomeSuccess):
return audit.OutcomeSuccess, nil
case string(audit.OutcomeFailed):
return audit.OutcomeFailed, nil
default:
return "", audit.ErrInvalidFilter
}
}
func parseActivitySecurity(raw string) (bool, error) {
switch raw {
case "":
return false, nil
case securityOnly:
return true, nil
default:
return false, audit.ErrInvalidFilter
}
}
func resolveActivityPaging(query ActivityQuery) (int, int, error) {
page, size := query.Page, query.Size
if page == 0 {
page = 1
}
if size == 0 {
size = activityDefaultSize
}
if page < 1 || !containsInt(activityPageSizes, size) {
return 0, 0, audit.ErrInvalidFilter
}
return page, size, nil
}
func containsInt(values []int, wanted int) bool {
for _, value := range values {
if value == wanted {
return true
}
}
return false
}