gdata_wapi_url_generator.cc revision a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7
1// Copyright (c) 2012 The Chromium Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5#include "google_apis/drive/gdata_wapi_url_generator.h"
6
7#include "base/logging.h"
8#include "base/strings/string_number_conversions.h"
9#include "base/strings/stringprintf.h"
10#include "net/base/escape.h"
11#include "net/base/url_util.h"
12#include "url/gurl.h"
13
14namespace google_apis {
15namespace {
16
17// Content URL for modification or resource list retrieval in a particular
18// directory specified by "%s" which will be replaced with its resource id.
19const char kContentURLFormat[] = "/feeds/default/private/full/%s/contents";
20
21// Content URL for removing a resource specified by the latter "%s" from the
22// directory specified by the former "%s".
23const char kResourceURLForRemovalFormat[] =
24    "/feeds/default/private/full/%s/contents/%s";
25
26// URL requesting single resource entry whose resource id is followed by this
27// prefix.
28const char kGetEditURLPrefix[] = "/feeds/default/private/full/";
29
30// Root resource list url.
31const char kResourceListRootURL[] = "/feeds/default/private/full";
32
33// Metadata feed with things like user quota.
34const char kAccountMetadataURL[] = "/feeds/metadata/default";
35
36// URL to upload a new file under a particular directory specified by "%s".
37const char kInitiateUploadNewFileURLFormat[] =
38    "/feeds/upload/create-session/default/private/full/%s/contents";
39
40// URL to upload a file content to overwrite a file whose resource id is
41// followed by this prefix.
42const char kInitiateUploadExistingFileURLPrefix[] =
43    "/feeds/upload/create-session/default/private/full/";
44
45// Maximum number of resource entries to include in a feed.
46// Be careful not to use something too small because it might overload the
47// server. Be careful not to use something too large because it makes the
48// "fetched N items" UI less responsive.
49const int kMaxDocumentsPerFeed = 500;
50const int kMaxDocumentsPerSearchFeed = 50;
51
52// URL requesting documents list of changes to documents collections.
53const char kGetChangesListURL[] = "/feeds/default/private/changes";
54
55}  // namespace
56
57const char GDataWapiUrlGenerator::kBaseUrlForProduction[] =
58    "https://docs.google.com/";
59
60const char GDataWapiUrlGenerator::kBaseDownloadUrlForProduction[] =
61    "https://www.googledrive.com/host/";
62
63// static
64GURL GDataWapiUrlGenerator::AddStandardUrlParams(const GURL& url) {
65  GURL result = net::AppendOrReplaceQueryParameter(url, "v", "3");
66  result = net::AppendOrReplaceQueryParameter(result, "alt", "json");
67  result = net::AppendOrReplaceQueryParameter(result, "showroot", "true");
68  return result;
69}
70
71// static
72GURL GDataWapiUrlGenerator::AddInitiateUploadUrlParams(const GURL& url) {
73  GURL result = net::AppendOrReplaceQueryParameter(url, "convert", "false");
74  return AddStandardUrlParams(result);
75}
76
77// static
78GURL GDataWapiUrlGenerator::AddFeedUrlParams(
79    const GURL& url,
80    int num_items_to_fetch) {
81  GURL result = AddStandardUrlParams(url);
82  result = net::AppendOrReplaceQueryParameter(result, "showfolders", "true");
83  result = net::AppendOrReplaceQueryParameter(result, "include-shared", "true");
84  result = net::AppendOrReplaceQueryParameter(
85      result, "max-results", base::IntToString(num_items_to_fetch));
86  return result;
87}
88
89GDataWapiUrlGenerator::GDataWapiUrlGenerator(const GURL& base_url,
90                                             const GURL& base_download_url)
91    : base_url_(base_url),
92      base_download_url_(base_download_url) {
93}
94
95GDataWapiUrlGenerator::~GDataWapiUrlGenerator() {
96}
97
98GURL GDataWapiUrlGenerator::GenerateResourceListUrl(
99    const GURL& override_url,
100    int64 start_changestamp,
101    const std::string& search_string,
102    const std::string& directory_resource_id) const {
103  DCHECK_LE(0, start_changestamp);
104
105  int max_docs = search_string.empty() ? kMaxDocumentsPerFeed :
106                                         kMaxDocumentsPerSearchFeed;
107  GURL url;
108  if (!override_url.is_empty()) {
109    // |override_url| specifies the URL of the continuation feed when the feed
110    // is broken up to multiple chunks. In this case we must not add the
111    // |start_changestamp| that provides the original start point.
112    start_changestamp = 0;
113    url = override_url;
114  } else if (start_changestamp > 0) {
115    // The start changestamp shouldn't be used for a search.
116    DCHECK(search_string.empty());
117    url = base_url_.Resolve(kGetChangesListURL);
118  } else if (!directory_resource_id.empty()) {
119    url = base_url_.Resolve(
120        base::StringPrintf(kContentURLFormat,
121                           net::EscapePath(
122                               directory_resource_id).c_str()));
123  } else {
124    url = base_url_.Resolve(kResourceListRootURL);
125  }
126
127  url = AddFeedUrlParams(url, max_docs);
128
129  if (start_changestamp) {
130    url = net::AppendOrReplaceQueryParameter(
131        url, "start-index", base::Int64ToString(start_changestamp));
132  }
133  if (!search_string.empty()) {
134    url = net::AppendOrReplaceQueryParameter(url, "q", search_string);
135  }
136
137  return url;
138}
139
140GURL GDataWapiUrlGenerator::GenerateSearchByTitleUrl(
141    const std::string& title,
142    const std::string& directory_resource_id) const {
143  DCHECK(!title.empty());
144
145  GURL url = directory_resource_id.empty() ?
146      base_url_.Resolve(kResourceListRootURL) :
147      base_url_.Resolve(base::StringPrintf(
148          kContentURLFormat, net::EscapePath(directory_resource_id).c_str()));
149  url = AddFeedUrlParams(url, kMaxDocumentsPerFeed);
150  url = net::AppendOrReplaceQueryParameter(url, "title", title);
151  url = net::AppendOrReplaceQueryParameter(url, "title-exact", "true");
152  return url;
153}
154
155GURL GDataWapiUrlGenerator::GenerateEditUrl(
156    const std::string& resource_id) const {
157  return AddStandardUrlParams(GenerateEditUrlWithoutParams(resource_id));
158}
159
160GURL GDataWapiUrlGenerator::GenerateEditUrlWithoutParams(
161    const std::string& resource_id) const {
162  return base_url_.Resolve(kGetEditURLPrefix + net::EscapePath(resource_id));
163}
164
165GURL GDataWapiUrlGenerator::GenerateEditUrlWithEmbedOrigin(
166    const std::string& resource_id, const GURL& embed_origin) const {
167  GURL url = GenerateEditUrl(resource_id);
168  if (!embed_origin.is_empty()) {
169    // Construct a valid serialized embed origin from an url, according to
170    // WD-html5-20110525. Such string has to be built manually, since
171    // GURL::spec() always adds the trailing slash. Moreover, ports are
172    // currently not supported.
173    DCHECK(!embed_origin.has_port());
174    DCHECK(!embed_origin.has_path() || embed_origin.path() == "/");
175    const std::string serialized_embed_origin =
176        embed_origin.scheme() + "://" + embed_origin.host();
177    url = net::AppendOrReplaceQueryParameter(
178        url, "embedOrigin", serialized_embed_origin);
179  }
180  return url;
181}
182
183GURL GDataWapiUrlGenerator::GenerateContentUrl(
184    const std::string& resource_id) const {
185  if (resource_id.empty()) {
186    // |resource_id| must not be empty. Return an empty GURL as an error.
187    return GURL();
188  }
189
190  GURL result = base_url_.Resolve(
191      base::StringPrintf(kContentURLFormat,
192                         net::EscapePath(resource_id).c_str()));
193  return AddStandardUrlParams(result);
194}
195
196GURL GDataWapiUrlGenerator::GenerateResourceUrlForRemoval(
197    const std::string& parent_resource_id,
198    const std::string& resource_id) const {
199  if (resource_id.empty() || parent_resource_id.empty()) {
200    // Both |resource_id| and |parent_resource_id| must be non-empty.
201    // Return an empty GURL as an error.
202    return GURL();
203  }
204
205  GURL result = base_url_.Resolve(
206      base::StringPrintf(kResourceURLForRemovalFormat,
207                         net::EscapePath(parent_resource_id).c_str(),
208                         net::EscapePath(resource_id).c_str()));
209  return AddStandardUrlParams(result);
210}
211
212GURL GDataWapiUrlGenerator::GenerateInitiateUploadNewFileUrl(
213    const std::string& parent_resource_id) const {
214  GURL result = base_url_.Resolve(
215      base::StringPrintf(kInitiateUploadNewFileURLFormat,
216                         net::EscapePath(parent_resource_id).c_str()));
217  return AddInitiateUploadUrlParams(result);
218}
219
220GURL GDataWapiUrlGenerator::GenerateInitiateUploadExistingFileUrl(
221    const std::string& resource_id) const {
222  GURL result = base_url_.Resolve(
223      kInitiateUploadExistingFileURLPrefix + net::EscapePath(resource_id));
224  return AddInitiateUploadUrlParams(result);
225}
226
227GURL GDataWapiUrlGenerator::GenerateResourceListRootUrl() const {
228  return AddStandardUrlParams(base_url_.Resolve(kResourceListRootURL));
229}
230
231GURL GDataWapiUrlGenerator::GenerateAccountMetadataUrl(
232    bool include_installed_apps) const {
233  GURL result = AddStandardUrlParams(base_url_.Resolve(kAccountMetadataURL));
234  if (include_installed_apps) {
235    result = net::AppendOrReplaceQueryParameter(
236        result, "include-installed-apps", "true");
237  }
238  return result;
239}
240
241GURL GDataWapiUrlGenerator::GenerateDownloadFileUrl(
242    const std::string& resource_id) const {
243  // Strip the file type prefix before the colon character.
244  size_t colon = resource_id.find(':');
245  return base_download_url_.Resolve(net::EscapePath(
246      colon == std::string::npos ? resource_id
247                                 : resource_id.substr(colon + 1)));
248}
249
250}  // namespace google_apis
251