linkedin2 0.0.16 → 0.0.17

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.
Files changed (51) hide show
  1. checksums.yaml +4 -4
  2. data/.coveralls.yml +2 -0
  3. data/.gitignore +2 -0
  4. data/.rspec +2 -2
  5. data/.travis.yml +1 -1
  6. data/README.md +3 -3
  7. data/Rakefile +1 -1
  8. data/lib/linkedin/api.rb +16 -0
  9. data/lib/linkedin/api/authentication.rb +5 -7
  10. data/lib/linkedin/api/companies.rb +16 -10
  11. data/lib/linkedin/api/invitation.rb +39 -0
  12. data/lib/linkedin/api/messaging.rb +24 -0
  13. data/lib/linkedin/api/network_updates.rb +5 -9
  14. data/lib/linkedin/api/people.rb +18 -0
  15. data/lib/linkedin/client.rb +34 -72
  16. data/lib/linkedin/configuration.rb +26 -11
  17. data/lib/linkedin/credentials.rb +27 -0
  18. data/lib/linkedin/errors.rb +45 -0
  19. data/lib/linkedin/faraday_middleware.rb +6 -4
  20. data/lib/linkedin/faraday_middleware/credentials_request.rb +31 -0
  21. data/lib/linkedin/faraday_middleware/{linkedin_error_response.rb → error_response.rb} +2 -10
  22. data/lib/linkedin/faraday_middleware/format_request.rb +17 -0
  23. data/lib/linkedin/faraday_middleware/user_agent_request.rb +10 -0
  24. data/lib/linkedin/fields.rb +58 -0
  25. data/lib/linkedin/industries.rb +199 -0
  26. data/lib/linkedin/response.rb +19 -0
  27. data/lib/linkedin/version.rb +1 -1
  28. data/lib/linkedin2.rb +11 -19
  29. data/linkedin.gemspec +16 -7
  30. data/spec/api/companies_spec.rb +8 -9
  31. data/spec/api/groups_spec.rb +3 -4
  32. data/spec/api/jobs_spec.rb +2 -3
  33. data/spec/api/network_updates_spec.rb +9 -15
  34. data/spec/api/{profiles_spec.rb → people_spec.rb} +11 -13
  35. data/spec/faraday_middleware/{linkedin_error_response_spec.rb → error_response_spec.rb} +8 -21
  36. data/spec/fixtures/requests/companies.yml +25 -15
  37. data/spec/fixtures/requests/invalid.yml +7 -7
  38. data/spec/fixtures/requests/network_updates.yml +12 -12
  39. data/spec/fixtures/requests/people.yml +380 -0
  40. data/spec/spec_helper.rb +18 -19
  41. data/spec/support/coverage.rb +14 -0
  42. data/spec/support/vcr.rb +9 -0
  43. data/spec/test_app.yml +3 -2
  44. metadata +119 -38
  45. data/lib/linkedin/api/industries.rb +0 -171
  46. data/lib/linkedin/api/permissions.rb +0 -42
  47. data/lib/linkedin/api/profiles.rb +0 -71
  48. data/lib/linkedin/error.rb +0 -29
  49. data/lib/linkedin/faraday_middleware/linkedin_format_request.rb +0 -26
  50. data/lib/linkedin/industry.rb +0 -33
  51. 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
@@ -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
@@ -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