Extended imageImport and ImageBuilder to get the content type of the file for the attacment for avoiding issues for files without extensions.

Updated config/locale/en.yml for the active_storage_validations related error messages
This commit is contained in:
Prikesh Savla
2025-12-08 22:08:59 +05:30
parent f4d59305d7
commit 631306cfb3
6 changed files with 71 additions and 26 deletions

View File

@@ -11,7 +11,9 @@ class ImageImporter
image = Spree::Image.create do |img|
PrivateAddressCheck.only_public_connections do
img.attachment.attach(io: valid_url.open, filename:, metadata:)
io = valid_url.open
content_type = Marcel::MimeType.for(io)
img.attachment.attach(io:, filename:, metadata:, content_type:)
end
end
product.image = image if image

View File

@@ -138,26 +138,66 @@ en:
# Used by active_storage_validations
errors:
messages:
content_type_invalid: "has an invalid content type"
file_size_out_of_range: "size %{file_size} is not between required range"
limit_out_of_range: "total number is out of range"
image_metadata_missing: "is not a valid image"
dimension_min_inclusion: "must be greater than or equal to %{width} x %{height} pixel."
dimension_max_inclusion: "must be less than or equal to %{width} x %{height} pixel."
dimension_width_inclusion: "width is not included between %{min} and %{max} pixel."
dimension_height_inclusion: "height is not included between %{min} and %{max} pixel."
dimension_width_greater_than_or_equal_to: "width must be greater than or equal to %{length} pixel."
dimension_height_greater_than_or_equal_to: "height must be greater than or equal to %{length} pixel."
dimension_width_less_than_or_equal_to: "width must be less than or equal to %{length} pixel."
dimension_height_less_than_or_equal_to: "height must be less than or equal to %{length} pixel."
dimension_width_equal_to: "width must be equal to %{length} pixel."
dimension_height_equal_to: "height must be equal to %{length} pixel."
aspect_ratio_not_square: "must be a square image"
aspect_ratio_not_portrait: "must be a portrait image"
aspect_ratio_not_landscape: "must be a landscape image"
aspect_ratio_is_not: "must have an aspect ratio of %{aspect_ratio}"
aspect_ratio_unknown: "has an unknown aspect ratio"
image_not_processable: "is not a valid image"
content_type_invalid:
one: "has an invalid content type (authorized content type is %{authorized_human_content_types})"
other: "has an invalid content type (authorized content types are %{authorized_human_content_types})"
content_type_spoofed:
one: "has a content type that is not equivalent to the one that is detected through its content (authorized content type is %{authorized_human_content_types})"
other: "has a content type that is not equivalent to the one that is detected through its content (authorized content types are %{authorized_human_content_types})"
file_size_not_less_than: "file size must be less than %{max} (current size is %{file_size})"
file_size_not_less_than_or_equal_to: "file size must be less than or equal to %{max} (current size is %{file_size})"
file_size_not_greater_than: "file size must be greater than %{min} (current size is %{file_size})"
file_size_not_greater_than_or_equal_to: "file size must be greater than or equal to %{min} (current size is %{file_size})"
file_size_not_between: "file size must be between %{min} and %{max} (current size is %{file_size})"
file_size_not_equal_to: "file size must be equal to %{exact} (current size is %{file_size})"
total_file_size_not_less_than: "total file size must be less than %{max} (current size is %{total_file_size})"
total_file_size_not_less_than_or_equal_to: "total file size must be less than or equal to %{max} (current size is %{total_file_size})"
total_file_size_not_greater_than: "total file size must be greater than %{min} (current size is %{total_file_size})"
total_file_size_not_greater_than_or_equal_to: "total file size must be greater than or equal to %{min} (current size is %{total_file_size})"
total_file_size_not_between: "total file size must be between %{min} and %{max} (current size is %{total_file_size})"
total_file_size_not_equal_to: "total file size must be equal to %{exact} (current size is %{total_file_size})"
duration_not_less_than: "duration must be less than %{max} (current duration is %{duration})"
duration_not_less_than_or_equal_to: "duration must be less than or equal to %{max} (current duration is %{duration})"
duration_not_greater_than: "duration must be greater than %{min} (current duration is %{duration})"
duration_not_greater_than_or_equal_to: "duration must be greater than or equal to %{min} (current duration is %{duration})"
duration_not_between: "duration must be between %{min} and %{max} (current duration is %{duration})"
duration_not_equal_to: "duration must be equal to %{exact} (current duration is %{duration})"
limit_out_of_range:
zero: "no files attached (must have between %{min} and %{max} files)"
one: "only 1 file attached (must have between %{min} and %{max} files)"
other: "total number of files must be between %{min} and %{max} files (there are %{count} files attached)"
limit_min_not_reached:
zero: "no files attached (must have at least %{min} files)"
one: "only 1 file attached (must have at least %{min} files)"
other: "%{count} files attached (must have at least %{min} files)"
limit_max_exceeded:
zero: "no files attached (maximum is %{max} files)"
one: "too many files attached (maximum is %{max} files, got %{count})"
other: "too many files attached (maximum is %{max} files, got %{count})"
attachment_missing: "is missing its attachment"
media_metadata_missing: "is not a valid media file"
dimension_min_not_included_in: "must be greater than or equal to %{width} x %{height} pixels"
dimension_max_not_included_in: "must be less than or equal to %{width} x %{height} pixels"
dimension_width_not_included_in: "width is not included between %{min} and %{max} pixels"
dimension_height_not_included_in: "height is not included between %{min} and %{max} pixels"
dimension_width_not_greater_than_or_equal_to: "width must be greater than or equal to %{length} pixels"
dimension_height_not_greater_than_or_equal_to: "height must be greater than or equal to %{length} pixels"
dimension_width_not_less_than_or_equal_to: "width must be less than or equal to %{length} pixels"
dimension_height_not_less_than_or_equal_to: "height must be less than or equal to %{length} pixels"
dimension_width_not_equal_to: "width must be equal to %{length} pixels"
dimension_height_not_equal_to: "height must be equal to %{length} pixels"
aspect_ratio_not_square: "must be square (current file is %{width}x%{height}px)"
aspect_ratio_not_portrait: "must be portrait (current file is %{width}x%{height}px)"
aspect_ratio_not_landscape: "must be landscape (current file is %{width}x%{height}px)"
aspect_ratio_not_x_y: "must be %{authorized_aspect_ratios} (current file is %{width}x%{height}px)"
aspect_ratio_invalid: "has an invalid aspect ratio (valid aspect ratios are %{authorized_aspect_ratios})"
file_not_processable: "is not identified as a valid media file"
pages_not_less_than: "page count must be less than %{max} (current page count is %{pages})"
pages_not_less_than_or_equal_to: "page count must be less than or equal to %{max} (current page count is %{pages})"
pages_not_greater_than: "page count must be greater than %{min} (current page count is %{pages})"
pages_not_greater_than_or_equal_to: "page count must be greater than or equal to %{min} (current page count is %{pages})"
pages_not_between: "page count must be between %{min} and %{max} (current page count is %{pages})"
pages_not_equal_to: "page count must be equal to %{exact} (current page count is %{pages})"
not_found:
title: "The page you were looking for doesn't exist (404)"
message_html: "<b>Please try again</b>

