sync_engine_initializer.cc revision d0247b1b59f9c528cb6df88b4f2b9afaf80d181e
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/sync_engine_initializer.h"
6
7#include "base/bind.h"
8#include "base/callback.h"
9#include "base/logging.h"
10#include "chrome/browser/drive/drive_api_service.h"
11#include "chrome/browser/drive/drive_api_util.h"
12#include "chrome/browser/google_apis/drive_api_parser.h"
13#include "chrome/browser/google_apis/gdata_wapi_parser.h"
14#include "chrome/browser/sync_file_system/drive_backend/metadata_database.h"
15#include "chrome/browser/sync_file_system/drive_backend_v1/drive_file_sync_util.h"
16
17namespace sync_file_system {
18namespace drive_backend {
19
20namespace {
21
22// TODO(tzik): Move them to separate file.
23const char kSyncRootFolderTitle[] = "Chrome Syncable FileSystem";
24
25////////////////////////////////////////////////////////////////////////////////
26// Functions below are for wrapping the access to legacy GData WAPI classes.
27
28bool IsDeleted(const google_apis::ResourceEntry& entry) {
29  return entry.deleted();
30}
31
32bool HasNoParents(const google_apis::ResourceEntry& entry) {
33  return !entry.GetLinkByType(google_apis::Link::LINK_PARENT);
34}
35
36bool HasFolderAsParent(const google_apis::ResourceEntry& entry,
37                       const std::string& parent_id) {
38  const ScopedVector<google_apis::Link>& links = entry.links();
39  for (ScopedVector<google_apis::Link>::const_iterator itr = links.begin();
40       itr != links.end(); ++itr) {
41    const google_apis::Link& link = **itr;
42    if (link.type() != google_apis::Link::LINK_PARENT)
43      continue;
44    if (drive::util::ExtractResourceIdFromUrl(link.href()) == parent_id)
45      return true;
46  }
47  return false;
48}
49
50bool LessOnCreationTime(const google_apis::ResourceEntry& left,
51                        const google_apis::ResourceEntry& right) {
52  return left.published_time() < right.published_time();
53}
54
55// Posts a request to continue listing.  Returns false if the list doesn't need
56// listing anymore.
57bool GetRemainingFileList(
58    google_apis::CancelCallback* cancel_callback,
59    drive::DriveServiceInterface* api_service,
60    const google_apis::ResourceList& resource_list,
61    const google_apis::GetResourceListCallback& callback) {
62  GURL next_url;
63  if (!resource_list.GetNextFeedURL(&next_url))
64    return false;
65
66  *cancel_callback = api_service->GetRemainingFileList(next_url, callback);
67  return true;
68}
69
70std::string GetID(const google_apis::ResourceEntry& entry) {
71  return entry.resource_id();
72}
73
74ScopedVector<google_apis::FileResource> ConvertResourceEntriesToFileResources(
75    const ScopedVector<google_apis::ResourceEntry>& entries) {
76  ScopedVector<google_apis::FileResource> resources;
77  for (ScopedVector<google_apis::ResourceEntry>::const_iterator itr =
78           entries.begin();
79       itr != entries.end();
80       ++itr) {
81    resources.push_back(
82        drive::util::ConvertResourceEntryToFileResource(
83            **itr).release());
84  }
85  return resources.Pass();
86}
87
88// Functions above are for wrapping the access to legacy GData WAPI classes.
89////////////////////////////////////////////////////////////////////////////////
90
91}  // namespace
92
93SyncEngineInitializer::SyncEngineInitializer(
94    base::SequencedTaskRunner* task_runner,
95    drive::DriveServiceInterface* drive_service,
96    const base::FilePath& database_path)
97    : task_runner_(task_runner),
98      drive_service_(drive_service),
99      database_path_(database_path),
100      largest_change_id_(0),
101      weak_ptr_factory_(this) {
102  DCHECK(task_runner);
103  DCHECK(drive_service_);
104}
105
106SyncEngineInitializer::~SyncEngineInitializer() {
107  if (!cancel_callback_.is_null())
108    cancel_callback_.Run();
109}
110
111void SyncEngineInitializer::Run(const SyncStatusCallback& callback) {
112  MetadataDatabase::Create(
113      task_runner_.get(), database_path_,
114      base::Bind(&SyncEngineInitializer::DidCreateMetadataDatabase,
115                 weak_ptr_factory_.GetWeakPtr(), callback));
116}
117
118scoped_ptr<MetadataDatabase> SyncEngineInitializer::PassMetadataDatabase() {
119  return metadata_database_.Pass();
120}
121
122void SyncEngineInitializer::DidCreateMetadataDatabase(
123    const SyncStatusCallback& callback,
124    SyncStatusCode status,
125    scoped_ptr<MetadataDatabase> instance) {
126  if (status != SYNC_STATUS_OK) {
127    callback.Run(status);
128    return;
129  }
130
131  DCHECK(instance);
132  metadata_database_ = instance.Pass();
133  if (metadata_database_->HasSyncRoot()) {
134    callback.Run(SYNC_STATUS_OK);
135    return;
136  }
137
138  GetAboutResource(callback);
139}
140
141void SyncEngineInitializer::GetAboutResource(
142    const SyncStatusCallback& callback) {
143  drive_service_->GetAboutResource(
144      base::Bind(&SyncEngineInitializer::DidGetAboutResource,
145                 weak_ptr_factory_.GetWeakPtr(), callback));
146}
147
148void SyncEngineInitializer::DidGetAboutResource(
149    const SyncStatusCallback& callback,
150    google_apis::GDataErrorCode error,
151    scoped_ptr<google_apis::AboutResource> about_resource) {
152  cancel_callback_.Reset();
153  if (error != google_apis::HTTP_SUCCESS) {
154    callback.Run(GDataErrorCodeToSyncStatusCode(error));
155    return;
156  }
157
158  DCHECK(about_resource);
159  root_folder_id_ = about_resource->root_folder_id();
160  largest_change_id_ = about_resource->largest_change_id();
161
162  DCHECK(!root_folder_id_.empty());
163  FindSyncRoot(callback);
164}
165
166void SyncEngineInitializer::FindSyncRoot(const SyncStatusCallback& callback) {
167  cancel_callback_ = drive_service_->SearchByTitle(
168      kSyncRootFolderTitle,
169      std::string(), // parent_folder_id
170      base::Bind(&SyncEngineInitializer::DidFindSyncRoot,
171                 weak_ptr_factory_.GetWeakPtr(),
172                 callback));
173}
174
175void SyncEngineInitializer::DidFindSyncRoot(
176    const SyncStatusCallback& callback,
177    google_apis::GDataErrorCode error,
178    scoped_ptr<google_apis::ResourceList> resource_list) {
179  cancel_callback_.Reset();
180  if (error != google_apis::HTTP_SUCCESS) {
181    callback.Run(GDataErrorCodeToSyncStatusCode(error));
182    return;
183  }
184
185  ScopedVector<google_apis::ResourceEntry>* entries =
186      resource_list->mutable_entries();
187  for (ScopedVector<google_apis::ResourceEntry>::iterator itr =
188           entries->begin();
189       itr != entries->end(); ++itr) {
190    google_apis::ResourceEntry* entry = *itr;
191
192    // Ignore deleted folder.
193    if (IsDeleted(*entry))
194      continue;
195
196    // Pick an orphaned folder or a direct child of the root folder and
197    // ignore others.
198    DCHECK(!root_folder_id_.empty());
199    if (!HasNoParents(*entry) && !HasFolderAsParent(*entry, root_folder_id_))
200      continue;
201
202    if (!sync_root_folder_ || LessOnCreationTime(*entry, *sync_root_folder_)) {
203      sync_root_folder_.reset(entry);
204      *itr = NULL;
205    }
206  }
207
208  // If there are more results, retrieve them.
209  if (GetRemainingFileList(
210          &cancel_callback_,
211          drive_service_, *resource_list,
212          base::Bind(&SyncEngineInitializer::DidFindSyncRoot,
213                     weak_ptr_factory_.GetWeakPtr(),
214                     callback)))
215    return;
216
217  if (!sync_root_folder_) {
218    CreateSyncRoot(callback);
219    return;
220  }
221
222  if (!HasNoParents(*sync_root_folder_)) {
223    DetachSyncRoot(callback);
224    return;
225  }
226
227  ListAppRootFolders(callback);
228}
229
230void SyncEngineInitializer::CreateSyncRoot(const SyncStatusCallback& callback) {
231  DCHECK(!sync_root_folder_);
232  cancel_callback_ = drive_service_->AddNewDirectory(
233      root_folder_id_, kSyncRootFolderTitle,
234      base::Bind(&SyncEngineInitializer::DidCreateSyncRoot,
235                 weak_ptr_factory_.GetWeakPtr(),
236                 callback));
237}
238
239void SyncEngineInitializer::DidCreateSyncRoot(
240    const SyncStatusCallback& callback,
241    google_apis::GDataErrorCode error,
242    scoped_ptr<google_apis::ResourceEntry> entry) {
243  DCHECK(!sync_root_folder_);
244  cancel_callback_.Reset();
245  if (error != google_apis::HTTP_SUCCESS &&
246      error != google_apis::HTTP_CREATED) {
247    callback.Run(GDataErrorCodeToSyncStatusCode(error));
248    return;
249  }
250
251  FindSyncRoot(callback);
252}
253
254void SyncEngineInitializer::DetachSyncRoot(const SyncStatusCallback& callback) {
255  DCHECK(sync_root_folder_);
256  cancel_callback_ = drive_service_->RemoveResourceFromDirectory(
257      root_folder_id_, GetID(*sync_root_folder_),
258      base::Bind(&SyncEngineInitializer::DidDetachSyncRoot,
259                 weak_ptr_factory_.GetWeakPtr(),
260                 callback));
261}
262
263void SyncEngineInitializer::DidDetachSyncRoot(
264    const SyncStatusCallback& callback,
265    google_apis::GDataErrorCode error) {
266  cancel_callback_.Reset();
267  if (error != google_apis::HTTP_SUCCESS) {
268    callback.Run(GDataErrorCodeToSyncStatusCode(error));
269    return;
270  }
271  ListAppRootFolders(callback);
272}
273
274void SyncEngineInitializer::ListAppRootFolders(
275    const SyncStatusCallback& callback) {
276  DCHECK(sync_root_folder_);
277  cancel_callback_ = drive_service_->GetResourceListInDirectory(
278      GetID(*sync_root_folder_),
279      base::Bind(&SyncEngineInitializer::DidListAppRootFolders,
280                 weak_ptr_factory_.GetWeakPtr(),
281                 callback));
282}
283
284void SyncEngineInitializer::DidListAppRootFolders(
285    const SyncStatusCallback& callback,
286    google_apis::GDataErrorCode error,
287    scoped_ptr<google_apis::ResourceList> resource_list) {
288  cancel_callback_.Reset();
289  if (error != google_apis::HTTP_SUCCESS) {
290    callback.Run(GDataErrorCodeToSyncStatusCode(error));
291    return;
292  }
293
294  ScopedVector<google_apis::ResourceEntry>* new_entries =
295      resource_list->mutable_entries();
296  app_root_folders_.insert(app_root_folders_.end(),
297                           new_entries->begin(), new_entries->end());
298  new_entries->weak_clear();
299
300  if (GetRemainingFileList(
301          &cancel_callback_,
302          drive_service_,
303          *resource_list,
304          base::Bind(&SyncEngineInitializer::DidListAppRootFolders,
305                     weak_ptr_factory_.GetWeakPtr(), callback)))
306    return;
307
308  PopulateDatabase(callback);
309}
310
311void SyncEngineInitializer::PopulateDatabase(
312    const SyncStatusCallback& callback) {
313  DCHECK(sync_root_folder_);
314  metadata_database_->PopulateInitialData(
315      largest_change_id_,
316      *drive::util::ConvertResourceEntryToFileResource(
317          *sync_root_folder_),
318      ConvertResourceEntriesToFileResources(app_root_folders_),
319      base::Bind(&SyncEngineInitializer::DidPopulateDatabase,
320                 weak_ptr_factory_.GetWeakPtr(),
321                 callback));
322}
323
324void SyncEngineInitializer::DidPopulateDatabase(
325    const SyncStatusCallback& callback,
326    SyncStatusCode status) {
327  if (status != SYNC_STATUS_OK) {
328    callback.Run(status);
329    return;
330  }
331
332  callback.Run(SYNC_STATUS_OK);
333}
334
335}  // namespace drive_backend
336}  // namespace sync_file_system
337