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"
9 #include "base/files/file_util.h"
10 #include "base/json/json_string_value_serializer.h"
11 #include "base/logging.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 "content/public/browser/browser_thread.h"
23 #include "google_apis/drive/drive_api_parser.h"
24 #include "google_apis/drive/test_util.h"
25 #include "net/base/escape.h"
26 #include "net/base/url_util.h"
28 using content::BrowserThread;
29 using google_apis::AboutResource;
30 using google_apis::AboutResourceCallback;
31 using google_apis::AppList;
32 using google_apis::AppListCallback;
33 using google_apis::AuthStatusCallback;
34 using google_apis::AuthorizeAppCallback;
35 using google_apis::CancelCallback;
36 using google_apis::ChangeList;
37 using google_apis::ChangeListCallback;
38 using google_apis::ChangeResource;
39 using google_apis::DownloadActionCallback;
40 using google_apis::EntryActionCallback;
41 using google_apis::FileList;
42 using google_apis::FileListCallback;
43 using google_apis::FileResource;
44 using google_apis::FileResourceCallback;
45 using google_apis::GDATA_FILE_ERROR;
46 using google_apis::GDATA_NO_CONNECTION;
47 using google_apis::GDATA_OTHER_ERROR;
48 using google_apis::GDataErrorCode;
49 using google_apis::GetContentCallback;
50 using google_apis::GetShareUrlCallback;
51 using google_apis::HTTP_BAD_REQUEST;
52 using google_apis::HTTP_CREATED;
53 using google_apis::HTTP_FORBIDDEN;
54 using google_apis::HTTP_NOT_FOUND;
55 using google_apis::HTTP_NO_CONTENT;
56 using google_apis::HTTP_PRECONDITION;
57 using google_apis::HTTP_RESUME_INCOMPLETE;
58 using google_apis::HTTP_SUCCESS;
59 using google_apis::InitiateUploadCallback;
60 using google_apis::ParentReference;
61 using google_apis::ProgressCallback;
62 using google_apis::UploadRangeResponse;
63 using google_apis::drive::UploadRangeCallback;
64 namespace test_util = google_apis::test_util;
69 // Returns true if the entry matches with the search query.
70 // Supports queries consist of following format.
71 // - Phrases quoted by double/single quotes
72 // - AND search for multiple words/phrases segmented by space
73 // - Limited attribute search. Only "title:" is supported.
74 bool EntryMatchWithQuery(const ChangeResource& entry,
75 const std::string& query) {
76 base::StringTokenizer tokenizer(query, " ");
77 tokenizer.set_quote_chars("\"'");
78 while (tokenizer.GetNext()) {
79 std::string key, value;
80 const std::string& token = tokenizer.token();
81 if (token.find(':') == std::string::npos) {
82 base::TrimString(token, "\"'", &value);
84 base::StringTokenizer key_value(token, ":");
85 key_value.set_quote_chars("\"'");
86 if (!key_value.GetNext())
88 key = key_value.token();
89 if (!key_value.GetNext())
91 base::TrimString(key_value.token(), "\"'", &value);
94 // TODO(peria): Deal with other attributes than title.
95 if (!key.empty() && key != "title")
97 // Search query in the title.
99 entry.file()->title().find(value) == std::string::npos)
105 void ScheduleUploadRangeCallback(const UploadRangeCallback& callback,
106 int64 start_position,
108 GDataErrorCode error,
109 scoped_ptr<FileResource> entry) {
110 base::MessageLoop::current()->PostTask(
113 UploadRangeResponse(error,
116 base::Passed(&entry)));
119 void FileListCallbackAdapter(const FileListCallback& callback,
120 GDataErrorCode error,
121 scoped_ptr<ChangeList> change_list) {
122 scoped_ptr<FileList> file_list;
124 callback.Run(error, file_list.Pass());
128 file_list.reset(new FileList);
129 file_list->set_next_link(change_list->next_link());
130 for (size_t i = 0; i < change_list->items().size(); ++i) {
131 const ChangeResource& entry = *change_list->items()[i];
133 file_list->mutable_items()->push_back(new FileResource(*entry.file()));
135 callback.Run(error, file_list.Pass());
138 bool UserHasWriteAccess(google_apis::drive::PermissionRole user_permission) {
139 switch (user_permission) {
140 case google_apis::drive::PERMISSION_ROLE_OWNER:
141 case google_apis::drive::PERMISSION_ROLE_WRITER:
143 case google_apis::drive::PERMISSION_ROLE_READER:
144 case google_apis::drive::PERMISSION_ROLE_COMMENTER:
152 struct FakeDriveService::EntryInfo {
153 EntryInfo() : user_permission(google_apis::drive::PERMISSION_ROLE_OWNER) {}
155 google_apis::ChangeResource change_resource;
157 std::string content_data;
159 // Behaves in the same way as "userPermission" described in
160 // https://developers.google.com/drive/v2/reference/files
161 google_apis::drive::PermissionRole user_permission;
164 struct FakeDriveService::UploadSession {
165 std::string content_type;
166 int64 content_length;
167 std::string parent_resource_id;
168 std::string resource_id;
179 std::string content_type,
180 int64 content_length,
181 std::string parent_resource_id,
182 std::string resource_id,
185 : content_type(content_type),
186 content_length(content_length),
187 parent_resource_id(parent_resource_id),
188 resource_id(resource_id),
195 FakeDriveService::FakeDriveService()
196 : about_resource_(new AboutResource),
197 published_date_seq_(0),
198 next_upload_sequence_number_(0),
199 default_max_results_(0),
200 resource_id_count_(0),
201 file_list_load_count_(0),
202 change_list_load_count_(0),
203 directory_load_count_(0),
204 about_resource_load_count_(0),
205 app_list_load_count_(0),
206 blocked_file_list_load_count_(0),
208 never_return_all_file_list_(false),
209 share_url_base_("https://share_url/"),
210 weak_ptr_factory_(this) {
211 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
213 about_resource_->set_largest_change_id(654321);
214 about_resource_->set_quota_bytes_total(9876543210);
215 about_resource_->set_quota_bytes_used(6789012345);
216 about_resource_->set_root_folder_id(GetRootResourceId());
219 FakeDriveService::~FakeDriveService() {
220 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
221 STLDeleteValues(&entries_);
224 bool FakeDriveService::LoadAppListForDriveApi(
225 const std::string& relative_path) {
226 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
228 // Load JSON data, which must be a dictionary.
229 scoped_ptr<base::Value> value = test_util::LoadJSONFile(relative_path);
230 CHECK_EQ(base::Value::TYPE_DICTIONARY, value->GetType());
231 app_info_value_.reset(
232 static_cast<base::DictionaryValue*>(value.release()));
233 return app_info_value_;
236 void FakeDriveService::AddApp(const std::string& app_id,
237 const std::string& app_name,
238 const std::string& product_id,
239 const std::string& create_url,
241 if (app_json_template_.empty()) {
242 base::FilePath path =
243 test_util::GetTestFilePath("drive/applist_app_template.json");
244 CHECK(base::ReadFileToString(path, &app_json_template_));
247 std::string app_json = app_json_template_;
248 ReplaceSubstringsAfterOffset(&app_json, 0, "$AppId", app_id);
249 ReplaceSubstringsAfterOffset(&app_json, 0, "$AppName", app_name);
250 ReplaceSubstringsAfterOffset(&app_json, 0, "$ProductId", product_id);
251 ReplaceSubstringsAfterOffset(&app_json, 0, "$CreateUrl", create_url);
252 ReplaceSubstringsAfterOffset(
253 &app_json, 0, "$Removable", is_removable ? "true" : "false");
255 JSONStringValueSerializer json(app_json);
256 std::string error_message;
257 scoped_ptr<base::Value> value(json.Deserialize(NULL, &error_message));
258 CHECK_EQ(base::Value::TYPE_DICTIONARY, value->GetType());
260 base::ListValue* item_list;
261 CHECK(app_info_value_->GetListWithoutPathExpansion("items", &item_list));
262 item_list->Append(value.release());
265 void FakeDriveService::RemoveAppByProductId(const std::string& product_id) {
266 base::ListValue* item_list;
267 CHECK(app_info_value_->GetListWithoutPathExpansion("items", &item_list));
268 for (size_t i = 0; i < item_list->GetSize(); ++i) {
269 base::DictionaryValue* item;
270 CHECK(item_list->GetDictionary(i, &item));
271 const char kKeyProductId[] = "productId";
272 std::string item_product_id;
273 if (item->GetStringWithoutPathExpansion(kKeyProductId, &item_product_id) &&
274 product_id == item_product_id) {
275 item_list->Remove(i, NULL);
281 bool FakeDriveService::HasApp(const std::string& app_id) const {
282 base::ListValue* item_list;
283 CHECK(app_info_value_->GetListWithoutPathExpansion("items", &item_list));
284 for (size_t i = 0; i < item_list->GetSize(); ++i) {
285 base::DictionaryValue* item;
286 CHECK(item_list->GetDictionary(i, &item));
287 const char kKeyId[] = "id";
289 if (item->GetStringWithoutPathExpansion(kKeyId, &item_id) &&
298 void FakeDriveService::SetQuotaValue(int64 used, int64 total) {
299 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
301 about_resource_->set_quota_bytes_used(used);
302 about_resource_->set_quota_bytes_total(total);
305 GURL FakeDriveService::GetFakeLinkUrl(const std::string& resource_id) {
306 return GURL("https://fake_server/" + net::EscapePath(resource_id));
309 void FakeDriveService::Initialize(const std::string& account_id) {
310 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
313 void FakeDriveService::AddObserver(DriveServiceObserver* observer) {
314 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
317 void FakeDriveService::RemoveObserver(DriveServiceObserver* observer) {
318 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
321 bool FakeDriveService::CanSendRequest() const {
322 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
326 bool FakeDriveService::HasAccessToken() const {
327 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
331 void FakeDriveService::RequestAccessToken(const AuthStatusCallback& callback) {
332 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
333 DCHECK(!callback.is_null());
334 callback.Run(google_apis::HTTP_NOT_MODIFIED, "fake_access_token");
337 bool FakeDriveService::HasRefreshToken() const {
338 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
342 void FakeDriveService::ClearAccessToken() {
343 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
346 void FakeDriveService::ClearRefreshToken() {
347 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
350 std::string FakeDriveService::GetRootResourceId() const {
354 CancelCallback FakeDriveService::GetAllFileList(
355 const FileListCallback& callback) {
356 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
357 DCHECK(!callback.is_null());
359 if (never_return_all_file_list_) {
360 ++blocked_file_list_load_count_;
361 return CancelCallback();
364 GetChangeListInternal(0, // start changestamp
365 std::string(), // empty search query
366 std::string(), // no directory resource id,
368 default_max_results_,
369 &file_list_load_count_,
370 base::Bind(&FileListCallbackAdapter, callback));
371 return CancelCallback();
374 CancelCallback FakeDriveService::GetFileListInDirectory(
375 const std::string& directory_resource_id,
376 const FileListCallback& callback) {
377 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
378 DCHECK(!directory_resource_id.empty());
379 DCHECK(!callback.is_null());
381 GetChangeListInternal(0, // start changestamp
382 std::string(), // empty search query
383 directory_resource_id,
385 default_max_results_,
386 &directory_load_count_,
387 base::Bind(&FileListCallbackAdapter, callback));
388 return CancelCallback();
391 CancelCallback FakeDriveService::Search(
392 const std::string& search_query,
393 const FileListCallback& callback) {
394 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
395 DCHECK(!search_query.empty());
396 DCHECK(!callback.is_null());
398 GetChangeListInternal(0, // start changestamp
400 std::string(), // no directory resource id,
402 default_max_results_,
404 base::Bind(&FileListCallbackAdapter, callback));
405 return CancelCallback();
408 CancelCallback FakeDriveService::SearchByTitle(
409 const std::string& title,
410 const std::string& directory_resource_id,
411 const FileListCallback& callback) {
412 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
413 DCHECK(!title.empty());
414 DCHECK(!callback.is_null());
416 // Note: the search implementation here doesn't support quotation unescape,
417 // so don't escape here.
418 GetChangeListInternal(0, // start changestamp
419 base::StringPrintf("title:'%s'", title.c_str()),
420 directory_resource_id,
422 default_max_results_,
424 base::Bind(&FileListCallbackAdapter, callback));
425 return CancelCallback();
428 CancelCallback FakeDriveService::GetChangeList(
429 int64 start_changestamp,
430 const ChangeListCallback& callback) {
431 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
432 DCHECK(!callback.is_null());
434 GetChangeListInternal(start_changestamp,
435 std::string(), // empty search query
436 std::string(), // no directory resource id,
438 default_max_results_,
439 &change_list_load_count_,
441 return CancelCallback();
444 CancelCallback FakeDriveService::GetRemainingChangeList(
445 const GURL& next_link,
446 const ChangeListCallback& callback) {
447 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
448 DCHECK(!next_link.is_empty());
449 DCHECK(!callback.is_null());
451 // "changestamp", "q", "parent" and "start-offset" are parameters to
452 // implement "paging" of the result on FakeDriveService.
453 // The URL should be the one filled in GetChangeListInternal of the
454 // previous method invocation, so it should start with "http://localhost/?".
455 // See also GetChangeListInternal.
456 DCHECK_EQ(next_link.host(), "localhost");
457 DCHECK_EQ(next_link.path(), "/");
459 int64 start_changestamp = 0;
460 std::string search_query;
461 std::string directory_resource_id;
462 int start_offset = 0;
463 int max_results = default_max_results_;
464 base::StringPairs parameters;
465 if (base::SplitStringIntoKeyValuePairs(
466 next_link.query(), '=', '&', ¶meters)) {
467 for (size_t i = 0; i < parameters.size(); ++i) {
468 if (parameters[i].first == "changestamp") {
469 base::StringToInt64(parameters[i].second, &start_changestamp);
470 } else if (parameters[i].first == "q") {
472 net::UnescapeURLComponent(parameters[i].second,
473 net::UnescapeRule::URL_SPECIAL_CHARS);
474 } else if (parameters[i].first == "parent") {
475 directory_resource_id =
476 net::UnescapeURLComponent(parameters[i].second,
477 net::UnescapeRule::URL_SPECIAL_CHARS);
478 } else if (parameters[i].first == "start-offset") {
479 base::StringToInt(parameters[i].second, &start_offset);
480 } else if (parameters[i].first == "max-results") {
481 base::StringToInt(parameters[i].second, &max_results);
486 GetChangeListInternal(start_changestamp, search_query, directory_resource_id,
487 start_offset, max_results, NULL, callback);
488 return CancelCallback();
491 CancelCallback FakeDriveService::GetRemainingFileList(
492 const GURL& next_link,
493 const FileListCallback& callback) {
494 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
495 DCHECK(!next_link.is_empty());
496 DCHECK(!callback.is_null());
498 return GetRemainingChangeList(
499 next_link, base::Bind(&FileListCallbackAdapter, callback));
502 CancelCallback FakeDriveService::GetFileResource(
503 const std::string& resource_id,
504 const FileResourceCallback& callback) {
505 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
506 DCHECK(!callback.is_null());
509 base::MessageLoop::current()->PostTask(
513 base::Passed(scoped_ptr<FileResource>())));
514 return CancelCallback();
517 EntryInfo* entry = FindEntryByResourceId(resource_id);
518 if (entry && entry->change_resource.file()) {
519 base::MessageLoop::current()->PostTask(
521 base::Bind(callback, HTTP_SUCCESS, base::Passed(make_scoped_ptr(
522 new FileResource(*entry->change_resource.file())))));
523 return CancelCallback();
526 base::MessageLoop::current()->PostTask(
528 base::Bind(callback, HTTP_NOT_FOUND,
529 base::Passed(scoped_ptr<FileResource>())));
530 return CancelCallback();
533 CancelCallback FakeDriveService::GetShareUrl(
534 const std::string& resource_id,
535 const GURL& /* embed_origin */,
536 const GetShareUrlCallback& callback) {
537 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
538 DCHECK(!callback.is_null());
541 base::MessageLoop::current()->PostTask(
546 return CancelCallback();
549 EntryInfo* entry = FindEntryByResourceId(resource_id);
551 base::MessageLoop::current()->PostTask(
553 base::Bind(callback, HTTP_SUCCESS, entry->share_url));
554 return CancelCallback();
557 base::MessageLoop::current()->PostTask(
559 base::Bind(callback, HTTP_NOT_FOUND, GURL()));
560 return CancelCallback();
563 CancelCallback FakeDriveService::GetAboutResource(
564 const AboutResourceCallback& callback) {
565 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
566 DCHECK(!callback.is_null());
569 scoped_ptr<AboutResource> null;
570 base::MessageLoop::current()->PostTask(
573 GDATA_NO_CONNECTION, base::Passed(&null)));
574 return CancelCallback();
577 ++about_resource_load_count_;
578 scoped_ptr<AboutResource> about_resource(new AboutResource(*about_resource_));
579 base::MessageLoop::current()->PostTask(
582 HTTP_SUCCESS, base::Passed(&about_resource)));
583 return CancelCallback();
586 CancelCallback FakeDriveService::GetAppList(const AppListCallback& callback) {
587 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
588 DCHECK(!callback.is_null());
589 DCHECK(app_info_value_);
592 scoped_ptr<AppList> null;
593 base::MessageLoop::current()->PostTask(
597 base::Passed(&null)));
598 return CancelCallback();
601 ++app_list_load_count_;
602 scoped_ptr<AppList> app_list(AppList::CreateFrom(*app_info_value_));
603 base::MessageLoop::current()->PostTask(
605 base::Bind(callback, HTTP_SUCCESS, base::Passed(&app_list)));
606 return CancelCallback();
609 CancelCallback FakeDriveService::DeleteResource(
610 const std::string& resource_id,
611 const std::string& etag,
612 const EntryActionCallback& callback) {
613 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
614 DCHECK(!callback.is_null());
617 base::MessageLoop::current()->PostTask(
618 FROM_HERE, base::Bind(callback, GDATA_NO_CONNECTION));
619 return CancelCallback();
622 EntryInfo* entry = FindEntryByResourceId(resource_id);
624 base::MessageLoop::current()->PostTask(
625 FROM_HERE, base::Bind(callback, HTTP_NOT_FOUND));
626 return CancelCallback();
629 ChangeResource* change = &entry->change_resource;
630 const FileResource* file = change->file();
631 if (change->is_deleted()) {
632 base::MessageLoop::current()->PostTask(
633 FROM_HERE, base::Bind(callback, HTTP_NOT_FOUND));
634 return CancelCallback();
637 if (!etag.empty() && etag != file->etag()) {
638 base::MessageLoop::current()->PostTask(
639 FROM_HERE, base::Bind(callback, HTTP_PRECONDITION));
640 return CancelCallback();
643 if (entry->user_permission != google_apis::drive::PERMISSION_ROLE_OWNER) {
644 base::MessageLoop::current()->PostTask(
645 FROM_HERE, base::Bind(callback, HTTP_FORBIDDEN));
646 return CancelCallback();
649 change->set_deleted(true);
650 AddNewChangestamp(change);
651 change->set_file(scoped_ptr<FileResource>());
652 base::MessageLoop::current()->PostTask(
653 FROM_HERE, base::Bind(callback, HTTP_NO_CONTENT));
654 base::MessageLoop::current()->PostTask(
656 base::Bind(&FakeDriveService::NotifyObservers,
657 weak_ptr_factory_.GetWeakPtr()));
658 return CancelCallback();
661 CancelCallback FakeDriveService::TrashResource(
662 const std::string& resource_id,
663 const EntryActionCallback& callback) {
664 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
665 DCHECK(!callback.is_null());
668 base::MessageLoop::current()->PostTask(
669 FROM_HERE, base::Bind(callback, GDATA_NO_CONNECTION));
670 return CancelCallback();
673 EntryInfo* entry = FindEntryByResourceId(resource_id);
675 base::MessageLoop::current()->PostTask(
676 FROM_HERE, base::Bind(callback, HTTP_NOT_FOUND));
677 return CancelCallback();
680 ChangeResource* change = &entry->change_resource;
681 FileResource* file = change->mutable_file();
682 if (change->is_deleted() || file->labels().is_trashed()) {
683 base::MessageLoop::current()->PostTask(
684 FROM_HERE, base::Bind(callback, HTTP_NOT_FOUND));
685 return CancelCallback();
688 if (entry->user_permission != google_apis::drive::PERMISSION_ROLE_OWNER) {
689 base::MessageLoop::current()->PostTask(
690 FROM_HERE, base::Bind(callback, HTTP_FORBIDDEN));
691 return CancelCallback();
694 file->mutable_labels()->set_trashed(true);
695 AddNewChangestamp(change);
696 base::MessageLoop::current()->PostTask(
697 FROM_HERE, base::Bind(callback, HTTP_SUCCESS));
698 base::MessageLoop::current()->PostTask(
700 base::Bind(&FakeDriveService::NotifyObservers,
701 weak_ptr_factory_.GetWeakPtr()));
702 return CancelCallback();
705 CancelCallback FakeDriveService::DownloadFile(
706 const base::FilePath& local_cache_path,
707 const std::string& resource_id,
708 const DownloadActionCallback& download_action_callback,
709 const GetContentCallback& get_content_callback,
710 const ProgressCallback& progress_callback) {
711 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
712 DCHECK(!download_action_callback.is_null());
715 base::MessageLoop::current()->PostTask(
717 base::Bind(download_action_callback,
720 return CancelCallback();
723 EntryInfo* entry = FindEntryByResourceId(resource_id);
724 if (!entry || entry->change_resource.file()->IsHostedDocument()) {
725 base::MessageLoopProxy::current()->PostTask(
727 base::Bind(download_action_callback, HTTP_NOT_FOUND, base::FilePath()));
728 return CancelCallback();
731 const FileResource* file = entry->change_resource.file();
732 const std::string& content_data = entry->content_data;
733 int64 file_size = file->file_size();
734 DCHECK_EQ(static_cast<size_t>(file_size), content_data.size());
736 if (!get_content_callback.is_null()) {
737 const int64 kBlockSize = 5;
738 for (int64 i = 0; i < file_size; i += kBlockSize) {
739 const int64 size = std::min(kBlockSize, file_size - i);
740 scoped_ptr<std::string> content_for_callback(
741 new std::string(content_data.substr(i, size)));
742 base::MessageLoopProxy::current()->PostTask(
744 base::Bind(get_content_callback, HTTP_SUCCESS,
745 base::Passed(&content_for_callback)));
749 if (!test_util::WriteStringToFile(local_cache_path, content_data)) {
750 // Failed to write the content.
751 base::MessageLoopProxy::current()->PostTask(
753 base::Bind(download_action_callback,
754 GDATA_FILE_ERROR, base::FilePath()));
755 return CancelCallback();
758 if (!progress_callback.is_null()) {
759 // See also the comment in ResumeUpload(). For testing that clients
760 // can handle the case progress_callback is called multiple times,
761 // here we invoke the callback twice.
762 base::MessageLoopProxy::current()->PostTask(
764 base::Bind(progress_callback, file_size / 2, file_size));
765 base::MessageLoopProxy::current()->PostTask(
767 base::Bind(progress_callback, file_size, file_size));
769 base::MessageLoopProxy::current()->PostTask(
771 base::Bind(download_action_callback,
774 return CancelCallback();
777 CancelCallback FakeDriveService::CopyResource(
778 const std::string& resource_id,
779 const std::string& in_parent_resource_id,
780 const std::string& new_title,
781 const base::Time& last_modified,
782 const FileResourceCallback& callback) {
783 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
784 DCHECK(!callback.is_null());
787 base::MessageLoop::current()->PostTask(
791 base::Passed(scoped_ptr<FileResource>())));
792 return CancelCallback();
795 const std::string& parent_resource_id = in_parent_resource_id.empty() ?
796 GetRootResourceId() : in_parent_resource_id;
798 EntryInfo* entry = FindEntryByResourceId(resource_id);
800 base::MessageLoop::current()->PostTask(
802 base::Bind(callback, HTTP_NOT_FOUND,
803 base::Passed(scoped_ptr<FileResource>())));
804 return CancelCallback();
807 // Make a copy and set the new resource ID and the new title.
808 scoped_ptr<EntryInfo> copied_entry(new EntryInfo);
809 copied_entry->content_data = entry->content_data;
810 copied_entry->share_url = entry->share_url;
811 copied_entry->change_resource.set_file(
812 make_scoped_ptr(new FileResource(*entry->change_resource.file())));
814 ChangeResource* new_change = &copied_entry->change_resource;
815 FileResource* new_file = new_change->mutable_file();
816 const std::string new_resource_id = GetNewResourceId();
817 new_change->set_file_id(new_resource_id);
818 new_file->set_file_id(new_resource_id);
819 new_file->set_title(new_title);
821 ParentReference parent;
822 parent.set_file_id(parent_resource_id);
823 parent.set_parent_link(GetFakeLinkUrl(parent_resource_id));
824 std::vector<ParentReference> parents;
825 parents.push_back(parent);
826 *new_file->mutable_parents() = parents;
828 if (!last_modified.is_null())
829 new_file->set_modified_date(last_modified);
831 AddNewChangestamp(new_change);
832 UpdateETag(new_file);
834 // Add the new entry to the map.
835 entries_[new_resource_id] = copied_entry.release();
837 base::MessageLoop::current()->PostTask(
841 base::Passed(make_scoped_ptr(new FileResource(*new_file)))));
842 base::MessageLoop::current()->PostTask(
844 base::Bind(&FakeDriveService::NotifyObservers,
845 weak_ptr_factory_.GetWeakPtr()));
846 return CancelCallback();
849 CancelCallback FakeDriveService::UpdateResource(
850 const std::string& resource_id,
851 const std::string& parent_resource_id,
852 const std::string& new_title,
853 const base::Time& last_modified,
854 const base::Time& last_viewed_by_me,
855 const google_apis::FileResourceCallback& callback) {
856 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
857 DCHECK(!callback.is_null());
860 base::MessageLoop::current()->PostTask(
861 FROM_HERE, base::Bind(callback, GDATA_NO_CONNECTION,
862 base::Passed(scoped_ptr<FileResource>())));
863 return CancelCallback();
866 EntryInfo* entry = FindEntryByResourceId(resource_id);
868 base::MessageLoop::current()->PostTask(
870 base::Bind(callback, HTTP_NOT_FOUND,
871 base::Passed(scoped_ptr<FileResource>())));
872 return CancelCallback();
875 if (!UserHasWriteAccess(entry->user_permission)) {
876 base::MessageLoop::current()->PostTask(
878 base::Bind(callback, HTTP_FORBIDDEN,
879 base::Passed(scoped_ptr<FileResource>())));
880 return CancelCallback();
883 ChangeResource* change = &entry->change_resource;
884 FileResource* file = change->mutable_file();
886 if (!new_title.empty())
887 file->set_title(new_title);
889 // Set parent if necessary.
890 if (!parent_resource_id.empty()) {
891 ParentReference parent;
892 parent.set_file_id(parent_resource_id);
893 parent.set_parent_link(GetFakeLinkUrl(parent_resource_id));
895 std::vector<ParentReference> parents;
896 parents.push_back(parent);
897 *file->mutable_parents() = parents;
900 if (!last_modified.is_null())
901 file->set_modified_date(last_modified);
903 if (!last_viewed_by_me.is_null())
904 file->set_last_viewed_by_me_date(last_viewed_by_me);
906 AddNewChangestamp(change);
909 base::MessageLoop::current()->PostTask(
911 base::Bind(callback, HTTP_SUCCESS,
912 base::Passed(make_scoped_ptr(new FileResource(*file)))));
913 base::MessageLoop::current()->PostTask(
915 base::Bind(&FakeDriveService::NotifyObservers,
916 weak_ptr_factory_.GetWeakPtr()));
917 return CancelCallback();
920 CancelCallback FakeDriveService::AddResourceToDirectory(
921 const std::string& parent_resource_id,
922 const std::string& resource_id,
923 const EntryActionCallback& callback) {
924 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
925 DCHECK(!callback.is_null());
928 base::MessageLoop::current()->PostTask(
929 FROM_HERE, base::Bind(callback, GDATA_NO_CONNECTION));
930 return CancelCallback();
933 EntryInfo* entry = FindEntryByResourceId(resource_id);
935 base::MessageLoop::current()->PostTask(
936 FROM_HERE, base::Bind(callback, HTTP_NOT_FOUND));
937 return CancelCallback();
940 ChangeResource* change = &entry->change_resource;
941 // On the real Drive server, resources do not necessary shape a tree
942 // structure. That is, each resource can have multiple parent.
943 // We mimic the behavior here; AddResourceToDirectoy just adds
944 // one more parent, not overwriting old ones.
945 ParentReference parent;
946 parent.set_file_id(parent_resource_id);
947 parent.set_parent_link(GetFakeLinkUrl(parent_resource_id));
948 change->mutable_file()->mutable_parents()->push_back(parent);
950 AddNewChangestamp(change);
951 base::MessageLoop::current()->PostTask(
952 FROM_HERE, base::Bind(callback, HTTP_SUCCESS));
953 base::MessageLoop::current()->PostTask(
955 base::Bind(&FakeDriveService::NotifyObservers,
956 weak_ptr_factory_.GetWeakPtr()));
957 return CancelCallback();
960 CancelCallback FakeDriveService::RemoveResourceFromDirectory(
961 const std::string& parent_resource_id,
962 const std::string& resource_id,
963 const EntryActionCallback& callback) {
964 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
965 DCHECK(!callback.is_null());
968 base::MessageLoop::current()->PostTask(
969 FROM_HERE, base::Bind(callback, GDATA_NO_CONNECTION));
970 return CancelCallback();
973 EntryInfo* entry = FindEntryByResourceId(resource_id);
975 base::MessageLoop::current()->PostTask(
976 FROM_HERE, base::Bind(callback, HTTP_NOT_FOUND));
977 return CancelCallback();
980 ChangeResource* change = &entry->change_resource;
981 FileResource* file = change->mutable_file();
982 std::vector<ParentReference>* parents = file->mutable_parents();
983 for (size_t i = 0; i < parents->size(); ++i) {
984 if ((*parents)[i].file_id() == parent_resource_id) {
985 parents->erase(parents->begin() + i);
986 AddNewChangestamp(change);
987 base::MessageLoop::current()->PostTask(
988 FROM_HERE, base::Bind(callback, HTTP_NO_CONTENT));
989 base::MessageLoop::current()->PostTask(
991 base::Bind(&FakeDriveService::NotifyObservers,
992 weak_ptr_factory_.GetWeakPtr()));
993 return CancelCallback();
997 base::MessageLoop::current()->PostTask(
998 FROM_HERE, base::Bind(callback, HTTP_NOT_FOUND));
999 return CancelCallback();
1002 CancelCallback FakeDriveService::AddNewDirectory(
1003 const std::string& parent_resource_id,
1004 const std::string& directory_title,
1005 const AddNewDirectoryOptions& options,
1006 const FileResourceCallback& callback) {
1007 return AddNewDirectoryWithResourceId(
1009 parent_resource_id.empty() ? GetRootResourceId() : parent_resource_id,
1015 CancelCallback FakeDriveService::InitiateUploadNewFile(
1016 const std::string& content_type,
1017 int64 content_length,
1018 const std::string& parent_resource_id,
1019 const std::string& title,
1020 const InitiateUploadNewFileOptions& options,
1021 const InitiateUploadCallback& callback) {
1022 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
1023 DCHECK(!callback.is_null());
1026 base::MessageLoop::current()->PostTask(
1028 base::Bind(callback, GDATA_NO_CONNECTION, GURL()));
1029 return CancelCallback();
1032 if (parent_resource_id != GetRootResourceId() &&
1033 !entries_.count(parent_resource_id)) {
1034 base::MessageLoop::current()->PostTask(
1036 base::Bind(callback, HTTP_NOT_FOUND, GURL()));
1037 return CancelCallback();
1040 GURL session_url = GetNewUploadSessionUrl();
1041 upload_sessions_[session_url] =
1042 UploadSession(content_type, content_length,
1048 base::MessageLoop::current()->PostTask(
1050 base::Bind(callback, HTTP_SUCCESS, session_url));
1051 return CancelCallback();
1054 CancelCallback FakeDriveService::InitiateUploadExistingFile(
1055 const std::string& content_type,
1056 int64 content_length,
1057 const std::string& resource_id,
1058 const InitiateUploadExistingFileOptions& options,
1059 const InitiateUploadCallback& callback) {
1060 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
1061 DCHECK(!callback.is_null());
1064 base::MessageLoop::current()->PostTask(
1066 base::Bind(callback, GDATA_NO_CONNECTION, GURL()));
1067 return CancelCallback();
1070 EntryInfo* entry = FindEntryByResourceId(resource_id);
1072 base::MessageLoop::current()->PostTask(
1074 base::Bind(callback, HTTP_NOT_FOUND, GURL()));
1075 return CancelCallback();
1078 if (!UserHasWriteAccess(entry->user_permission)) {
1079 base::MessageLoop::current()->PostTask(
1081 base::Bind(callback, HTTP_FORBIDDEN, GURL()));
1082 return CancelCallback();
1085 FileResource* file = entry->change_resource.mutable_file();
1086 if (!options.etag.empty() && options.etag != file->etag()) {
1087 base::MessageLoop::current()->PostTask(
1089 base::Bind(callback, HTTP_PRECONDITION, GURL()));
1090 return CancelCallback();
1092 // TODO(hashimoto): Update |file|'s metadata with |options|.
1094 GURL session_url = GetNewUploadSessionUrl();
1095 upload_sessions_[session_url] =
1096 UploadSession(content_type, content_length,
1097 "", // parent_resource_id
1102 base::MessageLoop::current()->PostTask(
1104 base::Bind(callback, HTTP_SUCCESS, session_url));
1105 return CancelCallback();
1108 CancelCallback FakeDriveService::GetUploadStatus(
1109 const GURL& upload_url,
1110 int64 content_length,
1111 const UploadRangeCallback& callback) {
1112 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
1113 DCHECK(!callback.is_null());
1114 return CancelCallback();
1117 CancelCallback FakeDriveService::ResumeUpload(
1118 const GURL& upload_url,
1119 int64 start_position,
1121 int64 content_length,
1122 const std::string& content_type,
1123 const base::FilePath& local_file_path,
1124 const UploadRangeCallback& callback,
1125 const ProgressCallback& progress_callback) {
1126 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
1127 DCHECK(!callback.is_null());
1129 FileResourceCallback completion_callback
1130 = base::Bind(&ScheduleUploadRangeCallback,
1131 callback, start_position, end_position);
1134 completion_callback.Run(GDATA_NO_CONNECTION, scoped_ptr<FileResource>());
1135 return CancelCallback();
1138 if (!upload_sessions_.count(upload_url)) {
1139 completion_callback.Run(HTTP_NOT_FOUND, scoped_ptr<FileResource>());
1140 return CancelCallback();
1143 UploadSession* session = &upload_sessions_[upload_url];
1145 // Chunks are required to be sent in such a ways that they fill from the start
1146 // of the not-yet-uploaded part with no gaps nor overlaps.
1147 if (session->uploaded_size != start_position) {
1148 completion_callback.Run(HTTP_BAD_REQUEST, scoped_ptr<FileResource>());
1149 return CancelCallback();
1152 if (!progress_callback.is_null()) {
1153 // In the real GDataWapi/Drive DriveService, progress is reported in
1154 // nondeterministic timing. In this fake implementation, we choose to call
1155 // it twice per one ResumeUpload. This is for making sure that client code
1156 // works fine even if the callback is invoked more than once; it is the
1157 // crucial difference of the progress callback from others.
1158 // Note that progress is notified in the relative offset in each chunk.
1159 const int64 chunk_size = end_position - start_position;
1160 base::MessageLoop::current()->PostTask(
1161 FROM_HERE, base::Bind(progress_callback, chunk_size / 2, chunk_size));
1162 base::MessageLoop::current()->PostTask(
1163 FROM_HERE, base::Bind(progress_callback, chunk_size, chunk_size));
1166 if (content_length != end_position) {
1167 session->uploaded_size = end_position;
1168 completion_callback.Run(HTTP_RESUME_INCOMPLETE, scoped_ptr<FileResource>());
1169 return CancelCallback();
1172 std::string content_data;
1173 if (!base::ReadFileToString(local_file_path, &content_data)) {
1174 session->uploaded_size = end_position;
1175 completion_callback.Run(GDATA_FILE_ERROR, scoped_ptr<FileResource>());
1176 return CancelCallback();
1178 session->uploaded_size = end_position;
1180 // |resource_id| is empty if the upload is for new file.
1181 if (session->resource_id.empty()) {
1182 DCHECK(!session->parent_resource_id.empty());
1183 DCHECK(!session->title.empty());
1184 const EntryInfo* new_entry = AddNewEntry(
1185 "", // auto generate resource id.
1186 session->content_type,
1188 session->parent_resource_id,
1190 false); // shared_with_me
1192 completion_callback.Run(HTTP_NOT_FOUND, scoped_ptr<FileResource>());
1193 return CancelCallback();
1196 completion_callback.Run(HTTP_CREATED, make_scoped_ptr(
1197 new FileResource(*new_entry->change_resource.file())));
1198 base::MessageLoop::current()->PostTask(
1200 base::Bind(&FakeDriveService::NotifyObservers,
1201 weak_ptr_factory_.GetWeakPtr()));
1202 return CancelCallback();
1205 EntryInfo* entry = FindEntryByResourceId(session->resource_id);
1207 completion_callback.Run(HTTP_NOT_FOUND, scoped_ptr<FileResource>());
1208 return CancelCallback();
1211 ChangeResource* change = &entry->change_resource;
1212 FileResource* file = change->mutable_file();
1213 if (file->etag().empty() || session->etag != file->etag()) {
1214 completion_callback.Run(HTTP_PRECONDITION, scoped_ptr<FileResource>());
1215 return CancelCallback();
1218 file->set_md5_checksum(base::MD5String(content_data));
1219 entry->content_data = content_data;
1220 file->set_file_size(end_position);
1221 AddNewChangestamp(change);
1224 completion_callback.Run(HTTP_SUCCESS, make_scoped_ptr(
1225 new FileResource(*file)));
1226 base::MessageLoop::current()->PostTask(
1228 base::Bind(&FakeDriveService::NotifyObservers,
1229 weak_ptr_factory_.GetWeakPtr()));
1230 return CancelCallback();
1233 CancelCallback FakeDriveService::AuthorizeApp(
1234 const std::string& resource_id,
1235 const std::string& app_id,
1236 const AuthorizeAppCallback& callback) {
1237 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
1238 DCHECK(!callback.is_null());
1240 if (entries_.count(resource_id) == 0) {
1241 callback.Run(google_apis::HTTP_NOT_FOUND, GURL());
1242 return CancelCallback();
1245 callback.Run(HTTP_SUCCESS,
1246 GURL(base::StringPrintf(open_url_format_.c_str(),
1247 resource_id.c_str(),
1249 return CancelCallback();
1252 CancelCallback FakeDriveService::UninstallApp(
1253 const std::string& app_id,
1254 const google_apis::EntryActionCallback& callback) {
1255 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
1256 DCHECK(!callback.is_null());
1259 base::MessageLoop::current()->PostTask(
1261 base::Bind(callback, google_apis::GDATA_NO_CONNECTION));
1262 return CancelCallback();
1265 // Find app_id from app_info_value_ and delete.
1266 base::ListValue* items = NULL;
1267 if (!app_info_value_->GetList("items", &items)) {
1268 base::MessageLoop::current()->PostTask(
1270 base::Bind(callback, google_apis::HTTP_NOT_FOUND));
1271 return CancelCallback();
1274 for (size_t i = 0; i < items->GetSize(); ++i) {
1275 base::DictionaryValue* item = NULL;
1277 if (items->GetDictionary(i, &item) && item->GetString("id", &id) &&
1279 base::MessageLoop::current()->PostTask(
1281 base::Bind(callback,
1282 items->Remove(i, NULL) ? google_apis::HTTP_NO_CONTENT
1283 : google_apis::HTTP_NOT_FOUND));
1284 return CancelCallback();
1288 base::MessageLoop::current()->PostTask(
1290 base::Bind(callback, google_apis::HTTP_NOT_FOUND));
1291 return CancelCallback();
1294 void FakeDriveService::AddNewFile(const std::string& content_type,
1295 const std::string& content_data,
1296 const std::string& parent_resource_id,
1297 const std::string& title,
1298 bool shared_with_me,
1299 const FileResourceCallback& callback) {
1300 AddNewFileWithResourceId("", content_type, content_data, parent_resource_id,
1301 title, shared_with_me, callback);
1304 void FakeDriveService::AddNewFileWithResourceId(
1305 const std::string& resource_id,
1306 const std::string& content_type,
1307 const std::string& content_data,
1308 const std::string& parent_resource_id,
1309 const std::string& title,
1310 bool shared_with_me,
1311 const FileResourceCallback& callback) {
1312 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
1313 DCHECK(!callback.is_null());
1316 base::MessageLoop::current()->PostTask(
1318 base::Bind(callback,
1319 GDATA_NO_CONNECTION,
1320 base::Passed(scoped_ptr<FileResource>())));
1324 const EntryInfo* new_entry = AddNewEntry(resource_id,
1331 base::MessageLoop::current()->PostTask(
1333 base::Bind(callback, HTTP_NOT_FOUND,
1334 base::Passed(scoped_ptr<FileResource>())));
1338 base::MessageLoop::current()->PostTask(
1340 base::Bind(callback, HTTP_CREATED,
1341 base::Passed(make_scoped_ptr(
1342 new FileResource(*new_entry->change_resource.file())))));
1343 base::MessageLoop::current()->PostTask(
1345 base::Bind(&FakeDriveService::NotifyObservers,
1346 weak_ptr_factory_.GetWeakPtr()));
1349 CancelCallback FakeDriveService::AddNewDirectoryWithResourceId(
1350 const std::string& resource_id,
1351 const std::string& parent_resource_id,
1352 const std::string& directory_title,
1353 const AddNewDirectoryOptions& options,
1354 const FileResourceCallback& callback) {
1355 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
1356 DCHECK(!callback.is_null());
1359 base::MessageLoop::current()->PostTask(
1361 base::Bind(callback,
1362 GDATA_NO_CONNECTION,
1363 base::Passed(scoped_ptr<FileResource>())));
1364 return CancelCallback();
1367 const EntryInfo* new_entry = AddNewEntry(resource_id,
1368 util::kDriveFolderMimeType,
1372 false); // shared_with_me
1374 base::MessageLoop::current()->PostTask(
1376 base::Bind(callback, HTTP_NOT_FOUND,
1377 base::Passed(scoped_ptr<FileResource>())));
1378 return CancelCallback();
1381 base::MessageLoop::current()->PostTask(
1383 base::Bind(callback, HTTP_CREATED,
1384 base::Passed(make_scoped_ptr(
1385 new FileResource(*new_entry->change_resource.file())))));
1386 base::MessageLoop::current()->PostTask(
1388 base::Bind(&FakeDriveService::NotifyObservers,
1389 weak_ptr_factory_.GetWeakPtr()));
1390 return CancelCallback();
1393 void FakeDriveService::SetLastModifiedTime(
1394 const std::string& resource_id,
1395 const base::Time& last_modified_time,
1396 const FileResourceCallback& callback) {
1397 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
1398 DCHECK(!callback.is_null());
1401 base::MessageLoop::current()->PostTask(
1403 base::Bind(callback,
1404 GDATA_NO_CONNECTION,
1405 base::Passed(scoped_ptr<FileResource>())));
1409 EntryInfo* entry = FindEntryByResourceId(resource_id);
1411 base::MessageLoop::current()->PostTask(
1413 base::Bind(callback, HTTP_NOT_FOUND,
1414 base::Passed(scoped_ptr<FileResource>())));
1418 ChangeResource* change = &entry->change_resource;
1419 FileResource* file = change->mutable_file();
1420 file->set_modified_date(last_modified_time);
1422 base::MessageLoop::current()->PostTask(
1424 base::Bind(callback, HTTP_SUCCESS,
1425 base::Passed(make_scoped_ptr(new FileResource(*file)))));
1428 google_apis::GDataErrorCode FakeDriveService::SetUserPermission(
1429 const std::string& resource_id,
1430 google_apis::drive::PermissionRole user_permission) {
1431 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
1433 EntryInfo* entry = FindEntryByResourceId(resource_id);
1435 return HTTP_NOT_FOUND;
1437 entry->user_permission = user_permission;
1438 return HTTP_SUCCESS;
1441 void FakeDriveService::AddChangeObserver(ChangeObserver* change_observer) {
1442 change_observers_.AddObserver(change_observer);
1445 void FakeDriveService::RemoveChangeObserver(ChangeObserver* change_observer) {
1446 change_observers_.RemoveObserver(change_observer);
1449 FakeDriveService::EntryInfo* FakeDriveService::FindEntryByResourceId(
1450 const std::string& resource_id) {
1451 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
1453 EntryInfoMap::iterator it = entries_.find(resource_id);
1454 // Deleted entries don't have FileResource.
1455 return it != entries_.end() && it->second->change_resource.file() ?
1459 std::string FakeDriveService::GetNewResourceId() {
1460 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
1462 ++resource_id_count_;
1463 return base::StringPrintf("resource_id_%d", resource_id_count_);
1466 void FakeDriveService::UpdateETag(google_apis::FileResource* file) {
1468 "etag_" + base::Int64ToString(about_resource_->largest_change_id()));
1471 void FakeDriveService::AddNewChangestamp(google_apis::ChangeResource* change) {
1472 about_resource_->set_largest_change_id(
1473 about_resource_->largest_change_id() + 1);
1474 change->set_change_id(about_resource_->largest_change_id());
1477 const FakeDriveService::EntryInfo* FakeDriveService::AddNewEntry(
1478 const std::string& given_resource_id,
1479 const std::string& content_type,
1480 const std::string& content_data,
1481 const std::string& parent_resource_id,
1482 const std::string& title,
1483 bool shared_with_me) {
1484 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
1486 if (!parent_resource_id.empty() &&
1487 parent_resource_id != GetRootResourceId() &&
1488 !entries_.count(parent_resource_id)) {
1492 const std::string resource_id =
1493 given_resource_id.empty() ? GetNewResourceId() : given_resource_id;
1494 if (entries_.count(resource_id))
1496 GURL upload_url = GURL("https://xxx/upload/" + resource_id);
1498 scoped_ptr<EntryInfo> new_entry(new EntryInfo);
1499 ChangeResource* new_change = &new_entry->change_resource;
1500 FileResource* new_file = new FileResource;
1501 new_change->set_file(make_scoped_ptr(new_file));
1503 // Set the resource ID and the title
1504 new_change->set_file_id(resource_id);
1505 new_file->set_file_id(resource_id);
1506 new_file->set_title(title);
1507 // Set the contents, size and MD5 for a file.
1508 if (content_type != util::kDriveFolderMimeType &&
1509 !util::IsKnownHostedDocumentMimeType(content_type)) {
1510 new_entry->content_data = content_data;
1511 new_file->set_file_size(content_data.size());
1512 new_file->set_md5_checksum(base::MD5String(content_data));
1515 if (shared_with_me) {
1516 // Set current time to mark the file as shared_with_me.
1517 new_file->set_shared_with_me_date(base::Time::Now());
1520 std::string escaped_resource_id = net::EscapePath(resource_id);
1523 new_file->set_mime_type(content_type);
1525 // Set alternate link if needed.
1526 if (content_type == util::kGoogleDocumentMimeType)
1527 new_file->set_alternate_link(GURL("https://document_alternate_link"));
1530 if (!parent_resource_id.empty()) {
1531 ParentReference parent;
1532 parent.set_file_id(parent_resource_id);
1533 parent.set_parent_link(GetFakeLinkUrl(parent.file_id()));
1534 std::vector<ParentReference> parents;
1535 parents.push_back(parent);
1536 *new_file->mutable_parents() = parents;
1539 new_entry->share_url = net::AppendOrReplaceQueryParameter(
1540 share_url_base_, "name", title);
1542 AddNewChangestamp(new_change);
1543 UpdateETag(new_file);
1545 base::Time published_date =
1546 base::Time() + base::TimeDelta::FromMilliseconds(++published_date_seq_);
1547 new_file->set_created_date(published_date);
1549 EntryInfo* raw_new_entry = new_entry.release();
1550 entries_[resource_id] = raw_new_entry;
1551 return raw_new_entry;
1554 void FakeDriveService::GetChangeListInternal(
1555 int64 start_changestamp,
1556 const std::string& search_query,
1557 const std::string& directory_resource_id,
1561 const ChangeListCallback& callback) {
1563 base::MessageLoop::current()->PostTask(
1565 base::Bind(callback,
1566 GDATA_NO_CONNECTION,
1567 base::Passed(scoped_ptr<ChangeList>())));
1571 // Filter out entries per parameters like |directory_resource_id| and
1573 ScopedVector<ChangeResource> entries;
1574 int num_entries_matched = 0;
1575 for (EntryInfoMap::iterator it = entries_.begin(); it != entries_.end();
1577 const ChangeResource& entry = it->second->change_resource;
1578 bool should_exclude = false;
1580 // If |directory_resource_id| is set, exclude the entry if it's not in
1581 // the target directory.
1582 if (!directory_resource_id.empty()) {
1583 // Get the parent resource ID of the entry.
1584 std::string parent_resource_id;
1585 if (entry.file() && !entry.file()->parents().empty())
1586 parent_resource_id = entry.file()->parents()[0].file_id();
1588 if (directory_resource_id != parent_resource_id)
1589 should_exclude = true;
1592 // If |search_query| is set, exclude the entry if it does not contain the
1593 // search query in the title.
1594 if (!should_exclude && !search_query.empty() &&
1595 !EntryMatchWithQuery(entry, search_query)) {
1596 should_exclude = true;
1599 // If |start_changestamp| is set, exclude the entry if the
1600 // changestamp is older than |largest_changestamp|.
1601 // See https://developers.google.com/google-apps/documents-list/
1602 // #retrieving_all_changes_since_a_given_changestamp
1603 if (start_changestamp > 0 && entry.change_id() < start_changestamp)
1604 should_exclude = true;
1606 // If the caller requests other list than change list by specifying
1607 // zero-|start_changestamp|, exclude deleted entry from the result.
1608 const bool deleted = entry.is_deleted() ||
1609 (entry.file() && entry.file()->labels().is_trashed());
1610 if (!start_changestamp && deleted)
1611 should_exclude = true;
1613 // The entry matched the criteria for inclusion.
1614 if (!should_exclude)
1615 ++num_entries_matched;
1617 // If |start_offset| is set, exclude the entry if the entry is before the
1618 // start index. <= instead of < as |num_entries_matched| was
1619 // already incremented.
1620 if (start_offset > 0 && num_entries_matched <= start_offset)
1621 should_exclude = true;
1623 if (!should_exclude) {
1624 scoped_ptr<ChangeResource> entry_copied(new ChangeResource);
1625 entry_copied->set_change_id(entry.change_id());
1626 entry_copied->set_file_id(entry.file_id());
1627 entry_copied->set_deleted(entry.is_deleted());
1629 entry_copied->set_file(
1630 make_scoped_ptr(new FileResource(*entry.file())));
1632 entry_copied->set_modification_date(entry.modification_date());
1633 entries.push_back(entry_copied.release());
1637 scoped_ptr<ChangeList> change_list(new ChangeList);
1638 if (start_changestamp > 0 && start_offset == 0) {
1639 change_list->set_largest_change_id(about_resource_->largest_change_id());
1642 // If |max_results| is set, trim the entries if the number exceeded the max
1644 if (max_results > 0 && entries.size() > static_cast<size_t>(max_results)) {
1645 entries.erase(entries.begin() + max_results, entries.end());
1646 // Adds the next URL.
1647 // Here, we embed information which is needed for continuing the
1648 // GetChangeList request in the next invocation into url query
1650 GURL next_url(base::StringPrintf(
1651 "http://localhost/?start-offset=%d&max-results=%d",
1652 start_offset + max_results,
1654 if (start_changestamp > 0) {
1655 next_url = net::AppendOrReplaceQueryParameter(
1656 next_url, "changestamp",
1657 base::Int64ToString(start_changestamp).c_str());
1659 if (!search_query.empty()) {
1660 next_url = net::AppendOrReplaceQueryParameter(
1661 next_url, "q", search_query);
1663 if (!directory_resource_id.empty()) {
1664 next_url = net::AppendOrReplaceQueryParameter(
1665 next_url, "parent", directory_resource_id);
1668 change_list->set_next_link(next_url);
1670 *change_list->mutable_items() = entries.Pass();
1674 base::MessageLoop::current()->PostTask(
1676 base::Bind(callback, HTTP_SUCCESS, base::Passed(&change_list)));
1679 GURL FakeDriveService::GetNewUploadSessionUrl() {
1680 return GURL("https://upload_session_url/" +
1681 base::Int64ToString(next_upload_sequence_number_++));
1684 google_apis::CancelCallback FakeDriveService::AddPermission(
1685 const std::string& resource_id,
1686 const std::string& email,
1687 google_apis::drive::PermissionRole role,
1688 const google_apis::EntryActionCallback& callback) {
1689 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
1690 DCHECK(!callback.is_null());
1693 return CancelCallback();
1696 void FakeDriveService::NotifyObservers() {
1697 FOR_EACH_OBSERVER(ChangeObserver, change_observers_, OnNewChangeAvailable());
1700 } // namespace drive