ipgeolocation_sdk 1.0.0 → 2.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.
- checksums.yaml +4 -4
- data/CHANGELOG.md +8 -0
- data/README.md +288 -2003
- data/lib/ipgeolocation_sdk/client.rb +223 -0
- data/lib/ipgeolocation_sdk/client_config.rb +102 -0
- data/lib/ipgeolocation_sdk/enums.rb +110 -0
- data/lib/ipgeolocation_sdk/errors.rb +38 -0
- data/lib/ipgeolocation_sdk/json_output.rb +30 -0
- data/lib/ipgeolocation_sdk/models.rb +186 -0
- data/lib/ipgeolocation_sdk/request_models.rb +175 -0
- data/lib/ipgeolocation_sdk/serde.rb +433 -0
- data/lib/ipgeolocation_sdk/transport.rb +109 -0
- data/lib/ipgeolocation_sdk/value_object.rb +60 -0
- data/lib/ipgeolocation_sdk/version.rb +1 -13
- data/lib/ipgeolocation_sdk.rb +19 -101
- metadata +29 -310
- data/Gemfile +0 -9
- data/Rakefile +0 -10
- data/docs/ASNConnection.md +0 -22
- data/docs/ASNDetails.md +0 -48
- data/docs/ASNLookupApi.md +0 -89
- data/docs/ASNResponse.md +0 -20
- data/docs/ASNResponseXML.md +0 -20
- data/docs/ASNResponseXMLAsn.md +0 -48
- data/docs/Abuse.md +0 -40
- data/docs/AbuseContactApi.md +0 -85
- data/docs/AbuseResponse.md +0 -20
- data/docs/AbuseResponseXML.md +0 -20
- data/docs/Astronomy.md +0 -68
- data/docs/AstronomyApi.md +0 -97
- data/docs/AstronomyEvening.md +0 -36
- data/docs/AstronomyLocation.md +0 -50
- data/docs/AstronomyMorning.md +0 -36
- data/docs/AstronomyResponse.md +0 -22
- data/docs/AstronomyXMLResponse.md +0 -22
- data/docs/BulkIPGeolocation.md +0 -42
- data/docs/BulkIPSecurity.md +0 -42
- data/docs/CountryMetadata.md +0 -22
- data/docs/Currency.md +0 -22
- data/docs/ErrorResponse.md +0 -18
- data/docs/ErrorXMLResponse.md +0 -18
- data/docs/ErrorXMLResponseArray.md +0 -18
- data/docs/GeolocationResponse.md +0 -38
- data/docs/GeolocationXMLResponse.md +0 -38
- data/docs/GeolocationXMLResponseArray.md +0 -38
- data/docs/GetBulkIpGeolocation200ResponseInner1.md +0 -49
- data/docs/GetBulkIpGeolocationRequest.md +0 -18
- data/docs/GetBulkIpSecurityInfo200ResponseInner1.md +0 -49
- data/docs/IPLocationApi.md +0 -175
- data/docs/Location.md +0 -58
- data/docs/LocationMinimal.md +0 -52
- data/docs/Network.md +0 -22
- data/docs/NetworkAsn.md +0 -38
- data/docs/NetworkCompany.md +0 -22
- data/docs/NetworkMinimal.md +0 -20
- data/docs/NetworkMinimalAsn.md +0 -22
- data/docs/NetworkMinimalCompany.md +0 -18
- data/docs/ParseBulkUserAgentStringsRequest.md +0 -18
- data/docs/ParseUserAgentStringRequest.md +0 -18
- data/docs/Security.md +0 -38
- data/docs/SecurityAPIResponse.md +0 -34
- data/docs/SecurityAPIXMLResponse.md +0 -34
- data/docs/SecurityAPIXMLResponseArray.md +0 -34
- data/docs/SecurityApi.md +0 -175
- data/docs/TimeConversionApi.md +0 -109
- data/docs/TimeConversionResponse.md +0 -24
- data/docs/TimeConversionXMLResponse.md +0 -24
- data/docs/TimeZone.md +0 -36
- data/docs/TimeZoneDetailedResponse.md +0 -26
- data/docs/TimeZoneDetailedXMLResponse.md +0 -26
- data/docs/TimeZoneDstEnd.md +0 -28
- data/docs/TimeZoneDstStart.md +0 -28
- data/docs/TimezoneAirport.md +0 -40
- data/docs/TimezoneApi.md +0 -99
- data/docs/TimezoneDetail.md +0 -56
- data/docs/TimezoneDetailDstEnd.md +0 -28
- data/docs/TimezoneDetailDstStart.md +0 -28
- data/docs/TimezoneLocation.md +0 -48
- data/docs/TimezoneLocode.md +0 -32
- data/docs/UserAgentApi.md +0 -235
- data/docs/UserAgentData.md +0 -32
- data/docs/UserAgentDataDevice.md +0 -24
- data/docs/UserAgentDataEngine.md +0 -24
- data/docs/UserAgentDataOperatingSystem.md +0 -26
- data/docs/UserAgentXMLData.md +0 -32
- data/docs/UserAgentXMLDataArray.md +0 -32
- data/git_push.sh +0 -57
- data/ipgeolocation_sdk.gemspec +0 -29
- data/lib/ipgeolocation_sdk/api/abuse_contact_api.rb +0 -86
- data/lib/ipgeolocation_sdk/api/asn_lookup_api.rb +0 -92
- data/lib/ipgeolocation_sdk/api/astronomy_api.rb +0 -116
- data/lib/ipgeolocation_sdk/api/ip_geolocation_api.rb +0 -186
- data/lib/ipgeolocation_sdk/api/ip_security_api.rb +0 -184
- data/lib/ipgeolocation_sdk/api/time_conversion_api.rb +0 -122
- data/lib/ipgeolocation_sdk/api/timezone_api.rb +0 -113
- data/lib/ipgeolocation_sdk/api/user_agent_api.rb +0 -158
- data/lib/ipgeolocation_sdk/api_client.rb +0 -393
- data/lib/ipgeolocation_sdk/api_error.rb +0 -58
- data/lib/ipgeolocation_sdk/configuration.rb +0 -308
- data/lib/ipgeolocation_sdk/models/abuse.rb +0 -305
- data/lib/ipgeolocation_sdk/models/abuse_response.rb +0 -229
- data/lib/ipgeolocation_sdk/models/abuse_response_xml.rb +0 -229
- data/lib/ipgeolocation_sdk/models/asn_connection.rb +0 -238
- data/lib/ipgeolocation_sdk/models/asn_response.rb +0 -230
- data/lib/ipgeolocation_sdk/models/asn_response_asn.rb +0 -368
- data/lib/ipgeolocation_sdk/models/asn_response_xml.rb +0 -229
- data/lib/ipgeolocation_sdk/models/asn_response_xml_asn.rb +0 -364
- data/lib/ipgeolocation_sdk/models/astronomy.rb +0 -445
- data/lib/ipgeolocation_sdk/models/astronomy_evening.rb +0 -301
- data/lib/ipgeolocation_sdk/models/astronomy_location.rb +0 -364
- data/lib/ipgeolocation_sdk/models/astronomy_morning.rb +0 -301
- data/lib/ipgeolocation_sdk/models/astronomy_response.rb +0 -238
- data/lib/ipgeolocation_sdk/models/astronomy_xml_response.rb +0 -238
- data/lib/ipgeolocation_sdk/models/bulk_ip_geolocation.rb +0 -113
- data/lib/ipgeolocation_sdk/models/bulk_ip_security.rb +0 -113
- data/lib/ipgeolocation_sdk/models/country_metadata.rb +0 -240
- data/lib/ipgeolocation_sdk/models/currency.rb +0 -238
- data/lib/ipgeolocation_sdk/models/error_response.rb +0 -220
- data/lib/ipgeolocation_sdk/models/error_xml_response.rb +0 -220
- data/lib/ipgeolocation_sdk/models/error_xml_response_array.rb +0 -220
- data/lib/ipgeolocation_sdk/models/geolocation_response.rb +0 -310
- data/lib/ipgeolocation_sdk/models/geolocation_xml_response.rb +0 -310
- data/lib/ipgeolocation_sdk/models/geolocation_xml_response_array.rb +0 -310
- data/lib/ipgeolocation_sdk/models/get_bulk_ip_geolocation200_response_inner1.rb +0 -105
- data/lib/ipgeolocation_sdk/models/get_bulk_ip_geolocation_request.rb +0 -241
- data/lib/ipgeolocation_sdk/models/get_bulk_ip_security_info200_response_inner1.rb +0 -105
- data/lib/ipgeolocation_sdk/models/location.rb +0 -400
- data/lib/ipgeolocation_sdk/models/location_minimal.rb +0 -373
- data/lib/ipgeolocation_sdk/models/network.rb +0 -238
- data/lib/ipgeolocation_sdk/models/network_asn.rb +0 -310
- data/lib/ipgeolocation_sdk/models/network_company.rb +0 -238
- data/lib/ipgeolocation_sdk/models/network_minimal.rb +0 -229
- data/lib/ipgeolocation_sdk/models/network_minimal_asn.rb +0 -238
- data/lib/ipgeolocation_sdk/models/network_minimal_company.rb +0 -220
- data/lib/ipgeolocation_sdk/models/parse_bulk_user_agent_strings_request.rb +0 -222
- data/lib/ipgeolocation_sdk/models/parse_user_agent_string_request.rb +0 -220
- data/lib/ipgeolocation_sdk/models/security.rb +0 -310
- data/lib/ipgeolocation_sdk/models/security_api_response.rb +0 -292
- data/lib/ipgeolocation_sdk/models/security_apixml_response.rb +0 -292
- data/lib/ipgeolocation_sdk/models/security_apixml_response_array.rb +0 -292
- data/lib/ipgeolocation_sdk/models/time_conversion_response.rb +0 -247
- data/lib/ipgeolocation_sdk/models/time_conversion_xml_response.rb +0 -247
- data/lib/ipgeolocation_sdk/models/time_zone.rb +0 -301
- data/lib/ipgeolocation_sdk/models/time_zone_detailed_response.rb +0 -256
- data/lib/ipgeolocation_sdk/models/time_zone_detailed_xml_response.rb +0 -256
- data/lib/ipgeolocation_sdk/models/time_zone_dst_end.rb +0 -265
- data/lib/ipgeolocation_sdk/models/time_zone_dst_start.rb +0 -265
- data/lib/ipgeolocation_sdk/models/timezone_airport.rb +0 -319
- data/lib/ipgeolocation_sdk/models/timezone_detail.rb +0 -391
- data/lib/ipgeolocation_sdk/models/timezone_detail_dst_end.rb +0 -265
- data/lib/ipgeolocation_sdk/models/timezone_detail_dst_start.rb +0 -265
- data/lib/ipgeolocation_sdk/models/timezone_location.rb +0 -355
- data/lib/ipgeolocation_sdk/models/timezone_locode.rb +0 -283
- data/lib/ipgeolocation_sdk/models/user_agent_data.rb +0 -283
- data/lib/ipgeolocation_sdk/models/user_agent_data_device.rb +0 -247
- data/lib/ipgeolocation_sdk/models/user_agent_data_engine.rb +0 -247
- data/lib/ipgeolocation_sdk/models/user_agent_data_operating_system.rb +0 -256
- data/lib/ipgeolocation_sdk/models/user_agent_xml_data.rb +0 -283
- data/lib/ipgeolocation_sdk/models/user_agent_xml_data_array.rb +0 -283
- data/spec/api/abuse_contact_api_spec.rb +0 -48
- data/spec/api/asn_lookup_api_spec.rb +0 -50
- data/spec/api/astronomy_api_spec.rb +0 -54
- data/spec/api/ip_location_api_spec.rb +0 -67
- data/spec/api/security_api_spec.rb +0 -67
- data/spec/api/time_conversion_api_spec.rb +0 -60
- data/spec/api/timezone_api_spec.rb +0 -56
- data/spec/api/user_agent_api_spec.rb +0 -74
- data/spec/models/abuse_response_spec.rb +0 -42
- data/spec/models/abuse_response_xml_spec.rb +0 -42
- data/spec/models/abuse_spec.rb +0 -90
- data/spec/models/asn_connection_spec.rb +0 -48
- data/spec/models/asn_response_asn_spec.rb +0 -126
- data/spec/models/asn_response_spec.rb +0 -42
- data/spec/models/asn_response_xml_asn_spec.rb +0 -126
- data/spec/models/asn_response_xml_spec.rb +0 -42
- data/spec/models/astronomy_evening_spec.rb +0 -90
- data/spec/models/astronomy_location_spec.rb +0 -132
- data/spec/models/astronomy_morning_spec.rb +0 -90
- data/spec/models/astronomy_response_spec.rb +0 -48
- data/spec/models/astronomy_spec.rb +0 -186
- data/spec/models/astronomy_xml_response_spec.rb +0 -48
- data/spec/models/country_metadata_spec.rb +0 -48
- data/spec/models/currency_spec.rb +0 -48
- data/spec/models/error_response_spec.rb +0 -36
- data/spec/models/error_xml_response_array_spec.rb +0 -36
- data/spec/models/error_xml_response_spec.rb +0 -36
- data/spec/models/geolocation_response_spec.rb +0 -96
- data/spec/models/geolocation_xml_response_array_spec.rb +0 -96
- data/spec/models/geolocation_xml_response_spec.rb +0 -96
- data/spec/models/get_bulk_ip_geolocation200_response_inner1_spec.rb +0 -32
- data/spec/models/get_bulk_ip_geolocation200_response_inner_spec.rb +0 -32
- data/spec/models/get_bulk_ip_geolocation_request_spec.rb +0 -36
- data/spec/models/get_bulk_ip_security_info200_response_inner1_spec.rb +0 -32
- data/spec/models/get_bulk_ip_security_info200_response_inner_spec.rb +0 -32
- data/spec/models/location_minimal_spec.rb +0 -138
- data/spec/models/location_spec.rb +0 -156
- data/spec/models/network_asn_spec.rb +0 -96
- data/spec/models/network_company_spec.rb +0 -48
- data/spec/models/network_minimal_asn_spec.rb +0 -48
- data/spec/models/network_minimal_company_spec.rb +0 -36
- data/spec/models/network_minimal_spec.rb +0 -42
- data/spec/models/network_spec.rb +0 -48
- data/spec/models/parse_bulk_user_agent_strings_request_spec.rb +0 -36
- data/spec/models/parse_user_agent_string_request_spec.rb +0 -36
- data/spec/models/security_api_response_spec.rb +0 -84
- data/spec/models/security_apixml_response_array_spec.rb +0 -84
- data/spec/models/security_apixml_response_spec.rb +0 -84
- data/spec/models/security_spec.rb +0 -96
- data/spec/models/time_conversion_response_spec.rb +0 -54
- data/spec/models/time_conversion_xml_response_spec.rb +0 -54
- data/spec/models/time_zone_detailed_response_spec.rb +0 -60
- data/spec/models/time_zone_detailed_xml_response_spec.rb +0 -60
- data/spec/models/time_zone_dst_end_spec.rb +0 -66
- data/spec/models/time_zone_dst_start_spec.rb +0 -66
- data/spec/models/time_zone_spec.rb +0 -90
- data/spec/models/timezone_airport_spec.rb +0 -102
- data/spec/models/timezone_detail_dst_end_spec.rb +0 -66
- data/spec/models/timezone_detail_dst_start_spec.rb +0 -66
- data/spec/models/timezone_detail_spec.rb +0 -150
- data/spec/models/timezone_location_spec.rb +0 -126
- data/spec/models/timezone_locode_spec.rb +0 -78
- data/spec/models/user_agent_data_device_spec.rb +0 -54
- data/spec/models/user_agent_data_engine_spec.rb +0 -54
- data/spec/models/user_agent_data_operating_system_spec.rb +0 -60
- data/spec/models/user_agent_data_spec.rb +0 -78
- data/spec/models/user_agent_xml_data_array_spec.rb +0 -78
- data/spec/models/user_agent_xml_data_spec.rb +0 -78
- data/spec/spec_helper.rb +0 -111
|
@@ -0,0 +1,175 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module IpgeolocationSdk
|
|
4
|
+
class LookupIpGeolocationRequest < ValueObject
|
|
5
|
+
attributes :ip, :lang, :include, :fields, :excludes, :user_agent, :headers, :output
|
|
6
|
+
|
|
7
|
+
def initialize(
|
|
8
|
+
ip: nil,
|
|
9
|
+
lang: nil,
|
|
10
|
+
include: nil,
|
|
11
|
+
fields: nil,
|
|
12
|
+
excludes: nil,
|
|
13
|
+
user_agent: nil,
|
|
14
|
+
headers: nil,
|
|
15
|
+
output: ResponseFormat::JSON
|
|
16
|
+
)
|
|
17
|
+
normalized_ip = self.class.send(:normalize_ip, ip)
|
|
18
|
+
normalized_lang = Language.normalize(lang)
|
|
19
|
+
normalized_include = self.class.send(:normalize_tokens, include, "include")
|
|
20
|
+
normalized_fields = self.class.send(:normalize_tokens, fields, "fields")
|
|
21
|
+
normalized_excludes = self.class.send(:normalize_tokens, excludes, "excludes")
|
|
22
|
+
normalized_user_agent = self.class.send(:normalize_optional_string, user_agent, "user_agent")
|
|
23
|
+
normalized_headers = self.class.send(:normalize_headers, headers)
|
|
24
|
+
normalized_output = ResponseFormat.normalize(output)
|
|
25
|
+
|
|
26
|
+
super(
|
|
27
|
+
ip: normalized_ip,
|
|
28
|
+
lang: normalized_lang,
|
|
29
|
+
include: normalized_include,
|
|
30
|
+
fields: normalized_fields,
|
|
31
|
+
excludes: normalized_excludes,
|
|
32
|
+
user_agent: normalized_user_agent,
|
|
33
|
+
headers: normalized_headers,
|
|
34
|
+
output: normalized_output
|
|
35
|
+
)
|
|
36
|
+
end
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
class BulkLookupIpGeolocationRequest < ValueObject
|
|
40
|
+
attributes :ips, :lang, :include, :fields, :excludes, :user_agent, :headers, :output
|
|
41
|
+
|
|
42
|
+
def initialize(
|
|
43
|
+
ips:,
|
|
44
|
+
lang: nil,
|
|
45
|
+
include: nil,
|
|
46
|
+
fields: nil,
|
|
47
|
+
excludes: nil,
|
|
48
|
+
user_agent: nil,
|
|
49
|
+
headers: nil,
|
|
50
|
+
output: ResponseFormat::JSON
|
|
51
|
+
)
|
|
52
|
+
normalized_ips = self.class.send(:normalize_tokens, ips, "ips")
|
|
53
|
+
raise ValidationError, "ips must not be empty" if normalized_ips.empty?
|
|
54
|
+
raise ValidationError, "ips must contain at most 50000 entries" if normalized_ips.length > 50_000
|
|
55
|
+
|
|
56
|
+
normalized_lang = Language.normalize(lang)
|
|
57
|
+
normalized_include = self.class.send(:normalize_tokens, include, "include")
|
|
58
|
+
normalized_fields = self.class.send(:normalize_tokens, fields, "fields")
|
|
59
|
+
normalized_excludes = self.class.send(:normalize_tokens, excludes, "excludes")
|
|
60
|
+
normalized_user_agent = self.class.send(:normalize_optional_string, user_agent, "user_agent")
|
|
61
|
+
normalized_headers = self.class.send(:normalize_headers, headers)
|
|
62
|
+
normalized_output = ResponseFormat.normalize(output)
|
|
63
|
+
|
|
64
|
+
super(
|
|
65
|
+
ips: normalized_ips,
|
|
66
|
+
lang: normalized_lang,
|
|
67
|
+
include: normalized_include,
|
|
68
|
+
fields: normalized_fields,
|
|
69
|
+
excludes: normalized_excludes,
|
|
70
|
+
user_agent: normalized_user_agent,
|
|
71
|
+
headers: normalized_headers,
|
|
72
|
+
output: normalized_output
|
|
73
|
+
)
|
|
74
|
+
end
|
|
75
|
+
end
|
|
76
|
+
|
|
77
|
+
class << LookupIpGeolocationRequest
|
|
78
|
+
private
|
|
79
|
+
|
|
80
|
+
def normalize_ip(value)
|
|
81
|
+
return nil if value.nil?
|
|
82
|
+
raise TypeError, "ip must be a string" unless value.is_a?(String)
|
|
83
|
+
|
|
84
|
+
normalized = value.strip
|
|
85
|
+
raise ValidationError, "ip must not be blank" if normalized.empty?
|
|
86
|
+
|
|
87
|
+
normalized
|
|
88
|
+
end
|
|
89
|
+
|
|
90
|
+
def normalize_optional_string(value, field)
|
|
91
|
+
return nil if value.nil?
|
|
92
|
+
raise TypeError, "#{field} must be a string" unless value.is_a?(String)
|
|
93
|
+
|
|
94
|
+
normalized = value.strip
|
|
95
|
+
raise ValidationError, "#{field} must not be blank" if normalized.empty?
|
|
96
|
+
normalized
|
|
97
|
+
end
|
|
98
|
+
|
|
99
|
+
def normalize_tokens(values, field)
|
|
100
|
+
return [].freeze if values.nil?
|
|
101
|
+
raise ValidationError, "#{field} must be an array of strings, not a single string" if values.is_a?(String)
|
|
102
|
+
raise TypeError, "#{field} must be an array of strings" unless values.is_a?(Array)
|
|
103
|
+
|
|
104
|
+
normalized = values.map do |value|
|
|
105
|
+
raise TypeError, "#{field} values must be strings" unless value.is_a?(String)
|
|
106
|
+
|
|
107
|
+
token = value.strip
|
|
108
|
+
raise ValidationError, "#{field} values must not be blank" if token.empty?
|
|
109
|
+
|
|
110
|
+
token
|
|
111
|
+
end
|
|
112
|
+
normalized.freeze
|
|
113
|
+
end
|
|
114
|
+
|
|
115
|
+
def normalize_headers(value)
|
|
116
|
+
return {}.freeze if value.nil?
|
|
117
|
+
raise TypeError, "headers must be a hash" unless value.is_a?(Hash)
|
|
118
|
+
|
|
119
|
+
value.each_with_object({}) do |(raw_name, raw_value), result|
|
|
120
|
+
raise TypeError, "header names must be strings" unless raw_name.is_a?(String)
|
|
121
|
+
|
|
122
|
+
name = raw_name.strip
|
|
123
|
+
raise ValidationError, "header names must not be blank" if name.empty?
|
|
124
|
+
raise ValidationError, "header names must not contain CR or LF" if contains_cr_or_lf?(name)
|
|
125
|
+
|
|
126
|
+
values = normalize_header_values(raw_value)
|
|
127
|
+
result[name] = values.freeze
|
|
128
|
+
end.freeze
|
|
129
|
+
end
|
|
130
|
+
|
|
131
|
+
def normalize_header_values(value)
|
|
132
|
+
raw_values =
|
|
133
|
+
case value
|
|
134
|
+
when String
|
|
135
|
+
[value]
|
|
136
|
+
when Array
|
|
137
|
+
value
|
|
138
|
+
else
|
|
139
|
+
raise TypeError, "header values must be strings or arrays of strings"
|
|
140
|
+
end
|
|
141
|
+
|
|
142
|
+
raise ValidationError, "header values must not be empty" if raw_values.empty?
|
|
143
|
+
|
|
144
|
+
raw_values.map do |item|
|
|
145
|
+
raise TypeError, "header values must contain only strings" unless item.is_a?(String)
|
|
146
|
+
|
|
147
|
+
normalized = item.strip
|
|
148
|
+
raise ValidationError, "header values must not contain blank strings" if normalized.empty?
|
|
149
|
+
raise ValidationError, "header values must not contain CR or LF" if contains_cr_or_lf?(normalized)
|
|
150
|
+
|
|
151
|
+
normalized
|
|
152
|
+
end
|
|
153
|
+
end
|
|
154
|
+
|
|
155
|
+
def contains_cr_or_lf?(value)
|
|
156
|
+
value.include?("\r") || value.include?("\n")
|
|
157
|
+
end
|
|
158
|
+
end
|
|
159
|
+
|
|
160
|
+
class << BulkLookupIpGeolocationRequest
|
|
161
|
+
private
|
|
162
|
+
|
|
163
|
+
def normalize_optional_string(value, field)
|
|
164
|
+
LookupIpGeolocationRequest.send(:normalize_optional_string, value, field)
|
|
165
|
+
end
|
|
166
|
+
|
|
167
|
+
def normalize_tokens(values, field)
|
|
168
|
+
LookupIpGeolocationRequest.send(:normalize_tokens, values, field)
|
|
169
|
+
end
|
|
170
|
+
|
|
171
|
+
def normalize_headers(value)
|
|
172
|
+
LookupIpGeolocationRequest.send(:normalize_headers, value)
|
|
173
|
+
end
|
|
174
|
+
end
|
|
175
|
+
end
|
|
@@ -0,0 +1,433 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "cgi"
|
|
4
|
+
require "json"
|
|
5
|
+
|
|
6
|
+
module IpgeolocationSdk
|
|
7
|
+
module Serde
|
|
8
|
+
module_function
|
|
9
|
+
|
|
10
|
+
def parse_single_lookup(body)
|
|
11
|
+
payload = parse_json(body, "Failed to deserialize API response")
|
|
12
|
+
raise SerializationError, "Failed to deserialize API response" unless payload.is_a?(Hash)
|
|
13
|
+
|
|
14
|
+
parse_ip_geolocation_response(payload)
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
def parse_bulk_lookup(body)
|
|
18
|
+
payload = parse_json(body, "Failed to deserialize bulk lookup response")
|
|
19
|
+
unless payload.is_a?(Array)
|
|
20
|
+
raise SerializationError, "Failed to deserialize bulk response: expected an array payload"
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
payload.map do |item|
|
|
24
|
+
if bulk_error_item?(item)
|
|
25
|
+
BulkLookupError.new(
|
|
26
|
+
error: BulkLookupErrorDetails.new(message: string_or_nil(item["message"]))
|
|
27
|
+
)
|
|
28
|
+
else
|
|
29
|
+
raise SerializationError, "Failed to deserialize API response" unless item.is_a?(Hash)
|
|
30
|
+
|
|
31
|
+
BulkLookupSuccess.new(data: parse_ip_geolocation_response(item))
|
|
32
|
+
end
|
|
33
|
+
end.freeze
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
def build_query(params)
|
|
37
|
+
items = params.each_with_object([]) do |(key, value), result|
|
|
38
|
+
next if key.nil? || value.nil?
|
|
39
|
+
|
|
40
|
+
key_text = key.to_s.strip
|
|
41
|
+
value_text = value.to_s.strip
|
|
42
|
+
next if key_text.empty? || value_text.empty?
|
|
43
|
+
|
|
44
|
+
result << "#{CGI.escape(key_text)}=#{CGI.escape(value_text)}"
|
|
45
|
+
end
|
|
46
|
+
|
|
47
|
+
items.empty? ? "" : "?#{items.join('&')}"
|
|
48
|
+
end
|
|
49
|
+
|
|
50
|
+
def merge_headers(*header_maps)
|
|
51
|
+
merged = {}
|
|
52
|
+
names_by_lower = {}
|
|
53
|
+
|
|
54
|
+
header_maps.compact.each do |header_map|
|
|
55
|
+
header_map.each do |raw_name, raw_values|
|
|
56
|
+
next if raw_name.nil?
|
|
57
|
+
|
|
58
|
+
name = raw_name.to_s.strip
|
|
59
|
+
next if name.empty?
|
|
60
|
+
|
|
61
|
+
existing_name = names_by_lower[name.downcase]
|
|
62
|
+
merged.delete(existing_name) if existing_name && existing_name != name
|
|
63
|
+
names_by_lower[name.downcase] = name
|
|
64
|
+
merged[name] = Array(raw_values).map(&:to_s).freeze
|
|
65
|
+
end
|
|
66
|
+
end
|
|
67
|
+
|
|
68
|
+
merged.freeze
|
|
69
|
+
end
|
|
70
|
+
|
|
71
|
+
def resolve_user_agent_header(request_user_agent, request_headers, default_user_agent)
|
|
72
|
+
return [request_user_agent].freeze unless request_user_agent.nil?
|
|
73
|
+
|
|
74
|
+
custom = first_header_ignore_case(request_headers, "User-Agent")
|
|
75
|
+
return [custom].freeze unless custom.nil?
|
|
76
|
+
|
|
77
|
+
[default_user_agent].freeze
|
|
78
|
+
end
|
|
79
|
+
|
|
80
|
+
def validate_json_output(output)
|
|
81
|
+
return unless ResponseFormat.normalize(output) == ResponseFormat::XML
|
|
82
|
+
|
|
83
|
+
raise ValidationError, "XML output is not supported by typed methods. Use ResponseFormat::JSON."
|
|
84
|
+
end
|
|
85
|
+
|
|
86
|
+
def to_api_error(status_code, body)
|
|
87
|
+
api_message = extract_api_message(body)
|
|
88
|
+
message =
|
|
89
|
+
if api_message.nil?
|
|
90
|
+
"API request failed with HTTP status #{status_code}"
|
|
91
|
+
else
|
|
92
|
+
"API request failed with HTTP status #{status_code}: #{api_message}"
|
|
93
|
+
end
|
|
94
|
+
|
|
95
|
+
klass =
|
|
96
|
+
case status_code
|
|
97
|
+
when 400 then BadRequestError
|
|
98
|
+
when 401 then UnauthorizedError
|
|
99
|
+
when 404 then NotFoundError
|
|
100
|
+
when 405 then MethodNotAllowedError
|
|
101
|
+
when 413 then PayloadTooLargeError
|
|
102
|
+
when 415 then UnsupportedMediaTypeError
|
|
103
|
+
when 423 then LockedError
|
|
104
|
+
when 429 then RateLimitError
|
|
105
|
+
when 499 then ClientClosedRequestError
|
|
106
|
+
else
|
|
107
|
+
status_code.between?(500, 599) ? ServerError : ApiError
|
|
108
|
+
end
|
|
109
|
+
|
|
110
|
+
klass.new(message, status_code, api_message)
|
|
111
|
+
end
|
|
112
|
+
|
|
113
|
+
def extract_api_message(body)
|
|
114
|
+
return nil if body.nil?
|
|
115
|
+
|
|
116
|
+
normalized = body.to_s.strip
|
|
117
|
+
return nil if normalized.empty?
|
|
118
|
+
|
|
119
|
+
payload = JSON.parse(normalized)
|
|
120
|
+
return normalized[0, 512] unless payload.is_a?(Hash)
|
|
121
|
+
|
|
122
|
+
message = payload["message"]
|
|
123
|
+
if message.nil? && payload["error"].is_a?(Hash)
|
|
124
|
+
message = payload["error"]["message"]
|
|
125
|
+
end
|
|
126
|
+
string_or_nil(message) || normalized[0, 512]
|
|
127
|
+
rescue JSON::ParserError
|
|
128
|
+
normalized[0, 512]
|
|
129
|
+
end
|
|
130
|
+
|
|
131
|
+
def parse_int_header(value)
|
|
132
|
+
return nil if value.nil?
|
|
133
|
+
|
|
134
|
+
normalized = value.to_s.strip
|
|
135
|
+
return nil unless normalized.match?(/\A\d+\z/)
|
|
136
|
+
|
|
137
|
+
Integer(normalized, 10)
|
|
138
|
+
end
|
|
139
|
+
|
|
140
|
+
def first_header_ignore_case(headers, name)
|
|
141
|
+
headers.each do |key, values|
|
|
142
|
+
return values.first if key.casecmp?(name)
|
|
143
|
+
end
|
|
144
|
+
nil
|
|
145
|
+
end
|
|
146
|
+
|
|
147
|
+
def to_metadata(status_code, duration_ms, raw_headers)
|
|
148
|
+
headers = raw_headers || {}.freeze
|
|
149
|
+
ApiResponseMetadata.new(
|
|
150
|
+
credits_charged: parse_int_header(first_header_ignore_case(headers, "X-Credits-Charged")),
|
|
151
|
+
successful_records: parse_int_header(first_header_ignore_case(headers, "X-Successful-Record")),
|
|
152
|
+
status_code: status_code,
|
|
153
|
+
duration_ms: duration_ms,
|
|
154
|
+
raw_headers: headers
|
|
155
|
+
)
|
|
156
|
+
end
|
|
157
|
+
|
|
158
|
+
def to_plain_data(value, mode = JsonOutputMode::COMPACT)
|
|
159
|
+
normalized_mode = JsonOutputMode.normalize(mode)
|
|
160
|
+
|
|
161
|
+
case value
|
|
162
|
+
when ValueObject
|
|
163
|
+
value.class.attribute_names.each_with_object({}) do |name, result|
|
|
164
|
+
item = value.public_send(name)
|
|
165
|
+
next if normalized_mode == JsonOutputMode::COMPACT && item.nil?
|
|
166
|
+
|
|
167
|
+
result[name.to_s] = to_plain_data(item, normalized_mode)
|
|
168
|
+
end
|
|
169
|
+
when Array
|
|
170
|
+
value.map { |item| to_plain_data(item, normalized_mode) }
|
|
171
|
+
when Hash
|
|
172
|
+
value.each_with_object({}) do |(key, item), result|
|
|
173
|
+
next if normalized_mode == JsonOutputMode::COMPACT && item.nil?
|
|
174
|
+
|
|
175
|
+
result[key] = to_plain_data(item, normalized_mode)
|
|
176
|
+
end
|
|
177
|
+
else
|
|
178
|
+
value
|
|
179
|
+
end
|
|
180
|
+
end
|
|
181
|
+
|
|
182
|
+
def parse_ip_geolocation_response(node)
|
|
183
|
+
IpGeolocationResponse.new(
|
|
184
|
+
ip: string_or_nil(node["ip"]),
|
|
185
|
+
domain: string_or_nil(node["domain"]),
|
|
186
|
+
hostname: string_or_nil(node["hostname"]),
|
|
187
|
+
location: parse_optional_object(node["location"]) { |item| parse_location(item) },
|
|
188
|
+
country_metadata: parse_optional_object(node["country_metadata"]) { |item| parse_country_metadata(item) },
|
|
189
|
+
network: parse_optional_object(node["network"]) { |item| parse_network(item) },
|
|
190
|
+
currency: parse_optional_object(node["currency"]) { |item| parse_currency(item) },
|
|
191
|
+
asn: parse_optional_object(node["asn"]) { |item| parse_asn(item) },
|
|
192
|
+
company: parse_optional_object(node["company"]) { |item| parse_company(item) },
|
|
193
|
+
security: parse_optional_object(node["security"]) { |item| parse_security(item) },
|
|
194
|
+
abuse: parse_optional_object(node["abuse"]) { |item| parse_abuse(item) },
|
|
195
|
+
time_zone: parse_optional_object(node["time_zone"]) { |item| parse_time_zone_info(item) },
|
|
196
|
+
user_agent: parse_optional_object(node["user_agent"]) { |item| parse_user_agent(item) }
|
|
197
|
+
)
|
|
198
|
+
end
|
|
199
|
+
|
|
200
|
+
def parse_abuse(node)
|
|
201
|
+
Abuse.new(
|
|
202
|
+
route: string_or_nil(node["route"]),
|
|
203
|
+
country: string_or_nil(node["country"]),
|
|
204
|
+
name: string_or_nil(node["name"]),
|
|
205
|
+
organization: string_or_nil(node["organization"]),
|
|
206
|
+
kind: string_or_nil(node["kind"]),
|
|
207
|
+
address: string_or_nil(node["address"]),
|
|
208
|
+
emails: string_array_or_nil(node["emails"]),
|
|
209
|
+
phone_numbers: string_array_or_nil(node["phone_numbers"])
|
|
210
|
+
)
|
|
211
|
+
end
|
|
212
|
+
|
|
213
|
+
def parse_asn(node)
|
|
214
|
+
Asn.new(
|
|
215
|
+
as_number: string_or_nil(node["as_number"]),
|
|
216
|
+
organization: string_or_nil(node["organization"]),
|
|
217
|
+
country: string_or_nil(node["country"]),
|
|
218
|
+
type: string_or_nil(node["type"]),
|
|
219
|
+
domain: string_or_nil(node["domain"]),
|
|
220
|
+
date_allocated: string_or_nil(node["date_allocated"]),
|
|
221
|
+
rir: string_or_nil(node["rir"])
|
|
222
|
+
)
|
|
223
|
+
end
|
|
224
|
+
|
|
225
|
+
def parse_company(node)
|
|
226
|
+
Company.new(
|
|
227
|
+
name: string_or_nil(node["name"]),
|
|
228
|
+
type: string_or_nil(node["type"]),
|
|
229
|
+
domain: string_or_nil(node["domain"])
|
|
230
|
+
)
|
|
231
|
+
end
|
|
232
|
+
|
|
233
|
+
def parse_country_metadata(node)
|
|
234
|
+
CountryMetadata.new(
|
|
235
|
+
calling_code: string_or_nil(node["calling_code"]),
|
|
236
|
+
tld: string_or_nil(node["tld"]),
|
|
237
|
+
languages: string_array_or_nil(node["languages"])
|
|
238
|
+
)
|
|
239
|
+
end
|
|
240
|
+
|
|
241
|
+
def parse_currency(node)
|
|
242
|
+
Currency.new(
|
|
243
|
+
code: string_or_nil(node["code"]),
|
|
244
|
+
name: string_or_nil(node["name"]),
|
|
245
|
+
symbol: string_or_nil(node["symbol"])
|
|
246
|
+
)
|
|
247
|
+
end
|
|
248
|
+
|
|
249
|
+
def parse_dst_transition(node)
|
|
250
|
+
DstTransition.new(
|
|
251
|
+
utc_time: string_or_nil(node["utc_time"]),
|
|
252
|
+
duration: string_or_nil(node["duration"]),
|
|
253
|
+
gap: boolean_or_nil(node["gap"]),
|
|
254
|
+
date_time_after: string_or_nil(node["date_time_after"]),
|
|
255
|
+
date_time_before: string_or_nil(node["date_time_before"]),
|
|
256
|
+
overlap: boolean_or_nil(node["overlap"])
|
|
257
|
+
)
|
|
258
|
+
end
|
|
259
|
+
|
|
260
|
+
def parse_location(node)
|
|
261
|
+
Location.new(
|
|
262
|
+
continent_code: string_or_nil(node["continent_code"]),
|
|
263
|
+
continent_name: string_or_nil(node["continent_name"]),
|
|
264
|
+
country_code2: string_or_nil(node["country_code2"]),
|
|
265
|
+
country_code3: string_or_nil(node["country_code3"]),
|
|
266
|
+
country_name: string_or_nil(node["country_name"]),
|
|
267
|
+
country_name_official: string_or_nil(node["country_name_official"]),
|
|
268
|
+
country_capital: string_or_nil(node["country_capital"]),
|
|
269
|
+
state_prov: string_or_nil(node["state_prov"]),
|
|
270
|
+
state_code: string_or_nil(node["state_code"]),
|
|
271
|
+
district: string_or_nil(node["district"]),
|
|
272
|
+
city: string_or_nil(node["city"]),
|
|
273
|
+
locality: string_or_nil(node["locality"]),
|
|
274
|
+
accuracy_radius: string_or_nil(node["accuracy_radius"]),
|
|
275
|
+
confidence: LocationConfidence.normalize(node["confidence"]),
|
|
276
|
+
dma_code: string_or_nil(node["dma_code"]),
|
|
277
|
+
zipcode: string_or_nil(node["zipcode"]),
|
|
278
|
+
latitude: string_or_nil(node["latitude"]),
|
|
279
|
+
longitude: string_or_nil(node["longitude"]),
|
|
280
|
+
is_eu: boolean_or_nil(node["is_eu"]),
|
|
281
|
+
country_flag: string_or_nil(node["country_flag"]),
|
|
282
|
+
geoname_id: string_or_nil(node["geoname_id"]),
|
|
283
|
+
country_emoji: string_or_nil(node["country_emoji"])
|
|
284
|
+
)
|
|
285
|
+
end
|
|
286
|
+
|
|
287
|
+
def parse_network(node)
|
|
288
|
+
Network.new(
|
|
289
|
+
connection_type: string_or_nil(node["connection_type"]),
|
|
290
|
+
route: string_or_nil(node["route"]),
|
|
291
|
+
is_anycast: boolean_or_nil(node["is_anycast"])
|
|
292
|
+
)
|
|
293
|
+
end
|
|
294
|
+
|
|
295
|
+
def parse_security(node)
|
|
296
|
+
Security.new(
|
|
297
|
+
threat_score: numeric_or_nil(node["threat_score"]),
|
|
298
|
+
is_tor: boolean_or_nil(node["is_tor"]),
|
|
299
|
+
is_proxy: boolean_or_nil(node["is_proxy"]),
|
|
300
|
+
proxy_provider_names: string_array_or_nil(node["proxy_provider_names"]),
|
|
301
|
+
proxy_confidence_score: numeric_or_nil(node["proxy_confidence_score"]),
|
|
302
|
+
proxy_last_seen: string_or_nil(node["proxy_last_seen"]),
|
|
303
|
+
is_residential_proxy: boolean_or_nil(node["is_residential_proxy"]),
|
|
304
|
+
is_vpn: boolean_or_nil(node["is_vpn"]),
|
|
305
|
+
vpn_provider_names: string_array_or_nil(node["vpn_provider_names"]),
|
|
306
|
+
vpn_confidence_score: numeric_or_nil(node["vpn_confidence_score"]),
|
|
307
|
+
vpn_last_seen: string_or_nil(node["vpn_last_seen"]),
|
|
308
|
+
is_relay: boolean_or_nil(node["is_relay"]),
|
|
309
|
+
relay_provider_name: string_or_nil(node["relay_provider_name"]),
|
|
310
|
+
is_anonymous: boolean_or_nil(node["is_anonymous"]),
|
|
311
|
+
is_known_attacker: boolean_or_nil(node["is_known_attacker"]),
|
|
312
|
+
is_bot: boolean_or_nil(node["is_bot"]),
|
|
313
|
+
is_spam: boolean_or_nil(node["is_spam"]),
|
|
314
|
+
is_cloud_provider: boolean_or_nil(node["is_cloud_provider"]),
|
|
315
|
+
cloud_provider_name: string_or_nil(node["cloud_provider_name"])
|
|
316
|
+
)
|
|
317
|
+
end
|
|
318
|
+
|
|
319
|
+
def parse_time_zone_info(node)
|
|
320
|
+
TimeZoneInfo.new(
|
|
321
|
+
name: string_or_nil(node["name"]),
|
|
322
|
+
offset: numeric_or_nil(node["offset"]),
|
|
323
|
+
offset_with_dst: numeric_or_nil(node["offset_with_dst"]),
|
|
324
|
+
current_time: string_or_nil(node["current_time"]),
|
|
325
|
+
current_time_unix: numeric_or_nil(node["current_time_unix"]),
|
|
326
|
+
current_tz_abbreviation: string_or_nil(node["current_tz_abbreviation"]),
|
|
327
|
+
current_tz_full_name: string_or_nil(node["current_tz_full_name"]),
|
|
328
|
+
standard_tz_abbreviation: string_or_nil(node["standard_tz_abbreviation"]),
|
|
329
|
+
standard_tz_full_name: string_or_nil(node["standard_tz_full_name"]),
|
|
330
|
+
is_dst: boolean_or_nil(node["is_dst"]),
|
|
331
|
+
dst_savings: numeric_or_nil(node["dst_savings"]),
|
|
332
|
+
dst_exists: boolean_or_nil(node["dst_exists"]),
|
|
333
|
+
dst_tz_abbreviation: string_or_nil(node["dst_tz_abbreviation"]),
|
|
334
|
+
dst_tz_full_name: string_or_nil(node["dst_tz_full_name"]),
|
|
335
|
+
dst_start: parse_optional_object(node["dst_start"]) { |item| parse_dst_transition(item) },
|
|
336
|
+
dst_end: parse_optional_object(node["dst_end"]) { |item| parse_dst_transition(item) }
|
|
337
|
+
)
|
|
338
|
+
end
|
|
339
|
+
|
|
340
|
+
def parse_user_agent(node)
|
|
341
|
+
UserAgent.new(
|
|
342
|
+
user_agent_string: string_or_nil(node["user_agent_string"]),
|
|
343
|
+
name: string_or_nil(node["name"]),
|
|
344
|
+
type: string_or_nil(node["type"]),
|
|
345
|
+
version: string_or_nil(node["version"]),
|
|
346
|
+
version_major: string_or_nil(node["version_major"]),
|
|
347
|
+
device: parse_optional_object(node["device"]) { |item| parse_user_agent_device(item) },
|
|
348
|
+
engine: parse_optional_object(node["engine"]) { |item| parse_user_agent_engine(item) },
|
|
349
|
+
operating_system: parse_optional_object(node["operating_system"]) { |item| parse_user_agent_operating_system(item) }
|
|
350
|
+
)
|
|
351
|
+
end
|
|
352
|
+
|
|
353
|
+
def parse_user_agent_device(node)
|
|
354
|
+
UserAgentDevice.new(
|
|
355
|
+
name: string_or_nil(node["name"]),
|
|
356
|
+
type: string_or_nil(node["type"]),
|
|
357
|
+
brand: string_or_nil(node["brand"]),
|
|
358
|
+
cpu: string_or_nil(node["cpu"])
|
|
359
|
+
)
|
|
360
|
+
end
|
|
361
|
+
|
|
362
|
+
def parse_user_agent_engine(node)
|
|
363
|
+
UserAgentEngine.new(
|
|
364
|
+
name: string_or_nil(node["name"]),
|
|
365
|
+
type: string_or_nil(node["type"]),
|
|
366
|
+
version: string_or_nil(node["version"]),
|
|
367
|
+
version_major: string_or_nil(node["version_major"])
|
|
368
|
+
)
|
|
369
|
+
end
|
|
370
|
+
|
|
371
|
+
def parse_user_agent_operating_system(node)
|
|
372
|
+
UserAgentOperatingSystem.new(
|
|
373
|
+
name: string_or_nil(node["name"]),
|
|
374
|
+
type: string_or_nil(node["type"]),
|
|
375
|
+
version: string_or_nil(node["version"]),
|
|
376
|
+
version_major: string_or_nil(node["version_major"]),
|
|
377
|
+
build: string_or_nil(node["build"])
|
|
378
|
+
)
|
|
379
|
+
end
|
|
380
|
+
|
|
381
|
+
def parse_optional_object(value)
|
|
382
|
+
return nil if value.nil?
|
|
383
|
+
raise SerializationError, "Failed to deserialize API response" unless value.is_a?(Hash)
|
|
384
|
+
|
|
385
|
+
yield(value)
|
|
386
|
+
end
|
|
387
|
+
|
|
388
|
+
def string_or_nil(value)
|
|
389
|
+
return nil if value.nil?
|
|
390
|
+
raise SerializationError, "Failed to deserialize API response" unless value.is_a?(String)
|
|
391
|
+
|
|
392
|
+
value
|
|
393
|
+
end
|
|
394
|
+
|
|
395
|
+
def numeric_or_nil(value)
|
|
396
|
+
return nil if value.nil?
|
|
397
|
+
raise SerializationError, "Failed to deserialize API response" unless value.is_a?(Numeric)
|
|
398
|
+
|
|
399
|
+
value
|
|
400
|
+
end
|
|
401
|
+
|
|
402
|
+
def boolean_or_nil(value)
|
|
403
|
+
return nil if value.nil?
|
|
404
|
+
raise SerializationError, "Failed to deserialize API response" unless value == true || value == false
|
|
405
|
+
|
|
406
|
+
value
|
|
407
|
+
end
|
|
408
|
+
|
|
409
|
+
def string_array_or_nil(value)
|
|
410
|
+
return nil if value.nil?
|
|
411
|
+
raise SerializationError, "Failed to deserialize API response" unless value.is_a?(Array)
|
|
412
|
+
|
|
413
|
+
value.map do |item|
|
|
414
|
+
raise SerializationError, "Failed to deserialize API response" unless item.is_a?(String)
|
|
415
|
+
|
|
416
|
+
item
|
|
417
|
+
end.freeze
|
|
418
|
+
end
|
|
419
|
+
|
|
420
|
+
def parse_json(body, message)
|
|
421
|
+
JSON.parse(body)
|
|
422
|
+
rescue JSON::ParserError, TypeError => error
|
|
423
|
+
raise SerializationError.new(message, cause: error)
|
|
424
|
+
end
|
|
425
|
+
|
|
426
|
+
def bulk_error_item?(item)
|
|
427
|
+
return false unless item.is_a?(Hash)
|
|
428
|
+
return false if item["success"] == true
|
|
429
|
+
|
|
430
|
+
item["success"] == false || item.key?("message")
|
|
431
|
+
end
|
|
432
|
+
end
|
|
433
|
+
end
|