I'm working on a Rails API that's supposed to allow for attachments to digital forms using ActiveStorage. My routes are as follows:
resources :forms, defaults: { format: :json } do
resources :attachments, except: [ :update ]
end
My attachment create
endpoint is as follows:
def create
@form = Form.find(params[:form_id])
if params[:attachment].present?
if @form.update(attachments: params[:form][:attachment])
render json: { message: "Attachment uploaded successfully", file_url: rails_blob_path(@form.attachments.last, only_path: true) }, status: :created
else
render json: form.errors, status: :unprocessable_entity
end
else
render json: { error: "No attachment uploaded" }, status: :unprocessable_entity
end
end
For testing I created a factory definition:
FactoryBot.define do
factory :form do
user_email { "[email protected]" }
status { "started" }
trait :with_attachment do
after (:create) do |form|
form.attachments.attach(
io: Rails.root.join("spec", "files", "upload_test_file.pdf").open,
filename: "upload_test_file.pdf",
content_type: "application/pdf"
)
end
end
end
end
And here's my spec test:
let(:valid_file) { fixture_file_upload(Rails.root.join("spec", "files", "upload_test_file.pdf"), "application/pdf") }
let(:myform) { FactoryBot.create(:form) }
...
describe "POST /create" do
it "creates a new Form Attachment" do
post "/forms/#{myform.id}/attachments", params: { form: { attachment: valid_file } }, headers: valid_headers, as: :json
myform.reload
expect(myform.attachments).to be_attached
expect(response).to have_http_status(:created)
end
end
I suspect it's close but I'm not sure what's causing this error:
1) /forms/:form_id/attachments POST /create creates a new Form Attachment
Failure/Error: if @form.update(attachments: params[:form][:attachment])
ArgumentError:
Could not find or build blob: expected attachable, got #<ActionController::Parameters {"content_type" => "application/pdf", "original_filename" => "upload_test_file.pdf", "tempfile" => "#<File:0x00007f38379ef7c0>"} permitted: false>
All the controller examples I've seen online follow this pattern. I'm also confident I built my model correctly because I'm able to use my Factory pattern to repeatedly build my forms and attachments. And the other request endpoint spec tests run just fine. I suspect there's something trivial I've overlooked.
I'm working on a Rails API that's supposed to allow for attachments to digital forms using ActiveStorage. My routes are as follows:
resources :forms, defaults: { format: :json } do
resources :attachments, except: [ :update ]
end
My attachment create
endpoint is as follows:
def create
@form = Form.find(params[:form_id])
if params[:attachment].present?
if @form.update(attachments: params[:form][:attachment])
render json: { message: "Attachment uploaded successfully", file_url: rails_blob_path(@form.attachments.last, only_path: true) }, status: :created
else
render json: form.errors, status: :unprocessable_entity
end
else
render json: { error: "No attachment uploaded" }, status: :unprocessable_entity
end
end
For testing I created a factory definition:
FactoryBot.define do
factory :form do
user_email { "[email protected]" }
status { "started" }
trait :with_attachment do
after (:create) do |form|
form.attachments.attach(
io: Rails.root.join("spec", "files", "upload_test_file.pdf").open,
filename: "upload_test_file.pdf",
content_type: "application/pdf"
)
end
end
end
end
And here's my spec test:
let(:valid_file) { fixture_file_upload(Rails.root.join("spec", "files", "upload_test_file.pdf"), "application/pdf") }
let(:myform) { FactoryBot.create(:form) }
...
describe "POST /create" do
it "creates a new Form Attachment" do
post "/forms/#{myform.id}/attachments", params: { form: { attachment: valid_file } }, headers: valid_headers, as: :json
myform.reload
expect(myform.attachments).to be_attached
expect(response).to have_http_status(:created)
end
end
I suspect it's close but I'm not sure what's causing this error:
1) /forms/:form_id/attachments POST /create creates a new Form Attachment
Failure/Error: if @form.update(attachments: params[:form][:attachment])
ArgumentError:
Could not find or build blob: expected attachable, got #<ActionController::Parameters {"content_type" => "application/pdf", "original_filename" => "upload_test_file.pdf", "tempfile" => "#<File:0x00007f38379ef7c0>"} permitted: false>
All the controller examples I've seen online follow this pattern. I'm also confident I built my model correctly because I'm able to use my Factory pattern to repeatedly build my forms and attachments. And the other request endpoint spec tests run just fine. I suspect there's something trivial I've overlooked.
Need to transform the parameter into a blob before attaching it. Something like this…
attachment = params[:form][:attachment]
blob = ActiveStorage::Blob.create_and_upload!(
io:attachment,
filename: attachment.original_filename,
content_type: attachment.content_type,
)
if @form.update(attachments: blob)
It turns out if you're going to upload a binary file, you're not supposed to include as: :json
as part of your command or else everything in your request
will turn into text. This is what I ended up using:
post "/forms/#{myform.id}/attachments", params: { attachments: [ file0, file1 ] }, headers: valid_headers