snov 0.1.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.
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA256:
3
+ metadata.gz: 66024313d2ed01c16ffeeaec3f536f93b00957250903c5d18e3d30458ba4e413
4
+ data.tar.gz: e915c4231981bd3fad2d4fd6a7eb18dfcdcc2005058d97202a7374cc76df25a0
5
+ SHA512:
6
+ metadata.gz: ba368d2617e6b8ca49cb9bcde3c75ed8b43348dd187f7a1f60a459b596f9e1999b282eb940114dae99354330583cc33dcc90e95c8c0a1b13d2fe482daf713058
7
+ data.tar.gz: eef471ba1b9bdcf927fa489addafaf3283f083a6067d7a7b3bba8b414dcbccbf31edc2769ad405fb4abc59cb3129753dee699ab86080faced65e1855435ee4d5
@@ -0,0 +1,13 @@
1
+ /.bundle/
2
+ /.yardoc
3
+ /_yardoc/
4
+ /coverage/
5
+ /doc/
6
+ /pkg/
7
+ /spec/reports/
8
+ /tmp/
9
+
10
+ /.rspec_status
11
+ /gems.locked
12
+ /vendor/bundle
13
+ /.vscode
data/.rspec ADDED
@@ -0,0 +1,3 @@
1
+ --format documentation
2
+ --color
3
+ --require spec_helper
@@ -0,0 +1,13 @@
1
+ require:
2
+ - rubocop-rspec
3
+
4
+ AllCops:
5
+ NewCops: enable
6
+ Exclude:
7
+ - 'vendor/**/*'
8
+
9
+ Style:
10
+ Enabled: false
11
+
12
+ Metrics/AbcSize:
13
+ Max: 25
@@ -0,0 +1,15 @@
1
+ ---
2
+ language: ruby
3
+ gemfile: gems.rb
4
+ cache: bundler
5
+ rvm:
6
+ - 2.4
7
+ - 2.7
8
+ before_install:
9
+ - yes | gem update --system --force
10
+ - gem install bundler
11
+
12
+ script:
13
+ - bundle install
14
+ - bundle exec rake
15
+ - bin/rubocop
@@ -0,0 +1,5 @@
1
+ ## [0.1.0]
2
+ * GetUserLists
3
+ * GetProspectList
4
+ * GetAllProspectsFromList
5
+ * GetProspectsByEmail
@@ -0,0 +1,143 @@
1
+ [![Build Status](https://travis-ci.org/NEXL-LTS/snov-ruby.svg?branch=main)](https://travis-ci.org/NEXL-LTS/snov-ruby)
2
+
3
+ ## Installation
4
+
5
+ Add this line to your application's Gemfile:
6
+
7
+ ```ruby
8
+ gem 'snov'
9
+ ```
10
+
11
+ And then execute:
12
+
13
+ $ bundle install
14
+
15
+ Or install it yourself as:
16
+
17
+ $ gem install snov
18
+
19
+ ## Usage
20
+
21
+ set `SNOV_USER_ID` and `SNOV_SECRET` environment variables
22
+
23
+
24
+ ### GetProfileByEmail
25
+
26
+ see https://snov.io/api#GetProfileByEmail
27
+
28
+ ```ruby
29
+ prospect = Snov::GetProfileByEmail.new(email: "gavin.vanrooyen@octagon.com")
30
+
31
+ puts prospect.success
32
+ puts prospect.id
33
+ puts prospect.name
34
+ puts prospect.first_name
35
+ puts prospect.last_name
36
+ puts prospect.industry
37
+ puts prospect.country
38
+ puts prospect.locality
39
+ prospect.social.each do |social_info|
40
+ puts social.link
41
+ puts social.type
42
+ end
43
+ prospect.current_jobs.each do |social_info|
44
+ puts social.company_name
45
+ puts social.position
46
+ # etc
47
+ end
48
+ # etc
49
+ ```
50
+
51
+ ### GetProspectList
52
+
53
+ see https://snov.io/api#FindProspectbyEmail
54
+
55
+ ```ruby
56
+ prospects = Snov::GetProspectsByEmail.new(email: "gavin.vanrooyen@octagon.com")
57
+ prospects.each do |prospect|
58
+ puts prospect.id
59
+ puts prospect.name
60
+ puts prospect.first_name
61
+ puts prospect.last_name
62
+ puts prospect.industry
63
+ puts prospect.country
64
+ puts prospect.locality
65
+ prospect.social.each do |social_info|
66
+ puts social.link
67
+ puts social.type
68
+ end
69
+ # etc
70
+ end
71
+ ```
72
+
73
+ ### GetUserLists
74
+
75
+ see https://snov.io/api#UserLists
76
+
77
+ ```ruby
78
+ lists = Snov::GetUserLists.new
79
+ lists.each do |list|
80
+ puts list.id
81
+ puts list.name
82
+ puts list.is_deleted
83
+ puts list.contacts
84
+ puts list.creation_date.date
85
+ puts list.deletion_date.date
86
+ end
87
+ ```
88
+
89
+ ### GetProspectList
90
+
91
+ see https://snov.io/api#ViewProspectsInList
92
+
93
+ ```ruby
94
+ prospects = Snov::GetProspectList.new(list_id: 1, page: 1, per_page: 100)
95
+ prospects.each do |prospect|
96
+ puts prospect.id
97
+ puts prospect.name
98
+ puts prospect.first_name
99
+ puts prospect.last_name
100
+ puts prospect.source
101
+ prospect.emails.each do |email_info|
102
+ puts email_info.email
103
+ puts email_info.is_verified
104
+ puts email_info.job_status
105
+ # etc
106
+ end
107
+ end
108
+ ```
109
+
110
+ ### GetAllProspectsFromList
111
+
112
+ convenience wrapper for `GetProspectList` to get all the prospects on all the pages
113
+
114
+ see https://snov.io/api#ViewProspectsInList
115
+
116
+ ```ruby
117
+ prospects = Snov::GetAllProspectsFromList.new(list_id: 1)
118
+ prospects.each do |prospect|
119
+ puts prospect.id
120
+ puts prospect.name
121
+ puts prospect.first_name
122
+ puts prospect.last_name
123
+ puts prospect.source
124
+ prospect.emails.each do |email_info|
125
+ puts email_info.email
126
+ puts email_info.is_verified
127
+ puts email_info.job_status
128
+ # etc
129
+ end
130
+ end
131
+ ```
132
+
133
+
134
+ ## Development
135
+
136
+ After checking out the repo, run `bin/setup` to install dependencies. Then, run `bin/rspec` to run the tests. You can also run `bin/console` for an interactive prompt that will allow you to experiment.
137
+
138
+ To install this gem onto your local machine, run `bundle exec rake install`. To release a new version, update the version number in `version.rb`, and then run `bundle exec rake release`, which will create a git tag for the version, push git commits and tags, and push the `.gem` file to [rubygems.org](https://rubygems.org).
139
+
140
+ ## Contributing
141
+
142
+ Bug reports and pull requests are welcome on GitHub at https://github.com/[USERNAME]/snov.
143
+
@@ -0,0 +1,6 @@
1
+ require "bundler/gem_tasks"
2
+ require "rspec/core/rake_task"
3
+
4
+ RSpec::Core::RakeTask.new(:spec)
5
+
6
+ task :default => :spec
@@ -0,0 +1,14 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require "bundler/setup"
4
+ require "snov"
5
+
6
+ # You can add fixtures and/or initialization code here to make experimenting
7
+ # with your gem easier. You can also use a different console, if you like.
8
+
9
+ # (If you use this, don't forget to add pry to your Gemfile!)
10
+ # require "pry"
11
+ # Pry.start
12
+
13
+ require "irb"
14
+ IRB.start(__FILE__)
@@ -0,0 +1,29 @@
1
+ #!/usr/bin/env ruby
2
+ # frozen_string_literal: true
3
+
4
+ #
5
+ # This file was generated by Bundler.
6
+ #
7
+ # The application 'rspec' is installed as part of a gem, and
8
+ # this file is here to facilitate running it.
9
+ #
10
+
11
+ require "pathname"
12
+ ENV["BUNDLE_GEMFILE"] ||= File.expand_path("../../gems.rb",
13
+ Pathname.new(__FILE__).realpath)
14
+
15
+ bundle_binstub = File.expand_path("../bundle", __FILE__)
16
+
17
+ if File.file?(bundle_binstub)
18
+ if File.read(bundle_binstub, 300) =~ /This file was generated by Bundler/
19
+ load(bundle_binstub)
20
+ else
21
+ abort("Your `bin/bundle` was not generated by Bundler, so this binstub cannot run.
22
+ Replace `bin/bundle` by running `bundle binstubs bundler --force`, then run this command again.")
23
+ end
24
+ end
25
+
26
+ require "rubygems"
27
+ require "bundler/setup"
28
+
29
+ load Gem.bin_path("rspec-core", "rspec")
@@ -0,0 +1,29 @@
1
+ #!/usr/bin/env ruby
2
+ # frozen_string_literal: true
3
+
4
+ #
5
+ # This file was generated by Bundler.
6
+ #
7
+ # The application 'rubocop' is installed as part of a gem, and
8
+ # this file is here to facilitate running it.
9
+ #
10
+
11
+ require "pathname"
12
+ ENV["BUNDLE_GEMFILE"] ||= File.expand_path("../../gems.rb",
13
+ Pathname.new(__FILE__).realpath)
14
+
15
+ bundle_binstub = File.expand_path("../bundle", __FILE__)
16
+
17
+ if File.file?(bundle_binstub)
18
+ if File.read(bundle_binstub, 300) =~ /This file was generated by Bundler/
19
+ load(bundle_binstub)
20
+ else
21
+ abort("Your `bin/bundle` was not generated by Bundler, so this binstub cannot run.
22
+ Replace `bin/bundle` by running `bundle binstubs bundler --force`, then run this command again.")
23
+ end
24
+ end
25
+
26
+ require "rubygems"
27
+ require "bundler/setup"
28
+
29
+ load Gem.bin_path("rubocop", "rubocop")
@@ -0,0 +1,8 @@
1
+ #!/usr/bin/env bash
2
+ set -euo pipefail
3
+ IFS=$'\n\t'
4
+ set -vx
5
+
6
+ bundle install
7
+
8
+ # Do any other automated setup that you need to do here
data/gems.rb ADDED
@@ -0,0 +1,11 @@
1
+ source "https://rubygems.org"
2
+
3
+ # Specify your gem's dependencies in snov.gemspec
4
+ gemspec
5
+
6
+ gem "rake", "~> 12.0"
7
+ gem "rspec", "~> 3.0"
8
+ gem "rubocop"
9
+ gem "rubocop-rspec"
10
+ gem "simplecov"
11
+ gem "webmock"
@@ -0,0 +1,26 @@
1
+ require "snov/version"
2
+
3
+ module Snov
4
+ class Error < StandardError; end
5
+
6
+ def self.client
7
+ if !use_fake?
8
+ Client.new(client_id: ENV['SNOV_USER_ID'], client_secret: ENV['SNOV_SECRET'])
9
+ else
10
+ FakeClient.new
11
+ end
12
+ end
13
+
14
+ def self.use_fake?
15
+ ENV['SNOV_USE_FAKE'].present? || (!ENV.key?('SNOV_USER_ID') && !ENV.key?('SNOV_SECRET'))
16
+ end
17
+ end
18
+
19
+ require 'active_model'
20
+ require 'snov/client'
21
+ require 'snov/fake_client'
22
+ require 'snov/get_profile_by_email'
23
+ require 'snov/get_all_prospects_from_list'
24
+ require 'snov/get_prospects_by_email'
25
+ require 'snov/get_prospect_list'
26
+ require 'snov/get_user_lists'
@@ -0,0 +1,104 @@
1
+ require 'faraday'
2
+ require 'multi_json'
3
+
4
+ module Snov
5
+ class Client
6
+ class SnovError < ::Snov::Error; end
7
+
8
+ class TimedOut < SnovError; end
9
+
10
+ class UnauthorizedError < SnovError; end
11
+
12
+ class BadGatewayError < SnovError; end
13
+
14
+ class ForbiddenError < SnovError; end
15
+
16
+ class GatewayTimeOut < SnovError; end
17
+
18
+ class BadRequest < SnovError; end
19
+
20
+ class AuthError < SnovError; end
21
+
22
+ class MethodNotAllowed < SnovError; end
23
+ ERROR_CLASSES = { 401 => UnauthorizedError, 502 => BadGatewayError, 403 => ForbiddenError,
24
+ 504 => GatewayTimeOut, 400 => BadRequest, 405 => MethodNotAllowed }
25
+
26
+ def initialize(client_id:, client_secret:, access_token: nil, timeout_seconds: 60)
27
+ self.client_id = client_id.to_str
28
+ self.client_secret = client_secret.to_str
29
+ @access_token = access_token
30
+ @timeout_seconds = timeout_seconds.to_int
31
+ end
32
+
33
+ def get(path, params = {})
34
+ resp = conn.get(path) do |req|
35
+ req.body = MultiJson.dump(params.merge('access_token' => access_token))
36
+ req.options.timeout = timeout_seconds # open/read timeout in seconds
37
+ req.options.open_timeout = timeout_seconds # connection open timeout in seconds
38
+ end
39
+ parse_response(resp, path, params)
40
+ rescue Faraday::TimeoutError, Timeout::Error => e
41
+ raise TimedOut, e.message
42
+ end
43
+
44
+ def post(path, params = {})
45
+ resp = conn.post(path) do |req|
46
+ req.body = MultiJson.dump(params.merge('access_token' => access_token))
47
+ req.options.timeout = timeout_seconds # open/read timeout in seconds
48
+ req.options.open_timeout = timeout_seconds # connection open timeout in seconds
49
+ end
50
+ parse_response(resp, path, params)
51
+ rescue Faraday::TimeoutError, Timeout::Error => e
52
+ raise TimedOut, e.message
53
+ end
54
+
55
+ private
56
+
57
+ def parse_response(resp, path, _params)
58
+ unless resp.success?
59
+ raise ERROR_CLASSES.fetch(resp.status, SnovError),
60
+ "#{path} (#{resp.status})"
61
+ end
62
+ MultiJson.load(resp.body)
63
+ end
64
+
65
+ attr_accessor :client_id, :client_secret, :timeout_seconds
66
+
67
+ def access_token
68
+ return @access_token if @access_token
69
+
70
+ @access_token = generate_access_token
71
+ end
72
+
73
+ def access_token_params
74
+ { 'grant_type' => 'client_credentials', 'client_id' => client_id, 'client_secret' => client_secret }
75
+ end
76
+
77
+ def generate_access_token
78
+ resp = conn.post('/v1/oauth/access_token') do |req|
79
+ req.body = MultiJson.dump(access_token_params)
80
+ req.options.timeout = timeout_seconds # open/read timeout in seconds
81
+ req.options.open_timeout = timeout_seconds # connection open timeout in seconds
82
+ end
83
+ handle_error(resp, "POST /v1/oauth/access_token")
84
+ raise AuthError, 'Snov auth failed' if resp.body.blank?
85
+
86
+ MultiJson.load(resp.body).fetch("access_token")
87
+ rescue Timeout::Error => e
88
+ raise TimedOut, e.message
89
+ end
90
+
91
+ def handle_error(resp, prefix)
92
+ return if resp.success?
93
+
94
+ raise ERROR_CLASSES.fetch(resp.status, SnovError), "#{prefix} (#{resp.status})"
95
+ end
96
+
97
+ def conn
98
+ @conn ||= Faraday.new(
99
+ url: 'https://api.snov.io',
100
+ headers: { 'Content-Type' => 'application/json' }
101
+ )
102
+ end
103
+ end
104
+ end
@@ -0,0 +1,14 @@
1
+ module Snov
2
+ class FakeClient
3
+ def get(path)
4
+ data = File.read("#{__dir__}/fake_client/get#{path.tr("/", "_")}.json")
5
+ MultiJson.load(data)
6
+ end
7
+
8
+ def post(path, params = {})
9
+ params.to_a.map(&:join).join("&")
10
+ data = File.read("#{__dir__}/fake_client/post#{path.tr("/", "_")}.json")
11
+ MultiJson.load(data)
12
+ end
13
+ end
14
+ end
@@ -0,0 +1,46 @@
1
+ [
2
+ {
3
+ "id": 1818597,
4
+ "name": "FirstSend",
5
+ "contacts": 1,
6
+ "isDeleted": false,
7
+ "creationDate": {
8
+ "date": "2020-04-07 08:25:44.000000",
9
+ "timezone_type": 3,
10
+ "timezone": "UTC"
11
+ },
12
+ "deletionDate": null
13
+ },
14
+ {
15
+ "id": 1505383,
16
+ "name": "All prospects",
17
+ "contacts": 10,
18
+ "isDeleted": true,
19
+ "creationDate": {
20
+ "date": "2019-12-17 15:07:30.000000",
21
+ "timezone_type": 3,
22
+ "timezone": "UTC"
23
+ },
24
+ "deletionDate": {
25
+ "date": "2020-02-17 14:05:44.000000",
26
+ "timezone_type": 3,
27
+ "timezone": "UTC"
28
+ }
29
+ },
30
+ {
31
+ "id": 1479070,
32
+ "name": "EMAIL",
33
+ "contacts": 13,
34
+ "isDeleted": true,
35
+ "creationDate": {
36
+ "date": "2019-12-06 10:51:01.000000",
37
+ "timezone_type": 3,
38
+ "timezone": "UTC"
39
+ },
40
+ "deletionDate": {
41
+ "date": "2020-02-17 14:05:48.000000",
42
+ "timezone_type": 3,
43
+ "timezone": "UTC"
44
+ }
45
+ }
46
+ ]
@@ -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,75 @@
1
+ {
2
+ "success": true,
3
+ "data": [
4
+ {
5
+ "id": "xusD3-T_K5IktGoaa8Jc8A==",
6
+ "name": "Gavin Vanrooyen",
7
+ "firstName": "Gavin",
8
+ "lastName": "Vanrooyen",
9
+ "industry": "Entertainment",
10
+ "country": "United States",
11
+ "locality": "Greater Atlanta Area",
12
+ "social": [
13
+ {
14
+ "link": "https:\/\/www.linkedin.com\/in\/gavin-vanrooyen-809073755\/",
15
+ "type": "linkedIn"
16
+ }
17
+ ],
18
+ "lastUpdateDate": {
19
+ "date": "2019-09-11 12:37:58.000000",
20
+ "timezone_type": 3,
21
+ "timezone": "UTC"
22
+ },
23
+ "currentJob": [
24
+ {
25
+ "companyName": "Octagon",
26
+ "position": "Senior Brand Director",
27
+ "socialLink": "https:\/\/www.linkedin.com\/company\/659333",
28
+ "site": "http:\/\/octagon.com",
29
+ "locality": "United States",
30
+ "state": null,
31
+ "city": null,
32
+ "street": null,
33
+ "street2": null,
34
+ "postal": null,
35
+ "founded": null,
36
+ "startDate": "2018-07-31",
37
+ "endDate": null,
38
+ "size": "1-10",
39
+ "industry": "Entertainment",
40
+ "companyType": "Public Company",
41
+ "country": "United States"
42
+ }
43
+ ],
44
+ "previousJob": [
45
+ {
46
+ "companyName": "UPS",
47
+ "position": "Manager, Sponsorships and Events",
48
+ "socialLink": "https:\/\/www.linkedin.com\/company\/1523574",
49
+ "site": "http:\/\/www.ups.com\/",
50
+ "locality": "United States",
51
+ "state": "GA",
52
+ "city": "Atlanta",
53
+ "street": "55 Glenlake Parkway, NE",
54
+ "street2": null,
55
+ "postal": "30328",
56
+ "founded": "1907",
57
+ "startDate": null,
58
+ "endDate": null,
59
+ "size": "10001+",
60
+ "industry": "Logistics and Supply Chain",
61
+ "companyType": "Public Company",
62
+ "country": "United States"
63
+ }
64
+ ],
65
+ "lists": [
66
+ {
67
+ "id": 1250344,
68
+ "name": "People List"
69
+ }
70
+ ],
71
+ "campaigns": []
72
+ }
73
+ ]
74
+ }
75
+
@@ -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,26 @@
1
+ module Snov
2
+ class GetAllProspectsFromList
3
+ include Enumerable
4
+
5
+ attr_reader :client, :list_id, :max, :per_page
6
+
7
+ def initialize(list_id:, max: 9999, per_page: 100, client: Snov.client)
8
+ @client = client
9
+ @list_id = list_id
10
+ @max = max.to_int
11
+ @per_page = per_page
12
+ end
13
+
14
+ def each(&block)
15
+ (1..max).each do |page|
16
+ list = GetProspectList.new(list_id: list_id, page: page, per_page: per_page,
17
+ client: client)
18
+ prospects = list.prospects
19
+
20
+ prospects.each(&block)
21
+
22
+ break if prospects.size < per_page
23
+ end
24
+ end
25
+ end
26
+ end
@@ -0,0 +1,84 @@
1
+ require 'active_support/core_ext/array'
2
+
3
+ module Snov
4
+ class GetProfileByEmail
5
+ attr_reader :client, :email
6
+
7
+ def initialize(email:, client: Snov.client)
8
+ @client = client
9
+ @email = email.to_str
10
+ end
11
+
12
+ def each(&block)
13
+ prospects.each(&block)
14
+ end
15
+
16
+ def prospect
17
+ @prospect ||= ProspectResult.new(raw_result)
18
+ end
19
+
20
+ def method_missing(method_name, *arguments, &block)
21
+ if prospect.respond_to?(method_name)
22
+ prospect.public_send(method_name, *arguments, &block)
23
+ else
24
+ super
25
+ end
26
+ end
27
+
28
+ def respond_to_missing?(method_name, include_private = false)
29
+ prospect.respond_to?(method_name) || super
30
+ end
31
+
32
+ def raw_result
33
+ @raw_result ||= client.post("/v1/get-profile-by-email",
34
+ 'email' => email)
35
+ .deep_transform_keys! { |key| key.underscore }
36
+ end
37
+
38
+ class Job
39
+ include ActiveModel::Model
40
+
41
+ attr_accessor :company_name, :position, :social_link, :site, :locality, :state, :city
42
+ attr_accessor :street, :street2, :postal, :founded, :start_date, :end_date, :size
43
+ attr_accessor :industry, :company_type, :country
44
+ end
45
+
46
+ class Social
47
+ include ActiveModel::Model
48
+
49
+ attr_accessor :link, :type
50
+ end
51
+
52
+ class List
53
+ include ActiveModel::Model
54
+
55
+ attr_accessor :id, :name
56
+ end
57
+
58
+ class ProspectResult
59
+ include ActiveModel::Model
60
+
61
+ attr_accessor :id, :name, :first_name, :last_name, :industry, :country, :locality, :success, :source
62
+ attr_accessor :logo, :last_update_date, :message
63
+ attr_reader :social, :current_jobs, :previous_jobs
64
+
65
+ def social=(val)
66
+ @social = Array.wrap(val).map do |rel|
67
+ Social.new(rel)
68
+ end
69
+ end
70
+
71
+ def current_jobs=(val)
72
+ @current_jobs = Array.wrap(val).map do |rel|
73
+ Job.new(rel)
74
+ end
75
+ end
76
+
77
+ def previous_jobs=(val)
78
+ @previous_jobs = Array.wrap(val).map do |rel|
79
+ Job.new(rel)
80
+ end
81
+ end
82
+ end
83
+ end
84
+ end
@@ -0,0 +1,59 @@
1
+ require 'active_support/core_ext/array'
2
+
3
+ module Snov
4
+ class GetProspectList
5
+ include Enumerable
6
+
7
+ attr_reader :client, :list_id, :page, :per_page
8
+
9
+ def initialize(list_id:, page:, per_page: 100, client: Snov.client)
10
+ @client = client
11
+ @list_id = list_id
12
+ @page = page
13
+ @per_page = per_page
14
+ end
15
+
16
+ def each(&block)
17
+ prospects.each(&block)
18
+ end
19
+
20
+ def prospects
21
+ @prospects ||= raw_result.fetch('prospects').map do |result|
22
+ ProspectResult.new(result)
23
+ end
24
+ end
25
+
26
+ def raw_result
27
+ @raw_result ||= client.post("/v1/prospect-list",
28
+ 'listId' => list_id.to_s,
29
+ 'page' => page.to_s,
30
+ 'perPage' => per_page.to_s)
31
+ .deep_transform_keys! { |key| key.underscore }
32
+ end
33
+
34
+ class ProspectEmail
35
+ include ActiveModel::Model
36
+
37
+ attr_accessor :email, :is_verified, :job_status, :domain_type, :is_valid_format
38
+ attr_accessor :is_disposable, :is_webmail, :is_gibberish, :smtp_status, :probability
39
+ attr_accessor :status_type_text, :status_verify_text, :email_verify_text
40
+ end
41
+
42
+ class ProspectResult
43
+ include ActiveModel::Model
44
+
45
+ attr_accessor :id, :name, :first_name, :last_name, :source
46
+ attr_reader :emails
47
+
48
+ def emails=(val)
49
+ @emails = Array.wrap(val).map do |rel|
50
+ ProspectEmail.new(rel)
51
+ end
52
+ end
53
+
54
+ def email
55
+ emails.first&.email
56
+ end
57
+ end
58
+ end
59
+ end
@@ -0,0 +1,92 @@
1
+ require 'active_support/core_ext/array'
2
+ require_relative 'types/date_details'
3
+
4
+ module Snov
5
+ class GetProspectsByEmail
6
+ include Enumerable
7
+
8
+ attr_reader :client, :email
9
+
10
+ def initialize(email:, client: Snov.client)
11
+ @client = client
12
+ @email = email.to_str
13
+ end
14
+
15
+ def each(&block)
16
+ prospects.each(&block)
17
+ end
18
+
19
+ def prospects
20
+ @prospects ||= raw_result.fetch('data', []).map do |result|
21
+ ProspectResult.new(result)
22
+ end
23
+ end
24
+
25
+ def raw_result
26
+ @raw_result ||= client.post("/v1/get-prospects-by-email",
27
+ 'email' => email)
28
+ .deep_transform_keys! { |key| key.underscore }
29
+ end
30
+
31
+ class Job
32
+ include ActiveModel::Model
33
+
34
+ attr_accessor :company_name, :position, :social_link, :site, :locality, :state, :city
35
+ attr_accessor :street, :street2, :postal, :founded, :start_date, :end_date, :size
36
+ attr_accessor :industry, :company_type, :country
37
+ end
38
+
39
+ class Social
40
+ include ActiveModel::Model
41
+
42
+ attr_accessor :link, :type
43
+ end
44
+
45
+ class List
46
+ include ActiveModel::Model
47
+
48
+ attr_accessor :id, :name
49
+ end
50
+
51
+ class ProspectResult
52
+ include ActiveModel::Model
53
+
54
+ attr_accessor :id, :name, :first_name, :last_name, :industry, :country, :locality
55
+ attr_reader :social, :current_job, :previous_job, :lists, :campaigns, :last_update_date
56
+
57
+ def social=(val)
58
+ @social = Array.wrap(val).map do |rel|
59
+ Social.new(rel)
60
+ end
61
+ end
62
+
63
+ def current_job=(val)
64
+ @current_job = Array.wrap(val).map do |rel|
65
+ Job.new(rel)
66
+ end
67
+ end
68
+
69
+ def previous_job=(val)
70
+ @previous_job = Array.wrap(val).map do |rel|
71
+ Job.new(rel)
72
+ end
73
+ end
74
+
75
+ def lists=(val)
76
+ @lists = Array.wrap(val).map do |rel|
77
+ List.new(rel)
78
+ end
79
+ end
80
+
81
+ def campaigns=(val)
82
+ @campaigns = Array.wrap(val).map do |rel|
83
+ OpenStruct.new(rel)
84
+ end
85
+ end
86
+
87
+ def last_update_date=(val)
88
+ @last_update_date = Types::DateDetails.new(val.to_hash)
89
+ end
90
+ end
91
+ end
92
+ end
@@ -0,0 +1,47 @@
1
+ require_relative 'types/date_details'
2
+
3
+ module Snov
4
+ class GetUserLists
5
+ include Enumerable
6
+
7
+ attr_reader :client
8
+
9
+ def initialize(client: Snov.client)
10
+ @client = client
11
+ end
12
+
13
+ def each(&block)
14
+ all.each(&block)
15
+ end
16
+
17
+ def all
18
+ @all ||= raw_result.map { |result| UserList.new(result) }
19
+ end
20
+
21
+ def raw_result
22
+ @raw_result ||= client.get("/v1/get-user-lists").map do |val|
23
+ val.deep_transform_keys! { |key| key.underscore }
24
+ end
25
+ end
26
+
27
+ class UserList
28
+ include ActiveModel::Model
29
+
30
+ attr_accessor :id, :name, :is_deleted, :contacts
31
+ attr_reader :creation_date, :deletion_date
32
+
33
+ def creation_date=(val)
34
+ @creation_date = Types::DateDetails.new(val.to_hash)
35
+ end
36
+
37
+ def deletion_date=(val)
38
+ @deletion_date = val
39
+ @deletion_date = Types::DateDetails.new(val.to_hash) if val
40
+ end
41
+
42
+ def to_h
43
+ { id: id, is_deleted: is_deleted, name: name, contacts: contacts }
44
+ end
45
+ end
46
+ end
47
+ end
@@ -0,0 +1,9 @@
1
+ module Snov
2
+ module Types
3
+ class DateDetails
4
+ include ActiveModel::Model
5
+
6
+ attr_accessor :date, :timezone_type, :timezone
7
+ end
8
+ end
9
+ end
@@ -0,0 +1,3 @@
1
+ module Snov
2
+ VERSION = "0.1.0"
3
+ end
@@ -0,0 +1,30 @@
1
+ require_relative 'lib/snov/version'
2
+
3
+ Gem::Specification.new do |spec|
4
+ spec.name = "snov"
5
+ spec.version = Snov::VERSION
6
+ spec.authors = ["Grant Petersen-Speelman"]
7
+ spec.email = ["grantspeelman@gmail.com"]
8
+
9
+ spec.summary = %q{Snov client to interact with snov api}
10
+ spec.description = %q{Snov client to interact with snov api}
11
+ spec.homepage = "https://github.com/NEXL-LTS/snov-ruby"
12
+ spec.required_ruby_version = Gem::Requirement.new(">= 2.4.0")
13
+
14
+ spec.metadata["homepage_uri"] = spec.homepage
15
+ spec.metadata["source_code_uri"] = "https://github.com/NEXL-LTS/snov-ruby"
16
+ spec.metadata["changelog_uri"] = "https://github.com/NEXL-LTS/snov-ruby/CHANGELOG.md"
17
+
18
+ # Specify which files should be added to the gem when it is released.
19
+ # The `git ls-files -z` loads the files in the RubyGem that have been added into git.
20
+ spec.files = Dir.chdir(File.expand_path('..', __FILE__)) do
21
+ `git ls-files -z`.split("\x0").reject { |f| f.match(%r{^(test|spec|features)/}) }
22
+ end
23
+ spec.bindir = "exe"
24
+ spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
25
+ spec.require_paths = ["lib"]
26
+ spec.add_dependency 'activemodel'
27
+ spec.add_dependency 'activesupport'
28
+ spec.add_dependency 'faraday'
29
+ spec.add_dependency 'multi_json'
30
+ end
metadata ADDED
@@ -0,0 +1,128 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: snov
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.0
5
+ platform: ruby
6
+ authors:
7
+ - Grant Petersen-Speelman
8
+ autorequire:
9
+ bindir: exe
10
+ cert_chain: []
11
+ date: 2020-11-28 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: activemodel
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - ">="
18
+ - !ruby/object:Gem::Version
19
+ version: '0'
20
+ type: :runtime
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - ">="
25
+ - !ruby/object:Gem::Version
26
+ version: '0'
27
+ - !ruby/object:Gem::Dependency
28
+ name: activesupport
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - ">="
32
+ - !ruby/object:Gem::Version
33
+ version: '0'
34
+ type: :runtime
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - ">="
39
+ - !ruby/object:Gem::Version
40
+ version: '0'
41
+ - !ruby/object:Gem::Dependency
42
+ name: faraday
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - ">="
46
+ - !ruby/object:Gem::Version
47
+ version: '0'
48
+ type: :runtime
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - ">="
53
+ - !ruby/object:Gem::Version
54
+ version: '0'
55
+ - !ruby/object:Gem::Dependency
56
+ name: multi_json
57
+ requirement: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - ">="
60
+ - !ruby/object:Gem::Version
61
+ version: '0'
62
+ type: :runtime
63
+ prerelease: false
64
+ version_requirements: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - ">="
67
+ - !ruby/object:Gem::Version
68
+ version: '0'
69
+ description: Snov client to interact with snov api
70
+ email:
71
+ - grantspeelman@gmail.com
72
+ executables: []
73
+ extensions: []
74
+ extra_rdoc_files: []
75
+ files:
76
+ - ".gitignore"
77
+ - ".rspec"
78
+ - ".rubocop.yml"
79
+ - ".travis.yml"
80
+ - CHANGELOG.md
81
+ - README.md
82
+ - Rakefile
83
+ - bin/console
84
+ - bin/rspec
85
+ - bin/rubocop
86
+ - bin/setup
87
+ - gems.rb
88
+ - lib/snov.rb
89
+ - lib/snov/client.rb
90
+ - lib/snov/fake_client.rb
91
+ - lib/snov/fake_client/get_v1_get-user-lists.json
92
+ - lib/snov/fake_client/post_v1_get-profile-by-email.json
93
+ - lib/snov/fake_client/post_v1_get-prospects-by-email.json
94
+ - lib/snov/fake_client/post_v1_prospect-list.json
95
+ - lib/snov/get_all_prospects_from_list.rb
96
+ - lib/snov/get_profile_by_email.rb
97
+ - lib/snov/get_prospect_list.rb
98
+ - lib/snov/get_prospects_by_email.rb
99
+ - lib/snov/get_user_lists.rb
100
+ - lib/snov/types/date_details.rb
101
+ - lib/snov/version.rb
102
+ - snov.gemspec
103
+ homepage: https://github.com/NEXL-LTS/snov-ruby
104
+ licenses: []
105
+ metadata:
106
+ homepage_uri: https://github.com/NEXL-LTS/snov-ruby
107
+ source_code_uri: https://github.com/NEXL-LTS/snov-ruby
108
+ changelog_uri: https://github.com/NEXL-LTS/snov-ruby/CHANGELOG.md
109
+ post_install_message:
110
+ rdoc_options: []
111
+ require_paths:
112
+ - lib
113
+ required_ruby_version: !ruby/object:Gem::Requirement
114
+ requirements:
115
+ - - ">="
116
+ - !ruby/object:Gem::Version
117
+ version: 2.4.0
118
+ required_rubygems_version: !ruby/object:Gem::Requirement
119
+ requirements:
120
+ - - ">="
121
+ - !ruby/object:Gem::Version
122
+ version: '0'
123
+ requirements: []
124
+ rubygems_version: 3.1.4
125
+ signing_key:
126
+ specification_version: 4
127
+ summary: Snov client to interact with snov api
128
+ test_files: []