| // Copyright (c) 2012 The Chromium Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| #include "components/drive/service/drive_api_service.h" |
| |
| #include <stddef.h> |
| |
| #include <memory> |
| #include <string> |
| #include <utility> |
| #include <vector> |
| |
| #include "base/bind.h" |
| #include "base/strings/stringprintf.h" |
| #include "components/drive/drive_api_util.h" |
| #include "google_apis/drive/auth_service.h" |
| #include "google_apis/drive/base_requests.h" |
| #include "google_apis/drive/drive_api_parser.h" |
| #include "google_apis/drive/drive_api_requests.h" |
| #include "google_apis/drive/drive_switches.h" |
| #include "google_apis/drive/files_list_request_runner.h" |
| #include "google_apis/drive/request_sender.h" |
| #include "google_apis/google_api_keys.h" |
| #include "net/url_request/url_request_context_getter.h" |
| #include "services/network/public/cpp/shared_url_loader_factory.h" |
| |
| using google_apis::AboutResourceCallback; |
| using google_apis::AppList; |
| using google_apis::AppListCallback; |
| using google_apis::AuthStatusCallback; |
| using google_apis::AuthorizeAppCallback; |
| using google_apis::CancelCallback; |
| using google_apis::ChangeList; |
| using google_apis::ChangeListCallback; |
| using google_apis::DRIVE_OTHER_ERROR; |
| using google_apis::DRIVE_PARSE_ERROR; |
| using google_apis::DownloadActionCallback; |
| using google_apis::DriveApiErrorCode; |
| using google_apis::EntryActionCallback; |
| using google_apis::FileList; |
| using google_apis::FileListCallback; |
| using google_apis::FileResource; |
| using google_apis::FileResourceCallback; |
| using google_apis::FilesListCorpora; |
| using google_apis::FilesListRequestRunner; |
| using google_apis::GetContentCallback; |
| using google_apis::GetShareUrlCallback; |
| using google_apis::HTTP_NOT_IMPLEMENTED; |
| using google_apis::HTTP_SUCCESS; |
| using google_apis::InitiateUploadCallback; |
| using google_apis::ProgressCallback; |
| using google_apis::RequestSender; |
| using google_apis::StartPageTokenCallback; |
| using google_apis::TeamDriveListCallback; |
| using google_apis::UploadRangeResponse; |
| using google_apis::drive::AboutGetRequest; |
| using google_apis::drive::AppsListRequest; |
| using google_apis::drive::ChangesListNextPageRequest; |
| using google_apis::drive::ChangesListRequest; |
| using google_apis::drive::ChildrenDeleteRequest; |
| using google_apis::drive::ChildrenInsertRequest; |
| using google_apis::drive::DownloadFileRequest; |
| using google_apis::drive::FilesCopyRequest; |
| using google_apis::drive::FilesDeleteRequest; |
| using google_apis::drive::FilesGetRequest; |
| using google_apis::drive::FilesInsertRequest; |
| using google_apis::drive::FilesListNextPageRequest; |
| using google_apis::drive::FilesListRequest; |
| using google_apis::drive::FilesPatchRequest; |
| using google_apis::drive::FilesTrashRequest; |
| using google_apis::drive::GetUploadStatusRequest; |
| using google_apis::drive::InitiateUploadExistingFileRequest; |
| using google_apis::drive::InitiateUploadNewFileRequest; |
| using google_apis::drive::ResumeUploadRequest; |
| using google_apis::drive::StartPageTokenRequest; |
| using google_apis::drive::TeamDriveListRequest; |
| using google_apis::drive::UploadRangeCallback; |
| |
| namespace drive { |
| |
| namespace { |
| |
| // OAuth2 scopes for Drive API. |
| const char kDriveScope[] = "https://www.googleapis.com/auth/drive"; |
| const char kDriveAppsReadonlyScope[] = |
| "https://www.googleapis.com/auth/drive.apps.readonly"; |
| const char kDriveAppsScope[] = "https://www.googleapis.com/auth/drive.apps"; |
| const char kDocsListScope[] = "https://docs.google.com/feeds/"; |
| |
| // Mime type to create a directory. |
| const char kFolderMimeType[] = "application/vnd.google-apps.folder"; |
| |
| // Max number of Team Drive entries to be fetched in a single http request. |
| const int kMaxNumTeamDriveResourcePerRequest = 100; |
| |
| // Max number of file entries to be fetched in a single http request. |
| // |
| // The larger the number is, |
| // - The total running time to fetch the whole file list will become shorter. |
| // - The running time for a single request tends to become longer. |
| // Since the file list fetching is a completely background task, for our side, |
| // only the total time matters. However, the server seems to have a time limit |
| // per single request, which disables us to set the largest value (1000). |
| // TODO(kinaba): make it larger when the server gets faster. |
| const int kMaxNumFilesResourcePerRequest = 300; |
| const int kMaxNumFilesResourcePerRequestForSearch = 100; |
| |
| // For performance, we declare all fields we use. |
| const char kAboutResourceFields[] = |
| "kind,quotaBytesTotal,quotaBytesUsedAggregate,largestChangeId,rootFolderId"; |
| const char kFileResourceFields[] = |
| "kind,id,title,createdDate,sharedWithMeDate,mimeType," |
| "md5Checksum,fileSize,labels/trashed,labels/starred," |
| "imageMediaMetadata/width," |
| "imageMediaMetadata/height,imageMediaMetadata/rotation,etag," |
| "parents(id,parentLink),alternateLink," |
| "modifiedDate,lastViewedByMeDate,shared,modifiedByMeDate"; |
| const char kFileResourceOpenWithLinksFields[] = |
| "kind,id,openWithLinks/*"; |
| const char kFileResourceShareLinkFields[] = |
| "kind,id,shareLink"; |
| const char kFileListFields[] = |
| "kind,items(kind,id,title,createdDate,sharedWithMeDate," |
| "mimeType,md5Checksum,fileSize,labels/trashed,labels/starred," |
| "imageMediaMetadata/width," |
| "imageMediaMetadata/height,imageMediaMetadata/rotation,etag," |
| "parents(id,parentLink),alternateLink," |
| "modifiedDate,lastViewedByMeDate,shared,modifiedByMeDate,capabilities)," |
| "nextLink"; |
| const char kChangeListFields[] = |
| "kind,items(type,file(kind,id,title,createdDate,sharedWithMeDate," |
| "mimeType,md5Checksum,fileSize,labels/trashed,labels/starred," |
| "imageMediaMetadata/width," |
| "imageMediaMetadata/height,imageMediaMetadata/rotation,etag," |
| "parents(id,parentLink),alternateLink,modifiedDate," |
| "lastViewedByMeDate,shared,modifiedByMeDate,capabilities)," |
| "teamDrive(kind,id,name,capabilities),teamDriveId," |
| "deleted,id,fileId,modificationDate),nextLink," |
| "largestChangeId,newStartPageToken"; |
| const char kTeamDrivesListFields[] = |
| "nextPageToken,kind,items(kind,id,name,capabilities)"; |
| |
| void ExtractOpenUrlAndRun(const std::string& app_id, |
| const AuthorizeAppCallback& callback, |
| DriveApiErrorCode error, |
| std::unique_ptr<FileResource> value) { |
| DCHECK(!callback.is_null()); |
| |
| if (!value) { |
| callback.Run(error, GURL()); |
| return; |
| } |
| |
| const std::vector<FileResource::OpenWithLink>& open_with_links = |
| value->open_with_links(); |
| for (size_t i = 0; i < open_with_links.size(); ++i) { |
| if (open_with_links[i].app_id == app_id) { |
| callback.Run(HTTP_SUCCESS, open_with_links[i].open_url); |
| return; |
| } |
| } |
| |
| // Not found. |
| callback.Run(DRIVE_OTHER_ERROR, GURL()); |
| } |
| |
| void ExtractShareUrlAndRun(const GetShareUrlCallback& callback, |
| DriveApiErrorCode error, |
| std::unique_ptr<FileResource> value) { |
| callback.Run(error, value ? value->share_link() : GURL()); |
| } |
| |
| // Ignores the |entry|, and runs the |callback|. |
| void EntryActionCallbackAdapter(const EntryActionCallback& callback, |
| DriveApiErrorCode error, |
| std::unique_ptr<FileResource> entry) { |
| callback.Run(error); |
| } |
| |
| // The resource ID for the root directory for Drive API is defined in the spec: |
| // https://developers.google.com/drive/folder |
| const char kDriveApiRootDirectoryResourceId[] = "root"; |
| |
| } // namespace |
| |
| BatchRequestConfigurator::BatchRequestConfigurator( |
| const base::WeakPtr<google_apis::drive::BatchUploadRequest>& batch_request, |
| base::SequencedTaskRunner* task_runner, |
| const google_apis::DriveApiUrlGenerator& url_generator, |
| const google_apis::CancelCallback& cancel_callback) |
| : batch_request_(batch_request), |
| task_runner_(task_runner), |
| url_generator_(url_generator), |
| cancel_callback_(cancel_callback) { |
| } |
| |
| BatchRequestConfigurator::~BatchRequestConfigurator() { |
| DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); |
| // The batch requst has not been committed. |
| if (batch_request_) |
| cancel_callback_.Run(); |
| } |
| |
| google_apis::CancelCallback BatchRequestConfigurator::MultipartUploadNewFile( |
| const std::string& content_type, |
| int64_t content_length, |
| const std::string& parent_resource_id, |
| const std::string& title, |
| const base::FilePath& local_file_path, |
| const UploadNewFileOptions& options, |
| const google_apis::FileResourceCallback& callback, |
| const google_apis::ProgressCallback& progress_callback) { |
| DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); |
| DCHECK(!callback.is_null()); |
| |
| std::unique_ptr<google_apis::BatchableDelegate> delegate( |
| new google_apis::drive::MultipartUploadNewFileDelegate( |
| task_runner_.get(), title, parent_resource_id, content_type, |
| content_length, options.modified_date, options.last_viewed_by_me_date, |
| local_file_path, options.properties, url_generator_, callback, |
| progress_callback)); |
| // Batch request can be null when pre-authorization for the requst is failed |
| // in request sender. |
| if (batch_request_) |
| batch_request_->AddRequest(delegate.release()); |
| else |
| delegate->NotifyError(DRIVE_OTHER_ERROR); |
| return cancel_callback_; |
| } |
| |
| google_apis::CancelCallback |
| BatchRequestConfigurator::MultipartUploadExistingFile( |
| const std::string& content_type, |
| int64_t content_length, |
| const std::string& resource_id, |
| const base::FilePath& local_file_path, |
| const UploadExistingFileOptions& options, |
| const google_apis::FileResourceCallback& callback, |
| const google_apis::ProgressCallback& progress_callback) { |
| DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); |
| DCHECK(!callback.is_null()); |
| |
| std::unique_ptr<google_apis::BatchableDelegate> delegate( |
| new google_apis::drive::MultipartUploadExistingFileDelegate( |
| task_runner_.get(), options.title, resource_id, |
| options.parent_resource_id, content_type, content_length, |
| options.modified_date, options.last_viewed_by_me_date, |
| local_file_path, options.etag, options.properties, url_generator_, |
| callback, progress_callback)); |
| // Batch request can be null when pre-authorization for the requst is failed |
| // in request sender. |
| if (batch_request_) |
| batch_request_->AddRequest(delegate.release()); |
| else |
| delegate->NotifyError(DRIVE_OTHER_ERROR); |
| return cancel_callback_; |
| } |
| |
| void BatchRequestConfigurator::Commit() { |
| DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); |
| if (!batch_request_) |
| return; |
| batch_request_->Commit(); |
| batch_request_.reset(); |
| } |
| |
| DriveAPIService::DriveAPIService( |
| OAuth2TokenService* oauth2_token_service, |
| net::URLRequestContextGetter* url_request_context_getter, |
| scoped_refptr<network::SharedURLLoaderFactory> url_loader_factory, |
| base::SequencedTaskRunner* blocking_task_runner, |
| const GURL& base_url, |
| const GURL& base_thumbnail_url, |
| const std::string& custom_user_agent, |
| const net::NetworkTrafficAnnotationTag& traffic_annotation) |
| : oauth2_token_service_(oauth2_token_service), |
| url_request_context_getter_(url_request_context_getter), |
| url_loader_factory_(url_loader_factory), |
| blocking_task_runner_(blocking_task_runner), |
| url_generator_(base_url, |
| base_thumbnail_url, |
| google_apis::GetTeamDrivesIntegrationSwitch()), |
| custom_user_agent_(custom_user_agent), |
| traffic_annotation_(traffic_annotation) {} |
| |
| DriveAPIService::~DriveAPIService() { |
| DCHECK(thread_checker_.CalledOnValidThread()); |
| if (sender_) |
| sender_->auth_service()->RemoveObserver(this); |
| } |
| |
| void DriveAPIService::Initialize(const std::string& account_id) { |
| DCHECK(thread_checker_.CalledOnValidThread()); |
| |
| std::vector<std::string> scopes; |
| scopes.push_back(kDriveScope); |
| scopes.push_back(kDriveAppsReadonlyScope); |
| scopes.push_back(kDriveAppsScope); |
| |
| // Note: The following scope is used to support GetShareUrl on Drive API v2. |
| // Unfortunately, there is no support on Drive API v2, so we need to fall back |
| // to GData WAPI for the GetShareUrl. |
| scopes.push_back(kDocsListScope); |
| |
| sender_ = std::make_unique<RequestSender>( |
| new google_apis::AuthService(oauth2_token_service_, account_id, |
| url_request_context_getter_.get(), |
| url_loader_factory_, scopes), |
| url_request_context_getter_.get(), blocking_task_runner_.get(), |
| custom_user_agent_, traffic_annotation_); |
| sender_->auth_service()->AddObserver(this); |
| |
| files_list_request_runner_ = |
| std::make_unique<FilesListRequestRunner>(sender_.get(), url_generator_); |
| } |
| |
| void DriveAPIService::AddObserver(DriveServiceObserver* observer) { |
| observers_.AddObserver(observer); |
| } |
| |
| void DriveAPIService::RemoveObserver(DriveServiceObserver* observer) { |
| observers_.RemoveObserver(observer); |
| } |
| |
| bool DriveAPIService::CanSendRequest() const { |
| DCHECK(thread_checker_.CalledOnValidThread()); |
| |
| return HasRefreshToken(); |
| } |
| |
| std::string DriveAPIService::GetRootResourceId() const { |
| return kDriveApiRootDirectoryResourceId; |
| } |
| |
| CancelCallback DriveAPIService::GetAllTeamDriveList( |
| const TeamDriveListCallback& callback) { |
| DCHECK(thread_checker_.CalledOnValidThread()); |
| DCHECK(!callback.is_null()); |
| |
| std::unique_ptr<TeamDriveListRequest> request = |
| std::make_unique<TeamDriveListRequest>(sender_.get(), url_generator_, |
| callback); |
| request->set_max_results(kMaxNumTeamDriveResourcePerRequest); |
| request->set_fields(kTeamDrivesListFields); |
| return sender_->StartRequestWithAuthRetry(std::move(request)); |
| } |
| |
| CancelCallback DriveAPIService::GetAllFileList( |
| const std::string& team_drive_id, |
| const FileListCallback& callback) { |
| DCHECK(thread_checker_.CalledOnValidThread()); |
| DCHECK(!callback.is_null()); |
| |
| std::unique_ptr<FilesListRequest> request = |
| std::make_unique<FilesListRequest>(sender_.get(), url_generator_, |
| callback); |
| request->set_max_results(kMaxNumFilesResourcePerRequest); |
| request->set_q("trashed = false"); // Exclude trashed files. |
| request->set_fields(kFileListFields); |
| if (team_drive_id.empty()) { |
| request->set_corpora(google_apis::FilesListCorpora::DEFAULT); |
| } else { |
| request->set_team_drive_id(team_drive_id); |
| request->set_corpora(google_apis::FilesListCorpora::TEAM_DRIVE); |
| } |
| return sender_->StartRequestWithAuthRetry(std::move(request)); |
| } |
| |
| CancelCallback DriveAPIService::GetFileListInDirectory( |
| const std::string& directory_resource_id, |
| const FileListCallback& callback) { |
| DCHECK(thread_checker_.CalledOnValidThread()); |
| DCHECK(!directory_resource_id.empty()); |
| DCHECK(!callback.is_null()); |
| |
| // TODO(yamaguchi): Use FileListScope::CreateForTeamDrive instead of |
| // kAllTeamDrives for efficiency. It'll require to add a new parameter to tell |
| // which team drive the directory resource belongs to. |
| FilesListCorpora corpora = |
| (google_apis::GetTeamDrivesIntegrationSwitch() == |
| google_apis::TEAM_DRIVES_INTEGRATION_ENABLED) |
| ? google_apis::FilesListCorpora::ALL_TEAM_DRIVES |
| : google_apis::FilesListCorpora::DEFAULT; |
| |
| // Because children.list method on Drive API v2 returns only the list of |
| // children's references, but we need all file resource list. |
| // So, here we use files.list method instead, with setting parents query. |
| // After the migration from GData WAPI to Drive API v2, we should clean the |
| // code up by moving the responsibility to include "parents" in the query |
| // to client side. |
| // We aren't interested in files in trash in this context, neither. |
| return files_list_request_runner_->CreateAndStartWithSizeBackoff( |
| kMaxNumFilesResourcePerRequest, corpora, std::string(), |
| base::StringPrintf( |
| "'%s' in parents and trashed = false", |
| util::EscapeQueryStringValue(directory_resource_id).c_str()), |
| kFileListFields, callback); |
| } |
| |
| CancelCallback DriveAPIService::Search( |
| const std::string& search_query, |
| const FileListCallback& callback) { |
| DCHECK(thread_checker_.CalledOnValidThread()); |
| DCHECK(!search_query.empty()); |
| DCHECK(!callback.is_null()); |
| |
| FilesListCorpora corpora = |
| (google_apis::GetTeamDrivesIntegrationSwitch() == |
| google_apis::TEAM_DRIVES_INTEGRATION_ENABLED) |
| ? google_apis::FilesListCorpora::ALL_TEAM_DRIVES |
| : google_apis::FilesListCorpora::DEFAULT; |
| |
| std::string query = util::TranslateQuery(search_query); |
| if (!query.empty()) |
| query += " and "; |
| query += "trashed = false"; |
| |
| return files_list_request_runner_->CreateAndStartWithSizeBackoff( |
| kMaxNumFilesResourcePerRequestForSearch, corpora, std::string(), query, |
| kFileListFields, callback); |
| } |
| |
| CancelCallback DriveAPIService::SearchByTitle( |
| const std::string& title, |
| const std::string& directory_resource_id, |
| const FileListCallback& callback) { |
| DCHECK(thread_checker_.CalledOnValidThread()); |
| DCHECK(!title.empty()); |
| DCHECK(!callback.is_null()); |
| |
| std::string query; |
| base::StringAppendF(&query, "title = '%s'", |
| util::EscapeQueryStringValue(title).c_str()); |
| if (!directory_resource_id.empty()) { |
| base::StringAppendF( |
| &query, " and '%s' in parents", |
| util::EscapeQueryStringValue(directory_resource_id).c_str()); |
| } |
| query += " and trashed = false"; |
| |
| std::unique_ptr<FilesListRequest> request = |
| std::make_unique<FilesListRequest>(sender_.get(), url_generator_, |
| callback); |
| request->set_max_results(kMaxNumFilesResourcePerRequest); |
| request->set_q(query); |
| request->set_fields(kFileListFields); |
| return sender_->StartRequestWithAuthRetry(std::move(request)); |
| } |
| |
| CancelCallback DriveAPIService::GetChangeList( |
| int64_t start_changestamp, |
| const ChangeListCallback& callback) { |
| DCHECK(thread_checker_.CalledOnValidThread()); |
| DCHECK(!callback.is_null()); |
| |
| std::unique_ptr<ChangesListRequest> request = |
| std::make_unique<ChangesListRequest>(sender_.get(), url_generator_, |
| callback); |
| request->set_max_results(kMaxNumFilesResourcePerRequest); |
| request->set_start_change_id(start_changestamp); |
| request->set_fields(kChangeListFields); |
| return sender_->StartRequestWithAuthRetry(std::move(request)); |
| } |
| |
| CancelCallback DriveAPIService::GetChangeListByToken( |
| const std::string& team_drive_id, |
| const std::string& start_page_token, |
| const ChangeListCallback& callback) { |
| DCHECK(thread_checker_.CalledOnValidThread()); |
| DCHECK(!callback.is_null()); |
| |
| std::unique_ptr<ChangesListRequest> request = |
| std::make_unique<ChangesListRequest>(sender_.get(), url_generator_, |
| callback); |
| request->set_max_results(kMaxNumFilesResourcePerRequest); |
| request->set_page_token(start_page_token); |
| request->set_team_drive_id(team_drive_id); |
| request->set_fields(kChangeListFields); |
| return sender_->StartRequestWithAuthRetry(std::move(request)); |
| } |
| |
| CancelCallback DriveAPIService::GetRemainingChangeList( |
| const GURL& next_link, |
| const ChangeListCallback& callback) { |
| DCHECK(thread_checker_.CalledOnValidThread()); |
| DCHECK(!next_link.is_empty()); |
| DCHECK(!callback.is_null()); |
| |
| std::unique_ptr<ChangesListNextPageRequest> request = |
| std::make_unique<ChangesListNextPageRequest>(sender_.get(), callback); |
| request->set_next_link(next_link); |
| request->set_fields(kChangeListFields); |
| return sender_->StartRequestWithAuthRetry(std::move(request)); |
| } |
| |
| CancelCallback DriveAPIService::GetRemainingTeamDriveList( |
| const std::string& page_token, |
| const TeamDriveListCallback& callback) { |
| DCHECK(thread_checker_.CalledOnValidThread()); |
| DCHECK(!page_token.empty()); |
| DCHECK(!callback.is_null()); |
| |
| std::unique_ptr<TeamDriveListRequest> request = |
| std::make_unique<TeamDriveListRequest>(sender_.get(), url_generator_, |
| callback); |
| request->set_page_token(page_token); |
| request->set_fields(kTeamDrivesListFields); |
| return sender_->StartRequestWithAuthRetry(std::move(request)); |
| } |
| |
| CancelCallback DriveAPIService::GetRemainingFileList( |
| const GURL& next_link, |
| const FileListCallback& callback) { |
| DCHECK(thread_checker_.CalledOnValidThread()); |
| DCHECK(!next_link.is_empty()); |
| DCHECK(!callback.is_null()); |
| |
| std::unique_ptr<FilesListNextPageRequest> request = |
| std::make_unique<FilesListNextPageRequest>(sender_.get(), callback); |
| request->set_next_link(next_link); |
| request->set_fields(kFileListFields); |
| return sender_->StartRequestWithAuthRetry(std::move(request)); |
| } |
| |
| CancelCallback DriveAPIService::GetFileResource( |
| const std::string& resource_id, |
| const FileResourceCallback& callback) { |
| DCHECK(thread_checker_.CalledOnValidThread()); |
| DCHECK(!callback.is_null()); |
| |
| std::unique_ptr<FilesGetRequest> request = std::make_unique<FilesGetRequest>( |
| sender_.get(), url_generator_, google_apis::IsGoogleChromeAPIKeyUsed(), |
| callback); |
| request->set_file_id(resource_id); |
| request->set_fields(kFileResourceFields); |
| return sender_->StartRequestWithAuthRetry(std::move(request)); |
| } |
| |
| CancelCallback DriveAPIService::GetShareUrl( |
| const std::string& resource_id, |
| const GURL& embed_origin, |
| const GetShareUrlCallback& callback) { |
| DCHECK(thread_checker_.CalledOnValidThread()); |
| DCHECK(!callback.is_null()); |
| |
| if (!google_apis::IsGoogleChromeAPIKeyUsed()) { |
| LOG(ERROR) << "Only the official build of Chrome OS can open share dialogs " |
| << "from the file manager."; |
| } |
| |
| std::unique_ptr<FilesGetRequest> request = std::make_unique<FilesGetRequest>( |
| sender_.get(), url_generator_, google_apis::IsGoogleChromeAPIKeyUsed(), |
| base::Bind(&ExtractShareUrlAndRun, callback)); |
| request->set_file_id(resource_id); |
| request->set_fields(kFileResourceShareLinkFields); |
| request->set_embed_origin(embed_origin); |
| return sender_->StartRequestWithAuthRetry(std::move(request)); |
| } |
| |
| CancelCallback DriveAPIService::GetAboutResource( |
| const AboutResourceCallback& callback) { |
| DCHECK(thread_checker_.CalledOnValidThread()); |
| DCHECK(!callback.is_null()); |
| |
| std::unique_ptr<AboutGetRequest> request = std::make_unique<AboutGetRequest>( |
| sender_.get(), url_generator_, callback); |
| request->set_fields(kAboutResourceFields); |
| return sender_->StartRequestWithAuthRetry(std::move(request)); |
| } |
| |
| CancelCallback DriveAPIService::GetStartPageToken( |
| const std::string& team_drive_id, |
| const StartPageTokenCallback& callback) { |
| DCHECK(thread_checker_.CalledOnValidThread()); |
| DCHECK(!callback.is_null()); |
| |
| std::unique_ptr<StartPageTokenRequest> request = |
| std::make_unique<StartPageTokenRequest>(sender_.get(), url_generator_, |
| callback); |
| request->set_team_drive_id(team_drive_id); |
| return sender_->StartRequestWithAuthRetry(std::move(request)); |
| } |
| |
| CancelCallback DriveAPIService::GetAppList(const AppListCallback& callback) { |
| DCHECK(thread_checker_.CalledOnValidThread()); |
| DCHECK(!callback.is_null()); |
| |
| return sender_->StartRequestWithAuthRetry(std::make_unique<AppsListRequest>( |
| sender_.get(), url_generator_, google_apis::IsGoogleChromeAPIKeyUsed(), |
| callback)); |
| } |
| |
| CancelCallback DriveAPIService::DownloadFile( |
| const base::FilePath& local_cache_path, |
| const std::string& resource_id, |
| const DownloadActionCallback& download_action_callback, |
| const GetContentCallback& get_content_callback, |
| const ProgressCallback& progress_callback) { |
| DCHECK(thread_checker_.CalledOnValidThread()); |
| DCHECK(!download_action_callback.is_null()); |
| // get_content_callback may be null. |
| |
| return sender_->StartRequestWithAuthRetry( |
| std::make_unique<DownloadFileRequest>( |
| sender_.get(), url_generator_, resource_id, local_cache_path, |
| download_action_callback, get_content_callback, progress_callback)); |
| } |
| |
| CancelCallback DriveAPIService::DeleteResource( |
| const std::string& resource_id, |
| const std::string& etag, |
| const EntryActionCallback& callback) { |
| DCHECK(thread_checker_.CalledOnValidThread()); |
| DCHECK(!callback.is_null()); |
| |
| std::unique_ptr<FilesDeleteRequest> request = |
| std::make_unique<FilesDeleteRequest>(sender_.get(), url_generator_, |
| callback); |
| request->set_file_id(resource_id); |
| request->set_etag(etag); |
| return sender_->StartRequestWithAuthRetry(std::move(request)); |
| } |
| |
| CancelCallback DriveAPIService::TrashResource( |
| const std::string& resource_id, |
| const EntryActionCallback& callback) { |
| DCHECK(thread_checker_.CalledOnValidThread()); |
| DCHECK(!callback.is_null()); |
| |
| std::unique_ptr<FilesTrashRequest> request = |
| std::make_unique<FilesTrashRequest>( |
| sender_.get(), url_generator_, |
| base::Bind(&EntryActionCallbackAdapter, callback)); |
| request->set_file_id(resource_id); |
| request->set_fields(kFileResourceFields); |
| return sender_->StartRequestWithAuthRetry(std::move(request)); |
| } |
| |
| CancelCallback DriveAPIService::AddNewDirectory( |
| const std::string& parent_resource_id, |
| const std::string& directory_title, |
| const AddNewDirectoryOptions& options, |
| const FileResourceCallback& callback) { |
| DCHECK(thread_checker_.CalledOnValidThread()); |
| DCHECK(!callback.is_null()); |
| |
| std::unique_ptr<FilesInsertRequest> request = |
| std::make_unique<FilesInsertRequest>(sender_.get(), url_generator_, |
| callback); |
| request->set_visibility(options.visibility); |
| request->set_last_viewed_by_me_date(options.last_viewed_by_me_date); |
| request->set_mime_type(kFolderMimeType); |
| request->set_modified_date(options.modified_date); |
| request->add_parent(parent_resource_id); |
| request->set_title(directory_title); |
| request->set_properties(options.properties); |
| request->set_fields(kFileResourceFields); |
| return sender_->StartRequestWithAuthRetry(std::move(request)); |
| } |
| |
| CancelCallback DriveAPIService::CopyResource( |
| const std::string& resource_id, |
| const std::string& parent_resource_id, |
| const std::string& new_title, |
| const base::Time& last_modified, |
| const FileResourceCallback& callback) { |
| DCHECK(thread_checker_.CalledOnValidThread()); |
| DCHECK(!callback.is_null()); |
| |
| std::unique_ptr<FilesCopyRequest> request = |
| std::make_unique<FilesCopyRequest>(sender_.get(), url_generator_, |
| callback); |
| request->set_file_id(resource_id); |
| request->add_parent(parent_resource_id); |
| request->set_title(new_title); |
| request->set_modified_date(last_modified); |
| request->set_fields(kFileResourceFields); |
| return sender_->StartRequestWithAuthRetry(std::move(request)); |
| } |
| |
| CancelCallback DriveAPIService::UpdateResource( |
| const std::string& resource_id, |
| const std::string& parent_resource_id, |
| const std::string& new_title, |
| const base::Time& last_modified, |
| const base::Time& last_viewed_by_me, |
| const google_apis::drive::Properties& properties, |
| const FileResourceCallback& callback) { |
| DCHECK(thread_checker_.CalledOnValidThread()); |
| DCHECK(!callback.is_null()); |
| |
| std::unique_ptr<FilesPatchRequest> request = |
| std::make_unique<FilesPatchRequest>(sender_.get(), url_generator_, |
| callback); |
| request->set_file_id(resource_id); |
| request->set_title(new_title); |
| if (!parent_resource_id.empty()) |
| request->add_parent(parent_resource_id); |
| if (!last_modified.is_null()) { |
| // Need to set setModifiedDate to true to overwrite modifiedDate. |
| request->set_set_modified_date(true); |
| request->set_modified_date(last_modified); |
| } |
| if (!last_viewed_by_me.is_null()) { |
| // Need to set updateViewedDate to false, otherwise the lastViewedByMeDate |
| // will be set to the request time (not the specified time via request). |
| request->set_update_viewed_date(false); |
| request->set_last_viewed_by_me_date(last_viewed_by_me); |
| } |
| request->set_fields(kFileResourceFields); |
| request->set_properties(properties); |
| return sender_->StartRequestWithAuthRetry(std::move(request)); |
| } |
| |
| CancelCallback DriveAPIService::AddResourceToDirectory( |
| const std::string& parent_resource_id, |
| const std::string& resource_id, |
| const EntryActionCallback& callback) { |
| DCHECK(thread_checker_.CalledOnValidThread()); |
| DCHECK(!callback.is_null()); |
| |
| std::unique_ptr<ChildrenInsertRequest> request = |
| std::make_unique<ChildrenInsertRequest>(sender_.get(), url_generator_, |
| callback); |
| request->set_folder_id(parent_resource_id); |
| request->set_id(resource_id); |
| return sender_->StartRequestWithAuthRetry(std::move(request)); |
| } |
| |
| CancelCallback DriveAPIService::RemoveResourceFromDirectory( |
| const std::string& parent_resource_id, |
| const std::string& resource_id, |
| const EntryActionCallback& callback) { |
| DCHECK(thread_checker_.CalledOnValidThread()); |
| DCHECK(!callback.is_null()); |
| |
| std::unique_ptr<ChildrenDeleteRequest> request = |
| std::make_unique<ChildrenDeleteRequest>(sender_.get(), url_generator_, |
| callback); |
| request->set_child_id(resource_id); |
| request->set_folder_id(parent_resource_id); |
| return sender_->StartRequestWithAuthRetry(std::move(request)); |
| } |
| |
| CancelCallback DriveAPIService::InitiateUploadNewFile( |
| const std::string& content_type, |
| int64_t content_length, |
| const std::string& parent_resource_id, |
| const std::string& title, |
| const UploadNewFileOptions& options, |
| const InitiateUploadCallback& callback) { |
| DCHECK(thread_checker_.CalledOnValidThread()); |
| DCHECK(!callback.is_null()); |
| |
| std::unique_ptr<InitiateUploadNewFileRequest> request = |
| std::make_unique<InitiateUploadNewFileRequest>( |
| sender_.get(), url_generator_, content_type, content_length, |
| parent_resource_id, title, callback); |
| request->set_modified_date(options.modified_date); |
| request->set_last_viewed_by_me_date(options.last_viewed_by_me_date); |
| request->set_properties(options.properties); |
| return sender_->StartRequestWithAuthRetry(std::move(request)); |
| } |
| |
| CancelCallback DriveAPIService::InitiateUploadExistingFile( |
| const std::string& content_type, |
| int64_t content_length, |
| const std::string& resource_id, |
| const UploadExistingFileOptions& options, |
| const InitiateUploadCallback& callback) { |
| DCHECK(thread_checker_.CalledOnValidThread()); |
| DCHECK(!callback.is_null()); |
| |
| std::unique_ptr<InitiateUploadExistingFileRequest> request = |
| std::make_unique<InitiateUploadExistingFileRequest>( |
| sender_.get(), url_generator_, content_type, content_length, |
| resource_id, options.etag, callback); |
| request->set_parent_resource_id(options.parent_resource_id); |
| request->set_title(options.title); |
| request->set_modified_date(options.modified_date); |
| request->set_last_viewed_by_me_date(options.last_viewed_by_me_date); |
| request->set_properties(options.properties); |
| return sender_->StartRequestWithAuthRetry(std::move(request)); |
| } |
| |
| CancelCallback DriveAPIService::ResumeUpload( |
| const GURL& upload_url, |
| int64_t start_position, |
| int64_t end_position, |
| int64_t content_length, |
| const std::string& content_type, |
| const base::FilePath& local_file_path, |
| const UploadRangeCallback& callback, |
| const ProgressCallback& progress_callback) { |
| DCHECK(thread_checker_.CalledOnValidThread()); |
| DCHECK(!callback.is_null()); |
| |
| return sender_->StartRequestWithAuthRetry( |
| std::make_unique<ResumeUploadRequest>( |
| sender_.get(), upload_url, start_position, end_position, |
| content_length, content_type, local_file_path, callback, |
| progress_callback)); |
| } |
| |
| CancelCallback DriveAPIService::GetUploadStatus( |
| const GURL& upload_url, |
| int64_t content_length, |
| const UploadRangeCallback& callback) { |
| DCHECK(thread_checker_.CalledOnValidThread()); |
| DCHECK(!callback.is_null()); |
| |
| return sender_->StartRequestWithAuthRetry( |
| std::make_unique<GetUploadStatusRequest>(sender_.get(), upload_url, |
| content_length, callback)); |
| } |
| |
| CancelCallback DriveAPIService::MultipartUploadNewFile( |
| const std::string& content_type, |
| int64_t content_length, |
| const std::string& parent_resource_id, |
| const std::string& title, |
| const base::FilePath& local_file_path, |
| const drive::UploadNewFileOptions& options, |
| const FileResourceCallback& callback, |
| const google_apis::ProgressCallback& progress_callback) { |
| DCHECK(thread_checker_.CalledOnValidThread()); |
| DCHECK(!callback.is_null()); |
| |
| return sender_->StartRequestWithAuthRetry( |
| std::make_unique<google_apis::drive::SingleBatchableDelegateRequest>( |
| sender_.get(), |
| std::make_unique<google_apis::drive::MultipartUploadNewFileDelegate>( |
| sender_->blocking_task_runner(), title, parent_resource_id, |
| content_type, content_length, options.modified_date, |
| options.last_viewed_by_me_date, local_file_path, |
| options.properties, url_generator_, callback, |
| progress_callback))); |
| } |
| |
| CancelCallback DriveAPIService::MultipartUploadExistingFile( |
| const std::string& content_type, |
| int64_t content_length, |
| const std::string& resource_id, |
| const base::FilePath& local_file_path, |
| const drive::UploadExistingFileOptions& options, |
| const FileResourceCallback& callback, |
| const google_apis::ProgressCallback& progress_callback) { |
| DCHECK(thread_checker_.CalledOnValidThread()); |
| DCHECK(!callback.is_null()); |
| |
| return sender_->StartRequestWithAuthRetry( |
| std::make_unique<google_apis::drive::SingleBatchableDelegateRequest>( |
| sender_.get(), |
| std::make_unique< |
| google_apis::drive::MultipartUploadExistingFileDelegate>( |
| sender_->blocking_task_runner(), options.title, resource_id, |
| options.parent_resource_id, content_type, content_length, |
| options.modified_date, options.last_viewed_by_me_date, |
| local_file_path, options.etag, options.properties, url_generator_, |
| callback, progress_callback))); |
| } |
| |
| CancelCallback DriveAPIService::AuthorizeApp( |
| const std::string& resource_id, |
| const std::string& app_id, |
| const AuthorizeAppCallback& callback) { |
| DCHECK(thread_checker_.CalledOnValidThread()); |
| DCHECK(!callback.is_null()); |
| |
| // Files.Authorize is only available for whitelisted clients like official |
| // Google Chrome. In other cases, we fall back to Files.Get that returns the |
| // same value as Files.Authorize without doing authorization. In that case, |
| // the app can open if it was authorized by other means (from whitelisted |
| // clients or drive.google.com web UI.) |
| if (google_apis::IsGoogleChromeAPIKeyUsed()) { |
| std::unique_ptr<google_apis::drive::FilesAuthorizeRequest> request = |
| std::make_unique<google_apis::drive::FilesAuthorizeRequest>( |
| sender_.get(), url_generator_, |
| base::Bind(&ExtractOpenUrlAndRun, app_id, callback)); |
| request->set_app_id(app_id); |
| request->set_file_id(resource_id); |
| request->set_fields(kFileResourceOpenWithLinksFields); |
| return sender_->StartRequestWithAuthRetry(std::move(request)); |
| } else { |
| std::unique_ptr<FilesGetRequest> request = |
| std::make_unique<FilesGetRequest>( |
| sender_.get(), url_generator_, |
| google_apis::IsGoogleChromeAPIKeyUsed(), |
| base::Bind(&ExtractOpenUrlAndRun, app_id, callback)); |
| request->set_file_id(resource_id); |
| request->set_fields(kFileResourceOpenWithLinksFields); |
| return sender_->StartRequestWithAuthRetry(std::move(request)); |
| } |
| } |
| |
| CancelCallback DriveAPIService::UninstallApp( |
| const std::string& app_id, |
| const google_apis::EntryActionCallback& callback) { |
| DCHECK(thread_checker_.CalledOnValidThread()); |
| DCHECK(!callback.is_null()); |
| |
| std::unique_ptr<google_apis::drive::AppsDeleteRequest> request = |
| std::make_unique<google_apis::drive::AppsDeleteRequest>( |
| sender_.get(), url_generator_, callback); |
| request->set_app_id(app_id); |
| return sender_->StartRequestWithAuthRetry(std::move(request)); |
| } |
| |
| google_apis::CancelCallback DriveAPIService::AddPermission( |
| const std::string& resource_id, |
| const std::string& email, |
| google_apis::drive::PermissionRole role, |
| const google_apis::EntryActionCallback& callback) { |
| DCHECK(thread_checker_.CalledOnValidThread()); |
| DCHECK(!callback.is_null()); |
| |
| std::unique_ptr<google_apis::drive::PermissionsInsertRequest> request = |
| std::make_unique<google_apis::drive::PermissionsInsertRequest>( |
| sender_.get(), url_generator_, callback); |
| request->set_id(resource_id); |
| request->set_role(role); |
| request->set_type(google_apis::drive::PERMISSION_TYPE_USER); |
| request->set_value(email); |
| return sender_->StartRequestWithAuthRetry(std::move(request)); |
| } |
| |
| bool DriveAPIService::HasAccessToken() const { |
| DCHECK(thread_checker_.CalledOnValidThread()); |
| return sender_->auth_service()->HasAccessToken(); |
| } |
| |
| void DriveAPIService::RequestAccessToken(const AuthStatusCallback& callback) { |
| DCHECK(thread_checker_.CalledOnValidThread()); |
| DCHECK(!callback.is_null()); |
| |
| const std::string access_token = sender_->auth_service()->access_token(); |
| if (!access_token.empty()) { |
| callback.Run(google_apis::HTTP_NOT_MODIFIED, access_token); |
| return; |
| } |
| |
| // Retrieve the new auth token. |
| sender_->auth_service()->StartAuthentication(callback); |
| } |
| |
| bool DriveAPIService::HasRefreshToken() const { |
| DCHECK(thread_checker_.CalledOnValidThread()); |
| return sender_->auth_service()->HasRefreshToken(); |
| } |
| |
| void DriveAPIService::ClearAccessToken() { |
| DCHECK(thread_checker_.CalledOnValidThread()); |
| sender_->auth_service()->ClearAccessToken(); |
| } |
| |
| void DriveAPIService::ClearRefreshToken() { |
| DCHECK(thread_checker_.CalledOnValidThread()); |
| sender_->auth_service()->ClearRefreshToken(); |
| } |
| |
| void DriveAPIService::OnOAuth2RefreshTokenChanged() { |
| DCHECK(thread_checker_.CalledOnValidThread()); |
| if (CanSendRequest()) { |
| for (auto& observer : observers_) |
| observer.OnReadyToSendRequests(); |
| } else if (!HasRefreshToken()) { |
| for (auto& observer : observers_) |
| observer.OnRefreshTokenInvalid(); |
| } |
| } |
| |
| std::unique_ptr<BatchRequestConfiguratorInterface> |
| DriveAPIService::StartBatchRequest() { |
| std::unique_ptr<google_apis::drive::BatchUploadRequest> request = |
| std::make_unique<google_apis::drive::BatchUploadRequest>(sender_.get(), |
| url_generator_); |
| const base::WeakPtr<google_apis::drive::BatchUploadRequest> weak_ref = |
| request->GetWeakPtrAsBatchUploadRequest(); |
| // Have sender_ manage the lifetime of the request. |
| // TODO(hirono): Currently we need to pass the ownership of the request to |
| // RequestSender before the request is committed because the request has a |
| // reference to RequestSender and we should ensure to delete the request when |
| // the sender is deleted. Resolve the circulating dependency and fix it. |
| const google_apis::CancelCallback callback = |
| sender_->StartRequestWithAuthRetry(std::move(request)); |
| return std::make_unique<BatchRequestConfigurator>( |
| weak_ref, sender_->blocking_task_runner(), url_generator_, callback); |
| } |
| |
| } // namespace drive |