supportify_client 1.0.2 → 3.0.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -7,36 +7,37 @@ require 'uri'
7
7
 
8
8
  module Supportify
9
9
  class ApiClient
10
-
11
- attr_accessor :host
10
+ # The Configuration object holding settings to be used in the API client.
11
+ attr_accessor :config
12
12
 
13
13
  # Defines the headers to be used in HTTP requests of all API calls by default.
14
14
  #
15
15
  # @return [Hash]
16
16
  attr_accessor :default_headers
17
17
 
18
- # Stores the HTTP response from the last API call using this API client.
19
- attr_accessor :last_response
20
-
21
- def initialize(host = nil)
22
- @host = host || Configuration.base_url
23
- @format = 'json'
18
+ def initialize(config = Configuration.default)
19
+ @config = config
24
20
  @user_agent = "ruby-swagger-#{VERSION}"
25
21
  @default_headers = {
26
- 'Content-Type' => "application/#{@format.downcase}",
22
+ 'Content-Type' => "application/json",
27
23
  'User-Agent' => @user_agent
28
24
  }
29
25
  end
30
26
 
27
+ def self.default
28
+ @@default ||= ApiClient.new
29
+ end
30
+
31
+ # Call an API with given options.
32
+ #
33
+ # @return [Array<(Object, Fixnum, Hash)>] an array of 3 elements:
34
+ # the data deserialized from response body (could be nil), response status code and response headers.
31
35
  def call_api(http_method, path, opts = {})
32
36
  request = build_request(http_method, path, opts)
33
37
  response = request.run
34
38
 
35
- # record as last response
36
- @last_response = response
37
-
38
- if Configuration.debugging
39
- Configuration.logger.debug "HTTP response body ~BEGIN~\n#{response.body}\n~END~\n"
39
+ if @config.debugging
40
+ @config.logger.debug "HTTP response body ~BEGIN~\n#{response.body}\n~END~\n"
40
41
  end
41
42
 
42
43
  unless response.success?
@@ -47,10 +48,11 @@ module Supportify
47
48
  end
48
49
 
49
50
  if opts[:return_type]
50
- deserialize(response, opts[:return_type])
51
+ data = deserialize(response, opts[:return_type])
51
52
  else
52
- nil
53
+ data = nil
53
54
  end
55
+ return data, response.code, response.headers
54
56
  end
55
57
 
56
58
  def build_request(http_method, path, opts = {})
@@ -69,22 +71,34 @@ module Supportify
69
71
  :method => http_method,
70
72
  :headers => header_params,
71
73
  :params => query_params,
72
- :ssl_verifypeer => Configuration.verify_ssl,
73
- :cainfo => Configuration.ssl_ca_cert,
74
- :verbose => Configuration.debugging
74
+ :timeout => @config.timeout,
75
+ :ssl_verifypeer => @config.verify_ssl,
76
+ :sslcert => @config.cert_file,
77
+ :sslkey => @config.key_file,
78
+ :cainfo => @config.ssl_ca_cert,
79
+ :verbose => @config.debugging
75
80
  }
76
81
 
77
82
  if [:post, :patch, :put, :delete].include?(http_method)
78
83
  req_body = build_request_body(header_params, form_params, opts[:body])
79
84
  req_opts.update :body => req_body
80
- if Configuration.debugging
81
- Configuration.logger.debug "HTTP request body param ~BEGIN~\n#{req_body}\n~END~\n"
85
+ if @config.debugging
86
+ @config.logger.debug "HTTP request body param ~BEGIN~\n#{req_body}\n~END~\n"
82
87
  end
83
88
  end
84
89
 
85
90
  Typhoeus::Request.new(url, req_opts)
86
91
  end
87
92
 
93
+ # Check if the given MIME is a JSON MIME.
94
+ # JSON MIME examples:
95
+ # application/json
96
+ # application/json; charset=UTF8
97
+ # APPLICATION/JSON
98
+ def json_mime?(mime)
99
+ !!(mime =~ /\Aapplication\/json(;.*)?\z/i)
100
+ end
101
+
88
102
  # Deserialize the response to the given return type.
89
103
  #
90
104
  # @param [String] return_type some examples: "User", "Array[User]", "Hash[String,Integer]"
