people_provider.cc revision f8ee788a64d60abd8f2d742a5fdedde054ecd910
1// Copyright 2013 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/ui/app_list/search/people/people_provider.h" 6 7#include <string> 8 9#include "base/bind.h" 10#include "base/callback.h" 11#include "base/strings/string_number_conversions.h" 12#include "base/strings/utf_string_conversions.h" 13#include "base/values.h" 14#include "chrome/browser/browser_process.h" 15#include "chrome/browser/profiles/profile.h" 16#include "chrome/browser/search/search.h" 17#include "chrome/browser/signin/profile_oauth2_token_service_factory.h" 18#include "chrome/browser/signin/signin_manager_factory.h" 19#include "chrome/browser/ui/app_list/search/common/json_response_fetcher.h" 20#include "chrome/browser/ui/app_list/search/people/people_result.h" 21#include "chrome/browser/ui/app_list/search/people/person.h" 22#include "components/signin/core/browser/profile_oauth2_token_service.h" 23#include "components/signin/core/browser/signin_manager.h" 24#include "google_apis/gaia/gaia_constants.h" 25#include "net/base/url_util.h" 26#include "url/gurl.h" 27 28namespace app_list { 29 30namespace { 31 32const char kKeyItems[] = "items"; 33 34const char kAccessTokenField[] = "access_token"; 35const char kQueryField[] = "query"; 36const char kPeopleSearchUrl[] = 37 "https://www.googleapis.com/plus/v2whitelisted/people/autocomplete"; 38 39// OAuth2 scope for access to the Google+ People Search API. 40const char kPeopleSearchOAuth2Scope[] = 41 "https://www.googleapis.com/auth/plus.peopleapi.readwrite"; 42 43} // namespace 44 45PeopleProvider::PeopleProvider(Profile* profile) 46 : WebserviceSearchProvider(profile), 47 OAuth2TokenService::Consumer("people_provider"), 48 people_search_url_(kPeopleSearchUrl), 49 skip_request_token_for_test_(false) { 50 oauth2_scope_.insert(kPeopleSearchOAuth2Scope); 51} 52 53PeopleProvider::~PeopleProvider() {} 54 55void PeopleProvider::Start(const base::string16& query) { 56 ClearResults(); 57 if (!IsValidQuery(query)) { 58 query_.clear(); 59 return; 60 } 61 62 query_ = base::UTF16ToUTF8(query); 63 64 const CacheResult result = cache_->Get(WebserviceCache::PEOPLE, query_); 65 if (result.second) { 66 ProcessPeopleSearchResults(result.second); 67 if (!people_search_fetched_callback_.is_null()) 68 people_search_fetched_callback_.Run(); 69 if (result.first == FRESH) 70 return; 71 } 72 73 74 if (!people_search_) { 75 people_search_.reset(new JSONResponseFetcher( 76 base::Bind(&PeopleProvider::OnPeopleSearchFetched, 77 base::Unretained(this)), 78 profile_->GetRequestContext())); 79 } 80 81 if (!skip_request_token_for_test_) { 82 // We start with reqesting the access token. Once the token is fetched, 83 // we'll create the full query URL and fetch it. 84 StartThrottledQuery(base::Bind(&PeopleProvider::RequestAccessToken, 85 base::Unretained(this))); 86 } else { 87 // Running in a test, skip requesting the access token, straight away 88 // start our query. 89 StartThrottledQuery(base::Bind(&PeopleProvider::StartQuery, 90 base::Unretained(this))); 91 } 92} 93 94void PeopleProvider::Stop() { 95 if (people_search_) 96 people_search_->Stop(); 97} 98 99void PeopleProvider::OnGetTokenSuccess( 100 const OAuth2TokenService::Request* request, 101 const std::string& access_token, 102 const base::Time& expiration_time) { 103 DCHECK_EQ(access_token_request_, request); 104 access_token_request_.reset(); 105 access_token_ = access_token; 106 StartQuery(); 107} 108 109void PeopleProvider::OnGetTokenFailure( 110 const OAuth2TokenService::Request* request, 111 const GoogleServiceAuthError& error) { 112 DCHECK_EQ(access_token_request_, request); 113 access_token_request_.reset(); 114} 115 116void PeopleProvider::RequestAccessToken() { 117 // Only one active request at a time. 118 if (access_token_request_ != NULL) 119 return; 120 121 ProfileOAuth2TokenService* token_service = 122 ProfileOAuth2TokenServiceFactory::GetForProfile(profile_); 123 SigninManagerBase* signin_manager = 124 SigninManagerFactory::GetInstance()->GetForProfile(profile_); 125 access_token_request_ = token_service->StartRequest( 126 signin_manager->GetAuthenticatedAccountId(), oauth2_scope_, this); 127} 128 129GURL PeopleProvider::GetQueryUrl(const std::string& query) { 130 GURL people_search_url = people_search_url_; 131 people_search_url = net::AppendQueryParameter(people_search_url, 132 kAccessTokenField, 133 access_token_); 134 people_search_url = net::AppendQueryParameter(people_search_url, 135 kQueryField, 136 query); 137 138 return people_search_url; 139} 140 141void PeopleProvider::StartQuery() { 142 // |query_| can be NULL when the query is scheduled but then canceled. 143 if (!people_search_ || query_.empty()) 144 return; 145 146 GURL url = GetQueryUrl(query_); 147 people_search_->Start(url); 148} 149 150void PeopleProvider::OnPeopleSearchFetched( 151 scoped_ptr<base::DictionaryValue> json) { 152 ProcessPeopleSearchResults(json.get()); 153 cache_->Put(WebserviceCache::PEOPLE, query_, json.Pass()); 154 155 if (!people_search_fetched_callback_.is_null()) 156 people_search_fetched_callback_.Run(); 157} 158 159void PeopleProvider::ProcessPeopleSearchResults( 160 const base::DictionaryValue* json) { 161 const base::ListValue* item_list = NULL; 162 if (!json || 163 !json->GetList(kKeyItems, &item_list) || 164 !item_list || 165 item_list->empty()) { 166 return; 167 } 168 169 ClearResults(); 170 for (base::ListValue::const_iterator it = item_list->begin(); 171 it != item_list->end(); 172 ++it) { 173 const base::DictionaryValue* dict; 174 if (!(*it)->GetAsDictionary(&dict)) 175 continue; 176 177 scoped_ptr<SearchResult> result(CreateResult(*dict)); 178 if (!result) 179 continue; 180 181 Add(result.Pass()); 182 } 183} 184 185scoped_ptr<ChromeSearchResult> PeopleProvider::CreateResult( 186 const base::DictionaryValue& dict) { 187 scoped_ptr<ChromeSearchResult> result; 188 189 scoped_ptr<Person> person = Person::Create(dict); 190 if (!person) 191 return result.Pass(); 192 193 result.reset(new PeopleResult(profile_, person.Pass())); 194 return result.Pass(); 195} 196 197void PeopleProvider::SetupForTest( 198 const base::Closure& people_search_fetched_callback, 199 const GURL& people_search_url) { 200 people_search_fetched_callback_ = people_search_fetched_callback; 201 people_search_url_ = people_search_url; 202 skip_request_token_for_test_ = true; 203} 204 205} // namespace app_list 206