namira 1.2.0 → 1.3.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
- SHA1:
3
- metadata.gz: cd40f31fd9c696c000f54eeb83a09db3c4213222
4
- data.tar.gz: a707df33c8946cb12d735e7ba9ec5204c54ae3fc
2
+ SHA256:
3
+ metadata.gz: 61aed0e8cd917b38eb9eb293a3ed95331aa79066c08e1230fd517335bfe4a9c4
4
+ data.tar.gz: 129c02c6bb5a4fdaad7d1f91fd6f7ea1f6811ce30d6d20cb3f5b995954742e0a
5
5
  SHA512:
6
- metadata.gz: 8149d922e3f943da4717c04df6aca73d51b27a8a45e69108e37b2d47bb35624e31638df989ca102b7288bb9c2524938509a7e1263406dd957357b1edf62b54c4
7
- data.tar.gz: faca22f4f42291a472e2c19486b2953648e9dd29116a9f49b282d96eee27906b87994b89f7e240dcead130e357d88d67399145cc769179a9dd0bc65edfb134c2
6
+ metadata.gz: 847929dfe0d91112e5d54f6b8f0a3889ea9b2f0245e556d22d6c6b5681aa3ba97cf2c503c055e4fd118c0036c9c45cc3d8e1b3ab16edbea0775066f4603471ca
7
+ data.tar.gz: d4db9832c632cc6b299484ca7116236d9792dfec85ca569a5b747670bfd76fa26c9fe92754225df4efab01325f6bb3cc75bdc253f781f89fcc18a4d911819b2c
@@ -1,8 +1,7 @@
1
1
  AllCops:
2
- TargetRubyVersion: 2.2
2
+ TargetRubyVersion: 2.3
3
3
  Exclude:
4
4
  - Rakefile
5
- - spec/**/*
6
5
 
7
6
  Metrics/AbcSize:
8
7
  Max: 30
@@ -19,6 +18,11 @@ Metrics/ParameterLists:
19
18
  Metrics/CyclomaticComplexity:
20
19
  Max: 10
21
20
 
21
+ Metrics/BlockLength:
22
+ Enabled: true
23
+ Exclude:
24
+ - spec/**/*
25
+
22
26
  DotPosition:
23
27
  EnforcedStyle: leading
24
28
 
@@ -0,0 +1 @@
1
+ 2.5.3
@@ -1,3 +1,7 @@
1
+ # 1.3 (2019-02-06)
2
+
3
+ * Support for async requests. Supports ActiveJob and plain Sidekiq.
4
+
1
5
  # 1.2 (2019-01-18)
2
6
 
3
7
  * Auto add Bugsnag error information
@@ -1,3 +1,4 @@
1
+ require 'namira/async'
1
2
  require 'namira/config'
2
3
  require 'namira/env'
3
4
  require 'namira/error_helpers'
