Roll src/third_party/skia d32087a:1052f51
[chromium-blink-merge.git] / components / drive / service / fake_drive_service.cc
blob9a1604cc20ef9e50e1a6100bf72daeb8af0b659e
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 "components/drive/service/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/strings/string_number_conversions.h"
14 #include "base/strings/string_split.h"
15 #include "base/strings/string_tokenizer.h"
16 #include "base/strings/string_util.h"
17 #include "base/strings/stringprintf.h"
18 #include "base/strings/utf_string_conversions.h"
19 #include "base/thread_task_runner_handle.h"
20 #include "base/values.h"
21 #include "components/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::ThreadTaskRunnerHandle::Get()->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()
184 : user_permission(google_apis::drive::PERMISSION_ROLE_OWNER),
185 visibility(google_apis::drive::FILE_VISIBILITY_DEFAULT) {}
187 google_apis::ChangeResource change_resource;
188 GURL share_url;
189 std::string content_data;
191 // Behaves in the same way as "userPermission" described in
192 // https://developers.google.com/drive/v2/reference/files
193 google_apis::drive::PermissionRole user_permission;
195 google_apis::drive::FileVisibility visibility;
198 struct FakeDriveService::UploadSession {
199 std::string content_type;
200 int64 content_length;
201 std::string parent_resource_id;
202 std::string resource_id;
203 std::string etag;
204 std::string title;
206 int64 uploaded_size;
208 UploadSession()
209 : content_length(0),
210 uploaded_size(0) {}
212 UploadSession(
213 std::string content_type,
214 int64 content_length,
215 std::string parent_resource_id,
216 std::string resource_id,
217 std::string etag,
218 std::string title)
219 : content_type(content_type),
220 content_length(content_length),
221 parent_resource_id(parent_resource_id),
222 resource_id(resource_id),
223 etag(etag),
224 title(title),
225 uploaded_size(0) {
229 FakeDriveService::FakeDriveService()
230 : about_resource_(new AboutResource),
231 published_date_seq_(0),
232 next_upload_sequence_number_(0),
233 default_max_results_(0),
234 resource_id_count_(0),
235 file_list_load_count_(0),
236 change_list_load_count_(0),
237 directory_load_count_(0),
238 about_resource_load_count_(0),
239 app_list_load_count_(0),
240 blocked_file_list_load_count_(0),
241 offline_(false),
242 never_return_all_file_list_(false),
243 share_url_base_("https://share_url/"),
244 weak_ptr_factory_(this) {
245 about_resource_->set_largest_change_id(654321);
246 about_resource_->set_quota_bytes_total(9876543210);
247 about_resource_->set_quota_bytes_used_aggregate(6789012345);
248 about_resource_->set_root_folder_id(GetRootResourceId());
251 FakeDriveService::~FakeDriveService() {
252 DCHECK(thread_checker_.CalledOnValidThread());
253 STLDeleteValues(&entries_);
256 bool FakeDriveService::LoadAppListForDriveApi(
257 const std::string& relative_path) {
258 DCHECK(thread_checker_.CalledOnValidThread());
260 // Load JSON data, which must be a dictionary.
261 scoped_ptr<base::Value> value = test_util::LoadJSONFile(relative_path);
262 CHECK_EQ(base::Value::TYPE_DICTIONARY, value->GetType());
263 app_info_value_.reset(
264 static_cast<base::DictionaryValue*>(value.release()));
265 return app_info_value_;
268 void FakeDriveService::AddApp(const std::string& app_id,
269 const std::string& app_name,
270 const std::string& product_id,
271 const std::string& create_url,
272 bool is_removable) {
273 if (app_json_template_.empty()) {
274 base::FilePath path =
275 test_util::GetTestFilePath("drive/applist_app_template.json");
276 CHECK(base::ReadFileToString(path, &app_json_template_));
279 std::string app_json = app_json_template_;
280 base::ReplaceSubstringsAfterOffset(&app_json, 0, "$AppId", app_id);
281 base::ReplaceSubstringsAfterOffset(&app_json, 0, "$AppName", app_name);
282 base::ReplaceSubstringsAfterOffset(&app_json, 0, "$ProductId", product_id);
283 base::ReplaceSubstringsAfterOffset(&app_json, 0, "$CreateUrl", create_url);
284 base::ReplaceSubstringsAfterOffset(
285 &app_json, 0, "$Removable", is_removable ? "true" : "false");
287 JSONStringValueDeserializer json(app_json);
288 std::string error_message;
289 scoped_ptr<base::Value> value(json.Deserialize(NULL, &error_message));
290 CHECK_EQ(base::Value::TYPE_DICTIONARY, value->GetType());
292 base::ListValue* item_list;
293 CHECK(app_info_value_->GetListWithoutPathExpansion("items", &item_list));
294 item_list->Append(value.release());
297 void FakeDriveService::RemoveAppByProductId(const std::string& product_id) {
298 base::ListValue* item_list;
299 CHECK(app_info_value_->GetListWithoutPathExpansion("items", &item_list));
300 for (size_t i = 0; i < item_list->GetSize(); ++i) {
301 base::DictionaryValue* item;
302 CHECK(item_list->GetDictionary(i, &item));
303 const char kKeyProductId[] = "productId";
304 std::string item_product_id;
305 if (item->GetStringWithoutPathExpansion(kKeyProductId, &item_product_id) &&
306 product_id == item_product_id) {
307 item_list->Remove(i, NULL);
308 return;
313 bool FakeDriveService::HasApp(const std::string& app_id) const {
314 base::ListValue* item_list;
315 CHECK(app_info_value_->GetListWithoutPathExpansion("items", &item_list));
316 for (size_t i = 0; i < item_list->GetSize(); ++i) {
317 base::DictionaryValue* item;
318 CHECK(item_list->GetDictionary(i, &item));
319 const char kKeyId[] = "id";
320 std::string item_id;
321 if (item->GetStringWithoutPathExpansion(kKeyId, &item_id) &&
322 item_id == app_id) {
323 return true;
327 return false;
330 void FakeDriveService::SetQuotaValue(int64 used, int64 total) {
331 DCHECK(thread_checker_.CalledOnValidThread());
333 about_resource_->set_quota_bytes_used_aggregate(used);
334 about_resource_->set_quota_bytes_total(total);
337 GURL FakeDriveService::GetFakeLinkUrl(const std::string& resource_id) {
338 return GURL("https://fake_server/" + net::EscapePath(resource_id));
341 void FakeDriveService::Initialize(const std::string& account_id) {
342 DCHECK(thread_checker_.CalledOnValidThread());
345 void FakeDriveService::AddObserver(DriveServiceObserver* observer) {
346 DCHECK(thread_checker_.CalledOnValidThread());
349 void FakeDriveService::RemoveObserver(DriveServiceObserver* observer) {
350 DCHECK(thread_checker_.CalledOnValidThread());
353 bool FakeDriveService::CanSendRequest() const {
354 DCHECK(thread_checker_.CalledOnValidThread());
355 return true;
358 bool FakeDriveService::HasAccessToken() const {
359 DCHECK(thread_checker_.CalledOnValidThread());
360 return true;
363 void FakeDriveService::RequestAccessToken(const AuthStatusCallback& callback) {
364 DCHECK(thread_checker_.CalledOnValidThread());
365 DCHECK(!callback.is_null());
366 callback.Run(google_apis::HTTP_NOT_MODIFIED, "fake_access_token");
369 bool FakeDriveService::HasRefreshToken() const {
370 DCHECK(thread_checker_.CalledOnValidThread());
371 return true;
374 void FakeDriveService::ClearAccessToken() {
375 DCHECK(thread_checker_.CalledOnValidThread());
378 void FakeDriveService::ClearRefreshToken() {
379 DCHECK(thread_checker_.CalledOnValidThread());
382 std::string FakeDriveService::GetRootResourceId() const {
383 return "fake_root";
386 CancelCallback FakeDriveService::GetAllFileList(
387 const FileListCallback& callback) {
388 DCHECK(thread_checker_.CalledOnValidThread());
389 DCHECK(!callback.is_null());
391 if (never_return_all_file_list_) {
392 ++blocked_file_list_load_count_;
393 return CancelCallback();
396 GetChangeListInternal(0, // start changestamp
397 std::string(), // empty search query
398 std::string(), // no directory resource id,
399 0, // start offset
400 default_max_results_,
401 &file_list_load_count_,
402 base::Bind(&FileListCallbackAdapter, callback));
403 return CancelCallback();
406 CancelCallback FakeDriveService::GetFileListInDirectory(
407 const std::string& directory_resource_id,
408 const FileListCallback& callback) {
409 DCHECK(thread_checker_.CalledOnValidThread());
410 DCHECK(!directory_resource_id.empty());
411 DCHECK(!callback.is_null());
413 GetChangeListInternal(0, // start changestamp
414 std::string(), // empty search query
415 directory_resource_id,
416 0, // start offset
417 default_max_results_,
418 &directory_load_count_,
419 base::Bind(&FileListCallbackAdapter, callback));
420 return CancelCallback();
423 CancelCallback FakeDriveService::Search(
424 const std::string& search_query,
425 const FileListCallback& callback) {
426 DCHECK(thread_checker_.CalledOnValidThread());
427 DCHECK(!search_query.empty());
428 DCHECK(!callback.is_null());
430 GetChangeListInternal(0, // start changestamp
431 search_query,
432 std::string(), // no directory resource id,
433 0, // start offset
434 default_max_results_,
435 NULL,
436 base::Bind(&FileListCallbackAdapter, callback));
437 return CancelCallback();
440 CancelCallback FakeDriveService::SearchByTitle(
441 const std::string& title,
442 const std::string& directory_resource_id,
443 const FileListCallback& callback) {
444 DCHECK(thread_checker_.CalledOnValidThread());
445 DCHECK(!title.empty());
446 DCHECK(!callback.is_null());
448 // Note: the search implementation here doesn't support quotation unescape,
449 // so don't escape here.
450 GetChangeListInternal(0, // start changestamp
451 base::StringPrintf("title:'%s'", title.c_str()),
452 directory_resource_id,
453 0, // start offset
454 default_max_results_,
455 NULL,
456 base::Bind(&FileListCallbackAdapter, callback));
457 return CancelCallback();
460 CancelCallback FakeDriveService::GetChangeList(
461 int64 start_changestamp,
462 const ChangeListCallback& callback) {
463 DCHECK(thread_checker_.CalledOnValidThread());
464 DCHECK(!callback.is_null());
466 GetChangeListInternal(start_changestamp,
467 std::string(), // empty search query
468 std::string(), // no directory resource id,
469 0, // start offset
470 default_max_results_,
471 &change_list_load_count_,
472 callback);
473 return CancelCallback();
476 CancelCallback FakeDriveService::GetRemainingChangeList(
477 const GURL& next_link,
478 const ChangeListCallback& callback) {
479 DCHECK(thread_checker_.CalledOnValidThread());
480 DCHECK(!next_link.is_empty());
481 DCHECK(!callback.is_null());
483 // "changestamp", "q", "parent" and "start-offset" are parameters to
484 // implement "paging" of the result on FakeDriveService.
485 // The URL should be the one filled in GetChangeListInternal of the
486 // previous method invocation, so it should start with "http://localhost/?".
487 // See also GetChangeListInternal.
488 DCHECK_EQ(next_link.host(), "localhost");
489 DCHECK_EQ(next_link.path(), "/");
491 int64 start_changestamp = 0;
492 std::string search_query;
493 std::string directory_resource_id;
494 int start_offset = 0;
495 int max_results = default_max_results_;
496 base::StringPairs parameters;
497 if (base::SplitStringIntoKeyValuePairs(
498 next_link.query(), '=', '&', &parameters)) {
499 for (size_t i = 0; i < parameters.size(); ++i) {
500 if (parameters[i].first == "changestamp") {
501 base::StringToInt64(parameters[i].second, &start_changestamp);
502 } else if (parameters[i].first == "q") {
503 search_query =
504 net::UnescapeURLComponent(parameters[i].second,
505 net::UnescapeRule::URL_SPECIAL_CHARS);
506 } else if (parameters[i].first == "parent") {
507 directory_resource_id =
508 net::UnescapeURLComponent(parameters[i].second,
509 net::UnescapeRule::URL_SPECIAL_CHARS);
510 } else if (parameters[i].first == "start-offset") {
511 base::StringToInt(parameters[i].second, &start_offset);
512 } else if (parameters[i].first == "max-results") {
513 base::StringToInt(parameters[i].second, &max_results);
518 GetChangeListInternal(start_changestamp, search_query, directory_resource_id,
519 start_offset, max_results, NULL, callback);
520 return CancelCallback();
523 CancelCallback FakeDriveService::GetRemainingFileList(
524 const GURL& next_link,
525 const FileListCallback& callback) {
526 DCHECK(thread_checker_.CalledOnValidThread());
527 DCHECK(!next_link.is_empty());
528 DCHECK(!callback.is_null());
530 return GetRemainingChangeList(
531 next_link, base::Bind(&FileListCallbackAdapter, callback));
534 CancelCallback FakeDriveService::GetFileResource(
535 const std::string& resource_id,
536 const FileResourceCallback& callback) {
537 DCHECK(thread_checker_.CalledOnValidThread());
538 DCHECK(!callback.is_null());
540 if (offline_) {
541 base::ThreadTaskRunnerHandle::Get()->PostTask(
542 FROM_HERE,
543 base::Bind(callback,
544 DRIVE_NO_CONNECTION,
545 base::Passed(scoped_ptr<FileResource>())));
546 return CancelCallback();
549 EntryInfo* entry = FindEntryByResourceId(resource_id);
550 if (entry && entry->change_resource.file()) {
551 base::ThreadTaskRunnerHandle::Get()->PostTask(
552 FROM_HERE,
553 base::Bind(callback, HTTP_SUCCESS, base::Passed(make_scoped_ptr(
554 new FileResource(*entry->change_resource.file())))));
555 return CancelCallback();
558 base::ThreadTaskRunnerHandle::Get()->PostTask(
559 FROM_HERE,
560 base::Bind(callback, HTTP_NOT_FOUND,
561 base::Passed(scoped_ptr<FileResource>())));
562 return CancelCallback();
565 CancelCallback FakeDriveService::GetShareUrl(
566 const std::string& resource_id,
567 const GURL& /* embed_origin */,
568 const GetShareUrlCallback& callback) {
569 DCHECK(thread_checker_.CalledOnValidThread());
570 DCHECK(!callback.is_null());
572 if (offline_) {
573 base::ThreadTaskRunnerHandle::Get()->PostTask(
574 FROM_HERE,
575 base::Bind(callback,
576 DRIVE_NO_CONNECTION,
577 GURL()));
578 return CancelCallback();
581 EntryInfo* entry = FindEntryByResourceId(resource_id);
582 if (entry) {
583 base::ThreadTaskRunnerHandle::Get()->PostTask(
584 FROM_HERE,
585 base::Bind(callback, HTTP_SUCCESS, entry->share_url));
586 return CancelCallback();
589 base::ThreadTaskRunnerHandle::Get()->PostTask(
590 FROM_HERE,
591 base::Bind(callback, HTTP_NOT_FOUND, GURL()));
592 return CancelCallback();
595 CancelCallback FakeDriveService::GetAboutResource(
596 const AboutResourceCallback& callback) {
597 DCHECK(thread_checker_.CalledOnValidThread());
598 DCHECK(!callback.is_null());
600 if (offline_) {
601 scoped_ptr<AboutResource> null;
602 base::ThreadTaskRunnerHandle::Get()->PostTask(
603 FROM_HERE,
604 base::Bind(callback,
605 DRIVE_NO_CONNECTION, base::Passed(&null)));
606 return CancelCallback();
609 ++about_resource_load_count_;
610 scoped_ptr<AboutResource> about_resource(new AboutResource(*about_resource_));
611 base::ThreadTaskRunnerHandle::Get()->PostTask(
612 FROM_HERE,
613 base::Bind(callback,
614 HTTP_SUCCESS, base::Passed(&about_resource)));
615 return CancelCallback();
618 CancelCallback FakeDriveService::GetAppList(const AppListCallback& callback) {
619 DCHECK(thread_checker_.CalledOnValidThread());
620 DCHECK(!callback.is_null());
621 DCHECK(app_info_value_);
623 if (offline_) {
624 scoped_ptr<AppList> null;
625 base::ThreadTaskRunnerHandle::Get()->PostTask(
626 FROM_HERE,
627 base::Bind(callback,
628 DRIVE_NO_CONNECTION,
629 base::Passed(&null)));
630 return CancelCallback();
633 ++app_list_load_count_;
634 scoped_ptr<AppList> app_list(AppList::CreateFrom(*app_info_value_));
635 base::ThreadTaskRunnerHandle::Get()->PostTask(
636 FROM_HERE,
637 base::Bind(callback, HTTP_SUCCESS, base::Passed(&app_list)));
638 return CancelCallback();
641 CancelCallback FakeDriveService::DeleteResource(
642 const std::string& resource_id,
643 const std::string& etag,
644 const EntryActionCallback& callback) {
645 DCHECK(thread_checker_.CalledOnValidThread());
646 DCHECK(!callback.is_null());
648 if (offline_) {
649 base::ThreadTaskRunnerHandle::Get()->PostTask(
650 FROM_HERE, base::Bind(callback, DRIVE_NO_CONNECTION));
651 return CancelCallback();
654 EntryInfo* entry = FindEntryByResourceId(resource_id);
655 if (!entry) {
656 base::ThreadTaskRunnerHandle::Get()->PostTask(
657 FROM_HERE, base::Bind(callback, HTTP_NOT_FOUND));
658 return CancelCallback();
661 ChangeResource* change = &entry->change_resource;
662 const FileResource* file = change->file();
663 if (change->is_deleted()) {
664 base::ThreadTaskRunnerHandle::Get()->PostTask(
665 FROM_HERE, base::Bind(callback, HTTP_NOT_FOUND));
666 return CancelCallback();
669 if (!etag.empty() && etag != file->etag()) {
670 base::ThreadTaskRunnerHandle::Get()->PostTask(
671 FROM_HERE, base::Bind(callback, HTTP_PRECONDITION));
672 return CancelCallback();
675 if (entry->user_permission != google_apis::drive::PERMISSION_ROLE_OWNER) {
676 base::ThreadTaskRunnerHandle::Get()->PostTask(
677 FROM_HERE, base::Bind(callback, HTTP_FORBIDDEN));
678 return CancelCallback();
681 change->set_deleted(true);
682 AddNewChangestamp(change);
683 change->set_file(scoped_ptr<FileResource>());
684 base::ThreadTaskRunnerHandle::Get()->PostTask(
685 FROM_HERE, base::Bind(callback, HTTP_NO_CONTENT));
686 base::ThreadTaskRunnerHandle::Get()->PostTask(
687 FROM_HERE,
688 base::Bind(&FakeDriveService::NotifyObservers,
689 weak_ptr_factory_.GetWeakPtr()));
690 return CancelCallback();
693 CancelCallback FakeDriveService::TrashResource(
694 const std::string& resource_id,
695 const EntryActionCallback& callback) {
696 DCHECK(thread_checker_.CalledOnValidThread());
697 DCHECK(!callback.is_null());
699 if (offline_) {
700 base::ThreadTaskRunnerHandle::Get()->PostTask(
701 FROM_HERE, base::Bind(callback, DRIVE_NO_CONNECTION));
702 return CancelCallback();
705 EntryInfo* entry = FindEntryByResourceId(resource_id);
706 if (!entry) {
707 base::ThreadTaskRunnerHandle::Get()->PostTask(
708 FROM_HERE, base::Bind(callback, HTTP_NOT_FOUND));
709 return CancelCallback();
712 ChangeResource* change = &entry->change_resource;
713 FileResource* file = change->mutable_file();
714 if (change->is_deleted() || file->labels().is_trashed()) {
715 base::ThreadTaskRunnerHandle::Get()->PostTask(
716 FROM_HERE, base::Bind(callback, HTTP_NOT_FOUND));
717 return CancelCallback();
720 if (entry->user_permission != google_apis::drive::PERMISSION_ROLE_OWNER) {
721 base::ThreadTaskRunnerHandle::Get()->PostTask(
722 FROM_HERE, base::Bind(callback, HTTP_FORBIDDEN));
723 return CancelCallback();
726 file->mutable_labels()->set_trashed(true);
727 AddNewChangestamp(change);
728 base::ThreadTaskRunnerHandle::Get()->PostTask(
729 FROM_HERE, base::Bind(callback, HTTP_SUCCESS));
730 base::ThreadTaskRunnerHandle::Get()->PostTask(
731 FROM_HERE,
732 base::Bind(&FakeDriveService::NotifyObservers,
733 weak_ptr_factory_.GetWeakPtr()));
734 return CancelCallback();
737 CancelCallback FakeDriveService::DownloadFile(
738 const base::FilePath& local_cache_path,
739 const std::string& resource_id,
740 const DownloadActionCallback& download_action_callback,
741 const GetContentCallback& get_content_callback,
742 const ProgressCallback& progress_callback) {
743 DCHECK(thread_checker_.CalledOnValidThread());
744 DCHECK(!download_action_callback.is_null());
746 if (offline_) {
747 base::ThreadTaskRunnerHandle::Get()->PostTask(
748 FROM_HERE,
749 base::Bind(download_action_callback,
750 DRIVE_NO_CONNECTION,
751 base::FilePath()));
752 return CancelCallback();
755 EntryInfo* entry = FindEntryByResourceId(resource_id);
756 if (!entry || entry->change_resource.file()->IsHostedDocument()) {
757 base::ThreadTaskRunnerHandle::Get()->PostTask(
758 FROM_HERE,
759 base::Bind(download_action_callback, HTTP_NOT_FOUND, base::FilePath()));
760 return CancelCallback();
763 const FileResource* file = entry->change_resource.file();
764 const std::string& content_data = entry->content_data;
765 int64 file_size = file->file_size();
766 DCHECK_EQ(static_cast<size_t>(file_size), content_data.size());
768 if (!get_content_callback.is_null()) {
769 const int64 kBlockSize = 5;
770 for (int64 i = 0; i < file_size; i += kBlockSize) {
771 const int64 size = std::min(kBlockSize, file_size - i);
772 scoped_ptr<std::string> content_for_callback(
773 new std::string(content_data.substr(i, size)));
774 base::ThreadTaskRunnerHandle::Get()->PostTask(
775 FROM_HERE,
776 base::Bind(get_content_callback, HTTP_SUCCESS,
777 base::Passed(&content_for_callback)));
781 if (!test_util::WriteStringToFile(local_cache_path, content_data)) {
782 // Failed to write the content.
783 base::ThreadTaskRunnerHandle::Get()->PostTask(
784 FROM_HERE,
785 base::Bind(download_action_callback,
786 DRIVE_FILE_ERROR, base::FilePath()));
787 return CancelCallback();
790 if (!progress_callback.is_null()) {
791 // See also the comment in ResumeUpload(). For testing that clients
792 // can handle the case progress_callback is called multiple times,
793 // here we invoke the callback twice.
794 base::ThreadTaskRunnerHandle::Get()->PostTask(
795 FROM_HERE,
796 base::Bind(progress_callback, file_size / 2, file_size));
797 base::ThreadTaskRunnerHandle::Get()->PostTask(
798 FROM_HERE,
799 base::Bind(progress_callback, file_size, file_size));
801 base::ThreadTaskRunnerHandle::Get()->PostTask(
802 FROM_HERE,
803 base::Bind(download_action_callback,
804 HTTP_SUCCESS,
805 local_cache_path));
806 return CancelCallback();
809 CancelCallback FakeDriveService::CopyResource(
810 const std::string& resource_id,
811 const std::string& in_parent_resource_id,
812 const std::string& new_title,
813 const base::Time& last_modified,
814 const FileResourceCallback& callback) {
815 DCHECK(thread_checker_.CalledOnValidThread());
816 DCHECK(!callback.is_null());
818 if (offline_) {
819 base::ThreadTaskRunnerHandle::Get()->PostTask(
820 FROM_HERE,
821 base::Bind(callback,
822 DRIVE_NO_CONNECTION,
823 base::Passed(scoped_ptr<FileResource>())));
824 return CancelCallback();
827 const std::string& parent_resource_id = in_parent_resource_id.empty() ?
828 GetRootResourceId() : in_parent_resource_id;
830 EntryInfo* entry = FindEntryByResourceId(resource_id);
831 if (!entry) {
832 base::ThreadTaskRunnerHandle::Get()->PostTask(
833 FROM_HERE,
834 base::Bind(callback, HTTP_NOT_FOUND,
835 base::Passed(scoped_ptr<FileResource>())));
836 return CancelCallback();
839 // Make a copy and set the new resource ID and the new title.
840 scoped_ptr<EntryInfo> copied_entry(new EntryInfo);
841 copied_entry->content_data = entry->content_data;
842 copied_entry->share_url = entry->share_url;
843 copied_entry->change_resource.set_file(
844 make_scoped_ptr(new FileResource(*entry->change_resource.file())));
846 ChangeResource* new_change = &copied_entry->change_resource;
847 FileResource* new_file = new_change->mutable_file();
848 const std::string new_resource_id = GetNewResourceId();
849 new_change->set_file_id(new_resource_id);
850 new_file->set_file_id(new_resource_id);
851 new_file->set_title(new_title);
853 ParentReference parent;
854 parent.set_file_id(parent_resource_id);
855 parent.set_parent_link(GetFakeLinkUrl(parent_resource_id));
856 std::vector<ParentReference> parents;
857 parents.push_back(parent);
858 *new_file->mutable_parents() = parents;
860 if (!last_modified.is_null())
861 new_file->set_modified_date(last_modified);
863 AddNewChangestamp(new_change);
864 UpdateETag(new_file);
866 // Add the new entry to the map.
867 entries_[new_resource_id] = copied_entry.release();
869 base::ThreadTaskRunnerHandle::Get()->PostTask(
870 FROM_HERE,
871 base::Bind(callback,
872 HTTP_SUCCESS,
873 base::Passed(make_scoped_ptr(new FileResource(*new_file)))));
874 base::ThreadTaskRunnerHandle::Get()->PostTask(
875 FROM_HERE,
876 base::Bind(&FakeDriveService::NotifyObservers,
877 weak_ptr_factory_.GetWeakPtr()));
878 return CancelCallback();
881 CancelCallback FakeDriveService::UpdateResource(
882 const std::string& resource_id,
883 const std::string& parent_resource_id,
884 const std::string& new_title,
885 const base::Time& last_modified,
886 const base::Time& last_viewed_by_me,
887 const google_apis::drive::Properties& properties,
888 const google_apis::FileResourceCallback& callback) {
889 DCHECK(thread_checker_.CalledOnValidThread());
890 DCHECK(!callback.is_null());
892 if (offline_) {
893 base::ThreadTaskRunnerHandle::Get()->PostTask(
894 FROM_HERE, base::Bind(callback, DRIVE_NO_CONNECTION,
895 base::Passed(scoped_ptr<FileResource>())));
896 return CancelCallback();
899 EntryInfo* entry = FindEntryByResourceId(resource_id);
900 if (!entry) {
901 base::ThreadTaskRunnerHandle::Get()->PostTask(
902 FROM_HERE,
903 base::Bind(callback, HTTP_NOT_FOUND,
904 base::Passed(scoped_ptr<FileResource>())));
905 return CancelCallback();
908 if (!UserHasWriteAccess(entry->user_permission)) {
909 base::ThreadTaskRunnerHandle::Get()->PostTask(
910 FROM_HERE,
911 base::Bind(callback, HTTP_FORBIDDEN,
912 base::Passed(scoped_ptr<FileResource>())));
913 return CancelCallback();
916 ChangeResource* change = &entry->change_resource;
917 FileResource* file = change->mutable_file();
919 if (!new_title.empty())
920 file->set_title(new_title);
922 // Set parent if necessary.
923 if (!parent_resource_id.empty()) {
924 ParentReference parent;
925 parent.set_file_id(parent_resource_id);
926 parent.set_parent_link(GetFakeLinkUrl(parent_resource_id));
928 std::vector<ParentReference> parents;
929 parents.push_back(parent);
930 *file->mutable_parents() = parents;
933 if (!last_modified.is_null())
934 file->set_modified_date(last_modified);
936 if (!last_viewed_by_me.is_null())
937 file->set_last_viewed_by_me_date(last_viewed_by_me);
939 AddNewChangestamp(change);
940 UpdateETag(file);
942 base::ThreadTaskRunnerHandle::Get()->PostTask(
943 FROM_HERE,
944 base::Bind(callback, HTTP_SUCCESS,
945 base::Passed(make_scoped_ptr(new FileResource(*file)))));
946 base::ThreadTaskRunnerHandle::Get()->PostTask(
947 FROM_HERE,
948 base::Bind(&FakeDriveService::NotifyObservers,
949 weak_ptr_factory_.GetWeakPtr()));
950 return CancelCallback();
953 CancelCallback FakeDriveService::AddResourceToDirectory(
954 const std::string& parent_resource_id,
955 const std::string& resource_id,
956 const EntryActionCallback& callback) {
957 DCHECK(thread_checker_.CalledOnValidThread());
958 DCHECK(!callback.is_null());
960 if (offline_) {
961 base::ThreadTaskRunnerHandle::Get()->PostTask(
962 FROM_HERE, base::Bind(callback, DRIVE_NO_CONNECTION));
963 return CancelCallback();
966 EntryInfo* entry = FindEntryByResourceId(resource_id);
967 if (!entry) {
968 base::ThreadTaskRunnerHandle::Get()->PostTask(
969 FROM_HERE, base::Bind(callback, HTTP_NOT_FOUND));
970 return CancelCallback();
973 ChangeResource* change = &entry->change_resource;
974 // On the real Drive server, resources do not necessary shape a tree
975 // structure. That is, each resource can have multiple parent.
976 // We mimic the behavior here; AddResourceToDirectoy just adds
977 // one more parent, not overwriting old ones.
978 ParentReference parent;
979 parent.set_file_id(parent_resource_id);
980 parent.set_parent_link(GetFakeLinkUrl(parent_resource_id));
981 change->mutable_file()->mutable_parents()->push_back(parent);
983 AddNewChangestamp(change);
984 base::ThreadTaskRunnerHandle::Get()->PostTask(
985 FROM_HERE, base::Bind(callback, HTTP_SUCCESS));
986 base::ThreadTaskRunnerHandle::Get()->PostTask(
987 FROM_HERE,
988 base::Bind(&FakeDriveService::NotifyObservers,
989 weak_ptr_factory_.GetWeakPtr()));
990 return CancelCallback();
993 CancelCallback FakeDriveService::RemoveResourceFromDirectory(
994 const std::string& parent_resource_id,
995 const std::string& resource_id,
996 const EntryActionCallback& callback) {
997 DCHECK(thread_checker_.CalledOnValidThread());
998 DCHECK(!callback.is_null());
1000 if (offline_) {
1001 base::ThreadTaskRunnerHandle::Get()->PostTask(
1002 FROM_HERE, base::Bind(callback, DRIVE_NO_CONNECTION));
1003 return CancelCallback();
1006 EntryInfo* entry = FindEntryByResourceId(resource_id);
1007 if (!entry) {
1008 base::ThreadTaskRunnerHandle::Get()->PostTask(
1009 FROM_HERE, base::Bind(callback, HTTP_NOT_FOUND));
1010 return CancelCallback();
1013 ChangeResource* change = &entry->change_resource;
1014 FileResource* file = change->mutable_file();
1015 std::vector<ParentReference>* parents = file->mutable_parents();
1016 for (size_t i = 0; i < parents->size(); ++i) {
1017 if ((*parents)[i].file_id() == parent_resource_id) {
1018 parents->erase(parents->begin() + i);
1019 AddNewChangestamp(change);
1020 base::ThreadTaskRunnerHandle::Get()->PostTask(
1021 FROM_HERE, base::Bind(callback, HTTP_NO_CONTENT));
1022 base::ThreadTaskRunnerHandle::Get()->PostTask(
1023 FROM_HERE,
1024 base::Bind(&FakeDriveService::NotifyObservers,
1025 weak_ptr_factory_.GetWeakPtr()));
1026 return CancelCallback();
1030 base::ThreadTaskRunnerHandle::Get()->PostTask(
1031 FROM_HERE, base::Bind(callback, HTTP_NOT_FOUND));
1032 return CancelCallback();
1035 CancelCallback FakeDriveService::AddNewDirectory(
1036 const std::string& parent_resource_id,
1037 const std::string& directory_title,
1038 const AddNewDirectoryOptions& options,
1039 const FileResourceCallback& callback) {
1040 return AddNewDirectoryWithResourceId(
1042 parent_resource_id.empty() ? GetRootResourceId() : parent_resource_id,
1043 directory_title,
1044 options,
1045 callback);
1048 CancelCallback FakeDriveService::InitiateUploadNewFile(
1049 const std::string& content_type,
1050 int64 content_length,
1051 const std::string& parent_resource_id,
1052 const std::string& title,
1053 const UploadNewFileOptions& options,
1054 const InitiateUploadCallback& callback) {
1055 DCHECK(thread_checker_.CalledOnValidThread());
1056 DCHECK(!callback.is_null());
1058 if (offline_) {
1059 base::ThreadTaskRunnerHandle::Get()->PostTask(
1060 FROM_HERE,
1061 base::Bind(callback, DRIVE_NO_CONNECTION, GURL()));
1062 return CancelCallback();
1065 if (parent_resource_id != GetRootResourceId() &&
1066 !entries_.count(parent_resource_id)) {
1067 base::ThreadTaskRunnerHandle::Get()->PostTask(
1068 FROM_HERE,
1069 base::Bind(callback, HTTP_NOT_FOUND, GURL()));
1070 return CancelCallback();
1073 GURL session_url = GetNewUploadSessionUrl();
1074 upload_sessions_[session_url] =
1075 UploadSession(content_type, content_length,
1076 parent_resource_id,
1077 "", // resource_id
1078 "", // etag
1079 title);
1081 base::ThreadTaskRunnerHandle::Get()->PostTask(
1082 FROM_HERE,
1083 base::Bind(callback, HTTP_SUCCESS, session_url));
1084 return CancelCallback();
1087 CancelCallback FakeDriveService::InitiateUploadExistingFile(
1088 const std::string& content_type,
1089 int64 content_length,
1090 const std::string& resource_id,
1091 const UploadExistingFileOptions& options,
1092 const InitiateUploadCallback& callback) {
1093 DCHECK(thread_checker_.CalledOnValidThread());
1094 DCHECK(!callback.is_null());
1096 if (offline_) {
1097 base::ThreadTaskRunnerHandle::Get()->PostTask(
1098 FROM_HERE,
1099 base::Bind(callback, DRIVE_NO_CONNECTION, GURL()));
1100 return CancelCallback();
1103 EntryInfo* entry = FindEntryByResourceId(resource_id);
1104 if (!entry) {
1105 base::ThreadTaskRunnerHandle::Get()->PostTask(
1106 FROM_HERE,
1107 base::Bind(callback, HTTP_NOT_FOUND, GURL()));
1108 return CancelCallback();
1111 if (!UserHasWriteAccess(entry->user_permission)) {
1112 base::ThreadTaskRunnerHandle::Get()->PostTask(
1113 FROM_HERE,
1114 base::Bind(callback, HTTP_FORBIDDEN, GURL()));
1115 return CancelCallback();
1118 FileResource* file = entry->change_resource.mutable_file();
1119 if (!options.etag.empty() && options.etag != file->etag()) {
1120 base::ThreadTaskRunnerHandle::Get()->PostTask(
1121 FROM_HERE,
1122 base::Bind(callback, HTTP_PRECONDITION, GURL()));
1123 return CancelCallback();
1125 // TODO(hashimoto): Update |file|'s metadata with |options|.
1127 GURL session_url = GetNewUploadSessionUrl();
1128 upload_sessions_[session_url] =
1129 UploadSession(content_type, content_length,
1130 "", // parent_resource_id
1131 resource_id,
1132 file->etag(),
1133 "" /* title */);
1135 base::ThreadTaskRunnerHandle::Get()->PostTask(
1136 FROM_HERE,
1137 base::Bind(callback, HTTP_SUCCESS, session_url));
1138 return CancelCallback();
1141 CancelCallback FakeDriveService::GetUploadStatus(
1142 const GURL& upload_url,
1143 int64 content_length,
1144 const UploadRangeCallback& callback) {
1145 DCHECK(thread_checker_.CalledOnValidThread());
1146 DCHECK(!callback.is_null());
1147 return CancelCallback();
1150 CancelCallback FakeDriveService::ResumeUpload(
1151 const GURL& upload_url,
1152 int64 start_position,
1153 int64 end_position,
1154 int64 content_length,
1155 const std::string& content_type,
1156 const base::FilePath& local_file_path,
1157 const UploadRangeCallback& callback,
1158 const ProgressCallback& progress_callback) {
1159 DCHECK(thread_checker_.CalledOnValidThread());
1160 DCHECK(!callback.is_null());
1162 FileResourceCallback completion_callback
1163 = base::Bind(&ScheduleUploadRangeCallback,
1164 callback, start_position, end_position);
1166 if (offline_) {
1167 completion_callback.Run(DRIVE_NO_CONNECTION, scoped_ptr<FileResource>());
1168 return CancelCallback();
1171 if (!upload_sessions_.count(upload_url)) {
1172 completion_callback.Run(HTTP_NOT_FOUND, scoped_ptr<FileResource>());
1173 return CancelCallback();
1176 UploadSession* session = &upload_sessions_[upload_url];
1178 // Chunks are required to be sent in such a ways that they fill from the start
1179 // of the not-yet-uploaded part with no gaps nor overlaps.
1180 if (session->uploaded_size != start_position) {
1181 completion_callback.Run(HTTP_BAD_REQUEST, scoped_ptr<FileResource>());
1182 return CancelCallback();
1185 if (!progress_callback.is_null()) {
1186 // In the real GDataWapi/Drive DriveService, progress is reported in
1187 // nondeterministic timing. In this fake implementation, we choose to call
1188 // it twice per one ResumeUpload. This is for making sure that client code
1189 // works fine even if the callback is invoked more than once; it is the
1190 // crucial difference of the progress callback from others.
1191 // Note that progress is notified in the relative offset in each chunk.
1192 const int64 chunk_size = end_position - start_position;
1193 base::ThreadTaskRunnerHandle::Get()->PostTask(
1194 FROM_HERE, base::Bind(progress_callback, chunk_size / 2, chunk_size));
1195 base::ThreadTaskRunnerHandle::Get()->PostTask(
1196 FROM_HERE, base::Bind(progress_callback, chunk_size, chunk_size));
1199 if (content_length != end_position) {
1200 session->uploaded_size = end_position;
1201 completion_callback.Run(HTTP_RESUME_INCOMPLETE, scoped_ptr<FileResource>());
1202 return CancelCallback();
1205 std::string content_data;
1206 if (!base::ReadFileToString(local_file_path, &content_data)) {
1207 session->uploaded_size = end_position;
1208 completion_callback.Run(DRIVE_FILE_ERROR, scoped_ptr<FileResource>());
1209 return CancelCallback();
1211 session->uploaded_size = end_position;
1213 // |resource_id| is empty if the upload is for new file.
1214 if (session->resource_id.empty()) {
1215 DCHECK(!session->parent_resource_id.empty());
1216 DCHECK(!session->title.empty());
1217 const EntryInfo* new_entry = AddNewEntry(
1218 "", // auto generate resource id.
1219 session->content_type,
1220 content_data,
1221 session->parent_resource_id,
1222 session->title,
1223 false); // shared_with_me
1224 if (!new_entry) {
1225 completion_callback.Run(HTTP_NOT_FOUND, scoped_ptr<FileResource>());
1226 return CancelCallback();
1229 completion_callback.Run(HTTP_CREATED, make_scoped_ptr(
1230 new FileResource(*new_entry->change_resource.file())));
1231 base::ThreadTaskRunnerHandle::Get()->PostTask(
1232 FROM_HERE,
1233 base::Bind(&FakeDriveService::NotifyObservers,
1234 weak_ptr_factory_.GetWeakPtr()));
1235 return CancelCallback();
1238 EntryInfo* entry = FindEntryByResourceId(session->resource_id);
1239 if (!entry) {
1240 completion_callback.Run(HTTP_NOT_FOUND, scoped_ptr<FileResource>());
1241 return CancelCallback();
1244 ChangeResource* change = &entry->change_resource;
1245 FileResource* file = change->mutable_file();
1246 if (file->etag().empty() || session->etag != file->etag()) {
1247 completion_callback.Run(HTTP_PRECONDITION, scoped_ptr<FileResource>());
1248 return CancelCallback();
1251 file->set_md5_checksum(base::MD5String(content_data));
1252 entry->content_data = content_data;
1253 file->set_file_size(end_position);
1254 AddNewChangestamp(change);
1255 UpdateETag(file);
1257 completion_callback.Run(HTTP_SUCCESS, make_scoped_ptr(
1258 new FileResource(*file)));
1259 base::ThreadTaskRunnerHandle::Get()->PostTask(
1260 FROM_HERE,
1261 base::Bind(&FakeDriveService::NotifyObservers,
1262 weak_ptr_factory_.GetWeakPtr()));
1263 return CancelCallback();
1266 CancelCallback FakeDriveService::MultipartUploadNewFile(
1267 const std::string& content_type,
1268 int64 content_length,
1269 const std::string& parent_resource_id,
1270 const std::string& title,
1271 const base::FilePath& local_file_path,
1272 const UploadNewFileOptions& options,
1273 const FileResourceCallback& callback,
1274 const ProgressCallback& progress_callback) {
1275 CallResumeUpload* const call_resume_upload = new CallResumeUpload();
1276 call_resume_upload->service = weak_ptr_factory_.GetWeakPtr();
1277 call_resume_upload->content_type = content_type;
1278 call_resume_upload->content_length = content_length;
1279 call_resume_upload->local_file_path = local_file_path;
1280 call_resume_upload->callback = callback;
1281 call_resume_upload->progress_callback = progress_callback;
1282 InitiateUploadNewFile(
1283 content_type,
1284 content_length,
1285 parent_resource_id,
1286 title,
1287 options,
1288 base::Bind(&CallResumeUpload::Run, base::Owned(call_resume_upload)));
1289 return CancelCallback();
1292 CancelCallback FakeDriveService::MultipartUploadExistingFile(
1293 const std::string& content_type,
1294 int64 content_length,
1295 const std::string& resource_id,
1296 const base::FilePath& local_file_path,
1297 const UploadExistingFileOptions& options,
1298 const FileResourceCallback& callback,
1299 const ProgressCallback& progress_callback) {
1300 CallResumeUpload* const call_resume_upload = new CallResumeUpload();
1301 call_resume_upload->service = weak_ptr_factory_.GetWeakPtr();
1302 call_resume_upload->content_type = content_type;
1303 call_resume_upload->content_length = content_length;
1304 call_resume_upload->local_file_path = local_file_path;
1305 call_resume_upload->callback = callback;
1306 call_resume_upload->progress_callback = progress_callback;
1307 InitiateUploadExistingFile(
1308 content_type,
1309 content_length,
1310 resource_id,
1311 options,
1312 base::Bind(&CallResumeUpload::Run, base::Owned(call_resume_upload)));
1313 return CancelCallback();
1316 CancelCallback FakeDriveService::AuthorizeApp(
1317 const std::string& resource_id,
1318 const std::string& app_id,
1319 const AuthorizeAppCallback& callback) {
1320 DCHECK(thread_checker_.CalledOnValidThread());
1321 DCHECK(!callback.is_null());
1323 if (entries_.count(resource_id) == 0) {
1324 callback.Run(google_apis::HTTP_NOT_FOUND, GURL());
1325 return CancelCallback();
1328 callback.Run(HTTP_SUCCESS,
1329 GURL(base::StringPrintf(open_url_format_.c_str(),
1330 resource_id.c_str(),
1331 app_id.c_str())));
1332 return CancelCallback();
1335 CancelCallback FakeDriveService::UninstallApp(
1336 const std::string& app_id,
1337 const google_apis::EntryActionCallback& callback) {
1338 DCHECK(thread_checker_.CalledOnValidThread());
1339 DCHECK(!callback.is_null());
1341 if (offline_) {
1342 base::ThreadTaskRunnerHandle::Get()->PostTask(
1343 FROM_HERE,
1344 base::Bind(callback, google_apis::DRIVE_NO_CONNECTION));
1345 return CancelCallback();
1348 // Find app_id from app_info_value_ and delete.
1349 base::ListValue* items = NULL;
1350 if (!app_info_value_->GetList("items", &items)) {
1351 base::ThreadTaskRunnerHandle::Get()->PostTask(
1352 FROM_HERE,
1353 base::Bind(callback, google_apis::HTTP_NOT_FOUND));
1354 return CancelCallback();
1357 for (size_t i = 0; i < items->GetSize(); ++i) {
1358 base::DictionaryValue* item = NULL;
1359 std::string id;
1360 if (items->GetDictionary(i, &item) && item->GetString("id", &id) &&
1361 id == app_id) {
1362 base::ThreadTaskRunnerHandle::Get()->PostTask(
1363 FROM_HERE,
1364 base::Bind(callback,
1365 items->Remove(i, NULL) ? google_apis::HTTP_NO_CONTENT
1366 : google_apis::HTTP_NOT_FOUND));
1367 return CancelCallback();
1371 base::ThreadTaskRunnerHandle::Get()->PostTask(
1372 FROM_HERE,
1373 base::Bind(callback, google_apis::HTTP_NOT_FOUND));
1374 return CancelCallback();
1377 void FakeDriveService::AddNewFile(const std::string& content_type,
1378 const std::string& content_data,
1379 const std::string& parent_resource_id,
1380 const std::string& title,
1381 bool shared_with_me,
1382 const FileResourceCallback& callback) {
1383 AddNewFileWithResourceId("", content_type, content_data, parent_resource_id,
1384 title, shared_with_me, callback);
1387 void FakeDriveService::AddNewFileWithResourceId(
1388 const std::string& resource_id,
1389 const std::string& content_type,
1390 const std::string& content_data,
1391 const std::string& parent_resource_id,
1392 const std::string& title,
1393 bool shared_with_me,
1394 const FileResourceCallback& callback) {
1395 DCHECK(thread_checker_.CalledOnValidThread());
1396 DCHECK(!callback.is_null());
1398 if (offline_) {
1399 base::ThreadTaskRunnerHandle::Get()->PostTask(
1400 FROM_HERE,
1401 base::Bind(callback,
1402 DRIVE_NO_CONNECTION,
1403 base::Passed(scoped_ptr<FileResource>())));
1404 return;
1407 const EntryInfo* new_entry = AddNewEntry(resource_id,
1408 content_type,
1409 content_data,
1410 parent_resource_id,
1411 title,
1412 shared_with_me);
1413 if (!new_entry) {
1414 base::ThreadTaskRunnerHandle::Get()->PostTask(
1415 FROM_HERE,
1416 base::Bind(callback, HTTP_NOT_FOUND,
1417 base::Passed(scoped_ptr<FileResource>())));
1418 return;
1421 base::ThreadTaskRunnerHandle::Get()->PostTask(
1422 FROM_HERE,
1423 base::Bind(callback, HTTP_CREATED,
1424 base::Passed(make_scoped_ptr(
1425 new FileResource(*new_entry->change_resource.file())))));
1426 base::ThreadTaskRunnerHandle::Get()->PostTask(
1427 FROM_HERE,
1428 base::Bind(&FakeDriveService::NotifyObservers,
1429 weak_ptr_factory_.GetWeakPtr()));
1432 CancelCallback FakeDriveService::AddNewDirectoryWithResourceId(
1433 const std::string& resource_id,
1434 const std::string& parent_resource_id,
1435 const std::string& directory_title,
1436 const AddNewDirectoryOptions& options,
1437 const FileResourceCallback& callback) {
1438 DCHECK(thread_checker_.CalledOnValidThread());
1439 DCHECK(!callback.is_null());
1441 if (offline_) {
1442 base::ThreadTaskRunnerHandle::Get()->PostTask(
1443 FROM_HERE,
1444 base::Bind(callback,
1445 DRIVE_NO_CONNECTION,
1446 base::Passed(scoped_ptr<FileResource>())));
1447 return CancelCallback();
1450 const EntryInfo* new_entry = AddNewEntry(resource_id,
1451 util::kDriveFolderMimeType,
1452 "", // content_data
1453 parent_resource_id,
1454 directory_title,
1455 false); // shared_with_me
1456 if (!new_entry) {
1457 base::ThreadTaskRunnerHandle::Get()->PostTask(
1458 FROM_HERE,
1459 base::Bind(callback, HTTP_NOT_FOUND,
1460 base::Passed(scoped_ptr<FileResource>())));
1461 return CancelCallback();
1464 const google_apis::DriveApiErrorCode result =
1465 SetFileVisibility(new_entry->change_resource.file_id(),
1466 options.visibility);
1467 DCHECK_EQ(HTTP_SUCCESS, result);
1469 base::ThreadTaskRunnerHandle::Get()->PostTask(
1470 FROM_HERE,
1471 base::Bind(callback, HTTP_CREATED,
1472 base::Passed(make_scoped_ptr(
1473 new FileResource(*new_entry->change_resource.file())))));
1474 base::ThreadTaskRunnerHandle::Get()->PostTask(
1475 FROM_HERE,
1476 base::Bind(&FakeDriveService::NotifyObservers,
1477 weak_ptr_factory_.GetWeakPtr()));
1478 return CancelCallback();
1481 void FakeDriveService::SetLastModifiedTime(
1482 const std::string& resource_id,
1483 const base::Time& last_modified_time,
1484 const FileResourceCallback& callback) {
1485 DCHECK(thread_checker_.CalledOnValidThread());
1486 DCHECK(!callback.is_null());
1488 if (offline_) {
1489 base::ThreadTaskRunnerHandle::Get()->PostTask(
1490 FROM_HERE,
1491 base::Bind(callback,
1492 DRIVE_NO_CONNECTION,
1493 base::Passed(scoped_ptr<FileResource>())));
1494 return;
1497 EntryInfo* entry = FindEntryByResourceId(resource_id);
1498 if (!entry) {
1499 base::ThreadTaskRunnerHandle::Get()->PostTask(
1500 FROM_HERE,
1501 base::Bind(callback, HTTP_NOT_FOUND,
1502 base::Passed(scoped_ptr<FileResource>())));
1503 return;
1506 ChangeResource* change = &entry->change_resource;
1507 FileResource* file = change->mutable_file();
1508 file->set_modified_date(last_modified_time);
1510 base::ThreadTaskRunnerHandle::Get()->PostTask(
1511 FROM_HERE,
1512 base::Bind(callback, HTTP_SUCCESS,
1513 base::Passed(make_scoped_ptr(new FileResource(*file)))));
1516 google_apis::DriveApiErrorCode FakeDriveService::SetUserPermission(
1517 const std::string& resource_id,
1518 google_apis::drive::PermissionRole user_permission) {
1519 DCHECK(thread_checker_.CalledOnValidThread());
1521 EntryInfo* entry = FindEntryByResourceId(resource_id);
1522 if (!entry)
1523 return HTTP_NOT_FOUND;
1525 entry->user_permission = user_permission;
1526 return HTTP_SUCCESS;
1529 google_apis::DriveApiErrorCode FakeDriveService::SetFileVisibility(
1530 const std::string& resource_id,
1531 google_apis::drive::FileVisibility visibility) {
1532 DCHECK(thread_checker_.CalledOnValidThread());
1534 EntryInfo* entry = FindEntryByResourceId(resource_id);
1535 if (!entry)
1536 return HTTP_NOT_FOUND;
1538 entry->visibility = visibility;
1539 return HTTP_SUCCESS;
1542 google_apis::DriveApiErrorCode FakeDriveService::GetFileVisibility(
1543 const std::string& resource_id,
1544 google_apis::drive::FileVisibility* visibility) {
1545 DCHECK(thread_checker_.CalledOnValidThread());
1546 DCHECK(visibility);
1548 EntryInfo* entry = FindEntryByResourceId(resource_id);
1549 if (!entry)
1550 return HTTP_NOT_FOUND;
1552 *visibility = entry->visibility;
1553 return HTTP_SUCCESS;
1556 void FakeDriveService::AddChangeObserver(ChangeObserver* change_observer) {
1557 change_observers_.AddObserver(change_observer);
1560 void FakeDriveService::RemoveChangeObserver(ChangeObserver* change_observer) {
1561 change_observers_.RemoveObserver(change_observer);
1564 FakeDriveService::EntryInfo* FakeDriveService::FindEntryByResourceId(
1565 const std::string& resource_id) {
1566 DCHECK(thread_checker_.CalledOnValidThread());
1568 EntryInfoMap::iterator it = entries_.find(resource_id);
1569 // Deleted entries don't have FileResource.
1570 return it != entries_.end() && it->second->change_resource.file() ?
1571 it->second : NULL;
1574 std::string FakeDriveService::GetNewResourceId() {
1575 DCHECK(thread_checker_.CalledOnValidThread());
1577 ++resource_id_count_;
1578 return base::StringPrintf("resource_id_%d", resource_id_count_);
1581 void FakeDriveService::UpdateETag(google_apis::FileResource* file) {
1582 file->set_etag(
1583 "etag_" + base::Int64ToString(about_resource_->largest_change_id()));
1586 void FakeDriveService::AddNewChangestamp(google_apis::ChangeResource* change) {
1587 about_resource_->set_largest_change_id(
1588 about_resource_->largest_change_id() + 1);
1589 change->set_change_id(about_resource_->largest_change_id());
1592 const FakeDriveService::EntryInfo* FakeDriveService::AddNewEntry(
1593 const std::string& given_resource_id,
1594 const std::string& content_type,
1595 const std::string& content_data,
1596 const std::string& parent_resource_id,
1597 const std::string& title,
1598 bool shared_with_me) {
1599 DCHECK(thread_checker_.CalledOnValidThread());
1601 if (!parent_resource_id.empty() &&
1602 parent_resource_id != GetRootResourceId() &&
1603 !entries_.count(parent_resource_id)) {
1604 return NULL;
1607 const std::string resource_id =
1608 given_resource_id.empty() ? GetNewResourceId() : given_resource_id;
1609 if (entries_.count(resource_id))
1610 return NULL;
1611 GURL upload_url = GURL("https://xxx/upload/" + resource_id);
1613 scoped_ptr<EntryInfo> new_entry(new EntryInfo);
1614 ChangeResource* new_change = &new_entry->change_resource;
1615 FileResource* new_file = new FileResource;
1616 new_change->set_file(make_scoped_ptr(new_file));
1618 // Set the resource ID and the title
1619 new_change->set_file_id(resource_id);
1620 new_file->set_file_id(resource_id);
1621 new_file->set_title(title);
1622 // Set the contents, size and MD5 for a file.
1623 if (content_type != util::kDriveFolderMimeType &&
1624 !util::IsKnownHostedDocumentMimeType(content_type)) {
1625 new_entry->content_data = content_data;
1626 new_file->set_file_size(content_data.size());
1627 new_file->set_md5_checksum(base::MD5String(content_data));
1630 if (shared_with_me) {
1631 // Set current time to mark the file as shared_with_me.
1632 new_file->set_shared_with_me_date(base::Time::Now());
1635 std::string escaped_resource_id = net::EscapePath(resource_id);
1637 // Set mime type.
1638 new_file->set_mime_type(content_type);
1640 // Set alternate link if needed.
1641 if (content_type == util::kGoogleDocumentMimeType)
1642 new_file->set_alternate_link(GURL("https://document_alternate_link"));
1644 // Set parents.
1645 if (!parent_resource_id.empty()) {
1646 ParentReference parent;
1647 parent.set_file_id(parent_resource_id);
1648 parent.set_parent_link(GetFakeLinkUrl(parent.file_id()));
1649 std::vector<ParentReference> parents;
1650 parents.push_back(parent);
1651 *new_file->mutable_parents() = parents;
1654 new_entry->share_url = net::AppendOrReplaceQueryParameter(
1655 share_url_base_, "name", title);
1657 AddNewChangestamp(new_change);
1658 UpdateETag(new_file);
1660 base::Time published_date =
1661 base::Time() + base::TimeDelta::FromMilliseconds(++published_date_seq_);
1662 new_file->set_created_date(published_date);
1664 EntryInfo* raw_new_entry = new_entry.release();
1665 entries_[resource_id] = raw_new_entry;
1666 return raw_new_entry;
1669 void FakeDriveService::GetChangeListInternal(
1670 int64 start_changestamp,
1671 const std::string& search_query,
1672 const std::string& directory_resource_id,
1673 int start_offset,
1674 int max_results,
1675 int* load_counter,
1676 const ChangeListCallback& callback) {
1677 if (offline_) {
1678 base::ThreadTaskRunnerHandle::Get()->PostTask(
1679 FROM_HERE,
1680 base::Bind(callback,
1681 DRIVE_NO_CONNECTION,
1682 base::Passed(scoped_ptr<ChangeList>())));
1683 return;
1686 // Filter out entries per parameters like |directory_resource_id| and
1687 // |search_query|.
1688 ScopedVector<ChangeResource> entries;
1689 int num_entries_matched = 0;
1690 for (EntryInfoMap::iterator it = entries_.begin(); it != entries_.end();
1691 ++it) {
1692 const ChangeResource& entry = it->second->change_resource;
1693 bool should_exclude = false;
1695 // If |directory_resource_id| is set, exclude the entry if it's not in
1696 // the target directory.
1697 if (!directory_resource_id.empty()) {
1698 // Get the parent resource ID of the entry.
1699 std::string parent_resource_id;
1700 if (entry.file() && !entry.file()->parents().empty())
1701 parent_resource_id = entry.file()->parents()[0].file_id();
1703 if (directory_resource_id != parent_resource_id)
1704 should_exclude = true;
1707 // If |search_query| is set, exclude the entry if it does not contain the
1708 // search query in the title.
1709 if (!should_exclude && !search_query.empty() &&
1710 !EntryMatchWithQuery(entry, search_query)) {
1711 should_exclude = true;
1714 // If |start_changestamp| is set, exclude the entry if the
1715 // changestamp is older than |largest_changestamp|.
1716 // See https://developers.google.com/google-apps/documents-list/
1717 // #retrieving_all_changes_since_a_given_changestamp
1718 if (start_changestamp > 0 && entry.change_id() < start_changestamp)
1719 should_exclude = true;
1721 // If the caller requests other list than change list by specifying
1722 // zero-|start_changestamp|, exclude deleted entry from the result.
1723 const bool deleted = entry.is_deleted() ||
1724 (entry.file() && entry.file()->labels().is_trashed());
1725 if (!start_changestamp && deleted)
1726 should_exclude = true;
1728 // The entry matched the criteria for inclusion.
1729 if (!should_exclude)
1730 ++num_entries_matched;
1732 // If |start_offset| is set, exclude the entry if the entry is before the
1733 // start index. <= instead of < as |num_entries_matched| was
1734 // already incremented.
1735 if (start_offset > 0 && num_entries_matched <= start_offset)
1736 should_exclude = true;
1738 if (!should_exclude) {
1739 scoped_ptr<ChangeResource> entry_copied(new ChangeResource);
1740 entry_copied->set_change_id(entry.change_id());
1741 entry_copied->set_file_id(entry.file_id());
1742 entry_copied->set_deleted(entry.is_deleted());
1743 if (entry.file()) {
1744 entry_copied->set_file(
1745 make_scoped_ptr(new FileResource(*entry.file())));
1747 entry_copied->set_modification_date(entry.modification_date());
1748 entries.push_back(entry_copied.release());
1752 scoped_ptr<ChangeList> change_list(new ChangeList);
1753 if (start_changestamp > 0 && start_offset == 0) {
1754 change_list->set_largest_change_id(about_resource_->largest_change_id());
1757 // If |max_results| is set, trim the entries if the number exceeded the max
1758 // results.
1759 if (max_results > 0 && entries.size() > static_cast<size_t>(max_results)) {
1760 entries.erase(entries.begin() + max_results, entries.end());
1761 // Adds the next URL.
1762 // Here, we embed information which is needed for continuing the
1763 // GetChangeList request in the next invocation into url query
1764 // parameters.
1765 GURL next_url(base::StringPrintf(
1766 "http://localhost/?start-offset=%d&max-results=%d",
1767 start_offset + max_results,
1768 max_results));
1769 if (start_changestamp > 0) {
1770 next_url = net::AppendOrReplaceQueryParameter(
1771 next_url, "changestamp",
1772 base::Int64ToString(start_changestamp).c_str());
1774 if (!search_query.empty()) {
1775 next_url = net::AppendOrReplaceQueryParameter(
1776 next_url, "q", search_query);
1778 if (!directory_resource_id.empty()) {
1779 next_url = net::AppendOrReplaceQueryParameter(
1780 next_url, "parent", directory_resource_id);
1783 change_list->set_next_link(next_url);
1785 *change_list->mutable_items() = entries.Pass();
1787 if (load_counter)
1788 *load_counter += 1;
1789 base::ThreadTaskRunnerHandle::Get()->PostTask(
1790 FROM_HERE,
1791 base::Bind(callback, HTTP_SUCCESS, base::Passed(&change_list)));
1794 GURL FakeDriveService::GetNewUploadSessionUrl() {
1795 return GURL("https://upload_session_url/" +
1796 base::Int64ToString(next_upload_sequence_number_++));
1799 google_apis::CancelCallback FakeDriveService::AddPermission(
1800 const std::string& resource_id,
1801 const std::string& email,
1802 google_apis::drive::PermissionRole role,
1803 const google_apis::EntryActionCallback& callback) {
1804 DCHECK(thread_checker_.CalledOnValidThread());
1805 DCHECK(!callback.is_null());
1807 NOTREACHED();
1808 return CancelCallback();
1811 scoped_ptr<BatchRequestConfiguratorInterface>
1812 FakeDriveService::StartBatchRequest() {
1813 DCHECK(thread_checker_.CalledOnValidThread());
1815 NOTREACHED();
1816 return scoped_ptr<BatchRequestConfiguratorInterface>();
1819 void FakeDriveService::NotifyObservers() {
1820 FOR_EACH_OBSERVER(ChangeObserver, change_observers_, OnNewChangeAvailable());
1823 } // namespace drive