diff --git a/BUILD.gn b/BUILD.gn index 94fad4e..83e8a96 100644 --- a/BUILD.gn +++ b/BUILD.gn @@ -71,6 +71,7 @@ source_set("ads") { "src/client.cc", "src/client_state.cc", "src/math_helper.cc", + "src/error_helper.cc", "src/locale_helper.cc", "src/search_providers.cc", "src/string_helper.cc", @@ -78,6 +79,8 @@ source_set("ads") { "src/json_helper.cc", "src/uri_helper.cc", "src/bat/ads/ad_info.cc", + "src/bat/ads/issuer_info.cc", + "src/bat/ads/issuers_info.cc", "src/bat/ads/notification_info.cc", "src/bat/ads/client_info.cc", "src/bat/ads/url_components.cc", diff --git a/CMakeLists.txt b/CMakeLists.txt index 6c9343e..76086d2 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -56,7 +56,7 @@ message(STATUS "URI Parser dependency") FetchContent_Declare( URIParserProject GIT_REPOSITORY git@github.com:uriparser/uriparser.git - GIT_TAG master + GIT_TAG uriparser-0.9.0 ) FetchContent_GetProperties(URIParserProject) @@ -92,6 +92,8 @@ message(STATUS "BAT Native Ads") set(SOURCES "${PROJECT_SOURCE_DIR}/src/bat/ads/ad_info.cc" "${PROJECT_SOURCE_DIR}/src/bat/ads/client_info.cc" + "${PROJECT_SOURCE_DIR}/src/bat/ads/issuer_info.cc" + "${PROJECT_SOURCE_DIR}/src/bat/ads/issuers_info.cc" "${PROJECT_SOURCE_DIR}/src/bat/ads/notification_info.cc" "${PROJECT_SOURCE_DIR}/src/bat/ads/url_components.cc" "${PROJECT_SOURCE_DIR}/src/ads.cc" @@ -105,6 +107,7 @@ set(SOURCES "${PROJECT_SOURCE_DIR}/src/client_state.cc" "${PROJECT_SOURCE_DIR}/src/json_helper.cc" "${PROJECT_SOURCE_DIR}/src/math_helper.cc" + "${PROJECT_SOURCE_DIR}/src/error_helper.cc" "${PROJECT_SOURCE_DIR}/src/locale_helper.cc" "${PROJECT_SOURCE_DIR}/src/search_providers.cc" "${PROJECT_SOURCE_DIR}/src/string_helper.cc" diff --git a/README.md b/README.md index 06f8d15..53fee13 100644 --- a/README.md +++ b/README.md @@ -20,6 +20,12 @@ bundle-schema.json `catalog-schema.json` and `bundle-schema.json` are JSON Schemas which specify the JSON-based format to define the structure of the JSON data for validation, documentation, and interaction control. It provides the contract for the JSON data and how that data can be modified. +## Build + + mkdir -p build && cd build + cmake .. + make + ## API ### Native @@ -68,7 +74,7 @@ void TabUpdated( const int32_t tab_id, const std::string& url, const bool is_active, - const bool is_incognito); + const bool is_incognito) ``` `TabClosed` should be called to record when a browser tab is closed @@ -175,11 +181,6 @@ void LoadUserModelForLocale( const std::string GenerateUUID() const ``` -`GetSSID` should return the network SSID or an empty string if not available -``` -const std::string GetSSID() const -``` - `IsForeground` should return `true` if the browser is in the foreground otherwise returns `false` ``` bool IsForeground() const @@ -195,14 +196,19 @@ bool IsNotificationsAvailable() const void ShowNotification(std::unique_ptr info) ``` -`CanShowAd` should return `true` if Confirmations is ready to show the ad otherwise returns `false` +`SetCatalogIssuers` should notify that the catalog issuers have changed ``` -bool CanShowAd(const AdInfo& ad_info) + void SetCatalogIssuers(std::unique_ptr info) +``` + +`IsConfirmationsReadyToShowAds` should return `true` if Confirmations is ready to show ads otherwise returns `false` +``` +bool IsConfirmationsReadyToShowAds() ``` `AdSustained` should be called to inform Confirmations that an ad was sustained ``` -void AdSustained(const AdInfo& ad_info) +void AdSustained(std::unique_ptr info) ``` `SetTimer` should create a timer to trigger after the time offset specified in seconds. If the timer was created successfully a unique identifier should be returned, otherwise returns `0` @@ -215,12 +221,6 @@ uint32_t SetTimer(const uint64_t time_offset) void KillTimer(uint32_t timer_id) ``` -`OnCatalogIssuersChanged` should notify that the catalog issuers have changed -``` - void OnCatalogIssuersChanged( - const std::vector& issuers); -``` - `URLRequest` should start a URL request ``` void URLRequest( diff --git a/include/bat/ads/ad_info.h b/include/bat/ads/ad_info.h index 1941421..ef5f0bb 100644 --- a/include/bat/ads/ad_info.h +++ b/include/bat/ads/ad_info.h @@ -9,6 +9,7 @@ #include #include "bat/ads/export.h" +#include "bat/ads/result.h" namespace ads { @@ -18,7 +19,9 @@ struct ADS_EXPORT AdInfo { ~AdInfo(); const std::string ToJson() const; - bool FromJson(const std::string& json); + Result FromJson( + const std::string& json, + std::string* error_description = nullptr); std::string creative_set_id; std::string campaign_id; diff --git a/include/bat/ads/ads.h b/include/bat/ads/ads.h index 5acf085..a1e3e7f 100644 --- a/include/bat/ads/ads.h +++ b/include/bat/ads/ads.h @@ -75,6 +75,9 @@ class ADS_EXPORT Ads { // Should be called to remove all cached history virtual void RemoveAllHistory() = 0; + // Should be called to get the region, i.e. US + virtual std::string GetRegion() = 0; + // Should be called when the user changes the operating system's locale, i.e. // en, en_US or en_GB.UTF-8 unless the operating system restarts the app virtual void ChangeLocale(const std::string& locale) = 0; diff --git a/include/bat/ads/ads_client.h b/include/bat/ads/ads_client.h index a57c0bd..d4f53ab 100644 --- a/include/bat/ads/ads_client.h +++ b/include/bat/ads/ads_client.h @@ -14,12 +14,13 @@ #include #include "bat/ads/ad_info.h" -#include "bat/ads/issuer_info.h" +#include "bat/ads/issuers_info.h" #include "bat/ads/bundle_state.h" #include "bat/ads/client_info.h" #include "bat/ads/export.h" #include "bat/ads/notification_info.h" #include "bat/ads/url_components.h" +#include "bat/ads/result.h" namespace ads { @@ -35,11 +36,6 @@ enum ADS_EXPORT URLRequestMethod { POST = 2 }; -enum ADS_EXPORT Result { - SUCCESS, - FAILED -}; - class ADS_EXPORT LogStream { public: virtual ~LogStream() = default; @@ -110,12 +106,15 @@ class ADS_EXPORT AdsClient { // Should show a notification virtual void ShowNotification(std::unique_ptr info) = 0; + // Should notify that the catalog issuers have changed + virtual void SetCatalogIssuers(std::unique_ptr info) = 0; + // Should return true if Confirmations is ready to show ad otherwise returns // false - virtual bool CanShowAd(const AdInfo& ad_info) = 0; + virtual bool IsConfirmationsReadyToShowAds() = 0; // Should be called to inform Confirmations that an ad was sustained - virtual void AdSustained(const NotificationInfo& info) = 0; + virtual void AdSustained(std::unique_ptr info) = 0; // Should create a timer to trigger after the time offset specified in // seconds. If the timer was created successfully a unique identifier should @@ -125,10 +124,6 @@ class ADS_EXPORT AdsClient { // Should destroy the timer associated with the specified timer identifier virtual void KillTimer(uint32_t timer_id) = 0; - // Should notify that the catalog issuers have changed - virtual void OnCatalogIssuersChanged( - const std::vector& issuers) = 0; - // Should start a URL request virtual void URLRequest( const std::string& url, diff --git a/include/bat/ads/bundle_state.h b/include/bat/ads/bundle_state.h index 0fcc4d2..ea2dee4 100644 --- a/include/bat/ads/bundle_state.h +++ b/include/bat/ads/bundle_state.h @@ -19,7 +19,10 @@ struct BundleState { ~BundleState(); const std::string ToJson() const; - bool FromJson(const std::string& json, const std::string& jsonSchema); + Result FromJson( + const std::string& json, + const std::string& json_schema, + std::string* error_description = nullptr); std::string catalog_id; uint64_t catalog_version; diff --git a/include/bat/ads/client_info.h b/include/bat/ads/client_info.h index 12d919d..22f4962 100644 --- a/include/bat/ads/client_info.h +++ b/include/bat/ads/client_info.h @@ -9,6 +9,7 @@ #include "bat/ads/export.h" #include "bat/ads/client_info_platform_type.h" +#include "bat/ads/result.h" namespace ads { @@ -18,7 +19,9 @@ struct ADS_EXPORT ClientInfo { ~ClientInfo(); const std::string ToJson() const; - bool FromJson(const std::string& json); + Result FromJson( + const std::string& json, + std::string* error_description = nullptr); const std::string GetPlatformName() const; diff --git a/include/bat/ads/issuer_info.h b/include/bat/ads/issuer_info.h index 7b65fe7..89514c4 100644 --- a/include/bat/ads/issuer_info.h +++ b/include/bat/ads/issuer_info.h @@ -7,22 +7,14 @@ #include -namespace ads { - -struct IssuerInfo { - IssuerInfo() : - name(""), - public_key("") {} +#include "bat/ads/export.h" - explicit IssuerInfo(const std::string& public_key) : - name(""), - public_key(public_key) {} - - IssuerInfo(const IssuerInfo& info) : - name(info.name), - public_key(info.public_key) {} +namespace ads { - ~IssuerInfo() {} +struct ADS_EXPORT IssuerInfo { + IssuerInfo(); + IssuerInfo(const IssuerInfo& info); + ~IssuerInfo(); std::string name; std::string public_key; diff --git a/include/bat/ads/issuers_info.h b/include/bat/ads/issuers_info.h new file mode 100644 index 0000000..5fdb082 --- /dev/null +++ b/include/bat/ads/issuers_info.h @@ -0,0 +1,33 @@ +/* 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 BAT_ADS_ISSUERS_INFO_H_ +#define BAT_ADS_ISSUERS_INFO_H_ + +#include +#include + +#include "bat/ads/export.h" +#include "bat/ads/issuer_info.h" +#include "bat/ads/result.h" + +namespace ads { + +struct ADS_EXPORT IssuersInfo { + IssuersInfo(); + IssuersInfo(const IssuersInfo& info); + ~IssuersInfo(); + + const std::string ToJson() const; + Result FromJson( + const std::string& json, + std::string* error_description = nullptr); + + std::string public_key; + std::vector issuers; +}; + +} // namespace ads + +#endif // BAT_ADS_ISSUERS_INFO_H_ diff --git a/include/bat/ads/notification_info.h b/include/bat/ads/notification_info.h index eabdb03..5b32c42 100644 --- a/include/bat/ads/notification_info.h +++ b/include/bat/ads/notification_info.h @@ -8,6 +8,7 @@ #include #include "bat/ads/export.h" +#include "bat/ads/result.h" namespace ads { @@ -17,7 +18,9 @@ struct ADS_EXPORT NotificationInfo { ~NotificationInfo(); const std::string ToJson() const; - bool FromJson(const std::string& json); + Result FromJson( + const std::string& json, + std::string* error_description = nullptr); std::string creative_set_id; std::string category; diff --git a/include/bat/ads/result.h b/include/bat/ads/result.h new file mode 100644 index 0000000..45d42a6 --- /dev/null +++ b/include/bat/ads/result.h @@ -0,0 +1,19 @@ +/* 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 BAT_ADS_RESULT_H_ +#define BAT_ADS_RESULT_H_ + +#include "bat/ads/export.h" + +namespace ads { + +enum ADS_EXPORT Result { + SUCCESS, + FAILED +}; + +} // namespace ads + +#endif // BAT_ADS_RESULT_H_ diff --git a/include/bat/ads/url_components.h b/include/bat/ads/url_components.h index 29153bd..c41924d 100644 --- a/include/bat/ads/url_components.h +++ b/include/bat/ads/url_components.h @@ -8,6 +8,7 @@ #include #include "bat/ads/export.h" +#include "bat/ads/result.h" namespace ads { @@ -17,7 +18,9 @@ struct ADS_EXPORT UrlComponents { ~UrlComponents(); const std::string ToJson() const; - bool FromJson(const std::string& json); + Result FromJson( + const std::string& json, + std::string* error_description = nullptr); std::string url; std::string scheme; diff --git a/src/ads_impl.cc b/src/ads_impl.cc index 334c05f..81c4018 100644 --- a/src/ads_impl.cc +++ b/src/ads_impl.cc @@ -31,8 +31,8 @@ AdsImpl::AdsImpl(AdsClient* ads_client) : is_first_run_(true), is_foreground_(false), media_playing_({}), + last_shown_tab_id_(0), last_shown_tab_url_(""), - last_page_classification_(""), page_score_cache_({}), last_shown_notification_info_(NotificationInfo()), collect_activity_timer_id_(0), @@ -114,7 +114,6 @@ void AdsImpl::Deinitialize() { last_shown_notification_info_ = NotificationInfo(); - last_page_classification_ = ""; page_score_cache_.clear(); is_first_run_ = true; @@ -139,7 +138,7 @@ void AdsImpl::LoadUserModel() { } void AdsImpl::OnUserModelLoaded(const Result result, const std::string& json) { - if (result == FAILED) { + if (result != SUCCESS) { LOG(ERROR) << "Failed to load user model"; return; @@ -255,15 +254,11 @@ void AdsImpl::TabUpdated( client_->UpdateLastUserActivity(); - LoadInfo load_info; - load_info.tab_id = tab_id; - load_info.tab_url = url; - GenerateAdReportingLoadEvent(load_info); - if (is_active) { LOG(INFO) << "TabUpdated.IsFocused for tab id: " << tab_id << " and url: " << url; + last_shown_tab_id_ = tab_id; last_shown_tab_url_ = url; TestShoppingData(url); @@ -313,6 +308,13 @@ void AdsImpl::ConfirmAdUUIDIfAdEnabled() { } } +std::string AdsImpl::GetRegion() { + auto locale = ads_client_->GetAdsLocale(); + auto region = helper::Locale::GetCountryCode(locale); + + return region; +} + void AdsImpl::ChangeLocale(const std::string& locale) { if (!IsInitialized()) { return; @@ -343,18 +345,27 @@ void AdsImpl::ChangeLocale(const std::string& locale) { void AdsImpl::ClassifyPage(const std::string& url, const std::string& html) { if (!IsInitialized()) { + LOG(INFO) << "Site visited " << url << ", not initialized"; + return; } if (IsUrlFromLastShownNotification(url)) { + LOG(INFO) << "Site visited " << url + << ", URL is from last shown notification"; + return; } if (!IsValidScheme(url)) { + LOG(INFO) << "Site visited " << url << ", invalid URL scheme"; + return; } if (TestSearchState(url)) { + LOG(INFO) << "Site visited " << url << ", testing search state"; + return; } @@ -369,7 +380,7 @@ void AdsImpl::ClassifyPage(const std::string& url, const std::string& html) { return; } - last_page_classification_ = winning_category; + client_->SetLastPageClassification(winning_category); client_->AppendPageScoreToPageScoreHistory(page_score); @@ -379,8 +390,16 @@ void AdsImpl::ClassifyPage(const std::string& url, const std::string& html) { auto winner_over_time_category = GetWinnerOverTimeCategory(); LOG(INFO) << "Site visited " << url << ", immediateWinner is " - << last_page_classification_ << " and winnerOverTime is " + << winning_category << " and winnerOverTime is " << winner_over_time_category; + + if (last_shown_tab_url_ == url) { + LoadInfo load_info; + load_info.tab_id = last_shown_tab_id_; + load_info.tab_url = last_shown_tab_url_; + load_info.tab_classification = winning_category; + GenerateAdReportingLoadEvent(load_info); + } } std::string AdsImpl::GetWinnerOverTimeCategory() { @@ -391,9 +410,9 @@ std::string AdsImpl::GetWinnerOverTimeCategory() { uint64_t count = page_score_history.front().size(); - std::vector winner_over_time_page_scores(count); - std::fill(winner_over_time_page_scores.begin(), - winner_over_time_page_scores.end(), 0); + std::vector winner_over_time_page_score(count); + std::fill(winner_over_time_page_score.begin(), + winner_over_time_page_score.end(), 0); for (const auto& page_score : page_score_history) { if (page_score.size() != count) { @@ -401,17 +420,23 @@ std::string AdsImpl::GetWinnerOverTimeCategory() { } for (size_t i = 0; i < page_score.size(); i++) { - winner_over_time_page_scores[i] += page_score[i]; + winner_over_time_page_score[i] += page_score[i]; } } - return GetWinningCategory(winner_over_time_page_scores); + return GetWinningCategory(winner_over_time_page_score); } -std::string AdsImpl::GetWinningCategory(const std::vector& page_score) { +std::string AdsImpl::GetWinningCategory( + const std::vector& page_score) { return user_model_->WinningCategory(page_score); } +std::string AdsImpl::GetWinningCategory(const std::string& html) { + auto page_score = user_model_->ClassifyPage(html); + return GetWinningCategory(page_score); +} + void AdsImpl::CachePageScore( const std::string& url, const std::vector& page_score) { @@ -470,7 +495,7 @@ void AdsImpl::ServeSampleAd() { void AdsImpl::OnLoadSampleBundle( const Result result, const std::string& json) { - if (result == FAILED) { + if (result != SUCCESS) { LOG(ERROR) << "Failed to load sample bundle"; return; @@ -478,10 +503,13 @@ void AdsImpl::OnLoadSampleBundle( LOG(INFO) << "Successfully loaded sample bundle"; - BundleState sample_bundle_state; - if (!sample_bundle_state.FromJson(json, - ads_client_->LoadJsonSchema(_bundle_schema_name))) { - LOG(ERROR) << "Failed to parse sample bundle: " << json; + BundleState state; + std::string error_description; + std::string json_schema = ads_client_->LoadJsonSchema(_bundle_schema_name); + auto json_result = state.FromJson(json, json_schema, &error_description); + if (json_result != SUCCESS) { + LOG(ERROR) << "Failed to parse sample bundle (" << error_description + << "): " << json; return; } @@ -492,8 +520,8 @@ void AdsImpl::OnLoadSampleBundle( // the below code should be abstracted into GetAdForSampleCategory once the // necessary changes have been made in Brave Core by Brian Johnson - auto categories = sample_bundle_state.categories.begin(); - auto categories_count = sample_bundle_state.categories.size(); + auto categories = state.categories.begin(); + auto categories_count = state.categories.size(); if (categories_count == 0) { // TODO(Terry Mancey): Implement Log (#44) // 'Notification not made', { reason: 'no categories' } @@ -556,6 +584,12 @@ void AdsImpl::CheckReadyAdServe(const bool forced) { } if (!forced) { + if (!ads_client_->IsConfirmationsReadyToShowAds()) { + LOG(INFO) << "Notification not made: Confirmations not ready"; + + return; + } + if (!IsMobile() && !IsForeground()) { // TODO(Terry Mancey): Implement Log (#44) // 'Notification not made', { reason: 'not in foreground' } @@ -621,7 +655,7 @@ void AdsImpl::OnGetAds( const std::string& region, const std::string& category, const std::vector& ads) { - if (result == FAILED) { + if (result != SUCCESS) { auto pos = category.find_last_of('-'); if (pos != std::string::npos) { std::string new_category = category.substr(0, pos); @@ -735,17 +769,6 @@ bool AdsImpl::ShowAd( return false; } - if (!ads_client_->CanShowAd(ad_info)) { - LOG(INFO) << "Notification not made: Confirmations not ready" - << std::endl << " advertiser: " << ad_info.advertiser - << std::endl << " notificationText: " << ad_info.notification_text - << std::endl << " notificationUrl: " << ad_info.notification_url - << std::endl << " creativeSetId: " << ad_info.notification_url - << std::endl << " uuid: " << ad_info.notification_url; - - return false; - } - auto notification_info = std::make_unique(); notification_info->advertiser = ad_info.advertiser; notification_info->category = category; @@ -805,10 +828,17 @@ bool AdsImpl::IsAllowedToShowAds() { auto respects_hour_limit = HistoryRespectsRollingTimeConstraint( ads_shown_history, hour_window, hour_allowed); +#if 0 +/* + TBD: [MTR] LEAVE UNTIL DESIGN/PRODUCT RESOLVES THE USE OF THIS FEATURE + */ auto day_window = kOneDayInSeconds; auto day_allowed = ads_client_->GetAdsPerDay(); auto respects_day_limit = HistoryRespectsRollingTimeConstraint( ads_shown_history, day_window, day_allowed); +#else + auto respects_day_limit = true; +#endif auto minimum_wait_time = hour_window / hour_allowed; bool respects_minimum_wait_time = @@ -861,7 +891,6 @@ bool AdsImpl::IsCollectingActivity() const { return true; } - void AdsImpl::StartDeliveringNotifications(const uint64_t start_timer_in) { StopDeliveringNotifications(); @@ -992,6 +1021,10 @@ void AdsImpl::SustainAdInteraction() { LOG(INFO) << "Sustained ad interaction"; GenerateAdReportingSustainEvent(last_shown_notification_info_); + + auto notification_info = + std::make_unique(last_shown_notification_info_); + ads_client_->AdSustained(std::move(notification_info)); } void AdsImpl::StopSustainingAdInteraction() { @@ -1028,6 +1061,10 @@ bool AdsImpl::IsStillViewingAd() const { if (last_shown_notification_info_url_components.hostname != last_shown_tab_url_components.hostname) { + LOG(INFO) << "IsStillViewingAd last_shown_notification_info_url: " + << last_shown_notification_info_url_components.hostname + << " does not match last_shown_tab_url:" + << last_shown_tab_url_components.hostname; return false; } @@ -1035,12 +1072,19 @@ bool AdsImpl::IsStillViewingAd() const { } void AdsImpl::OnTimer(const uint32_t timer_id) { + LOG(INFO) << "OnTimer: " + << std::endl << " timer_id: " << std::to_string(timer_id) + << std::endl << " collect_activity_timer_id_: " << std::to_string(collect_activity_timer_id_) + << std::endl << " deliverying_notifications_timer_id_: " << std::to_string(delivering_notifications_timer_id_) + << std::endl << " sustained_ad_interaction_timer_id_: " << std::to_string(sustained_ad_interaction_timer_id_); if (timer_id == collect_activity_timer_id_) { CollectActivity(); } else if (timer_id == delivering_notifications_timer_id_) { DeliverNotification(); } else if (timer_id == sustained_ad_interaction_timer_id_) { SustainAdInteraction(); + } else { + LOG(WARNING) << "Unexpected OnTimer: " << std::to_string(timer_id); } } @@ -1072,7 +1116,7 @@ void AdsImpl::GenerateAdReportingNotificationShownEvent( writer.String("notificationClassification"); writer.StartArray(); - std::vector classifications; + std::vector classifications = {}; helper::String::Split(info.category, '-', &classifications); for (const auto& classification : classifications) { writer.String(classification.c_str()); @@ -1091,6 +1135,8 @@ void AdsImpl::GenerateAdReportingNotificationShownEvent( writer.EndObject(); + writer.EndObject(); + auto json = buffer.GetString(); ads_client_->EventLog(json); } @@ -1143,7 +1189,7 @@ void AdsImpl::GenerateAdReportingNotificationResultEvent( writer.String("notificationClassification"); writer.StartArray(); - std::vector classifications; + std::vector classifications = {}; helper::String::Split(info.category, '-', &classifications); for (const auto& classification : classifications) { writer.String(classification.c_str()); @@ -1162,6 +1208,8 @@ void AdsImpl::GenerateAdReportingNotificationResultEvent( writer.EndObject(); + writer.EndObject(); + auto json = buffer.GetString(); ads_client_->EventLog(json); } @@ -1191,10 +1239,10 @@ void AdsImpl::GenerateAdReportingSustainEvent( writer.EndObject(); + writer.EndObject(); + auto json = buffer.GetString(); ads_client_->EventLog(json); - - ads_client_->AdSustained(info); } void AdsImpl::GenerateAdReportingLoadEvent( @@ -1233,8 +1281,8 @@ void AdsImpl::GenerateAdReportingLoadEvent( writer.String("tabClassification"); writer.StartArray(); - std::vector classifications; - helper::String::Split(last_page_classification_, '-', &classifications); + std::vector classifications = {}; + helper::String::Split(info.tab_classification, '-', &classifications); for (const auto& classification : classifications) { writer.String(classification.c_str()); } @@ -1252,6 +1300,8 @@ void AdsImpl::GenerateAdReportingLoadEvent( writer.EndObject(); + writer.EndObject(); + auto json = buffer.GetString(); ads_client_->EventLog(json); @@ -1276,6 +1326,8 @@ void AdsImpl::GenerateAdReportingBackgroundEvent() { writer.EndObject(); + writer.EndObject(); + auto json = buffer.GetString(); ads_client_->EventLog(json); } @@ -1298,6 +1350,8 @@ void AdsImpl::GenerateAdReportingForegroundEvent() { writer.EndObject(); + writer.EndObject(); + auto json = buffer.GetString(); ads_client_->EventLog(json); } @@ -1324,6 +1378,8 @@ void AdsImpl::GenerateAdReportingBlurEvent( writer.EndObject(); + writer.EndObject(); + auto json = buffer.GetString(); ads_client_->EventLog(json); } @@ -1350,6 +1406,8 @@ void AdsImpl::GenerateAdReportingDestroyEvent( writer.EndObject(); + writer.EndObject(); + auto json = buffer.GetString(); ads_client_->EventLog(json); } @@ -1376,6 +1434,8 @@ void AdsImpl::GenerateAdReportingFocusEvent( writer.EndObject(); + writer.EndObject(); + auto json = buffer.GetString(); ads_client_->EventLog(json); } @@ -1398,6 +1458,8 @@ void AdsImpl::GenerateAdReportingRestartEvent() { writer.EndObject(); + writer.EndObject(); + auto json = buffer.GetString(); ads_client_->EventLog(json); } @@ -1434,9 +1496,14 @@ void AdsImpl::GenerateAdReportingSettingsEvent() { auto locale = client_->GetLocale(); writer.String(locale.c_str()); +#if 0 +/* + TBD: [MTR] LEAVE UNTIL DESIGN/PRODUCT RESOLVES THE USE OF THIS FEATURE + */ writer.String("adsPerDay"); auto ads_per_day = ads_client_->GetAdsPerDay(); writer.Uint64(ads_per_day); +#endif writer.String("adsPerHour"); auto ads_per_hour = ads_client_->GetAdsPerHour(); @@ -1446,6 +1513,8 @@ void AdsImpl::GenerateAdReportingSettingsEvent() { writer.EndObject(); + writer.EndObject(); + auto json = buffer.GetString(); ads_client_->EventLog(json); } diff --git a/src/ads_impl.h b/src/ads_impl.h index 54a3f69..db2b55b 100644 --- a/src/ads_impl.h +++ b/src/ads_impl.h @@ -62,6 +62,7 @@ class AdsImpl : public Ads { void OnMediaStopped(const int32_t tab_id) override; bool IsMediaPlaying() const; + int32_t last_shown_tab_id_; std::string last_shown_tab_url_; void TabUpdated( const int32_t tab_id, @@ -74,12 +75,14 @@ class AdsImpl : public Ads { void ConfirmAdUUIDIfAdEnabled(); + std::string GetRegion() override; + void ChangeLocale(const std::string& locale) override; void ClassifyPage(const std::string& url, const std::string& html) override; - std::string last_page_classification_; std::string GetWinnerOverTimeCategory(); std::string GetWinningCategory(const std::vector& page_score); + std::string GetWinningCategory(const std::string& html); std::map> page_score_cache_; void CachePageScore( diff --git a/src/ads_serve.cc b/src/ads_serve.cc index 53eaca0..50fd548 100644 --- a/src/ads_serve.cc +++ b/src/ads_serve.cc @@ -130,11 +130,6 @@ bool AdsServe::ProcessCatalog(const std::string& json) { LOG(INFO) << "Parsing catalog"; if (!catalog.FromJson(json)) { - // TODO(Terry Mancey): Implement Log (#44) - // 'Failed to parse catalog' - - LOG(ERROR) << "Failed to parse catalog"; - return false; } @@ -148,24 +143,22 @@ bool AdsServe::ProcessCatalog(const std::string& json) { LOG(INFO) << "Generating bundle"; if (!bundle_->UpdateFromCatalog(catalog)) { - // TODO(Terry Mancey): Implement Log (#44) - // 'Failed to generate bundle' - LOG(ERROR) << "Failed to generate bundle"; return false; } - ads_client_->OnCatalogIssuersChanged(catalog.GetIssuers()); - auto callback = std::bind(&AdsServe::OnCatalogSaved, this, _1); catalog.Save(json, callback); + auto issuers_info = std::make_unique(catalog.GetIssuers()); + ads_client_->SetCatalogIssuers(std::move(issuers_info)); + return true; } void AdsServe::OnCatalogSaved(const Result result) { - if (result == FAILED) { + if (result != SUCCESS) { // If the catalog fails to save, we will retry the next time we collect // activity @@ -202,7 +195,7 @@ void AdsServe::ResetCatalog() { } void AdsServe::OnCatalogReset(const Result result) { - if (result == FAILED) { + if (result != SUCCESS) { LOG(ERROR) << "Failed to reset catalog"; return; diff --git a/src/bat/ads/ad_info.cc b/src/bat/ads/ad_info.cc index 0c353f5..20c3d35 100644 --- a/src/bat/ads/ad_info.cc +++ b/src/bat/ads/ad_info.cc @@ -44,12 +44,18 @@ const std::string AdInfo::ToJson() const { return json; } -bool AdInfo::FromJson(const std::string& json) { +Result AdInfo::FromJson( + const std::string& json, + std::string* error_description) { rapidjson::Document document; document.Parse(json.c_str()); if (document.HasParseError()) { - return false; + if (error_description) { + *error_description = helper::JSON::GetLastError(&document); + } + + return FAILED; } if (document.HasMember("creative_set_id")) { @@ -103,7 +109,7 @@ bool AdInfo::FromJson(const std::string& json) { uuid = document["uuid"].GetString(); } - return true; + return SUCCESS; } void SaveToJson(JsonWriter* writer, const AdInfo& info) { diff --git a/src/bat/ads/client_info.cc b/src/bat/ads/client_info.cc index 696f088..a1f98e3 100644 --- a/src/bat/ads/client_info.cc +++ b/src/bat/ads/client_info.cc @@ -26,12 +26,18 @@ const std::string ClientInfo::ToJson() const { return json; } -bool ClientInfo::FromJson(const std::string& json) { +Result ClientInfo::FromJson( + const std::string& json, + std::string* error_description) { rapidjson::Document document; document.Parse(json.c_str()); if (document.HasParseError()) { - return false; + if (error_description) { + *error_description = helper::JSON::GetLastError(&document); + } + + return FAILED; } if (document.HasMember("application_version")) { @@ -47,7 +53,7 @@ bool ClientInfo::FromJson(const std::string& json) { platform_version = document["platform_version"].GetString(); } - return true; + return SUCCESS; } void SaveToJson(JsonWriter* writer, const ClientInfo& info) { diff --git a/src/bat/ads/issuer_info.cc b/src/bat/ads/issuer_info.cc new file mode 100644 index 0000000..367d873 --- /dev/null +++ b/src/bat/ads/issuer_info.cc @@ -0,0 +1,19 @@ +/* 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/issuer_info.h" + +namespace ads { + +IssuerInfo::IssuerInfo() : + name(""), + public_key("") {} + +IssuerInfo::IssuerInfo(const IssuerInfo& info) : + name(info.name), + public_key(info.public_key) {} + +IssuerInfo::~IssuerInfo() = default; + +} // namespace ads diff --git a/src/bat/ads/issuers_info.cc b/src/bat/ads/issuers_info.cc new file mode 100644 index 0000000..a0a1b84 --- /dev/null +++ b/src/bat/ads/issuers_info.cc @@ -0,0 +1,99 @@ +/* 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/issuers_info.h" + +#include "json_helper.h" + +namespace ads { + +IssuersInfo::IssuersInfo() : + public_key(""), + issuers({}) {} + +IssuersInfo::IssuersInfo(const IssuersInfo& info) : + public_key(info.public_key), + issuers(info.issuers) {} + +IssuersInfo::~IssuersInfo() = default; + +const std::string IssuersInfo::ToJson() const { + std::string json; + SaveToJson(*this, &json); + return json; +} + +Result IssuersInfo::FromJson( + const std::string& json, + std::string* error_description) { + rapidjson::Document document; + document.Parse(json.c_str()); + + if (document.HasParseError()) { + if (error_description) { + *error_description = helper::JSON::GetLastError(&document); + } + + return FAILED; + } + + // Public key + if (!document.HasMember("public_key")) { + if (error_description) { + *error_description = "Catalog issuers public key is missing"; + } + + return FAILED; + } + + public_key = document["public_key"].GetString(); + + // Issuers + if (!document.HasMember("issuers")) { + if (error_description) { + *error_description = "No catalog issuers"; + } + + return FAILED; + } + + std::vector issuers = {}; + for (const auto& issuer : document["issuers"].GetArray()) { + IssuerInfo info; + info.name = issuer["name"].GetString(); + info.public_key = issuer["public_key"].GetString(); + + issuers.push_back(info); + } + + return SUCCESS; +} + +void SaveToJson(JsonWriter* writer, const IssuersInfo& info) { + writer->StartObject(); + + // Public key + writer->String("public_key"); + writer->String(info.public_key.c_str()); + + // Issuers + writer->String("issuers"); + writer->StartArray(); + for (const auto& issuer : info.issuers) { + writer->StartObject(); + + writer->String("name"); + writer->String(issuer.name.c_str()); + + writer->String("public_key"); + writer->String(issuer.public_key.c_str()); + + writer->EndObject(); + } + writer->EndArray(); + + writer->EndObject(); +} + +} // namespace ads diff --git a/src/bat/ads/notification_info.cc b/src/bat/ads/notification_info.cc index b857666..796237a 100644 --- a/src/bat/ads/notification_info.cc +++ b/src/bat/ads/notification_info.cc @@ -32,12 +32,18 @@ const std::string NotificationInfo::ToJson() const { return json; } -bool NotificationInfo::FromJson(const std::string& json) { +Result NotificationInfo::FromJson( + const std::string& json, + std::string* error_description) { rapidjson::Document document; document.Parse(json.c_str()); if (document.HasParseError()) { - return false; + if (error_description != nullptr) { + *error_description = helper::JSON::GetLastError(&document); + } + + return FAILED; } if (document.HasMember("creative_set_id")) { @@ -64,7 +70,7 @@ bool NotificationInfo::FromJson(const std::string& json) { uuid = document["uuid"].GetString(); } - return true; + return SUCCESS; } void SaveToJson(JsonWriter* writer, const NotificationInfo& info) { diff --git a/src/bat/ads/url_components.cc b/src/bat/ads/url_components.cc index 0fafc2d..dd3f712 100644 --- a/src/bat/ads/url_components.cc +++ b/src/bat/ads/url_components.cc @@ -28,12 +28,18 @@ UrlComponents::UrlComponents(const UrlComponents& components) : UrlComponents::~UrlComponents() = default; -bool UrlComponents::FromJson(const std::string& json) { +Result UrlComponents::FromJson( + const std::string& json, + std::string* error_description) { rapidjson::Document document; document.Parse(json.c_str()); if (document.HasParseError()) { - return false; + if (error_description) { + *error_description = helper::JSON::GetLastError(&document); + } + + return FAILED; } if (document.HasMember("url")) { @@ -64,7 +70,7 @@ bool UrlComponents::FromJson(const std::string& json) { fragment = document["fragment"].GetString(); } - return true; + return SUCCESS; } const std::string UrlComponents::ToJson() const { diff --git a/src/bundle.cc b/src/bundle.cc index 8fc3e19..05f3454 100644 --- a/src/bundle.cc +++ b/src/bundle.cc @@ -109,26 +109,27 @@ std::unique_ptr Bundle::GenerateFromCatalog( // Creative Sets for (const auto& creative_set : campaign.creative_sets) { // Segments - std::vector heirarchy = {}; + std::vector hierarchy = {}; for (const auto& segment : creative_set.segments) { auto name = helper::String::ToLower(segment.name); - if (std::find(heirarchy.begin(), heirarchy.end(), name) - != heirarchy.end()) { + if (std::find(hierarchy.begin(), hierarchy.end(), name) + != hierarchy.end()) { continue; } - heirarchy.push_back(name); + hierarchy.push_back(name); } - if (heirarchy.empty()) { + if (hierarchy.empty()) { + LOG(ERROR) << "creativeSet segments are empty"; return nullptr; } std::string category; - helper::String::Join(heirarchy, '-', &category); + helper::String::Join(hierarchy, '-', &category); - auto top_level = heirarchy.front(); + auto top_level = hierarchy.front(); uint64_t entries = 0; for (const auto& creative : creative_set.creatives) { @@ -160,6 +161,7 @@ std::unique_ptr Bundle::GenerateFromCatalog( } if (entries == 0) { + LOG(ERROR) << "creativeSet creatives are empty"; return nullptr; } } @@ -181,7 +183,7 @@ void Bundle::OnStateSaved( const uint64_t& catalog_ping, const uint64_t& catalog_last_updated_timestamp, const Result result) { - if (result == FAILED) { + if (result != SUCCESS) { LOG(ERROR) << "Failed to save bundle state"; // If the bundle fails to save, we will retry the next time a bundle is @@ -203,7 +205,7 @@ void Bundle::OnStateReset( const uint64_t& catalog_ping, const uint64_t& catalog_last_updated_timestamp, const Result result) { - if (result == FAILED) { + if (result != SUCCESS) { LOG(ERROR) << "Failed to reset bundle state"; return; diff --git a/src/bundle_state.cc b/src/bundle_state.cc index 95a453e..dbf488e 100644 --- a/src/bundle_state.cc +++ b/src/bundle_state.cc @@ -31,14 +31,20 @@ const std::string BundleState::ToJson() const { return json; } -bool BundleState::FromJson( +Result BundleState::FromJson( const std::string& json, - const std::string& jsonSchema) { + const std::string& json_schema, + std::string* error_description) { rapidjson::Document bundle; bundle.Parse(json.c_str()); - if (!helper::JSON::Validate(&bundle, jsonSchema)) { - return false; + auto result = helper::JSON::Validate(&bundle, json_schema); + if (result != SUCCESS) { + if (error_description != nullptr) { + *error_description = helper::JSON::GetLastError(&bundle); + } + + return result; } std::map> new_categories = {}; @@ -101,7 +107,7 @@ bool BundleState::FromJson( categories = new_categories; - return true; + return SUCCESS; } void SaveToJson(JsonWriter* writer, const BundleState& state) { diff --git a/src/catalog.cc b/src/catalog.cc index ca1aef5..a177dc2 100755 --- a/src/catalog.cc +++ b/src/catalog.cc @@ -24,9 +24,14 @@ Catalog::~Catalog() {} bool Catalog::FromJson(const std::string& json) { auto catalog_state = std::make_unique(); - auto json_schema = ads_client_->LoadJsonSchema(_catalog_schema_name); - if (!LoadFromJson(catalog_state.get(), json, json_schema)) { + std::string error_description; + auto result = LoadFromJson(catalog_state.get(), json, json_schema, + &error_description); + if (result != SUCCESS) { + LOG(ERROR) << "Failed to to load catalog JSON (" << error_description + << "): " << json; + return false; } @@ -60,7 +65,7 @@ const std::vector& Catalog::GetCampaigns() const { return catalog_state_->campaigns; } -const std::vector& Catalog::GetIssuers() const { +const IssuersInfo& Catalog::GetIssuers() const { return catalog_state_->issuers; } diff --git a/src/catalog.h b/src/catalog.h index fa7374a..38999e5 100755 --- a/src/catalog.h +++ b/src/catalog.h @@ -30,7 +30,7 @@ class Catalog { const std::vector& GetCampaigns() const; - const std::vector& GetIssuers() const; + const IssuersInfo& GetIssuers() const; void Save(const std::string& json, OnSaveCallback callback); void Reset(OnSaveCallback callback); diff --git a/src/catalog_state.cc b/src/catalog_state.cc index 7444c45..b51da1a 100644 --- a/src/catalog_state.cc +++ b/src/catalog_state.cc @@ -14,7 +14,7 @@ CatalogState::CatalogState() : version(0), ping(0), campaigns({}), - issuers({}) {} + issuers(IssuersInfo()) {} CatalogState::CatalogState(const CatalogState& state) : catalog_id(state.catalog_id), @@ -25,21 +25,27 @@ CatalogState::CatalogState(const CatalogState& state) : CatalogState::~CatalogState() = default; -bool CatalogState::FromJson( +Result CatalogState::FromJson( const std::string& json, - const std::string& jsonSchema) { + const std::string& json_schema, + std::string* error_description) { rapidjson::Document catalog; catalog.Parse(json.c_str()); - if (!helper::JSON::Validate(&catalog, jsonSchema)) { - return false; + auto result = helper::JSON::Validate(&catalog, json_schema); + if (result != SUCCESS) { + if (error_description != nullptr) { + *error_description = helper::JSON::GetLastError(&catalog); + } + + return result; } std::string new_catalog_id = ""; uint64_t new_version = 0; uint64_t new_ping = kDefaultCatalogPing * kMillisecondsInASecond; std::vector new_campaigns = {}; - std::vector new_issuers = {}; + IssuersInfo new_issuers = IssuersInfo(); new_catalog_id = catalog["catalogId"].GetString(); @@ -47,7 +53,7 @@ bool CatalogState::FromJson( if (new_version != 1) { // TODO(Terry Mancey): Implement Log (#44) // 'patch invalid', { reason: 'unsupported version', version: version } - return false; + return SUCCESS; } new_ping = catalog["ping"].GetUint64(); @@ -83,10 +89,12 @@ bool CatalogState::FromJson( std::string execution = creative_set["execution"].GetString(); if (execution != "per_click") { - // TODO(Terry Mancey): Implement Log (#44) - // 'Catalog invalid', 'creativeSet with unknown execution: ' - // + creativeSet.execution - return false; + if (error_description != nullptr) { + *error_description = "Catalog invalid: creativeSet has unknown " + "execution: " + execution; + } + + return FAILED; } creative_set_info.execution = execution; @@ -97,9 +105,12 @@ bool CatalogState::FromJson( // Segments auto segments = creative_set["segments"].GetArray(); if (segments.Size() == 0) { - // TODO(Terry Mancey): Implement Log (#44) - // 'Catalog invalid', 'creativeSet with no segments: ' + creativeSetId - return false; + if (error_description != nullptr) { + *error_description = "Catalog invalid: No segments for creativeSet " + "with creativeSetId: " + creative_set_info.creative_set_id; + } + + return FAILED; } for (const auto& segment : segments) { @@ -125,10 +136,13 @@ bool CatalogState::FromJson( std::string name = type["name"].GetString(); if (name != "notification") { - // TODO(Terry Mancey): Implement Log (#44) - // 'Catalog invalid', 'creative with invalid type: ' - // + creative.creativeId + ' type=' + type - return false; + if (error_description != nullptr) { + *error_description = "Catalog invalid: Invalid creative type: " + + name + " for creativeInstanceId: " + + creative_info.creative_instance_id; + } + + return FAILED; } creative_info.type.name = name; @@ -156,10 +170,18 @@ bool CatalogState::FromJson( for (const auto& issuer : catalog["issuers"].GetArray()) { IssuerInfo issuer_info; - issuer_info.name = issuer["name"].GetString(); - issuer_info.public_key = issuer["publicKey"].GetString(); + std::string name = issuer["name"].GetString(); + std::string public_key = issuer["publicKey"].GetString(); + + if (name == "confirmation") { + new_issuers.public_key = public_key; + continue; + } + + issuer_info.name = name; + issuer_info.public_key = public_key; - new_issuers.push_back(issuer_info); + new_issuers.issuers.push_back(issuer_info); } catalog_id = new_catalog_id; @@ -168,7 +190,7 @@ bool CatalogState::FromJson( campaigns = new_campaigns; issuers = new_issuers; - return true; + return SUCCESS; } } // namespace ads diff --git a/src/catalog_state.h b/src/catalog_state.h index cd74d75..0e700d9 100644 --- a/src/catalog_state.h +++ b/src/catalog_state.h @@ -10,7 +10,7 @@ #include #include "campaign_info.h" -#include "bat/ads/issuer_info.h" +#include "bat/ads/issuers_info.h" #include "json_helper.h" namespace ads { @@ -20,13 +20,16 @@ struct CatalogState { explicit CatalogState(const CatalogState& state); ~CatalogState(); - bool FromJson(const std::string& json, const std::string& jsonSchema); + Result FromJson( + const std::string& json, + const std::string& json_schema, + std::string* error_description = nullptr); std::string catalog_id; uint64_t version; uint64_t ping; std::vector campaigns; - std::vector issuers; + IssuersInfo issuers; }; } // namespace ads diff --git a/src/client.cc b/src/client.cc index b60db06..d5883e5 100644 --- a/src/client.cc +++ b/src/client.cc @@ -179,9 +179,20 @@ const std::vector Client::GetLocales() { return client_state_->locales; } +void Client::SetLastPageClassification( + const std::string& classification) { + client_state_->last_page_classification = classification; + + SaveState(); +} + +const std::string Client::GetLastPageClassification() { + return client_state_->last_page_classification; +} + void Client::AppendPageScoreToPageScoreHistory( - const std::vector& page_scores) { - client_state_->page_score_history.push_front(page_scores); + const std::vector& page_score) { + client_state_->page_score_history.push_front(page_score); if (client_state_->page_score_history.size() > kMaximumEntriesInPageScoreHistory) { client_state_->page_score_history.pop_back(); @@ -241,7 +252,7 @@ void Client::RemoveAllHistory() { /////////////////////////////////////////////////////////////////////////////// void Client::OnStateSaved(const Result result) { - if (result == FAILED) { + if (result != SUCCESS) { LOG(ERROR) << "Failed to save client state"; return; @@ -251,14 +262,12 @@ void Client::OnStateSaved(const Result result) { } void Client::OnStateLoaded(const Result result, const std::string& json) { - if (result == FAILED) { + if (result != SUCCESS) { LOG(ERROR) << "Failed to load client state, resetting to default values"; client_state_.reset(new ClientState()); } else { if (!FromJson(json)) { - LOG(ERROR) << "Failed to parse client state: " << json; - return; } @@ -270,7 +279,12 @@ void Client::OnStateLoaded(const Result result, const std::string& json) { bool Client::FromJson(const std::string& json) { ClientState state; - if (!LoadFromJson(&state, json)) { + std::string error_description; + auto result = LoadFromJson(&state, json, &error_description); + if (result != SUCCESS) { + LOG(ERROR) << "Failed to parse client JSON (" << error_description << + "): " << json; + return false; } diff --git a/src/client.h b/src/client.h index 07fda8a..f7f5916 100644 --- a/src/client.h +++ b/src/client.h @@ -49,8 +49,10 @@ class Client { const std::string GetLocale(); void SetLocales(const std::vector& locales); const std::vector GetLocales(); + void SetLastPageClassification(const std::string& classification); + const std::string GetLastPageClassification(); void AppendPageScoreToPageScoreHistory( - const std::vector& page_scores); + const std::vector& page_score); const std::deque> GetPageScoreHistory(); void AppendCurrentTimeToCreativeSetHistory( const std::string& creative_set_id); diff --git a/src/client_state.cc b/src/client_state.cc index 835ad07..a92543a 100644 --- a/src/client_state.cc +++ b/src/client_state.cc @@ -20,6 +20,7 @@ ClientState::ClientState() : last_user_idle_stop_time(0), locale(kDefaultLanguageCode), locales({}), + last_page_classification(""), page_score_history({}), creative_set_history({}), campaign_history({}), @@ -40,6 +41,7 @@ ClientState::ClientState(const ClientState& state) : last_user_idle_stop_time(state.last_user_idle_stop_time), locale(state.locale), locales(state.locales), + last_page_classification(state.last_page_classification), page_score_history(state.page_score_history), creative_set_history(state.creative_set_history), campaign_history(state.campaign_history), @@ -57,12 +59,18 @@ const std::string ClientState::ToJson() { return json; } -bool ClientState::FromJson(const std::string& json) { +Result ClientState::FromJson( + const std::string& json, + std::string* error_description) { rapidjson::Document client; client.Parse(json.c_str()); if (client.HasParseError()) { - return false; + if (error_description) { + *error_description = helper::JSON::GetLastError(&client); + } + + return FAILED; } if (client.HasMember("adsShownHistory")) { @@ -112,8 +120,13 @@ bool ClientState::FromJson(const std::string& json) { } } + if (client.HasMember("last_page_classification")) { + last_page_classification = client["last_page_classification"].GetString(); + } + if (client.HasMember("pageScoreHistory")) { - for (const auto& history : client["pageScoreHistory"].GetArray()) { + for (const auto& history : + client["pageScoreHistory"].GetArray()) { std::vector page_scores = {}; for (const auto& page_score : history.GetArray()) { @@ -172,7 +185,7 @@ bool ClientState::FromJson(const std::string& json) { shop_url = client["shopUrl"].GetString(); } - return true; + return SUCCESS; } void SaveToJson(JsonWriter* writer, const ClientState& state) { @@ -221,12 +234,15 @@ void SaveToJson(JsonWriter* writer, const ClientState& state) { } writer->EndArray(); + writer->String("last_page_classification"); + writer->String(state.last_page_classification.c_str()); + writer->String("pageScoreHistory"); writer->StartArray(); - for (const auto& history : state.page_score_history) { + for (const auto& page_score : state.page_score_history) { writer->StartArray(); - for (const auto& pageScore : history) { - writer->Double(pageScore); + for (const auto& score : page_score) { + writer->Double(score); } writer->EndArray(); } diff --git a/src/client_state.h b/src/client_state.h index cd2171e..4366c58 100644 --- a/src/client_state.h +++ b/src/client_state.h @@ -10,6 +10,8 @@ #include #include +#include "bat/ads/result.h" + namespace ads { struct ClientState { @@ -18,7 +20,9 @@ struct ClientState { ~ClientState(); const std::string ToJson(); - bool FromJson(const std::string& json); + Result FromJson( + const std::string& json, + std::string* error_description = nullptr); std::deque ads_shown_history; std::string ad_uuid; @@ -31,6 +35,7 @@ struct ClientState { uint64_t last_user_idle_stop_time; std::string locale; std::vector locales; + std::string last_page_classification; std::deque> page_score_history; std::map> creative_set_history; std::map> campaign_history; diff --git a/src/error_helper.cc b/src/error_helper.cc new file mode 100644 index 0000000..3e6753b --- /dev/null +++ b/src/error_helper.cc @@ -0,0 +1,27 @@ +/* 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 + +#include "error_helper.h" + +namespace helper { + +const std::string GetDescription(const ads::Result result) { + std::string description = ""; + + switch (result) { + case ads::Result::SUCCESS: + description = "Successful"; + break; + + case ads::Result::FAILED: + description = "Failed"; + break; + } + + return description; +} + +} // namespace helper diff --git a/src/error_helper.h b/src/error_helper.h new file mode 100644 index 0000000..a85a5f9 --- /dev/null +++ b/src/error_helper.h @@ -0,0 +1,21 @@ +/* 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 BAT_ADS_ERROR_HELPER_H_ +#define BAT_ADS_ERROR_HELPER_H_ + +#include + +#include "bat/ads/result.h" + +namespace helper { + +class Error { + public: + static const std::string GetDescription(const ads::Result result); +}; + +} // namespace helper + +#endif // BAT_ADS_ERROR_HELPER_H_ diff --git a/src/event_type_load_info.h b/src/event_type_load_info.h index 3564bb9..e11b68a 100644 --- a/src/event_type_load_info.h +++ b/src/event_type_load_info.h @@ -12,16 +12,19 @@ namespace ads { struct LoadInfo { LoadInfo() : tab_id(-1), - tab_url("") {} + tab_url(""), + tab_classification("") {} explicit LoadInfo(const LoadInfo& info) : tab_id(info.tab_id), - tab_url(info.tab_url) {} + tab_url(info.tab_url), + tab_classification(info.tab_classification) {} ~LoadInfo() {} int32_t tab_id; std::string tab_url; + std::string tab_classification; }; } // namespace ads diff --git a/src/json_helper.cc b/src/json_helper.cc index 258b96e..cc4890c 100644 --- a/src/json_helper.cc +++ b/src/json_helper.cc @@ -8,27 +8,42 @@ namespace helper { -bool JSON::Validate( - rapidjson::Document *document, - const std::string& jsonSchema) { +ads::Result JSON::Validate( + rapidjson::Document* document, + const std::string& json_schema) { + if (!document) { + return ads::Result::FAILED; + } + if (document->HasParseError()) { - return false; + return ads::Result::FAILED; } rapidjson::Document document_schema; - document_schema.Parse(jsonSchema.c_str()); + document_schema.Parse(json_schema.c_str()); if (document_schema.HasParseError()) { - return false; + return ads::Result::FAILED; } rapidjson::SchemaDocument schema(document_schema); rapidjson::SchemaValidator validator(schema); if (!document->Accept(validator)) { - return false; + return ads::Result::FAILED; + } + + return ads::Result::SUCCESS; +} + +std::string JSON::GetLastError(rapidjson::Document* document) { + if (!document) { + return "Invalid document"; } - return true; + auto parse_error_code = document->GetParseError(); + std::string description(rapidjson::GetParseError_En(parse_error_code)); + std::string error_offset = std::to_string(document->GetErrorOffset()); + return description + " (" + error_offset + ")"; } } // namespace helper diff --git a/src/json_helper.h b/src/json_helper.h index 6343d6e..6d06229 100644 --- a/src/json_helper.h +++ b/src/json_helper.h @@ -5,16 +5,20 @@ #ifndef BAT_ADS_JSON_HELPER_H_ #define BAT_ADS_JSON_HELPER_H_ +#include + #include "rapidjson/document.h" #include "rapidjson/error/en.h" #include "rapidjson/stringbuffer.h" #include "rapidjson/writer.h" #include "rapidjson/schema.h" +#include "bat/ads/result.h" namespace ads { -struct AdInfo; struct ClientInfo; +struct IssuersInfo; +struct AdInfo; struct NotificationInfo; struct UrlComponents; struct ClientState; @@ -22,8 +26,9 @@ struct BundleState; using JsonWriter = rapidjson::Writer; -void SaveToJson(JsonWriter* writer, const AdInfo& info); void SaveToJson(JsonWriter* writer, const ClientInfo& info); +void SaveToJson(JsonWriter* writer, const IssuersInfo& info); +void SaveToJson(JsonWriter* writer, const AdInfo& info); void SaveToJson(JsonWriter* writer, const NotificationInfo& info); void SaveToJson(JsonWriter* writer, const UrlComponents& components); void SaveToJson(JsonWriter* writer, const ClientState& state); @@ -39,16 +44,20 @@ void SaveToJson(const T& t, std::string* json) { } template -bool LoadFromJson(T* t, const std::string& json) { - return t->FromJson(json); +Result LoadFromJson( + T* t, + const std::string& json, + std::string* error_description) { + return t->FromJson(json, error_description); } template -bool LoadFromJson( +Result LoadFromJson( T* t, const std::string& json, - const std::string& jsonSchema) { - return t->FromJson(json, jsonSchema); + const std::string& json_schema, + std::string* error_description) { + return t->FromJson(json, json_schema, error_description); } } // namespace ads @@ -57,9 +66,11 @@ namespace helper { class JSON { public: - static bool Validate( - rapidjson::Document *document, - const std::string& jsonSchema); + static ads::Result Validate( + rapidjson::Document* document, + const std::string& json_schema); + + static std::string GetLastError(rapidjson::Document* document); }; } // namespace helper diff --git a/src/locale_helper.cc b/src/locale_helper.cc index cfb007d..abb3ede 100644 --- a/src/locale_helper.cc +++ b/src/locale_helper.cc @@ -13,7 +13,7 @@ namespace helper { const std::string Locale::GetLanguageCode(const std::string& locale) { - std::vector locale_components; + std::vector locale_components = {}; if (!helper::String::Split(locale, '_', &locale_components)) { return ads::kDefaultLanguageCode; } @@ -23,7 +23,7 @@ const std::string Locale::GetLanguageCode(const std::string& locale) { } const std::string Locale::GetCountryCode(const std::string& locale) { - std::vector locale_components; + std::vector locale_components = {}; if (!helper::String::Split(locale, '.', &locale_components)) { return ads::kDefaultCountryCode; } @@ -31,7 +31,7 @@ const std::string Locale::GetCountryCode(const std::string& locale) { auto normalized_locale = locale_components.front(); std::replace(normalized_locale.begin(), normalized_locale.end(), '-', '_'); - std::vector country_code_components; + std::vector country_code_components = {}; helper::String::Split(normalized_locale, '_', &country_code_components); if (country_code_components.size() != 2) { return ads::kDefaultCountryCode; diff --git a/src/locale_helper.h b/src/locale_helper.h index 8deaa1f..4955462 100644 --- a/src/locale_helper.h +++ b/src/locale_helper.h @@ -5,6 +5,8 @@ #ifndef BAT_ADS_LOCALE_HELPER_H_ #define BAT_ADS_LOCALE_HELPER_H_ +#include + namespace helper { class Locale { diff --git a/src/mock_ads_client.cc b/src/mock_ads_client.cc index 9d3d0f6..c52028d 100644 --- a/src/mock_ads_client.cc +++ b/src/mock_ads_client.cc @@ -11,6 +11,7 @@ #include "mock_ads_client.h" #include "bat/ads/bundle_state.h" #include "bat/ads/ad_info.h" +#include "bat/ads/issuers_info.h" #include "math_helper.h" #include "string_helper.h" #include "time_helper.h" @@ -140,8 +141,7 @@ bool MockAdsClient::IsNotificationsAvailable() const { return true; } -void MockAdsClient::ShowNotification( - std::unique_ptr info) { +void MockAdsClient::ShowNotification(std::unique_ptr info) { std::cout << std::endl << "------------------------------------------------"; std::cout << std::endl << "Notification shown:"; std::cout << std::endl << " advertiser: " << info->advertiser; @@ -151,12 +151,15 @@ void MockAdsClient::ShowNotification( std::cout << std::endl << " uuid: " << info->uuid; } -bool MockAdsClient::CanShowAd(const AdInfo& ad_info) { - (void)ad_info; +void MockAdsClient::SetCatalogIssuers(std::unique_ptr info) { + (void)info; +} + +bool MockAdsClient::IsConfirmationsReadyToShowAds() { return true; } -void MockAdsClient::AdSustained(const NotificationInfo& info) { +void MockAdsClient::AdSustained(std::unique_ptr info) { (void)info; } @@ -173,11 +176,6 @@ void MockAdsClient::KillTimer(uint32_t timer_id) { (void)timer_id; } -void MockAdsClient::OnCatalogIssuersChanged( - const std::vector& issuers) { - (void)issuers; -} - void MockAdsClient::URLRequest( const std::string& url, const std::vector& headers, @@ -392,17 +390,19 @@ void MockAdsClient::LoadBundleState() { void MockAdsClient::OnBundleStateLoaded( const Result result, const std::string& json) { - if (result == FAILED) { + if (result != SUCCESS) { LOG(LOG_ERROR) << "Failed to load bundle: " << json; return; } - auto json_schema = LoadJsonSchema(_bundle_schema_name); - BundleState state; - if (!state.FromJson(json, json_schema)) { - LOG(LOG_ERROR) << "Failed to parse bundle: " << json; + auto json_schema = LoadJsonSchema(_bundle_schema_name); + std::string error_description; + auto json_result = state.FromJson(json, json_schema, &error_description); + if (json_result != SUCCESS) { + LOG(LOG_ERROR) << "Failed to parse bundle (" << error_description + << "): " << json; return; } @@ -426,17 +426,19 @@ void MockAdsClient::LoadSampleBundleState() { void MockAdsClient::OnSampleBundleStateLoaded( const Result result, const std::string& json) { - if (result == FAILED) { + if (result != SUCCESS) { LOG(LOG_ERROR) << "Failed to load sample bundle"; return; } - auto json_schema = LoadJsonSchema(_bundle_schema_name); - BundleState state; - if (!state.FromJson(json, json_schema)) { - LOG(LOG_ERROR) << "Failed to parse sample bundle: " << json; + auto json_schema = LoadJsonSchema(_bundle_schema_name); + std::string error_description; + auto json_result = state.FromJson(json, json_schema, &error_description); + if (json_result != SUCCESS) { + LOG(LOG_ERROR) << "Failed to parse sample bundle (" << error_description + << "): " << json; return; } diff --git a/src/mock_ads_client.h b/src/mock_ads_client.h index 5465581..b4cde90 100644 --- a/src/mock_ads_client.h +++ b/src/mock_ads_client.h @@ -53,16 +53,13 @@ class MockAdsClient : public AdsClient { bool IsNotificationsAvailable() const override; void ShowNotification(std::unique_ptr info) override; - bool CanShowAd(const AdInfo& ad_info) override; - - void AdSustained(const NotificationInfo& info) override; + void SetCatalogIssuers(std::unique_ptr info) override; + bool IsConfirmationsReadyToShowAds() override; + void AdSustained(std::unique_ptr info) override; uint32_t SetTimer(const uint64_t time_offset) override; void KillTimer(const uint32_t timer_id) override; - void OnCatalogIssuersChanged( - const std::vector& issuers) override; - void URLRequest( const std::string& url, const std::vector& headers, diff --git a/test/mock_ads_client.h b/test/mock_ads_client.h index 2f849d7..4bc445d 100644 --- a/test/mock_ads_client.h +++ b/test/mock_ads_client.h @@ -89,18 +89,17 @@ class MockAdsClient : public AdsClient { MOCK_METHOD1(ShowNotification, void( std::unique_ptr info)); - MOCK_METHOD1(CanShowAd, bool( - const AdInfo& ad_info)); + MOCK_METHOD1(SetCatalogIssuers, void( + std::unique_ptr info)); + + MOCK_METHOD0(IsConfirmationsReadyToShowAds, bool()); MOCK_METHOD1(AdSustained, void( - const NotificationInfo& info)); + std::unique_ptr info)); MOCK_METHOD1(SetTimer, uint32_t( const uint64_t time_offset)); - MOCK_METHOD1(OnCatalogIssuersChanged, void( - const std::vector& issuers)); - MOCK_METHOD1(KillTimer, void( uint32_t timer_id)); diff --git a/test/test_tabs.cc b/test/test_tabs.cc index e73e677..ca0b15a 100644 --- a/test/test_tabs.cc +++ b/test/test_tabs.cc @@ -48,8 +48,7 @@ void SuccessfullyLoadUserModelForLocale( callback(SUCCESS, value); } -const std::string SuccessfullyLoadJsonSchema( - const std::string& name) { +const std::string SuccessfullyLoadJsonSchema(const std::string& name) { std::string path = "mock_data/" + name; std::ifstream ifs{path}; @@ -66,7 +65,7 @@ const std::string SuccessfullyLoadJsonSchema( class TabsTest : public ::testing::Test { protected: - MockAdsClient *mock_ads_client; + MockAdsClient* mock_ads_client; AdsImpl* ads; TabsTest() :