diff options
author | hirono <hirono@chromium.org> | 2015-01-07 04:55:59 -0800 |
---|---|---|
committer | Commit bot <commit-bot@chromium.org> | 2015-01-07 12:56:41 +0000 |
commit | 97a0311a411e6d0dc2a7dea14e68415e27fe7a76 (patch) | |
tree | 3dd30e958e419c2e648085040a2198d78ff0f1bd /google_apis | |
parent | 35290f1ba94f90e89492b51567826270c02a81da (diff) | |
download | chromium_src-97a0311a411e6d0dc2a7dea14e68415e27fe7a76.zip chromium_src-97a0311a411e6d0dc2a7dea14e68415e27fe7a76.tar.gz chromium_src-97a0311a411e6d0dc2a7dea14e68415e27fe7a76.tar.bz2 |
Drive API: Add MultipartUploadRequestBase class.
MultipartUploadRequestBase is base class of multipart/related uploading requests. It has responsible for
* Determine boundary string of multipart/related.
* Create UploadDataStreamFactory.
* Generate FileResource result from response.
BUG=269922
TEST=MultipartUploadRequestBaseTest.*
Review URL: https://codereview.chromium.org/823543002
Cr-Commit-Position: refs/heads/master@{#310273}
Diffstat (limited to 'google_apis')
-rw-r--r-- | google_apis/drive/base_requests.cc | 206 | ||||
-rw-r--r-- | google_apis/drive/base_requests.h | 87 | ||||
-rw-r--r-- | google_apis/drive/base_requests_unittest.cc | 97 | ||||
-rw-r--r-- | google_apis/drive/drive_api_requests.cc | 30 | ||||
-rw-r--r-- | google_apis/drive/drive_api_requests.h | 6 | ||||
-rw-r--r-- | google_apis/drive/request_util.cc | 11 | ||||
-rw-r--r-- | google_apis/drive/request_util.h | 9 |
7 files changed, 418 insertions, 28 deletions
diff --git a/google_apis/drive/base_requests.cc b/google_apis/drive/base_requests.cc index fe058fe..a0b9297 100644 --- a/google_apis/drive/base_requests.cc +++ b/google_apis/drive/base_requests.cc @@ -4,18 +4,28 @@ #include "google_apis/drive/base_requests.h" +#include "base/files/file_util.h" #include "base/json/json_reader.h" +#include "base/json/json_writer.h" #include "base/location.h" #include "base/sequenced_task_runner.h" #include "base/strings/string_number_conversions.h" #include "base/strings/stringprintf.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/task_util.h" +#include "google_apis/drive/time_util.h" +#include "net/base/elements_upload_data_stream.h" #include "net/base/io_buffer.h" #include "net/base/load_flags.h" #include "net/base/net_errors.h" +#include "net/base/upload_bytes_element_reader.h" +#include "net/base/upload_data_stream.h" +#include "net/base/upload_element_reader.h" +#include "net/base/upload_file_element_reader.h" #include "net/http/http_byte_range.h" #include "net/http/http_response_headers.h" #include "net/http/http_util.h" @@ -43,6 +53,20 @@ const char kUploadResponseLocation[] = "location"; const char kUploadContentRange[] = "Content-Range: bytes "; const char kUploadResponseRange[] = "range"; +// The prefix of multipart/related mime type. +const char kMultipartMimeTypePrefix[] = "multipart/related; boundary="; + +// Template for multipart request body. +const char kMessageFormatBeforeFile[] = + "--%s\nContent-Type: %s\n\n%s\n--%s\nContent-Type: %s\n\n"; +const char kMessageFormatAfterFile[] = "\n--%s--"; + +// The predetermined boundary string for multipart/related. +// TODO(hirono): Generates the boundary string randomly and ensure the string +// does not appears in the content parts. +const char kBoundaryForPrototype[] = + "LJx6AFbwBL94CmojDJBcmXpl1WfnvCUOenZqWrpI1Ua3l7xv8Y"; + // 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. @@ -80,6 +104,69 @@ bool IsSuccessfulResponseCode(int response_code) { return 200 <= response_code && response_code <= 299; } +// Creates metadata JSON string for multipart uploading. +// All the values are optional. If the value is empty or null, the value does +// not appear in the metadata. +std::string CreateMultipartUploadMetadataJson( + const std::string& title, + const std::string& parent_resource_id, + const base::Time& modified_date, + const base::Time& last_viewed_by_me_date) { + base::DictionaryValue root; + if (!title.empty()) + root.SetString("title", title); + + // Fill parent link. + if (!parent_resource_id.empty()) { + scoped_ptr<base::ListValue> parents(new base::ListValue); + parents->Append( + google_apis::util::CreateParentValue(parent_resource_id).release()); + root.Set("parents", parents.release()); + } + + if (!modified_date.is_null()) + root.SetString("modifiedDate", + google_apis::util::FormatTimeAsString(modified_date)); + + if (!last_viewed_by_me_date.is_null()) { + root.SetString("lastViewedByMeDate", google_apis::util::FormatTimeAsString( + last_viewed_by_me_date)); + } + + std::string json_string; + base::JSONWriter::Write(&root, &json_string); + return json_string; +} + +// Obtains the multipart body for the metadata string and file contents. If +// predetermined_boundary is empty, the function generates the boundary string. +// TODO(hirono): Generates the boundary string randomly and ensure the string +// does not appears in the content parts. +bool GetMultipartContent(const std::string& predetermined_boundary, + const std::string& metadata_json, + const std::string& content_type, + const base::FilePath& path, + std::string* upload_content_type, + std::string* upload_content_data) { + std::string file_content; + if (!ReadFileToString(path, &file_content)) + return false; + + const std::string boundary = predetermined_boundary.empty() + ? kBoundaryForPrototype + : predetermined_boundary; + + *upload_content_type = kMultipartMimeTypePrefix + boundary; + const std::string body_before_file = base::StringPrintf( + kMessageFormatBeforeFile, boundary.c_str(), "application/json", + metadata_json.c_str(), boundary.c_str(), content_type.c_str()); + const std::string body_after_file = + base::StringPrintf(kMessageFormatAfterFile, boundary.c_str()); + *upload_content_data = body_before_file + file_content + body_after_file; + + return true; +} + } // namespace namespace google_apis { @@ -683,6 +770,125 @@ GetUploadStatusRequestBase::GetExtraRequestHeaders() const { return headers; } +//========================= MultipartUploadRequestBase ======================== + +MultipartUploadRequestBase::MultipartUploadRequestBase( + RequestSender* sender, + const std::string& title, + const std::string& parent_resource_id, + const std::string& content_type, + int64 content_length, + const base::Time& modified_date, + const base::Time& last_viewed_by_me_date, + const base::FilePath& local_file_path, + const FileResourceCallback& callback, + const ProgressCallback& progress_callback) + : UrlFetchRequestBase(sender), + metadata_json_(CreateMultipartUploadMetadataJson(title, + parent_resource_id, + modified_date, + last_viewed_by_me_date)), + content_type_(content_type), + local_path_(local_file_path), + has_modified_date_(!modified_date.is_null()), + callback_(callback), + progress_callback_(progress_callback), + weak_ptr_factory_(this) { + DCHECK(!content_type.empty()); + DCHECK_GE(content_length, 0); + DCHECK(!local_file_path.empty()); + DCHECK(!callback.is_null()); +} + +MultipartUploadRequestBase::~MultipartUploadRequestBase() { +} + +void MultipartUploadRequestBase::Start(const std::string& access_token, + const std::string& custom_user_agent, + const ReAuthenticateCallback& callback) { + // If the request is cancelled, the request instance will be deleted in + // |UrlFetchRequestBase::Cancel| and OnPrepareUploadContent won't be called. + std::string* const upload_content_type = new std::string(); + std::string* const upload_content_data = new std::string(); + PostTaskAndReplyWithResult( + blocking_task_runner(), FROM_HERE, + base::Bind(&GetMultipartContent, boundary_, metadata_json_, content_type_, + local_path_, base::Unretained(upload_content_type), + base::Unretained(upload_content_data)), + base::Bind(&MultipartUploadRequestBase::OnPrepareUploadContent, + weak_ptr_factory_.GetWeakPtr(), access_token, + custom_user_agent, callback, base::Owned(upload_content_type), + base::Owned(upload_content_data))); +} + +void MultipartUploadRequestBase::OnPrepareUploadContent( + const std::string& access_token, + const std::string& custom_user_agent, + const ReAuthenticateCallback& callback, + std::string* upload_content_type, + std::string* upload_content_data, + bool result) { + if (!result) { + RunCallbackOnPrematureFailure(GDATA_FILE_ERROR); + return; + } + upload_content_type_.swap(*upload_content_type); + upload_content_data_.swap(*upload_content_data); + UrlFetchRequestBase::Start(access_token, custom_user_agent, callback); +} + +void MultipartUploadRequestBase::SetBoundaryForTesting( + const std::string& boundary) { + boundary_ = boundary; +} + +bool MultipartUploadRequestBase::GetContentData( + std::string* upload_content_type, + std::string* upload_content_data) { + // TODO(hirono): Pass stream instead of actual data to reduce memory usage. + upload_content_type->swap(upload_content_type_); + upload_content_data->swap(upload_content_data_); + return true; +} + +void MultipartUploadRequestBase::ProcessURLFetchResults( + const URLFetcher* source) { + // The upload is successfully done. Parse the response which should be + // the entry's metadata. + const GDataErrorCode code = GetErrorCode(); + if (code == HTTP_CREATED || code == HTTP_SUCCESS) { + ParseJsonOnBlockingPool( + blocking_task_runner(), response_writer()->data(), + base::Bind(&MultipartUploadRequestBase::OnDataParsed, + weak_ptr_factory_.GetWeakPtr(), code)); + } else { + OnDataParsed(code, scoped_ptr<base::Value>()); + } +} + +void MultipartUploadRequestBase::RunCallbackOnPrematureFailure( + GDataErrorCode code) { + callback_.Run(code, scoped_ptr<FileResource>()); +} + +void MultipartUploadRequestBase::OnURLFetchUploadProgress( + const net::URLFetcher* source, + int64 current, + int64 total) { + if (!progress_callback_.is_null()) + progress_callback_.Run(current, total); +} + +void MultipartUploadRequestBase::OnDataParsed(GDataErrorCode code, + scoped_ptr<base::Value> value) { + DCHECK(CalledOnValidThread()); + if (value) + callback_.Run(code, google_apis::FileResource::CreateFrom(*value)); + else + callback_.Run(GDATA_PARSE_ERROR, scoped_ptr<FileResource>()); + OnProcessURLFetchResultsComplete(); +} + //============================ DownloadFileRequestBase ========================= DownloadFileRequestBase::DownloadFileRequestBase( diff --git a/google_apis/drive/base_requests.h b/google_apis/drive/base_requests.h index fb1bf08..2c81798 100644 --- a/google_apis/drive/base_requests.h +++ b/google_apis/drive/base_requests.h @@ -27,8 +27,15 @@ class Value; namespace google_apis { +class FileResource; class RequestSender; +// Callback used for requests that the server returns FileResource data +// formatted into JSON value. +typedef base::Callback<void(GDataErrorCode error, + scoped_ptr<FileResource> entry)> + FileResourceCallback; + // Callback used for DownloadFileRequest and ResumeUploadRequestBase. typedef base::Callback<void(int64 progress, int64 total)> ProgressCallback; @@ -428,6 +435,86 @@ class GetUploadStatusRequestBase : public UploadRangeRequestBase { DISALLOW_COPY_AND_ASSIGN(GetUploadStatusRequestBase); }; +//=========================== MultipartUploadRequestBase======================= + +// This class provides base implementation for performing the request for +// uploading a file by multipart body. +class MultipartUploadRequestBase : public UrlFetchRequestBase { + public: + // Set boundary. Only tests can use this method. + void SetBoundaryForTesting(const std::string& boundary); + + protected: + // |callback| will be called with the file resource.upload URL. + // |content_type| and |content_length| should be the attributes of the + // uploading file. Other parameters are optional and can be empty or null + // depending on Upload URL provided by the subclasses. + MultipartUploadRequestBase( + RequestSender* sender, + const std::string& title, + const std::string& parent_resource_id, + const std::string& content_type, + int64 content_length, + const base::Time& modified_date, + const base::Time& last_viewed_by_me_date, + const base::FilePath& local_file_path, + const FileResourceCallback& callback, + const google_apis::ProgressCallback& progress_callback); + ~MultipartUploadRequestBase() override; + + // Overridden from AuthenticatedRequestInterface. + void Start(const std::string& access_token, + const std::string& custom_user_agent, + const ReAuthenticateCallback& callback) override; + + // Overridden from UrlFetchRequestBase. + bool GetContentData(std::string* upload_content_type, + std::string* upload_content) override; + void ProcessURLFetchResults(const net::URLFetcher* source) override; + void RunCallbackOnPrematureFailure(GDataErrorCode code) override; + + // content::UrlFetcherDelegate overrides. + void OnURLFetchUploadProgress(const net::URLFetcher* source, + int64 current, + int64 total) override; + + // Parses the response value and invokes |callback_| with |FileResource|. + void OnDataParsed(GDataErrorCode code, scoped_ptr<base::Value> value); + + // Whether to the request has modified date information or not. + bool has_modified_date() const { return has_modified_date_; } + + private: + // Continues to rest part of |Start| method after determining boundary string + // of multipart/related. + void OnPrepareUploadContent(const std::string& access_token, + const std::string& custom_user_agent, + const ReAuthenticateCallback& callback, + std::string* upload_content_type, + std::string* upload_content_data, + bool result); + + const std::string metadata_json_; + const std::string content_type_; + const base::FilePath local_path_; + const bool has_modified_date_; + const FileResourceCallback callback_; + const ProgressCallback progress_callback_; + + // Boundary of multipart body. + std::string boundary_; + + // Upload content of multipart body. + std::string upload_content_type_; + std::string upload_content_data_; + + // 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<MultipartUploadRequestBase> weak_ptr_factory_; + + DISALLOW_COPY_AND_ASSIGN(MultipartUploadRequestBase); +}; + //============================ DownloadFileRequest =========================== // Callback type for receiving the completion of DownloadFileRequest. diff --git a/google_apis/drive/base_requests_unittest.cc b/google_apis/drive/base_requests_unittest.cc index 9f624cd..e2dab7d 100644 --- a/google_apis/drive/base_requests_unittest.cc +++ b/google_apis/drive/base_requests_unittest.cc @@ -9,6 +9,7 @@ #include "base/message_loop/message_loop.h" #include "base/run_loop.h" #include "base/values.h" +#include "google_apis/drive/drive_api_parser.h" #include "google_apis/drive/dummy_auth_service.h" #include "google_apis/drive/request_sender.h" #include "google_apis/drive/test_util.h" @@ -50,6 +51,60 @@ class FakeUrlFetchRequest : public UrlFetchRequestBase { GURL url_; }; +class FakeMultipartUploadRequest : public MultipartUploadRequestBase { + public: + FakeMultipartUploadRequest( + RequestSender* sender, + const std::string& title, + const std::string& parent_resource_id, + const std::string& content_type, + int64 content_length, + const base::Time& modified_date, + const base::Time& last_viewed_by_me_date, + const base::FilePath& local_file_path, + const FileResourceCallback& callback, + const google_apis::ProgressCallback& progress_callback, + const GURL& url, + std::string* upload_content_type, + std::string* upload_content_data) + : MultipartUploadRequestBase(sender, + title, + parent_resource_id, + content_type, + content_length, + modified_date, + last_viewed_by_me_date, + local_file_path, + callback, + progress_callback), + url_(url), + upload_content_type_(upload_content_type), + upload_content_data_(upload_content_data) {} + + ~FakeMultipartUploadRequest() override {} + + bool GetContentData(std::string* content_type, + std::string* content_data) override { + const bool result = + MultipartUploadRequestBase::GetContentData(content_type, content_data); + *upload_content_type_ = *content_type; + *upload_content_data_ = *content_data; + return result; + } + + base::SequencedTaskRunner* blocking_task_runner() const { + return MultipartUploadRequestBase::blocking_task_runner(); + } + + protected: + GURL GetURL() const override { return url_; } + + private: + const GURL url_; + std::string* const upload_content_type_; + std::string* const upload_content_data_; +}; + } // namespace class BaseRequestsTest : public testing::Test { @@ -89,6 +144,8 @@ class BaseRequestsTest : public testing::Test { std::string response_body_; }; +typedef BaseRequestsTest MultipartUploadRequestBaseTest; + TEST_F(BaseRequestsTest, ParseValidJson) { scoped_ptr<base::Value> json(ParseJson(kValidJsonString)); @@ -135,4 +192,42 @@ TEST_F(BaseRequestsTest, UrlFetchRequestBaseResponseCodeOverride) { EXPECT_EQ(HTTP_SERVICE_UNAVAILABLE, error); } -} // namespace google_apis +TEST_F(MultipartUploadRequestBaseTest, Basic) { + response_code_ = net::HTTP_OK; + response_body_ = "{\"kind\": \"drive#file\", \"id\": \"file_id\"}"; + scoped_ptr<google_apis::FileResource> file; + GDataErrorCode error = GDATA_OTHER_ERROR; + base::RunLoop run_loop; + const base::FilePath source_path = + google_apis::test_util::GetTestFilePath("chromeos/file_manager/text.txt"); + std::string upload_content_type; + std::string upload_content_data; + scoped_ptr<FakeMultipartUploadRequest> request(new FakeMultipartUploadRequest( + sender_.get(), "test.txt", "parent_id", "text/plain", 10, base::Time(), + base::Time(), source_path, + test_util::CreateQuitCallback( + &run_loop, test_util::CreateCopyResultCallback(&error, &file)), + ProgressCallback(), test_server_.base_url(), &upload_content_type, + &upload_content_data)); + request->SetBoundaryForTesting("TESTBOUNDARY"); + sender_->StartRequestWithRetry(request.release()); + run_loop.Run(); + EXPECT_EQ("multipart/related; boundary=TESTBOUNDARY", upload_content_type); + EXPECT_EQ( + "--TESTBOUNDARY\n" + "Content-Type: application/json\n" + "\n" + "{\"parents\":[{\"id\":\"parent_id\",\"kind\":\"drive#fileLink\"}]," + "\"title\":\"test.txt\"}\n" + "--TESTBOUNDARY\n" + "Content-Type: text/plain\n" + "\n" + "This is a sample file. I like chocolate and chips.\n" + "\n" + "--TESTBOUNDARY--", + upload_content_data); + ASSERT_EQ(HTTP_SUCCESS, error); + EXPECT_EQ("file_id", file->file_id()); +} + +} // Namespace google_apis diff --git a/google_apis/drive/drive_api_requests.cc b/google_apis/drive/drive_api_requests.cc index d711d07..d734125d 100644 --- a/google_apis/drive/drive_api_requests.cc +++ b/google_apis/drive/drive_api_requests.cc @@ -19,9 +19,6 @@ namespace google_apis { namespace { -const char kContentTypeApplicationJson[] = "application/json"; -const char kParentLinkKind[] = "drive#fileLink"; - // 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 @@ -48,15 +45,6 @@ void ParseFileResourceWithUploadRangeAndRun( callback.Run(response, file_resource.Pass()); } -// Creates a Parents value which can be used as a part of request body. -scoped_ptr<base::DictionaryValue> CreateParentValue( - const std::string& file_id) { - scoped_ptr<base::DictionaryValue> parent(new base::DictionaryValue); - parent->SetString("kind", kParentLinkKind); - parent->SetString("id", file_id); - return parent.Pass(); -} - } // namespace namespace drive { @@ -134,7 +122,7 @@ net::URLFetcher::RequestType FilesInsertRequest::GetRequestType() const { bool FilesInsertRequest::GetContentData(std::string* upload_content_type, std::string* upload_content) { - *upload_content_type = kContentTypeApplicationJson; + *upload_content_type = util::kContentTypeApplicationJson; base::DictionaryValue root; @@ -210,7 +198,7 @@ bool FilesPatchRequest::GetContentData(std::string* upload_content_type, parents_.empty()) return false; - *upload_content_type = kContentTypeApplicationJson; + *upload_content_type = util::kContentTypeApplicationJson; base::DictionaryValue root; if (!title_.empty()) @@ -267,7 +255,7 @@ bool FilesCopyRequest::GetContentData(std::string* upload_content_type, if (parents_.empty() && title_.empty()) return false; - *upload_content_type = kContentTypeApplicationJson; + *upload_content_type = util::kContentTypeApplicationJson; base::DictionaryValue root; @@ -492,7 +480,7 @@ GURL ChildrenInsertRequest::GetURL() const { bool ChildrenInsertRequest::GetContentData(std::string* upload_content_type, std::string* upload_content) { - *upload_content_type = kContentTypeApplicationJson; + *upload_content_type = util::kContentTypeApplicationJson; base::DictionaryValue root; root.SetString("id", id_); @@ -557,14 +545,14 @@ InitiateUploadNewFileRequest::GetRequestType() const { bool InitiateUploadNewFileRequest::GetContentData( std::string* upload_content_type, std::string* upload_content) { - *upload_content_type = kContentTypeApplicationJson; + *upload_content_type = util::kContentTypeApplicationJson; base::DictionaryValue root; root.SetString("title", title_); // Fill parent link. scoped_ptr<base::ListValue> parents(new base::ListValue); - parents->Append(CreateParentValue(parent_resource_id_).release()); + parents->Append(util::CreateParentValue(parent_resource_id_).release()); root.Set("parents", parents.release()); if (!modified_date_.is_null()) @@ -627,7 +615,7 @@ bool InitiateUploadExistingFileRequest::GetContentData( base::DictionaryValue root; if (!parent_resource_id_.empty()) { scoped_ptr<base::ListValue> parents(new base::ListValue); - parents->Append(CreateParentValue(parent_resource_id_).release()); + parents->Append(util::CreateParentValue(parent_resource_id_).release()); root.Set("parents", parents.release()); } @@ -645,7 +633,7 @@ bool InitiateUploadExistingFileRequest::GetContentData( if (root.empty()) return false; - *upload_content_type = kContentTypeApplicationJson; + *upload_content_type = util::kContentTypeApplicationJson; base::JSONWriter::Write(&root, upload_content); DVLOG(1) << "InitiateUploadExistingFile data: " << *upload_content_type << ", [" << *upload_content << "]"; @@ -762,7 +750,7 @@ PermissionsInsertRequest::GetRequestType() const { bool PermissionsInsertRequest::GetContentData(std::string* upload_content_type, std::string* upload_content) { - *upload_content_type = kContentTypeApplicationJson; + *upload_content_type = util::kContentTypeApplicationJson; base::DictionaryValue root; switch (type_) { diff --git a/google_apis/drive/drive_api_requests.h b/google_apis/drive/drive_api_requests.h index f7ff826..951c4e7 100644 --- a/google_apis/drive/drive_api_requests.h +++ b/google_apis/drive/drive_api_requests.h @@ -20,12 +20,6 @@ namespace google_apis { -// Callback used for requests that the server returns FileResource data -// formatted into JSON value. -typedef base::Callback<void(GDataErrorCode error, - scoped_ptr<FileResource> entry)> - FileResourceCallback; - // Callback used for requests that the server returns FileList data // formatted into JSON value. typedef base::Callback<void(GDataErrorCode error, diff --git a/google_apis/drive/request_util.cc b/google_apis/drive/request_util.cc index 9737a6f..1e55285 100644 --- a/google_apis/drive/request_util.cc +++ b/google_apis/drive/request_util.cc @@ -5,6 +5,7 @@ #include "google_apis/drive/request_util.h" #include <string> +#include "base/values.h" namespace google_apis { namespace util { @@ -13,14 +14,24 @@ namespace { // etag matching header. const char kIfMatchHeaderPrefix[] = "If-Match: "; +const char kParentLinkKind[] = "drive#fileLink"; } // namespace const char kIfMatchAllHeader[] = "If-Match: *"; +const char kContentTypeApplicationJson[] = "application/json"; std::string GenerateIfMatchHeader(const std::string& etag) { return etag.empty() ? kIfMatchAllHeader : (kIfMatchHeaderPrefix + etag); } +scoped_ptr<base::DictionaryValue> CreateParentValue( + const std::string& file_id) { + scoped_ptr<base::DictionaryValue> parent(new base::DictionaryValue); + parent->SetString("kind", kParentLinkKind); + parent->SetString("id", file_id); + return parent.Pass(); +} + } // namespace util } // namespace google_apis diff --git a/google_apis/drive/request_util.h b/google_apis/drive/request_util.h index f0672ed..1dc6f5e 100644 --- a/google_apis/drive/request_util.h +++ b/google_apis/drive/request_util.h @@ -6,17 +6,26 @@ #define GOOGLE_APIS_DRIVE_REQUEST_UTIL_H_ #include <string> +#include "base/memory/scoped_ptr.h" + +namespace base { +class DictionaryValue; +} namespace google_apis { namespace util { // If-Match header which matches to all etags. extern const char kIfMatchAllHeader[]; +extern const char kContentTypeApplicationJson[]; // Returns If-Match header string for |etag|. // If |etag| is empty, the returned header should match any etag. std::string GenerateIfMatchHeader(const std::string& etag); +// Creates a Parent value which can be used as a part of request body. +scoped_ptr<base::DictionaryValue> CreateParentValue(const std::string& file_id); + } // namespace util } // namespace google_apis |