Skip to content

Commit 9de3c7e

Browse files
committed
feat: implement riot sync and scout params
1 parent e989344 commit 9de3c7e

9 files changed

Lines changed: 400 additions & 26 deletions

app/controllers/api/v1/players_controller.rb

Lines changed: 157 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -157,12 +157,118 @@ def matches
157157
end
158158

159159
def import
160-
# This will be implemented when Riot API is ready
161-
render_error(
162-
message: 'Import functionality not yet implemented',
163-
code: 'NOT_IMPLEMENTED',
164-
status: :not_implemented
165-
)
160+
summoner_name = params[:summoner_name]
161+
role = params[:role]
162+
region = params[:region] || 'br1'
163+
164+
# Validate required params
165+
unless summoner_name.present? && role.present?
166+
return render_error(
167+
message: 'Summoner name and role are required',
168+
code: 'MISSING_PARAMETERS',
169+
status: :unprocessable_entity
170+
)
171+
end
172+
173+
# Validate role
174+
unless %w[top jungle mid adc support].include?(role)
175+
return render_error(
176+
message: 'Invalid role',
177+
code: 'INVALID_ROLE',
178+
status: :unprocessable_entity
179+
)
180+
end
181+
182+
# Check if player already exists
183+
existing_player = organization_scoped(Player).find_by(summoner_name: summoner_name)
184+
if existing_player
185+
return render_error(
186+
message: 'Player already exists in your organization',
187+
code: 'PLAYER_EXISTS',
188+
status: :unprocessable_entity
189+
)
190+
end
191+
192+
# Get Riot API key
193+
riot_api_key = ENV['RIOT_API_KEY']
194+
unless riot_api_key.present?
195+
return render_error(
196+
message: 'Riot API key not configured',
197+
code: 'RIOT_API_NOT_CONFIGURED',
198+
status: :service_unavailable
199+
)
200+
end
201+
202+
begin
203+
# Fetch summoner data from Riot API
204+
summoner_data = fetch_summoner_by_name(summoner_name, region, riot_api_key)
205+
ranked_data = fetch_ranked_stats(summoner_data['id'], region, riot_api_key)
206+
207+
# Prepare player data
208+
player_data = {
209+
summoner_name: summoner_name,
210+
role: role,
211+
status: 'active',
212+
riot_puuid: summoner_data['puuid'],
213+
riot_summoner_id: summoner_data['id'],
214+
summoner_level: summoner_data['summonerLevel'],
215+
profile_icon_id: summoner_data['profileIconId'],
216+
sync_status: 'success',
217+
last_sync_at: Time.current
218+
}
219+
220+
# Add ranked stats if available
221+
solo_queue = ranked_data.find { |q| q['queueType'] == 'RANKED_SOLO_5x5' }
222+
if solo_queue
223+
player_data.merge!({
224+
solo_queue_tier: solo_queue['tier'],
225+
solo_queue_rank: solo_queue['rank'],
226+
solo_queue_lp: solo_queue['leaguePoints'],
227+
solo_queue_wins: solo_queue['wins'],
228+
solo_queue_losses: solo_queue['losses']
229+
})
230+
end
231+
232+
flex_queue = ranked_data.find { |q| q['queueType'] == 'RANKED_FLEX_SR' }
233+
if flex_queue
234+
player_data.merge!({
235+
flex_queue_tier: flex_queue['tier'],
236+
flex_queue_rank: flex_queue['rank'],
237+
flex_queue_lp: flex_queue['leaguePoints']
238+
})
239+
end
240+
241+
# Create player
242+
player = organization_scoped(Player).create!(player_data)
243+
244+
log_user_action(
245+
action: 'import_riot',
246+
entity_type: 'Player',
247+
entity_id: player.id,
248+
new_values: player_data
249+
)
250+
251+
render_created({
252+
player: PlayerSerializer.render_as_hash(player),
253+
message: "Player #{summoner_name} imported successfully from Riot API"
254+
})
255+
256+
rescue ActiveRecord::RecordInvalid => e
257+
render_error(
258+
message: "Failed to create player: #{e.message}",
259+
code: 'VALIDATION_ERROR',
260+
status: :unprocessable_entity
261+
)
262+
rescue StandardError => e
263+
Rails.logger.error "Riot API import error: #{e.message}"
264+
Rails.logger.error e.backtrace.join("\n")
265+
266+
render_error(
267+
message: "Failed to import from Riot API: #{e.message}",
268+
code: 'RIOT_API_ERROR',
269+
status: :service_unavailable
270+
)
271+
end
166272
end
167273

