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.
- 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
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 97c526507b2313e93d3e8f8774e21ddac5b9a64b
|
4
|
+
data.tar.gz: 59b303455324238c1d9328d7e20cb165ea982218
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: b48333d3d41d0ecb1ac40cb11853ea92378822cb30e7c1906eba0841851d6f7b11ee630853d472ec91193284b2d703fb6741696f8c4bb5fcb31e3c8667f2a69e
|
7
|
+
data.tar.gz: 8d3f033c03d7232f64825b7a0b3bb8577e33e4d2455506298c9ae07de9b9389a2ad6dca4f10f5bf43c2236f1f7c906635ad2684f87fe78d6a3a4ca36f535e319
|
data/.coveralls.yml
ADDED
data/.gitignore
CHANGED
data/.rspec
CHANGED
data/.travis.yml
CHANGED
data/README.md
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
# LinkedIn 2
|
2
|
-
[](https://travis-ci.org/bobbrez/linkedin2)
|
3
|
+
[](https://coveralls.io/r/bobbrez/linkedin2)
|
4
|
+
[](https://codeclimate.com/github/bobbrez/linkedin2)
|
5
5
|
A modernized LinkedIn Ruby client.
|
6
6
|
|
7
7
|
## Installation
|
data/Rakefile
CHANGED
data/lib/linkedin/api.rb
ADDED
@@ -0,0 +1,16 @@
|
|
1
|
+
module LinkedIn
|
2
|
+
module API
|
3
|
+
Dir[ File.expand_path('../api/**/*.rb', __FILE__) ].each { |f| require f }
|
4
|
+
|
5
|
+
def self.included(base)
|
6
|
+
base.send :include, LinkedIn::API::Authentication,
|
7
|
+
LinkedIn::API::Companies,
|
8
|
+
LinkedIn::API::Groups,
|
9
|
+
LinkedIn::API::Invitation,
|
10
|
+
LinkedIn::API::Jobs,
|
11
|
+
LinkedIn::API::Messaging,
|
12
|
+
LinkedIn::API::NetworkUpdates,
|
13
|
+
LinkedIn::API::People
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
@@ -1,21 +1,19 @@
|
|
1
1
|
module LinkedIn
|
2
2
|
module API
|
3
3
|
module Authentication
|
4
|
-
|
5
|
-
|
6
|
-
def authorize_url(params = {})
|
7
|
-
params.reverse_merge! config.to_h.slice :scope, :state, :redirect_uri
|
4
|
+
def authorize_url(**params)
|
5
|
+
params.reverse_merge! configuration.to_h.slice :scope, :state, :redirect_uri
|
8
6
|
params[:scope] = serialize_scope params[:scope]
|
9
|
-
auth_code.authorize_url params
|
7
|
+
credentials.auth_code.authorize_url params
|
10
8
|
end
|
11
9
|
|
12
10
|
def request_access_token(authorization_code, params = {})
|
13
11
|
raise Error::CSRF.new state, params[:state] if params[:state] && params[:state] != state
|
14
12
|
|
15
|
-
params.reverse_merge! redirect_uri:
|
13
|
+
params.reverse_merge! redirect_uri: configuration.redirect_uri
|
16
14
|
opts = { mode: :query, param_name: 'oauth2_access_token' }
|
17
15
|
|
18
|
-
|
16
|
+
credentials.auth_code.get_token authorization_code, params, opts
|
19
17
|
end
|
20
18
|
|
21
19
|
private
|
@@ -1,19 +1,25 @@
|
|
1
1
|
module LinkedIn
|
2
2
|
module API
|
3
3
|
module Companies
|
4
|
-
def company(
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
end
|
4
|
+
def company(*selector, filter: nil, **opts)
|
5
|
+
root = 'companies'
|
6
|
+
|
7
|
+
selector.compact!
|
8
|
+
selector = selector.first if selector.size == 1
|
10
9
|
|
11
|
-
|
12
|
-
|
10
|
+
unless filter.blank?
|
11
|
+
filter = Hash[ *filter.to_s.split('=') ] unless filter.respond_to? :keys
|
12
|
+
opts[:params] = {} if opts[:params].blank?
|
13
|
+
opts[:params].to_h.merge! filter
|
14
|
+
selector = nil
|
15
|
+
end
|
13
16
|
|
14
|
-
|
17
|
+
if selector.respond_to? :each
|
18
|
+
selector = "companies::(#{ selector.map(&:to_param).join(',') })"
|
19
|
+
root = nil
|
20
|
+
end
|
15
21
|
|
16
|
-
|
22
|
+
execute root, opts.merge(selector: selector)
|
17
23
|
end
|
18
24
|
end
|
19
25
|
end
|
@@ -0,0 +1,39 @@
|
|
1
|
+
module LinkedIn
|
2
|
+
module API
|
3
|
+
module Invitation
|
4
|
+
def connect_with(recipient_selector, subject, message, type: :friend, x_auth_token: nil)
|
5
|
+
if x_auth_token.blank?
|
6
|
+
target_profile = profile recipient_selector, Fields::PROFILE_API_STD_PROFILE_REQ
|
7
|
+
x_auth_token = target_profile.body.apiStandardProfileRequest_.headers_.values_.first.value
|
8
|
+
end
|
9
|
+
|
10
|
+
x_auth_name, x_auth_value = *x_auth_token.split(':')
|
11
|
+
|
12
|
+
connection_body = build_connection_body selector: recipient_selector, subject: subject,
|
13
|
+
message: message, type: type,
|
14
|
+
auth_name: x_auth_name, auth_value: x_auth_value
|
15
|
+
|
16
|
+
execute 'people/~/mailbox', method: :post, body: connection_body
|
17
|
+
end
|
18
|
+
|
19
|
+
private
|
20
|
+
|
21
|
+
def build_connection_body(**args)
|
22
|
+
[:selector, :subject, :message, :type, :auth_name, :auth_value].each do |key|
|
23
|
+
raise ArgumentError.new "Missing / blank argument `#{key}`" if args[key].blank?
|
24
|
+
end
|
25
|
+
|
26
|
+
{ recipients: { values: [ { person: { _path: "/people/#{args[:selector].to_param}" } } ] },
|
27
|
+
subject: args[:subject],
|
28
|
+
body: args[:message],
|
29
|
+
'item-content' => {
|
30
|
+
'invitation-request' => {
|
31
|
+
'connect-type' => args[:type],
|
32
|
+
authorization: { name: args[:auth_name], value: args[:auth_value] }
|
33
|
+
}
|
34
|
+
}
|
35
|
+
}
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
@@ -0,0 +1,24 @@
|
|
1
|
+
module LinkedIn
|
2
|
+
module API
|
3
|
+
module Messaging
|
4
|
+
def message(subject, message, recipient_selectors, **opts)
|
5
|
+
message_body = build_message_body selectors: [recipient_selectors].flatten,
|
6
|
+
subject: subject, message: message
|
7
|
+
|
8
|
+
execute 'people/~/mailbox', opts.merge(method: :post, body: message_body)
|
9
|
+
end
|
10
|
+
|
11
|
+
private
|
12
|
+
|
13
|
+
def build_message_body(**args)
|
14
|
+
[:selectors, :subject, :message].each do |key|
|
15
|
+
raise ArgumentError.new "Missing / blank argument `#{key}`" if args[key].blank?
|
16
|
+
end
|
17
|
+
|
18
|
+
recipients = args[:selectors].map { |sel| { person: { _path: "/people/#{sel.to_param}" } } }
|
19
|
+
|
20
|
+
{ recipients: { values: recipients }, subject: args[:subject], body: args[:message] }
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
@@ -1,16 +1,12 @@
|
|
1
1
|
module LinkedIn
|
2
2
|
module API
|
3
3
|
module NetworkUpdates
|
4
|
-
def network_updates(
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
def network_update_comments(options = {})
|
9
|
-
network_updates(options.merge attached_object_type: 'update-comments')
|
10
|
-
end
|
4
|
+
def network_updates(selector = '~', key: nil, type: nil, **opts)
|
5
|
+
path = ['network','updates']
|
6
|
+
path << { key: key }.to_param if key
|
7
|
+
path << type
|
11
8
|
|
12
|
-
|
13
|
-
network_updates(options.merge attached_object_type: 'likes')
|
9
|
+
execute 'people', opts.merge(selector: selector, path: path.compact.join('/'))
|
14
10
|
end
|
15
11
|
end
|
16
12
|
end
|
@@ -0,0 +1,18 @@
|
|
1
|
+
module LinkedIn
|
2
|
+
module API
|
3
|
+
module People
|
4
|
+
def profile(selector = '~', **opts)
|
5
|
+
execute 'people', opts.merge(selector: selector)
|
6
|
+
end
|
7
|
+
|
8
|
+
def connections(selector = '~', **opts)
|
9
|
+
execute 'people', opts.merge(selector: selector, path: 'connections')
|
10
|
+
end
|
11
|
+
|
12
|
+
def people_search(query, **opts)
|
13
|
+
wrapped_fields = opts[:fields].blank? ? nil : { people: opts[:fields] }
|
14
|
+
execute 'people-search', opts.merge(params: query, fields: wrapped_fields)
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
data/lib/linkedin/client.rb
CHANGED
@@ -1,96 +1,58 @@
|
|
1
1
|
module LinkedIn
|
2
2
|
class Client
|
3
|
-
extend Forwardable
|
4
|
-
|
5
3
|
include Configuration
|
6
|
-
include LinkedIn::API
|
7
|
-
include LinkedIn::API::Profiles
|
8
|
-
include LinkedIn::API::NetworkUpdates
|
9
|
-
include LinkedIn::API::Companies
|
10
|
-
|
11
|
-
HTTP_METHODS = [:get, :post, :put, :patch, :delete, :headers].freeze
|
12
|
-
|
13
|
-
attr_writer :profile_fields
|
14
|
-
attr_reader :access_token
|
4
|
+
include LinkedIn::API
|
15
5
|
|
16
|
-
|
17
|
-
|
18
|
-
def initialize(options={}, &block)
|
19
|
-
configure options, &block
|
20
|
-
self.access_token ||= self.config.access_token.to_s
|
6
|
+
def initialize(**config, &block)
|
7
|
+
configure config, &block
|
21
8
|
end
|
22
9
|
|
23
|
-
def
|
24
|
-
|
10
|
+
def credentials
|
11
|
+
@credentials ||= Credentials.new configuration
|
25
12
|
end
|
26
13
|
|
27
14
|
def connection
|
28
|
-
@connection ||=
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
15
|
+
@connection ||= Faraday.new 'https://api.linkedin.com' do |conn|
|
16
|
+
conn.request :json
|
17
|
+
conn.request :url_encoded
|
18
|
+
conn.request :linkedin_credentials, configuration
|
19
|
+
conn.request :linkedin_format
|
20
|
+
conn.request :linkedin_user_agent
|
21
|
+
|
22
|
+
conn.response :linkedin_errors
|
23
|
+
conn.response :mashify
|
24
|
+
conn.response :logger, configuration.logger
|
25
|
+
conn.response :json, content_type: /\bjson$/
|
26
|
+
|
27
|
+
conn.adapter Faraday.default_adapter
|
39
28
|
end
|
40
29
|
end
|
41
30
|
|
42
|
-
def
|
43
|
-
|
44
|
-
options = { access_token: token, mode: :query, param_name: 'oauth2_access_token' }
|
45
|
-
return @access_token = OAuth2::AccessToken.from_hash(connection, options)
|
46
|
-
end
|
47
|
-
|
48
|
-
@access_token = token
|
31
|
+
def headers
|
32
|
+
@headers ||= {}
|
49
33
|
end
|
50
34
|
|
51
|
-
def
|
52
|
-
|
53
|
-
scopes = config.scope unless config.scope.respond_to?(:values)
|
54
|
-
scopes ||= config.scope
|
55
|
-
|
56
|
-
@profile_fields = scopes.reduce([]) { |fields, scope| fields + LinkedIn.send(scope) }
|
57
|
-
end
|
58
|
-
|
59
|
-
def method_missing(method, *args, &body)
|
60
|
-
return simple_request(method, args[0], (args[1] || {}), &body) if HTTP_METHODS.include? method
|
61
|
-
super
|
62
|
-
end
|
63
|
-
|
64
|
-
def self.default_config
|
65
|
-
{
|
66
|
-
request_format: :json,
|
67
|
-
|
68
|
-
key: nil,
|
69
|
-
secret: nil,
|
70
|
-
access_token: nil,
|
71
|
-
|
72
|
-
scope: ['r_basicprofile'],
|
73
|
-
state: Utils.generate_random_state,
|
74
|
-
redirect_uri: 'http://localhost',
|
75
|
-
|
76
|
-
logger: Logger.new('/dev/null')
|
77
|
-
}
|
35
|
+
def params
|
36
|
+
@params ||= {}
|
78
37
|
end
|
79
38
|
|
80
39
|
private
|
81
40
|
|
82
|
-
def
|
83
|
-
|
41
|
+
def override(global, overrides)
|
42
|
+
global.to_h.merge overrides.to_h
|
84
43
|
end
|
85
44
|
|
86
|
-
def
|
87
|
-
|
88
|
-
|
45
|
+
def execute(root, method: :get, selector: nil, fields: nil, **opts)
|
46
|
+
rendered_fields = Fields.render fields
|
47
|
+
query = ['v1', root, selector.to_param, opts[:path]].compact.join('/').concat(rendered_fields)
|
48
|
+
|
49
|
+
response = connection.send method, query do |req|
|
50
|
+
req.headers.update override(@headers, opts[:headers])
|
51
|
+
req.params.update override(@params, opts[:params])
|
52
|
+
req.body = opts[:body].to_json if opts[:body]
|
53
|
+
end
|
89
54
|
|
90
|
-
|
91
|
-
{ site: 'https://api.linkedin.com',
|
92
|
-
authorize_url: 'https://www.linkedin.com/uas/oauth2/authorization',
|
93
|
-
token_url: 'https://www.linkedin.com/uas/oauth2/accessToken' }
|
55
|
+
Response.new response
|
94
56
|
end
|
95
57
|
end
|
96
58
|
end
|
@@ -1,41 +1,56 @@
|
|
1
1
|
module LinkedIn
|
2
2
|
module Configuration
|
3
3
|
module ClassConfiguration
|
4
|
-
def
|
5
|
-
|
4
|
+
def default_config
|
5
|
+
{
|
6
|
+
request_format: :json,
|
7
|
+
|
8
|
+
app_key: nil,
|
9
|
+
app_secret: nil,
|
10
|
+
access_token: nil,
|
11
|
+
|
12
|
+
scope: ['r_basicprofile'],
|
13
|
+
redirect_uri: 'http://localhost',
|
14
|
+
|
15
|
+
logger: Logger.new('/dev/null')
|
16
|
+
}
|
17
|
+
end
|
18
|
+
|
19
|
+
def configuration
|
20
|
+
@configuration ||= OpenStruct.new default_config
|
6
21
|
end
|
7
22
|
|
8
23
|
def reset
|
9
|
-
|
24
|
+
configuration.marshal_load default_config
|
10
25
|
end
|
11
26
|
end
|
12
27
|
|
13
28
|
module InstanceConfiguration
|
14
|
-
def
|
15
|
-
@
|
29
|
+
def configuration
|
30
|
+
@configuration ||= self.class.configuration.dup
|
16
31
|
end
|
17
32
|
|
18
33
|
def reset
|
19
|
-
@
|
34
|
+
@configuration.marshal_load self.class.configuration
|
20
35
|
end
|
21
36
|
end
|
22
37
|
|
23
38
|
module BaseConfiguration
|
24
39
|
def configure(config={}, &block)
|
25
|
-
self.
|
40
|
+
self.configuration.marshal_load self.configuration.marshal_dump.merge(config)
|
26
41
|
|
27
|
-
yield self.
|
42
|
+
yield self.configuration if block_given?
|
28
43
|
|
29
|
-
self.
|
44
|
+
self.configuration
|
30
45
|
end
|
31
46
|
|
32
|
-
def load(file_path='linkedin.yml')
|
47
|
+
def load(file_path = 'linkedin.yml')
|
33
48
|
config = YAML::load(File.open(file_path)).symbolize_keys
|
34
49
|
configure config
|
35
50
|
end
|
36
51
|
|
37
52
|
def config(*keys)
|
38
|
-
|
53
|
+
configuration.marshal_dump.slice(*keys)
|
39
54
|
end
|
40
55
|
end
|
41
56
|
|