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