@@ -98,9 +112,7 @@ module Supportify
98
112
  # ensuring a default content type
99
113
  content_type = response.headers['Content-Type'] || 'application/json'
100
114
 
101
- unless content_type.start_with?('application/json')
102
- fail "Content-Type is not supported: #{content_type}"
103
- end
115
+ fail "Content-Type is not supported: #{content_type}" unless json_mime?(content_type)
104
116
 
105
117
  begin
106
118
  data = JSON.parse("[#{body}]", :symbolize_names => true)[0]
@@ -158,38 +170,58 @@ module Supportify
158
170
  # from the "Content-Disposition" header if provided, otherwise a random filename.
159
171
  #
160
172
  # @see Configuration#temp_folder_path
161
- # @return [File] the file downloaded
173
+ # @return [Tempfile] the file downloaded
162
174
  def download_file(response)
163
- tmp_file = Tempfile.new '', Configuration.temp_folder_path
164
175
  content_disposition = response.headers['Content-Disposition']
165
176
  if content_disposition
166
177
  filename = content_disposition[/filename=['"]?([^'"\s]+)['"]?/, 1]
167
- path = File.join File.dirname(tmp_file), filename
178
+ prefix = sanitize_filename(filename)
168
179
  else
169
- path = tmp_file.path
180
+ prefix = 'download-'
181
+ end
182
+ prefix = prefix + '-' unless prefix.end_with?('-')
183
+
184
+ tempfile = nil
185
+ encoding = response.body.encoding
186
+ Tempfile.open(prefix, @config.temp_folder_path, encoding: encoding) do |file|
187
+ file.write(response.body)
188
+ tempfile = file
170
189
  end
171
- # close and delete temp file
172
- tmp_file.close!
190
+ @config.logger.info "Temp file written to #{tempfile.path}, please copy the file to a proper folder "\
191
+ "with e.g. `FileUtils.cp(tempfile.path, '/new/file/path')` otherwise the temp file "\
192
+ "will be deleted automatically with GC. It's also recommended to delete the temp file "\
193
+ "explicitly with `tempfile.delete`"
194
+ tempfile
195
+ end
173
196
 
174
- File.open(path, 'w') { |file| file.write(response.body) }
175
- Configuration.logger.info "File written to #{path}. Please move the file to a proper "\
176
- "folder for further processing and delete the temp afterwards"
177
- File.new(path)
197
+ # Sanitize filename by removing path.
198
+ # e.g. ../../sun.gif becomes sun.gif
199
+ #
200
+ # @param [String] filename the filename to be sanitized
201
+ # @return [String] the sanitized filename
202
+ def sanitize_filename(filename)
203
+ filename.gsub /.*[\/\\]/, ''
178
204
  end
179
205
 
180
206
  def build_request_url(path)
181
207
  # Add leading and trailing slashes to path
182
208
  path = "/#{path}".gsub(/\/+/, '/')
183
- URI.encode(host + path)
209
+ URI.encode(@config.base_url + path)
184
210
  end
185
211
 
186
212
  def build_request_body(header_params, form_params, body)
187
213
  # http form
188
214
  if header_params['Content-Type'] == 'application/x-www-form-urlencoded' ||
189
215
  header_params['Content-Type'] == 'multipart/form-data'
190
- data = form_params.dup
191
- data.each do |key, value|
192
- data[key] = value.to_s if value && !value.is_a?(File)
216
+ data = {}
217
+ form_params.each do |key, value|
218
+ case value
219
+ when File, Array, nil
220
+ # let typhoeus handle File, Array and nil parameters
221
+ data[key] = value
222
+ else
223
+ data[key] = value.to_s
224
+ end
193
225
  end
194
226
  elsif body
195
227
  data = body.is_a?(String) ? body : body.to_json
@@ -202,7 +234,7 @@ module Supportify
202
234
  # Update hearder and query params based on authentication settings.
203
235
  def update_params_for_auth!(header_params, query_params, auth_names)
204
236
  Array(auth_names).each do |auth_name|
205
- auth_setting = Configuration.auth_settings[auth_name]
237
+ auth_setting = @config.auth_settings[auth_name]
206
238
  next unless auth_setting
207
239
  case auth_setting[:in]
