Rename gdata_errorcode.h to drive_api_error_codes.h
[chromium-blink-merge.git] / chrome / browser / drive / fake_drive_service.cc
blob52d790a54ef37145e611106d610f60529c68200d
1 // Copyright (c) 2012 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.
5 #include "chrome/browser/drive/fake_drive_service.h"
7 #include <string>
9 #include "base/files/file_util.h"
10 #include "base/json/json_string_value_serializer.h"
11 #include "base/logging.h"
12 #include "base/md5.h"
13 #include "base/message_loop/message_loop.h"
14 #include "base/strings/string_number_conversions.h"
15 #include "base/strings/string_split.h"
16 #include "base/strings/string_tokenizer.h"
17 #include "base/strings/string_util.h"
18 #include "base/strings/stringprintf.h"
19 #include "base/strings/utf_string_conversions.h"
20 #include "base/values.h"
21 #include "chrome/browser/drive/drive_api_util.h"
22 #include "google_apis/drive/drive_api_parser.h"
23 #include "google_apis/drive/test_util.h"
24 #include "net/base/escape.h"
25 #include "net/base/url_util.h"
27 using google_apis::AboutResource;
28 using google_apis::AboutResourceCallback;
29 using google_apis::AppList;
30 using google_apis::AppListCallback;
31 using google_apis::AuthStatusCallback;
32 using google_apis::AuthorizeAppCallback;
33 using google_apis::CancelCallback;
34 using google_apis::ChangeList;
35 using google_apis::ChangeListCallback;
36 using google_apis::ChangeResource;
37 using google_apis::DownloadActionCallback;
38 using google_apis::EntryActionCallback;
39 using google_apis::FileList;
40 using google_apis::FileListCallback;
41 using google_apis::FileResource;
42 using google_apis::FileResourceCallback;
43 using google_apis::DRIVE_FILE_ERROR;
44 using google_apis::DRIVE_NO_CONNECTION;
45 using google_apis::DRIVE_OTHER_ERROR;
46 using google_apis::DriveApiErrorCode;
47 using google_apis::GetContentCallback;
48 using google_apis::GetShareUrlCallback;
49 using google_apis::HTTP_BAD_REQUEST;
50 using google_apis::HTTP_CREATED;
51 using google_apis::HTTP_FORBIDDEN;
52 using google_apis::HTTP_NOT_FOUND;
53 using google_apis::HTTP_NO_CONTENT;
54 using google_apis::HTTP_PRECONDITION;
55 using google_apis::HTTP_RESUME_INCOMPLETE;
56 using google_apis::HTTP_SUCCESS;
57 using google_apis::InitiateUploadCallback;
58 using google_apis::ParentReference;
59 using google_apis::ProgressCallback;
60 using google_apis::UploadRangeResponse;
61 using google_apis::drive::UploadRangeCallback;
62 namespace test_util = google_apis::test_util;
64 namespace drive {
65 namespace {
67 // Returns true if the entry matches with the search query.
68 // Supports queries consist of following format.
69 // - Phrases quoted by double/single quotes
70 // - AND search for multiple words/phrases segmented by space
71 // - Limited attribute search. Only "title:" is supported.
72 bool EntryMatchWithQuery(const ChangeResource& entry,
73 const std::string& query) {
74 base::StringTokenizer tokenizer(query, " ");
75 tokenizer.set_quote_chars("\"'");
76 while (tokenizer.GetNext()) {
77 std::string key, value;
78 const std::string& token = tokenizer.token();
79 if (token.find(':') == std::string::npos) {
80 base::TrimString(token, "\"'", &value);
81 } else {
82 base::StringTokenizer key_value(token, ":");
83 key_value.set_quote_chars("\"'");
84 if (!key_value.GetNext())
85 return false;
86 key = key_value.token();
87 if (!key_value.GetNext())
88 return false;
89 base::TrimString(key_value.token(), "\"'", &value);
92 // TODO(peria): Deal with other attributes than title.
93 if (!key.empty() && key != "title")
94 return false;
95 // Search query in the title.
96 if (!entry.file() ||
97 entry.file()->title().find(value) == std::string::npos)
98 return false;
100 return true;
103 void ScheduleUploadRangeCallback(const UploadRangeCallback& callback,
104 int64 start_position,
105 int64 end_position,
106 DriveApiErrorCode error,
107 scoped_ptr<FileResource> entry) {
108 base::MessageLoop::current()->PostTask(
109 FROM_HERE,
110 base::Bind(callback,
111 UploadRangeResponse(error,
112 start_position,
113 end_position),
114 base::Passed(&entry)));
117 void FileListCallbackAdapter(const FileListCallback& callback,
118 DriveApiErrorCode error,
119 scoped_ptr<ChangeList> change_list) {
120 scoped_ptr<FileList> file_list;
121 if (!change_list) {
122 callback.Run(error, file_list.Pass());
123 return;
126 file_list.reset(new FileList);
127 file_list->set_next_link(change_list->next_link());
128 for (size_t i = 0; i < change_list->items().size(); ++i) {
129 const ChangeResource& entry = *change_list->items()[i];
130 if (entry.file())
131 file_list->mutable_items()->push_back(new FileResource(*entry.file()));
133 callback.Run(error, file_list.Pass());
136 bool UserHasWriteAccess(google_apis::drive::PermissionRole user_permission) {
137 switch (user_permission) {
138 case google_apis::drive::PERMISSION_ROLE_OWNER:
139 case google_apis::drive::PERMISSION_ROLE_WRITER:
140 return true;
141 case google_apis::drive::PERMISSION_ROLE_READER:
142 case google_apis::drive::PERMISSION_ROLE_COMMENTER:
143 break;
145 return false;
148 void CallFileResouceCallback(const FileResourceCallback& callback,
149 const UploadRangeResponse& response,
150 scoped_ptr<FileResource> entry) {
151 callback.Run(response.code, entry.Pass());
154 struct CallResumeUpload {
155 CallResumeUpload() {}
156 ~CallResumeUpload() {}
158 void Run(DriveApiErrorCode code, const GURL& upload_url) {
159 if (service) {
160 service->ResumeUpload(
161 upload_url,
162 /* start position */ 0,
163 /* end position */ content_length,
164 content_length,
165 content_type,
166 local_file_path,
167 base::Bind(&CallFileResouceCallback, callback),
168 progress_callback);
172 base::WeakPtr<FakeDriveService> service;
173 int64 content_length;
174 std::string content_type;
175 base::FilePath local_file_path;
176 FileResourceCallback callback;
177 ProgressCallback progress_callback;
180 } // namespace
182 struct FakeDriveService::EntryInfo {
183 EntryInfo() : user_permission(google_apis::drive::PERMISSION_ROLE_OWNER) {}
185 google_apis::ChangeResource change_resource;
186 GURL share_url;
187 std::string content_data;
189 // Behaves in the same way as "userPermission" described in
190 // https://developers.google.com/drive/v2/reference/files
191 google_apis::drive::PermissionRole user_permission;
194 struct FakeDriveService::UploadSession {
195 std::string content_type;
196 int64 content_length;
197 std::string parent_resource_id;
198 std::string resource_id;
199 std::string etag;
200 std::string title;
202 int64 uploaded_size;
204 UploadSession()
205 : content_length(0),
206 uploaded_size(0) {}
208 UploadSession(
209 std::string content_type,
210 int64 content_length,
211 std::string parent_resource_id,
212 std::string resource_id,
213 std::string etag,
214 std::string title)
215 : content_type(content_type),
216 content_length(content_length),
217 parent_resource_id(parent_resource_id),
218 resource_id(resource_id),
219 etag(etag),
220 title(title),
221 uploaded_size(0) {
225 FakeDriveService::FakeDriveService()
226 : about_resource_(new AboutResource),
227 published_date_seq_(0),
228 next_upload_sequence_number_(0),
229 default_max_results_(0),
230 resource_id_count_(0),
231 file_list_load_count_(0),
232 change_list_load_count_(0),
233 directory_load_count_(0),
234 about_resource_load_count_(0),
235 app_list_load_count_(0),
236 blocked_file_list_load_count_(0),
237 offline_(false),
238 never_return_all_file_list_(false),
239 share_url_base_("https://share_url/"),
240 weak_ptr_factory_(this) {
241 about_resource_->set_largest_change_id(654321);
242 about_resource_->set_quota_bytes_total(9876543210);
243 about_resource_->set_quota_bytes_used(6789012345);
244 about_resource_->set_root_folder_id(GetRootResourceId());
247 FakeDriveService::~FakeDriveService() {
248 DCHECK(thread_checker_.CalledOnValidThread());
249 STLDeleteValues(&entries_);
252 bool FakeDriveService::LoadAppListForDriveApi(
253 const std::string& relative_path) {
254 DCHECK(thread_checker_.CalledOnValidThread());
256 // Load JSON data, which must be a dictionary.
257 scoped_ptr<base::Value> value = test_util::LoadJSONFile(relative_path);
258 CHECK_EQ(base::Value::TYPE_DICTIONARY, value->GetType());
259 app_info_value_.reset(
260 static_cast<base::DictionaryValue*>(value.release()));
261 return app_info_value_;
264 void FakeDriveService::AddApp(const std::string& app_id,
265 const std::string& app_name,
266 const std::string& product_id,
267 const std::string& create_url,
268 bool is_removable) {
269 if (app_json_template_.empty()) {
270 base::FilePath path =
271 test_util::GetTestFilePath("drive/applist_app_template.json");
272 CHECK(base::ReadFileToString(path, &app_json_template_));
275 std::string app_json = app_json_template_;
276 ReplaceSubstringsAfterOffset(&app_json, 0, "$AppId", app_id);
277 ReplaceSubstringsAfterOffset(&app_json, 0, "$AppName", app_name);
278 ReplaceSubstringsAfterOffset(&app_json, 0, "$ProductId", product_id);
279 ReplaceSubstringsAfterOffset(&app_json, 0, "$CreateUrl", create_url);
280 ReplaceSubstringsAfterOffset(
281 &app_json, 0, "$Removable", is_removable ? "true" : "false");
283 JSONStringValueSerializer json(app_json);
284 std::string error_message;
285 scoped_ptr<base::Value> value(json.Deserialize(NULL, &error_message));
286 CHECK_EQ(base::Value::TYPE_DICTIONARY, value->GetType());
288 base::ListValue* item_list;
289 CHECK(app_info_value_->GetListWithoutPathExpansion("items", &item_list));
290 item_list->Append(value.release());
293 void FakeDriveService::RemoveAppByProductId(const std::string& product_id) {
294 base::ListValue* item_list;
295 CHECK(app_info_value_->GetListWithoutPathExpansion("items", &item_list));
296 for (size_t i = 0; i < item_list->GetSize(); ++i) {
297 base::DictionaryValue* item;
298 CHECK(item_list->GetDictionary(i, &item));
299 const char kKeyProductId[] = "productId";
300 std::string item_product_id;
301 if (item->GetStringWithoutPathExpansion(kKeyProductId, &item_product_id) &&
302 product_id == item_product_id) {
303 item_list->Remove(i, NULL);
304 return;
309 bool FakeDriveService::HasApp(const std::string& app_id) const {
310 base::ListValue* item_list;
311 CHECK(app_info_value_->GetListWithoutPathExpansion("items", &item_list));
312 for (size_t i = 0; i < item_list->GetSize(); ++i) {
313 base::DictionaryValue* item;
314 CHECK(item_list->GetDictionary(i, &item));
315 const char kKeyId[] = "id";
316 std::string item_id;
317 if (item->GetStringWithoutPathExpansion(kKeyId, &item_id) &&
318 item_id == app_id) {
319 return true;
323 return false;
326 void FakeDriveService::SetQuotaValue(int64 used, int64 total) {
327 DCHECK(thread_checker_.CalledOnValidThread());
329 about_resource_->set_quota_bytes_used(used);
330 about_resource_->set_quota_bytes_total(total);
333 GURL FakeDriveService::GetFakeLinkUrl(const std::string& resource_id) {
334 return GURL("https://fake_server/" + net::EscapePath(resource_id));
337 void FakeDriveService::Initialize(const std::string& account_id) {
338 DCHECK(thread_checker_.CalledOnValidThread());
341 void FakeDriveService::AddObserver(DriveServiceObserver* observer) {
342 DCHECK(thread_checker_.CalledOnValidThread());
345 void FakeDriveService::RemoveObserver(DriveServiceObserver* observer) {
346 DCHECK(thread_checker_.CalledOnValidThread());
349 bool FakeDriveService::CanSendRequest() const {
350 DCHECK(thread_checker_.CalledOnValidThread());
351 return true;
354 bool FakeDriveService::HasAccessToken() const {
355 DCHECK(thread_checker_.CalledOnValidThread());
356 return true;
359 void FakeDriveService::RequestAccessToken(const AuthStatusCallback& callback) {
360 DCHECK(thread_checker_.CalledOnValidThread());
361 DCHECK(!callback.is_null());
362 callback.Run(google_apis::HTTP_NOT_MODIFIED, "fake_access_token");
365 bool FakeDriveService::HasRefreshToken() const {
366 DCHECK(thread_checker_.CalledOnValidThread());
367 return true;
370 void FakeDriveService::ClearAccessToken() {
371 DCHECK(thread_checker_.CalledOnValidThread());
374 void FakeDriveService::ClearRefreshToken() {
375 DCHECK(thread_checker_.CalledOnValidThread());
378 std::string FakeDriveService::GetRootResourceId() const {
379 return "fake_root";
382 CancelCallback FakeDriveService::GetAllFileList(
383 const FileListCallback& callback) {
384 DCHECK(thread_checker_.CalledOnValidThread());
385 DCHECK(!callback.is_null());
387 if (never_return_all_file_list_) {
388 ++blocked_file_list_load_count_;
389 return CancelCallback();
392 GetChangeListInternal(0, // start changestamp
393 std::string(), // empty search query
394 std::string(), // no directory resource id,
395 0, // start offset
396 default_max_results_,
397 &file_list_load_count_,
398 base::Bind(&FileListCallbackAdapter, callback));
399 return CancelCallback();
402 CancelCallback FakeDriveService::GetFileListInDirectory(
403 const std::string& directory_resource_id,
404 const FileListCallback& callback) {
405 DCHECK(thread_checker_.CalledOnValidThread());
406 DCHECK(!directory_resource_id.empty());
407 DCHECK(!callback.is_null());
409 GetChangeListInternal(0, // start changestamp
410 std::string(), // empty search query
411 directory_resource_id,
412 0, // start offset
413 default_max_results_,
414 &directory_load_count_,
415 base::Bind(&FileListCallbackAdapter, callback));
416 return CancelCallback();
419 CancelCallback FakeDriveService::Search(
420 const std::string& search_query,
421 const FileListCallback& callback) {
422 DCHECK(thread_checker_.CalledOnValidThread());
423 DCHECK(!search_query.empty());
424 DCHECK(!callback.is_null());
426 GetChangeListInternal(0, // start changestamp
427 search_query,
428 std::string(), // no directory resource id,
429 0, // start offset
430 default_max_results_,
431 NULL,
432 base::Bind(&FileListCallbackAdapter, callback));
433 return CancelCallback();
436 CancelCallback FakeDriveService::SearchByTitle(
437 const std::string& title,
438 const std::string& directory_resource_id,
439 const FileListCallback& callback) {
440 DCHECK(thread_checker_.CalledOnValidThread());
441 DCHECK(!title.empty());
442 DCHECK(!callback.is_null());
444 // Note: the search implementation here doesn't support quotation unescape,
445 // so don't escape here.
446 GetChangeListInternal(0, // start changestamp
447 base::StringPrintf("title:'%s'", title.c_str()),
448 directory_resource_id,
449 0, // start offset
450 default_max_results_,
451 NULL,
452 base::Bind(&FileListCallbackAdapter, callback));
453 return CancelCallback();
456 CancelCallback FakeDriveService::GetChangeList(
457 int64 start_changestamp,
458 const ChangeListCallback& callback) {
459 DCHECK(thread_checker_.CalledOnValidThread());
460 DCHECK(!callback.is_null());
462 GetChangeListInternal(start_changestamp,
463 std::string(), // empty search query
464 std::string(), // no directory resource id,
465 0, // start offset
466 default_max_results_,
467 &change_list_load_count_,
468 callback);
469 return CancelCallback();
472 CancelCallback FakeDriveService::GetRemainingChangeList(
473 const GURL& next_link,
474 const ChangeListCallback& callback) {
475 DCHECK(thread_checker_.CalledOnValidThread());
476 DCHECK(!next_link.is_empty());
477 DCHECK(!callback.is_null());
479 // "changestamp", "q", "parent" and "start-offset" are parameters to
480 // implement "paging" of the result on FakeDriveService.
481 // The URL should be the one filled in GetChangeListInternal of the
482 // previous method invocation, so it should start with "http://localhost/?".
483 // See also GetChangeListInternal.
484 DCHECK_EQ(next_link.host(), "localhost");
485 DCHECK_EQ(next_link.path(), "/");
487 int64 start_changestamp = 0;
488 std::string search_query;
489 std::string directory_resource_id;
490 int start_offset = 0;
491 int max_results = default_max_results_;
492 base::StringPairs parameters;
493 if (base::SplitStringIntoKeyValuePairs(
494 next_link.query(), '=', '&', &parameters)) {
495 for (size_t i = 0; i < parameters.size(); ++i) {
496 if (parameters[i].first == "changestamp") {
497 base::StringToInt64(parameters[i].second, &start_changestamp);
498 } else if (parameters[i].first == "q") {
499 search_query =
500 net::UnescapeURLComponent(parameters[i].second,
501 net::UnescapeRule::URL_SPECIAL_CHARS);
502 } else if (parameters[i].first == "parent") {
503 directory_resource_id =
504 net::UnescapeURLComponent(parameters[i].second,
505 net::UnescapeRule::URL_SPECIAL_CHARS);
506 } else if (parameters[i].first == "start-offset") {
507 base::StringToInt(parameters[i].second, &start_offset);
508 } else if (parameters[i].first == "max-results") {
509 base::StringToInt(parameters[i].second, &max_results);
514 GetChangeListInternal(start_changestamp, search_query, directory_resource_id,
515 start_offset, max_results, NULL, callback);
516 return CancelCallback();
519 CancelCallback FakeDriveService::GetRemainingFileList(
520 const GURL& next_link,
521 const FileListCallback& callback) {
522 DCHECK(thread_checker_.CalledOnValidThread());
523 DCHECK(!next_link.is_empty());
524 DCHECK(!callback.is_null());
526 return GetRemainingChangeList(
527 next_link, base::Bind(&FileListCallbackAdapter, callback));
530 CancelCallback FakeDriveService::GetFileResource(
531 const std::string& resource_id,
532 const FileResourceCallback& callback) {
533 DCHECK(thread_checker_.CalledOnValidThread());
534 DCHECK(!callback.is_null());
536 if (offline_) {
537 base::MessageLoop::current()->PostTask(
538 FROM_HERE,
539 base::Bind(callback,
540 DRIVE_NO_CONNECTION,
541 base::Passed(scoped_ptr<FileResource>())));
542 return CancelCallback();
545 EntryInfo* entry = FindEntryByResourceId(resource_id);
546 if (entry && entry->change_resource.file()) {
547 base::MessageLoop::current()->PostTask(
548 FROM_HERE,
549 base::Bind(callback, HTTP_SUCCESS, base::Passed(make_scoped_ptr(
550 new FileResource(*entry->change_resource.file())))));
551 return CancelCallback();
554 base::MessageLoop::current()->PostTask(
555 FROM_HERE,
556 base::Bind(callback, HTTP_NOT_FOUND,
557 base::Passed(scoped_ptr<FileResource>())));
558 return CancelCallback();
561 CancelCallback FakeDriveService::GetShareUrl(
562 const std::string& resource_id,
563 const GURL& /* embed_origin */,
564 const GetShareUrlCallback& callback) {
565 DCHECK(thread_checker_.CalledOnValidThread());
566 DCHECK(!callback.is_null());
568 if (offline_) {
569 base::MessageLoop::current()->PostTask(
570 FROM_HERE,
571 base::Bind(callback,
572 DRIVE_NO_CONNECTION,
573 GURL()));
574 return CancelCallback();
577 EntryInfo* entry = FindEntryByResourceId(resource_id);
578 if (entry) {
579 base::MessageLoop::current()->PostTask(
580 FROM_HERE,
581 base::Bind(callback, HTTP_SUCCESS, entry->share_url));
582 return CancelCallback();
585 base::MessageLoop::current()->PostTask(
586 FROM_HERE,
587 base::Bind(callback, HTTP_NOT_FOUND, GURL()));
588 return CancelCallback();
591 CancelCallback FakeDriveService::GetAboutResource(
592 const AboutResourceCallback& callback) {
593 DCHECK(thread_checker_.CalledOnValidThread());
594 DCHECK(!callback.is_null());
596 if (offline_) {
597 scoped_ptr<AboutResource> null;
598 base::MessageLoop::current()->PostTask(
599 FROM_HERE,
600 base::Bind(callback,
601 DRIVE_NO_CONNECTION, base::Passed(&null)));
602 return CancelCallback();
605 ++about_resource_load_count_;
606 scoped_ptr<AboutResource> about_resource(new AboutResource(*about_resource_));
607 base::MessageLoop::current()->PostTask(
608 FROM_HERE,
609 base::Bind(callback,
610 HTTP_SUCCESS, base::Passed(&about_resource)));
611 return CancelCallback();
614 CancelCallback FakeDriveService::GetAppList(const AppListCallback& callback) {
615 DCHECK(thread_checker_.CalledOnValidThread());
616 DCHECK(!callback.is_null());
617 DCHECK(app_info_value_);
619 if (offline_) {
620 scoped_ptr<AppList> null;
621 base::MessageLoop::current()->PostTask(
622 FROM_HERE,
623 base::Bind(callback,
624 DRIVE_NO_CONNECTION,
625 base::Passed(&null)));
626 return CancelCallback();
629 ++app_list_load_count_;
630 scoped_ptr<AppList> app_list(AppList::CreateFrom(*app_info_value_));
631 base::MessageLoop::current()->PostTask(
632 FROM_HERE,
633 base::Bind(callback, HTTP_SUCCESS, base::Passed(&app_list)));
634 return CancelCallback();
637 CancelCallback FakeDriveService::DeleteResource(
638 const std::string& resource_id,
639 const std::string& etag,
640 const EntryActionCallback& callback) {
641 DCHECK(thread_checker_.CalledOnValidThread());
642 DCHECK(!callback.is_null());
644 if (offline_) {
645 base::MessageLoop::current()->PostTask(
646 FROM_HERE, base::Bind(callback, DRIVE_NO_CONNECTION));
647 return CancelCallback();
650 EntryInfo* entry = FindEntryByResourceId(resource_id);
651 if (!entry) {
652 base::MessageLoop::current()->PostTask(
653 FROM_HERE, base::Bind(callback, HTTP_NOT_FOUND));
654 return CancelCallback();
657 ChangeResource* change = &entry->change_resource;
658 const FileResource* file = change->file();
659 if (change->is_deleted()) {
660 base::MessageLoop::current()->PostTask(
661 FROM_HERE, base::Bind(callback, HTTP_NOT_FOUND));
662 return CancelCallback();
665 if (!etag.empty() && etag != file->etag()) {
666 base::MessageLoop::current()->PostTask(
667 FROM_HERE, base::Bind(callback, HTTP_PRECONDITION));
668 return CancelCallback();
671 if (entry->user_permission != google_apis::drive::PERMISSION_ROLE_OWNER) {
672 base::MessageLoop::current()->PostTask(
673 FROM_HERE, base::Bind(callback, HTTP_FORBIDDEN));
674 return CancelCallback();
677 change->set_deleted(true);
678 AddNewChangestamp(change);
679 change->set_file(scoped_ptr<FileResource>());
680 base::MessageLoop::current()->PostTask(
681 FROM_HERE, base::Bind(callback, HTTP_NO_CONTENT));
682 base::MessageLoop::current()->PostTask(
683 FROM_HERE,
684 base::Bind(&FakeDriveService::NotifyObservers,
685 weak_ptr_factory_.GetWeakPtr()));
686 return CancelCallback();
689 CancelCallback FakeDriveService::TrashResource(
690 const std::string& resource_id,
691 const EntryActionCallback& callback) {
692 DCHECK(thread_checker_.CalledOnValidThread());
693 DCHECK(!callback.is_null());
695 if (offline_) {
696 base::MessageLoop::current()->PostTask(
697 FROM_HERE, base::Bind(callback, DRIVE_NO_CONNECTION));
698 return CancelCallback();
701 EntryInfo* entry = FindEntryByResourceId(resource_id);
702 if (!entry) {
703 base::MessageLoop::current()->PostTask(
704 FROM_HERE, base::Bind(callback, HTTP_NOT_FOUND));
705 return CancelCallback();
708 ChangeResource* change = &entry->change_resource;
709 FileResource* file = change->mutable_file();
710 if (change->is_deleted() || file->labels().is_trashed()) {
711 base::MessageLoop::current()->PostTask(
712 FROM_HERE, base::Bind(callback, HTTP_NOT_FOUND));
713 return CancelCallback();
716 if (entry->user_permission != google_apis::drive::PERMISSION_ROLE_OWNER) {
717 base::MessageLoop::current()->PostTask(
718 FROM_HERE, base::Bind(callback, HTTP_FORBIDDEN));
719 return CancelCallback();
722 file->mutable_labels()->set_trashed(true);
723 AddNewChangestamp(change);
724 base::MessageLoop::current()->PostTask(
725 FROM_HERE, base::Bind(callback, HTTP_SUCCESS));
726 base::MessageLoop::current()->PostTask(
727 FROM_HERE,
728 base::Bind(&FakeDriveService::NotifyObservers,
729 weak_ptr_factory_.GetWeakPtr()));
730 return CancelCallback();
733 CancelCallback FakeDriveService::DownloadFile(
734 const base::FilePath& local_cache_path,
735 const std::string& resource_id,
736 const DownloadActionCallback& download_action_callback,
737 const GetContentCallback& get_content_callback,
738 const ProgressCallback& progress_callback) {
739 DCHECK(thread_checker_.CalledOnValidThread());
740 DCHECK(!download_action_callback.is_null());
742 if (offline_) {
743 base::MessageLoop::current()->PostTask(
744 FROM_HERE,
745 base::Bind(download_action_callback,
746 DRIVE_NO_CONNECTION,
747 base::FilePath()));
748 return CancelCallback();
751 EntryInfo* entry = FindEntryByResourceId(resource_id);
752 if (!entry || entry->change_resource.file()->IsHostedDocument()) {
753 base::MessageLoopProxy::current()->PostTask(
754 FROM_HERE,
755 base::Bind(download_action_callback, HTTP_NOT_FOUND, base::FilePath()));
756 return CancelCallback();
759 const FileResource* file = entry->change_resource.file();
760 const std::string& content_data = entry->content_data;
761 int64 file_size = file->file_size();
762 DCHECK_EQ(static_cast<size_t>(file_size), content_data.size());
764 if (!get_content_callback.is_null()) {
765 const int64 kBlockSize = 5;
766 for (int64 i = 0; i < file_size; i += kBlockSize) {
767 const int64 size = std::min(kBlockSize, file_size - i);
768 scoped_ptr<std::string> content_for_callback(
769 new std::string(content_data.substr(i, size)));
770 base::MessageLoopProxy::current()->PostTask(
771 FROM_HERE,
772 base::Bind(get_content_callback, HTTP_SUCCESS,
773 base::Passed(&content_for_callback)));
777 if (!test_util::WriteStringToFile(local_cache_path, content_data)) {
778 // Failed to write the content.
779 base::MessageLoopProxy::current()->PostTask(
780 FROM_HERE,
781 base::Bind(download_action_callback,
782 DRIVE_FILE_ERROR, base::FilePath()));
783 return CancelCallback();
786 if (!progress_callback.is_null()) {
787 // See also the comment in ResumeUpload(). For testing that clients
788 // can handle the case progress_callback is called multiple times,
789 // here we invoke the callback twice.
790 base::MessageLoopProxy::current()->PostTask(
791 FROM_HERE,
792 base::Bind(progress_callback, file_size / 2, file_size));
793 base::MessageLoopProxy::current()->PostTask(
794 FROM_HERE,
795 base::Bind(progress_callback, file_size, file_size));
797 base::MessageLoopProxy::current()->PostTask(
798 FROM_HERE,
799 base::Bind(download_action_callback,
800 HTTP_SUCCESS,
801 local_cache_path));
802 return CancelCallback();
805 CancelCallback FakeDriveService::CopyResource(
806 const std::string& resource_id,
807 const std::string& in_parent_resource_id,
808 const std::string& new_title,
809 const base::Time& last_modified,
810 const FileResourceCallback& callback) {
811 DCHECK(thread_checker_.CalledOnValidThread());
812 DCHECK(!callback.is_null());
814 if (offline_) {
815 base::MessageLoop::current()->PostTask(
816 FROM_HERE,
817 base::Bind(callback,
818 DRIVE_NO_CONNECTION,
819 base::Passed(scoped_ptr<FileResource>())));
820 return CancelCallback();
823 const std::string& parent_resource_id = in_parent_resource_id.empty() ?
824 GetRootResourceId() : in_parent_resource_id;
826 EntryInfo* entry = FindEntryByResourceId(resource_id);
827 if (!entry) {
828 base::MessageLoop::current()->PostTask(
829 FROM_HERE,
830 base::Bind(callback, HTTP_NOT_FOUND,
831 base::Passed(scoped_ptr<FileResource>())));
832 return CancelCallback();
835 // Make a copy and set the new resource ID and the new title.
836 scoped_ptr<EntryInfo> copied_entry(new EntryInfo);
837 copied_entry->content_data = entry->content_data;
838 copied_entry->share_url = entry->share_url;
839 copied_entry->change_resource.set_file(
840 make_scoped_ptr(new FileResource(*entry->change_resource.file())));
842 ChangeResource* new_change = &copied_entry->change_resource;
843 FileResource* new_file = new_change->mutable_file();
844 const std::string new_resource_id = GetNewResourceId();
845 new_change->set_file_id(new_resource_id);
846 new_file->set_file_id(new_resource_id);
847 new_file->set_title(new_title);
849 ParentReference parent;
850 parent.set_file_id(parent_resource_id);
851 parent.set_parent_link(GetFakeLinkUrl(parent_resource_id));
852 std::vector<ParentReference> parents;
853 parents.push_back(parent);
854 *new_file->mutable_parents() = parents;
856 if (!last_modified.is_null())
857 new_file->set_modified_date(last_modified);
859 AddNewChangestamp(new_change);
860 UpdateETag(new_file);
862 // Add the new entry to the map.
863 entries_[new_resource_id] = copied_entry.release();
865 base::MessageLoop::current()->PostTask(
866 FROM_HERE,
867 base::Bind(callback,
868 HTTP_SUCCESS,
869 base::Passed(make_scoped_ptr(new FileResource(*new_file)))));
870 base::MessageLoop::current()->PostTask(
871 FROM_HERE,
872 base::Bind(&FakeDriveService::NotifyObservers,
873 weak_ptr_factory_.GetWeakPtr()));
874 return CancelCallback();
877 CancelCallback FakeDriveService::UpdateResource(
878 const std::string& resource_id,
879 const std::string& parent_resource_id,
880 const std::string& new_title,
881 const base::Time& last_modified,
882 const base::Time& last_viewed_by_me,
883 const google_apis::FileResourceCallback& callback) {
884 DCHECK(thread_checker_.CalledOnValidThread());
885 DCHECK(!callback.is_null());
887 if (offline_) {
888 base::MessageLoop::current()->PostTask(
889 FROM_HERE, base::Bind(callback, DRIVE_NO_CONNECTION,
890 base::Passed(scoped_ptr<FileResource>())));
891 return CancelCallback();
894 EntryInfo* entry = FindEntryByResourceId(resource_id);
895 if (!entry) {
896 base::MessageLoop::current()->PostTask(
897 FROM_HERE,
898 base::Bind(callback, HTTP_NOT_FOUND,
899 base::Passed(scoped_ptr<FileResource>())));
900 return CancelCallback();
903 if (!UserHasWriteAccess(entry->user_permission)) {
904 base::MessageLoop::current()->PostTask(
905 FROM_HERE,
906 base::Bind(callback, HTTP_FORBIDDEN,
907 base::Passed(scoped_ptr<FileResource>())));
908 return CancelCallback();
911 ChangeResource* change = &entry->change_resource;
912 FileResource* file = change->mutable_file();
914 if (!new_title.empty())
915 file->set_title(new_title);
917 // Set parent if necessary.
918 if (!parent_resource_id.empty()) {
919 ParentReference parent;
920 parent.set_file_id(parent_resource_id);
921 parent.set_parent_link(GetFakeLinkUrl(parent_resource_id));
923 std::vector<ParentReference> parents;
924 parents.push_back(parent);
925 *file->mutable_parents() = parents;
928 if (!last_modified.is_null())
929 file->set_modified_date(last_modified);
931 if (!last_viewed_by_me.is_null())
932 file->set_last_viewed_by_me_date(last_viewed_by_me);
934 AddNewChangestamp(change);
935 UpdateETag(file);
937 base::MessageLoop::current()->PostTask(
938 FROM_HERE,
939 base::Bind(callback, HTTP_SUCCESS,
940 base::Passed(make_scoped_ptr(new FileResource(*file)))));
941 base::MessageLoop::current()->PostTask(
942 FROM_HERE,
943 base::Bind(&FakeDriveService::NotifyObservers,
944 weak_ptr_factory_.GetWeakPtr()));
945 return CancelCallback();
948 CancelCallback FakeDriveService::AddResourceToDirectory(
949 const std::string& parent_resource_id,
950 const std::string& resource_id,
951 const EntryActionCallback& callback) {
952 DCHECK(thread_checker_.CalledOnValidThread());
953 DCHECK(!callback.is_null());
955 if (offline_) {
956 base::MessageLoop::current()->PostTask(
957 FROM_HERE, base::Bind(callback, DRIVE_NO_CONNECTION));
958 return CancelCallback();
961 EntryInfo* entry = FindEntryByResourceId(resource_id);
962 if (!entry) {
963 base::MessageLoop::current()->PostTask(
964 FROM_HERE, base::Bind(callback, HTTP_NOT_FOUND));
965 return CancelCallback();
968 ChangeResource* change = &entry->change_resource;
969 // On the real Drive server, resources do not necessary shape a tree
970 // structure. That is, each resource can have multiple parent.
971 // We mimic the behavior here; AddResourceToDirectoy just adds
972 // one more parent, not overwriting old ones.
973 ParentReference parent;
974 parent.set_file_id(parent_resource_id);
975 parent.set_parent_link(GetFakeLinkUrl(parent_resource_id));
976 change->mutable_file()->mutable_parents()->push_back(parent);
978 AddNewChangestamp(change);
979 base::MessageLoop::current()->PostTask(
980 FROM_HERE, base::Bind(callback, HTTP_SUCCESS));
981 base::MessageLoop::current()->PostTask(
982 FROM_HERE,
983 base::Bind(&FakeDriveService::NotifyObservers,
984 weak_ptr_factory_.GetWeakPtr()));
985 return CancelCallback();
988 CancelCallback FakeDriveService::RemoveResourceFromDirectory(
989 const std::string& parent_resource_id,
990 const std::string& resource_id,
991 const EntryActionCallback& callback) {
992 DCHECK(thread_checker_.CalledOnValidThread());
993 DCHECK(!callback.is_null());
995 if (offline_) {
996 base::MessageLoop::current()->PostTask(
997 FROM_HERE, base::Bind(callback, DRIVE_NO_CONNECTION));
998 return CancelCallback();
1001 EntryInfo* entry = FindEntryByResourceId(resource_id);
1002 if (!entry) {
1003 base::MessageLoop::current()->PostTask(
1004 FROM_HERE, base::Bind(callback, HTTP_NOT_FOUND));
1005 return CancelCallback();
1008 ChangeResource* change = &entry->change_resource;
1009 FileResource* file = change->mutable_file();
1010 std::vector<ParentReference>* parents = file->mutable_parents();
1011 for (size_t i = 0; i < parents->size(); ++i) {
1012 if ((*parents)[i].file_id() == parent_resource_id) {
1013 parents->erase(parents->begin() + i);
1014 AddNewChangestamp(change);
1015 base::MessageLoop::current()->PostTask(
1016 FROM_HERE, base::Bind(callback, HTTP_NO_CONTENT));
1017 base::MessageLoop::current()->PostTask(
1018 FROM_HERE,
1019 base::Bind(&FakeDriveService::NotifyObservers,
1020 weak_ptr_factory_.GetWeakPtr()));
1021 return CancelCallback();
1025 base::MessageLoop::current()->PostTask(
1026 FROM_HERE, base::Bind(callback, HTTP_NOT_FOUND));
1027 return CancelCallback();
1030 CancelCallback FakeDriveService::AddNewDirectory(
1031 const std::string& parent_resource_id,
1032 const std::string& directory_title,
1033 const AddNewDirectoryOptions& options,
1034 const FileResourceCallback& callback) {
1035 return AddNewDirectoryWithResourceId(
1037 parent_resource_id.empty() ? GetRootResourceId() : parent_resource_id,
1038 directory_title,
1039 options,
1040 callback);
1043 CancelCallback FakeDriveService::InitiateUploadNewFile(
1044 const std::string& content_type,
1045 int64 content_length,
1046 const std::string& parent_resource_id,
1047 const std::string& title,
1048 const UploadNewFileOptions& options,
1049 const InitiateUploadCallback& callback) {
1050 DCHECK(thread_checker_.CalledOnValidThread());
1051 DCHECK(!callback.is_null());
1053 if (offline_) {
1054 base::MessageLoop::current()->PostTask(
1055 FROM_HERE,
1056 base::Bind(callback, DRIVE_NO_CONNECTION, GURL()));
1057 return CancelCallback();
1060 if (parent_resource_id != GetRootResourceId() &&
1061 !entries_.count(parent_resource_id)) {
1062 base::MessageLoop::current()->PostTask(
1063 FROM_HERE,
1064 base::Bind(callback, HTTP_NOT_FOUND, GURL()));
1065 return CancelCallback();
1068 GURL session_url = GetNewUploadSessionUrl();
1069 upload_sessions_[session_url] =
1070 UploadSession(content_type, content_length,
1071 parent_resource_id,
1072 "", // resource_id
1073 "", // etag
1074 title);
1076 base::MessageLoop::current()->PostTask(
1077 FROM_HERE,
1078 base::Bind(callback, HTTP_SUCCESS, session_url));
1079 return CancelCallback();
1082 CancelCallback FakeDriveService::InitiateUploadExistingFile(
1083 const std::string& content_type,
1084 int64 content_length,
1085 const std::string& resource_id,
1086 const UploadExistingFileOptions& options,
1087 const InitiateUploadCallback& callback) {
1088 DCHECK(thread_checker_.CalledOnValidThread());
1089 DCHECK(!callback.is_null());
1091 if (offline_) {
1092 base::MessageLoop::current()->PostTask(
1093 FROM_HERE,
1094 base::Bind(callback, DRIVE_NO_CONNECTION, GURL()));
1095 return CancelCallback();
1098 EntryInfo* entry = FindEntryByResourceId(resource_id);
1099 if (!entry) {
1100 base::MessageLoop::current()->PostTask(
1101 FROM_HERE,
1102 base::Bind(callback, HTTP_NOT_FOUND, GURL()));
1103 return CancelCallback();
1106 if (!UserHasWriteAccess(entry->user_permission)) {
1107 base::MessageLoop::current()->PostTask(
1108 FROM_HERE,
1109 base::Bind(callback, HTTP_FORBIDDEN, GURL()));
1110 return CancelCallback();
1113 FileResource* file = entry->change_resource.mutable_file();
1114 if (!options.etag.empty() && options.etag != file->etag()) {
1115 base::MessageLoop::current()->PostTask(
1116 FROM_HERE,
1117 base::Bind(callback, HTTP_PRECONDITION, GURL()));
1118 return CancelCallback();
1120 // TODO(hashimoto): Update |file|'s metadata with |options|.
1122 GURL session_url = GetNewUploadSessionUrl();
1123 upload_sessions_[session_url] =
1124 UploadSession(content_type, content_length,
1125 "", // parent_resource_id
1126 resource_id,
1127 file->etag(),
1128 "" /* title */);
1130 base::MessageLoop::current()->PostTask(
1131 FROM_HERE,
1132 base::Bind(callback, HTTP_SUCCESS, session_url));
1133 return CancelCallback();
1136 CancelCallback FakeDriveService::GetUploadStatus(
1137 const GURL& upload_url,
1138 int64 content_length,
1139 const UploadRangeCallback& callback) {
1140 DCHECK(thread_checker_.CalledOnValidThread());
1141 DCHECK(!callback.is_null());
1142 return CancelCallback();
1145 CancelCallback FakeDriveService::ResumeUpload(
1146 const GURL& upload_url,
1147 int64 start_position,
1148 int64 end_position,
1149 int64 content_length,
1150 const std::string& content_type,
1151 const base::FilePath& local_file_path,
1152 const UploadRangeCallback& callback,
1153 const ProgressCallback& progress_callback) {
1154 DCHECK(thread_checker_.CalledOnValidThread());
1155 DCHECK(!callback.is_null());
1157 FileResourceCallback completion_callback
1158 = base::Bind(&ScheduleUploadRangeCallback,
1159 callback, start_position, end_position);
1161 if (offline_) {
1162 completion_callback.Run(DRIVE_NO_CONNECTION, scoped_ptr<FileResource>());
1163 return CancelCallback();
1166 if (!upload_sessions_.count(upload_url)) {
1167 completion_callback.Run(HTTP_NOT_FOUND, scoped_ptr<FileResource>());
1168 return CancelCallback();
1171 UploadSession* session = &upload_sessions_[upload_url];
1173 // Chunks are required to be sent in such a ways that they fill from the start
1174 // of the not-yet-uploaded part with no gaps nor overlaps.
1175 if (session->uploaded_size != start_position) {
1176 completion_callback.Run(HTTP_BAD_REQUEST, scoped_ptr<FileResource>());
1177 return CancelCallback();
1180 if (!progress_callback.is_null()) {
1181 // In the real GDataWapi/Drive DriveService, progress is reported in
1182 // nondeterministic timing. In this fake implementation, we choose to call
1183 // it twice per one ResumeUpload. This is for making sure that client code
1184 // works fine even if the callback is invoked more than once; it is the
1185 // crucial difference of the progress callback from others.
1186 // Note that progress is notified in the relative offset in each chunk.
1187 const int64 chunk_size = end_position - start_position;
1188 base::MessageLoop::current()->PostTask(
1189 FROM_HERE, base::Bind(progress_callback, chunk_size / 2, chunk_size));
1190 base::MessageLoop::current()->PostTask(
1191 FROM_HERE, base::Bind(progress_callback, chunk_size, chunk_size));
1194 if (content_length != end_position) {
1195 session->uploaded_size = end_position;
1196 completion_callback.Run(HTTP_RESUME_INCOMPLETE, scoped_ptr<FileResource>());
1197 return CancelCallback();
1200 std::string content_data;
1201 if (!base::ReadFileToString(local_file_path, &content_data)) {
1202 session->uploaded_size = end_position;
1203 completion_callback.Run(DRIVE_FILE_ERROR, scoped_ptr<FileResource>());
1204 return CancelCallback();
1206 session->uploaded_size = end_position;
1208 // |resource_id| is empty if the upload is for new file.
1209 if (session->resource_id.empty()) {
1210 DCHECK(!session->parent_resource_id.empty());
1211 DCHECK(!session->title.empty());
1212 const EntryInfo* new_entry = AddNewEntry(
1213 "", // auto generate resource id.
1214 session->content_type,
1215 content_data,
1216 session->parent_resource_id,
1217 session->title,
1218 false); // shared_with_me
1219 if (!new_entry) {
1220 completion_callback.Run(HTTP_NOT_FOUND, scoped_ptr<FileResource>());
1221 return CancelCallback();
1224 completion_callback.Run(HTTP_CREATED, make_scoped_ptr(
1225 new FileResource(*new_entry->change_resource.file())));
1226 base::MessageLoop::current()->PostTask(
1227 FROM_HERE,
1228 base::Bind(&FakeDriveService::NotifyObservers,
1229 weak_ptr_factory_.GetWeakPtr()));
1230 return CancelCallback();
1233 EntryInfo* entry = FindEntryByResourceId(session->resource_id);
1234 if (!entry) {
1235 completion_callback.Run(HTTP_NOT_FOUND, scoped_ptr<FileResource>());
1236 return CancelCallback();
1239 ChangeResource* change = &entry->change_resource;
1240 FileResource* file = change->mutable_file();
1241 if (file->etag().empty() || session->etag != file->etag()) {
1242 completion_callback.Run(HTTP_PRECONDITION, scoped_ptr<FileResource>());
1243 return CancelCallback();
1246 file->set_md5_checksum(base::MD5String(content_data));
1247 entry->content_data = content_data;
1248 file->set_file_size(end_position);
1249 AddNewChangestamp(change);
1250 UpdateETag(file);
1252 completion_callback.Run(HTTP_SUCCESS, make_scoped_ptr(
1253 new FileResource(*file)));
1254 base::MessageLoop::current()->PostTask(
1255 FROM_HERE,
1256 base::Bind(&FakeDriveService::NotifyObservers,
1257 weak_ptr_factory_.GetWeakPtr()));
1258 return CancelCallback();
1261 CancelCallback FakeDriveService::MultipartUploadNewFile(
1262 const std::string& content_type,
1263 int64 content_length,
1264 const std::string& parent_resource_id,
1265 const std::string& title,
1266 const base::FilePath& local_file_path,
1267 const UploadNewFileOptions& options,
1268 const FileResourceCallback& callback,
1269 const ProgressCallback& progress_callback) {
1270 CallResumeUpload* const call_resume_upload = new CallResumeUpload();
1271 call_resume_upload->service = weak_ptr_factory_.GetWeakPtr();
1272 call_resume_upload->content_type = content_type;
1273 call_resume_upload->content_length = content_length;
1274 call_resume_upload->local_file_path = local_file_path;
1275 call_resume_upload->callback = callback;
1276 call_resume_upload->progress_callback = progress_callback;
1277 InitiateUploadNewFile(
1278 content_type,
1279 content_length,
1280 parent_resource_id,
1281 title,
1282 options,
1283 base::Bind(&CallResumeUpload::Run, base::Owned(call_resume_upload)));
1284 return CancelCallback();
1287 CancelCallback FakeDriveService::MultipartUploadExistingFile(
1288 const std::string& content_type,
1289 int64 content_length,
1290 const std::string& resource_id,
1291 const base::FilePath& local_file_path,
1292 const UploadExistingFileOptions& options,
1293 const FileResourceCallback& callback,
1294 const ProgressCallback& progress_callback) {
1295 CallResumeUpload* const call_resume_upload = new CallResumeUpload();
1296 call_resume_upload->service = weak_ptr_factory_.GetWeakPtr();
1297 call_resume_upload->content_type = content_type;
1298 call_resume_upload->content_length = content_length;
1299 call_resume_upload->local_file_path = local_file_path;
1300 call_resume_upload->callback = callback;
1301 call_resume_upload->progress_callback = progress_callback;
1302 InitiateUploadExistingFile(
1303 content_type,
1304 content_length,
1305 resource_id,
1306 options,
1307 base::Bind(&CallResumeUpload::Run, base::Owned(call_resume_upload)));
1308 return CancelCallback();
1311 CancelCallback FakeDriveService::AuthorizeApp(
1312 const std::string& resource_id,
1313 const std::string& app_id,
1314 const AuthorizeAppCallback& callback) {
1315 DCHECK(thread_checker_.CalledOnValidThread());
1316 DCHECK(!callback.is_null());
1318 if (entries_.count(resource_id) == 0) {
1319 callback.Run(google_apis::HTTP_NOT_FOUND, GURL());
1320 return CancelCallback();
1323 callback.Run(HTTP_SUCCESS,
1324 GURL(base::StringPrintf(open_url_format_.c_str(),
1325 resource_id.c_str(),
1326 app_id.c_str())));
1327 return CancelCallback();
1330 CancelCallback FakeDriveService::UninstallApp(
1331 const std::string& app_id,
1332 const google_apis::EntryActionCallback& callback) {
1333 DCHECK(thread_checker_.CalledOnValidThread());
1334 DCHECK(!callback.is_null());
1336 if (offline_) {
1337 base::MessageLoop::current()->PostTask(
1338 FROM_HERE,
1339 base::Bind(callback, google_apis::DRIVE_NO_CONNECTION));
1340 return CancelCallback();
1343 // Find app_id from app_info_value_ and delete.
1344 base::ListValue* items = NULL;
1345 if (!app_info_value_->GetList("items", &items)) {
1346 base::MessageLoop::current()->PostTask(
1347 FROM_HERE,
1348 base::Bind(callback, google_apis::HTTP_NOT_FOUND));
1349 return CancelCallback();
1352 for (size_t i = 0; i < items->GetSize(); ++i) {
1353 base::DictionaryValue* item = NULL;
1354 std::string id;
1355 if (items->GetDictionary(i, &item) && item->GetString("id", &id) &&
1356 id == app_id) {
1357 base::MessageLoop::current()->PostTask(
1358 FROM_HERE,
1359 base::Bind(callback,
1360 items->Remove(i, NULL) ? google_apis::HTTP_NO_CONTENT
1361 : google_apis::HTTP_NOT_FOUND));
1362 return CancelCallback();
1366 base::MessageLoop::current()->PostTask(
1367 FROM_HERE,
1368 base::Bind(callback, google_apis::HTTP_NOT_FOUND));
1369 return CancelCallback();
1372 void FakeDriveService::AddNewFile(const std::string& content_type,
1373 const std::string& content_data,
1374 const std::string& parent_resource_id,
1375 const std::string& title,
1376 bool shared_with_me,
1377 const FileResourceCallback& callback) {
1378 AddNewFileWithResourceId("", content_type, content_data, parent_resource_id,
1379 title, shared_with_me, callback);
1382 void FakeDriveService::AddNewFileWithResourceId(
1383 const std::string& resource_id,
1384 const std::string& content_type,
1385 const std::string& content_data,
1386 const std::string& parent_resource_id,
1387 const std::string& title,
1388 bool shared_with_me,
1389 const FileResourceCallback& callback) {
1390 DCHECK(thread_checker_.CalledOnValidThread());
1391 DCHECK(!callback.is_null());
1393 if (offline_) {
1394 base::MessageLoop::current()->PostTask(
1395 FROM_HERE,
1396 base::Bind(callback,
1397 DRIVE_NO_CONNECTION,
1398 base::Passed(scoped_ptr<FileResource>())));
1399 return;
1402 const EntryInfo* new_entry = AddNewEntry(resource_id,
1403 content_type,
1404 content_data,
1405 parent_resource_id,
1406 title,
1407 shared_with_me);
1408 if (!new_entry) {
1409 base::MessageLoop::current()->PostTask(
1410 FROM_HERE,
1411 base::Bind(callback, HTTP_NOT_FOUND,
1412 base::Passed(scoped_ptr<FileResource>())));
1413 return;
1416 base::MessageLoop::current()->PostTask(
1417 FROM_HERE,
1418 base::Bind(callback, HTTP_CREATED,
1419 base::Passed(make_scoped_ptr(
1420 new FileResource(*new_entry->change_resource.file())))));
1421 base::MessageLoop::current()->PostTask(
1422 FROM_HERE,
1423 base::Bind(&FakeDriveService::NotifyObservers,
1424 weak_ptr_factory_.GetWeakPtr()));
1427 CancelCallback FakeDriveService::AddNewDirectoryWithResourceId(
1428 const std::string& resource_id,
1429 const std::string& parent_resource_id,
1430 const std::string& directory_title,
1431 const AddNewDirectoryOptions& options,
1432 const FileResourceCallback& callback) {
1433 DCHECK(thread_checker_.CalledOnValidThread());
1434 DCHECK(!callback.is_null());
1436 if (offline_) {
1437 base::MessageLoop::current()->PostTask(
1438 FROM_HERE,
1439 base::Bind(callback,
1440 DRIVE_NO_CONNECTION,
1441 base::Passed(scoped_ptr<FileResource>())));
1442 return CancelCallback();
1445 const EntryInfo* new_entry = AddNewEntry(resource_id,
1446 util::kDriveFolderMimeType,
1447 "", // content_data
1448 parent_resource_id,
1449 directory_title,
1450 false); // shared_with_me
1451 if (!new_entry) {
1452 base::MessageLoop::current()->PostTask(
1453 FROM_HERE,
1454 base::Bind(callback, HTTP_NOT_FOUND,
1455 base::Passed(scoped_ptr<FileResource>())));
1456 return CancelCallback();
1459 base::MessageLoop::current()->PostTask(
1460 FROM_HERE,
1461 base::Bind(callback, HTTP_CREATED,
1462 base::Passed(make_scoped_ptr(
1463 new FileResource(*new_entry->change_resource.file())))));
1464 base::MessageLoop::current()->PostTask(
1465 FROM_HERE,
1466 base::Bind(&FakeDriveService::NotifyObservers,
1467 weak_ptr_factory_.GetWeakPtr()));
1468 return CancelCallback();
1471 void FakeDriveService::SetLastModifiedTime(
1472 const std::string& resource_id,
1473 const base::Time& last_modified_time,
1474 const FileResourceCallback& callback) {
1475 DCHECK(thread_checker_.CalledOnValidThread());
1476 DCHECK(!callback.is_null());
1478 if (offline_) {
1479 base::MessageLoop::current()->PostTask(
1480 FROM_HERE,
1481 base::Bind(callback,
1482 DRIVE_NO_CONNECTION,
1483 base::Passed(scoped_ptr<FileResource>())));
1484 return;
1487 EntryInfo* entry = FindEntryByResourceId(resource_id);
1488 if (!entry) {
1489 base::MessageLoop::current()->PostTask(
1490 FROM_HERE,
1491 base::Bind(callback, HTTP_NOT_FOUND,
1492 base::Passed(scoped_ptr<FileResource>())));
1493 return;
1496 ChangeResource* change = &entry->change_resource;
1497 FileResource* file = change->mutable_file();
1498 file->set_modified_date(last_modified_time);
1500 base::MessageLoop::current()->PostTask(
1501 FROM_HERE,
1502 base::Bind(callback, HTTP_SUCCESS,
1503 base::Passed(make_scoped_ptr(new FileResource(*file)))));
1506 google_apis::DriveApiErrorCode FakeDriveService::SetUserPermission(
1507 const std::string& resource_id,
1508 google_apis::drive::PermissionRole user_permission) {
1509 DCHECK(thread_checker_.CalledOnValidThread());
1511 EntryInfo* entry = FindEntryByResourceId(resource_id);
1512 if (!entry)
1513 return HTTP_NOT_FOUND;
1515 entry->user_permission = user_permission;
1516 return HTTP_SUCCESS;
1519 void FakeDriveService::AddChangeObserver(ChangeObserver* change_observer) {
1520 change_observers_.AddObserver(change_observer);
1523 void FakeDriveService::RemoveChangeObserver(ChangeObserver* change_observer) {
1524 change_observers_.RemoveObserver(change_observer);
1527 FakeDriveService::EntryInfo* FakeDriveService::FindEntryByResourceId(
1528 const std::string& resource_id) {
1529 DCHECK(thread_checker_.CalledOnValidThread());
1531 EntryInfoMap::iterator it = entries_.find(resource_id);
1532 // Deleted entries don't have FileResource.
1533 return it != entries_.end() && it->second->change_resource.file() ?
1534 it->second : NULL;
1537 std::string FakeDriveService::GetNewResourceId() {
1538 DCHECK(thread_checker_.CalledOnValidThread());
1540 ++resource_id_count_;
1541 return base::StringPrintf("resource_id_%d", resource_id_count_);
1544 void FakeDriveService::UpdateETag(google_apis::FileResource* file) {
1545 file->set_etag(
1546 "etag_" + base::Int64ToString(about_resource_->largest_change_id()));
1549 void FakeDriveService::AddNewChangestamp(google_apis::ChangeResource* change) {
1550 about_resource_->set_largest_change_id(
1551 about_resource_->largest_change_id() + 1);
1552 change->set_change_id(about_resource_->largest_change_id());
1555 const FakeDriveService::EntryInfo* FakeDriveService::AddNewEntry(
1556 const std::string& given_resource_id,
1557 const std::string& content_type,
1558 const std::string& content_data,
1559 const std::string& parent_resource_id,
1560 const std::string& title,
1561 bool shared_with_me) {
1562 DCHECK(thread_checker_.CalledOnValidThread());
1564 if (!parent_resource_id.empty() &&
1565 parent_resource_id != GetRootResourceId() &&
1566 !entries_.count(parent_resource_id)) {
1567 return NULL;
1570 const std::string resource_id =
1571 given_resource_id.empty() ? GetNewResourceId() : given_resource_id;
1572 if (entries_.count(resource_id))
1573 return NULL;
1574 GURL upload_url = GURL("https://xxx/upload/" + resource_id);
1576 scoped_ptr<EntryInfo> new_entry(new EntryInfo);
1577 ChangeResource* new_change = &new_entry->change_resource;
1578 FileResource* new_file = new FileResource;
1579 new_change->set_file(make_scoped_ptr(new_file));
1581 // Set the resource ID and the title
1582 new_change->set_file_id(resource_id);
1583 new_file->set_file_id(resource_id);
1584 new_file->set_title(title);
1585 // Set the contents, size and MD5 for a file.
1586 if (content_type != util::kDriveFolderMimeType &&
1587 !util::IsKnownHostedDocumentMimeType(content_type)) {
1588 new_entry->content_data = content_data;
1589 new_file->set_file_size(content_data.size());
1590 new_file->set_md5_checksum(base::MD5String(content_data));
1593 if (shared_with_me) {
1594 // Set current time to mark the file as shared_with_me.
1595 new_file->set_shared_with_me_date(base::Time::Now());
1598 std::string escaped_resource_id = net::EscapePath(resource_id);
1600 // Set mime type.
1601 new_file->set_mime_type(content_type);
1603 // Set alternate link if needed.
1604 if (content_type == util::kGoogleDocumentMimeType)
1605 new_file->set_alternate_link(GURL("https://document_alternate_link"));
1607 // Set parents.
1608 if (!parent_resource_id.empty()) {
1609 ParentReference parent;
1610 parent.set_file_id(parent_resource_id);
1611 parent.set_parent_link(GetFakeLinkUrl(parent.file_id()));
1612 std::vector<ParentReference> parents;
1613 parents.push_back(parent);
1614 *new_file->mutable_parents() = parents;
1617 new_entry->share_url = net::AppendOrReplaceQueryParameter(
1618 share_url_base_, "name", title);
1620 AddNewChangestamp(new_change);
1621 UpdateETag(new_file);
1623 base::Time published_date =
1624 base::Time() + base::TimeDelta::FromMilliseconds(++published_date_seq_);
1625 new_file->set_created_date(published_date);
1627 EntryInfo* raw_new_entry = new_entry.release();
1628 entries_[resource_id] = raw_new_entry;
1629 return raw_new_entry;
1632 void FakeDriveService::GetChangeListInternal(
1633 int64 start_changestamp,
1634 const std::string& search_query,
1635 const std::string& directory_resource_id,
1636 int start_offset,
1637 int max_results,
1638 int* load_counter,
1639 const ChangeListCallback& callback) {
1640 if (offline_) {
1641 base::MessageLoop::current()->PostTask(
1642 FROM_HERE,
1643 base::Bind(callback,
1644 DRIVE_NO_CONNECTION,
1645 base::Passed(scoped_ptr<ChangeList>())));
1646 return;
1649 // Filter out entries per parameters like |directory_resource_id| and
1650 // |search_query|.
1651 ScopedVector<ChangeResource> entries;
1652 int num_entries_matched = 0;
1653 for (EntryInfoMap::iterator it = entries_.begin(); it != entries_.end();
1654 ++it) {
1655 const ChangeResource& entry = it->second->change_resource;
1656 bool should_exclude = false;
1658 // If |directory_resource_id| is set, exclude the entry if it's not in
1659 // the target directory.
1660 if (!directory_resource_id.empty()) {
1661 // Get the parent resource ID of the entry.
1662 std::string parent_resource_id;
1663 if (entry.file() && !entry.file()->parents().empty())
1664 parent_resource_id = entry.file()->parents()[0].file_id();
1666 if (directory_resource_id != parent_resource_id)
1667 should_exclude = true;
1670 // If |search_query| is set, exclude the entry if it does not contain the
1671 // search query in the title.
1672 if (!should_exclude && !search_query.empty() &&
1673 !EntryMatchWithQuery(entry, search_query)) {
1674 should_exclude = true;
1677 // If |start_changestamp| is set, exclude the entry if the
1678 // changestamp is older than |largest_changestamp|.
1679 // See https://developers.google.com/google-apps/documents-list/
1680 // #retrieving_all_changes_since_a_given_changestamp
1681 if (start_changestamp > 0 && entry.change_id() < start_changestamp)
1682 should_exclude = true;
1684 // If the caller requests other list than change list by specifying
1685 // zero-|start_changestamp|, exclude deleted entry from the result.
1686 const bool deleted = entry.is_deleted() ||
1687 (entry.file() && entry.file()->labels().is_trashed());
1688 if (!start_changestamp && deleted)
1689 should_exclude = true;
1691 // The entry matched the criteria for inclusion.
1692 if (!should_exclude)
1693 ++num_entries_matched;
1695 // If |start_offset| is set, exclude the entry if the entry is before the
1696 // start index. <= instead of < as |num_entries_matched| was
1697 // already incremented.
1698 if (start_offset > 0 && num_entries_matched <= start_offset)
1699 should_exclude = true;
1701 if (!should_exclude) {
1702 scoped_ptr<ChangeResource> entry_copied(new ChangeResource);
1703 entry_copied->set_change_id(entry.change_id());
1704 entry_copied->set_file_id(entry.file_id());
1705 entry_copied->set_deleted(entry.is_deleted());
1706 if (entry.file()) {
1707 entry_copied->set_file(
1708 make_scoped_ptr(new FileResource(*entry.file())));
1710 entry_copied->set_modification_date(entry.modification_date());
1711 entries.push_back(entry_copied.release());
1715 scoped_ptr<ChangeList> change_list(new ChangeList);
1716 if (start_changestamp > 0 && start_offset == 0) {
1717 change_list->set_largest_change_id(about_resource_->largest_change_id());
1720 // If |max_results| is set, trim the entries if the number exceeded the max
1721 // results.
1722 if (max_results > 0 && entries.size() > static_cast<size_t>(max_results)) {
1723 entries.erase(entries.begin() + max_results, entries.end());
1724 // Adds the next URL.
1725 // Here, we embed information which is needed for continuing the
1726 // GetChangeList request in the next invocation into url query
1727 // parameters.
1728 GURL next_url(base::StringPrintf(
1729 "http://localhost/?start-offset=%d&max-results=%d",
1730 start_offset + max_results,
1731 max_results));
1732 if (start_changestamp > 0) {
1733 next_url = net::AppendOrReplaceQueryParameter(
1734 next_url, "changestamp",
1735 base::Int64ToString(start_changestamp).c_str());
1737 if (!search_query.empty()) {
1738 next_url = net::AppendOrReplaceQueryParameter(
1739 next_url, "q", search_query);
1741 if (!directory_resource_id.empty()) {
1742 next_url = net::AppendOrReplaceQueryParameter(
1743 next_url, "parent", directory_resource_id);
1746 change_list->set_next_link(next_url);
1748 *change_list->mutable_items() = entries.Pass();
1750 if (load_counter)
1751 *load_counter += 1;
1752 base::MessageLoop::current()->PostTask(
1753 FROM_HERE,
1754 base::Bind(callback, HTTP_SUCCESS, base::Passed(&change_list)));
1757 GURL FakeDriveService::GetNewUploadSessionUrl() {
1758 return GURL("https://upload_session_url/" +
1759 base::Int64ToString(next_upload_sequence_number_++));
1762 google_apis::CancelCallback FakeDriveService::AddPermission(
1763 const std::string& resource_id,
1764 const std::string& email,
1765 google_apis::drive::PermissionRole role,
1766 const google_apis::EntryActionCallback& callback) {
1767 DCHECK(thread_checker_.CalledOnValidThread());
1768 DCHECK(!callback.is_null());
1770 NOTREACHED();
1771 return CancelCallback();
1774 void FakeDriveService::NotifyObservers() {
1775 FOR_EACH_OBSERVER(ChangeObserver, change_observers_, OnNewChangeAvailable());
1778 } // namespace drive