vpndetection 4.0.1 → 5.1.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.
Files changed (59) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +24 -4
  3. data/lib/vpndetection/api/account_wire_api.rb +16 -16
  4. data/lib/vpndetection/api/api_keys_wire_api.rb +336 -0
  5. data/lib/vpndetection/api/authorization_wire_api.rb +421 -0
  6. data/lib/vpndetection/api/database_wire_api.rb +1 -1
  7. data/lib/vpndetection/api/entitlement_wire_api.rb +79 -0
  8. data/lib/vpndetection/api/lookup_wire_api.rb +71 -3
  9. data/lib/vpndetection/api/organization_wire_api.rb +136 -0
  10. data/lib/vpndetection/api_client.rb +1 -1
  11. data/lib/vpndetection/api_error.rb +1 -1
  12. data/lib/vpndetection/api_model_base.rb +1 -1
  13. data/lib/vpndetection/client.rb +57 -26
  14. data/lib/vpndetection/configuration.rb +1 -1
  15. data/lib/vpndetection/errors.rb +7 -0
  16. data/lib/vpndetection/models/account_create_apikey_request.rb +177 -0
  17. data/lib/vpndetection/models/account_created_apikey.rb +257 -0
  18. data/lib/vpndetection/models/account_org.rb +183 -0
  19. data/lib/vpndetection/models/account_org_ref.rb +164 -0
  20. data/lib/vpndetection/models/account_org_wrap.rb +190 -0
  21. data/lib/vpndetection/models/account_rc.rb +165 -0
  22. data/lib/vpndetection/models/account_revealed_apikey.rb +191 -0
  23. data/lib/vpndetection/models/account_user.rb +182 -0
  24. data/lib/vpndetection/models/apikey_detail.rb +307 -0
  25. data/lib/vpndetection/models/apikey_list.rb +192 -0
  26. data/lib/vpndetection/models/batch_lookup_error.rb +193 -0
  27. data/lib/vpndetection/models/batch_lookup_request.rb +185 -0
  28. data/lib/vpndetection/models/batch_lookup_response.rb +196 -0
  29. data/lib/vpndetection/models/class_detail.rb +1 -1
  30. data/lib/vpndetection/models/database.rb +1 -1
  31. data/lib/vpndetection/models/database_checksums_response.rb +1 -1
  32. data/lib/vpndetection/models/database_format.rb +1 -1
  33. data/lib/vpndetection/models/database_format_size.rb +2 -2
  34. data/lib/vpndetection/models/database_list.rb +1 -1
  35. data/lib/vpndetection/models/database_metadata.rb +1 -1
  36. data/lib/vpndetection/models/database_metadata_column.rb +1 -1
  37. data/lib/vpndetection/models/database_version.rb +1 -1
  38. data/lib/vpndetection/models/db_checksums.rb +1 -1
  39. data/lib/vpndetection/models/device_authorization.rb +281 -0
  40. data/lib/vpndetection/models/download.rb +2 -2
  41. data/lib/vpndetection/models/download_list.rb +1 -1
  42. data/lib/vpndetection/models/{account_me.rb → entitlement.rb} +7 -7
  43. data/lib/vpndetection/models/{account_apikey.rb → entitlement_apikey.rb} +4 -4
  44. data/lib/vpndetection/models/{account_error.rb → entitlement_error.rb} +4 -4
  45. data/lib/vpndetection/models/{account_plan.rb → entitlement_plan.rb} +4 -4
  46. data/lib/vpndetection/models/{account_usage.rb → entitlement_usage.rb} +4 -4
  47. data/lib/vpndetection/models/error_envelope.rb +1 -1
  48. data/lib/vpndetection/models/identity.rb +245 -0
  49. data/lib/vpndetection/models/lookup_error.rb +1 -1
  50. data/lib/vpndetection/models/lookup_response.rb +1 -1
  51. data/lib/vpndetection/models/oauth_error.rb +173 -0
  52. data/lib/vpndetection/models/oauth_metadata.rb +288 -0
  53. data/lib/vpndetection/models/proxy_detail.rb +1 -1
  54. data/lib/vpndetection/models/standing.rb +1 -1
  55. data/lib/vpndetection/models/token_response.rb +235 -0
  56. data/lib/vpndetection/models/vpn_detail.rb +1 -1
  57. data/lib/vpndetection/transport.rb +28 -5
  58. data/lib/vpndetection/version.rb +1 -1
  59. metadata +28 -6
