blob: c58c4c2c12c97db8db189b6c493f39cf281ad2f3 [file] [log] [blame]
// Copyright 2022 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#ifndef CHROME_BROWSER_APPS_ALMANAC_API_CLIENT_ALMANAC_API_UTIL_H_
#define CHROME_BROWSER_APPS_ALMANAC_API_CLIENT_ALMANAC_API_UTIL_H_
#include <memory>
#include <optional>
#include <string>
#include <string_view>
#include "base/functional/callback.h"
#include "base/functional/callback_forward.h"
#include "base/memory/scoped_refptr.h"
#include "base/types/expected.h"
#include "chrome/browser/apps/almanac_api_client/device_info_manager.h"
#include "chrome/browser/apps/almanac_api_client/device_info_manager_factory.h"
#include "chrome/browser/profiles/profile.h"
#include "net/traffic_annotation/network_traffic_annotation.h"
#include "services/network/public/mojom/url_response_head.mojom-forward.h"
#include "third_party/abseil-cpp/absl/status/status.h"
class GURL;
namespace network {
class SharedURLLoaderFactory;
} // namespace network
namespace apps {
// Returns the base URL (scheme, host and port) for the ChromeOS
// Almanac API. This can be overridden with the command-line switch
// --almanac-api-url.
std::string GetAlmanacApiUrl();
// Returns the URL for the specified endpoint for the ChromeOS Almanac
// API. An endpoint suffix is e.g. "v1/app-preload".
GURL GetAlmanacEndpointUrl(std::string_view endpoint_suffix);
// Overrides the Almanac endpoint URL for testing.
void SetAlmanacEndpointUrlForTesting(std::optional<std::string> url_override);
struct QueryError {
enum Type {
kConnectionError,
kBadRequest,
kBadResponse,
} type;
std::string message;
bool operator==(const QueryError& other) const;
};
std::ostream& operator<<(std::ostream& out, const QueryError& error);
namespace internal {
void QueryAlmanacApiRaw(
scoped_refptr<network::SharedURLLoaderFactory> url_loader_factory,
const net::NetworkTrafficAnnotationTag& traffic_annotation,
std::string_view endpoint_suffix,
int max_response_size,
std::optional<std::string> error_histogram_name,
base::OnceCallback<void(base::expected<std::string, QueryError>)> callback,
const std::string& request_body);
template <typename T>
base::expected<T, QueryError> ParseResponse(
base::expected<std::string, QueryError> string_response) {
if (!string_response.has_value()) {
return base::unexpected(std::move(string_response).error());
}
T result;
if (result.ParseFromString(*string_response)) {
return base::ok(result);
}
return base::unexpected(QueryError{
QueryError::kBadResponse,
"Parsing failed",
});
}
} // namespace internal
// Sends a network fetch to the `endpoint_suffix` of the Almanac server, with a
// client context attached, and calls `callback` with the result.
//
// `partial_request` is a instance of a RequestProto message for an Almanac
// endpoint which has all non-context fields filled. This method will attach a
// device and user context, serialize the proto, and attach it to the network
// request. RequestProto must have fields for the device_context and
// user_context, e.g.:
//
// optional ClientDeviceContext device_context = 1;
// optional ClientUserContext user_context = 2;
//
// Any response from the server will be parsed into a ResponseProto and
// returned. If any HTTP or other failure occurs, a QueryError is returned
// instead.
//
// Adds HTTP response codes to UMA if an `error_histogram_name` is specified.
// The histogram must be defined in a histograms.xml file using enum
// CombinedHttpResponseAndNetErrorCode.
template <typename RequestProto, typename ResponseProto>
void QueryAlmanacApiWithContext(
Profile* profile,
std::string_view endpoint_suffix,
RequestProto partial_request,
const net::NetworkTrafficAnnotationTag& traffic_annotation,
int max_response_size,
std::optional<std::string> error_histogram_name,
base::OnceCallback<void(base::expected<ResponseProto, QueryError>)>
callback) {
DeviceInfoManager* manager = DeviceInfoManagerFactory::GetForProfile(profile);
if (!manager) {
std::move(callback).Run(base::unexpected(
QueryError{QueryError::kBadRequest,
"Unavailable for incognito and system profiles"}));
}
base::OnceCallback<void(const std::string&)> query_with_request_body =
base::BindOnce(&internal::QueryAlmanacApiRaw,
profile->GetURLLoaderFactory(), traffic_annotation,
endpoint_suffix, max_response_size, error_histogram_name,
base::BindOnce(&internal::ParseResponse<ResponseProto>)
.Then(std::move(callback)));
manager->GetDeviceInfo(base::BindOnce(
[](RequestProto partial_request, DeviceInfo info) {
*partial_request.mutable_device_context() =
info.ToDeviceContext();
*partial_request.mutable_user_context() =
info.ToUserContext();
return partial_request.SerializeAsString();
},
std::move(partial_request))
.Then(std::move(query_with_request_body)));
}
// Sends a network fetch to the `endpoint_suffix` of the Almanac server with
// `request_body` and returns the T proto response or QueryError if there was a
// failure.
// Adds HTTP response codes to UMA if a histogram name is specified. The
// histogram must be defined in apps/histograms.xml using enum
// CombinedHttpResponseAndNetErrorCode.
template <typename T>
void QueryAlmanacApi(
scoped_refptr<network::SharedURLLoaderFactory> url_loader_factory,
const net::NetworkTrafficAnnotationTag& traffic_annotation,
const std::string& request_body,
std::string_view endpoint_suffix,
int max_response_size,
std::optional<std::string> error_histogram_name,
base::OnceCallback<void(base::expected<T, QueryError>)> callback) {
internal::QueryAlmanacApiRaw(
url_loader_factory, traffic_annotation, endpoint_suffix,
max_response_size, error_histogram_name,
base::BindOnce(&internal::ParseResponse<T>).Then(std::move(callback)),
request_body);
}
} // namespace apps
#endif // CHROME_BROWSER_APPS_ALMANAC_API_CLIENT_ALMANAC_API_UTIL_H_