diff --git a/components/brave_ads/test/BUILD.gn b/components/brave_ads/test/BUILD.gn index f2c47167d575..bc80cc392927 100644 --- a/components/brave_ads/test/BUILD.gn +++ b/components/brave_ads/test/BUILD.gn @@ -64,6 +64,7 @@ source_set("brave_ads_unit_tests") { "//brave/vendor/bat-native-ads/src/bat/ads/internal/features/ad_serving/ad_serving_features_unittest.cc", "//brave/vendor/bat-native-ads/src/bat/ads/internal/features/anti_targeting/anti_targeting_features_unittest.cc", "//brave/vendor/bat-native-ads/src/bat/ads/internal/features/bandits/epsilon_greedy_bandit_features_unittest.cc", + "//brave/vendor/bat-native-ads/src/bat/ads/internal/features/conversions/conversions_features_unittest.cc", "//brave/vendor/bat-native-ads/src/bat/ads/internal/features/purchase_intent/purchase_intent_features_unittest.cc", "//brave/vendor/bat-native-ads/src/bat/ads/internal/features/text_classification/text_classification_features_unittest.cc", "//brave/vendor/bat-native-ads/src/bat/ads/internal/features/user_activity/user_activity_features_unittest.cc", @@ -129,6 +130,7 @@ source_set("brave_ads_unit_tests") { "//brave/vendor/bat-native-ads/src/bat/ads/internal/resources/behavioral/bandits/epsilon_greedy_bandit_resource_unittest.cc", "//brave/vendor/bat-native-ads/src/bat/ads/internal/resources/behavioral/purchase_intent/purchase_intent_resource_unittest.cc", "//brave/vendor/bat-native-ads/src/bat/ads/internal/resources/contextual/text_classification/text_classification_resource_unittest.cc", + "//brave/vendor/bat-native-ads/src/bat/ads/internal/resources/conversions/conversions_resource_unittest.cc", "//brave/vendor/bat-native-ads/src/bat/ads/internal/resources/frequency_capping/anti_targeting_resource_unittest.cc", "//brave/vendor/bat-native-ads/src/bat/ads/internal/security/conversions/conversions_util_unittest.cc", "//brave/vendor/bat-native-ads/src/bat/ads/internal/security/crypto_util_unittest.cc", diff --git a/vendor/bat-native-ads/BUILD.gn b/vendor/bat-native-ads/BUILD.gn index c0bc101cd372..05f7153a16ca 100644 --- a/vendor/bat-native-ads/BUILD.gn +++ b/vendor/bat-native-ads/BUILD.gn @@ -391,6 +391,8 @@ source_set("ads") { "src/bat/ads/internal/features/anti_targeting/anti_targeting_features.h", "src/bat/ads/internal/features/bandits/epsilon_greedy_bandit_features.cc", "src/bat/ads/internal/features/bandits/epsilon_greedy_bandit_features.h", + "src/bat/ads/internal/features/conversions/conversions_features.cc", + "src/bat/ads/internal/features/conversions/conversions_features.h", "src/bat/ads/internal/features/features.cc", "src/bat/ads/internal/features/features.h", "src/bat/ads/internal/features/features_util.cc", @@ -552,6 +554,10 @@ source_set("ads") { "src/bat/ads/internal/resources/behavioral/purchase_intent/purchase_intent_resource.h", "src/bat/ads/internal/resources/contextual/text_classification/text_classification_resource.cc", "src/bat/ads/internal/resources/contextual/text_classification/text_classification_resource.h", + "src/bat/ads/internal/resources/conversions/conversion_id_pattern_info.cc", + "src/bat/ads/internal/resources/conversions/conversion_id_pattern_info.h", + "src/bat/ads/internal/resources/conversions/conversions_resource.cc", + "src/bat/ads/internal/resources/conversions/conversions_resource.h", "src/bat/ads/internal/resources/country_components.h", "src/bat/ads/internal/resources/frequency_capping/anti_targeting_info.cc", "src/bat/ads/internal/resources/frequency_capping/anti_targeting_info.h", diff --git a/vendor/bat-native-ads/data/test/resources/nnqccijfhvzwyrxpxwjrpmynaiazctqb b/vendor/bat-native-ads/data/test/resources/nnqccijfhvzwyrxpxwjrpmynaiazctqb new file mode 100644 index 000000000000..7ac9772067b0 --- /dev/null +++ b/vendor/bat-native-ads/data/test/resources/nnqccijfhvzwyrxpxwjrpmynaiazctqb @@ -0,0 +1,15 @@ +{ + "locale": "us", + "version": 1, + "timestamp": "2021-04-26 09:00:00", + "conversion_id_patterns": { + "https://brave.com/foobar": { + "id_pattern": "(.*)<\/div>", + "search_in": "html" + }, + "https://brave.com/foobar?conversion_id=*": { + "id_pattern": "conversion_id\\=(.*)", + "search_in": "url" + } + } +} \ No newline at end of file diff --git a/vendor/bat-native-ads/src/bat/ads/internal/ads_impl.cc b/vendor/bat-native-ads/src/bat/ads/internal/ads_impl.cc index 12e98109db87..ca6b44bdf24a 100644 --- a/vendor/bat-native-ads/src/bat/ads/internal/ads_impl.cc +++ b/vendor/bat-native-ads/src/bat/ads/internal/ads_impl.cc @@ -45,6 +45,7 @@ #include "bat/ads/internal/resources/behavioral/bandits/epsilon_greedy_bandit_resource.h" #include "bat/ads/internal/resources/behavioral/purchase_intent/purchase_intent_resource.h" #include "bat/ads/internal/resources/contextual/text_classification/text_classification_resource.h" +#include "bat/ads/internal/resources/conversions/conversions_resource.h" #include "bat/ads/internal/resources/country_components.h" #include "bat/ads/internal/resources/frequency_capping/anti_targeting_resource.h" #include "bat/ads/internal/resources/language_components.h" @@ -126,6 +127,7 @@ void AdsImpl::ChangeLocale(const std::string& locale) { text_classification_resource_->Load(); purchase_intent_resource_->Load(); anti_targeting_resource_->Load(); + conversions_resource_->Load(); } void AdsImpl::OnAdsSubdivisionTargetingCodeHasChanged() { @@ -150,7 +152,8 @@ void AdsImpl::OnHtmlLoaded(const int32_t tab_id, const std::string original_url = redirect_chain.front(); ad_transfer_->MaybeTransferAd(tab_id, original_url); - conversions_->MaybeConvert(redirect_chain, html); + conversions_->MaybeConvert(redirect_chain, html, + conversions_resource_->get()); } void AdsImpl::OnTextLoaded(const int32_t tab_id, @@ -274,6 +277,7 @@ void AdsImpl::OnResourceComponentUpdated(const std::string& id) { } else if (kComponentCountryIds.find(id) != kComponentCountryIds.end()) { purchase_intent_resource_->Load(); anti_targeting_resource_->Load(); + conversions_resource_->Load(); } else { BLOG(0, "Unknown resource for " << id); } @@ -424,6 +428,8 @@ void AdsImpl::set(privacy::TokenGeneratorInterface* token_generator) { anti_targeting_resource_ = std::make_unique(); + conversions_resource_ = std::make_unique(); + ad_targeting_ = std::make_unique(); subdivision_targeting_ = std::make_unique(); diff --git a/vendor/bat-native-ads/src/bat/ads/internal/ads_impl.h b/vendor/bat-native-ads/src/bat/ads/internal/ads_impl.h index 88837c896bbb..2cc14ca277a1 100644 --- a/vendor/bat-native-ads/src/bat/ads/internal/ads_impl.h +++ b/vendor/bat-native-ads/src/bat/ads/internal/ads_impl.h @@ -47,6 +47,7 @@ class SubdivisionTargeting; namespace resource { class AntiTargeting; +class Conversions; class EpsilonGreedyBandit; class PurchaseIntent; class TextClassification; @@ -202,6 +203,7 @@ class AdsImpl : public Ads, std::unique_ptr purchase_intent_processor_; std::unique_ptr anti_targeting_resource_; + std::unique_ptr conversions_resource_; std::unique_ptr subdivision_targeting_; std::unique_ptr ad_targeting_; diff --git a/vendor/bat-native-ads/src/bat/ads/internal/conversions/conversions.cc b/vendor/bat-native-ads/src/bat/ads/internal/conversions/conversions.cc index 840e70a641f8..45eec294e2d5 100644 --- a/vendor/bat-native-ads/src/bat/ads/internal/conversions/conversions.cc +++ b/vendor/bat-native-ads/src/bat/ads/internal/conversions/conversions.cc @@ -20,6 +20,7 @@ #include "bat/ads/internal/database/tables/ad_events_database_table.h" #include "bat/ads/internal/database/tables/conversion_queue_database_table.h" #include "bat/ads/internal/database/tables/conversions_database_table.h" +#include "bat/ads/internal/features/conversions/conversions_features.h" #include "bat/ads/internal/logging.h" #include "bat/ads/internal/time_formatting_util.h" #include "bat/ads/internal/url_util.h" @@ -34,8 +35,8 @@ namespace { const int64_t kConvertAfterSeconds = base::Time::kHoursPerDay * base::Time::kSecondsPerHour; const int64_t kDebugConvertAfterSeconds = 10 * base::Time::kSecondsPerMinute; - const int64_t kExpiredConvertAfterSeconds = 1 * base::Time::kSecondsPerMinute; +const char kSearchInUrl[] = "url"; bool HasObservationWindowForAdEventExpired(const int observation_window, const AdEventInfo& ad_event) { @@ -83,14 +84,41 @@ bool DoesConfirmationTypeMatchConversionType( } } -std::string ExtractVerifiableConversionIdFromHtml(const std::string& html) { - re2::StringPiece text_string_piece(html); - RE2 r(""); +std::string ExtractConversionIdFromText( + const std::string& html, + const std::vector& redirect_chain, + const std::string& conversion_url_pattern, + const ConversionIdPatternMap& conversion_id_patterns) { + std::string conversion_id; + std::string conversion_id_pattern = + features::GetGetDefaultConversionIdPattern(); + std::string text = html; + + const auto iter = conversion_id_patterns.find(conversion_url_pattern); + if (iter != conversion_id_patterns.end()) { + const ConversionIdPatternInfo conversion_id_pattern_info = iter->second; + if (conversion_id_pattern_info.search_in == kSearchInUrl) { + const auto url_iter = std::find_if( + redirect_chain.begin(), redirect_chain.end(), + [=](const std::string& url) { + return DoesUrlMatchPattern(url, conversion_url_pattern); + }); + + if (url_iter == redirect_chain.end()) { + return conversion_id; + } + + text = *url_iter; + } + + conversion_id_pattern = conversion_id_pattern_info.id_pattern; + } - std::string verifiable_conversion_id; - RE2::FindAndConsume(&text_string_piece, r, &verifiable_conversion_id); + re2::StringPiece text_string_piece(text); + RE2 r(conversion_id_pattern); + RE2::FindAndConsume(&text_string_piece, r, &conversion_id); - return verifiable_conversion_id; + return conversion_id; } std::set GetConvertedCreativeSets(const AdEventList& ad_events) { @@ -155,8 +183,10 @@ void Conversions::RemoveObserver(ConversionsObserver* observer) { observers_.RemoveObserver(observer); } -void Conversions::MaybeConvert(const std::vector& redirect_chain, - const std::string& html) { +void Conversions::MaybeConvert( + const std::vector& redirect_chain, + const std::string& html, + const ConversionIdPatternMap& conversion_id_patterns) { if (!ShouldAllow()) { BLOG(1, "Conversions are not allowed"); return; @@ -168,7 +198,7 @@ void Conversions::MaybeConvert(const std::vector& redirect_chain, return; } - CheckRedirectChain(redirect_chain, html); + CheckRedirectChain(redirect_chain, html, conversion_id_patterns); } void Conversions::StartTimerIfReady() { @@ -202,7 +232,8 @@ bool Conversions::ShouldAllow() const { void Conversions::CheckRedirectChain( const std::vector& redirect_chain, - const std::string& html) { + const std::string& html, + const ConversionIdPatternMap& conversion_id_patterns) { BLOG(1, "Checking URL for conversions"); database::table::AdEvents ad_events_database_table; @@ -254,8 +285,9 @@ void Conversions::CheckRedirectChain( creative_set_ids.insert(ad_event.creative_set_id); VerifiableConversionInfo verifiable_conversion; - verifiable_conversion.id = - ExtractVerifiableConversionIdFromHtml(html); + verifiable_conversion.id = ExtractConversionIdFromText( + html, redirect_chain, conversion.url_pattern, + conversion_id_patterns); verifiable_conversion.public_key = conversion.advertiser_public_key; Convert(ad_event, verifiable_conversion); diff --git a/vendor/bat-native-ads/src/bat/ads/internal/conversions/conversions.h b/vendor/bat-native-ads/src/bat/ads/internal/conversions/conversions.h index 86606f8988a3..84b2d868ed90 100644 --- a/vendor/bat-native-ads/src/bat/ads/internal/conversions/conversions.h +++ b/vendor/bat-native-ads/src/bat/ads/internal/conversions/conversions.h @@ -17,6 +17,7 @@ #include "bat/ads/internal/conversions/conversion_queue_item_info.h" #include "bat/ads/internal/conversions/conversions_observer.h" #include "bat/ads/internal/conversions/verifiable_conversion_info.h" +#include "bat/ads/internal/resources/conversions/conversion_id_pattern_info.h" #include "bat/ads/internal/security/conversions/verifiable_conversion_envelope_info.h" #include "bat/ads/internal/timer.h" @@ -34,7 +35,8 @@ class Conversions { bool ShouldAllow() const; void MaybeConvert(const std::vector& redirect_chain, - const std::string& html); + const std::string& html, + const ConversionIdPatternMap& conversion_id_patterns); void StartTimerIfReady(); @@ -44,7 +46,8 @@ class Conversions { Timer timer_; void CheckRedirectChain(const std::vector& redirect_chain, - const std::string& html); + const std::string& html, + const ConversionIdPatternMap& conversion_id_patterns); void Convert(const AdEventInfo& ad_event, const VerifiableConversionInfo& verifiable_conversion); diff --git a/vendor/bat-native-ads/src/bat/ads/internal/conversions/conversions_unittest.cc b/vendor/bat-native-ads/src/bat/ads/internal/conversions/conversions_unittest.cc index d7e5179ae9cf..607951c17610 100644 --- a/vendor/bat-native-ads/src/bat/ads/internal/conversions/conversions_unittest.cc +++ b/vendor/bat-native-ads/src/bat/ads/internal/conversions/conversions_unittest.cc @@ -10,7 +10,9 @@ #include "base/strings/stringprintf.h" #include "bat/ads/internal/ad_events/ad_events.h" #include "bat/ads/internal/database/tables/ad_events_database_table.h" +#include "bat/ads/internal/database/tables/conversion_queue_database_table.h" #include "bat/ads/internal/database/tables/conversions_database_table.h" +#include "bat/ads/internal/resources/conversions/conversions_resource.h" #include "bat/ads/internal/unittest_base.h" #include "bat/ads/internal/unittest_util.h" #include "bat/ads/pref_names.h" @@ -25,6 +27,8 @@ class BatAdsConversionsTest : public UnitTestBase { : conversions_(std::make_unique()), ad_events_database_table_( std::make_unique()), + conversion_queue_database_table_( + std::make_unique()), conversions_database_table_( std::make_unique()) {} @@ -58,6 +62,8 @@ class BatAdsConversionsTest : public UnitTestBase { std::unique_ptr conversions_; std::unique_ptr ad_events_database_table_; + std::unique_ptr + conversion_queue_database_table_; std::unique_ptr conversions_database_table_; }; @@ -80,7 +86,7 @@ TEST_F(BatAdsConversionsTest, ShouldNotAllowConversionTracking) { SaveConversions(conversions); // Act - conversions_->MaybeConvert({"https://www.foobar.com/signup"}, ""); + conversions_->MaybeConvert({"https://www.foobar.com/signup"}, "", {}); // Assert const std::string condition = base::StringPrintf( @@ -113,7 +119,7 @@ TEST_F(BatAdsConversionsTest, ConvertViewedAd) { FireAdEvent(conversion.creative_set_id, ConfirmationType::kViewed); // Act - conversions_->MaybeConvert({"https://www.foo.com/bar"}, ""); + conversions_->MaybeConvert({"https://www.foo.com/bar"}, "", {}); // Assert const std::string condition = base::StringPrintf( @@ -151,7 +157,7 @@ TEST_F(BatAdsConversionsTest, ConvertClickedAd) { FireAdEvent(conversion.creative_set_id, ConfirmationType::kClicked); // Act - conversions_->MaybeConvert({"https://www.foo.com/bar/baz"}, ""); + conversions_->MaybeConvert({"https://www.foo.com/bar/baz"}, "", {}); // Assert const std::string condition = base::StringPrintf( @@ -200,9 +206,9 @@ TEST_F(BatAdsConversionsTest, ConvertMultipleAds) { FireAdEvent(conversion_2.creative_set_id, ConfirmationType::kClicked); // Act - conversions_->MaybeConvert({"https://www.foo.com/qux"}, ""); + conversions_->MaybeConvert({"https://www.foo.com/qux"}, "", {}); - conversions_->MaybeConvert({"https://www.foo.com/bar/baz"}, ""); + conversions_->MaybeConvert({"https://www.foo.com/bar/baz"}, "", {}); // Assert const std::string condition = base::StringPrintf( @@ -247,7 +253,7 @@ TEST_F(BatAdsConversionsTest, ConvertViewedAdWhenAdWasDismissed) { FireAdEvent(conversion.creative_set_id, ConfirmationType::kDismissed); // Act - conversions_->MaybeConvert({"https://www.foo.com/quxbarbaz"}, ""); + conversions_->MaybeConvert({"https://www.foo.com/quxbarbaz"}, "", {}); // Assert const std::string condition = base::StringPrintf( @@ -288,7 +294,7 @@ TEST_F(BatAdsConversionsTest, DoNotConvertNonViewedOrClickedAds) { FireAdEvent(conversion.creative_set_id, ConfirmationType::kDownvoted); // Act - conversions_->MaybeConvert({"https://www.foo.com/bar"}, ""); + conversions_->MaybeConvert({"https://www.foo.com/bar"}, "", {}); // Assert const std::string condition = base::StringPrintf( @@ -321,7 +327,7 @@ TEST_F(BatAdsConversionsTest, DoNotConvertViewedAdForPostClick) { FireAdEvent(conversion.creative_set_id, ConfirmationType::kViewed); // Act - conversions_->MaybeConvert({"https://www.foo.com/bar"}, ""); + conversions_->MaybeConvert({"https://www.foo.com/bar"}, "", {}); // Assert const std::string condition = base::StringPrintf( @@ -343,7 +349,7 @@ TEST_F(BatAdsConversionsTest, DoNotConvertAdIfConversionDoesNotExist) { FireAdEvent(creative_set_id, ConfirmationType::kViewed); // Act - conversions_->MaybeConvert({"https://www.foo.com/bar"}, ""); + conversions_->MaybeConvert({"https://www.foo.com/bar"}, "", {}); // Assert const std::string condition = @@ -376,10 +382,10 @@ TEST_F(BatAdsConversionsTest, FireAdEvent(conversion.creative_set_id, ConfirmationType::kViewed); - conversions_->MaybeConvert({"https://www.foo.com/bar"}, ""); + conversions_->MaybeConvert({"https://www.foo.com/bar"}, "", {}); // Act - conversions_->MaybeConvert({"https://www.foo.com/bar"}, ""); + conversions_->MaybeConvert({"https://www.foo.com/bar"}, "", {}); // Assert const std::string condition = base::StringPrintf( @@ -399,7 +405,7 @@ TEST_F(BatAdsConversionsTest, } TEST_F(BatAdsConversionsTest, - DoNotConvertAdWhenUrlDoesNotMatchConversionPattern) { + DoNotConvertAdWhenUrlDoesNotMatchConversionIdPattern) { // Arrange ConversionList conversions; @@ -417,7 +423,7 @@ TEST_F(BatAdsConversionsTest, FireAdEvent(conversion.creative_set_id, ConfirmationType::kViewed); // Act - conversions_->MaybeConvert({"https://www.foo.com/qux"}, ""); + conversions_->MaybeConvert({"https://www.foo.com/qux"}, "", {}); // Assert const std::string condition = base::StringPrintf( @@ -453,7 +459,7 @@ TEST_F(BatAdsConversionsTest, ConvertAdWhenTheConversionIsOnTheCuspOfExpiring) { base::TimeDelta::FromMinutes(1)); // Act - conversions_->MaybeConvert({"https://foo.bar.com/qux"}, ""); + conversions_->MaybeConvert({"https://foo.bar.com/qux"}, "", {}); // Assert const std::string condition = base::StringPrintf( @@ -492,7 +498,7 @@ TEST_F(BatAdsConversionsTest, DoNotConvertAdWhenTheConversionHasExpired) { task_environment_.FastForwardBy(base::TimeDelta::FromDays(3)); // Act - conversions_->MaybeConvert({"https://www.foo.com/bar/qux"}, ""); + conversions_->MaybeConvert({"https://www.foo.com/bar/qux"}, "", {}); // Assert const std::string condition = base::StringPrintf( @@ -526,8 +532,8 @@ TEST_F(BatAdsConversionsTest, ConvertAdForRedirectChainIntermediateUrl) { // Act conversions_->MaybeConvert( - {"https://foo.com/bar", "https://foo.com/baz", "https://foo.com/qux"}, - ""); + {"https://foo.com/bar", "https://foo.com/baz", "https://foo.com/qux"}, "", + {}); // Assert const std::string condition = base::StringPrintf( @@ -565,8 +571,8 @@ TEST_F(BatAdsConversionsTest, ConvertAdForRedirectChainOriginalUrl) { // Act conversions_->MaybeConvert( - {"https://foo.com/bar", "https://foo.com/baz", "https://foo.com/qux"}, - ""); + {"https://foo.com/bar", "https://foo.com/baz", "https://foo.com/qux"}, "", + {}); // Assert const std::string condition = base::StringPrintf( @@ -604,8 +610,8 @@ TEST_F(BatAdsConversionsTest, ConvertAdForRedirectChainUrl) { // Act conversions_->MaybeConvert( - {"https://foo.com/bar", "https://foo.com/baz", "https://foo.com/qux"}, - ""); + {"https://foo.com/bar", "https://foo.com/baz", "https://foo.com/qux"}, "", + {}); // Assert const std::string condition = base::StringPrintf( @@ -624,4 +630,141 @@ TEST_F(BatAdsConversionsTest, ConvertAdForRedirectChainUrl) { }); } +TEST_F(BatAdsConversionsTest, ExtractConversionId) { + // Arrange + resource::Conversions resource; + resource.Load(); + + ConversionList conversions; + + ConversionInfo conversion; + conversion.advertiser_public_key = + "ofIveUY/bM7qlL9eIkAv/xbjDItFs1xRTTYKRZZsPHI="; + conversion.creative_set_id = "3519f52c-46a4-4c48-9c2b-c264c0067f04"; + conversion.type = "postview"; + conversion.url_pattern = "https://brave.com/thankyou"; + conversion.observation_window = 3; + conversion.expiry_timestamp = + CalculateExpiryTimestamp(conversion.observation_window); + conversions.push_back(conversion); + + SaveConversions(conversions); + + FireAdEvent(conversion.creative_set_id, ConfirmationType::kViewed); + + // Act + conversions_->MaybeConvert( + {"https://foo.bar/", "https://brave.com/thankyou"}, + "", + resource.get()); + + // Assert + conversion_queue_database_table_->GetAll( + [=](const Result result, + const ConversionQueueItemList& conversion_queue_items) { + ASSERT_EQ(Result::SUCCESS, result); + + ASSERT_EQ(1UL, conversion_queue_items.size()); + ConversionQueueItemInfo item = conversion_queue_items.front(); + + ASSERT_EQ(conversion.creative_set_id, item.creative_set_id); + ASSERT_EQ(conversion.advertiser_public_key, item.advertiser_public_key); + + const std::string expected_conversion_id = "abc123"; + EXPECT_EQ(expected_conversion_id, item.conversion_id); + }); +} + +TEST_F(BatAdsConversionsTest, ExtractConversionIdWithResourcePatternFromHtml) { + // Arrange + resource::Conversions resource; + resource.Load(); + + ConversionList conversions; + + ConversionInfo conversion; + conversion.advertiser_public_key = + "ofIveUY/bM7qlL9eIkAv/xbjDItFs1xRTTYKRZZsPHI="; + conversion.creative_set_id = "3519f52c-46a4-4c48-9c2b-c264c0067f04"; + conversion.type = "postview"; + conversion.url_pattern = "https://brave.com/foobar"; + conversion.observation_window = 3; + conversion.expiry_timestamp = + CalculateExpiryTimestamp(conversion.observation_window); + conversions.push_back(conversion); + + SaveConversions(conversions); + + FireAdEvent(conversion.creative_set_id, ConfirmationType::kViewed); + + // Act + // See associated patterns in the verifiable conversion resource + // /data/test/resources/nnqccijfhvzwyrxpxwjrpmynaiazctqb + conversions_->MaybeConvert( + {"https://foo.bar/", "https://brave.com/foobar"}, + "
abc123
", resource.get()); + + // Assert + conversion_queue_database_table_->GetAll( + [=](const Result result, + const ConversionQueueItemList& conversion_queue_items) { + ASSERT_EQ(Result::SUCCESS, result); + + ASSERT_EQ(1UL, conversion_queue_items.size()); + ConversionQueueItemInfo item = conversion_queue_items.front(); + + ASSERT_EQ(conversion.creative_set_id, item.creative_set_id); + ASSERT_EQ(conversion.advertiser_public_key, item.advertiser_public_key); + + const std::string expected_conversion_id = "abc123"; + EXPECT_EQ(expected_conversion_id, item.conversion_id); + }); +} + +TEST_F(BatAdsConversionsTest, ExtractConversionIdWithResourcePatternFromUrl) { + // Arrange + resource::Conversions resource; + resource.Load(); + + ConversionList conversions; + + ConversionInfo conversion; + conversion.advertiser_public_key = + "ofIveUY/bM7qlL9eIkAv/xbjDItFs1xRTTYKRZZsPHI="; + conversion.creative_set_id = "3519f52c-46a4-4c48-9c2b-c264c0067f04"; + conversion.type = "postview"; + conversion.url_pattern = "https://brave.com/foobar?conversion_id=*"; + conversion.observation_window = 3; + conversion.expiry_timestamp = + CalculateExpiryTimestamp(conversion.observation_window); + conversions.push_back(conversion); + + SaveConversions(conversions); + + FireAdEvent(conversion.creative_set_id, ConfirmationType::kViewed); + + // Act + // See associated patterns in the verifiable conversion resource + // /data/test/resources/nnqccijfhvzwyrxpxwjrpmynaiazctqb + conversions_->MaybeConvert( + {"https://foo.bar/", "https://brave.com/foobar?conversion_id=abc123"}, + "
foobar
", resource.get()); + + // Assert + conversion_queue_database_table_->GetAll( + [=](const Result result, + const ConversionQueueItemList& conversion_queue_items) { + ASSERT_EQ(Result::SUCCESS, result); + + ASSERT_EQ(1UL, conversion_queue_items.size()); + ConversionQueueItemInfo item = conversion_queue_items.front(); + + ASSERT_EQ(conversion.creative_set_id, item.creative_set_id); + ASSERT_EQ(conversion.advertiser_public_key, item.advertiser_public_key); + + const std::string expected_conversion_id = "abc123"; + EXPECT_EQ(expected_conversion_id, item.conversion_id); + }); +} + } // namespace ads diff --git a/vendor/bat-native-ads/src/bat/ads/internal/features/conversions/conversions_features.cc b/vendor/bat-native-ads/src/bat/ads/internal/features/conversions/conversions_features.cc new file mode 100644 index 000000000000..cbb2d7ec7051 --- /dev/null +++ b/vendor/bat-native-ads/src/bat/ads/internal/features/conversions/conversions_features.cc @@ -0,0 +1,46 @@ +/* Copyright (c) 2021 The Brave Authors. All rights reserved. + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this file, + * You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include "bat/ads/internal/features/conversions/conversions_features.h" + +#include "bat/ads/internal/features/features_util.h" + +namespace ads { +namespace features { + +namespace { + +const char kFeatureName[] = "Conversions"; + +const char kFieldTrialParameterResourceVersion[] = + "conversions_resource_version"; +const int kDefaultResourceVersion = 1; + +const char kFieldTrialParameterDefaultConversionIdPattern[] = + "conversions_default_conversion_id_pattern"; +const char kDefaultDefaultConversionIdPattern[] = + ""; + +} // namespace + +const base::Feature kFeature{kFeatureName, base::FEATURE_ENABLED_BY_DEFAULT}; + +bool IsConversionsEnabled() { + return base::FeatureList::IsEnabled(kFeature); +} + +int GetConversionsResourceVersion() { + return GetFieldTrialParamByFeatureAsInt( + kFeature, kFieldTrialParameterResourceVersion, kDefaultResourceVersion); +} + +std::string GetGetDefaultConversionIdPattern() { + return GetFieldTrialParamByFeatureAsString( + kFeature, kFieldTrialParameterDefaultConversionIdPattern, + kDefaultDefaultConversionIdPattern); +} + +} // namespace features +} // namespace ads diff --git a/vendor/bat-native-ads/src/bat/ads/internal/features/conversions/conversions_features.h b/vendor/bat-native-ads/src/bat/ads/internal/features/conversions/conversions_features.h new file mode 100644 index 000000000000..60fb6b2d1f61 --- /dev/null +++ b/vendor/bat-native-ads/src/bat/ads/internal/features/conversions/conversions_features.h @@ -0,0 +1,28 @@ +/* Copyright (c) 2021 The Brave Authors. All rights reserved. + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this file, + * You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#ifndef BRAVE_VENDOR_BAT_NATIVE_ADS_SRC_BAT_ADS_INTERNAL_FEATURES_CONVERSIONS_CONVERSIONS_FEATURES_H_ +#define BRAVE_VENDOR_BAT_NATIVE_ADS_SRC_BAT_ADS_INTERNAL_FEATURES_CONVERSIONS_CONVERSIONS_FEATURES_H_ + +#include +#include + +#include "base/feature_list.h" + +namespace ads { +namespace features { + +extern const base::Feature kConversions; + +bool IsConversionsEnabled(); + +int GetConversionsResourceVersion(); + +std::string GetGetDefaultConversionIdPattern(); + +} // namespace features +} // namespace ads + +#endif // BRAVE_VENDOR_BAT_NATIVE_ADS_SRC_BAT_ADS_INTERNAL_FEATURES_CONVERSIONS_CONVERSIONS_FEATURES_H_ diff --git a/vendor/bat-native-ads/src/bat/ads/internal/features/conversions/conversions_features_unittest.cc b/vendor/bat-native-ads/src/bat/ads/internal/features/conversions/conversions_features_unittest.cc new file mode 100644 index 000000000000..c6a920db920d --- /dev/null +++ b/vendor/bat-native-ads/src/bat/ads/internal/features/conversions/conversions_features_unittest.cc @@ -0,0 +1,43 @@ +/* Copyright (c) 2021 The Brave Authors. All rights reserved. + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this file, + * You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include "bat/ads/internal/features/conversions/conversions_features.h" + +#include "bat/ads/internal/unittest_base.h" + +// npm run test -- brave_unit_tests --filter=BatAds* + +namespace ads { + +TEST(BatAdsConversionsFeaturesTest, ConversionsEnabled) { + // Arrange + + // Act + + // Assert + EXPECT_TRUE(features::IsConversionsEnabled()); +} + +TEST(BatAdsConversionsFeaturesTest, ConversionsResourceVersion) { + // Arrange + + // Act + + // Assert + EXPECT_EQ(1, features::GetConversionsResourceVersion()); +} + +TEST(BatAdsConversionsFeaturesTest, DefaultConversionIdPattern) { + // Arrange + + // Act + + // Assert + std::string expected_pattern = + ""; + EXPECT_EQ(expected_pattern, features::GetGetDefaultConversionIdPattern()); +} + +} // namespace ads diff --git a/vendor/bat-native-ads/src/bat/ads/internal/resources/conversions/conversion_id_pattern_info.cc b/vendor/bat-native-ads/src/bat/ads/internal/resources/conversions/conversion_id_pattern_info.cc new file mode 100644 index 000000000000..a655fc3fe205 --- /dev/null +++ b/vendor/bat-native-ads/src/bat/ads/internal/resources/conversions/conversion_id_pattern_info.cc @@ -0,0 +1,28 @@ +/* Copyright (c) 2021 The Brave Authors. All rights reserved. + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this file, + * You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include "bat/ads/internal/resources/conversions/conversion_id_pattern_info.h" + +namespace ads { + +ConversionIdPatternInfo::ConversionIdPatternInfo() = default; + +ConversionIdPatternInfo::ConversionIdPatternInfo( + const ConversionIdPatternInfo& info) = default; + +ConversionIdPatternInfo::~ConversionIdPatternInfo() = default; + +bool ConversionIdPatternInfo::operator==( + const ConversionIdPatternInfo& rhs) const { + return id_pattern == rhs.id_pattern && url_pattern == rhs.url_pattern && + search_in == rhs.search_in; +} + +bool ConversionIdPatternInfo::operator!=( + const ConversionIdPatternInfo& rhs) const { + return !(*this == rhs); +} + +} // namespace ads diff --git a/vendor/bat-native-ads/src/bat/ads/internal/resources/conversions/conversion_id_pattern_info.h b/vendor/bat-native-ads/src/bat/ads/internal/resources/conversions/conversion_id_pattern_info.h new file mode 100644 index 000000000000..f6f29bb10ebc --- /dev/null +++ b/vendor/bat-native-ads/src/bat/ads/internal/resources/conversions/conversion_id_pattern_info.h @@ -0,0 +1,31 @@ +/* Copyright (c) 2021 The Brave Authors. All rights reserved. + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this file, + * You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#ifndef BRAVE_VENDOR_BAT_NATIVE_ADS_SRC_BAT_ADS_INTERNAL_RESOURCES_CONVERSIONS_CONVERSION_ID_PATTERN_INFO_H_ +#define BRAVE_VENDOR_BAT_NATIVE_ADS_SRC_BAT_ADS_INTERNAL_RESOURCES_CONVERSIONS_CONVERSION_ID_PATTERN_INFO_H_ + +#include +#include + +namespace ads { + +struct ConversionIdPatternInfo { + ConversionIdPatternInfo(); + ConversionIdPatternInfo(const ConversionIdPatternInfo& info); + ~ConversionIdPatternInfo(); + + bool operator==(const ConversionIdPatternInfo& rhs) const; + bool operator!=(const ConversionIdPatternInfo& rhs) const; + + std::string id_pattern; + std::string url_pattern; + std::string search_in; +}; + +using ConversionIdPatternMap = std::map; + +} // namespace ads + +#endif // BRAVE_VENDOR_BAT_NATIVE_ADS_SRC_BAT_ADS_INTERNAL_RESOURCES_CONVERSIONS_CONVERSION_ID_PATTERN_INFO_H_ diff --git a/vendor/bat-native-ads/src/bat/ads/internal/resources/conversions/conversions_resource.cc b/vendor/bat-native-ads/src/bat/ads/internal/resources/conversions/conversions_resource.cc new file mode 100644 index 000000000000..c85783ca04fd --- /dev/null +++ b/vendor/bat-native-ads/src/bat/ads/internal/resources/conversions/conversions_resource.cc @@ -0,0 +1,129 @@ +/* Copyright (c) 2021 The Brave Authors. All rights reserved. + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this file, + * You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include "bat/ads/internal/resources/conversions/conversions_resource.h" + +#include + +#include "base/json/json_reader.h" +#include "bat/ads/internal/ads_client_helper.h" +#include "bat/ads/internal/logging.h" +#include "bat/ads/result.h" + +namespace ads { +namespace resource { + +namespace { +const char kResourceId[] = "nnqccijfhvzwyrxpxwjrpmynaiazctqb"; +const char kVersionId = 1; +} // namespace + +Conversions::Conversions() = default; + +Conversions::~Conversions() = default; + +bool Conversions::IsInitialized() const { + return is_initialized_; +} + +void Conversions::Load() { + AdsClientHelper::Get()->LoadAdsResource( + kResourceId, kVersionId, + [=](const Result result, const std::string& json) { + if (result != SUCCESS) { + BLOG(1, "Failed to load resource " << kResourceId); + is_initialized_ = false; + return; + } + + BLOG(1, "Successfully loaded resource " << kResourceId); + + if (!FromJson(json)) { + BLOG(1, "Failed to initialize resource " << kResourceId); + is_initialized_ = false; + return; + } + + is_initialized_ = true; + + BLOG(1, "Successfully initialized resource " << kResourceId); + }); +} + +ConversionIdPatternMap Conversions::get() const { + return conversion_id_patterns_; +} + +/////////////////////////////////////////////////////////////////////////////// + +bool Conversions::FromJson(const std::string& json) { + ConversionIdPatternMap conversion_id_patterns; + + base::Optional root = base::JSONReader::Read(json); + if (!root) { + BLOG(1, "Failed to load from JSON, root missing"); + return false; + } + + if (base::Optional version = root->FindIntPath("version")) { + if (kVersionId != *version) { + BLOG(1, "Failed to load from JSON, version missing"); + return false; + } + } + + base::Value* conversion_id_patterns_value = + root->FindDictPath("conversion_id_patterns"); + if (!conversion_id_patterns_value) { + BLOG(1, "Failed to load from JSON, conversion patterns missing"); + return false; + } + + if (!conversion_id_patterns_value->is_dict()) { + BLOG(1, "Failed to load from JSON, conversion patterns not of type dict"); + return false; + } + + base::DictionaryValue* dict; + if (!conversion_id_patterns_value->GetAsDictionary(&dict)) { + BLOG(1, "Failed to load from JSON, get conversion patterns as dict"); + return false; + } + + for (base::DictionaryValue::Iterator iter(*dict); !iter.IsAtEnd(); + iter.Advance()) { + if (!iter.value().is_dict()) { + BLOG(1, "Failed to load from JSON, conversion pattern not of type dict") + return false; + } + + const std::string* id_pattern = iter.value().FindStringKey("id_pattern"); + if (id_pattern->empty()) { + BLOG(1, "Failed to load from JSON, pattern id_pattern missing"); + return false; + } + + const std::string* search_in = iter.value().FindStringKey("search_in"); + if (search_in->empty()) { + BLOG(1, "Failed to load from JSON, pattern search_in missing"); + return false; + } + + ConversionIdPatternInfo info; + info.id_pattern = *id_pattern; + info.search_in = *search_in; + info.url_pattern = iter.key(); + conversion_id_patterns.insert({info.url_pattern, info}); + } + + conversion_id_patterns_ = conversion_id_patterns; + + BLOG(1, "Parsed verifiable conversion resource version " << kVersionId); + + return true; +} + +} // namespace resource +} // namespace ads diff --git a/vendor/bat-native-ads/src/bat/ads/internal/resources/conversions/conversions_resource.h b/vendor/bat-native-ads/src/bat/ads/internal/resources/conversions/conversions_resource.h new file mode 100644 index 000000000000..4089ead65c6c --- /dev/null +++ b/vendor/bat-native-ads/src/bat/ads/internal/resources/conversions/conversions_resource.h @@ -0,0 +1,42 @@ +/* Copyright (c) 2021 The Brave Authors. All rights reserved. + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this file, + * You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#ifndef BRAVE_VENDOR_BAT_NATIVE_ADS_SRC_BAT_ADS_INTERNAL_RESOURCES_CONVERSIONS_CONVERSIONS_RESOURCE_H_ +#define BRAVE_VENDOR_BAT_NATIVE_ADS_SRC_BAT_ADS_INTERNAL_RESOURCES_CONVERSIONS_CONVERSIONS_RESOURCE_H_ + +#include + +#include "bat/ads/internal/resources/conversions/conversion_id_pattern_info.h" +#include "bat/ads/internal/resources/resource.h" + +namespace ads { +namespace resource { + +class Conversions : public Resource { + public: + Conversions(); + ~Conversions() override; + + Conversions(const Conversions&) = delete; + Conversions& operator=(const Conversions&) = delete; + + bool IsInitialized() const override; + + void Load(); + + ConversionIdPatternMap get() const override; + + private: + bool is_initialized_ = false; + + ConversionIdPatternMap conversion_id_patterns_; + + bool FromJson(const std::string& json); +}; + +} // namespace resource +} // namespace ads + +#endif // BRAVE_VENDOR_BAT_NATIVE_ADS_SRC_BAT_ADS_INTERNAL_RESOURCES_CONVERSIONS_CONVERSIONS_RESOURCE_H_ diff --git a/vendor/bat-native-ads/src/bat/ads/internal/resources/conversions/conversions_resource_unittest.cc b/vendor/bat-native-ads/src/bat/ads/internal/resources/conversions/conversions_resource_unittest.cc new file mode 100644 index 000000000000..2c6a12caf9bb --- /dev/null +++ b/vendor/bat-native-ads/src/bat/ads/internal/resources/conversions/conversions_resource_unittest.cc @@ -0,0 +1,48 @@ +/* Copyright (c) 2021 The Brave Authors. All rights reserved. + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this file, + * You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include "bat/ads/internal/resources/conversions/conversions_resource.h" + +#include "bat/ads/internal/unittest_base.h" +#include "bat/ads/internal/unittest_util.h" + +// npm run test -- brave_unit_tests --filter=BatAds* + +namespace ads { +namespace resource { + +class BatAdsConversionsResourceTest : public UnitTestBase { + protected: + BatAdsConversionsResourceTest() = default; + + ~BatAdsConversionsResourceTest() override = default; +}; + +TEST_F(BatAdsConversionsResourceTest, Load) { + // Arrange + resource::Conversions resource; + + // Act + resource.Load(); + + // Assert + const bool is_initialized = resource.IsInitialized(); + EXPECT_TRUE(is_initialized); +} + +TEST_F(BatAdsConversionsResourceTest, Get) { + // Arrange + resource::Conversions resource; + resource.Load(); + + // Act + ConversionIdPatternMap conversion_id_patterns = resource.get(); + + // Assert + EXPECT_EQ(2u, conversion_id_patterns.size()); +} + +} // namespace resource +} // namespace ads