Skip to content

Commit

Permalink
FEATURE: Allow filtering a feed on the category property of items (#23)
Browse files Browse the repository at this point in the history
  • Loading branch information
xfalcox committed Jul 30, 2021
1 parent 37542ff commit d52b361
Show file tree
Hide file tree
Showing 13 changed files with 89 additions and 20 deletions.
Expand Up @@ -14,7 +14,7 @@ def update
new_feed_settings = []
else
new_feed_settings = (feed_setting_params.presence || []).map do |feed_setting|
feed_setting.values_at(:feed_url, :author_username)
feed_setting.values_at(:feed_url, :author_username, :feed_category_filter)
end
end

Expand Down
12 changes: 8 additions & 4 deletions app/jobs/jobs/discourse_rss_polling/poll_feed.rb
Expand Up @@ -10,13 +10,14 @@ def execute(args)

@feed_url = args[:feed_url]
@author = User.find_by_username(args[:author_username])
@feed_category_filter = args[:feed_category_filter]

poll_feed if not_polled_recently?
end

private

attr_reader :feed_url, :author
attr_reader :feed_url, :author, :feed_category_filter

def feed_key
"rss-polling-feed-polled:#{Digest::SHA1.hexdigest(feed_url)}"
Expand All @@ -28,15 +29,18 @@ def not_polled_recently?

def poll_feed
topics_polled_from_feed.each do |topic|
TopicEmbed.import(author, topic.url, topic.title, CGI.unescapeHTML(topic.content)) if topic.content.present?

next if !topic.content.present?
next if (feed_category_filter.present? && !topic.categories.include?(feed_category_filter))

TopicEmbed.import(author, topic.url, topic.title, CGI.unescapeHTML(topic.content))
end
end

def topics_polled_from_feed
raw_feed = fetch_raw_feed
return [] if raw_feed.blank?

RSS::Parser.parse(raw_feed).items.map { |item| ::DiscourseRssPolling::FeedItem.new(item) }
RSS::Parser.parse(raw_feed, false).items.map { |item| ::DiscourseRssPolling::FeedItem.new(item) }
rescue RSS::NotWellFormedError, RSS::InvalidRSSError
[]
end
Expand Down
4 changes: 4 additions & 0 deletions app/models/discourse_rss_polling/feed_item.rb
Expand Up @@ -28,6 +28,10 @@ def title
CGI::unescapeHTML(unclean_title) if unclean_title
end

def categories
@accessor.element_content(:categories).map { |c| c.content }
end

private

# The tag name's relative order implies its priority.
Expand Down
8 changes: 5 additions & 3 deletions app/models/discourse_rss_polling/feed_setting.rb
Expand Up @@ -7,18 +7,20 @@ class FeedSetting
attr_accessor(
:feed_url,
:author_username,
:feed_category_filter,
)

def initialize(feed_url:, author_username:)
def initialize(feed_url:, author_username:, feed_category_filter:)
@feed_url = feed_url
@author_username = author_username
@feed_category_filter = feed_category_filter
end

def poll(inline: false)
if inline
Jobs::DiscourseRssPolling::PollFeed.new.execute(feed_url: feed_url, author_username: author_username)
Jobs::DiscourseRssPolling::PollFeed.new.execute(feed_url: feed_url, author_username: author_username, feed_category_filter: feed_category_filter)
else
Jobs.enqueue('DiscourseRssPolling::PollFeed', feed_url: feed_url, author_username: author_username)
Jobs.enqueue('DiscourseRssPolling::PollFeed', feed_url: feed_url, author_username: author_username, feed_category_filter: feed_category_filter)
end
end
end
Expand Down
2 changes: 1 addition & 1 deletion app/services/discourse_rss_polling/feed_setting_finder.rb
Expand Up @@ -23,7 +23,7 @@ def where(&block)
def all
YAML.load(SiteSetting.rss_polling_feed_setting)
.select(&@condition)
.map { |(feed_url, author_username)| FeedSetting.new(feed_url: feed_url, author_username: author_username) }
.map { |(feed_url, author_username, feed_category_filter)| FeedSetting.new(feed_url: feed_url, author_username: author_username, feed_category_filter: feed_category_filter) }
end

def take
Expand Down
Expand Up @@ -19,24 +19,25 @@ export default Ember.Controller.extend({
// TODO: extract feed setting into its own component && more validation
@observes("feedSettings.@each.{feed_url,author_username}")
validate() {
let overallVaildity = true;
let overallValidity = true;

this.get("feedSettings").forEach((feedSetting) => {
const localValidity =
!Ember.isBlank(feedSetting.feed_url) &&
!Ember.isBlank(feedSetting.author_username);
Ember.set(feedSetting, "valid", localValidity);
overallVaildity = overallVaildity && localValidity;
overallValidity = overallValidity && localValidity;
});

this.set("valid", overallVaildity);
this.set("valid", overallValidity);
},

actions: {
create() {
this.get("feedSettings").addObject({
feed_url: null,
author_username: null,
feed_category_filter: null,
});
},

Expand Down
Expand Up @@ -4,6 +4,7 @@
<tr>
<th>{{i18n "admin.rss_polling.feed_url"}}</th>
<th>{{i18n "admin.rss_polling.author"}}</th>
<th>{{i18n "admin.rss_polling.feed_category_filter"}}</th>
<th>
{{d-button
icon="save"
Expand Down Expand Up @@ -35,6 +36,13 @@
)
}}
</td>
<td>
{{input
value=setting.feed_category_filter
placeholder="updates"
disabled=saving
}}
</td>
<td>
{{d-button
icon="times"
Expand All @@ -49,6 +57,7 @@

<tfoot>
<tr>
<td></td>
<td></td>
<td></td>
<td>
Expand Down
9 changes: 5 additions & 4 deletions assets/stylesheets/rss-polling.scss
Expand Up @@ -4,13 +4,14 @@
td {
position: relative;

&:first-child {
width: 80%;
&:first-child input {
width: 400px;
}
.user-chooser {
width: 150px;
}

input {
margin-bottom: 0;
width: 95%;
}
}
}
Expand Down
1 change: 1 addition & 0 deletions config/locales/client.en.yml
Expand Up @@ -7,4 +7,5 @@ en:
rss_polling:
author: Author
feed_url: Feed URL
feed_category_filter: Feed Category Filter
remember_to_add_allowed_hosts: To set the Discourse category that your feed will be published to, add the domain of the feed's link attributes to the Allowed Hosts section of your Admin / Customize / Embedding page.
21 changes: 21 additions & 0 deletions spec/fixtures/files/multiple_categories.atom
@@ -0,0 +1,21 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<feed
xmlns="http://www.w3.org/2005/Atom">
<title>Discourse</title>
<link href="https://blog.discourse.org/feed/atom/" rel="self" type="application/atom+xml"/>
<updated>2019-08-21T01:42:02Z</updated>
<id>https://blog.discourse.org/feed/atom/</id>
<entry>
<id>https://blog.discourse.org/?p=pollfeedspec</id>
<title type="text">Poll Feed Spec Fixture</title>
<author>
<name>xrav3nz</name>
</author>
<category>spec</category>
<category>xrav3nz diary</category>
<link href="https://blog.discourse.org/2017/09/poll-feed-spec-fixture/" rel="alternate" type="text/html"/>
<published>2019-08-16T21:46:39Z</published>
<updated>2019-08-16T21:46:39Z</updated>
<summary type="html">Here are some random descriptions...</summary>
</entry>
</feed>
14 changes: 14 additions & 0 deletions spec/models/feed_item_spec.rb
Expand Up @@ -71,4 +71,18 @@
title: 'Poll Feed Spec Fixture',
)
end

context 'ATOM items with categories elements' do
let(:raw_feed) { rss_polling_file_fixture('multiple_categories.atom').read }
let(:feed) { RSS::Parser.parse(raw_feed, false) }
let(:raw_feed_item) { feed.entries.first }

include_examples(
'correctly parses the feed',
content: 'Here are some random descriptions...',
url: 'https://blog.discourse.org/2017/09/poll-feed-spec-fixture/',
title: 'Poll Feed Spec Fixture',
categories: ['spec', 'xrav3nz diary']
)
end
end
13 changes: 12 additions & 1 deletion spec/models/feed_setting_spec.rb
Expand Up @@ -6,7 +6,9 @@
SiteSetting.rss_polling_enabled = true
let(:feed_url) { 'https://blog.discourse.org/feed/' }
let(:author) { Fabricate(:user) }
let(:feed_setting) { DiscourseRssPolling::FeedSetting.new(feed_url: feed_url, author_username: author.username) }
let(:feed_category_filter) { 'spec' }
let(:feed_setting) { DiscourseRssPolling::FeedSetting.new(feed_url: feed_url, author_username: author.username, feed_category_filter: feed_category_filter) }
let(:wrong_feed_setting) { DiscourseRssPolling::FeedSetting.new(feed_url: feed_url, author_username: author.username, feed_category_filter: 'non existing category') }
let(:poll_feed_job) { Jobs::DiscourseRssPolling::PollFeed }

describe '#poll' do
Expand All @@ -23,6 +25,7 @@

expect(enqueued_job['args'][0]['feed_url']).to eq(feed_url)
expect(enqueued_job['args'][0]['author_username']).to eq(author.username)
expect(enqueued_job['args'][0]['feed_category_filter']).to eq(feed_category_filter)
end
end
end
Expand All @@ -41,6 +44,14 @@
expect(topic.first_post.raw).to include('<p>This is the body &amp; content. </p>')
expect(topic.topic_embed.embed_url).to eq('https://blog.discourse.org/2017/09/poll-feed-spec-fixture')
end

it 'polls and the feed and does not create the new topics because of the category filter' do
$redis.del("rss-polling-feed-polled:#{Digest::SHA1.hexdigest(feed_url)}")
stub_request(:head, feed_url).to_return(status: 200, body: '')
stub_request(:get, feed_url).to_return(status: 200, body: file_from_fixtures('feed.rss', 'feed'))

expect { wrong_feed_setting.poll(inline: true) }.not_to change { author.topics.count }
end
end
end
end
7 changes: 4 additions & 3 deletions spec/requests/feed_settings_controller_spec.rb
Expand Up @@ -11,7 +11,7 @@
SiteSetting.rss_polling_enabled = true
SiteSetting.rss_polling_feed_setting = [
['https://www.example.com/feed', 'system'],
['https://blog.discourse.org/feed/', 'discourse'],
['https://blog.discourse.org/feed/', 'discourse', 'updates'],
].to_yaml
end

Expand All @@ -35,14 +35,15 @@
feed_settings: [
{
feed_url: 'https://www.newsite.com/feed',
author_username: 'system'
author_username: 'system',
feed_category_filter: 'updates'
}
]
}

expect(response.status).to eq(200)
expect(SiteSetting.rss_polling_feed_setting).to eq([
['https://www.newsite.com/feed', 'system']
['https://www.newsite.com/feed', 'system', 'updates']
].to_yaml)
end
end
Expand Down

0 comments on commit d52b361

Please sign in to comment.