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