@@ -0,0 +1,5 @@
1
+ require_relative 'async/active_job/request_job' if defined?(::ActiveJob)
2
+ require_relative 'async/sidekiq/request_worker' if defined?(::Sidekiq)
3
+
4
+ require_relative 'async/performer'
5
+ require_relative 'async/serializer'
@@ -0,0 +1,13 @@
1
+ require_relative '../performer'
2
+
3
+ module Namira
4
+ module Async
5
+ module ActiveJob
6
+ class RequestJob < ::ActiveJob::Base
7
+ def perform(payload)
8
+ Namira::Async::Performer.perform(payload)
9
+ end
10
+ end
11
+ end
12
+ end
13
+ end
@@ -0,0 +1,47 @@
1
+ require_relative 'serializer'
2
+ require_relative '../config'
3
+ require_relative '../errors'
4
+
5
+ module Namira
6
+ module Async
7
+ class Performer
8
+ class << self
9
+ def schedule(request, async_adapter, queue_name)
10
+ async_adapter = adapter(async_adapter)
11
+ queue_name ||= Namira.config.async_queue_name
12
+ payload = Namira::Async::Serializer.serialize_request(request)
13
+
14
+ case async_adapter
15
+ when :active_job
16
+ Namira::Async::ActiveJob::RequestJob.set(queue: queue_name).perform_later(payload)
17
+ when :sidekiq
18
+ Namira::Async::Sidekiq::RequestWorker.set(queue: queue_name).perform_async(payload)
19
+ when :thread
20
+ Thread.new { perform(payload) }
21
+ else
22
+ raise Namira::Errors::AsyncError, "Unknown Async Adapter #{async_adapter}"
23
+ end
24
+ end
25
+
26
+ def perform(payload)
27
+ request = Namira::Async::Serializer.unserialize_request(payload)
28
+ request.send_request
29
+ Namira::Async::Serializer.serialize_response(request.response)
30
+ end
31
+
32
+ private
33
+
34
+ def adapter(async_adapter)
35
+ async_adapter ||= Namira.config.async_adapter
36
+ if async_adapter == :active_job && !defined?(ActiveJob)
37
+ adapter(:sidekiq)
38
+ elsif async_adapter == :sidekiq && !defined?(Sidekiq)
39
+ adapter(:thread)
40
+ else
41
+ async_adapter
42
+ end
43
+ end
44
+ end
45
+ end
46
+ end
47
+ end
@@ -0,0 +1,61 @@
1
+ require_relative '../errors'
2
+
3
+ module Namira
4
+ module Async
5
+ class Serializer
6
+ class << self
7
+ def serialize_request(request)
8
+ JSON.dump(
9
+ u: request.uri,
10
+ m: request.http_method,
11
+ h: request.headers.to_h,
12
+ b: request.body,
13
+ c: request.config.to_h
14
+ )
15
+ end
16
+
17
+ def unserialize_request(payload)
18
+ data = JSON.parse(payload)
19
+ Namira::Request.new(
20
+ uri: data.fetch('u'),
21
+ http_method: data.fetch('m').to_sym,
22
+ headers: data.fetch('h'),
23
+ body: data.fetch('b'),
24
+ config: data.fetch('c')
25
+ )
26
+ rescue KeyError => error
27
+ raise Namira::Errors::AsyncError, error.message
28
+ end
29
+
30
+ def serialize_response(response)
31
+ backing = response.instance_variable_get('@backing')
32
+ JSON.dump(
33
+ b: backing.to_a,
34
+ m: response.method,
35
+ r: response.redirect_count,
36
+ u: response.url.to_s,
37
+ v: backing.instance_variable_get('@version')
38
+ )
39
+ end
40
+
41
+ def unserialize_response(payload)
42
+ data = JSON.parse(payload)
43
+ backing = HTTP::Response.new(
44
+ status: data.fetch('b')[0],
45
+ headers: data.fetch('b')[1],
46
+ body: data.fetch('b')[2],
47
+ version: data.fetch('v')
48
+ )
49
+ Namira::Response.new(
50
+ data.fetch('m').to_sym,
51
+ Addressable::URI.parse(data.fetch('u')),
52
+ data.fetch('r').to_i,
53
+ backing
54
+ )
55
+ rescue KeyError => error
56
+ raise Namira::Errors::AsyncError, error.message
57
+ end
58
+ end
59
+ end
60
+ end
61
+ end
@@ -0,0 +1,15 @@
1
+ require_relative '../performer'
2
+
3
+ module Namira
4
+ module Async
5
+ module ActiveJob
6
+ class RequestWorker
7
+ include Sidekiq::Worker
8
+
9
+ def perform(payload)
10
+ Namira::Async::Performer.perform(payload)
11
+ end
12
+ end
13
+ end
14
+ end
15
+ end
@@ -30,11 +30,20 @@ module Namira
30
30
  #
31
31
  # @!attribute [r] headers
32
32
  # Default headers to send with each request
33
- # @return [OpenStruct]
33
+ # @return [Hash]
34
34
  #
35
35
  # @!attribute [rw] log_requests
36
36
  # Log requests using puts or Rails.logger.debug if it's defined
37
37
  # @return (Bool) Defaults: true
