Commit 07168912 authored by Hesen Zhang's avatar Hesen Zhang Committed by Commit Bot

[Upboarding]: QueryTile tile group manager implementation.

- Setup the tile group manager component.
- Implemented the load, save and get APIs.
- Added unittests.

- [UPDATED] Have a big update since initial reviews.

Bug: 1068368
Change-Id: I03ebf98f9c67af403d355418cb1304f161bb28e1
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/2138161
Auto-Submit: Hesen Zhang <hesen@chromium.org>
Commit-Queue: Hesen Zhang <hesen@chromium.org>
Reviewed-by: default avatarShakti Sahu <shaktisahu@chromium.org>
Reviewed-by: default avatarXing Liu <xingliu@chromium.org>
Reviewed-by: default avatarDavid Trainor <dtrainor@chromium.org>
Cr-Commit-Position: refs/heads/master@{#761153}
parent de53ef75
......@@ -30,6 +30,8 @@ source_set("internal") {
"tile_group.h",
"tile_info_fetcher.cc",
"tile_info_fetcher.h",
"tile_manager.cc",
"tile_manager.h",
"tile_service_impl.cc",
"tile_service_impl.h",
]
......@@ -59,6 +61,7 @@ source_set("unit_tests") {
"query_tile_store_unittest.cc",
"tile_group_unittest.cc",
"tile_info_fetcher_unittest.cc",
"tile_manager_unittest.cc",
]
deps = [
......
......@@ -17,6 +17,13 @@ constexpr char kDefaultGetQueryTilePath[] = "/v1/querytiles";
// Default state of QueryTile feature.
constexpr bool kDefaultQueryTileState = false;
// Default expire duration.
constexpr base::TimeDelta kDefaultExpireDuration =
base::TimeDelta::FromHours(48);
// Default locale string.
constexpr char kDefaultLocale[] = "en-US";
const GURL BuildGetQueryTileURL(const GURL& base_url, const char* path) {
GURL::Replacements replacements;
replacements.SetPathStr(path);
......@@ -38,7 +45,9 @@ QueryTilesConfig::QueryTilesConfig()
: is_enabled(kDefaultQueryTileState),
base_url(GURL(kDefaultBaseURL)),
get_query_tile_url(
BuildGetQueryTileURL(base_url, kDefaultGetQueryTilePath)) {}
BuildGetQueryTileURL(base_url, kDefaultGetQueryTilePath)),
expire_duration(kDefaultExpireDuration),
locale(kDefaultLocale) {}
QueryTilesConfig::~QueryTilesConfig() = default;
......
......@@ -9,6 +9,7 @@
#include <string>
#include <utility>
#include "base/time/time.h"
#include "url/gurl.h"
namespace upboarding {
......@@ -33,6 +34,12 @@ struct QueryTilesConfig {
// The URL for GetQueryTiles RPC.
GURL get_query_tile_url;
// The maximum duration for holding current group's info and images.
base::TimeDelta expire_duration;
// Locale setting from operating system.
std::string locale;
};
} // namespace upboarding
......
......@@ -16,4 +16,17 @@ enum class TileInfoRequestStatus {
kMaxValue = kFailure,
};
enum class TileGroupStatus {
// No errors happen in tile group manager.
kSuccess = 0,
// Database and manager component is not fully initialized.
kUninitialized = 1,
// Db operations failed.
kFailureDbOperation = 2,
// The group status is invalid, reason could be expired or locale not match.
kInvalidGroup = 3,
// Max value.
kMaxValue = kInvalidGroup,
};
#endif // CHROME_BROWSER_UPBOARDING_QUERY_TILES_INTERNAL_QUERY_TILE_TYPES_H_
// Copyright 2020 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include <map>
#include <string>
#include <utility>
#include "base/bind.h"
#include "base/guid.h"
#include "base/memory/weak_ptr.h"
#include "base/time/time.h"
#include "chrome/browser/upboarding/query_tiles/internal/tile_manager.h"
namespace upboarding {
namespace {
class TileManagerImpl : public TileManager {
public:
TileManagerImpl(std::unique_ptr<TileStore> store,
base::Clock* clock,
QueryTilesConfig* config)
: initialized_(false),
store_(std::move(store)),
clock_(clock),
config_(config) {}
TileManagerImpl(const TileManagerImpl& other) = delete;
TileManagerImpl& operator=(const TileManagerImpl& other) = delete;
private:
// TileManager implementation.
void Init(TileGroupStatusCallback callback) override {
store_->InitAndLoad(base::BindOnce(&TileManagerImpl::OnTileStoreInitialized,
weak_ptr_factory_.GetWeakPtr(),
std::move(callback)));
}
void SaveTiles(std::vector<std::unique_ptr<QueryTileEntry>> top_level_tiles,
TileGroupStatusCallback callback) override {
if (!initialized_) {
std::move(callback).Run(TileGroupStatus::kUninitialized);
return;
}
auto group = std::make_unique<TileGroup>();
group->id = base::GenerateGUID();
group->last_updated_ts = clock_->Now();
group->locale = config_->locale;
group->tiles = std::move(top_level_tiles);
store_->Update(group->id, *group.get(),
base::BindOnce(&TileManagerImpl::OnGroupSaved,
weak_ptr_factory_.GetWeakPtr(),
std::move(group), std::move(callback)));
}
void GetTiles(std::vector<QueryTileEntry*>* tiles) override {
DCHECK(tiles);
if (!initialized_)
return;
tiles->clear();
if (tile_group_ && ValidateGroup(tile_group_.get())) {
for (const auto& tile : tile_group_->tiles)
tiles->emplace_back(tile.get());
}
}
void OnTileStoreInitialized(
TileGroupStatusCallback callback,
bool success,
std::map<std::string, std::unique_ptr<TileGroup>> loaded_group) {
if (!success) {
std::move(callback).Run(TileGroupStatus::kFailureDbOperation);
return;
}
initialized_ = true;
PruneInvalidGroup(std::move(callback), std::move(loaded_group));
}
// Filters out and deletes invalid groups from db and memory.
void PruneInvalidGroup(
TileGroupStatusCallback callback,
std::map<std::string, std::unique_ptr<TileGroup>> loaded_group) {
DCHECK_LE(loaded_group.size(), 1u);
TileGroupStatus status = TileGroupStatus::kSuccess;
std::vector<std::string> to_deprecated_in_db;
for (const auto& pair : loaded_group) {
auto group_id = pair.first;
auto* group = pair.second.get();
if (!ValidateGroup(group)) {
status = TileGroupStatus::kInvalidGroup;
to_deprecated_in_db.emplace_back(group_id);
}
}
for (const auto& key : to_deprecated_in_db) {
DeleteGroup(key);
loaded_group.erase(key);
}
// Moves the valid group into in memory holder.
if (!loaded_group.empty())
std::swap(tile_group_, loaded_group.begin()->second);
std::move(callback).Run(status);
}
// Returns true if the group is not expired and the locale matches OS setting.
bool ValidateGroup(const TileGroup* group) const {
return clock_->Now() - group->last_updated_ts < config_->expire_duration &&
group->locale == config_->locale;
}
void OnGroupSaved(std::unique_ptr<TileGroup> group,
TileGroupStatusCallback callback,
bool success) {
if (!success) {
std::move(callback).Run(TileGroupStatus::kFailureDbOperation);
return;
}
if (tile_group_)
DeleteGroup(tile_group_->id);
std::swap(tile_group_, group);
std::move(callback).Run(TileGroupStatus::kSuccess);
}
void DeleteGroup(const std::string& key) {
store_->Delete(key, base::BindOnce(&TileManagerImpl::OnGroupDeleted,
weak_ptr_factory_.GetWeakPtr()));
}
void OnGroupDeleted(bool success) {
// TODO(hesen): Record db operation metrics.
NOTIMPLEMENTED();
}
// Indicates if the db is fully initialized, rejects calls if not.
bool initialized_;
// Storage layer of query tiles.
std::unique_ptr<TileStore> store_;
// The tile group in-memory holder.
std::unique_ptr<TileGroup> tile_group_;
// Clock object.
base::Clock* clock_;
// QueryTileConfig object.
QueryTilesConfig* config_;
base::WeakPtrFactory<TileManagerImpl> weak_ptr_factory_{this};
};
} // namespace
TileManager::TileManager() = default;
std::unique_ptr<TileManager> TileManager::Create(
std::unique_ptr<TileStore> tile_store,
base::Clock* clock,
QueryTilesConfig* config) {
return std::make_unique<TileManagerImpl>(std::move(tile_store), clock,
config);
}
} // namespace upboarding
// Copyright 2020 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#ifndef CHROME_BROWSER_UPBOARDING_QUERY_TILES_INTERNAL_TILE_MANAGER_H_
#define CHROME_BROWSER_UPBOARDING_QUERY_TILES_INTERNAL_TILE_MANAGER_H_
#include <memory>
#include <string>
#include <vector>
#include "base/callback.h"
#include "base/time/clock.h"
#include "chrome/browser/upboarding/query_tiles/internal/config.h"
#include "chrome/browser/upboarding/query_tiles/internal/query_tile_types.h"
#include "chrome/browser/upboarding/query_tiles/internal/store.h"
#include "chrome/browser/upboarding/query_tiles/internal/tile_group.h"
#include "chrome/browser/upboarding/query_tiles/query_tile_entry.h"
namespace upboarding {
// Manages the in-memory tile group and coordinates with storage layer.
class TileManager {
public:
using TileStore = Store<TileGroup>;
using TileGroupStatusCallback = base::OnceCallback<void(TileGroupStatus)>;
// Creates the instance.
static std::unique_ptr<TileManager> Create(
std::unique_ptr<TileStore> tile_store,
base::Clock* clock,
QueryTilesConfig* config);
// Initializes the query tile store, loading them into memory after
// validating.
virtual void Init(TileGroupStatusCallback callback) = 0;
// Returns tiles to the caller.
virtual void GetTiles(std::vector<QueryTileEntry*>* tiles) = 0;
// Save the query tiles into database.
virtual void SaveTiles(
std::vector<std::unique_ptr<QueryTileEntry>> top_level_tiles,
TileGroupStatusCallback callback) = 0;
TileManager();
virtual ~TileManager() = default;
TileManager(const TileManager& other) = delete;
TileManager& operator=(const TileManager& other) = delete;
};
} // namespace upboarding
#endif // CHROME_BROWSER_UPBOARDING_QUERY_TILES_INTERNAL_TILE_MANAGER_H_
......@@ -165,6 +165,26 @@ bool AreTilesIdentical(const QueryTileEntry& lhs, const QueryTileEntry& rhs) {
return true;
}
bool AreTilesIdentical(std::vector<QueryTileEntry*> lhs,
std::vector<QueryTileEntry*> rhs) {
if (lhs.size() != rhs.size())
return false;
auto entry_comparator = [](QueryTileEntry* a, QueryTileEntry* b) {
return a->id < b->id;
};
std::sort(lhs.begin(), lhs.end(), entry_comparator);
std::sort(rhs.begin(), rhs.end(), entry_comparator);
for (size_t i = 0; i < lhs.size(); i++) {
if (*lhs[i] != *rhs[i])
return false;
}
return true;
}
} // namespace test
} // namespace upboarding
......@@ -6,6 +6,7 @@
#define CHROME_BROWSER_UPBOARDING_QUERY_TILES_TEST_TEST_UTILS_H_
#include <string>
#include <vector>
#include "chrome/browser/upboarding/query_tiles/internal/tile_group.h"
#include "chrome/browser/upboarding/query_tiles/query_tile_entry.h"
......@@ -23,6 +24,8 @@ std::string DebugString(const TileGroup* group);
// Build and reset the TileGroup for test usage.
void ResetTestGroup(TileGroup* group);
// TODO(hesen): Have a better builder with parameters to specify the structure
// of tree.
// Build and reset the TileEntry for test usage.
void ResetTestEntry(QueryTileEntry* entry);
......@@ -32,6 +35,10 @@ bool AreTileGroupsIdentical(const TileGroup& lhs, const TileGroup& rhs);
// Returns true if all data in two QueryTileEntries are identical.
bool AreTilesIdentical(const QueryTileEntry& lhs, const QueryTileEntry& rhs);
// Returns true if all data in two lists of QueryTileEntry are identical.
bool AreTilesIdentical(std::vector<QueryTileEntry*> lhs,
std::vector<QueryTileEntry*> rhs);
} // namespace test
} // namespace upboarding
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment