Allow providers to define a data schema for their channel parameters

This commit is contained in:
David Taylor 2017-07-13 20:50:45 +01:00
parent e07a4da460
commit 4b25dcec8f
6 changed files with 86 additions and 34 deletions

View File

@ -2,28 +2,39 @@ class DiscourseChat::Channel < DiscourseChat::PluginModel
KEY_PREFIX = 'channel:' KEY_PREFIX = 'channel:'
# Setup ActiveRecord::Store to use the JSON field to read/write these values # Setup ActiveRecord::Store to use the JSON field to read/write these values
store :value, accessors: [ :provider, :descriptor ], coder: JSON store :value, accessors: [ :provider, :data ], coder: JSON
validate :provider_and_descriptor_valid? after_initialize :init_data
def provider_and_descriptor_valid? def init_data
self.data = {} if self.data.nil?
end
validate :provider_valid?, :data_valid?
def provider_valid?
# Validate provider # Validate provider
if not ::DiscourseChat::Provider.provider_names.include? provider if not ::DiscourseChat::Provider.provider_names.include? provider
errors.add(:provider, "#{provider} is not a valid provider") errors.add(:provider, "#{provider} is not a valid provider")
return return
end end
end
# Validate descriptor def data_valid?
if descriptor.blank? # If provider is invalid, don't try and check data
errors.add(:descriptor, "channel descriptor cannot be blank") return unless ::DiscourseChat::Provider.provider_names.include? provider
params = ::DiscourseChat::Provider.get_by_name(provider)::CHANNEL_PARAMETERS
unless params.keys.sort == data.keys.sort
errors.add(:data, "data does not match the required structure for provider #{provider}")
return return
end end
provider_class = ::DiscourseChat::Provider.get_by_name(provider) data.each do |key, value|
if defined? provider_class::PROVIDER_CHANNEL_REGEX regex_string = params[key]
channel_regex = Regexp.new provider_class::PROVIDER_CHANNEL_REGEX if !Regexp.new(regex_string).match?(value)
if not channel_regex.match?(descriptor) errors.add(:data, "data.#{key} is invalid")
errors.add(:descriptor, "#{descriptor} is not a valid channel descriptor for provider #{provider}")
end end
end end
end end

View File