38
+ #
39
+ # @!attribute [rw] async_queue_name
40
+ # The queue name that async requests should be added too.
41
+ # @return [Symbol] Defaults: :default
42
+ #
43
+ # @!attribute [rw] async_adapter
44
+ # The preferred async adapter to use.
45
+ # Possible Values: :active_job, :sidekiq
46
+ # @return [Symbol] Defaults: :active_job
38
47
  class Config < OpenStruct
39
48
  ##
40
49
  # The
@@ -45,11 +54,13 @@ module Namira
45
54
 
46
55
  DEFAULT_SETTINGS = {
47
56
  max_redirect: 3,
48
- timeout: 5.0,
49
- backend: nil,
50
- user_agent: "Namira/#{Namira::VERSION}",
51
- headers: OpenStruct.new,
52
- log_requests: true
57
+ timeout: 5.0,
58
+ backend: nil,
59
+ user_agent: "Namira/#{Namira::VERSION}",
60
+ headers: {},
61
+ log_requests: true,
62
+ async_queue_name: :default,
63
+ async_adapter: :active_job
53
64
  }.freeze
54
65
 
55
66
  private_constant :DEFAULT_SETTINGS
@@ -62,8 +73,12 @@ module Namira
62
73
  ##
63
74
  # The shared configuration
64
75
  def self.configure
76
+ yield(config) if block_given?
77
+ config
78
+ end
79
+
80
+ def self.config
65
81
  @config ||= Config.new
66
- yield(@config) if block_given?
67
82
  @config
68
83
  end
69
84
  end
@@ -17,13 +17,12 @@ if defined?(::Bugsnag)
17
17
  notification.exceptions.each do |exception|
18
18
  next unless exception.is_a?(Namira::Errors::HTTPError)
19
19
 
20
- notification.add_tab("Namira #{exception.response.status.to_i}", {
20
+ notification.add_tab("Namira #{exception.response.status.to_i}",
21
21
  headers: exception.response.headers.to_h,
22
22
  body: exception.response.body.to_s[0...200],
23
23
  method: exception.response.method.to_s,
24
24
  url: exception.response.url.to_s,
25
- redirected: (exception.response.redirect_count > 0).to_s
26
- })
25
+ redirected: (exception.response.redirect_count > 0).to_s)
27
26
  end
28
27
 
29
28
  @bugsnag.call(notification)
@@ -1,4 +1,5 @@
1
1
  require_relative 'errors/base_error'
2
+ require_relative 'errors/async_error'
2
3
  require_relative 'errors/http_error'
3
4
  require_relative 'errors/invalid_uri_error'
4
5
  require_relative 'errors/redirect_error'
@@ -0,0 +1,8 @@
1
+ module Namira
2
+ module Errors
3
+ ##
4
+ # An async API error
5
+ class AsyncError < BaseError
6
+ end
7
+ end
8
+ end
@@ -87,7 +87,7 @@ module Namira
87
87
  '415' => 'Unsupported Media Type',
88
88
  '416' => 'Range Not Satisfiable',
89
89
  '417' => 'Expectation Failed',
90
- '418' => "Im A Teapot",
90
+ '418' => 'Im A Teapot',
91
91
  '421' => 'Misdirected Request',
92
92
  '422' => 'Unprocessable Entity',
93
93
  '423' => 'Locked',
@@ -19,7 +19,7 @@ module Namira
19
19
  private
20
20
 
21
21
  def merge_config(config)
22
- Namira.configure.to_h.merge(config)
22
+ Namira.config.to_h.merge(config)
23
23
  end
24
24
  end
25
25
  end
@@ -12,7 +12,7 @@ module Namira
12
12
  #
13
13
  # @param env [Namira::Env] The request environment
14
14
  def call(env)
15
- headers = Hash(Namira.configure.headers.to_h).dup
15
+ headers = Hash(Namira.config.headers.to_h).dup
16
16
  headers.merge!(additional_headers(env))
17
17
  headers.merge!(env.headers.to_h)
18
18
  env.headers = convert_headers(headers)
@@ -23,15 +23,16 @@ module Namira
23
23
  private
24
24
 
25
25
  def logging?(env)
