├── .rspec
├── spec
├── fixtures
│ ├── v3
│ │ ├── get-ids-by-tc.json
│ │ ├── get-static-champions-by-id.json
│ │ ├── get-summoner-by-name.json
│ │ ├── get-summoner.json
│ │ ├── get-static-runes-by-id.json
│ │ ├── get-static-masteries-by-id.json
│ │ ├── get-champion-266.json
│ │ ├── get-static-items-by-id.json
│ │ ├── get-static-summoner-spells-by-id.json
│ │ ├── get-league-positions.json
│ │ ├── get-static-realms.json
│ │ ├── get-champion-mastery.json
│ │ ├── get-tournament-code.json
│ │ ├── get-static-reforged-runes-by-id.json
│ │ ├── get-static-maps.json
│ │ ├── get-static-versions.json
│ │ ├── get-matches-recent.json
│ │ ├── get-lol-status-shard.json
│ │ ├── get-masteries.json
│ │ ├── get-static-summoner-spells.json
│ │ ├── get-current-game.json
│ │ ├── get-runes.json
│ │ ├── get-featured-games.json
│ │ ├── get-static-champions.json
│ │ ├── get-static-masteries.json
│ │ ├── get-champion-all.json
│ │ ├── get-champion-masteries.json
│ │ ├── get-match.json
│ │ └── get-match-with-tc.json
│ └── v1
│ │ └── get-get-code.json
├── lol
│ ├── runes_request_spec.rb
│ ├── masteries_request_spec.rb
│ ├── lol_status_request_spec.rb
│ ├── spectator_request_spec.rb
│ ├── champion_request_spec.rb
│ ├── tournament_request_spec.rb
│ ├── summoner_request_spec.rb
│ ├── match_request_spec.rb
│ ├── league_request_spec.rb
│ ├── champion_mastery_request_spec.rb
│ ├── dynamic_model_spec.rb
│ ├── invalid_api_response_spec.rb
│ ├── static_request_spec.rb
│ ├── client_spec.rb
│ └── request_spec.rb
├── api_version_spec.rb
├── spec_helper.rb
├── support
│ ├── helpers.rb
│ └── model_helpers.rb
└── acceptance_spec.rb
├── .autotest
├── lib
├── lol
│ ├── version.rb
│ ├── autoloader.rb
│ ├── lol_status_request.rb
│ ├── featured_game_list.rb
│ ├── runes_request.rb
│ ├── masteries_request.rb
│ ├── spectator_request.rb
│ ├── champion_request.rb
│ ├── summoner_request.rb
│ ├── invalid_api_response.rb
│ ├── dynamic_model.rb
│ ├── champion_mastery_request.rb
│ ├── league_request.rb
│ ├── static_request.rb
│ ├── match_request.rb
│ ├── client.rb
│ ├── tournament_request.rb
│ └── request.rb
└── lol.rb
├── Gemfile
├── .travis.yml
├── TODO.md
├── .gitignore
├── .editorconfig
├── demo
└── demo.rb
├── Rakefile
├── LICENSE.txt
├── Guardfile
├── ruby-lol.gemspec
└── README.md
/.rspec:
--------------------------------------------------------------------------------
1 | --color
2 |
--------------------------------------------------------------------------------
/spec/fixtures/v3/get-ids-by-tc.json:
--------------------------------------------------------------------------------
1 | [2972702365]
--------------------------------------------------------------------------------
/.autotest:
--------------------------------------------------------------------------------
1 | require 'autotest/fsevent'
2 | require 'autotest/growl'
--------------------------------------------------------------------------------
/lib/lol/version.rb:
--------------------------------------------------------------------------------
1 | module Lol
2 | VERSION = "1.2.0"
3 | end
4 |
--------------------------------------------------------------------------------
/Gemfile:
--------------------------------------------------------------------------------
1 | source 'https://rubygems.org'
2 |
3 | # Specify your gem's dependencies in ruby-lol.gemspec
4 | gemspec
5 |
--------------------------------------------------------------------------------
/spec/fixtures/v3/get-static-champions-by-id.json:
--------------------------------------------------------------------------------
1 | {"id":266,"key":"Aatrox","name":"Aatrox","title":"the Darkin Blade"}
--------------------------------------------------------------------------------
/spec/fixtures/v3/get-summoner-by-name.json:
--------------------------------------------------------------------------------
1 | {"id":19325407,"accountId":22016665,"name":"FoO","profileIconId":11,"revisionDate":1447284435000,"summonerLevel":30}
--------------------------------------------------------------------------------
/spec/fixtures/v3/get-summoner.json:
--------------------------------------------------------------------------------
1 | {"id":100023,"accountId":105754,"name":"qjQ1rLbjXvq05BRT","profileIconId":20,"revisionDate":1323135738000,"summonerLevel":3}
--------------------------------------------------------------------------------
/.travis.yml:
--------------------------------------------------------------------------------
1 | rvm: 2.4
2 | services: redis-server
3 | addons:
4 | code_climate:
5 | repo_token: c98576197ae22bdd3bede9cd3cdfdad33fac26bfdddb1e5f2bbc4fb535812185
6 |
--------------------------------------------------------------------------------
/spec/fixtures/v3/get-static-runes-by-id.json:
--------------------------------------------------------------------------------
1 | {"id":5001,"name":"Lesser Mark of Attack Damage","description":"+0.53 attack damage","rune":{"isRune":true,"tier":"1","type":"red"}}
--------------------------------------------------------------------------------
/spec/fixtures/v3/get-static-masteries-by-id.json:
--------------------------------------------------------------------------------
1 | {"id":6111,"name":"Fury","description":["+0.8% Attack Speed","+1.6% Attack Speed","+2.4% Attack Speed","+3.2% Attack Speed","+4% Attack Speed"]}
--------------------------------------------------------------------------------
/spec/fixtures/v3/get-champion-266.json:
--------------------------------------------------------------------------------
1 | {
2 | "botMmEnabled": false,
3 | "id": 266,
4 | "rankedPlayEnabled": true,
5 | "botEnabled": false,
6 | "active": true,
7 | "freeToPlay": false
8 | }
9 |
--------------------------------------------------------------------------------
/TODO.md:
--------------------------------------------------------------------------------
1 | # TODO #
2 |
3 | * finish writing acceptance specs in spec/acceptance_spec.rb
4 | * migrate everything to the new version of the api (just migrated game for now)
5 | * release 1.0
6 | * start better object mappings
7 |
--------------------------------------------------------------------------------
/spec/fixtures/v1/get-get-code.json:
--------------------------------------------------------------------------------
1 | {"id":1,"providerId":2,"tournamentId":3,"code":"CODE-FOR-TEST","region":"EUW","map":"SUMMONERS_RIFT","teamSize":5,"spectators":"ALL","pickType":"BLIND_PICK","lobbyName":"LOBBY-NAME","password":"PASSWORD"}
2 |
--------------------------------------------------------------------------------
/lib/lol/autoloader.rb:
--------------------------------------------------------------------------------
1 | Dir["#{File.expand_path('..', __FILE__)}/*.rb"].each do |file|
2 | filename = File.basename file
3 | classname = filename.split('.rb').first.camelize
4 | Lol.autoload classname, File.expand_path("../#{filename}", __FILE__)
5 | end
6 |
--------------------------------------------------------------------------------
/spec/fixtures/v3/get-static-items-by-id.json:
--------------------------------------------------------------------------------
1 | {"id":1001,"name":"Boots of Speed","description":"Limited to 1.
UNIQUE Passive - Enhanced Movement: +25 Movement Speed","plaintext":"Slightly increases Movement Speed"}
--------------------------------------------------------------------------------
/spec/fixtures/v3/get-static-summoner-spells-by-id.json:
--------------------------------------------------------------------------------
1 | {"name":"Cleanse","description":"Removes all disables (excluding suppression) and summoner spell debuffs affecting your champion and lowers the duration of incoming disables by 65% for 3 seconds.","summonerLevel":6,"id":1,"key":"SummonerBoost"}
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | *.gem
2 | *.rbc
3 | .bundle
4 | .config
5 | .yardoc
6 | Gemfile.lock
7 | InstalledFiles
8 | _yardoc
9 | coverage
10 | doc/
11 | lib/bundler/man
12 | pkg
13 | rdoc
14 | spec/reports
15 | test/tmp
16 | test/version_tmp
17 | tmp
18 | /output.txt
19 | /bin
20 | /.vscode
21 | /.idea
22 |
--------------------------------------------------------------------------------
/spec/fixtures/v3/get-league-positions.json:
--------------------------------------------------------------------------------
1 | [{"leagueName":"Vladimir's Ravagers","tier":"BRONZE","queueType":"RANKED_SOLO_5x5","rank":"I","playerOrTeamId":"30743211","playerOrTeamName":"Riot intinig","leaguePoints":40,"wins":11,"losses":14,"veteran":false,"inactive":false,"freshBlood":false,"hotStreak":false}]
--------------------------------------------------------------------------------
/spec/fixtures/v3/get-static-realms.json:
--------------------------------------------------------------------------------
1 | {"css":"7.9.1","dd":"7.9.1","l":"en_GB","n":{"item":"7.9.1","rune":"7.9.1","mastery":"7.9.1","summoner":"7.9.1","champion":"7.9.1","profileicon":"7.9.1","map":"7.9.1","language":"7.9.1","sticker":"7.9.1"},"profileiconmax":28,"v":"7.9.1","lg":"7.9.1","cdn":"http://ddragon.leagueoflegends.com/cdn"}
--------------------------------------------------------------------------------
/spec/fixtures/v3/get-champion-mastery.json:
--------------------------------------------------------------------------------
1 | {
2 | "highestGrade": "S+",
3 | "championPoints": 34356,
4 | "playerId": 1,
5 | "championPointsUntilNextLevel": 0,
6 | "chestGranted": true,
7 | "championLevel": 5,
8 | "tokensEarned": 2,
9 | "championId": 40,
10 | "championPointsSinceLastLevel": 12756,
11 | "lastPlayTime": 1464499894000
12 | }
--------------------------------------------------------------------------------
/spec/fixtures/v3/get-tournament-code.json:
--------------------------------------------------------------------------------
1 | {"id":3030173,"providerId":1000,"tournamentId":244507,"code":"EUW04397-222ec1fe-cb5f-430c-b5dd-51298c8cc50d","region":"EUW","map":"SUMMONERS_RIFT","teamSize":1,"spectators":"ALL","pickType":"TOURNAMENT_DRAFT","lobbyName":"c82325b7-6c4d-4424-a249-a11834f2a366","password":"46e61504b16f30a3666fa11beb58d5","participants":[30743211,79607125]}
--------------------------------------------------------------------------------
/.editorconfig:
--------------------------------------------------------------------------------
1 | # EditorConfig is awesome: http://EditorConfig.org
2 |
3 | # top-most EditorConfig file
4 | root = true
5 |
6 | # Unix-style newlines with a newline ending every file
7 | [*]
8 | charset = utf-8
9 | end_of_line = lf
10 | insert_final_newline = true
11 | indent_style = space
12 | trim_trailing_whitespace = true
13 | indent_size = 2
14 |
15 | [*.md]
16 | trim_trailing_whitespace = false
17 |
--------------------------------------------------------------------------------
/demo/demo.rb:
--------------------------------------------------------------------------------
1 | $LOAD_PATH.unshift(__dir__ + "/../lib")
2 |
3 | require "rubygems"
4 | require "ap"
5 | require "lol"
6 |
7 | include Lol
8 |
9 | client = Client.new "18b7c486-0d5a-459f-9d60-cf8776b154c6"
10 | intinig = client.summoner.by_name "intinig"
11 | leagues = client.league.get intinig.id
12 | my_league = leagues.first
13 |
14 | my_league.entries.select {|entry| entry.player_or_team_name == "intinig"}.each do |entry|
15 | ap entry
16 | end
17 |
--------------------------------------------------------------------------------
/spec/lol/runes_request_spec.rb:
--------------------------------------------------------------------------------
1 | require "spec_helper"
2 | require "lol"
3 |
4 | describe Lol::RunesRequest do
5 | subject { Lol::RunesRequest.new "api_key", "euw" }
6 |
7 | describe "#by_summoner_id" do
8 | it "returns an Array of DynamicModel" do
9 | stub_request subject, "runes", "runes/by-summoner/1"
10 | result = subject.by_summoner_id 1
11 | expect(result).to be_a Array
12 | expect(result.map(&:class).uniq).to eq [Lol::DynamicModel]
13 | end
14 | end
15 | end
16 |
--------------------------------------------------------------------------------
/spec/lol/masteries_request_spec.rb:
--------------------------------------------------------------------------------
1 | require "spec_helper"
2 | require "lol"
3 |
4 | describe Lol::MasteriesRequest do
5 | subject { Lol::MasteriesRequest.new "api_key", "euw" }
6 |
7 | describe "#by_summoner_id" do
8 | it "returns an Array of DynamicModel" do
9 | stub_request subject, "masteries", "masteries/by-summoner/1"
10 | result = subject.by_summoner_id 1
11 | expect(result).to be_a Array
12 | expect(result.map(&:class).uniq).to eq [Lol::DynamicModel]
13 | end
14 | end
15 | end
16 |
--------------------------------------------------------------------------------
/lib/lol/lol_status_request.rb:
--------------------------------------------------------------------------------
1 | module Lol
2 | # Bindings for the Status API.
3 | #
4 | # See: https://developer.riotgames.com/api-methods/#lol-status-v3
5 | class LolStatusRequest < Request
6 | # @!visibility private
7 | def api_base_path
8 | "/lol/status/#{self.class.api_version}"
9 | end
10 |
11 | # Get League of Legends status for the given shard
12 | # @return [DynamicModel]
13 | def shard_data
14 | DynamicModel.new perform_request api_url "shard-data"
15 | end
16 | end
17 | end
18 |
--------------------------------------------------------------------------------
/lib/lol/featured_game_list.rb:
--------------------------------------------------------------------------------
1 | module Lol
2 | # List of featured games
3 | #
4 | # Each contained featured game is represented through {DynamicModel}
5 | class FeaturedGameList < Array
6 | # The suggested interval to wait before requesting FeaturedGames again
7 | # @return [Integer]
8 | attr_reader :client_refresh_interval
9 |
10 | def initialize data
11 | @client_refresh_interval = data['clientRefreshInterval']
12 | super data['gameList'].map { |g| DynamicModel.new g }
13 | end
14 | end
15 | end
16 |
--------------------------------------------------------------------------------
/lib/lol/runes_request.rb:
--------------------------------------------------------------------------------
1 | module Lol
2 | # Bindings for the Runes API.
3 | #
4 | # See: https://developer.riotgames.com/api-methods/#runes-v3
5 | class RunesRequest < Request
6 | # Get rune pages for a given summoner ID
7 | # @param [Integer] summoner_id Summoner ID
8 | # @return [Array] Rune pages
9 | def by_summoner_id summoner_id
10 | result = perform_request api_url "runes/by-summoner/#{summoner_id}"
11 | result["pages"].map { |p| DynamicModel.new p }
12 | end
13 | end
14 | end
15 |
--------------------------------------------------------------------------------
/lib/lol/masteries_request.rb:
--------------------------------------------------------------------------------
1 | module Lol
2 | # Bindings for the Masteries API.
3 | #
4 | # See: https://developer.riotgames.com/api-methods/#masteries-v3
5 | class MasteriesRequest < Request
6 | # Get mastery pages for a given summoner ID
7 | # @param [Integer] summoner_id Summoner ID
8 | # @return [Array] Mastery pages
9 | def by_summoner_id summoner_id
10 | result = perform_request api_url "masteries/by-summoner/#{summoner_id}"
11 | result["pages"].map { |p| DynamicModel.new p }
12 | end
13 | end
14 | end
15 |
--------------------------------------------------------------------------------
/spec/fixtures/v3/get-static-reforged-runes-by-id.json:
--------------------------------------------------------------------------------
1 | {"id":8410,"key":"ApproachVelocity","name":"Approach Velocity","shortDesc":"Bonus MS towards nearby ally champions that are movement impaired or enemy champions that you impair.","longDesc":"Gain 15% Movement Speed towards nearby ally champions that are movement impaired or enemy champions that you impair.
Range: 1000","icon":"perk-images/Styles/Resolve/ApproachVelocity/ApproachVelocity.png","runePathId":8300,"runePathName":"Inspiration"}
2 |
--------------------------------------------------------------------------------
/lib/lol.rb:
--------------------------------------------------------------------------------
1 | require 'active_support/core_ext/string/inflections'
2 | require 'httparty'
3 | require 'uri'
4 | require 'redis'
5 | require 'glutton_ratelimit'
6 |
7 | module Lol
8 | # Takes a hash and returns a copy of it with the keys that have been underscored
9 | # This method is here but should be somewhere else, probably an helper module
10 | # @param [Hash] a hash with keys in camelCase format
11 | # @return [Hash] a copy of the original hash, with hash keys that have been underscored
12 | def self.underscore_hash_keys hash
13 | hash.inject({}) { |memo, (key, value)| memo.update key.to_s.underscore => value }
14 | end
15 | end
16 |
17 | require 'lol/autoloader'
18 | require "lol/invalid_api_response"
19 |
--------------------------------------------------------------------------------
/spec/lol/lol_status_request_spec.rb:
--------------------------------------------------------------------------------
1 | require "spec_helper"
2 | require "lol"
3 |
4 | include Lol
5 |
6 | describe LolStatusRequest do
7 | subject { LolStatusRequest.new "api_key", "euw" }
8 |
9 | it 'inherits from Request' do
10 | expect(LolStatusRequest).to be < Request
11 | end
12 |
13 | describe '#shard_data' do
14 | let(:response) { subject.shard_data }
15 |
16 | before(:each) { stub_request(subject, 'lol-status-shard', 'shard-data') }
17 |
18 | it 'returns a Shard' do
19 | expect(response).to be_a(DynamicModel)
20 | end
21 |
22 | it 'services returns an array of Services' do
23 | expect(response.services.map(&:class).uniq).to eq([DynamicModel])
24 | end
25 | end
26 |
27 | end
28 |
--------------------------------------------------------------------------------
/Rakefile:
--------------------------------------------------------------------------------
1 | require "bundler/setup"
2 | require "bundler/gem_tasks"
3 | require "rspec/core/rake_task"
4 |
5 | RSpec::Core::RakeTask.new(:spec, :tag) do |t, task_args|
6 | t.rspec_opts = "--tag #{task_args[:tag]}" if task_args[:tag]
7 | end
8 |
9 | task :default => :spec
10 |
11 | desc "Open an irb session preloaded with this library"
12 | task :console do
13 | sh "irb -rubygems -I . -r lib/lol.rb"
14 | end
15 |
16 | desc "Clean up leftover fixtures"
17 | task :clean_fixtures do
18 | puts Dir.glob("spec/fixtures/*/*").map {|f| f.gsub("spec/fixtures/", "")}.map {|f| f.split("/")}.each_with_object({}) {|e, ack| ack[e[1]] ||= []; ack[e[1]] << e[0]}.select {|k,v| v.size > 1}.map {|k,v| "spec/fixtures/#{v.first}/#{k}"}.each {|f| File.delete f}
19 | end
20 |
--------------------------------------------------------------------------------
/spec/api_version_spec.rb:
--------------------------------------------------------------------------------
1 | require "spec_helper"
2 | require "lol"
3 |
4 | include Lol
5 |
6 |
7 | def check_api_version(klass, version)
8 | describe klass do
9 | it "should use API version #{version}" do
10 | expect(klass.api_version).to eq(version)
11 | end
12 | end
13 | end
14 |
15 | describe "API Versions" do
16 | check_api_version(ChampionRequest, "v3")
17 | check_api_version(ChampionMasteryRequest, "v3")
18 | check_api_version(LeagueRequest, "v3")
19 | check_api_version(StaticRequest, "v3")
20 | check_api_version(LolStatusRequest, "v3")
21 | check_api_version(MatchRequest, "v3")
22 | check_api_version(SummonerRequest, "v3")
23 | check_api_version(RunesRequest, "v3")
24 | check_api_version(MasteriesRequest, "v3")
25 | check_api_version(SpectatorRequest, "v3")
26 | end
27 |
--------------------------------------------------------------------------------
/spec/lol/spectator_request_spec.rb:
--------------------------------------------------------------------------------
1 | require "spec_helper"
2 | require "lol"
3 |
4 | describe Lol::SpectatorRequest do
5 | subject { described_class.new "api_key", "euw" }
6 |
7 | describe "#current_game" do
8 | it "returns a DynamicModel" do
9 | stub_request subject, "current-game", "active-games/by-summoner/23"
10 | expect(subject.current_game summoner_id: 23).to be_a Lol::DynamicModel
11 | end
12 | end
13 |
14 | describe "#featured_games" do
15 | it "returns a FeaturedGameList" do
16 | stub_request subject, "featured-games", "featured-games"
17 | result = subject.featured_games
18 | expect(result).to be_a Lol::FeaturedGameList
19 | expect(result.client_refresh_interval).not_to be_nil
20 | expect(result.size).not_to be_zero
21 | end
22 | end
23 | end
24 |
--------------------------------------------------------------------------------
/spec/spec_helper.rb:
--------------------------------------------------------------------------------
1 | require "rspec"
2 | require "simplecov"
3 | require "coveralls"
4 | require "codeclimate-test-reporter"
5 | require "vcr"
6 |
7 | SPEC_ROOT = __dir__
8 |
9 | Dir[File.expand_path(File.join(File.dirname(__FILE__),'support','**','*.rb'))].each {|f| require f}
10 |
11 | SimpleCov.formatter = SimpleCov::Formatter::MultiFormatter.new [
12 | Coveralls::SimpleCov::Formatter,
13 | SimpleCov::Formatter::HTMLFormatter
14 | ]
15 |
16 | SimpleCov.start
17 |
18 | VCR.configure do |c|
19 | c.cassette_library_dir = File.join(SPEC_ROOT, '..', 'fixtures', 'vcr_cassettes')
20 | c.hook_into :webmock
21 | c.ignore_hosts 'codeclimate.com'
22 | c.configure_rspec_metadata!
23 | end
24 |
25 | RSpec.configure do |c|
26 | # c.fail_fast = true
27 | c.filter_run_excluding :remote => true
28 | c.include Helpers
29 | end
30 |
--------------------------------------------------------------------------------
/lib/lol/spectator_request.rb:
--------------------------------------------------------------------------------
1 | module Lol
2 | # Bindings for the Match API.
3 | #
4 | # See: https://developer.riotgames.com/api-methods/#match-v3
5 | class SpectatorRequest < Request
6 | # @!visibility private
7 | def api_base_path
8 | "/lol/spectator/#{self.class.api_version}"
9 | end
10 |
11 | # Get current game information for the given summoner ID.
12 | # @param [Integer] summoner_id Summoner ID
13 | # @return [DynamicModel] Current game representation
14 | def current_game summoner_id:
15 | DynamicModel.new perform_request api_url "active-games/by-summoner/#{summoner_id}"
16 | end
17 |
18 | # Get list of featured games.
19 | # @return [FeaturedGameList] list of featured games
20 | def featured_games
21 | FeaturedGameList.new perform_request api_url "featured-games"
22 | end
23 | end
24 | end
25 |
--------------------------------------------------------------------------------
/spec/fixtures/v3/get-static-maps.json:
--------------------------------------------------------------------------------
1 | {"type":"map","version":"7.9.1","data":{"11":{"mapName":"Summoner's Rift","mapId":11,"image":{"full":"map11.png","sprite":"map0.png","group":"map","x":96,"y":0,"w":48,"h":48}},"12":{"mapName":"Howling Abyss","mapId":12,"image":{"full":"map12.png","sprite":"map0.png","group":"map","x":144,"y":0,"w":48,"h":48}},"14":{"mapName":"Butcher's Bridge","mapId":14,"image":{"full":"map14.png","sprite":"map0.png","group":"map","x":192,"y":0,"w":48,"h":48}},"16":{"mapName":"Cosmic Ruins","mapId":16,"image":{"full":"map16.png","sprite":"map0.png","group":"map","x":240,"y":0,"w":48,"h":48}},"8":{"mapName":"The Crystal Scar","mapId":8,"image":{"full":"map8.png","sprite":"map0.png","group":"map","x":0,"y":0,"w":48,"h":48}},"10":{"mapName":"The Twisted Treeline","mapId":10,"image":{"full":"map10.png","sprite":"map0.png","group":"map","x":48,"y":0,"w":48,"h":48}}}}
--------------------------------------------------------------------------------
/spec/lol/champion_request_spec.rb:
--------------------------------------------------------------------------------
1 | require "spec_helper"
2 | require "lol"
3 |
4 | include Lol
5 |
6 | describe ChampionRequest do
7 | subject { described_class.new("api_key", "euw") }
8 |
9 | it "inherits from Request" do
10 | expect(described_class.ancestors[1]).to eq(Request)
11 | end
12 |
13 | describe "#find" do
14 | it "returns a champion" do
15 | stub_request subject, 'champion-266', 'champions/266'
16 | expect(subject.find 266).to be_a DynamicModel
17 | end
18 | end
19 |
20 | describe "#all" do
21 | before { stub_request subject, 'champion-all', 'champions', 'freeToPlay' => false }
22 | let(:result) { subject.all }
23 |
24 | it "returns an array of champions" do
25 | expect(result).to be_a Array
26 | expect(result.map(&:class).uniq).to eq [DynamicModel]
27 | end
28 |
29 | it "fetches champions from the API" do
30 | expect(result.size).to eq load_fixture('champion-all', described_class.api_version)['champions'].size
31 | end
32 | end
33 | end
34 |
--------------------------------------------------------------------------------
/lib/lol/champion_request.rb:
--------------------------------------------------------------------------------
1 | module Lol
2 | # Bindings for the Champion API.
3 | #
4 | # See: https://developer.riotgames.com/api-methods/#champion-v3
5 | class ChampionRequest < Request
6 | # Retrieve all champions
7 | #
8 | # See: https://developer.riotgames.com/api-methods/#champion-v3/GET_getChampions
9 | # @param free_to_play [Boolean] filter param to retrieve only free to play champions
10 | # @return [Array] an array of champions
11 | def all free_to_play: false
12 | result = perform_request api_url("champions", "freeToPlay" => free_to_play)
13 | result["champions"].map { |c| DynamicModel.new c }
14 | end
15 |
16 | # Retrieve champion by ID
17 | #
18 | # See: https://developer.riotgames.com/api-methods/#champion-v3/GET_getChampionsById
19 | # @param id [Integer] id of the champion to get
20 | # @return [Lol::DynamicModel] the found champion
21 | def find id
22 | result = perform_request api_url "champions/#{id}"
23 | DynamicModel.new result
24 | end
25 | end
26 | end
27 |
--------------------------------------------------------------------------------
/LICENSE.txt:
--------------------------------------------------------------------------------
1 | Copyright (c) 2013 Giovanni Intini
2 |
3 | MIT License
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining
6 | a copy of this software and associated documentation files (the
7 | "Software"), to deal in the Software without restriction, including
8 | without limitation the rights to use, copy, modify, merge, publish,
9 | distribute, sublicense, and/or sell copies of the Software, and to
10 | permit persons to whom the Software is furnished to do so, subject to
11 | the following conditions:
12 |
13 | The above copyright notice and this permission notice shall be
14 | included in all copies or substantial portions of the Software.
15 |
16 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
17 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
18 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
19 | NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
20 | LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
21 | OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
22 | WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
23 |
--------------------------------------------------------------------------------
/lib/lol/summoner_request.rb:
--------------------------------------------------------------------------------
1 | module Lol
2 | # Bindings for the Summoner API.
3 | #
4 | # See: https://developer.riotgames.com/api-methods/#summoner-v3
5 | class SummonerRequest < Request
6 | # @!visibility private
7 | def api_base_path
8 | "/lol/summoner/#{self.class.api_version}"
9 | end
10 |
11 | # Get a summoner by summoner ID.
12 | # @param [Integer] id Summoner ID
13 | # @return [DynamicModel] Summoner representation
14 | def find id
15 | DynamicModel.new perform_request api_url "summoners/#{id}"
16 | end
17 |
18 | # Get a summoner by summoner name.
19 | # @param [String] name Summoner name
20 | # @return [DynamicModel] Summoner representation
21 | def find_by_name name
22 | name = CGI.escape name.downcase.gsub(/\s/, '')
23 | DynamicModel.new perform_request api_url "summoners/by-name/#{name}"
24 | end
25 |
26 | # Get a summoner by account ID.
27 | # @param [Integer] account_id Account ID
28 | # @return [DynamicModel] Summoner representation
29 | def find_by_account_id account_id
30 | DynamicModel.new perform_request api_url "summoners/by-account/#{account_id}"
31 | end
32 | end
33 | end
34 |
--------------------------------------------------------------------------------
/spec/lol/tournament_request_spec.rb:
--------------------------------------------------------------------------------
1 | require "spec_helper"
2 | require "lol"
3 |
4 | include Lol
5 |
6 | describe TournamentRequest do
7 | subject { described_class.new "api_key", "euw" }
8 |
9 | it "inherits from Request" do
10 | expect(TournamentRequest).to be < Request
11 | end
12 |
13 | describe "#create_provider" do
14 | it "returns the provider id" do
15 | expect(subject).to receive(:perform_request).with(instance_of(String), :post, { "url" => "https://foo.com", "region" => "EUW" }).and_return 10
16 | expect(subject.create_provider url: "https://foo.com").to eq 10
17 | end
18 | end
19 |
20 | describe "#create_tournament" do
21 | it "returns the tournament id" do
22 | expect(subject).to receive(:perform_request).with(instance_of(String), :post, { "providerId" => 10, "name" => "ASD" }).and_return 10
23 | expect(subject.create_tournament provider_id: 10, name: "ASD").to eq 10
24 | end
25 | end
26 |
27 | describe "#find_code" do
28 | it "returns a DynamicModel" do
29 | stub_request subject, "tournament-code", "codes/foo"
30 | expect(subject.find_code 'foo').to be_a DynamicModel
31 | end
32 | end
33 |
34 | pending '#create_codes'
35 | pending '#update_code'
36 | end
37 |
--------------------------------------------------------------------------------
/Guardfile:
--------------------------------------------------------------------------------
1 | # A sample Guardfile
2 | # More info at https://github.com/guard/guard#readme
3 |
4 | guard :rspec, cmd: "bundle exec rspec" do
5 | watch(%r{^spec/.+_spec\.rb$})
6 | watch(%r{^lib/(.+)\.rb$}) { |m| "spec/lib/#{m[1]}_spec.rb" }
7 | watch('spec/spec_helper.rb') { "spec" }
8 |
9 | # Rails example
10 | watch(%r{^app/(.+)\.rb$}) { |m| "spec/#{m[1]}_spec.rb" }
11 | watch(%r{^app/(.*)(\.erb|\.haml|\.slim)$}) { |m| "spec/#{m[1]}#{m[2]}_spec.rb" }
12 | watch(%r{^app/controllers/(.+)_(controller)\.rb$}) { |m| ["spec/routing/#{m[1]}_routing_spec.rb", "spec/#{m[2]}s/#{m[1]}_#{m[2]}_spec.rb", "spec/acceptance/#{m[1]}_spec.rb"] }
13 | watch(%r{^spec/support/(.+)\.rb$}) { "spec" }
14 | watch('config/routes.rb') { "spec/routing" }
15 | watch('app/controllers/application_controller.rb') { "spec/controllers" }
16 |
17 | # Capybara features specs
18 | watch(%r{^app/views/(.+)/.*\.(erb|haml|slim)$}) { |m| "spec/features/#{m[1]}_spec.rb" }
19 |
20 | # Turnip features and steps
21 | watch(%r{^spec/acceptance/(.+)\.feature$})
22 | watch(%r{^spec/acceptance/steps/(.+)_steps\.rb$}) { |m| Dir[File.join("**/#{m[1]}.feature")][0] || 'spec/acceptance' }
23 | end
24 |
--------------------------------------------------------------------------------
/spec/lol/summoner_request_spec.rb:
--------------------------------------------------------------------------------
1 | require "spec_helper"
2 | require "lol"
3 |
4 | include Lol
5 |
6 | describe SummonerRequest do
7 | subject { SummonerRequest.new "api_key", "euw" }
8 |
9 | it "inherits from Request" do
10 | expect(SummonerRequest).to be < Request
11 | end
12 |
13 | describe "#find" do
14 | it "returns a DynamicModel" do
15 | stub_request subject, "summoner", "summoners/23"
16 | expect(subject.find 23).to be_a DynamicModel
17 | end
18 | end
19 |
20 | describe "#find_by_name" do
21 | it "returns a DynamicModel" do
22 | stub_request subject, 'summoner-by-name', 'summoners/by-name/foo'
23 | expect(subject.find_by_name 'foo').to be_a DynamicModel
24 | end
25 |
26 | it "escapes the given name" do
27 | stub_request subject, 'summoner-by-name', 'summoners/by-name/f%C3%B2%C3%A5'
28 | subject.find_by_name 'fòå'
29 | end
30 |
31 | it "downcases the given name" do
32 | stub_request subject, 'summoner-by-name', 'summoners/by-name/arg'
33 | subject.find_by_name 'ARG'
34 | end
35 |
36 | it 'strips spaces from names' do
37 | stub_request(subject, 'summoner-by-name', 'summoners/by-name/foo')
38 | subject.find_by_name('fo o')
39 | end
40 | end
41 | end
42 |
--------------------------------------------------------------------------------
/lib/lol/invalid_api_response.rb:
--------------------------------------------------------------------------------
1 | module Lol
2 | class InvalidAPIResponse < StandardError
3 | attr_reader :raw
4 |
5 | def initialize url, response
6 | @raw = extract_raw_response_info response
7 | super "#{raw[:status]} calling #{url}"
8 | end
9 |
10 | private
11 |
12 | def extract_raw_response_info response
13 | {
14 | headers: extract_raw_headers(response),
15 | body: extract_raw_body(response),
16 | status: extract_raw_status(response)
17 | }
18 | end
19 |
20 | def extract_raw_headers response
21 | response.respond_to?(:headers) && response.headers || {}
22 | end
23 |
24 | def extract_raw_body response
25 | if response.respond_to?(:parsed_response)
26 | response.parsed_response
27 | elsif response.respond_to?(:body)
28 | response.body
29 | else
30 | response
31 | end
32 | end
33 |
34 | def extract_raw_status response
35 | if response.is_a?(Hash) && response['status']
36 | response['status']['message']
37 | elsif response.respond_to?(:response)
38 | "#{response.response.code} #{response.response.message}"
39 | else
40 | "Unknown Error"
41 | end
42 | end
43 | end
44 | end
45 |
--------------------------------------------------------------------------------
/spec/fixtures/v3/get-static-versions.json:
--------------------------------------------------------------------------------
1 | ["7.10.1","7.9.2","7.9.1","7.8.1","7.7.1","7.6.1","7.5.2","7.5.1","7.4.3","7.4.2","7.4.1","7.3.3","7.3.2","7.3.1","7.2.1","7.1.1","6.24.1","6.23.1","6.22.1","6.21.1","6.20.1","6.19.1","6.18.1","6.17.1","6.16.2","6.16.1","6.15.1","6.14.2","6.14.1","6.13.1","6.12.1","6.11.1","6.10.1","6.9.1","6.8.1","6.7.1","6.6.1","6.5.1","6.4.2","6.4.1","6.3.1","6.2.1","6.1.1","5.24.2","5.24.1","5.23.1","5.22.3","5.22.2","5.22.1","5.21.1","5.20.1","5.19.1","5.18.1","5.17.1","5.16.1","5.15.1","5.14.1","5.13.1","5.12.1","5.11.1","5.10.1","5.9.1","5.8.1","5.7.2","5.7.1","5.6.2","5.6.1","5.5.3","5.5.2","5.5.1","5.4.1","5.3.1","5.2.2","5.2.1","5.1.2","5.1.1","4.21.5","4.21.4","4.21.3","4.21.1","4.20.2","4.20.1","4.19.3","4.19.2","4.18.1","4.17.1","4.16.1","4.15.1","4.14.2","4.13.1","4.12.2","4.12.1","4.11.3","4.10.7","4.10.2","4.9.1","4.8.3","4.8.2","4.8.1","4.7.16","4.7.9","4.7.8","4.6.3","4.5.4","4.4.3","4.4.2","4.3.18","4.3.12","4.3.10","4.3.4","4.3.2","4.2.6","4.2.5","4.2.1","4.1.43","4.1.41","4.1.13","4.1.9","4.1.2","3.15.5","3.15.4","3.15.2","3.14.41","3.14.23","3.14.22","3.14.20","3.14.19","3.14.16","3.14.13","3.14.12","3.13.24","3.13.8","3.13.6","3.13.1","3.12.37","3.12.36","3.12.34","3.12.33","3.12.26","3.12.24","3.12.2","3.11.4","3.11.2","3.10.6","3.10.3","3.10.2","3.9.7","3.9.5","3.9.4","3.8.5","3.8.3","3.8.1","3.7.9","3.7.2","3.7.1","3.6.15","3.6.14","0.154.3","0.154.2","0.153.2","0.152.115","0.152.108","0.152.107","0.152.55","0.151.101","0.151.2"]
--------------------------------------------------------------------------------
/spec/lol/match_request_spec.rb:
--------------------------------------------------------------------------------
1 | require "spec_helper"
2 | require "lol"
3 |
4 | include Lol
5 |
6 | describe MatchRequest do
7 | subject { MatchRequest.new "api_key", "euw" }
8 |
9 | it "inherits from Request" do
10 | expect(MatchRequest).to be < Request
11 | end
12 |
13 | describe "#find" do
14 | it "returns a DynamicModel" do
15 | stub_request subject, 'match', "matches/1"
16 | expect(subject.find match_id: 1).to be_a DynamicModel
17 | end
18 | end
19 |
20 | describe "#find_timeline" do
21 | it "returns a DynamicModel" do
22 | stub_request subject, 'timeline', "timelines/by-match/1"
23 | expect(subject.find_timeline 1).to be_a DynamicModel
24 | end
25 | end
26 |
27 | describe "#ids_by_tournament_code" do
28 | it "returns a list of ids" do
29 | stub_request subject, 'ids-by-tc', "matches/by-tournament-code/1/ids"
30 | result = subject.ids_by_tournament_code '1'
31 | expect(result).to be_a Array
32 | expect(result.map(&:class).uniq).to eq [Fixnum]
33 | end
34 | end
35 |
36 | describe "#find_by_tournament" do
37 | it "returns a DynamicModel" do
38 | stub_request subject, 'match-with-tc', "matches/1/by-tournament-code/2"
39 | expect(subject.find_by_tournament 1, 2).to be_a DynamicModel
40 | end
41 | end
42 |
43 | describe "#all" do
44 | it "returns a DynamicModel" do
45 | stub_request subject, 'matches', "matchlists/by-account/1"
46 | expect(subject.all account_id: 1).to be_a DynamicModel
47 | end
48 | end
49 |
50 | describe "#recent" do
51 | it "returns a DynamicModel" do
52 | stub_request subject, 'matches-recent', "matchlists/by-account/1/recent"
53 | expect(subject.recent account_id: 1).to be_a DynamicModel
54 | end
55 | end
56 | end
57 |
--------------------------------------------------------------------------------
/lib/lol/dynamic_model.rb:
--------------------------------------------------------------------------------
1 | require 'ostruct'
2 | require 'active_support/core_ext/string/inflections'
3 |
4 | module Lol
5 | # DynamicModel extends OpenStruct adding the following features:
6 | # - nested generation ({a: {}}) results in DynamicModel(a: DynamicModel)
7 | # - parsing of date/time when property name ends with _at or _date and the value is a number
8 | class DynamicModel < OpenStruct
9 | attr_reader :raw
10 |
11 | def initialize(hash={})
12 | raise ArgumentError, 'An hash is required as parameter' unless hash.is_a? Hash
13 | @raw = hash
14 | @table = {}
15 | @hash_table = {}
16 |
17 | hash.each do |k,v|
18 | key = k.to_s.underscore
19 | set_property key, v
20 | new_ostruct_member(key)
21 | end
22 | end
23 |
24 | def to_h
25 | @hash_table
26 | end
27 |
28 | def as_json opts={}
29 | @table.as_json
30 | end
31 |
32 | protected
33 |
34 | def class_for_property property
35 | self.class
36 | end
37 |
38 | private
39 |
40 | def date_key? key
41 | key.match(/^(.+_)?(at|date)$/)
42 | end
43 |
44 | def set_property key, v
45 | if date_key?(key) && v.is_a?(Integer)
46 | @table[key.to_sym] = @hash_table[key.to_sym] = value_to_date v
47 | else
48 | @table[key.to_sym] = convert_object v, property: key.to_sym
49 | @hash_table[key.to_sym] = v
50 | end
51 | end
52 |
53 | def value_to_date v
54 | Time.at(v / 1000)
55 | end
56 |
57 | def convert_object obj, property:
58 | if obj.is_a? Hash
59 | class_for_property(property).new obj
60 | elsif obj.respond_to?(:map)
61 | obj.map { |o| convert_object o, property: property }
62 | else
63 | obj
64 | end
65 | end
66 | end
67 | end
68 |
--------------------------------------------------------------------------------
/ruby-lol.gemspec:
--------------------------------------------------------------------------------
1 | # coding: utf-8
2 | lib = File.expand_path('../lib', __FILE__)
3 | $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4 | require 'lol/version'
5 |
6 | Gem::Specification.new do |spec|
7 | spec.name = "ruby-lol"
8 | spec.version = Lol::VERSION
9 | spec.authors = ["Giovanni Intini"]
10 | spec.email = ["giovanni@mikamai.com"]
11 | spec.description = %q{Ruby wrapper to Riot Games API. Maps results to full blown ruby objects.}
12 | spec.summary = %q{Ruby wrapper to Riot Games API}
13 | spec.homepage = "https://github.com/mikamai/ruby-lol"
14 | spec.license = "MIT"
15 |
16 | spec.files = `git ls-files`.split($/)
17 | spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
18 | spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
19 | spec.require_paths = ["lib"]
20 |
21 | spec.add_development_dependency "bundler", "~> 1.3"
22 | spec.add_development_dependency "rake"
23 | spec.add_development_dependency "yard"
24 | spec.add_development_dependency "redcarpet"
25 | spec.add_development_dependency "rspec"
26 | spec.add_development_dependency "guard-rspec"
27 | spec.add_development_dependency "ZenTest"
28 | spec.add_development_dependency "autotest-growl"
29 | spec.add_development_dependency "codeclimate-test-reporter", '~> 0.5.0'
30 | spec.add_development_dependency "coveralls"
31 | spec.add_development_dependency "vcr"
32 | spec.add_development_dependency "webmock", ">= 1.8.0", "< 1.16"
33 | spec.add_development_dependency "awesome_print"
34 |
35 | spec.add_runtime_dependency "httparty", "0.14.0" # due to https://github.com/jnunemaker/httparty/issues/533
36 | spec.add_runtime_dependency "activesupport"
37 | spec.add_runtime_dependency "redis"
38 | spec.add_runtime_dependency "glutton_ratelimit"
39 | end
40 |
--------------------------------------------------------------------------------
/lib/lol/champion_mastery_request.rb:
--------------------------------------------------------------------------------
1 | module Lol
2 | # Bindings for the Champion Mastery API.
3 | #
4 | # See: https://developer.riotgames.com/api-methods/#champion-mastery-v3
5 | class ChampionMasteryRequest < Request
6 | # @!visibility private
7 | def api_base_path
8 | "/lol/champion-mastery/#{api_version}"
9 | end
10 |
11 | # Get a player's total champion mastery score, which is the sum of individual champion mastery levels
12 | #
13 | # See: https://developer.riotgames.com/api-methods/#champion-mastery-v3/GET_getDynamicModelScore
14 | # @param [Integer] summoner_id Summoner ID associated with the player
15 | # @return [Integer] Player's total champion master score
16 | def total_score summoner_id:
17 | perform_request api_url "scores/by-summoner/#{summoner_id}"
18 | end
19 |
20 | # Get all champion mastery entries sorted by number of champion points descending
21 | #
22 | # See: https://developer.riotgames.com/api-methods/#champion-mastery-v3/GET_getAllChampionMasteries
23 | # @param [Integer] summoner_id Summoner ID associated with the player
24 | # @return [Array] Champion Masteries
25 | def all summoner_id:
26 | result = perform_request api_url "champion-masteries/by-summoner/#{summoner_id}"
27 | result.map { |c| DynamicModel.new c }
28 | end
29 |
30 | # Get a champion mastery by player ID and champion ID
31 | #
32 | # See: https://developer.riotgames.com/api-methods/#champion-mastery-v3/GET_getDynamicModel
33 | # @param [Integer] summoner_id Summoner ID associated with the player
34 | # @return [Lol::DynamicModel] Champion Mastery
35 | def find champion_id, summoner_id:
36 | result = perform_request api_url "champion-masteries/by-summoner/#{summoner_id}/by-champion/#{champion_id}"
37 | DynamicModel.new result
38 | end
39 | end
40 | end
41 |
--------------------------------------------------------------------------------
/spec/lol/league_request_spec.rb:
--------------------------------------------------------------------------------
1 | require "spec_helper"
2 | require "lol"
3 |
4 | include Lol
5 |
6 | describe LeagueRequest do
7 | subject { LeagueRequest.new 'api_key', 'euw' }
8 |
9 | it 'inherits from V3 Request' do
10 | expect(LeagueRequest).to be < Request
11 | end
12 |
13 | describe '#find_challenger' do
14 | it 'returns a DynamicModel' do
15 | stub_request subject, 'league-challenger', 'challengerleagues/by-queue/RANKED_SOLO_5x5'
16 | expect(subject.find_challenger).to be_a DynamicModel
17 | end
18 |
19 | it 'finds the challenger league for the given queue' do
20 | stub_request subject, 'league-challenger', 'challengerleagues/by-queue/foo'
21 | subject.find_challenger queue: 'foo'
22 | end
23 | end
24 |
25 | describe '#find_master' do
26 | it 'returns a LeagueList' do
27 | stub_request subject, 'league-master', 'masterleagues/by-queue/RANKED_SOLO_5x5'
28 | expect(subject.find_master).to be_a DynamicModel
29 | end
30 |
31 | it 'finds the master league for the given queue' do
32 | stub_request subject, 'league-master', 'masterleagues/by-queue/foo'
33 | subject.find_master queue: 'foo'
34 | end
35 | end
36 |
37 | describe '#summoner_leagues' do
38 | it 'returns an array of LeagueList objects' do
39 | stub_request subject, 'league-summoner', 'leagues/by-summoner/1'
40 | result = subject.summoner_leagues summoner_id: 1
41 | expect(result).to be_a Array
42 | expect(result.map(&:class).uniq).to eq [DynamicModel]
43 | end
44 | end
45 |
46 | describe '#summoner_positions' do
47 | it 'returns an array of DynamicModel objects' do
48 | stub_request subject, 'league-positions', 'positions/by-summoner/1'
49 | result = subject.summoner_positions summoner_id: 1
50 | expect(result).to be_a Array
51 | expect(result.map(&:class).uniq).to eq [DynamicModel]
52 | end
53 | end
54 | end
55 |
--------------------------------------------------------------------------------
/lib/lol/league_request.rb:
--------------------------------------------------------------------------------
1 | module Lol
2 | # Bindings for the League API.
3 | #
4 | # See: https://developer.riotgames.com/api-methods/#league-v3
5 | class LeagueRequest < Request
6 | # @!visibility private
7 | def api_base_path
8 | "/lol/league/#{api_version}"
9 | end
10 |
11 | # Get the challenger league for a given queue
12 | # @param [String] queue Queue identifier. See the list of game constants (developer.riotgames.com/game-constants.html) for the available queue identifiers
13 | # @return [DynamicModel] Challenger league
14 | def find_challenger queue: 'RANKED_SOLO_5x5'
15 | DynamicModel.new perform_request api_url "challengerleagues/by-queue/#{queue}"
16 | end
17 |
18 | # Get the master league for a given queue
19 | # @param [String] queue Queue identifier. See the list of game constants (developer.riotgames.com/game-constants.html) for the available queue identifiers
20 | # @return [DynamicModel] lMaster league
21 | def find_master queue: 'RANKED_SOLO_5x5'
22 | DynamicModel.new perform_request api_url "masterleagues/by-queue/#{queue}"
23 | end
24 |
25 | # Get leagues in all queues for a given summoner ID
26 | # @param [Integer] summoner_id Summoner ID associated with the player
27 | # @return [Array] List of leagues summoner is participating in
28 | def summoner_leagues summoner_id:
29 | result = perform_request api_url "leagues/by-summoner/#{summoner_id}"
30 | result.map { |c| DynamicModel.new c }
31 | end
32 |
33 | # Get league positions in all queues for a given summoner ID
34 | # @param [Integer] summoner_id Summoner ID associated with the player
35 | # @return [Array] list of league positions
36 | def summoner_positions summoner_id:
37 | result = perform_request api_url "positions/by-summoner/#{summoner_id}"
38 | result.map { |c| DynamicModel.new c }
39 | end
40 | end
41 | end
42 |
--------------------------------------------------------------------------------
/spec/lol/champion_mastery_request_spec.rb:
--------------------------------------------------------------------------------
1 | require "spec_helper"
2 | require "lol"
3 | include Lol
4 |
5 | describe ChampionMasteryRequest do
6 | subject { ChampionMasteryRequest.new("api_key", "euw") }
7 |
8 | it "inherits from Request" do
9 | expect(ChampionMasteryRequest.ancestors[1]).to eq Request
10 | end
11 |
12 | describe "#total_score" do
13 | it "returns the total score" do
14 | stub_request_raw subject, 60, 'scores/by-summoner/1'
15 | expect(subject.total_score summoner_id: 1).to eq 60
16 | end
17 | end
18 |
19 | describe "#find" do
20 | it "returns a ChampionMastery" do
21 | stub_request(subject, 'champion-mastery', 'champion-masteries/by-summoner/1/by-champion/40')
22 | expect(subject.find 40, summoner_id: 1).to be_a DynamicModel
23 | end
24 |
25 | it "fetches ChampionMastery from the API" do
26 | stub_request(subject, 'champion-mastery', 'champion-masteries/by-summoner/1/by-champion/40')
27 | result = subject.find 40, summoner_id: 1
28 | expect(result.highest_grade).to eq('S+')
29 | expect(result.champion_points).to eq(34356)
30 | expect(result.player_id).to eq(1)
31 | expect(result.champion_points_until_next_level).to eq(0)
32 | expect(result.chest_granted).to be(true)
33 | expect(result.champion_level).to eq(5)
34 | expect(result.tokens_earned).to eq(2)
35 | expect(result.champion_id).to eq(40)
36 | expect(result.champion_points_since_last_level).to eq(12756)
37 | end
38 | end
39 |
40 | describe "#all" do
41 | before { stub_request(subject, 'champion-masteries', 'champion-masteries/by-summoner/1') }
42 | let(:result) { subject.all summoner_id: 1 }
43 |
44 | it "returns an Array of ChampionMastery" do
45 | expect(result).to be_a Array
46 | expect(result.map(&:class).uniq).to eq [DynamicModel]
47 | end
48 |
49 | it "fetches ChampionMastery properties from the API" do
50 | fixture = load_fixture('champion-masteries', described_class.api_version)
51 | expect(result.count).to eq fixture.count
52 | end
53 | end
54 | end
55 |
--------------------------------------------------------------------------------
/spec/support/helpers.rb:
--------------------------------------------------------------------------------
1 | module Helpers
2 | def underscore(s)
3 | s.to_s.scan(/[A-Z][a-z]*/).join("_").downcase
4 | end
5 |
6 | def camelize(s)
7 | s[0] + s.to_s.split("_").each {|s| s.capitalize! }.join("")[1..-1]
8 | end
9 |
10 | def load_fixture(subject, version, method='get')
11 | fixture_file = File.join(SPEC_ROOT, 'fixtures', "#{version}", "#{method}-#{subject}.json")
12 | JSON.parse(File.read(fixture_file, :encoding => "utf-8"))
13 | end
14 |
15 | def expect_init_attribute(subject, attribute)
16 | expect(subject.new(camelize(attribute) => "foo").send(attribute)).to eq("foo")
17 | end
18 |
19 | def expect_read_only_attribute(subject, attribute)
20 | expect { subject.new.send("#{attribute}=".to_sym, "bar") }.to raise_error(NoMethodError)
21 | end
22 |
23 | def error_401
24 | response = {"status" => {"message" => "Foo", "status_code" => 401}}
25 | response.send :instance_eval do
26 | def code; 401; end
27 | def not_found?; false; end
28 | end
29 | response
30 | end
31 |
32 | def error_429
33 | response = {"status" => {"message" => "Foo", "status_code" => 429}}
34 | response.send :instance_eval do
35 | def code; 429; end
36 | def not_found?; false; end
37 | end
38 | response
39 | end
40 |
41 | def summoners
42 | {
43 | "euw" => "30743211",
44 | "na" => "5908",
45 | "eune" => "35778105"
46 | }
47 | end
48 |
49 | def stub_request(request_object, fixture_name, url, params={})
50 | request_class = request_object.class
51 | full_url = request_object.api_url(url, params)
52 | fixture_json = load_fixture(fixture_name, request_class.api_version, :get)
53 |
54 | expect(request_class).to receive(:get).with(full_url, instance_of(Hash)).and_return(fixture_json)
55 | end
56 |
57 | def stub_request_raw(request_object, raw_response, url, params={})
58 | request_class = request_object.class
59 | full_url = request_object.api_url(url, params)
60 |
61 | expect(request_class).to receive(:get).with(full_url, instance_of(Hash)).and_return(raw_response)
62 | end
63 | end
64 |
--------------------------------------------------------------------------------
/spec/acceptance_spec.rb:
--------------------------------------------------------------------------------
1 | require "spec_helper"
2 | require "lol"
3 | require "awesome_print"
4 |
5 | # Requires connection
6 | describe "Live API testing", :remote => true do
7 | before(:all) do
8 | VCR.configure do |c|
9 | c.allow_http_connections_when_no_cassette = true
10 | end
11 | end
12 |
13 | let(:api_key) { ENV['RIOT_GAMES_API_KEY'] }
14 | subject { Lol::Client.new api_key }
15 | let(:fallback) { Lol::Client.new ENV['RIOT_GAMES_NEW_KEY'] }
16 |
17 | # @TODO: Maybe have aliases with singular / plural names so I can do subject.champions?
18 | let (:champions) { subject.champion.get }
19 | let (:intinig) { subject.summoner.by_name("intinig").first }
20 | let (:team) { fallback.team.by_summoner(intinig.id).first }
21 |
22 | describe "champion" do
23 | it "works on the collection" do
24 | expect {champions}.not_to raise_error
25 | end
26 |
27 | it "works on the single champion" do
28 | expect {subject.champion.get(:id => champions.first.id)}.not_to raise_error
29 | end
30 | end
31 |
32 | describe "game" do
33 | it "works on recent games for a summoner" do
34 | expect {fallback.game.recent intinig.id}.not_to raise_error
35 | end
36 | end
37 |
38 | describe "league" do
39 | it "works with get" do
40 | expect {fallback.league.get intinig.id}.not_to raise_error
41 | end
42 |
43 | it "works with entries" do
44 | expect {fallback.league.get_entries intinig.id}.not_to raise_error
45 | end
46 |
47 | it "works with teams" do
48 | expect {fallback.league.by_team team.id}.not_to raise_error
49 | end
50 | end
51 |
52 | describe "lol-static-data" do
53 | pending
54 | end
55 |
56 | describe "match" do
57 | pending
58 | end
59 |
60 | describe "matchhistory" do
61 | pending
62 | end
63 |
64 | describe "stats" do
65 | pending
66 | end
67 |
68 | describe "summoner" do
69 | pending
70 | end
71 |
72 | describe "team" do
73 | pending
74 | end
75 |
76 | after(:all) do
77 | VCR.configure do |c|
78 | c.allow_http_connections_when_no_cassette = true
79 | end
80 | end
81 | end
82 |
--------------------------------------------------------------------------------
/spec/lol/dynamic_model_spec.rb:
--------------------------------------------------------------------------------
1 | require 'spec_helper'
2 | require 'lol/dynamic_model'
3 |
4 | include Lol
5 |
6 | describe DynamicModel do
7 | shared_examples 'enables date parsing' do
8 | let(:timestamp_value) { 1423243755000 }
9 | subject { DynamicModel.new attribute => timestamp_value}
10 | let(:attribute_value) { subject.send attribute }
11 |
12 | it 'and casts the value to Time' do
13 | expect(attribute_value).to be_a Time
14 | end
15 |
16 | it 'and divides the number by 1000' do
17 | expect(attribute_value.to_i).to eq timestamp_value / 1000
18 | end
19 |
20 | it 'and ignores casting when the value is not an integer' do
21 | subject = DynamicModel.new attribute => 'foo bar'
22 | expect(subject.send attribute).not_to be_a Time
23 | end
24 | end
25 |
26 | context 'when is not initialized with an hash' do
27 | subject { DynamicModel.new 'not enumerable data' }
28 |
29 | it 'raises an argument error' do
30 | expect{ subject }.to raise_error ArgumentError, 'An hash is required as parameter'
31 | end
32 | end
33 |
34 | context 'when initialized with hash' do
35 | subject { DynamicModel.new key: 'value' }
36 |
37 | it 'does not raise error' do
38 | expect { subject }.not_to raise_error
39 | end
40 |
41 | it 'defines a method per each argument key' do
42 | expect(subject).to respond_to(:key)
43 | end
44 | end
45 |
46 | %w(date at).each do |attr_name|
47 | context "when an hash key is called '#{attr_name}'" do
48 | it_behaves_like 'enables date parsing' do
49 | let(:attribute) { attr_name }
50 | end
51 | end
52 |
53 | context "when an hash key ends with '_#{attr_name}'" do
54 | it_behaves_like 'enables date parsing' do
55 | let(:attribute) { "a2342_#{attr_name}"}
56 | end
57 | end
58 | end
59 |
60 | context "when an hash value is an array" do
61 | it 'preserves the array' do
62 | subject = DynamicModel.new foo: ['foo']
63 | expect(subject.foo).to eq ['foo']
64 | end
65 |
66 | context 'and one or more array items are hashes' do
67 | subject { DynamicModel.new foo: [{}] }
68 |
69 | it 'casts each hash to another DynamicModel instance' do
70 | expect(subject.foo.map(&:class).uniq).to eq [DynamicModel]
71 | end
72 | end
73 | end
74 | end
75 |
--------------------------------------------------------------------------------
/lib/lol/static_request.rb:
--------------------------------------------------------------------------------
1 | module Lol
2 | class StaticRequest < Request
3 | # @!visibility private
4 | def api_base_path
5 | "/lol/static-data/#{self.class.api_version}"
6 | end
7 |
8 | {
9 | "champion" => "champions",
10 | "item" => "items",
11 | "mastery" => "masteries",
12 | "rune" => "runes",
13 | "summoner_spell" => "summoner_spells"
14 | }.each do |old_endpoint, new_endpoint|
15 | define_method new_endpoint do
16 | Proxy.new self, new_endpoint
17 | end
18 | end
19 |
20 | def language_strings params={}
21 | perform_request(api_url "language-strings", params).to_hash["data"]
22 | end
23 |
24 | def languages
25 | perform_request api_url "languages"
26 | end
27 |
28 | def maps
29 | Proxy.new self, "maps"
30 | end
31 |
32 | def profile_icons params={}
33 | all "profile_icons", params
34 | end
35 |
36 | def realms
37 | Proxy.new self, "realms"
38 | end
39 |
40 | def reforged_runes
41 | Proxy.new self, "reforged_runes"
42 | end
43 |
44 | def versions
45 | Proxy.new self, "versions"
46 | end
47 |
48 | def get(endpoint, id=nil, params={})
49 | return perform_request(api_url("versions")) if endpoint == "versions"
50 | id ? find(endpoint, id, params) : all(endpoint, params)
51 | end
52 |
53 | private
54 |
55 | def find(endpoint, id, params={})
56 | OpenStruct.new \
57 | perform_request(api_url("#{endpoint.dasherize}/#{id}", params)).to_hash
58 | end
59 |
60 | def all(endpoint, params={})
61 | if %w(realms).include? endpoint
62 | OpenStruct.new perform_request(api_url(endpoint.dasherize, params)).to_hash
63 | elsif %w(reforged_runes).include? endpoint
64 | perform_request(api_url(endpoint.dasherize, params)).map do |hash|
65 | OpenStruct.new(hash)
66 | end
67 | else
68 | perform_request(api_url(endpoint.dasherize, params))["data"].map do |id, values|
69 | OpenStruct.new(values.merge(id: values["id"] || id))
70 | end
71 | end
72 | end
73 |
74 | class Proxy
75 | def initialize(request, endpoint)
76 | @request = request
77 | @endpoint = endpoint
78 | end
79 |
80 | def get(id=nil, params={})
81 | if id.is_a?(Hash)
82 | params = id
83 | id = nil
84 | end
85 |
86 | @request.get @endpoint, id, params
87 | end
88 | end
89 | end
90 | end
91 |
--------------------------------------------------------------------------------
/spec/fixtures/v3/get-matches-recent.json:
--------------------------------------------------------------------------------
1 | {"matches":[{"platformId":"EUW1","gameId":3185083402,"champion":41,"queue":420,"season":8,"timestamp":1494888385722,"role":"SOLO","lane":"TOP"},{"platformId":"EUW1","gameId":3184692827,"champion":60,"queue":0,"season":8,"timestamp":1494881999945,"role":"NONE","lane":"JUNGLE"},{"platformId":"EUW1","gameId":3184652546,"champion":60,"queue":0,"season":8,"timestamp":1494878962107,"role":"NONE","lane":"JUNGLE"},{"platformId":"EUW1","gameId":3184652015,"champion":60,"queue":0,"season":8,"timestamp":1494875667971,"role":"NONE","lane":"JUNGLE"},{"platformId":"EUW1","gameId":3184772187,"champion":64,"queue":420,"season":8,"timestamp":1494871012420,"role":"NONE","lane":"JUNGLE"},{"platformId":"EUW1","gameId":3184673600,"champion":114,"queue":420,"season":8,"timestamp":1494868332986,"role":"SOLO","lane":"TOP"},{"platformId":"EUW1","gameId":3184101351,"champion":114,"queue":420,"season":8,"timestamp":1494851759999,"role":"SOLO","lane":"TOP"},{"platformId":"EUW1","gameId":3184039602,"champion":114,"queue":420,"season":8,"timestamp":1494849918307,"role":"SOLO","lane":"TOP"},{"platformId":"EUW1","gameId":3183878862,"champion":4,"queue":420,"season":8,"timestamp":1494807966896,"role":"SOLO","lane":"MID"},{"platformId":"EUW1","gameId":3183868620,"champion":5,"queue":420,"season":8,"timestamp":1494805667573,"role":"NONE","lane":"JUNGLE"},{"platformId":"EUW1","gameId":3183876056,"champion":412,"queue":610,"season":8,"timestamp":1494804584364,"role":"DUO","lane":"MID"},{"platformId":"EUW1","gameId":3183875342,"champion":412,"queue":610,"season":8,"timestamp":1494803790404,"role":"DUO","lane":"MID"},{"platformId":"EUW1","gameId":3183874708,"champion":412,"queue":610,"season":8,"timestamp":1494802949703,"role":"NONE","lane":"MID"},{"platformId":"EUW1","gameId":3183853468,"champion":107,"queue":420,"season":8,"timestamp":1494800755359,"role":"NONE","lane":"JUNGLE"},{"platformId":"EUW1","gameId":3183814158,"champion":64,"queue":420,"season":8,"timestamp":1494797442606,"role":"NONE","lane":"JUNGLE"},{"platformId":"EUW1","gameId":3183511601,"champion":238,"queue":420,"season":8,"timestamp":1494781445331,"role":"SOLO","lane":"MID"},{"platformId":"EUW1","gameId":3183407264,"champion":238,"queue":420,"season":8,"timestamp":1494778042415,"role":"SOLO","lane":"MID"},{"platformId":"EUW1","gameId":3183339181,"champion":238,"queue":420,"season":8,"timestamp":1494775693663,"role":"SOLO","lane":"MID"},{"platformId":"EUW1","gameId":3183306511,"champion":412,"queue":420,"season":8,"timestamp":1494773237191,"role":"DUO_SUPPORT","lane":"BOTTOM"},{"platformId":"EUW1","gameId":3183235120,"champion":41,"queue":420,"season":8,"timestamp":1494770284775,"role":"SOLO","lane":"TOP"}],"startIndex":0,"endIndex":20,"totalGames":20}
--------------------------------------------------------------------------------
/spec/fixtures/v3/get-lol-status-shard.json:
--------------------------------------------------------------------------------
1 | {"name":"EU West","slug":"euw","locales":["en_GB","fr_FR","it_IT","es_ES","de_DE"],"hostname":"prod.euw1.lol.riotgames.com","region_tag":"eu","services":[{"name":"Game","slug":"game","status":"online","incidents":[{"id":4365,"active":true,"created_at":"2017-05-15T19:39:34.590Z","updates":[{"id":"591a03f6f76f3d01008927e0","author":"","content":"On 17/05/17, starting at 03:30 UK Time (02:30 UTC), ranked queues will be disabled in preparation for patch 7.10. At 05:00 UK Time (04:00 UTC), the servers will be shut down and all games currently in progress will end in a draw and stats will not be recorded. We estimate the battlegrounds will be unavailable for 3 hours.","severity":"info","created_at":"2017-05-15T19:39:34.590Z","updated_at":"2017-05-15T19:39:34.590Z","translations":[{"locale":"fr_FR","heading":"7.10","content":"Le 17/05/17 à partir de 04:30 heure de l'hexagone (02:30 UTC), les parties classées seront désactivées pour préparer le patch 7.10. À 06:00 (04:00 UTC), les serveurs seront fermés, toutes les parties en cours prendront fin sur un match nul et les statistiques ne seront pas prises en compte. Nous estimons l'indisponibilité des champs de justice à 3 heures."},{"locale":"it_IT","heading":"7.10","content":"Dalle 04:30 italiane (02:30 UTC) del 17/05/17, le code classificate verranno disabilitate in preparazione per la patch 7.10. Alle 06:00 italiane (04:00 UTC) del 17/05/17, i server andranno offline e tutte le partite in corso finiranno in un pareggio e le statistiche non verranno registrate. Le nostre arene dovrebbero essere offline per circa 3 ore."},{"locale":"es_ES","heading":"7.10","content":"El 17/05/17, desde las 04:30 horario peninsular (02:30 UTC), las colas de clasificatorias estarán desactivadas en preparación para la versión 7.10. A las 06:00 horario peninsular (04:00 UTC), los servidores se deshabilitarán, las partidas que estén en juego acabarán con empate y las estadísticas no serán registradas. Los campos de batalla estarán desactivados durante unas 3 horas."},{"locale":"de_DE","heading":"7.10","content":"Am 17/05/17, um 04:30 Uhr deutscher Zeit (02:30 UTC), werden die Ranglistenwarteschlangen in Vorbereitung auf den Patch 7.10 deaktiviert. Um 06:00 Uhr deutscher Zeit (04:00 UTC), werden die Server heruntergefahren und alle laufenden Spiele werden in einem Unentschieden enden, wobei keine Statistiken aufgezeichnet werden. Wir gehen davon aus, dass die Richtfelder für etwa 3 Stunden nicht verfügbar sein werden."}]}]}]},{"name":"Store","slug":"store","status":"online","incidents":[]},{"name":"Website","slug":"website","status":"online","incidents":[]},{"name":"Client","slug":"client","status":"online","incidents":[]},{"name":"League client update","slug":"league client update","status":"online","incidents":[]}]}
--------------------------------------------------------------------------------
/spec/fixtures/v3/get-masteries.json:
--------------------------------------------------------------------------------
1 | {"summonerId":100012,"pages":[{"id":34059355,"name":"ad critique","current":false,"masteries":[{"id":6343,"rank":1},{"id":6131,"rank":5},{"id":6122,"rank":1},{"id":6331,"rank":5},{"id":6111,"rank":5},{"id":6141,"rank":1},{"id":6312,"rank":5},{"id":6322,"rank":1},{"id":6161,"rank":1},{"id":6151,"rank":5}]},{"id":34059356,"name":"ad vitesse","current":false,"masteries":[{"id":6343,"rank":1},{"id":6131,"rank":5},{"id":6122,"rank":1},{"id":6331,"rank":5},{"id":6111,"rank":5},{"id":6141,"rank":1},{"id":6312,"rank":5},{"id":6322,"rank":1},{"id":6162,"rank":1},{"id":6151,"rank":5}]},{"id":34059357,"name":"cast AP","current":false,"masteries":[{"id":6343,"rank":1},{"id":6131,"rank":5},{"id":6114,"rank":5},{"id":6122,"rank":1},{"id":6331,"rank":5},{"id":6312,"rank":5},{"id":6142,"rank":1},{"id":6322,"rank":1},{"id":6154,"rank":5},{"id":6164,"rank":1}]},{"id":34059358,"name":"Burst AP","current":false,"masteries":[{"id":6343,"rank":1},{"id":6114,"rank":5},{"id":6122,"rank":1},{"id":6331,"rank":5},{"id":6312,"rank":5},{"id":6142,"rank":1},{"id":6322,"rank":1},{"id":6351,"rank":5},{"id":6134,"rank":5},{"id":6362,"rank":1}]},{"id":34059359,"name":"tank","current":false,"masteries":[{"id":6121,"rank":1},{"id":6223,"rank":1},{"id":6114,"rank":5},{"id":6242,"rank":1},{"id":6232,"rank":3},{"id":6312,"rank":5},{"id":6263,"rank":1},{"id":6322,"rank":1},{"id":6212,"rank":5},{"id":6231,"rank":2},{"id":6252,"rank":5}]},{"id":34059360,"name":"support agressif","current":false,"masteries":[{"id":6342,"rank":1},{"id":6121,"rank":1},{"id":6311,"rank":5},{"id":6131,"rank":5},{"id":6114,"rank":5},{"id":6331,"rank":5},{"id":6142,"rank":1},{"id":6322,"rank":1},{"id":6154,"rank":5},{"id":6164,"rank":1}]},{"id":34059361,"name":"support deffensif","current":false,"masteries":[{"id":6342,"rank":1},{"id":6241,"rank":1},{"id":6311,"rank":5},{"id":6221,"rank":1},{"id":6211,"rank":5},{"id":6322,"rank":1},{"id":6332,"rank":5},{"id":6231,"rank":5},{"id":6363,"rank":1},{"id":6352,"rank":5}]},{"id":34059362,"name":"jungle bruiser","current":false,"masteries":[{"id":6121,"rank":1},{"id":6343,"rank":1},{"id":6311,"rank":5},{"id":6114,"rank":5},{"id":6331,"rank":5},{"id":6321,"rank":1},{"id":6142,"rank":1},{"id":6351,"rank":5},{"id":6134,"rank":5},{"id":6362,"rank":1}]},{"id":34059363,"name":"jungle mage","current":false,"masteries":[{"id":6121,"rank":1},{"id":6343,"rank":1},{"id":6311,"rank":5},{"id":6131,"rank":5},{"id":6114,"rank":5},{"id":6331,"rank":5},{"id":6321,"rank":1},{"id":6142,"rank":1},{"id":6154,"rank":5},{"id":6164,"rank":1}]},{"id":34059364,"name":"AP DE CHEZ AP","current":true,"masteries":[{"id":6121,"rank":1},{"id":6114,"rank":5},{"id":6311,"rank":5},{"id":6343,"rank":1},{"id":6331,"rank":5},{"id":6142,"rank":1},{"id":6322,"rank":1},{"id":6351,"rank":5},{"id":6134,"rank":5},{"id":6362,"rank":1}]}]}
--------------------------------------------------------------------------------
/spec/lol/invalid_api_response_spec.rb:
--------------------------------------------------------------------------------
1 | require "spec_helper"
2 | require "lol"
3 |
4 | include Lol
5 |
6 | describe InvalidAPIResponse do
7 | subject { InvalidAPIResponse.new "foo", "status" => { "message" => "bar"} }
8 |
9 | it "does not crash on initialization" do
10 | expect { subject }.not_to raise_error
11 | end
12 |
13 | it "assigns raw" do
14 | expect(subject.raw).not_to be_nil
15 | end
16 |
17 | context "when the received response is an hash with a 'status' key" do
18 | it "sets a certain message including the status message" do
19 | expect(subject.message).to match /^bar/
20 | end
21 |
22 | it "sets a certain message including the url" do
23 | expect(subject.message).to match /foo$/
24 | end
25 | end
26 |
27 | context "when the received response has no 'status' key" do
28 | subject { InvalidAPIResponse.new "foo", headers: {}, body: 'asd' }
29 |
30 | it "sets a certain message including 'Unknown Error'" do
31 | expect(subject.message).to match /^Unknown Error/
32 | end
33 |
34 | it "sets a certain message including the url" do
35 | expect(subject.message).to match /foo$/
36 | end
37 | end
38 |
39 | describe '#raw' do
40 | describe ':headers' do
41 | it 'equals to headers in the response hash' do
42 | subject = InvalidAPIResponse.new 'foo', OpenStruct.new(headers: { a: 1 })
43 | expect(subject.raw[:headers]).to eq a: 1
44 | end
45 |
46 | it 'equals an empty hash if response contain no headers' do
47 | subject = InvalidAPIResponse.new 'foo', OpenStruct.new
48 | expect(subject.raw[:headers]).to eq Hash.new
49 | end
50 | end
51 |
52 | describe ':body' do
53 | it 'equals to #parsed_response' do
54 | subject = InvalidAPIResponse.new 'foo', OpenStruct.new(parsed_response: 'a')
55 | expect(subject.raw[:body]).to eq 'a'
56 | end
57 |
58 | it 'equals to #body if response has no #parsed_response' do
59 | subject = InvalidAPIResponse.new 'foo', OpenStruct.new(body: 'a')
60 | expect(subject.raw[:body]).to eq 'a'
61 | end
62 |
63 | it 'equals to the response itself if response has no #body or #parsed_response' do
64 | r = OpenStruct.new
65 | expect(InvalidAPIResponse.new('foo', r).raw[:body]).to eq r
66 | end
67 | end
68 |
69 | describe ':status' do
70 | it 'equals to response status if response is an hash' do
71 | subject = InvalidAPIResponse.new 'foo', 'status' => { 'message' => 'a' }
72 | expect(subject.raw[:status]).to eq 'a'
73 | end
74 |
75 | it 'equals to Unknown Error otherwise' do
76 | subject = InvalidAPIResponse.new 'foo', OpenStruct.new
77 | expect(subject.raw[:status]).to eq 'Unknown Error'
78 | end
79 | end
80 | end
81 | end
82 |
--------------------------------------------------------------------------------
/lib/lol/match_request.rb:
--------------------------------------------------------------------------------
1 | module Lol
2 | # Bindings for the Match API.
3 | #
4 | # See: https://developer.riotgames.com/api-methods/#match-v3
5 | class MatchRequest < Request
6 | # @!visibility private
7 | def api_base_path
8 | "/lol/match/#{self.class.api_version}"
9 | end
10 |
11 | # Get match by match ID.
12 | # @param [Integer] match_id Match ID
13 | # @option options [Integer] forAccountId Optional used to identify the participant to be unobfuscated
14 | # @option options [Integer] forPlatformId Optional used to identify the participant to be unobfuscated (for when user have changed regions)
15 | # @return [DynamicModel] Match representation
16 | def find options={}, match_id:
17 | DynamicModel.new perform_request api_url "matches/#{match_id}", options
18 | end
19 |
20 | # Get match timeline by match ID.
21 | # @param [Integer] match_id Match ID
22 | # @return [DynamicModel] Timeline represantion
23 | def find_timeline match_id
24 | DynamicModel.new perform_request api_url "timelines/by-match/#{match_id}"
25 | end
26 |
27 | # Get match IDs by tournament code.
28 | # @param [String] tournament_code Tournament code
29 | # @return [Array] List of match IDs
30 | def ids_by_tournament_code tournament_code
31 | perform_request api_url "matches/by-tournament-code/#{tournament_code}/ids"
32 | end
33 |
34 | # Get match by match ID and tournament code.
35 | # @param [Integer] match_id Match ID
36 | # @param [String] tournament_code Tournament code the match belongs to
37 | # @return [DynamicModel] Match representation
38 | def find_by_tournament match_id, tournament_code
39 | DynamicModel.new perform_request api_url "matches/#{match_id}/by-tournament-code/#{tournament_code}"
40 | end
41 |
42 | # Get matchlist for ranked games played on given account ID and platform ID and filtered using given filter parameters, if any.
43 | # @param [Integer] account_id Account ID
44 | # @param [Hash] options the options to pass to the call
45 | # @option options [Array] queue Set of queue IDs for which to filtering matchlist.
46 | # @option options [Integer] beginTime The begin time to use for filtering matchlist specified as epoch milliseconds.
47 | # @option options [Integer] endTime The end time to use for filtering matchlist specified as epoch milliseconds.
48 | # @option options [Integer] beginIndex The begin index to use for filtering matchlist.
49 | # @option options [Integer] endIndex The end index to use for filtering matchlist.
50 | # @option options [Array] season Set of season IDs for which to filtering matchlist.
51 | # @option options [Array] champion Set of champion IDs for which to filtering matchlist.
52 | # @return [DynamicModel] MatchList represantion
53 | def all options={}, account_id:
54 | DynamicModel.new perform_request api_url "matchlists/by-account/#{account_id}", options
55 | end
56 |
57 | # Get matchlist for last 20 matches played on given account ID and platform ID.
58 | # @param [Integer] account_id Account ID
59 | # @return [DynamicModel] MatchList represantion
60 | def recent account_id:
61 | DynamicModel.new perform_request api_url "matchlists/by-account/#{account_id}/recent"
62 | end
63 | end
64 | end
65 |
--------------------------------------------------------------------------------
/spec/support/model_helpers.rb:
--------------------------------------------------------------------------------
1 | shared_examples 'attribute' do
2 | let(:setter) { "#{attribute}=" }
3 |
4 | it 'is read only' do
5 | expect(subject).to_not respond_to setter
6 | end
7 | end
8 |
9 | shared_examples 'Lol model' do
10 | let(:subject_class) { subject.class }
11 |
12 | describe '#new' do
13 | it "takes an option hash as argument" do
14 | expect { subject_class.new valid_attributes }.not_to raise_error
15 | end
16 |
17 | it 'raises an error if an attribute is not allowed' do
18 | expect { subject_class.new({ :foo => :bar }) }.to raise_error NoMethodError
19 | end
20 |
21 | it 'sets the given option hash as #raw' do
22 | expect(subject_class.new(valid_attributes).raw).to eq valid_attributes
23 | end
24 | end
25 |
26 | describe '#raw' do
27 | it_behaves_like 'attribute' do
28 | let(:attribute) { 'raw' }
29 | end
30 | end
31 | end
32 |
33 | shared_examples 'plain attribute' do
34 | let(:subject_class) { subject.class }
35 | let(:setter) { "#{attribute}=" }
36 |
37 | it_behaves_like 'attribute'
38 |
39 | context 'during #new' do
40 | it 'is set if the hash contains the attribute name "underscored"' do
41 | model = subject_class.new attribute => attribute_value
42 | expect(model.send attribute).to eq attribute_value
43 | end
44 |
45 | it 'is set if the hash contains the attribute name "camelized"' do
46 | model = subject_class.new camelize(attribute) => attribute_value
47 | expect(model.send attribute).to eq attribute_value
48 | end
49 | end
50 | end
51 |
52 | shared_examples 'time attribute' do
53 | let(:subject_class) { subject.class }
54 | let(:setter) { "#{attribute}=" }
55 |
56 | it_behaves_like 'plain attribute' do
57 | let(:attribute_value) { Time.now }
58 | end
59 |
60 | it "does not parse the value is it isn't a Numeric value" do
61 | model = subject_class.new(attribute => Date.today)
62 | expect(model.send attribute).to be_a Date
63 | end
64 |
65 | it "works with LoL format" do
66 | model = subject_class.new(attribute => 1386804971247)
67 | expect(model.send(attribute).year).to eq 2013
68 | end
69 | end
70 |
71 | shared_examples 'collection attribute' do
72 | let(:subject_class) { subject.class }
73 | let(:setter) { "#{attribute}=" }
74 |
75 | it_behaves_like 'attribute'
76 |
77 | it 'is sets if the hash contains the attribute name "underscored"' do
78 | value = respond_to?(:attribute_value) && attribute_value || [{}, {}]
79 | model = subject_class.new({ attribute => value })
80 | expect(model.send(attribute).size).to eq 2
81 | end
82 |
83 | it 'is set if the hash contains the attribute name "camelized"' do
84 | value = respond_to?(:attribute_value) && attribute_value || [{}, {}]
85 | model = subject_class.new({ camelize(attribute) => value })
86 | expect(model.send(attribute).size).to eq 2
87 | end
88 |
89 | context 'if the value is enumerable' do
90 | context 'and contains items as Hash' do
91 | it 'parses the item' do
92 | value = respond_to?(:attribute_value) && attribute_value || [{}, {}]
93 | model = subject_class.new attribute => value
94 | expect(model.send(attribute).map(&:class).uniq).to eq [attribute_class]
95 | end
96 | end
97 |
98 | context 'and contains items as non-Hash' do
99 | it 'does not parse the item' do
100 | model = subject_class.new attribute => [attribute_class.new, Object.new]
101 | expect(model.send(attribute).map(&:class).uniq).to eq [attribute_class, Object]
102 | end
103 | end
104 | end
105 | end
106 |
--------------------------------------------------------------------------------
/spec/fixtures/v3/get-static-summoner-spells.json:
--------------------------------------------------------------------------------
1 | {"type":"summoner","version":"7.9.1","data":{"SummonerBarrier":{"name":"Barrier","description":"Shields your champion from 115-455 damage (depending on champion level) for 2 seconds.","summonerLevel":4,"id":21,"key":"SummonerBarrier"},"SummonerPoroRecall":{"name":"To the King!","description":"Quickly travel to the Poro King's side.","summonerLevel":1,"id":30,"key":"SummonerPoroRecall"},"SummonerBoost":{"name":"Cleanse","description":"Removes all disables (excluding suppression) and summoner spell debuffs affecting your champion and lowers the duration of incoming disables by 65% for 3 seconds.","summonerLevel":6,"id":1,"key":"SummonerBoost"},"SummonerDarkStarChampSelect2":{"name":"Disabled Summoner Spells","description":"Summoner spells are disabled in this mode.","summonerLevel":1,"id":36,"key":"SummonerDarkStarChampSelect2"},"SummonerTeleport":{"name":"Teleport","description":"After channeling for 4.5 seconds, teleports your champion to target allied structure, minion, or ward.","summonerLevel":6,"id":12,"key":"SummonerTeleport"},"SummonerDarkStarChampSelect1":{"name":"Disabled Summoner Spells","description":"Summoner spells are disabled in this mode.","summonerLevel":1,"id":35,"key":"SummonerDarkStarChampSelect1"},"SummonerFlash":{"name":"Flash","description":"Teleports your champion a short distance toward your cursor's location.","summonerLevel":8,"id":4,"key":"SummonerFlash"},"SummonerSnowball":{"name":"Mark","description":"Throw a snowball in a straight line at your enemies. If it hits an enemy, they become marked, granting True Sight, and your champion can quickly travel to the marked target as a follow up.","summonerLevel":1,"id":32,"key":"SummonerSnowball"},"SummonerHeal":{"name":"Heal","description":"Restores 90-345 Health (depending on champion level) and grants 30% Movement Speed for 1 second to you and target allied champion. This healing is halved for units recently affected by Summoner Heal.","summonerLevel":1,"id":7,"key":"SummonerHeal"},"SummonerMana":{"name":"Clarity","description":"Restores 50% of your champion's maximum Mana. Also restores allies for 25% of their maximum Mana.","summonerLevel":1,"id":13,"key":"SummonerMana"},"SummonerPoroThrow":{"name":"Poro Toss","description":"Toss a Poro at your enemies. If it hits, you can quickly travel to your target as a follow up.","summonerLevel":1,"id":31,"key":"SummonerPoroThrow"},"SummonerSmite":{"name":"Smite","description":"Deals 390-1000 true damage (depending on champion level) to target epic or large monster or enemy minion. Restores Health based on your maximum life when used against monsters.","summonerLevel":10,"id":11,"key":"SummonerSmite"},"SummonerSiegeChampSelect1":{"name":"Nexus Siege: Siege Weapon Slot","description":"In Nexus Siege, Summoner Spells are replaced with Siege Weapon Slots. Spend Crystal Shards to buy single-use Siege Weapons from the item shop, then use your Summoner Spell keys to activate them!","summonerLevel":1,"id":33,"key":"SummonerSiegeChampSelect1"},"SummonerSiegeChampSelect2":{"name":"Nexus Siege: Siege Weapon Slot","description":"In Nexus Siege, Summoner Spells are replaced with Siege Weapon Slots. Spend Crystal Shards to buy single-use Siege Weapons from the item shop, then use your Summoner Spell keys to activate them!","summonerLevel":1,"id":34,"key":"SummonerSiegeChampSelect2"},"SummonerExhaust":{"name":"Exhaust","description":"Exhausts target enemy champion, reducing their Movement Speed by 30%, and their damage dealt by 40% for 2.5 seconds.","summonerLevel":4,"id":3,"key":"SummonerExhaust"},"SummonerDot":{"name":"Ignite","description":"Ignites target enemy champion, dealing 70-410 true damage (depending on champion level) over 5 seconds, grants you vision of the target, and reduces healing effects on them for the duration.","summonerLevel":10,"id":14,"key":"SummonerDot"},"SummonerHaste":{"name":"Ghost","description":"Your champion gains increased Movement Speed and can move through units for 10 seconds. Grants a maximum of 28-45% (depending on champion level) Movement Speed after accelerating for 2 seconds.","summonerLevel":1,"id":6,"key":"SummonerHaste"}}}
--------------------------------------------------------------------------------
/spec/lol/static_request_spec.rb:
--------------------------------------------------------------------------------
1 | require "spec_helper"
2 | require "lol"
3 |
4 | include Lol
5 |
6 | describe StaticRequest do
7 | let(:subject) { StaticRequest.new("api_key", "euw") }
8 |
9 | {
10 | "champion" => "champions",
11 | "item" => "items",
12 | "mastery" => "masteries",
13 | "rune" => "runes",
14 | "summoner_spell" => "summoner_spells"
15 | }.each do |old_edpoint, new_endpoint|
16 | describe "##{new_endpoint}" do
17 | it "returns a Proxy" do
18 | expect(subject.send(new_endpoint).class).to eq(StaticRequest::Proxy)
19 | end
20 |
21 | describe "#get" do
22 | let(:fixture_name) { "static-#{new_endpoint.dasherize}" }
23 |
24 | it "proxies get to StaticRequest with the correct endpoint" do
25 | expect(subject).to receive(:get).with(new_endpoint, anything, anything)
26 | subject.send(new_endpoint).get
27 | end
28 |
29 | context "without an id" do
30 | let(:fixture) { load_fixture(fixture_name, StaticRequest.api_version) }
31 |
32 | let(:result) { subject.public_send(new_endpoint).get }
33 |
34 | before { stub_request(subject, fixture_name, "#{new_endpoint.dasherize}") }
35 |
36 | it "returns an Array of OpenStruct" do
37 | expect(result).to be_a Array
38 | expect(result.map(&:class).uniq).to eq([OpenStruct])
39 | end
40 |
41 | it "fetches #{new_endpoint} from the API" do
42 | expect(result.size).to eq(fixture["data"].size)
43 | end
44 | end
45 |
46 | context "with an id" do
47 | it "returns an OpenStruct" do
48 | stub_request(subject, "#{fixture_name}-by-id", "#{new_endpoint.dasherize}/1")
49 | expect(subject.public_send(new_endpoint).get 1).to be_a OpenStruct
50 | end
51 | end
52 | end
53 | end
54 | end
55 |
56 | describe "#reforged_runes" do
57 | it "returns a Proxy" do
58 | expect(subject.reforged_runes.class).to eq(StaticRequest::Proxy)
59 | end
60 |
61 | describe "#get" do
62 | let(:fixture_name) { "static-reforged-runes" }
63 |
64 | it "proxies get to StaticRequest with the correct endpoint" do
65 | expect(subject).to receive(:get).with('reforged_runes', anything, anything)
66 | subject.reforged_runes.get
67 | end
68 |
69 | context "without an id" do
70 | let(:fixture) { load_fixture(fixture_name, StaticRequest.api_version) }
71 |
72 | let(:result) { subject.reforged_runes.get }
73 |
74 | before { stub_request(subject, fixture_name, "reforged-runes") }
75 |
76 | it "returns an Array of OpenStruct" do
77 | expect(result).to be_a Array
78 | expect(result.map(&:class).uniq).to eq([OpenStruct])
79 | end
80 |
81 | it "fetches reforged_runes from the API" do
82 | expect(result.size).to eq(fixture.size)
83 | end
84 | end
85 |
86 | context "with an id" do
87 | it "returns an OpenStruct" do
88 | stub_request(subject, "#{fixture_name}-by-id", "reforged-runes/1")
89 | expect(subject.reforged_runes.get 1).to be_a OpenStruct
90 | end
91 | end
92 | end
93 | end
94 |
95 | describe "#maps" do
96 | let(:result) { subject.maps.get }
97 |
98 | before(:each) { stub_request(subject, 'static-maps', 'maps') }
99 |
100 | it "returns an Array of OpenStructs" do
101 | expect(result).to be_a Array
102 | expect(result.map(&:class).uniq).to eq [OpenStruct]
103 | end
104 | end
105 |
106 | describe "#realms" do
107 | let(:result) { subject.realms.get }
108 |
109 | before(:each) { stub_request(subject, 'static-realms', 'realms') }
110 |
111 | it "returns an OpenStruct" do
112 | expect(result).to be_a OpenStruct
113 | end
114 | end
115 |
116 | describe "#versions" do
117 | let(:result) { subject.versions.get }
118 |
119 | before(:each) { stub_request(subject, 'static-versions', 'versions') }
120 |
121 | it "returns an Array" do
122 | expect(result).to be_an(Array)
123 | end
124 | end
125 |
126 | end
127 |
--------------------------------------------------------------------------------
/lib/lol/client.rb:
--------------------------------------------------------------------------------
1 | module Lol
2 | class Client
3 |
4 | # @!attribute [rw] region
5 | # @return [String] name of region
6 | attr_accessor :region
7 |
8 | # @!attribute [r] api_key
9 | # @return [String] the API key that has been used
10 | attr_reader :api_key
11 |
12 | # @!attribute [r] ttl
13 | # @return [Integer] the ttl on cached requests
14 | attr_reader :ttl
15 |
16 | # @!attribute[r] rate_limiter
17 | # @return [Object] the rate limiter, if one exists (else nil)
18 | attr_reader :rate_limiter
19 |
20 | # @return [ChampionRequest]
21 | def champion
22 | @champion_request ||= ChampionRequest.new(api_key, region, cache_store, rate_limiter)
23 | end
24 |
25 | # @return [ChampionMasteryRequest]
26 | def champion_mastery
27 | @champion_mastery_request ||= ChampionMasteryRequest.new(api_key, region, cache_store, rate_limiter)
28 | end
29 |
30 | # @return [MatchRequest]
31 | def match
32 | @match_request ||= MatchRequest.new(api_key, region, cache_store, rate_limiter)
33 | end
34 |
35 | # @return [LeagueRequest]
36 | def league
37 | @league_request ||= LeagueRequest.new(api_key, region, cache_store, rate_limiter)
38 | end
39 |
40 | # @return [RunesRequest]
41 | def runes
42 | @runes_request ||= RunesRequest.new(api_key, region, cache_store, rate_limiter)
43 | end
44 |
45 | # @return [MasteriesRequest]
46 | def masteries
47 | @masteries_request ||= MasteriesRequest.new(api_key, region, cache_store, rate_limiter)
48 | end
49 |
50 | # @return [SpectatorRequest]
51 | def spectator
52 | @spectator_request ||= SpectatorRequest.new(api_key, region, cache_store, rate_limiter)
53 | end
54 |
55 | # @return [SummonerRequest]
56 | def summoner
57 | @summoner_request ||= SummonerRequest.new(api_key, region, cache_store, rate_limiter)
58 | end
59 |
60 | # @return [StaticRequest]
61 | def static
62 | @static_request ||= StaticRequest.new(api_key, region, cache_store, rate_limiter)
63 | end
64 |
65 | # @return [LolStatusRequest]
66 | def lol_status
67 | @lol_status ||= LolStatusRequest.new(api_key, region, cache_store, rate_limiter)
68 | end
69 |
70 | # @return [CurrentGameRequest]
71 | def current_game
72 | @current_game ||= CurrentGameRequest.new(api_key, region, cache_store, rate_limiter)
73 | end
74 |
75 | # @return [FeaturedGamesRequest]
76 | def featured_games
77 | @featured_games ||= FeaturedGamesRequest.new(api_key, region, cache_store, rate_limiter)
78 | end
79 |
80 | # @return [TournamentProviderRequest]
81 | def tournament
82 | @tournament ||= TournamentRequest.new(api_key, region, cache_store, rate_limiter)
83 | end
84 |
85 | # Initializes a Lol::Client
86 | # @param api_key [String]
87 | # @param options [Hash]
88 | # @option options [String] :region ("EUW") The region on which the requests will be made
89 | # @option options [String] :redis the redis url to use for caching
90 | # @option options [Integer] :ttl (900) the cache ttl
91 | # @option options [Fixnum] :rate_limit_requests number of requests
92 | # @option options [Fixnum] :rate_limit_seconds number of seconds to limit the rate in
93 | # @return [Lol::Client]
94 | def initialize api_key, options = {}
95 | @api_key = api_key
96 | @region = options.delete(:region) || "euw"
97 | set_up_cache(options.delete(:redis), options.delete(:ttl))
98 | set_up_rate_limiter(options.delete(:rate_limit_requests), options.delete(:rate_limit_seconds))
99 | end
100 |
101 | def set_up_cache(redis_url, ttl)
102 | return @cached = false unless redis_url
103 |
104 | @ttl = ttl || 900
105 | @cached = true
106 | @redis = Redis.new :url => redis_url
107 | end
108 |
109 | def set_up_rate_limiter(number_of_requests, number_of_seconds)
110 | return @rate_limited = false unless number_of_requests
111 |
112 | @rate_limited = true
113 | @rate_limiter = GluttonRatelimit::AveragedThrottle.new number_of_requests, number_of_seconds
114 | end
115 |
116 | # Returns an options hash with cache keys
117 | # @return [Hash]
118 | def cache_store
119 | {
120 | redis: @redis,
121 | ttl: @ttl,
122 | cached: @cached,
123 | }
124 | end
125 |
126 | # @return [Boolean] true if requests are automatically rate limited
127 | def rate_limited?
128 | @rate_limiter
129 | end
130 |
131 | # @return [Boolean] true if requests are cached
132 | def cached?
133 | @cached
134 | end
135 |
136 | # @return [Redis] the cache store (if available)
137 | def redis
138 | @redis
139 | end
140 | end
141 | end
142 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # ruby-lol
2 | [](http://badge.fury.io/rb/ruby-lol) [](https://coveralls.io/r/mikamai/ruby-lol) [](https://travis-ci.org/mikamai/ruby-lol) [](https://gemnasium.com/mikamai/ruby-lol) [](http://inch-ci.org/github/mikamai/ruby-lol)
3 |
4 |
5 | ruby-lol is a wrapper to the [Riot Games API](https://developer.riotgames.com).
6 |
7 | ## Installation
8 |
9 | Add this line to your application's Gemfile:
10 |
11 | ```ruby
12 | gem 'ruby-lol'
13 | ```
14 |
15 | And then execute:
16 |
17 | $ bundle
18 |
19 | Or install it yourself as:
20 |
21 | $ gem install ruby-lol
22 |
23 | ## Usage
24 |
25 | [ Outdated, anyone who wants to contribute to this please do it :) ]
26 |
27 | ```ruby
28 | require 'lol'
29 |
30 | client = Lol::Client.new "my_api_key", region: "euw"
31 | # =>
32 |
33 | # You can cache requests using Redis now
34 | # ttl defaults to 900
35 | client = Lol::Client.new "my_api_key", region: "euw", redis: "redis://localhost:6379", ttl: 900
36 |
37 | # You can specify your rate limits so that the library will throttle requests to avoid errors
38 | client = Lol::Client.new "new_api_key", region: "euw", rate_limit_requests: 1, rate_limit_seconds: 10
39 |
40 | # Available Requests
41 | client.champion
42 | # => Lol::ChampionRequest
43 | client.game
44 | # => Lol::GameRequest
45 | client.league
46 | # => Lol::LeagueRequest
47 | client.stats
48 | # => Lol::StatsRequest
49 | client.summoner
50 | # => Lol::SummonerRequest
51 | client.team
52 | # => Lol::TeamRequest
53 |
54 | # Available methods for each request type
55 | client.champion.get
56 | # => Lol::Champion
57 |
58 | client.game.recent(summoner_id)
59 | # => Lol::Game
60 |
61 | client.league.get(summoner_id)
62 | # => Lol::League
63 |
64 | client.stats.summary(summoner_id)
65 | # => Lol::SummaryStats
66 | client.stats.ranked(summoner_id)
67 | # => Lol::RankedStats
68 |
69 | client.summoner.masteries(summoner_id)
70 | # => [Lol::Masterypage]
71 | client.summoner.runes(summoner_id)
72 | # => [Lol::Runepage]
73 | client.summoner.by_name(name)
74 | # => Lol::Summoner
75 | client.summoner.get(summoner_id)
76 | # => Lol::Summoner
77 | client.summoner.name(summoner_ids)
78 | # => [Hash]
79 |
80 | client.team.get(summoner_id)
81 | # => Array
82 | ```
83 |
84 | # Making Static Requests
85 | The Riot API has a [section](http://developer.riotgames.com/api/methods#!/378) carved out for static-data. These requests don't count against your rate limit. The mechanism for using them is similar to the standard requests above.
86 |
87 | Each static endpoint has two possible requests: `get` and `get(id)`. `get` returns an array of OpenStructs representing the data from Riot's API, and `get(id)` returns an OpenStruct with a single record. Here are some examples:
88 |
89 | ```ruby
90 | client.static
91 | # => Lol::StaticRequest
92 | **NOTE**: StaticRequest is not meant to be used directly, but can be!
93 |
94 | client.static.champion
95 | # => Lol::StaticRequest (for endpoint /static-data/champion)
96 |
97 | client.static.champion.get
98 | # => [OpenStruct] (with keys from http://developer.riotgames.com/api/methods#!/378/1349)
99 |
100 | client.static.champion.get(id)
101 | # => OpenStruct (with keys from http://developer.riotgames.com/api/methods#!/378/1349)
102 | ```
103 |
104 | You can also pass query parameters as listed in the Riot API documentation. For example:
105 |
106 | ```ruby
107 |
108 | # returns all attributes
109 | client.static.champion.get(champData: 'all')
110 |
111 | # returns only lore information
112 | client.static.champion.get(champData: 'lore')
113 | ```
114 |
115 | **NOTE**: The realm endpoint is not implemented. Let us know if you need it, but it seemed somewhat unnecessary.
116 |
117 | ## Contributing
118 |
119 | 1. Fork it
120 | 2. Create your feature branch (`git checkout -b my-new-feature`)
121 | 3. Commit your changes (`git commit -am 'Add some feature'`)
122 | 4. Push to the branch (`git push origin my-new-feature`)
123 | 5. Create new Pull Request
124 |
125 | ## Changelog
126 | - 0.9.14 Fixed a caching bug
127 | - 0.9.13 Updated to latest API versions
128 | - 0.9.12 Fixed a caching bug
129 | - 0.9.11 Added caching support via REDIS
130 | - 0.9.7 Updated LeagueRequest to API v2.3
131 | - 0.9.6 Updated SummonerRequest and GameRequest to API v1.3
132 | - 0.9.5 Fixed documentation
133 | - 0.9.4 Completed support for updated API
134 |
135 |
136 | [](https://bitdeli.com/free "Bitdeli Badge")
137 |
--------------------------------------------------------------------------------
/lib/lol/tournament_request.rb:
--------------------------------------------------------------------------------
1 | module Lol
2 | class TournamentRequest < Request
3 | # @!visibility private
4 | def api_base_path
5 | "/lol/tournament/#{self.class.api_version}"
6 | end
7 |
8 | # @!visibility private
9 | def api_base_url
10 | "https://americas.api.riotgames.com"
11 | end
12 |
13 | # Creates a tournament provider and returns its ID.
14 | # @param [String] url The provider's callback URL to which tournament game results in this region should be posted. The URL must be well-formed, use the http or https protocol, and use the default port for the protocol
15 | # @return [Integer] Provider ID
16 | def create_provider url:
17 | body = {
18 | "url" => url,
19 | "region" => region.upcase
20 | }
21 | perform_request api_url("providers"), :post, body
22 | end
23 |
24 | # Creates a tournament and returns its ID.
25 | # @param [Integer] provider_id The provider ID to specify the regional registered provider data to associate this tournament.
26 | # @param [String] name Name of the tournament
27 | # @return [Integer] Tournament ID
28 | def create_tournament provider_id:, name: nil
29 | body = { "providerId" => provider_id, "name" => name }.delete_if do |k, v|
30 | v.nil?
31 | end
32 | perform_request api_url("tournaments"), :post, body
33 | end
34 |
35 | # Create a tournament code for the given tournament.
36 | # @param [Integer] count The number of codes to create (max 1000)
37 | # @param [Integer] tournament_id The tournament ID
38 | # @param [String] spectator_type The spectator type of the game. Valid values are NONE, LOBBYONLY, ALL.
39 | # @param [Integer] team_size The team size of the game. Valid values are 1-5.
40 | # @param [String] pick_type The pick type of the game. Valid values are BLIND_PICK, DRAFT_MODE, ALL_RANDOM, TOURNAMENT_DRAFT.
41 | # @param [String] map_type The map type of the game. Valid values are SUMMONERS_RIFT, TWISTED_TREELINE, CRYSTAL_SCAR, and HOWLING_ABYSS.
42 | # @param [Array] allowed_participants List of participants in order to validate the players eligible to join the lobby.
43 | # @param [String] metadata Optional string that may contain any data in any format, if specified at all. Used to denote any custom information about the game.
44 | # @return [Array] generated tournament codes
45 | def create_codes tournament_id:, count: nil, allowed_participants: nil,
46 | map_type: "SUMMONERS_RIFT", metadata: nil, team_size: 5,
47 | pick_type: "TOURNAMENT_DRAFT", spectator_type: "ALL"
48 | body = {
49 | "allowedSummonerIds" => allowed_participants,
50 | "mapType" => map_type,
51 | "metadata" => metadata,
52 | "pickType" => pick_type,
53 | "spectatorType" => spectator_type,
54 | "teamSize" => team_size
55 | }.compact
56 | uri_params = {
57 | "tournamentId" => tournament_id,
58 | "count" => count
59 | }.compact
60 | perform_request api_url("codes", uri_params), :post, body
61 | end
62 |
63 | # Update the pick type, map, spectator type, or allowed summoners for a code.
64 | # @param [String] tournament_code The tournament code to update
65 | # @param [Array] allowed_participants List of participants in order to validate the players eligible to join the lobby.
66 | # @param [String] map_type The map type of the game. Valid values are SUMMONERS_RIFT, TWISTED_TREELINE, CRYSTAL_SCAR, and HOWLING_ABYSS.
67 | # @param [String] pick_type The pick type of the game. Valid values are BLIND_PICK, DRAFT_MODE, ALL_RANDOM, TOURNAMENT_DRAFT.
68 | # @param [String] spectator_type The spectator type of the game. Valid values are NONE, LOBBYONLY, ALL.
69 | def update_code tournament_code, allowed_participants: nil, map_type: nil, pick_type: nil, spectator_type: nil
70 | body = {
71 | "allowedSummonerIds" => allowed_participants,
72 | "mapType" => map_type,
73 | "pickType" => pick_type,
74 | "spectatorType" => spectator_type
75 | }.compact
76 | perform_request api_url("codes/#{tournament_code}"), :put, body
77 | end
78 |
79 | # Returns the tournament code details
80 | # @param [String] tournament_code the tournament code string
81 | # @return [DynamicModel] A tournament code representation
82 | def find_code tournament_code
83 | DynamicModel.new perform_request api_url "codes/#{tournament_code}"
84 | end
85 |
86 | # Gets a list of lobby events by tournament code
87 | # @param [String] tournament_code the tournament code string
88 | # @return [Array] List of lobby events
89 | def all_lobby_events tournament_code:
90 | result = perform_request api_url "lobby-events/by-code/#{tournament_code}"
91 | result["eventList"].map { |e| DynamicModel.new e }
92 | end
93 | end
94 | end
95 |
--------------------------------------------------------------------------------
/spec/fixtures/v3/get-current-game.json:
--------------------------------------------------------------------------------
1 | {"gameId":3186584028,"mapId":12,"gameMode":"ARAM","gameType":"MATCHED_GAME","gameQueueConfigId":65,"participants":[{"teamId":100,"spell1Id":1,"spell2Id":4,"championId":119,"profileIconId":1212,"summonerName":"FaisaI","bot":false,"summonerId":52576163,"runes":[{"count":9,"runeId":5245},{"count":5,"runeId":5277},{"count":4,"runeId":5289},{"count":9,"runeId":5317},{"count":3,"runeId":5337}],"masteries":[{"rank":5,"masteryId":6111},{"rank":1,"masteryId":6121},{"rank":5,"masteryId":6131},{"rank":1,"masteryId":6141},{"rank":5,"masteryId":6151},{"rank":1,"masteryId":6161},{"rank":5,"masteryId":6312},{"rank":1,"masteryId":6322},{"rank":5,"masteryId":6331},{"rank":1,"masteryId":6343}]},{"teamId":100,"spell1Id":32,"spell2Id":4,"championId":12,"profileIconId":907,"summonerName":"AndersStefan","bot":false,"summonerId":29311441,"runes":[{"count":9,"runeId":5245},{"count":9,"runeId":5289},{"count":9,"runeId":5317},{"count":3,"runeId":5347}],"masteries":[{"rank":5,"masteryId":6114},{"rank":1,"masteryId":6122},{"rank":5,"masteryId":6131},{"rank":1,"masteryId":6142},{"rank":5,"masteryId":6212},{"rank":1,"masteryId":6223},{"rank":5,"masteryId":6232},{"rank":1,"masteryId":6242},{"rank":5,"masteryId":6251},{"rank":1,"masteryId":6261}]},{"teamId":100,"spell1Id":6,"spell2Id":4,"championId":112,"profileIconId":903,"summonerName":"ChaoticHero","bot":false,"summonerId":19892437,"runes":[{"count":9,"runeId":5273},{"count":3,"runeId":5289},{"count":6,"runeId":5296},{"count":5,"runeId":5316},{"count":4,"runeId":5317},{"count":3,"runeId":5357}],"masteries":[{"rank":5,"masteryId":6114},{"rank":1,"masteryId":6122},{"rank":5,"masteryId":6134},{"rank":1,"masteryId":6143},{"rank":5,"masteryId":6312},{"rank":1,"masteryId":6322},{"rank":5,"masteryId":6331},{"rank":1,"masteryId":6342},{"rank":5,"masteryId":6351},{"rank":1,"masteryId":6362}]},{"teamId":100,"spell1Id":4,"spell2Id":14,"championId":432,"profileIconId":2073,"summonerName":"Holakamionom","bot":false,"summonerId":78977495,"runes":[{"count":6,"runeId":5245},{"count":2,"runeId":5247},{"count":1,"runeId":5251},{"count":9,"runeId":5289},{"count":9,"runeId":5317},{"count":2,"runeId":5335},{"count":1,"runeId":5337}],"masteries":[{"rank":5,"masteryId":6211},{"rank":1,"masteryId":6222},{"rank":5,"masteryId":6231},{"rank":1,"masteryId":6241},{"rank":5,"masteryId":6311},{"rank":1,"masteryId":6322},{"rank":5,"masteryId":6332},{"rank":1,"masteryId":6343},{"rank":5,"masteryId":6351},{"rank":1,"masteryId":6362}]},{"teamId":100,"spell1Id":32,"spell2Id":4,"championId":57,"profileIconId":1391,"summonerName":"xWr4th","bot":false,"summonerId":88007182,"runes":[{"count":9,"runeId":5273},{"count":9,"runeId":5290},{"count":9,"runeId":5317},{"count":3,"runeId":5357}],"masteries":[{"rank":5,"masteryId":6114},{"rank":1,"masteryId":6122},{"rank":5,"masteryId":6134},{"rank":1,"masteryId":6142},{"rank":5,"masteryId":6154},{"rank":1,"masteryId":6164},{"rank":5,"masteryId":6312},{"rank":1,"masteryId":6322},{"rank":5,"masteryId":6331},{"rank":1,"masteryId":6343}]},{"teamId":200,"spell1Id":4,"spell2Id":7,"championId":203,"profileIconId":1637,"summonerName":"Heika","bot":false,"summonerId":29385999,"runes":[{"count":9,"runeId":5245},{"count":5,"runeId":5289},{"count":4,"runeId":5290},{"count":9,"runeId":5317},{"count":3,"runeId":5337}],"masteries":[{"rank":5,"masteryId":6114},{"rank":1,"masteryId":6121},{"rank":1,"masteryId":6131},{"rank":4,"masteryId":6134},{"rank":1,"masteryId":6143},{"rank":5,"masteryId":6151},{"rank":1,"masteryId":6161},{"rank":5,"masteryId":6212},{"rank":1,"masteryId":6223},{"rank":5,"masteryId":6231},{"rank":1,"masteryId":6241}]},{"teamId":200,"spell1Id":32,"spell2Id":4,"championId":84,"profileIconId":984,"summonerName":"Swamp00","bot":false,"summonerId":56610525,"runes":[{"count":3,"runeId":5257},{"count":6,"runeId":5273},{"count":9,"runeId":5289},{"count":9,"runeId":5317},{"count":3,"runeId":5357}],"masteries":[{"rank":5,"masteryId":6114},{"rank":1,"masteryId":6121},{"rank":5,"masteryId":6134},{"rank":1,"masteryId":6141},{"rank":5,"masteryId":6312},{"rank":1,"masteryId":6323},{"rank":3,"masteryId":6331},{"rank":2,"masteryId":6332},{"rank":1,"masteryId":6343},{"rank":5,"masteryId":6351},{"rank":1,"masteryId":6362}]},{"teamId":200,"spell1Id":4,"spell2Id":3,"championId":267,"profileIconId":1427,"summonerName":"Pîzza","bot":false,"summonerId":60314423,"runes":[{"count":6,"runeId":5245},{"count":3,"runeId":5273},{"count":9,"runeId":5297},{"count":7,"runeId":5315},{"count":2,"runeId":5317},{"count":2,"runeId":5347},{"count":1,"runeId":5357}],"masteries":[{"rank":5,"masteryId":6212},{"rank":1,"masteryId":6221},{"rank":5,"masteryId":6231},{"rank":1,"masteryId":6241},{"rank":5,"masteryId":6311},{"rank":1,"masteryId":6322},{"rank":5,"masteryId":6332},{"rank":1,"masteryId":6342},{"rank":5,"masteryId":6352},{"rank":1,"masteryId":6363}]},{"teamId":200,"spell1Id":4,"spell2Id":32,"championId":420,"profileIconId":711,"summonerName":"ECraft","bot":false,"summonerId":19542828,"runes":[{"count":9,"runeId":5245},{"count":9,"runeId":5289},{"count":9,"runeId":5317},{"count":3,"runeId":5335}],"masteries":[{"rank":5,"masteryId":6111},{"rank":1,"masteryId":6121},{"rank":5,"masteryId":6131},{"rank":1,"masteryId":6142},{"rank":5,"masteryId":6311},{"rank":1,"masteryId":6322},{"rank":5,"masteryId":6331},{"rank":1,"masteryId":6341},{"rank":5,"masteryId":6352},{"rank":1,"masteryId":6362}]},{"teamId":200,"spell1Id":4,"spell2Id":32,"championId":11,"profileIconId":1132,"summonerName":"LÖBÖLÖNK","bot":false,"summonerId":88689058,"runes":[{"count":9,"runeId":5245},{"count":3,"runeId":5289},{"count":6,"runeId":5296},{"count":9,"runeId":5317},{"count":3,"runeId":5335}],"masteries":[{"rank":5,"masteryId":6111},{"rank":1,"masteryId":6121},{"rank":5,"masteryId":6131},{"rank":1,"masteryId":6141},{"rank":5,"masteryId":6151},{"rank":1,"masteryId":6162},{"rank":5,"masteryId":6311},{"rank":1,"masteryId":6322},{"rank":5,"masteryId":6331},{"rank":1,"masteryId":6343}]}],"observers":{"encryptionKey":"U+rZl9qb1YBLuZS63/cDazyOQIhp4pSb"},"platformId":"EUW1","bannedChampions":[],"gameStartTime":1495009864765,"gameLength":531}
--------------------------------------------------------------------------------
/spec/fixtures/v3/get-runes.json:
--------------------------------------------------------------------------------
1 | {"summonerId":100012,"pages":[{"id":599388,"name":"ad","current":false,"slots":[{"runeSlotId":1,"runeId":5253},{"runeSlotId":2,"runeId":5253},{"runeSlotId":3,"runeId":5253},{"runeSlotId":4,"runeId":5253},{"runeSlotId":5,"runeId":5253},{"runeSlotId":6,"runeId":5253},{"runeSlotId":7,"runeId":5253},{"runeSlotId":8,"runeId":5253},{"runeSlotId":9,"runeId":5253},{"runeSlotId":10,"runeId":5195},{"runeSlotId":11,"runeId":5331},{"runeSlotId":12,"runeId":5331},{"runeSlotId":13,"runeId":5331},{"runeSlotId":14,"runeId":5331},{"runeSlotId":15,"runeId":5331},{"runeSlotId":16,"runeId":5331},{"runeSlotId":17,"runeId":5331},{"runeSlotId":18,"runeId":5331},{"runeSlotId":19,"runeId":5289},{"runeSlotId":20,"runeId":5289},{"runeSlotId":21,"runeId":5289},{"runeSlotId":22,"runeId":5289},{"runeSlotId":23,"runeId":5289},{"runeSlotId":24,"runeId":5289},{"runeSlotId":25,"runeId":5289},{"runeSlotId":26,"runeId":5289},{"runeSlotId":27,"runeId":5289},{"runeSlotId":28,"runeId":5335},{"runeSlotId":29,"runeId":5335},{"runeSlotId":30,"runeId":5335}]},{"id":599389,"name":"ad steel life","current":false,"slots":[{"runeSlotId":1,"runeId":5253},{"runeSlotId":2,"runeId":5253},{"runeSlotId":3,"runeId":5253},{"runeSlotId":4,"runeId":5253},{"runeSlotId":5,"runeId":5253},{"runeSlotId":6,"runeId":5253},{"runeSlotId":7,"runeId":5253},{"runeSlotId":8,"runeId":5253},{"runeSlotId":9,"runeId":5253},{"runeSlotId":10,"runeId":5317},{"runeSlotId":11,"runeId":5317},{"runeSlotId":12,"runeId":5317},{"runeSlotId":13,"runeId":5317},{"runeSlotId":14,"runeId":5317},{"runeSlotId":15,"runeId":5317},{"runeSlotId":16,"runeId":5317},{"runeSlotId":17,"runeId":5317},{"runeSlotId":18,"runeId":5317},{"runeSlotId":19,"runeId":5289},{"runeSlotId":20,"runeId":5289},{"runeSlotId":21,"runeId":5289},{"runeSlotId":22,"runeId":5289},{"runeSlotId":23,"runeId":5289},{"runeSlotId":24,"runeId":5289},{"runeSlotId":25,"runeId":5289},{"runeSlotId":26,"runeId":5289},{"runeSlotId":27,"runeId":5289},{"runeSlotId":28,"runeId":5343},{"runeSlotId":29,"runeId":5343},{"runeSlotId":30,"runeId":5343}]},{"id":3976397,"name":"AP regen mana","current":true,"slots":[{"runeSlotId":1,"runeId":5273},{"runeSlotId":2,"runeId":5273},{"runeSlotId":3,"runeId":5273},{"runeSlotId":4,"runeId":5273},{"runeSlotId":5,"runeId":5273},{"runeSlotId":6,"runeId":5273},{"runeSlotId":7,"runeId":5273},{"runeSlotId":8,"runeId":5273},{"runeSlotId":9,"runeId":5273},{"runeSlotId":10,"runeId":5327},{"runeSlotId":11,"runeId":5327},{"runeSlotId":12,"runeId":5327},{"runeSlotId":13,"runeId":5327},{"runeSlotId":14,"runeId":5327},{"runeSlotId":15,"runeId":5327},{"runeSlotId":16,"runeId":5327},{"runeSlotId":17,"runeId":5327},{"runeSlotId":18,"runeId":5327},{"runeSlotId":19,"runeId":5289},{"runeSlotId":20,"runeId":5289},{"runeSlotId":21,"runeId":5289},{"runeSlotId":22,"runeId":5289},{"runeSlotId":23,"runeId":5289},{"runeSlotId":24,"runeId":5289},{"runeSlotId":25,"runeId":5289},{"runeSlotId":26,"runeId":5289},{"runeSlotId":27,"runeId":5289},{"runeSlotId":28,"runeId":5357},{"runeSlotId":29,"runeId":5357},{"runeSlotId":30,"runeId":5357}]},{"id":23126766,"name":"AP armor","current":false,"slots":[{"runeSlotId":1,"runeId":5273},{"runeSlotId":2,"runeId":5273},{"runeSlotId":3,"runeId":5273},{"runeSlotId":4,"runeId":5273},{"runeSlotId":5,"runeId":5273},{"runeSlotId":6,"runeId":5273},{"runeSlotId":7,"runeId":5273},{"runeSlotId":8,"runeId":5273},{"runeSlotId":9,"runeId":5273},{"runeSlotId":10,"runeId":5317},{"runeSlotId":11,"runeId":5317},{"runeSlotId":12,"runeId":5317},{"runeSlotId":13,"runeId":5317},{"runeSlotId":14,"runeId":5317},{"runeSlotId":15,"runeId":5317},{"runeSlotId":16,"runeId":5317},{"runeSlotId":17,"runeId":5317},{"runeSlotId":18,"runeId":5317},{"runeSlotId":19,"runeId":5289},{"runeSlotId":20,"runeId":5289},{"runeSlotId":21,"runeId":5289},{"runeSlotId":22,"runeId":5289},{"runeSlotId":23,"runeId":5289},{"runeSlotId":24,"runeId":5289},{"runeSlotId":25,"runeId":5289},{"runeSlotId":26,"runeId":5289},{"runeSlotId":27,"runeId":5289},{"runeSlotId":28,"runeId":5357},{"runeSlotId":29,"runeId":5357},{"runeSlotId":30,"runeId":5357}]},{"id":84133736,"name":"rune je suis un costeau","current":false,"slots":[{"runeSlotId":1,"runeId":5257},{"runeSlotId":2,"runeId":5257},{"runeSlotId":3,"runeId":5257},{"runeSlotId":4,"runeId":5257},{"runeSlotId":5,"runeId":5257},{"runeSlotId":6,"runeId":5257},{"runeSlotId":7,"runeId":5257},{"runeSlotId":8,"runeId":5257},{"runeSlotId":9,"runeId":5257},{"runeSlotId":10,"runeId":5317},{"runeSlotId":11,"runeId":5317},{"runeSlotId":12,"runeId":5317},{"runeSlotId":13,"runeId":5317},{"runeSlotId":14,"runeId":5317},{"runeSlotId":15,"runeId":5317},{"runeSlotId":16,"runeId":5317},{"runeSlotId":17,"runeId":5317},{"runeSlotId":18,"runeId":5317},{"runeSlotId":19,"runeId":5289},{"runeSlotId":20,"runeId":5289},{"runeSlotId":21,"runeId":5289},{"runeSlotId":22,"runeId":5289},{"runeSlotId":23,"runeId":5289},{"runeSlotId":24,"runeId":5289},{"runeSlotId":25,"runeId":5289},{"runeSlotId":26,"runeId":5289},{"runeSlotId":27,"runeId":5289},{"runeSlotId":28,"runeId":5347},{"runeSlotId":29,"runeId":5347},{"runeSlotId":30,"runeId":5347}]},{"id":101534484,"name":"ad critik de chez mendes","current":false,"slots":[{"runeSlotId":1,"runeId":5249},{"runeSlotId":2,"runeId":5249},{"runeSlotId":3,"runeId":5249},{"runeSlotId":4,"runeId":5249},{"runeSlotId":5,"runeId":5249},{"runeSlotId":6,"runeId":5249},{"runeSlotId":7,"runeId":5245},{"runeSlotId":8,"runeId":5245},{"runeSlotId":9,"runeId":5245},{"runeSlotId":10,"runeId":5311},{"runeSlotId":11,"runeId":5311},{"runeSlotId":12,"runeId":5311},{"runeSlotId":13,"runeId":5311},{"runeSlotId":14,"runeId":5311},{"runeSlotId":15,"runeId":5311},{"runeSlotId":16,"runeId":5311},{"runeSlotId":17,"runeId":5311},{"runeSlotId":18,"runeId":5311},{"runeSlotId":19,"runeId":5289},{"runeSlotId":20,"runeId":5289},{"runeSlotId":21,"runeId":5289},{"runeSlotId":22,"runeId":5289},{"runeSlotId":23,"runeId":5289},{"runeSlotId":24,"runeId":5289},{"runeSlotId":25,"runeId":5289},{"runeSlotId":26,"runeId":5289},{"runeSlotId":27,"runeId":5289},{"runeSlotId":28,"runeId":5339},{"runeSlotId":29,"runeId":5335},{"runeSlotId":30,"runeId":5335}]}]}
--------------------------------------------------------------------------------
/lib/lol/request.rb:
--------------------------------------------------------------------------------
1 | require "uri"
2 | require "active_support/core_ext/object/to_query"
3 |
4 | module Lol
5 | class NotFound < StandardError; end
6 | class TooManyRequests < StandardError; end
7 | class InvalidCacheStore < StandardError; end
8 |
9 | # Encapsulates common methods for all requests
10 | # Request classes inherit from this
11 | class Request
12 | include HTTParty
13 |
14 | # @!attribute [r] api_key
15 | # @return [String] api_key
16 | attr_reader :api_key
17 |
18 |
19 | # @!attribute [rw] region
20 | # @return [String] region
21 | attr_accessor :region
22 |
23 | # @!attribute[r] cache_store
24 | # @return [Object] the cache_store
25 | attr_reader :cache_store
26 |
27 | # @!attribute[r] rate_limiter
28 | # @return [Object] the rate limiter, if one exists (else nil)
29 | attr_reader :rate_limiter
30 |
31 | # Returns the supported API Version.
32 | # @return [String] v3
33 | def self.api_version
34 | "v3"
35 | end
36 |
37 | def self.platforms
38 | {
39 | :br => 'br1',
40 | :eune => 'eun1',
41 | :euw => 'euw1',
42 | :jp => 'jp1',
43 | :kr => 'kr',
44 | :lan => 'la1',
45 | :las => 'la2',
46 | :na => 'na1',
47 | :oce => 'oc1',
48 | :ru => 'ru',
49 | :tr => 'tr1',
50 | }
51 | end
52 |
53 | # Initializes a new Request
54 | # @param api_key [String] the Riot Games API key
55 | # @param region [String] the region you want to use in API calls
56 | # @param cache_store [Hash]
57 | # @option cache_store [Redis] :redis Redis instance to use
58 | # @option cache_store [Boolean] :cached should the request be cached
59 | # @option cacche_store [Integer] :ttl ttl for cache keys
60 | # @return [Request]
61 | def initialize api_key, region, cache_store = {}, rate_limiter = nil
62 | @cache_store = cache_store
63 | @rate_limiter = rate_limiter
64 | raise InvalidCacheStore if cached? && !store.is_a?(Redis)
65 | @api_key = api_key
66 | @region = region
67 | end
68 |
69 | def platform
70 | self.class.platforms[region.downcase.to_sym]
71 | end
72 |
73 | # Returns the supported API Version.
74 | # @return [String] v3
75 | def api_version
76 | self.class.api_version
77 | end
78 |
79 | # Returns a full url for an API call
80 | # @param path [String] API path to call
81 | # @return [String] full fledged url
82 | def api_url path, params = {}
83 | url = File.join File.join(api_base_url, api_base_path), path
84 | "#{url}?#{api_query_string params}"
85 | end
86 |
87 | # Returns the API base domain
88 | # @return [String] path domain
89 | def api_base_url
90 | "https://#{platform}.api.riotgames.com"
91 | end
92 |
93 | # Returns the API base path, which is everything between the domain and the request-specific path
94 | # @return [String] API path
95 | def api_base_path
96 | "/lol/platform/#{api_version}"
97 | end
98 |
99 | def api_query_string params = {}
100 | URI.encode_www_form params.merge api_key: api_key
101 | end
102 |
103 | # Returns just a path from a full api url
104 | # @return [String]
105 | def clean_url(url)
106 | uri = URI.parse(url)
107 | uri.query = CGI.parse(uri.query || '').reject { |k| k == 'api_key' }.to_query
108 | uri.to_s
109 | end
110 |
111 | # Calls the API via HTTParty and handles errors caching it if a cache is
112 | # enabled and rate limiting it if a rate limiter is configured
113 | # @param url [String] the url to call
114 | # @param verb [Symbol] HTTP verb to use. Defaults to :get
115 | # @param body [Hash] Body for POST request
116 | # @param options [Hash] Options passed to HTTParty
117 | # @return [String] raw response of the call
118 | def perform_request url, verb = :get, body = nil, options = {}
119 | options_id = options.inspect
120 | can_cache = [:post, :put].include?(verb) ? false : cached?
121 | if can_cache && result = store.get("#{clean_url(url)}#{options_id}")
122 | return JSON.parse(result)
123 | end
124 | response = perform_rate_limited_request(url, verb, body, options)
125 | store.setex "#{clean_url(url)}#{options_id}", ttl, response.to_json if can_cache
126 | response
127 | end
128 |
129 | def perform_rate_limited_request url, verb = :get, body = nil, options = {}
130 | return perform_uncached_request(url, verb, body, options) unless rate_limiter
131 | @rate_limiter.times 1 do
132 | return perform_uncached_request(url, verb, body, options)
133 | end
134 | end
135 |
136 | def perform_uncached_request url, verb = :get, body = nil, options = {}
137 | options[:headers] ||= {}
138 | options[:headers].merge!({
139 | "Content-Type" => "application/json",
140 | "Accept" => "application/json"
141 | })
142 | if [:post, :put].include?(verb)
143 | options[:body] = body.to_json if body
144 | options[:headers]['X-Riot-Token'] = api_key
145 | end
146 | response = self.class.send(verb, url, options)
147 | if response.respond_to?(:code) && !(200...300).include?(response.code)
148 | raise NotFound.new("404 Not Found") if response.not_found?
149 | raise TooManyRequests.new('429 Rate limit exceeded') if response.code == 429
150 | raise InvalidAPIResponse.new(url, response)
151 | end
152 |
153 | if response.respond_to?(:parsed_response)
154 | response.parsed_response
155 | else
156 | response
157 | end
158 | end
159 |
160 | # @return [Redis] returns the cache store
161 | def store
162 | cache_store[:redis]
163 | end
164 |
165 | # @return [Boolean] true if the request should be cached
166 | def cached?
167 | cache_store[:cached]
168 | end
169 |
170 | # @return [Integer] the ttl to apply to cached keys
171 | def ttl
172 | cache_store[:ttl]
173 | end
174 | end
175 | end
176 |
--------------------------------------------------------------------------------
/spec/lol/client_spec.rb:
--------------------------------------------------------------------------------
1 | require "spec_helper"
2 | require "lol"
3 |
4 | include Lol
5 |
6 | describe Client do
7 | subject { Client.new "foo" }
8 |
9 | describe "#new" do
10 | it "requires an API key argument" do
11 | expect { Client.new }.to raise_error(ArgumentError)
12 | end
13 |
14 | it "accepts a region argument" do
15 | expect { Client.new("foo", :region => "na") }.not_to raise_error
16 | end
17 |
18 | it "defaults on EUW as a region" do
19 | expect(subject.region).to eq("euw")
20 | end
21 |
22 | context "caching" do
23 | let(:client) { Client.new "foo", redis: "redis://dummy-url" }
24 | let(:real_redis) { Client.new "foo", redis: "redis://localhost:6379" }
25 |
26 | it "sets caching if redis is specified in the options" do
27 | expect(client.cached?).to be_truthy
28 | end
29 |
30 | it "sets a default ttl of 15 * 60s" do
31 | expect(client.ttl).to eq(15*60)
32 | end
33 |
34 | it "accepts a custom ttl" do
35 | client = Client.new "foo", redis: "redis://dummy-url", ttl: 10
36 | expect(client.ttl).to eq(10)
37 | end
38 |
39 | it "instantiates a redis client if redis is in the options" do
40 | expect(real_redis.instance_variable_get(:@redis)).to be_a(Redis)
41 | end
42 |
43 | it "passes the redis_store to the request" do
44 | champion_request = real_redis.champion
45 | expect(champion_request.cache_store).to eq(real_redis.cache_store)
46 | end
47 | end
48 |
49 | context "rate_limiting" do
50 | let(:client) { Client.new "foo", rate_limit_requests: 10 }
51 |
52 | it "sets rate limiting if specified in the options" do
53 | expect(client.rate_limited?).to be_truthy
54 | end
55 |
56 | it "instantiates a rate limiter if it is in the options" do
57 | expect(client.rate_limiter).not_to be_nil
58 | end
59 |
60 | it "passes the rate limiter to the request" do
61 | champion_request = client.champion
62 | expect(champion_request.rate_limiter).to eq(client.rate_limiter)
63 | end
64 | end
65 | end
66 |
67 | describe "#cached?" do
68 | it "is true if @cached is true" do
69 | subject.instance_variable_set(:@cached, true)
70 | expect(subject.cached?).to be_truthy
71 | end
72 |
73 | it "is false if @cached is false" do
74 | subject.instance_variable_set(:@cached, false)
75 | expect(subject.cached?).to be_falsy
76 | end
77 | end
78 |
79 | describe "#champion" do
80 | it "returns an instance of ChampionRequest" do
81 | expect(subject.champion).to be_a(ChampionRequest)
82 | end
83 |
84 | it "initializes the ChampionRequest with the current API key and region" do
85 | expect(ChampionRequest).to receive(:new).with(subject.api_key, subject.region, subject.cache_store, subject.rate_limiter)
86 |
87 | subject.champion
88 | end
89 | end
90 |
91 | describe '#match' do
92 | it "returns an instance of MatchRequest" do
93 | expect(subject.match).to be_a(MatchRequest)
94 | end
95 |
96 | it "initializes the MatchRequest with the current API key and region" do
97 | expect(MatchRequest).to receive(:new).with(subject.api_key, subject.region, subject.cache_store, subject.rate_limiter)
98 |
99 | subject.match
100 | end
101 | end
102 |
103 | describe '#runes' do
104 | it "returns an instance of RunesRequest" do
105 | expect(subject.runes).to be_a(RunesRequest)
106 | end
107 |
108 | it "initializes the RunesRequest with the current API key and region" do
109 | expect(RunesRequest).to receive(:new).with(subject.api_key, subject.region, subject.cache_store, subject.rate_limiter)
110 |
111 | subject.runes
112 | end
113 | end
114 |
115 | describe '#masteries' do
116 | it "returns an instance of MasteriesRequest" do
117 | expect(subject.masteries).to be_a(MasteriesRequest)
118 | end
119 |
120 | it "initializes the MasteriesRequest with the current API key and region" do
121 | expect(MasteriesRequest).to receive(:new).with(subject.api_key, subject.region, subject.cache_store, subject.rate_limiter)
122 |
123 | subject.masteries
124 | end
125 | end
126 |
127 | describe "#league" do
128 | it "returns an instance of LeagueRequest" do
129 | expect(subject.league).to be_a(LeagueRequest)
130 | end
131 |
132 | it "initializes the LeagueRequest with the current API key and region" do
133 | expect(LeagueRequest).to receive(:new).with(subject.api_key, subject.region, subject.cache_store, subject.rate_limiter)
134 |
135 | subject.league
136 | end
137 | end
138 |
139 | describe "#summoner" do
140 | it "returns an instance of SummonerRequest" do
141 | expect(subject.summoner).to be_a(SummonerRequest)
142 | end
143 |
144 | it "initializes the SummonerRequest with the current API key and region" do
145 | expect(SummonerRequest).to receive(:new).with(subject.api_key, subject.region, subject.cache_store, subject.rate_limiter)
146 |
147 | subject.summoner
148 | end
149 | end
150 |
151 | describe "#static" do
152 | it "returns an instance of StaticRequest" do
153 | expect(subject.static).to be_a(StaticRequest)
154 | end
155 |
156 | it "initializes the StaticRequest with the current API key and region" do
157 | expect(StaticRequest).to receive(:new).with(subject.api_key, subject.region, subject.cache_store, subject.rate_limiter)
158 |
159 | subject.static
160 | end
161 | end
162 |
163 | describe '#lol_status' do
164 | it 'return an instance of LolStatusRequest' do
165 | expect(subject.lol_status).to be_a(LolStatusRequest)
166 | end
167 | end
168 |
169 | describe "#api_key" do
170 | it "returns an api key" do
171 | expect(subject.api_key).to eq("foo")
172 | end
173 |
174 | it "is read_only" do
175 | expect { subject.api_key = "bar" }.to raise_error(NoMethodError)
176 | end
177 | end
178 |
179 | describe "#region" do
180 | it "returns current region" do
181 | expect(subject.region).to eq("euw")
182 | end
183 |
184 | it "can be set to a new region" do
185 | subject.region = "NA"
186 | expect(subject.region).to eq("NA")
187 | end
188 | end
189 |
190 | end
191 |
--------------------------------------------------------------------------------
/spec/fixtures/v3/get-featured-games.json:
--------------------------------------------------------------------------------
1 | {"gameList":[{"gameId":3186584028,"mapId":12,"gameMode":"ARAM","gameType":"MATCHED_GAME","gameQueueConfigId":65,"participants":[{"teamId":100,"spell1Id":1,"spell2Id":4,"championId":119,"profileIconId":1212,"summonerName":"FaisaI","bot":false},{"teamId":100,"spell1Id":32,"spell2Id":4,"championId":12,"profileIconId":907,"summonerName":"AndersStefan","bot":false},{"teamId":100,"spell1Id":6,"spell2Id":4,"championId":112,"profileIconId":903,"summonerName":"ChaoticHero","bot":false},{"teamId":100,"spell1Id":4,"spell2Id":14,"championId":432,"profileIconId":2073,"summonerName":"Holakamionom","bot":false},{"teamId":100,"spell1Id":32,"spell2Id":4,"championId":57,"profileIconId":1391,"summonerName":"xWr4th","bot":false},{"teamId":200,"spell1Id":4,"spell2Id":7,"championId":203,"profileIconId":1637,"summonerName":"Heika","bot":false},{"teamId":200,"spell1Id":32,"spell2Id":4,"championId":84,"profileIconId":984,"summonerName":"Swamp00","bot":false},{"teamId":200,"spell1Id":4,"spell2Id":3,"championId":267,"profileIconId":1427,"summonerName":"Pîzza","bot":false},{"teamId":200,"spell1Id":4,"spell2Id":32,"championId":420,"profileIconId":711,"summonerName":"ECraft","bot":false},{"teamId":200,"spell1Id":4,"spell2Id":32,"championId":11,"profileIconId":1132,"summonerName":"LÖBÖLÖNK","bot":false}],"observers":{"encryptionKey":"U+rZl9qb1YBLuZS63/cDazyOQIhp4pSb"},"platformId":"EUW1","bannedChampions":[],"gameStartTime":1495009864765,"gameLength":269},{"gameId":3186554674,"mapId":11,"gameMode":"CLASSIC","gameType":"MATCHED_GAME","gameQueueConfigId":420,"participants":[{"teamId":100,"spell1Id":4,"spell2Id":11,"championId":64,"profileIconId":1629,"summonerName":"Gripex","bot":false},{"teamId":100,"spell1Id":14,"spell2Id":4,"championId":1,"profileIconId":588,"summonerName":"Back2Me","bot":false},{"teamId":100,"spell1Id":4,"spell2Id":12,"championId":59,"profileIconId":595,"summonerName":"bog","bot":false},{"teamId":100,"spell1Id":7,"spell2Id":4,"championId":236,"profileIconId":1597,"summonerName":"OSP","bot":false},{"teamId":100,"spell1Id":4,"spell2Id":3,"championId":25,"profileIconId":578,"summonerName":"nigdy szczeście","bot":false},{"teamId":200,"spell1Id":4,"spell2Id":12,"championId":76,"profileIconId":585,"summonerName":"br0keñ","bot":false},{"teamId":200,"spell1Id":4,"spell2Id":14,"championId":55,"profileIconId":1594,"summonerName":"Revilous","bot":false},{"teamId":200,"spell1Id":11,"spell2Id":4,"championId":121,"profileIconId":1601,"summonerName":"Áncient","bot":false},{"teamId":200,"spell1Id":4,"spell2Id":14,"championId":89,"profileIconId":508,"summonerName":"TEA Darkvenger","bot":false},{"teamId":200,"spell1Id":7,"spell2Id":4,"championId":51,"profileIconId":22,"summonerName":"Upiornyy","bot":false}],"observers":{"encryptionKey":"iDz9y9ULmEOIyDhyB6hK2VN4vWkgG4mG"},"platformId":"EUW1","bannedChampions":[{"championId":427,"teamId":100,"pickTurn":1},{"championId":117,"teamId":200,"pickTurn":2},{"championId":105,"teamId":100,"pickTurn":3},{"championId":104,"teamId":200,"pickTurn":4},{"championId":7,"teamId":100,"pickTurn":5},{"championId":38,"teamId":200,"pickTurn":6}],"gameStartTime":1495009791957,"gameLength":342},{"gameId":3186583988,"mapId":12,"gameMode":"ARAM","gameType":"MATCHED_GAME","gameQueueConfigId":65,"participants":[{"teamId":100,"spell1Id":4,"spell2Id":7,"championId":96,"profileIconId":1162,"summonerName":"Kawaboungara","bot":false},{"teamId":100,"spell1Id":4,"spell2Id":32,"championId":53,"profileIconId":1301,"summonerName":"Lnerbo","bot":false},{"teamId":100,"spell1Id":32,"spell2Id":4,"championId":86,"profileIconId":1436,"summonerName":"BuryMySoul","bot":false},{"teamId":100,"spell1Id":4,"spell2Id":21,"championId":268,"profileIconId":642,"summonerName":"Cank Wannister","bot":false},{"teamId":100,"spell1Id":4,"spell2Id":7,"championId":202,"profileIconId":898,"summonerName":"Halanah","bot":false},{"teamId":200,"spell1Id":4,"spell2Id":7,"championId":90,"profileIconId":522,"summonerName":"Milfbubi","bot":false},{"teamId":200,"spell1Id":32,"spell2Id":4,"championId":50,"profileIconId":508,"summonerName":"blyxXx","bot":false},{"teamId":200,"spell1Id":4,"spell2Id":3,"championId":84,"profileIconId":10,"summonerName":"Raíñbow Dash","bot":false},{"teamId":200,"spell1Id":4,"spell2Id":32,"championId":113,"profileIconId":1642,"summonerName":"Aninis","bot":false},{"teamId":200,"spell1Id":32,"spell2Id":4,"championId":35,"profileIconId":717,"summonerName":"3X1st3NC3","bot":false}],"observers":{"encryptionKey":"jx39BM9Lwsp5a6MStzCkVP2OBSjGg9zc"},"platformId":"EUW1","bannedChampions":[],"gameStartTime":1495009789297,"gameLength":345},{"gameId":3186343650,"mapId":11,"gameMode":"CLASSIC","gameType":"MATCHED_GAME","gameQueueConfigId":2,"participants":[{"teamId":100,"spell1Id":4,"spell2Id":12,"championId":92,"profileIconId":563,"summonerName":"basouK","bot":false},{"teamId":100,"spell1Id":14,"spell2Id":4,"championId":63,"profileIconId":1640,"summonerName":"Witoad v1","bot":false},{"teamId":100,"spell1Id":4,"spell2Id":7,"championId":81,"profileIconId":1157,"summonerName":"Leafor","bot":false},{"teamId":100,"spell1Id":3,"spell2Id":4,"championId":30,"profileIconId":1601,"summonerName":"IS1ffdb2a4c978a0","bot":false},{"teamId":100,"spell1Id":11,"spell2Id":4,"championId":28,"profileIconId":1601,"summonerName":"EVELYNN POGCHAMP","bot":false},{"teamId":200,"spell1Id":4,"spell2Id":7,"championId":51,"profileIconId":1601,"summonerName":"Shepherd Shock","bot":false},{"teamId":200,"spell1Id":4,"spell2Id":12,"championId":245,"profileIconId":1594,"summonerName":"Tusci","bot":false},{"teamId":200,"spell1Id":11,"spell2Id":4,"championId":20,"profileIconId":1154,"summonerName":"Ghandi handi","bot":false},{"teamId":200,"spell1Id":14,"spell2Id":4,"championId":57,"profileIconId":1634,"summonerName":"Decapitator Jake","bot":false},{"teamId":200,"spell1Id":4,"spell2Id":12,"championId":75,"profileIconId":713,"summonerName":"Adeanin96","bot":false}],"observers":{"encryptionKey":"sPzPqqa6m2KNvtuzhipz/NIVNVQjAbci"},"platformId":"EUW1","bannedChampions":[],"gameStartTime":1495010031086,"gameLength":103},{"gameId":3186603689,"mapId":12,"gameMode":"ARAM","gameType":"MATCHED_GAME","gameQueueConfigId":65,"participants":[{"teamId":100,"spell1Id":4,"spell2Id":21,"championId":115,"profileIconId":781,"summonerName":"Verievil","bot":false},{"teamId":100,"spell1Id":4,"spell2Id":3,"championId":43,"profileIconId":685,"summonerName":"tacotak21","bot":false},{"teamId":100,"spell1Id":21,"spell2Id":32,"championId":238,"profileIconId":983,"summonerName":"Kurau","bot":false},{"teamId":100,"spell1Id":7,"spell2Id":4,"championId":81,"profileIconId":1375,"summonerName":"Chitter","bot":false},{"teamId":100,"spell1Id":13,"spell2Id":4,"championId":117,"profileIconId":544,"summonerName":"Jòhn Wick","bot":false},{"teamId":200,"spell1Id":4,"spell2Id":32,"championId":92,"profileIconId":1155,"summonerName":"Turken22","bot":false},{"teamId":200,"spell1Id":4,"spell2Id":32,"championId":62,"profileIconId":907,"summonerName":"ReIief","bot":false},{"teamId":200,"spell1Id":3,"spell2Id":4,"championId":37,"profileIconId":22,"summonerName":"C me C you","bot":false},{"teamId":200,"spell1Id":7,"spell2Id":4,"championId":161,"profileIconId":747,"summonerName":"SR GunsRosesXIII","bot":false},{"teamId":200,"spell1Id":3,"spell2Id":4,"championId":67,"profileIconId":512,"summonerName":"FeestHoed","bot":false}],"observers":{"encryptionKey":"9GCwWjk59SOoS5igQ8F6baztrpfXtgRO"},"platformId":"EUW1","bannedChampions":[],"gameStartTime":1495009938752,"gameLength":195}],"clientRefreshInterval":300}
--------------------------------------------------------------------------------
/spec/lol/request_spec.rb:
--------------------------------------------------------------------------------
1 | require "spec_helper"
2 | require "lol"
3 |
4 | include Lol
5 |
6 | describe Request do
7 | context "initialization" do
8 | it "requires an api_key parameter" do
9 | expect { ChampionRequest.new }.to raise_error(ArgumentError)
10 | end
11 |
12 | it "accepts a region parameter" do
13 | expect { ChampionRequest.new "foo" }.to raise_error(ArgumentError)
14 | end
15 |
16 | it "correctly sets api key" do
17 | expect(ChampionRequest.new("api_key", "euw").api_key).to eq("api_key")
18 | end
19 |
20 | it "sets the cache store" do
21 | redis_store = Redis.new
22 | c = ChampionRequest.new("api_key", "euw", {redis: redis_store})
23 | expect(c.store).to eq(redis_store)
24 | end
25 |
26 | it "returns an error if the cache store is not supported" do
27 | expect { ChampionRequest.new "api_key", "euw", {cached: true, redis: "FOO"}}.to raise_error(InvalidCacheStore)
28 | end
29 | end
30 |
31 | subject { Request.new "api_key", "euw", {}, rate_limiter }
32 | let(:rate_limiter) { nil }
33 |
34 | describe "#perform_request" do
35 |
36 | let(:error401) { error_401 }
37 | let(:error429) { error_429 }
38 |
39 | it "calls HTTParty get" do
40 | expect(subject.class).to receive(:get).and_return(error401)
41 | expect { subject.perform_request subject.api_url("foo")}.to raise_error(InvalidAPIResponse)
42 | end
43 |
44 | it "sets content type in get requests" do
45 | expect(subject.class).to receive(:get) do |url, options|
46 | expect(options[:headers]["Content-Type"]).to eq "application/json"
47 | end
48 | subject.perform_request subject.api_url("foo")
49 | end
50 |
51 | it "sets the accept header in get requests" do
52 | expect(subject.class).to receive(:get) do |url, options|
53 | expect(options[:headers]["Accept"]).to eq "application/json"
54 | end
55 | subject.perform_request subject.api_url("foo")
56 | end
57 |
58 | it "sets content type in post requests" do
59 | expect(subject.class).to receive(:post) do |url, options|
60 | expect(options[:headers]["Content-Type"]).to eq "application/json"
61 | end
62 | subject.perform_request subject.api_url("foo"), :post
63 | end
64 |
65 | it "sets the accept header in post requests" do
66 | expect(subject.class).to receive(:post) do |url, options|
67 | expect(options[:headers]["Accept"]).to eq "application/json"
68 | end
69 | subject.perform_request subject.api_url("foo"), :post
70 | end
71 |
72 | it "sets the api key header in post requests" do
73 | expect(subject.class).to receive(:post) do |url, options|
74 | expect(options[:headers]["X-Riot-Token"]).to eq(subject.api_key)
75 | end
76 | subject.perform_request subject.api_url("foo"), :post
77 | end
78 |
79 | it "handles 401" do
80 | expect(subject.class).to receive(:get).and_return(error401)
81 | expect { subject.perform_request subject.api_url("foo") }.to raise_error(InvalidAPIResponse)
82 | end
83 |
84 | it "handles 404" do
85 | expect(error401).to receive(:respond_to?).at_least(:once).and_return(true)
86 | expect(error401).to receive(:not_found?).and_return(true)
87 | expect(subject.class).to receive(:get).and_return(error401)
88 | expect { subject.perform_request subject.api_url("foo")}.to raise_error(NotFound)
89 | end
90 |
91 | it 'handles 429' do
92 | expect(error429).to receive(:respond_to?).and_return(true)
93 | expect(subject.class).to receive(:get).and_return(error429)
94 | expect { subject.perform_request subject.api_url("foo")}.to raise_error(TooManyRequests)
95 | end
96 |
97 | context "post requests" do
98 | it "supports post" do
99 | expect(subject.class).to receive(:post).and_return(true)
100 | expect { subject.perform_request "http://foo.com", :post }.not_to raise_error
101 | end
102 | end
103 |
104 | context "caching" do
105 | before :all do
106 | class FakeRedis < Redis
107 | attr_reader :store
108 |
109 | def initialize options = {}
110 | @store = {}
111 | end
112 |
113 | def get key
114 | @store[key]
115 | end
116 |
117 | def setex key, ttl, val
118 | @store[key] = val
119 | @store["#{key}:ttl"] = ttl
120 | end
121 | end
122 | end
123 |
124 | let(:fake_redis) { FakeRedis.new }
125 | let(:request) { Request.new "api_key", "euw", {redis: fake_redis, ttl: 60, cached: true }}
126 | before :each do
127 | expect(request.class).to receive(:get).with("/foo?api_key=asd", instance_of(Hash)).and_return({foo: "bar"}).at_least(:once)
128 | first_result = request.perform_request "/foo?api_key=asd"
129 | end
130 |
131 | it "is cached" do
132 | expect(request.class).not_to receive(:get)
133 | request.perform_request "/foo?api_key=asd"
134 | end
135 |
136 | it "is not cached if post" do
137 | expect(request.class).to receive(:post).twice
138 | request.perform_request "/foo?api_key=asd", :post
139 | request.perform_request "/foo?api_key=asd", :post
140 | end
141 |
142 | it "serializes cached responses" do
143 | expect(JSON).to receive(:parse)
144 | request.perform_request "/foo?api_key=asd"
145 | end
146 |
147 | it "sets ttl" do
148 | expect(fake_redis.get("/foo?{}:ttl")).to eq(60)
149 | end
150 |
151 | it "uses clean urls" do
152 | expect(request).to receive(:clean_url).at_least(:once)
153 | request.perform_request "/foo?api_key=asd"
154 | end
155 | end
156 |
157 | context 'with rate limits' do
158 | let(:rate_limiter) { Lol::Client.new('api_key').set_up_rate_limiter(1, 20) }
159 | before { stub_request subject, 'champion-all', 'champions' }
160 |
161 | it 'uses rate limiter' do
162 | expect(subject.rate_limiter).to receive(:times).and_call_original
163 | subject.perform_request subject.api_url('champions')
164 | end
165 | end
166 | end
167 |
168 | describe "clean_url" do
169 | it "works when url does not have a query string" do
170 | expect { subject.clean_url 'http://www.leagueoflegends.com' }.not_to raise_error
171 | end
172 | end
173 |
174 | describe "api_url" do
175 | it "defaults on Request#region" do
176 | expect(subject.api_url("bar")).to match(/\/euw1\./)
177 | end
178 |
179 | it "defaults on Reques.api_version" do
180 | expect(subject.api_url("bar")).to match(/\/v3\//)
181 | end
182 |
183 | it "a path" do
184 | expect { subject.api_url }.to raise_error(ArgumentError)
185 | end
186 |
187 | it "returns a full fledged api url" do
188 | expect(subject.api_url("bar")).to eq("https://euw1.api.riotgames.com/lol/platform/v3/bar?api_key=api_key")
189 | end
190 |
191 | it "optionally accept query string parameters" do
192 | expect(subject.api_url("foo", a: 'b')).to eq("https://euw1.api.riotgames.com/lol/platform/v3/foo?a=b&api_key=api_key")
193 | end
194 |
195 | it "delegates the base url to #api_base_url" do
196 | allow(subject).to receive(:api_base_url).and_return 'foo'
197 | expect(subject.api_url 'bar').to match /^foo/
198 | end
199 |
200 | it "delegates the query string to #api_query_string" do
201 | allow(subject).to receive(:api_query_string).and_return 'foo'
202 | expect(subject.api_url 'bar').to match /\?foo$/
203 | end
204 | end
205 |
206 | describe "#api_base_url" do
207 | it "returns the base domain" do
208 | expect(subject.api_base_url).to eq "https://euw1.api.riotgames.com"
209 | end
210 | end
211 |
212 | describe "#api_query_string" do
213 | it "returns the api key as query string" do
214 | expect(subject.api_query_string).to eq "api_key=api_key"
215 | end
216 |
217 | it "optionally accept other params" do
218 | expect(subject.api_query_string foo: 'bar').to eq "foo=bar&api_key=api_key"
219 | end
220 | end
221 | end
222 |
--------------------------------------------------------------------------------
/spec/fixtures/v3/get-static-champions.json:
--------------------------------------------------------------------------------
1 | {"type":"champion","version":"7.9.1","data":{"89":{"id":89,"key":"Leona","name":"Leona","title":"the Radiant Dawn"},"110":{"id":110,"key":"Varus","name":"Varus","title":"the Arrow of Retribution"},"111":{"id":111,"key":"Nautilus","name":"Nautilus","title":"the Titan of the Depths"},"112":{"id":112,"key":"Viktor","name":"Viktor","title":"the Machine Herald"},"113":{"id":113,"key":"Sejuani","name":"Sejuani","title":"Fury of the North"},"114":{"id":114,"key":"Fiora","name":"Fiora","title":"the Grand Duelist"},"236":{"id":236,"key":"Lucian","name":"Lucian","title":"the Purifier"},"115":{"id":115,"key":"Ziggs","name":"Ziggs","title":"the Hexplosives Expert"},"117":{"id":117,"key":"Lulu","name":"Lulu","title":"the Fae Sorceress"},"90":{"id":90,"key":"Malzahar","name":"Malzahar","title":"the Prophet of the Void"},"238":{"id":238,"key":"Zed","name":"Zed","title":"the Master of Shadows"},"91":{"id":91,"key":"Talon","name":"Talon","title":"the Blade's Shadow"},"119":{"id":119,"key":"Draven","name":"Draven","title":"the Glorious Executioner"},"92":{"id":92,"key":"Riven","name":"Riven","title":"the Exile"},"96":{"id":96,"key":"KogMaw","name":"Kog'Maw","title":"the Mouth of the Abyss"},"10":{"id":10,"key":"Kayle","name":"Kayle","title":"The Judicator"},"98":{"id":98,"key":"Shen","name":"Shen","title":"the Eye of Twilight"},"99":{"id":99,"key":"Lux","name":"Lux","title":"the Lady of Luminosity"},"11":{"id":11,"key":"MasterYi","name":"Master Yi","title":"the Wuju Bladesman"},"12":{"id":12,"key":"Alistar","name":"Alistar","title":"the Minotaur"},"13":{"id":13,"key":"Ryze","name":"Ryze","title":"the Rune Mage"},"14":{"id":14,"key":"Sion","name":"Sion","title":"The Undead Juggernaut"},"15":{"id":15,"key":"Sivir","name":"Sivir","title":"the Battle Mistress"},"16":{"id":16,"key":"Soraka","name":"Soraka","title":"the Starchild"},"17":{"id":17,"key":"Teemo","name":"Teemo","title":"the Swift Scout"},"18":{"id":18,"key":"Tristana","name":"Tristana","title":"the Yordle Gunner"},"19":{"id":19,"key":"Warwick","name":"Warwick","title":"the Uncaged Wrath of Zaun"},"240":{"id":240,"key":"Kled","name":"Kled","title":"the Cantankerous Cavalier"},"120":{"id":120,"key":"Hecarim","name":"Hecarim","title":"the Shadow of War"},"121":{"id":121,"key":"Khazix","name":"Kha'Zix","title":"the Voidreaver"},"1":{"id":1,"key":"Annie","name":"Annie","title":"the Dark Child"},"122":{"id":122,"key":"Darius","name":"Darius","title":"the Hand of Noxus"},"2":{"id":2,"key":"Olaf","name":"Olaf","title":"the Berserker"},"245":{"id":245,"key":"Ekko","name":"Ekko","title":"the Boy Who Shattered Time"},"3":{"id":3,"key":"Galio","name":"Galio","title":"the Colossus"},"4":{"id":4,"key":"TwistedFate","name":"Twisted Fate","title":"the Card Master"},"126":{"id":126,"key":"Jayce","name":"Jayce","title":"the Defender of Tomorrow"},"5":{"id":5,"key":"XinZhao","name":"Xin Zhao","title":"the Seneschal of Demacia"},"127":{"id":127,"key":"Lissandra","name":"Lissandra","title":"the Ice Witch"},"6":{"id":6,"key":"Urgot","name":"Urgot","title":"the Headsman's Pride"},"7":{"id":7,"key":"Leblanc","name":"LeBlanc","title":"the Deceiver"},"8":{"id":8,"key":"Vladimir","name":"Vladimir","title":"the Crimson Reaper"},"9":{"id":9,"key":"Fiddlesticks","name":"Fiddlesticks","title":"the Harbinger of Doom"},"20":{"id":20,"key":"Nunu","name":"Nunu","title":"the Yeti Rider"},"21":{"id":21,"key":"MissFortune","name":"Miss Fortune","title":"the Bounty Hunter"},"22":{"id":22,"key":"Ashe","name":"Ashe","title":"the Frost Archer"},"23":{"id":23,"key":"Tryndamere","name":"Tryndamere","title":"the Barbarian King"},"24":{"id":24,"key":"Jax","name":"Jax","title":"Grandmaster at Arms"},"25":{"id":25,"key":"Morgana","name":"Morgana","title":"Fallen Angel"},"26":{"id":26,"key":"Zilean","name":"Zilean","title":"the Chronokeeper"},"27":{"id":27,"key":"Singed","name":"Singed","title":"the Mad Chemist"},"28":{"id":28,"key":"Evelynn","name":"Evelynn","title":"the Widowmaker"},"29":{"id":29,"key":"Twitch","name":"Twitch","title":"the Plague Rat"},"131":{"id":131,"key":"Diana","name":"Diana","title":"Scorn of the Moon"},"133":{"id":133,"key":"Quinn","name":"Quinn","title":"Demacia's Wings"},"254":{"id":254,"key":"Vi","name":"Vi","title":"the Piltover Enforcer"},"497":{"id":497,"key":"Rakan","name":"Rakan","title":"The Charmer"},"134":{"id":134,"key":"Syndra","name":"Syndra","title":"the Dark Sovereign"},"498":{"id":498,"key":"Xayah","name":"Xayah","title":"the Rebel"},"136":{"id":136,"key":"AurelionSol","name":"Aurelion Sol","title":"The Star Forger"},"412":{"id":412,"key":"Thresh","name":"Thresh","title":"the Chain Warden"},"30":{"id":30,"key":"Karthus","name":"Karthus","title":"the Deathsinger"},"31":{"id":31,"key":"Chogath","name":"Cho'Gath","title":"the Terror of the Void"},"32":{"id":32,"key":"Amumu","name":"Amumu","title":"the Sad Mummy"},"33":{"id":33,"key":"Rammus","name":"Rammus","title":"the Armordillo"},"34":{"id":34,"key":"Anivia","name":"Anivia","title":"the Cryophoenix"},"35":{"id":35,"key":"Shaco","name":"Shaco","title":"the Demon Jester"},"36":{"id":36,"key":"DrMundo","name":"Dr. Mundo","title":"the Madman of Zaun"},"37":{"id":37,"key":"Sona","name":"Sona","title":"Maven of the Strings"},"38":{"id":38,"key":"Kassadin","name":"Kassadin","title":"the Void Walker"},"39":{"id":39,"key":"Irelia","name":"Irelia","title":"the Will of the Blades"},"143":{"id":143,"key":"Zyra","name":"Zyra","title":"Rise of the Thorns"},"266":{"id":266,"key":"Aatrox","name":"Aatrox","title":"the Darkin Blade"},"420":{"id":420,"key":"Illaoi","name":"Illaoi","title":"the Kraken Priestess"},"267":{"id":267,"key":"Nami","name":"Nami","title":"the Tidecaller"},"421":{"id":421,"key":"RekSai","name":"Rek'Sai","title":"the Void Burrower"},"268":{"id":268,"key":"Azir","name":"Azir","title":"the Emperor of the Sands"},"427":{"id":427,"key":"Ivern","name":"Ivern","title":"the Green Father"},"429":{"id":429,"key":"Kalista","name":"Kalista","title":"the Spear of Vengeance"},"40":{"id":40,"key":"Janna","name":"Janna","title":"the Storm's Fury"},"41":{"id":41,"key":"Gangplank","name":"Gangplank","title":"the Saltwater Scourge"},"42":{"id":42,"key":"Corki","name":"Corki","title":"the Daring Bombardier"},"43":{"id":43,"key":"Karma","name":"Karma","title":"the Enlightened One"},"44":{"id":44,"key":"Taric","name":"Taric","title":"the Shield of Valoran"},"45":{"id":45,"key":"Veigar","name":"Veigar","title":"the Tiny Master of Evil"},"48":{"id":48,"key":"Trundle","name":"Trundle","title":"the Troll King"},"150":{"id":150,"key":"Gnar","name":"Gnar","title":"the Missing Link"},"154":{"id":154,"key":"Zac","name":"Zac","title":"the Secret Weapon"},"432":{"id":432,"key":"Bard","name":"Bard","title":"the Wandering Caretaker"},"157":{"id":157,"key":"Yasuo","name":"Yasuo","title":"the Unforgiven"},"50":{"id":50,"key":"Swain","name":"Swain","title":"the Master Tactician"},"51":{"id":51,"key":"Caitlyn","name":"Caitlyn","title":"the Sheriff of Piltover"},"53":{"id":53,"key":"Blitzcrank","name":"Blitzcrank","title":"the Great Steam Golem"},"54":{"id":54,"key":"Malphite","name":"Malphite","title":"Shard of the Monolith"},"55":{"id":55,"key":"Katarina","name":"Katarina","title":"the Sinister Blade"},"56":{"id":56,"key":"Nocturne","name":"Nocturne","title":"the Eternal Nightmare"},"57":{"id":57,"key":"Maokai","name":"Maokai","title":"the Twisted Treant"},"58":{"id":58,"key":"Renekton","name":"Renekton","title":"the Butcher of the Sands"},"59":{"id":59,"key":"JarvanIV","name":"Jarvan IV","title":"the Exemplar of Demacia"},"161":{"id":161,"key":"Velkoz","name":"Vel'Koz","title":"the Eye of the Void"},"163":{"id":163,"key":"Taliyah","name":"Taliyah","title":"the Stoneweaver"},"164":{"id":164,"key":"Camille","name":"Camille","title":"the Steel Shadow"},"201":{"id":201,"key":"Braum","name":"Braum","title":"the Heart of the Freljord"},"202":{"id":202,"key":"Jhin","name":"Jhin","title":"the Virtuoso"},"203":{"id":203,"key":"Kindred","name":"Kindred","title":"The Eternal Hunters"},"60":{"id":60,"key":"Elise","name":"Elise","title":"the Spider Queen"},"61":{"id":61,"key":"Orianna","name":"Orianna","title":"the Lady of Clockwork"},"62":{"id":62,"key":"MonkeyKing","name":"Wukong","title":"the Monkey King"},"63":{"id":63,"key":"Brand","name":"Brand","title":"the Burning Vengeance"},"64":{"id":64,"key":"LeeSin","name":"Lee Sin","title":"the Blind Monk"},"67":{"id":67,"key":"Vayne","name":"Vayne","title":"the Night Hunter"},"68":{"id":68,"key":"Rumble","name":"Rumble","title":"the Mechanized Menace"},"69":{"id":69,"key":"Cassiopeia","name":"Cassiopeia","title":"the Serpent's Embrace"},"72":{"id":72,"key":"Skarner","name":"Skarner","title":"the Crystal Vanguard"},"74":{"id":74,"key":"Heimerdinger","name":"Heimerdinger","title":"the Revered Inventor"},"75":{"id":75,"key":"Nasus","name":"Nasus","title":"the Curator of the Sands"},"76":{"id":76,"key":"Nidalee","name":"Nidalee","title":"the Bestial Huntress"},"77":{"id":77,"key":"Udyr","name":"Udyr","title":"the Spirit Walker"},"78":{"id":78,"key":"Poppy","name":"Poppy","title":"Keeper of the Hammer"},"79":{"id":79,"key":"Gragas","name":"Gragas","title":"the Rabble Rouser"},"222":{"id":222,"key":"Jinx","name":"Jinx","title":"the Loose Cannon"},"101":{"id":101,"key":"Xerath","name":"Xerath","title":"the Magus Ascendant"},"102":{"id":102,"key":"Shyvana","name":"Shyvana","title":"the Half-Dragon"},"223":{"id":223,"key":"TahmKench","name":"Tahm Kench","title":"the River King"},"103":{"id":103,"key":"Ahri","name":"Ahri","title":"the Nine-Tailed Fox"},"104":{"id":104,"key":"Graves","name":"Graves","title":"the Outlaw"},"105":{"id":105,"key":"Fizz","name":"Fizz","title":"the Tidal Trickster"},"106":{"id":106,"key":"Volibear","name":"Volibear","title":"the Thunder's Roar"},"80":{"id":80,"key":"Pantheon","name":"Pantheon","title":"the Artisan of War"},"107":{"id":107,"key":"Rengar","name":"Rengar","title":"the Pridestalker"},"81":{"id":81,"key":"Ezreal","name":"Ezreal","title":"the Prodigal Explorer"},"82":{"id":82,"key":"Mordekaiser","name":"Mordekaiser","title":"the Iron Revenant"},"83":{"id":83,"key":"Yorick","name":"Yorick","title":"Shepherd of Souls"},"84":{"id":84,"key":"Akali","name":"Akali","title":"the Fist of Shadow"},"85":{"id":85,"key":"Kennen","name":"Kennen","title":"the Heart of the Tempest"},"86":{"id":86,"key":"Garen","name":"Garen","title":"The Might of Demacia"}}}
--------------------------------------------------------------------------------
/spec/fixtures/v3/get-static-masteries.json:
--------------------------------------------------------------------------------
1 | {"type":"mastery","version":"7.9.1","data":{"6131":{"id":6131,"name":"Vampirism","description":["+0.4% Lifesteal and Spell Vamp","+0.8% Lifesteal and Spell Vamp","+1.2% Lifesteal and Spell Vamp","+1.6% Lifesteal and Spell Vamp","+2.0% Lifesteal and Spell Vamp"]},"6351":{"id":6351,"name":"Precision","description":["Gain 1.2 Lethality and 0.3 + 0.05 per level Magic Penetration","Gain 2.4 Lethality and 0.6 + 0.10 per level Magic Penetration","Gain 3.6 Lethality and 0.9 + 0.15 per level Magic Penetration","Gain 4.8 Lethality and 1.2 + 0.20 per level Magic Penetration","Gain 6 Lethality and 1.5 + 0.25 per level Magic Penetration"]},"6252":{"id":6252,"name":"Legendary Guardian","description":["+0.6 Armor and Magic Resist for each nearby enemy champion","+1.2 Armor and Magic Resist for each nearby enemy champion","+1.8 Armor and Magic Resist for each nearby enemy champion","+2.4 Armor and Magic Resist for each nearby enemy champion","+3 Armor and Magic Resist for each nearby enemy champion"]},"6251":{"id":6251,"name":"Swiftness","description":["+3% Tenacity and Slow Resist","+6% Tenacity and Slow Resist","+9% Tenacity and Slow Resist","+12% Tenacity and Slow Resist","+15% Tenacity and Slow Resist"]},"6111":{"id":6111,"name":"Fury","description":["+0.8% Attack Speed","+1.6% Attack Speed","+2.4% Attack Speed","+3.2% Attack Speed","+4% Attack Speed"]},"6331":{"id":6331,"name":"Merciless","description":["Deal 0.6% increased damage to champions below 40% Health","Deal 1.2% increased damage to champions below 40% Health","Deal 1.8% increased damage to champions below 40% Health","Deal 2.4% increased damage to champions below 40% Health","Deal 3% increased damage to champions below 40% Health"]},"6232":{"id":6232,"name":"Veteran's Scars","description":["+10 Health","+20 Health","+30 Health","+40 Health","+50 Health"]},"6154":{"id":6154,"name":"Piercing Thoughts","description":["+1.4% Magic Penetration","+2.8% Magic Penetration","+4.2% Magic Penetration","+5.6% Magic Penetration","+7% Magic Penetration"]},"6352":{"id":6352,"name":"Intelligence","description":["Your Cooldown Reduction cap is increased to 41% and you gain 1% Cooldown Reduction","Your Cooldown Reduction cap is increased to 42% and you gain 2% Cooldown Reduction","Your Cooldown Reduction cap is increased to 43% and you gain 3% Cooldown Reduction","Your Cooldown Reduction cap is increased to 44% and you gain 4% Cooldown Reduction","Your Cooldown Reduction cap is increased to 45% and you gain 5% Cooldown Reduction"]},"6231":{"id":6231,"name":"Runic Armor","description":["Shields, healing, regeneration, and lifesteal on you are 1.6% stronger","Shields, healing, regeneration, and lifesteal on you are 3.2% stronger","Shields, healing, regeneration, and lifesteal on you are 4.8% stronger","Shields, healing, regeneration, and lifesteal on you are 6.4% stronger","Shields, healing, regeneration, and lifesteal on you are 8% stronger"]},"6311":{"id":6311,"name":"Wanderer","description":["+0.6% Movement Speed out of combat","+1.2% Movement Speed out of combat","+1.8% Movement Speed out of combat","+2.4% Movement Speed out of combat","+3% Movement Speed out of combat"]},"6212":{"id":6212,"name":"Unyielding","description":["+1% Bonus Armor and Magic Resist","+2% Bonus Armor and Magic Resist","+3% Bonus Armor and Magic Resist","+4% Bonus Armor and Magic Resist","+5% Bonus Armor and Magic Resist"]},"6134":{"id":6134,"name":"Natural Talent","description":["Gain 0.4 + 0.09 per level Attack Damage, and 0.6 + 0.13 per level Ability Power (+2 Attack Damage and 3 Ability Power at level 18)","Gain 0.8 + 0.18 per level Attack Damage, and 1.2 + 0.27 per level Ability Power (+4 Attack Damage and 6 Ability Power at level 18)","Gain 1.2 + 0.27 per level Attack Damage, and 1.8 + 0.4 per level Ability Power (+6 Attack Damage and 9 Ability Power at level 18)","Gain 1.6 + 0.36 per level Attack Damage, and 2.4 + 0.53 per level Ability Power (+8 Attack Damage and 12 Ability Power at level 18)","Gain 2 + 0.44 per level Attack Damage, and 3 + 0.67 per level Ability Power (+10 Attack Damage and 15 Ability Power at level 18)"]},"6332":{"id":6332,"name":"Meditation","description":["Regenerate 0.25% of your missing Mana every 5 seconds","Regenerate 0.5% of your missing Mana every 5 seconds","Regenerate 0.75% of your missing Mana every 5 seconds","Regenerate 1.0% of your missing Mana every 5 seconds","Regenerate 1.25% of your missing Mana every 5 seconds"]},"6211":{"id":6211,"name":"Recovery","description":["+0.4 Health per 5 seconds","+0.8 Health per 5 seconds","+1.2 Health per 5 seconds","+1.6 Health per 5 seconds","+2.0 Health per 5 seconds"]},"6114":{"id":6114,"name":"Sorcery","description":["+0.4% increased Ability damage","+0.8% increased Ability damage","+1.2% increased Ability damage","+1.6% increased Ability damage","+2.0% increased Ability damage"]},"6312":{"id":6312,"name":"Savagery","description":["Single target attacks and spells deal 1 bonus damage to minions and monsters","Single target attacks and spells deal 2 bonus damage to minions and monsters","Single target attacks and spells deal 3 bonus damage to minions and monsters","Single target attacks and spells deal 4 bonus damage to minions and monsters","Single target attacks and spells deal 5 bonus damage to minions and monsters"]},"6151":{"id":6151,"name":"Battering Blows","description":["+1.4% Armor Penetration","+2.8% Armor Penetration","+4.2% Armor Penetration","+5.6% Armor Penetration","+7% Armor Penetration"]},"6142":{"id":6142,"name":"Double Edged Sword","description":["Deal 3% additional damage, take 1.5% additional damage."]},"6164":{"id":6164,"name":"Deathfire Touch","description":["Your damaging abilities cause enemy champions to take magic damage over 4 seconds.
Damage: 8 + 45% Bonus Attack Damage and 25% Ability Power
Deathfire Touch's duration is reduced for:
- Area of Effect: 2 second duration.
- Damage over Time: 1 second duration."]},"6362":{"id":6362,"name":"Thunderlord's Decree","description":["Your 3rd attack or damaging spell against the same enemy champion calls down a lightning strike, dealing magic damage in the area.
Damage: 10 per level, plus 30% of your Bonus Attack Damage, and 10% of your Ability Power (25-15 second cooldown, based on level)."]},"6241":{"id":6241,"name":"Insight","description":["Reduces the cooldown of Summoner Spells by 15%"]},"6263":{"id":6263,"name":"Stoneborn Pact","description":["Gain 5% total health.
Your movement impairing effects brand enemy champions with an earthen rune for 4 seconds. Other allied champions who attack branded enemies heal for 5 + 2.5% of your maximum health over 2 seconds (halved if you are ranged)."]},"6141":{"id":6141,"name":"Bounty Hunter","description":["Deal 1% increased damage for each unique enemy champion you have killed"]},"6361":{"id":6361,"name":"Stormraider's Surge","description":["Dealing 30% of a champion's max Health within 2.5 seconds grants you 40% Movement Speed and 75% Slow Resistance for 3 seconds (10 second cooldown)."]},"6262":{"id":6262,"name":"Courage of the Colossus","description":["Gain a shield for 3-54 (+5% of your maximum health) for each nearby enemy champion for 3 seconds after hitting an enemy champion with a stun, taunt, snare, or knock up (45-30 second cooldown, based on level)."]},"6122":{"id":6122,"name":"Feast","description":["Killing a unit restores 20 Health (30 second cooldown)"]},"6342":{"id":6342,"name":"Bandit","description":["Gain 1 gold for each nearby minion killed by an ally.
Gain 3 gold (10 if melee) when hitting an enemy champion with a basic attack (5 second cooldown)"]},"6221":{"id":6221,"name":"Explorer","description":["+15 Movement Speed in Brush and River"]},"6243":{"id":6243,"name":"Fearless","description":["Gain 10% +1.5 per level bonus Armor and Magic Resist when damaged by an enemy champion for 2 seconds (9s Cooldown)"]},"6121":{"id":6121,"name":"Fresh Blood","description":["Your first basic attack against a champion deals an additional 10 +1 per level damage (6 second cooldown)"]},"6143":{"id":6143,"name":"Battle Trance","description":["Gain up to 3% increased damage over 3 seconds when in combat with enemy Champions"]},"6341":{"id":6341,"name":"Greenfather's Gift","description":["Stepping into brush causes your next damaging attack or ability to deal 3% of your target's current health as bonus magic damage (9s Cooldown)"]},"6363":{"id":6363,"name":"Windspeaker's Blessing","description":["Your heals and shields are 10% stronger. Additionally, your shields and heals on other allies increase their armor by 5-22 (based on level) and their magic resistance by half that amount for 3 seconds."]},"6242":{"id":6242,"name":"Perseverance","description":["+50% Base Health Regen, increased to +200% when below 25% Health"]},"6322":{"id":6322,"name":"Secret Stash","description":["Your Potions and Elixirs last 10% longer.
Your Health Potions are replaced with Biscuits that restore 15 Health and Mana instantly on use"]},"6223":{"id":6223,"name":"Tough Skin","description":["You take 2 less damage from champion and neutral monster basic attacks"]},"6123":{"id":6123,"name":"Expose Weakness","description":["Damaging enemy champions causes them to take 3% more damage from your allies"]},"6321":{"id":6321,"name":"Runic Affinity","description":["Buffs from neutral monsters last 15% longer"]},"6343":{"id":6343,"name":"Dangerous Game","description":["Champion kills and assists restore 5% of your missing Health and Mana"]},"6222":{"id":6222,"name":"Siegemaster","description":["Gain 8 Armor and Magic Resistance when near an allied tower"]},"6323":{"id":6323,"name":"Assassin","description":["Deal 2% increased damage to champions when no allied champions are nearby"]},"6162":{"id":6162,"name":"Fervor of Battle","description":["Hitting champions with basic attacks generates a Fervor stack (2 for melee attacks). Stacks of Fervor last 8 seconds (max 8 stacks)and increase your AD by 1-8 for each stack."]},"6261":{"id":6261,"name":"Grasp of the Undying","description":["Every 4 seconds in combat, your next attack against an enemy champion deals damage equal to 3% of your max Health and heals you for 1.5% of your max Health (halved for ranged champions, deals magic damage)"]},"6161":{"id":6161,"name":"Warlord's Bloodlust","description":["Moving or attacking will charge an Energized attack. Energized attacks heal for 5-40% of your total Attack Damage (amplified by Critical Strikes) and grant 30% Movement Speed for 0.75 seconds."]}}}
--------------------------------------------------------------------------------
/spec/fixtures/v3/get-champion-all.json:
--------------------------------------------------------------------------------
1 | {"champions":[{"id":266,"active":true,"botEnabled":false,"freeToPlay":false,"botMmEnabled":false,"rankedPlayEnabled":true},{"id":103,"active":true,"botEnabled":false,"freeToPlay":true,"botMmEnabled":false,"rankedPlayEnabled":true},{"id":84,"active":true,"botEnabled":false,"freeToPlay":false,"botMmEnabled":false,"rankedPlayEnabled":true},{"id":12,"active":true,"botEnabled":false,"freeToPlay":false,"botMmEnabled":true,"rankedPlayEnabled":true},{"id":32,"active":true,"botEnabled":false,"freeToPlay":false,"botMmEnabled":true,"rankedPlayEnabled":true},{"id":34,"active":true,"botEnabled":false,"freeToPlay":false,"botMmEnabled":false,"rankedPlayEnabled":true},{"id":1,"active":true,"botEnabled":true,"freeToPlay":false,"botMmEnabled":true,"rankedPlayEnabled":true},{"id":22,"active":true,"botEnabled":true,"freeToPlay":false,"botMmEnabled":true,"rankedPlayEnabled":true},{"id":53,"active":true,"botEnabled":false,"freeToPlay":false,"botMmEnabled":true,"rankedPlayEnabled":true},{"id":63,"active":true,"botEnabled":false,"freeToPlay":false,"botMmEnabled":true,"rankedPlayEnabled":true},{"id":51,"active":true,"botEnabled":false,"freeToPlay":false,"botMmEnabled":true,"rankedPlayEnabled":true},{"id":69,"active":true,"botEnabled":false,"freeToPlay":false,"botMmEnabled":true,"rankedPlayEnabled":true},{"id":31,"active":true,"botEnabled":true,"freeToPlay":false,"botMmEnabled":true,"rankedPlayEnabled":true},{"id":42,"active":true,"botEnabled":false,"freeToPlay":false,"botMmEnabled":false,"rankedPlayEnabled":true},{"id":122,"active":true,"botEnabled":false,"freeToPlay":false,"botMmEnabled":true,"rankedPlayEnabled":true},{"id":131,"active":true,"botEnabled":false,"freeToPlay":false,"botMmEnabled":false,"rankedPlayEnabled":true},{"id":119,"active":true,"botEnabled":false,"freeToPlay":true,"botMmEnabled":false,"rankedPlayEnabled":true},{"id":36,"active":true,"botEnabled":false,"freeToPlay":false,"botMmEnabled":true,"rankedPlayEnabled":true},{"id":60,"active":true,"botEnabled":false,"freeToPlay":false,"botMmEnabled":false,"rankedPlayEnabled":true},{"id":28,"active":true,"botEnabled":false,"freeToPlay":false,"botMmEnabled":false,"rankedPlayEnabled":true},{"id":81,"active":true,"botEnabled":false,"freeToPlay":false,"botMmEnabled":true,"rankedPlayEnabled":true},{"id":9,"active":true,"botEnabled":true,"freeToPlay":false,"botMmEnabled":true,"rankedPlayEnabled":true},{"id":114,"active":true,"botEnabled":false,"freeToPlay":false,"botMmEnabled":false,"rankedPlayEnabled":true},{"id":105,"active":true,"botEnabled":false,"freeToPlay":false,"botMmEnabled":false,"rankedPlayEnabled":true},{"id":3,"active":true,"botEnabled":false,"freeToPlay":false,"botMmEnabled":true,"rankedPlayEnabled":true},{"id":41,"active":true,"botEnabled":false,"freeToPlay":false,"botMmEnabled":false,"rankedPlayEnabled":true},{"id":86,"active":true,"botEnabled":true,"freeToPlay":false,"botMmEnabled":true,"rankedPlayEnabled":true},{"id":79,"active":true,"botEnabled":false,"freeToPlay":false,"botMmEnabled":false,"rankedPlayEnabled":true},{"id":104,"active":true,"botEnabled":true,"freeToPlay":false,"botMmEnabled":true,"rankedPlayEnabled":true},{"id":120,"active":true,"botEnabled":false,"freeToPlay":false,"botMmEnabled":false,"rankedPlayEnabled":true},{"id":74,"active":true,"botEnabled":false,"freeToPlay":false,"botMmEnabled":false,"rankedPlayEnabled":true},{"id":39,"active":true,"botEnabled":false,"freeToPlay":false,"botMmEnabled":true,"rankedPlayEnabled":true},{"id":40,"active":true,"botEnabled":false,"freeToPlay":true,"botMmEnabled":false,"rankedPlayEnabled":true},{"id":59,"active":true,"botEnabled":false,"freeToPlay":false,"botMmEnabled":true,"rankedPlayEnabled":true},{"id":24,"active":true,"botEnabled":false,"freeToPlay":true,"botMmEnabled":true,"rankedPlayEnabled":true},{"id":126,"active":true,"botEnabled":false,"freeToPlay":false,"botMmEnabled":false,"rankedPlayEnabled":true},{"id":222,"active":true,"botEnabled":false,"freeToPlay":false,"botMmEnabled":false,"rankedPlayEnabled":true},{"id":43,"active":true,"botEnabled":false,"freeToPlay":false,"botMmEnabled":false,"rankedPlayEnabled":true},{"id":30,"active":true,"botEnabled":false,"freeToPlay":false,"botMmEnabled":true,"rankedPlayEnabled":true},{"id":38,"active":true,"botEnabled":false,"freeToPlay":false,"botMmEnabled":false,"rankedPlayEnabled":true},{"id":55,"active":true,"botEnabled":false,"freeToPlay":false,"botMmEnabled":false,"rankedPlayEnabled":true},{"id":10,"active":true,"botEnabled":false,"freeToPlay":false,"botMmEnabled":true,"rankedPlayEnabled":true},{"id":85,"active":true,"botEnabled":false,"freeToPlay":false,"botMmEnabled":false,"rankedPlayEnabled":true},{"id":121,"active":true,"botEnabled":false,"freeToPlay":false,"botMmEnabled":false,"rankedPlayEnabled":true},{"id":96,"active":true,"botEnabled":false,"freeToPlay":false,"botMmEnabled":false,"rankedPlayEnabled":true},{"id":7,"active":true,"botEnabled":false,"freeToPlay":false,"botMmEnabled":false,"rankedPlayEnabled":true},{"id":64,"active":true,"botEnabled":false,"freeToPlay":false,"botMmEnabled":false,"rankedPlayEnabled":true},{"id":89,"active":true,"botEnabled":true,"freeToPlay":false,"botMmEnabled":true,"rankedPlayEnabled":true},{"id":127,"active":true,"botEnabled":false,"freeToPlay":false,"botMmEnabled":false,"rankedPlayEnabled":true},{"id":236,"active":true,"botEnabled":false,"freeToPlay":false,"botMmEnabled":true,"rankedPlayEnabled":true},{"id":117,"active":true,"botEnabled":false,"freeToPlay":false,"botMmEnabled":false,"rankedPlayEnabled":true},{"id":99,"active":true,"botEnabled":true,"freeToPlay":false,"botMmEnabled":true,"rankedPlayEnabled":true},{"id":54,"active":true,"botEnabled":true,"freeToPlay":false,"botMmEnabled":true,"rankedPlayEnabled":true},{"id":90,"active":true,"botEnabled":true,"freeToPlay":false,"botMmEnabled":true,"rankedPlayEnabled":true},{"id":57,"active":true,"botEnabled":false,"freeToPlay":false,"botMmEnabled":false,"rankedPlayEnabled":true},{"id":11,"active":true,"botEnabled":true,"freeToPlay":false,"botMmEnabled":true,"rankedPlayEnabled":true},{"id":21,"active":true,"botEnabled":true,"freeToPlay":false,"botMmEnabled":true,"rankedPlayEnabled":true},{"id":62,"active":true,"botEnabled":true,"freeToPlay":false,"botMmEnabled":true,"rankedPlayEnabled":true},{"id":82,"active":true,"botEnabled":false,"freeToPlay":false,"botMmEnabled":false,"rankedPlayEnabled":true},{"id":25,"active":true,"botEnabled":true,"freeToPlay":false,"botMmEnabled":true,"rankedPlayEnabled":true},{"id":267,"active":true,"botEnabled":false,"freeToPlay":false,"botMmEnabled":false,"rankedPlayEnabled":true},{"id":75,"active":true,"botEnabled":true,"freeToPlay":false,"botMmEnabled":true,"rankedPlayEnabled":true},{"id":111,"active":true,"botEnabled":false,"freeToPlay":false,"botMmEnabled":false,"rankedPlayEnabled":true},{"id":76,"active":true,"botEnabled":false,"freeToPlay":false,"botMmEnabled":true,"rankedPlayEnabled":true},{"id":56,"active":true,"botEnabled":false,"freeToPlay":true,"botMmEnabled":false,"rankedPlayEnabled":true},{"id":20,"active":true,"botEnabled":true,"freeToPlay":false,"botMmEnabled":true,"rankedPlayEnabled":true},{"id":2,"active":true,"botEnabled":false,"freeToPlay":false,"botMmEnabled":false,"rankedPlayEnabled":true},{"id":61,"active":true,"botEnabled":false,"freeToPlay":false,"botMmEnabled":false,"rankedPlayEnabled":true},{"id":80,"active":true,"botEnabled":false,"freeToPlay":false,"botMmEnabled":false,"rankedPlayEnabled":true},{"id":78,"active":true,"botEnabled":false,"freeToPlay":false,"botMmEnabled":false,"rankedPlayEnabled":true},{"id":133,"active":true,"botEnabled":false,"freeToPlay":false,"botMmEnabled":false,"rankedPlayEnabled":true},{"id":33,"active":true,"botEnabled":false,"freeToPlay":false,"botMmEnabled":true,"rankedPlayEnabled":true},{"id":58,"active":true,"botEnabled":true,"freeToPlay":false,"botMmEnabled":true,"rankedPlayEnabled":true},{"id":107,"active":true,"botEnabled":false,"freeToPlay":false,"botMmEnabled":false,"rankedPlayEnabled":true},{"id":92,"active":true,"botEnabled":false,"freeToPlay":false,"botMmEnabled":false,"rankedPlayEnabled":true},{"id":68,"active":true,"botEnabled":false,"freeToPlay":false,"botMmEnabled":false,"rankedPlayEnabled":true},{"id":13,"active":true,"botEnabled":true,"freeToPlay":true,"botMmEnabled":true,"rankedPlayEnabled":true},{"id":113,"active":true,"botEnabled":false,"freeToPlay":false,"botMmEnabled":false,"rankedPlayEnabled":true},{"id":35,"active":true,"botEnabled":false,"freeToPlay":false,"botMmEnabled":false,"rankedPlayEnabled":true},{"id":98,"active":true,"botEnabled":true,"freeToPlay":false,"botMmEnabled":true,"rankedPlayEnabled":true},{"id":102,"active":true,"botEnabled":true,"freeToPlay":false,"botMmEnabled":true,"rankedPlayEnabled":true},{"id":27,"active":true,"botEnabled":false,"freeToPlay":false,"botMmEnabled":false,"rankedPlayEnabled":true},{"id":14,"active":true,"botEnabled":false,"freeToPlay":false,"botMmEnabled":true,"rankedPlayEnabled":true},{"id":15,"active":true,"botEnabled":true,"freeToPlay":false,"botMmEnabled":true,"rankedPlayEnabled":true},{"id":72,"active":true,"botEnabled":false,"freeToPlay":true,"botMmEnabled":false,"rankedPlayEnabled":true},{"id":37,"active":true,"botEnabled":true,"freeToPlay":false,"botMmEnabled":true,"rankedPlayEnabled":true},{"id":16,"active":true,"botEnabled":true,"freeToPlay":false,"botMmEnabled":true,"rankedPlayEnabled":true},{"id":50,"active":true,"botEnabled":true,"freeToPlay":false,"botMmEnabled":true,"rankedPlayEnabled":true},{"id":134,"active":true,"botEnabled":false,"freeToPlay":false,"botMmEnabled":false,"rankedPlayEnabled":true},{"id":91,"active":true,"botEnabled":false,"freeToPlay":false,"botMmEnabled":false,"rankedPlayEnabled":true},{"id":44,"active":true,"botEnabled":true,"freeToPlay":false,"botMmEnabled":true,"rankedPlayEnabled":true},{"id":17,"active":true,"botEnabled":false,"freeToPlay":false,"botMmEnabled":false,"rankedPlayEnabled":true},{"id":412,"active":true,"botEnabled":false,"freeToPlay":false,"botMmEnabled":false,"rankedPlayEnabled":true},{"id":18,"active":true,"botEnabled":true,"freeToPlay":false,"botMmEnabled":true,"rankedPlayEnabled":true},{"id":48,"active":true,"botEnabled":true,"freeToPlay":false,"botMmEnabled":true,"rankedPlayEnabled":true},{"id":23,"active":true,"botEnabled":false,"freeToPlay":true,"botMmEnabled":false,"rankedPlayEnabled":true},{"id":4,"active":true,"botEnabled":false,"freeToPlay":false,"botMmEnabled":false,"rankedPlayEnabled":true},{"id":29,"active":true,"botEnabled":false,"freeToPlay":false,"botMmEnabled":false,"rankedPlayEnabled":true},{"id":77,"active":true,"botEnabled":false,"freeToPlay":false,"botMmEnabled":true,"rankedPlayEnabled":true},{"id":6,"active":true,"botEnabled":false,"freeToPlay":false,"botMmEnabled":false,"rankedPlayEnabled":true},{"id":110,"active":true,"botEnabled":false,"freeToPlay":true,"botMmEnabled":false,"rankedPlayEnabled":true},{"id":67,"active":true,"botEnabled":false,"freeToPlay":false,"botMmEnabled":false,"rankedPlayEnabled":true},{"id":45,"active":true,"botEnabled":false,"freeToPlay":false,"botMmEnabled":true,"rankedPlayEnabled":true},{"id":161,"active":true,"botEnabled":false,"freeToPlay":false,"botMmEnabled":false,"rankedPlayEnabled":true},{"id":254,"active":true,"botEnabled":false,"freeToPlay":false,"botMmEnabled":false,"rankedPlayEnabled":true},{"id":112,"active":true,"botEnabled":false,"freeToPlay":false,"botMmEnabled":false,"rankedPlayEnabled":true},{"id":8,"active":true,"botEnabled":false,"freeToPlay":false,"botMmEnabled":true,"rankedPlayEnabled":true},{"id":106,"active":true,"botEnabled":false,"freeToPlay":false,"botMmEnabled":false,"rankedPlayEnabled":true},{"id":19,"active":true,"botEnabled":true,"freeToPlay":false,"botMmEnabled":true,"rankedPlayEnabled":true},{"id":101,"active":true,"botEnabled":false,"freeToPlay":false,"botMmEnabled":false,"rankedPlayEnabled":true},{"id":5,"active":true,"botEnabled":true,"freeToPlay":false,"botMmEnabled":true,"rankedPlayEnabled":true},{"id":157,"active":true,"botEnabled":false,"freeToPlay":false,"botMmEnabled":false,"rankedPlayEnabled":true},{"id":83,"active":true,"botEnabled":false,"freeToPlay":false,"botMmEnabled":false,"rankedPlayEnabled":true},{"id":154,"active":true,"botEnabled":false,"freeToPlay":false,"botMmEnabled":false,"rankedPlayEnabled":true},{"id":238,"active":true,"botEnabled":false,"freeToPlay":false,"botMmEnabled":false,"rankedPlayEnabled":true},{"id":115,"active":true,"botEnabled":false,"freeToPlay":false,"botMmEnabled":true,"rankedPlayEnabled":true},{"id":26,"active":true,"botEnabled":false,"freeToPlay":false,"botMmEnabled":true,"rankedPlayEnabled":true},{"id":143,"active":true,"botEnabled":false,"freeToPlay":true,"botMmEnabled":true,"rankedPlayEnabled":true}]}
--------------------------------------------------------------------------------
/spec/fixtures/v3/get-champion-masteries.json:
--------------------------------------------------------------------------------
1 | [
2 | {
3 | "highestGrade": "S+",
4 | "championPoints": 34356,
5 | "playerId": 496402,
6 | "championPointsUntilNextLevel": 0,
7 | "chestGranted": true,
8 | "championLevel": 5,
9 | "tokensEarned": 2,
10 | "championId": 40,
11 | "championPointsSinceLastLevel": 12756,
12 | "lastPlayTime": 1464499894000
13 | },
14 | {
15 | "highestGrade": "A-",
16 | "championPoints": 8972,
17 | "playerId": 496402,
18 | "championPointsUntilNextLevel": 3628,
19 | "chestGranted": false,
20 | "championLevel": 3,
21 | "tokensEarned": 0,
22 | "championId": 429,
23 | "championPointsSinceLastLevel": 2972,
24 | "lastPlayTime": 1463373502000
25 | },
26 | {
27 | "highestGrade": "A-",
28 | "championPoints": 7355,
29 | "playerId": 496402,
30 | "championPointsUntilNextLevel": 5245,
31 | "chestGranted": false,
32 | "championLevel": 3,
33 | "tokensEarned": 0,
34 | "championId": 15,
35 | "championPointsSinceLastLevel": 1355,
36 | "lastPlayTime": 1462509127000
37 | },
38 | {
39 | "highestGrade": "A+",
40 | "championPoints": 6581,
41 | "playerId": 496402,
42 | "championPointsUntilNextLevel": 6019,
43 | "chestGranted": true,
44 | "championLevel": 3,
45 | "tokensEarned": 0,
46 | "championId": 236,
47 | "championPointsSinceLastLevel": 581,
48 | "lastPlayTime": 1463462873000
49 | },
50 | {
51 | "highestGrade": "S+",
52 | "championPoints": 6555,
53 | "playerId": 496402,
54 | "championPointsUntilNextLevel": 6045,
55 | "chestGranted": false,
56 | "championLevel": 3,
57 | "tokensEarned": 0,
58 | "championId": 267,
59 | "championPointsSinceLastLevel": 555,
60 | "lastPlayTime": 1463983471000
61 | },
62 | {
63 | "highestGrade": "A+",
64 | "championPoints": 4478,
65 | "playerId": 496402,
66 | "championPointsUntilNextLevel": 1522,
67 | "chestGranted": false,
68 | "championLevel": 2,
69 | "tokensEarned": 0,
70 | "championId": 56,
71 | "championPointsSinceLastLevel": 2678,
72 | "lastPlayTime": 1463723059000
73 | },
74 | {
75 | "highestGrade": "A+",
76 | "championPoints": 4285,
77 | "playerId": 496402,
78 | "championPointsUntilNextLevel": 1715,
79 | "chestGranted": false,
80 | "championLevel": 2,
81 | "tokensEarned": 0,
82 | "championId": 245,
83 | "championPointsSinceLastLevel": 2485,
84 | "lastPlayTime": 1462688704000
85 | },
86 | {
87 | "championPoints": 3162,
88 | "playerId": 496402,
89 | "championPointsUntilNextLevel": 2838,
90 | "chestGranted": false,
91 | "championLevel": 2,
92 | "tokensEarned": 0,
93 | "championId": 67,
94 | "championPointsSinceLastLevel": 1362,
95 | "lastPlayTime": 1437768811000
96 | },
97 | {
98 | "highestGrade": "S",
99 | "championPoints": 2686,
100 | "playerId": 496402,
101 | "championPointsUntilNextLevel": 3314,
102 | "chestGranted": false,
103 | "championLevel": 2,
104 | "tokensEarned": 0,
105 | "championId": 81,
106 | "championPointsSinceLastLevel": 886,
107 | "lastPlayTime": 1464415184000
108 | },
109 | {
110 | "highestGrade": "S-",
111 | "championPoints": 2589,
112 | "playerId": 496402,
113 | "championPointsUntilNextLevel": 3411,
114 | "chestGranted": true,
115 | "championLevel": 2,
116 | "tokensEarned": 0,
117 | "championId": 53,
118 | "championPointsSinceLastLevel": 789,
119 | "lastPlayTime": 1463114828000
120 | },
121 | {
122 | "championPoints": 2567,
123 | "playerId": 496402,
124 | "championPointsUntilNextLevel": 3433,
125 | "chestGranted": false,
126 | "championLevel": 2,
127 | "tokensEarned": 0,
128 | "championId": 59,
129 | "championPointsSinceLastLevel": 767,
130 | "lastPlayTime": 1444791429000
131 | },
132 | {
133 | "championPoints": 2556,
134 | "playerId": 496402,
135 | "championPointsUntilNextLevel": 3444,
136 | "chestGranted": false,
137 | "championLevel": 2,
138 | "tokensEarned": 0,
139 | "championId": 37,
140 | "championPointsSinceLastLevel": 756,
141 | "lastPlayTime": 1444277799000
142 | },
143 | {
144 | "highestGrade": "S-",
145 | "championPoints": 2332,
146 | "playerId": 496402,
147 | "championPointsUntilNextLevel": 3668,
148 | "chestGranted": false,
149 | "championLevel": 2,
150 | "tokensEarned": 0,
151 | "championId": 157,
152 | "championPointsSinceLastLevel": 532,
153 | "lastPlayTime": 1464412893000
154 | },
155 | {
156 | "highestGrade": "A-",
157 | "championPoints": 2164,
158 | "playerId": 496402,
159 | "championPointsUntilNextLevel": 3836,
160 | "chestGranted": false,
161 | "championLevel": 2,
162 | "tokensEarned": 0,
163 | "championId": 51,
164 | "championPointsSinceLastLevel": 364,
165 | "lastPlayTime": 1463375891000
166 | },
167 | {
168 | "championPoints": 1561,
169 | "playerId": 496402,
170 | "championPointsUntilNextLevel": 239,
171 | "chestGranted": false,
172 | "championLevel": 1,
173 | "tokensEarned": 0,
174 | "championId": 22,
175 | "championPointsSinceLastLevel": 1561,
176 | "lastPlayTime": 1433019816000
177 | },
178 | {
179 | "highestGrade": "A-",
180 | "championPoints": 1435,
181 | "playerId": 496402,
182 | "championPointsUntilNextLevel": 365,
183 | "chestGranted": false,
184 | "championLevel": 1,
185 | "tokensEarned": 0,
186 | "championId": 82,
187 | "championPointsSinceLastLevel": 1435,
188 | "lastPlayTime": 1462607727000
189 | },
190 | {
191 | "highestGrade": "S",
192 | "championPoints": 1226,
193 | "playerId": 496402,
194 | "championPointsUntilNextLevel": 574,
195 | "chestGranted": true,
196 | "championLevel": 1,
197 | "tokensEarned": 0,
198 | "championId": 103,
199 | "championPointsSinceLastLevel": 1226,
200 | "lastPlayTime": 1464327096000
201 | },
202 | {
203 | "highestGrade": "A-",
204 | "championPoints": 1198,
205 | "playerId": 496402,
206 | "championPointsUntilNextLevel": 602,
207 | "chestGranted": false,
208 | "championLevel": 1,
209 | "tokensEarned": 0,
210 | "championId": 203,
211 | "championPointsSinceLastLevel": 1198,
212 | "lastPlayTime": 1462164785000
213 | },
214 | {
215 | "highestGrade": "A+",
216 | "championPoints": 1159,
217 | "playerId": 496402,
218 | "championPointsUntilNextLevel": 641,
219 | "chestGranted": false,
220 | "championLevel": 1,
221 | "tokensEarned": 0,
222 | "championId": 127,
223 | "championPointsSinceLastLevel": 1159,
224 | "lastPlayTime": 1463981860000
225 | },
226 | {
227 | "highestGrade": "S-",
228 | "championPoints": 1143,
229 | "playerId": 496402,
230 | "championPointsUntilNextLevel": 657,
231 | "chestGranted": true,
232 | "championLevel": 1,
233 | "tokensEarned": 0,
234 | "championId": 412,
235 | "championPointsSinceLastLevel": 1143,
236 | "lastPlayTime": 1462335465000
237 | },
238 | {
239 | "highestGrade": "A+",
240 | "championPoints": 1136,
241 | "playerId": 496402,
242 | "championPointsUntilNextLevel": 664,
243 | "chestGranted": true,
244 | "championLevel": 1,
245 | "tokensEarned": 0,
246 | "championId": 143,
247 | "championPointsSinceLastLevel": 1136,
248 | "lastPlayTime": 1462601925000
249 | },
250 | {
251 | "highestGrade": "A-",
252 | "championPoints": 1055,
253 | "playerId": 496402,
254 | "championPointsUntilNextLevel": 745,
255 | "chestGranted": false,
256 | "championLevel": 1,
257 | "tokensEarned": 0,
258 | "championId": 136,
259 | "championPointsSinceLastLevel": 1055,
260 | "lastPlayTime": 1462600100000
261 | },
262 | {
263 | "highestGrade": "B",
264 | "championPoints": 1012,
265 | "playerId": 496402,
266 | "championPointsUntilNextLevel": 788,
267 | "chestGranted": false,
268 | "championLevel": 1,
269 | "tokensEarned": 0,
270 | "championId": 3,
271 | "championPointsSinceLastLevel": 1012,
272 | "lastPlayTime": 1463976232000
273 | },
274 | {
275 | "championPoints": 942,
276 | "playerId": 496402,
277 | "championPointsUntilNextLevel": 858,
278 | "chestGranted": false,
279 | "championLevel": 1,
280 | "tokensEarned": 0,
281 | "championId": 60,
282 | "championPointsSinceLastLevel": 942,
283 | "lastPlayTime": 1444367620000
284 | },
285 | {
286 | "highestGrade": "A+",
287 | "championPoints": 933,
288 | "playerId": 496402,
289 | "championPointsUntilNextLevel": 867,
290 | "chestGranted": false,
291 | "championLevel": 1,
292 | "tokensEarned": 0,
293 | "championId": 64,
294 | "championPointsSinceLastLevel": 933,
295 | "lastPlayTime": 1464414045000
296 | },
297 | {
298 | "championPoints": 930,
299 | "playerId": 496402,
300 | "championPointsUntilNextLevel": 870,
301 | "chestGranted": false,
302 | "championLevel": 1,
303 | "tokensEarned": 0,
304 | "championId": 42,
305 | "championPointsSinceLastLevel": 930,
306 | "lastPlayTime": 1437273197000
307 | },
308 | {
309 | "highestGrade": "C-",
310 | "championPoints": 353,
311 | "playerId": 496402,
312 | "championPointsUntilNextLevel": 1447,
313 | "chestGranted": false,
314 | "championLevel": 1,
315 | "tokensEarned": 0,
316 | "championId": 89,
317 | "championPointsSinceLastLevel": 353,
318 | "lastPlayTime": 1463110408000
319 | },
320 | {
321 | "highestGrade": "S-",
322 | "championPoints": 284,
323 | "playerId": 496402,
324 | "championPointsUntilNextLevel": 1516,
325 | "chestGranted": true,
326 | "championLevel": 1,
327 | "tokensEarned": 0,
328 | "championId": 83,
329 | "championPointsSinceLastLevel": 284,
330 | "lastPlayTime": 1462686606000
331 | },
332 | {
333 | "highestGrade": "B",
334 | "championPoints": 229,
335 | "playerId": 496402,
336 | "championPointsUntilNextLevel": 1571,
337 | "chestGranted": false,
338 | "championLevel": 1,
339 | "tokensEarned": 0,
340 | "championId": 238,
341 | "championPointsSinceLastLevel": 229,
342 | "lastPlayTime": 1462691429000
343 | },
344 | {
345 | "highestGrade": "B",
346 | "championPoints": 219,
347 | "playerId": 496402,
348 | "championPointsUntilNextLevel": 1581,
349 | "chestGranted": false,
350 | "championLevel": 1,
351 | "tokensEarned": 0,
352 | "championId": 21,
353 | "championPointsSinceLastLevel": 219,
354 | "lastPlayTime": 1462515262000
355 | },
356 | {
357 | "highestGrade": "A-",
358 | "championPoints": 204,
359 | "playerId": 496402,
360 | "championPointsUntilNextLevel": 1596,
361 | "chestGranted": false,
362 | "championLevel": 1,
363 | "tokensEarned": 0,
364 | "championId": 420,
365 | "championPointsSinceLastLevel": 204,
366 | "lastPlayTime": 1462166929000
367 | },
368 | {
369 | "championPoints": 199,
370 | "playerId": 496402,
371 | "championPointsUntilNextLevel": 1601,
372 | "chestGranted": false,
373 | "championLevel": 1,
374 | "tokensEarned": 0,
375 | "championId": 133,
376 | "championPointsSinceLastLevel": 199,
377 | "lastPlayTime": 1436418634000
378 | },
379 | {
380 | "highestGrade": "A",
381 | "championPoints": 181,
382 | "playerId": 496402,
383 | "championPointsUntilNextLevel": 1619,
384 | "chestGranted": false,
385 | "championLevel": 1,
386 | "tokensEarned": 0,
387 | "championId": 99,
388 | "championPointsSinceLastLevel": 181,
389 | "lastPlayTime": 1463978449000
390 | },
391 | {
392 | "championPoints": 176,
393 | "playerId": 496402,
394 | "championPointsUntilNextLevel": 1624,
395 | "chestGranted": false,
396 | "championLevel": 1,
397 | "tokensEarned": 0,
398 | "championId": 76,
399 | "championPointsSinceLastLevel": 176,
400 | "lastPlayTime": 1444598939000
401 | },
402 | {
403 | "highestGrade": "B-",
404 | "championPoints": 174,
405 | "playerId": 496402,
406 | "championPointsUntilNextLevel": 1626,
407 | "chestGranted": false,
408 | "championLevel": 1,
409 | "tokensEarned": 0,
410 | "championId": 122,
411 | "championPointsSinceLastLevel": 174,
412 | "lastPlayTime": 1462162936000
413 | },
414 | {
415 | "championPoints": 161,
416 | "playerId": 496402,
417 | "championPointsUntilNextLevel": 1639,
418 | "chestGranted": false,
419 | "championLevel": 1,
420 | "tokensEarned": 0,
421 | "championId": 18,
422 | "championPointsSinceLastLevel": 161,
423 | "lastPlayTime": 1437161890000
424 | },
425 | {
426 | "highestGrade": "C",
427 | "championPoints": 159,
428 | "playerId": 496402,
429 | "championPointsUntilNextLevel": 1641,
430 | "chestGranted": false,
431 | "championLevel": 1,
432 | "tokensEarned": 0,
433 | "championId": 202,
434 | "championPointsSinceLastLevel": 159,
435 | "lastPlayTime": 1462603951000
436 | },
437 | {
438 | "championPoints": 158,
439 | "playerId": 496402,
440 | "championPointsUntilNextLevel": 1642,
441 | "chestGranted": false,
442 | "championLevel": 1,
443 | "tokensEarned": 0,
444 | "championId": 222,
445 | "championPointsSinceLastLevel": 158,
446 | "lastPlayTime": 1431475624000
447 | },
448 | {
449 | "highestGrade": "B-",
450 | "championPoints": 124,
451 | "playerId": 496402,
452 | "championPointsUntilNextLevel": 1676,
453 | "chestGranted": false,
454 | "championLevel": 1,
455 | "tokensEarned": 0,
456 | "championId": 5,
457 | "championPointsSinceLastLevel": 124,
458 | "lastPlayTime": 1464547676000
459 | }
460 | ]
--------------------------------------------------------------------------------
/spec/fixtures/v3/get-match.json:
--------------------------------------------------------------------------------
1 | {"gameId":2972702365,"platformId":"EUW1","gameCreation":1481654475333,"gameDuration":1514,"queueId":0,"mapId":11,"seasonId":8,"gameVersion":"6.24.168.1268","gameMode":"CLASSIC","gameType":"CUSTOM_GAME","teams":[{"teamId":100,"win":"Win","firstBlood":true,"firstTower":true,"firstInhibitor":true,"firstBaron":true,"firstDragon":true,"firstRiftHerald":false,"towerKills":9,"inhibitorKills":1,"baronKills":1,"dragonKills":3,"vilemawKills":0,"riftHeraldKills":0,"dominionVictoryScore":0,"bans":[{"championId":121,"pickTurn":1},{"championId":41,"pickTurn":3},{"championId":78,"pickTurn":5}]},{"teamId":200,"win":"Fail","firstBlood":false,"firstTower":false,"firstInhibitor":false,"firstBaron":false,"firstDragon":false,"firstRiftHerald":false,"towerKills":2,"inhibitorKills":0,"baronKills":0,"dragonKills":0,"vilemawKills":0,"riftHeraldKills":0,"dominionVictoryScore":0,"bans":[{"championId":7,"pickTurn":2},{"championId":134,"pickTurn":4},{"championId":55,"pickTurn":6}]}],"participants":[{"participantId":1,"teamId":100,"championId":57,"spell1Id":4,"spell2Id":12,"masteries":[{"masteryId":6114,"rank":5},{"masteryId":6122,"rank":1},{"masteryId":6134,"rank":5},{"masteryId":6143,"rank":1},{"masteryId":6211,"rank":5},{"masteryId":6223,"rank":1},{"masteryId":6231,"rank":5},{"masteryId":6241,"rank":1},{"masteryId":6251,"rank":5},{"masteryId":6262,"rank":1}],"runes":[{"runeId":5247,"rank":9},{"runeId":5290,"rank":9},{"runeId":5318,"rank":9},{"runeId":5357,"rank":3}],"highestAchievedSeasonTier":"CHALLENGER","stats":{"participantId":1,"win":true,"item0":1056,"item1":3211,"item2":1028,"item3":3068,"item4":3025,"item5":3111,"item6":3340,"kills":1,"deaths":2,"assists":11,"largestKillingSpree":0,"largestMultiKill":1,"killingSprees":0,"longestTimeSpentLiving":1079,"doubleKills":0,"tripleKills":0,"quadraKills":0,"pentaKills":0,"unrealKills":0,"totalDamageDealt":112612,"magicDamageDealt":89519,"physicalDamageDealt":23093,"trueDamageDealt":0,"largestCriticalStrike":0,"totalDamageDealtToChampions":13447,"magicDamageDealtToChampions":11117,"physicalDamageDealtToChampions":2330,"trueDamageDealtToChampions":0,"totalHeal":3602,"totalUnitsHealed":1,"damageSelfMitigated":0,"damageDealtToObjectives":0,"damageDealtToTurrets":0,"visionScore":0,"timeCCingOthers":0,"totalDamageTaken":15042,"magicalDamageTaken":6334,"physicalDamageTaken":7035,"trueDamageTaken":1672,"goldEarned":10313,"goldSpent":9360,"turretKills":2,"inhibitorKills":0,"totalMinionsKilled":192,"neutralMinionsKilled":0,"neutralMinionsKilledTeamJungle":0,"neutralMinionsKilledEnemyJungle":0,"totalTimeCrowdControlDealt":533,"champLevel":15,"visionWardsBoughtInGame":0,"sightWardsBoughtInGame":0,"wardsPlaced":9,"wardsKilled":4,"firstBloodKill":false,"firstBloodAssist":false,"firstTowerKill":true,"firstTowerAssist":false,"firstInhibitorKill":false,"firstInhibitorAssist":true,"combatPlayerScore":0,"objectivePlayerScore":0,"totalPlayerScore":0,"totalScoreRank":0},"timeline":{"participantId":1,"creepsPerMinDeltas":{"10-20":8.0,"0-10":7.300000000000001},"xpPerMinDeltas":{"10-20":511.3,"0-10":468.9},"goldPerMinDeltas":{"10-20":373.1,"0-10":340.5},"csDiffPerMinDeltas":{"10-20":0.5,"0-10":1.1000000000000005},"xpDiffPerMinDeltas":{"10-20":70.30000000000001,"0-10":75.60000000000002},"damageTakenPerMinDeltas":{"10-20":635.8,"0-10":160.89999999999998},"damageTakenDiffPerMinDeltas":{"10-20":-216.5,"0-10":-311.2},"role":"SOLO","lane":"TOP"}},{"participantId":2,"teamId":100,"championId":64,"spell1Id":4,"spell2Id":11,"masteries":[{"masteryId":6111,"rank":5},{"masteryId":6123,"rank":1},{"masteryId":6134,"rank":5},{"masteryId":6142,"rank":1},{"masteryId":6312,"rank":5},{"masteryId":6323,"rank":1},{"masteryId":6331,"rank":5},{"masteryId":6341,"rank":1},{"masteryId":6351,"rank":5},{"masteryId":6362,"rank":1}],"runes":[{"runeId":5245,"rank":9},{"runeId":5290,"rank":3},{"runeId":5295,"rank":6},{"runeId":5317,"rank":9},{"runeId":5337,"rank":3}],"highestAchievedSeasonTier":"CHALLENGER","stats":{"participantId":2,"win":true,"item0":3142,"item1":3143,"item2":3117,"item3":3211,"item4":3067,"item5":1408,"item6":3340,"kills":12,"deaths":1,"assists":10,"largestKillingSpree":11,"largestMultiKill":2,"killingSprees":1,"longestTimeSpentLiving":524,"doubleKills":1,"tripleKills":0,"quadraKills":0,"pentaKills":0,"unrealKills":0,"totalDamageDealt":155666,"magicDamageDealt":20587,"physicalDamageDealt":125239,"trueDamageDealt":9840,"largestCriticalStrike":0,"totalDamageDealtToChampions":19731,"magicDamageDealtToChampions":2673,"physicalDamageDealtToChampions":16970,"trueDamageDealtToChampions":88,"totalHeal":5771,"totalUnitsHealed":1,"damageSelfMitigated":0,"damageDealtToObjectives":0,"damageDealtToTurrets":0,"visionScore":0,"timeCCingOthers":0,"totalDamageTaken":19710,"magicalDamageTaken":5817,"physicalDamageTaken":13218,"trueDamageTaken":674,"goldEarned":12679,"goldSpent":11700,"turretKills":2,"inhibitorKills":1,"totalMinionsKilled":20,"neutralMinionsKilled":103,"neutralMinionsKilledTeamJungle":44,"neutralMinionsKilledEnemyJungle":59,"totalTimeCrowdControlDealt":744,"champLevel":14,"visionWardsBoughtInGame":3,"sightWardsBoughtInGame":0,"wardsPlaced":21,"wardsKilled":1,"firstBloodKill":true,"firstBloodAssist":false,"firstTowerKill":false,"firstTowerAssist":true,"firstInhibitorKill":true,"firstInhibitorAssist":false,"combatPlayerScore":0,"objectivePlayerScore":0,"totalPlayerScore":0,"totalScoreRank":0},"timeline":{"participantId":2,"creepsPerMinDeltas":{"10-20":1.0,"0-10":0.4},"xpPerMinDeltas":{"10-20":599.4,"0-10":344.1},"goldPerMinDeltas":{"10-20":584.0,"0-10":300.1},"csDiffPerMinDeltas":{"10-20":0.9,"0-10":0.30000000000000004},"xpDiffPerMinDeltas":{"10-20":260.29999999999995,"0-10":50.5},"damageTakenPerMinDeltas":{"10-20":807.4,"0-10":640.0},"damageTakenDiffPerMinDeltas":{"10-20":-64.5,"0-10":146.2},"role":"NONE","lane":"JUNGLE"}},{"participantId":3,"teamId":100,"championId":13,"spell1Id":4,"spell2Id":6,"masteries":[{"masteryId":6114,"rank":5},{"masteryId":6121,"rank":1},{"masteryId":6131,"rank":5},{"masteryId":6142,"rank":1},{"masteryId":6211,"rank":5},{"masteryId":6221,"rank":1},{"masteryId":6231,"rank":5},{"masteryId":6241,"rank":1},{"masteryId":6251,"rank":5},{"masteryId":6262,"rank":1}],"runes":[{"runeId":5273,"rank":9},{"runeId":5289,"rank":3},{"runeId":5296,"rank":6},{"runeId":5316,"rank":9},{"runeId":5365,"rank":3}],"highestAchievedSeasonTier":"CHALLENGER","stats":{"participantId":3,"win":true,"item0":3070,"item1":3111,"item2":0,"item3":3001,"item4":3027,"item5":0,"item6":3363,"kills":3,"deaths":1,"assists":5,"largestKillingSpree":2,"largestMultiKill":1,"killingSprees":1,"longestTimeSpentLiving":1209,"doubleKills":0,"tripleKills":0,"quadraKills":0,"pentaKills":0,"unrealKills":0,"totalDamageDealt":108566,"magicDamageDealt":96136,"physicalDamageDealt":12430,"trueDamageDealt":0,"largestCriticalStrike":699,"totalDamageDealtToChampions":8751,"magicDamageDealtToChampions":8159,"physicalDamageDealtToChampions":592,"trueDamageDealtToChampions":0,"totalHeal":1403,"totalUnitsHealed":1,"damageSelfMitigated":0,"damageDealtToObjectives":0,"damageDealtToTurrets":0,"visionScore":0,"timeCCingOthers":0,"totalDamageTaken":7212,"magicalDamageTaken":2709,"physicalDamageTaken":3306,"trueDamageTaken":1196,"goldEarned":10076,"goldSpent":8125,"turretKills":0,"inhibitorKills":0,"totalMinionsKilled":162,"neutralMinionsKilled":4,"neutralMinionsKilledTeamJungle":2,"neutralMinionsKilledEnemyJungle":2,"totalTimeCrowdControlDealt":11,"champLevel":14,"visionWardsBoughtInGame":7,"sightWardsBoughtInGame":0,"wardsPlaced":17,"wardsKilled":3,"firstBloodKill":false,"firstBloodAssist":false,"firstTowerKill":false,"firstTowerAssist":false,"firstInhibitorKill":false,"firstInhibitorAssist":true,"combatPlayerScore":0,"objectivePlayerScore":0,"totalPlayerScore":0,"totalScoreRank":0},"timeline":{"participantId":3,"creepsPerMinDeltas":{"10-20":7.5,"0-10":7.5},"xpPerMinDeltas":{"10-20":506.9,"0-10":438.29999999999995},"goldPerMinDeltas":{"10-20":426.6,"0-10":268.8},"csDiffPerMinDeltas":{"10-20":-1.8000000000000003,"0-10":-1.6999999999999997},"xpDiffPerMinDeltas":{"10-20":-2.400000000000034,"0-10":-31.0},"damageTakenPerMinDeltas":{"10-20":216.89999999999998,"0-10":171.8},"damageTakenDiffPerMinDeltas":{"10-20":-200.20000000000005,"0-10":-35.29999999999999},"role":"SOLO","lane":"MIDDLE"}},{"participantId":4,"teamId":100,"championId":202,"spell1Id":4,"spell2Id":7,"masteries":[{"masteryId":6114,"rank":5},{"masteryId":6121,"rank":1},{"masteryId":6134,"rank":5},{"masteryId":6143,"rank":1},{"masteryId":6151,"rank":5},{"masteryId":6164,"rank":1},{"masteryId":6312,"rank":5},{"masteryId":6322,"rank":1},{"masteryId":6331,"rank":5},{"masteryId":6343,"rank":1}],"runes":[{"runeId":5245,"rank":9},{"runeId":5275,"rank":4},{"runeId":5289,"rank":5},{"runeId":5317,"rank":9},{"runeId":5335,"rank":3}],"highestAchievedSeasonTier":"CHALLENGER","stats":{"participantId":4,"win":true,"item0":3508,"item1":3094,"item2":3140,"item3":3009,"item4":1037,"item5":1055,"item6":3363,"kills":8,"deaths":1,"assists":4,"largestKillingSpree":4,"largestMultiKill":2,"killingSprees":2,"longestTimeSpentLiving":873,"doubleKills":1,"tripleKills":0,"quadraKills":0,"pentaKills":0,"unrealKills":0,"totalDamageDealt":155523,"magicDamageDealt":3953,"physicalDamageDealt":151289,"trueDamageDealt":280,"largestCriticalStrike":839,"totalDamageDealtToChampions":18355,"magicDamageDealtToChampions":2753,"physicalDamageDealtToChampions":15602,"trueDamageDealtToChampions":0,"totalHeal":1067,"totalUnitsHealed":2,"damageSelfMitigated":0,"damageDealtToObjectives":0,"damageDealtToTurrets":0,"visionScore":0,"timeCCingOthers":0,"totalDamageTaken":5048,"magicalDamageTaken":1870,"physicalDamageTaken":3135,"trueDamageTaken":42,"goldEarned":12279,"goldSpent":9900,"turretKills":4,"inhibitorKills":0,"totalMinionsKilled":232,"neutralMinionsKilled":2,"neutralMinionsKilledTeamJungle":0,"neutralMinionsKilledEnemyJungle":2,"totalTimeCrowdControlDealt":56,"champLevel":13,"visionWardsBoughtInGame":1,"sightWardsBoughtInGame":0,"wardsPlaced":8,"wardsKilled":1,"firstBloodKill":false,"firstBloodAssist":false,"firstTowerKill":false,"firstTowerAssist":false,"firstInhibitorKill":false,"firstInhibitorAssist":true,"combatPlayerScore":0,"objectivePlayerScore":0,"totalPlayerScore":0,"totalScoreRank":0},"timeline":{"participantId":4,"creepsPerMinDeltas":{"10-20":11.6,"0-10":8.2},"xpPerMinDeltas":{"10-20":481.5,"0-10":327.1},"goldPerMinDeltas":{"10-20":533.2,"0-10":330.8},"csDiffPerMinDeltas":{"10-20":0.5499999999999998,"0-10":-0.5499999999999998},"xpDiffPerMinDeltas":{"10-20":12.55000000000004,"0-10":26.85000000000005},"damageTakenPerMinDeltas":{"10-20":263.3,"0-10":129.4},"damageTakenDiffPerMinDeltas":{"10-20":-251.64999999999992,"0-10":-113.49999999999999},"role":"DUO_CARRY","lane":"BOTTOM"}},{"participantId":5,"teamId":100,"championId":43,"spell1Id":3,"spell2Id":4,"masteries":[{"masteryId":6211,"rank":5},{"masteryId":6223,"rank":1},{"masteryId":6231,"rank":5},{"masteryId":6241,"rank":1},{"masteryId":6312,"rank":5},{"masteryId":6322,"rank":1},{"masteryId":6332,"rank":5},{"masteryId":6341,"rank":1},{"masteryId":6352,"rank":5},{"masteryId":6363,"rank":1}],"runes":[{"runeId":5273,"rank":9},{"runeId":5297,"rank":9},{"runeId":5315,"rank":9},{"runeId":5347,"rank":2},{"runeId":5357,"rank":1}],"highestAchievedSeasonTier":"CHALLENGER","stats":{"participantId":5,"win":true,"item0":0,"item1":3107,"item2":2301,"item3":0,"item4":3158,"item5":1033,"item6":3364,"kills":0,"deaths":1,"assists":15,"largestKillingSpree":0,"largestMultiKill":0,"killingSprees":0,"longestTimeSpentLiving":906,"doubleKills":0,"tripleKills":0,"quadraKills":0,"pentaKills":0,"unrealKills":0,"totalDamageDealt":23658,"magicDamageDealt":15121,"physicalDamageDealt":8536,"trueDamageDealt":0,"largestCriticalStrike":0,"totalDamageDealtToChampions":6211,"magicDamageDealtToChampions":5187,"physicalDamageDealtToChampions":1024,"trueDamageDealtToChampions":0,"totalHeal":1383,"totalUnitsHealed":4,"damageSelfMitigated":0,"damageDealtToObjectives":0,"damageDealtToTurrets":0,"visionScore":0,"timeCCingOthers":0,"totalDamageTaken":7019,"magicalDamageTaken":3137,"physicalDamageTaken":3031,"trueDamageTaken":849,"goldEarned":7653,"goldSpent":6600,"turretKills":0,"inhibitorKills":0,"totalMinionsKilled":11,"neutralMinionsKilled":0,"neutralMinionsKilledTeamJungle":0,"neutralMinionsKilledEnemyJungle":0,"totalTimeCrowdControlDealt":163,"champLevel":12,"visionWardsBoughtInGame":8,"sightWardsBoughtInGame":0,"wardsPlaced":29,"wardsKilled":6,"firstBloodKill":false,"firstBloodAssist":false,"firstTowerKill":false,"firstTowerAssist":false,"firstInhibitorKill":false,"firstInhibitorAssist":true,"combatPlayerScore":0,"objectivePlayerScore":0,"totalPlayerScore":0,"totalScoreRank":0},"timeline":{"participantId":5,"creepsPerMinDeltas":{"10-20":0.4,"0-10":0.30000000000000004},"xpPerMinDeltas":{"10-20":340.9,"0-10":310.6},"goldPerMinDeltas":{"10-20":292.70000000000005,"0-10":182.0},"csDiffPerMinDeltas":{"10-20":0.5499999999999998,"0-10":-0.5499999999999998},"xpDiffPerMinDeltas":{"10-20":12.55000000000004,"0-10":26.85000000000005},"damageTakenPerMinDeltas":{"10-20":262.6,"0-10":223.6},"damageTakenDiffPerMinDeltas":{"10-20":-251.64999999999992,"0-10":-113.49999999999999},"role":"DUO_SUPPORT","lane":"BOTTOM"}},{"participantId":6,"teamId":200,"championId":48,"spell1Id":4,"spell2Id":12,"masteries":[{"masteryId":6111,"rank":5},{"masteryId":6122,"rank":1},{"masteryId":6131,"rank":5},{"masteryId":6143,"rank":1},{"masteryId":6211,"rank":5},{"masteryId":6223,"rank":1},{"masteryId":6231,"rank":5},{"masteryId":6241,"rank":1},{"masteryId":6251,"rank":5},{"masteryId":6261,"rank":1}],"runes":[{"runeId":5245,"rank":6},{"runeId":5247,"rank":3},{"runeId":5296,"rank":9},{"runeId":5316,"rank":9},{"runeId":5349,"rank":3}],"highestAchievedSeasonTier":"MASTER","stats":{"participantId":6,"win":false,"item0":3748,"item1":3111,"item2":2033,"item3":3211,"item4":3123,"item5":0,"item6":3340,"kills":1,"deaths":5,"assists":1,"largestKillingSpree":0,"largestMultiKill":1,"killingSprees":0,"longestTimeSpentLiving":392,"doubleKills":0,"tripleKills":0,"quadraKills":0,"pentaKills":0,"unrealKills":0,"totalDamageDealt":83510,"magicDamageDealt":1358,"physicalDamageDealt":82151,"trueDamageDealt":0,"largestCriticalStrike":0,"totalDamageDealtToChampions":5334,"magicDamageDealtToChampions":1358,"physicalDamageDealtToChampions":3975,"trueDamageDealtToChampions":0,"totalHeal":5810,"totalUnitsHealed":1,"damageSelfMitigated":0,"damageDealtToObjectives":0,"damageDealtToTurrets":0,"visionScore":0,"timeCCingOthers":0,"totalDamageTaken":24544,"magicalDamageTaken":11479,"physicalDamageTaken":13025,"trueDamageTaken":40,"goldEarned":7407,"goldSpent":7250,"turretKills":0,"inhibitorKills":0,"totalMinionsKilled":162,"neutralMinionsKilled":0,"neutralMinionsKilledTeamJungle":0,"neutralMinionsKilledEnemyJungle":0,"totalTimeCrowdControlDealt":254,"champLevel":13,"visionWardsBoughtInGame":2,"sightWardsBoughtInGame":0,"wardsPlaced":12,"wardsKilled":2,"firstBloodKill":false,"firstBloodAssist":false,"firstTowerKill":false,"firstTowerAssist":false,"firstInhibitorKill":false,"firstInhibitorAssist":false,"combatPlayerScore":0,"objectivePlayerScore":0,"totalPlayerScore":0,"totalScoreRank":0},"timeline":{"participantId":6,"creepsPerMinDeltas":{"10-20":7.5,"0-10":6.199999999999999},"xpPerMinDeltas":{"10-20":441.0,"0-10":393.29999999999995},"goldPerMinDeltas":{"10-20":325.5,"0-10":226.8},"csDiffPerMinDeltas":{"10-20":-0.5,"0-10":-1.1000000000000005},"xpDiffPerMinDeltas":{"10-20":-70.30000000000001,"0-10":-75.60000000000002},"damageTakenPerMinDeltas":{"10-20":852.3,"0-10":472.09999999999997},"damageTakenDiffPerMinDeltas":{"10-20":216.5,"0-10":311.2},"role":"SOLO","lane":"TOP"}},{"participantId":7,"teamId":200,"championId":60,"spell1Id":4,"spell2Id":11,"masteries":[{"masteryId":6114,"rank":5},{"masteryId":6121,"rank":1},{"masteryId":6134,"rank":5},{"masteryId":6142,"rank":1},{"masteryId":6312,"rank":5},{"masteryId":6321,"rank":1},{"masteryId":6331,"rank":5},{"masteryId":6341,"rank":1},{"masteryId":6351,"rank":5},{"masteryId":6362,"rank":1}],"runes":[{"runeId":5247,"rank":9},{"runeId":5289,"rank":3},{"runeId":5296,"rank":6},{"runeId":5315,"rank":5},{"runeId":5317,"rank":4},{"runeId":5357,"rank":3}],"highestAchievedSeasonTier":"DIAMOND","stats":{"participantId":7,"win":false,"item0":1414,"item1":2031,"item2":2055,"item3":3116,"item4":3111,"item5":0,"item6":3364,"kills":3,"deaths":7,"assists":2,"largestKillingSpree":0,"largestMultiKill":1,"killingSprees":0,"longestTimeSpentLiving":579,"doubleKills":0,"tripleKills":0,"quadraKills":0,"pentaKills":0,"unrealKills":0,"totalDamageDealt":76421,"magicDamageDealt":52030,"physicalDamageDealt":19567,"trueDamageDealt":4824,"largestCriticalStrike":0,"totalDamageDealtToChampions":8046,"magicDamageDealtToChampions":6672,"physicalDamageDealtToChampions":906,"trueDamageDealtToChampions":468,"totalHeal":5598,"totalUnitsHealed":1,"damageSelfMitigated":0,"damageDealtToObjectives":0,"damageDealtToTurrets":0,"visionScore":0,"timeCCingOthers":0,"totalDamageTaken":19533,"magicalDamageTaken":4795,"physicalDamageTaken":14690,"trueDamageTaken":48,"goldEarned":7266,"goldSpent":6850,"turretKills":0,"inhibitorKills":0,"totalMinionsKilled":10,"neutralMinionsKilled":74,"neutralMinionsKilledTeamJungle":57,"neutralMinionsKilledEnemyJungle":17,"totalTimeCrowdControlDealt":182,"champLevel":12,"visionWardsBoughtInGame":5,"sightWardsBoughtInGame":0,"wardsPlaced":5,"wardsKilled":5,"firstBloodKill":false,"firstBloodAssist":false,"firstTowerKill":false,"firstTowerAssist":false,"firstInhibitorKill":false,"firstInhibitorAssist":false,"combatPlayerScore":0,"objectivePlayerScore":0,"totalPlayerScore":0,"totalScoreRank":0},"timeline":{"participantId":7,"creepsPerMinDeltas":{"10-20":0.1,"0-10":0.1},"xpPerMinDeltas":{"10-20":339.1,"0-10":293.6},"goldPerMinDeltas":{"10-20":264.4,"0-10":241.6},"csDiffPerMinDeltas":{"10-20":-0.9,"0-10":-0.30000000000000004},"xpDiffPerMinDeltas":{"10-20":-260.29999999999995,"0-10":-50.5},"damageTakenPerMinDeltas":{"10-20":871.9,"0-10":493.79999999999995},"damageTakenDiffPerMinDeltas":{"10-20":64.5,"0-10":-146.2},"role":"NONE","lane":"JUNGLE"}},{"participantId":8,"teamId":200,"championId":103,"spell1Id":4,"spell2Id":14,"masteries":[{"masteryId":6114,"rank":5},{"masteryId":6122,"rank":1},{"masteryId":6131,"rank":1},{"masteryId":6134,"rank":4},{"masteryId":6142,"rank":1},{"masteryId":6312,"rank":5},{"masteryId":6322,"rank":1},{"masteryId":6331,"rank":4},{"masteryId":6332,"rank":1},{"masteryId":6343,"rank":1},{"masteryId":6351,"rank":5},{"masteryId":6362,"rank":1}],"runes":[{"runeId":5273,"rank":9},{"runeId":5296,"rank":6},{"runeId":5298,"rank":3},{"runeId":5316,"rank":9},{"runeId":5357,"rank":3}],"highestAchievedSeasonTier":"MASTER","stats":{"participantId":8,"win":false,"item0":1056,"item1":2031,"item2":3020,"item3":3165,"item4":3001,"item5":1026,"item6":3340,"kills":2,"deaths":1,"assists":2,"largestKillingSpree":2,"largestMultiKill":1,"killingSprees":1,"longestTimeSpentLiving":782,"doubleKills":0,"tripleKills":0,"quadraKills":0,"pentaKills":0,"unrealKills":0,"totalDamageDealt":108759,"magicDamageDealt":59980,"physicalDamageDealt":15251,"trueDamageDealt":33527,"largestCriticalStrike":0,"totalDamageDealtToChampions":11463,"magicDamageDealtToChampions":7595,"physicalDamageDealtToChampions":602,"trueDamageDealtToChampions":3265,"totalHeal":2577,"totalUnitsHealed":1,"damageSelfMitigated":0,"damageDealtToObjectives":0,"damageDealtToTurrets":0,"visionScore":0,"timeCCingOthers":0,"totalDamageTaken":8700,"magicalDamageTaken":4265,"physicalDamageTaken":4435,"trueDamageTaken":0,"goldEarned":9156,"goldSpent":8975,"turretKills":0,"inhibitorKills":0,"totalMinionsKilled":226,"neutralMinionsKilled":2,"neutralMinionsKilledTeamJungle":1,"neutralMinionsKilledEnemyJungle":1,"totalTimeCrowdControlDealt":28,"champLevel":14,"visionWardsBoughtInGame":3,"sightWardsBoughtInGame":0,"wardsPlaced":10,"wardsKilled":2,"firstBloodKill":false,"firstBloodAssist":false,"firstTowerKill":false,"firstTowerAssist":false,"firstInhibitorKill":false,"firstInhibitorAssist":false,"combatPlayerScore":0,"objectivePlayerScore":0,"totalPlayerScore":0,"totalScoreRank":0},"timeline":{"participantId":8,"creepsPerMinDeltas":{"10-20":9.3,"0-10":9.2},"xpPerMinDeltas":{"10-20":509.3,"0-10":469.29999999999995},"goldPerMinDeltas":{"10-20":374.20000000000005,"0-10":284.3},"csDiffPerMinDeltas":{"10-20":1.8000000000000003,"0-10":1.6999999999999997},"xpDiffPerMinDeltas":{"10-20":2.400000000000034,"0-10":31.0},"damageTakenPerMinDeltas":{"10-20":417.1,"0-10":207.1},"damageTakenDiffPerMinDeltas":{"10-20":200.20000000000005,"0-10":35.29999999999999},"role":"SOLO","lane":"MIDDLE"}},{"participantId":9,"teamId":200,"championId":51,"spell1Id":7,"spell2Id":4,"masteries":[{"masteryId":6111,"rank":5},{"masteryId":6121,"rank":1},{"masteryId":6131,"rank":5},{"masteryId":6141,"rank":1},{"masteryId":6151,"rank":5},{"masteryId":6161,"rank":1},{"masteryId":6312,"rank":5},{"masteryId":6322,"rank":1},{"masteryId":6331,"rank":5},{"masteryId":6343,"rank":1}],"runes":[{"runeId":5245,"rank":9},{"runeId":5289,"rank":9},{"runeId":5317,"rank":9},{"runeId":5337,"rank":3}],"highestAchievedSeasonTier":"DIAMOND","stats":{"participantId":9,"win":false,"item0":1055,"item1":3006,"item2":3085,"item3":3031,"item4":2015,"item5":1042,"item6":3363,"kills":0,"deaths":6,"assists":3,"largestKillingSpree":0,"largestMultiKill":0,"killingSprees":0,"longestTimeSpentLiving":525,"doubleKills":0,"tripleKills":0,"quadraKills":0,"pentaKills":0,"unrealKills":0,"totalDamageDealt":124947,"magicDamageDealt":413,"physicalDamageDealt":123000,"trueDamageDealt":1534,"largestCriticalStrike":1015,"totalDamageDealtToChampions":7726,"magicDamageDealtToChampions":191,"physicalDamageDealtToChampions":6833,"trueDamageDealtToChampions":702,"totalHeal":971,"totalUnitsHealed":2,"damageSelfMitigated":0,"damageDealtToObjectives":0,"damageDealtToTurrets":0,"visionScore":0,"timeCCingOthers":0,"totalDamageTaken":14459,"magicalDamageTaken":3999,"physicalDamageTaken":10460,"trueDamageTaken":0,"goldEarned":9030,"goldSpent":8975,"turretKills":1,"inhibitorKills":0,"totalMinionsKilled":206,"neutralMinionsKilled":19,"neutralMinionsKilledTeamJungle":6,"neutralMinionsKilledEnemyJungle":13,"totalTimeCrowdControlDealt":152,"champLevel":12,"visionWardsBoughtInGame":1,"sightWardsBoughtInGame":0,"wardsPlaced":8,"wardsKilled":4,"firstBloodKill":false,"firstBloodAssist":false,"firstTowerKill":false,"firstTowerAssist":false,"firstInhibitorKill":false,"firstInhibitorAssist":false,"combatPlayerScore":0,"objectivePlayerScore":0,"totalPlayerScore":0,"totalScoreRank":0},"timeline":{"participantId":9,"creepsPerMinDeltas":{"10-20":9.3,"0-10":8.5},"xpPerMinDeltas":{"10-20":483.5,"0-10":311.29999999999995},"goldPerMinDeltas":{"10-20":403.4,"0-10":273.8},"csDiffPerMinDeltas":{"10-20":-0.5499999999999998,"0-10":0.5499999999999998},"xpDiffPerMinDeltas":{"10-20":-12.55000000000004,"0-10":-26.85000000000005},"damageTakenPerMinDeltas":{"10-20":610.6,"0-10":280.4},"damageTakenDiffPerMinDeltas":{"10-20":251.64999999999992,"0-10":113.49999999999999},"role":"DUO_CARRY","lane":"BOTTOM"}},{"participantId":10,"teamId":200,"championId":412,"spell1Id":4,"spell2Id":3,"masteries":[{"masteryId":6211,"rank":5},{"masteryId":6221,"rank":1},{"masteryId":6231,"rank":5},{"masteryId":6241,"rank":1},{"masteryId":6251,"rank":5},{"masteryId":6262,"rank":1},{"masteryId":6312,"rank":5},{"masteryId":6322,"rank":1},{"masteryId":6332,"rank":5},{"masteryId":6342,"rank":1}],"runes":[{"runeId":5245,"rank":9},{"runeId":5290,"rank":3},{"runeId":5295,"rank":6},{"runeId":5315,"rank":5},{"runeId":5317,"rank":4},{"runeId":5347,"rank":2},{"runeId":5351,"rank":1}],"highestAchievedSeasonTier":"MASTER","stats":{"participantId":10,"win":false,"item0":2045,"item1":2055,"item2":3801,"item3":3302,"item4":3117,"item5":3114,"item6":3364,"kills":0,"deaths":5,"assists":4,"largestKillingSpree":0,"largestMultiKill":0,"killingSprees":0,"longestTimeSpentLiving":637,"doubleKills":0,"tripleKills":0,"quadraKills":0,"pentaKills":0,"unrealKills":0,"totalDamageDealt":14307,"magicDamageDealt":9264,"physicalDamageDealt":5042,"trueDamageDealt":0,"largestCriticalStrike":0,"totalDamageDealtToChampions":3073,"magicDamageDealtToChampions":2625,"physicalDamageDealtToChampions":447,"trueDamageDealtToChampions":0,"totalHeal":809,"totalUnitsHealed":3,"damageSelfMitigated":0,"damageDealtToObjectives":0,"damageDealtToTurrets":0,"visionScore":0,"timeCCingOthers":0,"totalDamageTaken":16156,"magicalDamageTaken":6480,"physicalDamageTaken":9675,"trueDamageTaken":0,"goldEarned":5577,"goldSpent":5300,"turretKills":0,"inhibitorKills":0,"totalMinionsKilled":31,"neutralMinionsKilled":0,"neutralMinionsKilledTeamJungle":0,"neutralMinionsKilledEnemyJungle":0,"totalTimeCrowdControlDealt":89,"champLevel":10,"visionWardsBoughtInGame":8,"sightWardsBoughtInGame":0,"wardsPlaced":26,"wardsKilled":6,"firstBloodKill":false,"firstBloodAssist":false,"firstTowerKill":false,"firstTowerAssist":false,"firstInhibitorKill":false,"firstInhibitorAssist":false,"combatPlayerScore":0,"objectivePlayerScore":0,"totalPlayerScore":0,"totalScoreRank":0},"timeline":{"participantId":10,"creepsPerMinDeltas":{"10-20":1.6,"0-10":1.1},"xpPerMinDeltas":{"10-20":313.8,"0-10":272.7},"goldPerMinDeltas":{"10-20":237.9,"0-10":153.10000000000002},"csDiffPerMinDeltas":{"10-20":-0.5499999999999998,"0-10":0.5499999999999998},"xpDiffPerMinDeltas":{"10-20":-12.55000000000004,"0-10":-26.85000000000005},"damageTakenPerMinDeltas":{"10-20":418.6,"0-10":299.6},"damageTakenDiffPerMinDeltas":{"10-20":251.64999999999992,"0-10":113.49999999999999},"role":"DUO_SUPPORT","lane":"BOTTOM"}}],"participantIdentities":[{"participantId":1},{"participantId":2},{"participantId":3},{"participantId":4},{"participantId":5},{"participantId":6},{"participantId":7},{"participantId":8},{"participantId":9},{"participantId":10}]}
--------------------------------------------------------------------------------
/spec/fixtures/v3/get-match-with-tc.json:
--------------------------------------------------------------------------------
1 | {"gameId":2972702365,"platformId":"EUW1","gameCreation":1481654475333,"gameDuration":1514,"queueId":0,"mapId":11,"seasonId":8,"gameVersion":"6.24.168.1268","gameMode":"CLASSIC","gameType":"CUSTOM_GAME","teams":[{"teamId":100,"win":"Win","firstBlood":true,"firstTower":true,"firstInhibitor":true,"firstBaron":true,"firstDragon":true,"firstRiftHerald":false,"towerKills":9,"inhibitorKills":1,"baronKills":1,"dragonKills":3,"vilemawKills":0,"riftHeraldKills":0,"dominionVictoryScore":0,"bans":[{"championId":121,"pickTurn":1},{"championId":41,"pickTurn":3},{"championId":78,"pickTurn":5}]},{"teamId":200,"win":"Fail","firstBlood":false,"firstTower":false,"firstInhibitor":false,"firstBaron":false,"firstDragon":false,"firstRiftHerald":false,"towerKills":2,"inhibitorKills":0,"baronKills":0,"dragonKills":0,"vilemawKills":0,"riftHeraldKills":0,"dominionVictoryScore":0,"bans":[{"championId":7,"pickTurn":2},{"championId":134,"pickTurn":4},{"championId":55,"pickTurn":6}]}],"participants":[{"participantId":1,"teamId":100,"championId":57,"spell1Id":4,"spell2Id":12,"masteries":[{"masteryId":6114,"rank":5},{"masteryId":6122,"rank":1},{"masteryId":6134,"rank":5},{"masteryId":6143,"rank":1},{"masteryId":6211,"rank":5},{"masteryId":6223,"rank":1},{"masteryId":6231,"rank":5},{"masteryId":6241,"rank":1},{"masteryId":6251,"rank":5},{"masteryId":6262,"rank":1}],"runes":[{"runeId":5247,"rank":9},{"runeId":5290,"rank":9},{"runeId":5318,"rank":9},{"runeId":5357,"rank":3}],"highestAchievedSeasonTier":"CHALLENGER","stats":{"participantId":1,"win":true,"item0":1056,"item1":3211,"item2":1028,"item3":3068,"item4":3025,"item5":3111,"item6":3340,"kills":1,"deaths":2,"assists":11,"largestKillingSpree":0,"largestMultiKill":1,"killingSprees":0,"longestTimeSpentLiving":1079,"doubleKills":0,"tripleKills":0,"quadraKills":0,"pentaKills":0,"unrealKills":0,"totalDamageDealt":112612,"magicDamageDealt":89519,"physicalDamageDealt":23093,"trueDamageDealt":0,"largestCriticalStrike":0,"totalDamageDealtToChampions":13447,"magicDamageDealtToChampions":11117,"physicalDamageDealtToChampions":2330,"trueDamageDealtToChampions":0,"totalHeal":3602,"totalUnitsHealed":1,"damageSelfMitigated":0,"damageDealtToObjectives":0,"damageDealtToTurrets":0,"visionScore":0,"timeCCingOthers":0,"totalDamageTaken":15042,"magicalDamageTaken":6334,"physicalDamageTaken":7035,"trueDamageTaken":1672,"goldEarned":10313,"goldSpent":9360,"turretKills":2,"inhibitorKills":0,"totalMinionsKilled":192,"neutralMinionsKilled":0,"neutralMinionsKilledTeamJungle":0,"neutralMinionsKilledEnemyJungle":0,"totalTimeCrowdControlDealt":533,"champLevel":15,"visionWardsBoughtInGame":0,"sightWardsBoughtInGame":0,"wardsPlaced":9,"wardsKilled":4,"firstBloodKill":false,"firstBloodAssist":false,"firstTowerKill":true,"firstTowerAssist":false,"firstInhibitorKill":false,"firstInhibitorAssist":true,"combatPlayerScore":0,"objectivePlayerScore":0,"totalPlayerScore":0,"totalScoreRank":0},"timeline":{"participantId":1,"creepsPerMinDeltas":{"10-20":8.0,"0-10":7.300000000000001},"xpPerMinDeltas":{"10-20":511.3,"0-10":468.9},"goldPerMinDeltas":{"10-20":373.1,"0-10":340.5},"csDiffPerMinDeltas":{"10-20":0.5,"0-10":1.1000000000000005},"xpDiffPerMinDeltas":{"10-20":70.30000000000001,"0-10":75.60000000000002},"damageTakenPerMinDeltas":{"10-20":635.8,"0-10":160.89999999999998},"damageTakenDiffPerMinDeltas":{"10-20":-216.5,"0-10":-311.2},"role":"SOLO","lane":"TOP"}},{"participantId":2,"teamId":100,"championId":64,"spell1Id":4,"spell2Id":11,"masteries":[{"masteryId":6111,"rank":5},{"masteryId":6123,"rank":1},{"masteryId":6134,"rank":5},{"masteryId":6142,"rank":1},{"masteryId":6312,"rank":5},{"masteryId":6323,"rank":1},{"masteryId":6331,"rank":5},{"masteryId":6341,"rank":1},{"masteryId":6351,"rank":5},{"masteryId":6362,"rank":1}],"runes":[{"runeId":5245,"rank":9},{"runeId":5290,"rank":3},{"runeId":5295,"rank":6},{"runeId":5317,"rank":9},{"runeId":5337,"rank":3}],"highestAchievedSeasonTier":"CHALLENGER","stats":{"participantId":2,"win":true,"item0":3142,"item1":3143,"item2":3117,"item3":3211,"item4":3067,"item5":1408,"item6":3340,"kills":12,"deaths":1,"assists":10,"largestKillingSpree":11,"largestMultiKill":2,"killingSprees":1,"longestTimeSpentLiving":524,"doubleKills":1,"tripleKills":0,"quadraKills":0,"pentaKills":0,"unrealKills":0,"totalDamageDealt":155666,"magicDamageDealt":20587,"physicalDamageDealt":125239,"trueDamageDealt":9840,"largestCriticalStrike":0,"totalDamageDealtToChampions":19731,"magicDamageDealtToChampions":2673,"physicalDamageDealtToChampions":16970,"trueDamageDealtToChampions":88,"totalHeal":5771,"totalUnitsHealed":1,"damageSelfMitigated":0,"damageDealtToObjectives":0,"damageDealtToTurrets":0,"visionScore":0,"timeCCingOthers":0,"totalDamageTaken":19710,"magicalDamageTaken":5817,"physicalDamageTaken":13218,"trueDamageTaken":674,"goldEarned":12679,"goldSpent":11700,"turretKills":2,"inhibitorKills":1,"totalMinionsKilled":20,"neutralMinionsKilled":103,"neutralMinionsKilledTeamJungle":44,"neutralMinionsKilledEnemyJungle":59,"totalTimeCrowdControlDealt":744,"champLevel":14,"visionWardsBoughtInGame":3,"sightWardsBoughtInGame":0,"wardsPlaced":21,"wardsKilled":1,"firstBloodKill":true,"firstBloodAssist":false,"firstTowerKill":false,"firstTowerAssist":true,"firstInhibitorKill":true,"firstInhibitorAssist":false,"combatPlayerScore":0,"objectivePlayerScore":0,"totalPlayerScore":0,"totalScoreRank":0},"timeline":{"participantId":2,"creepsPerMinDeltas":{"10-20":1.0,"0-10":0.4},"xpPerMinDeltas":{"10-20":599.4,"0-10":344.1},"goldPerMinDeltas":{"10-20":584.0,"0-10":300.1},"csDiffPerMinDeltas":{"10-20":0.9,"0-10":0.30000000000000004},"xpDiffPerMinDeltas":{"10-20":260.29999999999995,"0-10":50.5},"damageTakenPerMinDeltas":{"10-20":807.4,"0-10":640.0},"damageTakenDiffPerMinDeltas":{"10-20":-64.5,"0-10":146.2},"role":"NONE","lane":"JUNGLE"}},{"participantId":3,"teamId":100,"championId":13,"spell1Id":4,"spell2Id":6,"masteries":[{"masteryId":6114,"rank":5},{"masteryId":6121,"rank":1},{"masteryId":6131,"rank":5},{"masteryId":6142,"rank":1},{"masteryId":6211,"rank":5},{"masteryId":6221,"rank":1},{"masteryId":6231,"rank":5},{"masteryId":6241,"rank":1},{"masteryId":6251,"rank":5},{"masteryId":6262,"rank":1}],"runes":[{"runeId":5273,"rank":9},{"runeId":5289,"rank":3},{"runeId":5296,"rank":6},{"runeId":5316,"rank":9},{"runeId":5365,"rank":3}],"highestAchievedSeasonTier":"CHALLENGER","stats":{"participantId":3,"win":true,"item0":3070,"item1":3111,"item2":0,"item3":3001,"item4":3027,"item5":0,"item6":3363,"kills":3,"deaths":1,"assists":5,"largestKillingSpree":2,"largestMultiKill":1,"killingSprees":1,"longestTimeSpentLiving":1209,"doubleKills":0,"tripleKills":0,"quadraKills":0,"pentaKills":0,"unrealKills":0,"totalDamageDealt":108566,"magicDamageDealt":96136,"physicalDamageDealt":12430,"trueDamageDealt":0,"largestCriticalStrike":699,"totalDamageDealtToChampions":8751,"magicDamageDealtToChampions":8159,"physicalDamageDealtToChampions":592,"trueDamageDealtToChampions":0,"totalHeal":1403,"totalUnitsHealed":1,"damageSelfMitigated":0,"damageDealtToObjectives":0,"damageDealtToTurrets":0,"visionScore":0,"timeCCingOthers":0,"totalDamageTaken":7212,"magicalDamageTaken":2709,"physicalDamageTaken":3306,"trueDamageTaken":1196,"goldEarned":10076,"goldSpent":8125,"turretKills":0,"inhibitorKills":0,"totalMinionsKilled":162,"neutralMinionsKilled":4,"neutralMinionsKilledTeamJungle":2,"neutralMinionsKilledEnemyJungle":2,"totalTimeCrowdControlDealt":11,"champLevel":14,"visionWardsBoughtInGame":7,"sightWardsBoughtInGame":0,"wardsPlaced":17,"wardsKilled":3,"firstBloodKill":false,"firstBloodAssist":false,"firstTowerKill":false,"firstTowerAssist":false,"firstInhibitorKill":false,"firstInhibitorAssist":true,"combatPlayerScore":0,"objectivePlayerScore":0,"totalPlayerScore":0,"totalScoreRank":0},"timeline":{"participantId":3,"creepsPerMinDeltas":{"10-20":7.5,"0-10":7.5},"xpPerMinDeltas":{"10-20":506.9,"0-10":438.29999999999995},"goldPerMinDeltas":{"10-20":426.6,"0-10":268.8},"csDiffPerMinDeltas":{"10-20":-1.8000000000000003,"0-10":-1.6999999999999997},"xpDiffPerMinDeltas":{"10-20":-2.400000000000034,"0-10":-31.0},"damageTakenPerMinDeltas":{"10-20":216.89999999999998,"0-10":171.8},"damageTakenDiffPerMinDeltas":{"10-20":-200.20000000000005,"0-10":-35.29999999999999},"role":"SOLO","lane":"MIDDLE"}},{"participantId":4,"teamId":100,"championId":202,"spell1Id":4,"spell2Id":7,"masteries":[{"masteryId":6114,"rank":5},{"masteryId":6121,"rank":1},{"masteryId":6134,"rank":5},{"masteryId":6143,"rank":1},{"masteryId":6151,"rank":5},{"masteryId":6164,"rank":1},{"masteryId":6312,"rank":5},{"masteryId":6322,"rank":1},{"masteryId":6331,"rank":5},{"masteryId":6343,"rank":1}],"runes":[{"runeId":5245,"rank":9},{"runeId":5275,"rank":4},{"runeId":5289,"rank":5},{"runeId":5317,"rank":9},{"runeId":5335,"rank":3}],"highestAchievedSeasonTier":"CHALLENGER","stats":{"participantId":4,"win":true,"item0":3508,"item1":3094,"item2":3140,"item3":3009,"item4":1037,"item5":1055,"item6":3363,"kills":8,"deaths":1,"assists":4,"largestKillingSpree":4,"largestMultiKill":2,"killingSprees":2,"longestTimeSpentLiving":873,"doubleKills":1,"tripleKills":0,"quadraKills":0,"pentaKills":0,"unrealKills":0,"totalDamageDealt":155523,"magicDamageDealt":3953,"physicalDamageDealt":151289,"trueDamageDealt":280,"largestCriticalStrike":839,"totalDamageDealtToChampions":18355,"magicDamageDealtToChampions":2753,"physicalDamageDealtToChampions":15602,"trueDamageDealtToChampions":0,"totalHeal":1067,"totalUnitsHealed":2,"damageSelfMitigated":0,"damageDealtToObjectives":0,"damageDealtToTurrets":0,"visionScore":0,"timeCCingOthers":0,"totalDamageTaken":5048,"magicalDamageTaken":1870,"physicalDamageTaken":3135,"trueDamageTaken":42,"goldEarned":12279,"goldSpent":9900,"turretKills":4,"inhibitorKills":0,"totalMinionsKilled":232,"neutralMinionsKilled":2,"neutralMinionsKilledTeamJungle":0,"neutralMinionsKilledEnemyJungle":2,"totalTimeCrowdControlDealt":56,"champLevel":13,"visionWardsBoughtInGame":1,"sightWardsBoughtInGame":0,"wardsPlaced":8,"wardsKilled":1,"firstBloodKill":false,"firstBloodAssist":false,"firstTowerKill":false,"firstTowerAssist":false,"firstInhibitorKill":false,"firstInhibitorAssist":true,"combatPlayerScore":0,"objectivePlayerScore":0,"totalPlayerScore":0,"totalScoreRank":0},"timeline":{"participantId":4,"creepsPerMinDeltas":{"10-20":11.6,"0-10":8.2},"xpPerMinDeltas":{"10-20":481.5,"0-10":327.1},"goldPerMinDeltas":{"10-20":533.2,"0-10":330.8},"csDiffPerMinDeltas":{"10-20":0.5499999999999998,"0-10":-0.5499999999999998},"xpDiffPerMinDeltas":{"10-20":12.55000000000004,"0-10":26.85000000000005},"damageTakenPerMinDeltas":{"10-20":263.3,"0-10":129.4},"damageTakenDiffPerMinDeltas":{"10-20":-251.64999999999992,"0-10":-113.49999999999999},"role":"DUO_CARRY","lane":"BOTTOM"}},{"participantId":5,"teamId":100,"championId":43,"spell1Id":3,"spell2Id":4,"masteries":[{"masteryId":6211,"rank":5},{"masteryId":6223,"rank":1},{"masteryId":6231,"rank":5},{"masteryId":6241,"rank":1},{"masteryId":6312,"rank":5},{"masteryId":6322,"rank":1},{"masteryId":6332,"rank":5},{"masteryId":6341,"rank":1},{"masteryId":6352,"rank":5},{"masteryId":6363,"rank":1}],"runes":[{"runeId":5273,"rank":9},{"runeId":5297,"rank":9},{"runeId":5315,"rank":9},{"runeId":5347,"rank":2},{"runeId":5357,"rank":1}],"highestAchievedSeasonTier":"CHALLENGER","stats":{"participantId":5,"win":true,"item0":0,"item1":3107,"item2":2301,"item3":0,"item4":3158,"item5":1033,"item6":3364,"kills":0,"deaths":1,"assists":15,"largestKillingSpree":0,"largestMultiKill":0,"killingSprees":0,"longestTimeSpentLiving":906,"doubleKills":0,"tripleKills":0,"quadraKills":0,"pentaKills":0,"unrealKills":0,"totalDamageDealt":23658,"magicDamageDealt":15121,"physicalDamageDealt":8536,"trueDamageDealt":0,"largestCriticalStrike":0,"totalDamageDealtToChampions":6211,"magicDamageDealtToChampions":5187,"physicalDamageDealtToChampions":1024,"trueDamageDealtToChampions":0,"totalHeal":1383,"totalUnitsHealed":4,"damageSelfMitigated":0,"damageDealtToObjectives":0,"damageDealtToTurrets":0,"visionScore":0,"timeCCingOthers":0,"totalDamageTaken":7019,"magicalDamageTaken":3137,"physicalDamageTaken":3031,"trueDamageTaken":849,"goldEarned":7653,"goldSpent":6600,"turretKills":0,"inhibitorKills":0,"totalMinionsKilled":11,"neutralMinionsKilled":0,"neutralMinionsKilledTeamJungle":0,"neutralMinionsKilledEnemyJungle":0,"totalTimeCrowdControlDealt":163,"champLevel":12,"visionWardsBoughtInGame":8,"sightWardsBoughtInGame":0,"wardsPlaced":29,"wardsKilled":6,"firstBloodKill":false,"firstBloodAssist":false,"firstTowerKill":false,"firstTowerAssist":false,"firstInhibitorKill":false,"firstInhibitorAssist":true,"combatPlayerScore":0,"objectivePlayerScore":0,"totalPlayerScore":0,"totalScoreRank":0},"timeline":{"participantId":5,"creepsPerMinDeltas":{"10-20":0.4,"0-10":0.30000000000000004},"xpPerMinDeltas":{"10-20":340.9,"0-10":310.6},"goldPerMinDeltas":{"10-20":292.70000000000005,"0-10":182.0},"csDiffPerMinDeltas":{"10-20":0.5499999999999998,"0-10":-0.5499999999999998},"xpDiffPerMinDeltas":{"10-20":12.55000000000004,"0-10":26.85000000000005},"damageTakenPerMinDeltas":{"10-20":262.6,"0-10":223.6},"damageTakenDiffPerMinDeltas":{"10-20":-251.64999999999992,"0-10":-113.49999999999999},"role":"DUO_SUPPORT","lane":"BOTTOM"}},{"participantId":6,"teamId":200,"championId":48,"spell1Id":4,"spell2Id":12,"masteries":[{"masteryId":6111,"rank":5},{"masteryId":6122,"rank":1},{"masteryId":6131,"rank":5},{"masteryId":6143,"rank":1},{"masteryId":6211,"rank":5},{"masteryId":6223,"rank":1},{"masteryId":6231,"rank":5},{"masteryId":6241,"rank":1},{"masteryId":6251,"rank":5},{"masteryId":6261,"rank":1}],"runes":[{"runeId":5245,"rank":6},{"runeId":5247,"rank":3},{"runeId":5296,"rank":9},{"runeId":5316,"rank":9},{"runeId":5349,"rank":3}],"highestAchievedSeasonTier":"MASTER","stats":{"participantId":6,"win":false,"item0":3748,"item1":3111,"item2":2033,"item3":3211,"item4":3123,"item5":0,"item6":3340,"kills":1,"deaths":5,"assists":1,"largestKillingSpree":0,"largestMultiKill":1,"killingSprees":0,"longestTimeSpentLiving":392,"doubleKills":0,"tripleKills":0,"quadraKills":0,"pentaKills":0,"unrealKills":0,"totalDamageDealt":83510,"magicDamageDealt":1358,"physicalDamageDealt":82151,"trueDamageDealt":0,"largestCriticalStrike":0,"totalDamageDealtToChampions":5334,"magicDamageDealtToChampions":1358,"physicalDamageDealtToChampions":3975,"trueDamageDealtToChampions":0,"totalHeal":5810,"totalUnitsHealed":1,"damageSelfMitigated":0,"damageDealtToObjectives":0,"damageDealtToTurrets":0,"visionScore":0,"timeCCingOthers":0,"totalDamageTaken":24544,"magicalDamageTaken":11479,"physicalDamageTaken":13025,"trueDamageTaken":40,"goldEarned":7407,"goldSpent":7250,"turretKills":0,"inhibitorKills":0,"totalMinionsKilled":162,"neutralMinionsKilled":0,"neutralMinionsKilledTeamJungle":0,"neutralMinionsKilledEnemyJungle":0,"totalTimeCrowdControlDealt":254,"champLevel":13,"visionWardsBoughtInGame":2,"sightWardsBoughtInGame":0,"wardsPlaced":12,"wardsKilled":2,"firstBloodKill":false,"firstBloodAssist":false,"firstTowerKill":false,"firstTowerAssist":false,"firstInhibitorKill":false,"firstInhibitorAssist":false,"combatPlayerScore":0,"objectivePlayerScore":0,"totalPlayerScore":0,"totalScoreRank":0},"timeline":{"participantId":6,"creepsPerMinDeltas":{"10-20":7.5,"0-10":6.199999999999999},"xpPerMinDeltas":{"10-20":441.0,"0-10":393.29999999999995},"goldPerMinDeltas":{"10-20":325.5,"0-10":226.8},"csDiffPerMinDeltas":{"10-20":-0.5,"0-10":-1.1000000000000005},"xpDiffPerMinDeltas":{"10-20":-70.30000000000001,"0-10":-75.60000000000002},"damageTakenPerMinDeltas":{"10-20":852.3,"0-10":472.09999999999997},"damageTakenDiffPerMinDeltas":{"10-20":216.5,"0-10":311.2},"role":"SOLO","lane":"TOP"}},{"participantId":7,"teamId":200,"championId":60,"spell1Id":4,"spell2Id":11,"masteries":[{"masteryId":6114,"rank":5},{"masteryId":6121,"rank":1},{"masteryId":6134,"rank":5},{"masteryId":6142,"rank":1},{"masteryId":6312,"rank":5},{"masteryId":6321,"rank":1},{"masteryId":6331,"rank":5},{"masteryId":6341,"rank":1},{"masteryId":6351,"rank":5},{"masteryId":6362,"rank":1}],"runes":[{"runeId":5247,"rank":9},{"runeId":5289,"rank":3},{"runeId":5296,"rank":6},{"runeId":5315,"rank":5},{"runeId":5317,"rank":4},{"runeId":5357,"rank":3}],"highestAchievedSeasonTier":"DIAMOND","stats":{"participantId":7,"win":false,"item0":1414,"item1":2031,"item2":2055,"item3":3116,"item4":3111,"item5":0,"item6":3364,"kills":3,"deaths":7,"assists":2,"largestKillingSpree":0,"largestMultiKill":1,"killingSprees":0,"longestTimeSpentLiving":579,"doubleKills":0,"tripleKills":0,"quadraKills":0,"pentaKills":0,"unrealKills":0,"totalDamageDealt":76421,"magicDamageDealt":52030,"physicalDamageDealt":19567,"trueDamageDealt":4824,"largestCriticalStrike":0,"totalDamageDealtToChampions":8046,"magicDamageDealtToChampions":6672,"physicalDamageDealtToChampions":906,"trueDamageDealtToChampions":468,"totalHeal":5598,"totalUnitsHealed":1,"damageSelfMitigated":0,"damageDealtToObjectives":0,"damageDealtToTurrets":0,"visionScore":0,"timeCCingOthers":0,"totalDamageTaken":19533,"magicalDamageTaken":4795,"physicalDamageTaken":14690,"trueDamageTaken":48,"goldEarned":7266,"goldSpent":6850,"turretKills":0,"inhibitorKills":0,"totalMinionsKilled":10,"neutralMinionsKilled":74,"neutralMinionsKilledTeamJungle":57,"neutralMinionsKilledEnemyJungle":17,"totalTimeCrowdControlDealt":182,"champLevel":12,"visionWardsBoughtInGame":5,"sightWardsBoughtInGame":0,"wardsPlaced":5,"wardsKilled":5,"firstBloodKill":false,"firstBloodAssist":false,"firstTowerKill":false,"firstTowerAssist":false,"firstInhibitorKill":false,"firstInhibitorAssist":false,"combatPlayerScore":0,"objectivePlayerScore":0,"totalPlayerScore":0,"totalScoreRank":0},"timeline":{"participantId":7,"creepsPerMinDeltas":{"10-20":0.1,"0-10":0.1},"xpPerMinDeltas":{"10-20":339.1,"0-10":293.6},"goldPerMinDeltas":{"10-20":264.4,"0-10":241.6},"csDiffPerMinDeltas":{"10-20":-0.9,"0-10":-0.30000000000000004},"xpDiffPerMinDeltas":{"10-20":-260.29999999999995,"0-10":-50.5},"damageTakenPerMinDeltas":{"10-20":871.9,"0-10":493.79999999999995},"damageTakenDiffPerMinDeltas":{"10-20":64.5,"0-10":-146.2},"role":"NONE","lane":"JUNGLE"}},{"participantId":8,"teamId":200,"championId":103,"spell1Id":4,"spell2Id":14,"masteries":[{"masteryId":6114,"rank":5},{"masteryId":6122,"rank":1},{"masteryId":6131,"rank":1},{"masteryId":6134,"rank":4},{"masteryId":6142,"rank":1},{"masteryId":6312,"rank":5},{"masteryId":6322,"rank":1},{"masteryId":6331,"rank":4},{"masteryId":6332,"rank":1},{"masteryId":6343,"rank":1},{"masteryId":6351,"rank":5},{"masteryId":6362,"rank":1}],"runes":[{"runeId":5273,"rank":9},{"runeId":5296,"rank":6},{"runeId":5298,"rank":3},{"runeId":5316,"rank":9},{"runeId":5357,"rank":3}],"highestAchievedSeasonTier":"MASTER","stats":{"participantId":8,"win":false,"item0":1056,"item1":2031,"item2":3020,"item3":3165,"item4":3001,"item5":1026,"item6":3340,"kills":2,"deaths":1,"assists":2,"largestKillingSpree":2,"largestMultiKill":1,"killingSprees":1,"longestTimeSpentLiving":782,"doubleKills":0,"tripleKills":0,"quadraKills":0,"pentaKills":0,"unrealKills":0,"totalDamageDealt":108759,"magicDamageDealt":59980,"physicalDamageDealt":15251,"trueDamageDealt":33527,"largestCriticalStrike":0,"totalDamageDealtToChampions":11463,"magicDamageDealtToChampions":7595,"physicalDamageDealtToChampions":602,"trueDamageDealtToChampions":3265,"totalHeal":2577,"totalUnitsHealed":1,"damageSelfMitigated":0,"damageDealtToObjectives":0,"damageDealtToTurrets":0,"visionScore":0,"timeCCingOthers":0,"totalDamageTaken":8700,"magicalDamageTaken":4265,"physicalDamageTaken":4435,"trueDamageTaken":0,"goldEarned":9156,"goldSpent":8975,"turretKills":0,"inhibitorKills":0,"totalMinionsKilled":226,"neutralMinionsKilled":2,"neutralMinionsKilledTeamJungle":1,"neutralMinionsKilledEnemyJungle":1,"totalTimeCrowdControlDealt":28,"champLevel":14,"visionWardsBoughtInGame":3,"sightWardsBoughtInGame":0,"wardsPlaced":10,"wardsKilled":2,"firstBloodKill":false,"firstBloodAssist":false,"firstTowerKill":false,"firstTowerAssist":false,"firstInhibitorKill":false,"firstInhibitorAssist":false,"combatPlayerScore":0,"objectivePlayerScore":0,"totalPlayerScore":0,"totalScoreRank":0},"timeline":{"participantId":8,"creepsPerMinDeltas":{"10-20":9.3,"0-10":9.2},"xpPerMinDeltas":{"10-20":509.3,"0-10":469.29999999999995},"goldPerMinDeltas":{"10-20":374.20000000000005,"0-10":284.3},"csDiffPerMinDeltas":{"10-20":1.8000000000000003,"0-10":1.6999999999999997},"xpDiffPerMinDeltas":{"10-20":2.400000000000034,"0-10":31.0},"damageTakenPerMinDeltas":{"10-20":417.1,"0-10":207.1},"damageTakenDiffPerMinDeltas":{"10-20":200.20000000000005,"0-10":35.29999999999999},"role":"SOLO","lane":"MIDDLE"}},{"participantId":9,"teamId":200,"championId":51,"spell1Id":7,"spell2Id":4,"masteries":[{"masteryId":6111,"rank":5},{"masteryId":6121,"rank":1},{"masteryId":6131,"rank":5},{"masteryId":6141,"rank":1},{"masteryId":6151,"rank":5},{"masteryId":6161,"rank":1},{"masteryId":6312,"rank":5},{"masteryId":6322,"rank":1},{"masteryId":6331,"rank":5},{"masteryId":6343,"rank":1}],"runes":[{"runeId":5245,"rank":9},{"runeId":5289,"rank":9},{"runeId":5317,"rank":9},{"runeId":5337,"rank":3}],"highestAchievedSeasonTier":"DIAMOND","stats":{"participantId":9,"win":false,"item0":1055,"item1":3006,"item2":3085,"item3":3031,"item4":2015,"item5":1042,"item6":3363,"kills":0,"deaths":6,"assists":3,"largestKillingSpree":0,"largestMultiKill":0,"killingSprees":0,"longestTimeSpentLiving":525,"doubleKills":0,"tripleKills":0,"quadraKills":0,"pentaKills":0,"unrealKills":0,"totalDamageDealt":124947,"magicDamageDealt":413,"physicalDamageDealt":123000,"trueDamageDealt":1534,"largestCriticalStrike":1015,"totalDamageDealtToChampions":7726,"magicDamageDealtToChampions":191,"physicalDamageDealtToChampions":6833,"trueDamageDealtToChampions":702,"totalHeal":971,"totalUnitsHealed":2,"damageSelfMitigated":0,"damageDealtToObjectives":0,"damageDealtToTurrets":0,"visionScore":0,"timeCCingOthers":0,"totalDamageTaken":14459,"magicalDamageTaken":3999,"physicalDamageTaken":10460,"trueDamageTaken":0,"goldEarned":9030,"goldSpent":8975,"turretKills":1,"inhibitorKills":0,"totalMinionsKilled":206,"neutralMinionsKilled":19,"neutralMinionsKilledTeamJungle":6,"neutralMinionsKilledEnemyJungle":13,"totalTimeCrowdControlDealt":152,"champLevel":12,"visionWardsBoughtInGame":1,"sightWardsBoughtInGame":0,"wardsPlaced":8,"wardsKilled":4,"firstBloodKill":false,"firstBloodAssist":false,"firstTowerKill":false,"firstTowerAssist":false,"firstInhibitorKill":false,"firstInhibitorAssist":false,"combatPlayerScore":0,"objectivePlayerScore":0,"totalPlayerScore":0,"totalScoreRank":0},"timeline":{"participantId":9,"creepsPerMinDeltas":{"10-20":9.3,"0-10":8.5},"xpPerMinDeltas":{"10-20":483.5,"0-10":311.29999999999995},"goldPerMinDeltas":{"10-20":403.4,"0-10":273.8},"csDiffPerMinDeltas":{"10-20":-0.5499999999999998,"0-10":0.5499999999999998},"xpDiffPerMinDeltas":{"10-20":-12.55000000000004,"0-10":-26.85000000000005},"damageTakenPerMinDeltas":{"10-20":610.6,"0-10":280.4},"damageTakenDiffPerMinDeltas":{"10-20":251.64999999999992,"0-10":113.49999999999999},"role":"DUO_CARRY","lane":"BOTTOM"}},{"participantId":10,"teamId":200,"championId":412,"spell1Id":4,"spell2Id":3,"masteries":[{"masteryId":6211,"rank":5},{"masteryId":6221,"rank":1},{"masteryId":6231,"rank":5},{"masteryId":6241,"rank":1},{"masteryId":6251,"rank":5},{"masteryId":6262,"rank":1},{"masteryId":6312,"rank":5},{"masteryId":6322,"rank":1},{"masteryId":6332,"rank":5},{"masteryId":6342,"rank":1}],"runes":[{"runeId":5245,"rank":9},{"runeId":5290,"rank":3},{"runeId":5295,"rank":6},{"runeId":5315,"rank":5},{"runeId":5317,"rank":4},{"runeId":5347,"rank":2},{"runeId":5351,"rank":1}],"highestAchievedSeasonTier":"MASTER","stats":{"participantId":10,"win":false,"item0":2045,"item1":2055,"item2":3801,"item3":3302,"item4":3117,"item5":3114,"item6":3364,"kills":0,"deaths":5,"assists":4,"largestKillingSpree":0,"largestMultiKill":0,"killingSprees":0,"longestTimeSpentLiving":637,"doubleKills":0,"tripleKills":0,"quadraKills":0,"pentaKills":0,"unrealKills":0,"totalDamageDealt":14307,"magicDamageDealt":9264,"physicalDamageDealt":5042,"trueDamageDealt":0,"largestCriticalStrike":0,"totalDamageDealtToChampions":3073,"magicDamageDealtToChampions":2625,"physicalDamageDealtToChampions":447,"trueDamageDealtToChampions":0,"totalHeal":809,"totalUnitsHealed":3,"damageSelfMitigated":0,"damageDealtToObjectives":0,"damageDealtToTurrets":0,"visionScore":0,"timeCCingOthers":0,"totalDamageTaken":16156,"magicalDamageTaken":6480,"physicalDamageTaken":9675,"trueDamageTaken":0,"goldEarned":5577,"goldSpent":5300,"turretKills":0,"inhibitorKills":0,"totalMinionsKilled":31,"neutralMinionsKilled":0,"neutralMinionsKilledTeamJungle":0,"neutralMinionsKilledEnemyJungle":0,"totalTimeCrowdControlDealt":89,"champLevel":10,"visionWardsBoughtInGame":8,"sightWardsBoughtInGame":0,"wardsPlaced":26,"wardsKilled":6,"firstBloodKill":false,"firstBloodAssist":false,"firstTowerKill":false,"firstTowerAssist":false,"firstInhibitorKill":false,"firstInhibitorAssist":false,"combatPlayerScore":0,"objectivePlayerScore":0,"totalPlayerScore":0,"totalScoreRank":0},"timeline":{"participantId":10,"creepsPerMinDeltas":{"10-20":1.6,"0-10":1.1},"xpPerMinDeltas":{"10-20":313.8,"0-10":272.7},"goldPerMinDeltas":{"10-20":237.9,"0-10":153.10000000000002},"csDiffPerMinDeltas":{"10-20":-0.5499999999999998,"0-10":0.5499999999999998},"xpDiffPerMinDeltas":{"10-20":-12.55000000000004,"0-10":-26.85000000000005},"damageTakenPerMinDeltas":{"10-20":418.6,"0-10":299.6},"damageTakenDiffPerMinDeltas":{"10-20":251.64999999999992,"0-10":113.49999999999999},"role":"DUO_SUPPORT","lane":"BOTTOM"}}],"participantIdentities":[{"participantId":1,"player":{"summonerName":"Kikis","profileIcon":603}},{"participantId":2,"player":{"summonerName":"Broxah","profileIcon":1424}},{"participantId":3,"player":{"summonerName":"FNC NlSQY","profileIcon":563}},{"participantId":4,"player":{"summonerName":"Dana 60 gs babay","profileIcon":2074}},{"participantId":5,"player":{"summonerName":"FNC Klaj","profileIcon":1023}},{"participantId":6,"player":{"summonerName":"MLG Glebo HLTV","profileIcon":566}},{"participantId":7,"player":{"summonerName":"KROPLA DESZCZU","profileIcon":548}},{"participantId":8,"player":{"summonerName":"Roison","profileIcon":1410}},{"participantId":9,"player":{"summonerName":"SheIsMyMorphine","profileIcon":1448}},{"participantId":10,"player":{"summonerName":"Pyrka","profileIcon":21}}]}
--------------------------------------------------------------------------------