208
240
  when 'header' then header_params[auth_setting[:key]] = auth_setting[:value]
@@ -221,26 +253,21 @@ module Supportify
221
253
  # @param [Array] accepts array for Accept
222
254
  # @return [String] the Accept header (e.g. application/json)
223
255
  def select_header_accept(accepts)
224
- if accepts.empty?
225
- return
226
- elsif accepts.any?{ |s| s.casecmp('application/json') == 0 }
227
- 'application/json' # look for json data by default
228
- else
229
- accepts.join(',')
230
- end
256
+ return nil if accepts.nil? || accepts.empty?
257
+ # use JSON when present, otherwise use all of the provided
258
+ json_accept = accepts.find { |s| json_mime?(s) }
259
+ return json_accept || accepts.join(',')
231
260
  end
232
261
 
233
262
  # Return Content-Type header based on an array of content types provided.
234
263
  # @param [Array] content_types array for Content-Type
235
264
  # @return [String] the Content-Type header (e.g. application/json)
236
265
  def select_header_content_type(content_types)
237
- if content_types.empty?
238
- 'application/json' # use application/json by default
239
- elsif content_types.any?{ |s| s.casecmp('application/json')==0 }
240
- 'application/json' # use application/json if it's included
241
- else
242
- content_types[0] # otherwise, use the first one
243
- end
266
+ # use application/json by default
267
+ return 'application/json' if content_types.nil? || content_types.empty?
268
+ # use JSON when present, otherwise use the first one
269
+ json_content_type = content_types.find { |s| json_mime?(s) }
270
+ return json_content_type || content_types.first
244
271
  end
245
272
 
246
273
  # Convert object (array, hash, object, etc) to JSON string.
@@ -267,5 +294,25 @@ module Supportify
267
294
  obj
268
295
  end
269
296
  end
297
+
298
+ # Build parameter value according to the given collection format.
299
+ # @param [String] collection_format one of :csv, :ssv, :tsv, :pipes and :multi
300
+ def build_collection_param(param, collection_format)
301
+ case collection_format
302
+ when :csv
303
+ param.join(',')
304
+ when :ssv
305
+ param.join(' ')
306
+ when :tsv
307
+ param.join("\t")
308
+ when :pipes
309
+ param.join('|')
310
+ when :multi
311
+ # return the array directly as typhoeus will handle it as expected
312
+ param
313
+ else
314
+ fail "unknown collection format: #{collection_format.inspect}"
315
+ end
316
+ end
270
317
  end
271
318
  end
@@ -1,14 +1,7 @@
1
1
  require 'uri'
2
- require 'singleton'
3
2
 
4
3
  module Supportify
5
4
  class Configuration
6
-
7
- include Singleton
8
-
9
- # Default api client
10
- attr_accessor :api_client
11
-
12
5
  # Defines url scheme
13
6
  attr_accessor :scheme
14
7
 
@@ -44,6 +37,9 @@ module Supportify
44
37
  # @return [String]
45
38
  attr_accessor :password
46
39
 
40
+ # Defines the access token (Bearer) used with OAuth2.
41
+ attr_accessor :access_token
42
+
47
43
  # Set this to enable/disable debugging. When enabled (set to true), HTTP request/response
48
44
  # details will be logged with `logger.debug` (see the `logger` attribute).
49
45
  # Default to false.
@@ -64,6 +60,11 @@ module Supportify
64
60
  # @return [String]
65
61
  attr_accessor :temp_folder_path
66
62
 
63
+ # The time limit for HTTP request in seconds.
64
+ # Default to 0 (never times out).
65
+ attr_accessor :timeout
66
+
67
+ ### TLS/SSL
67
68
  # Set this to false to skip verifying SSL certificate when calling API from https server.
68
69
  # Default to true.
69
70
  #
@@ -80,36 +81,41 @@ module Supportify
80
81
  # https://github.com/typhoeus/typhoeus/blob/master/lib/typhoeus/easy_factory.rb#L145
81
82
  attr_accessor :ssl_ca_cert
82
83
 
84
+ # Client certificate file (for client certificate)
85
+ attr_accessor :cert_file
86
+
87
+ # Client private key file (for client certificate)
88
+ attr_accessor :key_file
89
+
83
90
  attr_accessor :inject_format
