Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Implement ad conversion attribution for brave ads #4204

Closed
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
37 changes: 37 additions & 0 deletions components/brave_ads/browser/ads_service_impl.cc
Original file line number Diff line number Diff line change
Expand Up @@ -309,6 +309,20 @@ std::vector<ads::AdInfo> GetAdsForCategoryOnFileTaskRunner(
return ads;
}

std::vector<ads::ConversionTrackingInfo> GetConversionsOnFileTaskRunner(
const std::string url,
BundleStateDatabase* backend) {
std::vector<ads::ConversionTrackingInfo> conversions;

if (!backend) {
return conversions;
}

backend->GetConversions(url, &conversions);

return conversions;
}

bool ResetOnFileTaskRunner(const base::FilePath& path) {
bool recursive;

Expand Down Expand Up @@ -1026,6 +1040,19 @@ void AdsServiceImpl::OnGetAdsForCategory(
callback(result, category, ads);
}

void AdsServiceImpl::OnGetConversions(
const ads::OnGetConversionsCallback& callback,
const std::string& url,
const std::vector<ads::ConversionTrackingInfo>& conversions) {
if (!connected()) {
return;
}

auto result = conversions.empty() ? ads::Result::FAILED : ads::Result::SUCCESS;

callback(result, url, conversions);
}

void AdsServiceImpl::OnGetAdsHistory(
OnGetAdsHistoryCallback callback,
const base::flat_map<uint64_t, std::vector<std::string>>& json) {
Expand Down Expand Up @@ -2096,6 +2123,16 @@ void AdsServiceImpl::GetAds(
std::move(callback), category));
}

void AdsServiceImpl::GetConversions(
const std::string& url,
ads::OnGetConversionsCallback callback) {
base::PostTaskAndReplyWithResult(file_task_runner_.get(), FROM_HERE,
base::BindOnce(&GetConversionsOnFileTaskRunner, url,
bundle_state_backend_.get()),
base::BindOnce(&AdsServiceImpl::OnGetConversions, AsWeakPtr(),
std::move(callback), url));
}

void AdsServiceImpl::EventLog(
const std::string& json) const {
VLOG(0) << "AdsService Event Log: " << json;
Expand Down
9 changes: 9 additions & 0 deletions components/brave_ads/browser/ads_service_impl.h
Original file line number Diff line number Diff line change
Expand Up @@ -210,6 +210,11 @@ class AdsServiceImpl : public AdsService,
const std::string& category,
const std::vector<ads::AdInfo>& ads);

void OnGetConversions(
const ads::OnGetConversionsCallback& callback,
const std::string& url,
const std::vector<ads::ConversionTrackingInfo>& conversions);

void OnGetAdsHistory(
OnGetAdsHistoryCallback callback,
const base::flat_map<uint64_t, std::vector<std::string>>& json);
Expand Down Expand Up @@ -413,6 +418,10 @@ class AdsServiceImpl : public AdsService,
const std::string& category,
ads::OnGetAdsCallback callback) override;

void GetConversions(
const std::string& url,
ads::OnGetConversionsCallback callback) override;

void EventLog(
const std::string& json) const override;

Expand Down
116 changes: 114 additions & 2 deletions components/brave_ads/browser/bundle_state_database.cc
Original file line number Diff line number Diff line change
Expand Up @@ -64,7 +64,8 @@ bool BundleStateDatabase::Init() {
if (!CreateCategoryTable() ||
!CreateAdInfoTable() ||
!CreateAdInfoCategoryTable() ||
!CreateAdInfoCategoryNameIndex())
!CreateAdInfoCategoryNameIndex() ||
!CreateConversionsTable())
return false;

// Version check.
Expand Down Expand Up @@ -113,6 +114,40 @@ bool BundleStateDatabase::TruncateCategoryTable() {
return sql.Run();
}

bool BundleStateDatabase::CreateConversionsTable() {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);

const char* name = "conversions";
if (GetDB().DoesTableExist(name))
return true;

std::string sql;
sql.append("CREATE TABLE ");
sql.append(name);
sql.append(
"("
"id INTEGER PRIMARY KEY,"
"creative_set_id LONGVARCHAR NOT NULL,"
"type LONGVARCHAR NOT NULL,"
"url_pattern LONGVARCHAR NOT NULL,"
"observation_window INTEGER NOT NULL)");
return GetDB().Execute(sql.c_str());
}

bool BundleStateDatabase::TruncateConversionsTable() {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);

bool initialized = Init();
DCHECK(initialized);

if (!initialized)
return false;

sql::Statement sql(
GetDB().GetCachedStatement(SQL_FROM_HERE,
"DELETE FROM conversions"));
return sql.Run();
}

