whiplash-app 0.3.0 → 0.6.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: 193d13b5243acadeaeaa29acbddfab95699b9ed6
4
- data.tar.gz: 745b79664dcf13199ddf31041074357c0a3b02a9
2
+ SHA256:
3
+ metadata.gz: 29188ceeacb00228aee15d9f56ab151aac8b491c43b05463d25e648114fbb8ac
4
+ data.tar.gz: 93b8d38a3809d3e30d570f4acfe1ad3007f3754273d2ae1cf7299f3f4ce0f113
5
5
  SHA512:
6
- metadata.gz: 1911aec4369daf630a855b794180ae7ed8e9d1988f61d5aef881591a33b636b50af1349687867173f1cbfcfc8defc56167c2159cd1597992d274a833bed701d6
7
- data.tar.gz: 0f8e81b13812f1b7dbea5d56a3e7b36c1ac561a40ee283497421f94cf732c62f16a01ef7db94e0ab9c6927662bea25728d90e11bbbdf425b0cca2b8d45421df5
6
+ metadata.gz: 8a952642f993be0711907cbef3567362f7eb55026c43f22a2d0a1a83e40c24742d5f26db1e9ab0ca36ad7ca8479c9b04e3dc5b930cc8eebfa97b09a7d9e834ca
7
+ data.tar.gz: a2e597b3658c0cdfec8727e44f91a5b6050871ab62d876f59fddcfb8c9412aba62bd0ecac8a4d300ffc5a85002553e746234cde7b70bbdf29c656655fff43a87
data/.gitignore CHANGED
@@ -7,3 +7,6 @@
7
7
  /pkg/
8
8
  /spec/reports/
9
9
  /tmp/