26
- if env.config.keys.include?(SKIP_LOGGING_KEY)
26
+ if env.config.key?(SKIP_LOGGING_KEY)
27
27
  env.config[SKIP_LOGGING_KEY] == false
28
28
  else
29
- Namira.configure.log_requests == true
29
+ Namira.config.log_requests == true
30
30
  end
31
31
  end
32
32
 
33
33
  def log_request(env)
34
34
  return unless logging?(env)
35
+
35
36
  message = "#{env.method.to_s.upcase} - #{env.uri}"
36
37
  if defined?(::Rails)
37
38
  Rails.logger.debug(message)
@@ -15,9 +15,9 @@ module Namira
15
15
  timeout = env.config[:timeout] || 30.0
16
16
  http = HTTP.timeout(
17
17
  :per_operation,
18
- write: timeout,
18
+ write: timeout,
19
19
  connect: timeout,
20
- read: timeout
20
+ read: timeout
21
21
  )
22
22
  http = http.headers(env.headers)
23
23
  env.response = http.send(env.method, env.uri, body: env.body)
@@ -59,6 +59,7 @@ module Namira
59
59
 
60
60
  def redirect?(error, env)
61
61
  return false unless env.config[:follow_redirect].nil? ? true : env.config[:follow_redirect]
62
+
62
63
  REDIRECT_STATUS.include?(error.status)
63
64
  end
64
65
  end
@@ -1,4 +1,5 @@
1
1
  require 'http'
2
+ require_relative 'async'
2
3
 
3
4
  module Namira
4
5
  ##
@@ -6,7 +7,7 @@ module Namira
6
7
  #
7
8
  # response = Namira::Request.new(uri: 'https://httpbin.org/headers').response
8
9
  class Request
9
- attr_reader :uri, :http_method
10
+ attr_reader :uri, :http_method, :headers, :body, :config
10
11
 
11
12
  ##
12
13
  # Create a new request
@@ -21,7 +22,7 @@ module Namira
21
22
  @http_method = http_method
22
23
  @headers = Hash(headers)
23
24
  @body = body
24
- @config = Namira.configure.to_h.merge(Hash(config))
25
+ @config = Namira.config.to_h.merge(Hash(config))
25
26
  @stack = Namira::Stack.default
26
27
  end
27
28
 
@@ -33,6 +34,10 @@ module Namira
33
34
  @response = _send_request
34
35
  end
35
36
 
37
+ def send_async(queue_name: nil, adapter: nil)
38
+ Namira::Async::Performer.schedule(self, adapter, queue_name)
39
+ end
40
+
36
41
  ##
37
42
  # The {Namira::Response} for the request.
38
43
  #
@@ -1,3 +1,5 @@
1
+ require_relative 'async/serializer'
2
+
1
3
  module Namira
2
4
  ##
3
5
  # HTTP response
@@ -8,6 +10,10 @@ module Namira
8
10
 
9
11
  attr_reader :redirect_count
10
12
 
13
+ def self.serialized(raw_response)
14
+ Namira::Async::Serializer.unserialize_response(raw_response)
15
+ end
16
+
11
17
  ##
12
18
  # Create a new {Namira::Response}
13
19
  def initialize(method, url, redirect_count, backing)
@@ -30,6 +30,7 @@ module Namira
30
30
  # @param env [Namira::Env] The request environment
31
31
  def call(env)
32
32
  raise ArgumentError, 'Invalid environment' unless env.is_a?(Namira::Env)
33
+
33
34
  to_app.call(env)
34
35
  end
35
36
 
@@ -1,5 +1,5 @@
1
1
  module Namira
2
2
  ##
3
3
  # The current version of Namira
4
- VERSION = '1.2.0'.freeze
4
+ VERSION = '1.3.0'.freeze
5
5
  end
@@ -18,15 +18,16 @@ Gem::Specification.new do |spec|
18
18
  spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
19
19
  spec.require_paths = ['lib']
20
20
 
21
- spec.required_ruby_version = '>= 2.2'
21
+ spec.required_ruby_version = '>= 2.3'
22
22
 