@@ -0,0 +1,136 @@
1
+ =begin
2
+ #VPNDetection API
3
+
4
+ #The VPNDetection API: classify any IP address, and download the databases behind the answers. See https://docs.vpndetection.io for guides and https://github.com/vpndetection-io for the official client libraries.
5
+
6
+ The version of the OpenAPI document: 2026.09.15
7
+ Contact: support@vpndetection.io
8
+ Generated by: https://openapi-generator.tech
9
+ Generator version: 7.25.0
10
+
11
+ =end
12
+
13
+ require 'cgi'
14
+
15
+ module VPNDetection
16
+ class OrganizationWireApi
17
+ attr_accessor :api_client
18
+
19
+ def initialize(api_client = ApiClient.default)
20
+ @api_client = api_client
21
+ end
22
+ # Organization
23
+ # The organization this credential is scoped to. There is no way to name a different one. A credential describes exactly one organization, so an identifier in the path could only ever be your own or a refusal.
24
+ # @param [Hash] opts the optional parameters
25
+ # @return [AccountOrgWrap]
26
+ def account_org(opts = {})
27
+ data, _status_code, _headers = account_org_with_http_info(opts)
28
+ data
29
+ end
30
+
31
+ # Organization
32
+ # The organization this credential is scoped to. There is no way to name a different one. A credential describes exactly one organization, so an identifier in the path could only ever be your own or a refusal.
33
+ # @param [Hash] opts the optional parameters
34
+ # @return [Array<(AccountOrgWrap, Integer, Hash)>] AccountOrgWrap data, response status code and response headers
35
+ def account_org_with_http_info(opts = {})
36
+ if @api_client.config.debugging
37
+ @api_client.config.logger.debug 'Calling API: OrganizationWireApi.account_org ...'
38
+ end
39
+ # resource path
40
+ local_var_path = '/api/v1/iam/org'
41
+
42
+ # query parameters
43
+ query_params = opts[:query_params] || {}
44
+
45
+ # header parameters
46
+ header_params = opts[:header_params] || {}
47
+ # HTTP header 'Accept' (if needed)
48
+ header_params['Accept'] = @api_client.select_header_accept(['application/json']) unless header_params['Accept']
49
+
50
+ # form parameters
51
+ form_params = opts[:form_params] || {}
52
+
53
+ # http body (model)
54
+ post_body = opts[:debug_body]
55
+
56
+ # return_type
57
+ return_type = opts[:debug_return_type] || 'AccountOrgWrap'
58
+
59
+ # auth_names
60
+ auth_names = opts[:debug_auth_names] || ['bearerAuth']
61
+
62
+ new_options = opts.merge(
63
+ :operation => :"OrganizationWireApi.account_org",
64
+ :header_params => header_params,
65
+ :query_params => query_params,
66
+ :form_params => form_params,
67
+ :body => post_body,
68
+ :auth_names => auth_names,
69
+ :return_type => return_type
70
+ )
71
+
72
+ data, status_code, headers = @api_client.call_api(:GET, local_var_path, new_options)
73
+ if @api_client.config.debugging
74
+ @api_client.config.logger.debug "API called: OrganizationWireApi#account_org\nData: #{data.inspect}\nStatus code: #{status_code}\nHeaders: #{headers}"
75
+ end
76
+ return data, status_code, headers
77
+ end
78
+
79
+ # Members
80
+ # Read-only. Adding or removing a member is an invitation flow with email in the middle rather than a POST, and modelling it as one here would promise something this API does not do.
81
+ # @param [Hash] opts the optional parameters
82
+ # @return [AccountRc]
83
+ def account_org_members(opts = {})
84
+ data, _status_code, _headers = account_org_members_with_http_info(opts)
85
+ data
86
+ end
87
+
88
+ # Members
89
+ # Read-only. Adding or removing a member is an invitation flow with email in the middle rather than a POST, and modelling it as one here would promise something this API does not do.
90
+ # @param [Hash] opts the optional parameters
91
+ # @return [Array<(AccountRc, Integer, Hash)>] AccountRc data, response status code and response headers
92
+ def account_org_members_with_http_info(opts = {})
93
+ if @api_client.config.debugging
94
+ @api_client.config.logger.debug 'Calling API: OrganizationWireApi.account_org_members ...'
95
+ end
96
+ # resource path
97
+ local_var_path = '/api/v1/iam/org/members'
98
+
99
+ # query parameters
100
+ query_params = opts[:query_params] || {}
101
+
102
+ # header parameters
103
+ header_params = opts[:header_params] || {}
104
+ # HTTP header 'Accept' (if needed)
105
+ header_params['Accept'] = @api_client.select_header_accept(['application/json']) unless header_params['Accept']
106
+
107
+ # form parameters
108
+ form_params = opts[:form_params] || {}
109
+
110
+ # http body (model)
111
+ post_body = opts[:debug_body]
112
+
113
+ # return_type
114
+ return_type = opts[:debug_return_type] || 'AccountRc'
115
+
116
+ # auth_names
117
+ auth_names = opts[:debug_auth_names] || ['bearerAuth']
118
+
119
+ new_options = opts.merge(
120
+ :operation => :"OrganizationWireApi.account_org_members",
121
+ :header_params => header_params,
122
+ :query_params => query_params,
123
+ :form_params => form_params,
124
+ :body => post_body,
125
+ :auth_names => auth_names,
126
+ :return_type => return_type
127
+ )
128
+
129
+ data, status_code, headers = @api_client.call_api(:GET, local_var_path, new_options)
130
+ if @api_client.config.debugging
131
+ @api_client.config.logger.debug "API called: OrganizationWireApi#account_org_members\nData: #{data.inspect}\nStatus code: #{status_code}\nHeaders: #{headers}"
132
+ end
133
+ return data, status_code, headers
134
+ end
135
+ end
136
+ end
@@ -3,7 +3,7 @@
3
3
 
