whiplash-app 0.4.0 → 0.6.2

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
- SHA1:
3
- metadata.gz: 5b252f73755dcd2e6cc7d907d0009393a0661993
4
- data.tar.gz: d38c18badde2221b2c9de962db701ca54a486739
2
+ SHA256:
3
+ metadata.gz: 3d70f904d63e4b816748c23f7a3287763420f8c222b2e7e4c63519c63614613f
4
+ data.tar.gz: 3a267c71d211814b6e85bfd8fa8075d1f048a90f936187b4353a0d36bc40b4d2
5
5
  SHA512:
6
- metadata.gz: ae66c0ded848674943ad97c2781236569d4d3cfff515ad30218b7025028660ce0cf10374f243d516b553781ca3a023460ca4b25e21713225b0a135c3ec6708cb
7
- data.tar.gz: '04845cdd6a2eb36c1f05b43932f11bf25c6d32d0c91fbb74db2cb7f23b5ca9d84d3f8719fe58926831a4d26c1c07119aa03d3b74a92b7f1528f706fa169b37f1'
6
+ metadata.gz: 27d80f78a200d4d202b1236941ec7b7c5b3b2e185bdcf3c51ba98ce7304010cc258c350f2cf524d684343eae2c8111f837ced586b10fda93439ed2f1e608a556
7
+ data.tar.gz: 92bcf7a2e0bc197d7b97aba7d4748fe4759b9ede63cf60ec9027645b44ee4155057b2e6b63ed2177b2a6d208fb8563409cf2d7dae0bb68a04cca0e8c0eed8e8c
data/.gitignore CHANGED
@@ -9,3 +9,4 @@
9
9
  /tmp/
10
10
  .env
11
11
  .ruby-version
