Commit 113e3657 authored by Tatiana Gornak's avatar Tatiana Gornak Committed by Commit Bot

Implements the topic registration request to Per-User-Topic FCM server

This CL implements the wrapper for the POST request to the FCM server.

BUG=801985

Cq-Include-Trybots: master.tryserver.chromium.mac:ios-simulator-cronet;master.tryserver.chromium.mac:ios-simulator-full-configs
Change-Id: Ibba578f7181920e24b39c633111d47b717eb7bb1
Reviewed-on: https://chromium-review.googlesource.com/911108
Commit-Queue: Tatiana Gornak <melandory@chromium.org>
Reviewed-by: default avatarMatt Menke <mmenke@chromium.org>
Reviewed-by: default avatarChristian Dullweber <dullweber@chromium.org>
Reviewed-by: default avatarPavel Yatsuk <pavely@chromium.org>
Reviewed-by: default avatarJan Krcal <jkrcal@chromium.org>
Cr-Commit-Position: refs/heads/master@{#550625}
parent ef3709c4
...@@ -27,8 +27,12 @@ static_library("impl") { ...@@ -27,8 +27,12 @@ static_library("impl") {
"invalidator_storage.h", "invalidator_storage.h",
"mock_ack_handler.cc", "mock_ack_handler.cc",
"mock_ack_handler.h", "mock_ack_handler.h",
"per_user_topic_registration_request.cc",
"per_user_topic_registration_request.h",
"profile_invalidation_provider.cc", "profile_invalidation_provider.cc",
"profile_invalidation_provider.h", "profile_invalidation_provider.h",
"status.cc",
"status.h",
"unacked_invalidation_set.cc", "unacked_invalidation_set.cc",
"unacked_invalidation_set.h", "unacked_invalidation_set.h",
] ]
...@@ -48,6 +52,8 @@ static_library("impl") { ...@@ -48,6 +52,8 @@ static_library("impl") {
"//google_apis", "//google_apis",
"//jingle:notifier", "//jingle:notifier",
"//net:net", "//net:net",
"//services/network/public/cpp",
"//services/network/public/mojom",
# TODO(sync): Remove this (http://crbug.com/133352); # TODO(sync): Remove this (http://crbug.com/133352);
"//third_party/protobuf:protobuf_lite", "//third_party/protobuf:protobuf_lite",
...@@ -96,16 +102,34 @@ static_library("impl") { ...@@ -96,16 +102,34 @@ static_library("impl") {
} }
} }
source_set("json_unsafe_parser") {
testonly = !is_ios
sources = [
"json_unsafe_parser.cc",
"json_unsafe_parser.h",
]
public_deps = [
"//base",
]
}
source_set("unit_tests") { source_set("unit_tests") {
testonly = true testonly = true
sources = [ sources = [
"invalidation_logger_unittest.cc", "invalidation_logger_unittest.cc",
"per_user_topic_registration_request_unittest.cc",
] ]
deps = [ deps = [
":impl", ":impl",
":json_unsafe_parser",
":test_support", ":test_support",
"//base", "//base",
"//base/test:test_support",
"//components/prefs", "//components/prefs",
"//net:test_support",
"//services/network:test_support",
"//testing/gmock", "//testing/gmock",
"//testing/gtest", "//testing/gtest",
] ]
......
...@@ -16,7 +16,11 @@ include_rules = [ ...@@ -16,7 +16,11 @@ include_rules = [
"+net/base/backoff_entry.h", "+net/base/backoff_entry.h",
"+net/base/ip_endpoint.h", "+net/base/ip_endpoint.h",
"+net/base/network_change_notifier.h", "+net/base/network_change_notifier.h",
"+net/http/http_request_headers.h",
"+net/http/http_status_code.h", "+net/http/http_status_code.h",
"+net/traffic_annotation", "+net/traffic_annotation",
"+net/url_request", "+net/url_request",
"+services/network/public",
"+services/network/test",
] ]
// Copyright 2016 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.
#include "components/invalidation/impl/json_unsafe_parser.h"
#include "base/bind.h"
#include "base/callback.h"
#include "base/json/json_parser.h"
#include "base/strings/stringprintf.h"
#include "base/threading/thread_task_runner_handle.h"
#include "base/values.h"
namespace syncer {
void JsonUnsafeParser::Parse(const std::string& unsafe_json,
SuccessCallback success_callback,
ErrorCallback error_callback) {
base::ThreadTaskRunnerHandle::Get()->PostTask(
FROM_HERE,
base::BindOnce(
[](const std::string& unsafe_json, SuccessCallback success_callback,
ErrorCallback error_callback) {
std::string error_msg;
int error_line, error_column;
std::unique_ptr<base::Value> value =
base::JSONReader::ReadAndReturnError(
unsafe_json, base::JSON_ALLOW_TRAILING_COMMAS, nullptr,
&error_msg, &error_line, &error_column);
if (value) {
std::move(success_callback).Run(std::move(value));
} else {
std::move(error_callback)
.Run(base::StringPrintf("%s (%d:%d)", error_msg.c_str(),
error_line, error_column));
}
},
unsafe_json, std::move(success_callback), std::move(error_callback)));
}
} // namespace syncer
// Copyright 2018 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 COMPONENTS_INVALIDATION_IMPL_JSON_UNSAFE_PARSER_H_
#define COMPONENTS_INVALIDATION_IMPL_JSON_UNSAFE_PARSER_H_
#include <memory>
#include <string>
#include "base/callback_forward.h"
namespace base {
class Value;
}
namespace syncer {
// Mimics SafeJsonParser, but parses unsafely.
//
// Do not use this class, unless you can't help it. On most platforms,
// SafeJsonParser is available and is safer. If it is not available (e.g. on
// iOS), then this class mimics its API without its safety.
//
// TODO(https://crbug.com/828833): This code is the duplicate of same code in
// the ntp component. It should be removed, once appropriate place is found.
class JsonUnsafeParser {
public:
using SuccessCallback =
base::OnceCallback<void(std::unique_ptr<base::Value>)>;
using ErrorCallback = base::OnceCallback<void(const std::string&)>;
// As with SafeJsonParser, runs either success_callback or error_callback on
// the calling thread, but not before the call returns.
static void Parse(const std::string& unsafe_json,
SuccessCallback success_callback,
ErrorCallback error_callback);
JsonUnsafeParser() = delete;
};
} // namespace syncer
#endif // COMPONENTS_INVALIDATION_IMPL_JSON_UNSAFE_PARSER_H_
// Copyright 2018 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.
#include "components/invalidation/impl/per_user_topic_registration_request.h"
#include "base/json/json_reader.h"
#include "base/json/json_writer.h"
#include "base/memory/ptr_util.h"
#include "base/strings/stringprintf.h"
#include "base/values.h"
#include "net/base/load_flags.h"
#include "net/http/http_status_code.h"
#include "net/url_request/url_fetcher.h"
using net::HttpRequestHeaders;
using net::URLRequestStatus;
namespace {
const char kPrivateTopicNameKey[] = "private_topic_name";
base::Value* GetPrivateTopicName(base::Value* value) {
if (!value || !value->is_dict()) {
return nullptr;
}
return value->FindKeyOfType(kPrivateTopicNameKey, base::Value::Type::STRING);
}
}; // namespace
namespace syncer {
PerUserTopicRegistrationRequest::PerUserTopicRegistrationRequest()
: weak_ptr_factory_(this) {}
PerUserTopicRegistrationRequest::~PerUserTopicRegistrationRequest() = default;
void PerUserTopicRegistrationRequest::Start(
CompletedCallback callback,
ParseJSONCallback parse_json,
network::mojom::URLLoaderFactory* loader_factory) {
DCHECK(request_completed_callback_.is_null()) << "Request already running!";
request_completed_callback_ = std::move(callback);
parse_json_ = std::move(parse_json);
simple_loader_->DownloadToStringOfUnboundedSizeUntilCrashAndDie(
loader_factory,
base::BindOnce(&PerUserTopicRegistrationRequest::OnURLFetchComplete,
weak_ptr_factory_.GetWeakPtr()));
}
void PerUserTopicRegistrationRequest::OnURLFetchComplete(
std::unique_ptr<std::string> response_body) {
int response_code = 0;
if (simple_loader_->ResponseInfo() &&
simple_loader_->ResponseInfo()->headers) {
response_code = simple_loader_->ResponseInfo()->headers->response_code();
}
OnURLFetchCompleteInternal(simple_loader_->NetError(), response_code,
std::move(response_body));
}
void PerUserTopicRegistrationRequest::OnURLFetchCompleteInternal(
int net_error,
int response_code,
std::unique_ptr<std::string> response_body) {
if (net_error != net::OK) {
std::move(request_completed_callback_)
.Run(Status(StatusCode::FAILED, base::StringPrintf("Network Error")),
std::string());
return;
}
if (response_code != net::HTTP_OK) {
std::move(request_completed_callback_)
.Run(Status(StatusCode::FAILED,
base::StringPrintf("HTTP Error: %d", response_code)),
std::string());
return;
}
if (!response_body.get() || response_body->empty()) {
std::move(request_completed_callback_)
.Run(Status(StatusCode::FAILED, base::StringPrintf("Body parse error")),
std::string());
return;
}
std::move(parse_json_)
.Run(*response_body,
base::BindOnce(&PerUserTopicRegistrationRequest::OnJsonParseSuccess,
weak_ptr_factory_.GetWeakPtr()),
base::BindOnce(&PerUserTopicRegistrationRequest::OnJsonParseFailure,
weak_ptr_factory_.GetWeakPtr()));
}
void PerUserTopicRegistrationRequest::OnJsonParseFailure(
const std::string& error) {
std::move(request_completed_callback_)
.Run(Status(StatusCode::FAILED, base::StringPrintf("Body parse error")),
std::string());
}
void PerUserTopicRegistrationRequest::OnJsonParseSuccess(
std::unique_ptr<base::Value> value) {
const base::Value* private_topic_name_value =
GetPrivateTopicName(value.get());
if (private_topic_name_value) {
std::move(request_completed_callback_)
.Run(Status(StatusCode::SUCCESS, std::string()),
private_topic_name_value->GetString());
} else {
std::move(request_completed_callback_)
.Run(Status(StatusCode::FAILED, base::StringPrintf("Body parse error")),
std::string());
}
}
PerUserTopicRegistrationRequest::Builder::Builder() = default;
PerUserTopicRegistrationRequest::Builder::Builder(
PerUserTopicRegistrationRequest::Builder&&) = default;
PerUserTopicRegistrationRequest::Builder::~Builder() = default;
std::unique_ptr<PerUserTopicRegistrationRequest>
PerUserTopicRegistrationRequest::Builder::Build() const {
DCHECK(!scope_.empty());
auto request = base::WrapUnique(new PerUserTopicRegistrationRequest);
GURL full_url(base::StringPrintf(
"%s/v1/perusertopics/%s/rel/topics/?subscriber_token=%s", scope_.c_str(),
project_id_.c_str(), token_.c_str()));
DCHECK(full_url.is_valid());
request->url_ = full_url;
std::string body = BuildBody();
net::HttpRequestHeaders headers = BuildHeaders();
request->simple_loader_ = BuildURLFetcher(headers, body, full_url);
// Log the request for debugging network issues.
DVLOG(1) << "Building a subscription request to " << full_url << ":\n"
<< headers.ToString() << "\n"
<< body;
return request;
}
PerUserTopicRegistrationRequest::Builder&
PerUserTopicRegistrationRequest::Builder::SetToken(const std::string& token) {
token_ = token;
return *this;
}
PerUserTopicRegistrationRequest::Builder&
PerUserTopicRegistrationRequest::Builder::SetScope(const std::string& scope) {
scope_ = scope;
return *this;
}
PerUserTopicRegistrationRequest::Builder&
PerUserTopicRegistrationRequest::Builder::SetAuthenticationHeader(
const std::string& auth_header) {
auth_header_ = auth_header;
return *this;
}
PerUserTopicRegistrationRequest::Builder&
PerUserTopicRegistrationRequest::Builder::SetPublicTopicName(
const std::string& topic) {
topic_ = topic;
return *this;
}
PerUserTopicRegistrationRequest::Builder&
PerUserTopicRegistrationRequest::Builder::SetProjectId(
const std::string& project_id) {
project_id_ = project_id;
return *this;
}
HttpRequestHeaders PerUserTopicRegistrationRequest::Builder::BuildHeaders()
const {
HttpRequestHeaders headers;
if (!auth_header_.empty()) {
headers.SetHeader(HttpRequestHeaders::kAuthorization, auth_header_);
}
return headers;
}
std::string PerUserTopicRegistrationRequest::Builder::BuildBody() const {
base::DictionaryValue request;
request.SetString("public_topic_name", topic_);
std::string request_json;
bool success = base::JSONWriter::Write(request, &request_json);
DCHECK(success);
return request_json;
}
std::unique_ptr<network::SimpleURLLoader>
PerUserTopicRegistrationRequest::Builder::BuildURLFetcher(
const HttpRequestHeaders& headers,
const std::string& body,
const GURL& url) const {
net::NetworkTrafficAnnotationTag traffic_annotation =
net::DefineNetworkTrafficAnnotation("per_user_topic_registration_request",
R"(
semantics {
sender: "Register the Sync client for listening of the specific topic"
description:
"Chromium can receive Sync invalidations via FCM messages."
"This request registers the client for receiving messages for the"
"concrete topic. In case of Chrome Sync topic is a ModelType,"
"e.g. BOOKMARK"
trigger:
"Subscription takes place only once per profile per topic. "
data:
"An OAuth2 token is sent as an authorization header."
destination: GOOGLE_OWNED_SERVICE
}
policy {
cookies_allowed: NO
setting:
"This feature can not be disabled by settings now"
chrome_policy: {
SyncDisabled {
policy_options {mode: MANDATORY}
SyncDisabled: false
}
}
})");
auto request = std::make_unique<network::ResourceRequest>();
request->method = "POST";
request->url = url;
request->headers = headers;
std::unique_ptr<network::SimpleURLLoader> url_loader =
network::SimpleURLLoader::Create(std::move(request), traffic_annotation);
url_loader->AttachStringForUpload(body, "application/json; charset=UTF-8");
return url_loader;
}
} // namespace syncer
// Copyright 2018 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 COMPONENTS_INVALIDATION_IMPL_PER_USER_TOPIC_REGISTRATION_REQUEST_H_
#define COMPONENTS_INVALIDATION_IMPL_PER_USER_TOPIC_REGISTRATION_REQUEST_H_
#include <memory>
#include <string>
#include <utility>
#include "base/callback.h"
#include "base/memory/weak_ptr.h"
#include "base/optional.h"
#include "base/time/time.h"
#include "components/invalidation/impl/status.h"
#include "google_apis/gaia/oauth2_token_service.h"
#include "net/http/http_request_headers.h"
#include "services/network/public/cpp/simple_url_loader.h"
#include "services/network/public/mojom/url_loader_factory.mojom.h"
namespace syncer {
using ParseJSONCallback = base::OnceCallback<void(
const std::string& unsafe_json,
base::OnceCallback<void(std::unique_ptr<base::Value>)> success_callback,
base::OnceCallback<void(const std::string&)> error_callback)>;
// A single request to register against per user topic service.
class PerUserTopicRegistrationRequest {
public:
// The request result consists of the request status and name of the private
// topic. The |private_topic_name| will be empty in the case of error.
using CompletedCallback =
base::OnceCallback<void(const Status& status,
const std::string& private_topic_name)>;
// Builds authenticated PerUserTopicRegistrationRequests.
class Builder {
public:
Builder();
Builder(Builder&&);
~Builder();
// Builds a Request object in order to perform the registration.
std::unique_ptr<PerUserTopicRegistrationRequest> Build() const;
Builder& SetToken(const std::string& token);
Builder& SetScope(const std::string& scope);
Builder& SetAuthenticationHeader(const std::string& auth_header);
Builder& SetPublicTopicName(const std::string& topic);
Builder& SetProjectId(const std::string& project_id);
private:
net::HttpRequestHeaders BuildHeaders() const;
std::string BuildBody() const;
std::unique_ptr<network::SimpleURLLoader> BuildURLFetcher(
const net::HttpRequestHeaders& headers,
const std::string& body,
const GURL& url) const;
// GCM subscription token obtained from GCM driver (instanceID::getToken()).
std::string token_;
std::string topic_;
std::string project_id_;
std::string scope_;
std::string auth_header_;
DISALLOW_COPY_AND_ASSIGN(Builder);
};
~PerUserTopicRegistrationRequest();
// Starts an async request. The callback is invoked when the request succeeds
// or fails. The callback is not called if the request is destroyed.
void Start(CompletedCallback callback,
ParseJSONCallback parsed_json,
network::mojom::URLLoaderFactory* loader_factory);
private:
friend class PerUserTopicRegistrationRequestTest;
PerUserTopicRegistrationRequest();
void OnURLFetchComplete(std::unique_ptr<std::string> response_body);
void OnURLFetchCompleteInternal(int net_error,
int response_code,
std::unique_ptr<std::string> response_body);
void OnJsonParseFailure(const std::string& error);
void OnJsonParseSuccess(std::unique_ptr<base::Value> parsed_json);
// For tests only. Returns the full URL of the request.
GURL getUrl() const { return url_; }
// The fetcher for subscribing.
std::unique_ptr<network::SimpleURLLoader> simple_loader_;
// The callback to notify when URLFetcher finished and results are available.
// When the request is finished/aborted/destroyed, it's called in the dtor!
CompletedCallback request_completed_callback_;
// The callback for Parsing JSON.
ParseJSONCallback parse_json_;
// Full URL. Used in tests only.
GURL url_;
base::WeakPtrFactory<PerUserTopicRegistrationRequest> weak_ptr_factory_;
DISALLOW_COPY_AND_ASSIGN(PerUserTopicRegistrationRequest);
};
} // namespace syncer
#endif // COMPONENTS_INVALIDATION_IMPL_PER_USER_TOPIC_REGISTRATION_REQUEST_H_
// Copyright 2018 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.
#include "components/invalidation/impl/per_user_topic_registration_request.h"
#include "base/json/json_reader.h"
#include "base/memory/ptr_util.h"
#include "base/message_loop/message_loop.h"
#include "base/strings/stringprintf.h"
#include "base/test/gtest_util.h"
#include "base/test/mock_callback.h"
#include "base/test/test_simple_task_runner.h"
#include "base/threading/thread_task_runner_handle.h"
#include "base/values.h"
#include "components/invalidation/impl/json_unsafe_parser.h"
#include "net/url_request/test_url_fetcher_factory.h"
#include "net/url_request/url_request_test_util.h"
#include "services/network/test/test_url_loader_factory.h"
#include "testing/gmock/include/gmock/gmock.h"
#include "testing/gtest/include/gtest/gtest.h"
namespace syncer {
namespace {
using testing::_;
using testing::SaveArg;
MATCHER_P(EqualsJSON, json, "equals JSON") {
std::unique_ptr<base::Value> expected = base::JSONReader::Read(json);
if (!expected) {
*result_listener << "INTERNAL ERROR: couldn't parse expected JSON";
return false;
}
std::string err_msg;
int err_line, err_col;
std::unique_ptr<base::Value> actual = base::JSONReader::ReadAndReturnError(
arg, base::JSON_PARSE_RFC, nullptr, &err_msg, &err_line, &err_col);
if (!actual) {
*result_listener << "input:" << err_line << ":" << err_col << ": "
<< "parse error: " << err_msg;
return false;
}
return *expected == *actual;
}
network::ResourceResponseHead CreateHeadersForTest(int responce_code) {
network::ResourceResponseHead head;
head.headers = new net::HttpResponseHeaders(base::StringPrintf(
"HTTP/1.1 %d OK\nContent-type: text/html\n\n", responce_code));
head.mime_type = "text/html";
return head;
}
} // namespace
class PerUserTopicRegistrationRequestTest : public testing::Test {
public:
PerUserTopicRegistrationRequestTest() {}
GURL url(PerUserTopicRegistrationRequest* request) {
return request->getUrl();
}
network::TestURLLoaderFactory* url_loader_factory() {
return &url_loader_factory_;
}
private:
base::MessageLoop message_loop_;
network::TestURLLoaderFactory url_loader_factory_;
DISALLOW_COPY_AND_ASSIGN(PerUserTopicRegistrationRequestTest);
};
TEST_F(PerUserTopicRegistrationRequestTest,
ShouldNotInvokeCallbackWhenCancelled) {
std::string token = "1234567890";
std::string url = "http://valid-url.test";
std::string topic = "test";
std::string project_id = "smarty-pants-12345";
base::MockCallback<PerUserTopicRegistrationRequest::CompletedCallback>
callback;
EXPECT_CALL(callback, Run(_, _)).Times(0);
PerUserTopicRegistrationRequest::Builder builder;
std::unique_ptr<PerUserTopicRegistrationRequest> request =
builder.SetToken(token)
.SetScope(url)
.SetPublicTopicName(topic)
.SetProjectId(project_id)
.Build();
request->Start(callback.Get(),
base::BindOnce(syncer::JsonUnsafeParser::Parse),
url_loader_factory());
base::RunLoop().RunUntilIdle();
// Destroy the request before getting any response.
request.reset();
}
TEST_F(PerUserTopicRegistrationRequestTest, ShouldSubscribeWithoutErrors) {
std::string token = "1234567890";
std::string base_url = "http://valid-url.test";
std::string topic = "test";
std::string project_id = "smarty-pants-12345";
base::MockCallback<PerUserTopicRegistrationRequest::CompletedCallback>
callback;
Status status(StatusCode::FAILED, "initial");
std::string private_topic;
EXPECT_CALL(callback, Run(_, _))
.WillOnce(DoAll(SaveArg<0>(&status), SaveArg<1>(&private_topic)));
PerUserTopicRegistrationRequest::Builder builder;
std::unique_ptr<PerUserTopicRegistrationRequest> request =
builder.SetToken(token)
.SetScope(base_url)
.SetPublicTopicName(topic)
.SetProjectId(project_id)
.Build();
std::string response_body = R"(
{
"private_topic_name": "test-pr"
}
)";
network::URLLoaderCompletionStatus response_status(net::OK);
response_status.decoded_body_length = response_body.size();
url_loader_factory()->AddResponse(url(request.get()),
CreateHeadersForTest(net::HTTP_OK),
response_body, response_status);
request->Start(callback.Get(),
base::BindOnce(syncer::JsonUnsafeParser::Parse),
url_loader_factory());
base::RunLoop().RunUntilIdle();
EXPECT_EQ(status.code, StatusCode::SUCCESS);
EXPECT_EQ(private_topic, "test-pr");
}
TEST_F(PerUserTopicRegistrationRequestTest,
ShouleNotSubscribeWhenNetworkProblem) {
std::string token = "1234567890";
std::string base_url = "http://valid-url.test";
std::string topic = "test";
std::string project_id = "smarty-pants-12345";
base::MockCallback<PerUserTopicRegistrationRequest::CompletedCallback>
callback;
Status status(StatusCode::FAILED, "initial");
std::string private_topic;
EXPECT_CALL(callback, Run(_, _))
.WillOnce(DoAll(SaveArg<0>(&status), SaveArg<1>(&private_topic)));
PerUserTopicRegistrationRequest::Builder builder;
std::unique_ptr<PerUserTopicRegistrationRequest> request =
builder.SetToken(token)
.SetScope(base_url)
.SetPublicTopicName(topic)
.SetProjectId(project_id)
.Build();
std::string response_body = R"(
{
"private_topic_name": "test-pr"
}
)";
network::URLLoaderCompletionStatus response_status(net::ERR_TIMED_OUT);
response_status.decoded_body_length = response_body.size();
url_loader_factory()->AddResponse(url(request.get()),
CreateHeadersForTest(net::HTTP_OK),
response_body, response_status);
request->Start(callback.Get(),
base::BindOnce(syncer::JsonUnsafeParser::Parse),
url_loader_factory());
base::RunLoop().RunUntilIdle();
EXPECT_EQ(status.code, StatusCode::FAILED);
}
TEST_F(PerUserTopicRegistrationRequestTest,
ShouldNotSubscribeWhenWrongResponse) {
std::string token = "1234567890";
std::string base_url = "http://valid-url.test";
std::string topic = "test";
std::string project_id = "smarty-pants-12345";
base::MockCallback<PerUserTopicRegistrationRequest::CompletedCallback>
callback;
Status status(StatusCode::FAILED, "initial");
std::string private_topic;
EXPECT_CALL(callback, Run(_, _))
.WillOnce(DoAll(SaveArg<0>(&status), SaveArg<1>(&private_topic)));
PerUserTopicRegistrationRequest::Builder builder;
std::unique_ptr<PerUserTopicRegistrationRequest> request =
builder.SetToken(token)
.SetScope(base_url)
.SetPublicTopicName(topic)
.SetProjectId(project_id)
.Build();
std::string response_body = R"(
{}
)";
network::URLLoaderCompletionStatus response_status(net::OK);
response_status.decoded_body_length = response_body.size();
url_loader_factory()->AddResponse(url(request.get()),
CreateHeadersForTest(net::HTTP_OK),
response_body, response_status);
request->Start(callback.Get(),
base::BindOnce(syncer::JsonUnsafeParser::Parse),
url_loader_factory());
base::RunLoop().RunUntilIdle();
EXPECT_EQ(status.code, StatusCode::FAILED);
}
} // namespace syncer
// Copyright 2018 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.
#include "components/invalidation/impl/status.h"
namespace syncer {
Status::Status(StatusCode status_code, const std::string& message)
: code(status_code), message(message) {}
Status Status::Success() {
return Status(StatusCode::SUCCESS, std::string());
}
Status::~Status() = default;
} // namespace syncer
// Copyright 2018 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 COMPONENTS_INVALIDATION_IMPL_STATUS_H_
#define COMPONENTS_INVALIDATION_IMPL_STATUS_H_
#include <string>
namespace syncer {
// This enum indicates how an operation was completed. These values are written
// to logs. New enum values can be added, but existing enums must never be
// renumbered or deleted and reused.
enum class StatusCode {
// The operation has been completed successfully.
SUCCESS = 0,
// The operation failed.
FAILED = 1
};
// This struct provides the status code of a request and an optional message
// describing the status (esp. failures) in detail.
struct Status {
Status(StatusCode status_code, const std::string& message);
~Status();
// Errors always need a message but a success does not.
static Status Success();
bool IsSuccess() const { return code == StatusCode::SUCCESS; }
StatusCode code;
// The message is not meant to be displayed to the user.
std::string message;
// Copy and assignment allowed.
};
} // namespace syncer
#endif // COMPONENTS_INVALIDATION_IMPL_STATUS_H_
...@@ -262,4 +262,5 @@ Refer to README.md for content description and update process. ...@@ -262,4 +262,5 @@ Refer to README.md for content description and update process.
<item id="webstore_installer" hash_code="18764319" type="0" content_hash_code="11030110" os_list="linux,windows" file_path="chrome/browser/extensions/webstore_installer.cc"/> <item id="webstore_installer" hash_code="18764319" type="0" content_hash_code="11030110" os_list="linux,windows" file_path="chrome/browser/extensions/webstore_installer.cc"/>
<item id="webui_content_scripts_download" hash_code="100545943" type="0" content_hash_code="119898059" os_list="linux,windows" file_path="extensions/browser/guest_view/web_view/web_ui/web_ui_url_fetcher.cc"/> <item id="webui_content_scripts_download" hash_code="100545943" type="0" content_hash_code="119898059" os_list="linux,windows" file_path="extensions/browser/guest_view/web_view/web_ui/web_ui_url_fetcher.cc"/>
<item id="xmpp_signal_strategy" hash_code="88906454" type="0" content_hash_code="88958321" os_list="linux,windows" file_path="remoting/signaling/xmpp_signal_strategy.cc"/> <item id="xmpp_signal_strategy" hash_code="88906454" type="0" content_hash_code="88958321" os_list="linux,windows" file_path="remoting/signaling/xmpp_signal_strategy.cc"/>
<item id="per_user_topic_registration_request" hash_code="10498172" type="0" content_hash_code="32495619" os_list="linux,windows,android" file_path="components/invalidation/impl/per_user_topic_registration_request.cc"/>
</annotations> </annotations>
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