Module: Whiplash::App::Connections

Included in:
Whiplash::App
Defined in:
lib/whiplash/app/connections.rb

Constant Summary collapse

PER_PAGE_REQUEST_LIMIT =
50

Instance Method Summary collapse

Instance Method Details

#app_request(options = {}) ⇒ Object



28
29
30
31
32
33
34
# File 'lib/whiplash/app/connections.rb', line 28

def app_request(options={})
  return base_app_request(options) unless defined?(Sidekiq)
  limiter = Sidekiq::Limiter.window('whiplash-core', self.class.rate_limit, :second, wait_timeout: 15)
  limiter.within_limit do
    base_app_request(options)
  end
end

#app_request!(options = {}) ⇒ Object



36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
# File 'lib/whiplash/app/connections.rb', line 36

def app_request!(options = {})
  begin
    response = app_request(options)
    return response if response.success?
    message = response.body if response.body.is_a? String
    message = response.body.dig('error') if response.body.respond_to?(:dig)
    store_whiplash_error!(response.status)
    error_response(response.status, message)
  rescue Faraday::ConnectionFailed => e
    case e.message
    when 'end of file reached'
      store_whiplash_error!(:eof, options)
      Rails.logger.error "[Whiplash][EOF] Failed to connect with options: #{options.inspect}"
      raise WhiplashApiError::InternalServerError, e.message
    when 'Net::OpenTimeout'
      store_whiplash_error!(:timeout, options)
      Rails.logger.error "[Whiplash][Timeout] Request with options: #{options.inspect} timed out"
      raise WhiplashApiError::Timeout, e.message
    else
      store_whiplash_error!(:connection, options)
      Rails.logger.error "[Whiplash] Request with options: #{options.inspect} failed"
      raise WhiplashApiError::InternalServerError, e.message
    end
  end
end

#base_app_request(options = {}) ⇒ Object



7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
# File 'lib/whiplash/app/connections.rb', line 7

def base_app_request(options={})
  if options[:params][:id]
    endpoint = [options[:endpoint], options[:params].delete(:id)].join('/')
  else
    endpoint = options[:endpoint]
  end
  options[:headers] ||= {}
  options[:headers][:customer_id] ||= customer_id if customer_id
  options[:headers][:shop_id] ||= shop_id if shop_id
  options[:headers][:version] ||= 2

  args = [
    options[:method],
    endpoint,
    options[:params],
    sanitize_headers(options[:headers])
  ]

  connection.send(*args)
end

#delete(endpoint, params = {}, headers = nil) ⇒ Object



103
104
105
106
107
108
# File 'lib/whiplash/app/connections.rb', line 103

def delete(endpoint, params = {}, headers = nil)
  app_request(method: :delete,
              endpoint: endpoint,
              params: params,
              headers: headers)
end

#delete!(endpoint, params = {}, headers = nil) ⇒ Object



131
132
133
134
135
136
# File 'lib/whiplash/app/connections.rb', line 131

def delete!(endpoint, params = {}, headers = nil)
  app_request!(method: :delete,
              endpoint: endpoint,
              params: params,
              headers: headers)
end

#error_codesObject



216
217
218
# File 'lib/whiplash/app/connections.rb', line 216

def error_codes
  WhiplashApiError.codes
end

#error_response(status_code, message = nil) ⇒ Object

Select an applicable error message on a request to a provider.

Raises:



229
230
231
232
# File 'lib/whiplash/app/connections.rb', line 229

def error_response(status_code, message=nil)
  message ||= "Your request has been denied as a #{status_code} error"
  raise select_error(status_code), message
end

#get(endpoint, params = {}, headers = nil) ⇒ Object



110
111
112
113
114
115
# File 'lib/whiplash/app/connections.rb', line 110

def get(endpoint, params = {}, headers = nil)
  app_request(method: :get,
              endpoint: endpoint,
              params: params,
              headers: headers)
end

#get!(endpoint, params = {}, headers = nil) ⇒ Object



138
139
140
141
142
143
# File 'lib/whiplash/app/connections.rb', line 138

def get!(endpoint, params = {}, headers = nil)
  app_request!(method: :get,
              endpoint: endpoint,
              params: params,
              headers: headers)
end

#get_body_or_empty(endpoint, params = {}, headers = nil) ⇒ Object



159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
# File 'lib/whiplash/app/connections.rb', line 159

