cbortz/vainglory-api-ruby

View on GitHub
lib/vainglory_api/client.rb

Summary

Maintainability
A
2 hrs
Test Coverage
require 'json'
require 'ostruct'
require 'openssl'
require 'net/http'
require 'vainglory_api/region'

module VaingloryAPI
  # Used to interface with the official Vainglory API
  #
  # @see https://developer.vainglorygame.com/docs Vainglory API Documentation
  # @see https://developer.vainglorygame.com/docs#payload Vainglory API "Payload"
  # @see https://developer.vainglorygame.com/docs#rate-limits Vainglory API "Rate Limits"
  class Client
    # The base URL used for most requests
    BASE_URL = 'https://api.dc01.gamelockerapp.com'.freeze

    # A new instance of Client.
    #
    # @param (String) api_key your Vainglory API key
    # @param (String) region_identifier the name or short name for your specified Region shard
    # @example Initialize a new client
    #   client = VaingloryAPI::Client.new('API_KEY', 'na')
    # @return [Client] a new instance of the client
    # @note Requires a valid region short name.
    # @see VaingloryAPI::Region::SHORT_NAMES
    def initialize(api_key, region_identifier = 'na')
      @api_key = api_key
      @region = Region.find(region_identifier)
    end

    # Gets batches of random match data
    #
    # @param [Hash] filter_params the parameters used to filter results
    # @option filter_params [String] 'page[offset]' (0) Allows paging over results
    # @option filter_params [String] 'page[limit]' (50) Values less than 50 and great than 2 are supported.
    # @option filter_params [String] 'sort' (createdAt) By default, Matches are sorted by creation time ascending.
    # @option filter_params [String] 'filter[createdAt-start]' (3hrs ago)  Must occur before end time. Format is iso8601
    # @option filter_params [String] 'filter[createdAt-end]' (Now) Queries search the last 3 hrs. Format is iso8601
    # @example Get samples
    #   client = VaingloryAPI::Client.new('API_KEY', 'na')
    #   client.samples
    # @return [OpenStruct] the response and metadata
    # @see https://developer.vainglorygame.com/docs#samples Vainglory API "Samples"
    # @see https://developer.vainglorygame.com/docs#pagination Vainglory API "Pagination"
    # @see https://developer.vainglorygame.com/docs#sorting Vainglory API "Sorting"
    def samples(filter_params = {})
      get_request(shard_endpoint_uri('samples', filter_params))
    end

    # Gets data from matches (multiple)
    #
    # @param [Hash] filter_params the parameters used to filter results
    # @option filter_params [String] 'page[offset]' (0) Allows paging over results
    # @option filter_params [String] 'page[limit]' (50) Values less than 50 and great than 2 are supported.
    # @option filter_params [String] 'sort' (createdAt) By default, Matches are sorted by creation time ascending.
    # @option filter_params [String] 'filter[createdAt-start]' (3hrs ago)  Must occur before end time. Format is iso8601
    # @option filter_params [String] 'filter[createdAt-end]' (Now) Queries search the last 3 hrs. Format is iso8601
    # @option filter_params [String] 'filter[playerNames]' Filters by player name, separated by commas.
    # @option filter_params [String] 'filter[playerIds]' Filters by player Id, separated by commas.
    # @option filter_params [String] 'filter[teamNames]' Filters by team names. Team names are the same as the in game team tags.
    # @option filter_params [String] 'filter[gameMode]' Filters by Game Mode
    # @example Get matches
    #   client = VaingloryAPI::Client.new('API_KEY', 'na')
    #   client.matches
    # @example Get matches with a filter
    #   client = VaingloryAPI::Client.new('API_KEY', 'na')
    #   client.matches('filter[playerNames]' => 'player_name')
    # @return [OpenStruct] the response and metadata
    # @see https://developer.vainglorygame.com/docs#get-a-collection-of-matches Vainglory API "Get a collection of Matches"
    # @see https://developer.vainglorygame.com/docs#rosters Vainglory API "Rosters"
    # @see https://developer.vainglorygame.com/docs#match-data-summary Vainglory API "Match Data Summary"
    # @see https://developer.vainglorygame.com/docs#pagination Vainglory API "Pagination"
    # @see https://developer.vainglorygame.com/docs#sorting Vainglory API "Sorting"
    def matches(filter_params = {})
      get_request(shard_endpoint_uri('matches', filter_params))
    end

    # Gets data for a single match
    #
    # @param [String] match_id the ID of the requested match
    # @example Get a single match
    #   client = VaingloryAPI::Client.new('API_KEY', 'na')
    #   client.match('MATCH_ID')
    # @return [OpenStruct] the response and metadata
    # @see https://developer.vainglorygame.com/docs#get-a-single-match Vainglory API "Get a single Match"
    # @see https://developer.vainglorygame.com/docs#rosters Vainglory API "Rosters"
    # @see https://developer.vainglorygame.com/docs#match-data-summary Vainglory API "Match Data Summary"
    def match(match_id)
      get_request(shard_endpoint_uri("matches/#{match_id}"))
    end

    # Gets data about players (one or more)
    #
    # @param [String] player_name the in-game name (IGN) of a player
    # @param [String] additional_player_names additional IGNs for search for
    # @example Search for a player
    #   client = VaingloryAPI::Client.new('API_KEY', 'na')
    #   client.players('player_name')
    # @example Search for multiple players
    #   client = VaingloryAPI::Client.new('API_KEY', 'na')
    #   client.players('player_name', 'player_name2')
    # @return [OpenStruct] the response and metadata
    # @see https://developer.vainglorygame.com/docs#get-a-collection-of-players Vainglory API "Get a collection of players"
    def players(player_name, *additional_player_names)
      player_names  = [player_name].concat(additional_player_names)
      filter_params = { 'filter[playerNames]' => player_names.join(',') }

      get_request(shard_endpoint_uri('players', filter_params))
    end

    # Gets data for a single player
    #
    # @param [String] player_id the ID of the requested player
    # @example Get a single player
    #   client = VaingloryAPI::Client.new('API_KEY', 'na')
    #   client.match('PLAYER_ID')
    # @return [OpenStruct] the response and metadata
    # @see https://developer.vainglorygame.com/docs#get-a-single-player Vainglory API "Get a single Player"
    def player(player_id)
      get_request(shard_endpoint_uri("players/#{player_id}"))
    end

    # Gets telemtry data from a specified URL
    #
    # @param [String] url the URL of the requested Telemetry data
    # @example Get telemetry data
    #   client = VaingloryAPI::Client.new('API_KEY', 'na')
    #   client.telemetry('TELEMETRY_URL')
    # @return [OpenStruct] the response and metadata
    # @see https://developer.vainglorygame.com/docs#telemetry Vainglory API "Telemetry"
    def telemetry(url)
      get_request(URI(url), true, false)
    end

    # Gets aggregated lifetime information about teams (multiple)
    #
    # @param [Hash] _filter_params the parameters used to filter results
    # @option _filter_params [String] 'filter[teamNames]' Filters by team name
    # @option _filter_params [String] 'filter[teamIds]' Filters by team ID
    # @raise [NotImplementedError] this endpoint is not yet available
    # @see https://developer.vainglorygame.com/docs#teams-coming-soon Vainglory API "Get a collection of Teams"
    def teams(_filter_params = {})
      raise(NotImplementedError, 'Coming soon!')
    end

    # Gets aggregated lifetime information about a single team
    #
    # @raise [NotImplementedError] this endpoint is not yet available
    # @see https://developer.vainglorygame.com/docs#get-a-single-team Vainglory API "Get a single Team"
    def team(_team_id)
      raise(NotImplementedError, 'Coming soon!')
    end

    # Checks to see if a link object exists for a given code
    #
    # @raise [NotImplementedError] this endpoint is not yet available
    # @see https://developer.vainglorygame.com/docs#links-coming-soon Vainglory API "Links"
    def link(_link_id)
      raise(NotImplementedError, 'Coming soon!')
    end

    # Gets current API version and release date
    #
    # @example Get the API's status information
    #   client = VaingloryAPI::Client.new('API_KEY', 'na')
    #   client.status
    # @return [OpenStruct] the response and metadata
    # @see https://developer.vainglorygame.com/docs#versioning Vainglory API "Versioning"
    def status
      get_request_without_headers(endpoint_uri('status'))
    end

    private

    def region_identifier
      @region.short_name
    end

    def get_request_without_headers(uri)
      get_request(uri, false)
    end

    def get_request(uri, with_headers = true, with_auth = true)
      req = Net::HTTP::Get.new(uri)
      req = apply_headers(req, with_auth) if with_headers

      request(uri, req)
    end

    def apply_headers(req, with_auth = true)
      req['Authorization'] = "Bearer #{@api_key}" if with_auth
      req['X-TITLE-ID'] = 'semc-vainglory'
      req['Accept'] = 'application/vnd.api+json'

      req
    end

    def request(uri, req)
      http              = Net::HTTP.new(uri.host, uri.port)
      http.verify_mode  = OpenSSL::SSL::VERIFY_NONE
      http.use_ssl      = true

      response(http.request(req))
    end

    def shard_endpoint_uri(path, filter_params = {})
      endpoint_uri("shards/#{region_identifier}/#{path}", filter_params)
    end

    def endpoint_uri(path, filter_params = {})
      uri = URI(endpoint_url(path))
      uri.query = URI.encode_www_form(filter_params)

      uri
    end

    def endpoint_url(path)
      [BASE_URL, path].join('/')
    end

    def response(response_data)
      response_object = serialize_response_data(response_data.body)
      metadata        = serialize_response_metadata(response_data)

      # Add metadata members to response_object
      metadata.each do |k, v|
        response_object[k] = v
      end

      response_object
    end

    def serialize_response_metadata(response_data)
      response_code = response_data.code.to_i

      {
        code:           response_code,
        success?:       response_code < 400,
        rate_limit:     response_data['X-RateLimit-Limit'].to_i,
        rate_remaining: response_data['X-RateLimit-Remaining'].to_i,
        rate_reset:     response_data['X-RateLimit-Reset'].to_i,
        raw:            response_data
      }
    end

    def serialize_response_data(raw_response_body)
      data = JSON.parse(raw_response_body, object_class: OpenStruct)

      if data.is_a?(Array)
        OpenStruct.new(data: data)
      else
        data
      end
    end
  end
end