chromium/chrome/browser/apps/almanac_api_client/almanac_api_util.h

// 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/types/expected.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 {
namespace mojom {
class URLLoaderFactory;
}
}  // 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(
    network::mojom::URLLoaderFactory& 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<std::string, QueryError>)> callback);

}  // namespace internal

// 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(
    network::mojom::URLLoaderFactory& 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, request_body, endpoint_suffix,
      max_response_size, error_histogram_name,
      base::BindOnce([](base::expected<std::string, QueryError> string_response)
                         -> base::expected<T, QueryError> {
        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",
        });
      }).Then(std::move(callback)));
}

}  // namespace apps

#endif  // CHROME_BROWSER_APPS_ALMANAC_API_CLIENT_ALMANAC_API_UTIL_H_