Skip to content

Instantly share code, notes, and snippets.

@bcooksey
Forked from endymion/contact.rb
Created June 18, 2013 17:07
Show Gist options
  • Save bcooksey/5807278 to your computer and use it in GitHub Desktop.
Save bcooksey/5807278 to your computer and use it in GitHub Desktop.
This is the orignal, see the new one
class Hook < ActiveRecord::Base
attr_accessible :event, :account_id, :subscription_url, :target_url
validates_presence_of :event, :account_id, :subscription_url, :target_url
# Looks for an appropriate REST hook that matches the record, and triggers the hook if one exists.
def self.trigger(event, record)
hooks = Hook.find(:all, :conditions => {
:event => event,
:account_id => record.account_id,
})
return if hooks.empty?
unless Rails.env.development?
hook = hooks.first
Rails.logger.info "Triggering REST hook: #{hook.inspect}"
RestClient.post(hook.target_url, record.to_json) do |response, request, result|
if response.code.eql? 410
Rails.logger.info "Destroying REST hook because of 410 response: #{hook.inspect}"
hook.destroy
end
end
end
end
end
require 'test_helper'
require 'fakeweb'
class HookTest < ActiveSupport::TestCase
def setup
@account = Factory(:account)
@subscription_url = "https://zapier.com/hooks/standard/wpGRPPcRxZt2GxBbSSeUAlWPBnhLiRWB/"
@target_url = "https://zapier.com/hooks/standard/wpGRPPcRxZt2GxBbSSeUAlWPBnhLiRWB/"
hook = Hook.create(
{
"event" => "new_contact",
"account_id" => @account.id,
"subscription_url" => @subscription_url,
"target_url" => @target_url
}
)
@contact = Factory(:contact, :account => @account,
:first => 'Ryan', :last => 'Porter', :email => '[email protected]')
end
def test_trigger
FakeWeb.register_uri(
:post,
@target_url,
:body => 'irrelevant',
:status => ['200', 'Triggered']
)
Hook.trigger('new_contact', @contact)
assert_equal "POST", FakeWeb.last_request.method
assert_equal @contact.to_json, FakeWeb.last_request.body
end
def test_trigger_remove_hook_on_410_response
FakeWeb.register_uri(
:post,
@target_url,
:body => 'irrelevant',
:status => ['410', 'Danger, Will Robinson!']
)
Hook.trigger('new_contact', @contact)
assert_equal "POST", FakeWeb.last_request.method
assert_equal @contact.to_json, FakeWeb.last_request.body
assert_equal 0, Hook.count # The 410 response should trigger removal of the hook.
end
end
class HooksController < ApplicationController
def create
hook = Hook.new params
render :nothing => true, :status => 500 and return unless hook.save
Rails.logger.info "Created REST hook: #{hook.inspect}"
# The Zapier documentation says to return 201 - Created.
render :json => hook.to_json(:only => :id), :status => 201
end
def destroy
hook = Hook.find(params[:id]) if params[:id]
if hook.nil? && params[:subscription_url]
hook = Hook.find_by_subscription_url(params[:subscription_url]).destroy
end
Rails.logger.info "Destroying REST hook: #{hook.inspect}"
hook.destroy
render :nothing => true, :status => 200
end
end
require 'test_helper'
class HooksControllerTest < ActionController::TestCase
def setup
@account = Factory(:account)
@user = Factory(:user, :username => 'username') # Password: "password", by default.
@staff = Factory(:staff, :account => @account, :user => @user)
end
def test_subscribe_requires_authentication
post :create,
{
"event" => "new_contact",
"subscription_url" => "whatever",
"target_url" => "whatever"
}
assert_redirected_to :controller => 'public/signin', :action => 'signin'
end
def test_subscribe
@request.env["HTTP_AUTHORIZATION"] = "Basic " + Base64::encode64("username:password")
subscription_url = "https://zapier.com/hooks/standard/wpGRPPcRxZt2GxBbSSeUAlWPBnhLiRWB/"
target_url = "https://zapier.com/hooks/standard/wpGRPPcRxZt2GxBbSSeUAlWPBnhLiRWB/"
post :create,
{
"event" => "new_contact",
"account_id" => @account.id,
"subscription_url" => subscription_url,
"target_url" => target_url
}
assert_response 201
assert_equal 1, Hook.count
hook = Hook.last
assert_equal %Q[{"id":#{hook.id}}], @response.body
assert_equal "new_contact", hook.event
assert_equal @account.id, hook.account_id.to_i
assert_equal subscription_url, hook.subscription_url
assert_equal target_url, hook.target_url
end
def test_subscribe_error
@request.env["HTTP_AUTHORIZATION"] = "Basic " + Base64::encode64("username:password")
post :create, nil
assert_response 500
assert_equal 0, Hook.count
end
def test_unsubscribe_rest
@request.env["HTTP_AUTHORIZATION"] = "Basic " + Base64::encode64("username:password")
hook = Hook.create(
:event => "new_contact",
:account_id => @account.id,
:subscription_url => 'whatever',
:target_url => 'whatever'
)
assert_equal 1, Hook.count
post :destroy,
{
:id => hook.id.to_s,
:subscription_url => 'whatever'
}
assert_response 200
assert_equal 0, Hook.count
end
def test_unsubscribe_the_hacky_way
@request.env["HTTP_AUTHORIZATION"] = "Basic " + Base64::encode64("username:password")
subscription_url = "https://zapier.com/hooks/standard/wpGRPPcRxZt2GxBbSSeUAlWPBnhLiRWB/"
target_url = "https://zapier.com/hooks/standard/wpGRPPcRxZt2GxBbSSeUAlWPBnhLiRWB/"
Hook.create(
:event => "new_contact",
:account_id => @account.id,
:subscription_url => subscription_url,
:target_url => target_url
)
assert_equal 1, Hook.count
post :destroy,
{
"subscription_url" => subscription_url
}
assert_response 200
assert_equal 0, Hook.count
end
end
@endymion
Copy link

FYI: I updated the original fork just now to support triggering REST hooks from Resque background jobs.

@endymion
Copy link

Updated the main fork again to check for an applicable hook before enqueuing a Resque job.

And to run ALL applicable hooks (instead of just the first match).

And to demonstrate using Resque-Retry for dealing with Resque worker jobs that run before the transaction is closed. If you're using Rails 3 then use "after_commit :do_something, :on => :create" instead.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment