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 "chrome/browser/google_apis/drive_api_requests.h"
6
7#include "base/bind.h"
8#include "base/callback.h"
9#include "base/json/json_writer.h"
10#include "base/values.h"
11#include "chrome/browser/google_apis/drive_api_parser.h"
12#include "chrome/browser/google_apis/request_util.h"
13#include "chrome/browser/google_apis/time_util.h"
14
15namespace google_apis {
16namespace {
17
18const char kContentTypeApplicationJson[] = "application/json";
19const char kDirectoryMimeType[] = "application/vnd.google-apps.folder";
20const char kParentLinkKind[] = "drive#fileLink";
21
22// Parses the JSON value to a resource typed |T| and runs |callback| on the UI
23// thread once parsing is done.
24template<typename T>
25void ParseJsonAndRun(
26    const base::Callback<void(GDataErrorCode, scoped_ptr<T>)>& callback,
27    GDataErrorCode error,
28    scoped_ptr<base::Value> value) {
29  DCHECK(!callback.is_null());
30
31  scoped_ptr<T> resource;
32  if (value) {
33    resource = T::CreateFrom(*value);
34    if (!resource) {
35      // Failed to parse the JSON value, although the JSON value is available,
36      // so let the callback know the parsing error.
37      error = GDATA_PARSE_ERROR;
38    }
39  }
40
41  callback.Run(error, resource.Pass());
42}
43
44// Parses the JSON value to FileResource instance and runs |callback| on the
45// UI thread once parsing is done.
46// This is customized version of ParseJsonAndRun defined above to adapt the
47// remaining response type.
48void ParseFileResourceWithUploadRangeAndRun(
49    const drive::UploadRangeCallback& callback,
50    const UploadRangeResponse& response,
51    scoped_ptr<base::Value> value) {
52  DCHECK(!callback.is_null());
53
54  scoped_ptr<FileResource> file_resource;
55  if (value) {
56    file_resource = FileResource::CreateFrom(*value);
57    if (!file_resource) {
58      callback.Run(
59          UploadRangeResponse(GDATA_PARSE_ERROR,
60                              response.start_position_received,
61                              response.end_position_received),
62          scoped_ptr<FileResource>());
63      return;
64    }
65  }
66
67  callback.Run(response, file_resource.Pass());
68}
69
70}  // namespace
71
72//============================== GetAboutRequest =============================
73
74GetAboutRequest::GetAboutRequest(
75    RequestSender* sender,
76    const DriveApiUrlGenerator& url_generator,
77    const GetAboutResourceCallback& callback)
78    : GetDataRequest(sender,
79                     base::Bind(&ParseJsonAndRun<AboutResource>, callback)),
80      url_generator_(url_generator) {
81  DCHECK(!callback.is_null());
82}
83
84GetAboutRequest::~GetAboutRequest() {}
85
86GURL GetAboutRequest::GetURL() const {
87  return url_generator_.GetAboutUrl();
88}
89
90//============================== GetApplistRequest ===========================
91
92GetApplistRequest::GetApplistRequest(
93    RequestSender* sender,
94    const DriveApiUrlGenerator& url_generator,
95    const GetDataCallback& callback)
96    : GetDataRequest(sender, callback),
97      url_generator_(url_generator) {
98  DCHECK(!callback.is_null());
99}
100
101GetApplistRequest::~GetApplistRequest() {}
102
103GURL GetApplistRequest::GetURL() const {
104  return url_generator_.GetApplistUrl();
105}
106
107//============================ GetChangelistRequest ==========================
108
109GetChangelistRequest::GetChangelistRequest(
110    RequestSender* sender,
111    const DriveApiUrlGenerator& url_generator,
112    bool include_deleted,
113    int64 start_changestamp,
114    int max_results,
115    const GetDataCallback& callback)
116    : GetDataRequest(sender, callback),
117      url_generator_(url_generator),
118      include_deleted_(include_deleted),
119      start_changestamp_(start_changestamp),
120      max_results_(max_results) {
121  DCHECK(!callback.is_null());
122}
123
124GetChangelistRequest::~GetChangelistRequest() {}
125
126GURL GetChangelistRequest::GetURL() const {
127  return url_generator_.GetChangelistUrl(
128      include_deleted_, start_changestamp_, max_results_);
129}
130
131//============================= GetFilelistRequest ===========================
132
133GetFilelistRequest::GetFilelistRequest(
134    RequestSender* sender,
135    const DriveApiUrlGenerator& url_generator,
136    const std::string& search_string,
137    int max_results,
138    const GetDataCallback& callback)
139    : GetDataRequest(sender, callback),
140      url_generator_(url_generator),
141      search_string_(search_string),
142      max_results_(max_results) {
143  DCHECK(!callback.is_null());
144}
145
146GetFilelistRequest::~GetFilelistRequest() {}
147
148GURL GetFilelistRequest::GetURL() const {
149  return url_generator_.GetFilelistUrl(search_string_, max_results_);
150}
151
152//=============================== GetFileRequest =============================
153
154GetFileRequest::GetFileRequest(
155    RequestSender* sender,
156    const DriveApiUrlGenerator& url_generator,
157    const std::string& file_id,
158    const FileResourceCallback& callback)
159    : GetDataRequest(sender,
160                     base::Bind(&ParseJsonAndRun<FileResource>, callback)),
161      url_generator_(url_generator),
162      file_id_(file_id) {
163  DCHECK(!callback.is_null());
164}
165
166GetFileRequest::~GetFileRequest() {}
167
168GURL GetFileRequest::GetURL() const {
169  return url_generator_.GetFileUrl(file_id_);
170}
171
172namespace drive {
173
174//======================= ContinueGetFileListRequest =========================
175
176ContinueGetFileListRequest::ContinueGetFileListRequest(
177    RequestSender* sender,
178    const GURL& url,
179    const GetDataCallback& callback)
180    : GetDataRequest(sender, callback),
181      url_(url) {
182  DCHECK(!callback.is_null());
183}
184
185ContinueGetFileListRequest::~ContinueGetFileListRequest() {}
186
187GURL ContinueGetFileListRequest::GetURL() const {
188  return url_;
189}
190
191//========================== CreateDirectoryRequest ==========================
192
193CreateDirectoryRequest::CreateDirectoryRequest(
194    RequestSender* sender,
195    const DriveApiUrlGenerator& url_generator,
196    const std::string& parent_resource_id,
197    const std::string& directory_title,
198    const FileResourceCallback& callback)
199    : GetDataRequest(sender,
200                     base::Bind(&ParseJsonAndRun<FileResource>, callback)),
201      url_generator_(url_generator),
202      parent_resource_id_(parent_resource_id),
203      directory_title_(directory_title) {
204  DCHECK(!callback.is_null());
205  DCHECK(!parent_resource_id_.empty());
206  DCHECK(!directory_title_.empty());
207}
208
209CreateDirectoryRequest::~CreateDirectoryRequest() {}
210
211GURL CreateDirectoryRequest::GetURL() const {
212  return url_generator_.GetFilesUrl();
213}
214
215net::URLFetcher::RequestType CreateDirectoryRequest::GetRequestType() const {
216  return net::URLFetcher::POST;
217}
218
219bool CreateDirectoryRequest::GetContentData(std::string* upload_content_type,
220                                            std::string* upload_content) {
221  *upload_content_type = kContentTypeApplicationJson;
222
223  base::DictionaryValue root;
224  root.SetString("title", directory_title_);
225  {
226    base::DictionaryValue* parent_value = new base::DictionaryValue;
227    parent_value->SetString("id", parent_resource_id_);
228    base::ListValue* parent_list_value = new base::ListValue;
229    parent_list_value->Append(parent_value);
230    root.Set("parents", parent_list_value);
231  }
232  root.SetString("mimeType", kDirectoryMimeType);
233
234  base::JSONWriter::Write(&root, upload_content);
235
236  DVLOG(1) << "CreateDirectory data: " << *upload_content_type << ", ["
237           << *upload_content << "]";
238  return true;
239}
240
241//=========================== RenameResourceRequest ==========================
242
243RenameResourceRequest::RenameResourceRequest(
244    RequestSender* sender,
245    const DriveApiUrlGenerator& url_generator,
246    const std::string& resource_id,
247    const std::string& new_title,
248    const EntryActionCallback& callback)
249    : EntryActionRequest(sender, callback),
250      url_generator_(url_generator),
251      resource_id_(resource_id),
252      new_title_(new_title) {
253  DCHECK(!callback.is_null());
254}
255
256RenameResourceRequest::~RenameResourceRequest() {}
257
258net::URLFetcher::RequestType RenameResourceRequest::GetRequestType() const {
259  return net::URLFetcher::PATCH;
260}
261
262std::vector<std::string>
263RenameResourceRequest::GetExtraRequestHeaders() const {
264  std::vector<std::string> headers;
265  headers.push_back(util::kIfMatchAllHeader);
266  return headers;
267}
268
269GURL RenameResourceRequest::GetURL() const {
270  return url_generator_.GetFileUrl(resource_id_);
271}
272
273bool RenameResourceRequest::GetContentData(std::string* upload_content_type,
274                                           std::string* upload_content) {
275  *upload_content_type = kContentTypeApplicationJson;
276
277  base::DictionaryValue root;
278  root.SetString("title", new_title_);
279  base::JSONWriter::Write(&root, upload_content);
280
281  DVLOG(1) << "RenameResource data: " << *upload_content_type << ", ["
282           << *upload_content << "]";
283  return true;
284}
285
286//=========================== TouchResourceRequest ===========================
287
288TouchResourceRequest::TouchResourceRequest(
289    RequestSender* sender,
290    const DriveApiUrlGenerator& url_generator,
291    const std::string& resource_id,
292    const base::Time& modified_date,
293    const base::Time& last_viewed_by_me_date,
294    const FileResourceCallback& callback)
295    : GetDataRequest(sender,
296                     base::Bind(&ParseJsonAndRun<FileResource>, callback)),
297      url_generator_(url_generator),
298      resource_id_(resource_id),
299      modified_date_(modified_date),
300      last_viewed_by_me_date_(last_viewed_by_me_date) {
301  DCHECK(!modified_date.is_null());
302  DCHECK(!last_viewed_by_me_date.is_null());
303  DCHECK(!callback.is_null());
304}
305
306TouchResourceRequest::~TouchResourceRequest() {}
307
308net::URLFetcher::RequestType TouchResourceRequest::GetRequestType() const {
309  return net::URLFetcher::PATCH;
310}
311
312std::vector<std::string>
313TouchResourceRequest::GetExtraRequestHeaders() const {
314  std::vector<std::string> headers;
315  headers.push_back(util::kIfMatchAllHeader);
316  return headers;
317}
318
319GURL TouchResourceRequest::GetURL() const {
320  return url_generator_.GetFileTouchUrl(resource_id_);
321}
322
323bool TouchResourceRequest::GetContentData(std::string* upload_content_type,
324                                          std::string* upload_content) {
325  *upload_content_type = kContentTypeApplicationJson;
326
327  base::DictionaryValue root;
328  root.SetString("modifiedDate", util::FormatTimeAsString(modified_date_));
329  root.SetString("lastViewedByMeDate",
330                 util::FormatTimeAsString(last_viewed_by_me_date_));
331  base::JSONWriter::Write(&root, upload_content);
332
333  DVLOG(1) << "TouchResource data: " << *upload_content_type << ", ["
334           << *upload_content << "]";
335  return true;
336}
337
338//=========================== CopyResourceRequest ============================
339
340CopyResourceRequest::CopyResourceRequest(
341    RequestSender* sender,
342    const DriveApiUrlGenerator& url_generator,
343    const std::string& resource_id,
344    const std::string& parent_resource_id,
345    const std::string& new_title,
346    const FileResourceCallback& callback)
347    : GetDataRequest(sender,
348                     base::Bind(&ParseJsonAndRun<FileResource>, callback)),
349      url_generator_(url_generator),
350      resource_id_(resource_id),
351      parent_resource_id_(parent_resource_id),
352      new_title_(new_title) {
353  DCHECK(!callback.is_null());
354}
355
356CopyResourceRequest::~CopyResourceRequest() {
357}
358
359net::URLFetcher::RequestType CopyResourceRequest::GetRequestType() const {
360  return net::URLFetcher::POST;
361}
362
363GURL CopyResourceRequest::GetURL() const {
364  return url_generator_.GetFileCopyUrl(resource_id_);
365}
366
367bool CopyResourceRequest::GetContentData(std::string* upload_content_type,
368                                         std::string* upload_content) {
369  *upload_content_type = kContentTypeApplicationJson;
370
371  base::DictionaryValue root;
372  root.SetString("title", new_title_);
373
374  if (!parent_resource_id_.empty()) {
375    // Set the parent resource (destination directory) of the new resource.
376    base::ListValue* parents = new base::ListValue;
377    root.Set("parents", parents);
378    base::DictionaryValue* parent_value = new base::DictionaryValue;
379    parents->Append(parent_value);
380    parent_value->SetString("id", parent_resource_id_);
381  }
382
383  base::JSONWriter::Write(&root, upload_content);
384
385  DVLOG(1) << "CopyResource data: " << *upload_content_type << ", ["
386           << *upload_content << "]";
387  return true;
388}
389
390//=========================== TrashResourceRequest ===========================
391
392TrashResourceRequest::TrashResourceRequest(
393    RequestSender* sender,
394    const DriveApiUrlGenerator& url_generator,
395    const std::string& resource_id,
396    const EntryActionCallback& callback)
397    : EntryActionRequest(sender, callback),
398      url_generator_(url_generator),
399      resource_id_(resource_id) {
400  DCHECK(!callback.is_null());
401}
402
403TrashResourceRequest::~TrashResourceRequest() {}
404
405GURL TrashResourceRequest::GetURL() const {
406  return url_generator_.GetFileTrashUrl(resource_id_);
407}
408
409net::URLFetcher::RequestType TrashResourceRequest::GetRequestType() const {
410  return net::URLFetcher::POST;
411}
412
413//========================== InsertResourceRequest ===========================
414
415InsertResourceRequest::InsertResourceRequest(
416    RequestSender* sender,
417    const DriveApiUrlGenerator& url_generator,
418    const std::string& parent_resource_id,
419    const std::string& resource_id,
420    const EntryActionCallback& callback)
421    : EntryActionRequest(sender, callback),
422      url_generator_(url_generator),
423      parent_resource_id_(parent_resource_id),
424      resource_id_(resource_id) {
425  DCHECK(!callback.is_null());
426}
427
428InsertResourceRequest::~InsertResourceRequest() {}
429
430GURL InsertResourceRequest::GetURL() const {
431  return url_generator_.GetChildrenUrl(parent_resource_id_);
432}
433
434net::URLFetcher::RequestType InsertResourceRequest::GetRequestType() const {
435  return net::URLFetcher::POST;
436}
437
438bool InsertResourceRequest::GetContentData(std::string* upload_content_type,
439                                           std::string* upload_content) {
440  *upload_content_type = kContentTypeApplicationJson;
441
442  base::DictionaryValue root;
443  root.SetString("id", resource_id_);
444  base::JSONWriter::Write(&root, upload_content);
445
446  DVLOG(1) << "InsertResource data: " << *upload_content_type << ", ["
447           << *upload_content << "]";
448  return true;
449}
450
451//========================== DeleteResourceRequest ===========================
452
453DeleteResourceRequest::DeleteResourceRequest(
454    RequestSender* sender,
455    const DriveApiUrlGenerator& url_generator,
456    const std::string& parent_resource_id,
457    const std::string& resource_id,
458    const EntryActionCallback& callback)
459    : EntryActionRequest(sender, callback),
460      url_generator_(url_generator),
461      parent_resource_id_(parent_resource_id),
462      resource_id_(resource_id) {
463  DCHECK(!callback.is_null());
464}
465
466DeleteResourceRequest::~DeleteResourceRequest() {}
467
468GURL DeleteResourceRequest::GetURL() const {
469  return url_generator_.GetChildrenUrlForRemoval(
470      parent_resource_id_, resource_id_);
471}
472
473net::URLFetcher::RequestType DeleteResourceRequest::GetRequestType() const {
474  return net::URLFetcher::DELETE_REQUEST;
475}
476
477//======================= InitiateUploadNewFileRequest =======================
478
479InitiateUploadNewFileRequest::InitiateUploadNewFileRequest(
480    RequestSender* sender,
481    const DriveApiUrlGenerator& url_generator,
482    const std::string& content_type,
483    int64 content_length,
484    const std::string& parent_resource_id,
485    const std::string& title,
486    const InitiateUploadCallback& callback)
487    : InitiateUploadRequestBase(sender,
488                                callback,
489                                content_type,
490                                content_length),
491      url_generator_(url_generator),
492      parent_resource_id_(parent_resource_id),
493      title_(title) {
494}
495
496InitiateUploadNewFileRequest::~InitiateUploadNewFileRequest() {}
497
498GURL InitiateUploadNewFileRequest::GetURL() const {
499  return url_generator_.GetInitiateUploadNewFileUrl();
500}
501
502net::URLFetcher::RequestType
503InitiateUploadNewFileRequest::GetRequestType() const {
504  return net::URLFetcher::POST;
505}
506
507bool InitiateUploadNewFileRequest::GetContentData(
508    std::string* upload_content_type,
509    std::string* upload_content) {
510  *upload_content_type = kContentTypeApplicationJson;
511
512  base::DictionaryValue root;
513  root.SetString("title", title_);
514
515  // Fill parent link.
516  {
517    scoped_ptr<base::DictionaryValue> parent(new base::DictionaryValue);
518    parent->SetString("kind", kParentLinkKind);
519    parent->SetString("id", parent_resource_id_);
520
521    scoped_ptr<base::ListValue> parents(new base::ListValue);
522    parents->Append(parent.release());
523
524    root.Set("parents", parents.release());
525  }
526
527  base::JSONWriter::Write(&root, upload_content);
528
529  DVLOG(1) << "InitiateUploadNewFile data: " << *upload_content_type << ", ["
530           << *upload_content << "]";
531  return true;
532}
533
534//===================== InitiateUploadExistingFileRequest ====================
535
536InitiateUploadExistingFileRequest::InitiateUploadExistingFileRequest(
537    RequestSender* sender,
538    const DriveApiUrlGenerator& url_generator,
539    const std::string& content_type,
540    int64 content_length,
541    const std::string& resource_id,
542    const std::string& etag,
543    const InitiateUploadCallback& callback)
544    : InitiateUploadRequestBase(sender,
545                                callback,
546                                content_type,
547                                content_length),
548      url_generator_(url_generator),
549      resource_id_(resource_id),
550      etag_(etag) {
551}
552
553InitiateUploadExistingFileRequest::~InitiateUploadExistingFileRequest() {}
554
555GURL InitiateUploadExistingFileRequest::GetURL() const {
556  return url_generator_.GetInitiateUploadExistingFileUrl(resource_id_);
557}
558
559net::URLFetcher::RequestType
560InitiateUploadExistingFileRequest::GetRequestType() const {
561  return net::URLFetcher::PUT;
562}
563
564std::vector<std::string>
565InitiateUploadExistingFileRequest::GetExtraRequestHeaders() const {
566  std::vector<std::string> headers(
567      InitiateUploadRequestBase::GetExtraRequestHeaders());
568  headers.push_back(util::GenerateIfMatchHeader(etag_));
569  return headers;
570}
571
572//============================ ResumeUploadRequest ===========================
573
574ResumeUploadRequest::ResumeUploadRequest(
575    RequestSender* sender,
576    const GURL& upload_location,
577    int64 start_position,
578    int64 end_position,
579    int64 content_length,
580    const std::string& content_type,
581    const base::FilePath& local_file_path,
582    const UploadRangeCallback& callback,
583    const ProgressCallback& progress_callback)
584    : ResumeUploadRequestBase(sender,
585                              upload_location,
586                              start_position,
587                              end_position,
588                              content_length,
589                              content_type,
590                              local_file_path),
591      callback_(callback),
592      progress_callback_(progress_callback) {
593  DCHECK(!callback_.is_null());
594}
595
596ResumeUploadRequest::~ResumeUploadRequest() {}
597
598void ResumeUploadRequest::OnRangeRequestComplete(
599    const UploadRangeResponse& response,
600    scoped_ptr<base::Value> value) {
601  DCHECK(CalledOnValidThread());
602  ParseFileResourceWithUploadRangeAndRun(callback_, response, value.Pass());
603}
604
605void ResumeUploadRequest::OnURLFetchUploadProgress(
606    const net::URLFetcher* source, int64 current, int64 total) {
607  if (!progress_callback_.is_null())
608    progress_callback_.Run(current, total);
609}
610
611//========================== GetUploadStatusRequest ==========================
612
613GetUploadStatusRequest::GetUploadStatusRequest(
614    RequestSender* sender,
615    const GURL& upload_url,
616    int64 content_length,
617    const UploadRangeCallback& callback)
618    : GetUploadStatusRequestBase(sender,
619                                 upload_url,
620                                 content_length),
621      callback_(callback) {
622  DCHECK(!callback.is_null());
623}
624
625GetUploadStatusRequest::~GetUploadStatusRequest() {}
626
627void GetUploadStatusRequest::OnRangeRequestComplete(
628    const UploadRangeResponse& response,
629    scoped_ptr<base::Value> value) {
630  DCHECK(CalledOnValidThread());
631  ParseFileResourceWithUploadRangeAndRun(callback_, response, value.Pass());
632}
633
634//========================== DownloadFileRequest ==========================
635
636DownloadFileRequest::DownloadFileRequest(
637    RequestSender* sender,
638    const DriveApiUrlGenerator& url_generator,
639    const std::string& resource_id,
640    const base::FilePath& output_file_path,
641    const DownloadActionCallback& download_action_callback,
642    const GetContentCallback& get_content_callback,
643    const ProgressCallback& progress_callback)
644    : DownloadFileRequestBase(
645          sender,
646          download_action_callback,
647          get_content_callback,
648          progress_callback,
649          url_generator.GenerateDownloadFileUrl(resource_id),
650          output_file_path) {
651}
652
653DownloadFileRequest::~DownloadFileRequest() {
654}
655
656}  // namespace drive
657}  // namespace google_apis
658