linkedin-ruby 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,41 @@
1
+ module LinkedIn
2
+ # Configuration for the LinkedIn gem.
3
+ #
4
+ # LinkedIn.configure do |config|
5
+ # config.client_id = ENV["LINKEDIN_CLIENT_ID"]
6
+ # config.client_secret = ENV["LINKEDIN_CLIENT_SECRET"]
7
+ # end
8
+ #
9
+ # The default endpoints for LinkedIn are also stored here.
10
+ #
11
+ # LinkedIn uses the term "API key" to refer to "client id". They also
12
+ # use the term "Secret Key" to refer to "client_secret". We alias those
13
+ # terms in the config.
14
+ #
15
+ # * LinkedIn.config.site = "https://www.linkedin.com"
16
+ # * LinkedIn.config.token_url = "/oauth/v2/accessToken"
17
+ # * LinkedIn.config.authorize_url = "/oauth/v2/authorization"
18
+ class Configuration
19
+ attr_accessor :api,
20
+ :site,
21
+ :scope,
22
+ :client_id,
23
+ :token_url,
24
+ :api_version,
25
+ :redirect_uri,
26
+ :authorize_url,
27
+ :client_secret,
28
+ :default_profile_fields
29
+
30
+ alias_method :api_key, :client_id
31
+ alias_method :secret_key, :client_secret
32
+
33
+ def initialize
34
+ @api = "https://api.linkedin.com"
35
+ @api_version = "/v2"
36
+ @site = "https://www.linkedin.com"
37
+ @token_url = "/oauth/v2/accessToken"
38
+ @authorize_url = "/oauth/v2/authorization"
39
+ end
40
+ end
41
+ end
@@ -0,0 +1,32 @@
1
+ module LinkedIn
2
+ # Used to perform requests against LinkedIn's API.
3
+ class Connection < ::Faraday::Connection
4
+
5
+ def initialize(url=nil, options=nil, &block)
6
+
7
+ if url.is_a? Hash
8
+ options = url
9
+ url = options[:url]
10
+ end
11
+
12
+ url = default_url if url.nil?
13
+
14
+ super url, options, &block
15
+
16
+ # We need to use the FlatParamsEncoder so we can pass multiple of
17
+ # the same param to certain endpoints (like the search API).
18
+ self.options.params_encoder = ::Faraday::FlatParamsEncoder
19
+
20
+ middleware = Faraday::RackBuilder::Handler.new(LinkedIn::RaiseError)
21
+ self.builder.handlers.push(middleware)
22
+ end
23
+
24
+
25
+ private ##############################################################
26
+
27
+
28
+ def default_url
29
+ LinkedIn.config.api + LinkedIn.config.api_version
30
+ end
31
+ end
32
+ end
@@ -0,0 +1,48 @@
1
+ module LinkedIn
2
+
3
+ # Raised when users call a deprecated function
4
+ class Deprecated < StandardError; end
5
+
6
+ # Raised when we know requests will be malformed
7
+ class InvalidRequest < StandardError; end
8
+
9
+ # Raised when we get a throttle error from the API
10
+ class ThrottleError < StandardError; end
11
+
12
+ # Raised when LinkedIn returns a non 400+ status code during an OAuth
13
+ # request.
14
+ class OAuthError < OAuth2::Error; end
15
+
16
+ # Raised when LinkedIn returns a non 400+ status code during an API
17
+ # request
18
+ class APIError < OAuth2::Error; end
19
+
20
+ module ErrorMessages
21
+ class << self
22
+ attr_reader :deprecated,
23
+ :redirect_uri,
24
+ :no_auth_code,
25
+ :no_access_token,
26
+ :credentials_missing,
27
+ :redirect_uri_mismatch,
28
+ :throttled
29
+ end
30
+
31
+ @deprecated = "This has been deprecated by LinkedIn. Check https://developer.linkedin.com to see the latest available API calls"
32
+
33
+ @redirect_uri = "You must provide a redirect_uri. Set it in LinkedIn.configure or pass it in as the redirect_uri option. It must exactly match the redirect_uri you set on your application's settings page on LinkedIn's website."
34
+
35
+ @no_auth_code = "You must provide the authorization code passed to your redirect uri in the url params"
36
+
37
+ @no_access_token = "You must have an access token to use LinkedIn's API. Use the LinkedIn::OAuth2 module to obtain an access token"
38
+
39
+ @credentials_missing = "Client credentials do not exist. Please either pass your client_id and client_secret to the LinkedIn::Oauth.new constructor or set them via LinkedIn.configure"
40
+
41
+ @redirect_uri_mismatch = "Redirect URI mismatch error. Please check your the redierct uri params"
42
+
43
+ def klass
44
+
45
+ end
46
+
47
+ end
48
+ end
@@ -0,0 +1,68 @@
1
+ module LinkedIn
2
+ # Coerces LinkedIn JSON to a nice Ruby hash
3
+ # LinkedIn::Mash inherits from Hashie::Mash
4
+ class Mash < ::Hashie::Mash
5
+
6
+ # a simple helper to convert a json string to a Mash
7
+ def self.from_json(json_string)
8
+ result_hash = JSON.load(json_string)
9
+ new(result_hash)
10
+ end
11
+
12
+ # returns a Date if we have year, month and day, and no conflicting key
13
+ def to_date
14
+ if !self.has_key?('to_date') && contains_date_fields?
15
+ Date.civil(self.year, self.month, self.day)
16
+ else
17
+ super
18
+ end
19
+ end
20
+
21
+ def timestamp
22
+ value = self['timestamp']
23
+ if value.kind_of? Integer
24
+ value = value / 1000 if value > 9999999999
25
+ Time.at(value)
26
+ else
27
+ value
28
+ end
29
+ end
30
+
31
+
32
+ protected ############################################################
33
+
34
+
35
+ def contains_date_fields?
36
+ self.year? && self.month? && self.day?
37
+ end
38
+
39
+ # overload the convert_key mash method so that the LinkedIn
40
+ # keys are made a little more ruby-ish
41
+ def convert_key(key)
42
+ case key.to_s
43
+ when '_key'
44
+ 'id'
45
+ when '_total'
46
+ 'total'
47
+ when 'values'
48
+ 'all'
49
+ when 'numResults'
50
+ 'total_results'
51
+ else
52
+ underscore(key)
53
+ end
54
+ end
55
+
56
+ # borrowed from ActiveSupport
57
+ # no need require an entire lib when we only need one method
58
+ def underscore(camel_cased_word)
59
+ word = camel_cased_word.to_s.dup
60
+ word.gsub!(/::/, '/')
61
+ word.gsub!(/([A-Z]+)([A-Z][a-z])/, '\1_\2')
62
+ word.gsub!(/([a-z\d])([A-Z])/, '\1_\2')
63
+ word.tr!("-", "_")
64
+ word.downcase!
65
+ word
66
+ end
67
+ end
68
+ end
@@ -0,0 +1,223 @@
1
+ module LinkedIn
2
+ # The LinkedIn::OAuth2::Client class. Inherits directly from [intreda/oauth2](https://github.com/intridea/oauth2)'s `OAuth2::Client`
3
+ #
4
+ # LinkedIn::OAuth2 sets the following default options:
5
+ #
6
+ # * site = "https://www.linkedin.com"
7
+ # * token_url = "/uas/oauth2/accessToken"
8
+ # * authorize_url = "/uas/oauth2/authorization"
9
+ #
10
+ # More details on LinkedIn's Authorization process can be found here: https://developer.linkedin.com/documents/authentication
11
+ class OAuth2 < ::OAuth2::Client
12
+
13
+ attr_accessor :access_token
14
+
15
+ # Instantiate a new OAuth 2.0 client using your client ID (aka API
16
+ # Key) and client secret (aka Secret Key).
17
+ #
18
+ # You should set the client_id and client_secret in the config.
19
+ #
20
+ # LinkedIn.configure do |config|
21
+ # config.client_id = ENV["LINKEDIN_CLIENT_ID"]
22
+ # config.client_secret = ENV["LINKEDIN_CLIENT_SECRET"]
23
+ # end
24
+ #
25
+ # This will let you initialize with zero arguments.
26
+ #
27
+ # If you have already set the `client_id` and `client_secret` in your
28
+ # config, the first and only argument can be the `options` hash.
29
+ #
30
+ # @param [String] client_id the client_id value
31
+ # @param [String] client_secret the client_secret value
32
+ # @param [Hash] options the options to create the client with
33
+ # @option options [Symbol] :token_method (:post) HTTP method to use to
34
+ # request token (:get or :post)
35
+ # @option options [Hash] :connection_opts ({}) Hash of connection options
36
+ # to pass to initialize Faraday with
37
+ # @option options [FixNum] :max_redirects (5) maximum number of redirects
38
+ # to follow
39
+ # @option options [Boolean] :raise_errors (true) whether or not to
40
+ # raise an error on malformed responses
41
+ # @yield [builder] The Faraday connection builder
42
+ def initialize(client_id=LinkedIn.config.client_id,
43
+ client_secret=LinkedIn.config.client_secret,
44
+ options = {}, &block)
45
+
46
+ if client_id.is_a? Hash
47
+ options = client_id
48
+ client_id = LinkedIn.config.client_id
49
+ end
50
+
51
+ options = default_oauth_options(options)
52
+
53
+ super client_id, client_secret, options, &block
54
+
55
+ @redirect_uri = options[:redirect_uri]
56
+
57
+ if self.options[:raise_errors]
58
+ check_credentials!(client_id, client_secret)
59
+ end
60
+ end
61
+
62
+ # Generates the URL users use to sign into your application.
63
+ #
64
+ # Once a user enters their LinkedIn credentials, they will be
65
+ # redirected to your `redirect_uri` with the `code` parameter attached
66
+ # to it. The value of the `code` parameter can be used to get an
67
+ # access token.
68
+ #
69
+ # We recommend you set your `client_id, `client_secret`, and
70
+ # `redirect_uri` in the `LinkedIn.configure` block. They can also be
71
+ # passed in as options.
72
+ #
73
+ # @param [Hash] options the options to generate the url with
74
+ # @option options [String] :redirect_uri The url that gets redirected
75
+ # to after a successful authentication. This must exactly match the
76
+ # redirect urls setup on your LinkedIn Application Settings page.
77
+ # This option is not required if you already set the redirect_uri in
78
+ # the config.
79
+ # @option options [String] :scope A string of requested permissions
80
+ # you want from users when they authenticate with your app. If these
81
+ # are set on yoru LinkedIn Application settings page, you do not
82
+ # need to pass them in. The string must be a space-sparated,
83
+ # case-sensitive list of available scopes. See available scopes on
84
+ # LinkedIn's API documentation page.
85
+ # @option options [String] :state A long string used for CSRF
86
+ # protection. It is added as the `state` GET param in the
87
+ # redirect_uri
88
+ # @option options [Boolean] :raise_errors (true) whether or not to
89
+ # raise an error on malformed responses
90
+ def auth_code_url(options={})
91
+ options = default_auth_code_url_options(options)
92
+
93
+ if self.options[:raise_errors]
94
+ check_redirect_uri!(options)
95
+ end
96
+
97
+ @redirect_uri = options[:redirect_uri]
98
+
99
+ self.auth_code.authorize_url(options)
100
+ end
101
+
102
+ # Returns the access token string for the newly authenticated user.
103
+ #
104
+ # It also sets the `access_token` field on this LinkedIn::OAuth2
105
+ # instance.
106
+ #
107
+ # The required `code`
108
+ #
109
+ # @param [String] code the auth code which is passed in as a GET
110
+ # parameter to your `redirect_uri` after users authenticate your app
111
+ # @param [Hash] options
112
+ # @option options [String] :redirect_uri You normally should not have
113
+ # to pass in the redirect_uri again. If `auth_code_url` was called
114
+ # on this LinkedIn::OAuth2 instance, then the `redirect_uri` will
115
+ # already be set. This is because the `redirect_uri` in the access
116
+ # token request must exactly match the `redirect_uri` in the auth
117
+ # code url.
118
+ # @option options [Boolean] :raise_errors (true) whether or not to
119
+ # raise an error on malformed responses
120
+ def get_access_token(code=nil, options={})
121
+ check_for_code!(code)
122
+ options = default_access_code_options(options)
123
+
124
+ if self.options[:raise_errors]
125
+ check_access_code_url!(options)
126
+ end
127
+
128
+ tok = self.auth_code.get_token(code, options.map { |key, value| [key.to_s, value] }.to_h)
129
+ self.access_token = LinkedIn::AccessToken.new(tok.token,
130
+ tok.expires_in,
131
+ tok.expires_at)
132
+ return self.access_token
133
+ rescue ::OAuth2::Error => e
134
+ raise OAuthError.new(e.response)
135
+ end
136
+
137
+
138
+ private ##############################################################
139
+
140
+
141
+ def default_access_code_options(custom_options={})
142
+ custom_options ||= {}
143
+ options = {raise_errors: true}
144
+
145
+ @redirect_uri = LinkedIn.config.redirect_uri if @redirect_uri.nil?
146
+ options[:redirect_uri] = @redirect_uri
147
+
148
+ options = options.merge custom_options
149
+ return options
150
+ end
151
+
152
+ def default_auth_code_url_options(custom_options={})
153
+ custom_options ||= {}
154
+ options = {raise_errors: true}
155
+
156
+ if not LinkedIn.config.redirect_uri.nil?
157
+ options[:redirect_uri] = LinkedIn.config.redirect_uri
158
+ end
159
+ if not LinkedIn.config.scope.nil?
160
+ options[:scope] = LinkedIn.config.scope
161
+ end
162
+
163
+ options = options.merge custom_options
164
+
165
+ if options[:state].nil?
166
+ options[:state] = generate_csrf_token
167
+ end
168
+
169
+ return options
170
+ end
171
+
172
+ def generate_csrf_token
173
+ SecureRandom.base64(32)
174
+ end
175
+
176
+ def check_access_code_url!(options={})
177
+ check_redirect_uri!(options)
178
+ if options[:redirect_uri] != @redirect_uri
179
+ raise redirect_uri_mismatch
180
+ end
181
+ end
182
+
183
+ def check_for_code!(code)
184
+ if code.nil?
185
+ msg = ErrorMessages.no_auth_code
186
+ raise InvalidRequest.new(msg)
187
+ end
188
+ end
189
+
190
+ def check_redirect_uri!(options={})
191
+ if options[:redirect_uri].nil?
192
+ raise redirect_uri_error
193
+ end
194
+ end
195
+
196
+ def default_oauth_options(custom_options={})
197
+ custom_options ||= {}
198
+ options = {}
199
+ options[:site] = LinkedIn.config.site
200
+ options[:token_url] = LinkedIn.config.token_url
201
+ options[:authorize_url] = LinkedIn.config.authorize_url
202
+ return options.merge custom_options
203
+ end
204
+
205
+ def check_credentials!(client_id, client_secret)
206
+ if client_id.nil? or client_secret.nil?
207
+ raise credential_error
208
+ end
209
+ end
210
+
211
+ def redirect_uri_error
212
+ InvalidRequest.new ErrorMessages.redirect_uri
213
+ end
214
+
215
+ def credential_error
216
+ InvalidRequest.new ErrorMessages.credentials_missing
217
+ end
218
+
219
+ def redirect_uri_mismatch
220
+ InvalidRequest.new ErrorMessages.redirect_uri_mismatch
221
+ end
222
+ end
223
+ end
@@ -0,0 +1,52 @@
1
+ module LinkedIn
2
+ # Organizations API
3
+ #
4
+ # @see https://developer.linkedin.com/docs/guide/v2/organizations
5
+ #
6
+ # [(contribute here)](https://github.com/mallowtechdev/linkedin-ruby)
7
+ class Organizations < APIResource
8
+ # Retrieve an Organization
9
+ #
10
+ # @see https://developer.linkedin.com/docs/guide/v2/organizations/organization-lookup-api
11
+ #
12
+ # @macro organization_path_options
13
+ # @option options [String] :scope
14
+ # @option options [String] :type
15
+ # @option options [String] :count
16
+ # @option options [String] :start
17
+ # @return [LinkedIn::Mash]
18
+ def organization(options = {})
19
+ path = organization_path(options)
20
+ get(path, options)
21
+ end
22
+
23
+ # Retrieve Organization Access Control informaion
24
+ #
25
+ # @see https://developer.linkedin.com/docs/guide/v2/organizations/organization-lookup-api#acls
26
+ #
27
+ def organization_acls(options = {})
28
+ path = '/organizationalEntityAcls'
29
+ get(path, options)
30
+ end
31
+
32
+ private ##############################################################
33
+
34
+
35
+ def organization_path(options)
36
+ path = '/organizations'
37
+
38
+ if email_domain = options.delete(:email_domain)
39
+ path += "?q=emailDomain&emailDomain=#{CGI.escape(email_domain)}"
40
+ elsif id = options.delete(:id)
41
+ path += "/#{id}"
42
+ elsif urn = options.delete(:urn)
43
+ path += "/#{urn_to_id(urn)}"
44
+ elsif vanity_name = options.delete(:vanity_name)
45
+ path += "?q=vanityName&vanityName=#{CGI.escape(vanity_name)}"
46
+ else
47
+ path += "/me"
48
+ end
49
+ end
50
+
51
+ end
52
+ end