summaryrefslogtreecommitdiffstats
path: root/google_apis
diff options
context:
space:
mode:
Diffstat (limited to 'google_apis')
-rw-r--r--google_apis/drive/base_requests.cc206
-rw-r--r--google_apis/drive/base_requests.h87
-rw-r--r--google_apis/drive/base_requests_unittest.cc97
-rw-r--r--google_apis/drive/drive_api_requests.cc30
-rw-r--r--google_apis/drive/drive_api_requests.h6
-rw-r--r--google_apis/drive/request_util.cc11
-rw-r--r--google_apis/drive/request_util.h9
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