blob: 48ee6b58d687dcf66979f6339bc54b8899597a08 [file] [log] [blame]
// 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 "chrome/browser/drive/drive_api_service.h"
#include <string>
#include <vector>
#include "base/bind.h"
#include "base/strings/stringprintf.h"
#include "base/task_runner_util.h"
#include "base/values.h"
#include "chrome/browser/drive/drive_api_util.h"
#include "chrome/browser/google_apis/auth_service.h"
#include "chrome/browser/google_apis/drive_api_parser.h"
#include "chrome/browser/google_apis/drive_api_requests.h"
#include "chrome/browser/google_apis/gdata_errorcode.h"
#include "chrome/browser/google_apis/gdata_wapi_parser.h"
#include "chrome/browser/google_apis/gdata_wapi_requests.h"
#include "chrome/browser/google_apis/request_sender.h"
#include "content/public/browser/browser_thread.h"
using content::BrowserThread;
using google_apis::AppList;
using google_apis::AuthStatusCallback;
using google_apis::AuthorizeAppCallback;
using google_apis::CancelCallback;
using google_apis::ChangeList;
using google_apis::DownloadActionCallback;
using google_apis::EntryActionCallback;
using google_apis::FileList;
using google_apis::FileResource;
using google_apis::GDATA_OTHER_ERROR;
using google_apis::GDATA_PARSE_ERROR;
using google_apis::GDataErrorCode;
using google_apis::GetAboutRequest;
using google_apis::GetAboutResourceCallback;
using google_apis::GetAppListCallback;
using google_apis::GetApplistRequest;
using google_apis::GetChangelistRequest;
using google_apis::GetContentCallback;
using google_apis::GetFileRequest;
using google_apis::GetFilelistRequest;
using google_apis::GetResourceEntryCallback;
using google_apis::GetResourceEntryRequest;
using google_apis::GetResourceListCallback;
using google_apis::GetShareUrlCallback;
using google_apis::HTTP_NOT_IMPLEMENTED;
using google_apis::HTTP_SUCCESS;
using google_apis::InitiateUploadCallback;
using google_apis::Link;
using google_apis::ProgressCallback;
using google_apis::RequestSender;
using google_apis::ResourceEntry;
using google_apis::ResourceList;
using google_apis::UploadRangeCallback;
using google_apis::UploadRangeResponse;
using google_apis::drive::ContinueGetFileListRequest;
using google_apis::drive::CopyResourceRequest;
using google_apis::drive::CreateDirectoryRequest;
using google_apis::drive::DeleteResourceRequest;
using google_apis::drive::DownloadFileRequest;
using google_apis::drive::GetUploadStatusRequest;
using google_apis::drive::InitiateUploadExistingFileRequest;
using google_apis::drive::InitiateUploadNewFileRequest;
using google_apis::drive::InsertResourceRequest;
using google_apis::drive::MoveResourceRequest;
using google_apis::drive::ResumeUploadRequest;
using google_apis::drive::TouchResourceRequest;
using google_apis::drive::TrashResourceRequest;
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";
// Expected max number of files resources in a http request.
// Be careful not to use something too small because it might overload the
// server. Be careful not to use something too large because it takes longer
// time to fetch the result without UI response.
const int kMaxNumFilesResourcePerRequest = 500;
const int kMaxNumFilesResourcePerRequestForSearch = 50;
scoped_ptr<ResourceList> ParseChangeListJsonToResourceList(
scoped_ptr<base::Value> value) {
scoped_ptr<ChangeList> change_list(ChangeList::CreateFrom(*value));
if (!change_list) {
return scoped_ptr<ResourceList>();
}
return ResourceList::CreateFromChangeList(*change_list);
}
scoped_ptr<ResourceList> ParseFileListJsonToResourceList(
scoped_ptr<base::Value> value) {
scoped_ptr<FileList> file_list(FileList::CreateFrom(*value));
if (!file_list) {
return scoped_ptr<ResourceList>();
}
return ResourceList::CreateFromFileList(*file_list);
}
// Parses JSON value representing either ChangeList or FileList into
// ResourceList.
scoped_ptr<ResourceList> ParseResourceListOnBlockingPool(
scoped_ptr<base::Value> value) {
DCHECK(value);
// Dispatch the parsing based on kind field.
if (ChangeList::HasChangeListKind(*value)) {
return ParseChangeListJsonToResourceList(value.Pass());
}
if (FileList::HasFileListKind(*value)) {
return ParseFileListJsonToResourceList(value.Pass());
}
// The value type is unknown, so give up to parse and return an error.
return scoped_ptr<ResourceList>();
}
// Callback invoked when the parsing of resource list is completed,
// regardless whether it is succeeded or not.
void DidParseResourceListOnBlockingPool(
const GetResourceListCallback& callback,
scoped_ptr<ResourceList> resource_list) {
GDataErrorCode error = resource_list ? HTTP_SUCCESS : GDATA_PARSE_ERROR;
callback.Run(error, resource_list.Pass());
}
// Sends a task to parse the JSON value into ResourceList on blocking pool,
// with a callback which is called when the task is done.
void ParseResourceListOnBlockingPoolAndRun(
scoped_refptr<base::TaskRunner> blocking_task_runner,
const GetResourceListCallback& callback,
GDataErrorCode error,
scoped_ptr<base::Value> value) {
DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
DCHECK(!callback.is_null());
if (error != HTTP_SUCCESS) {
// An error occurs, so run callback immediately.
callback.Run(error, scoped_ptr<ResourceList>());
return;
}
PostTaskAndReplyWithResult(
blocking_task_runner.get(),
FROM_HERE,
base::Bind(&ParseResourceListOnBlockingPool, base::Passed(&value)),
base::Bind(&DidParseResourceListOnBlockingPool, callback));
}
// Parses the FileResource value to ResourceEntry and runs |callback| on the
// UI thread.
void ParseResourceEntryAndRun(
const GetResourceEntryCallback& callback,
GDataErrorCode error,
scoped_ptr<FileResource> value) {
DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
DCHECK(!callback.is_null());
if (!value) {
callback.Run(error, scoped_ptr<ResourceEntry>());
return;
}
// Converting to ResourceEntry is cheap enough to do on UI thread.
scoped_ptr<ResourceEntry> entry =
ResourceEntry::CreateFromFileResource(*value);
if (!entry) {
callback.Run(GDATA_PARSE_ERROR, scoped_ptr<ResourceEntry>());
return;
}
callback.Run(error, entry.Pass());
}
// Parses the JSON value to AppList runs |callback| on the UI thread
// once parsing is done.
void ParseAppListAndRun(const google_apis::GetAppListCallback& callback,
google_apis::GDataErrorCode error,
scoped_ptr<base::Value> value) {
DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
DCHECK(!callback.is_null());
if (!value) {
callback.Run(error, scoped_ptr<google_apis::AppList>());
return;
}
// Parsing AppList is cheap enough to do on UI thread.
scoped_ptr<google_apis::AppList> app_list =
google_apis::AppList::CreateFrom(*value);
if (!app_list) {
callback.Run(google_apis::GDATA_PARSE_ERROR,
scoped_ptr<google_apis::AppList>());
return;
}
callback.Run(error, app_list.Pass());
}
// Parses the FileResource value to ResourceEntry for upload range request,
// and runs |callback| on the UI thread.
void ParseResourceEntryForUploadRangeAndRun(
const UploadRangeCallback& callback,
const UploadRangeResponse& response,
scoped_ptr<FileResource> value) {
DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
DCHECK(!callback.is_null());
if (!value) {
callback.Run(response, scoped_ptr<ResourceEntry>());
return;
}
// Converting to ResourceEntry is cheap enough to do on UI thread.
scoped_ptr<ResourceEntry> entry =
ResourceEntry::CreateFromFileResource(*value);
if (!entry) {
callback.Run(UploadRangeResponse(GDATA_PARSE_ERROR,
response.start_position_received,
response.end_position_received),
scoped_ptr<ResourceEntry>());
return;
}
callback.Run(response, entry.Pass());
}
void ExtractOpenUrlAndRun(const std::string& app_id,
const AuthorizeAppCallback& callback,
GDataErrorCode error,
scoped_ptr<FileResource> value) {
DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
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(GDATA_OTHER_ERROR, GURL());
}
// Ignores the |entry|, and runs the |callback|.
void EntryActionCallbackAdapter(
const EntryActionCallback& callback,
GDataErrorCode error, scoped_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
DriveAPIService::DriveAPIService(
OAuth2TokenService* oauth2_token_service,
net::URLRequestContextGetter* url_request_context_getter,
base::TaskRunner* blocking_task_runner,
const GURL& base_url,
const GURL& base_download_url,
const GURL& wapi_base_url,
const std::string& custom_user_agent)
: oauth2_token_service_(oauth2_token_service),
url_request_context_getter_(url_request_context_getter),
blocking_task_runner_(blocking_task_runner),
url_generator_(base_url, base_download_url),
wapi_url_generator_(wapi_base_url, base_download_url),
custom_user_agent_(custom_user_agent) {
DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
}
DriveAPIService::~DriveAPIService() {
DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
if (sender_.get())
sender_->auth_service()->RemoveObserver(this);
}
void DriveAPIService::Initialize() {
DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
std::vector<std::string> scopes;
scopes.push_back(kDriveScope);
scopes.push_back(kDriveAppsReadonlyScope);
// GData WAPI token. These are for GetShareUrl().
scopes.push_back(util::kDocsListScope);
scopes.push_back(util::kDriveAppsScope);
sender_.reset(new RequestSender(
new google_apis::AuthService(
oauth2_token_service_, url_request_context_getter_, scopes),
url_request_context_getter_,
blocking_task_runner_.get(),
custom_user_agent_));
sender_->auth_service()->AddObserver(this);
}
void DriveAPIService::AddObserver(DriveServiceObserver* observer) {
observers_.AddObserver(observer);
}
void DriveAPIService::RemoveObserver(DriveServiceObserver* observer) {
observers_.RemoveObserver(observer);
}
bool DriveAPIService::CanSendRequest() const {
DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
return HasRefreshToken();
}
std::string DriveAPIService::CanonicalizeResourceId(
const std::string& resource_id) const {
return drive::util::CanonicalizeResourceId(resource_id);
}
std::string DriveAPIService::GetRootResourceId() const {
return kDriveApiRootDirectoryResourceId;
}
CancelCallback DriveAPIService::GetAllResourceList(
const GetResourceListCallback& callback) {
DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
DCHECK(!callback.is_null());
// The simplest way to fetch the all resources list looks files.list method,
// but it seems impossible to know the returned list's changestamp.
// Thus, instead, we use changes.list method with includeDeleted=false here.
// The returned list should contain only resources currently existing.
return sender_->StartRequestWithRetry(
new GetChangelistRequest(
sender_.get(),
url_generator_,
false, // include deleted
0,
kMaxNumFilesResourcePerRequest,
base::Bind(&ParseResourceListOnBlockingPoolAndRun,
blocking_task_runner_,
callback)));
}
CancelCallback DriveAPIService::GetResourceListInDirectory(
const std::string& directory_resource_id,
const GetResourceListCallback& callback) {
DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
DCHECK(!directory_resource_id.empty());
DCHECK(!callback.is_null());
// 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 sender_->StartRequestWithRetry(
new GetFilelistRequest(
sender_.get(),
url_generator_,
base::StringPrintf(
"'%s' in parents and trashed = false",
drive::util::EscapeQueryStringValue(
directory_resource_id).c_str()),
kMaxNumFilesResourcePerRequest,
base::Bind(&ParseResourceListOnBlockingPoolAndRun,
blocking_task_runner_,
callback)));
}
CancelCallback DriveAPIService::Search(
const std::string& search_query,
const GetResourceListCallback& callback) {
DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
DCHECK(!search_query.empty());
DCHECK(!callback.is_null());
return sender_->StartRequestWithRetry(
new GetFilelistRequest(
sender_.get(),
url_generator_,
drive::util::TranslateQuery(search_query),
kMaxNumFilesResourcePerRequestForSearch,
base::Bind(&ParseResourceListOnBlockingPoolAndRun,
blocking_task_runner_,
callback)));
}
CancelCallback DriveAPIService::SearchByTitle(
const std::string& title,
const std::string& directory_resource_id,
const GetResourceListCallback& callback) {
DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
DCHECK(!title.empty());
DCHECK(!callback.is_null());
std::string query;
base::StringAppendF(&query, "title = '%s'",
drive::util::EscapeQueryStringValue(title).c_str());
if (!directory_resource_id.empty()) {
base::StringAppendF(
&query, " and '%s' in parents",
drive::util::EscapeQueryStringValue(directory_resource_id).c_str());
}
query += " and trashed = false";
return sender_->StartRequestWithRetry(
new GetFilelistRequest(
sender_.get(),
url_generator_,
query,
kMaxNumFilesResourcePerRequest,
base::Bind(&ParseResourceListOnBlockingPoolAndRun,
blocking_task_runner_,
callback)));
}
CancelCallback DriveAPIService::GetChangeList(
int64 start_changestamp,
const GetResourceListCallback& callback) {
DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
DCHECK(!callback.is_null());
return sender_->StartRequestWithRetry(
new GetChangelistRequest(
sender_.get(),
url_generator_,
true, // include deleted
start_changestamp,
kMaxNumFilesResourcePerRequest,
base::Bind(&ParseResourceListOnBlockingPoolAndRun,
blocking_task_runner_,
callback)));
}
CancelCallback DriveAPIService::ContinueGetResourceList(
const GURL& override_url,
const GetResourceListCallback& callback) {
DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
DCHECK(!callback.is_null());
return sender_->StartRequestWithRetry(
new ContinueGetFileListRequest(
sender_.get(),
override_url,
base::Bind(&ParseResourceListOnBlockingPoolAndRun,
blocking_task_runner_,
callback)));
}
CancelCallback DriveAPIService::GetResourceEntry(
const std::string& resource_id,
const GetResourceEntryCallback& callback) {
DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
DCHECK(!callback.is_null());
return sender_->StartRequestWithRetry(new GetFileRequest(
sender_.get(),
url_generator_,
resource_id,
base::Bind(&ParseResourceEntryAndRun, callback)));
}
CancelCallback DriveAPIService::GetShareUrl(
const std::string& resource_id,
const GURL& embed_origin,
const GetShareUrlCallback& callback) {
DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
DCHECK(!callback.is_null());
// Unfortunately "share url" is not yet supported on Drive API v2.
// So, as a fallback, we use GData WAPI protocol for this method.
// TODO(hidehiko): Get rid of this implementation when share url is
// supported on Drive API v2.
return sender_->StartRequestWithRetry(
new GetResourceEntryRequest(sender_.get(),
wapi_url_generator_,
resource_id,
embed_origin,
base::Bind(&util::ParseShareUrlAndRun,
callback)));
}
CancelCallback DriveAPIService::GetAboutResource(
const GetAboutResourceCallback& callback) {
DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
DCHECK(!callback.is_null());
return sender_->StartRequestWithRetry(
new GetAboutRequest(
sender_.get(),
url_generator_,
callback));
}
CancelCallback DriveAPIService::GetAppList(const GetAppListCallback& callback) {
DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
DCHECK(!callback.is_null());
return sender_->StartRequestWithRetry(new GetApplistRequest(
sender_.get(),
url_generator_,
base::Bind(&ParseAppListAndRun, 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(BrowserThread::CurrentlyOn(BrowserThread::UI));
DCHECK(!download_action_callback.is_null());
// get_content_callback may be null.
return sender_->StartRequestWithRetry(
new 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(BrowserThread::CurrentlyOn(BrowserThread::UI));
DCHECK(!callback.is_null());
return sender_->StartRequestWithRetry(new TrashResourceRequest(
sender_.get(),
url_generator_,
resource_id,
callback));
}
CancelCallback DriveAPIService::AddNewDirectory(
const std::string& parent_resource_id,
const std::string& directory_title,
const GetResourceEntryCallback& callback) {
DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
DCHECK(!callback.is_null());
return sender_->StartRequestWithRetry(
new CreateDirectoryRequest(
sender_.get(),
url_generator_,
parent_resource_id,
directory_title,
base::Bind(&ParseResourceEntryAndRun, callback)));
}
CancelCallback DriveAPIService::CopyResource(
const std::string& resource_id,
const std::string& parent_resource_id,
const std::string& new_title,
const GetResourceEntryCallback& callback) {
DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
DCHECK(!callback.is_null());
return sender_->StartRequestWithRetry(
new CopyResourceRequest(
sender_.get(),
url_generator_,
resource_id,
parent_resource_id,
new_title,
base::Bind(&ParseResourceEntryAndRun, callback)));
}
CancelCallback DriveAPIService::CopyHostedDocument(
const std::string& resource_id,
const std::string& new_title,
const GetResourceEntryCallback& callback) {
DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
DCHECK(!callback.is_null());
return sender_->StartRequestWithRetry(
new CopyResourceRequest(
sender_.get(),
url_generator_,
resource_id,
std::string(), // parent_resource_id.
new_title,
base::Bind(&ParseResourceEntryAndRun, callback)));
}
CancelCallback DriveAPIService::MoveResource(
const std::string& resource_id,
const std::string& parent_resource_id,
const std::string& new_title,
const GetResourceEntryCallback& callback) {
DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
DCHECK(!callback.is_null());
return sender_->StartRequestWithRetry(
new MoveResourceRequest(
sender_.get(),
url_generator_,
resource_id,
parent_resource_id,
new_title,
base::Bind(&ParseResourceEntryAndRun, callback)));
}
CancelCallback DriveAPIService::RenameResource(
const std::string& resource_id,
const std::string& new_title,
const EntryActionCallback& callback) {
DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
DCHECK(!callback.is_null());
return sender_->StartRequestWithRetry(
new MoveResourceRequest(
sender_.get(),
url_generator_,
resource_id,
std::string(),
new_title,
base::Bind(&EntryActionCallbackAdapter, callback)));
}
CancelCallback DriveAPIService::TouchResource(
const std::string& resource_id,
const base::Time& modified_date,
const base::Time& last_viewed_by_me_date,
const GetResourceEntryCallback& callback) {
DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
DCHECK(!modified_date.is_null());
DCHECK(!last_viewed_by_me_date.is_null());
DCHECK(!callback.is_null());
return sender_->StartRequestWithRetry(
new TouchResourceRequest(
sender_.get(),
url_generator_,
resource_id,
modified_date,
last_viewed_by_me_date,
base::Bind(&ParseResourceEntryAndRun, callback)));
}
CancelCallback DriveAPIService::AddResourceToDirectory(
const std::string& parent_resource_id,
const std::string& resource_id,
const EntryActionCallback& callback) {
DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
DCHECK(!callback.is_null());
return sender_->StartRequestWithRetry(
new InsertResourceRequest(
sender_.get(),
url_generator_,
parent_resource_id,
resource_id,
callback));
}
CancelCallback DriveAPIService::RemoveResourceFromDirectory(
const std::string& parent_resource_id,
const std::string& resource_id,
const EntryActionCallback& callback) {
DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
DCHECK(!callback.is_null());
return sender_->StartRequestWithRetry(
new DeleteResourceRequest(
sender_.get(),
url_generator_,
parent_resource_id,
resource_id,
callback));
}
CancelCallback DriveAPIService::InitiateUploadNewFile(
const std::string& content_type,
int64 content_length,
const std::string& parent_resource_id,
const std::string& title,
const InitiateUploadCallback& callback) {
DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
DCHECK(!callback.is_null());
return sender_->StartRequestWithRetry(
new InitiateUploadNewFileRequest(
sender_.get(),
url_generator_,
content_type,
content_length,
parent_resource_id,
title,
callback));
}
CancelCallback DriveAPIService::InitiateUploadExistingFile(
const std::string& content_type,
int64 content_length,
const std::string& resource_id,
const std::string& etag,
const InitiateUploadCallback& callback) {
DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
DCHECK(!callback.is_null());
return sender_->StartRequestWithRetry(
new InitiateUploadExistingFileRequest(
sender_.get(),
url_generator_,
content_type,
content_length,
resource_id,
etag,
callback));
}
CancelCallback DriveAPIService::ResumeUpload(
const GURL& upload_url,
int64 start_position,
int64 end_position,
int64 content_length,
const std::string& content_type,
const base::FilePath& local_file_path,
const UploadRangeCallback& callback,
const ProgressCallback& progress_callback) {
DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
DCHECK(!callback.is_null());
return sender_->StartRequestWithRetry(
new ResumeUploadRequest(
sender_.get(),
upload_url,
start_position,
end_position,
content_length,
content_type,
local_file_path,
base::Bind(&ParseResourceEntryForUploadRangeAndRun, callback),
progress_callback));
}
CancelCallback DriveAPIService::GetUploadStatus(
const GURL& upload_url,
int64 content_length,
const UploadRangeCallback& callback) {
DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
DCHECK(!callback.is_null());
return sender_->StartRequestWithRetry(new GetUploadStatusRequest(
sender_.get(),
upload_url,
content_length,
base::Bind(&ParseResourceEntryForUploadRangeAndRun, callback)));
}
CancelCallback DriveAPIService::AuthorizeApp(
const std::string& resource_id,
const std::string& app_id,
const AuthorizeAppCallback& callback) {
DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
DCHECK(!callback.is_null());
return sender_->StartRequestWithRetry(new GetFileRequest(
sender_.get(),
url_generator_,
resource_id,
base::Bind(&ExtractOpenUrlAndRun, app_id, callback)));
}
bool DriveAPIService::HasAccessToken() const {
DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
return sender_->auth_service()->HasAccessToken();
}
void DriveAPIService::RequestAccessToken(const AuthStatusCallback& callback) {
DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
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(BrowserThread::CurrentlyOn(BrowserThread::UI));
return sender_->auth_service()->HasRefreshToken();
}
void DriveAPIService::ClearAccessToken() {
DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
sender_->auth_service()->ClearAccessToken();
}
void DriveAPIService::ClearRefreshToken() {
DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
sender_->auth_service()->ClearRefreshToken();
}
void DriveAPIService::OnOAuth2RefreshTokenChanged() {
DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
if (CanSendRequest()) {
FOR_EACH_OBSERVER(
DriveServiceObserver, observers_, OnReadyToSendRequests());
} else if (!HasRefreshToken()) {
FOR_EACH_OBSERVER(
DriveServiceObserver, observers_, OnRefreshTokenInvalid());
}
}
} // namespace drive