linkedin2 0.0.16 → 0.0.17
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.coveralls.yml +2 -0
- data/.gitignore +2 -0
- data/.rspec +2 -2
- data/.travis.yml +1 -1
- data/README.md +3 -3
- data/Rakefile +1 -1
- data/lib/linkedin/api.rb +16 -0
- data/lib/linkedin/api/authentication.rb +5 -7
- data/lib/linkedin/api/companies.rb +16 -10
- data/lib/linkedin/api/invitation.rb +39 -0
- data/lib/linkedin/api/messaging.rb +24 -0
- data/lib/linkedin/api/network_updates.rb +5 -9
- data/lib/linkedin/api/people.rb +18 -0
- data/lib/linkedin/client.rb +34 -72
- data/lib/linkedin/configuration.rb +26 -11
- data/lib/linkedin/credentials.rb +27 -0
- data/lib/linkedin/errors.rb +45 -0
- data/lib/linkedin/faraday_middleware.rb +6 -4
- data/lib/linkedin/faraday_middleware/credentials_request.rb +31 -0
- data/lib/linkedin/faraday_middleware/{linkedin_error_response.rb → error_response.rb} +2 -10
- data/lib/linkedin/faraday_middleware/format_request.rb +17 -0
- data/lib/linkedin/faraday_middleware/user_agent_request.rb +10 -0
- data/lib/linkedin/fields.rb +58 -0
- data/lib/linkedin/industries.rb +199 -0
- data/lib/linkedin/response.rb +19 -0
- data/lib/linkedin/version.rb +1 -1
- data/lib/linkedin2.rb +11 -19
- data/linkedin.gemspec +16 -7
- data/spec/api/companies_spec.rb +8 -9
- data/spec/api/groups_spec.rb +3 -4
- data/spec/api/jobs_spec.rb +2 -3
- data/spec/api/network_updates_spec.rb +9 -15
- data/spec/api/{profiles_spec.rb → people_spec.rb} +11 -13
- data/spec/faraday_middleware/{linkedin_error_response_spec.rb → error_response_spec.rb} +8 -21
- data/spec/fixtures/requests/companies.yml +25 -15
- data/spec/fixtures/requests/invalid.yml +7 -7
- data/spec/fixtures/requests/network_updates.yml +12 -12
- data/spec/fixtures/requests/people.yml +380 -0
- data/spec/spec_helper.rb +18 -19
- data/spec/support/coverage.rb +14 -0
- data/spec/support/vcr.rb +9 -0
- data/spec/test_app.yml +3 -2
- metadata +119 -38
- data/lib/linkedin/api/industries.rb +0 -171
- data/lib/linkedin/api/permissions.rb +0 -42
- data/lib/linkedin/api/profiles.rb +0 -71
- data/lib/linkedin/error.rb +0 -29
- data/lib/linkedin/faraday_middleware/linkedin_format_request.rb +0 -26
- data/lib/linkedin/industry.rb +0 -33
- data/spec/fixtures/requests/profiles.yml +0 -201
@@ -1,42 +0,0 @@
|
|
1
|
-
module LinkedIn
|
2
|
-
module API
|
3
|
-
module Permissions
|
4
|
-
COMPANY = { 'company' => [ 'id', 'name', 'type', 'size', 'industry', 'ticker' ] }
|
5
|
-
LOCATION = { 'location' => ['name', { 'country' => 'code' } ] }
|
6
|
-
POSITIONS = { 'positions' => [ 'id', 'title', 'summary', 'start-date', 'end-date', 'is-current', COMPANY ] }
|
7
|
-
PROFILE_REQ = { 'api-standard-profile-request' => ['url', 'headers'] }
|
8
|
-
|
9
|
-
PROFILE_BASE = [ 'id', 'first-name', 'last-name','maiden-name', 'formatted-name', 'phonetic-first-name',
|
10
|
-
'phonetic-last-name', 'formatted-phonetic-name', 'headline', 'industry', 'distance',
|
11
|
-
'current-share', 'num-connections', 'num-connections-capped', 'summary', 'specialties',
|
12
|
-
'picture-url', 'site-standard-profile-request', 'public-profile-url',
|
13
|
-
LOCATION, POSITIONS, PROFILE_REQ ]
|
14
|
-
|
15
|
-
RELATION = { 'relation-to-viewer' => [ 'num-related-connections', 'related-connections' => PROFILE_BASE ] }
|
16
|
-
|
17
|
-
MEMBER_RESOURCES = { 'member-url-resources' => [ 'url', 'name' ] }
|
18
|
-
FULL_BASE = [ 'last-modified-timestamp', 'proposal-comments', 'associations', 'interests', 'publications',
|
19
|
-
'patents', 'languages', 'skills', 'certifications', 'educations', 'courses', 'volunteer',
|
20
|
-
'three-current-positions', 'three-past-positions', 'num-recommenders', 'recommendations-received',
|
21
|
-
'mfeed-rss-url', 'following', 'job-bookmarks', 'suggestions', 'date-of-birth',
|
22
|
-
'related-profile-views', 'honors-awards' ]
|
23
|
-
|
24
|
-
R_BASICPROFILE = PROFILE_BASE + [ RELATION ]
|
25
|
-
R_EMAILADDRESS = [ 'email-address' ]
|
26
|
-
R_FULLPROFILE = R_BASICPROFILE + FULL_BASE + [ MEMBER_RESOURCES ]
|
27
|
-
R_CONTACTINFO = [ 'phone-numbers', 'bound-account-types', 'im-accounts', 'main-address', 'twitter-accounts', 'primary-twitter-account']
|
28
|
-
R_NETWORK = [ 'connections' ]
|
29
|
-
RW_GROUPS = [ 'group-memberships' ]
|
30
|
-
RW_NUS = [ 'network' ]
|
31
|
-
W_MESSAGES = []
|
32
|
-
|
33
|
-
def self.render_permissions (*fields)
|
34
|
-
fields = fields.first if fields.size == 1
|
35
|
-
|
36
|
-
return render_permissions(fields.map { |key, value| "#{key}:(#{render_permissions value})" }) if fields.respond_to? :keys
|
37
|
-
return fields.map { |value| render_permissions value }.join ',' if fields.respond_to? :each
|
38
|
-
fields.to_s
|
39
|
-
end
|
40
|
-
end
|
41
|
-
end
|
42
|
-
end
|
@@ -1,71 +0,0 @@
|
|
1
|
-
module LinkedIn
|
2
|
-
module API
|
3
|
-
module Profiles
|
4
|
-
def profile_api(selector)
|
5
|
-
profile selector, Permissions::PROFILE_REQ
|
6
|
-
end
|
7
|
-
|
8
|
-
def profile(selector = '~', fields = nil, token = nil)
|
9
|
-
fields ||= profile_fields
|
10
|
-
query = "#{selector.to_param}:(#{Permissions.render_permissions fields})"
|
11
|
-
get "v1/people/#{query}" do |req|
|
12
|
-
req.headers.merge! 'x-li-auth-token' => token unless token.blank?
|
13
|
-
end
|
14
|
-
end
|
15
|
-
|
16
|
-
def connections(selector = '~', fields = nil)
|
17
|
-
profile [selector, 'connections'].join('/'), fields
|
18
|
-
end
|
19
|
-
|
20
|
-
def shared_connections(selector, fields = Permissions::RELATION, offset = 0, limit = 10, token = nil)
|
21
|
-
query = "#{selector.to_param}:(#{Permissions.render_permissions fields})"
|
22
|
-
get "v1/people/#{query}" do |req|
|
23
|
-
req.params.merge! start: offset, count: limit
|
24
|
-
req.headers.merge! 'x-li-auth-token' => token unless token.blank?
|
25
|
-
end
|
26
|
-
end
|
27
|
-
|
28
|
-
def search(options = {}, token = nil)
|
29
|
-
get "v1/people-search?#{options.to_param}" do |req|
|
30
|
-
req.headers.merge! 'x-li-auth-token' => token unless token.blank?
|
31
|
-
end
|
32
|
-
end
|
33
|
-
|
34
|
-
def connect_with(selector, subject, message, type = :friend, auth_name = nil, auth_value = nil)
|
35
|
-
if auth_name.blank? || auth_value.blank?
|
36
|
-
profile = profile_api selector
|
37
|
-
token = profile.apiStandardProfileRequest_.headers_.values_.first.value
|
38
|
-
auth_name, auth_value = *token.split(':')
|
39
|
-
end
|
40
|
-
|
41
|
-
connection_request = { recipients: { values: [ { person: { _path: "/people/#{selector.to_param}" } } ] },
|
42
|
-
subject: subject,
|
43
|
-
body: message,
|
44
|
-
'item-content' => {
|
45
|
-
'invitation-request' => {
|
46
|
-
'connect-type' => type,
|
47
|
-
authorization: { name: auth_name, value: auth_value } } } }
|
48
|
-
|
49
|
-
post 'v1/people/~/mailbox' do |req|
|
50
|
-
req.headers['Content-Type'] = 'application/json'
|
51
|
-
req.headers['x-li-format'] = 'json'
|
52
|
-
req.body = connection_request.to_json
|
53
|
-
end
|
54
|
-
end
|
55
|
-
|
56
|
-
def message(subject, message, recipients)
|
57
|
-
recipients = [recipients].flatten
|
58
|
-
|
59
|
-
recipients = recipients.map { |recipient| { person: { _path: "/people/#{recipient.to_param}" } } }
|
60
|
-
|
61
|
-
message = { recipients: { values: recipients }, subject: subject, body: message }
|
62
|
-
|
63
|
-
post 'v1/people/~/mailbox' do |req|
|
64
|
-
req.headers['Content-Type'] = 'application/json'
|
65
|
-
req.headers['x-li-format'] = 'json'
|
66
|
-
req.body = message.to_json
|
67
|
-
end
|
68
|
-
end
|
69
|
-
end
|
70
|
-
end
|
71
|
-
end
|
data/lib/linkedin/error.rb
DELETED
@@ -1,29 +0,0 @@
|
|
1
|
-
module LinkedIn
|
2
|
-
class Error < StandardError
|
3
|
-
extend Forwardable
|
4
|
-
|
5
|
-
attr_accessor :request, :response
|
6
|
-
delegate [:status, :body] => :response
|
7
|
-
|
8
|
-
def initialize(error_data={})
|
9
|
-
if hash = Hash.try_convert(error_data)
|
10
|
-
self.request, self.response = hash[:request], hash[:response]
|
11
|
-
end
|
12
|
-
|
13
|
-
super (self.response && self.response.body['message']) || error_data
|
14
|
-
end
|
15
|
-
end
|
16
|
-
|
17
|
-
class BadRequest < Error; end
|
18
|
-
class Unauthorized < Error; end
|
19
|
-
class Forbidden < Error; end
|
20
|
-
class NotFound < Error; end
|
21
|
-
class InternalServerError < Error; end
|
22
|
-
|
23
|
-
class CSRF < Error
|
24
|
-
def initialize(expected=nil, received=nil)
|
25
|
-
additional = "Excepted '#{expected}' but received '#{received}'" if expected
|
26
|
-
super "Response state did not match sent state. #{additional}"
|
27
|
-
end
|
28
|
-
end
|
29
|
-
end
|
@@ -1,26 +0,0 @@
|
|
1
|
-
module LinkedIn
|
2
|
-
module FaradayMiddleware
|
3
|
-
class LinkedinFormatRequest < Faraday::Middleware
|
4
|
-
def initialize(app = nil, format = :json)
|
5
|
-
super app
|
6
|
-
|
7
|
-
@format = format
|
8
|
-
end
|
9
|
-
|
10
|
-
def call(env)
|
11
|
-
set_request_format! env[:url] unless has_format?(env[:url])
|
12
|
-
|
13
|
-
@app.call env
|
14
|
-
end
|
15
|
-
|
16
|
-
def has_format?(url)
|
17
|
-
!(url.query =~ /(^|&)format=/).nil?
|
18
|
-
end
|
19
|
-
|
20
|
-
def set_request_format!(url)
|
21
|
-
ar = URI.decode_www_form(url.query || '') << ['format', @format]
|
22
|
-
url.query = URI.encode_www_form(ar)
|
23
|
-
end
|
24
|
-
end
|
25
|
-
end
|
26
|
-
end
|
data/lib/linkedin/industry.rb
DELETED
@@ -1,33 +0,0 @@
|
|
1
|
-
module LinkedIn
|
2
|
-
module Industry
|
3
|
-
def self.all
|
4
|
-
LinkedIn::API::Industries::INDUSTRIES
|
5
|
-
end
|
6
|
-
|
7
|
-
def self.[](code)
|
8
|
-
find_by_code(code)
|
9
|
-
end
|
10
|
-
|
11
|
-
def self.find_by_code(code)
|
12
|
-
all.detect { |indust| indust[:code] == code }
|
13
|
-
end
|
14
|
-
|
15
|
-
def self.find_by_group(*groups)
|
16
|
-
all.reject { |indust| (groups & indust[:group]) != groups }
|
17
|
-
end
|
18
|
-
|
19
|
-
def self.find_by_description(description)
|
20
|
-
all.detect { |indust| normalize_description(indust[:description]) == normalize_description(description) }
|
21
|
-
end
|
22
|
-
|
23
|
-
def self.find_group_names(*groups)
|
24
|
-
Array[groups].flatten.map { |group| LinkedIn::API::Industries::GROUPS[group.to_sym] }
|
25
|
-
end
|
26
|
-
|
27
|
-
private
|
28
|
-
|
29
|
-
def self.normalize_description(description)
|
30
|
-
description.to_s.downcase.strip.gsub(' ','_').gsub(/[^\w]/, '').to_sym
|
31
|
-
end
|
32
|
-
end
|
33
|
-
end
|
@@ -1,201 +0,0 @@
|
|
1
|
-
---
|
2
|
-
http_interactions:
|
3
|
-
- request:
|
4
|
-
method: get
|
5
|
-
uri: https://api.linkedin.com/v1/people/~?oauth2_access_token=AQUJwEF40pJUbVxsW_mujQ3QCiXvlTnMFk55SlfVPYRcAPdsn1oE1Hm8Ldlc61o57k96i04ufG81KFdPJIOSJswXsGyZ0tk9IMZea8sfNXMGMnZgikQJUQPkmRVYVw9BP1qH9tp7hJF32DQtzkBB_NE8xPASPVgXVWbbntChGyYqqDvF1p8&format=json
|
6
|
-
body:
|
7
|
-
encoding: US-ASCII
|
8
|
-
string: ''
|
9
|
-
headers:
|
10
|
-
User-Agent:
|
11
|
-
- Faraday v0.8.8
|
12
|
-
response:
|
13
|
-
status:
|
14
|
-
code: 200
|
15
|
-
message:
|
16
|
-
headers:
|
17
|
-
server:
|
18
|
-
- Apache-Coyote/1.1
|
19
|
-
x-li-request-id:
|
20
|
-
- 03M2HIZ5ZZ
|
21
|
-
vary:
|
22
|
-
- '*'
|
23
|
-
x-li-format:
|
24
|
-
- json
|
25
|
-
content-type:
|
26
|
-
- application/json;charset=UTF-8
|
27
|
-
date:
|
28
|
-
- Sun, 13 Oct 2013 22:48:35 GMT
|
29
|
-
x-li-fabric:
|
30
|
-
- PROD-ELA4
|
31
|
-
access-control-allow-origin:
|
32
|
-
- https://www.linkedin.com
|
33
|
-
age:
|
34
|
-
- '0'
|
35
|
-
transfer-encoding:
|
36
|
-
- chunked
|
37
|
-
connection:
|
38
|
-
- keep-alive
|
39
|
-
x-li-pop:
|
40
|
-
- PROD-ELA4
|
41
|
-
x-li-uuid:
|
42
|
-
- mBWfxe/MLBPwzEjFASsAAA==
|
43
|
-
body:
|
44
|
-
encoding: UTF-8
|
45
|
-
string: |-
|
46
|
-
{
|
47
|
-
"firstName": "Josh",
|
48
|
-
"headline": "Test Engineer at LinkedIn2 Gem",
|
49
|
-
"lastName": "Testjordan",
|
50
|
-
"siteStandardProfileRequest": {"url": "http://www.linkedin.com/profile/view?id=292221312&authType=name&authToken=lTbb&trk=api*a3296321*s3370311*"}
|
51
|
-
}
|
52
|
-
http_version:
|
53
|
-
recorded_at: Sun, 13 Oct 2013 22:48:40 GMT
|
54
|
-
- request:
|
55
|
-
method: get
|
56
|
-
uri: https://api.linkedin.com/v1/people/id=Fy5e5a4mqr?oauth2_access_token=AQUJwEF40pJUbVxsW_mujQ3QCiXvlTnMFk55SlfVPYRcAPdsn1oE1Hm8Ldlc61o57k96i04ufG81KFdPJIOSJswXsGyZ0tk9IMZea8sfNXMGMnZgikQJUQPkmRVYVw9BP1qH9tp7hJF32DQtzkBB_NE8xPASPVgXVWbbntChGyYqqDvF1p8&format=json
|
57
|
-
body:
|
58
|
-
encoding: US-ASCII
|
59
|
-
string: ''
|
60
|
-
headers:
|
61
|
-
User-Agent:
|
62
|
-
- Faraday v0.8.8
|
63
|
-
response:
|
64
|
-
status:
|
65
|
-
code: 200
|
66
|
-
message:
|
67
|
-
headers:
|
68
|
-
server:
|
69
|
-
- Apache-Coyote/1.1
|
70
|
-
x-li-request-id:
|
71
|
-
- K70EU26VBI
|
72
|
-
vary:
|
73
|
-
- '*'
|
74
|
-
x-li-format:
|
75
|
-
- json
|
76
|
-
content-type:
|
77
|
-
- application/json;charset=UTF-8
|
78
|
-
date:
|
79
|
-
- Sun, 13 Oct 2013 22:48:36 GMT
|
80
|
-
x-li-fabric:
|
81
|
-
- PROD-ELA4
|
82
|
-
access-control-allow-origin:
|
83
|
-
- https://www.linkedin.com
|
84
|
-
age:
|
85
|
-
- '0'
|
86
|
-
transfer-encoding:
|
87
|
-
- chunked
|
88
|
-
connection:
|
89
|
-
- keep-alive
|
90
|
-
x-li-pop:
|
91
|
-
- PROD-ELA4
|
92
|
-
x-li-uuid:
|
93
|
-
- sLbq6O/MLBMQaCG8uSoAAA==
|
94
|
-
body:
|
95
|
-
encoding: UTF-8
|
96
|
-
string: |-
|
97
|
-
{
|
98
|
-
"firstName": "Sir Richard",
|
99
|
-
"headline": "Chairman,Virgin Group.",
|
100
|
-
"lastName": "B.",
|
101
|
-
"siteStandardProfileRequest": {"url": "http://www.linkedin.com/profile/view?id=293623781&authType=name&authToken=Y22Y&trk=api*a3296321*s3370311*"}
|
102
|
-
}
|
103
|
-
http_version:
|
104
|
-
recorded_at: Sun, 13 Oct 2013 22:48:41 GMT
|
105
|
-
- request:
|
106
|
-
method: get
|
107
|
-
uri: https://api.linkedin.com/v1/people/~/connections?oauth2_access_token=AQUJwEF40pJUbVxsW_mujQ3QCiXvlTnMFk55SlfVPYRcAPdsn1oE1Hm8Ldlc61o57k96i04ufG81KFdPJIOSJswXsGyZ0tk9IMZea8sfNXMGMnZgikQJUQPkmRVYVw9BP1qH9tp7hJF32DQtzkBB_NE8xPASPVgXVWbbntChGyYqqDvF1p8&format=json
|
108
|
-
body:
|
109
|
-
encoding: US-ASCII
|
110
|
-
string: ''
|
111
|
-
headers:
|
112
|
-
User-Agent:
|
113
|
-
- Faraday v0.8.8
|
114
|
-
response:
|
115
|
-
status:
|
116
|
-
code: 200
|
117
|
-
message:
|
118
|
-
headers:
|
119
|
-
server:
|
120
|
-
- Apache-Coyote/1.1
|
121
|
-
x-li-request-id:
|
122
|
-
- DKYIS1K51I
|
123
|
-
vary:
|
124
|
-
- '*'
|
125
|
-
x-li-format:
|
126
|
-
- json
|
127
|
-
content-type:
|
128
|
-
- application/json;charset=UTF-8
|
129
|
-
date:
|
130
|
-
- Sun, 13 Oct 2013 22:50:18 GMT
|
131
|
-
x-li-fabric:
|
132
|
-
- PROD-ELA4
|
133
|
-
access-control-allow-origin:
|
134
|
-
- https://www.linkedin.com
|
135
|
-
age:
|
136
|
-
- '2'
|
137
|
-
transfer-encoding:
|
138
|
-
- chunked
|
139
|
-
connection:
|
140
|
-
- keep-alive
|
141
|
-
x-li-pop:
|
142
|
-
- PROD-ELA4
|
143
|
-
x-li-uuid:
|
144
|
-
- yIk8xgfNLBMQblmOAisAAA==
|
145
|
-
body:
|
146
|
-
encoding: UTF-8
|
147
|
-
string: |-
|
148
|
-
{
|
149
|
-
"_total": 2,
|
150
|
-
"values": [
|
151
|
-
{
|
152
|
-
"apiStandardProfileRequest": {
|
153
|
-
"headers": {
|
154
|
-
"_total": 1,
|
155
|
-
"values": [{
|
156
|
-
"name": "x-li-auth-token",
|
157
|
-
"value": "name:24s3"
|
158
|
-
}]
|
159
|
-
},
|
160
|
-
"url": "http://api.linkedin.com/v1/people/4eTIydMs1T"
|
161
|
-
},
|
162
|
-
"firstName": "Bob",
|
163
|
-
"headline": "Guy at EverTrue",
|
164
|
-
"id": "4eTIydMs1T",
|
165
|
-
"industry": "Leisure, Travel & Tourism",
|
166
|
-
"lastName": "Breznak",
|
167
|
-
"location": {
|
168
|
-
"country": {"code": "us"},
|
169
|
-
"name": "Greater Boston Area"
|
170
|
-
},
|
171
|
-
"pictureUrl": "http://m.c.lnkd.licdn.com/mpr/mprx/0_zR-8K5tqrNEyhBCYz0APKFGVrqDrGlCYcx60KFPQftJhjzvOMjGuObFeOJSfTn_tq4rYx8SQ5fvK",
|
172
|
-
"siteStandardProfileRequest": {"url": "http://www.linkedin.com/profile/view?id=28903483&authType=name&authToken=24s3&trk=api*a3296321*s3370311*"}
|
173
|
-
},
|
174
|
-
{
|
175
|
-
"apiStandardProfileRequest": {
|
176
|
-
"headers": {
|
177
|
-
"_total": 1,
|
178
|
-
"values": [{
|
179
|
-
"name": "x-li-auth-token",
|
180
|
-
"value": "name:Dack"
|
181
|
-
}]
|
182
|
-
},
|
183
|
-
"url": "http://api.linkedin.com/v1/people/mbAZ4HR8RV"
|
184
|
-
},
|
185
|
-
"firstName": "Josh",
|
186
|
-
"headline": "No-bullshit Software Engineer",
|
187
|
-
"id": "mbAZ4HR8RV",
|
188
|
-
"industry": "Computer Software",
|
189
|
-
"lastName": "Jordan",
|
190
|
-
"location": {
|
191
|
-
"country": {"code": "us"},
|
192
|
-
"name": "Greater Boston Area"
|
193
|
-
},
|
194
|
-
"pictureUrl": "http://m.c.lnkd.licdn.com/mpr/mprx/0_tK6jMqPfpQ370lofKczaMnrmy_kxjqUfrB-CMBig_LK7GKu7On9AccBPtmX8Y1siPPXinrgl7GAr",
|
195
|
-
"siteStandardProfileRequest": {"url": "http://www.linkedin.com/profile/view?id=18939563&authType=name&authToken=Dack&trk=api*a3296321*s3370311*"}
|
196
|
-
}
|
197
|
-
]
|
198
|
-
}
|
199
|
-
http_version:
|
200
|
-
recorded_at: Sun, 13 Oct 2013 22:50:23 GMT
|
201
|
-
recorded_with: VCR 2.5.0
|