12
+ .idea/*
@@ -0,0 +1,99 @@
1
+ class WhiplashApiError < StandardError
2
+ class Deadlock < WhiplashApiError
3
+ end
4
+
5
+ class Timeout < WhiplashApiError
6
+ end
7
+
8
+ class Configuration < WhiplashApiError
9
+ end
10
+
11
+ class Inventory < WhiplashApiError
12
+ end
13
+
14
+ class Ignore < WhiplashApiError
15
+ end
16
+
17
+ # 400
18
+ class BadRequest < WhiplashApiError
19
+ end
20
+
21
+ # 401
22
+ class Unauthorized < WhiplashApiError
23
+ end
24
+
25
+ # 403
26
+ class Forbidden < WhiplashApiError
27
+ end
28
+
29
+ # 404
30
+ class RecordNotFound < WhiplashApiError
31
+ end
32
+
33
+ # 405
34
+ class MethodNotAllowed < WhiplashApiError
35
+ end
36
+
37
+ # 406
38
+ class NotAcceptable < WhiplashApiError
39
+ end
40
+
41
+ # 408
42
+ class Timeout < WhiplashApiError
43
+ end
44
+
45
+ # 409
46
+ class Conflict < WhiplashApiError
47
+ end
48
+
49
+ # 415
50
+ class UnsupportedMediaType < WhiplashApiError
51
+ end
52
+
53
+ # 422
54
+ class UnprocessableEntity < WhiplashApiError
55
+ end
56
+
57
+ # 429
58
+ class OverRateLimit < WhiplashApiError
59
+ end
60
+
61
+ class SSLError < WhiplashApiError
62
+ end
63
+
64
+ # 500+
65
+ class InternalServerError < WhiplashApiError
66
+ end
67
+
68
+ # 502
69
+ class Timeout < WhiplashApiError
70
+ end
71
+
72
+ # 503
73
+ class ServiceUnavailable < WhiplashApiError
74
+ end
75
+
76
+ # ???
77
+ class UnknownError < WhiplashApiError
78
+ end
79
+
80
+ def self.codes
81
+ {
82
+ 400 => WhiplashApiError::BadRequest,
83
+ 401 => WhiplashApiError::Unauthorized,
84
+ 403 => WhiplashApiError::Forbidden,
85
+ 404 => WhiplashApiError::RecordNotFound,
86
+ 405 => WhiplashApiError::MethodNotAllowed,
87
+ 406 => WhiplashApiError::NotAcceptable,
88
+ 408 => WhiplashApiError::Timeout,
89
+ 409 => WhiplashApiError::Conflict,
90
+ 415 => WhiplashApiError::UnsupportedMediaType,
91
+ 422 => WhiplashApiError::UnprocessableEntity,
92
+ 429 => WhiplashApiError::OverRateLimit,
93
+ 495 => WhiplashApiError::SSLError,
94
+ 500 => WhiplashApiError::InternalServerError,
95
+ 502 => WhiplashApiError::Timeout,
96
+ 503 => WhiplashApiError::ServiceUnavailable
97
+ }
98
+ end
99
+ end
@@ -4,6 +4,7 @@ require "whiplash/app/connections"
4
4
  require "whiplash/app/finder_methods"
5
5
  require "whiplash/app/signing"
6
6
  require "whiplash/app/version"
7
+ require "errors/whiplash_api_error"
7
8
  require "oauth2"
8
9
  require "faraday_middleware"
9
10
 
@@ -19,11 +20,18 @@ module Whiplash
19
20
 
20
21
  def initialize(token=nil, options={})
21
22
  opts = options.with_indifferent_access
23
+ token ||= cache_store["whiplash_api_token"]
22
24
  @token = format_token(token) unless token.nil?
23
25
  @customer_id = options[:customer_id]
24
26
  @shop_id = options[:shop_id]
25
27
  end
26
28
 
29
+ def self.whiplash_api_token
30
+ store = Moneta.new(:Redis, host: ENV["REDIS_HOST"], port: ENV["REDIS_PORT"], password: ENV["REDIS_PASSWORD"], expires: 7200)
31
+ cache_store = Moneta::Namespace.new store, Whiplash::App::Caching.namespace_value
32
+ cache_store["whiplash_api_token"]
33
+ end
34
+
27
35
  def client
28
36
  OAuth2::Client.new(ENV["WHIPLASH_CLIENT_ID"], ENV["WHIPLASH_CLIENT_SECRET"], site: api_url)
29
37
  end
@@ -56,7 +64,7 @@ module Whiplash
56
64
 
57
65
  def token_expired?
58
66
  return token.expired? unless token.blank?
59
- return true unless cache_store.has_key?("whiplash_api_token")
67
+ return true unless cache_store.key?("whiplash_api_token")
60
68
  return true if cache_store["whiplash_api_token"].blank?
61
69
  false
62
70
  end
@@ -18,6 +18,10 @@ module Whiplash
18
18
  ENV["WHIPLASH_API_URL"] || "https://sandbox.getwhiplash.com"
19
19
  end
20
20
 
21
+ def rate_limit
22
+ (ENV['WHIPLASH_RATE_LIMIT'] || 25).to_i
23
+ end
24
+
21
25
  end
22
26
  end
23
27
  end
@@ -7,13 +7,13 @@ module Whiplash
7
7
  def cache_store
8
8
  if ENV["REDIS_HOST"]
9
9
  store = Moneta.new(:Redis, host: ENV["REDIS_HOST"], port: ENV["REDIS_PORT"], password: ENV["REDIS_PASSWORD"], expires: 7200)
10
- Moneta::Namespace.new store, namespace_value
10
+ Moneta::Namespace.new store, Whiplash::App::Caching.namespace_value
11
11
  else
12
12
  Moneta.new(:File, dir: "tmp", expires: 7200)
13
13
  end
14
14
  end
15
15
 
16
- def namespace_value
16
+ def self.namespace_value
17
17
  ENV["REDIS_NAMESPACE"] || ENV["WHIPLASH_CLIENT_ID"]
18
18
  end
19
19
 
@@ -2,7 +2,7 @@ module Whiplash
2
2
  class App
3
3
  module Connections
4
4
 
5
- def app_request(options = {})
5
+ def base_app_request(options={})
6
6
  if options[:params][:id]
7
7
  endpoint = [options[:endpoint], options[:params].delete(:id)].join('/')
8
8
  else
@@ -11,10 +11,49 @@ module Whiplash
11
11
  options[:headers] ||= {}
12
12
  options[:headers][:customer_id] ||= customer_id if customer_id
13
13
  options[:headers][:shop_id] ||= shop_id if shop_id
14
- connection.send(options[:method],
15
- endpoint,
16
- options[:params],
17
- sanitize_headers(options[:headers]))
14
+
15
+ args = [
16
+ options[:method],
17
+ endpoint,
18
+ options[:params],
19
+ sanitize_headers(options[:headers])
20
+ ]
21
+
22
+ connection.send(*args)
23
+ end
24
+
25
+ def app_request(options={})
26
+ return base_app_request(options) unless defined?(Sidekiq)
27
+ limiter = Sidekiq::Limiter.window('whiplash-core', self.rate_limit, :second, wait_timeout: 15)
28
+ limiter.within_limit do
29
+ base_app_request(options)
30
+ end
31
+ end
32
+
33
+ def app_request!(options = {})
34
+ begin
35
+ response = app_request(options)
36
+ return response if response.success?
37
+ message = response.body if response.body.is_a? String
38
+ message = response.body.dig('error') if response.body.respond_to?(:dig)
39
+ store_whiplash_error!(response.status)
40
+ error_response(response.status, message)
41
+ rescue Faraday::ConnectionFailed => e
42
+ case e.message
43
+ when 'end of file reached'
44
+ store_whiplash_error!(:eof, options)
45
+ Rails.logger.error "[Whiplash][EOF] Failed to connect to #{url}"
46
+ raise ProviderError::InternalServerError, e.message
47
+ when 'Net::OpenTimeout'
48
+ store_whiplash_error!(:timeout, options)
49
+ Rails.logger.error "[Whiplash][Timeout] Request to #{url} timed out"
50
+ raise ProviderError::Timeout, e.message
51
+ else
52
+ store_whiplash_error!(:connection, options)
53
+ Rails.logger.error "[Whiplash] Request to #{url} failed"
54
+ raise ProviderError::InternalServerError, e.message
55
+ end
56
+ end
18
57
  end
19
58
 
20
59
  def delete(endpoint, params = {}, headers = nil)
@@ -45,6 +84,65 @@ module Whiplash
45
84
  headers: headers)
46
85
  end
47
86
 
87
+ def delete!(endpoint, params = {}, headers = nil)
88
+ app_request!(method: :delete,
89
+ endpoint: endpoint,
90
+ params: params,
91
+ headers: headers)
92
+ end
93
+
94
+ def get!(endpoint, params = {}, headers = nil)
95
+ app_request!(method: :get,
96
+ endpoint: endpoint,
97
+ params: params,
98
+ headers: headers)
99
+ end
100
+
101
+ def post!(endpoint, params = {}, headers = nil)
102
+ app_request!(method: :post,
103
+ endpoint: endpoint,
104
+ params: params,
105
+ headers: headers)
106
+ end
107
+
108
+ def put!(endpoint, params = {}, headers = nil)
109
+ app_request!(method: :put,
110
+ endpoint: endpoint,
111
+ params: params,
112
+ headers: headers)
113
+ end
114
+
115
+ def get_body_or_empty(endpoint, params = {}, headers = nil)
116
+ response = get(endpoint, params, headers)
117
+ if !response.success?
118
+ case response.status
119
+ when 500
120
+ Appsignal.send_error(WhiplashApiError::InternalServerError.new(response.body), {raised: false})
121
+ else
122
+ end
123
+
124
+ case get_context(endpoint)
125
+ when 'collection'
126
+ Rails.logger.info "[WhiplashApi] Returned #{response.status}. Returning an empty array..."
127
+ return []
128
+ when 'member'
129
+ Rails.logger.info "[WhiplashApi] Returned #{response.status}. Returning nil..."
130
+ return nil
131
+ when 'aggregate'
132
+ Rails.logger.info "[WhiplashApi] Returned #{response.status}. Returning an empty hash..."
133
+ return {}
134
+ end
135
+ end
136
+ response.body
137
+ end
138
+
139
+ def get_context(endpoint)
140
+ parts = endpoint.split('/').compact
141
+ return 'member' if (parts.last =~ /\d+/).present?
142
+ return 'aggregate' if parts.include?('aggregate')
143
+ 'collection'
144
+ end
145
+
48
146
  def sanitize_headers(headers)
49
147
  if headers
50
148
  {}.tap do |hash|
@@ -55,6 +153,36 @@ module Whiplash
55
153
  end
56
154
  end
57
155
 
156
+ def store_whiplash_error!(error, options={})
157
+ return unless defined?(Appsignal)
158
+ options = options.with_indifferent_access
159
+ Appsignal.increment_counter(
160
+ "whiplash_error",
161
+ 1.0,
162
+ shop_id: options[:shop_id],
163
+ customer_id: options[:customer_id],
164
+ error: error.to_s
165
+ )
166
+ end
167
+
168
+ def error_codes
169
+ WhiplashApiError.codes
170
+ end
171
+
172
+ def select_error(status_code)
173
+ unless error_codes.keys.include? status_code
174
+ Rails.logger.info "[Provider] Unknown status code from #{self.class.name}: #{status_code}"
175
+ return WhiplashApiError::UnknownError
176
+ end
177
+ error_codes[status_code]
178
+ end
179
+
180
+ # Select an applicable error message on a request to a provider.
181
+ def error_response(status_code, message=nil)
182
+ message ||= "Your request has been denied as a #{status_code} error"
183
+ raise select_error(status_code), message
184
+ end
185
+
58
186
  end
59
187
  end
60
188
  end
@@ -1,5 +1,5 @@
1
1
  module Whiplash
2
2
  class App
3
- VERSION = "0.4.0"
3
+ VERSION = "0.6.2"
4
4
  end
5
5
  end
@@ -23,6 +23,7 @@ Gem::Specification.new do |spec|
23
23
  spec.add_dependency "moneta", "~> 0.8.0"
24
24
 
25
25
  spec.add_development_dependency "bundler", "~> 1.11"
26
- spec.add_development_dependency "rake", "~> 10.0"
26
+ spec.add_development_dependency "rake", ">= 12.3.3"
27
27
  spec.add_development_dependency "rspec", "~> 3.0"
28
+ spec.add_development_dependency "pry" , '~> 0.12.2'
28
29
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: whiplash-app
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.4.0
4
+ version: 0.6.2
5
5
  platform: ruby
6
6
  authors:
7
7
  - Don Sullivan, Mark Dickson
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2017-09-12 00:00:00.000000000 Z
11
+ date: 2020-07-13 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: oauth2
@@ -70,16 +70,16 @@ dependencies:
70
70
  name: rake
71
71
  requirement: !ruby/object:Gem::Requirement
72
72
  requirements:
73
- - - "~>"
73
+ - - ">="
74
74
  - !ruby/object:Gem::Version
75
- version: '10.0'
75
+ version: 12.3.3
76
76
  type: :development
77
77
  prerelease: false
78
78
  version_requirements: !ruby/object:Gem::Requirement
79
79
  requirements:
80
- - - "~>"
80
+ - - ">="
81
81
  - !ruby/object:Gem::Version
82
- version: '10.0'
82
+ version: 12.3.3
83
83
  - !ruby/object:Gem::Dependency
84
84
  name: rspec
85
85
  requirement: !ruby/object:Gem::Requirement
@@ -94,6 +94,20 @@ dependencies:
94
94
  - - "~>"
95
95
  - !ruby/object:Gem::Version
96
96
  version: '3.0'
97
+ - !ruby/object:Gem::Dependency
98
+ name: pry
99
+ requirement: !ruby/object:Gem::Requirement
100
+ requirements:
101
+ - - "~>"
102
+ - !ruby/object:Gem::Version
103
+ version: 0.12.2
104
+ type: :development
105
+ prerelease: false
106
+ version_requirements: !ruby/object:Gem::Requirement
107
+ requirements:
108
+ - - "~>"
109
+ - !ruby/object:Gem::Version
110
+ version: 0.12.2
97
111
  description: this gem provides connectivity to the Whiplash API for authentication
98
112
  and REST requests.
99
113
  email:
@@ -103,6 +117,7 @@ extensions: []
103
117
  extra_rdoc_files: []
104
118
  files:
105
119
  - ".gitignore"
120
+ - ".idea/.gitignore"
106
121
  - ".rspec"
107
122
  - ".travis.yml"
108
123
  - Gemfile
@@ -110,6 +125,7 @@ files:
110
125
  - Rakefile
111
126
  - bin/console
112
127
  - bin/setup
128
+ - lib/errors/whiplash_api_error.rb
113
129
  - lib/whiplash/app.rb
114
130
  - lib/whiplash/app/api_config.rb
115
131
  - lib/whiplash/app/caching.rb
@@ -138,7 +154,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
138
154
  version: '0'
139
155
  requirements: []
140
156
  rubyforge_project:
141
- rubygems_version: 2.6.11
157
+ rubygems_version: 2.7.6.2
142
158
  signing_key:
143
159
  specification_version: 4
144
160
  summary: this gem provides connectivity to the Whiplash API for authentication and