webhookr 0.0.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (68) hide show
  1. data/.gitignore +14 -0
  2. data/Gemfile +18 -0
  3. data/Guardfile +31 -0
  4. data/MIT-LICENSE +22 -0
  5. data/README.md +159 -0
  6. data/Rakefile +46 -0
  7. data/app/controllers/webhookr/events_controller.rb +34 -0
  8. data/config/routes.rb +4 -0
  9. data/lib/generators/webhookr/add_route_generator.rb +12 -0
  10. data/lib/generators/webhookr/init_generator.rb +30 -0
  11. data/lib/tasks/webhookr_tasks.rake +16 -0
  12. data/lib/webhookr/adapter_response.rb +3 -0
  13. data/lib/webhookr/engine.rb +16 -0
  14. data/lib/webhookr/invalid_payload_error.rb +3 -0
  15. data/lib/webhookr/invalid_security_token_error.rb +3 -0
  16. data/lib/webhookr/ostruct_utils.rb +28 -0
  17. data/lib/webhookr/service.rb +51 -0
  18. data/lib/webhookr/services/adapter/base.rb +20 -0
  19. data/lib/webhookr/services/adapter.rb +7 -0
  20. data/lib/webhookr/services.rb +7 -0
  21. data/lib/webhookr/version.rb +3 -0
  22. data/lib/webhookr.rb +24 -0
  23. data/script/rails +8 -0
  24. data/test/dummy/README.rdoc +261 -0
  25. data/test/dummy/Rakefile +7 -0
  26. data/test/dummy/app/assets/javascripts/application.js +15 -0
  27. data/test/dummy/app/assets/stylesheets/application.css +13 -0
  28. data/test/dummy/app/controllers/application_controller.rb +3 -0
  29. data/test/dummy/app/helpers/application_helper.rb +2 -0
  30. data/test/dummy/app/mailers/.gitkeep +0 -0
  31. data/test/dummy/app/models/.gitkeep +0 -0
  32. data/test/dummy/app/views/layouts/application.html.erb +14 -0
  33. data/test/dummy/config/application.rb +65 -0
  34. data/test/dummy/config/boot.rb +10 -0
  35. data/test/dummy/config/environment.rb +5 -0
  36. data/test/dummy/config/environments/development.rb +31 -0
  37. data/test/dummy/config/environments/production.rb +64 -0
  38. data/test/dummy/config/environments/test.rb +35 -0
  39. data/test/dummy/config/initializers/backtrace_silencers.rb +7 -0
  40. data/test/dummy/config/initializers/inflections.rb +15 -0
  41. data/test/dummy/config/initializers/mime_types.rb +5 -0
  42. data/test/dummy/config/initializers/secret_token.rb +7 -0
  43. data/test/dummy/config/initializers/session_store.rb +8 -0
  44. data/test/dummy/config/initializers/webhookr.rb +2 -0
  45. data/test/dummy/config/initializers/wrap_parameters.rb +10 -0
  46. data/test/dummy/config/locales/en.yml +5 -0
  47. data/test/dummy/config/routes.rb +3 -0
  48. data/test/dummy/config.ru +4 -0
  49. data/test/dummy/lib/assets/.gitkeep +0 -0
  50. data/test/dummy/log/.gitkeep +0 -0
  51. data/test/dummy/public/404.html +26 -0
  52. data/test/dummy/public/422.html +26 -0
  53. data/test/dummy/public/500.html +25 -0
  54. data/test/dummy/public/favicon.ico +0 -0
  55. data/test/dummy/script/rails +6 -0
  56. data/test/functional/webhookr/events_controller_test.rb +76 -0
  57. data/test/functional/webhookr/events_routes_test.rb +33 -0
  58. data/test/functional/webhookr/service_test.rb +91 -0
  59. data/test/integration/webhookr/add_route_generator_test.rb +15 -0
  60. data/test/integration/webhookr/init_generator_test.rb +27 -0
  61. data/test/stubs/service_under_test_stubs.rb +73 -0
  62. data/test/test_helper.rb +18 -0
  63. data/test/unit/webhookr/Services/ServiceUnderTest/adapter_test.rb +39 -0
  64. data/test/unit/webhookr/adapter_response_test.rb +20 -0
  65. data/test/unit/webhookr/ostruct_utils_test.rb +32 -0
  66. data/test/webhookr_test.rb +11 -0
  67. data/webhookr.gemspec +22 -0
  68. metadata +171 -0