@ -3,7 +3,7 @@ module DiscourseChat::Provider::SlackProvider
PROVIDER_ENABLED_SETTING = :chat_integration_slack_enabled PROVIDER_ENABLED_SETTING = :chat_integration_slack_enabled
PROVIDER_CHANNEL_REGEX = '^[@#]\S*$' CHANNEL_PARAMETERS = {"channel" => '^[@#]\S*$'}
def self.excerpt(post, max_length = SiteSetting.chat_integration_slack_excerpt_length) def self.excerpt(post, max_length = SiteSetting.chat_integration_slack_excerpt_length)
doc = Nokogiri::HTML.fragment(post.excerpt(max_length, doc = Nokogiri::HTML.fragment(post.excerpt(max_length,

View File

@ -1,10 +1,10 @@
module DiscourseChat module DiscourseChat
module Provider module Provider
module TelegramProvider module TelegramProvider
include Provider
PROVIDER_NAME = "telegram".freeze PROVIDER_NAME = "telegram".freeze
PROVIDER_ENABLED_SETTING = :chat_integration_telegram_enabled PROVIDER_ENABLED_SETTING = :chat_integration_telegram_enabled
CHANNEL_PARAMETERS = {}
end end
end end
end end

View File

@ -8,6 +8,8 @@ RSpec.shared_context "dummy provider" do
module ::DiscourseChat::Provider::DummyProvider module ::DiscourseChat::Provider::DummyProvider
PROVIDER_NAME = "dummy".freeze PROVIDER_NAME = "dummy".freeze
PROVIDER_ENABLED_SETTING = :chat_integration_enabled # Tie to main plugin enabled setting PROVIDER_ENABLED_SETTING = :chat_integration_enabled # Tie to main plugin enabled setting
CHANNEL_PARAMETERS = {}
@@sent_messages = [] @@sent_messages = []
@@raise_exception = nil @@raise_exception = nil
@ -30,9 +32,37 @@ RSpec.shared_context "dummy provider" do
end end
let(:provider){::DiscourseChat::Provider::DummyProvider} let(:provider){::DiscourseChat::Provider::DummyProvider}
end
RSpec.shared_context "validated dummy provider" do
before(:each) do
if defined? ::DiscourseChat::Provider::Dummy2Provider
::DiscourseChat::Provider.send(:remove_const, :Dummy2Provider)
end
module ::DiscourseChat::Provider::Dummy2Provider
PROVIDER_NAME = "dummy2".freeze
PROVIDER_ENABLED_SETTING = :chat_integration_enabled # Tie to main plugin enabled setting
CHANNEL_PARAMETERS = {"val" => '\S+'}
@@sent_messages = []
def self.trigger_notification(post, channel)
@@sent_messages.push(post: post.id, channel: channel)
end
def self.sent_messages
@@sent_messages
end
end
end
let(:provider){::DiscourseChat::Provider::DummyProvider}
end end
RSpec.configure do |rspec| RSpec.configure do |rspec|
rspec.include_context "dummy provider" rspec.include_context "dummy provider"
rspec.include_context "validated dummy provider"
end end

View File

@ -3,6 +3,7 @@ require_relative '../dummy_provider'
RSpec.describe DiscourseChat::Channel do RSpec.describe DiscourseChat::Channel do
include_context "dummy provider" include_context "dummy provider"
include_context "validated dummy provider"
it 'should save and load successfully' do it 'should save and load successfully' do
@ -10,7 +11,6 @@ RSpec.describe DiscourseChat::Channel do
chan = DiscourseChat::Channel.create({ chan = DiscourseChat::Channel.create({
provider:"dummy", provider:"dummy",
descriptor: "#random",
}) })
expect(DiscourseChat::Channel.all.length).to eq(1) expect(DiscourseChat::Channel.all.length).to eq(1)
@ -18,23 +18,22 @@ RSpec.describe DiscourseChat::Channel do
loadedChan = DiscourseChat::Channel.find(chan.id) loadedChan = DiscourseChat::Channel.find(chan.id)
expect(loadedChan.provider).to eq('dummy') expect(loadedChan.provider).to eq('dummy')
expect(loadedChan.descriptor).to eq('#random')
end end
it 'can be filtered by provider' do it 'can be filtered by provider' do
channel1 = DiscourseChat::Channel.create({provider:'dummy', descriptor:'blah'}) channel1 = DiscourseChat::Channel.create!(provider:'dummy')
channel2 = DiscourseChat::Channel.create({provider:'slack', descriptor:'#blah'}) channel2 = DiscourseChat::Channel.create!(provider:'dummy2', data:{val:"blah"})
channel3 = DiscourseChat::Channel.create({provider:'slack', descriptor:'#blah'}) channel3 = DiscourseChat::Channel.create!(provider:'dummy2', data:{val:"blah"})
expect(DiscourseChat::Channel.all.length).to eq(3) expect(DiscourseChat::Channel.all.length).to eq(3)
expect(DiscourseChat::Channel.with_provider('slack').length).to eq(2) expect(DiscourseChat::Channel.with_provider('dummy2').length).to eq(2)
expect(DiscourseChat::Channel.with_provider('dummy').length).to eq(1) expect(DiscourseChat::Channel.with_provider('dummy').length).to eq(1)
end end
it 'can find its own rules' do it 'can find its own rules' do
channel = DiscourseChat::Channel.create({provider:'dummy', descriptor:'blah'}) channel = DiscourseChat::Channel.create({provider:'dummy'})
expect(channel.rules.size).to eq(0) expect(channel.rules.size).to eq(0)
DiscourseChat::Rule.create(channel: channel) DiscourseChat::Rule.create(channel: channel)
DiscourseChat::Rule.create(channel: channel) DiscourseChat::Rule.create(channel: channel)
@ -43,21 +42,33 @@ RSpec.describe DiscourseChat::Channel do
end end
describe 'validations' do describe 'validations' do
let(:channel) { DiscourseChat::Channel.create( let(:channel) { }
provider:"dummy",
descriptor: "#general"
) }
it 'validates provider correctly' do it 'validates provider correctly' do
channel = DiscourseChat::Channel.create!(provider:"dummy")
expect(channel.valid?).to eq(true) expect(channel.valid?).to eq(true)
channel.provider = 'somerandomprovider' channel.provider = 'somerandomprovider'
expect(channel.valid?).to eq(false) expect(channel.valid?).to eq(false)
end end
it 'validates channel correctly' do it 'succeeds with valid data' do
expect(channel.valid?).to eq(true) channel2 = DiscourseChat::Channel.new(provider:"dummy2", data:{val:"hello"})
channel.descriptor = '' expect(channel2.valid?).to eq(true)
expect(channel.valid?).to eq(false) end
it 'disallows invalid data' do
channel2 = DiscourseChat::Channel.new(provider:"dummy2", data:{val:''})
expect(channel2.valid?).to eq(false)
end
it 'disallows unknown keys' do
channel2 = DiscourseChat::Channel.new(provider:"dummy2", data:{val:"hello", unknown:"world"})
expect(channel2.valid?).to eq(false)
end
it 'requires all keys' do
channel2 = DiscourseChat::Channel.new(provider:"dummy2", data:{})
expect(channel2.valid?).to eq(false)
end end
end end

View File

@ -7,7 +7,7 @@ RSpec.describe DiscourseChat::Rule do
let(:tag1){Fabricate(:tag)} let(:tag1){Fabricate(:tag)}
let(:tag2){Fabricate(:tag)} let(:tag2){Fabricate(:tag)}
let(:channel){DiscourseChat::Channel.create(provider:'dummy', descriptor:'#general')} let(:channel){DiscourseChat::Channel.create(provider:'dummy')}
describe '.alloc_key' do describe '.alloc_key' do
it 'should return sequential numbers' do it 'should return sequential numbers' do
@ -87,8 +87,8 @@ RSpec.describe DiscourseChat::Rule do
end end
it 'can be filtered by channel' do it 'can be filtered by channel' do
channel2 = DiscourseChat::Channel.create(provider:'dummy', descriptor:'#random') channel2 = DiscourseChat::Channel.create(provider:'dummy')
channel3 = DiscourseChat::Channel.create(provider:'dummy', descriptor:'#another') channel3 = DiscourseChat::Channel.create(provider:'dummy')
rule2 = DiscourseChat::Rule.create(channel:channel) rule2 = DiscourseChat::Rule.create(channel:channel)
rule3 = DiscourseChat::Rule.create(channel:channel) rule3 = DiscourseChat::Rule.create(channel:channel)