1 // Copyright 2016 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4 
5 #include "components/ntp_snippets/remote/remote_suggestion_builder.h"
6 
7 #include <limits>
8 #include <memory>
9 
10 #include "base/strings/utf_string_conversions.h"
11 #include "base/time/time.h"
12 #include "components/ntp_snippets/remote/proto/ntp_snippets.pb.h"
13 #include "components/ntp_snippets/time_serialization.h"
14 #include "testing/gtest/include/gtest/gtest.h"
15 
16 namespace ntp_snippets {
17 
18 namespace test {
19 
20 namespace {
21 
GetDefaultSuggestionCreationTime()22 base::Time GetDefaultSuggestionCreationTime() {
23   base::Time out_time;
24   EXPECT_TRUE(base::Time::FromUTCString("2000-01-01T00:00:01Z", &out_time));
25   return out_time;
26 }
27 
GetDefaultSuggestionExpirationTime()28 base::Time GetDefaultSuggestionExpirationTime() {
29   base::Time out_time;
30   EXPECT_TRUE(base::Time::FromUTCString("2100-01-01T00:00:01Z", &out_time));
31   return out_time;
32 }
33 
34 }  // namespace
35 
36 RemoteSuggestionBuilder::RemoteSuggestionBuilder() = default;
37 RemoteSuggestionBuilder::RemoteSuggestionBuilder(
38     const RemoteSuggestionBuilder& other) = default;
39 RemoteSuggestionBuilder::~RemoteSuggestionBuilder() = default;
40 
AddId(const std::string & id)41 RemoteSuggestionBuilder& RemoteSuggestionBuilder::AddId(const std::string& id) {
42   if (!ids_) {
43     ids_ = std::vector<std::string>();
44   }
45   ids_->push_back(id);
46   return *this;
47 }
48 
SetTitle(const std::string & title)49 RemoteSuggestionBuilder& RemoteSuggestionBuilder::SetTitle(
50     const std::string& title) {
51   title_ = title;
52   return *this;
53 }
54 
SetSnippet(const std::string & snippet)55 RemoteSuggestionBuilder& RemoteSuggestionBuilder::SetSnippet(
56     const std::string& snippet) {
57   snippet_ = snippet;
58   return *this;
59 }
60 
SetImageUrl(const std::string & image_url)61 RemoteSuggestionBuilder& RemoteSuggestionBuilder::SetImageUrl(
62     const std::string& image_url) {
63   salient_image_url_ = image_url;
64   return *this;
65 }
66 
SetPublishDate(const base::Time & publish_date)67 RemoteSuggestionBuilder& RemoteSuggestionBuilder::SetPublishDate(
68     const base::Time& publish_date) {
69   publish_date_ = publish_date;
70   return *this;
71 }
72 
SetExpiryDate(const base::Time & expiry_date)73 RemoteSuggestionBuilder& RemoteSuggestionBuilder::SetExpiryDate(
74     const base::Time& expiry_date) {
75   expiry_date_ = expiry_date;
76   return *this;
77 }
78 
SetScore(double score)79 RemoteSuggestionBuilder& RemoteSuggestionBuilder::SetScore(double score) {
80   score_ = score;
81   return *this;
82 }
83 
SetIsDismissed(bool is_dismissed)84 RemoteSuggestionBuilder& RemoteSuggestionBuilder::SetIsDismissed(
85     bool is_dismissed) {
86   is_dismissed_ = is_dismissed;
87   return *this;
88 }
89 
SetRemoteCategoryId(int remote_category_id)90 RemoteSuggestionBuilder& RemoteSuggestionBuilder::SetRemoteCategoryId(
91     int remote_category_id) {
92   remote_category_id_ = remote_category_id;
93   return *this;
94 }
95 
SetUrl(const std::string & url)96 RemoteSuggestionBuilder& RemoteSuggestionBuilder::SetUrl(
97     const std::string& url) {
98   url_ = url;
99   return *this;
100 }
101 
SetPublisher(const std::string & publisher)102 RemoteSuggestionBuilder& RemoteSuggestionBuilder::SetPublisher(
103     const std::string& publisher) {
104   publisher_name_ = publisher;
105   return *this;
106 }
107 
SetAmpUrl(const std::string & amp_url)108 RemoteSuggestionBuilder& RemoteSuggestionBuilder::SetAmpUrl(
109     const std::string& amp_url) {
110   amp_url_ = amp_url;
111   return *this;
112 }
113 
SetFetchDate(const base::Time & fetch_date)114 RemoteSuggestionBuilder& RemoteSuggestionBuilder::SetFetchDate(
115     const base::Time& fetch_date) {
116   fetch_date_ = fetch_date;
117   return *this;
118 }
119 
SetRank(int rank)120 RemoteSuggestionBuilder& RemoteSuggestionBuilder::SetRank(int rank) {
121   rank_ = rank;
122   return *this;
123 }
124 
SetShouldNotify(bool should_notify)125 RemoteSuggestionBuilder& RemoteSuggestionBuilder::SetShouldNotify(
126     bool should_notify) {
127   should_notify_ = should_notify;
128   return *this;
129 }
130 
SetNotificationDeadline(const base::Time & notification_deadline)131 RemoteSuggestionBuilder& RemoteSuggestionBuilder::SetNotificationDeadline(
132     const base::Time& notification_deadline) {
133   notification_deadline_ = notification_deadline;
134   return *this;
135 }
136 
Build() const137 std::unique_ptr<RemoteSuggestion> RemoteSuggestionBuilder::Build() const {
138   SnippetProto proto;
139   proto.set_title(title_.value_or("Title"));
140   proto.set_snippet(snippet_.value_or("Snippet"));
141   proto.set_salient_image_url(
142       salient_image_url_.value_or("http://image_url.com/"));
143   proto.set_publish_date(SerializeTime(
144       publish_date_.value_or(GetDefaultSuggestionCreationTime())));
145   proto.set_expiry_date(SerializeTime(
146       expiry_date_.value_or(GetDefaultSuggestionExpirationTime())));
147   proto.set_score(score_.value_or(1));
148   proto.set_dismissed(is_dismissed_.value_or(false));
149   proto.set_remote_category_id(remote_category_id_.value_or(1));
150   auto* source = proto.mutable_source();
151   source->set_url(url_.value_or("http://url.com/"));
152   source->set_publisher_name(publisher_name_.value_or("Publisher"));
153   source->set_amp_url(amp_url_.value_or("http://amp_url.com/"));
154   proto.set_fetch_date(SerializeTime(fetch_date_.value_or(base::Time::Now())));
155   for (const auto& id :
156        ids_.value_or(std::vector<std::string>{source->url()})) {
157     proto.add_ids(id);
158   }
159   proto.set_rank(rank_.value_or(std::numeric_limits<int>::max()));
160   std::unique_ptr<RemoteSuggestion> suggestion =
161       RemoteSuggestion::CreateFromProto(proto);
162   suggestion->set_should_notify(should_notify_.value_or(false));
163   suggestion->set_notification_deadline(
164       notification_deadline_.value_or(base::Time()));
165   return suggestion;
166 }
167 
168 FetchedCategoryBuilder::FetchedCategoryBuilder() = default;
169 FetchedCategoryBuilder::FetchedCategoryBuilder(
170     const FetchedCategoryBuilder& other) = default;
171 FetchedCategoryBuilder::~FetchedCategoryBuilder() = default;
172 
SetCategory(Category category)173 FetchedCategoryBuilder& FetchedCategoryBuilder::SetCategory(Category category) {
174   category_ = category;
175   return *this;
176 }
177 
SetTitle(const std::string & title)178 FetchedCategoryBuilder& FetchedCategoryBuilder::SetTitle(
179     const std::string& title) {
180   title_ = base::UTF8ToUTF16(title);
181   return *this;
182 }
183 
SetCardLayout(ContentSuggestionsCardLayout card_layout)184 FetchedCategoryBuilder& FetchedCategoryBuilder::SetCardLayout(
185     ContentSuggestionsCardLayout card_layout) {
186   card_layout_ = card_layout;
187   return *this;
188 }
189 
SetAdditionalAction(ContentSuggestionsAdditionalAction additional_action)190 FetchedCategoryBuilder& FetchedCategoryBuilder::SetAdditionalAction(
191     ContentSuggestionsAdditionalAction additional_action) {
192   additional_action_ = additional_action;
193   return *this;
194 }
195 
SetShowIfEmpty(bool show_if_empty)196 FetchedCategoryBuilder& FetchedCategoryBuilder::SetShowIfEmpty(
197     bool show_if_empty) {
198   show_if_empty_ = show_if_empty;
199   return *this;
200 }
201 
SetNoSuggestionsMessage(const std::string & no_suggestions_message)202 FetchedCategoryBuilder& FetchedCategoryBuilder::SetNoSuggestionsMessage(
203     const std::string& no_suggestions_message) {
204   no_suggestions_message_ = base::UTF8ToUTF16(no_suggestions_message);
205   return *this;
206 }
207 
AddSuggestionViaBuilder(const RemoteSuggestionBuilder & builder)208 FetchedCategoryBuilder& FetchedCategoryBuilder::AddSuggestionViaBuilder(
209     const RemoteSuggestionBuilder& builder) {
210   if (!suggestion_builders_) {
211     suggestion_builders_ = std::vector<RemoteSuggestionBuilder>();
212   }
213   suggestion_builders_->push_back(builder);
214   return *this;
215 }
216 
Build() const217 FetchedCategory FetchedCategoryBuilder::Build() const {
218   FetchedCategory result = FetchedCategory(
219       category_.value_or(Category::FromRemoteCategory(1)),
220       CategoryInfo(
221           title_.value_or(base::UTF8ToUTF16("Category title")),
222           card_layout_.value_or(ContentSuggestionsCardLayout::FULL_CARD),
223           additional_action_.value_or(
224               ContentSuggestionsAdditionalAction::FETCH),
225           show_if_empty_.value_or(false),
226           no_suggestions_message_.value_or(
227               base::UTF8ToUTF16("No suggestions message"))));
228 
229   if (suggestion_builders_) {
230     for (const auto& suggestion_builder : *suggestion_builders_)
231       result.suggestions.push_back(suggestion_builder.Build());
232   }
233   return result;
234 }
235 
236 }  // namespace test
237 
238 }  // namespace ntp_snippets
239