'Unpermitted parameter' from react to rails active storage API - ruby-on-rails

I'm trying to store and image to a rails 6 API, but when I try to send the image from the front end it creates a new record but it doesn't attach the image to it, saying my parameter is unpermitted. Here is my code:
BACK-END:
class Avatar < ApplicationRecord
has_one_attached :attachment
end
class AvatarsController < ApplicationController
def create
avatar = Avatar.new(avatar_params)
if avatar.save
render json: avatar, status: :created
else
render json: avatar.errors, status: :unprocessable_entity
end
end
private
def avatar_params
params.require(:avatar).permit(:attachment)
end
end
FRONT-END:
import React from 'react';
import axios from 'axios';
const Avatar = () => {
const handleChange = event => {
const attachment = new FormData();
attachment.append('avatar[attachment]', event.target.files[0]);
axios.post('/api/v1/avatars', { avatar: { attachment } }, { withCredentials: true });
};
return (
<form>
<input type="file" accept="image/*" onChange={handleChange} />
</form>
);
};
export default Avatar;
But when I try to submit it I get this:
Parameters: {"avatar"=>{"attachment"=>{}}}
Unpermitted parameter: :attachment
However the transaction begins and commits successfully, without making the attachment
Here
is a very a very similar question, but that solution didn't work for me either.
I anyone have any idea on how to solve this, I would really appreciate the help.

Related

How to send react object to rails api with a post fetch request? 406 error