bool BundleStateDatabase::CreateAdInfoTable() {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
Expand Down Expand Up @@ -221,7 +256,8 @@ bool BundleStateDatabase::SaveBundleState(
// we are completely replacing here so first truncate all the tables
if (!TruncateAdInfoCategoryTable() ||
!TruncateAdInfoTable() ||
!TruncateCategoryTable()) {
!TruncateCategoryTable() ||
!TruncateConversionsTable()) {
GetDB().RollbackTransaction();
return false;
}
Expand All @@ -247,6 +283,19 @@ bool BundleStateDatabase::SaveBundleState(
}
}

auto conversions = bundle_state.conversions;
for (std::vector<ads::ConversionTrackingInfo>::iterator conversion_it =
conversions.begin(); conversion_it != conversions.end();
++conversion_it) {
auto conversion_tracking_info = *conversion_it;

if (!InsertOrUpdateConversion(conversion_tracking_info)) {
GetDB().RollbackTransaction();
return false;
}

}

if (GetDB().CommitTransaction()) {
Vacuum();
return true;
Expand Down Expand Up @@ -313,6 +362,34 @@ bool BundleStateDatabase::InsertOrUpdateAdInfo(const ads::AdInfo& info) {
return true;
}

bool BundleStateDatabase::InsertOrUpdateConversion(
const ads::ConversionTrackingInfo& conversion_tracking_info) {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);

bool initialized = Init();
DCHECK(initialized);

if (!initialized)
return false;

sql::Statement conversion_info_statement(
GetDB().GetCachedStatement(SQL_FROM_HERE,
"INSERT OR REPLACE INTO conversions "
"(creative_set_id, type, url_pattern, observation_window) "
"VALUES (?, ?, ?, ?)"));

conversion_info_statement.BindString(0,
conversion_tracking_info.creative_set_id);
conversion_info_statement.BindString(1,
conversion_tracking_info.type);
conversion_info_statement.BindString(2,
conversion_tracking_info.url_pattern);
conversion_info_statement.BindInt(3,
conversion_tracking_info.observation_window);

return conversion_info_statement.Run();
}

bool BundleStateDatabase::InsertOrUpdateAdInfoCategory(
const ads::AdInfo& ad_info,
const std::string& category) {
Expand Down Expand Up @@ -382,6 +459,41 @@ bool BundleStateDatabase::GetAdsForCategory(
return true;
}

bool BundleStateDatabase::GetConversions(
const std::string& url,
std::vector<ads::ConversionTrackingInfo>* conversions) {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);

bool initialized = Init();
DCHECK(initialized);

if (!initialized)
return false;

sql::Statement info_sql(
db_.GetUniqueStatement(
"SELECT c.creative_set_id, c.type, c.url_pattern, "
"c.observation_window "
"FROM conversions AS c"));

while (info_sql.Step()) {
ads::ConversionTrackingInfo info;
info.creative_set_id = info_sql.ColumnString(0);
info.type = info_sql.ColumnString(1);
info.url_pattern = info_sql.ColumnString(2);
info.observation_window = info_sql.ColumnInt(3);
conversions->emplace_back(info);
}

return true;
}

bool BundleStateDatabase::SaveConversion(
const ads::ConversionTrackingInfo& conversion) {
LOG(INFO) << "*** Save Conversion";
return true;
}

// static
int BundleStateDatabase::GetCurrentVersion() {
return kCurrentVersionNumber;
Expand Down
10 changes: 10 additions & 0 deletions components/brave_ads/browser/bundle_state_database.h
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
#include <memory>

#include "bat/ads/ad_info.h"
#include "bat/ads/conversion_tracking_info.h"
#include "bat/ads/bundle_state.h"
#include "base/compiler_specific.h"
#include "base/files/file_path.h"
Expand Down Expand Up @@ -41,6 +42,11 @@ class BundleStateDatabase {
const std::string& category,
std::vector<ads::AdInfo>* ads);

bool SaveConversion(const ads::ConversionTrackingInfo& conversion);
bool GetConversions(
const std::string& url,
std::vector<ads::ConversionTrackingInfo>* conversions);

// Returns the current version of the publisher info database
static int GetCurrentVersion();

Expand All @@ -56,15 +62,19 @@ class BundleStateDatabase {
base::MemoryPressureListener::MemoryPressureLevel memory_pressure_level);

bool CreateCategoryTable();
bool CreateConversionsTable();
bool CreateAdInfoTable();
bool CreateAdInfoCategoryTable();
bool CreateAdInfoCategoryNameIndex();

bool TruncateCategoryTable();
bool TruncateConversionsTable();
bool TruncateAdInfoTable();
bool TruncateAdInfoCategoryTable();

bool InsertOrUpdateCategory(const std::string& category);
bool InsertOrUpdateConversion(
const ads::ConversionTrackingInfo& conversion_tracking_info);
bool InsertOrUpdateAdInfo(const ads::AdInfo& info);
bool InsertOrUpdateAdInfoCategory(
const ads::AdInfo& ad_info,
Expand Down
33 changes: 33 additions & 0 deletions components/services/bat_ads/bat_ads_client_mojo_bridge.cc
Original file line number Diff line number Diff line change
Expand Up @@ -440,6 +440,39 @@ void BatAdsClientMojoBridge::GetAds(
std::move(callback)));
}

void OnGetConversions(
const ads::OnGetConversionsCallback& callback,
const int32_t result,
const std::string& url,
const std::vector<std::string>& conversion_tracking_info_json_list) {
std::vector<ads::ConversionTrackingInfo> conversions;

for (const auto& it : conversion_tracking_info_json_list) {
ads::ConversionTrackingInfo conversion_tracking_info;
if (conversion_tracking_info.FromJson(it) == ads::Result::SUCCESS) {
conversions.push_back(conversion_tracking_info);
} else {
callback(ads::Result::FAILED, url, {}); // Pass URL through
return;
}
}

callback(ToAdsResult(result), url, conversions); // Pass URL through
}


void BatAdsClientMojoBridge::GetConversions(
const std::string& url, // Pass URL through
ads::OnGetConversionsCallback callback) {
if (!connected()) {
callback(ads::Result::FAILED, url, std::vector<ads::ConversionTrackingInfo>()); // Pass URL through
return;
}

bat_ads_client_->GetConversions(url, base::BindOnce(&OnGetConversions,
std::move(callback)));
}

void BatAdsClientMojoBridge::EventLog(
const std::string& json) const {
if (!connected()) {
Expand Down
4 changes: 4 additions & 0 deletions components/services/bat_ads/bat_ads_client_mojo_bridge.h
Original file line number Diff line number Diff line change
Expand Up @@ -100,6 +100,10 @@ class BatAdsClientMojoBridge : public ads::AdsClient {
const std::string& category,
ads::OnGetAdsCallback callback) override;

void GetConversions(
const std::string& url,
ads::OnGetConversionsCallback callback) override;

void EventLog(
const std::string& json) const override;

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -391,4 +391,30 @@ void AdsClientMojoBridge::GetAds(
holder, _1, _2, _3));
}

void AdsClientMojoBridge::OnGetConversions(
CallbackHolder<GetConversionsCallback>* holder,
ads::Result result,
const std::string& url,
const std::vector<ads::ConversionTrackingInfo>& conversion_tracking_info) {
if (holder->is_valid()) {
std::vector<std::string> conversion_tracking_info_json;
for (const auto it : conversion_tracking_info) {
conversion_tracking_info_json.push_back(it.ToJson());
}
std::move(holder->get()).Run(ToMojomResult(result), url, conversion_tracking_info_json);
}
delete holder;
}

void AdsClientMojoBridge::GetConversions(
const std::string& url,
GetConversionsCallback callback) {
// this gets deleted in OnSaveBundleState
auto* holder = new CallbackHolder<GetConversionsCallback>(
AsWeakPtr(), std::move(callback));

ads_client_->GetConversions(url, std::bind(AdsClientMojoBridge::OnGetConversions,
holder, _1, _2, _3));
}

} // namespace bat_ads
Original file line number Diff line number Diff line change
Expand Up @@ -124,6 +124,9 @@ class AdsClientMojoBridge : public mojom::BatAdsClient,
void GetAds(
const std::string& category,
GetAdsCallback callback) override;
void GetConversions(
const std::string& url,
GetConversionsCallback callback) override;

private:
// workaround to pass base::OnceCallback into std::bind
Expand Down Expand Up @@ -173,6 +176,11 @@ class AdsClientMojoBridge : public mojom::BatAdsClient,
ads::Result result,
const std::string& category,
const std::vector<ads::AdInfo>& ad_info);
static void OnGetConversions(
CallbackHolder<GetConversionsCallback>* holder,
ads::Result result,
const std::string& url,
const std::vector<ads::ConversionTrackingInfo>& conversion_tracking_info);

ads::AdsClient* ads_client_;

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,7 @@ interface BatAdsClient {
LoadSampleBundle() => (int32 result, string value);
Reset(string name) => (int32 result);
GetAds(string category) => (int32 result, string category, array<string> ad_info);
GetConversions(string url) => (int32 result, string url, array<string> conversion_tracking_info);
EventLog(string json);
};

Expand Down
Loading