10
+ .env
11
+ .ruby-version
12
+ .idea/*
data/README.md CHANGED
@@ -22,7 +22,27 @@ Or install it yourself as:
22
22
 
23
23
  ## Usage
24
24
 
25
- ###Authentication
25
+ **NOTE: 0.4.0 introduces a breaking change and is NOT backward compatible with previous versions.**
26
+
27
+ To upgrade from < 0.4.0, you need to make two small changes:
28
+ 1. `Whiplash::App` must now be instantiated.
29
+ 2. Tokens are **not** automatically refreshed
30
+
31
+ Before:
32
+ ```ruby
33
+ api = Whiplash::App
34
+ ```
35
+
36
+ After:
37
+ ```ruby
38
+ api = Whiplash::App.new
39
+ api.refresh_token! # Since you don't have one yet
40
+ api.token # Confirm you've got a token
41
+ . . .
42
+ api.refresh_token! if api.token_expired?
43
+ ```
44
+
45
+ ### Authentication
26
46
  In order to authenticate, make sure the following `ENV` vars are set:
27
47
 
28
48
  ```ruby
@@ -30,85 +50,90 @@ ENV["WHIPLASH_CLIENT_ID"]
30
50
  ENV["WHIPLASH_CLIENT_SECRET"]
31
51
  ENV["WHIPLASH_CLIENT_SCOPE"]
32
52
  ```
33
- Once those are set, authentication is handled in app. While authentication is
34
- mostly baked into the calls, there is a method that allows for getting the app's
35
- token from the main app: `Whiplash::App.token`.
53
+
54
+ Once those are set, authentication is handled in app.
55
+
56
+ ### Oauth Client Credentials
57
+ You can authenticate using Oauth Client Credentials (i.e. auth an entire app).
58
+ You probably want this for apps that work offline, _on behalf_ of users or customers, or that don't work at the user/customer-level at all.
59
+
60
+ ```ruby
61
+ api = Whiplash::App.new
62
+ api.refresh_token! # Since you don't have one yet
63
+ api.token # Confirm you've got a token
64
+ ```
65
+
66
+ ### Oauth Authorization Code
67
+ You can also authenticate using Oauth Authorization Code (i.e. auth an individual user). This is most common for user-facing app's with a front end.
68
+
69
+ ```ruby
70
+ # Authenticate using Devise Omniauthenticateable strategy; you'll get oauth creds back as a hash
71
+ api = Whiplash::App.new(oauth_credentials_hash)
72
+ api.token # Confirm you've got a token
73
+ ```
36
74
 
37
75
  ### API URL
38
76
  In order to set your api url, you can use the following environment URL:
39
77
  ```
40
78
  ENV["WHIPLASH_API_URL"]
41
79
  ```
42
- If it isn't set, then the API URL defaults to either `https://testing.whiplashmerch.com` (test or dev environment) or `https://www.whiplashmerch.com` (prod environment).
80
+ If it isn't set, then the API URL defaults to either `https://sandbox.getwhiplash.com` (test or dev environment) or `https://www.getwhiplash.com` (prod environment).
81
+
82
+ ### Sending Customer ID and Shop ID headers
83
+ You can send the headers in `headers` array, like `{customer_id: 123, shop_id: 111}`.
84
+ Alternatively, you can set them on instantiation like `Whiplash::App.new(token, {customer_id: 123, shop_id: 111})`.
43
85
 
44
- ###Rails AR type calls
86
+ ### Rails AR type calls
45
87
 
46
88
  In order to make the use of the gem seem more "AR-ish", we've added AR oriented methods that can be used for basic object creation/deletion/updating/viewing. The basic gist of these AR style CRUD methods is that they will all follow the same pattern. If you are performing a collection action, such as `create` or `find`, the pattern is this:
47
89
 
48
90
  ```ruby
49
- Whiplash::App.create(resource, params, headers)
91
+ api.create(resource, params, headers)
50
92
  ```
51
93
 
52
94
  For member actions, such as `show`, or `destroy` methods, the pattern is this:
53
95
 
54
96
  ```ruby
55
- Whiplash::App.find(resource, id, headers)
56
- Whiplash::App.destroy(resource, id, headers)
97
+ api.find(resource, id, headers)
98
+ api.destroy(resource, id, headers)
57
99
  ```
58
100
 
59
101
  Finally, for `update` calls, it's a mixture of those:
60
102
 
61
103
  ```ruby
62
- Whiplash::App.update(resource, id, params_to_update, headers)
104
+ api.update(resource, id, params_to_update, headers)
63
105
  ```
64
106
 
65
107
  So, basic AR style calls can be performed like so:
66
108
 
67
109
  ```ruby
68
- Whiplash::App.find_all('orders', {}, { customer_id: 187 })
69
- Whiplash::App.find('orders', 1)
70
- Whiplash::App.create('orders', { key: "value", key2: "value" }, { customer_id: 187 } )
71
- Whiplash::App.update('orders', 1, { key: "value"}, { customer_id: 187 } )
72
- Whiplash::App.destroy('orders', 1, { customer_id: 187 } )
73
- Whiplash::App.count('customers') #unlike other calls, which return Faraday responses, this call returns an integer.
110
+ api.find_all('orders', {}, { customer_id: 187 })
111
+ api.find('orders', 1)
112
+ api.create('orders', { key: "value", key2: "value" }, { customer_id: 187 } )
113
+ api.update('orders', 1, { key: "value"}, { customer_id: 187 } )
114
+ api.destroy('orders', 1, { customer_id: 187 } )
115
+ api.count('customers')
74
116
  ```
75
117
 
76
- ###CRUD Wrapper methods
118
+ ### CRUD Wrapper methods
77
119
  In reality, all of these methods are simply wrapper methods around simple `GET/POST/PUT/DELETE` wrappers on Faraday, so if you want to get more granular,you can also make calls that simply reference the lower level REST verb:
78
120
 
79
121
  ```ruby
80
- Whiplash::App.get('orders', {}, {})
122
+ api.get('orders')
81
123
  ```
82
124
  Which will return all orders and roughly correspond to an index call. If you need to use `Whiplash::App` for nonRESTful calls, simply drop the full endpoint in as your first argument:
83
125
 
84
126
  ```ruby
85
- Whiplash::App.get('orders/non_restful_action', {}, {})
127
+ api.get('orders/non_restful_action', {}, {})
86
128
  ```
87
129
  `POST`, `PUT`, and `DELETE` calls can be performed in much the same way:
88
130
  ```ruby
89
- Whiplash::App.post(endpoint, params, headers) #POST request to the specified endpoint passing the payload in params
131
+ api.post(endpoint, params, headers) # POST request to the specified endpoint passing the payload in params
132
+ api.put(endpoint, params, headers) # PUT request to the specified endpoint passing the payload in params
133
+ api.delete(endpoint, params, headers) # DELETE request to the specified endpoint. Params would probably just be an id.
90
134
  ```
91
- ```ruby
92
- Whiplash::App.put(endpoint, params, headers) #PUT request to the specified endpoint passing the payload in params
93
- ```
94
- ```ruby
95
- Whiplash::App.delete(endpoint, params, headers) #DELETE request to the specified endpoint. Params would probably just be an id.
96
- ```
97
-
98
- *IMPORTANT!!!! PLEASE READ!*
99
- It's best if possible to use the AR style methods. They hide a lot of issues with the way Faraday handles params. For example, this should, in theory, work:
100
- ```ruby
101
- Whiplash::App.get('orders', {id: 1}, {customer_id: 187})
102
- ```
103
- BUT, due to the way Faraday handles params, this would not, as expected, route to `orders#show` in the Whiplash App, but would instead route to `orders#index`, so it wouldn't return the expected singular order with an ID of 1, but all orders for that customer.
104
-
105
- The gem works best when the base CRUD methods are used ONLY for situations where a singular endpoint can't be reached, such as a cancel action, or uncancel, which would have to be done like so:
106
- ```ruby
107
- Whiplash::App.post('orders/1/cancel', {}, {customer_id: 187})
108
- ```
109
-
110
135
 
111
- ###Signing and Verifying.
136
+ ### Signing and Verifying.
112
137
  `whiplash-app` supports signing and verifying signatures like so:
113
138
  ```ruby
114
139
  Whiplash::App.signature(request_body)
@@ -118,10 +143,9 @@ and verifications are done like so:
118
143
  Whiplash::App.verified?(request)
119
144
  ```
120
145
 
121
- ###Caching
122
- `whiplash-app` is Cache agnostic, relying on the `moneta` gem to provide that
123
- interface. However, if you intend to specify `REDIS` as your key-value store of
124
- choice, it's dead simple. Simply declare the following variables:
146
+ ### Caching
147
+ `whiplash-app` is Cache agnostic, relying on the `moneta` gem to provide a local store, if needed.
148
+ However, if you intend to specify `REDIS` as your key-value store of choice, it's dead simple. Simply declare the following variables:
125
149
  ```
126
150
  ENV["REDIS_HOST"]
127
151
  ENV["REDIS_PORT"]
@@ -130,6 +154,19 @@ ENV["REDIS_NAMESPACE"]
130
154
  ```
131
155
  If those are provided, `moneta` will use your redis connection and will namespace your cache storage under the redis namespace. By default, if you do not declare a `REDIS_NAMESPACE` value, the app will default to the `WHIPLASH_CLIENT_ID`.
132
156
 
157
+ **For user-facing apps, best practice is to store the `oauth_credentials_hash` in a session variable.**
158
+
159
+ ### Gotchas
160
+ Due to the way Faraday handles params, this would not, as expected, route to `orders#show` in the Whiplash App, but would instead route to `orders#index`, so it wouldn't return the expected singular order with an ID of 1, but all orders for that customer.
161
+ ```ruby
162
+ api.get('orders', {id: 1}, {customer_id: 187})
163
+ ```
164
+ Instead, you'd want to do:
165
+ ```ruby
166
+ api.get('orders/1', {}, {customer_id: 187})
167
+ ```
168
+
169
+
133
170
  ## Development
134
171
 
135
172
  After checking out the repo, run `bin/setup` to install dependencies. Then, run `rake spec` to run the tests. You can also run `bin/console` for an interactive prompt that will allow you to experiment.
@@ -0,0 +1,94 @@
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
+ # 503
69
+ class ServiceUnavailable < WhiplashApiError
70
+ end
71
+
72
+ # ???
73
+ class UnknownError < WhiplashApiError
74
+ end
75
+
76
+ def self.codes
77
+ {
78
+ 400 => WhiplashApiError::BadRequest,
79
+ 401 => WhiplashApiError::Unauthorized,
80
+ 403 => WhiplashApiError::Forbidden,
81
+ 404 => WhiplashApiError::RecordNotFound,
82
+ 405 => WhiplashApiError::MethodNotAllowed,
83
+ 406 => WhiplashApiError::NotAcceptable,
84
+ 408 => WhiplashApiError::Timeout,
85
+ 409 => WhiplashApiError::Conflict,
86
+ 415 => WhiplashApiError::UnsupportedMediaType,
87
+ 422 => WhiplashApiError::UnprocessableEntity,
88
+ 429 => WhiplashApiError::OverRateLimit,
89
+ 495 => WhiplashApiError::SSLError,
90
+ 500 => WhiplashApiError::InternalServerError,
91
+ 503 => WhiplashApiError::ServiceUnavailable
92
+ }
93
+ end
94
+ end
@@ -4,46 +4,76 @@ 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
 
10
11
  module Whiplash
12
+ class App
13
+ include Whiplash::App::ApiConfig
14
+ include Whiplash::App::Caching
15
+ include Whiplash::App::Connections
16
+ include Whiplash::App::FinderMethods
17
+ extend Whiplash::App::Signing
11
18
 
12
- module App
13
- class << self
14
- include Whiplash::App::ApiConfig
15
- include Whiplash::App::Caching
16
- include Whiplash::App::Connections
17
- include Whiplash::App::FinderMethods
18
- include Whiplash::App::Signing
19
+ attr_accessor :customer_id, :shop_id, :token
19
20
 
20
- attr_accessor :customer_id, :shop_id
21
+ def initialize(token=nil, options={})
22
+ opts = options.with_indifferent_access
23
+ @token = format_token(token) unless token.nil?
24
+ @customer_id = options[:customer_id]
25
+ @shop_id = options[:shop_id]
26
+ end
21
27
 
22
- def client
23
- OAuth2::Client.new(ENV["WHIPLASH_CLIENT_ID"], ENV["WHIPLASH_CLIENT_SECRET"], site: api_url)
24
- end
28
+ def client
29
+ OAuth2::Client.new(ENV["WHIPLASH_CLIENT_ID"], ENV["WHIPLASH_CLIENT_SECRET"], site: api_url)
30
+ end
25
31
 
26
- def connection
27
- out = Faraday.new [api_url, "api/v2"].join("/") do |conn|
28
- conn.request :oauth2, token, token_type: "bearer"
29
- conn.request :json
30
- conn.response :json, :content_type => /\bjson$/
31
- conn.use :instrumentation
32
- conn.adapter Faraday.default_adapter
33
- end
34
- return out
32
+ def connection
33
+ out = Faraday.new [api_url, "api/v2"].join("/") do |conn|
34
+ conn.request :oauth2, token.token, token_type: "bearer"
35
+ conn.request :json
36
+ conn.response :json, :content_type => /\bjson$/
37
+ conn.use :instrumentation
38
+ conn.adapter Faraday.default_adapter
35
39
  end
40
+ return out
41
+ end
42
+
43
+ def token=(oauth_token)
44
+ instance_variable_set("@token", format_token(oauth_token))
45
+ end
36
46
 
37
- def refresh_token!
38
- oauth_token = client.client_credentials.get_token(scope: ENV["WHIPLASH_CLIENT_SCOPE"])
39
- new_token = oauth_token.token
47
+ def refresh_token!
48
+ if token.blank? # If we're storing locally, grab a new token and cache it
49
+ access_token = client.client_credentials.get_token(scope: ENV["WHIPLASH_CLIENT_SCOPE"])
50
+ new_token = access_token.to_hash
40
51
  cache_store["whiplash_api_token"] = new_token
52
+ else
53
+ access_token = token.refresh!
41
54
  end
55
+ self.token = access_token
56
+ end
57
+
58
+ def token_expired?
59
+ return token.expired? unless token.blank?
60
+ return true unless cache_store.has_key?("whiplash_api_token")
61
+ return true if cache_store["whiplash_api_token"].blank?
62
+ false
63
+ end
42
64
 
43
- def token
44
- refresh_token! unless cache_store["whiplash_api_token"]
45
- return cache_store["whiplash_api_token"]
65
+ private
66
+ def format_token(oauth_token)
67
+ unless oauth_token.is_a?(OAuth2::AccessToken)
68
+ raise StandardError, "Token must either be a Hash or an OAuth2::AccessToken" unless oauth_token.is_a?(Hash)
69
+ oauth_token['expires'] = oauth_token['expires'].to_s # from_hash expects 'true'
70
+ if oauth_token.has_key?('token')
71
+ oauth_token['access_token'] = oauth_token['token']
72
+ oauth_token.delete('token')
73
+ end
74
+ oauth_token = OAuth2::AccessToken.from_hash(client, oauth_token)
46
75
  end
47
76
  end
77
+
48
78
  end
49
79
  end
@@ -1,5 +1,5 @@
1
1
  module Whiplash
2
- module App
2
+ class App
3
3
  module ApiConfig
4
4
 
5
5
  def api_url
@@ -15,7 +15,11 @@ module Whiplash
15
15
  end
16
16
 
17
17
  def testing_url
18
- ENV["WHIPLASH_API_URL"] || "https://testing.getwhiplash.com"
18
+ ENV["WHIPLASH_API_URL"] || "https://sandbox.getwhiplash.com"
19
+ end
20
+
21
+ def rate_limit
22
+ (ENV['WHIPLASH_RATE_LIMIT'] || 25).to_i
19
23
  end
20
24
 
21
25
  end
@@ -1,7 +1,7 @@
1
1
  require "moneta"
2
2
  require "whiplash/app/moneta/namespace"
3
3
  module Whiplash
4
- module App
4
+ class App
5
5
  module Caching
6
6
 
7
7
  def cache_store
@@ -1,17 +1,59 @@
1
1
  module Whiplash
2
- module App
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
9
9
  endpoint = options[:endpoint]
10
10
  end
11
- connection.send(options[:method],
12
- endpoint,
13
- options[:params],
14
- sanitize_headers(options[:headers]))
11
+ options[:headers] ||= {}
12
+ options[:headers][:customer_id] ||= customer_id if customer_id
13
+ options[:headers][:shop_id] ||= shop_id if shop_id
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.body 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
15
57
  end
16
58
 
17
59
  def delete(endpoint, params = {}, headers = nil)
@@ -42,6 +84,65 @@ module Whiplash
42
84
  headers: headers)
43
85
  end
44
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
+
45
146
  def sanitize_headers(headers)
46
147
  if headers
47
148
  {}.tap do |hash|
@@ -52,6 +153,36 @@ module Whiplash
52
153
  end
53
154
  end
54
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
+
55
186
  end
56
187
  end
57
188
  end
@@ -1,5 +1,5 @@
1
1
  module Whiplash
2
- module App
2
+ class App
3
3
  module FinderMethods
4
4
 
5
5
  def count(resource, params = {}, headers = nil)
@@ -1,11 +1,6 @@
1
1
  module Whiplash
2
- module App
2
+ class App
3
3
  module Signing
4
-
5
- def request_body(body)
6
- body.blank? ? ENV["WHIPLASH_CLIENT_ID"] : body
7
- end
8
-
9
4
  def signature(body)
10
5
  sha256 = OpenSSL::Digest::SHA256.new
11
6
  OpenSSL::HMAC.hexdigest(sha256,
@@ -17,6 +12,11 @@ module Whiplash
17
12
  request.headers["X-WHIPLASH-SIGNATURE"] == signature(body)
18
13
  end
19
14
 
15
+ private
16
+
17
+ def request_body(body)
18
+ body.blank? ? ENV["WHIPLASH_CLIENT_ID"] : body
19
+ end
20
20
  end
21
21
  end
22
22
  end
@@ -1,5 +1,5 @@
1
1
  module Whiplash
2
- module App
3
- VERSION = "0.3.0"
2
+ class App
3
+ VERSION = "0.6.0"
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.3.0
4
+ version: 0.6.0
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-05-23 00:00:00.000000000 Z
11
+ date: 2020-05-26 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,10 +154,9 @@ required_rubygems_version: !ruby/object:Gem::Requirement
138
154
  version: '0'
139
155
  requirements: []
140
156
  rubyforge_project:
141
- rubygems_version: 2.4.5.1
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
145
161
  REST requests.
146
162
  test_files: []
147
- has_rdoc: