zerobounce-sdk 1.1.2 → 2.0.13
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/.env.example +3 -0
- data/.github/auto_assign.yml +5 -0
- data/.github/workflows/auto_assign_ci.yaml +21 -0
- data/.github/workflows/codeql.yml +105 -0
- data/.github/workflows/sdk_ci.yml +29 -0
- data/.gitignore +3 -0
- data/CODE_OF_CONDUCT.md +1 -6
- data/CONTRIBUTING.md +112 -1
- data/Dockerfile +15 -0
- data/Gemfile.lock +5 -1
- data/LICENSE +1 -1
- data/LICENSE.txt +6 -6
- data/README.md +282 -22
- data/SECURITY.md +21 -0
- data/documentation.md +87 -9
- data/lib/zerobounce/api_urls.rb +18 -0
- data/lib/zerobounce/base_request.rb +31 -3
- data/lib/zerobounce/configuration.rb +13 -9
- data/lib/zerobounce/mock_request.rb +3 -3
- data/lib/zerobounce/request.rb +3 -3
- data/lib/zerobounce/validate_status.rb +17 -0
- data/lib/zerobounce/validate_sub_status.rb +40 -0
- data/lib/zerobounce/version.rb +1 -1
- data/lib/zerobounce.rb +158 -14
- data/zerobounce.gemspec +2 -0
- metadata +41 -8
- data/.env.sample +0 -3
- data/documentation_es.md +0 -449
data/README.md
CHANGED
|
@@ -23,19 +23,128 @@ Import
|
|
|
23
23
|
require 'zerobounce'
|
|
24
24
|
```
|
|
25
25
|
|
|
26
|
-
|
|
26
|
+
## Configuration
|
|
27
|
+
|
|
28
|
+
### Setting API Key
|
|
29
|
+
|
|
30
|
+
#### Method 1: Using configure block (recommended)
|
|
27
31
|
```ruby
|
|
28
32
|
Zerobounce.configure do |config|
|
|
29
33
|
config.apikey = '<zerobounce-api-key>'
|
|
30
|
-
|
|
34
|
+
# Optional: Set custom API URLs
|
|
35
|
+
config.api_root_url = 'https://api.zerobounce.net/v2'
|
|
36
|
+
config.bulk_api_root_url = 'https://bulkapi.zerobounce.net/v2'
|
|
31
37
|
end
|
|
32
38
|
```
|
|
33
|
-
|
|
39
|
+
|
|
40
|
+
#### Method 1a: Using ApiUrls constants (convenience)
|
|
41
|
+
```ruby
|
|
42
|
+
# Global API (default)
|
|
43
|
+
Zerobounce.configure do |config|
|
|
44
|
+
config.apikey = '<zerobounce-api-key>'
|
|
45
|
+
config.api_root_url = Zerobounce::ApiUrls::DEFAULT_URL
|
|
46
|
+
config.bulk_api_root_url = Zerobounce::ApiUrls::BULK_DEFAULT_URL
|
|
47
|
+
end
|
|
48
|
+
|
|
49
|
+
# European API (convenience)
|
|
50
|
+
Zerobounce.configure do |config|
|
|
51
|
+
config.apikey = '<zerobounce-api-key>'
|
|
52
|
+
config.api_root_url = Zerobounce::ApiUrls::EU_URL
|
|
53
|
+
config.bulk_api_root_url = Zerobounce::ApiUrls::BULK_DEFAULT_URL
|
|
54
|
+
end
|
|
55
|
+
|
|
56
|
+
# US API (convenience)
|
|
57
|
+
Zerobounce.configure do |config|
|
|
58
|
+
config.apikey = '<zerobounce-api-key>'
|
|
59
|
+
config.api_root_url = Zerobounce::ApiUrls::US_URL
|
|
60
|
+
config.bulk_api_root_url = Zerobounce::ApiUrls::BULK_DEFAULT_URL
|
|
61
|
+
end
|
|
34
62
|
```
|
|
63
|
+
|
|
64
|
+
#### Method 2: Direct configuration
|
|
65
|
+
```ruby
|
|
35
66
|
Zerobounce.config.apikey = '<zerobounce-api-key>'
|
|
36
|
-
|
|
67
|
+
# Optional: Set custom API URLs
|
|
68
|
+
Zerobounce.config.api_root_url = 'https://api.zerobounce.net/v2'
|
|
69
|
+
Zerobounce.config.bulk_api_root_url = 'https://bulkapi.zerobounce.net/v2'
|
|
37
70
|
```
|
|
38
71
|
|
|
72
|
+
#### Method 3: Using environment variables
|
|
73
|
+
Create a `.env` file in your project root:
|
|
74
|
+
```bash
|
|
75
|
+
ZEROBOUNCE_API_KEY=your_api_key_here
|
|
76
|
+
ZEROBOUNCE_API_URL=https://api.zerobounce.net/v2
|
|
77
|
+
ZEROBOUNCE_BULK_API_URL=https://bulkapi.zerobounce.net/v2
|
|
78
|
+
```
|
|
79
|
+
|
|
80
|
+
The gem will automatically load these environment variables when initialized. No additional configuration needed in your code.
|
|
81
|
+
|
|
82
|
+
#### Method 4: System environment variables
|
|
83
|
+
Set environment variables in your system:
|
|
84
|
+
```bash
|
|
85
|
+
export ZEROBOUNCE_API_KEY=your_api_key_here
|
|
86
|
+
export ZEROBOUNCE_API_URL=https://api.zerobounce.net/v2
|
|
87
|
+
export ZEROBOUNCE_BULK_API_URL=https://bulkapi.zerobounce.net/v2
|
|
88
|
+
```
|
|
89
|
+
|
|
90
|
+
|
|
91
|
+
### API URL Configuration Details
|
|
92
|
+
|
|
93
|
+
#### Default Behavior
|
|
94
|
+
If you don't specify `api_root_url` or `bulk_api_root_url`, the gem will use the following defaults:
|
|
95
|
+
- **Main API**: `https://api.zerobounce.net/v2`
|
|
96
|
+
- **Bulk API**: `https://bulkapi.zerobounce.net/v2`
|
|
97
|
+
|
|
98
|
+
These defaults are defined in the `Zerobounce::ApiUrls` class constants:
|
|
99
|
+
- `Zerobounce::ApiUrls::DEFAULT_URL` - Main API URL (Global)
|
|
100
|
+
- `Zerobounce::ApiUrls::EU_URL` - European API URL
|
|
101
|
+
- `Zerobounce::ApiUrls::US_URL` - US API URL
|
|
102
|
+
- `Zerobounce::ApiUrls::BULK_DEFAULT_URL` - Bulk API URL
|
|
103
|
+
|
|
104
|
+
#### Using ApiUrls Constants
|
|
105
|
+
You can use the predefined constants in your configuration. The EU and US URLs are provided as convenience options:
|
|
106
|
+
|
|
107
|
+
```ruby
|
|
108
|
+
# Global API (default)
|
|
109
|
+
Zerobounce.configure do |config|
|
|
110
|
+
config.apikey = 'your-api-key'
|
|
111
|
+
config.api_root_url = Zerobounce::ApiUrls::DEFAULT_URL
|
|
112
|
+
config.bulk_api_root_url = Zerobounce::ApiUrls::BULK_DEFAULT_URL
|
|
113
|
+
end
|
|
114
|
+
|
|
115
|
+
# European API
|
|
116
|
+
Zerobounce.configure do |config|
|
|
117
|
+
config.apikey = 'your-api-key'
|
|
118
|
+
config.api_root_url = Zerobounce::ApiUrls::EU_URL
|
|
119
|
+
config.bulk_api_root_url = Zerobounce::ApiUrls::BULK_DEFAULT_URL
|
|
120
|
+
end
|
|
121
|
+
|
|
122
|
+
# US API
|
|
123
|
+
Zerobounce.configure do |config|
|
|
124
|
+
config.apikey = 'your-api-key'
|
|
125
|
+
config.api_root_url = Zerobounce::ApiUrls::US_URL
|
|
126
|
+
config.bulk_api_root_url = Zerobounce::ApiUrls::BULK_DEFAULT_URL
|
|
127
|
+
end
|
|
128
|
+
```
|
|
129
|
+
|
|
130
|
+
#### Custom API URLs
|
|
131
|
+
You can override the default URLs for custom deployments or testing:
|
|
132
|
+
|
|
133
|
+
```ruby
|
|
134
|
+
Zerobounce.configure do |config|
|
|
135
|
+
config.apikey = 'your-api-key'
|
|
136
|
+
# Use custom URLs (e.g., for testing or custom deployments)
|
|
137
|
+
config.api_root_url = 'https://custom-api.example.com/v2'
|
|
138
|
+
config.bulk_api_root_url = 'https://custom-bulk-api.example.com/v2'
|
|
139
|
+
end
|
|
140
|
+
```
|
|
141
|
+
|
|
142
|
+
#### Environment Variable Priority
|
|
143
|
+
The configuration follows this priority order (highest to lowest):
|
|
144
|
+
1. Explicitly set in code (`config.api_root_url = '...'`)
|
|
145
|
+
2. Environment variables (`ZEROBOUNCE_API_URL`, `ZEROBOUNCE_BULK_API_URL`)
|
|
146
|
+
3. Default constants (`ApiUrls::DEFAULT_URL`, `ApiUrls::BULK_DEFAULT_URL`)
|
|
147
|
+
|
|
39
148
|
Credits
|
|
40
149
|
```ruby
|
|
41
150
|
Zerobounce.credits
|
|
@@ -80,10 +189,13 @@ Zerobounce.api_usage(Date.today, Date.today)
|
|
|
80
189
|
"sub_status_mailbox_quota_exceeded"=>0,
|
|
81
190
|
"sub_status_forcible_disconnect"=>0,
|
|
82
191
|
"sub_status_failed_smtp_connection"=>0,
|
|
192
|
+
"sub_status_accept_all"=>0,
|
|
83
193
|
"sub_status_mx_forward"=>0,
|
|
84
194
|
"sub_status_alternate"=>0,
|
|
85
|
-
"sub_status_blocked"=>0,
|
|
86
195
|
"sub_status_allowed"=>0,
|
|
196
|
+
"sub_status_blocked"=>0,
|
|
197
|
+
"sub_status_gold"=>0,
|
|
198
|
+
"sub_status_role_based_accept_all"=>0,
|
|
87
199
|
"start_date"=>"4/28/2023",
|
|
88
200
|
"end_date"=>"4/28/2023"}
|
|
89
201
|
```
|
|
@@ -412,9 +524,10 @@ When `has_header_row: false` is provided to `scoring_file_send()` method, column
|
|
|
412
524
|
|
|
413
525
|
### Email Finder
|
|
414
526
|
|
|
415
|
-
Guess Format
|
|
527
|
+
Guess Format by Domain
|
|
416
528
|
```ruby
|
|
417
|
-
|
|
529
|
+
# New keyword argument syntax (recommended)
|
|
530
|
+
Zerobounce.guessformat(domain: "zerobounce.net")
|
|
418
531
|
=>
|
|
419
532
|
{"email"=>"",
|
|
420
533
|
"domain"=>"zerobounce.net",
|
|
@@ -451,21 +564,166 @@ Zerobounce.guessformat("zerobounce.net")
|
|
|
451
564
|
{"format"=>"lastf", "confidence"=>"medium"},
|
|
452
565
|
{"format"=>"l-first", "confidence"=>"low"},
|
|
453
566
|
{"format"=>"l_first", "confidence"=>"low"}]}
|
|
454
|
-
|
|
567
|
+
|
|
568
|
+
# With names for better accuracy (new syntax)
|
|
569
|
+
Zerobounce.guessformat(domain: "zerobounce.net", first_name: "John", middle_name: 'Deere', last_name: "Doe")
|
|
570
|
+
|
|
571
|
+
# Backwards compatible syntax (still supported)
|
|
572
|
+
Zerobounce.guessformat("zerobounce.net")
|
|
573
|
+
Zerobounce.guessformat("zerobounce.net", first_name: "John", middle_name: 'Deere', last_name: "Doe")
|
|
574
|
+
```
|
|
575
|
+
|
|
576
|
+
Guess Format by Company Name
|
|
577
|
+
```ruby
|
|
578
|
+
# New keyword argument syntax (recommended)
|
|
579
|
+
Zerobounce.guessformat(company_name: "Zero Bounce")
|
|
580
|
+
=>
|
|
581
|
+
{"email"=>"",
|
|
582
|
+
"company_name"=>"Zero Bounce",
|
|
583
|
+
"format"=>"first.last",
|
|
584
|
+
"status"=>"",
|
|
585
|
+
"sub_status"=>"",
|
|
586
|
+
"confidence"=>"high",
|
|
587
|
+
"did_you_mean"=>"",
|
|
588
|
+
"failure_reason"=>"",
|
|
589
|
+
"other_domain_formats"=>[...]}
|
|
590
|
+
|
|
591
|
+
# With names for better accuracy (new syntax)
|
|
592
|
+
Zerobounce.guessformat(first_name: "John", last_name: "Doe", company_name: "Zero Bounce")
|
|
593
|
+
```
|
|
594
|
+
|
|
595
|
+
Find Email Address
|
|
596
|
+
```ruby
|
|
597
|
+
# Find email by domain
|
|
598
|
+
Zerobounce.find_email("John", domain: "zerobounce.net")
|
|
599
|
+
=>
|
|
600
|
+
{
|
|
601
|
+
"email": "john@zerobounce.net",
|
|
602
|
+
"email_confidence": "medium",
|
|
603
|
+
"domain": "zerobounce.net",
|
|
604
|
+
"company_name": "ZeroBounce",
|
|
605
|
+
"did_you_mean": "",
|
|
606
|
+
"failure_reason": ""
|
|
607
|
+
}
|
|
608
|
+
|
|
609
|
+
# Find email by company name
|
|
610
|
+
Zerobounce.find_email("John", company_name: "Zero Bounce")
|
|
611
|
+
=>
|
|
612
|
+
{
|
|
613
|
+
"email": "john@zerobounce.net",
|
|
614
|
+
"email_confidence": "medium",
|
|
615
|
+
"domain": "zerobounce.net",
|
|
616
|
+
"company_name": "ZeroBounce",
|
|
617
|
+
"did_you_mean": "",
|
|
618
|
+
"failure_reason": ""
|
|
619
|
+
}
|
|
620
|
+
|
|
621
|
+
# With additional name information for better accuracy
|
|
622
|
+
Zerobounce.find_email("John", domain: "zerobounce.net", middle_name: "Deere", last_name: "Doe")
|
|
623
|
+
```
|
|
624
|
+
|
|
625
|
+
Find Domain Information
|
|
626
|
+
```ruby
|
|
627
|
+
# Find domain format by domain
|
|
628
|
+
Zerobounce.find_domain(domain: "zerobounce.net")
|
|
629
|
+
=>
|
|
630
|
+
{
|
|
631
|
+
"domain": "zerobounce.net",
|
|
632
|
+
"company_name": "Hertza, LLC",
|
|
633
|
+
"format": "first.last",
|
|
634
|
+
"confidence": "high",
|
|
635
|
+
"did_you_mean": "",
|
|
636
|
+
"failure_reason": "",
|
|
637
|
+
"other_domain_formats": [
|
|
638
|
+
{"format": "first", "confidence": "high"},
|
|
639
|
+
{"format": "last.first", "confidence": "high"},
|
|
640
|
+
{"format": "lastfirst", "confidence": "high"},
|
|
641
|
+
{"format": "firstl", "confidence": "high"},
|
|
642
|
+
{"format": "lfirst", "confidence": "high"},
|
|
643
|
+
{"format": "firstlast", "confidence": "high"},
|
|
644
|
+
{"format": "last_middle_f", "confidence": "high"},
|
|
645
|
+
{"format": "last", "confidence": "high"},
|
|
646
|
+
{"format": "f.last", "confidence": "medium"},
|
|
647
|
+
{"format": "last-f", "confidence": "medium"},
|
|
648
|
+
{"format": "l.first", "confidence": "medium"},
|
|
649
|
+
{"format": "last_f", "confidence": "medium"},
|
|
650
|
+
{"format": "first.middle.last", "confidence": "medium"},
|
|
651
|
+
{"format": "first-last", "confidence": "medium"},
|
|
652
|
+
{"format": "last.f", "confidence": "medium"},
|
|
653
|
+
{"format": "last_first", "confidence": "medium"},
|
|
654
|
+
{"format": "f-last", "confidence": "medium"},
|
|
655
|
+
{"format": "first.l", "confidence": "medium"},
|
|
656
|
+
{"format": "first-l", "confidence": "medium"},
|
|
657
|
+
{"format": "first_l", "confidence": "medium"},
|
|
658
|
+
{"format": "first_last", "confidence": "medium"},
|
|
659
|
+
{"format": "f_last", "confidence": "medium"},
|
|
660
|
+
{"format": "last-first", "confidence": "medium"},
|
|
661
|
+
{"format": "flast", "confidence": "medium"},
|
|
662
|
+
{"format": "lastf", "confidence": "medium"},
|
|
663
|
+
{"format": "l_first", "confidence": "medium"},
|
|
664
|
+
{"format": "l-first", "confidence": "medium"},
|
|
665
|
+
{"format": "first-middle-last", "confidence": "low"},
|
|
666
|
+
{"format": "firstmlast", "confidence": "low"},
|
|
667
|
+
{"format": "last.middle.first", "confidence": "low"},
|
|
668
|
+
{"format": "last_middle_first", "confidence": "low"},
|
|
669
|
+
{"format": "first_middle_last", "confidence": "low"},
|
|
670
|
+
{"format": "last-middle-first", "confidence": "low"},
|
|
671
|
+
{"format": "first-m-last", "confidence": "low"},
|
|
672
|
+
{"format": "firstmiddlelast", "confidence": "low"},
|
|
673
|
+
{"format": "last.m.first", "confidence": "low"},
|
|
674
|
+
{"format": "lastmfirst", "confidence": "low"},
|
|
675
|
+
{"format": "lastmiddlefirst", "confidence": "low"},
|
|
676
|
+
{"format": "last_m_first", "confidence": "low"},
|
|
677
|
+
{"format": "first.m.last", "confidence": "low"},
|
|
678
|
+
{"format": "first_m_last", "confidence": "low"},
|
|
679
|
+
{"format": "last-m-first", "confidence": "low"}
|
|
680
|
+
]
|
|
681
|
+
}
|
|
682
|
+
|
|
683
|
+
# Find domain format by company name
|
|
684
|
+
Zerobounce.find_domain(company_name: "Zero Bounce")
|
|
685
|
+
=>
|
|
686
|
+
{
|
|
687
|
+
"domain": "zerobounce.net",
|
|
688
|
+
"company_name": "Zero Bounce",
|
|
689
|
+
"format": "first.last",
|
|
690
|
+
"confidence": "high",
|
|
691
|
+
"did_you_mean": "",
|
|
692
|
+
"failure_reason": "",
|
|
693
|
+
"other_domain_formats": [...]
|
|
694
|
+
}
|
|
455
695
|
```
|
|
456
696
|
|
|
457
697
|
## Development
|
|
458
698
|
|
|
459
|
-
|
|
699
|
+
### Local setup
|
|
700
|
+
```bash
|
|
701
|
+
sudo apt install -y rbenv
|
|
702
|
+
git clone https://github.com/rbenv/ruby-build.git ~/.rbenv/plugins/ruby-build
|
|
703
|
+
rbenv install 3.2.1
|
|
704
|
+
rbenv global 3.2.1
|
|
705
|
+
rbenv rehash
|
|
706
|
+
ruby -v
|
|
707
|
+
gem install bundler -v "~>2.4.6"
|
|
708
|
+
bundle install
|
|
709
|
+
```
|
|
710
|
+
|
|
711
|
+
### Run tests with Docker
|
|
712
|
+
From the **parent repository root** (the folder that contains all SDKs and `docker-compose.yml`):
|
|
713
|
+
|
|
714
|
+
```bash
|
|
715
|
+
docker compose build ruby
|
|
716
|
+
docker compose run --rm ruby
|
|
717
|
+
```
|
|
718
|
+
|
|
719
|
+
### Run tests (local)
|
|
460
720
|
```bash
|
|
461
|
-
rspec --init # if needed
|
|
462
721
|
bundle exec rspec
|
|
463
722
|
```
|
|
464
723
|
|
|
465
724
|
You should see an output like this
|
|
466
725
|
```bash
|
|
467
|
-
|
|
468
|
-
running live tests
|
|
726
|
+
running tests
|
|
469
727
|
.....................................................
|
|
470
728
|
|
|
471
729
|
Finished in 6.81 seconds (files took 0.40587 seconds to load)
|
|
@@ -473,18 +731,20 @@ Finished in 6.81 seconds (files took 0.40587 seconds to load)
|
|
|
473
731
|
```
|
|
474
732
|
|
|
475
733
|
### Test parameters
|
|
476
|
-
The tests use the following environment
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
|
|
734
|
+
The tests use the following environment parameter:
|
|
735
|
+
- **ZEROBOUNCE_API_KEY** – Your API key; used in mock tests as the valid key sample (any value will work for mock tests).
|
|
736
|
+
|
|
737
|
+
An invalid API key for error-handling tests is hardcoded in the spec; no env var is required.
|
|
480
738
|
|
|
481
|
-
To set
|
|
739
|
+
To set your key:
|
|
482
740
|
```bash
|
|
483
|
-
export ZEROBOUNCE_API_KEY=
|
|
484
|
-
export INCORRECT_API_KEY=thiskeyisinvalidorotherwiseincorrect
|
|
485
|
-
export TEST=unit
|
|
741
|
+
export ZEROBOUNCE_API_KEY=your_api_key_here
|
|
486
742
|
```
|
|
487
743
|
|
|
488
|
-
A .env.
|
|
744
|
+
A .env.example file is provided.
|
|
745
|
+
|
|
746
|
+
Tests use webmock and vcr for mocking HTTP requests. This means that actual requests were made and recorded in the spec/cassettes with an (at the time) valid API key used for testing purposes. This key has been invalidated in the meantime, however it is provided in the .env.example file for the mock tests to work. If you do not wish to use this key for mocks, you can replace it with any value in the .yml files under spec/cassettes or delete all of them and rerun the tests so that vcr records them with a new key.
|
|
747
|
+
|
|
748
|
+
## Publish
|
|
489
749
|
|
|
490
|
-
|
|
750
|
+
See the [sdk-docs (RubyGems)](../sdk-docs/rubygems/) guide in the SDKs repo for build and `gem push` steps.
|
data/SECURITY.md
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
# Security Policy
|
|
2
|
+
|
|
3
|
+
## Reporting a Vulnerability
|
|
4
|
+
|
|
5
|
+
If you think you've found a security issue, please report it privately instead of opening a public issue.
|
|
6
|
+
|
|
7
|
+
**Email:** integrations@zerobounce.net (use a subject like `[zero-bounce-ruby] Security`).
|
|
8
|
+
|
|
9
|
+
We'll look into reports as we can. If the issue is in the Zero Bounce API or service rather than this SDK, we may forward it to the right team.
|
|
10
|
+
|
|
11
|
+
## Supported Versions
|
|
12
|
+
|
|
13
|
+
We focus on the current release line for fixes. Using the [latest version](https://rubygems.org/gems/zerobounce-sdk) is recommended.
|
|
14
|
+
|
|
15
|
+
## Tips for Using This SDK
|
|
16
|
+
|
|
17
|
+
* Don't commit API keys or `.env` files—use environment variables or a secrets manager.
|
|
18
|
+
* Keep dependencies up to date with `bundle install` and upgrade when new versions are released.
|
|
19
|
+
* The client uses HTTPS by default; avoid overriding to non-HTTPS in production.
|
|
20
|
+
|
|
21
|
+
Thanks for helping keep things secure.
|
data/documentation.md
CHANGED
|
@@ -76,10 +76,13 @@ Zerobounce.api_usage(Date.today, Date.today)
|
|
|
76
76
|
"sub_status_mailbox_quota_exceeded"=>0,
|
|
77
77
|
"sub_status_forcible_disconnect"=>0,
|
|
78
78
|
"sub_status_failed_smtp_connection"=>0,
|
|
79
|
+
"sub_status_accept_all"=>0,
|
|
79
80
|
"sub_status_mx_forward"=>0,
|
|
80
81
|
"sub_status_alternate"=>0,
|
|
81
|
-
"sub_status_blocked"=>0,
|
|
82
82
|
"sub_status_allowed"=>0,
|
|
83
|
+
"sub_status_blocked"=>0,
|
|
84
|
+
"sub_status_gold"=>0,
|
|
85
|
+
"sub_status_role_based_accept_all"=>0,
|
|
83
86
|
"start_date"=>"4/28/2023",
|
|
84
87
|
"end_date"=>"4/28/2023"}
|
|
85
88
|
```
|
|
@@ -400,6 +403,81 @@ Zerobounce.guessformat("zerobounce.net")
|
|
|
400
403
|
# Zerobounce.guessformat("zerobounce.net", first_name: "John", middle_name: 'Deere', last_name: "Doe")
|
|
401
404
|
```
|
|
402
405
|
|
|
406
|
+
Find Email Address
|
|
407
|
+
```ruby
|
|
408
|
+
Zerobounce.find_email("John", domain: "zerobounce.net")
|
|
409
|
+
=>
|
|
410
|
+
{
|
|
411
|
+
"email": "john@zerobounce.net",
|
|
412
|
+
"email_confidence": "medium",
|
|
413
|
+
"domain": "zerobounce.net",
|
|
414
|
+
"company_name": "ZeroBounce",
|
|
415
|
+
"did_you_mean": "",
|
|
416
|
+
"failure_reason": ""
|
|
417
|
+
}
|
|
418
|
+
|
|
419
|
+
# With additional name information for better accuracy
|
|
420
|
+
Zerobounce.find_email("John", domain: "zerobounce.net", middle_name: "Deere", last_name: "Doe")
|
|
421
|
+
```
|
|
422
|
+
|
|
423
|
+
Find Domain Information
|
|
424
|
+
```ruby
|
|
425
|
+
Zerobounce.find_domain(domain: "zerobounce.net")
|
|
426
|
+
=>
|
|
427
|
+
{
|
|
428
|
+
"domain": "zerobounce.net",
|
|
429
|
+
"company_name": "Hertza, LLC",
|
|
430
|
+
"format": "first.last",
|
|
431
|
+
"confidence": "high",
|
|
432
|
+
"did_you_mean": "",
|
|
433
|
+
"failure_reason": "",
|
|
434
|
+
"other_domain_formats": [
|
|
435
|
+
{"format": "first", "confidence": "high"},
|
|
436
|
+
{"format": "last.first", "confidence": "high"},
|
|
437
|
+
{"format": "lastfirst", "confidence": "high"},
|
|
438
|
+
{"format": "firstl", "confidence": "high"},
|
|
439
|
+
{"format": "lfirst", "confidence": "high"},
|
|
440
|
+
{"format": "firstlast", "confidence": "high"},
|
|
441
|
+
{"format": "last_middle_f", "confidence": "high"},
|
|
442
|
+
{"format": "last", "confidence": "high"},
|
|
443
|
+
{"format": "f.last", "confidence": "medium"},
|
|
444
|
+
{"format": "last-f", "confidence": "medium"},
|
|
445
|
+
{"format": "l.first", "confidence": "medium"},
|
|
446
|
+
{"format": "last_f", "confidence": "medium"},
|
|
447
|
+
{"format": "first.middle.last", "confidence": "medium"},
|
|
448
|
+
{"format": "first-last", "confidence": "medium"},
|
|
449
|
+
{"format": "last.f", "confidence": "medium"},
|
|
450
|
+
{"format": "last_first", "confidence": "medium"},
|
|
451
|
+
{"format": "f-last", "confidence": "medium"},
|
|
452
|
+
{"format": "first.l", "confidence": "medium"},
|
|
453
|
+
{"format": "first-l", "confidence": "medium"},
|
|
454
|
+
{"format": "first_l", "confidence": "medium"},
|
|
455
|
+
{"format": "first_last", "confidence": "medium"},
|
|
456
|
+
{"format": "f_last", "confidence": "medium"},
|
|
457
|
+
{"format": "last-first", "confidence": "medium"},
|
|
458
|
+
{"format": "flast", "confidence": "medium"},
|
|
459
|
+
{"format": "lastf", "confidence": "medium"},
|
|
460
|
+
{"format": "l_first", "confidence": "medium"},
|
|
461
|
+
{"format": "l-first", "confidence": "medium"},
|
|
462
|
+
{"format": "first-middle-last", "confidence": "low"},
|
|
463
|
+
{"format": "firstmlast", "confidence": "low"},
|
|
464
|
+
{"format": "last.middle.first", "confidence": "low"},
|
|
465
|
+
{"format": "last_middle_first", "confidence": "low"},
|
|
466
|
+
{"format": "first_middle_last", "confidence": "low"},
|
|
467
|
+
{"format": "last-middle-first", "confidence": "low"},
|
|
468
|
+
{"format": "first-m-last", "confidence": "low"},
|
|
469
|
+
{"format": "firstmiddlelast", "confidence": "low"},
|
|
470
|
+
{"format": "last.m.first", "confidence": "low"},
|
|
471
|
+
{"format": "lastmfirst", "confidence": "low"},
|
|
472
|
+
{"format": "lastmiddlefirst", "confidence": "low"},
|
|
473
|
+
{"format": "last_m_first", "confidence": "low"},
|
|
474
|
+
{"format": "first.m.last", "confidence": "low"},
|
|
475
|
+
{"format": "first_m_last", "confidence": "low"},
|
|
476
|
+
{"format": "last-m-first", "confidence": "low"}
|
|
477
|
+
]
|
|
478
|
+
}
|
|
479
|
+
```
|
|
480
|
+
|
|
403
481
|
|
|
404
482
|
#### Development
|
|
405
483
|
|
|
@@ -421,17 +499,17 @@ Finished in 6.81 seconds (files took 0.40587 seconds to load)
|
|
|
421
499
|
|
|
422
500
|
##### Test parameters
|
|
423
501
|
The tests use the following environment parameters:
|
|
424
|
-
TEST {unit|live}
|
|
425
|
-
ZEROBOUNCE_API_KEY
|
|
426
|
-
|
|
502
|
+
- **TEST** {unit|live} – Influences whether mocked unit tests are run or the live server is used (credits may be used if you choose to do this).
|
|
503
|
+
- **ZEROBOUNCE_API_KEY** – Your API key; used to make requests to the live server and in mock tests as the valid key sample (any value will work for mock tests).
|
|
504
|
+
|
|
505
|
+
An invalid API key for error-handling tests is hardcoded in the spec; no env var is required.
|
|
427
506
|
|
|
428
|
-
To set them
|
|
507
|
+
To set them:
|
|
429
508
|
```bash
|
|
430
|
-
export ZEROBOUNCE_API_KEY=
|
|
431
|
-
export INCORRECT_API_KEY=thiskeyisinvalidorotherwiseincorrect
|
|
509
|
+
export ZEROBOUNCE_API_KEY=your_api_key_here
|
|
432
510
|
export TEST=unit
|
|
433
511
|
```
|
|
434
512
|
|
|
435
|
-
A .env.
|
|
513
|
+
A .env.example file is provided.
|
|
436
514
|
|
|
437
|
-
Mock tests were generated using webmock and vcr. This means that actual requests were made and recorded in the spec/cassettes with an (at the time) valid API key used for testing purposes. This key has been invalidated in the meantime, however it is provided in the .env.
|
|
515
|
+
Mock tests were generated using webmock and vcr. This means that actual requests were made and recorded in the spec/cassettes with an (at the time) valid API key used for testing purposes. This key has been invalidated in the meantime, however it is provided in the .env.example file for the mock tests to work. If you do not wish to use this key for mocks, you can replace it with any value in the .yml files under spec/cassettes or delete all of them and rerun the tests so that vcr records them with a new key.
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Zerobounce
|
|
4
|
+
# API URL constants for Zerobounce endpoints
|
|
5
|
+
#
|
|
6
|
+
# This class provides constant values for different Zerobounce API endpoints
|
|
7
|
+
# that can be accessed publicly by gem users.
|
|
8
|
+
#
|
|
9
|
+
class ApiUrls
|
|
10
|
+
# API URLs
|
|
11
|
+
DEFAULT_URL = 'https://api.zerobounce.net/v2/'
|
|
12
|
+
EU_URL = 'https://api-eu.zerobounce.net/v2/'
|
|
13
|
+
US_URL = 'https://api-us.zerobounce.net/v2/'
|
|
14
|
+
|
|
15
|
+
# Bulk API URLs
|
|
16
|
+
BULK_DEFAULT_URL = 'https://bulkapi.zerobounce.net/v2/'
|
|
17
|
+
end
|
|
18
|
+
end
|
|
@@ -13,6 +13,34 @@ module Zerobounce
|
|
|
13
13
|
|
|
14
14
|
protected
|
|
15
15
|
|
|
16
|
+
# Strips trailing slashes from root URL without using a regex (avoids ReDoS).
|
|
17
|
+
def self.__root_without_trailing_slashes__(root)
|
|
18
|
+
s = root.to_s
|
|
19
|
+
s = s.chomp('/') while s.end_with?('/')
|
|
20
|
+
s
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
# Resolves and validates filepath to prevent path traversal (e.g. ../../etc/passwd).
|
|
24
|
+
# Returns a canonical path only if the file is under the current directory and is a regular file.
|
|
25
|
+
def self.__safe_file_path__(filepath)
|
|
26
|
+
raise ArgumentError, 'File path is required' if filepath.nil? || filepath.to_s.empty?
|
|
27
|
+
filepath = filepath.to_s
|
|
28
|
+
expanded = File.expand_path(filepath)
|
|
29
|
+
base = File.realpath(Dir.pwd)
|
|
30
|
+
base_with_sep = base + File::SEPARATOR
|
|
31
|
+
unless expanded == base || expanded.start_with?(base_with_sep)
|
|
32
|
+
raise ArgumentError, 'File path must be under the current directory'
|
|
33
|
+
end
|
|
34
|
+
canonical = File.realpath(expanded)
|
|
35
|
+
unless canonical == base || canonical.start_with?(base_with_sep)
|
|
36
|
+
raise ArgumentError, 'File path must be under the current directory'
|
|
37
|
+
end
|
|
38
|
+
unless File.file?(canonical)
|
|
39
|
+
raise ArgumentError, 'File path must point to a regular file'
|
|
40
|
+
end
|
|
41
|
+
canonical
|
|
42
|
+
end
|
|
43
|
+
|
|
16
44
|
def self._get(root, path, params, content_type='application/json')
|
|
17
45
|
|
|
18
46
|
# puts path
|
|
@@ -21,7 +49,7 @@ module Zerobounce
|
|
|
21
49
|
raise ("API key must be assigned") if not Zerobounce.config.apikey
|
|
22
50
|
|
|
23
51
|
params[:api_key] = Zerobounce.config.apikey
|
|
24
|
-
url = "#{root}/#{path}"
|
|
52
|
+
url = "#{Zerobounce::BaseRequest.__root_without_trailing_slashes__(root)}/#{path}"
|
|
25
53
|
|
|
26
54
|
response = RestClient.get(url, {params: params})
|
|
27
55
|
return response
|
|
@@ -32,11 +60,11 @@ module Zerobounce
|
|
|
32
60
|
raise ("API key must be assigned") if not Zerobounce.config.apikey
|
|
33
61
|
|
|
34
62
|
params[:api_key] = Zerobounce.config.apikey
|
|
35
|
-
url = "#{root}/#{path}"
|
|
63
|
+
url = "#{Zerobounce::BaseRequest.__root_without_trailing_slashes__(root)}/#{path}"
|
|
36
64
|
response = nil
|
|
37
65
|
|
|
38
66
|
if filepath or content_type == 'multipart/form-data'
|
|
39
|
-
params[:file] = File.new(filepath, 'rb')
|
|
67
|
+
params[:file] = File.new(Zerobounce::BaseRequest.__safe_file_path__(filepath), 'rb')
|
|
40
68
|
params[:multipart] = true
|
|
41
69
|
response = RestClient.post(url, params)
|
|
42
70
|
|
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
require 'rest-client'
|
|
4
4
|
require 'dotenv'
|
|
5
|
+
require_relative 'api_urls'
|
|
5
6
|
|
|
6
7
|
module Zerobounce
|
|
7
8
|
# Configuration object for Zerobounce.
|
|
@@ -17,20 +18,23 @@ module Zerobounce
|
|
|
17
18
|
# @attr [String] apikey
|
|
18
19
|
# A Zerobounce API key.
|
|
19
20
|
#
|
|
21
|
+
# @attr [String] api_root_url
|
|
22
|
+
# The Zerobounce API root URL. Defaults to ApiUrls::DEFAULT_URL.
|
|
23
|
+
#
|
|
24
|
+
# @attr [String] bulk_api_root_url
|
|
25
|
+
# The Zerobounce bulk API root URL.
|
|
26
|
+
#
|
|
20
27
|
# @attr [Array<Symbol>] valid_statues
|
|
21
28
|
# The statuses that are considered valid by {Response#valid?}.
|
|
22
29
|
class Configuration
|
|
23
|
-
attr_accessor :
|
|
24
|
-
attr_accessor :headers
|
|
25
|
-
attr_accessor :apikey
|
|
26
|
-
attr_accessor :valid_statuses
|
|
27
|
-
attr_accessor :mock
|
|
30
|
+
attr_accessor :headers, :apikey, :api_root_url, :bulk_api_root_url, :valid_statuses, :mock
|
|
28
31
|
|
|
29
|
-
def initialize(mock=false)
|
|
30
|
-
|
|
31
|
-
self.host = 'https://api.zerobounce.net'
|
|
32
|
+
def initialize(mock = false)
|
|
33
|
+
File.file?('.env') ? Dotenv.load('.env') : Dotenv.load
|
|
32
34
|
self.apikey = ENV['ZEROBOUNCE_API_KEY']
|
|
33
|
-
self.
|
|
35
|
+
self.api_root_url = ENV['ZEROBOUNCE_API_URL'] || ApiUrls::DEFAULT_URL
|
|
36
|
+
self.bulk_api_root_url = ENV['ZEROBOUNCE_BULK_API_URL'] || ApiUrls::BULK_DEFAULT_URL
|
|
37
|
+
self.valid_statuses = %i[valid catch_all accept_all]
|
|
34
38
|
self.headers = { user_agent: "ZerobounceRubyGem/#{Zerobounce::VERSION}" }
|
|
35
39
|
self.mock = mock
|
|
36
40
|
end
|
|
@@ -8,7 +8,7 @@ module Zerobounce
|
|
|
8
8
|
class MockRequest < BaseRequest
|
|
9
9
|
|
|
10
10
|
def self.get(path, params, content_type='application/json')
|
|
11
|
-
response = self._get(Zerobounce
|
|
11
|
+
response = self._get(Zerobounce.configuration.api_root_url, path, params, content_type)
|
|
12
12
|
if response.headers[:content_type] == 'application/json'
|
|
13
13
|
response_body = response.body
|
|
14
14
|
response_body_json = JSON.parse(response_body)
|
|
@@ -25,7 +25,7 @@ module Zerobounce
|
|
|
25
25
|
end
|
|
26
26
|
|
|
27
27
|
def self.bulk_get(path, params, content_type='application/json')
|
|
28
|
-
response = self._get(Zerobounce
|
|
28
|
+
response = self._get(Zerobounce.configuration.bulk_api_root_url, path, params, content_type)
|
|
29
29
|
if response.headers[:content_type] == 'application/json'
|
|
30
30
|
response_body = response.body
|
|
31
31
|
response_body_json = JSON.parse(response_body)
|
|
@@ -42,7 +42,7 @@ module Zerobounce
|
|
|
42
42
|
end
|
|
43
43
|
|
|
44
44
|
def self.bulk_post(path, params, content_type='application/json', filepath=nil)
|
|
45
|
-
response = self._post(Zerobounce
|
|
45
|
+
response = self._post(Zerobounce.configuration.bulk_api_root_url, path, params, \
|
|
46
46
|
content_type, filepath)
|
|
47
47
|
if response.headers[:content_type] == 'application/json'
|
|
48
48
|
response_body = response.body
|