fix(import): data dump already exists

handle the import when there is already an active data dump exists.

Resolves #502
This commit is contained in:
Edward 2023-07-13 23:54:48 +08:00
parent 5f1ca4ed69
commit 7b0bbcefe6
No known key found for this signature in database
2 changed files with 55 additions and 11 deletions

View File

@ -6,6 +6,10 @@ type DataDumpViewModel struct {
TotalPages int `json:"total_pages"` TotalPages int `json:"total_pages"`
} }
type DataDumpResultErrorModel struct {
Error string `json:"error"`
}
type DataDumpResultViewModel struct { type DataDumpResultViewModel struct {
Data *DataDumpData `json:"data"` Data *DataDumpData `json:"data"`
} }

View File

@ -6,6 +6,9 @@ import (
"encoding/json" "encoding/json"
"errors" "errors"
"fmt" "fmt"
"net/http"
"time"
"github.com/duke-git/lancet/v2/slice" "github.com/duke-git/lancet/v2/slice"
"github.com/emvi/logbuch" "github.com/emvi/logbuch"
"github.com/muety/artifex/v2" "github.com/muety/artifex/v2"
@ -13,8 +16,6 @@ import (
"github.com/muety/wakapi/models" "github.com/muety/wakapi/models"
wakatime "github.com/muety/wakapi/models/compat/wakatime/v1" wakatime "github.com/muety/wakapi/models/compat/wakatime/v1"
"github.com/muety/wakapi/utils" "github.com/muety/wakapi/utils"
"net/http"
"time"
) )
// data example: https://github.com/muety/wakapi/issues/323#issuecomment-1627467052 // data example: https://github.com/muety/wakapi/issues/323#issuecomment-1627467052
@ -37,22 +38,52 @@ func (w *WakatimeDumpImporter) Import(user *models.User, minFrom time.Time, maxT
out := make(chan *models.Heartbeat) out := make(chan *models.Heartbeat)
logbuch.Info("running wakatime dump import for user '%s'", user.ID) logbuch.Info("running wakatime dump import for user '%s'", user.ID)
dump_exist := false
url := config.WakatimeApiUrl + config.WakatimeApiDataDumpUrl // this importer only works with wakatime currently, so no point in using user's custom wakatime api url url := config.WakatimeApiUrl + config.WakatimeApiDataDumpUrl // this importer only works with wakatime currently, so no point in using user's custom wakatime api url
req, _ := http.NewRequest(http.MethodPost, url, bytes.NewBuffer([]byte(`{ "type": "heartbeats", "email_when_finished": false }`))) req, _ := http.NewRequest(http.MethodPost, url, bytes.NewBuffer([]byte(`{ "type": "heartbeats", "email_when_finished": false }`)))
res, err := utils.RaiseForStatus(w.httpClient.Do(w.withHeaders(req))) res, err := utils.RaiseForStatus(w.httpClient.Do(w.withHeaders(req)))
if err != nil { if err != nil {
return nil, err if res.StatusCode == 400 {
var datadumpError wakatime.DataDumpResultErrorModel
if err := json.NewDecoder(res.Body).Decode(&datadumpError); err != nil {
return nil, err
}
if datadumpError.Error == "Wait for your current export to expire before creating another."{
dump_exist = true
}
} else {
return nil, err
}
} }
defer res.Body.Close() defer res.Body.Close()
var datadumpData wakatime.DataDumpResultViewModel var datadumpData wakatime.DataDumpResultViewModel
if err := json.NewDecoder(res.Body).Decode(&datadumpData); err != nil { if !dump_exist {
return nil, err if err := json.NewDecoder(res.Body).Decode(&datadumpData); err != nil {
return nil, err
}
} }
var readyPollTimer *artifex.DispatchTicker var readyPollTimer *artifex.DispatchTicker
// callbacks // callbacks
getLatestDump := func(user *models.User) (bool, *wakatime.DataDumpData, error) {
req, _ := http.NewRequest(http.MethodGet, url, nil)
res, err := utils.RaiseForStatus(w.httpClient.Do(w.withHeaders(req)))
if err != nil {
return false, nil, err
}
var datadumpData wakatime.DataDumpViewModel
if err := json.NewDecoder(res.Body).Decode(&datadumpData); err != nil {
return false, nil, err
}
dump := datadumpData.Data[0]
return dump.Status == "Completed", dump, nil
}
checkDumpReady := func(dumpId string, user *models.User) (bool, *wakatime.DataDumpData, error) { checkDumpReady := func(dumpId string, user *models.User) (bool, *wakatime.DataDumpData, error) {
req, _ := http.NewRequest(http.MethodGet, url, nil) req, _ := http.NewRequest(http.MethodGet, url, nil)
res, err := utils.RaiseForStatus(w.httpClient.Do(w.withHeaders(req))) res, err := utils.RaiseForStatus(w.httpClient.Do(w.withHeaders(req)))
@ -131,12 +162,21 @@ func (w *WakatimeDumpImporter) Import(user *models.User, minFrom time.Time, maxT
// start polling for dump to be ready // start polling for dump to be ready
readyPollTimer, err = w.queue.DispatchEvery(func() { readyPollTimer, err = w.queue.DispatchEvery(func() {
u := *user u := *user
ok, dump, err := checkDumpReady(datadumpData.Data.Id, &u) if dump_exist {
logbuch.Info("waiting for data dump '%s' for user '%s' to become downloadable (%.2f percent complete)", datadumpData.Data.Id, u.ID, dump.PercentComplete) ok, dump, err := getLatestDump(&u)
if err != nil { if err != nil {
onDumpFailed(err, &u) onDumpFailed(err, &u)
} else if ok { } else if ok {
onDumpReady(dump, &u, out) onDumpReady(dump, &u, out)
}
} else {
ok, dump, err := checkDumpReady(datadumpData.Data.Id, &u)
logbuch.Info("waiting for data dump '%s' for user '%s' to become downloadable (%.2f percent complete)", datadumpData.Data.Id, u.ID, dump.PercentComplete)
if err != nil {
onDumpFailed(err, &u)
} else if ok {
onDumpReady(dump, &u, out)
}
} }
}, 10*time.Second) }, 10*time.Second)