diff --git a/lib/discourse_chat/helper.rb b/lib/discourse_chat/helper.rb index 9356c37..82c76d1 100644 --- a/lib/discourse_chat/helper.rb +++ b/lib/discourse_chat/helper.rb @@ -1,6 +1,7 @@ module DiscourseChat module Helper + # Produce a string with a list of all rules associated with a channel def self.status_for_channel(provider, channel) rules = DiscourseChat::Rule.all_for_channel(provider, channel) @@ -40,6 +41,61 @@ module DiscourseChat return text end + # Create a rule for a specific channel + # Designed to be used by provider's "Slash commands" + # Will intelligently adjust existing rules to avoid duplicates + # Returns + # :updated if an existing rule has been updated + # :created if a new rule has been created + # false if there was an error + def self.smart_create_rule(provider:, channel:, filter:, category_id:, tags:) + existing_rules = DiscourseChat::Rule.all_for_channel(provider, channel) + + # Select the ones that have the same category + same_category = existing_rules.select { |rule| rule.category_id == category_id } + + same_category_and_tags = same_category.select{ |rule| (rule.tags.nil? ? [] : rule.tags.sort) == (tags.nil? ? [] : tags.sort) } + + if same_category_and_tags.size > 0 + # These rules have exactly the same criteria as what we're trying to create + the_rule = same_category_and_tags.shift # Take out the first one + + same_category_and_tags.each do |rule| # Destroy all the others - they're duplicates + rule.destroy + end + + return :updated if the_rule.update(filter:filter) # Update the filter + return false # Error, probably validation + end + + same_category_and_filters = same_category.select { |rule| rule.filter == filter } + + if same_category_and_filters.size > 0 + # These rules are exactly the same, except for tags. Let's combine the tags together + tags = [] if tags.nil? + same_category_and_filters.each do |rule| + tags = tags | rule.tags unless rule.tags.nil? # Append the tags together, avoiding duplicates by magic + end + + the_rule = same_category_and_filters.shift # Take out the first one + + if the_rule.update(tags: tags) # Update the tags + same_category_and_filters.each do |rule| # Destroy all the others - they're duplicates + rule.destroy + end + return :updated + end + + return false # Error + end + + # This rule is unique! Create a new one: + return :created if Rule.new({provider: provider, channel: channel, filter: filter, category_id: category_id, tags: tags}).save + + return false # Error + + end + end end diff --git a/spec/lib/discourse_chat/helper_spec.rb b/spec/lib/discourse_chat/helper_spec.rb index 447162e..f3c721f 100644 --- a/spec/lib/discourse_chat/helper_spec.rb +++ b/spec/lib/discourse_chat/helper_spec.rb @@ -4,6 +4,8 @@ RSpec.describe DiscourseChat::Manager do let(:category) {Fabricate(:category)} let(:tag1){Fabricate(:tag)} + let(:tag2){Fabricate(:tag)} + let(:tag3){Fabricate(:tag)} describe '.status_for_channel' do @@ -54,4 +56,93 @@ RSpec.describe DiscourseChat::Manager do end + describe '.smart_create_rule' do + + it 'creates a rule when there are none' do + val = DiscourseChat::Helper.smart_create_rule(provider: 'slack', + channel: '#general', + filter: 'watch', + category_id: category.id, + tags: [tag1.name] + ) + expect(val).to eq(:created) + + record = DiscourseChat::Rule.all.first + expect(record.provider).to eq('slack') + expect(record.channel).to eq('#general') + expect(record.filter).to eq('watch') + expect(record.category_id).to eq(category.id) + expect(record.tags).to eq([tag1.name]) + end + + it 'updates a rule when it has the same category and tags' do + existing = DiscourseChat::Rule.new({provider: 'slack', + channel: '#general', + filter: 'watch', + category_id: category.id, + tags: [tag2.name, tag1.name] + }).save! + + val = DiscourseChat::Helper.smart_create_rule(provider: 'slack', + channel: '#general', + filter: 'mute', + category_id: category.id, + tags: [tag1.name, tag2.name] + ) + + expect(val).to eq(:updated) + + expect(DiscourseChat::Rule.all.size).to eq(1) + expect(DiscourseChat::Rule.all.first.filter).to eq('mute') + end + + it 'updates a rule when it has the same category and filter' do + existing = DiscourseChat::Rule.new({provider: 'slack', + channel: '#general', + filter: 'watch', + category_id: category.id, + tags: [tag1.name, tag2.name] + }).save! + + val = DiscourseChat::Helper.smart_create_rule(provider: 'slack', + channel: '#general', + filter: 'watch', + category_id: category.id, + tags: [tag1.name, tag3.name] + ) + + expect(val).to eq(:updated) + + expect(DiscourseChat::Rule.all.size).to eq(1) + expect(DiscourseChat::Rule.all.first.tags).to contain_exactly(tag1.name, tag2.name, tag3.name) + end + + it 'destroys duplicate rules on save' do + DiscourseChat::Rule.new({provider: 'slack', channel: '#general', filter: 'watch'}).save! + DiscourseChat::Rule.new({provider: 'slack', channel: '#general', filter: 'watch'}).save! + expect(DiscourseChat::Rule.all.size).to eq(2) + val = DiscourseChat::Helper.smart_create_rule(provider: 'slack', + channel: '#general', + filter: 'watch', + category_id: nil, + tags: nil + ) + expect(val).to eq(:updated) + expect(DiscourseChat::Rule.all.size).to eq(1) + end + + it 'returns false on error' do + val = DiscourseChat::Helper.smart_create_rule(provider: 'nonexistantprovider', + channel: '#general', + filter: 'watch', + category_id: nil, + tags: nil + ) + + expect(val).to eq(false) + end + + + end + end \ No newline at end of file