Commit 33227920 authored by derat@chromium.org's avatar derat@chromium.org

contacts: Add GDataContactsService.

This adds a class for fetching a user's Google contacts via
the Contacts API.

BUG=128805
TEST=added
TBR=ben@chromium.org

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

git-svn-id: svn://svn.chromium.org/chrome/trunk/src@149005 0039d316-1c4b-4281-b951-d872f2087c98
parent ba00db9f
......@@ -19,7 +19,8 @@ message Contact {
// Provider-assigned unique identifier.
optional string provider_id = 1;
// Last time at which this contact was updated within the upstream provider.
// Last time at which this contact was updated within the upstream provider,
// as given by base::Time::ToInternalValue().
optional int64 update_time = 2;
// Has the contact been deleted recently within the upstream provider?
......
This diff is collapsed.
// Copyright (c) 2012 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#ifndef CHROME_BROWSER_CHROMEOS_GDATA_GDATA_CONTACTS_SERVICE_H_
#define CHROME_BROWSER_CHROMEOS_GDATA_GDATA_CONTACTS_SERVICE_H_
#include <set>
#include <vector>
#include "base/basictypes.h"
#include "base/callback.h"
#include "base/compiler_specific.h"
#include "base/memory/scoped_ptr.h"
#include "base/memory/scoped_vector.h"
#include "base/time.h"
#include "chrome/browser/chromeos/gdata/gdata_errorcode.h"
#include "googleurl/src/gurl.h"
class Profile;
namespace base {
class Value;
}
namespace contacts {
class Contact;
}
namespace gdata {
class GDataAuthService;
class GDataOperationRunner;
// Interface for fetching a user's Google contacts via the Contacts API
// (described at https://developers.google.com/google-apps/contacts/v3/).
class GDataContactsServiceInterface {
public:
typedef base::Callback<void(scoped_ptr<ScopedVector<contacts::Contact> >)>
SuccessCallback;
typedef base::Closure FailureCallback;
virtual ~GDataContactsServiceInterface() {}
virtual void Initialize() = 0;
// Downloads all contacts changed at or after |min_update_time| and invokes
// the appropriate callback asynchronously on the UI thread when complete. If
// min_update_time.is_null() is true, all contacts will be returned.
virtual void DownloadContacts(SuccessCallback success_callback,
FailureCallback failure_callback,
const base::Time& min_update_time) = 0;
protected:
GDataContactsServiceInterface() {}
private:
DISALLOW_COPY_AND_ASSIGN(GDataContactsServiceInterface);
};
class GDataContactsService : public GDataContactsServiceInterface {
public:
typedef base::Callback<std::string(const std::string&)>
RewritePhotoUrlCallback;
explicit GDataContactsService(Profile* profile);
virtual ~GDataContactsService();
GDataAuthService* auth_service_for_testing();
void set_max_simultaneous_photo_downloads_for_testing(int max_downloads) {
max_simultaneous_photo_downloads_ = max_downloads;
}
void set_feed_url_for_testing(const GURL& url) {
feed_url_for_testing_ = url;
}
void set_rewrite_photo_url_callback_for_testing(RewritePhotoUrlCallback cb) {
rewrite_photo_url_callback_for_testing_ = cb;
}
// Overridden from GDataContactsServiceInterface:
virtual void Initialize() OVERRIDE;
virtual void DownloadContacts(SuccessCallback success_callback,
FailureCallback failure_callback,
const base::Time& min_update_time) OVERRIDE;
private:
class DownloadContactsRequest;
// Invoked by a download request once it's finished (either successfully or
// unsuccessfully).
void OnRequestComplete(DownloadContactsRequest* request);
Profile* profile_; // not owned
scoped_ptr<GDataOperationRunner> runner_;
// In-progress download requests. Pointers are owned by this class.
std::set<DownloadContactsRequest*> requests_;
// If non-empty, URL that will be used to fetch the feed. URLs contained
// within the feed will also be modified to use the host and port from this
// member.
GURL feed_url_for_testing_;
// Maximum number of photos we'll try to download at once (per
// DownloadContacts() request).
int max_simultaneous_photo_downloads_;
// Callback that's invoked to rewrite photo URLs for tests.
// This is needed for tests that serve static feed data from a host/port
// that's only known at runtime.
RewritePhotoUrlCallback rewrite_photo_url_callback_for_testing_;
DISALLOW_COPY_AND_ASSIGN(GDataContactsService);
};
} // namespace gdata
#endif // CHROME_BROWSER_CHROMEOS_GDATA_GDATA_CONTACTS_SERVICE_H_
......@@ -7,6 +7,7 @@
#include "base/string_number_conversions.h"
#include "base/stringprintf.h"
#include "base/values.h"
#include "chrome/browser/chromeos/gdata/gdata_util.h"
#include "chrome/browser/chromeos/gdata/gdata_wapi_parser.h"
#include "chrome/common/net/url_util.h"
#include "content/public/browser/browser_thread.h"
......@@ -48,6 +49,18 @@ const char kGetDocumentEntryURLFormat[] =
const char kAccountMetadataURL[] =
"https://docs.google.com/feeds/metadata/default";
// URL requesting all contacts.
// TODO(derat): Per https://goo.gl/AufHP, "The feed may not contain all of the
// user's contacts, because there's a default limit on the number of results
// returned." Decide if 10000 is reasonable or not.
const char kGetContactsURL[] =
"https://www.google.com/m8/feeds/contacts/default/full"
"?alt=json&showdeleted=true&max-results=10000";
// Query parameter optionally appended to |kGetContactsURL| to return only
// recently-updated contacts.
const char kGetContactsUpdatedMinParam[] = "updated-min";
const char kUploadContentRange[] = "Content-Range: bytes ";
const char kUploadContentType[] = "X-Upload-Content-Type: ";
const char kUploadContentLength[] = "X-Upload-Content-Length: ";
......@@ -875,4 +888,62 @@ void ResumeUploadOperation::OnURLFetchUploadProgress(
NotifyProgress(params_.start_range + current, params_.content_length);
}
//============================ GetContactsOperation ============================
GetContactsOperation::GetContactsOperation(GDataOperationRegistry* registry,
Profile* profile,
const base::Time& min_update_time,
const GetDataCallback& callback)
: GetDataOperation(registry, profile, callback),
min_update_time_(min_update_time) {
}
GetContactsOperation::~GetContactsOperation() {}
GURL GetContactsOperation::GetURL() const {
if (!feed_url_for_testing_.is_empty())
return GURL(feed_url_for_testing_);
GURL url(kGetContactsURL);
if (!min_update_time_.is_null()) {
std::string time_rfc3339 = util::FormatTimeAsString(min_update_time_);
url = chrome_common_net::AppendQueryParameter(
url, kGetContactsUpdatedMinParam, time_rfc3339);
}
return url;
}
//========================== GetContactPhotoOperation ==========================
GetContactPhotoOperation::GetContactPhotoOperation(
GDataOperationRegistry* registry,
Profile* profile,
const GURL& photo_url,
const GetDownloadDataCallback& callback)
: UrlFetchOperationBase(registry, profile),
photo_url_(photo_url),
callback_(callback) {
}
GetContactPhotoOperation::~GetContactPhotoOperation() {}
GURL GetContactPhotoOperation::GetURL() const {
return photo_url_;
}
bool GetContactPhotoOperation::ProcessURLFetchResults(
const net::URLFetcher* source) {
GDataErrorCode code = static_cast<GDataErrorCode>(source->GetResponseCode());
scoped_ptr<std::string> data(new std::string);
source->GetResponseAsString(data.get());
callback_.Run(code, data.Pass());
return code == HTTP_SUCCESS;
}
void GetContactPhotoOperation::RunCallbackOnPrematureFailure(
GDataErrorCode code) {
scoped_ptr<std::string> data(new std::string);
callback_.Run(code, data.Pass());
}
} // namespace gdata
......@@ -383,6 +383,63 @@ class ResumeUploadOperation : public UrlFetchOperationBase {
DISALLOW_COPY_AND_ASSIGN(ResumeUploadOperation);
};
//============================ GetContactsOperation ============================
// This class fetches a user's contacts.
class GetContactsOperation : public GetDataOperation {
public:
GetContactsOperation(GDataOperationRegistry* registry,
Profile* profile,
const base::Time& min_update_time,
const GetDataCallback& callback);
virtual ~GetContactsOperation();
void set_feed_url_for_testing(const GURL& url) {
feed_url_for_testing_ = url;
}
protected:
// Overridden from GetDataOperation.
virtual GURL GetURL() const OVERRIDE;
private:
// If non-empty, URL of the feed to fetch.
GURL feed_url_for_testing_;
// If is_null() is false, contains a minimum last-updated time that will be
// used to filter contacts.
base::Time min_update_time_;
DISALLOW_COPY_AND_ASSIGN(GetContactsOperation);
};
//========================== GetContactPhotoOperation ==========================
// This class fetches a contact's photo.
class GetContactPhotoOperation : public UrlFetchOperationBase {
public:
GetContactPhotoOperation(GDataOperationRegistry* registry,
Profile* profile,
const GURL& photo_url,
const GetDownloadDataCallback& callback);
virtual ~GetContactPhotoOperation();
protected:
// Overridden from UrlFetchOperationBase.
virtual GURL GetURL() const OVERRIDE;
virtual bool ProcessURLFetchResults(const net::URLFetcher* source) OVERRIDE;
virtual void RunCallbackOnPrematureFailure(GDataErrorCode code) OVERRIDE;
private:
// Location of the photo to fetch.
GURL photo_url_;
// Callback to which the photo data is passed.
GetDownloadDataCallback callback_;
DISALLOW_COPY_AND_ASSIGN(GetContactPhotoOperation);
};
} // namespace gdata
#endif // CHROME_BROWSER_CHROMEOS_GDATA_GDATA_OPERATIONS_H_
......@@ -8,6 +8,7 @@
#include "base/bind_helpers.h"
#include "chrome/browser/browser_process.h"
#include "chrome/browser/chromeos/gdata/drive_webapps_registry.h"
#include "chrome/browser/chromeos/gdata/gdata_contacts_service.h"
#include "chrome/browser/chromeos/gdata/gdata_documents_service.h"
#include "chrome/browser/chromeos/gdata/gdata_download_observer.h"
#include "chrome/browser/chromeos/gdata/gdata_file_system.h"
......@@ -70,6 +71,7 @@ void GDataSystemService::Initialize(
download_observer_.reset(new GDataDownloadObserver(uploader(),
file_system()));
sync_client_.reset(new GDataSyncClient(profile_, file_system(), cache()));
contacts_service_.reset(new GDataContactsService(profile_));
sync_client_->Initialize();
file_system_->Initialize();
......@@ -84,6 +86,7 @@ void GDataSystemService::Initialize(
GDataCache::CACHE_TYPE_TMP_DOWNLOADS));
AddDriveMountPoint();
contacts_service_->Initialize();
}
void GDataSystemService::Shutdown() {
......@@ -91,6 +94,7 @@ void GDataSystemService::Shutdown() {
RemoveDriveMountPoint();
// Shut down the member objects in the reverse order of creation.
contacts_service_.reset();
sync_client_.reset();
download_observer_.reset();
file_system_.reset();
......
......@@ -20,6 +20,7 @@ namespace gdata {
class DocumentsServiceInterface;
class DriveWebAppsRegistry;
class GDataCache;
class GDataContactsService;
class GDataDownloadObserver;
class GDataFileSystemInterface;
class GDataSyncClient;
......@@ -33,19 +34,11 @@ class GDataUploader;
// created per-profile.
class GDataSystemService : public ProfileKeyedService {
public:
// Returns the documents service instance.
DocumentsServiceInterface* docs_service() { return documents_service_.get(); }
// Returns the cache instance.
GDataCache* cache() { return cache_; }
// Returns the file system instance.
GDataFileSystemInterface* file_system() { return file_system_.get(); }
// Returns the uploader instance.
GDataUploader* uploader() { return uploader_.get(); }
// Returns the file system instance.
GDataContactsService* contacts_service() { return contacts_service_.get(); }
DriveWebAppsRegistry* webapps_registry() { return webapps_registry_.get(); }
// ProfileKeyedService override:
......@@ -76,6 +69,7 @@ class GDataSystemService : public ProfileKeyedService {
scoped_ptr<GDataFileSystemInterface> file_system_;
scoped_ptr<GDataDownloadObserver> download_observer_;
scoped_ptr<GDataSyncClient> sync_client_;
scoped_ptr<GDataContactsService> contacts_service_;
DISALLOW_COPY_AND_ASSIGN(GDataSystemService);
};
......
......@@ -46,6 +46,9 @@ const char kDocsListScope[] = "https://docs.google.com/feeds/";
const char kSpreadsheetsScope[] = "https://spreadsheets.google.com/feeds/";
const char kUserContentScope[] = "https://docs.googleusercontent.com/";
// OAuth scope for the Contacts API.
const char kContactsScope[] = "https://www.google.com/m8/feeds/";
// OAuth scope for Drive API.
const char kDriveAppsScope[] = "https://www.googleapis.com/auth/drive.apps";
......@@ -71,6 +74,7 @@ void AuthOperation::Start() {
scopes.push_back(kDocsListScope);
scopes.push_back(kSpreadsheetsScope);
scopes.push_back(kUserContentScope);
scopes.push_back(kContactsScope);
if (gdata::util::IsDriveV2ApiEnabled())
scopes.push_back(kDriveAppsScope);
oauth2_access_token_fetcher_.reset(new OAuth2AccessTokenFetcher(
......
......@@ -549,6 +549,8 @@
'browser/chromeos/gdata/gdata_cache.h',
'browser/chromeos/gdata/gdata_cache_metadata.cc',
'browser/chromeos/gdata/gdata_cache_metadata.h',
'browser/chromeos/gdata/gdata_contacts_service.cc',
'browser/chromeos/gdata/gdata_contacts_service.h',
'browser/chromeos/gdata/gdata_db.h',
'browser/chromeos/gdata/gdata_db_factory.cc',
'browser/chromeos/gdata/gdata_db_factory.h',
......
......@@ -2633,6 +2633,7 @@
'browser/chromeos/bluetooth/test/mock_bluetooth_adapter.h',
'browser/chromeos/bluetooth/test/mock_bluetooth_device.cc',
'browser/chromeos/bluetooth/test/mock_bluetooth_device.h',
'browser/chromeos/contacts/contact_test_util.cc',
'browser/chromeos/cros/cros_in_process_browser_test.cc',
'browser/chromeos/cros/cros_in_process_browser_test.h',
'browser/chromeos/cros/cros_mock.cc',
......@@ -2644,6 +2645,7 @@
'browser/chromeos/extensions/file_browser_private_apitest.cc',
'browser/chromeos/extensions/echo_private_apitest.cc',
'browser/chromeos/extensions/external_filesystem_apitest.cc',
'browser/chromeos/gdata/gdata_contacts_service_browsertest.cc',
'browser/chromeos/gdata/gdata_documents_service_browsertest.cc',
'browser/chromeos/gdata/mock_gdata_documents_service.cc',
'browser/chromeos/gdata/mock_gdata_documents_service.h',
......
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