datumfactory 0.1.0 → 0.2.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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: f894820936bc03f06a3b84379fe706d88c72dae1f32be39a752f96bd277181aa
4
- data.tar.gz: 9db8449f2d72faecb1fc8190bcadd4dcd84125bc1cbe08bda89ca159b74d5162
3
+ metadata.gz: 1011bfbea1d54f85f28c0e299d808028e55aafd45364fb594f3af22e2f8f90af
4
+ data.tar.gz: 5cdfe12953c2cdd16884b02775aa5e630412fbb491390c54152e16720bb8729d
5
5
  SHA512:
6
- metadata.gz: c4dc2807dd987364f0ed8f551302c1b1a930083a012f424323699d8586c1bc576c1f00b60183c73e0022cc2eaf2ba6edc5fb60cb911d481bf89c65f6191fe6a0
7
- data.tar.gz: 87357100422226444c621efbadd5dd1dd28b7de744f8d116a21ca9f70dc87e0e2026cd843b11d59bcd4eba6a956a4c3acfd18aaa836a0f270804d435c622339a
6
+ metadata.gz: 5faa07f7b5174d0898d461eec68be975083429ab62922d7cae65382103f91214da14b18e0a5338c6e2e4e36d8cdd30b4a33a907116cd7de73289a5ac12ef2621
7
+ data.tar.gz: ee23e33eae5bf4e54cb3244edeec4604b200480155334967ed96185e169528098bcf5f03cd5b6a6d1c270b0e65e3a35a4daaba465a70f19423daf7bf6eec1c76
data/.rubocop.yml ADDED
@@ -0,0 +1,2 @@
1
+ AllCops:
2
+ TargetRubyVersion: 2.6.5
data/Gemfile.lock CHANGED
@@ -1,7 +1,7 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- datumfactory (0.1.0)
4
+ datumfactory (0.2.0)
5
5
  httparty (~> 0.18.1)
6
6
 
7
7
  GEM
data/datumfactory.gemspec CHANGED
@@ -22,7 +22,7 @@ Gem::Specification.new do |spec|
22
22
  spec.files = Dir.chdir(File.expand_path(__dir__)) do
23
23
  `git ls-files -z`.split("\x0").reject { |f| f.match(%r{\A(?:test|spec|features)/}) }
24
24
  end
25
- spec.bindir = "exe"
25
+ spec.bindir = 'exe'
26
26
  spec.executables = spec.files.grep(%r{\Aexe/}) { |f| File.basename(f) }
27
27
  spec.require_paths = ['lib']
28
28
 
data/lib/datumfactory.rb CHANGED
@@ -13,7 +13,32 @@ module Datumfactory
13
13
  attr_reader :auth_token
14
14
 
15
15
  format :json
16
- base_uri "datumfactory.com"
16
+ base_uri 'datumfactory.com'
17
+
18
+ def self.capture_event(container, auth_token, data, title, slug, description = nil)
19
+ body = {
20
+ query: {
21
+ event: {
22
+ data: data,
23
+ title: title,
24
+ description: description,
25
+ slug: slug
26
+ }
27
+ }
28
+ }
29
+
30
+ options = {
31
+ headers: {
32
+ 'Content-Type' => 'application/json',
33
+ 'X-API-Key' => auth_token.to_s
34
+ }
35
+ }
36
+
37
+ #options.merge!({ headers: { 'X-API-Key' => auth_token.to_s } })
38
+ options.merge!(body)
39
+ base_uri "https://#{container}.datumfactory.com"
40
+ post('/api/v1/events', options).parsed_response
41
+ end
17
42
 
18
43
  def initialize(site, email, password)
19
44
  self.class.base_uri "#{site}.datumfactory.com"
