upfluence-utils 0.1.0
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 +7 -0
- data/.gitignore +9 -0
- data/.rspec +2 -0
- data/.travis.yml +5 -0
- data/Gemfile +4 -0
- data/LICENSE.txt +21 -0
- data/README.md +1 -0
- data/Rakefile +6 -0
- data/lib/upfluence.rb +4 -0
- data/lib/upfluence/base/exceptions/validation_error.rb +37 -0
- data/lib/upfluence/endpoint/api_endpoint.rb +78 -0
- data/lib/upfluence/environment.rb +11 -0
- data/lib/upfluence/error_logger.rb +14 -0
- data/lib/upfluence/error_logger/null.rb +23 -0
- data/lib/upfluence/error_logger/sentry.rb +31 -0
- data/lib/upfluence/handler/base.rb +56 -0
- data/lib/upfluence/logger.rb +56 -0
- data/lib/upfluence/mixin/strong_parameters.rb +269 -0
- data/lib/upfluence/utils.rb +6 -0
- data/lib/upfluence/utils/http/middleware/logger.rb +39 -0
- data/lib/upfluence/utils/http/middleware/null.rb +3 -0
- data/lib/upfluence/utils/thrift.rb +1 -0
- data/lib/upfluence/utils/thrift/middleware.rb +22 -0
- data/lib/upfluence/utils/thrift/middleware/error_catcher.rb +27 -0
- data/lib/upfluence/utils/thrift/middleware/request_logger.rb +42 -0
- data/lib/upfluence/utils/thrift/middleware/timeout.rb +23 -0
- data/lib/upfluence/utils/version.rb +5 -0
- data/rbutils.gemspec +30 -0
- metadata +231 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: 233394d9586ebad31920557ce48d4ea7cc0ea7f0
|
4
|
+
data.tar.gz: 100f0a1c056732f934c0b6df0727c2325b02e05e
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: da3be0d66b0e1f9b213de1d5f946b55e705d7781de680c27a7b5de8b6c2b4e912e373b74a709418529d5c0f8603642311fddf35323f53627a7193e597668efc8
|
7
|
+
data.tar.gz: 1a2d3fa9d3c93215b07725d81b73fc08e08a06980cd0d4934dd678a92582576de3bf5d14e60c5b69c8dde17c39026297e50ff66d853dd0143f03fba843828947
|
data/.gitignore
ADDED
data/.rspec
ADDED
data/.travis.yml
ADDED
data/Gemfile
ADDED
data/LICENSE.txt
ADDED
@@ -0,0 +1,21 @@
|
|
1
|
+
The MIT License (MIT)
|
2
|
+
|
3
|
+
Copyright (c) 2016 Julien Levesy
|
4
|
+
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
7
|
+
in the Software without restriction, including without limitation the rights
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
10
|
+
furnished to do so, subject to the following conditions:
|
11
|
+
|
12
|
+
The above copyright notice and this permission notice shall be included in
|
13
|
+
all copies or substantial portions of the Software.
|
14
|
+
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
21
|
+
THE SOFTWARE.
|
data/README.md
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
# Rbutils
|
data/Rakefile
ADDED
data/lib/upfluence.rb
ADDED
@@ -0,0 +1,37 @@
|
|
1
|
+
require 'thrift/exceptions'
|
2
|
+
require 'base/exceptions/exceptions_types'
|
3
|
+
|
4
|
+
module Base
|
5
|
+
module Exceptions
|
6
|
+
class ValidationError < ::Thrift::Exception
|
7
|
+
class << self
|
8
|
+
attr_accessor :domain
|
9
|
+
|
10
|
+
def from_model(model)
|
11
|
+
validation_errors = model.errors.details.map do |error_field, errors|
|
12
|
+
errors.map do |error|
|
13
|
+
Base::Exceptions::Validation.new(
|
14
|
+
domain: domain,
|
15
|
+
model: model.model_name.singular,
|
16
|
+
field: error_field.to_s,
|
17
|
+
error: error[:error].to_s
|
18
|
+
)
|
19
|
+
end
|
20
|
+
end.flatten
|
21
|
+
|
22
|
+
new(validations: validation_errors)
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
def to_json
|
27
|
+
{
|
28
|
+
errors: validations.reduce([]) do |acc, v|
|
29
|
+
acc << {
|
30
|
+
'resource' => v.model, 'field' => v.field, 'code' => v.error
|
31
|
+
}
|
32
|
+
end
|
33
|
+
}.to_json
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
@@ -0,0 +1,78 @@
|
|
1
|
+
require 'sinatra'
|
2
|
+
|
3
|
+
module Upfluence
|
4
|
+
module Endpoint
|
5
|
+
class BadRequest < StandardError; end
|
6
|
+
class ApiEndpoint < Sinatra::Base
|
7
|
+
disable :show_exceptions
|
8
|
+
|
9
|
+
configure :development do
|
10
|
+
require 'sinatra/reloader'
|
11
|
+
register Sinatra::Reloader
|
12
|
+
end
|
13
|
+
|
14
|
+
configure :test do
|
15
|
+
enable :raise_errors
|
16
|
+
end
|
17
|
+
|
18
|
+
helpers do
|
19
|
+
def ok
|
20
|
+
content_type :json
|
21
|
+
[200, { status: 'OK' }.to_json]
|
22
|
+
end
|
23
|
+
|
24
|
+
def access_token
|
25
|
+
token = params[:access_token]
|
26
|
+
|
27
|
+
unless access_token
|
28
|
+
pattern = /^Bearer /
|
29
|
+
header = request.env['HTTP_AUTHORIZATION']
|
30
|
+
token = header.gsub(pattern, '') if header && header.match(pattern)
|
31
|
+
end
|
32
|
+
|
33
|
+
token
|
34
|
+
end
|
35
|
+
|
36
|
+
def respond_with(resource, *args)
|
37
|
+
if resource.respond_to?(:errors) && resource.errors.any?
|
38
|
+
status = 422
|
39
|
+
result = Base::Exceptions::ValidationError.from_model(
|
40
|
+
resource
|
41
|
+
).to_json
|
42
|
+
else
|
43
|
+
status = 200
|
44
|
+
result = if resource.is_a? Enumerable
|
45
|
+
ActiveModel::ArraySerializer.new(
|
46
|
+
resource, *args
|
47
|
+
).to_json
|
48
|
+
elsif resource.respond_to?(:serialize)
|
49
|
+
resource.serialize(*args).to_json
|
50
|
+
else
|
51
|
+
ActiveModel::Serializer.serializer_for(resource).new(resource, *args).to_json
|
52
|
+
end
|
53
|
+
end
|
54
|
+
|
55
|
+
halt [status, result]
|
56
|
+
end
|
57
|
+
|
58
|
+
def json_params
|
59
|
+
ActiveSupport::HashWithIndifferentAccess.new(JSON.parse(request_body))
|
60
|
+
end
|
61
|
+
|
62
|
+
def request_body
|
63
|
+
@request_body ||= request.body.read
|
64
|
+
end
|
65
|
+
end
|
66
|
+
end
|
67
|
+
|
68
|
+
Sinatra::Base.error BadRequest do
|
69
|
+
status 400
|
70
|
+
{ error: 'Bad request' }.to_json
|
71
|
+
end
|
72
|
+
|
73
|
+
Sinatra::Base.error JSON::ParserError do
|
74
|
+
status 400
|
75
|
+
{ message: 'Invalid JSON' }.to_json
|
76
|
+
end
|
77
|
+
end
|
78
|
+
end
|
@@ -0,0 +1,14 @@
|
|
1
|
+
require 'upfluence/error_logger/sentry'
|
2
|
+
require 'upfluence/error_logger/null'
|
3
|
+
|
4
|
+
module Upfluence
|
5
|
+
class << self
|
6
|
+
def error_logger
|
7
|
+
@error_logger ||= if ENV['SENTRY_DSN']
|
8
|
+
ErrorLogger::Sentry.new
|
9
|
+
else
|
10
|
+
ErrorLogger::Null.new
|
11
|
+
end
|
12
|
+
end
|
13
|
+
end
|
14
|
+
end
|
@@ -0,0 +1,23 @@
|
|
1
|
+
module Upfluence
|
2
|
+
module ErrorLogger
|
3
|
+
class Null
|
4
|
+
class Middleware
|
5
|
+
def initialize(app)
|
6
|
+
@app = app
|
7
|
+
end
|
8
|
+
|
9
|
+
def call(env)
|
10
|
+
@app.call(env)
|
11
|
+
end
|
12
|
+
end
|
13
|
+
|
14
|
+
def notify(error, _method, *_args)
|
15
|
+
Upfluence.logger.error(error.inspect)
|
16
|
+
end
|
17
|
+
|
18
|
+
def middleware
|
19
|
+
Middleware
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
@@ -0,0 +1,31 @@
|
|
1
|
+
require 'raven'
|
2
|
+
|
3
|
+
module Upfluence
|
4
|
+
module ErrorLogger
|
5
|
+
class Sentry
|
6
|
+
EXCLUDED_ERRORS = (Raven::Configuration::IGNORE_DEFAULT + ['Identity::Thrift::Forbidden']).freeze
|
7
|
+
|
8
|
+
def initialize
|
9
|
+
::Raven.configure do |config|
|
10
|
+
config.dsn = ENV['SENTRY_DSN']
|
11
|
+
config.current_environment = Upfluence.env
|
12
|
+
config.excluded_exceptions = EXCLUDED_ERRORS
|
13
|
+
config.logger = Upfluence.logger
|
14
|
+
config.release = ENV['SEMVER_VERSION']
|
15
|
+
config.server_name = ENV['UNIT_NAME']
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
def notify(error, method, *args)
|
20
|
+
Raven.capture_exception(
|
21
|
+
error,
|
22
|
+
extra: { method: method, arguments: args, environment: @env }
|
23
|
+
)
|
24
|
+
end
|
25
|
+
|
26
|
+
def middleware
|
27
|
+
::Raven::Rack
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
@@ -0,0 +1,56 @@
|
|
1
|
+
require 'base/base_service'
|
2
|
+
require 'base/version/version_types'
|
3
|
+
require 'base/version'
|
4
|
+
|
5
|
+
module Upfluence
|
6
|
+
module Handler
|
7
|
+
class Base
|
8
|
+
def initialize(modules = [])
|
9
|
+
@alive_since = Time.now.to_i
|
10
|
+
@modules = modules.reduce({ 'base' => ::Base::VERSION }) do |acc, cur|
|
11
|
+
acc.merge cur.name.downcase => cur::VERSION
|
12
|
+
end
|
13
|
+
end
|
14
|
+
|
15
|
+
def getVersion
|
16
|
+
semantic_version = if ENV['SEMVER_VERSION']
|
17
|
+
major, minor, patch = ENV['SEMVER_VERSION'].split('.')
|
18
|
+
::Base::Version::SemanticVersion.new(
|
19
|
+
major: major[1..-1].to_i,
|
20
|
+
minor: minor.to_i,
|
21
|
+
patch: patch.to_i
|
22
|
+
)
|
23
|
+
end
|
24
|
+
|
25
|
+
git_version = if ENV['GIT_COMMIT']
|
26
|
+
::Base::Version::GitVersion.new(
|
27
|
+
commit: ENV['GIT_COMMIT'],
|
28
|
+
branch: ENV['GIT_BRANCH'],
|
29
|
+
remote: ENV['GIT_REMOTE']
|
30
|
+
)
|
31
|
+
end
|
32
|
+
|
33
|
+
::Base::Version::Version.new(
|
34
|
+
semantic_version: semantic_version,
|
35
|
+
git_version: git_version
|
36
|
+
)
|
37
|
+
end
|
38
|
+
|
39
|
+
def getName
|
40
|
+
ENV['UNIT_NAME'] || 'default'
|
41
|
+
end
|
42
|
+
|
43
|
+
def getStatus
|
44
|
+
::Base::Status::ALIVE
|
45
|
+
end
|
46
|
+
|
47
|
+
def aliveSince
|
48
|
+
@alive_since
|
49
|
+
end
|
50
|
+
|
51
|
+
def getInterfaceVersions
|
52
|
+
@modules
|
53
|
+
end
|
54
|
+
end
|
55
|
+
end
|
56
|
+
end
|
@@ -0,0 +1,56 @@
|
|
1
|
+
require 'logger'
|
2
|
+
|
3
|
+
module Upfluence
|
4
|
+
class Logger < Logger
|
5
|
+
class Formatter < Logger::Formatter
|
6
|
+
TIME_FORMAT = '%y%m%d %H:%M:%S'.freeze
|
7
|
+
LOG_FORMAT = '[%s %s %s] %s'.freeze
|
8
|
+
|
9
|
+
def initialize(extra = 0)
|
10
|
+
@extra = extra
|
11
|
+
end
|
12
|
+
|
13
|
+
def call(severity, tstamp, _progname, msg)
|
14
|
+
LOG_FORMAT % [
|
15
|
+
severity[0], formatted_time(tstamp), formatted_caller,
|
16
|
+
message_to_str(msg)
|
17
|
+
]
|
18
|
+
end
|
19
|
+
|
20
|
+
def message_to_str(msg)
|
21
|
+
msg.is_a?(String) ? msg : msg.inspect
|
22
|
+
end
|
23
|
+
|
24
|
+
def formatted_time(timestamp)
|
25
|
+
timestamp.strftime(TIME_FORMAT)
|
26
|
+
end
|
27
|
+
|
28
|
+
def formatted_caller
|
29
|
+
caller[4 + @extra].gsub!(/(^.+\/)?(.*):(.*):in `.*'/, '\\2:\\3')
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
def logger_level(level)
|
34
|
+
{
|
35
|
+
'info' => Logger::INFO,
|
36
|
+
'warn' => Logger::WARN,
|
37
|
+
'debug' => Logger::DEBUG,
|
38
|
+
'error' => Logger::ERROR,
|
39
|
+
'fatal' => Logger::FATAL
|
40
|
+
}[level] || Logger::INFO
|
41
|
+
end
|
42
|
+
|
43
|
+
def initialize(level = (ENV['LOGGER_LEVEL'] || '').downcase, caller_depth = 0)
|
44
|
+
STDOUT.sync = true
|
45
|
+
@logdev = STDOUT
|
46
|
+
@level = logger_level(level)
|
47
|
+
@formatter = Formatter.new(caller_depth)
|
48
|
+
end
|
49
|
+
end
|
50
|
+
|
51
|
+
class << self
|
52
|
+
def logger
|
53
|
+
@logger ||= Logger.new
|
54
|
+
end
|
55
|
+
end
|
56
|
+
end
|
@@ -0,0 +1,269 @@
|
|
1
|
+
module Upfluence
|
2
|
+
module Mixin
|
3
|
+
module StrongParameters
|
4
|
+
class ParameterMissing < IndexError
|
5
|
+
attr_reader :param
|
6
|
+
|
7
|
+
def initialize(param)
|
8
|
+
@param = param
|
9
|
+
super("param is missing or the value is empty: #{param}")
|
10
|
+
end
|
11
|
+
end
|
12
|
+
|
13
|
+
class UnpermittedParameters < IndexError
|
14
|
+
attr_reader :params
|
15
|
+
|
16
|
+
def initialize(params)
|
17
|
+
@params = params
|
18
|
+
super("found unpermitted parameters: #{params.join(", ")}")
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
class Parameters < ActiveSupport::HashWithIndifferentAccess
|
23
|
+
attr_accessor :permitted
|
24
|
+
alias :permitted? :permitted
|
25
|
+
|
26
|
+
cattr_accessor :action_on_unpermitted_parameters, :instance_accessor => false
|
27
|
+
|
28
|
+
# Never raise an UnpermittedParameters exception because of these params
|
29
|
+
# are present. They are added by Rails and it's of no concern.
|
30
|
+
NEVER_UNPERMITTED_PARAMS = %w( controller action )
|
31
|
+
|
32
|
+
def initialize(attributes = nil)
|
33
|
+
super(attributes)
|
34
|
+
@permitted = false
|
35
|
+
end
|
36
|
+
|
37
|
+
def permit!
|
38
|
+
each_pair do |key, value|
|
39
|
+
value = convert_hashes_to_parameters(key, value)
|
40
|
+
Array.wrap(value).each do |_|
|
41
|
+
_.permit! if _.respond_to? :permit!
|
42
|
+
end
|
43
|
+
end
|
44
|
+
|
45
|
+
@permitted = true
|
46
|
+
self
|
47
|
+
end
|
48
|
+
|
49
|
+
def require(key)
|
50
|
+
return key.map { |k| require(k) } if key.is_a?(Array)
|
51
|
+
value = self[key]
|
52
|
+
if value.present? || value == false
|
53
|
+
value
|
54
|
+
else
|
55
|
+
raise ParameterMissing.new(key)
|
56
|
+
end
|
57
|
+
end
|
58
|
+
|
59
|
+
alias :required :require
|
60
|
+
|
61
|
+
def permit(*filters)
|
62
|
+
params = self.class.new
|
63
|
+
|
64
|
+
filters.flatten.each do |filter|
|
65
|
+
case filter
|
66
|
+
when Symbol, String
|
67
|
+
permitted_scalar_filter(params, filter)
|
68
|
+
when Hash then
|
69
|
+
hash_filter(params, filter)
|
70
|
+
end
|
71
|
+
end
|
72
|
+
|
73
|
+
unpermitted_parameters!(params) if self.class.action_on_unpermitted_parameters
|
74
|
+
|
75
|
+
params.permit!
|
76
|
+
end
|
77
|
+
|
78
|
+
def [](key)
|
79
|
+
convert_hashes_to_parameters(key, super)
|
80
|
+
end
|
81
|
+
|
82
|
+
def fetch(key, *args)
|
83
|
+
convert_hashes_to_parameters(key, super, false)
|
84
|
+
rescue KeyError, IndexError
|
85
|
+
raise ParameterMissing.new(key)
|
86
|
+
end
|
87
|
+
|
88
|
+
def slice(*keys)
|
89
|
+
self.class.new(super).tap do |new_instance|
|
90
|
+
new_instance.instance_variable_set :@permitted, @permitted
|
91
|
+
end
|
92
|
+
end
|
93
|
+
|
94
|
+
def dup
|
95
|
+
self.class.new(self).tap do |duplicate|
|
96
|
+
duplicate.default = default
|
97
|
+
duplicate.instance_variable_set :@permitted, @permitted
|
98
|
+
end
|
99
|
+
end
|
100
|
+
|
101
|
+
protected
|
102
|
+
|
103
|
+
def convert_value(value, options = {})
|
104
|
+
if value.class == Hash
|
105
|
+
self.class.new(value)
|
106
|
+
elsif value.is_a?(Array)
|
107
|
+
value.dup.replace(value.map { |e| convert_value(e) })
|
108
|
+
else
|
109
|
+
value
|
110
|
+
end
|
111
|
+
end
|
112
|
+
|
113
|
+
private
|
114
|
+
|
115
|
+
def convert_hashes_to_parameters(key, value, assign_if_converted=true)
|
116
|
+
converted = convert_value_to_parameters(value)
|
117
|
+
self[key] = converted if assign_if_converted && !converted.equal?(value)
|
118
|
+
converted
|
119
|
+
end
|
120
|
+
|
121
|
+
def convert_value_to_parameters(value)
|
122
|
+
if value.is_a?(Array)
|
123
|
+
value.map { |_| convert_value_to_parameters(_) }
|
124
|
+
elsif value.is_a?(Parameters) || !value.is_a?(Hash)
|
125
|
+
value
|
126
|
+
else
|
127
|
+
self.class.new(value)
|
128
|
+
end
|
129
|
+
end
|
130
|
+
|
131
|
+
#
|
132
|
+
# --- Filtering ----------------------------------------------------------
|
133
|
+
#
|
134
|
+
|
135
|
+
# This is a white list of permitted scalar types that includes the ones
|
136
|
+
# supported in XML and JSON requests.
|
137
|
+
#
|
138
|
+
# This list is in particular used to filter ordinary requests, String goes
|
139
|
+
# as first element to quickly short-circuit the common case.
|
140
|
+
#
|
141
|
+
# If you modify this collection please update the README.
|
142
|
+
PERMITTED_SCALAR_TYPES = [
|
143
|
+
String,
|
144
|
+
Symbol,
|
145
|
+
NilClass,
|
146
|
+
Numeric,
|
147
|
+
TrueClass,
|
148
|
+
FalseClass,
|
149
|
+
Date,
|
150
|
+
Time,
|
151
|
+
# DateTimes are Dates, we document the type but avoid the redundant check.
|
152
|
+
StringIO,
|
153
|
+
IO,
|
154
|
+
#ActionDispatch::Http::UploadedFile,
|
155
|
+
#Rack::Test::UploadedFile,
|
156
|
+
]
|
157
|
+
|
158
|
+
def permitted_scalar?(value)
|
159
|
+
PERMITTED_SCALAR_TYPES.any? {|type| value.is_a?(type)}
|
160
|
+
end
|
161
|
+
|
162
|
+
def array_of_permitted_scalars?(value)
|
163
|
+
if value.is_a?(Array)
|
164
|
+
value.all? {|element| permitted_scalar?(element)}
|
165
|
+
end
|
166
|
+
end
|
167
|
+
|
168
|
+
def permitted_scalar_filter(params, key)
|
169
|
+
if has_key?(key) && permitted_scalar?(self[key])
|
170
|
+
params[key] = self[key]
|
171
|
+
end
|
172
|
+
|
173
|
+
keys.grep(/\A#{Regexp.escape(key.to_s)}\(\d+[if]?\)\z/).each do |key|
|
174
|
+
if permitted_scalar?(self[key])
|
175
|
+
params[key] = self[key]
|
176
|
+
end
|
177
|
+
end
|
178
|
+
end
|
179
|
+
|
180
|
+
def array_of_permitted_scalars_filter(params, key, hash = self)
|
181
|
+
if hash.has_key?(key) && array_of_permitted_scalars?(hash[key])
|
182
|
+
params[key] = hash[key]
|
183
|
+
end
|
184
|
+
end
|
185
|
+
|
186
|
+
def hash_filter(params, filter)
|
187
|
+
filter = filter.with_indifferent_access
|
188
|
+
|
189
|
+
# Slicing filters out non-declared keys.
|
190
|
+
slice(*filter.keys).each do |key, value|
|
191
|
+
next unless value
|
192
|
+
|
193
|
+
if filter[key] == []
|
194
|
+
# Declaration {:comment_ids => []}.
|
195
|
+
array_of_permitted_scalars_filter(params, key)
|
196
|
+
else
|
197
|
+
# Declaration {:user => :name} or {:user => [:name, :age, {:adress => ...}]}.
|
198
|
+
params[key] = each_element(value) do |element, index|
|
199
|
+
if element.is_a?(Hash)
|
200
|
+
element = self.class.new(element) unless element.respond_to?(:permit)
|
201
|
+
element.permit(*Array.wrap(filter[key]))
|
202
|
+
elsif filter[key].is_a?(Hash) && filter[key][index] == []
|
203
|
+
array_of_permitted_scalars_filter(params, index, value)
|
204
|
+
end
|
205
|
+
end
|
206
|
+
end
|
207
|
+
end
|
208
|
+
end
|
209
|
+
|
210
|
+
def each_element(value)
|
211
|
+
if value.is_a?(Array)
|
212
|
+
value.map { |el| yield el }.compact
|
213
|
+
# fields_for on an array of records uses numeric hash keys.
|
214
|
+
elsif fields_for_style?(value)
|
215
|
+
hash = value.class.new
|
216
|
+
value.each { |k,v| hash[k] = yield(v, k) }
|
217
|
+
hash
|
218
|
+
else
|
219
|
+
yield value
|
220
|
+
end
|
221
|
+
end
|
222
|
+
|
223
|
+
def fields_for_style?(object)
|
224
|
+
object.is_a?(Hash) && object.all? { |k, v| k =~ /\A-?\d+\z/ && v.is_a?(Hash) }
|
225
|
+
end
|
226
|
+
|
227
|
+
def unpermitted_parameters!(params)
|
228
|
+
return unless self.class.action_on_unpermitted_parameters
|
229
|
+
|
230
|
+
unpermitted_keys = unpermitted_keys(params)
|
231
|
+
|
232
|
+
if unpermitted_keys.any?
|
233
|
+
case self.class.action_on_unpermitted_parameters
|
234
|
+
when :log
|
235
|
+
name = "unpermitted_parameters.action_controller"
|
236
|
+
ActiveSupport::Notifications.instrument(name, :keys => unpermitted_keys)
|
237
|
+
when :raise
|
238
|
+
raise UnpermittedParameters.new(unpermitted_keys)
|
239
|
+
end
|
240
|
+
end
|
241
|
+
end
|
242
|
+
|
243
|
+
def unpermitted_keys(params)
|
244
|
+
self.keys - params.keys - NEVER_UNPERMITTED_PARAMS
|
245
|
+
end
|
246
|
+
end
|
247
|
+
|
248
|
+
def params
|
249
|
+
@_params ||= Parameters.new(@params)
|
250
|
+
end
|
251
|
+
|
252
|
+
def params=(val)
|
253
|
+
@_params = val.is_a?(Hash) ? Parameters.new(val) : val
|
254
|
+
end
|
255
|
+
|
256
|
+
def json_params
|
257
|
+
begin
|
258
|
+
@_json_params ||= Parameters.new(super)
|
259
|
+
rescue
|
260
|
+
raise "You must extend Upfluence::Endpoint::ApiEndpoint to use json_params"
|
261
|
+
end
|
262
|
+
end
|
263
|
+
|
264
|
+
def json_params=(val)
|
265
|
+
@_json_params = val.is_a?(Hash) ? Parameters.new(val) : val
|
266
|
+
end
|
267
|
+
end
|
268
|
+
end
|
269
|
+
end
|
@@ -0,0 +1,39 @@
|
|
1
|
+
require 'rack/body_proxy'
|
2
|
+
|
3
|
+
module Upfluence
|
4
|
+
module Utils
|
5
|
+
module HTTP
|
6
|
+
module Middleware
|
7
|
+
class Logger
|
8
|
+
def initialize(app)
|
9
|
+
@app = app
|
10
|
+
end
|
11
|
+
|
12
|
+
def call(env)
|
13
|
+
began_at = Time.now
|
14
|
+
status, header, body = @app.call(env)
|
15
|
+
header = Rack::Utils::HeaderHash.new(header)
|
16
|
+
body = Rack::BodyProxy.new(body) { log(env, status, header, began_at) }
|
17
|
+
[status, header, body]
|
18
|
+
end
|
19
|
+
|
20
|
+
private
|
21
|
+
|
22
|
+
def log(env, status, header, began_at)
|
23
|
+
now = Time.now
|
24
|
+
|
25
|
+
Upfluence.logger.info(
|
26
|
+
"%d %s %s%s (%s) %.2fms\n" % [
|
27
|
+
status, env[Rack::REQUEST_METHOD],
|
28
|
+
env[Rack::PATH_INFO],
|
29
|
+
env[Rack::QUERY_STRING].empty? ? "" : "?"+env[Rack::QUERY_STRING],
|
30
|
+
env['HTTP_X_FORWARDED_FOR'] || env["REMOTE_ADDR"] || "-",
|
31
|
+
(now - began_at) * 1000
|
32
|
+
]
|
33
|
+
)
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
@@ -0,0 +1 @@
|
|
1
|
+
require 'upfluence/utils/thrift/middleware'
|
@@ -0,0 +1,22 @@
|
|
1
|
+
require 'upfluence/utils/thrift/middleware/error_catcher'
|
2
|
+
require 'upfluence/utils/thrift/middleware/request_logger'
|
3
|
+
require 'upfluence/utils/thrift/middleware/timeout'
|
4
|
+
|
5
|
+
module Upfluence
|
6
|
+
module Utils
|
7
|
+
module Thrift
|
8
|
+
module Middleware
|
9
|
+
class << self
|
10
|
+
def setup(handler, timeout = 30)
|
11
|
+
ErrorCatcher.new(
|
12
|
+
Timeout.new(
|
13
|
+
RequestLogger.new(handler, Upfluence.logger), timeout
|
14
|
+
),
|
15
|
+
Upfluence.error_logger
|
16
|
+
)
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
@@ -0,0 +1,27 @@
|
|
1
|
+
module Upfluence
|
2
|
+
module Utils
|
3
|
+
module Thrift
|
4
|
+
module Middleware
|
5
|
+
class ErrorCatcher
|
6
|
+
def initialize(app, error_logger)
|
7
|
+
@app = app
|
8
|
+
@error_logger = error_logger
|
9
|
+
end
|
10
|
+
|
11
|
+
def method_missing(method, *args, &block)
|
12
|
+
@app.send(method, *args, &block)
|
13
|
+
rescue ::Thrift::Exception => e
|
14
|
+
raise e
|
15
|
+
rescue => e
|
16
|
+
@error_logger.notify(e, method, *args)
|
17
|
+
|
18
|
+
raise ::Thrift::ApplicationException.new(
|
19
|
+
::Thrift::ApplicationException::INTERNAL_ERROR,
|
20
|
+
e.to_s
|
21
|
+
)
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
@@ -0,0 +1,42 @@
|
|
1
|
+
module Upfluence
|
2
|
+
module Utils
|
3
|
+
module Thrift
|
4
|
+
module Middleware
|
5
|
+
class RequestLogger
|
6
|
+
def initialize(app, logger)
|
7
|
+
@app = app
|
8
|
+
@logger = logger
|
9
|
+
end
|
10
|
+
|
11
|
+
def method_missing(method, *args, &block)
|
12
|
+
args_str = args.map(&:to_s).join(',')[0..99]
|
13
|
+
t0 = Time.now
|
14
|
+
|
15
|
+
@logger.info(
|
16
|
+
"Running method `#{method}` with args [#{args_str}]"
|
17
|
+
)
|
18
|
+
|
19
|
+
result = @app.send method, *args, &block
|
20
|
+
|
21
|
+
@logger.info(
|
22
|
+
"Finished method `#{method}` with args [#{args_str}]. Took: #{time_since(t0)}ms"
|
23
|
+
)
|
24
|
+
|
25
|
+
result
|
26
|
+
rescue => e
|
27
|
+
@logger.error(
|
28
|
+
"Finished method `#{method}` with args [#{args_str}] failed: #{e.class}. Took: #{time_since(t0)}ms"
|
29
|
+
)
|
30
|
+
raise e
|
31
|
+
end
|
32
|
+
|
33
|
+
private
|
34
|
+
|
35
|
+
def time_since(t0)
|
36
|
+
((Time.now - t0) * 1000).to_i
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end
|
42
|
+
end
|
@@ -0,0 +1,23 @@
|
|
1
|
+
module Upfluence
|
2
|
+
module Utils
|
3
|
+
module Thrift
|
4
|
+
module Middleware
|
5
|
+
class Timeout
|
6
|
+
def initialize(app, duration)
|
7
|
+
@app = app
|
8
|
+
@duration = duration
|
9
|
+
end
|
10
|
+
|
11
|
+
def method_missing(method, *args, &block)
|
12
|
+
::Timeout.timeout(@duration) { @app.send(method, *args, &block) }
|
13
|
+
rescue ::Timeout::Error
|
14
|
+
raise ::Thrift::ApplicationException.new(
|
15
|
+
::Thrift::ApplicationException::INTERNAL_ERROR,
|
16
|
+
'Timeout reached'
|
17
|
+
)
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
data/rbutils.gemspec
ADDED
@@ -0,0 +1,30 @@
|
|
1
|
+
# coding: utf-8
|
2
|
+
lib = File.expand_path('../lib', __FILE__)
|
3
|
+
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
4
|
+
require 'upfluence/utils/version'
|
5
|
+
|
6
|
+
Gem::Specification.new do |spec|
|
7
|
+
spec.name = 'upfluence-utils'
|
8
|
+
spec.version = Upfluence::Utils::VERSION
|
9
|
+
spec.authors = ['Upfluence']
|
10
|
+
spec.email = ['dev@upfluence.com']
|
11
|
+
|
12
|
+
spec.summary = 'Upfluence common utils for Ruby projects'
|
13
|
+
spec.homepage = 'https://github.com/upfluence/rbutils'
|
14
|
+
spec.license = 'MIT'
|
15
|
+
|
16
|
+
spec.files = `git ls-files -z`.split("\x0").reject { |f| f.match(%r{^(test|spec|features)/}) }
|
17
|
+
spec.require_paths = ["lib"]
|
18
|
+
|
19
|
+
spec.add_development_dependency "bundler", "~> 1.12"
|
20
|
+
spec.add_development_dependency "rake", "~> 10.0"
|
21
|
+
spec.add_development_dependency "rspec", "~> 3.0"
|
22
|
+
spec.add_runtime_dependency 'upfluence-thrift', '~> 1.0', '>= 1.0.3'
|
23
|
+
spec.add_runtime_dependency 'base-thrift', '~> 0.0.19'
|
24
|
+
spec.add_runtime_dependency 'sinatra'
|
25
|
+
spec.add_runtime_dependency 'redis'
|
26
|
+
spec.add_runtime_dependency 'sentry-raven'
|
27
|
+
spec.add_runtime_dependency 'sinatra-contrib'
|
28
|
+
spec.add_runtime_dependency 'activesupport'
|
29
|
+
spec.add_runtime_dependency 'active_model_serializers', '~> 0.9.0'
|
30
|
+
end
|
metadata
ADDED
@@ -0,0 +1,231 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: upfluence-utils
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.1.0
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Upfluence
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
date: 2017-06-27 00:00:00.000000000 Z
|
12
|
+
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
14
|
+
name: bundler
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
16
|
+
requirements:
|
17
|
+
- - "~>"
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: '1.12'
|
20
|
+
type: :development
|
21
|
+
prerelease: false
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
23
|
+
requirements:
|
24
|
+
- - "~>"
|
25
|
+
- !ruby/object:Gem::Version
|
26
|
+
version: '1.12'
|
27
|
+
- !ruby/object:Gem::Dependency
|
28
|
+
name: rake
|
29
|
+
requirement: !ruby/object:Gem::Requirement
|
30
|
+
requirements:
|
31
|
+
- - "~>"
|
32
|
+
- !ruby/object:Gem::Version
|
33
|
+
version: '10.0'
|
34
|
+
type: :development
|
35
|
+
prerelease: false
|
36
|
+
version_requirements: !ruby/object:Gem::Requirement
|
37
|
+
requirements:
|
38
|
+
- - "~>"
|
39
|
+
- !ruby/object:Gem::Version
|
40
|
+
version: '10.0'
|
41
|
+
- !ruby/object:Gem::Dependency
|
42
|
+
name: rspec
|
43
|
+
requirement: !ruby/object:Gem::Requirement
|
44
|
+
requirements:
|
45
|
+
- - "~>"
|
46
|
+
- !ruby/object:Gem::Version
|
47
|
+
version: '3.0'
|
48
|
+
type: :development
|
49
|
+
prerelease: false
|
50
|
+
version_requirements: !ruby/object:Gem::Requirement
|
51
|
+
requirements:
|
52
|
+
- - "~>"
|
53
|
+
- !ruby/object:Gem::Version
|
54
|
+
version: '3.0'
|
55
|
+
- !ruby/object:Gem::Dependency
|
56
|
+
name: upfluence-thrift
|
57
|
+
requirement: !ruby/object:Gem::Requirement
|
58
|
+
requirements:
|
59
|
+
- - "~>"
|
60
|
+
- !ruby/object:Gem::Version
|
61
|
+
version: '1.0'
|
62
|
+
- - ">="
|
63
|
+
- !ruby/object:Gem::Version
|
64
|
+
version: 1.0.3
|
65
|
+
type: :runtime
|
66
|
+
prerelease: false
|
67
|
+
version_requirements: !ruby/object:Gem::Requirement
|
68
|
+
requirements:
|
69
|
+
- - "~>"
|
70
|
+
- !ruby/object:Gem::Version
|
71
|
+
version: '1.0'
|
72
|
+
- - ">="
|
73
|
+
- !ruby/object:Gem::Version
|
74
|
+
version: 1.0.3
|
75
|
+
- !ruby/object:Gem::Dependency
|
76
|
+
name: base-thrift
|
77
|
+
requirement: !ruby/object:Gem::Requirement
|
78
|
+
requirements:
|
79
|
+
- - "~>"
|
80
|
+
- !ruby/object:Gem::Version
|
81
|
+
version: 0.0.19
|
82
|
+
type: :runtime
|
83
|
+
prerelease: false
|
84
|
+
version_requirements: !ruby/object:Gem::Requirement
|
85
|
+
requirements:
|
86
|
+
- - "~>"
|
87
|
+
- !ruby/object:Gem::Version
|
88
|
+
version: 0.0.19
|
89
|
+
- !ruby/object:Gem::Dependency
|
90
|
+
name: sinatra
|
91
|
+
requirement: !ruby/object:Gem::Requirement
|
92
|
+
requirements:
|
93
|
+
- - ">="
|
94
|
+
- !ruby/object:Gem::Version
|
95
|
+
version: '0'
|
96
|
+
type: :runtime
|
97
|
+
prerelease: false
|
98
|
+
version_requirements: !ruby/object:Gem::Requirement
|
99
|
+
requirements:
|
100
|
+
- - ">="
|
101
|
+
- !ruby/object:Gem::Version
|
102
|
+
version: '0'
|
103
|
+
- !ruby/object:Gem::Dependency
|
104
|
+
name: redis
|
105
|
+
requirement: !ruby/object:Gem::Requirement
|
106
|
+
requirements:
|
107
|
+
- - ">="
|
108
|
+
- !ruby/object:Gem::Version
|
109
|
+
version: '0'
|
110
|
+
type: :runtime
|
111
|
+
prerelease: false
|
112
|
+
version_requirements: !ruby/object:Gem::Requirement
|
113
|
+
requirements:
|
114
|
+
- - ">="
|
115
|
+
- !ruby/object:Gem::Version
|
116
|
+
version: '0'
|
117
|
+
- !ruby/object:Gem::Dependency
|
118
|
+
name: sentry-raven
|
119
|
+
requirement: !ruby/object:Gem::Requirement
|
120
|
+
requirements:
|
121
|
+
- - ">="
|
122
|
+
- !ruby/object:Gem::Version
|
123
|
+
version: '0'
|
124
|
+
type: :runtime
|
125
|
+
prerelease: false
|
126
|
+
version_requirements: !ruby/object:Gem::Requirement
|
127
|
+
requirements:
|
128
|
+
- - ">="
|
129
|
+
- !ruby/object:Gem::Version
|
130
|
+
version: '0'
|
131
|
+
- !ruby/object:Gem::Dependency
|
132
|
+
name: sinatra-contrib
|
133
|
+
requirement: !ruby/object:Gem::Requirement
|
134
|
+
requirements:
|
135
|
+
- - ">="
|
136
|
+
- !ruby/object:Gem::Version
|
137
|
+
version: '0'
|
138
|
+
type: :runtime
|
139
|
+
prerelease: false
|
140
|
+
version_requirements: !ruby/object:Gem::Requirement
|
141
|
+
requirements:
|
142
|
+
- - ">="
|
143
|
+
- !ruby/object:Gem::Version
|
144
|
+
version: '0'
|
145
|
+
- !ruby/object:Gem::Dependency
|
146
|
+
name: activesupport
|
147
|
+
requirement: !ruby/object:Gem::Requirement
|
148
|
+
requirements:
|
149
|
+
- - ">="
|
150
|
+
- !ruby/object:Gem::Version
|
151
|
+
version: '0'
|
152
|
+
type: :runtime
|
153
|
+
prerelease: false
|
154
|
+
version_requirements: !ruby/object:Gem::Requirement
|
155
|
+
requirements:
|
156
|
+
- - ">="
|
157
|
+
- !ruby/object:Gem::Version
|
158
|
+
version: '0'
|
159
|
+
- !ruby/object:Gem::Dependency
|
160
|
+
name: active_model_serializers
|
161
|
+
requirement: !ruby/object:Gem::Requirement
|
162
|
+
requirements:
|
163
|
+
- - "~>"
|
164
|
+
- !ruby/object:Gem::Version
|
165
|
+
version: 0.9.0
|
166
|
+
type: :runtime
|
167
|
+
prerelease: false
|
168
|
+
version_requirements: !ruby/object:Gem::Requirement
|
169
|
+
requirements:
|
170
|
+
- - "~>"
|
171
|
+
- !ruby/object:Gem::Version
|
172
|
+
version: 0.9.0
|
173
|
+
description:
|
174
|
+
email:
|
175
|
+
- dev@upfluence.com
|
176
|
+
executables: []
|
177
|
+
extensions: []
|
178
|
+
extra_rdoc_files: []
|
179
|
+
files:
|
180
|
+
- ".gitignore"
|
181
|
+
- ".rspec"
|
182
|
+
- ".travis.yml"
|
183
|
+
- Gemfile
|
184
|
+
- LICENSE.txt
|
185
|
+
- README.md
|
186
|
+
- Rakefile
|
187
|
+
- lib/upfluence.rb
|
188
|
+
- lib/upfluence/base/exceptions/validation_error.rb
|
189
|
+
- lib/upfluence/endpoint/api_endpoint.rb
|
190
|
+
- lib/upfluence/environment.rb
|
191
|
+
- lib/upfluence/error_logger.rb
|
192
|
+
- lib/upfluence/error_logger/null.rb
|
193
|
+
- lib/upfluence/error_logger/sentry.rb
|
194
|
+
- lib/upfluence/handler/base.rb
|
195
|
+
- lib/upfluence/logger.rb
|
196
|
+
- lib/upfluence/mixin/strong_parameters.rb
|
197
|
+
- lib/upfluence/utils.rb
|
198
|
+
- lib/upfluence/utils/http/middleware/logger.rb
|
199
|
+
- lib/upfluence/utils/http/middleware/null.rb
|
200
|
+
- lib/upfluence/utils/thrift.rb
|
201
|
+
- lib/upfluence/utils/thrift/middleware.rb
|
202
|
+
- lib/upfluence/utils/thrift/middleware/error_catcher.rb
|
203
|
+
- lib/upfluence/utils/thrift/middleware/request_logger.rb
|
204
|
+
- lib/upfluence/utils/thrift/middleware/timeout.rb
|
205
|
+
- lib/upfluence/utils/version.rb
|
206
|
+
- rbutils.gemspec
|
207
|
+
homepage: https://github.com/upfluence/rbutils
|
208
|
+
licenses:
|
209
|
+
- MIT
|
210
|
+
metadata: {}
|
211
|
+
post_install_message:
|
212
|
+
rdoc_options: []
|
213
|
+
require_paths:
|
214
|
+
- lib
|
215
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
216
|
+
requirements:
|
217
|
+
- - ">="
|
218
|
+
- !ruby/object:Gem::Version
|
219
|
+
version: '0'
|
220
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
221
|
+
requirements:
|
222
|
+
- - ">="
|
223
|
+
- !ruby/object:Gem::Version
|
224
|
+
version: '0'
|
225
|
+
requirements: []
|
226
|
+
rubyforge_project:
|
227
|
+
rubygems_version: 2.4.8
|
228
|
+
signing_key:
|
229
|
+
specification_version: 4
|
230
|
+
summary: Upfluence common utils for Ruby projects
|
231
|
+
test_files: []
|