Commit 61f5a0e0 authored by kochi@chromium.org's avatar kochi@chromium.org

Add parsers for Drive v2 Change/ChangeList/Parents

Parent means a folder containing a file in Drive v2 API.
If a file does not contain any reference to its parent, it should be
interpreted as the file is under the root directory.
Changed existing unit test for checking the case that parent is missing.

BUG=chromium:127728
TEST=unit_tests --gtest_filter="DriveAPIParserTest.*"

Review URL: https://chromiumcodereview.appspot.com/10829139

git-svn-id: svn://svn.chromium.org/chrome/trunk/src@149768 0039d316-1c4b-4281-b951-d872f2087c98
parent 7e7b782e
......@@ -37,13 +37,13 @@ const char kKind[] = "kind";
const char kId[] = "id";
const char kETag[] = "etag";
const char kItems[] = "items";
const char kLargestChangeId[] = "largestChangeId";
// About Resource:
const char kAboutKind[] = "drive#about";
const char kRootFolderId[] = "rootFolderId";
const char kQuotaBytesTotal[] = "quotaBytesTotal";
const char kQuotaBytesUsed[] = "quotaBytesUsed";
const char kLargestChangeId[] = "largestChangeId";
// App Icon:
const char kCategory[] = "category";
......@@ -68,11 +68,16 @@ const char kIcons[] = "icons";
// Apps List:
const char kAppListKind[] = "drive#appList";
// Parent Resource:
const char kParentReferenceKind[] = "drive#parentReference";
const char kIsRoot[] = "isRoot";
// File Resource:
const char kFileKind[] = "drive#file";
const char kMimeType[] = "mimeType";
const char kTitle[] = "title";
const char kModifiedByMeDate[] = "modifiedByMeDate";
const char kParents[] = "parents";
const char kDownloadUrl[] = "downloadUrl";
const char kFileExtension[] = "fileExtension";
const char kMd5Checksum[] = "md5Checksum";
......@@ -85,6 +90,14 @@ const char kFileListKind[] = "drive#fileList";
const char kNextPageToken[] = "nextPageToken";
const char kNextLink[] = "nextLink";
// Change Resource:
const char kChangeKind[] = "drive#change";
const char kFileId[] = "fileId";
const char kFile[] = "file";
// Changes List:
const char kChangeListKind[] = "drive#changeList";
// Maps category name to enum IconCategory.
struct AppIconCategoryMap {
gdata::DriveAppIcon::IconCategory category;
......@@ -295,6 +308,41 @@ bool AppList::Parse(const base::Value& value) {
return true;
}
////////////////////////////////////////////////////////////////////////////////
// ParentReference implementation
ParentReference::ParentReference() : is_root_(false) {}
ParentReference::~ParentReference() {}
// static
void ParentReference::RegisterJSONConverter(
base::JSONValueConverter<ParentReference>* converter) {
converter->RegisterStringField(kId, &ParentReference::file_id_);
converter->RegisterBoolField(kIsRoot, &ParentReference::is_root_);
}
// static
scoped_ptr<ParentReference>
ParentReference::CreateFrom(const base::Value& value) {
scoped_ptr<ParentReference> reference(new ParentReference());
if (!IsResourceKindExpected(value, kParentReferenceKind) ||
!reference->Parse(value)) {
LOG(ERROR) << "Unable to create: Invalid ParentRefernce JSON!";
return scoped_ptr<ParentReference>(NULL);
}
return reference.Pass();
}
bool ParentReference::Parse(const base::Value& value) {
base::JSONValueConverter<ParentReference> converter;
if (!converter.Convert(value, this)) {
LOG(ERROR) << "Unable to parse: Invalid ParentReference";
return false;
}
return true;
}
////////////////////////////////////////////////////////////////////////////////
// FileResource implementation
......@@ -313,6 +361,8 @@ void FileResource::RegisterJSONConverter(
kModifiedByMeDate,
&FileResource::modified_by_me_date_,
&gdata::util::GetTimeFromString);
converter->RegisterRepeatedMessage<ParentReference>(kParents,
&FileResource::parents_);
converter->RegisterCustomField<GURL>(kDownloadUrl,
&FileResource::download_url_,
GetGURLFromString);
......@@ -386,4 +436,83 @@ bool FileList::Parse(const base::Value& value) {
return true;
}
////////////////////////////////////////////////////////////////////////////////
// ChangeResource implementation
ChangeResource::ChangeResource() : change_id_(0), deleted_(false) {}
ChangeResource::~ChangeResource() {}
// static
void ChangeResource::RegisterJSONConverter(
base::JSONValueConverter<ChangeResource>* converter) {
converter->RegisterCustomField<int64>(kId,
&ChangeResource::change_id_,
&base::StringToInt64);
converter->RegisterStringField(kFileId, &ChangeResource::file_id_);
converter->RegisterNestedField(kFile, &ChangeResource::file_);
}
// static
scoped_ptr<ChangeResource>
ChangeResource::CreateFrom(const base::Value& value) {
scoped_ptr<ChangeResource> resource(new ChangeResource());
if (!IsResourceKindExpected(value, kChangeKind) || !resource->Parse(value)) {
LOG(ERROR) << "Unable to create: Invalid ChangeResource JSON!";
return scoped_ptr<ChangeResource>(NULL);
}
return resource.Pass();
}
bool ChangeResource::Parse(const base::Value& value) {
base::JSONValueConverter<ChangeResource> converter;
if (!converter.Convert(value, this)) {
LOG(ERROR) << "Unable to parse: Invalid ChangeResource";
return false;
}
return true;
}
////////////////////////////////////////////////////////////////////////////////
// ChangeList implementation
ChangeList::ChangeList() : largest_change_id_(0) {}
ChangeList::~ChangeList() {}
// static
void ChangeList::RegisterJSONConverter(
base::JSONValueConverter<ChangeList>* converter) {
converter->RegisterStringField(kETag, &ChangeList::etag_);
converter->RegisterStringField(kNextPageToken, &ChangeList::next_page_token_);
converter->RegisterCustomField<GURL>(kNextLink,
&ChangeList::next_link_,
GetGURLFromString);
converter->RegisterCustomField<int64>(kLargestChangeId,
&ChangeList::largest_change_id_,
&base::StringToInt64);
converter->RegisterRepeatedMessage<ChangeResource>(kItems,
&ChangeList::items_);
}
// static
scoped_ptr<ChangeList> ChangeList::CreateFrom(const base::Value& value) {
scoped_ptr<ChangeList> resource(new ChangeList());
if (!IsResourceKindExpected(value, kChangeListKind) ||
!resource->Parse(value)) {
LOG(ERROR) << "Unable to create: Invalid ChangeList JSON!";
return scoped_ptr<ChangeList>(NULL);
}
return resource.Pass();
}
bool ChangeList::Parse(const base::Value& value) {
base::JSONValueConverter<ChangeList> converter;
if (!converter.Convert(value, this)) {
LOG(ERROR) << "Unable to parse: Invalid ChangeList";
return false;
}
return true;
}
} // namespace gdata
......@@ -258,7 +258,41 @@ class AppList {
DISALLOW_COPY_AND_ASSIGN(AppList);
};
// FileResource reporesents a file or folder metadata in Drive.
// ParentReference represents a directory.
// https://developers.google.com/drive/v2/reference/parents
class ParentReference {
public:
~ParentReference();
// Registers the mapping between JSON field names and the members in this
// class.
static void RegisterJSONConverter(
base::JSONValueConverter<ParentReference>* converter);
// Creates parent reference from parsed JSON.
static scoped_ptr<ParentReference> CreateFrom(const base::Value& value);
// Returns the file id of the reference.
const std::string& file_id() const { return file_id_; }
// Returns true if the reference is root directory.
bool is_root() const { return is_root_; }
private:
friend class base::internal::RepeatedMessageConverter<ParentReference>;
ParentReference();
// Parses and initializes data members from content of |value|.
// Return false if parsing fails.
bool Parse(const base::Value& value);
std::string file_id_;
bool is_root_;
DISALLOW_COPY_AND_ASSIGN(ParentReference);
};
// FileResource represents a file or folder metadata in Drive.
// https://developers.google.com/drive/v2/reference/files
class FileResource {
public:
......@@ -268,6 +302,8 @@ class FileResource {
// class.
static void RegisterJSONConverter(
base::JSONValueConverter<FileResource>* converter);
// Creates file resource from parsed JSON.
static scoped_ptr<FileResource> CreateFrom(const base::Value& value);
// Returns true if this is a directory.
......@@ -290,6 +326,9 @@ class FileResource {
// Returns modification time by the user.
const base::Time& modified_by_me_date() const { return modified_by_me_date_; }
// Returns parent references (directories) of this file.
const ScopedVector<ParentReference>& parents() const { return parents_; }
// Returns the download URL.
const GURL& download_url() const { return download_url_; }
......@@ -304,6 +343,7 @@ class FileResource {
private:
friend class base::internal::RepeatedMessageConverter<FileResource>;
friend class ChangeResource;
friend class FileList;
FileResource();
......@@ -316,6 +356,7 @@ class FileResource {
std::string mime_type_;
std::string title_;
base::Time modified_by_me_date_;
ScopedVector<ParentReference> parents_;
GURL download_url_;
std::string file_extension_;
std::string md5_checksum_;
......@@ -334,6 +375,8 @@ class FileList {
// class.
static void RegisterJSONConverter(
base::JSONValueConverter<FileList>* converter);
// Creates file list from parsed JSON.
static scoped_ptr<FileList> CreateFrom(const base::Value& value);
// Returns the ETag of the list.
......@@ -367,6 +410,99 @@ class FileList {
DISALLOW_COPY_AND_ASSIGN(FileList);
};
// ChangeResource represents a change in a file.
// https://developers.google.com/drive/v2/reference/changes
class ChangeResource {
public:
~ChangeResource();
// Registers the mapping between JSON field names and the members in this
// class.
static void RegisterJSONConverter(
base::JSONValueConverter<ChangeResource>* converter);
// Creates change resource from parsed JSON.
static scoped_ptr<ChangeResource> CreateFrom(const base::Value& value);
// Returns change ID for this change. This is a monotonically increasing
// number.
int64 change_id() const { return change_id_; }
// Returns a string file ID for corresponding file of the change.
const std::string& file_id() const { return file_id_; }
// Returns true if this file is deleted in the change.
bool is_deleted() const { return deleted_; }
// Returns FileResource of the file which the change refers to.
const FileResource& file() const { return file_; }
private:
friend class base::internal::RepeatedMessageConverter<ChangeResource>;
friend class ChangeList;
ChangeResource();
// Parses and initializes data members from content of |value|.
// Return false if parsing fails.
bool Parse(const base::Value& value);
int64 change_id_;
std::string file_id_;
bool deleted_;
FileResource file_;
DISALLOW_COPY_AND_ASSIGN(ChangeResource);
};
// ChangeList represents a set of changes in the drive.
// https://developers.google.com/drive/v2/reference/changes/list
class ChangeList {
public:
~ChangeList();
// Registers the mapping between JSON field names and the members in this
// class.
static void RegisterJSONConverter(
base::JSONValueConverter<ChangeList>* converter);
// Creates change list from parsed JSON.
static scoped_ptr<ChangeList> CreateFrom(const base::Value& value);
// Returns the ETag of the list.
const std::string& etag() const { return etag_; }
// Returns the page token for the next page of files, if the list is large
// to fit in one response. If this is empty, there is no more file lists.
const std::string& next_page_token() const { return next_page_token_; }
// Returns a link to the next page of files. The URL includes the next page
// token.
const GURL& next_link() const { return next_link_; }
// Returns the largest change ID number.
int64 largest_change_id() const { return largest_change_id_; }
// Returns a set of changes in this list.
const ScopedVector<ChangeResource>& items() const { return items_; }
private:
friend class DriveAPIParserTest;
FRIEND_TEST_ALL_PREFIXES(DriveAPIParserTest, ChangeListParser);
ChangeList();
// Parses and initializes data members from content of |value|.
// Return false if parsing fails.
bool Parse(const base::Value& value);
std::string etag_;
std::string next_page_token_;
GURL next_link_;
int64 largest_change_id_;
ScopedVector<ChangeResource> items_;
DISALLOW_COPY_AND_ASSIGN(ChangeList);
};
} // namespace gdata
#endif // CHROME_BROWSER_CHROMEOS_GDATA_DRIVE_API_PARSER_H_
......@@ -174,6 +174,11 @@ TEST_F(DriveAPIParserTest, FileListParser) {
ASSERT_TRUE(gdata::util::GetTimeFromString("2012-07-27T05:43:20.269Z",
&modified_time));
EXPECT_EQ(modified_time, file1.modified_by_me_date());
ASSERT_EQ(1U, file1.parents().size());
EXPECT_EQ("0B4v7G8yEYAWHYW1OcExsUVZLABC", file1.parents()[0]->file_id());
EXPECT_FALSE(file1.parents()[0]->is_root());
EXPECT_EQ(GURL("https://www.example.com/download"), file1.download_url());
EXPECT_EQ("ext", file1.file_extension());
EXPECT_EQ("d41d8cd98f00b204e9800998ecf8427e", file1.md5_checksum());
......@@ -185,12 +190,52 @@ TEST_F(DriveAPIParserTest, FileListParser) {
EXPECT_EQ("application/vnd.google-apps.document", file2.mime_type());
EXPECT_EQ(0U, file2.file_size());
ASSERT_EQ(0U, file2.parents().size());
// Check file 3 (a folder)
const FileResource& file3 = *filelist->items()[2];
EXPECT_EQ(0U, file3.file_size());
EXPECT_EQ("TestFolder", file3.title());
EXPECT_EQ("application/vnd.google-apps.folder", file3.mime_type());
ASSERT_TRUE(file3.IsDirectory());
ASSERT_EQ(1U, file3.parents().size());
EXPECT_EQ("0AIv7G8yEYAWHUk9ABC", file3.parents()[0]->file_id());
EXPECT_TRUE(file3.parents()[0]->is_root());
}
// Test change list parsing.
TEST_F(DriveAPIParserTest, ChangeListParser) {
std::string error;
scoped_ptr<Value> document(LoadJSONFile("changelist.json"));
ASSERT_TRUE(document.get());
ASSERT_EQ(Value::TYPE_DICTIONARY, document->GetType());
scoped_ptr<ChangeList> changelist(new ChangeList);
EXPECT_TRUE(changelist->Parse(*document));
EXPECT_EQ("\"Lp2bjAtLP341hvGmYHhxjYyBPJ8/BWbu_eylt5f_aGtCN6mGRv9hABC\"",
changelist->etag());
EXPECT_EQ("8929", changelist->next_page_token());
EXPECT_EQ("https://www.googleapis.com/drive/v2/changes?pageToken=8929",
changelist->next_link().spec());
EXPECT_EQ(13664, changelist->largest_change_id());
ASSERT_EQ(3U, changelist->items().size());
const ChangeResource& change1 = *changelist->items()[0];
EXPECT_EQ(8421, change1.change_id());
EXPECT_FALSE(change1.is_deleted());
EXPECT_EQ(change1.file_id(), change1.file().file_id());
const ChangeResource& change2 = *changelist->items()[1];
EXPECT_EQ(8424, change2.change_id());
EXPECT_FALSE(change2.is_deleted());
EXPECT_EQ(change2.file_id(), change2.file().file_id());
const ChangeResource& change3 = *changelist->items()[2];
EXPECT_EQ(8429, change3.change_id());
EXPECT_FALSE(change3.is_deleted());
EXPECT_EQ(change3.file_id(), change3.file().file_id());
}
} // namespace gdata
{
"kind": "drive#changeList",
"etag": "\"Lp2bjAtLP341hvGmYHhxjYyBPJ8/BWbu_eylt5f_aGtCN6mGRv9hABC\"",
"selfLink": "https://www.googleapis.com/drive/v2/changes",
"nextPageToken": "8929",
"nextLink": "https://www.googleapis.com/drive/v2/changes?pageToken=8929",
"largestChangeId": "13664",
"items": [
{
"kind": "drive#change",
"id": "8421",
"fileId": "1Pc8jzfU1ErbN_eucMMqdqzY3eBm0v8sxXm_1CtLxABC",
"selfLink": "https://www.googleapis.com/drive/v2/changes/8421",
"deleted": false,
"file": {
"kind": "drive#file",
"id": "1Pc8jzfU1ErbN_eucMMqdqzY3eBm0v8sxXm_1CtLxABC",
"etag": "\"Lp2bjAtLP341hvGmYHhxjYyBPJ8/MTM0MzM2NDg1MTAABC\"",
"selfLink": "https://www.googleapis.com/drive/v2/files/1Pc8jzfU1ErbN_eucMMqdqzY3eBm0v8sxXm_1CtLxABC",
"alternateLink": "https://docs.google.com/a/chromium.org/document/d/1Pc8jzfU1ErbN_eucMMqdqzY3eBm0v8sxXm_1CtLxABC/edit",
"embedLink": "https://docs.google.com/a/chromium.org/document/d/1Pc8jzfU1ErbN_eucMMqdqzY3eBm0v8sxXm_1CtLxABC/preview",
"thumbnailLink": "https://docs.google.com/feeds/vt?gd=true&id=1Pc8jzfU1ErbN_eucMMqdqzY3eBm0v8sxXm_1CtLxABC&v=3&s=AMedNnoAAAAAUBo617fje6DQnYkylcdbcGp8aXpFiXYZ&sz=s220",
"title": "Test Google Document",
"mimeType": "application/vnd.google-apps.document",
"labels": {
"starred": false,
"hidden": false,
"trashed": false,
"restricted": false,
"viewed": true
},
"createdDate": "2012-07-27T04:54:09.686Z",
"modifiedDate": "2012-07-27T04:54:11.030Z",
"modifiedByMeDate": "2012-07-27T04:54:11.030Z",
"lastViewedByMeDate": "2012-07-27T04:54:11.030Z",
"parents": [
{
"kind": "drive#parentReference",
"id": "0AIv7G8yEYAWHUk9ABC",
"selfLink": "https://www.googleapis.com/drive/v2/files/1Pc8jzfU1ErbN_eucMMqdqzY3eBm0v8sxXm_1CtLxABC/parents/0AIv7G8yEYAWHUk9ABC",
"parentLink": "https://www.googleapis.com/drive/v2/files/0AIv7G8yEYAWHUk9ABC",
"isRoot": true
}
],
"exportLinks": {
"application/msword": "https://docs.google.com/feeds/download/documents/export/Export?id=1Pc8jzfU1ErbN_eucMMqdqzY3eBm0v8sxXm_1CtLxABC&exportFormat=doc",
"text/html": "https://docs.google.com/feeds/download/documents/export/Export?id=1Pc8jzfU1ErbN_eucMMqdqzY3eBm0v8sxXm_1CtLxABC&exportFormat=html",
"application/pdf": "https://docs.google.com/feeds/download/documents/export/Export?id=1Pc8jzfU1ErbN_eucMMqdqzY3eBm0v8sxXm_1CtLxABC&exportFormat=pdf"
},
"userPermission": {
"kind": "drive#permission",
"etag": "\"Lp2bjAtLP341hvGmYHhxjYyBPJ8/ufh4Gu_RoDI23a1Nr8tDm1yJXYZ\"",
"id": "me",
"selfLink": "https://www.googleapis.com/drive/v2/files/1Pc8jzfU1ErbN_eucMMqdqzY3eBm0v8sxXm_1CtLxABC/permissions/me",
"role": "owner",
"type": "user"
},
"quotaBytesUsed": "0",
"ownerNames": [
"Test User"
],
"lastModifyingUserName": "Test User",
"editable": true,
"writersCanShare": true
}
},
{
"kind": "drive#change",
"id": "8424",
"fileId": "0B4v7G8yEYAWHUmRrU2lMS2hLABC",
"selfLink": "https://www.googleapis.com/drive/v2/changes/8424",
"deleted": false,
"file": {
"kind": "drive#file",
"id": "0B4v7G8yEYAWHUmRrU2lMS2hLABC",
"etag": "\"Lp2bjAtLP341hvGmYHhxjYyBPJ8/MTM0MzM2NzgwMDIABC\"",
"selfLink": "https://www.googleapis.com/drive/v2/files/0B4v7G8yEYAWHUmRrU2lMS2hLABC",
"webContentLink": "https://docs.google.com/a/chromium.org/uc?id=0B4v7G8yEYAWHUmRrU2lMS2hLABC&export=download",
"alternateLink": "https://docs.google.com/a/chromium.org/file/d/0B4v7G8yEYAWHUmRrU2lMS2hLABC/edit",
"title": "My first file data",
"mimeType": "application/octet-stream",
"labels": {
"starred": false,
"hidden": false,
"trashed": false,
"restricted": false,
"viewed": true
},
"createdDate": "2012-07-24T08:51:16.570Z",
"modifiedDate": "2012-07-27T05:43:20.269Z",
"modifiedByMeDate": "2012-07-27T05:43:20.269Z",
"lastViewedByMeDate": "2012-07-27T05:43:20.269Z",
"parents": [
{
"kind": "drive#parentReference",
"id": "0AIv7G8yEYAWHUk9ABC",
"selfLink": "https://www.googleapis.com/drive/v2/files/0B4v7G8yEYAWHUmRrU2lMS2hLABC/parents/0AIv7G8yEYAWHUk9ABC",
"parentLink": "https://www.googleapis.com/drive/v2/files/0AIv7G8yEYAWHUk9ABC",
"isRoot": true
}
],
"downloadUrl": "https://www.example.com/download",
"userPermission": {
"kind": "drive#permission",
"etag": "\"Lp2bjAtLP341hvGmYHhxjYyBPJ8/TZY6eNr9QBUIWHY-czo93820XYZ\"",
"id": "me",
"selfLink": "https://www.googleapis.com/drive/v2/files/0B4v7G8yEYAWHUmRrU2lMS2hLABC/permissions/me",
"role": "owner",
"type": "user"
},
"originalFilename": "Untitled Document.ext",
"fileExtension": "ext",
"md5Checksum": "d41d8cd98f00b204e9800998ecf8427e",
"fileSize": "1000",
"quotaBytesUsed": "1000",
"ownerNames": [
"Test User"
],
"lastModifyingUserName": "Takayoshi Kochi",
"editable": true,
"writersCanShare": true
}
},
{
"kind": "drive#change",
"id": "8429",
"fileId": "0B4v7G8yEYAWHYW1OcExsUVZLABC",
"selfLink": "https://www.googleapis.com/drive/v2/changes/8429",
"deleted": false,
"file": {
"kind": "drive#file",
"id": "0B4v7G8yEYAWHYW1OcExsUVZLABC",
"etag": "\"Lp2bjAtLP341hvGmYHhxjYyBPJ8/MTM0MzM2NDE5ODUABC\"",
"selfLink": "https://www.googleapis.com/drive/v2/files/0B4v7G8yEYAWHYW1OcExsUVZLABC",
"alternateLink": "https://docs.google.com/a/chromium.org/folder/d/0B4v7G8yEYAWHYW1OcExsUVZLABC/edit",
"title": "TestFolder",
"mimeType": "application/vnd.google-apps.folder",
"labels": {
"starred": false,
"hidden": false,
"trashed": false,
"restricted": false,
"viewed": true
},
"createdDate": "2012-07-02T09:53:18.422Z",
"modifiedDate": "2012-07-27T04:43:18.507Z",
"modifiedByMeDate": "2012-07-27T04:43:18.507Z",
"lastViewedByMeDate": "2012-07-27T04:40:52.371Z",
"parents": [
{
"kind": "drive#parentReference",
"id": "0AIv7G8yEYAWHUk9ABC",
"selfLink": "https://www.googleapis.com/drive/v2/files/0B4v7G8yEYAWHYW1OcExsUVZLABC/parents/0AIv7G8yEYAWHUk9ABC",
"parentLink": "https://www.googleapis.com/drive/v2/files/0AIv7G8yEYAWHUk9ABC",
"isRoot": true
}
],
"userPermission": {
"kind": "drive#permission",
"etag": "\"Lp2bjAtLP341hvGmYHhxjYyBPJ8/ID_i9phaKU8rzxPcIL8yOWlMABC\"",
"id": "me",
"selfLink": "https://www.googleapis.com/drive/v2/files/0B4v7G8yEYAWHYW1OcExsUVZLABC/permissions/me",
"role": "owner",
"type": "user"
},
"quotaBytesUsed": "0",
"ownerNames": [
"Test User"
],
"lastModifyingUserName": "Test User",
"editable": true,
"writersCanShare": true
}
}
]
}
......@@ -75,15 +75,6 @@
"modifiedDate": "2012-07-27T04:54:11.030Z",
"modifiedByMeDate": "2012-07-27T04:54:11.030Z",
"lastViewedByMeDate": "2012-07-27T04:54:11.030Z",
"parents": [
{
"kind": "drive#parentReference",
"id": "0AIv7G8yEYAWHUk9ABC",
"selfLink": "https://www.googleapis.com/drive/v2/files/1Pc8jzfU1ErbN_eucMMqdqzY3eBm0v8sxXm_1CtLxABC/parents/0AIv7G8yEYAWHUk9ABC",
"parentLink": "https://www.googleapis.com/drive/v2/files/0AIv7G8yEYAWHUk9ABC",
"isRoot": true
}
],
"exportLinks": {
"application/msword": "https://docs.google.com/feeds/download/documents/export/Export?id=1Pc8jzfU1ErbN_eucMMqdqzY3eBm0v8sxXm_1CtLxABC&exportFormat=doc",
"text/html": "https://docs.google.com/feeds/download/documents/export/Export?id=1Pc8jzfU1ErbN_eucMMqdqzY3eBm0v8sxXm_1CtLxABC&exportFormat=html",
......
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