4
4
  #The VPNDetection API: classify any IP address, and download the databases behind the answers. See https://docs.vpndetection.io for guides and https://github.com/vpndetection-io for the official client libraries.
5
5
 
6
- The version of the OpenAPI document: 2026.09.13
6
+ The version of the OpenAPI document: 2026.09.15
7
7
  Contact: support@vpndetection.io
8
8
  Generated by: https://openapi-generator.tech
9
9
  Generator version: 7.25.0
@@ -3,7 +3,7 @@
3
3
 
4
4
  #The VPNDetection API: classify any IP address, and download the databases behind the answers. See https://docs.vpndetection.io for guides and https://github.com/vpndetection-io for the official client libraries.
5
5
 
6
- The version of the OpenAPI document: 2026.09.13
6
+ The version of the OpenAPI document: 2026.09.15
7
7
  Contact: support@vpndetection.io
8
8
  Generated by: https://openapi-generator.tech
9
9
  Generator version: 7.25.0
@@ -3,7 +3,7 @@
3
3
 
4
4
  #The VPNDetection API: classify any IP address, and download the databases behind the answers. See https://docs.vpndetection.io for guides and https://github.com/vpndetection-io for the official client libraries.
5
5
 
6
- The version of the OpenAPI document: 2026.09.13
6
+ The version of the OpenAPI document: 2026.09.15
7
7
  Contact: support@vpndetection.io
