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