mirror of
https://github.com/Xevion/todoist-late-reset.git
synced 2025-12-05 23:16:36 -06:00
omitempty for sync response, sync handling for items, most state handling, working
This commit is contained in:
@@ -1,47 +1,53 @@
|
|||||||
package api
|
package api
|
||||||
|
|
||||||
import "encoding/json"
|
import (
|
||||||
|
"encoding/json"
|
||||||
|
"fmt"
|
||||||
|
"io"
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
type SyncResponse struct {
|
type SyncResponse struct {
|
||||||
// The new token to use for the next incremental sync.
|
// The new token to use for the next incremental sync.
|
||||||
SyncToken string `json:"sync_token"`
|
SyncToken string `json:"sync_token,omitempty"`
|
||||||
// If true, this response is a full sync and the client should not merge but instead replace it's state.
|
// If true, this response is a full sync and the client should not merge but instead replace its state.
|
||||||
FullSync bool `json:"full_sync"`
|
FullSync bool `json:"full_sync,omitempty"`
|
||||||
// Used for commands where local IDs are temporarily chosen by the client and need to be mapped to the server's IDs.
|
// Used for commands where local IDs are temporarily chosen by the client and need to be mapped to the server's IDs.
|
||||||
TempIDMapping map[string]interface{} `json:"temp_id_mapping"`
|
TempIDMapping map[string]interface{} `json:"temp_id_mapping,omitempty"`
|
||||||
Items []interface{} `json:"items"`
|
Items []Item `json:"items,omitempty"`
|
||||||
// CompletedInfo []interface{} `json:"completed_info"`
|
// CompletedInfo []interface{} `json:"completed_info,omitempty"`
|
||||||
// Collaborators []interface{} `json:"collaborators"`
|
// Collaborators []interface{} `json:"collaborators,omitempty"`
|
||||||
// CollaboratorStates []interface{} `json:"collaborator_states"`
|
// CollaboratorStates []interface{} `json:"collaborator_states,omitempty"`
|
||||||
// DayOrders map[string]interface{} `json:"day_orders"`
|
// DayOrders map[string]interface{} `json:"day_orders,omitempty"`
|
||||||
// Filters []interface{} `json:"filters"`
|
// Filters []interface{} `json:"filters,omitempty"`
|
||||||
// Labels []interface{} `json:"labels"`
|
// Labels []interface{} `json:"labels,omitempty"`
|
||||||
// LiveNotifications []interface{} `json:"live_notifications"`
|
// LiveNotifications []interface{} `json:"live_notifications,omitempty"`
|
||||||
// LiveNotificationsLastReadID string `json:"live_notifications_last_read_id"`
|
// LiveNotificationsLastReadID string `json:"live_notifications_last_read_id,omitempty"`
|
||||||
// Locations []interface{} `json:"locations"`
|
// Locations []interface{} `json:"locations,omitempty"`
|
||||||
// Notes []interface{} `json:"notes"`
|
// Notes []interface{} `json:"notes,omitempty"`
|
||||||
// ProjectNotes []interface{} `json:"project_notes"`
|
// ProjectNotes []interface{} `json:"project_notes,omitempty"`
|
||||||
// Projects []interface{} `json:"projects"`
|
// Projects []interface{} `json:"projects,omitempty"`
|
||||||
// Reminders []interface{} `json:"reminders"`
|
// Reminders []interface{} `json:"reminders,omitempty"`
|
||||||
// Sections []interface{} `json:"sections"`
|
// Sections []interface{} `json:"sections,omitempty"`
|
||||||
// Stats map[string]interface{} `json:"stats"`
|
// Stats map[string]interface{} `json:"stats,omitempty"`
|
||||||
// SettingsNotifications map[string]interface{} `json:"settings_notifications"`
|
// SettingsNotifications map[string]interface{} `json:"settings_notifications,omitempty"`
|
||||||
// User map[string]interface{} `json:"user"`
|
// User map[string]interface{} `json:"user,omitempty"`
|
||||||
// UserPlanLimits map[string]interface{} `json:"user_plan_limits"`
|
// UserPlanLimits map[string]interface{} `json:"user_plan_limits,omitempty"`
|
||||||
// UserSettings map[string]interface{} `json:"user_settings"`
|
// UserSettings map[string]interface{} `json:"user_settings,omitempty"`
|
||||||
}
|
}
|
||||||
|
|
||||||
type State struct {
|
type State struct {
|
||||||
Items map[string]Item
|
Items map[string]Item
|
||||||
}
|
}
|
||||||
|
|
||||||
type Changes struct {
|
// NewState creates a new blank state with initialized fields.
|
||||||
Added []string
|
func NewState() *State {
|
||||||
Updated []string
|
return &State{
|
||||||
Deleted []string
|
Items: make(map[string]Item),
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// sync synchronizes the client's state with the server. If the full parameter is set to true,
|
// Synchronize the client's state with the server. If the full parameter is set to true,
|
||||||
// a full synchronization is performed, otherwise, a partial synchronization is done.
|
// a full synchronization is performed, otherwise, a partial synchronization is done.
|
||||||
// This strongly mutates the client's state.
|
// This strongly mutates the client's state.
|
||||||
//
|
//
|
||||||
@@ -54,54 +60,72 @@ type Changes struct {
|
|||||||
// int - the number of changes synchronized.
|
// int - the number of changes synchronized.
|
||||||
// *Changes - a pointer to a Changes struct containing the details of the changes.
|
// *Changes - a pointer to a Changes struct containing the details of the changes.
|
||||||
// error - an error object if an error occurred during synchronization, otherwise nil.
|
// error - an error object if an error occurred during synchronization, otherwise nil.
|
||||||
func (sc *SyncClient) Synchronize(full bool) (*Changes, error) {
|
func (sc *SyncClient) Synchronize(full bool) (*[]byte, error) {
|
||||||
if sc.RequireFullSync {
|
if sc.requireFullSync || sc.lastFullSync.IsZero() {
|
||||||
|
fmt.Printf("Performing full sync\n")
|
||||||
sc.syncToken = "*"
|
sc.syncToken = "*"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
resourceTypes := make([]string, 4)
|
||||||
|
for resourceType := range sc.resourceTypes {
|
||||||
|
resourceTypes = append(resourceTypes, string(resourceType))
|
||||||
|
}
|
||||||
|
|
||||||
body := map[string]interface{}{
|
body := map[string]interface{}{
|
||||||
"sync_token": sc.syncToken,
|
"sync_token": sc.syncToken,
|
||||||
|
"resource_types": resourceTypes,
|
||||||
}
|
}
|
||||||
|
|
||||||
jsonBody, err := json.Marshal(body)
|
jsonBody, err := json.Marshal(body)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return 0, nil, err
|
return nil, fmt.Errorf("failed to marshal request body: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
res, err := sc.post("/sync", nil, jsonBody)
|
res, err := sc.post("/sync", nil, jsonBody)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, fmt.Errorf("failed to post sync request: %w", err)
|
||||||
}
|
}
|
||||||
|
now := time.Now()
|
||||||
|
|
||||||
defer res.Body.Close()
|
defer res.Body.Close()
|
||||||
var syncResponse SyncResponse
|
bodyBytes, err := io.ReadAll(res.Body)
|
||||||
if err := json.NewDecoder(res.Body).Decode(&syncResponse); err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, fmt.Errorf("failed to read response body: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var syncResponse SyncResponse
|
||||||
|
if err := json.Unmarshal(bodyBytes, &syncResponse); err != nil {
|
||||||
|
return nil, fmt.Errorf("failed to decode sync response: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Always set in every request
|
||||||
sc.syncToken = syncResponse.SyncToken
|
sc.syncToken = syncResponse.SyncToken
|
||||||
|
sc.lastSync = now
|
||||||
|
|
||||||
// changes := &Changes{
|
// Simple, replace the state with the new items
|
||||||
// Added: []string{},
|
if syncResponse.FullSync {
|
||||||
// Updated: []string{},
|
sc.lastFullSync = now
|
||||||
// Deleted: []string{},
|
sc.requireFullSync = false
|
||||||
// }
|
|
||||||
|
|
||||||
// // Process the items in syncResponse.Items to populate changes
|
if syncResponse.Items != nil {
|
||||||
// for _, item := range syncResponse.Items {
|
sc.State.Items = make(map[string]Item)
|
||||||
// // Assuming item is a map[string]interface{} and has a "status" field
|
for _, item := range syncResponse.Items {
|
||||||
// itemMap := item.(map[string]interface{})
|
sc.State.Items[item.ID] = item
|
||||||
// if status, ok := itemMap["status"].(string); ok {
|
}
|
||||||
// switch status {
|
}
|
||||||
// case "added":
|
} else {
|
||||||
// changes.Added = append(changes.Added, itemMap["id"].(string))
|
// Partial sync
|
||||||
// case "updated":
|
if syncResponse.Items != nil {
|
||||||
// changes.Updated = append(changes.Updated, itemMap["id"].(string))
|
for _, item := range syncResponse.Items {
|
||||||
// case "deleted":
|
sc.State.Items[item.ID] = item
|
||||||
// changes.Deleted = append(changes.Deleted, itemMap["id"].(string))
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
|
|
||||||
return changes, nil
|
if item.IsDeleted {
|
||||||
|
delete(sc.State.Items, item.ID)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil, nil
|
||||||
|
// return changes, nil
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user