8
8
  Generated by: https://openapi-generator.tech
9
9
  Generator version: 7.25.0
@@ -9,6 +9,9 @@ module VPNDetection
9
9
  DEFAULT_CONCURRENCY = 8
10
10
  DEFAULT_RETRIES = 2
11
11
  DEFAULT_TIMEOUT = 10
12
+ # The most addresses POST /batch takes in one call; a larger batch is sent in
13
+ # chunks of this size.
14
+ BATCH_MAX = 1000
12
15
 
13
16
  # A client for the VPNDetection API.
14
17
  #
@@ -23,7 +26,7 @@ module VPNDetection
23
26
  # address.
24
27
  # @param cache [Boolean] pass false to disable caching.
25
28
  # @param cache_ttl [Numeric] how long an answer stays fresh, in seconds.
26
- # @param concurrency [Integer] in-flight requests during a batch.
29
+ # @param concurrency [Integer] batch requests - chunks of up to 1000 addresses - in flight during a batch.
27
30
  # @param retries [Integer] extra attempts for a transient failure.
28
31
  # @param transport [Transport, nil] override the HTTP layer, mostly for tests.
29
32
  def initialize(api_key: nil, base_url: DEFAULT_BASE_URL, cache: true,
@@ -87,7 +90,7 @@ module VPNDetection
87
90
  #
88
91
  # Named for what it answers rather than `me`, which sits one letter from
89
92
  # {#my_ip} and means something quite different: one is which address you are
90
- # calling FROM, the other is which account you are calling AS.
93
+ # calling FROM, the other is what the key you are calling WITH may spend.
91
94
  #
92
95
  # Unlike a lookup there is no useful unauthenticated answer, so a client
93
96
  # built without an API key gets an unauthorized error rather than a partial
@@ -101,20 +104,26 @@ module VPNDetection
101
104
  # Deliberately NOT cached: the whole point is what has been spent, and a
102
105
  # cached answer is a wrong one within seconds of the next request.
103
106
  #
104
- # @return [AccountMe]
105
- def my_account(retries: nil)
107
+ # @return [Entitlement]
108
+ def my_entitlement(retries: nil)
106
109
  Retries.with_retries(retries || @retries) do
107
- Transport.account_result(@transport.account_request.run)
110
+ Transport.entitlement_result(@transport.entitlement_request.run)
108
111
  end
109
112
  end
110
113
 
111
- # Classify many addresses in parallel.
114
+ # Classify many addresses in as few requests as possible.
112
115
  #
113
- # Keyed by address rather than positional, so duplicates in the input
114
- # collapse to a single request and the caller never has to line two lists
115
- # up. An address that fails carries its error as its value, so one bad entry
116
- # cannot lose the rest of the answers.
116
+ # Bogons are answered locally and cached answers are reused; everything else
117
+ # goes to the batch endpoint in chunks of up to 1000 addresses, with at most
118
+ # `concurrency` chunks in flight. Keyed by address rather than positional, so
119
+ # duplicates in the input collapse to a single entry and the caller never has
120
+ # to line two lists up. An address that fails carries its error as its value,
121
+ # so one bad entry cannot lose the rest of the answers: the API reports a
122
+ # per-entry failure with the status the single lookup would have answered,
123
+ # and a chunk that fails as a whole marks every address in it.
117
124
  #
125
+ # @param concurrency [Integer, nil] chunks in flight, for THIS batch only.
126
+ # @param retries [Integer, nil] extra attempts for a failed chunk, for THIS batch only.
118
127
  # @return [Hash{String => Result, Error}] in the order the addresses were given
119
128
  def lookup_batch(ips, concurrency: nil, retries: nil)
120
129
  addresses = ips.to_a.uniq
@@ -126,7 +135,8 @@ module VPNDetection
126
135
  hit.nil? ? pending << ip : answers[ip] = hit
127
136
  end
128
137
  unless pending.empty?
129
- run_batch(pending, answers, concurrency || @concurrency, retries || @retries)
138
+ run_batch(pending.each_slice(BATCH_MAX).to_a, answers,
139
+ concurrency || @concurrency, retries || @retries)
130
140
  end
131
141
 
132
142
  # Reinstated in input order: a hydra settles in completion order, and a
@@ -138,37 +148,58 @@ module VPNDetection
138
148
 
139
149
  # One hydra per call, sized for THIS call. Reusing an instance-level hydra
140
150
  # would silently cap a per-call concurrency at the client's setting, and
141
- # would not be safe to drive from two threads either.
142
- def run_batch(pending, answers, concurrency, retries)
151
+ # would not be safe to drive from two threads either. Each request is one
152
+ # chunk of up to 1000 addresses.
153
+ def run_batch(chunks, answers, concurrency, retries)
143
154
  hydra = Typhoeus::Hydra.new(max_concurrency: concurrency)
144
155
  attempts = Hash.new(0)
145
156
 
146
- enqueue = lambda do |ip|
147
- request = @transport.lookup_request(ip)
157
+ enqueue = lambda do |chunk|
158
+ request = @transport.batch_request(chunk)
148
159
  request.on_complete do |response|
149
- outcome = settle(ip, response, attempts, retries, enqueue)
150
- answers[ip] = outcome unless outcome.nil?
160
+ outcome = settle(chunk, response, attempts, retries, enqueue)
161
+ answers.merge!(outcome) unless outcome.nil?
151
162
  end
152
163
  hydra.queue(request)
153
164
  end
154
165
 
155
- pending.each { |ip| enqueue.call(ip) }
166
+ chunks.each { |chunk| enqueue.call(chunk) }
156
167
  hydra.run
157
168
  end
158
169
 
159
- def settle(ip, response, attempts, retries, enqueue)
160
- result = Transport.lookup_result(response)
161
- @cache&.set(ip, result)
162
- result
170
+ # One POST /batch, mapped back onto the addresses it was asked about. A
171
+ # chunk-level failure - the call refused, the transport failing, the retries
172
+ # exhausted - becomes every address's error, exactly as it would have been
173
+ # had each been looked up alone.
174
+ def settle(chunk, response, attempts, retries, enqueue)
175
+ body = Transport.batch_body(response)
176
+ chunk.to_h do |ip|
177
+ answer = batch_answer(ip, body)
178
+ @cache&.set(ip, answer) if answer.is_a?(Result)
179
+ [ip, answer]
180
+ end
163
181
  rescue Error => e
164
- return e unless e.retryable? && attempts[ip] < retries
182
+ return chunk.to_h { |ip| [ip, e] } unless e.retryable? && attempts[chunk] < retries
165
183
 
166
- attempts[ip] += 1
184
+ attempts[chunk] += 1
167
185
  # Sleeping here stalls the whole hydra, which is what a server-supplied
168
186
  # delay asks for: it is telling every request to this host to back off.
169
- sleep(Retries.delay_for(e, attempts[ip]))
170
- enqueue.call(ip)
187
+ sleep(Retries.delay_for(e, attempts[chunk]))
188
+ enqueue.call(chunk)
171
189
  nil
172
190
  end
191
+
192
+ # Every address lands in exactly one of `results` and `errors`; an address in
193
+ # neither is the server breaking its own contract, and is reported as such
194
+ # rather than lost.
195
+ def batch_answer(ip, body)
196
+ if (served = body['results'][ip])
197
+ Result.new(served)
198
+ elsif (failed = body['errors'][ip])
199
+ Error.from_entry(failed['status'], failed['error'])
200
+ else
201
+ Error.new(:server_error, "the batch answer did not include #{ip}", status: 200)
202
+ end
203
+ end
173
204
  end
174
205
  end
@@ -3,7 +3,7 @@
3
3
 
4
4
  #The VPNDetection API: classify any IP address, and download the databases behind the answers. See https://docs.vpndetection.io for guides and https://github.com/vpndetection-io for the official client libraries.
5
5
 
6
- The version of the OpenAPI document: 2026.09.13
6
+ The version of the OpenAPI document: 2026.09.15
7
7
  Contact: support@vpndetection.io
8
8
  Generated by: https://openapi-generator.tech
9
9
  Generator version: 7.25.0
@@ -67,6 +67,13 @@ module VPNDetection
67
67
  new(:network, message)
68
68
  end
69
69
 
70
+ # A per-entry failure inside a successful batch: the status the single lookup
71
+ # would have answered, and its message, with no headers at all - so a 429
72
+ # here is a spent allowance, which is the only kind the API puts in an entry.
73
+ def self.from_entry(status, message)
74
+ from_status(status.to_i, {}, nil, message: message.to_s)
75
+ end
76
+
70
77
  # Case-insensitive, and works with a plain Hash as well as with the
71
78
  # case-blind header object Typhoeus builds from a live response.
72
79
  def self.header(headers, name)
@@ -0,0 +1,177 @@
1
+ =begin
2
+ #VPNDetection API
3
+
4
+ #The VPNDetection API: classify any IP address, and download the databases behind the answers. See https://docs.vpndetection.io for guides and https://github.com/vpndetection-io for the official client libraries.
5
+
6
+ The version of the OpenAPI document: 2026.09.15
7
+ Contact: support@vpndetection.io
8
+ Generated by: https://openapi-generator.tech
9
+ Generator version: 7.25.0
10
+
11
+ =end
12
+
13
+ require 'date'
14
+ require 'time'
15
+
16
+ module VPNDetection
17
+ class AccountCreateApikeyRequest < ApiModelBase
18
+ # A label you will recognise later. Shown wherever the key is listed.
19
+ attr_accessor :name
20
+
21
+ # What the new key may do. Omit for a key that carries no named scope, which is the safe default.
22
+ attr_accessor :allowed_scopes
23
+
24
+ # Attribute mapping from ruby-style variable name to JSON key.
25
+ def self.attribute_map
26
+ {
27
+ :'name' => :'name',
28
+ :'allowed_scopes' => :'allowed_scopes'
29
+ }
30
+ end
31
+
32
+ # Returns attribute mapping this model knows about
33
+ def self.acceptable_attribute_map
34
+ attribute_map
35
+ end
36
+
37
+ # Returns all the JSON keys this model knows about
38
+ def self.acceptable_attributes
39
+ acceptable_attribute_map.values
40
+ end
41
+
42
+ # Attribute type mapping.
43
+ def self.openapi_types
44
+ {
45
+ :'name' => :'String',
46
+ :'allowed_scopes' => :'Array<String>'
47
+ }
48
+ end
49
+
50
+ # List of attributes with nullable: true
51
+ def self.openapi_nullable
52
+ Set.new([
53
+ ])
54
+ end
55
+
56
+ # Initializes the object
57
+ # @param [Hash] attributes Model attributes in the form of hash
58
+ def initialize(attributes = {})
59
+ if (!attributes.is_a?(Hash))
60
+ fail ArgumentError, "The input argument (attributes) must be a hash in `VPNDetection::AccountCreateApikeyRequest` initialize method"
61
+ end
62
+
63
+ # check to see if the attribute exists and convert string to symbol for hash key
64
+ acceptable_attribute_map = self.class.acceptable_attribute_map
65
+ attributes = attributes.each_with_object({}) { |(k, v), h|
66
+ if (!acceptable_attribute_map.key?(k.to_sym))
67
+ fail ArgumentError, "`#{k}` is not a valid attribute in `VPNDetection::AccountCreateApikeyRequest`. Please check the name to make sure it's valid. List of attributes: " + acceptable_attribute_map.keys.inspect
68
+ end
69
+ h[k.to_sym] = v
70
+ }
71
+
72
+ if attributes.key?(:'name')
73
+ self.name = attributes[:'name']
74
+ else
75
+ self.name = nil
76
+ end
77
+
78
+ if attributes.key?(:'allowed_scopes')
79
+ if (value = attributes[:'allowed_scopes']).is_a?(Array)
80
+ self.allowed_scopes = value
81
+ end
82
+ end
83
+ end
84
+
85
+ # Show invalid properties with the reasons. Usually used together with valid?
86
+ # @return Array for valid properties with the reasons
87
+ def list_invalid_properties
88
+ warn '[DEPRECATED] the `list_invalid_properties` method is obsolete'
89
+ invalid_properties = Array.new
90
+ if @name.nil?
91
+ invalid_properties.push('invalid value for "name", name cannot be nil.')
92
+ end
93
+
94
+ invalid_properties
95
+ end
96
+
97
+ # Check to see if the all the properties in the model are valid
98
+ # @return true if the model is valid
99
+ def valid?
100
+ warn '[DEPRECATED] the `valid?` method is obsolete'
101
+ return false if @name.nil?
102
+ true
103
+ end
104
+
105
+ # Custom attribute writer method with validation
106
+ # @param [Object] name Value to be assigned
107
+ def name=(name)
108
+ if name.nil?
109
+ fail ArgumentError, 'name cannot be nil'
110
+ end
111
+
112
+ @name = name
113
+ end
114
+
115
+ # Checks equality by comparing each attribute.
116
+ # @param [Object] Object to be compared
117
+ def ==(o)
118
+ return true if self.equal?(o)
119
+ self.class == o.class &&
120
+ name == o.name &&
121
+ allowed_scopes == o.allowed_scopes
122
+ end
123
+
124
+ # @see the `==` method
125
+ # @param [Object] Object to be compared
126
+ def eql?(o)
127
+ self == o
128
+ end
129
+
130
+ # Calculates hash code according to all attributes.
131
+ # @return [Integer] Hash code
132
+ def hash
133
+ [name, allowed_scopes].hash
134
+ end
135
+
136
+ # Builds the object from hash
137
+ # @param [Hash] attributes Model attributes in the form of hash
138
+ # @return [Object] Returns the model itself
139
+ def self.build_from_hash(attributes)
140
+ return nil unless attributes.is_a?(Hash)
141
+ attributes = attributes.transform_keys(&:to_sym)
142
+ transformed_hash = {}
143
+ openapi_types.each_pair do |key, type|
144
+ if attributes.key?(attribute_map[key]) && attributes[attribute_map[key]].nil?
145
+ transformed_hash["#{key}"] = nil
146
+ elsif type =~ /\AArray<(.*)>/i
147
+ # check to ensure the input is an array given that the attribute
148
+ # is documented as an array but the input is not
149
+ if attributes[attribute_map[key]].is_a?(Array)
150
+ transformed_hash["#{key}"] = attributes[attribute_map[key]].map { |v| _deserialize($1, v) }
151
+ end
152
+ elsif !attributes[attribute_map[key]].nil?
153
+ transformed_hash["#{key}"] = _deserialize(type, attributes[attribute_map[key]])
154
+ end
155
+ end
156
+ new(transformed_hash)
157
+ end
158
+
159
+ # Returns the object in the form of hash
160
+ # @return [Hash] Returns the object in the form of hash
161
+ def to_hash
162
+ hash = {}
163
+ self.class.attribute_map.each_pair do |attr, param|
164
+ value = self.send(attr)
165
+ if value.nil?
166
+ is_nullable = self.class.openapi_nullable.include?(attr)
167
+ next if !is_nullable || (is_nullable && !instance_variable_defined?(:"@#{attr}"))
168
+ end
169
+
170
+ hash[param] = _to_hash(value)
171
+ end
172
+ hash
173
+ end
174
+
175
+ end
176
+
177
+ end