84
91
 
85
92
  attr_accessor :force_ending_format
86
93
 
87
- class << self
88
- def method_missing(method_name, *args, &block)
89
- config = Configuration.instance
90
- if config.respond_to?(method_name)
91
- config.send(method_name, *args, &block)
92
- else
93
- super
94
- end
95
- end
96
- end
97
-
98
94
  def initialize
99
95
  @scheme = 'https'
100
96
  @host = 'api.supportify.io'
101
- @base_path = '/v2'
97
+ @base_path = '/v3'
102
98
  @api_key = {}
103
99
  @api_key_prefix = {}
100
+ @timeout = 0
104
101
  @verify_ssl = true
102
+ @cert_file = nil
103
+ @key_file = nil
105
104
  @debugging = false
106
105
  @inject_format = false
107
106
  @force_ending_format = false
108
107
  @logger = defined?(Rails) ? Rails.logger : Logger.new(STDOUT)
108
+
109
+ yield(self) if block_given?
110
+ end
111
+
112
+ # The default Configuration object.
113
+ def self.default
114
+ @@default ||= Configuration.new
109
115
  end
110
116
 
111
- def api_client
112
- @api_client ||= ApiClient.new
117
+ def configure
118
+ yield(self) if block_given?
113
119
  end
114
120
 
115
121
  def scheme=(scheme)
@@ -129,7 +135,7 @@ module Supportify
129
135
  end
130
136
 
131
137
  def base_url
132
- url = "#{scheme}://#{[host, base_path].join('/').gsub(/\/+/, '/')}"
138
+ url = "#{scheme}://#{[host, base_path].join('/').gsub(/\/+/, '/')}".sub(/\/+\z/, '')
133
139
  URI.encode(url)
134
140
  end
135
141
 
@@ -163,23 +169,23 @@ module Supportify
163
169
  def application_api_key
164
170
  @api_key['X-SUPPORTIFY-APPKEY']
165
171
  end
166
-
172
+
167
173
  # Returns Auth Settings hash for api client.
168
174
  def auth_settings
169
175
  {
170
- 'api_key' =>
176
+ 'app_key' =>
171
177
  {
172
178
  type: 'api_key',
173
179
  in: 'header',
174
- key: 'X-SUPPORTIFY-APIKEY',
175
- value: api_key_with_prefix('X-SUPPORTIFY-APIKEY')
180
+ key: 'X-SUPPORTIFY-APPKEY',
181
+ value: api_key_with_prefix('X-SUPPORTIFY-APPKEY')
176
182
  },
177
- 'app_key' =>
183
+ 'api_key' =>
178
184
  {
179
185
  type: 'api_key',
180
186
  in: 'header',
181
- key: 'X-SUPPORTIFY-APPKEY',
182
- value: api_key_with_prefix('X-SUPPORTIFY-APPKEY')
187
+ key: 'X-SUPPORTIFY-APIKEY',
188
+ value: api_key_with_prefix('X-SUPPORTIFY-APIKEY')
183
189
  },
184
190
  }
185
191
  end
@@ -1,24 +1,30 @@
1
+ require 'date'
2
+
1
3
  module Supportify
2
- #
3
- class Category < BaseObject
4
- attr_accessor :id, :description, :name
5
- # attribute mapping from ruby-style variable name to JSON key
4
+ class Category
5
+ # Unique identifier representing a specific category within an application.
6
+ attr_accessor :id
7
+
8
+ # Description of the category.
9
+ attr_accessor :description
10
+
11
+ # Display name of the category.
12
+ attr_accessor :name
13
+
14
+ # Attribute mapping from ruby-style variable name to JSON key.
6
15
  def self.attribute_map
7
16
  {
8
17
 
9
- # Unique identifier representing a specific category within an application.
10
18
  :'id' => :'id',
11
19
 
12
- # Description of the category.
13
20
  :'description' => :'description',
14
21
 
15
- # Display name of the category.
16
22
  :'name' => :'name'
17
23
 
18
24
  }
19
25
  end
20
26
 
21
- # attribute type
27
+ # Attribute type mapping.
22
28
  def self.swagger_types