168274
def sync_from_riot
@@ -226,6 +332,9 @@ def sync_from_riot
226332
})
227333
end
228334

335+
update_data[:sync_status] = 'success'
336+
update_data[:last_sync_at] = Time.current
337+
229338
@player.update!(update_data)
230339

231340
log_user_action(
@@ -242,6 +351,10 @@ def sync_from_riot
242351

243352
rescue StandardError => e
244353
Rails.logger.error "Riot API sync error: #{e.message}"
354+
355+
# Update sync status to error
356+
@player.update(sync_status: 'error', last_sync_at: Time.current)
357+
245358
render_error(
246359
message: "Failed to sync with Riot API: #{e.message}",
247360
code: 'RIOT_API_ERROR',
@@ -250,6 +363,44 @@ def sync_from_riot
250363
end
251364
end
252365

366+
def bulk_sync
367+
status = params[:status] || 'active'
368+
369+
# Get players to sync
370+
players = organization_scoped(Player).where(status: status)
371+
372+
if players.empty?
373+
return render_error(
374+
message: "No #{status} players found to sync",
375+
code: 'NO_PLAYERS_FOUND',
376+
status: :not_found
377+
)
378+
end
379+
380+
# Check if Riot API is configured
381+
riot_api_key = ENV['RIOT_API_KEY']
382+
unless riot_api_key.present?
383+
return render_error(
384+
message: 'Riot API key not configured',
385+
code: 'RIOT_API_NOT_CONFIGURED',
386+
status: :service_unavailable
387+
)
388+
end
389+
390+
# Queue all players for sync (mark as syncing)
391+
players.update_all(sync_status: 'syncing')
392+
393+
# Perform sync in background
394+
players.each do |player|
395+
SyncPlayerFromRiotJob.perform_later(player.id)
396+
end
397+
398+
render_success({
399+
message: "#{players.count} players queued for sync",
400+
players_count: players.count
401+
})
402+
end
403+
253404
private
254405

255406
def set_player
Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
class Api::V1::RiotIntegrationController < Api::V1::BaseController
2+
def sync_status
3+
players = organization_scoped(Player)
4+
5+
# Calculate statistics
6+
total_players = players.count
7+
synced_players = players.where(sync_status: 'success').count
8+
pending_sync = players.where(sync_status: ['pending', nil]).or(players.where(sync_status: nil)).count
9+
failed_sync = players.where(sync_status: 'error').count
10+
11+
# Players synced in last 24 hours
12+
recently_synced = players.where('last_sync_at > ?', 24.hours.ago).count
13+
14+
# Players that need sync (never synced or synced more than 1 hour ago)
15+
needs_sync = players.where(last_sync_at: nil)
16+
.or(players.where('last_sync_at < ?', 1.hour.ago))
17+
.count
18+
19+
# Get recent syncs (last 10)
20+
recent_syncs = players
21+
.where.not(last_sync_at: nil)
22+
.order(last_sync_at: :desc)
23+
.limit(10)
24+
.map do |player|
25+
{
26+
id: player.id,
27+
summoner_name: player.summoner_name,
28+
last_sync_at: player.last_sync_at,
29+
sync_status: player.sync_status || 'pending'
30+
}
31+
end
32+
33+
render_success({
34+
stats: {
35+
total_players: total_players,
36+
synced_players: synced_players,
37+
pending_sync: pending_sync,
38+
failed_sync: failed_sync,
39+
recently_synced: recently_synced,
40+
needs_sync: needs_sync
41+
},
42+
recent_syncs: recent_syncs
43+
})
44+
end
45+
end

app/controllers/api/v1/scouting/players_controller.rb

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,12 @@ def index
1010
targets = targets.by_priority(params[:priority]) if params[:priority].present?
1111
targets = targets.by_region(params[:region]) if params[:region].present?
1212

13+
# Age range filter
14+
if params[:age_range].present? && params[:age_range].is_a?(Array)
15+
min_age, max_age = params[:age_range]
16+
targets = targets.where(age: min_age..max_age) if min_age && max_age
17+
end
18+
1319
# Special filters
1420
targets = targets.active if params[:active] == 'true'
1521
targets = targets.high_priority if params[:high_priority] == 'true'

app/jobs/application_job.rb

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
class ApplicationJob < ActiveJob::Base
2+
# Automatically retry jobs that encountered a deadlock
3+
# retry_on ActiveRecord::Deadlocked
4+
5+
# Most jobs are safe to ignore if the underlying records are no longer available
6+
# discard_on ActiveJob::DeserializationError
7+
end

0 commit comments

Comments
 (0)