-
Notifications
You must be signed in to change notification settings - Fork 4
/
sinhook.rb
215 lines (178 loc) · 5.67 KB
/
sinhook.rb
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
require_relative 'hooks.rb'
require_relative 'config/config'
require 'sinatra/base'
require 'pry'
require 'json'
class SinHook < Sinatra::Base
# load configuration file
def self.config
return @config unless @config.nil?
@config = App.config.load!(:general, ENV['ENCRYPTED_YAML'].to_s.strip.downcase.include?('true'))
# update config with environment variables if they exist
@config.keys.each { |key| @config[key] = ENV[key.to_s.upcase] unless ENV[key.to_s.upcase].nil? }
@config
end
if config[:app_basic_auth].to_s.strip == 'true'
use Rack::Auth::Basic, "Restricted Area" do |username, password|
username == config[:app_username] and password == config[:app_password]
end
end
# Configuration settings for the app.
configure do
set :bind, '0.0.0.0'
set :app_settings, config
set :environment, :production
set :hooks, Hooks::Data.new(app_settings)
set :hooks_responses, Hooks::Responses.new
end
# Helpers which allow easy management of webhooks.
module HooksHelper
RESPONSE_TYPES = %i[status delay message].freeze
MAXIMUM_SECONDS_DELAY = 3600
def hooks
settings.hooks
end
def hooks_responses
settings.hooks_responses
end
def hook_found?(hook_id)
halt 404 unless settings.hooks.is_available?(hook_id)
end
def valid_http_code?(number)
number >= 200 && number <= 600
end
def seconds_delay(seconds)
seconds > 0 && seconds < MAXIMUM_SECONDS_DELAY ? seconds : 0
end
def response_message(data)
if data.is_a?(Hash)
data.to_json
else
response = {}
response['Message'] = data
response.to_json
end
end
def response_status(value)
valid_http_code?(value) ? status(value) : status(500)
end
def response_delay(value)
sleep value
end
def execute_responses(hook_id)
settings.hooks_responses.get(hook_id).each {|key, value| send("response_#{key}", value)}
end
end
helpers HooksHelper
# ENDPOINTS:
# Generate new webhook endpoint.
# Default or with name passed by parameter 'name'
post '/hook/generate', provides: :json do
hook_id = params[:name]
if hooks.is_available?(hook_id)
response_status(405)
response_message("Web hook with id: #{hook_id} already exists.")
elsif hook_id.eql?('list')
response_status(405)
response_message("#{hook_id} is a reserved word.")
else
hook_id = settings.hooks.create(hook_id)
response_message({
'Message' => 'New webhook endopint created.',
'HookUrl' => "#{url.match(/(.*)\/generate/)[1]}/#{hook_id}",
})
end
end
# Accept data on specific webhook endpoint.
post '/hook/:hook_id' do
hook_id = params[:hook_id]
hook_found?(hook_id)
execute_responses(hook_id)
hooks.set_data(hook_id, request.env['rack.input'].read)
hooks.read_data(hook_id)
end
# Delete existing webhook endpoint.
delete '/hook/:hook_id' do
hook_id = params[:hook_id]
hook_found?(hook_id)
if hooks.delete(hook_id)
response_message("Endpoint #{hook_id} deleted.")
else
response_status(500)
response_message("Could not delete hook with id: #{hook_id}.")
end
end
get '/hook/list', provides: :json do
hooks.endpoints.map { |e| "#{url.match(/(.*)\/list/)[1]}/#{e}" }.to_json
end
# Read specific webhook endpoint response.
get '/hook/:hook_id', provides: :json do
hook_id = params[:hook_id]
hook_found?(hook_id)
execute_responses(hook_id)
hooks.read_data(hook_id)
end
# Update webhook endpoint, so that post/get /hook/:endpoint return modified response
# Optional modification parameters:
#
# ?response_status=500 (any valid http number)
# ?response_delay=5 (seconds, any valid number)
# ?clear=data
# ?clear=responses
# ?clear=data,responses
put '/hook/:hook_id' do
hook_id = params[:hook_id]
hook_found?(hook_id)
message = []
# Clear all webhook data.
if params[:clear].to_s.include? 'data'
hooks.clear_data(params[:hook_id])
message << 'Cleared data.'
end
# Clear all webhook response modifications, like delayed response.
if params[:clear].to_s.include? 'response'
hooks_responses.clear(hook_id)
message << 'Cleared response modifications.'
end
responses = {}
responses[:status] = params[:response_status].to_i
responses[:delay] = params[:response_delay].to_i
responses.each do |type, response|
unless response == 0
hooks_responses.add(hook_id, type, response)
message << "#{type.to_s.capitalize} set to #{response}."
end
end
response_message(message.join(' '))
end
# Webhook endpoint with sole purpose to return response after :seconds seconds
%i[get post put].each do |method|
send method, '/hook/delay/:seconds' do
seconds = seconds_delay(params[:seconds].to_i)
response_delay(seconds)
response_message("Delayed response for #{seconds} seconds.")
end
end
# Webhook endpoint with sole purpose to return response with :status_code status
%i[get post put].each do |method|
# get hook data
send method, '/hook/status/:status_code' do
http_code = params[:status_code].to_i
response_status(http_code)
response_message("Returned status: #{http_code}.")
end
end
get '/robots.txt', provides: :text do
"User-agent: *\nDisallow: /"
end
get '/' do
'Sinhook says hello.'
end
# GENERAL ENDPOINTS:
not_found do
halt 404, response_message('End point not found.')
end
error do
response_message("Ooops, sorry there was a nasty error - #{env['sinatra.error'].name}.")
end
end