Skip to content
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

New param --offline-only #1064

Merged
merged 1 commit into from
Jul 20, 2024
Merged
Show file tree
Hide file tree
Changes from all 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
2 changes: 2 additions & 0 deletions cmd/heartbeat/heartbeat.go
Original file line number Diff line number Diff line change
Expand Up @@ -105,6 +105,8 @@
}

handleOpts = append(handleOpts, offline.WithQueue(queueFilepath))
} else if params.Offline.OfflineOnly {
return errors.New("--offline-only can NOT be used with --disable-offline")

Check warning on line 109 in cmd/heartbeat/heartbeat.go

View check run for this annotation

Codecov / codecov/patch

cmd/heartbeat/heartbeat.go#L108-L109

Added lines #L108 - L109 were not covered by tests
}

handleOpts = append(handleOpts, backoff.WithBackoff(backoff.Config{
Expand Down
6 changes: 3 additions & 3 deletions cmd/offline/offline.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,9 +18,9 @@ import (
"github.com/spf13/viper"
)

// SaveHeartbeats saves heartbeats to the offline db, when we haven't
// tried sending them to the API. If we tried sending to API already,
// to the API. Used when we have heartbeats unsent to API.
// SaveHeartbeats saves heartbeats to the offline db without trying to send to the API.
// Used when we have more heartbeats than `offline.SendLimit`, when --offline-only enabled,
// when we couldn't send heartbeats to the API, or the API returned an auth error.
func SaveHeartbeats(v *viper.Viper, heartbeats []heartbeat.Heartbeat, queueFilepath string) error {
params, err := loadParams(v)
if err != nil {
Expand Down
9 changes: 6 additions & 3 deletions cmd/params/params.go
Original file line number Diff line number Diff line change
Expand Up @@ -131,9 +131,10 @@ type (
// Offline contains offline related parameters.
Offline struct {
Disabled bool
OfflineOnly bool
PrintMax int
QueueFile string
QueueFileLegacy string
PrintMax int
SyncMax int
}

Expand Down Expand Up @@ -656,9 +657,10 @@ func LoadOfflineParams(v *viper.Viper) Offline {

return Offline{
Disabled: disabled,
OfflineOnly: v.GetBool("offline-only"),
PrintMax: v.GetInt("print-offline-heartbeats"),
QueueFile: vipertools.GetString(v, "offline-queue-file"),
QueueFileLegacy: vipertools.GetString(v, "offline-queue-file-legacy"),
PrintMax: v.GetInt("print-offline-heartbeats"),
SyncMax: syncMax,
}
}
Expand Down Expand Up @@ -1039,8 +1041,9 @@ func (p Heartbeat) String() string {
// String implements fmt.Stringer interface.
func (p Offline) String() string {
return fmt.Sprintf(
"disabled: %t, print max: %d, queue file: '%s', queue file legacy: '%s', num sync max: %d",
"disabled: %t, offline only: %t, print max: %d, queue file: '%s', queue file legacy: '%s', num sync max: %d",
p.Disabled,
p.OfflineOnly,
p.PrintMax,
p.QueueFile,
p.QueueFileLegacy,
Expand Down
2 changes: 1 addition & 1 deletion cmd/params/params_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -2505,7 +2505,7 @@ func TestOffline_String(t *testing.T) {

assert.Equal(
t,
"disabled: true, print max: 6, queue file: '/path/to/queue.file',"+
"disabled: true, offline only: false, print max: 6, queue file: '/path/to/queue.file',"+
" queue file legacy: '/path/to/legacy.file', num sync max: 12",
offline.String(),
)
Expand Down
1 change: 1 addition & 0 deletions cmd/root.go
Original file line number Diff line number Diff line change
Expand Up @@ -230,6 +230,7 @@
" new heartbeats.", offline.SyncMaxDefault),
)
flags.Bool("offline-count", false, "Prints the number of heartbeats in the offline db, then exits.")
flags.Bool("offline-only", false, "Saves the heartbeat(s) to the offline db, then exits.")

Check warning on line 233 in cmd/root.go

View check run for this annotation

Codecov / codecov/patch

cmd/root.go#L233

Added line #L233 was not covered by tests
flags.Int(
"timeout",
api.DefaultTimeoutSecs,
Expand Down
6 changes: 6 additions & 0 deletions cmd/run.go
Original file line number Diff line number Diff line change
Expand Up @@ -134,6 +134,12 @@
if v.IsSet("entity") {
log.Debugln("command: heartbeat")

if v.GetBool("offline-only") {
saveHeartbeats(v)
shutdown()
os.Exit(exitcode.Success)
}

Check warning on line 141 in cmd/run.go

View check run for this annotation

Codecov / codecov/patch

cmd/run.go#L137-L141

Added lines #L137 - L141 were not covered by tests

RunCmdWithOfflineSync(v, logFileParams.Verbose, logFileParams.SendDiagsOnErrors, cmdheartbeat.Run, shutdown)
}

Expand Down
62 changes: 60 additions & 2 deletions main_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -327,7 +327,7 @@ func TestSendHeartbeats_ExtraHeartbeats(t *testing.T) {
offlineCount, err := offline.CountHeartbeats(offlineQueueFile.Name())
require.NoError(t, err)

assert.Zero(t, offlineCount)
assert.Equal(t, 1, offlineCount)

assert.Eventually(t, func() bool { return numCalls == 2 }, time.Second, 50*time.Millisecond)
}
Expand Down Expand Up @@ -357,6 +357,8 @@ func TestSendHeartbeats_ExtraHeartbeats_SyncLegacyOfflineActivity(t *testing.T)
filename = "testdata/api_heartbeats_response_extra_heartbeats_legacy_offline.json"
case 3:
filename = "testdata/api_heartbeats_response_extra_heartbeats_extra.json"
case 4:
filename = "testdata/api_heartbeats_response_extra_heartbeats_extra.json"
}

// write response
Expand Down Expand Up @@ -455,7 +457,63 @@ func TestSendHeartbeats_ExtraHeartbeats_SyncLegacyOfflineActivity(t *testing.T)

assert.Zero(t, offlineCount)

assert.Eventually(t, func() bool { return numCalls == 3 }, time.Second, 50*time.Millisecond)
assert.Eventually(t, func() bool { return numCalls == 4 }, time.Second, 50*time.Millisecond)
}

func TestSendHeartbeats_OfflineOnly(t *testing.T) {
apiURL, router, close := setupTestServer()
defer close()

router.HandleFunc("/users/current/heartbeats.bulk", func(_ http.ResponseWriter, _ *http.Request) {
require.FailNow(t, "Should not make any API request")
})

tmpDir := t.TempDir()

offlineQueueFile, err := os.CreateTemp(tmpDir, "")
require.NoError(t, err)

defer offlineQueueFile.Close()

tmpConfigFile, err := os.CreateTemp(tmpDir, "wakatime.cfg")
require.NoError(t, err)

defer tmpConfigFile.Close()

tmpInternalConfigFile, err := os.CreateTemp(tmpDir, "wakatime-internal.cfg")
require.NoError(t, err)

defer tmpInternalConfigFile.Close()

data, err := os.ReadFile("testdata/extra_heartbeats.json")
require.NoError(t, err)

buffer := bytes.NewBuffer(data)

runWakatimeCli(
t,
buffer,
"--api-url", apiURL,
"--key", "00000000-0000-4000-8000-000000000000",
"--config", tmpConfigFile.Name(),
"--internal-config", tmpInternalConfigFile.Name(),
"--entity", "testdata/main.go",
"--extra-heartbeats", "true",
"--cursorpos", "100",
"--offline-queue-file", offlineQueueFile.Name(),
"--offline-only",
"--lineno", "42",
"--lines-in-file", "100",
"--time", "1585598059",
"--hide-branch-names", ".*",
"--write",
"--verbose",
)

offlineCount, err := offline.CountHeartbeats(offlineQueueFile.Name())
require.NoError(t, err)

assert.Equal(t, 27, offlineCount)
}

func TestSendHeartbeats_Err(t *testing.T) {
Expand Down
8 changes: 7 additions & 1 deletion pkg/heartbeat/heartbeat.go
Original file line number Diff line number Diff line change
Expand Up @@ -113,8 +113,14 @@
isWrite = *h.IsWrite
}

return fmt.Sprintf("%f-%s-%s-%s-%s-%s-%t",
cursorPos := "nil"
if h.CursorPosition != nil {
cursorPos = fmt.Sprint(*h.CursorPosition)
}

Check warning on line 119 in pkg/heartbeat/heartbeat.go

View check run for this annotation

Codecov / codecov/patch

pkg/heartbeat/heartbeat.go#L118-L119

Added lines #L118 - L119 were not covered by tests

return fmt.Sprintf("%f-%s-%s-%s-%s-%s-%s-%t",
h.Time,
cursorPos,
h.EntityType,
h.Category,
project,
Expand Down
4 changes: 2 additions & 2 deletions pkg/heartbeat/heartbeat_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -75,7 +75,7 @@ func TestHeartbeat_ID(t *testing.T) {
Project: heartbeat.PointerTo("wakatime"),
Time: 1592868313.541149,
}
assert.Equal(t, "1592868313.541149-file-coding-wakatime-heartbeat-/tmp/main.go-true", h.ID())
assert.Equal(t, "1592868313.541149-nil-file-coding-wakatime-heartbeat-/tmp/main.go-true", h.ID())
}

func TestHeartbeat_ID_NilFields(t *testing.T) {
Expand All @@ -85,7 +85,7 @@ func TestHeartbeat_ID_NilFields(t *testing.T) {
EntityType: heartbeat.FileType,
Time: 1592868313.541149,
}
assert.Equal(t, "1592868313.541149-file-coding-unset-unset-/tmp/main.go-false", h.ID())
assert.Equal(t, "1592868313.541149-nil-file-coding-unset-unset-/tmp/main.go-false", h.ID())
}

func TestHeartbeat_JSON(t *testing.T) {
Expand Down
5 changes: 2 additions & 3 deletions pkg/offline/offline.go
Original file line number Diff line number Diff line change
Expand Up @@ -62,14 +62,13 @@

results, err := next(hh)
if err != nil {
log.Debugf("pushing %d heartbeat(s) to queue due to error", len(hh))
log.Debugf("pushing %d heartbeat(s) to queue after error: %s", len(hh), err)

requeueErr := pushHeartbeatsWithRetry(filepath, hh)
if requeueErr != nil {
return nil, fmt.Errorf(
"failed to push heartbeats to queue after api error: %w. error: %w",
"failed to push heartbeats to queue: %s",

Check warning on line 70 in pkg/offline/offline.go

View check run for this annotation

Codecov / codecov/patch

pkg/offline/offline.go#L70

Added line #L70 was not covered by tests
requeueErr,
err,
)
}

Expand Down
32 changes: 16 additions & 16 deletions pkg/offline/offline_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -221,10 +221,10 @@ func TestWithQueue_ApiError(t *testing.T) {

require.Len(t, stored, 2)

assert.Equal(t, "1592868367.219124-file-coding-wakatime-cli-heartbeat-/tmp/main.go-true", stored[0].ID)
assert.Equal(t, "1592868367.219124-12-file-coding-wakatime-cli-heartbeat-/tmp/main.go-true", stored[0].ID)
assert.JSONEq(t, string(dataGo), stored[0].Heartbeat)

assert.Equal(t, "1592868386.079084-file-debugging-wakatime-summary-/tmp/main.py-false", stored[1].ID)
assert.Equal(t, "1592868386.079084-13-file-debugging-wakatime-summary-/tmp/main.py-false", stored[1].ID)
assert.JSONEq(t, string(dataPy), stored[1].Heartbeat)
}

Expand Down Expand Up @@ -307,10 +307,10 @@ func TestWithQueue_InvalidResults(t *testing.T) {

assert.Len(t, stored, 2)

assert.Equal(t, "1592868386.079084-file-debugging-wakatime-summary-/tmp/main.py-false", stored[0].ID)
assert.Equal(t, "1592868386.079084-13-file-debugging-wakatime-summary-/tmp/main.py-false", stored[0].ID)
assert.JSONEq(t, string(dataPy), stored[0].Heartbeat)

assert.Equal(t, "1592868394.084354-file-building-wakatime-todaygoal-/tmp/main.js-false", stored[1].ID)
assert.Equal(t, "1592868394.084354-14-file-building-wakatime-todaygoal-/tmp/main.js-false", stored[1].ID)
assert.JSONEq(t, string(dataJs), stored[1].Heartbeat)
}

Expand Down Expand Up @@ -376,10 +376,10 @@ func TestWithQueue_HandleLeftovers(t *testing.T) {

require.Len(t, stored, 2)

assert.Equal(t, "1592868386.079084-file-debugging-wakatime-summary-/tmp/main.py-false", stored[0].ID)
assert.Equal(t, "1592868386.079084-13-file-debugging-wakatime-summary-/tmp/main.py-false", stored[0].ID)
assert.JSONEq(t, string(dataPy), stored[0].Heartbeat)

assert.Equal(t, "1592868394.084354-file-building-wakatime-todaygoal-/tmp/main.js-false", stored[1].ID)
assert.Equal(t, "1592868394.084354-14-file-building-wakatime-todaygoal-/tmp/main.js-false", stored[1].ID)
assert.JSONEq(t, string(dataJs), stored[1].Heartbeat)
}

Expand All @@ -401,11 +401,11 @@ func TestWithSync(t *testing.T) {

insertHeartbeatRecords(t, db, "heartbeats", []heartbeatRecord{
{
ID: "1592868367.219124-file-coding-wakatime-cli-heartbeat-/tmp/main.go-true",
ID: "1592868367.219124-12-file-coding-wakatime-cli-heartbeat-/tmp/main.go-true",
Heartbeat: string(dataGo),
},
{
ID: "1592868386.079084-file-debugging-wakatime-summary-/tmp/main.py-false",
ID: "1592868386.079084-13-file-debugging-wakatime-summary-/tmp/main.py-false",
Heartbeat: string(dataPy),
},
})
Expand Down Expand Up @@ -571,11 +571,11 @@ func TestSync_APIError(t *testing.T) {

insertHeartbeatRecords(t, db, "heartbeats", []heartbeatRecord{
{
ID: "1592868367.219124-file-coding-wakatime-cli-heartbeat-/tmp/main.go-true",
ID: "1592868367.219124-12-file-coding-wakatime-cli-heartbeat-/tmp/main.go-true",
Heartbeat: string(dataGo),
},
{
ID: "1592868386.079084-file-debugging-wakatime-summary-/tmp/main.py-false",
ID: "1592868386.079084-13-file-debugging-wakatime-summary-/tmp/main.py-false",
Heartbeat: string(dataPy),
},
})
Expand Down Expand Up @@ -625,10 +625,10 @@ func TestSync_APIError(t *testing.T) {

require.Len(t, stored, 2)

assert.Equal(t, "1592868367.219124-file-coding-wakatime-cli-heartbeat-/tmp/main.go-true", stored[0].ID)
assert.Equal(t, "1592868367.219124-12-file-coding-wakatime-cli-heartbeat-/tmp/main.go-true", stored[0].ID)
assert.JSONEq(t, string(dataGo), stored[0].Heartbeat)

assert.Equal(t, "1592868386.079084-file-debugging-wakatime-summary-/tmp/main.py-false", stored[1].ID)
assert.Equal(t, "1592868386.079084-13-file-debugging-wakatime-summary-/tmp/main.py-false", stored[1].ID)
assert.JSONEq(t, string(dataPy), stored[1].Heartbeat)

assert.Eventually(t, func() bool { return numCalls == 1 }, time.Second, 50*time.Millisecond)
Expand Down Expand Up @@ -1189,7 +1189,7 @@ func TestQueue_PushMany(t *testing.T) {
require.NoError(t, err)

insertHeartbeatRecord(t, db, "test_bucket", heartbeatRecord{
ID: "1592868367.219124-file-coding-wakatime-cli-heartbeat-/tmp/main.go-true",
ID: "1592868367.219124-1-file-coding-wakatime-cli-heartbeat-/tmp/main.go-true",
Heartbeat: string(dataGo),
})

Expand Down Expand Up @@ -1240,13 +1240,13 @@ func TestQueue_PushMany(t *testing.T) {

assert.Len(t, stored, 3)

assert.Equal(t, "1592868367.219124-file-coding-wakatime-cli-heartbeat-/tmp/main.go-true", stored[0].ID)
assert.Equal(t, "1592868367.219124-1-file-coding-wakatime-cli-heartbeat-/tmp/main.go-true", stored[0].ID)
assert.JSONEq(t, string(dataGo), stored[0].Heartbeat)

assert.Equal(t, "1592868386.079084-file-debugging-wakatime-summary-/tmp/main.py-false", stored[1].ID)
assert.Equal(t, "1592868386.079084-13-file-debugging-wakatime-summary-/tmp/main.py-false", stored[1].ID)
assert.JSONEq(t, string(dataPy), stored[1].Heartbeat)

assert.Equal(t, "1592868394.084354-file-building-wakatime-todaygoal-/tmp/main.js-false", stored[2].ID)
assert.Equal(t, "1592868394.084354-14-file-building-wakatime-todaygoal-/tmp/main.js-false", stored[2].ID)
assert.JSONEq(t, string(dataJs), stored[2].Heartbeat)
}

Expand Down
Loading
Loading