def process_url
@card ||= PreviewCard.new(url: @url)
+ attempt_oembed || attempt_opengraph
+ end
+
+ def html
+ return @html if defined?(@html)
+
Request.new(:get, @url).perform do |res|
if res.code == 200 && res.mime_type == 'text/html'
@html = res.body_with_limit
@html_charset = nil
end
end
-
- return if @html.nil?
-
- attempt_oembed || attempt_opengraph
end
def attach_card
end
def attempt_oembed
- service = FetchOEmbedService.new
- embed = service.call(@url, html: @html)
- url = Addressable::URI.parse(service.endpoint_url)
+ service = FetchOEmbedService.new
+ url_domain = Addressable::URI.parse(@url).normalized_host
+ cached_endpoint = Rails.cache.read("oembed_endpoint:#{url_domain}")
+
+ embed = service.call(@url, cached_endpoint: cached_endpoint) unless cached_endpoint.nil?
+ embed ||= service.call(@url, html: html) unless html.nil?
return false if embed.nil?
+ url = Addressable::URI.parse(service.endpoint_url)
+
@card.type = embed[:type]
@card.title = embed[:title] || ''
@card.author_name = embed[:author_name] || ''
end
def attempt_opengraph
+ return if html.nil?
+
detector = CharlockHolmes::EncodingDetector.new
detector.strip_tags = true
# frozen_string_literal: true
class FetchOEmbedService
+ ENDPOINT_CACHE_EXPIRES_IN = 24.hours.freeze
+
attr_reader :url, :options, :format, :endpoint_url
def call(url, options = {})
@url = url
@options = options
- discover_endpoint!
+ if @options[:cached_endpoint]
+ parse_cached_endpoint!
+ else
+ discover_endpoint!
+ end
+
fetch!
end
return if @endpoint_url.blank?
@endpoint_url = (Addressable::URI.parse(@url) + @endpoint_url).to_s
+
+ cache_endpoint!
rescue Addressable::URI::InvalidURIError
@endpoint_url = nil
end
+ def parse_cached_endpoint!
+ cached = @options[:cached_endpoint]
+
+ return if cached[:endpoint].nil? || cached[:format].nil?
+
+ @endpoint_url = Addressable::Template.new(cached[:endpoint]).expand(url: @url).to_s
+ @format = cached[:format]
+ end
+
+ def cache_endpoint!
+ url_domain = Addressable::URI.parse(@url).normalized_host
+
+ endpoint_hash = {
+ endpoint: @endpoint_url.gsub(URI.encode_www_form_component(@url), '{url}'),
+ format: @format,
+ }
+
+ Rails.cache.write("oembed_endpoint:#{url_domain}", endpoint_hash, expires_in: ENDPOINT_CACHE_EXPIRES_IN)
+ end
+
def fetch!
return if @endpoint_url.blank?
end
+ context 'when endpoint is cached' do
+ before do
+ stub_request(:get, 'http://www.youtube.com/oembed?format=json&url=https://www.youtube.com/watch?v=dqwpQarrDwk').to_return(
+ status: 200,
+ headers: { 'Content-Type': 'text/html' },
+ body: request_fixture('oembed_json_empty.html')
+ )
+ end
+
+ it 'returns new provider without fetching original URL first' do
+ subject.call('https://www.youtube.com/watch?v=dqwpQarrDwk', cached_endpoint: { endpoint: 'http://www.youtube.com/oembed?format=json&url={url}', format: :json })
+ expect(a_request(:get, 'https://www.youtube.com/watch?v=dqwpQarrDwk')).to_not have_been_made
+ expect(subject.endpoint_url).to eq 'http://www.youtube.com/oembed?format=json&url=https%3A%2F%2Fwww.youtube.com%2Fwatch%3Fv%3DdqwpQarrDwk'
+ expect(subject.format).to eq :json
+ expect(a_request(:get, 'http://www.youtube.com/oembed?format=json&url=https%3A%2F%2Fwww.youtube.com%2Fwatch%3Fv%3DdqwpQarrDwk')).to have_been_made
+ end
+ end
+
context 'when status code is not 200' do
before do
stub_request(:get, 'https://host.test/oembed.html').to_return(