@@ -0,0 +1,26 @@
1
+ <!DOCTYPE html>
2
+ <html>
3
+ <head>
4
+ <title>The change you wanted was rejected (422)</title>
5
+ <style type="text/css">
6
+ body { background-color: #fff; color: #666; text-align: center; font-family: arial, sans-serif; }
7
+ div.dialog {
8
+ width: 25em;
9
+ padding: 0 4em;
10
+ margin: 4em auto 0 auto;
11
+ border: 1px solid #ccc;
12
+ border-right-color: #999;
13
+ border-bottom-color: #999;
14
+ }
15
+ h1 { font-size: 100%; color: #f00; line-height: 1.5em; }
16
+ </style>
17
+ </head>
18
+
19
+ <body>
20
+ <!-- This file lives in public/422.html -->
21
+ <div class="dialog">
22
+ <h1>The change you wanted was rejected.</h1>
23
+ <p>Maybe you tried to change something you didn't have access to.</p>
24
+ </div>
25
+ </body>
26
+ </html>
@@ -0,0 +1,25 @@
1
+ <!DOCTYPE html>
2
+ <html>
3
+ <head>
4
+ <title>We're sorry, but something went wrong (500)</title>
5
+ <style type="text/css">
6
+ body { background-color: #fff; color: #666; text-align: center; font-family: arial, sans-serif; }
7
+ div.dialog {
8
+ width: 25em;
9
+ padding: 0 4em;
10
+ margin: 4em auto 0 auto;
11
+ border: 1px solid #ccc;
12
+ border-right-color: #999;
13
+ border-bottom-color: #999;
14
+ }
15
+ h1 { font-size: 100%; color: #f00; line-height: 1.5em; }
16
+ </style>
17
+ </head>
18
+
19
+ <body>
20
+ <!-- This file lives in public/500.html -->
21
+ <div class="dialog">
22
+ <h1>We're sorry, but something went wrong.</h1>
23
+ </div>
24
+ </body>
25
+ </html>
File without changes
@@ -0,0 +1,6 @@
1
+ #!/usr/bin/env ruby
2
+ # This command will automatically be run when you run "rails" with Rails 3 gems installed from the root of your application.
3
+
4
+ APP_PATH = File.expand_path('../../config/application', __FILE__)
5
+ require File.expand_path('../../config/boot', __FILE__)
6
+ require 'rails/commands'
@@ -0,0 +1,76 @@
1
+
2
+ $: << File.join(File.dirname(__FILE__), %w{ .. .. })
3
+ require 'test_helper'
4
+ require 'stubs/service_under_test_stubs'
5
+
6
+ module Webhookr
7
+ class EventsControllerTest < ActionController::TestCase
8
+
9
+ include Webhookr::ServiceUnderTest
10
+
11
+ def setup
12
+ @routes = Webhookr::Engine.routes
13
+ PlainOldCallBackClass.reset!
14
+ Webhookr::ServiceUnderTest::Adapter.config.security_token = nil
15
+ @security_token = "secure_blort"
16
+ end
17
+
18
+ test ":get with no service id should return a ActionController::RoutingError" do
19
+ assert_raise(ActionController::RoutingError) {
20
+ get(:show)
21
+ }
22
+ end
23
+
24
+ test ":get with an unknown service id should return a ActionController::RoutingError" do
25
+ assert_raise(ActionController::RoutingError) {
26
+ get(:show, {:service_id => "blort"})
27
+ }
28
+ end
29
+
30
+ test ":get with known service id should return success and an empty body" do
31
+ get(:show, {:service_id => stub.service_name})
32
+ assert_response :success
33
+ assert(@response.body.blank?, "Expected an empty reponse, but got'#{@response.body}'")
34
+ end
35
+
36
+ test ":post with valid payload should return success" do
37
+ PlainOldCallBackClass.reset!
38
+ Webhookr::ServiceUnderTest::Adapter.config.callback = PlainOldCallBackClass
39
+ post(:create, {
40
+ :service_id => stub.service_name,
41
+ :event => stub.event_type,
42
+ :data => { :email => stub.email }
43
+ }
44
+ )
45
+ assert_equal 1, PlainOldCallBackClass.call_count
46
+ end
47
+
48
+ test ":get with :security_token configured and not passed should return :InvalidAuthenticityToken" do
49
+ Webhookr::ServiceUnderTest::Adapter.config.security_token = @security_token
50
+ assert_raise(ActionController::InvalidAuthenticityToken) {
51
+ get(:show, {:service_id => stub.service_name})
52
+ }
53
+ end
54
+
55
+ test ":get with :security_token configured and passed should return :success" do
56
+ Webhookr::ServiceUnderTest::Adapter.config.security_token = @security_token
57
+ get(:show, {:service_id => stub.service_name, :security_token => @security_token})
58
+ assert_response(:success)
59
+ end
60
+
61
+ test "basic auth will prevent unauthorized access" do
62
+ # pending "more time" do
63
+ # Webhookr.config.basic_auth.username = "admin"
64
+ # Webhookr.config.basic_auth.password = "password"
65
+ #
66
+ # post(:create, {
67
+ # :service_id => stub.service_name,
68
+ # :event => stub.event
69
+ # }
70
+ # )
71
+ # assert_response :unauthorized
72
+ # end
73
+ end
74
+
75
+ end
76
+ end
@@ -0,0 +1,33 @@
1
+ require 'test_helper'
2
+
3
+ module Webhookr
4
+ class EventsRoutesTest < ActionController::TestCase
5
+
6
+ def setup
7
+ @routes = Webhookr::Engine.routes
8
+ @show_controller = { :controller => "webhookr/events", :action => "show", :service_id => "service_id" }
9
+ @show_controller_with_token = @show_controller.merge({ :security_token => "secure" })
10
+ @create_controller = { :controller => "webhookr/events", :action => "create", :service_id => "service_id" }
11
+ @create_controller_with_token = @create_controller.merge({ :security_token => "secure" })
12
+ @path = "/events/service_id"
13
+ @path_with_token = "/events/service_id/secure"
14
+ end
15
+
16
+ test ":get route to events" do
17
+ assert_recognizes(@show_controller, { :path => @path, :method => :get })
18
+ end
19
+
20
+ test ":get route to events with optional :security_token" do
21
+ assert_recognizes(@show_controller_with_token, { :path => @path_with_token, :method => :get })
22
+ end
23
+
24
+ test ":post route to events" do
25
+ assert_recognizes(@create_controller, { :path => @path, :method => :post })
26
+ end
27
+
28
+ test ":post route to events with optional :security_token" do
29
+ assert_recognizes(@create_controller_with_token, { :path => @path_with_token, :method => :post })
30
+ end
31
+
32
+ end
33
+ end
@@ -0,0 +1,91 @@
1
+ require 'test_helper'
2
+ require 'stubs/service_under_test_stubs'
3
+
4
+ module Webhookr
5
+ class ServiceTest < ActiveSupport::TestCase
6
+
7
+ include Webhookr::ServiceUnderTest
8
+
9
+ test "should raise NameError for a nil service" do
10
+ assert_raise(NameError) {
11
+ Webhookr::Service.new(nil)
12
+ }
13
+ end
14
+
15
+ test "should raise NameError for an unknown service" do
16
+ assert_raise(NameError) {
17
+ Webhookr::Service.new("unknown_service")
18
+ }
19
+ end
20
+
21
+ test "should raise a Webhookr::InvalidPayloadError for an invalid payload" do
22
+ assert_raise(Webhookr::InvalidPayloadError) {
23
+ Webhookr::Service.new(stub.service_name,
24
+ :payload => "blort").process!
25
+ }
26
+ end
27
+
28
+ test "should raise a Runtime error if there is no callback class configured" do
29
+ Webhookr::ServiceUnderTest::Adapter.config.callback = nil
30
+ assert_raise(RuntimeError) {
31
+ Webhookr::Service.new(stub.service_name,
32
+ :payload => stub.payload).process!
33
+ }
34
+ end
35
+
36
+ end
37
+
38
+ class ServiceTest < ActiveSupport::TestCase
39
+
40
+ include Webhookr::ServiceUnderTest
41
+
42
+ def setup
43
+ Webhookr::ServiceUnderTest::Adapter.config.callback = PlainOldCallBackClass
44
+ PlainOldCallBackClass.reset!
45
+ end
46
+
47
+ test "should accept a callback class configuration" do
48
+ assert_nothing_raised {
49
+ Webhookr::ServiceUnderTest::Adapter.config.callback = Object
50
+ }
51
+ end
52
+
53
+ test "process! should not raise a Webhookr::InvalidPayloadError for a valid payload" do
54
+ assert_nothing_raised {
55
+ Webhookr::Service.new(stub.service_name,
56
+ :payload => stub.payload).process!
57
+ }
58
+ end
59
+
60
+ test "a service should be initialized" do
61
+ # pending "more time" do
62
+ # assert_false
63
+ # end
64
+ end
65
+
66
+ test "process! should silently ignore on_event not present in callback class" do
67
+ assert_nothing_raised {
68
+ Webhookr::Service.new(stub.service_name,
69
+ :payload => stub(:event_type => "no_event").payload).process!
70
+ }
71
+ end
72
+
73
+ test "process! should call the on_event method of the callback class for a valid payload" do
74
+ Webhookr::Service.new(stub.service_name, :payload => stub.payload).process!
75
+ assert_equal PlainOldCallBackClass.call_count, 1
76
+ end
77
+
78
+ test "process! should have the payload data email" do
79
+ email = "test@test.com"
80
+ Webhookr::Service.new(stub.service_name,
81
+ :payload => stub(:email => email).payload).process!
82
+ assert_equal(email, PlainOldCallBackClass.email)
83
+ end
84
+
85
+ test "process! should process multiple records" do
86
+ Webhookr::Service.new(stub.service_name,
87
+ :payload => [ stub.payload, stub.payload ]).process!
88
+ assert_equal 2, PlainOldCallBackClass.call_count
89
+ end
90
+ end
91
+ end
@@ -0,0 +1,15 @@
1
+
2
+ $: << File.join(File.dirname(__FILE__), %w{ .. .. })
3
+ require 'test_helper'
4
+ require 'generators/webhookr/add_route_generator'
5
+
6
+ class InitGeneratorTest < Rails::Generators::TestCase
7
+ tests Webhookr::Generators::AddRouteGenerator
8
+ destination File.expand_path("../../../tmp", File.dirname(__FILE__))
9
+ setup :prepare_destination
10
+
11
+ test "it should add the engine to routes" do
12
+ run_generator
13
+ # TODO: Not sure how to confirm the route was added, so just run it for now
14
+ end
15
+ end
@@ -0,0 +1,27 @@
1
+
2
+ $: << File.join(File.dirname(__FILE__), %w{ .. .. })
3
+ require 'test_helper'
4
+ require 'generators/webhookr/init_generator'
5
+
6
+ class InitGeneratorTest < Rails::Generators::TestCase
7
+ tests Webhookr::Generators::InitGenerator
8
+ destination File.expand_path("../../../tmp", File.dirname(__FILE__))
9
+ setup :prepare_destination
10
+
11
+ def setup
12
+ @name = "test_initializer"
13
+ @initializer = "config/initializers/#{@name}.rb"
14
+ run_generator Array.wrap(@name)
15
+ end
16
+
17
+ test "it should create the initializer" do
18
+ assert_file @initializer
19
+ end
20
+
21
+ test "it should have authorization information" do
22
+ assert_file @initializer do |content|
23
+ assert_match(%r{basic_auth\.username}, content)
24
+ assert_match(%r{basic_auth\.password}, content)
25
+ end
26
+ end
27
+ end
@@ -0,0 +1,73 @@
1
+ module Webhookr
2
+ module ServiceUnderTest
3
+ class Adapter
4
+ SERVICE_NAME = 'service_under_test'
5
+
6
+ include Webhookr::Services::Adapter::Base
7
+
8
+ class << self
9
+ def process(payload)
10
+ [*payload].collect do |p|
11
+ p = Rack::Utils.parse_nested_query(p)
12
+ validate(payload)
13
+ OpenStruct.new({
14
+ :event_type => p["event"],
15
+ :data => OpenStruct.new(p["data"])
16
+ })
17
+ end
18
+ end
19
+
20
+ def validate(payload)
21
+ if payload.nil? || payload == "blort"
22
+ raise Webhookr::InvalidPayloadError.new("'#{payload}' is not valid")
23
+ end
24
+ end
25
+
26
+ end
27
+ end
28
+ end
29
+ end
30
+
31
+ module Webhookr
32
+ module ServiceUnderTest
33
+
34
+ def stub(options = {})
35
+ ops = {
36
+ :service_name => "service_under_test",
37
+ :event_type => "test_event",
38
+ :email => "gerry@zoocasa.com"
39
+ }.merge(options)
40
+
41
+ OpenStruct.new({
42
+ :payload => "event=#{ops[:event_type]}&data[email]=#{ops[:email]}",
43
+ :service_name => ops[:service_name],
44
+ :event_type => ops[:event_type],
45
+ :email => ops[:email]
46
+ })
47
+ end
48
+
49
+ end
50
+ end
51
+
52
+ class PlainOldCallBackClass
53
+ @@call_count = 0
54
+ @@email = nil
55
+
56
+ def self.reset!
57
+ @@call_count = 0
58
+ @@email = nil
59
+ end
60
+
61
+ def self.call_count
62
+ @@call_count
63
+ end
64
+
65
+ def self.email
66
+ @@email
67
+ end
68
+
69
+ def on_test_event(payload)
70
+ @@call_count += 1
71
+ @@email = payload.data.email
72
+ end
73
+ end
@@ -0,0 +1,18 @@
1
+ require 'simplecov'
2
+ SimpleCov.start
3
+
4
+ # Configure Rails Environment
5
+ ENV["RAILS_ENV"] = "test"
6
+
7
+ require File.expand_path("../dummy/config/environment.rb", __FILE__)
8
+ require "rails/test_help"
9
+ require "rails/generators/test_case"
10
+ require "minitest/reporters"
11
+
12
+ Rails.backtrace_cleaner.remove_silencers!
13
+
14
+ # Load support files
15
+ Dir["#{File.dirname(__FILE__)}/support/**/*.rb"].each { |f| require f }
16
+
17
+ MiniTest::Reporters.use!(MiniTest::Reporters::SpecReporter.new)
18
+
@@ -0,0 +1,39 @@
1
+ require 'test_helper'
2
+ require 'stubs/service_under_test_stubs'
3
+
4
+ module Webhookr
5
+ module Services
6
+ class ServiceUnderTest::AdapterTest < ActiveSupport::TestCase
7
+
8
+ def setup
9
+ @event_type = "test_event"
10
+ @data_msg = "Hello world!"
11
+ @valid_response = "event=#{@event_type}&data[msg]=#{@data_msg}"
12
+ @invalid_response = "blort"
13
+ end
14
+
15
+ test "should not raise an exception for a valid payload" do
16
+ assert_nothing_raised {
17
+ Webhookr::ServiceUnderTest::Adapter.process(@valid_response)
18
+ }
19
+ end
20
+
21
+ test "should raise an exception for a invalid payload" do
22
+ assert_raise(Webhookr::InvalidPayloadError) {
23
+ Webhookr::ServiceUnderTest::Adapter.process(@invalid_response)
24
+ }
25
+ end
26
+
27
+ test "should repond with the correct event" do
28
+ responses = Webhookr::ServiceUnderTest::Adapter.process(@valid_response)
29
+ assert_equal(@event_type, responses.first.event_type)
30
+ end
31
+
32
+ test "should respond with valid data" do
33
+ responses = Webhookr::ServiceUnderTest::Adapter.process(@valid_response)
34
+ assert_equal(@data_msg, responses.first.data.msg)
35
+ end
36
+
37
+ end
38
+ end
39
+ end
@@ -0,0 +1,20 @@
1
+ require 'test_helper'
2
+
3
+ class AdapterResponseTest < ActiveSupport::TestCase
4
+ def setup
5
+ @subject = Webhookr::AdapterResponse.new("service_name", "event_type", "payload")
6
+ end
7
+
8
+ test "should respond to event_type" do
9
+ assert_respond_to(@subject, :event_type)
10
+ end
11
+
12
+ test "should respond to payload" do
13
+ assert_respond_to(@subject, :payload)
14
+ end
15
+
16
+ test "should respond to service_name" do
17
+ assert_respond_to(@subject, :service_name)
18
+ end
19
+
20
+ end
@@ -0,0 +1,32 @@
1
+ require 'test_helper'
2
+ require 'webhookr/ostruct_utils'
3
+ require 'ostruct'
4
+
5
+ class OstructUtilsTest < ActiveSupport::TestCase
6
+
7
+ def setup
8
+ @hash = { :a => { :b => { :c => 1 } }, :a1 => [ { :b1 => { :c1 => 1 } } ] }
9
+ @converted = Webhookr::OstructUtils.to_ostruct(@hash)
10
+ end
11
+
12
+ test "should be an OpenStruct" do
13
+ assert(@converted.is_a?(OpenStruct))
14
+ end
15
+
16
+ test "should have a nested OpenStruct" do
17
+ assert(@converted.a.is_a?(OpenStruct))
18
+ end
19
+
20
+ test "should have a nested nested OpenStruct" do
21
+ assert(@converted.a.b.is_a?(OpenStruct))
22
+ end
23
+
24
+ test "should have a nested nested nested value of 1" do
25
+ assert(@converted.a.b.c == 1)
26
+ end
27
+
28
+ test "should replace a nested hash in an array" do
29
+ assert(@converted.a1.first.b1.c1 == 1)
30
+ end
31
+
32
+ end
@@ -0,0 +1,11 @@
1
+ require 'test_helper'
2
+
3
+ class WebhookrTest < ActiveSupport::TestCase
4
+ test "should have Webhookr configuration" do
5
+ assert_kind_of Hash, Webhookr.config
6
+ end
7
+
8
+ test "should have a basic_auth hash" do
9
+ assert_kind_of Hash, Webhookr.config.basic_auth
10
+ end
11
+ end
data/webhookr.gemspec ADDED
@@ -0,0 +1,22 @@
1
+ # -*- encoding: utf-8 -*-
2
+
3
+ require File.expand_path('../lib/webhookr/version', __FILE__)
4
+
5
+ Gem::Specification.new do |s|
6
+ s.name = "webhookr"
7
+ s.version = Webhookr::VERSION
8
+
9
+ s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
10
+ s.authors = ["Gerry Power"]
11
+ s.email = ["code@zoocasa.com"]
12
+ s.description = "Webhookr - easily and securely add webhooks to your Rails app."
13
+ s.summary = s.description
14
+
15
+ s.files = `git ls-files`.split($\)
16
+ s.executables = s.files.grep(%r{^bin/}).map { |f| File.basename(f) }
17
+ s.test_files = s.files.grep(%r{^(test|spec|features)/})
18
+ s.homepage = "http://github.com/zoocasa/webhookr"
19
+ s.require_paths = ["lib"]
20
+
21
+ s.add_dependency "rails", ["~> 3.1"]
22
+ end