Skip to content

Commit 03cd195

Browse files
committed
fix: solve scrims lobby issue
1 parent 6b18fa0 commit 03cd195

2 files changed

Lines changed: 43 additions & 16 deletions

File tree

app/modules/scrims/controllers/lobby_controller.rb

Lines changed: 34 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -47,7 +47,7 @@ def index
4747
def fetch_scrim_entries(game:, region:)
4848
scrims = Scrim.unscoped
4949
.eager_load(:organization)
50-
.includes(:opponent_team, organization: :players)
50+
.includes(:opponent_team)
5151
.where(scrims: { visibility: 'public' })
5252
.where(organizations: { is_public: true })
5353
.where('scrims.scheduled_at >= ?', Time.current)
@@ -58,7 +58,9 @@ def fetch_scrim_entries(game:, region:)
5858
scrims = scrims.where(organizations: { region: region }) if region
5959
scrims = filter_by_tier(scrims, params[:tier]) if params[:tier].present?
6060

61-
scrims.map { |s| serialize_lobby_scrim(s) }
61+
records = scrims.to_a
62+
players_by_org = load_public_players(records.map { |s| s.organization_id })
63+
records.map { |s| serialize_lobby_scrim(s, players_by_org) }
6264
end
6365

6466
# ── Source 2: AvailabilityWindow records → next occurrence ───────────────
@@ -69,18 +71,20 @@ def fetch_window_entries(game:, region:, exclude_org_ids:)
6971
.joins(:organization)
7072
.where(organizations: { is_public: true })
7173
.where.not(organization_id: exclude_org_ids.to_a)
72-
.includes(organization: :players)
74+
.includes(:organization)
7375
.limit(WINDOW_CAP)
7476

7577
windows = windows.where(availability_windows: { game: game }) if game
7678
windows = windows.where(availability_windows: { region: region }) if region
7779

78-
windows.filter_map { |w| serialize_lobby_window(w) }
80+
records = windows.to_a
81+
players_by_org = load_public_players(records.map { |w| w.organization_id })
82+
records.filter_map { |w| serialize_lobby_window(w, players_by_org) }
7983
end
8084

8185
# ── Serializers ───────────────────────────────────────────────────────────
8286

83-
def serialize_lobby_scrim(scrim)
87+
def serialize_lobby_scrim(scrim, players_by_org)
8488
org = scrim.organization
8589
{
8690
id: scrim.id,
@@ -90,15 +94,16 @@ def serialize_lobby_scrim(scrim)
9094
games_planned: scrim.games_planned,
9195
status: scrim.status,
9296
source: scrim.try(:source) || 'internal',
93-
organization: serialize_org(org)
97+
organization: serialize_org(org, players_by_org[org.id] || [])
9498
}
9599
end
96100

97101
# Returns nil if next_occurrence cannot be computed — filter_map drops nils.
98-
def serialize_lobby_window(window)
102+
def serialize_lobby_window(window, players_by_org)
99103
occurs_at = next_occurrence(window)
100104
return nil unless occurs_at
101105

106+
org = window.organization
102107
{
103108
id: "window-#{window.id}", # namespaced to avoid collision with Scrim IDs
104109
scheduled_at: occurs_at,
@@ -107,13 +112,13 @@ def serialize_lobby_window(window)
107112
games_planned: 3,
108113
status: 'open',
109114
source: 'availability_window',
110-
organization: serialize_org(window.organization)
115+
organization: serialize_org(org, players_by_org[org.id] || [])
111116
}
112117
end
113118

114119
# Only expose fields safe for public consumption.
115120
# Notably absent: email, subscription_plan, is_public, internal config.
116-
def serialize_org(org)
121+
def serialize_org(org, players)
117122
{
118123
id: org.id,
119124
name: org.name,
@@ -122,18 +127,18 @@ def serialize_org(org)
122127
tier: org.try(:tier),
123128
public_tagline: org.try(:public_tagline),
124129
discord_invite_url: org.try(:discord_invite_url),
125-
roster: serialize_org_roster(org)
130+
roster: serialize_org_roster(players)
126131
}
127132
end
128133

129-
# Returns the org's active players sorted by role, already preloaded via includes.
134+
# Players are preloaded via load_public_players — no association traversal here.
130135
# Capped at 10 to keep the response lean.
131-
def serialize_org_roster(org)
136+
def serialize_org_roster(players)
132137
role_sort = %w[top jungle mid adc support]
133-
players = org.players.select(&:active?)
134-
players.sort_by { |p| [role_sort.index(p.role) || 99, p.summoner_name] }
135-
.first(10)
136-
.map do |p|
138+
active = players.select { |p| p.status == 'active' && p.deleted_at.nil? }
139+
active.sort_by { |p| [role_sort.index(p.role) || 99, p.summoner_name.to_s] }
140+
.first(10)
141+
.map do |p|
137142
{
138143
summoner_name: p.summoner_name,
139144
role: p.role,
@@ -145,6 +150,19 @@ def serialize_org_roster(org)
145150

146151
# ── Helpers ───────────────────────────────────────────────────────────────
147152

153+
# Loads players for the given org_ids bypassing OrganizationScoped, since
154+
# this is a public endpoint with no authenticated user. Returns a Hash
155+
# keyed by organization_id (UUID string) for O(1) lookup in serializers.
156+
def load_public_players(org_ids)
157+
return {} if org_ids.empty?
158+
159+
Player.unscoped
160+
.where(organization_id: org_ids, deleted_at: nil)
161+
.select(:id, :organization_id, :summoner_name, :role,
162+
:solo_queue_tier, :solo_queue_rank, :status, :deleted_at)
163+
.group_by(&:organization_id)
164+
end
165+
148166
def filter_by_tier(scrims, tier)
149167
tier_plans = case tier
150168
when 'professional' then %w[professional enterprise]
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
# frozen_string_literal: true
2+
3+
class AddGameToScrims < ActiveRecord::Migration[7.1]
4+
def change
5+
add_column :scrims, :game, :string, null: false, default: 'league_of_legends'
6+
add_index :scrims, :game
7+
add_index :scrims, %i[game visibility scheduled_at], name: 'idx_scrims_game_visibility_scheduled'
8+
end
9+
end

0 commit comments

Comments
 (0)