[15 min fix] Fallback to the image URL if Cloudinary/Imgproxy are not configured (#13767)

* Fallback to the image URL if neither Cloudinary nor Imgproxy are configured

* Fix specs

* Cloudinary api_secret is required

* Update spec/models/message_spec.rb

Co-authored-by: Michael Kohl <citizen428@dev.to>

Co-authored-by: Michael Kohl <citizen428@dev.to>
This commit is contained in:
rhymes 2021-05-19 15:03:20 +02:00 committed by GitHub
parent f39b76b865
commit a798bd2f7d
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
16 changed files with 95 additions and 27 deletions

View file

@ -96,10 +96,10 @@ BUFFER_LISTINGS_PROFILE="Optional"
# Cloudinary for image resizing and cache??
# (https://cloudinary.com/documentation/rails_integration)
CLOUDINARY_API_KEY="Optional"
CLOUDINARY_API_SECRET="Optional"
CLOUDINARY_CLOUD_NAME="Optional"
CLOUDINARY_SECURE="Optional"
CLOUDINARY_API_KEY=
CLOUDINARY_API_SECRET=
CLOUDINARY_CLOUD_NAME=
CLOUDINARY_SECURE=
# HTML/CSS to Image for generating social preview images
# (https://docs.htmlcsstoimage.com/example-code/ruby)

View file

@ -5,8 +5,10 @@ module Images
if imgproxy_enabled?
imgproxy(img_src, **kwargs)
else
elsif cloudinary_enabled?
cloudinary(img_src, **kwargs)
else
img_src
end
end
@ -59,6 +61,12 @@ module Images
Imgproxy.config.key.present? && Imgproxy.config.salt.present?
end
def self.cloudinary_enabled?
config = Cloudinary.config
config.cloud_name.present? && config.api_key.present? && config.api_secret.present?
end
def self.get_imgproxy_endpoint
if Rails.env.production?
# Use /images with the same domain on Production as

View file

@ -1,5 +1,5 @@
Cloudinary.config do |config|
config.cloud_name = Rails.env.test? ? "TEST-CLOUD" : ApplicationConfig["CLOUDINARY_CLOUD_NAME"]
config.cloud_name = ApplicationConfig["CLOUDINARY_CLOUD_NAME"]
config.api_key = ApplicationConfig["CLOUDINARY_API_KEY"]
config.api_secret = ApplicationConfig["CLOUDINARY_API_SECRET"]
config.secure = ApplicationConfig["CLOUDINARY_SECURE"]

View file

@ -218,7 +218,7 @@ RSpec.describe ApplicationHelper, type: :helper do
end
end
describe "#cloudinary" do
describe "#cloudinary", cloudinary: true do
it "returns cloudinary-manipulated link" do
image = helper.optimized_image_url(Faker::Placeholdit.image)
expect(image).to start_with("https://res.cloudinary.com")
@ -240,7 +240,7 @@ RSpec.describe ApplicationHelper, type: :helper do
end
describe "#optimized_image_tag" do
it "works just like cl_image_tag" do
it "works just like cl_image_tag", cloudinary: true do
image_url = "https://i.imgur.com/fKYKgo4.png"
cloudinary_image_tag = cl_image_tag(image_url,
type: "fetch", crop: "imagga_scale",

View file

@ -42,7 +42,7 @@ describe SocialImageHelper do
expect(url).to eq article_social_preview_url(article, format: :png, host: "hello.com")
end
it "returns the main image if set" do
it "returns the main image if set", cloudinary: true do
article.main_image = Faker::CryptoCoin.url_logo
url = helper.article_social_image_url(article)
@ -66,7 +66,7 @@ describe SocialImageHelper do
expect(url).to eq article_social_preview_url(article, format: :png, host: "hello.com")
end
it "returns correct manipulation of cloudinary links" do
it "returns correct manipulation of cloudinary links", cloudinary: true do
article.update_column(
:main_image,
"https://res.cloudinary.com/practicaldev/image/fetch/s--A-gun7rr--/c_imagga_scale,f_auto,fl_progressive,h_420,q_auto,w_1000/https://res.cloudinary.com/practicaldev/image/fetch/s--hcD8ZkbP--/c_imagga_scale%2Cf_auto%2Cfl_progressive%2Ch_420%2Cq_auto%2Cw_1000/https://dev-to-uploads.s3.amazonaws.com/i/th93d625o27nuz63oeen.png", # rubocop:disable Layout/LineLength

View file

@ -49,7 +49,7 @@ RSpec.describe HtmlVariant, type: :model do
expect(described_class.find_for_test(["hello"]).id).to eq(html_variant.id)
end
it "prefixes an image with cloudinary" do
it "prefixes an image with cloudinary", cloudinary: true do
html = "<div><img src='https://devimages.com/image.jpg' /></div>"
html_variant.update(approved: false, html: html)
cloudinary_string = "/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_420/https://devimages.com/image.jpg"

View file

@ -43,6 +43,18 @@ RSpec.describe Message, type: :model do
describe "#message_html" do
it "creates rich link with proper link for article" do
link = URL.article(article)
message.message_markdown = "hello #{link}"
message.validate!
expect(message.message_html).to include(
article.title,
"sidecar-article",
link,
)
end
it "creates rich link with proper link for article when Cloudinary is enabled", cloudinary: true do
message.message_markdown = "hello http://#{ApplicationConfig['APP_DOMAIN']}#{article.path}"
message.validate!

View file

@ -96,7 +96,7 @@ RSpec.describe PodcastEpisode, type: :model do
end
end
describe "Cloudinary configuration and processing" do
describe "Cloudinary configuration and processing", cloudinary: true do
it "prefixes an image URL with a path" do
image_url = "https://dummyimage.com/10x10"
podcast_episode.body = "<img src=\"#{image_url}\">"

View file

@ -65,7 +65,7 @@ RSpec.describe Html::Parser, type: :service do
end
context "when an image is used" do
it "wraps the image with Cloudinary" do
it "wraps the image with Cloudinary", cloudinary: true do
html = "<img src='https://image.com/image.jpg'>"
parsed_html = described_class.new(html).prefix_all_images.html
expect(parsed_html).to include("https://res.cloudinary.com")

View file

@ -15,7 +15,7 @@ RSpec.describe Images::GenerateSocialImage, type: :labor do
expect(described_class.call(article)).to eq(article.main_image)
end
it "returns article social image" do
it "returns article social image", cloudinary: true do
article.main_image = nil
article.social_image = nil
article.cached_tag_list = "discuss, hello, goodbye"

View file

@ -13,29 +13,46 @@ RSpec.describe Images::Optimizer, type: :service do
it "does nothing when given a relative url" do
relative_asset_path = "/assets/something.jpg"
expect(described_class.call(relative_asset_path)).to eq relative_asset_path
expect(described_class.call(relative_asset_path)).to eq(relative_asset_path)
end
it "does nothing when given nil" do
expect(described_class.call(nil)).to eq nil
expect(described_class.call(nil)).to be(nil)
end
it "returns the image if neither cloudinary nor imgproxy are enabled", :aggregate_failures do
allow(described_class).to receive(:cloudinary_enabled?).and_return(false)
allow(described_class).to receive(:imgproxy_enabled?).and_return(false)
expect(described_class.call(image_url)).to eq(image_url)
expect(described_class).not_to have_received(:cloudinary)
expect(described_class).not_to have_received(:imgproxy)
end
it "calls cloudinary if imgproxy is not enabled" do
allow(described_class).to receive(:cloudinary_enabled?).and_return(true)
allow(described_class).to receive(:imgproxy_enabled?).and_return(false)
described_class.call(image_url)
expect(described_class).to have_received(:cloudinary)
expect(described_class).not_to have_received(:imgproxy)
end
it "calls imgproxy if imgproxy is enabled" do
allow(described_class).to receive(:cloudinary_enabled?).and_return(true)
allow(described_class).to receive(:imgproxy_enabled?).and_return(true)
described_class.call(image_url)
expect(described_class).not_to have_received(:cloudinary)
expect(described_class).to have_received(:imgproxy)
end
end
describe "#cloudinary" do
describe "#cloudinary", cloudinary: true do
it "performs exactly like cl_image_path" do
allow(described_class).to receive(:imgproxy_enabled?).and_return(false)
cloudinary_url = cl_image_path(image_url,
type: "fetch",
width: 50, height: 50,
@ -48,7 +65,6 @@ RSpec.describe Images::Optimizer, type: :service do
end
it "generates correct url by relying on DEFAULT_CL_OPTIONS" do
allow(described_class).to receive(:imgproxy_enabled?).and_return(false)
cloudinary_url = cl_image_path(image_url,
type: "fetch",
quality: "auto",
@ -67,10 +83,34 @@ RSpec.describe Images::Optimizer, type: :service do
end
end
describe "#cloudinary_enabled?" do
it "returns false if cloud_name, api_key or api_secret are missing", :aggregate_failures do
allow(Cloudinary.config).to receive(:cloud_name).and_return("")
expect(described_class.cloudinary_enabled?).to be(false)
allow(Cloudinary.config).to receive(:cloud_name).and_return("cloud name")
allow(Cloudinary.config).to receive(:api_key).and_return("")
expect(described_class.cloudinary_enabled?).to be(false)
allow(Cloudinary.config).to receive(:cloud_name).and_return("cloud name")
allow(Cloudinary.config).to receive(:api_key).and_return("api key")
allow(Cloudinary.config).to receive(:api_secret).and_return("")
expect(described_class.cloudinary_enabled?).to be(false)
end
it "returns true if cloud_name and api_key and api_secret are provided" do
allow(Cloudinary.config).to receive(:cloud_name).and_return("cloud name")
allow(Cloudinary.config).to receive(:api_key).and_return("api key")
allow(Cloudinary.config).to receive(:api_secret).and_return("api secret")
expect(described_class.cloudinary_enabled?).to be(true)
end
end
describe "#imgproxy_enabled?" do
it "returns false if key and salt are missing" do
allow(Imgproxy).to receive(:config).and_return(Imgproxy::Config.new)
expect(described_class.imgproxy_enabled?).to eq(false)
expect(described_class.imgproxy_enabled?).to be(false)
end
it "returns true if key and salt are provided" do
@ -81,7 +121,7 @@ RSpec.describe Images::Optimizer, type: :service do
end
allow(Imgproxy).to receive(:config).and_return(imgproxy_config_stub)
expect(described_class.imgproxy_enabled?).to eq(true)
expect(described_class.imgproxy_enabled?).to be(true)
end
end

View file

@ -8,7 +8,7 @@ RSpec.describe Images::Profile, type: :services do
end
context "when user has no profile_image" do
it "returns backup image prefixed with Cloudinary" do
it "returns backup image prefixed with Cloudinary", cloudinary: true do
user = build_stubbed(:user, profile_image: nil)
correct_prefix = "/c_fill,f_auto,fl_progressive,h_120,q_auto,w_120/"
expect(described_class.call(user.profile_image_url)).to include(correct_prefix + described_class::BACKUP_LINK)

View file

@ -326,7 +326,7 @@ RSpec.describe MarkdownProcessor::Parser, type: :service do
expect(generate_and_parse_markdown(markdown_with_img)).to include("<a")
end
it "wraps the image with Cloudinary" do
it "wraps the image with Cloudinary", cloudinary: true do
expect(generate_and_parse_markdown(markdown_with_img))
.to include("https://res.cloudinary.com")
end

View file

@ -0,0 +1,8 @@
RSpec.configure do |config|
config.before(:each, cloudinary: true) do |_example|
allow(Cloudinary.config).to receive(:cloud_name).and_return("CLOUD_NAME")
allow(Cloudinary.config).to receive(:api_key).and_return("API_KEY")
allow(Cloudinary.config).to receive(:api_secret).and_return("API_SECRET")
allow(Cloudinary.config).to receive(:secure).and_return("SECURE")
end
end

View file

@ -43,7 +43,7 @@ RSpec.describe "Using the editor", type: :system do
page.evaluate_script("window.onbeforeunload = function(){}")
end
it "fills out form with rich content and click preview" do
it "fills out form with rich content and click preview", cloudinary: true do
article_body = find("div.crayons-article__body")["innerHTML"]
article_body.gsub!(%r{"https://res\.cloudinary\.com/.{1,}"}, "cloudinary_link")
@ -58,7 +58,7 @@ RSpec.describe "Using the editor", type: :system do
end
describe "Submitting an article", js: true do
it "fill out form and submit" do
it "fill out form and submit", cloudinary: true do
fill_markdown_with(read_from_file(raw_text))
find("button", text: /\ASave changes\z/).click
article_body = find(:xpath, "//div[@id='article-body']")["innerHTML"]

View file

@ -1,8 +1,8 @@
require "rails_helper"
RSpec.describe CloudCoverUrl, type: :view_object do
RSpec.describe CloudCoverUrl, type: :view_object, cloudinary: true do
let(:article) { create(:article) }
let(:cloudinary_prefix) { "https://res.cloudinary.com/TEST-CLOUD/image/fetch/" }
let(:cloudinary_prefix) { "https://res.cloudinary.com/#{Cloudinary.config.cloud_name}/image/fetch/" }
it "returns proper url" do
expect(described_class.new(article.main_image).call)