firetail 0.0.0 → 1.0.1
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.
- checksums.yaml +4 -4
- data/.github/pull_request_template.md +11 -0
- data/.github/workflows/main.yml +40 -0
- data/.gitignore +28 -0
- data/Gemfile +5 -0
- data/Gemfile.lock +74 -0
- data/LICENSE +165 -0
- data/LICENSE.txt +165 -21
- data/README.md +24 -10
- data/examples/rails/.gitattributes +8 -0
- data/examples/rails/.gitignore +31 -0
- data/examples/rails/.rspec +1 -0
- data/examples/rails/.ruby-version +1 -0
- data/examples/rails/Gemfile +44 -0
- data/examples/rails/Gemfile.lock +240 -0
- data/examples/rails/README.md +24 -0
- data/examples/rails/Rakefile +6 -0
- data/examples/rails/app/channels/application_cable/channel.rb +4 -0
- data/examples/rails/app/channels/application_cable/connection.rb +4 -0
- data/examples/rails/app/controllers/application_controller.rb +2 -0
- data/examples/rails/app/controllers/comments_controller.rb +52 -0
- data/examples/rails/app/controllers/concerns/.keep +0 -0
- data/examples/rails/app/controllers/posts_controller.rb +51 -0
- data/examples/rails/app/jobs/application_job.rb +7 -0
- data/examples/rails/app/mailers/application_mailer.rb +4 -0
- data/examples/rails/app/models/application_record.rb +3 -0
- data/examples/rails/app/models/comment.rb +4 -0
- data/examples/rails/app/models/concerns/.keep +0 -0
- data/examples/rails/app/models/post.rb +5 -0
- data/examples/rails/app/views/layouts/mailer.html.erb +13 -0
- data/examples/rails/app/views/layouts/mailer.text.erb +1 -0
- data/examples/rails/bin/bundle +114 -0
- data/examples/rails/bin/rails +5 -0
- data/examples/rails/bin/rake +5 -0
- data/examples/rails/bin/setup +33 -0
- data/examples/rails/bin/spring +14 -0
- data/examples/rails/config/application.rb +41 -0
- data/examples/rails/config/boot.rb +4 -0
- data/examples/rails/config/cable.yml +10 -0
- data/examples/rails/config/credentials.yml.enc +1 -0
- data/examples/rails/config/database.yml +25 -0
- data/examples/rails/config/environment.rb +5 -0
- data/examples/rails/config/environments/development.rb +66 -0
- data/examples/rails/config/environments/production.rb +113 -0
- data/examples/rails/config/environments/test.rb +60 -0
- data/examples/rails/config/firetail.yml +2 -0
- data/examples/rails/config/initializers/application_controller_renderer.rb +8 -0
- data/examples/rails/config/initializers/backtrace_silencers.rb +8 -0
- data/examples/rails/config/initializers/cors.rb +16 -0
- data/examples/rails/config/initializers/filter_parameter_logging.rb +6 -0
- data/examples/rails/config/initializers/inflections.rb +16 -0
- data/examples/rails/config/initializers/mime_types.rb +4 -0
- data/examples/rails/config/initializers/wrap_parameters.rb +14 -0
- data/examples/rails/config/locales/en.yml +33 -0
- data/examples/rails/config/puma.rb +43 -0
- data/examples/rails/config/routes.rb +6 -0
- data/examples/rails/config/schema.json +431 -0
- data/examples/rails/config/spring.rb +6 -0
- data/examples/rails/config/storage.yml +34 -0
- data/examples/rails/config.ru +6 -0
- data/examples/rails/db/migrate/20230730163722_create_posts.rb +8 -0
- data/examples/rails/db/migrate/20230730163741_create_comments.rb +9 -0
- data/examples/rails/db/migrate/20230730164121_add_fields_to_post.rb +6 -0
- data/examples/rails/db/migrate/20230730164214_add_fields_to_comments.rb +6 -0
- data/examples/rails/db/schema.rb +30 -0
- data/examples/rails/db/seeds.rb +7 -0
- data/examples/rails/lib/tasks/.keep +0 -0
- data/examples/rails/log/.keep +0 -0
- data/examples/rails/public/robots.txt +1 -0
- data/examples/rails/spec/models/comment_spec.rb +5 -0
- data/examples/rails/spec/models/post_spec.rb +5 -0
- data/examples/rails/spec/rails_helper.rb +63 -0
- data/examples/rails/spec/requests/comments_spec.rb +127 -0
- data/examples/rails/spec/requests/posts_spec.rb +127 -0
- data/examples/rails/spec/routing/comments_routing_spec.rb +30 -0
- data/examples/rails/spec/routing/posts_routing_spec.rb +30 -0
- data/examples/rails/spec/spec_helper.rb +94 -0
- data/examples/rails/storage/.keep +0 -0
- data/examples/rails/test/channels/application_cable/connection_test.rb +11 -0
- data/examples/rails/test/controllers/.keep +0 -0
- data/examples/rails/test/fixtures/files/.keep +0 -0
- data/examples/rails/test/integration/.keep +0 -0
- data/examples/rails/test/mailers/.keep +0 -0
- data/examples/rails/test/models/.keep +0 -0
- data/examples/rails/test/test_helper.rb +13 -0
- data/examples/rails/tmp/.keep +0 -0
- data/examples/rails/tmp/pids/.keep +0 -0
- data/examples/rails/vendor/.keep +0 -0
- data/firetail.gemspec +8 -1
- data/lib/backend.rb +34 -0
- data/lib/background_tasks.rb +43 -0
- data/lib/case_sensitive_headers.rb +29 -0
- data/lib/firetail/version.rb +1 -1
- data/lib/firetail.rb +264 -1
- data/lib/generators/firetail/install/templates/README +15 -0
- data/lib/generators/firetail/install/templates/firetail.yml +2 -0
- data/lib/generators/firetail/install/templates/schema.json +108 -0
- data/lib/generators/firetail/install_generator.rb +26 -0
- data/lib/railtie.rb +29 -0
- metadata +169 -8
data/lib/firetail.rb
CHANGED
|
@@ -1,6 +1,269 @@
|
|
|
1
1
|
require "firetail/version"
|
|
2
|
+
require "rack"
|
|
3
|
+
require 'objspace'
|
|
4
|
+
require 'yaml'
|
|
5
|
+
require 'json'
|
|
6
|
+
require 'net/http'
|
|
7
|
+
require 'case_sensitive_headers' # a hack because firetail API headers is case-sensitive
|
|
8
|
+
require "async"
|
|
9
|
+
require 'digest/sha1'
|
|
10
|
+
require 'jwt'
|
|
11
|
+
require 'logger'
|
|
12
|
+
require 'background_tasks'
|
|
13
|
+
require 'committee'
|
|
14
|
+
|
|
15
|
+
# If the library detects rails, it will load rail's methods
|
|
16
|
+
if defined?(Rails)
|
|
17
|
+
require 'action_dispatch'
|
|
18
|
+
require 'action_pack'
|
|
19
|
+
require 'railtie'
|
|
20
|
+
end
|
|
2
21
|
|
|
3
22
|
module Firetail
|
|
4
23
|
class Error < StandardError; end
|
|
5
|
-
|
|
24
|
+
|
|
25
|
+
class Run
|
|
26
|
+
MAX_BULK_SIZE_IN_BYTES = 1 * 1024 * 1024 # 1 MB
|
|
27
|
+
|
|
28
|
+
def initialize app
|
|
29
|
+
@app = app
|
|
30
|
+
@reqres ||= [] # request data in stored in array memory
|
|
31
|
+
@init_time ||= Time.now # initialize time
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
def call(env)
|
|
35
|
+
# This block initialises the configuration and checks
|
|
36
|
+
# sets the values for certain necessary configuration
|
|
37
|
+
# If it is Rails
|
|
38
|
+
if defined?(Rails)
|
|
39
|
+
begin
|
|
40
|
+
default_location = File.join(Rails.root, "config/firetail.yml")
|
|
41
|
+
config = YAML.safe_load(ERB.new(File.read(default_location)).result)
|
|
42
|
+
rescue Errno::ENOENT
|
|
43
|
+
# error message if firetail is not installed
|
|
44
|
+
puts ""
|
|
45
|
+
puts "Please run 'rails generate firetail:install' first"
|
|
46
|
+
puts ""
|
|
47
|
+
end
|
|
48
|
+
else # other frameworks
|
|
49
|
+
config = YAML.load_file("firetail.yml")
|
|
50
|
+
end
|
|
51
|
+
|
|
52
|
+
raise Error.new "Please run 'rails generate firetail:install' first" if config.nil?
|
|
53
|
+
raise Error.new "API Key is missing from firetail.yml configuration" if config['api_key'].nil?
|
|
54
|
+
|
|
55
|
+
@api_key = config['api_key']
|
|
56
|
+
@url = config['url'] ? config['url'] : "https://api.logging.eu-west-1.prod.firetail.app/logs/bulk" # default goes to europe
|
|
57
|
+
@log_drains_timeout = config['log_drains_timeout'] ? config['log_drains_timeout'] : 5
|
|
58
|
+
@network_timeout = config['network_timeout'] ? config['network_timeout'] : 10
|
|
59
|
+
@number_of_retries = config['number_of_retries'] ? config['number_of_retries'] : 4
|
|
60
|
+
@retry_timeout = config['retry_timeout'] ? config['retry_timeout'] : 2
|
|
61
|
+
# End of configuration initialization
|
|
62
|
+
|
|
63
|
+
# Gets the rack middleware requests
|
|
64
|
+
@request = Rack::Request.new(env)
|
|
65
|
+
started_on = Time.now
|
|
66
|
+
begin
|
|
67
|
+
status, client_headers, body = response = @app.call(env)
|
|
68
|
+
log(env, status, body, started_on, Time.now)
|
|
69
|
+
rescue Exception => exception
|
|
70
|
+
log(env, status, body, started_on, Time.now, exception)
|
|
71
|
+
raise exception
|
|
72
|
+
end
|
|
73
|
+
|
|
74
|
+
response
|
|
75
|
+
end
|
|
76
|
+
|
|
77
|
+
def log(env,
|
|
78
|
+
status,
|
|
79
|
+
body,
|
|
80
|
+
started_on,
|
|
81
|
+
ended_on,
|
|
82
|
+
exception = nil)
|
|
83
|
+
|
|
84
|
+
# request values
|
|
85
|
+
time_spent = ended_on - started_on
|
|
86
|
+
request_ip = defined?(Rails) ? env['action_dispatch.remote_ip'].calculate_ip : env['REMOTE_ADDR']
|
|
87
|
+
request_method = env['REQUEST_METHOD']
|
|
88
|
+
request_path = env['REQUEST_PATH']
|
|
89
|
+
request_http_version = env['HTTP_VERSION']
|
|
90
|
+
|
|
91
|
+
# get the resource parameters if it is rails
|
|
92
|
+
if defined?(Rails)
|
|
93
|
+
resource = Rails.application.routes.recognize_path(request_path)
|
|
94
|
+
#Firetail.logger.debug "res: #{resource}"
|
|
95
|
+
# sample hash of the above resource:
|
|
96
|
+
# example url: /posts/1/comments/2/options/3
|
|
97
|
+
# hash = {:controller=>"options", :action=>"show", :comment_id => 3, :post_id=>"1", :id=>"1"}
|
|
98
|
+
# take the resource hash above, get keys, conver to string, split "_" to get name at first index, together
|
|
99
|
+
# with the key, to string and camelcase route id name and keys that only include "id", compact (remove nil) and add "s" to the key
|
|
100
|
+
rmap = resource.map {|k,v| [k.to_s.split("_")[0], "{#{k.to_s.camelize(:lower)}}"] if k.to_s.include? "id" }
|
|
101
|
+
.compact.map {|k,v| [k.to_s + "s", v] if k != "id" }
|
|
102
|
+
|
|
103
|
+
if resource.key? :id
|
|
104
|
+
# It will appear like: [["comments", "commentId"], ["posts", "postId"], ["id", "id"]],
|
|
105
|
+
# but we want post to be first in order, so we reverse sort, and drop "id", which will be first in array
|
|
106
|
+
# after being sorted
|
|
107
|
+
reverse_resource = rmap.reverse.drop(1)
|
|
108
|
+
resource_path = "/" + reverse_resource * "/" + "/" + resource[:controller] + "/" + "{id}"
|
|
109
|
+
# rebuild the resource path
|
|
110
|
+
# reverse_resource * "/" will loop the array and add "/"
|
|
111
|
+
#resource_path = "/" + reverse_resource * "/" + "/" + resource[:controller] + "/" + "{id}"
|
|
112
|
+
# end result is /posts/{postId}/comments/{commentId}/options/{id}
|
|
113
|
+
else
|
|
114
|
+
if rmap.empty?
|
|
115
|
+
# if resoruce is empty, means we are at the first level of the url path, so no need extra paths
|
|
116
|
+
resource_path = "/" + rmap * "/" + resource[:controller]
|
|
117
|
+
else
|
|
118
|
+
# resource path from rmap above without the [:id] key (which is the last parameter in URL)
|
|
119
|
+
# only used for index, create which does not have id
|
|
120
|
+
resource_path = "/" + rmap * "/" + "/" + resource[:controller]
|
|
121
|
+
end
|
|
122
|
+
end
|
|
123
|
+
else
|
|
124
|
+
resource_path = nil
|
|
125
|
+
end
|
|
126
|
+
|
|
127
|
+
#Firetail.logger.debug("resource path: #{resource_path}")
|
|
128
|
+
# select those with "HTTP_" prefix, these are request headers
|
|
129
|
+
request_headers = env.select {|key,val| key.start_with? 'HTTP_' } # find HTTP_ prefixes, these are requests only
|
|
130
|
+
.collect {|key, val| { "#{key.sub(/^HTTP_/, '')}": [val] }} # remove HTTP_ prefix
|
|
131
|
+
.reduce({}, :merge) # reduce from [{key:val},{key2: val2}] to {key: val, key2: val2}
|
|
132
|
+
|
|
133
|
+
# do the inverse of the above and get rack specific keys
|
|
134
|
+
response_headers = env.select {|key,val| !key.start_with? 'HTTP_' } # only keys with no HTTP_ prefix
|
|
135
|
+
.select {|key, val| key =~ /^[A-Z._]*$/} # select keys with uppercase and underline
|
|
136
|
+
.map {|key, val| { "#{key}": [val] }} # map to firetail api format
|
|
137
|
+
.reduce({}, :merge) # reduce from [{key:val},{key2: val2}] to {key: val, key2: val2}
|
|
138
|
+
|
|
139
|
+
# get the jwt "sub" information
|
|
140
|
+
if request_headers[:AUTHORIZATION]
|
|
141
|
+
subject = self.jwt_decoder(request_headers[:AUTHORIZATION])
|
|
142
|
+
else
|
|
143
|
+
subject = nil
|
|
144
|
+
end
|
|
145
|
+
|
|
146
|
+
# default time spent in ruby is in seconds, so multiple by 1000 to ms
|
|
147
|
+
time_spent_in_ms = time_spent * 1000
|
|
148
|
+
#Firetail.logger.debug "request params: #{@request.params.inspect}"
|
|
149
|
+
# add the request and response data
|
|
150
|
+
# to array of data for batching up
|
|
151
|
+
@request.body.rewind
|
|
152
|
+
if body.is_a? Array
|
|
153
|
+
body = body[0]
|
|
154
|
+
else
|
|
155
|
+
body = body.body
|
|
156
|
+
end
|
|
157
|
+
@reqres.push({
|
|
158
|
+
version: "1.0.0-alpha",
|
|
159
|
+
dateCreated: Time.now.utc.to_i,
|
|
160
|
+
executionTime: time_spent_in_ms,
|
|
161
|
+
request: {
|
|
162
|
+
httpProtocol: request_http_version,
|
|
163
|
+
headers: request_headers, # headers must be in: headers: {"key": ["value"]}, array in object
|
|
164
|
+
method: request_method,
|
|
165
|
+
body: @request.body.read,
|
|
166
|
+
ip: request_ip,
|
|
167
|
+
resource: resource_path,
|
|
168
|
+
uri: @request.url
|
|
169
|
+
},
|
|
170
|
+
response: {
|
|
171
|
+
statusCode: status,
|
|
172
|
+
body: body,
|
|
173
|
+
headers: response_headers,
|
|
174
|
+
}
|
|
175
|
+
})
|
|
176
|
+
@request.body.rewind
|
|
177
|
+
#Firetail.logger.debug "Request: #{body}"
|
|
178
|
+
|
|
179
|
+
# the time we calculate if request that is
|
|
180
|
+
# buffered max is 120 seconds
|
|
181
|
+
current_time = Time.now
|
|
182
|
+
# duration in millseconds
|
|
183
|
+
duration = (current_time - @init_time)
|
|
184
|
+
|
|
185
|
+
#Firetail.logger.debug "size in bytes #{ObjectSpace.memsize_of(@request_data.to_s)}"
|
|
186
|
+
#request data size in bytes
|
|
187
|
+
request_data_size = ObjectSpace.memsize_of(@request_data)
|
|
188
|
+
# It is difficult to calculate the object size in bytes,
|
|
189
|
+
# seems to not return the accurate values
|
|
190
|
+
|
|
191
|
+
# This will send the data we need in batches of 5 requests or when it is more than 120 seconds
|
|
192
|
+
# if there are more than 5 requests or is more than
|
|
193
|
+
# 2 minutes, then send to backend - this is for testing
|
|
194
|
+
if @reqres.length >= 5 || duration > 120
|
|
195
|
+
#Firetail.logger.debug "request data #{@reqres}"
|
|
196
|
+
# we parse the data hash into json-nl (json-newlines)
|
|
197
|
+
payload = @reqres.map { |data| JSON.generate(data) }.join("\n")
|
|
198
|
+
|
|
199
|
+
# send the data to backend API
|
|
200
|
+
# This is an async task
|
|
201
|
+
BackgroundTasks.http_task(@url,
|
|
202
|
+
@network_timeout,
|
|
203
|
+
@api_key,
|
|
204
|
+
@number_of_tries,
|
|
205
|
+
payload)
|
|
206
|
+
|
|
207
|
+
# reset back to the initial conditions
|
|
208
|
+
payload = nil
|
|
209
|
+
@reqres = []
|
|
210
|
+
@init_time = Time.now
|
|
211
|
+
end
|
|
212
|
+
rescue Exception => exception
|
|
213
|
+
Firetail.logger.error(exception.message)
|
|
214
|
+
end
|
|
215
|
+
|
|
216
|
+
def sha1_hash(value)
|
|
217
|
+
encode_utf8 = value.encode(Encoding::UTF_8)
|
|
218
|
+
hash = Digest::SHA1.hexdigest(encode_utf8)
|
|
219
|
+
sha1 = "sha1: #{hash}"
|
|
220
|
+
end
|
|
221
|
+
|
|
222
|
+
def jwt_decoder(value)
|
|
223
|
+
bearer_string = value
|
|
224
|
+
# if authorization exists, get the value at index
|
|
225
|
+
# split the values which has a space (example: "Bearer 123") and
|
|
226
|
+
# get the value at index 1
|
|
227
|
+
token = bearer_string.split(" ")[1]
|
|
228
|
+
# decode the token
|
|
229
|
+
jwt_value = JWT.decode token, nil, false
|
|
230
|
+
# get the subject value
|
|
231
|
+
subject = jwt_value[0]["sub"]
|
|
232
|
+
#Firetail.logger.debug("subject value: #{subject}")
|
|
233
|
+
end
|
|
234
|
+
end
|
|
235
|
+
|
|
236
|
+
def self.logger
|
|
237
|
+
@@logger ||= defined?(Rails) ? Rails.logger : Logger.new(STDOUT)
|
|
238
|
+
end
|
|
239
|
+
|
|
240
|
+
def self.logger=(logger)
|
|
241
|
+
@@logger = logger
|
|
242
|
+
end
|
|
243
|
+
|
|
244
|
+
# custom error message
|
|
245
|
+
# https://blog.frankel.ch/structured-errors-http-apis/
|
|
246
|
+
# https://www.rfc-editor.org/rfc/rfc7807
|
|
247
|
+
class Committee::ValidationError
|
|
248
|
+
def error_body
|
|
249
|
+
{
|
|
250
|
+
errors: [
|
|
251
|
+
{
|
|
252
|
+
type: "#{request.env['rack.url_scheme']}://#{request.env['HTTP_HOST']}#{request.env['REQUEST_URI']}",
|
|
253
|
+
title: id,
|
|
254
|
+
detail: message,
|
|
255
|
+
status: status
|
|
256
|
+
}
|
|
257
|
+
]
|
|
258
|
+
}
|
|
259
|
+
end
|
|
260
|
+
|
|
261
|
+
def render
|
|
262
|
+
[
|
|
263
|
+
status,
|
|
264
|
+
{ "Content-Type" => "application/json" },
|
|
265
|
+
[JSON.generate(error_body)]
|
|
266
|
+
]
|
|
267
|
+
end
|
|
268
|
+
end
|
|
6
269
|
end
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
====================================================================================
|
|
2
|
+
|
|
3
|
+
Thank you for installing Firetail!
|
|
4
|
+
|
|
5
|
+
Before you start using firetail, please edit the file in "config/firetail.yml"
|
|
6
|
+
with your API Key, Token and URL first before running your Rails app.
|
|
7
|
+
|
|
8
|
+
The middleware is already configured in "config/application.rb" (you can check that)
|
|
9
|
+
|
|
10
|
+
Also we have installed a sample copy of schema.json in "config/schema.json" (your API schema)
|
|
11
|
+
for you to better understand and create your schema based on your needs.
|
|
12
|
+
|
|
13
|
+
Happy coding!
|
|
14
|
+
|
|
15
|
+
- From the team at Firetail
|
|
@@ -0,0 +1,108 @@
|
|
|
1
|
+
{
|
|
2
|
+
"openapi": "3.0.0",
|
|
3
|
+
"info": {
|
|
4
|
+
"title": "api-test",
|
|
5
|
+
"version": "1.0"
|
|
6
|
+
},
|
|
7
|
+
"servers": [
|
|
8
|
+
{
|
|
9
|
+
"url": "http://localhost:3000",
|
|
10
|
+
"description": "dev"
|
|
11
|
+
}
|
|
12
|
+
],
|
|
13
|
+
"paths": {
|
|
14
|
+
"/posts": {
|
|
15
|
+
"post": {
|
|
16
|
+
"parameters": [
|
|
17
|
+
{
|
|
18
|
+
"in": "query",
|
|
19
|
+
"name": "page",
|
|
20
|
+
"required": true,
|
|
21
|
+
"schema": {
|
|
22
|
+
"type": "integer"
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
],
|
|
26
|
+
"requestBody": {
|
|
27
|
+
"content": {
|
|
28
|
+
"application/json": {
|
|
29
|
+
"schema": {
|
|
30
|
+
"type": "object",
|
|
31
|
+
"required": [
|
|
32
|
+
"year"
|
|
33
|
+
],
|
|
34
|
+
"properties": {
|
|
35
|
+
"year": {
|
|
36
|
+
"type": "integer"
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
},
|
|
43
|
+
"responses": {
|
|
44
|
+
"200": {
|
|
45
|
+
"description": "correct response",
|
|
46
|
+
"content": {
|
|
47
|
+
"application/json": {
|
|
48
|
+
"schema": {
|
|
49
|
+
"type": "object",
|
|
50
|
+
"required": [
|
|
51
|
+
"title",
|
|
52
|
+
"page",
|
|
53
|
+
"year"
|
|
54
|
+
],
|
|
55
|
+
"properties": {
|
|
56
|
+
"title": {
|
|
57
|
+
"type": "string"
|
|
58
|
+
},
|
|
59
|
+
"page": {
|
|
60
|
+
"type": "integer"
|
|
61
|
+
},
|
|
62
|
+
"year": {
|
|
63
|
+
"type": "integer"
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
},
|
|
73
|
+
"/dont_allow_additional_parameter": {
|
|
74
|
+
"post": {
|
|
75
|
+
"requestBody": {
|
|
76
|
+
"content": {
|
|
77
|
+
"application/json": {
|
|
78
|
+
"schema": {
|
|
79
|
+
"type": "object",
|
|
80
|
+
"additionalProperties": false,
|
|
81
|
+
"required": [
|
|
82
|
+
"only_param"
|
|
83
|
+
],
|
|
84
|
+
"properties": {
|
|
85
|
+
"only_param": {
|
|
86
|
+
"type": "string"
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
},
|
|
93
|
+
"responses": {
|
|
94
|
+
"200": {
|
|
95
|
+
"description": "No Content",
|
|
96
|
+
"content": {
|
|
97
|
+
"application/json": {
|
|
98
|
+
"schema": {
|
|
99
|
+
"type": "object"
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
}
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
module Firetail
|
|
2
|
+
module Generators
|
|
3
|
+
class InstallGenerator < Rails::Generators::Base
|
|
4
|
+
source_root File.expand_path("../install/templates", __FILE__)
|
|
5
|
+
|
|
6
|
+
desc "create firetail configuration template to config/firetail.yml and add middleware"
|
|
7
|
+
def add_firetail_configuration
|
|
8
|
+
template "firetail.yml", File.join("config", "firetail.yml")
|
|
9
|
+
end
|
|
10
|
+
|
|
11
|
+
desc "add firetail middleware to rails application.rb"
|
|
12
|
+
def add_firetail_middleware
|
|
13
|
+
application "config.middleware.use Firetail::Run"
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
desc "add firetail sample json-schema template"
|
|
17
|
+
def add_firetail_sample_schema
|
|
18
|
+
template "schema.json", File.join("config", "schema.json")
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
def show_readme
|
|
22
|
+
readme "README" if behavior == :invoke
|
|
23
|
+
end
|
|
24
|
+
end
|
|
25
|
+
end
|
|
26
|
+
end
|
data/lib/railtie.rb
ADDED
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
require 'committee'
|
|
2
|
+
require 'rails'
|
|
3
|
+
|
|
4
|
+
class Error < StandardError; end
|
|
5
|
+
|
|
6
|
+
class Railtie < Rails::Railtie
|
|
7
|
+
|
|
8
|
+
initializer "commitee.insert_middleware" do |app|
|
|
9
|
+
begin
|
|
10
|
+
schema_path = File.join(Rails.root, "config/schema.json")
|
|
11
|
+
|
|
12
|
+
# check if schema file exists in config/
|
|
13
|
+
if File.exist?(schema_path)
|
|
14
|
+
app.config.middleware.use Committee::Middleware::RequestValidation,
|
|
15
|
+
schema_path: schema_path,
|
|
16
|
+
coerce_date_times: true,
|
|
17
|
+
params_key: 'action_dispatch.request.request_parameters',
|
|
18
|
+
query_hash_key: 'action_dispatch.request.query_parameters'
|
|
19
|
+
|
|
20
|
+
app.config.middleware.use Committee::Middleware::ResponseValidation, schema_path: schema_path
|
|
21
|
+
else
|
|
22
|
+
puts "Need schema.json in \"config/\" directory"
|
|
23
|
+
puts "Try re-running \"rails g firetail:install\" again"
|
|
24
|
+
end
|
|
25
|
+
rescue Error => e
|
|
26
|
+
puts e
|
|
27
|
+
end
|
|
28
|
+
end
|
|
29
|
+
end
|
metadata
CHANGED
|
@@ -1,14 +1,14 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: firetail
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version:
|
|
4
|
+
version: 1.0.1
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Muhammad Nuzaihan
|
|
8
|
-
autorequire:
|
|
8
|
+
autorequire:
|
|
9
9
|
bindir: exe
|
|
10
10
|
cert_chain: []
|
|
11
|
-
date:
|
|
11
|
+
date: 2024-04-29 00:00:00.000000000 Z
|
|
12
12
|
dependencies:
|
|
13
13
|
- !ruby/object:Gem::Dependency
|
|
14
14
|
name: bundler
|
|
@@ -30,14 +30,14 @@ dependencies:
|
|
|
30
30
|
requirements:
|
|
31
31
|
- - "~>"
|
|
32
32
|
- !ruby/object:Gem::Version
|
|
33
|
-
version: '
|
|
33
|
+
version: '13.0'
|
|
34
34
|
type: :development
|
|
35
35
|
prerelease: false
|
|
36
36
|
version_requirements: !ruby/object:Gem::Requirement
|
|
37
37
|
requirements:
|
|
38
38
|
- - "~>"
|
|
39
39
|
- !ruby/object:Gem::Version
|
|
40
|
-
version: '
|
|
40
|
+
version: '13.0'
|
|
41
41
|
- !ruby/object:Gem::Dependency
|
|
42
42
|
name: rspec
|
|
43
43
|
requirement: !ruby/object:Gem::Requirement
|
|
@@ -52,6 +52,76 @@ dependencies:
|
|
|
52
52
|
- - "~>"
|
|
53
53
|
- !ruby/object:Gem::Version
|
|
54
54
|
version: '3.0'
|
|
55
|
+
- !ruby/object:Gem::Dependency
|
|
56
|
+
name: webmock
|
|
57
|
+
requirement: !ruby/object:Gem::Requirement
|
|
58
|
+
requirements:
|
|
59
|
+
- - "~>"
|
|
60
|
+
- !ruby/object:Gem::Version
|
|
61
|
+
version: 3.18.1
|
|
62
|
+
type: :development
|
|
63
|
+
prerelease: false
|
|
64
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
65
|
+
requirements:
|
|
66
|
+
- - "~>"
|
|
67
|
+
- !ruby/object:Gem::Version
|
|
68
|
+
version: 3.18.1
|
|
69
|
+
- !ruby/object:Gem::Dependency
|
|
70
|
+
name: async
|
|
71
|
+
requirement: !ruby/object:Gem::Requirement
|
|
72
|
+
requirements:
|
|
73
|
+
- - "~>"
|
|
74
|
+
- !ruby/object:Gem::Version
|
|
75
|
+
version: 1.30.3
|
|
76
|
+
type: :runtime
|
|
77
|
+
prerelease: false
|
|
78
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
79
|
+
requirements:
|
|
80
|
+
- - "~>"
|
|
81
|
+
- !ruby/object:Gem::Version
|
|
82
|
+
version: 1.30.3
|
|
83
|
+
- !ruby/object:Gem::Dependency
|
|
84
|
+
name: jwt
|
|
85
|
+
requirement: !ruby/object:Gem::Requirement
|
|
86
|
+
requirements:
|
|
87
|
+
- - "~>"
|
|
88
|
+
- !ruby/object:Gem::Version
|
|
89
|
+
version: '2.5'
|
|
90
|
+
type: :runtime
|
|
91
|
+
prerelease: false
|
|
92
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
93
|
+
requirements:
|
|
94
|
+
- - "~>"
|
|
95
|
+
- !ruby/object:Gem::Version
|
|
96
|
+
version: '2.5'
|
|
97
|
+
- !ruby/object:Gem::Dependency
|
|
98
|
+
name: json-schema
|
|
99
|
+
requirement: !ruby/object:Gem::Requirement
|
|
100
|
+
requirements:
|
|
101
|
+
- - "~>"
|
|
102
|
+
- !ruby/object:Gem::Version
|
|
103
|
+
version: 3.0.0
|
|
104
|
+
type: :runtime
|
|
105
|
+
prerelease: false
|
|
106
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
107
|
+
requirements:
|
|
108
|
+
- - "~>"
|
|
109
|
+
- !ruby/object:Gem::Version
|
|
110
|
+
version: 3.0.0
|
|
111
|
+
- !ruby/object:Gem::Dependency
|
|
112
|
+
name: committee_firetail
|
|
113
|
+
requirement: !ruby/object:Gem::Requirement
|
|
114
|
+
requirements:
|
|
115
|
+
- - "~>"
|
|
116
|
+
- !ruby/object:Gem::Version
|
|
117
|
+
version: 5.0.1
|
|
118
|
+
type: :runtime
|
|
119
|
+
prerelease: false
|
|
120
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
121
|
+
requirements:
|
|
122
|
+
- - "~>"
|
|
123
|
+
- !ruby/object:Gem::Version
|
|
124
|
+
version: 5.0.1
|
|
55
125
|
description: API security library that is designed for ruby
|
|
56
126
|
email:
|
|
57
127
|
- zaihan@flitnetics.com
|
|
@@ -59,19 +129,110 @@ executables: []
|
|
|
59
129
|
extensions: []
|
|
60
130
|
extra_rdoc_files: []
|
|
61
131
|
files:
|
|
132
|
+
- ".github/pull_request_template.md"
|
|
133
|
+
- ".github/workflows/main.yml"
|
|
62
134
|
- ".gitignore"
|
|
63
135
|
- ".rspec"
|
|
64
136
|
- ".travis.yml"
|
|
65
137
|
- CODE_OF_CONDUCT.md
|
|
66
138
|
- Gemfile
|
|
139
|
+
- Gemfile.lock
|
|
140
|
+
- LICENSE
|
|
67
141
|
- LICENSE.txt
|
|
68
142
|
- README.md
|
|
69
143
|
- Rakefile
|
|
70
144
|
- bin/console
|
|
71
145
|
- bin/setup
|
|
146
|
+
- examples/rails/.gitattributes
|
|
147
|
+
- examples/rails/.gitignore
|
|
148
|
+
- examples/rails/.rspec
|
|
149
|
+
- examples/rails/.ruby-version
|
|
150
|
+
- examples/rails/Gemfile
|
|
151
|
+
- examples/rails/Gemfile.lock
|
|
152
|
+
- examples/rails/README.md
|
|
153
|
+
- examples/rails/Rakefile
|
|
154
|
+
- examples/rails/app/channels/application_cable/channel.rb
|
|
155
|
+
- examples/rails/app/channels/application_cable/connection.rb
|
|
156
|
+
- examples/rails/app/controllers/application_controller.rb
|
|
157
|
+
- examples/rails/app/controllers/comments_controller.rb
|
|
158
|
+
- examples/rails/app/controllers/concerns/.keep
|
|
159
|
+
- examples/rails/app/controllers/posts_controller.rb
|
|
160
|
+
- examples/rails/app/jobs/application_job.rb
|
|
161
|
+
- examples/rails/app/mailers/application_mailer.rb
|
|
162
|
+
- examples/rails/app/models/application_record.rb
|
|
163
|
+
- examples/rails/app/models/comment.rb
|
|
164
|
+
- examples/rails/app/models/concerns/.keep
|
|
165
|
+
- examples/rails/app/models/post.rb
|
|
166
|
+
- examples/rails/app/views/layouts/mailer.html.erb
|
|
167
|
+
- examples/rails/app/views/layouts/mailer.text.erb
|
|
168
|
+
- examples/rails/bin/bundle
|
|
169
|
+
- examples/rails/bin/rails
|
|
170
|
+
- examples/rails/bin/rake
|
|
171
|
+
- examples/rails/bin/setup
|
|
172
|
+
- examples/rails/bin/spring
|
|
173
|
+
- examples/rails/config.ru
|
|
174
|
+
- examples/rails/config/application.rb
|
|
175
|
+
- examples/rails/config/boot.rb
|
|
176
|
+
- examples/rails/config/cable.yml
|
|
177
|
+
- examples/rails/config/credentials.yml.enc
|
|
178
|
+
- examples/rails/config/database.yml
|
|
179
|
+
- examples/rails/config/environment.rb
|
|
180
|
+
- examples/rails/config/environments/development.rb
|
|
181
|
+
- examples/rails/config/environments/production.rb
|
|
182
|
+
- examples/rails/config/environments/test.rb
|
|
183
|
+
- examples/rails/config/firetail.yml
|
|
184
|
+
- examples/rails/config/initializers/application_controller_renderer.rb
|
|
185
|
+
- examples/rails/config/initializers/backtrace_silencers.rb
|
|
186
|
+
- examples/rails/config/initializers/cors.rb
|
|
187
|
+
- examples/rails/config/initializers/filter_parameter_logging.rb
|
|
188
|
+
- examples/rails/config/initializers/inflections.rb
|
|
189
|
+
- examples/rails/config/initializers/mime_types.rb
|
|
190
|
+
- examples/rails/config/initializers/wrap_parameters.rb
|
|
191
|
+
- examples/rails/config/locales/en.yml
|
|
192
|
+
- examples/rails/config/puma.rb
|
|
193
|
+
- examples/rails/config/routes.rb
|
|
194
|
+
- examples/rails/config/schema.json
|
|
195
|
+
- examples/rails/config/spring.rb
|
|
196
|
+
- examples/rails/config/storage.yml
|
|
197
|
+
- examples/rails/db/migrate/20230730163722_create_posts.rb
|
|
198
|
+
- examples/rails/db/migrate/20230730163741_create_comments.rb
|
|
199
|
+
- examples/rails/db/migrate/20230730164121_add_fields_to_post.rb
|
|
200
|
+
- examples/rails/db/migrate/20230730164214_add_fields_to_comments.rb
|
|
201
|
+
- examples/rails/db/schema.rb
|
|
202
|
+
- examples/rails/db/seeds.rb
|
|
203
|
+
- examples/rails/lib/tasks/.keep
|
|
204
|
+
- examples/rails/log/.keep
|
|
205
|
+
- examples/rails/public/robots.txt
|
|
206
|
+
- examples/rails/spec/models/comment_spec.rb
|
|
207
|
+
- examples/rails/spec/models/post_spec.rb
|
|
208
|
+
- examples/rails/spec/rails_helper.rb
|
|
209
|
+
- examples/rails/spec/requests/comments_spec.rb
|
|
210
|
+
- examples/rails/spec/requests/posts_spec.rb
|
|
211
|
+
- examples/rails/spec/routing/comments_routing_spec.rb
|
|
212
|
+
- examples/rails/spec/routing/posts_routing_spec.rb
|
|
213
|
+
- examples/rails/spec/spec_helper.rb
|
|
214
|
+
- examples/rails/storage/.keep
|
|
215
|
+
- examples/rails/test/channels/application_cable/connection_test.rb
|
|
216
|
+
- examples/rails/test/controllers/.keep
|
|
217
|
+
- examples/rails/test/fixtures/files/.keep
|
|
218
|
+
- examples/rails/test/integration/.keep
|
|
219
|
+
- examples/rails/test/mailers/.keep
|
|
220
|
+
- examples/rails/test/models/.keep
|
|
221
|
+
- examples/rails/test/test_helper.rb
|
|
222
|
+
- examples/rails/tmp/.keep
|
|
223
|
+
- examples/rails/tmp/pids/.keep
|
|
224
|
+
- examples/rails/vendor/.keep
|
|
72
225
|
- firetail.gemspec
|
|
226
|
+
- lib/backend.rb
|
|
227
|
+
- lib/background_tasks.rb
|
|
228
|
+
- lib/case_sensitive_headers.rb
|
|
73
229
|
- lib/firetail.rb
|
|
74
230
|
- lib/firetail/version.rb
|
|
231
|
+
- lib/generators/firetail/install/templates/README
|
|
232
|
+
- lib/generators/firetail/install/templates/firetail.yml
|
|
233
|
+
- lib/generators/firetail/install/templates/schema.json
|
|
234
|
+
- lib/generators/firetail/install_generator.rb
|
|
235
|
+
- lib/railtie.rb
|
|
75
236
|
homepage: https://www.firetail.io
|
|
76
237
|
licenses:
|
|
77
238
|
- MIT
|
|
@@ -80,7 +241,7 @@ metadata:
|
|
|
80
241
|
homepage_uri: https://www.firetail.io
|
|
81
242
|
source_code_uri: https://github.com/firetail-io/ruby
|
|
82
243
|
changelog_uri: https://github.com/firetail-io/ruby/CHANGELOG.md
|
|
83
|
-
post_install_message:
|
|
244
|
+
post_install_message:
|
|
84
245
|
rdoc_options: []
|
|
85
246
|
require_paths:
|
|
86
247
|
- lib
|
|
@@ -95,8 +256,8 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
|
95
256
|
- !ruby/object:Gem::Version
|
|
96
257
|
version: '0'
|
|
97
258
|
requirements: []
|
|
98
|
-
rubygems_version: 3.
|
|
99
|
-
signing_key:
|
|
259
|
+
rubygems_version: 3.5.9
|
|
260
|
+
signing_key:
|
|
100
261
|
specification_version: 4
|
|
101
262
|
summary: Ruby library for firetail
|
|
102
263
|
test_files: []
|