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 JSONStringValueSerializer
json(app_json
);
284 std::string error_message
;
285 scoped_ptr
<base::Value
> value(json
.Deserialize(NULL
, &error_message
));
286 CHECK_EQ(base::Value::TYPE_DICTIONARY
, value
->GetType());
288 base::ListValue
* item_list
;
289 CHECK(app_info_value_
->GetListWithoutPathExpansion("items", &item_list
));
290 item_list
->Append(value
.release());
293 void FakeDriveService::RemoveAppByProductId(const std::string
& product_id
) {
294 base::ListValue
* item_list
;
295 CHECK(app_info_value_
->GetListWithoutPathExpansion("items", &item_list
));
296 for (size_t i
= 0; i
< item_list
->GetSize(); ++i
) {
297 base::DictionaryValue
* item
;
298 CHECK(item_list
->GetDictionary(i
, &item
));
299 const char kKeyProductId
[] = "productId";
300 std::string item_product_id
;
301 if (item
->GetStringWithoutPathExpansion(kKeyProductId
, &item_product_id
) &&
302 product_id
== item_product_id
) {
303 item_list
->Remove(i
, NULL
);
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::FileResourceCallback
& callback
) {
884 DCHECK(thread_checker_
.CalledOnValidThread());
885 DCHECK(!callback
.is_null());
888 base::MessageLoop::current()->PostTask(
889 FROM_HERE
, base::Bind(callback
, DRIVE_NO_CONNECTION
,
890 base::Passed(scoped_ptr
<FileResource
>())));
891 return CancelCallback();
894 EntryInfo
* entry
= FindEntryByResourceId(resource_id
);
896 base::MessageLoop::current()->PostTask(
898 base::Bind(callback
, HTTP_NOT_FOUND
,
899 base::Passed(scoped_ptr
<FileResource
>())));
900 return CancelCallback();
903 if (!UserHasWriteAccess(entry
->user_permission
)) {
904 base::MessageLoop::current()->PostTask(
906 base::Bind(callback
, HTTP_FORBIDDEN
,
907 base::Passed(scoped_ptr
<FileResource
>())));
908 return CancelCallback();
911 ChangeResource
* change
= &entry
->change_resource
;
912 FileResource
* file
= change
->mutable_file();
914 if (!new_title
.empty())
915 file
->set_title(new_title
);
917 // Set parent if necessary.
918 if (!parent_resource_id
.empty()) {
919 ParentReference parent
;
920 parent
.set_file_id(parent_resource_id
);
921 parent
.set_parent_link(GetFakeLinkUrl(parent_resource_id
));
923 std::vector
<ParentReference
> parents
;
924 parents
.push_back(parent
);
925 *file
->mutable_parents() = parents
;
928 if (!last_modified
.is_null())
929 file
->set_modified_date(last_modified
);
931 if (!last_viewed_by_me
.is_null())
932 file
->set_last_viewed_by_me_date(last_viewed_by_me
);
934 AddNewChangestamp(change
);
937 base::MessageLoop::current()->PostTask(
939 base::Bind(callback
, HTTP_SUCCESS
,
940 base::Passed(make_scoped_ptr(new FileResource(*file
)))));
941 base::MessageLoop::current()->PostTask(
943 base::Bind(&FakeDriveService::NotifyObservers
,
944 weak_ptr_factory_
.GetWeakPtr()));
945 return CancelCallback();
948 CancelCallback
FakeDriveService::AddResourceToDirectory(
949 const std::string
& parent_resource_id
,
950 const std::string
& resource_id
,
951 const EntryActionCallback
& callback
) {
952 DCHECK(thread_checker_
.CalledOnValidThread());
953 DCHECK(!callback
.is_null());
956 base::MessageLoop::current()->PostTask(
957 FROM_HERE
, base::Bind(callback
, DRIVE_NO_CONNECTION
));
958 return CancelCallback();
961 EntryInfo
* entry
= FindEntryByResourceId(resource_id
);
963 base::MessageLoop::current()->PostTask(
964 FROM_HERE
, base::Bind(callback
, HTTP_NOT_FOUND
));
965 return CancelCallback();
968 ChangeResource
* change
= &entry
->change_resource
;
969 // On the real Drive server, resources do not necessary shape a tree
970 // structure. That is, each resource can have multiple parent.
971 // We mimic the behavior here; AddResourceToDirectoy just adds
972 // one more parent, not overwriting old ones.
973 ParentReference parent
;
974 parent
.set_file_id(parent_resource_id
);
975 parent
.set_parent_link(GetFakeLinkUrl(parent_resource_id
));
976 change
->mutable_file()->mutable_parents()->push_back(parent
);
978 AddNewChangestamp(change
);
979 base::MessageLoop::current()->PostTask(
980 FROM_HERE
, base::Bind(callback
, HTTP_SUCCESS
));
981 base::MessageLoop::current()->PostTask(
983 base::Bind(&FakeDriveService::NotifyObservers
,
984 weak_ptr_factory_
.GetWeakPtr()));
985 return CancelCallback();
988 CancelCallback
FakeDriveService::RemoveResourceFromDirectory(
989 const std::string
& parent_resource_id
,
990 const std::string
& resource_id
,
991 const EntryActionCallback
& callback
) {
992 DCHECK(thread_checker_
.CalledOnValidThread());
993 DCHECK(!callback
.is_null());
996 base::MessageLoop::current()->PostTask(
997 FROM_HERE
, base::Bind(callback
, DRIVE_NO_CONNECTION
));
998 return CancelCallback();
1001 EntryInfo
* entry
= FindEntryByResourceId(resource_id
);
1003 base::MessageLoop::current()->PostTask(
1004 FROM_HERE
, base::Bind(callback
, HTTP_NOT_FOUND
));
1005 return CancelCallback();
1008 ChangeResource
* change
= &entry
->change_resource
;
1009 FileResource
* file
= change
->mutable_file();
1010 std::vector
<ParentReference
>* parents
= file
->mutable_parents();
1011 for (size_t i
= 0; i
< parents
->size(); ++i
) {
1012 if ((*parents
)[i
].file_id() == parent_resource_id
) {
1013 parents
->erase(parents
->begin() + i
);
1014 AddNewChangestamp(change
);
1015 base::MessageLoop::current()->PostTask(
1016 FROM_HERE
, base::Bind(callback
, HTTP_NO_CONTENT
));
1017 base::MessageLoop::current()->PostTask(
1019 base::Bind(&FakeDriveService::NotifyObservers
,
1020 weak_ptr_factory_
.GetWeakPtr()));
1021 return CancelCallback();
1025 base::MessageLoop::current()->PostTask(
1026 FROM_HERE
, base::Bind(callback
, HTTP_NOT_FOUND
));
1027 return CancelCallback();
1030 CancelCallback
FakeDriveService::AddNewDirectory(
1031 const std::string
& parent_resource_id
,
1032 const std::string
& directory_title
,
1033 const AddNewDirectoryOptions
& options
,
1034 const FileResourceCallback
& callback
) {
1035 return AddNewDirectoryWithResourceId(
1037 parent_resource_id
.empty() ? GetRootResourceId() : parent_resource_id
,
1043 CancelCallback
FakeDriveService::InitiateUploadNewFile(
1044 const std::string
& content_type
,
1045 int64 content_length
,
1046 const std::string
& parent_resource_id
,
1047 const std::string
& title
,
1048 const UploadNewFileOptions
& options
,
1049 const InitiateUploadCallback
& callback
) {
1050 DCHECK(thread_checker_
.CalledOnValidThread());
1051 DCHECK(!callback
.is_null());
1054 base::MessageLoop::current()->PostTask(
1056 base::Bind(callback
, DRIVE_NO_CONNECTION
, GURL()));
1057 return CancelCallback();
1060 if (parent_resource_id
!= GetRootResourceId() &&
1061 !entries_
.count(parent_resource_id
)) {
1062 base::MessageLoop::current()->PostTask(
1064 base::Bind(callback
, HTTP_NOT_FOUND
, GURL()));
1065 return CancelCallback();
1068 GURL session_url
= GetNewUploadSessionUrl();
1069 upload_sessions_
[session_url
] =
1070 UploadSession(content_type
, content_length
,
1076 base::MessageLoop::current()->PostTask(
1078 base::Bind(callback
, HTTP_SUCCESS
, session_url
));
1079 return CancelCallback();
1082 CancelCallback
FakeDriveService::InitiateUploadExistingFile(
1083 const std::string
& content_type
,
1084 int64 content_length
,
1085 const std::string
& resource_id
,
1086 const UploadExistingFileOptions
& options
,
1087 const InitiateUploadCallback
& callback
) {
1088 DCHECK(thread_checker_
.CalledOnValidThread());
1089 DCHECK(!callback
.is_null());
1092 base::MessageLoop::current()->PostTask(
1094 base::Bind(callback
, DRIVE_NO_CONNECTION
, GURL()));
1095 return CancelCallback();
1098 EntryInfo
* entry
= FindEntryByResourceId(resource_id
);
1100 base::MessageLoop::current()->PostTask(
1102 base::Bind(callback
, HTTP_NOT_FOUND
, GURL()));
1103 return CancelCallback();
1106 if (!UserHasWriteAccess(entry
->user_permission
)) {
1107 base::MessageLoop::current()->PostTask(
1109 base::Bind(callback
, HTTP_FORBIDDEN
, GURL()));
1110 return CancelCallback();
1113 FileResource
* file
= entry
->change_resource
.mutable_file();
1114 if (!options
.etag
.empty() && options
.etag
!= file
->etag()) {
1115 base::MessageLoop::current()->PostTask(
1117 base::Bind(callback
, HTTP_PRECONDITION
, GURL()));
1118 return CancelCallback();
1120 // TODO(hashimoto): Update |file|'s metadata with |options|.
1122 GURL session_url
= GetNewUploadSessionUrl();
1123 upload_sessions_
[session_url
] =
1124 UploadSession(content_type
, content_length
,
1125 "", // parent_resource_id
1130 base::MessageLoop::current()->PostTask(
1132 base::Bind(callback
, HTTP_SUCCESS
, session_url
));
1133 return CancelCallback();
1136 CancelCallback
FakeDriveService::GetUploadStatus(
1137 const GURL
& upload_url
,
1138 int64 content_length
,
1139 const UploadRangeCallback
& callback
) {
1140 DCHECK(thread_checker_
.CalledOnValidThread());
1141 DCHECK(!callback
.is_null());
1142 return CancelCallback();
1145 CancelCallback
FakeDriveService::ResumeUpload(
1146 const GURL
& upload_url
,
1147 int64 start_position
,
1149 int64 content_length
,
1150 const std::string
& content_type
,
1151 const base::FilePath
& local_file_path
,
1152 const UploadRangeCallback
& callback
,
1153 const ProgressCallback
& progress_callback
) {
1154 DCHECK(thread_checker_
.CalledOnValidThread());
1155 DCHECK(!callback
.is_null());
1157 FileResourceCallback completion_callback
1158 = base::Bind(&ScheduleUploadRangeCallback
,
1159 callback
, start_position
, end_position
);
1162 completion_callback
.Run(DRIVE_NO_CONNECTION
, scoped_ptr
<FileResource
>());
1163 return CancelCallback();
1166 if (!upload_sessions_
.count(upload_url
)) {
1167 completion_callback
.Run(HTTP_NOT_FOUND
, scoped_ptr
<FileResource
>());
1168 return CancelCallback();
1171 UploadSession
* session
= &upload_sessions_
[upload_url
];
1173 // Chunks are required to be sent in such a ways that they fill from the start
1174 // of the not-yet-uploaded part with no gaps nor overlaps.
1175 if (session
->uploaded_size
!= start_position
) {
1176 completion_callback
.Run(HTTP_BAD_REQUEST
, scoped_ptr
<FileResource
>());
1177 return CancelCallback();
1180 if (!progress_callback
.is_null()) {
1181 // In the real GDataWapi/Drive DriveService, progress is reported in
1182 // nondeterministic timing. In this fake implementation, we choose to call
1183 // it twice per one ResumeUpload. This is for making sure that client code
1184 // works fine even if the callback is invoked more than once; it is the
1185 // crucial difference of the progress callback from others.
1186 // Note that progress is notified in the relative offset in each chunk.
1187 const int64 chunk_size
= end_position
- start_position
;
1188 base::MessageLoop::current()->PostTask(
1189 FROM_HERE
, base::Bind(progress_callback
, chunk_size
/ 2, chunk_size
));
1190 base::MessageLoop::current()->PostTask(
1191 FROM_HERE
, base::Bind(progress_callback
, chunk_size
, chunk_size
));
1194 if (content_length
!= end_position
) {
1195 session
->uploaded_size
= end_position
;
1196 completion_callback
.Run(HTTP_RESUME_INCOMPLETE
, scoped_ptr
<FileResource
>());
1197 return CancelCallback();
1200 std::string content_data
;
1201 if (!base::ReadFileToString(local_file_path
, &content_data
)) {
1202 session
->uploaded_size
= end_position
;
1203 completion_callback
.Run(DRIVE_FILE_ERROR
, scoped_ptr
<FileResource
>());
1204 return CancelCallback();
1206 session
->uploaded_size
= end_position
;
1208 // |resource_id| is empty if the upload is for new file.
1209 if (session
->resource_id
.empty()) {
1210 DCHECK(!session
->parent_resource_id
.empty());
1211 DCHECK(!session
->title
.empty());
1212 const EntryInfo
* new_entry
= AddNewEntry(
1213 "", // auto generate resource id.
1214 session
->content_type
,
1216 session
->parent_resource_id
,
1218 false); // shared_with_me
1220 completion_callback
.Run(HTTP_NOT_FOUND
, scoped_ptr
<FileResource
>());
1221 return CancelCallback();
1224 completion_callback
.Run(HTTP_CREATED
, make_scoped_ptr(
1225 new FileResource(*new_entry
->change_resource
.file())));
1226 base::MessageLoop::current()->PostTask(
1228 base::Bind(&FakeDriveService::NotifyObservers
,
1229 weak_ptr_factory_
.GetWeakPtr()));
1230 return CancelCallback();
1233 EntryInfo
* entry
= FindEntryByResourceId(session
->resource_id
);
1235 completion_callback
.Run(HTTP_NOT_FOUND
, scoped_ptr
<FileResource
>());
1236 return CancelCallback();
1239 ChangeResource
* change
= &entry
->change_resource
;
1240 FileResource
* file
= change
->mutable_file();
1241 if (file
->etag().empty() || session
->etag
!= file
->etag()) {
1242 completion_callback
.Run(HTTP_PRECONDITION
, scoped_ptr
<FileResource
>());
1243 return CancelCallback();
1246 file
->set_md5_checksum(base::MD5String(content_data
));
1247 entry
->content_data
= content_data
;
1248 file
->set_file_size(end_position
);
1249 AddNewChangestamp(change
);
1252 completion_callback
.Run(HTTP_SUCCESS
, make_scoped_ptr(
1253 new FileResource(*file
)));
1254 base::MessageLoop::current()->PostTask(
1256 base::Bind(&FakeDriveService::NotifyObservers
,
1257 weak_ptr_factory_
.GetWeakPtr()));
1258 return CancelCallback();
1261 CancelCallback
FakeDriveService::MultipartUploadNewFile(
1262 const std::string
& content_type
,
1263 int64 content_length
,
1264 const std::string
& parent_resource_id
,
1265 const std::string
& title
,
1266 const base::FilePath
& local_file_path
,
1267 const UploadNewFileOptions
& options
,
1268 const FileResourceCallback
& callback
,
1269 const ProgressCallback
& progress_callback
) {
1270 CallResumeUpload
* const call_resume_upload
= new CallResumeUpload();
1271 call_resume_upload
->service
= weak_ptr_factory_
.GetWeakPtr();
1272 call_resume_upload
->content_type
= content_type
;
1273 call_resume_upload
->content_length
= content_length
;
1274 call_resume_upload
->local_file_path
= local_file_path
;
1275 call_resume_upload
->callback
= callback
;
1276 call_resume_upload
->progress_callback
= progress_callback
;
1277 InitiateUploadNewFile(
1283 base::Bind(&CallResumeUpload::Run
, base::Owned(call_resume_upload
)));
1284 return CancelCallback();
1287 CancelCallback
FakeDriveService::MultipartUploadExistingFile(
1288 const std::string
& content_type
,
1289 int64 content_length
,
1290 const std::string
& resource_id
,
1291 const base::FilePath
& local_file_path
,
1292 const UploadExistingFileOptions
& options
,
1293 const FileResourceCallback
& callback
,
1294 const ProgressCallback
& progress_callback
) {
1295 CallResumeUpload
* const call_resume_upload
= new CallResumeUpload();
1296 call_resume_upload
->service
= weak_ptr_factory_
.GetWeakPtr();
1297 call_resume_upload
->content_type
= content_type
;
1298 call_resume_upload
->content_length
= content_length
;
1299 call_resume_upload
->local_file_path
= local_file_path
;
1300 call_resume_upload
->callback
= callback
;
1301 call_resume_upload
->progress_callback
= progress_callback
;
1302 InitiateUploadExistingFile(
1307 base::Bind(&CallResumeUpload::Run
, base::Owned(call_resume_upload
)));
1308 return CancelCallback();
1311 CancelCallback
FakeDriveService::AuthorizeApp(
1312 const std::string
& resource_id
,
1313 const std::string
& app_id
,
1314 const AuthorizeAppCallback
& callback
) {
1315 DCHECK(thread_checker_
.CalledOnValidThread());
1316 DCHECK(!callback
.is_null());
1318 if (entries_
.count(resource_id
) == 0) {
1319 callback
.Run(google_apis::HTTP_NOT_FOUND
, GURL());
1320 return CancelCallback();
1323 callback
.Run(HTTP_SUCCESS
,
1324 GURL(base::StringPrintf(open_url_format_
.c_str(),
1325 resource_id
.c_str(),
1327 return CancelCallback();
1330 CancelCallback
FakeDriveService::UninstallApp(
1331 const std::string
& app_id
,
1332 const google_apis::EntryActionCallback
& callback
) {
1333 DCHECK(thread_checker_
.CalledOnValidThread());
1334 DCHECK(!callback
.is_null());
1337 base::MessageLoop::current()->PostTask(
1339 base::Bind(callback
, google_apis::DRIVE_NO_CONNECTION
));
1340 return CancelCallback();
1343 // Find app_id from app_info_value_ and delete.
1344 base::ListValue
* items
= NULL
;
1345 if (!app_info_value_
->GetList("items", &items
)) {
1346 base::MessageLoop::current()->PostTask(
1348 base::Bind(callback
, google_apis::HTTP_NOT_FOUND
));
1349 return CancelCallback();
1352 for (size_t i
= 0; i
< items
->GetSize(); ++i
) {
1353 base::DictionaryValue
* item
= NULL
;
1355 if (items
->GetDictionary(i
, &item
) && item
->GetString("id", &id
) &&
1357 base::MessageLoop::current()->PostTask(
1359 base::Bind(callback
,
1360 items
->Remove(i
, NULL
) ? google_apis::HTTP_NO_CONTENT
1361 : google_apis::HTTP_NOT_FOUND
));
1362 return CancelCallback();
1366 base::MessageLoop::current()->PostTask(
1368 base::Bind(callback
, google_apis::HTTP_NOT_FOUND
));
1369 return CancelCallback();
1372 void FakeDriveService::AddNewFile(const std::string
& content_type
,
1373 const std::string
& content_data
,
1374 const std::string
& parent_resource_id
,
1375 const std::string
& title
,
1376 bool shared_with_me
,
1377 const FileResourceCallback
& callback
) {
1378 AddNewFileWithResourceId("", content_type
, content_data
, parent_resource_id
,
1379 title
, shared_with_me
, callback
);
1382 void FakeDriveService::AddNewFileWithResourceId(
1383 const std::string
& resource_id
,
1384 const std::string
& content_type
,
1385 const std::string
& content_data
,
1386 const std::string
& parent_resource_id
,
1387 const std::string
& title
,
1388 bool shared_with_me
,
1389 const FileResourceCallback
& callback
) {
1390 DCHECK(thread_checker_
.CalledOnValidThread());
1391 DCHECK(!callback
.is_null());
1394 base::MessageLoop::current()->PostTask(
1396 base::Bind(callback
,
1397 DRIVE_NO_CONNECTION
,
1398 base::Passed(scoped_ptr
<FileResource
>())));
1402 const EntryInfo
* new_entry
= AddNewEntry(resource_id
,
1409 base::MessageLoop::current()->PostTask(
1411 base::Bind(callback
, HTTP_NOT_FOUND
,
1412 base::Passed(scoped_ptr
<FileResource
>())));
1416 base::MessageLoop::current()->PostTask(
1418 base::Bind(callback
, HTTP_CREATED
,
1419 base::Passed(make_scoped_ptr(
1420 new FileResource(*new_entry
->change_resource
.file())))));
1421 base::MessageLoop::current()->PostTask(
1423 base::Bind(&FakeDriveService::NotifyObservers
,
1424 weak_ptr_factory_
.GetWeakPtr()));
1427 CancelCallback
FakeDriveService::AddNewDirectoryWithResourceId(
1428 const std::string
& resource_id
,
1429 const std::string
& parent_resource_id
,
1430 const std::string
& directory_title
,
1431 const AddNewDirectoryOptions
& options
,
1432 const FileResourceCallback
& callback
) {
1433 DCHECK(thread_checker_
.CalledOnValidThread());
1434 DCHECK(!callback
.is_null());
1437 base::MessageLoop::current()->PostTask(
1439 base::Bind(callback
,
1440 DRIVE_NO_CONNECTION
,
1441 base::Passed(scoped_ptr
<FileResource
>())));
1442 return CancelCallback();
1445 const EntryInfo
* new_entry
= AddNewEntry(resource_id
,
1446 util::kDriveFolderMimeType
,
1450 false); // shared_with_me
1452 base::MessageLoop::current()->PostTask(
1454 base::Bind(callback
, HTTP_NOT_FOUND
,
1455 base::Passed(scoped_ptr
<FileResource
>())));
1456 return CancelCallback();
1459 base::MessageLoop::current()->PostTask(
1461 base::Bind(callback
, HTTP_CREATED
,
1462 base::Passed(make_scoped_ptr(
1463 new FileResource(*new_entry
->change_resource
.file())))));
1464 base::MessageLoop::current()->PostTask(
1466 base::Bind(&FakeDriveService::NotifyObservers
,
1467 weak_ptr_factory_
.GetWeakPtr()));
1468 return CancelCallback();
1471 void FakeDriveService::SetLastModifiedTime(
1472 const std::string
& resource_id
,
1473 const base::Time
& last_modified_time
,
1474 const FileResourceCallback
& callback
) {
1475 DCHECK(thread_checker_
.CalledOnValidThread());
1476 DCHECK(!callback
.is_null());
1479 base::MessageLoop::current()->PostTask(
1481 base::Bind(callback
,
1482 DRIVE_NO_CONNECTION
,
1483 base::Passed(scoped_ptr
<FileResource
>())));
1487 EntryInfo
* entry
= FindEntryByResourceId(resource_id
);
1489 base::MessageLoop::current()->PostTask(
1491 base::Bind(callback
, HTTP_NOT_FOUND
,
1492 base::Passed(scoped_ptr
<FileResource
>())));
1496 ChangeResource
* change
= &entry
->change_resource
;
1497 FileResource
* file
= change
->mutable_file();
1498 file
->set_modified_date(last_modified_time
);
1500 base::MessageLoop::current()->PostTask(
1502 base::Bind(callback
, HTTP_SUCCESS
,
1503 base::Passed(make_scoped_ptr(new FileResource(*file
)))));
1506 google_apis::DriveApiErrorCode
FakeDriveService::SetUserPermission(
1507 const std::string
& resource_id
,
1508 google_apis::drive::PermissionRole user_permission
) {
1509 DCHECK(thread_checker_
.CalledOnValidThread());
1511 EntryInfo
* entry
= FindEntryByResourceId(resource_id
);
1513 return HTTP_NOT_FOUND
;
1515 entry
->user_permission
= user_permission
;
1516 return HTTP_SUCCESS
;
1519 void FakeDriveService::AddChangeObserver(ChangeObserver
* change_observer
) {
1520 change_observers_
.AddObserver(change_observer
);
1523 void FakeDriveService::RemoveChangeObserver(ChangeObserver
* change_observer
) {
1524 change_observers_
.RemoveObserver(change_observer
);
1527 FakeDriveService::EntryInfo
* FakeDriveService::FindEntryByResourceId(
1528 const std::string
& resource_id
) {
1529 DCHECK(thread_checker_
.CalledOnValidThread());
1531 EntryInfoMap::iterator it
= entries_
.find(resource_id
);
1532 // Deleted entries don't have FileResource.
1533 return it
!= entries_
.end() && it
->second
->change_resource
.file() ?
1537 std::string
FakeDriveService::GetNewResourceId() {
1538 DCHECK(thread_checker_
.CalledOnValidThread());
1540 ++resource_id_count_
;
1541 return base::StringPrintf("resource_id_%d", resource_id_count_
);
1544 void FakeDriveService::UpdateETag(google_apis::FileResource
* file
) {
1546 "etag_" + base::Int64ToString(about_resource_
->largest_change_id()));
1549 void FakeDriveService::AddNewChangestamp(google_apis::ChangeResource
* change
) {
1550 about_resource_
->set_largest_change_id(
1551 about_resource_
->largest_change_id() + 1);
1552 change
->set_change_id(about_resource_
->largest_change_id());
1555 const FakeDriveService::EntryInfo
* FakeDriveService::AddNewEntry(
1556 const std::string
& given_resource_id
,
1557 const std::string
& content_type
,
1558 const std::string
& content_data
,
1559 const std::string
& parent_resource_id
,
1560 const std::string
& title
,
1561 bool shared_with_me
) {
1562 DCHECK(thread_checker_
.CalledOnValidThread());
1564 if (!parent_resource_id
.empty() &&
1565 parent_resource_id
!= GetRootResourceId() &&
1566 !entries_
.count(parent_resource_id
)) {
1570 const std::string resource_id
=
1571 given_resource_id
.empty() ? GetNewResourceId() : given_resource_id
;
1572 if (entries_
.count(resource_id
))
1574 GURL upload_url
= GURL("https://xxx/upload/" + resource_id
);
1576 scoped_ptr
<EntryInfo
> new_entry(new EntryInfo
);
1577 ChangeResource
* new_change
= &new_entry
->change_resource
;
1578 FileResource
* new_file
= new FileResource
;
1579 new_change
->set_file(make_scoped_ptr(new_file
));
1581 // Set the resource ID and the title
1582 new_change
->set_file_id(resource_id
);
1583 new_file
->set_file_id(resource_id
);
1584 new_file
->set_title(title
);
1585 // Set the contents, size and MD5 for a file.
1586 if (content_type
!= util::kDriveFolderMimeType
&&
1587 !util::IsKnownHostedDocumentMimeType(content_type
)) {
1588 new_entry
->content_data
= content_data
;
1589 new_file
->set_file_size(content_data
.size());
1590 new_file
->set_md5_checksum(base::MD5String(content_data
));
1593 if (shared_with_me
) {
1594 // Set current time to mark the file as shared_with_me.
1595 new_file
->set_shared_with_me_date(base::Time::Now());
1598 std::string escaped_resource_id
= net::EscapePath(resource_id
);
1601 new_file
->set_mime_type(content_type
);
1603 // Set alternate link if needed.
1604 if (content_type
== util::kGoogleDocumentMimeType
)
1605 new_file
->set_alternate_link(GURL("https://document_alternate_link"));
1608 if (!parent_resource_id
.empty()) {
1609 ParentReference parent
;
1610 parent
.set_file_id(parent_resource_id
);
1611 parent
.set_parent_link(GetFakeLinkUrl(parent
.file_id()));
1612 std::vector
<ParentReference
> parents
;
1613 parents
.push_back(parent
);
1614 *new_file
->mutable_parents() = parents
;
1617 new_entry
->share_url
= net::AppendOrReplaceQueryParameter(
1618 share_url_base_
, "name", title
);
1620 AddNewChangestamp(new_change
);
1621 UpdateETag(new_file
);
1623 base::Time published_date
=
1624 base::Time() + base::TimeDelta::FromMilliseconds(++published_date_seq_
);
1625 new_file
->set_created_date(published_date
);
1627 EntryInfo
* raw_new_entry
= new_entry
.release();
1628 entries_
[resource_id
] = raw_new_entry
;
1629 return raw_new_entry
;
1632 void FakeDriveService::GetChangeListInternal(
1633 int64 start_changestamp
,
1634 const std::string
& search_query
,
1635 const std::string
& directory_resource_id
,
1639 const ChangeListCallback
& callback
) {
1641 base::MessageLoop::current()->PostTask(
1643 base::Bind(callback
,
1644 DRIVE_NO_CONNECTION
,
1645 base::Passed(scoped_ptr
<ChangeList
>())));
1649 // Filter out entries per parameters like |directory_resource_id| and
1651 ScopedVector
<ChangeResource
> entries
;
1652 int num_entries_matched
= 0;
1653 for (EntryInfoMap::iterator it
= entries_
.begin(); it
!= entries_
.end();
1655 const ChangeResource
& entry
= it
->second
->change_resource
;
1656 bool should_exclude
= false;
1658 // If |directory_resource_id| is set, exclude the entry if it's not in
1659 // the target directory.
1660 if (!directory_resource_id
.empty()) {
1661 // Get the parent resource ID of the entry.
1662 std::string parent_resource_id
;
1663 if (entry
.file() && !entry
.file()->parents().empty())
1664 parent_resource_id
= entry
.file()->parents()[0].file_id();
1666 if (directory_resource_id
!= parent_resource_id
)
1667 should_exclude
= true;
1670 // If |search_query| is set, exclude the entry if it does not contain the
1671 // search query in the title.
1672 if (!should_exclude
&& !search_query
.empty() &&
1673 !EntryMatchWithQuery(entry
, search_query
)) {
1674 should_exclude
= true;
1677 // If |start_changestamp| is set, exclude the entry if the
1678 // changestamp is older than |largest_changestamp|.
1679 // See https://developers.google.com/google-apps/documents-list/
1680 // #retrieving_all_changes_since_a_given_changestamp
1681 if (start_changestamp
> 0 && entry
.change_id() < start_changestamp
)
1682 should_exclude
= true;
1684 // If the caller requests other list than change list by specifying
1685 // zero-|start_changestamp|, exclude deleted entry from the result.
1686 const bool deleted
= entry
.is_deleted() ||
1687 (entry
.file() && entry
.file()->labels().is_trashed());
1688 if (!start_changestamp
&& deleted
)
1689 should_exclude
= true;
1691 // The entry matched the criteria for inclusion.
1692 if (!should_exclude
)
1693 ++num_entries_matched
;
1695 // If |start_offset| is set, exclude the entry if the entry is before the
1696 // start index. <= instead of < as |num_entries_matched| was
1697 // already incremented.
1698 if (start_offset
> 0 && num_entries_matched
<= start_offset
)
1699 should_exclude
= true;
1701 if (!should_exclude
) {
1702 scoped_ptr
<ChangeResource
> entry_copied(new ChangeResource
);
1703 entry_copied
->set_change_id(entry
.change_id());
1704 entry_copied
->set_file_id(entry
.file_id());
1705 entry_copied
->set_deleted(entry
.is_deleted());
1707 entry_copied
->set_file(
1708 make_scoped_ptr(new FileResource(*entry
.file())));
1710 entry_copied
->set_modification_date(entry
.modification_date());
1711 entries
.push_back(entry_copied
.release());
1715 scoped_ptr
<ChangeList
> change_list(new ChangeList
);
1716 if (start_changestamp
> 0 && start_offset
== 0) {
1717 change_list
->set_largest_change_id(about_resource_
->largest_change_id());
1720 // If |max_results| is set, trim the entries if the number exceeded the max
1722 if (max_results
> 0 && entries
.size() > static_cast<size_t>(max_results
)) {
1723 entries
.erase(entries
.begin() + max_results
, entries
.end());
1724 // Adds the next URL.
1725 // Here, we embed information which is needed for continuing the
1726 // GetChangeList request in the next invocation into url query
1728 GURL
next_url(base::StringPrintf(
1729 "http://localhost/?start-offset=%d&max-results=%d",
1730 start_offset
+ max_results
,
1732 if (start_changestamp
> 0) {
1733 next_url
= net::AppendOrReplaceQueryParameter(
1734 next_url
, "changestamp",
1735 base::Int64ToString(start_changestamp
).c_str());
1737 if (!search_query
.empty()) {
1738 next_url
= net::AppendOrReplaceQueryParameter(
1739 next_url
, "q", search_query
);
1741 if (!directory_resource_id
.empty()) {
1742 next_url
= net::AppendOrReplaceQueryParameter(
1743 next_url
, "parent", directory_resource_id
);
1746 change_list
->set_next_link(next_url
);
1748 *change_list
->mutable_items() = entries
.Pass();
1752 base::MessageLoop::current()->PostTask(
1754 base::Bind(callback
, HTTP_SUCCESS
, base::Passed(&change_list
)));
1757 GURL
FakeDriveService::GetNewUploadSessionUrl() {
1758 return GURL("https://upload_session_url/" +
1759 base::Int64ToString(next_upload_sequence_number_
++));
1762 google_apis::CancelCallback
FakeDriveService::AddPermission(
1763 const std::string
& resource_id
,
1764 const std::string
& email
,
1765 google_apis::drive::PermissionRole role
,
1766 const google_apis::EntryActionCallback
& callback
) {
1767 DCHECK(thread_checker_
.CalledOnValidThread());
1768 DCHECK(!callback
.is_null());
1771 return CancelCallback();
1774 void FakeDriveService::NotifyObservers() {
1775 FOR_EACH_OBSERVER(ChangeObserver
, change_observers_
, OnNewChangeAvailable());
1778 } // namespace drive