View File

@@ -24,7 +24,9 @@ class ImageBuilder < DfcBuilder
Spree::Image.new.tap do |image|
PrivateAddressCheck.only_public_connections do
image.attachment.attach(io: url.open, filename:, metadata:)
io = url.open
content_type = Marcel::MimeType.for(io)
image.attachment.attach(io:, filename:, metadata:, content_type:)
end
end
rescue StandardError

View File

@@ -45,7 +45,8 @@ RSpec.describe Api::V0::ProductImagesController do
expect(response).to have_http_status :unprocessable_entity
expect(product_without_image.image).to be_nil
expect(json_response["id"]).to eq nil
expect(json_response["errors"]).to include "Attachment has an invalid content type"
expect(json_response["errors"]).to include "Attachment is " \
"not identified as a valid media file"
end
end
end

View File

@@ -2,7 +2,7 @@
http_interactions:
- request:
method: get
uri: https://s3.amazonaws.com/ofn_production/eofop2en1y6tu9fr1x9b0wzwgs5r.png
uri: https://s3.amazonaws.com/ofn_production/eofop2en1y6tu9fr1x9b0wzwgs5r
body:
encoding: US-ASCII
string: ''

View File

@@ -3,11 +3,11 @@
require 'spec_helper'
RSpec.describe ImageImporter do
let(:ofn_url) { "https://s3.amazonaws.com/ofn_production/eofop2en1y6tu9fr1x9b0wzwgs5r.png" }
let(:ofn_url) { "https://s3.amazonaws.com/ofn_production/eofop2en1y6tu9fr1x9b0wzwgs5r" }
let(:product) { create(:product) }
describe "#import" do
it "downloads from the Internet", :vcr do
it "downloads from the Internet", :vcr, :aggregate_failures do
expect {
subject.import(ofn_url, product)
}.to change {