I'm having issues with an API in Rails4/mongoid application. I need to manipulate the data with a python 3 script through an API but I'm getting
NoMethodError (undefined method `permit' for "note_id":String):
error when I try to submit the request.
My python codes looks like this
import requests
import json
url = 'http://0.0.0.0:3000/api/v1/note_proc_logs.json'
payload = {'note_proc_log' : { 'note_id' : '120904'}}
head = {"Authorization":"Token token=xxxxxxxxxxxxxxxxxxx"}
r = requests.post(url, payload, headers=head)
The API controler
module Api
module V1
# This class does not inherit from ApplicationController like the rest to skip Devise authentication
class NoteProcLogsController < ActionController::Base
before_filter :restrict_access if Rails.env.development? == false
respond_to :json
def create
Rails.logger.warn "note_proc_log_params: #{params}" if Rails.env.development?
#note_proc_log = NoteProcLog.new(note_proc_log_params)
respond_to do |format|
if #note_proc_log.save
format.json { render :show, status: :created, location: #note_proc_log }
else
format.json { render json: #note_proc_log.errors, status: :unprocessable_entity }
end
end
end
private
def restrict_access
authenticate_or_request_with_http_token do |token, options|
ApiKey.where(access_token: token).exists?
end
end
# Never trust parameters from the scary internet, only allow the white list through.
def note_proc_log_params
params.require(:note_proc_log).permit(:note_id)
end
end
end
end
I saw few question with the same error but couldn't find a solution to my problem.
Any help would be greatly appreciated.
UPDATE:
Rails.logger.warn "note_proc_log_params: #{params}" if Rails.env.development?
gives me
W, [2016-07-25T15:10:38.362848 #48352] WARN -- : params: {"note_proc_log"=>"note_id", "format"=>"json", "controller"=>"api/v1/note_proc_logs", "action"=>"create"}
The problem was in the python script. A simple python dictionary is ok as payload but nested ones appear not to be.
My final python script look like this
import requests
import json
url = 'http://0.0.0.0:3000/api/v1/note_proc_logs.json'
payload='note_proc_log[chip_id]=120904¬e_proc_log[test_timestamp]=2016-07-19T13:24:49'
head = {"Authorization":"Token token=xxxxxxxxxxxxxxxxxxxx"}
r = requests.post(url=url, data=payload, headers=head)
On Rails side everything will be treated as string so no need for adding additional quotation marks, even for strings with spaces, parent attribute has to be specified for each child attribute and elements separated with &.
This is what works for me, it would be interesting to know if there are other/better ways to do it, in particular how to include an Array of values.
Related
So i have written a module which has a function. And now i am trying to test the function.
My question here is that how do i use mocking or stub etc. to test that for a random userid we grab the request to github from the function and return a custom json back to the function.?
Module being tested:
require 'json'
require 'httparty'
module util
GITHUB_URL = 'http://github.com/'
def get_name(id)
begin
response = HTTParty.get("#{GITHUB_URL}#{id}.json")
data = JSON.parse(response.body)
return data.first['actor_attributes']['name']
rescue Exception => e
return nil
end
end
end
My Rspec file:
# coding: UTF-8
require 'spec_helper'
class DummyClass
end
describe 'GET_NAME' do
before(:each) do
#dummy_class = DummyClass.new
#dummy_class.extend(util)
end
context 'for invalid Github ID' do
it 'return nil' do
expect(#dummy_class.getName('invalid')).to be_nil
end
end
end
Thank you all for your help.
Checkout https://github.com/bblimke/webmock or http://fakeweb.rubyforge.org/
Then you do something like:
stub_request(
:post, "#{GITHUB_URL}1.json"
).to_return(
body: { actor_attributes: {name: "Bill"} }.to_json
)
I'm a bigger fan of Webmock than Fakeweb (it's got a few more features), but they'll both do for you, and it's pretty easy to switch between them.
I followed this guide to get drag and drop file uploads through AJAX: http://dannemanne.com/posts/drag-n-drop_upload_that_works_with_ror_and_paperclip
Everything was working fine on my development environment with WebBrick but if I deploy to PhusionPassenger then I get:
Paperclip::AdapterRegistry::NoHandlerError (No handler found for #<PhusionPassenger::Utils::RewindableInput:0x000000041aef38 #io=#<PhusionPassen...
I'm using this in my controller:
before_filter :parse_raw_upload, :only => :bulk_submissions
def bulk_submissions
...
#submission = Submission.create!(url: "", file: #raw_file, description: "Please edit this description", work_type: "other", date_completed: DateTime.now.to_date)
...
end
private
def parse_raw_upload
if env['HTTP_X_FILE_UPLOAD'] == 'true'
#raw_file = env['rack.input']
#raw_file.class.class_eval { attr_accessor :original_filename, :content_type }
#raw_file.original_filename = env['HTTP_X_FILE_NAME']
#raw_file.content_type = env['HTTP_X_MIME_TYPE']
end
end
Looking at the request itself all the headers are set (X_MIME_TYPE, X_FILE_NAME) etc.
Any ideas?
Thanks in advance!
The example you're cribbing from expects the file stream to be a StringIO object, but Passenger is giving you a PhusionPassenger::Utils::RewindableInput object instead.
Fortunately, a RewindableInput is duckalike to StringIO for this case, so Paperclip's StringioAdapter can be used to wrap your upload stream.
Inside the if block in your parse_raw_upload, at the end, do:
if #raw_file.class.name == 'PhusionPassenger::Utils::RewindableInput'
#raw_file = Paperclip::StringioAdapter.new(#raw_file)
end
I have a before_filter which wants to default the response type depending on various aspects of the request and parameters. E.g.,
request.format = ( params.format ||= 'html' ) if ... # an HTML-only request/client
request.format = ( params.format ||= 'json' ) if request.xhr?
the idea being that the respond_to do |format|; format.html { ... }; format.json { ... } would then render appropriately according to the client conditions. Some of the clients are coming in as type */* (presumably this is request.content_type ?) and I want to force these to be HTML responses. Doesn't seem to be working however. What is a clean way to do this, and without setting a default type for each route ? I.e. I just want to poke the response type into the request so that respond_to will switch on it accordingly.
request.format= should be an object of type MIME::Type.
So you'd have to do something like request.format = MIME::Types.type_for('html').first
I'm trying to get an ajax upload working with rails 3.1.3 and paperclip.
I found this solution to my problem Rails 3 get raw post data and write it to tmp file, but using this, I get an 'encoding undefined conversion error "\xFF" from ASCII-8BIT to UTF-8.
The error occurs at the line #user.photo = #user.photo = QqFile.parse(params[:qqfile], request)
I have not edited the code supplied in the previous answer, but I'll include it here so you don't have to switch back and forth.
the gem list paperclip, returns 2.5.2, 2.4.5, 2.3.8
my controller
def create
#user = User.new(params[:user])
#user.photo = QqFile.parse(params[:qqfile], request)
if #user.save
return render :json => #user
else
return render :json => #user.errors
end
end
qq_file.rb
# encoding: utf-8
require 'digest/sha1'
require 'mime/types'
# Usage (paperclip example)
# #asset.data = QqFile.new(params[:qqfile], request)
class QqFile < ::Tempfile
def initialize(filename, request, tmpdir = Dir::tmpdir)
#original_filename = filename
#request = request
super Digest::SHA1.hexdigest(filename), tmpdir
fetch
end
def self.parse(*args)
return args.first unless args.first.is_a?(String)
new(*args)
end
def fetch
self.write #request.raw_post
self.rewind
self
end
def original_filename
#original_filename
end
def content_type
types = MIME::Types.type_for(#request.content_type)
types.empty? ? #request.content_type : types.first.to_s
end
end
This was an encoding error related to Ruby 1.9.2 (or I believe Ruby 1.9+).
this github post lead to the answer
https://github.com/lassebunk/webcam_app/issues/1
You must specify raw_post.force_encoding("UTF-8") when reading an upload as far as I could tell (I'm not a great programmer).
I have Backbone.js collection and model for a project object:
window.Project = Backbone.Model.extend();
window.Projects = Backbone.Collection.extend({
model: Project,
url: '/projects'
});
I have setup a rails controller to respond to the Backbone.js collection:
class ProjectsController < ApplicationController
def index
render :json => Project.all
end
def create
project = Project.create! params
render :json => project
end
end
Index works fine and I get a list of projects in my web app. The problem is if I try and create a model on the Projects collection I get a 500 error from the server.
The error message on the server is as follows:
Started POST "/projects" for 127.0.0.1 at 2011-08-21 08:27:56 +0100
Processing by ProjectsController#create as JSON
Parameters: {"title"=>"another test"}
Completed 500 Internal Server Error in 16ms
ActiveRecord::UnknownAttributeError (unknown attribute: action):
app/controllers/projects_controller.rb:8:in `create'
I am not sure what the unknown attribute: action is referring to.
For info I have set up the projects_controller as resources :projects. I have also set rails to ActiveRecord::Base.include_root_in_json = false.
Yes, Rails always adds the action and controller to params. The parameters come from ActionDispatch::Http::Parameters:
def parameters
#env["action_dispatch.request.parameters"] ||= begin
params = request_parameters.merge(query_parameters)
params.merge!(path_parameters)
encode_params(params).with_indifferent_access
end
end
And path_parameters:
Returns a hash with the parameters used to form the path of the request. Returned hash keys are strings:
{'action' => 'my_action', 'controller' => 'my_controller'}
So you shouldn't be doing project = Project.create! params. You could go the update_attributes route:
project = Project.new
project.update_attributes params[:model_name]
But this assumes that you have what you need in a sub-hash of params and it won't call your validators. Backbone won't namespace your attributes by default but you could override Backbone.sync and do it yourself. Still, you probably want your validations so update_attributes should generally be avoided.
Your best bet is to pull exactly the attributes out of params that you're expecting to be there. This is even the Backbone recommended practise:
*(In real code, never use update_attributes blindly, and always whitelist the attributes you allow to be changed.)*
You can enable parameter wrapping. Add a file in the initializer directory with:
ActiveSupport.on_load(:action_controller) do
wrap_parameters format: [:json]
end
and, for json request, you post params will now be wrapped with the model name.