1// Copyright 2013 The Chromium Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5#include "chrome/browser/sync_file_system/drive_backend/list_changes_task.h"
6
7#include <vector>
8
9#include "base/bind.h"
10#include "base/format_macros.h"
11#include "base/location.h"
12#include "base/strings/stringprintf.h"
13#include "chrome/browser/drive/drive_service_interface.h"
14#include "chrome/browser/sync_file_system/drive_backend/drive_backend_util.h"
15#include "chrome/browser/sync_file_system/drive_backend/metadata_database.h"
16#include "chrome/browser/sync_file_system/drive_backend/metadata_database.pb.h"
17#include "chrome/browser/sync_file_system/drive_backend/sync_engine_context.h"
18#include "chrome/browser/sync_file_system/drive_backend/sync_task_manager.h"
19#include "chrome/browser/sync_file_system/drive_backend/sync_task_token.h"
20#include "chrome/browser/sync_file_system/logger.h"
21#include "chrome/browser/sync_file_system/syncable_file_system_util.h"
22#include "google_apis/drive/drive_api_parser.h"
23#include "google_apis/drive/gdata_wapi_parser.h"
24
25namespace sync_file_system {
26namespace drive_backend {
27
28ListChangesTask::ListChangesTask(SyncEngineContext* sync_context)
29    : sync_context_(sync_context),
30      weak_ptr_factory_(this) {
31}
32
33ListChangesTask::~ListChangesTask() {
34}
35
36void ListChangesTask::RunPreflight(scoped_ptr<SyncTaskToken> token) {
37  token->InitializeTaskLog("List Changes");
38
39  if (!IsContextReady()) {
40    token->RecordLog("Failed to get required service.");
41    SyncTaskManager::NotifyTaskDone(token.Pass(), SYNC_STATUS_FAILED);
42    return;
43  }
44
45  SyncTaskManager::UpdateTaskBlocker(
46      token.Pass(),
47      scoped_ptr<TaskBlocker>(new TaskBlocker),
48      base::Bind(&ListChangesTask::StartListing,
49                 weak_ptr_factory_.GetWeakPtr()));
50}
51
52void ListChangesTask::StartListing(scoped_ptr<SyncTaskToken> token) {
53  drive_service()->GetChangeList(
54      metadata_database()->GetLargestFetchedChangeID() + 1,
55      base::Bind(&ListChangesTask::DidListChanges,
56                 weak_ptr_factory_.GetWeakPtr(), base::Passed(&token)));
57}
58
59void ListChangesTask::DidListChanges(
60    scoped_ptr<SyncTaskToken> token,
61    google_apis::GDataErrorCode error,
62    scoped_ptr<google_apis::ChangeList> change_list) {
63  SyncStatusCode status = GDataErrorCodeToSyncStatusCode(error);
64  if (status != SYNC_STATUS_OK) {
65    token->RecordLog("Failed to fetch change list.");
66    SyncTaskManager::NotifyTaskDone(
67        token.Pass(), SYNC_STATUS_NETWORK_ERROR);
68    return;
69  }
70
71  if (!change_list) {
72    NOTREACHED();
73    token->RecordLog("Got invalid change list.");
74    SyncTaskManager::NotifyTaskDone(token.Pass(), SYNC_STATUS_FAILED);
75    return;
76  }
77
78  std::vector<google_apis::ChangeResource*> changes;
79  change_list->mutable_items()->release(&changes);
80
81  change_list_.reserve(change_list_.size() + changes.size());
82  for (size_t i = 0; i < changes.size(); ++i)
83    change_list_.push_back(changes[i]);
84
85  if (!change_list->next_link().is_empty()) {
86    drive_service()->GetRemainingChangeList(
87        change_list->next_link(),
88        base::Bind(
89            &ListChangesTask::DidListChanges,
90            weak_ptr_factory_.GetWeakPtr(),
91            base::Passed(&token)));
92    return;
93  }
94
95  if (change_list_.empty()) {
96    token->RecordLog("Got no change.");
97    SyncTaskManager::NotifyTaskDone(
98        token.Pass(), SYNC_STATUS_NO_CHANGE_TO_SYNC);
99    return;
100  }
101
102  scoped_ptr<TaskBlocker> task_blocker(new TaskBlocker);
103  task_blocker->exclusive = true;
104  SyncTaskManager::UpdateTaskBlocker(
105      token.Pass(),
106      task_blocker.Pass(),
107      base::Bind(&ListChangesTask::CheckInChangeList,
108                 weak_ptr_factory_.GetWeakPtr(),
109                 change_list->largest_change_id()));
110}
111
112void ListChangesTask::CheckInChangeList(int64 largest_change_id,
113                                        scoped_ptr<SyncTaskToken> token) {
114  token->RecordLog(base::StringPrintf(
115      "Got %" PRIuS " changes, updating MetadataDatabase.",
116      change_list_.size()));
117
118  DCHECK(file_ids_.empty());
119  file_ids_.reserve(change_list_.size());
120  for (size_t i = 0; i < change_list_.size(); ++i)
121    file_ids_.push_back(change_list_[i]->file_id());
122
123  SyncStatusCode status =
124      metadata_database()->UpdateByChangeList(
125          largest_change_id, change_list_.Pass());
126  if (status != SYNC_STATUS_OK) {
127    SyncTaskManager::NotifyTaskDone(token.Pass(), status);
128    return;
129  }
130
131  status = metadata_database()->SweepDirtyTrackers(file_ids_);
132  SyncTaskManager::NotifyTaskDone(token.Pass(), status);
133}
134
135bool ListChangesTask::IsContextReady() {
136  return sync_context_->GetMetadataDatabase() &&
137      sync_context_->GetDriveService();
138}
139
140MetadataDatabase* ListChangesTask::metadata_database() {
141  return sync_context_->GetMetadataDatabase();
142}
143
144drive::DriveServiceInterface* ListChangesTask::drive_service() {
145  set_used_network(true);
146  return sync_context_->GetDriveService();
147}
148
149}  // namespace drive_backend
150}  // namespace sync_file_system
151