add webhooks controller

This commit is contained in:
Andrew Cantino 2013-05-08 23:24:08 -07:00
parent db5cb918bf
commit d0715756f0
8 changed files with 115 additions and 2 deletions

View file

@ -1,6 +1,7 @@
source 'https://rubygems.org'
gem 'rails'
gem 'rake'
gem 'mysql2'
gem 'devise'
gem 'rails_admin'

View file

@ -0,0 +1,39 @@
# This controller is designed to allow your Agents to receive cross-site Webhooks (posts). When POSTed, your Agent will
# have #receive_webhook called on itself with the POST params.
#
# Make POSTs to the following URL:
# http://yourserver.com/users/:user_id/webhooks/:agent_id/:secret
# where :user_id is your User's id, :agent_id is an Agent's id, and :secret is a token that should be
# user-specifiable in your Agent. It is highly recommended that you verify this token whenever #receive_webhook
# is called. For example, one of your Agent's options could be :secret and you could compare this value
# to params[:secret] whenever #receive_webhook is called on your Agent, rejecting invalid requests.
#
# Your Agent's #receive_webhook method should return an Array of [json_or_string_response, status_code]. For example:
# [{status: "success"}, 200]
# or
# ["not found", 404]
class WebhooksController < ApplicationController
skip_before_filter :authenticate_user!
def create
user = User.find_by_id(params[:user_id])
if user
agent = user.agents.find_by_id(params[:agent_id])
if agent
response, status = agent.trigger_webhook(params.except(:action, :controller, :agent_id, :user_id))
if response.is_a?(String)
render :text => response, :status => status || 200
elsif response.is_a?(Hash)
render :json => response, :status => status || 200
else
head :ok
end
else
render :text => "agent not found", :status => :not_found
end
else
render :text => "user not found", :status => :not_found
end
end
end

View file

@ -60,6 +60,11 @@ class Agent < ActiveRecord::Base
# Implement me in your subclass of Agent.
end
def receive_webhook(params)
# Implement me in your subclass of Agent.
["not implemented", 404]
end
# Implement me in your subclass to decide if your Agent is working.
def working?
raise "Implement me in your subclass"
@ -88,6 +93,13 @@ class Agent < ActiveRecord::Base
message.gsub(/<([^>]+)>/) { Utils.value_at(payload, $1) || "??" }
end
def trigger_webhook(params)
receive_webhook(params).tap do
self.last_webhook_at = Time.now
save!
end
end
def set_default_schedule
self.schedule = default_schedule unless schedule.present? || cannot_be_scheduled?
end

View file

@ -16,6 +16,7 @@ Huginn::Application.routes.draw do
match "/worker_status" => "worker_status#show"
post "/users/:user_id/update_location/:secret" => "user_location_updates#create"
post "/users/:user_id/webhooks/:agent_id/:secret" => "webhooks#create"
mount RailsAdmin::Engine => '/admin', :as => 'rails_admin'
# match "/delayed_job" => DelayedJobWeb, :anchor => false

View file

@ -0,0 +1,5 @@
class AddLastWebhookAtToAgents < ActiveRecord::Migration
def change
add_column :agents, :last_webhook_at, :datetime
end
end

View file

@ -11,7 +11,7 @@
#
# It's strongly recommended to check this file into your version control system.
ActiveRecord::Schema.define(:version => 20130126080736) do
ActiveRecord::Schema.define(:version => 20130509053743) do
create_table "agents", :force => true do |t|
t.integer "user_id"
@ -26,6 +26,7 @@ ActiveRecord::Schema.define(:version => 20130126080736) do
t.datetime "created_at", :null => false
t.datetime "updated_at", :null => false
t.text "memory", :limit => 2147483647
t.datetime "last_webhook_at"
end
add_index "agents", ["schedule"], :name => "index_agents_on_schedule"

View file

@ -0,0 +1,54 @@
require 'spec_helper'
describe WebhooksController do
class Agents::WebhookReceiverAgent < Agent
cannot_receive_events!
cannot_be_scheduled!
def receive_webhook(params)
if params.delete(:secret) == options[:secret]
memory[:webhook_values] = params
["success", 200]
else
["failure", 404]
end
end
end
before do
stub(Agents::WebhookReceiverAgent).valid_type?("Agents::WebhookReceiverAgent") { true }
@agent = Agents::WebhookReceiverAgent.new(:name => "something", :options => { :secret => "my_secret" })
@agent.user = users(:bob)
@agent.save!
end
it "should not require login to trigger a webhook" do
@agent.last_webhook_at.should be_nil
post :create, :user_id => users(:bob).to_param, :agent_id => @agent.id, :secret => "my_secret", :key => "value", :another_key => "5"
@agent.reload.last_webhook_at.should be_within(2).of(Time.now)
response.body.should == "success"
response.should be_success
end
it "should call receive_webhook" do
post :create, :user_id => users(:bob).to_param, :agent_id => @agent.id, :secret => "my_secret", :key => "value", :another_key => "5"
@agent.reload.memory[:webhook_values].should == { :key => "value", :another_key => "5" }
response.body.should == "success"
response.should be_success
post :create, :user_id => users(:bob).to_param, :agent_id => @agent.id, :secret => "not_my_secret", :no => "go"
@agent.reload.memory[:webhook_values].should_not == { :no => "go" }
response.body.should == "failure"
response.should be_missing
end
it "should fail on incorrect users" do
post :create, :user_id => users(:jane).to_param, :agent_id => @agent.id, :secret => "my_secret", :no => "go"
response.should be_missing
end
it "should fail on incorrect agents" do
post :create, :user_id => users(:bob).to_param, :agent_id => 454545, :secret => "my_secret", :no => "go"
response.should be_missing
end
end

View file

@ -49,7 +49,7 @@ describe Agent do
end
end
describe "with a mock source" do
describe "with an example Agent" do
class Agents::SomethingSource < Agent
default_schedule "2pm"