I am currently working on a project using react and ruby on rails. My goal right now is to send a post request using fetch to create and store my user in my backend api on submission of my react form. My problem is, the backend isn't receiving my data correctly, resulting in a 406 error. I feel like i've tried everything, i'm going crazy, help.
REACT CODE:
form-
<form onSubmit={handleSubmit}>
<label>Name:</label>
<input type="text" required value={name} onChange={handleNameChange} name="name" placeholder="name"/>
<label>Password:</label>
<input type="password" required value={password} onChange={handlePasswordChange} name="password" placeholder="password"/>
<input type="submit" value="Create Account"/>
</form>
methods -
const [name, setName] = useState("")
const [password, setPassword] = useState("")
const handleNameChange = (e) => {
setName(e.target.value);
}
const handlePasswordChange = (e) => {
setPassword(e.target.value);
}
const handleSubmit = (e) => {
e.preventDefault();
const data = {name, password}
fetch("http://localhost:3000/users", {
method: 'POST',
body: JSON.stringify(data),
headers: {
Content_Type: "application/json",
}
})
RAILS CODE:
users controller-
def create
user = User.create(user_params)
if user.valid?
payload = {user_id: user.id}
token = encode_token(payload)
render json: { user: user, jwt: token }
else
render json: { error: 'failed to create user' }, status: :not_acceptable
end
end
private
def user_params
params.permit(:name, :password)
end
error -
backend error
It looks like user.valid? returns false, so your else statement kicks in:
render json: { error: 'failed to create user' }, status: :not_acceptable
The status: :not_acceptable generates the 406 error.
You should probably include the reason why user is not valid, and return a bad request response instead:
render json: { error: user.errors.full_messages}, status: :bad_request

Value of ActionText body is always " "(empty string)

I am using Rails 6 API and React. I'm trying to build a Rich Text Editor with ActionText. When I send the RTE content from the Trix editor on the front end, it just doesn't set the ActionText body to the body I sent through with Axios.
I am sure that the body has come correctly to the controller because I used byebug and printed out the param value.
For example, it looked like this: <div><!--block-->test</div>
But whenever I try to view what it actually is by running announcement.details.to_s it returns " " for some reason.
I set the details field like this: has_rich_text :details in the Announcement model.
My controller which handles this looks like this:
module V1
class AnnouncementsController < ApplicationController
def create
announcement = Announcement.new(announcement_params)
announcement.author = #current_user
authorize announcement
if announcement.valid? && announcement.save
render json: { message: "Announcement successfully created! You can view it here." }, status: 201
else
render json: { messages: announcement.errors.full_messages }, status: 400
end
end
private
def announcement_params
params.require(:announcement).permit(:title, :details)
end
end
end
If it helps in any way, this is the React code:
const RTE = (props) => {
let trixInput = React.createRef()
useEffect(() => {
trixInput.current.addEventListener("trix-change", event => {
console.log("fired")
props.onChange(event.target.innerHTML)
})
}, [])
return (
<div>
<input
type="hidden"
id="trix"
value={props.value}
/>
<trix-editor
input="trix"
data-direct-upload-url={`${bURL}/rails/active_storage/direct_uploads`}
data-blob-url-template={`${bURL}/rails/active_storage/blobs/:signed_id/*filename`}
ref={trixInput}
className="trix-content"
></trix-editor>
</div>
);
}
And then I just normally pass it with Axios:
axios.post(`${bURL}/v1/announcements/create`, {
"announcement": {
"title": title,
"details": value
}
}, {
headers: {
'Authorization': `token goes here`
}
}).then(res => {
// success
}).catch(err => {
// error
})
If you need any more code snippets or information please comment.

jquery fileupload rails s3 shrine image upload error

I am sort of new to rails and I am trying to upload images directly to S3 with Shrine. I got direct uploads to S3 to work perfectly, however, when I introduced jquery file upload and upload an image, chrome console throws
this error
at me. I'm not sure what I'm doing wrong and I can't seem to find a solution anywhere online. I get that it's a presign error and it's probably not finding the cache link but I don't know how to resolve that.
EDIT: This was solved by including the presign code in the Routes file and altering the storage location in the uploads.js to the correct location. Now, however, I have an issue with the files being rolled back when they attempt to upload.
I'm using the cloud based ide C9,
This is my uploads.js file:
$(document).on("turbolinks:load", function(){
$("[type=file]").fileupload({
add: function(e, data) {
console.log("add", data);
data.progressBar = $('<div class="progress"><div class="determinate"
style="width: 70%"></div></div>').insertBefore("form")
var options = {
extension: data.files[0].name.match(/(\.\w+)?$/)[0], //set the
file extention
_: Date.now() //prevent caching
};
$.getJSON("/autos/upload/cache/presign", options, function(result) {
console.log("presign", result);
data.formData = result['fields'];
data.url = result['url'];
data.paramName = "file";
data.submit()
});
},
progress: function(e, data) {
console.log("progress", data);
var progress = parseInt(data.loaded / data.total * 100, 10);
var percentage = progress.toString() + '%'
data.progressBar.find(".progress-bar").css("width",
percentage).html(percentage);
},
done: function(e, data) {
console.log("done", data);
data.progressBar.remove();
var image = {
id: data.formData.key.match(/cache\/(.+)/)[1], // we have to
remove the prefix part
storage: 'cache',
metadata: {
size: data.files[0].size,
filename: data.files[0].name.match(/[^\/\\]+$/)[0], // IE return full
path
mime_type: data.files[0].type
}
}
form = $(this).closest("form");
form_data = new FormData(form[0]);
form_data.append($(this).attr("name"), JSON.stringify(image))
$.ajax(form.attr("action"), {
contentType: false,
processData: false,
data: form_data,
method: form.attr("method"),
dataType: "json"
}).done(function(data) {
console.log("done from rails", data);
});
}
});
});
My routes.rb file includes:
mount ImageUploader::UploadEndpoint => "/images/upload"
mount Shrine.presign_endpoint(:cache) => "/autos/upload/cache/presign"
I have a model which accepts these images as well as other fields called Autos, this is included in the Autos file:
include ImageUploader[:image]
My Autos Controller is:
class AutosController < ApplicationController
before_action :find_auto, only: [:show, :edit, :update, :destroy]
def index
#autos = Auto.all.order("created_at DESC")
end
def show
end
def new
#auto = current_user.autos.build
end
def create
#auto = current_user.autos.build(auto_params[:auto])
if #auto.save
flash[:notice] = "Successfully created post."
redirect_to autos_path
else
render 'new'
end
end
def edit
end
def update
if #auto.update(auto_params[:auto])
flash[:notice] = "Successfully updated post."
redirect_to auto_path(#auto)
else
render 'edit'
end
end
def destroy
#auto.destroy
redirect_to autos_path
end
private
def auto_params
params.require(:auto).permit(:title, :price, :description, :contact, :image, :remove_image)
end
def find_auto
#auto = Auto.find(params[:id])
end
end
Assuming your image_uploader.rb has the ImageUploader class defined and given that your presign endpoint is something like /autos/upload/cache/presign, your routes.rb should have the presign route defined like so:
mount ImageUploader.presign_endpoint(:cache) => '/autos/upload/cache/presign'
I hope this single change in the route file would make you able to get the presign object that should contain 3 keys: url, fields and headers
# GET /autos/upload/cache/presign
{
"url": "https://my-bucket.s3-eu-west-1.amazonaws.com",
"fields": {
"key": "cache/b7d575850ba61b44c8a9ff889dfdb14d88cdc25f8dd121004c8",
"policy": "eyJleHBpcmF0aW9uIjoiMjAxNS0QwMToxMToyOVoiLCJjb25kaXRpb25zIjpbeyJidWNrZXQiOiJzaHJpbmUtdGVzdGluZyJ9LHsia2V5IjoiYjdkNTc1ODUwYmE2MWI0NGU3Y2M4YTliZmY4OGU5ZGZkYjE2NTQ0ZDk4OGNkYzI1ZjhkZDEyMTAwNGM4In0seyJ4LWFtei1jcmVkZW50aWFsIjoiQUtJQUlKRjU1VE1aWlk0NVVUNlEvMjAxNTEwMjQvZXUtd2VzdC0xL3MzL2F3czRfcmVxdWVzdCJ9LHsieC1hbXotYWxnb3JpdGhtIjoiQVdTNC1ITUFDLVNIQTI1NiJ9LHsieC1hbXotZGF0ZSI6IjIwMTUxMDI0VDAwMTEyOVoifV19",
"x-amz-credential": "AKIAIJF55TMZYT6Q/20151024/eu-west-1/s3/aws4_request",
"x-amz-algorithm": "AWS4-HMAC-SHA256",
"x-amz-date": "20151024T001129Z",
"x-amz-signature": "c1eb634f83f96b69bd675f535b3ff15ae184b102fcba51e4db5f4959b4ae26f4"
},
"headers": {}
}
When upload starts, you will now find this object in developer console instead of the previous 404 not found error.
UPDATE
I think you are very close to the solution. In your create/update actions, use auto_params[:auto] instead of auto_params
You would also like to check the RoR guide on Association Basics for collection methods
I think you following the tutorial of gorails direct upload s3
in you gem file make sure you use the right roda version
gem 'roda', "~> 2.29.0"

Show Associated Model Data with React Rails

I'm using the react-rails gem and have two models: Message and User. User has_many :messages.
In my message.js.jsx, I'd like to show the User of that message. In regular erb, it'd just be <%= message.user.name %>. How would I do this in the message.js.jsx component?
You could rename your component to message.js.jsx.erb and use ERB in it, but it will only be compiled once when Rails starts up.
A more React-ish way to handle is to AJAX load the user data in componentDidMount (or a Store, if using Flux).
message.js.jsx
getInitialState: function() {
return { user: { name: '' } };
},
componentDidMount: function() {
$.getJSON('/users/'+ this.props.id +'.json', function(userData) {
if (this.isMounted()) {
this.setState({ user: userData })
}
});
},
You can create a Rails endpoint to return userData as JSON something like this:
users_controller.rb
def show
#user = User.find(params[:id])
respond_to do |format|
format.html # default html response
format.json { render json: #user.to_json(only: [:id, :name]) }
end
end
See Facebook's page on this for more details
I agree with Unixmonkey that is the react way. You can also do it a few more ways.
#user = JSON.parse user.to_json(include: [:messages], only: [:id, :name])
As well as using componentDidMount to hit a JSON endpoint using jbuilder which you can put a timeout on if you want to update dynamically.
componentDidMount: function() {
$.getJSON('/users/'+ this.props.id +'.json', function(user) {
if (this.isMounted()) {
this.setState({ user: user })
}
});
},
Your show.json.jbuilder under user views would look something like this:
json.id #user.id
json.name #user.name
json.messages #user.messages do |message|
json.id message.id
json.content message.content
json.created_at message.created_at
end

Using Ajax to POST JSON object in rails, and save it to a database?

I've looked through a number of these types of problems, but i can't quite figure it out.I'm really new to ruby on rails. I'm creating an application in rails that administers a printer. Basically what I'm trying to do here is POST a json object using ajax from the frontend which is written in javascript to the backend which I'm writing in rails. Basically what happens is when a user decides to checkout and print something, the json object is sent to the server and a new rails object is created using the json object's data. I want to create a rails object containing the data from the json object.
My ajax, located in the frontend javascript. Note that this code is in a completely seperate application:
$.ajax({
beforeSend: function(xhr) {
toggleModal();
xhr.setRequestHeader("Authorization", "OAuth "+auth_header);
},
url:message.action,
async:true,
type:message.method,
dataType:'json',
contentType: 'application/json',
data: JSON.stringify(userSession),
error: function(data){
toggleModal();
$.mobile.changePage($("#page_thankyou"),{ transition: "slide"});
},
success: function(data){
toggleModal();
console.log(userSession);
$.mobile.changePage($("#page_thankyou"),{ transition: "slide"});
}
})
Here is the userSession Object:
userSession = {
"kiosk_session":{
"session_id":"",
"is_order":"",
"session_items":[]
}
};
In my Kiosk_session controller:
# POST /kiosk_sessions.json
def create
puts YAML::dump params
#kiosk_session = KioskSession.new(params[:kiosk_session])
respond_to do |format|
if #kiosk_session.save
format.html { redirect_to #kiosk_session, notice: 'Kiosk session was successfully created.' }
format.json { render json: #kiosk_session, status: :created, location: #kiosk_session }
else
format.html { render action: "new" }
format.json { render json: #kiosk_session.errors, status: :unprocessable_entity }
end
end
end
and my kiosk_session model:
class KioskSession < ActiveRecord::Base
attr_accessible :id, :is_order, :is_reprint, :reprint_reason, :price,
:paid, :printed, :print_date, :session_items_attributes, :kiosk_id
has_many :session_items
has_one :kiosk_kiosk_session
belongs_to :user
belongs_to :kiosk
accepts_nested_attributes_for :session_items
before_save :before_save_callback
def before_save_callback
new_KSS = KioskKioskSession.new(:kiosk_id => self.kiosk_id, :kiosk_session_id => self.id)
new_KSS.save
self.total_price
end
def total_price
#session_items.to_a.sum {|item| session_items.total_price}
#array to hold subtotals
price = 0.00
#finds each item in session
session_items = SessionItem.where(:kiosk_session_id => self.id)
#creates array of subtotals
session_items.each do |t|
#if t.price
t.set_price
price += t.price
end
self.price = price
end
end
oh and my POST route for creating a kiosk session is /kiosk_session#create
I know this code is a mess, but any help would be greatly appreciated.

Resources