1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193
|
// 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_
|