Simple POST request failing with em-http-request - ruby

The following query works with requestmaker:
URI:
http://www.cleverbot.com/webservicemin/
Query:
start=y&icognoid=wsf&fno=0&sub=Say&islearning=1&cleanslate=false&stimulus=!!!%20there%20was%20an%20error%20!!!&icognocheck=af71393ce00d9126a247df2f53948e79
But it does not work with em-http-request:
require 'eventmachine'
require 'em-http-request'
uri = 'http://www.cleverbot.com/webservicemin/'
query = 'start=y&icognoid=wsf&fno=0&sub=Say&islearning=1&cleanslate=false&stimulus=!!!%20there%20was%20an%20error%20!!!&icognocheck=af71393ce00d9126a247df2f53948e79'
EM.run do
http = EM::HttpRequest.new(uri).post(query: query)
http.callback { puts http.response; EM.stop }
http.errback { puts 'There was an error'; EM.stop }
end
which prints There was an error. I feel stumped because this simple example works with any other method of sending a request and I've checked around to see if my usage was wrong but it doesn't seem to be.
Edit: Just for reference, this is not the correct way to use cleverbot. I made a second mistake by sending the data under :query. If you use http.post(body: query) it will work

Looks like a badly implemented server: it aborts the TCP connection without returning a proper HTTP status code, which is why you see "connection closed by server" when you query http.error.
If you change the default user agent to curl's UA string, you get a response:
http = EM::HttpRequest.new(uri).post({
:query => query,
:head => {'User-Agent' => 'curl/7.30.0'}
})

Related