@@ -0,0 +1,21 @@
1
+ require 'socket'
2
+
3
+ module Datumfactory
4
+ class Config
5
+ class Boolean; end
6
+
7
+ DEVELOPMENT_ENVIRONMENTS = %w[development test].map(&:freeze).freeze
8
+
9
+ DEFAULT_PATHS = ['config/datumfactory.yml'].map(&:freeze).freeze
10
+
11
+ OPTIONS = {
12
+ api_key: {
13
+ description: 'The API key for your Datumfactory container.',
14
+ default: nil,
15
+ type: String
16
+ }
17
+ }.freeze
18
+
19
+ DEFAULTS = Hash[OPTIONS.map{|k,v| [k, v[:default]] }].freeze
20
+ end
21
+ end
@@ -0,0 +1,73 @@
1
+ require 'pathname'
2
+ require 'yaml'
3
+ require 'erb'
4
+
5
+ module Datumfactory
6
+ class Config
7
+ module Yaml
8
+ DISALLOWED_KEYS = [:'config.path'].freeze
9
+
10
+ def self.new(path, env = 'production')
11
+ path = path.kind_of?(Pathname) ? path : Pathname.new(path)
12
+
13
+ if !path.exist?
14
+ raise ConfigError, "The configuration file #{path} was not found."
15
+ elsif !path.file?
16
+ raise ConfigError, "The configuration file #{path} is not a file."
17
+ elsif !path.readable?
18
+ raise ConfigError, "The configuration file #{path} is not readable."
19
+ end
20
+
21
+ yaml = load_yaml(path)
22
+ yaml.merge!(yaml[env]) if yaml[env].kind_of?(Hash)
23
+
24
+ dotify_keys(yaml)
25
+ end
26
+
27
+ def self.load_yaml(path)
28
+ begin
29
+ yaml = YAML.load(ERB.new(path.read).result)
30
+ rescue => e
31
+ config_error = ConfigError.new(e.to_s)
32
+
33
+ if e.backtrace
34
+ backtrace = e.backtrace.map do |line|
35
+ if line.start_with?('(erb)'.freeze)
36
+ line.gsub('(erb)'.freeze, path.to_s)
37
+ else
38
+ line
39
+ end
40
+ end
41
+ config_error.set_backtrace(backtrace)
42
+ end
43
+
44
+ raise config_error
45
+ end
46
+
47
+ case yaml
48
+ when Hash
49
+ yaml
50
+ when NilClass, FalseClass
51
+ {}
52
+ else
53
+ raise ConfigError, "The configuration file #{path} is invalid."
54
+ end
55
+ end
56
+
57
+ def self.dotify_keys(hash, key_prefix = nil)
58
+ {}.tap do |new_hash|
59
+ hash.each_pair do |k,v|
60
+ k = [key_prefix, k].compact.join('.')
61
+ if v.kind_of?(Hash)
62
+ new_hash.update(dotify_keys(v, k))
63
+ else
64
+ next if DISALLOWED_KEYS.include?(k.to_sym)
65
+
66
+ new_hash[k.to_sym] = v
67
+ end
68
+ end
69
+ end
70
+ end
71
+ end
72
+ end
73
+ end
@@ -0,0 +1,390 @@
1
+ require 'pathname'
2
+ require 'delegate'
3
+ require 'logger'
4
+ require 'fileutils'
5
+ require 'openssl'
6
+
7
+ require 'datumfactory/version'
8
+ require 'datumfactory/backend'
9
+ require 'datumfactory/config/defaults'
10
+ require 'datumfactory/util/http'
11
+
12
+ module Datumfactory
13
+ class Config
14
+ extend Forwardable
15
+
16
+ class ConfigError < StandardError; end
17
+
18
+ KEY_REPLACEMENT = Regexp.new('[^a-z\d_]', Regexp::IGNORECASE).freeze
19
+
20
+ DOTTED_KEY = Regexp.new('\A([^\.]+)\.(.+)\z').freeze
21
+
22
+ NOT_BLANK = Regexp.new('\S').freeze
23
+
24
+ def initialize(opts = {})
25
+ @ruby = opts.freeze
26
+ @env = {}.freeze
27
+ @yaml = {}.freeze
28
+ @framework = {}.freeze
29
+ end
30
+
31
+ attr_accessor :ruby, :env, :yaml, :framework
32
+
33
+ def load!(framework: {}, env: ENV)
34
+ return self if @load
35
+
36
+ self.framework = framework.freeze
37
+ self.env = Env.new(env).freeze
38
+ load_config_from_disk {|yaml| self.yaml = yaml.freeze }
39
+ detect_revision!
40
+ @loaded = true
41
+ self
42
+ end
43
+
44
+ def configure
45
+ new_ruby = Ruby.new(self)
46
+ yield(new_ruby)
47
+ self.ruby = ruby.merge(new_ruby).freeze
48
+ @logger = @backend = nil
49
+ self
50
+ end
51
+
52
+ def backtrace_filter(&block)
53
+ if block_given?
54
+ warn('DEPRECATED: backtrace_filter is deprecated. Please use before_notify instead. See https://docs.honeybadger.io/ruby/support/v4-upgrade#backtrace_filter')
55
+ self[:backtrace_filter] = block
56
+ end
57
+
58
+ self[:backtrace_filter]
59
+ end
60
+
61
+ def before_notify_hooks
62
+ (ruby[:before_notify] || []).clone
63
+ end
64
+
65
+ def exception_filter(&block)
66
+ if block_given?
67
+ warn('DEPRECATED: exception_filter is deprecated. Please use before_notify instead. See https://docs.honeybadger.io/ruby/support/v4-upgrade#exception_filter')
68
+ self[:exception_filter] = block
69
+ end
70
+
71
+ self[:exception_filter]
72
+ end
73
+
74
+ def exception_fingerprint(&block)
75
+ if block_given?
76
+ warn('DEPRECATED: exception_fingerprint is deprecated. Please use before_notify instead. See https://docs.honeybadger.io/ruby/support/v4-upgrade#exception_fingerprint')
77
+ self[:exception_fingerprint] = block
78
+ end
79
+
80
+ self[:exception_fingerprint]
81
+ end
82
+
83
+ def get(key)
84
+ IVARS.each do |var|
85
+ source = instance_variable_get(var)
86
+ if source.has_key?(key)
87
+ return source[key]
88
+ end
89
+ end
90
+
91
+ DEFAULTS[key]
92
+ end
93
+ alias [] :get
94
+
95
+ def set(key, value)
96
+ self.ruby = ruby.merge(key => value).freeze
97
+ @logger = @backend = nil
98
+ end
99
+ alias []= :set
100
+
101
+ def to_hash(defaults = false)
102
+ hash = [:@ruby, :@env, :@yaml, :@framework].reverse.reduce({}) do |a,e|
103
+ a.merge!(instance_variable_get(e))
104
+ end
105
+
106
+ hash = DEFAULTS.merge(hash) if defaults
107
+
108
+ undotify_keys(hash.select {|k,v| DEFAULTS.has_key?(k) })
109
+ end
110
+ alias to_h to_hash
111
+
112
+ # Internal Helpers
113
+
114
+
115
+ def logger
116
+ init_logging! unless @logger
117
+ @logger
118
+ end
119
+
120
+ def backend
121
+ init_backend! unless @backend
122
+ @backend
123
+ end
124
+
125
+ def backend=(backend)
126
+ set(:backend, backend)
127
+ @backend = nil
128
+ end
129
+
130
+ def dev?
131
+ self[:env] && Array(self[:development_environments]).include?(self[:env])
132
+ end
133
+
134
+ def warn_development?
135
+ dev? && backend.kind_of?(Backend::Null)
136
+ end
137
+
138
+ def public?
139
+ return true if self[:report_data]
140
+ return false if self[:report_data] == false
141
+
142
+ !self[:env] || !dev?
143
+ end
144
+
145
+ def debug?
146
+ !!self[:debug]
147
+ end
148
+
149
+ def log_debug?
150
+ return debug? if self[:'logging.debug'].nil?
151
+
152
+ !!self[:'logging.debug']
153
+ end
154
+
155
+ def ignored_classes
156
+ ignore_only = get(:'exceptions.ignore_only')
157
+ return ignore_only if ignore_only
158
+ return DEFAULTS[:'exceptions.ignore'] unless ignore = get(:'exceptions.ignore')
159
+
160
+ DEFAULTS[:'exceptions.ignore'] | Array(ignore)
161
+ end
162
+
163
+ def ca_bundle_path
164
+ if self[:'connection.system_ssl_cert_chain'] && File.exist?(OpenSSL::X509::DEFAULT_CERT_FILE)
165
+ OpenSSL::X509::DEFAULT_CERT_FILE
166
+ elsif self[:'connection.ssl_ca_bundle_path']
167
+ self[:'connection.ssl_ca_bundle_path']
168
+ else
169
+ local_cert_path
170
+ end
171
+ end
172
+
173
+ def local_cert_path
174
+ File.expand_path(File.join('..', '..', '..', 'resources', 'ca-bundle.crt'), __FILE__)
175
+ end
176
+
177
+ def connection_port
178
+ if self[:'connection.port']
179
+ self[:'connection.port']
180
+ elsif self[:'connection.secure']
181
+ 443
182
+ else
183
+ 80
184
+ end
185
+ end
186
+
187
+ def connection_protocol
188
+ if self[:'connection.secure']
189
+ 'https'
190
+ else
191
+ 'http'
192
+ end
193
+ end
194
+
195
+ def max_queue_size
196
+ self[:max_queue_size]
197
+ end
198
+
199
+ def params_filters
200
+ Array(self[:'request.filter_keys'])
201
+ end
202
+
203
+ def excluded_request_keys
204
+ [].tap do |keys|
205
+ keys << :session if self[:'request.disable_session']
206
+ keys << :params if self[:'request.disable_params']
207
+ keys << :cgi_data if self[:'request.disable_environment']
208
+ keys << :url if self[:'request.disable_url']
209
+ end
210
+ end
211
+
212
+ def log_level(key = :'logging.level')
213
+ case self[key].to_s
214
+ when /\A(0|debug)\z/i then Logger::DEBUG
215
+ when /\A(1|info)\z/i then Logger::INFO
216
+ when /\A(2|warn)\z/i then Logger::WARN
217
+ when /\A(3|error)\z/i then Logger::ERROR
218
+ else
219
+ Logger::INFO
220
+ end
221
+ end
222
+
223
+ def load_plugin?(name)
224
+ return false if includes_token?(self[:'skipped_plugins'], name)
225
+ return true unless self[:plugins].kind_of?(Array)
226
+
227
+ includes_token?(self[:plugins], name)
228
+ end
229
+
230
+ def root_regexp
231
+ return @root_regexp if @root_regexp
232
+ return nil if @no_root
233
+
234
+ root = get(:root).to_s
235
+ @no_root = true and return nil unless root =~ NOT_BLANK
236
+
237
+ @root_regexp = Regexp.new("^#{ Regexp.escape(root) }")
238
+ end
239
+
240
+ def detected_framework
241
+ if self[:framework] =~ NOT_BLANK
242
+ self[:framework].to_sym
243
+ elsif defined?(::Rails::VERSION) && ::Rails::VERSION::STRING > '3.0'
244
+ :rails
245
+ elsif defined?(::Sinatra::VERSION)
246
+ :sinatra
247
+ elsif defined?(::Rack.release)
248
+ :rack
249
+ else
250
+ :ruby
251
+ end
252
+ end
253
+
254
+ def framework_name
255
+ case detected_framework
256
+ when :rails then "Rails #{::Rails::VERSION::STRING}"
257
+ when :sinatra then "Sinatra #{::Sinatra::VERSION}"
258
+ when :rack then "Rack #{::Rack.release}"
259
+ else
260
+ "Ruby #{RUBY_VERSION}"
261
+ end
262
+ end
263
+
264
+ private
265
+
266
+ def detect_revision!
267
+ return if self[:revision]
268
+
269
+ set(:revision, Util::Revision.detect(self[:root]))
270
+ end
271
+
272
+ def log_path
273
+ return if log_stdout?
274
+ return if !self[:'logging.path']
275
+
276
+ locate_absolute_path(self[:'logging.path'], self[:root])
277
+ end
278
+
279
+ def config_path
280
+ config_paths.first
281
+ end
282
+
283
+ def config_paths
284
+ Array(ENV['HONEYBADGER_CONFIG_PATH'] || get(:'config.path')).map do |c|
285
+ locate_absolute_path(c, self[:root])
286
+ end
287
+ end
288
+
289
+ def default_backend
290
+ return Backend::Server.new(self) if public?
291
+
292
+ Backend::Null.new(self)
293
+ end
294
+
295
+ def init_backend!
296
+ if self[:backend].is_a?(String) || self[:backend].is_a?(Symbol)
297
+ @backend = Backend.for(self[:backend].to_sym).new(self)
298
+ return
299
+ end
300
+
301
+ if ruby[:backend].respond_to?(:notify)
302
+ @backend = ruby[:backend]
303
+ return
304
+ end
305
+
306
+ if ruby[:backend]
307
+ logger.warn(sprintf('Unknown backend: %p; default will be used. Backend must respond to #notify', self[:backend]))
308
+ end
309
+
310
+ @backend = default_backend
311
+ end
312
+
313
+ def build_stdout_logger
314
+ logger = Logger.new($stdout)
315
+ logger.formatter = lambda do |severity, datetime, progname, msg|
316
+ "#{msg}\n"
317
+ end
318
+ logger.level = log_level
319
+ Logging::FormattedLogger.new(logger)
320
+ end
321
+
322
+ def build_file_logger(path)
323
+ Logger.new(path).tap do |logger|
324
+ logger.level = log_level
325
+ logger.formatter = Logger::Formatter.new
326
+ end
327
+ end
328
+
329
+ def log_stdout?
330
+ self[:'logging.path'] && self[:'logging.path'].to_s.downcase == 'stdout'
331
+ end
332
+
333
+ def build_logger
334
+ return ruby[:logger] if ruby[:logger]
335
+
336
+ return build_stdout_logger if log_stdout?
337
+
338
+ if path = log_path
339
+ FileUtils.mkdir_p(path.dirname) unless path.dirname.writable?
340
+ return build_file_logger(path)
341
+ end
342
+
343
+ return framework[:logger] if framework[:logger]
344
+
345
+ Logger.new(nil)
346
+ end
347
+
348
+ def init_logging!
349
+ @logger = Logging::ConfigLogger.new(self, build_logger)
350
+ end
351
+
352
+ # Takes an Array and a value and returns true if the value exists in the
353
+ # array in String or Symbol form, otherwise false.
354
+ def includes_token?(obj, value)
355
+ return false unless obj.kind_of?(Array)
356
+
357
+ obj.map(&:to_sym).include?(value.to_sym)
358
+ end
359
+
360
+ def locate_absolute_path(path, root)
361
+ path = Pathname.new(path.to_s)
362
+ if path.absolute?
363
+ path
364
+ else
365
+ Pathname.new(root.to_s).join(path.to_s)
366
+ end
367
+ end
368
+
369
+ def load_config_from_disk
370
+ if (path = config_paths.find(&:exist?)) && path.file?
371
+ Yaml.new(path, self[:env]).tap do |yml|
372
+ yield(yml) if block_given?
373
+ end
374
+ end
375
+ end
376
+
377
+ def undotify_keys(hash)
378
+ {}.tap do |new_hash|
379
+ hash.each_pair do |k,v|
380
+ if k.to_s =~ DOTTED_KEY
381
+ new_hash[$1] ||= {}
382
+ new_hash[$1] = undotify_keys(new_hash[$1].merge({$2 => v}))
383
+ else
384
+ new_hash[k.to_s] = v
385
+ end
386
+ end
387
+ end
388
+ end
389
+ end
390
+ end
@@ -0,0 +1,89 @@
1
+ require 'forwardable'
2
+ require 'net/http'
3
+ require 'json'
4
+ require 'zlib'
5
+ require 'openssl'
6
+
7
+ require 'datumfactory/version'
8
+
9
+ module Datumfactory
10
+ module Util
11
+ class HTTP
12
+ extend Forwardable
13
+
14
+ HEADERS = {
15
+ 'Content-type'.freeze => 'application/json'.freeze,
16
+ 'Content-Encoding'.freeze => 'deflate'.freeze,
17
+ 'Accept'.freeze => 'text/json, application/json'.freeze,
18
+ 'User-Agent'.freeze => "HB-Ruby #{VERSION}; #{RUBY_VERSION}; #{RUBY_PLATFORM}".freeze
19
+ }.freeze
20
+
21
+ ERRORS = [Timeout::Error,
22
+ Errno::EINVAL,
23
+ Errno::ECONNRESET,
24
+ Errno::ECONNREFUSED,
25
+ Errno::ENETUNREACH,
26
+ EOFError,
27
+ Net::HTTPBadResponse,
28
+ Net::HTTPHeaderSyntaxError,
29
+ Net::ProtocolError,
30
+ OpenSSL::SSL::SSLError,
31
+ SocketError].freeze
32
+
33
+ def initialize(config)
34
+ @config = config
35
+ end
36
+
37
+ def get(endpoint)
38
+ response = http_connection.get(endpoint)
39
+ debug { format('http method=GET path=%s code=%d', endpoint.dump, response.code) }
40
+ response
41
+ end
42
+
43
+ def post(endpoint, payload, headers = nil)
44
+ response = http_connection.post(endpoint, compress(payload.to_json), http_headers(headers))
45
+ debug { format('http method=POST path=%s code=%d', endpoint.dump, response.code) }
46
+ response
47
+ end
48
+
49
+ private
50
+
51
+ attr_reader :config
52
+
53
+ def http_connection
54
+ setup_http_connection
55
+ end
56
+
57
+ def http_headers(headers = nil)
58
+ {}.tap do |hash|
59
+ hash.merge!(HEADERS)
60
+ hash.merge!({ 'X-API-Key' => config[:api_key].to_s })
61
+ hash.merge!(headers) if headers
62
+ end
63
+ end
64
+
65
+ def setup_http_connection
66
+ http_class = Net::HTTP::Proxy(config[:'connection.proxy_host'], config[:'connection.proxy_port'], config[:'connection.proxy_user'], config[:'connection.proxy_pass'])
67
+ http = http_class.new(config[:'connection.host'], config.connection_port)
68
+
69
+ http.read_timeout = config[:'connection.http_read_timeout']
70
+ http.open_timeout = config[:'connection.http_open_timeout']
71
+
72
+ if config[:'connection.secure']
73
+ http.use_ssl = true
74
+
75
+ http.ca_file = config.ca_bundle_path
76
+ http.verify_mode = OpenSSL::SSL::VERIFY_PEER
77
+ else
78
+ http.use_ssl = false
79
+ end
80
+
81
+ http
82
+ end
83
+
84
+ def compress(string, level = Zlib::DEFAULT_COMPRESSION)
85
+ Zlib::Deflate.deflate(string, level)
86
+ end
87
+ end
88
+ end
89
+ end
@@ -1,3 +1,3 @@
1
1
  module Datumfactory
2
- VERSION = "0.1.0"
2
+ VERSION = '0.2.0'.freeze
3
3
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: datumfactory
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.0
4
+ version: 0.2.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Rob Bazinet
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2021-04-21 00:00:00.000000000 Z
11
+ date: 2021-04-23 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: httparty
@@ -32,6 +32,7 @@ extensions: []
32
32
  extra_rdoc_files: []
33
33
  files:
34
34
  - ".gitignore"
35
+ - ".rubocop.yml"
35
36
  - CODE_OF_CONDUCT.md
36
37
  - Gemfile
37
38
  - Gemfile.lock
@@ -40,6 +41,10 @@ files:
40
41
  - Rakefile
41
42
  - datumfactory.gemspec
42
43
  - lib/datumfactory.rb
44
+ - lib/datumfactory/config/defaults.rb
45
+ - lib/datumfactory/config/yaml.rb
46
+ - lib/datumfactory/util/config.rb
47
+ - lib/datumfactory/util/http.rb
43
48
  - lib/datumfactory/version.rb
44
49
  homepage: https://github.com/rbazinet/datumfactory-ruby
45
50
  licenses: