mirror of
https://github.com/kidfromjupiter/nearby.git
synced 2026-09-14 22:56:12 -04:00
Implement FastPairClient to provides APIs to the FastPair backend OAuth/un-OAuth API
PiperOrigin-RevId: 543807725
This commit is contained in:
committed by
Copybara-Service
parent
b89eeb1ac1
commit
259350e343
@@ -31,17 +31,16 @@
|
||||
namespace nearby {
|
||||
namespace fastpair {
|
||||
|
||||
::nearby::fastpair::proto::FastPairInfo BuildFastPairInfo(
|
||||
const FastPairDevice& fast_pair_device) {
|
||||
void BuildFastPairInfo(::nearby::fastpair::proto::FastPairInfo* fast_pair_info,
|
||||
const FastPairDevice& fast_pair_device) {
|
||||
NEARBY_LOGS(VERBOSE) << __func__;
|
||||
const AccountKey& account_key = fast_pair_device.GetAccountKey();
|
||||
auto& metadata = fast_pair_device.GetMetadata();
|
||||
DCHECK(metadata);
|
||||
DCHECK(account_key.Ok());
|
||||
|
||||
::nearby::fastpair::proto::FastPairInfo proto;
|
||||
// Sets FastPairDevice to FastPairInfo
|
||||
auto* device = proto.mutable_device();
|
||||
// Sets FastPairDevice of FastPairInfo
|
||||
auto* device = fast_pair_info->mutable_device();
|
||||
device->set_account_key(account_key.GetAsBytes());
|
||||
// Create a SHA256 hash of the |mac_address| with the |account_key| as salt.
|
||||
// The hash is used to identify devices via non discoverable advertisements.
|
||||
@@ -122,15 +121,11 @@ namespace fastpair {
|
||||
fast_pair_strings->set_sync_sms_description(strings.sync_sms_description());
|
||||
|
||||
device->set_discovery_item_bytes(discovery_item.SerializeAsString());
|
||||
|
||||
return proto;
|
||||
}
|
||||
|
||||
::nearby::fastpair::proto::FastPairInfo BuildFastPairInfoForOptIn(
|
||||
proto::OptInStatus opt_in_status) {
|
||||
proto::FastPairInfo proto;
|
||||
proto.set_opt_in_status(opt_in_status);
|
||||
return proto;
|
||||
void BuildFastPairInfo(::nearby::fastpair::proto::FastPairInfo* fast_pair_info,
|
||||
proto::OptInStatus opt_in_status) {
|
||||
fast_pair_info->set_opt_in_status(opt_in_status);
|
||||
}
|
||||
} // namespace fastpair
|
||||
} // namespace nearby
|
||||
|
||||
@@ -22,12 +22,12 @@ namespace nearby {
|
||||
namespace fastpair {
|
||||
|
||||
// Builds FastPairInfo proto from a FastPairDevice instance
|
||||
::nearby::fastpair::proto::FastPairInfo BuildFastPairInfo(
|
||||
const FastPairDevice& fast_pair_device);
|
||||
void BuildFastPairInfo(::nearby::fastpair::proto::FastPairInfo* fast_pair_info,
|
||||
const FastPairDevice& fast_pair_device);
|
||||
|
||||
// Builds FastPairInfo proto from OptInStatus
|
||||
::nearby::fastpair::proto::FastPairInfo BuildFastPairInfoForOptIn(
|
||||
proto::OptInStatus opt_in_status);
|
||||
void BuildFastPairInfo(::nearby::fastpair::proto::FastPairInfo* fast_pair_info,
|
||||
proto::OptInStatus opt_in_status);
|
||||
|
||||
} // namespace fastpair
|
||||
} // namespace nearby
|
||||
|
||||
@@ -52,7 +52,8 @@ TEST(ProtoBuilderTest, BuildFastPairInfo) {
|
||||
device.SetMetadata(device_metadata);
|
||||
|
||||
// Builds FastPairInfo from the created FastPairDevice
|
||||
proto::FastPairInfo fast_proto_info = BuildFastPairInfo(device);
|
||||
proto::FastPairInfo fast_proto_info;
|
||||
BuildFastPairInfo(&fast_proto_info, device);
|
||||
|
||||
EXPECT_EQ(fast_proto_info.device().account_key(), account_key.GetAsBytes());
|
||||
EXPECT_EQ(absl::BytesToHexString(
|
||||
@@ -69,8 +70,9 @@ TEST(ProtoBuilderTest, BuildFastPairInfo) {
|
||||
}
|
||||
|
||||
TEST(ProtoBuilderTest, BuildFastPairInfoForOptIn) {
|
||||
proto::FastPairInfo fast_proto_info =
|
||||
BuildFastPairInfoForOptIn(proto::OptInStatus::OPT_IN_STATUS_OPTED_IN);
|
||||
proto::FastPairInfo fast_proto_info;
|
||||
BuildFastPairInfo(&fast_proto_info,
|
||||
proto::OptInStatus::OPT_IN_STATUS_OPTED_IN);
|
||||
EXPECT_EQ(fast_proto_info.opt_in_status(),
|
||||
proto::OptInStatus::OPT_IN_STATUS_OPTED_IN);
|
||||
}
|
||||
|
||||
@@ -17,6 +17,7 @@ licenses(["notice"])
|
||||
cc_library(
|
||||
name = "server_access",
|
||||
srcs = [
|
||||
"fast_pair_client_impl.cc",
|
||||
"fast_pair_http_notifier.cc",
|
||||
"fast_pair_metadata_downloader.cc",
|
||||
"fast_pair_metadata_downloader_impl.cc",
|
||||
@@ -24,6 +25,8 @@ cc_library(
|
||||
"fast_pair_repository_impl.cc",
|
||||
],
|
||||
hdrs = [
|
||||
"fast_pair_client.h",
|
||||
"fast_pair_client_impl.h",
|
||||
"fast_pair_http_notifier.h",
|
||||
"fast_pair_metadata_downloader.h",
|
||||
"fast_pair_metadata_downloader_impl.h",
|
||||
@@ -38,14 +41,20 @@ cc_library(
|
||||
"//fastpair/proto:fastpair_cc_proto",
|
||||
"//fastpair/proto:proto_to_json",
|
||||
"//fastpair/repository",
|
||||
"//internal/account",
|
||||
"//internal/auth:types",
|
||||
"//internal/base",
|
||||
"//internal/base:bluetooth_address",
|
||||
"//internal/crypto",
|
||||
"//internal/network:nearby_http_client",
|
||||
"//internal/network:types",
|
||||
"//internal/platform:logging",
|
||||
"//internal/platform:types",
|
||||
"@com_google_absl//absl/functional:any_invocable",
|
||||
"@com_google_absl//absl/status:statusor",
|
||||
"@com_google_absl//absl/strings",
|
||||
"@com_google_absl//absl/strings:str_format",
|
||||
"@com_google_absl//absl/synchronization",
|
||||
"@nlohmann_json//:json",
|
||||
],
|
||||
)
|
||||
@@ -140,3 +149,29 @@ cc_test(
|
||||
"@com_google_googletest//:gtest_main",
|
||||
],
|
||||
)
|
||||
|
||||
cc_test(
|
||||
name = "fast_pair_client_impl_test",
|
||||
srcs = [
|
||||
"fast_pair_client_impl_test.cc",
|
||||
],
|
||||
copts = [
|
||||
"-Ithird_party",
|
||||
],
|
||||
deps = [
|
||||
":server_access",
|
||||
"//fastpair/common",
|
||||
"//fastpair/proto:fastpair_cc_proto",
|
||||
"//fastpair/proto:proto_builder",
|
||||
"//internal/account",
|
||||
"//internal/account:test_support",
|
||||
"//internal/auth:credential",
|
||||
"//internal/network:types",
|
||||
"//internal/platform:types",
|
||||
"//internal/platform/implementation/g3", # build_cleaner: keep
|
||||
"//internal/test",
|
||||
"//internal/test/google3_only:test",
|
||||
"@com_github_protobuf_matchers//protobuf-matchers",
|
||||
"@com_google_googletest//:gtest_main",
|
||||
],
|
||||
)
|
||||
|
||||
@@ -0,0 +1,51 @@
|
||||
// Copyright 2023 Google LLC
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// https://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
#ifndef THIRD_PARTY_NEARBY_FASTPAIR_SERVER_ACCESS_FAST_PAIR_CLIENT_H_
|
||||
#define THIRD_PARTY_NEARBY_FASTPAIR_SERVER_ACCESS_FAST_PAIR_CLIENT_H_
|
||||
|
||||
#include "absl/status/statusor.h"
|
||||
#include "fastpair/proto/fastpair_rpcs.proto.h"
|
||||
|
||||
namespace nearby {
|
||||
namespace fastpair {
|
||||
|
||||
// FastPairClient is used to access Fast Pair backend APIs.
|
||||
class FastPairClient {
|
||||
public:
|
||||
virtual ~FastPairClient() = default;
|
||||
// Gets an observed device.
|
||||
// Blocking function
|
||||
virtual absl::StatusOr<proto::GetObservedDeviceResponse> GetObservedDevice(
|
||||
const proto::GetObservedDeviceRequest& request) = 0;
|
||||
|
||||
// Reads the user's devices.
|
||||
// Blocking function
|
||||
virtual absl::StatusOr<proto::UserReadDevicesResponse> UserReadDevices(
|
||||
const proto::UserReadDevicesRequest& request) = 0;
|
||||
|
||||
// Writes a new device to a user's account.
|
||||
// Blocking function
|
||||
virtual absl::StatusOr<proto::UserWriteDeviceResponse> UserWriteDevice(
|
||||
const proto::UserWriteDeviceRequest& request) = 0;
|
||||
|
||||
// Deletes an existing device from a user's account.
|
||||
// Blocking function
|
||||
virtual absl::StatusOr<proto::UserDeleteDeviceResponse> UserDeleteDevice(
|
||||
const proto::UserDeleteDeviceRequest& request) = 0;
|
||||
};
|
||||
} // namespace fastpair
|
||||
} // namespace nearby
|
||||
|
||||
#endif // THIRD_PARTY_NEARBY_FASTPAIR_SERVER_ACCESS_FAST_PAIR_CLIENT_H_
|
||||
@@ -0,0 +1,346 @@
|
||||
// Copyright 2023 Google LLC
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// https://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
#include "fastpair/server_access/fast_pair_client_impl.h"
|
||||
|
||||
#include <memory>
|
||||
#include <optional>
|
||||
#include <string>
|
||||
#include <utility>
|
||||
#include <vector>
|
||||
|
||||
#include "absl/strings/str_cat.h"
|
||||
#include "absl/strings/str_format.h"
|
||||
#include "absl/strings/string_view.h"
|
||||
#include "absl/synchronization/notification.h"
|
||||
#include "fastpair/common/fast_pair_switches.h"
|
||||
#include "internal/account/account_manager.h"
|
||||
#include "internal/network/http_client.h"
|
||||
#include "internal/network/url.h"
|
||||
#include "internal/platform/logging.h"
|
||||
|
||||
namespace nearby {
|
||||
namespace fastpair {
|
||||
namespace {
|
||||
using ::nearby::network::HttpClient;
|
||||
using ::nearby::network::HttpRequest;
|
||||
using ::nearby::network::HttpRequestMethod;
|
||||
using ::nearby::network::HttpResponse;
|
||||
using ::nearby::network::Url;
|
||||
// ----------------- HTTP Constants ---------------------------------
|
||||
constexpr absl::string_view kProtobufContentType = "application/x-protobuf";
|
||||
constexpr absl::string_view kKey = "key";
|
||||
constexpr absl::string_view kClientId =
|
||||
"AIzaSyBv7ZrOlX5oIJLVQrZh-WkZFKm5L6FlStQ";
|
||||
constexpr absl::string_view kMode = "mode";
|
||||
constexpr absl::string_view kQueryParameterAlternateOutputKey = "alt";
|
||||
constexpr absl::string_view kQueryParameterAlternateOutputProto = "proto";
|
||||
constexpr absl::string_view kPlatformTypeHeaderName =
|
||||
"X-FastPair-Platform-Type";
|
||||
constexpr absl::string_view kDefaultNearbyDevicesHttpHost =
|
||||
"https://nearbydevices-pa.googleapis.com";
|
||||
|
||||
constexpr absl::string_view kNearbyV1Path = "v1/";
|
||||
constexpr absl::string_view kDevicesPath = "device/";
|
||||
constexpr absl::string_view kUserDevicesPath = "user/devices";
|
||||
constexpr absl::string_view kUserDeleteDevicePath = "user/device";
|
||||
const char* GetObservedDeviceMode[3] = {"MODE_UNKNOWN", "MODE_RELEASE",
|
||||
"MODE_DEBUG"};
|
||||
|
||||
absl::string_view GetPlatformTypeString(api::DeviceInfo::OsType os_type) {
|
||||
switch (os_type) {
|
||||
case api::DeviceInfo::OsType::kAndroid:
|
||||
return "OSType.ANDROID";
|
||||
case api::DeviceInfo::OsType::kChromeOs:
|
||||
return "OSType.CHROME_OS";
|
||||
case api::DeviceInfo::OsType::kIos:
|
||||
return "OSType.IOS";
|
||||
case api::DeviceInfo::OsType::kWindows:
|
||||
return "OSType.WINDOWS";
|
||||
default:
|
||||
return "OSType.UNKNOWN";
|
||||
}
|
||||
}
|
||||
|
||||
// Creates the full Nearby V1 URL with |request_path|.
|
||||
Url CreateV1RequestUrl(absl::string_view request_path) {
|
||||
std::string host = std::string(kDefaultNearbyDevicesHttpHost);
|
||||
auto host_switch = switches::GetNearbyFastPairHttpHost();
|
||||
if (!host_switch.empty()) {
|
||||
host = host_switch;
|
||||
}
|
||||
std::string path =
|
||||
absl::StrFormat("%s/%s%s", host, kNearbyV1Path, request_path);
|
||||
NEARBY_LOGS(INFO) << __func__ << "= " << path;
|
||||
return Url::Create(path).value();
|
||||
}
|
||||
} // namespace
|
||||
|
||||
FastPairClientImpl::FastPairClientImpl(
|
||||
auth::AuthenticationManager* authentication_manager,
|
||||
AccountManager* account_manager,
|
||||
std::unique_ptr<network::HttpClient> http_client,
|
||||
FastPairHttpNotifier* notifier, DeviceInfo* device_info)
|
||||
: authentication_manager_(authentication_manager),
|
||||
account_manager_(account_manager),
|
||||
http_client_(std::move(http_client)),
|
||||
notifier_(notifier),
|
||||
device_info_(device_info) {}
|
||||
|
||||
// Gets an observed device.
|
||||
absl::StatusOr<proto::GetObservedDeviceResponse>
|
||||
FastPairClientImpl::GetObservedDevice(
|
||||
const proto::GetObservedDeviceRequest& request) {
|
||||
NEARBY_LOGS(VERBOSE) << __func__ << ": Start API call to get observed device";
|
||||
notifier_->NotifyOfRequest(request);
|
||||
|
||||
// Sets up request mode.
|
||||
QueryParameters params;
|
||||
params.push_back({std::string(kMode), GetObservedDeviceMode[request.mode()]});
|
||||
|
||||
HttpRequest http_request = CreateHttpRequest(
|
||||
/*access token= */ std::nullopt,
|
||||
/*Url=*/
|
||||
CreateV1RequestUrl(std::string(kDevicesPath) +
|
||||
std::to_string(request.device_id())),
|
||||
RequestType::kGet,
|
||||
/*query parameters=*/params,
|
||||
/*body=*/std::string());
|
||||
|
||||
absl::StatusOr<HttpResponse> http_response =
|
||||
http_client_->GetResponse(http_request);
|
||||
|
||||
if (!http_response.ok()) {
|
||||
NEARBY_LOGS(ERROR) << __func__ << ": Failed to get response.";
|
||||
return http_response.status();
|
||||
}
|
||||
|
||||
proto::GetObservedDeviceResponse response;
|
||||
if (!response.ParseFromString(http_response->GetBody().GetRawData())) {
|
||||
NEARBY_LOGS(ERROR) << __func__ << ": Failed to parse server response.";
|
||||
return absl::InvalidArgumentError("Parse proto error");
|
||||
}
|
||||
|
||||
notifier_->NotifyOfResponse(response);
|
||||
NEARBY_LOGS(INFO)
|
||||
<< __func__ << ": Complete API call to get observed device successfully";
|
||||
return response;
|
||||
}
|
||||
|
||||
// Reads the user's devices.
|
||||
absl::StatusOr<proto::UserReadDevicesResponse>
|
||||
FastPairClientImpl::UserReadDevices(
|
||||
const proto::UserReadDevicesRequest& request) {
|
||||
NEARBY_LOGS(VERBOSE) << __func__ << ": Start API call to read user's devices";
|
||||
notifier_->NotifyOfRequest(request);
|
||||
|
||||
absl::StatusOr<std::string> access_token = GetAccessToken();
|
||||
if (!access_token.ok()) {
|
||||
NEARBY_LOGS(WARNING) << __func__
|
||||
<< ": Skip API call to read user devices due to no "
|
||||
"available access token.";
|
||||
return absl::UnauthenticatedError("No user logged in.");
|
||||
}
|
||||
|
||||
HttpRequest http_request = CreateHttpRequest(
|
||||
*access_token,
|
||||
/*Url=*/CreateV1RequestUrl(kUserDevicesPath), RequestType::kGet,
|
||||
/*query parameters=*/std::nullopt,
|
||||
/*body=*/request.SerializeAsString());
|
||||
|
||||
absl::StatusOr<HttpResponse> http_response =
|
||||
http_client_->GetResponse(http_request);
|
||||
|
||||
if (!http_response.ok()) {
|
||||
NEARBY_LOGS(ERROR) << __func__ << ": Failed to get response.";
|
||||
return http_response.status();
|
||||
}
|
||||
|
||||
proto::UserReadDevicesResponse response;
|
||||
if (!response.ParseFromString(http_response->GetBody().GetRawData())) {
|
||||
NEARBY_LOGS(ERROR) << __func__ << ": Failed to parse server response.";
|
||||
return absl::InvalidArgumentError("Parse proto error");
|
||||
}
|
||||
|
||||
notifier_->NotifyOfResponse(response);
|
||||
NEARBY_LOGS(INFO) << __func__
|
||||
<< ": Complete API call to read user devices successfully";
|
||||
return response;
|
||||
}
|
||||
|
||||
// Writes a new device to a user's account.
|
||||
absl::StatusOr<proto::UserWriteDeviceResponse>
|
||||
FastPairClientImpl::UserWriteDevice(
|
||||
const proto::UserWriteDeviceRequest& request) {
|
||||
NEARBY_LOGS(VERBOSE) << __func__ << ": Start API call to write user device";
|
||||
notifier_->NotifyOfRequest(request);
|
||||
|
||||
absl::StatusOr<std::string> access_token = GetAccessToken();
|
||||
if (!access_token.ok()) {
|
||||
NEARBY_LOGS(WARNING) << __func__
|
||||
<< ": Skip API call to write user devices due to no "
|
||||
"available access token.";
|
||||
return absl::UnauthenticatedError("No user logged in.");
|
||||
}
|
||||
|
||||
HttpRequest http_request = CreateHttpRequest(
|
||||
*access_token,
|
||||
/*Url=*/CreateV1RequestUrl(kUserDevicesPath), RequestType::kPost,
|
||||
/*query parameters=*/std::nullopt,
|
||||
/*body=*/request.SerializeAsString());
|
||||
|
||||
absl::StatusOr<HttpResponse> http_response =
|
||||
http_client_->GetResponse(http_request);
|
||||
|
||||
if (!http_response.ok()) {
|
||||
NEARBY_LOGS(ERROR) << __func__ << ": Failed to get response.";
|
||||
return http_response.status();
|
||||
}
|
||||
|
||||
proto::UserWriteDeviceResponse response;
|
||||
if (!response.ParseFromString(http_response->GetBody().GetRawData())) {
|
||||
NEARBY_LOGS(ERROR) << __func__ << ": Failed to parse server response.";
|
||||
return absl::InvalidArgumentError("Parse proto error");
|
||||
}
|
||||
|
||||
notifier_->NotifyOfResponse(response);
|
||||
NEARBY_LOGS(INFO) << __func__
|
||||
<< ": Complete API call to write user devices successfully";
|
||||
return response;
|
||||
}
|
||||
|
||||
// Deletes an existing device from a user's account.
|
||||
absl::StatusOr<proto::UserDeleteDeviceResponse>
|
||||
FastPairClientImpl::UserDeleteDevice(
|
||||
const proto::UserDeleteDeviceRequest& request) {
|
||||
NEARBY_LOGS(VERBOSE) << __func__ << ": Start API call to delete user device";
|
||||
notifier_->NotifyOfRequest(request);
|
||||
|
||||
absl::StatusOr<std::string> access_token = GetAccessToken();
|
||||
if (!access_token.ok()) {
|
||||
NEARBY_LOGS(WARNING) << __func__
|
||||
<< ": Skip API call to delete user devices due to no "
|
||||
"available access token.";
|
||||
return absl::UnauthenticatedError("No user logged in.");
|
||||
}
|
||||
|
||||
HttpRequest http_request =
|
||||
CreateHttpRequest(*access_token,
|
||||
/*Url=*/
|
||||
CreateV1RequestUrl(std::string(kUserDeleteDevicePath) +
|
||||
"/" + request.hex_account_key()),
|
||||
RequestType::kDelete,
|
||||
/*query parameters=*/std::nullopt,
|
||||
/*body=*/std::string());
|
||||
|
||||
absl::StatusOr<HttpResponse> http_response =
|
||||
http_client_->GetResponse(http_request);
|
||||
|
||||
if (!http_response.ok()) {
|
||||
NEARBY_LOGS(ERROR) << __func__ << ": Failed to get response.";
|
||||
return http_response.status();
|
||||
}
|
||||
|
||||
proto::UserDeleteDeviceResponse response;
|
||||
if (!response.ParseFromString(http_response->GetBody().GetRawData())) {
|
||||
NEARBY_LOGS(ERROR) << __func__ << ": Failed to parse server response.";
|
||||
return absl::InvalidArgumentError("Parse proto error");
|
||||
}
|
||||
|
||||
notifier_->NotifyOfResponse(response);
|
||||
NEARBY_LOGS(INFO)
|
||||
<< __func__ << ": Complete API call to delete user devices successfully";
|
||||
return response;
|
||||
}
|
||||
|
||||
// Blocking function
|
||||
absl::StatusOr<std::string> FastPairClientImpl::GetAccessToken() {
|
||||
NEARBY_LOGS(VERBOSE) << __func__;
|
||||
std::optional<AccountManager::Account> account =
|
||||
account_manager_->GetCurrentAccount();
|
||||
if (!account.has_value()) {
|
||||
NEARBY_LOGS(WARNING)
|
||||
<< __func__ << ": Failed to get access token due to no login user.";
|
||||
return absl::UnauthenticatedError("No user logged in.");
|
||||
}
|
||||
absl::StatusOr<std::string> result;
|
||||
absl::Notification notification;
|
||||
authentication_manager_->FetchAccessToken(
|
||||
account->id, {
|
||||
.success_cb =
|
||||
[&](absl::string_view access_token) {
|
||||
result = std::string(access_token);
|
||||
notification.Notify();
|
||||
},
|
||||
.failure_cb =
|
||||
[&](auth::AuthStatus status) {
|
||||
result = absl::UnknownError(
|
||||
absl::StrCat(static_cast<int>(status)));
|
||||
notification.Notify();
|
||||
},
|
||||
});
|
||||
notification.WaitForNotification();
|
||||
return result;
|
||||
}
|
||||
|
||||
HttpRequest FastPairClientImpl::CreateHttpRequest(
|
||||
std::optional<absl::string_view> access_token, Url url,
|
||||
RequestType request_type,
|
||||
std::optional<QueryParameters> request_as_query_parameters,
|
||||
std::optional<std::string> body) {
|
||||
NEARBY_LOGS(VERBOSE) << __func__;
|
||||
HttpRequest request{url};
|
||||
|
||||
// Handles query strings
|
||||
request.AddQueryParameter(kQueryParameterAlternateOutputKey,
|
||||
kQueryParameterAlternateOutputProto);
|
||||
request.AddQueryParameter(kKey, kClientId);
|
||||
if (request_as_query_parameters.has_value()) {
|
||||
for (const auto& key_value_pair : *request_as_query_parameters) {
|
||||
request.AddQueryParameter(key_value_pair.first, key_value_pair.second);
|
||||
}
|
||||
}
|
||||
|
||||
// Handles request method
|
||||
request.SetMethod(GetRequestMethod(request_type));
|
||||
// Handles headers
|
||||
if (access_token.has_value()) {
|
||||
NEARBY_LOGS(INFO) << __func__ << " : Authorization with access token";
|
||||
request.AddHeader("Authorization",
|
||||
absl::StrCat("Bearer ", access_token.value()));
|
||||
}
|
||||
request.AddHeader(kPlatformTypeHeaderName,
|
||||
GetPlatformTypeString(device_info_->GetOsType()));
|
||||
request.AddHeader("Content-Type",
|
||||
body ? kProtobufContentType : std::string());
|
||||
|
||||
// Handles request body
|
||||
request.SetBody(body.value_or(std::string()));
|
||||
return request;
|
||||
}
|
||||
|
||||
HttpRequestMethod FastPairClientImpl::GetRequestMethod(
|
||||
RequestType request_type) const {
|
||||
if (request_type == RequestType::kPost) {
|
||||
NEARBY_LOGS(INFO) << __func__ << " : request_type= kPost";
|
||||
return HttpRequestMethod::kPost;
|
||||
} else if (request_type == RequestType::kDelete) {
|
||||
NEARBY_LOGS(INFO) << __func__ << ": request_type= kDelete";
|
||||
return HttpRequestMethod::kDelete;
|
||||
}
|
||||
NEARBY_LOGS(INFO) << __func__ << " : request_type= kGet";
|
||||
return HttpRequestMethod::kGet;
|
||||
}
|
||||
} // namespace fastpair
|
||||
} // namespace nearby
|
||||
@@ -0,0 +1,87 @@
|
||||
// Copyright 2023 Google LLC
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// https://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
#ifndef THIRD_PARTY_NEARBY_FASTPAIR_SERVER_ACCESS_FAST_PAIR_CLIENT_IMPL_H_
|
||||
#define THIRD_PARTY_NEARBY_FASTPAIR_SERVER_ACCESS_FAST_PAIR_CLIENT_IMPL_H_
|
||||
|
||||
#include <memory>
|
||||
#include <optional>
|
||||
#include <string>
|
||||
#include <utility>
|
||||
#include <vector>
|
||||
|
||||
#include "absl/strings/string_view.h"
|
||||
#include "fastpair/server_access/fast_pair_client.h"
|
||||
#include "fastpair/server_access/fast_pair_http_notifier.h"
|
||||
#include "internal/account/account_manager.h"
|
||||
#include "internal/auth/authentication_manager.h"
|
||||
#include "internal/network/http_client.h"
|
||||
#include "internal/network/url.h"
|
||||
#include "internal/platform/device_info.h"
|
||||
|
||||
namespace nearby {
|
||||
namespace fastpair {
|
||||
// An implementation of FastPairClient that fetches access tokens and makes
|
||||
// HTTP request to FastPair backend.
|
||||
class FastPairClientImpl : public FastPairClient {
|
||||
public:
|
||||
// Query strings of the request.
|
||||
using QueryParameters = std::vector<std::pair<std::string, std::string>>;
|
||||
|
||||
FastPairClientImpl(auth::AuthenticationManager* authentication_manager,
|
||||
AccountManager* account_manager,
|
||||
std::unique_ptr<network::HttpClient> http_client,
|
||||
FastPairHttpNotifier* notifier, DeviceInfo* device_info);
|
||||
FastPairClientImpl(FastPairClientImpl&) = delete;
|
||||
FastPairClientImpl& operator=(FastPairClientImpl&) = delete;
|
||||
~FastPairClientImpl() override = default;
|
||||
|
||||
// Gets an observed device.
|
||||
absl::StatusOr<proto::GetObservedDeviceResponse> GetObservedDevice(
|
||||
const proto::GetObservedDeviceRequest& request) override;
|
||||
|
||||
// Reads the user's devices.
|
||||
absl::StatusOr<proto::UserReadDevicesResponse> UserReadDevices(
|
||||
const proto::UserReadDevicesRequest& request) override;
|
||||
|
||||
// Writes a new device to a user's account.
|
||||
absl::StatusOr<proto::UserWriteDeviceResponse> UserWriteDevice(
|
||||
const proto::UserWriteDeviceRequest& request) override;
|
||||
|
||||
// Deletes an existing device from a user's account.
|
||||
absl::StatusOr<proto::UserDeleteDeviceResponse> UserDeleteDevice(
|
||||
const proto::UserDeleteDeviceRequest& request) override;
|
||||
|
||||
private:
|
||||
enum class RequestType { kGet, kPost, kDelete };
|
||||
network::HttpRequestMethod GetRequestMethod(
|
||||
FastPairClientImpl::RequestType request_type) const;
|
||||
// Fetches access token for a logged in user.
|
||||
absl::StatusOr<std::string> GetAccessToken();
|
||||
network::HttpRequest CreateHttpRequest(
|
||||
std::optional<absl::string_view> access_token, network::Url url,
|
||||
RequestType request_type,
|
||||
std::optional<QueryParameters> request_as_query_parameters,
|
||||
std::optional<std::string> body);
|
||||
|
||||
auth::AuthenticationManager* authentication_manager_ = nullptr;
|
||||
AccountManager* account_manager_ = nullptr;
|
||||
std::unique_ptr<network::HttpClient> http_client_;
|
||||
FastPairHttpNotifier* notifier_ = nullptr;
|
||||
DeviceInfo* device_info_ = nullptr;
|
||||
};
|
||||
} // namespace fastpair
|
||||
} // namespace nearby
|
||||
|
||||
#endif // THIRD_PARTY_NEARBY_FASTPAIR_SERVER_ACCESS_FAST_PAIR_CLIENT_IMPL_H_
|
||||
@@ -0,0 +1,669 @@
|
||||
// Copyright 2023 Google LLC
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// https://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
#include "fastpair/server_access/fast_pair_client_impl.h"
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
#include <memory>
|
||||
#include <optional>
|
||||
#include <string>
|
||||
#include <utility>
|
||||
#include <vector>
|
||||
|
||||
#include "gmock/gmock.h"
|
||||
#include "protobuf-matchers/protocol-buffer-matchers.h"
|
||||
#include "gtest/gtest.h"
|
||||
#include "fastpair/common/fast_pair_device.h"
|
||||
#include "fastpair/common/fast_pair_prefs.h"
|
||||
#include "fastpair/common/fast_pair_switches.h"
|
||||
#include "fastpair/proto/data.proto.h"
|
||||
#include "fastpair/proto/enum.proto.h"
|
||||
#include "fastpair/proto/fast_pair_string.proto.h"
|
||||
#include "fastpair/proto/proto_builder.h"
|
||||
#include "fastpair/server_access/fast_pair_http_notifier.h"
|
||||
#include "internal/account/account_manager.h"
|
||||
#include "internal/account/fake_account_manager.h"
|
||||
#include "internal/auth/auth_status_util.h"
|
||||
#include "internal/network/http_client.h"
|
||||
#include "internal/network/http_client_factory.h"
|
||||
#include "internal/network/http_request.h"
|
||||
#include "internal/network/http_response.h"
|
||||
#include "internal/network/http_status_code.h"
|
||||
#include "internal/network/url.h"
|
||||
#include "internal/platform/task_runner_impl.h"
|
||||
#include "internal/test/fake_device_info.h"
|
||||
#include "internal/test/google3_only/fake_authentication_manager.h"
|
||||
|
||||
namespace nearby {
|
||||
namespace fastpair {
|
||||
namespace {
|
||||
|
||||
using ::nearby::network::HttpClient;
|
||||
using ::nearby::network::HttpRequest;
|
||||
using ::nearby::network::HttpRequestMethod;
|
||||
using ::nearby::network::HttpResponse;
|
||||
using ::nearby::network::HttpStatusCode;
|
||||
using ::nearby::network::Url;
|
||||
|
||||
constexpr char kHexModelId[] = "718C17";
|
||||
constexpr char kAccessToken[] = "access_token";
|
||||
constexpr char kTestAccountId[] = "test_account_id";
|
||||
constexpr char kFastPairPreferencesFilePath[] = "Google/Nearby/FastPair";
|
||||
constexpr char kDevicesPath[] = "device/";
|
||||
constexpr char kUserDevicesPath[] = "user/devices";
|
||||
constexpr char kUserDeleteDevicePath[] = "user/device";
|
||||
constexpr char kKey[] = "key";
|
||||
constexpr char kClientId[] = "AIzaSyBv7ZrOlX5oIJLVQrZh-WkZFKm5L6FlStQ";
|
||||
const char kQueryParameterAlternateOutputKey[] = "alt";
|
||||
const char kQueryParameterAlternateOutputProto[] = "proto";
|
||||
const char kPlatformTypeHeaderName[] = "X-FastPair-Platform-Type";
|
||||
constexpr char kWindowsPlatformType[] = "OSType.WINDOWS";
|
||||
constexpr char kMode[] = "mode";
|
||||
constexpr char kReleaseMode[] = "MODE_RELEASE";
|
||||
constexpr char kTestGoogleApisUrl[] =
|
||||
"https://nearbydevices-pa.testgoogleapis.com";
|
||||
constexpr char kBleAddress[] = "11::22::33::44::55::66";
|
||||
constexpr char kPublicAddress[] = "20:64:DE:40:F8:93";
|
||||
constexpr char kDisplayName[] = "Test Device";
|
||||
constexpr char kInitialPairingdescription[] = "InitialPairingdescription";
|
||||
constexpr char kAccountKey[] = "04b85786180add47fb81a04a8ce6b0de";
|
||||
constexpr char kExpectedSha256Hash[] =
|
||||
"6353c0075a35b7d81bb30a6190ab246da4b8c55a6111d387400579133c090ed8";
|
||||
|
||||
class MockHttpClient : public HttpClient {
|
||||
public:
|
||||
MOCK_METHOD(void, StartRequest,
|
||||
(const HttpRequest& request,
|
||||
std::function<void(const absl::StatusOr<HttpResponse>&)>),
|
||||
(override));
|
||||
MOCK_METHOD(void, StartCancellableRequest,
|
||||
(std::unique_ptr<CancellableRequest> request,
|
||||
std::function<void(const absl::StatusOr<HttpResponse>&)>),
|
||||
(override));
|
||||
MOCK_METHOD(absl::StatusOr<HttpResponse>, GetResponse, (const HttpRequest&),
|
||||
(override));
|
||||
};
|
||||
|
||||
// Return the values associated with |key|, or fail the test if |key| isn't in
|
||||
// |query_parameters|
|
||||
std::vector<std::string> ExpectQueryStringValues(
|
||||
const std::vector<std::pair<std::string, std::string>>& query_parameters,
|
||||
absl::string_view key) {
|
||||
std::vector<std::string> values;
|
||||
for (const auto& pair : query_parameters) {
|
||||
const auto& query_key = pair.first;
|
||||
const auto& query_value = pair.second;
|
||||
if (query_key == key) {
|
||||
values.push_back(query_value);
|
||||
}
|
||||
}
|
||||
EXPECT_GT(values.size(), 0);
|
||||
return values;
|
||||
}
|
||||
|
||||
// A gMock matcher to match proto values. Use this matcher like:
|
||||
// request/response proto, expected_proto;
|
||||
// EXPECT_THAT(proto, MatchesProto(expected_proto));
|
||||
MATCHER_P(MatchesProto, expected_proto,
|
||||
absl::StrCat(negation ? "does not match" : "matches",
|
||||
testing::PrintToString(expected_proto.SerializeAsString()))) {
|
||||
return arg.has_value() &&
|
||||
arg->SerializeAsString() == expected_proto.SerializeAsString();
|
||||
}
|
||||
|
||||
class FastPairClientImplTest : public ::testing::Test,
|
||||
public FastPairHttpNotifier::Observer {
|
||||
protected:
|
||||
FastPairClientImplTest() {
|
||||
preferences_manager_ = std::make_unique<preferences::PreferencesManager>(
|
||||
kFastPairPreferencesFilePath);
|
||||
authentication_manager_ = std::make_unique<FakeAuthenticationManager>();
|
||||
AccountManager::Account account;
|
||||
account.id = kTestAccountId;
|
||||
account_manager_ = std::make_unique<FakeAccountManager>(
|
||||
preferences_manager_.get(), prefs::kNearbyFastPairUsersName,
|
||||
authentication_manager_.get(), task_runner_.get());
|
||||
account_manager_->SetAccount(account);
|
||||
task_runner_ = std::make_unique<TaskRunnerImpl>(1);
|
||||
device_info_ = std::make_unique<FakeDeviceInfo>();
|
||||
}
|
||||
|
||||
void SetUp() override {
|
||||
GetAuthManager()->EnableSyncMode();
|
||||
auto http_client = std::make_unique<::testing::NiceMock<MockHttpClient>>();
|
||||
http_client_ =
|
||||
dynamic_cast<::testing::NiceMock<MockHttpClient>*>(http_client.get());
|
||||
switches::SetNearbyFastPairHttpHost(kTestGoogleApisUrl);
|
||||
fast_pair_client_ = std::make_unique<FastPairClientImpl>(
|
||||
authentication_manager_.get(), account_manager_.get(),
|
||||
std::move(http_client), ¬ifier_, device_info_.get());
|
||||
notifier_.AddObserver(this);
|
||||
}
|
||||
|
||||
void TearDown() override { notifier_.RemoveObserver(this); }
|
||||
|
||||
nearby::FakeAuthenticationManager* GetAuthManager() {
|
||||
return reinterpret_cast<nearby::FakeAuthenticationManager*>(
|
||||
authentication_manager_.get());
|
||||
}
|
||||
|
||||
// FastPairHttpNotifier::Observer:
|
||||
// Called when HTTP RPC is made for GetObservedDeviceRequest/Response
|
||||
void OnGetObservedDeviceRequest(
|
||||
const proto::GetObservedDeviceRequest& request) override {
|
||||
get_observer_device_request_ = request;
|
||||
}
|
||||
|
||||
void OnGetObservedDeviceResponse(
|
||||
const proto::GetObservedDeviceResponse& response) override {
|
||||
get_observer_device_response_ = response;
|
||||
}
|
||||
|
||||
// Called when HTTP RPC is made for UserReadDevicesRequest/Response
|
||||
void OnUserReadDevicesRequest(
|
||||
const proto::UserReadDevicesRequest& request) override {
|
||||
read_devices_request_ = request;
|
||||
}
|
||||
void OnUserReadDevicesResponse(
|
||||
const proto::UserReadDevicesResponse& response) override {
|
||||
read_devices_response_ = response;
|
||||
}
|
||||
|
||||
// Called when HTTP RPC is made for UserWriteDeviceRequest/Response
|
||||
void OnUserWriteDeviceRequest(
|
||||
const proto::UserWriteDeviceRequest& request) override {
|
||||
write_device_request_ = request;
|
||||
}
|
||||
void OnUserWriteDeviceResponse(
|
||||
const proto::UserWriteDeviceResponse& response) override {
|
||||
write_device_response_ = response;
|
||||
}
|
||||
|
||||
// Called when HTTP RPC is made for UserDeleteDeviceRequest/Response
|
||||
void OnUserDeleteDeviceRequest(
|
||||
const proto::UserDeleteDeviceRequest& request) override {
|
||||
delete_device_request_ = request;
|
||||
}
|
||||
void OnUserDeleteDeviceResponse(
|
||||
const proto::UserDeleteDeviceResponse& response) override {
|
||||
delete_device_response_ = response;
|
||||
}
|
||||
|
||||
Url GetUrl(absl::string_view path) {
|
||||
return Url::Create(absl::StrCat(kTestGoogleApisUrl, "/v1/", path)).value();
|
||||
}
|
||||
|
||||
// Requests/Responses
|
||||
std::optional<proto::GetObservedDeviceRequest> get_observer_device_request_;
|
||||
std::optional<proto::GetObservedDeviceResponse> get_observer_device_response_;
|
||||
std::optional<proto::UserReadDevicesRequest> read_devices_request_;
|
||||
std::optional<proto::UserReadDevicesResponse> read_devices_response_;
|
||||
std::optional<proto::UserWriteDeviceRequest> write_device_request_;
|
||||
std::optional<proto::UserWriteDeviceResponse> write_device_response_;
|
||||
std::optional<proto::UserDeleteDeviceRequest> delete_device_request_;
|
||||
std::optional<proto::UserDeleteDeviceResponse> delete_device_response_;
|
||||
|
||||
std::unique_ptr<preferences::PreferencesManager> preferences_manager_;
|
||||
std::unique_ptr<auth::AuthenticationManager> authentication_manager_;
|
||||
std::unique_ptr<FakeAccountManager> account_manager_;
|
||||
std::unique_ptr<FastPairClient> fast_pair_client_;
|
||||
std::unique_ptr<network::HttpClientFactory> http_client_factory_;
|
||||
std::unique_ptr<DeviceInfo> device_info_;
|
||||
std::unique_ptr<TaskRunner> task_runner_;
|
||||
::testing::NiceMock<MockHttpClient>* http_client_;
|
||||
FastPairHttpNotifier notifier_;
|
||||
};
|
||||
|
||||
TEST_F(FastPairClientImplTest, GetObservedDeviceSuccess) {
|
||||
// Sets up proto::GetObservedDeviceRequest
|
||||
proto::GetObservedDeviceRequest request_proto;
|
||||
int64_t device_id;
|
||||
CHECK(absl::SimpleHexAtoi(kHexModelId, &device_id));
|
||||
request_proto.set_device_id(device_id);
|
||||
request_proto.set_mode(proto::GetObservedDeviceRequest::MODE_RELEASE);
|
||||
|
||||
// Sets up proto::GetObservedDeviceResponse
|
||||
proto::GetObservedDeviceResponse response_proto;
|
||||
auto* device = response_proto.mutable_device();
|
||||
device->set_id(device_id);
|
||||
auto* observed_device_strings = response_proto.mutable_strings();
|
||||
observed_device_strings->set_initial_pairing_description(
|
||||
kInitialPairingdescription);
|
||||
|
||||
// Sets up HttpResponse
|
||||
HttpResponse http_response;
|
||||
http_response.SetStatusCode(nearby::network::HttpStatusCode::kHttpOk);
|
||||
http_response.SetBody(response_proto.SerializeAsString());
|
||||
|
||||
// Verifies HttpRequest is as expected
|
||||
EXPECT_CALL(*http_client_, GetResponse)
|
||||
.WillOnce([&](const HttpRequest& request) {
|
||||
EXPECT_EQ(request.GetMethod(), HttpRequestMethod::kGet);
|
||||
EXPECT_THAT(request.GetUrl().GetUrlPath(),
|
||||
::testing::HasSubstr(GetUrl(kDevicesPath).GetUrlPath()));
|
||||
|
||||
EXPECT_EQ(request.GetAllHeaders().find(kPlatformTypeHeaderName)->second,
|
||||
std::vector<std::string>{kWindowsPlatformType});
|
||||
EXPECT_EQ(
|
||||
ExpectQueryStringValues(request.GetAllQueryParameters(), kKey),
|
||||
std::vector<std::string>{kClientId});
|
||||
EXPECT_EQ(
|
||||
ExpectQueryStringValues(request.GetAllQueryParameters(),
|
||||
kQueryParameterAlternateOutputKey),
|
||||
std::vector<std::string>{kQueryParameterAlternateOutputProto});
|
||||
EXPECT_EQ(
|
||||
ExpectQueryStringValues(request.GetAllQueryParameters(), kMode),
|
||||
std::vector<std::string>{kReleaseMode});
|
||||
proto::GetObservedDeviceRequest expected_request;
|
||||
EXPECT_TRUE(expected_request.ParseFromString(
|
||||
request_proto.SerializeAsString()));
|
||||
EXPECT_EQ(expected_request.device_id(), device_id);
|
||||
EXPECT_EQ(expected_request.mode(),
|
||||
proto::GetObservedDeviceRequest::MODE_RELEASE);
|
||||
return http_response;
|
||||
});
|
||||
|
||||
absl::StatusOr<proto::GetObservedDeviceResponse> response =
|
||||
fast_pair_client_->GetObservedDevice(request_proto);
|
||||
|
||||
EXPECT_OK(response);
|
||||
|
||||
// Verifies proto::GetObservedDeviceRequest is as expected
|
||||
EXPECT_THAT(get_observer_device_request_, MatchesProto(request_proto));
|
||||
|
||||
// Verifies proto::GetObservedDeviceResponse is as expected
|
||||
EXPECT_THAT(get_observer_device_response_, MatchesProto(response_proto));
|
||||
EXPECT_EQ(response->device().id(), device_id);
|
||||
EXPECT_EQ(response->strings().initial_pairing_description(),
|
||||
kInitialPairingdescription);
|
||||
}
|
||||
|
||||
TEST_F(FastPairClientImplTest, GetObservedDeviceFailureWhenNoRespsone) {
|
||||
// Sets up proto::GetObservedDeviceRequest
|
||||
proto::GetObservedDeviceRequest request_proto;
|
||||
int64_t device_id;
|
||||
CHECK(absl::SimpleHexAtoi(kHexModelId, &device_id));
|
||||
request_proto.set_device_id(device_id);
|
||||
request_proto.set_mode(proto::GetObservedDeviceRequest::MODE_RELEASE);
|
||||
|
||||
// Verifies HttpRequest is as expected
|
||||
EXPECT_CALL(*http_client_, GetResponse)
|
||||
.WillOnce([&](const HttpRequest& request) {
|
||||
EXPECT_EQ(request.GetMethod(), HttpRequestMethod::kGet);
|
||||
EXPECT_THAT(request.GetUrl().GetUrlPath(),
|
||||
::testing::HasSubstr(GetUrl(kDevicesPath).GetUrlPath()));
|
||||
return absl::UnavailableError("");
|
||||
});
|
||||
|
||||
absl::StatusOr<proto::GetObservedDeviceResponse> response =
|
||||
fast_pair_client_->GetObservedDevice(request_proto);
|
||||
|
||||
EXPECT_FALSE(response.ok());
|
||||
EXPECT_TRUE(absl::IsUnavailable(response.status()));
|
||||
}
|
||||
|
||||
TEST_F(FastPairClientImplTest, GetObservedDeviceFailureWhenParseResponse) {
|
||||
EXPECT_CALL(*http_client_, GetResponse)
|
||||
.WillOnce([&](const HttpRequest& request) {
|
||||
EXPECT_EQ(request.GetMethod(), HttpRequestMethod::kGet);
|
||||
HttpResponse http_response;
|
||||
http_response.SetStatusCode(HttpStatusCode::kHttpOk);
|
||||
http_response.SetBody("Not a valid serialized response message.");
|
||||
return http_response;
|
||||
});
|
||||
|
||||
absl::StatusOr<proto::GetObservedDeviceResponse> response =
|
||||
fast_pair_client_->GetObservedDevice(proto::GetObservedDeviceRequest());
|
||||
EXPECT_TRUE(absl::IsInvalidArgument(response.status()));
|
||||
}
|
||||
|
||||
TEST_F(FastPairClientImplTest, UserReadDevicesSuccess) {
|
||||
// Sets up proto::UserReadDevicesRequest
|
||||
proto::UserReadDevicesRequest request_proto;
|
||||
|
||||
// Sets up proto::UserReadDevicesResponse
|
||||
proto::UserReadDevicesResponse response_proto;
|
||||
auto* fast_pair_info = response_proto.add_fast_pair_info();
|
||||
fast_pair_info->set_opt_in_status(proto::OptInStatus::OPT_IN_STATUS_OPTED_IN);
|
||||
|
||||
// Sets up HttpResponse
|
||||
HttpResponse http_response;
|
||||
http_response.SetStatusCode(nearby::network::HttpStatusCode::kHttpOk);
|
||||
http_response.SetBody(response_proto.SerializeAsString());
|
||||
|
||||
GetAuthManager()->SetFetchAccessTokenResult(auth::AuthStatus::SUCCESS,
|
||||
kAccessToken);
|
||||
|
||||
// Verifies HttpRequest is as expected
|
||||
EXPECT_CALL(*http_client_, GetResponse)
|
||||
.WillOnce([&](const HttpRequest& request) {
|
||||
EXPECT_EQ(request.GetMethod(), HttpRequestMethod::kGet);
|
||||
EXPECT_THAT(
|
||||
request.GetUrl().GetUrlPath(),
|
||||
::testing::HasSubstr(GetUrl(kUserDevicesPath).GetUrlPath()));
|
||||
return http_response;
|
||||
});
|
||||
|
||||
absl::StatusOr<proto::UserReadDevicesResponse> response =
|
||||
fast_pair_client_->UserReadDevices(request_proto);
|
||||
|
||||
EXPECT_OK(response);
|
||||
|
||||
// Verifies proto::UserReadDevicesRequest is as expected
|
||||
EXPECT_THAT(read_devices_request_, MatchesProto(request_proto));
|
||||
|
||||
// Verifies proto::UserReadDevicesResponse is as expected
|
||||
EXPECT_THAT(read_devices_response_, MatchesProto(response_proto));
|
||||
EXPECT_EQ(response->fast_pair_info().size(), 1);
|
||||
EXPECT_EQ(response->fast_pair_info().Get(0).opt_in_status(),
|
||||
proto::OptInStatus::OPT_IN_STATUS_OPTED_IN);
|
||||
}
|
||||
|
||||
TEST_F(FastPairClientImplTest, UserReadDevicesFailureWhenNoRespsone) {
|
||||
// Sets up proto::UserReadDevicesRequest
|
||||
proto::UserReadDevicesRequest request_proto;
|
||||
|
||||
GetAuthManager()->SetFetchAccessTokenResult(auth::AuthStatus::SUCCESS,
|
||||
kAccessToken);
|
||||
|
||||
// Verifies HttpRequest is as expected
|
||||
EXPECT_CALL(*http_client_, GetResponse)
|
||||
.WillOnce([&](const HttpRequest& request) {
|
||||
EXPECT_EQ(request.GetMethod(), HttpRequestMethod::kGet);
|
||||
EXPECT_THAT(
|
||||
request.GetUrl().GetUrlPath(),
|
||||
::testing::HasSubstr(GetUrl(kUserDevicesPath).GetUrlPath()));
|
||||
return absl::UnavailableError("");
|
||||
});
|
||||
|
||||
absl::StatusOr<proto::UserReadDevicesResponse> response =
|
||||
fast_pair_client_->UserReadDevices(request_proto);
|
||||
|
||||
EXPECT_FALSE(response.ok());
|
||||
EXPECT_TRUE(absl::IsUnavailable(response.status()));
|
||||
}
|
||||
|
||||
TEST_F(FastPairClientImplTest, UserReadDevicesFailureWhenNoLoginUser) {
|
||||
account_manager_->SetAccount(std::nullopt);
|
||||
|
||||
EXPECT_CALL(*http_client_, GetResponse).Times(0);
|
||||
absl::StatusOr<proto::UserReadDevicesResponse> response =
|
||||
fast_pair_client_->UserReadDevices(proto::UserReadDevicesRequest());
|
||||
EXPECT_FALSE(response.ok());
|
||||
}
|
||||
|
||||
TEST_F(FastPairClientImplTest, UserReadDevicesFailureWhenParseResponse) {
|
||||
EXPECT_CALL(*http_client_, GetResponse)
|
||||
.WillOnce([&](const HttpRequest& request) {
|
||||
EXPECT_EQ(request.GetMethod(), HttpRequestMethod::kGet);
|
||||
HttpResponse http_response;
|
||||
http_response.SetStatusCode(HttpStatusCode::kHttpOk);
|
||||
http_response.SetBody("Not a valid serialized response message.");
|
||||
return http_response;
|
||||
});
|
||||
|
||||
GetAuthManager()->SetFetchAccessTokenResult(auth::AuthStatus::SUCCESS,
|
||||
kAccessToken);
|
||||
absl::StatusOr<proto::UserReadDevicesResponse> response =
|
||||
fast_pair_client_->UserReadDevices(proto::UserReadDevicesRequest());
|
||||
EXPECT_TRUE(absl::IsInvalidArgument(response.status()));
|
||||
}
|
||||
|
||||
TEST_F(FastPairClientImplTest, UserWriteDeviceSuccess) {
|
||||
// Sets up proto::UserWriteDeviceRequest
|
||||
proto::UserWriteDeviceRequest request_proto;
|
||||
FastPairDevice device(kHexModelId, kBleAddress,
|
||||
Protocol::kFastPairInitialPairing);
|
||||
AccountKey account_key(absl::HexStringToBytes(kAccountKey));
|
||||
device.SetAccountKey(account_key);
|
||||
device.SetPublicAddress(kPublicAddress);
|
||||
device.SetDisplayName(kDisplayName);
|
||||
proto::GetObservedDeviceResponse get_observed_device_response;
|
||||
auto* observed_device_strings =
|
||||
get_observed_device_response.mutable_strings();
|
||||
observed_device_strings->set_initial_pairing_description(
|
||||
kInitialPairingdescription);
|
||||
DeviceMetadata device_metadata(get_observed_device_response);
|
||||
device.SetMetadata(device_metadata);
|
||||
auto* fast_pair_info = request_proto.mutable_fast_pair_info();
|
||||
BuildFastPairInfo(fast_pair_info, device);
|
||||
|
||||
// Sets up proto::UserWriteDeviceResponse
|
||||
proto::UserWriteDeviceResponse response_proto;
|
||||
|
||||
// Sets up HttpResponse
|
||||
HttpResponse http_response;
|
||||
http_response.SetStatusCode(nearby::network::HttpStatusCode::kHttpOk);
|
||||
http_response.SetBody(response_proto.SerializeAsString());
|
||||
|
||||
GetAuthManager()->SetFetchAccessTokenResult(auth::AuthStatus::SUCCESS,
|
||||
kAccessToken);
|
||||
|
||||
// Verifies HttpRequest is as expected
|
||||
EXPECT_CALL(*http_client_, GetResponse)
|
||||
.WillOnce([&](const HttpRequest& request) {
|
||||
EXPECT_EQ(request.GetMethod(), HttpRequestMethod::kPost);
|
||||
EXPECT_THAT(
|
||||
request.GetUrl().GetUrlPath(),
|
||||
::testing::HasSubstr(GetUrl(kUserDevicesPath).GetUrlPath()));
|
||||
proto::UserWriteDeviceRequest expected_request;
|
||||
EXPECT_TRUE(expected_request.ParseFromString(
|
||||
request_proto.SerializeAsString()));
|
||||
EXPECT_TRUE(expected_request.has_fast_pair_info());
|
||||
auto fast_proto_info = expected_request.fast_pair_info();
|
||||
EXPECT_TRUE(fast_proto_info.has_device());
|
||||
auto device = fast_proto_info.device();
|
||||
EXPECT_EQ(device.account_key(), account_key.GetAsBytes());
|
||||
EXPECT_EQ(
|
||||
absl::BytesToHexString(device.sha256_account_key_public_address()),
|
||||
kExpectedSha256Hash);
|
||||
proto::StoredDiscoveryItem stored_discovery_item;
|
||||
EXPECT_TRUE(stored_discovery_item.ParseFromString(
|
||||
device.discovery_item_bytes()));
|
||||
EXPECT_EQ(stored_discovery_item.title(), kDisplayName);
|
||||
proto::FastPairStrings fast_pair_strings =
|
||||
stored_discovery_item.fast_pair_strings();
|
||||
EXPECT_EQ(fast_pair_strings.initial_pairing_description(),
|
||||
kInitialPairingdescription);
|
||||
return http_response;
|
||||
});
|
||||
|
||||
absl::StatusOr<proto::UserWriteDeviceResponse> response =
|
||||
fast_pair_client_->UserWriteDevice(request_proto);
|
||||
|
||||
EXPECT_OK(response);
|
||||
|
||||
// Verifies proto::UserWriteDeviceRequest is as expected
|
||||
EXPECT_THAT(write_device_request_, MatchesProto(request_proto));
|
||||
|
||||
// Verifies proto::UserWriteDeviceResponse is as expected
|
||||
EXPECT_THAT(write_device_response_, MatchesProto(response_proto));
|
||||
}
|
||||
|
||||
TEST_F(FastPairClientImplTest, UserWriteDeviceFailureWhenNoRespsone) {
|
||||
// Sets up proto::UserWriteDeviceRequest
|
||||
proto::UserWriteDeviceRequest request_proto;
|
||||
|
||||
GetAuthManager()->SetFetchAccessTokenResult(auth::AuthStatus::SUCCESS,
|
||||
kAccessToken);
|
||||
|
||||
// Verifies HttpRequest is as expected
|
||||
EXPECT_CALL(*http_client_, GetResponse)
|
||||
.WillOnce([&](const HttpRequest& request) {
|
||||
EXPECT_EQ(request.GetMethod(), HttpRequestMethod::kPost);
|
||||
EXPECT_THAT(
|
||||
request.GetUrl().GetUrlPath(),
|
||||
::testing::HasSubstr(GetUrl(kUserDevicesPath).GetUrlPath()));
|
||||
return absl::UnavailableError("");
|
||||
});
|
||||
|
||||
absl::StatusOr<proto::UserWriteDeviceResponse> response =
|
||||
fast_pair_client_->UserWriteDevice(request_proto);
|
||||
|
||||
EXPECT_FALSE(response.ok());
|
||||
EXPECT_TRUE(absl::IsUnavailable(response.status()));
|
||||
}
|
||||
|
||||
TEST_F(FastPairClientImplTest, UserWriteDeviceFailureWhenNoLoginUser) {
|
||||
account_manager_->SetAccount(std::nullopt);
|
||||
|
||||
EXPECT_CALL(*http_client_, GetResponse).Times(0);
|
||||
absl::StatusOr<proto::UserWriteDeviceResponse> response =
|
||||
fast_pair_client_->UserWriteDevice(proto::UserWriteDeviceRequest());
|
||||
EXPECT_FALSE(response.ok());
|
||||
}
|
||||
|
||||
TEST_F(FastPairClientImplTest, UserWriteDeviceFailureWhenParseResponse) {
|
||||
EXPECT_CALL(*http_client_, GetResponse)
|
||||
.WillOnce([&](const HttpRequest& request) {
|
||||
EXPECT_EQ(request.GetMethod(), HttpRequestMethod::kPost);
|
||||
HttpResponse http_response;
|
||||
http_response.SetStatusCode(HttpStatusCode::kHttpOk);
|
||||
http_response.SetBody("Not a valid serialized response message.");
|
||||
return http_response;
|
||||
});
|
||||
|
||||
GetAuthManager()->SetFetchAccessTokenResult(auth::AuthStatus::SUCCESS,
|
||||
kAccessToken);
|
||||
absl::StatusOr<proto::UserWriteDeviceResponse> response =
|
||||
fast_pair_client_->UserWriteDevice(proto::UserWriteDeviceRequest());
|
||||
EXPECT_TRUE(absl::IsInvalidArgument(response.status()));
|
||||
}
|
||||
|
||||
TEST_F(FastPairClientImplTest, UserDeleteDeviceSuccess) {
|
||||
// Sets up proto::UserDeleteDeviceRequest
|
||||
std::string hex_account_key = kAccountKey;
|
||||
absl::AsciiStrToUpper(&hex_account_key);
|
||||
proto::UserDeleteDeviceRequest request_proto;
|
||||
request_proto.set_hex_account_key(hex_account_key);
|
||||
|
||||
// Sets up proto::UserDeleteDeviceResponse
|
||||
proto::UserDeleteDeviceResponse response_proto;
|
||||
|
||||
// Sets up HttpResponse
|
||||
HttpResponse http_response;
|
||||
http_response.SetStatusCode(nearby::network::HttpStatusCode::kHttpOk);
|
||||
http_response.SetBody(response_proto.SerializeAsString());
|
||||
|
||||
GetAuthManager()->SetFetchAccessTokenResult(auth::AuthStatus::SUCCESS,
|
||||
kAccessToken);
|
||||
|
||||
// Verifies HttpRequest is as expected
|
||||
EXPECT_CALL(*http_client_, GetResponse)
|
||||
.WillOnce([&](const HttpRequest& request) {
|
||||
EXPECT_EQ(request.GetMethod(), HttpRequestMethod::kDelete);
|
||||
EXPECT_THAT(
|
||||
request.GetUrl().GetUrlPath(),
|
||||
::testing::HasSubstr(GetUrl(kUserDeleteDevicePath).GetUrlPath()));
|
||||
proto::UserDeleteDeviceRequest expected_request;
|
||||
EXPECT_TRUE(expected_request.ParseFromString(
|
||||
request_proto.SerializeAsString()));
|
||||
EXPECT_EQ(expected_request.hex_account_key(), hex_account_key);
|
||||
return http_response;
|
||||
});
|
||||
|
||||
absl::StatusOr<proto::UserDeleteDeviceResponse> response =
|
||||
fast_pair_client_->UserDeleteDevice(request_proto);
|
||||
|
||||
EXPECT_OK(response);
|
||||
|
||||
// Verifies proto::UserDeleteDeviceRequest is as expected
|
||||
EXPECT_THAT(delete_device_request_, MatchesProto(request_proto));
|
||||
|
||||
// Verifies proto::UserDeleteDeviceResponse is as expected
|
||||
EXPECT_THAT(delete_device_response_, MatchesProto(response_proto));
|
||||
}
|
||||
|
||||
TEST_F(FastPairClientImplTest, UserDeleteDeviceFailureWhenNoRespsone) {
|
||||
// Sets up proto::UserDeleteDeviceRequest
|
||||
proto::UserDeleteDeviceRequest request_proto;
|
||||
|
||||
GetAuthManager()->SetFetchAccessTokenResult(auth::AuthStatus::SUCCESS,
|
||||
kAccessToken);
|
||||
|
||||
// Verifies HttpRequest is as expected
|
||||
EXPECT_CALL(*http_client_, GetResponse)
|
||||
.WillOnce([&](const HttpRequest& request) {
|
||||
EXPECT_EQ(request.GetMethod(), HttpRequestMethod::kDelete);
|
||||
EXPECT_THAT(
|
||||
request.GetUrl().GetUrlPath(),
|
||||
::testing::HasSubstr(GetUrl(kUserDeleteDevicePath).GetUrlPath()));
|
||||
return absl::UnavailableError("");
|
||||
});
|
||||
|
||||
absl::StatusOr<proto::UserDeleteDeviceResponse> response =
|
||||
fast_pair_client_->UserDeleteDevice(request_proto);
|
||||
|
||||
EXPECT_FALSE(response.ok());
|
||||
EXPECT_TRUE(absl::IsUnavailable(response.status()));
|
||||
}
|
||||
|
||||
TEST_F(FastPairClientImplTest, UserDeleteDeviceFailureWhenNoLoginUser) {
|
||||
account_manager_->SetAccount(std::nullopt);
|
||||
|
||||
EXPECT_CALL(*http_client_, GetResponse).Times(0);
|
||||
absl::StatusOr<proto::UserDeleteDeviceResponse> response =
|
||||
fast_pair_client_->UserDeleteDevice(proto::UserDeleteDeviceRequest());
|
||||
EXPECT_FALSE(response.ok());
|
||||
}
|
||||
|
||||
TEST_F(FastPairClientImplTest, UserDeleteDeviceFailureWhenParseResponse) {
|
||||
EXPECT_CALL(*http_client_, GetResponse)
|
||||
.WillOnce([&](const HttpRequest& request) {
|
||||
EXPECT_EQ(request.GetMethod(), HttpRequestMethod::kDelete);
|
||||
HttpResponse http_response;
|
||||
http_response.SetStatusCode(HttpStatusCode::kHttpOk);
|
||||
http_response.SetBody("Not a valid serialized response message.");
|
||||
return http_response;
|
||||
});
|
||||
|
||||
GetAuthManager()->SetFetchAccessTokenResult(auth::AuthStatus::SUCCESS,
|
||||
kAccessToken);
|
||||
absl::StatusOr<proto::UserDeleteDeviceResponse> response =
|
||||
fast_pair_client_->UserDeleteDevice(proto::UserDeleteDeviceRequest());
|
||||
EXPECT_TRUE(absl::IsInvalidArgument(response.status()));
|
||||
}
|
||||
|
||||
TEST_F(FastPairClientImplTest, FetchAccessTokenFailure) {
|
||||
EXPECT_CALL(*http_client_, GetResponse).Times(0);
|
||||
|
||||
GetAuthManager()->SetFetchAccessTokenResult(
|
||||
auth::AuthStatus::PERMISSION_DENIED, std::nullopt);
|
||||
absl::StatusOr<proto::UserReadDevicesResponse> response =
|
||||
fast_pair_client_->UserReadDevices(proto::UserReadDevicesRequest());
|
||||
|
||||
EXPECT_TRUE(absl::IsUnauthenticated(response.status()));
|
||||
}
|
||||
|
||||
TEST_F(FastPairClientImplTest, ParseResponseProtoFailure) {
|
||||
EXPECT_CALL(*http_client_, GetResponse)
|
||||
.WillOnce([&](const HttpRequest& request) {
|
||||
EXPECT_EQ(request.GetMethod(), HttpRequestMethod::kGet);
|
||||
HttpResponse http_response;
|
||||
http_response.SetStatusCode(HttpStatusCode::kHttpOk);
|
||||
http_response.SetBody("Not a valid serialized response message.");
|
||||
return http_response;
|
||||
});
|
||||
|
||||
GetAuthManager()->SetFetchAccessTokenResult(auth::AuthStatus::SUCCESS,
|
||||
kAccessToken);
|
||||
absl::StatusOr<proto::UserReadDevicesResponse> response =
|
||||
fast_pair_client_->UserReadDevices(proto::UserReadDevicesRequest());
|
||||
EXPECT_TRUE(absl::IsInvalidArgument(response.status()));
|
||||
}
|
||||
|
||||
} // namespace
|
||||
} // namespace fastpair
|
||||
} // namespace nearby
|
||||
Reference in New Issue
Block a user