zerobounce-sdk 2.2.0 → 3.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 +28 -2
- data/README.md +7 -2
- data/lib/zerobounce/api_key.rb +21 -0
- data/lib/zerobounce/base_request.rb +64 -12
- data/lib/zerobounce/client.rb +8 -0
- data/lib/zerobounce/error.rb +6 -4
- data/lib/zerobounce/get_file_helper.rb +5 -3
- data/lib/zerobounce/priority3.rb +24 -4
- data/lib/zerobounce/request.rb +27 -24
- data/lib/zerobounce/version.rb +1 -1
- data/lib/zerobounce/warmup.rb +7 -3
- data/lib/zerobounce.rb +13 -14
- metadata +40 -10
- data/LICENSE.txt +0 -21
- data/lib/zerobounce/mock_request.rb +0 -105
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: a38febb2edd021c081aefb3df14db686a54ef1f41142f6559fd95069ad97219c
|
|
4
|
+
data.tar.gz: 13f9d5846f1ba7ac10c4afb082197f4d7b0efcbfa3bee2bb25310b82f27b2979
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 4d0c27a41276b918ba70daa8e62dc9b7bff1fe4f067ffe5119b33cd1ab891b0249a3f83eb53cc6fa237388de13a47e7edce9c3ade36f38a85dde9031f741fdef
|
|
7
|
+
data.tar.gz: 4b7c4ba9febcbf1a45d5b1f8028d35ac6128d374c827973130aa7e040992df988ab6c36ecd13a6974ba7c170d2ee30a4df0967af541baccff8ceebeeb2e5a8fe
|
data/CHANGELOG.md
CHANGED
|
@@ -5,7 +5,32 @@ All notable changes to this project will be documented in this file.
|
|
|
5
5
|
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
|
|
6
6
|
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
|
7
7
|
|
|
8
|
-
## [
|
|
8
|
+
## [3.0.0] - 2026-11-12
|
|
9
|
+
|
|
10
|
+
### Breaking
|
|
11
|
+
|
|
12
|
+
- **Breaking:** boolean options are parsed strictly. `true`/`false`, `'true'`/`'false'` (any case) and `1`/`0` are accepted; anything else raises `ArgumentError` naming the option before a request is sent. Up to 2.2.0 any truthy value was sent as `true`, so `activity_data: 'false'` sent `true`. Affects `validate` (`activity_data`, `verify_plus`, `domain_info`, `greylist_processing`), `validate_batch`, `validate_file_send`/`scoring_file_send` (`has_header_row`, `allow_phase_2`), `GetFileOptions#activity_data`, `send_email_finder_file`, `send_domain_search_file`, and the S3/SFTP jobs (`has_header_row`, `remove_duplicates`); a `nil` `has_header_row` on an S3/SFTP job, which 2.2.0 sent as `false`, now raises.
|
|
13
|
+
- **Breaking:** an API key with surrounding whitespace or control characters (for example one read from a file with its newline) raises `ArgumentError` before any request, from `Zerobounce.configure`'s key, `Zerobounce.with_api_key` and `Zerobounce::Client.new`, as `Zerobounce::Warmup.new` already did. 2.2.0 sent it as it was.
|
|
14
|
+
|
|
15
|
+
### Fixed
|
|
16
|
+
|
|
17
|
+
- `validate` returns the greylist answer `{"Message": "Greylist validation still in progress..."}` as its result (GET and `use_post: true`). 2.2.0 raised `RuntimeError` for it, so `greylist_processing: true` could never be used to pick up a result later.
|
|
18
|
+
- A 2xx `{"error_message": "..."}` with no `success` field raises. 2.2.0 returned it as the result, and `credits` returned `0` for it.
|
|
19
|
+
- `find_email`, `find_domain` and `guessformat` raise for a body made only of message fields (`detail`, `error_message`, `message`, as well as `Message`); 2.2.0 returned `{"detail": ...}` and `{"error_message": ...}` as results.
|
|
20
|
+
- `validate_file_get`, `scoring_file_get` and the email finder and domain search downloads raise `Empty response from ZeroBounce (HTTP 200)` for an empty body instead of returning `""`.
|
|
21
|
+
- `validate_file_check`, `validate_file_delete` and the scoring equivalents no longer raise an empty message for `{"error": ""}`; an empty `error` is not an error.
|
|
22
|
+
- `Zerobounce::Client#inspect` and `Zerobounce::Warmup#inspect` no longer show the API key.
|
|
23
|
+
|
|
24
|
+
### Changed
|
|
25
|
+
|
|
26
|
+
- Errors reported on a 2xx response raise `Zerobounce::ApiResponseError` with `#status_code` set to the HTTP status (usually 200). `ApiResponseError` is now a subclass of `RuntimeError` (it was a `StandardError`), so `rescue RuntimeError` catches these errors as before.
|
|
27
|
+
- The license file is named `LICENSE` (it was `LICENSE.txt`), and the gem ships only that one license file.
|
|
28
|
+
- The gemspec has a fuller summary and description, and `source_code_uri`, `changelog_uri` and `homepage_uri` metadata.
|
|
29
|
+
- Development only (nothing ships to gem users): `benchmark` is a development dependency, because it left Ruby 4.0's default gems and RuboCop needs it.
|
|
30
|
+
|
|
31
|
+
### Removed
|
|
32
|
+
|
|
33
|
+
- `Zerobounce::MockRequest` and the `TEST=unit` switch are gone. With `TEST=unit` set in the environment, the gem answered every call with canned responses instead of calling the API, which could silently fake results in an application's own test or CI setup. Nothing in the gem's specs used it.
|
|
9
34
|
|
|
10
35
|
## [2.2.0] - 2026-11-11
|
|
11
36
|
|
|
@@ -147,7 +172,8 @@ These change behaviour that 2.1.8 callers can rely on. Check each before upgradi
|
|
|
147
172
|
|
|
148
173
|
- Initial client for ZeroBounce API v2.
|
|
149
174
|
|
|
150
|
-
[Unreleased]: https://github.com/zerobounce/zero-bounce-ruby/compare/
|
|
175
|
+
[Unreleased]: https://github.com/zerobounce/zero-bounce-ruby/compare/v3.0.0...HEAD
|
|
176
|
+
[3.0.0]: https://github.com/zerobounce/zero-bounce-ruby/compare/v2.2.0...v3.0.0
|
|
151
177
|
[2.2.0]: https://github.com/zerobounce/zero-bounce-ruby/compare/v2.1.8...v2.2.0
|
|
152
178
|
[2.1.8]: https://github.com/zerobounce/zero-bounce-ruby/compare/v2.1.6...v2.1.8
|
|
153
179
|
[2.1.6]: https://github.com/zerobounce/zero-bounce-ruby/compare/v2.1.5...v2.1.6
|
data/README.md
CHANGED
|
@@ -700,8 +700,9 @@ Zerobounce.find_domain(company_name: "Zero Bounce")
|
|
|
700
700
|
## Errors
|
|
701
701
|
|
|
702
702
|
- An HTTP error status raises the `RestClient` exception for that status, as it did up to 2.1.8: `RestClient::NotFound`, `RestClient::Forbidden` and so on, all subclasses of `RestClient::ExceptionWithResponse` and of `RuntimeError`. `#http_code` gives the status. The message is the API's own short error text (up to 2.1.8 it was RestClient's, such as `404 Not Found`); an HTML error page is reduced to its title.
|
|
703
|
-
- An error reported with an HTTP 200 - an `error` field, `{"success": false}`, or an account-level `validate_batch` failure - raises `RuntimeError` with the API's message.
|
|
704
|
-
-
|
|
703
|
+
- An error reported with an HTTP 200 - an `error` field, an `Error` or `error_message` field without `success`, `{"success": false}`, or an account-level `validate_batch` failure - raises `Zerobounce::ApiResponseError` (a `RuntimeError`) with the API's message; `#status_code` is the HTTP status, usually 200. `find_email`, `find_domain` and `guessformat` also raise for a body made only of message fields (such as `{"Message": "Invalid API key or your account ran out of credits"}`).
|
|
704
|
+
- `validate`'s greylist answer, `{"Message": "Greylist validation still in progress..."}`, is a result: the Hash is returned with its `Message`.
|
|
705
|
+
- Invalid arguments, a boolean option that is not `true`/`false`, `'true'`/`'false'` or `1`/`0`, an API key with surrounding whitespace or control characters, and a base URL that is not `https://` raise `ArgumentError` before any request is sent.
|
|
705
706
|
- Redirects are not followed; a 3xx raises its `RestClient` exception (for example `RestClient::Found`).
|
|
706
707
|
- The Warmup client raises `Zerobounce::ApiResponseError`, whose `#status_code` gives the HTTP status.
|
|
707
708
|
|
|
@@ -839,3 +840,7 @@ Run it by hand before every release: the offline suite proves the SDK sends what
|
|
|
839
840
|
2. **Actions → Publish → Run workflow** with that tag.
|
|
840
841
|
|
|
841
842
|
Registry: [zerobounce-sdk on RubyGems](https://rubygems.org/gems/zerobounce-sdk)
|
|
843
|
+
|
|
844
|
+
## License
|
|
845
|
+
|
|
846
|
+
MIT. See [LICENSE](LICENSE).
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Zerobounce
|
|
4
|
+
# Checks an API key before it is sent.
|
|
5
|
+
module ApiKey
|
|
6
|
+
module_function
|
|
7
|
+
|
|
8
|
+
# The key, unchanged, or an ArgumentError when it has surrounding
|
|
9
|
+
# whitespace or control characters (a key read from a file with its
|
|
10
|
+
# newline). Such a key would fail at the API, and the HTTP library's own
|
|
11
|
+
# error could quote it unredacted.
|
|
12
|
+
def check!(api_key)
|
|
13
|
+
key = api_key.to_s
|
|
14
|
+
if key != key.strip || key.match?(/[\u0000-\u001f\u007f]/)
|
|
15
|
+
raise ArgumentError, 'api_key has surrounding whitespace or control characters'
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
api_key
|
|
19
|
+
end
|
|
20
|
+
end
|
|
21
|
+
end
|
|
@@ -3,6 +3,8 @@
|
|
|
3
3
|
require 'json'
|
|
4
4
|
require 'zerobounce/api_urls'
|
|
5
5
|
require 'zerobounce/response_error'
|
|
6
|
+
require 'zerobounce/api_key'
|
|
7
|
+
require 'zerobounce/error'
|
|
6
8
|
|
|
7
9
|
# https://github.com/rest-client/rest-client
|
|
8
10
|
require 'rest-client'
|
|
@@ -70,6 +72,17 @@ module Zerobounce
|
|
|
70
72
|
key.nil? ? Zerobounce.config.apikey : key
|
|
71
73
|
end
|
|
72
74
|
|
|
75
|
+
# The key for this request, or an error before anything is sent. A key with
|
|
76
|
+
# surrounding whitespace or control characters (one read from a file with
|
|
77
|
+
# its newline) would go out as it is and fail, and the HTTP library could
|
|
78
|
+
# quote it unredacted.
|
|
79
|
+
def self.__checked_api_key__
|
|
80
|
+
api_key = current_api_key
|
|
81
|
+
raise ('API key must be assigned') if api_key.nil? || api_key.to_s.strip.empty?
|
|
82
|
+
|
|
83
|
+
ApiKey.check!(api_key)
|
|
84
|
+
end
|
|
85
|
+
|
|
73
86
|
# The message for an HTTP error status.
|
|
74
87
|
def self.__http_error_message__(error)
|
|
75
88
|
code = error.http_code.to_i
|
|
@@ -109,8 +122,7 @@ module Zerobounce
|
|
|
109
122
|
# puts path
|
|
110
123
|
# puts Zerobounce.config.apikey
|
|
111
124
|
|
|
112
|
-
api_key =
|
|
113
|
-
raise ('API key must be assigned') if api_key.nil? || api_key.to_s.strip.empty?
|
|
125
|
+
api_key = __checked_api_key__
|
|
114
126
|
|
|
115
127
|
params[:api_key] = api_key
|
|
116
128
|
url = "#{Zerobounce::BaseRequest.__root_without_trailing_slashes__(root)}/#{path}"
|
|
@@ -122,8 +134,7 @@ module Zerobounce
|
|
|
122
134
|
|
|
123
135
|
def self._post(root, path, params, content_type='application/json', filepath=nil)
|
|
124
136
|
|
|
125
|
-
api_key =
|
|
126
|
-
raise ('API key must be assigned') if api_key.nil? || api_key.to_s.strip.empty?
|
|
137
|
+
api_key = __checked_api_key__
|
|
127
138
|
|
|
128
139
|
params[:api_key] = api_key
|
|
129
140
|
url = "#{Zerobounce::BaseRequest.__root_without_trailing_slashes__(root)}/#{path}"
|
|
@@ -165,8 +176,7 @@ module Zerobounce
|
|
|
165
176
|
# parameter, which the Inbox Placement Tester requires and which keeps the
|
|
166
177
|
# key out of request logs.
|
|
167
178
|
def self.__execute_with_method__(method, url, params, form: nil, json: nil, bearer: false)
|
|
168
|
-
api_key =
|
|
169
|
-
raise ('API key must be assigned') if api_key.nil? || api_key.to_s.strip.empty?
|
|
179
|
+
api_key = __checked_api_key__
|
|
170
180
|
|
|
171
181
|
params = (params || {}).dup
|
|
172
182
|
extra = {}
|
|
@@ -251,30 +261,72 @@ module Zerobounce
|
|
|
251
261
|
def self.__raise_if_success_false__(response, parsed)
|
|
252
262
|
return unless parsed.is_a?(Hash) && parsed.key?('success') && __success_means_failure__(parsed['success'])
|
|
253
263
|
|
|
254
|
-
raise
|
|
264
|
+
raise ApiResponseError.new(
|
|
265
|
+
ResponseError.message_from_parts(response.code, response.headers[:content_type].to_s, response.body.to_s),
|
|
266
|
+
response.code
|
|
267
|
+
)
|
|
268
|
+
end
|
|
269
|
+
|
|
270
|
+
# A value that reads as an error: a non-blank String.
|
|
271
|
+
def self.__error_text__(value)
|
|
272
|
+
value.is_a?(String) && !value.strip.empty?
|
|
273
|
+
end
|
|
274
|
+
|
|
275
|
+
# An "error" value that means failure: anything but nil or a blank String.
|
|
276
|
+
def self.__error_value__(value)
|
|
277
|
+
!value.nil? && !(value.is_a?(String) && value.strip.empty?)
|
|
278
|
+
end
|
|
279
|
+
|
|
280
|
+
# The keys a message-only body is made of.
|
|
281
|
+
MESSAGE_KEYS = %w[error message error_message Message Error detail].freeze
|
|
282
|
+
|
|
283
|
+
# A body made only of message keys (no "success"), for the calls whose
|
|
284
|
+
# result is never a bare message.
|
|
285
|
+
def self.__raise_if_message_only__(response, parsed)
|
|
286
|
+
return if parsed.empty? || parsed.key?('success')
|
|
287
|
+
return unless parsed.keys.all? { |key| MESSAGE_KEYS.include?(key) }
|
|
288
|
+
|
|
289
|
+
text = MESSAGE_KEYS.map { |key| parsed[key] }.find { |value| __error_text__(value) }
|
|
290
|
+
raise ApiResponseError.new(ResponseError.strip_prefix(text), response.code) if text
|
|
291
|
+
end
|
|
292
|
+
|
|
293
|
+
# A dead or invalid key on an HTTP 200: no "success" field and a non-empty
|
|
294
|
+
# "Error" (LIVE: the filters spell it so) or "error_message".
|
|
295
|
+
def self.__raise_if_bare_error__(response, parsed)
|
|
296
|
+
return if parsed.key?('success')
|
|
297
|
+
|
|
298
|
+
%w[Error error_message].each do |key|
|
|
299
|
+
next unless __error_text__(parsed[key])
|
|
300
|
+
|
|
301
|
+
raise ApiResponseError.new(ResponseError.strip_prefix(parsed[key]), response.code)
|
|
302
|
+
end
|
|
255
303
|
end
|
|
256
304
|
|
|
257
305
|
def self.__parse_json_response__(response)
|
|
258
306
|
body = response.body.to_s
|
|
259
307
|
content_type = (response.headers[:content_type] || '').to_s
|
|
308
|
+
code = response.code
|
|
260
309
|
if ResponseError.html?(body, content_type) || body.strip.empty?
|
|
261
|
-
raise ResponseError.message_from_parts(
|
|
310
|
+
raise ApiResponseError.new(ResponseError.message_from_parts(code, content_type, body), code)
|
|
262
311
|
end
|
|
263
312
|
begin
|
|
264
313
|
parsed = JSON.parse(body)
|
|
265
314
|
rescue JSON::ParserError
|
|
266
|
-
raise 'ZeroBounce returned a response that is not valid JSON.'
|
|
315
|
+
raise ApiResponseError.new('ZeroBounce returned a response that is not valid JSON.', code)
|
|
267
316
|
end
|
|
268
317
|
return parsed unless parsed.is_a?(Hash)
|
|
269
318
|
|
|
270
319
|
__raise_if_success_false__(response, parsed)
|
|
271
320
|
|
|
272
|
-
raise ResponseError.strip_prefix(parsed['error']) if
|
|
321
|
+
raise ApiResponseError.new(ResponseError.strip_prefix(parsed['error']), code) if __error_value__(parsed['error'])
|
|
273
322
|
# The Inbox Placement Tester is a Django REST framework app and reports
|
|
274
323
|
# its errors under "detail" rather than "error"/"message".
|
|
275
|
-
|
|
324
|
+
if __error_value__(parsed['detail'])
|
|
325
|
+
raise ApiResponseError.new(ResponseError.strip_prefix(parsed['detail']), code)
|
|
326
|
+
end
|
|
327
|
+
__raise_if_bare_error__(response, parsed)
|
|
276
328
|
if parsed.key?('errors') && parsed['errors'].is_a?(Array) && !parsed['errors'].empty?
|
|
277
|
-
raise ResponseError.strip_prefix(parsed['errors'][0]['error'])
|
|
329
|
+
raise ApiResponseError.new(ResponseError.strip_prefix(parsed['errors'][0]['error']), code)
|
|
278
330
|
end
|
|
279
331
|
parsed
|
|
280
332
|
end
|
data/lib/zerobounce/client.rb
CHANGED
|
@@ -1,12 +1,20 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
|
+
require 'zerobounce/api_key'
|
|
4
|
+
|
|
3
5
|
module Zerobounce
|
|
4
6
|
# One API key. Package methods share Zerobounce.config; a Client does not.
|
|
5
7
|
class Client
|
|
6
8
|
def initialize(apikey)
|
|
9
|
+
ApiKey.check!(apikey) unless apikey.nil? || apikey.to_s.strip.empty?
|
|
7
10
|
@apikey = apikey
|
|
8
11
|
end
|
|
9
12
|
|
|
13
|
+
# Keeps the API key out of logs and consoles.
|
|
14
|
+
def inspect
|
|
15
|
+
"#<#{self.class.name} apikey=#{@apikey.nil? ? 'nil' : '[FILTERED]'}>"
|
|
16
|
+
end
|
|
17
|
+
|
|
10
18
|
def validate(email, ip_address = nil, **options)
|
|
11
19
|
Zerobounce.with_api_key(@apikey) { Zerobounce.validate(email, ip_address, **options) }
|
|
12
20
|
end
|
data/lib/zerobounce/error.rb
CHANGED
|
@@ -59,10 +59,12 @@ module Zerobounce
|
|
|
59
59
|
# Carries the HTTP status so callers can tell a 403 (Cloudflare) from a
|
|
60
60
|
# 400/404 (API) or a 429 (the Warmup rate limit). The Warmup client raises
|
|
61
61
|
# it. The validation client raises the RestClient exception for an HTTP
|
|
62
|
-
# error status (status in #http_code), as it did up to 2.1.8, and
|
|
63
|
-
#
|
|
64
|
-
# short message.
|
|
65
|
-
|
|
62
|
+
# error status (status in #http_code), as it did up to 2.1.8, and this
|
|
63
|
+
# class, with the 2xx status (usually 200), for an error reported on a
|
|
64
|
+
# successful HTTP response; both carry the same short message. It is a
|
|
65
|
+
# RuntimeError, which those errors were before, so `rescue RuntimeError`
|
|
66
|
+
# still catches them.
|
|
67
|
+
class ApiResponseError < RuntimeError
|
|
66
68
|
# @return [Integer] the HTTP status of the failed response, or 0
|
|
67
69
|
attr_reader :status_code
|
|
68
70
|
|
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
require 'json'
|
|
4
4
|
require 'stringio'
|
|
5
|
+
require 'zerobounce/error'
|
|
5
6
|
require 'zerobounce/response_error'
|
|
6
7
|
|
|
7
8
|
module Zerobounce
|
|
@@ -70,12 +71,13 @@ module Zerobounce
|
|
|
70
71
|
body_str = response.body.to_s
|
|
71
72
|
ct = (response.headers[:content_type] || response.headers['Content-Type'] || '').to_s
|
|
72
73
|
|
|
73
|
-
|
|
74
|
-
|
|
74
|
+
# An empty body is no result file: an error, as in the other SDKs.
|
|
75
|
+
if code > 299 || body_str.strip.empty? || ResponseError.html?(body_str, ct)
|
|
76
|
+
raise ApiResponseError.new(ResponseError.message_from_parts(code, ct, body_str), code)
|
|
75
77
|
end
|
|
76
78
|
|
|
77
79
|
if should_treat_as_error?(body_str, ct)
|
|
78
|
-
raise format_error_message(body_str)
|
|
80
|
+
raise ApiResponseError.new(format_error_message(body_str), code)
|
|
79
81
|
end
|
|
80
82
|
|
|
81
83
|
io = StringIO.new(response.body)
|
data/lib/zerobounce/priority3.rb
CHANGED
|
@@ -27,7 +27,7 @@ module Zerobounce
|
|
|
27
27
|
end
|
|
28
28
|
|
|
29
29
|
params = { domain_column: domain_column }
|
|
30
|
-
params[:has_header_row] = has_header_row
|
|
30
|
+
params[:has_header_row] = __wire_bool__(has_header_row, 'has_header_row') unless has_header_row.nil?
|
|
31
31
|
params[:first_name_column] = first_name_column unless first_name_column.nil?
|
|
32
32
|
params[:last_name_column] = last_name_column unless last_name_column.nil?
|
|
33
33
|
params[:middle_name_column] = middle_name_column unless middle_name_column.nil?
|
|
@@ -51,7 +51,7 @@ module Zerobounce
|
|
|
51
51
|
raise ArgumentError, 'domain_column is required' if domain_column.nil? || domain_column.to_i < 1
|
|
52
52
|
|
|
53
53
|
params = { domain_column: domain_column }
|
|
54
|
-
params[:has_header_row] = has_header_row
|
|
54
|
+
params[:has_header_row] = __wire_bool__(has_header_row, 'has_header_row') unless has_header_row.nil?
|
|
55
55
|
zb_request.bulk_root_post("#{DOMAIN_SEARCH_BULK_BASE}/sendfile", params, 'multipart/form-data', filepath)
|
|
56
56
|
end
|
|
57
57
|
|
|
@@ -287,6 +287,26 @@ module Zerobounce
|
|
|
287
287
|
value.nil? || value.to_s.strip.empty?
|
|
288
288
|
end
|
|
289
289
|
|
|
290
|
+
# A boolean option, parsed strictly: true/false, 'true'/'false' in any
|
|
291
|
+
# case, or 1/0. Anything else is an ArgumentError, so 'false' can no
|
|
292
|
+
# longer be sent as true and a typo is not silently taken as a flag.
|
|
293
|
+
def __strict_bool__(value, name)
|
|
294
|
+
return value if [true, false].include?(value)
|
|
295
|
+
return value == 1 if value.is_a?(Integer) && [0, 1].include?(value)
|
|
296
|
+
|
|
297
|
+
if value.is_a?(String)
|
|
298
|
+
text = value.strip.downcase
|
|
299
|
+
return true if %w[true 1].include?(text)
|
|
300
|
+
return false if %w[false 0].include?(text)
|
|
301
|
+
end
|
|
302
|
+
raise ArgumentError, "#{name} must be true or false"
|
|
303
|
+
end
|
|
304
|
+
|
|
305
|
+
# A strict boolean option as the string the API expects on the wire.
|
|
306
|
+
def __wire_bool__(value, name)
|
|
307
|
+
__strict_bool__(value, name) ? 'true' : 'false'
|
|
308
|
+
end
|
|
309
|
+
|
|
290
310
|
def __inbox_placement_test_type__(value)
|
|
291
311
|
test_type = value.is_a?(Integer) ? value : value.to_s.strip.to_i
|
|
292
312
|
unless INBOX_PLACEMENT_TEST_TYPES.include?(test_type)
|
|
@@ -311,12 +331,12 @@ module Zerobounce
|
|
|
311
331
|
return_url = options[:return_url]
|
|
312
332
|
|
|
313
333
|
params = {
|
|
314
|
-
has_header_row: has_header_row
|
|
334
|
+
has_header_row: __wire_bool__(has_header_row, 'has_header_row'),
|
|
315
335
|
file_name: file_name,
|
|
316
336
|
return_url: return_url
|
|
317
337
|
}
|
|
318
338
|
if options.key?(:remove_duplicates)
|
|
319
|
-
params[:remove_duplicates] = options[:remove_duplicates]
|
|
339
|
+
params[:remove_duplicates] = __wire_bool__(options[:remove_duplicates], 'remove_duplicates')
|
|
320
340
|
end
|
|
321
341
|
|
|
322
342
|
zb_request.bulk_root_form_post(path, params)
|
data/lib/zerobounce/request.rb
CHANGED
|
@@ -9,9 +9,14 @@ module Zerobounce
|
|
|
9
9
|
# Sends the HTTP request.
|
|
10
10
|
class Request < BaseRequest
|
|
11
11
|
|
|
12
|
-
#
|
|
13
|
-
#
|
|
14
|
-
|
|
12
|
+
# The email finder and domain search. Their result is never a bare
|
|
13
|
+
# message, so a body made only of message keys is how they report a
|
|
14
|
+
# rejected request. LIVE: an invalid or drained key gets an HTTP 200 with
|
|
15
|
+
# {"Message": "Invalid API key or your account ran out of credits"}.
|
|
16
|
+
# Everywhere else a bare message is a result: validate's greylist answer
|
|
17
|
+
# {"Message": "Greylist validation still in progress..."} and the
|
|
18
|
+
# filters' {"Message": "Filter successfully added"}.
|
|
19
|
+
MESSAGE_ONLY_ERROR_PATHS = %w[guessformat].freeze
|
|
15
20
|
|
|
16
21
|
def self.get(path, params, content_type='application/json')
|
|
17
22
|
response = self._get(Zerobounce.configuration.api_root_url, path, params, content_type)
|
|
@@ -25,11 +30,14 @@ module Zerobounce
|
|
|
25
30
|
response_body_json = JSON.parse(response_body)
|
|
26
31
|
return response_body_json unless response_body_json.is_a?(Hash)
|
|
27
32
|
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
33
|
+
if __error_value__(response_body_json['error'])
|
|
34
|
+
raise ApiResponseError.new(ResponseError.strip_prefix(response_body_json['error']), response.code)
|
|
35
|
+
end
|
|
36
|
+
if response_body_json['errors'].is_a?(Array) && !response_body_json['errors'].empty?
|
|
37
|
+
raise ApiResponseError.new(response_body_json['errors'][0]['error'].to_s, response.code)
|
|
38
|
+
end
|
|
32
39
|
__raise_if_success_false__(response, response_body_json)
|
|
40
|
+
__raise_if_bare_error__(response, response_body_json)
|
|
33
41
|
|
|
34
42
|
return response_body_json
|
|
35
43
|
else
|
|
@@ -94,35 +102,30 @@ module Zerobounce
|
|
|
94
102
|
content_type = (response.headers[:content_type] || '').to_s
|
|
95
103
|
if ResponseError.html?(body, content_type) || body.strip.empty? ||
|
|
96
104
|
(!content_type.include?('json') && !body.strip.start_with?('{', '['))
|
|
97
|
-
raise ResponseError.message_from_parts(response.code, content_type, body)
|
|
105
|
+
raise ApiResponseError.new(ResponseError.message_from_parts(response.code, content_type, body), response.code)
|
|
98
106
|
end
|
|
99
107
|
begin
|
|
100
108
|
parsed = JSON.parse(body)
|
|
101
109
|
rescue JSON::ParserError
|
|
102
|
-
raise 'ZeroBounce returned a response that is not valid JSON.'
|
|
110
|
+
raise ApiResponseError.new('ZeroBounce returned a response that is not valid JSON.', response.code)
|
|
103
111
|
end
|
|
104
112
|
return parsed unless parsed.is_a?(Hash)
|
|
105
113
|
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
# them with {"Error": "Invalid API key or your account ran out of
|
|
110
|
-
# credits"} on an HTTP 200, which used to come back as their result.
|
|
111
|
-
if !parsed.key?('success') && parsed['Error'].is_a?(String) && !parsed['Error'].strip.empty?
|
|
112
|
-
raise ResponseError.strip_prefix(parsed['Error'])
|
|
113
|
-
end
|
|
114
|
-
# A body that is nothing but "Message" is how the validate endpoints
|
|
115
|
-
# report a rejected request. The filters endpoints use the same shape for
|
|
116
|
-
# a *success* ("Filter successfully added"), so they are left out.
|
|
117
|
-
if parsed.key?('Message') && parsed.length == 1 && !FILTER_PATHS.include?(path)
|
|
118
|
-
raise ResponseError.strip_prefix(parsed['Message'])
|
|
114
|
+
code = response.code
|
|
115
|
+
if __error_value__(parsed['error'])
|
|
116
|
+
raise ApiResponseError.new(ResponseError.strip_prefix(parsed['error']), code)
|
|
119
117
|
end
|
|
118
|
+
__raise_if_success_false__(response, parsed)
|
|
119
|
+
__raise_if_bare_error__(response, parsed)
|
|
120
|
+
__raise_if_message_only__(response, parsed) if MESSAGE_ONLY_ERROR_PATHS.include?(path)
|
|
120
121
|
if parsed.key?('errors') && parsed['errors'].is_a?(Array) && !parsed['errors'].empty?
|
|
121
122
|
account = parsed['errors'].find { |item| item.is_a?(Hash) && item['email_address'].to_s.casecmp('all').zero? }
|
|
122
123
|
if account
|
|
123
|
-
raise ResponseError.strip_prefix(account['error'])
|
|
124
|
+
raise ApiResponseError.new(ResponseError.strip_prefix(account['error']), code)
|
|
125
|
+
end
|
|
126
|
+
unless path == 'validatebatch'
|
|
127
|
+
raise ApiResponseError.new(ResponseError.strip_prefix(parsed['errors'][0]['error']), code)
|
|
124
128
|
end
|
|
125
|
-
raise ResponseError.strip_prefix(parsed['errors'][0]['error']) unless path == 'validatebatch'
|
|
126
129
|
end
|
|
127
130
|
parsed
|
|
128
131
|
end
|
data/lib/zerobounce/version.rb
CHANGED
data/lib/zerobounce/warmup.rb
CHANGED
|
@@ -5,6 +5,7 @@ require 'erb'
|
|
|
5
5
|
require 'json'
|
|
6
6
|
require 'rest-client'
|
|
7
7
|
|
|
8
|
+
require 'zerobounce/api_key'
|
|
8
9
|
require 'zerobounce/error'
|
|
9
10
|
require 'zerobounce/response_error'
|
|
10
11
|
|
|
@@ -40,9 +41,7 @@ module Zerobounce
|
|
|
40
41
|
|
|
41
42
|
# A key with surrounding whitespace or control characters would be
|
|
42
43
|
# quoted, unredacted, in the HTTP library's own error.
|
|
43
|
-
|
|
44
|
-
raise ArgumentError, 'api_key has surrounding whitespace or control characters'
|
|
45
|
-
end
|
|
44
|
+
ApiKey.check!(api_key)
|
|
46
45
|
unless base_url.to_s.downcase.start_with?('https://')
|
|
47
46
|
raise ArgumentError, 'base_url must be an https:// URL'
|
|
48
47
|
end
|
|
@@ -53,6 +52,11 @@ module Zerobounce
|
|
|
53
52
|
@root = "#{root}#{BASE_PATH}"
|
|
54
53
|
end
|
|
55
54
|
|
|
55
|
+
# Keeps the API key out of logs and consoles.
|
|
56
|
+
def inspect
|
|
57
|
+
"#<#{self.class.name} root=#{@root.inspect} api_key=[FILTERED]>"
|
|
58
|
+
end
|
|
59
|
+
|
|
56
60
|
# -- sending accounts ---------------------------------------------------
|
|
57
61
|
|
|
58
62
|
# Every sending account of the key's team. GET /sending-accounts
|
data/lib/zerobounce.rb
CHANGED
|
@@ -14,7 +14,6 @@ require 'zerobounce/get_file_helper'
|
|
|
14
14
|
require 'zerobounce/validate_status'
|
|
15
15
|
require 'zerobounce/validate_sub_status'
|
|
16
16
|
require 'zerobounce/request'
|
|
17
|
-
require 'zerobounce/mock_request'
|
|
18
17
|
require 'zerobounce/configuration'
|
|
19
18
|
require 'zerobounce/api_urls'
|
|
20
19
|
require 'zerobounce/priority3'
|
|
@@ -27,7 +26,7 @@ module Zerobounce
|
|
|
27
26
|
include Priority3
|
|
28
27
|
attr_writer :configuration
|
|
29
28
|
|
|
30
|
-
@@request =
|
|
29
|
+
@@request = Request
|
|
31
30
|
|
|
32
31
|
# Zerobounce configuration
|
|
33
32
|
#
|
|
@@ -102,6 +101,10 @@ module Zerobounce
|
|
|
102
101
|
raise ArgumentError, 'Invalid parameter: timeout (must be between 3 and 60 seconds)'
|
|
103
102
|
end
|
|
104
103
|
end
|
|
104
|
+
activity_data = __strict_bool__(activity_data, 'activity_data') unless activity_data.nil?
|
|
105
|
+
verify_plus = __strict_bool__(verify_plus, 'verify_plus') unless verify_plus.nil?
|
|
106
|
+
domain_info = __strict_bool__(domain_info, 'domain_info') unless domain_info.nil?
|
|
107
|
+
greylist_processing = __strict_bool__(greylist_processing, 'greylist_processing') unless greylist_processing.nil?
|
|
105
108
|
unless callback_url.nil?
|
|
106
109
|
raise ArgumentError, 'Empty parameter: callback_url' if callback_url.to_s.strip.empty?
|
|
107
110
|
|
|
@@ -312,8 +315,8 @@ module Zerobounce
|
|
|
312
315
|
# the API rejects the whole batch with the account-level "Invalid API Key
|
|
313
316
|
# or your account ran out of credits".
|
|
314
317
|
params[:timeout] = timeout.to_s unless timeout.nil?
|
|
315
|
-
params[:activity_data] = activity_data unless activity_data.nil?
|
|
316
|
-
params[:verify_plus] = verify_plus unless verify_plus.nil?
|
|
318
|
+
params[:activity_data] = __strict_bool__(activity_data, 'activity_data') unless activity_data.nil?
|
|
319
|
+
params[:verify_plus] = __strict_bool__(verify_plus, 'verify_plus') unless verify_plus.nil?
|
|
317
320
|
results = @@request.post('validatebatch', params)
|
|
318
321
|
results
|
|
319
322
|
end
|
|
@@ -345,16 +348,14 @@ module Zerobounce
|
|
|
345
348
|
return_url: nil,
|
|
346
349
|
allow_phase_2: nil
|
|
347
350
|
)
|
|
348
|
-
params = {
|
|
349
|
-
|
|
350
|
-
has_header_row: has_header_row,
|
|
351
|
-
}
|
|
351
|
+
params = { email_address_column: email_address_column }
|
|
352
|
+
params[:has_header_row] = __wire_bool__(has_header_row, 'has_header_row') unless has_header_row.nil?
|
|
352
353
|
params[:first_name_column] = first_name_column unless first_name_column.nil?
|
|
353
354
|
params[:last_name_column] = last_name_column unless last_name_column.nil?
|
|
354
355
|
params[:gender_column] = gender_column unless gender_column.nil?
|
|
355
356
|
params[:return_url] = return_url unless return_url.nil? || return_url.to_s.empty?
|
|
356
357
|
unless allow_phase_2.nil?
|
|
357
|
-
params[:allow_phase_2] = allow_phase_2
|
|
358
|
+
params[:allow_phase_2] = __wire_bool__(allow_phase_2, 'allow_phase_2')
|
|
358
359
|
end
|
|
359
360
|
@@request.bulk_post('sendfile', params, 'multipart/form-data', filepath)
|
|
360
361
|
end
|
|
@@ -414,7 +415,7 @@ module Zerobounce
|
|
|
414
415
|
if options
|
|
415
416
|
params[:download_type] = options.download_type if options.download_type
|
|
416
417
|
unless options.activity_data.nil?
|
|
417
|
-
params[:activity_data] = options.activity_data
|
|
418
|
+
params[:activity_data] = __wire_bool__(options.activity_data, 'activity_data')
|
|
418
419
|
end
|
|
419
420
|
end
|
|
420
421
|
@@request.bulk_getfile('getfile', params)
|
|
@@ -457,10 +458,8 @@ module Zerobounce
|
|
|
457
458
|
has_header_row: true,
|
|
458
459
|
return_url: nil
|
|
459
460
|
)
|
|
460
|
-
params = {
|
|
461
|
-
|
|
462
|
-
has_header_row: has_header_row,
|
|
463
|
-
}
|
|
461
|
+
params = { email_address_column: email_address_column }
|
|
462
|
+
params[:has_header_row] = __wire_bool__(has_header_row, 'has_header_row') unless has_header_row.nil?
|
|
464
463
|
params[:return_url] = return_url if return_url
|
|
465
464
|
@@request.bulk_post('scoring/sendfile', params, 'multipart/form-data', filepath)
|
|
466
465
|
end
|
metadata
CHANGED
|
@@ -1,14 +1,13 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: zerobounce-sdk
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version:
|
|
4
|
+
version: 3.0.0
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Zero Bounce
|
|
8
|
-
autorequire:
|
|
9
8
|
bindir: exe
|
|
10
9
|
cert_chain: []
|
|
11
|
-
date:
|
|
10
|
+
date: 1980-01-02 00:00:00.000000000 Z
|
|
12
11
|
dependencies:
|
|
13
12
|
- !ruby/object:Gem::Dependency
|
|
14
13
|
name: rest-client
|
|
@@ -94,6 +93,34 @@ dependencies:
|
|
|
94
93
|
- - ">="
|
|
95
94
|
- !ruby/object:Gem::Version
|
|
96
95
|
version: '0'
|
|
96
|
+
- !ruby/object:Gem::Dependency
|
|
97
|
+
name: racc
|
|
98
|
+
requirement: !ruby/object:Gem::Requirement
|
|
99
|
+
requirements:
|
|
100
|
+
- - ">="
|
|
101
|
+
- !ruby/object:Gem::Version
|
|
102
|
+
version: '0'
|
|
103
|
+
type: :development
|
|
104
|
+
prerelease: false
|
|
105
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
106
|
+
requirements:
|
|
107
|
+
- - ">="
|
|
108
|
+
- !ruby/object:Gem::Version
|
|
109
|
+
version: '0'
|
|
110
|
+
- !ruby/object:Gem::Dependency
|
|
111
|
+
name: benchmark
|
|
112
|
+
requirement: !ruby/object:Gem::Requirement
|
|
113
|
+
requirements:
|
|
114
|
+
- - ">="
|
|
115
|
+
- !ruby/object:Gem::Version
|
|
116
|
+
version: '0'
|
|
117
|
+
type: :development
|
|
118
|
+
prerelease: false
|
|
119
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
120
|
+
requirements:
|
|
121
|
+
- - ">="
|
|
122
|
+
- !ruby/object:Gem::Version
|
|
123
|
+
version: '0'
|
|
97
124
|
- !ruby/object:Gem::Dependency
|
|
98
125
|
name: bundler
|
|
99
126
|
requirement: !ruby/object:Gem::Requirement
|
|
@@ -282,7 +309,9 @@ dependencies:
|
|
|
282
309
|
- - "~>"
|
|
283
310
|
- !ruby/object:Gem::Version
|
|
284
311
|
version: '6.4'
|
|
285
|
-
description:
|
|
312
|
+
description: 'The official Ruby client for the ZeroBounce API: real-time and batch
|
|
313
|
+
email validation, AI scoring, bulk file validation and scoring, email finder and
|
|
314
|
+
domain search, the list evaluator, filters, the Inbox Placement Tester and Warmup.'
|
|
286
315
|
email:
|
|
287
316
|
- integrations@zerobounce.net
|
|
288
317
|
executables: []
|
|
@@ -291,9 +320,9 @@ extra_rdoc_files: []
|
|
|
291
320
|
files:
|
|
292
321
|
- CHANGELOG.md
|
|
293
322
|
- LICENSE
|
|
294
|
-
- LICENSE.txt
|
|
295
323
|
- README.md
|
|
296
324
|
- lib/zerobounce.rb
|
|
325
|
+
- lib/zerobounce/api_key.rb
|
|
297
326
|
- lib/zerobounce/api_urls.rb
|
|
298
327
|
- lib/zerobounce/base_request.rb
|
|
299
328
|
- lib/zerobounce/client.rb
|
|
@@ -302,7 +331,6 @@ files:
|
|
|
302
331
|
- lib/zerobounce/error.rb
|
|
303
332
|
- lib/zerobounce/get_file_helper.rb
|
|
304
333
|
- lib/zerobounce/get_file_options.rb
|
|
305
|
-
- lib/zerobounce/mock_request.rb
|
|
306
334
|
- lib/zerobounce/priority3.rb
|
|
307
335
|
- lib/zerobounce/request.rb
|
|
308
336
|
- lib/zerobounce/response_error.rb
|
|
@@ -315,7 +343,9 @@ licenses:
|
|
|
315
343
|
- MIT
|
|
316
344
|
metadata:
|
|
317
345
|
yard.run: yri
|
|
318
|
-
|
|
346
|
+
homepage_uri: https://zerobounce.net
|
|
347
|
+
source_code_uri: https://github.com/zerobounce/zero-bounce-ruby
|
|
348
|
+
changelog_uri: https://github.com/zerobounce/zero-bounce-ruby/blob/develop/CHANGELOG.md
|
|
319
349
|
rdoc_options: []
|
|
320
350
|
require_paths:
|
|
321
351
|
- lib
|
|
@@ -330,8 +360,8 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
|
330
360
|
- !ruby/object:Gem::Version
|
|
331
361
|
version: '0'
|
|
332
362
|
requirements: []
|
|
333
|
-
rubygems_version: 3.
|
|
334
|
-
signing_key:
|
|
363
|
+
rubygems_version: 3.6.9
|
|
335
364
|
specification_version: 4
|
|
336
|
-
summary:
|
|
365
|
+
summary: 'Official ZeroBounce Ruby SDK: email validation, AI scoring, bulk files,
|
|
366
|
+
email finder and domain search'
|
|
337
367
|
test_files: []
|
data/LICENSE.txt
DELETED
|
@@ -1,21 +0,0 @@
|
|
|
1
|
-
MIT License
|
|
2
|
-
|
|
3
|
-
Copyright (c) 2026 ZeroBounce
|
|
4
|
-
|
|
5
|
-
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
-
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
-
in the Software without restriction, including without limitation the rights
|
|
8
|
-
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
-
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
-
furnished to do so, subject to the following conditions:
|
|
11
|
-
|
|
12
|
-
The above copyright notice and this permission notice shall be included in all
|
|
13
|
-
copies or substantial portions of the Software.
|
|
14
|
-
|
|
15
|
-
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
-
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
-
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
-
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
-
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
-
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
-
SOFTWARE.
|
|
@@ -1,105 +0,0 @@
|
|
|
1
|
-
# frozen_string_literal: true
|
|
2
|
-
|
|
3
|
-
require 'zerobounce/base_request'
|
|
4
|
-
require 'zerobounce/get_file_helper'
|
|
5
|
-
|
|
6
|
-
module Zerobounce
|
|
7
|
-
|
|
8
|
-
# Sends the HTTP request.
|
|
9
|
-
class MockRequest < BaseRequest
|
|
10
|
-
|
|
11
|
-
def self.get(path, params, content_type='application/json')
|
|
12
|
-
response = self._get(Zerobounce.configuration.api_root_url, path, params, content_type)
|
|
13
|
-
if __json_content_type__(response.headers[:content_type])
|
|
14
|
-
response_body = response.body
|
|
15
|
-
response_body_json = JSON.parse(response_body)
|
|
16
|
-
|
|
17
|
-
raise (response_body_json['error']) if response_body_json.key?('error')
|
|
18
|
-
raise (response_body_json['errors'][0]['error']) \
|
|
19
|
-
if response_body_json.key?('errors') and \
|
|
20
|
-
response_body_json['errors'].length > 0
|
|
21
|
-
|
|
22
|
-
return response_body_json
|
|
23
|
-
else
|
|
24
|
-
return response
|
|
25
|
-
end
|
|
26
|
-
end
|
|
27
|
-
|
|
28
|
-
def self.bulk_get(path, params, content_type='application/json')
|
|
29
|
-
response = self._get(Zerobounce.configuration.bulk_api_root_url, path, params, content_type)
|
|
30
|
-
if __json_content_type__(response.headers[:content_type])
|
|
31
|
-
response_body = response.body
|
|
32
|
-
response_body_json = JSON.parse(response_body)
|
|
33
|
-
return response_body_json unless response_body_json.is_a?(Hash)
|
|
34
|
-
|
|
35
|
-
raise (response_body_json['error']) if response_body_json.key?('error')
|
|
36
|
-
raise (response_body_json['errors'][0]['error']) \
|
|
37
|
-
if response_body_json.key?('errors') and \
|
|
38
|
-
response_body_json['errors'].length > 0
|
|
39
|
-
|
|
40
|
-
return response_body_json
|
|
41
|
-
else
|
|
42
|
-
return response.body
|
|
43
|
-
end
|
|
44
|
-
end
|
|
45
|
-
|
|
46
|
-
def self.bulk_getfile(path, params)
|
|
47
|
-
response = self._get(Zerobounce.configuration.bulk_api_root_url, path, params, 'application/json')
|
|
48
|
-
GetFileHelper.process_getfile_response(response)
|
|
49
|
-
end
|
|
50
|
-
|
|
51
|
-
def self.post(path, params, content_type='application/json', filepath=nil)
|
|
52
|
-
response = self._post(Zerobounce.configuration.api_root_url, path, params, \
|
|
53
|
-
content_type, filepath)
|
|
54
|
-
if __json_content_type__(response.headers[:content_type])
|
|
55
|
-
response_body = response.body
|
|
56
|
-
response_body_json = JSON.parse(response_body)
|
|
57
|
-
|
|
58
|
-
raise (response_body_json['error']) if response_body_json.key?('error')
|
|
59
|
-
raise (response_body_json['errors'][0]['error']) \
|
|
60
|
-
if response_body_json.key?('errors') and \
|
|
61
|
-
response_body_json['errors'].length > 0
|
|
62
|
-
|
|
63
|
-
return response_body_json
|
|
64
|
-
end
|
|
65
|
-
return response.body
|
|
66
|
-
end
|
|
67
|
-
|
|
68
|
-
def self.bulk_post(path, params, content_type='application/json', filepath=nil)
|
|
69
|
-
response = self._post(Zerobounce.configuration.bulk_api_root_url, path, params, \
|
|
70
|
-
content_type, filepath)
|
|
71
|
-
if __json_content_type__(response.headers[:content_type])
|
|
72
|
-
return __parse_json_response__(response)
|
|
73
|
-
end
|
|
74
|
-
response.body
|
|
75
|
-
end
|
|
76
|
-
|
|
77
|
-
def self.bulk_root_get(path, params, content_type='application/json')
|
|
78
|
-
response = self._get(bulk_api_root_without_v2, path, params, content_type)
|
|
79
|
-
if __json_content_type__(response.headers[:content_type])
|
|
80
|
-
return __parse_json_response__(response)
|
|
81
|
-
end
|
|
82
|
-
response.body
|
|
83
|
-
end
|
|
84
|
-
|
|
85
|
-
def self.bulk_root_getfile(path, params)
|
|
86
|
-
response = self._get(bulk_api_root_without_v2, path, params, 'application/json')
|
|
87
|
-
GetFileHelper.process_getfile_response(response)
|
|
88
|
-
end
|
|
89
|
-
|
|
90
|
-
def self.bulk_root_post(path, params, content_type='application/json', filepath=nil)
|
|
91
|
-
response = self._post(bulk_api_root_without_v2, path, params, content_type, filepath)
|
|
92
|
-
if __json_content_type__(response.headers[:content_type])
|
|
93
|
-
return __parse_json_response__(response)
|
|
94
|
-
end
|
|
95
|
-
response.body
|
|
96
|
-
end
|
|
97
|
-
|
|
98
|
-
def self.bulk_root_form_post(path, params)
|
|
99
|
-
response = self._post(bulk_api_root_without_v2, path, params, 'application/x-www-form-urlencoded')
|
|
100
|
-
__parse_json_response__(response)
|
|
101
|
-
end
|
|
102
|
-
|
|
103
|
-
end
|
|
104
|
-
|
|
105
|
-
end
|