From 7ee93b74317c0b51516146fbe32e72cd9bbb151c Mon Sep 17 00:00:00 2001
From: Claire <claire.github-309c@sitedethib.com>
Date: Tue, 6 Feb 2024 18:10:17 +0100
Subject: [PATCH] Change `source` attribute of `Suggestion` entity in
 `/api/v2/suggestions` back to a string (#29108)

---
 .../components/inline_follow_suggestions.jsx  |  8 ++++----
 app/models/account_suggestions.rb             |  4 ++--
 app/models/account_suggestions/suggestion.rb  |  2 +-
 app/serializers/rest/suggestion_serializer.rb | 15 +++++++++++++-
 spec/requests/api/v2/suggestions_spec.rb      | 20 ++++++++++++++++++-
 .../rest/suggestion_serializer_spec.rb        |  2 +-
 6 files changed, 41 insertions(+), 10 deletions(-)

diff --git a/app/javascript/mastodon/features/home_timeline/components/inline_follow_suggestions.jsx b/app/javascript/mastodon/features/home_timeline/components/inline_follow_suggestions.jsx
index ac414d04d6..f76526e045 100644
--- a/app/javascript/mastodon/features/home_timeline/components/inline_follow_suggestions.jsx
+++ b/app/javascript/mastodon/features/home_timeline/components/inline_follow_suggestions.jsx
@@ -59,7 +59,7 @@ Source.propTypes = {
   id: PropTypes.oneOf(['friends_of_friends', 'similar_to_recently_followed', 'featured', 'most_followed', 'most_interactions']),
 };
 
-const Card = ({ id, source }) => {
+const Card = ({ id, sources }) => {
   const intl = useIntl();
   const account = useSelector(state => state.getIn(['accounts', id]));
   const relationship = useSelector(state => state.getIn(['relationships', id]));
@@ -89,7 +89,7 @@ const Card = ({ id, source }) => {
 
       <div className='inline-follow-suggestions__body__scrollable__card__text-stack'>
         <Link to={`/@${account.get('acct')}`}><DisplayName account={account} /></Link>
-        {firstVerifiedField ? <VerifiedBadge link={firstVerifiedField.get('value')} /> : <Source id={source.get(0)} />}
+        {firstVerifiedField ? <VerifiedBadge link={firstVerifiedField.get('value')} /> : <Source id={sources.get(0)} />}
       </div>
 
       <Button text={intl.formatMessage(following ? messages.unfollow : messages.follow)} onClick={handleFollow} />
@@ -99,7 +99,7 @@ const Card = ({ id, source }) => {
 
 Card.propTypes = {
   id: PropTypes.string.isRequired,
-  source: ImmutablePropTypes.list,
+  sources: ImmutablePropTypes.list,
 };
 
 const DISMISSIBLE_ID = 'home/follow-suggestions';
@@ -175,7 +175,7 @@ export const InlineFollowSuggestions = ({ hidden }) => {
             <Card
               key={suggestion.get('account')}
               id={suggestion.get('account')}
-              source={suggestion.get('source')}
+              sources={suggestion.get('sources')}
             />
           ))}
         </div>
diff --git a/app/models/account_suggestions.rb b/app/models/account_suggestions.rb
index 25c8b04d50..98ccf4ad4f 100644
--- a/app/models/account_suggestions.rb
+++ b/app/models/account_suggestions.rb
@@ -31,12 +31,12 @@ class AccountSuggestions
       account_ids  = account_ids_with_sources[offset, limit]
       accounts_map = Account.where(id: account_ids.map(&:first)).includes(:account_stat, :user).index_by(&:id)
 
-      account_ids.filter_map do |(account_id, source)|
+      account_ids.filter_map do |(account_id, sources)|
         next unless accounts_map.key?(account_id)
 
         AccountSuggestions::Suggestion.new(
           account: accounts_map[account_id],
-          source: source
+          sources: sources
         )
       end
     end
diff --git a/app/models/account_suggestions/suggestion.rb b/app/models/account_suggestions/suggestion.rb
index 2c6f4d27f5..8a5888069a 100644
--- a/app/models/account_suggestions/suggestion.rb
+++ b/app/models/account_suggestions/suggestion.rb
@@ -1,7 +1,7 @@
 # frozen_string_literal: true
 
 class AccountSuggestions::Suggestion < ActiveModelSerializers::Model
-  attributes :account, :source
+  attributes :account, :sources
 
   delegate :id, to: :account, prefix: true
 end
diff --git a/app/serializers/rest/suggestion_serializer.rb b/app/serializers/rest/suggestion_serializer.rb
index 3d697fd9f1..c60f343ba4 100644
--- a/app/serializers/rest/suggestion_serializer.rb
+++ b/app/serializers/rest/suggestion_serializer.rb
@@ -1,7 +1,20 @@
 # frozen_string_literal: true
 
 class REST::SuggestionSerializer < ActiveModel::Serializer
-  attributes :source
+  attributes :source, :sources
 
   has_one :account, serializer: REST::AccountSerializer
+
+  LEGACY_SOURCE_TYPE_MAP = {
+    featured: 'staff',
+    most_followed: 'global',
+    most_interactions: 'global',
+    # NOTE: Those are not completely accurate, but those are personalized interactions
+    similar_to_recently_followed: 'past_interactions',
+    friends_of_friends: 'past_interactions',
+  }.freeze
+
+  def source
+    LEGACY_SOURCE_TYPE_MAP[object.sources.first]
+  end
 end
diff --git a/spec/requests/api/v2/suggestions_spec.rb b/spec/requests/api/v2/suggestions_spec.rb
index 5f1c97b8ae..a7d6a0864f 100644
--- a/spec/requests/api/v2/suggestions_spec.rb
+++ b/spec/requests/api/v2/suggestions_spec.rb
@@ -9,10 +9,28 @@ describe 'Suggestions API' do
   let(:headers) { { 'Authorization' => "Bearer #{token.token}" } }
 
   describe 'GET /api/v2/suggestions' do
-    it 'returns http success' do
+    let(:bob) { Fabricate(:account) }
+    let(:jeff) { Fabricate(:account) }
+    let(:params) { {} }
+
+    before do
+      Setting.bootstrap_timeline_accounts = [bob, jeff].map(&:acct).join(',')
+    end
+
+    it 'returns the expected suggestions' do
       get '/api/v2/suggestions', headers: headers
 
       expect(response).to have_http_status(200)
+
+      expect(body_as_json).to match_array(
+        [bob, jeff].map do |account|
+          hash_including({
+            source: 'staff',
+            sources: ['featured'],
+            account: hash_including({ id: account.id.to_s }),
+          })
+        end
+      )
     end
   end
 end
diff --git a/spec/serializers/rest/suggestion_serializer_spec.rb b/spec/serializers/rest/suggestion_serializer_spec.rb
index 60420d8023..b5efba082d 100644
--- a/spec/serializers/rest/suggestion_serializer_spec.rb
+++ b/spec/serializers/rest/suggestion_serializer_spec.rb
@@ -7,7 +7,7 @@ describe REST::SuggestionSerializer do
   let(:record) do
     AccountSuggestions::Suggestion.new(
       account: account,
-      source: 'SuggestionSource'
+      sources: ['SuggestionSource']
     )
   end
   let(:account) { Fabricate(:account) }