diff --git a/.env_sample b/.env_sample index 5abe2e081..c60b7cc03 100644 --- a/.env_sample +++ b/.env_sample @@ -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) diff --git a/app/services/images/optimizer.rb b/app/services/images/optimizer.rb index 637dba381..c6ea38106 100644 --- a/app/services/images/optimizer.rb +++ b/app/services/images/optimizer.rb @@ -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 diff --git a/config/initializers/cloudinary.rb b/config/initializers/cloudinary.rb index 1ac97196e..e80740594 100644 --- a/config/initializers/cloudinary.rb +++ b/config/initializers/cloudinary.rb @@ -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"] diff --git a/spec/helpers/application_helper_spec.rb b/spec/helpers/application_helper_spec.rb index 81aa2c17a..e21fd2987 100644 --- a/spec/helpers/application_helper_spec.rb +++ b/spec/helpers/application_helper_spec.rb @@ -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", diff --git a/spec/helpers/social_image_helper_spec.rb b/spec/helpers/social_image_helper_spec.rb index 4f823b1dd..7f20ad7ff 100644 --- a/spec/helpers/social_image_helper_spec.rb +++ b/spec/helpers/social_image_helper_spec.rb @@ -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 diff --git a/spec/models/html_variant_spec.rb b/spec/models/html_variant_spec.rb index 00be54dad..fb31dfa1f 100644 --- a/spec/models/html_variant_spec.rb +++ b/spec/models/html_variant_spec.rb @@ -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 = "
" html_variant.update(approved: false, html: html) cloudinary_string = "/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_420/https://devimages.com/image.jpg" diff --git a/spec/models/message_spec.rb b/spec/models/message_spec.rb index 625dfde2f..119e77613 100644 --- a/spec/models/message_spec.rb +++ b/spec/models/message_spec.rb @@ -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! diff --git a/spec/models/podcast_episode_spec.rb b/spec/models/podcast_episode_spec.rb index cb657b273..55539a68d 100644 --- a/spec/models/podcast_episode_spec.rb +++ b/spec/models/podcast_episode_spec.rb @@ -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 = "" diff --git a/spec/services/html/parser_spec.rb b/spec/services/html/parser_spec.rb index 10ba0d191..99919d221 100644 --- a/spec/services/html/parser_spec.rb +++ b/spec/services/html/parser_spec.rb @@ -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 = "" parsed_html = described_class.new(html).prefix_all_images.html expect(parsed_html).to include("https://res.cloudinary.com") diff --git a/spec/services/images/generate_social_image_spec.rb b/spec/services/images/generate_social_image_spec.rb index 111ad6308..66d12129a 100644 --- a/spec/services/images/generate_social_image_spec.rb +++ b/spec/services/images/generate_social_image_spec.rb @@ -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" diff --git a/spec/services/images/optimizer_spec.rb b/spec/services/images/optimizer_spec.rb index 5ac26b9ac..c52b4fee8 100644 --- a/spec/services/images/optimizer_spec.rb +++ b/spec/services/images/optimizer_spec.rb @@ -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 diff --git a/spec/services/images/profile_spec.rb b/spec/services/images/profile_spec.rb index cd68dbf9e..ab73edbfc 100644 --- a/spec/services/images/profile_spec.rb +++ b/spec/services/images/profile_spec.rb @@ -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) diff --git a/spec/services/markdown_processor/parser_spec.rb b/spec/services/markdown_processor/parser_spec.rb index afc11daae..be6fbe315 100644 --- a/spec/services/markdown_processor/parser_spec.rb +++ b/spec/services/markdown_processor/parser_spec.rb @@ -326,7 +326,7 @@ RSpec.describe MarkdownProcessor::Parser, type: :service do expect(generate_and_parse_markdown(markdown_with_img)).to include("