Underdog Fantasy Backend Challenge
Clone the repo - https://github.com/spimonid/players_api.git
cd players_api
run rails db:create
run rails db:migrate
run rake initial_import to import the data from CBS
run rails server
🟥 import from CBS
task :initial_import => :environment do
baseball_players = HTTParty . get ( "https://api.cbssports.com/fantasy/players/list?version=3.0&SPORT=baseball&response_format=JSON" ) [ "body" ] [ "players" ]
basketball_players = HTTParty . get ( "https://api.cbssports.com/fantasy/players/list?version=3.0&SPORT=basketball&response_format=JSON" ) [ "body" ] [ "players" ]
football_players = HTTParty . get ( "https://api.cbssports.com/fantasy/players/list?version=3.0&SPORT=football&response_format=JSON" ) [ "body" ] [ "players" ]
🟦 persist by setting up the table and saving each record with custom logic on name_brief and average_position_age_diff .
all_players . each do |player |
generator = BriefNameGenerator . new
name_brief = generator . generate_brief_name ( player [ "firstname" ] , player [ "lastname" ] , player [ "sport" ] )
player_record = Player . new ( { full_name : player [ "fullname" ] ,
first_name : player [ "firstname" ] ,
last_name : player [ "lastname" ] ,
position : player [ "position" ] ,
age : player [ "age" ] ,
sport : player [ "sport" ] ,
name_brief : name_brief ,
average_position_age_diff : player [ "age" ] ? player [ "age" ] - average_age_by_position [ player [ "position" ] ] : nil
} )
player_record . save
end
🟩 Routing - an endpoint to show an individual player and a search endpoint
Rails . application . routes . draw do
get "/players/:id" , to : "players#show"
get "/search" , to : "players#search"
end
class PlayersController < ApplicationController
def show
@player = Player . find ( params [ :id ] )
render :json => @player . to_json ( :except => [ :full_name , :sport , :created_at , :updated_at ] )
end
def search
@result = Player . search ( params [ "player" ] )
feeling_lucky = params [ "player" ] . keys . include? ( "feeling_lucky" )
result = feeling_lucky ? @result [ 0 ] : @result
render :json => result . to_json ( :except => [ :full_name , :sport , :created_at , :updated_at ] )
end
private
def permitted_search_params
params . permit ( :sport , :position , :age , :min_age , :max_age , :first_letter_last_name )
end
end
🟨 Scoping and logic to power the search endpoint
class Player < ApplicationRecord
scope :in_age_range , -> ( min_age , max_age ) { where ( "players.age >= ? AND players.age <= ?" , min_age , max_age ) }
scope :match_first_letter , -> ( first_letter_last_name ) { where ( "substr(players.last_name, 1, 1) = ?" , first_letter_last_name ) }
validates :first_name , presence : true
validates :last_name , presence : true
validates :name_brief , presence : true
def self . search ( params )
players = Player . all
players = players . in_age_range ( params [ "min_age" ] , params [ "max_age" ] ) if params [ "min_age" ] && params [ "max_age" ]
players = players . match_first_letter ( params [ "first_letter_last_name" ] ) if params [ "first_letter_last_name" ]
players = players . where ( sport : params [ "sport" ] ) if params [ "sport" ]
players = players . where ( age : params [ "age" ] ) if params [ "age" ]
players = players . where ( position : params [ "position" ] ) if params [ "position" ]
players
end
end
If feeling_lucky param is present, returns just the first result of your search, otherwise there could be multiple records in the response
def search
@result = Player . search ( params [ "player" ] )
feeling_lucky = params [ "player" ] . keys . include? ( "feeling_lucky" )
result = feeling_lucky ? @result [ 0 ] : @result
render :json => result . to_json ( :except => [ :full_name , :sport , :created_at , :updated_at ] )
end
Fire up your preferred API testing tool
Run rails test test/models/player_test.rb and rails test test/controllers/players_controller_test.rb
A couple things that would come next that I couldn't get to
Refactoring the search from three .wheres in a row to something more concise
More robust testing set up and execution