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,223 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "time"
|
|
4
|
+
|
|
5
|
+
module IpgeolocationSdk
|
|
6
|
+
class IpGeolocationClient
|
|
7
|
+
def self.default_user_agent
|
|
8
|
+
"ipgeolocation-ruby-sdk/#{VERSION}"
|
|
9
|
+
end
|
|
10
|
+
|
|
11
|
+
def initialize(config, transport = nil)
|
|
12
|
+
raise TypeError, "config must not be nil" if config.nil?
|
|
13
|
+
|
|
14
|
+
@config =
|
|
15
|
+
case config
|
|
16
|
+
when IpGeolocationClientConfig
|
|
17
|
+
config
|
|
18
|
+
when Hash
|
|
19
|
+
IpGeolocationClientConfig.new(**config)
|
|
20
|
+
else
|
|
21
|
+
raise TypeError, "config must be an IpGeolocationClientConfig or hash"
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
@transport = transport || NetHttpTransport.new
|
|
25
|
+
@owns_transport = transport.nil?
|
|
26
|
+
@closed = false
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
attr_reader :config
|
|
30
|
+
|
|
31
|
+
def lookup_ip_geolocation(request = nil)
|
|
32
|
+
ensure_open
|
|
33
|
+
typed_request = to_lookup_request(request)
|
|
34
|
+
validate_lookup_request_base
|
|
35
|
+
Serde.validate_json_output(typed_request.output)
|
|
36
|
+
|
|
37
|
+
response, duration_ms = execute_with_metrics(build_lookup_http_request(typed_request))
|
|
38
|
+
raise Serde.to_api_error(response.status_code, response.body) unless success_status?(response.status_code)
|
|
39
|
+
|
|
40
|
+
ApiResponse.new(
|
|
41
|
+
data: Serde.parse_single_lookup(response.body),
|
|
42
|
+
metadata: Serde.to_metadata(response.status_code, duration_ms, response.headers)
|
|
43
|
+
)
|
|
44
|
+
end
|
|
45
|
+
|
|
46
|
+
def lookup_ip_geolocation_raw(request = nil)
|
|
47
|
+
ensure_open
|
|
48
|
+
typed_request = to_lookup_request(request)
|
|
49
|
+
validate_lookup_request_base
|
|
50
|
+
|
|
51
|
+
response, duration_ms = execute_with_metrics(build_lookup_http_request(typed_request))
|
|
52
|
+
raise Serde.to_api_error(response.status_code, response.body) unless success_status?(response.status_code)
|
|
53
|
+
|
|
54
|
+
ApiResponse.new(
|
|
55
|
+
data: response.body,
|
|
56
|
+
metadata: Serde.to_metadata(response.status_code, duration_ms, response.headers)
|
|
57
|
+
)
|
|
58
|
+
end
|
|
59
|
+
|
|
60
|
+
def bulk_lookup_ip_geolocation(request)
|
|
61
|
+
ensure_open
|
|
62
|
+
typed_request = to_bulk_request(request)
|
|
63
|
+
validate_bulk_request_base
|
|
64
|
+
Serde.validate_json_output(typed_request.output)
|
|
65
|
+
|
|
66
|
+
response, duration_ms = execute_with_metrics(build_bulk_http_request(typed_request))
|
|
67
|
+
raise Serde.to_api_error(response.status_code, response.body) unless success_status?(response.status_code)
|
|
68
|
+
|
|
69
|
+
ApiResponse.new(
|
|
70
|
+
data: Serde.parse_bulk_lookup(response.body),
|
|
71
|
+
metadata: Serde.to_metadata(response.status_code, duration_ms, response.headers)
|
|
72
|
+
)
|
|
73
|
+
end
|
|
74
|
+
|
|
75
|
+
def bulk_lookup_ip_geolocation_raw(request)
|
|
76
|
+
ensure_open
|
|
77
|
+
typed_request = to_bulk_request(request)
|
|
78
|
+
validate_bulk_request_base
|
|
79
|
+
|
|
80
|
+
response, duration_ms = execute_with_metrics(build_bulk_http_request(typed_request))
|
|
81
|
+
raise Serde.to_api_error(response.status_code, response.body) unless success_status?(response.status_code)
|
|
82
|
+
|
|
83
|
+
ApiResponse.new(
|
|
84
|
+
data: response.body,
|
|
85
|
+
metadata: Serde.to_metadata(response.status_code, duration_ms, response.headers)
|
|
86
|
+
)
|
|
87
|
+
end
|
|
88
|
+
|
|
89
|
+
def close
|
|
90
|
+
return if @closed
|
|
91
|
+
|
|
92
|
+
@closed = true
|
|
93
|
+
@transport.close if @owns_transport && @transport.respond_to?(:close)
|
|
94
|
+
nil
|
|
95
|
+
end
|
|
96
|
+
|
|
97
|
+
private
|
|
98
|
+
|
|
99
|
+
def ensure_open
|
|
100
|
+
raise ValidationError, "client is closed" if @closed
|
|
101
|
+
end
|
|
102
|
+
|
|
103
|
+
def validate_lookup_request_base
|
|
104
|
+
return unless @config.api_key.nil? && @config.request_origin.nil?
|
|
105
|
+
|
|
106
|
+
raise ValidationError, "single lookup requires api_key or request_origin in client config"
|
|
107
|
+
end
|
|
108
|
+
|
|
109
|
+
def validate_bulk_request_base
|
|
110
|
+
raise ValidationError, "bulk lookup requires api_key in client config" if @config.api_key.nil?
|
|
111
|
+
end
|
|
112
|
+
|
|
113
|
+
def to_lookup_request(request)
|
|
114
|
+
case request
|
|
115
|
+
when nil
|
|
116
|
+
LookupIpGeolocationRequest.new
|
|
117
|
+
when LookupIpGeolocationRequest
|
|
118
|
+
request
|
|
119
|
+
when Hash
|
|
120
|
+
LookupIpGeolocationRequest.new(**request)
|
|
121
|
+
else
|
|
122
|
+
raise TypeError, "request must be a LookupIpGeolocationRequest, hash, or nil"
|
|
123
|
+
end
|
|
124
|
+
end
|
|
125
|
+
|
|
126
|
+
def to_bulk_request(request)
|
|
127
|
+
case request
|
|
128
|
+
when BulkLookupIpGeolocationRequest
|
|
129
|
+
request
|
|
130
|
+
when Hash
|
|
131
|
+
BulkLookupIpGeolocationRequest.new(**request)
|
|
132
|
+
when nil
|
|
133
|
+
raise ValidationError, "request must not be nil"
|
|
134
|
+
else
|
|
135
|
+
raise TypeError, "request must be a BulkLookupIpGeolocationRequest or hash"
|
|
136
|
+
end
|
|
137
|
+
end
|
|
138
|
+
|
|
139
|
+
def build_lookup_http_request(request)
|
|
140
|
+
output = request.output || ResponseFormat::JSON
|
|
141
|
+
query = Serde.build_query(
|
|
142
|
+
"apiKey" => @config.api_key,
|
|
143
|
+
"ip" => request.ip,
|
|
144
|
+
"lang" => request.lang,
|
|
145
|
+
"include" => request.include.empty? ? nil : request.include.join(","),
|
|
146
|
+
"fields" => request.fields.empty? ? nil : request.fields.join(","),
|
|
147
|
+
"excludes" => request.excludes.empty? ? nil : request.excludes.join(","),
|
|
148
|
+
"output" => output
|
|
149
|
+
)
|
|
150
|
+
|
|
151
|
+
headers = Serde.merge_headers(
|
|
152
|
+
request.headers,
|
|
153
|
+
@config.request_origin.nil? ? nil : { "Origin" => [@config.request_origin].freeze },
|
|
154
|
+
{
|
|
155
|
+
"User-Agent" => Serde.resolve_user_agent_header(
|
|
156
|
+
request.user_agent,
|
|
157
|
+
request.headers,
|
|
158
|
+
self.class.default_user_agent
|
|
159
|
+
),
|
|
160
|
+
"Accept" => [output == ResponseFormat::XML ? "application/xml" : "application/json"].freeze
|
|
161
|
+
}.freeze
|
|
162
|
+
)
|
|
163
|
+
|
|
164
|
+
HttpRequestData.new(
|
|
165
|
+
url: "#{@config.base_url}/v3/ipgeo#{query}",
|
|
166
|
+
method: "GET",
|
|
167
|
+
headers: headers,
|
|
168
|
+
body: nil,
|
|
169
|
+
connect_timeout: @config.connect_timeout,
|
|
170
|
+
read_timeout: @config.read_timeout
|
|
171
|
+
)
|
|
172
|
+
end
|
|
173
|
+
|
|
174
|
+
def build_bulk_http_request(request)
|
|
175
|
+
output = request.output || ResponseFormat::JSON
|
|
176
|
+
query = Serde.build_query(
|
|
177
|
+
"apiKey" => @config.api_key,
|
|
178
|
+
"lang" => request.lang,
|
|
179
|
+
"include" => request.include.empty? ? nil : request.include.join(","),
|
|
180
|
+
"fields" => request.fields.empty? ? nil : request.fields.join(","),
|
|
181
|
+
"excludes" => request.excludes.empty? ? nil : request.excludes.join(","),
|
|
182
|
+
"output" => output
|
|
183
|
+
)
|
|
184
|
+
|
|
185
|
+
payload = JSON.generate({ ips: request.ips })
|
|
186
|
+
headers = Serde.merge_headers(
|
|
187
|
+
request.headers,
|
|
188
|
+
@config.request_origin.nil? ? nil : { "Origin" => [@config.request_origin].freeze },
|
|
189
|
+
{
|
|
190
|
+
"User-Agent" => Serde.resolve_user_agent_header(
|
|
191
|
+
request.user_agent,
|
|
192
|
+
request.headers,
|
|
193
|
+
self.class.default_user_agent
|
|
194
|
+
),
|
|
195
|
+
"Accept" => [output == ResponseFormat::XML ? "application/xml" : "application/json"].freeze,
|
|
196
|
+
"Content-Type" => ["application/json"].freeze
|
|
197
|
+
}.freeze
|
|
198
|
+
)
|
|
199
|
+
|
|
200
|
+
HttpRequestData.new(
|
|
201
|
+
url: "#{@config.base_url}/v3/ipgeo-bulk#{query}",
|
|
202
|
+
method: "POST",
|
|
203
|
+
headers: headers,
|
|
204
|
+
body: payload,
|
|
205
|
+
connect_timeout: @config.connect_timeout,
|
|
206
|
+
read_timeout: @config.read_timeout
|
|
207
|
+
)
|
|
208
|
+
rescue JSON::GeneratorError => error
|
|
209
|
+
raise SerializationError.new("Failed to serialize bulk lookup request body", cause: error)
|
|
210
|
+
end
|
|
211
|
+
|
|
212
|
+
def execute_with_metrics(request)
|
|
213
|
+
started_at = Process.clock_gettime(Process::CLOCK_MONOTONIC)
|
|
214
|
+
response = @transport.send_request(request)
|
|
215
|
+
duration_ms = ((Process.clock_gettime(Process::CLOCK_MONOTONIC) - started_at) * 1000).floor
|
|
216
|
+
[response, [duration_ms, 0].max]
|
|
217
|
+
end
|
|
218
|
+
|
|
219
|
+
def success_status?(status_code)
|
|
220
|
+
status_code / 100 == 2
|
|
221
|
+
end
|
|
222
|
+
end
|
|
223
|
+
end
|
|
@@ -0,0 +1,102 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "uri"
|
|
4
|
+
|
|
5
|
+
module IpgeolocationSdk
|
|
6
|
+
class IpGeolocationClientConfig < ValueObject
|
|
7
|
+
DEFAULT_BASE_URL = "https://api.ipgeolocation.io"
|
|
8
|
+
DEFAULT_CONNECT_TIMEOUT = 10.0
|
|
9
|
+
DEFAULT_READ_TIMEOUT = 30.0
|
|
10
|
+
|
|
11
|
+
attributes :api_key, :request_origin, :base_url, :connect_timeout, :read_timeout
|
|
12
|
+
|
|
13
|
+
def initialize(
|
|
14
|
+
api_key: nil,
|
|
15
|
+
request_origin: nil,
|
|
16
|
+
base_url: DEFAULT_BASE_URL,
|
|
17
|
+
connect_timeout: DEFAULT_CONNECT_TIMEOUT,
|
|
18
|
+
read_timeout: DEFAULT_READ_TIMEOUT
|
|
19
|
+
)
|
|
20
|
+
super(
|
|
21
|
+
api_key: normalize_api_key(api_key),
|
|
22
|
+
request_origin: normalize_request_origin(request_origin),
|
|
23
|
+
base_url: normalize_base_url(base_url),
|
|
24
|
+
connect_timeout: normalize_timeout(connect_timeout, "connect_timeout"),
|
|
25
|
+
read_timeout: normalize_timeout(read_timeout, "read_timeout")
|
|
26
|
+
)
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
private
|
|
30
|
+
|
|
31
|
+
def normalize_api_key(value)
|
|
32
|
+
return nil if value.nil?
|
|
33
|
+
raise TypeError, "api_key must be a string" unless value.is_a?(String)
|
|
34
|
+
|
|
35
|
+
normalized = value.strip
|
|
36
|
+
raise ValidationError, "api_key must not be blank" if normalized.empty?
|
|
37
|
+
|
|
38
|
+
normalized
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
def normalize_request_origin(value)
|
|
42
|
+
return nil if value.nil?
|
|
43
|
+
raise TypeError, "request_origin must be a string" unless value.is_a?(String)
|
|
44
|
+
|
|
45
|
+
normalized = value.strip
|
|
46
|
+
raise ValidationError, "request_origin must not be blank" if normalized.empty?
|
|
47
|
+
raise ValidationError, "request_origin must not contain CR or LF" if normalized.include?("\r") || normalized.include?("\n")
|
|
48
|
+
|
|
49
|
+
uri = parse_uri(normalized, "request_origin must be an absolute http or https origin")
|
|
50
|
+
|
|
51
|
+
unless %w[http https].include?(uri.scheme) && uri.host
|
|
52
|
+
raise ValidationError, "request_origin must be an absolute http or https origin"
|
|
53
|
+
end
|
|
54
|
+
raise ValidationError, "request_origin must not include a path" unless ["", "/"].include?(uri.path.to_s)
|
|
55
|
+
raise ValidationError, "request_origin must not include params, query, or fragment" unless [nil, ""].include?(uri.query) && [nil, ""].include?(uri.fragment)
|
|
56
|
+
raise ValidationError, "request_origin must not include userinfo" unless uri.userinfo.nil?
|
|
57
|
+
|
|
58
|
+
"#{uri.scheme}://#{uri.host}#{port_suffix(uri)}"
|
|
59
|
+
end
|
|
60
|
+
|
|
61
|
+
def normalize_base_url(value)
|
|
62
|
+
raise TypeError, "base_url must be a string" unless value.is_a?(String)
|
|
63
|
+
|
|
64
|
+
normalized = value.strip.sub(%r{/+\z}, "")
|
|
65
|
+
raise ValidationError, "base_url must not be blank" if normalized.empty?
|
|
66
|
+
|
|
67
|
+
uri = parse_uri(normalized, "base_url must be an absolute http or https URL")
|
|
68
|
+
|
|
69
|
+
unless %w[http https].include?(uri.scheme) && uri.host
|
|
70
|
+
raise ValidationError, "base_url must be an absolute http or https URL"
|
|
71
|
+
end
|
|
72
|
+
raise ValidationError, "base_url must not include params, query, or fragment" unless [nil, ""].include?(uri.query) && [nil, ""].include?(uri.fragment)
|
|
73
|
+
raise ValidationError, "base_url must not include userinfo" unless uri.userinfo.nil?
|
|
74
|
+
|
|
75
|
+
normalized
|
|
76
|
+
end
|
|
77
|
+
|
|
78
|
+
def normalize_timeout(value, field)
|
|
79
|
+
raise TypeError, "#{field} must be a number" unless value.is_a?(Numeric) && !value.is_a?(TrueClass) && !value.is_a?(FalseClass)
|
|
80
|
+
|
|
81
|
+
normalized = value.to_f
|
|
82
|
+
raise ValidationError, "#{field} must be finite" unless normalized.finite?
|
|
83
|
+
raise ValidationError, "#{field} must be greater than zero" unless normalized.positive?
|
|
84
|
+
|
|
85
|
+
normalized
|
|
86
|
+
end
|
|
87
|
+
|
|
88
|
+
def parse_uri(value, message)
|
|
89
|
+
URI.parse(value)
|
|
90
|
+
rescue URI::InvalidURIError
|
|
91
|
+
raise ValidationError, message
|
|
92
|
+
end
|
|
93
|
+
|
|
94
|
+
def port_suffix(uri)
|
|
95
|
+
return "" if uri.port.nil?
|
|
96
|
+
return "" if uri.scheme == "http" && uri.port == 80
|
|
97
|
+
return "" if uri.scheme == "https" && uri.port == 443
|
|
98
|
+
|
|
99
|
+
":#{uri.port}"
|
|
100
|
+
end
|
|
101
|
+
end
|
|
102
|
+
end
|
|
@@ -0,0 +1,110 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module IpgeolocationSdk
|
|
4
|
+
module Language
|
|
5
|
+
EN = "en"
|
|
6
|
+
DE = "de"
|
|
7
|
+
RU = "ru"
|
|
8
|
+
JA = "ja"
|
|
9
|
+
FR = "fr"
|
|
10
|
+
CN = "cn"
|
|
11
|
+
ES = "es"
|
|
12
|
+
CS = "cs"
|
|
13
|
+
IT = "it"
|
|
14
|
+
KO = "ko"
|
|
15
|
+
FA = "fa"
|
|
16
|
+
PT = "pt"
|
|
17
|
+
|
|
18
|
+
VALUES = [
|
|
19
|
+
EN, DE, RU, JA, FR, CN, ES, CS, IT, KO, FA, PT
|
|
20
|
+
].freeze
|
|
21
|
+
|
|
22
|
+
module_function
|
|
23
|
+
|
|
24
|
+
def from_code(code)
|
|
25
|
+
normalized = normalize_token(code, "code")
|
|
26
|
+
value = VALUES.find { |item| item.casecmp?(normalized) }
|
|
27
|
+
raise ValidationError, "Unsupported language code: #{code}" if value.nil?
|
|
28
|
+
|
|
29
|
+
value
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
def valid?(code)
|
|
33
|
+
VALUES.any? { |item| item.casecmp?(code.to_s.strip) }
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
def normalize(value)
|
|
37
|
+
return nil if value.nil?
|
|
38
|
+
|
|
39
|
+
from_code(value)
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
def normalize_token(value, field)
|
|
43
|
+
raise TypeError, "#{field} must be a string or symbol" unless value.is_a?(String) || value.is_a?(Symbol)
|
|
44
|
+
|
|
45
|
+
normalized = value.to_s.strip
|
|
46
|
+
raise ValidationError, "#{field} must not be blank" if normalized.empty?
|
|
47
|
+
|
|
48
|
+
normalized
|
|
49
|
+
end
|
|
50
|
+
private_class_method :normalize_token
|
|
51
|
+
end
|
|
52
|
+
|
|
53
|
+
module ResponseFormat
|
|
54
|
+
JSON = "json"
|
|
55
|
+
XML = "xml"
|
|
56
|
+
|
|
57
|
+
VALUES = [JSON, XML].freeze
|
|
58
|
+
|
|
59
|
+
module_function
|
|
60
|
+
|
|
61
|
+
def normalize(value)
|
|
62
|
+
return JSON if value.nil?
|
|
63
|
+
raise TypeError, "output must be a string or symbol" unless value.is_a?(String) || value.is_a?(Symbol)
|
|
64
|
+
|
|
65
|
+
normalized = value.to_s.strip.downcase
|
|
66
|
+
return normalized if VALUES.include?(normalized)
|
|
67
|
+
|
|
68
|
+
raise ValidationError, "output must be either 'json' or 'xml'"
|
|
69
|
+
end
|
|
70
|
+
end
|
|
71
|
+
|
|
72
|
+
module JsonOutputMode
|
|
73
|
+
COMPACT = "compact"
|
|
74
|
+
FULL = "full"
|
|
75
|
+
|
|
76
|
+
VALUES = [COMPACT, FULL].freeze
|
|
77
|
+
|
|
78
|
+
module_function
|
|
79
|
+
|
|
80
|
+
def normalize(value)
|
|
81
|
+
return COMPACT if value.nil?
|
|
82
|
+
raise TypeError, "mode must be a string or symbol" unless value.is_a?(String) || value.is_a?(Symbol)
|
|
83
|
+
|
|
84
|
+
normalized = value.to_s.strip.downcase
|
|
85
|
+
return normalized if VALUES.include?(normalized)
|
|
86
|
+
|
|
87
|
+
raise ValidationError, "mode must be either 'compact' or 'full'"
|
|
88
|
+
end
|
|
89
|
+
end
|
|
90
|
+
|
|
91
|
+
module LocationConfidence
|
|
92
|
+
HIGH = "high"
|
|
93
|
+
MEDIUM = "medium"
|
|
94
|
+
LOW = "low"
|
|
95
|
+
UNKNOWN = "unknown"
|
|
96
|
+
|
|
97
|
+
VALUES = [HIGH, MEDIUM, LOW, UNKNOWN].freeze
|
|
98
|
+
|
|
99
|
+
module_function
|
|
100
|
+
|
|
101
|
+
def normalize(value)
|
|
102
|
+
return nil if value.nil?
|
|
103
|
+
|
|
104
|
+
normalized = value.to_s.strip.downcase
|
|
105
|
+
return normalized if VALUES.include?(normalized)
|
|
106
|
+
|
|
107
|
+
UNKNOWN
|
|
108
|
+
end
|
|
109
|
+
end
|
|
110
|
+
end
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module IpgeolocationSdk
|
|
4
|
+
class IpGeolocationError < StandardError
|
|
5
|
+
attr_reader :cause
|
|
6
|
+
|
|
7
|
+
def initialize(message, cause: nil)
|
|
8
|
+
super(message)
|
|
9
|
+
@cause = cause
|
|
10
|
+
end
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
class ValidationError < IpGeolocationError; end
|
|
14
|
+
class SerializationError < IpGeolocationError; end
|
|
15
|
+
class TransportError < IpGeolocationError; end
|
|
16
|
+
class RequestTimeoutError < TransportError; end
|
|
17
|
+
|
|
18
|
+
class ApiError < IpGeolocationError
|
|
19
|
+
attr_reader :status_code, :api_message
|
|
20
|
+
|
|
21
|
+
def initialize(message, status_code, api_message = nil)
|
|
22
|
+
super(message)
|
|
23
|
+
@status_code = status_code
|
|
24
|
+
@api_message = api_message
|
|
25
|
+
end
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
class BadRequestError < ApiError; end
|
|
29
|
+
class UnauthorizedError < ApiError; end
|
|
30
|
+
class NotFoundError < ApiError; end
|
|
31
|
+
class MethodNotAllowedError < ApiError; end
|
|
32
|
+
class PayloadTooLargeError < ApiError; end
|
|
33
|
+
class UnsupportedMediaTypeError < ApiError; end
|
|
34
|
+
class LockedError < ApiError; end
|
|
35
|
+
class RateLimitError < ApiError; end
|
|
36
|
+
class ClientClosedRequestError < ApiError; end
|
|
37
|
+
class ServerError < ApiError; end
|
|
38
|
+
end
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "json"
|
|
4
|
+
|
|
5
|
+
module IpgeolocationSdk
|
|
6
|
+
module JsonOutput
|
|
7
|
+
module_function
|
|
8
|
+
|
|
9
|
+
def to_json(value, mode = JsonOutputMode::COMPACT)
|
|
10
|
+
write(value, mode, pretty: false)
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
def to_pretty_json(value, mode = JsonOutputMode::COMPACT)
|
|
14
|
+
write(value, mode, pretty: true)
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
def write(value, mode, pretty:)
|
|
18
|
+
normalized_mode = JsonOutputMode.normalize(mode)
|
|
19
|
+
payload = Serde.to_plain_data(value, normalized_mode)
|
|
20
|
+
if pretty
|
|
21
|
+
JSON.pretty_generate(payload)
|
|
22
|
+
else
|
|
23
|
+
JSON.generate(payload)
|
|
24
|
+
end
|
|
25
|
+
rescue StandardError => error
|
|
26
|
+
raise SerializationError.new("Failed to serialize output as JSON", cause: error)
|
|
27
|
+
end
|
|
28
|
+
private_class_method :write
|
|
29
|
+
end
|
|
30
|
+
end
|
|
@@ -0,0 +1,186 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module IpgeolocationSdk
|
|
4
|
+
class ApiResponse < ValueObject
|
|
5
|
+
attributes :data, :metadata
|
|
6
|
+
end
|
|
7
|
+
|
|
8
|
+
class ApiResponseMetadata < ValueObject
|
|
9
|
+
attributes :credits_charged, :successful_records, :status_code, :duration_ms, :raw_headers
|
|
10
|
+
|
|
11
|
+
def initialize(
|
|
12
|
+
credits_charged: nil,
|
|
13
|
+
successful_records: nil,
|
|
14
|
+
status_code:,
|
|
15
|
+
duration_ms:,
|
|
16
|
+
raw_headers: {}
|
|
17
|
+
)
|
|
18
|
+
validate_optional_integer(credits_charged, "credits_charged")
|
|
19
|
+
validate_optional_integer(successful_records, "successful_records")
|
|
20
|
+
validate_status_code(status_code)
|
|
21
|
+
validate_duration(duration_ms)
|
|
22
|
+
|
|
23
|
+
normalized_headers = normalize_headers(raw_headers)
|
|
24
|
+
|
|
25
|
+
super(
|
|
26
|
+
credits_charged: credits_charged,
|
|
27
|
+
successful_records: successful_records,
|
|
28
|
+
status_code: status_code,
|
|
29
|
+
duration_ms: duration_ms,
|
|
30
|
+
raw_headers: normalized_headers
|
|
31
|
+
)
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
def header_values(name)
|
|
35
|
+
raise TypeError, "header name must be a string" unless name.is_a?(String)
|
|
36
|
+
|
|
37
|
+
normalized_name = name.strip
|
|
38
|
+
raise ArgumentError, "header name must not be blank" if normalized_name.empty?
|
|
39
|
+
|
|
40
|
+
raw_headers.each do |key, values|
|
|
41
|
+
return values if key.casecmp?(normalized_name)
|
|
42
|
+
end
|
|
43
|
+
|
|
44
|
+
EMPTY_HEADER_VALUES
|
|
45
|
+
end
|
|
46
|
+
|
|
47
|
+
def first_header_value(name)
|
|
48
|
+
header_values(name).first
|
|
49
|
+
end
|
|
50
|
+
|
|
51
|
+
EMPTY_HEADER_VALUES = [].freeze
|
|
52
|
+
|
|
53
|
+
private
|
|
54
|
+
|
|
55
|
+
def validate_optional_integer(value, field)
|
|
56
|
+
return if value.nil?
|
|
57
|
+
raise TypeError, "#{field} must be an integer" unless value.is_a?(Integer)
|
|
58
|
+
raise ArgumentError, "#{field} must be greater than or equal to zero" if value.negative?
|
|
59
|
+
end
|
|
60
|
+
|
|
61
|
+
def validate_status_code(value)
|
|
62
|
+
raise TypeError, "status_code must be an integer" unless value.is_a?(Integer)
|
|
63
|
+
raise ArgumentError, "status_code must be between 100 and 599" unless value.between?(100, 599)
|
|
64
|
+
end
|
|
65
|
+
|
|
66
|
+
def validate_duration(value)
|
|
67
|
+
raise TypeError, "duration_ms must be an integer" unless value.is_a?(Integer)
|
|
68
|
+
raise ArgumentError, "duration_ms must be greater than or equal to zero" if value.negative?
|
|
69
|
+
end
|
|
70
|
+
|
|
71
|
+
def normalize_headers(input)
|
|
72
|
+
raise TypeError, "raw_headers must be a hash" unless input.is_a?(Hash)
|
|
73
|
+
|
|
74
|
+
input.each_with_object({}) do |(key, values), result|
|
|
75
|
+
next if key.nil?
|
|
76
|
+
|
|
77
|
+
name = key.to_s
|
|
78
|
+
next if name.strip.empty?
|
|
79
|
+
raise TypeError, "raw_headers values must be arrays of strings" unless values.is_a?(Array)
|
|
80
|
+
normalized_values = values.map do |value|
|
|
81
|
+
raise TypeError, "raw_headers values must contain only strings" unless value.is_a?(String)
|
|
82
|
+
|
|
83
|
+
value
|
|
84
|
+
end
|
|
85
|
+
result[name] = normalized_values.freeze
|
|
86
|
+
end.freeze
|
|
87
|
+
end
|
|
88
|
+
end
|
|
89
|
+
|
|
90
|
+
class Abuse < ValueObject
|
|
91
|
+
attributes :route, :country, :name, :organization, :kind, :address, :emails, :phone_numbers
|
|
92
|
+
end
|
|
93
|
+
|
|
94
|
+
class Asn < ValueObject
|
|
95
|
+
attributes :as_number, :organization, :country, :type, :domain, :date_allocated, :rir
|
|
96
|
+
end
|
|
97
|
+
|
|
98
|
+
class Company < ValueObject
|
|
99
|
+
attributes :name, :type, :domain
|
|
100
|
+
end
|
|
101
|
+
|
|
102
|
+
class CountryMetadata < ValueObject
|
|
103
|
+
attributes :calling_code, :tld, :languages
|
|
104
|
+
end
|
|
105
|
+
|
|
106
|
+
class Currency < ValueObject
|
|
107
|
+
attributes :code, :name, :symbol
|
|
108
|
+
end
|
|
109
|
+
|
|
110
|
+
class DstTransition < ValueObject
|
|
111
|
+
attributes :utc_time, :duration, :gap, :date_time_after, :date_time_before, :overlap
|
|
112
|
+
end
|
|
113
|
+
|
|
114
|
+
class Location < ValueObject
|
|
115
|
+
attributes :continent_code, :continent_name, :country_code2, :country_code3,
|
|
116
|
+
:country_name, :country_name_official, :country_capital, :state_prov,
|
|
117
|
+
:state_code, :district, :city, :locality, :accuracy_radius, :confidence,
|
|
118
|
+
:dma_code, :zipcode, :latitude, :longitude, :is_eu, :country_flag,
|
|
119
|
+
:geoname_id, :country_emoji
|
|
120
|
+
end
|
|
121
|
+
|
|
122
|
+
class Network < ValueObject
|
|
123
|
+
attributes :connection_type, :route, :is_anycast
|
|
124
|
+
end
|
|
125
|
+
|
|
126
|
+
class Security < ValueObject
|
|
127
|
+
attributes :threat_score, :is_tor, :is_proxy, :proxy_provider_names,
|
|
128
|
+
:proxy_confidence_score, :proxy_last_seen, :is_residential_proxy,
|
|
129
|
+
:is_vpn, :vpn_provider_names, :vpn_confidence_score, :vpn_last_seen,
|
|
130
|
+
:is_relay, :relay_provider_name, :is_anonymous, :is_known_attacker,
|
|
131
|
+
:is_bot, :is_spam, :is_cloud_provider, :cloud_provider_name
|
|
132
|
+
end
|
|
133
|
+
|
|
134
|
+
class TimeZoneInfo < ValueObject
|
|
135
|
+
attributes :name, :offset, :offset_with_dst, :current_time, :current_time_unix,
|
|
136
|
+
:current_tz_abbreviation, :current_tz_full_name, :standard_tz_abbreviation,
|
|
137
|
+
:standard_tz_full_name, :is_dst, :dst_savings, :dst_exists,
|
|
138
|
+
:dst_tz_abbreviation, :dst_tz_full_name, :dst_start, :dst_end
|
|
139
|
+
end
|
|
140
|
+
|
|
141
|
+
class UserAgentDevice < ValueObject
|
|
142
|
+
attributes :name, :type, :brand, :cpu
|
|
143
|
+
end
|
|
144
|
+
|
|
145
|
+
class UserAgentEngine < ValueObject
|
|
146
|
+
attributes :name, :type, :version, :version_major
|
|
147
|
+
end
|
|
148
|
+
|
|
149
|
+
class UserAgentOperatingSystem < ValueObject
|
|
150
|
+
attributes :name, :type, :version, :version_major, :build
|
|
151
|
+
end
|
|
152
|
+
|
|
153
|
+
class UserAgent < ValueObject
|
|
154
|
+
attributes :user_agent_string, :name, :type, :version, :version_major,
|
|
155
|
+
:device, :engine, :operating_system
|
|
156
|
+
end
|
|
157
|
+
|
|
158
|
+
class IpGeolocationResponse < ValueObject
|
|
159
|
+
attributes :ip, :domain, :hostname, :location, :country_metadata, :network,
|
|
160
|
+
:currency, :asn, :company, :security, :abuse, :time_zone, :user_agent
|
|
161
|
+
end
|
|
162
|
+
|
|
163
|
+
class BulkLookupSuccess < ValueObject
|
|
164
|
+
attributes :data
|
|
165
|
+
|
|
166
|
+
def success
|
|
167
|
+
true
|
|
168
|
+
end
|
|
169
|
+
|
|
170
|
+
alias success? success
|
|
171
|
+
end
|
|
172
|
+
|
|
173
|
+
class BulkLookupErrorDetails < ValueObject
|
|
174
|
+
attributes :message
|
|
175
|
+
end
|
|
176
|
+
|
|
177
|
+
class BulkLookupError < ValueObject
|
|
178
|
+
attributes :error
|
|
179
|
+
|
|
180
|
+
def success
|
|
181
|
+
false
|
|
182
|
+
end
|
|
183
|
+
|
|
184
|
+
alias success? success
|
|
185
|
+
end
|
|
186
|
+
end
|