Add support for Basecamp, Campfire & Slack webhooks
This commit is contained in:
+16
-1
@@ -1,10 +1,13 @@
|
||||
class Webhook < ApplicationRecord
|
||||
include Triggerable
|
||||
|
||||
SLACK_WEBHOOK_URL_REGEX = %r{//hooks\.slack\.com/services/T[^\/]+/B[^\/]+/[^\/]+\Z}i
|
||||
CAMPFIRE_WEBHOOK_URL_REGEX = %r{/rooms/\d+/\d+-[^\/]+/messages\Z}i
|
||||
BASECAMP_CAMPFIRE_WEBHOOK_URL_REGEX = %r{/\d+/integrations/[^\/]+/buckets/\d+/chats/\d+/lines\Z}i
|
||||
|
||||
PERMITTED_SCHEMES = %w[ http https ].freeze
|
||||
PERMITTED_ACTIONS = %w[
|
||||
card_assigned
|
||||
card_boosted
|
||||
card_closed
|
||||
card_collection_changed
|
||||
card_created
|
||||
@@ -48,6 +51,18 @@ class Webhook < ApplicationRecord
|
||||
@renderer ||= ApplicationController.renderer.new
|
||||
end
|
||||
|
||||
def for_basecamp?
|
||||
url.match? BASECAMP_CAMPFIRE_WEBHOOK_URL_REGEX
|
||||
end
|
||||
|
||||
def for_campfire?
|
||||
url.match? CAMPFIRE_WEBHOOK_URL_REGEX
|
||||
end
|
||||
|
||||
def for_slack?
|
||||
url.match? SLACK_WEBHOOK_URL_REGEX
|
||||
end
|
||||
|
||||
private
|
||||
def validate_url
|
||||
uri = URI.parse(url.presence)
|
||||
|
||||
@@ -106,6 +106,37 @@ class Webhook::Delivery < ApplicationRecord
|
||||
end
|
||||
|
||||
def payload
|
||||
webhook.renderer.render(template: "webhooks/event", assigns: { event: event }, format: :json)
|
||||
@payload ||= if webhook.for_basecamp?
|
||||
{ line: { content: render_payload(formats: :html) } }.to_json
|
||||
elsif webhook.for_campfire?
|
||||
render_payload(formats: :html)
|
||||
elsif webhook.for_slack?
|
||||
html = render_payload(formats: :html)
|
||||
{ text: convert_html_to_mrkdwn(html) }.to_json
|
||||
else
|
||||
render_payload(formats: :json)
|
||||
end
|
||||
end
|
||||
|
||||
def render_payload(**options)
|
||||
webhook.renderer.render(layout: false, template: "webhooks/event", assigns: { event: event }, **options).strip
|
||||
end
|
||||
|
||||
def convert_html_to_mrkdwn(html)
|
||||
document = Nokogiri::HTML5(html)
|
||||
|
||||
document.css("a").each do |a|
|
||||
a.replace("<#{a["href"].strip}|#{a.text}>") if a["href"].present?
|
||||
end
|
||||
|
||||
document.css("b").each do |b|
|
||||
b.replace("*#{b.text}*")
|
||||
end
|
||||
|
||||
document.css("i").each do |i|
|
||||
i.replace("_#{i.text}_")
|
||||
end
|
||||
|
||||
document.text
|
||||
end
|
||||
end
|
||||
|
||||
@@ -0,0 +1,4 @@
|
||||
<%= event_action_sentence(@event) %>
|
||||
<% if @event.eventable %>
|
||||
<%= link_to "See more", @event.eventable %>
|
||||
<% end %>
|
||||
@@ -108,4 +108,87 @@ class Webhook::DeliveryTest < ActiveSupport::TestCase
|
||||
|
||||
assert_equal "errored", delivery.state
|
||||
end
|
||||
|
||||
test "deliver with basecamp webhook format" do
|
||||
webhook = Webhook.create!(
|
||||
name: "Basecamp",
|
||||
url: "https://3.basecamp.com/123/integrations/webhook/buckets/456/chats/789/lines"
|
||||
)
|
||||
event = events(:layout_commented)
|
||||
delivery = Webhook::Delivery.create!(webhook: webhook, event: event)
|
||||
|
||||
request_stub = stub_request(:post, webhook.url)
|
||||
.with do |request|
|
||||
body = JSON.parse(request.body)
|
||||
body.key?("line") && body["line"].key?("content") && body["line"]["content"].present?
|
||||
end
|
||||
.to_return(status: 200)
|
||||
|
||||
delivery.deliver
|
||||
|
||||
assert_requested request_stub
|
||||
assert delivery.succeeded?
|
||||
end
|
||||
|
||||
test "deliver with campfire webhook format" do
|
||||
webhook = Webhook.create!(
|
||||
name: "Campfire",
|
||||
url: "https://example.com/rooms/123/456-room-name/messages"
|
||||
)
|
||||
event = events(:layout_commented)
|
||||
delivery = Webhook::Delivery.create!(webhook: webhook, event: event)
|
||||
|
||||
request_stub = stub_request(:post, webhook.url)
|
||||
.with do |request|
|
||||
request.body.is_a?(String) && !request.body.start_with?("{") && request.body.present?
|
||||
end
|
||||
.to_return(status: 200)
|
||||
|
||||
delivery.deliver
|
||||
|
||||
assert_requested request_stub
|
||||
assert delivery.succeeded?
|
||||
end
|
||||
|
||||
test "deliver with slack webhook format" do
|
||||
webhook = Webhook.create!(
|
||||
name: "Slack",
|
||||
url: "https://hooks.slack.com/services/T12345678/B12345678/abcdefghijklmnopqrstuvwx"
|
||||
)
|
||||
event = events(:layout_commented)
|
||||
delivery = Webhook::Delivery.create!(webhook: webhook, event: event)
|
||||
|
||||
request_stub = stub_request(:post, webhook.url)
|
||||
.with do |request|
|
||||
body = JSON.parse(request.body)
|
||||
body.key?("text") && body["text"].present?
|
||||
end
|
||||
.to_return(status: 200)
|
||||
|
||||
delivery.deliver
|
||||
|
||||
assert_requested request_stub
|
||||
assert delivery.succeeded?
|
||||
end
|
||||
|
||||
test "deliver with generic webhook format" do
|
||||
webhook = Webhook.create!(
|
||||
name: "Generic",
|
||||
url: "https://example.com/webhook"
|
||||
)
|
||||
event = events(:layout_commented)
|
||||
delivery = Webhook::Delivery.create!(webhook: webhook, event: event)
|
||||
|
||||
request_stub = stub_request(:post, webhook.url)
|
||||
.with do |request|
|
||||
body = JSON.parse(request.body)
|
||||
body.present? && !body.key?("line") && !body.key?("text")
|
||||
end
|
||||
.to_return(status: 200)
|
||||
|
||||
delivery.deliver
|
||||
|
||||
assert_requested request_stub
|
||||
assert delivery.succeeded?
|
||||
end
|
||||
end
|
||||
|
||||
@@ -52,4 +52,55 @@ class WebhookTest < ActiveSupport::TestCase
|
||||
webhook.activate
|
||||
end
|
||||
end
|
||||
|
||||
test "for_slack?" do
|
||||
webhook = Webhook.new name: "Test", url: "https://hooks.slack.com/services/T12345678/B12345678/abcdefghijklmnopqrstuvwx"
|
||||
assert webhook.for_slack?
|
||||
|
||||
webhook = Webhook.new name: "Test", url: "https://hooks.slack.com/services/T12345678/B12345678"
|
||||
assert_not webhook.for_slack?
|
||||
|
||||
webhook = Webhook.new name: "Test", url: "https://hooks.slack.com/services/T12345678"
|
||||
assert_not webhook.for_slack?
|
||||
|
||||
webhook = Webhook.new name: "Test", url: "https://hooks.slack.com/services/"
|
||||
assert_not webhook.for_slack?
|
||||
|
||||
webhook = Webhook.new name: "Test", url: "https://example.com/webhook"
|
||||
assert_not webhook.for_slack?
|
||||
end
|
||||
|
||||
test "for_campfire?" do
|
||||
webhook = Webhook.new name: "Test", url: "https://example.com/rooms/123/456-room-name/messages"
|
||||
assert webhook.for_campfire?
|
||||
|
||||
webhook = Webhook.new name: "Test", url: "https://campfire.example.com/rooms/999/123-test-room/messages"
|
||||
assert webhook.for_campfire?
|
||||
|
||||
webhook = Webhook.new name: "Test", url: "https://campfire.example.com/rooms/999/123/messages"
|
||||
assert_not webhook.for_campfire?, "The bot key is missing a token"
|
||||
|
||||
webhook = Webhook.new name: "Test", url: "https://example.com/webhook"
|
||||
assert_not webhook.for_campfire?
|
||||
|
||||
webhook = Webhook.new name: "Test", url: "https://example.com/rooms/123/messages"
|
||||
assert_not webhook.for_campfire?
|
||||
|
||||
webhook = Webhook.new name: "Test", url: "https://example.com/rooms/123/456-room-name/"
|
||||
assert_not webhook.for_campfire?
|
||||
end
|
||||
|
||||
test "for_basecamp?" do
|
||||
webhook = Webhook.new name: "Test", url: "https://basecamp.com/999/integrations/some-token/buckets/111/chats/222/lines"
|
||||
assert webhook.for_basecamp?
|
||||
|
||||
webhook = Webhook.new name: "Test", url: "https://example.com/webhook"
|
||||
assert_not webhook.for_basecamp?
|
||||
|
||||
webhook = Webhook.new name: "Test", url: "https://3.basecamp.com/123/integrations/webhook/buckets/456/chats/"
|
||||
assert_not webhook.for_basecamp?
|
||||
|
||||
webhook = Webhook.new name: "Test", url: "https://3.basecamp.com/integrations/webhook/buckets/456/chats/789/lines"
|
||||
assert_not webhook.for_basecamp?
|
||||
end
|
||||
end
|
||||
|
||||
Reference in New Issue
Block a user