Make DataOutputAgent serve RSS output as application/rss+xml

For backward compatibility, a new option `rss_content_type` is added
and existing agents will have its value set to `text/xml`, which was
the Content-Type value before this change.

This fixes #1968.
This commit is contained in:
Akinori MUSHA 2017-04-19 16:44:55 +09:00
parent 37a70f62a7
commit 722349c5cd
3 changed files with 48 additions and 12 deletions

View file

@ -27,6 +27,7 @@ module Agents
* `ttl` - A value for the \\<ttl\\> element in RSS output. (default: `60`)
* `ns_media` - Add [yahoo media namespace](https://en.wikipedia.org/wiki/Media_RSS) in output xml
* `ns_itunes` - Add [itunes compatible namespace](http://lists.apple.com/archives/syndication-dev/2005/Nov/msg00002.html) in output xml
* `rss_content_type` - Content-Type for RSS output (default: `application/rss+xml`)
* `push_hubs` - Set to a list of PubSubHubbub endpoints you want to publish an update to every time this agent receives an event. (default: none) Popular hubs include [Superfeedr](https://pubsubhubbub.superfeedr.com/) and [Google](https://pubsubhubbub.appspot.com/). Note that publishing updates will make your feed URL known to the public, so if you want to keep it secret, set up a reverse proxy to serve your feed via a safe URL and specify it in `template.self`.
If you'd like to output RSS tags with attributes, such as `enclosure`, use something like the following in your `template`:
@ -165,6 +166,10 @@ module Agents
interpolated['template']['description'].presence || "A feed of Events received by the '#{name}' Huginn Agent"
end
def rss_content_type
interpolated['rss_content_type'].presence || 'application/rss+xml'
end
def xml_namespace
namespaces = ['xmlns:atom="http://www.w3.org/2005/Atom"']
@ -285,7 +290,7 @@ module Agents
.to_xml(skip_types: true, root: "items", skip_instruct: true, indent: 1)
.gsub(%r{^</?items>\n}, '')
return [<<-XML, 200, 'text/xml']
return [<<-XML, 200, rss_content_type]
<?xml version="1.0" encoding="UTF-8" ?>
<rss version="2.0" #{xml_namespace}>
<channel>

View file

@ -0,0 +1,18 @@
class SetRssContentType < ActiveRecord::Migration[5.0]
def up
Agents::DataOutputAgent.find_each do |agent|
if agent.options['rss_content_type'].nil?
agent.options['rss_content_type'] = 'text/xml'
agent.save(validate: false)
end
end
end
def down
Agents::DataOutputAgent.find_each do |agent|
if agent.options.delete('rss_content_type')
agent.save(validate: false)
end
end
end
end

View file

@ -150,7 +150,7 @@ describe Agents::DataOutputAgent do
stub(agent).feed_link { "https://yoursite.com" }
content, status, content_type = agent.receive_web_request({ 'secret' => 'secret1' }, 'get', 'text/xml')
expect(status).to eq(200)
expect(content_type).to eq('text/xml')
expect(content_type).to eq('application/rss+xml')
expect(content.gsub(/\s+/, '')).to eq Utils.unindent(<<-XML).gsub(/\s+/, '')
<?xml version="1.0" encoding="UTF-8" ?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:media="http://search.yahoo.com/mrss/">
@ -193,12 +193,25 @@ describe Agents::DataOutputAgent do
XML
end
describe "with cumstom rss_content_type given" do
before do
agent.options['rss_content_type'] = 'text/xml'
agent.save!
end
it "can output RSS with the Content-Type" do
content, status, content_type = agent.receive_web_request({ 'secret' => 'secret1' }, 'get', 'text/xml')
expect(status).to eq(200)
expect(content_type).to eq('text/xml')
end
end
it "can output RSS with hub links when push_hubs is specified" do
stub(agent).feed_link { "https://yoursite.com" }
agent.options[:push_hubs] = %w[https://pubsubhubbub.superfeedr.com/ https://pubsubhubbub.appspot.com/]
content, status, content_type = agent.receive_web_request({ 'secret' => 'secret1' }, 'get', 'text/xml')
expect(status).to eq(200)
expect(content_type).to eq('text/xml')
expect(content_type).to eq('application/rss+xml')
xml = Nokogiri::XML(content)
expect(xml.xpath('/rss/channel/atom:link[@rel="hub"]/@href').map(&:text).sort).to eq agent.options[:push_hubs].sort
end
@ -314,7 +327,7 @@ describe Agents::DataOutputAgent do
stub(agent).feed_link { "https://yoursite.com" }
content, status, content_type = agent.receive_web_request({ 'secret' => 'secret1' }, 'get', 'text/xml')
expect(status).to eq(200)
expect(content_type).to eq('text/xml')
expect(content_type).to eq('application/rss+xml')
expect(Nokogiri(content).at('/rss/channel/title/text()').text).to eq('XKCD comics as a feed (XKCD)')
end
@ -358,7 +371,7 @@ describe Agents::DataOutputAgent do
stub(agent).feed_link { "https://yoursite.com" }
content, status, content_type = agent.receive_web_request({ 'secret' => 'secret1' }, 'get', 'text/xml')
expect(status).to eq(200)
expect(content_type).to eq('text/xml')
expect(content_type).to eq('application/rss+xml')
expect(Nokogiri(content).at('/rss/channel/atom:icon/text()').text).to eq('https://somesite.com/icon.png')
end
end
@ -373,7 +386,7 @@ describe Agents::DataOutputAgent do
stub(agent).feed_link { "https://yoursite.com" }
content, status, content_type = agent.receive_web_request({ 'secret' => 'secret1' }, 'get', 'text/xml')
expect(status).to eq(200)
expect(content_type).to eq('text/xml')
expect(content_type).to eq('application/rss+xml')
doc = Nokogiri(content)
namespaces = doc.collect_namespaces
@ -391,7 +404,7 @@ describe Agents::DataOutputAgent do
stub(agent).feed_link { "https://yoursite.com" }
content, status, content_type = agent.receive_web_request({ 'secret' => 'secret1' }, 'get', 'text/xml')
expect(status).to eq(200)
expect(content_type).to eq('text/xml')
expect(content_type).to eq('application/rss+xml')
doc = Nokogiri(content)
namespaces = doc.collect_namespaces
@ -411,7 +424,7 @@ describe Agents::DataOutputAgent do
stub(agent).feed_link { "https://yoursite.com" }
content, status, content_type = agent.receive_web_request({ 'secret' => 'secret1' }, 'get', 'text/xml')
expect(status).to eq(200)
expect(content_type).to eq('text/xml')
expect(content_type).to eq('application/rss+xml')
doc = Nokogiri(content)
namespaces = doc.collect_namespaces
@ -429,7 +442,7 @@ describe Agents::DataOutputAgent do
stub(agent).feed_link { "https://yoursite.com" }
content, status, content_type = agent.receive_web_request({ 'secret' => 'secret1' }, 'get', 'text/xml')
expect(status).to eq(200)
expect(content_type).to eq('text/xml')
expect(content_type).to eq('application/rss+xml')
doc = Nokogiri(content)
namespaces = doc.collect_namespaces
@ -447,7 +460,7 @@ describe Agents::DataOutputAgent do
stub(agent).feed_link { "https://yoursite.com" }
content, status, content_type = agent.receive_web_request({ 'secret' => 'secret1' }, 'get', 'text/xml')
expect(status).to eq(200)
expect(content_type).to eq('text/xml')
expect(content_type).to eq('application/rss+xml')
doc = Nokogiri(content)
namespaces = doc.collect_namespaces
@ -467,7 +480,7 @@ describe Agents::DataOutputAgent do
stub(agent).feed_link { "https://yoursite.com" }
content, status, content_type = agent.receive_web_request({ 'secret' => 'secret1' }, 'get', 'text/xml')
expect(status).to eq(200)
expect(content_type).to eq('text/xml')
expect(content_type).to eq('application/rss+xml')
doc = Nokogiri(content)
namespaces = doc.collect_namespaces
@ -569,7 +582,7 @@ describe Agents::DataOutputAgent do
stub(agent).feed_link { "https://yoursite.com" }
content, status, content_type = agent.receive_web_request({ 'secret' => 'secret1' }, 'get', 'text/xml')
expect(status).to eq(200)
expect(content_type).to eq('text/xml')
expect(content_type).to eq('application/rss+xml')
expect(content.gsub(/\s+/, '')).to eq Utils.unindent(<<-XML).gsub(/\s+/, '')
<?xml version="1.0" encoding="UTF-8" ?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:media="http://search.yahoo.com/mrss/" >