def get_body_or_empty(endpoint, params = {}, headers = nil)
  response = get(endpoint, params, headers)
  if !response.success?
    case response.status
    when 500
      # Do we want to replace this with anything?
      # Appsignal.send_error(WhiplashApiError::InternalServerError.new(response.body), {raised: false})
    else
    end

    case get_context(endpoint)
    when 'collection'
      Rails.logger.info "[WhiplashApi] Returned #{response.status}. Returning an empty array..."
      return []
    when 'member'
      Rails.logger.info "[WhiplashApi] Returned #{response.status}. Returning nil..."
      return nil
    when 'aggregate'
      Rails.logger.info "[WhiplashApi] Returned #{response.status}. Returning an empty hash..."
      return {}
    end
  end
  response.body
end

#get_context(endpoint) ⇒ Object



184
185
186
187
188
189
# File 'lib/whiplash/app/connections.rb', line 184

def get_context(endpoint)
  parts = endpoint.split('/').compact
  return 'member' unless (parts.last =~ /\d+/).nil?
  return 'aggregate' if parts.include?('aggregate')
  'collection'
end

#multi_page_get!(endpoint, params = {}, headers = nil) ⇒ Object



62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
# File 'lib/whiplash/app/connections.rb', line 62

def multi_page_get!(endpoint, params = {}, headers = nil)
  results = []
  page = 1
  params[:per_page] = PER_PAGE_REQUEST_LIMIT
  response = nil

  loop do
    partial_results_request = app_request!(
      method: :get,
      endpoint: endpoint,
      params: params,
      headers: headers
    )

    if !partial_results_request.success?
      response = Faraday::Response.new(
        body: partial_results_request.body,
        status: partial_results_request.status,
        method: :get
      )
      break
    end

    results << partial_results_request.body
    results.flatten!

    page += 1
    params[:page] = page

    break if partial_results_request.body.size == 0
    break if partial_results_request.body.size < PER_PAGE_REQUEST_LIMIT
  end

  response ||= Faraday::Response.new(
    body: results,
    status: 200,
    method: :get
  )

end

#post(endpoint, params = {}, headers = nil) ⇒ Object



117
118
119
120
121
122
# File 'lib/whiplash/app/connections.rb', line 117

def post(endpoint, params = {}, headers = nil)
  app_request(method: :post,
              endpoint: endpoint,
              params: params,
              headers: headers)
end

#post!(endpoint, params = {}, headers = nil) ⇒ Object



145
146
147
148
149
150
# File 'lib/whiplash/app/connections.rb', line 145

def post!(endpoint, params = {}, headers = nil)
  app_request!(method: :post,
              endpoint: endpoint,
              params: params,
              headers: headers)
end

#put(endpoint, params = {}, headers = nil) ⇒ Object



124
125
126
127
128
129
# File 'lib/whiplash/app/connections.rb', line 124

def put(endpoint, params = {}, headers = nil)
  app_request(method: :put,
              endpoint: endpoint,
              params: params,
              headers: headers)
end

#put!(endpoint, params = {}, headers = nil) ⇒ Object



152
153
154
155
156
157
# File 'lib/whiplash/app/connections.rb', line 152

def put!(endpoint, params = {}, headers = nil)
  app_request!(method: :put,
              endpoint: endpoint,
              params: params,
              headers: headers)
end

#sanitize_headers(headers) ⇒ Object



191
192
193
194
195
196
197
198
199
200
201
202
# File 'lib/whiplash/app/connections.rb', line 191

def sanitize_headers(headers)
  return if headers.nil? || headers.empty?
  out = {}.tap do |hash|
    headers.each do |k,v|
      if k.to_s == 'version'
        hash['Accept-Version'] = "v#{v}"
      else
        hash["X-#{k.to_s.upcase.gsub('_','-')}"] = v.to_s
      end
    end
  end
end

#select_error(status_code) ⇒ Object



220
221
222
223
224
225
226
# File 'lib/whiplash/app/connections.rb', line 220

def select_error(status_code)
  unless error_codes.keys.include? status_code
    Rails.logger.info "[Provider] Unknown status code from #{self.class.name}: #{status_code}"
    return WhiplashApiError::UnknownError
  end
  error_codes[status_code]
end

#store_whiplash_error!(error, options = {}) ⇒ Object



204
205
206
207
208
209
210
211
212
213
214
# File 'lib/whiplash/app/connections.rb', line 204

def store_whiplash_error!(error, options={})
  return unless defined?(Appsignal)
  options.transform_keys!(&:to_sym)
  Appsignal.increment_counter(
    "whiplash_error",
    1.0,
    shop_id: options[:shop_id],
    customer_id: options[:customer_id],
    error: error.to_s
  )
end