23
23
  spec.add_dependency 'http', '>= 2.0.0', '< 4.0'
24
24
 
25
- spec.add_development_dependency 'bundler', '~> 1.15'
26
- spec.add_development_dependency 'pry', '~> 0.11.3'
27
- spec.add_development_dependency 'rake', '~> 10.0'
28
- spec.add_development_dependency 'rspec', '~> 3.2'
29
- spec.add_development_dependency 'simplecov', '~> 0.15'
30
- spec.add_development_dependency 'webmock', '~> 3.3.0'
31
- spec.add_development_dependency 'yard', '~> 0.9'
25
+ spec.add_development_dependency 'bundler', '~> 1.15'
26
+ spec.add_development_dependency 'pry', '~> 0.11.3'
27
+ spec.add_development_dependency 'rake', '~> 10.0'
28
+ spec.add_development_dependency 'rspec', '~> 3.2'
29
+ spec.add_development_dependency 'simplecov', '~> 0.15'
30
+ spec.add_development_dependency 'sinatra', '~> 2.0.5'
31
+ spec.add_development_dependency 'webmock', '~> 3.3.0'
32
+ spec.add_development_dependency 'yard', '~> 0.9'
32
33
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: namira
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.2.0
4
+ version: 1.3.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Skylar Schipper
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2019-01-18 00:00:00.000000000 Z
11
+ date: 2019-02-06 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: http
@@ -100,6 +100,20 @@ dependencies:
100
100
  - - "~>"
101
101
  - !ruby/object:Gem::Version
102
102
  version: '0.15'
103
+ - !ruby/object:Gem::Dependency
104
+ name: sinatra
105
+ requirement: !ruby/object:Gem::Requirement
106
+ requirements:
107
+ - - "~>"
108
+ - !ruby/object:Gem::Version
109
+ version: 2.0.5
110
+ type: :development
111
+ prerelease: false
112
+ version_requirements: !ruby/object:Gem::Requirement
113
+ requirements:
114
+ - - "~>"
115
+ - !ruby/object:Gem::Version
116
+ version: 2.0.5
103
117
  - !ruby/object:Gem::Dependency
104
118
  name: webmock
105
119
  requirement: !ruby/object:Gem::Requirement
@@ -139,6 +153,7 @@ files:
139
153
  - ".gitignore"
140
154
  - ".rspec"
141
155
  - ".rubocop.yml"
156
+ - ".ruby-version"
142
157
  - ".travis.yml"
143
158
  - CHANGELOG.md
144
159
  - Gemfile
@@ -148,10 +163,16 @@ files:
148
163
  - bin/console
149
164
  - bin/setup
150
165
  - lib/namira.rb
166
+ - lib/namira/async.rb
167
+ - lib/namira/async/active_job/request_job.rb
168
+ - lib/namira/async/performer.rb
169
+ - lib/namira/async/serializer.rb
170
+ - lib/namira/async/sidekiq/request_worker.rb
151
171
  - lib/namira/config.rb
152
172
  - lib/namira/env.rb
153
173
  - lib/namira/error_helpers.rb
154
174
  - lib/namira/errors.rb
175
+ - lib/namira/errors/async_error.rb
155
176
  - lib/namira/errors/base_error.rb
156
177
  - lib/namira/errors/http_error.rb
157
178
  - lib/namira/errors/invalid_uri_error.rb
@@ -184,7 +205,7 @@ required_ruby_version: !ruby/object:Gem::Requirement
184
205
  requirements:
185
206
  - - ">="
186
207
  - !ruby/object:Gem::Version
187
- version: '2.2'
208
+ version: '2.3'
188
209
  required_rubygems_version: !ruby/object:Gem::Requirement
189
210
  requirements:
190
211
  - - ">="
@@ -192,7 +213,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
192
213
  version: '0'
193
214
  requirements: []
194
215
  rubyforge_project:
195
- rubygems_version: 2.5.2.3
216
+ rubygems_version: 2.7.6
196
217
  signing_key:
197
218
  specification_version: 4
198
219
  summary: A simple wrapper around HTTP