Drive: Add definition of StartBatchRequest to DriveServiceInterface.
[chromium-blink-merge.git] / chrome / browser / drive / fake_drive_service.cc
blob3e4e854c812d1d4f3db666852e17609a356d05a7
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 JSONStringValueDeserializer 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::drive::Properties& properties,
884 const google_apis::FileResourceCallback& callback) {
885 DCHECK(thread_checker_.CalledOnValidThread());
886 DCHECK(!callback.is_null());
888 if (offline_) {
889 base::MessageLoop::current()->PostTask(
890 FROM_HERE, base::Bind(callback, DRIVE_NO_CONNECTION,
891 base::Passed(scoped_ptr<FileResource>())));
892 return CancelCallback();
895 EntryInfo* entry = FindEntryByResourceId(resource_id);
896 if (!entry) {
897 base::MessageLoop::current()->PostTask(
898 FROM_HERE,
899 base::Bind(callback, HTTP_NOT_FOUND,
900 base::Passed(scoped_ptr<FileResource>())));
901 return CancelCallback();
904 if (!UserHasWriteAccess(entry->user_permission)) {
905 base::MessageLoop::current()->PostTask(
906 FROM_HERE,
907 base::Bind(callback, HTTP_FORBIDDEN,
908 base::Passed(scoped_ptr<FileResource>())));
909 return CancelCallback();
912 ChangeResource* change = &entry->change_resource;
913 FileResource* file = change->mutable_file();
915 if (!new_title.empty())
916 file->set_title(new_title);
918 // Set parent if necessary.
919 if (!parent_resource_id.empty()) {
920 ParentReference parent;
921 parent.set_file_id(parent_resource_id);
922 parent.set_parent_link(GetFakeLinkUrl(parent_resource_id));
924 std::vector<ParentReference> parents;
925 parents.push_back(parent);
926 *file->mutable_parents() = parents;
929 if (!last_modified.is_null())
930 file->set_modified_date(last_modified);
932 if (!last_viewed_by_me.is_null())
933 file->set_last_viewed_by_me_date(last_viewed_by_me);
935 AddNewChangestamp(change);
936 UpdateETag(file);
938 base::MessageLoop::current()->PostTask(
939 FROM_HERE,
940 base::Bind(callback, HTTP_SUCCESS,
941 base::Passed(make_scoped_ptr(new FileResource(*file)))));
942 base::MessageLoop::current()->PostTask(
943 FROM_HERE,
944 base::Bind(&FakeDriveService::NotifyObservers,
945 weak_ptr_factory_.GetWeakPtr()));
946 return CancelCallback();
949 CancelCallback FakeDriveService::AddResourceToDirectory(
950 const std::string& parent_resource_id,
951 const std::string& resource_id,
952 const EntryActionCallback& callback) {
953 DCHECK(thread_checker_.CalledOnValidThread());
954 DCHECK(!callback.is_null());
956 if (offline_) {
957 base::MessageLoop::current()->PostTask(
958 FROM_HERE, base::Bind(callback, DRIVE_NO_CONNECTION));
959 return CancelCallback();
962 EntryInfo* entry = FindEntryByResourceId(resource_id);
963 if (!entry) {
964 base::MessageLoop::current()->PostTask(
965 FROM_HERE, base::Bind(callback, HTTP_NOT_FOUND));
966 return CancelCallback();
969 ChangeResource* change = &entry->change_resource;
970 // On the real Drive server, resources do not necessary shape a tree
971 // structure. That is, each resource can have multiple parent.
972 // We mimic the behavior here; AddResourceToDirectoy just adds
973 // one more parent, not overwriting old ones.
974 ParentReference parent;
975 parent.set_file_id(parent_resource_id);
976 parent.set_parent_link(GetFakeLinkUrl(parent_resource_id));
977 change->mutable_file()->mutable_parents()->push_back(parent);
979 AddNewChangestamp(change);
980 base::MessageLoop::current()->PostTask(
981 FROM_HERE, base::Bind(callback, HTTP_SUCCESS));
982 base::MessageLoop::current()->PostTask(
983 FROM_HERE,
984 base::Bind(&FakeDriveService::NotifyObservers,
985 weak_ptr_factory_.GetWeakPtr()));
986 return CancelCallback();
989 CancelCallback FakeDriveService::RemoveResourceFromDirectory(
990 const std::string& parent_resource_id,
991 const std::string& resource_id,
992 const EntryActionCallback& callback) {
993 DCHECK(thread_checker_.CalledOnValidThread());
994 DCHECK(!callback.is_null());
996 if (offline_) {
997 base::MessageLoop::current()->PostTask(
998 FROM_HERE, base::Bind(callback, DRIVE_NO_CONNECTION));
999 return CancelCallback();
1002 EntryInfo* entry = FindEntryByResourceId(resource_id);
1003 if (!entry) {
1004 base::MessageLoop::current()->PostTask(
1005 FROM_HERE, base::Bind(callback, HTTP_NOT_FOUND));
1006 return CancelCallback();
1009 ChangeResource* change = &entry->change_resource;
1010 FileResource* file = change->mutable_file();
1011 std::vector<ParentReference>* parents = file->mutable_parents();
1012 for (size_t i = 0; i < parents->size(); ++i) {
1013 if ((*parents)[i].file_id() == parent_resource_id) {
1014 parents->erase(parents->begin() + i);
1015 AddNewChangestamp(change);
1016 base::MessageLoop::current()->PostTask(
1017 FROM_HERE, base::Bind(callback, HTTP_NO_CONTENT));
1018 base::MessageLoop::current()->PostTask(
1019 FROM_HERE,
1020 base::Bind(&FakeDriveService::NotifyObservers,
1021 weak_ptr_factory_.GetWeakPtr()));
1022 return CancelCallback();
1026 base::MessageLoop::current()->PostTask(
1027 FROM_HERE, base::Bind(callback, HTTP_NOT_FOUND));
1028 return CancelCallback();
1031 CancelCallback FakeDriveService::AddNewDirectory(
1032 const std::string& parent_resource_id,
1033 const std::string& directory_title,
1034 const AddNewDirectoryOptions& options,
1035 const FileResourceCallback& callback) {
1036 return AddNewDirectoryWithResourceId(
1038 parent_resource_id.empty() ? GetRootResourceId() : parent_resource_id,
1039 directory_title,
1040 options,
1041 callback);
1044 CancelCallback FakeDriveService::InitiateUploadNewFile(
1045 const std::string& content_type,
1046 int64 content_length,
1047 const std::string& parent_resource_id,
1048 const std::string& title,
1049 const UploadNewFileOptions& options,
1050 const InitiateUploadCallback& callback) {
1051 DCHECK(thread_checker_.CalledOnValidThread());
1052 DCHECK(!callback.is_null());
1054 if (offline_) {
1055 base::MessageLoop::current()->PostTask(
1056 FROM_HERE,
1057 base::Bind(callback, DRIVE_NO_CONNECTION, GURL()));
1058 return CancelCallback();
1061 if (parent_resource_id != GetRootResourceId() &&
1062 !entries_.count(parent_resource_id)) {
1063 base::MessageLoop::current()->PostTask(
1064 FROM_HERE,
1065 base::Bind(callback, HTTP_NOT_FOUND, GURL()));
1066 return CancelCallback();
1069 GURL session_url = GetNewUploadSessionUrl();
1070 upload_sessions_[session_url] =
1071 UploadSession(content_type, content_length,
1072 parent_resource_id,
1073 "", // resource_id
1074 "", // etag
1075 title);
1077 base::MessageLoop::current()->PostTask(
1078 FROM_HERE,
1079 base::Bind(callback, HTTP_SUCCESS, session_url));
1080 return CancelCallback();
1083 CancelCallback FakeDriveService::InitiateUploadExistingFile(
1084 const std::string& content_type,
1085 int64 content_length,
1086 const std::string& resource_id,
1087 const UploadExistingFileOptions& options,
1088 const InitiateUploadCallback& callback) {
1089 DCHECK(thread_checker_.CalledOnValidThread());
1090 DCHECK(!callback.is_null());
1092 if (offline_) {
1093 base::MessageLoop::current()->PostTask(
1094 FROM_HERE,
1095 base::Bind(callback, DRIVE_NO_CONNECTION, GURL()));
1096 return CancelCallback();
1099 EntryInfo* entry = FindEntryByResourceId(resource_id);
1100 if (!entry) {
1101 base::MessageLoop::current()->PostTask(
1102 FROM_HERE,
1103 base::Bind(callback, HTTP_NOT_FOUND, GURL()));
1104 return CancelCallback();
1107 if (!UserHasWriteAccess(entry->user_permission)) {
1108 base::MessageLoop::current()->PostTask(
1109 FROM_HERE,
1110 base::Bind(callback, HTTP_FORBIDDEN, GURL()));
1111 return CancelCallback();
1114 FileResource* file = entry->change_resource.mutable_file();
1115 if (!options.etag.empty() && options.etag != file->etag()) {
1116 base::MessageLoop::current()->PostTask(
1117 FROM_HERE,
1118 base::Bind(callback, HTTP_PRECONDITION, GURL()));
1119 return CancelCallback();
1121 // TODO(hashimoto): Update |file|'s metadata with |options|.
1123 GURL session_url = GetNewUploadSessionUrl();
1124 upload_sessions_[session_url] =
1125 UploadSession(content_type, content_length,
1126 "", // parent_resource_id
1127 resource_id,
1128 file->etag(),
1129 "" /* title */);
1131 base::MessageLoop::current()->PostTask(
1132 FROM_HERE,
1133 base::Bind(callback, HTTP_SUCCESS, session_url));
1134 return CancelCallback();
1137 CancelCallback FakeDriveService::GetUploadStatus(
1138 const GURL& upload_url,
1139 int64 content_length,
1140 const UploadRangeCallback& callback) {
1141 DCHECK(thread_checker_.CalledOnValidThread());
1142 DCHECK(!callback.is_null());
1143 return CancelCallback();
1146 CancelCallback FakeDriveService::ResumeUpload(
1147 const GURL& upload_url,
1148 int64 start_position,
1149 int64 end_position,
1150 int64 content_length,
1151 const std::string& content_type,
1152 const base::FilePath& local_file_path,
1153 const UploadRangeCallback& callback,
1154 const ProgressCallback& progress_callback) {
1155 DCHECK(thread_checker_.CalledOnValidThread());
1156 DCHECK(!callback.is_null());
1158 FileResourceCallback completion_callback
1159 = base::Bind(&ScheduleUploadRangeCallback,
1160 callback, start_position, end_position);
1162 if (offline_) {
1163 completion_callback.Run(DRIVE_NO_CONNECTION, scoped_ptr<FileResource>());
1164 return CancelCallback();
1167 if (!upload_sessions_.count(upload_url)) {
1168 completion_callback.Run(HTTP_NOT_FOUND, scoped_ptr<FileResource>());
1169 return CancelCallback();
1172 UploadSession* session = &upload_sessions_[upload_url];
1174 // Chunks are required to be sent in such a ways that they fill from the start
1175 // of the not-yet-uploaded part with no gaps nor overlaps.
1176 if (session->uploaded_size != start_position) {
1177 completion_callback.Run(HTTP_BAD_REQUEST, scoped_ptr<FileResource>());
1178 return CancelCallback();
1181 if (!progress_callback.is_null()) {
1182 // In the real GDataWapi/Drive DriveService, progress is reported in
1183 // nondeterministic timing. In this fake implementation, we choose to call
1184 // it twice per one ResumeUpload. This is for making sure that client code
1185 // works fine even if the callback is invoked more than once; it is the
1186 // crucial difference of the progress callback from others.
1187 // Note that progress is notified in the relative offset in each chunk.
1188 const int64 chunk_size = end_position - start_position;
1189 base::MessageLoop::current()->PostTask(
1190 FROM_HERE, base::Bind(progress_callback, chunk_size / 2, chunk_size));
1191 base::MessageLoop::current()->PostTask(
1192 FROM_HERE, base::Bind(progress_callback, chunk_size, chunk_size));
1195 if (content_length != end_position) {
1196 session->uploaded_size = end_position;
1197 completion_callback.Run(HTTP_RESUME_INCOMPLETE, scoped_ptr<FileResource>());
1198 return CancelCallback();
1201 std::string content_data;
1202 if (!base::ReadFileToString(local_file_path, &content_data)) {
1203 session->uploaded_size = end_position;
1204 completion_callback.Run(DRIVE_FILE_ERROR, scoped_ptr<FileResource>());
1205 return CancelCallback();
1207 session->uploaded_size = end_position;
1209 // |resource_id| is empty if the upload is for new file.
1210 if (session->resource_id.empty()) {
1211 DCHECK(!session->parent_resource_id.empty());
1212 DCHECK(!session->title.empty());
1213 const EntryInfo* new_entry = AddNewEntry(
1214 "", // auto generate resource id.
1215 session->content_type,
1216 content_data,
1217 session->parent_resource_id,
1218 session->title,
1219 false); // shared_with_me
1220 if (!new_entry) {
1221 completion_callback.Run(HTTP_NOT_FOUND, scoped_ptr<FileResource>());
1222 return CancelCallback();
1225 completion_callback.Run(HTTP_CREATED, make_scoped_ptr(
1226 new FileResource(*new_entry->change_resource.file())));
1227 base::MessageLoop::current()->PostTask(
1228 FROM_HERE,
1229 base::Bind(&FakeDriveService::NotifyObservers,
1230 weak_ptr_factory_.GetWeakPtr()));
1231 return CancelCallback();
1234 EntryInfo* entry = FindEntryByResourceId(session->resource_id);
1235 if (!entry) {
1236 completion_callback.Run(HTTP_NOT_FOUND, scoped_ptr<FileResource>());
1237 return CancelCallback();
1240 ChangeResource* change = &entry->change_resource;
1241 FileResource* file = change->mutable_file();
1242 if (file->etag().empty() || session->etag != file->etag()) {
1243 completion_callback.Run(HTTP_PRECONDITION, scoped_ptr<FileResource>());
1244 return CancelCallback();
1247 file->set_md5_checksum(base::MD5String(content_data));
1248 entry->content_data = content_data;
1249 file->set_file_size(end_position);
1250 AddNewChangestamp(change);
1251 UpdateETag(file);
1253 completion_callback.Run(HTTP_SUCCESS, make_scoped_ptr(
1254 new FileResource(*file)));
1255 base::MessageLoop::current()->PostTask(
1256 FROM_HERE,
1257 base::Bind(&FakeDriveService::NotifyObservers,
1258 weak_ptr_factory_.GetWeakPtr()));
1259 return CancelCallback();
1262 CancelCallback FakeDriveService::MultipartUploadNewFile(
1263 const std::string& content_type,
1264 int64 content_length,
1265 const std::string& parent_resource_id,
1266 const std::string& title,
1267 const base::FilePath& local_file_path,
1268 const UploadNewFileOptions& options,
1269 const FileResourceCallback& callback,
1270 const ProgressCallback& progress_callback) {
1271 CallResumeUpload* const call_resume_upload = new CallResumeUpload();
1272 call_resume_upload->service = weak_ptr_factory_.GetWeakPtr();
1273 call_resume_upload->content_type = content_type;
1274 call_resume_upload->content_length = content_length;
1275 call_resume_upload->local_file_path = local_file_path;
1276 call_resume_upload->callback = callback;
1277 call_resume_upload->progress_callback = progress_callback;
1278 InitiateUploadNewFile(
1279 content_type,
1280 content_length,
1281 parent_resource_id,
1282 title,
1283 options,
1284 base::Bind(&CallResumeUpload::Run, base::Owned(call_resume_upload)));
1285 return CancelCallback();
1288 CancelCallback FakeDriveService::MultipartUploadExistingFile(
1289 const std::string& content_type,
1290 int64 content_length,
1291 const std::string& resource_id,
1292 const base::FilePath& local_file_path,
1293 const UploadExistingFileOptions& options,
1294 const FileResourceCallback& callback,
1295 const ProgressCallback& progress_callback) {
1296 CallResumeUpload* const call_resume_upload = new CallResumeUpload();
1297 call_resume_upload->service = weak_ptr_factory_.GetWeakPtr();
1298 call_resume_upload->content_type = content_type;
1299 call_resume_upload->content_length = content_length;
1300 call_resume_upload->local_file_path = local_file_path;
1301 call_resume_upload->callback = callback;
1302 call_resume_upload->progress_callback = progress_callback;
1303 InitiateUploadExistingFile(
1304 content_type,
1305 content_length,
1306 resource_id,
1307 options,
1308 base::Bind(&CallResumeUpload::Run, base::Owned(call_resume_upload)));
1309 return CancelCallback();
1312 CancelCallback FakeDriveService::AuthorizeApp(
1313 const std::string& resource_id,
1314 const std::string& app_id,
1315 const AuthorizeAppCallback& callback) {
1316 DCHECK(thread_checker_.CalledOnValidThread());
1317 DCHECK(!callback.is_null());
1319 if (entries_.count(resource_id) == 0) {
1320 callback.Run(google_apis::HTTP_NOT_FOUND, GURL());
1321 return CancelCallback();
1324 callback.Run(HTTP_SUCCESS,
1325 GURL(base::StringPrintf(open_url_format_.c_str(),
1326 resource_id.c_str(),
1327 app_id.c_str())));
1328 return CancelCallback();
1331 CancelCallback FakeDriveService::UninstallApp(
1332 const std::string& app_id,
1333 const google_apis::EntryActionCallback& callback) {
1334 DCHECK(thread_checker_.CalledOnValidThread());
1335 DCHECK(!callback.is_null());
1337 if (offline_) {
1338 base::MessageLoop::current()->PostTask(
1339 FROM_HERE,
1340 base::Bind(callback, google_apis::DRIVE_NO_CONNECTION));
1341 return CancelCallback();
1344 // Find app_id from app_info_value_ and delete.
1345 base::ListValue* items = NULL;
1346 if (!app_info_value_->GetList("items", &items)) {
1347 base::MessageLoop::current()->PostTask(
1348 FROM_HERE,
1349 base::Bind(callback, google_apis::HTTP_NOT_FOUND));
1350 return CancelCallback();
1353 for (size_t i = 0; i < items->GetSize(); ++i) {
1354 base::DictionaryValue* item = NULL;
1355 std::string id;
1356 if (items->GetDictionary(i, &item) && item->GetString("id", &id) &&
1357 id == app_id) {
1358 base::MessageLoop::current()->PostTask(
1359 FROM_HERE,
1360 base::Bind(callback,
1361 items->Remove(i, NULL) ? google_apis::HTTP_NO_CONTENT
1362 : google_apis::HTTP_NOT_FOUND));
1363 return CancelCallback();
1367 base::MessageLoop::current()->PostTask(
1368 FROM_HERE,
1369 base::Bind(callback, google_apis::HTTP_NOT_FOUND));
1370 return CancelCallback();
1373 void FakeDriveService::AddNewFile(const std::string& content_type,
1374 const std::string& content_data,
1375 const std::string& parent_resource_id,
1376 const std::string& title,
1377 bool shared_with_me,
1378 const FileResourceCallback& callback) {
1379 AddNewFileWithResourceId("", content_type, content_data, parent_resource_id,
1380 title, shared_with_me, callback);
1383 void FakeDriveService::AddNewFileWithResourceId(
1384 const std::string& resource_id,
1385 const std::string& content_type,
1386 const std::string& content_data,
1387 const std::string& parent_resource_id,
1388 const std::string& title,
1389 bool shared_with_me,
1390 const FileResourceCallback& callback) {
1391 DCHECK(thread_checker_.CalledOnValidThread());
1392 DCHECK(!callback.is_null());
1394 if (offline_) {
1395 base::MessageLoop::current()->PostTask(
1396 FROM_HERE,
1397 base::Bind(callback,
1398 DRIVE_NO_CONNECTION,
1399 base::Passed(scoped_ptr<FileResource>())));
1400 return;
1403 const EntryInfo* new_entry = AddNewEntry(resource_id,
1404 content_type,
1405 content_data,
1406 parent_resource_id,
1407 title,
1408 shared_with_me);
1409 if (!new_entry) {
1410 base::MessageLoop::current()->PostTask(
1411 FROM_HERE,
1412 base::Bind(callback, HTTP_NOT_FOUND,
1413 base::Passed(scoped_ptr<FileResource>())));
1414 return;
1417 base::MessageLoop::current()->PostTask(
1418 FROM_HERE,
1419 base::Bind(callback, HTTP_CREATED,
1420 base::Passed(make_scoped_ptr(
1421 new FileResource(*new_entry->change_resource.file())))));
1422 base::MessageLoop::current()->PostTask(
1423 FROM_HERE,
1424 base::Bind(&FakeDriveService::NotifyObservers,
1425 weak_ptr_factory_.GetWeakPtr()));
1428 CancelCallback FakeDriveService::AddNewDirectoryWithResourceId(
1429 const std::string& resource_id,
1430 const std::string& parent_resource_id,
1431 const std::string& directory_title,
1432 const AddNewDirectoryOptions& options,
1433 const FileResourceCallback& callback) {
1434 DCHECK(thread_checker_.CalledOnValidThread());
1435 DCHECK(!callback.is_null());
1437 if (offline_) {
1438 base::MessageLoop::current()->PostTask(
1439 FROM_HERE,
1440 base::Bind(callback,
1441 DRIVE_NO_CONNECTION,
1442 base::Passed(scoped_ptr<FileResource>())));
1443 return CancelCallback();
1446 const EntryInfo* new_entry = AddNewEntry(resource_id,
1447 util::kDriveFolderMimeType,
1448 "", // content_data
1449 parent_resource_id,
1450 directory_title,
1451 false); // shared_with_me
1452 if (!new_entry) {
1453 base::MessageLoop::current()->PostTask(
1454 FROM_HERE,
1455 base::Bind(callback, HTTP_NOT_FOUND,
1456 base::Passed(scoped_ptr<FileResource>())));
1457 return CancelCallback();
1460 base::MessageLoop::current()->PostTask(
1461 FROM_HERE,
1462 base::Bind(callback, HTTP_CREATED,
1463 base::Passed(make_scoped_ptr(
1464 new FileResource(*new_entry->change_resource.file())))));
1465 base::MessageLoop::current()->PostTask(
1466 FROM_HERE,
1467 base::Bind(&FakeDriveService::NotifyObservers,
1468 weak_ptr_factory_.GetWeakPtr()));
1469 return CancelCallback();
1472 void FakeDriveService::SetLastModifiedTime(
1473 const std::string& resource_id,
1474 const base::Time& last_modified_time,
1475 const FileResourceCallback& callback) {
1476 DCHECK(thread_checker_.CalledOnValidThread());
1477 DCHECK(!callback.is_null());
1479 if (offline_) {
1480 base::MessageLoop::current()->PostTask(
1481 FROM_HERE,
1482 base::Bind(callback,
1483 DRIVE_NO_CONNECTION,
1484 base::Passed(scoped_ptr<FileResource>())));
1485 return;
1488 EntryInfo* entry = FindEntryByResourceId(resource_id);
1489 if (!entry) {
1490 base::MessageLoop::current()->PostTask(
1491 FROM_HERE,
1492 base::Bind(callback, HTTP_NOT_FOUND,
1493 base::Passed(scoped_ptr<FileResource>())));
1494 return;
1497 ChangeResource* change = &entry->change_resource;
1498 FileResource* file = change->mutable_file();
1499 file->set_modified_date(last_modified_time);
1501 base::MessageLoop::current()->PostTask(
1502 FROM_HERE,
1503 base::Bind(callback, HTTP_SUCCESS,
1504 base::Passed(make_scoped_ptr(new FileResource(*file)))));
1507 google_apis::DriveApiErrorCode FakeDriveService::SetUserPermission(
1508 const std::string& resource_id,
1509 google_apis::drive::PermissionRole user_permission) {
1510 DCHECK(thread_checker_.CalledOnValidThread());
1512 EntryInfo* entry = FindEntryByResourceId(resource_id);
1513 if (!entry)
1514 return HTTP_NOT_FOUND;
1516 entry->user_permission = user_permission;
1517 return HTTP_SUCCESS;
1520 void FakeDriveService::AddChangeObserver(ChangeObserver* change_observer) {
1521 change_observers_.AddObserver(change_observer);
1524 void FakeDriveService::RemoveChangeObserver(ChangeObserver* change_observer) {
1525 change_observers_.RemoveObserver(change_observer);
1528 FakeDriveService::EntryInfo* FakeDriveService::FindEntryByResourceId(
1529 const std::string& resource_id) {
1530 DCHECK(thread_checker_.CalledOnValidThread());
1532 EntryInfoMap::iterator it = entries_.find(resource_id);
1533 // Deleted entries don't have FileResource.
1534 return it != entries_.end() && it->second->change_resource.file() ?
1535 it->second : NULL;
1538 std::string FakeDriveService::GetNewResourceId() {
1539 DCHECK(thread_checker_.CalledOnValidThread());
1541 ++resource_id_count_;
1542 return base::StringPrintf("resource_id_%d", resource_id_count_);
1545 void FakeDriveService::UpdateETag(google_apis::FileResource* file) {
1546 file->set_etag(
1547 "etag_" + base::Int64ToString(about_resource_->largest_change_id()));
1550 void FakeDriveService::AddNewChangestamp(google_apis::ChangeResource* change) {
1551 about_resource_->set_largest_change_id(
1552 about_resource_->largest_change_id() + 1);
1553 change->set_change_id(about_resource_->largest_change_id());
1556 const FakeDriveService::EntryInfo* FakeDriveService::AddNewEntry(
1557 const std::string& given_resource_id,
1558 const std::string& content_type,
1559 const std::string& content_data,
1560 const std::string& parent_resource_id,
1561 const std::string& title,
1562 bool shared_with_me) {
1563 DCHECK(thread_checker_.CalledOnValidThread());
1565 if (!parent_resource_id.empty() &&
1566 parent_resource_id != GetRootResourceId() &&
1567 !entries_.count(parent_resource_id)) {
1568 return NULL;
1571 const std::string resource_id =
1572 given_resource_id.empty() ? GetNewResourceId() : given_resource_id;
1573 if (entries_.count(resource_id))
1574 return NULL;
1575 GURL upload_url = GURL("https://xxx/upload/" + resource_id);
1577 scoped_ptr<EntryInfo> new_entry(new EntryInfo);
1578 ChangeResource* new_change = &new_entry->change_resource;
1579 FileResource* new_file = new FileResource;
1580 new_change->set_file(make_scoped_ptr(new_file));
1582 // Set the resource ID and the title
1583 new_change->set_file_id(resource_id);
1584 new_file->set_file_id(resource_id);
1585 new_file->set_title(title);
1586 // Set the contents, size and MD5 for a file.
1587 if (content_type != util::kDriveFolderMimeType &&
1588 !util::IsKnownHostedDocumentMimeType(content_type)) {
1589 new_entry->content_data = content_data;
1590 new_file->set_file_size(content_data.size());
1591 new_file->set_md5_checksum(base::MD5String(content_data));
1594 if (shared_with_me) {
1595 // Set current time to mark the file as shared_with_me.
1596 new_file->set_shared_with_me_date(base::Time::Now());
1599 std::string escaped_resource_id = net::EscapePath(resource_id);
1601 // Set mime type.
1602 new_file->set_mime_type(content_type);
1604 // Set alternate link if needed.
1605 if (content_type == util::kGoogleDocumentMimeType)
1606 new_file->set_alternate_link(GURL("https://document_alternate_link"));
1608 // Set parents.
1609 if (!parent_resource_id.empty()) {
1610 ParentReference parent;
1611 parent.set_file_id(parent_resource_id);
1612 parent.set_parent_link(GetFakeLinkUrl(parent.file_id()));
1613 std::vector<ParentReference> parents;
1614 parents.push_back(parent);
1615 *new_file->mutable_parents() = parents;
1618 new_entry->share_url = net::AppendOrReplaceQueryParameter(
1619 share_url_base_, "name", title);
1621 AddNewChangestamp(new_change);
1622 UpdateETag(new_file);
1624 base::Time published_date =
1625 base::Time() + base::TimeDelta::FromMilliseconds(++published_date_seq_);
1626 new_file->set_created_date(published_date);
1628 EntryInfo* raw_new_entry = new_entry.release();
1629 entries_[resource_id] = raw_new_entry;
1630 return raw_new_entry;
1633 void FakeDriveService::GetChangeListInternal(
1634 int64 start_changestamp,
1635 const std::string& search_query,
1636 const std::string& directory_resource_id,
1637 int start_offset,
1638 int max_results,
1639 int* load_counter,
1640 const ChangeListCallback& callback) {
1641 if (offline_) {
1642 base::MessageLoop::current()->PostTask(
1643 FROM_HERE,
1644 base::Bind(callback,
1645 DRIVE_NO_CONNECTION,
1646 base::Passed(scoped_ptr<ChangeList>())));
1647 return;
1650 // Filter out entries per parameters like |directory_resource_id| and
1651 // |search_query|.
1652 ScopedVector<ChangeResource> entries;
1653 int num_entries_matched = 0;
1654 for (EntryInfoMap::iterator it = entries_.begin(); it != entries_.end();
1655 ++it) {
1656 const ChangeResource& entry = it->second->change_resource;
1657 bool should_exclude = false;
1659 // If |directory_resource_id| is set, exclude the entry if it's not in
1660 // the target directory.
1661 if (!directory_resource_id.empty()) {
1662 // Get the parent resource ID of the entry.
1663 std::string parent_resource_id;
1664 if (entry.file() && !entry.file()->parents().empty())
1665 parent_resource_id = entry.file()->parents()[0].file_id();
1667 if (directory_resource_id != parent_resource_id)
1668 should_exclude = true;
1671 // If |search_query| is set, exclude the entry if it does not contain the
1672 // search query in the title.
1673 if (!should_exclude && !search_query.empty() &&
1674 !EntryMatchWithQuery(entry, search_query)) {
1675 should_exclude = true;
1678 // If |start_changestamp| is set, exclude the entry if the
1679 // changestamp is older than |largest_changestamp|.
1680 // See https://developers.google.com/google-apps/documents-list/
1681 // #retrieving_all_changes_since_a_given_changestamp
1682 if (start_changestamp > 0 && entry.change_id() < start_changestamp)
1683 should_exclude = true;
1685 // If the caller requests other list than change list by specifying
1686 // zero-|start_changestamp|, exclude deleted entry from the result.
1687 const bool deleted = entry.is_deleted() ||
1688 (entry.file() && entry.file()->labels().is_trashed());
1689 if (!start_changestamp && deleted)
1690 should_exclude = true;
1692 // The entry matched the criteria for inclusion.
1693 if (!should_exclude)
1694 ++num_entries_matched;
1696 // If |start_offset| is set, exclude the entry if the entry is before the
1697 // start index. <= instead of < as |num_entries_matched| was
1698 // already incremented.
1699 if (start_offset > 0 && num_entries_matched <= start_offset)
1700 should_exclude = true;
1702 if (!should_exclude) {
1703 scoped_ptr<ChangeResource> entry_copied(new ChangeResource);
1704 entry_copied->set_change_id(entry.change_id());
1705 entry_copied->set_file_id(entry.file_id());
1706 entry_copied->set_deleted(entry.is_deleted());
1707 if (entry.file()) {
1708 entry_copied->set_file(
1709 make_scoped_ptr(new FileResource(*entry.file())));
1711 entry_copied->set_modification_date(entry.modification_date());
1712 entries.push_back(entry_copied.release());
1716 scoped_ptr<ChangeList> change_list(new ChangeList);
1717 if (start_changestamp > 0 && start_offset == 0) {
1718 change_list->set_largest_change_id(about_resource_->largest_change_id());
1721 // If |max_results| is set, trim the entries if the number exceeded the max
1722 // results.
1723 if (max_results > 0 && entries.size() > static_cast<size_t>(max_results)) {
1724 entries.erase(entries.begin() + max_results, entries.end());
1725 // Adds the next URL.
1726 // Here, we embed information which is needed for continuing the
1727 // GetChangeList request in the next invocation into url query
1728 // parameters.
1729 GURL next_url(base::StringPrintf(
1730 "http://localhost/?start-offset=%d&max-results=%d",
1731 start_offset + max_results,
1732 max_results));
1733 if (start_changestamp > 0) {
1734 next_url = net::AppendOrReplaceQueryParameter(
1735 next_url, "changestamp",
1736 base::Int64ToString(start_changestamp).c_str());
1738 if (!search_query.empty()) {
1739 next_url = net::AppendOrReplaceQueryParameter(
1740 next_url, "q", search_query);
1742 if (!directory_resource_id.empty()) {
1743 next_url = net::AppendOrReplaceQueryParameter(
1744 next_url, "parent", directory_resource_id);
1747 change_list->set_next_link(next_url);
1749 *change_list->mutable_items() = entries.Pass();
1751 if (load_counter)
1752 *load_counter += 1;
1753 base::MessageLoop::current()->PostTask(
1754 FROM_HERE,
1755 base::Bind(callback, HTTP_SUCCESS, base::Passed(&change_list)));
1758 GURL FakeDriveService::GetNewUploadSessionUrl() {
1759 return GURL("https://upload_session_url/" +
1760 base::Int64ToString(next_upload_sequence_number_++));
1763 google_apis::CancelCallback FakeDriveService::AddPermission(
1764 const std::string& resource_id,
1765 const std::string& email,
1766 google_apis::drive::PermissionRole role,
1767 const google_apis::EntryActionCallback& callback) {
1768 DCHECK(thread_checker_.CalledOnValidThread());
1769 DCHECK(!callback.is_null());
1771 NOTREACHED();
1772 return CancelCallback();
1775 scoped_ptr<BatchRequestConfiguratorInterface>
1776 FakeDriveService::StartBatchRequest() {
1777 DCHECK(thread_checker_.CalledOnValidThread());
1779 NOTREACHED();
1780 return scoped_ptr<BatchRequestConfiguratorInterface>();
1783 void FakeDriveService::NotifyObservers() {
1784 FOR_EACH_OBSERVER(ChangeObserver, change_observers_, OnNewChangeAvailable());
1787 } // namespace drive