1 // Copyright (c) 2012 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
5 #include "chrome/browser/drive/fake_drive_service.h"
9 #include "base/files/file_util.h"
10 #include "base/json/json_string_value_serializer.h"
11 #include "base/logging.h"
13 #include "base/message_loop/message_loop.h"
14 #include "base/strings/string_number_conversions.h"
15 #include "base/strings/string_split.h"
16 #include "base/strings/string_tokenizer.h"
17 #include "base/strings/string_util.h"
18 #include "base/strings/stringprintf.h"
19 #include "base/strings/utf_string_conversions.h"
20 #include "base/values.h"
21 #include "chrome/browser/drive/drive_api_util.h"
22 #include "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
;
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
);
82 base::StringTokenizer
key_value(token
, ":");
83 key_value
.set_quote_chars("\"'");
84 if (!key_value
.GetNext())
86 key
= key_value
.token();
87 if (!key_value
.GetNext())
89 base::TrimString(key_value
.token(), "\"'", &value
);
92 // TODO(peria): Deal with other attributes than title.
93 if (!key
.empty() && key
!= "title")
95 // Search query in the title.
97 entry
.file()->title().find(value
) == std::string::npos
)
103 void ScheduleUploadRangeCallback(const UploadRangeCallback
& callback
,
104 int64 start_position
,
106 DriveApiErrorCode error
,
107 scoped_ptr
<FileResource
> entry
) {
108 base::MessageLoop::current()->PostTask(
111 UploadRangeResponse(error
,
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
;
122 callback
.Run(error
, file_list
.Pass());
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
];
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
:
141 case google_apis::drive::PERMISSION_ROLE_READER
:
142 case google_apis::drive::PERMISSION_ROLE_COMMENTER
:
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
) {
160 service
->ResumeUpload(
162 /* start position */ 0,
163 /* end position */ content_length
,
167 base::Bind(&CallFileResouceCallback
, 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
;
182 struct FakeDriveService::EntryInfo
{
183 EntryInfo() : user_permission(google_apis::drive::PERMISSION_ROLE_OWNER
) {}
185 google_apis::ChangeResource change_resource
;
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
;
209 std::string content_type
,
210 int64 content_length
,
211 std::string parent_resource_id
,
212 std::string resource_id
,
215 : content_type(content_type
),
216 content_length(content_length
),
217 parent_resource_id(parent_resource_id
),
218 resource_id(resource_id
),
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),
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
,
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
);
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";
317 if (item
->GetStringWithoutPathExpansion(kKeyId
, &item_id
) &&
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());
354 bool FakeDriveService::HasAccessToken() const {
355 DCHECK(thread_checker_
.CalledOnValidThread());
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());
370 void FakeDriveService::ClearAccessToken() {
371 DCHECK(thread_checker_
.CalledOnValidThread());
374 void FakeDriveService::ClearRefreshToken() {
375 DCHECK(thread_checker_
.CalledOnValidThread());
378 std::string
FakeDriveService::GetRootResourceId() const {
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,
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
,
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
428 std::string(), // no directory resource id,
430 default_max_results_
,
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
,
450 default_max_results_
,
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,
466 default_max_results_
,
467 &change_list_load_count_
,
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(), '=', '&', ¶meters
)) {
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") {
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());
537 base::MessageLoop::current()->PostTask(
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(
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(
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());
569 base::MessageLoop::current()->PostTask(
574 return CancelCallback();
577 EntryInfo
* entry
= FindEntryByResourceId(resource_id
);
579 base::MessageLoop::current()->PostTask(
581 base::Bind(callback
, HTTP_SUCCESS
, entry
->share_url
));
582 return CancelCallback();
585 base::MessageLoop::current()->PostTask(
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());
597 scoped_ptr
<AboutResource
> null
;
598 base::MessageLoop::current()->PostTask(
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(
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_
);
620 scoped_ptr
<AppList
> null
;
621 base::MessageLoop::current()->PostTask(
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(
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());
645 base::MessageLoop::current()->PostTask(
646 FROM_HERE
, base::Bind(callback
, DRIVE_NO_CONNECTION
));
647 return CancelCallback();
650 EntryInfo
* entry
= FindEntryByResourceId(resource_id
);
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(
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());
696 base::MessageLoop::current()->PostTask(
697 FROM_HERE
, base::Bind(callback
, DRIVE_NO_CONNECTION
));
698 return CancelCallback();
701 EntryInfo
* entry
= FindEntryByResourceId(resource_id
);
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(
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());
743 base::MessageLoop::current()->PostTask(
745 base::Bind(download_action_callback
,
748 return CancelCallback();
751 EntryInfo
* entry
= FindEntryByResourceId(resource_id
);
752 if (!entry
|| entry
->change_resource
.file()->IsHostedDocument()) {
753 base::MessageLoopProxy::current()->PostTask(
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(
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(
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(
792 base::Bind(progress_callback
, file_size
/ 2, file_size
));
793 base::MessageLoopProxy::current()->PostTask(
795 base::Bind(progress_callback
, file_size
, file_size
));
797 base::MessageLoopProxy::current()->PostTask(
799 base::Bind(download_action_callback
,
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());
815 base::MessageLoop::current()->PostTask(
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
);
828 base::MessageLoop::current()->PostTask(
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(
869 base::Passed(make_scoped_ptr(new FileResource(*new_file
)))));
870 base::MessageLoop::current()->PostTask(
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());
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
);
897 base::MessageLoop::current()->PostTask(
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(
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
);
938 base::MessageLoop::current()->PostTask(
940 base::Bind(callback
, HTTP_SUCCESS
,
941 base::Passed(make_scoped_ptr(new FileResource(*file
)))));
942 base::MessageLoop::current()->PostTask(
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());
957 base::MessageLoop::current()->PostTask(
958 FROM_HERE
, base::Bind(callback
, DRIVE_NO_CONNECTION
));
959 return CancelCallback();
962 EntryInfo
* entry
= FindEntryByResourceId(resource_id
);
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(
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());
997 base::MessageLoop::current()->PostTask(
998 FROM_HERE
, base::Bind(callback
, DRIVE_NO_CONNECTION
));
999 return CancelCallback();
1002 EntryInfo
* entry
= FindEntryByResourceId(resource_id
);
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(
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
,
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());
1055 base::MessageLoop::current()->PostTask(
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(
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
,
1077 base::MessageLoop::current()->PostTask(
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());
1093 base::MessageLoop::current()->PostTask(
1095 base::Bind(callback
, DRIVE_NO_CONNECTION
, GURL()));
1096 return CancelCallback();
1099 EntryInfo
* entry
= FindEntryByResourceId(resource_id
);
1101 base::MessageLoop::current()->PostTask(
1103 base::Bind(callback
, HTTP_NOT_FOUND
, GURL()));
1104 return CancelCallback();
1107 if (!UserHasWriteAccess(entry
->user_permission
)) {
1108 base::MessageLoop::current()->PostTask(
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(
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
1131 base::MessageLoop::current()->PostTask(
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
,
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
);
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
,
1217 session
->parent_resource_id
,
1219 false); // shared_with_me
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(
1229 base::Bind(&FakeDriveService::NotifyObservers
,
1230 weak_ptr_factory_
.GetWeakPtr()));
1231 return CancelCallback();
1234 EntryInfo
* entry
= FindEntryByResourceId(session
->resource_id
);
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
);
1253 completion_callback
.Run(HTTP_SUCCESS
, make_scoped_ptr(
1254 new FileResource(*file
)));
1255 base::MessageLoop::current()->PostTask(
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(
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(
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(),
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());
1338 base::MessageLoop::current()->PostTask(
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(
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
;
1356 if (items
->GetDictionary(i
, &item
) && item
->GetString("id", &id
) &&
1358 base::MessageLoop::current()->PostTask(
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(
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());
1395 base::MessageLoop::current()->PostTask(
1397 base::Bind(callback
,
1398 DRIVE_NO_CONNECTION
,
1399 base::Passed(scoped_ptr
<FileResource
>())));
1403 const EntryInfo
* new_entry
= AddNewEntry(resource_id
,
1410 base::MessageLoop::current()->PostTask(
1412 base::Bind(callback
, HTTP_NOT_FOUND
,
1413 base::Passed(scoped_ptr
<FileResource
>())));
1417 base::MessageLoop::current()->PostTask(
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(
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());
1438 base::MessageLoop::current()->PostTask(
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
,
1451 false); // shared_with_me
1453 base::MessageLoop::current()->PostTask(
1455 base::Bind(callback
, HTTP_NOT_FOUND
,
1456 base::Passed(scoped_ptr
<FileResource
>())));
1457 return CancelCallback();
1460 base::MessageLoop::current()->PostTask(
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(
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());
1480 base::MessageLoop::current()->PostTask(
1482 base::Bind(callback
,
1483 DRIVE_NO_CONNECTION
,
1484 base::Passed(scoped_ptr
<FileResource
>())));
1488 EntryInfo
* entry
= FindEntryByResourceId(resource_id
);
1490 base::MessageLoop::current()->PostTask(
1492 base::Bind(callback
, HTTP_NOT_FOUND
,
1493 base::Passed(scoped_ptr
<FileResource
>())));
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(
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
);
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() ?
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
) {
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
)) {
1571 const std::string resource_id
=
1572 given_resource_id
.empty() ? GetNewResourceId() : given_resource_id
;
1573 if (entries_
.count(resource_id
))
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
);
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"));
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
,
1640 const ChangeListCallback
& callback
) {
1642 base::MessageLoop::current()->PostTask(
1644 base::Bind(callback
,
1645 DRIVE_NO_CONNECTION
,
1646 base::Passed(scoped_ptr
<ChangeList
>())));
1650 // Filter out entries per parameters like |directory_resource_id| and
1652 ScopedVector
<ChangeResource
> entries
;
1653 int num_entries_matched
= 0;
1654 for (EntryInfoMap::iterator it
= entries_
.begin(); it
!= entries_
.end();
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());
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
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
1729 GURL
next_url(base::StringPrintf(
1730 "http://localhost/?start-offset=%d&max-results=%d",
1731 start_offset
+ 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();
1753 base::MessageLoop::current()->PostTask(
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());
1772 return CancelCallback();
1775 scoped_ptr
<BatchRequestConfiguratorInterface
>
1776 FakeDriveService::StartBatchRequest() {
1777 DCHECK(thread_checker_
.CalledOnValidThread());
1780 return scoped_ptr
<BatchRequestConfiguratorInterface
>();
1783 void FakeDriveService::NotifyObservers() {
1784 FOR_EACH_OBSERVER(ChangeObserver
, change_observers_
, OnNewChangeAvailable());
1787 } // namespace drive