airthings 1.0.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.
@@ -0,0 +1,389 @@
1
+ # frozen_string_literal: true
2
+
3
+ # #Airthings API
4
+ #
5
+ # No description provided (generated by Openapi Generator https://github.com/openapitools/openapi-generator)
6
+ #
7
+ # The version of the OpenAPI document: v1
8
+ #
9
+ # Generated by: https://openapi-generator.tech
10
+ # OpenAPI Generator version: 6.2.0
11
+ #
12
+
13
+ require "date"
14
+ require "json"
15
+ require "logger"
16
+ require "tempfile"
17
+ require "time"
18
+ require "typhoeus"
19
+
20
+ module Airthings
21
+ class ApiClient
22
+ # The Configuration object holding settings to be used in the API client.
23
+ attr_accessor :config
24
+
25
+ # Defines the headers to be used in HTTP requests of all API calls by default.
26
+ #
27
+ # @return [Hash]
28
+ attr_accessor :default_headers
29
+
30
+ # Initializes the ApiClient
31
+ # @option config [Configuration] Configuration for initializing the object, default to Configuration.default
32
+ def initialize(config = Configuration.default)
33
+ @config = config
34
+ @user_agent = "OpenAPI-Generator/#{VERSION}/ruby"
35
+ @default_headers = {
36
+ "Content-Type" => "application/json",
37
+ "User-Agent" => @user_agent
38
+ }
39
+ end
40
+
41
+ def self.default
42
+ @@default ||= ApiClient.new
43
+ end
44
+
45
+ # Call an API with given options.
46
+ #
47
+ # @return [Array<(Object, Integer, Hash)>] an array of 3 elements:
48
+ # the data deserialized from response body (could be nil), response status code and response headers.
49
+ def call_api(http_method, path, opts = {})
50
+ request = build_request(http_method, path, opts)
51
+ response = request.run
52
+
53
+ @config.logger.debug "HTTP response body ~BEGIN~\n#{response.body}\n~END~\n" if @config.debugging
54
+
55
+ unless response.success?
56
+ if response.timed_out?
57
+ raise ApiError, "Connection timed out"
58
+ elsif response.code.zero?
59
+ # Errors from libcurl will be made visible here
60
+ raise ApiError.new(code: 0,
61
+ message: response.return_message)
62
+ else
63
+ raise ApiError.new(code: response.code,
64
+ response_headers: response.headers,
65
+ response_body: response.body),
66
+ response.status_message
67
+ end
68
+ end
69
+
70
+ data = (deserialize(response, opts[:return_type]) if opts[:return_type])
71
+ [data, response.code, response.headers]
72
+ end
73
+
74
+ # Builds the HTTP request
75
+ #
76
+ # @param [String] http_method HTTP method/verb (e.g. POST)
77
+ # @param [String] path URL path (e.g. /account/new)
78
+ # @option opts [Hash] :header_params Header parameters
79
+ # @option opts [Hash] :query_params Query parameters
80
+ # @option opts [Hash] :form_params Query parameters
81
+ # @option opts [Object] :body HTTP body (JSON/XML)
82
+ # @return [Typhoeus::Request] A Typhoeus Request
83
+ def build_request(http_method, path, opts = {})
84
+ url = build_request_url(path, opts)
85
+ http_method = http_method.to_sym.downcase
86
+
87
+ header_params = @default_headers.merge(opts[:header_params] || {})
88
+ query_params = opts[:query_params] || {}
89
+ form_params = opts[:form_params] || {}
90
+ follow_location = opts[:follow_location] || true
91
+
92
+ update_params_for_auth! header_params, query_params, opts[:auth_names]
93
+
94
+ # set ssl_verifyhosts option based on @config.verify_ssl_host (true/false)
95
+ _verify_ssl_host = @config.verify_ssl_host ? 2 : 0
96
+
97
+ req_opts = {
98
+ method: http_method,
99
+ headers: header_params,
100
+ params: query_params,
101
+ params_encoding: @config.params_encoding,
102
+ timeout: @config.timeout,
103
+ ssl_verifypeer: @config.verify_ssl,
104
+ ssl_verifyhost: _verify_ssl_host,
105
+ sslcert: @config.cert_file,
106
+ sslkey: @config.key_file,
107
+ verbose: @config.debugging,
108
+ followlocation: follow_location
109
+ }
110
+
111
+ # set custom cert, if provided
112
+ req_opts[:cainfo] = @config.ssl_ca_cert if @config.ssl_ca_cert
113
+
114
+ if %i[post patch put delete].include?(http_method)
115
+ req_body = build_request_body(header_params, form_params, opts[:body])
116
+ req_opts.update body: req_body
117
+ @config.logger.debug "HTTP request body param ~BEGIN~\n#{req_body}\n~END~\n" if @config.debugging
118
+ end
119
+
120
+ request = Typhoeus::Request.new(url, req_opts)
121
+ download_file(request) if opts[:return_type] == "File"
122
+ request
123
+ end
124
+
125
+ # Builds the HTTP request body
126
+ #
127
+ # @param [Hash] header_params Header parameters
128
+ # @param [Hash] form_params Query parameters
129
+ # @param [Object] body HTTP body (JSON/XML)
130
+ # @return [String] HTTP body data in the form of string
131
+ def build_request_body(header_params, form_params, body)
132
+ # http form
133
+ if header_params["Content-Type"] == "application/x-www-form-urlencoded" ||
134
+ header_params["Content-Type"] == "multipart/form-data"
135
+ data = {}
136
+ form_params.each do |key, value|
137
+ data[key] = case value
138
+ when ::File, ::Array, nil
139
+ # let typhoeus handle File, Array and nil parameters
140
+ value
141
+ else
142
+ value.to_s
143
+ end
144
+ end
145
+ elsif body
146
+ data = body.is_a?(String) ? body : body.to_json
147
+ else
148
+ data = nil
149
+ end
150
+ data
151
+ end
152
+
153
+ # Save response body into a file in (the defined) temporary folder, using the filename
154
+ # from the "Content-Disposition" header if provided, otherwise a random filename.
155
+ # The response body is written to the file in chunks in order to handle files which
156
+ # size is larger than maximum Ruby String or even larger than the maximum memory a Ruby
157
+ # process can use.
158
+ #
159
+ # @see Configuration#temp_folder_path
160
+ def download_file(request)
161
+ tempfile = nil
162
+ encoding = nil
163
+ request.on_headers do |response|
164
+ content_disposition = response.headers["Content-Disposition"]
165
+ if content_disposition && content_disposition =~ /filename=/i
166
+ filename = content_disposition[/filename=['"]?([^'"\s]+)['"]?/, 1]
167
+ prefix = sanitize_filename(filename)
168
+ else
169
+ prefix = "download-"
170
+ end
171
+ prefix += "-" unless prefix.end_with?("-")
172
+ encoding = response.body.encoding
173
+ tempfile = Tempfile.open(prefix, @config.temp_folder_path, encoding: encoding)
174
+ @tempfile = tempfile
175
+ end
176
+ request.on_body do |chunk|
177
+ chunk.force_encoding(encoding)
178
+ tempfile.write(chunk)
179
+ end
180
+ request.on_complete do |_response|
181
+ if tempfile
182
+ tempfile.close
183
+ @config.logger.info "Temp file written to #{tempfile.path}, please copy the file to a proper folder " \
184
+ "with e.g. `FileUtils.cp(tempfile.path, '/new/file/path')` otherwise the temp file " \
185
+ "will be deleted automatically with GC. It's also recommended to delete the temp file " \
186
+ "explicitly with `tempfile.delete`"
187
+ end
188
+ end
189
+ end
190
+
191
+ # Check if the given MIME is a JSON MIME.
192
+ # JSON MIME examples:
193
+ # application/json
194
+ # application/json; charset=UTF8
195
+ # APPLICATION/JSON
196
+ # */*
197
+ # @param [String] mime MIME
198
+ # @return [Boolean] True if the MIME is application/json
199
+ def json_mime?(mime)
200
+ (mime == "*/*") || !(mime =~ %r{Application/.*json(?!p)(;.*)?}i).nil?
201
+ end
202
+
203
+ # Deserialize the response to the given return type.
204
+ #
205
+ # @param [Response] response HTTP response
206
+ # @param [String] return_type some examples: "User", "Array<User>", "Hash<String, Integer>"
207
+ def deserialize(response, return_type)
208
+ body = response.body
209
+
210
+ # handle file downloading - return the File instance processed in request callbacks
211
+ # note that response body is empty when the file is written in chunks in request on_body callback
212
+ return @tempfile if return_type == "File"
213
+
214
+ return nil if body.nil? || body.empty?
215
+
216
+ # return response body directly for String return type
217
+ return body if return_type == "String"
218
+
219
+ # ensuring a default content type
220
+ content_type = response.headers["Content-Type"] || "application/json"
221
+
222
+ raise "Content-Type is not supported: #{content_type}" unless json_mime?(content_type)
223
+
224
+ begin
225
+ data = JSON.parse("[#{body}]", symbolize_names: true)[0]
226
+ rescue JSON::ParserError => e
227
+ if %w[String Date Time].include?(return_type)
228
+ data = body
229
+ else
230
+ raise e
231
+ end
232
+ end
233
+
234
+ convert_to_type data, return_type
235
+ end
236
+
237
+ # Convert data to the given return type.
238
+ # @param [Object] data Data to be converted
239
+ # @param [String] return_type Return type
240
+ # @return [Mixed] Data in a particular type
241
+ def convert_to_type(data, return_type)
242
+ return nil if data.nil?
243
+
244
+ case return_type
245
+ when "String"
246
+ data.to_s
247
+ when "Integer"
248
+ data.to_i
249
+ when "Float"
250
+ data.to_f
251
+ when "Boolean"
252
+ data == true
253
+ when "Time"
254
+ # parse date time (expecting ISO 8601 format)
255
+ Time.parse data
256
+ when "Date"
257
+ # parse date time (expecting ISO 8601 format)
258
+ Date.parse data
259
+ when "Object"
260
+ # generic object (usually a Hash), return directly
261
+ data
262
+ when /\AArray<(.+)>\z/
263
+ # e.g. Array<Pet>
264
+ sub_type = $1
265
+ data.map { |item| convert_to_type(item, sub_type) }
266
+ when /\AHash<String, (.+)>\z/
267
+ # e.g. Hash<String, Integer>
268
+ sub_type = $1
269
+ {}.tap do |hash|
270
+ data.each { |k, v| hash[k] = convert_to_type(v, sub_type) }
271
+ end
272
+ else
273
+ # models (e.g. Pet) or oneOf
274
+ klass = Airthings.const_get(return_type)
275
+ klass.respond_to?(:openapi_one_of) ? klass.build(data) : klass.build_from_hash(data)
276
+ end
277
+ end
278
+
279
+ # Sanitize filename by removing path.
280
+ # e.g. ../../sun.gif becomes sun.gif
281
+ #
282
+ # @param [String] filename the filename to be sanitized
283
+ # @return [String] the sanitized filename
284
+ def sanitize_filename(filename)
285
+ filename.gsub(%r{.*[/\\]}, "")
286
+ end
287
+
288
+ def build_request_url(path, opts = {})
289
+ # Add leading and trailing slashes to path
290
+ path = "/#{path}".squeeze("/")
291
+ @config.base_url(opts[:operation]) + path
292
+ end
293
+
294
+ # Update header and query params based on authentication settings.
295
+ #
296
+ # @param [Hash] header_params Header parameters
297
+ # @param [Hash] query_params Query parameters
298
+ # @param [String] auth_names Authentication scheme name
299
+ def update_params_for_auth!(header_params, query_params, auth_names)
300
+ Array(auth_names).each do |auth_name|
301
+ auth_setting = @config.auth_settings[auth_name]
302
+ next unless auth_setting
303
+
304
+ case auth_setting[:in]
305
+ when "header" then header_params[auth_setting[:key]] = auth_setting[:value]
306
+ when "query" then query_params[auth_setting[:key]] = auth_setting[:value]
307
+ else raise ArgumentError, "Authentication token must be in `query` or `header`"
308
+ end
309
+ end
310
+ end
311
+
312
+ # Sets user agent in HTTP header
313
+ #
314
+ # @param [String] user_agent User agent (e.g. openapi-generator/ruby/1.0.0)
315
+ def user_agent=(user_agent)
316
+ @user_agent = user_agent
317
+ @default_headers["User-Agent"] = @user_agent
318
+ end
319
+
320
+ # Return Accept header based on an array of accepts provided.
321
+ # @param [Array] accepts array for Accept
322
+ # @return [String] the Accept header (e.g. application/json)
323
+ def select_header_accept(accepts)
324
+ return nil if accepts.nil? || accepts.empty?
325
+
326
+ # use JSON when present, otherwise use all of the provided
327
+ json_accept = accepts.find { |s| json_mime?(s) }
328
+ json_accept || accepts.join(",")
329
+ end
330
+
331
+ # Return Content-Type header based on an array of content types provided.
332
+ # @param [Array] content_types array for Content-Type
333
+ # @return [String] the Content-Type header (e.g. application/json)
334
+ def select_header_content_type(content_types)
335
+ # return nil by default
336
+ return if content_types.nil? || content_types.empty?
337
+
338
+ # use JSON when present, otherwise use the first one
339
+ json_content_type = content_types.find { |s| json_mime?(s) }
340
+ json_content_type || content_types.first
341
+ end
342
+
343
+ # Convert object (array, hash, object, etc) to JSON string.
344
+ # @param [Object] model object to be converted into JSON string
345
+ # @return [String] JSON string representation of the object
346
+ def object_to_http_body(model)
347
+ return model if model.nil? || model.is_a?(String)
348
+
349
+ local_body = nil
350
+ local_body = if model.is_a?(Array)
351
+ model.map { |m| object_to_hash(m) }
352
+ else
353
+ object_to_hash(model)
354
+ end
355
+ local_body.to_json
356
+ end
357
+
358
+ # Convert object(non-array) to hash.
359
+ # @param [Object] obj object to be converted into JSON string
360
+ # @return [String] JSON string representation of the object
361
+ def object_to_hash(obj)
362
+ if obj.respond_to?(:to_hash)
363
+ obj.to_hash
364
+ else
365
+ obj
366
+ end
367
+ end
368
+
369
+ # Build parameter value according to the given collection format.
370
+ # @param [String] collection_format one of :csv, :ssv, :tsv, :pipes and :multi
371
+ def build_collection_param(param, collection_format)
372
+ case collection_format
373
+ when :csv
374
+ param.join(",")
375
+ when :ssv
376
+ param.join(" ")
377
+ when :tsv
378
+ param.join("\t")
379
+ when :pipes
380
+ param.join("|")
381
+ when :multi
382
+ # return the array directly as typhoeus will handle it as expected
383
+ param
384
+ else
385
+ raise "unknown collection format: #{collection_format.inspect}"
386
+ end
387
+ end
388
+ end
389
+ end
@@ -0,0 +1,57 @@
1
+ # frozen_string_literal: true
2
+
3
+ # #Airthings API
4
+ #
5
+ # No description provided (generated by Openapi Generator https://github.com/openapitools/openapi-generator)
6
+ #
7
+ # The version of the OpenAPI document: v1
8
+ #
9
+ # Generated by: https://openapi-generator.tech
10
+ # OpenAPI Generator version: 6.2.0
11
+ #
12
+
13
+ module Airthings
14
+ class ApiError < StandardError
15
+ attr_reader :code, :response_headers, :response_body
16
+
17
+ # Usage examples:
18
+ # ApiError.new
19
+ # ApiError.new("message")
20
+ # ApiError.new(:code => 500, :response_headers => {}, :response_body => "")
21
+ # ApiError.new(:code => 404, :message => "Not Found")
22
+ def initialize(arg = nil)
23
+ if arg.is_a? Hash
24
+ if arg.key?(:message) || arg.key?("message")
25
+ super(arg[:message] || arg["message"])
26
+ else
27
+ super arg
28
+ end
29
+
30
+ arg.each do |k, v|
31
+ instance_variable_set "@#{k}", v
32
+ end
33
+ else
34
+ super arg
35
+ end
36
+ end
37
+
38
+ # Override to_s to display a friendly error message
39
+ def to_s
40
+ message
41
+ end
42
+
43
+ def message
44
+ msg = if @message.nil?
45
+ "Error message: the server returns an error"
46
+ else
47
+ @message
48
+ end
49
+
50
+ msg += "\nHTTP status code: #{code}" if code
51
+ msg += "\nResponse headers: #{response_headers}" if response_headers
52
+ msg += "\nResponse body: #{response_body}" if response_body
53
+
54
+ msg
55
+ end
56
+ end
57
+ end