`fetch': key not found: "data" (KeyError) : graphql-client error

I was trying to use the graphql query within ruby to fetch github repositories. Before writing the query, I was working on getting the graphql-client working. I'm facing issues with graphql client.
I was following this link for graphql client:
https://github.com/github/graphql-client
require 'graphql'
require 'graphql/client'
require 'graphql/client/http'
module MyGraphQL
HTTP = GraphQL::Client::HTTP.new('https://github.com/graphql') do
def client_context
{ access_token: 'xxxxxxxxxxxxxxxxxx' }
end
def headers(_context)
client_context
end
end
Schema = GraphQL::Client.load_schema(HTTP)
Client = GraphQL::Client.new(schema: Schema, execute: HTTP)
end
I'm getting the following errors in a terminal:
'fetch': key not found: "data" (KeyError)
'load'
'load_schema'
'load_schema'
This error appears due to response you receive via fetching https://github.com/graphql.
You can use binding.pry (this gem), for example, to see what happening, when you try to run load_schema method.
It tries to fetch data from response here: http://i.imgur.com/9T9WRUu.png
But there is no data attribute, because you get {"errors"=>[{"message"=>"422 Unprocessable Entity"}]}
Try to fetch http://graphql-swapi.parseapp.com/, for example, worked for me.
Also faced with same problem. Replacing body method to this helped:
def headers(context)
{
"Authorization" => "bearer #{TOKEN}"
}
end
Where is the TOKEN is the Personal access token gotten from here: https://github.com/settings/tokens

Ruby stubbing with faraday, can't get it to work

Sorry for the title, I'm too frustrated to come up with anything better right now.
I have a class, Judge, which has a method #stats. This stats method is supposed to send a GET request to an api and get some data as response. I'm trying to test this and stub the stats method so that I don't perform an actual request. This is what my test looks like:
describe Judge do
describe '.stats' do
context 'when success' do
subject { Judge.stats }
it 'returns stats' do
allow(Faraday).to receive(:get).and_return('some data')
expect(subject.status).to eq 200
expect(subject).to be_success
end
end
end
end
This is the class I'm testing:
class Judge
def self.stats
Faraday.get "some-domain-dot-com/stats"
end
end
This currently gives me the error: Faraday does not implement: get
So How do you stub this with faraday? I have seen methods like:
stubs = Faraday::Adapter::Test::Stubs.new do |stub|
stub.get('http://stats-api.com') { [200, {}, 'Lorem ipsum'] }
end
But I can't seem to apply it the right way. What am I missing here?
Note that Faraday.new returns an instance of Faraday::Connection, not Faraday. So you can try using
allow_any_instance_of(Faraday::Connection).to receive(:get).and_return("some data")
Note that I don't know if returning "some data" as shown in your question is correct, because Faraday::Connection.get should return a response object, which would include the body and status code instead of a string. You might try something like this:
allow_any_instance_of(Faraday::Connection).to receive(:get).and_return(
double("response", status: 200, body: "some data")
)
Here's a rails console that shows the class you get back from Faraday.new
$ rails c
Loading development environment (Rails 4.1.5)
2.1.2 :001 > fara = Faraday.new
=> #<Faraday::Connection:0x0000010abcdd28 #parallel_manager=nil, #headers={"User-Agent"=>"Faraday v0.9.1"}, #params={}, #options=#<Faraday::RequestOptions (empty)>, #ssl=#<Faraday::SSLOptions (empty)>, #default_parallel_manager=nil, #builder=#<Faraday::RackBuilder:0x0000010abcd990 #handlers=[Faraday::Request::UrlEncoded, Faraday::Adapter::NetHttp]>, #url_prefix=#<URI::HTTP:0x0000010abcd378 URL:http:/>, #proxy=nil>
2.1.2 :002 > fara.class
=> Faraday::Connection
Coming to this late, but incase anyone else is too, this is what worked for me - a combination of the approaches above:
let(:json_data) { File.read Rails.root.join("..", "fixtures", "ror", "501100000267.json") }
before do
allow_any_instance_of(Faraday::Connection).to receive(:get).and_return(
double(Faraday::Response, status: 200, body: json_data, success?: true)
)
end
Faraday the class has no get method, only the instance does. Since you are using this in a class method what you can do is something like this:
class Judge
def self.stats
connection.get "some-domain-dot-com/stats"
end
def self.connection=(val)
#connection = val
end
def self.connection
#connection ||= Faraday.new(some stuff to build up connection)
end
end
Then in your test you can just set up a double:
let(:connection) { double :connection, get: nil }
before do
allow(connection).to receive(:get).with("some-domain-dot-com/stats").and_return('some data')
Judge.connection = connection
end
I ran into the same problem with Faraday::Adapter::Test::Stubs erroring with Faraday does not implement: get. It seems you need to set stubs to a Faraday adapter, like so:
stubs = Faraday::Adapter::Test::Stubs.new do |stub|
stub.get("some-domain-dot-com/stats") { |env| [200, {}, 'egg'] }
end
test = Faraday.new do |builder|
builder.adapter :test, stubs
end
allow(Faraday).to receive(:new).and_return(test)
expect(Judge.stats.body).to eq "egg"
expect(Judge.stats.status).to eq 200
A better way to do this, rather than using allow_any_instance_of, is to set the default connection for Faraday, so that Faraday.get will use the connection you setup in your tests.
For example:
let(:stubs) { Faraday::Adapter::Test::Stubs.new }
let(:conn) { Faraday.new { |b| b.adapter(:test, stubs) } }
before do
stubs.get('/maps/api/place/details/json') do |_env|
[
200,
{ 'Content-Type': 'application/json' },
{ 'result' => { 'photos' => [] } }.to_json
]
end
Faraday.default_connection = conn
end
after do
Faraday.default_connection = nil
end

Post request via Net::HTTP doesn't get a response and times out

I want to use the Microsoft Translate API and I am already stuck obtaining the access token.
This is how my code looks like:
authUri = "https://datamarket.accesscontrol.windows.net/v2/OAuth2-13/"
paramHash = {
"client_id" => "test",
"client_secret" => "*****",
"scope" => "http://api.microsofttranslator.com",
"grant_type" => "client_credentials"
}
postData = Net::HTTP.post_form(URI.parse(authUri), paramHash)
puts postData.body
But this won't terminate and after a while I get a Timeout Error:
`rescue in rbuf_fill': Timeout::Error (Timeout::Error)
I already tried the a Rest-Client gem and curl. Both worked perfectly fine, but I don't like incorporating another gem for such a simple task.
When I send the post request to localhost and listen with netcat I see the exact same requests (except for User-Agent, but even changing that didn't solve the problem)

Goliath + Redis: Testing sequence of requests

I wrote an endpoint that has two operations: PUT and GET. GET retrieves what was PUT, so a good way to ensure both work would be to test them in sequence.
My cURL testing tells me the service works. However, I can't get my test that does the same thing to pass!
it "accepts authenticated PUT data" do
attributes1 = {
user: {
lat: '12.34',
lng: '56.78'
}
}
with_api(Location, { log_stdout: true, verbose: true }) do
put_request({ query: { auth_token: 'abc' }, body: attributes1.to_json }) do |client|
client.response_header.status.must_equal 201
get_request({ query: { auth_token: 'abc' } }) do |client|
client.response_header.status.must_equal 200
client.response.must_match_json_expression([attributes1[:user]])
end
end
end
end
Note that PUT accepts JSON in the body, and GET returns JSON. The service uses Redis as the datastore, and I use mock_redis to mock it.
test_helper.rb:
$redis = MockRedis.new
class Goliath::Server
def load_config(file = nil)
config['redis'] = $redis
end
end
top of spec:
before do
$redis.flushdb
end
The GET should retrieve what was just PUT, but instead it retrieves JSON "null".
Am I doing anything obviously wrong? Perhaps with how I am passing along body data to the request?
It would definitely help to get some better logging when my tests are running. I tried the { log_stdout: true, verbose: true } options, but this still only seems to output basic INFO logging from the Goliath server, which doesn't tell me anything about data and params.
Any help would be appreciated!

Rails pagination in API using Her, Faraday

I've been trying to figure this out all day, and it's driving me crazy.
I have two rails apps, ServerApp and ClientApp. ClientApp gets data from ServerApp through an API, using the Her gem. Everything was great until I needed pagination information.
This is the method I am using to get the orders (this uses kamainari for pagination and ransack for search):
# ServerApp
def search
#search = Order.includes(:documents, :client).order('id desc').search(params[:q])
#orders = #search.result(distinct: true).page(params[:page]).per(params[:per])
respond_with #orders.as_json(include: :documents)
end
It returns an array of hashes in json, which Her uses as a collection of orders. That works fine.
# Response
[
{
"client_id": 239,
"created_at": "2013-05-15T15:37:03-07:00",
"id": 2422,
"ordered_at": "2013-05-15T15:37:03-07:00",
"origin": "online",
"updated_at": "2013-05-15T15:37:03-07:00",
"documents": [
{ ... }
]
},
...
]
But I needed pagination information. It looked like I needed to send it as metadata with my json. So I change my response to this:
respond_to do |format|
format.json do
render json: { orders: #orders.as_json(include: :documents), metadata: 'sent' }
end
end
This does indeed send over metadata, so in my ClientApp I can write #orders.metadata and get 'sent'. But now my orders are nested in an array inside of 'orders', so I need to use #orders.orders, and then it treats it like an array instead of a Her collection.
After doing some reading, it seemed sending pagination info through headers was the way a lot of other people did this (I was able to get the headers set up in an after_filter using this guide). But I am even more lost on how to get those response headers in my ClientApp - I believe I need a Faraday Middleware but I just am having no luck getting this to work.
If anyone knows how I can just get this done, I would be very grateful. I can't take another day of banging my head against the wall on this, but I feel like I am just one vital piece of info away from solving this!
I encountered the same issue and solved it by adding my own middleware and rewriting the "parse" and "on_complete" methods without that much hassle and avoiding the use of global variables.
Here's the code:
class CustomParserMiddleware < Her::Middleware::DefaultParseJSON
def parse(env)
json = parse_json(env[:body])
pagination = parse_json(env[:response_headers][:pagination_key]) || {}
errors = json.delete(:errors) || {}
metadata = json.delete(:metadata) || {}
{
:data => json,
:errors => errors,
:metadata => {
:pagination => pagination,
:additional_metadata => metadata
},
end
def on_complete(env)
env[:body] = case env[:status]
when 204
parse('{}')
else
parse(env)
end
end
end
then, you can access the pagination as follows:
model = Model.all
model.metadata[:pagination]
I finally got this working. The trick was to use a global variable in the faraday on_complete - I tried to find a better solution but this was the best I could do. Once again, I got the header code from here. Here's the full guide to how to get pagination working with Her:
First, on my server side, I have the Kaminari gem, and I pass page and per as params to the server from the client. (This is also using ransack for searching)
def search
#search = Order.order('id desc').search(params[:q])
#orders = #search.result(distinct: true).page(params[:page]).per(params[:per])
respond_with #orders.as_json(include: :items)
end
My client makes the request like so:
#orders = Order.search(q: { client_id_eq: #current_user.id }, page: params[:page], per: 3)`
Back on the server, I have this in my ApiController (app controller for api):
protected
def self.set_pagination_headers(name, options = {})
after_filter(options) do |controller|
results = instance_variable_get("##{name}")
headers["X-Pagination"] = {
total_count: results.total_count,
offset_value: results.offset_value
}.to_json
end
end
In the server orders_controller.rb, I set the pagination headers for the search method:
class OrdersController < ApiController
set_pagination_headers :orders, only: [:search]
...
end
Now to receive the headers we need a Faraday middleware in Her on the client.
# config/initializers/her.rb
Her::API.setup url: Constants.api.url do |c|
c.use TokenAuthentication
c.use HeaderParser # <= This is my middleware for headers
c.use Faraday::Request::UrlEncoded
c.use Her::Middleware::DefaultParseJSON
c.use Faraday::Adapter::NetHttp
c.use Faraday::Response::RaiseError
end
# lib/header_parser.rb
# don't forget to load this file in application.rb with something like:
# config.autoload_paths += Dir[File.join(Rails.root, "lib", "*.rb")].each { |l| require l }
class HeaderParser < Faraday::Response::Middleware
def on_complete(env)
unless env[:response_headers]['x-pagination'].nil?
# Set the global var for pagination
$pagination = JSON.parse(env[:response_headers]['x-pagination'], symbolize_names: true)
end
end
end
Now back in your client controller, you have a global variable of hash called $pagination; mine looks like this:
$pagintation = { total_count: 0, offset_value: 0 }`
Finally, I added Kaminari gem to my client app to paginate the array and get those easy pagination links:
#orders = Kaminari.paginate_array(#orders, total_count: $pagination[:total_count]).page(params[:page]).per(params[:per_page])`
I hope this can help someone else, and if anyone knows a better way to do this, let me know!
You can pass header options to Faraday when setting up the connection, see the docs at http://rubydoc.info/gems/faraday/0.8.7/Faraday/Connection:initialize
Sometimes it helps to do a curl request first, esp. use -vv option for verbose output where you will see all headers. (Maybe you can attach some log outputs from the Server too)
You can use e.g. clogger (http://clogger.rubyforge.org/) do monitor header information on the Rails server side

Resources