1 // Copyright 2017 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4
5 #include "components/feature_engagement/internal/persistent_availability_store.h"
6
7 #include <memory>
8 #include <string>
9 #include <utility>
10 #include <vector>
11
12 #include "base/bind.h"
13 #include "base/callback.h"
14 #include "base/feature_list.h"
15 #include "components/feature_engagement/internal/proto/availability.pb.h"
16 #include "components/feature_engagement/internal/stats.h"
17 #include "components/feature_engagement/public/feature_list.h"
18 #include "components/leveldb_proto/public/proto_database.h"
19
20 namespace feature_engagement {
21
22 namespace {
23
24 using KeyAvailabilityPair = std::pair<std::string, Availability>;
25 using KeyAvailabilityList = std::vector<KeyAvailabilityPair>;
26
OnDBUpdateComplete(std::unique_ptr<leveldb_proto::ProtoDatabase<Availability>> db,PersistentAvailabilityStore::OnLoadedCallback on_loaded_callback,std::unique_ptr<std::map<std::string,uint32_t>> feature_availabilities,bool success)27 void OnDBUpdateComplete(
28 std::unique_ptr<leveldb_proto::ProtoDatabase<Availability>> db,
29 PersistentAvailabilityStore::OnLoadedCallback on_loaded_callback,
30 std::unique_ptr<std::map<std::string, uint32_t>> feature_availabilities,
31 bool success) {
32 stats::RecordDbUpdate(success, stats::StoreType::AVAILABILITY_STORE);
33 std::move(on_loaded_callback).Run(success, std::move(feature_availabilities));
34 }
35
OnDBLoadComplete(std::unique_ptr<leveldb_proto::ProtoDatabase<Availability>> db,FeatureVector feature_filter,PersistentAvailabilityStore::OnLoadedCallback on_loaded_callback,uint32_t current_day,bool success,std::unique_ptr<std::vector<Availability>> availabilities)36 void OnDBLoadComplete(
37 std::unique_ptr<leveldb_proto::ProtoDatabase<Availability>> db,
38 FeatureVector feature_filter,
39 PersistentAvailabilityStore::OnLoadedCallback on_loaded_callback,
40 uint32_t current_day,
41 bool success,
42 std::unique_ptr<std::vector<Availability>> availabilities) {
43 stats::RecordAvailabilityDbLoadEvent(success);
44 if (!success) {
45 std::move(on_loaded_callback)
46 .Run(false, std::make_unique<std::map<std::string, uint32_t>>());
47 return;
48 }
49
50 // Create map from feature name to Feature.
51 std::map<std::string, const base::Feature*> feature_mapping;
52 for (const base::Feature* feature : feature_filter) {
53 DCHECK(feature_mapping.find(feature->name) == feature_mapping.end());
54 feature_mapping[feature->name] = feature;
55 }
56
57 // Find all availabilities from DB and find out what should be deleted.
58 auto feature_availabilities =
59 std::make_unique<std::map<std::string, uint32_t>>();
60 auto deletes = std::make_unique<std::vector<std::string>>();
61 for (auto& availability : *availabilities) {
62 // Check if in |feature_filter|.
63 if (feature_mapping.find(availability.feature_name()) ==
64 feature_mapping.end()) {
65 deletes->push_back(availability.feature_name());
66 continue;
67 }
68
69 // Check if enabled.
70 const base::Feature* feature = feature_mapping[availability.feature_name()];
71 if (!base::FeatureList::IsEnabled(*feature)) {
72 deletes->push_back(availability.feature_name());
73 continue;
74 }
75
76 // Both in |feature_filter| and is enabled, so keep around.
77 feature_availabilities->insert(
78 std::make_pair(feature->name, availability.day()));
79 DVLOG(2) << "Keeping availability for " << feature->name << " @ "
80 << availability.day();
81 }
82
83 // Find features from |feature_filter| that are enabled, but not in DB yet.
84 auto additions = std::make_unique<KeyAvailabilityList>();
85 for (const base::Feature* feature : feature_filter) {
86 // Check if already in DB.
87 if (feature_availabilities->find(feature->name) !=
88 feature_availabilities->end())
89 continue;
90
91 // Check if enabled.
92 if (!base::FeatureList::IsEnabled(*feature))
93 continue;
94
95 // Both in feature filter, and is enabled, but not in DB, so add to DB.
96 Availability availability;
97 availability.set_feature_name(feature->name);
98 availability.set_day(current_day);
99 additions->push_back({feature->name, std::move(availability)});
100 // Since it will be written to the DB, also add to the callback result.
101 feature_availabilities->insert({feature->name, current_day});
102
103 DVLOG(2) << "Adding availability for " << feature->name << " @ "
104 << current_day;
105 }
106
107 // Write all changes to the DB.
108 auto* db_ptr = db.get();
109 db_ptr->UpdateEntries(std::move(additions), std::move(deletes),
110 base::BindOnce(&OnDBUpdateComplete, std::move(db),
111 std::move(on_loaded_callback),
112 std::move(feature_availabilities)));
113 }
114
OnDBInitComplete(std::unique_ptr<leveldb_proto::ProtoDatabase<Availability>> db,FeatureVector feature_filter,PersistentAvailabilityStore::OnLoadedCallback on_loaded_callback,uint32_t current_day,leveldb_proto::Enums::InitStatus status)115 void OnDBInitComplete(
116 std::unique_ptr<leveldb_proto::ProtoDatabase<Availability>> db,
117 FeatureVector feature_filter,
118 PersistentAvailabilityStore::OnLoadedCallback on_loaded_callback,
119 uint32_t current_day,
120 leveldb_proto::Enums::InitStatus status) {
121 bool success = status == leveldb_proto::Enums::InitStatus::kOK;
122 stats::RecordDbInitEvent(success, stats::StoreType::AVAILABILITY_STORE);
123
124 if (!success) {
125 std::move(on_loaded_callback)
126 .Run(false, std::make_unique<std::map<std::string, uint32_t>>());
127 return;
128 }
129
130 auto* db_ptr = db.get();
131 db_ptr->LoadEntries(base::BindOnce(
132 &OnDBLoadComplete, std::move(db), std::move(feature_filter),
133 std::move(on_loaded_callback), current_day));
134 }
135
136 } // namespace
137
138 // static
LoadAndUpdateStore(const base::FilePath & storage_dir,std::unique_ptr<leveldb_proto::ProtoDatabase<Availability>> db,FeatureVector feature_filter,PersistentAvailabilityStore::OnLoadedCallback on_loaded_callback,uint32_t current_day)139 void PersistentAvailabilityStore::LoadAndUpdateStore(
140 const base::FilePath& storage_dir,
141 std::unique_ptr<leveldb_proto::ProtoDatabase<Availability>> db,
142 FeatureVector feature_filter,
143 PersistentAvailabilityStore::OnLoadedCallback on_loaded_callback,
144 uint32_t current_day) {
145 auto* db_ptr = db.get();
146 db_ptr->Init(base::BindOnce(&OnDBInitComplete, std::move(db),
147 std::move(feature_filter),
148 std::move(on_loaded_callback), current_day));
149 }
150
151 } // namespace feature_engagement
152