Commit d6907a5c authored by kinaba@chromium.org's avatar kinaba@chromium.org

Revert 288017 "Parse Drive API responses all at once in the bloc..."

It broke Drive sync completely.

> Parse Drive API responses all at once in the blocking pool.
> 
> Previous implementation did the parsing of string to base::Value
> on blocking pool, and that of base::Value to specific data types
> either on UI thread or on yet another post to blocking pool.
> 
> The previous implementation is slightly inefficient and moreover
> involves a subtle bug 284244.
> 
> BUG=284244
> 
> Review URL: https://codereview.chromium.org/442193002

TBR=kinaba@chromium.org
BUG=401843

Review URL: https://codereview.chromium.org/449323002

Cr-Commit-Position: refs/heads/master@{#288216}
git-svn-id: svn://svn.chromium.org/chrome/trunk/src@288216 0039d316-1c4b-4281-b951-d872f2087c98
parent 39cfeda9
......@@ -8,7 +8,10 @@
#include <vector>
#include "base/bind.h"
#include "base/sequenced_task_runner.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 "content/public/browser/browser_thread.h"
#include "google_apis/drive/auth_service.h"
......@@ -146,16 +149,6 @@ void ExtractOpenUrlAndRun(const std::string& app_id,
callback.Run(GDATA_OTHER_ERROR, GURL());
}
void ExtractShareUrlAndRun(const google_apis::GetShareUrlCallback& callback,
google_apis::GDataErrorCode error,
scoped_ptr<google_apis::ResourceEntry> entry) {
DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI));
const google_apis::Link* share_link =
entry ? entry->GetLinkByType(google_apis::Link::LINK_SHARE) : NULL;
callback.Run(error, share_link ? share_link->href() : GURL());
}
// Ignores the |entry|, and runs the |callback|.
void EntryActionCallbackAdapter(
const EntryActionCallback& callback,
......@@ -383,7 +376,7 @@ CancelCallback DriveAPIService::GetShareUrl(
wapi_url_generator_,
resource_id,
embed_origin,
base::Bind(&ExtractShareUrlAndRun,
base::Bind(&util::ParseShareUrlAndRun,
callback)));
}
......
......@@ -136,6 +136,29 @@ std::string CanonicalizeResourceId(const std::string& resource_id) {
return resource_id;
}
void ParseShareUrlAndRun(const google_apis::GetShareUrlCallback& callback,
google_apis::GDataErrorCode error,
scoped_ptr<base::Value> value) {
DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI));
if (!value) {
callback.Run(error, GURL());
return;
}
// Parsing ResourceEntry is cheap enough to do on UI thread.
scoped_ptr<google_apis::ResourceEntry> entry =
google_apis::ResourceEntry::ExtractAndParse(*value);
if (!entry) {
callback.Run(google_apis::GDATA_PARSE_ERROR, GURL());
return;
}
const google_apis::Link* share_link =
entry->GetLinkByType(google_apis::Link::LINK_SHARE);
callback.Run(error, share_link ? share_link->href() : GURL());
}
scoped_ptr<google_apis::ResourceEntry>
ConvertFileResourceToResourceEntry(
const google_apis::FileResource& file_resource) {
......
......@@ -63,6 +63,12 @@ std::string ExtractResourceIdFromUrl(const GURL& url);
// into the new format.
std::string CanonicalizeResourceId(const std::string& resource_id);
// Extracts an url to the sharing dialog and returns it via |callback|. If
// the share url doesn't exist, then an empty url is returned.
void ParseShareUrlAndRun(const google_apis::GetShareUrlCallback& callback,
google_apis::GDataErrorCode error,
scoped_ptr<base::Value> value);
// Converts FileResource to ResourceEntry.
scoped_ptr<google_apis::ResourceEntry>
ConvertFileResourceToResourceEntry(
......
......@@ -43,18 +43,29 @@ const char kUploadResponseLocation[] = "location";
const char kUploadContentRange[] = "Content-Range: bytes ";
const char kUploadResponseRange[] = "range";
// Parses JSON passed in |json| on |blocking_task_runner|. Runs |callback| on
// the calling thread when finished with either success or failure.
// The callback must not be null.
void ParseJsonOnBlockingPool(
base::TaskRunner* blocking_task_runner,
const std::string& json,
const base::Callback<void(scoped_ptr<base::Value> value)>& callback) {
base::PostTaskAndReplyWithResult(
blocking_task_runner,
FROM_HERE,
base::Bind(&google_apis::ParseJson, json),
callback);
// Parse JSON string to base::Value object.
scoped_ptr<base::Value> ParseJsonInternal(const std::string& json) {
int error_code = -1;
std::string error_message;
scoped_ptr<base::Value> value(base::JSONReader::ReadAndReturnError(
json, base::JSON_PARSE_RFC, &error_code, &error_message));
if (!value.get()) {
std::string trimmed_json;
if (json.size() < 80) {
trimmed_json = json;
} else {
// Take the first 50 and the last 10 bytes.
trimmed_json = base::StringPrintf(
"%s [%s bytes] %s",
json.substr(0, 50).c_str(),
base::Uint64ToString(json.size() - 60).c_str(),
json.substr(json.size() - 10).c_str());
}
LOG(WARNING) << "Error while parsing entry response: " << error_message
<< ", code: " << error_code << ", json:\n" << trimmed_json;
}
return value.Pass();
}
// Returns response headers as a string. Returns a warning message if
......@@ -84,28 +95,14 @@ bool IsSuccessfulResponseCode(int response_code) {
namespace google_apis {
scoped_ptr<base::Value> ParseJson(const std::string& json) {
int error_code = -1;
std::string error_message;
scoped_ptr<base::Value> value(base::JSONReader::ReadAndReturnError(
json, base::JSON_PARSE_RFC, &error_code, &error_message));
if (!value.get()) {
std::string trimmed_json;
if (json.size() < 80) {
trimmed_json = json;
} else {
// Take the first 50 and the last 10 bytes.
trimmed_json = base::StringPrintf(
"%s [%s bytes] %s",
json.substr(0, 50).c_str(),
base::Uint64ToString(json.size() - 60).c_str(),
json.substr(json.size() - 10).c_str());
}
LOG(WARNING) << "Error while parsing entry response: " << error_message
<< ", code: " << error_code << ", json:\n" << trimmed_json;
}
return value.Pass();
void ParseJson(base::TaskRunner* blocking_task_runner,
const std::string& json,
const ParseJsonCallback& callback) {
base::PostTaskAndReplyWithResult(
blocking_task_runner,
FROM_HERE,
base::Bind(&ParseJsonInternal, json),
callback);
}
//=========================== ResponseWriter ==================================
......@@ -362,7 +359,7 @@ void UrlFetchRequestBase::OnURLFetchComplete(const URLFetcher* source) {
const char kErrorReasonUserRateLimitExceeded[] = "userRateLimitExceeded";
const char kErrorReasonQuotaExceeded[] = "quotaExceeded";
scoped_ptr<base::Value> value(ParseJson(response_writer_->data()));
scoped_ptr<base::Value> value(ParseJsonInternal(response_writer_->data()));
base::DictionaryValue* dictionary = NULL;
base::DictionaryValue* error = NULL;
if (value &&
......@@ -437,6 +434,62 @@ void EntryActionRequest::RunCallbackOnPrematureFailure(GDataErrorCode code) {
callback_.Run(code);
}
//============================== GetDataRequest ==============================
GetDataRequest::GetDataRequest(RequestSender* sender,
const GetDataCallback& callback)
: UrlFetchRequestBase(sender),
callback_(callback),
weak_ptr_factory_(this) {
DCHECK(!callback_.is_null());
}
GetDataRequest::~GetDataRequest() {}
void GetDataRequest::ParseResponse(GDataErrorCode fetch_error_code,
const std::string& data) {
DCHECK(CalledOnValidThread());
VLOG(1) << "JSON received from " << GetURL().spec() << ": "
<< data.size() << " bytes";
ParseJson(blocking_task_runner(),
data,
base::Bind(&GetDataRequest::OnDataParsed,
weak_ptr_factory_.GetWeakPtr(),
fetch_error_code));
}
void GetDataRequest::ProcessURLFetchResults(const URLFetcher* source) {
GDataErrorCode fetch_error_code = GetErrorCode();
switch (fetch_error_code) {
case HTTP_SUCCESS:
case HTTP_CREATED:
ParseResponse(fetch_error_code, response_writer()->data());
break;
default:
RunCallbackOnPrematureFailure(fetch_error_code);
OnProcessURLFetchResultsComplete();
break;
}
}
void GetDataRequest::RunCallbackOnPrematureFailure(
GDataErrorCode fetch_error_code) {
callback_.Run(fetch_error_code, scoped_ptr<base::Value>());
}
void GetDataRequest::OnDataParsed(GDataErrorCode fetch_error_code,
scoped_ptr<base::Value> value) {
DCHECK(CalledOnValidThread());
if (!value.get())
fetch_error_code = GDATA_PARSE_ERROR;
callback_.Run(fetch_error_code, value.Pass());
OnProcessURLFetchResultsComplete();
}
//========================= InitiateUploadRequestBase ========================
InitiateUploadRequestBase::InitiateUploadRequestBase(
......@@ -565,11 +618,11 @@ void UploadRangeRequestBase::ProcessURLFetchResults(
} else if (code == HTTP_CREATED || code == HTTP_SUCCESS) {
// The upload is successfully done. Parse the response which should be
// the entry's metadata.
ParseJsonOnBlockingPool(blocking_task_runner(),
response_writer()->data(),
base::Bind(&UploadRangeRequestBase::OnDataParsed,
weak_ptr_factory_.GetWeakPtr(),
code));
ParseJson(blocking_task_runner(),
response_writer()->data(),
base::Bind(&UploadRangeRequestBase::OnDataParsed,
weak_ptr_factory_.GetWeakPtr(),
code));
} else {
// Failed to upload. Run callbacks to notify the error.
OnRangeRequestComplete(
......
......@@ -29,6 +29,10 @@ namespace google_apis {
class RequestSender;
// Callback used to pass parsed JSON from ParseJson(). If parsing error occurs,
// then the passed argument is null.
typedef base::Callback<void(scoped_ptr<base::Value> value)> ParseJsonCallback;
// Callback used for DownloadFileRequest and ResumeUploadRequestBase.
typedef base::Callback<void(int64 progress, int64 total)> ProgressCallback;
......@@ -37,8 +41,12 @@ typedef base::Callback<void(
GDataErrorCode error,
scoped_ptr<std::string> content)> GetContentCallback;
// Parses JSON passed in |json|. Returns NULL on failure.
scoped_ptr<base::Value> ParseJson(const std::string& json);
// Parses JSON passed in |json| on |blocking_task_runner|. Runs |callback| on
// the calling thread when finished with either success or failure.
// The callback must not be null.
void ParseJson(base::TaskRunner* blocking_task_runner,
const std::string& json,
const ParseJsonCallback& callback);
//======================= AuthenticatedRequestInterface ======================
......@@ -241,6 +249,45 @@ class EntryActionRequest : public UrlFetchRequestBase {
DISALLOW_COPY_AND_ASSIGN(EntryActionRequest);
};
//============================== GetDataRequest ==============================
// Callback type for requests that returns JSON data.
typedef base::Callback<void(GDataErrorCode error,
scoped_ptr<base::Value> json_data)> GetDataCallback;
// This class performs the request for fetching and converting the fetched
// content into a base::Value.
class GetDataRequest : public UrlFetchRequestBase {
public:
// |callback| is called when the request finishes either by success or by
// failure. On success, a JSON Value object is passed. It must not be null.
GetDataRequest(RequestSender* sender, const GetDataCallback& callback);
virtual ~GetDataRequest();
protected:
// UrlFetchRequestBase overrides.
virtual void ProcessURLFetchResults(const net::URLFetcher* source) OVERRIDE;
virtual void RunCallbackOnPrematureFailure(
GDataErrorCode fetch_error_code) OVERRIDE;
private:
// Parses JSON response.
void ParseResponse(GDataErrorCode fetch_error_code, const std::string& data);
// Called when ParseJsonOnBlockingPool() is completed.
void OnDataParsed(GDataErrorCode fetch_error_code,
scoped_ptr<base::Value> value);
const GetDataCallback callback_;
// Note: This should remain the last member so it'll be destroyed and
// invalidate its weak pointers before any other members are destroyed.
base::WeakPtrFactory<GetDataRequest> weak_ptr_factory_;
DISALLOW_COPY_AND_ASSIGN(GetDataRequest);
};
//=========================== InitiateUploadRequestBase=======================
// Callback type for DriveServiceInterface::InitiateUpload.
......
......@@ -51,6 +51,24 @@ class FakeUrlFetchRequest : public UrlFetchRequestBase {
GURL url_;
};
class FakeGetDataRequest : public GetDataRequest {
public:
explicit FakeGetDataRequest(RequestSender* sender,
const GetDataCallback& callback,
const GURL& url)
: GetDataRequest(sender, callback),
url_(url) {
}
virtual ~FakeGetDataRequest() {
}
protected:
virtual GURL GetURL() const OVERRIDE { return url_; }
GURL url_;
};
} // namespace
class BaseRequestsTest : public testing::Test {
......@@ -91,7 +109,11 @@ class BaseRequestsTest : public testing::Test {
};
TEST_F(BaseRequestsTest, ParseValidJson) {
scoped_ptr<base::Value> json(ParseJson(kValidJsonString));
scoped_ptr<base::Value> json;
ParseJson(message_loop_.message_loop_proxy(),
kValidJsonString,
base::Bind(test_util::CreateCopyResultCallback(&json)));
base::RunLoop().RunUntilIdle();
base::DictionaryValue* root_dict = NULL;
ASSERT_TRUE(json);
......@@ -103,7 +125,14 @@ TEST_F(BaseRequestsTest, ParseValidJson) {
}
TEST_F(BaseRequestsTest, ParseInvalidJson) {
EXPECT_FALSE(ParseJson(kInvalidJsonString));
// Initialize with a valid pointer to verify that null is indeed assigned.
scoped_ptr<base::Value> json(base::Value::CreateNullValue());
ParseJson(message_loop_.message_loop_proxy(),
kInvalidJsonString,
base::Bind(test_util::CreateCopyResultCallback(&json)));
base::RunLoop().RunUntilIdle();
EXPECT_FALSE(json);
}
TEST_F(BaseRequestsTest, UrlFetchRequestBaseResponseCodeOverride) {
......@@ -136,4 +165,40 @@ TEST_F(BaseRequestsTest, UrlFetchRequestBaseResponseCodeOverride) {
EXPECT_EQ(HTTP_SERVICE_UNAVAILABLE, error);
}
TEST_F(BaseRequestsTest, GetDataRequestParseValidResponse) {
response_body_ = kValidJsonString;
GDataErrorCode error = GDATA_OTHER_ERROR;
scoped_ptr<base::Value> value;
base::RunLoop run_loop;
sender_->StartRequestWithRetry(
new FakeGetDataRequest(
sender_.get(),
test_util::CreateQuitCallback(
&run_loop, test_util::CreateCopyResultCallback(&error, &value)),
test_server_.base_url()));
run_loop.Run();
EXPECT_EQ(HTTP_SUCCESS, error);
EXPECT_TRUE(value);
}
TEST_F(BaseRequestsTest, GetDataRequestParseInvalidResponse) {
response_body_ = kInvalidJsonString;
GDataErrorCode error = GDATA_OTHER_ERROR;
scoped_ptr<base::Value> value;
base::RunLoop run_loop;
sender_->StartRequestWithRetry(
new FakeGetDataRequest(
sender_.get(),
test_util::CreateQuitCallback(
&run_loop, test_util::CreateCopyResultCallback(&error, &value)),
test_server_.base_url()));
run_loop.Run();
EXPECT_EQ(GDATA_PARSE_ERROR, error);
EXPECT_FALSE(value);
}
} // namespace google_apis
......@@ -11,6 +11,7 @@
#include "base/sequenced_task_runner.h"
#include "base/task_runner_util.h"
#include "base/values.h"
#include "google_apis/drive/drive_api_parser.h"
#include "google_apis/drive/request_sender.h"
#include "google_apis/drive/request_util.h"
#include "google_apis/drive/time_util.h"
......@@ -22,6 +23,70 @@ namespace {
const char kContentTypeApplicationJson[] = "application/json";
const char kParentLinkKind[] = "drive#fileLink";
// Parses the JSON value to a resource typed |T| and runs |callback| on the UI
// thread once parsing is done.
template<typename T>
void ParseJsonAndRun(
const base::Callback<void(GDataErrorCode, scoped_ptr<T>)>& callback,
GDataErrorCode error,
scoped_ptr<base::Value> value) {
DCHECK(!callback.is_null());
scoped_ptr<T> resource;
if (value) {
resource = T::CreateFrom(*value);
if (!resource) {
// Failed to parse the JSON value, although the JSON value is available,
// so let the callback know the parsing error.
error = GDATA_PARSE_ERROR;
}
}
callback.Run(error, resource.Pass());
}
// Thin adapter of T::CreateFrom.
template<typename T>
scoped_ptr<T> ParseJsonOnBlockingPool(scoped_ptr<base::Value> value) {
return T::CreateFrom(*value);
}
// Runs |callback| with given |error| and |value|. If |value| is null,
// overwrites |error| to GDATA_PARSE_ERROR.
template<typename T>
void ParseJsonOnBlockingPoolAndRunAfterBlockingPoolTask(
const base::Callback<void(GDataErrorCode, scoped_ptr<T>)>& callback,
GDataErrorCode error, scoped_ptr<T> value) {
if (!value)
error = GDATA_PARSE_ERROR;
callback.Run(error, value.Pass());
}
// Parses the JSON value to a resource typed |T| and runs |callback| on
// blocking pool, and then run on the current thread.
// TODO(hidehiko): Move this and ParseJsonAndRun defined above into base with
// merging the tasks running on blocking pool into one.
template<typename T>
void ParseJsonOnBlockingPoolAndRun(
scoped_refptr<base::TaskRunner> blocking_task_runner,
const base::Callback<void(GDataErrorCode, scoped_ptr<T>)>& callback,
GDataErrorCode error,
scoped_ptr<base::Value> value) {
DCHECK(!callback.is_null());
if (!value) {
callback.Run(error, scoped_ptr<T>());
return;
}
base::PostTaskAndReplyWithResult(
blocking_task_runner,
FROM_HERE,
base::Bind(&ParseJsonOnBlockingPool<T>, base::Passed(&value)),
base::Bind(&ParseJsonOnBlockingPoolAndRunAfterBlockingPoolTask<T>,
callback, error));
}
// Parses the JSON value to FileResource instance and runs |callback| on the
// UI thread once parsing is done.
// This is customized version of ParseJsonAndRun defined above to adapt the
......@@ -61,16 +126,17 @@ scoped_ptr<base::DictionaryValue> CreateParentValue(
namespace drive {
//============================ DriveApiPartialFieldRequest ====================
//============================ DriveApiDataRequest ===========================
DriveApiPartialFieldRequest::DriveApiPartialFieldRequest(
RequestSender* sender) : UrlFetchRequestBase(sender) {
DriveApiDataRequest::DriveApiDataRequest(RequestSender* sender,
const GetDataCallback& callback)
: GetDataRequest(sender, callback) {
}
DriveApiPartialFieldRequest::~DriveApiPartialFieldRequest() {
DriveApiDataRequest::~DriveApiDataRequest() {
}
GURL DriveApiPartialFieldRequest::GetURL() const {
GURL DriveApiDataRequest::GetURL() const {
GURL url = GetURLInternal();
if (!fields_.empty())
url = net::AppendOrReplaceQueryParameter(url, "fields", fields_);
......@@ -83,7 +149,9 @@ FilesGetRequest::FilesGetRequest(
RequestSender* sender,
const DriveApiUrlGenerator& url_generator,
const FileResourceCallback& callback)
: DriveApiDataRequest<FileResource>(sender, callback),
: DriveApiDataRequest(
sender,
base::Bind(&ParseJsonAndRun<FileResource>, callback)),
url_generator_(url_generator) {
DCHECK(!callback.is_null());
}
......@@ -100,7 +168,9 @@ FilesAuthorizeRequest::FilesAuthorizeRequest(
RequestSender* sender,
const DriveApiUrlGenerator& url_generator,
const FileResourceCallback& callback)
: DriveApiDataRequest<FileResource>(sender, callback),
: DriveApiDataRequest(
sender,
base::Bind(&ParseJsonAndRun<FileResource>, callback)),
url_generator_(url_generator) {
DCHECK(!callback.is_null());
}
......@@ -121,7 +191,9 @@ FilesInsertRequest::FilesInsertRequest(
RequestSender* sender,
const DriveApiUrlGenerator& url_generator,
const FileResourceCallback& callback)
: DriveApiDataRequest<FileResource>(sender, callback),
: DriveApiDataRequest(
sender,
base::Bind(&ParseJsonAndRun<FileResource>, callback)),
url_generator_(url_generator) {
DCHECK(!callback.is_null());
}
......@@ -178,7 +250,9 @@ FilesPatchRequest::FilesPatchRequest(
RequestSender* sender,
const DriveApiUrlGenerator& url_generator,
const FileResourceCallback& callback)
: DriveApiDataRequest<FileResource>(sender, callback),
: DriveApiDataRequest(
sender,
base::Bind(&ParseJsonAndRun<FileResource>, callback)),
url_generator_(url_generator),
set_modified_date_(false),
update_viewed_date_(true) {
......@@ -246,7 +320,9 @@ FilesCopyRequest::FilesCopyRequest(
RequestSender* sender,
const DriveApiUrlGenerator& url_generator,
const FileResourceCallback& callback)
: DriveApiDataRequest<FileResource>(sender, callback),
: DriveApiDataRequest(
sender,
base::Bind(&ParseJsonAndRun<FileResource>, callback)),
url_generator_(url_generator) {
DCHECK(!callback.is_null());
}
......@@ -299,7 +375,11 @@ FilesListRequest::FilesListRequest(
RequestSender* sender,
const DriveApiUrlGenerator& url_generator,
const FileListCallback& callback)
: DriveApiDataRequest<FileList>(sender, callback),
: DriveApiDataRequest(
sender,
base::Bind(&ParseJsonOnBlockingPoolAndRun<FileList>,
make_scoped_refptr(sender->blocking_task_runner()),
callback)),
url_generator_(url_generator),
max_results_(100) {
DCHECK(!callback.is_null());
......@@ -316,7 +396,11 @@ GURL FilesListRequest::GetURLInternal() const {
FilesListNextPageRequest::FilesListNextPageRequest(
RequestSender* sender,
const FileListCallback& callback)
: DriveApiDataRequest<FileList>(sender, callback) {
: DriveApiDataRequest(
sender,
base::Bind(&ParseJsonOnBlockingPoolAndRun<FileList>,
make_scoped_refptr(sender->blocking_task_runner()),
callback)) {
DCHECK(!callback.is_null());
}
......@@ -361,7 +445,9 @@ FilesTrashRequest::FilesTrashRequest(
RequestSender* sender,
const DriveApiUrlGenerator& url_generator,
const FileResourceCallback& callback)
: DriveApiDataRequest<FileResource>(sender, callback),
: DriveApiDataRequest(
sender,
base::Bind(&ParseJsonAndRun<FileResource>, callback)),
url_generator_(url_generator) {
DCHECK(!callback.is_null());
}
......@@ -382,7 +468,9 @@ AboutGetRequest::AboutGetRequest(
RequestSender* sender,
const DriveApiUrlGenerator& url_generator,
const AboutResourceCallback& callback)
: DriveApiDataRequest<AboutResource>(sender, callback),
: DriveApiDataRequest(
sender,
base::Bind(&ParseJsonAndRun<AboutResource>, callback)),
url_generator_(url_generator) {
DCHECK(!callback.is_null());
}
......@@ -399,7 +487,11 @@ ChangesListRequest::ChangesListRequest(
RequestSender* sender,
const DriveApiUrlGenerator& url_generator,
const ChangeListCallback& callback)
: DriveApiDataRequest<ChangeList>(sender, callback),
: DriveApiDataRequest(
sender,
base::Bind(&ParseJsonOnBlockingPoolAndRun<ChangeList>,
make_scoped_refptr(sender->blocking_task_runner()),
callback)),
url_generator_(url_generator),
include_deleted_(true),
max_results_(100),
......@@ -419,7 +511,11 @@ GURL ChangesListRequest::GetURLInternal() const {
ChangesListNextPageRequest::ChangesListNextPageRequest(
RequestSender* sender,
const ChangeListCallback& callback)
: DriveApiDataRequest<ChangeList>(sender, callback) {
: DriveApiDataRequest(
sender,
base::Bind(&ParseJsonOnBlockingPoolAndRun<ChangeList>,
make_scoped_refptr(sender->blocking_task_runner()),
callback)) {
DCHECK(!callback.is_null());
}
......@@ -437,7 +533,9 @@ AppsListRequest::AppsListRequest(
const DriveApiUrlGenerator& url_generator,
bool use_internal_endpoint,
const AppListCallback& callback)
: DriveApiDataRequest<AppList>(sender, callback),
: DriveApiDataRequest(
sender,
base::Bind(&ParseJsonAndRun<AppList>, callback)),
url_generator_(url_generator),
use_internal_endpoint_(use_internal_endpoint) {
DCHECK(!callback.is_null());
......
This diff is collapsed.
......@@ -4,37 +4,22 @@
#include "google_apis/drive/gdata_wapi_requests.h"
#include "base/location.h"
#include "base/sequenced_task_runner.h"
#include "base/task_runner_util.h"
#include "base/values.h"
#include "google_apis/drive/gdata_wapi_parser.h"
#include "google_apis/drive/gdata_wapi_url_generator.h"
namespace google_apis {
namespace {
scoped_ptr<ResourceEntry> ParseResourceEntry(const std::string& json) {
scoped_ptr<base::Value> value = ParseJson(json);
return value ? ResourceEntry::ExtractAndParse(*value) :
scoped_ptr<ResourceEntry>();
}
} // namespace
//============================ GetResourceEntryRequest =======================
GetResourceEntryRequest::GetResourceEntryRequest(
RequestSender* sender,
const GDataWapiUrlGenerator& url_generator,
const std::string& resource_id,
const GURL& embed_origin,
const GetResourceEntryCallback& callback)
: UrlFetchRequestBase(sender),
const GetDataCallback& callback)
: GetDataRequest(sender, callback),
url_generator_(url_generator),
resource_id_(resource_id),
embed_origin_(embed_origin),
callback_(callback),
weak_ptr_factory_(this) {
embed_origin_(embed_origin) {
DCHECK(!callback.is_null());
}
......@@ -45,35 +30,4 @@ GURL GetResourceEntryRequest::GetURL() const {
resource_id_, embed_origin_);
}
void GetResourceEntryRequest::ProcessURLFetchResults(
const net::URLFetcher* source) {
GDataErrorCode error = GetErrorCode();
switch (error) {
case HTTP_SUCCESS:
case HTTP_CREATED:
base::PostTaskAndReplyWithResult(
blocking_task_runner(),
FROM_HERE,
base::Bind(&ParseResourceEntry, response_writer()->data()),
base::Bind(&GetResourceEntryRequest::OnDataParsed,
weak_ptr_factory_.GetWeakPtr(), error));
break;
default:
RunCallbackOnPrematureFailure(error);
OnProcessURLFetchResultsComplete();
break;
}
}
void GetResourceEntryRequest::RunCallbackOnPrematureFailure(
GDataErrorCode error) {
callback_.Run(error, scoped_ptr<ResourceEntry>());
}
void GetResourceEntryRequest::OnDataParsed(GDataErrorCode error,
scoped_ptr<ResourceEntry> entry) {
callback_.Run(entry ? error : GDATA_PARSE_ERROR, entry.Pass());
OnProcessURLFetchResultsComplete();
}
} // namespace google_apis
......@@ -12,44 +12,29 @@
namespace google_apis {
class ResourceEntry;
// Callback type for GetResourceEntryRequest.
typedef base::Callback<void(GDataErrorCode error,
scoped_ptr<ResourceEntry> entry)>
GetResourceEntryCallback;
//========================= GetResourceEntryRequest ==========================
// This class performs the request for fetching a single resource entry.
class GetResourceEntryRequest : public UrlFetchRequestBase {
class GetResourceEntryRequest : public GetDataRequest {
public:
// |callback| must not be null.
GetResourceEntryRequest(RequestSender* sender,
const GDataWapiUrlGenerator& url_generator,
const std::string& resource_id,
const GURL& embed_origin,
const GetResourceEntryCallback& callback);
const GetDataCallback& callback);
virtual ~GetResourceEntryRequest();
protected:
// UrlFetchRequestBase overrides.
virtual void ProcessURLFetchResults(const net::URLFetcher* source) OVERRIDE;
virtual void RunCallbackOnPrematureFailure(GDataErrorCode error) OVERRIDE;
virtual GURL GetURL() const OVERRIDE;
private:
void OnDataParsed(GDataErrorCode error, scoped_ptr<ResourceEntry> entry);
const GDataWapiUrlGenerator url_generator_;
// Resource id of the requested entry.
const std::string resource_id_;
// Embed origin for an url to the sharing dialog. Can be empty.
GURL embed_origin_;
const GetResourceEntryCallback callback_;
// Note: This should remain the last member so it'll be destroyed and
// invalidate its weak pointers before any other members are destroyed.
base::WeakPtrFactory<GetResourceEntryRequest> weak_ptr_factory_;
const GURL& embed_origin_;
DISALLOW_COPY_AND_ASSIGN(GetResourceEntryRequest);
};
......
......@@ -5,6 +5,7 @@
#include "base/bind.h"
#include "base/message_loop/message_loop.h"
#include "base/run_loop.h"
#include "base/values.h"
#include "google_apis/drive/dummy_auth_service.h"
#include "google_apis/drive/gdata_wapi_parser.h"
#include "google_apis/drive/gdata_wapi_requests.h"
......@@ -98,7 +99,7 @@ class GDataWapiRequestsTest : public testing::Test {
TEST_F(GDataWapiRequestsTest, GetResourceEntryRequest_ValidResourceId) {
GDataErrorCode result_code = GDATA_OTHER_ERROR;
scoped_ptr<ResourceEntry> result_data;
scoped_ptr<base::Value> result_data;
{
base::RunLoop run_loop;
......@@ -119,14 +120,16 @@ TEST_F(GDataWapiRequestsTest, GetResourceEntryRequest_ValidResourceId) {
EXPECT_EQ("/feeds/default/private/full/file%3A2_file_resource_id"
"?v=3&alt=json&showroot=true",
http_request_.relative_url);
scoped_ptr<base::Value> expected_json =
test_util::LoadJSONFile("gdata/file_entry.json");
ASSERT_TRUE(expected_json);
EXPECT_TRUE(result_data);
EXPECT_EQ("File 1.mp3", result_data->filename());
EXPECT_EQ("3b4382ebefec6e743578c76bbd0575ce", result_data->file_md5());
EXPECT_TRUE(base::Value::Equals(expected_json.get(), result_data.get()));
}
TEST_F(GDataWapiRequestsTest, GetResourceEntryRequest_InvalidResourceId) {
GDataErrorCode result_code = GDATA_OTHER_ERROR;
scoped_ptr<ResourceEntry> result_data;
scoped_ptr<base::Value> result_data;
{
base::RunLoop run_loop;
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment