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