diff --git a/client/todo/sync.go b/client/todo/sync.go index 5ff915a..64568ce 100644 --- a/client/todo/sync.go +++ b/client/todo/sync.go @@ -80,6 +80,10 @@ func syncAction(context context.Context, c *cli.Command) error { response.SyncedTodos = append(response.SyncedTodos, todo.ServerTodo) } else { response.SyncedTodos = append(response.SyncedTodos, todo.LocalTodo) + if common.AskUserBool("Do you wish to push this to the server?") { + println("TODO not yet implemented") + // TODO push to server + } } } } diff --git a/common/todo.go b/common/todo.go index 6283958..58334fd 100644 --- a/common/todo.go +++ b/common/todo.go @@ -108,7 +108,8 @@ func (todo Todo) PrintIndexed(index int) { } func (t Todo) IsEqual(other Todo) bool { - return t.Name == other.Name && + return t.Id == other.Id && + t.Name == other.Name && t.Description == other.Description && t.Status == other.Status && t.Owner == other.Owner && @@ -116,7 +117,8 @@ func (t Todo) IsEqual(other Todo) bool { } func (t Todo) IsEqualIgnoringStatus(other Todo) bool { - return t.Name == other.Name && + return t.Id == other.Id && + t.Name == other.Name && t.Description == other.Description && t.Owner == other.Owner && t.Deleted == other.Deleted diff --git a/server/handler/loginHandler.go b/server/handler/loginHandler.go index a3a4f91..bb36d28 100644 --- a/server/handler/loginHandler.go +++ b/server/handler/loginHandler.go @@ -21,6 +21,7 @@ func LoginHandler(w http.ResponseWriter, r *http.Request) { if handleError(w, http.StatusInternalServerError, err) { return } + // Check password password := db.GetFromBucketByKey(common.UserBucket, user.Username) if user.ComparePasswords(password) { w.Header().Set(common.AuthHeader, jwt.CreateUserJWT(user.Username)) diff --git a/server/handler/syncHandler.go b/server/handler/syncHandler.go index 8471ca7..a4cb6a3 100644 --- a/server/handler/syncHandler.go +++ b/server/handler/syncHandler.go @@ -10,14 +10,14 @@ import ( ) func SyncHandler(w http.ResponseWriter, r *http.Request) { + // 1. Setup: Auth, Decode, Database Connection user, err := jwt.GetVerifiedUser(r) if handleError(w, http.StatusUnauthorized, err) { return } var todoList common.TodoList - err = json.NewDecoder(r.Body).Decode(&todoList) - if handleError(w, http.StatusBadRequest, err) { + if handleError(w, http.StatusBadRequest, json.NewDecoder(r.Body).Decode(&todoList)) { return } @@ -26,62 +26,76 @@ func SyncHandler(w http.ResponseWriter, r *http.Request) { return } + // 2. Load Server State serverTodos := store.GetTodoMap(user) - - var response = common.SyncResponse{ + response := common.SyncResponse{ SyncedTodos: []common.Todo{}, MisMatchingTodos: []common.MisMatchingTodo{}, } + // 3. Process Client Updates for _, clientTodo := range todoList.Todos { + + // Default to "Now" if clientTodo.LastModified.IsZero() { clientTodo.LastModified = time.Now().UTC() } + serverTodo, exists := serverTodos[clientTodo.Id] + // New Item (Does not exist on server) if !exists { if clientTodo.Deleted { - continue + continue // Ignore deleted items that don't exist } - err = clientTodo.Store(store, user) - if handleError(w, http.StatusInternalServerError, err) { + + if handleError(w, http.StatusInternalServerError, clientTodo.Store(store, user)) { return } + // Add to map so it gets returned in the "Synced" list serverTodos[clientTodo.Id] = clientTodo continue } + + // Item is deleted (Client wants to delete) if clientTodo.Deleted { + // Only delete if client is newer than server if clientTodo.LastModified.After(serverTodo.LastModified) { - err = store.RemoveValueFromBucket(user, clientTodo.Id) - if handleError(w, http.StatusInternalServerError, err) { + if handleError(w, http.StatusInternalServerError, store.RemoveValueFromBucket(user, clientTodo.Id)) { return } + // Remove from map so it is NOT returned in the "Synced" list delete(serverTodos, clientTodo.Id) } - } else if !serverTodo.IsEqual(clientTodo) { - if serverTodo.IsEqualIgnoringStatus(clientTodo) && clientTodo.LastModified.After(serverTodo.LastModified) { - err = clientTodo.Store(store, user) - if handleError(w, http.StatusInternalServerError, err) { - return - } - serverTodos[clientTodo.Id] = clientTodo - } else { - response.MisMatchingTodos = append(response.MisMatchingTodos, common.MisMatchingTodo{ - ServerTodo: serverTodo, - LocalTodo: clientTodo, - }) - delete(serverTodos, clientTodo.Id) + continue + } + + // Item is updated (Item exists and is not deleted) --- + if serverTodo.IsEqual(clientTodo) { + continue + } + + isMergeable := serverTodo.IsEqualIgnoringStatus(clientTodo) && clientTodo.LastModified.After(serverTodo.LastModified) + + if isMergeable { + if handleError(w, http.StatusInternalServerError, clientTodo.Store(store, user)) { + return } + serverTodos[clientTodo.Id] = clientTodo + } else { + response.MisMatchingTodos = append(response.MisMatchingTodos, common.MisMatchingTodo{ + ServerTodo: serverTodo, + LocalTodo: clientTodo, + }) + delete(serverTodos, clientTodo.Id) } } + // Build Final Response for _, todo := range serverTodos { response.SyncedTodos = append(response.SyncedTodos, todo) } w.Header().Set("Content-Type", "application/json") - err = json.NewEncoder(w).Encode(response) - if handleError(w, http.StatusInternalServerError, err) { - return - } + handleError(w, http.StatusInternalServerError, json.NewEncoder(w).Encode(response)) }