23
29
  {
24
30
  :'id' => :'Integer',
@@ -29,7 +35,7 @@ module Supportify
29
35
  end
30
36
 
31
37
  def initialize(attributes = {})
32
- return if !attributes.is_a?(Hash) || attributes.empty?
38
+ return unless attributes.is_a?(Hash)
33
39
 
34
40
  # convert string to symbol for hash key
35
41
  attributes = attributes.inject({}){|memo,(k,v)| memo[k.to_sym] = v; memo}
@@ -49,5 +55,115 @@ module Supportify
49
55
 
50
56
  end
51
57
 
58
+ # Check equality by comparing each attribute.
59
+ def ==(o)
60
+ return true if self.equal?(o)
61
+ self.class == o.class &&
62
+ id == o.id &&
63
+ description == o.description &&
64
+ name == o.name
65
+ end
66
+
67
+ # @see the `==` method
68
+ def eql?(o)
69
+ self == o
70
+ end
71
+
72
+ # Calculate hash code according to all attributes.
73
+ def hash
74
+ [id, description, name].hash
75
+ end
76
+
77
+ # build the object from hash
78
+ def build_from_hash(attributes)
79
+ return nil unless attributes.is_a?(Hash)
80
+ self.class.swagger_types.each_pair do |key, type|
81
+ if type =~ /^Array<(.*)>/i
82
+ if attributes[self.class.attribute_map[key]].is_a?(Array)
83
+ self.send("#{key}=", attributes[self.class.attribute_map[key]].map{ |v| _deserialize($1, v) } )
84
+ else
85
+ #TODO show warning in debug mode
86
+ end
87
+ elsif !attributes[self.class.attribute_map[key]].nil?
88
+ self.send("#{key}=", _deserialize(type, attributes[self.class.attribute_map[key]]))
89
+ else
90
+ # data not found in attributes(hash), not an issue as the data can be optional
91
+ end
92
+ end
93
+
94
+ self
95
+ end
96
+
97
+ def _deserialize(type, value)
98
+ case type.to_sym
99
+ when :DateTime
100
+ DateTime.parse(value)
101
+ when :Date
102
+ Date.parse(value)
103
+ when :String
104
+ value.to_s
105
+ when :Integer
106
+ value.to_i
107
+ when :Float
108
+ value.to_f
109
+ when :BOOLEAN
110
+ if value =~ /^(true|t|yes|y|1)$/i
111
+ true
112
+ else
113
+ false
114
+ end
115
+ when /\AArray<(?<inner_type>.+)>\z/
116
+ inner_type = Regexp.last_match[:inner_type]
117
+ value.map { |v| _deserialize(inner_type, v) }
118
+ when /\AHash<(?<k_type>.+), (?<v_type>.+)>\z/
119
+ k_type = Regexp.last_match[:k_type]
120
+ v_type = Regexp.last_match[:v_type]
121
+ {}.tap do |hash|
122
+ value.each do |k, v|
123
+ hash[_deserialize(k_type, k)] = _deserialize(v_type, v)
124
+ end
125
+ end
126
+ else # model
127
+ _model = Supportify.const_get(type).new
128
+ _model.build_from_hash(value)
129
+ end
130
+ end
131
+
132
+ def to_s
133
+ to_hash.to_s
134
+ end
135
+
136
+ # to_body is an alias to to_body (backward compatibility))
137
+ def to_body
138
+ to_hash
139
+ end
140
+
141
+ # return the object in the form of hash
142
+ def to_hash
143
+ hash = {}
144
+ self.class.attribute_map.each_pair do |attr, param|
145
+ value = self.send(attr)
146
+ next if value.nil?
147
+ hash[param] = _to_hash(value)
148
+ end
149
+ hash
150
+ end
151
+
152
+ # Method to output non-array value in the form of hash
153
+ # For object, use to_hash. Otherwise, just return the value
154
+ def _to_hash(value)
155
+ if value.is_a?(Array)
156
+ value.compact.map{ |v| _to_hash(v) }
157
+ elsif value.is_a?(Hash)
158
+ {}.tap do |hash|
159
+ value.each { |k, v| hash[k] = _to_hash(v) }
160
+ end
161
+ elsif value.respond_to? :to_hash
162
+ value.to_hash
163
+ else
164
+ value
165
+ end
166
+ end
167
+
52
168
  end
53
169
  end