snov 0.2.2 → 0.3.2

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 4c9707254fe18c6d11cce223d1519211a70250bfb42a77a4e1c0d9edfe65d8e8
4
- data.tar.gz: 1a36316bc0816173d223d19f1a8dd3c5fd73ff0d82a3ed97e31e387cb7b60b55
3
+ metadata.gz: 7faf9e2f35034621c1431218b982ad536b36286982b5dcbe6cce5e88a167b5c8
4
+ data.tar.gz: 733b376b869be78af8537a41e62405c4006e0ac2745f8c05c980522e21662ecf
5
5
  SHA512:
6
- metadata.gz: 8404a9e0b1e4d40c19e2f3f582285f48264c6cc708a445056718f8902ad8518dced48d633c5073f6a08aa1efc01eabafbec87a18eab70260dde1660588bce525
7
- data.tar.gz: 1a3a974b53c40a26e29709789dc00fb0f642f96896f4aca833c9c209a7ade74c5c86011988f4e6bd6452b305ba481c3d383af6d2be90bdbcdda0fd205e0a4d5b
6
+ metadata.gz: de5cd49d6bc4a25101314ab0434510bcee7436ce25c574d8dfe46546516069b2f57322716d8b6c74e6740c917ec5753ba1d4c54eecb5a9e93b30b14d4f30aa20
7
+ data.tar.gz: 72a08e7800eb9297ebd746ba7a19c75458a312ad6a1b271ec28c384bf384e33688134b5d041809d01da3b6ef2783b98d58c1c8e8c50a045e76f62948baa5deea
data/.gitignore CHANGED
@@ -10,4 +10,5 @@
10
10
  /.rspec_status
11
11
  /gems.locked
12
12
  /vendor/bundle
13
- /.vscode
13
+ /.vscode
14
+ /spec/snov/moved
data/CHANGELOG.md CHANGED
@@ -1,3 +1,17 @@
1
+ ## [0.3.2]
2
+ * increase snov timeout to 90 seconds
3
+ ## [0.3.1]
4
+ * fix fake result for /v1/prospect-list
5
+
6
+ ## [0.3.0]
7
+ * add Faraday response into exception
8
+ * fake client to only return success result for selected queries
9
+
10
+ ## [0.2.4]
11
+ * Fix DomainSearch to_a
12
+
13
+ ## [0.2.3]
14
+ * Fix DomainSearch doing post instead of get
1
15
  ## [0.2.2]
2
16
  * DomainSearch Response class
3
17
 
data/README.md CHANGED
@@ -26,7 +26,7 @@ see https://snov.io/api#DomainSearch2
26
26
 
