I'm fairly new to rails and I'm currently creating my first API only rails app.
What I'm struggling to get my head around is where exactly are entries being stored in the test environment? In a full rails app FactoryGirl would store entries in the _test database and passed to the controller - tested via something like RSpec.
I understand that in a rails API app you're foregoing the use of databases and instead aiming to store data in JSON format on a server - but in the case where I'm writing request specs, where is the information being stored/retrieved from? There is no server (locally or remotely) to hold the data!?
Apologies for my newbie question!
JSON is just the format you use to make the communication from the client and the server, and backward, happen. But data are stored in a database as well. This is a spec I wrote in a rails api project to save a category (it's a request spec).
context 'everything is ok' do
it 'gets a successfull response' do
params = { category: category }.to_json
post "/categories", headers: headers.merge("X-Api-Key" => partner.token), params: params
json = JSON.parse(response.body)
expect(json['success']).to be true
expect(json['category']['name']).to eq 'Programming Languages'
end
end
Just to give you an idea, I'm not pro at testing, I'm learning as well.
and instead aiming to store data in JSON format on a server
Nope. That would be silly. Rails API app is a regular rails app (with a database and whatnot). Only it does waaaay less html view rendering (ERB and such). That's the main difference.
Related
I'm creating a API with Rails 6 and JSON API Resource in order to learn more. I got stuck with requests tests. What I'm doing for now is using the gem json_matchers to test the response of my endpoint, but my real problem is to build the body of requests for: post, put and patch.
Hi, thanks for you attention.
Here is the deal, I started adding to my RSpec files all the json needed for the tests, but these files ended up getting too big. So I thought, how can I makes this to stay more lean?
Then I copy all the json in my tests to json files and importing when needed, for example:
# RSpec Helper
def request_json(json_name)
request_directory = "#{Dir.pwd}/spec/support/api/requests"
request_path = "#{request_directory}/#{json_name}.json"
File.read(request_path)
end
# Example of params for user request
let(:params) { request_json("user") }
I thoutgh it was a great idea, but then I ran into two problems: I need to create a file for every request test and... how I will modify same value of this json file at runtime? For example, I create a object using FactoryBot and now I need to use the ID of this object in my request for a update. How can I do that?
Using regex?
let(:params) do
json = request_json("user")
json.gsub(/\"id\": \"1\"/, "\"id\": \"#{id}\"")
end
Ok... It works, but I don't like it! I think that this can turn to be a real mess. Another options was to convert this to hash and then json again... Nope, nope, nope, don't like it either.
Now I'm trying to create something more dynamic using FactoryBot and Faker. For now, with the code below, I can pass a factory name and receive a perfect json body for a post.
def serializer_for(resource)
{
"data": {
"type": resource.to_s.tableize,
"attributes": create_attributes_for(resource),
}
}.to_json
end
def create_attributes_for(resource)
attributes = attributes_for(resource)
attributes.reduce({}) do |hash, element|
hash.update(standardize_json_key() => element.last.to_s)
end
end
def standardize_json_key(symbol)
symbol.to_s.gsub("_", "-")
end
But then I began to think about the challenges of this approach:
Post/Put/Patch with relationships?(has_one or has_many)
How I'll add the ID field for put/path?
Add how I'll add the upload files when needed?
So... returning to my question: How can I prepare the json body for the request using factory bot (using even traits) with all the concerns above?
If you have/know a better answer for this problem, share with, please. If not, I'll try to create a gem for this.
Appreciate your time, thanks!
I believe that to have a Shopify webhook integrate with a Rails app, the Rails app needs to disable the default verify_authenticity_token method, and implement its own authentication using the X_SHOPIFY_HMAC_SHA256 header. The Shopify docs say to just use request.body.read. So, I did that:
def create
verify_webhook(request)
# Send back a 200 OK response
head :ok
end
def verify_webhook(request)
header_hmac = request.headers["HTTP_X_SHOPIFY_HMAC_SHA256"]
digest = OpenSSL::Digest.new("sha256")
request.body.rewind
calculated_hmac = Base64.encode64(OpenSSL::HMAC.digest(digest, SHARED_SECRET, request.body.read)).strip
puts "header hmac: #{header_hmac}"
puts "calculated hmac: #{calculated_hmac}"
puts "Verified:#{ActiveSupport::SecurityUtils.secure_compare(calculated_hmac, header_hmac)}"
end
The Shopify webhook is directed to the correct URL and the route gives it to the controller method shown above. But when I send a test notification, the output is not right. The two HMACs are not equal, and so it is not verified. I am fairly sure that the problem is that Shopify is using the entire request as their seed for the authentication hash, not just the POST contents. So, I need the original, untouched HTTP request, unless I am mistaken.
This question seemed like the only promising thing on the Internet after at least an hour of searching. It was exactly what I was asking and it had an accepted answer with 30 upvotes. But his answer... is absurd. It spits out an unintelligible, garbled mess of all kinds of things. Am I missing something glaring?
Furthermore, this article seemed to suggest that what I am looking for is not possible. It seems that Rails is never given the unadulterated request, but it is split into disparate parts by Rack, before it ever gets to Rails. If so, I guess I could maybe attempt to reassemble it, but I would have to even get the order of the headers correct for a hash to work, so I can't imagine that would be possible.
I guess my main question is, am I totally screwed?
The problem was in my SHARED_SECRET. I assumed this was the API secret key, because a few days ago it was called the shared secret in the Shopify admin page. But now I see a tiny paragraph at the bottom of the notifications page that says,
All your webhooks will be signed with ---MY_REAL_SHARED_SECRET--- so
you can verify their integrity.
This is the secret I need to use to verify the webhooks. Why there are two of them, I have no idea.
Have you tried doing it in the order they show in their guides? They have a working sample for ruby.
def create
request.body.rewind
data = request.body.read
header = request.headers["HTTP_X_SHOPIFY_HMAC_SHA256"]
verified = verify_webhook(data, header)
head :ok
end
They say in their guides:
Each Webhook request includes a X-Shopify-Hmac-SHA256 header which is
generated using the app's shared secret, along with the data sent in
the request.
the keywors being "generated using shared secret AND DATA sent in the request" so all of this should be available on your end, both the DATA and the shared secret.
I'm a newb hobbyist developer. Can I just throw this repo of their ACRCloud's ruby example code into a controller? I'd like to use an audio fingerprinting song recognition database as a name validation for songs users are uploading using paperclip. Not sure if it's possible, just starting to research it, any hints or suggestions would be appreciated.
Obviously I'd have to replace
file_name = ARGV[0]
etc, but I'm also wondering about the require 'openssl' etc
Definitely! But there are few points to be taken care of. That's a pure ruby script, when it comes to rails there are certain rules/best practices. One of which is thin controller and fat model..
You need to create a route/action in your app which will ask the app to execute this request with required params.
Write a method in your model which contains the code and call it from controller and pass the permitted params to it.
Instead of hardcoding your credentials in the model, make them environment variables.
Would suggest using Httparty gem wgich will reduce many lines of your code and you just need to pass headers, params, etc. as hash in the arguments.
Last, but not the least...if you notice..there's a puts in the end however, rails uses mvc and so you need to have a view for the controller action you created in step1. Return and save the response.body in the class variable like #response = res.body and you can play with the body depending on the response type.
Hope it helps..
P.S. I wish I could write few lines of code/optimise it for you but i m using my mobile right now. But I think this much information should be enough to convert that script to mvc rails structure..
I am working on an app and up to this point I have been testing just stuff like authentication and request response codes. but it seems like a good idea to test the structure of the payload. ie. if there is embedded resources or sidloaded resources. how do you guys test this. here is a sample of some of the testing I am doing. I am using active model serializers. but seems like a bit of cruft to organize.
describe '#index' do
it 'should return an array of email templates' do
template = EmailTemplate.new(name: 'new email template')
EmailTemplate.stub(:all).and_return([template])
get :index
payload = {:email_templates => [JSON.parse(response.body)["email_templates"][0].symbolize_keys]}
template_as_json_payload = {:email_templates => [ActiveModel::SerializableResource.new(template).as_json[:email_template] ]}
expect(payload).to eq(template_as_json_payload)
end
end
I'm fond of defining a schema for JSON responses and validating that any response seen in a test conforms to it. That alone does not guarantee that the values of the response are correct but it does tell you that the structure of the response matched your expectations.
Those schemas then become part of the documentation of the API which client implementations can reference. By using the schema in tests I get more confidence that the API documentation is not going to fall out of sync with the implementation. Making schema changes in order to get a passing test is also a good prompt for me to consider if a change is safe for existing API clients or if I need to release a new API version.
The folks at Thoughtbot have a nice example of validating schemas using rspec: https://robots.thoughtbot.com/validating-json-schemas-with-an-rspec-matcher
This is one way to do it:
body = JSON.parse(response.body)
assert_equal(body.keys, ["id", "author"])
assert_equal(body["author"].keys, ["id", "name"])
But you should checkout the link that Jonah shared, it's worth reading.
The different Rails 3/Ajax solutions I have come across show examples that retrieve data from a database.
I would like to make an Ajax call and have the server return data that does not come from a database. The data actually comes from REST aware pages on the web.
Any suggestions or examples?
See this example:
# Use the class methods to get down to business quickly
response = HTTParty.get('http://twitter.com/statuses/public_timeline.json')
puts response.body, response.code, response.message, response.headers.inspect
With HTTParty gem.
You will return results as usual with Rails, for example, you could call in your controller:
render json: response.body
If you want it AJAX you can call this controller I mention with:
$.get('my_rails_route');
Check your requirements, you could simply do:
$.get('http://twitter.com/statuses/public_timeline.json');
Without passing by Rails server.