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