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 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
@@ -0,0 +1,9 @@
1
+ /.bundle/
2
+ /.yardoc
3
+ /Gemfile.lock
4
+ /_yardoc/
5
+ /coverage/
6
+ /doc/
7
+ /pkg/
8
+ /spec/reports/
9
+ /tmp/
data/.rspec ADDED
@@ -0,0 +1,2 @@
1
+ --format documentation
2
+ --color
data/.travis.yml ADDED
@@ -0,0 +1,5 @@
1
+ sudo: false
2
+ language: ruby
3
+ rvm:
4
+ - 2.3.1
5
+ before_install: gem install bundler -v 1.12.3
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ source 'https://rubygems.org'
2
+
3
+ # Specify your gem's dependencies in rbutils.gemspec
4
+ gemspec
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
@@ -0,0 +1,6 @@
1
+ require "bundler/gem_tasks"
2
+ require "rspec/core/rake_task"
3
+
4
+ RSpec::Core::RakeTask.new(:spec)
5
+
6
+ task :default => :spec
data/lib/upfluence.rb ADDED
@@ -0,0 +1,4 @@
1
+ require 'upfluence/utils'
2
+ require 'upfluence/endpoint/api_endpoint'
3
+ require 'upfluence/mixin/strong_parameters'
4
+ require 'upfluence/base/exceptions/validation_error'
@@ -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,11 @@
1
+ require 'active_support/string_inquirer'
2
+
3
+ module Upfluence
4
+ class << self
5
+ def env
6
+ @env ||= ActiveSupport::StringInquirer.new(
7
+ ENV['ENV'] || ENV['RACK_ENV'] || 'development'
8
+ )
9
+ end
10
+ end
11
+ 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,6 @@
1
+ require 'upfluence/utils/version'
2
+ require 'upfluence/utils/thrift'
3
+ require 'upfluence/utils/http/middleware/logger'
4
+ require 'upfluence/logger'
5
+ require 'upfluence/error_logger'
6
+ require 'upfluence/environment'
@@ -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,3 @@
1
+ module Upfluence
2
+
3
+ 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
@@ -0,0 +1,5 @@
1
+ module Upfluence
2
+ module Utils
3
+ VERSION = '0.1.0'.freeze
4
+ end
5
+ 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: []