mirror of
https://github.com/muety/wakapi.git
synced 2023-08-10 21:12:56 +03:00
refactor: introduce repositories as an additional layer of abstraction to allow for better testability
This commit is contained in:
32
main.go
32
main.go
@ -4,6 +4,7 @@ import (
|
|||||||
"github.com/gorilla/handlers"
|
"github.com/gorilla/handlers"
|
||||||
conf "github.com/muety/wakapi/config"
|
conf "github.com/muety/wakapi/config"
|
||||||
"github.com/muety/wakapi/migrations/common"
|
"github.com/muety/wakapi/migrations/common"
|
||||||
|
"github.com/muety/wakapi/repositories"
|
||||||
"log"
|
"log"
|
||||||
"net/http"
|
"net/http"
|
||||||
"strconv"
|
"strconv"
|
||||||
@ -27,6 +28,15 @@ var (
|
|||||||
config *conf.Config
|
config *conf.Config
|
||||||
)
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
aliasRepository *repositories.AliasRepository
|
||||||
|
heartbeatRepository *repositories.HeartbeatRepository
|
||||||
|
userRepository *repositories.UserRepository
|
||||||
|
customRuleRepository *repositories.CustomRuleRepository
|
||||||
|
summaryRepository *repositories.SummaryRepository
|
||||||
|
keyValueRepository *repositories.KeyValueRepository
|
||||||
|
)
|
||||||
|
|
||||||
var (
|
var (
|
||||||
aliasService *services.AliasService
|
aliasService *services.AliasService
|
||||||
heartbeatService *services.HeartbeatService
|
heartbeatService *services.HeartbeatService
|
||||||
@ -71,14 +81,22 @@ func main() {
|
|||||||
runDatabaseMigrations()
|
runDatabaseMigrations()
|
||||||
runCustomMigrations()
|
runCustomMigrations()
|
||||||
|
|
||||||
|
// Repositories
|
||||||
|
aliasRepository = repositories.NewAliasRepository(db)
|
||||||
|
heartbeatRepository = repositories.NewHeartbeatRepository(db)
|
||||||
|
userRepository = repositories.NewUserRepository(db)
|
||||||
|
customRuleRepository = repositories.NewCustomRuleRepository(db)
|
||||||
|
summaryRepository = repositories.NewSummaryRepository(db)
|
||||||
|
keyValueRepository = repositories.NewKeyValueRepository(db)
|
||||||
|
|
||||||
// Services
|
// Services
|
||||||
aliasService = services.NewAliasService(db)
|
aliasService = services.NewAliasService(aliasRepository)
|
||||||
heartbeatService = services.NewHeartbeatService(db)
|
heartbeatService = services.NewHeartbeatService(heartbeatRepository)
|
||||||
userService = services.NewUserService(db)
|
userService = services.NewUserService(userRepository)
|
||||||
customRuleService = services.NewCustomRuleService(db)
|
customRuleService = services.NewCustomRuleService(customRuleRepository)
|
||||||
summaryService = services.NewSummaryService(db, heartbeatService, aliasService, customRuleService)
|
summaryService = services.NewSummaryService(summaryRepository, heartbeatService, aliasService, customRuleService)
|
||||||
aggregationService = services.NewAggregationService(db, userService, summaryService, heartbeatService)
|
aggregationService = services.NewAggregationService(userService, summaryService, heartbeatService)
|
||||||
keyValueService = services.NewKeyValueService(db)
|
keyValueService = services.NewKeyValueService(keyValueRepository)
|
||||||
|
|
||||||
// Aggregate heartbeats to summaries and persist them
|
// Aggregate heartbeats to summaries and persist them
|
||||||
go aggregationService.Schedule()
|
go aggregationService.Schedule()
|
||||||
|
24
repositories/alias.go
Normal file
24
repositories/alias.go
Normal file
@ -0,0 +1,24 @@
|
|||||||
|
package repositories
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/jinzhu/gorm"
|
||||||
|
"github.com/muety/wakapi/models"
|
||||||
|
)
|
||||||
|
|
||||||
|
type AliasRepository struct {
|
||||||
|
db *gorm.DB
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewAliasRepository(db *gorm.DB) *AliasRepository {
|
||||||
|
return &AliasRepository{db: db}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *AliasRepository) GetByUser(userId string) ([]*models.Alias, error) {
|
||||||
|
var aliases []*models.Alias
|
||||||
|
if err := r.db.
|
||||||
|
Where(&models.Alias{UserID: userId}).
|
||||||
|
Find(&aliases).Error; err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return aliases, nil
|
||||||
|
}
|
46
repositories/custom_rule.go
Normal file
46
repositories/custom_rule.go
Normal file
@ -0,0 +1,46 @@
|
|||||||
|
package repositories
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/jinzhu/gorm"
|
||||||
|
"github.com/muety/wakapi/models"
|
||||||
|
)
|
||||||
|
|
||||||
|
type CustomRuleRepository struct {
|
||||||
|
db *gorm.DB
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewCustomRuleRepository(db *gorm.DB) *CustomRuleRepository {
|
||||||
|
return &CustomRuleRepository{db: db}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *CustomRuleRepository) GetById(id uint) (*models.CustomRule, error) {
|
||||||
|
rule := &models.CustomRule{}
|
||||||
|
if err := r.db.Where(&models.CustomRule{ID: id}).First(rule).Error; err != nil {
|
||||||
|
return rule, err
|
||||||
|
}
|
||||||
|
return rule, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *CustomRuleRepository) GetByUser(userId string) ([]*models.CustomRule, error) {
|
||||||
|
var rules []*models.CustomRule
|
||||||
|
if err := r.db.
|
||||||
|
Where(&models.CustomRule{UserID: userId}).
|
||||||
|
Find(&rules).Error; err != nil {
|
||||||
|
return rules, err
|
||||||
|
}
|
||||||
|
return rules, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *CustomRuleRepository) Insert(rule *models.CustomRule) (*models.CustomRule, error) {
|
||||||
|
result := r.db.Create(rule)
|
||||||
|
if err := result.Error; err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return rule, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *CustomRuleRepository) Delete(id uint) error {
|
||||||
|
return r.db.
|
||||||
|
Where("id = ?", id).
|
||||||
|
Delete(models.CustomRule{}).Error
|
||||||
|
}
|
64
repositories/heartbeart.go
Normal file
64
repositories/heartbeart.go
Normal file
@ -0,0 +1,64 @@
|
|||||||
|
package repositories
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/jinzhu/gorm"
|
||||||
|
"github.com/muety/wakapi/models"
|
||||||
|
gormbulk "github.com/t-tiger/gorm-bulk-insert"
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
|
type HeartbeatRepository struct {
|
||||||
|
db *gorm.DB
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewHeartbeatRepository(db *gorm.DB) *HeartbeatRepository {
|
||||||
|
return &HeartbeatRepository{db: db}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *HeartbeatRepository) InsertBatch(heartbeats []*models.Heartbeat) error {
|
||||||
|
var batch []interface{}
|
||||||
|
for _, h := range heartbeats {
|
||||||
|
batch = append(batch, *h)
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := gormbulk.BulkInsert(r.db, batch, 3000); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *HeartbeatRepository) GetAllWithin(from, to time.Time, user *models.User) ([]*models.Heartbeat, error) {
|
||||||
|
var heartbeats []*models.Heartbeat
|
||||||
|
if err := r.db.
|
||||||
|
Where(&models.Heartbeat{UserID: user.ID}).
|
||||||
|
Where("time >= ?", from).
|
||||||
|
Where("time < ?", to).
|
||||||
|
Order("time asc").
|
||||||
|
Find(&heartbeats).Error; err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return heartbeats, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Will return *models.Heartbeat object with only user_id and time fields filled
|
||||||
|
func (r *HeartbeatRepository) GetFirstByUsers(userIds []string) ([]*models.Heartbeat, error) {
|
||||||
|
var heartbeats []*models.Heartbeat
|
||||||
|
if err := r.db.
|
||||||
|
Table("heartbeats").
|
||||||
|
Select("user_id, min(time) as time").
|
||||||
|
Where("user_id IN (?)", userIds).
|
||||||
|
Group("user_id").
|
||||||
|
Scan(&heartbeats).Error; err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return heartbeats, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *HeartbeatRepository) DeleteBefore(t time.Time) error {
|
||||||
|
if err := r.db.
|
||||||
|
Where("time <= ?", t).
|
||||||
|
Delete(models.Heartbeat{}).Error; err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
58
repositories/key_value.go
Normal file
58
repositories/key_value.go
Normal file
@ -0,0 +1,58 @@
|
|||||||
|
package repositories
|
||||||
|
|
||||||
|
import (
|
||||||
|
"errors"
|
||||||
|
"github.com/jinzhu/gorm"
|
||||||
|
"github.com/muety/wakapi/models"
|
||||||
|
)
|
||||||
|
|
||||||
|
type KeyValueRepository struct {
|
||||||
|
db *gorm.DB
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewKeyValueRepository(db *gorm.DB) *KeyValueRepository {
|
||||||
|
return &KeyValueRepository{db: db}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *KeyValueRepository) GetString(key string) (*models.KeyStringValue, error) {
|
||||||
|
kv := &models.KeyStringValue{}
|
||||||
|
if err := r.db.
|
||||||
|
Where(&models.KeyStringValue{Key: key}).
|
||||||
|
First(&kv).Error; err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return kv, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *KeyValueRepository) PutString(kv *models.KeyStringValue) error {
|
||||||
|
result := r.db.
|
||||||
|
Where(&models.KeyStringValue{Key: kv.Key}).
|
||||||
|
Assign(kv).
|
||||||
|
FirstOrCreate(kv)
|
||||||
|
|
||||||
|
if err := result.Error; err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
if result.RowsAffected != 1 {
|
||||||
|
return errors.New("nothing updated")
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *KeyValueRepository) DeleteString(key string) error {
|
||||||
|
result := r.db.
|
||||||
|
Delete(&models.KeyStringValue{}, &models.KeyStringValue{Key: key})
|
||||||
|
|
||||||
|
if err := result.Error; err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
if result.RowsAffected != 1 {
|
||||||
|
return errors.New("nothing deleted")
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
53
repositories/summary.go
Normal file
53
repositories/summary.go
Normal file
@ -0,0 +1,53 @@
|
|||||||
|
package repositories
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/jinzhu/gorm"
|
||||||
|
"github.com/muety/wakapi/models"
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
|
type SummaryRepository struct {
|
||||||
|
db *gorm.DB
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewSummaryRepository(db *gorm.DB) *SummaryRepository {
|
||||||
|
return &SummaryRepository{db: db}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *SummaryRepository) Insert(summary *models.Summary) error {
|
||||||
|
if err := r.db.Create(summary).Error; err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *SummaryRepository) GetByUserWithin(user *models.User, from, to time.Time) ([]*models.Summary, error) {
|
||||||
|
var summaries []*models.Summary
|
||||||
|
if err := r.db.
|
||||||
|
Where(&models.Summary{UserID: user.ID}).
|
||||||
|
Where("from_time >= ?", from).
|
||||||
|
Where("to_time <= ?", to).
|
||||||
|
Order("from_time asc").
|
||||||
|
Preload("Projects", "type = ?", models.SummaryProject).
|
||||||
|
Preload("Languages", "type = ?", models.SummaryLanguage).
|
||||||
|
Preload("Editors", "type = ?", models.SummaryEditor).
|
||||||
|
Preload("OperatingSystems", "type = ?", models.SummaryOS).
|
||||||
|
Preload("Machines", "type = ?", models.SummaryMachine).
|
||||||
|
Find(&summaries).Error; err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return summaries, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Will return *models.Index objects with only user_id and to_time filled
|
||||||
|
func (r *SummaryRepository) GetLatestByUser() ([]*models.Summary, error) {
|
||||||
|
var summaries []*models.Summary
|
||||||
|
if err := r.db.
|
||||||
|
Table("summaries").
|
||||||
|
Select("user_id, max(to_time) as to_time").
|
||||||
|
Group("user_id").
|
||||||
|
Scan(&summaries).Error; err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return summaries, nil
|
||||||
|
}
|
80
repositories/user.go
Normal file
80
repositories/user.go
Normal file
@ -0,0 +1,80 @@
|
|||||||
|
package repositories
|
||||||
|
|
||||||
|
import (
|
||||||
|
"errors"
|
||||||
|
"github.com/jinzhu/gorm"
|
||||||
|
"github.com/muety/wakapi/models"
|
||||||
|
)
|
||||||
|
|
||||||
|
type UserRepository struct {
|
||||||
|
db *gorm.DB
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewUserRepository(db *gorm.DB) *UserRepository {
|
||||||
|
return &UserRepository{db: db}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *UserRepository) GetById(userId string) (*models.User, error) {
|
||||||
|
u := &models.User{}
|
||||||
|
if err := r.db.Where(&models.User{ID: userId}).First(u).Error; err != nil {
|
||||||
|
return u, err
|
||||||
|
}
|
||||||
|
return u, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *UserRepository) GetByApiKey(key string) (*models.User, error) {
|
||||||
|
u := &models.User{}
|
||||||
|
if err := r.db.Where(&models.User{ApiKey: key}).First(u).Error; err != nil {
|
||||||
|
return u, err
|
||||||
|
}
|
||||||
|
return u, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *UserRepository) GetAll() ([]*models.User, error) {
|
||||||
|
var users []*models.User
|
||||||
|
if err := r.db.
|
||||||
|
Table("users").
|
||||||
|
Find(&users).Error; err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return users, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *UserRepository) InsertOrGet(user *models.User) (*models.User, bool, error) {
|
||||||
|
result := r.db.FirstOrCreate(user, &models.User{ID: user.ID})
|
||||||
|
if err := result.Error; err != nil {
|
||||||
|
return nil, false, err
|
||||||
|
}
|
||||||
|
|
||||||
|
if result.RowsAffected == 1 {
|
||||||
|
return user, true, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
return user, false, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *UserRepository) Update(user *models.User) (*models.User, error) {
|
||||||
|
result := r.db.Model(&models.User{}).Updates(user)
|
||||||
|
if err := result.Error; err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
if result.RowsAffected != 1 {
|
||||||
|
return nil, errors.New("nothing updated")
|
||||||
|
}
|
||||||
|
|
||||||
|
return user, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *UserRepository) UpdateField(user *models.User, key string, value interface{}) (*models.User, error) {
|
||||||
|
result := r.db.Model(user).Update(key, value)
|
||||||
|
if err := result.Error; err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
if result.RowsAffected != 1 {
|
||||||
|
return nil, errors.New("nothing updated")
|
||||||
|
}
|
||||||
|
|
||||||
|
return user, nil
|
||||||
|
}
|
@ -124,7 +124,13 @@ func (h *SettingsHandler) DeleteCustomRule(w http.ResponseWriter, r *http.Reques
|
|||||||
ID: uint(ruleId),
|
ID: uint(ruleId),
|
||||||
UserID: user.ID,
|
UserID: user.ID,
|
||||||
}
|
}
|
||||||
h.customRuleSrvc.Delete(rule)
|
|
||||||
|
err = h.customRuleSrvc.Delete(rule)
|
||||||
|
if err != nil {
|
||||||
|
respondAlert(w, "internal server error", "", conf.SettingsTemplate, http.StatusInternalServerError)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
msg := url.QueryEscape("Custom rule deleted successfully.")
|
msg := url.QueryEscape("Custom rule deleted successfully.")
|
||||||
|
|
||||||
http.Redirect(w, r, fmt.Sprintf("%s/settings?success=%s", h.config.Server.BasePath, msg), http.StatusFound)
|
http.Redirect(w, r, fmt.Sprintf("%s/settings?success=%s", h.config.Server.BasePath, msg), http.StatusFound)
|
||||||
|
@ -7,7 +7,6 @@ import (
|
|||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/jasonlvhit/gocron"
|
"github.com/jasonlvhit/gocron"
|
||||||
"github.com/jinzhu/gorm"
|
|
||||||
"github.com/muety/wakapi/models"
|
"github.com/muety/wakapi/models"
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -16,20 +15,18 @@ const (
|
|||||||
)
|
)
|
||||||
|
|
||||||
type AggregationService struct {
|
type AggregationService struct {
|
||||||
Config *config.Config
|
config *config.Config
|
||||||
Db *gorm.DB
|
userService *UserService
|
||||||
UserService *UserService
|
summaryService *SummaryService
|
||||||
SummaryService *SummaryService
|
heartbeatService *HeartbeatService
|
||||||
HeartbeatService *HeartbeatService
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewAggregationService(db *gorm.DB, userService *UserService, summaryService *SummaryService, heartbeatService *HeartbeatService) *AggregationService {
|
func NewAggregationService(userService *UserService, summaryService *SummaryService, heartbeatService *HeartbeatService) *AggregationService {
|
||||||
return &AggregationService{
|
return &AggregationService{
|
||||||
Config: config.Get(),
|
config: config.Get(),
|
||||||
Db: db,
|
userService: userService,
|
||||||
UserService: userService,
|
summaryService: summaryService,
|
||||||
SummaryService: summaryService,
|
heartbeatService: heartbeatService,
|
||||||
HeartbeatService: heartbeatService,
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -50,20 +47,20 @@ func (srv *AggregationService) Schedule() {
|
|||||||
go srv.summaryWorker(jobs, summaries)
|
go srv.summaryWorker(jobs, summaries)
|
||||||
}
|
}
|
||||||
|
|
||||||
for i := 0; i < int(srv.Config.Db.MaxConn); i++ {
|
for i := 0; i < int(srv.config.Db.MaxConn); i++ {
|
||||||
go srv.persistWorker(summaries)
|
go srv.persistWorker(summaries)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Run once initially
|
// Run once initially
|
||||||
srv.trigger(jobs)
|
srv.trigger(jobs)
|
||||||
|
|
||||||
gocron.Every(1).Day().At(srv.Config.App.AggregationTime).Do(srv.trigger, jobs)
|
gocron.Every(1).Day().At(srv.config.App.AggregationTime).Do(srv.trigger, jobs)
|
||||||
<-gocron.Start()
|
<-gocron.Start()
|
||||||
}
|
}
|
||||||
|
|
||||||
func (srv *AggregationService) summaryWorker(jobs <-chan *AggregationJob, summaries chan<- *models.Summary) {
|
func (srv *AggregationService) summaryWorker(jobs <-chan *AggregationJob, summaries chan<- *models.Summary) {
|
||||||
for job := range jobs {
|
for job := range jobs {
|
||||||
if summary, err := srv.SummaryService.Construct(job.From, job.To, &models.User{ID: job.UserID}, true); err != nil {
|
if summary, err := srv.summaryService.Construct(job.From, job.To, &models.User{ID: job.UserID}, true); err != nil {
|
||||||
log.Printf("Failed to generate summary (%v, %v, %s) – %v.\n", job.From, job.To, job.UserID, err)
|
log.Printf("Failed to generate summary (%v, %v, %s) – %v.\n", job.From, job.To, job.UserID, err)
|
||||||
} else {
|
} else {
|
||||||
log.Printf("Successfully generated summary (%v, %v, %s).\n", job.From, job.To, job.UserID)
|
log.Printf("Successfully generated summary (%v, %v, %s).\n", job.From, job.To, job.UserID)
|
||||||
@ -74,7 +71,7 @@ func (srv *AggregationService) summaryWorker(jobs <-chan *AggregationJob, summar
|
|||||||
|
|
||||||
func (srv *AggregationService) persistWorker(summaries <-chan *models.Summary) {
|
func (srv *AggregationService) persistWorker(summaries <-chan *models.Summary) {
|
||||||
for summary := range summaries {
|
for summary := range summaries {
|
||||||
if err := srv.SummaryService.Insert(summary); err != nil {
|
if err := srv.summaryService.Insert(summary); err != nil {
|
||||||
log.Printf("Failed to save summary (%v, %v, %s) – %v.\n", summary.UserID, summary.FromTime, summary.ToTime, err)
|
log.Printf("Failed to save summary (%v, %v, %s) – %v.\n", summary.UserID, summary.FromTime, summary.ToTime, err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -83,13 +80,13 @@ func (srv *AggregationService) persistWorker(summaries <-chan *models.Summary) {
|
|||||||
func (srv *AggregationService) trigger(jobs chan<- *AggregationJob) error {
|
func (srv *AggregationService) trigger(jobs chan<- *AggregationJob) error {
|
||||||
log.Println("Generating summaries.")
|
log.Println("Generating summaries.")
|
||||||
|
|
||||||
users, err := srv.UserService.GetAll()
|
users, err := srv.userService.GetAll()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Println(err)
|
log.Println(err)
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
latestSummaries, err := srv.SummaryService.GetLatestByUser()
|
latestSummaries, err := srv.summaryService.GetLatestByUser()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Println(err)
|
log.Println(err)
|
||||||
return err
|
return err
|
||||||
@ -107,7 +104,7 @@ func (srv *AggregationService) trigger(jobs chan<- *AggregationJob) error {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
firstHeartbeats, err := srv.HeartbeatService.GetFirstUserHeartbeats(missingUserIDs)
|
firstHeartbeats, err := srv.heartbeatService.GetFirstUserHeartbeats(missingUserIDs)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Println(err)
|
log.Println(err)
|
||||||
return err
|
return err
|
||||||
|
@ -2,36 +2,32 @@ package services
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"github.com/muety/wakapi/config"
|
"github.com/muety/wakapi/config"
|
||||||
|
"github.com/muety/wakapi/repositories"
|
||||||
"sync"
|
"sync"
|
||||||
|
|
||||||
"github.com/jinzhu/gorm"
|
|
||||||
"github.com/muety/wakapi/models"
|
"github.com/muety/wakapi/models"
|
||||||
)
|
)
|
||||||
|
|
||||||
type AliasService struct {
|
type AliasService struct {
|
||||||
Config *config.Config
|
config *config.Config
|
||||||
Db *gorm.DB
|
repository *repositories.AliasRepository
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewAliasService(db *gorm.DB) *AliasService {
|
func NewAliasService(aliasRepo *repositories.AliasRepository) *AliasService {
|
||||||
return &AliasService{
|
return &AliasService{
|
||||||
Config: config.Get(),
|
config: config.Get(),
|
||||||
Db: db,
|
repository: aliasRepo,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
var userAliases sync.Map
|
var userAliases sync.Map
|
||||||
|
|
||||||
func (srv *AliasService) LoadUserAliases(userId string) error {
|
func (srv *AliasService) LoadUserAliases(userId string) error {
|
||||||
var aliases []*models.Alias
|
aliases, err := srv.repository.GetByUser(userId)
|
||||||
if err := srv.Db.
|
if err == nil {
|
||||||
Where(&models.Alias{UserID: userId}).
|
|
||||||
Find(&aliases).Error; err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
userAliases.Store(userId, aliases)
|
userAliases.Store(userId, aliases)
|
||||||
return nil
|
}
|
||||||
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
func (srv *AliasService) GetAliasOrDefault(userId string, summaryType uint8, value string) (string, error) {
|
func (srv *AliasService) GetAliasOrDefault(userId string, summaryType uint8, value string) (string, error) {
|
||||||
|
@ -1,64 +1,56 @@
|
|||||||
package services
|
package services
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"github.com/jinzhu/gorm"
|
|
||||||
"github.com/muety/wakapi/config"
|
"github.com/muety/wakapi/config"
|
||||||
"github.com/muety/wakapi/models"
|
"github.com/muety/wakapi/models"
|
||||||
|
"github.com/muety/wakapi/repositories"
|
||||||
"github.com/patrickmn/go-cache"
|
"github.com/patrickmn/go-cache"
|
||||||
"time"
|
"time"
|
||||||
)
|
)
|
||||||
|
|
||||||
type CustomRuleService struct {
|
type CustomRuleService struct {
|
||||||
Config *config.Config
|
config *config.Config
|
||||||
Db *gorm.DB
|
repository *repositories.CustomRuleRepository
|
||||||
cache *cache.Cache
|
cache *cache.Cache
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewCustomRuleService(db *gorm.DB) *CustomRuleService {
|
func NewCustomRuleService(customRuleRepo *repositories.CustomRuleRepository) *CustomRuleService {
|
||||||
return &CustomRuleService{
|
return &CustomRuleService{
|
||||||
Config: config.Get(),
|
config: config.Get(),
|
||||||
Db: db,
|
repository: customRuleRepo,
|
||||||
cache: cache.New(1*time.Hour, 2*time.Hour),
|
cache: cache.New(1*time.Hour, 2*time.Hour),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (srv *CustomRuleService) GetCustomRuleById(customRuleId uint) (*models.CustomRule, error) {
|
func (srv *CustomRuleService) GetCustomRuleById(id uint) (*models.CustomRule, error) {
|
||||||
r := &models.CustomRule{}
|
return srv.repository.GetById(id)
|
||||||
if err := srv.Db.Where(&models.CustomRule{ID: customRuleId}).First(r).Error; err != nil {
|
|
||||||
return r, err
|
|
||||||
}
|
|
||||||
return r, nil
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (srv *CustomRuleService) GetCustomRuleForUser(userId string) ([]*models.CustomRule, error) {
|
func (srv *CustomRuleService) GetCustomRuleForUser(userId string) ([]*models.CustomRule, error) {
|
||||||
var rules []*models.CustomRule
|
|
||||||
if rules, found := srv.cache.Get(userId); found {
|
if rules, found := srv.cache.Get(userId); found {
|
||||||
return rules.([]*models.CustomRule), nil
|
return rules.([]*models.CustomRule), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
if err := srv.Db.
|
rules, err := srv.repository.GetByUser(userId)
|
||||||
Where(&models.CustomRule{UserID: userId}).
|
if err != nil {
|
||||||
Find(&rules).Error; err != nil {
|
return nil, err
|
||||||
return rules, err
|
|
||||||
}
|
}
|
||||||
srv.cache.Set(userId, rules, cache.DefaultExpiration)
|
srv.cache.Set(userId, rules, cache.DefaultExpiration)
|
||||||
return rules, nil
|
return rules, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (srv *CustomRuleService) Create(rule *models.CustomRule) (*models.CustomRule, error) {
|
func (srv *CustomRuleService) Create(rule *models.CustomRule) (*models.CustomRule, error) {
|
||||||
result := srv.Db.Create(rule)
|
result, err := srv.repository.Insert(rule)
|
||||||
if err := result.Error; err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
srv.cache.Delete(rule.UserID)
|
|
||||||
|
|
||||||
return rule, nil
|
srv.cache.Delete(result.UserID)
|
||||||
|
return result, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (srv *CustomRuleService) Delete(rule *models.CustomRule) {
|
func (srv *CustomRuleService) Delete(rule *models.CustomRule) error {
|
||||||
srv.Db.
|
err := srv.repository.Delete(rule.ID)
|
||||||
Where("id = ?", rule.ID).
|
|
||||||
Where("user_id = ?", rule.UserID).
|
|
||||||
Delete(models.CustomRule{})
|
|
||||||
srv.cache.Delete(rule.UserID)
|
srv.cache.Delete(rule.UserID)
|
||||||
|
return err
|
||||||
}
|
}
|
||||||
|
@ -3,78 +3,44 @@ package services
|
|||||||
import (
|
import (
|
||||||
"github.com/jasonlvhit/gocron"
|
"github.com/jasonlvhit/gocron"
|
||||||
"github.com/muety/wakapi/config"
|
"github.com/muety/wakapi/config"
|
||||||
|
"github.com/muety/wakapi/repositories"
|
||||||
"github.com/muety/wakapi/utils"
|
"github.com/muety/wakapi/utils"
|
||||||
"log"
|
"log"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/jinzhu/gorm"
|
|
||||||
"github.com/muety/wakapi/models"
|
"github.com/muety/wakapi/models"
|
||||||
gormbulk "github.com/t-tiger/gorm-bulk-insert"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
TableHeartbeat = "heartbeat"
|
|
||||||
cleanUpInterval = time.Duration(aggregateIntervalDays) * 2 * 24 * time.Hour
|
cleanUpInterval = time.Duration(aggregateIntervalDays) * 2 * 24 * time.Hour
|
||||||
)
|
)
|
||||||
|
|
||||||
type HeartbeatService struct {
|
type HeartbeatService struct {
|
||||||
Config *config.Config
|
config *config.Config
|
||||||
Db *gorm.DB
|
repository *repositories.HeartbeatRepository
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewHeartbeatService(db *gorm.DB) *HeartbeatService {
|
func NewHeartbeatService(heartbeatRepo *repositories.HeartbeatRepository) *HeartbeatService {
|
||||||
return &HeartbeatService{
|
return &HeartbeatService{
|
||||||
Config: config.Get(),
|
config: config.Get(),
|
||||||
Db: db,
|
repository: heartbeatRepo,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (srv *HeartbeatService) InsertBatch(heartbeats []*models.Heartbeat) error {
|
func (srv *HeartbeatService) InsertBatch(heartbeats []*models.Heartbeat) error {
|
||||||
var batch []interface{}
|
return srv.repository.InsertBatch(heartbeats)
|
||||||
for _, h := range heartbeats {
|
|
||||||
batch = append(batch, *h)
|
|
||||||
}
|
|
||||||
|
|
||||||
if err := gormbulk.BulkInsert(srv.Db, batch, 3000); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (srv *HeartbeatService) GetAllWithin(from, to time.Time, user *models.User) ([]*models.Heartbeat, error) {
|
func (srv *HeartbeatService) GetAllWithin(from, to time.Time, user *models.User) ([]*models.Heartbeat, error) {
|
||||||
var heartbeats []*models.Heartbeat
|
return srv.repository.GetAllWithin(from, to, user)
|
||||||
if err := srv.Db.
|
|
||||||
Where(&models.Heartbeat{UserID: user.ID}).
|
|
||||||
Where("time >= ?", from).
|
|
||||||
Where("time < ?", to).
|
|
||||||
Order("time asc").
|
|
||||||
Find(&heartbeats).Error; err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
return heartbeats, nil
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Will return *models.Heartbeat object with only user_id and time fields filled
|
|
||||||
func (srv *HeartbeatService) GetFirstUserHeartbeats(userIds []string) ([]*models.Heartbeat, error) {
|
func (srv *HeartbeatService) GetFirstUserHeartbeats(userIds []string) ([]*models.Heartbeat, error) {
|
||||||
var heartbeats []*models.Heartbeat
|
return srv.repository.GetFirstByUsers(userIds)
|
||||||
if err := srv.Db.
|
|
||||||
Table("heartbeats").
|
|
||||||
Select("user_id, min(time) as time").
|
|
||||||
Where("user_id IN (?)", userIds).
|
|
||||||
Group("user_id").
|
|
||||||
Scan(&heartbeats).Error; err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
return heartbeats, nil
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (srv *HeartbeatService) DeleteBefore(t time.Time) error {
|
func (srv *HeartbeatService) DeleteBefore(t time.Time) error {
|
||||||
if err := srv.Db.
|
return srv.repository.DeleteBefore(t)
|
||||||
Where("time <= ?", t).
|
|
||||||
Delete(models.Heartbeat{}).Error; err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (srv *HeartbeatService) CleanUp() error {
|
func (srv *HeartbeatService) CleanUp() error {
|
||||||
|
@ -1,63 +1,31 @@
|
|||||||
package services
|
package services
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"errors"
|
|
||||||
"github.com/jinzhu/gorm"
|
|
||||||
"github.com/muety/wakapi/config"
|
"github.com/muety/wakapi/config"
|
||||||
"github.com/muety/wakapi/models"
|
"github.com/muety/wakapi/models"
|
||||||
|
"github.com/muety/wakapi/repositories"
|
||||||
)
|
)
|
||||||
|
|
||||||
type KeyValueService struct {
|
type KeyValueService struct {
|
||||||
Config *config.Config
|
config *config.Config
|
||||||
Db *gorm.DB
|
repository *repositories.KeyValueRepository
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewKeyValueService(db *gorm.DB) *KeyValueService {
|
func NewKeyValueService(keyValueRepo *repositories.KeyValueRepository) *KeyValueService {
|
||||||
return &KeyValueService{
|
return &KeyValueService{
|
||||||
Config: config.Get(),
|
config: config.Get(),
|
||||||
Db: db,
|
repository: keyValueRepo,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (srv *KeyValueService) GetString(key string) (*models.KeyStringValue, error) {
|
func (srv *KeyValueService) GetString(key string) (*models.KeyStringValue, error) {
|
||||||
kv := &models.KeyStringValue{}
|
return srv.repository.GetString(key)
|
||||||
if err := srv.Db.
|
|
||||||
Where(&models.KeyStringValue{Key: key}).
|
|
||||||
First(&kv).Error; err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
return kv, nil
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (srv *KeyValueService) PutString(kv *models.KeyStringValue) error {
|
func (srv *KeyValueService) PutString(kv *models.KeyStringValue) error {
|
||||||
result := srv.Db.
|
return srv.repository.PutString(kv)
|
||||||
Where(&models.KeyStringValue{Key: kv.Key}).
|
|
||||||
Assign(kv).
|
|
||||||
FirstOrCreate(kv)
|
|
||||||
|
|
||||||
if err := result.Error; err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
if result.RowsAffected != 1 {
|
|
||||||
return errors.New("nothing updated")
|
|
||||||
}
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (srv *KeyValueService) DeleteString(key string) error {
|
func (srv *KeyValueService) DeleteString(key string) error {
|
||||||
result := srv.Db.
|
return srv.repository.DeleteString(key)
|
||||||
Delete(&models.KeyStringValue{}, &models.KeyStringValue{Key: key})
|
|
||||||
|
|
||||||
if err := result.Error; err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
if result.RowsAffected != 1 {
|
|
||||||
return errors.New("nothing deleted")
|
|
||||||
}
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
}
|
||||||
|
@ -4,35 +4,35 @@ import (
|
|||||||
"crypto/md5"
|
"crypto/md5"
|
||||||
"errors"
|
"errors"
|
||||||
"github.com/muety/wakapi/config"
|
"github.com/muety/wakapi/config"
|
||||||
|
"github.com/muety/wakapi/repositories"
|
||||||
"github.com/patrickmn/go-cache"
|
"github.com/patrickmn/go-cache"
|
||||||
"math"
|
"math"
|
||||||
"sort"
|
"sort"
|
||||||
"strconv"
|
"strconv"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/jinzhu/gorm"
|
|
||||||
"github.com/muety/wakapi/models"
|
"github.com/muety/wakapi/models"
|
||||||
)
|
)
|
||||||
|
|
||||||
const HeartbeatDiffThreshold = 2 * time.Minute
|
const HeartbeatDiffThreshold = 2 * time.Minute
|
||||||
|
|
||||||
type SummaryService struct {
|
type SummaryService struct {
|
||||||
Config *config.Config
|
config *config.Config
|
||||||
Cache *cache.Cache
|
cache *cache.Cache
|
||||||
Db *gorm.DB
|
repository *repositories.SummaryRepository
|
||||||
HeartbeatService *HeartbeatService
|
heartbeatService *HeartbeatService
|
||||||
AliasService *AliasService
|
aliasService *AliasService
|
||||||
CustomRuleService *CustomRuleService
|
customRuleService *CustomRuleService
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewSummaryService(db *gorm.DB, heartbeatService *HeartbeatService, aliasService *AliasService, customRuleService *CustomRuleService) *SummaryService {
|
func NewSummaryService(summaryRepo *repositories.SummaryRepository, heartbeatService *HeartbeatService, aliasService *AliasService, customRuleService *CustomRuleService) *SummaryService {
|
||||||
return &SummaryService{
|
return &SummaryService{
|
||||||
Config: config.Get(),
|
config: config.Get(),
|
||||||
Cache: cache.New(24*time.Hour, 24*time.Hour),
|
cache: cache.New(24*time.Hour, 24*time.Hour),
|
||||||
Db: db,
|
repository: summaryRepo,
|
||||||
HeartbeatService: heartbeatService,
|
heartbeatService: heartbeatService,
|
||||||
AliasService: aliasService,
|
aliasService: aliasService,
|
||||||
CustomRuleService: customRuleService,
|
customRuleService: customRuleService,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -50,7 +50,7 @@ func (srv *SummaryService) Construct(from, to time.Time, user *models.User, reco
|
|||||||
existingSummaries = make([]*models.Summary, 0)
|
existingSummaries = make([]*models.Summary, 0)
|
||||||
} else {
|
} else {
|
||||||
cacheKey = getHash([]time.Time{from, to}, user)
|
cacheKey = getHash([]time.Time{from, to}, user)
|
||||||
if result, ok := srv.Cache.Get(cacheKey); ok {
|
if result, ok := srv.cache.Get(cacheKey); ok {
|
||||||
return result.(*models.Summary), nil
|
return result.(*models.Summary), nil
|
||||||
}
|
}
|
||||||
summaries, err := srv.GetByUserWithin(user, from, to)
|
summaries, err := srv.GetByUserWithin(user, from, to)
|
||||||
@ -64,7 +64,7 @@ func (srv *SummaryService) Construct(from, to time.Time, user *models.User, reco
|
|||||||
|
|
||||||
heartbeats := make([]*models.Heartbeat, 0)
|
heartbeats := make([]*models.Heartbeat, 0)
|
||||||
for _, interval := range missingIntervals {
|
for _, interval := range missingIntervals {
|
||||||
hb, err := srv.HeartbeatService.GetAllWithin(interval.Start, interval.End, user)
|
hb, err := srv.heartbeatService.GetAllWithin(interval.Start, interval.End, user)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
@ -79,7 +79,7 @@ func (srv *SummaryService) Construct(from, to time.Time, user *models.User, reco
|
|||||||
var osItems []*models.SummaryItem
|
var osItems []*models.SummaryItem
|
||||||
var machineItems []*models.SummaryItem
|
var machineItems []*models.SummaryItem
|
||||||
|
|
||||||
if err := srv.AliasService.LoadUserAliases(user.ID); err != nil {
|
if err := srv.aliasService.LoadUserAliases(user.ID); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -144,7 +144,7 @@ func (srv *SummaryService) Construct(from, to time.Time, user *models.User, reco
|
|||||||
}
|
}
|
||||||
|
|
||||||
if cacheKey != "" {
|
if cacheKey != "" {
|
||||||
srv.Cache.SetDefault(cacheKey, summary)
|
srv.cache.SetDefault(cacheKey, summary)
|
||||||
}
|
}
|
||||||
|
|
||||||
return summary, nil
|
return summary, nil
|
||||||
@ -179,14 +179,14 @@ func (srv *SummaryService) PostProcess(summary *models.Summary) *models.Summary
|
|||||||
|
|
||||||
for _, item := range origin {
|
for _, item := range origin {
|
||||||
// Add all "top-level" items, i.e. such without aliases
|
// Add all "top-level" items, i.e. such without aliases
|
||||||
if key, _ := srv.AliasService.GetAliasOrDefault(summary.UserID, item.Type, item.Key); key == item.Key {
|
if key, _ := srv.aliasService.GetAliasOrDefault(summary.UserID, item.Type, item.Key); key == item.Key {
|
||||||
target = append(target, item)
|
target = append(target, item)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
for _, item := range origin {
|
for _, item := range origin {
|
||||||
// Add all remaining projects and merge with their alias
|
// Add all remaining projects and merge with their alias
|
||||||
if key, _ := srv.AliasService.GetAliasOrDefault(summary.UserID, item.Type, item.Key); key != item.Key {
|
if key, _ := srv.aliasService.GetAliasOrDefault(summary.UserID, item.Type, item.Key); key != item.Key {
|
||||||
if targetItem := findItem(key); targetItem != nil {
|
if targetItem := findItem(key); targetItem != nil {
|
||||||
targetItem.Total += item.Total
|
targetItem.Total += item.Total
|
||||||
} else {
|
} else {
|
||||||
@ -215,41 +215,16 @@ func (srv *SummaryService) PostProcess(summary *models.Summary) *models.Summary
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (srv *SummaryService) Insert(summary *models.Summary) error {
|
func (srv *SummaryService) Insert(summary *models.Summary) error {
|
||||||
if err := srv.Db.Create(summary).Error; err != nil {
|
return srv.repository.Insert(summary)
|
||||||
return err
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (srv *SummaryService) GetByUserWithin(user *models.User, from, to time.Time) ([]*models.Summary, error) {
|
func (srv *SummaryService) GetByUserWithin(user *models.User, from, to time.Time) ([]*models.Summary, error) {
|
||||||
var summaries []*models.Summary
|
return srv.repository.GetByUserWithin(user, from, to)
|
||||||
if err := srv.Db.
|
|
||||||
Where(&models.Summary{UserID: user.ID}).
|
|
||||||
Where("from_time >= ?", from).
|
|
||||||
Where("to_time <= ?", to).
|
|
||||||
Order("from_time asc").
|
|
||||||
Preload("Projects", "type = ?", models.SummaryProject).
|
|
||||||
Preload("Languages", "type = ?", models.SummaryLanguage).
|
|
||||||
Preload("Editors", "type = ?", models.SummaryEditor).
|
|
||||||
Preload("OperatingSystems", "type = ?", models.SummaryOS).
|
|
||||||
Preload("Machines", "type = ?", models.SummaryMachine).
|
|
||||||
Find(&summaries).Error; err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
return summaries, nil
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Will return *models.Index objects with only user_id and to_time filled
|
// Will return *models.Index objects with only user_id and to_time filled
|
||||||
func (srv *SummaryService) GetLatestByUser() ([]*models.Summary, error) {
|
func (srv *SummaryService) GetLatestByUser() ([]*models.Summary, error) {
|
||||||
var summaries []*models.Summary
|
return srv.repository.GetLatestByUser()
|
||||||
if err := srv.Db.
|
|
||||||
Table("summaries").
|
|
||||||
Select("user_id, max(to_time) as to_time").
|
|
||||||
Group("user_id").
|
|
||||||
Scan(&summaries).Error; err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
return summaries, nil
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (srv *SummaryService) aggregateBy(heartbeats []*models.Heartbeat, summaryType uint8, user *models.User, c chan models.SummaryItemContainer) {
|
func (srv *SummaryService) aggregateBy(heartbeats []*models.Heartbeat, summaryType uint8, user *models.User, c chan models.SummaryItemContainer) {
|
||||||
|
@ -1,50 +1,35 @@
|
|||||||
package services
|
package services
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"errors"
|
|
||||||
"github.com/jinzhu/gorm"
|
|
||||||
"github.com/muety/wakapi/config"
|
"github.com/muety/wakapi/config"
|
||||||
"github.com/muety/wakapi/models"
|
"github.com/muety/wakapi/models"
|
||||||
|
"github.com/muety/wakapi/repositories"
|
||||||
"github.com/muety/wakapi/utils"
|
"github.com/muety/wakapi/utils"
|
||||||
uuid "github.com/satori/go.uuid"
|
uuid "github.com/satori/go.uuid"
|
||||||
)
|
)
|
||||||
|
|
||||||
type UserService struct {
|
type UserService struct {
|
||||||
Config *config.Config
|
Config *config.Config
|
||||||
Db *gorm.DB
|
repository *repositories.UserRepository
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewUserService(db *gorm.DB) *UserService {
|
func NewUserService(userRepo *repositories.UserRepository) *UserService {
|
||||||
return &UserService{
|
return &UserService{
|
||||||
Config: config.Get(),
|
Config: config.Get(),
|
||||||
Db: db,
|
repository: userRepo,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (srv *UserService) GetUserById(userId string) (*models.User, error) {
|
func (srv *UserService) GetUserById(userId string) (*models.User, error) {
|
||||||
u := &models.User{}
|
return srv.repository.GetById(userId)
|
||||||
if err := srv.Db.Where(&models.User{ID: userId}).First(u).Error; err != nil {
|
|
||||||
return u, err
|
|
||||||
}
|
|
||||||
return u, nil
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (srv *UserService) GetUserByKey(key string) (*models.User, error) {
|
func (srv *UserService) GetUserByKey(key string) (*models.User, error) {
|
||||||
u := &models.User{}
|
return srv.repository.GetByApiKey(key)
|
||||||
if err := srv.Db.Where(&models.User{ApiKey: key}).First(u).Error; err != nil {
|
|
||||||
return u, err
|
|
||||||
}
|
|
||||||
return u, nil
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (srv *UserService) GetAll() ([]*models.User, error) {
|
func (srv *UserService) GetAll() ([]*models.User, error) {
|
||||||
var users []*models.User
|
return srv.repository.GetAll()
|
||||||
if err := srv.Db.
|
|
||||||
Table("users").
|
|
||||||
Find(&users).Error; err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
return users, nil
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (srv *UserService) CreateOrGet(signup *models.Signup) (*models.User, bool, error) {
|
func (srv *UserService) CreateOrGet(signup *models.Signup) (*models.User, bool, error) {
|
||||||
@ -60,29 +45,11 @@ func (srv *UserService) CreateOrGet(signup *models.Signup) (*models.User, bool,
|
|||||||
u.Password = hash
|
u.Password = hash
|
||||||
}
|
}
|
||||||
|
|
||||||
result := srv.Db.FirstOrCreate(u, &models.User{ID: u.ID})
|
return srv.repository.InsertOrGet(u)
|
||||||
if err := result.Error; err != nil {
|
|
||||||
return nil, false, err
|
|
||||||
}
|
|
||||||
|
|
||||||
if result.RowsAffected == 1 {
|
|
||||||
return u, true, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
return u, false, nil
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (srv *UserService) Update(user *models.User) (*models.User, error) {
|
func (srv *UserService) Update(user *models.User) (*models.User, error) {
|
||||||
result := srv.Db.Model(&models.User{}).Updates(user)
|
return srv.repository.Update(user)
|
||||||
if err := result.Error; err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
if result.RowsAffected != 1 {
|
|
||||||
return nil, errors.New("nothing updated")
|
|
||||||
}
|
|
||||||
|
|
||||||
return user, nil
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (srv *UserService) ResetApiKey(user *models.User) (*models.User, error) {
|
func (srv *UserService) ResetApiKey(user *models.User) (*models.User, error) {
|
||||||
@ -91,16 +58,7 @@ func (srv *UserService) ResetApiKey(user *models.User) (*models.User, error) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (srv *UserService) ToggleBadges(user *models.User) (*models.User, error) {
|
func (srv *UserService) ToggleBadges(user *models.User) (*models.User, error) {
|
||||||
result := srv.Db.Model(user).Update("badges_enabled", !user.BadgesEnabled)
|
return srv.repository.UpdateField(user, "badges_enabled", !user.BadgesEnabled)
|
||||||
if err := result.Error; err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
if result.RowsAffected != 1 {
|
|
||||||
return nil, errors.New("nothing updated")
|
|
||||||
}
|
|
||||||
|
|
||||||
return user, nil
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (srv *UserService) MigrateMd5Password(user *models.User, login *models.Login) (*models.User, error) {
|
func (srv *UserService) MigrateMd5Password(user *models.User, login *models.Login) (*models.User, error) {
|
||||||
@ -110,13 +68,5 @@ func (srv *UserService) MigrateMd5Password(user *models.User, login *models.Logi
|
|||||||
} else {
|
} else {
|
||||||
user.Password = hash
|
user.Password = hash
|
||||||
}
|
}
|
||||||
|
return srv.repository.UpdateField(user, "password", user.Password)
|
||||||
result := srv.Db.Model(user).Update("password", user.Password)
|
|
||||||
if err := result.Error; err != nil {
|
|
||||||
return nil, err
|
|
||||||
} else if result.RowsAffected < 1 {
|
|
||||||
return nil, errors.New("nothing changes")
|
|
||||||
}
|
|
||||||
|
|
||||||
return user, nil
|
|
||||||
}
|
}
|
||||||
|
Reference in New Issue
Block a user