blob: f645586d4f9c4dc8c8de73e39363513edbc80646 [file] [log] [blame]
// Copyright 2024 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#ifndef COMPONENTS_COMMERCE_CORE_COMPARE_CLUSTER_MANAGER_H_
#define COMPONENTS_COMMERCE_CORE_COMPARE_CLUSTER_MANAGER_H_
#include <map>
#include <memory>
#include <set>
#include <vector>
#include "base/memory/weak_ptr.h"
#include "base/observer_list.h"
#include "base/observer_list_types.h"
#include "base/scoped_observation.h"
#include "base/uuid.h"
#include "components/commerce/core/commerce_types.h"
#include "components/commerce/core/product_specifications/product_specifications_set.h"
#include "url/gurl.h"
namespace commerce {
class ClusterServerProxy;
class ProductSpecificationsService;
struct CandidateProduct;
struct ProductGroup;
// Class for clustering product information.
class ClusterManager : public ProductSpecificationsSet::Observer {
public:
using GetProductInfoCallback =
base::RepeatingCallback<void(const GURL&, ProductInfoCallback)>;
using GetProductInfoBatchCallback =
base::RepeatingCallback<void(const std::vector<GURL>& urls,
ProductInfoBatchCallback)>;
using GetOpenUrlInfosCallback =
base::RepeatingCallback<const std::vector<UrlInfo>()>;
using GetEntryPointInfoCallback =
base::OnceCallback<void(std::optional<EntryPointInfo>)>;
class Observer : public base::CheckedObserver {
public:
// Notifies that ClusterManager has finished clustering for a recent
// navigation with `url`.
virtual void OnClusterFinishedForNavigation(const GURL& url) {}
};
ClusterManager(ProductSpecificationsService* product_specification_service,
std::unique_ptr<ClusterServerProxy> cluster_server_proxy,
const GetProductInfoCallback& get_product_info_cb,
const GetProductInfoBatchCallback& get_product_info_batch_cb,
const GetOpenUrlInfosCallback& get_open_url_infos_cb);
~ClusterManager() override;
ClusterManager(const ClusterManager&) = delete;
ClusterManager& operator=(const ClusterManager&) = delete;
// ProductSpecificationsSet::Observe Implementation.
void OnProductSpecificationsSetAdded(
const ProductSpecificationsSet& product_specifications_set) override;
void OnProductSpecificationsSetUpdate(
const ProductSpecificationsSet& before,
const ProductSpecificationsSet& product_specifications_set) override;
void OnProductSpecificationsSetRemoved(
const ProductSpecificationsSet& set) override;
// A notification that a WebWrapper with `url` has been destroyed. This
// signals that the web page backing the provided WebWrapper is about to be
// destroyed. Typically corresponds to a user closing a tab.
void WebWrapperDestroyed(const GURL& url);
// A notification that a web wrapper with `url` finished a navigation in the
// primary main frame.
void DidNavigatePrimaryMainFrame(const GURL& url);
// A notification that the user navigated away from `from_url`.
void DidNavigateAway(const GURL& from_url);
// Gets a product group that the given product can be clustered into. If
// this candidate product is already in a product group, empty result
// is returned.
virtual std::optional<ProductGroup> GetProductGroupForCandidateProduct(
const GURL& product_url);
// Gets information to decide if entry point should show on navivation to
// `url` and return it. The returned EntryPointInfo will include `url`
// if it can be clustered into a group.
virtual void GetEntryPointInfoForNavigation(
const GURL& url,
GetEntryPointInfoCallback callback);
// Gets information to decide if entry point should show on selection and
// return it. `old_url` is the URL of the tab before selection.
// `new_url` is the URL of the tab after selection.
virtual void GetEntryPointInfoForSelection(
const GURL& old_url,
const GURL& new_url,
GetEntryPointInfoCallback callback);
// Finds similar candidate products for a product group.
std::vector<GURL> FindSimilarCandidateProductsForProductGroup(
const base::Uuid& uuid);
// Finds comparable products from an EntryPointInfo.
virtual void GetComparableProducts(const EntryPointInfo& entry_point_info,
GetEntryPointInfoCallback callback);
// Registers an observer for cluster manager.
void AddObserver(Observer* observer);
// Removes an observer for cluster manager.
void RemoveObserver(Observer* observer);
private:
friend class ClusterManagerTest;
void OnGetAllProductSpecificationsSets(
const std::vector<ProductSpecificationsSet> sets);
// Called when information about a product is retrieved.
void OnProductInfoRetrieved(
const GURL& url,
const std::optional<const ProductInfo>& product_info);
// Called when category data for a list of URLs are retrieved.
void OnAllCategoryDataRetrieved(
const base::Uuid& uuid,
const std::set<GURL>& urls,
const std::vector<CategoryData>& category_data);
// Adds a candidate product to `candidate_product_map_`.
void AddCandidateProduct(
const GURL& url,
const std::optional<const ProductInfo>& product_info);
// Removes a candidate product URL if it is not open in any tabs.
void RemoveCandidateProductURLIfNotOpen(const GURL& url);
// Finds similar candidate products for a candidate product. The returned
// URLs doesn't include the `product_url`.
std::set<GURL> FindSimilarCandidateProducts(const GURL& product_url);
void OnGetComparableProducts(
const EntryPointInfo& entry_point_info,
GetEntryPointInfoCallback callback,
const std::vector<uint64_t>& cluster_product_ids);
void OnProductInfoFetchedForSimilarUrls(
GetEntryPointInfoCallback callback,
const std::map<GURL, std::optional<ProductInfo>> product_infos);
// Check if product set is still eligible for clustering recommendations given
// its uuid and last updated time. Please note that this method can be used
// for both `ProductSpecificationSet` and `ProductGroup`.
bool IsSetEligibleForClustering(const base::Uuid& uuid,
const base::Time& update_time);
// A ProductGroup might become ineligible for clustering because it hasn't
// been updated for a long time. This method will be scheduled to run every
// day after ClusterManager is constructed to remove the ineiligible
// ProductGroups.
//
// Please note that this doesn't mean the underlying ProductSpecificationsSet
// will be removed; only the ProductGroup is removed in ClusterManager so that
// we'll stop making clustering recommendations for these ProductGroups.
void RemoveIneligibleGroupsForClustering();
std::unique_ptr<ClusterServerProxy> cluster_server_proxy_;
// Callback to get product info.
GetProductInfoCallback get_product_info_cb_;
// Callback to get product info in batches.
GetProductInfoBatchCallback get_product_info_batch_cb_;
// Callback to get currently opened urls.
GetOpenUrlInfosCallback get_open_url_infos_cb_;
// A map storing info of existing product groups, keyed by product group ID.
std::map<base::Uuid, std::unique_ptr<ProductGroup>> product_group_map_;
// A map storing info of candidate products, keyed by product page URL.
std::map<GURL, std::unique_ptr<CandidateProduct>> candidate_product_map_;
base::ScopedObservation<ProductSpecificationsService,
ProductSpecificationsSet::Observer>
obs_{this};
base::ObserverList<Observer> observers_;
base::WeakPtrFactory<ClusterManager> weak_ptr_factory_{this};
};
} // namespace commerce
#endif // COMPONENTS_COMMERCE_CORE_COMPARE_CLUSTER_MANAGER_H_