Ruby on Rails trouble with getting JSON from external API - ruby-on-rails-3.1

I can't seem to figure out how to make an API call to a URI like:
http://elophant.com/api/v1/na/getSummonerNames?summonerIds=7428,26670961,12468&key=APIKey
Where summonerIds are the IDs of the players in the game and the key is the API key to access the API.
When I run it in the browser it returns (with the actual API key, of course):
["Phreak","Test","OranGe"]
When I run try to access it in my main controller for my Rails app by doing:
require 'net/http'
class MainController < ApplicationController
def index
end
def player
#player = params[:player]
url = URI.parse('http://elophant.com/api/v1/na/getSummonerNames?summonerIds=' + #player + '&key=APIKey')
req = Net::HTTP::Get.new(url.path)
res = Net::HTTP.start(url.host, url.port) {|http|
http.request(req)
}
#b = res.body
end
end
(Where it says "&key=APIKey", I would replace APIKey with the actual API key.)
then print out the #b in my player.html.erb:
<%= #b %>
it returns:
v1 getSummonerNames
which is the exact same response I get in the browser if I don't provide an API key, but I do provide an API key in the URL in the code.
So my question is, how do I get the browser to use the API key to get a response?
Thank you,
Alex D.

Related

Httparty - appending to url and adding headers to get request via Ruby class

I'm currently working with Httparty to make a GET Seamless.giv API which returns field from a form. In the requests there are Authentication headers that need to be passed in order to access the API. But the request has to be made to a specific form. Thats where the issue lays should this be in the base URI or appended?
Here is the curl example of the request:
curl -X GET -H "AuthDate: 1531236159"\
-H "Authorization: HMAC-SHA256 api_key=XXXXXXXX nonce=12345 Signature=XXXXXXXXXXX"\
-d 'false' https://nycopp.seamlessdocs.com/api/form/:form_id/elements
and this is the approach im currently taking:
class SeamlessGov
include HTTParty
base_uri "https://nycopp.seamlessdocs.com/api"
def initialize(args={})
#api_key = args[:api_key]
#nonce = args[:nonce]
#signature = generate_signature
end
def form(form_id)
#form = form_id
end
def headers(headers={})
#headers = headers
end
def generate_signature
# bash command
end
end
Is the best practice to append it or put it in the base_uri for example:
base_uri "https://nycopp.seamlessdocs.com/api/form/:form_id/elements" or created a method to append to the base_uri for example:
def append_form(form)
"/form/#{form}/elements"
end
What would the best approach be? So that when I call
#form = SeamlessGov.new(params, headers) works.
If I understand what you're asking correctly, you would write a method like:
def create_form
get("/form/#{form_id}/elements", headers)
end
Which you can then call like:
#form = SeamlessGov.new(params, headers).create_form

Ruby HTTP post request authorization in header

I'm trying to write some scripts in Ruby to interface with Guild Wars 2's API (https://api.guildwars2.com/v2)
At the bottom of that page it has this info:
APIs which require authentication need to be passed an API key belonging to
the account to be accessed. The API key must have the appropriate permissions
associated with it (/v2/tokeninfo can be used to inspect key permissions). Keys
can be generated on the ArenaNet account site.
Keys can be passed either via query parameter or HTTP header. Our servers do
not support preflighted CORS requests, so if your application is running
in the user's browser you'll need to user the query parameter.
To pass via query parameter, include "?access_token=" in your request.
To pass via HTTP header, include "Authentication: Bearer (API key)".
The code I'm working with right now is as follows:
class Gw2
attr_reader :response, :uri, :http
def initialize
#uri = URI.parse('https://api.guildwars2.com/v2')
#http = Net::HTTP.new(#uri.host, #uri.port)
#http.use_ssl = true
#http.verify_mode = OpenSSL::SSL::VERIFY_NONE
end
def wallet
path ="/v2/account/wallet"
#response = #http.get(path).body
end
end
I'm not sure how to go about setting that up.
Here is a little example:
require 'net/http'
require 'uri'
url = URI.parse('http://some.url')
req = Net::HTTP::Get.new(url.path)
req.add_field('X-Forwarded-For', '0.0.0.0')
# For content type, you could also use content_type=(type, params={})
# req.set_form_data({'query' => 'search me'})
# req['X-Forwarded-For'] = '0.0.0.0'
res = Net::HTTP.new(url.host, url.port).start do |http|
http.request(req)
end
puts res.body

Ruby/Sinatra - How can I call post in lambda class?

I'm make a little program in sinatra and I'm wanted to perfom some dynamic call of post, with diynamic uri, so I make a Connexion class like this:
class Connexion
def initialize(path)
#path = path
end
def sinatraPost
post "/#{#path}" do
# some code
end
end
end
But when I'm launch sinatraPost, I've got this error:
undefined method `post' for #<Connexion:0x000000026206b8> (NoMethodError)
How can I call the sinatra post method in my class ?
EDIT: Okay ! So, I change my strategy, I have this following code:
class Webhook < Sinatra::Base
get '/:name' do
# compare with names array
end
end
Webhook.run!
Thank's to everyone !
It looks like you're going about this the wrong way. If you want to set up your app to receive a POST request, you'll need routing logic in your controller. Sinatra controllers normally look like this:
require 'sinatra'
get '/route1' do
# do stuff
end
post '/route2' do
# do stuff
end
If you're using a modular app, you'll want to have your app inherit from Sinatra::Base. See the Sinatra docs for more.
Making a post request is different, and doesn't rely on Sinatra methods.
require 'net/http'
uri = URI("http://google.com")
headers = {}
http = Net::HTTP.new(uri.host, uri.port)
request = Net::HTTP::Post.new(uri.request_uri, headers)
response = http.request(request)
Or something like that. Good luck!

How do I convert this example Savon 1 code into Savon 2?

I tried running this code from Ryan Bates's ruby cast but it doesn't work. I think it assumes the use of Savon 1.
require "savon"
client = Savon::Client.new("http://www.webservicex.net/uszip.asmx?WSDL")
response = client.request :web, :get_info_by_zip, body: { "USZip" => zip }
if response.success?
data = response.to_array(:get_info_by_zip_response, :get_info_by_zip_result, :new_data_set, :table).first
if data
#state = data[:state]
#city = data[:city]
#area_code = data[:area_code]
#time_zone = data[:time_zone]
puts #state
puts #city
puts #area_code
end
end
What is the correct implementation for Savon 2? I would like to copy and paste it and have it work.
Here is an excerpt from my Service-Wrapper class providing the SOAP-Services to other ruby objects. I initialize the client by providing the WSDL location and an own method for each SOAP operation. The thirdparty method is invoked with #client.call passing a block which creates a SOAP message with provided params.
class Client
include Singleton
def wsdl_method_to_call
begin
response = #client.call(:wsdl_method_to_call) do
message auth:CREDENTIALS, param_1: param_1_value, param_2: param_2_value
end
rescue
raise CustomServiceException.new("Error ... , response : #{response}")
end
end
def initialize()
#client = Savon::Client.new(wsdl: WSDL)
end
private
WSDL = "http://service_host/wsdl"
CREDENTIALS = "foo|bar"
end
so maybe you should try something like:
response = client.call(:get_info_by_zip) do
message USZip: zip
end

OAuth2 gem: implementation for third party - access other accounts data in seek.com

I'm connecting to an API (seek.com.au) which uses OAuth2 for authentication. I struggled with OAuth2 gem for a while and I ended up writing the plain requests as will follow. Although this is working, I still would like to understand what was wrong with my initial OAuth2 implementation.
Here is my current working code, **the third party* relates to the fact that I'm accessing the API with an account that have access to other accounts. This logic is mainly implemented in the scope method (at the bottom of this snippet).
The following includes some extra logic, but the get_grant and post_for_token methods should include everything.
module Seek::Base
CONFIG = YAML.load_file "#{Rails.root}/config/seek.yml"
HOST = 'http://test.api.seek.com.au/v1/'
REQUEST_URIS = {
get_grant: HOST + 'OAuth/auth',
post_for_token: HOST + 'OAuth/token',
get_applications: HOST + 'advertiser/applications'
}
def uri_for(request, params = {})
uri = REQUEST_URIS[request]
uri += '?' + params.to_param if params.any?
URI.parse uri
end
end
class Seek::OAuth2 # TODO? is instance needed?
include Seek::Base
# by account_id
##tokens = {}
def initialize(account_id)
#account_id = account_id
end
def self.authenticate!(account_id)
new(account_id).authenticate!
end
# eg: when a request responded that the token is expired
def self.expire_token(account_id)
##tokens.delete account_id
end
###########################################################################
############################### begin #####################################
# authentication
# see: http://developer.seek.com.au/docs/partner-api/api-methods/oauth-2.0
def authenticate!
##tokens[#account_id] ||= begin
grant = get_grant
raise Exception.new(#error) if #error
Rails.logger.info "Retrive token for #{#account_id}"
post_for_token
end
end
private
# part of t he authentication process
# as we have one account for many entities, we use third party variation
# see: http://developer.seek.com.au/docs/partner-api/api-methods/oauth2/auth
def get_grant
uri = uri_for :get_grant, {response_type: :code, client_id: username, scope: scope}
response = Net::HTTP.get_response uri
params = response['location'].split('?').second
#error = params.split('error=').second
#grant_code = params.split('code=').second
end
# part of the authentication process
# see: http://developer.seek.com.au/docs/partner-api/api-methods/oauth2/token
def post_for_token
uri = uri_for :post_for_token
request = Net::HTTP::Post.new uri.path, {'Content-Type' => 'application/x-www-form-urlencoded;charset=UTF-8'}
request.set_form grant_type: :authorization_code, code: #grant_code, redirect_uri: ''
request.basic_auth username, password
response = Net::HTTP.new(uri.host, uri.port).request request
JSON(response.body)['access_token']
end
########################## end ############################################
###########################################################################
def username
CONFIG['credentials']['username']
end
def password
CONFIG['credentials']['password']
end
############## the scope method
############## I think I need to insert this in the OAuth request
def scope
"urn:seek:thirdparty:username:#{username},urn:seek:advertiser:identity:#{#account_id}"
end
end
And here are the few lines (to replace the authenticate! method) that were meant to do the same, but sadly, OAuth returns invalid_client.
client = OAuth2::Client.new(username, password, :site => 'http://test.api.seek.com.au/v1')
client.auth_code.authorize_url redirect_uri: ''
token = client.auth_code.get_token 'authorization_code_value',
headers: {'Authorization' => %^Basic #{Base64.encode64 "#{username}:#{password}"}^ }
I think the problem relies in the scope method created by OAuth (see bottom of the first snippet), but I'm not sure and anyways I couldn't find a way to amend it.
I also opened an issue in GitHub, but I think this is covered, just it's not documented (or I can't find it).
Ruby (Rails) implementation
This implementation is not using any wrapper, I tried the gem OAuth2 but I was not able to get the grant code,
I presume because the third party implementation require a customization of the scope which I was not able to set with the gem.
module Api::Base
CONFIG = YAML.load_file "#{Rails.root}/config/api.yml"
HOST = 'https://api.com.au/v1/'
REQUEST_URIS = {
get_grant: HOST + 'OAuth/auth',
post_for_token: HOST + 'OAuth/token',
get_applications: HOST + 'advertiser/applications'
}
def uri_for(request, params = {})
uri = REQUEST_URIS[request]
uri += '?' + params.to_param if params.any?
URI.parse uri
end
end
class Api::OAuth2
include Api::Base
# by account_id
##tokens = {}
def initialize(account_id)
#account_id = account_id
end
def self.authenticate!(account_id)
new(account_id).authenticate!
end
# eg: when a request responded that the token is expired
def self.expire_token(account_id)
##tokens.delete account_id
end
# authentication
def authenticate!
##tokens[#account_id] ||= begin
grant = get_grant
raise StandardError.new(#error) if #error
puts "Retrive token for #{#account_id}"
post_for_token
end
end
private
# part of t he authentication process
# as we have one account for many entities, we use third party variation
def get_grant
uri = uri_for :get_grant, {response_type: :code, client_id: username, scope: scope}
http = Net::HTTP.new uri.host, uri.port
http.use_ssl = uri.port == 443
puts "SSL not set for uri #{uri}" unless http.use_ssl?
response = http.get uri.to_s
raise Exception.new(response.message) unless response.is_a? Net::HTTPFound
params = response['location'].split('?').second
#error = params.split('error=').second
#grant_code = params.split('code=').second
end
# part of the authentication process
def post_for_token
uri = uri_for :post_for_token
request = Net::HTTP::Post.new uri.path, {'Content-Type' => 'application/x-www-form-urlencoded;charset=UTF-8'}
request.set_form grant_type: 'authorization_code', code: #grant_code, redirect_uri: ''
request.basic_auth username, password
http = Net::HTTP.new uri.host, uri.port
http.use_ssl = uri.port == 443
response = http.start {|http| http.request request}
JSON(response.body)['access_token']
end
end
def username
CONFIG['credentials']['username']
end
def password
CONFIG['credentials']['password']
end
def scope
"urn:api:thirdparty:username:#{username},urn:api:advertiser:identity:#{#account_id}"
end
end
I'm still planning to use OAuth 2, I'll post my updates here if any

Resources