27
27
  ```ruby
28
28
  results = Snov::DomainSearch.new(domain: "octagon.com", type: "personal", limit: 10)
29
- results.lastId
29
+ results.last_id
30
30
  results.each do |result|
31
31
  puts result.email
32
32
  puts result.first_name
data/gems.rb CHANGED
@@ -3,6 +3,7 @@ source "https://rubygems.org"
3
3
  # Specify your gem's dependencies in snov.gemspec
4
4
  gemspec
5
5
 
6
+ gem "byebug"
6
7
  gem "rake", "~> 12.0"
7
8
  gem "rspec", "~> 3.0"
8
9
  gem "rubocop"
data/lib/snov/client.rb CHANGED
@@ -7,23 +7,33 @@ module Snov
7
7
 
8
8
  class TimedOut < SnovError; end
9
9
 
10
- class UnauthorizedError < SnovError; end
10
+ class AuthError < SnovError; end
11
11
 
12
- class BadGatewayError < SnovError; end
12
+ class ResponseError < SnovError
13
+ attr_reader :response
13
14
 
14
- class ForbiddenError < SnovError; end
15
+ def initialize(message, response: nil)
16
+ super(message)
17
+ @response = response
18
+ end
19
+ end
15
20
 
16
- class GatewayTimeOut < SnovError; end
21
+ class UnauthorizedError < ResponseError; end
17
22
 
18
- class BadRequest < SnovError; end
23
+ class BadGatewayError < ResponseError; end
19
24
 
20
- class AuthError < SnovError; end
25
+ class ForbiddenError < ResponseError; end
26
+
27
+ class GatewayTimeOut < ResponseError; end
28
+
29
+ class BadRequest < ResponseError; end
30
+
31
+ class MethodNotAllowed < ResponseError; end
21
32
 
22
- class MethodNotAllowed < SnovError; end
23
33
  ERROR_CLASSES = { 401 => UnauthorizedError, 502 => BadGatewayError, 403 => ForbiddenError,
24
34
  504 => GatewayTimeOut, 400 => BadRequest, 405 => MethodNotAllowed }
25
35
 
26
- def initialize(client_id:, client_secret:, access_token: nil, timeout_seconds: 60)
36
+ def initialize(client_id:, client_secret:, access_token: nil, timeout_seconds: 90)
27
37
  self.client_id = client_id.to_str
28
38
  self.client_secret = client_secret.to_str
29
39
  @access_token = access_token
@@ -56,8 +66,8 @@ module Snov
56
66
 
57
67
  def parse_response(resp, path, _params)
58
68
  unless resp.success?
59
- raise ERROR_CLASSES.fetch(resp.status, SnovError),
60
- "#{path} (#{resp.status})"
69
+ error_class = ERROR_CLASSES.fetch(resp.status, ResponseError)
70
+ raise error_class.new("#{path} (#{resp.status})", response: resp&.body)
61
71
  end
62
72
  MultiJson.load(resp.body)
63
73
  end
@@ -4,7 +4,9 @@ require 'camel_snake_struct'
4
4
  module Snov
5
5
  class DomainSearch
6
6
  Response = Class.new(CamelSnakeStruct)
7
- Response.example(MultiJson.load(File.read("#{__dir__}/fake_client/post_v2_domain-emails-with-info.json")))
7
+ Dir["#{__dir__}/fake_client/get_v2_domain-emails-with-info/*.json"].each do |example_file|
8
+ Response.example(MultiJson.load(File.read(example_file)))
9
+ end
8
10
  include Enumerable
9
11
 
10
12
  attr_reader :client, :domain, :type, :limit
@@ -18,7 +20,7 @@ module Snov
18
20
  end
19
21
 
20
22
  def each(&block)
21
- raw_result.emails.each(&block)
23
+ emails.each(&block)
22
24
  end
23
25
 
24
26
  def success
@@ -41,12 +43,20 @@ module Snov
41
43
  raw_result.company_name
42
44
  end
43
45
 
46
+ def emails
47
+ raw_result.emails
48
+ end
49
+
44
50
  def raw_result
45
- Response.new(client.post("/v2/domain-emails-with-info",
46
- 'lastId' => @last_id,
47
- 'limit' => @limit,
48
- 'type' => type.to_s,
49
- 'domain' => domain.to_s))
51
+ @raw_result ||= Response.new(client.get("/v2/domain-emails-with-info",
52
+ 'lastId' => @last_id,
53
+ 'limit' => @limit,
54
+ 'type' => type.to_s,
55
+ 'domain' => domain.to_s))
56
+ end
57
+
58
+ def to_h
59
+ raw_result.to_h
50
60
  end
51
61
  end
52
62
  end
@@ -1,14 +1,52 @@
1
1
  module Snov
2
2
  class FakeClient
3
- def get(path)
4
- data = File.read("#{__dir__}/fake_client/get#{path.tr("/", "_")}.json")
3
+ def self.folder=(val)
4
+ @folder = val
5
+ FileUtils.mkdir_p(@folder)
6
+ ["post_v1_get-profile-by-email", "get_v2_domain-emails-with-info",
7
+ "post_v1_get-prospects-by-email", "post_v1_prospect-list", "get_v1_get-user-lists"].each do |sub_folder|
8
+ FileUtils.cp_r "#{default_folder}/#{sub_folder}", @folder
9
+ end
10
+ end
11
+
12
+ def self.folder
13
+ @folder || default_folder
14
+ end
15
+
16
+ def self.reset_folder
17
+ @folder = nil
18
+ end
19
+
20
+ def self.default_folder
21
+ "#{__dir__}/fake_client"
22
+ end
23
+
24
+ def get(path, payload_hash = {})
25
+ data = File.read(filename("get", path, payload_hash))
26
+ MultiJson.load(data)
27
+ rescue Errno::ENOENT
28
+ data = File.read(filename("get", path, 'not_found' => 'true'))
5
29
  MultiJson.load(data)
6
30
  end
7
31
 
8
- def post(path, params = {})
9
- params.to_a.map(&:join).join("&")
10
- data = File.read("#{__dir__}/fake_client/post#{path.tr("/", "_")}.json")
32
+ def post(path, payload_hash = {})
33
+ data = File.read(filename("post", path, payload_hash))
11
34
  MultiJson.load(data)
35
+ rescue Errno::ENOENT => e
36
+ file = filename("post", path, 'not_found' => 'true')
37
+ if File.exist?(file)
38
+ MultiJson.load(File.read(file))
39
+ else
40
+ raise Snov::Client::BadRequest, e.message
41
+ end
42
+ end
43
+
44
+ private
45
+
46
+ def filename(method, path, payload_hash)
47
+ add = payload_hash.to_a.map { |v| v.join("=") }.join("&").tr(".", "_")
48
+ add = "default" if add == ""
49
+ "#{self.class.folder}/#{method}#{path.tr("/", "_")}/#{add}.json"
12
50
  end
13
51
  end
14
52
  end
@@ -0,0 +1 @@
1
+ {"success":true,"domain":"","webmail":false,"result":0,"lastId":0,"limit":10,"companyName":"","emails":[]}
@@ -0,0 +1,103 @@
1
+ {
2
+ "success": true,
3
+ "id": 301592,
4
+ "source": "linkedIn",
5
+ "name": "Lizi Hamer",
6
+ "firstName": "Lizi",
7
+ "lastName": "Hamer",
8
+ "logo": "https://app.snov.io/img/peoples/010fcf23c70dfa68d880545ec89a9215.jpg",
9
+ "industry": null,
10
+ "country": "Singapore",
11
+ "locality": "Singapore",
12
+ "social": [
13
+ {
14
+ "link": "https://www.linkedin.com/in/lizihamer/",
15
+ "type": "linkedIn"
16
+ },
17
+ {
18
+ "link": "https://twitter.com/LiziHamer",
19
+ "type": "twitter"
20
+ }
21
+ ],
22
+ "currentJobs": [
23
+ {
24
+ "companyName": "Octagon",
25
+ "position": "Regional Creative Director",
26
+ "socialLink": "https://www.linkedin.com/company/165282",
27
+ "site": "www.octagon.com",
28
+ "locality": "Greater New York City Area",
29
+ "state": "Connecticut",
30
+ "city": "Stamford",
31
+ "street": "290 Harbor Dr",
32
+ "street2": "2nd Floor",
33
+ "postal": "06902",
34
+ "founded": "1983",
35
+ "startDate": "2016-01-31",
36
+ "endDate": null,
37
+ "size": "1-10",
38
+ "industry": "Marketing and Advertising",
39
+ "companyType": "Public Company",
40
+ "country": "United States"
41
+ },
42
+ {
43
+ "companyName": "SisuGirls",
44
+ "position": "Co Founder",
45
+ "socialLink": "https://www.linkedin.com/company/3841118",
46
+ "site": "http://www.sisugirls.org",
47
+ "locality": null,
48
+ "state": "SG",
49
+ "city": "Singapore",
50
+ "street": "33-03 Hong Leong Building",
51
+ "street2": null,
52
+ "postal": null,
53
+ "founded": "2014",
54
+ "startDate": "2015-07-31",
55
+ "endDate": null,
56
+ "size": "1-10",
57
+ "industry": "Health, Wellness and Fitness",
58
+ "companyType": null,
59
+ "country": "Singapore"
60
+ }
61
+ ],
62
+ "previousJobs": [
63
+ {
64
+ "companyName": "Fusion Co-innovation Labs",
65
+ "position": "Creative Entrepreneur",
66
+ "socialLink": null,
67
+ "site": null,
68
+ "locality": null,
69
+ "state": null,
70
+ "city": null,
71
+ "street": null,
72
+ "street2": null,
73
+ "postal": null,
74
+ "founded": null,
75
+ "startDate": "2013-05-31",
76
+ "endDate": "2013-10-31",
77
+ "size": null,
78
+ "industry": null,
79
+ "companyType": null,
80
+ "country": null
81
+ },
82
+ {
83
+ "companyName": "Russell Commission",
84
+ "position": "Youth Advisory Board Member",
85
+ "socialLink": null,
86
+ "site": null,
87
+ "locality": null,
88
+ "state": null,
89
+ "city": null,
90
+ "street": null,
91
+ "street2": null,
92
+ "postal": null,
93
+ "founded": null,
94
+ "startDate": "2004-06-30",
95
+ "endDate": "2006-06-30",
96
+ "size": null,
97
+ "industry": null,
98
+ "companyType": null,
99
+ "country": null
100
+ }
101
+ ],
102
+ "lastUpdateDate": "2018-02-07 10:12:28"
103
+ }
@@ -0,0 +1,4 @@
1
+ {
2
+ "success": true,
3
+ "message": "We couldn't find profile with this email"
4
+ }
@@ -0,0 +1 @@
1
+ {"success":false,"errors":"Prospect with email 'no.one.home@octagon.com' not found"}
@@ -0,0 +1,55 @@
1
+ {
2
+ "success": true,
3
+ "list": {
4
+ "name": "Lead LIST",
5
+ "contacts": 3,
6
+ "creationDate": {
7
+ "date": "2020-05-19 17:34:39.000000",
8
+ "timezone_type": 3,
9
+ "timezone": "UTC"
10
+ },
11
+ "emailsCount": []
12
+ },
13
+ "prospects": [
14
+ {
15
+ "id": "226db935fc93422496fda5d5209e8cbf77cc77ec685891706028009b86608f7ce5877a3faf",
16
+ "name": "Andrew Garfiled",
17
+ "firstName": "Andrew",
18
+ "lastName": "Garfiled",
19
+ "emails": [
20
+ {
21
+ "email": "andrewexp@exp.com",
22
+ "probability": 99,
23
+ "isVerified": null,
24
+ "jobStatus": "any",
25
+ "domainType": "linkedin_email",
26
+ "isValidFormat": null,
27
+ "isDisposable": null,
28
+ "isWebmail": null,
29
+ "isGibberish": null,
30
+ "smtpStatus": null
31
+ }
32
+ ]
33
+ },
34
+ {
35
+ "id": "f20d30219b039d1408d837a748a1e2ab843c97e65080f6cf8fa7d948477d9093d87413f05f",
36
+ "name": "John Doe",
37
+ "firstName": "John",
38
+ "lastName": "Doe",
39
+ "emails": [
40
+ {
41
+ "email": "johndoe@gmail.com",
42
+ "probability": 99,
43
+ "isVerified": null,
44
+ "jobStatus": "any",
45
+ "domainType": "linkedin_email",
46
+ "isValidFormat": true,
47
+ "isDisposable": false,
48
+ "isWebmail": true,
49
+ "isGibberish": false,
50
+ "smtpStatus": 3
51
+ }
52
+ ]
53
+ }
54
+ ]
55
+ }
@@ -0,0 +1,55 @@
1
+ {
2
+ "success": true,
3
+ "list": {
4
+ "name": "Lead LIST",
5
+ "contacts": 3,
6
+ "creationDate": {
7
+ "date": "2020-05-19 17:34:39.000000",
8
+ "timezone_type": 3,
9
+ "timezone": "UTC"
10
+ },
11
+ "emailsCount": []
12
+ },
13
+ "prospects": [
14
+ {
15
+ "id": "226db935fc93422496fda5d5209e8cbf77cc77ec685891706028009b86608f7ce5877a3faf",
16
+ "name": "Andrew Garfiled",
17
+ "firstName": "Andrew",
18
+ "lastName": "Garfiled",
19
+ "emails": [
20
+ {
21
+ "email": "andrewexp@exp.com",
22
+ "probability": 99,
23
+ "isVerified": null,
24
+ "jobStatus": "any",
25
+ "domainType": "linkedin_email",
26
+ "isValidFormat": null,
27
+ "isDisposable": null,
28
+ "isWebmail": null,
29
+ "isGibberish": null,
30
+ "smtpStatus": null
31
+ }
32
+ ]
33
+ },
34
+ {
35
+ "id": "f20d30219b039d1408d837a748a1e2ab843c97e65080f6cf8fa7d948477d9093d87413f05f",
36
+ "name": "John Doe",
37
+ "firstName": "John",
38
+ "lastName": "Doe",
39
+ "emails": [
40
+ {
41
+ "email": "johndoe@gmail.com",
42
+ "probability": 99,
43
+ "isVerified": null,
44
+ "jobStatus": "any",
45
+ "domainType": "linkedin_email",
46
+ "isValidFormat": true,
47
+ "isDisposable": false,
48
+ "isWebmail": true,
49
+ "isGibberish": false,
50
+ "smtpStatus": 3
51
+ }
52
+ ]
53
+ }
54
+ ]
55
+ }
data/lib/snov/version.rb CHANGED
@@ -1,3 +1,3 @@
1
1
  module Snov
2
- VERSION = "0.2.2"
2
+ VERSION = "0.3.2"
3
3
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: snov
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.2.2
4
+ version: 0.3.2
5
5
  platform: ruby
6
6
  authors:
7
7
  - Grant Petersen-Speelman
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2021-01-11 00:00:00.000000000 Z
11
+ date: 2021-02-15 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: activemodel
@@ -134,11 +134,16 @@ files:
134
134
  - lib/snov/client.rb
135
135
  - lib/snov/domain_search.rb
136
136
  - lib/snov/fake_client.rb
137
- - lib/snov/fake_client/get_v1_get-user-lists.json
138
- - lib/snov/fake_client/post_v1_get-profile-by-email.json
139
- - lib/snov/fake_client/post_v1_get-prospects-by-email.json
140
- - lib/snov/fake_client/post_v1_prospect-list.json
141
- - lib/snov/fake_client/post_v2_domain-emails-with-info.json
137
+ - lib/snov/fake_client/get_v1_get-user-lists/default.json
138
+ - lib/snov/fake_client/get_v2_domain-emails-with-info/domain=octagon_com.json
139
+ - lib/snov/fake_client/get_v2_domain-emails-with-info/not_found=true.json
140
+ - lib/snov/fake_client/post_v1_get-profile-by-email/email=lizi_hamer@octagon_com.json
141
+ - lib/snov/fake_client/post_v1_get-profile-by-email/not_found=true.json
142
+ - lib/snov/fake_client/post_v1_get-prospects-by-email/email=gavin_vanrooyen@octagon_com.json
143
+ - lib/snov/fake_client/post_v1_get-prospects-by-email/not_found=true.json
144
+ - lib/snov/fake_client/post_v1_prospect-list/listId=1479070&page=1&perPage=100.json
145
+ - lib/snov/fake_client/post_v1_prospect-list/listId=1505383&page=1&perPage=100.json
146
+ - lib/snov/fake_client/post_v1_prospect-list/listId=1818597&page=1&perPage=100.json
142
147
  - lib/snov/get_all_prospects_from_list.rb
143
148
  - lib/snov/get_profile_by_email.rb
144
149
  - lib/snov/get_prospect_list.rb
@@ -1,103 +0,0 @@
1
- {
2
- "success": true,
3
- "id": 301592,
4
- "source": "linkedIn",
5
- "name": "Lizi Hamer",
6
- "firstName": "Lizi",
7
- "lastName": "Hamer",
8
- "logo": "https://app.snov.io/img/peoples/010fcf23c70dfa68d880545ec89a9215.jpg",
9
- "industry": null,
10
- "country": "Singapore",
11
- "locality": "Singapore",
12
- "social": [
13
- {
14
- "link": "https://www.linkedin.com/in/lizihamer/",
15
- "type": "linkedIn"
16
- },
17
- {
18
- "link": "https://twitter.com/LiziHamer",
19
- "type": "twitter"
20
- }
21
- ],
22
- "currentJobs": [
23
- {
24
- "companyName": "Octagon",
25
- "position": "Regional Creative Director",
26
- "socialLink": "https://www.linkedin.com/company/165282",
27
- "site": "www.octagon.com",
28
- "locality": "Greater New York City Area",
29
- "state": "Connecticut",
30
- "city": "Stamford",
31
- "street": "290 Harbor Dr",
32
- "street2": "2nd Floor",
33
- "postal": "06902",
34
- "founded": "1983",
35
- "startDate": "2016-01-31",
36
- "endDate": null,
37
- "size": "1-10",
38
- "industry": "Marketing and Advertising",
39
- "companyType": "Public Company",
40
- "country": "United States"
41
- },
42
- {
43
- "companyName": "SisuGirls",
44
- "position": "Co Founder",
45
- "socialLink": "https://www.linkedin.com/company/3841118",
46
- "site": "http://www.sisugirls.org",
47
- "locality": null,
48
- "state": "SG",
49
- "city": "Singapore",
50
- "street": "33-03 Hong Leong Building",
51
- "street2": null,
52
- "postal": null,
53
- "founded": "2014",
54
- "startDate": "2015-07-31",
55
- "endDate": null,
56
- "size": "1-10",
57
- "industry": "Health, Wellness and Fitness",
58
- "companyType": null,
59
- "country": "Singapore"
60
- }
61
- ],
62
- "previousJobs": [
63
- {
64
- "companyName": "Fusion Co-innovation Labs",
65
- "position": "Creative Entrepreneur",
66
- "socialLink": null,
67
- "site": null,
68
- "locality": null,
69
- "state": null,
70
- "city": null,
71
- "street": null,
72
- "street2": null,
73
- "postal": null,
74
- "founded": null,
75
- "startDate": "2013-05-31",
76
- "endDate": "2013-10-31",
77
- "size": null,
78
- "industry": null,
79
- "companyType": null,
80
- "country": null
81
- },
82
- {
83
- "companyName": "Russell Commission",
84
- "position": "Youth Advisory Board Member",
85
- "socialLink": null,
86
- "site": null,
87
- "locality": null,
88
- "state": null,
89
- "city": null,
90
- "street": null,
91
- "street2": null,
92
- "postal": null,
93
- "founded": null,
94
- "startDate": "2004-06-30",
95
- "endDate": "2006-06-30",
96
- "size": null,
97
- "industry": null,
98
- "companyType": null,
99
- "country": null
100
- }
101
- ],
102
- "lastUpdateDate": "2018-02-07 10:12:28"
103
- }