Skip to content

Fix incorrect webhook time and use relative-time to display it #24477

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 9 commits into from
May 3, 2023
Merged
Show file tree
Hide file tree
Changes from 3 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
39 changes: 19 additions & 20 deletions models/webhook/hooktask.go
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ import (
"code.gitea.io/gitea/modules/log"
"code.gitea.io/gitea/modules/setting"
api "code.gitea.io/gitea/modules/structs"
"code.gitea.io/gitea/modules/timeutil"
webhook_module "code.gitea.io/gitea/modules/webhook"

gouuid "github.com/google/uuid"
Expand Down Expand Up @@ -40,15 +41,14 @@ type HookResponse struct {

// HookTask represents a hook task.
type HookTask struct {
ID int64 `xorm:"pk autoincr"`
HookID int64 `xorm:"index"`
UUID string `xorm:"unique"`
api.Payloader `xorm:"-"`
PayloadContent string `xorm:"LONGTEXT"`
EventType webhook_module.HookEventType
IsDelivered bool
Delivered int64
DeliveredString string `xorm:"-"`
ID int64 `xorm:"pk autoincr"`
HookID int64 `xorm:"index"`
UUID string `xorm:"unique"`
api.Payloader `xorm:"-"`
PayloadContent string `xorm:"LONGTEXT"`
EventType webhook_module.HookEventType
IsDelivered bool
Delivered timeutil.TimeStampNano

// History info.
IsSucceed bool
Expand All @@ -75,8 +75,6 @@ func (t *HookTask) BeforeUpdate() {

// AfterLoad updates the webhook object upon setting a column
func (t *HookTask) AfterLoad() {
t.DeliveredString = time.Unix(0, t.Delivered).Format("2006-01-02 15:04:05 MST")

if len(t.RequestContent) == 0 {
return
}
Expand Down Expand Up @@ -115,12 +113,15 @@ func HookTasks(hookID int64, page int) ([]*HookTask, error) {
// CreateHookTask creates a new hook task,
// it handles conversion from Payload to PayloadContent.
func CreateHookTask(ctx context.Context, t *HookTask) (*HookTask, error) {
data, err := t.Payloader.JSONPayload()
if err != nil {
return nil, err
}
t.UUID = gouuid.New().String()
t.PayloadContent = string(data)
if t.Payloader != nil {
data, err := t.Payloader.JSONPayload()
if err != nil {
return nil, err
}
t.PayloadContent = string(data)
}
t.Delivered = timeutil.TimeStampNanoNow()
return t, db.Insert(ctx, t)
}

Expand Down Expand Up @@ -161,13 +162,11 @@ func ReplayHookTask(ctx context.Context, hookID int64, uuid string) (*HookTask,
}
}

newTask := &HookTask{
UUID: gouuid.New().String(),
return CreateHookTask(ctx, &HookTask{
HookID: task.HookID,
PayloadContent: task.PayloadContent,
EventType: task.EventType,
}
return newTask, db.Insert(ctx, newTask)
})
}

// FindUndeliveredHookTaskIDs will find the next 100 undelivered hook tasks with ID greater than the provided lowerID
Expand Down
10 changes: 5 additions & 5 deletions models/webhook/webhook_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ import (
"code.gitea.io/gitea/models/unittest"
"code.gitea.io/gitea/modules/json"
api "code.gitea.io/gitea/modules/structs"
"code.gitea.io/gitea/modules/timeutil"
"code.gitea.io/gitea/modules/util"
webhook_module "code.gitea.io/gitea/modules/webhook"

Expand Down Expand Up @@ -222,7 +223,6 @@ func TestUpdateHookTask(t *testing.T) {

hook := unittest.AssertExistsAndLoadBean(t, &HookTask{ID: 1})
hook.PayloadContent = "new payload content"
hook.DeliveredString = "new delivered string"
hook.IsDelivered = true
unittest.AssertNotExistsBean(t, hook)
assert.NoError(t, UpdateHookTask(hook))
Expand All @@ -235,7 +235,7 @@ func TestCleanupHookTaskTable_PerWebhook_DeletesDelivered(t *testing.T) {
HookID: 3,
Payloader: &api.PushPayload{},
IsDelivered: true,
Delivered: time.Now().UnixNano(),
Delivered: timeutil.TimeStampNanoNow(),
}
unittest.AssertNotExistsBean(t, hookTask)
_, err := CreateHookTask(db.DefaultContext, hookTask)
Expand Down Expand Up @@ -268,7 +268,7 @@ func TestCleanupHookTaskTable_PerWebhook_LeavesMostRecentTask(t *testing.T) {
HookID: 4,
Payloader: &api.PushPayload{},
IsDelivered: true,
Delivered: time.Now().UnixNano(),
Delivered: timeutil.TimeStampNanoNow(),
}
unittest.AssertNotExistsBean(t, hookTask)
_, err := CreateHookTask(db.DefaultContext, hookTask)
Expand All @@ -285,7 +285,7 @@ func TestCleanupHookTaskTable_OlderThan_DeletesDelivered(t *testing.T) {
HookID: 3,
Payloader: &api.PushPayload{},
IsDelivered: true,
Delivered: time.Now().AddDate(0, 0, -8).UnixNano(),
Delivered: timeutil.TimeStampNano(time.Now().AddDate(0, 0, -8).UnixNano()),
}
unittest.AssertNotExistsBean(t, hookTask)
_, err := CreateHookTask(db.DefaultContext, hookTask)
Expand Down Expand Up @@ -318,7 +318,7 @@ func TestCleanupHookTaskTable_OlderThan_LeavesTaskEarlierThanAgeToDelete(t *test
HookID: 4,
Payloader: &api.PushPayload{},
IsDelivered: true,
Delivered: time.Now().AddDate(0, 0, -6).UnixNano(),
Delivered: timeutil.TimeStampNano(time.Now().AddDate(0, 0, -6).UnixNano()),
}
unittest.AssertNotExistsBean(t, hookTask)
_, err := CreateHookTask(db.DefaultContext, hookTask)
Expand Down
97 changes: 97 additions & 0 deletions modules/timeutil/timestampnano.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,97 @@
// Copyright 2017 The Gitea Authors. All rights reserved.
// SPDX-License-Identifier: MIT

package timeutil

import (
"time"

"code.gitea.io/gitea/modules/setting"
)

// TimeStampNano defines a nano timestamp
type TimeStampNano int64

var (
// Used for IsZero, to check if timestamp is the zero time instant.
timeZeroUnixNano = time.Time{}.UnixNano()
)

// TimeStampNanoNow returns now nano int64
func TimeStampNanoNow() TimeStampNano {
if !mock.IsZero() {
return TimeStampNano(mock.UnixNano())
}
return TimeStampNano(time.Now().UnixNano())
}

// Add adds nanos and return sum
func (tsn TimeStampNano) Add(nanos int64) TimeStampNano {
return tsn + TimeStampNano(nanos)
}

// AddDuration adds time.Duration and return sum
func (tsn TimeStampNano) AddDuration(interval time.Duration) TimeStampNano {
return tsn + TimeStampNano(interval/time.Nanosecond)
}

// Year returns the time's year
func (tsn TimeStampNano) Year() int {
return tsn.AsTime().Year()
}

// AsTime convert timestamp as time.Time in Local locale
func (tsn TimeStampNano) AsTime() (tm time.Time) {
return tsn.AsTimeInLocation(setting.DefaultUILocation)
}

// AsLocalTime convert timestamp as time.Time in local location
func (tsn TimeStampNano) AsLocalTime() time.Time {
return time.Unix(0, int64(tsn))
}

// AsTimeInLocation convert timestamp as time.Time in Local locale
func (tsn TimeStampNano) AsTimeInLocation(loc *time.Location) time.Time {
return time.Unix(0, int64(tsn)).In(loc)
}

// AsTimePtr convert timestamp as *time.Time in Local locale
func (tsn TimeStampNano) AsTimePtr() *time.Time {
return tsn.AsTimePtrInLocation(setting.DefaultUILocation)
}

// AsTimePtrInLocation convert timestamp as *time.Time in customize location
func (tsn TimeStampNano) AsTimePtrInLocation(loc *time.Location) *time.Time {
tm := time.Unix(0, int64(tsn)).In(loc)
return &tm
}

// Format formats timestamp as given format
func (tsn TimeStampNano) Format(f string) string {
return tsn.FormatInLocation(f, setting.DefaultUILocation)
}

// FormatInLocation formats timestamp as given format with spiecific location
func (tsn TimeStampNano) FormatInLocation(f string, loc *time.Location) string {
return tsn.AsTimeInLocation(loc).Format(f)
}

// FormatLong formats as RFC1123Z
func (tsn TimeStampNano) FormatLong() string {
return tsn.Format(time.RFC1123Z)
}

// FormatShort formats as short
func (tsn TimeStampNano) FormatShort() string {
return tsn.Format("Jan 02, 2006")
}

// FormatDate formats a date in YYYY-MM-DD server time zone
func (tsn TimeStampNano) FormatDate() string {
return time.Unix(0, int64(tsn)).String()[:10]
}

// IsZero is zero time
func (tsn TimeStampNano) IsZero() bool {
return int64(tsn) == 0 || int64(tsn) == timeZeroUnixNano
}
3 changes: 2 additions & 1 deletion services/webhook/deliver.go
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ import (
"code.gitea.io/gitea/modules/proxy"
"code.gitea.io/gitea/modules/queue"
"code.gitea.io/gitea/modules/setting"
"code.gitea.io/gitea/modules/timeutil"
webhook_module "code.gitea.io/gitea/modules/webhook"

"github.com/gobwas/glob"
Expand Down Expand Up @@ -175,7 +176,7 @@ func Deliver(ctx context.Context, t *webhook_model.HookTask) error {

// All code from this point will update the hook task
defer func() {
t.Delivered = time.Now().UnixNano()
t.Delivered = timeutil.TimeStampNanoNow()
if t.IsSucceed {
log.Trace("Hook delivered: %s", t.UUID)
} else if !w.IsActive {
Expand Down
2 changes: 1 addition & 1 deletion templates/repo/settings/webhook/history.tmpl
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@
<a class="ui primary sha label toggle button show-panel" data-panel="#info-{{.ID}}">{{.UUID}}</a>
<div class="ui right">
<span class="text grey time">
{{.DeliveredString}}
{{TimeSince .Delivered.AsTime $.lcoale}}
</span>
</div>
</div>
Expand Down