Add Cloudflare to Images::Optimizer options (proof of concept) (#19574)

* Add cloudflare image option

* Add tests for cloudflare

* Update app/services/images/optimizer.rb

Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>

* Avoid nested prefixes in cloudflare

* Update spec/services/images/optimizer_spec.rb

Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>

* Update spec/services/images/optimizer_spec.rb

Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>

* Update spec/services/images/optimizer_spec.rb

Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>

* Update app/services/images/optimizer.rb

Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>

* Update spec/services/images/optimizer_spec.rb

Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>

* Rebase

* Refactor optimizer and finalize tests

* Skips scheduled posts in featured post count (#19563)

* Add image title for comment (#19586)

* Add image title for comment

* Break out boolean logic

* Add organizations to onboarding follow suggestions (#19564)

* Prepare: relocate user suggestions

* Prepare: relocate users suggestion service -> query

* Organization query for orgs with above-average scores

* Rubocop

* Limit to last 3 weeks

* Tweak recent scope, limit to 5 orgs

* Onboarding routes are also always JSON

* Divide by zero makes NaN means

* Add Orgs suggester into suggestions

* Rubocop

* select distinct orgs

* Fix for weird edge-case with bad local data

* Include type_identifier in JSON payload

* Update follows API to allow org_ids as input

* Update onboarding front-end to distinguish users/orgs

* Fix: i18n issues

* Fix: type_identifier in json output

* Fix: distinct is weird

* Fix: JS linter

* Continue tweaking front-end

* Audit import order

* Cleanup @todo note

* Try renaming controller action

* Move Article average calculation to postgres and fix math

* Refactor decorated type_identifier

* Refactor SuggestProminent, return more orgs, fix spec math

* Use FeatureFlag for organization suggestions

* This might fix the jest

* Sort organization members and tag moderators by badge count (#19582)

* show tag moderators in descending badge count order

* show organization users in descending badge count order

* specs & docs for find_each_respecting_scope

* Update spec/services/images/optimizer_spec.rb

Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>

* Update spec/services/images/optimizer_spec.rb

Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>

* Update spec/services/images/optimizer_spec.rb

Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>

* Update spec/services/images/optimizer_spec.rb

Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>

* Update spec/services/images/optimizer_spec.rb

Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>

* Update app/services/images/optimizer.rb

Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>

* Remove cloudflare typo

* Adjust cloudflare prefix line in tests

* Clean up test logic

* Update spec/services/images/optimizer_spec.rb

Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>

* Update app/services/images/optimizer.rb

Co-authored-by: Mac Siri <mac@forem.com>

---------

Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
Co-authored-by: Joshua Wehner <joshua@forem.com>
Co-authored-by: Dhurba baral <dhurba87@gmail.com>
Co-authored-by: PJ <pj@forem.com>
Co-authored-by: Mac Siri <mac@forem.com>
This commit is contained in:
Ben Halpern 2023-06-21 09:42:06 -04:00 committed by GitHub
parent a4419a249a
commit f0e99c6279
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
2 changed files with 89 additions and 0 deletions

View file

@ -7,6 +7,8 @@ module Images
imgproxy(img_src, **kwargs)
elsif cloudinary_enabled?
cloudinary(img_src, **kwargs)
elsif cloudflare_enabled?
cloudflare(img_src, **kwargs)
else
img_src
end
@ -23,6 +25,21 @@ module Images
sign_url: true
}.freeze
def self.cloudflare(img_src, **kwargs)
template = Addressable::Template.new("https://{domain}/cdn-cgi/image/{options*}/{src}")
template.expand(
domain: ApplicationConfig["CLOUDFLARE_IMAGES_DOMAIN"],
options: {
width: kwargs[:width],
height: kwargs[:height],
fit: "cover",
gravity: "auto",
format: "auto"
},
src: extract_suffix_url(img_src),
).to_s
end
def self.cloudinary(img_src, **kwargs)
options = DEFAULT_CL_OPTIONS.merge(kwargs).compact_blank
@ -68,6 +85,10 @@ module Images
config.cloud_name.present? && config.api_key.present? && config.api_secret.present?
end
def self.cloudflare_enabled?
ApplicationConfig["CLOUDFLARE_IMAGES_DOMAIN"].present?
end
def self.get_imgproxy_endpoint
if Rails.env.production?
# Use /images with the same domain on Production as
@ -81,5 +102,14 @@ module Images
ApplicationConfig["IMGPROXY_ENDPOINT"] || "http://localhost:8080"
end
end
def self.extract_suffix_url(full_url)
prefix = "https://#{ApplicationConfig['CLOUDFLARE_IMAGES_DOMAIN']}/cdn-cgi/image"
return full_url unless full_url&.starts_with?(prefix)
uri = URI.parse(full_url)
match = uri.path.match(%r{https?.+})
CGI.unescape(match[0]) if match
end
end
end

View file

@ -8,6 +8,7 @@ RSpec.describe Images::Optimizer, type: :service do
describe "#call" do
before do
allow(described_class).to receive(:cloudinary)
allow(described_class).to receive(:cloudflare)
allow(described_class).to receive(:imgproxy)
end
@ -85,6 +86,52 @@ RSpec.describe Images::Optimizer, type: :service do
end
end
describe "#cloudflare" do
before do
allow(ApplicationConfig).to receive(:[]).with("CLOUDFLARE_IMAGES_DOMAIN").and_return("images.example.com")
end
it "generates correct url based on h/w input" do
cloudflare_url = described_class.cloudflare(image_url, width: 821, height: 420)
expect(cloudflare_url).to match(%r{/width=821,height=420,fit=cover,gravity=auto,format=auto/#{CGI.escape(image_url)}})
end
it "does not error if nil" do
cloudflare_url = described_class.cloudflare(nil, width: 821, height: 420)
expect(cloudflare_url).to match(%r{/width=821,height=420,fit=cover,gravity=auto,format=auto/})
end
it "pulls suffix if nested cloudflare url is provided" do
cloudflare_url = described_class.cloudflare(
"https://#{ApplicationConfig['CLOUDFLARE_IMAGES_DOMAIN']}/cdn-cgi/image/width=821,height=900,fit=cover,gravity=auto,format=auto/#{CGI.escape(image_url)}", width: 821, height: 420
)
expect(cloudflare_url).to eq("https://#{ApplicationConfig['CLOUDFLARE_IMAGES_DOMAIN']}/cdn-cgi/image/width=821,height=420,fit=cover,gravity=auto,format=auto/#{CGI.escape(image_url)}")
end
it "does not error out if image is empty" do
cloudflare_url = described_class.cloudflare(
"https://#{ApplicationConfig['CLOUDFLARE_IMAGES_DOMAIN']}/cdn-cgi/image/width=821,height=900,fit=cover,gravity=auto,format=auto/", width: 821, height: 420
)
expect(cloudflare_url).to eq("https://#{ApplicationConfig['CLOUDFLARE_IMAGES_DOMAIN']}/cdn-cgi/image/width=821,height=420,fit=cover,gravity=auto,format=auto/")
end
it "does not error out if image is not proper url and has https" do
image_url = "https:hello"
cloudflare_url = described_class.cloudflare(
"https://#{ApplicationConfig['CLOUDFLARE_IMAGES_DOMAIN']}/cdn-cgi/image/width=821,height=900,fit=cover,gravity=auto,format=auto/#{CGI.escape(image_url)}", width: 821, height: 420
)
expect(cloudflare_url).to eq("https://#{ApplicationConfig['CLOUDFLARE_IMAGES_DOMAIN']}/cdn-cgi/image/width=821,height=420,fit=cover,gravity=auto,format=auto/https%3Ahello")
end
it "does not error out if image is not proper url and does not have https" do
image_url = "hello"
cloudflare_url = described_class.cloudflare(
"https://#{ApplicationConfig['CLOUDFLARE_IMAGES_DOMAIN']}/cdn-cgi/image/width=821,height=900,fit=cover,gravity=auto,format=auto/#{CGI.escape(image_url)}", width: 821, height: 420
)
expect(cloudflare_url).to eq("https://#{ApplicationConfig['CLOUDFLARE_IMAGES_DOMAIN']}/cdn-cgi/image/width=821,height=420,fit=cover,gravity=auto,format=auto/")
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("")
@ -127,6 +174,18 @@ RSpec.describe Images::Optimizer, type: :service do
end
end
describe "#cloudflare_enabled?" do
it "returns false if config missing" do
allow(ApplicationConfig).to receive(:[]).with("CLOUDFLARE_IMAGES_DOMAIN").and_return(nil)
expect(described_class.cloudflare_enabled?).to be(false)
end
it "returns true if config is present" do
allow(ApplicationConfig).to receive(:[]).with("CLOUDFLARE_IMAGES_DOMAIN").and_return("images.com")
expect(described_class.cloudflare_enabled?).to be(true)
end
end
describe "#translate_cloudinary_options" do
it "sets resizing_type to fill if crop: fill is provided" do
options = { width: 100, height: 100, crop: "fill" }